From 2c029fd0cb1ca710c74363b4a3f359577dcc51cf Mon Sep 17 00:00:00 2001 From: Green Baneling Date: Wed, 11 Jan 2023 13:22:22 +0000 Subject: [PATCH 001/558] Allow duplicate topics in smart contract events (#13065) * Removed `has_duplicates` check form the `deposit_event`. Removed the usage of `Error::DuplicateTopics`. * ".git/.scripts/commands/bench/bench.sh" pallet dev pallet_contracts Co-authored-by: command-bot <> --- frame/contracts/src/lib.rs | 2 - frame/contracts/src/wasm/mod.rs | 56 +- frame/contracts/src/wasm/runtime.rs | 18 +- frame/contracts/src/weights.rs | 1874 ++++++++++++++------------- 4 files changed, 972 insertions(+), 978 deletions(-) diff --git a/frame/contracts/src/lib.rs b/frame/contracts/src/lib.rs index 9df790e09..9efeec65f 100644 --- a/frame/contracts/src/lib.rs +++ b/frame/contracts/src/lib.rs @@ -816,8 +816,6 @@ pub mod pallet { RandomSubjectTooLong, /// The amount of topics passed to `seal_deposit_events` exceeds the limit. TooManyTopics, - /// The topics passed to `seal_deposit_events` contains at least one duplicate. - DuplicateTopics, /// The chain does not provide a chain extension. Calling the chain extension results /// in this error. Note that this usually shouldn't happen as deploying such contracts /// is rejected. diff --git a/frame/contracts/src/wasm/mod.rs b/frame/contracts/src/wasm/mod.rs index f1d0ccc7c..b540b3deb 100644 --- a/frame/contracts/src/wasm/mod.rs +++ b/frame/contracts/src/wasm/mod.rs @@ -1988,7 +1988,7 @@ mod tests { assert!(mock_ext.gas_meter.gas_left().ref_time() > 0); } - const CODE_DEPOSIT_EVENT_MAX_TOPICS: &str = r#" + const CODE_DEPOSIT_EVENT_DUPLICATES: &str = r#" (module (import "seal0" "seal_deposit_event" (func $seal_deposit_event (param i32 i32 i32 i32))) (import "env" "memory" (memory 1 1)) @@ -1996,7 +1996,7 @@ mod tests { (func (export "call") (call $seal_deposit_event (i32.const 32) ;; Pointer to the start of topics buffer - (i32.const 161) ;; The length of the topics buffer. + (i32.const 129) ;; The length of the topics buffer. (i32.const 8) ;; Pointer to the start of the data buffer (i32.const 13) ;; Length of the buffer ) @@ -2005,29 +2005,36 @@ mod tests { (data (i32.const 8) "\00\01\2A\00\00\00\00\00\00\00\E5\14\00") - ;; Encoded Vec>, the buffer has length of 161 bytes. - (data (i32.const 32) "\14" + ;; Encoded Vec>, the buffer has length of 129 bytes. + (data (i32.const 32) "\10" "\01\01\01\01\01\01\01\01\01\01\01\01\01\01\01\01\01\01\01\01\01\01\01\01\01\01\01\01\01\01\01\01" "\02\02\02\02\02\02\02\02\02\02\02\02\02\02\02\02\02\02\02\02\02\02\02\02\02\02\02\02\02\02\02\02" -"\03\03\03\03\03\03\03\03\03\03\03\03\03\03\03\03\03\03\03\03\03\03\03\03\03\03\03\03\03\03\03\03" -"\04\04\04\04\04\04\04\04\04\04\04\04\04\04\04\04\04\04\04\04\04\04\04\04\04\04\04\04\04\04\04\04" -"\05\05\05\05\05\05\05\05\05\05\05\05\05\05\05\05\05\05\05\05\05\05\05\05\05\05\05\05\05\05\05\05") +"\01\01\01\01\01\01\01\01\01\01\01\01\01\01\01\01\01\01\01\01\01\01\01\01\01\01\01\01\01\01\01\01" +"\04\04\04\04\04\04\04\04\04\04\04\04\04\04\04\04\04\04\04\04\04\04\04\04\04\04\04\04\04\04\04\04") ) "#; - /// Checks that the runtime traps if there are more than `max_topic_events` topics. + /// Checks that the runtime allows duplicate topics. #[test] - fn deposit_event_max_topics() { + fn deposit_event_duplicates_allowed() { + let mut mock_ext = MockExt::default(); + assert_ok!(execute(CODE_DEPOSIT_EVENT_DUPLICATES, vec![], &mut mock_ext,)); + assert_eq!( - execute(CODE_DEPOSIT_EVENT_MAX_TOPICS, vec![], MockExt::default(),), - Err(ExecError { - error: Error::::TooManyTopics.into(), - origin: ErrorOrigin::Caller, - }) + mock_ext.events, + vec![( + vec![ + H256::repeat_byte(0x01), + H256::repeat_byte(0x02), + H256::repeat_byte(0x01), + H256::repeat_byte(0x04) + ], + vec![0x00, 0x01, 0x2a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xe5, 0x14, 0x00] + )] ); } - const CODE_DEPOSIT_EVENT_DUPLICATES: &str = r#" + const CODE_DEPOSIT_EVENT_MAX_TOPICS: &str = r#" (module (import "seal0" "seal_deposit_event" (func $seal_deposit_event (param i32 i32 i32 i32))) (import "env" "memory" (memory 1 1)) @@ -2035,7 +2042,7 @@ mod tests { (func (export "call") (call $seal_deposit_event (i32.const 32) ;; Pointer to the start of topics buffer - (i32.const 129) ;; The length of the topics buffer. + (i32.const 161) ;; The length of the topics buffer. (i32.const 8) ;; Pointer to the start of the data buffer (i32.const 13) ;; Length of the buffer ) @@ -2044,22 +2051,23 @@ mod tests { (data (i32.const 8) "\00\01\2A\00\00\00\00\00\00\00\E5\14\00") - ;; Encoded Vec>, the buffer has length of 129 bytes. - (data (i32.const 32) "\10" + ;; Encoded Vec>, the buffer has length of 161 bytes. + (data (i32.const 32) "\14" "\01\01\01\01\01\01\01\01\01\01\01\01\01\01\01\01\01\01\01\01\01\01\01\01\01\01\01\01\01\01\01\01" "\02\02\02\02\02\02\02\02\02\02\02\02\02\02\02\02\02\02\02\02\02\02\02\02\02\02\02\02\02\02\02\02" -"\01\01\01\01\01\01\01\01\01\01\01\01\01\01\01\01\01\01\01\01\01\01\01\01\01\01\01\01\01\01\01\01" -"\04\04\04\04\04\04\04\04\04\04\04\04\04\04\04\04\04\04\04\04\04\04\04\04\04\04\04\04\04\04\04\04") +"\03\03\03\03\03\03\03\03\03\03\03\03\03\03\03\03\03\03\03\03\03\03\03\03\03\03\03\03\03\03\03\03" +"\04\04\04\04\04\04\04\04\04\04\04\04\04\04\04\04\04\04\04\04\04\04\04\04\04\04\04\04\04\04\04\04" +"\05\05\05\05\05\05\05\05\05\05\05\05\05\05\05\05\05\05\05\05\05\05\05\05\05\05\05\05\05\05\05\05") ) "#; - /// Checks that the runtime traps if there are duplicates. + /// Checks that the runtime traps if there are more than `max_topic_events` topics. #[test] - fn deposit_event_duplicates() { + fn deposit_event_max_topics() { assert_eq!( - execute(CODE_DEPOSIT_EVENT_DUPLICATES, vec![], MockExt::default(),), + execute(CODE_DEPOSIT_EVENT_MAX_TOPICS, vec![], MockExt::default(),), Err(ExecError { - error: Error::::DuplicateTopics.into(), + error: Error::::TooManyTopics.into(), origin: ErrorOrigin::Caller, }) ); diff --git a/frame/contracts/src/wasm/runtime.rs b/frame/contracts/src/wasm/runtime.rs index 098f8d280..b5398bb62 100644 --- a/frame/contracts/src/wasm/runtime.rs +++ b/frame/contracts/src/wasm/runtime.rs @@ -2085,15 +2085,6 @@ pub mod env { data_ptr: u32, data_len: u32, ) -> Result<(), TrapReason> { - fn has_duplicates(items: &mut Vec) -> bool { - items.sort(); - // Find any two consecutive equal elements. - items.windows(2).any(|w| match &w { - &[a, b] => a == b, - _ => false, - }) - } - let num_topic = topics_len .checked_div(sp_std::mem::size_of::>() as u32) .ok_or("Zero sized topics are not allowed")?; @@ -2102,7 +2093,7 @@ pub mod env { return Err(Error::::ValueTooLarge.into()) } - let mut topics: Vec::T>> = match topics_len { + let topics: Vec::T>> = match topics_len { 0 => Vec::new(), _ => ctx.read_sandbox_memory_as_unbounded(memory, topics_ptr, topics_len)?, }; @@ -2112,13 +2103,6 @@ pub mod env { return Err(Error::::TooManyTopics.into()) } - // Check for duplicate topics. If there are any, then trap. - // Complexity O(n * log(n)) and no additional allocations. - // This also sorts the topics. - if has_duplicates(&mut topics) { - return Err(Error::::DuplicateTopics.into()) - } - let event_data = ctx.read_sandbox_memory(memory, data_ptr, data_len)?; ctx.ext.deposit_event(topics, event_data); diff --git a/frame/contracts/src/weights.rs b/frame/contracts/src/weights.rs index 69fcbd6ee..7ae1a9c59 100644 --- a/frame/contracts/src/weights.rs +++ b/frame/contracts/src/weights.rs @@ -18,7 +18,7 @@ //! Autogenerated weights for pallet_contracts //! //! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev -//! DATE: 2022-12-09, STEPS: `50`, REPEAT: 20, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! DATE: 2023-01-10, STEPS: `50`, REPEAT: 20, LOW RANGE: `[]`, HIGH RANGE: `[]` //! HOSTNAME: `bm3`, CPU: `Intel(R) Core(TM) i7-7700K CPU @ 4.20GHz` //! EXECUTION: Some(Wasm), WASM-EXECUTION: Compiled, CHAIN: Some("dev"), DB CACHE: 1024 @@ -172,17 +172,17 @@ pub struct SubstrateWeight(PhantomData); impl WeightInfo for SubstrateWeight { // Storage: Contracts DeletionQueue (r:1 w:0) fn on_process_deletion_queue_batch() -> Weight { - // Minimum execution time: 3_196 nanoseconds. - Weight::from_ref_time(3_293_000) + // Minimum execution time: 3_326 nanoseconds. + Weight::from_ref_time(3_433_000) .saturating_add(T::DbWeight::get().reads(1)) } // Storage: Skipped Metadata (r:0 w:0) /// The range of component `k` is `[0, 1024]`. fn on_initialize_per_trie_key(k: u32, ) -> Weight { - // Minimum execution time: 14_857 nanoseconds. - Weight::from_ref_time(14_957_593) - // Standard Error: 1_015 - .saturating_add(Weight::from_ref_time(935_359).saturating_mul(k.into())) + // Minimum execution time: 15_703 nanoseconds. + Weight::from_ref_time(15_288_927) + // Standard Error: 951 + .saturating_add(Weight::from_ref_time(940_816).saturating_mul(k.into())) .saturating_add(T::DbWeight::get().reads(1)) .saturating_add(T::DbWeight::get().writes(1)) .saturating_add(T::DbWeight::get().writes((1_u64).saturating_mul(k.into()))) @@ -190,10 +190,10 @@ impl WeightInfo for SubstrateWeight { // Storage: Contracts DeletionQueue (r:1 w:0) /// The range of component `q` is `[0, 128]`. fn on_initialize_per_queue_item(q: u32, ) -> Weight { - // Minimum execution time: 3_231 nanoseconds. - Weight::from_ref_time(14_859_580) - // Standard Error: 3_479 - .saturating_add(Weight::from_ref_time(1_185_600).saturating_mul(q.into())) + // Minimum execution time: 3_548 nanoseconds. + Weight::from_ref_time(15_764_121) + // Standard Error: 3_339 + .saturating_add(Weight::from_ref_time(1_214_890).saturating_mul(q.into())) .saturating_add(T::DbWeight::get().reads(1)) .saturating_add(T::DbWeight::get().writes(1)) } @@ -201,10 +201,10 @@ impl WeightInfo for SubstrateWeight { // Storage: Contracts CodeStorage (r:0 w:1) /// The range of component `c` is `[0, 64226]`. fn reinstrument(c: u32, ) -> Weight { - // Minimum execution time: 34_565 nanoseconds. - Weight::from_ref_time(29_199_016) - // Standard Error: 70 - .saturating_add(Weight::from_ref_time(47_107).saturating_mul(c.into())) + // Minimum execution time: 29_801 nanoseconds. + Weight::from_ref_time(27_932_706) + // Standard Error: 49 + .saturating_add(Weight::from_ref_time(50_532).saturating_mul(c.into())) .saturating_add(T::DbWeight::get().reads(1)) .saturating_add(T::DbWeight::get().writes(1)) } @@ -215,10 +215,10 @@ impl WeightInfo for SubstrateWeight { // Storage: System EventTopics (r:2 w:2) /// The range of component `c` is `[0, 131072]`. fn call_with_code_per_byte(c: u32, ) -> Weight { - // Minimum execution time: 392_074 nanoseconds. - Weight::from_ref_time(404_090_909) - // Standard Error: 24 - .saturating_add(Weight::from_ref_time(30_454).saturating_mul(c.into())) + // Minimum execution time: 307_894 nanoseconds. + Weight::from_ref_time(322_346_319) + // Standard Error: 25 + .saturating_add(Weight::from_ref_time(30_678).saturating_mul(c.into())) .saturating_add(T::DbWeight::get().reads(6)) .saturating_add(T::DbWeight::get().writes(4)) } @@ -234,14 +234,14 @@ impl WeightInfo for SubstrateWeight { /// The range of component `i` is `[0, 1048576]`. /// The range of component `s` is `[0, 1048576]`. fn instantiate_with_code(c: u32, i: u32, s: u32, ) -> Weight { - // Minimum execution time: 3_785_934 nanoseconds. - Weight::from_ref_time(683_143_843) - // Standard Error: 272 - .saturating_add(Weight::from_ref_time(87_620).saturating_mul(c.into())) + // Minimum execution time: 3_610_424 nanoseconds. + Weight::from_ref_time(583_426_386) + // Standard Error: 277 + .saturating_add(Weight::from_ref_time(90_224).saturating_mul(c.into())) // Standard Error: 16 - .saturating_add(Weight::from_ref_time(1_363).saturating_mul(i.into())) + .saturating_add(Weight::from_ref_time(1_325).saturating_mul(i.into())) // Standard Error: 16 - .saturating_add(Weight::from_ref_time(1_778).saturating_mul(s.into())) + .saturating_add(Weight::from_ref_time(1_727).saturating_mul(s.into())) .saturating_add(T::DbWeight::get().reads(8)) .saturating_add(T::DbWeight::get().writes(9)) } @@ -255,12 +255,12 @@ impl WeightInfo for SubstrateWeight { /// The range of component `i` is `[0, 1048576]`. /// The range of component `s` is `[0, 1048576]`. fn instantiate(i: u32, s: u32, ) -> Weight { - // Minimum execution time: 1_935_310 nanoseconds. - Weight::from_ref_time(203_531_122) + // Minimum execution time: 1_892_996 nanoseconds. + Weight::from_ref_time(202_958_196) // Standard Error: 8 - .saturating_add(Weight::from_ref_time(1_674).saturating_mul(i.into())) + .saturating_add(Weight::from_ref_time(1_627).saturating_mul(i.into())) // Standard Error: 8 - .saturating_add(Weight::from_ref_time(1_789).saturating_mul(s.into())) + .saturating_add(Weight::from_ref_time(1_755).saturating_mul(s.into())) .saturating_add(T::DbWeight::get().reads(8)) .saturating_add(T::DbWeight::get().writes(7)) } @@ -270,8 +270,8 @@ impl WeightInfo for SubstrateWeight { // Storage: System Account (r:1 w:1) // Storage: System EventTopics (r:2 w:2) fn call() -> Weight { - // Minimum execution time: 151_999 nanoseconds. - Weight::from_ref_time(153_527_000) + // Minimum execution time: 157_347 nanoseconds. + Weight::from_ref_time(159_084_000) .saturating_add(T::DbWeight::get().reads(6)) .saturating_add(T::DbWeight::get().writes(4)) } @@ -281,10 +281,10 @@ impl WeightInfo for SubstrateWeight { // Storage: Contracts OwnerInfoOf (r:0 w:1) /// The range of component `c` is `[0, 64226]`. fn upload_code(c: u32, ) -> Weight { - // Minimum execution time: 391_165 nanoseconds. - Weight::from_ref_time(394_519_487) - // Standard Error: 75 - .saturating_add(Weight::from_ref_time(89_736).saturating_mul(c.into())) + // Minimum execution time: 299_987 nanoseconds. + Weight::from_ref_time(305_274_879) + // Standard Error: 72 + .saturating_add(Weight::from_ref_time(91_916).saturating_mul(c.into())) .saturating_add(T::DbWeight::get().reads(2)) .saturating_add(T::DbWeight::get().writes(4)) } @@ -293,8 +293,8 @@ impl WeightInfo for SubstrateWeight { // Storage: Contracts CodeStorage (r:0 w:1) // Storage: Contracts PristineCode (r:0 w:1) fn remove_code() -> Weight { - // Minimum execution time: 39_354 nanoseconds. - Weight::from_ref_time(39_855_000) + // Minimum execution time: 40_795 nanoseconds. + Weight::from_ref_time(41_297_000) .saturating_add(T::DbWeight::get().reads(2)) .saturating_add(T::DbWeight::get().writes(4)) } @@ -302,8 +302,8 @@ impl WeightInfo for SubstrateWeight { // Storage: Contracts OwnerInfoOf (r:2 w:2) // Storage: System EventTopics (r:3 w:3) fn set_code() -> Weight { - // Minimum execution time: 40_909 nanoseconds. - Weight::from_ref_time(41_275_000) + // Minimum execution time: 41_957 nanoseconds. + Weight::from_ref_time(42_536_000) .saturating_add(T::DbWeight::get().reads(6)) .saturating_add(T::DbWeight::get().writes(6)) } @@ -314,10 +314,10 @@ impl WeightInfo for SubstrateWeight { // Storage: System EventTopics (r:2 w:2) /// The range of component `r` is `[0, 20]`. fn seal_caller(r: u32, ) -> Weight { - // Minimum execution time: 380_150 nanoseconds. - Weight::from_ref_time(384_429_035) - // Standard Error: 34_740 - .saturating_add(Weight::from_ref_time(15_582_218).saturating_mul(r.into())) + // Minimum execution time: 293_544 nanoseconds. + Weight::from_ref_time(298_981_222) + // Standard Error: 39_139 + .saturating_add(Weight::from_ref_time(17_278_436).saturating_mul(r.into())) .saturating_add(T::DbWeight::get().reads(6)) .saturating_add(T::DbWeight::get().writes(3)) } @@ -328,10 +328,10 @@ impl WeightInfo for SubstrateWeight { // Storage: System EventTopics (r:2 w:2) /// The range of component `r` is `[0, 20]`. fn seal_is_contract(r: u32, ) -> Weight { - // Minimum execution time: 380_608 nanoseconds. - Weight::from_ref_time(326_544_805) - // Standard Error: 475_381 - .saturating_add(Weight::from_ref_time(190_717_772).saturating_mul(r.into())) + // Minimum execution time: 293_500 nanoseconds. + Weight::from_ref_time(237_825_284) + // Standard Error: 450_234 + .saturating_add(Weight::from_ref_time(198_995_806).saturating_mul(r.into())) .saturating_add(T::DbWeight::get().reads(6)) .saturating_add(T::DbWeight::get().reads((80_u64).saturating_mul(r.into()))) .saturating_add(T::DbWeight::get().writes(3)) @@ -343,10 +343,10 @@ impl WeightInfo for SubstrateWeight { // Storage: System EventTopics (r:2 w:2) /// The range of component `r` is `[0, 20]`. fn seal_code_hash(r: u32, ) -> Weight { - // Minimum execution time: 382_780 nanoseconds. - Weight::from_ref_time(333_775_113) - // Standard Error: 446_086 - .saturating_add(Weight::from_ref_time(232_531_042).saturating_mul(r.into())) + // Minimum execution time: 295_851 nanoseconds. + Weight::from_ref_time(258_113_447) + // Standard Error: 408_747 + .saturating_add(Weight::from_ref_time(237_490_216).saturating_mul(r.into())) .saturating_add(T::DbWeight::get().reads(6)) .saturating_add(T::DbWeight::get().reads((80_u64).saturating_mul(r.into()))) .saturating_add(T::DbWeight::get().writes(3)) @@ -358,10 +358,10 @@ impl WeightInfo for SubstrateWeight { // Storage: System EventTopics (r:2 w:2) /// The range of component `r` is `[0, 20]`. fn seal_own_code_hash(r: u32, ) -> Weight { - // Minimum execution time: 381_815 nanoseconds. - Weight::from_ref_time(390_931_793) - // Standard Error: 61_918 - .saturating_add(Weight::from_ref_time(18_798_438).saturating_mul(r.into())) + // Minimum execution time: 294_614 nanoseconds. + Weight::from_ref_time(301_528_998) + // Standard Error: 40_613 + .saturating_add(Weight::from_ref_time(20_815_088).saturating_mul(r.into())) .saturating_add(T::DbWeight::get().reads(6)) .saturating_add(T::DbWeight::get().writes(3)) } @@ -372,10 +372,10 @@ impl WeightInfo for SubstrateWeight { // Storage: System EventTopics (r:2 w:2) /// The range of component `r` is `[0, 20]`. fn seal_caller_is_origin(r: u32, ) -> Weight { - // Minimum execution time: 379_162 nanoseconds. - Weight::from_ref_time(383_796_363) - // Standard Error: 25_555 - .saturating_add(Weight::from_ref_time(10_843_721).saturating_mul(r.into())) + // Minimum execution time: 293_088 nanoseconds. + Weight::from_ref_time(299_120_595) + // Standard Error: 23_277 + .saturating_add(Weight::from_ref_time(11_130_334).saturating_mul(r.into())) .saturating_add(T::DbWeight::get().reads(6)) .saturating_add(T::DbWeight::get().writes(3)) } @@ -386,10 +386,10 @@ impl WeightInfo for SubstrateWeight { // Storage: System EventTopics (r:2 w:2) /// The range of component `r` is `[0, 20]`. fn seal_address(r: u32, ) -> Weight { - // Minimum execution time: 380_307 nanoseconds. - Weight::from_ref_time(390_211_779) - // Standard Error: 53_326 - .saturating_add(Weight::from_ref_time(15_311_171).saturating_mul(r.into())) + // Minimum execution time: 295_110 nanoseconds. + Weight::from_ref_time(305_295_787) + // Standard Error: 48_521 + .saturating_add(Weight::from_ref_time(16_610_806).saturating_mul(r.into())) .saturating_add(T::DbWeight::get().reads(6)) .saturating_add(T::DbWeight::get().writes(3)) } @@ -400,10 +400,10 @@ impl WeightInfo for SubstrateWeight { // Storage: System EventTopics (r:2 w:2) /// The range of component `r` is `[0, 20]`. fn seal_gas_left(r: u32, ) -> Weight { - // Minimum execution time: 380_230 nanoseconds. - Weight::from_ref_time(383_470_453) - // Standard Error: 45_007 - .saturating_add(Weight::from_ref_time(15_582_472).saturating_mul(r.into())) + // Minimum execution time: 293_414 nanoseconds. + Weight::from_ref_time(299_960_283) + // Standard Error: 32_003 + .saturating_add(Weight::from_ref_time(16_652_433).saturating_mul(r.into())) .saturating_add(T::DbWeight::get().reads(6)) .saturating_add(T::DbWeight::get().writes(3)) } @@ -414,10 +414,10 @@ impl WeightInfo for SubstrateWeight { // Storage: System EventTopics (r:2 w:2) /// The range of component `r` is `[0, 20]`. fn seal_balance(r: u32, ) -> Weight { - // Minimum execution time: 380_244 nanoseconds. - Weight::from_ref_time(388_092_461) - // Standard Error: 98_199 - .saturating_add(Weight::from_ref_time(97_339_528).saturating_mul(r.into())) + // Minimum execution time: 292_429 nanoseconds. + Weight::from_ref_time(301_025_128) + // Standard Error: 92_155 + .saturating_add(Weight::from_ref_time(92_731_719).saturating_mul(r.into())) .saturating_add(T::DbWeight::get().reads(7)) .saturating_add(T::DbWeight::get().writes(3)) } @@ -428,10 +428,10 @@ impl WeightInfo for SubstrateWeight { // Storage: System EventTopics (r:2 w:2) /// The range of component `r` is `[0, 20]`. fn seal_value_transferred(r: u32, ) -> Weight { - // Minimum execution time: 380_242 nanoseconds. - Weight::from_ref_time(384_078_258) - // Standard Error: 28_510 - .saturating_add(Weight::from_ref_time(15_423_359).saturating_mul(r.into())) + // Minimum execution time: 293_516 nanoseconds. + Weight::from_ref_time(300_071_428) + // Standard Error: 25_871 + .saturating_add(Weight::from_ref_time(16_599_369).saturating_mul(r.into())) .saturating_add(T::DbWeight::get().reads(6)) .saturating_add(T::DbWeight::get().writes(3)) } @@ -442,10 +442,10 @@ impl WeightInfo for SubstrateWeight { // Storage: System EventTopics (r:2 w:2) /// The range of component `r` is `[0, 20]`. fn seal_minimum_balance(r: u32, ) -> Weight { - // Minimum execution time: 379_890 nanoseconds. - Weight::from_ref_time(383_658_430) - // Standard Error: 44_976 - .saturating_add(Weight::from_ref_time(15_451_905).saturating_mul(r.into())) + // Minimum execution time: 293_666 nanoseconds. + Weight::from_ref_time(299_803_599) + // Standard Error: 24_508 + .saturating_add(Weight::from_ref_time(16_500_973).saturating_mul(r.into())) .saturating_add(T::DbWeight::get().reads(6)) .saturating_add(T::DbWeight::get().writes(3)) } @@ -456,10 +456,10 @@ impl WeightInfo for SubstrateWeight { // Storage: System EventTopics (r:2 w:2) /// The range of component `r` is `[0, 20]`. fn seal_block_number(r: u32, ) -> Weight { - // Minimum execution time: 380_269 nanoseconds. - Weight::from_ref_time(383_580_481) - // Standard Error: 31_862 - .saturating_add(Weight::from_ref_time(15_230_473).saturating_mul(r.into())) + // Minimum execution time: 289_193 nanoseconds. + Weight::from_ref_time(297_946_784) + // Standard Error: 43_719 + .saturating_add(Weight::from_ref_time(16_442_254).saturating_mul(r.into())) .saturating_add(T::DbWeight::get().reads(6)) .saturating_add(T::DbWeight::get().writes(3)) } @@ -470,10 +470,10 @@ impl WeightInfo for SubstrateWeight { // Storage: System EventTopics (r:2 w:2) /// The range of component `r` is `[0, 20]`. fn seal_now(r: u32, ) -> Weight { - // Minimum execution time: 380_308 nanoseconds. - Weight::from_ref_time(383_732_372) - // Standard Error: 38_359 - .saturating_add(Weight::from_ref_time(15_358_775).saturating_mul(r.into())) + // Minimum execution time: 288_897 nanoseconds. + Weight::from_ref_time(298_856_658) + // Standard Error: 31_003 + .saturating_add(Weight::from_ref_time(16_490_508).saturating_mul(r.into())) .saturating_add(T::DbWeight::get().reads(6)) .saturating_add(T::DbWeight::get().writes(3)) } @@ -485,10 +485,10 @@ impl WeightInfo for SubstrateWeight { // Storage: TransactionPayment NextFeeMultiplier (r:1 w:0) /// The range of component `r` is `[0, 20]`. fn seal_weight_to_fee(r: u32, ) -> Weight { - // Minimum execution time: 380_834 nanoseconds. - Weight::from_ref_time(388_999_459) - // Standard Error: 96_447 - .saturating_add(Weight::from_ref_time(86_714_414).saturating_mul(r.into())) + // Minimum execution time: 289_850 nanoseconds. + Weight::from_ref_time(302_898_449) + // Standard Error: 92_822 + .saturating_add(Weight::from_ref_time(86_376_874).saturating_mul(r.into())) .saturating_add(T::DbWeight::get().reads(7)) .saturating_add(T::DbWeight::get().writes(3)) } @@ -499,10 +499,10 @@ impl WeightInfo for SubstrateWeight { // Storage: System EventTopics (r:2 w:2) /// The range of component `r` is `[0, 20]`. fn seal_gas(r: u32, ) -> Weight { - // Minimum execution time: 140_955 nanoseconds. - Weight::from_ref_time(144_716_423) - // Standard Error: 11_370 - .saturating_add(Weight::from_ref_time(7_858_807).saturating_mul(r.into())) + // Minimum execution time: 145_105 nanoseconds. + Weight::from_ref_time(152_388_934) + // Standard Error: 29_988 + .saturating_add(Weight::from_ref_time(7_761_947).saturating_mul(r.into())) .saturating_add(T::DbWeight::get().reads(6)) .saturating_add(T::DbWeight::get().writes(3)) } @@ -513,10 +513,10 @@ impl WeightInfo for SubstrateWeight { // Storage: System EventTopics (r:2 w:2) /// The range of component `r` is `[0, 20]`. fn seal_input(r: u32, ) -> Weight { - // Minimum execution time: 380_210 nanoseconds. - Weight::from_ref_time(384_185_776) - // Standard Error: 49_038 - .saturating_add(Weight::from_ref_time(13_649_793).saturating_mul(r.into())) + // Minimum execution time: 289_319 nanoseconds. + Weight::from_ref_time(298_390_911) + // Standard Error: 27_329 + .saturating_add(Weight::from_ref_time(14_225_527).saturating_mul(r.into())) .saturating_add(T::DbWeight::get().reads(6)) .saturating_add(T::DbWeight::get().writes(3)) } @@ -527,10 +527,10 @@ impl WeightInfo for SubstrateWeight { // Storage: System EventTopics (r:2 w:2) /// The range of component `n` is `[0, 1024]`. fn seal_input_per_kb(n: u32, ) -> Weight { - // Minimum execution time: 396_209 nanoseconds. - Weight::from_ref_time(424_522_611) - // Standard Error: 3_917 - .saturating_add(Weight::from_ref_time(9_627_216).saturating_mul(n.into())) + // Minimum execution time: 305_538 nanoseconds. + Weight::from_ref_time(339_734_596) + // Standard Error: 4_636 + .saturating_add(Weight::from_ref_time(9_592_149).saturating_mul(n.into())) .saturating_add(T::DbWeight::get().reads(6)) .saturating_add(T::DbWeight::get().writes(3)) } @@ -541,10 +541,10 @@ impl WeightInfo for SubstrateWeight { // Storage: System EventTopics (r:2 w:2) /// The range of component `r` is `[0, 1]`. fn seal_return(r: u32, ) -> Weight { - // Minimum execution time: 378_412 nanoseconds. - Weight::from_ref_time(380_502_085) - // Standard Error: 201_552 - .saturating_add(Weight::from_ref_time(1_434_214).saturating_mul(r.into())) + // Minimum execution time: 287_189 nanoseconds. + Weight::from_ref_time(295_472_383) + // Standard Error: 314_142 + .saturating_add(Weight::from_ref_time(214_016).saturating_mul(r.into())) .saturating_add(T::DbWeight::get().reads(6)) .saturating_add(T::DbWeight::get().writes(3)) } @@ -555,10 +555,10 @@ impl WeightInfo for SubstrateWeight { // Storage: System EventTopics (r:2 w:2) /// The range of component `n` is `[0, 1024]`. fn seal_return_per_kb(n: u32, ) -> Weight { - // Minimum execution time: 381_463 nanoseconds. - Weight::from_ref_time(383_955_553) - // Standard Error: 998 - .saturating_add(Weight::from_ref_time(230_512).saturating_mul(n.into())) + // Minimum execution time: 289_380 nanoseconds. + Weight::from_ref_time(296_302_404) + // Standard Error: 889 + .saturating_add(Weight::from_ref_time(190_017).saturating_mul(n.into())) .saturating_add(T::DbWeight::get().reads(6)) .saturating_add(T::DbWeight::get().writes(3)) } @@ -571,10 +571,10 @@ impl WeightInfo for SubstrateWeight { // Storage: Contracts OwnerInfoOf (r:1 w:1) /// The range of component `r` is `[0, 1]`. fn seal_terminate(r: u32, ) -> Weight { - // Minimum execution time: 379_877 nanoseconds. - Weight::from_ref_time(381_729_546) - // Standard Error: 214_004 - .saturating_add(Weight::from_ref_time(52_528_353).saturating_mul(r.into())) + // Minimum execution time: 292_383 nanoseconds. + Weight::from_ref_time(297_557_691) + // Standard Error: 251_228 + .saturating_add(Weight::from_ref_time(56_688_008).saturating_mul(r.into())) .saturating_add(T::DbWeight::get().reads(6)) .saturating_add(T::DbWeight::get().reads((5_u64).saturating_mul(r.into()))) .saturating_add(T::DbWeight::get().writes(3)) @@ -588,10 +588,10 @@ impl WeightInfo for SubstrateWeight { // Storage: RandomnessCollectiveFlip RandomMaterial (r:1 w:0) /// The range of component `r` is `[0, 20]`. fn seal_random(r: u32, ) -> Weight { - // Minimum execution time: 380_275 nanoseconds. - Weight::from_ref_time(386_495_777) - // Standard Error: 94_674 - .saturating_add(Weight::from_ref_time(108_432_929).saturating_mul(r.into())) + // Minimum execution time: 293_019 nanoseconds. + Weight::from_ref_time(301_747_439) + // Standard Error: 92_043 + .saturating_add(Weight::from_ref_time(109_861_144).saturating_mul(r.into())) .saturating_add(T::DbWeight::get().reads(7)) .saturating_add(T::DbWeight::get().writes(3)) } @@ -602,10 +602,10 @@ impl WeightInfo for SubstrateWeight { // Storage: System EventTopics (r:2 w:2) /// The range of component `r` is `[0, 20]`. fn seal_deposit_event(r: u32, ) -> Weight { - // Minimum execution time: 379_095 nanoseconds. - Weight::from_ref_time(393_997_924) - // Standard Error: 141_916 - .saturating_add(Weight::from_ref_time(212_937_170).saturating_mul(r.into())) + // Minimum execution time: 287_586 nanoseconds. + Weight::from_ref_time(304_751_302) + // Standard Error: 127_698 + .saturating_add(Weight::from_ref_time(221_525_053).saturating_mul(r.into())) .saturating_add(T::DbWeight::get().reads(6)) .saturating_add(T::DbWeight::get().writes(3)) } @@ -617,12 +617,12 @@ impl WeightInfo for SubstrateWeight { /// The range of component `t` is `[0, 4]`. /// The range of component `n` is `[0, 16]`. fn seal_deposit_event_per_topic_and_kb(t: u32, n: u32, ) -> Weight { - // Minimum execution time: 1_241_001 nanoseconds. - Weight::from_ref_time(548_526_917) - // Standard Error: 496_807 - .saturating_add(Weight::from_ref_time(177_087_769).saturating_mul(t.into())) - // Standard Error: 136_447 - .saturating_add(Weight::from_ref_time(71_558_402).saturating_mul(n.into())) + // Minimum execution time: 1_182_204 nanoseconds. + Weight::from_ref_time(508_607_957) + // Standard Error: 503_533 + .saturating_add(Weight::from_ref_time(173_998_655).saturating_mul(t.into())) + // Standard Error: 138_294 + .saturating_add(Weight::from_ref_time(67_991_373).saturating_mul(n.into())) .saturating_add(T::DbWeight::get().reads(6)) .saturating_add(T::DbWeight::get().reads((80_u64).saturating_mul(t.into()))) .saturating_add(T::DbWeight::get().writes(3)) @@ -635,20 +635,20 @@ impl WeightInfo for SubstrateWeight { // Storage: System EventTopics (r:2 w:2) /// The range of component `r` is `[0, 20]`. fn seal_debug_message(r: u32, ) -> Weight { - // Minimum execution time: 159_158 nanoseconds. - Weight::from_ref_time(163_427_712) - // Standard Error: 22_442 - .saturating_add(Weight::from_ref_time(12_647_838).saturating_mul(r.into())) + // Minimum execution time: 157_706 nanoseconds. + Weight::from_ref_time(161_895_583) + // Standard Error: 14_952 + .saturating_add(Weight::from_ref_time(12_990_237).saturating_mul(r.into())) .saturating_add(T::DbWeight::get().reads(6)) .saturating_add(T::DbWeight::get().writes(3)) } // Storage: Skipped Metadata (r:0 w:0) /// The range of component `r` is `[0, 10]`. fn seal_set_storage(r: u32, ) -> Weight { - // Minimum execution time: 381_068 nanoseconds. - Weight::from_ref_time(341_041_230) - // Standard Error: 464_053 - .saturating_add(Weight::from_ref_time(402_677_314).saturating_mul(r.into())) + // Minimum execution time: 290_063 nanoseconds. + Weight::from_ref_time(256_404_853) + // Standard Error: 436_195 + .saturating_add(Weight::from_ref_time(406_569_743).saturating_mul(r.into())) .saturating_add(T::DbWeight::get().reads(6)) .saturating_add(T::DbWeight::get().reads((80_u64).saturating_mul(r.into()))) .saturating_add(T::DbWeight::get().writes(3)) @@ -657,10 +657,10 @@ impl WeightInfo for SubstrateWeight { // Storage: Skipped Metadata (r:0 w:0) /// The range of component `n` is `[0, 8]`. fn seal_set_storage_per_new_kb(n: u32, ) -> Weight { - // Minimum execution time: 508_695 nanoseconds. - Weight::from_ref_time(663_159_695) - // Standard Error: 1_419_342 - .saturating_add(Weight::from_ref_time(96_558_570).saturating_mul(n.into())) + // Minimum execution time: 426_295 nanoseconds. + Weight::from_ref_time(582_989_911) + // Standard Error: 1_404_141 + .saturating_add(Weight::from_ref_time(89_545_963).saturating_mul(n.into())) .saturating_add(T::DbWeight::get().reads(52)) .saturating_add(T::DbWeight::get().reads((7_u64).saturating_mul(n.into()))) .saturating_add(T::DbWeight::get().writes(50)) @@ -669,10 +669,10 @@ impl WeightInfo for SubstrateWeight { // Storage: Skipped Metadata (r:0 w:0) /// The range of component `n` is `[0, 8]`. fn seal_set_storage_per_old_kb(n: u32, ) -> Weight { - // Minimum execution time: 508_542 nanoseconds. - Weight::from_ref_time(634_146_978) - // Standard Error: 1_168_252 - .saturating_add(Weight::from_ref_time(64_231_947).saturating_mul(n.into())) + // Minimum execution time: 425_062 nanoseconds. + Weight::from_ref_time(552_380_900) + // Standard Error: 1_140_169 + .saturating_add(Weight::from_ref_time(64_085_108).saturating_mul(n.into())) .saturating_add(T::DbWeight::get().reads(51)) .saturating_add(T::DbWeight::get().reads((7_u64).saturating_mul(n.into()))) .saturating_add(T::DbWeight::get().writes(49)) @@ -681,10 +681,10 @@ impl WeightInfo for SubstrateWeight { // Storage: Skipped Metadata (r:0 w:0) /// The range of component `r` is `[0, 10]`. fn seal_clear_storage(r: u32, ) -> Weight { - // Minimum execution time: 381_743 nanoseconds. - Weight::from_ref_time(337_309_674) - // Standard Error: 527_407 - .saturating_add(Weight::from_ref_time(395_767_068).saturating_mul(r.into())) + // Minimum execution time: 294_421 nanoseconds. + Weight::from_ref_time(260_897_072) + // Standard Error: 474_135 + .saturating_add(Weight::from_ref_time(403_191_862).saturating_mul(r.into())) .saturating_add(T::DbWeight::get().reads(6)) .saturating_add(T::DbWeight::get().reads((80_u64).saturating_mul(r.into()))) .saturating_add(T::DbWeight::get().writes(3)) @@ -693,10 +693,10 @@ impl WeightInfo for SubstrateWeight { // Storage: Skipped Metadata (r:0 w:0) /// The range of component `n` is `[0, 8]`. fn seal_clear_storage_per_kb(n: u32, ) -> Weight { - // Minimum execution time: 478_283 nanoseconds. - Weight::from_ref_time(616_673_245) - // Standard Error: 1_290_784 - .saturating_add(Weight::from_ref_time(66_534_552).saturating_mul(n.into())) + // Minimum execution time: 391_743 nanoseconds. + Weight::from_ref_time(535_327_545) + // Standard Error: 1_297_157 + .saturating_add(Weight::from_ref_time(67_222_981).saturating_mul(n.into())) .saturating_add(T::DbWeight::get().reads(51)) .saturating_add(T::DbWeight::get().reads((7_u64).saturating_mul(n.into()))) .saturating_add(T::DbWeight::get().writes(48)) @@ -705,10 +705,10 @@ impl WeightInfo for SubstrateWeight { // Storage: Skipped Metadata (r:0 w:0) /// The range of component `r` is `[0, 10]`. fn seal_get_storage(r: u32, ) -> Weight { - // Minimum execution time: 381_916 nanoseconds. - Weight::from_ref_time(349_150_912) - // Standard Error: 443_388 - .saturating_add(Weight::from_ref_time(316_975_558).saturating_mul(r.into())) + // Minimum execution time: 294_947 nanoseconds. + Weight::from_ref_time(266_280_887) + // Standard Error: 382_477 + .saturating_add(Weight::from_ref_time(323_306_898).saturating_mul(r.into())) .saturating_add(T::DbWeight::get().reads(6)) .saturating_add(T::DbWeight::get().reads((80_u64).saturating_mul(r.into()))) .saturating_add(T::DbWeight::get().writes(3)) @@ -716,10 +716,10 @@ impl WeightInfo for SubstrateWeight { // Storage: Skipped Metadata (r:0 w:0) /// The range of component `n` is `[0, 8]`. fn seal_get_storage_per_kb(n: u32, ) -> Weight { - // Minimum execution time: 459_294 nanoseconds. - Weight::from_ref_time(579_698_524) - // Standard Error: 1_111_681 - .saturating_add(Weight::from_ref_time(159_276_087).saturating_mul(n.into())) + // Minimum execution time: 375_487 nanoseconds. + Weight::from_ref_time(497_019_365) + // Standard Error: 1_141_672 + .saturating_add(Weight::from_ref_time(152_354_482).saturating_mul(n.into())) .saturating_add(T::DbWeight::get().reads(51)) .saturating_add(T::DbWeight::get().reads((7_u64).saturating_mul(n.into()))) .saturating_add(T::DbWeight::get().writes(3)) @@ -727,10 +727,10 @@ impl WeightInfo for SubstrateWeight { // Storage: Skipped Metadata (r:0 w:0) /// The range of component `r` is `[0, 10]`. fn seal_contains_storage(r: u32, ) -> Weight { - // Minimum execution time: 381_700 nanoseconds. - Weight::from_ref_time(352_544_675) - // Standard Error: 401_504 - .saturating_add(Weight::from_ref_time(304_380_106).saturating_mul(r.into())) + // Minimum execution time: 294_716 nanoseconds. + Weight::from_ref_time(266_187_186) + // Standard Error: 395_323 + .saturating_add(Weight::from_ref_time(309_907_221).saturating_mul(r.into())) .saturating_add(T::DbWeight::get().reads(6)) .saturating_add(T::DbWeight::get().reads((80_u64).saturating_mul(r.into()))) .saturating_add(T::DbWeight::get().writes(3)) @@ -738,10 +738,10 @@ impl WeightInfo for SubstrateWeight { // Storage: Skipped Metadata (r:0 w:0) /// The range of component `n` is `[0, 8]`. fn seal_contains_storage_per_kb(n: u32, ) -> Weight { - // Minimum execution time: 455_595 nanoseconds. - Weight::from_ref_time(560_428_166) - // Standard Error: 991_088 - .saturating_add(Weight::from_ref_time(61_810_610).saturating_mul(n.into())) + // Minimum execution time: 371_667 nanoseconds. + Weight::from_ref_time(478_075_482) + // Standard Error: 979_116 + .saturating_add(Weight::from_ref_time(62_075_707).saturating_mul(n.into())) .saturating_add(T::DbWeight::get().reads(51)) .saturating_add(T::DbWeight::get().reads((7_u64).saturating_mul(n.into()))) .saturating_add(T::DbWeight::get().writes(3)) @@ -749,10 +749,10 @@ impl WeightInfo for SubstrateWeight { // Storage: Skipped Metadata (r:0 w:0) /// The range of component `r` is `[0, 10]`. fn seal_take_storage(r: u32, ) -> Weight { - // Minimum execution time: 382_000 nanoseconds. - Weight::from_ref_time(336_164_219) - // Standard Error: 601_744 - .saturating_add(Weight::from_ref_time(406_198_079).saturating_mul(r.into())) + // Minimum execution time: 292_147 nanoseconds. + Weight::from_ref_time(254_374_598) + // Standard Error: 442_367 + .saturating_add(Weight::from_ref_time(413_674_705).saturating_mul(r.into())) .saturating_add(T::DbWeight::get().reads(6)) .saturating_add(T::DbWeight::get().reads((80_u64).saturating_mul(r.into()))) .saturating_add(T::DbWeight::get().writes(3)) @@ -761,10 +761,10 @@ impl WeightInfo for SubstrateWeight { // Storage: Skipped Metadata (r:0 w:0) /// The range of component `n` is `[0, 8]`. fn seal_take_storage_per_kb(n: u32, ) -> Weight { - // Minimum execution time: 482_335 nanoseconds. - Weight::from_ref_time(634_245_177) - // Standard Error: 1_418_845 - .saturating_add(Weight::from_ref_time(164_352_113).saturating_mul(n.into())) + // Minimum execution time: 398_554 nanoseconds. + Weight::from_ref_time(553_715_911) + // Standard Error: 1_415_272 + .saturating_add(Weight::from_ref_time(158_254_544).saturating_mul(n.into())) .saturating_add(T::DbWeight::get().reads(51)) .saturating_add(T::DbWeight::get().reads((7_u64).saturating_mul(n.into()))) .saturating_add(T::DbWeight::get().writes(48)) @@ -777,10 +777,10 @@ impl WeightInfo for SubstrateWeight { // Storage: System EventTopics (r:2 w:2) /// The range of component `r` is `[0, 20]`. fn seal_transfer(r: u32, ) -> Weight { - // Minimum execution time: 382_142 nanoseconds. - Weight::from_ref_time(317_581_708) - // Standard Error: 682_156 - .saturating_add(Weight::from_ref_time(1_305_289_569).saturating_mul(r.into())) + // Minimum execution time: 295_081 nanoseconds. + Weight::from_ref_time(253_461_581) + // Standard Error: 740_198 + .saturating_add(Weight::from_ref_time(1_369_153_623).saturating_mul(r.into())) .saturating_add(T::DbWeight::get().reads(7)) .saturating_add(T::DbWeight::get().reads((80_u64).saturating_mul(r.into()))) .saturating_add(T::DbWeight::get().writes(4)) @@ -793,10 +793,10 @@ impl WeightInfo for SubstrateWeight { // Storage: System EventTopics (r:2 w:2) /// The range of component `r` is `[0, 20]`. fn seal_call(r: u32, ) -> Weight { - // Minimum execution time: 383_580 nanoseconds. - Weight::from_ref_time(384_176_000) - // Standard Error: 6_483_482 - .saturating_add(Weight::from_ref_time(28_047_685_517).saturating_mul(r.into())) + // Minimum execution time: 296_089 nanoseconds. + Weight::from_ref_time(297_193_000) + // Standard Error: 6_571_547 + .saturating_add(Weight::from_ref_time(21_080_280_397).saturating_mul(r.into())) .saturating_add(T::DbWeight::get().reads(7)) .saturating_add(T::DbWeight::get().reads((160_u64).saturating_mul(r.into()))) .saturating_add(T::DbWeight::get().writes(3)) @@ -809,10 +809,10 @@ impl WeightInfo for SubstrateWeight { // Storage: System EventTopics (r:2 w:2) /// The range of component `r` is `[0, 20]`. fn seal_delegate_call(r: u32, ) -> Weight { - // Minimum execution time: 384_523 nanoseconds. - Weight::from_ref_time(385_105_000) - // Standard Error: 6_156_142 - .saturating_add(Weight::from_ref_time(27_780_652_513).saturating_mul(r.into())) + // Minimum execution time: 296_828 nanoseconds. + Weight::from_ref_time(297_685_000) + // Standard Error: 6_864_880 + .saturating_add(Weight::from_ref_time(20_770_321_946).saturating_mul(r.into())) .saturating_add(T::DbWeight::get().reads(6)) .saturating_add(T::DbWeight::get().reads((150_u64).saturating_mul(r.into()))) .saturating_add(T::DbWeight::get().writes(3)) @@ -826,12 +826,12 @@ impl WeightInfo for SubstrateWeight { /// The range of component `t` is `[0, 1]`. /// The range of component `c` is `[0, 1024]`. fn seal_call_per_transfer_clone_kb(t: u32, c: u32, ) -> Weight { - // Minimum execution time: 9_509_961 nanoseconds. - Weight::from_ref_time(8_509_991_348) - // Standard Error: 6_614_757 - .saturating_add(Weight::from_ref_time(1_244_514_376).saturating_mul(t.into())) - // Standard Error: 9_918 - .saturating_add(Weight::from_ref_time(9_856_517).saturating_mul(c.into())) + // Minimum execution time: 9_824_671 nanoseconds. + Weight::from_ref_time(8_713_861_450) + // Standard Error: 7_388_925 + .saturating_add(Weight::from_ref_time(1_327_819_806).saturating_mul(t.into())) + // Standard Error: 11_079 + .saturating_add(Weight::from_ref_time(9_789_514).saturating_mul(c.into())) .saturating_add(T::DbWeight::get().reads(167)) .saturating_add(T::DbWeight::get().reads((81_u64).saturating_mul(t.into()))) .saturating_add(T::DbWeight::get().writes(163)) @@ -846,10 +846,10 @@ impl WeightInfo for SubstrateWeight { // Storage: Contracts OwnerInfoOf (r:80 w:80) /// The range of component `r` is `[0, 20]`. fn seal_instantiate(r: u32, ) -> Weight { - // Minimum execution time: 384_604 nanoseconds. - Weight::from_ref_time(385_278_000) - // Standard Error: 21_140_468 - .saturating_add(Weight::from_ref_time(33_100_726_150).saturating_mul(r.into())) + // Minimum execution time: 293_374 nanoseconds. + Weight::from_ref_time(302_067_000) + // Standard Error: 20_137_097 + .saturating_add(Weight::from_ref_time(26_444_812_817).saturating_mul(r.into())) .saturating_add(T::DbWeight::get().reads(8)) .saturating_add(T::DbWeight::get().reads((400_u64).saturating_mul(r.into()))) .saturating_add(T::DbWeight::get().writes(5)) @@ -866,12 +866,14 @@ impl WeightInfo for SubstrateWeight { /// The range of component `i` is `[0, 960]`. /// The range of component `s` is `[0, 960]`. fn seal_instantiate_per_transfer_input_salt_kb(t: u32, i: u32, s: u32, ) -> Weight { - // Minimum execution time: 129_699_480 nanoseconds. - Weight::from_ref_time(10_187_699_005) - // Standard Error: 155_040 - .saturating_add(Weight::from_ref_time(125_284_310).saturating_mul(i.into())) - // Standard Error: 155_040 - .saturating_add(Weight::from_ref_time(125_850_564).saturating_mul(s.into())) + // Minimum execution time: 126_456_770 nanoseconds. + Weight::from_ref_time(9_223_715_752) + // Standard Error: 96_040_318 + .saturating_add(Weight::from_ref_time(19_550_519).saturating_mul(t.into())) + // Standard Error: 156_614 + .saturating_add(Weight::from_ref_time(122_958_298).saturating_mul(i.into())) + // Standard Error: 156_614 + .saturating_add(Weight::from_ref_time(123_316_079).saturating_mul(s.into())) .saturating_add(T::DbWeight::get().reads(249)) .saturating_add(T::DbWeight::get().reads((1_u64).saturating_mul(t.into()))) .saturating_add(T::DbWeight::get().writes(247)) @@ -884,10 +886,10 @@ impl WeightInfo for SubstrateWeight { // Storage: System EventTopics (r:2 w:2) /// The range of component `r` is `[0, 1]`. fn seal_hash_sha2_256(r: u32, ) -> Weight { - // Minimum execution time: 380_397 nanoseconds. - Weight::from_ref_time(382_881_855) - // Standard Error: 290_948 - .saturating_add(Weight::from_ref_time(41_017_644).saturating_mul(r.into())) + // Minimum execution time: 289_275 nanoseconds. + Weight::from_ref_time(297_803_961) + // Standard Error: 303_405 + .saturating_add(Weight::from_ref_time(40_800_138).saturating_mul(r.into())) .saturating_add(T::DbWeight::get().reads(6)) .saturating_add(T::DbWeight::get().writes(3)) } @@ -898,10 +900,10 @@ impl WeightInfo for SubstrateWeight { // Storage: System EventTopics (r:2 w:2) /// The range of component `n` is `[0, 1024]`. fn seal_hash_sha2_256_per_kb(n: u32, ) -> Weight { - // Minimum execution time: 422_439 nanoseconds. - Weight::from_ref_time(423_480_000) - // Standard Error: 56_072 - .saturating_add(Weight::from_ref_time(329_103_825).saturating_mul(n.into())) + // Minimum execution time: 336_077 nanoseconds. + Weight::from_ref_time(336_846_000) + // Standard Error: 51_610 + .saturating_add(Weight::from_ref_time(322_208_787).saturating_mul(n.into())) .saturating_add(T::DbWeight::get().reads(6)) .saturating_add(T::DbWeight::get().writes(3)) } @@ -912,10 +914,10 @@ impl WeightInfo for SubstrateWeight { // Storage: System EventTopics (r:2 w:2) /// The range of component `r` is `[0, 1]`. fn seal_hash_keccak_256(r: u32, ) -> Weight { - // Minimum execution time: 380_433 nanoseconds. - Weight::from_ref_time(382_325_624) - // Standard Error: 139_248 - .saturating_add(Weight::from_ref_time(53_159_175).saturating_mul(r.into())) + // Minimum execution time: 290_189 nanoseconds. + Weight::from_ref_time(298_454_259) + // Standard Error: 252_946 + .saturating_add(Weight::from_ref_time(55_034_240).saturating_mul(r.into())) .saturating_add(T::DbWeight::get().reads(6)) .saturating_add(T::DbWeight::get().writes(3)) } @@ -926,10 +928,10 @@ impl WeightInfo for SubstrateWeight { // Storage: System EventTopics (r:2 w:2) /// The range of component `n` is `[0, 1024]`. fn seal_hash_keccak_256_per_kb(n: u32, ) -> Weight { - // Minimum execution time: 434_834 nanoseconds. - Weight::from_ref_time(435_383_000) - // Standard Error: 59_824 - .saturating_add(Weight::from_ref_time(251_297_967).saturating_mul(n.into())) + // Minimum execution time: 350_030 nanoseconds. + Weight::from_ref_time(351_356_000) + // Standard Error: 57_422 + .saturating_add(Weight::from_ref_time(256_912_844).saturating_mul(n.into())) .saturating_add(T::DbWeight::get().reads(6)) .saturating_add(T::DbWeight::get().writes(3)) } @@ -940,10 +942,10 @@ impl WeightInfo for SubstrateWeight { // Storage: System EventTopics (r:2 w:2) /// The range of component `r` is `[0, 1]`. fn seal_hash_blake2_256(r: u32, ) -> Weight { - // Minimum execution time: 379_088 nanoseconds. - Weight::from_ref_time(381_627_077) - // Standard Error: 152_561 - .saturating_add(Weight::from_ref_time(31_696_922).saturating_mul(r.into())) + // Minimum execution time: 288_480 nanoseconds. + Weight::from_ref_time(297_613_263) + // Standard Error: 456_736 + .saturating_add(Weight::from_ref_time(38_100_936).saturating_mul(r.into())) .saturating_add(T::DbWeight::get().reads(6)) .saturating_add(T::DbWeight::get().writes(3)) } @@ -954,10 +956,10 @@ impl WeightInfo for SubstrateWeight { // Storage: System EventTopics (r:2 w:2) /// The range of component `n` is `[0, 1024]`. fn seal_hash_blake2_256_per_kb(n: u32, ) -> Weight { - // Minimum execution time: 411_630 nanoseconds. - Weight::from_ref_time(412_354_000) - // Standard Error: 50_788 - .saturating_add(Weight::from_ref_time(103_105_715).saturating_mul(n.into())) + // Minimum execution time: 321_195 nanoseconds. + Weight::from_ref_time(325_561_000) + // Standard Error: 46_289 + .saturating_add(Weight::from_ref_time(99_536_902).saturating_mul(n.into())) .saturating_add(T::DbWeight::get().reads(6)) .saturating_add(T::DbWeight::get().writes(3)) } @@ -968,10 +970,10 @@ impl WeightInfo for SubstrateWeight { // Storage: System EventTopics (r:2 w:2) /// The range of component `r` is `[0, 1]`. fn seal_hash_blake2_128(r: u32, ) -> Weight { - // Minimum execution time: 379_549 nanoseconds. - Weight::from_ref_time(382_406_346) - // Standard Error: 701_449 - .saturating_add(Weight::from_ref_time(31_066_353).saturating_mul(r.into())) + // Minimum execution time: 287_659 nanoseconds. + Weight::from_ref_time(295_963_340) + // Standard Error: 433_889 + .saturating_add(Weight::from_ref_time(30_251_859).saturating_mul(r.into())) .saturating_add(T::DbWeight::get().reads(6)) .saturating_add(T::DbWeight::get().writes(3)) } @@ -982,10 +984,10 @@ impl WeightInfo for SubstrateWeight { // Storage: System EventTopics (r:2 w:2) /// The range of component `n` is `[0, 1024]`. fn seal_hash_blake2_128_per_kb(n: u32, ) -> Weight { - // Minimum execution time: 412_112 nanoseconds. - Weight::from_ref_time(412_710_000) - // Standard Error: 54_875 - .saturating_add(Weight::from_ref_time(103_169_035).saturating_mul(n.into())) + // Minimum execution time: 324_298 nanoseconds. + Weight::from_ref_time(325_738_000) + // Standard Error: 48_563 + .saturating_add(Weight::from_ref_time(99_479_838).saturating_mul(n.into())) .saturating_add(T::DbWeight::get().reads(6)) .saturating_add(T::DbWeight::get().writes(3)) } @@ -996,10 +998,10 @@ impl WeightInfo for SubstrateWeight { // Storage: System EventTopics (r:2 w:2) /// The range of component `r` is `[0, 1]`. fn seal_ecdsa_recover(r: u32, ) -> Weight { - // Minimum execution time: 381_994 nanoseconds. - Weight::from_ref_time(383_832_551) - // Standard Error: 676_656 - .saturating_add(Weight::from_ref_time(3_020_035_748).saturating_mul(r.into())) + // Minimum execution time: 294_257 nanoseconds. + Weight::from_ref_time(299_467_620) + // Standard Error: 589_749 + .saturating_add(Weight::from_ref_time(3_015_389_579).saturating_mul(r.into())) .saturating_add(T::DbWeight::get().reads(6)) .saturating_add(T::DbWeight::get().writes(3)) } @@ -1010,10 +1012,10 @@ impl WeightInfo for SubstrateWeight { // Storage: System EventTopics (r:2 w:2) /// The range of component `r` is `[0, 1]`. fn seal_ecdsa_to_eth_address(r: u32, ) -> Weight { - // Minimum execution time: 381_472 nanoseconds. - Weight::from_ref_time(383_590_834) - // Standard Error: 538_574 - .saturating_add(Weight::from_ref_time(2_077_926_265).saturating_mul(r.into())) + // Minimum execution time: 289_480 nanoseconds. + Weight::from_ref_time(298_762_412) + // Standard Error: 397_004 + .saturating_add(Weight::from_ref_time(743_937_087).saturating_mul(r.into())) .saturating_add(T::DbWeight::get().reads(6)) .saturating_add(T::DbWeight::get().writes(3)) } @@ -1025,10 +1027,10 @@ impl WeightInfo for SubstrateWeight { // Storage: Contracts OwnerInfoOf (r:16 w:16) /// The range of component `r` is `[0, 20]`. fn seal_set_code_hash(r: u32, ) -> Weight { - // Minimum execution time: 381_527 nanoseconds. - Weight::from_ref_time(382_291_000) - // Standard Error: 2_760_840 - .saturating_add(Weight::from_ref_time(1_356_115_009).saturating_mul(r.into())) + // Minimum execution time: 293_494 nanoseconds. + Weight::from_ref_time(297_756_000) + // Standard Error: 2_731_227 + .saturating_add(Weight::from_ref_time(1_387_380_436).saturating_mul(r.into())) .saturating_add(T::DbWeight::get().reads(6)) .saturating_add(T::DbWeight::get().reads((225_u64).saturating_mul(r.into()))) .saturating_add(T::DbWeight::get().writes(3)) @@ -1041,10 +1043,10 @@ impl WeightInfo for SubstrateWeight { // Storage: System EventTopics (r:2 w:2) /// The range of component `r` is `[0, 20]`. fn seal_reentrance_count(r: u32, ) -> Weight { - // Minimum execution time: 382_722 nanoseconds. - Weight::from_ref_time(387_231_409) - // Standard Error: 28_817 - .saturating_add(Weight::from_ref_time(11_349_809).saturating_mul(r.into())) + // Minimum execution time: 295_339 nanoseconds. + Weight::from_ref_time(301_577_907) + // Standard Error: 23_836 + .saturating_add(Weight::from_ref_time(10_876_508).saturating_mul(r.into())) .saturating_add(T::DbWeight::get().reads(6)) .saturating_add(T::DbWeight::get().writes(3)) } @@ -1055,10 +1057,10 @@ impl WeightInfo for SubstrateWeight { // Storage: System EventTopics (r:2 w:2) /// The range of component `r` is `[0, 20]`. fn seal_account_reentrance_count(r: u32, ) -> Weight { - // Minimum execution time: 383_568 nanoseconds. - Weight::from_ref_time(419_835_108) - // Standard Error: 125_982 - .saturating_add(Weight::from_ref_time(25_241_800).saturating_mul(r.into())) + // Minimum execution time: 297_096 nanoseconds. + Weight::from_ref_time(336_319_823) + // Standard Error: 128_941 + .saturating_add(Weight::from_ref_time(25_211_374).saturating_mul(r.into())) .saturating_add(T::DbWeight::get().reads(6)) .saturating_add(T::DbWeight::get().writes(3)) } @@ -1070,376 +1072,376 @@ impl WeightInfo for SubstrateWeight { // Storage: Contracts Nonce (r:1 w:1) /// The range of component `r` is `[0, 20]`. fn seal_instantiation_nonce(r: u32, ) -> Weight { - // Minimum execution time: 382_084 nanoseconds. - Weight::from_ref_time(388_155_568) - // Standard Error: 29_161 - .saturating_add(Weight::from_ref_time(9_015_217).saturating_mul(r.into())) + // Minimum execution time: 293_819 nanoseconds. + Weight::from_ref_time(302_765_659) + // Standard Error: 28_518 + .saturating_add(Weight::from_ref_time(9_325_552).saturating_mul(r.into())) .saturating_add(T::DbWeight::get().reads(7)) .saturating_add(T::DbWeight::get().writes(4)) } /// The range of component `r` is `[0, 50]`. fn instr_i64const(r: u32, ) -> Weight { - // Minimum execution time: 593 nanoseconds. - Weight::from_ref_time(816_706) - // Standard Error: 173 - .saturating_add(Weight::from_ref_time(344_732).saturating_mul(r.into())) + // Minimum execution time: 805 nanoseconds. + Weight::from_ref_time(1_002_066) + // Standard Error: 609 + .saturating_add(Weight::from_ref_time(346_608).saturating_mul(r.into())) } /// The range of component `r` is `[0, 50]`. fn instr_i64load(r: u32, ) -> Weight { - // Minimum execution time: 705 nanoseconds. - Weight::from_ref_time(1_191_205) - // Standard Error: 600 - .saturating_add(Weight::from_ref_time(986_102).saturating_mul(r.into())) + // Minimum execution time: 917 nanoseconds. + Weight::from_ref_time(1_247_710) + // Standard Error: 1_069 + .saturating_add(Weight::from_ref_time(984_608).saturating_mul(r.into())) } /// The range of component `r` is `[0, 50]`. fn instr_i64store(r: u32, ) -> Weight { - // Minimum execution time: 717 nanoseconds. - Weight::from_ref_time(1_019_448) - // Standard Error: 421 - .saturating_add(Weight::from_ref_time(882_531).saturating_mul(r.into())) + // Minimum execution time: 917 nanoseconds. + Weight::from_ref_time(1_244_315) + // Standard Error: 524 + .saturating_add(Weight::from_ref_time(882_167).saturating_mul(r.into())) } /// The range of component `r` is `[0, 50]`. fn instr_select(r: u32, ) -> Weight { - // Minimum execution time: 642 nanoseconds. - Weight::from_ref_time(917_880) - // Standard Error: 362 - .saturating_add(Weight::from_ref_time(957_235).saturating_mul(r.into())) + // Minimum execution time: 791 nanoseconds. + Weight::from_ref_time(1_119_053) + // Standard Error: 379 + .saturating_add(Weight::from_ref_time(956_169).saturating_mul(r.into())) } /// The range of component `r` is `[0, 50]`. fn instr_if(r: u32, ) -> Weight { - // Minimum execution time: 630 nanoseconds. - Weight::from_ref_time(694_427) - // Standard Error: 1_506 - .saturating_add(Weight::from_ref_time(1_298_411).saturating_mul(r.into())) + // Minimum execution time: 782 nanoseconds. + Weight::from_ref_time(773_373) + // Standard Error: 463 + .saturating_add(Weight::from_ref_time(1_299_306).saturating_mul(r.into())) } /// The range of component `r` is `[0, 50]`. fn instr_br(r: u32, ) -> Weight { - // Minimum execution time: 634 nanoseconds. - Weight::from_ref_time(1_101_754) - // Standard Error: 840 - .saturating_add(Weight::from_ref_time(526_433).saturating_mul(r.into())) + // Minimum execution time: 743 nanoseconds. + Weight::from_ref_time(1_273_896) + // Standard Error: 925 + .saturating_add(Weight::from_ref_time(527_015).saturating_mul(r.into())) } /// The range of component `r` is `[0, 50]`. fn instr_br_if(r: u32, ) -> Weight { - // Minimum execution time: 651 nanoseconds. - Weight::from_ref_time(790_908) - // Standard Error: 849 - .saturating_add(Weight::from_ref_time(800_188).saturating_mul(r.into())) + // Minimum execution time: 761 nanoseconds. + Weight::from_ref_time(929_565) + // Standard Error: 1_275 + .saturating_add(Weight::from_ref_time(805_592).saturating_mul(r.into())) } /// The range of component `r` is `[0, 50]`. fn instr_br_table(r: u32, ) -> Weight { - // Minimum execution time: 622 nanoseconds. - Weight::from_ref_time(416_266) - // Standard Error: 1_574 - .saturating_add(Weight::from_ref_time(1_080_225).saturating_mul(r.into())) + // Minimum execution time: 813 nanoseconds. + Weight::from_ref_time(703_802) + // Standard Error: 1_665 + .saturating_add(Weight::from_ref_time(1_074_321).saturating_mul(r.into())) } /// The range of component `e` is `[1, 256]`. fn instr_br_table_per_entry(e: u32, ) -> Weight { - // Minimum execution time: 2_131 nanoseconds. - Weight::from_ref_time(2_540_446) - // Standard Error: 75 - .saturating_add(Weight::from_ref_time(4_997).saturating_mul(e.into())) + // Minimum execution time: 2_347 nanoseconds. + Weight::from_ref_time(2_752_638) + // Standard Error: 69 + .saturating_add(Weight::from_ref_time(4_771).saturating_mul(e.into())) } /// The range of component `r` is `[0, 50]`. fn instr_call(r: u32, ) -> Weight { - // Minimum execution time: 650 nanoseconds. - Weight::from_ref_time(1_577_978) - // Standard Error: 2_696 - .saturating_add(Weight::from_ref_time(2_204_044).saturating_mul(r.into())) + // Minimum execution time: 826 nanoseconds. + Weight::from_ref_time(1_784_596) + // Standard Error: 1_646 + .saturating_add(Weight::from_ref_time(2_189_993).saturating_mul(r.into())) } /// The range of component `r` is `[0, 50]`. fn instr_call_indirect(r: u32, ) -> Weight { - // Minimum execution time: 742 nanoseconds. - Weight::from_ref_time(1_947_575) - // Standard Error: 1_651 - .saturating_add(Weight::from_ref_time(2_799_445).saturating_mul(r.into())) + // Minimum execution time: 926 nanoseconds. + Weight::from_ref_time(1_898_358) + // Standard Error: 2_678 + .saturating_add(Weight::from_ref_time(2_811_379).saturating_mul(r.into())) } /// The range of component `p` is `[0, 128]`. fn instr_call_indirect_per_param(p: u32, ) -> Weight { - // Minimum execution time: 4_230 nanoseconds. - Weight::from_ref_time(5_079_432) - // Standard Error: 315 - .saturating_add(Weight::from_ref_time(179_278).saturating_mul(p.into())) + // Minimum execution time: 4_428 nanoseconds. + Weight::from_ref_time(5_257_445) + // Standard Error: 306 + .saturating_add(Weight::from_ref_time(178_882).saturating_mul(p.into())) } /// The range of component `l` is `[0, 1024]`. fn instr_call_per_local(l: u32, ) -> Weight { - // Minimum execution time: 2_830 nanoseconds. - Weight::from_ref_time(3_601_633) - // Standard Error: 31 - .saturating_add(Weight::from_ref_time(92_499).saturating_mul(l.into())) + // Minimum execution time: 2_945 nanoseconds. + Weight::from_ref_time(4_240_619) + // Standard Error: 39 + .saturating_add(Weight::from_ref_time(46_640).saturating_mul(l.into())) } /// The range of component `r` is `[0, 50]`. fn instr_local_get(r: u32, ) -> Weight { - // Minimum execution time: 2_896 nanoseconds. - Weight::from_ref_time(3_137_247) - // Standard Error: 245 - .saturating_add(Weight::from_ref_time(364_431).saturating_mul(r.into())) + // Minimum execution time: 2_204 nanoseconds. + Weight::from_ref_time(2_430_272) + // Standard Error: 270 + .saturating_add(Weight::from_ref_time(365_718).saturating_mul(r.into())) } /// The range of component `r` is `[0, 50]`. fn instr_local_set(r: u32, ) -> Weight { - // Minimum execution time: 2_795 nanoseconds. - Weight::from_ref_time(3_199_878) - // Standard Error: 549 - .saturating_add(Weight::from_ref_time(380_524).saturating_mul(r.into())) + // Minimum execution time: 2_117 nanoseconds. + Weight::from_ref_time(2_429_354) + // Standard Error: 240 + .saturating_add(Weight::from_ref_time(381_830).saturating_mul(r.into())) } /// The range of component `r` is `[0, 50]`. fn instr_local_tee(r: u32, ) -> Weight { - // Minimum execution time: 2_807 nanoseconds. - Weight::from_ref_time(3_130_120) - // Standard Error: 319 - .saturating_add(Weight::from_ref_time(525_857).saturating_mul(r.into())) + // Minimum execution time: 2_123 nanoseconds. + Weight::from_ref_time(2_460_016) + // Standard Error: 262 + .saturating_add(Weight::from_ref_time(526_554).saturating_mul(r.into())) } /// The range of component `r` is `[0, 50]`. fn instr_global_get(r: u32, ) -> Weight { - // Minimum execution time: 678 nanoseconds. - Weight::from_ref_time(1_013_348) - // Standard Error: 362 - .saturating_add(Weight::from_ref_time(810_232).saturating_mul(r.into())) + // Minimum execution time: 892 nanoseconds. + Weight::from_ref_time(1_234_618) + // Standard Error: 485 + .saturating_add(Weight::from_ref_time(813_721).saturating_mul(r.into())) } /// The range of component `r` is `[0, 50]`. fn instr_global_set(r: u32, ) -> Weight { - // Minimum execution time: 624 nanoseconds. - Weight::from_ref_time(973_583) - // Standard Error: 373 - .saturating_add(Weight::from_ref_time(829_360).saturating_mul(r.into())) + // Minimum execution time: 901 nanoseconds. + Weight::from_ref_time(1_182_838) + // Standard Error: 482 + .saturating_add(Weight::from_ref_time(831_308).saturating_mul(r.into())) } /// The range of component `r` is `[0, 50]`. fn instr_memory_current(r: u32, ) -> Weight { - // Minimum execution time: 698 nanoseconds. - Weight::from_ref_time(904_862) - // Standard Error: 360 - .saturating_add(Weight::from_ref_time(694_614).saturating_mul(r.into())) + // Minimum execution time: 929 nanoseconds. + Weight::from_ref_time(1_221_388) + // Standard Error: 434 + .saturating_add(Weight::from_ref_time(693_301).saturating_mul(r.into())) } /// The range of component `r` is `[0, 1]`. fn instr_memory_grow(r: u32, ) -> Weight { - // Minimum execution time: 642 nanoseconds. - Weight::from_ref_time(735_085) - // Standard Error: 105_815 - .saturating_add(Weight::from_ref_time(233_816_514).saturating_mul(r.into())) + // Minimum execution time: 808 nanoseconds. + Weight::from_ref_time(906_944) + // Standard Error: 6_164 + .saturating_add(Weight::from_ref_time(187_445_255).saturating_mul(r.into())) } /// The range of component `r` is `[0, 50]`. fn instr_i64clz(r: u32, ) -> Weight { - // Minimum execution time: 606 nanoseconds. - Weight::from_ref_time(850_590) - // Standard Error: 262 - .saturating_add(Weight::from_ref_time(507_721).saturating_mul(r.into())) + // Minimum execution time: 807 nanoseconds. + Weight::from_ref_time(1_064_776) + // Standard Error: 287 + .saturating_add(Weight::from_ref_time(507_278).saturating_mul(r.into())) } /// The range of component `r` is `[0, 50]`. fn instr_i64ctz(r: u32, ) -> Weight { - // Minimum execution time: 663 nanoseconds. - Weight::from_ref_time(853_060) - // Standard Error: 250 - .saturating_add(Weight::from_ref_time(514_225).saturating_mul(r.into())) + // Minimum execution time: 776 nanoseconds. + Weight::from_ref_time(1_033_205) + // Standard Error: 443 + .saturating_add(Weight::from_ref_time(512_445).saturating_mul(r.into())) } /// The range of component `r` is `[0, 50]`. fn instr_i64popcnt(r: u32, ) -> Weight { - // Minimum execution time: 605 nanoseconds. - Weight::from_ref_time(849_563) - // Standard Error: 275 - .saturating_add(Weight::from_ref_time(511_494).saturating_mul(r.into())) + // Minimum execution time: 909 nanoseconds. + Weight::from_ref_time(1_089_083) + // Standard Error: 335 + .saturating_add(Weight::from_ref_time(510_392).saturating_mul(r.into())) } /// The range of component `r` is `[0, 50]`. fn instr_i64eqz(r: u32, ) -> Weight { - // Minimum execution time: 618 nanoseconds. - Weight::from_ref_time(839_855) - // Standard Error: 228 - .saturating_add(Weight::from_ref_time(524_614).saturating_mul(r.into())) + // Minimum execution time: 791 nanoseconds. + Weight::from_ref_time(1_063_118) + // Standard Error: 210 + .saturating_add(Weight::from_ref_time(522_817).saturating_mul(r.into())) } /// The range of component `r` is `[0, 50]`. fn instr_i64extendsi32(r: u32, ) -> Weight { - // Minimum execution time: 637 nanoseconds. - Weight::from_ref_time(860_326) + // Minimum execution time: 789 nanoseconds. + Weight::from_ref_time(1_056_006) // Standard Error: 240 - .saturating_add(Weight::from_ref_time(504_847).saturating_mul(r.into())) + .saturating_add(Weight::from_ref_time(505_198).saturating_mul(r.into())) } /// The range of component `r` is `[0, 50]`. fn instr_i64extendui32(r: u32, ) -> Weight { - // Minimum execution time: 623 nanoseconds. - Weight::from_ref_time(844_585) - // Standard Error: 235 - .saturating_add(Weight::from_ref_time(505_821).saturating_mul(r.into())) + // Minimum execution time: 804 nanoseconds. + Weight::from_ref_time(1_087_882) + // Standard Error: 234 + .saturating_add(Weight::from_ref_time(503_830).saturating_mul(r.into())) } /// The range of component `r` is `[0, 50]`. fn instr_i32wrapi64(r: u32, ) -> Weight { - // Minimum execution time: 672 nanoseconds. - Weight::from_ref_time(826_784) - // Standard Error: 225 - .saturating_add(Weight::from_ref_time(504_632).saturating_mul(r.into())) + // Minimum execution time: 756 nanoseconds. + Weight::from_ref_time(1_050_994) + // Standard Error: 172 + .saturating_add(Weight::from_ref_time(503_343).saturating_mul(r.into())) } /// The range of component `r` is `[0, 50]`. fn instr_i64eq(r: u32, ) -> Weight { - // Minimum execution time: 642 nanoseconds. - Weight::from_ref_time(867_080) - // Standard Error: 231 - .saturating_add(Weight::from_ref_time(732_430).saturating_mul(r.into())) + // Minimum execution time: 775 nanoseconds. + Weight::from_ref_time(1_061_228) + // Standard Error: 232 + .saturating_add(Weight::from_ref_time(730_751).saturating_mul(r.into())) } /// The range of component `r` is `[0, 50]`. fn instr_i64ne(r: u32, ) -> Weight { - // Minimum execution time: 639 nanoseconds. - Weight::from_ref_time(866_094) - // Standard Error: 272 - .saturating_add(Weight::from_ref_time(732_560).saturating_mul(r.into())) + // Minimum execution time: 809 nanoseconds. + Weight::from_ref_time(1_080_957) + // Standard Error: 320 + .saturating_add(Weight::from_ref_time(732_246).saturating_mul(r.into())) } /// The range of component `r` is `[0, 50]`. fn instr_i64lts(r: u32, ) -> Weight { - // Minimum execution time: 619 nanoseconds. - Weight::from_ref_time(928_672) - // Standard Error: 484 - .saturating_add(Weight::from_ref_time(739_523).saturating_mul(r.into())) + // Minimum execution time: 808 nanoseconds. + Weight::from_ref_time(1_055_752) + // Standard Error: 496 + .saturating_add(Weight::from_ref_time(740_084).saturating_mul(r.into())) } /// The range of component `r` is `[0, 50]`. fn instr_i64ltu(r: u32, ) -> Weight { - // Minimum execution time: 612 nanoseconds. - Weight::from_ref_time(863_312) - // Standard Error: 328 - .saturating_add(Weight::from_ref_time(743_687).saturating_mul(r.into())) + // Minimum execution time: 792 nanoseconds. + Weight::from_ref_time(1_111_972) + // Standard Error: 270 + .saturating_add(Weight::from_ref_time(741_581).saturating_mul(r.into())) } /// The range of component `r` is `[0, 50]`. fn instr_i64gts(r: u32, ) -> Weight { - // Minimum execution time: 648 nanoseconds. - Weight::from_ref_time(931_331) - // Standard Error: 612 - .saturating_add(Weight::from_ref_time(747_653).saturating_mul(r.into())) + // Minimum execution time: 862 nanoseconds. + Weight::from_ref_time(1_111_246) + // Standard Error: 263 + .saturating_add(Weight::from_ref_time(746_026).saturating_mul(r.into())) } /// The range of component `r` is `[0, 50]`. fn instr_i64gtu(r: u32, ) -> Weight { - // Minimum execution time: 632 nanoseconds. - Weight::from_ref_time(868_901) - // Standard Error: 276 - .saturating_add(Weight::from_ref_time(744_778).saturating_mul(r.into())) + // Minimum execution time: 810 nanoseconds. + Weight::from_ref_time(1_094_933) + // Standard Error: 271 + .saturating_add(Weight::from_ref_time(743_645).saturating_mul(r.into())) } /// The range of component `r` is `[0, 50]`. fn instr_i64les(r: u32, ) -> Weight { - // Minimum execution time: 629 nanoseconds. - Weight::from_ref_time(898_516) - // Standard Error: 288 - .saturating_add(Weight::from_ref_time(734_393).saturating_mul(r.into())) + // Minimum execution time: 769 nanoseconds. + Weight::from_ref_time(819_933) + // Standard Error: 4_528 + .saturating_add(Weight::from_ref_time(751_599).saturating_mul(r.into())) } /// The range of component `r` is `[0, 50]`. fn instr_i64leu(r: u32, ) -> Weight { - // Minimum execution time: 603 nanoseconds. - Weight::from_ref_time(853_744) - // Standard Error: 204 - .saturating_add(Weight::from_ref_time(739_499).saturating_mul(r.into())) + // Minimum execution time: 797 nanoseconds. + Weight::from_ref_time(1_107_352) + // Standard Error: 344 + .saturating_add(Weight::from_ref_time(738_684).saturating_mul(r.into())) } /// The range of component `r` is `[0, 50]`. fn instr_i64ges(r: u32, ) -> Weight { - // Minimum execution time: 625 nanoseconds. - Weight::from_ref_time(885_174) - // Standard Error: 238 - .saturating_add(Weight::from_ref_time(734_010).saturating_mul(r.into())) + // Minimum execution time: 784 nanoseconds. + Weight::from_ref_time(1_068_487) + // Standard Error: 3_796 + .saturating_add(Weight::from_ref_time(741_207).saturating_mul(r.into())) } /// The range of component `r` is `[0, 50]`. fn instr_i64geu(r: u32, ) -> Weight { - // Minimum execution time: 602 nanoseconds. - Weight::from_ref_time(915_329) - // Standard Error: 273 - .saturating_add(Weight::from_ref_time(738_209).saturating_mul(r.into())) + // Minimum execution time: 813 nanoseconds. + Weight::from_ref_time(1_067_278) + // Standard Error: 459 + .saturating_add(Weight::from_ref_time(737_841).saturating_mul(r.into())) } /// The range of component `r` is `[0, 50]`. fn instr_i64add(r: u32, ) -> Weight { - // Minimum execution time: 585 nanoseconds. - Weight::from_ref_time(862_239) - // Standard Error: 294 - .saturating_add(Weight::from_ref_time(719_000).saturating_mul(r.into())) + // Minimum execution time: 798 nanoseconds. + Weight::from_ref_time(1_100_254) + // Standard Error: 297 + .saturating_add(Weight::from_ref_time(717_622).saturating_mul(r.into())) } /// The range of component `r` is `[0, 50]`. fn instr_i64sub(r: u32, ) -> Weight { - // Minimum execution time: 633 nanoseconds. - Weight::from_ref_time(881_696) - // Standard Error: 195 - .saturating_add(Weight::from_ref_time(713_153).saturating_mul(r.into())) + // Minimum execution time: 778 nanoseconds. + Weight::from_ref_time(1_063_748) + // Standard Error: 378 + .saturating_add(Weight::from_ref_time(713_577).saturating_mul(r.into())) } /// The range of component `r` is `[0, 50]`. fn instr_i64mul(r: u32, ) -> Weight { - // Minimum execution time: 629 nanoseconds. - Weight::from_ref_time(856_238) - // Standard Error: 269 - .saturating_add(Weight::from_ref_time(715_451).saturating_mul(r.into())) + // Minimum execution time: 780 nanoseconds. + Weight::from_ref_time(1_088_755) + // Standard Error: 316 + .saturating_add(Weight::from_ref_time(714_127).saturating_mul(r.into())) } /// The range of component `r` is `[0, 50]`. fn instr_i64divs(r: u32, ) -> Weight { - // Minimum execution time: 633 nanoseconds. - Weight::from_ref_time(880_804) - // Standard Error: 725 - .saturating_add(Weight::from_ref_time(1_349_577).saturating_mul(r.into())) + // Minimum execution time: 776 nanoseconds. + Weight::from_ref_time(1_062_591) + // Standard Error: 610 + .saturating_add(Weight::from_ref_time(1_355_430).saturating_mul(r.into())) } /// The range of component `r` is `[0, 50]`. fn instr_i64divu(r: u32, ) -> Weight { - // Minimum execution time: 639 nanoseconds. - Weight::from_ref_time(867_528) - // Standard Error: 1_940 - .saturating_add(Weight::from_ref_time(1_287_959).saturating_mul(r.into())) + // Minimum execution time: 801 nanoseconds. + Weight::from_ref_time(1_092_395) + // Standard Error: 423 + .saturating_add(Weight::from_ref_time(1_282_356).saturating_mul(r.into())) } /// The range of component `r` is `[0, 50]`. fn instr_i64rems(r: u32, ) -> Weight { - // Minimum execution time: 595 nanoseconds. - Weight::from_ref_time(851_253) - // Standard Error: 503 - .saturating_add(Weight::from_ref_time(1_398_668).saturating_mul(r.into())) + // Minimum execution time: 822 nanoseconds. + Weight::from_ref_time(1_083_079) + // Standard Error: 562 + .saturating_add(Weight::from_ref_time(1_398_522).saturating_mul(r.into())) } /// The range of component `r` is `[0, 50]`. fn instr_i64remu(r: u32, ) -> Weight { - // Minimum execution time: 604 nanoseconds. - Weight::from_ref_time(804_977) - // Standard Error: 812 - .saturating_add(Weight::from_ref_time(1_288_816).saturating_mul(r.into())) + // Minimum execution time: 798 nanoseconds. + Weight::from_ref_time(1_088_997) + // Standard Error: 401 + .saturating_add(Weight::from_ref_time(1_283_442).saturating_mul(r.into())) } /// The range of component `r` is `[0, 50]`. fn instr_i64and(r: u32, ) -> Weight { - // Minimum execution time: 609 nanoseconds. - Weight::from_ref_time(890_945) - // Standard Error: 1_645 - .saturating_add(Weight::from_ref_time(719_770).saturating_mul(r.into())) + // Minimum execution time: 815 nanoseconds. + Weight::from_ref_time(1_081_903) + // Standard Error: 264 + .saturating_add(Weight::from_ref_time(718_069).saturating_mul(r.into())) } /// The range of component `r` is `[0, 50]`. fn instr_i64or(r: u32, ) -> Weight { - // Minimum execution time: 628 nanoseconds. - Weight::from_ref_time(897_973) - // Standard Error: 1_641 - .saturating_add(Weight::from_ref_time(718_838).saturating_mul(r.into())) + // Minimum execution time: 813 nanoseconds. + Weight::from_ref_time(1_091_426) + // Standard Error: 296 + .saturating_add(Weight::from_ref_time(717_894).saturating_mul(r.into())) } /// The range of component `r` is `[0, 50]`. fn instr_i64xor(r: u32, ) -> Weight { - // Minimum execution time: 634 nanoseconds. - Weight::from_ref_time(848_440) - // Standard Error: 249 - .saturating_add(Weight::from_ref_time(718_206).saturating_mul(r.into())) + // Minimum execution time: 794 nanoseconds. + Weight::from_ref_time(1_040_144) + // Standard Error: 400 + .saturating_add(Weight::from_ref_time(719_886).saturating_mul(r.into())) } /// The range of component `r` is `[0, 50]`. fn instr_i64shl(r: u32, ) -> Weight { - // Minimum execution time: 625 nanoseconds. - Weight::from_ref_time(881_579) - // Standard Error: 273 - .saturating_add(Weight::from_ref_time(736_934).saturating_mul(r.into())) + // Minimum execution time: 774 nanoseconds. + Weight::from_ref_time(1_092_466) + // Standard Error: 4_173 + .saturating_add(Weight::from_ref_time(739_873).saturating_mul(r.into())) } /// The range of component `r` is `[0, 50]`. fn instr_i64shrs(r: u32, ) -> Weight { - // Minimum execution time: 630 nanoseconds. - Weight::from_ref_time(897_813) - // Standard Error: 249 - .saturating_add(Weight::from_ref_time(734_657).saturating_mul(r.into())) + // Minimum execution time: 793 nanoseconds. + Weight::from_ref_time(1_105_611) + // Standard Error: 394 + .saturating_add(Weight::from_ref_time(735_358).saturating_mul(r.into())) } /// The range of component `r` is `[0, 50]`. fn instr_i64shru(r: u32, ) -> Weight { - // Minimum execution time: 607 nanoseconds. - Weight::from_ref_time(871_660) - // Standard Error: 312 - .saturating_add(Weight::from_ref_time(735_377).saturating_mul(r.into())) + // Minimum execution time: 805 nanoseconds. + Weight::from_ref_time(1_130_462) + // Standard Error: 327 + .saturating_add(Weight::from_ref_time(734_198).saturating_mul(r.into())) } /// The range of component `r` is `[0, 50]`. fn instr_i64rotl(r: u32, ) -> Weight { - // Minimum execution time: 610 nanoseconds. - Weight::from_ref_time(861_640) - // Standard Error: 293 - .saturating_add(Weight::from_ref_time(735_524).saturating_mul(r.into())) + // Minimum execution time: 831 nanoseconds. + Weight::from_ref_time(1_118_894) + // Standard Error: 389 + .saturating_add(Weight::from_ref_time(733_834).saturating_mul(r.into())) } /// The range of component `r` is `[0, 50]`. fn instr_i64rotr(r: u32, ) -> Weight { - // Minimum execution time: 625 nanoseconds. - Weight::from_ref_time(880_203) - // Standard Error: 373 - .saturating_add(Weight::from_ref_time(737_354).saturating_mul(r.into())) + // Minimum execution time: 772 nanoseconds. + Weight::from_ref_time(1_123_733) + // Standard Error: 337 + .saturating_add(Weight::from_ref_time(733_923).saturating_mul(r.into())) } } @@ -1447,17 +1449,17 @@ impl WeightInfo for SubstrateWeight { impl WeightInfo for () { // Storage: Contracts DeletionQueue (r:1 w:0) fn on_process_deletion_queue_batch() -> Weight { - // Minimum execution time: 3_196 nanoseconds. - Weight::from_ref_time(3_293_000) + // Minimum execution time: 3_326 nanoseconds. + Weight::from_ref_time(3_433_000) .saturating_add(RocksDbWeight::get().reads(1)) } // Storage: Skipped Metadata (r:0 w:0) /// The range of component `k` is `[0, 1024]`. fn on_initialize_per_trie_key(k: u32, ) -> Weight { - // Minimum execution time: 14_857 nanoseconds. - Weight::from_ref_time(14_957_593) - // Standard Error: 1_015 - .saturating_add(Weight::from_ref_time(935_359).saturating_mul(k.into())) + // Minimum execution time: 15_703 nanoseconds. + Weight::from_ref_time(15_288_927) + // Standard Error: 951 + .saturating_add(Weight::from_ref_time(940_816).saturating_mul(k.into())) .saturating_add(RocksDbWeight::get().reads(1)) .saturating_add(RocksDbWeight::get().writes(1)) .saturating_add(RocksDbWeight::get().writes((1_u64).saturating_mul(k.into()))) @@ -1465,10 +1467,10 @@ impl WeightInfo for () { // Storage: Contracts DeletionQueue (r:1 w:0) /// The range of component `q` is `[0, 128]`. fn on_initialize_per_queue_item(q: u32, ) -> Weight { - // Minimum execution time: 3_231 nanoseconds. - Weight::from_ref_time(14_859_580) - // Standard Error: 3_479 - .saturating_add(Weight::from_ref_time(1_185_600).saturating_mul(q.into())) + // Minimum execution time: 3_548 nanoseconds. + Weight::from_ref_time(15_764_121) + // Standard Error: 3_339 + .saturating_add(Weight::from_ref_time(1_214_890).saturating_mul(q.into())) .saturating_add(RocksDbWeight::get().reads(1)) .saturating_add(RocksDbWeight::get().writes(1)) } @@ -1476,10 +1478,10 @@ impl WeightInfo for () { // Storage: Contracts CodeStorage (r:0 w:1) /// The range of component `c` is `[0, 64226]`. fn reinstrument(c: u32, ) -> Weight { - // Minimum execution time: 34_565 nanoseconds. - Weight::from_ref_time(29_199_016) - // Standard Error: 70 - .saturating_add(Weight::from_ref_time(47_107).saturating_mul(c.into())) + // Minimum execution time: 29_801 nanoseconds. + Weight::from_ref_time(27_932_706) + // Standard Error: 49 + .saturating_add(Weight::from_ref_time(50_532).saturating_mul(c.into())) .saturating_add(RocksDbWeight::get().reads(1)) .saturating_add(RocksDbWeight::get().writes(1)) } @@ -1490,10 +1492,10 @@ impl WeightInfo for () { // Storage: System EventTopics (r:2 w:2) /// The range of component `c` is `[0, 131072]`. fn call_with_code_per_byte(c: u32, ) -> Weight { - // Minimum execution time: 392_074 nanoseconds. - Weight::from_ref_time(404_090_909) - // Standard Error: 24 - .saturating_add(Weight::from_ref_time(30_454).saturating_mul(c.into())) + // Minimum execution time: 307_894 nanoseconds. + Weight::from_ref_time(322_346_319) + // Standard Error: 25 + .saturating_add(Weight::from_ref_time(30_678).saturating_mul(c.into())) .saturating_add(RocksDbWeight::get().reads(6)) .saturating_add(RocksDbWeight::get().writes(4)) } @@ -1509,14 +1511,14 @@ impl WeightInfo for () { /// The range of component `i` is `[0, 1048576]`. /// The range of component `s` is `[0, 1048576]`. fn instantiate_with_code(c: u32, i: u32, s: u32, ) -> Weight { - // Minimum execution time: 3_785_934 nanoseconds. - Weight::from_ref_time(683_143_843) - // Standard Error: 272 - .saturating_add(Weight::from_ref_time(87_620).saturating_mul(c.into())) + // Minimum execution time: 3_610_424 nanoseconds. + Weight::from_ref_time(583_426_386) + // Standard Error: 277 + .saturating_add(Weight::from_ref_time(90_224).saturating_mul(c.into())) // Standard Error: 16 - .saturating_add(Weight::from_ref_time(1_363).saturating_mul(i.into())) + .saturating_add(Weight::from_ref_time(1_325).saturating_mul(i.into())) // Standard Error: 16 - .saturating_add(Weight::from_ref_time(1_778).saturating_mul(s.into())) + .saturating_add(Weight::from_ref_time(1_727).saturating_mul(s.into())) .saturating_add(RocksDbWeight::get().reads(8)) .saturating_add(RocksDbWeight::get().writes(9)) } @@ -1530,12 +1532,12 @@ impl WeightInfo for () { /// The range of component `i` is `[0, 1048576]`. /// The range of component `s` is `[0, 1048576]`. fn instantiate(i: u32, s: u32, ) -> Weight { - // Minimum execution time: 1_935_310 nanoseconds. - Weight::from_ref_time(203_531_122) + // Minimum execution time: 1_892_996 nanoseconds. + Weight::from_ref_time(202_958_196) // Standard Error: 8 - .saturating_add(Weight::from_ref_time(1_674).saturating_mul(i.into())) + .saturating_add(Weight::from_ref_time(1_627).saturating_mul(i.into())) // Standard Error: 8 - .saturating_add(Weight::from_ref_time(1_789).saturating_mul(s.into())) + .saturating_add(Weight::from_ref_time(1_755).saturating_mul(s.into())) .saturating_add(RocksDbWeight::get().reads(8)) .saturating_add(RocksDbWeight::get().writes(7)) } @@ -1545,8 +1547,8 @@ impl WeightInfo for () { // Storage: System Account (r:1 w:1) // Storage: System EventTopics (r:2 w:2) fn call() -> Weight { - // Minimum execution time: 151_999 nanoseconds. - Weight::from_ref_time(153_527_000) + // Minimum execution time: 157_347 nanoseconds. + Weight::from_ref_time(159_084_000) .saturating_add(RocksDbWeight::get().reads(6)) .saturating_add(RocksDbWeight::get().writes(4)) } @@ -1556,10 +1558,10 @@ impl WeightInfo for () { // Storage: Contracts OwnerInfoOf (r:0 w:1) /// The range of component `c` is `[0, 64226]`. fn upload_code(c: u32, ) -> Weight { - // Minimum execution time: 391_165 nanoseconds. - Weight::from_ref_time(394_519_487) - // Standard Error: 75 - .saturating_add(Weight::from_ref_time(89_736).saturating_mul(c.into())) + // Minimum execution time: 299_987 nanoseconds. + Weight::from_ref_time(305_274_879) + // Standard Error: 72 + .saturating_add(Weight::from_ref_time(91_916).saturating_mul(c.into())) .saturating_add(RocksDbWeight::get().reads(2)) .saturating_add(RocksDbWeight::get().writes(4)) } @@ -1568,8 +1570,8 @@ impl WeightInfo for () { // Storage: Contracts CodeStorage (r:0 w:1) // Storage: Contracts PristineCode (r:0 w:1) fn remove_code() -> Weight { - // Minimum execution time: 39_354 nanoseconds. - Weight::from_ref_time(39_855_000) + // Minimum execution time: 40_795 nanoseconds. + Weight::from_ref_time(41_297_000) .saturating_add(RocksDbWeight::get().reads(2)) .saturating_add(RocksDbWeight::get().writes(4)) } @@ -1577,8 +1579,8 @@ impl WeightInfo for () { // Storage: Contracts OwnerInfoOf (r:2 w:2) // Storage: System EventTopics (r:3 w:3) fn set_code() -> Weight { - // Minimum execution time: 40_909 nanoseconds. - Weight::from_ref_time(41_275_000) + // Minimum execution time: 41_957 nanoseconds. + Weight::from_ref_time(42_536_000) .saturating_add(RocksDbWeight::get().reads(6)) .saturating_add(RocksDbWeight::get().writes(6)) } @@ -1589,10 +1591,10 @@ impl WeightInfo for () { // Storage: System EventTopics (r:2 w:2) /// The range of component `r` is `[0, 20]`. fn seal_caller(r: u32, ) -> Weight { - // Minimum execution time: 380_150 nanoseconds. - Weight::from_ref_time(384_429_035) - // Standard Error: 34_740 - .saturating_add(Weight::from_ref_time(15_582_218).saturating_mul(r.into())) + // Minimum execution time: 293_544 nanoseconds. + Weight::from_ref_time(298_981_222) + // Standard Error: 39_139 + .saturating_add(Weight::from_ref_time(17_278_436).saturating_mul(r.into())) .saturating_add(RocksDbWeight::get().reads(6)) .saturating_add(RocksDbWeight::get().writes(3)) } @@ -1603,10 +1605,10 @@ impl WeightInfo for () { // Storage: System EventTopics (r:2 w:2) /// The range of component `r` is `[0, 20]`. fn seal_is_contract(r: u32, ) -> Weight { - // Minimum execution time: 380_608 nanoseconds. - Weight::from_ref_time(326_544_805) - // Standard Error: 475_381 - .saturating_add(Weight::from_ref_time(190_717_772).saturating_mul(r.into())) + // Minimum execution time: 293_500 nanoseconds. + Weight::from_ref_time(237_825_284) + // Standard Error: 450_234 + .saturating_add(Weight::from_ref_time(198_995_806).saturating_mul(r.into())) .saturating_add(RocksDbWeight::get().reads(6)) .saturating_add(RocksDbWeight::get().reads((80_u64).saturating_mul(r.into()))) .saturating_add(RocksDbWeight::get().writes(3)) @@ -1618,10 +1620,10 @@ impl WeightInfo for () { // Storage: System EventTopics (r:2 w:2) /// The range of component `r` is `[0, 20]`. fn seal_code_hash(r: u32, ) -> Weight { - // Minimum execution time: 382_780 nanoseconds. - Weight::from_ref_time(333_775_113) - // Standard Error: 446_086 - .saturating_add(Weight::from_ref_time(232_531_042).saturating_mul(r.into())) + // Minimum execution time: 295_851 nanoseconds. + Weight::from_ref_time(258_113_447) + // Standard Error: 408_747 + .saturating_add(Weight::from_ref_time(237_490_216).saturating_mul(r.into())) .saturating_add(RocksDbWeight::get().reads(6)) .saturating_add(RocksDbWeight::get().reads((80_u64).saturating_mul(r.into()))) .saturating_add(RocksDbWeight::get().writes(3)) @@ -1633,10 +1635,10 @@ impl WeightInfo for () { // Storage: System EventTopics (r:2 w:2) /// The range of component `r` is `[0, 20]`. fn seal_own_code_hash(r: u32, ) -> Weight { - // Minimum execution time: 381_815 nanoseconds. - Weight::from_ref_time(390_931_793) - // Standard Error: 61_918 - .saturating_add(Weight::from_ref_time(18_798_438).saturating_mul(r.into())) + // Minimum execution time: 294_614 nanoseconds. + Weight::from_ref_time(301_528_998) + // Standard Error: 40_613 + .saturating_add(Weight::from_ref_time(20_815_088).saturating_mul(r.into())) .saturating_add(RocksDbWeight::get().reads(6)) .saturating_add(RocksDbWeight::get().writes(3)) } @@ -1647,10 +1649,10 @@ impl WeightInfo for () { // Storage: System EventTopics (r:2 w:2) /// The range of component `r` is `[0, 20]`. fn seal_caller_is_origin(r: u32, ) -> Weight { - // Minimum execution time: 379_162 nanoseconds. - Weight::from_ref_time(383_796_363) - // Standard Error: 25_555 - .saturating_add(Weight::from_ref_time(10_843_721).saturating_mul(r.into())) + // Minimum execution time: 293_088 nanoseconds. + Weight::from_ref_time(299_120_595) + // Standard Error: 23_277 + .saturating_add(Weight::from_ref_time(11_130_334).saturating_mul(r.into())) .saturating_add(RocksDbWeight::get().reads(6)) .saturating_add(RocksDbWeight::get().writes(3)) } @@ -1661,10 +1663,10 @@ impl WeightInfo for () { // Storage: System EventTopics (r:2 w:2) /// The range of component `r` is `[0, 20]`. fn seal_address(r: u32, ) -> Weight { - // Minimum execution time: 380_307 nanoseconds. - Weight::from_ref_time(390_211_779) - // Standard Error: 53_326 - .saturating_add(Weight::from_ref_time(15_311_171).saturating_mul(r.into())) + // Minimum execution time: 295_110 nanoseconds. + Weight::from_ref_time(305_295_787) + // Standard Error: 48_521 + .saturating_add(Weight::from_ref_time(16_610_806).saturating_mul(r.into())) .saturating_add(RocksDbWeight::get().reads(6)) .saturating_add(RocksDbWeight::get().writes(3)) } @@ -1675,10 +1677,10 @@ impl WeightInfo for () { // Storage: System EventTopics (r:2 w:2) /// The range of component `r` is `[0, 20]`. fn seal_gas_left(r: u32, ) -> Weight { - // Minimum execution time: 380_230 nanoseconds. - Weight::from_ref_time(383_470_453) - // Standard Error: 45_007 - .saturating_add(Weight::from_ref_time(15_582_472).saturating_mul(r.into())) + // Minimum execution time: 293_414 nanoseconds. + Weight::from_ref_time(299_960_283) + // Standard Error: 32_003 + .saturating_add(Weight::from_ref_time(16_652_433).saturating_mul(r.into())) .saturating_add(RocksDbWeight::get().reads(6)) .saturating_add(RocksDbWeight::get().writes(3)) } @@ -1689,10 +1691,10 @@ impl WeightInfo for () { // Storage: System EventTopics (r:2 w:2) /// The range of component `r` is `[0, 20]`. fn seal_balance(r: u32, ) -> Weight { - // Minimum execution time: 380_244 nanoseconds. - Weight::from_ref_time(388_092_461) - // Standard Error: 98_199 - .saturating_add(Weight::from_ref_time(97_339_528).saturating_mul(r.into())) + // Minimum execution time: 292_429 nanoseconds. + Weight::from_ref_time(301_025_128) + // Standard Error: 92_155 + .saturating_add(Weight::from_ref_time(92_731_719).saturating_mul(r.into())) .saturating_add(RocksDbWeight::get().reads(7)) .saturating_add(RocksDbWeight::get().writes(3)) } @@ -1703,10 +1705,10 @@ impl WeightInfo for () { // Storage: System EventTopics (r:2 w:2) /// The range of component `r` is `[0, 20]`. fn seal_value_transferred(r: u32, ) -> Weight { - // Minimum execution time: 380_242 nanoseconds. - Weight::from_ref_time(384_078_258) - // Standard Error: 28_510 - .saturating_add(Weight::from_ref_time(15_423_359).saturating_mul(r.into())) + // Minimum execution time: 293_516 nanoseconds. + Weight::from_ref_time(300_071_428) + // Standard Error: 25_871 + .saturating_add(Weight::from_ref_time(16_599_369).saturating_mul(r.into())) .saturating_add(RocksDbWeight::get().reads(6)) .saturating_add(RocksDbWeight::get().writes(3)) } @@ -1717,10 +1719,10 @@ impl WeightInfo for () { // Storage: System EventTopics (r:2 w:2) /// The range of component `r` is `[0, 20]`. fn seal_minimum_balance(r: u32, ) -> Weight { - // Minimum execution time: 379_890 nanoseconds. - Weight::from_ref_time(383_658_430) - // Standard Error: 44_976 - .saturating_add(Weight::from_ref_time(15_451_905).saturating_mul(r.into())) + // Minimum execution time: 293_666 nanoseconds. + Weight::from_ref_time(299_803_599) + // Standard Error: 24_508 + .saturating_add(Weight::from_ref_time(16_500_973).saturating_mul(r.into())) .saturating_add(RocksDbWeight::get().reads(6)) .saturating_add(RocksDbWeight::get().writes(3)) } @@ -1731,10 +1733,10 @@ impl WeightInfo for () { // Storage: System EventTopics (r:2 w:2) /// The range of component `r` is `[0, 20]`. fn seal_block_number(r: u32, ) -> Weight { - // Minimum execution time: 380_269 nanoseconds. - Weight::from_ref_time(383_580_481) - // Standard Error: 31_862 - .saturating_add(Weight::from_ref_time(15_230_473).saturating_mul(r.into())) + // Minimum execution time: 289_193 nanoseconds. + Weight::from_ref_time(297_946_784) + // Standard Error: 43_719 + .saturating_add(Weight::from_ref_time(16_442_254).saturating_mul(r.into())) .saturating_add(RocksDbWeight::get().reads(6)) .saturating_add(RocksDbWeight::get().writes(3)) } @@ -1745,10 +1747,10 @@ impl WeightInfo for () { // Storage: System EventTopics (r:2 w:2) /// The range of component `r` is `[0, 20]`. fn seal_now(r: u32, ) -> Weight { - // Minimum execution time: 380_308 nanoseconds. - Weight::from_ref_time(383_732_372) - // Standard Error: 38_359 - .saturating_add(Weight::from_ref_time(15_358_775).saturating_mul(r.into())) + // Minimum execution time: 288_897 nanoseconds. + Weight::from_ref_time(298_856_658) + // Standard Error: 31_003 + .saturating_add(Weight::from_ref_time(16_490_508).saturating_mul(r.into())) .saturating_add(RocksDbWeight::get().reads(6)) .saturating_add(RocksDbWeight::get().writes(3)) } @@ -1760,10 +1762,10 @@ impl WeightInfo for () { // Storage: TransactionPayment NextFeeMultiplier (r:1 w:0) /// The range of component `r` is `[0, 20]`. fn seal_weight_to_fee(r: u32, ) -> Weight { - // Minimum execution time: 380_834 nanoseconds. - Weight::from_ref_time(388_999_459) - // Standard Error: 96_447 - .saturating_add(Weight::from_ref_time(86_714_414).saturating_mul(r.into())) + // Minimum execution time: 289_850 nanoseconds. + Weight::from_ref_time(302_898_449) + // Standard Error: 92_822 + .saturating_add(Weight::from_ref_time(86_376_874).saturating_mul(r.into())) .saturating_add(RocksDbWeight::get().reads(7)) .saturating_add(RocksDbWeight::get().writes(3)) } @@ -1774,10 +1776,10 @@ impl WeightInfo for () { // Storage: System EventTopics (r:2 w:2) /// The range of component `r` is `[0, 20]`. fn seal_gas(r: u32, ) -> Weight { - // Minimum execution time: 140_955 nanoseconds. - Weight::from_ref_time(144_716_423) - // Standard Error: 11_370 - .saturating_add(Weight::from_ref_time(7_858_807).saturating_mul(r.into())) + // Minimum execution time: 145_105 nanoseconds. + Weight::from_ref_time(152_388_934) + // Standard Error: 29_988 + .saturating_add(Weight::from_ref_time(7_761_947).saturating_mul(r.into())) .saturating_add(RocksDbWeight::get().reads(6)) .saturating_add(RocksDbWeight::get().writes(3)) } @@ -1788,10 +1790,10 @@ impl WeightInfo for () { // Storage: System EventTopics (r:2 w:2) /// The range of component `r` is `[0, 20]`. fn seal_input(r: u32, ) -> Weight { - // Minimum execution time: 380_210 nanoseconds. - Weight::from_ref_time(384_185_776) - // Standard Error: 49_038 - .saturating_add(Weight::from_ref_time(13_649_793).saturating_mul(r.into())) + // Minimum execution time: 289_319 nanoseconds. + Weight::from_ref_time(298_390_911) + // Standard Error: 27_329 + .saturating_add(Weight::from_ref_time(14_225_527).saturating_mul(r.into())) .saturating_add(RocksDbWeight::get().reads(6)) .saturating_add(RocksDbWeight::get().writes(3)) } @@ -1802,10 +1804,10 @@ impl WeightInfo for () { // Storage: System EventTopics (r:2 w:2) /// The range of component `n` is `[0, 1024]`. fn seal_input_per_kb(n: u32, ) -> Weight { - // Minimum execution time: 396_209 nanoseconds. - Weight::from_ref_time(424_522_611) - // Standard Error: 3_917 - .saturating_add(Weight::from_ref_time(9_627_216).saturating_mul(n.into())) + // Minimum execution time: 305_538 nanoseconds. + Weight::from_ref_time(339_734_596) + // Standard Error: 4_636 + .saturating_add(Weight::from_ref_time(9_592_149).saturating_mul(n.into())) .saturating_add(RocksDbWeight::get().reads(6)) .saturating_add(RocksDbWeight::get().writes(3)) } @@ -1816,10 +1818,10 @@ impl WeightInfo for () { // Storage: System EventTopics (r:2 w:2) /// The range of component `r` is `[0, 1]`. fn seal_return(r: u32, ) -> Weight { - // Minimum execution time: 378_412 nanoseconds. - Weight::from_ref_time(380_502_085) - // Standard Error: 201_552 - .saturating_add(Weight::from_ref_time(1_434_214).saturating_mul(r.into())) + // Minimum execution time: 287_189 nanoseconds. + Weight::from_ref_time(295_472_383) + // Standard Error: 314_142 + .saturating_add(Weight::from_ref_time(214_016).saturating_mul(r.into())) .saturating_add(RocksDbWeight::get().reads(6)) .saturating_add(RocksDbWeight::get().writes(3)) } @@ -1830,10 +1832,10 @@ impl WeightInfo for () { // Storage: System EventTopics (r:2 w:2) /// The range of component `n` is `[0, 1024]`. fn seal_return_per_kb(n: u32, ) -> Weight { - // Minimum execution time: 381_463 nanoseconds. - Weight::from_ref_time(383_955_553) - // Standard Error: 998 - .saturating_add(Weight::from_ref_time(230_512).saturating_mul(n.into())) + // Minimum execution time: 289_380 nanoseconds. + Weight::from_ref_time(296_302_404) + // Standard Error: 889 + .saturating_add(Weight::from_ref_time(190_017).saturating_mul(n.into())) .saturating_add(RocksDbWeight::get().reads(6)) .saturating_add(RocksDbWeight::get().writes(3)) } @@ -1846,10 +1848,10 @@ impl WeightInfo for () { // Storage: Contracts OwnerInfoOf (r:1 w:1) /// The range of component `r` is `[0, 1]`. fn seal_terminate(r: u32, ) -> Weight { - // Minimum execution time: 379_877 nanoseconds. - Weight::from_ref_time(381_729_546) - // Standard Error: 214_004 - .saturating_add(Weight::from_ref_time(52_528_353).saturating_mul(r.into())) + // Minimum execution time: 292_383 nanoseconds. + Weight::from_ref_time(297_557_691) + // Standard Error: 251_228 + .saturating_add(Weight::from_ref_time(56_688_008).saturating_mul(r.into())) .saturating_add(RocksDbWeight::get().reads(6)) .saturating_add(RocksDbWeight::get().reads((5_u64).saturating_mul(r.into()))) .saturating_add(RocksDbWeight::get().writes(3)) @@ -1863,10 +1865,10 @@ impl WeightInfo for () { // Storage: RandomnessCollectiveFlip RandomMaterial (r:1 w:0) /// The range of component `r` is `[0, 20]`. fn seal_random(r: u32, ) -> Weight { - // Minimum execution time: 380_275 nanoseconds. - Weight::from_ref_time(386_495_777) - // Standard Error: 94_674 - .saturating_add(Weight::from_ref_time(108_432_929).saturating_mul(r.into())) + // Minimum execution time: 293_019 nanoseconds. + Weight::from_ref_time(301_747_439) + // Standard Error: 92_043 + .saturating_add(Weight::from_ref_time(109_861_144).saturating_mul(r.into())) .saturating_add(RocksDbWeight::get().reads(7)) .saturating_add(RocksDbWeight::get().writes(3)) } @@ -1877,10 +1879,10 @@ impl WeightInfo for () { // Storage: System EventTopics (r:2 w:2) /// The range of component `r` is `[0, 20]`. fn seal_deposit_event(r: u32, ) -> Weight { - // Minimum execution time: 379_095 nanoseconds. - Weight::from_ref_time(393_997_924) - // Standard Error: 141_916 - .saturating_add(Weight::from_ref_time(212_937_170).saturating_mul(r.into())) + // Minimum execution time: 287_586 nanoseconds. + Weight::from_ref_time(304_751_302) + // Standard Error: 127_698 + .saturating_add(Weight::from_ref_time(221_525_053).saturating_mul(r.into())) .saturating_add(RocksDbWeight::get().reads(6)) .saturating_add(RocksDbWeight::get().writes(3)) } @@ -1892,12 +1894,12 @@ impl WeightInfo for () { /// The range of component `t` is `[0, 4]`. /// The range of component `n` is `[0, 16]`. fn seal_deposit_event_per_topic_and_kb(t: u32, n: u32, ) -> Weight { - // Minimum execution time: 1_241_001 nanoseconds. - Weight::from_ref_time(548_526_917) - // Standard Error: 496_807 - .saturating_add(Weight::from_ref_time(177_087_769).saturating_mul(t.into())) - // Standard Error: 136_447 - .saturating_add(Weight::from_ref_time(71_558_402).saturating_mul(n.into())) + // Minimum execution time: 1_182_204 nanoseconds. + Weight::from_ref_time(508_607_957) + // Standard Error: 503_533 + .saturating_add(Weight::from_ref_time(173_998_655).saturating_mul(t.into())) + // Standard Error: 138_294 + .saturating_add(Weight::from_ref_time(67_991_373).saturating_mul(n.into())) .saturating_add(RocksDbWeight::get().reads(6)) .saturating_add(RocksDbWeight::get().reads((80_u64).saturating_mul(t.into()))) .saturating_add(RocksDbWeight::get().writes(3)) @@ -1910,20 +1912,20 @@ impl WeightInfo for () { // Storage: System EventTopics (r:2 w:2) /// The range of component `r` is `[0, 20]`. fn seal_debug_message(r: u32, ) -> Weight { - // Minimum execution time: 159_158 nanoseconds. - Weight::from_ref_time(163_427_712) - // Standard Error: 22_442 - .saturating_add(Weight::from_ref_time(12_647_838).saturating_mul(r.into())) + // Minimum execution time: 157_706 nanoseconds. + Weight::from_ref_time(161_895_583) + // Standard Error: 14_952 + .saturating_add(Weight::from_ref_time(12_990_237).saturating_mul(r.into())) .saturating_add(RocksDbWeight::get().reads(6)) .saturating_add(RocksDbWeight::get().writes(3)) } // Storage: Skipped Metadata (r:0 w:0) /// The range of component `r` is `[0, 10]`. fn seal_set_storage(r: u32, ) -> Weight { - // Minimum execution time: 381_068 nanoseconds. - Weight::from_ref_time(341_041_230) - // Standard Error: 464_053 - .saturating_add(Weight::from_ref_time(402_677_314).saturating_mul(r.into())) + // Minimum execution time: 290_063 nanoseconds. + Weight::from_ref_time(256_404_853) + // Standard Error: 436_195 + .saturating_add(Weight::from_ref_time(406_569_743).saturating_mul(r.into())) .saturating_add(RocksDbWeight::get().reads(6)) .saturating_add(RocksDbWeight::get().reads((80_u64).saturating_mul(r.into()))) .saturating_add(RocksDbWeight::get().writes(3)) @@ -1932,10 +1934,10 @@ impl WeightInfo for () { // Storage: Skipped Metadata (r:0 w:0) /// The range of component `n` is `[0, 8]`. fn seal_set_storage_per_new_kb(n: u32, ) -> Weight { - // Minimum execution time: 508_695 nanoseconds. - Weight::from_ref_time(663_159_695) - // Standard Error: 1_419_342 - .saturating_add(Weight::from_ref_time(96_558_570).saturating_mul(n.into())) + // Minimum execution time: 426_295 nanoseconds. + Weight::from_ref_time(582_989_911) + // Standard Error: 1_404_141 + .saturating_add(Weight::from_ref_time(89_545_963).saturating_mul(n.into())) .saturating_add(RocksDbWeight::get().reads(52)) .saturating_add(RocksDbWeight::get().reads((7_u64).saturating_mul(n.into()))) .saturating_add(RocksDbWeight::get().writes(50)) @@ -1944,10 +1946,10 @@ impl WeightInfo for () { // Storage: Skipped Metadata (r:0 w:0) /// The range of component `n` is `[0, 8]`. fn seal_set_storage_per_old_kb(n: u32, ) -> Weight { - // Minimum execution time: 508_542 nanoseconds. - Weight::from_ref_time(634_146_978) - // Standard Error: 1_168_252 - .saturating_add(Weight::from_ref_time(64_231_947).saturating_mul(n.into())) + // Minimum execution time: 425_062 nanoseconds. + Weight::from_ref_time(552_380_900) + // Standard Error: 1_140_169 + .saturating_add(Weight::from_ref_time(64_085_108).saturating_mul(n.into())) .saturating_add(RocksDbWeight::get().reads(51)) .saturating_add(RocksDbWeight::get().reads((7_u64).saturating_mul(n.into()))) .saturating_add(RocksDbWeight::get().writes(49)) @@ -1956,10 +1958,10 @@ impl WeightInfo for () { // Storage: Skipped Metadata (r:0 w:0) /// The range of component `r` is `[0, 10]`. fn seal_clear_storage(r: u32, ) -> Weight { - // Minimum execution time: 381_743 nanoseconds. - Weight::from_ref_time(337_309_674) - // Standard Error: 527_407 - .saturating_add(Weight::from_ref_time(395_767_068).saturating_mul(r.into())) + // Minimum execution time: 294_421 nanoseconds. + Weight::from_ref_time(260_897_072) + // Standard Error: 474_135 + .saturating_add(Weight::from_ref_time(403_191_862).saturating_mul(r.into())) .saturating_add(RocksDbWeight::get().reads(6)) .saturating_add(RocksDbWeight::get().reads((80_u64).saturating_mul(r.into()))) .saturating_add(RocksDbWeight::get().writes(3)) @@ -1968,10 +1970,10 @@ impl WeightInfo for () { // Storage: Skipped Metadata (r:0 w:0) /// The range of component `n` is `[0, 8]`. fn seal_clear_storage_per_kb(n: u32, ) -> Weight { - // Minimum execution time: 478_283 nanoseconds. - Weight::from_ref_time(616_673_245) - // Standard Error: 1_290_784 - .saturating_add(Weight::from_ref_time(66_534_552).saturating_mul(n.into())) + // Minimum execution time: 391_743 nanoseconds. + Weight::from_ref_time(535_327_545) + // Standard Error: 1_297_157 + .saturating_add(Weight::from_ref_time(67_222_981).saturating_mul(n.into())) .saturating_add(RocksDbWeight::get().reads(51)) .saturating_add(RocksDbWeight::get().reads((7_u64).saturating_mul(n.into()))) .saturating_add(RocksDbWeight::get().writes(48)) @@ -1980,10 +1982,10 @@ impl WeightInfo for () { // Storage: Skipped Metadata (r:0 w:0) /// The range of component `r` is `[0, 10]`. fn seal_get_storage(r: u32, ) -> Weight { - // Minimum execution time: 381_916 nanoseconds. - Weight::from_ref_time(349_150_912) - // Standard Error: 443_388 - .saturating_add(Weight::from_ref_time(316_975_558).saturating_mul(r.into())) + // Minimum execution time: 294_947 nanoseconds. + Weight::from_ref_time(266_280_887) + // Standard Error: 382_477 + .saturating_add(Weight::from_ref_time(323_306_898).saturating_mul(r.into())) .saturating_add(RocksDbWeight::get().reads(6)) .saturating_add(RocksDbWeight::get().reads((80_u64).saturating_mul(r.into()))) .saturating_add(RocksDbWeight::get().writes(3)) @@ -1991,10 +1993,10 @@ impl WeightInfo for () { // Storage: Skipped Metadata (r:0 w:0) /// The range of component `n` is `[0, 8]`. fn seal_get_storage_per_kb(n: u32, ) -> Weight { - // Minimum execution time: 459_294 nanoseconds. - Weight::from_ref_time(579_698_524) - // Standard Error: 1_111_681 - .saturating_add(Weight::from_ref_time(159_276_087).saturating_mul(n.into())) + // Minimum execution time: 375_487 nanoseconds. + Weight::from_ref_time(497_019_365) + // Standard Error: 1_141_672 + .saturating_add(Weight::from_ref_time(152_354_482).saturating_mul(n.into())) .saturating_add(RocksDbWeight::get().reads(51)) .saturating_add(RocksDbWeight::get().reads((7_u64).saturating_mul(n.into()))) .saturating_add(RocksDbWeight::get().writes(3)) @@ -2002,10 +2004,10 @@ impl WeightInfo for () { // Storage: Skipped Metadata (r:0 w:0) /// The range of component `r` is `[0, 10]`. fn seal_contains_storage(r: u32, ) -> Weight { - // Minimum execution time: 381_700 nanoseconds. - Weight::from_ref_time(352_544_675) - // Standard Error: 401_504 - .saturating_add(Weight::from_ref_time(304_380_106).saturating_mul(r.into())) + // Minimum execution time: 294_716 nanoseconds. + Weight::from_ref_time(266_187_186) + // Standard Error: 395_323 + .saturating_add(Weight::from_ref_time(309_907_221).saturating_mul(r.into())) .saturating_add(RocksDbWeight::get().reads(6)) .saturating_add(RocksDbWeight::get().reads((80_u64).saturating_mul(r.into()))) .saturating_add(RocksDbWeight::get().writes(3)) @@ -2013,10 +2015,10 @@ impl WeightInfo for () { // Storage: Skipped Metadata (r:0 w:0) /// The range of component `n` is `[0, 8]`. fn seal_contains_storage_per_kb(n: u32, ) -> Weight { - // Minimum execution time: 455_595 nanoseconds. - Weight::from_ref_time(560_428_166) - // Standard Error: 991_088 - .saturating_add(Weight::from_ref_time(61_810_610).saturating_mul(n.into())) + // Minimum execution time: 371_667 nanoseconds. + Weight::from_ref_time(478_075_482) + // Standard Error: 979_116 + .saturating_add(Weight::from_ref_time(62_075_707).saturating_mul(n.into())) .saturating_add(RocksDbWeight::get().reads(51)) .saturating_add(RocksDbWeight::get().reads((7_u64).saturating_mul(n.into()))) .saturating_add(RocksDbWeight::get().writes(3)) @@ -2024,10 +2026,10 @@ impl WeightInfo for () { // Storage: Skipped Metadata (r:0 w:0) /// The range of component `r` is `[0, 10]`. fn seal_take_storage(r: u32, ) -> Weight { - // Minimum execution time: 382_000 nanoseconds. - Weight::from_ref_time(336_164_219) - // Standard Error: 601_744 - .saturating_add(Weight::from_ref_time(406_198_079).saturating_mul(r.into())) + // Minimum execution time: 292_147 nanoseconds. + Weight::from_ref_time(254_374_598) + // Standard Error: 442_367 + .saturating_add(Weight::from_ref_time(413_674_705).saturating_mul(r.into())) .saturating_add(RocksDbWeight::get().reads(6)) .saturating_add(RocksDbWeight::get().reads((80_u64).saturating_mul(r.into()))) .saturating_add(RocksDbWeight::get().writes(3)) @@ -2036,10 +2038,10 @@ impl WeightInfo for () { // Storage: Skipped Metadata (r:0 w:0) /// The range of component `n` is `[0, 8]`. fn seal_take_storage_per_kb(n: u32, ) -> Weight { - // Minimum execution time: 482_335 nanoseconds. - Weight::from_ref_time(634_245_177) - // Standard Error: 1_418_845 - .saturating_add(Weight::from_ref_time(164_352_113).saturating_mul(n.into())) + // Minimum execution time: 398_554 nanoseconds. + Weight::from_ref_time(553_715_911) + // Standard Error: 1_415_272 + .saturating_add(Weight::from_ref_time(158_254_544).saturating_mul(n.into())) .saturating_add(RocksDbWeight::get().reads(51)) .saturating_add(RocksDbWeight::get().reads((7_u64).saturating_mul(n.into()))) .saturating_add(RocksDbWeight::get().writes(48)) @@ -2052,10 +2054,10 @@ impl WeightInfo for () { // Storage: System EventTopics (r:2 w:2) /// The range of component `r` is `[0, 20]`. fn seal_transfer(r: u32, ) -> Weight { - // Minimum execution time: 382_142 nanoseconds. - Weight::from_ref_time(317_581_708) - // Standard Error: 682_156 - .saturating_add(Weight::from_ref_time(1_305_289_569).saturating_mul(r.into())) + // Minimum execution time: 295_081 nanoseconds. + Weight::from_ref_time(253_461_581) + // Standard Error: 740_198 + .saturating_add(Weight::from_ref_time(1_369_153_623).saturating_mul(r.into())) .saturating_add(RocksDbWeight::get().reads(7)) .saturating_add(RocksDbWeight::get().reads((80_u64).saturating_mul(r.into()))) .saturating_add(RocksDbWeight::get().writes(4)) @@ -2068,10 +2070,10 @@ impl WeightInfo for () { // Storage: System EventTopics (r:2 w:2) /// The range of component `r` is `[0, 20]`. fn seal_call(r: u32, ) -> Weight { - // Minimum execution time: 383_580 nanoseconds. - Weight::from_ref_time(384_176_000) - // Standard Error: 6_483_482 - .saturating_add(Weight::from_ref_time(28_047_685_517).saturating_mul(r.into())) + // Minimum execution time: 296_089 nanoseconds. + Weight::from_ref_time(297_193_000) + // Standard Error: 6_571_547 + .saturating_add(Weight::from_ref_time(21_080_280_397).saturating_mul(r.into())) .saturating_add(RocksDbWeight::get().reads(7)) .saturating_add(RocksDbWeight::get().reads((160_u64).saturating_mul(r.into()))) .saturating_add(RocksDbWeight::get().writes(3)) @@ -2084,10 +2086,10 @@ impl WeightInfo for () { // Storage: System EventTopics (r:2 w:2) /// The range of component `r` is `[0, 20]`. fn seal_delegate_call(r: u32, ) -> Weight { - // Minimum execution time: 384_523 nanoseconds. - Weight::from_ref_time(385_105_000) - // Standard Error: 6_156_142 - .saturating_add(Weight::from_ref_time(27_780_652_513).saturating_mul(r.into())) + // Minimum execution time: 296_828 nanoseconds. + Weight::from_ref_time(297_685_000) + // Standard Error: 6_864_880 + .saturating_add(Weight::from_ref_time(20_770_321_946).saturating_mul(r.into())) .saturating_add(RocksDbWeight::get().reads(6)) .saturating_add(RocksDbWeight::get().reads((150_u64).saturating_mul(r.into()))) .saturating_add(RocksDbWeight::get().writes(3)) @@ -2101,12 +2103,12 @@ impl WeightInfo for () { /// The range of component `t` is `[0, 1]`. /// The range of component `c` is `[0, 1024]`. fn seal_call_per_transfer_clone_kb(t: u32, c: u32, ) -> Weight { - // Minimum execution time: 9_509_961 nanoseconds. - Weight::from_ref_time(8_509_991_348) - // Standard Error: 6_614_757 - .saturating_add(Weight::from_ref_time(1_244_514_376).saturating_mul(t.into())) - // Standard Error: 9_918 - .saturating_add(Weight::from_ref_time(9_856_517).saturating_mul(c.into())) + // Minimum execution time: 9_824_671 nanoseconds. + Weight::from_ref_time(8_713_861_450) + // Standard Error: 7_388_925 + .saturating_add(Weight::from_ref_time(1_327_819_806).saturating_mul(t.into())) + // Standard Error: 11_079 + .saturating_add(Weight::from_ref_time(9_789_514).saturating_mul(c.into())) .saturating_add(RocksDbWeight::get().reads(167)) .saturating_add(RocksDbWeight::get().reads((81_u64).saturating_mul(t.into()))) .saturating_add(RocksDbWeight::get().writes(163)) @@ -2121,10 +2123,10 @@ impl WeightInfo for () { // Storage: Contracts OwnerInfoOf (r:80 w:80) /// The range of component `r` is `[0, 20]`. fn seal_instantiate(r: u32, ) -> Weight { - // Minimum execution time: 384_604 nanoseconds. - Weight::from_ref_time(385_278_000) - // Standard Error: 21_140_468 - .saturating_add(Weight::from_ref_time(33_100_726_150).saturating_mul(r.into())) + // Minimum execution time: 293_374 nanoseconds. + Weight::from_ref_time(302_067_000) + // Standard Error: 20_137_097 + .saturating_add(Weight::from_ref_time(26_444_812_817).saturating_mul(r.into())) .saturating_add(RocksDbWeight::get().reads(8)) .saturating_add(RocksDbWeight::get().reads((400_u64).saturating_mul(r.into()))) .saturating_add(RocksDbWeight::get().writes(5)) @@ -2141,12 +2143,14 @@ impl WeightInfo for () { /// The range of component `i` is `[0, 960]`. /// The range of component `s` is `[0, 960]`. fn seal_instantiate_per_transfer_input_salt_kb(t: u32, i: u32, s: u32, ) -> Weight { - // Minimum execution time: 129_699_480 nanoseconds. - Weight::from_ref_time(10_187_699_005) - // Standard Error: 155_040 - .saturating_add(Weight::from_ref_time(125_284_310).saturating_mul(i.into())) - // Standard Error: 155_040 - .saturating_add(Weight::from_ref_time(125_850_564).saturating_mul(s.into())) + // Minimum execution time: 126_456_770 nanoseconds. + Weight::from_ref_time(9_223_715_752) + // Standard Error: 96_040_318 + .saturating_add(Weight::from_ref_time(19_550_519).saturating_mul(t.into())) + // Standard Error: 156_614 + .saturating_add(Weight::from_ref_time(122_958_298).saturating_mul(i.into())) + // Standard Error: 156_614 + .saturating_add(Weight::from_ref_time(123_316_079).saturating_mul(s.into())) .saturating_add(RocksDbWeight::get().reads(249)) .saturating_add(RocksDbWeight::get().reads((1_u64).saturating_mul(t.into()))) .saturating_add(RocksDbWeight::get().writes(247)) @@ -2159,10 +2163,10 @@ impl WeightInfo for () { // Storage: System EventTopics (r:2 w:2) /// The range of component `r` is `[0, 1]`. fn seal_hash_sha2_256(r: u32, ) -> Weight { - // Minimum execution time: 380_397 nanoseconds. - Weight::from_ref_time(382_881_855) - // Standard Error: 290_948 - .saturating_add(Weight::from_ref_time(41_017_644).saturating_mul(r.into())) + // Minimum execution time: 289_275 nanoseconds. + Weight::from_ref_time(297_803_961) + // Standard Error: 303_405 + .saturating_add(Weight::from_ref_time(40_800_138).saturating_mul(r.into())) .saturating_add(RocksDbWeight::get().reads(6)) .saturating_add(RocksDbWeight::get().writes(3)) } @@ -2173,10 +2177,10 @@ impl WeightInfo for () { // Storage: System EventTopics (r:2 w:2) /// The range of component `n` is `[0, 1024]`. fn seal_hash_sha2_256_per_kb(n: u32, ) -> Weight { - // Minimum execution time: 422_439 nanoseconds. - Weight::from_ref_time(423_480_000) - // Standard Error: 56_072 - .saturating_add(Weight::from_ref_time(329_103_825).saturating_mul(n.into())) + // Minimum execution time: 336_077 nanoseconds. + Weight::from_ref_time(336_846_000) + // Standard Error: 51_610 + .saturating_add(Weight::from_ref_time(322_208_787).saturating_mul(n.into())) .saturating_add(RocksDbWeight::get().reads(6)) .saturating_add(RocksDbWeight::get().writes(3)) } @@ -2187,10 +2191,10 @@ impl WeightInfo for () { // Storage: System EventTopics (r:2 w:2) /// The range of component `r` is `[0, 1]`. fn seal_hash_keccak_256(r: u32, ) -> Weight { - // Minimum execution time: 380_433 nanoseconds. - Weight::from_ref_time(382_325_624) - // Standard Error: 139_248 - .saturating_add(Weight::from_ref_time(53_159_175).saturating_mul(r.into())) + // Minimum execution time: 290_189 nanoseconds. + Weight::from_ref_time(298_454_259) + // Standard Error: 252_946 + .saturating_add(Weight::from_ref_time(55_034_240).saturating_mul(r.into())) .saturating_add(RocksDbWeight::get().reads(6)) .saturating_add(RocksDbWeight::get().writes(3)) } @@ -2201,10 +2205,10 @@ impl WeightInfo for () { // Storage: System EventTopics (r:2 w:2) /// The range of component `n` is `[0, 1024]`. fn seal_hash_keccak_256_per_kb(n: u32, ) -> Weight { - // Minimum execution time: 434_834 nanoseconds. - Weight::from_ref_time(435_383_000) - // Standard Error: 59_824 - .saturating_add(Weight::from_ref_time(251_297_967).saturating_mul(n.into())) + // Minimum execution time: 350_030 nanoseconds. + Weight::from_ref_time(351_356_000) + // Standard Error: 57_422 + .saturating_add(Weight::from_ref_time(256_912_844).saturating_mul(n.into())) .saturating_add(RocksDbWeight::get().reads(6)) .saturating_add(RocksDbWeight::get().writes(3)) } @@ -2215,10 +2219,10 @@ impl WeightInfo for () { // Storage: System EventTopics (r:2 w:2) /// The range of component `r` is `[0, 1]`. fn seal_hash_blake2_256(r: u32, ) -> Weight { - // Minimum execution time: 379_088 nanoseconds. - Weight::from_ref_time(381_627_077) - // Standard Error: 152_561 - .saturating_add(Weight::from_ref_time(31_696_922).saturating_mul(r.into())) + // Minimum execution time: 288_480 nanoseconds. + Weight::from_ref_time(297_613_263) + // Standard Error: 456_736 + .saturating_add(Weight::from_ref_time(38_100_936).saturating_mul(r.into())) .saturating_add(RocksDbWeight::get().reads(6)) .saturating_add(RocksDbWeight::get().writes(3)) } @@ -2229,10 +2233,10 @@ impl WeightInfo for () { // Storage: System EventTopics (r:2 w:2) /// The range of component `n` is `[0, 1024]`. fn seal_hash_blake2_256_per_kb(n: u32, ) -> Weight { - // Minimum execution time: 411_630 nanoseconds. - Weight::from_ref_time(412_354_000) - // Standard Error: 50_788 - .saturating_add(Weight::from_ref_time(103_105_715).saturating_mul(n.into())) + // Minimum execution time: 321_195 nanoseconds. + Weight::from_ref_time(325_561_000) + // Standard Error: 46_289 + .saturating_add(Weight::from_ref_time(99_536_902).saturating_mul(n.into())) .saturating_add(RocksDbWeight::get().reads(6)) .saturating_add(RocksDbWeight::get().writes(3)) } @@ -2243,10 +2247,10 @@ impl WeightInfo for () { // Storage: System EventTopics (r:2 w:2) /// The range of component `r` is `[0, 1]`. fn seal_hash_blake2_128(r: u32, ) -> Weight { - // Minimum execution time: 379_549 nanoseconds. - Weight::from_ref_time(382_406_346) - // Standard Error: 701_449 - .saturating_add(Weight::from_ref_time(31_066_353).saturating_mul(r.into())) + // Minimum execution time: 287_659 nanoseconds. + Weight::from_ref_time(295_963_340) + // Standard Error: 433_889 + .saturating_add(Weight::from_ref_time(30_251_859).saturating_mul(r.into())) .saturating_add(RocksDbWeight::get().reads(6)) .saturating_add(RocksDbWeight::get().writes(3)) } @@ -2257,10 +2261,10 @@ impl WeightInfo for () { // Storage: System EventTopics (r:2 w:2) /// The range of component `n` is `[0, 1024]`. fn seal_hash_blake2_128_per_kb(n: u32, ) -> Weight { - // Minimum execution time: 412_112 nanoseconds. - Weight::from_ref_time(412_710_000) - // Standard Error: 54_875 - .saturating_add(Weight::from_ref_time(103_169_035).saturating_mul(n.into())) + // Minimum execution time: 324_298 nanoseconds. + Weight::from_ref_time(325_738_000) + // Standard Error: 48_563 + .saturating_add(Weight::from_ref_time(99_479_838).saturating_mul(n.into())) .saturating_add(RocksDbWeight::get().reads(6)) .saturating_add(RocksDbWeight::get().writes(3)) } @@ -2271,10 +2275,10 @@ impl WeightInfo for () { // Storage: System EventTopics (r:2 w:2) /// The range of component `r` is `[0, 1]`. fn seal_ecdsa_recover(r: u32, ) -> Weight { - // Minimum execution time: 381_994 nanoseconds. - Weight::from_ref_time(383_832_551) - // Standard Error: 676_656 - .saturating_add(Weight::from_ref_time(3_020_035_748).saturating_mul(r.into())) + // Minimum execution time: 294_257 nanoseconds. + Weight::from_ref_time(299_467_620) + // Standard Error: 589_749 + .saturating_add(Weight::from_ref_time(3_015_389_579).saturating_mul(r.into())) .saturating_add(RocksDbWeight::get().reads(6)) .saturating_add(RocksDbWeight::get().writes(3)) } @@ -2285,10 +2289,10 @@ impl WeightInfo for () { // Storage: System EventTopics (r:2 w:2) /// The range of component `r` is `[0, 1]`. fn seal_ecdsa_to_eth_address(r: u32, ) -> Weight { - // Minimum execution time: 381_472 nanoseconds. - Weight::from_ref_time(383_590_834) - // Standard Error: 538_574 - .saturating_add(Weight::from_ref_time(2_077_926_265).saturating_mul(r.into())) + // Minimum execution time: 289_480 nanoseconds. + Weight::from_ref_time(298_762_412) + // Standard Error: 397_004 + .saturating_add(Weight::from_ref_time(743_937_087).saturating_mul(r.into())) .saturating_add(RocksDbWeight::get().reads(6)) .saturating_add(RocksDbWeight::get().writes(3)) } @@ -2300,10 +2304,10 @@ impl WeightInfo for () { // Storage: Contracts OwnerInfoOf (r:16 w:16) /// The range of component `r` is `[0, 20]`. fn seal_set_code_hash(r: u32, ) -> Weight { - // Minimum execution time: 381_527 nanoseconds. - Weight::from_ref_time(382_291_000) - // Standard Error: 2_760_840 - .saturating_add(Weight::from_ref_time(1_356_115_009).saturating_mul(r.into())) + // Minimum execution time: 293_494 nanoseconds. + Weight::from_ref_time(297_756_000) + // Standard Error: 2_731_227 + .saturating_add(Weight::from_ref_time(1_387_380_436).saturating_mul(r.into())) .saturating_add(RocksDbWeight::get().reads(6)) .saturating_add(RocksDbWeight::get().reads((225_u64).saturating_mul(r.into()))) .saturating_add(RocksDbWeight::get().writes(3)) @@ -2316,10 +2320,10 @@ impl WeightInfo for () { // Storage: System EventTopics (r:2 w:2) /// The range of component `r` is `[0, 20]`. fn seal_reentrance_count(r: u32, ) -> Weight { - // Minimum execution time: 382_722 nanoseconds. - Weight::from_ref_time(387_231_409) - // Standard Error: 28_817 - .saturating_add(Weight::from_ref_time(11_349_809).saturating_mul(r.into())) + // Minimum execution time: 295_339 nanoseconds. + Weight::from_ref_time(301_577_907) + // Standard Error: 23_836 + .saturating_add(Weight::from_ref_time(10_876_508).saturating_mul(r.into())) .saturating_add(RocksDbWeight::get().reads(6)) .saturating_add(RocksDbWeight::get().writes(3)) } @@ -2330,10 +2334,10 @@ impl WeightInfo for () { // Storage: System EventTopics (r:2 w:2) /// The range of component `r` is `[0, 20]`. fn seal_account_reentrance_count(r: u32, ) -> Weight { - // Minimum execution time: 383_568 nanoseconds. - Weight::from_ref_time(419_835_108) - // Standard Error: 125_982 - .saturating_add(Weight::from_ref_time(25_241_800).saturating_mul(r.into())) + // Minimum execution time: 297_096 nanoseconds. + Weight::from_ref_time(336_319_823) + // Standard Error: 128_941 + .saturating_add(Weight::from_ref_time(25_211_374).saturating_mul(r.into())) .saturating_add(RocksDbWeight::get().reads(6)) .saturating_add(RocksDbWeight::get().writes(3)) } @@ -2345,375 +2349,375 @@ impl WeightInfo for () { // Storage: Contracts Nonce (r:1 w:1) /// The range of component `r` is `[0, 20]`. fn seal_instantiation_nonce(r: u32, ) -> Weight { - // Minimum execution time: 382_084 nanoseconds. - Weight::from_ref_time(388_155_568) - // Standard Error: 29_161 - .saturating_add(Weight::from_ref_time(9_015_217).saturating_mul(r.into())) + // Minimum execution time: 293_819 nanoseconds. + Weight::from_ref_time(302_765_659) + // Standard Error: 28_518 + .saturating_add(Weight::from_ref_time(9_325_552).saturating_mul(r.into())) .saturating_add(RocksDbWeight::get().reads(7)) .saturating_add(RocksDbWeight::get().writes(4)) } /// The range of component `r` is `[0, 50]`. fn instr_i64const(r: u32, ) -> Weight { - // Minimum execution time: 593 nanoseconds. - Weight::from_ref_time(816_706) - // Standard Error: 173 - .saturating_add(Weight::from_ref_time(344_732).saturating_mul(r.into())) + // Minimum execution time: 805 nanoseconds. + Weight::from_ref_time(1_002_066) + // Standard Error: 609 + .saturating_add(Weight::from_ref_time(346_608).saturating_mul(r.into())) } /// The range of component `r` is `[0, 50]`. fn instr_i64load(r: u32, ) -> Weight { - // Minimum execution time: 705 nanoseconds. - Weight::from_ref_time(1_191_205) - // Standard Error: 600 - .saturating_add(Weight::from_ref_time(986_102).saturating_mul(r.into())) + // Minimum execution time: 917 nanoseconds. + Weight::from_ref_time(1_247_710) + // Standard Error: 1_069 + .saturating_add(Weight::from_ref_time(984_608).saturating_mul(r.into())) } /// The range of component `r` is `[0, 50]`. fn instr_i64store(r: u32, ) -> Weight { - // Minimum execution time: 717 nanoseconds. - Weight::from_ref_time(1_019_448) - // Standard Error: 421 - .saturating_add(Weight::from_ref_time(882_531).saturating_mul(r.into())) + // Minimum execution time: 917 nanoseconds. + Weight::from_ref_time(1_244_315) + // Standard Error: 524 + .saturating_add(Weight::from_ref_time(882_167).saturating_mul(r.into())) } /// The range of component `r` is `[0, 50]`. fn instr_select(r: u32, ) -> Weight { - // Minimum execution time: 642 nanoseconds. - Weight::from_ref_time(917_880) - // Standard Error: 362 - .saturating_add(Weight::from_ref_time(957_235).saturating_mul(r.into())) + // Minimum execution time: 791 nanoseconds. + Weight::from_ref_time(1_119_053) + // Standard Error: 379 + .saturating_add(Weight::from_ref_time(956_169).saturating_mul(r.into())) } /// The range of component `r` is `[0, 50]`. fn instr_if(r: u32, ) -> Weight { - // Minimum execution time: 630 nanoseconds. - Weight::from_ref_time(694_427) - // Standard Error: 1_506 - .saturating_add(Weight::from_ref_time(1_298_411).saturating_mul(r.into())) + // Minimum execution time: 782 nanoseconds. + Weight::from_ref_time(773_373) + // Standard Error: 463 + .saturating_add(Weight::from_ref_time(1_299_306).saturating_mul(r.into())) } /// The range of component `r` is `[0, 50]`. fn instr_br(r: u32, ) -> Weight { - // Minimum execution time: 634 nanoseconds. - Weight::from_ref_time(1_101_754) - // Standard Error: 840 - .saturating_add(Weight::from_ref_time(526_433).saturating_mul(r.into())) + // Minimum execution time: 743 nanoseconds. + Weight::from_ref_time(1_273_896) + // Standard Error: 925 + .saturating_add(Weight::from_ref_time(527_015).saturating_mul(r.into())) } /// The range of component `r` is `[0, 50]`. fn instr_br_if(r: u32, ) -> Weight { - // Minimum execution time: 651 nanoseconds. - Weight::from_ref_time(790_908) - // Standard Error: 849 - .saturating_add(Weight::from_ref_time(800_188).saturating_mul(r.into())) + // Minimum execution time: 761 nanoseconds. + Weight::from_ref_time(929_565) + // Standard Error: 1_275 + .saturating_add(Weight::from_ref_time(805_592).saturating_mul(r.into())) } /// The range of component `r` is `[0, 50]`. fn instr_br_table(r: u32, ) -> Weight { - // Minimum execution time: 622 nanoseconds. - Weight::from_ref_time(416_266) - // Standard Error: 1_574 - .saturating_add(Weight::from_ref_time(1_080_225).saturating_mul(r.into())) + // Minimum execution time: 813 nanoseconds. + Weight::from_ref_time(703_802) + // Standard Error: 1_665 + .saturating_add(Weight::from_ref_time(1_074_321).saturating_mul(r.into())) } /// The range of component `e` is `[1, 256]`. fn instr_br_table_per_entry(e: u32, ) -> Weight { - // Minimum execution time: 2_131 nanoseconds. - Weight::from_ref_time(2_540_446) - // Standard Error: 75 - .saturating_add(Weight::from_ref_time(4_997).saturating_mul(e.into())) + // Minimum execution time: 2_347 nanoseconds. + Weight::from_ref_time(2_752_638) + // Standard Error: 69 + .saturating_add(Weight::from_ref_time(4_771).saturating_mul(e.into())) } /// The range of component `r` is `[0, 50]`. fn instr_call(r: u32, ) -> Weight { - // Minimum execution time: 650 nanoseconds. - Weight::from_ref_time(1_577_978) - // Standard Error: 2_696 - .saturating_add(Weight::from_ref_time(2_204_044).saturating_mul(r.into())) + // Minimum execution time: 826 nanoseconds. + Weight::from_ref_time(1_784_596) + // Standard Error: 1_646 + .saturating_add(Weight::from_ref_time(2_189_993).saturating_mul(r.into())) } /// The range of component `r` is `[0, 50]`. fn instr_call_indirect(r: u32, ) -> Weight { - // Minimum execution time: 742 nanoseconds. - Weight::from_ref_time(1_947_575) - // Standard Error: 1_651 - .saturating_add(Weight::from_ref_time(2_799_445).saturating_mul(r.into())) + // Minimum execution time: 926 nanoseconds. + Weight::from_ref_time(1_898_358) + // Standard Error: 2_678 + .saturating_add(Weight::from_ref_time(2_811_379).saturating_mul(r.into())) } /// The range of component `p` is `[0, 128]`. fn instr_call_indirect_per_param(p: u32, ) -> Weight { - // Minimum execution time: 4_230 nanoseconds. - Weight::from_ref_time(5_079_432) - // Standard Error: 315 - .saturating_add(Weight::from_ref_time(179_278).saturating_mul(p.into())) + // Minimum execution time: 4_428 nanoseconds. + Weight::from_ref_time(5_257_445) + // Standard Error: 306 + .saturating_add(Weight::from_ref_time(178_882).saturating_mul(p.into())) } /// The range of component `l` is `[0, 1024]`. fn instr_call_per_local(l: u32, ) -> Weight { - // Minimum execution time: 2_830 nanoseconds. - Weight::from_ref_time(3_601_633) - // Standard Error: 31 - .saturating_add(Weight::from_ref_time(92_499).saturating_mul(l.into())) + // Minimum execution time: 2_945 nanoseconds. + Weight::from_ref_time(4_240_619) + // Standard Error: 39 + .saturating_add(Weight::from_ref_time(46_640).saturating_mul(l.into())) } /// The range of component `r` is `[0, 50]`. fn instr_local_get(r: u32, ) -> Weight { - // Minimum execution time: 2_896 nanoseconds. - Weight::from_ref_time(3_137_247) - // Standard Error: 245 - .saturating_add(Weight::from_ref_time(364_431).saturating_mul(r.into())) + // Minimum execution time: 2_204 nanoseconds. + Weight::from_ref_time(2_430_272) + // Standard Error: 270 + .saturating_add(Weight::from_ref_time(365_718).saturating_mul(r.into())) } /// The range of component `r` is `[0, 50]`. fn instr_local_set(r: u32, ) -> Weight { - // Minimum execution time: 2_795 nanoseconds. - Weight::from_ref_time(3_199_878) - // Standard Error: 549 - .saturating_add(Weight::from_ref_time(380_524).saturating_mul(r.into())) + // Minimum execution time: 2_117 nanoseconds. + Weight::from_ref_time(2_429_354) + // Standard Error: 240 + .saturating_add(Weight::from_ref_time(381_830).saturating_mul(r.into())) } /// The range of component `r` is `[0, 50]`. fn instr_local_tee(r: u32, ) -> Weight { - // Minimum execution time: 2_807 nanoseconds. - Weight::from_ref_time(3_130_120) - // Standard Error: 319 - .saturating_add(Weight::from_ref_time(525_857).saturating_mul(r.into())) + // Minimum execution time: 2_123 nanoseconds. + Weight::from_ref_time(2_460_016) + // Standard Error: 262 + .saturating_add(Weight::from_ref_time(526_554).saturating_mul(r.into())) } /// The range of component `r` is `[0, 50]`. fn instr_global_get(r: u32, ) -> Weight { - // Minimum execution time: 678 nanoseconds. - Weight::from_ref_time(1_013_348) - // Standard Error: 362 - .saturating_add(Weight::from_ref_time(810_232).saturating_mul(r.into())) + // Minimum execution time: 892 nanoseconds. + Weight::from_ref_time(1_234_618) + // Standard Error: 485 + .saturating_add(Weight::from_ref_time(813_721).saturating_mul(r.into())) } /// The range of component `r` is `[0, 50]`. fn instr_global_set(r: u32, ) -> Weight { - // Minimum execution time: 624 nanoseconds. - Weight::from_ref_time(973_583) - // Standard Error: 373 - .saturating_add(Weight::from_ref_time(829_360).saturating_mul(r.into())) + // Minimum execution time: 901 nanoseconds. + Weight::from_ref_time(1_182_838) + // Standard Error: 482 + .saturating_add(Weight::from_ref_time(831_308).saturating_mul(r.into())) } /// The range of component `r` is `[0, 50]`. fn instr_memory_current(r: u32, ) -> Weight { - // Minimum execution time: 698 nanoseconds. - Weight::from_ref_time(904_862) - // Standard Error: 360 - .saturating_add(Weight::from_ref_time(694_614).saturating_mul(r.into())) + // Minimum execution time: 929 nanoseconds. + Weight::from_ref_time(1_221_388) + // Standard Error: 434 + .saturating_add(Weight::from_ref_time(693_301).saturating_mul(r.into())) } /// The range of component `r` is `[0, 1]`. fn instr_memory_grow(r: u32, ) -> Weight { - // Minimum execution time: 642 nanoseconds. - Weight::from_ref_time(735_085) - // Standard Error: 105_815 - .saturating_add(Weight::from_ref_time(233_816_514).saturating_mul(r.into())) + // Minimum execution time: 808 nanoseconds. + Weight::from_ref_time(906_944) + // Standard Error: 6_164 + .saturating_add(Weight::from_ref_time(187_445_255).saturating_mul(r.into())) } /// The range of component `r` is `[0, 50]`. fn instr_i64clz(r: u32, ) -> Weight { - // Minimum execution time: 606 nanoseconds. - Weight::from_ref_time(850_590) - // Standard Error: 262 - .saturating_add(Weight::from_ref_time(507_721).saturating_mul(r.into())) + // Minimum execution time: 807 nanoseconds. + Weight::from_ref_time(1_064_776) + // Standard Error: 287 + .saturating_add(Weight::from_ref_time(507_278).saturating_mul(r.into())) } /// The range of component `r` is `[0, 50]`. fn instr_i64ctz(r: u32, ) -> Weight { - // Minimum execution time: 663 nanoseconds. - Weight::from_ref_time(853_060) - // Standard Error: 250 - .saturating_add(Weight::from_ref_time(514_225).saturating_mul(r.into())) + // Minimum execution time: 776 nanoseconds. + Weight::from_ref_time(1_033_205) + // Standard Error: 443 + .saturating_add(Weight::from_ref_time(512_445).saturating_mul(r.into())) } /// The range of component `r` is `[0, 50]`. fn instr_i64popcnt(r: u32, ) -> Weight { - // Minimum execution time: 605 nanoseconds. - Weight::from_ref_time(849_563) - // Standard Error: 275 - .saturating_add(Weight::from_ref_time(511_494).saturating_mul(r.into())) + // Minimum execution time: 909 nanoseconds. + Weight::from_ref_time(1_089_083) + // Standard Error: 335 + .saturating_add(Weight::from_ref_time(510_392).saturating_mul(r.into())) } /// The range of component `r` is `[0, 50]`. fn instr_i64eqz(r: u32, ) -> Weight { - // Minimum execution time: 618 nanoseconds. - Weight::from_ref_time(839_855) - // Standard Error: 228 - .saturating_add(Weight::from_ref_time(524_614).saturating_mul(r.into())) + // Minimum execution time: 791 nanoseconds. + Weight::from_ref_time(1_063_118) + // Standard Error: 210 + .saturating_add(Weight::from_ref_time(522_817).saturating_mul(r.into())) } /// The range of component `r` is `[0, 50]`. fn instr_i64extendsi32(r: u32, ) -> Weight { - // Minimum execution time: 637 nanoseconds. - Weight::from_ref_time(860_326) + // Minimum execution time: 789 nanoseconds. + Weight::from_ref_time(1_056_006) // Standard Error: 240 - .saturating_add(Weight::from_ref_time(504_847).saturating_mul(r.into())) + .saturating_add(Weight::from_ref_time(505_198).saturating_mul(r.into())) } /// The range of component `r` is `[0, 50]`. fn instr_i64extendui32(r: u32, ) -> Weight { - // Minimum execution time: 623 nanoseconds. - Weight::from_ref_time(844_585) - // Standard Error: 235 - .saturating_add(Weight::from_ref_time(505_821).saturating_mul(r.into())) + // Minimum execution time: 804 nanoseconds. + Weight::from_ref_time(1_087_882) + // Standard Error: 234 + .saturating_add(Weight::from_ref_time(503_830).saturating_mul(r.into())) } /// The range of component `r` is `[0, 50]`. fn instr_i32wrapi64(r: u32, ) -> Weight { - // Minimum execution time: 672 nanoseconds. - Weight::from_ref_time(826_784) - // Standard Error: 225 - .saturating_add(Weight::from_ref_time(504_632).saturating_mul(r.into())) + // Minimum execution time: 756 nanoseconds. + Weight::from_ref_time(1_050_994) + // Standard Error: 172 + .saturating_add(Weight::from_ref_time(503_343).saturating_mul(r.into())) } /// The range of component `r` is `[0, 50]`. fn instr_i64eq(r: u32, ) -> Weight { - // Minimum execution time: 642 nanoseconds. - Weight::from_ref_time(867_080) - // Standard Error: 231 - .saturating_add(Weight::from_ref_time(732_430).saturating_mul(r.into())) + // Minimum execution time: 775 nanoseconds. + Weight::from_ref_time(1_061_228) + // Standard Error: 232 + .saturating_add(Weight::from_ref_time(730_751).saturating_mul(r.into())) } /// The range of component `r` is `[0, 50]`. fn instr_i64ne(r: u32, ) -> Weight { - // Minimum execution time: 639 nanoseconds. - Weight::from_ref_time(866_094) - // Standard Error: 272 - .saturating_add(Weight::from_ref_time(732_560).saturating_mul(r.into())) + // Minimum execution time: 809 nanoseconds. + Weight::from_ref_time(1_080_957) + // Standard Error: 320 + .saturating_add(Weight::from_ref_time(732_246).saturating_mul(r.into())) } /// The range of component `r` is `[0, 50]`. fn instr_i64lts(r: u32, ) -> Weight { - // Minimum execution time: 619 nanoseconds. - Weight::from_ref_time(928_672) - // Standard Error: 484 - .saturating_add(Weight::from_ref_time(739_523).saturating_mul(r.into())) + // Minimum execution time: 808 nanoseconds. + Weight::from_ref_time(1_055_752) + // Standard Error: 496 + .saturating_add(Weight::from_ref_time(740_084).saturating_mul(r.into())) } /// The range of component `r` is `[0, 50]`. fn instr_i64ltu(r: u32, ) -> Weight { - // Minimum execution time: 612 nanoseconds. - Weight::from_ref_time(863_312) - // Standard Error: 328 - .saturating_add(Weight::from_ref_time(743_687).saturating_mul(r.into())) + // Minimum execution time: 792 nanoseconds. + Weight::from_ref_time(1_111_972) + // Standard Error: 270 + .saturating_add(Weight::from_ref_time(741_581).saturating_mul(r.into())) } /// The range of component `r` is `[0, 50]`. fn instr_i64gts(r: u32, ) -> Weight { - // Minimum execution time: 648 nanoseconds. - Weight::from_ref_time(931_331) - // Standard Error: 612 - .saturating_add(Weight::from_ref_time(747_653).saturating_mul(r.into())) + // Minimum execution time: 862 nanoseconds. + Weight::from_ref_time(1_111_246) + // Standard Error: 263 + .saturating_add(Weight::from_ref_time(746_026).saturating_mul(r.into())) } /// The range of component `r` is `[0, 50]`. fn instr_i64gtu(r: u32, ) -> Weight { - // Minimum execution time: 632 nanoseconds. - Weight::from_ref_time(868_901) - // Standard Error: 276 - .saturating_add(Weight::from_ref_time(744_778).saturating_mul(r.into())) + // Minimum execution time: 810 nanoseconds. + Weight::from_ref_time(1_094_933) + // Standard Error: 271 + .saturating_add(Weight::from_ref_time(743_645).saturating_mul(r.into())) } /// The range of component `r` is `[0, 50]`. fn instr_i64les(r: u32, ) -> Weight { - // Minimum execution time: 629 nanoseconds. - Weight::from_ref_time(898_516) - // Standard Error: 288 - .saturating_add(Weight::from_ref_time(734_393).saturating_mul(r.into())) + // Minimum execution time: 769 nanoseconds. + Weight::from_ref_time(819_933) + // Standard Error: 4_528 + .saturating_add(Weight::from_ref_time(751_599).saturating_mul(r.into())) } /// The range of component `r` is `[0, 50]`. fn instr_i64leu(r: u32, ) -> Weight { - // Minimum execution time: 603 nanoseconds. - Weight::from_ref_time(853_744) - // Standard Error: 204 - .saturating_add(Weight::from_ref_time(739_499).saturating_mul(r.into())) + // Minimum execution time: 797 nanoseconds. + Weight::from_ref_time(1_107_352) + // Standard Error: 344 + .saturating_add(Weight::from_ref_time(738_684).saturating_mul(r.into())) } /// The range of component `r` is `[0, 50]`. fn instr_i64ges(r: u32, ) -> Weight { - // Minimum execution time: 625 nanoseconds. - Weight::from_ref_time(885_174) - // Standard Error: 238 - .saturating_add(Weight::from_ref_time(734_010).saturating_mul(r.into())) + // Minimum execution time: 784 nanoseconds. + Weight::from_ref_time(1_068_487) + // Standard Error: 3_796 + .saturating_add(Weight::from_ref_time(741_207).saturating_mul(r.into())) } /// The range of component `r` is `[0, 50]`. fn instr_i64geu(r: u32, ) -> Weight { - // Minimum execution time: 602 nanoseconds. - Weight::from_ref_time(915_329) - // Standard Error: 273 - .saturating_add(Weight::from_ref_time(738_209).saturating_mul(r.into())) + // Minimum execution time: 813 nanoseconds. + Weight::from_ref_time(1_067_278) + // Standard Error: 459 + .saturating_add(Weight::from_ref_time(737_841).saturating_mul(r.into())) } /// The range of component `r` is `[0, 50]`. fn instr_i64add(r: u32, ) -> Weight { - // Minimum execution time: 585 nanoseconds. - Weight::from_ref_time(862_239) - // Standard Error: 294 - .saturating_add(Weight::from_ref_time(719_000).saturating_mul(r.into())) + // Minimum execution time: 798 nanoseconds. + Weight::from_ref_time(1_100_254) + // Standard Error: 297 + .saturating_add(Weight::from_ref_time(717_622).saturating_mul(r.into())) } /// The range of component `r` is `[0, 50]`. fn instr_i64sub(r: u32, ) -> Weight { - // Minimum execution time: 633 nanoseconds. - Weight::from_ref_time(881_696) - // Standard Error: 195 - .saturating_add(Weight::from_ref_time(713_153).saturating_mul(r.into())) + // Minimum execution time: 778 nanoseconds. + Weight::from_ref_time(1_063_748) + // Standard Error: 378 + .saturating_add(Weight::from_ref_time(713_577).saturating_mul(r.into())) } /// The range of component `r` is `[0, 50]`. fn instr_i64mul(r: u32, ) -> Weight { - // Minimum execution time: 629 nanoseconds. - Weight::from_ref_time(856_238) - // Standard Error: 269 - .saturating_add(Weight::from_ref_time(715_451).saturating_mul(r.into())) + // Minimum execution time: 780 nanoseconds. + Weight::from_ref_time(1_088_755) + // Standard Error: 316 + .saturating_add(Weight::from_ref_time(714_127).saturating_mul(r.into())) } /// The range of component `r` is `[0, 50]`. fn instr_i64divs(r: u32, ) -> Weight { - // Minimum execution time: 633 nanoseconds. - Weight::from_ref_time(880_804) - // Standard Error: 725 - .saturating_add(Weight::from_ref_time(1_349_577).saturating_mul(r.into())) + // Minimum execution time: 776 nanoseconds. + Weight::from_ref_time(1_062_591) + // Standard Error: 610 + .saturating_add(Weight::from_ref_time(1_355_430).saturating_mul(r.into())) } /// The range of component `r` is `[0, 50]`. fn instr_i64divu(r: u32, ) -> Weight { - // Minimum execution time: 639 nanoseconds. - Weight::from_ref_time(867_528) - // Standard Error: 1_940 - .saturating_add(Weight::from_ref_time(1_287_959).saturating_mul(r.into())) + // Minimum execution time: 801 nanoseconds. + Weight::from_ref_time(1_092_395) + // Standard Error: 423 + .saturating_add(Weight::from_ref_time(1_282_356).saturating_mul(r.into())) } /// The range of component `r` is `[0, 50]`. fn instr_i64rems(r: u32, ) -> Weight { - // Minimum execution time: 595 nanoseconds. - Weight::from_ref_time(851_253) - // Standard Error: 503 - .saturating_add(Weight::from_ref_time(1_398_668).saturating_mul(r.into())) + // Minimum execution time: 822 nanoseconds. + Weight::from_ref_time(1_083_079) + // Standard Error: 562 + .saturating_add(Weight::from_ref_time(1_398_522).saturating_mul(r.into())) } /// The range of component `r` is `[0, 50]`. fn instr_i64remu(r: u32, ) -> Weight { - // Minimum execution time: 604 nanoseconds. - Weight::from_ref_time(804_977) - // Standard Error: 812 - .saturating_add(Weight::from_ref_time(1_288_816).saturating_mul(r.into())) + // Minimum execution time: 798 nanoseconds. + Weight::from_ref_time(1_088_997) + // Standard Error: 401 + .saturating_add(Weight::from_ref_time(1_283_442).saturating_mul(r.into())) } /// The range of component `r` is `[0, 50]`. fn instr_i64and(r: u32, ) -> Weight { - // Minimum execution time: 609 nanoseconds. - Weight::from_ref_time(890_945) - // Standard Error: 1_645 - .saturating_add(Weight::from_ref_time(719_770).saturating_mul(r.into())) + // Minimum execution time: 815 nanoseconds. + Weight::from_ref_time(1_081_903) + // Standard Error: 264 + .saturating_add(Weight::from_ref_time(718_069).saturating_mul(r.into())) } /// The range of component `r` is `[0, 50]`. fn instr_i64or(r: u32, ) -> Weight { - // Minimum execution time: 628 nanoseconds. - Weight::from_ref_time(897_973) - // Standard Error: 1_641 - .saturating_add(Weight::from_ref_time(718_838).saturating_mul(r.into())) + // Minimum execution time: 813 nanoseconds. + Weight::from_ref_time(1_091_426) + // Standard Error: 296 + .saturating_add(Weight::from_ref_time(717_894).saturating_mul(r.into())) } /// The range of component `r` is `[0, 50]`. fn instr_i64xor(r: u32, ) -> Weight { - // Minimum execution time: 634 nanoseconds. - Weight::from_ref_time(848_440) - // Standard Error: 249 - .saturating_add(Weight::from_ref_time(718_206).saturating_mul(r.into())) + // Minimum execution time: 794 nanoseconds. + Weight::from_ref_time(1_040_144) + // Standard Error: 400 + .saturating_add(Weight::from_ref_time(719_886).saturating_mul(r.into())) } /// The range of component `r` is `[0, 50]`. fn instr_i64shl(r: u32, ) -> Weight { - // Minimum execution time: 625 nanoseconds. - Weight::from_ref_time(881_579) - // Standard Error: 273 - .saturating_add(Weight::from_ref_time(736_934).saturating_mul(r.into())) + // Minimum execution time: 774 nanoseconds. + Weight::from_ref_time(1_092_466) + // Standard Error: 4_173 + .saturating_add(Weight::from_ref_time(739_873).saturating_mul(r.into())) } /// The range of component `r` is `[0, 50]`. fn instr_i64shrs(r: u32, ) -> Weight { - // Minimum execution time: 630 nanoseconds. - Weight::from_ref_time(897_813) - // Standard Error: 249 - .saturating_add(Weight::from_ref_time(734_657).saturating_mul(r.into())) + // Minimum execution time: 793 nanoseconds. + Weight::from_ref_time(1_105_611) + // Standard Error: 394 + .saturating_add(Weight::from_ref_time(735_358).saturating_mul(r.into())) } /// The range of component `r` is `[0, 50]`. fn instr_i64shru(r: u32, ) -> Weight { - // Minimum execution time: 607 nanoseconds. - Weight::from_ref_time(871_660) - // Standard Error: 312 - .saturating_add(Weight::from_ref_time(735_377).saturating_mul(r.into())) + // Minimum execution time: 805 nanoseconds. + Weight::from_ref_time(1_130_462) + // Standard Error: 327 + .saturating_add(Weight::from_ref_time(734_198).saturating_mul(r.into())) } /// The range of component `r` is `[0, 50]`. fn instr_i64rotl(r: u32, ) -> Weight { - // Minimum execution time: 610 nanoseconds. - Weight::from_ref_time(861_640) - // Standard Error: 293 - .saturating_add(Weight::from_ref_time(735_524).saturating_mul(r.into())) + // Minimum execution time: 831 nanoseconds. + Weight::from_ref_time(1_118_894) + // Standard Error: 389 + .saturating_add(Weight::from_ref_time(733_834).saturating_mul(r.into())) } /// The range of component `r` is `[0, 50]`. fn instr_i64rotr(r: u32, ) -> Weight { - // Minimum execution time: 625 nanoseconds. - Weight::from_ref_time(880_203) - // Standard Error: 373 - .saturating_add(Weight::from_ref_time(737_354).saturating_mul(r.into())) + // Minimum execution time: 772 nanoseconds. + Weight::from_ref_time(1_123_733) + // Standard Error: 337 + .saturating_add(Weight::from_ref_time(733_923).saturating_mul(r.into())) } } From 293c1f24e968a8d956250324fb7e91b7df4507df Mon Sep 17 00:00:00 2001 From: Jegor Sidorenko <5252494+jsidorenko@users.noreply.github.com> Date: Wed, 11 Jan 2023 15:27:59 +0200 Subject: [PATCH 002/558] [NFTs] Track item's metadata depositor (#13124) * Refactor do_mint() * Track the depositor of item's metadata * Revert back the access control * On collection destroy return the metadata deposit * Clear the metadata on item burn returning the deposit * Address comments * Fix clippy * Don't return Ok on non-existing attribute removal --- frame/nfts/src/features/attributes.rs | 105 +++++++++--------- .../src/features/create_delete_collection.rs | 13 ++- frame/nfts/src/features/create_delete_item.rs | 31 ++++-- frame/nfts/src/features/metadata.rs | 69 ++++++++---- frame/nfts/src/impl_nonfungibles.rs | 6 +- frame/nfts/src/lib.rs | 17 +-- frame/nfts/src/tests.rs | 4 +- frame/nfts/src/types.rs | 22 +++- 8 files changed, 161 insertions(+), 106 deletions(-) diff --git a/frame/nfts/src/features/attributes.rs b/frame/nfts/src/features/attributes.rs index da663d39a..b25f2a60c 100644 --- a/frame/nfts/src/features/attributes.rs +++ b/frame/nfts/src/features/attributes.rs @@ -152,65 +152,68 @@ impl, I: 'static> Pallet { namespace: AttributeNamespace, key: BoundedVec, ) -> DispatchResult { - if let Some((_, deposit)) = - Attribute::::take((collection, maybe_item, &namespace, &key)) - { - let mut collection_details = - Collection::::get(&collection).ok_or(Error::::UnknownCollection)?; - - if let Some(check_owner) = &maybe_check_owner { - if deposit.account != maybe_check_owner { - ensure!( - Self::is_valid_namespace( - &check_owner, - &namespace, - &collection, - &collection_details.owner, - &maybe_item, - )?, - Error::::NoPermission - ); - } + let (_, deposit) = Attribute::::take((collection, maybe_item, &namespace, &key)) + .ok_or(Error::::AttributeNotFound)?; + let mut collection_details = + Collection::::get(&collection).ok_or(Error::::UnknownCollection)?; - // can't clear `CollectionOwner` type attributes if the collection/item is locked - match namespace { - AttributeNamespace::CollectionOwner => match maybe_item { - None => { - let collection_config = Self::get_collection_config(&collection)?; - ensure!( - collection_config - .is_setting_enabled(CollectionSetting::UnlockedAttributes), - Error::::LockedCollectionAttributes - ) - }, - Some(item) => { - // NOTE: if the item was previously burned, the ItemConfigOf record - // might not exist. In that case, we allow to clear the attribute. - let maybe_is_locked = Self::get_item_config(&collection, &item) - .map_or(false, |c| { - c.has_disabled_setting(ItemSetting::UnlockedAttributes) - }); - ensure!(!maybe_is_locked, Error::::LockedItemAttributes); - }, - }, - _ => (), - }; + if let Some(check_owner) = &maybe_check_owner { + // validate the provided namespace when it's not a root call and the caller is not + // the same as the `deposit.account` (e.g. the deposit was paid by different account) + if deposit.account != maybe_check_owner { + ensure!( + Self::is_valid_namespace( + &check_owner, + &namespace, + &collection, + &collection_details.owner, + &maybe_item, + )?, + Error::::NoPermission + ); } - collection_details.attributes.saturating_dec(); + // can't clear `CollectionOwner` type attributes if the collection/item is locked match namespace { - AttributeNamespace::CollectionOwner => { - collection_details.owner_deposit.saturating_reduce(deposit.amount); - T::Currency::unreserve(&collection_details.owner, deposit.amount); + AttributeNamespace::CollectionOwner => match maybe_item { + None => { + let collection_config = Self::get_collection_config(&collection)?; + ensure!( + collection_config + .is_setting_enabled(CollectionSetting::UnlockedAttributes), + Error::::LockedCollectionAttributes + ) + }, + Some(item) => { + // NOTE: if the item was previously burned, the ItemConfigOf record + // might not exist. In that case, we allow to clear the attribute. + let maybe_is_locked = Self::get_item_config(&collection, &item) + .map_or(false, |c| { + c.has_disabled_setting(ItemSetting::UnlockedAttributes) + }); + ensure!(!maybe_is_locked, Error::::LockedItemAttributes); + }, }, _ => (), }; - if let Some(deposit_account) = deposit.account { - T::Currency::unreserve(&deposit_account, deposit.amount); - } - Collection::::insert(collection, &collection_details); - Self::deposit_event(Event::AttributeCleared { collection, maybe_item, key, namespace }); } + + collection_details.attributes.saturating_dec(); + match namespace { + AttributeNamespace::CollectionOwner => { + collection_details.owner_deposit.saturating_reduce(deposit.amount); + T::Currency::unreserve(&collection_details.owner, deposit.amount); + }, + _ => (), + }; + + if let Some(deposit_account) = deposit.account { + T::Currency::unreserve(&deposit_account, deposit.amount); + } + + Collection::::insert(collection, &collection_details); + Self::deposit_event(Event::AttributeCleared { collection, maybe_item, key, namespace }); + Ok(()) } diff --git a/frame/nfts/src/features/create_delete_collection.rs b/frame/nfts/src/features/create_delete_collection.rs index 86625bf49..a2caa04bc 100644 --- a/frame/nfts/src/features/create_delete_collection.rs +++ b/frame/nfts/src/features/create_delete_collection.rs @@ -82,12 +82,13 @@ impl, I: 'static> Pallet { Account::::remove((&details.owner, &collection, &item)); T::Currency::unreserve(&details.deposit.account, details.deposit.amount); } - #[allow(deprecated)] - ItemMetadataOf::::remove_prefix(&collection, None); - #[allow(deprecated)] - ItemPriceOf::::remove_prefix(&collection, None); - #[allow(deprecated)] - PendingSwapOf::::remove_prefix(&collection, None); + for (_, metadata) in ItemMetadataOf::::drain_prefix(&collection) { + if let Some(depositor) = metadata.deposit.account { + T::Currency::unreserve(&depositor, metadata.deposit.amount); + } + } + let _ = ItemPriceOf::::clear_prefix(&collection, witness.items, None); + let _ = PendingSwapOf::::clear_prefix(&collection, witness.items, None); CollectionMetadataOf::::remove(&collection); Self::clear_roles(&collection)?; diff --git a/frame/nfts/src/features/create_delete_item.rs b/frame/nfts/src/features/create_delete_item.rs index 8d6c8a280..f724fe5c6 100644 --- a/frame/nfts/src/features/create_delete_item.rs +++ b/frame/nfts/src/features/create_delete_item.rs @@ -22,10 +22,9 @@ impl, I: 'static> Pallet { pub fn do_mint( collection: T::CollectionId, item: T::ItemId, - depositor: T::AccountId, + maybe_depositor: Option, mint_to: T::AccountId, item_config: ItemConfig, - deposit_collection_owner: bool, with_details_and_config: impl FnOnce( &CollectionDetailsFor, &CollectionConfigFor, @@ -55,9 +54,9 @@ impl, I: 'static> Pallet { true => T::ItemDeposit::get(), false => Zero::zero(), }; - let deposit_account = match deposit_collection_owner { - true => collection_details.owner.clone(), - false => depositor, + let deposit_account = match maybe_depositor { + None => collection_details.owner.clone(), + Some(depositor) => depositor, }; let item_owner = mint_to.clone(); @@ -92,6 +91,7 @@ impl, I: 'static> Pallet { with_details: impl FnOnce(&ItemDetailsFor) -> DispatchResult, ) -> DispatchResult { ensure!(!T::Locker::is_locked(collection, item), Error::::ItemLocked); + let item_config = Self::get_item_config(&collection, &item)?; let owner = Collection::::try_mutate( &collection, |maybe_collection_details| -> Result { @@ -104,6 +104,24 @@ impl, I: 'static> Pallet { // Return the deposit. T::Currency::unreserve(&details.deposit.account, details.deposit.amount); collection_details.items.saturating_dec(); + + // Clear the metadata if it's not locked. + if item_config.is_setting_enabled(ItemSetting::UnlockedMetadata) { + if let Some(metadata) = ItemMetadataOf::::take(&collection, &item) { + let depositor_account = + metadata.deposit.account.unwrap_or(collection_details.owner.clone()); + + T::Currency::unreserve(&depositor_account, metadata.deposit.amount); + collection_details.item_metadatas.saturating_dec(); + + if depositor_account == collection_details.owner { + collection_details + .owner_deposit + .saturating_reduce(metadata.deposit.amount); + } + } + } + Ok(details.owner) }, )?; @@ -116,8 +134,7 @@ impl, I: 'static> Pallet { // NOTE: if item's settings are not empty (e.g. item's metadata is locked) // then we keep the record and don't remove it - let config = Self::get_item_config(&collection, &item)?; - if !config.has_disabled_settings() { + if !item_config.has_disabled_settings() { ItemConfigOf::::remove(&collection, &item); } diff --git a/frame/nfts/src/features/metadata.rs b/frame/nfts/src/features/metadata.rs index 942f37714..272b22474 100644 --- a/frame/nfts/src/features/metadata.rs +++ b/frame/nfts/src/features/metadata.rs @@ -19,19 +19,21 @@ use crate::*; use frame_support::pallet_prelude::*; impl, I: 'static> Pallet { + /// Note: if `maybe_depositor` is None, that means the depositor will be a collection's owner pub(crate) fn do_set_item_metadata( maybe_check_owner: Option, collection: T::CollectionId, item: T::ItemId, data: BoundedVec, + maybe_depositor: Option, ) -> DispatchResult { + let is_root = maybe_check_owner.is_none(); let mut collection_details = Collection::::get(&collection).ok_or(Error::::UnknownCollection)?; let item_config = Self::get_item_config(&collection, &item)?; ensure!( - maybe_check_owner.is_none() || - item_config.is_setting_enabled(ItemSetting::UnlockedMetadata), + is_root || item_config.is_setting_enabled(ItemSetting::UnlockedMetadata), Error::::LockedItemMetadata ); @@ -45,24 +47,38 @@ impl, I: 'static> Pallet { if metadata.is_none() { collection_details.item_metadatas.saturating_inc(); } - let old_deposit = metadata.take().map_or(Zero::zero(), |m| m.deposit); - collection_details.owner_deposit.saturating_reduce(old_deposit); + + let old_deposit = metadata + .take() + .map_or(ItemMetadataDeposit { account: None, amount: Zero::zero() }, |m| m.deposit); + let mut deposit = Zero::zero(); - if collection_config.is_setting_enabled(CollectionSetting::DepositRequired) && - maybe_check_owner.is_some() + if collection_config.is_setting_enabled(CollectionSetting::DepositRequired) && !is_root { deposit = T::DepositPerByte::get() .saturating_mul(((data.len()) as u32).into()) .saturating_add(T::MetadataDepositBase::get()); } - if deposit > old_deposit { - T::Currency::reserve(&collection_details.owner, deposit - old_deposit)?; - } else if deposit < old_deposit { - T::Currency::unreserve(&collection_details.owner, old_deposit - deposit); + + // the previous deposit was taken from the item's owner + if old_deposit.account.is_some() && maybe_depositor.is_none() { + T::Currency::unreserve(&old_deposit.account.unwrap(), old_deposit.amount); + T::Currency::reserve(&collection_details.owner, deposit)?; + } else if deposit > old_deposit.amount { + T::Currency::reserve(&collection_details.owner, deposit - old_deposit.amount)?; + } else if deposit < old_deposit.amount { + T::Currency::unreserve(&collection_details.owner, old_deposit.amount - deposit); + } + + if maybe_depositor.is_none() { + collection_details.owner_deposit.saturating_accrue(deposit); + collection_details.owner_deposit.saturating_reduce(old_deposit.amount); } - collection_details.owner_deposit.saturating_accrue(deposit); - *metadata = Some(ItemMetadata { deposit, data: data.clone() }); + *metadata = Some(ItemMetadata { + deposit: ItemMetadataDeposit { account: maybe_depositor, amount: deposit }, + data: data.clone(), + }); Collection::::insert(&collection, &collection_details); Self::deposit_event(Event::ItemMetadataSet { collection, item, data }); @@ -75,8 +91,14 @@ impl, I: 'static> Pallet { collection: T::CollectionId, item: T::ItemId, ) -> DispatchResult { + let is_root = maybe_check_owner.is_none(); + let metadata = ItemMetadataOf::::take(collection, item) + .ok_or(Error::::MetadataNotFound)?; let mut collection_details = Collection::::get(&collection).ok_or(Error::::UnknownCollection)?; + let depositor_account = + metadata.deposit.account.unwrap_or(collection_details.owner.clone()); + if let Some(check_owner) = &maybe_check_owner { ensure!(check_owner == &collection_details.owner, Error::::NoPermission); } @@ -85,20 +107,19 @@ impl, I: 'static> Pallet { let is_locked = Self::get_item_config(&collection, &item) .map_or(false, |c| c.has_disabled_setting(ItemSetting::UnlockedMetadata)); - ensure!(maybe_check_owner.is_none() || !is_locked, Error::::LockedItemMetadata); + ensure!(is_root || !is_locked, Error::::LockedItemMetadata); - ItemMetadataOf::::try_mutate_exists(collection, item, |metadata| { - if metadata.is_some() { - collection_details.item_metadatas.saturating_dec(); - } - let deposit = metadata.take().ok_or(Error::::UnknownItem)?.deposit; - T::Currency::unreserve(&collection_details.owner, deposit); - collection_details.owner_deposit.saturating_reduce(deposit); + collection_details.item_metadatas.saturating_dec(); + T::Currency::unreserve(&depositor_account, metadata.deposit.amount); - Collection::::insert(&collection, &collection_details); - Self::deposit_event(Event::ItemMetadataCleared { collection, item }); - Ok(()) - }) + if depositor_account == collection_details.owner { + collection_details.owner_deposit.saturating_reduce(metadata.deposit.amount); + } + + Collection::::insert(&collection, &collection_details); + Self::deposit_event(Event::ItemMetadataCleared { collection, item }); + + Ok(()) } pub(crate) fn do_set_collection_metadata( diff --git a/frame/nfts/src/impl_nonfungibles.rs b/frame/nfts/src/impl_nonfungibles.rs index ab4d7a16e..86d82b402 100644 --- a/frame/nfts/src/impl_nonfungibles.rs +++ b/frame/nfts/src/impl_nonfungibles.rs @@ -157,10 +157,12 @@ impl, I: 'static> Mutate<::AccountId, ItemConfig Self::do_mint( *collection, *item, - who.clone(), + match deposit_collection_owner { + true => None, + false => Some(who.clone()), + }, who.clone(), *item_config, - deposit_collection_owner, |_, _| Ok(()), ) } diff --git a/frame/nfts/src/lib.rs b/frame/nfts/src/lib.rs index 9a6242143..8f24c8dcd 100644 --- a/frame/nfts/src/lib.rs +++ b/frame/nfts/src/lib.rs @@ -263,7 +263,7 @@ pub mod pallet { T::CollectionId, Blake2_128Concat, T::ItemId, - ItemMetadata, T::StringLimit>, + ItemMetadata, T::StringLimit>, OptionQuery, >; @@ -559,6 +559,10 @@ pub mod pallet { UnknownItem, /// Swap doesn't exist. UnknownSwap, + /// The given item has no metadata set. + MetadataNotFound, + /// The provided attribute can't be found. + AttributeNotFound, /// Item is not for sale. NotForSale, /// The provided bid is too low. @@ -746,10 +750,9 @@ pub mod pallet { Self::do_mint( collection, item, - caller.clone(), + Some(caller.clone()), mint_to.clone(), item_config, - false, |collection_details, collection_config| { // Issuer can mint regardless of mint settings if Self::has_role(&collection, &caller, CollectionRole::Issuer) { @@ -849,9 +852,7 @@ pub mod pallet { Error::::NoPermission ); } - Self::do_mint(collection, item, mint_to.clone(), mint_to, item_config, true, |_, _| { - Ok(()) - }) + Self::do_mint(collection, item, None, mint_to, item_config, |_, _| Ok(())) } /// Destroy a single item. @@ -1362,7 +1363,7 @@ pub mod pallet { /// Clear an attribute for a collection or item. /// /// Origin must be either `ForceOrigin` or Signed and the sender should be the Owner of the - /// `collection`. + /// attribute. /// /// Any deposit is freed for the collection's owner. /// @@ -1464,7 +1465,7 @@ pub mod pallet { let maybe_check_owner = T::ForceOrigin::try_origin(origin) .map(|_| None) .or_else(|origin| ensure_signed(origin).map(Some).map_err(DispatchError::from))?; - Self::do_set_item_metadata(maybe_check_owner, collection, item, data) + Self::do_set_item_metadata(maybe_check_owner, collection, item, data, None) } /// Clear the metadata for an item. diff --git a/frame/nfts/src/tests.rs b/frame/nfts/src/tests.rs index f8187b377..ebbba33b0 100644 --- a/frame/nfts/src/tests.rs +++ b/frame/nfts/src/tests.rs @@ -608,7 +608,7 @@ fn set_item_metadata_should_work() { ); assert_noop!( Nfts::clear_metadata(RuntimeOrigin::signed(1), 1, 42), - Error::::UnknownCollection, + Error::::MetadataNotFound, ); assert_ok!(Nfts::clear_metadata(RuntimeOrigin::root(), 0, 42)); assert!(!ItemMetadataOf::::contains_key(0, 42)); @@ -1267,7 +1267,7 @@ fn burn_works() { assert_noop!( Nfts::burn(RuntimeOrigin::signed(5), 0, 42, Some(5)), - Error::::UnknownCollection + Error::::UnknownItem ); assert_ok!(Nfts::force_mint(RuntimeOrigin::signed(2), 0, 42, 5, default_item_config())); diff --git a/frame/nfts/src/types.rs b/frame/nfts/src/types.rs index 9b89fb964..d8938aab4 100644 --- a/frame/nfts/src/types.rs +++ b/frame/nfts/src/types.rs @@ -43,6 +43,8 @@ pub(super) type ItemDepositOf = ItemDeposit, ::AccountId>; pub(super) type AttributeDepositOf = AttributeDeposit, ::AccountId>; +pub(super) type ItemMetadataDepositOf = + ItemMetadataDeposit, ::AccountId>; pub(super) type ItemDetailsFor = ItemDetails<::AccountId, ItemDepositOf, ApprovalsOf>; pub(super) type BalanceOf = @@ -137,12 +139,12 @@ pub struct ItemDeposit { /// Information about the collection's metadata. #[derive(Clone, Encode, Decode, Eq, PartialEq, RuntimeDebug, Default, TypeInfo, MaxEncodedLen)] #[scale_info(skip_type_params(StringLimit))] -#[codec(mel_bound(DepositBalance: MaxEncodedLen))] -pub struct CollectionMetadata> { +#[codec(mel_bound(Deposit: MaxEncodedLen))] +pub struct CollectionMetadata> { /// The balance deposited for this metadata. /// /// This pays for the data stored in this struct. - pub(super) deposit: DepositBalance, + pub(super) deposit: Deposit, /// General information concerning this collection. Limited in length by `StringLimit`. This /// will generally be either a JSON dump or the hash of some JSON which can be found on a /// hash-addressable global publication system such as IPFS. @@ -152,12 +154,11 @@ pub struct CollectionMetadata> { /// Information about the item's metadata. #[derive(Clone, Encode, Decode, Eq, PartialEq, RuntimeDebug, Default, TypeInfo, MaxEncodedLen)] #[scale_info(skip_type_params(StringLimit))] -#[codec(mel_bound(DepositBalance: MaxEncodedLen))] -pub struct ItemMetadata> { +pub struct ItemMetadata> { /// The balance deposited for this metadata. /// /// This pays for the data stored in this struct. - pub(super) deposit: DepositBalance, + pub(super) deposit: Deposit, /// General information concerning this item. Limited in length by `StringLimit`. This will /// generally be either a JSON dump or the hash of some JSON which can be found on a /// hash-addressable global publication system such as IPFS. @@ -199,6 +200,15 @@ pub struct AttributeDeposit { pub(super) amount: DepositBalance, } +/// Information about the reserved item's metadata deposit. +#[derive(Clone, Encode, Decode, Eq, PartialEq, RuntimeDebug, TypeInfo, MaxEncodedLen)] +pub struct ItemMetadataDeposit { + /// A depositor account, None means the deposit is collection's owner. + pub(super) account: Option, + /// An amount that gets reserved. + pub(super) amount: DepositBalance, +} + /// Specifies whether the tokens will be sent or received. #[derive(Clone, Encode, Decode, Eq, PartialEq, RuntimeDebug, TypeInfo, MaxEncodedLen)] pub enum PriceDirection { From e752af116c0b92e37bb5aeef19d3934cc9347439 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bastian=20K=C3=B6cher?= Date: Wed, 11 Jan 2023 16:40:52 +0100 Subject: [PATCH 003/558] `IntegrityTest` implementation should be feature gated (#13094) * `IntegrityTest` implementation should be feature gated The initial implementation for the old declarative macros is still feature gating the implementation. As we only call this in a test, there is no need to have this compiled for wasm. * Don't assume that all "consumers" have a `std` feature --- .../procedural/src/pallet/expand/hooks.rs | 19 +++++++++++-------- frame/support/src/lib.rs | 3 +++ frame/system/benches/bench.rs | 5 +---- 3 files changed, 15 insertions(+), 12 deletions(-) diff --git a/frame/support/procedural/src/pallet/expand/hooks.rs b/frame/support/procedural/src/pallet/expand/hooks.rs index c2b70c563..c41a65b57 100644 --- a/frame/support/procedural/src/pallet/expand/hooks.rs +++ b/frame/support/procedural/src/pallet/expand/hooks.rs @@ -191,16 +191,19 @@ pub fn expand_hooks(def: &mut Def) -> proc_macro2::TokenStream { } } - impl<#type_impl_gen> - #frame_support::traits::IntegrityTest + // Integrity tests are only required for when `std` is enabled. + #frame_support::std_enabled! { + impl<#type_impl_gen> + #frame_support::traits::IntegrityTest for #pallet_ident<#type_use_gen> #where_clause - { - fn integrity_test() { - < - Self as #frame_support::traits::Hooks< + { + fn integrity_test() { + < + Self as #frame_support::traits::Hooks< ::BlockNumber - > - >::integrity_test() + > + >::integrity_test() + } } } diff --git a/frame/support/src/lib.rs b/frame/support/src/lib.rs index efecbb75f..902893972 100644 --- a/frame/support/src/lib.rs +++ b/frame/support/src/lib.rs @@ -2744,3 +2744,6 @@ pub mod pallet_macros { type_value, unbounded, validate_unsigned, weight, whitelist_storage, }; } + +// Generate a macro that will enable/disable code based on `std` feature being active. +sp_core::generate_feature_enabled_macro!(std_enabled, feature = "std", $); diff --git a/frame/system/benches/bench.rs b/frame/system/benches/bench.rs index a623a17dc..f2acc319d 100644 --- a/frame/system/benches/bench.rs +++ b/frame/system/benches/bench.rs @@ -16,10 +16,7 @@ // limitations under the License. use criterion::{black_box, criterion_group, criterion_main, Criterion}; -use frame_support::{ - traits::{ConstU32, ConstU64}, - weights::Weight, -}; +use frame_support::traits::{ConstU32, ConstU64}; use sp_core::H256; use sp_runtime::{ testing::Header, From ad58606c403e13cb1819174ec311d82a02e40028 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 11 Jan 2023 15:59:13 +0000 Subject: [PATCH 004/558] Bump git2 from 0.14.4 to 0.16.0 (#13123) Bumps [git2](https://github.com/rust-lang/git2-rs) from 0.14.4 to 0.16.0. - [Release notes](https://github.com/rust-lang/git2-rs/releases) - [Commits](https://github.com/rust-lang/git2-rs/compare/0.14.4...git2-curl-0.16.0) --- updated-dependencies: - dependency-name: git2 dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- Cargo.lock | 8 ++++---- utils/frame/generate-bags/Cargo.toml | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index eb644202a..68c144b3f 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2705,9 +2705,9 @@ checksum = "dec7af912d60cdbd3677c1af9352ebae6fb8394d165568a2234df0fa00f87793" [[package]] name = "git2" -version = "0.14.4" +version = "0.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d0155506aab710a86160ddb504a480d2964d7ab5b9e62419be69e0032bc5931c" +checksum = "be36bc9e0546df253c0cc41fd0af34f5e92845ad8509462ec76672fac6997f5b" dependencies = [ "bitflags", "libc", @@ -3570,9 +3570,9 @@ checksum = "201de327520df007757c1f0adce6e827fe8562fbc28bfd9c15571c66ca1f5f79" [[package]] name = "libgit2-sys" -version = "0.13.4+1.4.2" +version = "0.14.1+1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d0fa6563431ede25f5cc7f6d803c6afbc1c5d3ad3d4925d12c882bf2b526f5d1" +checksum = "4a07fb2692bc3593bda59de45a502bb3071659f2c515e28c71e728306b038e17" dependencies = [ "cc", "libc", diff --git a/utils/frame/generate-bags/Cargo.toml b/utils/frame/generate-bags/Cargo.toml index 6006cdb5d..a24af6a27 100644 --- a/utils/frame/generate-bags/Cargo.toml +++ b/utils/frame/generate-bags/Cargo.toml @@ -17,5 +17,5 @@ pallet-staking = { version = "4.0.0-dev", path = "../../../frame/staking" } # third party chrono = { version = "0.4.19" } -git2 = { version = "0.14.2", default-features = false } +git2 = { version = "0.16.0", default-features = false } num-format = "0.4.3" From 6d3596f8a04873f965cf401a84e52d4404337424 Mon Sep 17 00:00:00 2001 From: Oliver Tale-Yazdi Date: Wed, 11 Jan 2023 17:16:56 +0100 Subject: [PATCH 005/558] Scheduler is already at V4 (#13105) * Scheduler is already at V4 Signed-off-by: Oliver Tale-Yazdi * Fix Referenda log target Signed-off-by: Oliver Tale-Yazdi * Fix test Signed-off-by: Oliver Tale-Yazdi Signed-off-by: Oliver Tale-Yazdi --- frame/referenda/src/migration.rs | 2 +- frame/scheduler/src/lib.rs | 2 +- frame/scheduler/src/tests.rs | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/frame/referenda/src/migration.rs b/frame/referenda/src/migration.rs index e495090c1..39575556e 100644 --- a/frame/referenda/src/migration.rs +++ b/frame/referenda/src/migration.rs @@ -88,7 +88,7 @@ pub mod v1 { use super::*; /// The log target. - const TARGET: &'static str = "runtime::democracy::migration::v1"; + const TARGET: &'static str = "runtime::referenda::migration::v1"; /// Transforms a submission deposit of ReferendumInfo(Approved|Rejected|Cancelled|TimedOut) to /// optional value, making it refundable. diff --git a/frame/scheduler/src/lib.rs b/frame/scheduler/src/lib.rs index 20e142067..afc4fd66e 100644 --- a/frame/scheduler/src/lib.rs +++ b/frame/scheduler/src/lib.rs @@ -169,7 +169,7 @@ pub mod pallet { use frame_system::pallet_prelude::*; /// The current storage version. - const STORAGE_VERSION: StorageVersion = StorageVersion::new(3); + const STORAGE_VERSION: StorageVersion = StorageVersion::new(4); #[pallet::pallet] #[pallet::generate_store(pub(super) trait Store)] diff --git a/frame/scheduler/src/tests.rs b/frame/scheduler/src/tests.rs index 7c261fdd7..e5467fc8d 100644 --- a/frame/scheduler/src/tests.rs +++ b/frame/scheduler/src/tests.rs @@ -973,7 +973,7 @@ fn migration_to_v4_works() { } assert_eq_uvec!(x, expected); - assert_eq!(Scheduler::current_storage_version(), 3); + assert_eq!(Scheduler::on_chain_storage_version(), 4); }); } From f9d1dcdfa1f0a2edc2b933a0b11ffd499c22c73a Mon Sep 17 00:00:00 2001 From: David Dunn <26876072+doubledup@users.noreply.github.com> Date: Thu, 12 Jan 2023 07:23:57 +0200 Subject: [PATCH 006/558] Optimize merkle proofs for efficient verification in Solidity (#12857) * Sort hashes when generating & verifying MMR proofs * Remove unused variables * Double-hash leaves in beefy-merkle-tree * Revert "Double-hash leaves in beefy-merkle-tree" This reverts commit f788f5ed85e575a866639316aaef29dffbcd1c8c. * Retry Polkadot companion CI jobs --- frame/beefy-mmr/primitives/src/lib.rs | 46 ++++++++++++++------------ frame/beefy-mmr/src/tests.rs | 10 +++--- frame/merkle-mountain-range/src/lib.rs | 3 +- 3 files changed, 31 insertions(+), 28 deletions(-) diff --git a/frame/beefy-mmr/primitives/src/lib.rs b/frame/beefy-mmr/primitives/src/lib.rs index f88fb89ac..e6f8acefb 100644 --- a/frame/beefy-mmr/primitives/src/lib.rs +++ b/frame/beefy-mmr/primitives/src/lib.rs @@ -27,7 +27,8 @@ //! Merkle Tree is constructed from arbitrary-length leaves, that are initially hashed using the //! same hasher as the inner nodes. //! Inner nodes are created by concatenating child hashes and hashing again. The implementation -//! does not perform any sorting of the input data (leaves) nor when inner nodes are created. +//! sorts each pair of hashes before every hash operation. This makes proof verification more +//! efficient by removing the need to track which side each intermediate hash is concatenated on. //! //! If the number of leaves is not even, last leaf (hash of) is promoted to the upper layer. @@ -45,7 +46,7 @@ use beefy_primitives::mmr::{BeefyAuthoritySet, BeefyNextAuthoritySet}; pub fn merkle_root(leaves: I) -> H::Output where H: HashT, - H::Output: Default + AsRef<[u8]>, + H::Output: Default + AsRef<[u8]> + PartialOrd, I: IntoIterator, I::Item: AsRef<[u8]>, { @@ -56,7 +57,7 @@ where fn merkelize(leaves: I, visitor: &mut V) -> H::Output where H: HashT, - H::Output: Default + AsRef<[u8]>, + H::Output: Default + AsRef<[u8]> + PartialOrd, V: Visitor, I: Iterator, { @@ -141,7 +142,7 @@ impl Visitor for () { pub fn merkle_proof(leaves: I, leaf_index: usize) -> MerkleProof where H: HashT, - H::Output: Default + Copy + AsRef<[u8]>, + H::Output: Default + Copy + AsRef<[u8]> + PartialOrd, I: IntoIterator, I::IntoIter: ExactSizeIterator, T: AsRef<[u8]>, @@ -241,7 +242,7 @@ pub fn verify_proof<'a, H, P, L>( ) -> bool where H: HashT, - H::Output: PartialEq + AsRef<[u8]>, + H::Output: PartialEq + AsRef<[u8]> + PartialOrd, P: IntoIterator, L: Into>, { @@ -256,15 +257,13 @@ where let hash_len = ::LENGTH; let mut combined = vec![0_u8; hash_len * 2]; - let mut position = leaf_index; - let mut width = number_of_leaves; let computed = proof.into_iter().fold(leaf_hash, |a, b| { - if position % 2 == 1 || position + 1 == width { - combined[..hash_len].copy_from_slice(&b.as_ref()); - combined[hash_len..].copy_from_slice(&a.as_ref()); - } else { + if a < b { combined[..hash_len].copy_from_slice(&a.as_ref()); combined[hash_len..].copy_from_slice(&b.as_ref()); + } else { + combined[..hash_len].copy_from_slice(&b.as_ref()); + combined[hash_len..].copy_from_slice(&a.as_ref()); } let hash = ::hash(&combined); #[cfg(feature = "debug")] @@ -275,8 +274,6 @@ where array_bytes::bytes2hex("", &hash.as_ref()), array_bytes::bytes2hex("", &combined.as_ref()) ); - position /= 2; - width = ((width - 1) / 2) + 1; hash }); @@ -295,7 +292,7 @@ fn merkelize_row( ) -> Result> where H: HashT, - H::Output: AsRef<[u8]>, + H::Output: AsRef<[u8]> + PartialOrd, V: Visitor, I: Iterator, { @@ -321,8 +318,13 @@ where index += 2; match (a, b) { (Some(a), Some(b)) => { - combined[..hash_len].copy_from_slice(a.as_ref()); - combined[hash_len..].copy_from_slice(b.as_ref()); + if a < b { + combined[..hash_len].copy_from_slice(a.as_ref()); + combined[hash_len..].copy_from_slice(b.as_ref()); + } else { + combined[..hash_len].copy_from_slice(b.as_ref()); + combined[hash_len..].copy_from_slice(a.as_ref()); + } next.push(::hash(&combined)); }, @@ -428,12 +430,12 @@ mod tests { }; test( - "aff1208e69c9e8be9b584b07ebac4e48a1ee9d15ce3afe20b77a4d29e4175aa3", + "5842148bc6ebeb52af882a317c765fccd3ae80589b21a9b8cbf21abb630e46a7", vec!["a", "b", "c"], ); test( - "b8912f7269068901f231a965adfefbc10f0eedcfa61852b103efd54dac7db3d7", + "7b84bec68b13c39798c6c50e9e40a0b268e3c1634db8f4cb97314eb243d4c514", vec!["a", "b", "a"], ); @@ -443,7 +445,7 @@ mod tests { ); test( - "fb3b3be94be9e983ba5e094c9c51a7d96a4fa2e5d8e891df00ca89ba05bb1239", + "cc50382cfd3c9a617741e9a85efee8752b8feb95a2cbecd6365fb21366ce0c8c", vec!["a", "b", "c", "d", "e", "f", "g", "h", "i", "j"], ); } @@ -761,7 +763,7 @@ mod tests { "0xc26B34D375533fFc4c5276282Fa5D660F3d8cbcB", ]; let root: H256 = array_bytes::hex2array_unchecked( - "72b0acd7c302a84f1f6b6cefe0ba7194b7398afb440e1b44a9dbbe270394ca53", + "7b2c6eebec6e85b2e272325a11c31af71df52bc0534d2d4f903e0ced191f022e", ) .into(); @@ -806,11 +808,11 @@ mod tests { ) .into(), array_bytes::hex2array_unchecked( - "d02609d2bbdb28aa25f58b85afec937d5a4c85d37925bce6d0cf802f9d76ba79" + "1fad92ed8d0504ef6c0231bbbeeda960a40693f297c64e87b582beb92ecfb00f" ) .into(), array_bytes::hex2array_unchecked( - "ae3f8991955ed884613b0a5f40295902eea0e0abe5858fc520b72959bc016d4e" + "0b84c852cbcf839d562d826fd935e1b37975ccaa419e1def8d219df4b83dcbf4" ) .into(), ], diff --git a/frame/beefy-mmr/src/tests.rs b/frame/beefy-mmr/src/tests.rs index 1826331f5..0f7804e54 100644 --- a/frame/beefy-mmr/src/tests.rs +++ b/frame/beefy-mmr/src/tests.rs @@ -70,7 +70,7 @@ fn should_contain_mmr_digest() { ValidatorSet::new(vec![mock_beefy_id(1), mock_beefy_id(2)], 1).unwrap() )), beefy_log(ConsensusLog::MmrRoot(array_bytes::hex_n_into_unchecked( - "95803defe6ea9f41e7ec6afa497064f21bfded027d8812efacbdf984e630cbdc" + "200e73880940ac0b66735ffb560fa0a3989292463d262deac6ad61e78a3e46a4" ))) ] ); @@ -85,13 +85,13 @@ fn should_contain_mmr_digest() { ValidatorSet::new(vec![mock_beefy_id(1), mock_beefy_id(2)], 1).unwrap() )), beefy_log(ConsensusLog::MmrRoot(array_bytes::hex_n_into_unchecked( - "95803defe6ea9f41e7ec6afa497064f21bfded027d8812efacbdf984e630cbdc" + "200e73880940ac0b66735ffb560fa0a3989292463d262deac6ad61e78a3e46a4" ))), beefy_log(ConsensusLog::AuthoritiesChange( ValidatorSet::new(vec![mock_beefy_id(3), mock_beefy_id(4)], 2).unwrap() )), beefy_log(ConsensusLog::MmrRoot(array_bytes::hex_n_into_unchecked( - "a73271a0974f1e67d6e9b8dd58e506177a2e556519a330796721e98279a753e2" + "ba37d8d5d195ac8caec391da35472f9ecf1116ff1642409148b62e08896d3884" ))), ] ); @@ -124,7 +124,7 @@ fn should_contain_valid_leaf_data() { ) }, leaf_extra: array_bytes::hex2bytes_unchecked( - "55b8e9e1cc9f0db7776fac0ca66318ef8acfb8ec26db11e373120583e07ee648" + "5572d58c82bddf323f4fc7aecab8a8f0ad6ed2f06ab2bfb8ade36a77a45fcc68" ) } ); @@ -149,7 +149,7 @@ fn should_contain_valid_leaf_data() { ) }, leaf_extra: array_bytes::hex2bytes_unchecked( - "55b8e9e1cc9f0db7776fac0ca66318ef8acfb8ec26db11e373120583e07ee648" + "5572d58c82bddf323f4fc7aecab8a8f0ad6ed2f06ab2bfb8ade36a77a45fcc68" ) } ); diff --git a/frame/merkle-mountain-range/src/lib.rs b/frame/merkle-mountain-range/src/lib.rs index 46af84d21..170c1d724 100644 --- a/frame/merkle-mountain-range/src/lib.rs +++ b/frame/merkle-mountain-range/src/lib.rs @@ -164,7 +164,8 @@ pub mod pallet { + codec::Codec + codec::EncodeLike + scale_info::TypeInfo - + MaxEncodedLen; + + MaxEncodedLen + + PartialOrd; /// Data stored in the leaf nodes. /// From e9b0facf70eeb08032cc7e83548c62f0b4a24bb1 Mon Sep 17 00:00:00 2001 From: Adrian Catangiu Date: Thu, 12 Jan 2023 15:49:01 +0200 Subject: [PATCH 007/558] client/beefy: fix logs (#13130) Minor change to logs but required to minimize spamming during initial BEEFY deployment. --- client/beefy/src/worker.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/client/beefy/src/worker.rs b/client/beefy/src/worker.rs index b48dfa369..8ec1403bb 100644 --- a/client/beefy/src/worker.rs +++ b/client/beefy/src/worker.rs @@ -498,7 +498,7 @@ where warn!(target: "beefy", "🥩 Buffer vote dropped for round: {:?}", block_num) } } else { - error!(target: "beefy", "🥩 Buffer justification dropped for round: {:?}.", block_num); + warn!(target: "beefy", "🥩 Buffer vote dropped for round: {:?}.", block_num); } }, RoundAction::Drop => (), @@ -528,7 +528,7 @@ where if self.pending_justifications.len() < MAX_BUFFERED_JUSTIFICATIONS { self.pending_justifications.entry(block_num).or_insert(justification); } else { - error!(target: "beefy", "🥩 Buffer justification dropped for round: {:?}.", block_num); + warn!(target: "beefy", "🥩 Buffer justification dropped for round: {:?}.", block_num); } }, RoundAction::Drop => (), From 2bfc1dd66ef32cf8beb90007dfb544a9d28f1b2f Mon Sep 17 00:00:00 2001 From: Davide Galassi Date: Thu, 12 Jan 2023 20:36:31 +0100 Subject: [PATCH 008/558] Stop keeping track of epoch changes for the sync gap (#13127) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Stop keeping track of epoch changes data within the sync gap * Fix docs * Apply suggestions from code review Co-authored-by: Bastian Köcher * Fix typo Co-authored-by: Bastian Köcher --- client/consensus/babe/src/lib.rs | 51 +-- client/consensus/epochs/src/lib.rs | 432 +---------------------- client/consensus/epochs/src/migration.rs | 4 +- 3 files changed, 33 insertions(+), 454 deletions(-) diff --git a/client/consensus/babe/src/lib.rs b/client/consensus/babe/src/lib.rs index 41c00169e..c9e171dbe 100644 --- a/client/consensus/babe/src/lib.rs +++ b/client/consensus/babe/src/lib.rs @@ -111,7 +111,7 @@ use sp_api::{ApiExt, ProvideRuntimeApi}; use sp_application_crypto::AppKey; use sp_block_builder::BlockBuilder as BlockBuilderApi; use sp_blockchain::{ - Backend as _, Error as ClientError, ForkBackend, HeaderBackend, HeaderMetadata, + Backend as _, BlockStatus, Error as ClientError, ForkBackend, HeaderBackend, HeaderMetadata, Result as ClientResult, }; use sp_consensus::{ @@ -1133,11 +1133,17 @@ where let hash = block.header.hash(); let parent_hash = *block.header.parent_hash(); - if block.with_state() { - // When importing whole state we don't calculate epoch descriptor, but rather - // read it from the state after import. We also skip all verifications - // because there's no parent state and we trust the sync module to verify - // that the state is correct and finalized. + let info = self.client.info(); + let number = *block.header.number(); + + if info.block_gap.map_or(false, |(s, e)| s <= number && number <= e) || block.with_state() { + // Verification for imported blocks is skipped in two cases: + // 1. When importing blocks below the last finalized block during network initial + // synchronization. + // 2. When importing whole state we don't calculate epoch descriptor, but rather + // read it from the state after import. We also skip all verifications + // because there's no parent state and we trust the sync module to verify + // that the state is correct and finalized. return Ok((block, Default::default())) } @@ -1399,18 +1405,24 @@ where ) -> Result { let hash = block.post_hash(); let number = *block.header.number(); + let info = self.client.info(); - // early exit if block already in chain, otherwise the check for - // epoch changes will error when trying to re-import an epoch change - match self.client.status(hash) { - Ok(sp_blockchain::BlockStatus::InChain) => { - // When re-importing existing block strip away intermediates. - let _ = block.remove_intermediate::>(INTERMEDIATE_KEY); - block.fork_choice = Some(ForkChoiceStrategy::Custom(false)); - return self.inner.import_block(block, new_cache).await.map_err(Into::into) - }, - Ok(sp_blockchain::BlockStatus::Unknown) => {}, - Err(e) => return Err(ConsensusError::ClientImport(e.to_string())), + let block_status = self + .client + .status(hash) + .map_err(|e| ConsensusError::ClientImport(e.to_string()))?; + + // Skip babe logic if block already in chain or importing blocks during initial sync, + // otherwise the check for epoch changes will error because trying to re-import an + // epoch change or because of missing epoch data in the tree, respectivelly. + if info.block_gap.map_or(false, |(s, e)| s <= number && number <= e) || + block_status == BlockStatus::InChain + { + // When re-importing existing block strip away intermediates. + // In case of initial sync intermediates should not be present... + let _ = block.remove_intermediate::>(INTERMEDIATE_KEY); + block.fork_choice = Some(ForkChoiceStrategy::Custom(false)); + return self.inner.import_block(block, new_cache).await.map_err(Into::into) } if block.with_state() { @@ -1506,8 +1518,6 @@ where )), } - let info = self.client.info(); - if let Some(next_epoch_descriptor) = next_epoch_digest { old_epoch_changes = Some((*epoch_changes).clone()); @@ -1701,9 +1711,6 @@ where Client: HeaderBackend + HeaderMetadata, { let info = client.info(); - if info.block_gap.is_none() { - epoch_changes.clear_gap(); - } let finalized_slot = { let finalized_header = client diff --git a/client/consensus/epochs/src/lib.rs b/client/consensus/epochs/src/lib.rs index c213a49b8..7784a9943 100644 --- a/client/consensus/epochs/src/lib.rs +++ b/client/consensus/epochs/src/lib.rs @@ -320,106 +320,6 @@ impl AsRef for IncrementedEpoch { } } -/// A pair of epochs for the gap block download validation. -/// Block gap is created after the warp sync is complete. Blocks -/// are imported both at the tip of the chain and at the start of the gap. -/// This holds a pair of epochs that are required to validate headers -/// at the start of the gap. Since gap download does not allow forks we don't -/// need to keep a tree of epochs. -#[derive(Clone, Encode, Decode, Debug)] -pub struct GapEpochs { - current: (Hash, Number, PersistedEpoch), - next: Option<(Hash, Number, E)>, -} - -impl GapEpochs -where - Hash: Copy + PartialEq + std::fmt::Debug, - Number: Copy + PartialEq + std::fmt::Debug, - E: Epoch, -{ - /// Check if given slot matches one of the gap epochs. - /// Returns epoch identifier if it does. - fn matches( - &self, - slot: E::Slot, - ) -> Option<(Hash, Number, EpochHeader, EpochIdentifierPosition)> { - match &self.current { - (_, _, PersistedEpoch::Genesis(epoch_0, _)) - if slot >= epoch_0.start_slot() && slot < epoch_0.end_slot() => - return Some(( - self.current.0, - self.current.1, - epoch_0.into(), - EpochIdentifierPosition::Genesis0, - )), - (_, _, PersistedEpoch::Genesis(_, epoch_1)) - if slot >= epoch_1.start_slot() && slot < epoch_1.end_slot() => - return Some(( - self.current.0, - self.current.1, - epoch_1.into(), - EpochIdentifierPosition::Genesis1, - )), - (_, _, PersistedEpoch::Regular(epoch_n)) - if slot >= epoch_n.start_slot() && slot < epoch_n.end_slot() => - return Some(( - self.current.0, - self.current.1, - epoch_n.into(), - EpochIdentifierPosition::Regular, - )), - _ => {}, - }; - match &self.next { - Some((h, n, epoch_n)) if slot >= epoch_n.start_slot() && slot < epoch_n.end_slot() => - Some((*h, *n, epoch_n.into(), EpochIdentifierPosition::Regular)), - _ => None, - } - } - - /// Returns epoch data if it matches given identifier. - pub fn epoch(&self, id: &EpochIdentifier) -> Option<&E> { - match (&self.current, &self.next) { - ((h, n, e), _) if h == &id.hash && n == &id.number => match e { - PersistedEpoch::Genesis(ref epoch_0, _) - if id.position == EpochIdentifierPosition::Genesis0 => - Some(epoch_0), - PersistedEpoch::Genesis(_, ref epoch_1) - if id.position == EpochIdentifierPosition::Genesis1 => - Some(epoch_1), - PersistedEpoch::Regular(ref epoch_n) - if id.position == EpochIdentifierPosition::Regular => - Some(epoch_n), - _ => None, - }, - (_, Some((h, n, e))) - if h == &id.hash && - n == &id.number && id.position == EpochIdentifierPosition::Regular => - Some(e), - _ => None, - } - } - - /// Import a new gap epoch, potentially replacing an old epoch. - fn import(&mut self, slot: E::Slot, hash: Hash, number: Number, epoch: E) -> Result<(), E> { - match (&mut self.current, &mut self.next) { - ((_, _, PersistedEpoch::Genesis(_, epoch_1)), _) if slot == epoch_1.end_slot() => { - self.next = Some((hash, number, epoch)); - Ok(()) - }, - (_, Some((_, _, epoch_n))) if slot == epoch_n.end_slot() => { - let (cur_h, cur_n, cur_epoch) = - self.next.take().expect("Already matched as `Some`"); - self.current = (cur_h, cur_n, PersistedEpoch::Regular(cur_epoch)); - self.next = Some((hash, number, epoch)); - Ok(()) - }, - _ => Err(epoch), - } - } -} - /// Tree of all epoch changes across all *seen* forks. Data stored in tree is /// the hash and block number of the block signaling the epoch change, and the /// epoch that was signalled at that block. @@ -435,14 +335,10 @@ where /// same DAG entry, pinned to a specific block #1. /// /// Further epochs (epoch_2, ..., epoch_n) each get their own entry. -/// -/// Also maintains a pair of epochs for the start of the gap, -/// as long as there's an active gap download after a warp sync. #[derive(Clone, Encode, Decode, Debug)] pub struct EpochChanges { inner: ForkTree>, epochs: BTreeMap<(Hash, Number), PersistedEpoch>, - gap: Option>, } // create a fake header hash which hasn't been included in the chain. @@ -460,7 +356,7 @@ where Number: Ord, { fn default() -> Self { - EpochChanges { inner: ForkTree::new(), epochs: BTreeMap::new(), gap: None } + EpochChanges { inner: ForkTree::new(), epochs: BTreeMap::new() } } } @@ -480,11 +376,6 @@ where self.inner.rebalance() } - /// Clear gap epochs if any. - pub fn clear_gap(&mut self) { - self.gap = None; - } - /// Map the epoch changes from one storing data to a different one. pub fn map(self, mut f: F) -> EpochChanges where @@ -493,10 +384,6 @@ where { EpochChanges { inner: self.inner.map(&mut |_, _, header: PersistedEpochHeader| header.map()), - gap: self.gap.map(|GapEpochs { current: (h, n, header), next }| GapEpochs { - current: (h, n, header.map(&h, &n, &mut f)), - next: next.map(|(h, n, e)| (h, n, f(&h, &n, e))), - }), epochs: self .epochs .into_iter() @@ -536,9 +423,6 @@ where /// Get a reference to an epoch with given identifier. pub fn epoch(&self, id: &EpochIdentifier) -> Option<&E> { - if let Some(e) = &self.gap.as_ref().and_then(|gap| gap.epoch(id)) { - return Some(e) - } self.epochs.get(&(id.hash, id.number)).and_then(|v| match v { PersistedEpoch::Genesis(ref epoch_0, _) if id.position == EpochIdentifierPosition::Genesis0 => @@ -665,15 +549,6 @@ where return Ok(Some(ViableEpochDescriptor::UnimportedGenesis(slot))) } - if let Some(gap) = &self.gap { - if let Some((hash, number, hdr, position)) = gap.matches(slot) { - return Ok(Some(ViableEpochDescriptor::Signaled( - EpochIdentifier { position, hash, number }, - hdr, - ))) - } - } - // find_node_where will give you the node in the fork-tree which is an ancestor // of the `parent_hash` by default. if the last epoch was signalled at the parent_hash, // then it won't be returned. we need to create a new fake chain head hash which @@ -744,28 +619,9 @@ where ) -> Result<(), fork_tree::Error> { let is_descendent_of = descendent_of_builder.build_is_descendent_of(Some((hash, parent_hash))); - let slot = epoch.as_ref().start_slot(); - let IncrementedEpoch(mut epoch) = epoch; + let IncrementedEpoch(epoch) = epoch; let header = PersistedEpochHeader::::from(&epoch); - if let Some(gap) = &mut self.gap { - if let PersistedEpoch::Regular(e) = epoch { - epoch = match gap.import(slot, hash, number, e) { - Ok(()) => return Ok(()), - Err(e) => PersistedEpoch::Regular(e), - } - } - } else if epoch.is_genesis() && - !self.epochs.is_empty() && - !self.epochs.values().any(|e| e.is_genesis()) - { - // There's a genesis epoch imported when we already have an active epoch. - // This happens after the warp sync as the ancient blocks download start. - // We need to start tracking gap epochs here. - self.gap = Some(GapEpochs { current: (hash, number, epoch), next: None }); - return Ok(()) - } - let res = self.inner.import(hash, number, header, &is_descendent_of); match res { @@ -1278,288 +1134,4 @@ mod tests { list.sort(); assert_eq!(list, vec![b"A", b"G", b"L"]); } - - /// Test that ensures that the gap is not enabled when we import multiple genesis blocks. - #[test] - fn gap_is_not_enabled_when_multiple_genesis_epochs_are_imported() { - // X - // / - // 0 - A - // - let is_descendent_of = |base: &Hash, block: &Hash| -> Result { - match (base, *block) { - (b"0", _) => Ok(true), - _ => Ok(false), - } - }; - - let duration = 100; - - let make_genesis = |slot| Epoch { start_slot: slot, duration }; - - let mut epoch_changes = EpochChanges::new(); - let next_descriptor = (); - - // insert genesis epoch for A - { - let genesis_epoch_a_descriptor = epoch_changes - .epoch_descriptor_for_child_of(&is_descendent_of, b"0", 0, 100) - .unwrap() - .unwrap(); - - let incremented_epoch = epoch_changes - .viable_epoch(&genesis_epoch_a_descriptor, &make_genesis) - .unwrap() - .increment(next_descriptor); - - epoch_changes - .import(&is_descendent_of, *b"A", 1, *b"0", incremented_epoch) - .unwrap(); - } - - // insert genesis epoch for X - { - let genesis_epoch_x_descriptor = epoch_changes - .epoch_descriptor_for_child_of(&is_descendent_of, b"0", 0, 1000) - .unwrap() - .unwrap(); - - let incremented_epoch = epoch_changes - .viable_epoch(&genesis_epoch_x_descriptor, &make_genesis) - .unwrap() - .increment(next_descriptor); - - epoch_changes - .import(&is_descendent_of, *b"X", 1, *b"0", incremented_epoch) - .unwrap(); - } - - // Clearing the gap should be a no-op. - epoch_changes.clear_gap(); - - // Check that both epochs are available. - let epoch_a = epoch_changes - .epoch_data_for_child_of(&is_descendent_of, b"A", 1, 101, &make_genesis) - .unwrap() - .unwrap(); - - let epoch_x = epoch_changes - .epoch_data_for_child_of(&is_descendent_of, b"X", 1, 1001, &make_genesis) - .unwrap() - .unwrap(); - - assert!(epoch_a != epoch_x) - } - - #[test] - fn gap_epochs_advance() { - // 0 - 1 - 2 - 3 - .... 42 - 43 - let is_descendent_of = |base: &Hash, block: &Hash| -> Result { - match (base, *block) { - (b"0", _) => Ok(true), - (b"1", b) => Ok(b == *b"0"), - (b"2", b) => Ok(b == *b"1"), - (b"3", b) => Ok(b == *b"2"), - _ => Ok(false), - } - }; - - let duration = 100; - - let make_genesis = |slot| Epoch { start_slot: slot, duration }; - - let mut epoch_changes = EpochChanges::new(); - let next_descriptor = (); - - let epoch42 = Epoch { start_slot: 42, duration: 100 }; - let epoch43 = Epoch { start_slot: 43, duration: 100 }; - epoch_changes.reset(*b"0", *b"1", 4200, epoch42, epoch43); - assert!(epoch_changes.gap.is_none()); - - // Import a new genesis epoch, this should crate the gap. - let genesis_epoch_a_descriptor = epoch_changes - .epoch_descriptor_for_child_of(&is_descendent_of, b"0", 0, 100) - .unwrap() - .unwrap(); - - let incremented_epoch = epoch_changes - .viable_epoch(&genesis_epoch_a_descriptor, &make_genesis) - .unwrap() - .increment(next_descriptor); - - epoch_changes - .import(&is_descendent_of, *b"1", 1, *b"0", incremented_epoch) - .unwrap(); - assert!(epoch_changes.gap.is_some()); - - let genesis_epoch = epoch_changes - .epoch_descriptor_for_child_of(&is_descendent_of, b"0", 0, 100) - .unwrap() - .unwrap(); - - assert_eq!(genesis_epoch, ViableEpochDescriptor::UnimportedGenesis(100)); - - // Import more epochs and check that gap advances. - let import_epoch_1 = - epoch_changes.viable_epoch(&genesis_epoch, &make_genesis).unwrap().increment(()); - - let epoch_1 = import_epoch_1.as_ref().clone(); - epoch_changes - .import(&is_descendent_of, *b"1", 1, *b"0", import_epoch_1) - .unwrap(); - let genesis_epoch_data = epoch_changes.epoch_data(&genesis_epoch, &make_genesis).unwrap(); - let end_slot = genesis_epoch_data.end_slot(); - let x = epoch_changes - .epoch_data_for_child_of(&is_descendent_of, b"1", 1, end_slot, &make_genesis) - .unwrap() - .unwrap(); - - assert_eq!(x, epoch_1); - assert_eq!(epoch_changes.gap.as_ref().unwrap().current.0, *b"1"); - assert!(epoch_changes.gap.as_ref().unwrap().next.is_none()); - - let epoch_1_desriptor = epoch_changes - .epoch_descriptor_for_child_of(&is_descendent_of, b"1", 1, end_slot) - .unwrap() - .unwrap(); - let epoch_1 = epoch_changes.epoch_data(&epoch_1_desriptor, &make_genesis).unwrap(); - let import_epoch_2 = epoch_changes - .viable_epoch(&epoch_1_desriptor, &make_genesis) - .unwrap() - .increment(()); - let epoch_2 = import_epoch_2.as_ref().clone(); - epoch_changes - .import(&is_descendent_of, *b"2", 2, *b"1", import_epoch_2) - .unwrap(); - - let end_slot = epoch_1.end_slot(); - let x = epoch_changes - .epoch_data_for_child_of(&is_descendent_of, b"2", 2, end_slot, &make_genesis) - .unwrap() - .unwrap(); - assert_eq!(epoch_changes.gap.as_ref().unwrap().current.0, *b"1"); - assert_eq!(epoch_changes.gap.as_ref().unwrap().next.as_ref().unwrap().0, *b"2"); - assert_eq!(x, epoch_2); - - let epoch_2_desriptor = epoch_changes - .epoch_descriptor_for_child_of(&is_descendent_of, b"2", 2, end_slot) - .unwrap() - .unwrap(); - let import_epoch_3 = epoch_changes - .viable_epoch(&epoch_2_desriptor, &make_genesis) - .unwrap() - .increment(()); - epoch_changes - .import(&is_descendent_of, *b"3", 3, *b"2", import_epoch_3) - .unwrap(); - - assert_eq!(epoch_changes.gap.as_ref().unwrap().current.0, *b"2"); - - epoch_changes.clear_gap(); - assert!(epoch_changes.gap.is_none()); - } - - /// Test that ensures that the gap is not enabled when there's still genesis - /// epochs imported, regardless of whether there are already other further - /// epochs imported descending from such genesis epochs. - #[test] - fn gap_is_not_enabled_when_at_least_one_genesis_epoch_is_still_imported() { - // A (#1) - B (#201) - // / - // 0 - C (#1) - // - // The epoch duration is 100 slots, each of these blocks represents - // an epoch change block. block B starts a new epoch at #201 since the - // genesis epoch spans two epochs. - - let is_descendent_of = |base: &Hash, block: &Hash| -> Result { - match (base, block) { - (b"0", _) => Ok(true), - (b"A", b"B") => Ok(true), - _ => Ok(false), - } - }; - - let duration = 100; - let make_genesis = |slot| Epoch { start_slot: slot, duration }; - let mut epoch_changes = EpochChanges::new(); - let next_descriptor = (); - - // insert genesis epoch for A at slot 1 - { - let genesis_epoch_a_descriptor = epoch_changes - .epoch_descriptor_for_child_of(&is_descendent_of, b"0", 0, 1) - .unwrap() - .unwrap(); - - let incremented_epoch = epoch_changes - .viable_epoch(&genesis_epoch_a_descriptor, &make_genesis) - .unwrap() - .increment(next_descriptor); - - epoch_changes - .import(&is_descendent_of, *b"A", 1, *b"0", incremented_epoch) - .unwrap(); - } - - // insert regular epoch for B at slot 201, descending from A - { - let epoch_b_descriptor = epoch_changes - .epoch_descriptor_for_child_of(&is_descendent_of, b"A", 1, 201) - .unwrap() - .unwrap(); - - let incremented_epoch = epoch_changes - .viable_epoch(&epoch_b_descriptor, &make_genesis) - .unwrap() - .increment(next_descriptor); - - epoch_changes - .import(&is_descendent_of, *b"B", 201, *b"A", incremented_epoch) - .unwrap(); - } - - // insert genesis epoch for C at slot 1000 - { - let genesis_epoch_x_descriptor = epoch_changes - .epoch_descriptor_for_child_of(&is_descendent_of, b"0", 0, 1000) - .unwrap() - .unwrap(); - - let incremented_epoch = epoch_changes - .viable_epoch(&genesis_epoch_x_descriptor, &make_genesis) - .unwrap() - .increment(next_descriptor); - - epoch_changes - .import(&is_descendent_of, *b"C", 1, *b"0", incremented_epoch) - .unwrap(); - } - - // Clearing the gap should be a no-op. - epoch_changes.clear_gap(); - - // Check that all three epochs are available. - let epoch_a = epoch_changes - .epoch_data_for_child_of(&is_descendent_of, b"A", 1, 10, &make_genesis) - .unwrap() - .unwrap(); - - let epoch_b = epoch_changes - .epoch_data_for_child_of(&is_descendent_of, b"B", 201, 201, &make_genesis) - .unwrap() - .unwrap(); - - assert!(epoch_a != epoch_b); - - // the genesis epoch A will span slots [1, 200] with epoch B starting at slot 201 - assert_eq!(epoch_b.start_slot(), 201); - - let epoch_c = epoch_changes - .epoch_data_for_child_of(&is_descendent_of, b"C", 1, 1001, &make_genesis) - .unwrap() - .unwrap(); - - assert!(epoch_a != epoch_c); - } } diff --git a/client/consensus/epochs/src/migration.rs b/client/consensus/epochs/src/migration.rs index c4ed47e9c..052d0b7be 100644 --- a/client/consensus/epochs/src/migration.rs +++ b/client/consensus/epochs/src/migration.rs @@ -64,7 +64,7 @@ where header }); - EpochChanges { inner, epochs, gap: None } + EpochChanges { inner, epochs } } } @@ -75,6 +75,6 @@ where { /// Migrate the type into current epoch changes definition. pub fn migrate(self) -> EpochChanges { - EpochChanges { inner: self.inner, epochs: self.epochs, gap: None } + EpochChanges { inner: self.inner, epochs: self.epochs } } } From d92281a5634baf55a3dab058b904a3932b4ed340 Mon Sep 17 00:00:00 2001 From: Vladimir Istyufeev Date: Fri, 13 Jan 2023 14:32:29 +0400 Subject: [PATCH 009/558] CI: Unpin `ci-linux` and use Rust 1.66.1 (#13134) --- .gitlab-ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 90298297a..ecc27d016 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -48,7 +48,7 @@ variables: CARGO_INCREMENTAL: 0 DOCKER_OS: "debian:stretch" ARCH: "x86_64" - CI_IMAGE: "paritytech/ci-linux@sha256:9140bc3c843a8b12a3bcf6f5886346536092795bbadfd7f1836362cb28dfcc71" + CI_IMAGE: "paritytech/ci-linux:production" BUILDAH_IMAGE: "quay.io/buildah/stable:v1.27" RUSTY_CACHIER_SINGLE_BRANCH: master RUSTY_CACHIER_DONT_OPERATE_ON_MAIN_BRANCH: "true" From 88be4b64df842daf2dd5473a45eb9f8b85f466c4 Mon Sep 17 00:00:00 2001 From: Adrian Catangiu Date: Fri, 13 Jan 2023 14:52:18 +0200 Subject: [PATCH 010/558] sp-beefy: align authority id key type with its signature type (#13131) Still allows custom message hasher, but ties together the crypto types used for private+public keys and the signature. Signed-off-by: acatangiu --- client/beefy/src/keystore.rs | 4 +-- primitives/beefy/src/lib.rs | 52 +++++++++++++++++------------------- 2 files changed, 27 insertions(+), 29 deletions(-) diff --git a/client/beefy/src/keystore.rs b/client/beefy/src/keystore.rs index 886c00fc5..8d7741074 100644 --- a/client/beefy/src/keystore.rs +++ b/client/beefy/src/keystore.rs @@ -25,7 +25,7 @@ use log::warn; use beefy_primitives::{ crypto::{Public, Signature}, - BeefyVerify, KEY_TYPE, + BeefyAuthorityId, KEY_TYPE, }; use crate::error; @@ -99,7 +99,7 @@ impl BeefyKeystore { /// /// Return `true` if the signature is authentic, `false` otherwise. pub fn verify(public: &Public, sig: &Signature, message: &[u8]) -> bool { - BeefyVerify::::verify(sig, message, public) + BeefyAuthorityId::::verify(public, sig, message) } } diff --git a/primitives/beefy/src/lib.rs b/primitives/beefy/src/lib.rs index d7ac09149..eb7b8db89 100644 --- a/primitives/beefy/src/lib.rs +++ b/primitives/beefy/src/lib.rs @@ -49,20 +49,14 @@ use sp_std::prelude::*; /// Key type for BEEFY module. pub const KEY_TYPE: sp_application_crypto::KeyTypeId = sp_application_crypto::KeyTypeId(*b"beef"); -/// Trait representing BEEFY authority id. -pub trait BeefyAuthorityId: RuntimeAppPublic {} - -/// Means of verification for a BEEFY authority signature. +/// Trait representing BEEFY authority id, including custom signature verification. /// /// Accepts custom hashing fn for the message and custom convertor fn for the signer. -pub trait BeefyVerify { - /// Type of the signer. - type Signer: BeefyAuthorityId; - +pub trait BeefyAuthorityId: RuntimeAppPublic { /// Verify a signature. /// - /// Return `true` if signature is valid for the value. - fn verify(&self, msg: &[u8], signer: &Self::Signer) -> bool; + /// Return `true` if signature over `msg` is valid for this id. + fn verify(&self, signature: &::Signature, msg: &[u8]) -> bool; } /// BEEFY cryptographic types @@ -78,7 +72,7 @@ pub trait BeefyVerify { /// The current underlying crypto scheme used is ECDSA. This can be changed, /// without affecting code restricted against the above listed crypto types. pub mod crypto { - use super::{BeefyAuthorityId, BeefyVerify, Hash}; + use super::{BeefyAuthorityId, Hash, RuntimeAppPublic}; use sp_application_crypto::{app_crypto, ecdsa}; use sp_core::crypto::Wraps; app_crypto!(ecdsa, crate::KEY_TYPE); @@ -89,21 +83,17 @@ pub mod crypto { /// Signature for a BEEFY authority using ECDSA as its crypto. pub type AuthoritySignature = Signature; - impl BeefyAuthorityId for AuthorityId {} - - impl BeefyVerify for AuthoritySignature + impl BeefyAuthorityId for AuthorityId where ::Output: Into<[u8; 32]>, { - type Signer = AuthorityId; - - fn verify(&self, msg: &[u8], signer: &Self::Signer) -> bool { + fn verify(&self, signature: &::Signature, msg: &[u8]) -> bool { let msg_hash = ::hash(msg).into(); match sp_io::crypto::secp256k1_ecdsa_recover_compressed( - self.as_inner_ref().as_ref(), + signature.as_inner_ref().as_ref(), &msg_hash, ) { - Ok(raw_pubkey) => raw_pubkey.as_ref() == AsRef::<[u8]>::as_ref(signer), + Ok(raw_pubkey) => raw_pubkey.as_ref() == AsRef::<[u8]>::as_ref(self), _ => false, } } @@ -248,23 +238,31 @@ mod tests { pair.as_inner_ref().sign_prehashed(&blake2_256(msg)).into(); // Verification works if same hashing function is used when signing and verifying. - assert!(BeefyVerify::::verify(&keccak_256_signature, msg, &pair.public())); - assert!(BeefyVerify::::verify(&blake2_256_signature, msg, &pair.public())); + assert!(BeefyAuthorityId::::verify(&pair.public(), &keccak_256_signature, msg)); + assert!(BeefyAuthorityId::::verify( + &pair.public(), + &blake2_256_signature, + msg + )); // Verification fails if distinct hashing functions are used when signing and verifying. - assert!(!BeefyVerify::::verify(&blake2_256_signature, msg, &pair.public())); - assert!(!BeefyVerify::::verify(&keccak_256_signature, msg, &pair.public())); + assert!(!BeefyAuthorityId::::verify(&pair.public(), &blake2_256_signature, msg)); + assert!(!BeefyAuthorityId::::verify( + &pair.public(), + &keccak_256_signature, + msg + )); // Other public key doesn't work let (other_pair, _) = crypto::Pair::generate(); - assert!(!BeefyVerify::::verify( + assert!(!BeefyAuthorityId::::verify( + &other_pair.public(), &keccak_256_signature, msg, - &other_pair.public() )); - assert!(!BeefyVerify::::verify( + assert!(!BeefyAuthorityId::::verify( + &other_pair.public(), &blake2_256_signature, msg, - &other_pair.public() )); } } From cbc525f271146ba9c684736bfc5c26f77e849dae Mon Sep 17 00:00:00 2001 From: Squirrel Date: Fri, 13 Jan 2023 13:47:18 +0000 Subject: [PATCH 011/558] Use the balance trait as we have one (#13136) --- .../asset-tx-payment/src/payment.rs | 14 +++----------- frame/transaction-payment/src/payment.rs | 17 ++++------------- 2 files changed, 7 insertions(+), 24 deletions(-) diff --git a/frame/transaction-payment/asset-tx-payment/src/payment.rs b/frame/transaction-payment/asset-tx-payment/src/payment.rs index 80ff4e40d..85d1bec4b 100644 --- a/frame/transaction-payment/asset-tx-payment/src/payment.rs +++ b/frame/transaction-payment/asset-tx-payment/src/payment.rs @@ -21,15 +21,13 @@ use codec::FullCodec; use frame_support::{ traits::{ fungibles::{Balanced, CreditOf, Inspect}, - tokens::BalanceConversion, + tokens::{Balance, BalanceConversion}, }, unsigned::TransactionValidityError, }; use scale_info::TypeInfo; use sp_runtime::{ - traits::{ - AtLeast32BitUnsigned, DispatchInfoOf, MaybeSerializeDeserialize, One, PostDispatchInfoOf, - }, + traits::{DispatchInfoOf, MaybeSerializeDeserialize, One, PostDispatchInfoOf}, transaction_validity::InvalidTransaction, }; use sp_std::{fmt::Debug, marker::PhantomData}; @@ -37,13 +35,7 @@ use sp_std::{fmt::Debug, marker::PhantomData}; /// Handle withdrawing, refunding and depositing of transaction fees. pub trait OnChargeAssetTransaction { /// The underlying integer type in which fees are calculated. - type Balance: AtLeast32BitUnsigned - + FullCodec - + Copy - + MaybeSerializeDeserialize - + Debug - + Default - + TypeInfo; + type Balance: Balance; /// The type used to identify the assets used for transaction payment. type AssetId: FullCodec + Copy + MaybeSerializeDeserialize + Debug + Default + Eq + TypeInfo; /// The type used to store the intermediate values between pre- and post-dispatch. diff --git a/frame/transaction-payment/src/payment.rs b/frame/transaction-payment/src/payment.rs index ebc9c5c5a..bc871deaf 100644 --- a/frame/transaction-payment/src/payment.rs +++ b/frame/transaction-payment/src/payment.rs @@ -1,15 +1,11 @@ /// ! Traits and default implementation for paying transaction fees. use crate::Config; -use codec::FullCodec; use sp_runtime::{ - traits::{ - AtLeast32BitUnsigned, DispatchInfoOf, MaybeSerializeDeserialize, PostDispatchInfoOf, - Saturating, Zero, - }, + traits::{DispatchInfoOf, PostDispatchInfoOf, Saturating, Zero}, transaction_validity::InvalidTransaction, }; -use sp_std::{fmt::Debug, marker::PhantomData}; +use sp_std::marker::PhantomData; use frame_support::{ traits::{Currency, ExistenceRequirement, Imbalance, OnUnbalanced, WithdrawReasons}, @@ -22,13 +18,8 @@ type NegativeImbalanceOf = /// Handle withdrawing, refunding and depositing of transaction fees. pub trait OnChargeTransaction { /// The underlying integer type in which fees are calculated. - type Balance: AtLeast32BitUnsigned - + FullCodec - + Copy - + MaybeSerializeDeserialize - + Debug - + Default - + scale_info::TypeInfo; + type Balance: frame_support::traits::tokens::Balance; + type LiquidityInfo: Default; /// Before the transaction is executed the payment of the transaction fees From 30641eed4763cc1ef466a0f447f2a1b2456910eb Mon Sep 17 00:00:00 2001 From: Vladimir Istyufeev Date: Fri, 13 Jan 2023 20:21:44 +0400 Subject: [PATCH 012/558] CI: Code mark to request a pipeline failure (#13139) --- scripts/ci/gitlab/pipeline/test.yml | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/scripts/ci/gitlab/pipeline/test.yml b/scripts/ci/gitlab/pipeline/test.yml index a468a7b04..466019e42 100644 --- a/scripts/ci/gitlab/pipeline/test.yml +++ b/scripts/ci/gitlab/pipeline/test.yml @@ -1,6 +1,27 @@ # This file is part of .gitlab-ci.yml # Here are all jobs that are executed during "test" stage + +# It's more like a check and it belongs to the previous stage, but we want to run this job with real tests in parallel +find-fail-ci-phrase: + stage: test + variables: + CI_IMAGE: "paritytech/tools:latest" + ASSERT_REGEX: "FAIL-CI" + GIT_DEPTH: 1 + extends: + - .kubernetes-env + script: + - set +e + - rg --line-number --hidden --type rust --glob '!{.git,target}' "$ASSERT_REGEX" .; exit_status=$? + - if [ $exit_status -eq 0 ]; then + echo "$ASSERT_REGEX was found, exiting with 1"; + exit 1; + else + echo "No $ASSERT_REGEX was found, exiting with 0"; + exit 0; + fi + cargo-deny: stage: test extends: From 49962f76ad9a1cc45086a80188f3d751ac15eb82 Mon Sep 17 00:00:00 2001 From: Jun Jiang Date: Sun, 15 Jan 2023 21:31:14 +0800 Subject: [PATCH 013/558] Annotate thiserror for sp_core::crypto::SecretStringError (#13144) --- primitives/core/src/crypto.rs | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/primitives/core/src/crypto.rs b/primitives/core/src/crypto.rs index 06703acea..52c805bb6 100644 --- a/primitives/core/src/crypto.rs +++ b/primitives/core/src/crypto.rs @@ -84,20 +84,27 @@ impl> UncheckedInto for S { } /// An error with the interpretation of a secret. +#[cfg_attr(feature = "std", derive(thiserror::Error))] #[derive(Debug, Clone, PartialEq, Eq)] #[cfg(feature = "full_crypto")] pub enum SecretStringError { /// The overall format was invalid (e.g. the seed phrase contained symbols). + #[cfg_attr(feature = "std", error("Invalid format"))] InvalidFormat, /// The seed phrase provided is not a valid BIP39 phrase. + #[cfg_attr(feature = "std", error("Invalid phrase"))] InvalidPhrase, /// The supplied password was invalid. + #[cfg_attr(feature = "std", error("Invalid password"))] InvalidPassword, /// The seed is invalid (bad content). + #[cfg_attr(feature = "std", error("Invalid seed"))] InvalidSeed, /// The seed has an invalid length. + #[cfg_attr(feature = "std", error("Invalid seed length"))] InvalidSeedLength, /// The derivation path was invalid (e.g. contains soft junctions when they are not supported). + #[cfg_attr(feature = "std", error("Invalid path"))] InvalidPath, } From 70f2e364ab1ad57ce8d3abe228222b299393c5c0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bastian=20K=C3=B6cher?= Date: Mon, 16 Jan 2023 13:39:13 +0100 Subject: [PATCH 014/558] pallet-offences-benchmarking: Box events in verify (#13151) * pallet-offences-benchmarking: Box events in verify Events in frame are represented by an enum in the pallet and the runtime. The size of an enum in Rust depends on the size of biggest variant. This means we always need to allocate memory for the biggest variant when allocating memory for an event. The offences benchmarking is verifying the benchmarking results by checking the events. To check the events it is generating all the expected events. With the recent changes in Polkadot the events are too big and lead to issues when running this verify functions. The solution is to box each event, as the vector holding all the events will then only need to hold fat pointers * expected events, instead of size_of(event) * expected events. This issue isn't a problem in production, as we never read the events on chain. When we are reading the events, it is done in an offchain context and they are only decoded one by one. Besides that this also enables the benchmarking verification for everyone running these benchmarks. * FMT * Disable checking again --- Cargo.lock | 1 + frame/offences/benchmarking/Cargo.toml | 2 + frame/offences/benchmarking/src/lib.rs | 189 +++++++++++++------------ 3 files changed, 104 insertions(+), 88 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 68c144b3f..e4e3e1b84 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -6079,6 +6079,7 @@ dependencies = [ "frame-election-provider-support", "frame-support", "frame-system", + "log", "pallet-babe", "pallet-balances", "pallet-grandpa", diff --git a/frame/offences/benchmarking/Cargo.toml b/frame/offences/benchmarking/Cargo.toml index e20aefd69..e31e8bbed 100644 --- a/frame/offences/benchmarking/Cargo.toml +++ b/frame/offences/benchmarking/Cargo.toml @@ -29,6 +29,7 @@ pallet-staking = { version = "4.0.0-dev", default-features = false, path = "../. sp-runtime = { version = "7.0.0", default-features = false, path = "../../../primitives/runtime" } sp-staking = { version = "4.0.0-dev", default-features = false, path = "../../../primitives/staking" } sp-std = { version = "5.0.0", default-features = false, path = "../../../primitives/std" } +log = { version = "0.4.17", default-features = false } [dev-dependencies] pallet-staking-reward-curve = { version = "4.0.0-dev", path = "../../staking/reward-curve" } @@ -55,6 +56,7 @@ std = [ "sp-runtime/std", "sp-staking/std", "sp-std/std", + "log/std", ] runtime-benchmarks = [ diff --git a/frame/offences/benchmarking/src/lib.rs b/frame/offences/benchmarking/src/lib.rs index e5ec2952f..7e586a96b 100644 --- a/frame/offences/benchmarking/src/lib.rs +++ b/frame/offences/benchmarking/src/lib.rs @@ -28,8 +28,10 @@ use frame_benchmarking::{account, benchmarks}; use frame_support::traits::{Currency, Get, ValidatorSet, ValidatorSetWithIdentification}; use frame_system::{Config as SystemConfig, Pallet as System, RawOrigin}; +#[cfg(test)] +use sp_runtime::traits::UniqueSaturatedInto; use sp_runtime::{ - traits::{Convert, Saturating, StaticLookup, UniqueSaturatedInto}, + traits::{Convert, Saturating, StaticLookup}, Perbill, }; use sp_staking::offence::{Offence, ReportOffence}; @@ -43,9 +45,11 @@ use pallet_session::{ historical::{Config as HistoricalConfig, IdentificationTuple}, Config as SessionConfig, SessionManager, }; +#[cfg(test)] +use pallet_staking::Event as StakingEvent; use pallet_staking::{ - Config as StakingConfig, Event as StakingEvent, Exposure, IndividualExposure, - Pallet as Staking, RewardDestination, ValidatorPrefs, + Config as StakingConfig, Exposure, IndividualExposure, Pallet as Staking, RewardDestination, + ValidatorPrefs, }; const SEED: u32 = 0; @@ -89,7 +93,9 @@ type BalanceOf = struct Offender { pub controller: T::AccountId, + #[allow(dead_code)] pub stash: T::AccountId, + #[allow(dead_code)] pub nominator_stashes: Vec, } @@ -217,55 +223,63 @@ fn make_offenders_im_online( } #[cfg(test)] -fn check_events::RuntimeEvent>>(expected: I) { +fn check_events< + T: Config, + I: Iterator, + Item: sp_std::borrow::Borrow<::RuntimeEvent> + sp_std::fmt::Debug, +>( + expected: I, +) { let events = System::::events() .into_iter() .map(|frame_system::EventRecord { event, .. }| event) .collect::>(); let expected = expected.collect::>(); - fn pretty(header: &str, ev: &[D], offset: usize) { - println!("{}", header); + fn pretty(header: &str, ev: &[D], offset: usize) { + log::info!("{}", header); for (idx, ev) in ev.iter().enumerate() { - println!("\t[{:04}] {:?}", idx + offset, ev); + log::info!("\t[{:04}] {:?}", idx + offset, ev); } } - fn print_events(idx: usize, events: &[D], expected: &[D]) { + fn print_events( + idx: usize, + events: &[D], + expected: &[E], + ) { let window = 10; let start = idx.saturating_sub(window / 2); let end_got = (idx + window / 2).min(events.len()); pretty("Got(window):", &events[start..end_got], start); let end_expected = (idx + window / 2).min(expected.len()); pretty("Expected(window):", &expected[start..end_expected], start); - println!("---------------"); + log::info!("---------------"); let start_got = events.len().saturating_sub(window); pretty("Got(end):", &events[start_got..], start_got); let start_expected = expected.len().saturating_sub(window); pretty("Expected(end):", &expected[start_expected..], start_expected); } - let events_copy = events.clone(); - let expected_copy = expected.clone(); - - for (idx, (a, b)) in events.into_iter().zip(expected).enumerate() { - if a != b { - print_events(idx, &events_copy, &expected_copy); - println!("Mismatch at: {}", idx); - println!(" Got: {:?}", b); - println!("Expected: {:?}", a); - if events_copy.len() != expected_copy.len() { - println!( + + for (idx, (a, b)) in events.iter().zip(expected.iter()).enumerate() { + if a != sp_std::borrow::Borrow::borrow(b) { + print_events(idx, &events, &expected); + log::info!("Mismatch at: {}", idx); + log::info!(" Got: {:?}", b); + log::info!("Expected: {:?}", a); + if events.len() != expected.len() { + log::info!( "Mismatching lengths. Got: {}, Expected: {}", - events_copy.len(), - expected_copy.len() + events.len(), + expected.len() ) } panic!("Mismatching events."); } } - if events_copy.len() != expected_copy.len() { - print_events(0, &events_copy, &expected_copy); - panic!("Mismatching lengths. Got: {}, Expected: {}", events_copy.len(), expected_copy.len()) + if events.len() != expected.len() { + print_events(0, &events, &expected); + panic!("Mismatching lengths. Got: {}, Expected: {}", events.len(), expected.len(),) } } @@ -304,79 +318,78 @@ benchmarks! { ); } verify { - let bond_amount: u32 = UniqueSaturatedInto::::unique_saturated_into(bond_amount::()); - let slash_amount = slash_fraction * bond_amount; - let reward_amount = slash_amount.saturating_mul(1 + n) / 2; - let reward = reward_amount / r; - let slash_report = |id| core::iter::once( - ::RuntimeEvent::from(StakingEvent::::SlashReported{ validator: id, fraction: slash_fraction, slash_era: 0}) - ); - let slash = |id| core::iter::once( - ::RuntimeEvent::from(StakingEvent::::Slashed{ staker: id, amount: BalanceOf::::from(slash_amount) }) - ); - let balance_slash = |id| core::iter::once( - ::RuntimeEvent::from(pallet_balances::Event::::Slashed{ who: id, amount: slash_amount.into() }) - ); - let chill = |id| core::iter::once( - ::RuntimeEvent::from(StakingEvent::::Chilled{ stash: id }) - ); - let balance_deposit = |id, amount: u32| + #[cfg(test)] + { + let bond_amount: u32 = UniqueSaturatedInto::::unique_saturated_into(bond_amount::()); + let slash_amount = slash_fraction * bond_amount; + let reward_amount = slash_amount.saturating_mul(1 + n) / 2; + let reward = reward_amount / r; + let slash_report = |id| core::iter::once( + ::RuntimeEvent::from(StakingEvent::::SlashReported{ validator: id, fraction: slash_fraction, slash_era: 0}) + ); + let slash = |id| core::iter::once( + ::RuntimeEvent::from(StakingEvent::::Slashed{ staker: id, amount: BalanceOf::::from(slash_amount) }) + ); + let balance_slash = |id| core::iter::once( + ::RuntimeEvent::from(pallet_balances::Event::::Slashed{ who: id, amount: slash_amount.into() }) + ); + let chill = |id| core::iter::once( + ::RuntimeEvent::from(StakingEvent::::Chilled{ stash: id }) + ); + let balance_deposit = |id, amount: u32| ::RuntimeEvent::from(pallet_balances::Event::::Deposit{ who: id, amount: amount.into() }); - let mut first = true; - let slash_events = raw_offenders.into_iter() - .flat_map(|offender| { - let nom_slashes = offender.nominator_stashes.into_iter().flat_map(|nom| { - balance_slash(nom.clone()).map(Into::into) - .chain(slash(nom).map(Into::into)) - }); - - let mut events = chill(offender.stash.clone()).map(Into::into) - .chain(slash_report(offender.stash.clone()).map(Into::into)) - .chain(balance_slash(offender.stash.clone()).map(Into::into)) - .chain(slash(offender.stash).map(Into::into)) - .chain(nom_slashes) - .collect::>(); - - // the first deposit creates endowed events, see `endowed_reward_events` - if first { - first = false; - let mut reward_events = reporters.clone().into_iter() - .flat_map(|reporter| vec![ - balance_deposit(reporter.clone(), reward).into(), - frame_system::Event::::NewAccount { account: reporter.clone() }.into(), - ::RuntimeEvent::from( - pallet_balances::Event::::Endowed{account: reporter, free_balance: reward.into()} - ).into(), - ]) + let mut first = true; + + // We need to box all events to prevent running into too big allocations in wasm. + // The event in FRAME is represented as an enum and the size of the enum depends on the biggest variant. + // So, instead of requiring `size_of() * expected_events` we only need to + // allocate `size_of>() * expected_events`. + let slash_events = raw_offenders.into_iter() + .flat_map(|offender| { + let nom_slashes = offender.nominator_stashes.into_iter().flat_map(|nom| { + balance_slash(nom.clone()).map(Into::into).chain(slash(nom).map(Into::into)).map(Box::new) + }); + + let events = chill(offender.stash.clone()).map(Into::into).map(Box::new) + .chain(slash_report(offender.stash.clone()).map(Into::into).map(Box::new)) + .chain(balance_slash(offender.stash.clone()).map(Into::into).map(Box::new)) + .chain(slash(offender.stash).map(Into::into).map(Box::new)) + .chain(nom_slashes) .collect::>(); - events.append(&mut reward_events); - events.into_iter() - } else { - let mut reward_events = reporters.clone().into_iter() - .map(|reporter| balance_deposit(reporter, reward).into()) - .collect::>(); - events.append(&mut reward_events); - events.into_iter() - } - }) - .collect::>(); - + // the first deposit creates endowed events, see `endowed_reward_events` + if first { + first = false; + let reward_events = reporters.iter() + .flat_map(|reporter| vec![ + Box::new(balance_deposit(reporter.clone(), reward).into()), + Box::new(frame_system::Event::::NewAccount { account: reporter.clone() }.into()), + Box::new(::RuntimeEvent::from( + pallet_balances::Event::::Endowed{ account: reporter.clone(), free_balance: reward.into() } + ).into()), + ]) + .collect::>(); + events.into_iter().chain(reward_events) + } else { + let reward_events = reporters.iter() + .map(|reporter| Box::new(balance_deposit(reporter.clone(), reward).into())) + .collect::>(); + events.into_iter().chain(reward_events) + } + }); - #[cfg(test)] - { // In case of error it's useful to see the inputs - println!("Inputs: r: {}, o: {}, n: {}", r, o, n); + log::info!("Inputs: r: {}, o: {}, n: {}", r, o, n); // make sure that all slashes have been applied - check_events::( - std::iter::empty() - .chain(slash_events.into_iter().map(Into::into)) - .chain(std::iter::once(::RuntimeEvent::from( + check_events::( + sp_std::iter::empty() + .chain(slash_events) + .chain(sp_std::iter::once(Box::new(::RuntimeEvent::from( pallet_offences::Event::Offence{ kind: UnresponsivenessOffence::::ID, timeslot: 0_u32.to_le_bytes().to_vec(), } - ).into())) + ).into()))) ); } } From ea002409d10af3fb7a402b6948303233c6f46549 Mon Sep 17 00:00:00 2001 From: JP <77391175+joao-paulo-parity@users.noreply.github.com> Date: Mon, 16 Jan 2023 11:03:46 -0300 Subject: [PATCH 015/558] More improvements for the crate publishing pipeline (#13153) * more improvements for the crate publishing pipeline * move default definitions to the publish-crates script * add script to check the crate publishing pipeline at the start * fix yaml references * move more variables to .crates-publishing-pipeline * separate .crates-publishing-pipeline from .crates-publishing-variables * clean up redundant and unused code --- .gitlab-ci.yml | 39 ++++++++++++++++---------- scripts/ci/gitlab/pipeline/publish.yml | 28 ++++++++---------- scripts/ci/gitlab/pipeline/test.yml | 7 ++++- 3 files changed, 41 insertions(+), 33 deletions(-) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index ecc27d016..ae25a2c59 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -229,19 +229,21 @@ variables: # this job runs only on nightly pipeline with the mentioned variable, against `master` branch - if: $CI_COMMIT_REF_NAME == "master" && $CI_PIPELINE_SOURCE == "schedule" && $PIPELINE == "nightly" -.crate-publishing-pipeline: - rules: - - if: $CI_COMMIT_REF_NAME != "master" - when: never +.crates-publishing-variables: + variables: + CRATESIO_CRATES_OWNER: parity-crate-owner + REPO: substrate + REPO_OWNER: paritytech -.scheduled-crate-publishing-pipeline: +.crates-publishing-pipeline: + extends: .crates-publishing-variables rules: - - !reference [.crate-publishing-pipeline, rules] - - if: $CI_PIPELINE_SOURCE == "schedule" && $PIPELINE == "automatic-crate-publishing" + - if: $CI_PIPELINE_SOURCE == "schedule" && $CI_COMMIT_REF_NAME == "master" && $PIPELINE == "automatic-crate-publishing" .crates-publishing-template: - stage: test - extends: .docker-env + extends: + - .docker-env + - .crates-publishing-variables # collect artifacts even on failure so that we know how the crates were generated (they'll be # generated to the artifacts folder according to SPUB_TMP further down) artifacts: @@ -251,12 +253,7 @@ variables: paths: - artifacts/ variables: - CRATESIO_API: https://crates.io/api - CRATESIO_CRATES_OWNER: parity-crate-owner - GH_API: https://api.github.com - REPO: substrate - REPO_OWNER: paritytech - SPUB_TMP: artifacts + SPUB_TMP: artifacts #### stage: .pre @@ -274,6 +271,18 @@ skip-if-draft: - ./scripts/ci/gitlab/skip_if_draft.sh allow_failure: true +check-crates-publishing-pipeline: + stage: .pre + extends: + - .kubernetes-env + - .crates-publishing-pipeline + script: + - git clone + --depth 1 + --branch "$RELENG_SCRIPTS_BRANCH" + https://github.com/paritytech/releng-scripts.git + - ONLY_CHECK_PIPELINE=true ./releng-scripts/publish-crates + include: # check jobs - scripts/ci/gitlab/pipeline/check.yml diff --git a/scripts/ci/gitlab/pipeline/publish.yml b/scripts/ci/gitlab/pipeline/publish.yml index d1a7514d1..4eafb0a05 100644 --- a/scripts/ci/gitlab/pipeline/publish.yml +++ b/scripts/ci/gitlab/pipeline/publish.yml @@ -184,19 +184,19 @@ publish-draft-release: .publish-crates-template: stage: publish - extends: .crates-publishing-template + extends: + - .crates-publishing-template + - .crates-publishing-pipeline # We don't want multiple jobs racing to publish crates as it's redundant and they might overwrite # the releases of one another. Use resource_group to ensure that at most one instance of this job # is running at any given time. resource_group: crates-publishing - variables: - # crates.io rate limits crates publishing by 1 per minute, so a delay needs to be inserted - # slightly higher than that after publishing each crate. The value is specified in seconds. - SPUB_AFTER_PUBLISH_DELAY: 64 - # We might have to publish lots of crates at a time. Given the 1 minute delay introduced above and - # taking into account the 202 (as of Dec 07, 2022) publishable Substrate crates, that would equate - # to roughly 202 minutes of delay, or 3h and 22 minutes. As such, the job needs to have a much - # higher timeout than average. + # crates.io currently rate limits crate publishing at 1 per minute: + # https://github.com/paritytech/release-engineering/issues/123#issuecomment-1335509748 + # Taking into account the 202 (as of Dec 07, 2022) publishable Substrate crates, in the worst + # case, due to the rate limits alone, we'd have to wait through at least 202 minutes of delay. + # Taking into account also the verification steps and extra synchronization delays after + # publishing the crate, the job needs to have a much higher timeout than average. timeout: 9h # A custom publishing environment is used for us to be able to set up protected secrets # specifically for it @@ -211,15 +211,9 @@ publish-draft-release: - rusty-cachier cache upload publish-crates: - extends: - - .publish-crates-template - - .scheduled-crate-publishing-pipeline - needs: - - job: publish-crates-locally - artifacts: false + extends: .publish-crates-template publish-crates-manual: extends: .publish-crates-template - rules: !reference [.crate-publishing-pipeline, rules] when: manual - allow_failure: true + interruptible: false diff --git a/scripts/ci/gitlab/pipeline/test.yml b/scripts/ci/gitlab/pipeline/test.yml index 466019e42..f00857ffa 100644 --- a/scripts/ci/gitlab/pipeline/test.yml +++ b/scripts/ci/gitlab/pipeline/test.yml @@ -14,7 +14,7 @@ find-fail-ci-phrase: script: - set +e - rg --line-number --hidden --type rust --glob '!{.git,target}' "$ASSERT_REGEX" .; exit_status=$? - - if [ $exit_status -eq 0 ]; then + - if [ $exit_status -eq 0 ]; then echo "$ASSERT_REGEX was found, exiting with 1"; exit 1; else @@ -417,9 +417,14 @@ cargo-check-each-crate: parallel: 2 publish-crates-locally: + stage: test extends: - .test-refs - .crates-publishing-template + # When lots of crates are taken into account (for example on master where all crates are tested) + # the job might take a long time, as evidenced by: + # https://gitlab.parity.io/parity/mirrors/substrate/-/jobs/2269364 + timeout: 4h script: - rusty-cachier snapshot create - git clone From 7ff6fa2bfa47f288216fdc6e1585f69756c5ec75 Mon Sep 17 00:00:00 2001 From: Michal Kucharczyk <1728078+michalkucharczyk@users.noreply.github.com> Date: Mon, 16 Jan 2023 23:15:22 +0100 Subject: [PATCH 016/558] txpool: don't maintain the pool during major sync (#13004) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * txpool: don't maintain the pool during major sync Fix shall prevent from wasting the CPU during the major sync. No actions are actually required in transaction pool during the major sync. Fixes: #12903 * passing sync_oracle to maintain method * fixed: builder, txpool tests * do not maintain tx-pool if node gone out of sync * EnactmentAction: all logic moved to EnactmentState Tests to be done. * maintain guard logic moved directly to MaintainedTransactionPool * minor fixes * EnactmentAction: all logic moved to EnactmentState (again) * SyncOracle fixes here and there * Update client/transaction-pool/src/enactment_state.rs Co-authored-by: Bastian Köcher * Update client/transaction-pool/src/enactment_state.rs Co-authored-by: Bastian Köcher * sync_oracle removed * spelling + fmt + doc * Review suggestions applied * log::info -> debug * Update client/transaction-pool/src/enactment_state.rs Co-authored-by: Bastian Köcher * ".git/.scripts/commands/fmt/fmt.sh" Co-authored-by: parity-processbot <> Co-authored-by: Bastian Köcher --- Cargo.lock | 1 + client/transaction-pool/Cargo.toml | 1 + .../transaction-pool/src/enactment_state.rs | 185 +++++++++++++----- client/transaction-pool/src/lib.rs | 14 +- 4 files changed, 145 insertions(+), 56 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index e4e3e1b84..446638277 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -9046,6 +9046,7 @@ dependencies = [ "futures-timer", "linked-hash-map", "log", + "num-traits", "parity-scale-codec", "parking_lot 0.12.1", "sc-block-builder", diff --git a/client/transaction-pool/Cargo.toml b/client/transaction-pool/Cargo.toml index 7a3ab042d..4b69b6d0f 100644 --- a/client/transaction-pool/Cargo.toml +++ b/client/transaction-pool/Cargo.toml @@ -19,6 +19,7 @@ futures = "0.3.21" futures-timer = "3.0.2" linked-hash-map = "0.5.4" log = "0.4.17" +num-traits = "0.2.8" parking_lot = "0.12.1" serde = { version = "1.0.136", features = ["derive"] } thiserror = "1.0.30" diff --git a/client/transaction-pool/src/enactment_state.rs b/client/transaction-pool/src/enactment_state.rs index 6aac98641..382b26831 100644 --- a/client/transaction-pool/src/enactment_state.rs +++ b/client/transaction-pool/src/enactment_state.rs @@ -18,13 +18,21 @@ //! Substrate transaction pool implementation. +use num_traits::CheckedSub; use sc_transaction_pool_api::ChainEvent; use sp_blockchain::TreeRoute; -use sp_runtime::traits::Block as BlockT; +use sp_runtime::traits::{Block as BlockT, NumberFor}; + +/// The threshold since the last update where we will skip any maintenance for blocks. +/// +/// This includes tracking re-orgs and sending out certain notifications. In general this shouldn't +/// happen and may only happen when the node is doing a full sync. +const SKIP_MAINTENANCE_THRESHOLD: u16 = 20; /// Helper struct for keeping track of the current state of processed new best /// block and finalized events. The main purpose of keeping track of this state -/// is to figure out if a transaction pool enactment is needed or not. +/// is to figure out which phases (enactment / finalization) of transaction pool +/// maintenance are needed. /// /// Given the following chain: /// @@ -54,6 +62,17 @@ where recent_finalized_block: Block::Hash, } +/// Enactment action that should be performed after processing the `ChainEvent` +#[derive(Debug)] +pub enum EnactmentAction { + /// Both phases of maintenance shall be skipped + Skip, + /// Both phases of maintenance shall be performed + HandleEnactment(TreeRoute), + /// Enactment phase of maintenance shall be skipped + HandleFinalization, +} + impl EnactmentState where Block: BlockT, @@ -71,23 +90,38 @@ where /// Updates the state according to the given `ChainEvent`, returning /// `Some(tree_route)` with a tree route including the blocks that need to /// be enacted/retracted. If no enactment is needed then `None` is returned. - pub fn update( + pub fn update( &mut self, event: &ChainEvent, - tree_route: &F, - ) -> Result>, String> + tree_route: &TreeRouteF, + hash_to_number: &BlockNumberF, + ) -> Result, String> where - F: Fn(Block::Hash, Block::Hash) -> Result, String>, + TreeRouteF: Fn(Block::Hash, Block::Hash) -> Result, String>, + BlockNumberF: Fn(Block::Hash) -> Result>, String>, { - let (new_hash, finalized) = match event { - ChainEvent::NewBestBlock { hash, .. } => (*hash, false), - ChainEvent::Finalized { hash, .. } => (*hash, true), + let (new_hash, current_hash, finalized) = match event { + ChainEvent::NewBestBlock { hash, .. } => (*hash, self.recent_best_block, false), + ChainEvent::Finalized { hash, .. } => (*hash, self.recent_finalized_block, true), }; + // do not proceed with txpool maintain if block distance is to high + let skip_maintenance = match (hash_to_number(new_hash), hash_to_number(current_hash)) { + (Ok(Some(new)), Ok(Some(current))) => + new.checked_sub(¤t) > Some(SKIP_MAINTENANCE_THRESHOLD.into()), + _ => true, + }; + + if skip_maintenance { + log::debug!(target: "txpool", "skip maintain: tree_route would be too long"); + self.force_update(event); + return Ok(EnactmentAction::Skip) + } + // block was already finalized if self.recent_finalized_block == new_hash { log::debug!(target: "txpool", "handle_enactment: block already finalized"); - return Ok(None) + return Ok(EnactmentAction::Skip) } // compute actual tree route from best_block to notified block, and use @@ -109,7 +143,7 @@ where "Recently finalized block {} would be retracted by ChainEvent {}, skipping", self.recent_finalized_block, new_hash ); - return Ok(None) + return Ok(EnactmentAction::Skip) } if finalized { @@ -124,7 +158,7 @@ where target: "txpool", "handle_enactment: no newly enacted blocks since recent best block" ); - return Ok(None) + return Ok(EnactmentAction::HandleFinalization) } // otherwise enacted finalized block becomes best block... @@ -132,7 +166,7 @@ where self.recent_best_block = new_hash; - Ok(Some(tree_route)) + Ok(EnactmentAction::HandleEnactment(tree_route)) } /// Forces update of the state according to the given `ChainEvent`. Intended to be used as a @@ -148,9 +182,10 @@ where #[cfg(test)] mod enactment_state_tests { - use super::EnactmentState; + use super::{EnactmentAction, EnactmentState}; use sc_transaction_pool_api::ChainEvent; use sp_blockchain::{HashAndNumber, TreeRoute}; + use sp_runtime::traits::NumberFor; use std::sync::Arc; use substrate_test_runtime_client::runtime::{Block, Hash}; @@ -170,6 +205,9 @@ mod enactment_state_tests { fn e1() -> HashAndNumber { HashAndNumber { number: 5, hash: Hash::from([0xE1; 32]) } } + fn x1() -> HashAndNumber { + HashAndNumber { number: 22, hash: Hash::from([0x1E; 32]) } + } fn b2() -> HashAndNumber { HashAndNumber { number: 2, hash: Hash::from([0xB2; 32]) } } @@ -182,11 +220,22 @@ mod enactment_state_tests { fn e2() -> HashAndNumber { HashAndNumber { number: 5, hash: Hash::from([0xE2; 32]) } } + fn x2() -> HashAndNumber { + HashAndNumber { number: 22, hash: Hash::from([0x2E; 32]) } + } + + fn test_chain() -> Vec> { + vec![x1(), e1(), d1(), c1(), b1(), a(), b2(), c2(), d2(), e2(), x2()] + } + + fn block_hash_to_block_number(hash: Hash) -> Result>, String> { + Ok(test_chain().iter().find(|x| x.hash == hash).map(|x| x.number)) + } /// mock tree_route computing function for simple two-forks chain fn tree_route(from: Hash, to: Hash) -> Result, String> { - let chain = vec![e1(), d1(), c1(), b1(), a(), b2(), c2(), d2(), e2()]; - let pivot = 4_usize; + let chain = test_chain(); + let pivot = chain.iter().position(|x| x.number == a().number).unwrap(); let from = chain .iter() @@ -197,13 +246,13 @@ mod enactment_state_tests { .position(|bn| bn.hash == to) .ok_or("existing block should be given")?; - // B1-C1-D1-E1 + // B1-C1-D1-E1-..-X1 // / // A // \ - // B2-C2-D2-E2 + // B2-C2-D2-E2-..-X2 // - // [E1 D1 C1 B1 A B2 C2 D2 E2] + // [X1 E1 D1 C1 B1 A B2 C2 D2 E2 X2] let vec: Vec> = if from < to { chain.into_iter().skip(from).take(to - from + 1).collect() @@ -373,13 +422,20 @@ mod enactment_state_tests { let expected = TreeRoute::new(vec![a()], 0); assert_treeroute_eq(result, expected); } + + #[test] + fn tree_route_mock_test_17() { + let result = tree_route(x2().hash, b1().hash); + let expected = TreeRoute::new(vec![x2(), e2(), d2(), c2(), b2(), a(), b1()], 5); + assert_treeroute_eq(result, expected); + } } fn trigger_new_best_block( state: &mut EnactmentState, from: HashAndNumber, acted_on: HashAndNumber, - ) -> bool { + ) -> EnactmentAction { let (from, acted_on) = (from.hash, acted_on.hash); let event_tree_route = tree_route(from, acted_on).expect("Tree route exists"); @@ -391,16 +447,16 @@ mod enactment_state_tests { tree_route: Some(Arc::new(event_tree_route)), }, &tree_route, + &block_hash_to_block_number, ) .unwrap() - .is_some() } fn trigger_finalized( state: &mut EnactmentState, from: HashAndNumber, acted_on: HashAndNumber, - ) -> bool { + ) -> EnactmentAction { let (from, acted_on) = (from.hash, acted_on.hash); let v = tree_route(from, acted_on) @@ -411,9 +467,12 @@ mod enactment_state_tests { .collect::>(); state - .update(&ChainEvent::Finalized { hash: acted_on, tree_route: v.into() }, &tree_route) + .update( + &ChainEvent::Finalized { hash: acted_on, tree_route: v.into() }, + &tree_route, + &block_hash_to_block_number, + ) .unwrap() - .is_some() } fn assert_es_eq( @@ -437,51 +496,51 @@ mod enactment_state_tests { // B2-C2-D2-E2 let result = trigger_new_best_block(&mut es, a(), d1()); - assert!(result); + assert!(matches!(result, EnactmentAction::HandleEnactment { .. })); assert_es_eq(&es, d1(), a()); let result = trigger_new_best_block(&mut es, d1(), e1()); - assert!(result); + assert!(matches!(result, EnactmentAction::HandleEnactment { .. })); assert_es_eq(&es, e1(), a()); let result = trigger_finalized(&mut es, a(), d2()); - assert!(result); + assert!(matches!(result, EnactmentAction::HandleEnactment { .. })); assert_es_eq(&es, d2(), d2()); let result = trigger_new_best_block(&mut es, d2(), e1()); - assert_eq!(result, false); + assert!(matches!(result, EnactmentAction::Skip)); assert_es_eq(&es, d2(), d2()); let result = trigger_finalized(&mut es, a(), b2()); - assert_eq!(result, false); + assert!(matches!(result, EnactmentAction::Skip)); assert_es_eq(&es, d2(), d2()); let result = trigger_finalized(&mut es, a(), b1()); - assert_eq!(result, false); + assert!(matches!(result, EnactmentAction::Skip)); assert_es_eq(&es, d2(), d2()); let result = trigger_new_best_block(&mut es, a(), d2()); - assert_eq!(result, false); + assert!(matches!(result, EnactmentAction::Skip)); assert_es_eq(&es, d2(), d2()); let result = trigger_finalized(&mut es, a(), d2()); - assert_eq!(result, false); + assert!(matches!(result, EnactmentAction::Skip)); assert_es_eq(&es, d2(), d2()); let result = trigger_new_best_block(&mut es, a(), c2()); - assert_eq!(result, false); + assert!(matches!(result, EnactmentAction::Skip)); assert_es_eq(&es, d2(), d2()); let result = trigger_new_best_block(&mut es, a(), c1()); - assert_eq!(result, false); + assert!(matches!(result, EnactmentAction::Skip)); assert_es_eq(&es, d2(), d2()); let result = trigger_new_best_block(&mut es, d2(), e2()); - assert!(result); + assert!(matches!(result, EnactmentAction::HandleEnactment { .. })); assert_es_eq(&es, e2(), d2()); let result = trigger_finalized(&mut es, d2(), e2()); - assert_eq!(result, false); + assert!(matches!(result, EnactmentAction::HandleFinalization)); assert_es_eq(&es, e2(), e2()); } @@ -493,27 +552,27 @@ mod enactment_state_tests { // A-B1-C1-D1-E1 let result = trigger_new_best_block(&mut es, a(), b1()); - assert!(result); + assert!(matches!(result, EnactmentAction::HandleEnactment { .. })); assert_es_eq(&es, b1(), a()); let result = trigger_new_best_block(&mut es, b1(), c1()); - assert!(result); + assert!(matches!(result, EnactmentAction::HandleEnactment { .. })); assert_es_eq(&es, c1(), a()); let result = trigger_new_best_block(&mut es, c1(), d1()); - assert!(result); + assert!(matches!(result, EnactmentAction::HandleEnactment { .. })); assert_es_eq(&es, d1(), a()); let result = trigger_new_best_block(&mut es, d1(), e1()); - assert!(result); + assert!(matches!(result, EnactmentAction::HandleEnactment { .. })); assert_es_eq(&es, e1(), a()); let result = trigger_finalized(&mut es, a(), c1()); - assert_eq!(result, false); + assert!(matches!(result, EnactmentAction::HandleFinalization)); assert_es_eq(&es, e1(), c1()); let result = trigger_finalized(&mut es, c1(), e1()); - assert_eq!(result, false); + assert!(matches!(result, EnactmentAction::HandleFinalization)); assert_es_eq(&es, e1(), e1()); } @@ -525,11 +584,11 @@ mod enactment_state_tests { // A-B1-C1-D1-E1 let result = trigger_new_best_block(&mut es, a(), e1()); - assert!(result); + assert!(matches!(result, EnactmentAction::HandleEnactment { .. })); assert_es_eq(&es, e1(), a()); let result = trigger_finalized(&mut es, a(), b1()); - assert_eq!(result, false); + assert!(matches!(result, EnactmentAction::HandleFinalization)); assert_es_eq(&es, e1(), b1()); } @@ -541,11 +600,11 @@ mod enactment_state_tests { // A-B1-C1-D1-E1 let result = trigger_finalized(&mut es, a(), e1()); - assert!(result); + assert!(matches!(result, EnactmentAction::HandleEnactment { .. })); assert_es_eq(&es, e1(), e1()); let result = trigger_finalized(&mut es, e1(), b1()); - assert_eq!(result, false); + assert!(matches!(result, EnactmentAction::Skip)); assert_es_eq(&es, e1(), e1()); } @@ -561,11 +620,11 @@ mod enactment_state_tests { // B2-C2-D2-E2 let result = trigger_finalized(&mut es, a(), e1()); - assert!(result); + assert!(matches!(result, EnactmentAction::HandleEnactment { .. })); assert_es_eq(&es, e1(), e1()); let result = trigger_finalized(&mut es, e1(), e2()); - assert_eq!(result, false); + assert!(matches!(result, EnactmentAction::Skip)); assert_es_eq(&es, e1(), e1()); } @@ -577,19 +636,19 @@ mod enactment_state_tests { // A-B1-C1-D1-E1 let result = trigger_new_best_block(&mut es, a(), b1()); - assert!(result); + assert!(matches!(result, EnactmentAction::HandleEnactment { .. })); assert_es_eq(&es, b1(), a()); let result = trigger_finalized(&mut es, a(), d1()); - assert!(result); + assert!(matches!(result, EnactmentAction::HandleEnactment { .. })); assert_es_eq(&es, d1(), d1()); let result = trigger_new_best_block(&mut es, a(), e1()); - assert!(result); + assert!(matches!(result, EnactmentAction::HandleEnactment { .. })); assert_es_eq(&es, e1(), d1()); let result = trigger_new_best_block(&mut es, a(), c1()); - assert_eq!(result, false); + assert!(matches!(result, EnactmentAction::Skip)); assert_es_eq(&es, e1(), d1()); } @@ -610,4 +669,26 @@ mod enactment_state_tests { es.force_update(&ChainEvent::Finalized { hash: b1().hash, tree_route: Arc::from([]) }); assert_es_eq(&es, a(), b1()); } + + #[test] + fn test_enactment_skip_long_enacted_path() { + sp_tracing::try_init_simple(); + let mut es = EnactmentState::new(a().hash, a().hash); + + // A-B1-C1-..-X1 + let result = trigger_new_best_block(&mut es, a(), x1()); + assert!(matches!(result, EnactmentAction::Skip)); + assert_es_eq(&es, x1(), a()); + } + + #[test] + fn test_enactment_proceed_with_enacted_path_at_threshold() { + sp_tracing::try_init_simple(); + let mut es = EnactmentState::new(b1().hash, b1().hash); + + // A-B1-C1-..-X1 + let result = trigger_new_best_block(&mut es, b1(), x1()); + assert!(matches!(result, EnactmentAction::HandleEnactment { .. })); + assert_es_eq(&es, x1(), b1()); + } } diff --git a/client/transaction-pool/src/lib.rs b/client/transaction-pool/src/lib.rs index f3797b180..1cd9bef77 100644 --- a/client/transaction-pool/src/lib.rs +++ b/client/transaction-pool/src/lib.rs @@ -33,7 +33,7 @@ mod tests; pub use crate::api::FullChainApi; use async_trait::async_trait; -use enactment_state::EnactmentState; +use enactment_state::{EnactmentAction, EnactmentState}; use futures::{ channel::oneshot, future::{self, ready}, @@ -732,16 +732,22 @@ where )), } }; + let block_id_to_number = + |hash| self.api.block_id_to_number(&BlockId::Hash(hash)).map_err(|e| format!("{}", e)); - let result = self.enactment_state.lock().update(&event, &compute_tree_route); + let result = + self.enactment_state + .lock() + .update(&event, &compute_tree_route, &block_id_to_number); match result { Err(msg) => { log::debug!(target: "txpool", "{msg}"); self.enactment_state.lock().force_update(&event); }, - Ok(None) => {}, - Ok(Some(tree_route)) => { + Ok(EnactmentAction::Skip) => return, + Ok(EnactmentAction::HandleFinalization) => {}, + Ok(EnactmentAction::HandleEnactment(tree_route)) => { self.handle_enactment(tree_route).await; }, }; From 28937302694787cd1e6dbd846ff2ba4b1cf82025 Mon Sep 17 00:00:00 2001 From: Anton Date: Tue, 17 Jan 2023 14:28:17 +0400 Subject: [PATCH 017/558] [client/network] Add support for `/wss` addresses (#13152) * join dns with another instance of WS transport Secure Websocket transport needs unresolved addresses, so we join DNS transport with yet another instance of Websocket transport. Closes #12024 * WSS transport itself need to wrap DNS transport in order to resolve addresses before passing them down to TCP transport Refs https://github.com/libp2p/rust-libp2p/issues/3330 * reverse order * simplify code: remove WS from WSS inner DNS transport * remove the 2nd instance of WS transport --- client/network/src/transport.rs | 25 +++++++++++++++++-------- 1 file changed, 17 insertions(+), 8 deletions(-) diff --git a/client/network/src/transport.rs b/client/network/src/transport.rs index 6c792a0c3..51a8483e0 100644 --- a/client/network/src/transport.rs +++ b/client/network/src/transport.rs @@ -54,18 +54,27 @@ pub fn build_transport( ) -> (Boxed<(PeerId, StreamMuxerBox)>, Arc) { // Build the base layer of the transport. let transport = if !memory_only { + // Main transport: DNS(TCP) let tcp_config = tcp::Config::new().nodelay(true); - let desktop_trans = tcp::tokio::Transport::new(tcp_config.clone()); - let desktop_trans = websocket::WsConfig::new(desktop_trans) - .or_transport(tcp::tokio::Transport::new(tcp_config.clone())); - let dns_init = dns::TokioDnsConfig::system(desktop_trans); + let tcp_trans = tcp::tokio::Transport::new(tcp_config.clone()); + let dns_init = dns::TokioDnsConfig::system(tcp_trans); + EitherTransport::Left(if let Ok(dns) = dns_init { - EitherTransport::Left(dns) + // WS + WSS transport + // + // Main transport can't be used for `/wss` addresses because WSS transport needs + // unresolved addresses (BUT WSS transport itself needs an instance of DNS transport to + // resolve and dial addresses). + let tcp_trans = tcp::tokio::Transport::new(tcp_config); + let dns_for_wss = dns::TokioDnsConfig::system(tcp_trans) + .expect("same system_conf & resolver to work"); + EitherTransport::Left(websocket::WsConfig::new(dns_for_wss).or_transport(dns)) } else { - let desktop_trans = tcp::tokio::Transport::new(tcp_config.clone()); - let desktop_trans = websocket::WsConfig::new(desktop_trans) + // In case DNS can't be constructed, fallback to TCP + WS (WSS won't work) + let tcp_trans = tcp::tokio::Transport::new(tcp_config.clone()); + let desktop_trans = websocket::WsConfig::new(tcp_trans) .or_transport(tcp::tokio::Transport::new(tcp_config)); - EitherTransport::Right(desktop_trans.map_err(dns::DnsErr::Transport)) + EitherTransport::Right(desktop_trans) }) } else { EitherTransport::Right(OptionalTransport::some( From 6bac243eec55cb17ac598e2bb689b5303e633b01 Mon Sep 17 00:00:00 2001 From: Gavin Wood Date: Tue, 17 Jan 2023 11:37:59 -0300 Subject: [PATCH 018/558] NIS should retain funds in reserve (#12928) * Keep funds with receipt holder * Counterpart is optional * Use named reserves * Tests * Benchmarks * Fixes * Update frame/nis/src/lib.rs Co-authored-by: Keith Yeung * Update frame/nis/src/lib.rs Co-authored-by: Keith Yeung * Update frame/nis/src/lib.rs * Update frame/nis/src/lib.rs * Update frame/nis/src/tests.rs * Update frame/nis/src/lib.rs Co-authored-by: Oliver Tale-Yazdi * Update frame/nis/src/lib.rs Co-authored-by: Oliver Tale-Yazdi * Update frame/nis/src/lib.rs Co-authored-by: Oliver Tale-Yazdi * Update frame/nis/src/lib.rs * Update frame/nis/src/lib.rs * Update frame/nis/src/lib.rs * Update frame/nis/src/lib.rs * Formatting Co-authored-by: Keith Yeung Co-authored-by: Oliver Tale-Yazdi --- bin/node/runtime/src/lib.rs | 2 + frame/nis/src/benchmarking.rs | 41 +- frame/nis/src/lib.rs | 312 +++++++++++--- frame/nis/src/mock.rs | 4 +- frame/nis/src/tests.rs | 453 ++++++++++++++------ frame/nis/src/weights.rs | 320 ++++++++------ frame/support/src/traits/tokens/fungible.rs | 5 + 7 files changed, 828 insertions(+), 309 deletions(-) diff --git a/bin/node/runtime/src/lib.rs b/bin/node/runtime/src/lib.rs index a9e93a16f..d34c04fcb 100644 --- a/bin/node/runtime/src/lib.rs +++ b/bin/node/runtime/src/lib.rs @@ -1499,6 +1499,7 @@ parameter_types! { pub const ThawThrottle: (Perquintill, BlockNumber) = (Perquintill::from_percent(25), 5); pub Target: Perquintill = Perquintill::zero(); pub const NisPalletId: PalletId = PalletId(*b"py/nis "); + pub const NisReserveId: [u8; 8] = *b"py/nis "; } impl pallet_nis::Config for Runtime { @@ -1522,6 +1523,7 @@ impl pallet_nis::Config for Runtime { type IntakePeriod = IntakePeriod; type MaxIntakeWeight = MaxIntakeWeight; type ThawThrottle = ThawThrottle; + type ReserveId = NisReserveId; } parameter_types! { diff --git a/frame/nis/src/benchmarking.rs b/frame/nis/src/benchmarking.rs index 606b1c484..b45c982bd 100644 --- a/frame/nis/src/benchmarking.rs +++ b/frame/nis/src/benchmarking.rs @@ -21,7 +21,7 @@ use super::*; use frame_benchmarking::{account, benchmarks, whitelisted_caller}; -use frame_support::traits::{Currency, EnsureOrigin, Get}; +use frame_support::traits::{nonfungible::Inspect, Currency, EnsureOrigin, Get}; use frame_system::RawOrigin; use sp_arithmetic::Perquintill; use sp_runtime::{ @@ -106,6 +106,7 @@ benchmarks! { T::Currency::make_free_balance_be(&caller, bid); Nis::::place_bid(RawOrigin::Signed(caller.clone()).into(), bid, 1)?; Nis::::process_queues(Perquintill::one(), 1, 1, &mut WeightCounter::unlimited()); + Nis::::communify(RawOrigin::Signed(caller.clone()).into(), 0)?; let original = T::Currency::free_balance(&Nis::::account_id()); T::Currency::make_free_balance_be(&Nis::::account_id(), BalanceOf::::min_value()); }: _(origin) @@ -116,7 +117,7 @@ benchmarks! { assert!(missing <= Perquintill::one() / 100_000); } - thaw { + thaw_private { let caller: T::AccountId = whitelisted_caller(); T::Currency::make_free_balance_be(&caller, T::MinBid::get() * BalanceOf::::from(3u32)); Nis::::place_bid(RawOrigin::Signed(caller.clone()).into(), T::MinBid::get(), 1)?; @@ -128,6 +129,42 @@ benchmarks! { assert!(Receipts::::get(0).is_none()); } + thaw_communal { + let caller: T::AccountId = whitelisted_caller(); + T::Currency::make_free_balance_be(&caller, T::MinBid::get() * BalanceOf::::from(3u32)); + Nis::::place_bid(RawOrigin::Signed(caller.clone()).into(), T::MinBid::get(), 1)?; + Nis::::place_bid(RawOrigin::Signed(caller.clone()).into(), T::MinBid::get(), 1)?; + Nis::::process_queues(Perquintill::one(), 1, 2, &mut WeightCounter::unlimited()); + Receipts::::mutate(0, |m_g| if let Some(ref mut g) = m_g { g.expiry = Zero::zero() }); + Nis::::communify(RawOrigin::Signed(caller.clone()).into(), 0)?; + }: _(RawOrigin::Signed(caller.clone()), 0) + verify { + assert!(Receipts::::get(0).is_none()); + } + + privatize { + let caller: T::AccountId = whitelisted_caller(); + T::Currency::make_free_balance_be(&caller, T::MinBid::get() * BalanceOf::::from(3u32)); + Nis::::place_bid(RawOrigin::Signed(caller.clone()).into(), T::MinBid::get(), 1)?; + Nis::::place_bid(RawOrigin::Signed(caller.clone()).into(), T::MinBid::get(), 1)?; + Nis::::process_queues(Perquintill::one(), 1, 2, &mut WeightCounter::unlimited()); + Nis::::communify(RawOrigin::Signed(caller.clone()).into(), 0)?; + }: _(RawOrigin::Signed(caller.clone()), 0) + verify { + assert_eq!(Nis::::owner(&0), Some(caller)); + } + + communify { + let caller: T::AccountId = whitelisted_caller(); + T::Currency::make_free_balance_be(&caller, T::MinBid::get() * BalanceOf::::from(3u32)); + Nis::::place_bid(RawOrigin::Signed(caller.clone()).into(), T::MinBid::get(), 1)?; + Nis::::place_bid(RawOrigin::Signed(caller.clone()).into(), T::MinBid::get(), 1)?; + Nis::::process_queues(Perquintill::one(), 1, 2, &mut WeightCounter::unlimited()); + }: _(RawOrigin::Signed(caller.clone()), 0) + verify { + assert_eq!(Nis::::owner(&0), None); + } + process_queues { fill_queues::()?; }: { diff --git a/frame/nis/src/lib.rs b/frame/nis/src/lib.rs index dff64625a..fa0163fb6 100644 --- a/frame/nis/src/lib.rs +++ b/frame/nis/src/lib.rs @@ -169,7 +169,7 @@ pub mod pallet { nonfungible::{Inspect as NonfungibleInspect, Transfer as NonfungibleTransfer}, Currency, Defensive, DefensiveSaturating, ExistenceRequirement::AllowDeath, - OnUnbalanced, ReservableCurrency, + NamedReservableCurrency, OnUnbalanced, }, PalletId, }; @@ -189,9 +189,10 @@ pub mod pallet { type ReceiptRecordOf = ReceiptRecord< ::AccountId, ::BlockNumber, + BalanceOf, >; type IssuanceInfoOf = IssuanceInfo>; - type SummaryRecordOf = SummaryRecord<::BlockNumber>; + type SummaryRecordOf = SummaryRecord<::BlockNumber, BalanceOf>; type BidOf = Bid, ::AccountId>; type QueueTotalsTypeOf = BoundedVec<(u32, BalanceOf), ::QueueCount>; @@ -208,7 +209,7 @@ pub mod pallet { type PalletId: Get; /// Currency type that this works on. - type Currency: ReservableCurrency; + type Currency: NamedReservableCurrency; /// Just the `Currency::Balance` type; we have this item to allow us to constrain it to /// `From`. @@ -300,6 +301,12 @@ pub mod pallet { /// The maximum proportion which may be thawed and the period over which it is reset. #[pallet::constant] type ThawThrottle: Get<(Perquintill, Self::BlockNumber)>; + + /// The name for the reserve ID. + #[pallet::constant] + type ReserveId: Get< + >::ReserveIdentifier, + >; } #[pallet::pallet] @@ -321,11 +328,13 @@ pub mod pallet { #[derive( Clone, Eq, PartialEq, Default, Encode, Decode, RuntimeDebug, TypeInfo, MaxEncodedLen, )] - pub struct ReceiptRecord { + pub struct ReceiptRecord { /// The proportion of the effective total issuance. pub proportion: Perquintill, - /// The account to whom this receipt belongs. - pub who: AccountId, + /// The account to whom this receipt belongs and the amount of funds on hold in their + /// account for servicing this receipt. If `None`, then it is a communal receipt and + /// fungible counterparts have been issued. + pub owner: Option<(AccountId, Balance)>, /// The time after which this receipt can be thawed. pub expiry: BlockNumber, } @@ -344,7 +353,7 @@ pub mod pallet { #[derive( Clone, Eq, PartialEq, Default, Encode, Decode, RuntimeDebug, TypeInfo, MaxEncodedLen, )] - pub struct SummaryRecord { + pub struct SummaryRecord { /// The total proportion over all outstanding receipts. pub proportion_owed: Perquintill, /// The total number of receipts created so far. @@ -353,6 +362,9 @@ pub mod pallet { pub thawed: Perquintill, /// The current thaw period's beginning. pub last_period: BlockNumber, + /// The total amount of funds on hold for receipts. This doesn't include the pot or funds + /// on hold for bids. + pub receipts_on_hold: Balance, } pub struct OnEmptyQueueTotals(sp_std::marker::PhantomData); @@ -440,24 +452,28 @@ pub mod pallet { /// The queue for the bid's duration is full and the amount bid is too low to get in /// through replacing an existing bid. BidTooLow, - /// Bond index is unknown. - Unknown, + /// Receipt index is unknown. + UnknownReceipt, /// Not the owner of the receipt. NotOwner, /// Bond not yet at expiry date. NotExpired, /// The given bid for retraction is not found. - NotFound, + UnknownBid, /// The portion supplied is beyond the value of the receipt. - TooMuch, + PortionTooBig, /// Not enough funds are held to pay out. Unfunded, /// There are enough funds for what is required. - Funded, + AlreadyFunded, /// The thaw throttle has been reached for this period. Throttled, /// The operation would result in a receipt worth an insignficant value. MakesDust, + /// The receipt is already communal. + AlreadyCommunal, + /// The receipt is already private. + AlreadyPrivate, } pub(crate) struct WeightCounter { @@ -539,13 +555,17 @@ pub mod pallet { |q| -> Result<(u32, BalanceOf), DispatchError> { let queue_full = q.len() == T::MaxQueueLen::get() as usize; ensure!(!queue_full || q[0].amount < amount, Error::::BidTooLow); - T::Currency::reserve(&who, amount)?; + T::Currency::reserve_named(&T::ReserveId::get(), &who, amount)?; // queue is let mut bid = Bid { amount, who: who.clone() }; let net = if queue_full { sp_std::mem::swap(&mut q[0], &mut bid); - let _ = T::Currency::unreserve(&bid.who, bid.amount); + let _ = T::Currency::unreserve_named( + &T::ReserveId::get(), + &bid.who, + bid.amount, + ); Self::deposit_event(Event::::BidDropped { who: bid.who, amount: bid.amount, @@ -597,7 +617,7 @@ pub mod pallet { let bid = Bid { amount, who }; let new_len = Queues::::try_mutate(duration, |q| -> Result { - let pos = q.iter().position(|i| i == &bid).ok_or(Error::::NotFound)?; + let pos = q.iter().position(|i| i == &bid).ok_or(Error::::UnknownBid)?; q.remove(pos); Ok(q.len() as u32) })?; @@ -608,7 +628,7 @@ pub mod pallet { qs[queue_index].1.saturating_reduce(bid.amount); }); - T::Currency::unreserve(&bid.who, bid.amount); + T::Currency::unreserve_named(&T::ReserveId::get(), &bid.who, bid.amount); Self::deposit_event(Event::BidRetracted { who: bid.who, amount: bid.amount, duration }); Ok(()) @@ -625,7 +645,7 @@ pub mod pallet { let our_account = Self::account_id(); let issuance = Self::issuance_with(&our_account, &summary); let deficit = issuance.required.saturating_sub(issuance.holdings); - ensure!(!deficit.is_zero(), Error::::Funded); + ensure!(!deficit.is_zero(), Error::::AlreadyFunded); T::Deficit::on_unbalanced(T::Currency::deposit_creating(&our_account, deficit)); Self::deposit_event(Event::::Funded { deficit }); Ok(()) @@ -640,27 +660,28 @@ pub mod pallet { /// - `portion`: If `Some`, then only the given portion of the receipt should be thawed. If /// `None`, then all of it should be. #[pallet::call_index(3)] - #[pallet::weight(T::WeightInfo::thaw())] - pub fn thaw( + #[pallet::weight(T::WeightInfo::thaw_private())] + pub fn thaw_private( origin: OriginFor, #[pallet::compact] index: ReceiptIndex, - portion: Option<>::Balance>, + maybe_proportion: Option, ) -> DispatchResult { let who = ensure_signed(origin)?; // Look for `index` let mut receipt: ReceiptRecordOf = - Receipts::::get(index).ok_or(Error::::Unknown)?; + Receipts::::get(index).ok_or(Error::::UnknownReceipt)?; // If found, check the owner is `who`. - ensure!(receipt.who == who, Error::::NotOwner); + let (owner, mut on_hold) = receipt.owner.ok_or(Error::::AlreadyCommunal)?; + ensure!(owner == who, Error::::NotOwner); + let now = frame_system::Pallet::::block_number(); ensure!(now >= receipt.expiry, Error::::NotExpired); let mut summary: SummaryRecordOf = Summary::::get(); - let proportion = if let Some(counterpart) = portion { - let proportion = T::CounterpartAmount::convert_back(counterpart); - ensure!(proportion <= receipt.proportion, Error::::TooMuch); + let proportion = if let Some(proportion) = maybe_proportion { + ensure!(proportion <= receipt.proportion, Error::::PortionTooBig); let remaining = receipt.proportion.saturating_sub(proportion); ensure!( remaining.is_zero() || remaining >= T::MinReceipt::get(), @@ -679,8 +700,6 @@ pub mod pallet { summary.thawed.saturating_accrue(proportion); ensure!(summary.thawed <= throttle, Error::::Throttled); - T::Counterpart::burn_from(&who, T::CounterpartAmount::convert(proportion))?; - // Multiply the proportion it is by the total issued. let our_account = Self::account_id(); let effective_issuance = Self::issuance_with(&our_account, &summary).effective; @@ -689,13 +708,55 @@ pub mod pallet { receipt.proportion.saturating_reduce(proportion); summary.proportion_owed.saturating_reduce(proportion); - T::Currency::transfer(&our_account, &who, amount, AllowDeath) - .map_err(|_| Error::::Unfunded)?; - let dropped = receipt.proportion.is_zero(); + + if amount > on_hold { + T::Currency::unreserve_named(&T::ReserveId::get(), &who, on_hold); + let deficit = amount - on_hold; + // Try to transfer deficit from pot to receipt owner. + summary.receipts_on_hold.saturating_reduce(on_hold); + on_hold = Zero::zero(); + T::Currency::transfer(&our_account, &who, deficit, AllowDeath) + .map_err(|_| Error::::Unfunded)?; + } else { + T::Currency::unreserve_named(&T::ReserveId::get(), &who, amount); + on_hold.saturating_reduce(amount); + summary.receipts_on_hold.saturating_reduce(amount); + if dropped && !on_hold.is_zero() { + // Reclaim any remainder: + // Transfer `excess` to the pot if we have now fully compensated for the + // receipt. + // + // This will legitimately fail if there is no pot account in existance. + // There's nothing we can do about this so we just swallow the error. + // This code is not ideal and could fail in the second phase leaving + // the system in an invalid state. It can be fixed properly with the + // new API in https://github.com/paritytech/substrate/pull/12951 + // + // Below is what it should look like then: + // let _ = T::Currency::repatriate_reserved_named( + // &T::ReserveId::get(), + // &who, + // &our_account, + // excess, + // BalanceStatus::Free, + // ).defensive(); + T::Currency::unreserve_named(&T::ReserveId::get(), &who, on_hold); + // It could theoretically be locked, so really we should be using a more + // forceful variant. But the alternative `repatriate_reserved_named` will + // fail if the destination account doesn't exist. This should be fixed when + // we move to the `fungible::*` traits, which should include a force + // transfer function to transfer the reserved balance into free balance in + // the destination regardless of locks and create it if it doesn't exist. + let _ = T::Currency::transfer(&who, &Self::account_id(), on_hold, AllowDeath); + summary.receipts_on_hold.saturating_reduce(on_hold); + } + } + if dropped { Receipts::::remove(index); } else { + receipt.owner = Some((owner, on_hold)); Receipts::::insert(index, &receipt); } Summary::::put(&summary); @@ -704,20 +765,159 @@ pub mod pallet { Ok(()) } + + /// Reduce or remove an outstanding receipt, placing the according proportion of funds into + /// the account of the owner. + /// + /// - `origin`: Must be Signed and the account must be the owner of the fungible counterpart + /// for receipt `index`. + /// - `index`: The index of the receipt. + #[pallet::call_index(4)] + #[pallet::weight(T::WeightInfo::thaw_communal())] + pub fn thaw_communal( + origin: OriginFor, + #[pallet::compact] index: ReceiptIndex, + ) -> DispatchResult { + let who = ensure_signed(origin)?; + + // Look for `index` + let receipt: ReceiptRecordOf = + Receipts::::get(index).ok_or(Error::::UnknownReceipt)?; + // If found, check it is actually communal. + ensure!(receipt.owner.is_none(), Error::::NotOwner); + let now = frame_system::Pallet::::block_number(); + ensure!(now >= receipt.expiry, Error::::NotExpired); + + let mut summary: SummaryRecordOf = Summary::::get(); + + let (throttle, throttle_period) = T::ThawThrottle::get(); + if now.saturating_sub(summary.last_period) >= throttle_period { + summary.thawed = Zero::zero(); + summary.last_period = now; + } + summary.thawed.saturating_accrue(receipt.proportion); + ensure!(summary.thawed <= throttle, Error::::Throttled); + + T::Counterpart::burn_from(&who, T::CounterpartAmount::convert(receipt.proportion))?; + + // Multiply the proportion it is by the total issued. + let our_account = Self::account_id(); + let effective_issuance = Self::issuance_with(&our_account, &summary).effective; + let amount = receipt.proportion * effective_issuance; + + summary.proportion_owed.saturating_reduce(receipt.proportion); + + // Try to transfer amount owed from pot to receipt owner. + T::Currency::transfer(&our_account, &who, amount, AllowDeath) + .map_err(|_| Error::::Unfunded)?; + + Receipts::::remove(index); + Summary::::put(&summary); + + let e = + Event::Thawed { index, who, amount, proportion: receipt.proportion, dropped: true }; + Self::deposit_event(e); + + Ok(()) + } + + /// Make a private receipt communal and create fungible counterparts for its owner. + #[pallet::call_index(5)] + #[pallet::weight(T::WeightInfo::communify())] + pub fn communify( + origin: OriginFor, + #[pallet::compact] index: ReceiptIndex, + ) -> DispatchResult { + let who = ensure_signed(origin)?; + + // Look for `index` + let mut receipt: ReceiptRecordOf = + Receipts::::get(index).ok_or(Error::::UnknownReceipt)?; + + // Check it's not already communal and make it so. + let (owner, on_hold) = receipt.owner.take().ok_or(Error::::AlreadyCommunal)?; + + // If found, check the owner is `who`. + ensure!(owner == who, Error::::NotOwner); + + // Unreserve and transfer the funds to the pot. + T::Currency::unreserve_named(&T::ReserveId::get(), &who, on_hold); + // Transfer `excess` to the pot if we have now fully compensated for the receipt. + T::Currency::transfer(&who, &Self::account_id(), on_hold, AllowDeath) + .map_err(|_| Error::::Unfunded)?; + // TODO #12951: ^^^ The above should be done in a single operation `transfer_on_hold`. + + // Record that we've moved the amount reserved. + let mut summary: SummaryRecordOf = Summary::::get(); + summary.receipts_on_hold.saturating_reduce(on_hold); + Summary::::put(&summary); + Receipts::::insert(index, &receipt); + + // Mint fungibles. + let fung_eq = T::CounterpartAmount::convert(receipt.proportion); + let _ = T::Counterpart::mint_into(&who, fung_eq).defensive(); + + Ok(()) + } + + /// Make a communal receipt private and burn fungible counterparts from its owner. + #[pallet::call_index(6)] + #[pallet::weight(T::WeightInfo::privatize())] + pub fn privatize( + origin: OriginFor, + #[pallet::compact] index: ReceiptIndex, + ) -> DispatchResult { + let who = ensure_signed(origin)?; + + // Look for `index` + let mut receipt: ReceiptRecordOf = + Receipts::::get(index).ok_or(Error::::UnknownReceipt)?; + + // If found, check there is no owner. + ensure!(receipt.owner.is_none(), Error::::AlreadyPrivate); + + // Multiply the proportion it is by the total issued. + let mut summary: SummaryRecordOf = Summary::::get(); + let our_account = Self::account_id(); + let effective_issuance = Self::issuance_with(&our_account, &summary).effective; + let max_amount = receipt.proportion * effective_issuance; + // Avoid trying to place more in the account's reserve than we have available in the pot + let amount = max_amount.min(T::Currency::free_balance(&our_account)); + + // Burn fungible counterparts. + T::Counterpart::burn_from(&who, T::CounterpartAmount::convert(receipt.proportion))?; + + // Transfer the funds from the pot to the owner and reserve + T::Currency::transfer(&Self::account_id(), &who, amount, AllowDeath) + .map_err(|_| Error::::Unfunded)?; + T::Currency::reserve_named(&T::ReserveId::get(), &who, amount)?; + // TODO: ^^^ The above should be done in a single operation `transfer_and_hold`. + + // Record that we've moved the amount reserved. + summary.receipts_on_hold.saturating_accrue(amount); + + receipt.owner = Some((who, amount)); + + Summary::::put(&summary); + Receipts::::insert(index, &receipt); + + Ok(()) + } } /// Issuance information returned by `issuance()`. - #[derive(RuntimeDebug)] + #[derive(Debug)] pub struct IssuanceInfo { - /// The balance held in reserve by this pallet instance. + /// The balance held by this pallet instance together with the balances on hold across + /// all receipt-owning accounts. pub holdings: Balance, /// The (non-ignored) issuance in the system, not including this pallet's account. pub other: Balance, /// The effective total issuance, hypothetically if all outstanding receipts were thawed at /// present. pub effective: Balance, - /// The amount needed to be the pallet instance's account in case all outstanding receipts - /// were thawed at present. + /// The amount needed to be accessible to this pallet in case all outstanding receipts were + /// thawed at present. If it is more than `holdings`, then the pallet will need funding. pub required: Balance, } @@ -725,7 +925,7 @@ pub mod pallet { type ItemId = ReceiptIndex; fn owner(item: &ReceiptIndex) -> Option { - Receipts::::get(item).map(|r| r.who) + Receipts::::get(item).and_then(|r| r.owner).map(|(who, _)| who) } fn attribute(item: &Self::ItemId, key: &[u8]) -> Option> { @@ -733,6 +933,8 @@ pub mod pallet { match key { b"proportion" => Some(item.proportion.encode()), b"expiry" => Some(item.expiry.encode()), + b"owner" => item.owner.as_ref().map(|x| x.0.encode()), + b"on_hold" => item.owner.as_ref().map(|x| x.1.encode()), _ => None, } } @@ -741,12 +943,28 @@ pub mod pallet { impl NonfungibleTransfer for Pallet { fn transfer(index: &ReceiptIndex, destination: &T::AccountId) -> DispatchResult { let mut item = Receipts::::get(index).ok_or(TokenError::UnknownAsset)?; - let from = item.who; - item.who = destination.clone(); + let (owner, on_hold) = item.owner.take().ok_or(Error::::AlreadyCommunal)?; + + // TODO: This should all be replaced by a single call `transfer_held`. + let shortfall = T::Currency::unreserve_named(&T::ReserveId::get(), &owner, on_hold); + if !shortfall.is_zero() { + let _ = + T::Currency::reserve_named(&T::ReserveId::get(), &owner, on_hold - shortfall); + return Err(TokenError::NoFunds.into()) + } + if let Err(e) = T::Currency::transfer(&owner, destination, on_hold, AllowDeath) { + let _ = T::Currency::reserve_named(&T::ReserveId::get(), &owner, on_hold); + return Err(e) + } + // This can never fail, and if it somehow does, then we can't handle this gracefully. + let _ = + T::Currency::reserve_named(&T::ReserveId::get(), destination, on_hold).defensive(); + + item.owner = Some((destination.clone(), on_hold)); Receipts::::insert(&index, &item); Pallet::::deposit_event(Event::::Transferred { - from, - to: item.who, + from: owner, + to: destination.clone(), index: *index, }); Ok(()) @@ -781,7 +999,8 @@ pub mod pallet { ) -> IssuanceInfo> { let total_issuance = T::Currency::total_issuance().saturating_sub(T::IgnoredIssuance::get()); - let holdings = T::Currency::free_balance(our_account); + let holdings = + T::Currency::free_balance(our_account).saturating_add(summary.receipts_on_hold); let other = total_issuance.saturating_sub(holdings); let effective = summary.proportion_owed.left_from_one().saturating_reciprocal_mul(other); @@ -893,7 +1112,7 @@ pub mod pallet { pub(crate) fn process_bid( mut bid: BidOf, expiry: T::BlockNumber, - our_account: &T::AccountId, + _our_account: &T::AccountId, issuance: &IssuanceInfo>, remaining: &mut BalanceOf, queue_amount: &mut BalanceOf, @@ -906,10 +1125,8 @@ pub mod pallet { } else { None }; - let amount = bid.amount.saturating_sub(T::Currency::unreserve(&bid.who, bid.amount)); - if T::Currency::transfer(&bid.who, &our_account, amount, AllowDeath).is_err() { - return result - } + let amount = bid.amount; + summary.receipts_on_hold.saturating_accrue(amount); // Can never overflow due to block above. remaining.saturating_reduce(amount); @@ -928,12 +1145,9 @@ pub mod pallet { let e = Event::Issued { index, expiry, who: who.clone(), amount, proportion }; Self::deposit_event(e); - let receipt = ReceiptRecord { proportion, who: who.clone(), expiry }; + let receipt = ReceiptRecord { proportion, owner: Some((who, amount)), expiry }; Receipts::::insert(index, receipt); - // issue the fungible counterpart - let fung_eq = T::CounterpartAmount::convert(proportion); - let _ = T::Counterpart::mint_into(&who, fung_eq).defensive(); result } } diff --git a/frame/nis/src/mock.rs b/frame/nis/src/mock.rs index ebe073d68..585ed2d8b 100644 --- a/frame/nis/src/mock.rs +++ b/frame/nis/src/mock.rs @@ -84,7 +84,7 @@ impl pallet_balances::Config for Test { type AccountStore = System; type WeightInfo = (); type MaxLocks = (); - type MaxReserves = (); + type MaxReserves = ConstU32<1>; type ReserveIdentifier = [u8; 8]; } @@ -112,6 +112,7 @@ parameter_types! { pub const MinReceipt: Perquintill = Perquintill::from_percent(1); pub const ThawThrottle: (Perquintill, u64) = (Perquintill::from_percent(25), 5); pub static MaxIntakeWeight: Weight = Weight::from_ref_time(2_000_000_000_000); + pub const ReserveId: [u8; 8] = *b"py/nis "; } ord_parameter_types! { @@ -139,6 +140,7 @@ impl pallet_nis::Config for Test { type MaxIntakeWeight = MaxIntakeWeight; type MinReceipt = MinReceipt; type ThawThrottle = ThawThrottle; + type ReserveId = ReserveId; } // This function basically just builds a genesis storage key/value store according to diff --git a/frame/nis/src/tests.rs b/frame/nis/src/tests.rs index f0c45cc80..b5feb4df1 100644 --- a/frame/nis/src/tests.rs +++ b/frame/nis/src/tests.rs @@ -34,8 +34,16 @@ fn pot() -> u64 { Balances::free_balance(&Nis::account_id()) } +fn holdings() -> u64 { + Nis::issuance().holdings +} + +fn signed(who: u64) -> RuntimeOrigin { + RuntimeOrigin::signed(who) +} + fn enlarge(amount: u64, max_bids: u32) { - let summary: SummaryRecord = Summary::::get(); + let summary: SummaryRecord = Summary::::get(); let increase_in_proportion_owed = Perquintill::from_rational(amount, Nis::issuance().effective); let target = summary.proportion_owed.saturating_add(increase_in_proportion_owed); Nis::process_queues(target, u32::max_value(), max_bids, &mut WeightCounter::unlimited()); @@ -55,7 +63,8 @@ fn basic_setup_works() { proportion_owed: Perquintill::zero(), index: 0, last_period: 0, - thawed: Perquintill::zero() + thawed: Perquintill::zero(), + receipts_on_hold: 0, } ); }); @@ -65,16 +74,13 @@ fn basic_setup_works() { fn place_bid_works() { new_test_ext().execute_with(|| { run_to_block(1); - assert_noop!(Nis::place_bid(RuntimeOrigin::signed(1), 1, 2), Error::::AmountTooSmall); + assert_noop!(Nis::place_bid(signed(1), 1, 2), Error::::AmountTooSmall); assert_noop!( - Nis::place_bid(RuntimeOrigin::signed(1), 101, 2), + Nis::place_bid(signed(1), 101, 2), BalancesError::::InsufficientBalance ); - assert_noop!( - Nis::place_bid(RuntimeOrigin::signed(1), 10, 4), - Error::::DurationTooBig - ); - assert_ok!(Nis::place_bid(RuntimeOrigin::signed(1), 10, 2)); + assert_noop!(Nis::place_bid(signed(1), 10, 4), Error::::DurationTooBig); + assert_ok!(Nis::place_bid(signed(1), 10, 2)); assert_eq!(Balances::reserved_balance(1), 10); assert_eq!(Queues::::get(2), vec![Bid { amount: 10, who: 1 }]); assert_eq!(QueueTotals::::get(), vec![(0, 0), (1, 10), (0, 0)]); @@ -85,16 +91,16 @@ fn place_bid_works() { fn place_bid_queuing_works() { new_test_ext().execute_with(|| { run_to_block(1); - assert_ok!(Nis::place_bid(RuntimeOrigin::signed(1), 20, 2)); - assert_ok!(Nis::place_bid(RuntimeOrigin::signed(1), 10, 2)); - assert_ok!(Nis::place_bid(RuntimeOrigin::signed(1), 5, 2)); - assert_noop!(Nis::place_bid(RuntimeOrigin::signed(1), 5, 2), Error::::BidTooLow); - assert_ok!(Nis::place_bid(RuntimeOrigin::signed(1), 15, 2)); + assert_ok!(Nis::place_bid(signed(1), 20, 2)); + assert_ok!(Nis::place_bid(signed(1), 10, 2)); + assert_ok!(Nis::place_bid(signed(1), 5, 2)); + assert_noop!(Nis::place_bid(signed(1), 5, 2), Error::::BidTooLow); + assert_ok!(Nis::place_bid(signed(1), 15, 2)); assert_eq!(Balances::reserved_balance(1), 45); - assert_ok!(Nis::place_bid(RuntimeOrigin::signed(1), 25, 2)); + assert_ok!(Nis::place_bid(signed(1), 25, 2)); assert_eq!(Balances::reserved_balance(1), 60); - assert_noop!(Nis::place_bid(RuntimeOrigin::signed(1), 10, 2), Error::::BidTooLow); + assert_noop!(Nis::place_bid(signed(1), 10, 2), Error::::BidTooLow); assert_eq!( Queues::::get(2), vec![ @@ -111,11 +117,11 @@ fn place_bid_queuing_works() { fn place_bid_fails_when_queue_full() { new_test_ext().execute_with(|| { run_to_block(1); - assert_ok!(Nis::place_bid(RuntimeOrigin::signed(1), 10, 2)); - assert_ok!(Nis::place_bid(RuntimeOrigin::signed(2), 10, 2)); - assert_ok!(Nis::place_bid(RuntimeOrigin::signed(3), 10, 2)); - assert_noop!(Nis::place_bid(RuntimeOrigin::signed(4), 10, 2), Error::::BidTooLow); - assert_ok!(Nis::place_bid(RuntimeOrigin::signed(4), 10, 3)); + assert_ok!(Nis::place_bid(signed(1), 10, 2)); + assert_ok!(Nis::place_bid(signed(2), 10, 2)); + assert_ok!(Nis::place_bid(signed(3), 10, 2)); + assert_noop!(Nis::place_bid(signed(4), 10, 2), Error::::BidTooLow); + assert_ok!(Nis::place_bid(signed(4), 10, 3)); }); } @@ -123,11 +129,11 @@ fn place_bid_fails_when_queue_full() { fn multiple_place_bids_works() { new_test_ext().execute_with(|| { run_to_block(1); - assert_ok!(Nis::place_bid(RuntimeOrigin::signed(1), 10, 1)); - assert_ok!(Nis::place_bid(RuntimeOrigin::signed(1), 10, 2)); - assert_ok!(Nis::place_bid(RuntimeOrigin::signed(1), 10, 2)); - assert_ok!(Nis::place_bid(RuntimeOrigin::signed(1), 10, 3)); - assert_ok!(Nis::place_bid(RuntimeOrigin::signed(2), 10, 2)); + assert_ok!(Nis::place_bid(signed(1), 10, 1)); + assert_ok!(Nis::place_bid(signed(1), 10, 2)); + assert_ok!(Nis::place_bid(signed(1), 10, 2)); + assert_ok!(Nis::place_bid(signed(1), 10, 3)); + assert_ok!(Nis::place_bid(signed(2), 10, 2)); assert_eq!(Balances::reserved_balance(1), 40); assert_eq!(Balances::reserved_balance(2), 10); @@ -149,9 +155,9 @@ fn multiple_place_bids_works() { fn retract_single_item_queue_works() { new_test_ext().execute_with(|| { run_to_block(1); - assert_ok!(Nis::place_bid(RuntimeOrigin::signed(1), 10, 1)); - assert_ok!(Nis::place_bid(RuntimeOrigin::signed(1), 10, 2)); - assert_ok!(Nis::retract_bid(RuntimeOrigin::signed(1), 10, 1)); + assert_ok!(Nis::place_bid(signed(1), 10, 1)); + assert_ok!(Nis::place_bid(signed(1), 10, 2)); + assert_ok!(Nis::retract_bid(signed(1), 10, 1)); assert_eq!(Balances::reserved_balance(1), 10); assert_eq!(Queues::::get(1), vec![]); @@ -164,12 +170,12 @@ fn retract_single_item_queue_works() { fn retract_with_other_and_duplicate_works() { new_test_ext().execute_with(|| { run_to_block(1); - assert_ok!(Nis::place_bid(RuntimeOrigin::signed(1), 10, 1)); - assert_ok!(Nis::place_bid(RuntimeOrigin::signed(1), 10, 2)); - assert_ok!(Nis::place_bid(RuntimeOrigin::signed(1), 10, 2)); - assert_ok!(Nis::place_bid(RuntimeOrigin::signed(2), 10, 2)); + assert_ok!(Nis::place_bid(signed(1), 10, 1)); + assert_ok!(Nis::place_bid(signed(1), 10, 2)); + assert_ok!(Nis::place_bid(signed(1), 10, 2)); + assert_ok!(Nis::place_bid(signed(2), 10, 2)); - assert_ok!(Nis::retract_bid(RuntimeOrigin::signed(1), 10, 2)); + assert_ok!(Nis::retract_bid(signed(1), 10, 2)); assert_eq!(Balances::reserved_balance(1), 20); assert_eq!(Balances::reserved_balance(2), 10); assert_eq!(Queues::::get(1), vec![Bid { amount: 10, who: 1 },]); @@ -185,11 +191,11 @@ fn retract_with_other_and_duplicate_works() { fn retract_non_existent_item_fails() { new_test_ext().execute_with(|| { run_to_block(1); - assert_noop!(Nis::retract_bid(RuntimeOrigin::signed(1), 10, 1), Error::::NotFound); - assert_ok!(Nis::place_bid(RuntimeOrigin::signed(1), 10, 1)); - assert_noop!(Nis::retract_bid(RuntimeOrigin::signed(1), 20, 1), Error::::NotFound); - assert_noop!(Nis::retract_bid(RuntimeOrigin::signed(1), 10, 2), Error::::NotFound); - assert_noop!(Nis::retract_bid(RuntimeOrigin::signed(2), 10, 1), Error::::NotFound); + assert_noop!(Nis::retract_bid(signed(1), 10, 1), Error::::UnknownBid); + assert_ok!(Nis::place_bid(signed(1), 10, 1)); + assert_noop!(Nis::retract_bid(signed(1), 20, 1), Error::::UnknownBid); + assert_noop!(Nis::retract_bid(signed(1), 10, 2), Error::::UnknownBid); + assert_noop!(Nis::retract_bid(signed(2), 10, 1), Error::::UnknownBid); }); } @@ -197,14 +203,14 @@ fn retract_non_existent_item_fails() { fn basic_enlarge_works() { new_test_ext().execute_with(|| { run_to_block(1); - assert_ok!(Nis::place_bid(RuntimeOrigin::signed(1), 40, 1)); - assert_ok!(Nis::place_bid(RuntimeOrigin::signed(2), 40, 2)); + assert_ok!(Nis::place_bid(signed(1), 40, 1)); + assert_ok!(Nis::place_bid(signed(2), 40, 2)); enlarge(40, 2); // Takes 2/2, then stopped because it reaches its max amount assert_eq!(Balances::reserved_balance(1), 40); - assert_eq!(Balances::reserved_balance(2), 0); - assert_eq!(pot(), 40); + assert_eq!(Balances::reserved_balance(2), 40); + assert_eq!(holdings(), 40); assert_eq!(Queues::::get(1), vec![Bid { amount: 40, who: 1 }]); assert_eq!(Queues::::get(2), vec![]); @@ -216,12 +222,17 @@ fn basic_enlarge_works() { proportion_owed: Perquintill::from_percent(10), index: 1, last_period: 0, - thawed: Perquintill::zero() + thawed: Perquintill::zero(), + receipts_on_hold: 40, } ); assert_eq!( Receipts::::get(0).unwrap(), - ReceiptRecord { proportion: Perquintill::from_percent(10), who: 2, expiry: 7 } + ReceiptRecord { + proportion: Perquintill::from_percent(10), + owner: Some((2, 40)), + expiry: 7 + } ); }); } @@ -230,10 +241,10 @@ fn basic_enlarge_works() { fn enlarge_respects_bids_limit() { new_test_ext().execute_with(|| { run_to_block(1); - assert_ok!(Nis::place_bid(RuntimeOrigin::signed(1), 40, 1)); - assert_ok!(Nis::place_bid(RuntimeOrigin::signed(2), 40, 2)); - assert_ok!(Nis::place_bid(RuntimeOrigin::signed(3), 40, 2)); - assert_ok!(Nis::place_bid(RuntimeOrigin::signed(4), 40, 3)); + assert_ok!(Nis::place_bid(signed(1), 40, 1)); + assert_ok!(Nis::place_bid(signed(2), 40, 2)); + assert_ok!(Nis::place_bid(signed(3), 40, 2)); + assert_ok!(Nis::place_bid(signed(4), 40, 3)); enlarge(100, 2); // Should have taken 4/3 and 2/2, then stopped because it's only allowed 2. @@ -244,11 +255,19 @@ fn enlarge_respects_bids_limit() { assert_eq!( Receipts::::get(0).unwrap(), - ReceiptRecord { proportion: Perquintill::from_percent(10), who: 4, expiry: 10 } + ReceiptRecord { + proportion: Perquintill::from_percent(10), + owner: Some((4, 40)), + expiry: 10 + } ); assert_eq!( Receipts::::get(1).unwrap(), - ReceiptRecord { proportion: Perquintill::from_percent(10), who: 2, expiry: 7 } + ReceiptRecord { + proportion: Perquintill::from_percent(10), + owner: Some((2, 40)), + expiry: 7 + } ); assert_eq!( Summary::::get(), @@ -256,7 +275,8 @@ fn enlarge_respects_bids_limit() { proportion_owed: Perquintill::from_percent(20), index: 2, last_period: 0, - thawed: Perquintill::zero() + thawed: Perquintill::zero(), + receipts_on_hold: 80, } ); }); @@ -266,7 +286,7 @@ fn enlarge_respects_bids_limit() { fn enlarge_respects_amount_limit_and_will_split() { new_test_ext().execute_with(|| { run_to_block(1); - assert_ok!(Nis::place_bid(RuntimeOrigin::signed(1), 80, 1)); + assert_ok!(Nis::place_bid(signed(1), 80, 1)); enlarge(40, 2); // Takes 2/2, then stopped because it reaches its max amount @@ -275,7 +295,11 @@ fn enlarge_respects_amount_limit_and_will_split() { assert_eq!( Receipts::::get(0).unwrap(), - ReceiptRecord { proportion: Perquintill::from_percent(10), who: 1, expiry: 4 } + ReceiptRecord { + proportion: Perquintill::from_percent(10), + owner: Some((1, 40)), + expiry: 4 + } ); assert_eq!( Summary::::get(), @@ -283,7 +307,8 @@ fn enlarge_respects_amount_limit_and_will_split() { proportion_owed: Perquintill::from_percent(10), index: 1, last_period: 0, - thawed: Perquintill::zero() + thawed: Perquintill::zero(), + receipts_on_hold: 40, } ); }); @@ -293,25 +318,25 @@ fn enlarge_respects_amount_limit_and_will_split() { fn basic_thaw_works() { new_test_ext().execute_with(|| { run_to_block(1); - assert_ok!(Nis::place_bid(RuntimeOrigin::signed(1), 40, 1)); + assert_ok!(Nis::place_bid(signed(1), 40, 1)); assert_eq!(Nis::issuance().effective, 400); assert_eq!(Balances::free_balance(1), 60); assert_eq!(Balances::reserved_balance(1), 40); - assert_eq!(pot(), 0); + assert_eq!(holdings(), 0); enlarge(40, 1); assert_eq!(Nis::issuance().effective, 400); assert_eq!(Balances::free_balance(1), 60); - assert_eq!(Balances::reserved_balance(1), 0); - assert_eq!(pot(), 40); + assert_eq!(Balances::reserved_balance(1), 40); + assert_eq!(holdings(), 40); run_to_block(3); - assert_noop!(Nis::thaw(RuntimeOrigin::signed(1), 0, None), Error::::NotExpired); + assert_noop!(Nis::thaw_private(signed(1), 0, None), Error::::NotExpired); run_to_block(4); - assert_noop!(Nis::thaw(RuntimeOrigin::signed(1), 1, None), Error::::Unknown); - assert_noop!(Nis::thaw(RuntimeOrigin::signed(2), 0, None), Error::::NotOwner); + assert_noop!(Nis::thaw_private(signed(1), 1, None), Error::::UnknownReceipt); + assert_noop!(Nis::thaw_private(signed(2), 0, None), Error::::NotOwner); - assert_ok!(Nis::thaw(RuntimeOrigin::signed(1), 0, None)); + assert_ok!(Nis::thaw_private(signed(1), 0, None)); assert_eq!(NisBalances::free_balance(1), 0); assert_eq!(Nis::typed_attribute::<_, Perquintill>(&0, b"proportion"), None); assert_eq!(Nis::issuance().effective, 400); @@ -323,7 +348,8 @@ fn basic_thaw_works() { proportion_owed: Perquintill::zero(), index: 1, last_period: 0, - thawed: Perquintill::from_percent(10) + thawed: Perquintill::from_percent(10), + receipts_on_hold: 0, } ); assert_eq!(Receipts::::get(0), None); @@ -334,18 +360,16 @@ fn basic_thaw_works() { fn partial_thaw_works() { new_test_ext().execute_with(|| { run_to_block(1); - assert_ok!(Nis::place_bid(RuntimeOrigin::signed(1), 80, 1)); + assert_ok!(Nis::place_bid(signed(1), 80, 1)); enlarge(80, 1); - assert_eq!(pot(), 80); + assert_eq!(holdings(), 80); run_to_block(4); - assert_noop!( - Nis::thaw(RuntimeOrigin::signed(1), 0, Some(4_100_000)), - Error::::MakesDust - ); - assert_ok!(Nis::thaw(RuntimeOrigin::signed(1), 0, Some(1_050_000))); + let prop = Perquintill::from_rational(4_100_000, 21_000_000u64); + assert_noop!(Nis::thaw_private(signed(1), 0, Some(prop)), Error::::MakesDust); + let prop = Perquintill::from_rational(1_050_000, 21_000_000u64); + assert_ok!(Nis::thaw_private(signed(1), 0, Some(prop))); - assert_eq!(NisBalances::free_balance(1), 3_150_000); assert_eq!( Nis::typed_attribute::<_, Perquintill>(&0, b"proportion"), Some(Perquintill::from_rational(3_150_000u64, 21_000_000u64)), @@ -353,9 +377,9 @@ fn partial_thaw_works() { assert_eq!(Nis::issuance().effective, 400); assert_eq!(Balances::free_balance(1), 40); - assert_eq!(pot(), 60); + assert_eq!(holdings(), 60); - assert_ok!(Nis::thaw(RuntimeOrigin::signed(1), 0, None)); + assert_ok!(Nis::thaw_private(signed(1), 0, None)); assert_eq!(Nis::issuance().effective, 400); assert_eq!(Balances::free_balance(1), 100); @@ -367,7 +391,8 @@ fn partial_thaw_works() { proportion_owed: Perquintill::zero(), index: 1, last_period: 0, - thawed: Perquintill::from_percent(20) + thawed: Perquintill::from_percent(20), + receipts_on_hold: 0, } ); assert_eq!(Receipts::::get(0), None); @@ -378,35 +403,144 @@ fn partial_thaw_works() { fn thaw_respects_transfers() { new_test_ext().execute_with(|| { run_to_block(1); - assert_ok!(Nis::place_bid(RuntimeOrigin::signed(1), 40, 1)); + assert_ok!(Nis::place_bid(signed(1), 40, 1)); enlarge(40, 1); run_to_block(4); assert_eq!(Nis::owner(&0), Some(1)); + assert_eq!(Balances::reserved_balance(&1), 40); + assert_eq!(Balances::reserved_balance(&2), 0); assert_ok!(Nis::transfer(&0, &2)); + assert_eq!(Balances::reserved_balance(&1), 0); + assert_eq!(Balances::reserved_balance(&2), 40); // Transfering the receipt... - assert_noop!(Nis::thaw(RuntimeOrigin::signed(1), 0, None), Error::::NotOwner); - // ...can't be thawed due to missing counterpart - assert_noop!(Nis::thaw(RuntimeOrigin::signed(2), 0, None), TokenError::NoFunds); + assert_noop!(Nis::thaw_private(signed(1), 0, None), Error::::NotOwner); - // Transfer the counterpart also... - assert_ok!(NisBalances::transfer(RuntimeOrigin::signed(1), 2, 2100000)); // ...and thawing is possible. - assert_ok!(Nis::thaw(RuntimeOrigin::signed(2), 0, None)); + assert_ok!(Nis::thaw_private(signed(2), 0, None)); - assert_eq!(Balances::free_balance(2), 140); - assert_eq!(Balances::free_balance(1), 60); + assert_eq!(Balances::total_balance(&2), 140); + assert_eq!(Balances::total_balance(&1), 60); + }); +} + +#[test] +fn communify_works() { + new_test_ext().execute_with(|| { + run_to_block(1); + assert_ok!(Nis::place_bid(signed(1), 40, 1)); + enlarge(40, 1); + run_to_block(4); + + assert_eq!(Nis::owner(&0), Some(1)); + assert_eq!(Balances::reserved_balance(&1), 40); + assert_eq!(pot(), 0); + assert_eq!(NisBalances::free_balance(&1), 0); + assert_ok!(Nis::communify(signed(1), 0)); + assert_eq!(Nis::owner(&0), None); + assert_eq!(Balances::reserved_balance(&1), 0); + assert_eq!(pot(), 40); + // We now have fungibles. + assert_eq!(NisBalances::free_balance(&1), 2_100_000); + + // Can't transfer the NFT or thaw it as a private. + assert_noop!(Nis::thaw_private(signed(1), 0, None), Error::::AlreadyCommunal); + assert_noop!(Nis::transfer(&0, &2), Error::::AlreadyCommunal); + // Communal thawing would be possible, except it's the wrong receipt. + assert_noop!(Nis::thaw_communal(signed(1), 1), Error::::UnknownReceipt); + + // Transfer some of the fungibles away. + assert_ok!(NisBalances::transfer(signed(1), 2, 100_000)); + assert_eq!(NisBalances::free_balance(&1), 2_000_000); + assert_eq!(NisBalances::free_balance(&2), 100_000); + + // Communal thawing with the correct index is not possible now. + assert_noop!(Nis::thaw_communal(signed(1), 0), TokenError::NoFunds); + assert_noop!(Nis::thaw_communal(signed(2), 0), TokenError::NoFunds); + + // Transfer the rest to 2... + assert_ok!(NisBalances::transfer(signed(1), 2, 2_000_000)); + assert_eq!(NisBalances::free_balance(&1), 0); + assert_eq!(NisBalances::free_balance(&2), 2_100_000); + + // ...and thawing becomes possible. + assert_ok!(Nis::thaw_communal(signed(2), 0)); + assert_eq!(NisBalances::free_balance(&1), 0); + assert_eq!(NisBalances::free_balance(&2), 0); + assert_eq!(pot(), 0); + assert_eq!(Balances::total_balance(&1), 60); + assert_eq!(Balances::total_balance(&2), 140); + + assert_noop!(Nis::thaw_communal(signed(2), 0), Error::::UnknownReceipt); + }); +} + +#[test] +fn privatize_works() { + new_test_ext().execute_with(|| { + run_to_block(1); + assert_ok!(Nis::place_bid(signed(1), 40, 1)); + enlarge(40, 1); + run_to_block(4); + assert_noop!(Nis::privatize(signed(2), 0), Error::::AlreadyPrivate); + assert_ok!(Nis::communify(signed(1), 0)); + + // Transfer the fungibles to #2 + assert_ok!(NisBalances::transfer(signed(1), 2, 2_100_000)); + assert_noop!(Nis::privatize(signed(1), 0), TokenError::NoFunds); + + // Privatize + assert_ok!(Nis::privatize(signed(2), 0)); + assert_noop!(Nis::privatize(signed(2), 0), Error::::AlreadyPrivate); + assert_eq!(NisBalances::free_balance(&2), 0); + assert_eq!(Nis::owner(&0), Some(2)); + assert_eq!(Balances::reserved_balance(&2), 40); + assert_eq!(pot(), 0); }); } #[test] -fn thaw_when_issuance_higher_works() { +fn privatize_and_thaw_with_another_receipt_works() { new_test_ext().execute_with(|| { run_to_block(1); - assert_ok!(Nis::place_bid(RuntimeOrigin::signed(1), 100, 1)); + assert_ok!(Nis::place_bid(signed(1), 40, 1)); + assert_ok!(Nis::place_bid(signed(2), 40, 1)); + enlarge(80, 2); + run_to_block(4); + + assert_ok!(Nis::communify(signed(1), 0)); + assert_ok!(Nis::communify(signed(2), 1)); + + // Transfer half of fungibles to #3 from each of #1 and #2, and the other half from #2 to #4 + assert_ok!(NisBalances::transfer(signed(1), 3, 1_050_000)); + assert_ok!(NisBalances::transfer(signed(2), 3, 1_050_000)); + assert_ok!(NisBalances::transfer(signed(2), 4, 1_050_000)); + + // #3 privatizes, partially thaws, then re-communifies with #0, then transfers the fungibles + // to #2 + assert_ok!(Nis::privatize(signed(3), 0)); + assert_ok!(Nis::thaw_private(signed(3), 0, Some(Perquintill::from_percent(5)))); + assert_ok!(Nis::communify(signed(3), 0)); + assert_ok!(NisBalances::transfer(signed(3), 1, 1_050_000)); + + // #1 now has enough to thaw using receipt 1 + assert_ok!(Nis::thaw_communal(signed(1), 1)); + + // #4 now has enough to thaw using receipt 0 + assert_ok!(Nis::thaw_communal(signed(4), 0)); + }); +} + +#[test] +fn communal_thaw_when_issuance_higher_works() { + new_test_ext().execute_with(|| { + run_to_block(1); + assert_ok!(Nis::place_bid(signed(1), 100, 1)); enlarge(100, 1); + assert_ok!(Nis::communify(signed(1), 0)); + assert_eq!(NisBalances::free_balance(1), 5_250_000); // (25% of 21m) // Everybody else's balances goes up by 50% @@ -417,19 +551,45 @@ fn thaw_when_issuance_higher_works() { run_to_block(4); // Unfunded initially... - assert_noop!(Nis::thaw(RuntimeOrigin::signed(1), 0, None), Error::::Unfunded); + assert_noop!(Nis::thaw_communal(signed(1), 0), Error::::Unfunded); // ...so we fund. - assert_ok!(Nis::fund_deficit(RuntimeOrigin::signed(1))); + assert_ok!(Nis::fund_deficit(signed(1))); - // Transfer counterpart away... - assert_ok!(NisBalances::transfer(RuntimeOrigin::signed(1), 2, 250_000)); + // Transfer counterparts away... + assert_ok!(NisBalances::transfer(signed(1), 2, 250_000)); // ...and it's not thawable. - assert_noop!(Nis::thaw(RuntimeOrigin::signed(1), 0, None), TokenError::NoFunds); + assert_noop!(Nis::thaw_communal(signed(1), 0), TokenError::NoFunds); - // Transfer counterpart back... - assert_ok!(NisBalances::transfer(RuntimeOrigin::signed(2), 1, 250_000)); + // Transfer counterparts back... + assert_ok!(NisBalances::transfer(signed(2), 1, 250_000)); // ...and it is. - assert_ok!(Nis::thaw(RuntimeOrigin::signed(1), 0, None)); + assert_ok!(Nis::thaw_communal(signed(1), 0)); + + assert_eq!(Balances::free_balance(1), 150); + assert_eq!(Balances::reserved_balance(1), 0); + }); +} + +#[test] +fn private_thaw_when_issuance_higher_works() { + new_test_ext().execute_with(|| { + run_to_block(1); + assert_ok!(Nis::place_bid(signed(1), 100, 1)); + enlarge(100, 1); + + // Everybody else's balances goes up by 50% + Balances::make_free_balance_be(&2, 150); + Balances::make_free_balance_be(&3, 150); + Balances::make_free_balance_be(&4, 150); + + run_to_block(4); + + // Unfunded initially... + assert_noop!(Nis::thaw_private(signed(1), 0, None), Error::::Unfunded); + // ...so we fund. + assert_ok!(Nis::fund_deficit(signed(1))); + + assert_ok!(Nis::thaw_private(signed(1), 0, None)); assert_eq!(Balances::free_balance(1), 150); assert_eq!(Balances::reserved_balance(1), 0); @@ -443,21 +603,21 @@ fn thaw_with_ignored_issuance_works() { // Give account zero some balance. Balances::make_free_balance_be(&0, 200); - assert_ok!(Nis::place_bid(RuntimeOrigin::signed(1), 100, 1)); + assert_ok!(Nis::place_bid(signed(1), 100, 1)); enlarge(100, 1); // Account zero transfers 50 into everyone else's accounts. - assert_ok!(Balances::transfer(RuntimeOrigin::signed(0), 2, 50)); - assert_ok!(Balances::transfer(RuntimeOrigin::signed(0), 3, 50)); - assert_ok!(Balances::transfer(RuntimeOrigin::signed(0), 4, 50)); + assert_ok!(Balances::transfer(signed(0), 2, 50)); + assert_ok!(Balances::transfer(signed(0), 3, 50)); + assert_ok!(Balances::transfer(signed(0), 4, 50)); run_to_block(4); // Unfunded initially... - assert_noop!(Nis::thaw(RuntimeOrigin::signed(1), 0, None), Error::::Unfunded); + assert_noop!(Nis::thaw_private(signed(1), 0, None), Error::::Unfunded); // ...so we fund... - assert_ok!(Nis::fund_deficit(RuntimeOrigin::signed(1))); + assert_ok!(Nis::fund_deficit(signed(1))); // ...and then it's ok. - assert_ok!(Nis::thaw(RuntimeOrigin::signed(1), 0, None)); + assert_ok!(Nis::thaw_private(signed(1), 0, None)); // Account zero changes have been ignored. assert_eq!(Balances::free_balance(1), 150); @@ -469,7 +629,7 @@ fn thaw_with_ignored_issuance_works() { fn thaw_when_issuance_lower_works() { new_test_ext().execute_with(|| { run_to_block(1); - assert_ok!(Nis::place_bid(RuntimeOrigin::signed(1), 100, 1)); + assert_ok!(Nis::place_bid(signed(1), 100, 1)); enlarge(100, 1); // Everybody else's balances goes down by 25% @@ -478,7 +638,7 @@ fn thaw_when_issuance_lower_works() { Balances::make_free_balance_be(&4, 75); run_to_block(4); - assert_ok!(Nis::thaw(RuntimeOrigin::signed(1), 0, None)); + assert_ok!(Nis::thaw_private(signed(1), 0, None)); assert_eq!(Balances::free_balance(1), 75); assert_eq!(Balances::reserved_balance(1), 0); @@ -489,23 +649,23 @@ fn thaw_when_issuance_lower_works() { fn multiple_thaws_works() { new_test_ext().execute_with(|| { run_to_block(1); - assert_ok!(Nis::place_bid(RuntimeOrigin::signed(1), 40, 1)); - assert_ok!(Nis::place_bid(RuntimeOrigin::signed(1), 60, 1)); - assert_ok!(Nis::place_bid(RuntimeOrigin::signed(2), 50, 1)); + assert_ok!(Nis::place_bid(signed(1), 40, 1)); + assert_ok!(Nis::place_bid(signed(1), 60, 1)); + assert_ok!(Nis::place_bid(signed(2), 50, 1)); enlarge(200, 3); // Double everyone's free balances. Balances::make_free_balance_be(&2, 100); Balances::make_free_balance_be(&3, 200); Balances::make_free_balance_be(&4, 200); - assert_ok!(Nis::fund_deficit(RuntimeOrigin::signed(1))); + assert_ok!(Nis::fund_deficit(signed(1))); run_to_block(4); - assert_ok!(Nis::thaw(RuntimeOrigin::signed(1), 0, None)); - assert_ok!(Nis::thaw(RuntimeOrigin::signed(1), 1, None)); - assert_noop!(Nis::thaw(RuntimeOrigin::signed(2), 2, None), Error::::Throttled); + assert_ok!(Nis::thaw_private(signed(1), 0, None)); + assert_ok!(Nis::thaw_private(signed(1), 1, None)); + assert_noop!(Nis::thaw_private(signed(2), 2, None), Error::::Throttled); run_to_block(5); - assert_ok!(Nis::thaw(RuntimeOrigin::signed(2), 2, None)); + assert_ok!(Nis::thaw_private(signed(2), 2, None)); assert_eq!(Balances::free_balance(1), 200); assert_eq!(Balances::free_balance(2), 200); @@ -516,24 +676,24 @@ fn multiple_thaws_works() { fn multiple_thaws_works_in_alternative_thaw_order() { new_test_ext().execute_with(|| { run_to_block(1); - assert_ok!(Nis::place_bid(RuntimeOrigin::signed(1), 40, 1)); - assert_ok!(Nis::place_bid(RuntimeOrigin::signed(1), 60, 1)); - assert_ok!(Nis::place_bid(RuntimeOrigin::signed(2), 50, 1)); + assert_ok!(Nis::place_bid(signed(1), 40, 1)); + assert_ok!(Nis::place_bid(signed(1), 60, 1)); + assert_ok!(Nis::place_bid(signed(2), 50, 1)); enlarge(200, 3); // Double everyone's free balances. Balances::make_free_balance_be(&2, 100); Balances::make_free_balance_be(&3, 200); Balances::make_free_balance_be(&4, 200); - assert_ok!(Nis::fund_deficit(RuntimeOrigin::signed(1))); + assert_ok!(Nis::fund_deficit(signed(1))); run_to_block(4); - assert_ok!(Nis::thaw(RuntimeOrigin::signed(2), 2, None)); - assert_noop!(Nis::thaw(RuntimeOrigin::signed(1), 1, None), Error::::Throttled); - assert_ok!(Nis::thaw(RuntimeOrigin::signed(1), 0, None)); + assert_ok!(Nis::thaw_private(signed(2), 2, None)); + assert_noop!(Nis::thaw_private(signed(1), 1, None), Error::::Throttled); + assert_ok!(Nis::thaw_private(signed(1), 0, None)); run_to_block(5); - assert_ok!(Nis::thaw(RuntimeOrigin::signed(1), 1, None)); + assert_ok!(Nis::thaw_private(signed(1), 1, None)); assert_eq!(Balances::free_balance(1), 200); assert_eq!(Balances::free_balance(2), 200); @@ -548,11 +708,11 @@ fn enlargement_to_target_works() { <() as WeightInfo>::process_queue() + (<() as WeightInfo>::process_bid() * 2); super::mock::MaxIntakeWeight::set(w); - assert_ok!(Nis::place_bid(RuntimeOrigin::signed(1), 40, 1)); - assert_ok!(Nis::place_bid(RuntimeOrigin::signed(1), 40, 2)); - assert_ok!(Nis::place_bid(RuntimeOrigin::signed(2), 40, 2)); - assert_ok!(Nis::place_bid(RuntimeOrigin::signed(2), 40, 3)); - assert_ok!(Nis::place_bid(RuntimeOrigin::signed(3), 40, 3)); + assert_ok!(Nis::place_bid(signed(1), 40, 1)); + assert_ok!(Nis::place_bid(signed(1), 40, 2)); + assert_ok!(Nis::place_bid(signed(2), 40, 2)); + assert_ok!(Nis::place_bid(signed(2), 40, 3)); + assert_ok!(Nis::place_bid(signed(3), 40, 3)); Target::set(Perquintill::from_percent(40)); run_to_block(3); @@ -571,11 +731,19 @@ fn enlargement_to_target_works() { // Two new items should have been issued to 2 & 3 for 40 each & duration of 3. assert_eq!( Receipts::::get(0).unwrap(), - ReceiptRecord { proportion: Perquintill::from_percent(10), who: 2, expiry: 13 } + ReceiptRecord { + proportion: Perquintill::from_percent(10), + owner: Some((2, 40)), + expiry: 13 + } ); assert_eq!( Receipts::::get(1).unwrap(), - ReceiptRecord { proportion: Perquintill::from_percent(10), who: 3, expiry: 13 } + ReceiptRecord { + proportion: Perquintill::from_percent(10), + owner: Some((3, 40)), + expiry: 13 + } ); assert_eq!( Summary::::get(), @@ -584,6 +752,7 @@ fn enlargement_to_target_works() { index: 2, last_period: 0, thawed: Perquintill::zero(), + receipts_on_hold: 80, } ); @@ -595,7 +764,8 @@ fn enlargement_to_target_works() { proportion_owed: Perquintill::from_percent(20), index: 2, last_period: 0, - thawed: Perquintill::zero() + thawed: Perquintill::zero(), + receipts_on_hold: 80, } ); @@ -603,11 +773,19 @@ fn enlargement_to_target_works() { // Two new items should have been issued to 1 & 2 for 40 each & duration of 2. assert_eq!( Receipts::::get(2).unwrap(), - ReceiptRecord { proportion: Perquintill::from_percent(10), who: 1, expiry: 12 } + ReceiptRecord { + proportion: Perquintill::from_percent(10), + owner: Some((1, 40)), + expiry: 12 + } ); assert_eq!( Receipts::::get(3).unwrap(), - ReceiptRecord { proportion: Perquintill::from_percent(10), who: 2, expiry: 12 } + ReceiptRecord { + proportion: Perquintill::from_percent(10), + owner: Some((2, 40)), + expiry: 12 + } ); assert_eq!( Summary::::get(), @@ -615,7 +793,8 @@ fn enlargement_to_target_works() { proportion_owed: Perquintill::from_percent(40), index: 4, last_period: 0, - thawed: Perquintill::zero() + thawed: Perquintill::zero(), + receipts_on_hold: 160, } ); @@ -627,7 +806,8 @@ fn enlargement_to_target_works() { proportion_owed: Perquintill::from_percent(40), index: 4, last_period: 0, - thawed: Perquintill::zero() + thawed: Perquintill::zero(), + receipts_on_hold: 160, } ); @@ -635,10 +815,14 @@ fn enlargement_to_target_works() { Target::set(Perquintill::from_percent(60)); run_to_block(10); - // Two new items should have been issued to 1 & 2 for 40 each & duration of 2. + // One new item should have been issued to 1 for 40 each & duration of 2. assert_eq!( Receipts::::get(4).unwrap(), - ReceiptRecord { proportion: Perquintill::from_percent(10), who: 1, expiry: 13 } + ReceiptRecord { + proportion: Perquintill::from_percent(10), + owner: Some((1, 40)), + expiry: 13 + } ); assert_eq!( @@ -647,7 +831,8 @@ fn enlargement_to_target_works() { proportion_owed: Perquintill::from_percent(50), index: 5, last_period: 0, - thawed: Perquintill::zero() + thawed: Perquintill::zero(), + receipts_on_hold: 200, } ); }); diff --git a/frame/nis/src/weights.rs b/frame/nis/src/weights.rs index 71577075a..769ff7955 100644 --- a/frame/nis/src/weights.rs +++ b/frame/nis/src/weights.rs @@ -1,40 +1,31 @@ -// This file is part of Substrate. - -// Copyright (C) 2022 Parity Technologies (UK) Ltd. -// SPDX-License-Identifier: Apache-2.0 - -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. //! Autogenerated weights for pallet_nis //! //! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev -//! DATE: 2022-11-07, STEPS: `50`, REPEAT: 20, LOW RANGE: `[]`, HIGH RANGE: `[]` -//! HOSTNAME: `bm2`, CPU: `Intel(R) Core(TM) i7-7700K CPU @ 4.20GHz` +//! DATE: 2022-12-15, STEPS: `5`, REPEAT: 2, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! HOSTNAME: `Workhorse.local`, CPU: `` //! EXECUTION: Some(Wasm), WASM-EXECUTION: Compiled, CHAIN: Some("dev"), DB CACHE: 1024 // Executed Command: -// ./target/production/substrate +// ../../../target/release/substrate // benchmark // pallet -// --chain=dev -// --steps=50 -// --repeat=20 -// --pallet=pallet_nis -// --extrinsic=* +// --chain +// dev +// --steps +// 5 +// --repeat +// 2 +// --pallet +// pallet_nis +// --extrinsic +// * // --execution=wasm // --wasm-execution=compiled -// --template=./.maintain/frame-weight-template.hbs -// --output=./frame/nis/src/weights.rs +// --output +// ../../../frame/nis/src/weights.rs +// --template +// ../../../.maintain/frame-weight-template.hbs #![cfg_attr(rustfmt, rustfmt_skip)] #![allow(unused_parens)] @@ -48,8 +39,11 @@ pub trait WeightInfo { fn place_bid(l: u32, ) -> Weight; fn place_bid_max() -> Weight; fn retract_bid(l: u32, ) -> Weight; - fn thaw() -> Weight; fn fund_deficit() -> Weight; + fn thaw_private() -> Weight; + fn thaw_communal() -> Weight; + fn privatize() -> Weight; + fn communify() -> Weight; fn process_queues() -> Weight; fn process_queue() -> Weight; fn process_bid() -> Weight; @@ -59,143 +53,223 @@ pub trait WeightInfo { pub struct SubstrateWeight(PhantomData); impl WeightInfo for SubstrateWeight { // Storage: Nis Queues (r:1 w:1) + // Storage: Balances Reserves (r:1 w:1) // Storage: Nis QueueTotals (r:1 w:1) + /// The range of component `l` is `[0, 999]`. fn place_bid(l: u32, ) -> Weight { - // Minimum execution time: 42_332 nanoseconds. - Weight::from_ref_time(45_584_514 as u64) - // Standard Error: 129 - .saturating_add(Weight::from_ref_time(45_727 as u64).saturating_mul(l as u64)) - .saturating_add(T::DbWeight::get().reads(2 as u64)) - .saturating_add(T::DbWeight::get().writes(2 as u64)) + // Minimum execution time: 47_000 nanoseconds. + Weight::from_ref_time(53_822_030) + // Standard Error: 4_869 + .saturating_add(Weight::from_ref_time(47_431).saturating_mul(l.into())) + .saturating_add(T::DbWeight::get().reads(3)) + .saturating_add(T::DbWeight::get().writes(3)) } // Storage: Nis Queues (r:1 w:1) + // Storage: Balances Reserves (r:1 w:1) // Storage: Nis QueueTotals (r:1 w:1) fn place_bid_max() -> Weight { - // Minimum execution time: 85_866 nanoseconds. - Weight::from_ref_time(87_171_000 as u64) - .saturating_add(T::DbWeight::get().reads(2 as u64)) - .saturating_add(T::DbWeight::get().writes(2 as u64)) + // Minimum execution time: 109_000 nanoseconds. + Weight::from_ref_time(109_000_000) + .saturating_add(T::DbWeight::get().reads(3)) + .saturating_add(T::DbWeight::get().writes(3)) } // Storage: Nis Queues (r:1 w:1) // Storage: Nis QueueTotals (r:1 w:1) + // Storage: Balances Reserves (r:1 w:1) + /// The range of component `l` is `[1, 1000]`. fn retract_bid(l: u32, ) -> Weight { - // Minimum execution time: 44_605 nanoseconds. - Weight::from_ref_time(46_850_108 as u64) - // Standard Error: 135 - .saturating_add(Weight::from_ref_time(34_178 as u64).saturating_mul(l as u64)) - .saturating_add(T::DbWeight::get().reads(2 as u64)) - .saturating_add(T::DbWeight::get().writes(2 as u64)) - } - // Storage: Nis Active (r:1 w:1) - // Storage: Nis ActiveTotal (r:1 w:1) - fn thaw() -> Weight { - // Minimum execution time: 55_143 nanoseconds. - Weight::from_ref_time(55_845_000 as u64) - .saturating_add(T::DbWeight::get().reads(2 as u64)) - .saturating_add(T::DbWeight::get().writes(2 as u64)) - } - // Storage: Nis Active (r:1 w:1) - // Storage: Nis ActiveTotal (r:1 w:1) + // Minimum execution time: 50_000 nanoseconds. + Weight::from_ref_time(54_479_879) + // Standard Error: 4_891 + .saturating_add(Weight::from_ref_time(38_224).saturating_mul(l.into())) + .saturating_add(T::DbWeight::get().reads(3)) + .saturating_add(T::DbWeight::get().writes(3)) + } + // Storage: Nis Summary (r:1 w:0) + // Storage: System Account (r:1 w:1) fn fund_deficit() -> Weight { - Weight::from_ref_time(47_753_000 as u64) - .saturating_add(T::DbWeight::get().reads(2 as u64)) - .saturating_add(T::DbWeight::get().writes(2 as u64)) + // Minimum execution time: 57_000 nanoseconds. + Weight::from_ref_time(62_000_000) + .saturating_add(T::DbWeight::get().reads(2)) + .saturating_add(T::DbWeight::get().writes(1)) } - // Storage: Nis ActiveTotal (r:1 w:0) - fn process_queues() -> Weight { - Weight::from_ref_time(1_663_000 as u64) - .saturating_add(T::DbWeight::get().reads(1 as u64)) + // Storage: Nis Receipts (r:1 w:1) + // Storage: Nis Summary (r:1 w:1) + // Storage: System Account (r:1 w:0) + // Storage: Balances Reserves (r:1 w:1) + fn thaw_private() -> Weight { + // Minimum execution time: 84_000 nanoseconds. + Weight::from_ref_time(85_000_000) + .saturating_add(T::DbWeight::get().reads(4)) + .saturating_add(T::DbWeight::get().writes(3)) + } + // Storage: Nis Receipts (r:1 w:1) + // Storage: Nis Summary (r:1 w:1) + // Storage: Assets Asset (r:1 w:1) + // Storage: Assets Account (r:1 w:1) + // Storage: System Account (r:1 w:1) + fn thaw_communal() -> Weight { + // Minimum execution time: 108_000 nanoseconds. + Weight::from_ref_time(115_000_000) + .saturating_add(T::DbWeight::get().reads(5)) + .saturating_add(T::DbWeight::get().writes(5)) } - // Storage: Nis ActiveTotal (r:1 w:1) + // Storage: Nis Receipts (r:1 w:1) + // Storage: Nis Summary (r:1 w:1) + // Storage: System Account (r:1 w:1) + // Storage: Assets Asset (r:1 w:1) + // Storage: Assets Account (r:1 w:1) + // Storage: Balances Reserves (r:1 w:1) + fn privatize() -> Weight { + // Minimum execution time: 107_000 nanoseconds. + Weight::from_ref_time(110_000_000) + .saturating_add(T::DbWeight::get().reads(6)) + .saturating_add(T::DbWeight::get().writes(6)) + } + // Storage: Nis Receipts (r:1 w:1) + // Storage: Balances Reserves (r:1 w:1) + // Storage: System Account (r:1 w:1) + // Storage: Nis Summary (r:1 w:1) + // Storage: Assets Asset (r:1 w:1) + // Storage: Assets Account (r:1 w:1) + fn communify() -> Weight { + // Minimum execution time: 89_000 nanoseconds. + Weight::from_ref_time(89_000_000) + .saturating_add(T::DbWeight::get().reads(6)) + .saturating_add(T::DbWeight::get().writes(6)) + } + // Storage: Nis Summary (r:1 w:1) + // Storage: System Account (r:1 w:0) // Storage: Nis QueueTotals (r:1 w:1) + fn process_queues() -> Weight { + // Minimum execution time: 34_000 nanoseconds. + Weight::from_ref_time(38_000_000) + .saturating_add(T::DbWeight::get().reads(3)) + .saturating_add(T::DbWeight::get().writes(2)) + } // Storage: Nis Queues (r:1 w:1) - // Storage: Nis Active (r:0 w:1) fn process_queue() -> Weight { - Weight::from_ref_time(40_797_000 as u64) - // Standard Error: 1_000 - .saturating_add(T::DbWeight::get().reads(3 as u64)) - .saturating_add(T::DbWeight::get().writes(3 as u64)) + // Minimum execution time: 6_000 nanoseconds. + Weight::from_ref_time(7_000_000) + .saturating_add(T::DbWeight::get().reads(1)) + .saturating_add(T::DbWeight::get().writes(1)) } - // Storage: Nis ActiveTotal (r:1 w:1) - // Storage: Nis QueueTotals (r:1 w:1) - // Storage: Nis Queues (r:1 w:1) - // Storage: Nis Active (r:0 w:1) + // Storage: Nis Receipts (r:0 w:1) fn process_bid() -> Weight { - Weight::from_ref_time(14_944_000 as u64) - // Standard Error: 6_000 - .saturating_add(T::DbWeight::get().reads(2 as u64)) - .saturating_add(T::DbWeight::get().writes(2 as u64)) + // Minimum execution time: 14_000 nanoseconds. + Weight::from_ref_time(15_000_000) + .saturating_add(T::DbWeight::get().writes(1)) } } // For backwards compatibility and tests impl WeightInfo for () { // Storage: Nis Queues (r:1 w:1) + // Storage: Balances Reserves (r:1 w:1) // Storage: Nis QueueTotals (r:1 w:1) + /// The range of component `l` is `[0, 999]`. fn place_bid(l: u32, ) -> Weight { - // Minimum execution time: 42_332 nanoseconds. - Weight::from_ref_time(45_584_514 as u64) - // Standard Error: 129 - .saturating_add(Weight::from_ref_time(45_727 as u64).saturating_mul(l as u64)) - .saturating_add(RocksDbWeight::get().reads(2 as u64)) - .saturating_add(RocksDbWeight::get().writes(2 as u64)) + // Minimum execution time: 47_000 nanoseconds. + Weight::from_ref_time(53_822_030) + // Standard Error: 4_869 + .saturating_add(Weight::from_ref_time(47_431).saturating_mul(l.into())) + .saturating_add(RocksDbWeight::get().reads(3)) + .saturating_add(RocksDbWeight::get().writes(3)) } // Storage: Nis Queues (r:1 w:1) + // Storage: Balances Reserves (r:1 w:1) // Storage: Nis QueueTotals (r:1 w:1) fn place_bid_max() -> Weight { - // Minimum execution time: 85_866 nanoseconds. - Weight::from_ref_time(87_171_000 as u64) - .saturating_add(RocksDbWeight::get().reads(2 as u64)) - .saturating_add(RocksDbWeight::get().writes(2 as u64)) + // Minimum execution time: 109_000 nanoseconds. + Weight::from_ref_time(109_000_000) + .saturating_add(RocksDbWeight::get().reads(3)) + .saturating_add(RocksDbWeight::get().writes(3)) } // Storage: Nis Queues (r:1 w:1) // Storage: Nis QueueTotals (r:1 w:1) + // Storage: Balances Reserves (r:1 w:1) + /// The range of component `l` is `[1, 1000]`. fn retract_bid(l: u32, ) -> Weight { - // Minimum execution time: 44_605 nanoseconds. - Weight::from_ref_time(46_850_108 as u64) - // Standard Error: 135 - .saturating_add(Weight::from_ref_time(34_178 as u64).saturating_mul(l as u64)) - .saturating_add(RocksDbWeight::get().reads(2 as u64)) - .saturating_add(RocksDbWeight::get().writes(2 as u64)) - } - // Storage: Nis Active (r:1 w:1) - // Storage: Nis ActiveTotal (r:1 w:1) - fn thaw() -> Weight { - // Minimum execution time: 55_143 nanoseconds. - Weight::from_ref_time(55_845_000 as u64) - .saturating_add(RocksDbWeight::get().reads(2 as u64)) - .saturating_add(RocksDbWeight::get().writes(2 as u64)) - } - // Storage: Nis Active (r:1 w:1) - // Storage: Nis ActiveTotal (r:1 w:1) + // Minimum execution time: 50_000 nanoseconds. + Weight::from_ref_time(54_479_879) + // Standard Error: 4_891 + .saturating_add(Weight::from_ref_time(38_224).saturating_mul(l.into())) + .saturating_add(RocksDbWeight::get().reads(3)) + .saturating_add(RocksDbWeight::get().writes(3)) + } + // Storage: Nis Summary (r:1 w:0) + // Storage: System Account (r:1 w:1) fn fund_deficit() -> Weight { - Weight::from_ref_time(47_753_000 as u64) - .saturating_add(RocksDbWeight::get().reads(2 as u64)) - .saturating_add(RocksDbWeight::get().writes(2 as u64)) + // Minimum execution time: 57_000 nanoseconds. + Weight::from_ref_time(62_000_000) + .saturating_add(RocksDbWeight::get().reads(2)) + .saturating_add(RocksDbWeight::get().writes(1)) } - // Storage: Nis ActiveTotal (r:1 w:0) - fn process_queues() -> Weight { - Weight::from_ref_time(1_663_000 as u64) - .saturating_add(RocksDbWeight::get().reads(1 as u64)) + // Storage: Nis Receipts (r:1 w:1) + // Storage: Nis Summary (r:1 w:1) + // Storage: System Account (r:1 w:0) + // Storage: Balances Reserves (r:1 w:1) + fn thaw_private() -> Weight { + // Minimum execution time: 84_000 nanoseconds. + Weight::from_ref_time(85_000_000) + .saturating_add(RocksDbWeight::get().reads(4)) + .saturating_add(RocksDbWeight::get().writes(3)) + } + // Storage: Nis Receipts (r:1 w:1) + // Storage: Nis Summary (r:1 w:1) + // Storage: Assets Asset (r:1 w:1) + // Storage: Assets Account (r:1 w:1) + // Storage: System Account (r:1 w:1) + fn thaw_communal() -> Weight { + // Minimum execution time: 108_000 nanoseconds. + Weight::from_ref_time(115_000_000) + .saturating_add(RocksDbWeight::get().reads(5)) + .saturating_add(RocksDbWeight::get().writes(5)) + } + // Storage: Nis Receipts (r:1 w:1) + // Storage: Nis Summary (r:1 w:1) + // Storage: System Account (r:1 w:1) + // Storage: Assets Asset (r:1 w:1) + // Storage: Assets Account (r:1 w:1) + // Storage: Balances Reserves (r:1 w:1) + fn privatize() -> Weight { + // Minimum execution time: 107_000 nanoseconds. + Weight::from_ref_time(110_000_000) + .saturating_add(RocksDbWeight::get().reads(6)) + .saturating_add(RocksDbWeight::get().writes(6)) } - // Storage: Nis ActiveTotal (r:1 w:1) + // Storage: Nis Receipts (r:1 w:1) + // Storage: Balances Reserves (r:1 w:1) + // Storage: System Account (r:1 w:1) + // Storage: Nis Summary (r:1 w:1) + // Storage: Assets Asset (r:1 w:1) + // Storage: Assets Account (r:1 w:1) + fn communify() -> Weight { + // Minimum execution time: 89_000 nanoseconds. + Weight::from_ref_time(89_000_000) + .saturating_add(RocksDbWeight::get().reads(6)) + .saturating_add(RocksDbWeight::get().writes(6)) + } + // Storage: Nis Summary (r:1 w:1) + // Storage: System Account (r:1 w:0) // Storage: Nis QueueTotals (r:1 w:1) + fn process_queues() -> Weight { + // Minimum execution time: 34_000 nanoseconds. + Weight::from_ref_time(38_000_000) + .saturating_add(RocksDbWeight::get().reads(3)) + .saturating_add(RocksDbWeight::get().writes(2)) + } // Storage: Nis Queues (r:1 w:1) - // Storage: Nis Active (r:0 w:1) fn process_queue() -> Weight { - Weight::from_ref_time(40_797_000 as u64) - // Standard Error: 1_000 - .saturating_add(RocksDbWeight::get().reads(3 as u64)) - .saturating_add(RocksDbWeight::get().writes(3 as u64)) + // Minimum execution time: 6_000 nanoseconds. + Weight::from_ref_time(7_000_000) + .saturating_add(RocksDbWeight::get().reads(1)) + .saturating_add(RocksDbWeight::get().writes(1)) } - // Storage: Nis ActiveTotal (r:1 w:1) - // Storage: Nis QueueTotals (r:1 w:1) - // Storage: Nis Queues (r:1 w:1) - // Storage: Nis Active (r:0 w:1) + // Storage: Nis Receipts (r:0 w:1) fn process_bid() -> Weight { - Weight::from_ref_time(14_944_000 as u64) - // Standard Error: 6_000 - .saturating_add(RocksDbWeight::get().reads(2 as u64)) - .saturating_add(RocksDbWeight::get().writes(2 as u64)) + // Minimum execution time: 14_000 nanoseconds. + Weight::from_ref_time(15_000_000) + .saturating_add(RocksDbWeight::get().writes(1)) } } diff --git a/frame/support/src/traits/tokens/fungible.rs b/frame/support/src/traits/tokens/fungible.rs index 05e109b87..a36e63e71 100644 --- a/frame/support/src/traits/tokens/fungible.rs +++ b/frame/support/src/traits/tokens/fungible.rs @@ -78,6 +78,7 @@ pub trait Mutate: Inspect { /// returned and nothing is changed. If successful, the amount of tokens reduced is returned. fn burn_from(who: &AccountId, amount: Self::Balance) -> Result; + // TODO: Remove. /// Attempt to reduce the balance of `who` by as much as possible up to `amount`, and possibly /// slightly more due to minimum_balance requirements. If no decrease is possible then an `Err` /// is returned and nothing is changed. If successful, the amount of tokens reduced is returned. @@ -143,6 +144,7 @@ pub trait InspectHold: Inspect { fn can_hold(who: &AccountId, amount: Self::Balance) -> bool; } +// TODO: Introduce `HoldReason`. /// Trait for mutating a fungible asset which can be reserved. pub trait MutateHold: InspectHold + Transfer { /// Hold some funds in an account. @@ -160,6 +162,8 @@ pub trait MutateHold: InspectHold + Transfer { best_effort: bool, ) -> Result; + // TODO: Introduce repatriate_held + /// Transfer held funds into a destination account. /// /// If `on_hold` is `true`, then the destination account must already exist and the assets @@ -195,6 +199,7 @@ pub trait BalancedHold: Balanced + MutateHold { } impl + MutateHold> BalancedHold for T { + // TODO: This should be implemented properly, and `slash` should be removed. fn slash_held( who: &AccountId, amount: Self::Balance, From 414caffad1ce634352676d1884942282a8ec6ebb Mon Sep 17 00:00:00 2001 From: Davide Galassi Date: Tue, 17 Jan 2023 16:55:11 +0100 Subject: [PATCH 019/558] Refactory of `next_slot` method (#13155) Refactory of `next_slot` method * Prevents slot worker exit if inherent data provider creation fails * Failure is not possible anymore * Fix potential failure after warp-sync where block headers of not already downloaded blocks are used by the inherent data provider --- client/consensus/slots/src/lib.rs | 8 +--- client/consensus/slots/src/slots.rs | 59 ++++++++++++++++------------- 2 files changed, 34 insertions(+), 33 deletions(-) diff --git a/client/consensus/slots/src/lib.rs b/client/consensus/slots/src/lib.rs index 6126647e6..30d2c1761 100644 --- a/client/consensus/slots/src/lib.rs +++ b/client/consensus/slots/src/lib.rs @@ -524,13 +524,7 @@ pub async fn start_slot_worker( let mut slots = Slots::new(slot_duration.as_duration(), create_inherent_data_providers, client); loop { - let slot_info = match slots.next_slot().await { - Ok(r) => r, - Err(e) => { - warn!(target: LOG_TARGET, "Error while polling for next slot: {}", e); - return - }, - }; + let slot_info = slots.next_slot().await; if sync_oracle.is_major_syncing() { debug!(target: LOG_TARGET, "Skipping proposal slot due to sync."); diff --git a/client/consensus/slots/src/slots.rs b/client/consensus/slots/src/slots.rs index 9bb5650b3..7bb263b2b 100644 --- a/client/consensus/slots/src/slots.rs +++ b/client/consensus/slots/src/slots.rs @@ -21,7 +21,7 @@ //! This is used instead of `futures_timer::Interval` because it was unreliable. use super::{InherentDataProviderExt, Slot, LOG_TARGET}; -use sp_consensus::{Error, SelectChain}; +use sp_consensus::SelectChain; use sp_inherents::{CreateInherentDataProviders, InherentDataProvider}; use sp_runtime::traits::{Block as BlockT, Header as HeaderT}; @@ -90,7 +90,7 @@ impl SlotInfo { pub(crate) struct Slots { last_slot: Slot, slot_duration: Duration, - inner_delay: Option, + until_next_slot: Option, create_inherent_data_providers: IDP, select_chain: SC, _phantom: std::marker::PhantomData, @@ -106,7 +106,7 @@ impl Slots { Slots { last_slot: 0.into(), slot_duration, - inner_delay: None, + until_next_slot: None, create_inherent_data_providers, select_chain, _phantom: Default::default(), @@ -122,26 +122,22 @@ where IDP::InherentDataProviders: crate::InherentDataProviderExt, { /// Returns a future that fires when the next slot starts. - pub async fn next_slot(&mut self) -> Result, Error> { + pub async fn next_slot(&mut self) -> SlotInfo { loop { - self.inner_delay = match self.inner_delay.take() { - None => { - // schedule wait. + // Wait for slot timeout + self.until_next_slot + .take() + .unwrap_or_else(|| { + // Schedule first timeout. let wait_dur = time_until_next_slot(self.slot_duration); - Some(Delay::new(wait_dur)) - }, - Some(d) => Some(d), - }; - - if let Some(inner_delay) = self.inner_delay.take() { - inner_delay.await; - } - // timeout has fired. + Delay::new(wait_dur) + }) + .await; - let ends_in = time_until_next_slot(self.slot_duration); + // Schedule delay for next slot. + let wait_dur = time_until_next_slot(self.slot_duration); + self.until_next_slot = Some(Delay::new(wait_dur)); - // reschedule delay for next slot. - self.inner_delay = Some(Delay::new(ends_in)); let chain_head = match self.select_chain.best_chain().await { Ok(x) => x, Err(e) => { @@ -150,30 +146,41 @@ where "Unable to author block in slot. No best block header: {}", e, ); - // Let's try at the next slot.. - self.inner_delay.take(); + // Let's retry at the next slot. continue }, }; - let inherent_data_providers = self + let inherent_data_providers = match self .create_inherent_data_providers .create_inherent_data_providers(chain_head.hash(), ()) - .await?; + .await + { + Ok(x) => x, + Err(e) => { + log::warn!( + target: LOG_TARGET, + "Unable to author block in slot. Failure creating inherent data provider: {}", + e, + ); + // Let's retry at the next slot. + continue + }, + }; let slot = inherent_data_providers.slot(); - // never yield the same slot twice. + // Never yield the same slot twice. if slot > self.last_slot { self.last_slot = slot; - break Ok(SlotInfo::new( + break SlotInfo::new( slot, Box::new(inherent_data_providers), self.slot_duration, chain_head, None, - )) + ) } } } From f7e53fe1a8c0e15604cc30177c312241624831bf Mon Sep 17 00:00:00 2001 From: Squirrel Date: Tue, 17 Jan 2023 16:26:10 +0000 Subject: [PATCH 020/558] Breakout mock runtimes to separate files (#13150) * break out moch runtimes to separate files * tranaction-payment: break out tests & mock runtime to separate files --- .../asset-tx-payment/src/lib.rs | 2 + .../asset-tx-payment/src/mock.rs | 213 ++++ .../asset-tx-payment/src/tests.rs | 197 +--- frame/transaction-payment/src/lib.rs | 961 +----------------- frame/transaction-payment/src/mock.rs | 162 +++ frame/transaction-payment/src/tests.rs | 836 +++++++++++++++ 6 files changed, 1223 insertions(+), 1148 deletions(-) create mode 100644 frame/transaction-payment/asset-tx-payment/src/mock.rs create mode 100644 frame/transaction-payment/src/mock.rs create mode 100644 frame/transaction-payment/src/tests.rs diff --git a/frame/transaction-payment/asset-tx-payment/src/lib.rs b/frame/transaction-payment/asset-tx-payment/src/lib.rs index 43cc1efa0..230b30731 100644 --- a/frame/transaction-payment/asset-tx-payment/src/lib.rs +++ b/frame/transaction-payment/asset-tx-payment/src/lib.rs @@ -59,6 +59,8 @@ use sp_runtime::{ FixedPointOperand, }; +#[cfg(test)] +mod mock; #[cfg(test)] mod tests; diff --git a/frame/transaction-payment/asset-tx-payment/src/mock.rs b/frame/transaction-payment/asset-tx-payment/src/mock.rs new file mode 100644 index 000000000..ddb02f5a6 --- /dev/null +++ b/frame/transaction-payment/asset-tx-payment/src/mock.rs @@ -0,0 +1,213 @@ +// Copyright (C) 2021-2022 Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +use super::*; +use crate as pallet_asset_tx_payment; + +use codec; +use frame_support::{ + dispatch::DispatchClass, + pallet_prelude::*, + parameter_types, + traits::{AsEnsureOriginWithArg, ConstU32, ConstU64, ConstU8, FindAuthor}, + weights::{Weight, WeightToFee as WeightToFeeT}, + ConsensusEngineId, +}; +use frame_system as system; +use frame_system::EnsureRoot; +use pallet_transaction_payment::CurrencyAdapter; +use sp_core::H256; +use sp_runtime::{ + testing::Header, + traits::{BlakeTwo256, ConvertInto, IdentityLookup, SaturatedConversion}, +}; + +type UncheckedExtrinsic = frame_system::mocking::MockUncheckedExtrinsic; +type Block = frame_system::mocking::MockBlock; +type Balance = u64; +type AccountId = u64; + +frame_support::construct_runtime!( + pub enum Runtime where + Block = Block, + NodeBlock = Block, + UncheckedExtrinsic = UncheckedExtrinsic, + { + System: system::{Pallet, Call, Config, Storage, Event}, + Balances: pallet_balances::{Pallet, Call, Storage, Config, Event}, + TransactionPayment: pallet_transaction_payment::{Pallet, Storage, Event}, + Assets: pallet_assets::{Pallet, Call, Storage, Event}, + Authorship: pallet_authorship::{Pallet, Call, Storage}, + AssetTxPayment: pallet_asset_tx_payment::{Pallet, Event}, + } +); + +parameter_types! { + pub(crate) static ExtrinsicBaseWeight: Weight = Weight::zero(); +} + +pub struct BlockWeights; +impl Get for BlockWeights { + fn get() -> frame_system::limits::BlockWeights { + frame_system::limits::BlockWeights::builder() + .base_block(Weight::zero()) + .for_class(DispatchClass::all(), |weights| { + weights.base_extrinsic = ExtrinsicBaseWeight::get().into(); + }) + .for_class(DispatchClass::non_mandatory(), |weights| { + weights.max_total = Weight::from_ref_time(1024).set_proof_size(u64::MAX).into(); + }) + .build_or_panic() + } +} + +parameter_types! { + pub static WeightToFee: u64 = 1; + pub static TransactionByteFee: u64 = 1; +} + +impl frame_system::Config for Runtime { + type BaseCallFilter = frame_support::traits::Everything; + type BlockWeights = BlockWeights; + type BlockLength = (); + type DbWeight = (); + type RuntimeOrigin = RuntimeOrigin; + type Index = u64; + type BlockNumber = u64; + type RuntimeCall = RuntimeCall; + type Hash = H256; + type Hashing = BlakeTwo256; + type AccountId = AccountId; + type Lookup = IdentityLookup; + type Header = Header; + type RuntimeEvent = RuntimeEvent; + type BlockHashCount = ConstU64<250>; + type Version = (); + type PalletInfo = PalletInfo; + type AccountData = pallet_balances::AccountData; + type OnNewAccount = (); + type OnKilledAccount = (); + type SystemWeightInfo = (); + type SS58Prefix = (); + type OnSetCode = (); + type MaxConsumers = ConstU32<16>; +} + +parameter_types! { + pub const ExistentialDeposit: u64 = 10; +} + +impl pallet_balances::Config for Runtime { + type Balance = Balance; + type RuntimeEvent = RuntimeEvent; + type DustRemoval = (); + type ExistentialDeposit = ConstU64<10>; + type AccountStore = System; + type MaxLocks = (); + type WeightInfo = (); + type MaxReserves = ConstU32<50>; + type ReserveIdentifier = [u8; 8]; +} + +impl WeightToFeeT for WeightToFee { + type Balance = u64; + + fn weight_to_fee(weight: &Weight) -> Self::Balance { + Self::Balance::saturated_from(weight.ref_time()) + .saturating_mul(WEIGHT_TO_FEE.with(|v| *v.borrow())) + } +} + +impl WeightToFeeT for TransactionByteFee { + type Balance = u64; + + fn weight_to_fee(weight: &Weight) -> Self::Balance { + Self::Balance::saturated_from(weight.ref_time()) + .saturating_mul(TRANSACTION_BYTE_FEE.with(|v| *v.borrow())) + } +} + +impl pallet_transaction_payment::Config for Runtime { + type RuntimeEvent = RuntimeEvent; + type OnChargeTransaction = CurrencyAdapter; + type WeightToFee = WeightToFee; + type LengthToFee = TransactionByteFee; + type FeeMultiplierUpdate = (); + type OperationalFeeMultiplier = ConstU8<5>; +} + +type AssetId = u32; + +impl pallet_assets::Config for Runtime { + type RuntimeEvent = RuntimeEvent; + type Balance = Balance; + type AssetId = AssetId; + type AssetIdParameter = codec::Compact; + type Currency = Balances; + type CreateOrigin = AsEnsureOriginWithArg>; + type ForceOrigin = EnsureRoot; + type AssetDeposit = ConstU64<2>; + type AssetAccountDeposit = ConstU64<2>; + type MetadataDepositBase = ConstU64<0>; + type MetadataDepositPerByte = ConstU64<0>; + type ApprovalDeposit = ConstU64<0>; + type StringLimit = ConstU32<20>; + type Freezer = (); + type Extra = (); + type CallbackHandle = (); + type WeightInfo = (); + type RemoveItemsLimit = ConstU32<1000>; + pallet_assets::runtime_benchmarks_enabled! { + type BenchmarkHelper = (); + } +} + +pub struct HardcodedAuthor; +pub(crate) const BLOCK_AUTHOR: AccountId = 1234; +impl FindAuthor for HardcodedAuthor { + fn find_author<'a, I>(_: I) -> Option + where + I: 'a + IntoIterator, + { + Some(BLOCK_AUTHOR) + } +} + +impl pallet_authorship::Config for Runtime { + type FindAuthor = HardcodedAuthor; + type UncleGenerations = (); + type FilterUncle = (); + type EventHandler = (); +} + +pub struct CreditToBlockAuthor; +impl HandleCredit for CreditToBlockAuthor { + fn handle_credit(credit: CreditOf) { + if let Some(author) = pallet_authorship::Pallet::::author() { + // What to do in case paying the author fails (e.g. because `fee < min_balance`) + // default: drop the result which will trigger the `OnDrop` of the imbalance. + let _ = >::resolve(&author, credit); + } + } +} + +impl Config for Runtime { + type RuntimeEvent = RuntimeEvent; + type Fungibles = Assets; + type OnChargeAssetTransaction = FungiblesAdapter< + pallet_assets::BalanceToAssetBalance, + CreditToBlockAuthor, + >; +} diff --git a/frame/transaction-payment/asset-tx-payment/src/tests.rs b/frame/transaction-payment/asset-tx-payment/src/tests.rs index b79b87a93..f76293f45 100644 --- a/frame/transaction-payment/asset-tx-payment/src/tests.rs +++ b/frame/transaction-payment/asset-tx-payment/src/tests.rs @@ -14,209 +14,22 @@ // limitations under the License. use super::*; -use crate as pallet_asset_tx_payment; -use codec; use frame_support::{ assert_ok, - dispatch::{DispatchClass, DispatchInfo, PostDispatchInfo}, + dispatch::{DispatchInfo, PostDispatchInfo}, pallet_prelude::*, - parameter_types, - traits::{fungibles::Mutate, AsEnsureOriginWithArg, ConstU32, ConstU64, ConstU8, FindAuthor}, - weights::{Weight, WeightToFee as WeightToFeeT}, - ConsensusEngineId, + traits::fungibles::Mutate, + weights::Weight, }; use frame_system as system; -use frame_system::EnsureRoot; +use mock::{ExtrinsicBaseWeight, *}; use pallet_balances::Call as BalancesCall; -use pallet_transaction_payment::CurrencyAdapter; -use sp_core::H256; -use sp_runtime::{ - testing::Header, - traits::{BlakeTwo256, ConvertInto, IdentityLookup, SaturatedConversion, StaticLookup}, -}; - -type UncheckedExtrinsic = frame_system::mocking::MockUncheckedExtrinsic; -type Block = frame_system::mocking::MockBlock; -type Balance = u64; -type AccountId = u64; - -frame_support::construct_runtime!( - pub enum Runtime where - Block = Block, - NodeBlock = Block, - UncheckedExtrinsic = UncheckedExtrinsic, - { - System: system::{Pallet, Call, Config, Storage, Event}, - Balances: pallet_balances::{Pallet, Call, Storage, Config, Event}, - TransactionPayment: pallet_transaction_payment::{Pallet, Storage, Event}, - Assets: pallet_assets::{Pallet, Call, Storage, Event}, - Authorship: pallet_authorship::{Pallet, Call, Storage}, - AssetTxPayment: pallet_asset_tx_payment::{Pallet, Event}, - } -); +use sp_runtime::traits::StaticLookup; const CALL: &::RuntimeCall = &RuntimeCall::Balances(BalancesCall::transfer { dest: 2, value: 69 }); -parameter_types! { - static ExtrinsicBaseWeight: Weight = Weight::zero(); -} - -pub struct BlockWeights; -impl Get for BlockWeights { - fn get() -> frame_system::limits::BlockWeights { - frame_system::limits::BlockWeights::builder() - .base_block(Weight::zero()) - .for_class(DispatchClass::all(), |weights| { - weights.base_extrinsic = ExtrinsicBaseWeight::get().into(); - }) - .for_class(DispatchClass::non_mandatory(), |weights| { - weights.max_total = Weight::from_ref_time(1024).set_proof_size(u64::MAX).into(); - }) - .build_or_panic() - } -} - -parameter_types! { - pub static WeightToFee: u64 = 1; - pub static TransactionByteFee: u64 = 1; -} - -impl frame_system::Config for Runtime { - type BaseCallFilter = frame_support::traits::Everything; - type BlockWeights = BlockWeights; - type BlockLength = (); - type DbWeight = (); - type RuntimeOrigin = RuntimeOrigin; - type Index = u64; - type BlockNumber = u64; - type RuntimeCall = RuntimeCall; - type Hash = H256; - type Hashing = BlakeTwo256; - type AccountId = AccountId; - type Lookup = IdentityLookup; - type Header = Header; - type RuntimeEvent = RuntimeEvent; - type BlockHashCount = ConstU64<250>; - type Version = (); - type PalletInfo = PalletInfo; - type AccountData = pallet_balances::AccountData; - type OnNewAccount = (); - type OnKilledAccount = (); - type SystemWeightInfo = (); - type SS58Prefix = (); - type OnSetCode = (); - type MaxConsumers = ConstU32<16>; -} - -parameter_types! { - pub const ExistentialDeposit: u64 = 10; -} - -impl pallet_balances::Config for Runtime { - type Balance = Balance; - type RuntimeEvent = RuntimeEvent; - type DustRemoval = (); - type ExistentialDeposit = ConstU64<10>; - type AccountStore = System; - type MaxLocks = (); - type WeightInfo = (); - type MaxReserves = ConstU32<50>; - type ReserveIdentifier = [u8; 8]; -} - -impl WeightToFeeT for WeightToFee { - type Balance = u64; - - fn weight_to_fee(weight: &Weight) -> Self::Balance { - Self::Balance::saturated_from(weight.ref_time()) - .saturating_mul(WEIGHT_TO_FEE.with(|v| *v.borrow())) - } -} - -impl WeightToFeeT for TransactionByteFee { - type Balance = u64; - - fn weight_to_fee(weight: &Weight) -> Self::Balance { - Self::Balance::saturated_from(weight.ref_time()) - .saturating_mul(TRANSACTION_BYTE_FEE.with(|v| *v.borrow())) - } -} - -impl pallet_transaction_payment::Config for Runtime { - type RuntimeEvent = RuntimeEvent; - type OnChargeTransaction = CurrencyAdapter; - type WeightToFee = WeightToFee; - type LengthToFee = TransactionByteFee; - type FeeMultiplierUpdate = (); - type OperationalFeeMultiplier = ConstU8<5>; -} - -type AssetId = u32; - -impl pallet_assets::Config for Runtime { - type RuntimeEvent = RuntimeEvent; - type Balance = Balance; - type AssetId = AssetId; - type AssetIdParameter = codec::Compact; - type Currency = Balances; - type CreateOrigin = AsEnsureOriginWithArg>; - type ForceOrigin = EnsureRoot; - type AssetDeposit = ConstU64<2>; - type AssetAccountDeposit = ConstU64<2>; - type MetadataDepositBase = ConstU64<0>; - type MetadataDepositPerByte = ConstU64<0>; - type ApprovalDeposit = ConstU64<0>; - type StringLimit = ConstU32<20>; - type Freezer = (); - type Extra = (); - type CallbackHandle = (); - type WeightInfo = (); - type RemoveItemsLimit = ConstU32<1000>; - pallet_assets::runtime_benchmarks_enabled! { - type BenchmarkHelper = (); - } -} - -pub struct HardcodedAuthor; -const BLOCK_AUTHOR: AccountId = 1234; -impl FindAuthor for HardcodedAuthor { - fn find_author<'a, I>(_: I) -> Option - where - I: 'a + IntoIterator, - { - Some(BLOCK_AUTHOR) - } -} - -impl pallet_authorship::Config for Runtime { - type FindAuthor = HardcodedAuthor; - type UncleGenerations = (); - type FilterUncle = (); - type EventHandler = (); -} - -pub struct CreditToBlockAuthor; -impl HandleCredit for CreditToBlockAuthor { - fn handle_credit(credit: CreditOf) { - if let Some(author) = pallet_authorship::Pallet::::author() { - // What to do in case paying the author fails (e.g. because `fee < min_balance`) - // default: drop the result which will trigger the `OnDrop` of the imbalance. - let _ = >::resolve(&author, credit); - } - } -} - -impl Config for Runtime { - type RuntimeEvent = RuntimeEvent; - type Fungibles = Assets; - type OnChargeAssetTransaction = FungiblesAdapter< - pallet_assets::BalanceToAssetBalance, - CreditToBlockAuthor, - >; -} - pub struct ExtBuilder { balance_factor: u64, base_weight: Weight, diff --git a/frame/transaction-payment/src/lib.rs b/frame/transaction-payment/src/lib.rs index ce747fa6b..13adbf89c 100644 --- a/frame/transaction-payment/src/lib.rs +++ b/frame/transaction-payment/src/lib.rs @@ -70,6 +70,11 @@ use frame_support::{ weights::{Weight, WeightToFee}, }; +#[cfg(test)] +mod mock; +#[cfg(test)] +mod tests; + mod payment; mod types; @@ -847,959 +852,3 @@ where Self::compute_actual_fee(len, &info, &post_info, Zero::zero()) } } - -#[cfg(test)] -mod tests { - use super::*; - use crate as pallet_transaction_payment; - - use codec::Encode; - - use sp_core::H256; - use sp_runtime::{ - testing::{Header, TestXt}, - traits::{BlakeTwo256, IdentityLookup, One}, - transaction_validity::InvalidTransaction, - }; - - use frame_support::{ - assert_noop, assert_ok, - dispatch::{DispatchClass, DispatchInfo, GetDispatchInfo, PostDispatchInfo}, - parameter_types, - traits::{ConstU32, ConstU64, Currency, GenesisBuild, Imbalance, OnUnbalanced}, - weights::{Weight, WeightToFee as WeightToFeeT}, - }; - use frame_system as system; - use pallet_balances::Call as BalancesCall; - - type UncheckedExtrinsic = frame_system::mocking::MockUncheckedExtrinsic; - type Block = frame_system::mocking::MockBlock; - - frame_support::construct_runtime!( - pub enum Runtime where - Block = Block, - NodeBlock = Block, - UncheckedExtrinsic = UncheckedExtrinsic, - { - System: system::{Pallet, Call, Config, Storage, Event}, - Balances: pallet_balances::{Pallet, Call, Storage, Config, Event}, - TransactionPayment: pallet_transaction_payment::{Pallet, Storage, Event}, - } - ); - - const CALL: &::RuntimeCall = - &RuntimeCall::Balances(BalancesCall::transfer { dest: 2, value: 69 }); - - parameter_types! { - static ExtrinsicBaseWeight: Weight = Weight::zero(); - } - - pub struct BlockWeights; - impl Get for BlockWeights { - fn get() -> frame_system::limits::BlockWeights { - frame_system::limits::BlockWeights::builder() - .base_block(Weight::zero()) - .for_class(DispatchClass::all(), |weights| { - weights.base_extrinsic = ExtrinsicBaseWeight::get().into(); - }) - .for_class(DispatchClass::non_mandatory(), |weights| { - weights.max_total = Weight::from_ref_time(1024).set_proof_size(u64::MAX).into(); - }) - .build_or_panic() - } - } - - parameter_types! { - pub static WeightToFee: u64 = 1; - pub static TransactionByteFee: u64 = 1; - pub static OperationalFeeMultiplier: u8 = 5; - } - - impl frame_system::Config for Runtime { - type BaseCallFilter = frame_support::traits::Everything; - type BlockWeights = BlockWeights; - type BlockLength = (); - type DbWeight = (); - type RuntimeOrigin = RuntimeOrigin; - type Index = u64; - type BlockNumber = u64; - type RuntimeCall = RuntimeCall; - type Hash = H256; - type Hashing = BlakeTwo256; - type AccountId = u64; - type Lookup = IdentityLookup; - type Header = Header; - type RuntimeEvent = RuntimeEvent; - type BlockHashCount = ConstU64<250>; - type Version = (); - type PalletInfo = PalletInfo; - type AccountData = pallet_balances::AccountData; - type OnNewAccount = (); - type OnKilledAccount = (); - type SystemWeightInfo = (); - type SS58Prefix = (); - type OnSetCode = (); - type MaxConsumers = ConstU32<16>; - } - - impl pallet_balances::Config for Runtime { - type Balance = u64; - type RuntimeEvent = RuntimeEvent; - type DustRemoval = (); - type ExistentialDeposit = ConstU64<1>; - type AccountStore = System; - type MaxLocks = (); - type MaxReserves = (); - type ReserveIdentifier = [u8; 8]; - type WeightInfo = (); - } - - impl WeightToFeeT for WeightToFee { - type Balance = u64; - - fn weight_to_fee(weight: &Weight) -> Self::Balance { - Self::Balance::saturated_from(weight.ref_time()) - .saturating_mul(WEIGHT_TO_FEE.with(|v| *v.borrow())) - } - } - - impl WeightToFeeT for TransactionByteFee { - type Balance = u64; - - fn weight_to_fee(weight: &Weight) -> Self::Balance { - Self::Balance::saturated_from(weight.ref_time()) - .saturating_mul(TRANSACTION_BYTE_FEE.with(|v| *v.borrow())) - } - } - - parameter_types! { - static TipUnbalancedAmount: u64 = 0; - static FeeUnbalancedAmount: u64 = 0; - } - - pub struct DealWithFees; - impl OnUnbalanced> for DealWithFees { - fn on_unbalanceds( - mut fees_then_tips: impl Iterator>, - ) { - if let Some(fees) = fees_then_tips.next() { - FeeUnbalancedAmount::mutate(|a| *a += fees.peek()); - if let Some(tips) = fees_then_tips.next() { - TipUnbalancedAmount::mutate(|a| *a += tips.peek()); - } - } - } - } - - impl Config for Runtime { - type RuntimeEvent = RuntimeEvent; - type OnChargeTransaction = CurrencyAdapter; - type OperationalFeeMultiplier = OperationalFeeMultiplier; - type WeightToFee = WeightToFee; - type LengthToFee = TransactionByteFee; - type FeeMultiplierUpdate = (); - } - - pub struct ExtBuilder { - balance_factor: u64, - base_weight: Weight, - byte_fee: u64, - weight_to_fee: u64, - initial_multiplier: Option, - } - - impl Default for ExtBuilder { - fn default() -> Self { - Self { - balance_factor: 1, - base_weight: Weight::zero(), - byte_fee: 1, - weight_to_fee: 1, - initial_multiplier: None, - } - } - } - - impl ExtBuilder { - pub fn base_weight(mut self, base_weight: Weight) -> Self { - self.base_weight = base_weight; - self - } - pub fn byte_fee(mut self, byte_fee: u64) -> Self { - self.byte_fee = byte_fee; - self - } - pub fn weight_fee(mut self, weight_to_fee: u64) -> Self { - self.weight_to_fee = weight_to_fee; - self - } - pub fn balance_factor(mut self, factor: u64) -> Self { - self.balance_factor = factor; - self - } - pub fn with_initial_multiplier(mut self, multiplier: Multiplier) -> Self { - self.initial_multiplier = Some(multiplier); - self - } - fn set_constants(&self) { - ExtrinsicBaseWeight::mutate(|v| *v = self.base_weight); - TRANSACTION_BYTE_FEE.with(|v| *v.borrow_mut() = self.byte_fee); - WEIGHT_TO_FEE.with(|v| *v.borrow_mut() = self.weight_to_fee); - } - pub fn build(self) -> sp_io::TestExternalities { - self.set_constants(); - let mut t = frame_system::GenesisConfig::default().build_storage::().unwrap(); - pallet_balances::GenesisConfig:: { - balances: if self.balance_factor > 0 { - vec![ - (1, 10 * self.balance_factor), - (2, 20 * self.balance_factor), - (3, 30 * self.balance_factor), - (4, 40 * self.balance_factor), - (5, 50 * self.balance_factor), - (6, 60 * self.balance_factor), - ] - } else { - vec![] - }, - } - .assimilate_storage(&mut t) - .unwrap(); - - if let Some(multiplier) = self.initial_multiplier { - let genesis = pallet::GenesisConfig { multiplier }; - GenesisBuild::::assimilate_storage(&genesis, &mut t).unwrap(); - } - - t.into() - } - } - - /// create a transaction info struct from weight. Handy to avoid building the whole struct. - pub fn info_from_weight(w: Weight) -> DispatchInfo { - // pays_fee: Pays::Yes -- class: DispatchClass::Normal - DispatchInfo { weight: w, ..Default::default() } - } - - fn post_info_from_weight(w: Weight) -> PostDispatchInfo { - PostDispatchInfo { actual_weight: Some(w), pays_fee: Default::default() } - } - - fn post_info_from_pays(p: Pays) -> PostDispatchInfo { - PostDispatchInfo { actual_weight: None, pays_fee: p } - } - - fn default_post_info() -> PostDispatchInfo { - PostDispatchInfo { actual_weight: None, pays_fee: Default::default() } - } - - #[test] - fn signed_extension_transaction_payment_work() { - ExtBuilder::default() - .balance_factor(10) - .base_weight(Weight::from_ref_time(5)) - .build() - .execute_with(|| { - let len = 10; - let pre = ChargeTransactionPayment::::from(0) - .pre_dispatch(&1, CALL, &info_from_weight(Weight::from_ref_time(5)), len) - .unwrap(); - assert_eq!(Balances::free_balance(1), 100 - 5 - 5 - 10); - - assert_ok!(ChargeTransactionPayment::::post_dispatch( - Some(pre), - &info_from_weight(Weight::from_ref_time(5)), - &default_post_info(), - len, - &Ok(()) - )); - assert_eq!(Balances::free_balance(1), 100 - 5 - 5 - 10); - assert_eq!(FeeUnbalancedAmount::get(), 5 + 5 + 10); - assert_eq!(TipUnbalancedAmount::get(), 0); - - FeeUnbalancedAmount::mutate(|a| *a = 0); - - let pre = ChargeTransactionPayment::::from(5 /* tipped */) - .pre_dispatch(&2, CALL, &info_from_weight(Weight::from_ref_time(100)), len) - .unwrap(); - assert_eq!(Balances::free_balance(2), 200 - 5 - 10 - 100 - 5); - - assert_ok!(ChargeTransactionPayment::::post_dispatch( - Some(pre), - &info_from_weight(Weight::from_ref_time(100)), - &post_info_from_weight(Weight::from_ref_time(50)), - len, - &Ok(()) - )); - assert_eq!(Balances::free_balance(2), 200 - 5 - 10 - 50 - 5); - assert_eq!(FeeUnbalancedAmount::get(), 5 + 10 + 50); - assert_eq!(TipUnbalancedAmount::get(), 5); - }); - } - - #[test] - fn signed_extension_transaction_payment_multiplied_refund_works() { - ExtBuilder::default() - .balance_factor(10) - .base_weight(Weight::from_ref_time(5)) - .build() - .execute_with(|| { - let len = 10; - >::put(Multiplier::saturating_from_rational(3, 2)); - - let pre = ChargeTransactionPayment::::from(5 /* tipped */) - .pre_dispatch(&2, CALL, &info_from_weight(Weight::from_ref_time(100)), len) - .unwrap(); - // 5 base fee, 10 byte fee, 3/2 * 100 weight fee, 5 tip - assert_eq!(Balances::free_balance(2), 200 - 5 - 10 - 150 - 5); - - assert_ok!(ChargeTransactionPayment::::post_dispatch( - Some(pre), - &info_from_weight(Weight::from_ref_time(100)), - &post_info_from_weight(Weight::from_ref_time(50)), - len, - &Ok(()) - )); - // 75 (3/2 of the returned 50 units of weight) is refunded - assert_eq!(Balances::free_balance(2), 200 - 5 - 10 - 75 - 5); - }); - } - - #[test] - fn signed_extension_transaction_payment_is_bounded() { - ExtBuilder::default().balance_factor(1000).byte_fee(0).build().execute_with(|| { - // maximum weight possible - assert_ok!(ChargeTransactionPayment::::from(0).pre_dispatch( - &1, - CALL, - &info_from_weight(Weight::MAX), - 10 - )); - // fee will be proportional to what is the actual maximum weight in the runtime. - assert_eq!( - Balances::free_balance(&1), - (10000 - - ::BlockWeights::get().max_block.ref_time()) as u64 - ); - }); - } - - #[test] - fn signed_extension_allows_free_transactions() { - ExtBuilder::default() - .base_weight(Weight::from_ref_time(100)) - .balance_factor(0) - .build() - .execute_with(|| { - // 1 ain't have a penny. - assert_eq!(Balances::free_balance(1), 0); - - let len = 100; - - // This is a completely free (and thus wholly insecure/DoS-ridden) transaction. - let operational_transaction = DispatchInfo { - weight: Weight::from_ref_time(0), - class: DispatchClass::Operational, - pays_fee: Pays::No, - }; - assert_ok!(ChargeTransactionPayment::::from(0).validate( - &1, - CALL, - &operational_transaction, - len - )); - - // like a InsecureFreeNormal - let free_transaction = DispatchInfo { - weight: Weight::from_ref_time(0), - class: DispatchClass::Normal, - pays_fee: Pays::Yes, - }; - assert_noop!( - ChargeTransactionPayment::::from(0).validate( - &1, - CALL, - &free_transaction, - len - ), - TransactionValidityError::Invalid(InvalidTransaction::Payment), - ); - }); - } - - #[test] - fn signed_ext_length_fee_is_also_updated_per_congestion() { - ExtBuilder::default() - .base_weight(Weight::from_ref_time(5)) - .balance_factor(10) - .build() - .execute_with(|| { - // all fees should be x1.5 - >::put(Multiplier::saturating_from_rational(3, 2)); - let len = 10; - - assert_ok!( - ChargeTransactionPayment::::from(10) // tipped - .pre_dispatch(&1, CALL, &info_from_weight(Weight::from_ref_time(3)), len) - ); - assert_eq!( - Balances::free_balance(1), - 100 // original - - 10 // tip - - 5 // base - - 10 // len - - (3 * 3 / 2) // adjusted weight - ); - }) - } - - #[test] - fn query_info_and_fee_details_works() { - let call = RuntimeCall::Balances(BalancesCall::transfer { dest: 2, value: 69 }); - let origin = 111111; - let extra = (); - let xt = TestXt::new(call.clone(), Some((origin, extra))); - let info = xt.get_dispatch_info(); - let ext = xt.encode(); - let len = ext.len() as u32; - - let unsigned_xt = TestXt::<_, ()>::new(call, None); - let unsigned_xt_info = unsigned_xt.get_dispatch_info(); - - ExtBuilder::default() - .base_weight(Weight::from_ref_time(5)) - .weight_fee(2) - .build() - .execute_with(|| { - // all fees should be x1.5 - >::put(Multiplier::saturating_from_rational(3, 2)); - - assert_eq!( - TransactionPayment::query_info(xt.clone(), len), - RuntimeDispatchInfo { - weight: info.weight, - class: info.class, - partial_fee: 5 * 2 /* base * weight_fee */ - + len as u64 /* len * 1 */ - + info.weight.min(BlockWeights::get().max_block).ref_time() as u64 * 2 * 3 / 2 /* weight */ - }, - ); - - assert_eq!( - TransactionPayment::query_info(unsigned_xt.clone(), len), - RuntimeDispatchInfo { - weight: unsigned_xt_info.weight, - class: unsigned_xt_info.class, - partial_fee: 0, - }, - ); - - assert_eq!( - TransactionPayment::query_fee_details(xt, len), - FeeDetails { - inclusion_fee: Some(InclusionFee { - base_fee: 5 * 2, - len_fee: len as u64, - adjusted_weight_fee: info - .weight - .min(BlockWeights::get().max_block) - .ref_time() as u64 * 2 * 3 / 2 - }), - tip: 0, - }, - ); - - assert_eq!( - TransactionPayment::query_fee_details(unsigned_xt, len), - FeeDetails { inclusion_fee: None, tip: 0 }, - ); - }); - } - - #[test] - fn query_call_info_and_fee_details_works() { - let call = RuntimeCall::Balances(BalancesCall::transfer { dest: 2, value: 69 }); - let info = call.get_dispatch_info(); - let encoded_call = call.encode(); - let len = encoded_call.len() as u32; - - ExtBuilder::default() - .base_weight(Weight::from_ref_time(5)) - .weight_fee(2) - .build() - .execute_with(|| { - // all fees should be x1.5 - >::put(Multiplier::saturating_from_rational(3, 2)); - - assert_eq!( - TransactionPayment::query_call_info(call.clone(), len), - RuntimeDispatchInfo { - weight: info.weight, - class: info.class, - partial_fee: 5 * 2 /* base * weight_fee */ - + len as u64 /* len * 1 */ - + info.weight.min(BlockWeights::get().max_block).ref_time() as u64 * 2 * 3 / 2 /* weight */ - }, - ); - - assert_eq!( - TransactionPayment::query_call_fee_details(call, len), - FeeDetails { - inclusion_fee: Some(InclusionFee { - base_fee: 5 * 2, /* base * weight_fee */ - len_fee: len as u64, /* len * 1 */ - adjusted_weight_fee: info - .weight - .min(BlockWeights::get().max_block) - .ref_time() as u64 * 2 * 3 / 2 /* weight * weight_fee * multipler */ - }), - tip: 0, - }, - ); - }); - } - - #[test] - fn compute_fee_works_without_multiplier() { - ExtBuilder::default() - .base_weight(Weight::from_ref_time(100)) - .byte_fee(10) - .balance_factor(0) - .build() - .execute_with(|| { - // Next fee multiplier is zero - assert_eq!(>::get(), Multiplier::one()); - - // Tip only, no fees works - let dispatch_info = DispatchInfo { - weight: Weight::from_ref_time(0), - class: DispatchClass::Operational, - pays_fee: Pays::No, - }; - assert_eq!(Pallet::::compute_fee(0, &dispatch_info, 10), 10); - // No tip, only base fee works - let dispatch_info = DispatchInfo { - weight: Weight::from_ref_time(0), - class: DispatchClass::Operational, - pays_fee: Pays::Yes, - }; - assert_eq!(Pallet::::compute_fee(0, &dispatch_info, 0), 100); - // Tip + base fee works - assert_eq!(Pallet::::compute_fee(0, &dispatch_info, 69), 169); - // Len (byte fee) + base fee works - assert_eq!(Pallet::::compute_fee(42, &dispatch_info, 0), 520); - // Weight fee + base fee works - let dispatch_info = DispatchInfo { - weight: Weight::from_ref_time(1000), - class: DispatchClass::Operational, - pays_fee: Pays::Yes, - }; - assert_eq!(Pallet::::compute_fee(0, &dispatch_info, 0), 1100); - }); - } - - #[test] - fn compute_fee_works_with_multiplier() { - ExtBuilder::default() - .base_weight(Weight::from_ref_time(100)) - .byte_fee(10) - .balance_factor(0) - .build() - .execute_with(|| { - // Add a next fee multiplier. Fees will be x3/2. - >::put(Multiplier::saturating_from_rational(3, 2)); - // Base fee is unaffected by multiplier - let dispatch_info = DispatchInfo { - weight: Weight::from_ref_time(0), - class: DispatchClass::Operational, - pays_fee: Pays::Yes, - }; - assert_eq!(Pallet::::compute_fee(0, &dispatch_info, 0), 100); - - // Everything works together :) - let dispatch_info = DispatchInfo { - weight: Weight::from_ref_time(123), - class: DispatchClass::Operational, - pays_fee: Pays::Yes, - }; - // 123 weight, 456 length, 100 base - assert_eq!( - Pallet::::compute_fee(456, &dispatch_info, 789), - 100 + (3 * 123 / 2) + 4560 + 789, - ); - }); - } - - #[test] - fn compute_fee_works_with_negative_multiplier() { - ExtBuilder::default() - .base_weight(Weight::from_ref_time(100)) - .byte_fee(10) - .balance_factor(0) - .build() - .execute_with(|| { - // Add a next fee multiplier. All fees will be x1/2. - >::put(Multiplier::saturating_from_rational(1, 2)); - - // Base fee is unaffected by multiplier. - let dispatch_info = DispatchInfo { - weight: Weight::from_ref_time(0), - class: DispatchClass::Operational, - pays_fee: Pays::Yes, - }; - assert_eq!(Pallet::::compute_fee(0, &dispatch_info, 0), 100); - - // Everything works together. - let dispatch_info = DispatchInfo { - weight: Weight::from_ref_time(123), - class: DispatchClass::Operational, - pays_fee: Pays::Yes, - }; - // 123 weight, 456 length, 100 base - assert_eq!( - Pallet::::compute_fee(456, &dispatch_info, 789), - 100 + (123 / 2) + 4560 + 789, - ); - }); - } - - #[test] - fn compute_fee_does_not_overflow() { - ExtBuilder::default() - .base_weight(Weight::from_ref_time(100)) - .byte_fee(10) - .balance_factor(0) - .build() - .execute_with(|| { - // Overflow is handled - let dispatch_info = DispatchInfo { - weight: Weight::MAX, - class: DispatchClass::Operational, - pays_fee: Pays::Yes, - }; - assert_eq!( - Pallet::::compute_fee(u32::MAX, &dispatch_info, u64::MAX), - u64::MAX - ); - }); - } - - #[test] - fn refund_does_not_recreate_account() { - ExtBuilder::default() - .balance_factor(10) - .base_weight(Weight::from_ref_time(5)) - .build() - .execute_with(|| { - // So events are emitted - System::set_block_number(10); - let len = 10; - let pre = ChargeTransactionPayment::::from(5 /* tipped */) - .pre_dispatch(&2, CALL, &info_from_weight(Weight::from_ref_time(100)), len) - .unwrap(); - assert_eq!(Balances::free_balance(2), 200 - 5 - 10 - 100 - 5); - - // kill the account between pre and post dispatch - assert_ok!(Balances::transfer(Some(2).into(), 3, Balances::free_balance(2))); - assert_eq!(Balances::free_balance(2), 0); - - assert_ok!(ChargeTransactionPayment::::post_dispatch( - Some(pre), - &info_from_weight(Weight::from_ref_time(100)), - &post_info_from_weight(Weight::from_ref_time(50)), - len, - &Ok(()) - )); - assert_eq!(Balances::free_balance(2), 0); - // Transfer Event - System::assert_has_event(RuntimeEvent::Balances( - pallet_balances::Event::Transfer { from: 2, to: 3, amount: 80 }, - )); - // Killed Event - System::assert_has_event(RuntimeEvent::System(system::Event::KilledAccount { - account: 2, - })); - }); - } - - #[test] - fn actual_weight_higher_than_max_refunds_nothing() { - ExtBuilder::default() - .balance_factor(10) - .base_weight(Weight::from_ref_time(5)) - .build() - .execute_with(|| { - let len = 10; - let pre = ChargeTransactionPayment::::from(5 /* tipped */) - .pre_dispatch(&2, CALL, &info_from_weight(Weight::from_ref_time(100)), len) - .unwrap(); - assert_eq!(Balances::free_balance(2), 200 - 5 - 10 - 100 - 5); - - assert_ok!(ChargeTransactionPayment::::post_dispatch( - Some(pre), - &info_from_weight(Weight::from_ref_time(100)), - &post_info_from_weight(Weight::from_ref_time(101)), - len, - &Ok(()) - )); - assert_eq!(Balances::free_balance(2), 200 - 5 - 10 - 100 - 5); - }); - } - - #[test] - fn zero_transfer_on_free_transaction() { - ExtBuilder::default() - .balance_factor(10) - .base_weight(Weight::from_ref_time(5)) - .build() - .execute_with(|| { - // So events are emitted - System::set_block_number(10); - let len = 10; - let dispatch_info = DispatchInfo { - weight: Weight::from_ref_time(100), - pays_fee: Pays::No, - class: DispatchClass::Normal, - }; - let user = 69; - let pre = ChargeTransactionPayment::::from(0) - .pre_dispatch(&user, CALL, &dispatch_info, len) - .unwrap(); - assert_eq!(Balances::total_balance(&user), 0); - assert_ok!(ChargeTransactionPayment::::post_dispatch( - Some(pre), - &dispatch_info, - &default_post_info(), - len, - &Ok(()) - )); - assert_eq!(Balances::total_balance(&user), 0); - // TransactionFeePaid Event - System::assert_has_event(RuntimeEvent::TransactionPayment( - pallet_transaction_payment::Event::TransactionFeePaid { - who: user, - actual_fee: 0, - tip: 0, - }, - )); - }); - } - - #[test] - fn refund_consistent_with_actual_weight() { - ExtBuilder::default() - .balance_factor(10) - .base_weight(Weight::from_ref_time(7)) - .build() - .execute_with(|| { - let info = info_from_weight(Weight::from_ref_time(100)); - let post_info = post_info_from_weight(Weight::from_ref_time(33)); - let prev_balance = Balances::free_balance(2); - let len = 10; - let tip = 5; - - >::put(Multiplier::saturating_from_rational(5, 4)); - - let pre = ChargeTransactionPayment::::from(tip) - .pre_dispatch(&2, CALL, &info, len) - .unwrap(); - - ChargeTransactionPayment::::post_dispatch( - Some(pre), - &info, - &post_info, - len, - &Ok(()), - ) - .unwrap(); - - let refund_based_fee = prev_balance - Balances::free_balance(2); - let actual_fee = - Pallet::::compute_actual_fee(len as u32, &info, &post_info, tip); - - // 33 weight, 10 length, 7 base, 5 tip - assert_eq!(actual_fee, 7 + 10 + (33 * 5 / 4) + 5); - assert_eq!(refund_based_fee, actual_fee); - }); - } - - #[test] - fn should_alter_operational_priority() { - let tip = 5; - let len = 10; - - ExtBuilder::default().balance_factor(100).build().execute_with(|| { - let normal = DispatchInfo { - weight: Weight::from_ref_time(100), - class: DispatchClass::Normal, - pays_fee: Pays::Yes, - }; - let priority = ChargeTransactionPayment::(tip) - .validate(&2, CALL, &normal, len) - .unwrap() - .priority; - - assert_eq!(priority, 60); - - let priority = ChargeTransactionPayment::(2 * tip) - .validate(&2, CALL, &normal, len) - .unwrap() - .priority; - - assert_eq!(priority, 110); - }); - - ExtBuilder::default().balance_factor(100).build().execute_with(|| { - let op = DispatchInfo { - weight: Weight::from_ref_time(100), - class: DispatchClass::Operational, - pays_fee: Pays::Yes, - }; - let priority = ChargeTransactionPayment::(tip) - .validate(&2, CALL, &op, len) - .unwrap() - .priority; - assert_eq!(priority, 5810); - - let priority = ChargeTransactionPayment::(2 * tip) - .validate(&2, CALL, &op, len) - .unwrap() - .priority; - assert_eq!(priority, 6110); - }); - } - - #[test] - fn no_tip_has_some_priority() { - let tip = 0; - let len = 10; - - ExtBuilder::default().balance_factor(100).build().execute_with(|| { - let normal = DispatchInfo { - weight: Weight::from_ref_time(100), - class: DispatchClass::Normal, - pays_fee: Pays::Yes, - }; - let priority = ChargeTransactionPayment::(tip) - .validate(&2, CALL, &normal, len) - .unwrap() - .priority; - - assert_eq!(priority, 10); - }); - - ExtBuilder::default().balance_factor(100).build().execute_with(|| { - let op = DispatchInfo { - weight: Weight::from_ref_time(100), - class: DispatchClass::Operational, - pays_fee: Pays::Yes, - }; - let priority = ChargeTransactionPayment::(tip) - .validate(&2, CALL, &op, len) - .unwrap() - .priority; - assert_eq!(priority, 5510); - }); - } - - #[test] - fn higher_tip_have_higher_priority() { - let get_priorities = |tip: u64| { - let mut priority1 = 0; - let mut priority2 = 0; - let len = 10; - ExtBuilder::default().balance_factor(100).build().execute_with(|| { - let normal = DispatchInfo { - weight: Weight::from_ref_time(100), - class: DispatchClass::Normal, - pays_fee: Pays::Yes, - }; - priority1 = ChargeTransactionPayment::(tip) - .validate(&2, CALL, &normal, len) - .unwrap() - .priority; - }); - - ExtBuilder::default().balance_factor(100).build().execute_with(|| { - let op = DispatchInfo { - weight: Weight::from_ref_time(100), - class: DispatchClass::Operational, - pays_fee: Pays::Yes, - }; - priority2 = ChargeTransactionPayment::(tip) - .validate(&2, CALL, &op, len) - .unwrap() - .priority; - }); - - (priority1, priority2) - }; - - let mut prev_priorities = get_priorities(0); - - for tip in 1..3 { - let priorities = get_priorities(tip); - assert!(prev_priorities.0 < priorities.0); - assert!(prev_priorities.1 < priorities.1); - prev_priorities = priorities; - } - } - - #[test] - fn post_info_can_change_pays_fee() { - ExtBuilder::default() - .balance_factor(10) - .base_weight(Weight::from_ref_time(7)) - .build() - .execute_with(|| { - let info = info_from_weight(Weight::from_ref_time(100)); - let post_info = post_info_from_pays(Pays::No); - let prev_balance = Balances::free_balance(2); - let len = 10; - let tip = 5; - - >::put(Multiplier::saturating_from_rational(5, 4)); - - let pre = ChargeTransactionPayment::::from(tip) - .pre_dispatch(&2, CALL, &info, len) - .unwrap(); - - ChargeTransactionPayment::::post_dispatch( - Some(pre), - &info, - &post_info, - len, - &Ok(()), - ) - .unwrap(); - - let refund_based_fee = prev_balance - Balances::free_balance(2); - let actual_fee = - Pallet::::compute_actual_fee(len as u32, &info, &post_info, tip); - - // Only 5 tip is paid - assert_eq!(actual_fee, 5); - assert_eq!(refund_based_fee, actual_fee); - }); - } - - #[test] - fn genesis_config_works() { - ExtBuilder::default() - .with_initial_multiplier(Multiplier::from_u32(100)) - .build() - .execute_with(|| { - assert_eq!( - >::get(), - Multiplier::saturating_from_integer(100) - ); - }); - } - - #[test] - fn genesis_default_works() { - ExtBuilder::default().build().execute_with(|| { - assert_eq!(>::get(), Multiplier::saturating_from_integer(1)); - }); - } -} diff --git a/frame/transaction-payment/src/mock.rs b/frame/transaction-payment/src/mock.rs new file mode 100644 index 000000000..e214458b3 --- /dev/null +++ b/frame/transaction-payment/src/mock.rs @@ -0,0 +1,162 @@ +// This file is part of Substrate. + +// Copyright (C) 2019-2022 Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +use super::*; +use crate as pallet_transaction_payment; + +use sp_core::H256; +use sp_runtime::{ + testing::Header, + traits::{BlakeTwo256, IdentityLookup}, +}; + +use frame_support::{ + dispatch::DispatchClass, + parameter_types, + traits::{ConstU32, ConstU64, Imbalance, OnUnbalanced}, + weights::{Weight, WeightToFee as WeightToFeeT}, +}; +use frame_system as system; +use pallet_balances::Call as BalancesCall; + +type UncheckedExtrinsic = frame_system::mocking::MockUncheckedExtrinsic; +type Block = frame_system::mocking::MockBlock; + +frame_support::construct_runtime!( + pub enum Runtime where + Block = Block, + NodeBlock = Block, + UncheckedExtrinsic = UncheckedExtrinsic, + { + System: system::{Pallet, Call, Config, Storage, Event}, + Balances: pallet_balances::{Pallet, Call, Storage, Config, Event}, + TransactionPayment: pallet_transaction_payment::{Pallet, Storage, Event}, + } +); + +pub(crate) const CALL: &::RuntimeCall = + &RuntimeCall::Balances(BalancesCall::transfer { dest: 2, value: 69 }); + +parameter_types! { + pub(crate) static ExtrinsicBaseWeight: Weight = Weight::zero(); +} + +pub struct BlockWeights; +impl Get for BlockWeights { + fn get() -> frame_system::limits::BlockWeights { + frame_system::limits::BlockWeights::builder() + .base_block(Weight::zero()) + .for_class(DispatchClass::all(), |weights| { + weights.base_extrinsic = ExtrinsicBaseWeight::get().into(); + }) + .for_class(DispatchClass::non_mandatory(), |weights| { + weights.max_total = Weight::from_ref_time(1024).set_proof_size(u64::MAX).into(); + }) + .build_or_panic() + } +} + +parameter_types! { + pub static WeightToFee: u64 = 1; + pub static TransactionByteFee: u64 = 1; + pub static OperationalFeeMultiplier: u8 = 5; +} + +impl frame_system::Config for Runtime { + type BaseCallFilter = frame_support::traits::Everything; + type BlockWeights = BlockWeights; + type BlockLength = (); + type DbWeight = (); + type RuntimeOrigin = RuntimeOrigin; + type Index = u64; + type BlockNumber = u64; + type RuntimeCall = RuntimeCall; + type Hash = H256; + type Hashing = BlakeTwo256; + type AccountId = u64; + type Lookup = IdentityLookup; + type Header = Header; + type RuntimeEvent = RuntimeEvent; + type BlockHashCount = ConstU64<250>; + type Version = (); + type PalletInfo = PalletInfo; + type AccountData = pallet_balances::AccountData; + type OnNewAccount = (); + type OnKilledAccount = (); + type SystemWeightInfo = (); + type SS58Prefix = (); + type OnSetCode = (); + type MaxConsumers = ConstU32<16>; +} + +impl pallet_balances::Config for Runtime { + type Balance = u64; + type RuntimeEvent = RuntimeEvent; + type DustRemoval = (); + type ExistentialDeposit = ConstU64<1>; + type AccountStore = System; + type MaxLocks = (); + type MaxReserves = (); + type ReserveIdentifier = [u8; 8]; + type WeightInfo = (); +} + +impl WeightToFeeT for WeightToFee { + type Balance = u64; + + fn weight_to_fee(weight: &Weight) -> Self::Balance { + Self::Balance::saturated_from(weight.ref_time()) + .saturating_mul(WEIGHT_TO_FEE.with(|v| *v.borrow())) + } +} + +impl WeightToFeeT for TransactionByteFee { + type Balance = u64; + + fn weight_to_fee(weight: &Weight) -> Self::Balance { + Self::Balance::saturated_from(weight.ref_time()) + .saturating_mul(TRANSACTION_BYTE_FEE.with(|v| *v.borrow())) + } +} + +parameter_types! { + pub(crate) static TipUnbalancedAmount: u64 = 0; + pub(crate) static FeeUnbalancedAmount: u64 = 0; +} + +pub struct DealWithFees; +impl OnUnbalanced> for DealWithFees { + fn on_unbalanceds( + mut fees_then_tips: impl Iterator>, + ) { + if let Some(fees) = fees_then_tips.next() { + FeeUnbalancedAmount::mutate(|a| *a += fees.peek()); + if let Some(tips) = fees_then_tips.next() { + TipUnbalancedAmount::mutate(|a| *a += tips.peek()); + } + } + } +} + +impl Config for Runtime { + type RuntimeEvent = RuntimeEvent; + type OnChargeTransaction = CurrencyAdapter; + type OperationalFeeMultiplier = OperationalFeeMultiplier; + type WeightToFee = WeightToFee; + type LengthToFee = TransactionByteFee; + type FeeMultiplierUpdate = (); +} diff --git a/frame/transaction-payment/src/tests.rs b/frame/transaction-payment/src/tests.rs new file mode 100644 index 000000000..ee54c1130 --- /dev/null +++ b/frame/transaction-payment/src/tests.rs @@ -0,0 +1,836 @@ +// This file is part of Substrate. + +// Copyright (C) 2019-2022 Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +use super::*; +use crate as pallet_transaction_payment; + +use codec::Encode; + +use sp_runtime::{testing::TestXt, traits::One, transaction_validity::InvalidTransaction}; + +use frame_support::{ + assert_noop, assert_ok, + dispatch::{DispatchClass, DispatchInfo, GetDispatchInfo, PostDispatchInfo}, + traits::{Currency, GenesisBuild}, + weights::Weight, +}; +use frame_system as system; +use mock::*; +use pallet_balances::Call as BalancesCall; + +pub struct ExtBuilder { + balance_factor: u64, + base_weight: Weight, + byte_fee: u64, + weight_to_fee: u64, + initial_multiplier: Option, +} + +impl Default for ExtBuilder { + fn default() -> Self { + Self { + balance_factor: 1, + base_weight: Weight::zero(), + byte_fee: 1, + weight_to_fee: 1, + initial_multiplier: None, + } + } +} + +impl ExtBuilder { + pub fn base_weight(mut self, base_weight: Weight) -> Self { + self.base_weight = base_weight; + self + } + pub fn byte_fee(mut self, byte_fee: u64) -> Self { + self.byte_fee = byte_fee; + self + } + pub fn weight_fee(mut self, weight_to_fee: u64) -> Self { + self.weight_to_fee = weight_to_fee; + self + } + pub fn balance_factor(mut self, factor: u64) -> Self { + self.balance_factor = factor; + self + } + pub fn with_initial_multiplier(mut self, multiplier: Multiplier) -> Self { + self.initial_multiplier = Some(multiplier); + self + } + fn set_constants(&self) { + ExtrinsicBaseWeight::mutate(|v| *v = self.base_weight); + TRANSACTION_BYTE_FEE.with(|v| *v.borrow_mut() = self.byte_fee); + WEIGHT_TO_FEE.with(|v| *v.borrow_mut() = self.weight_to_fee); + } + pub fn build(self) -> sp_io::TestExternalities { + self.set_constants(); + let mut t = frame_system::GenesisConfig::default().build_storage::().unwrap(); + pallet_balances::GenesisConfig:: { + balances: if self.balance_factor > 0 { + vec![ + (1, 10 * self.balance_factor), + (2, 20 * self.balance_factor), + (3, 30 * self.balance_factor), + (4, 40 * self.balance_factor), + (5, 50 * self.balance_factor), + (6, 60 * self.balance_factor), + ] + } else { + vec![] + }, + } + .assimilate_storage(&mut t) + .unwrap(); + + if let Some(multiplier) = self.initial_multiplier { + let genesis = pallet::GenesisConfig { multiplier }; + GenesisBuild::::assimilate_storage(&genesis, &mut t).unwrap(); + } + + t.into() + } +} + +/// create a transaction info struct from weight. Handy to avoid building the whole struct. +pub fn info_from_weight(w: Weight) -> DispatchInfo { + // pays_fee: Pays::Yes -- class: DispatchClass::Normal + DispatchInfo { weight: w, ..Default::default() } +} + +fn post_info_from_weight(w: Weight) -> PostDispatchInfo { + PostDispatchInfo { actual_weight: Some(w), pays_fee: Default::default() } +} + +fn post_info_from_pays(p: Pays) -> PostDispatchInfo { + PostDispatchInfo { actual_weight: None, pays_fee: p } +} + +fn default_post_info() -> PostDispatchInfo { + PostDispatchInfo { actual_weight: None, pays_fee: Default::default() } +} + +#[test] +fn signed_extension_transaction_payment_work() { + ExtBuilder::default() + .balance_factor(10) + .base_weight(Weight::from_ref_time(5)) + .build() + .execute_with(|| { + let len = 10; + let pre = ChargeTransactionPayment::::from(0) + .pre_dispatch(&1, CALL, &info_from_weight(Weight::from_ref_time(5)), len) + .unwrap(); + assert_eq!(Balances::free_balance(1), 100 - 5 - 5 - 10); + + assert_ok!(ChargeTransactionPayment::::post_dispatch( + Some(pre), + &info_from_weight(Weight::from_ref_time(5)), + &default_post_info(), + len, + &Ok(()) + )); + assert_eq!(Balances::free_balance(1), 100 - 5 - 5 - 10); + assert_eq!(FeeUnbalancedAmount::get(), 5 + 5 + 10); + assert_eq!(TipUnbalancedAmount::get(), 0); + + FeeUnbalancedAmount::mutate(|a| *a = 0); + + let pre = ChargeTransactionPayment::::from(5 /* tipped */) + .pre_dispatch(&2, CALL, &info_from_weight(Weight::from_ref_time(100)), len) + .unwrap(); + assert_eq!(Balances::free_balance(2), 200 - 5 - 10 - 100 - 5); + + assert_ok!(ChargeTransactionPayment::::post_dispatch( + Some(pre), + &info_from_weight(Weight::from_ref_time(100)), + &post_info_from_weight(Weight::from_ref_time(50)), + len, + &Ok(()) + )); + assert_eq!(Balances::free_balance(2), 200 - 5 - 10 - 50 - 5); + assert_eq!(FeeUnbalancedAmount::get(), 5 + 10 + 50); + assert_eq!(TipUnbalancedAmount::get(), 5); + }); +} + +#[test] +fn signed_extension_transaction_payment_multiplied_refund_works() { + ExtBuilder::default() + .balance_factor(10) + .base_weight(Weight::from_ref_time(5)) + .build() + .execute_with(|| { + let len = 10; + >::put(Multiplier::saturating_from_rational(3, 2)); + + let pre = ChargeTransactionPayment::::from(5 /* tipped */) + .pre_dispatch(&2, CALL, &info_from_weight(Weight::from_ref_time(100)), len) + .unwrap(); + // 5 base fee, 10 byte fee, 3/2 * 100 weight fee, 5 tip + assert_eq!(Balances::free_balance(2), 200 - 5 - 10 - 150 - 5); + + assert_ok!(ChargeTransactionPayment::::post_dispatch( + Some(pre), + &info_from_weight(Weight::from_ref_time(100)), + &post_info_from_weight(Weight::from_ref_time(50)), + len, + &Ok(()) + )); + // 75 (3/2 of the returned 50 units of weight) is refunded + assert_eq!(Balances::free_balance(2), 200 - 5 - 10 - 75 - 5); + }); +} + +#[test] +fn signed_extension_transaction_payment_is_bounded() { + ExtBuilder::default().balance_factor(1000).byte_fee(0).build().execute_with(|| { + // maximum weight possible + assert_ok!(ChargeTransactionPayment::::from(0).pre_dispatch( + &1, + CALL, + &info_from_weight(Weight::MAX), + 10 + )); + // fee will be proportional to what is the actual maximum weight in the runtime. + assert_eq!( + Balances::free_balance(&1), + (10000 - ::BlockWeights::get().max_block.ref_time()) + as u64 + ); + }); +} + +#[test] +fn signed_extension_allows_free_transactions() { + ExtBuilder::default() + .base_weight(Weight::from_ref_time(100)) + .balance_factor(0) + .build() + .execute_with(|| { + // 1 ain't have a penny. + assert_eq!(Balances::free_balance(1), 0); + + let len = 100; + + // This is a completely free (and thus wholly insecure/DoS-ridden) transaction. + let operational_transaction = DispatchInfo { + weight: Weight::from_ref_time(0), + class: DispatchClass::Operational, + pays_fee: Pays::No, + }; + assert_ok!(ChargeTransactionPayment::::from(0).validate( + &1, + CALL, + &operational_transaction, + len + )); + + // like a InsecureFreeNormal + let free_transaction = DispatchInfo { + weight: Weight::from_ref_time(0), + class: DispatchClass::Normal, + pays_fee: Pays::Yes, + }; + assert_noop!( + ChargeTransactionPayment::::from(0).validate( + &1, + CALL, + &free_transaction, + len + ), + TransactionValidityError::Invalid(InvalidTransaction::Payment), + ); + }); +} + +#[test] +fn signed_ext_length_fee_is_also_updated_per_congestion() { + ExtBuilder::default() + .base_weight(Weight::from_ref_time(5)) + .balance_factor(10) + .build() + .execute_with(|| { + // all fees should be x1.5 + >::put(Multiplier::saturating_from_rational(3, 2)); + let len = 10; + + assert_ok!(ChargeTransactionPayment::::from(10) // tipped + .pre_dispatch(&1, CALL, &info_from_weight(Weight::from_ref_time(3)), len)); + assert_eq!( + Balances::free_balance(1), + 100 // original + - 10 // tip + - 5 // base + - 10 // len + - (3 * 3 / 2) // adjusted weight + ); + }) +} + +#[test] +fn query_info_and_fee_details_works() { + let call = RuntimeCall::Balances(BalancesCall::transfer { dest: 2, value: 69 }); + let origin = 111111; + let extra = (); + let xt = TestXt::new(call.clone(), Some((origin, extra))); + let info = xt.get_dispatch_info(); + let ext = xt.encode(); + let len = ext.len() as u32; + + let unsigned_xt = TestXt::<_, ()>::new(call, None); + let unsigned_xt_info = unsigned_xt.get_dispatch_info(); + + ExtBuilder::default() + .base_weight(Weight::from_ref_time(5)) + .weight_fee(2) + .build() + .execute_with(|| { + // all fees should be x1.5 + >::put(Multiplier::saturating_from_rational(3, 2)); + + assert_eq!( + TransactionPayment::query_info(xt.clone(), len), + RuntimeDispatchInfo { + weight: info.weight, + class: info.class, + partial_fee: 5 * 2 /* base * weight_fee */ + + len as u64 /* len * 1 */ + + info.weight.min(BlockWeights::get().max_block).ref_time() as u64 * 2 * 3 / 2 /* weight */ + }, + ); + + assert_eq!( + TransactionPayment::query_info(unsigned_xt.clone(), len), + RuntimeDispatchInfo { + weight: unsigned_xt_info.weight, + class: unsigned_xt_info.class, + partial_fee: 0, + }, + ); + + assert_eq!( + TransactionPayment::query_fee_details(xt, len), + FeeDetails { + inclusion_fee: Some(InclusionFee { + base_fee: 5 * 2, + len_fee: len as u64, + adjusted_weight_fee: info + .weight + .min(BlockWeights::get().max_block) + .ref_time() as u64 * 2 * 3 / 2 + }), + tip: 0, + }, + ); + + assert_eq!( + TransactionPayment::query_fee_details(unsigned_xt, len), + FeeDetails { inclusion_fee: None, tip: 0 }, + ); + }); +} + +#[test] +fn query_call_info_and_fee_details_works() { + let call = RuntimeCall::Balances(BalancesCall::transfer { dest: 2, value: 69 }); + let info = call.get_dispatch_info(); + let encoded_call = call.encode(); + let len = encoded_call.len() as u32; + + ExtBuilder::default() + .base_weight(Weight::from_ref_time(5)) + .weight_fee(2) + .build() + .execute_with(|| { + // all fees should be x1.5 + >::put(Multiplier::saturating_from_rational(3, 2)); + + assert_eq!( + TransactionPayment::query_call_info(call.clone(), len), + RuntimeDispatchInfo { + weight: info.weight, + class: info.class, + partial_fee: 5 * 2 /* base * weight_fee */ + + len as u64 /* len * 1 */ + + info.weight.min(BlockWeights::get().max_block).ref_time() as u64 * 2 * 3 / 2 /* weight */ + }, + ); + + assert_eq!( + TransactionPayment::query_call_fee_details(call, len), + FeeDetails { + inclusion_fee: Some(InclusionFee { + base_fee: 5 * 2, /* base * weight_fee */ + len_fee: len as u64, /* len * 1 */ + adjusted_weight_fee: info + .weight + .min(BlockWeights::get().max_block) + .ref_time() as u64 * 2 * 3 / 2 /* weight * weight_fee * multipler */ + }), + tip: 0, + }, + ); + }); +} + +#[test] +fn compute_fee_works_without_multiplier() { + ExtBuilder::default() + .base_weight(Weight::from_ref_time(100)) + .byte_fee(10) + .balance_factor(0) + .build() + .execute_with(|| { + // Next fee multiplier is zero + assert_eq!(>::get(), Multiplier::one()); + + // Tip only, no fees works + let dispatch_info = DispatchInfo { + weight: Weight::from_ref_time(0), + class: DispatchClass::Operational, + pays_fee: Pays::No, + }; + assert_eq!(Pallet::::compute_fee(0, &dispatch_info, 10), 10); + // No tip, only base fee works + let dispatch_info = DispatchInfo { + weight: Weight::from_ref_time(0), + class: DispatchClass::Operational, + pays_fee: Pays::Yes, + }; + assert_eq!(Pallet::::compute_fee(0, &dispatch_info, 0), 100); + // Tip + base fee works + assert_eq!(Pallet::::compute_fee(0, &dispatch_info, 69), 169); + // Len (byte fee) + base fee works + assert_eq!(Pallet::::compute_fee(42, &dispatch_info, 0), 520); + // Weight fee + base fee works + let dispatch_info = DispatchInfo { + weight: Weight::from_ref_time(1000), + class: DispatchClass::Operational, + pays_fee: Pays::Yes, + }; + assert_eq!(Pallet::::compute_fee(0, &dispatch_info, 0), 1100); + }); +} + +#[test] +fn compute_fee_works_with_multiplier() { + ExtBuilder::default() + .base_weight(Weight::from_ref_time(100)) + .byte_fee(10) + .balance_factor(0) + .build() + .execute_with(|| { + // Add a next fee multiplier. Fees will be x3/2. + >::put(Multiplier::saturating_from_rational(3, 2)); + // Base fee is unaffected by multiplier + let dispatch_info = DispatchInfo { + weight: Weight::from_ref_time(0), + class: DispatchClass::Operational, + pays_fee: Pays::Yes, + }; + assert_eq!(Pallet::::compute_fee(0, &dispatch_info, 0), 100); + + // Everything works together :) + let dispatch_info = DispatchInfo { + weight: Weight::from_ref_time(123), + class: DispatchClass::Operational, + pays_fee: Pays::Yes, + }; + // 123 weight, 456 length, 100 base + assert_eq!( + Pallet::::compute_fee(456, &dispatch_info, 789), + 100 + (3 * 123 / 2) + 4560 + 789, + ); + }); +} + +#[test] +fn compute_fee_works_with_negative_multiplier() { + ExtBuilder::default() + .base_weight(Weight::from_ref_time(100)) + .byte_fee(10) + .balance_factor(0) + .build() + .execute_with(|| { + // Add a next fee multiplier. All fees will be x1/2. + >::put(Multiplier::saturating_from_rational(1, 2)); + + // Base fee is unaffected by multiplier. + let dispatch_info = DispatchInfo { + weight: Weight::from_ref_time(0), + class: DispatchClass::Operational, + pays_fee: Pays::Yes, + }; + assert_eq!(Pallet::::compute_fee(0, &dispatch_info, 0), 100); + + // Everything works together. + let dispatch_info = DispatchInfo { + weight: Weight::from_ref_time(123), + class: DispatchClass::Operational, + pays_fee: Pays::Yes, + }; + // 123 weight, 456 length, 100 base + assert_eq!( + Pallet::::compute_fee(456, &dispatch_info, 789), + 100 + (123 / 2) + 4560 + 789, + ); + }); +} + +#[test] +fn compute_fee_does_not_overflow() { + ExtBuilder::default() + .base_weight(Weight::from_ref_time(100)) + .byte_fee(10) + .balance_factor(0) + .build() + .execute_with(|| { + // Overflow is handled + let dispatch_info = DispatchInfo { + weight: Weight::MAX, + class: DispatchClass::Operational, + pays_fee: Pays::Yes, + }; + assert_eq!( + Pallet::::compute_fee(u32::MAX, &dispatch_info, u64::MAX), + u64::MAX + ); + }); +} + +#[test] +fn refund_does_not_recreate_account() { + ExtBuilder::default() + .balance_factor(10) + .base_weight(Weight::from_ref_time(5)) + .build() + .execute_with(|| { + // So events are emitted + System::set_block_number(10); + let len = 10; + let pre = ChargeTransactionPayment::::from(5 /* tipped */) + .pre_dispatch(&2, CALL, &info_from_weight(Weight::from_ref_time(100)), len) + .unwrap(); + assert_eq!(Balances::free_balance(2), 200 - 5 - 10 - 100 - 5); + + // kill the account between pre and post dispatch + assert_ok!(Balances::transfer(Some(2).into(), 3, Balances::free_balance(2))); + assert_eq!(Balances::free_balance(2), 0); + + assert_ok!(ChargeTransactionPayment::::post_dispatch( + Some(pre), + &info_from_weight(Weight::from_ref_time(100)), + &post_info_from_weight(Weight::from_ref_time(50)), + len, + &Ok(()) + )); + assert_eq!(Balances::free_balance(2), 0); + // Transfer Event + System::assert_has_event(RuntimeEvent::Balances(pallet_balances::Event::Transfer { + from: 2, + to: 3, + amount: 80, + })); + // Killed Event + System::assert_has_event(RuntimeEvent::System(system::Event::KilledAccount { + account: 2, + })); + }); +} + +#[test] +fn actual_weight_higher_than_max_refunds_nothing() { + ExtBuilder::default() + .balance_factor(10) + .base_weight(Weight::from_ref_time(5)) + .build() + .execute_with(|| { + let len = 10; + let pre = ChargeTransactionPayment::::from(5 /* tipped */) + .pre_dispatch(&2, CALL, &info_from_weight(Weight::from_ref_time(100)), len) + .unwrap(); + assert_eq!(Balances::free_balance(2), 200 - 5 - 10 - 100 - 5); + + assert_ok!(ChargeTransactionPayment::::post_dispatch( + Some(pre), + &info_from_weight(Weight::from_ref_time(100)), + &post_info_from_weight(Weight::from_ref_time(101)), + len, + &Ok(()) + )); + assert_eq!(Balances::free_balance(2), 200 - 5 - 10 - 100 - 5); + }); +} + +#[test] +fn zero_transfer_on_free_transaction() { + ExtBuilder::default() + .balance_factor(10) + .base_weight(Weight::from_ref_time(5)) + .build() + .execute_with(|| { + // So events are emitted + System::set_block_number(10); + let len = 10; + let dispatch_info = DispatchInfo { + weight: Weight::from_ref_time(100), + pays_fee: Pays::No, + class: DispatchClass::Normal, + }; + let user = 69; + let pre = ChargeTransactionPayment::::from(0) + .pre_dispatch(&user, CALL, &dispatch_info, len) + .unwrap(); + assert_eq!(Balances::total_balance(&user), 0); + assert_ok!(ChargeTransactionPayment::::post_dispatch( + Some(pre), + &dispatch_info, + &default_post_info(), + len, + &Ok(()) + )); + assert_eq!(Balances::total_balance(&user), 0); + // TransactionFeePaid Event + System::assert_has_event(RuntimeEvent::TransactionPayment( + pallet_transaction_payment::Event::TransactionFeePaid { + who: user, + actual_fee: 0, + tip: 0, + }, + )); + }); +} + +#[test] +fn refund_consistent_with_actual_weight() { + ExtBuilder::default() + .balance_factor(10) + .base_weight(Weight::from_ref_time(7)) + .build() + .execute_with(|| { + let info = info_from_weight(Weight::from_ref_time(100)); + let post_info = post_info_from_weight(Weight::from_ref_time(33)); + let prev_balance = Balances::free_balance(2); + let len = 10; + let tip = 5; + + >::put(Multiplier::saturating_from_rational(5, 4)); + + let pre = ChargeTransactionPayment::::from(tip) + .pre_dispatch(&2, CALL, &info, len) + .unwrap(); + + ChargeTransactionPayment::::post_dispatch( + Some(pre), + &info, + &post_info, + len, + &Ok(()), + ) + .unwrap(); + + let refund_based_fee = prev_balance - Balances::free_balance(2); + let actual_fee = + Pallet::::compute_actual_fee(len as u32, &info, &post_info, tip); + + // 33 weight, 10 length, 7 base, 5 tip + assert_eq!(actual_fee, 7 + 10 + (33 * 5 / 4) + 5); + assert_eq!(refund_based_fee, actual_fee); + }); +} + +#[test] +fn should_alter_operational_priority() { + let tip = 5; + let len = 10; + + ExtBuilder::default().balance_factor(100).build().execute_with(|| { + let normal = DispatchInfo { + weight: Weight::from_ref_time(100), + class: DispatchClass::Normal, + pays_fee: Pays::Yes, + }; + let priority = ChargeTransactionPayment::(tip) + .validate(&2, CALL, &normal, len) + .unwrap() + .priority; + + assert_eq!(priority, 60); + + let priority = ChargeTransactionPayment::(2 * tip) + .validate(&2, CALL, &normal, len) + .unwrap() + .priority; + + assert_eq!(priority, 110); + }); + + ExtBuilder::default().balance_factor(100).build().execute_with(|| { + let op = DispatchInfo { + weight: Weight::from_ref_time(100), + class: DispatchClass::Operational, + pays_fee: Pays::Yes, + }; + let priority = ChargeTransactionPayment::(tip) + .validate(&2, CALL, &op, len) + .unwrap() + .priority; + assert_eq!(priority, 5810); + + let priority = ChargeTransactionPayment::(2 * tip) + .validate(&2, CALL, &op, len) + .unwrap() + .priority; + assert_eq!(priority, 6110); + }); +} + +#[test] +fn no_tip_has_some_priority() { + let tip = 0; + let len = 10; + + ExtBuilder::default().balance_factor(100).build().execute_with(|| { + let normal = DispatchInfo { + weight: Weight::from_ref_time(100), + class: DispatchClass::Normal, + pays_fee: Pays::Yes, + }; + let priority = ChargeTransactionPayment::(tip) + .validate(&2, CALL, &normal, len) + .unwrap() + .priority; + + assert_eq!(priority, 10); + }); + + ExtBuilder::default().balance_factor(100).build().execute_with(|| { + let op = DispatchInfo { + weight: Weight::from_ref_time(100), + class: DispatchClass::Operational, + pays_fee: Pays::Yes, + }; + let priority = ChargeTransactionPayment::(tip) + .validate(&2, CALL, &op, len) + .unwrap() + .priority; + assert_eq!(priority, 5510); + }); +} + +#[test] +fn higher_tip_have_higher_priority() { + let get_priorities = |tip: u64| { + let mut priority1 = 0; + let mut priority2 = 0; + let len = 10; + ExtBuilder::default().balance_factor(100).build().execute_with(|| { + let normal = DispatchInfo { + weight: Weight::from_ref_time(100), + class: DispatchClass::Normal, + pays_fee: Pays::Yes, + }; + priority1 = ChargeTransactionPayment::(tip) + .validate(&2, CALL, &normal, len) + .unwrap() + .priority; + }); + + ExtBuilder::default().balance_factor(100).build().execute_with(|| { + let op = DispatchInfo { + weight: Weight::from_ref_time(100), + class: DispatchClass::Operational, + pays_fee: Pays::Yes, + }; + priority2 = ChargeTransactionPayment::(tip) + .validate(&2, CALL, &op, len) + .unwrap() + .priority; + }); + + (priority1, priority2) + }; + + let mut prev_priorities = get_priorities(0); + + for tip in 1..3 { + let priorities = get_priorities(tip); + assert!(prev_priorities.0 < priorities.0); + assert!(prev_priorities.1 < priorities.1); + prev_priorities = priorities; + } +} + +#[test] +fn post_info_can_change_pays_fee() { + ExtBuilder::default() + .balance_factor(10) + .base_weight(Weight::from_ref_time(7)) + .build() + .execute_with(|| { + let info = info_from_weight(Weight::from_ref_time(100)); + let post_info = post_info_from_pays(Pays::No); + let prev_balance = Balances::free_balance(2); + let len = 10; + let tip = 5; + + >::put(Multiplier::saturating_from_rational(5, 4)); + + let pre = ChargeTransactionPayment::::from(tip) + .pre_dispatch(&2, CALL, &info, len) + .unwrap(); + + ChargeTransactionPayment::::post_dispatch( + Some(pre), + &info, + &post_info, + len, + &Ok(()), + ) + .unwrap(); + + let refund_based_fee = prev_balance - Balances::free_balance(2); + let actual_fee = + Pallet::::compute_actual_fee(len as u32, &info, &post_info, tip); + + // Only 5 tip is paid + assert_eq!(actual_fee, 5); + assert_eq!(refund_based_fee, actual_fee); + }); +} + +#[test] +fn genesis_config_works() { + ExtBuilder::default() + .with_initial_multiplier(Multiplier::from_u32(100)) + .build() + .execute_with(|| { + assert_eq!( + >::get(), + Multiplier::saturating_from_integer(100) + ); + }); +} + +#[test] +fn genesis_default_works() { + ExtBuilder::default().build().execute_with(|| { + assert_eq!(>::get(), Multiplier::saturating_from_integer(1)); + }); +} From 42f38dbd961342cd965154858e50f0253b0b7c0a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bastian=20K=C3=B6cher?= Date: Tue, 17 Jan 2023 20:17:08 +0100 Subject: [PATCH 021/558] frame-benchmarking: Macros should not force a particular env (#13161) The macros in frame-benchmarking relied on having all the macros imported, which isn't a behavior for a proper macro :D This pr fixes this by making all internal macro usages absolute. --- bin/node/runtime/src/lib.rs | 6 +---- frame/benchmarking/src/lib.rs | 49 +++++++++++++++++------------------ 2 files changed, 25 insertions(+), 30 deletions(-) diff --git a/bin/node/runtime/src/lib.rs b/bin/node/runtime/src/lib.rs index d34c04fcb..fb5da9749 100644 --- a/bin/node/runtime/src/lib.rs +++ b/bin/node/runtime/src/lib.rs @@ -1823,13 +1823,9 @@ mod mmr { pub type Hashing = ::Hashing; } -#[cfg(feature = "runtime-benchmarks")] -#[macro_use] -extern crate frame_benchmarking; - #[cfg(feature = "runtime-benchmarks")] mod benches { - define_benchmarks!( + frame_benchmarking::define_benchmarks!( [frame_benchmarking, BaselineBench::] [pallet_alliance, Alliance] [pallet_assets, Assets] diff --git a/frame/benchmarking/src/lib.rs b/frame/benchmarking/src/lib.rs index 29fa0b6a6..e4ebd2f22 100644 --- a/frame/benchmarking/src/lib.rs +++ b/frame/benchmarking/src/lib.rs @@ -1836,15 +1836,15 @@ macro_rules! add_benchmark { /// Callback for `define_benchmarks` to call `add_benchmark`. #[macro_export] macro_rules! cb_add_benchmarks { - // anchor + // anchor ( $params:ident, $batches:ident, [ $name:path, $( $location:tt )* ] ) => { - add_benchmark!( $params, $batches, $name, $( $location )* ); + $crate::add_benchmark!( $params, $batches, $name, $( $location )* ); }; - // recursion tail + // recursion tail ( $params:ident, $batches:ident, [ $name:path, $( $location:tt )* ] $([ $names:path, $( $locations:tt )* ])+ ) => { - cb_add_benchmarks!( $params, $batches, [ $name, $( $location )* ] ); - cb_add_benchmarks!( $params, $batches, $([ $names, $( $locations )* ])+ ); - } + $crate::cb_add_benchmarks!( $params, $batches, [ $name, $( $location )* ] ); + $crate::cb_add_benchmarks!( $params, $batches, $([ $names, $( $locations )* ])+ ); + } } /// This macro allows users to easily generate a list of benchmarks for the pallets configured @@ -1884,24 +1884,23 @@ macro_rules! list_benchmark { /// Callback for `define_benchmarks` to call `list_benchmark`. #[macro_export] macro_rules! cb_list_benchmarks { - // anchor + // anchor ( $list:ident, $extra:ident, [ $name:path, $( $location:tt )* ] ) => { - list_benchmark!( $list, $extra, $name, $( $location )* ); + $crate::list_benchmark!( $list, $extra, $name, $( $location )* ); }; - // recursion tail + // recursion tail ( $list:ident, $extra:ident, [ $name:path, $( $location:tt )* ] $([ $names:path, $( $locations:tt )* ])+ ) => { - cb_list_benchmarks!( $list, $extra, [ $name, $( $location )* ] ); - cb_list_benchmarks!( $list, $extra, $([ $names, $( $locations )* ])+ ); - } + $crate::cb_list_benchmarks!( $list, $extra, [ $name, $( $location )* ] ); + $crate::cb_list_benchmarks!( $list, $extra, $([ $names, $( $locations )* ])+ ); + } } /// Defines pallet configs that `add_benchmarks` and `list_benchmarks` use. /// Should be preferred instead of having a repetitive list of configs /// in `add_benchmark` and `list_benchmark`. - #[macro_export] macro_rules! define_benchmarks { - ( $([ $names:path, $( $locations:tt )* ])* ) => { + ( $([ $names:path, $( $locations:tt )* ])* ) => { /// Calls `list_benchmark` with all configs from `define_benchmarks` /// and passes the first two parameters on. /// @@ -1910,11 +1909,11 @@ macro_rules! define_benchmarks { /// list_benchmarks!(list, extra); /// ``` #[macro_export] - macro_rules! list_benchmarks { - ( $list:ident, $extra:ident ) => { - cb_list_benchmarks!( $list, $extra, $([ $names, $( $locations )* ])+ ); - } - } + macro_rules! list_benchmarks { + ( $list:ident, $extra:ident ) => { + $crate::cb_list_benchmarks!( $list, $extra, $([ $names, $( $locations )* ])+ ); + } + } /// Calls `add_benchmark` with all configs from `define_benchmarks` /// and passes the first two parameters on. @@ -1924,10 +1923,10 @@ macro_rules! define_benchmarks { /// add_benchmarks!(params, batches); /// ``` #[macro_export] - macro_rules! add_benchmarks { - ( $params:ident, $batches:ident ) => { - cb_add_benchmarks!( $params, $batches, $([ $names, $( $locations )* ])+ ); - } - } - } + macro_rules! add_benchmarks { + ( $params:ident, $batches:ident ) => { + $crate::cb_add_benchmarks!( $params, $batches, $([ $names, $( $locations )* ])+ ); + } + } + } } From cfe926282a284f7bdbf7b336eb78e80f51858577 Mon Sep 17 00:00:00 2001 From: Koute Date: Wed, 18 Jan 2023 15:28:27 +0900 Subject: [PATCH 022/558] Fix the `storage_size`/`state_getStorageSize` RPC call (#13154) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Have `KeyIterator` clone the `prefix` it receives * Stream keys in `storage_size` RPC and add a runtime limit * Update client/rpc/Cargo.toml Co-authored-by: Bastian Köcher * Update client/rpc/src/state/utils.rs Co-authored-by: Bastian Köcher * Rename the types to signify that the cancellation is due to a timeout * Move the test into a `mod tests` * Add a comment regarding `biased` in `tokio::select` * Make the `clone` explicit when calling `KeyIterator::{new, new_child}` Co-authored-by: Bastian Köcher --- client/api/src/backend.rs | 26 +++--- client/rpc-api/src/state/mod.rs | 4 +- client/rpc/Cargo.toml | 4 +- client/rpc/src/state/mod.rs | 19 +++- client/rpc/src/state/state_full.rs | 67 +++++++++---- client/rpc/src/state/tests.rs | 7 +- client/rpc/src/state/utils.rs | 140 ++++++++++++++++++++++++++++ client/service/src/client/client.rs | 16 ++-- 8 files changed, 232 insertions(+), 51 deletions(-) create mode 100644 client/rpc/src/state/utils.rs diff --git a/client/api/src/backend.rs b/client/api/src/backend.rs index 79cc0d7a1..21d213ffb 100644 --- a/client/api/src/backend.rs +++ b/client/api/src/backend.rs @@ -303,17 +303,17 @@ pub trait AuxStore { } /// An `Iterator` that iterates keys in a given block under a prefix. -pub struct KeyIterator<'a, State, Block> { +pub struct KeyIterator { state: State, child_storage: Option, - prefix: Option<&'a StorageKey>, + prefix: Option, current_key: Vec, _phantom: PhantomData, } -impl<'a, State, Block> KeyIterator<'a, State, Block> { +impl KeyIterator { /// create a KeyIterator instance - pub fn new(state: State, prefix: Option<&'a StorageKey>, current_key: Vec) -> Self { + pub fn new(state: State, prefix: Option, current_key: Vec) -> Self { Self { state, child_storage: None, prefix, current_key, _phantom: PhantomData } } @@ -321,14 +321,14 @@ impl<'a, State, Block> KeyIterator<'a, State, Block> { pub fn new_child( state: State, child_info: ChildInfo, - prefix: Option<&'a StorageKey>, + prefix: Option, current_key: Vec, ) -> Self { Self { state, child_storage: Some(child_info), prefix, current_key, _phantom: PhantomData } } } -impl<'a, State, Block> Iterator for KeyIterator<'a, State, Block> +impl Iterator for KeyIterator where Block: BlockT, State: StateBackend>, @@ -344,7 +344,7 @@ where .ok() .flatten()?; // this terminates the iterator the first time it fails. - if let Some(prefix) = self.prefix { + if let Some(ref prefix) = self.prefix { if !next_key.starts_with(&prefix.0[..]) { return None } @@ -387,12 +387,12 @@ pub trait StorageProvider> { /// Given a block's `Hash` and a key prefix, return a `KeyIterator` iterates matching storage /// keys in that block. - fn storage_keys_iter<'a>( + fn storage_keys_iter( &self, hash: Block::Hash, - prefix: Option<&'a StorageKey>, + prefix: Option<&StorageKey>, start_key: Option<&StorageKey>, - ) -> sp_blockchain::Result>; + ) -> sp_blockchain::Result>; /// Given a block's `Hash`, a key and a child storage key, return the value under the key in /// that block. @@ -414,13 +414,13 @@ pub trait StorageProvider> { /// Given a block's `Hash` and a key `prefix` and a child storage key, /// return a `KeyIterator` that iterates matching storage keys in that block. - fn child_storage_keys_iter<'a>( + fn child_storage_keys_iter( &self, hash: Block::Hash, child_info: ChildInfo, - prefix: Option<&'a StorageKey>, + prefix: Option<&StorageKey>, start_key: Option<&StorageKey>, - ) -> sp_blockchain::Result>; + ) -> sp_blockchain::Result>; /// Given a block's `Hash`, a key and a child storage key, return the hash under the key in that /// block. diff --git a/client/rpc-api/src/state/mod.rs b/client/rpc-api/src/state/mod.rs index 40e208c2e..323e6ad1d 100644 --- a/client/rpc-api/src/state/mod.rs +++ b/client/rpc-api/src/state/mod.rs @@ -71,8 +71,8 @@ pub trait StateApi { fn storage_hash(&self, key: StorageKey, hash: Option) -> RpcResult>; /// Returns the size of a storage entry at a block's state. - #[method(name = "state_getStorageSize", aliases = ["state_getStorageSizeAt"], blocking)] - fn storage_size(&self, key: StorageKey, hash: Option) -> RpcResult>; + #[method(name = "state_getStorageSize", aliases = ["state_getStorageSizeAt"])] + async fn storage_size(&self, key: StorageKey, hash: Option) -> RpcResult>; /// Returns the runtime metadata as an opaque blob. #[method(name = "state_getMetadata", blocking)] diff --git a/client/rpc/Cargo.toml b/client/rpc/Cargo.toml index d97170ddf..d39d8d858 100644 --- a/client/rpc/Cargo.toml +++ b/client/rpc/Cargo.toml @@ -36,7 +36,7 @@ sp-runtime = { version = "7.0.0", path = "../../primitives/runtime" } sp-session = { version = "4.0.0-dev", path = "../../primitives/session" } sp-version = { version = "5.0.0", path = "../../primitives/version" } -tokio = { version = "1.22.0", optional = true } +tokio = "1.22.0" [dev-dependencies] env_logger = "0.9" @@ -51,4 +51,4 @@ sp-io = { version = "7.0.0", path = "../../primitives/io" } substrate-test-runtime-client = { version = "2.0.0", path = "../../test-utils/runtime/client" } [features] -test-helpers = ["tokio"] +test-helpers = [] diff --git a/client/rpc/src/state/mod.rs b/client/rpc/src/state/mod.rs index fd802e5a8..9ba4c8218 100644 --- a/client/rpc/src/state/mod.rs +++ b/client/rpc/src/state/mod.rs @@ -19,6 +19,7 @@ //! Substrate state API. mod state_full; +mod utils; #[cfg(test)] mod tests; @@ -28,7 +29,7 @@ use std::sync::Arc; use crate::SubscriptionTaskExecutor; use jsonrpsee::{ - core::{server::rpc_module::SubscriptionSink, Error as JsonRpseeError, RpcResult}, + core::{async_trait, server::rpc_module::SubscriptionSink, Error as JsonRpseeError, RpcResult}, types::SubscriptionResult, }; @@ -53,6 +54,7 @@ use sp_blockchain::{HeaderBackend, HeaderMetadata}; const STORAGE_KEYS_PAGED_MAX_COUNT: u32 = 1000; /// State backend API. +#[async_trait] pub trait StateBackend: Send + Sync + 'static where Block: BlockT + 'static, @@ -107,10 +109,11 @@ where /// /// If data is available at `key`, it is returned. Else, the sum of values who's key has `key` /// prefix is returned, i.e. all the storage (double) maps that have this prefix. - fn storage_size( + async fn storage_size( &self, block: Option, key: StorageKey, + deny_unsafe: DenyUnsafe, ) -> Result, Error>; /// Returns the runtime metadata as an opaque blob. @@ -202,6 +205,7 @@ pub struct State { deny_unsafe: DenyUnsafe, } +#[async_trait] impl StateApiServer for State where Block: BlockT + 'static, @@ -262,8 +266,15 @@ where self.backend.storage_hash(block, key).map_err(Into::into) } - fn storage_size(&self, key: StorageKey, block: Option) -> RpcResult> { - self.backend.storage_size(block, key).map_err(Into::into) + async fn storage_size( + &self, + key: StorageKey, + block: Option, + ) -> RpcResult> { + self.backend + .storage_size(block, key, self.deny_unsafe) + .await + .map_err(Into::into) } fn metadata(&self, block: Option) -> RpcResult { diff --git a/client/rpc/src/state/state_full.rs b/client/rpc/src/state/state_full.rs index 58aeac66e..d8fe39030 100644 --- a/client/rpc/src/state/state_full.rs +++ b/client/rpc/src/state/state_full.rs @@ -18,17 +18,20 @@ //! State API backend for full nodes. -use std::{collections::HashMap, marker::PhantomData, sync::Arc}; +use std::{collections::HashMap, marker::PhantomData, sync::Arc, time::Duration}; use super::{ client_err, error::{Error, Result}, ChildStateBackend, StateBackend, }; -use crate::SubscriptionTaskExecutor; +use crate::{DenyUnsafe, SubscriptionTaskExecutor}; use futures::{future, stream, FutureExt, StreamExt}; -use jsonrpsee::{core::Error as JsonRpseeError, SubscriptionSink}; +use jsonrpsee::{ + core::{async_trait, Error as JsonRpseeError}, + SubscriptionSink, +}; use sc_client_api::{ Backend, BlockBackend, BlockchainEvents, CallExecutor, ExecutorProvider, ProofProvider, StorageProvider, @@ -48,6 +51,9 @@ use sp_core::{ use sp_runtime::{generic::BlockId, traits::Block as BlockT}; use sp_version::RuntimeVersion; +/// The maximum time allowed for an RPC call when running without unsafe RPC enabled. +const MAXIMUM_SAFE_RPC_CALL_TIMEOUT: Duration = Duration::from_secs(30); + /// Ranges to query in state_queryStorage. struct QueryStorageRange { /// Hashes of all the blocks in the range. @@ -166,6 +172,7 @@ where } } +#[async_trait] impl StateBackend for FullState where Block: BlockT + 'static, @@ -251,33 +258,53 @@ where .map_err(client_err) } - fn storage_size( + async fn storage_size( &self, block: Option, key: StorageKey, + deny_unsafe: DenyUnsafe, ) -> std::result::Result, Error> { let block = match self.block_or_best(block) { Ok(b) => b, Err(e) => return Err(client_err(e)), }; - match self.client.storage(block, &key) { - Ok(Some(d)) => return Ok(Some(d.0.len() as u64)), - Err(e) => return Err(client_err(e)), - Ok(None) => {}, - } + let client = self.client.clone(); + let timeout = match deny_unsafe { + DenyUnsafe::Yes => Some(MAXIMUM_SAFE_RPC_CALL_TIMEOUT), + DenyUnsafe::No => None, + }; - self.client - .storage_pairs(block, &key) - .map(|kv| { - let item_sum = kv.iter().map(|(_, v)| v.0.len() as u64).sum::(); - if item_sum > 0 { - Some(item_sum) - } else { - None - } - }) - .map_err(client_err) + super::utils::spawn_blocking_with_timeout(timeout, move |is_timed_out| { + // Does the key point to a concrete entry in the database? + match client.storage(block, &key) { + Ok(Some(d)) => return Ok(Ok(Some(d.0.len() as u64))), + Err(e) => return Ok(Err(client_err(e))), + Ok(None) => {}, + } + + // The key doesn't point to anything, so it's probably a prefix. + let iter = match client.storage_keys_iter(block, Some(&key), None).map_err(client_err) { + Ok(iter) => iter, + Err(e) => return Ok(Err(e)), + }; + + let mut sum = 0; + for storage_key in iter { + let value = client.storage(block, &storage_key).ok().flatten().unwrap_or_default(); + sum += value.0.len() as u64; + + is_timed_out.check_if_timed_out()?; + } + + if sum > 0 { + Ok(Ok(Some(sum))) + } else { + Ok(Ok(None)) + } + }) + .await + .map_err(|error| Error::Client(Box::new(error)))? } fn storage_hash( diff --git a/client/rpc/src/state/tests.rs b/client/rpc/src/state/tests.rs index 3ef59e5ca..fe8bdf0ac 100644 --- a/client/rpc/src/state/tests.rs +++ b/client/rpc/src/state/tests.rs @@ -70,9 +70,12 @@ async fn should_return_storage() { client.storage_hash(key.clone(), Some(genesis_hash).into()).map(|x| x.is_some()), Ok(true) ); - assert_eq!(client.storage_size(key.clone(), None).unwrap().unwrap() as usize, VALUE.len(),); assert_eq!( - client.storage_size(StorageKey(b":map".to_vec()), None).unwrap().unwrap() as usize, + client.storage_size(key.clone(), None).await.unwrap().unwrap() as usize, + VALUE.len(), + ); + assert_eq!( + client.storage_size(StorageKey(b":map".to_vec()), None).await.unwrap().unwrap() as usize, 2 + 3, ); assert_eq!( diff --git a/client/rpc/src/state/utils.rs b/client/rpc/src/state/utils.rs new file mode 100644 index 000000000..81476cdd3 --- /dev/null +++ b/client/rpc/src/state/utils.rs @@ -0,0 +1,140 @@ +// This file is part of Substrate. + +// Copyright (C) 2017-2023 Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +use std::{ + sync::{ + atomic::{AtomicBool, Ordering}, + Arc, + }, + time::Duration, +}; + +/// An error signifying that a task has been cancelled due to a timeout. +#[derive(Debug)] +pub struct Timeout; + +impl std::error::Error for Timeout {} +impl std::fmt::Display for Timeout { + fn fmt(&self, fmt: &mut std::fmt::Formatter) -> std::fmt::Result { + fmt.write_str("task has been running too long") + } +} + +/// A handle which can be used to check whether the task has been cancelled due to a timeout. +#[repr(transparent)] +pub struct IsTimedOut(Arc); + +impl IsTimedOut { + #[must_use] + pub fn check_if_timed_out(&self) -> std::result::Result<(), Timeout> { + if self.0.load(Ordering::Relaxed) { + Err(Timeout) + } else { + Ok(()) + } + } +} + +/// An error for a task which either panicked, or has been cancelled due to a timeout. +#[derive(Debug)] +pub enum SpawnWithTimeoutError { + JoinError(tokio::task::JoinError), + Timeout, +} + +impl std::error::Error for SpawnWithTimeoutError {} +impl std::fmt::Display for SpawnWithTimeoutError { + fn fmt(&self, fmt: &mut std::fmt::Formatter) -> std::fmt::Result { + match self { + SpawnWithTimeoutError::JoinError(error) => error.fmt(fmt), + SpawnWithTimeoutError::Timeout => Timeout.fmt(fmt), + } + } +} + +struct CancelOnDrop(Arc); +impl Drop for CancelOnDrop { + fn drop(&mut self) { + self.0.store(true, Ordering::Relaxed) + } +} + +/// Spawns a new blocking task with a given `timeout`. +/// +/// The `callback` should continuously call [`IsTimedOut::check_if_timed_out`], +/// which will return an error once the task runs for longer than `timeout`. +/// +/// If `timeout` is `None` then this works just as a regular `spawn_blocking`. +pub async fn spawn_blocking_with_timeout( + timeout: Option, + callback: impl FnOnce(IsTimedOut) -> std::result::Result + Send + 'static, +) -> Result +where + R: Send + 'static, +{ + let is_timed_out_arc = Arc::new(AtomicBool::new(false)); + let is_timed_out = IsTimedOut(is_timed_out_arc.clone()); + let _cancel_on_drop = CancelOnDrop(is_timed_out_arc); + let task = tokio::task::spawn_blocking(move || callback(is_timed_out)); + + let result = if let Some(timeout) = timeout { + tokio::select! { + // Shouldn't really matter, but make sure the task is polled before the timeout, + // in case the task finishes after the timeout and the timeout is really short. + biased; + + task_result = task => task_result, + _ = tokio::time::sleep(timeout) => Ok(Err(Timeout)) + } + } else { + task.await + }; + + match result { + Ok(Ok(result)) => Ok(result), + Ok(Err(Timeout)) => Err(SpawnWithTimeoutError::Timeout), + Err(error) => Err(SpawnWithTimeoutError::JoinError(error)), + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[tokio::test] + async fn spawn_blocking_with_timeout_works() { + let task: Result<(), SpawnWithTimeoutError> = + spawn_blocking_with_timeout(Some(Duration::from_millis(100)), |is_timed_out| { + std::thread::sleep(Duration::from_millis(200)); + is_timed_out.check_if_timed_out()?; + unreachable!(); + }) + .await; + + assert_matches::assert_matches!(task, Err(SpawnWithTimeoutError::Timeout)); + + let task = spawn_blocking_with_timeout(Some(Duration::from_millis(100)), |is_timed_out| { + std::thread::sleep(Duration::from_millis(20)); + is_timed_out.check_if_timed_out()?; + Ok(()) + }) + .await; + + assert_matches::assert_matches!(task, Ok(())); + } +} diff --git a/client/service/src/client/client.rs b/client/service/src/client/client.rs index 18012fc19..8e10a7b2e 100644 --- a/client/service/src/client/client.rs +++ b/client/service/src/client/client.rs @@ -1432,27 +1432,27 @@ where Ok(keys) } - fn storage_keys_iter<'a>( + fn storage_keys_iter( &self, hash: ::Hash, - prefix: Option<&'a StorageKey>, + prefix: Option<&StorageKey>, start_key: Option<&StorageKey>, - ) -> sp_blockchain::Result> { + ) -> sp_blockchain::Result> { let state = self.state_at(hash)?; let start_key = start_key.or(prefix).map(|key| key.0.clone()).unwrap_or_else(Vec::new); - Ok(KeyIterator::new(state, prefix, start_key)) + Ok(KeyIterator::new(state, prefix.cloned(), start_key)) } - fn child_storage_keys_iter<'a>( + fn child_storage_keys_iter( &self, hash: ::Hash, child_info: ChildInfo, - prefix: Option<&'a StorageKey>, + prefix: Option<&StorageKey>, start_key: Option<&StorageKey>, - ) -> sp_blockchain::Result> { + ) -> sp_blockchain::Result> { let state = self.state_at(hash)?; let start_key = start_key.or(prefix).map(|key| key.0.clone()).unwrap_or_else(Vec::new); - Ok(KeyIterator::new_child(state, child_info, prefix, start_key)) + Ok(KeyIterator::new_child(state, child_info, prefix.cloned(), start_key)) } fn storage( From a447f03727ffd84020682f3e0e4044d5d282d274 Mon Sep 17 00:00:00 2001 From: Sasha Gryaznov Date: Wed, 18 Jan 2023 12:36:09 +0200 Subject: [PATCH 023/558] [contracts] Adapt storage reading host functions to Weights V2 (#12976) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * update RuntimeCosts to Weights V2, update tests * improve docs * clearer naming and docs to compat_weight helper * Apply suggestions from code review Co-authored-by: Alexander Theißen * save before master merge * HostFnWeights to Weight * added to_weight! macro * Apply suggestions from code review Co-authored-by: Alexander Theißen * RuntimeCosts::ChainExtension to weight_v2 * chain extension to weight v2 Co-authored-by: Alexander Theißen --- frame/contracts/proc-macro/src/lib.rs | 14 +- frame/contracts/src/benchmarking/mod.rs | 2 +- frame/contracts/src/chain_extension.rs | 7 +- frame/contracts/src/gas.rs | 4 +- frame/contracts/src/lib.rs | 22 +- frame/contracts/src/schedule.rs | 278 +++++++++++++----------- frame/contracts/src/tests.rs | 4 +- frame/contracts/src/wasm/mod.rs | 2 +- frame/contracts/src/wasm/runtime.rs | 10 +- 9 files changed, 189 insertions(+), 154 deletions(-) diff --git a/frame/contracts/proc-macro/src/lib.rs b/frame/contracts/proc-macro/src/lib.rs index a31d39f47..d0247d720 100644 --- a/frame/contracts/proc-macro/src/lib.rs +++ b/frame/contracts/proc-macro/src/lib.rs @@ -122,23 +122,23 @@ fn iterate_fields(data: &syn::DataStruct, fmt: impl Fn(&Ident) -> TokenStream2) fn format_weight(field: &Ident) -> TokenStream2 { quote_spanned! { field.span() => - &if self.#field > 1_000_000_000 { + &if self.#field.ref_time() > 1_000_000_000 { format!( "{:.1?} ms", - Fixed::saturating_from_rational(self.#field, 1_000_000_000).to_float() + Fixed::saturating_from_rational(self.#field.ref_time(), 1_000_000_000).to_float() ) - } else if self.#field > 1_000_000 { + } else if self.#field.ref_time() > 1_000_000 { format!( "{:.1?} µs", - Fixed::saturating_from_rational(self.#field, 1_000_000).to_float() + Fixed::saturating_from_rational(self.#field.ref_time(), 1_000_000).to_float() ) - } else if self.#field > 1_000 { + } else if self.#field.ref_time() > 1_000 { format!( "{:.1?} ns", - Fixed::saturating_from_rational(self.#field, 1_000).to_float() + Fixed::saturating_from_rational(self.#field.ref_time(), 1_000).to_float() ) } else { - format!("{} ps", self.#field) + format!("{} ps", self.#field.ref_time()) } } } diff --git a/frame/contracts/src/benchmarking/mod.rs b/frame/contracts/src/benchmarking/mod.rs index d46da8469..a750a6b17 100644 --- a/frame/contracts/src/benchmarking/mod.rs +++ b/frame/contracts/src/benchmarking/mod.rs @@ -2928,7 +2928,7 @@ benchmarks! { // configured `Schedule` during benchmark development. // It can be outputed using the following command: // cargo run --manifest-path=bin/node/cli/Cargo.toml --release \ - // --features runtime-benchmarks -- benchmark --extra --dev --execution=native \ + // --features runtime-benchmarks -- benchmark pallet --extra --dev --execution=native \ // -p pallet_contracts -e print_schedule --no-median-slopes --no-min-squares #[extra] print_schedule { diff --git a/frame/contracts/src/chain_extension.rs b/frame/contracts/src/chain_extension.rs index dfa9c7c2d..3974bdba3 100644 --- a/frame/contracts/src/chain_extension.rs +++ b/frame/contracts/src/chain_extension.rs @@ -227,7 +227,7 @@ impl<'a, 'b, E: Ext, S: State> Environment<'a, 'b, E, S> { /// /// Weight is synonymous with gas in substrate. pub fn charge_weight(&mut self, amount: Weight) -> Result { - self.inner.runtime.charge_gas(RuntimeCosts::ChainExtension(amount.ref_time())) + self.inner.runtime.charge_gas(RuntimeCosts::ChainExtension(amount)) } /// Adjust a previously charged amount down to its actual amount. @@ -237,7 +237,7 @@ impl<'a, 'b, E: Ext, S: State> Environment<'a, 'b, E, S> { pub fn adjust_weight(&mut self, charged: ChargedAmount, actual_weight: Weight) { self.inner .runtime - .adjust_gas(charged, RuntimeCosts::ChainExtension(actual_weight.ref_time())) + .adjust_gas(charged, RuntimeCosts::ChainExtension(actual_weight)) } /// Grants access to the execution environment of the current contract call. @@ -408,8 +408,7 @@ impl<'a, 'b, E: Ext, S: BufOut> Environment<'a, 'b, E, S> { buffer, allow_skip, |len| { - weight_per_byte - .map(|w| RuntimeCosts::ChainExtension(w.ref_time().saturating_mul(len.into()))) + weight_per_byte.map(|w| RuntimeCosts::ChainExtension(w.saturating_mul(len.into()))) }, ) } diff --git a/frame/contracts/src/gas.rs b/frame/contracts/src/gas.rs index ccbe680b2..c8383f307 100644 --- a/frame/contracts/src/gas.rs +++ b/frame/contracts/src/gas.rs @@ -153,8 +153,8 @@ impl GasMeter { /// Returns `OutOfGas` if there is not enough gas or addition of the specified /// amount of gas has lead to overflow. On success returns `Proceed`. /// - /// NOTE that amount is always consumed, i.e. if there is not enough gas - /// then the counter will be set to zero. + /// NOTE that amount isn't consumed if there is not enough gas. This is considered + /// safe because we always charge gas before performing any resource-spending action. #[inline] pub fn charge>(&mut self, token: Tok) -> Result { #[cfg(test)] diff --git a/frame/contracts/src/lib.rs b/frame/contracts/src/lib.rs index 9efeec65f..581798e44 100644 --- a/frame/contracts/src/lib.rs +++ b/frame/contracts/src/lib.rs @@ -381,7 +381,7 @@ pub mod pallet { { /// Deprecated version if [`Self::call`] for use in an in-storage `Call`. #[pallet::call_index(0)] - #[pallet::weight(T::WeightInfo::call().saturating_add(>::compat_weight(*gas_limit)))] + #[pallet::weight(T::WeightInfo::call().saturating_add(>::compat_weight_limit(*gas_limit)))] #[allow(deprecated)] #[deprecated(note = "1D weight is used in this extrinsic, please migrate to `call`")] pub fn call_old_weight( @@ -396,7 +396,7 @@ pub mod pallet { origin, dest, value, - >::compat_weight(gas_limit), + >::compat_weight_limit(gas_limit), storage_deposit_limit, data, ) @@ -406,7 +406,7 @@ pub mod pallet { #[pallet::call_index(1)] #[pallet::weight( T::WeightInfo::instantiate_with_code(code.len() as u32, data.len() as u32, salt.len() as u32) - .saturating_add(>::compat_weight(*gas_limit)) + .saturating_add(>::compat_weight_limit(*gas_limit)) )] #[allow(deprecated)] #[deprecated( @@ -424,7 +424,7 @@ pub mod pallet { Self::instantiate_with_code( origin, value, - >::compat_weight(gas_limit), + >::compat_weight_limit(gas_limit), storage_deposit_limit, code, data, @@ -435,7 +435,7 @@ pub mod pallet { /// Deprecated version if [`Self::instantiate`] for use in an in-storage `Call`. #[pallet::call_index(2)] #[pallet::weight( - T::WeightInfo::instantiate(data.len() as u32, salt.len() as u32).saturating_add(>::compat_weight(*gas_limit)) + T::WeightInfo::instantiate(data.len() as u32, salt.len() as u32).saturating_add(>::compat_weight_limit(*gas_limit)) )] #[allow(deprecated)] #[deprecated(note = "1D weight is used in this extrinsic, please migrate to `instantiate`")] @@ -451,7 +451,7 @@ pub mod pallet { Self::instantiate( origin, value, - >::compat_weight(gas_limit), + >::compat_weight_limit(gas_limit), storage_deposit_limit, code_hash, data, @@ -1216,12 +1216,12 @@ impl Pallet { >>::minimum_balance() } - /// Convert a 1D Weight to a 2D weight. + /// Convert gas_limit from 1D Weight to a 2D Weight. /// - /// Used by backwards compatible extrinsics. We cannot just set the proof to zero - /// or an old `Call` will just fail. - fn compat_weight(gas_limit: OldWeight) -> Weight { - Weight::from(gas_limit).set_proof_size(u64::from(T::MaxCodeLen::get()) * 2) + /// Used by backwards compatible extrinsics. We cannot just set the proof_size weight limit to + /// zero or an old `Call` will just fail with OutOfGas. + fn compat_weight_limit(gas_limit: OldWeight) -> Weight { + Weight::from_parts(gas_limit.0, u64::from(T::MaxCodeLen::get()) * 2) } } diff --git a/frame/contracts/src/schedule.rs b/frame/contracts/src/schedule.rs index 912e58b04..dabdc7013 100644 --- a/frame/contracts/src/schedule.rs +++ b/frame/contracts/src/schedule.rs @@ -21,7 +21,7 @@ use crate::{wasm::Determinism, weights::WeightInfo, Config}; use codec::{Decode, Encode}; -use frame_support::DefaultNoBound; +use frame_support::{weights::Weight, DefaultNoBound}; use pallet_contracts_proc_macro::{ScheduleDebug, WeightDebug}; use scale_info::TypeInfo; #[cfg(feature = "std")] @@ -169,7 +169,7 @@ impl Limits { /// that use them as supporting instructions. Supporting means mainly pushing arguments /// and dropping return values in order to maintain a valid module. #[cfg_attr(feature = "std", derive(Serialize, Deserialize))] -#[derive(Clone, Encode, Decode, PartialEq, Eq, WeightDebug, TypeInfo)] +#[derive(Clone, Encode, Decode, PartialEq, Eq, ScheduleDebug, TypeInfo)] #[scale_info(skip_type_params(T))] pub struct InstructionWeights { /// Version of the instruction weights. @@ -255,178 +255,178 @@ pub struct InstructionWeights { #[scale_info(skip_type_params(T))] pub struct HostFnWeights { /// Weight of calling `seal_caller`. - pub caller: u64, + pub caller: Weight, /// Weight of calling `seal_is_contract`. - pub is_contract: u64, + pub is_contract: Weight, /// Weight of calling `seal_code_hash`. - pub code_hash: u64, + pub code_hash: Weight, /// Weight of calling `seal_own_code_hash`. - pub own_code_hash: u64, + pub own_code_hash: Weight, /// Weight of calling `seal_caller_is_origin`. - pub caller_is_origin: u64, + pub caller_is_origin: Weight, /// Weight of calling `seal_address`. - pub address: u64, + pub address: Weight, /// Weight of calling `seal_gas_left`. - pub gas_left: u64, + pub gas_left: Weight, /// Weight of calling `seal_balance`. - pub balance: u64, + pub balance: Weight, /// Weight of calling `seal_value_transferred`. - pub value_transferred: u64, + pub value_transferred: Weight, /// Weight of calling `seal_minimum_balance`. - pub minimum_balance: u64, + pub minimum_balance: Weight, /// Weight of calling `seal_block_number`. - pub block_number: u64, + pub block_number: Weight, /// Weight of calling `seal_now`. - pub now: u64, + pub now: Weight, /// Weight of calling `seal_weight_to_fee`. - pub weight_to_fee: u64, + pub weight_to_fee: Weight, /// Weight of calling `gas`. - pub gas: u64, + pub gas: Weight, /// Weight of calling `seal_input`. - pub input: u64, + pub input: Weight, /// Weight per input byte copied to contract memory by `seal_input`. - pub input_per_byte: u64, + pub input_per_byte: Weight, /// Weight of calling `seal_return`. - pub r#return: u64, + pub r#return: Weight, /// Weight per byte returned through `seal_return`. - pub return_per_byte: u64, + pub return_per_byte: Weight, /// Weight of calling `seal_terminate`. - pub terminate: u64, + pub terminate: Weight, /// Weight of calling `seal_random`. - pub random: u64, + pub random: Weight, /// Weight of calling `seal_reposit_event`. - pub deposit_event: u64, + pub deposit_event: Weight, /// Weight per topic supplied to `seal_deposit_event`. - pub deposit_event_per_topic: u64, + pub deposit_event_per_topic: Weight, /// Weight per byte of an event deposited through `seal_deposit_event`. - pub deposit_event_per_byte: u64, + pub deposit_event_per_byte: Weight, /// Weight of calling `seal_debug_message`. - pub debug_message: u64, + pub debug_message: Weight, /// Weight of calling `seal_set_storage`. - pub set_storage: u64, + pub set_storage: Weight, /// Weight per written byten of an item stored with `seal_set_storage`. - pub set_storage_per_new_byte: u64, + pub set_storage_per_new_byte: Weight, /// Weight per overwritten byte of an item stored with `seal_set_storage`. - pub set_storage_per_old_byte: u64, + pub set_storage_per_old_byte: Weight, /// Weight of calling `seal_set_code_hash`. - pub set_code_hash: u64, + pub set_code_hash: Weight, /// Weight of calling `seal_clear_storage`. - pub clear_storage: u64, + pub clear_storage: Weight, /// Weight of calling `seal_clear_storage` per byte of the stored item. - pub clear_storage_per_byte: u64, + pub clear_storage_per_byte: Weight, /// Weight of calling `seal_contains_storage`. - pub contains_storage: u64, + pub contains_storage: Weight, /// Weight of calling `seal_contains_storage` per byte of the stored item. - pub contains_storage_per_byte: u64, + pub contains_storage_per_byte: Weight, /// Weight of calling `seal_get_storage`. - pub get_storage: u64, + pub get_storage: Weight, /// Weight per byte of an item received via `seal_get_storage`. - pub get_storage_per_byte: u64, + pub get_storage_per_byte: Weight, /// Weight of calling `seal_take_storage`. - pub take_storage: u64, + pub take_storage: Weight, /// Weight per byte of an item received via `seal_take_storage`. - pub take_storage_per_byte: u64, + pub take_storage_per_byte: Weight, /// Weight of calling `seal_transfer`. - pub transfer: u64, + pub transfer: Weight, /// Weight of calling `seal_call`. - pub call: u64, + pub call: Weight, /// Weight of calling `seal_delegate_call`. - pub delegate_call: u64, + pub delegate_call: Weight, /// Weight surcharge that is claimed if `seal_call` does a balance transfer. - pub call_transfer_surcharge: u64, + pub call_transfer_surcharge: Weight, /// Weight per byte that is cloned by supplying the `CLONE_INPUT` flag. - pub call_per_cloned_byte: u64, + pub call_per_cloned_byte: Weight, /// Weight of calling `seal_instantiate`. - pub instantiate: u64, + pub instantiate: Weight, /// Weight surcharge that is claimed if `seal_instantiate` does a balance transfer. - pub instantiate_transfer_surcharge: u64, + pub instantiate_transfer_surcharge: Weight, /// Weight per input byte supplied to `seal_instantiate`. - pub instantiate_per_input_byte: u64, + pub instantiate_per_input_byte: Weight, /// Weight per salt byte supplied to `seal_instantiate`. - pub instantiate_per_salt_byte: u64, + pub instantiate_per_salt_byte: Weight, /// Weight of calling `seal_hash_sha_256`. - pub hash_sha2_256: u64, + pub hash_sha2_256: Weight, /// Weight per byte hashed by `seal_hash_sha_256`. - pub hash_sha2_256_per_byte: u64, + pub hash_sha2_256_per_byte: Weight, /// Weight of calling `seal_hash_keccak_256`. - pub hash_keccak_256: u64, + pub hash_keccak_256: Weight, /// Weight per byte hashed by `seal_hash_keccak_256`. - pub hash_keccak_256_per_byte: u64, + pub hash_keccak_256_per_byte: Weight, /// Weight of calling `seal_hash_blake2_256`. - pub hash_blake2_256: u64, + pub hash_blake2_256: Weight, /// Weight per byte hashed by `seal_hash_blake2_256`. - pub hash_blake2_256_per_byte: u64, + pub hash_blake2_256_per_byte: Weight, /// Weight of calling `seal_hash_blake2_128`. - pub hash_blake2_128: u64, + pub hash_blake2_128: Weight, /// Weight per byte hashed by `seal_hash_blake2_128`. - pub hash_blake2_128_per_byte: u64, + pub hash_blake2_128_per_byte: Weight, /// Weight of calling `seal_ecdsa_recover`. - pub ecdsa_recover: u64, + pub ecdsa_recover: Weight, /// Weight of calling `seal_ecdsa_to_eth_address`. - pub ecdsa_to_eth_address: u64, + pub ecdsa_to_eth_address: Weight, /// Weight of calling `reentrance_count`. - pub reentrance_count: u64, + pub reentrance_count: Weight, /// Weight of calling `account_reentrance_count`. - pub account_reentrance_count: u64, + pub account_reentrance_count: Weight, /// Weight of calling `instantiation_nonce`. - pub instantiation_nonce: u64, + pub instantiation_nonce: Weight, /// The type parameter is used in the default implementation. #[codec(skip)] @@ -514,6 +514,12 @@ macro_rules! cost_byte_batched { }; } +macro_rules! to_weight { + ($ref_time:expr $(, $proof_size:expr )?) => { + Weight::from_ref_time($ref_time)$(.set_proof_size($proof_size))? + }; +} + impl Default for Limits { fn default() -> Self { Self { @@ -596,85 +602,115 @@ impl Default for InstructionWeights { } impl Default for HostFnWeights { + /// PoV should contain all trie nodes that are read during state transition (i.e. block + /// production). Hence we need to charge the `proof_size` weight for every host function which + /// reads storage, namely: + /// - get_storage, + /// - take_storage, + /// - contains_storage, + /// - clear_storage, + /// - set_storage. + /// + /// The last two functions write to storage, but they also do read storage in order to return + /// the size of the pre-existed value. Till we have PoV benchmarks implemented, we approximate + /// `proof_size` as being equal to the size of storage read. fn default() -> Self { Self { - caller: cost_batched!(seal_caller), - is_contract: cost_batched!(seal_is_contract), - code_hash: cost_batched!(seal_code_hash), - own_code_hash: cost_batched!(seal_own_code_hash), - caller_is_origin: cost_batched!(seal_caller_is_origin), - address: cost_batched!(seal_address), - gas_left: cost_batched!(seal_gas_left), - balance: cost_batched!(seal_balance), - value_transferred: cost_batched!(seal_value_transferred), - minimum_balance: cost_batched!(seal_minimum_balance), - block_number: cost_batched!(seal_block_number), - now: cost_batched!(seal_now), - weight_to_fee: cost_batched!(seal_weight_to_fee), - gas: cost_batched!(seal_gas), - input: cost_batched!(seal_input), - input_per_byte: cost_byte_batched!(seal_input_per_kb), - r#return: cost!(seal_return), - return_per_byte: cost_byte!(seal_return_per_kb), - terminate: cost!(seal_terminate), - random: cost_batched!(seal_random), - deposit_event: cost_batched!(seal_deposit_event), - deposit_event_per_topic: cost_batched_args!(seal_deposit_event_per_topic_and_kb, 1, 0), - deposit_event_per_byte: cost_byte_batched_args!( + caller: to_weight!(cost_batched!(seal_caller)), + is_contract: to_weight!(cost_batched!(seal_is_contract)), + code_hash: to_weight!(cost_batched!(seal_code_hash)), + own_code_hash: to_weight!(cost_batched!(seal_own_code_hash)), + caller_is_origin: to_weight!(cost_batched!(seal_caller_is_origin)), + address: to_weight!(cost_batched!(seal_address)), + gas_left: to_weight!(cost_batched!(seal_gas_left)), + balance: to_weight!(cost_batched!(seal_balance)), + value_transferred: to_weight!(cost_batched!(seal_value_transferred)), + minimum_balance: to_weight!(cost_batched!(seal_minimum_balance)), + block_number: to_weight!(cost_batched!(seal_block_number)), + now: to_weight!(cost_batched!(seal_now)), + weight_to_fee: to_weight!(cost_batched!(seal_weight_to_fee)), + gas: to_weight!(cost_batched!(seal_gas)), + input: to_weight!(cost_batched!(seal_input)), + input_per_byte: to_weight!(cost_byte_batched!(seal_input_per_kb)), + r#return: to_weight!(cost!(seal_return)), + return_per_byte: to_weight!(cost_byte!(seal_return_per_kb)), + terminate: to_weight!(cost!(seal_terminate)), + random: to_weight!(cost_batched!(seal_random)), + deposit_event: to_weight!(cost_batched!(seal_deposit_event)), + deposit_event_per_topic: to_weight!(cost_batched_args!( + seal_deposit_event_per_topic_and_kb, + 1, + 0 + )), + deposit_event_per_byte: to_weight!(cost_byte_batched_args!( seal_deposit_event_per_topic_and_kb, 0, 1 + )), + debug_message: to_weight!(cost_batched!(seal_debug_message)), + set_storage: to_weight!(cost_batched!(seal_set_storage)), + set_code_hash: to_weight!(cost_batched!(seal_set_code_hash)), + set_storage_per_new_byte: to_weight!(cost_byte_batched!(seal_set_storage_per_new_kb)), + set_storage_per_old_byte: to_weight!( + cost_byte_batched!(seal_set_storage_per_old_kb), + 1u64 ), - debug_message: cost_batched!(seal_debug_message), - set_storage: cost_batched!(seal_set_storage), - set_code_hash: cost_batched!(seal_set_code_hash), - set_storage_per_new_byte: cost_byte_batched!(seal_set_storage_per_new_kb), - set_storage_per_old_byte: cost_byte_batched!(seal_set_storage_per_old_kb), - clear_storage: cost_batched!(seal_clear_storage), - clear_storage_per_byte: cost_byte_batched!(seal_clear_storage_per_kb), - contains_storage: cost_batched!(seal_contains_storage), - contains_storage_per_byte: cost_byte_batched!(seal_contains_storage_per_kb), - get_storage: cost_batched!(seal_get_storage), - get_storage_per_byte: cost_byte_batched!(seal_get_storage_per_kb), - take_storage: cost_batched!(seal_take_storage), - take_storage_per_byte: cost_byte_batched!(seal_take_storage_per_kb), - transfer: cost_batched!(seal_transfer), - call: cost_batched!(seal_call), - delegate_call: cost_batched!(seal_delegate_call), - call_transfer_surcharge: cost_batched_args!(seal_call_per_transfer_clone_kb, 1, 0), - call_per_cloned_byte: cost_batched_args!(seal_call_per_transfer_clone_kb, 0, 1), - instantiate: cost_batched!(seal_instantiate), - instantiate_transfer_surcharge: cost_byte_batched_args!( + clear_storage: to_weight!(cost_batched!(seal_clear_storage)), + clear_storage_per_byte: to_weight!(cost_byte_batched!(seal_clear_storage_per_kb), 1u64), + contains_storage: to_weight!(cost_batched!(seal_contains_storage)), + contains_storage_per_byte: to_weight!( + cost_byte_batched!(seal_contains_storage_per_kb), + 1u64 + ), + get_storage: to_weight!(cost_batched!(seal_get_storage)), + get_storage_per_byte: to_weight!(cost_byte_batched!(seal_get_storage_per_kb), 1u64), + take_storage: to_weight!(cost_batched!(seal_take_storage)), + take_storage_per_byte: to_weight!(cost_byte_batched!(seal_take_storage_per_kb), 1u64), + transfer: to_weight!(cost_batched!(seal_transfer)), + call: to_weight!(cost_batched!(seal_call)), + delegate_call: to_weight!(cost_batched!(seal_delegate_call)), + call_transfer_surcharge: to_weight!(cost_batched_args!( + seal_call_per_transfer_clone_kb, + 1, + 0 + )), + call_per_cloned_byte: to_weight!(cost_batched_args!( + seal_call_per_transfer_clone_kb, + 0, + 1 + )), + instantiate: to_weight!(cost_batched!(seal_instantiate)), + instantiate_transfer_surcharge: to_weight!(cost_byte_batched_args!( seal_instantiate_per_transfer_input_salt_kb, 1, 0, 0 - ), - instantiate_per_input_byte: cost_byte_batched_args!( + )), + instantiate_per_input_byte: to_weight!(cost_byte_batched_args!( seal_instantiate_per_transfer_input_salt_kb, 0, 1, 0 - ), - instantiate_per_salt_byte: cost_byte_batched_args!( + )), + instantiate_per_salt_byte: to_weight!(cost_byte_batched_args!( seal_instantiate_per_transfer_input_salt_kb, 0, 0, 1 - ), - hash_sha2_256: cost_batched!(seal_hash_sha2_256), - hash_sha2_256_per_byte: cost_byte_batched!(seal_hash_sha2_256_per_kb), - hash_keccak_256: cost_batched!(seal_hash_keccak_256), - hash_keccak_256_per_byte: cost_byte_batched!(seal_hash_keccak_256_per_kb), - hash_blake2_256: cost_batched!(seal_hash_blake2_256), - hash_blake2_256_per_byte: cost_byte_batched!(seal_hash_blake2_256_per_kb), - hash_blake2_128: cost_batched!(seal_hash_blake2_128), - hash_blake2_128_per_byte: cost_byte_batched!(seal_hash_blake2_128_per_kb), - ecdsa_recover: cost_batched!(seal_ecdsa_recover), - ecdsa_to_eth_address: cost_batched!(seal_ecdsa_to_eth_address), - reentrance_count: cost_batched!(seal_reentrance_count), - account_reentrance_count: cost_batched!(seal_account_reentrance_count), - instantiation_nonce: cost_batched!(seal_instantiation_nonce), + )), + hash_sha2_256: to_weight!(cost_batched!(seal_hash_sha2_256)), + hash_sha2_256_per_byte: to_weight!(cost_byte_batched!(seal_hash_sha2_256_per_kb)), + hash_keccak_256: to_weight!(cost_batched!(seal_hash_keccak_256)), + hash_keccak_256_per_byte: to_weight!(cost_byte_batched!(seal_hash_keccak_256_per_kb)), + hash_blake2_256: to_weight!(cost_batched!(seal_hash_blake2_256)), + hash_blake2_256_per_byte: to_weight!(cost_byte_batched!(seal_hash_blake2_256_per_kb)), + hash_blake2_128: to_weight!(cost_batched!(seal_hash_blake2_128)), + hash_blake2_128_per_byte: to_weight!(cost_byte_batched!(seal_hash_blake2_128_per_kb)), + ecdsa_recover: to_weight!(cost_batched!(seal_ecdsa_recover)), + ecdsa_to_eth_address: to_weight!(cost_batched!(seal_ecdsa_to_eth_address)), + reentrance_count: to_weight!(cost_batched!(seal_reentrance_count)), + account_reentrance_count: to_weight!(cost_batched!(seal_account_reentrance_count)), + instantiation_nonce: to_weight!(cost_batched!(seal_instantiation_nonce)), _phantom: PhantomData, } } diff --git a/frame/contracts/src/tests.rs b/frame/contracts/src/tests.rs index 7a0736d1e..b9462ce45 100644 --- a/frame/contracts/src/tests.rs +++ b/frame/contracts/src/tests.rs @@ -416,7 +416,7 @@ pub const BOB: AccountId32 = AccountId32::new([2u8; 32]); pub const CHARLIE: AccountId32 = AccountId32::new([3u8; 32]); pub const DJANGO: AccountId32 = AccountId32::new([4u8; 32]); -pub const GAS_LIMIT: Weight = Weight::from_ref_time(100_000_000_000).set_proof_size(256 * 1024); +pub const GAS_LIMIT: Weight = Weight::from_parts(100_000_000_000, 512 * 1024); pub struct ExtBuilder { existential_deposit: u64, @@ -2837,7 +2837,7 @@ fn gas_estimation_call_runtime() { let call = RuntimeCall::Contracts(crate::Call::call { dest: addr_callee, value: 0, - gas_limit: GAS_LIMIT.set_ref_time(GAS_LIMIT.ref_time() / 3), + gas_limit: GAS_LIMIT / 3, storage_deposit_limit: None, data: vec![], }); diff --git a/frame/contracts/src/wasm/mod.rs b/frame/contracts/src/wasm/mod.rs index b540b3deb..c2b061176 100644 --- a/frame/contracts/src/wasm/mod.rs +++ b/frame/contracts/src/wasm/mod.rs @@ -430,7 +430,7 @@ mod tests { events: Default::default(), runtime_calls: Default::default(), schedule: Default::default(), - gas_meter: GasMeter::new(Weight::from_ref_time(10_000_000_000)), + gas_meter: GasMeter::new(Weight::from_parts(10_000_000_000, 10 * 1024 * 1024)), debug_buffer: Default::default(), ecdsa_recover: Default::default(), } diff --git a/frame/contracts/src/wasm/runtime.rs b/frame/contracts/src/wasm/runtime.rs index b5398bb62..745583b33 100644 --- a/frame/contracts/src/wasm/runtime.rs +++ b/frame/contracts/src/wasm/runtime.rs @@ -253,7 +253,7 @@ pub enum RuntimeCosts { /// Weight of calling `seal_ecdsa_recover`. EcdsaRecovery, /// Weight charged by a chain extension through `seal_call_chain_extension`. - ChainExtension(u64), + ChainExtension(Weight), /// Weight charged for calling into the runtime. CallRuntime(Weight), /// Weight of calling `seal_set_code_hash` @@ -272,7 +272,7 @@ impl RuntimeCosts { fn token(&self, s: &HostFnWeights) -> RuntimeToken { use self::RuntimeCosts::*; let weight = match *self { - MeteringBlock(amount) => s.gas.saturating_add(amount), + MeteringBlock(amount) => s.gas.saturating_add(Weight::from_ref_time(amount)), CopyFromContract(len) => s.return_per_byte.saturating_mul(len.into()), CopyToContract(len) => s.input_per_byte.saturating_mul(len.into()), Caller => s.caller, @@ -335,8 +335,8 @@ impl RuntimeCosts { .hash_blake2_128 .saturating_add(s.hash_blake2_128_per_byte.saturating_mul(len.into())), EcdsaRecovery => s.ecdsa_recover, - ChainExtension(amount) => amount, - CallRuntime(weight) => weight.ref_time(), + ChainExtension(weight) => weight, + CallRuntime(weight) => weight, SetCodeHash => s.set_code_hash, EcdsaToEthAddress => s.ecdsa_to_eth_address, ReentrantCount => s.reentrance_count, @@ -346,7 +346,7 @@ impl RuntimeCosts { RuntimeToken { #[cfg(test)] _created_from: *self, - weight: Weight::from_ref_time(weight), + weight, } } } From af5a499a269542cfd7f1aef415c801575f083d4a Mon Sep 17 00:00:00 2001 From: Michal Kucharczyk <1728078+michalkucharczyk@users.noreply.github.com> Date: Wed, 18 Jan 2023 16:26:12 +0100 Subject: [PATCH 024/558] zobmienet tests are not supposed to fail (#13015) * zobmienet tests are not supposed to fail * Update scripts/ci/gitlab/pipeline/zombienet.yml Co-authored-by: Alexander Samusev <41779041+alvicsam@users.noreply.github.com> Co-authored-by: Alexander Samusev <41779041+alvicsam@users.noreply.github.com> --- scripts/ci/gitlab/pipeline/zombienet.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/scripts/ci/gitlab/pipeline/zombienet.yml b/scripts/ci/gitlab/pipeline/zombienet.yml index 8d772ff51..7dc98c006 100644 --- a/scripts/ci/gitlab/pipeline/zombienet.yml +++ b/scripts/ci/gitlab/pipeline/zombienet.yml @@ -30,7 +30,6 @@ after_script: - mkdir -p ./zombienet-logs - cp /tmp/zombie*/logs/* ./zombienet-logs/ - allow_failure: true retry: 2 tags: - zombienet-polkadot-integration-test From cba59778aa5207b11f7513e5cbb127ea47dd8924 Mon Sep 17 00:00:00 2001 From: Sasha Gryaznov Date: Wed, 18 Jan 2023 18:03:52 +0200 Subject: [PATCH 025/558] [contracts] Add integrity checks by pallet hook (#12993) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * integrity test for MaxCodeLen and CallStack::len() * integrity test for MaxDebugBufferLen * addressed review comments * fix append_debug_buffer() * ci fix * updated code_len_limit formula after further discussion * enlarged mem safe margin after discussion * +doc to Config trait associated types * Apply suggestions from code review Co-authored-by: Alexander Theißen * more lil fixes from code review feedback * lowered max call depth to satisfy mem limits * fix node runtime pallet params to satisfy integrity check * fix max call depth value calc Co-authored-by: Alexander Theißen --- bin/node/runtime/src/lib.rs | 4 +- frame/contracts/src/exec.rs | 24 ++++++----- frame/contracts/src/lib.rs | 75 ++++++++++++++++++++++++++++++++- frame/contracts/src/schedule.rs | 4 -- frame/contracts/src/tests.rs | 4 +- 5 files changed, 91 insertions(+), 20 deletions(-) diff --git a/bin/node/runtime/src/lib.rs b/bin/node/runtime/src/lib.rs index fb5da9749..0ac6ade91 100644 --- a/bin/node/runtime/src/lib.rs +++ b/bin/node/runtime/src/lib.rs @@ -1206,7 +1206,7 @@ impl pallet_contracts::Config for Runtime { type CallFilter = Nothing; type DepositPerItem = DepositPerItem; type DepositPerByte = DepositPerByte; - type CallStack = [pallet_contracts::Frame; 31]; + type CallStack = [pallet_contracts::Frame; 5]; type WeightPrice = pallet_transaction_payment::Pallet; type WeightInfo = pallet_contracts::weights::SubstrateWeight; type ChainExtension = (); @@ -1214,7 +1214,7 @@ impl pallet_contracts::Config for Runtime { type DeletionWeightLimit = DeletionWeightLimit; type Schedule = Schedule; type AddressGenerator = pallet_contracts::DefaultAddressGenerator; - type MaxCodeLen = ConstU32<{ 128 * 1024 }>; + type MaxCodeLen = ConstU32<{ 123 * 1024 }>; type MaxStorageKeyLen = ConstU32<128>; type UnsafeUnstableInterface = ConstBool; type MaxDebugBufferLen = ConstU32<{ 2 * 1024 * 1024 }>; diff --git a/frame/contracts/src/exec.rs b/frame/contracts/src/exec.rs index 8ae70b804..b5b630653 100644 --- a/frame/contracts/src/exec.rs +++ b/frame/contracts/src/exec.rs @@ -1336,7 +1336,18 @@ where fn append_debug_buffer(&mut self, msg: &str) -> bool { if let Some(buffer) = &mut self.debug_message { - let mut msg = msg.bytes(); + let err_msg = scale_info::prelude::format!( + "Debug message too big (size={}) for debug buffer (bound={})", + msg.len(), + DebugBufferVec::::bound(), + ); + + let mut msg = if msg.len() > DebugBufferVec::::bound() { + err_msg.bytes() + } else { + msg.bytes() + }; + let num_drain = { let capacity = DebugBufferVec::::bound().checked_sub(buffer.len()).expect( " @@ -1349,16 +1360,7 @@ where msg.len().saturating_sub(capacity).min(buffer.len()) }; buffer.drain(0..num_drain); - buffer - .try_extend(&mut msg) - .map_err(|_| { - log::debug!( - target: "runtime::contracts", - "Debug message to big (size={}) for debug buffer (bound={})", - msg.len(), DebugBufferVec::::bound(), - ); - }) - .ok(); + buffer.try_extend(&mut msg).ok(); true } else { false diff --git a/frame/contracts/src/lib.rs b/frame/contracts/src/lib.rs index 581798e44..672c4517d 100644 --- a/frame/contracts/src/lib.rs +++ b/frame/contracts/src/lib.rs @@ -123,6 +123,7 @@ use pallet_contracts_primitives::{ StorageDeposit, }; use scale_info::TypeInfo; +use smallvec::Array; use sp_runtime::traits::{Convert, Hash, Saturating, StaticLookup, TrailingZeroInput}; use sp_std::{fmt::Debug, marker::PhantomData, prelude::*}; @@ -272,7 +273,10 @@ pub mod pallet { /// The allowed depth is `CallStack::size() + 1`. /// Therefore a size of `0` means that a contract cannot use call or instantiate. /// In other words only the origin called "root contract" is allowed to execute then. - type CallStack: smallvec::Array>; + /// + /// This setting along with [`MaxCodeLen`](#associatedtype.MaxCodeLen) directly affects + /// memory usage of your runtime. + type CallStack: Array>; /// The maximum number of contracts that can be pending for deletion. /// @@ -323,6 +327,10 @@ pub mod pallet { /// The maximum length of a contract code in bytes. This limit applies to the instrumented /// version of the code. Therefore `instantiate_with_code` can fail even when supplying /// a wasm binary below this maximum size. + /// + /// The value should be chosen carefully taking into the account the overall memory limit + /// your runtime has, as well as the [maximum allowed callstack + /// depth](#associatedtype.CallStack). Look into the `integrity_test()` for some insights. #[pallet::constant] type MaxCodeLen: Get; @@ -372,6 +380,71 @@ pub mod pallet { T::WeightInfo::on_process_deletion_queue_batch() } } + + fn integrity_test() { + // Total runtime memory is expected to have 128Mb upper limit + const MAX_RUNTIME_MEM: u32 = 1024 * 1024 * 128; + // Memory limits for a single contract: + // Value stack size: 1Mb per contract, default defined in wasmi + const MAX_STACK_SIZE: u32 = 1024 * 1024; + // Heap limit is normally 16 mempages of 64kb each = 1Mb per contract + let max_heap_size = T::Schedule::get().limits.max_memory_size(); + // Max call depth is CallStack::size() + 1 + let max_call_depth = u32::try_from(T::CallStack::size().saturating_add(1)) + .expect("CallStack size is too big"); + + // Check that given configured `MaxCodeLen`, runtime heap memory limit can't be broken. + // + // In worst case, the decoded wasm contract code would be `x16` times larger than the + // encoded one. This is because even a single-byte wasm instruction has 16-byte size in + // wasmi. This gives us `MaxCodeLen*16` safety margin. + // + // Next, the pallet keeps both the original and instrumented wasm blobs for each + // contract, hence we add up `MaxCodeLen*2` more to the safety margin. + // + // Finally, the inefficiencies of the freeing-bump allocator + // being used in the client for the runtime memory allocations, could lead to possible + // memory allocations for contract code grow up to `x4` times in some extreme cases, + // which gives us total multiplier of `18*4` for `MaxCodeLen`. + // + // That being said, for every contract executed in runtime, at least `MaxCodeLen*18*4` + // memory should be available. Note that maximum allowed heap memory and stack size per + // each contract (stack frame) should also be counted. + // + // Finally, we allow 50% of the runtime memory to be utilized by the contracts call + // stack, keeping the rest for other facilities, such as PoV, etc. + // + // This gives us the following formula: + // + // `(MaxCodeLen * 18 * 4 + MAX_STACK_SIZE + max_heap_size) * max_call_depth < + // MAX_RUNTIME_MEM/2` + // + // Hence the upper limit for the `MaxCodeLen` can be defined as follows: + let code_len_limit = MAX_RUNTIME_MEM + .saturating_div(2) + .saturating_div(max_call_depth) + .saturating_sub(max_heap_size) + .saturating_sub(MAX_STACK_SIZE) + .saturating_div(18 * 4); + + assert!( + T::MaxCodeLen::get() < code_len_limit, + "Given `CallStack` height {:?}, `MaxCodeLen` should be set less than {:?} \ + (current value is {:?}), to avoid possible runtime oom issues.", + max_call_depth, + code_len_limit, + T::MaxCodeLen::get(), + ); + + // Debug buffer should at least be large enough to accomodate a simple error message + const MIN_DEBUG_BUF_SIZE: u32 = 256; + assert!( + T::MaxDebugBufferLen::get() > MIN_DEBUG_BUF_SIZE, + "Debug buffer should have minimum size of {} (current setting is {})", + MIN_DEBUG_BUF_SIZE, + T::MaxDebugBufferLen::get(), + ) + } } #[pallet::call] diff --git a/frame/contracts/src/schedule.rs b/frame/contracts/src/schedule.rs index dabdc7013..eabd18c81 100644 --- a/frame/contracts/src/schedule.rs +++ b/frame/contracts/src/schedule.rs @@ -134,9 +134,6 @@ pub struct Limits { /// The maximum length of a subject in bytes used for PRNG generation. pub subject_len: u32, - /// The maximum nesting level of the call stack. - pub call_depth: u32, - /// The maximum size of a storage value and event payload in bytes. pub payload_len: u32, } @@ -532,7 +529,6 @@ impl Default for Limits { table_size: 4096, br_table_size: 256, subject_len: 32, - call_depth: 32, payload_len: 16 * 1024, } } diff --git a/frame/contracts/src/tests.rs b/frame/contracts/src/tests.rs index b9462ce45..ce84da743 100644 --- a/frame/contracts/src/tests.rs +++ b/frame/contracts/src/tests.rs @@ -394,7 +394,7 @@ impl Config for Test { type RuntimeEvent = RuntimeEvent; type RuntimeCall = RuntimeCall; type CallFilter = TestFilter; - type CallStack = [Frame; 31]; + type CallStack = [Frame; 5]; type WeightPrice = Self; type WeightInfo = (); type ChainExtension = @@ -405,7 +405,7 @@ impl Config for Test { type DepositPerByte = DepositPerByte; type DepositPerItem = DepositPerItem; type AddressGenerator = DefaultAddressGenerator; - type MaxCodeLen = ConstU32<{ 128 * 1024 }>; + type MaxCodeLen = ConstU32<{ 123 * 1024 }>; type MaxStorageKeyLen = ConstU32<128>; type UnsafeUnstableInterface = UnstableInterface; type MaxDebugBufferLen = ConstU32<{ 2 * 1024 * 1024 }>; From 911c9ab067d3cf487cfef61906ed2525cb87a5c9 Mon Sep 17 00:00:00 2001 From: Tsvetomir Dimitrov Date: Thu, 19 Jan 2023 13:32:50 +0200 Subject: [PATCH 026/558] Expose `UnknownBlock` error via `ApiError` (#12707) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Expose `UnknownBlock` error via `ApiError` In [certain cases](https://github.com/paritytech/polkadot/issues/5885) a runtime api is called for an unknown block. For example a block which is already pruned or on an abandon fork. In such cases the correct error is returned but it is wrapped in `ApiError::Application` and the only way to figure out what is the problem is to inspect the actual message in the error. In polkadot for example this usually happens when the runtime api version is being queried. It's beneficial to be able to clearly separate such errors so i that when they occur the client side can handle them more gracefully. E.g. log less stressful error message than `State already discarded for BlockId` or cancel any pending work related on this block. * Update primitives/api/src/lib.rs Co-authored-by: Bastian Köcher Co-authored-by: Bastian Köcher --- primitives/api/src/lib.rs | 2 ++ primitives/blockchain/src/error.rs | 1 + 2 files changed, 3 insertions(+) diff --git a/primitives/api/src/lib.rs b/primitives/api/src/lib.rs index 91d4b07a1..4ff4becb8 100644 --- a/primitives/api/src/lib.rs +++ b/primitives/api/src/lib.rs @@ -519,6 +519,8 @@ pub enum ApiError { StateBackendIsNotTrie, #[error(transparent)] Application(#[from] Box), + #[error("Api called for an unknown Block: {0}")] + UnknownBlock(String), } /// Extends the runtime api implementation with some common functionality. diff --git a/primitives/blockchain/src/error.rs b/primitives/blockchain/src/error.rs index 783c40c40..6585cc54f 100644 --- a/primitives/blockchain/src/error.rs +++ b/primitives/blockchain/src/error.rs @@ -191,6 +191,7 @@ impl From> for Error { impl From for ApiError { fn from(err: Error) -> ApiError { match err { + Error::UnknownBlock(msg) => ApiError::UnknownBlock(msg), Error::RuntimeApiError(err) => err, e => ApiError::Application(Box::new(e)), } From d4384f242f48fd62516aac02c3f18b5a8c9e5793 Mon Sep 17 00:00:00 2001 From: Sebastian Kunert Date: Thu, 19 Jan 2023 16:13:16 +0100 Subject: [PATCH 027/558] Notification-based block pinning (#13157) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Worker * Reorganize and unpin onnotification drop * Pin in state-db, pass block number * Pin blocks in blockchain db * Switch to reference counted LRU * Disable pinning when we keep all blocks * Fix pinning hint for state-db * Remove pinning from backend layer * Improve readability * Add justifications to test * Fix justification behaviour * Remove debug prints * Convert channels to tracing_unbounded * Add comments to the test * Documentation and Cleanup * Move task start to client * Simplify cache * Improve test, remove unwanted log * Add tracing logs, remove expect for block number * Cleanup * Add conversion method for unpin handle to Finalitynotification * Revert unwanted changes * Improve naming * Make clippy happy * Fix docs Co-authored-by: Michal Kucharczyk <1728078+michalkucharczyk@users.noreply.github.com> * Use `NumberFor` instead of u64 in API * Hand over weak reference to unpin worker task * Unwanted * &Hash -> Hash * Remove number from interface, rename `_unpin_handle`, LOG_TARGET * Move RwLock one layer up * Apply code style suggestions * Improve comments * Replace lru crate by schnellru * Only insert values for pinned items + better docs * Apply suggestions from code review Co-authored-by: Bastian Köcher * Improve comments, log target and test Co-authored-by: Michal Kucharczyk <1728078+michalkucharczyk@users.noreply.github.com> Co-authored-by: Bastian Köcher --- Cargo.lock | 52 +- client/api/src/backend.rs | 22 +- client/api/src/client.rs | 109 +++- client/api/src/in_mem.rs | 6 + client/db/Cargo.toml | 1 + client/db/src/lib.rs | 531 ++++++++++++++++-- client/db/src/pinned_blocks_cache.rs | 231 ++++++++ client/finality-grandpa/src/until_imported.rs | 13 +- client/service/src/builder.rs | 4 +- client/service/src/client/client.rs | 77 ++- test-utils/client/src/lib.rs | 7 +- 11 files changed, 954 insertions(+), 99 deletions(-) create mode 100644 client/db/src/pinned_blocks_cache.rs diff --git a/Cargo.lock b/Cargo.lock index 446638277..100e0d215 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -137,6 +137,18 @@ dependencies = [ "version_check", ] +[[package]] +name = "ahash" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bf6ccdb167abbf410dcb915cabd428929d7f6a04980b54a11f26a39f1c7f7107" +dependencies = [ + "cfg-if", + "getrandom 0.2.8", + "once_cell", + "version_check", +] + [[package]] name = "aho-corasick" version = "0.7.20" @@ -1836,7 +1848,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7c24f403d068ad0b359e577a77f92392118be3f3c927538f2bb544a5ecd828c6" dependencies = [ "curve25519-dalek 3.2.0", - "hashbrown", + "hashbrown 0.12.3", "hex", "rand_core 0.6.4", "sha2 0.9.9", @@ -2806,9 +2818,15 @@ version = "0.12.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" dependencies = [ - "ahash", + "ahash 0.7.6", ] +[[package]] +name = "hashbrown" +version = "0.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "43a3c133739dddd0d2990f9a4bdf8eb4b21ef50e4851ca85ab661199821d510e" + [[package]] name = "heck" version = "0.4.0" @@ -3122,7 +3140,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1885e79c1fc4b10f0e172c475f458b7f7b93061064d98c3293e98c5ba0c8b399" dependencies = [ "autocfg", - "hashbrown", + "hashbrown 0.12.3", "serde", ] @@ -4172,7 +4190,7 @@ version = "0.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b6e8aaa3f231bb4bd57b84b2d5dc3ae7f350265df8aa96492e0bc394a1571909" dependencies = [ - "hashbrown", + "hashbrown 0.12.3", ] [[package]] @@ -4310,7 +4328,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5e0c7cba9ce19ac7ffd2053ac9f49843bbd3f4318feedfd74e85c19d5fb0ba66" dependencies = [ "hash-db", - "hashbrown", + "hashbrown 0.12.3", ] [[package]] @@ -5085,7 +5103,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "21158b2c33aa6d4561f1c0a6ea283ca92bc54802a93b263e910746d679a7eb53" dependencies = [ "crc32fast", - "hashbrown", + "hashbrown 0.12.3", "indexmap", "memchr", ] @@ -7991,6 +8009,7 @@ dependencies = [ "rand 0.8.5", "sc-client-api", "sc-state-db", + "schnellru", "sp-arithmetic", "sp-blockchain", "sp-core", @@ -8334,7 +8353,7 @@ dependencies = [ name = "sc-finality-grandpa" version = "0.10.0-dev" dependencies = [ - "ahash", + "ahash 0.7.6", "array-bytes", "assert_matches", "async-trait", @@ -8534,7 +8553,7 @@ dependencies = [ name = "sc-network-gossip" version = "0.10.0-dev" dependencies = [ - "ahash", + "ahash 0.7.6", "futures", "futures-timer", "libp2p", @@ -9132,6 +9151,17 @@ dependencies = [ "windows-sys 0.36.1", ] +[[package]] +name = "schnellru" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "772575a524feeb803e5b0fcbc6dd9f367e579488197c94c6e4023aad2305774d" +dependencies = [ + "ahash 0.8.2", + "cfg-if", + "hashbrown 0.13.2", +] + [[package]] name = "schnorrkel" version = "0.9.1" @@ -10275,11 +10305,11 @@ dependencies = [ name = "sp-trie" version = "7.0.0" dependencies = [ - "ahash", + "ahash 0.7.6", "array-bytes", "criterion", "hash-db", - "hashbrown", + "hashbrown 0.12.3", "lazy_static", "lru", "memory-db", @@ -11279,7 +11309,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "004e1e8f92535694b4cb1444dc5a8073ecf0815e3357f729638b9f8fc4062908" dependencies = [ "hash-db", - "hashbrown", + "hashbrown 0.12.3", "log", "rustc-hex", "smallvec", diff --git a/client/api/src/backend.rs b/client/api/src/backend.rs index 21d213ffb..4ef609bdd 100644 --- a/client/api/src/backend.rs +++ b/client/api/src/backend.rs @@ -436,12 +436,24 @@ pub trait StorageProvider> { /// /// Manages the data layer. /// -/// Note on state pruning: while an object from `state_at` is alive, the state +/// # State Pruning +/// +/// While an object from `state_at` is alive, the state /// should not be pruned. The backend should internally reference-count /// its state objects. /// /// The same applies for live `BlockImportOperation`s: while an import operation building on a /// parent `P` is alive, the state for `P` should not be pruned. +/// +/// # Block Pruning +/// +/// Users can pin blocks in memory by calling `pin_block`. When +/// a block would be pruned, its value is kept in an in-memory cache +/// until it is unpinned via `unpin_block`. +/// +/// While a block is pinned, its state is also preserved. +/// +/// The backend should internally reference count the number of pin / unpin calls. pub trait Backend: AuxStore + Send + Sync { /// Associated block insertion operation type. type BlockImportOperation: BlockImportOperation; @@ -502,6 +514,14 @@ pub trait Backend: AuxStore + Send + Sync { /// Returns a handle to offchain storage. fn offchain_storage(&self) -> Option; + /// Pin the block to keep body, justification and state available after pruning. + /// Number of pins are reference counted. Users need to make sure to perform + /// one call to [`Self::unpin_block`] per call to [`Self::pin_block`]. + fn pin_block(&self, hash: Block::Hash) -> sp_blockchain::Result<()>; + + /// Unpin the block to allow pruning. + fn unpin_block(&self, hash: Block::Hash); + /// Returns true if state for given block is available. fn have_state_at(&self, hash: Block::Hash, _number: NumberFor) -> bool { self.state_at(hash).is_ok() diff --git a/client/api/src/client.rs b/client/api/src/client.rs index 0d00257fa..8e7ceb687 100644 --- a/client/api/src/client.rs +++ b/client/api/src/client.rs @@ -30,7 +30,7 @@ use std::{collections::HashSet, fmt, sync::Arc}; use crate::{blockchain::Info, notifications::StorageEventStream, FinalizeSummary, ImportSummary}; use sc_transaction_pool_api::ChainEvent; -use sc_utils::mpsc::TracingUnboundedReceiver; +use sc_utils::mpsc::{TracingUnboundedReceiver, TracingUnboundedSender}; use sp_blockchain; /// Type that implements `futures::Stream` of block import events. @@ -264,6 +264,53 @@ impl fmt::Display for UsageInfo { } } +/// Sends a message to the pinning-worker once dropped to unpin a block in the backend. +#[derive(Debug)] +pub struct UnpinHandleInner { + /// Hash of the block pinned by this handle + hash: Block::Hash, + unpin_worker_sender: TracingUnboundedSender, +} + +impl UnpinHandleInner { + /// Create a new [`UnpinHandleInner`] + pub fn new( + hash: Block::Hash, + unpin_worker_sender: TracingUnboundedSender, + ) -> Self { + Self { hash, unpin_worker_sender } + } +} + +impl Drop for UnpinHandleInner { + fn drop(&mut self) { + if let Err(err) = self.unpin_worker_sender.unbounded_send(self.hash) { + log::debug!(target: "db", "Unable to unpin block with hash: {}, error: {:?}", self.hash, err); + }; + } +} + +/// Keeps a specific block pinned while the handle is alive. +/// Once the last handle instance for a given block is dropped, the +/// block is unpinned in the [`Backend`](crate::backend::Backend::unpin_block). +#[derive(Debug, Clone)] +pub struct UnpinHandle(Arc>); + +impl UnpinHandle { + /// Create a new [`UnpinHandle`] + pub fn new( + hash: Block::Hash, + unpin_worker_sender: TracingUnboundedSender, + ) -> UnpinHandle { + UnpinHandle(Arc::new(UnpinHandleInner::new(hash, unpin_worker_sender))) + } + + /// Hash of the block this handle is unpinning on drop + pub fn hash(&self) -> Block::Hash { + self.0.hash + } +} + /// Summary of an imported block #[derive(Clone, Debug)] pub struct BlockImportNotification { @@ -279,6 +326,36 @@ pub struct BlockImportNotification { /// /// If `None`, there was no re-org while importing. pub tree_route: Option>>, + /// Handle to unpin the block this notification is for + unpin_handle: UnpinHandle, +} + +impl BlockImportNotification { + /// Create new notification + pub fn new( + hash: Block::Hash, + origin: BlockOrigin, + header: Block::Header, + is_new_best: bool, + tree_route: Option>>, + unpin_worker_sender: TracingUnboundedSender, + ) -> Self { + Self { + hash, + origin, + header, + is_new_best, + tree_route, + unpin_handle: UnpinHandle::new(hash, unpin_worker_sender), + } + } + + /// Consume this notification and extract the unpin handle. + /// + /// Note: Only use this if you want to keep the block pinned in the backend. + pub fn into_unpin_handle(self) -> UnpinHandle { + self.unpin_handle + } } /// Summary of a finalized block. @@ -294,6 +371,8 @@ pub struct FinalityNotification { pub tree_route: Arc<[Block::Hash]>, /// Stale branches heads. pub stale_heads: Arc<[Block::Hash]>, + /// Handle to unpin the block this notification is for + unpin_handle: UnpinHandle, } impl TryFrom> for ChainEvent { @@ -314,26 +393,44 @@ impl From> for ChainEvent { } } -impl From> for FinalityNotification { - fn from(mut summary: FinalizeSummary) -> Self { +impl FinalityNotification { + /// Create finality notification from finality summary. + pub fn from_summary( + mut summary: FinalizeSummary, + unpin_worker_sender: TracingUnboundedSender, + ) -> FinalityNotification { let hash = summary.finalized.pop().unwrap_or_default(); FinalityNotification { hash, header: summary.header, tree_route: Arc::from(summary.finalized), stale_heads: Arc::from(summary.stale_heads), + unpin_handle: UnpinHandle::new(hash, unpin_worker_sender), } } + + /// Consume this notification and extract the unpin handle. + /// + /// Note: Only use this if you want to keep the block pinned in the backend. + pub fn into_unpin_handle(self) -> UnpinHandle { + self.unpin_handle + } } -impl From> for BlockImportNotification { - fn from(summary: ImportSummary) -> Self { +impl BlockImportNotification { + /// Create finality notification from finality summary. + pub fn from_summary( + summary: ImportSummary, + unpin_worker_sender: TracingUnboundedSender, + ) -> BlockImportNotification { + let hash = summary.hash; BlockImportNotification { - hash: summary.hash, + hash, origin: summary.origin, header: summary.header, is_new_best: summary.is_new_best, tree_route: summary.tree_route.map(Arc::new), + unpin_handle: UnpinHandle::new(hash, unpin_worker_sender), } } } diff --git a/client/api/src/in_mem.rs b/client/api/src/in_mem.rs index 144aa352f..5e82757e7 100644 --- a/client/api/src/in_mem.rs +++ b/client/api/src/in_mem.rs @@ -788,6 +788,12 @@ where fn requires_full_sync(&self) -> bool { false } + + fn pin_block(&self, _: ::Hash) -> blockchain::Result<()> { + Ok(()) + } + + fn unpin_block(&self, _: ::Hash) {} } impl backend::LocalBackend for Backend where Block::Hash: Ord {} diff --git a/client/db/Cargo.toml b/client/db/Cargo.toml index 562a94f19..2d7291a01 100644 --- a/client/db/Cargo.toml +++ b/client/db/Cargo.toml @@ -26,6 +26,7 @@ parity-db = "0.4.2" parking_lot = "0.12.1" sc-client-api = { version = "4.0.0-dev", path = "../api" } sc-state-db = { version = "0.10.0-dev", path = "../state-db" } +schnellru = "0.2.1" sp-arithmetic = { version = "6.0.0", path = "../../primitives/arithmetic" } sp-blockchain = { version = "4.0.0-dev", path = "../../primitives/blockchain" } sp-core = { version = "7.0.0", path = "../../primitives/core" } diff --git a/client/db/src/lib.rs b/client/db/src/lib.rs index 99fa786e0..09ccfef1c 100644 --- a/client/db/src/lib.rs +++ b/client/db/src/lib.rs @@ -34,6 +34,7 @@ pub mod bench; mod children; mod parity_db; +mod pinned_blocks_cache; mod record_stats_state; mod stats; #[cfg(any(feature = "rocksdb", test))] @@ -51,6 +52,7 @@ use std::{ }; use crate::{ + pinned_blocks_cache::PinnedBlocksCache, record_stats_state::RecordStatsState, stats::StateUsageStats, utils::{meta_keys, read_db, read_meta, DatabaseType, Meta}, @@ -481,6 +483,7 @@ pub struct BlockchainDb { leaves: RwLock>>, header_metadata_cache: Arc>, header_cache: Mutex>>, + pinned_blocks_cache: Arc>>, } impl BlockchainDb { @@ -493,6 +496,7 @@ impl BlockchainDb { meta: Arc::new(RwLock::new(meta)), header_metadata_cache: Arc::new(HeaderMetadataCache::default()), header_cache: Default::default(), + pinned_blocks_cache: Arc::new(RwLock::new(PinnedBlocksCache::new())), }) } @@ -521,62 +525,83 @@ impl BlockchainDb { let mut meta = self.meta.write(); meta.block_gap = gap; } -} -impl sc_client_api::blockchain::HeaderBackend for BlockchainDb { - fn header(&self, hash: Block::Hash) -> ClientResult> { - let mut cache = self.header_cache.lock(); - if let Some(result) = cache.get_refresh(&hash) { - return Ok(result.clone()) + /// Empty the cache of pinned items. + fn clear_pinning_cache(&self) { + self.pinned_blocks_cache.write().clear(); + } + + /// Load a justification into the cache of pinned items. + /// Reference count of the item will not be increased. Use this + /// to load values for items into the cache which have already been pinned. + fn insert_justifications_if_pinned(&self, hash: Block::Hash, justification: Justification) { + let mut cache = self.pinned_blocks_cache.write(); + if !cache.contains(hash) { + return } - let header = utils::read_header( - &*self.db, - columns::KEY_LOOKUP, - columns::HEADER, - BlockId::::Hash(hash), - )?; - cache_header(&mut cache, hash, header.clone()); - Ok(header) + + let justifications = Justifications::from(justification); + cache.insert_justifications(hash, Some(justifications)); } - fn info(&self) -> sc_client_api::blockchain::Info { - let meta = self.meta.read(); - sc_client_api::blockchain::Info { - best_hash: meta.best_hash, - best_number: meta.best_number, - genesis_hash: meta.genesis_hash, - finalized_hash: meta.finalized_hash, - finalized_number: meta.finalized_number, - finalized_state: meta.finalized_state, - number_leaves: self.leaves.read().count(), - block_gap: meta.block_gap, + /// Load a justification from the db into the cache of pinned items. + /// Reference count of the item will not be increased. Use this + /// to load values for items into the cache which have already been pinned. + fn insert_persisted_justifications_if_pinned(&self, hash: Block::Hash) -> ClientResult<()> { + let mut cache = self.pinned_blocks_cache.write(); + if !cache.contains(hash) { + return Ok(()) } + + let justifications = self.justifications_uncached(hash)?; + cache.insert_justifications(hash, justifications); + Ok(()) } - fn status(&self, hash: Block::Hash) -> ClientResult { - match self.header(hash)?.is_some() { - true => Ok(sc_client_api::blockchain::BlockStatus::InChain), - false => Ok(sc_client_api::blockchain::BlockStatus::Unknown), + /// Load a block body from the db into the cache of pinned items. + /// Reference count of the item will not be increased. Use this + /// to load values for items items into the cache which have already been pinned. + fn insert_persisted_body_if_pinned(&self, hash: Block::Hash) -> ClientResult<()> { + let mut cache = self.pinned_blocks_cache.write(); + if !cache.contains(hash) { + return Ok(()) } + + let body = self.body_uncached(hash)?; + cache.insert_body(hash, body); + Ok(()) } - fn number(&self, hash: Block::Hash) -> ClientResult>> { - Ok(self.header_metadata(hash).ok().map(|header_metadata| header_metadata.number)) + /// Bump reference count for pinned item. + fn bump_ref(&self, hash: Block::Hash) { + self.pinned_blocks_cache.write().pin(hash); } - fn hash(&self, number: NumberFor) -> ClientResult> { - Ok(utils::read_header::( + /// Decrease reference count for pinned item and remove if reference count is 0. + fn unpin(&self, hash: Block::Hash) { + self.pinned_blocks_cache.write().unpin(hash); + } + + fn justifications_uncached(&self, hash: Block::Hash) -> ClientResult> { + match read_db( &*self.db, columns::KEY_LOOKUP, - columns::HEADER, - BlockId::Number(number), - )? - .map(|header| header.hash())) + columns::JUSTIFICATIONS, + BlockId::::Hash(hash), + )? { + Some(justifications) => match Decode::decode(&mut &justifications[..]) { + Ok(justifications) => Ok(Some(justifications)), + Err(err) => + return Err(sp_blockchain::Error::Backend(format!( + "Error decoding justifications: {}", + err + ))), + }, + None => Ok(None), + } } -} -impl sc_client_api::blockchain::Backend for BlockchainDb { - fn body(&self, hash: Block::Hash) -> ClientResult>> { + fn body_uncached(&self, hash: Block::Hash) -> ClientResult>> { if let Some(body) = read_db(&*self.db, columns::KEY_LOOKUP, columns::BODY, BlockId::Hash::(hash))? { @@ -640,24 +665,77 @@ impl sc_client_api::blockchain::Backend for BlockchainDb ClientResult> { - match read_db( +impl sc_client_api::blockchain::HeaderBackend for BlockchainDb { + fn header(&self, hash: Block::Hash) -> ClientResult> { + let mut cache = self.header_cache.lock(); + if let Some(result) = cache.get_refresh(&hash) { + return Ok(result.clone()) + } + let header = utils::read_header( &*self.db, columns::KEY_LOOKUP, - columns::JUSTIFICATIONS, + columns::HEADER, BlockId::::Hash(hash), - )? { - Some(justifications) => match Decode::decode(&mut &justifications[..]) { - Ok(justifications) => Ok(Some(justifications)), - Err(err) => - return Err(sp_blockchain::Error::Backend(format!( - "Error decoding justifications: {}", - err - ))), - }, - None => Ok(None), + )?; + cache_header(&mut cache, hash, header.clone()); + Ok(header) + } + + fn info(&self) -> sc_client_api::blockchain::Info { + let meta = self.meta.read(); + sc_client_api::blockchain::Info { + best_hash: meta.best_hash, + best_number: meta.best_number, + genesis_hash: meta.genesis_hash, + finalized_hash: meta.finalized_hash, + finalized_number: meta.finalized_number, + finalized_state: meta.finalized_state, + number_leaves: self.leaves.read().count(), + block_gap: meta.block_gap, + } + } + + fn status(&self, hash: Block::Hash) -> ClientResult { + match self.header(hash)?.is_some() { + true => Ok(sc_client_api::blockchain::BlockStatus::InChain), + false => Ok(sc_client_api::blockchain::BlockStatus::Unknown), + } + } + + fn number(&self, hash: Block::Hash) -> ClientResult>> { + Ok(self.header_metadata(hash).ok().map(|header_metadata| header_metadata.number)) + } + + fn hash(&self, number: NumberFor) -> ClientResult> { + Ok(utils::read_header::( + &*self.db, + columns::KEY_LOOKUP, + columns::HEADER, + BlockId::Number(number), + )? + .map(|header| header.hash())) + } +} + +impl sc_client_api::blockchain::Backend for BlockchainDb { + fn body(&self, hash: Block::Hash) -> ClientResult>> { + let cache = self.pinned_blocks_cache.read(); + if let Some(result) = cache.body(&hash) { + return Ok(result.clone()) + } + + self.body_uncached(hash) + } + + fn justifications(&self, hash: Block::Hash) -> ClientResult> { + let cache = self.pinned_blocks_cache.read(); + if let Some(result) = cache.justifications(&hash) { + return Ok(result.clone()) } + + self.justifications_uncached(hash) } fn last_finalized(&self) -> ClientResult { @@ -1291,20 +1369,28 @@ impl Backend { header: &Block::Header, last_finalized: Option, justification: Option, + current_transaction_justifications: &mut HashMap, ) -> ClientResult> { // TODO: ensure best chain contains this block. let number = *header.number(); self.ensure_sequential_finalization(header, last_finalized)?; let with_state = sc_client_api::Backend::have_state_at(self, hash, number); - self.note_finalized(transaction, header, hash, with_state)?; + self.note_finalized( + transaction, + header, + hash, + with_state, + current_transaction_justifications, + )?; if let Some(justification) = justification { transaction.set_from_vec( columns::JUSTIFICATIONS, &utils::number_and_hash_to_lookup_key(number, hash)?, - Justifications::from(justification).encode(), + Justifications::from(justification.clone()).encode(), ); + current_transaction_justifications.insert(hash, justification); } Ok(MetaUpdate { hash, number, is_best: false, is_finalized: true, with_state }) } @@ -1371,6 +1457,8 @@ impl Backend { (meta.best_number, meta.finalized_hash, meta.finalized_number, meta.block_gap) }; + let mut current_transaction_justifications: HashMap = + HashMap::new(); for (block_hash, justification) in operation.finalized_blocks { let block_header = self.blockchain.expect_header(block_hash)?; meta_updates.push(self.finalize_block_with_transaction( @@ -1379,6 +1467,7 @@ impl Backend { &block_header, Some(last_finalized_hash), justification, + &mut current_transaction_justifications, )?); last_finalized_hash = block_hash; last_finalized_num = *block_header.number(); @@ -1551,7 +1640,14 @@ impl Backend { if finalized { // TODO: ensure best chain contains this block. self.ensure_sequential_finalization(header, Some(last_finalized_hash))?; - self.note_finalized(&mut transaction, header, hash, operation.commit_state)?; + let mut current_transaction_justifications = HashMap::new(); + self.note_finalized( + &mut transaction, + header, + hash, + operation.commit_state, + &mut current_transaction_justifications, + )?; } else { // canonicalize blocks which are old enough, regardless of finality. self.force_delayed_canonicalize(&mut transaction)? @@ -1684,6 +1780,7 @@ impl Backend { f_header: &Block::Header, f_hash: Block::Hash, with_state: bool, + current_transaction_justifications: &mut HashMap, ) -> ClientResult<()> { let f_num = *f_header.number(); @@ -1709,7 +1806,7 @@ impl Backend { } let new_displaced = self.blockchain.leaves.write().finalize_height(f_num); - self.prune_blocks(transaction, f_num, &new_displaced)?; + self.prune_blocks(transaction, f_num, &new_displaced, current_transaction_justifications)?; Ok(()) } @@ -1717,22 +1814,39 @@ impl Backend { fn prune_blocks( &self, transaction: &mut Transaction, - finalized: NumberFor, + finalized_number: NumberFor, displaced: &FinalizationOutcome>, + current_transaction_justifications: &mut HashMap, ) -> ClientResult<()> { match self.blocks_pruning { BlocksPruning::KeepAll => {}, BlocksPruning::Some(blocks_pruning) => { // Always keep the last finalized block let keep = std::cmp::max(blocks_pruning, 1); - if finalized >= keep.into() { - let number = finalized.saturating_sub(keep.into()); + if finalized_number >= keep.into() { + let number = finalized_number.saturating_sub(keep.into()); + + // Before we prune a block, check if it is pinned + if let Some(hash) = self.blockchain.hash(number)? { + self.blockchain.insert_persisted_body_if_pinned(hash)?; + + // If the block was finalized in this transaction, it will not be in the db + // yet. + if let Some(justification) = + current_transaction_justifications.remove(&hash) + { + self.blockchain.insert_justifications_if_pinned(hash, justification); + } else { + self.blockchain.insert_persisted_justifications_if_pinned(hash)?; + } + }; + self.prune_block(transaction, BlockId::::number(number))?; } - self.prune_displaced_branches(transaction, finalized, displaced)?; + self.prune_displaced_branches(transaction, finalized_number, displaced)?; }, BlocksPruning::KeepFinalized => { - self.prune_displaced_branches(transaction, finalized, displaced)?; + self.prune_displaced_branches(transaction, finalized_number, displaced)?; }, } Ok(()) @@ -1755,6 +1869,8 @@ impl Backend { while self.blockchain.hash(number)? != Some(hash) { match self.blockchain.header(hash)? { Some(header) => { + self.blockchain.insert_persisted_body_if_pinned(hash)?; + self.prune_block(transaction, BlockId::::hash(hash))?; number = header.number().saturating_sub(One::one()); hash = *header.parent_hash(); @@ -1985,6 +2101,7 @@ impl sc_client_api::backend::Backend for Backend { .state_db .reset(state_meta_db) .map_err(sp_blockchain::Error::from_state_db)?; + self.blockchain.clear_pinning_cache(); Err(e) } else { self.storage.state_db.sync(); @@ -2000,12 +2117,14 @@ impl sc_client_api::backend::Backend for Backend { let mut transaction = Transaction::new(); let header = self.blockchain.expect_header(hash)?; + let mut current_transaction_justifications = HashMap::new(); let m = self.finalize_block_with_transaction( &mut transaction, hash, &header, None, justification, + &mut current_transaction_justifications, )?; self.storage.db.commit(transaction)?; @@ -2382,6 +2501,49 @@ impl sc_client_api::backend::Backend for Backend { PruningMode::ArchiveAll | PruningMode::ArchiveCanonical ) } + + fn pin_block(&self, hash: ::Hash) -> sp_blockchain::Result<()> { + let hint = || { + let header_metadata = self.blockchain.header_metadata(hash); + header_metadata + .map(|hdr| { + sc_state_db::NodeDb::get(self.storage.as_ref(), hdr.state_root.as_ref()) + .unwrap_or(None) + .is_some() + }) + .unwrap_or(false) + }; + + if let Some(number) = self.blockchain.number(hash)? { + self.storage.state_db.pin(&hash, number.saturated_into::(), hint).map_err( + |_| { + sp_blockchain::Error::UnknownBlock(format!( + "State already discarded for `{:?}`", + hash + )) + }, + )?; + } else { + return Err(ClientError::UnknownBlock(format!( + "Can not pin block with hash `{:?}`. Block not found.", + hash + ))) + } + + if self.blocks_pruning != BlocksPruning::KeepAll { + // Only increase reference count for this hash. Value is loaded once we prune. + self.blockchain.bump_ref(hash); + } + Ok(()) + } + + fn unpin_block(&self, hash: ::Hash) { + self.storage.state_db.unpin(&hash); + + if self.blocks_pruning != BlocksPruning::KeepAll { + self.blockchain.unpin(hash); + } + } } impl sc_client_api::backend::LocalBackend for Backend {} @@ -4009,4 +4171,249 @@ pub(crate) mod tests { assert_eq!(block4, backend.blockchain().hash(4).unwrap().unwrap()); } } + + #[test] + fn test_pinned_blocks_on_finalize() { + let backend = Backend::::new_test_with_tx_storage(BlocksPruning::Some(1), 10); + let mut blocks = Vec::new(); + let mut prev_hash = Default::default(); + + let build_justification = |i: u64| ([0, 0, 0, 0], vec![i.try_into().unwrap()]); + // Block tree: + // 0 -> 1 -> 2 -> 3 -> 4 + for i in 0..5 { + let hash = insert_block( + &backend, + i, + prev_hash, + None, + Default::default(), + vec![i.into()], + None, + ) + .unwrap(); + blocks.push(hash); + // Avoid block pruning. + backend.pin_block(blocks[i as usize]).unwrap(); + + prev_hash = hash; + } + + let bc = backend.blockchain(); + + // Check that we can properly access values when there is reference count + // but no value. + assert_eq!(Some(vec![1.into()]), bc.body(blocks[1]).unwrap()); + + // Block 1 gets pinned three times + backend.pin_block(blocks[1]).unwrap(); + backend.pin_block(blocks[1]).unwrap(); + + // Finalize all blocks. This will trigger pruning. + let mut op = backend.begin_operation().unwrap(); + backend.begin_state_operation(&mut op, blocks[4]).unwrap(); + for i in 1..5 { + op.mark_finalized(blocks[i], Some(build_justification(i.try_into().unwrap()))) + .unwrap(); + } + backend.commit_operation(op).unwrap(); + + // Block 0, 1, 2, 3 are pinned, so all values should be cached. + // Block 4 is inside the pruning window, its value is in db. + assert_eq!(Some(vec![0.into()]), bc.body(blocks[0]).unwrap()); + + assert_eq!(Some(vec![1.into()]), bc.body(blocks[1]).unwrap()); + assert_eq!( + Some(Justifications::from(build_justification(1))), + bc.justifications(blocks[1]).unwrap() + ); + + assert_eq!(Some(vec![2.into()]), bc.body(blocks[2]).unwrap()); + assert_eq!( + Some(Justifications::from(build_justification(2))), + bc.justifications(blocks[2]).unwrap() + ); + + assert_eq!(Some(vec![3.into()]), bc.body(blocks[3]).unwrap()); + assert_eq!( + Some(Justifications::from(build_justification(3))), + bc.justifications(blocks[3]).unwrap() + ); + + assert_eq!(Some(vec![4.into()]), bc.body(blocks[4]).unwrap()); + assert_eq!( + Some(Justifications::from(build_justification(4))), + bc.justifications(blocks[4]).unwrap() + ); + + // Unpin all blocks. Values should be removed from cache. + for block in &blocks { + backend.unpin_block(*block); + } + + assert!(bc.body(blocks[0]).unwrap().is_none()); + // Block 1 was pinned twice, we expect it to be still cached + assert!(bc.body(blocks[1]).unwrap().is_some()); + assert!(bc.justifications(blocks[1]).unwrap().is_some()); + // Headers should also be available while pinned + assert!(bc.header(blocks[1]).ok().flatten().is_some()); + assert!(bc.body(blocks[2]).unwrap().is_none()); + assert!(bc.justifications(blocks[2]).unwrap().is_none()); + assert!(bc.body(blocks[3]).unwrap().is_none()); + assert!(bc.justifications(blocks[3]).unwrap().is_none()); + + // After these unpins, block 1 should also be removed + backend.unpin_block(blocks[1]); + assert!(bc.body(blocks[1]).unwrap().is_some()); + assert!(bc.justifications(blocks[1]).unwrap().is_some()); + backend.unpin_block(blocks[1]); + assert!(bc.body(blocks[1]).unwrap().is_none()); + assert!(bc.justifications(blocks[1]).unwrap().is_none()); + + // Block 4 is inside the pruning window and still kept + assert_eq!(Some(vec![4.into()]), bc.body(blocks[4]).unwrap()); + assert_eq!( + Some(Justifications::from(build_justification(4))), + bc.justifications(blocks[4]).unwrap() + ); + + // Block tree: + // 0 -> 1 -> 2 -> 3 -> 4 -> 5 + let hash = + insert_block(&backend, 5, prev_hash, None, Default::default(), vec![5.into()], None) + .unwrap(); + blocks.push(hash); + + backend.pin_block(blocks[4]).unwrap(); + // Mark block 5 as finalized. + let mut op = backend.begin_operation().unwrap(); + backend.begin_state_operation(&mut op, blocks[5]).unwrap(); + op.mark_finalized(blocks[5], Some(build_justification(5))).unwrap(); + backend.commit_operation(op).unwrap(); + + assert!(bc.body(blocks[0]).unwrap().is_none()); + assert!(bc.body(blocks[1]).unwrap().is_none()); + assert!(bc.body(blocks[2]).unwrap().is_none()); + assert!(bc.body(blocks[3]).unwrap().is_none()); + + assert_eq!(Some(vec![4.into()]), bc.body(blocks[4]).unwrap()); + assert_eq!( + Some(Justifications::from(build_justification(4))), + bc.justifications(blocks[4]).unwrap() + ); + assert_eq!(Some(vec![5.into()]), bc.body(blocks[5]).unwrap()); + assert!(bc.header(blocks[5]).ok().flatten().is_some()); + + backend.unpin_block(blocks[4]); + assert!(bc.body(blocks[4]).unwrap().is_none()); + assert!(bc.justifications(blocks[4]).unwrap().is_none()); + + // Append a justification to block 5. + backend.append_justification(blocks[5], ([0, 0, 0, 1], vec![42])).unwrap(); + + let hash = + insert_block(&backend, 6, blocks[5], None, Default::default(), vec![6.into()], None) + .unwrap(); + blocks.push(hash); + + // Pin block 5 so it gets loaded into the cache on prune + backend.pin_block(blocks[5]).unwrap(); + + // Finalize block 6 so block 5 gets pruned. Since it is pinned both justifications should be + // in memory. + let mut op = backend.begin_operation().unwrap(); + backend.begin_state_operation(&mut op, blocks[6]).unwrap(); + op.mark_finalized(blocks[6], None).unwrap(); + backend.commit_operation(op).unwrap(); + + assert_eq!(Some(vec![5.into()]), bc.body(blocks[5]).unwrap()); + assert!(bc.header(blocks[5]).ok().flatten().is_some()); + let mut expected = Justifications::from(build_justification(5)); + expected.append(([0, 0, 0, 1], vec![42])); + assert_eq!(Some(expected), bc.justifications(blocks[5]).unwrap()); + } + + #[test] + fn test_pinned_blocks_on_finalize_with_fork() { + let backend = Backend::::new_test_with_tx_storage(BlocksPruning::Some(1), 10); + let mut blocks = Vec::new(); + let mut prev_hash = Default::default(); + + // Block tree: + // 0 -> 1 -> 2 -> 3 -> 4 + for i in 0..5 { + let hash = insert_block( + &backend, + i, + prev_hash, + None, + Default::default(), + vec![i.into()], + None, + ) + .unwrap(); + blocks.push(hash); + + // Avoid block pruning. + backend.pin_block(blocks[i as usize]).unwrap(); + + prev_hash = hash; + } + + // Insert a fork at the second block. + // Block tree: + // 0 -> 1 -> 2 -> 3 -> 4 + // \ -> 2 -> 3 + let fork_hash_root = + insert_block(&backend, 2, blocks[1], None, H256::random(), vec![2.into()], None) + .unwrap(); + let fork_hash_3 = insert_block( + &backend, + 3, + fork_hash_root, + None, + H256::random(), + vec![3.into(), 11.into()], + None, + ) + .unwrap(); + + // Do not prune the fork hash. + backend.pin_block(fork_hash_3).unwrap(); + + let mut op = backend.begin_operation().unwrap(); + backend.begin_state_operation(&mut op, blocks[4]).unwrap(); + op.mark_head(blocks[4]).unwrap(); + backend.commit_operation(op).unwrap(); + + for i in 1..5 { + let mut op = backend.begin_operation().unwrap(); + backend.begin_state_operation(&mut op, blocks[4]).unwrap(); + op.mark_finalized(blocks[i], None).unwrap(); + backend.commit_operation(op).unwrap(); + } + + let bc = backend.blockchain(); + assert_eq!(Some(vec![0.into()]), bc.body(blocks[0]).unwrap()); + assert_eq!(Some(vec![1.into()]), bc.body(blocks[1]).unwrap()); + assert_eq!(Some(vec![2.into()]), bc.body(blocks[2]).unwrap()); + assert_eq!(Some(vec![3.into()]), bc.body(blocks[3]).unwrap()); + assert_eq!(Some(vec![4.into()]), bc.body(blocks[4]).unwrap()); + // Check the fork hashes. + assert_eq!(None, bc.body(fork_hash_root).unwrap()); + assert_eq!(Some(vec![3.into(), 11.into()]), bc.body(fork_hash_3).unwrap()); + + // Unpin all blocks, except the forked one. + for block in &blocks { + backend.unpin_block(*block); + } + assert!(bc.body(blocks[0]).unwrap().is_none()); + assert!(bc.body(blocks[1]).unwrap().is_none()); + assert!(bc.body(blocks[2]).unwrap().is_none()); + assert!(bc.body(blocks[3]).unwrap().is_none()); + + assert!(bc.body(fork_hash_3).unwrap().is_some()); + backend.unpin_block(fork_hash_3); + assert!(bc.body(fork_hash_3).unwrap().is_none()); + } } diff --git a/client/db/src/pinned_blocks_cache.rs b/client/db/src/pinned_blocks_cache.rs new file mode 100644 index 000000000..39ff1c527 --- /dev/null +++ b/client/db/src/pinned_blocks_cache.rs @@ -0,0 +1,231 @@ +// This file is part of Substrate. + +// Copyright (C) 2017-2023 Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +use schnellru::{Limiter, LruMap}; +use sp_runtime::{traits::Block as BlockT, Justifications}; + +const LOG_TARGET: &str = "db::pin"; +const PINNING_CACHE_SIZE: usize = 1024; + +/// Entry for pinned blocks cache. +struct PinnedBlockCacheEntry { + /// How many times this item has been pinned + ref_count: u32, + + /// Cached justifications for this block + pub justifications: Option>, + + /// Cached body for this block + pub body: Option>>, +} + +impl Default for PinnedBlockCacheEntry { + fn default() -> Self { + Self { ref_count: 0, justifications: None, body: None } + } +} + +impl PinnedBlockCacheEntry { + pub fn decrease_ref(&mut self) { + self.ref_count = self.ref_count.saturating_sub(1); + } + + pub fn increase_ref(&mut self) { + self.ref_count = self.ref_count.saturating_add(1); + } + + pub fn has_no_references(&self) -> bool { + self.ref_count == 0 + } +} + +/// A limiter for a map which is limited by the number of elements. +#[derive(Copy, Clone, Debug)] +struct LoggingByLengthLimiter { + max_length: usize, +} + +impl LoggingByLengthLimiter { + /// Creates a new length limiter with a given `max_length`. + pub const fn new(max_length: usize) -> LoggingByLengthLimiter { + LoggingByLengthLimiter { max_length } + } +} + +impl Limiter> for LoggingByLengthLimiter { + type KeyToInsert<'a> = Block::Hash; + type LinkType = usize; + + fn is_over_the_limit(&self, length: usize) -> bool { + length > self.max_length + } + + fn on_insert( + &mut self, + _length: usize, + key: Self::KeyToInsert<'_>, + value: PinnedBlockCacheEntry, + ) -> Option<(Block::Hash, PinnedBlockCacheEntry)> { + if self.max_length > 0 { + Some((key, value)) + } else { + None + } + } + + fn on_replace( + &mut self, + _length: usize, + _old_key: &mut Block::Hash, + _new_key: Block::Hash, + _old_value: &mut PinnedBlockCacheEntry, + _new_value: &mut PinnedBlockCacheEntry, + ) -> bool { + true + } + + fn on_removed(&mut self, key: &mut Block::Hash, value: &mut PinnedBlockCacheEntry) { + // If reference count was larger than 0 on removal, + // the item was removed due to capacity limitations. + // Since the cache should be large enough for pinned items, + // we want to know about these evictions. + if value.ref_count > 0 { + log::warn!( + target: LOG_TARGET, + "Pinned block cache limit reached. Evicting value. hash = {}", + key + ); + } else { + log::trace!( + target: LOG_TARGET, + "Evicting value from pinned block cache. hash = {}", + key + ) + } + } + + fn on_cleared(&mut self) {} + + fn on_grow(&mut self, _new_memory_usage: usize) -> bool { + true + } +} + +/// Reference counted cache for pinned block bodies and justifications. +pub struct PinnedBlocksCache { + cache: LruMap, LoggingByLengthLimiter>, +} + +impl PinnedBlocksCache { + pub fn new() -> Self { + Self { cache: LruMap::new(LoggingByLengthLimiter::new(PINNING_CACHE_SIZE)) } + } + + /// Increase reference count of an item. + /// Create an entry with empty value in the cache if necessary. + pub fn pin(&mut self, hash: Block::Hash) { + match self.cache.get_or_insert(hash, Default::default) { + Some(entry) => { + entry.increase_ref(); + log::trace!( + target: LOG_TARGET, + "Bumped cache refcount. hash = {}, num_entries = {}", + hash, + self.cache.len() + ); + }, + None => + log::warn!(target: LOG_TARGET, "Unable to bump reference count. hash = {}", hash), + }; + } + + /// Clear the cache + pub fn clear(&mut self) { + self.cache.clear(); + } + + /// Check if item is contained in the cache + pub fn contains(&self, hash: Block::Hash) -> bool { + self.cache.peek(&hash).is_some() + } + + /// Attach body to an existing cache item + pub fn insert_body(&mut self, hash: Block::Hash, extrinsics: Option>) { + match self.cache.peek_mut(&hash) { + Some(mut entry) => { + entry.body = Some(extrinsics); + log::trace!( + target: LOG_TARGET, + "Cached body. hash = {}, num_entries = {}", + hash, + self.cache.len() + ); + }, + None => log::warn!( + target: LOG_TARGET, + "Unable to insert body for uncached item. hash = {}", + hash + ), + } + } + + /// Attach justification to an existing cache item + pub fn insert_justifications( + &mut self, + hash: Block::Hash, + justifications: Option, + ) { + match self.cache.peek_mut(&hash) { + Some(mut entry) => { + entry.justifications = Some(justifications); + log::trace!( + target: LOG_TARGET, + "Cached justification. hash = {}, num_entries = {}", + hash, + self.cache.len() + ); + }, + None => log::warn!( + target: LOG_TARGET, + "Unable to insert justifications for uncached item. hash = {}", + hash + ), + } + } + + /// Decreases reference count of an item. + /// If the count hits 0, the item is removed. + pub fn unpin(&mut self, hash: Block::Hash) { + if let Some(entry) = self.cache.peek_mut(&hash) { + entry.decrease_ref(); + if entry.has_no_references() { + self.cache.remove(&hash); + } + } + } + + /// Get justifications for cached block + pub fn justifications(&self, hash: &Block::Hash) -> Option<&Option> { + self.cache.peek(hash).and_then(|entry| entry.justifications.as_ref()) + } + + /// Get body for cached block + pub fn body(&self, hash: &Block::Hash) -> Option<&Option>> { + self.cache.peek(hash).and_then(|entry| entry.body.as_ref()) + } +} diff --git a/client/finality-grandpa/src/until_imported.rs b/client/finality-grandpa/src/until_imported.rs index 776411f8f..3bca77ae8 100644 --- a/client/finality-grandpa/src/until_imported.rs +++ b/client/finality-grandpa/src/until_imported.rs @@ -593,16 +593,17 @@ mod tests { fn import_header(&self, header: Header) { let hash = header.hash(); let number = *header.number(); - + let (tx, _rx) = tracing_unbounded("unpin-worker-channel", 10_000); self.known_blocks.lock().insert(hash, number); self.sender - .unbounded_send(BlockImportNotification { + .unbounded_send(BlockImportNotification::::new( hash, - origin: BlockOrigin::File, + BlockOrigin::File, header, - is_new_best: false, - tree_route: None, - }) + false, + None, + tx, + )) .unwrap(); } } diff --git a/client/service/src/builder.rs b/client/service/src/builder.rs index 1f94f96fa..0b09f550c 100644 --- a/client/service/src/builder.rs +++ b/client/service/src/builder.rs @@ -329,13 +329,15 @@ where let executor = crate::client::LocalCallExecutor::new( backend.clone(), executor, - spawn_handle, + spawn_handle.clone(), config.clone(), execution_extensions, )?; + crate::client::Client::new( backend, executor, + spawn_handle, genesis_block_builder, fork_blocks, bad_blocks, diff --git a/client/service/src/client/client.rs b/client/service/src/client/client.rs index 8e10a7b2e..d32baa671 100644 --- a/client/service/src/client/client.rs +++ b/client/service/src/client/client.rs @@ -22,7 +22,8 @@ use super::{ block_rules::{BlockRules, LookupResult as BlockLookupResult}, genesis::BuildGenesisBlock, }; -use log::{info, trace, warn}; +use futures::{FutureExt, StreamExt}; +use log::{error, info, trace, warn}; use parking_lot::{Mutex, RwLock}; use prometheus_endpoint::Registry; use rand::Rng; @@ -58,9 +59,12 @@ use sp_blockchain::{ use sp_consensus::{BlockOrigin, BlockStatus, Error as ConsensusError}; use sc_utils::mpsc::{tracing_unbounded, TracingUnboundedSender}; -use sp_core::storage::{ - well_known_keys, ChildInfo, ChildType, PrefixedStorageKey, Storage, StorageChild, StorageData, - StorageKey, +use sp_core::{ + storage::{ + well_known_keys, ChildInfo, ChildType, PrefixedStorageKey, Storage, StorageChild, + StorageData, StorageKey, + }, + traits::SpawnNamed, }; #[cfg(feature = "test-helpers")] use sp_keystore::SyncCryptoStorePtr; @@ -88,9 +92,7 @@ use std::{ #[cfg(feature = "test-helpers")] use { - super::call_executor::LocalCallExecutor, - sc_client_api::in_mem, - sp_core::traits::{CodeExecutor, SpawnNamed}, + super::call_executor::LocalCallExecutor, sc_client_api::in_mem, sp_core::traits::CodeExecutor, }; type NotificationSinks = Mutex>>; @@ -116,6 +118,7 @@ where block_rules: BlockRules, config: ClientConfig, telemetry: Option, + unpin_worker_sender: TracingUnboundedSender, _phantom: PhantomData, } @@ -246,7 +249,7 @@ where let call_executor = LocalCallExecutor::new( backend.clone(), executor, - spawn_handle, + spawn_handle.clone(), config.clone(), extensions, )?; @@ -254,6 +257,7 @@ where Client::new( backend, call_executor, + spawn_handle, genesis_block_builder, Default::default(), Default::default(), @@ -296,11 +300,20 @@ where let ClientImportOperation { mut op, notify_imported, notify_finalized } = op; - let finality_notification = notify_finalized.map(|summary| summary.into()); + let finality_notification = notify_finalized.map(|summary| { + FinalityNotification::from_summary(summary, self.unpin_worker_sender.clone()) + }); + let (import_notification, storage_changes) = match notify_imported { Some(mut summary) => { let storage_changes = summary.storage_changes.take(); - (Some(summary.into()), storage_changes) + ( + Some(BlockImportNotification::from_summary( + summary, + self.unpin_worker_sender.clone(), + )), + storage_changes, + ) }, None => (None, None), }; @@ -318,6 +331,27 @@ where self.backend.commit_operation(op)?; + // We need to pin the block in the backend once + // for each notification. Once all notifications are + // dropped, the block will be unpinned automatically. + if let Some(ref notification) = finality_notification { + if let Err(err) = self.backend.pin_block(notification.hash) { + error!( + "Unable to pin block for finality notification. hash: {}, Error: {}", + notification.hash, err + ); + }; + } + + if let Some(ref notification) = import_notification { + if let Err(err) = self.backend.pin_block(notification.hash) { + error!( + "Unable to pin block for import notification. hash: {}, Error: {}", + notification.hash, err + ); + }; + } + self.notify_finalized(finality_notification)?; self.notify_imported(import_notification, storage_changes)?; @@ -357,6 +391,7 @@ where pub fn new( backend: Arc, executor: E, + spawn_handle: Box, genesis_block_builder: G, fork_blocks: ForkBlocks, bad_blocks: BadBlocks, @@ -369,6 +404,7 @@ where Block, BlockImportOperation = >::BlockImportOperation, >, + B: 'static, { let info = backend.blockchain().info(); if info.finalized_state.is_none() { @@ -390,6 +426,26 @@ where backend.commit_operation(op)?; } + let (unpin_worker_sender, mut rx) = + tracing_unbounded::("unpin-worker-channel", 10_000); + let task_backend = Arc::downgrade(&backend); + spawn_handle.spawn( + "unpin-worker", + None, + async move { + while let Some(message) = rx.next().await { + if let Some(backend) = task_backend.upgrade() { + backend.unpin_block(message); + } else { + log::debug!("Terminating unpin-worker, backend reference was dropped."); + return + } + } + log::debug!("Terminating unpin-worker, stream terminated.") + } + .boxed(), + ); + Ok(Client { backend, executor, @@ -402,6 +458,7 @@ where block_rules: BlockRules::new(fork_blocks, bad_blocks), config, telemetry, + unpin_worker_sender, _phantom: Default::default(), }) } diff --git a/test-utils/client/src/lib.rs b/test-utils/client/src/lib.rs index 5dc93da13..ff744b80c 100644 --- a/test-utils/client/src/lib.rs +++ b/test-utils/client/src/lib.rs @@ -41,7 +41,7 @@ use futures::{future::Future, stream::StreamExt}; use sc_client_api::BlockchainEvents; use sc_service::client::{ClientConfig, LocalCallExecutor}; use serde::Deserialize; -use sp_core::storage::ChildInfo; +use sp_core::{storage::ChildInfo, testing::TaskExecutor}; use sp_runtime::{codec::Encode, traits::Block as BlockT, OpaqueExtrinsic}; use std::{ collections::{HashMap, HashSet}, @@ -62,7 +62,7 @@ impl GenesisInit for () { } /// A builder for creating a test client instance. -pub struct TestClientBuilder { +pub struct TestClientBuilder { execution_strategies: ExecutionStrategies, genesis_init: G, /// The key is an unprefixed storage key, this only contains @@ -237,9 +237,12 @@ impl ) .expect("Creates genesis block builder"); + let spawn_handle = Box::new(TaskExecutor::new()); + let client = client::Client::new( self.backend.clone(), executor, + spawn_handle, genesis_block_builder, self.fork_blocks, self.bad_blocks, From 0b6aec52a90870c999856cd37f7d04789cdd8dfc Mon Sep 17 00:00:00 2001 From: Gavin Wood Date: Thu, 19 Jan 2023 13:47:47 -0300 Subject: [PATCH 028/558] Make DispatchError impl MEL (#13169) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Make DispatchError impl MEL * Upgrade SCALE codec to support `codec(skip)` for MEL Co-authored-by: Bastian Köcher --- Cargo.lock | 12 ++++++------ bin/node-template/pallets/template/Cargo.toml | 2 +- bin/node-template/runtime/Cargo.toml | 2 +- bin/node/cli/Cargo.toml | 2 +- bin/node/executor/Cargo.toml | 2 +- bin/node/inspect/Cargo.toml | 2 +- bin/node/primitives/Cargo.toml | 2 +- bin/node/runtime/Cargo.toml | 2 +- bin/node/testing/Cargo.toml | 2 +- client/api/Cargo.toml | 2 +- client/authority-discovery/Cargo.toml | 2 +- client/basic-authorship/Cargo.toml | 2 +- client/beefy/Cargo.toml | 2 +- client/beefy/rpc/Cargo.toml | 2 +- client/block-builder/Cargo.toml | 2 +- client/cli/Cargo.toml | 2 +- client/consensus/aura/Cargo.toml | 2 +- client/consensus/babe/Cargo.toml | 2 +- client/consensus/epochs/Cargo.toml | 2 +- client/consensus/manual-seal/Cargo.toml | 2 +- client/consensus/pow/Cargo.toml | 2 +- client/consensus/slots/Cargo.toml | 2 +- client/db/Cargo.toml | 2 +- client/executor/Cargo.toml | 2 +- client/executor/wasmtime/Cargo.toml | 2 +- client/finality-grandpa/Cargo.toml | 2 +- client/finality-grandpa/rpc/Cargo.toml | 2 +- client/merkle-mountain-range/Cargo.toml | 2 +- client/merkle-mountain-range/rpc/Cargo.toml | 2 +- client/network/Cargo.toml | 2 +- client/network/common/Cargo.toml | 2 +- client/network/light/Cargo.toml | 2 +- client/network/sync/Cargo.toml | 2 +- client/network/transactions/Cargo.toml | 2 +- client/offchain/Cargo.toml | 2 +- client/rpc-api/Cargo.toml | 2 +- client/rpc-spec-v2/Cargo.toml | 2 +- client/rpc/Cargo.toml | 2 +- client/service/Cargo.toml | 2 +- client/service/test/Cargo.toml | 2 +- client/state-db/Cargo.toml | 2 +- client/sync-state-rpc/Cargo.toml | 2 +- client/transaction-pool/Cargo.toml | 2 +- frame/alliance/Cargo.toml | 2 +- frame/assets/Cargo.toml | 2 +- frame/atomic-swap/Cargo.toml | 2 +- frame/aura/Cargo.toml | 2 +- frame/authority-discovery/Cargo.toml | 2 +- frame/authorship/Cargo.toml | 2 +- frame/babe/Cargo.toml | 2 +- frame/bags-list/Cargo.toml | 2 +- frame/balances/Cargo.toml | 2 +- frame/beefy-mmr/Cargo.toml | 2 +- frame/beefy/Cargo.toml | 2 +- frame/benchmarking/Cargo.toml | 2 +- frame/bounties/Cargo.toml | 2 +- frame/child-bounties/Cargo.toml | 2 +- frame/collective/Cargo.toml | 2 +- frame/contracts/Cargo.toml | 2 +- frame/contracts/primitives/Cargo.toml | 2 +- frame/conviction-voting/Cargo.toml | 2 +- frame/democracy/Cargo.toml | 2 +- frame/election-provider-multi-phase/Cargo.toml | 2 +- frame/election-provider-support/Cargo.toml | 2 +- .../benchmarking/Cargo.toml | 2 +- .../solution-type/Cargo.toml | 2 +- .../solution-type/fuzzer/Cargo.toml | 2 +- frame/elections-phragmen/Cargo.toml | 2 +- frame/examples/basic/Cargo.toml | 2 +- frame/examples/offchain-worker/Cargo.toml | 2 +- frame/executive/Cargo.toml | 2 +- frame/fast-unstake/Cargo.toml | 2 +- frame/grandpa/Cargo.toml | 2 +- frame/identity/Cargo.toml | 2 +- frame/im-online/Cargo.toml | 2 +- frame/indices/Cargo.toml | 2 +- frame/lottery/Cargo.toml | 2 +- frame/membership/Cargo.toml | 2 +- frame/merkle-mountain-range/Cargo.toml | 2 +- frame/message-queue/Cargo.toml | 2 +- frame/multisig/Cargo.toml | 2 +- frame/nfts/Cargo.toml | 2 +- frame/nicks/Cargo.toml | 2 +- frame/nis/Cargo.toml | 2 +- frame/node-authorization/Cargo.toml | 2 +- frame/nomination-pools/Cargo.toml | 2 +- frame/nomination-pools/benchmarking/Cargo.toml | 2 +- frame/nomination-pools/runtime-api/Cargo.toml | 2 +- frame/nomination-pools/test-staking/Cargo.toml | 2 +- frame/offences/Cargo.toml | 2 +- frame/offences/benchmarking/Cargo.toml | 2 +- frame/preimage/Cargo.toml | 2 +- frame/proxy/Cargo.toml | 2 +- frame/randomness-collective-flip/Cargo.toml | 2 +- frame/ranked-collective/Cargo.toml | 2 +- frame/recovery/Cargo.toml | 2 +- frame/referenda/Cargo.toml | 2 +- frame/remark/Cargo.toml | 2 +- frame/root-offences/Cargo.toml | 2 +- frame/root-testing/Cargo.toml | 2 +- frame/scheduler/Cargo.toml | 2 +- frame/scored-pool/Cargo.toml | 2 +- frame/session/Cargo.toml | 2 +- frame/session/benchmarking/Cargo.toml | 2 +- frame/society/Cargo.toml | 2 +- frame/staking/Cargo.toml | 2 +- frame/state-trie-migration/Cargo.toml | 2 +- frame/sudo/Cargo.toml | 2 +- frame/support/Cargo.toml | 2 +- frame/support/test/Cargo.toml | 2 +- frame/support/test/compile_pass/Cargo.toml | 2 +- frame/support/test/pallet/Cargo.toml | 2 +- frame/system/Cargo.toml | 2 +- frame/system/benchmarking/Cargo.toml | 2 +- frame/system/rpc/runtime-api/Cargo.toml | 2 +- frame/timestamp/Cargo.toml | 2 +- frame/tips/Cargo.toml | 2 +- frame/transaction-payment/Cargo.toml | 2 +- .../transaction-payment/asset-tx-payment/Cargo.toml | 2 +- frame/transaction-payment/rpc/Cargo.toml | 2 +- frame/transaction-payment/rpc/runtime-api/Cargo.toml | 2 +- frame/transaction-storage/Cargo.toml | 2 +- frame/treasury/Cargo.toml | 2 +- frame/try-runtime/Cargo.toml | 2 +- frame/uniques/Cargo.toml | 2 +- frame/utility/Cargo.toml | 2 +- frame/vesting/Cargo.toml | 2 +- frame/whitelist/Cargo.toml | 2 +- primitives/api/Cargo.toml | 2 +- primitives/api/test/Cargo.toml | 2 +- primitives/application-crypto/Cargo.toml | 2 +- primitives/arithmetic/Cargo.toml | 2 +- primitives/arithmetic/src/lib.rs | 4 ++-- primitives/authority-discovery/Cargo.toml | 2 +- primitives/authorship/Cargo.toml | 2 +- primitives/beefy/Cargo.toml | 2 +- primitives/block-builder/Cargo.toml | 2 +- primitives/blockchain/Cargo.toml | 2 +- primitives/consensus/aura/Cargo.toml | 2 +- primitives/consensus/babe/Cargo.toml | 2 +- primitives/consensus/common/Cargo.toml | 2 +- primitives/consensus/pow/Cargo.toml | 2 +- primitives/consensus/slots/Cargo.toml | 2 +- primitives/consensus/vrf/Cargo.toml | 2 +- primitives/core/Cargo.toml | 2 +- primitives/externalities/Cargo.toml | 2 +- primitives/finality-grandpa/Cargo.toml | 2 +- primitives/inherents/Cargo.toml | 2 +- primitives/io/Cargo.toml | 2 +- primitives/keystore/Cargo.toml | 2 +- primitives/merkle-mountain-range/Cargo.toml | 2 +- primitives/npos-elections/Cargo.toml | 2 +- primitives/npos-elections/fuzzer/Cargo.toml | 2 +- primitives/runtime-interface/Cargo.toml | 2 +- primitives/runtime/Cargo.toml | 2 +- primitives/runtime/src/lib.rs | 8 ++++---- primitives/session/Cargo.toml | 2 +- primitives/staking/Cargo.toml | 2 +- primitives/state-machine/Cargo.toml | 2 +- primitives/storage/Cargo.toml | 2 +- primitives/test-primitives/Cargo.toml | 2 +- primitives/timestamp/Cargo.toml | 2 +- primitives/tracing/Cargo.toml | 2 +- primitives/transaction-storage-proof/Cargo.toml | 2 +- primitives/trie/Cargo.toml | 2 +- primitives/version/Cargo.toml | 2 +- primitives/version/proc-macro/Cargo.toml | 2 +- primitives/wasm-interface/Cargo.toml | 2 +- primitives/weights/Cargo.toml | 2 +- test-utils/client/Cargo.toml | 2 +- test-utils/runtime/Cargo.toml | 2 +- test-utils/runtime/client/Cargo.toml | 2 +- test-utils/runtime/transaction-pool/Cargo.toml | 2 +- utils/fork-tree/Cargo.toml | 2 +- utils/frame/benchmarking-cli/Cargo.toml | 2 +- utils/frame/remote-externalities/Cargo.toml | 2 +- utils/frame/rpc/state-trie-migration-rpc/Cargo.toml | 2 +- utils/frame/rpc/support/Cargo.toml | 2 +- utils/frame/rpc/system/Cargo.toml | 2 +- utils/frame/try-runtime/cli/Cargo.toml | 2 +- 180 files changed, 189 insertions(+), 189 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 100e0d215..dbb046ccd 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -6679,9 +6679,9 @@ dependencies = [ [[package]] name = "parity-scale-codec" -version = "3.2.1" +version = "3.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "366e44391a8af4cfd6002ef6ba072bae071a96aafca98d7d448a34c5dca38b6a" +checksum = "e7ab01d0f889e957861bc65888d5ccbe82c158d0270136ba46820d43837cdf72" dependencies = [ "arrayvec 0.7.2", "bitvec", @@ -6694,9 +6694,9 @@ dependencies = [ [[package]] name = "parity-scale-codec-derive" -version = "3.1.3" +version = "3.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9299338969a3d2f491d65f140b00ddec470858402f888af98e8642fb5e8965cd" +checksum = "86b26a931f824dd4eca30b3e43bb4f31cd5f0d3a403c5f5ff27106b805bfde7b" dependencies = [ "proc-macro-crate", "proc-macro2", @@ -7128,9 +7128,9 @@ dependencies = [ [[package]] name = "proc-macro2" -version = "1.0.49" +version = "1.0.50" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "57a8eca9f9c4ffde41714334dee777596264c7825420f521abc92b5b5deb63a5" +checksum = "6ef7d57beacfaf2d8aee5937dab7b7f28de3cb8b1828479bb5de2a7106f2bae2" dependencies = [ "unicode-ident", ] diff --git a/bin/node-template/pallets/template/Cargo.toml b/bin/node-template/pallets/template/Cargo.toml index 7c04838ca..6dec5c8da 100644 --- a/bin/node-template/pallets/template/Cargo.toml +++ b/bin/node-template/pallets/template/Cargo.toml @@ -13,7 +13,7 @@ repository = "https://github.com/substrate-developer-hub/substrate-node-template targets = ["x86_64-unknown-linux-gnu"] [dependencies] -codec = { package = "parity-scale-codec", version = "3.0.0", default-features = false, features = [ +codec = { package = "parity-scale-codec", version = "3.2.2", default-features = false, features = [ "derive", ] } scale-info = { version = "2.1.1", default-features = false, features = ["derive"] } diff --git a/bin/node-template/runtime/Cargo.toml b/bin/node-template/runtime/Cargo.toml index 5dc7b03d3..5560eb672 100644 --- a/bin/node-template/runtime/Cargo.toml +++ b/bin/node-template/runtime/Cargo.toml @@ -13,7 +13,7 @@ repository = "https://github.com/substrate-developer-hub/substrate-node-template targets = ["x86_64-unknown-linux-gnu"] [dependencies] -codec = { package = "parity-scale-codec", version = "3.0.0", default-features = false, features = ["derive"] } +codec = { package = "parity-scale-codec", version = "3.2.2", default-features = false, features = ["derive"] } scale-info = { version = "2.1.1", default-features = false, features = ["derive"] } pallet-aura = { version = "4.0.0-dev", default-features = false, path = "../../../frame/aura" } diff --git a/bin/node/cli/Cargo.toml b/bin/node/cli/Cargo.toml index 6b50115fd..4d9279b85 100644 --- a/bin/node/cli/Cargo.toml +++ b/bin/node/cli/Cargo.toml @@ -37,7 +37,7 @@ crate-type = ["cdylib", "rlib"] # third-party dependencies array-bytes = "4.1" clap = { version = "4.0.9", features = ["derive"], optional = true } -codec = { package = "parity-scale-codec", version = "3.0.0" } +codec = { package = "parity-scale-codec", version = "3.2.2" } serde = { version = "1.0.136", features = ["derive"] } jsonrpsee = { version = "0.16.2", features = ["server"] } futures = "0.3.21" diff --git a/bin/node/executor/Cargo.toml b/bin/node/executor/Cargo.toml index 8b3add78a..edcc4df9c 100644 --- a/bin/node/executor/Cargo.toml +++ b/bin/node/executor/Cargo.toml @@ -13,7 +13,7 @@ publish = false targets = ["x86_64-unknown-linux-gnu"] [dependencies] -codec = { package = "parity-scale-codec", version = "3.0.0" } +codec = { package = "parity-scale-codec", version = "3.2.2" } scale-info = { version = "2.1.1", features = ["derive"] } frame-benchmarking = { version = "4.0.0-dev", path = "../../../frame/benchmarking" } node-primitives = { version = "2.0.0", path = "../primitives" } diff --git a/bin/node/inspect/Cargo.toml b/bin/node/inspect/Cargo.toml index a7343b3ca..d6a875517 100644 --- a/bin/node/inspect/Cargo.toml +++ b/bin/node/inspect/Cargo.toml @@ -14,7 +14,7 @@ targets = ["x86_64-unknown-linux-gnu"] [dependencies] clap = { version = "4.0.9", features = ["derive"] } -codec = { package = "parity-scale-codec", version = "3.0.0" } +codec = { package = "parity-scale-codec", version = "3.2.2" } thiserror = "1.0" sc-cli = { version = "0.10.0-dev", path = "../../../client/cli" } sc-client-api = { version = "4.0.0-dev", path = "../../../client/api" } diff --git a/bin/node/primitives/Cargo.toml b/bin/node/primitives/Cargo.toml index 1aa0a8f0e..1f515f3e3 100644 --- a/bin/node/primitives/Cargo.toml +++ b/bin/node/primitives/Cargo.toml @@ -13,7 +13,7 @@ publish = false targets = ["x86_64-unknown-linux-gnu"] [dependencies] -codec = { package = "parity-scale-codec", version = "3.0.0", default-features = false, features = [ +codec = { package = "parity-scale-codec", version = "3.2.2", default-features = false, features = [ "derive", ] } scale-info = { version = "2.1.1", default-features = false, features = ["derive"] } diff --git a/bin/node/runtime/Cargo.toml b/bin/node/runtime/Cargo.toml index 7037be9d7..76fca730e 100644 --- a/bin/node/runtime/Cargo.toml +++ b/bin/node/runtime/Cargo.toml @@ -16,7 +16,7 @@ targets = ["x86_64-unknown-linux-gnu"] [dependencies] # third-party dependencies -codec = { package = "parity-scale-codec", version = "3.0.0", default-features = false, features = [ +codec = { package = "parity-scale-codec", version = "3.2.2", default-features = false, features = [ "derive", "max-encoded-len", ] } diff --git a/bin/node/testing/Cargo.toml b/bin/node/testing/Cargo.toml index 0154a7784..a43b2b9ba 100644 --- a/bin/node/testing/Cargo.toml +++ b/bin/node/testing/Cargo.toml @@ -13,7 +13,7 @@ publish = false targets = ["x86_64-unknown-linux-gnu"] [dependencies] -codec = { package = "parity-scale-codec", version = "3.0.0" } +codec = { package = "parity-scale-codec", version = "3.2.2" } fs_extra = "1" futures = "0.3.21" log = "0.4.17" diff --git a/client/api/Cargo.toml b/client/api/Cargo.toml index bd1f6f34b..f49420085 100644 --- a/client/api/Cargo.toml +++ b/client/api/Cargo.toml @@ -14,7 +14,7 @@ readme = "README.md" targets = ["x86_64-unknown-linux-gnu"] [dependencies] -codec = { package = "parity-scale-codec", version = "3.0.0", default-features = false, features = [ +codec = { package = "parity-scale-codec", version = "3.2.2", default-features = false, features = [ "derive", ] } fnv = "1.0.6" diff --git a/client/authority-discovery/Cargo.toml b/client/authority-discovery/Cargo.toml index a24002517..b37507662 100644 --- a/client/authority-discovery/Cargo.toml +++ b/client/authority-discovery/Cargo.toml @@ -17,7 +17,7 @@ targets = ["x86_64-unknown-linux-gnu"] prost-build = "0.11" [dependencies] -codec = { package = "parity-scale-codec", version = "3.0.0", default-features = false } +codec = { package = "parity-scale-codec", version = "3.2.2", default-features = false } futures = "0.3.21" futures-timer = "3.0.1" ip_network = "0.4.1" diff --git a/client/basic-authorship/Cargo.toml b/client/basic-authorship/Cargo.toml index 09b5c4739..c4f1d2e24 100644 --- a/client/basic-authorship/Cargo.toml +++ b/client/basic-authorship/Cargo.toml @@ -13,7 +13,7 @@ readme = "README.md" targets = ["x86_64-unknown-linux-gnu"] [dependencies] -codec = { package = "parity-scale-codec", version = "3.0.0" } +codec = { package = "parity-scale-codec", version = "3.2.2" } futures = "0.3.21" futures-timer = "3.0.1" log = "0.4.17" diff --git a/client/beefy/Cargo.toml b/client/beefy/Cargo.toml index b039faa2d..d81ff2587 100644 --- a/client/beefy/Cargo.toml +++ b/client/beefy/Cargo.toml @@ -11,7 +11,7 @@ homepage = "https://substrate.io" [dependencies] array-bytes = "4.1" async-trait = "0.1.57" -codec = { package = "parity-scale-codec", version = "3.0.0", features = ["derive"] } +codec = { package = "parity-scale-codec", version = "3.2.2", features = ["derive"] } fnv = "1.0.6" futures = "0.3" log = "0.4" diff --git a/client/beefy/rpc/Cargo.toml b/client/beefy/rpc/Cargo.toml index f853af8a8..ab3e6921f 100644 --- a/client/beefy/rpc/Cargo.toml +++ b/client/beefy/rpc/Cargo.toml @@ -9,7 +9,7 @@ description = "RPC for the BEEFY Client gadget for substrate" homepage = "https://substrate.io" [dependencies] -codec = { package = "parity-scale-codec", version = "3.0.0", features = ["derive"] } +codec = { package = "parity-scale-codec", version = "3.2.2", features = ["derive"] } futures = "0.3.21" jsonrpsee = { version = "0.16.2", features = ["client-core", "server", "macros"] } log = "0.4" diff --git a/client/block-builder/Cargo.toml b/client/block-builder/Cargo.toml index 251637486..3b4bea981 100644 --- a/client/block-builder/Cargo.toml +++ b/client/block-builder/Cargo.toml @@ -13,7 +13,7 @@ readme = "README.md" targets = ["x86_64-unknown-linux-gnu"] [dependencies] -codec = { package = "parity-scale-codec", version = "3.0.0", features = [ +codec = { package = "parity-scale-codec", version = "3.2.2", features = [ "derive", ] } sc-client-api = { version = "4.0.0-dev", path = "../api" } diff --git a/client/cli/Cargo.toml b/client/cli/Cargo.toml index 5e1f2bab4..c0da53ad1 100644 --- a/client/cli/Cargo.toml +++ b/client/cli/Cargo.toml @@ -21,7 +21,7 @@ futures = "0.3.21" libp2p = "0.50.0" log = "0.4.17" names = { version = "0.13.0", default-features = false } -parity-scale-codec = "3.0.0" +parity-scale-codec = "3.2.2" rand = "0.8.5" regex = "1.6.0" rpassword = "7.0.0" diff --git a/client/consensus/aura/Cargo.toml b/client/consensus/aura/Cargo.toml index 47aee0ec0..4c0305e9f 100644 --- a/client/consensus/aura/Cargo.toml +++ b/client/consensus/aura/Cargo.toml @@ -14,7 +14,7 @@ targets = ["x86_64-unknown-linux-gnu"] [dependencies] async-trait = "0.1.57" -codec = { package = "parity-scale-codec", version = "3.0.0" } +codec = { package = "parity-scale-codec", version = "3.2.2" } futures = "0.3.21" log = "0.4.17" thiserror = "1.0" diff --git a/client/consensus/babe/Cargo.toml b/client/consensus/babe/Cargo.toml index 312cc25dd..f0ff0080f 100644 --- a/client/consensus/babe/Cargo.toml +++ b/client/consensus/babe/Cargo.toml @@ -15,7 +15,7 @@ targets = ["x86_64-unknown-linux-gnu"] [dependencies] async-trait = "0.1.57" -codec = { package = "parity-scale-codec", version = "3.0.0", features = ["derive"] } +codec = { package = "parity-scale-codec", version = "3.2.2", features = ["derive"] } futures = "0.3.21" log = "0.4.17" merlin = "2.0" diff --git a/client/consensus/epochs/Cargo.toml b/client/consensus/epochs/Cargo.toml index c88b5c52b..89588cc7d 100644 --- a/client/consensus/epochs/Cargo.toml +++ b/client/consensus/epochs/Cargo.toml @@ -13,7 +13,7 @@ readme = "README.md" targets = ["x86_64-unknown-linux-gnu"] [dependencies] -codec = { package = "parity-scale-codec", version = "3.0.0", features = ["derive"] } +codec = { package = "parity-scale-codec", version = "3.2.2", features = ["derive"] } fork-tree = { version = "3.0.0", path = "../../../utils/fork-tree" } sc-client-api = { version = "4.0.0-dev", path = "../../api" } sc-consensus = { version = "0.10.0-dev", path = "../common" } diff --git a/client/consensus/manual-seal/Cargo.toml b/client/consensus/manual-seal/Cargo.toml index fb89445a9..19c4b2224 100644 --- a/client/consensus/manual-seal/Cargo.toml +++ b/client/consensus/manual-seal/Cargo.toml @@ -16,7 +16,7 @@ targets = ["x86_64-unknown-linux-gnu"] jsonrpsee = { version = "0.16.2", features = ["client-core", "server", "macros"] } assert_matches = "1.3.0" async-trait = "0.1.57" -codec = { package = "parity-scale-codec", version = "3.0.0" } +codec = { package = "parity-scale-codec", version = "3.2.2" } futures = "0.3.21" log = "0.4.17" serde = { version = "1.0", features = ["derive"] } diff --git a/client/consensus/pow/Cargo.toml b/client/consensus/pow/Cargo.toml index 480d9b23b..b5454e35f 100644 --- a/client/consensus/pow/Cargo.toml +++ b/client/consensus/pow/Cargo.toml @@ -14,7 +14,7 @@ targets = ["x86_64-unknown-linux-gnu"] [dependencies] async-trait = "0.1.57" -codec = { package = "parity-scale-codec", version = "3.0.0", features = ["derive"] } +codec = { package = "parity-scale-codec", version = "3.2.2", features = ["derive"] } futures = "0.3.21" futures-timer = "3.0.1" log = "0.4.17" diff --git a/client/consensus/slots/Cargo.toml b/client/consensus/slots/Cargo.toml index 6acd656d2..5cacf4f47 100644 --- a/client/consensus/slots/Cargo.toml +++ b/client/consensus/slots/Cargo.toml @@ -15,7 +15,7 @@ targets = ["x86_64-unknown-linux-gnu"] [dependencies] async-trait = "0.1.57" -codec = { package = "parity-scale-codec", version = "3.0.0" } +codec = { package = "parity-scale-codec", version = "3.2.2" } futures = "0.3.21" futures-timer = "3.0.1" log = "0.4.17" diff --git a/client/db/Cargo.toml b/client/db/Cargo.toml index 2d7291a01..028af5d6e 100644 --- a/client/db/Cargo.toml +++ b/client/db/Cargo.toml @@ -13,7 +13,7 @@ readme = "README.md" targets = ["x86_64-unknown-linux-gnu"] [dependencies] -codec = { package = "parity-scale-codec", version = "3.0.0", features = [ +codec = { package = "parity-scale-codec", version = "3.2.2", features = [ "derive", ] } hash-db = "0.15.2" diff --git a/client/executor/Cargo.toml b/client/executor/Cargo.toml index 83d4801d1..4604b976b 100644 --- a/client/executor/Cargo.toml +++ b/client/executor/Cargo.toml @@ -19,7 +19,7 @@ parking_lot = "0.12.1" tracing = "0.1.29" wasmi = "0.13" -codec = { package = "parity-scale-codec", version = "3.0.0" } +codec = { package = "parity-scale-codec", version = "3.2.2" } sc-executor-common = { version = "0.10.0-dev", path = "common" } sc-executor-wasmi = { version = "0.10.0-dev", path = "wasmi" } sc-executor-wasmtime = { version = "0.10.0-dev", path = "wasmtime" } diff --git a/client/executor/wasmtime/Cargo.toml b/client/executor/wasmtime/Cargo.toml index 7e38929f0..983ca3c44 100644 --- a/client/executor/wasmtime/Cargo.toml +++ b/client/executor/wasmtime/Cargo.toml @@ -47,4 +47,4 @@ sc-runtime-test = { version = "2.0.0", path = "../runtime-test" } sp-io = { version = "7.0.0", path = "../../../primitives/io" } tempfile = "3.3.0" paste = "1.0" -codec = { package = "parity-scale-codec", version = "3.0.0" } +codec = { package = "parity-scale-codec", version = "3.2.2" } diff --git a/client/finality-grandpa/Cargo.toml b/client/finality-grandpa/Cargo.toml index 95195bfc2..9a31a1d4b 100644 --- a/client/finality-grandpa/Cargo.toml +++ b/client/finality-grandpa/Cargo.toml @@ -22,7 +22,7 @@ finality-grandpa = { version = "0.16.1", features = ["derive-codec"] } futures = "0.3.21" futures-timer = "3.0.1" log = "0.4.17" -parity-scale-codec = { version = "3.0.0", features = ["derive"] } +parity-scale-codec = { version = "3.2.2", features = ["derive"] } parking_lot = "0.12.1" rand = "0.8.5" serde_json = "1.0.85" diff --git a/client/finality-grandpa/rpc/Cargo.toml b/client/finality-grandpa/rpc/Cargo.toml index 429331e96..e20ff174d 100644 --- a/client/finality-grandpa/rpc/Cargo.toml +++ b/client/finality-grandpa/rpc/Cargo.toml @@ -14,7 +14,7 @@ finality-grandpa = { version = "0.16.1", features = ["derive-codec"] } futures = "0.3.16" jsonrpsee = { version = "0.16.2", features = ["client-core", "server", "macros"] } log = "0.4.8" -parity-scale-codec = { version = "3.0.0", features = ["derive"] } +parity-scale-codec = { version = "3.2.2", features = ["derive"] } serde = { version = "1.0.105", features = ["derive"] } thiserror = "1.0" sc-client-api = { version = "4.0.0-dev", path = "../../api" } diff --git a/client/merkle-mountain-range/Cargo.toml b/client/merkle-mountain-range/Cargo.toml index cd7d94424..90fb65453 100644 --- a/client/merkle-mountain-range/Cargo.toml +++ b/client/merkle-mountain-range/Cargo.toml @@ -11,7 +11,7 @@ homepage = "https://substrate.io" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] -codec = { package = "parity-scale-codec", version = "3.0.0" } +codec = { package = "parity-scale-codec", version = "3.2.2" } futures = "0.3" log = "0.4" beefy-primitives = { version = "4.0.0-dev", path = "../../primitives/beefy", package = "sp-beefy" } diff --git a/client/merkle-mountain-range/rpc/Cargo.toml b/client/merkle-mountain-range/rpc/Cargo.toml index dcc5e49c5..ce7115880 100644 --- a/client/merkle-mountain-range/rpc/Cargo.toml +++ b/client/merkle-mountain-range/rpc/Cargo.toml @@ -12,7 +12,7 @@ description = "Node-specific RPC methods for interaction with Merkle Mountain Ra targets = ["x86_64-unknown-linux-gnu"] [dependencies] -codec = { package = "parity-scale-codec", version = "3.0.0" } +codec = { package = "parity-scale-codec", version = "3.2.2" } jsonrpsee = { version = "0.16.2", features = ["client-core", "server", "macros"] } serde = { version = "1.0.136", features = ["derive"] } sp-api = { version = "4.0.0-dev", path = "../../../primitives/api" } diff --git a/client/network/Cargo.toml b/client/network/Cargo.toml index 4356cc5df..44a7973b3 100644 --- a/client/network/Cargo.toml +++ b/client/network/Cargo.toml @@ -19,7 +19,7 @@ async-trait = "0.1" asynchronous-codec = "0.6" backtrace = "0.3.67" bytes = "1" -codec = { package = "parity-scale-codec", version = "3.0.0", features = ["derive"] } +codec = { package = "parity-scale-codec", version = "3.2.2", features = ["derive"] } either = "1.5.3" fnv = "1.0.6" futures = "0.3.21" diff --git a/client/network/common/Cargo.toml b/client/network/common/Cargo.toml index e2dd7d089..2aaada021 100644 --- a/client/network/common/Cargo.toml +++ b/client/network/common/Cargo.toml @@ -19,7 +19,7 @@ prost-build = "0.11" async-trait = "0.1.57" bitflags = "1.3.2" bytes = "1" -codec = { package = "parity-scale-codec", version = "3.0.0", features = [ +codec = { package = "parity-scale-codec", version = "3.2.2", features = [ "derive", ] } futures = "0.3.21" diff --git a/client/network/light/Cargo.toml b/client/network/light/Cargo.toml index 8a3e90377..f3e94790d 100644 --- a/client/network/light/Cargo.toml +++ b/client/network/light/Cargo.toml @@ -17,7 +17,7 @@ prost-build = "0.11" [dependencies] array-bytes = "4.1" -codec = { package = "parity-scale-codec", version = "3.0.0", features = [ +codec = { package = "parity-scale-codec", version = "3.2.2", features = [ "derive", ] } futures = "0.3.21" diff --git a/client/network/sync/Cargo.toml b/client/network/sync/Cargo.toml index d2cbeb5ec..ff72b4e99 100644 --- a/client/network/sync/Cargo.toml +++ b/client/network/sync/Cargo.toml @@ -18,7 +18,7 @@ prost-build = "0.11" [dependencies] array-bytes = "4.1" async-trait = "0.1.58" -codec = { package = "parity-scale-codec", version = "3.0.0", features = ["derive"] } +codec = { package = "parity-scale-codec", version = "3.2.2", features = ["derive"] } futures = "0.3.21" libp2p = "0.50.0" log = "0.4.17" diff --git a/client/network/transactions/Cargo.toml b/client/network/transactions/Cargo.toml index df13cdcb8..cab702fbd 100644 --- a/client/network/transactions/Cargo.toml +++ b/client/network/transactions/Cargo.toml @@ -14,7 +14,7 @@ targets = ["x86_64-unknown-linux-gnu"] [dependencies] array-bytes = "4.1" -codec = { package = "parity-scale-codec", version = "3.0.0", features = ["derive"] } +codec = { package = "parity-scale-codec", version = "3.2.2", features = ["derive"] } futures = "0.3.21" libp2p = "0.50.0" log = "0.4.17" diff --git a/client/offchain/Cargo.toml b/client/offchain/Cargo.toml index 09df9187f..dd6e2e44c 100644 --- a/client/offchain/Cargo.toml +++ b/client/offchain/Cargo.toml @@ -15,7 +15,7 @@ targets = ["x86_64-unknown-linux-gnu"] [dependencies] array-bytes = "4.1" bytes = "1.1" -codec = { package = "parity-scale-codec", version = "3.0.0", features = ["derive"] } +codec = { package = "parity-scale-codec", version = "3.2.2", features = ["derive"] } fnv = "1.0.6" futures = "0.3.21" futures-timer = "3.0.2" diff --git a/client/rpc-api/Cargo.toml b/client/rpc-api/Cargo.toml index f697f53e9..261339f13 100644 --- a/client/rpc-api/Cargo.toml +++ b/client/rpc-api/Cargo.toml @@ -13,7 +13,7 @@ readme = "README.md" targets = ["x86_64-unknown-linux-gnu"] [dependencies] -codec = { package = "parity-scale-codec", version = "3.0.0" } +codec = { package = "parity-scale-codec", version = "3.2.2" } scale-info = { version = "2.1.1", default-features = false, features = ["derive"] } serde = { version = "1.0.136", features = ["derive"] } serde_json = "1.0.85" diff --git a/client/rpc-spec-v2/Cargo.toml b/client/rpc-spec-v2/Cargo.toml index f9598b3c1..43fb18908 100644 --- a/client/rpc-spec-v2/Cargo.toml +++ b/client/rpc-spec-v2/Cargo.toml @@ -24,7 +24,7 @@ sp-api = { version = "4.0.0-dev", path = "../../primitives/api" } sp-blockchain = { version = "4.0.0-dev", path = "../../primitives/blockchain" } sp-version = { version = "5.0.0", path = "../../primitives/version" } sc-client-api = { version = "4.0.0-dev", path = "../api" } -codec = { package = "parity-scale-codec", version = "3.0.0" } +codec = { package = "parity-scale-codec", version = "3.2.2" } thiserror = "1.0" serde = "1.0" hex = "0.4" diff --git a/client/rpc/Cargo.toml b/client/rpc/Cargo.toml index d39d8d858..a22f65787 100644 --- a/client/rpc/Cargo.toml +++ b/client/rpc/Cargo.toml @@ -13,7 +13,7 @@ readme = "README.md" targets = ["x86_64-unknown-linux-gnu"] [dependencies] -codec = { package = "parity-scale-codec", version = "3.0.0" } +codec = { package = "parity-scale-codec", version = "3.2.2" } futures = "0.3.21" jsonrpsee = { version = "0.16.2", features = ["server"] } log = "0.4.17" diff --git a/client/service/Cargo.toml b/client/service/Cargo.toml index 45b1c0262..6122895d2 100644 --- a/client/service/Cargo.toml +++ b/client/service/Cargo.toml @@ -57,7 +57,7 @@ sc-chain-spec = { version = "4.0.0-dev", path = "../chain-spec" } sc-client-api = { version = "4.0.0-dev", path = "../api" } sp-api = { version = "4.0.0-dev", path = "../../primitives/api" } sc-client-db = { version = "0.10.0-dev", default-features = false, path = "../db" } -codec = { package = "parity-scale-codec", version = "3.0.0" } +codec = { package = "parity-scale-codec", version = "3.2.2" } sc-executor = { version = "0.10.0-dev", path = "../executor" } sc-transaction-pool = { version = "4.0.0-dev", path = "../transaction-pool" } sp-transaction-pool = { version = "4.0.0-dev", path = "../../primitives/transaction-pool" } diff --git a/client/service/test/Cargo.toml b/client/service/test/Cargo.toml index d3bc39aa3..488fe76c3 100644 --- a/client/service/test/Cargo.toml +++ b/client/service/test/Cargo.toml @@ -16,7 +16,7 @@ array-bytes = "4.1" fdlimit = "0.2.1" futures = "0.3.21" log = "0.4.17" -parity-scale-codec = "3.0.0" +parity-scale-codec = "3.2.2" parking_lot = "0.12.1" tempfile = "3.1.0" tokio = { version = "1.22.0", features = ["time"] } diff --git a/client/state-db/Cargo.toml b/client/state-db/Cargo.toml index fb70f5fc6..13439de11 100644 --- a/client/state-db/Cargo.toml +++ b/client/state-db/Cargo.toml @@ -13,7 +13,7 @@ readme = "README.md" targets = ["x86_64-unknown-linux-gnu"] [dependencies] -codec = { package = "parity-scale-codec", version = "3.0.0", features = ["derive"] } +codec = { package = "parity-scale-codec", version = "3.2.2", features = ["derive"] } log = "0.4.17" parking_lot = "0.12.1" sp-core = { version = "7.0.0", path = "../../primitives/core" } diff --git a/client/sync-state-rpc/Cargo.toml b/client/sync-state-rpc/Cargo.toml index a72b4106b..3a964521c 100644 --- a/client/sync-state-rpc/Cargo.toml +++ b/client/sync-state-rpc/Cargo.toml @@ -12,7 +12,7 @@ repository = "https://github.com/paritytech/substrate/" targets = ["x86_64-unknown-linux-gnu"] [dependencies] -codec = { package = "parity-scale-codec", version = "3.0.0" } +codec = { package = "parity-scale-codec", version = "3.2.2" } jsonrpsee = { version = "0.16.2", features = ["client-core", "server", "macros"] } serde = { version = "1.0.136", features = ["derive"] } serde_json = "1.0.85" diff --git a/client/transaction-pool/Cargo.toml b/client/transaction-pool/Cargo.toml index 4b69b6d0f..bbb5ebae4 100644 --- a/client/transaction-pool/Cargo.toml +++ b/client/transaction-pool/Cargo.toml @@ -14,7 +14,7 @@ targets = ["x86_64-unknown-linux-gnu"] [dependencies] async-trait = "0.1.57" -codec = { package = "parity-scale-codec", version = "3.0.0" } +codec = { package = "parity-scale-codec", version = "3.2.2" } futures = "0.3.21" futures-timer = "3.0.2" linked-hash-map = "0.5.4" diff --git a/frame/alliance/Cargo.toml b/frame/alliance/Cargo.toml index da0a7d665..d6a16e2c3 100644 --- a/frame/alliance/Cargo.toml +++ b/frame/alliance/Cargo.toml @@ -17,7 +17,7 @@ array-bytes = { version = "4.1", optional = true } sha2 = { version = "0.10.1", default-features = false, optional = true } log = { version = "0.4.14", default-features = false } -codec = { package = "parity-scale-codec", version = "3.0.0", default-features = false, features = ["derive"] } +codec = { package = "parity-scale-codec", version = "3.2.2", default-features = false, features = ["derive"] } scale-info = { version = "2.0.1", default-features = false, features = ["derive"] } sp-std = { version = "5.0.0", default-features = false, path = "../../primitives/std" } diff --git a/frame/assets/Cargo.toml b/frame/assets/Cargo.toml index 84bfd9535..6fe0e3e6c 100644 --- a/frame/assets/Cargo.toml +++ b/frame/assets/Cargo.toml @@ -13,7 +13,7 @@ readme = "README.md" targets = ["x86_64-unknown-linux-gnu"] [dependencies] -codec = { package = "parity-scale-codec", version = "3.0.0", default-features = false } +codec = { package = "parity-scale-codec", version = "3.2.2", default-features = false } scale-info = { version = "2.1.1", default-features = false, features = ["derive"] } sp-std = { version = "5.0.0", default-features = false, path = "../../primitives/std" } # Needed for various traits. In our case, `OnFinalize`. diff --git a/frame/atomic-swap/Cargo.toml b/frame/atomic-swap/Cargo.toml index 5220edb9d..9c3bcf73a 100644 --- a/frame/atomic-swap/Cargo.toml +++ b/frame/atomic-swap/Cargo.toml @@ -13,7 +13,7 @@ readme = "README.md" targets = ["x86_64-unknown-linux-gnu"] [dependencies] -codec = { package = "parity-scale-codec", version = "3.0.0", default-features = false } +codec = { package = "parity-scale-codec", version = "3.2.2", default-features = false } scale-info = { version = "2.1.1", default-features = false, features = ["derive"] } frame-support = { version = "4.0.0-dev", default-features = false, path = "../support" } frame-system = { version = "4.0.0-dev", default-features = false, path = "../system" } diff --git a/frame/aura/Cargo.toml b/frame/aura/Cargo.toml index 552f13301..d2fd5a5b9 100644 --- a/frame/aura/Cargo.toml +++ b/frame/aura/Cargo.toml @@ -13,7 +13,7 @@ readme = "README.md" targets = ["x86_64-unknown-linux-gnu"] [dependencies] -codec = { package = "parity-scale-codec", version = "3.0.0", default-features = false, features = ["derive", "max-encoded-len"] } +codec = { package = "parity-scale-codec", version = "3.2.2", default-features = false, features = ["derive", "max-encoded-len"] } scale-info = { version = "2.1.1", default-features = false, features = ["derive"] } frame-support = { version = "4.0.0-dev", default-features = false, path = "../support" } frame-system = { version = "4.0.0-dev", default-features = false, path = "../system" } diff --git a/frame/authority-discovery/Cargo.toml b/frame/authority-discovery/Cargo.toml index 47bd1a126..50c6c411c 100644 --- a/frame/authority-discovery/Cargo.toml +++ b/frame/authority-discovery/Cargo.toml @@ -13,7 +13,7 @@ readme = "README.md" targets = ["x86_64-unknown-linux-gnu"] [dependencies] -codec = { package = "parity-scale-codec", version = "3.0.0", default-features = false, features = [ +codec = { package = "parity-scale-codec", version = "3.2.2", default-features = false, features = [ "derive", ] } scale-info = { version = "2.1.1", default-features = false, features = ["derive"] } diff --git a/frame/authorship/Cargo.toml b/frame/authorship/Cargo.toml index 7c0289909..84d00a6fb 100644 --- a/frame/authorship/Cargo.toml +++ b/frame/authorship/Cargo.toml @@ -13,7 +13,7 @@ readme = "README.md" targets = ["x86_64-unknown-linux-gnu"] [dependencies] -codec = { package = "parity-scale-codec", version = "3.0.0", default-features = false, features = [ +codec = { package = "parity-scale-codec", version = "3.2.2", default-features = false, features = [ "derive", ] } impl-trait-for-tuples = "0.2.2" diff --git a/frame/babe/Cargo.toml b/frame/babe/Cargo.toml index a3232f6f9..aa5813927 100644 --- a/frame/babe/Cargo.toml +++ b/frame/babe/Cargo.toml @@ -13,7 +13,7 @@ readme = "README.md" targets = ["x86_64-unknown-linux-gnu"] [dependencies] -codec = { package = "parity-scale-codec", version = "3.0.0", default-features = false, features = ["derive"] } +codec = { package = "parity-scale-codec", version = "3.2.2", default-features = false, features = ["derive"] } log = { version = "0.4.17", default-features = false } scale-info = { version = "2.1.1", default-features = false, features = ["derive"] } frame-benchmarking = { version = "4.0.0-dev", default-features = false, optional = true, path = "../benchmarking" } diff --git a/frame/bags-list/Cargo.toml b/frame/bags-list/Cargo.toml index 52dd14b7d..e3a4965f6 100644 --- a/frame/bags-list/Cargo.toml +++ b/frame/bags-list/Cargo.toml @@ -13,7 +13,7 @@ targets = ["x86_64-unknown-linux-gnu"] [dependencies] # parity -codec = { package = "parity-scale-codec", version = "3.0.0", default-features = false, features = ["derive"] } +codec = { package = "parity-scale-codec", version = "3.2.2", default-features = false, features = ["derive"] } scale-info = { version = "2.1.1", default-features = false, features = ["derive"] } # primitives diff --git a/frame/balances/Cargo.toml b/frame/balances/Cargo.toml index 934138a90..15ef136aa 100644 --- a/frame/balances/Cargo.toml +++ b/frame/balances/Cargo.toml @@ -13,7 +13,7 @@ readme = "README.md" targets = ["x86_64-unknown-linux-gnu"] [dependencies] -codec = { package = "parity-scale-codec", version = "3.0.0", default-features = false, features = ["derive", "max-encoded-len"] } +codec = { package = "parity-scale-codec", version = "3.2.2", default-features = false, features = ["derive", "max-encoded-len"] } log = { version = "0.4.17", default-features = false } scale-info = { version = "2.1.1", default-features = false, features = ["derive"] } frame-benchmarking = { version = "4.0.0-dev", default-features = false, optional = true, path = "../benchmarking" } diff --git a/frame/beefy-mmr/Cargo.toml b/frame/beefy-mmr/Cargo.toml index f65270acd..54da26c39 100644 --- a/frame/beefy-mmr/Cargo.toml +++ b/frame/beefy-mmr/Cargo.toml @@ -10,7 +10,7 @@ homepage = "https://substrate.io" [dependencies] array-bytes = { version = "4.1", optional = true } -codec = { package = "parity-scale-codec", version = "3.0.0", default-features = false, features = ["derive"] } +codec = { package = "parity-scale-codec", version = "3.2.2", default-features = false, features = ["derive"] } log = { version = "0.4.17", default-features = false } scale-info = { version = "2.1.1", default-features = false, features = ["derive"] } serde = { version = "1.0.136", optional = true } diff --git a/frame/beefy/Cargo.toml b/frame/beefy/Cargo.toml index 707e8e257..4db8c0568 100644 --- a/frame/beefy/Cargo.toml +++ b/frame/beefy/Cargo.toml @@ -9,7 +9,7 @@ description = "BEEFY FRAME pallet" homepage = "https://substrate.io" [dependencies] -codec = { package = "parity-scale-codec", version = "3.0.0", default-features = false, features = ["derive"] } +codec = { package = "parity-scale-codec", version = "3.2.2", default-features = false, features = ["derive"] } scale-info = { version = "2.1.1", default-features = false, features = ["derive"] } serde = { version = "1.0.136", optional = true } beefy-primitives = { version = "4.0.0-dev", default-features = false, path = "../../primitives/beefy", package = "sp-beefy" } diff --git a/frame/benchmarking/Cargo.toml b/frame/benchmarking/Cargo.toml index 7c18b6940..168b26057 100644 --- a/frame/benchmarking/Cargo.toml +++ b/frame/benchmarking/Cargo.toml @@ -13,7 +13,7 @@ readme = "README.md" targets = ["x86_64-unknown-linux-gnu"] [dependencies] -codec = { package = "parity-scale-codec", version = "3.0.0", default-features = false } +codec = { package = "parity-scale-codec", version = "3.2.2", default-features = false } linregress = { version = "0.4.4", optional = true } log = { version = "0.4.17", default-features = false } paste = "1.0" diff --git a/frame/bounties/Cargo.toml b/frame/bounties/Cargo.toml index a5411952a..dcb1adae0 100644 --- a/frame/bounties/Cargo.toml +++ b/frame/bounties/Cargo.toml @@ -13,7 +13,7 @@ readme = "README.md" targets = ["x86_64-unknown-linux-gnu"] [dependencies] -codec = { package = "parity-scale-codec", version = "3.0.0", default-features = false, features = [ +codec = { package = "parity-scale-codec", version = "3.2.2", default-features = false, features = [ "derive", ] } log = { version = "0.4.17", default-features = false } diff --git a/frame/child-bounties/Cargo.toml b/frame/child-bounties/Cargo.toml index 6b0a672d0..8d29b7619 100644 --- a/frame/child-bounties/Cargo.toml +++ b/frame/child-bounties/Cargo.toml @@ -13,7 +13,7 @@ readme = "README.md" targets = ["x86_64-unknown-linux-gnu"] [dependencies] -codec = { package = "parity-scale-codec", version = "3.0.0", default-features = false, features = [ +codec = { package = "parity-scale-codec", version = "3.2.2", default-features = false, features = [ "derive", ] } log = { version = "0.4.17", default-features = false } diff --git a/frame/collective/Cargo.toml b/frame/collective/Cargo.toml index 0e8c5421f..26cd5a8ab 100644 --- a/frame/collective/Cargo.toml +++ b/frame/collective/Cargo.toml @@ -13,7 +13,7 @@ readme = "README.md" targets = ["x86_64-unknown-linux-gnu"] [dependencies] -codec = { package = "parity-scale-codec", version = "3.0.0", default-features = false, features = ["derive"] } +codec = { package = "parity-scale-codec", version = "3.2.2", default-features = false, features = ["derive"] } log = { version = "0.4.17", default-features = false } scale-info = { version = "2.1.1", default-features = false, features = ["derive"] } frame-benchmarking = { version = "4.0.0-dev", default-features = false, optional = true, path = "../benchmarking" } diff --git a/frame/contracts/Cargo.toml b/frame/contracts/Cargo.toml index 8e6368490..c238bdd14 100644 --- a/frame/contracts/Cargo.toml +++ b/frame/contracts/Cargo.toml @@ -14,7 +14,7 @@ targets = ["x86_64-unknown-linux-gnu"] [dependencies] bitflags = "1.3" -codec = { package = "parity-scale-codec", version = "3.0.0", default-features = false, features = [ +codec = { package = "parity-scale-codec", version = "3.2.2", default-features = false, features = [ "derive", "max-encoded-len", ] } diff --git a/frame/contracts/primitives/Cargo.toml b/frame/contracts/primitives/Cargo.toml index d0c3a34dd..aafcd912a 100644 --- a/frame/contracts/primitives/Cargo.toml +++ b/frame/contracts/primitives/Cargo.toml @@ -14,7 +14,7 @@ targets = ["x86_64-unknown-linux-gnu"] [dependencies] bitflags = "1.0" -codec = { package = "parity-scale-codec", version = "3.0.0", default-features = false, features = ["derive"] } +codec = { package = "parity-scale-codec", version = "3.2.2", default-features = false, features = ["derive"] } # Substrate Dependencies (This crate should not rely on frame) sp-std = { version = "5.0.0", default-features = false, path = "../../../primitives/std" } diff --git a/frame/conviction-voting/Cargo.toml b/frame/conviction-voting/Cargo.toml index 9bfc93f2d..d112c54fb 100644 --- a/frame/conviction-voting/Cargo.toml +++ b/frame/conviction-voting/Cargo.toml @@ -14,7 +14,7 @@ targets = ["x86_64-unknown-linux-gnu"] [dependencies] assert_matches = "1.3.0" -codec = { package = "parity-scale-codec", version = "3.0.3", default-features = false, features = [ +codec = { package = "parity-scale-codec", version = "3.2.2", default-features = false, features = [ "derive", "max-encoded-len", ] } diff --git a/frame/democracy/Cargo.toml b/frame/democracy/Cargo.toml index 49dbe133d..fa107218c 100644 --- a/frame/democracy/Cargo.toml +++ b/frame/democracy/Cargo.toml @@ -13,7 +13,7 @@ readme = "README.md" targets = ["x86_64-unknown-linux-gnu"] [dependencies] -codec = { package = "parity-scale-codec", version = "3.0.0", default-features = false, features = [ +codec = { package = "parity-scale-codec", version = "3.2.2", default-features = false, features = [ "derive", ] } scale-info = { version = "2.1.1", default-features = false, features = ["derive"] } diff --git a/frame/election-provider-multi-phase/Cargo.toml b/frame/election-provider-multi-phase/Cargo.toml index a21740dd7..aa734850a 100644 --- a/frame/election-provider-multi-phase/Cargo.toml +++ b/frame/election-provider-multi-phase/Cargo.toml @@ -12,7 +12,7 @@ description = "PALLET two phase election providers" targets = ["x86_64-unknown-linux-gnu"] [dependencies] -codec = { package = "parity-scale-codec", version = "3.0.0", default-features = false, features = [ +codec = { package = "parity-scale-codec", version = "3.2.2", default-features = false, features = [ "derive", ] } scale-info = { version = "2.1.1", default-features = false, features = [ diff --git a/frame/election-provider-support/Cargo.toml b/frame/election-provider-support/Cargo.toml index 108a49eb0..c7f47e721 100644 --- a/frame/election-provider-support/Cargo.toml +++ b/frame/election-provider-support/Cargo.toml @@ -12,7 +12,7 @@ description = "election provider supporting traits" targets = ["x86_64-unknown-linux-gnu"] [dependencies] -codec = { package = "parity-scale-codec", version = "3.0.0", default-features = false, features = ["derive"] } +codec = { package = "parity-scale-codec", version = "3.2.2", default-features = false, features = ["derive"] } scale-info = { version = "2.1.1", default-features = false, features = ["derive"] } frame-election-provider-solution-type = { version = "4.0.0-dev", path = "solution-type" } frame-support = { version = "4.0.0-dev", default-features = false, path = "../support" } diff --git a/frame/election-provider-support/benchmarking/Cargo.toml b/frame/election-provider-support/benchmarking/Cargo.toml index 605389977..bef371ec5 100644 --- a/frame/election-provider-support/benchmarking/Cargo.toml +++ b/frame/election-provider-support/benchmarking/Cargo.toml @@ -12,7 +12,7 @@ description = "Benchmarking for election provider support onchain config trait" targets = ["x86_64-unknown-linux-gnu"] [dependencies] -codec = { package = "parity-scale-codec", version = "3.0.0", default-features = false, features = [ +codec = { package = "parity-scale-codec", version = "3.2.2", default-features = false, features = [ "derive", ] } frame-benchmarking = { version = "4.0.0-dev", default-features = false, optional = true, path = "../../benchmarking" } diff --git a/frame/election-provider-support/solution-type/Cargo.toml b/frame/election-provider-support/solution-type/Cargo.toml index 50c39c547..eb9598dca 100644 --- a/frame/election-provider-support/solution-type/Cargo.toml +++ b/frame/election-provider-support/solution-type/Cargo.toml @@ -21,7 +21,7 @@ proc-macro2 = "1.0.37" proc-macro-crate = "1.1.3" [dev-dependencies] -parity-scale-codec = "3.0.0" +parity-scale-codec = "3.2.2" scale-info = "2.1.1" sp-arithmetic = { version = "6.0.0", path = "../../../primitives/arithmetic" } # used by generate_solution_type: diff --git a/frame/election-provider-support/solution-type/fuzzer/Cargo.toml b/frame/election-provider-support/solution-type/fuzzer/Cargo.toml index 34aeaf930..927569278 100644 --- a/frame/election-provider-support/solution-type/fuzzer/Cargo.toml +++ b/frame/election-provider-support/solution-type/fuzzer/Cargo.toml @@ -17,7 +17,7 @@ clap = { version = "4.0.9", features = ["derive"] } honggfuzz = "0.5" rand = { version = "0.8", features = ["std", "small_rng"] } -codec = { package = "parity-scale-codec", version = "3.0.0", default-features = false, features = ["derive"] } +codec = { package = "parity-scale-codec", version = "3.2.2", default-features = false, features = ["derive"] } scale-info = { version = "2.1.1", default-features = false, features = ["derive"] } frame-election-provider-solution-type = { version = "4.0.0-dev", path = ".." } frame-election-provider-support = { version = "4.0.0-dev", path = "../.." } diff --git a/frame/elections-phragmen/Cargo.toml b/frame/elections-phragmen/Cargo.toml index fb1d924db..ce39e42b8 100644 --- a/frame/elections-phragmen/Cargo.toml +++ b/frame/elections-phragmen/Cargo.toml @@ -13,7 +13,7 @@ readme = "README.md" targets = ["x86_64-unknown-linux-gnu"] [dependencies] -codec = { package = "parity-scale-codec", version = "3.0.0", default-features = false, features = [ +codec = { package = "parity-scale-codec", version = "3.2.2", default-features = false, features = [ "derive", ] } log = { version = "0.4.14", default-features = false } diff --git a/frame/examples/basic/Cargo.toml b/frame/examples/basic/Cargo.toml index 8c69dc6c3..47f623f45 100644 --- a/frame/examples/basic/Cargo.toml +++ b/frame/examples/basic/Cargo.toml @@ -13,7 +13,7 @@ readme = "README.md" targets = ["x86_64-unknown-linux-gnu"] [dependencies] -codec = { package = "parity-scale-codec", version = "3.0.0", default-features = false } +codec = { package = "parity-scale-codec", version = "3.2.2", default-features = false } log = { version = "0.4.17", default-features = false } scale-info = { version = "2.1.1", default-features = false, features = ["derive"] } frame-benchmarking = { version = "4.0.0-dev", default-features = false, optional = true, path = "../../benchmarking" } diff --git a/frame/examples/offchain-worker/Cargo.toml b/frame/examples/offchain-worker/Cargo.toml index 446af8dda..f94cf36b2 100644 --- a/frame/examples/offchain-worker/Cargo.toml +++ b/frame/examples/offchain-worker/Cargo.toml @@ -13,7 +13,7 @@ readme = "README.md" targets = ["x86_64-unknown-linux-gnu"] [dependencies] -codec = { package = "parity-scale-codec", version = "3.0.0", default-features = false } +codec = { package = "parity-scale-codec", version = "3.2.2", default-features = false } lite-json = { version = "0.2.0", default-features = false } log = { version = "0.4.17", default-features = false } scale-info = { version = "2.1.1", default-features = false, features = ["derive"] } diff --git a/frame/executive/Cargo.toml b/frame/executive/Cargo.toml index a6d16e9b0..1636cb392 100644 --- a/frame/executive/Cargo.toml +++ b/frame/executive/Cargo.toml @@ -13,7 +13,7 @@ readme = "README.md" targets = ["x86_64-unknown-linux-gnu"] [dependencies] -codec = { package = "parity-scale-codec", version = "3.0.0", default-features = false, features = [ +codec = { package = "parity-scale-codec", version = "3.2.2", default-features = false, features = [ "derive", ] } scale-info = { version = "2.1.1", default-features = false, features = ["derive"] } diff --git a/frame/fast-unstake/Cargo.toml b/frame/fast-unstake/Cargo.toml index b61060e77..36b4c3a5e 100644 --- a/frame/fast-unstake/Cargo.toml +++ b/frame/fast-unstake/Cargo.toml @@ -12,7 +12,7 @@ description = "FRAME fast unstake pallet" targets = ["x86_64-unknown-linux-gnu"] [dependencies] -codec = { package = "parity-scale-codec", version = "3.0.0", default-features = false } +codec = { package = "parity-scale-codec", version = "3.2.2", default-features = false } log = { version = "0.4.17", default-features = false } scale-info = { version = "2.1.1", default-features = false, features = ["derive"] } diff --git a/frame/grandpa/Cargo.toml b/frame/grandpa/Cargo.toml index 93dd4143a..fa63b8427 100644 --- a/frame/grandpa/Cargo.toml +++ b/frame/grandpa/Cargo.toml @@ -13,7 +13,7 @@ readme = "README.md" targets = ["x86_64-unknown-linux-gnu"] [dependencies] -codec = { package = "parity-scale-codec", version = "3.0.0", default-features = false, features = ["derive"] } +codec = { package = "parity-scale-codec", version = "3.2.2", default-features = false, features = ["derive"] } log = { version = "0.4.17", default-features = false } scale-info = { version = "2.1.1", default-features = false, features = ["derive"] } frame-benchmarking = { version = "4.0.0-dev", default-features = false, optional = true, path = "../benchmarking" } diff --git a/frame/identity/Cargo.toml b/frame/identity/Cargo.toml index 8c7655af6..c1a250368 100644 --- a/frame/identity/Cargo.toml +++ b/frame/identity/Cargo.toml @@ -13,7 +13,7 @@ readme = "README.md" targets = ["x86_64-unknown-linux-gnu"] [dependencies] -codec = { package = "parity-scale-codec", version = "3.0.0", default-features = false, features = ["derive", "max-encoded-len"] } +codec = { package = "parity-scale-codec", version = "3.2.2", default-features = false, features = ["derive", "max-encoded-len"] } enumflags2 = { version = "0.7.4" } scale-info = { version = "2.1.1", default-features = false, features = ["derive"] } frame-benchmarking = { version = "4.0.0-dev", default-features = false, optional = true, path = "../benchmarking" } diff --git a/frame/im-online/Cargo.toml b/frame/im-online/Cargo.toml index c0058e9f2..d76fba4fb 100644 --- a/frame/im-online/Cargo.toml +++ b/frame/im-online/Cargo.toml @@ -13,7 +13,7 @@ readme = "README.md" targets = ["x86_64-unknown-linux-gnu"] [dependencies] -codec = { package = "parity-scale-codec", version = "3.0.0", default-features = false, features = ["derive"] } +codec = { package = "parity-scale-codec", version = "3.2.2", default-features = false, features = ["derive"] } log = { version = "0.4.17", default-features = false } scale-info = { version = "2.1.1", default-features = false, features = ["derive"] } frame-benchmarking = { version = "4.0.0-dev", default-features = false, optional = true, path = "../benchmarking" } diff --git a/frame/indices/Cargo.toml b/frame/indices/Cargo.toml index b3afa397c..b667916eb 100644 --- a/frame/indices/Cargo.toml +++ b/frame/indices/Cargo.toml @@ -13,7 +13,7 @@ readme = "README.md" targets = ["x86_64-unknown-linux-gnu"] [dependencies] -codec = { package = "parity-scale-codec", version = "3.0.0", default-features = false, features = ["derive"] } +codec = { package = "parity-scale-codec", version = "3.2.2", default-features = false, features = ["derive"] } scale-info = { version = "2.1.1", default-features = false, features = ["derive"] } frame-benchmarking = { version = "4.0.0-dev", default-features = false, optional = true, path = "../benchmarking" } frame-support = { version = "4.0.0-dev", default-features = false, path = "../support" } diff --git a/frame/lottery/Cargo.toml b/frame/lottery/Cargo.toml index 9ac69d63e..8c1bf3010 100644 --- a/frame/lottery/Cargo.toml +++ b/frame/lottery/Cargo.toml @@ -12,7 +12,7 @@ description = "FRAME Participation Lottery Pallet" targets = ["x86_64-unknown-linux-gnu"] [dependencies] -codec = { package = "parity-scale-codec", version = "3.0.0", default-features = false, features = [ +codec = { package = "parity-scale-codec", version = "3.2.2", default-features = false, features = [ "derive", ] } scale-info = { version = "2.1.1", default-features = false, features = ["derive"] } diff --git a/frame/membership/Cargo.toml b/frame/membership/Cargo.toml index b457c4c29..bba5700cd 100644 --- a/frame/membership/Cargo.toml +++ b/frame/membership/Cargo.toml @@ -13,7 +13,7 @@ readme = "README.md" targets = ["x86_64-unknown-linux-gnu"] [dependencies] -codec = { package = "parity-scale-codec", version = "3.0.0", default-features = false } +codec = { package = "parity-scale-codec", version = "3.2.2", default-features = false } log = { version = "0.4.17", default-features = false } scale-info = { version = "2.1.1", default-features = false, features = ["derive"] } frame-benchmarking = { version = "4.0.0-dev", default-features = false, optional = true, path = "../benchmarking" } diff --git a/frame/merkle-mountain-range/Cargo.toml b/frame/merkle-mountain-range/Cargo.toml index cf26cfb23..3f7180f79 100644 --- a/frame/merkle-mountain-range/Cargo.toml +++ b/frame/merkle-mountain-range/Cargo.toml @@ -12,7 +12,7 @@ description = "FRAME Merkle Mountain Range pallet." targets = ["x86_64-unknown-linux-gnu"] [dependencies] -codec = { package = "parity-scale-codec", version = "3.0.0", default-features = false } +codec = { package = "parity-scale-codec", version = "3.2.2", default-features = false } scale-info = { version = "2.1.1", default-features = false, features = ["derive"] } frame-benchmarking = { version = "4.0.0-dev", default-features = false, optional = true, path = "../benchmarking" } frame-support = { version = "4.0.0-dev", default-features = false, path = "../support" } diff --git a/frame/message-queue/Cargo.toml b/frame/message-queue/Cargo.toml index 47d114902..115200b82 100644 --- a/frame/message-queue/Cargo.toml +++ b/frame/message-queue/Cargo.toml @@ -9,7 +9,7 @@ repository = "https://github.com/paritytech/substrate/" description = "FRAME pallet to queue and process messages" [dependencies] -codec = { package = "parity-scale-codec", version = "3.0.0", default-features = false, features = ["derive"] } +codec = { package = "parity-scale-codec", version = "3.2.2", default-features = false, features = ["derive"] } scale-info = { version = "2.1.2", default-features = false, features = ["derive"] } serde = { version = "1.0.137", optional = true, features = ["derive"] } log = { version = "0.4.17", default-features = false } diff --git a/frame/multisig/Cargo.toml b/frame/multisig/Cargo.toml index 4e9f4f5d8..5cd744124 100644 --- a/frame/multisig/Cargo.toml +++ b/frame/multisig/Cargo.toml @@ -13,7 +13,7 @@ readme = "README.md" targets = ["x86_64-unknown-linux-gnu"] [dependencies] -codec = { package = "parity-scale-codec", version = "3.0.0", default-features = false } +codec = { package = "parity-scale-codec", version = "3.2.2", default-features = false } scale-info = { version = "2.1.1", default-features = false, features = ["derive"] } frame-benchmarking = { version = "4.0.0-dev", default-features = false, optional = true, path = "../benchmarking" } frame-support = { version = "4.0.0-dev", default-features = false, path = "../support" } diff --git a/frame/nfts/Cargo.toml b/frame/nfts/Cargo.toml index d2cdb4853..9e010cb55 100644 --- a/frame/nfts/Cargo.toml +++ b/frame/nfts/Cargo.toml @@ -13,7 +13,7 @@ readme = "README.md" targets = ["x86_64-unknown-linux-gnu"] [dependencies] -codec = { package = "parity-scale-codec", version = "3.0.0", default-features = false } +codec = { package = "parity-scale-codec", version = "3.2.2", default-features = false } enumflags2 = { version = "0.7.5" } log = { version = "0.4.17", default-features = false } scale-info = { version = "2.1.1", default-features = false, features = ["derive"] } diff --git a/frame/nicks/Cargo.toml b/frame/nicks/Cargo.toml index 2390060c7..1c829efc2 100644 --- a/frame/nicks/Cargo.toml +++ b/frame/nicks/Cargo.toml @@ -13,7 +13,7 @@ readme = "README.md" targets = ["x86_64-unknown-linux-gnu"] [dependencies] -codec = { package = "parity-scale-codec", version = "3.0.0", default-features = false, features = ["derive"] } +codec = { package = "parity-scale-codec", version = "3.2.2", default-features = false, features = ["derive"] } scale-info = { version = "2.1.1", default-features = false, features = ["derive"] } frame-support = { version = "4.0.0-dev", default-features = false, path = "../support" } frame-system = { version = "4.0.0-dev", default-features = false, path = "../system" } diff --git a/frame/nis/Cargo.toml b/frame/nis/Cargo.toml index be12d97dd..a5fc29cd7 100644 --- a/frame/nis/Cargo.toml +++ b/frame/nis/Cargo.toml @@ -13,7 +13,7 @@ readme = "README.md" targets = ["x86_64-unknown-linux-gnu"] [dependencies] -codec = { package = "parity-scale-codec", version = "3.0.0", default-features = false, features = ["derive"] } +codec = { package = "parity-scale-codec", version = "3.2.2", default-features = false, features = ["derive"] } scale-info = { version = "2.1.1", default-features = false, features = ["derive"] } frame-benchmarking = { version = "4.0.0-dev", default-features = false, optional = true, path = "../benchmarking" } frame-support = { version = "4.0.0-dev", default-features = false, path = "../support" } diff --git a/frame/node-authorization/Cargo.toml b/frame/node-authorization/Cargo.toml index fbf486644..c60ce1d12 100644 --- a/frame/node-authorization/Cargo.toml +++ b/frame/node-authorization/Cargo.toml @@ -12,7 +12,7 @@ description = "FRAME pallet for node authorization" targets = ["x86_64-unknown-linux-gnu"] [dependencies] -codec = { package = "parity-scale-codec", version = "3.0.0", default-features = false, features = ["derive"] } +codec = { package = "parity-scale-codec", version = "3.2.2", default-features = false, features = ["derive"] } log = { version = "0.4.17", default-features = false } scale-info = { version = "2.1.1", default-features = false, features = ["derive"] } frame-support = { version = "4.0.0-dev", default-features = false, path = "../support" } diff --git a/frame/nomination-pools/Cargo.toml b/frame/nomination-pools/Cargo.toml index 3eb2d4bc5..d51bcec51 100644 --- a/frame/nomination-pools/Cargo.toml +++ b/frame/nomination-pools/Cargo.toml @@ -13,7 +13,7 @@ targets = ["x86_64-unknown-linux-gnu"] [dependencies] # parity -codec = { package = "parity-scale-codec", version = "3.0.0", default-features = false, features = ["derive"] } +codec = { package = "parity-scale-codec", version = "3.2.2", default-features = false, features = ["derive"] } scale-info = { version = "2.1.1", default-features = false, features = ["derive"] } # FRAME diff --git a/frame/nomination-pools/benchmarking/Cargo.toml b/frame/nomination-pools/benchmarking/Cargo.toml index 74b71a353..b96024af7 100644 --- a/frame/nomination-pools/benchmarking/Cargo.toml +++ b/frame/nomination-pools/benchmarking/Cargo.toml @@ -14,7 +14,7 @@ targets = ["x86_64-unknown-linux-gnu"] [dependencies] # parity -codec = { package = "parity-scale-codec", version = "3.0.0", default-features = false, features = ["derive"] } +codec = { package = "parity-scale-codec", version = "3.2.2", default-features = false, features = ["derive"] } scale-info = { version = "2.1.1", default-features = false, features = ["derive"] } # FRAME diff --git a/frame/nomination-pools/runtime-api/Cargo.toml b/frame/nomination-pools/runtime-api/Cargo.toml index cf72d795c..d1e4fbb30 100644 --- a/frame/nomination-pools/runtime-api/Cargo.toml +++ b/frame/nomination-pools/runtime-api/Cargo.toml @@ -13,7 +13,7 @@ readme = "README.md" targets = ["x86_64-unknown-linux-gnu"] [dependencies] -codec = { package = "parity-scale-codec", version = "3.0.0", default-features = false, features = ["derive"] } +codec = { package = "parity-scale-codec", version = "3.2.2", default-features = false, features = ["derive"] } sp-api = { version = "4.0.0-dev", default-features = false, path = "../../../primitives/api" } sp-std = { version = "5.0.0", default-features = false, path = "../../../primitives/std" } diff --git a/frame/nomination-pools/test-staking/Cargo.toml b/frame/nomination-pools/test-staking/Cargo.toml index a45c7852d..fbe5feca0 100644 --- a/frame/nomination-pools/test-staking/Cargo.toml +++ b/frame/nomination-pools/test-staking/Cargo.toml @@ -13,7 +13,7 @@ publish = false targets = ["x86_64-unknown-linux-gnu"] [dev-dependencies] -codec = { package = "parity-scale-codec", version = "3.0.0", features = ["derive"] } +codec = { package = "parity-scale-codec", version = "3.2.2", features = ["derive"] } scale-info = { version = "2.0.1", features = ["derive"] } sp-runtime = { version = "7.0.0", path = "../../../primitives/runtime" } diff --git a/frame/offences/Cargo.toml b/frame/offences/Cargo.toml index 107a0489c..47ae264a6 100644 --- a/frame/offences/Cargo.toml +++ b/frame/offences/Cargo.toml @@ -13,7 +13,7 @@ readme = "README.md" targets = ["x86_64-unknown-linux-gnu"] [dependencies] -codec = { package = "parity-scale-codec", version = "3.0.0", default-features = false, features = ["derive"] } +codec = { package = "parity-scale-codec", version = "3.2.2", default-features = false, features = ["derive"] } log = { version = "0.4.17", default-features = false } scale-info = { version = "2.1.1", default-features = false, features = ["derive"] } serde = { version = "1.0.136", optional = true } diff --git a/frame/offences/benchmarking/Cargo.toml b/frame/offences/benchmarking/Cargo.toml index e31e8bbed..23377f788 100644 --- a/frame/offences/benchmarking/Cargo.toml +++ b/frame/offences/benchmarking/Cargo.toml @@ -13,7 +13,7 @@ readme = "README.md" targets = ["x86_64-unknown-linux-gnu"] [dependencies] -codec = { package = "parity-scale-codec", version = "3.0.0", default-features = false } +codec = { package = "parity-scale-codec", version = "3.2.2", default-features = false } scale-info = { version = "2.0.0", default-features = false, features = ["derive"] } frame-benchmarking = { version = "4.0.0-dev", default-features = false, path = "../../benchmarking" } frame-election-provider-support = { version = "4.0.0-dev", default-features = false, path = "../../election-provider-support" } diff --git a/frame/preimage/Cargo.toml b/frame/preimage/Cargo.toml index def39d61d..6bc6c8c53 100644 --- a/frame/preimage/Cargo.toml +++ b/frame/preimage/Cargo.toml @@ -9,7 +9,7 @@ repository = "https://github.com/paritytech/substrate/" description = "FRAME pallet for storing preimages of hashes" [dependencies] -codec = { package = "parity-scale-codec", version = "3.0.0", default-features = false, features = ["derive"] } +codec = { package = "parity-scale-codec", version = "3.2.2", default-features = false, features = ["derive"] } scale-info = { version = "2.1.1", default-features = false, features = ["derive"] } frame-benchmarking = { version = "4.0.0-dev", default-features = false, optional = true, path = "../benchmarking" } frame-support = { version = "4.0.0-dev", default-features = false, path = "../support" } diff --git a/frame/proxy/Cargo.toml b/frame/proxy/Cargo.toml index 1674e4086..5c69fcfb9 100644 --- a/frame/proxy/Cargo.toml +++ b/frame/proxy/Cargo.toml @@ -13,7 +13,7 @@ readme = "README.md" targets = ["x86_64-unknown-linux-gnu"] [dependencies] -codec = { package = "parity-scale-codec", version = "3.0.0", default-features = false, features = ["max-encoded-len"] } +codec = { package = "parity-scale-codec", version = "3.2.2", default-features = false, features = ["max-encoded-len"] } scale-info = { version = "2.1.1", default-features = false, features = ["derive"] } frame-benchmarking = { version = "4.0.0-dev", default-features = false, optional = true, path = "../benchmarking" } frame-support = { version = "4.0.0-dev", default-features = false, path = "../support" } diff --git a/frame/randomness-collective-flip/Cargo.toml b/frame/randomness-collective-flip/Cargo.toml index 5a20949e9..435c38d63 100644 --- a/frame/randomness-collective-flip/Cargo.toml +++ b/frame/randomness-collective-flip/Cargo.toml @@ -13,7 +13,7 @@ readme = "README.md" targets = ["x86_64-unknown-linux-gnu"] [dependencies] -codec = { package = "parity-scale-codec", version = "3.0.0", default-features = false, features = ["derive"] } +codec = { package = "parity-scale-codec", version = "3.2.2", default-features = false, features = ["derive"] } safe-mix = { version = "1.0", default-features = false } scale-info = { version = "2.1.1", default-features = false, features = ["derive"] } frame-support = { version = "4.0.0-dev", default-features = false, path = "../support" } diff --git a/frame/ranked-collective/Cargo.toml b/frame/ranked-collective/Cargo.toml index c5e79eb68..e19aaa443 100644 --- a/frame/ranked-collective/Cargo.toml +++ b/frame/ranked-collective/Cargo.toml @@ -13,7 +13,7 @@ readme = "README.md" targets = ["x86_64-unknown-linux-gnu"] [dependencies] -codec = { package = "parity-scale-codec", version = "3.0.0", default-features = false, features = ["derive"] } +codec = { package = "parity-scale-codec", version = "3.2.2", default-features = false, features = ["derive"] } log = { version = "0.4.16", default-features = false } scale-info = { version = "2.0.1", default-features = false, features = ["derive"] } frame-benchmarking = { version = "4.0.0-dev", default-features = false, optional = true, path = "../benchmarking" } diff --git a/frame/recovery/Cargo.toml b/frame/recovery/Cargo.toml index cdcebbec1..3c25c0002 100644 --- a/frame/recovery/Cargo.toml +++ b/frame/recovery/Cargo.toml @@ -13,7 +13,7 @@ readme = "README.md" targets = ["x86_64-unknown-linux-gnu"] [dependencies] -codec = { package = "parity-scale-codec", version = "3.0.0", default-features = false, features = ["derive"] } +codec = { package = "parity-scale-codec", version = "3.2.2", default-features = false, features = ["derive"] } scale-info = { version = "2.1.1", default-features = false, features = ["derive"] } frame-benchmarking = { version = "4.0.0-dev", default-features = false, optional = true, path = "../benchmarking" } frame-support = { version = "4.0.0-dev", default-features = false, path = "../support" } diff --git a/frame/referenda/Cargo.toml b/frame/referenda/Cargo.toml index 02894e149..ac4c8044d 100644 --- a/frame/referenda/Cargo.toml +++ b/frame/referenda/Cargo.toml @@ -14,7 +14,7 @@ targets = ["x86_64-unknown-linux-gnu"] [dependencies] assert_matches = { version = "1.5", optional = true } -codec = { package = "parity-scale-codec", version = "3.0.3", default-features = false, features = [ +codec = { package = "parity-scale-codec", version = "3.2.2", default-features = false, features = [ "derive", ] } scale-info = { version = "2.1.1", default-features = false, features = ["derive"] } diff --git a/frame/remark/Cargo.toml b/frame/remark/Cargo.toml index a827d165f..151cc3888 100644 --- a/frame/remark/Cargo.toml +++ b/frame/remark/Cargo.toml @@ -13,7 +13,7 @@ readme = "README.md" targets = ["x86_64-unknown-linux-gnu"] [dependencies] -codec = { package = "parity-scale-codec", version = "3.0.0", default-features = false } +codec = { package = "parity-scale-codec", version = "3.2.2", default-features = false } scale-info = { version = "2.1.1", default-features = false, features = ["derive"] } serde = { version = "1.0.136", optional = true } frame-benchmarking = { version = "4.0.0-dev", default-features = false, optional = true, path = "../benchmarking" } diff --git a/frame/root-offences/Cargo.toml b/frame/root-offences/Cargo.toml index 76eb832c8..fd1f7c632 100644 --- a/frame/root-offences/Cargo.toml +++ b/frame/root-offences/Cargo.toml @@ -13,7 +13,7 @@ readme = "README.md" targets = ["x86_64-unknown-linux-gnu"] [dependencies] -codec = { package = "parity-scale-codec", version = "3.0.0", default-features = false, features = ["derive"] } +codec = { package = "parity-scale-codec", version = "3.2.2", default-features = false, features = ["derive"] } scale-info = { version = "2.1.1", default-features = false, features = ["derive"] } pallet-session = { version = "4.0.0-dev", features = [ "historical" ], path = "../../frame/session", default-features = false } diff --git a/frame/root-testing/Cargo.toml b/frame/root-testing/Cargo.toml index 4d3f70c5d..09262b3d3 100644 --- a/frame/root-testing/Cargo.toml +++ b/frame/root-testing/Cargo.toml @@ -14,7 +14,7 @@ publish = false targets = ["x86_64-unknown-linux-gnu"] [dependencies] -codec = { package = "parity-scale-codec", version = "3.0.0", default-features = false, features = ["derive"] } +codec = { package = "parity-scale-codec", version = "3.2.2", default-features = false, features = ["derive"] } scale-info = { version = "2.1.1", default-features = false, features = ["derive"] } frame-support = { version = "4.0.0-dev", default-features = false, path = "../support" } frame-system = { version = "4.0.0-dev", default-features = false, path = "../system" } diff --git a/frame/scheduler/Cargo.toml b/frame/scheduler/Cargo.toml index 25ac60268..9a67e68a0 100644 --- a/frame/scheduler/Cargo.toml +++ b/frame/scheduler/Cargo.toml @@ -10,7 +10,7 @@ description = "FRAME Scheduler pallet" readme = "README.md" [dependencies] -codec = { package = "parity-scale-codec", version = "3.0.0", default-features = false, features = ["derive"] } +codec = { package = "parity-scale-codec", version = "3.2.2", default-features = false, features = ["derive"] } log = { version = "0.4.17", default-features = false } scale-info = { version = "2.1.1", default-features = false, features = ["derive"] } frame-benchmarking = { version = "4.0.0-dev", default-features = false, optional = true, path = "../benchmarking" } diff --git a/frame/scored-pool/Cargo.toml b/frame/scored-pool/Cargo.toml index a1e8dc453..d44fc1b2f 100644 --- a/frame/scored-pool/Cargo.toml +++ b/frame/scored-pool/Cargo.toml @@ -13,7 +13,7 @@ readme = "README.md" targets = ["x86_64-unknown-linux-gnu"] [dependencies] -codec = { package = "parity-scale-codec", version = "3.0.0", default-features = false, features = ["derive"] } +codec = { package = "parity-scale-codec", version = "3.2.2", default-features = false, features = ["derive"] } scale-info = { version = "2.1.1", default-features = false, features = ["derive"] } frame-support = { version = "4.0.0-dev", default-features = false, path = "../support" } frame-system = { version = "4.0.0-dev", default-features = false, path = "../system" } diff --git a/frame/session/Cargo.toml b/frame/session/Cargo.toml index 57b519e81..bd28bffff 100644 --- a/frame/session/Cargo.toml +++ b/frame/session/Cargo.toml @@ -13,7 +13,7 @@ readme = "README.md" targets = ["x86_64-unknown-linux-gnu"] [dependencies] -codec = { package = "parity-scale-codec", version = "3.0.0", default-features = false, features = ["derive"] } +codec = { package = "parity-scale-codec", version = "3.2.2", default-features = false, features = ["derive"] } impl-trait-for-tuples = "0.2.2" log = { version = "0.4.17", default-features = false } scale-info = { version = "2.1.1", default-features = false, features = ["derive"] } diff --git a/frame/session/benchmarking/Cargo.toml b/frame/session/benchmarking/Cargo.toml index 53d6ff0cd..4d1d9b4ed 100644 --- a/frame/session/benchmarking/Cargo.toml +++ b/frame/session/benchmarking/Cargo.toml @@ -24,7 +24,7 @@ sp-session = { version = "4.0.0-dev", default-features = false, path = "../../.. sp-std = { version = "5.0.0", default-features = false, path = "../../../primitives/std" } [dev-dependencies] -codec = { package = "parity-scale-codec", version = "3.0.0", features = ["derive"] } +codec = { package = "parity-scale-codec", version = "3.2.2", features = ["derive"] } scale-info = "2.1.1" frame-election-provider-support = { version = "4.0.0-dev", path = "../../election-provider-support" } pallet-balances = { version = "4.0.0-dev", path = "../../balances" } diff --git a/frame/society/Cargo.toml b/frame/society/Cargo.toml index 40b78c892..ddc6ea6aa 100644 --- a/frame/society/Cargo.toml +++ b/frame/society/Cargo.toml @@ -13,7 +13,7 @@ readme = "README.md" targets = ["x86_64-unknown-linux-gnu"] [dependencies] -codec = { package = "parity-scale-codec", version = "3.0.0", default-features = false, features = ["derive"] } +codec = { package = "parity-scale-codec", version = "3.2.2", default-features = false, features = ["derive"] } rand_chacha = { version = "0.2", default-features = false } scale-info = { version = "2.1.1", default-features = false, features = ["derive"] } frame-support = { version = "4.0.0-dev", default-features = false, path = "../support" } diff --git a/frame/staking/Cargo.toml b/frame/staking/Cargo.toml index f6b3b95d0..3d5cf1161 100644 --- a/frame/staking/Cargo.toml +++ b/frame/staking/Cargo.toml @@ -14,7 +14,7 @@ targets = ["x86_64-unknown-linux-gnu"] [dependencies] serde = { version = "1.0.136", optional = true } -codec = { package = "parity-scale-codec", version = "3.0.0", default-features = false, features = [ +codec = { package = "parity-scale-codec", version = "3.2.2", default-features = false, features = [ "derive", ] } scale-info = { version = "2.1.1", default-features = false, features = ["derive"] } diff --git a/frame/state-trie-migration/Cargo.toml b/frame/state-trie-migration/Cargo.toml index 90c8f426d..36b5912a6 100644 --- a/frame/state-trie-migration/Cargo.toml +++ b/frame/state-trie-migration/Cargo.toml @@ -12,7 +12,7 @@ description = "FRAME pallet migration of trie" targets = ["x86_64-unknown-linux-gnu"] [dependencies] -codec = { package = "parity-scale-codec", version = "3.0.0", default-features = false } +codec = { package = "parity-scale-codec", version = "3.2.2", default-features = false } log = { version = "0.4.17", default-features = false } scale-info = { version = "2.1.1", default-features = false, features = ["derive"] } serde = { version = "1.0.133", optional = true } diff --git a/frame/sudo/Cargo.toml b/frame/sudo/Cargo.toml index b0e38b013..56d04b172 100644 --- a/frame/sudo/Cargo.toml +++ b/frame/sudo/Cargo.toml @@ -13,7 +13,7 @@ readme = "README.md" targets = ["x86_64-unknown-linux-gnu"] [dependencies] -codec = { package = "parity-scale-codec", version = "3.0.0", default-features = false, features = ["derive"] } +codec = { package = "parity-scale-codec", version = "3.2.2", default-features = false, features = ["derive"] } scale-info = { version = "2.1.1", default-features = false, features = ["derive"] } frame-support = { version = "4.0.0-dev", default-features = false, path = "../support" } frame-system = { version = "4.0.0-dev", default-features = false, path = "../system" } diff --git a/frame/support/Cargo.toml b/frame/support/Cargo.toml index 4945b5ab9..4f62ae42e 100644 --- a/frame/support/Cargo.toml +++ b/frame/support/Cargo.toml @@ -14,7 +14,7 @@ targets = ["x86_64-unknown-linux-gnu"] [dependencies] serde = { version = "1.0.136", optional = true, features = ["derive"] } -codec = { package = "parity-scale-codec", version = "3.0.0", default-features = false, features = ["derive", "max-encoded-len"] } +codec = { package = "parity-scale-codec", version = "3.2.2", default-features = false, features = ["derive", "max-encoded-len"] } scale-info = { version = "2.1.1", default-features = false, features = ["derive"] } frame-metadata = { version = "15.0.0", default-features = false, features = ["v14"] } sp-api = { version = "4.0.0-dev", default-features = false, path = "../../primitives/api" } diff --git a/frame/support/test/Cargo.toml b/frame/support/test/Cargo.toml index 7fe1bcb56..fc211d771 100644 --- a/frame/support/test/Cargo.toml +++ b/frame/support/test/Cargo.toml @@ -13,7 +13,7 @@ targets = ["x86_64-unknown-linux-gnu"] [dependencies] serde = { version = "1.0.136", default-features = false, features = ["derive"] } -codec = { package = "parity-scale-codec", version = "3.0.0", default-features = false, features = ["derive"] } +codec = { package = "parity-scale-codec", version = "3.2.2", default-features = false, features = ["derive"] } scale-info = { version = "2.1.1", default-features = false, features = ["derive"] } sp-arithmetic = { version = "6.0.0", default-features = false, path = "../../../primitives/arithmetic" } sp-io = { version = "7.0.0", path = "../../../primitives/io", default-features = false } diff --git a/frame/support/test/compile_pass/Cargo.toml b/frame/support/test/compile_pass/Cargo.toml index ea22a735b..dd5e1b996 100644 --- a/frame/support/test/compile_pass/Cargo.toml +++ b/frame/support/test/compile_pass/Cargo.toml @@ -12,7 +12,7 @@ repository = "https://github.com/paritytech/substrate/" targets = ["x86_64-unknown-linux-gnu"] [dependencies] -codec = { package = "parity-scale-codec", version = "3.0.0", default-features = false, features = ["derive"] } +codec = { package = "parity-scale-codec", version = "3.2.2", default-features = false, features = ["derive"] } scale-info = { version = "2.1.1", default-features = false, features = ["derive"] } frame-support = { version = "4.0.0-dev", default-features = false, path = "../../" } frame-system = { version = "4.0.0-dev", default-features = false, path = "../../../system" } diff --git a/frame/support/test/pallet/Cargo.toml b/frame/support/test/pallet/Cargo.toml index bf5febeb4..135d0e64b 100644 --- a/frame/support/test/pallet/Cargo.toml +++ b/frame/support/test/pallet/Cargo.toml @@ -12,7 +12,7 @@ repository = "https://github.com/paritytech/substrate/" targets = ["x86_64-unknown-linux-gnu"] [dependencies] -codec = { package = "parity-scale-codec", version = "3.0.0", default-features = false, features = ["derive"] } +codec = { package = "parity-scale-codec", version = "3.2.2", default-features = false, features = ["derive"] } scale-info = { version = "2.0.0", default-features = false, features = ["derive"] } frame-support = { version = "4.0.0-dev", default-features = false, path = "../../" } frame-system = { version = "4.0.0-dev", default-features = false, path = "../../../system" } diff --git a/frame/system/Cargo.toml b/frame/system/Cargo.toml index 55c9b5bda..7f573a9cb 100644 --- a/frame/system/Cargo.toml +++ b/frame/system/Cargo.toml @@ -13,7 +13,7 @@ readme = "README.md" targets = ["x86_64-unknown-linux-gnu"] [dependencies] -codec = { package = "parity-scale-codec", version = "3.0.0", default-features = false, features = ["derive"] } +codec = { package = "parity-scale-codec", version = "3.2.2", default-features = false, features = ["derive"] } log = { version = "0.4.17", default-features = false } scale-info = { version = "2.1.1", default-features = false, features = ["derive"] } serde = { version = "1.0.136", features = ["derive"], optional = true } diff --git a/frame/system/benchmarking/Cargo.toml b/frame/system/benchmarking/Cargo.toml index 30b299ea6..8f0009725 100644 --- a/frame/system/benchmarking/Cargo.toml +++ b/frame/system/benchmarking/Cargo.toml @@ -13,7 +13,7 @@ readme = "README.md" targets = ["x86_64-unknown-linux-gnu"] [dependencies] -codec = { package = "parity-scale-codec", version = "3.0.0", default-features = false } +codec = { package = "parity-scale-codec", version = "3.2.2", default-features = false } scale-info = { version = "2.1.1", default-features = false, features = ["derive"] } frame-benchmarking = { version = "4.0.0-dev", default-features = false, path = "../../benchmarking" } frame-support = { version = "4.0.0-dev", default-features = false, path = "../../support" } diff --git a/frame/system/rpc/runtime-api/Cargo.toml b/frame/system/rpc/runtime-api/Cargo.toml index 63d76d731..cedb4e35b 100644 --- a/frame/system/rpc/runtime-api/Cargo.toml +++ b/frame/system/rpc/runtime-api/Cargo.toml @@ -13,7 +13,7 @@ readme = "README.md" targets = ["x86_64-unknown-linux-gnu"] [dependencies] -codec = { package = "parity-scale-codec", version = "3.0.0", default-features = false } +codec = { package = "parity-scale-codec", version = "3.2.2", default-features = false } sp-api = { version = "4.0.0-dev", default-features = false, path = "../../../../primitives/api" } [features] diff --git a/frame/timestamp/Cargo.toml b/frame/timestamp/Cargo.toml index df63ed0d7..84c56da24 100644 --- a/frame/timestamp/Cargo.toml +++ b/frame/timestamp/Cargo.toml @@ -14,7 +14,7 @@ readme = "README.md" targets = ["x86_64-unknown-linux-gnu"] [dependencies] -codec = { package = "parity-scale-codec", version = "3.0.0", default-features = false, features = ["derive", "max-encoded-len"] } +codec = { package = "parity-scale-codec", version = "3.2.2", default-features = false, features = ["derive", "max-encoded-len"] } log = { version = "0.4.17", default-features = false } scale-info = { version = "2.1.1", default-features = false, features = ["derive"] } frame-benchmarking = { version = "4.0.0-dev", default-features = false, optional = true, path = "../benchmarking" } diff --git a/frame/tips/Cargo.toml b/frame/tips/Cargo.toml index 7d0576ec2..c7db61613 100644 --- a/frame/tips/Cargo.toml +++ b/frame/tips/Cargo.toml @@ -13,7 +13,7 @@ readme = "README.md" targets = ["x86_64-unknown-linux-gnu"] [dependencies] -codec = { package = "parity-scale-codec", version = "3.0.0", default-features = false, features = ["derive"] } +codec = { package = "parity-scale-codec", version = "3.2.2", default-features = false, features = ["derive"] } log = { version = "0.4.17", default-features = false } scale-info = { version = "2.1.1", default-features = false, features = ["derive"] } serde = { version = "1.0.136", features = ["derive"], optional = true } diff --git a/frame/transaction-payment/Cargo.toml b/frame/transaction-payment/Cargo.toml index a2f77b6cf..0c98796e4 100644 --- a/frame/transaction-payment/Cargo.toml +++ b/frame/transaction-payment/Cargo.toml @@ -13,7 +13,7 @@ readme = "README.md" targets = ["x86_64-unknown-linux-gnu"] [dependencies] -codec = { package = "parity-scale-codec", version = "3.0.0", default-features = false, features = [ +codec = { package = "parity-scale-codec", version = "3.2.2", default-features = false, features = [ "derive", ] } scale-info = { version = "2.1.1", default-features = false, features = ["derive"] } diff --git a/frame/transaction-payment/asset-tx-payment/Cargo.toml b/frame/transaction-payment/asset-tx-payment/Cargo.toml index 8e4645a26..324e17a08 100644 --- a/frame/transaction-payment/asset-tx-payment/Cargo.toml +++ b/frame/transaction-payment/asset-tx-payment/Cargo.toml @@ -25,7 +25,7 @@ pallet-transaction-payment = { version = "4.0.0-dev", default-features = false, frame-benchmarking = { version = "4.0.0-dev", default-features = false, path = "../../benchmarking", optional = true } # Other dependencies -codec = { package = "parity-scale-codec", version = "3.0.0", default-features = false, features = ["derive"] } +codec = { package = "parity-scale-codec", version = "3.2.2", default-features = false, features = ["derive"] } scale-info = { version = "2.1.1", default-features = false, features = ["derive"] } serde = { version = "1.0.136", optional = true } diff --git a/frame/transaction-payment/rpc/Cargo.toml b/frame/transaction-payment/rpc/Cargo.toml index b77143201..b9bf226e2 100644 --- a/frame/transaction-payment/rpc/Cargo.toml +++ b/frame/transaction-payment/rpc/Cargo.toml @@ -13,7 +13,7 @@ readme = "README.md" targets = ["x86_64-unknown-linux-gnu"] [dependencies] -codec = { package = "parity-scale-codec", version = "3.0.0" } +codec = { package = "parity-scale-codec", version = "3.2.2" } jsonrpsee = { version = "0.16.2", features = ["client-core", "server", "macros"] } pallet-transaction-payment-rpc-runtime-api = { version = "4.0.0-dev", path = "./runtime-api" } sp-api = { version = "4.0.0-dev", path = "../../../primitives/api" } diff --git a/frame/transaction-payment/rpc/runtime-api/Cargo.toml b/frame/transaction-payment/rpc/runtime-api/Cargo.toml index 86753526f..854e4310b 100644 --- a/frame/transaction-payment/rpc/runtime-api/Cargo.toml +++ b/frame/transaction-payment/rpc/runtime-api/Cargo.toml @@ -13,7 +13,7 @@ readme = "README.md" targets = ["x86_64-unknown-linux-gnu"] [dependencies] -codec = { package = "parity-scale-codec", version = "3.0.0", default-features = false, features = ["derive"] } +codec = { package = "parity-scale-codec", version = "3.2.2", default-features = false, features = ["derive"] } pallet-transaction-payment = { version = "4.0.0-dev", default-features = false, path = "../../../transaction-payment" } sp-api = { version = "4.0.0-dev", default-features = false, path = "../../../../primitives/api" } sp-runtime = { version = "7.0.0", default-features = false, path = "../../../../primitives/runtime" } diff --git a/frame/transaction-storage/Cargo.toml b/frame/transaction-storage/Cargo.toml index 73867c364..527ff4f24 100644 --- a/frame/transaction-storage/Cargo.toml +++ b/frame/transaction-storage/Cargo.toml @@ -14,7 +14,7 @@ targets = ["x86_64-unknown-linux-gnu"] [dependencies] array-bytes = { version = "4.1", optional = true } -codec = { package = "parity-scale-codec", version = "3.0.0", default-features = false } +codec = { package = "parity-scale-codec", version = "3.2.2", default-features = false } scale-info = { version = "2.1.1", default-features = false, features = ["derive"] } serde = { version = "1.0.136", optional = true } frame-benchmarking = { version = "4.0.0-dev", default-features = false, optional = true, path = "../benchmarking" } diff --git a/frame/treasury/Cargo.toml b/frame/treasury/Cargo.toml index 993f89ff0..1fd1d7b3d 100644 --- a/frame/treasury/Cargo.toml +++ b/frame/treasury/Cargo.toml @@ -13,7 +13,7 @@ readme = "README.md" targets = ["x86_64-unknown-linux-gnu"] [dependencies] -codec = { package = "parity-scale-codec", version = "3.0.0", default-features = false, features = [ +codec = { package = "parity-scale-codec", version = "3.2.2", default-features = false, features = [ "derive", "max-encoded-len", ] } diff --git a/frame/try-runtime/Cargo.toml b/frame/try-runtime/Cargo.toml index 87aca0d1e..042dba5ed 100644 --- a/frame/try-runtime/Cargo.toml +++ b/frame/try-runtime/Cargo.toml @@ -12,7 +12,7 @@ description = "FRAME pallet for democracy" targets = ["x86_64-unknown-linux-gnu"] [dependencies] -codec = { package = "parity-scale-codec", version = "3.0.0", default-features = false, features = ["derive"]} +codec = { package = "parity-scale-codec", version = "3.2.2", default-features = false, features = ["derive"]} frame-support = { version = "4.0.0-dev", default-features = false, path = "../support" } sp-api = { version = "4.0.0-dev", default-features = false, path = "../../primitives/api" } sp-runtime = { version = "7.0.0", default-features = false, path = "../../primitives/runtime" } diff --git a/frame/uniques/Cargo.toml b/frame/uniques/Cargo.toml index 6e3624074..a01ea60d8 100644 --- a/frame/uniques/Cargo.toml +++ b/frame/uniques/Cargo.toml @@ -13,7 +13,7 @@ readme = "README.md" targets = ["x86_64-unknown-linux-gnu"] [dependencies] -codec = { package = "parity-scale-codec", version = "3.0.0", default-features = false } +codec = { package = "parity-scale-codec", version = "3.2.2", default-features = false } log = { version = "0.4.17", default-features = false } scale-info = { version = "2.1.1", default-features = false, features = ["derive"] } frame-benchmarking = { version = "4.0.0-dev", default-features = false, optional = true, path = "../benchmarking" } diff --git a/frame/utility/Cargo.toml b/frame/utility/Cargo.toml index de293ed5d..099526d3d 100644 --- a/frame/utility/Cargo.toml +++ b/frame/utility/Cargo.toml @@ -13,7 +13,7 @@ readme = "README.md" targets = ["x86_64-unknown-linux-gnu"] [dependencies] -codec = { package = "parity-scale-codec", version = "3.0.0", default-features = false } +codec = { package = "parity-scale-codec", version = "3.2.2", default-features = false } scale-info = { version = "2.1.1", default-features = false, features = ["derive"] } frame-benchmarking = { version = "4.0.0-dev", default-features = false, optional = true, path = "../benchmarking" } frame-support = { version = "4.0.0-dev", default-features = false, path = "../support" } diff --git a/frame/vesting/Cargo.toml b/frame/vesting/Cargo.toml index 23fa06454..c18d1f45e 100644 --- a/frame/vesting/Cargo.toml +++ b/frame/vesting/Cargo.toml @@ -13,7 +13,7 @@ readme = "README.md" targets = ["x86_64-unknown-linux-gnu"] [dependencies] -codec = { package = "parity-scale-codec", version = "3.0.0", default-features = false, features = [ +codec = { package = "parity-scale-codec", version = "3.2.2", default-features = false, features = [ "derive", ] } log = { version = "0.4.17", default-features = false } diff --git a/frame/whitelist/Cargo.toml b/frame/whitelist/Cargo.toml index f3f44d318..56ceaca9f 100644 --- a/frame/whitelist/Cargo.toml +++ b/frame/whitelist/Cargo.toml @@ -12,7 +12,7 @@ description = "FRAME pallet for whitelisting call, and dispatch from specific or targets = ["x86_64-unknown-linux-gnu"] [dependencies] -codec = { package = "parity-scale-codec", version = "3.0.0", default-features = false, features = ["derive", "max-encoded-len"] } +codec = { package = "parity-scale-codec", version = "3.2.2", default-features = false, features = ["derive", "max-encoded-len"] } scale-info = { version = "2.1", default-features = false, features = ["derive"] } frame-benchmarking = { version = "4.0.0-dev", default-features = false, optional = true, path = "../benchmarking" } frame-support = { version = "4.0.0-dev", default-features = false, path = "../support" } diff --git a/primitives/api/Cargo.toml b/primitives/api/Cargo.toml index 3139c66ce..75197bcae 100644 --- a/primitives/api/Cargo.toml +++ b/primitives/api/Cargo.toml @@ -13,7 +13,7 @@ readme = "README.md" targets = ["x86_64-unknown-linux-gnu"] [dependencies] -codec = { package = "parity-scale-codec", version = "3.0.0", default-features = false } +codec = { package = "parity-scale-codec", version = "3.2.2", default-features = false } sp-api-proc-macro = { version = "4.0.0-dev", path = "proc-macro" } sp-core = { version = "7.0.0", default-features = false, path = "../core" } sp-std = { version = "5.0.0", default-features = false, path = "../std" } diff --git a/primitives/api/test/Cargo.toml b/primitives/api/test/Cargo.toml index 7f15287cf..b3ceb14fe 100644 --- a/primitives/api/test/Cargo.toml +++ b/primitives/api/test/Cargo.toml @@ -19,7 +19,7 @@ sp-tracing = { version = "6.0.0", path = "../../tracing" } sp-runtime = { version = "7.0.0", path = "../../runtime" } sp-consensus = { version = "0.10.0-dev", path = "../../consensus/common" } sc-block-builder = { version = "0.10.0-dev", path = "../../../client/block-builder" } -codec = { package = "parity-scale-codec", version = "3.0.0" } +codec = { package = "parity-scale-codec", version = "3.2.2" } sp-state-machine = { version = "0.13.0", path = "../../state-machine" } trybuild = "1.0.74" rustversion = "1.0.6" diff --git a/primitives/application-crypto/Cargo.toml b/primitives/application-crypto/Cargo.toml index 39a3413bc..e7aa118df 100644 --- a/primitives/application-crypto/Cargo.toml +++ b/primitives/application-crypto/Cargo.toml @@ -16,7 +16,7 @@ targets = ["x86_64-unknown-linux-gnu"] [dependencies] sp-core = { version = "7.0.0", default-features = false, path = "../core" } -codec = { package = "parity-scale-codec", version = "3.0.0", default-features = false, features = ["derive"] } +codec = { package = "parity-scale-codec", version = "3.2.2", default-features = false, features = ["derive"] } scale-info = { version = "2.1.1", default-features = false, features = ["derive"] } serde = { version = "1.0.136", optional = true, features = ["derive"] } sp-std = { version = "5.0.0", default-features = false, path = "../std" } diff --git a/primitives/arithmetic/Cargo.toml b/primitives/arithmetic/Cargo.toml index 2da8767fd..bbb4bbd17 100644 --- a/primitives/arithmetic/Cargo.toml +++ b/primitives/arithmetic/Cargo.toml @@ -14,7 +14,7 @@ readme = "README.md" targets = ["x86_64-unknown-linux-gnu"] [dependencies] -codec = { package = "parity-scale-codec", version = "3.0.0", default-features = false, features = [ +codec = { package = "parity-scale-codec", version = "3.2.2", default-features = false, features = [ "derive", "max-encoded-len", ] } diff --git a/primitives/arithmetic/src/lib.rs b/primitives/arithmetic/src/lib.rs index f1f8fc8b5..590aaa316 100644 --- a/primitives/arithmetic/src/lib.rs +++ b/primitives/arithmetic/src/lib.rs @@ -50,14 +50,14 @@ pub use rational::{Rational128, RationalInfinite}; use sp_std::{cmp::Ordering, fmt::Debug, prelude::*}; use traits::{BaseArithmetic, One, SaturatedConversion, Unsigned, Zero}; -use codec::{Decode, Encode}; +use codec::{Decode, Encode, MaxEncodedLen}; use scale_info::TypeInfo; #[cfg(feature = "std")] use serde::{Deserialize, Serialize}; /// Arithmetic errors. -#[derive(Eq, PartialEq, Clone, Copy, Encode, Decode, Debug, TypeInfo)] +#[derive(Eq, PartialEq, Clone, Copy, Encode, Decode, Debug, TypeInfo, MaxEncodedLen)] #[cfg_attr(feature = "std", derive(Serialize, Deserialize))] pub enum ArithmeticError { /// Underflow. diff --git a/primitives/authority-discovery/Cargo.toml b/primitives/authority-discovery/Cargo.toml index 4b450a4da..ff36cf58d 100644 --- a/primitives/authority-discovery/Cargo.toml +++ b/primitives/authority-discovery/Cargo.toml @@ -13,7 +13,7 @@ readme = "README.md" targets = ["x86_64-unknown-linux-gnu"] [dependencies] -codec = { package = "parity-scale-codec", version = "3.0.0", default-features = false } +codec = { package = "parity-scale-codec", version = "3.2.2", default-features = false } scale-info = { version = "2.1.1", default-features = false, features = ["derive"] } sp-api = { version = "4.0.0-dev", default-features = false, path = "../api" } sp-application-crypto = { version = "7.0.0", default-features = false, path = "../application-crypto" } diff --git a/primitives/authorship/Cargo.toml b/primitives/authorship/Cargo.toml index 49107ebed..31ea3b2d4 100644 --- a/primitives/authorship/Cargo.toml +++ b/primitives/authorship/Cargo.toml @@ -14,7 +14,7 @@ targets = ["x86_64-unknown-linux-gnu"] [dependencies] async-trait = { version = "0.1.57", optional = true } -codec = { package = "parity-scale-codec", version = "3.0.0", default-features = false, features = ["derive"] } +codec = { package = "parity-scale-codec", version = "3.2.2", default-features = false, features = ["derive"] } sp-inherents = { version = "4.0.0-dev", default-features = false, path = "../inherents" } sp-runtime = { version = "7.0.0", default-features = false, path = "../runtime" } sp-std = { version = "5.0.0", default-features = false, path = "../std" } diff --git a/primitives/beefy/Cargo.toml b/primitives/beefy/Cargo.toml index b286d9878..6a22f0383 100644 --- a/primitives/beefy/Cargo.toml +++ b/primitives/beefy/Cargo.toml @@ -12,7 +12,7 @@ description = "Primitives for BEEFY protocol." targets = ["x86_64-unknown-linux-gnu"] [dependencies] -codec = { package = "parity-scale-codec", version = "3.0.0", default-features = false, features = ["derive"] } +codec = { package = "parity-scale-codec", version = "3.2.2", default-features = false, features = ["derive"] } serde = { version = "1.0.136", optional = true, features = ["derive"] } scale-info = { version = "2.1.1", default-features = false, features = ["derive"] } sp-api = { version = "4.0.0-dev", default-features = false, path = "../api" } diff --git a/primitives/block-builder/Cargo.toml b/primitives/block-builder/Cargo.toml index 7770a0e21..6ccb7980d 100644 --- a/primitives/block-builder/Cargo.toml +++ b/primitives/block-builder/Cargo.toml @@ -13,7 +13,7 @@ readme = "README.md" targets = ["x86_64-unknown-linux-gnu"] [dependencies] -codec = { package = "parity-scale-codec", version = "3.0.0", default-features = false } +codec = { package = "parity-scale-codec", version = "3.2.2", default-features = false } sp-api = { version = "4.0.0-dev", default-features = false, path = "../api" } sp-inherents = { version = "4.0.0-dev", default-features = false, path = "../inherents" } sp-runtime = { version = "7.0.0", default-features = false, path = "../runtime" } diff --git a/primitives/blockchain/Cargo.toml b/primitives/blockchain/Cargo.toml index 7f5f22fe0..68791852d 100644 --- a/primitives/blockchain/Cargo.toml +++ b/primitives/blockchain/Cargo.toml @@ -14,7 +14,7 @@ readme = "README.md" targets = ["x86_64-unknown-linux-gnu"] [dependencies] -codec = { package = "parity-scale-codec", version = "3.0.0", default-features = false, features = ["derive"] } +codec = { package = "parity-scale-codec", version = "3.2.2", default-features = false, features = ["derive"] } futures = "0.3.21" log = "0.4.17" lru = "0.8.1" diff --git a/primitives/consensus/aura/Cargo.toml b/primitives/consensus/aura/Cargo.toml index 51b20e4fb..001dc4a69 100644 --- a/primitives/consensus/aura/Cargo.toml +++ b/primitives/consensus/aura/Cargo.toml @@ -14,7 +14,7 @@ targets = ["x86_64-unknown-linux-gnu"] [dependencies] async-trait = { version = "0.1.57", optional = true } -codec = { package = "parity-scale-codec", version = "3.0.0", default-features = false } +codec = { package = "parity-scale-codec", version = "3.2.2", default-features = false } scale-info = { version = "2.1.1", default-features = false, features = ["derive"] } sp-api = { version = "4.0.0-dev", default-features = false, path = "../../api" } sp-application-crypto = { version = "7.0.0", default-features = false, path = "../../application-crypto" } diff --git a/primitives/consensus/babe/Cargo.toml b/primitives/consensus/babe/Cargo.toml index 25cb8a2bf..334fff281 100644 --- a/primitives/consensus/babe/Cargo.toml +++ b/primitives/consensus/babe/Cargo.toml @@ -14,7 +14,7 @@ targets = ["x86_64-unknown-linux-gnu"] [dependencies] async-trait = { version = "0.1.57", optional = true } -codec = { package = "parity-scale-codec", version = "3.0.0", default-features = false } +codec = { package = "parity-scale-codec", version = "3.2.2", default-features = false } merlin = { version = "2.0", default-features = false } scale-info = { version = "2.1.1", default-features = false, features = ["derive"] } serde = { version = "1.0.136", features = ["derive"], optional = true } diff --git a/primitives/consensus/common/Cargo.toml b/primitives/consensus/common/Cargo.toml index 9b4cac722..83b09f9eb 100644 --- a/primitives/consensus/common/Cargo.toml +++ b/primitives/consensus/common/Cargo.toml @@ -15,7 +15,7 @@ targets = ["x86_64-unknown-linux-gnu"] [dependencies] async-trait = "0.1.57" -codec = { package = "parity-scale-codec", version = "3.0.0", features = [ +codec = { package = "parity-scale-codec", version = "3.2.2", features = [ "derive", ] } futures = { version = "0.3.21", features = ["thread-pool"] } diff --git a/primitives/consensus/pow/Cargo.toml b/primitives/consensus/pow/Cargo.toml index 495372089..6cc832b5a 100644 --- a/primitives/consensus/pow/Cargo.toml +++ b/primitives/consensus/pow/Cargo.toml @@ -13,7 +13,7 @@ readme = "README.md" targets = ["x86_64-unknown-linux-gnu"] [dependencies] -codec = { package = "parity-scale-codec", version = "3.0.0", default-features = false, features = ["derive"] } +codec = { package = "parity-scale-codec", version = "3.2.2", default-features = false, features = ["derive"] } sp-api = { version = "4.0.0-dev", default-features = false, path = "../../api" } sp-core = { version = "7.0.0", default-features = false, path = "../../core" } sp-runtime = { version = "7.0.0", default-features = false, path = "../../runtime" } diff --git a/primitives/consensus/slots/Cargo.toml b/primitives/consensus/slots/Cargo.toml index 3360e8077..a4d85dff6 100644 --- a/primitives/consensus/slots/Cargo.toml +++ b/primitives/consensus/slots/Cargo.toml @@ -13,7 +13,7 @@ readme = "README.md" targets = ["x86_64-unknown-linux-gnu"] [dependencies] -codec = { package = "parity-scale-codec", version = "3.0.0", default-features = false, features = ["derive", "max-encoded-len"] } +codec = { package = "parity-scale-codec", version = "3.2.2", default-features = false, features = ["derive", "max-encoded-len"] } scale-info = { version = "2.0.0", default-features = false, features = ["derive"] } serde = { version = "1.0", features = ["derive"], optional = true } sp-std = { version = "5.0.0", default-features = false, path = "../../std" } diff --git a/primitives/consensus/vrf/Cargo.toml b/primitives/consensus/vrf/Cargo.toml index 7159da2aa..a4f92c30c 100644 --- a/primitives/consensus/vrf/Cargo.toml +++ b/primitives/consensus/vrf/Cargo.toml @@ -13,7 +13,7 @@ readme = "README.md" targets = ["x86_64-unknown-linux-gnu"] [dependencies] -codec = { package = "parity-scale-codec", version = "3.0.0", default-features = false } +codec = { package = "parity-scale-codec", version = "3.2.2", default-features = false } scale-info = { version = "2.1.1", default-features = false } schnorrkel = { version = "0.9.1", default-features = false, features = ["preaudit_deprecated", "u64_backend"] } sp-core = { version = "7.0.0", default-features = false, path = "../../core" } diff --git a/primitives/core/Cargo.toml b/primitives/core/Cargo.toml index 68c780c47..f9a522178 100644 --- a/primitives/core/Cargo.toml +++ b/primitives/core/Cargo.toml @@ -13,7 +13,7 @@ documentation = "https://docs.rs/sp-core" targets = ["x86_64-unknown-linux-gnu"] [dependencies] -codec = { package = "parity-scale-codec", version = "3.0.0", default-features = false, features = [ +codec = { package = "parity-scale-codec", version = "3.2.2", default-features = false, features = [ "derive", "max-encoded-len", ] } diff --git a/primitives/externalities/Cargo.toml b/primitives/externalities/Cargo.toml index c3d32370d..0777111d8 100644 --- a/primitives/externalities/Cargo.toml +++ b/primitives/externalities/Cargo.toml @@ -14,7 +14,7 @@ readme = "README.md" targets = ["x86_64-unknown-linux-gnu"] [dependencies] -codec = { package = "parity-scale-codec", version = "3.0.0", default-features = false } +codec = { package = "parity-scale-codec", version = "3.2.2", default-features = false } environmental = { version = "1.1.3", default-features = false } sp-std = { version = "5.0.0", default-features = false, path = "../std" } sp-storage = { version = "7.0.0", default-features = false, path = "../storage" } diff --git a/primitives/finality-grandpa/Cargo.toml b/primitives/finality-grandpa/Cargo.toml index c33646e3c..39bb6713a 100644 --- a/primitives/finality-grandpa/Cargo.toml +++ b/primitives/finality-grandpa/Cargo.toml @@ -14,7 +14,7 @@ readme = "README.md" targets = ["x86_64-unknown-linux-gnu"] [dependencies] -codec = { package = "parity-scale-codec", version = "3.0.0", default-features = false, features = ["derive"] } +codec = { package = "parity-scale-codec", version = "3.2.2", default-features = false, features = ["derive"] } grandpa = { package = "finality-grandpa", version = "0.16.1", default-features = false, features = ["derive-codec"] } log = { version = "0.4.17", default-features = false } scale-info = { version = "2.1.1", default-features = false, features = ["derive"] } diff --git a/primitives/inherents/Cargo.toml b/primitives/inherents/Cargo.toml index 8f6d8aef1..c901a6e68 100644 --- a/primitives/inherents/Cargo.toml +++ b/primitives/inherents/Cargo.toml @@ -15,7 +15,7 @@ targets = ["x86_64-unknown-linux-gnu"] [dependencies] async-trait = { version = "0.1.57", optional = true } -codec = { package = "parity-scale-codec", version = "3.0.0", default-features = false, features = ["derive"] } +codec = { package = "parity-scale-codec", version = "3.2.2", default-features = false, features = ["derive"] } impl-trait-for-tuples = "0.2.2" thiserror = { version = "1.0.30", optional = true } sp-core = { version = "7.0.0", default-features = false, path = "../core" } diff --git a/primitives/io/Cargo.toml b/primitives/io/Cargo.toml index d83c82c02..e56bfcf56 100644 --- a/primitives/io/Cargo.toml +++ b/primitives/io/Cargo.toml @@ -16,7 +16,7 @@ targets = ["x86_64-unknown-linux-gnu"] [dependencies] bytes = { version = "1.1.0", default-features = false } -codec = { package = "parity-scale-codec", version = "3.1.3", default-features = false, features = ["bytes"] } +codec = { package = "parity-scale-codec", version = "3.2.2", default-features = false, features = ["bytes"] } sp-core = { version = "7.0.0", default-features = false, path = "../core" } sp-keystore = { version = "0.13.0", default-features = false, optional = true, path = "../keystore" } sp-std = { version = "5.0.0", default-features = false, path = "../std" } diff --git a/primitives/keystore/Cargo.toml b/primitives/keystore/Cargo.toml index 0d5d7ca56..9386cb5d1 100644 --- a/primitives/keystore/Cargo.toml +++ b/primitives/keystore/Cargo.toml @@ -14,7 +14,7 @@ targets = ["x86_64-unknown-linux-gnu"] [dependencies] async-trait = "0.1.57" -codec = { package = "parity-scale-codec", version = "3.0.0", default-features = false, features = ["derive"] } +codec = { package = "parity-scale-codec", version = "3.2.2", default-features = false, features = ["derive"] } futures = "0.3.21" merlin = { version = "2.0", default-features = false } parking_lot = { version = "0.12.1", default-features = false } diff --git a/primitives/merkle-mountain-range/Cargo.toml b/primitives/merkle-mountain-range/Cargo.toml index 2e532990a..97add1ed1 100644 --- a/primitives/merkle-mountain-range/Cargo.toml +++ b/primitives/merkle-mountain-range/Cargo.toml @@ -12,7 +12,7 @@ description = "Merkle Mountain Range primitives." targets = ["x86_64-unknown-linux-gnu"] [dependencies] -codec = { package = "parity-scale-codec", version = "3.0.0", default-features = false } +codec = { package = "parity-scale-codec", version = "3.2.2", default-features = false } scale-info = { version = "2.1.1", default-features = false, features = ["derive"] } log = { version = "0.4.17", default-features = false } mmr-lib = { package = "ckb-merkle-mountain-range", version = "0.5.2", default-features = false } diff --git a/primitives/npos-elections/Cargo.toml b/primitives/npos-elections/Cargo.toml index 702986da3..7a4bdbd67 100644 --- a/primitives/npos-elections/Cargo.toml +++ b/primitives/npos-elections/Cargo.toml @@ -13,7 +13,7 @@ readme = "README.md" targets = ["x86_64-unknown-linux-gnu"] [dependencies] -codec = { package = "parity-scale-codec", version = "3.0.0", default-features = false, features = ["derive"] } +codec = { package = "parity-scale-codec", version = "3.2.2", default-features = false, features = ["derive"] } scale-info = { version = "2.1.1", default-features = false, features = ["derive"] } serde = { version = "1.0.136", features = ["derive"], optional = true } sp-arithmetic = { version = "6.0.0", default-features = false, path = "../arithmetic" } diff --git a/primitives/npos-elections/fuzzer/Cargo.toml b/primitives/npos-elections/fuzzer/Cargo.toml index 860ed6b18..8a058cc92 100644 --- a/primitives/npos-elections/fuzzer/Cargo.toml +++ b/primitives/npos-elections/fuzzer/Cargo.toml @@ -15,7 +15,7 @@ targets = ["x86_64-unknown-linux-gnu"] [dependencies] clap = { version = "4.0.9", features = ["derive"] } -codec = { package = "parity-scale-codec", version = "3.0.0", default-features = false, features = ["derive"] } +codec = { package = "parity-scale-codec", version = "3.2.2", default-features = false, features = ["derive"] } honggfuzz = "0.5" rand = { version = "0.8", features = ["std", "small_rng"] } scale-info = { version = "2.1.1", default-features = false, features = ["derive"] } diff --git a/primitives/runtime-interface/Cargo.toml b/primitives/runtime-interface/Cargo.toml index da746179e..f2ddc84b1 100644 --- a/primitives/runtime-interface/Cargo.toml +++ b/primitives/runtime-interface/Cargo.toml @@ -20,7 +20,7 @@ sp-std = { version = "5.0.0", default-features = false, path = "../std" } sp-tracing = { version = "6.0.0", default-features = false, path = "../tracing" } sp-runtime-interface-proc-macro = { version = "6.0.0", path = "proc-macro" } sp-externalities = { version = "0.13.0", default-features = false, path = "../externalities" } -codec = { package = "parity-scale-codec", version = "3.0.0", default-features = false, features = ["bytes"] } +codec = { package = "parity-scale-codec", version = "3.2.2", default-features = false, features = ["bytes"] } static_assertions = "1.0.0" primitive-types = { version = "0.12.0", default-features = false } sp-storage = { version = "7.0.0", default-features = false, path = "../storage" } diff --git a/primitives/runtime/Cargo.toml b/primitives/runtime/Cargo.toml index 79bbda700..3976de60c 100644 --- a/primitives/runtime/Cargo.toml +++ b/primitives/runtime/Cargo.toml @@ -14,7 +14,7 @@ readme = "README.md" targets = ["x86_64-unknown-linux-gnu"] [dependencies] -codec = { package = "parity-scale-codec", version = "3.0.0", default-features = false, features = ["derive", "max-encoded-len"] } +codec = { package = "parity-scale-codec", version = "3.2.2", default-features = false, features = ["derive", "max-encoded-len"] } either = { version = "1.5", default-features = false } hash256-std-hasher = { version = "0.15.2", default-features = false } impl-trait-for-tuples = "0.2.2" diff --git a/primitives/runtime/src/lib.rs b/primitives/runtime/src/lib.rs index 96fe7d248..8a32d0b9b 100644 --- a/primitives/runtime/src/lib.rs +++ b/primitives/runtime/src/lib.rs @@ -474,7 +474,7 @@ pub type DispatchResult = sp_std::result::Result<(), DispatchError>; pub type DispatchResultWithInfo = sp_std::result::Result>; /// Reason why a pallet call failed. -#[derive(Eq, Clone, Copy, Encode, Decode, Debug, TypeInfo)] +#[derive(Eq, Clone, Copy, Encode, Decode, Debug, TypeInfo, MaxEncodedLen)] #[cfg_attr(feature = "std", derive(Serialize, Deserialize))] pub struct ModuleError { /// Module index, matching the metadata module index. @@ -494,7 +494,7 @@ impl PartialEq for ModuleError { } /// Errors related to transactional storage layers. -#[derive(Eq, PartialEq, Clone, Copy, Encode, Decode, Debug, TypeInfo)] +#[derive(Eq, PartialEq, Clone, Copy, Encode, Decode, Debug, TypeInfo, MaxEncodedLen)] #[cfg_attr(feature = "std", derive(Serialize, Deserialize))] pub enum TransactionalError { /// Too many transactional layers have been spawned. @@ -519,7 +519,7 @@ impl From for DispatchError { } /// Reason why a dispatch call failed. -#[derive(Eq, Clone, Copy, Encode, Decode, Debug, TypeInfo, PartialEq)] +#[derive(Eq, Clone, Copy, Encode, Decode, Debug, TypeInfo, PartialEq, MaxEncodedLen)] #[cfg_attr(feature = "std", derive(Serialize, Deserialize))] pub enum DispatchError { /// Some error occurred. @@ -602,7 +602,7 @@ impl From for DispatchError { } /// Description of what went wrong when trying to complete an operation on a token. -#[derive(Eq, PartialEq, Clone, Copy, Encode, Decode, Debug, TypeInfo)] +#[derive(Eq, PartialEq, Clone, Copy, Encode, Decode, Debug, TypeInfo, MaxEncodedLen)] #[cfg_attr(feature = "std", derive(Serialize, Deserialize))] pub enum TokenError { /// Funds are unavailable. diff --git a/primitives/session/Cargo.toml b/primitives/session/Cargo.toml index 94f9e8a23..31ec009ab 100644 --- a/primitives/session/Cargo.toml +++ b/primitives/session/Cargo.toml @@ -13,7 +13,7 @@ readme = "README.md" targets = ["x86_64-unknown-linux-gnu"] [dependencies] -codec = { package = "parity-scale-codec", version = "3.0.0", default-features = false, features = ["derive"] } +codec = { package = "parity-scale-codec", version = "3.2.2", default-features = false, features = ["derive"] } scale-info = { version = "2.1.1", default-features = false, features = ["derive"] } sp-api = { version = "4.0.0-dev", default-features = false, path = "../api" } sp-core = { version = "7.0.0", default-features = false, path = "../core" } diff --git a/primitives/staking/Cargo.toml b/primitives/staking/Cargo.toml index 35feae43e..a8e5a543d 100644 --- a/primitives/staking/Cargo.toml +++ b/primitives/staking/Cargo.toml @@ -13,7 +13,7 @@ readme = "README.md" targets = ["x86_64-unknown-linux-gnu"] [dependencies] -codec = { package = "parity-scale-codec", version = "3.0.0", default-features = false, features = ["derive"] } +codec = { package = "parity-scale-codec", version = "3.2.2", default-features = false, features = ["derive"] } scale-info = { version = "2.1.1", default-features = false, features = ["derive"] } sp-core = { version = "7.0.0", default-features = false, path = "../core" } sp-runtime = { version = "7.0.0", default-features = false, path = "../runtime" } diff --git a/primitives/state-machine/Cargo.toml b/primitives/state-machine/Cargo.toml index 06419c592..595dfe286 100644 --- a/primitives/state-machine/Cargo.toml +++ b/primitives/state-machine/Cargo.toml @@ -14,7 +14,7 @@ readme = "README.md" targets = ["x86_64-unknown-linux-gnu"] [dependencies] -codec = { package = "parity-scale-codec", version = "3.0.0", default-features = false } +codec = { package = "parity-scale-codec", version = "3.2.2", default-features = false } hash-db = { version = "0.15.2", default-features = false } log = { version = "0.4.17", optional = true } parking_lot = { version = "0.12.1", optional = true } diff --git a/primitives/storage/Cargo.toml b/primitives/storage/Cargo.toml index eb166ee37..72ebc40fc 100644 --- a/primitives/storage/Cargo.toml +++ b/primitives/storage/Cargo.toml @@ -14,7 +14,7 @@ readme = "README.md" targets = ["x86_64-unknown-linux-gnu"] [dependencies] -codec = { package = "parity-scale-codec", version = "3.0.0", default-features = false, features = ["derive"] } +codec = { package = "parity-scale-codec", version = "3.2.2", default-features = false, features = ["derive"] } impl-serde = { version = "0.4.0", optional = true } ref-cast = "1.0.0" serde = { version = "1.0.136", features = ["derive"], optional = true } diff --git a/primitives/test-primitives/Cargo.toml b/primitives/test-primitives/Cargo.toml index 1d12ed74e..deb120104 100644 --- a/primitives/test-primitives/Cargo.toml +++ b/primitives/test-primitives/Cargo.toml @@ -12,7 +12,7 @@ publish = false targets = ["x86_64-unknown-linux-gnu"] [dependencies] -codec = { package = "parity-scale-codec", version = "3.0.0", default-features = false, features = ["derive"] } +codec = { package = "parity-scale-codec", version = "3.2.2", default-features = false, features = ["derive"] } serde = { version = "1.0.136", features = ["derive"], optional = true } sp-application-crypto = { version = "7.0.0", default-features = false, path = "../application-crypto" } sp-core = { version = "7.0.0", default-features = false, path = "../core" } diff --git a/primitives/timestamp/Cargo.toml b/primitives/timestamp/Cargo.toml index 5d015086c..27e306040 100644 --- a/primitives/timestamp/Cargo.toml +++ b/primitives/timestamp/Cargo.toml @@ -14,7 +14,7 @@ targets = ["x86_64-unknown-linux-gnu"] [dependencies] async-trait = { version = "0.1.57", optional = true } -codec = { package = "parity-scale-codec", version = "3.0.0", default-features = false, features = ["derive"] } +codec = { package = "parity-scale-codec", version = "3.2.2", default-features = false, features = ["derive"] } futures-timer = { version = "3.0.2", optional = true } log = { version = "0.4.17", optional = true } thiserror = { version = "1.0.30", optional = true } diff --git a/primitives/tracing/Cargo.toml b/primitives/tracing/Cargo.toml index 794785085..602f83c7b 100644 --- a/primitives/tracing/Cargo.toml +++ b/primitives/tracing/Cargo.toml @@ -19,7 +19,7 @@ targets = ["x86_64-unknown-linux-gnu", "wasm32-unknown-unknown"] [dependencies] sp-std = { version = "5.0.0", path = "../std", default-features = false } -codec = { version = "3.0.0", package = "parity-scale-codec", default-features = false, features = [ +codec = { version = "3.2.2", package = "parity-scale-codec", default-features = false, features = [ "derive", ] } tracing = { version = "0.1.29", default-features = false } diff --git a/primitives/transaction-storage-proof/Cargo.toml b/primitives/transaction-storage-proof/Cargo.toml index ea6419dfa..565d3d8d2 100644 --- a/primitives/transaction-storage-proof/Cargo.toml +++ b/primitives/transaction-storage-proof/Cargo.toml @@ -14,7 +14,7 @@ targets = ["x86_64-unknown-linux-gnu"] [dependencies] async-trait = { version = "0.1.57", optional = true } -codec = { package = "parity-scale-codec", version = "3.0.0", default-features = false, features = ["derive"] } +codec = { package = "parity-scale-codec", version = "3.2.2", default-features = false, features = ["derive"] } log = { version = "0.4.17", optional = true } scale-info = { version = "2.1.1", default-features = false, features = ["derive"] } sp-core = { version = "7.0.0", optional = true, path = "../core" } diff --git a/primitives/trie/Cargo.toml b/primitives/trie/Cargo.toml index 68a5fb17c..3f045a1cb 100644 --- a/primitives/trie/Cargo.toml +++ b/primitives/trie/Cargo.toml @@ -19,7 +19,7 @@ harness = false [dependencies] ahash = { version = "0.7.6", optional = true } -codec = { package = "parity-scale-codec", version = "3.0.0", default-features = false } +codec = { package = "parity-scale-codec", version = "3.2.2", default-features = false } hashbrown = { version = "0.12.3", optional = true } hash-db = { version = "0.15.2", default-features = false } lazy_static = { version = "1.4.0", optional = true } diff --git a/primitives/version/Cargo.toml b/primitives/version/Cargo.toml index 56fabcd56..7d32c540f 100644 --- a/primitives/version/Cargo.toml +++ b/primitives/version/Cargo.toml @@ -14,7 +14,7 @@ readme = "README.md" targets = ["x86_64-unknown-linux-gnu"] [dependencies] -codec = { package = "parity-scale-codec", version = "3.0.0", default-features = false, features = ["derive"] } +codec = { package = "parity-scale-codec", version = "3.2.2", default-features = false, features = ["derive"] } impl-serde = { version = "0.4.0", optional = true } parity-wasm = { version = "0.45", optional = true } scale-info = { version = "2.1.1", default-features = false, features = ["derive"] } diff --git a/primitives/version/proc-macro/Cargo.toml b/primitives/version/proc-macro/Cargo.toml index 3d129d475..abe9e579a 100644 --- a/primitives/version/proc-macro/Cargo.toml +++ b/primitives/version/proc-macro/Cargo.toml @@ -16,7 +16,7 @@ targets = ["x86_64-unknown-linux-gnu"] proc-macro = true [dependencies] -codec = { package = "parity-scale-codec", version = "3.0.0", features = [ "derive" ] } +codec = { package = "parity-scale-codec", version = "3.2.2", features = [ "derive" ] } proc-macro2 = "1.0.37" quote = "1.0.10" syn = { version = "1.0.98", features = ["full", "fold", "extra-traits", "visit"] } diff --git a/primitives/wasm-interface/Cargo.toml b/primitives/wasm-interface/Cargo.toml index 1822ae43e..69937e6b0 100644 --- a/primitives/wasm-interface/Cargo.toml +++ b/primitives/wasm-interface/Cargo.toml @@ -14,7 +14,7 @@ readme = "README.md" targets = ["x86_64-unknown-linux-gnu"] [dependencies] -codec = { package = "parity-scale-codec", version = "3.0.0", default-features = false, features = ["derive"] } +codec = { package = "parity-scale-codec", version = "3.2.2", default-features = false, features = ["derive"] } impl-trait-for-tuples = "0.2.2" log = { version = "0.4.17", optional = true } wasmi = { version = "0.13", optional = true } diff --git a/primitives/weights/Cargo.toml b/primitives/weights/Cargo.toml index 00da83069..2368b913b 100644 --- a/primitives/weights/Cargo.toml +++ b/primitives/weights/Cargo.toml @@ -13,7 +13,7 @@ documentation = "https://docs.rs/sp-wasm-interface" targets = ["x86_64-unknown-linux-gnu"] [dependencies] -codec = { package = "parity-scale-codec", version = "3.0.0", default-features = false, features = ["derive"] } +codec = { package = "parity-scale-codec", version = "3.2.2", default-features = false, features = ["derive"] } scale-info = { version = "2.1.1", default-features = false, features = ["derive"] } serde = { version = "1.0.136", optional = true, features = ["derive"] } smallvec = "1.8.0" diff --git a/test-utils/client/Cargo.toml b/test-utils/client/Cargo.toml index 106ec21d7..682c868f2 100644 --- a/test-utils/client/Cargo.toml +++ b/test-utils/client/Cargo.toml @@ -15,7 +15,7 @@ targets = ["x86_64-unknown-linux-gnu"] [dependencies] array-bytes = "4.1" async-trait = "0.1.57" -codec = { package = "parity-scale-codec", version = "3.0.0" } +codec = { package = "parity-scale-codec", version = "3.2.2" } futures = "0.3.21" serde = "1.0.136" serde_json = "1.0.85" diff --git a/test-utils/runtime/Cargo.toml b/test-utils/runtime/Cargo.toml index cff0227d0..2c943c47f 100644 --- a/test-utils/runtime/Cargo.toml +++ b/test-utils/runtime/Cargo.toml @@ -19,7 +19,7 @@ sp-application-crypto = { version = "7.0.0", default-features = false, path = ". sp-consensus-aura = { version = "0.10.0-dev", default-features = false, path = "../../primitives/consensus/aura" } sp-consensus-babe = { version = "0.10.0-dev", default-features = false, path = "../../primitives/consensus/babe" } sp-block-builder = { version = "4.0.0-dev", default-features = false, path = "../../primitives/block-builder" } -codec = { package = "parity-scale-codec", version = "3.0.0", default-features = false, features = ["derive"] } +codec = { package = "parity-scale-codec", version = "3.2.2", default-features = false, features = ["derive"] } scale-info = { version = "2.1.1", default-features = false, features = ["derive"] } sp-inherents = { version = "4.0.0-dev", default-features = false, path = "../../primitives/inherents" } sp-keyring = { version = "7.0.0", optional = true, path = "../../primitives/keyring" } diff --git a/test-utils/runtime/client/Cargo.toml b/test-utils/runtime/client/Cargo.toml index 2ac944edc..7d5c9673f 100644 --- a/test-utils/runtime/client/Cargo.toml +++ b/test-utils/runtime/client/Cargo.toml @@ -12,7 +12,7 @@ publish = false targets = ["x86_64-unknown-linux-gnu"] [dependencies] -codec = { package = "parity-scale-codec", version = "3.0.0" } +codec = { package = "parity-scale-codec", version = "3.2.2" } futures = "0.3.21" sc-block-builder = { version = "0.10.0-dev", path = "../../../client/block-builder" } sc-client-api = { version = "4.0.0-dev", path = "../../../client/api" } diff --git a/test-utils/runtime/transaction-pool/Cargo.toml b/test-utils/runtime/transaction-pool/Cargo.toml index f5cba2b99..5ce397474 100644 --- a/test-utils/runtime/transaction-pool/Cargo.toml +++ b/test-utils/runtime/transaction-pool/Cargo.toml @@ -12,7 +12,7 @@ publish = false targets = ["x86_64-unknown-linux-gnu"] [dependencies] -codec = { package = "parity-scale-codec", version = "3.0.0" } +codec = { package = "parity-scale-codec", version = "3.2.2" } futures = "0.3.21" parking_lot = "0.12.1" thiserror = "1.0" diff --git a/utils/fork-tree/Cargo.toml b/utils/fork-tree/Cargo.toml index 4ac176f64..c60ef8fd3 100644 --- a/utils/fork-tree/Cargo.toml +++ b/utils/fork-tree/Cargo.toml @@ -14,4 +14,4 @@ readme = "README.md" targets = ["x86_64-unknown-linux-gnu"] [dependencies] -codec = { package = "parity-scale-codec", version = "3.0.0", features = ["derive"] } +codec = { package = "parity-scale-codec", version = "3.2.2", features = ["derive"] } diff --git a/utils/frame/benchmarking-cli/Cargo.toml b/utils/frame/benchmarking-cli/Cargo.toml index 9e231b817..fc23d07b6 100644 --- a/utils/frame/benchmarking-cli/Cargo.toml +++ b/utils/frame/benchmarking-cli/Cargo.toml @@ -16,7 +16,7 @@ targets = ["x86_64-unknown-linux-gnu"] array-bytes = "4.1" chrono = "0.4" clap = { version = "4.0.9", features = ["derive"] } -codec = { package = "parity-scale-codec", version = "3.0.0" } +codec = { package = "parity-scale-codec", version = "3.2.2" } comfy-table = { version = "6.0.0", default-features = false } handlebars = "4.2.2" Inflector = "0.11.4" diff --git a/utils/frame/remote-externalities/Cargo.toml b/utils/frame/remote-externalities/Cargo.toml index 5a51dbc66..8611ae498 100644 --- a/utils/frame/remote-externalities/Cargo.toml +++ b/utils/frame/remote-externalities/Cargo.toml @@ -12,7 +12,7 @@ description = "An externalities provided environment that can load itself from r targets = ["x86_64-unknown-linux-gnu"] [dependencies] -codec = { package = "parity-scale-codec", version = "3.0.0" } +codec = { package = "parity-scale-codec", version = "3.2.2" } log = "0.4.17" serde = "1.0.136" frame-support = { version = "4.0.0-dev", optional = true, path = "../../../frame/support" } diff --git a/utils/frame/rpc/state-trie-migration-rpc/Cargo.toml b/utils/frame/rpc/state-trie-migration-rpc/Cargo.toml index dd6456f71..17a68e2f4 100644 --- a/utils/frame/rpc/state-trie-migration-rpc/Cargo.toml +++ b/utils/frame/rpc/state-trie-migration-rpc/Cargo.toml @@ -14,7 +14,7 @@ targets = ["x86_64-unknown-linux-gnu"] [dependencies] scale-info = { version = "2.1.1", default-features = false, features = ["derive"] } -codec = { package = "parity-scale-codec", version = "3.0.0", default-features = false } +codec = { package = "parity-scale-codec", version = "3.2.2", default-features = false } serde = { version = "1", features = ["derive"] } log = { version = "0.4.17", default-features = false } diff --git a/utils/frame/rpc/support/Cargo.toml b/utils/frame/rpc/support/Cargo.toml index 066038469..d75d3a5af 100644 --- a/utils/frame/rpc/support/Cargo.toml +++ b/utils/frame/rpc/support/Cargo.toml @@ -15,7 +15,7 @@ description = "Substrate RPC for FRAME's support" targets = ["x86_64-unknown-linux-gnu"] [dependencies] -codec = { package = "parity-scale-codec", version = "3.0.0" } +codec = { package = "parity-scale-codec", version = "3.2.2" } jsonrpsee = { version = "0.16.2", features = ["jsonrpsee-types"] } serde = "1" frame-support = { version = "4.0.0-dev", path = "../../../../frame/support" } diff --git a/utils/frame/rpc/system/Cargo.toml b/utils/frame/rpc/system/Cargo.toml index 55fc4b27d..b6848ceb2 100644 --- a/utils/frame/rpc/system/Cargo.toml +++ b/utils/frame/rpc/system/Cargo.toml @@ -13,7 +13,7 @@ readme = "README.md" targets = ["x86_64-unknown-linux-gnu"] [dependencies] -codec = { package = "parity-scale-codec", version = "3.0.0" } +codec = { package = "parity-scale-codec", version = "3.2.2" } jsonrpsee = { version = "0.16.2", features = ["client-core", "server", "macros"] } futures = "0.3.21" log = "0.4.17" diff --git a/utils/frame/try-runtime/cli/Cargo.toml b/utils/frame/try-runtime/cli/Cargo.toml index ed0f25cae..8c14ddc29 100644 --- a/utils/frame/try-runtime/cli/Cargo.toml +++ b/utils/frame/try-runtime/cli/Cargo.toml @@ -33,7 +33,7 @@ substrate-rpc-client = { path = "../../rpc/client" } clap = { version = "4.0.9", features = ["derive"] } hex = { version = "0.4.3", default-features = false } log = "0.4.17" -parity-scale-codec = "3.0.0" +parity-scale-codec = "3.2.2" serde = "1.0.136" serde_json = "1.0.85" zstd = { version = "0.11.2", default-features = false } From b85d631d49b93d446074b364eb0e036c5ff12e22 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alexander=20Thei=C3=9Fen?= Date: Thu, 19 Jan 2023 17:56:35 -0300 Subject: [PATCH 029/558] Remove fixtures from crate (#13181) --- frame/contracts/Cargo.toml | 1 + 1 file changed, 1 insertion(+) diff --git a/frame/contracts/Cargo.toml b/frame/contracts/Cargo.toml index c238bdd14..0ee9e36f1 100644 --- a/frame/contracts/Cargo.toml +++ b/frame/contracts/Cargo.toml @@ -8,6 +8,7 @@ homepage = "https://substrate.io" repository = "https://github.com/paritytech/substrate/" description = "FRAME pallet for WASM contracts" readme = "README.md" +include = ["src/**/*", "README.md", "CHANGELOG.md"] [package.metadata.docs.rs] targets = ["x86_64-unknown-linux-gnu"] From d7acf9effdbb3a869fb3af75ce4adbb86c29918a Mon Sep 17 00:00:00 2001 From: Michal Kucharczyk <1728078+michalkucharczyk@users.noreply.github.com> Date: Thu, 19 Jan 2023 23:21:41 +0100 Subject: [PATCH 030/558] txpool: LOG_TARGET const added (#13180) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * txpool: LOG_TARGET const added part of: #12873 * LOG_TARGET added to tests mod * txpool::api for api * Apply suggestions from code review Co-authored-by: Bastian Köcher * ".git/.scripts/commands/fmt/fmt.sh" Co-authored-by: Bastian Köcher Co-authored-by: command-bot <> --- client/transaction-pool/api/src/lib.rs | 6 ++-- client/transaction-pool/src/api.rs | 3 +- .../transaction-pool/src/enactment_state.rs | 27 +++++++++----- .../transaction-pool/src/graph/base_pool.rs | 14 +++++--- client/transaction-pool/src/graph/listener.rs | 20 +++++++---- client/transaction-pool/src/graph/pool.rs | 15 +++++--- client/transaction-pool/src/graph/ready.rs | 10 +++--- .../src/graph/validated_pool.rs | 27 ++++++++------ client/transaction-pool/src/lib.rs | 26 +++++++------- client/transaction-pool/src/revalidation.rs | 32 ++++++++++++----- client/transaction-pool/tests/pool.rs | 36 ++++++++++--------- 11 files changed, 135 insertions(+), 81 deletions(-) diff --git a/client/transaction-pool/api/src/lib.rs b/client/transaction-pool/api/src/lib.rs index c1e49ad07..870760962 100644 --- a/client/transaction-pool/api/src/lib.rs +++ b/client/transaction-pool/api/src/lib.rs @@ -30,6 +30,8 @@ use sp_runtime::{ }; use std::{collections::HashMap, hash::Hash, pin::Pin, sync::Arc}; +const LOG_TARGET: &str = "txpool::api"; + pub use sp_runtime::transaction_validity::{ TransactionLongevity, TransactionPriority, TransactionSource, TransactionTag, }; @@ -353,7 +355,7 @@ impl OffchainSubmitTransaction for TP extrinsic: ::Extrinsic, ) -> Result<(), ()> { log::debug!( - target: "txpool", + target: LOG_TARGET, "(offchain call) Submitting a transaction to the pool: {:?}", extrinsic ); @@ -362,7 +364,7 @@ impl OffchainSubmitTransaction for TP result.map(|_| ()).map_err(|e| { log::warn!( - target: "txpool", + target: LOG_TARGET, "(offchain call) Error submitting a transaction to the pool: {}", e ) diff --git a/client/transaction-pool/src/api.rs b/client/transaction-pool/src/api.rs index a2b317cfb..0166dface 100644 --- a/client/transaction-pool/src/api.rs +++ b/client/transaction-pool/src/api.rs @@ -18,6 +18,7 @@ //! Chain api required for the transaction pool. +use crate::LOG_TARGET; use codec::Encode; use futures::{ channel::{mpsc, oneshot}, @@ -85,7 +86,7 @@ impl FullChainApi { let metrics = prometheus.map(ApiMetrics::register).and_then(|r| match r { Err(err) => { log::warn!( - target: "txpool", + target: LOG_TARGET, "Failed to register transaction pool api prometheus metrics: {:?}", err, ); diff --git a/client/transaction-pool/src/enactment_state.rs b/client/transaction-pool/src/enactment_state.rs index 382b26831..7252c0dec 100644 --- a/client/transaction-pool/src/enactment_state.rs +++ b/client/transaction-pool/src/enactment_state.rs @@ -18,6 +18,7 @@ //! Substrate transaction pool implementation. +use crate::LOG_TARGET; use num_traits::CheckedSub; use sc_transaction_pool_api::ChainEvent; use sp_blockchain::TreeRoute; @@ -113,14 +114,14 @@ where }; if skip_maintenance { - log::debug!(target: "txpool", "skip maintain: tree_route would be too long"); + log::debug!(target: LOG_TARGET, "skip maintain: tree_route would be too long"); self.force_update(event); return Ok(EnactmentAction::Skip) } // block was already finalized if self.recent_finalized_block == new_hash { - log::debug!(target: "txpool", "handle_enactment: block already finalized"); + log::debug!(target: LOG_TARGET, "handle_enactment: block already finalized"); return Ok(EnactmentAction::Skip) } @@ -129,9 +130,13 @@ where let tree_route = tree_route(self.recent_best_block, new_hash)?; log::debug!( - target: "txpool", + target: LOG_TARGET, "resolve hash:{:?} finalized:{:?} tree_route:{:?} best_block:{:?} finalized_block:{:?}", - new_hash, finalized, tree_route, self.recent_best_block, self.recent_finalized_block + new_hash, + finalized, + tree_route, + self.recent_best_block, + self.recent_finalized_block ); // check if recently finalized block is on retracted path. this could be @@ -139,9 +144,10 @@ where // best event for some old stale best head. if tree_route.retracted().iter().any(|x| x.hash == self.recent_finalized_block) { log::debug!( - target: "txpool", + target: LOG_TARGET, "Recently finalized block {} would be retracted by ChainEvent {}, skipping", - self.recent_finalized_block, new_hash + self.recent_finalized_block, + new_hash ); return Ok(EnactmentAction::Skip) } @@ -155,7 +161,7 @@ where // remains valid. if tree_route.enacted().is_empty() { log::trace!( - target: "txpool", + target: LOG_TARGET, "handle_enactment: no newly enacted blocks since recent best block" ); return Ok(EnactmentAction::HandleFinalization) @@ -176,7 +182,12 @@ where ChainEvent::NewBestBlock { hash, .. } => self.recent_best_block = *hash, ChainEvent::Finalized { hash, .. } => self.recent_finalized_block = *hash, }; - log::debug!(target: "txpool", "forced update: {:?}, {:?}", self.recent_best_block, self.recent_finalized_block); + log::debug!( + target: LOG_TARGET, + "forced update: {:?}, {:?}", + self.recent_best_block, + self.recent_finalized_block, + ); } } diff --git a/client/transaction-pool/src/graph/base_pool.rs b/client/transaction-pool/src/graph/base_pool.rs index 67580d698..44371be62 100644 --- a/client/transaction-pool/src/graph/base_pool.rs +++ b/client/transaction-pool/src/graph/base_pool.rs @@ -22,6 +22,7 @@ use std::{cmp::Ordering, collections::HashSet, fmt, hash, sync::Arc}; +use crate::LOG_TARGET; use log::{debug, trace, warn}; use sc_transaction_pool_api::{error, InPoolTransaction, PoolStatus}; use serde::Serialize; @@ -272,9 +273,9 @@ impl BasePool BasePool if first { - debug!(target: "txpool", "[{:?}] Error importing: {:?}", current_hash, e); + debug!(target: LOG_TARGET, "[{:?}] Error importing: {:?}", current_hash, e); return Err(e) } else { failed.push(current_hash); @@ -347,7 +348,7 @@ impl BasePool BasePool promoted.push(res), Err(e) => { - warn!(target: "txpool", "[{:?}] Failed to promote during pruning: {:?}", hash, e); + warn!( + target: LOG_TARGET, + "[{:?}] Failed to promote during pruning: {:?}", hash, e, + ); failed.push(hash) }, } diff --git a/client/transaction-pool/src/graph/listener.rs b/client/transaction-pool/src/graph/listener.rs index 776749abf..c603a542c 100644 --- a/client/transaction-pool/src/graph/listener.rs +++ b/client/transaction-pool/src/graph/listener.rs @@ -18,6 +18,7 @@ use std::{collections::HashMap, fmt::Debug, hash}; +use crate::LOG_TARGET; use linked_hash_map::LinkedHashMap; use log::{debug, trace}; use serde::Serialize; @@ -67,13 +68,13 @@ impl Listener { /// Notify the listeners about extrinsic broadcast. pub fn broadcasted(&mut self, hash: &H, peers: Vec) { - trace!(target: "txpool", "[{:?}] Broadcasted", hash); + trace!(target: LOG_TARGET, "[{:?}] Broadcasted", hash); self.fire(hash, |watcher| watcher.broadcast(peers)); } /// New transaction was added to the ready pool or promoted from the future pool. pub fn ready(&mut self, tx: &H, old: Option<&H>) { - trace!(target: "txpool", "[{:?}] Ready (replaced with {:?})", tx, old); + trace!(target: LOG_TARGET, "[{:?}] Ready (replaced with {:?})", tx, old); self.fire(tx, |watcher| watcher.ready()); if let Some(old) = old { self.fire(old, |watcher| watcher.usurped(tx.clone())); @@ -82,13 +83,13 @@ impl Listener { /// New transaction was added to the future pool. pub fn future(&mut self, tx: &H) { - trace!(target: "txpool", "[{:?}] Future", tx); + trace!(target: LOG_TARGET, "[{:?}] Future", tx); self.fire(tx, |watcher| watcher.future()); } /// Transaction was dropped from the pool because of the limit. pub fn dropped(&mut self, tx: &H, by: Option<&H>) { - trace!(target: "txpool", "[{:?}] Dropped (replaced with {:?})", tx, by); + trace!(target: LOG_TARGET, "[{:?}] Dropped (replaced with {:?})", tx, by); self.fire(tx, |watcher| match by { Some(t) => watcher.usurped(t.clone()), None => watcher.dropped(), @@ -97,13 +98,13 @@ impl Listener { /// Transaction was removed as invalid. pub fn invalid(&mut self, tx: &H) { - debug!(target: "txpool", "[{:?}] Extrinsic invalid", tx); + debug!(target: LOG_TARGET, "[{:?}] Extrinsic invalid", tx); self.fire(tx, |watcher| watcher.invalid()); } /// Transaction was pruned from the pool. pub fn pruned(&mut self, block_hash: BlockHash, tx: &H) { - debug!(target: "txpool", "[{:?}] Pruned at {:?}", tx, block_hash); + debug!(target: LOG_TARGET, "[{:?}] Pruned at {:?}", tx, block_hash); // Get the transactions included in the given block hash. let txs = self.finality_watchers.entry(block_hash).or_insert(vec![]); txs.push(tx.clone()); @@ -134,7 +135,12 @@ impl Listener { pub fn finalized(&mut self, block_hash: BlockHash) { if let Some(hashes) = self.finality_watchers.remove(&block_hash) { for (tx_index, hash) in hashes.into_iter().enumerate() { - log::debug!(target: "txpool", "[{:?}] Sent finalization event (block {:?})", hash, block_hash); + log::debug!( + target: LOG_TARGET, + "[{:?}] Sent finalization event (block {:?})", + hash, + block_hash, + ); self.fire(&hash, |watcher| watcher.finalized(block_hash, tx_index)) } } diff --git a/client/transaction-pool/src/graph/pool.rs b/client/transaction-pool/src/graph/pool.rs index 8e3570d1d..14217d56a 100644 --- a/client/transaction-pool/src/graph/pool.rs +++ b/client/transaction-pool/src/graph/pool.rs @@ -18,6 +18,7 @@ use std::{collections::HashMap, sync::Arc, time::Duration}; +use crate::LOG_TARGET; use futures::{channel::mpsc::Receiver, Future}; use sc_transaction_pool_api::error; use sp_blockchain::TreeRoute; @@ -208,7 +209,8 @@ impl Pool { ) { let now = Instant::now(); self.validated_pool.resubmit(revalidated_transactions); - log::debug!(target: "txpool", + log::debug!( + target: LOG_TARGET, "Resubmitted. Took {} ms. Status: {:?}", now.elapsed().as_millis(), self.validated_pool.status() @@ -249,7 +251,7 @@ impl Pool { extrinsics: &[ExtrinsicFor], ) -> Result<(), B::Error> { log::debug!( - target: "txpool", + target: LOG_TARGET, "Starting pruning of block {:?} (extrinsics: {})", at, extrinsics.len() @@ -287,7 +289,10 @@ impl Pool { future_tags.extend(validity.provides); } } else { - log::trace!(target: "txpool", "txpool is empty, skipping validation for block {at:?}"); + log::trace!( + target: LOG_TARGET, + "txpool is empty, skipping validation for block {at:?}", + ); } }, } @@ -323,7 +328,7 @@ impl Pool { tags: impl IntoIterator, known_imported_hashes: impl IntoIterator> + Clone, ) -> Result<(), B::Error> { - log::debug!(target: "txpool", "Pruning at {:?}", at); + log::debug!(target: LOG_TARGET, "Pruning at {:?}", at); // Prune all transactions that provide given tags let prune_status = self.validated_pool.prune_tags(tags)?; @@ -342,7 +347,7 @@ impl Pool { let reverified_transactions = self.verify(at, pruned_transactions, CheckBannedBeforeVerify::Yes).await?; - log::trace!(target: "txpool", "Pruning at {:?}. Resubmitting transactions.", at); + log::trace!(target: LOG_TARGET, "Pruning at {:?}. Resubmitting transactions.", at); // And finally - submit reverified transactions back to the pool self.validated_pool.resubmit_pruned( diff --git a/client/transaction-pool/src/graph/ready.rs b/client/transaction-pool/src/graph/ready.rs index b52372a3c..9040c81b3 100644 --- a/client/transaction-pool/src/graph/ready.rs +++ b/client/transaction-pool/src/graph/ready.rs @@ -23,6 +23,7 @@ use std::{ sync::Arc, }; +use crate::LOG_TARGET; use log::{debug, trace}; use sc_transaction_pool_api::error; use serde::Serialize; @@ -314,7 +315,7 @@ impl ReadyTransactions { } // add to removed - trace!(target: "txpool", "[{:?}] Removed as part of the subtree.", hash); + trace!(target: LOG_TARGET, "[{:?}] Removed as part of the subtree.", hash); removed.push(tx.transaction.transaction); } } @@ -521,7 +522,7 @@ impl BestIterator { pub fn report_invalid(&mut self, tx: &Arc>) { if let Some(to_report) = self.all.get(&tx.hash) { debug!( - target: "txpool", + target: LOG_TARGET, "[{:?}] Reported as invalid. Will skip sub-chains while iterating.", to_report.transaction.transaction.hash ); @@ -544,9 +545,8 @@ impl Iterator for BestIterator { // Check if the transaction was marked invalid. if self.invalid.contains(hash) { debug!( - target: "txpool", - "[{:?}] Skipping invalid child transaction while iterating.", - hash + target: LOG_TARGET, + "[{:?}] Skipping invalid child transaction while iterating.", hash, ); continue } diff --git a/client/transaction-pool/src/graph/validated_pool.rs b/client/transaction-pool/src/graph/validated_pool.rs index ab99a090e..bf8a059af 100644 --- a/client/transaction-pool/src/graph/validated_pool.rs +++ b/client/transaction-pool/src/graph/validated_pool.rs @@ -22,6 +22,7 @@ use std::{ sync::Arc, }; +use crate::LOG_TARGET; use futures::channel::mpsc::{channel, Sender}; use parking_lot::{Mutex, RwLock}; use sc_transaction_pool_api::{error, PoolStatus, ReadyTransactions}; @@ -199,7 +200,7 @@ impl ValidatedPool { Err(e) => if e.is_full() { log::warn!( - target: "txpool", + target: LOG_TARGET, "[{:?}] Trying to notify an import but the channel is full", hash, ); @@ -230,15 +231,17 @@ impl ValidatedPool { let ready_limit = &self.options.ready; let future_limit = &self.options.future; - log::debug!(target: "txpool", "Pool Status: {:?}", status); + log::debug!(target: LOG_TARGET, "Pool Status: {:?}", status); if ready_limit.is_exceeded(status.ready, status.ready_bytes) || future_limit.is_exceeded(status.future, status.future_bytes) { log::debug!( - target: "txpool", + target: LOG_TARGET, "Enforcing limits ({}/{}kB ready, {}/{}kB future", - ready_limit.count, ready_limit.total_bytes / 1024, - future_limit.count, future_limit.total_bytes / 1024, + ready_limit.count, + ready_limit.total_bytes / 1024, + future_limit.count, + future_limit.total_bytes / 1024, ); // clean up the pool @@ -254,7 +257,7 @@ impl ValidatedPool { removed }; if !removed.is_empty() { - log::debug!(target: "txpool", "Enforcing limits: {} dropped", removed.len()); + log::debug!(target: LOG_TARGET, "Enforcing limits: {} dropped", removed.len()); } // run notifications @@ -385,7 +388,7 @@ impl ValidatedPool { // unknown to caller => let's just notify listeners (and issue debug // message) log::warn!( - target: "txpool", + target: LOG_TARGET, "[{:?}] Removing invalid transaction from update: {}", hash, err, @@ -595,14 +598,14 @@ impl ValidatedPool { return vec![] } - log::debug!(target: "txpool", "Removing invalid transactions: {:?}", hashes); + log::debug!(target: LOG_TARGET, "Removing invalid transactions: {:?}", hashes); // temporarily ban invalid transactions self.rotator.ban(&Instant::now(), hashes.iter().cloned()); let invalid = self.pool.write().remove_subtree(hashes); - log::debug!(target: "txpool", "Removed invalid transactions: {:?}", invalid); + log::debug!(target: LOG_TARGET, "Removed invalid transactions: {:?}", invalid); let mut listener = self.listener.write(); for tx in &invalid { @@ -629,7 +632,11 @@ impl ValidatedPool { /// Notify all watchers that transactions in the block with hash have been finalized pub async fn on_block_finalized(&self, block_hash: BlockHash) -> Result<(), B::Error> { - log::trace!(target: "txpool", "Attempting to notify watchers of finalization for {}", block_hash); + log::trace!( + target: LOG_TARGET, + "Attempting to notify watchers of finalization for {}", + block_hash, + ); self.listener.write().finalized(block_hash); Ok(()) } diff --git a/client/transaction-pool/src/lib.rs b/client/transaction-pool/src/lib.rs index 1cd9bef77..44bda82da 100644 --- a/client/transaction-pool/src/lib.rs +++ b/client/transaction-pool/src/lib.rs @@ -67,6 +67,8 @@ use prometheus_endpoint::Registry as PrometheusRegistry; use sp_blockchain::{HashAndNumber, TreeRoute}; +pub(crate) const LOG_TARGET: &str = "txpool"; + type BoxedReadyIterator = Box>> + Send>; @@ -116,7 +118,7 @@ impl ReadyPoll { while idx < self.pollers.len() { if self.pollers[idx].0 <= number { let poller_sender = self.pollers.swap_remove(idx); - log::debug!(target: "txpool", "Sending ready signal at block {}", number); + log::debug!(target: LOG_TARGET, "Sending ready signal at block {}", number); let _ = poller_sender.1.send(iterator_factory()); } else { idx += 1; @@ -336,7 +338,7 @@ where } if self.ready_poll.lock().updated_at() >= at { - log::trace!(target: "txpool", "Transaction pool already processed block #{}", at); + log::trace!(target: LOG_TARGET, "Transaction pool already processed block #{}", at); let iterator: ReadyIteratorFor = Box::new(self.pool.validated_pool().ready()); return async move { iterator }.boxed() } @@ -554,16 +556,16 @@ async fn prune_known_txs_for_block>(); - log::trace!(target: "txpool", "Pruning transactions: {:?}", hashes); + log::trace!(target: LOG_TARGET, "Pruning transactions: {:?}", hashes); let header = match api.block_header(block_hash) { Ok(Some(h)) => h, Ok(None) => { - log::debug!(target: "txpool", "Could not find header for {:?}.", block_hash); + log::debug!(target: LOG_TARGET, "Could not find header for {:?}.", block_hash); return hashes }, Err(e) => { - log::debug!(target: "txpool", "Error retrieving header for {:?}: {}", block_hash, e); + log::debug!(target: LOG_TARGET, "Error retrieving header for {:?}: {}", block_hash, e); return hashes }, }; @@ -587,7 +589,7 @@ where /// (that have already been enacted) and resubmits transactions that were /// retracted. async fn handle_enactment(&self, tree_route: TreeRoute) { - log::trace!(target: "txpool", "handle_enactment tree_route: {tree_route:?}"); + log::trace!(target: LOG_TARGET, "handle_enactment tree_route: {tree_route:?}"); let pool = self.pool.clone(); let api = self.api.clone(); @@ -595,7 +597,7 @@ where Some(HashAndNumber { hash, number }) => (hash, number), None => { log::warn!( - target: "txpool", + target: LOG_TARGET, "Skipping ChainEvent - no last block in tree route {:?}", tree_route, ); @@ -666,7 +668,7 @@ where if !contains { log::debug!( - target: "txpool", + target: LOG_TARGET, "[{:?}]: Resubmitting from retracted block {:?}", tx_hash, hash, @@ -691,7 +693,7 @@ where .await { log::debug!( - target: "txpool", + target: LOG_TARGET, "[{:?}] Error re-submitting transactions: {}", hash, e, @@ -742,7 +744,7 @@ where match result { Err(msg) => { - log::debug!(target: "txpool", "{msg}"); + log::debug!(target: LOG_TARGET, "{msg}"); self.enactment_state.lock().force_update(&event); }, Ok(EnactmentAction::Skip) => return, @@ -754,7 +756,7 @@ where if let ChainEvent::Finalized { hash, tree_route } = event { log::trace!( - target: "txpool", + target: LOG_TARGET, "on-finalized enacted: {tree_route:?}, previously finalized: \ {prev_finalized_block:?}", ); @@ -762,7 +764,7 @@ where for hash in tree_route.iter().chain(std::iter::once(&hash)) { if let Err(e) = self.pool.validated_pool().on_block_finalized(*hash).await { log::warn!( - target: "txpool", + target: LOG_TARGET, "Error occurred while attempting to notify watchers about finalization {}: {}", hash, e ) diff --git a/client/transaction-pool/src/revalidation.rs b/client/transaction-pool/src/revalidation.rs index d8c8bea62..99602b50a 100644 --- a/client/transaction-pool/src/revalidation.rs +++ b/client/transaction-pool/src/revalidation.rs @@ -24,7 +24,10 @@ use std::{ sync::Arc, }; -use crate::graph::{ChainApi, ExtrinsicHash, NumberFor, Pool, ValidatedTransaction}; +use crate::{ + graph::{ChainApi, ExtrinsicHash, NumberFor, Pool, ValidatedTransaction}, + LOG_TARGET, +}; use sc_utils::mpsc::{tracing_unbounded, TracingUnboundedReceiver, TracingUnboundedSender}; use sp_runtime::{ generic::BlockId, @@ -82,13 +85,23 @@ async fn batch_revalidate( for (validation_result, ext_hash, ext) in validation_results { match validation_result { Ok(Err(TransactionValidityError::Invalid(err))) => { - log::debug!(target: "txpool", "[{:?}]: Revalidation: invalid {:?}", ext_hash, err); + log::debug!( + target: LOG_TARGET, + "[{:?}]: Revalidation: invalid {:?}", + ext_hash, + err, + ); invalid_hashes.push(ext_hash); }, Ok(Err(TransactionValidityError::Unknown(err))) => { // skipping unknown, they might be pushed by valid or invalid transaction // when latter resubmitted. - log::trace!(target: "txpool", "[{:?}]: Unknown during revalidation: {:?}", ext_hash, err); + log::trace!( + target: LOG_TARGET, + "[{:?}]: Unknown during revalidation: {:?}", + ext_hash, + err, + ); }, Ok(Ok(validity)) => { revalidated.insert( @@ -105,7 +118,7 @@ async fn batch_revalidate( }, Err(validation_err) => { log::debug!( - target: "txpool", + target: LOG_TARGET, "[{:?}]: Removing due to error during revalidation: {}", ext_hash, validation_err @@ -183,7 +196,7 @@ impl RevalidationWorker { // we don't add something that already scheduled for revalidation if self.members.contains_key(&ext_hash) { log::trace!( - target: "txpool", + target: LOG_TARGET, "[{:?}] Skipped adding for revalidation: Already there.", ext_hash, ); @@ -231,7 +244,7 @@ impl RevalidationWorker { if batch_len > 0 || this.len() > 0 { log::debug!( - target: "txpool", + target: LOG_TARGET, "Revalidated {} transactions. Left in the queue for revalidation: {}.", batch_len, this.len(), @@ -248,7 +261,7 @@ impl RevalidationWorker { if this.members.len() > 0 { log::debug!( - target: "txpool", + target: LOG_TARGET, "Updated revalidation queue at {:?}. Transactions: {:?}", this.best_block, this.members, @@ -320,14 +333,15 @@ where ) { if transactions.len() > 0 { log::debug!( - target: "txpool", "Sent {} transactions to revalidation queue", + target: LOG_TARGET, + "Sent {} transactions to revalidation queue", transactions.len(), ); } if let Some(ref to_worker) = self.background { if let Err(e) = to_worker.unbounded_send(WorkerPayload { at, transactions }) { - log::warn!(target: "txpool", "Failed to update background worker: {:?}", e); + log::warn!(target: LOG_TARGET, "Failed to update background worker: {:?}", e); } } else { let pool = self.pool.clone(); diff --git a/client/transaction-pool/tests/pool.rs b/client/transaction-pool/tests/pool.rs index 27d62c325..511f6d9e2 100644 --- a/client/transaction-pool/tests/pool.rs +++ b/client/transaction-pool/tests/pool.rs @@ -45,6 +45,8 @@ use substrate_test_runtime_client::{ }; use substrate_test_runtime_transaction_pool::{uxt, TestApi}; +const LOG_TARGET: &str = "txpool"; + fn pool() -> Pool { Pool::new(Default::default(), true.into(), TestApi::with_alice_nonce(209).into()) } @@ -505,7 +507,7 @@ fn fork_aware_finalization() { canon_watchers.push((watcher, header.hash())); assert_eq!(pool.status().ready, 1); - log::trace!(target:"txpool", ">> B1: {:?} {:?}", header.hash(), header); + log::trace!(target: LOG_TARGET, ">> B1: {:?} {:?}", header.hash(), header); let event = ChainEvent::NewBestBlock { hash: header.hash(), tree_route: None }; b1 = header.hash(); block_on(pool.maintain(event)); @@ -521,7 +523,7 @@ fn fork_aware_finalization() { block_on(pool.submit_and_watch(&BlockId::number(1), SOURCE, from_dave.clone())) .expect("1. Imported"); assert_eq!(pool.status().ready, 1); - log::trace!(target:"txpool", ">> C2: {:?} {:?}", header.hash(), header); + log::trace!(target: LOG_TARGET, ">> C2: {:?} {:?}", header.hash(), header); let event = ChainEvent::NewBestBlock { hash: header.hash(), tree_route: None }; c2 = header.hash(); block_on(pool.maintain(event)); @@ -536,7 +538,7 @@ fn fork_aware_finalization() { assert_eq!(pool.status().ready, 1); let header = pool.api().push_block_with_parent(c2, vec![from_bob.clone()], true); - log::trace!(target:"txpool", ">> D2: {:?} {:?}", header.hash(), header); + log::trace!(target: LOG_TARGET, ">> D2: {:?} {:?}", header.hash(), header); let event = ChainEvent::NewBestBlock { hash: header.hash(), tree_route: None }; d2 = header.hash(); block_on(pool.maintain(event)); @@ -550,7 +552,7 @@ fn fork_aware_finalization() { .expect("1.Imported"); assert_eq!(pool.status().ready, 1); let header = pool.api().push_block_with_parent(b1, vec![from_charlie.clone()], true); - log::trace!(target:"txpool", ">> C1: {:?} {:?}", header.hash(), header); + log::trace!(target: LOG_TARGET, ">> C1: {:?} {:?}", header.hash(), header); c1 = header.hash(); canon_watchers.push((watcher, header.hash())); let event = block_event_with_retracted(header.clone(), d2, pool.api()); @@ -568,7 +570,7 @@ fn fork_aware_finalization() { .expect("1. Imported"); assert_eq!(pool.status().ready, 3); let header = pool.api().push_block_with_parent(c1, vec![xt.clone()], true); - log::trace!(target:"txpool", ">> D1: {:?} {:?}", header.hash(), header); + log::trace!(target: LOG_TARGET, ">> D1: {:?} {:?}", header.hash(), header); d1 = header.hash(); canon_watchers.push((w, header.hash())); @@ -584,7 +586,7 @@ fn fork_aware_finalization() { // block E1 { let header = pool.api().push_block_with_parent(d1, vec![from_dave, from_bob], true); - log::trace!(target:"txpool", ">> E1: {:?} {:?}", header.hash(), header); + log::trace!(target: LOG_TARGET, ">> E1: {:?} {:?}", header.hash(), header); e1 = header.hash(); let event = ChainEvent::NewBestBlock { hash: header.hash(), tree_route: None }; block_on(pool.maintain(event)); @@ -1115,7 +1117,7 @@ fn switching_fork_with_finalized_works() { pool.api() .push_block_with_parent(a_header.hash(), vec![from_alice.clone()], true); assert_eq!(pool.status().ready, 1); - log::trace!(target:"txpool", ">> B1: {:?} {:?}", header.hash(), header); + log::trace!(target: LOG_TARGET, ">> B1: {:?} {:?}", header.hash(), header); b1_header = header; } @@ -1131,7 +1133,7 @@ fn switching_fork_with_finalized_works() { ); assert_eq!(pool.status().ready, 2); - log::trace!(target:"txpool", ">> B2: {:?} {:?}", header.hash(), header); + log::trace!(target: LOG_TARGET, ">> B2: {:?} {:?}", header.hash(), header); b2_header = header; } @@ -1193,7 +1195,7 @@ fn switching_fork_multiple_times_works() { pool.api() .push_block_with_parent(a_header.hash(), vec![from_alice.clone()], true); assert_eq!(pool.status().ready, 1); - log::trace!(target:"txpool", ">> B1: {:?} {:?}", header.hash(), header); + log::trace!(target: LOG_TARGET, ">> B1: {:?} {:?}", header.hash(), header); b1_header = header; } @@ -1209,7 +1211,7 @@ fn switching_fork_multiple_times_works() { ); assert_eq!(pool.status().ready, 2); - log::trace!(target:"txpool", ">> B2: {:?} {:?}", header.hash(), header); + log::trace!(target: LOG_TARGET, ">> B2: {:?} {:?}", header.hash(), header); b2_header = header; } @@ -1306,7 +1308,7 @@ fn two_blocks_delayed_finalization_works() { .push_block_with_parent(a_header.hash(), vec![from_alice.clone()], true); assert_eq!(pool.status().ready, 1); - log::trace!(target:"txpool", ">> B1: {:?} {:?}", header.hash(), header); + log::trace!(target: LOG_TARGET, ">> B1: {:?} {:?}", header.hash(), header); b1_header = header; } @@ -1320,7 +1322,7 @@ fn two_blocks_delayed_finalization_works() { .push_block_with_parent(b1_header.hash(), vec![from_bob.clone()], true); assert_eq!(pool.status().ready, 2); - log::trace!(target:"txpool", ">> C1: {:?} {:?}", header.hash(), header); + log::trace!(target: LOG_TARGET, ">> C1: {:?} {:?}", header.hash(), header); c1_header = header; } @@ -1334,7 +1336,7 @@ fn two_blocks_delayed_finalization_works() { .push_block_with_parent(c1_header.hash(), vec![from_charlie.clone()], true); assert_eq!(pool.status().ready, 3); - log::trace!(target:"txpool", ">> D1: {:?} {:?}", header.hash(), header); + log::trace!(target: LOG_TARGET, ">> D1: {:?} {:?}", header.hash(), header); d1_header = header; } @@ -1418,7 +1420,7 @@ fn delayed_finalization_does_not_retract() { .push_block_with_parent(a_header.hash(), vec![from_alice.clone()], true); assert_eq!(pool.status().ready, 1); - log::trace!(target:"txpool", ">> B1: {:?} {:?}", header.hash(), header); + log::trace!(target: LOG_TARGET, ">> B1: {:?} {:?}", header.hash(), header); b1_header = header; } @@ -1432,7 +1434,7 @@ fn delayed_finalization_does_not_retract() { .push_block_with_parent(b1_header.hash(), vec![from_bob.clone()], true); assert_eq!(pool.status().ready, 2); - log::trace!(target:"txpool", ">> C1: {:?} {:?}", header.hash(), header); + log::trace!(target: LOG_TARGET, ">> C1: {:?} {:?}", header.hash(), header); c1_header = header; } @@ -1513,7 +1515,7 @@ fn best_block_after_finalization_does_not_retract() { .push_block_with_parent(a_header.hash(), vec![from_alice.clone()], true); assert_eq!(pool.status().ready, 1); - log::trace!(target:"txpool", ">> B1: {:?} {:?}", header.hash(), header); + log::trace!(target: LOG_TARGET, ">> B1: {:?} {:?}", header.hash(), header); b1_header = header; } @@ -1527,7 +1529,7 @@ fn best_block_after_finalization_does_not_retract() { .push_block_with_parent(b1_header.hash(), vec![from_bob.clone()], true); assert_eq!(pool.status().ready, 2); - log::trace!(target:"txpool", ">> C1: {:?} {:?}", header.hash(), header); + log::trace!(target: LOG_TARGET, ">> C1: {:?} {:?}", header.hash(), header); c1_header = header; } From de943ee152ccedc0b418caac6061409431f74a49 Mon Sep 17 00:00:00 2001 From: Marcin S Date: Fri, 20 Jan 2023 11:19:30 +0100 Subject: [PATCH 031/558] Fix potential huge allocation as a result of `validate_block` output (#13183) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Fix potential huge allocation as a result of `validate_block` output * Address review comments; add more tests * Update client/executor/wasmtime/src/runtime.rs * Remove unnecessary comments Co-authored-by: Bastian Köcher --- Cargo.lock | 2 + client/executor/Cargo.toml | 1 + client/executor/runtime-test/Cargo.toml | 1 + client/executor/runtime-test/src/lib.rs | 37 +++++++++- client/executor/src/integration_tests/mod.rs | 71 +++++++++++++++++++- client/executor/wasmtime/src/runtime.rs | 17 ++++- 6 files changed, 125 insertions(+), 4 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index dbb046ccd..348f23d73 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -8270,6 +8270,7 @@ name = "sc-executor" version = "0.10.0-dev" dependencies = [ "array-bytes", + "assert_matches", "criterion", "env_logger", "lru", @@ -8842,6 +8843,7 @@ dependencies = [ "sp-core", "sp-io", "sp-runtime", + "sp-runtime-interface", "sp-std", "substrate-wasm-builder", ] diff --git a/client/executor/Cargo.toml b/client/executor/Cargo.toml index 4604b976b..8bedcd3ed 100644 --- a/client/executor/Cargo.toml +++ b/client/executor/Cargo.toml @@ -35,6 +35,7 @@ sp-wasm-interface = { version = "7.0.0", path = "../../primitives/wasm-interface [dev-dependencies] array-bytes = "4.1" +assert_matches = "1.3.0" wat = "1.0" sc-runtime-test = { version = "2.0.0", path = "runtime-test" } substrate-test-runtime = { version = "2.0.0", path = "../../test-utils/runtime" } diff --git a/client/executor/runtime-test/Cargo.toml b/client/executor/runtime-test/Cargo.toml index e99f3caa9..07626c234 100644 --- a/client/executor/runtime-test/Cargo.toml +++ b/client/executor/runtime-test/Cargo.toml @@ -16,6 +16,7 @@ targets = ["x86_64-unknown-linux-gnu"] sp-core = { version = "7.0.0", default-features = false, path = "../../../primitives/core" } sp-io = { version = "7.0.0", default-features = false, features = ["improved_panic_error_reporting"], path = "../../../primitives/io" } sp-runtime = { version = "7.0.0", default-features = false, path = "../../../primitives/runtime" } +sp-runtime-interface = { version = "7.0.0", default-features = false, path = "../../../primitives/runtime-interface" } sp-std = { version = "5.0.0", default-features = false, path = "../../../primitives/std" } [build-dependencies] diff --git a/client/executor/runtime-test/src/lib.rs b/client/executor/runtime-test/src/lib.rs index fc98d1909..0aa30e4bc 100644 --- a/client/executor/runtime-test/src/lib.rs +++ b/client/executor/runtime-test/src/lib.rs @@ -29,6 +29,8 @@ use sp_runtime::{ print, traits::{BlakeTwo256, Hash}, }; +#[cfg(not(feature = "std"))] +use sp_runtime_interface::pack_ptr_and_len; extern "C" { #[allow(dead_code)] @@ -38,6 +40,10 @@ extern "C" { fn yet_another_missing_external(); } +#[cfg(not(feature = "std"))] +/// The size of a WASM page in bytes. +const WASM_PAGE_SIZE: usize = 65536; + #[cfg(not(feature = "std"))] /// Mutable static variables should be always observed to have /// the initialized value at the start of a runtime call. @@ -92,7 +98,7 @@ sp_core::wasm_export_functions! { let heap_ptr = heap_base as usize; // Find the next wasm page boundary. - let heap_ptr = round_up_to(heap_ptr, 65536); + let heap_ptr = round_up_to(heap_ptr, WASM_PAGE_SIZE); // Make it an actual pointer let heap_ptr = heap_ptr as *mut u8; @@ -337,3 +343,32 @@ sp_core::wasm_export_functions! { return 1234; } } + +// Returns a huge len. It should result in an error, and not an allocation. +#[no_mangle] +#[cfg(not(feature = "std"))] +pub extern "C" fn test_return_huge_len(_params: *const u8, _len: usize) -> u64 { + pack_ptr_and_len(0, u32::MAX) +} + +// Returns an offset right at the edge of the wasm memory boundary. With length 0, it should +// succeed. +#[no_mangle] +#[cfg(not(feature = "std"))] +pub extern "C" fn test_return_max_memory_offset(_params: *const u8, _len: usize) -> u64 { + pack_ptr_and_len((core::arch::wasm32::memory_size(0) * WASM_PAGE_SIZE) as u32, 0) +} + +// Returns an offset right at the edge of the wasm memory boundary. With length 1, it should fail. +#[no_mangle] +#[cfg(not(feature = "std"))] +pub extern "C" fn test_return_max_memory_offset_plus_one(_params: *const u8, _len: usize) -> u64 { + pack_ptr_and_len((core::arch::wasm32::memory_size(0) * WASM_PAGE_SIZE) as u32, 1) +} + +// Returns an output that overflows the u32 range. It should result in an error. +#[no_mangle] +#[cfg(not(feature = "std"))] +pub extern "C" fn test_return_overflow(_params: *const u8, _len: usize) -> u64 { + pack_ptr_and_len(u32::MAX, 1) +} diff --git a/client/executor/src/integration_tests/mod.rs b/client/executor/src/integration_tests/mod.rs index 25b999f11..22e651abe 100644 --- a/client/executor/src/integration_tests/mod.rs +++ b/client/executor/src/integration_tests/mod.rs @@ -19,8 +19,13 @@ #[cfg(target_os = "linux")] mod linux; +use assert_matches::assert_matches; use codec::{Decode, Encode}; -use sc_executor_common::{error::Error, runtime_blob::RuntimeBlob, wasm_runtime::WasmModule}; +use sc_executor_common::{ + error::{Error, WasmError}, + runtime_blob::RuntimeBlob, + wasm_runtime::WasmModule, +}; use sc_runtime_test::wasm_binary_unwrap; use sp_core::{ blake2_128, blake2_256, ed25519, map, @@ -781,3 +786,67 @@ fn return_value(wasm_method: WasmExecutionMethod) { (1234u64).encode() ); } + +test_wasm_execution!(return_huge_len); +fn return_huge_len(wasm_method: WasmExecutionMethod) { + let mut ext = TestExternalities::default(); + let mut ext = ext.ext(); + + match call_in_wasm("test_return_huge_len", &[], wasm_method, &mut ext).unwrap_err() { + Error::Runtime => { + assert_matches!(wasm_method, WasmExecutionMethod::Interpreted); + }, + Error::RuntimeConstruction(WasmError::Other(error)) => { + assert_matches!(wasm_method, WasmExecutionMethod::Compiled { .. }); + assert_eq!(error, "output exceeds bounds of wasm memory"); + }, + error => panic!("unexpected error: {:?}", error), + } +} + +test_wasm_execution!(return_max_memory_offset); +fn return_max_memory_offset(wasm_method: WasmExecutionMethod) { + let mut ext = TestExternalities::default(); + let mut ext = ext.ext(); + + assert_eq!( + call_in_wasm("test_return_max_memory_offset", &[], wasm_method, &mut ext).unwrap(), + ().encode() + ); +} + +test_wasm_execution!(return_max_memory_offset_plus_one); +fn return_max_memory_offset_plus_one(wasm_method: WasmExecutionMethod) { + let mut ext = TestExternalities::default(); + let mut ext = ext.ext(); + + match call_in_wasm("test_return_max_memory_offset_plus_one", &[], wasm_method, &mut ext) + .unwrap_err() + { + Error::Runtime => { + assert_matches!(wasm_method, WasmExecutionMethod::Interpreted); + }, + Error::RuntimeConstruction(WasmError::Other(error)) => { + assert_matches!(wasm_method, WasmExecutionMethod::Compiled { .. }); + assert_eq!(error, "output exceeds bounds of wasm memory"); + }, + error => panic!("unexpected error: {:?}", error), + } +} + +test_wasm_execution!(return_overflow); +fn return_overflow(wasm_method: WasmExecutionMethod) { + let mut ext = TestExternalities::default(); + let mut ext = ext.ext(); + + match call_in_wasm("test_return_overflow", &[], wasm_method, &mut ext).unwrap_err() { + Error::Runtime => { + assert_matches!(wasm_method, WasmExecutionMethod::Interpreted); + }, + Error::RuntimeConstruction(WasmError::Other(error)) => { + assert_matches!(wasm_method, WasmExecutionMethod::Compiled { .. }); + assert_eq!(error, "output exceeds bounds of wasm memory"); + }, + error => panic!("unexpected error: {:?}", error), + } +} diff --git a/client/executor/wasmtime/src/runtime.rs b/client/executor/wasmtime/src/runtime.rs index b124fd627..a45dfa59b 100644 --- a/client/executor/wasmtime/src/runtime.rs +++ b/client/executor/wasmtime/src/runtime.rs @@ -30,6 +30,7 @@ use sc_executor_common::{ runtime_blob::{ self, DataSegmentsSnapshot, ExposedMutableGlobalsSet, GlobalsSnapshot, RuntimeBlob, }, + util::checked_range, wasm_runtime::{InvokeMethod, WasmInstance, WasmModule}, }; use sp_runtime_interface::unpack_ptr_and_len; @@ -41,7 +42,7 @@ use std::{ Arc, }, }; -use wasmtime::{Engine, Memory, StoreLimits, Table}; +use wasmtime::{AsContext, Engine, Memory, StoreLimits, Table}; pub(crate) struct StoreData { /// The limits we apply to the store. We need to store it here to return a reference to this @@ -793,7 +794,19 @@ fn extract_output_data( output_ptr: u32, output_len: u32, ) -> Result> { + let ctx = instance.store(); + + // Do a length check before allocating. The returned output should not be bigger than the + // available WASM memory. Otherwise, a malicious parachain can trigger a large allocation, + // potentially causing memory exhaustion. + // + // Get the size of the WASM memory in bytes. + let memory_size = ctx.as_context().data().memory().data_size(ctx); + if checked_range(output_ptr as usize, output_len as usize, memory_size).is_none() { + Err(WasmError::Other("output exceeds bounds of wasm memory".into()))? + } let mut output = vec![0; output_len as usize]; - util::read_memory_into(instance.store(), Pointer::new(output_ptr), &mut output)?; + + util::read_memory_into(ctx, Pointer::new(output_ptr), &mut output)?; Ok(output) } From 593c76b7e0249fbacd68b87be71a42a557b47b8c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bastian=20K=C3=B6cher?= Date: Fri, 20 Jan 2023 16:59:16 +0100 Subject: [PATCH 032/558] sc-network: Ensure private addresses are disabled if requested (#13185) When running with `--no-private-ipv4` the node should not trying to connect to any private ip addresses. With the switch to libp2p this behavior was broken. Part of this version upgrade was the following pr: https://github.com/libp2p/rust-libp2p/pull/2995. This pr changed the default cache size of `libp2p-identity` from `0` aka disabled to `100`. Together with our implementation that was calling into `identity` to request addresses for a given peer. Before the switch to libp2p 0.50.0 this was returning zero addresses, but now with the cache enabled it started to return addresses. This pr fixes this by only letting discovery return addresses for a peer. It also ensures that we filter private addresses if requested. The cache is also disabled to restore the previous caching behavior, but it will actually not be called anymore. --- client/network/src/peer_info.rs | 12 +++++++----- client/network/src/protocol.rs | 6 ++++-- 2 files changed, 11 insertions(+), 7 deletions(-) diff --git a/client/network/src/peer_info.rs b/client/network/src/peer_info.rs index 97604a82c..f3402c0af 100644 --- a/client/network/src/peer_info.rs +++ b/client/network/src/peer_info.rs @@ -89,7 +89,9 @@ impl PeerInfoBehaviour { pub fn new(user_agent: String, local_public_key: PublicKey) -> Self { let identify = { let cfg = IdentifyConfig::new("/substrate/1.0".to_string(), local_public_key) - .with_agent_version(user_agent); + .with_agent_version(user_agent) + // We don't need any peer information cached. + .with_cache_size(0); Identify::new(cfg) }; @@ -182,10 +184,10 @@ impl NetworkBehaviour for PeerInfoBehaviour { IntoConnectionHandler::select(self.ping.new_handler(), self.identify.new_handler()) } - fn addresses_of_peer(&mut self, peer_id: &PeerId) -> Vec { - let mut list = self.ping.addresses_of_peer(peer_id); - list.extend_from_slice(&self.identify.addresses_of_peer(peer_id)); - list + fn addresses_of_peer(&mut self, _: &PeerId) -> Vec { + // Only `Discovery::addresses_of_peer` must be returning addresses to ensure that we + // don't return unwanted addresses. + Vec::new() } fn on_swarm_event(&mut self, event: FromSwarm) { diff --git a/client/network/src/protocol.rs b/client/network/src/protocol.rs index fd25c6526..cd232334e 100644 --- a/client/network/src/protocol.rs +++ b/client/network/src/protocol.rs @@ -965,8 +965,10 @@ where self.behaviour.new_handler() } - fn addresses_of_peer(&mut self, peer_id: &PeerId) -> Vec { - self.behaviour.addresses_of_peer(peer_id) + fn addresses_of_peer(&mut self, _: &PeerId) -> Vec { + // Only `Discovery::addresses_of_peer` must be returning addresses to ensure that we + // don't return unwanted addresses. + Vec::new() } fn on_swarm_event(&mut self, event: FromSwarm) { From 38ec2d8957d843fa424397f79a484d773eea1454 Mon Sep 17 00:00:00 2001 From: Sergej Sakac <73715684+Szegoo@users.noreply.github.com> Date: Fri, 20 Jan 2023 20:26:12 +0100 Subject: [PATCH 033/558] Warn validators with slow hardware (#12620) * move Metric * run hardware bench if validiator flag is being used * fix rustdoc & update node-template * fix * unused improt * warn * move Requirement * bench_result * ensure_requirements * make the code compile * check if authority * Update client/sysinfo/src/sysinfo.rs Co-authored-by: Oliver Tale-Yazdi * nit fixes * warning signs * Update client/sysinfo/src/sysinfo.rs Co-authored-by: Oliver Tale-Yazdi Co-authored-by: Oliver Tale-Yazdi --- bin/node/cli/src/service.rs | 7 +- client/sysinfo/src/lib.rs | 3 +- client/sysinfo/src/sysinfo.rs | 135 +++++++++++++++++- utils/frame/benchmarking-cli/src/lib.rs | 2 +- .../benchmarking-cli/src/machine/hardware.rs | 91 +----------- .../frame/benchmarking-cli/src/machine/mod.rs | 5 +- 6 files changed, 144 insertions(+), 99 deletions(-) diff --git a/bin/node/cli/src/service.rs b/bin/node/cli/src/service.rs index cdee61af3..ace24f186 100644 --- a/bin/node/cli/src/service.rs +++ b/bin/node/cli/src/service.rs @@ -21,6 +21,7 @@ //! Service implementation. Specialized wrapper over substrate service. use codec::Encode; +use frame_benchmarking_cli::SUBSTRATE_REFERENCE_HARDWARE; use frame_system_rpc_runtime_api::AccountNonceApi; use futures::prelude::*; use kitchensink_runtime::RuntimeApi; @@ -320,7 +321,11 @@ pub fn new_full_base( let hwbench = if !disable_hardware_benchmarks { config.database.path().map(|database_path| { let _ = std::fs::create_dir_all(&database_path); - sc_sysinfo::gather_hwbench(Some(database_path)) + sc_sysinfo::gather_hwbench( + Some(database_path), + SUBSTRATE_REFERENCE_HARDWARE.clone(), + config.role.is_authority(), + ) }) } else { None diff --git a/client/sysinfo/src/lib.rs b/client/sysinfo/src/lib.rs index cef5a4d21..f623bdae5 100644 --- a/client/sysinfo/src/lib.rs +++ b/client/sysinfo/src/lib.rs @@ -29,7 +29,8 @@ mod sysinfo_linux; pub use sysinfo::{ benchmark_cpu, benchmark_disk_random_writes, benchmark_disk_sequential_writes, benchmark_memory, benchmark_sr25519_verify, gather_hwbench, gather_sysinfo, - serialize_throughput, serialize_throughput_option, Throughput, + serialize_throughput, serialize_throughput_option, Metric, Requirement, Requirements, + Throughput, }; /// The operating system part of the current target triplet. diff --git a/client/sysinfo/src/sysinfo.rs b/client/sysinfo/src/sysinfo.rs index c66a6f6a6..800df4b8a 100644 --- a/client/sysinfo/src/sysinfo.rs +++ b/client/sysinfo/src/sysinfo.rs @@ -21,10 +21,10 @@ use crate::{ExecutionLimit, HwBench}; use sc_telemetry::SysInfo; use sp_core::{sr25519, Pair}; use sp_io::crypto::sr25519_verify; -use sp_std::{fmt, prelude::*}; +use sp_std::{fmt, fmt::Formatter, prelude::*}; use rand::{seq::SliceRandom, Rng, RngCore}; -use serde::Serializer; +use serde::{de::Visitor, Deserialize, Deserializer, Serialize, Serializer}; use std::{ fs::File, io::{Seek, SeekFrom, Write}, @@ -33,6 +33,43 @@ use std::{ time::{Duration, Instant}, }; +/// A single hardware metric. +#[derive(Deserialize, Serialize, Debug, Clone, Copy, PartialEq)] +pub enum Metric { + /// SR25519 signature verification. + Sr25519Verify, + /// Blake2-256 hashing algorithm. + Blake2256, + /// Copying data in RAM. + MemCopy, + /// Disk sequential write. + DiskSeqWrite, + /// Disk random write. + DiskRndWrite, +} + +impl Metric { + /// The category of the metric. + pub fn category(&self) -> &'static str { + match self { + Self::Sr25519Verify | Self::Blake2256 => "CPU", + Self::MemCopy => "Memory", + Self::DiskSeqWrite | Self::DiskRndWrite => "Disk", + } + } + + /// The name of the metric. It is always prefixed by the [`self.category()`]. + pub fn name(&self) -> &'static str { + match self { + Self::Sr25519Verify => "SR25519-Verify", + Self::Blake2256 => "BLAKE2-256", + Self::MemCopy => "Copy", + Self::DiskSeqWrite => "Seq Write", + Self::DiskRndWrite => "Rnd Write", + } + } +} + /// The unit in which the [`Throughput`] (bytes per second) is denoted. pub enum Unit { GiBs, @@ -137,6 +174,54 @@ where serializer.serialize_none() } +/// Serializes throughput into MiBs and represents it as `f64`. +fn serialize_throughput_as_f64(throughput: &Throughput, serializer: S) -> Result +where + S: Serializer, +{ + serializer.serialize_f64(throughput.as_mibs()) +} + +struct ThroughputVisitor; +impl<'de> Visitor<'de> for ThroughputVisitor { + type Value = Throughput; + + fn expecting(&self, formatter: &mut Formatter) -> fmt::Result { + formatter.write_str("A value that is a f64.") + } + + fn visit_f64(self, value: f64) -> Result + where + E: serde::de::Error, + { + Ok(Throughput::from_mibs(value)) + } +} + +fn deserialize_throughput<'de, D>(deserializer: D) -> Result +where + D: Deserializer<'de>, +{ + Ok(deserializer.deserialize_f64(ThroughputVisitor))? +} + +/// Multiple requirements for the hardware. +#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)] +pub struct Requirements(pub Vec); + +/// A single requirement for the hardware. +#[derive(Deserialize, Serialize, Debug, Clone, Copy, PartialEq)] +pub struct Requirement { + /// The metric to measure. + pub metric: Metric, + /// The minimal throughput that needs to be archived for this requirement. + #[serde( + serialize_with = "serialize_throughput_as_f64", + deserialize_with = "deserialize_throughput" + )] + pub minimum: Throughput, +} + #[inline(always)] pub(crate) fn benchmark( name: &str, @@ -503,8 +588,14 @@ pub fn benchmark_sr25519_verify(limit: ExecutionLimit) -> Throughput { /// Benchmarks the hardware and returns the results of those benchmarks. /// -/// Optionally accepts a path to a `scratch_directory` to use to benchmark the disk. -pub fn gather_hwbench(scratch_directory: Option<&Path>) -> HwBench { +/// Optionally accepts a path to a `scratch_directory` to use to benchmark the +/// disk. Also accepts the `requirements` for the hardware benchmark and a +/// boolean to specify if the node is an authority. +pub fn gather_hwbench( + scratch_directory: Option<&Path>, + requirements: Requirements, + is_authority: bool, +) -> HwBench { #[allow(unused_mut)] let mut hwbench = HwBench { cpu_hashrate_score: benchmark_cpu(DEFAULT_CPU_EXECUTION_LIMIT), @@ -534,9 +625,45 @@ pub fn gather_hwbench(scratch_directory: Option<&Path>) -> HwBench { }; } + if is_authority { + ensure_requirements(hwbench.clone(), requirements); + } + hwbench } +fn ensure_requirements(hwbench: HwBench, requirements: Requirements) { + let mut failed = 0; + for requirement in requirements.0.iter() { + match requirement.metric { + Metric::Blake2256 => + if requirement.minimum > hwbench.cpu_hashrate_score { + failed += 1; + }, + Metric::MemCopy => + if requirement.minimum > hwbench.memory_memcpy_score { + failed += 1; + }, + Metric::DiskSeqWrite => + if let Some(score) = hwbench.disk_sequential_write_score { + if requirement.minimum > score { + failed += 1; + } + }, + Metric::DiskRndWrite => + if let Some(score) = hwbench.disk_random_write_score { + if requirement.minimum > score { + failed += 1; + } + }, + Metric::Sr25519Verify => {}, + } + } + if failed != 0 { + log::warn!("⚠️ Your hardware performance score was less than expected for role 'Authority'. See https://wiki.polkadot.network/docs/maintain-guides-how-to-validate-polkadot#reference-hardware"); + } +} + #[cfg(test)] mod tests { use super::*; diff --git a/utils/frame/benchmarking-cli/src/lib.rs b/utils/frame/benchmarking-cli/src/lib.rs index a44a208b1..5723a8038 100644 --- a/utils/frame/benchmarking-cli/src/lib.rs +++ b/utils/frame/benchmarking-cli/src/lib.rs @@ -27,7 +27,7 @@ mod storage; pub use block::BlockCmd; pub use extrinsic::{ExtrinsicBuilder, ExtrinsicCmd, ExtrinsicFactory}; -pub use machine::{MachineCmd, Requirements, SUBSTRATE_REFERENCE_HARDWARE}; +pub use machine::{MachineCmd, SUBSTRATE_REFERENCE_HARDWARE}; pub use overhead::OverheadCmd; pub use pallet::PalletCmd; pub use sc_service::BasePath; diff --git a/utils/frame/benchmarking-cli/src/machine/hardware.rs b/utils/frame/benchmarking-cli/src/machine/hardware.rs index 50c88ec74..318c193d7 100644 --- a/utils/frame/benchmarking-cli/src/machine/hardware.rs +++ b/utils/frame/benchmarking-cli/src/machine/hardware.rs @@ -18,40 +18,7 @@ //! Contains types to define hardware requirements. use lazy_static::lazy_static; -use sc_sysinfo::Throughput; -use serde::{de::Visitor, Deserialize, Deserializer, Serialize, Serializer}; -use sp_std::{fmt, fmt::Formatter}; - -/// Serializes throughput into MiBs and represents it as `f64`. -fn serialize_throughput_as_f64(throughput: &Throughput, serializer: S) -> Result -where - S: Serializer, -{ - serializer.serialize_f64(throughput.as_mibs()) -} - -struct ThroughputVisitor; -impl<'de> Visitor<'de> for ThroughputVisitor { - type Value = Throughput; - - fn expecting(&self, formatter: &mut Formatter) -> fmt::Result { - formatter.write_str("A value that is a f64.") - } - - fn visit_f64(self, value: f64) -> Result - where - E: serde::de::Error, - { - Ok(Throughput::from_mibs(value)) - } -} - -fn deserialize_throughput<'de, D>(deserializer: D) -> Result -where - D: Deserializer<'de>, -{ - Ok(deserializer.deserialize_f64(ThroughputVisitor))? -} +use sc_sysinfo::Requirements; lazy_static! { /// The hardware requirements as measured on reference hardware. @@ -67,62 +34,6 @@ lazy_static! { }; } -/// Multiple requirements for the hardware. -#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)] -pub struct Requirements(pub Vec); - -/// A single requirement for the hardware. -#[derive(Deserialize, Serialize, Debug, Clone, Copy, PartialEq)] -pub struct Requirement { - /// The metric to measure. - pub metric: Metric, - /// The minimal throughput that needs to be archived for this requirement. - #[serde( - serialize_with = "serialize_throughput_as_f64", - deserialize_with = "deserialize_throughput" - )] - pub minimum: Throughput, -} - -/// A single hardware metric. -/// -/// The implementation of these is in `sc-sysinfo`. -#[derive(Deserialize, Serialize, Debug, Clone, Copy, PartialEq)] -pub enum Metric { - /// SR25519 signature verification. - Sr25519Verify, - /// Blake2-256 hashing algorithm. - Blake2256, - /// Copying data in RAM. - MemCopy, - /// Disk sequential write. - DiskSeqWrite, - /// Disk random write. - DiskRndWrite, -} - -impl Metric { - /// The category of the metric. - pub fn category(&self) -> &'static str { - match self { - Self::Sr25519Verify | Self::Blake2256 => "CPU", - Self::MemCopy => "Memory", - Self::DiskSeqWrite | Self::DiskRndWrite => "Disk", - } - } - - /// The name of the metric. It is always prefixed by the [`self::category()`]. - pub fn name(&self) -> &'static str { - match self { - Self::Sr25519Verify => "SR25519-Verify", - Self::Blake2256 => "BLAKE2-256", - Self::MemCopy => "Copy", - Self::DiskSeqWrite => "Seq Write", - Self::DiskRndWrite => "Rnd Write", - } - } -} - #[cfg(test)] mod tests { use super::*; diff --git a/utils/frame/benchmarking-cli/src/machine/mod.rs b/utils/frame/benchmarking-cli/src/machine/mod.rs index 82b4e5be7..bcffef255 100644 --- a/utils/frame/benchmarking-cli/src/machine/mod.rs +++ b/utils/frame/benchmarking-cli/src/machine/mod.rs @@ -30,11 +30,12 @@ use sc_cli::{CliConfiguration, Result, SharedParams}; use sc_service::Configuration; use sc_sysinfo::{ benchmark_cpu, benchmark_disk_random_writes, benchmark_disk_sequential_writes, - benchmark_memory, benchmark_sr25519_verify, ExecutionLimit, Throughput, + benchmark_memory, benchmark_sr25519_verify, ExecutionLimit, Metric, Requirement, Requirements, + Throughput, }; use crate::shared::check_build_profile; -pub use hardware::{Metric, Requirement, Requirements, SUBSTRATE_REFERENCE_HARDWARE}; +pub use hardware::SUBSTRATE_REFERENCE_HARDWARE; /// Command to benchmark the hardware. /// From 2c16842143b92bbfe333498fe1a6dd97733f2080 Mon Sep 17 00:00:00 2001 From: Oliver Tale-Yazdi Date: Sat, 21 Jan 2023 00:42:00 +0100 Subject: [PATCH 034/558] Use year 2023 in the License headers (#13193) Signed-off-by: Oliver Tale-Yazdi Signed-off-by: Oliver Tale-Yazdi --- HEADER-APACHE2 | 2 +- HEADER-GPL3 | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/HEADER-APACHE2 b/HEADER-APACHE2 index 58baa5389..403497a86 100644 --- a/HEADER-APACHE2 +++ b/HEADER-APACHE2 @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2022 Parity Technologies (UK) Ltd. +// Copyright (C) 2023 Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/HEADER-GPL3 b/HEADER-GPL3 index 9412b5a70..ed7daba72 100644 --- a/HEADER-GPL3 +++ b/HEADER-GPL3 @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2022 Parity Technologies (UK) Ltd. +// Copyright (C) 2023 Parity Technologies (UK) Ltd. // SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 // This program is free software: you can redistribute it and/or modify From 355e4a9c87f85637185c0852bda9aeb3db404b6d Mon Sep 17 00:00:00 2001 From: Oliver Tale-Yazdi Date: Sat, 21 Jan 2023 11:53:07 +0100 Subject: [PATCH 035/558] Move slow hardware warning print logic to CLI (#13198) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Move slow hardware warning print logic to CLI Signed-off-by: Oliver Tale-Yazdi * Update client/sysinfo/src/sysinfo.rs Co-authored-by: Bastian Köcher * fmt Signed-off-by: Oliver Tale-Yazdi Signed-off-by: Oliver Tale-Yazdi Co-authored-by: Bastian Köcher --- bin/node/cli/src/service.rs | 21 ++++++----- client/sysinfo/src/sysinfo.rs | 66 +++++++++++++++-------------------- 2 files changed, 39 insertions(+), 48 deletions(-) diff --git a/bin/node/cli/src/service.rs b/bin/node/cli/src/service.rs index ace24f186..d77a333df 100644 --- a/bin/node/cli/src/service.rs +++ b/bin/node/cli/src/service.rs @@ -318,18 +318,12 @@ pub fn new_full_base( &sc_consensus_babe::BabeLink, ), ) -> Result { - let hwbench = if !disable_hardware_benchmarks { - config.database.path().map(|database_path| { + let hwbench = (!disable_hardware_benchmarks) + .then_some(config.database.path().map(|database_path| { let _ = std::fs::create_dir_all(&database_path); - sc_sysinfo::gather_hwbench( - Some(database_path), - SUBSTRATE_REFERENCE_HARDWARE.clone(), - config.role.is_authority(), - ) - }) - } else { - None - }; + sc_sysinfo::gather_hwbench(Some(database_path)) + })) + .flatten(); let sc_service::PartialComponents { client, @@ -403,6 +397,11 @@ pub fn new_full_base( if let Some(hwbench) = hwbench { sc_sysinfo::print_hwbench(&hwbench); + if !SUBSTRATE_REFERENCE_HARDWARE.check_hardware(&hwbench) && role.is_authority() { + log::warn!( + "⚠️ The hardware does not meet the minimal requirements for role 'Authority'." + ); + } if let Some(ref mut telemetry) = telemetry { let telemetry_handle = telemetry.handle(); diff --git a/client/sysinfo/src/sysinfo.rs b/client/sysinfo/src/sysinfo.rs index 800df4b8a..1fbca7d37 100644 --- a/client/sysinfo/src/sysinfo.rs +++ b/client/sysinfo/src/sysinfo.rs @@ -591,11 +591,7 @@ pub fn benchmark_sr25519_verify(limit: ExecutionLimit) -> Throughput { /// Optionally accepts a path to a `scratch_directory` to use to benchmark the /// disk. Also accepts the `requirements` for the hardware benchmark and a /// boolean to specify if the node is an authority. -pub fn gather_hwbench( - scratch_directory: Option<&Path>, - requirements: Requirements, - is_authority: bool, -) -> HwBench { +pub fn gather_hwbench(scratch_directory: Option<&Path>) -> HwBench { #[allow(unused_mut)] let mut hwbench = HwBench { cpu_hashrate_score: benchmark_cpu(DEFAULT_CPU_EXECUTION_LIMIT), @@ -625,42 +621,38 @@ pub fn gather_hwbench( }; } - if is_authority { - ensure_requirements(hwbench.clone(), requirements); - } - hwbench } -fn ensure_requirements(hwbench: HwBench, requirements: Requirements) { - let mut failed = 0; - for requirement in requirements.0.iter() { - match requirement.metric { - Metric::Blake2256 => - if requirement.minimum > hwbench.cpu_hashrate_score { - failed += 1; - }, - Metric::MemCopy => - if requirement.minimum > hwbench.memory_memcpy_score { - failed += 1; - }, - Metric::DiskSeqWrite => - if let Some(score) = hwbench.disk_sequential_write_score { - if requirement.minimum > score { - failed += 1; - } - }, - Metric::DiskRndWrite => - if let Some(score) = hwbench.disk_random_write_score { - if requirement.minimum > score { - failed += 1; - } - }, - Metric::Sr25519Verify => {}, +impl Requirements { + /// Whether the hardware requirements are met by the provided benchmark results. + pub fn check_hardware(&self, hwbench: &HwBench) -> bool { + for requirement in self.0.iter() { + match requirement.metric { + Metric::Blake2256 => + if requirement.minimum > hwbench.cpu_hashrate_score { + return false + }, + Metric::MemCopy => + if requirement.minimum > hwbench.memory_memcpy_score { + return false + }, + Metric::DiskSeqWrite => + if let Some(score) = hwbench.disk_sequential_write_score { + if requirement.minimum > score { + return false + } + }, + Metric::DiskRndWrite => + if let Some(score) = hwbench.disk_random_write_score { + if requirement.minimum > score { + return false + } + }, + Metric::Sr25519Verify => {}, + } } - } - if failed != 0 { - log::warn!("⚠️ Your hardware performance score was less than expected for role 'Authority'. See https://wiki.polkadot.network/docs/maintain-guides-how-to-validate-polkadot#reference-hardware"); + true } } From 79cb2e63312b16dc3e50d6df5283f7f7842f9bfb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bastian=20K=C3=B6cher?= Date: Sat, 21 Jan 2023 20:00:24 +0100 Subject: [PATCH 036/558] Fix flaky BABE test (#13199) The `authoring_blocks` test of BABE was calculating the slot based on the timestamp it sometimes failed in CI. The problem is that we combine all the notifications and authoring futures in one big future. This one big future may first polls one authoring future to build a block. Then it polls all notification futures again to import the block. Then some other authoring future is polled and builds on the imported block using the same slot and making the import fail. The solution is that we just artificially increase the slot to make the test work. --- client/consensus/babe/src/tests.rs | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) diff --git a/client/consensus/babe/src/tests.rs b/client/consensus/babe/src/tests.rs index f74864a00..304bc9fc2 100644 --- a/client/consensus/babe/src/tests.rs +++ b/client/consensus/babe/src/tests.rs @@ -432,6 +432,7 @@ async fn run_one_test(mutator: impl Fn(&mut TestHeader, Stage) + Send + Sync + ' .for_each(|_| future::ready(())), ); + let client_clone = client.clone(); babe_futures.push( start_babe(BabeParams { block_import: data.block_import.lock().take().expect("import set up during init"), @@ -439,12 +440,19 @@ async fn run_one_test(mutator: impl Fn(&mut TestHeader, Stage) + Send + Sync + ' client, env: environ, sync_oracle: DummyOracle, - create_inherent_data_providers: Box::new(|_, _| async { - let slot = InherentDataProvider::from_timestamp_and_slot_duration( - Timestamp::current(), - SlotDuration::from_millis(SLOT_DURATION_MS), + create_inherent_data_providers: Box::new(move |parent, _| { + // Get the slot of the parent header and just increase this slot. + // + // Below we will running everything in one big future. If we would use + // time based slot, it can happen that on babe instance imports a block from + // another babe instance and then tries to build a block in the same slot making + // this test fail. + let parent_header = client_clone.header(parent).ok().flatten().unwrap(); + let slot = Slot::from( + find_pre_digest::(&parent_header).unwrap().slot() + 1, ); - Ok((slot,)) + + async move { Ok((InherentDataProvider::new(slot),)) } }), force_authoring: false, backoff_authoring_blocks: Some(BackoffAuthoringOnFinalizedHeadLagging::default()), From 1f70226a61807064f3cf95c616d36094358eeb70 Mon Sep 17 00:00:00 2001 From: zjb0807 Date: Sun, 22 Jan 2023 21:34:57 +1300 Subject: [PATCH 037/558] Add debug info in assert_has_event and assert_last_event (#12979) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * improve debug info in assert_has_event and assert_last_event * Apply suggestions from code review Co-authored-by: Bastian Köcher Co-authored-by: Bastian Köcher --- frame/system/src/lib.rs | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/frame/system/src/lib.rs b/frame/system/src/lib.rs index 3909b1e9c..32627896e 100644 --- a/frame/system/src/lib.rs +++ b/frame/system/src/lib.rs @@ -1503,13 +1503,21 @@ impl Pallet { /// Assert the given `event` exists. #[cfg(any(feature = "std", feature = "runtime-benchmarks", test))] pub fn assert_has_event(event: T::RuntimeEvent) { - assert!(Self::events().iter().any(|record| record.event == event)) + let events = Self::events(); + assert!( + events.iter().any(|record| record.event == event), + "expected event {event:?} not found in events {events:?}", + ); } /// Assert the last event equal to the given `event`. #[cfg(any(feature = "std", feature = "runtime-benchmarks", test))] pub fn assert_last_event(event: T::RuntimeEvent) { - assert_eq!(Self::events().last().expect("events expected").event, event); + let last_event = Self::events().last().expect("events expected").event.clone(); + assert_eq!( + last_event, event, + "expected event {event:?} is not equal to the last event {last_event:?}", + ); } /// Return the chain's current runtime version. From ed3f055907116e7c194c1e5d0f884b8329ba70cb Mon Sep 17 00:00:00 2001 From: Sam Johnson Date: Mon, 23 Jan 2023 02:07:48 -0500 Subject: [PATCH 038/558] new proc-macro-based benchmarking syntax (#12924) * add stub for new benchmark macro * benchmark syntax * add #[extrinsic call] separator * parse #[benchmark] item as a function * proper emission of error when #[extrinsic_call] annotation is missing * clean up * enclosing module via benchmarks! { } working * use an attribute macro on the module instead of benchmarks! { } * cargo fmt * working component implementation * WIP * working * add syntax for Linear * parsing of param ranges (still need to build tuple though) * params parsing WIP * clean up (don't need extrinsic call name) * use proper Result syntax for BenchmarkDef parsing * proper parsing of Linear<0, 1> style args * successfully parse and make use of linear component ranges :boom: * rename support variable => home because eventually will be moved * compile-time check that param range types implement ParamRange * switch to using balances as example, failing on instance pallet * successfully set up __origin and __call with balances :boom: * clean up * use a module * don't need a variable for transfer * rename benchmark_transfer -> transfer because no longer conflicts * clean up * working with transfer_increasing_users as well :boom: * re-add BareBlock * add comments for undocumented structs+functions+traits * refactor in preparation for removing module requirements * switch to a block instead of a module * use the outer macro pattern to to enable #[benchmarks] aggregation * successfully generate SelectedBenchmark :boom: * implement components for SelectedBenchmark * implement instance for SelectedBenchmark * properly track #[extra] * working impl for fn benchmarks() * run_benchmarks WIP * finish run_benchmark! impl :boom: * import balances transfer_best_case benchmark * import transfer_keep_alive balances pallet benchmark * import set_balance_creating balances pallet benchmark * import set_balance_killing balances pallet benchmark * import force_transfer balances pallet benchmark * add #[extra] annotation and docs to transfer_increasing_users * import transfer_all balances pallet benchmark * import force_unreserve balances pallet benchmark * prepare to implement impl_benchmark_test_suite! * ensure tests cover #[extra] before and after #[benchmark] tag * refactor * clean up * fix * move to outer * switch to benchmarks/instance_benchmarks * test impl almost done, strange compiler error * benchmark test suites working :boom: * clean up * add stub and basic parsing for where_clause * working except where clause and extrinsic calls containing method chains * assume option (2) for now wrt https://github.com/paritytech/substrate/pull/12924#issuecomment-1372938718 * clean up * switch to attribute-style * properly handle where clauses * fix subtle missing where clause, now just MessageQueue issues * fix block formatting in message-queue pallet * switch to block vs non-block parsing of extrinsic call * working now but some benchmark tests failing * message-queue tests working (run order issue fixed) :tada: * add comments and internal docs for fame_support_procedural::benchmark * fix license years * docs for lib.rs * add docs to new support procedural macros * don't allow #[benchmark] outside of benchmarking module * add docs * use benchmark(extra, skip_meta) style args * update docs accordingly * appease clippy * bump ci * add notes about `extra` and `skip_meta` * fix doc tests * re-run CI * use `ignore` instead of `no_run` on doc examples * bump CI * replace some if-lets with if-elses * more refactoring of if-let statements * fix remaining if-lets in BenchmarkDef::from() * fix if-lets in benchmarks() * fix remaining if-lets, use nested find_map for extrinsic call * switch to use #[extrinsic_call] or #[block] situationally * refactor ExtrinsicCallDef => BenchmarkCallDef * update docs with info about #[block] * add macro stub for #[extrinsic_call] * fix docs and add stub for #[block] as well * remove unused extern crate line * fix clippy nits * Use V2 bench syntax in pallet-example-basic Just testing the dev-ex... Signed-off-by: Oliver Tale-Yazdi * carry over comment * use curly-brace style for impl_benchmark_test_suite! * remove unneeded parenthesis * proper handling of _() extrinsic call style * add docs for _() syntax * fix crate access * simplify keyword access Co-authored-by: Keith Yeung * simplify module content destructuring Co-authored-by: Keith Yeung * fix crate access "frame_benchmarking" => "frame-benchmarking", compiles * use _() extrinsic call syntax where possible in balances * simplify attr.path.segments.last() Co-authored-by: Keith Yeung * fix compile error being suppressed * simplify extrinsic call keyword parsing Co-authored-by: Keith Yeung * use ? operator instead of return None Co-authored-by: Keith Yeung * rename generics => type_use_generics rename full_generics => type_impl_generics * simplify extrinsic call extraction with transpose * bump CI * nit * proper handling of too many + too few block/extrinsic call annotations * change to B >= A Co-authored-by: Oliver Tale-Yazdi * remove unneeded ignore Co-authored-by: Oliver Tale-Yazdi * remove another ignore Co-authored-by: Oliver Tale-Yazdi * add ui tests * use _() style extrinsic call on accumulate_dummy Co-authored-by: Oliver Tale-Yazdi * add range check to ParamRange * ui test for bad param ranges * fix failing example * add ignore back to other failing example * tweak expr_call span Co-authored-by: Keith Yeung * fix typo * eliminate a match Co-authored-by: Keith Yeung * change pub fn benchmarks to return Result * fix origin error span * more informative error for invalid benchmark parameter name * fix spans on a few benchmark errors * remove unneeded clone * refactor inner loop of benchmark function parsing * preserve mod attributes * refactor outer loop of benchmark def parsing code, greatly simplified * simplify to use a ? operator when parsing benchmark attr path * fix another ? operator * further simplify benchmark function attr parsing with more ? ops * refactor extrinsic call handling to use if let rather than match * replace is_ok => is_err Co-authored-by: Keith Yeung * re-use name during expansion of benchmark def * remove unneeded clone * fix span for origin missing error * fix missing semi Signed-off-by: Oliver Tale-Yazdi Co-authored-by: Oliver Tale-Yazdi Co-authored-by: Keith Yeung Co-authored-by: parity-processbot <> --- Cargo.lock | 14 + frame/balances/src/benchmarking.rs | 127 ++- frame/examples/basic/src/benchmarking.rs | 62 +- frame/message-queue/src/benchmarking.rs | 217 +++-- frame/support/Cargo.toml | 1 + frame/support/procedural/Cargo.toml | 1 + frame/support/procedural/src/benchmark.rs | 860 ++++++++++++++++++ frame/support/procedural/src/lib.rs | 64 ++ frame/support/src/lib.rs | 223 +++++ frame/support/test/Cargo.toml | 2 + frame/support/test/tests/benchmark_ui.rs | 36 + .../test/tests/benchmark_ui/bad_param_name.rs | 18 + .../tests/benchmark_ui/bad_param_name.stderr | 5 + .../benchmark_ui/bad_param_name_too_long.rs | 14 + .../bad_param_name_too_long.stderr | 5 + .../benchmark_ui/bad_param_name_upper_case.rs | 14 + .../bad_param_name_upper_case.stderr | 5 + .../tests/benchmark_ui/bad_param_range.rs | 16 + .../tests/benchmark_ui/bad_param_range.stderr | 5 + .../test/tests/benchmark_ui/bad_params.rs | 18 + .../test/tests/benchmark_ui/bad_params.stderr | 5 + .../test/tests/benchmark_ui/dup_block.rs | 20 + .../test/tests/benchmark_ui/dup_block.stderr | 5 + .../tests/benchmark_ui/dup_extrinsic_call.rs | 20 + .../benchmark_ui/dup_extrinsic_call.stderr | 5 + .../test/tests/benchmark_ui/extra_extra.rs | 16 + .../tests/benchmark_ui/extra_extra.stderr | 5 + .../tests/benchmark_ui/extra_skip_meta.rs | 16 + .../tests/benchmark_ui/extra_skip_meta.stderr | 5 + .../benchmark_ui/extrinsic_call_out_of_fn.rs | 6 + .../extrinsic_call_out_of_fn.stderr | 7 + .../test/tests/benchmark_ui/missing_call.rs | 13 + .../tests/benchmark_ui/missing_call.stderr | 5 + .../test/tests/benchmark_ui/missing_origin.rs | 16 + .../tests/benchmark_ui/missing_origin.stderr | 5 + .../tests/benchmark_ui/pass/valid_basic.rs | 17 + .../tests/benchmark_ui/unrecognized_option.rs | 16 + .../benchmark_ui/unrecognized_option.stderr | 5 + 38 files changed, 1760 insertions(+), 134 deletions(-) create mode 100644 frame/support/procedural/src/benchmark.rs create mode 100644 frame/support/test/tests/benchmark_ui.rs create mode 100644 frame/support/test/tests/benchmark_ui/bad_param_name.rs create mode 100644 frame/support/test/tests/benchmark_ui/bad_param_name.stderr create mode 100644 frame/support/test/tests/benchmark_ui/bad_param_name_too_long.rs create mode 100644 frame/support/test/tests/benchmark_ui/bad_param_name_too_long.stderr create mode 100644 frame/support/test/tests/benchmark_ui/bad_param_name_upper_case.rs create mode 100644 frame/support/test/tests/benchmark_ui/bad_param_name_upper_case.stderr create mode 100644 frame/support/test/tests/benchmark_ui/bad_param_range.rs create mode 100644 frame/support/test/tests/benchmark_ui/bad_param_range.stderr create mode 100644 frame/support/test/tests/benchmark_ui/bad_params.rs create mode 100644 frame/support/test/tests/benchmark_ui/bad_params.stderr create mode 100644 frame/support/test/tests/benchmark_ui/dup_block.rs create mode 100644 frame/support/test/tests/benchmark_ui/dup_block.stderr create mode 100644 frame/support/test/tests/benchmark_ui/dup_extrinsic_call.rs create mode 100644 frame/support/test/tests/benchmark_ui/dup_extrinsic_call.stderr create mode 100644 frame/support/test/tests/benchmark_ui/extra_extra.rs create mode 100644 frame/support/test/tests/benchmark_ui/extra_extra.stderr create mode 100644 frame/support/test/tests/benchmark_ui/extra_skip_meta.rs create mode 100644 frame/support/test/tests/benchmark_ui/extra_skip_meta.stderr create mode 100644 frame/support/test/tests/benchmark_ui/extrinsic_call_out_of_fn.rs create mode 100644 frame/support/test/tests/benchmark_ui/extrinsic_call_out_of_fn.stderr create mode 100644 frame/support/test/tests/benchmark_ui/missing_call.rs create mode 100644 frame/support/test/tests/benchmark_ui/missing_call.stderr create mode 100644 frame/support/test/tests/benchmark_ui/missing_origin.rs create mode 100644 frame/support/test/tests/benchmark_ui/missing_origin.stderr create mode 100644 frame/support/test/tests/benchmark_ui/pass/valid_basic.rs create mode 100644 frame/support/test/tests/benchmark_ui/unrecognized_option.rs create mode 100644 frame/support/test/tests/benchmark_ui/unrecognized_option.stderr diff --git a/Cargo.lock b/Cargo.lock index 348f23d73..08948ffbd 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1614,6 +1614,17 @@ dependencies = [ "rusticata-macros", ] +[[package]] +name = "derive-syn-parse" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e79116f119dd1dba1abf1f3405f03b9b0e79a27a3883864bfebded8a3dc768cd" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "derive_builder" version = "0.11.2" @@ -2326,6 +2337,7 @@ dependencies = [ "sp-std", "sp-tracing", "sp-weights", + "static_assertions", "tt-call", ] @@ -2335,6 +2347,7 @@ version = "4.0.0-dev" dependencies = [ "Inflector", "cfg-expr", + "derive-syn-parse", "frame-support-procedural-tools", "itertools", "proc-macro2", @@ -2366,6 +2379,7 @@ dependencies = [ name = "frame-support-test" version = "3.0.0" dependencies = [ + "frame-benchmarking", "frame-support", "frame-support-test-pallet", "frame-system", diff --git a/frame/balances/src/benchmarking.rs b/frame/balances/src/benchmarking.rs index 206adba0f..f85ff43b7 100644 --- a/frame/balances/src/benchmarking.rs +++ b/frame/balances/src/benchmarking.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2020-2022 Parity Technologies (UK) Ltd. +// Copyright (C) 2020-2023 Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); @@ -20,22 +20,25 @@ #![cfg(feature = "runtime-benchmarks")] use super::*; +use crate::Pallet as Balances; -use frame_benchmarking::{account, benchmarks_instance_pallet, whitelisted_caller}; +use frame_benchmarking::{account, impl_benchmark_test_suite, whitelisted_caller}; +use frame_support::benchmarking::*; use frame_system::RawOrigin; -use sp_runtime::traits::Bounded; - -use crate::Pallet as Balances; const SEED: u32 = 0; // existential deposit multiplier const ED_MULTIPLIER: u32 = 10; -benchmarks_instance_pallet! { +#[instance_benchmarks] +mod benchmarks { + use super::*; + // Benchmark `transfer` extrinsic with the worst possible conditions: // * Transfer will kill the sender account. // * Transfer will create the recipient account. - transfer { + #[benchmark] + fn transfer() { let existential_deposit = T::ExistentialDeposit::get(); let caller = whitelisted_caller(); @@ -47,53 +50,66 @@ benchmarks_instance_pallet! { // and reap this user. let recipient: T::AccountId = account("recipient", 0, SEED); let recipient_lookup = T::Lookup::unlookup(recipient.clone()); - let transfer_amount = existential_deposit.saturating_mul((ED_MULTIPLIER - 1).into()) + 1u32.into(); - }: transfer(RawOrigin::Signed(caller.clone()), recipient_lookup, transfer_amount) - verify { + let transfer_amount = + existential_deposit.saturating_mul((ED_MULTIPLIER - 1).into()) + 1u32.into(); + + #[extrinsic_call] + _(RawOrigin::Signed(caller.clone()), recipient_lookup, transfer_amount); + assert_eq!(Balances::::free_balance(&caller), Zero::zero()); assert_eq!(Balances::::free_balance(&recipient), transfer_amount); } // Benchmark `transfer` with the best possible condition: // * Both accounts exist and will continue to exist. - #[extra] - transfer_best_case { + #[benchmark(extra)] + fn transfer_best_case() { let caller = whitelisted_caller(); let recipient: T::AccountId = account("recipient", 0, SEED); let recipient_lookup = T::Lookup::unlookup(recipient.clone()); - // Give the sender account max funds for transfer (their account will never reasonably be killed). - let _ = as Currency<_>>::make_free_balance_be(&caller, T::Balance::max_value()); + // Give the sender account max funds for transfer (their account will never reasonably be + // killed). + let _ = + as Currency<_>>::make_free_balance_be(&caller, T::Balance::max_value()); // Give the recipient account existential deposit (thus their account already exists). let existential_deposit = T::ExistentialDeposit::get(); - let _ = as Currency<_>>::make_free_balance_be(&recipient, existential_deposit); + let _ = + as Currency<_>>::make_free_balance_be(&recipient, existential_deposit); let transfer_amount = existential_deposit.saturating_mul(ED_MULTIPLIER.into()); - }: transfer(RawOrigin::Signed(caller.clone()), recipient_lookup, transfer_amount) - verify { + + #[extrinsic_call] + transfer(RawOrigin::Signed(caller.clone()), recipient_lookup, transfer_amount); + assert!(!Balances::::free_balance(&caller).is_zero()); assert!(!Balances::::free_balance(&recipient).is_zero()); } // Benchmark `transfer_keep_alive` with the worst possible condition: // * The recipient account is created. - transfer_keep_alive { + #[benchmark] + fn transfer_keep_alive() { let caller = whitelisted_caller(); let recipient: T::AccountId = account("recipient", 0, SEED); let recipient_lookup = T::Lookup::unlookup(recipient.clone()); // Give the sender account max funds, thus a transfer will not kill account. - let _ = as Currency<_>>::make_free_balance_be(&caller, T::Balance::max_value()); + let _ = + as Currency<_>>::make_free_balance_be(&caller, T::Balance::max_value()); let existential_deposit = T::ExistentialDeposit::get(); let transfer_amount = existential_deposit.saturating_mul(ED_MULTIPLIER.into()); - }: _(RawOrigin::Signed(caller.clone()), recipient_lookup, transfer_amount) - verify { + + #[extrinsic_call] + _(RawOrigin::Signed(caller.clone()), recipient_lookup, transfer_amount); + assert!(!Balances::::free_balance(&caller).is_zero()); assert_eq!(Balances::::free_balance(&recipient), transfer_amount); } // Benchmark `set_balance` coming from ROOT account. This always creates an account. - set_balance_creating { + #[benchmark] + fn set_balance_creating() { let user: T::AccountId = account("user", 0, SEED); let user_lookup = T::Lookup::unlookup(user.clone()); @@ -101,14 +117,17 @@ benchmarks_instance_pallet! { let existential_deposit = T::ExistentialDeposit::get(); let balance_amount = existential_deposit.saturating_mul(ED_MULTIPLIER.into()); let _ = as Currency<_>>::make_free_balance_be(&user, balance_amount); - }: set_balance(RawOrigin::Root, user_lookup, balance_amount, balance_amount) - verify { + + #[extrinsic_call] + set_balance(RawOrigin::Root, user_lookup, balance_amount, balance_amount); + assert_eq!(Balances::::free_balance(&user), balance_amount); assert_eq!(Balances::::reserved_balance(&user), balance_amount); } // Benchmark `set_balance` coming from ROOT account. This always kills an account. - set_balance_killing { + #[benchmark] + fn set_balance_killing() { let user: T::AccountId = account("user", 0, SEED); let user_lookup = T::Lookup::unlookup(user.clone()); @@ -116,15 +135,18 @@ benchmarks_instance_pallet! { let existential_deposit = T::ExistentialDeposit::get(); let balance_amount = existential_deposit.saturating_mul(ED_MULTIPLIER.into()); let _ = as Currency<_>>::make_free_balance_be(&user, balance_amount); - }: set_balance(RawOrigin::Root, user_lookup, Zero::zero(), Zero::zero()) - verify { + + #[extrinsic_call] + set_balance(RawOrigin::Root, user_lookup, Zero::zero(), Zero::zero()); + assert!(Balances::::free_balance(&user).is_zero()); } // Benchmark `force_transfer` extrinsic with the worst possible conditions: // * Transfer will kill the sender account. // * Transfer will create the recipient account. - force_transfer { + #[benchmark] + fn force_transfer() { let existential_deposit = T::ExistentialDeposit::get(); let source: T::AccountId = account("source", 0, SEED); let source_lookup = T::Lookup::unlookup(source.clone()); @@ -133,12 +155,16 @@ benchmarks_instance_pallet! { let balance = existential_deposit.saturating_mul(ED_MULTIPLIER.into()); let _ = as Currency<_>>::make_free_balance_be(&source, balance); - // Transfer `e - 1` existential deposits + 1 unit, which guarantees to create one account, and reap this user. + // Transfer `e - 1` existential deposits + 1 unit, which guarantees to create one account, + // and reap this user. let recipient: T::AccountId = account("recipient", 0, SEED); let recipient_lookup = T::Lookup::unlookup(recipient.clone()); - let transfer_amount = existential_deposit.saturating_mul((ED_MULTIPLIER - 1).into()) + 1u32.into(); - }: force_transfer(RawOrigin::Root, source_lookup, recipient_lookup, transfer_amount) - verify { + let transfer_amount = + existential_deposit.saturating_mul((ED_MULTIPLIER - 1).into()) + 1u32.into(); + + #[extrinsic_call] + _(RawOrigin::Root, source_lookup, recipient_lookup, transfer_amount); + assert_eq!(Balances::::free_balance(&source), Zero::zero()); assert_eq!(Balances::::free_balance(&recipient), transfer_amount); } @@ -146,10 +172,9 @@ benchmarks_instance_pallet! { // This benchmark performs the same operation as `transfer` in the worst case scenario, // but additionally introduces many new users into the storage, increasing the the merkle // trie and PoV size. - #[extra] - transfer_increasing_users { + #[benchmark(extra)] + fn transfer_increasing_users(u: Linear<0, 1_000>) { // 1_000 is not very much, but this upper bound can be controlled by the CLI. - let u in 0 .. 1_000; let existential_deposit = T::ExistentialDeposit::get(); let caller = whitelisted_caller(); @@ -161,17 +186,20 @@ benchmarks_instance_pallet! { // and reap this user. let recipient: T::AccountId = account("recipient", 0, SEED); let recipient_lookup = T::Lookup::unlookup(recipient.clone()); - let transfer_amount = existential_deposit.saturating_mul((ED_MULTIPLIER - 1).into()) + 1u32.into(); + let transfer_amount = + existential_deposit.saturating_mul((ED_MULTIPLIER - 1).into()) + 1u32.into(); // Create a bunch of users in storage. - for i in 0 .. u { + for i in 0..u { // The `account` function uses `blake2_256` to generate unique accounts, so these // should be quite random and evenly distributed in the trie. let new_user: T::AccountId = account("new_user", i, SEED); let _ = as Currency<_>>::make_free_balance_be(&new_user, balance); } - }: transfer(RawOrigin::Signed(caller.clone()), recipient_lookup, transfer_amount) - verify { + + #[extrinsic_call] + transfer(RawOrigin::Signed(caller.clone()), recipient_lookup, transfer_amount); + assert_eq!(Balances::::free_balance(&caller), Zero::zero()); assert_eq!(Balances::::free_balance(&recipient), transfer_amount); } @@ -179,7 +207,8 @@ benchmarks_instance_pallet! { // Benchmark `transfer_all` with the worst possible condition: // * The recipient account is created // * The sender is killed - transfer_all { + #[benchmark] + fn transfer_all() { let caller = whitelisted_caller(); let recipient: T::AccountId = account("recipient", 0, SEED); let recipient_lookup = T::Lookup::unlookup(recipient.clone()); @@ -188,13 +217,16 @@ benchmarks_instance_pallet! { let existential_deposit = T::ExistentialDeposit::get(); let balance = existential_deposit.saturating_mul(ED_MULTIPLIER.into()); let _ = as Currency<_>>::make_free_balance_be(&caller, balance); - }: _(RawOrigin::Signed(caller.clone()), recipient_lookup, false) - verify { + + #[extrinsic_call] + _(RawOrigin::Signed(caller.clone()), recipient_lookup, false); + assert!(Balances::::free_balance(&caller).is_zero()); assert_eq!(Balances::::free_balance(&recipient), balance); } - force_unreserve { + #[benchmark] + fn force_unreserve() { let user: T::AccountId = account("user", 0, SEED); let user_lookup = T::Lookup::unlookup(user.clone()); @@ -208,15 +240,16 @@ benchmarks_instance_pallet! { assert_eq!(Balances::::reserved_balance(&user), balance); assert!(Balances::::free_balance(&user).is_zero()); - }: _(RawOrigin::Root, user_lookup, balance) - verify { + #[extrinsic_call] + _(RawOrigin::Root, user_lookup, balance); + assert!(Balances::::reserved_balance(&user).is_zero()); assert_eq!(Balances::::free_balance(&user), balance); } - impl_benchmark_test_suite!( + impl_benchmark_test_suite! { Balances, crate::tests_composite::ExtBuilder::default().build(), crate::tests_composite::Test, - ) + } } diff --git a/frame/examples/basic/src/benchmarking.rs b/frame/examples/basic/src/benchmarking.rs index 13f069c23..699e56685 100644 --- a/frame/examples/basic/src/benchmarking.rs +++ b/frame/examples/basic/src/benchmarking.rs @@ -15,12 +15,14 @@ // See the License for the specific language governing permissions and // limitations under the License. -//! Benchmarking for pallet-example-basic. +//! Benchmarking for `pallet-example-basic`. +// Only enable this module for benchmarking. #![cfg(feature = "runtime-benchmarks")] use crate::*; -use frame_benchmarking::{benchmarks, whitelisted_caller}; +use frame_benchmarking::{impl_benchmark_test_suite, whitelisted_caller}; +use frame_support::benchmarking::{benchmarks, Linear}; use frame_system::RawOrigin; // To actually run this benchmark on pallet-example-basic, we need to put this pallet into the @@ -33,14 +35,19 @@ use frame_system::RawOrigin; // Details on using the benchmarks macro can be seen at: // https://paritytech.github.io/substrate/master/frame_benchmarking/trait.Benchmarking.html#tymethod.benchmarks -benchmarks! { +#[benchmarks] +mod benchmarks { + use super::*; + // This will measure the execution time of `set_dummy`. - set_dummy_benchmark { + #[benchmark] + fn set_dummy_benchmark() { // This is the benchmark setup phase. // `set_dummy` is a constant time function, hence we hard-code some random value here. let value = 1000u32.into(); - }: set_dummy(RawOrigin::Root, value) // The execution phase is just running `set_dummy` extrinsic call - verify { + #[extrinsic_call] + set_dummy(RawOrigin::Root, value); // The execution phase is just running `set_dummy` extrinsic call + // This is the optional benchmark verification phase, asserting certain states. assert_eq!(Pallet::::dummy(), Some(value)) } @@ -49,22 +56,43 @@ benchmarks! { // The benchmark execution phase is shorthanded. When the name of the benchmark case is the same // as the extrinsic call. `_(...)` is used to represent the extrinsic name. // The benchmark verification phase is omitted. - accumulate_dummy { + #[benchmark] + fn accumulate_dummy() { let value = 1000u32.into(); // The caller account is whitelisted for DB reads/write by the benchmarking macro. let caller: T::AccountId = whitelisted_caller(); - }: _(RawOrigin::Signed(caller), value) + + // You can use `_` if the name of the Call matches the benchmark name. + #[extrinsic_call] + _(RawOrigin::Signed(caller), value); + } + + /// You can write helper functions in here since its a normal Rust module. + fn setup_vector(len: u32) -> Vec { + let mut vector = Vec::::new(); + for i in (0..len).rev() { + vector.push(i); + } + vector + } // This will measure the execution time of sorting a vector. - sort_vector { - let x in 0 .. 10000; - let mut m = Vec::::new(); - for i in (0..x).rev() { - m.push(i); + // + // Define `x` as a linear component with range `[0, =10_000]`. This means that the benchmarking + // will assume that the weight grows at a linear rate depending on `x`. + #[benchmark] + fn sort_vector(x: Linear<0, 10_000>) { + let mut vector = setup_vector(x); + + // The benchmark execution phase could also be a closure with custom code: + #[block] + { + vector.sort(); } - }: { - // The benchmark execution phase could also be a closure with custom code - m.sort(); + + // Check that it was sorted correctly. This will not be benchmarked and is just for + // verification. + vector.windows(2).for_each(|w| assert!(w[0] <= w[1])); } // This line generates test cases for benchmarking, and could be run by: @@ -75,5 +103,5 @@ benchmarks! { // // The line generates three steps per benchmark, with repeat=1 and the three steps are // [low, mid, high] of the range. - impl_benchmark_test_suite!(Pallet, crate::tests::new_test_ext(), crate::tests::Test) + impl_benchmark_test_suite!(Pallet, crate::tests::new_test_ext(), crate::tests::Test); } diff --git a/frame/message-queue/src/benchmarking.rs b/frame/message-queue/src/benchmarking.rs index 9cd6b75e4..9651c81e5 100644 --- a/frame/message-queue/src/benchmarking.rs +++ b/frame/message-queue/src/benchmarking.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2022 Parity Technologies (UK) Ltd. +// Copyright (C) 2023 Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); @@ -22,68 +22,85 @@ use super::{mock_helpers::*, Pallet as MessageQueue, *}; -use frame_benchmarking::{benchmarks, whitelisted_caller}; -use frame_support::traits::Get; +use frame_benchmarking::{impl_benchmark_test_suite, whitelisted_caller}; +use frame_support::{benchmarking::*, traits::Get}; use frame_system::RawOrigin; use sp_std::prelude::*; -benchmarks! { - where_clause { - where - // NOTE: We need to generate multiple origins, therefore Origin is `From`. The - // `PartialEq` is for asserting the outcome of the ring (un)knitting and *could* be - // removed if really necessary. - <::MessageProcessor as ProcessMessage>::Origin: From + PartialEq, - ::Size: From, - } +#[benchmarks( + where + <::MessageProcessor as ProcessMessage>::Origin: From + PartialEq, + ::Size: From, + // NOTE: We need to generate multiple origins, therefore Origin is `From`. The + // `PartialEq` is for asserting the outcome of the ring (un)knitting and *could* be + // removed if really necessary. +)] +mod benchmarks { + use super::*; // Worst case path of `ready_ring_knit`. - ready_ring_knit { - let mid: MessageOriginOf:: = 1.into(); + #[benchmark] + fn ready_ring_knit() { + let mid: MessageOriginOf = 1.into(); build_ring::(&[0.into(), mid.clone(), 2.into()]); unknit::(&mid); assert_ring::(&[0.into(), 2.into()]); let mut neighbours = None; - }: { - neighbours = MessageQueue::::ready_ring_knit(&mid).ok(); - } verify { + + #[block] + { + neighbours = MessageQueue::::ready_ring_knit(&mid).ok(); + } + // The neighbours needs to be modified manually. - BookStateFor::::mutate(&mid, |b| { b.ready_neighbours = neighbours }); + BookStateFor::::mutate(&mid, |b| b.ready_neighbours = neighbours); assert_ring::(&[0.into(), 2.into(), mid]); } // Worst case path of `ready_ring_unknit`. - ready_ring_unknit { + #[benchmark] + fn ready_ring_unknit() { build_ring::(&[0.into(), 1.into(), 2.into()]); assert_ring::(&[0.into(), 1.into(), 2.into()]); - let o: MessageOriginOf:: = 0.into(); + let o: MessageOriginOf = 0.into(); let neighbours = BookStateFor::::get(&o).ready_neighbours.unwrap(); - }: { - MessageQueue::::ready_ring_unknit(&o, neighbours); - } verify { + + #[block] + { + MessageQueue::::ready_ring_unknit(&o, neighbours); + } + assert_ring::(&[1.into(), 2.into()]); } // `service_queues` without any queue processing. - service_queue_base { - }: { - MessageQueue::::service_queue(0.into(), &mut WeightMeter::max_limit(), Weight::MAX) + #[benchmark] + fn service_queue_base() { + #[block] + { + MessageQueue::::service_queue(0.into(), &mut WeightMeter::max_limit(), Weight::MAX); + } } // `service_page` without any message processing but with page completion. - service_page_base_completion { + #[benchmark] + fn service_page_base_completion() { let origin: MessageOriginOf = 0.into(); let page = PageOf::::default(); Pages::::insert(&origin, 0, &page); let mut book_state = single_page_book::(); let mut meter = WeightMeter::max_limit(); let limit = Weight::MAX; - }: { - MessageQueue::::service_page(&origin, &mut book_state, &mut meter, limit) + + #[block] + { + MessageQueue::::service_page(&origin, &mut book_state, &mut meter, limit); + } } // `service_page` without any message processing and without page completion. - service_page_base_no_completion { + #[benchmark] + fn service_page_base_no_completion() { let origin: MessageOriginOf = 0.into(); let mut page = PageOf::::default(); // Mock the storage such that `is_complete` returns `false` but `peek_first` returns `None`. @@ -93,49 +110,73 @@ benchmarks! { let mut book_state = single_page_book::(); let mut meter = WeightMeter::max_limit(); let limit = Weight::MAX; - }: { - MessageQueue::::service_page(&origin, &mut book_state, &mut meter, limit) + + #[block] + { + MessageQueue::::service_page(&origin, &mut book_state, &mut meter, limit); + } } // Processing a single message from a page. - service_page_item { + #[benchmark] + fn service_page_item() { let msg = vec![1u8; MaxMessageLenOf::::get() as usize]; let mut page = page::(&msg.clone()); let mut book = book_for::(&page); assert!(page.peek_first().is_some(), "There is one message"); let mut weight = WeightMeter::max_limit(); - }: { - let status = MessageQueue::::service_page_item(&0u32.into(), 0, &mut book, &mut page, &mut weight, Weight::MAX); - assert_eq!(status, ItemExecutionStatus::Executed(true)); - } verify { + + #[block] + { + let status = MessageQueue::::service_page_item( + &0u32.into(), + 0, + &mut book, + &mut page, + &mut weight, + Weight::MAX, + ); + assert_eq!(status, ItemExecutionStatus::Executed(true)); + } + // Check that it was processed. - assert_last_event::(Event::Processed { - hash: T::Hashing::hash(&msg), origin: 0.into(), - weight_used: 1.into_weight(), success: true - }.into()); + assert_last_event::( + Event::Processed { + hash: T::Hashing::hash(&msg), + origin: 0.into(), + weight_used: 1.into_weight(), + success: true, + } + .into(), + ); let (_, processed, _) = page.peek_index(0).unwrap(); assert!(processed); assert_eq!(book.message_count, 0); } // Worst case for calling `bump_service_head`. - bump_service_head { + #[benchmark] + fn bump_service_head() { setup_bump_service_head::(0.into(), 10.into()); let mut weight = WeightMeter::max_limit(); - }: { - MessageQueue::::bump_service_head(&mut weight); - } verify { + + #[block] + { + MessageQueue::::bump_service_head(&mut weight); + } + assert_eq!(ServiceHead::::get().unwrap(), 10u32.into()); assert_eq!(weight.consumed, T::WeightInfo::bump_service_head()); } - reap_page { + #[benchmark] + fn reap_page() { // Mock the storage to get a *cullable* but not *reapable* page. let origin: MessageOriginOf = 0.into(); let mut book = single_page_book::(); let (page, msgs) = full_page::(); - for p in 0 .. T::MaxStale::get() * T::MaxStale::get() { + for p in 0..T::MaxStale::get() * T::MaxStale::get() { if p == 0 { Pages::::insert(&origin, p, &page); } @@ -148,16 +189,19 @@ benchmarks! { BookStateFor::::insert(&origin, &book); assert!(Pages::::contains_key(&origin, 0)); - }: _(RawOrigin::Signed(whitelisted_caller()), 0u32.into(), 0) - verify { - assert_last_event::(Event::PageReaped{ origin: 0.into(), index: 0 }.into()); + #[extrinsic_call] + _(RawOrigin::Signed(whitelisted_caller()), 0u32.into(), 0); + + assert_last_event::(Event::PageReaped { origin: 0.into(), index: 0 }.into()); assert!(!Pages::::contains_key(&origin, 0)); } // Worst case for `execute_overweight` where the page is removed as completed. // - // The worst case occurs when executing the last message in a page of which all are skipped since it is using `peek_index` which has linear complexities. - execute_overweight_page_removed { + // The worst case occurs when executing the last message in a page of which all are skipped + // since it is using `peek_index` which has linear complexities. + #[benchmark] + fn execute_overweight_page_removed() { let origin: MessageOriginOf = 0.into(); let (mut page, msgs) = full_page::(); // Skip all messages. @@ -168,19 +212,34 @@ benchmarks! { let book = book_for::(&page); Pages::::insert(&origin, 0, &page); BookStateFor::::insert(&origin, &book); - }: { - MessageQueue::::execute_overweight(RawOrigin::Signed(whitelisted_caller()).into(), 0u32.into(), 0u32, ((msgs - 1) as u32).into(), Weight::MAX).unwrap() - } - verify { - assert_last_event::(Event::Processed { - hash: T::Hashing::hash(&((msgs - 1) as u32).encode()), origin: 0.into(), - weight_used: Weight::from_parts(1, 1), success: true - }.into()); + + #[block] + { + MessageQueue::::execute_overweight( + RawOrigin::Signed(whitelisted_caller()).into(), + 0u32.into(), + 0u32, + ((msgs - 1) as u32).into(), + Weight::MAX, + ) + .unwrap(); + } + + assert_last_event::( + Event::Processed { + hash: T::Hashing::hash(&((msgs - 1) as u32).encode()), + origin: 0.into(), + weight_used: Weight::from_parts(1, 1), + success: true, + } + .into(), + ); assert!(!Pages::::contains_key(&origin, 0), "Page must be removed"); } // Worst case for `execute_overweight` where the page is updated. - execute_overweight_page_updated { + #[benchmark] + fn execute_overweight_page_updated() { let origin: MessageOriginOf = 0.into(); let (mut page, msgs) = full_page::(); // Skip all messages. @@ -190,16 +249,34 @@ benchmarks! { let book = book_for::(&page); Pages::::insert(&origin, 0, &page); BookStateFor::::insert(&origin, &book); - }: { - MessageQueue::::execute_overweight(RawOrigin::Signed(whitelisted_caller()).into(), 0u32.into(), 0u32, ((msgs - 1) as u32).into(), Weight::MAX).unwrap() - } - verify { - assert_last_event::(Event::Processed { - hash: T::Hashing::hash(&((msgs - 1) as u32).encode()), origin: 0.into(), - weight_used: Weight::from_parts(1, 1), success: true - }.into()); + + #[block] + { + MessageQueue::::execute_overweight( + RawOrigin::Signed(whitelisted_caller()).into(), + 0u32.into(), + 0u32, + ((msgs - 1) as u32).into(), + Weight::MAX, + ) + .unwrap(); + } + + assert_last_event::( + Event::Processed { + hash: T::Hashing::hash(&((msgs - 1) as u32).encode()), + origin: 0.into(), + weight_used: Weight::from_parts(1, 1), + success: true, + } + .into(), + ); assert!(Pages::::contains_key(&origin, 0), "Page must be updated"); } - impl_benchmark_test_suite!(MessageQueue, crate::mock::new_test_ext::(), crate::integration_test::Test); + impl_benchmark_test_suite! { + MessageQueue, + crate::mock::new_test_ext::(), + crate::integration_test::Test + } } diff --git a/frame/support/Cargo.toml b/frame/support/Cargo.toml index 4f62ae42e..ae2fc3589 100644 --- a/frame/support/Cargo.toml +++ b/frame/support/Cargo.toml @@ -27,6 +27,7 @@ sp-arithmetic = { version = "6.0.0", default-features = false, path = "../../pri sp-inherents = { version = "4.0.0-dev", default-features = false, path = "../../primitives/inherents" } sp-staking = { version = "4.0.0-dev", default-features = false, path = "../../primitives/staking" } sp-weights = { version = "4.0.0", default-features = false, path = "../../primitives/weights" } +static_assertions = "1.1.0" tt-call = "1.0.8" frame-support-procedural = { version = "4.0.0-dev", default-features = false, path = "./procedural" } paste = "1.0" diff --git a/frame/support/procedural/Cargo.toml b/frame/support/procedural/Cargo.toml index 06b8056af..ee1ca4dff 100644 --- a/frame/support/procedural/Cargo.toml +++ b/frame/support/procedural/Cargo.toml @@ -15,6 +15,7 @@ targets = ["x86_64-unknown-linux-gnu"] proc-macro = true [dependencies] +derive-syn-parse = "0.1.5" Inflector = "0.11.4" cfg-expr = "0.10.3" itertools = "0.10.3" diff --git a/frame/support/procedural/src/benchmark.rs b/frame/support/procedural/src/benchmark.rs new file mode 100644 index 000000000..43e3e47de --- /dev/null +++ b/frame/support/procedural/src/benchmark.rs @@ -0,0 +1,860 @@ +// This file is part of Substrate. + +// Copyright (C) 2023 Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +//! Home of the parsing and expansion code for the new pallet benchmarking syntax + +use derive_syn_parse::Parse; +use frame_support_procedural_tools::generate_crate_access_2018; +use proc_macro::TokenStream; +use proc_macro2::{Ident, Span, TokenStream as TokenStream2}; +use quote::{quote, quote_spanned, ToTokens}; +use syn::{ + parenthesized, + parse::{Nothing, ParseStream}, + punctuated::Punctuated, + spanned::Spanned, + token::{Colon2, Comma, Gt, Lt, Paren}, + Attribute, Error, Expr, ExprBlock, ExprCall, ExprPath, FnArg, Item, ItemFn, ItemMod, LitInt, + Pat, Path, PathArguments, PathSegment, Result, Stmt, Token, Type, WhereClause, +}; + +mod keywords { + use syn::custom_keyword; + + custom_keyword!(benchmark); + custom_keyword!(benchmarks); + custom_keyword!(block); + custom_keyword!(extra); + custom_keyword!(extrinsic_call); + custom_keyword!(skip_meta); +} + +/// This represents the raw parsed data for a param definition such as `x: Linear<10, 20>`. +#[derive(Clone)] +struct ParamDef { + name: String, + typ: Type, + start: u32, + end: u32, +} + +/// Allows easy parsing of the `<10, 20>` component of `x: Linear<10, 20>`. +#[derive(Parse)] +struct RangeArgs { + _lt_token: Lt, + start: LitInt, + _comma: Comma, + end: LitInt, + _gt_token: Gt, +} + +#[derive(Clone, Debug)] +struct BenchmarkAttrs { + skip_meta: bool, + extra: bool, +} + +/// Represents a single benchmark option +enum BenchmarkAttrKeyword { + Extra, + SkipMeta, +} + +impl syn::parse::Parse for BenchmarkAttrKeyword { + fn parse(input: ParseStream) -> Result { + let lookahead = input.lookahead1(); + if lookahead.peek(keywords::extra) { + let _extra: keywords::extra = input.parse()?; + return Ok(BenchmarkAttrKeyword::Extra) + } else if lookahead.peek(keywords::skip_meta) { + let _skip_meta: keywords::skip_meta = input.parse()?; + return Ok(BenchmarkAttrKeyword::SkipMeta) + } else { + return Err(lookahead.error()) + } + } +} + +impl syn::parse::Parse for BenchmarkAttrs { + fn parse(input: ParseStream) -> syn::Result { + let lookahead = input.lookahead1(); + if !lookahead.peek(Paren) { + let _nothing: Nothing = input.parse()?; + return Ok(BenchmarkAttrs { skip_meta: false, extra: false }) + } + let content; + let _paren: Paren = parenthesized!(content in input); + let mut extra = false; + let mut skip_meta = false; + let args = Punctuated::::parse_terminated(&content)?; + for arg in args.into_iter() { + match arg { + BenchmarkAttrKeyword::Extra => { + if extra { + return Err(content.error("`extra` can only be specified once")) + } + extra = true; + }, + BenchmarkAttrKeyword::SkipMeta => { + if skip_meta { + return Err(content.error("`skip_meta` can only be specified once")) + } + skip_meta = true; + }, + } + } + Ok(BenchmarkAttrs { extra, skip_meta }) + } +} + +/// Represents the parsed extrinsic call for a benchmark +#[derive(Clone)] +enum BenchmarkCallDef { + ExtrinsicCall { origin: Expr, expr_call: ExprCall, attr_span: Span }, // #[extrinsic_call] + Block { block: ExprBlock, attr_span: Span }, // #[block] +} + +impl BenchmarkCallDef { + /// Returns the `span()` for attribute + fn attr_span(&self) -> Span { + match self { + BenchmarkCallDef::ExtrinsicCall { origin: _, expr_call: _, attr_span } => *attr_span, + BenchmarkCallDef::Block { block: _, attr_span } => *attr_span, + } + } +} + +/// Represents a parsed `#[benchmark]` or `#[instance_banchmark]` item. +#[derive(Clone)] +struct BenchmarkDef { + params: Vec, + setup_stmts: Vec, + call_def: BenchmarkCallDef, + verify_stmts: Vec, + extra: bool, + skip_meta: bool, +} + +impl BenchmarkDef { + /// Constructs a [`BenchmarkDef`] by traversing an existing [`ItemFn`] node. + pub fn from(item_fn: &ItemFn, extra: bool, skip_meta: bool) -> Result { + let mut params: Vec = Vec::new(); + + // parse params such as "x: Linear<0, 1>" + for arg in &item_fn.sig.inputs { + let invalid_param = |span| { + return Err(Error::new(span, "Invalid benchmark function param. A valid example would be `x: Linear<5, 10>`.", )) + }; + + let FnArg::Typed(arg) = arg else { return invalid_param(arg.span()) }; + let Pat::Ident(ident) = &*arg.pat else { return invalid_param(arg.span()) }; + + // check param name + let var_span = ident.span(); + let invalid_param_name = || { + return Err(Error::new( + var_span, + "Benchmark parameter names must consist of a single lowercase letter (a-z) and no other characters.", + )) + }; + let name = ident.ident.to_token_stream().to_string(); + if name.len() > 1 { + return invalid_param_name() + }; + let Some(name_char) = name.chars().next() else { return invalid_param_name() }; + if !name_char.is_alphabetic() || !name_char.is_lowercase() { + return invalid_param_name() + } + + // parse type + let typ = &*arg.ty; + let Type::Path(tpath) = typ else { return invalid_param(typ.span()) }; + let Some(segment) = tpath.path.segments.last() else { return invalid_param(typ.span()) }; + let args = segment.arguments.to_token_stream().into(); + let Ok(args) = syn::parse::(args) else { return invalid_param(typ.span()) }; + let Ok(start) = args.start.base10_parse::() else { return invalid_param(args.start.span()) }; + let Ok(end) = args.end.base10_parse::() else { return invalid_param(args.end.span()) }; + + if end < start { + return Err(Error::new( + args.start.span(), + "The start of a `ParamRange` must be less than or equal to the end", + )) + } + + params.push(ParamDef { name, typ: typ.clone(), start, end }); + } + + // #[extrinsic_call] / #[block] handling + let call_defs = item_fn.block.stmts.iter().enumerate().filter_map(|(i, child)| { + if let Stmt::Semi(Expr::Call(expr_call), _semi) = child { + // #[extrinsic_call] case + expr_call.attrs.iter().enumerate().find_map(|(k, attr)| { + let segment = attr.path.segments.last()?; + let _: keywords::extrinsic_call = syn::parse(segment.ident.to_token_stream().into()).ok()?; + let mut expr_call = expr_call.clone(); + + // consume #[extrinsic_call] tokens + expr_call.attrs.remove(k); + + // extract origin from expr_call + let Some(origin) = expr_call.args.first().cloned() else { + return Some(Err(Error::new(expr_call.span(), "Single-item extrinsic calls must specify their origin as the first argument."))) + }; + + Some(Ok((i, BenchmarkCallDef::ExtrinsicCall { origin, expr_call, attr_span: attr.span() }))) + }) + } else if let Stmt::Expr(Expr::Block(block)) = child { + // #[block] case + block.attrs.iter().enumerate().find_map(|(k, attr)| { + let segment = attr.path.segments.last()?; + let _: keywords::block = syn::parse(segment.ident.to_token_stream().into()).ok()?; + let mut block = block.clone(); + + // consume #[block] tokens + block.attrs.remove(k); + + Some(Ok((i, BenchmarkCallDef::Block { block, attr_span: attr.span() }))) + }) + } else { + None + } + }).collect::>>()?; + let (i, call_def) = match &call_defs[..] { + [(i, call_def)] => (*i, call_def.clone()), // = 1 + [] => return Err(Error::new( // = 0 + item_fn.block.brace_token.span, + "No valid #[extrinsic_call] or #[block] annotation could be found in benchmark function body." + )), + _ => return Err(Error::new( // > 1 + call_defs[1].1.attr_span(), + "Only one #[extrinsic_call] or #[block] attribute is allowed per benchmark." + )), + }; + + Ok(BenchmarkDef { + params, + setup_stmts: Vec::from(&item_fn.block.stmts[0..i]), + call_def, + verify_stmts: Vec::from(&item_fn.block.stmts[(i + 1)..item_fn.block.stmts.len()]), + extra, + skip_meta, + }) + } +} + +/// Parses and expands a `#[benchmarks]` or `#[instance_benchmarks]` invocation +pub fn benchmarks( + attrs: TokenStream, + tokens: TokenStream, + instance: bool, +) -> syn::Result { + // gather module info + let module: ItemMod = syn::parse(tokens)?; + let mod_span = module.span(); + let where_clause = match syn::parse::(attrs.clone()) { + Ok(_) => quote!(), + Err(_) => syn::parse::(attrs)?.predicates.to_token_stream(), + }; + let mod_vis = module.vis; + let mod_name = module.ident; + + // consume #[benchmarks] attribute by exclusing it from mod_attrs + let mod_attrs: Vec<&Attribute> = module + .attrs + .iter() + .filter(|attr| syn::parse2::(attr.to_token_stream()).is_err()) + .collect(); + + let mut benchmark_names: Vec = Vec::new(); + let mut extra_benchmark_names: Vec = Vec::new(); + let mut skip_meta_benchmark_names: Vec = Vec::new(); + + let (_brace, mut content) = + module.content.ok_or(syn::Error::new(mod_span, "Module cannot be empty!"))?; + + // find all function defs marked with #[benchmark] + let benchmark_fn_metas = content.iter_mut().filter_map(|stmt| { + // parse as a function def first + let Item::Fn(func) = stmt else { return None }; + + // find #[benchmark] attribute on function def + let benchmark_attr = func.attrs.iter().find_map(|attr| { + let seg = attr.path.segments.last()?; + syn::parse::(seg.ident.to_token_stream().into()).ok()?; + Some(attr) + })?; + + Some((benchmark_attr.clone(), func.clone(), stmt)) + }); + + // parse individual benchmark defs and args + for (benchmark_attr, func, stmt) in benchmark_fn_metas { + // parse any args provided to #[benchmark] + let attr_tokens = benchmark_attr.tokens.to_token_stream().into(); + let benchmark_args: BenchmarkAttrs = syn::parse(attr_tokens)?; + + // parse benchmark def + let benchmark_def = + BenchmarkDef::from(&func, benchmark_args.extra, benchmark_args.skip_meta)?; + + // record benchmark name + let name = &func.sig.ident; + benchmark_names.push(name.clone()); + + // record name sets + if benchmark_def.extra { + extra_benchmark_names.push(name.clone()); + } + if benchmark_def.skip_meta { + skip_meta_benchmark_names.push(name.clone()) + } + + // expand benchmark + let expanded = expand_benchmark(benchmark_def, name, instance, where_clause.clone()); + + // replace original function def with expanded code + *stmt = Item::Verbatim(expanded); + } + + // generics + let type_use_generics = match instance { + false => quote!(T), + true => quote!(T, I), + }; + let type_impl_generics = match instance { + false => quote!(T: Config), + true => quote!(T: Config, I: 'static), + }; + + let krate = generate_crate_access_2018("frame-benchmarking")?; + let support = quote!(#krate::frame_support); + + // benchmark name variables + let benchmark_names_str: Vec = benchmark_names.iter().map(|n| n.to_string()).collect(); + let extra_benchmark_names_str: Vec = + extra_benchmark_names.iter().map(|n| n.to_string()).collect(); + let skip_meta_benchmark_names_str: Vec = + skip_meta_benchmark_names.iter().map(|n| n.to_string()).collect(); + let mut selected_benchmark_mappings: Vec = Vec::new(); + let mut benchmarks_by_name_mappings: Vec = Vec::new(); + let test_idents: Vec = benchmark_names_str + .iter() + .map(|n| Ident::new(format!("test_{}", n).as_str(), Span::call_site())) + .collect(); + for i in 0..benchmark_names.len() { + let name_ident = &benchmark_names[i]; + let name_str = &benchmark_names_str[i]; + let test_ident = &test_idents[i]; + selected_benchmark_mappings.push(quote!(#name_str => SelectedBenchmark::#name_ident)); + benchmarks_by_name_mappings.push(quote!(#name_str => Self::#test_ident())) + } + + // emit final quoted tokens + let res = quote! { + #(#mod_attrs) + * + #mod_vis mod #mod_name { + #(#content) + * + + #[allow(non_camel_case_types)] + enum SelectedBenchmark { + #(#benchmark_names), + * + } + + impl<#type_impl_generics> #krate::BenchmarkingSetup<#type_use_generics> for SelectedBenchmark where #where_clause { + fn components(&self) -> #krate::Vec<(#krate::BenchmarkParameter, u32, u32)> { + match self { + #( + Self::#benchmark_names => { + <#benchmark_names as #krate::BenchmarkingSetup<#type_use_generics>>::components(&#benchmark_names) + } + ) + * + } + } + + fn instance( + &self, + components: &[(#krate::BenchmarkParameter, u32)], + verify: bool, + ) -> Result< + #krate::Box Result<(), #krate::BenchmarkError>>, + #krate::BenchmarkError, + > { + match self { + #( + Self::#benchmark_names => { + <#benchmark_names as #krate::BenchmarkingSetup< + #type_use_generics + >>::instance(&#benchmark_names, components, verify) + } + ) + * + } + } + } + #[cfg(any(feature = "runtime-benchmarks", test))] + impl<#type_impl_generics> #krate::Benchmarking for Pallet<#type_use_generics> + where T: frame_system::Config, #where_clause + { + fn benchmarks( + extra: bool, + ) -> #krate::Vec<#krate::BenchmarkMetadata> { + let mut all_names = #krate::vec![ + #(#benchmark_names_str), + * + ]; + if !extra { + let extra = [ + #(#extra_benchmark_names_str), + * + ]; + all_names.retain(|x| !extra.contains(x)); + } + all_names.into_iter().map(|benchmark| { + let selected_benchmark = match benchmark { + #(#selected_benchmark_mappings), + *, + _ => panic!("all benchmarks should be selectable") + }; + let components = >::components(&selected_benchmark); + #krate::BenchmarkMetadata { + name: benchmark.as_bytes().to_vec(), + components, + } + }).collect::<#krate::Vec<_>>() + } + + fn run_benchmark( + extrinsic: &[u8], + c: &[(#krate::BenchmarkParameter, u32)], + whitelist: &[#krate::TrackedStorageKey], + verify: bool, + internal_repeats: u32, + ) -> Result<#krate::Vec<#krate::BenchmarkResult>, #krate::BenchmarkError> { + let extrinsic = #krate::str::from_utf8(extrinsic).map_err(|_| "`extrinsic` is not a valid utf-8 string!")?; + let selected_benchmark = match extrinsic { + #(#selected_benchmark_mappings), + *, + _ => return Err("Could not find extrinsic.".into()), + }; + let mut whitelist = whitelist.to_vec(); + let whitelisted_caller_key = as #support::storage::StorageMap<_, _,>>::hashed_key_for( + #krate::whitelisted_caller::() + ); + whitelist.push(whitelisted_caller_key.into()); + let transactional_layer_key = #krate::TrackedStorageKey::new( + #support::storage::transactional::TRANSACTION_LEVEL_KEY.into(), + ); + whitelist.push(transactional_layer_key); + #krate::benchmarking::set_whitelist(whitelist); + let mut results: #krate::Vec<#krate::BenchmarkResult> = #krate::Vec::new(); + + // Always do at least one internal repeat... + for _ in 0 .. internal_repeats.max(1) { + // Always reset the state after the benchmark. + #krate::defer!(#krate::benchmarking::wipe_db()); + + // Set up the externalities environment for the setup we want to + // benchmark. + let closure_to_benchmark = < + SelectedBenchmark as #krate::BenchmarkingSetup<#type_use_generics> + >::instance(&selected_benchmark, c, verify)?; + + // Set the block number to at least 1 so events are deposited. + if #krate::Zero::is_zero(&frame_system::Pallet::::block_number()) { + frame_system::Pallet::::set_block_number(1u32.into()); + } + + // Commit the externalities to the database, flushing the DB cache. + // This will enable worst case scenario for reading from the database. + #krate::benchmarking::commit_db(); + + // Reset the read/write counter so we don't count operations in the setup process. + #krate::benchmarking::reset_read_write_count(); + + // Time the extrinsic logic. + #krate::log::trace!( + target: "benchmark", + "Start Benchmark: {} ({:?})", + extrinsic, + c + ); + + let start_pov = #krate::benchmarking::proof_size(); + let start_extrinsic = #krate::benchmarking::current_time(); + + closure_to_benchmark()?; + + let finish_extrinsic = #krate::benchmarking::current_time(); + let end_pov = #krate::benchmarking::proof_size(); + + // Calculate the diff caused by the benchmark. + let elapsed_extrinsic = finish_extrinsic.saturating_sub(start_extrinsic); + let diff_pov = match (start_pov, end_pov) { + (Some(start), Some(end)) => end.saturating_sub(start), + _ => Default::default(), + }; + + // Commit the changes to get proper write count + #krate::benchmarking::commit_db(); + #krate::log::trace!( + target: "benchmark", + "End Benchmark: {} ns", elapsed_extrinsic + ); + let read_write_count = #krate::benchmarking::read_write_count(); + #krate::log::trace!( + target: "benchmark", + "Read/Write Count {:?}", read_write_count + ); + + // Time the storage root recalculation. + let start_storage_root = #krate::benchmarking::current_time(); + #krate::storage_root(#krate::StateVersion::V1); + let finish_storage_root = #krate::benchmarking::current_time(); + let elapsed_storage_root = finish_storage_root - start_storage_root; + + let skip_meta = [ #(#skip_meta_benchmark_names_str),* ]; + let read_and_written_keys = if skip_meta.contains(&extrinsic) { + #krate::vec![(b"Skipped Metadata".to_vec(), 0, 0, false)] + } else { + #krate::benchmarking::get_read_and_written_keys() + }; + + results.push(#krate::BenchmarkResult { + components: c.to_vec(), + extrinsic_time: elapsed_extrinsic, + storage_root_time: elapsed_storage_root, + reads: read_write_count.0, + repeat_reads: read_write_count.1, + writes: read_write_count.2, + repeat_writes: read_write_count.3, + proof_size: diff_pov, + keys: read_and_written_keys, + }); + } + + return Ok(results); + } + } + + #[cfg(test)] + impl<#type_impl_generics> Pallet<#type_use_generics> where T: ::frame_system::Config, #where_clause { + /// Test a particular benchmark by name. + /// + /// This isn't called `test_benchmark_by_name` just in case some end-user eventually + /// writes a benchmark, itself called `by_name`; the function would be shadowed in + /// that case. + /// + /// This is generally intended to be used by child test modules such as those created + /// by the `impl_benchmark_test_suite` macro. However, it is not an error if a pallet + /// author chooses not to implement benchmarks. + #[allow(unused)] + fn test_bench_by_name(name: &[u8]) -> Result<(), #krate::BenchmarkError> { + let name = #krate::str::from_utf8(name) + .map_err(|_| -> #krate::BenchmarkError { "`name` is not a valid utf8 string!".into() })?; + match name { + #(#benchmarks_by_name_mappings), + *, + _ => Err("Could not find test for requested benchmark.".into()), + } + } + } + } + #mod_vis use #mod_name::*; + }; + Ok(res.into()) +} + +/// Prepares a [`Vec`] to be interpolated by [`quote!`] by creating easily-iterable +/// arrays formatted in such a way that they can be interpolated directly. +struct UnrolledParams { + param_ranges: Vec, + param_names: Vec, + param_types: Vec, +} + +impl UnrolledParams { + /// Constructs an [`UnrolledParams`] from a [`Vec`] + fn from(params: &Vec) -> UnrolledParams { + let param_ranges: Vec = params + .iter() + .map(|p| { + let name = Ident::new(&p.name, Span::call_site()); + let start = p.start; + let end = p.end; + quote!(#name, #start, #end) + }) + .collect(); + let param_names: Vec = params + .iter() + .map(|p| { + let name = Ident::new(&p.name, Span::call_site()); + quote!(#name) + }) + .collect(); + let param_types: Vec = params + .iter() + .map(|p| { + let typ = &p.typ; + quote!(#typ) + }) + .collect(); + UnrolledParams { param_ranges, param_names, param_types } + } +} + +/// Performs expansion of an already-parsed [`BenchmarkDef`]. +fn expand_benchmark( + benchmark_def: BenchmarkDef, + name: &Ident, + is_instance: bool, + where_clause: TokenStream2, +) -> TokenStream2 { + // set up variables needed during quoting + let krate = match generate_crate_access_2018("frame-benchmarking") { + Ok(ident) => ident, + Err(err) => return err.to_compile_error().into(), + }; + let home = quote!(#krate::frame_support::benchmarking); + let codec = quote!(#krate::frame_support::codec); + let traits = quote!(#krate::frame_support::traits); + let setup_stmts = benchmark_def.setup_stmts; + let verify_stmts = benchmark_def.verify_stmts; + let test_ident = Ident::new(format!("test_{}", name.to_string()).as_str(), Span::call_site()); + + // unroll params (prepare for quoting) + let unrolled = UnrolledParams::from(&benchmark_def.params); + let param_names = unrolled.param_names; + let param_ranges = unrolled.param_ranges; + let param_types = unrolled.param_types; + + let type_use_generics = match is_instance { + false => quote!(T), + true => quote!(T, I), + }; + + let type_impl_generics = match is_instance { + false => quote!(T: Config), + true => quote!(T: Config, I: 'static), + }; + + let (pre_call, post_call) = match benchmark_def.call_def { + BenchmarkCallDef::ExtrinsicCall { origin, expr_call, attr_span: _ } => { + let mut expr_call = expr_call.clone(); + + // remove first arg from expr_call + let mut final_args = Punctuated::::new(); + let args: Vec<&Expr> = expr_call.args.iter().collect(); + for arg in &args[1..] { + final_args.push((*(*arg)).clone()); + } + expr_call.args = final_args; + + // determine call name (handles `_` and normal call syntax) + let expr_span = expr_call.span(); + let call_err = || { + quote_spanned!(expr_span=> "Extrinsic call must be a function call or `_`".to_compile_error()).into() + }; + let call_name = match *expr_call.func { + Expr::Path(expr_path) => { + // normal function call + let Some(segment) = expr_path.path.segments.last() else { return call_err(); }; + segment.ident.to_string() + }, + Expr::Verbatim(tokens) => { + // `_` style + // replace `_` with fn name + let Ok(_) = syn::parse::(tokens.to_token_stream().into()) else { return call_err(); }; + name.to_string() + }, + _ => return call_err(), + }; + + // modify extrinsic call to be prefixed with "new_call_variant" + let call_name = format!("new_call_variant_{}", call_name); + let mut punct: Punctuated = Punctuated::new(); + punct.push(PathSegment { + arguments: PathArguments::None, + ident: Ident::new(call_name.as_str(), Span::call_site()), + }); + *expr_call.func = Expr::Path(ExprPath { + attrs: vec![], + qself: None, + path: Path { leading_colon: None, segments: punct }, + }); + + ( + // (pre_call, post_call): + quote! { + let __call = Call::<#type_use_generics>::#expr_call; + let __benchmarked_call_encoded = #codec::Encode::encode(&__call); + }, + quote! { + let __call_decoded = as #codec::Decode> + ::decode(&mut &__benchmarked_call_encoded[..]) + .expect("call is encoded above, encoding must be correct"); + let __origin = #origin.into(); + as #traits::UnfilteredDispatchable>::dispatch_bypass_filter( + __call_decoded, + __origin, + )?; + }, + ) + }, + BenchmarkCallDef::Block { block, attr_span: _ } => (quote!(), quote!(#block)), + }; + + // generate final quoted tokens + let res = quote! { + // compile-time assertions that each referenced param type implements ParamRange + #( + #home::assert_impl_all!(#param_types: #home::ParamRange); + )* + + #[allow(non_camel_case_types)] + struct #name; + + #[allow(unused_variables)] + impl<#type_impl_generics> #krate::BenchmarkingSetup<#type_use_generics> + for #name where #where_clause { + fn components(&self) -> #krate::Vec<(#krate::BenchmarkParameter, u32, u32)> { + #krate::vec! [ + #( + (#krate::BenchmarkParameter::#param_ranges) + ),* + ] + } + + fn instance( + &self, + components: &[(#krate::BenchmarkParameter, u32)], + verify: bool + ) -> Result<#krate::Box Result<(), #krate::BenchmarkError>>, #krate::BenchmarkError> { + #( + // prepare instance #param_names + let #param_names = components.iter() + .find(|&c| c.0 == #krate::BenchmarkParameter::#param_names) + .ok_or("Could not find component during benchmark preparation.")? + .1; + )* + + // benchmark setup code + #( + #setup_stmts + )* + #pre_call + Ok(#krate::Box::new(move || -> Result<(), #krate::BenchmarkError> { + #post_call + if verify { + #( + #verify_stmts + )* + } + Ok(()) + })) + } + } + + #[cfg(test)] + impl<#type_impl_generics> Pallet<#type_use_generics> where T: ::frame_system::Config, #where_clause { + #[allow(unused)] + fn #test_ident() -> Result<(), #krate::BenchmarkError> { + let selected_benchmark = SelectedBenchmark::#name; + let components = < + SelectedBenchmark as #krate::BenchmarkingSetup + >::components(&selected_benchmark); + let execute_benchmark = | + c: #krate::Vec<(#krate::BenchmarkParameter, u32)> + | -> Result<(), #krate::BenchmarkError> { + // Always reset the state after the benchmark. + #krate::defer!(#krate::benchmarking::wipe_db()); + + // Set up the benchmark, return execution + verification function. + let closure_to_verify = < + SelectedBenchmark as #krate::BenchmarkingSetup + >::instance(&selected_benchmark, &c, true)?; + + // Set the block number to at least 1 so events are deposited. + if #krate::Zero::is_zero(&frame_system::Pallet::::block_number()) { + frame_system::Pallet::::set_block_number(1u32.into()); + } + + // Run execution + verification + closure_to_verify() + }; + + if components.is_empty() { + execute_benchmark(Default::default())?; + } else { + let num_values: u32 = if let Ok(ev) = std::env::var("VALUES_PER_COMPONENT") { + ev.parse().map_err(|_| { + #krate::BenchmarkError::Stop( + "Could not parse env var `VALUES_PER_COMPONENT` as u32." + ) + })? + } else { + 6 + }; + + if num_values < 2 { + return Err("`VALUES_PER_COMPONENT` must be at least 2".into()); + } + + for (name, low, high) in components.clone().into_iter() { + // Test the lowest, highest (if its different from the lowest) + // and up to num_values-2 more equidistant values in between. + // For 0..10 and num_values=6 this would mean: [0, 2, 4, 6, 8, 10] + + let mut values = #krate::vec![low]; + let diff = (high - low).min(num_values - 1); + let slope = (high - low) as f32 / diff as f32; + + for i in 1..=diff { + let value = ((low as f32 + slope * i as f32) as u32) + .clamp(low, high); + values.push(value); + } + + for component_value in values { + // Select the max value for all the other components. + let c: #krate::Vec<(#krate::BenchmarkParameter, u32)> = components + .iter() + .map(|(n, _, h)| + if *n == name { + (*n, component_value) + } else { + (*n, *h) + } + ) + .collect(); + + execute_benchmark(c)?; + } + } + } + return Ok(()); + } + } + }; + res +} diff --git a/frame/support/procedural/src/lib.rs b/frame/support/procedural/src/lib.rs index 41dbc4ee9..9bdbbd1f2 100644 --- a/frame/support/procedural/src/lib.rs +++ b/frame/support/procedural/src/lib.rs @@ -19,6 +19,7 @@ #![recursion_limit = "512"] +mod benchmark; mod clone_no_bound; mod construct_runtime; mod crate_version; @@ -479,6 +480,69 @@ pub fn pallet(attr: TokenStream, item: TokenStream) -> TokenStream { pallet::pallet(attr, item) } +/// An attribute macro that can be attached to a (non-empty) module declaration. Doing so will +/// designate that module as a benchmarking module. +/// +/// See `frame_support::benchmarking` for more info. +#[proc_macro_attribute] +pub fn benchmarks(attr: TokenStream, tokens: TokenStream) -> TokenStream { + match benchmark::benchmarks(attr, tokens, false) { + Ok(tokens) => tokens, + Err(err) => err.to_compile_error().into(), + } +} + +/// An attribute macro that can be attached to a (non-empty) module declaration. Doing so will +/// designate that module as an instance benchmarking module. +/// +/// See `frame_support::benchmarking` for more info. +#[proc_macro_attribute] +pub fn instance_benchmarks(attr: TokenStream, tokens: TokenStream) -> TokenStream { + match benchmark::benchmarks(attr, tokens, true) { + Ok(tokens) => tokens, + Err(err) => err.to_compile_error().into(), + } +} + +/// An attribute macro used to declare a benchmark within a benchmarking module. Must be +/// attached to a function definition containing an `#[extrinsic_call]` or `#[block]` +/// attribute. +/// +/// See `frame_support::benchmarking` for more info. +#[proc_macro_attribute] +pub fn benchmark(_attrs: TokenStream, _tokens: TokenStream) -> TokenStream { + quote!(compile_error!( + "`#[benchmark]` must be in a module labeled with #[benchmarks] or #[instance_benchmarks]." + )) + .into() +} + +/// An attribute macro used to specify the extrinsic call inside a benchmark function, and also +/// used as a boundary designating where the benchmark setup code ends, and the benchmark +/// verification code begins. +/// +/// See `frame_support::benchmarking` for more info. +#[proc_macro_attribute] +pub fn extrinsic_call(_attrs: TokenStream, _tokens: TokenStream) -> TokenStream { + quote!(compile_error!( + "`#[extrinsic_call]` must be in a benchmark function definition labeled with `#[benchmark]`." + );) + .into() +} + +/// An attribute macro used to specify that a block should be the measured portion of the +/// enclosing benchmark function, This attribute is also used as a boundary designating where +/// the benchmark setup code ends, and the benchmark verification code begins. +/// +/// See `frame_support::benchmarking` for more info. +#[proc_macro_attribute] +pub fn block(_attrs: TokenStream, _tokens: TokenStream) -> TokenStream { + quote!(compile_error!( + "`#[block]` must be in a benchmark function definition labeled with `#[benchmark]`." + )) + .into() +} + /// Execute the annotated function in a new storage transaction. /// /// The return type of the annotated function must be `Result`. All changes to storage performed diff --git a/frame/support/src/lib.rs b/frame/support/src/lib.rs index 902893972..40bc878cf 100644 --- a/frame/support/src/lib.rs +++ b/frame/support/src/lib.rs @@ -2745,5 +2745,228 @@ pub mod pallet_macros { }; } +/// Contains macros, structs, and traits associated with v2 of the pallet benchmarking syntax. +/// This module contains macros, structs, and traits associated with v2 of the pallet +/// benchmarking syntax. +/// +/// The [`benchmarking::benchmarks`] and [`benchmarking::instance_benchmarks`] macros can be +/// used to designate a module as a benchmarking module that can contain benchmarks and +/// benchmark tests. The `#[benchmarks]` variant will set up a regular, non-instance +/// benchmarking module, and the `#[instance_benchmarks]` variant will set up the module in +/// instance benchmarking mode. +/// +/// Benchmarking modules should be gated behind a `#[cfg(feature = "runtime-benchmarks")]` +/// feature gate to ensure benchmarking code that is only compiled when the +/// `runtime-benchmarks` feature is enabled is not referenced. +/// +/// The following is the general syntax for a benchmarks (or instance benchmarks) module: +/// +/// ## General Syntax +/// +/// ```ignore +/// #![cfg(feature = "runtime-benchmarks")] +/// +/// use super::{mock_helpers::*, Pallet as MyPallet}; +/// use frame_support::benchmarking::*; +/// use frame_benchmarking::whitelisted_caller; +/// +/// #[benchmarks] +/// mod benchmarks { +/// use super::*; +/// +/// #[benchmark] +/// fn bench_name_1(x: Linear<7, 1_000>, y: Linear<1_000, 100_0000>) { +/// // setup code +/// let z = x + y; +/// let caller = whitelisted_caller(); +/// +/// #[extrinsic_call] +/// extrinsic_name(SystemOrigin::Signed(caller), other, arguments); +/// +/// // verification code +/// assert_eq!(MyPallet::::my_var(), z); +/// } +/// +/// #[benchmark] +/// fn bench_name_2() { +/// // setup code +/// let caller = whitelisted_caller(); +/// +/// #[block] +/// { +/// something(some, thing); +/// my_extrinsic(RawOrigin::Signed(caller), some, argument); +/// something_else(foo, bar); +/// } +/// +/// // verification code +/// assert_eq!(MyPallet::::something(), 37); +/// } +/// } +/// ``` +/// +/// ## Benchmark Definitions +/// +/// Within a `#[benchmarks]` or `#[instance_benchmarks]` module, you can define individual +/// benchmarks using the `#[benchmark]` attribute, as shown in the example above. +/// +/// The `#[benchmark]` attribute expects a function definition with a blank return type and +/// zero or more arguments whose names are valid `frame_benchmarking::BenchmarkParamater` +/// parameters, such as `x`, `y`, `a`, `b`, etc., and whose param types must implement +/// [`benchmarking::ParamRange`]. At the moment the only valid type that implements +/// [`benchmarking::ParamRange`] is [`benchmarking::Linear`]. +/// +/// The valid syntax for defining a `Linear` is `Linear` where `A`, and `B` are +/// valid integer literals (that fit in a `u32`), such that `B` >= `A`. +/// +/// Note that the benchmark function definition does not actually expand as a function +/// definition, but rather is used to automatically create a number of impls and structs +/// required by the benchmarking engine. For this reason, the visibility of the function +/// definition as well as the return type are not used for any purpose and are discarded by the +/// expansion code. +/// +/// Also note that the `// setup code` and `// verification code` comments shown above are not +/// required and are included simply for demonstration purposes. +/// +/// ### `#[extrinsic_call]` and `#[block]` +/// +/// Within the benchmark function body, either an `#[extrinsic_call]` or a `#[block]` +/// annotation is required. These attributes should be attached to a block (shown in +/// `bench_name_2` above) or a one-line function call (shown in `bench_name_1` above, in `syn` +/// parlance this should be an `ExprCall`), respectively. +/// +/// The `#[block]` syntax is broad and will benchmark any code contained within the block the +/// attribute is attached to. If `#[block]` is attached to something other than a block, a +/// compiler error will be emitted. +/// +/// The one-line `#[extrinsic_call]` syntax must consist of a function call to an extrinsic, +/// where the first argument is the origin. If `#[extrinsic_call]` is attached to an item that +/// doesn't meet these requirements, a compiler error will be emitted. +/// +/// As a short-hand, you may substitute the name of the extrinsic call with `_`, such as the +/// following: +/// +/// ```ignore +/// #[extrinsic_call] +/// _(RawOrigin::Signed(whitelisted_caller()), 0u32.into(), 0); +/// ``` +/// +/// The underscore will be substituted with the name of the benchmark (i.e. the name of the +/// function in the benchmark function definition). +/// +/// Regardless of whether `#[extrinsic_call]` or `#[block]` is used, this attribute also serves +/// the purpose of designating the boundary between the setup code portion of the benchmark +/// (everything before the `#[extrinsic_call]` or `#[block]` attribute) and the verification +/// stage (everything after the item that the `#[extrinsic_call]` or `#[block]` attribute is +/// attached to). The setup code section should contain any code that needs to execute before +/// the measured portion of the benchmark executes. The verification section is where you can +/// perform assertions to verify that the extrinsic call (or whatever is happening in your +/// block, if you used the `#[block]` syntax) executed successfully. +/// +/// Note that neither `#[extrinsic_call]` nor `#[block]` are real attribute macros and are +/// instead consumed by the outer macro pattern as part of the enclosing benchmark function +/// definition. This is why we are able to use `#[extrinsic_call]` and `#[block]` within a +/// function definition even though this behavior has not been stabilized +/// yet—`#[extrinsic_call]` and `#[block]` are parsed and consumed as part of the benchmark +/// definition parsing code, so they never expand as their own attribute macros. +/// +/// ### Optional Attributes +/// +/// The keywords `extra` and `skip_meta` can be provided as optional arguments to the +/// `#[benchmark]` attribute, i.e. `#[benchmark(extra, skip_meta)]`. Including either of these +/// will enable the `extra` or `skip_meta` option, respectively. These options enable the same +/// behavior they did in the old benchmarking syntax in `frame_benchmarking`, namely: +/// +/// #### `extra` +/// +/// Specifies that this benchmark should not normally run. To run benchmarks marked with +/// `extra`, you will need to invoke the `frame-benchmarking-cli` with `--extra`. +/// +/// #### `skip_meta` +/// +/// Specifies that the benchmarking framework should not analyze the storage keys that +/// benchmarked code read or wrote. This useful to suppress the prints in the form of unknown +/// 0x… in case a storage key that does not have metadata. Note that this skips the analysis +/// of all accesses, not just ones without metadata. +/// +/// ## Where Clause +/// +/// Some pallets require a where clause specifying constraints on their generics to make +/// writing benchmarks feasible. To accomodate this situation, you can provide such a where +/// clause as the (only) argument to the `#[benchmarks]` or `#[instance_benchmarks]` attribute +/// macros. Below is an example of this taken from the `message-queue` pallet. +/// +/// ```ignore +/// #[benchmarks( +/// where +/// <::MessageProcessor as ProcessMessage>::Origin: From + PartialEq, +/// ::Size: From, +/// )] +/// mod benchmarks { +/// use super::*; +/// // ... +/// } +/// ``` +/// +/// ## Benchmark Tests +/// +/// Benchmark tests can be generated using the old syntax in `frame_benchmarking`, +/// including the `frame_benchmarking::impl_benchmark_test_suite` macro. +/// +/// An example is shown below (taken from the `message-queue` pallet's `benchmarking` module): +/// ```ignore +/// #[benchmarks] +/// mod benchmarks { +/// use super::*; +/// // ... +/// impl_benchmark_test_suite!( +/// MessageQueue, +/// crate::mock::new_test_ext::(), +/// crate::integration_test::Test +/// ); +/// } +/// ``` +pub mod benchmarking { + pub use frame_support_procedural::{ + benchmark, benchmarks, block, extrinsic_call, instance_benchmarks, + }; + + // Used in #[benchmark] implementation to ensure that benchmark function arguments + // implement [`ParamRange`]. + #[doc(hidden)] + pub use static_assertions::assert_impl_all; + + /// Used by the new benchmarking code to specify that a benchmarking variable is linear + /// over some specified range, i.e. `Linear<0, 1_000>` means that the corresponding variable + /// is allowed to range from `0` to `1000`, inclusive. + /// + /// See [`frame_support::benchmarking`] for more info. + pub struct Linear; + + /// Trait that must be implemented by all structs that can be used as parameter range types + /// in the new benchmarking code (i.e. `Linear<0, 1_000>`). Right now there is just + /// [`Linear`] but this could later be extended to support additional non-linear parameter + /// ranges. + /// + /// See [`frame_support::benchmarking`] for more info. + pub trait ParamRange { + /// Represents the (inclusive) starting number of this [`ParamRange`]. + fn start(&self) -> u32; + + /// Represents the (inclusive) ending number of this [`ParamRange`] + fn end(&self) -> u32; + } + + impl ParamRange for Linear { + fn start(&self) -> u32 { + return A + } + + fn end(&self) -> u32 { + return B + } + } +} + // Generate a macro that will enable/disable code based on `std` feature being active. sp_core::generate_feature_enabled_macro!(std_enabled, feature = "std", $); diff --git a/frame/support/test/Cargo.toml b/frame/support/test/Cargo.toml index fc211d771..90ef243ee 100644 --- a/frame/support/test/Cargo.toml +++ b/frame/support/test/Cargo.toml @@ -19,6 +19,7 @@ sp-arithmetic = { version = "6.0.0", default-features = false, path = "../../../ sp-io = { version = "7.0.0", path = "../../../primitives/io", default-features = false } sp-state-machine = { version = "0.13.0", optional = true, path = "../../../primitives/state-machine" } frame-support = { version = "4.0.0-dev", default-features = false, path = "../" } +frame-benchmarking = { version = "4.0.0-dev", default-features = false, path = "../../benchmarking" } sp-runtime = { version = "7.0.0", default-features = false, path = "../../../primitives/runtime" } sp-core = { version = "7.0.0", default-features = false, path = "../../../primitives/core" } sp-std = { version = "5.0.0", default-features = false, path = "../../../primitives/std" } @@ -36,6 +37,7 @@ std = [ "serde/std", "codec/std", "scale-info/std", + "frame-benchmarking/std", "frame-support/std", "frame-system/std", "sp-core/std", diff --git a/frame/support/test/tests/benchmark_ui.rs b/frame/support/test/tests/benchmark_ui.rs new file mode 100644 index 000000000..243030bd0 --- /dev/null +++ b/frame/support/test/tests/benchmark_ui.rs @@ -0,0 +1,36 @@ +// This file is part of Substrate. + +// Copyright (C) 2020-2023 Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#[rustversion::attr(not(stable), ignore)] +#[cfg(not(feature = "disable-ui-tests"))] +#[test] +fn benchmark_ui() { + // Only run the ui tests when `RUN_UI_TESTS` is set. + if std::env::var("RUN_UI_TESTS").is_err() { + return + } + + // As trybuild is using `cargo check`, we don't need the real WASM binaries. + std::env::set_var("SKIP_WASM_BUILD", "1"); + + // Deny all warnings since we emit warnings as part of a Pallet's UI. + std::env::set_var("RUSTFLAGS", "--deny warnings"); + + let t = trybuild::TestCases::new(); + t.compile_fail("tests/benchmark_ui/*.rs"); + t.pass("tests/benchmark_ui/pass/*.rs"); +} diff --git a/frame/support/test/tests/benchmark_ui/bad_param_name.rs b/frame/support/test/tests/benchmark_ui/bad_param_name.rs new file mode 100644 index 000000000..ad4db4f0a --- /dev/null +++ b/frame/support/test/tests/benchmark_ui/bad_param_name.rs @@ -0,0 +1,18 @@ +use frame_support::benchmarking::*; +#[allow(unused_imports)] +use frame_support_test::Config; + +#[benchmarks] +mod benches { + use super::*; + + #[benchmark] + fn bench(winton: Linear<1, 2>) { + let a = 2 + 2; + #[block] + {} + assert_eq!(a, 4); + } +} + +fn main() {} diff --git a/frame/support/test/tests/benchmark_ui/bad_param_name.stderr b/frame/support/test/tests/benchmark_ui/bad_param_name.stderr new file mode 100644 index 000000000..4e2d63a6b --- /dev/null +++ b/frame/support/test/tests/benchmark_ui/bad_param_name.stderr @@ -0,0 +1,5 @@ +error: Benchmark parameter names must consist of a single lowercase letter (a-z) and no other characters. + --> tests/benchmark_ui/bad_param_name.rs:10:11 + | +10 | fn bench(winton: Linear<1, 2>) { + | ^^^^^^ diff --git a/frame/support/test/tests/benchmark_ui/bad_param_name_too_long.rs b/frame/support/test/tests/benchmark_ui/bad_param_name_too_long.rs new file mode 100644 index 000000000..50e4dc6fd --- /dev/null +++ b/frame/support/test/tests/benchmark_ui/bad_param_name_too_long.rs @@ -0,0 +1,14 @@ +use frame_support::benchmarking::*; +#[allow(unused_imports)] +use frame_support_test::Config; + +#[benchmarks] +mod benches { + #[benchmark] + fn bench(xx: Linear<1, 2>) { + #[block] + {} + } +} + +fn main() {} diff --git a/frame/support/test/tests/benchmark_ui/bad_param_name_too_long.stderr b/frame/support/test/tests/benchmark_ui/bad_param_name_too_long.stderr new file mode 100644 index 000000000..32f6bf8e4 --- /dev/null +++ b/frame/support/test/tests/benchmark_ui/bad_param_name_too_long.stderr @@ -0,0 +1,5 @@ +error: Benchmark parameter names must consist of a single lowercase letter (a-z) and no other characters. + --> tests/benchmark_ui/bad_param_name_too_long.rs:8:11 + | +8 | fn bench(xx: Linear<1, 2>) { + | ^^ diff --git a/frame/support/test/tests/benchmark_ui/bad_param_name_upper_case.rs b/frame/support/test/tests/benchmark_ui/bad_param_name_upper_case.rs new file mode 100644 index 000000000..0270582b3 --- /dev/null +++ b/frame/support/test/tests/benchmark_ui/bad_param_name_upper_case.rs @@ -0,0 +1,14 @@ +use frame_support::benchmarking::*; +#[allow(unused_imports)] +use frame_support_test::Config; + +#[benchmarks] +mod benches { + #[benchmark] + fn bench(D: Linear<1, 2>) { + #[block] + {} + } +} + +fn main() {} diff --git a/frame/support/test/tests/benchmark_ui/bad_param_name_upper_case.stderr b/frame/support/test/tests/benchmark_ui/bad_param_name_upper_case.stderr new file mode 100644 index 000000000..48dd41d32 --- /dev/null +++ b/frame/support/test/tests/benchmark_ui/bad_param_name_upper_case.stderr @@ -0,0 +1,5 @@ +error: Benchmark parameter names must consist of a single lowercase letter (a-z) and no other characters. + --> tests/benchmark_ui/bad_param_name_upper_case.rs:8:11 + | +8 | fn bench(D: Linear<1, 2>) { + | ^ diff --git a/frame/support/test/tests/benchmark_ui/bad_param_range.rs b/frame/support/test/tests/benchmark_ui/bad_param_range.rs new file mode 100644 index 000000000..aabb9fa74 --- /dev/null +++ b/frame/support/test/tests/benchmark_ui/bad_param_range.rs @@ -0,0 +1,16 @@ +use frame_support::benchmarking::*; +#[allow(unused_imports)] +use frame_support_test::Config; + +#[benchmarks] +mod benches { + use super::*; + + #[benchmark] + fn bench(x: Linear<3, 1>) { + #[block] + {} + } +} + +fn main() {} diff --git a/frame/support/test/tests/benchmark_ui/bad_param_range.stderr b/frame/support/test/tests/benchmark_ui/bad_param_range.stderr new file mode 100644 index 000000000..1347af0a0 --- /dev/null +++ b/frame/support/test/tests/benchmark_ui/bad_param_range.stderr @@ -0,0 +1,5 @@ +error: The start of a `ParamRange` must be less than or equal to the end + --> tests/benchmark_ui/bad_param_range.rs:10:21 + | +10 | fn bench(x: Linear<3, 1>) { + | ^ diff --git a/frame/support/test/tests/benchmark_ui/bad_params.rs b/frame/support/test/tests/benchmark_ui/bad_params.rs new file mode 100644 index 000000000..a0c923698 --- /dev/null +++ b/frame/support/test/tests/benchmark_ui/bad_params.rs @@ -0,0 +1,18 @@ +use frame_support::benchmarking::*; +#[allow(unused_imports)] +use frame_support_test::Config; + +#[benchmarks] +mod benches { + use super::*; + + #[benchmark] + fn bench(y: Linear<1, 2>, x: u32) { + let a = 2 + 2; + #[block] + {} + assert_eq!(a, 4); + } +} + +fn main() {} diff --git a/frame/support/test/tests/benchmark_ui/bad_params.stderr b/frame/support/test/tests/benchmark_ui/bad_params.stderr new file mode 100644 index 000000000..068eaedd5 --- /dev/null +++ b/frame/support/test/tests/benchmark_ui/bad_params.stderr @@ -0,0 +1,5 @@ +error: Invalid benchmark function param. A valid example would be `x: Linear<5, 10>`. + --> tests/benchmark_ui/bad_params.rs:10:31 + | +10 | fn bench(y: Linear<1, 2>, x: u32) { + | ^^^ diff --git a/frame/support/test/tests/benchmark_ui/dup_block.rs b/frame/support/test/tests/benchmark_ui/dup_block.rs new file mode 100644 index 000000000..4c4a8fc11 --- /dev/null +++ b/frame/support/test/tests/benchmark_ui/dup_block.rs @@ -0,0 +1,20 @@ +use frame_support::benchmarking::*; +#[allow(unused_imports)] +use frame_support_test::Config; + +#[benchmarks] +mod benches { + use super::*; + + #[benchmark] + fn bench() { + let a = 2 + 2; + #[block] + {} + #[block] + {} + assert_eq!(a, 4); + } +} + +fn main() {} diff --git a/frame/support/test/tests/benchmark_ui/dup_block.stderr b/frame/support/test/tests/benchmark_ui/dup_block.stderr new file mode 100644 index 000000000..3d73c3d66 --- /dev/null +++ b/frame/support/test/tests/benchmark_ui/dup_block.stderr @@ -0,0 +1,5 @@ +error: Only one #[extrinsic_call] or #[block] attribute is allowed per benchmark. + --> tests/benchmark_ui/dup_block.rs:14:3 + | +14 | #[block] + | ^ diff --git a/frame/support/test/tests/benchmark_ui/dup_extrinsic_call.rs b/frame/support/test/tests/benchmark_ui/dup_extrinsic_call.rs new file mode 100644 index 000000000..1a91b7c16 --- /dev/null +++ b/frame/support/test/tests/benchmark_ui/dup_extrinsic_call.rs @@ -0,0 +1,20 @@ +use frame_support::benchmarking::*; +#[allow(unused_imports)] +use frame_support_test::Config; + +#[benchmarks] +mod benches { + use super::*; + + #[benchmark] + fn bench() { + let a = 2 + 2; + #[extrinsic_call] + _(stuff); + #[extrinsic_call] + _(other_stuff); + assert_eq!(a, 4); + } +} + +fn main() {} diff --git a/frame/support/test/tests/benchmark_ui/dup_extrinsic_call.stderr b/frame/support/test/tests/benchmark_ui/dup_extrinsic_call.stderr new file mode 100644 index 000000000..593f7072b --- /dev/null +++ b/frame/support/test/tests/benchmark_ui/dup_extrinsic_call.stderr @@ -0,0 +1,5 @@ +error: Only one #[extrinsic_call] or #[block] attribute is allowed per benchmark. + --> tests/benchmark_ui/dup_extrinsic_call.rs:14:3 + | +14 | #[extrinsic_call] + | ^ diff --git a/frame/support/test/tests/benchmark_ui/extra_extra.rs b/frame/support/test/tests/benchmark_ui/extra_extra.rs new file mode 100644 index 000000000..021106c7a --- /dev/null +++ b/frame/support/test/tests/benchmark_ui/extra_extra.rs @@ -0,0 +1,16 @@ +use frame_support::benchmarking::*; +#[allow(unused_imports)] +use frame_support_test::Config; + +#[benchmarks] +mod benches { + use super::*; + + #[benchmark(extra, extra)] + fn bench() { + #[block] + {} + } +} + +fn main() {} diff --git a/frame/support/test/tests/benchmark_ui/extra_extra.stderr b/frame/support/test/tests/benchmark_ui/extra_extra.stderr new file mode 100644 index 000000000..bf36b4f08 --- /dev/null +++ b/frame/support/test/tests/benchmark_ui/extra_extra.stderr @@ -0,0 +1,5 @@ +error: unexpected end of input, `extra` can only be specified once + --> tests/benchmark_ui/extra_extra.rs:9:26 + | +9 | #[benchmark(extra, extra)] + | ^ diff --git a/frame/support/test/tests/benchmark_ui/extra_skip_meta.rs b/frame/support/test/tests/benchmark_ui/extra_skip_meta.rs new file mode 100644 index 000000000..1940f4cf1 --- /dev/null +++ b/frame/support/test/tests/benchmark_ui/extra_skip_meta.rs @@ -0,0 +1,16 @@ +use frame_support::benchmarking::*; +#[allow(unused_imports)] +use frame_support_test::Config; + +#[benchmarks] +mod benches { + use super::*; + + #[benchmark(skip_meta, skip_meta)] + fn bench() { + #[block] + {} + } +} + +fn main() {} diff --git a/frame/support/test/tests/benchmark_ui/extra_skip_meta.stderr b/frame/support/test/tests/benchmark_ui/extra_skip_meta.stderr new file mode 100644 index 000000000..4d48a8ad7 --- /dev/null +++ b/frame/support/test/tests/benchmark_ui/extra_skip_meta.stderr @@ -0,0 +1,5 @@ +error: unexpected end of input, `skip_meta` can only be specified once + --> tests/benchmark_ui/extra_skip_meta.rs:9:34 + | +9 | #[benchmark(skip_meta, skip_meta)] + | ^ diff --git a/frame/support/test/tests/benchmark_ui/extrinsic_call_out_of_fn.rs b/frame/support/test/tests/benchmark_ui/extrinsic_call_out_of_fn.rs new file mode 100644 index 000000000..4cb6bfc34 --- /dev/null +++ b/frame/support/test/tests/benchmark_ui/extrinsic_call_out_of_fn.rs @@ -0,0 +1,6 @@ +use frame_support::benchmarking::*; + +#[extrinsic_call] +mod stuff {} + +fn main() {} diff --git a/frame/support/test/tests/benchmark_ui/extrinsic_call_out_of_fn.stderr b/frame/support/test/tests/benchmark_ui/extrinsic_call_out_of_fn.stderr new file mode 100644 index 000000000..c5194d7a6 --- /dev/null +++ b/frame/support/test/tests/benchmark_ui/extrinsic_call_out_of_fn.stderr @@ -0,0 +1,7 @@ +error: `#[extrinsic_call]` must be in a benchmark function definition labeled with `#[benchmark]`. + --> tests/benchmark_ui/extrinsic_call_out_of_fn.rs:3:1 + | +3 | #[extrinsic_call] + | ^^^^^^^^^^^^^^^^^ + | + = note: this error originates in the attribute macro `extrinsic_call` (in Nightly builds, run with -Z macro-backtrace for more info) diff --git a/frame/support/test/tests/benchmark_ui/missing_call.rs b/frame/support/test/tests/benchmark_ui/missing_call.rs new file mode 100644 index 000000000..743704935 --- /dev/null +++ b/frame/support/test/tests/benchmark_ui/missing_call.rs @@ -0,0 +1,13 @@ +use frame_support::benchmarking::*; +#[allow(unused_imports)] +use frame_support_test::Config; + +#[benchmarks] +mod benches { + use super::*; + + #[benchmark] + fn bench() {} +} + +fn main() {} diff --git a/frame/support/test/tests/benchmark_ui/missing_call.stderr b/frame/support/test/tests/benchmark_ui/missing_call.stderr new file mode 100644 index 000000000..3b55f77d4 --- /dev/null +++ b/frame/support/test/tests/benchmark_ui/missing_call.stderr @@ -0,0 +1,5 @@ +error: No valid #[extrinsic_call] or #[block] annotation could be found in benchmark function body. + --> tests/benchmark_ui/missing_call.rs:10:13 + | +10 | fn bench() {} + | ^^ diff --git a/frame/support/test/tests/benchmark_ui/missing_origin.rs b/frame/support/test/tests/benchmark_ui/missing_origin.rs new file mode 100644 index 000000000..aad91bc79 --- /dev/null +++ b/frame/support/test/tests/benchmark_ui/missing_origin.rs @@ -0,0 +1,16 @@ +use frame_support::benchmarking::*; +#[allow(unused_imports)] +use frame_support_test::Config; + +#[benchmarks] +mod benches { + use super::*; + + #[benchmark] + fn bench() { + #[extrinsic_call] + thing(); + } +} + +fn main() {} diff --git a/frame/support/test/tests/benchmark_ui/missing_origin.stderr b/frame/support/test/tests/benchmark_ui/missing_origin.stderr new file mode 100644 index 000000000..0e72bff47 --- /dev/null +++ b/frame/support/test/tests/benchmark_ui/missing_origin.stderr @@ -0,0 +1,5 @@ +error: Single-item extrinsic calls must specify their origin as the first argument. + --> tests/benchmark_ui/missing_origin.rs:12:3 + | +12 | thing(); + | ^^^^^ diff --git a/frame/support/test/tests/benchmark_ui/pass/valid_basic.rs b/frame/support/test/tests/benchmark_ui/pass/valid_basic.rs new file mode 100644 index 000000000..5c84d7f76 --- /dev/null +++ b/frame/support/test/tests/benchmark_ui/pass/valid_basic.rs @@ -0,0 +1,17 @@ +use frame_support::benchmarking::*; +use frame_support_test::Config; + +#[benchmarks] +mod benches { + use super::*; + + #[benchmark(skip_meta, extra)] + fn bench() { + let a = 2 + 2; + #[block] + {} + assert_eq!(a, 4); + } +} + +fn main() {} diff --git a/frame/support/test/tests/benchmark_ui/unrecognized_option.rs b/frame/support/test/tests/benchmark_ui/unrecognized_option.rs new file mode 100644 index 000000000..4c2cea139 --- /dev/null +++ b/frame/support/test/tests/benchmark_ui/unrecognized_option.rs @@ -0,0 +1,16 @@ +use frame_support::benchmarking::*; +#[allow(unused_imports)] +use frame_support_test::Config; + +#[benchmarks] +mod benches { + use super::*; + + #[benchmark(skip_meta, extra, bad)] + fn bench() { + #[block] + {} + } +} + +fn main() {} diff --git a/frame/support/test/tests/benchmark_ui/unrecognized_option.stderr b/frame/support/test/tests/benchmark_ui/unrecognized_option.stderr new file mode 100644 index 000000000..5cebe9eab --- /dev/null +++ b/frame/support/test/tests/benchmark_ui/unrecognized_option.stderr @@ -0,0 +1,5 @@ +error: expected `extra` or `skip_meta` + --> tests/benchmark_ui/unrecognized_option.rs:9:32 + | +9 | #[benchmark(skip_meta, extra, bad)] + | ^^^ From d0a2c4ab86232a9a762d03f559715a14727bef3e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bastian=20K=C3=B6cher?= Date: Mon, 23 Jan 2023 12:35:04 +0100 Subject: [PATCH 039/558] Rename `*-private-ipv4` to `*-private-ip` CLI args (#13208) * Rename `*-private-ipv4` to `*-private-ip` CLI args Renames the `*-private-ipv4` to `*-private-ip` in the CLI interface. The old names are staying as alias, thus it will not break for anyone. Besides that it also fixes the naming in the rest of the code. * FMT --- client/cli/src/params/network_params.rs | 18 ++++++++--------- client/network/common/src/config.rs | 4 ++-- client/network/src/config.rs | 2 +- client/network/src/discovery.rs | 26 ++++++++++++------------- client/network/src/service.rs | 10 +++++++--- client/service/test/src/lib.rs | 2 +- 6 files changed, 33 insertions(+), 29 deletions(-) diff --git a/client/cli/src/params/network_params.rs b/client/cli/src/params/network_params.rs index 5580dea45..984789da7 100644 --- a/client/cli/src/params/network_params.rs +++ b/client/cli/src/params/network_params.rs @@ -68,18 +68,18 @@ pub struct NetworkParams { #[arg(long, value_name = "PORT", conflicts_with_all = &[ "listen_addr" ])] pub port: Option, - /// Always forbid connecting to private IPv4 addresses (as specified in + /// Always forbid connecting to private IPv4/IPv6 addresses (as specified in /// [RFC1918](https://tools.ietf.org/html/rfc1918)), unless the address was passed with /// `--reserved-nodes` or `--bootnodes`. Enabled by default for chains marked as "live" in /// their chain specifications. - #[arg(long, conflicts_with_all = &["allow_private_ipv4"])] - pub no_private_ipv4: bool, + #[arg(long, alias = "no-private-ipv4", conflicts_with_all = &["allow_private_ip"])] + pub no_private_ip: bool, - /// Always accept connecting to private IPv4 addresses (as specified in + /// Always accept connecting to private IPv4/IPv6 addresses (as specified in /// [RFC1918](https://tools.ietf.org/html/rfc1918)). Enabled by default for chains marked as /// "local" in their chain specifications, or when `--dev` is passed. - #[arg(long, conflicts_with_all = &["no_private_ipv4"])] - pub allow_private_ipv4: bool, + #[arg(long, alias = "allow-private-ipv4", conflicts_with_all = &["no_private_ip"])] + pub allow_private_ip: bool, /// Specify the number of outgoing connections we're trying to maintain. #[arg(long, value_name = "COUNT", default_value_t = 15)] @@ -200,8 +200,8 @@ impl NetworkParams { self.discover_local || is_dev || matches!(chain_type, ChainType::Local | ChainType::Development); - let allow_private_ipv4 = match (self.allow_private_ipv4, self.no_private_ipv4) { - (true, true) => unreachable!("`*_private_ipv4` flags are mutually exclusive; qed"), + let allow_private_ip = match (self.allow_private_ip, self.no_private_ip) { + (true, true) => unreachable!("`*_private_ip` flags are mutually exclusive; qed"), (true, false) => true, (false, true) => false, (false, false) => @@ -231,7 +231,7 @@ impl NetworkParams { client_version: client_id.to_string(), transport: TransportConfig::Normal { enable_mdns: !is_dev && !self.no_mdns, - allow_private_ipv4, + allow_private_ip, }, max_parallel_downloads: self.max_parallel_downloads, enable_dht_random_walk: !self.reserved_only, diff --git a/client/network/common/src/config.rs b/client/network/common/src/config.rs index 96c7c11ec..c35d8a42a 100644 --- a/client/network/common/src/config.rs +++ b/client/network/common/src/config.rs @@ -301,10 +301,10 @@ pub enum TransportConfig { /// and connect to them if they support the same chain. enable_mdns: bool, - /// If true, allow connecting to private IPv4 addresses (as defined in + /// If true, allow connecting to private IPv4/IPv6 addresses (as defined in /// [RFC1918](https://tools.ietf.org/html/rfc1918)). Irrelevant for addresses that have /// been passed in `::sc_network::config::NetworkConfiguration::boot_nodes`. - allow_private_ipv4: bool, + allow_private_ip: bool, }, /// Only allow connections within the same process. diff --git a/client/network/src/config.rs b/client/network/src/config.rs index 52993e251..8ca8d75f1 100644 --- a/client/network/src/config.rs +++ b/client/network/src/config.rs @@ -223,7 +223,7 @@ impl NetworkConfiguration { extra_sets: Vec::new(), client_version: client_version.into(), node_name: node_name.into(), - transport: TransportConfig::Normal { enable_mdns: false, allow_private_ipv4: true }, + transport: TransportConfig::Normal { enable_mdns: false, allow_private_ip: true }, max_parallel_downloads: 5, sync_mode: SyncMode::Full, enable_dht_random_walk: true, diff --git a/client/network/src/discovery.rs b/client/network/src/discovery.rs index 5421db819..7329ebf15 100644 --- a/client/network/src/discovery.rs +++ b/client/network/src/discovery.rs @@ -96,7 +96,7 @@ pub struct DiscoveryConfig { local_peer_id: PeerId, permanent_addresses: Vec<(PeerId, Multiaddr)>, dht_random_walk: bool, - allow_private_ipv4: bool, + allow_private_ip: bool, allow_non_globals_in_dht: bool, discovery_only_if_under_num: u64, enable_mdns: bool, @@ -111,7 +111,7 @@ impl DiscoveryConfig { local_peer_id: local_public_key.to_peer_id(), permanent_addresses: Vec::new(), dht_random_walk: true, - allow_private_ipv4: true, + allow_private_ip: true, allow_non_globals_in_dht: false, discovery_only_if_under_num: std::u64::MAX, enable_mdns: false, @@ -142,9 +142,9 @@ impl DiscoveryConfig { self } - /// Should private IPv4 addresses be reported? - pub fn allow_private_ipv4(&mut self, value: bool) -> &mut Self { - self.allow_private_ipv4 = value; + /// Should private IPv4/IPv6 addresses be reported? + pub fn allow_private_ip(&mut self, value: bool) -> &mut Self { + self.allow_private_ip = value; self } @@ -189,7 +189,7 @@ impl DiscoveryConfig { local_peer_id, permanent_addresses, dht_random_walk, - allow_private_ipv4, + allow_private_ip, allow_non_globals_in_dht, discovery_only_if_under_num, enable_mdns, @@ -231,7 +231,7 @@ impl DiscoveryConfig { pending_events: VecDeque::new(), local_peer_id, num_connections: 0, - allow_private_ipv4, + allow_private_ip, discovery_only_if_under_num, mdns: if enable_mdns { match TokioMdns::new(mdns::Config::default()) { @@ -278,9 +278,9 @@ pub struct DiscoveryBehaviour { local_peer_id: PeerId, /// Number of nodes we're currently connected to. num_connections: u64, - /// If false, `addresses_of_peer` won't return any private IPv4 address, except for the ones - /// stored in `permanent_addresses` or `ephemeral_addresses`. - allow_private_ipv4: bool, + /// If false, `addresses_of_peer` won't return any private IPv4/IPv6 address, except for the + /// ones stored in `permanent_addresses` or `ephemeral_addresses`. + allow_private_ip: bool, /// Number of active connections over which we interrupt the discovery process. discovery_only_if_under_num: u64, /// Should non-global addresses be added to the DHT? @@ -506,7 +506,7 @@ impl NetworkBehaviour for DiscoveryBehaviour { list_to_filter.extend(mdns.addresses_of_peer(peer_id)); } - if !self.allow_private_ipv4 { + if !self.allow_private_ip { list_to_filter.retain(|addr| match addr.iter().next() { Some(Protocol::Ip4(addr)) if !IpNetwork::from(addr).is_global() => false, Some(Protocol::Ip6(addr)) if !IpNetwork::from(addr).is_global() => false, @@ -947,7 +947,7 @@ mod tests { let mut config = DiscoveryConfig::new(keypair.public()); config .with_permanent_addresses(first_swarm_peer_id_and_addr.clone()) - .allow_private_ipv4(true) + .allow_private_ip(true) .allow_non_globals_in_dht(true) .discovery_limit(50) .with_kademlia(genesis_hash, fork_id, &protocol_id); @@ -1065,7 +1065,7 @@ mod tests { let keypair = Keypair::generate_ed25519(); let mut config = DiscoveryConfig::new(keypair.public()); config - .allow_private_ipv4(true) + .allow_private_ip(true) .allow_non_globals_in_dht(true) .discovery_limit(50) .with_kademlia(supported_genesis_hash, None, &supported_protocol_id); diff --git a/client/network/src/service.rs b/client/network/src/service.rs index f943a03f5..f3afb58e8 100644 --- a/client/network/src/service.rs +++ b/client/network/src/service.rs @@ -291,11 +291,15 @@ where match params.network_config.transport { TransportConfig::MemoryOnly => { config.with_mdns(false); - config.allow_private_ipv4(false); + config.allow_private_ip(false); }, - TransportConfig::Normal { enable_mdns, allow_private_ipv4, .. } => { + TransportConfig::Normal { + enable_mdns, + allow_private_ip: allow_private_ipv4, + .. + } => { config.with_mdns(enable_mdns); - config.allow_private_ipv4(allow_private_ipv4); + config.allow_private_ip(allow_private_ipv4); }, } diff --git a/client/service/test/src/lib.rs b/client/service/test/src/lib.rs index 5f75e3521..6533fe139 100644 --- a/client/service/test/src/lib.rs +++ b/client/service/test/src/lib.rs @@ -220,7 +220,7 @@ fn node_config< ); network_config.transport = - TransportConfig::Normal { enable_mdns: false, allow_private_ipv4: true }; + TransportConfig::Normal { enable_mdns: false, allow_private_ip: true }; Configuration { impl_name: String::from("network-test-impl"), From e72f72fc9f493d5219328158f182b1b99f2f0208 Mon Sep 17 00:00:00 2001 From: Koute Date: Mon, 23 Jan 2023 23:08:51 +0900 Subject: [PATCH 040/558] Remove dead code (#13213) --- client/db/src/storage_cache.rs | 1975 -------------------------------- 1 file changed, 1975 deletions(-) delete mode 100644 client/db/src/storage_cache.rs diff --git a/client/db/src/storage_cache.rs b/client/db/src/storage_cache.rs deleted file mode 100644 index 474599e5d..000000000 --- a/client/db/src/storage_cache.rs +++ /dev/null @@ -1,1975 +0,0 @@ -// This file is part of Substrate. - -// Copyright (C) 2019-2022 Parity Technologies (UK) Ltd. -// SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 - -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with this program. If not, see . - -//! Global state cache. Maintains recently queried/committed state values -//! Tracks changes over the span of a few recent blocks and handles forks -//! by tracking/removing cache entries for conflicting changes. - -use crate::{stats::StateUsageStats, utils::Meta}; -use hash_db::Hasher; -use linked_hash_map::{Entry, LinkedHashMap}; -use log::trace; -use parking_lot::{RwLock, RwLockUpgradableReadGuard}; -use sp_core::{hexdisplay::HexDisplay, storage::ChildInfo}; -use sp_runtime::{ - traits::{Block as BlockT, HashFor, Header, NumberFor}, - StateVersion, -}; -use sp_state_machine::{ - backend::Backend as StateBackend, ChildStorageCollection, StorageCollection, StorageKey, - StorageValue, TrieBackend, -}; -use std::{ - collections::{HashMap, HashSet, VecDeque}, - hash::Hash as StdHash, - sync::Arc, -}; - -const STATE_CACHE_BLOCKS: usize = 12; - -type ChildStorageKey = (Vec, Vec); - -/// Shared canonical state cache. -pub struct Cache { - /// Storage cache. `None` indicates that key is known to be missing. - lru_storage: LRUMap>, - /// Storage hashes cache. `None` indicates that key is known to be missing. - lru_hashes: LRUMap>, - /// Storage cache for child trie. `None` indicates that key is known to be missing. - lru_child_storage: LRUMap>, - /// Information on the modifications in recently committed blocks; specifically which keys - /// changed in which block. Ordered by block number. - modifications: VecDeque>, -} - -struct LRUMap(LinkedHashMap, usize, usize); - -/// Internal trait similar to `heapsize` but using a simple estimation. -/// -/// This should not be made public, it is an implementation detail trait. -trait EstimateSize { - /// Return a size estimation of the additional size needed to cache this struct (in bytes). - fn estimate_size(&self) -> usize; -} - -impl EstimateSize for Vec { - fn estimate_size(&self) -> usize { - self.capacity() - } -} - -impl EstimateSize for Option> { - fn estimate_size(&self) -> usize { - self.as_ref().map(|v| v.capacity()).unwrap_or(0) - } -} - -struct OptionHOut>(Option); - -impl> EstimateSize for OptionHOut { - fn estimate_size(&self) -> usize { - // capacity would be better - self.0.as_ref().map(|v| v.as_ref().len()).unwrap_or(0) - } -} - -impl EstimateSize for (T, T) { - fn estimate_size(&self) -> usize { - self.0.estimate_size() + self.1.estimate_size() - } -} - -impl LRUMap { - fn remove(&mut self, k: &K) { - let map = &mut self.0; - let storage_used_size = &mut self.1; - if let Some(v) = map.remove(k) { - *storage_used_size -= k.estimate_size(); - *storage_used_size -= v.estimate_size(); - } - } - - fn add(&mut self, k: K, v: V) { - let lmap = &mut self.0; - let storage_used_size = &mut self.1; - let limit = self.2; - let klen = k.estimate_size(); - *storage_used_size += v.estimate_size(); - // TODO assert k v size fit into limit?? to avoid insert remove? - match lmap.entry(k) { - Entry::Occupied(mut entry) => { - // note that in this case we are not running pure lru as - // it would require to remove first - *storage_used_size -= entry.get().estimate_size(); - entry.insert(v); - }, - Entry::Vacant(entry) => { - *storage_used_size += klen; - entry.insert(v); - }, - }; - - while *storage_used_size > limit { - if let Some((k, v)) = lmap.pop_front() { - *storage_used_size -= k.estimate_size(); - *storage_used_size -= v.estimate_size(); - } else { - // can happen fairly often as we get value from multiple lru - // and only remove from a single lru - break - } - } - } - - fn get(&mut self, k: &Q) -> Option<&mut V> - where - K: std::borrow::Borrow, - Q: StdHash + Eq, - { - self.0.get_refresh(k) - } - - fn used_size(&self) -> usize { - self.1 - } - fn clear(&mut self) { - self.0.clear(); - self.1 = 0; - } -} - -impl Cache { - /// Returns the used memory size of the storage cache in bytes. - pub fn used_storage_cache_size(&self) -> usize { - self.lru_storage.used_size() + self.lru_child_storage.used_size() - // ignore small hashes storage and self.lru_hashes.used_size() - } - - /// Synchronize the shared cache with the best block state. - /// - /// This function updates the shared cache by removing entries - /// that are invalidated by chain reorganization. It should be called - /// externally when chain reorg happens without importing a new block. - pub fn sync(&mut self, enacted: &[B::Hash], retracted: &[B::Hash]) { - trace!("Syncing shared cache, enacted = {:?}, retracted = {:?}", enacted, retracted); - - // Purge changes from re-enacted and retracted blocks. - let mut clear = false; - for block in enacted { - clear = clear || { - if let Some(m) = self.modifications.iter_mut().find(|m| &m.hash == block) { - trace!("Reverting enacted block {:?}", block); - m.is_canon = true; - for a in &m.storage { - trace!("Reverting enacted key {:?}", HexDisplay::from(a)); - self.lru_storage.remove(a); - self.lru_hashes.remove(a); - } - for a in &m.child_storage { - trace!("Reverting enacted child key {:?}", a); - self.lru_child_storage.remove(a); - } - false - } else { - true - } - }; - } - - for block in retracted { - clear = clear || { - if let Some(m) = self.modifications.iter_mut().find(|m| &m.hash == block) { - trace!("Retracting block {:?}", block); - m.is_canon = false; - for a in &m.storage { - trace!("Retracted key {:?}", HexDisplay::from(a)); - self.lru_storage.remove(a); - self.lru_hashes.remove(a); - } - for a in &m.child_storage { - trace!("Retracted child key {:?}", a); - self.lru_child_storage.remove(a); - } - false - } else { - true - } - }; - } - if clear { - // We don't know anything about the block; clear everything - trace!("Wiping cache"); - self.lru_storage.clear(); - self.lru_child_storage.clear(); - self.lru_hashes.clear(); - self.modifications.clear(); - } - } -} - -pub type SharedCache = Arc>>; - -/// Fix lru storage size for hash (small 64ko). -const FIX_LRU_HASH_SIZE: usize = 65_536; - -/// Create a new shared cache instance with given max memory usage. -pub fn new_shared_cache( - shared_cache_size: usize, - child_ratio: (usize, usize), -) -> SharedCache { - let top = child_ratio.1.saturating_sub(child_ratio.0); - Arc::new(RwLock::new(Cache { - lru_storage: LRUMap(LinkedHashMap::new(), 0, shared_cache_size * top / child_ratio.1), - lru_hashes: LRUMap(LinkedHashMap::new(), 0, FIX_LRU_HASH_SIZE), - lru_child_storage: LRUMap( - LinkedHashMap::new(), - 0, - shared_cache_size * child_ratio.0 / child_ratio.1, - ), - modifications: VecDeque::new(), - })) -} - -#[derive(Debug)] -/// Accumulates a list of storage changed in a block. -struct BlockChanges { - /// Block number. - number: B::Number, - /// Block hash. - hash: B::Hash, - /// Parent block hash. - parent: B::Hash, - /// A set of modified storage keys. - storage: HashSet, - /// A set of modified child storage keys. - child_storage: HashSet, - /// Block is part of the canonical chain. - is_canon: bool, -} - -/// Cached values specific to a state. -struct LocalCache { - /// Storage cache. - /// - /// `None` indicates that key is known to be missing. - storage: HashMap>, - /// Storage hashes cache. - /// - /// `None` indicates that key is known to be missing. - hashes: HashMap>, - /// Child storage cache. - /// - /// `None` indicates that key is known to be missing. - child_storage: HashMap>, -} - -/// Cache changes. -pub struct CacheChanges { - /// Shared canonical state cache. - shared_cache: SharedCache, - /// Local cache of values for this state. - local_cache: RwLock>>, - /// Hash of the block on top of which this instance was created or - /// `None` if cache is disabled - pub parent_hash: Option, -} - -/// State cache abstraction. -/// -/// Manages shared global state cache which reflects the canonical -/// state as it is on the disk. -/// -/// A instance of `CachingState` may be created as canonical or not. -/// For canonical instances local cache is accumulated and applied -/// in `sync_cache` along with the change overlay. -/// For non-canonical clones local cache and changes are dropped. -pub struct CachingState { - /// Usage statistics - usage: StateUsageStats, - /// State machine registered stats - overlay_stats: sp_state_machine::StateMachineStats, - /// Backing state. - state: S, - /// Cache data. - cache: CacheChanges, -} - -impl std::fmt::Debug for CachingState { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - write!(f, "Block {:?}", self.cache.parent_hash) - } -} - -impl CacheChanges { - /// Propagate local cache into the shared cache and synchronize - /// the shared cache with the best block state. - /// - /// This function updates the shared cache by removing entries - /// that are invalidated by chain reorganization. `sync_cache` - /// should be called after the block has been committed and the - /// blockchain route has been calculated. - pub fn sync_cache( - &mut self, - enacted: &[B::Hash], - retracted: &[B::Hash], - changes: StorageCollection, - child_changes: ChildStorageCollection, - commit_hash: Option, - commit_number: Option>, - is_best: bool, - ) { - let mut cache = self.shared_cache.write(); - trace!( - "Syncing cache, id = (#{:?}, {:?}), parent={:?}, best={}", - commit_number, - commit_hash, - self.parent_hash, - is_best, - ); - let cache = &mut *cache; - // Filter out committing block if any. - let mut enacted: Vec<_> = enacted - .iter() - .filter(|h| commit_hash.as_ref().map_or(true, |p| *h != p)) - .cloned() - .collect(); - - let mut retracted = std::borrow::Cow::Borrowed(retracted); - if let Some(commit_hash) = &commit_hash { - if let Some(m) = cache.modifications.iter_mut().find(|m| &m.hash == commit_hash) { - if m.is_canon != is_best { - // Same block comitted twice with different state changes. - // Treat it as reenacted/retracted. - if is_best { - enacted.push(*commit_hash); - } else { - retracted.to_mut().push(*commit_hash); - } - } - } - } - cache.sync(&enacted, &retracted); - // Propagate cache only if committing on top of the latest canonical state - // blocks are ordered by number and only one block with a given number is marked as - // canonical (contributed to canonical state cache) - if self.parent_hash.is_some() { - let mut local_cache = self.local_cache.write(); - if is_best { - trace!( - "Committing {} local, {} hashes, {} modified root entries, {} modified child entries", - local_cache.storage.len(), - local_cache.hashes.len(), - changes.len(), - child_changes.iter().map(|v|v.1.len()).sum::(), - ); - for (k, v) in local_cache.storage.drain() { - cache.lru_storage.add(k, v); - } - for (k, v) in local_cache.child_storage.drain() { - cache.lru_child_storage.add(k, v); - } - for (k, v) in local_cache.hashes.drain() { - cache.lru_hashes.add(k, OptionHOut(v)); - } - } - } - - if let (Some(ref number), Some(ref hash), Some(ref parent)) = - (commit_number, commit_hash, self.parent_hash) - { - if cache.modifications.len() == STATE_CACHE_BLOCKS { - cache.modifications.pop_back(); - } - let mut modifications = HashSet::new(); - let mut child_modifications = HashSet::new(); - child_changes.into_iter().for_each(|(sk, changes)| { - for (k, v) in changes.into_iter() { - let k = (sk.clone(), k); - if is_best { - cache.lru_child_storage.add(k.clone(), v); - } - child_modifications.insert(k); - } - }); - for (k, v) in changes.into_iter() { - if is_best { - cache.lru_hashes.remove(&k); - cache.lru_storage.add(k.clone(), v); - } - modifications.insert(k); - } - - // Save modified storage. These are ordered by the block number in reverse. - let block_changes = BlockChanges { - storage: modifications, - child_storage: child_modifications, - number: *number, - hash: *hash, - is_canon: is_best, - parent: *parent, - }; - let insert_at = cache - .modifications - .iter() - .enumerate() - .find(|(_, m)| m.number < *number) - .map(|(i, _)| i); - trace!("Inserting modifications at {:?}", insert_at); - if let Some(insert_at) = insert_at { - cache.modifications.insert(insert_at, block_changes); - } else { - cache.modifications.push_back(block_changes); - } - } - } -} - -impl>, B: BlockT> CachingState { - /// Create a new instance wrapping generic State and shared cache. - pub(crate) fn new( - state: S, - shared_cache: SharedCache, - parent_hash: Option, - ) -> Self { - CachingState { - usage: StateUsageStats::new(), - overlay_stats: sp_state_machine::StateMachineStats::default(), - state, - cache: CacheChanges { - shared_cache, - local_cache: RwLock::new(LocalCache { - storage: Default::default(), - hashes: Default::default(), - child_storage: Default::default(), - }), - parent_hash, - }, - } - } - - /// Check if the key can be returned from cache by matching current block parent hash against - /// canonical state and filtering out entries modified in later blocks. - fn is_allowed( - key: Option<&[u8]>, - child_key: Option<&ChildStorageKey>, - parent_hash: &Option, - modifications: &VecDeque>, - ) -> bool { - let mut parent = match *parent_hash { - None => { - trace!( - "Cache lookup skipped for {:?}: no parent hash", - key.as_ref().map(HexDisplay::from) - ); - return false - }, - Some(ref parent) => parent, - }; - // Ignore all storage entries modified in later blocks. - // Modifications contains block ordered by the number - // We search for our parent in that list first and then for - // all its parents until we hit the canonical block, - // checking against all the intermediate modifications. - for m in modifications { - if &m.hash == parent { - if m.is_canon { - return true - } - parent = &m.parent; - } - if let Some(key) = key { - if m.storage.contains(key) { - trace!( - "Cache lookup skipped for {:?}: modified in a later block", - HexDisplay::from(&key) - ); - return false - } - } - if let Some(child_key) = child_key { - if m.child_storage.contains(child_key) { - trace!("Cache lookup skipped for {:?}: modified in a later block", child_key); - return false - } - } - } - trace!( - "Cache lookup skipped for {:?}: parent hash is unknown", - key.as_ref().map(HexDisplay::from), - ); - false - } -} - -impl>, B: BlockT> StateBackend> for CachingState { - type Error = S::Error; - type Transaction = S::Transaction; - type TrieBackendStorage = S::TrieBackendStorage; - - fn storage(&self, key: &[u8]) -> Result>, Self::Error> { - let local_cache = self.cache.local_cache.upgradable_read(); - // Note that local cache makes that lru is not refreshed - if let Some(entry) = local_cache.storage.get(key).cloned() { - trace!("Found in local cache: {:?}", HexDisplay::from(&key)); - self.usage.tally_key_read(key, entry.as_ref(), true); - - return Ok(entry) - } - { - let cache = self.cache.shared_cache.upgradable_read(); - if Self::is_allowed(Some(key), None, &self.cache.parent_hash, &cache.modifications) { - let mut cache = RwLockUpgradableReadGuard::upgrade(cache); - if let Some(entry) = cache.lru_storage.get(key).map(|a| a.clone()) { - trace!("Found in shared cache: {:?}", HexDisplay::from(&key)); - self.usage.tally_key_read(key, entry.as_ref(), true); - return Ok(entry) - } - } - } - trace!("Cache miss: {:?}", HexDisplay::from(&key)); - let value = self.state.storage(key)?; - RwLockUpgradableReadGuard::upgrade(local_cache) - .storage - .insert(key.to_vec(), value.clone()); - self.usage.tally_key_read(key, value.as_ref(), false); - Ok(value) - } - - fn storage_hash(&self, key: &[u8]) -> Result, Self::Error> { - let local_cache = self.cache.local_cache.upgradable_read(); - if let Some(entry) = local_cache.hashes.get(key).cloned() { - trace!("Found hash in local cache: {:?}", HexDisplay::from(&key)); - return Ok(entry) - } - { - let cache = self.cache.shared_cache.upgradable_read(); - if Self::is_allowed(Some(key), None, &self.cache.parent_hash, &cache.modifications) { - let mut cache = RwLockUpgradableReadGuard::upgrade(cache); - if let Some(entry) = cache.lru_hashes.get(key).map(|a| a.0) { - trace!("Found hash in shared cache: {:?}", HexDisplay::from(&key)); - return Ok(entry) - } - } - } - trace!("Cache hash miss: {:?}", HexDisplay::from(&key)); - let hash = self.state.storage_hash(key)?; - RwLockUpgradableReadGuard::upgrade(local_cache) - .hashes - .insert(key.to_vec(), hash); - Ok(hash) - } - - fn child_storage( - &self, - child_info: &ChildInfo, - key: &[u8], - ) -> Result>, Self::Error> { - let key = (child_info.storage_key().to_vec(), key.to_vec()); - let local_cache = self.cache.local_cache.upgradable_read(); - if let Some(entry) = local_cache.child_storage.get(&key).cloned() { - trace!("Found in local cache: {:?}", key); - return Ok(self.usage.tally_child_key_read(&key, entry, true)) - } - { - let cache = self.cache.shared_cache.upgradable_read(); - if Self::is_allowed(None, Some(&key), &self.cache.parent_hash, &cache.modifications) { - let mut cache = RwLockUpgradableReadGuard::upgrade(cache); - if let Some(entry) = cache.lru_child_storage.get(&key).map(|a| a.clone()) { - trace!("Found in shared cache: {:?}", key); - return Ok(self.usage.tally_child_key_read(&key, entry, true)) - } - } - } - trace!("Cache miss: {:?}", key); - let value = self.state.child_storage(child_info, &key.1[..])?; - - // just pass it through the usage counter - let value = self.usage.tally_child_key_read(&key, value, false); - - RwLockUpgradableReadGuard::upgrade(local_cache) - .child_storage - .insert(key, value.clone()); - Ok(value) - } - - fn exists_storage(&self, key: &[u8]) -> Result { - Ok(self.storage(key)?.is_some()) - } - - fn exists_child_storage( - &self, - child_info: &ChildInfo, - key: &[u8], - ) -> Result { - self.state.exists_child_storage(child_info, key) - } - - fn apply_to_key_values_while, Vec) -> bool>( - &self, - child_info: Option<&ChildInfo>, - prefix: Option<&[u8]>, - start_at: Option<&[u8]>, - f: F, - allow_missing: bool, - ) -> Result { - self.state - .apply_to_key_values_while(child_info, prefix, start_at, f, allow_missing) - } - - fn apply_to_keys_while bool>( - &self, - child_info: Option<&ChildInfo>, - prefix: Option<&[u8]>, - start_at: Option<&[u8]>, - f: F, - ) { - self.state.apply_to_keys_while(child_info, prefix, start_at, f) - } - - fn next_storage_key(&self, key: &[u8]) -> Result>, Self::Error> { - self.state.next_storage_key(key) - } - - fn next_child_storage_key( - &self, - child_info: &ChildInfo, - key: &[u8], - ) -> Result>, Self::Error> { - self.state.next_child_storage_key(child_info, key) - } - - fn for_keys_with_prefix(&self, prefix: &[u8], f: F) { - self.state.for_keys_with_prefix(prefix, f) - } - - fn for_key_values_with_prefix(&self, prefix: &[u8], f: F) { - self.state.for_key_values_with_prefix(prefix, f) - } - - fn for_child_keys_with_prefix( - &self, - child_info: &ChildInfo, - prefix: &[u8], - f: F, - ) { - self.state.for_child_keys_with_prefix(child_info, prefix, f) - } - - fn storage_root<'a>( - &self, - delta: impl Iterator)>, - state_version: StateVersion, - ) -> (B::Hash, Self::Transaction) - where - B::Hash: Ord, - { - self.state.storage_root(delta, state_version) - } - - fn child_storage_root<'a>( - &self, - child_info: &ChildInfo, - delta: impl Iterator)>, - state_version: StateVersion, - ) -> (B::Hash, bool, Self::Transaction) - where - B::Hash: Ord, - { - self.state.child_storage_root(child_info, delta, state_version) - } - - fn pairs(&self) -> Vec<(Vec, Vec)> { - self.state.pairs() - } - - fn keys(&self, prefix: &[u8]) -> Vec> { - self.state.keys(prefix) - } - - fn child_keys(&self, child_info: &ChildInfo, prefix: &[u8]) -> Vec> { - self.state.child_keys(child_info, prefix) - } - - fn as_trie_backend(&self) -> Option<&TrieBackend>> { - self.state.as_trie_backend() - } - - fn register_overlay_stats(&self, stats: &sp_state_machine::StateMachineStats) { - self.overlay_stats.add(stats); - } - - fn usage_info(&self) -> sp_state_machine::UsageInfo { - let mut info = self.usage.take(); - info.include_state_machine_states(&self.overlay_stats); - info - } -} - -/// Extended [`CachingState`] that will sync the caches on drop. -pub struct SyncingCachingState { - /// The usage statistics of the backend. These will be updated on drop. - state_usage: Arc, - /// Reference to the meta db. - meta: Arc, Block::Hash>>>, - /// Mutex to lock get exlusive access to the backend. - lock: Arc>, - /// The wrapped caching state. - /// - /// This is required to be a `Option`, because sometimes we want to extract - /// the cache changes and Rust does not allow to move fields from types that - /// implement `Drop`. - caching_state: Option>, - /// Disable syncing of the cache. This is by default always `false`. However, - /// we need to disable syncing when this is a state in a - /// [`BlockImportOperation`](crate::BlockImportOperation). The import operation - /// takes care to sync the cache and more importantly we want to prevent a dead - /// lock. - disable_syncing: bool, -} - -impl SyncingCachingState { - /// Create new automatic syncing state. - pub fn new( - caching_state: CachingState, - state_usage: Arc, - meta: Arc, B::Hash>>>, - lock: Arc>, - ) -> Self { - Self { caching_state: Some(caching_state), state_usage, meta, lock, disable_syncing: false } - } - - /// Returns the reference to the internal [`CachingState`]. - fn caching_state(&self) -> &CachingState { - self.caching_state - .as_ref() - .expect("`caching_state` is always valid for the lifetime of the object; qed") - } - - /// Convert `Self` into the cache changes. - pub fn into_cache_changes(mut self) -> CacheChanges { - self.caching_state - .take() - .expect("`caching_state` is always valid for the lifetime of the object; qed") - .cache - } - - /// Disable syncing the cache on drop. - pub fn disable_syncing(&mut self) { - self.disable_syncing = true; - } -} - -impl std::fmt::Debug for SyncingCachingState { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - self.caching_state().fmt(f) - } -} - -impl>, B: BlockT> StateBackend> - for SyncingCachingState -{ - type Error = S::Error; - type Transaction = S::Transaction; - type TrieBackendStorage = S::TrieBackendStorage; - - fn storage(&self, key: &[u8]) -> Result>, Self::Error> { - self.caching_state().storage(key) - } - - fn storage_hash(&self, key: &[u8]) -> Result, Self::Error> { - self.caching_state().storage_hash(key) - } - - fn child_storage( - &self, - child_info: &ChildInfo, - key: &[u8], - ) -> Result>, Self::Error> { - self.caching_state().child_storage(child_info, key) - } - - fn exists_storage(&self, key: &[u8]) -> Result { - self.caching_state().exists_storage(key) - } - - fn exists_child_storage( - &self, - child_info: &ChildInfo, - key: &[u8], - ) -> Result { - self.caching_state().exists_child_storage(child_info, key) - } - - fn apply_to_key_values_while, Vec) -> bool>( - &self, - child_info: Option<&ChildInfo>, - prefix: Option<&[u8]>, - start_at: Option<&[u8]>, - f: F, - allow_missing: bool, - ) -> Result { - self.caching_state().apply_to_key_values_while( - child_info, - prefix, - start_at, - f, - allow_missing, - ) - } - - fn apply_to_keys_while bool>( - &self, - child_info: Option<&ChildInfo>, - prefix: Option<&[u8]>, - start_at: Option<&[u8]>, - f: F, - ) { - self.caching_state().apply_to_keys_while(child_info, prefix, start_at, f) - } - - fn next_storage_key(&self, key: &[u8]) -> Result>, Self::Error> { - self.caching_state().next_storage_key(key) - } - - fn next_child_storage_key( - &self, - child_info: &ChildInfo, - key: &[u8], - ) -> Result>, Self::Error> { - self.caching_state().next_child_storage_key(child_info, key) - } - - fn for_keys_with_prefix(&self, prefix: &[u8], f: F) { - self.caching_state().for_keys_with_prefix(prefix, f) - } - - fn for_key_values_with_prefix(&self, prefix: &[u8], f: F) { - self.caching_state().for_key_values_with_prefix(prefix, f) - } - - fn for_child_keys_with_prefix( - &self, - child_info: &ChildInfo, - prefix: &[u8], - f: F, - ) { - self.caching_state().for_child_keys_with_prefix(child_info, prefix, f) - } - - fn storage_root<'a>( - &self, - delta: impl Iterator)>, - state_version: StateVersion, - ) -> (B::Hash, Self::Transaction) - where - B::Hash: Ord, - { - self.caching_state().storage_root(delta, state_version) - } - - fn child_storage_root<'a>( - &self, - child_info: &ChildInfo, - delta: impl Iterator)>, - state_version: StateVersion, - ) -> (B::Hash, bool, Self::Transaction) - where - B::Hash: Ord, - { - self.caching_state().child_storage_root(child_info, delta, state_version) - } - - fn pairs(&self) -> Vec<(Vec, Vec)> { - self.caching_state().pairs() - } - - fn keys(&self, prefix: &[u8]) -> Vec> { - self.caching_state().keys(prefix) - } - - fn child_keys(&self, child_info: &ChildInfo, prefix: &[u8]) -> Vec> { - self.caching_state().child_keys(child_info, prefix) - } - - fn as_trie_backend(&self) -> Option<&TrieBackend>> { - self.caching_state - .as_ref() - .expect("`caching_state` is valid for the lifetime of the object; qed") - .as_trie_backend() - } - - fn register_overlay_stats(&self, stats: &sp_state_machine::StateMachineStats) { - self.caching_state().register_overlay_stats(stats); - } - - fn usage_info(&self) -> sp_state_machine::UsageInfo { - self.caching_state().usage_info() - } -} - -impl Drop for SyncingCachingState { - fn drop(&mut self) { - if self.disable_syncing { - return - } - - if let Some(mut caching_state) = self.caching_state.take() { - let _lock = self.lock.read(); - - self.state_usage.merge_sm(caching_state.usage.take()); - if let Some(hash) = caching_state.cache.parent_hash { - let is_best = self.meta.read().best_hash == hash; - caching_state.cache.sync_cache(&[], &[], vec![], vec![], None, None, is_best); - } - } - } -} - -#[cfg(test)] -mod tests { - use super::*; - use sp_runtime::{ - testing::{Block as RawBlock, ExtrinsicWrapper, H256}, - traits::BlakeTwo256, - }; - use sp_state_machine::InMemoryBackend; - - type Block = RawBlock>; - - #[test] - fn smoke() { - // init_log(); - let root_parent = H256::random(); - let key = H256::random()[..].to_vec(); - let h0 = H256::random(); - let h1a = H256::random(); - let h1b = H256::random(); - let h2a = H256::random(); - let h2b = H256::random(); - let h3a = H256::random(); - let h3b = H256::random(); - - let shared = new_shared_cache::(256 * 1024, (0, 1)); - - // blocks [ 3a(c) 2a(c) 2b 1b 1a(c) 0 ] - // state [ 5 5 4 3 2 2 ] - let mut s = CachingState::new( - InMemoryBackend::::default(), - shared.clone(), - Some(root_parent), - ); - s.cache.sync_cache( - &[], - &[], - vec![(key.clone(), Some(vec![2]))], - vec![], - Some(h0), - Some(0), - true, - ); - - let mut s = - CachingState::new(InMemoryBackend::::default(), shared.clone(), Some(h0)); - s.cache.sync_cache(&[], &[], vec![], vec![], Some(h1a), Some(1), true); - - let mut s = - CachingState::new(InMemoryBackend::::default(), shared.clone(), Some(h0)); - s.cache.sync_cache( - &[], - &[], - vec![(key.clone(), Some(vec![3]))], - vec![], - Some(h1b), - Some(1), - false, - ); - - let mut s = - CachingState::new(InMemoryBackend::::default(), shared.clone(), Some(h1b)); - s.cache.sync_cache( - &[], - &[], - vec![(key.clone(), Some(vec![4]))], - vec![], - Some(h2b), - Some(2), - false, - ); - - let mut s = - CachingState::new(InMemoryBackend::::default(), shared.clone(), Some(h1a)); - s.cache.sync_cache( - &[], - &[], - vec![(key.clone(), Some(vec![5]))], - vec![], - Some(h2a), - Some(2), - true, - ); - - let mut s = - CachingState::new(InMemoryBackend::::default(), shared.clone(), Some(h2a)); - s.cache.sync_cache(&[], &[], vec![], vec![], Some(h3a), Some(3), true); - - let s = - CachingState::new(InMemoryBackend::::default(), shared.clone(), Some(h3a)); - assert_eq!(s.storage(&key).unwrap().unwrap(), vec![5]); - - let s = - CachingState::new(InMemoryBackend::::default(), shared.clone(), Some(h1a)); - assert!(s.storage(&key).unwrap().is_none()); - - let s = - CachingState::new(InMemoryBackend::::default(), shared.clone(), Some(h2b)); - assert!(s.storage(&key).unwrap().is_none()); - - let s = - CachingState::new(InMemoryBackend::::default(), shared.clone(), Some(h1b)); - assert!(s.storage(&key).unwrap().is_none()); - - // reorg to 3b - // blocks [ 3b(c) 3a 2a 2b(c) 1b 1a 0 ] - let mut s = - CachingState::new(InMemoryBackend::::default(), shared.clone(), Some(h2b)); - s.cache.sync_cache( - &[h1b, h2b, h3b], - &[h1a, h2a, h3a], - vec![], - vec![], - Some(h3b), - Some(3), - true, - ); - let s = - CachingState::new(InMemoryBackend::::default(), shared.clone(), Some(h3a)); - assert!(s.storage(&key).unwrap().is_none()); - } - - #[test] - fn simple_fork() { - sp_tracing::try_init_simple(); - - let root_parent = H256::random(); - let key = H256::random()[..].to_vec(); - let h1 = H256::random(); - let h2a = H256::random(); - let h2b = H256::random(); - let h3b = H256::random(); - - let shared = new_shared_cache::(256 * 1024, (0, 1)); - - let mut s = CachingState::new( - InMemoryBackend::::default(), - shared.clone(), - Some(root_parent), - ); - s.cache.sync_cache( - &[], - &[], - vec![(key.clone(), Some(vec![2]))], - vec![], - Some(h1), - Some(1), - true, - ); - - let mut s = - CachingState::new(InMemoryBackend::::default(), shared.clone(), Some(h1)); - s.cache.sync_cache(&[], &[], vec![], vec![], Some(h2a), Some(2), true); - - let mut s = - CachingState::new(InMemoryBackend::::default(), shared.clone(), Some(h1)); - s.cache.sync_cache( - &[], - &[], - vec![(key.clone(), Some(vec![3]))], - vec![], - Some(h2b), - Some(2), - false, - ); - - let mut s = - CachingState::new(InMemoryBackend::::default(), shared.clone(), Some(h2b)); - s.cache.sync_cache( - &[], - &[], - vec![(key.clone(), Some(vec![3]))], - vec![], - Some(h3b), - Some(2), - false, - ); - - let s = - CachingState::new(InMemoryBackend::::default(), shared.clone(), Some(h2a)); - assert_eq!(s.storage(&key).unwrap().unwrap(), vec![2]); - } - - #[test] - fn double_fork() { - let root_parent = H256::random(); - let key = H256::random()[..].to_vec(); - let h1 = H256::random(); - let h2a = H256::random(); - let h2b = H256::random(); - let h3a = H256::random(); - let h3b = H256::random(); - - let shared = new_shared_cache::(256 * 1024, (0, 1)); - - let mut s = CachingState::new( - InMemoryBackend::::default(), - shared.clone(), - Some(root_parent), - ); - s.cache.sync_cache(&[], &[], vec![], vec![], Some(h1), Some(1), true); - - let mut s = - CachingState::new(InMemoryBackend::::default(), shared.clone(), Some(h1)); - s.cache.sync_cache(&[], &[], vec![], vec![], Some(h2a), Some(2), true); - - let mut s = - CachingState::new(InMemoryBackend::::default(), shared.clone(), Some(h2a)); - s.cache.sync_cache( - &[], - &[], - vec![(key.clone(), Some(vec![2]))], - vec![], - Some(h3a), - Some(3), - true, - ); - - let mut s = - CachingState::new(InMemoryBackend::::default(), shared.clone(), Some(h1)); - s.cache.sync_cache(&[], &[], vec![], vec![], Some(h2b), Some(2), false); - - let mut s = - CachingState::new(InMemoryBackend::::default(), shared.clone(), Some(h2b)); - s.cache.sync_cache( - &[], - &[], - vec![(key.clone(), Some(vec![3]))], - vec![], - Some(h3b), - Some(3), - false, - ); - - let s = - CachingState::new(InMemoryBackend::::default(), shared.clone(), Some(h3a)); - assert_eq!(s.storage(&key).unwrap().unwrap(), vec![2]); - } - - #[test] - fn reverts_storage_hash() { - let root_parent = H256::random(); - let key = H256::random()[..].to_vec(); - let h1a = H256::random(); - let h1b = H256::random(); - - let shared = new_shared_cache::(256 * 1024, (0, 1)); - let mut backend = InMemoryBackend::::default(); - backend.insert( - std::iter::once((None, vec![(key.clone(), Some(vec![1]))])), - Default::default(), - ); - - let mut s = CachingState::new(backend.clone(), shared.clone(), Some(root_parent)); - s.cache.sync_cache( - &[], - &[], - vec![(key.clone(), Some(vec![2]))], - vec![], - Some(h1a), - Some(1), - true, - ); - - let mut s = CachingState::new(backend.clone(), shared.clone(), Some(root_parent)); - s.cache.sync_cache(&[], &[h1a], vec![], vec![], Some(h1b), Some(1), true); - - let s = CachingState::new(backend.clone(), shared.clone(), Some(h1b)); - assert_eq!(s.storage_hash(&key).unwrap().unwrap(), BlakeTwo256::hash(&vec![1])); - } - - #[test] - fn should_track_used_size_correctly() { - let root_parent = H256::random(); - let shared = new_shared_cache::(109, ((109 - 36), 109)); - let h0 = H256::random(); - - let mut s = CachingState::new( - InMemoryBackend::::default(), - shared.clone(), - Some(root_parent), - ); - - let key = H256::random()[..].to_vec(); - let s_key = H256::random()[..].to_vec(); - s.cache.sync_cache( - &[], - &[], - vec![(key.clone(), Some(vec![1, 2, 3]))], - vec![], - Some(h0), - Some(0), - true, - ); - // 32 key, 3 byte size - assert_eq!(shared.read().used_storage_cache_size(), 35 /* bytes */); - - let key = H256::random()[..].to_vec(); - s.cache.sync_cache( - &[], - &[], - vec![], - vec![(s_key.clone(), vec![(key.clone(), Some(vec![1, 2]))])], - Some(h0), - Some(0), - true, - ); - // 35 + (2 * 32) key, 2 byte size - assert_eq!(shared.read().used_storage_cache_size(), 101 /* bytes */); - } - - #[test] - fn should_remove_lru_items_based_on_tracking_used_size() { - let root_parent = H256::random(); - let shared = new_shared_cache::(36 * 3, (2, 3)); - let h0 = H256::random(); - - let mut s = CachingState::new( - InMemoryBackend::::default(), - shared.clone(), - Some(root_parent), - ); - - let key = H256::random()[..].to_vec(); - s.cache.sync_cache( - &[], - &[], - vec![(key.clone(), Some(vec![1, 2, 3, 4]))], - vec![], - Some(h0), - Some(0), - true, - ); - // 32 key, 4 byte size - assert_eq!(shared.read().used_storage_cache_size(), 36 /* bytes */); - - let key = H256::random()[..].to_vec(); - s.cache.sync_cache( - &[], - &[], - vec![(key.clone(), Some(vec![1, 2]))], - vec![], - Some(h0), - Some(0), - true, - ); - // 32 key, 2 byte size - assert_eq!(shared.read().used_storage_cache_size(), 34 /* bytes */); - } - - #[test] - fn fix_storage_mismatch_issue() { - sp_tracing::try_init_simple(); - let root_parent = H256::random(); - - let key = H256::random()[..].to_vec(); - - let h0 = H256::random(); - let h1 = H256::random(); - - let shared = new_shared_cache::(256 * 1024, (0, 1)); - let mut s = CachingState::new( - InMemoryBackend::::default(), - shared.clone(), - Some(root_parent), - ); - s.cache.sync_cache( - &[], - &[], - vec![(key.clone(), Some(vec![2]))], - vec![], - Some(h0), - Some(0), - true, - ); - - let mut s = - CachingState::new(InMemoryBackend::::default(), shared.clone(), Some(h0)); - s.cache.sync_cache( - &[], - &[], - vec![(key.clone(), Some(vec![3]))], - vec![], - Some(h1), - Some(1), - true, - ); - - let mut s = - CachingState::new(InMemoryBackend::::default(), shared.clone(), Some(h1)); - assert_eq!(s.storage(&key).unwrap(), Some(vec![3])); - - // Restart (or unknown block?), clear caches. - { - let mut cache = s.cache.shared_cache.write(); - let cache = &mut *cache; - cache.lru_storage.clear(); - cache.lru_hashes.clear(); - cache.lru_child_storage.clear(); - cache.modifications.clear(); - } - - // New value is written because of cache miss. - s.cache.local_cache.write().storage.insert(key.clone(), Some(vec![42])); - - // New value is propagated. - s.cache.sync_cache(&[], &[], vec![], vec![], None, None, true); - - let s = - CachingState::new(InMemoryBackend::::default(), shared.clone(), Some(h1)); - assert_eq!(s.storage(&key).unwrap(), None); - } - - #[test] - fn same_block_no_changes() { - sp_tracing::try_init_simple(); - - let root_parent = H256::random(); - let key = H256::random()[..].to_vec(); - let h1 = H256::random(); - let h2 = H256::random(); - - let shared = new_shared_cache::(256 * 1024, (0, 1)); - - let mut s = CachingState::new( - InMemoryBackend::::default(), - shared.clone(), - Some(root_parent), - ); - s.cache.sync_cache( - &[], - &[], - vec![(key.clone(), Some(vec![1]))], - vec![], - Some(h1), - Some(1), - true, - ); - assert_eq!(shared.write().lru_storage.get(&key).unwrap(), &Some(vec![1])); - - let mut s = - CachingState::new(InMemoryBackend::::default(), shared.clone(), Some(h1)); - - // commit as non-best - s.cache.sync_cache( - &[], - &[], - vec![(key.clone(), Some(vec![2]))], - vec![], - Some(h2), - Some(2), - false, - ); - - assert_eq!(shared.write().lru_storage.get(&key).unwrap(), &Some(vec![1])); - - let mut s = - CachingState::new(InMemoryBackend::::default(), shared.clone(), Some(h1)); - - // commit again as best with no changes - s.cache.sync_cache(&[], &[], vec![], vec![], Some(h2), Some(2), true); - assert_eq!(s.storage(&key).unwrap(), None); - } -} - -#[cfg(test)] -mod qc { - use std::collections::{hash_map::Entry, HashMap}; - - use quickcheck::{quickcheck, Arbitrary, TestResult}; - - use super::*; - use sp_runtime::{ - testing::{Block as RawBlock, ExtrinsicWrapper, H256}, - traits::BlakeTwo256, - }; - use sp_state_machine::InMemoryBackend; - - type Block = RawBlock>; - - type KeySet = Vec<(Vec, Option>)>; - - type KeyMap = HashMap, Option>>; - - #[derive(Debug, Clone)] - struct Node { - hash: H256, - #[allow(unused)] - parent: H256, - state: KeyMap, - changes: KeySet, - } - - impl Node { - fn new_next(&self, hash: H256, changes: KeySet) -> Self { - let mut state = self.state.clone(); - - for (k, v) in self.state.iter() { - state.insert(k.clone(), v.clone()); - } - for (k, v) in changes.clone().into_iter() { - state.insert(k, v); - } - - Self { hash, parent: self.hash, changes, state } - } - - fn new(hash: H256, parent: H256, changes: KeySet) -> Self { - let mut state = KeyMap::new(); - - for (k, v) in changes.clone().into_iter() { - state.insert(k, v); - } - - Self { hash, parent, state, changes } - } - - fn purge(&mut self, other_changes: &KeySet) { - for (k, _) in other_changes.iter() { - self.state.remove(k); - } - } - } - - #[derive(Debug, Clone)] - enum Action { - Next { hash: H256, changes: KeySet }, - Fork { depth: usize, hash: H256, changes: KeySet }, - ReorgWithImport { depth: usize, hash: H256 }, - FinalizationReorg { fork_depth: usize, depth: usize }, - } - - impl Arbitrary for Action { - fn arbitrary(gen: &mut quickcheck::Gen) -> Self { - let path = u8::arbitrary(gen); - let buf = (0..32).map(|_| u8::arbitrary(gen)).collect::>(); - - match path { - 0..=175 => Action::Next { - hash: H256::from_slice(&buf[..]), - changes: { - let mut set = Vec::new(); - for _ in 0..::arbitrary(gen) / (64 * 256 * 256 * 256) { - set.push((vec![u8::arbitrary(gen)], Some(vec![u8::arbitrary(gen)]))); - } - set - }, - }, - 176..=220 => Action::Fork { - hash: H256::from_slice(&buf[..]), - depth: ((u8::arbitrary(gen)) / 32) as usize, - changes: { - let mut set = Vec::new(); - for _ in 0..::arbitrary(gen) / (64 * 256 * 256 * 256) { - set.push((vec![u8::arbitrary(gen)], Some(vec![u8::arbitrary(gen)]))); - } - set - }, - }, - 221..=240 => { - Action::ReorgWithImport { - hash: H256::from_slice(&buf[..]), - depth: ((u8::arbitrary(gen)) / 32) as usize, // 0-7 - } - }, - _ => { - Action::FinalizationReorg { - fork_depth: ((u8::arbitrary(gen)) / 32) as usize, // 0-7 - depth: ((u8::arbitrary(gen)) / 64) as usize, // 0-3 - } - }, - } - } - } - - struct Mutator { - shared: SharedCache, - canon: Vec, - forks: HashMap>, - } - - impl Mutator { - fn new_empty() -> Self { - let shared = new_shared_cache::(256 * 1024, (0, 1)); - - Self { shared, canon: vec![], forks: HashMap::new() } - } - - fn head_state(&self, hash: H256) -> CachingState, Block> { - CachingState::new( - InMemoryBackend::::default(), - self.shared.clone(), - Some(hash), - ) - } - - fn canon_head_state(&self) -> CachingState, Block> { - self.head_state(self.canon.last().expect("Expected to be one commit").hash) - } - - fn mutate_static( - &mut self, - action: Action, - ) -> CachingState, Block> { - self.mutate(action) - .expect("Expected to provide only valid actions to the mutate_static") - } - - fn canon_len(&self) -> usize { - return self.canon.len() - } - - fn head_storage_ref(&self) -> &KeyMap { - &self.canon.last().expect("Expected to be one commit").state - } - - fn key_permutations() -> Vec> { - (0u8..255).map(|x| vec![x]).collect() - } - - fn mutate( - &mut self, - action: Action, - ) -> Result, Block>, ()> { - let state = match action { - Action::Fork { depth, hash, changes } => { - let pos = self.canon.len() as isize - depth as isize; - if pos < 0 || self.canon.len() == 0 || pos >= (self.canon.len() - 1) as isize - // no fork on top also, thus len-1 - { - return Err(()) - } - - let pos = pos as usize; - - let fork_at = self.canon[pos].hash; - - let (total_h, parent) = match self.forks.entry(fork_at) { - Entry::Occupied(occupied) => { - let chain = occupied.into_mut(); - let parent = - chain.last().expect("No empty forks are ever created").clone(); - let mut node = parent.new_next(hash, changes.clone()); - - for earlier in chain.iter() { - node.purge(&earlier.changes.clone()); - } - - chain.push(node); - - (pos + chain.len(), parent.hash) - }, - Entry::Vacant(vacant) => { - let canon_parent = &self.canon[pos]; - vacant.insert(vec![canon_parent.new_next(hash, changes.clone())]); - - (pos + 1, fork_at) - }, - }; - - let mut state = CachingState::new( - InMemoryBackend::::default(), - self.shared.clone(), - Some(parent), - ); - - state.cache.sync_cache( - &[], - &[], - changes, - vec![], - Some(hash), - Some(total_h as u64), - false, - ); - - state - }, - Action::Next { hash, changes } => { - let (next, parent_hash) = match self.canon.last() { - None => { - let parent_hash = H256::from(&[0u8; 32]); - (Node::new(hash, parent_hash, changes.clone()), parent_hash) - }, - Some(parent) => (parent.new_next(hash, changes.clone()), parent.hash), - }; - - // delete cache entries for earlier - for node in self.canon.iter_mut() { - node.purge(&next.changes); - if let Some(fork) = self.forks.get_mut(&node.hash) { - for node in fork.iter_mut() { - node.purge(&next.changes); - } - } - } - - let mut state = CachingState::new( - InMemoryBackend::::default(), - self.shared.clone(), - Some(parent_hash), - ); - - state.cache.sync_cache( - &[], - &[], - next.changes.clone(), - vec![], - Some(hash), - Some(self.canon.len() as u64 + 1), - true, - ); - - self.canon.push(next); - - state - }, - Action::ReorgWithImport { depth, hash } => { - let pos = self.canon.len() as isize - depth as isize; - if pos < 0 || pos + 1 >= self.canon.len() as isize { - return Err(()) - } - let fork_at = self.canon[pos as usize].hash; - let pos = pos as usize; - - match self.forks.get_mut(&fork_at) { - Some(chain) => { - let mut new_fork = self.canon.drain(pos + 1..).collect::>(); - - let retracted: Vec = - new_fork.iter().map(|node| node.hash).collect(); - let enacted: Vec = chain.iter().map(|node| node.hash).collect(); - - std::mem::swap(chain, &mut new_fork); - - let mut node = new_fork - .last() - .map(|node| node.new_next(hash, vec![])) - .expect("No empty fork ever created!"); - - for invalidators in chain.iter().chain(new_fork.iter()) { - node.purge(&invalidators.changes); - } - - self.canon.extend(new_fork.into_iter()); - - self.canon.push(node); - - let mut state = CachingState::new( - InMemoryBackend::::default(), - self.shared.clone(), - Some(fork_at), - ); - - let height = pos as u64 + enacted.len() as u64 + 2; - state.cache.sync_cache( - &enacted[..], - &retracted[..], - vec![], - vec![], - Some(hash), - Some(height), - true, - ); - - state - }, - None => { - return Err(()) // no reorg without a fork atm! - }, - } - }, - Action::FinalizationReorg { fork_depth, depth } => { - let pos = self.canon.len() as isize - fork_depth as isize; - if pos < 0 || pos + 1 >= self.canon.len() as isize { - return Err(()) - } - let fork_at = self.canon[pos as usize].hash; - let pos = pos as usize; - - match self.forks.get_mut(&fork_at) { - Some(fork_chain) => { - let sync_pos = fork_chain.len() as isize - - fork_chain.len() as isize - depth as isize; - if sync_pos < 0 || sync_pos >= fork_chain.len() as isize { - return Err(()) - } - let sync_pos = sync_pos as usize; - - let mut new_fork = self.canon.drain(pos + 1..).collect::>(); - - let retracted: Vec = - new_fork.iter().map(|node| node.hash).collect(); - let enacted: Vec = fork_chain - .iter() - .take(sync_pos + 1) - .map(|node| node.hash) - .collect(); - - std::mem::swap(fork_chain, &mut new_fork); - - self.shared.write().sync(&retracted, &enacted); - - self.head_state( - self.canon - .last() - .expect("wasn't forking to emptiness so there should be one!") - .hash, - ) - }, - None => { - return Err(()) // no reorg to nothing pls! - }, - } - }, - }; - - Ok(state) - } - } - - #[test] - fn smoke() { - let key = H256::random()[..].to_vec(); - let h0 = H256::random(); - let h1a = H256::random(); - let h1b = H256::random(); - let h2a = H256::random(); - let h2b = H256::random(); - let h3a = H256::random(); - let h3b = H256::random(); - - let mut mutator = Mutator::new_empty(); - mutator - .mutate_static(Action::Next { hash: h0, changes: vec![(key.clone(), Some(vec![2]))] }); - mutator.mutate_static(Action::Next { hash: h1a, changes: vec![] }); - mutator.mutate_static(Action::Fork { - depth: 2, - hash: h1b, - changes: vec![(key.clone(), Some(vec![3]))], - }); - mutator.mutate_static(Action::Fork { - depth: 2, - hash: h2b, - changes: vec![(key.clone(), Some(vec![4]))], - }); - mutator - .mutate_static(Action::Next { hash: h2a, changes: vec![(key.clone(), Some(vec![5]))] }); - mutator.mutate_static(Action::Next { hash: h3a, changes: vec![] }); - - assert_eq!( - mutator.head_state(h3a).storage(&key).unwrap().expect("there should be a value"), - vec![5] - ); - assert!(mutator.head_state(h1a).storage(&key).unwrap().is_none()); - assert!(mutator.head_state(h2b).storage(&key).unwrap().is_none()); - assert!(mutator.head_state(h1b).storage(&key).unwrap().is_none()); - - mutator.mutate_static(Action::ReorgWithImport { depth: 4, hash: h3b }); - assert!(mutator.head_state(h3a).storage(&key).unwrap().is_none()); - } - - fn is_head_match(mutator: &Mutator) -> bool { - let head_state = mutator.canon_head_state(); - - for key in Mutator::key_permutations() { - match (head_state.storage(&key).unwrap(), mutator.head_storage_ref().get(&key)) { - (Some(x), Some(y)) => - if Some(&x) != y.as_ref() { - eprintln!("{:?} != {:?}", x, y); - return false - }, - (None, Some(_y)) => { - // TODO: cache miss is not tracked atm - }, - (Some(x), None) => { - eprintln!("{:?} != ", x); - return false - }, - _ => continue, - } - } - true - } - - fn is_canon_match(mutator: &Mutator) -> bool { - for node in mutator.canon.iter() { - let head_state = mutator.head_state(node.hash); - for key in Mutator::key_permutations() { - match (head_state.storage(&key).unwrap(), node.state.get(&key)) { - (Some(x), Some(y)) => - if Some(&x) != y.as_ref() { - eprintln!("at [{}]: {:?} != {:?}", node.hash, x, y); - return false - }, - (None, Some(_y)) => { - // cache miss is not tracked atm - }, - (Some(x), None) => { - eprintln!("at [{}]: {:?} != ", node.hash, x); - return false - }, - _ => continue, - } - } - } - true - } - - #[test] - fn reorg() { - let key = H256::random()[..].to_vec(); - let h0 = H256::random(); - let h1 = H256::random(); - let h2 = H256::random(); - let h1b = H256::random(); - let h2b = H256::random(); - - let mut mutator = Mutator::new_empty(); - mutator.mutate_static(Action::Next { hash: h0, changes: vec![] }); - mutator.mutate_static(Action::Next { hash: h1, changes: vec![] }); - mutator - .mutate_static(Action::Next { hash: h2, changes: vec![(key.clone(), Some(vec![2]))] }); - mutator.mutate_static(Action::Fork { - depth: 2, - hash: h1b, - changes: vec![(key.clone(), Some(vec![3]))], - }); - mutator.mutate_static(Action::ReorgWithImport { depth: 2, hash: h2b }); - - assert!(is_head_match(&mutator)) - } - - fn key(k: u8) -> Vec { - vec![k] - } - fn val(v: u8) -> Option> { - Some(vec![v]) - } - fn keyval(k: u8, v: u8) -> KeySet { - vec![(key(k), val(v))] - } - - #[test] - fn reorg2() { - let h0 = H256::random(); - let h1a = H256::random(); - let h1b = H256::random(); - let h2b = H256::random(); - let h2a = H256::random(); - let h3a = H256::random(); - - let mut mutator = Mutator::new_empty(); - mutator.mutate_static(Action::Next { hash: h0, changes: keyval(1, 1) }); - mutator.mutate_static(Action::Next { hash: h1a, changes: keyval(1, 1) }); - mutator.mutate_static(Action::Fork { depth: 2, hash: h1b, changes: keyval(2, 2) }); - - mutator.mutate_static(Action::Next { hash: h2a, changes: keyval(3, 3) }); - mutator.mutate_static(Action::Next { hash: h3a, changes: keyval(4, 4) }); - mutator.mutate_static(Action::ReorgWithImport { depth: 4, hash: h2b }); - - assert!(is_head_match(&mutator)) - } - - #[test] - fn fork2() { - let h1 = H256::random(); - let h2a = H256::random(); - let h2b = H256::random(); - let h3a = H256::random(); - let h3b = H256::random(); - - let mut mutator = Mutator::new_empty(); - mutator.mutate_static(Action::Next { hash: h1, changes: vec![] }); - mutator.mutate_static(Action::Next { hash: h2a, changes: vec![] }); - mutator.mutate_static(Action::Next { hash: h3a, changes: keyval(1, 1) }); - - mutator.mutate_static(Action::Fork { depth: 2, hash: h2b, changes: vec![] }); - mutator.mutate_static(Action::Fork { depth: 2, hash: h3b, changes: keyval(1, 2) }); - - assert!(is_head_match(&mutator)) - } - - #[test] - fn fork3() { - let h1 = H256::random(); - let h2a = H256::random(); - let h2b = H256::random(); - let h3a = H256::random(); - - let mut mutator = Mutator::new_empty(); - mutator.mutate_static(Action::Next { hash: h1, changes: keyval(1, 1) }); - mutator.mutate_static(Action::Next { hash: h2a, changes: keyval(2, 2) }); - mutator.mutate_static(Action::Next { hash: h3a, changes: keyval(3, 3) }); - - mutator.mutate_static(Action::Fork { depth: 2, hash: h2b, changes: keyval(1, 3) }); - - assert!(is_canon_match(&mutator)) - } - - quickcheck! { - fn head_complete(actions: Vec) -> TestResult { - let mut mutator = Mutator::new_empty(); - - for action in actions.into_iter() { - if let Err(_) = mutator.mutate(action) { - return TestResult::discard(); - } - } - - if mutator.canon_len() == 0 { - return TestResult::discard(); - } - - TestResult::from_bool(is_head_match(&mutator)) - } - - fn canon_complete(actions: Vec) -> TestResult { - let mut mutator = Mutator::new_empty(); - - for action in actions.into_iter() { - if let Err(_) = mutator.mutate(action) { - return TestResult::discard(); - } - } - - if mutator.canon_len() == 0 { - return TestResult::discard(); - } - - TestResult::from_bool(is_canon_match(&mutator)) - } - } -} From 419b840fa1f170a1c2027b63310549d1bf6d2ed0 Mon Sep 17 00:00:00 2001 From: Roman Useinov Date: Mon, 23 Jan 2023 15:32:09 +0100 Subject: [PATCH 041/558] [Fix] CountedMap::set now takes Counter into account (#13214) * [Fix] CountedMap::set now takes Counter into account * introduce tests for StorageMap --- .../support/src/storage/types/counted_map.rs | 55 ++++++++++++++++++- frame/support/src/storage/types/map.rs | 42 ++++++++++++++ 2 files changed, 96 insertions(+), 1 deletion(-) diff --git a/frame/support/src/storage/types/counted_map.rs b/frame/support/src/storage/types/counted_map.rs index 8c1943476..3361c4093 100644 --- a/frame/support/src/storage/types/counted_map.rs +++ b/frame/support/src/storage/types/counted_map.rs @@ -134,7 +134,10 @@ where /// Store or remove the value to be associated with `key` so that `get` returns the `query`. pub fn set>(key: KeyArg, q: QueryKind::Query) { - ::Map::set(key, q) + match QueryKind::from_query_to_optional_value(q) { + Some(v) => Self::insert(key, v), + None => Self::remove(key), + } } /// Swap the values of two keys. @@ -745,6 +748,22 @@ mod test { // Test initialize_counter. assert_eq!(A::initialize_counter(), 2); + + // Set non-existing. + A::set(30, 100); + + assert_eq!(A::contains_key(30), true); + assert_eq!(A::get(30), 100); + assert_eq!(A::try_get(30), Ok(100)); + assert_eq!(A::count(), 3); + + // Set existing. + A::set(30, 101); + + assert_eq!(A::contains_key(30), true); + assert_eq!(A::get(30), 101); + assert_eq!(A::try_get(30), Ok(101)); + assert_eq!(A::count(), 3); }) } @@ -976,6 +995,40 @@ mod test { // Test initialize_counter. assert_eq!(B::initialize_counter(), 2); + + // Set non-existing. + B::set(30, Some(100)); + + assert_eq!(B::contains_key(30), true); + assert_eq!(B::get(30), Some(100)); + assert_eq!(B::try_get(30), Ok(100)); + assert_eq!(B::count(), 3); + + // Set existing. + B::set(30, Some(101)); + + assert_eq!(B::contains_key(30), true); + assert_eq!(B::get(30), Some(101)); + assert_eq!(B::try_get(30), Ok(101)); + assert_eq!(B::count(), 3); + + // Unset existing. + B::set(30, None); + + assert_eq!(B::contains_key(30), false); + assert_eq!(B::get(30), None); + assert_eq!(B::try_get(30), Err(())); + + assert_eq!(B::count(), 2); + + // Unset non-existing. + B::set(31, None); + + assert_eq!(B::contains_key(31), false); + assert_eq!(B::get(31), None); + assert_eq!(B::try_get(31), Err(())); + + assert_eq!(B::count(), 2); }) } diff --git a/frame/support/src/storage/types/map.rs b/frame/support/src/storage/types/map.rs index 0f89e2378..3e2b744d7 100644 --- a/frame/support/src/storage/types/map.rs +++ b/frame/support/src/storage/types/map.rs @@ -627,6 +627,48 @@ mod test { assert_eq!(AValueQueryWithAnOnEmpty::take(2), 97); assert_eq!(A::contains_key(2), false); + // Set non-existing. + B::set(30, 100); + + assert_eq!(B::contains_key(30), true); + assert_eq!(B::get(30), 100); + assert_eq!(B::try_get(30), Ok(100)); + + // Set existing. + B::set(30, 101); + + assert_eq!(B::contains_key(30), true); + assert_eq!(B::get(30), 101); + assert_eq!(B::try_get(30), Ok(101)); + + // Set non-existing. + A::set(30, Some(100)); + + assert_eq!(A::contains_key(30), true); + assert_eq!(A::get(30), Some(100)); + assert_eq!(A::try_get(30), Ok(100)); + + // Set existing. + A::set(30, Some(101)); + + assert_eq!(A::contains_key(30), true); + assert_eq!(A::get(30), Some(101)); + assert_eq!(A::try_get(30), Ok(101)); + + // Unset existing. + A::set(30, None); + + assert_eq!(A::contains_key(30), false); + assert_eq!(A::get(30), None); + assert_eq!(A::try_get(30), Err(())); + + // Unset non-existing. + A::set(31, None); + + assert_eq!(A::contains_key(31), false); + assert_eq!(A::get(31), None); + assert_eq!(A::try_get(31), Err(())); + B::insert(2, 10); assert_eq!(A::migrate_key::(2), Some(10)); assert_eq!(A::contains_key(2), true); From 52d72b396393696fefd2cb50cd6a48707675ee60 Mon Sep 17 00:00:00 2001 From: Muharem Ismailov Date: Mon, 23 Jan 2023 16:39:11 +0100 Subject: [PATCH 042/558] debug assert events at genesis (#13217) --- frame/system/src/lib.rs | 14 ++++++++++++++ frame/system/src/tests.rs | 3 ++- 2 files changed, 16 insertions(+), 1 deletion(-) diff --git a/frame/system/src/lib.rs b/frame/system/src/lib.rs index 32627896e..4bd5cf629 100644 --- a/frame/system/src/lib.rs +++ b/frame/system/src/lib.rs @@ -1247,6 +1247,8 @@ impl Pallet { } /// Deposits an event into this block's event record. + /// + /// NOTE: Events not registered at the genesis block and quietly omitted. pub fn deposit_event(event: impl Into) { Self::deposit_event_indexed(&[], event.into()); } @@ -1256,6 +1258,8 @@ impl Pallet { /// /// This will update storage entries that correspond to the specified topics. /// It is expected that light-clients could subscribe to this topics. + /// + /// NOTE: Events not registered at the genesis block and quietly omitted. pub fn deposit_event_indexed(topics: &[T::Hash], event: T::RuntimeEvent) { let block_number = Self::block_number(); // Don't populate events on genesis. @@ -1445,8 +1449,14 @@ impl Pallet { /// NOTE: This should only be used in tests. Reading events from the runtime can have a large /// impact on the PoV size of a block. Users should use alternative and well bounded storage /// items for any behavior like this. + /// + /// NOTE: Events not registered at the genesis block and quietly omitted. #[cfg(any(feature = "std", feature = "runtime-benchmarks", test))] pub fn events() -> Vec> { + debug_assert!( + !Self::block_number().is_zero(), + "events not registered at the genesis block" + ); // Dereferencing the events here is fine since we are not in the // memory-restricted runtime. Self::read_events_no_consensus().map(|e| *e).collect() @@ -1501,6 +1511,8 @@ impl Pallet { } /// Assert the given `event` exists. + /// + /// NOTE: Events not registered at the genesis block and quietly omitted. #[cfg(any(feature = "std", feature = "runtime-benchmarks", test))] pub fn assert_has_event(event: T::RuntimeEvent) { let events = Self::events(); @@ -1511,6 +1523,8 @@ impl Pallet { } /// Assert the last event equal to the given `event`. + /// + /// NOTE: Events not registered at the genesis block and quietly omitted. #[cfg(any(feature = "std", feature = "runtime-benchmarks", test))] pub fn assert_last_event(event: T::RuntimeEvent) { let last_event = Self::events().last().expect("events expected").event.clone(); diff --git a/frame/system/src/tests.rs b/frame/system/src/tests.rs index c42131c45..8223f6674 100644 --- a/frame/system/src/tests.rs +++ b/frame/system/src/tests.rs @@ -627,7 +627,8 @@ fn events_not_emitted_during_genesis() { assert!(System::block_number().is_zero()); let mut account_data = AccountInfo::default(); System::on_created_account(Default::default(), &mut account_data); - assert!(System::events().is_empty()); + // No events registered at the genesis block + assert!(!System::read_events_no_consensus().any(|_| true)); // Events will be emitted starting on block 1 System::set_block_number(1); System::on_created_account(Default::default(), &mut account_data); From 9052607ad7a808e7a5e9e735460840a15d48aee7 Mon Sep 17 00:00:00 2001 From: Michal Kucharczyk <1728078+michalkucharczyk@users.noreply.github.com> Date: Tue, 24 Jan 2023 13:52:01 +0100 Subject: [PATCH 043/558] BlockId removal: refactor: CallExecutor trait (#13173) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * BlockId removal: refactor: CallExecutor trait It changes the arguments of CallExecutor methods: - `call`, 'contextual_call', 'runtime_version', 'prove_execution' from: `BlockId` to: `Block::Hash` This PR is part of BlockId::Number refactoring analysis (paritytech/substrate#11292) * Apply suggestions from code review Co-authored-by: Bastian Köcher * ".git/.scripts/commands/fmt/fmt.sh" Co-authored-by: Bastian Köcher Co-authored-by: command-bot <> --- client/api/src/call_executor.rs | 10 ++-- client/finality-grandpa/src/lib.rs | 6 +-- .../rpc-spec-v2/src/chain_head/chain_head.rs | 2 +- client/rpc/src/state/state_full.rs | 2 +- client/service/src/client/call_executor.rs | 54 +++++++++---------- client/service/src/client/client.rs | 11 ++-- client/service/src/client/wasm_substitutes.rs | 20 +++---- primitives/version/src/lib.rs | 6 +-- 8 files changed, 55 insertions(+), 56 deletions(-) diff --git a/client/api/src/call_executor.rs b/client/api/src/call_executor.rs index 7a4238501..74a2b8b85 100644 --- a/client/api/src/call_executor.rs +++ b/client/api/src/call_executor.rs @@ -19,7 +19,7 @@ //! A method call executor interface. use sc_executor::{RuntimeVersion, RuntimeVersionOf}; -use sp_runtime::{generic::BlockId, traits::Block as BlockT}; +use sp_runtime::traits::Block as BlockT; use sp_state_machine::{ExecutionStrategy, OverlayedChanges, StorageProof}; use std::cell::RefCell; @@ -54,7 +54,7 @@ pub trait CallExecutor: RuntimeVersionOf { /// No changes are made. fn call( &self, - id: &BlockId, + at_hash: B::Hash, method: &str, call_data: &[u8], strategy: ExecutionStrategy, @@ -67,7 +67,7 @@ pub trait CallExecutor: RuntimeVersionOf { /// of the execution context. fn contextual_call( &self, - at: &BlockId, + at_hash: B::Hash, method: &str, call_data: &[u8], changes: &RefCell, @@ -83,14 +83,14 @@ pub trait CallExecutor: RuntimeVersionOf { /// Extract RuntimeVersion of given block /// /// No changes are made. - fn runtime_version(&self, id: &BlockId) -> Result; + fn runtime_version(&self, at_hash: B::Hash) -> Result; /// Prove the execution of the given `method`. /// /// No changes are made. fn prove_execution( &self, - at: &BlockId, + at_hash: B::Hash, method: &str, call_data: &[u8], ) -> Result<(Vec, StorageProof), sp_blockchain::Error>; diff --git a/client/finality-grandpa/src/lib.rs b/client/finality-grandpa/src/lib.rs index 8758efef6..15d689e91 100644 --- a/client/finality-grandpa/src/lib.rs +++ b/client/finality-grandpa/src/lib.rs @@ -464,10 +464,10 @@ pub trait GenesisAuthoritySetProvider { fn get(&self) -> Result; } -impl GenesisAuthoritySetProvider - for Arc> +impl GenesisAuthoritySetProvider for Arc where E: CallExecutor, + Client: ExecutorProvider + HeaderBackend, { fn get(&self) -> Result { // This implementation uses the Grandpa runtime API instead of reading directly from the @@ -475,7 +475,7 @@ where // the chain, whereas the runtime API is backwards compatible. self.executor() .call( - &BlockId::Number(Zero::zero()), + self.expect_block_hash_from_id(&BlockId::Number(Zero::zero()))?, "GrandpaApi_grandpa_authorities", &[], ExecutionStrategy::NativeElseWasm, diff --git a/client/rpc-spec-v2/src/chain_head/chain_head.rs b/client/rpc-spec-v2/src/chain_head/chain_head.rs index c63d373e0..2a9cabaf2 100644 --- a/client/rpc-spec-v2/src/chain_head/chain_head.rs +++ b/client/rpc-spec-v2/src/chain_head/chain_head.rs @@ -744,7 +744,7 @@ where let res = client .executor() .call( - &BlockId::Hash(hash), + hash, &function, &call_parameters, client.execution_extensions().strategies().other, diff --git a/client/rpc/src/state/state_full.rs b/client/rpc/src/state/state_full.rs index d8fe39030..58dfd9ea8 100644 --- a/client/rpc/src/state/state_full.rs +++ b/client/rpc/src/state/state_full.rs @@ -203,7 +203,7 @@ where self.client .executor() .call( - &BlockId::Hash(block), + block, &method, &call_data, self.client.execution_extensions().strategies().other, diff --git a/client/service/src/client/call_executor.rs b/client/service/src/client/call_executor.rs index 7ee19671d..7906d6224 100644 --- a/client/service/src/client/call_executor.rs +++ b/client/service/src/client/call_executor.rs @@ -81,13 +81,13 @@ where fn check_override<'a>( &'a self, onchain_code: RuntimeCode<'a>, - id: &BlockId, + hash: ::Hash, ) -> sp_blockchain::Result<(RuntimeCode<'a>, RuntimeVersion)> where Block: BlockT, B: backend::Backend, { - let on_chain_version = self.on_chain_runtime_version(id)?; + let on_chain_version = self.on_chain_runtime_version(hash)?; let code_and_version = if let Some(d) = self.wasm_override.as_ref().as_ref().and_then(|o| { o.get( &on_chain_version.spec_version, @@ -95,18 +95,18 @@ where &on_chain_version.spec_name, ) }) { - log::debug!(target: "wasm_overrides", "using WASM override for block {}", id); + log::debug!(target: "wasm_overrides", "using WASM override for block {}", hash); d } else if let Some(s) = self.wasm_substitutes - .get(on_chain_version.spec_version, onchain_code.heap_pages, id) + .get(on_chain_version.spec_version, onchain_code.heap_pages, hash) { - log::debug!(target: "wasm_substitutes", "Using WASM substitute for block {:?}", id); + log::debug!(target: "wasm_substitutes", "Using WASM substitute for block {:?}", hash); s } else { log::debug!( target: "wasm_overrides", - "Neither WASM override nor substitute available for block {id}, using onchain code", + "Neither WASM override nor substitute available for block {hash}, using onchain code", ); (onchain_code, on_chain_version) }; @@ -115,14 +115,10 @@ where } /// Returns the on chain runtime version. - fn on_chain_runtime_version( - &self, - id: &BlockId, - ) -> sp_blockchain::Result { + fn on_chain_runtime_version(&self, hash: Block::Hash) -> sp_blockchain::Result { let mut overlay = OverlayedChanges::default(); - let at_hash = self.backend.blockchain().expect_block_hash_from_id(id)?; - let state = self.backend.state_at(at_hash)?; + let state = self.backend.state_at(hash)?; let mut cache = StorageTransactionCache::::default(); let mut ext = Ext::new(&mut overlay, &mut cache, &state, None); let state_runtime_code = sp_state_machine::backend::BackendRuntimeCode::new(&state); @@ -166,21 +162,21 @@ where fn call( &self, - at: &BlockId, + at_hash: Block::Hash, method: &str, call_data: &[u8], strategy: ExecutionStrategy, ) -> sp_blockchain::Result> { let mut changes = OverlayedChanges::default(); - let at_hash = self.backend.blockchain().expect_block_hash_from_id(at)?; - let at_number = self.backend.blockchain().expect_block_number_from_id(at)?; + let at_number = + self.backend.blockchain().expect_block_number_from_id(&BlockId::Hash(at_hash))?; let state = self.backend.state_at(at_hash)?; let state_runtime_code = sp_state_machine::backend::BackendRuntimeCode::new(&state); let runtime_code = state_runtime_code.runtime_code().map_err(sp_blockchain::Error::RuntimeCode)?; - let runtime_code = self.check_override(runtime_code, at)?.0; + let runtime_code = self.check_override(runtime_code, at_hash)?.0; let extensions = self.execution_extensions.extensions( at_hash, @@ -206,7 +202,7 @@ where fn contextual_call( &self, - at: &BlockId, + at_hash: Block::Hash, method: &str, call_data: &[u8], changes: &RefCell, @@ -216,8 +212,8 @@ where ) -> Result, sp_blockchain::Error> { let mut storage_transaction_cache = storage_transaction_cache.map(|c| c.borrow_mut()); - let at_hash = self.backend.blockchain().expect_block_hash_from_id(at)?; - let at_number = self.backend.blockchain().expect_block_number_from_id(at)?; + let at_number = + self.backend.blockchain().expect_block_number_from_id(&BlockId::Hash(at_hash))?; let state = self.backend.state_at(at_hash)?; let (execution_manager, extensions) = @@ -232,7 +228,7 @@ where let runtime_code = state_runtime_code.runtime_code().map_err(sp_blockchain::Error::RuntimeCode)?; - let runtime_code = self.check_override(runtime_code, at)?.0; + let runtime_code = self.check_override(runtime_code, at_hash)?.0; match recorder { Some(recorder) => { @@ -275,24 +271,23 @@ where .map_err(Into::into) } - fn runtime_version(&self, id: &BlockId) -> sp_blockchain::Result { - let at_hash = self.backend.blockchain().expect_block_hash_from_id(id)?; + fn runtime_version(&self, at_hash: Block::Hash) -> sp_blockchain::Result { let state = self.backend.state_at(at_hash)?; let state_runtime_code = sp_state_machine::backend::BackendRuntimeCode::new(&state); let runtime_code = state_runtime_code.runtime_code().map_err(sp_blockchain::Error::RuntimeCode)?; - self.check_override(runtime_code, id).map(|(_, v)| v) + self.check_override(runtime_code, at_hash).map(|(_, v)| v) } fn prove_execution( &self, - at: &BlockId, + at_hash: Block::Hash, method: &str, call_data: &[u8], ) -> sp_blockchain::Result<(Vec, StorageProof)> { - let at_hash = self.backend.blockchain().expect_block_hash_from_id(at)?; - let at_number = self.backend.blockchain().expect_block_number_from_id(at)?; + let at_number = + self.backend.blockchain().expect_block_number_from_id(&BlockId::Hash(at_hash))?; let state = self.backend.state_at(at_hash)?; let trie_backend = state.as_trie_backend(); @@ -300,7 +295,7 @@ where let state_runtime_code = sp_state_machine::backend::BackendRuntimeCode::new(trie_backend); let runtime_code = state_runtime_code.runtime_code().map_err(sp_blockchain::Error::RuntimeCode)?; - let runtime_code = self.check_override(runtime_code, at)?.0; + let runtime_code = self.check_override(runtime_code, at_hash)?.0; sp_state_machine::prove_execution_on_trie_backend( trie_backend, @@ -340,7 +335,7 @@ where E: CodeExecutor + RuntimeVersionOf + Clone + 'static, Block: BlockT, { - fn runtime_version(&self, at: &BlockId) -> Result { + fn runtime_version(&self, at: Block::Hash) -> Result { CallExecutor::runtime_version(self, at).map_err(|e| e.to_string()) } } @@ -359,6 +354,7 @@ where #[cfg(test)] mod tests { use super::*; + use backend::Backend; use sc_client_api::in_mem; use sc_executor::{NativeElseWasmExecutor, WasmExecutionMethod}; use sp_core::{ @@ -432,7 +428,7 @@ mod tests { }; let check = call_executor - .check_override(onchain_code, &BlockId::Number(Default::default())) + .check_override(onchain_code, backend.blockchain().info().genesis_hash) .expect("RuntimeCode override") .0; diff --git a/client/service/src/client/client.rs b/client/service/src/client/client.rs index d32baa671..6a75fad62 100644 --- a/client/service/src/client/client.rs +++ b/client/service/src/client/client.rs @@ -493,7 +493,8 @@ where /// Get the RuntimeVersion at a given block. pub fn runtime_version_at(&self, id: &BlockId) -> sp_blockchain::Result { - CallExecutor::runtime_version(&self.executor, id) + let hash = self.backend.blockchain().expect_block_hash_from_id(id)?; + CallExecutor::runtime_version(&self.executor, hash) } /// Apply a checked and validated block to an operation. If a justification is provided @@ -1241,7 +1242,7 @@ where method: &str, call_data: &[u8], ) -> sp_blockchain::Result<(Vec, StorageProof)> { - self.executor.prove_execution(&BlockId::Hash(hash), method, call_data) + self.executor.prove_execution(hash, method, call_data) } fn read_proof_collection( @@ -1726,9 +1727,10 @@ where &self, params: CallApiAtParams, ) -> Result, sp_api::ApiError> { + let at_hash = self.expect_block_hash_from_id(params.at)?; self.executor .contextual_call( - params.at, + at_hash, params.function, ¶ms.arguments, params.overlayed_changes, @@ -1740,7 +1742,8 @@ where } fn runtime_version_at(&self, at: &BlockId) -> Result { - CallExecutor::runtime_version(&self.executor, at).map_err(Into::into) + let hash = self.backend.blockchain().expect_block_hash_from_id(at)?; + CallExecutor::runtime_version(&self.executor, hash).map_err(Into::into) } fn state_at(&self, at: &BlockId) -> Result { diff --git a/client/service/src/client/wasm_substitutes.rs b/client/service/src/client/wasm_substitutes.rs index a7a3b2cbb..ed931d3da 100644 --- a/client/service/src/client/wasm_substitutes.rs +++ b/client/service/src/client/wasm_substitutes.rs @@ -22,10 +22,7 @@ use sc_client_api::backend; use sc_executor::RuntimeVersionOf; use sp_blockchain::{HeaderBackend, Result}; use sp_core::traits::{FetchRuntimeCode, RuntimeCode, WrappedRuntimeCode}; -use sp_runtime::{ - generic::BlockId, - traits::{Block as BlockT, NumberFor}, -}; +use sp_runtime::traits::{Block as BlockT, NumberFor}; use sp_state_machine::BasicExternalities; use sp_version::RuntimeVersion; use std::{ @@ -54,10 +51,13 @@ impl WasmSubstitute { RuntimeCode { code_fetcher: self, hash: self.hash.clone(), heap_pages } } - /// Returns `true` when the substitute matches for the given `block_id`. - fn matches(&self, block_id: &BlockId, backend: &impl backend::Backend) -> bool { - let requested_block_number = - backend.blockchain().block_number_from_id(block_id).ok().flatten(); + /// Returns `true` when the substitute matches for the given `hash`. + fn matches( + &self, + hash: ::Hash, + backend: &impl backend::Backend, + ) -> bool { + let requested_block_number = backend.blockchain().number(hash).ok().flatten(); Some(self.block_number) <= requested_block_number } @@ -147,10 +147,10 @@ where &self, spec: u32, pages: Option, - block_id: &BlockId, + hash: Block::Hash, ) -> Option<(RuntimeCode<'_>, RuntimeVersion)> { let s = self.substitutes.get(&spec)?; - s.matches(block_id, &*self.backend) + s.matches(hash, &*self.backend) .then(|| (s.runtime_code(pages), s.version.clone())) } diff --git a/primitives/version/src/lib.rs b/primitives/version/src/lib.rs index 0bd62f0ba..37bb15afb 100644 --- a/primitives/version/src/lib.rs +++ b/primitives/version/src/lib.rs @@ -48,7 +48,7 @@ pub use sp_runtime::{create_runtime_str, StateVersion}; pub use sp_std; #[cfg(feature = "std")] -use sp_runtime::{generic::BlockId, traits::Block as BlockT}; +use sp_runtime::traits::Block as BlockT; #[cfg(feature = "std")] pub mod embed; @@ -370,14 +370,14 @@ pub trait GetNativeVersion { #[cfg(feature = "std")] pub trait GetRuntimeVersionAt { /// Returns the version of runtime at the given block. - fn runtime_version(&self, at: &BlockId) -> Result; + fn runtime_version(&self, at: ::Hash) -> Result; } #[cfg(feature = "std")] impl, Block: BlockT> GetRuntimeVersionAt for std::sync::Arc { - fn runtime_version(&self, at: &BlockId) -> Result { + fn runtime_version(&self, at: ::Hash) -> Result { (&**self).runtime_version(at) } } From ca5a505b7085f7af51eb76c686cc80b55bf6d5ed Mon Sep 17 00:00:00 2001 From: Michal Kucharczyk <1728078+michalkucharczyk@users.noreply.github.com> Date: Tue, 24 Jan 2023 14:26:04 +0100 Subject: [PATCH 044/558] service: storage monitor added (#13082) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * service: storage monitor added Storage monitor added. It uses `notify` create to get notifications about any changes to monitored path (which is database path). Notifications are consumed in essential task which terminates when available storage space drops below given threshold. Closes: #12399 * Cargo.lock updated * misspell * fs events throttling added * minor updates * filter out non mutating events * misspell * ".git/.scripts/commands/fmt/fmt.sh" * Update client/service/src/storage_monitor.rs Co-authored-by: Anton * storage-monitor crate added * cleanup: configuration + service builder * storage_monitor in custom service (wip) * copy-paste bad desc fixed * notify removed * storage_monitor added to node * fix for clippy * publish = false * Update bin/node/cli/src/command.rs Co-authored-by: Dmitry Markin * Apply suggestions from code review Co-authored-by: Bastian Köcher * crate name: storage-monitor -> sc-storage-monitor * error handling improved * Apply suggestions from code review Co-authored-by: Bastian Köcher * publish=false removed Co-authored-by: command-bot <> Co-authored-by: Anton Co-authored-by: Dmitry Markin Co-authored-by: Bastian Köcher --- Cargo.lock | 31 +++++ Cargo.toml | 1 + bin/node/cli/Cargo.toml | 2 + bin/node/cli/src/cli.rs | 4 + bin/node/cli/src/command.rs | 3 +- bin/node/cli/src/service.rs | 19 ++- client/cli/src/params/database_params.rs | 4 +- client/service/Cargo.toml | 1 + client/service/src/error.rs | 3 + client/storage-monitor/Cargo.toml | 20 +++ client/storage-monitor/src/lib.rs | 149 +++++++++++++++++++++++ 11 files changed, 227 insertions(+), 10 deletions(-) create mode 100644 client/storage-monitor/Cargo.toml create mode 100644 client/storage-monitor/src/lib.rs diff --git a/Cargo.lock b/Cargo.lock index 08948ffbd..214c4f72c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4665,6 +4665,20 @@ dependencies = [ "memoffset 0.6.5", ] +[[package]] +name = "nix" +version = "0.26.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "46a58d1d356c6597d08cde02c2f09d785b09e28711837b1ed667dc652c08a694" +dependencies = [ + "bitflags", + "cfg-if", + "libc", + "memoffset 0.7.1", + "pin-utils", + "static_assertions", +] + [[package]] name = "node-bench" version = "0.9.0-dev" @@ -4752,6 +4766,7 @@ dependencies = [ "sc-rpc", "sc-service", "sc-service-test", + "sc-storage-monitor", "sc-sync-state-rpc", "sc-sysinfo", "sc-telemetry", @@ -8895,6 +8910,7 @@ dependencies = [ "sc-rpc", "sc-rpc-server", "sc-rpc-spec-v2", + "sc-storage-monitor", "sc-sysinfo", "sc-telemetry", "sc-tracing", @@ -8973,6 +8989,21 @@ dependencies = [ "sp-core", ] +[[package]] +name = "sc-storage-monitor" +version = "0.1.0" +dependencies = [ + "clap 4.0.32", + "futures", + "log", + "nix 0.26.1", + "sc-client-db", + "sc-utils", + "sp-core", + "thiserror", + "tokio", +] + [[package]] name = "sc-sync-state-rpc" version = "0.10.0-dev" diff --git a/Cargo.toml b/Cargo.toml index 8f55d8e52..f7eedadd1 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -64,6 +64,7 @@ members = [ "client/service", "client/service/test", "client/state-db", + "client/storage-monitor", "client/sysinfo", "client/sync-state-rpc", "client/telemetry", diff --git a/bin/node/cli/Cargo.toml b/bin/node/cli/Cargo.toml index 4d9279b85..8a883ee01 100644 --- a/bin/node/cli/Cargo.toml +++ b/bin/node/cli/Cargo.toml @@ -81,6 +81,7 @@ sc-executor = { version = "0.10.0-dev", path = "../../../client/executor" } sc-authority-discovery = { version = "0.10.0-dev", path = "../../../client/authority-discovery" } sc-sync-state-rpc = { version = "0.10.0-dev", path = "../../../client/sync-state-rpc" } sc-sysinfo = { version = "6.0.0-dev", path = "../../../client/sysinfo" } +sc-storage-monitor = { version = "0.1.0", path = "../../../client/storage-monitor" } # frame dependencies frame-system = { version = "4.0.0-dev", path = "../../../frame/system" } @@ -138,6 +139,7 @@ substrate-frame-cli = { version = "4.0.0-dev", optional = true, path = "../../.. try-runtime-cli = { version = "0.10.0-dev", optional = true, path = "../../../utils/frame/try-runtime/cli" } sc-cli = { version = "0.10.0-dev", path = "../../../client/cli", optional = true } pallet-balances = { version = "4.0.0-dev", path = "../../../frame/balances" } +sc-storage-monitor = { version = "0.1.0", path = "../../../client/storage-monitor" } [features] default = ["cli"] diff --git a/bin/node/cli/src/cli.rs b/bin/node/cli/src/cli.rs index bb7f8a4c6..7bea336c8 100644 --- a/bin/node/cli/src/cli.rs +++ b/bin/node/cli/src/cli.rs @@ -36,6 +36,10 @@ pub struct Cli { /// telemetry, if telemetry is enabled. #[arg(long)] pub no_hardware_benchmarks: bool, + + #[allow(missing_docs)] + #[clap(flatten)] + pub storage_monitor: sc_storage_monitor::StorageMonitorParams, } /// Possible subcommands of the main binary. diff --git a/bin/node/cli/src/command.rs b/bin/node/cli/src/command.rs index fd464bbc9..2ed8a2c75 100644 --- a/bin/node/cli/src/command.rs +++ b/bin/node/cli/src/command.rs @@ -87,8 +87,7 @@ pub fn run() -> Result<()> { None => { let runner = cli.create_runner(&cli.run)?; runner.run_node_until_exit(|config| async move { - service::new_full(config, cli.no_hardware_benchmarks) - .map_err(sc_cli::Error::Service) + service::new_full(config, cli).map_err(sc_cli::Error::Service) }) }, Some(Subcommand::Inspect(cmd)) => { diff --git a/bin/node/cli/src/service.rs b/bin/node/cli/src/service.rs index d77a333df..e32908794 100644 --- a/bin/node/cli/src/service.rs +++ b/bin/node/cli/src/service.rs @@ -20,6 +20,7 @@ //! Service implementation. Specialized wrapper over substrate service. +use crate::Cli; use codec::Encode; use frame_benchmarking_cli::SUBSTRATE_REFERENCE_HARDWARE; use frame_system_rpc_runtime_api::AccountNonceApi; @@ -556,12 +557,18 @@ pub fn new_full_base( } /// Builds a new service for a full client. -pub fn new_full( - config: Configuration, - disable_hardware_benchmarks: bool, -) -> Result { - new_full_base(config, disable_hardware_benchmarks, |_, _| ()) - .map(|NewFullBase { task_manager, .. }| task_manager) +pub fn new_full(config: Configuration, cli: Cli) -> Result { + let database_source = config.database.clone(); + let task_manager = new_full_base(config, cli.no_hardware_benchmarks, |_, _| ()) + .map(|NewFullBase { task_manager, .. }| task_manager)?; + + sc_storage_monitor::StorageMonitorService::try_spawn( + cli.storage_monitor, + database_source, + &task_manager.spawn_essential_handle(), + )?; + + Ok(task_manager) } #[cfg(test)] diff --git a/client/cli/src/params/database_params.rs b/client/cli/src/params/database_params.rs index fdd362258..06a154fd6 100644 --- a/client/cli/src/params/database_params.rs +++ b/client/cli/src/params/database_params.rs @@ -19,7 +19,7 @@ use crate::arg_enums::Database; use clap::Args; -/// Parameters for block import. +/// Parameters for database #[derive(Debug, Clone, PartialEq, Args)] pub struct DatabaseParams { /// Select database backend to use. @@ -32,7 +32,7 @@ pub struct DatabaseParams { } impl DatabaseParams { - /// Limit the memory the database cache can use. + /// Database backend pub fn database(&self) -> Option { self.database } diff --git a/client/service/Cargo.toml b/client/service/Cargo.toml index 6122895d2..b4ce3bbbb 100644 --- a/client/service/Cargo.toml +++ b/client/service/Cargo.toml @@ -73,6 +73,7 @@ sc-offchain = { version = "4.0.0-dev", path = "../offchain" } prometheus-endpoint = { package = "substrate-prometheus-endpoint", path = "../../utils/prometheus", version = "0.10.0-dev" } sc-tracing = { version = "4.0.0-dev", path = "../tracing" } sc-sysinfo = { version = "6.0.0-dev", path = "../sysinfo" } +sc-storage-monitor = { version = "0.1.0", path = "../storage-monitor" } tracing = "0.1.29" tracing-futures = { version = "0.2.4" } async-trait = "0.1.57" diff --git a/client/service/src/error.rs b/client/service/src/error.rs index 001a83922..ec2951193 100644 --- a/client/service/src/error.rs +++ b/client/service/src/error.rs @@ -48,6 +48,9 @@ pub enum Error { #[error(transparent)] Telemetry(#[from] sc_telemetry::Error), + #[error(transparent)] + Storage(#[from] sc_storage_monitor::Error), + #[error("Best chain selection strategy (SelectChain) is not provided.")] SelectChainRequired, diff --git a/client/storage-monitor/Cargo.toml b/client/storage-monitor/Cargo.toml new file mode 100644 index 000000000..2ba24f9e2 --- /dev/null +++ b/client/storage-monitor/Cargo.toml @@ -0,0 +1,20 @@ +[package] +name = "sc-storage-monitor" +version = "0.1.0" +authors = ["Parity Technologies "] +edition = "2021" +license = "GPL-3.0-or-later WITH Classpath-exception-2.0" +repository = "https://github.com/paritytech/substrate" +description = "Storage monitor service for substrate" +homepage = "https://substrate.io" + +[dependencies] +clap = { version = "4.0.9", features = ["derive", "string"] } +futures = "0.3.21" +log = "0.4.17" +nix = { version = "0.26.1", features = ["fs"] } +sc-client-db = { version = "0.10.0-dev", default-features = false, path = "../db" } +sc-utils = { version = "4.0.0-dev", path = "../utils" } +sp-core = { version = "7.0.0", path = "../../primitives/core" } +tokio = "1.22.0" +thiserror = "1.0.30" diff --git a/client/storage-monitor/src/lib.rs b/client/storage-monitor/src/lib.rs new file mode 100644 index 000000000..39bd15675 --- /dev/null +++ b/client/storage-monitor/src/lib.rs @@ -0,0 +1,149 @@ +// This file is part of Substrate. + +// Copyright (C) 2022 Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +use clap::Args; +use nix::{errno::Errno, sys::statvfs::statvfs}; +use sc_client_db::DatabaseSource; +use sp_core::traits::SpawnEssentialNamed; +use std::{ + path::{Path, PathBuf}, + time::Duration, +}; + +const LOG_TARGET: &str = "storage-monitor"; + +/// Error type used in this crate. +#[derive(Debug, thiserror::Error)] +pub enum Error { + #[error("IO Error")] + IOError(#[from] Errno), + #[error("Out of storage space: available {0}MB, required {1}MB")] + StorageOutOfSpace(u64, u64), +} + +/// Parameters used to create the storage monitor. +#[derive(Default, Debug, Clone, Args)] +pub struct StorageMonitorParams { + /// Required available space on database storage. If available space for DB storage drops below + /// the given threshold, node will be gracefully terminated. If `0` is given monitoring will be + /// disabled. + #[arg(long = "db-storage-threshold", value_name = "MB", default_value_t = 1000)] + pub threshold: u64, + + /// How often available space is polled. + #[arg(long = "db-storage-polling-period", value_name = "SECONDS", default_value_t = 5, value_parser = clap::value_parser!(u32).range(1..))] + pub polling_period: u32, +} + +/// Storage monitor service: checks the available space for the filesystem for fiven path. +pub struct StorageMonitorService { + /// watched path + path: PathBuf, + /// number of megabytes that shall be free on the filesystem for watched path + threshold: u64, + /// storage space polling period (seconds) + polling_period: u32, +} + +impl StorageMonitorService { + /// Creates new StorageMonitorService for given client config + pub fn try_spawn( + parameters: StorageMonitorParams, + database: DatabaseSource, + spawner: &impl SpawnEssentialNamed, + ) -> Result<(), Error> { + Ok(match (parameters.threshold, database.path()) { + (0, _) => { + log::info!( + target: LOG_TARGET, + "StorageMonitorService: threshold `0` given, storage monitoring disabled", + ); + }, + (_, None) => { + log::warn!( + target: LOG_TARGET, + "StorageMonitorService: no database path to observe", + ); + }, + (threshold, Some(path)) => { + log::debug!( + target: LOG_TARGET, + "Initializing StorageMonitorService for db path: {:?}", + path, + ); + + Self::check_free_space(&path, threshold)?; + + let storage_monitor_service = StorageMonitorService { + path: path.to_path_buf(), + threshold, + polling_period: parameters.polling_period, + }; + + spawner.spawn_essential( + "storage-monitor", + None, + Box::pin(storage_monitor_service.run()), + ); + }, + }) + } + + /// Main monitoring loop, intended to be spawned as essential task. Quits if free space drop + /// below threshold. + async fn run(self) { + loop { + tokio::time::sleep(Duration::from_secs(self.polling_period.into())).await; + if Self::check_free_space(&self.path, self.threshold).is_err() { + break + }; + } + } + + /// Returns free space in MB, or error if statvfs failed. + fn free_space(path: &Path) -> Result { + statvfs(path) + .map(|stats| stats.blocks_available() * stats.block_size() / 1_000_000) + .map_err(Error::from) + } + + /// Checks if the amount of free space for given `path` is above given `threshold`. + /// If it dropped below, error is returned. + /// System errors are silently ignored. + fn check_free_space(path: &Path, threshold: u64) -> Result<(), Error> { + match StorageMonitorService::free_space(path) { + Ok(available_space) => { + log::trace!( + target: LOG_TARGET, + "free: {available_space} , threshold: {threshold}.", + ); + + if available_space < threshold { + log::error!(target: LOG_TARGET, "Available space {available_space}MB for path `{}` dropped below threshold: {threshold}MB , terminating...", path.display()); + Err(Error::StorageOutOfSpace(available_space, threshold)) + } else { + Ok(()) + } + }, + Err(e) => { + log::error!(target: LOG_TARGET, "Could not read available space: {:?}.", e); + Err(e) + }, + } + } +} From 84af67204b422fc2b7d05db55cb75eb2d941246b Mon Sep 17 00:00:00 2001 From: Alejandro Martinez Andres <11448715+al3mart@users.noreply.github.com> Date: Tue, 24 Jan 2023 15:55:42 +0100 Subject: [PATCH 045/558] beefy: Add LOG_TARGET constant (#13222) * LOG_TARGET const * fmt --- client/beefy/src/aux_schema.rs | 6 +- client/beefy/src/communication/gossip.rs | 15 ++- .../incoming_requests_handler.rs | 8 +- .../src/communication/request_response/mod.rs | 2 + .../outgoing_requests_engine.rs | 41 +++--- client/beefy/src/import.rs | 11 +- client/beefy/src/keystore.rs | 9 +- client/beefy/src/lib.rs | 33 +++-- client/beefy/src/round.rs | 10 +- client/beefy/src/worker.rs | 120 ++++++++++-------- 10 files changed, 153 insertions(+), 102 deletions(-) diff --git a/client/beefy/src/aux_schema.rs b/client/beefy/src/aux_schema.rs index 67deee19b..fafa9948c 100644 --- a/client/beefy/src/aux_schema.rs +++ b/client/beefy/src/aux_schema.rs @@ -18,7 +18,7 @@ //! Schema for BEEFY state persisted in the aux-db. -use crate::worker::PersistedState; +use crate::{worker::PersistedState, LOG_TARGET}; use codec::{Decode, Encode}; use log::{info, trace}; use sc_client_api::{backend::AuxStore, Backend}; @@ -31,7 +31,7 @@ const WORKER_STATE: &[u8] = b"beefy_voter_state"; const CURRENT_VERSION: u32 = 1; pub(crate) fn write_current_version(backend: &B) -> ClientResult<()> { - info!(target: "beefy", "🥩 write aux schema version {:?}", CURRENT_VERSION); + info!(target: LOG_TARGET, "🥩 write aux schema version {:?}", CURRENT_VERSION); AuxStore::insert_aux(backend, &[(VERSION_KEY, CURRENT_VERSION.encode().as_slice())], &[]) } @@ -40,7 +40,7 @@ pub(crate) fn write_voter_state( backend: &B, state: &PersistedState, ) -> ClientResult<()> { - trace!(target: "beefy", "🥩 persisting {:?}", state); + trace!(target: LOG_TARGET, "🥩 persisting {:?}", state); backend.insert_aux(&[(WORKER_STATE, state.encode().as_slice())], &[]) } diff --git a/client/beefy/src/communication/gossip.rs b/client/beefy/src/communication/gossip.rs index bbc35ac8e..116a6286e 100644 --- a/client/beefy/src/communication/gossip.rs +++ b/client/beefy/src/communication/gossip.rs @@ -28,7 +28,7 @@ use log::{debug, trace}; use parking_lot::{Mutex, RwLock}; use wasm_timer::Instant; -use crate::{communication::peers::KnownPeers, keystore::BeefyKeystore}; +use crate::{communication::peers::KnownPeers, keystore::BeefyKeystore, LOG_TARGET}; use beefy_primitives::{ crypto::{Public, Signature}, VoteMessage, @@ -122,7 +122,7 @@ where /// /// Noting round will start a live `round`. pub(crate) fn note_round(&self, round: NumberFor) { - debug!(target: "beefy", "🥩 About to note gossip round #{}", round); + debug!(target: LOG_TARGET, "🥩 About to note gossip round #{}", round); self.known_votes.write().insert(round); } @@ -130,7 +130,7 @@ where /// /// This can be called once round is complete so we stop gossiping for it. pub(crate) fn conclude_round(&self, round: NumberFor) { - debug!(target: "beefy", "🥩 About to drop gossip round #{}", round); + debug!(target: LOG_TARGET, "🥩 About to drop gossip round #{}", round); self.known_votes.write().conclude(round); } } @@ -174,7 +174,10 @@ where return ValidationResult::ProcessAndKeep(self.topic) } else { // TODO: report peer - debug!(target: "beefy", "🥩 Bad signature on message: {:?}, from: {:?}", msg, sender); + debug!( + target: LOG_TARGET, + "🥩 Bad signature on message: {:?}, from: {:?}", msg, sender + ); } } @@ -192,7 +195,7 @@ where let round = msg.commitment.block_number; let expired = !known_votes.is_live(&round); - trace!(target: "beefy", "🥩 Message for round #{} expired: {}", round, expired); + trace!(target: LOG_TARGET, "🥩 Message for round #{} expired: {}", round, expired); expired }) @@ -226,7 +229,7 @@ where let round = msg.commitment.block_number; let allowed = known_votes.is_live(&round); - trace!(target: "beefy", "🥩 Message for round #{} allowed: {}", round, allowed); + trace!(target: LOG_TARGET, "🥩 Message for round #{} allowed: {}", round, allowed); allowed }) diff --git a/client/beefy/src/communication/request_response/incoming_requests_handler.rs b/client/beefy/src/communication/request_response/incoming_requests_handler.rs index 9f02b7162..d3be73dc0 100644 --- a/client/beefy/src/communication/request_response/incoming_requests_handler.rs +++ b/client/beefy/src/communication/request_response/incoming_requests_handler.rs @@ -30,7 +30,7 @@ use sp_runtime::traits::Block; use std::{marker::PhantomData, sync::Arc}; use crate::communication::request_response::{ - on_demand_justifications_protocol_config, Error, JustificationRequest, + on_demand_justifications_protocol_config, Error, JustificationRequest, BEEFY_SYNC_LOG_TARGET, }; /// A request coming in, including a sender for sending responses. @@ -174,21 +174,21 @@ where /// Run [`BeefyJustifsRequestHandler`]. pub async fn run(mut self) { - trace!(target: "beefy::sync", "🥩 Running BeefyJustifsRequestHandler"); + trace!(target: BEEFY_SYNC_LOG_TARGET, "🥩 Running BeefyJustifsRequestHandler"); while let Ok(request) = self.request_receiver.recv(|| vec![]).await { let peer = request.peer; match self.handle_request(request) { Ok(()) => { debug!( - target: "beefy::sync", + target: BEEFY_SYNC_LOG_TARGET, "🥩 Handled BEEFY justification request from {:?}.", peer ) }, Err(e) => { // TODO (issue #12293): apply reputation changes here based on error type. debug!( - target: "beefy::sync", + target: BEEFY_SYNC_LOG_TARGET, "🥩 Failed to handle BEEFY justification request from {:?}: {}", peer, e, ) }, diff --git a/client/beefy/src/communication/request_response/mod.rs b/client/beefy/src/communication/request_response/mod.rs index c83bb9d57..22a20c247 100644 --- a/client/beefy/src/communication/request_response/mod.rs +++ b/client/beefy/src/communication/request_response/mod.rs @@ -40,6 +40,8 @@ const JUSTIF_CHANNEL_SIZE: usize = 10; const MAX_RESPONSE_SIZE: u64 = 1024 * 1024; const JUSTIF_REQUEST_TIMEOUT: Duration = Duration::from_secs(3); +const BEEFY_SYNC_LOG_TARGET: &str = "beefy::sync"; + /// Get the configuration for the BEEFY justifications Request/response protocol. /// /// Returns a receiver for messages received on this protocol and the requested diff --git a/client/beefy/src/communication/request_response/outgoing_requests_engine.rs b/client/beefy/src/communication/request_response/outgoing_requests_engine.rs index 00ee7610d..c7dc269b4 100644 --- a/client/beefy/src/communication/request_response/outgoing_requests_engine.rs +++ b/client/beefy/src/communication/request_response/outgoing_requests_engine.rs @@ -32,7 +32,7 @@ use sp_runtime::traits::{Block, NumberFor}; use std::{collections::VecDeque, result::Result, sync::Arc}; use crate::{ - communication::request_response::{Error, JustificationRequest}, + communication::request_response::{Error, JustificationRequest, BEEFY_SYNC_LOG_TARGET}, justification::{decode_and_verify_finality_proof, BeefyVersionedFinalityProof}, KnownPeers, }; @@ -96,10 +96,8 @@ impl OnDemandJustificationsEngine { fn request_from_peer(&mut self, peer: PeerId, req_info: RequestInfo) { debug!( - target: "beefy::sync", - "🥩 requesting justif #{:?} from peer {:?}", - req_info.block, - peer, + target: BEEFY_SYNC_LOG_TARGET, + "🥩 requesting justif #{:?} from peer {:?}", req_info.block, peer, ); let payload = JustificationRequest:: { begin: req_info.block }.encode(); @@ -132,7 +130,10 @@ impl OnDemandJustificationsEngine { if let Some(peer) = self.try_next_peer() { self.request_from_peer(peer, RequestInfo { block, active_set }); } else { - debug!(target: "beefy::sync", "🥩 no good peers to request justif #{:?} from", block); + debug!( + target: BEEFY_SYNC_LOG_TARGET, + "🥩 no good peers to request justif #{:?} from", block + ); } } @@ -141,8 +142,8 @@ impl OnDemandJustificationsEngine { match &self.state { State::AwaitingResponse(_, req_info, _) if req_info.block <= block => { debug!( - target: "beefy::sync", "🥩 cancel pending request for justification #{:?}", - req_info.block + target: BEEFY_SYNC_LOG_TARGET, + "🥩 cancel pending request for justification #{:?}", req_info.block ); self.state = State::Idle; }, @@ -159,17 +160,21 @@ impl OnDemandJustificationsEngine { response .map_err(|e| { debug!( - target: "beefy::sync", + target: BEEFY_SYNC_LOG_TARGET, "🥩 for on demand justification #{:?}, peer {:?} hung up: {:?}", - req_info.block, peer, e + req_info.block, + peer, + e ); Error::InvalidResponse })? .map_err(|e| { debug!( - target: "beefy::sync", + target: BEEFY_SYNC_LOG_TARGET, "🥩 for on demand justification #{:?}, peer {:?} error: {:?}", - req_info.block, peer, e + req_info.block, + peer, + e ); Error::InvalidResponse }) @@ -181,7 +186,7 @@ impl OnDemandJustificationsEngine { ) .map_err(|e| { debug!( - target: "beefy::sync", + target: BEEFY_SYNC_LOG_TARGET, "🥩 for on demand justification #{:?}, peer {:?} responded with invalid proof: {:?}", req_info.block, peer, e ); @@ -213,14 +218,16 @@ impl OnDemandJustificationsEngine { if let Some(peer) = self.try_next_peer() { self.request_from_peer(peer, req_info); } else { - warn!(target: "beefy::sync", "🥩 ran out of peers to request justif #{:?} from", block); + warn!( + target: BEEFY_SYNC_LOG_TARGET, + "🥩 ran out of peers to request justif #{:?} from", block + ); } }) .map(|proof| { debug!( - target: "beefy::sync", - "🥩 received valid on-demand justif #{:?} from {:?}", - block, peer + target: BEEFY_SYNC_LOG_TARGET, + "🥩 received valid on-demand justif #{:?} from {:?}", block, peer ); proof }) diff --git a/client/beefy/src/import.rs b/client/beefy/src/import.rs index 0ed50d0ec..f0fe3bfa0 100644 --- a/client/beefy/src/import.rs +++ b/client/beefy/src/import.rs @@ -35,6 +35,7 @@ use sc_consensus::{BlockCheckParams, BlockImport, BlockImportParams, ImportResul use crate::{ communication::notification::BeefyVersionedFinalityProofSender, justification::{decode_and_verify_finality_proof, BeefyVersionedFinalityProof}, + LOG_TARGET, }; /// A block-import handler for BEEFY. @@ -138,16 +139,20 @@ where (Some(encoded), ImportResult::Imported(_)) => { if let Ok(proof) = self.decode_and_verify(&encoded, number, hash) { // The proof is valid and the block is imported and final, we can import. - debug!(target: "beefy", "🥩 import justif {:?} for block number {:?}.", proof, number); + debug!( + target: LOG_TARGET, + "🥩 import justif {:?} for block number {:?}.", proof, number + ); // Send the justification to the BEEFY voter for processing. self.justification_sender .notify(|| Ok::<_, ()>(proof)) .expect("forwards closure result; the closure always returns Ok; qed."); } else { debug!( - target: "beefy", + target: LOG_TARGET, "🥩 error decoding justification: {:?} for imported block {:?}", - encoded, number, + encoded, + number, ); } }, diff --git a/client/beefy/src/keystore.rs b/client/beefy/src/keystore.rs index 8d7741074..d1f5615a9 100644 --- a/client/beefy/src/keystore.rs +++ b/client/beefy/src/keystore.rs @@ -28,7 +28,7 @@ use beefy_primitives::{ BeefyAuthorityId, KEY_TYPE, }; -use crate::error; +use crate::{error, LOG_TARGET}; /// A BEEFY specific keystore implemented as a `Newtype`. This is basically a /// wrapper around [`sp_keystore::SyncCryptoStore`] and allows to customize @@ -53,7 +53,12 @@ impl BeefyKeystore { .collect(); if public.len() > 1 { - warn!(target: "beefy", "🥩 Multiple private keys found for: {:?} ({})", public, public.len()); + warn!( + target: LOG_TARGET, + "🥩 Multiple private keys found for: {:?} ({})", + public, + public.len() + ); } public.get(0).cloned() diff --git a/client/beefy/src/lib.rs b/client/beefy/src/lib.rs index 5b6531822..185f3b1ad 100644 --- a/client/beefy/src/lib.rs +++ b/client/beefy/src/lib.rs @@ -75,6 +75,8 @@ pub use communication::beefy_protocol_name::{ #[cfg(test)] mod tests; +const LOG_TARGET: &str = "beefy"; + /// A convenience BEEFY client trait that defines all the type bounds a BEEFY client /// has to satisfy. Ideally that should actually be a trait alias. Unfortunately as /// of today, Rust does not allow a type alias to be used as a trait bound. Tracking @@ -252,11 +254,11 @@ where prometheus_registry.as_ref().map(metrics::Metrics::register).and_then( |result| match result { Ok(metrics) => { - debug!(target: "beefy", "🥩 Registered metrics"); + debug!(target: LOG_TARGET, "🥩 Registered metrics"); Some(metrics) }, Err(err) => { - debug!(target: "beefy", "🥩 Failed to register metrics: {:?}", err); + debug!(target: LOG_TARGET, "🥩 Failed to register metrics: {:?}", err); None }, }, @@ -276,7 +278,7 @@ where }) { Ok(state) => state, Err(e) => { - error!(target: "beefy", "Error: {:?}. Terminating.", e); + error!(target: LOG_TARGET, "Error: {:?}. Terminating.", e); return }, }; @@ -321,7 +323,7 @@ where state.set_best_grandpa(best_grandpa); // Overwrite persisted data with newly provided `min_block_delta`. state.set_min_block_delta(min_block_delta); - info!(target: "beefy", "🥩 Loading BEEFY voter state from db: {:?}.", state); + info!(target: LOG_TARGET, "🥩 Loading BEEFY voter state from db: {:?}.", state); Ok(state) } else { initialize_voter_state(backend, runtime, best_grandpa, min_block_delta) @@ -357,7 +359,7 @@ where .map(|justifs| justifs.get(BEEFY_ENGINE_ID).is_some()) { info!( - target: "beefy", + target: LOG_TARGET, "🥩 Initialize BEEFY voter at last BEEFY finalized block: {:?}.", *header.number() ); @@ -382,10 +384,11 @@ where let genesis_set = expect_validator_set(runtime, BlockId::hash(header.hash())) .and_then(genesis_set_sanity_check)?; info!( - target: "beefy", + target: LOG_TARGET, "🥩 Loading BEEFY voter state from genesis on what appears to be first startup. \ Starting voting rounds at block {:?}, genesis validator set {:?}.", - genesis_num, genesis_set, + genesis_num, + genesis_set, ); sessions.push_front(Rounds::new(genesis_num, genesis_set)); @@ -394,7 +397,11 @@ where } if let Some(active) = worker::find_authorities_change::(&header) { - info!(target: "beefy", "🥩 Marking block {:?} as BEEFY Mandatory.", *header.number()); + info!( + target: LOG_TARGET, + "🥩 Marking block {:?} as BEEFY Mandatory.", + *header.number() + ); sessions.push_front(Rounds::new(*header.number(), active)); } @@ -407,7 +414,7 @@ where .flatten() .ok_or_else(|| { let msg = format!("{}. Could not initialize BEEFY voter.", parent_hash); - error!(target: "beefy", "🥩 {}", msg); + error!(target: LOG_TARGET, "🥩 {}", msg); ClientError::Consensus(sp_consensus::Error::StateUnavailable(msg)) })?; @@ -432,7 +439,7 @@ where R: ProvideRuntimeApi, R::Api: BeefyApi, { - info!(target: "beefy", "🥩 BEEFY gadget waiting for BEEFY pallet to become available..."); + info!(target: LOG_TARGET, "🥩 BEEFY gadget waiting for BEEFY pallet to become available..."); loop { futures::select! { notif = finality.next() => { @@ -444,7 +451,7 @@ where if let Some(active) = runtime.runtime_api().validator_set(&at).ok().flatten() { // Beefy pallet available, return best grandpa at the time. info!( - target: "beefy", "🥩 BEEFY pallet available: block {:?} validator set {:?}", + target: LOG_TARGET, "🥩 BEEFY pallet available: block {:?} validator set {:?}", notif.header.number(), active ); return Ok(notif.header) @@ -456,7 +463,7 @@ where } } let err_msg = "🥩 Gossip engine has unexpectedly terminated.".into(); - error!(target: "beefy", "{}", err_msg); + error!(target: LOG_TARGET, "{}", err_msg); Err(ClientError::Backend(err_msg)) } @@ -466,7 +473,7 @@ fn genesis_set_sanity_check( if active.id() == GENESIS_AUTHORITY_SET_ID { Ok(active) } else { - error!(target: "beefy", "🥩 Unexpected ID for genesis validator set {:?}.", active); + error!(target: LOG_TARGET, "🥩 Unexpected ID for genesis validator set {:?}.", active); Err(ClientError::Backend("BEEFY Genesis sanity check failed.".into())) } } diff --git a/client/beefy/src/round.rs b/client/beefy/src/round.rs index 48d3d0872..647d42110 100644 --- a/client/beefy/src/round.rs +++ b/client/beefy/src/round.rs @@ -16,6 +16,8 @@ // You should have received a copy of the GNU General Public License // along with this program. If not, see . +use crate::LOG_TARGET; + use beefy_primitives::{ crypto::{Public, Signature}, ValidatorSet, ValidatorSetId, @@ -122,11 +124,11 @@ where ) -> bool { let num = round.1; if num < self.session_start || Some(num) <= self.best_done { - debug!(target: "beefy", "🥩 received vote for old stale round {:?}, ignoring", num); + debug!(target: LOG_TARGET, "🥩 received vote for old stale round {:?}, ignoring", num); false } else if !self.validators().iter().any(|id| vote.0 == *id) { debug!( - target: "beefy", + target: LOG_TARGET, "🥩 received vote {:?} from validator that is not in the validator set, ignoring", vote ); @@ -145,7 +147,7 @@ where .get(round) .map(|tracker| tracker.is_done(threshold(self.validator_set.len()))) .unwrap_or(false); - trace!(target: "beefy", "🥩 Round #{} done: {}", round.1, done); + trace!(target: LOG_TARGET, "🥩 Round #{} done: {}", round.1, done); if done { let signatures = self.rounds.remove(round)?.votes; @@ -165,7 +167,7 @@ where self.rounds.retain(|&(_, number), _| number > round_num); self.mandatory_done = self.mandatory_done || round_num == self.session_start; self.best_done = self.best_done.max(Some(round_num)); - debug!(target: "beefy", "🥩 Concluded round #{}", round_num); + debug!(target: LOG_TARGET, "🥩 Concluded round #{}", round_num); } } diff --git a/client/beefy/src/worker.rs b/client/beefy/src/worker.rs index 8ec1403bb..19ab52f52 100644 --- a/client/beefy/src/worker.rs +++ b/client/beefy/src/worker.rs @@ -27,7 +27,7 @@ use crate::{ metric_inc, metric_set, metrics::Metrics, round::Rounds, - BeefyVoterLinks, + BeefyVoterLinks, LOG_TARGET, }; use beefy_primitives::{ crypto::{AuthorityId, Signature}, @@ -129,7 +129,7 @@ impl VoterOracle { min_block_delta: min_block_delta.max(1), }) } else { - error!(target: "beefy", "🥩 Invalid sessions queue: {:?}.", sessions); + error!(target: LOG_TARGET, "🥩 Invalid sessions queue: {:?}.", sessions); None } } @@ -227,7 +227,7 @@ impl VoterOracle { let rounds = if let Some(r) = self.sessions.front() { r } else { - debug!(target: "beefy", "🥩 No voting round started"); + debug!(target: LOG_TARGET, "🥩 No voting round started"); return None }; @@ -235,7 +235,7 @@ impl VoterOracle { let target = vote_target(best_grandpa, best_beefy, rounds.session_start(), self.min_block_delta); trace!( - target: "beefy", + target: LOG_TARGET, "🥩 best beefy: #{:?}, best finalized: #{:?}, current_vote_target: {:?}", best_beefy, best_grandpa, @@ -405,7 +405,7 @@ where if store.intersection(&active).count() == 0 { let msg = "no authority public key found in store".to_string(); - debug!(target: "beefy", "🥩 for block {:?} {}", block, msg); + debug!(target: LOG_TARGET, "🥩 for block {:?} {}", block, msg); Err(Error::Keystore(msg)) } else { Ok(()) @@ -418,13 +418,14 @@ where validator_set: ValidatorSet, new_session_start: NumberFor, ) { - debug!(target: "beefy", "🥩 New active validator set: {:?}", validator_set); + debug!(target: LOG_TARGET, "🥩 New active validator set: {:?}", validator_set); // BEEFY should finalize a mandatory block during each session. if let Some(active_session) = self.active_rounds() { if !active_session.mandatory_done() { debug!( - target: "beefy", "🥩 New session {} while active session {} is still lagging.", + target: LOG_TARGET, + "🥩 New session {} while active session {} is still lagging.", validator_set.id(), active_session.validator_set_id(), ); @@ -432,7 +433,7 @@ where } } - if log_enabled!(target: "beefy", log::Level::Debug) { + if log_enabled!(target: LOG_TARGET, log::Level::Debug) { // verify the new validator set - only do it if we're also logging the warning let _ = self.verify_validator_set(&new_session_start, &validator_set); } @@ -443,14 +444,15 @@ where .add_session(Rounds::new(new_session_start, validator_set)); metric_set!(self, beefy_validator_set_id, id); info!( - target: "beefy", + target: LOG_TARGET, "🥩 New Rounds for validator set id: {:?} with session_start {:?}", - id, new_session_start + id, + new_session_start ); } fn handle_finality_notification(&mut self, notification: &FinalityNotification) { - debug!(target: "beefy", "🥩 Finality notification: {:?}", notification); + debug!(target: LOG_TARGET, "🥩 Finality notification: {:?}", notification); let header = ¬ification.header; if *header.number() > self.best_grandpa_block() { @@ -491,14 +493,17 @@ where false, )?, RoundAction::Enqueue => { - debug!(target: "beefy", "🥩 Buffer vote for round: {:?}.", block_num); + debug!(target: LOG_TARGET, "🥩 Buffer vote for round: {:?}.", block_num); if self.pending_votes.len() < MAX_BUFFERED_VOTE_ROUNDS { let votes_vec = self.pending_votes.entry(block_num).or_default(); if votes_vec.try_push(vote).is_err() { - warn!(target: "beefy", "🥩 Buffer vote dropped for round: {:?}", block_num) + warn!( + target: LOG_TARGET, + "🥩 Buffer vote dropped for round: {:?}", block_num + ) } } else { - warn!(target: "beefy", "🥩 Buffer vote dropped for round: {:?}.", block_num); + warn!(target: LOG_TARGET, "🥩 Buffer vote dropped for round: {:?}.", block_num); } }, RoundAction::Drop => (), @@ -520,15 +525,18 @@ where let best_grandpa = self.best_grandpa_block(); match self.voting_oracle().triage_round(block_num, best_grandpa)? { RoundAction::Process => { - debug!(target: "beefy", "🥩 Process justification for round: {:?}.", block_num); + debug!(target: LOG_TARGET, "🥩 Process justification for round: {:?}.", block_num); self.finalize(justification)? }, RoundAction::Enqueue => { - debug!(target: "beefy", "🥩 Buffer justification for round: {:?}.", block_num); + debug!(target: LOG_TARGET, "🥩 Buffer justification for round: {:?}.", block_num); if self.pending_justifications.len() < MAX_BUFFERED_JUSTIFICATIONS { self.pending_justifications.entry(block_num).or_insert(justification); } else { - warn!(target: "beefy", "🥩 Buffer justification dropped for round: {:?}.", block_num); + warn!( + target: LOG_TARGET, + "🥩 Buffer justification dropped for round: {:?}.", block_num + ); } }, RoundAction::Drop => (), @@ -566,7 +574,10 @@ where metric_set!(self, beefy_round_concluded, block_num); - info!(target: "beefy", "🥩 Round #{} concluded, finality_proof: {:?}.", round.1, finality_proof); + info!( + target: LOG_TARGET, + "🥩 Round #{} concluded, finality_proof: {:?}.", round.1, finality_proof + ); // We created the `finality_proof` and know to be valid. // New state is persisted after finalization. @@ -627,7 +638,10 @@ where self.backend .append_justification(hash, (BEEFY_ENGINE_ID, finality_proof.encode())) }) { - error!(target: "beefy", "🥩 Error {:?} on appending justification: {:?}", e, finality_proof); + error!( + target: LOG_TARGET, + "🥩 Error {:?} on appending justification: {:?}", e, finality_proof + ); } self.links @@ -635,7 +649,7 @@ where .notify(|| Ok::<_, ()>(finality_proof)) .expect("forwards closure result; the closure always returns Ok; qed."); } else { - debug!(target: "beefy", "🥩 Can't set best beefy to older: {}", block_num); + debug!(target: LOG_TARGET, "🥩 Can't set best beefy to older: {}", block_num); } Ok(()) } @@ -666,9 +680,9 @@ where if !self.pending_justifications.is_empty() { let justifs_to_handle = to_process_for(&mut self.pending_justifications, interval, _ph); for (num, justification) in justifs_to_handle.into_iter() { - debug!(target: "beefy", "🥩 Handle buffered justification for: {:?}.", num); + debug!(target: LOG_TARGET, "🥩 Handle buffered justification for: {:?}.", num); if let Err(err) = self.finalize(justification) { - error!(target: "beefy", "🥩 Error finalizing block: {}", err); + error!(target: LOG_TARGET, "🥩 Error finalizing block: {}", err); } } // Possibly new interval after processing justifications. @@ -679,14 +693,14 @@ where if !self.pending_votes.is_empty() { let votes_to_handle = to_process_for(&mut self.pending_votes, interval, _ph); for (num, votes) in votes_to_handle.into_iter() { - debug!(target: "beefy", "🥩 Handle buffered votes for: {:?}.", num); + debug!(target: LOG_TARGET, "🥩 Handle buffered votes for: {:?}.", num); for v in votes.into_iter() { if let Err(err) = self.handle_vote( (v.commitment.payload, v.commitment.block_number), (v.id, v.signature), false, ) { - error!(target: "beefy", "🥩 Error handling buffered vote: {}", err); + error!(target: LOG_TARGET, "🥩 Error handling buffered vote: {}", err); }; } } @@ -711,7 +725,7 @@ where /// /// Also handle this self vote by calling `self.handle_vote()` for it. fn do_vote(&mut self, target_number: NumberFor) -> Result<(), Error> { - debug!(target: "beefy", "🥩 Try voting on {}", target_number); + debug!(target: LOG_TARGET, "🥩 Try voting on {}", target_number); // Most of the time we get here, `target` is actually `best_grandpa`, // avoid getting header from backend in that case. @@ -743,7 +757,7 @@ where let payload = if let Some(hash) = self.payload_provider.payload(&target_header) { hash } else { - warn!(target: "beefy", "🥩 No MMR root digest found for: {:?}", target_hash); + warn!(target: LOG_TARGET, "🥩 No MMR root digest found for: {:?}", target_hash); return Ok(()) }; @@ -753,16 +767,22 @@ where .active_rounds_mut() .ok_or(Error::UninitSession)?; if !rounds.should_self_vote(&(payload.clone(), target_number)) { - debug!(target: "beefy", "🥩 Don't double vote for block number: {:?}", target_number); + debug!( + target: LOG_TARGET, + "🥩 Don't double vote for block number: {:?}", target_number + ); return Ok(()) } let (validators, validator_set_id) = (rounds.validators(), rounds.validator_set_id()); let authority_id = if let Some(id) = self.key_store.authority_id(validators) { - debug!(target: "beefy", "🥩 Local authority id: {:?}", id); + debug!(target: LOG_TARGET, "🥩 Local authority id: {:?}", id); id } else { - debug!(target: "beefy", "🥩 Missing validator id - can't vote for: {:?}", target_hash); + debug!( + target: LOG_TARGET, + "🥩 Missing validator id - can't vote for: {:?}", target_hash + ); return Ok(()) }; @@ -772,13 +792,13 @@ where let signature = match self.key_store.sign(&authority_id, &encoded_commitment) { Ok(sig) => sig, Err(err) => { - warn!(target: "beefy", "🥩 Error signing commitment: {:?}", err); + warn!(target: LOG_TARGET, "🥩 Error signing commitment: {:?}", err); return Ok(()) }, }; trace!( - target: "beefy", + target: LOG_TARGET, "🥩 Produced signature using {:?}, is_valid: {:?}", authority_id, BeefyKeystore::verify(&authority_id, &signature, &encoded_commitment) @@ -790,14 +810,14 @@ where metric_inc!(self, beefy_votes_sent); - debug!(target: "beefy", "🥩 Sent vote message: {:?}", message); + debug!(target: LOG_TARGET, "🥩 Sent vote message: {:?}", message); if let Err(err) = self.handle_vote( (message.commitment.payload, message.commitment.block_number), (message.id, message.signature), true, ) { - error!(target: "beefy", "🥩 Error handling self vote: {}", err); + error!(target: LOG_TARGET, "🥩 Error handling self vote: {}", err); } self.gossip_engine.gossip_message(topic::(), encoded_message, false); @@ -808,14 +828,14 @@ where fn process_new_state(&mut self) { // Handle pending justifications and/or votes for now GRANDPA finalized blocks. if let Err(err) = self.try_pending_justif_and_votes() { - debug!(target: "beefy", "🥩 {}", err); + debug!(target: LOG_TARGET, "🥩 {}", err); } // Don't bother voting or requesting justifications during major sync. if !self.network.is_major_syncing() { // There were external events, 'state' is changed, author a vote if needed/possible. if let Err(err) = self.try_to_vote() { - debug!(target: "beefy", "🥩 {}", err); + debug!(target: LOG_TARGET, "🥩 {}", err); } // If the current target is a mandatory block, // make sure there's also an on-demand justification request out for it. @@ -835,13 +855,17 @@ where mut block_import_justif: Fuse>>, mut finality_notifications: Fuse>, ) { - info!(target: "beefy", "🥩 run BEEFY worker, best grandpa: #{:?}.", self.best_grandpa_block()); + info!( + target: LOG_TARGET, + "🥩 run BEEFY worker, best grandpa: #{:?}.", + self.best_grandpa_block() + ); let mut votes = Box::pin( self.gossip_engine .messages_for(topic::()) .filter_map(|notification| async move { - trace!(target: "beefy", "🥩 Got vote message: {:?}", notification); + trace!(target: LOG_TARGET, "🥩 Got vote message: {:?}", notification); VoteMessage::, AuthorityId, Signature>::decode( &mut ¬ification.message[..], @@ -863,7 +887,7 @@ where // Use `select_biased!` to prioritize order below. // Make sure to pump gossip engine. _ = gossip_engine => { - error!(target: "beefy", "🥩 Gossip engine has terminated, closing worker."); + error!(target: LOG_TARGET, "🥩 Gossip engine has terminated, closing worker."); return; }, // Process finality notifications first since these drive the voter. @@ -871,7 +895,7 @@ where if let Some(notification) = notification { self.handle_finality_notification(¬ification); } else { - error!(target: "beefy", "🥩 Finality stream terminated, closing worker."); + error!(target: LOG_TARGET, "🥩 Finality stream terminated, closing worker."); return; } }, @@ -879,7 +903,7 @@ where justif = self.on_demand_justifications.next().fuse() => { if let Some(justif) = justif { if let Err(err) = self.triage_incoming_justif(justif) { - debug!(target: "beefy", "🥩 {}", err); + debug!(target: LOG_TARGET, "🥩 {}", err); } } }, @@ -888,10 +912,10 @@ where // Block import justifications have already been verified to be valid // by `BeefyBlockImport`. if let Err(err) = self.triage_incoming_justif(justif) { - debug!(target: "beefy", "🥩 {}", err); + debug!(target: LOG_TARGET, "🥩 {}", err); } } else { - error!(target: "beefy", "🥩 Block import stream terminated, closing worker."); + error!(target: LOG_TARGET, "🥩 Block import stream terminated, closing worker."); return; } }, @@ -900,10 +924,10 @@ where if let Some(vote) = vote { // Votes have already been verified to be valid by the gossip validator. if let Err(err) = self.triage_incoming_vote(vote) { - debug!(target: "beefy", "🥩 {}", err); + debug!(target: LOG_TARGET, "🥩 {}", err); } } else { - error!(target: "beefy", "🥩 Votes gossiping stream terminated, closing worker."); + error!(target: LOG_TARGET, "🥩 Votes gossiping stream terminated, closing worker."); return; } }, @@ -937,18 +961,14 @@ where // if the mandatory block (session_start) does not have a beefy justification yet, // we vote on it let target = if best_beefy < session_start { - debug!( - target: "beefy", - "🥩 vote target - mandatory block: #{:?}", - session_start, - ); + debug!(target: LOG_TARGET, "🥩 vote target - mandatory block: #{:?}", session_start,); session_start } else { let diff = best_grandpa.saturating_sub(best_beefy) + 1u32.into(); let diff = diff.saturated_into::() / 2; let target = best_beefy + min_delta.max(diff.next_power_of_two()).into(); trace!( - target: "beefy", + target: LOG_TARGET, "🥩 vote target - diff: {:?}, next_power_of_two: {:?}, target block: #{:?}", diff, diff.next_power_of_two(), From 09de7b41599add51cf27eca8f1bc4c50ed8e9453 Mon Sep 17 00:00:00 2001 From: Brian Anderson Date: Tue, 24 Jan 2023 12:21:52 -0600 Subject: [PATCH 046/558] Upgrade wasm-opt to 0.111.0 (#13038) --- Cargo.lock | 12 ++++++------ utils/wasm-builder/Cargo.toml | 2 +- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 214c4f72c..5d34dd81e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -11804,9 +11804,9 @@ dependencies = [ [[package]] name = "wasm-opt" -version = "0.110.2" +version = "0.111.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b68e8037b4daf711393f4be2056246d12d975651b14d581520ad5d1f19219cec" +checksum = "84a303793cbc01fb96551badfc7367db6007396bba6bac97936b3c8b6f7fdb41" dependencies = [ "anyhow", "libc", @@ -11820,9 +11820,9 @@ dependencies = [ [[package]] name = "wasm-opt-cxx-sys" -version = "0.110.2" +version = "0.111.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "91adbad477e97bba3fbd21dd7bfb594e7ad5ceb9169ab1c93ab9cb0ada636b6f" +checksum = "d9c9deb56f8a9f2ec177b3bd642a8205621835944ed5da55f2388ef216aca5a4" dependencies = [ "anyhow", "cxx", @@ -11832,9 +11832,9 @@ dependencies = [ [[package]] name = "wasm-opt-sys" -version = "0.110.2" +version = "0.111.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ec4fa5a322a4e6ac22fd141f498d56afbdbf9df5debeac32380d2dcaa3e06941" +checksum = "4432e28b542738a9776cedf92e8a99d8991c7b4667ee2c7ccddfb479dd2856a7" dependencies = [ "anyhow", "cc", diff --git a/utils/wasm-builder/Cargo.toml b/utils/wasm-builder/Cargo.toml index 46c592996..84693f5ee 100644 --- a/utils/wasm-builder/Cargo.toml +++ b/utils/wasm-builder/Cargo.toml @@ -22,4 +22,4 @@ toml = "0.5.4" walkdir = "2.3.2" sp-maybe-compressed-blob = { version = "4.1.0-dev", path = "../../primitives/maybe-compressed-blob" } filetime = "0.2.16" -wasm-opt = "0.110" \ No newline at end of file +wasm-opt = "0.111" \ No newline at end of file From 5749760b7d95bd82599be2b1a23e129dcfa5be28 Mon Sep 17 00:00:00 2001 From: Michal Kucharczyk <1728078+michalkucharczyk@users.noreply.github.com> Date: Tue, 24 Jan 2023 20:22:47 +0100 Subject: [PATCH 047/558] storage-monitor: statvfs arithmetic bug fixed (#13234) --- client/storage-monitor/src/lib.rs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/client/storage-monitor/src/lib.rs b/client/storage-monitor/src/lib.rs index 39bd15675..162fcfe34 100644 --- a/client/storage-monitor/src/lib.rs +++ b/client/storage-monitor/src/lib.rs @@ -118,7 +118,10 @@ impl StorageMonitorService { /// Returns free space in MB, or error if statvfs failed. fn free_space(path: &Path) -> Result { statvfs(path) - .map(|stats| stats.blocks_available() * stats.block_size() / 1_000_000) + .map(|stats| { + u64::from(stats.blocks_available()).saturating_mul(u64::from(stats.block_size())) / + 1_000_000 + }) .map_err(Error::from) } From d2afb5d97bc0ddad72b02b1e2e6ea8c56835bb6a Mon Sep 17 00:00:00 2001 From: Arkadiy Paronyan Date: Tue, 24 Jan 2023 23:10:21 +0100 Subject: [PATCH 048/558] Bump parity-db (#13226) --- Cargo.lock | 4 ++-- bin/node/bench/Cargo.toml | 2 +- client/db/Cargo.toml | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 5d34dd81e..53e5d4882 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -6689,9 +6689,9 @@ dependencies = [ [[package]] name = "parity-db" -version = "0.4.2" +version = "0.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3a7511a0bec4a336b5929999d02b560d2439c993cccf98c26481484e811adc43" +checksum = "dd684a725651d9588ef21f140a328b6b4f64e646b2e931f3e6f14f75eedf9980" dependencies = [ "blake2", "crc32fast", diff --git a/bin/node/bench/Cargo.toml b/bin/node/bench/Cargo.toml index 9732faf6e..0097b51ab 100644 --- a/bin/node/bench/Cargo.toml +++ b/bin/node/bench/Cargo.toml @@ -38,7 +38,7 @@ tempfile = "3.1.0" fs_extra = "1" rand = { version = "0.8.5", features = ["small_rng"] } lazy_static = "1.4.0" -parity-db = "0.4.2" +parity-db = "0.4.3" sc-transaction-pool = { version = "4.0.0-dev", path = "../../../client/transaction-pool" } sc-transaction-pool-api = { version = "4.0.0-dev", path = "../../../client/transaction-pool/api" } futures = { version = "0.3.21", features = ["thread-pool"] } diff --git a/client/db/Cargo.toml b/client/db/Cargo.toml index 028af5d6e..a9f5e28c7 100644 --- a/client/db/Cargo.toml +++ b/client/db/Cargo.toml @@ -22,7 +22,7 @@ kvdb-memorydb = "0.13.0" kvdb-rocksdb = { version = "0.17.0", optional = true } linked-hash-map = "0.5.4" log = "0.4.17" -parity-db = "0.4.2" +parity-db = "0.4.3" parking_lot = "0.12.1" sc-client-api = { version = "4.0.0-dev", path = "../api" } sc-state-db = { version = "0.10.0-dev", path = "../state-db" } From 1314eeca18c2b1a3094a6d8fca378da1b0c386a6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bastian=20K=C3=B6cher?= Date: Wed, 25 Jan 2023 12:57:01 +0100 Subject: [PATCH 049/558] pallet-assets: Rename `total_supply` to `amount` (#13229) * pallet-assets: Rename `total_supply` to `amount` We are actually passing the `amount` on assets being minted and not the total supply. Closes: https://github.com/paritytech/substrate/issues/13210 * ".git/.scripts/commands/fmt/fmt.sh" * Fix compilation * FMT Co-authored-by: command-bot <> --- frame/assets/src/benchmarking.rs | 2 +- frame/assets/src/functions.rs | 15 ++++++--------- frame/assets/src/lib.rs | 8 +++----- 3 files changed, 10 insertions(+), 15 deletions(-) diff --git a/frame/assets/src/benchmarking.rs b/frame/assets/src/benchmarking.rs index ede5b4e77..235d1d937 100644 --- a/frame/assets/src/benchmarking.rs +++ b/frame/assets/src/benchmarking.rs @@ -220,7 +220,7 @@ benchmarks_instance_pallet! { let amount = T::Balance::from(100u32); }: _(SystemOrigin::Signed(caller.clone()), asset_id, caller_lookup, amount) verify { - assert_last_event::(Event::Issued { asset_id: asset_id.into(), owner: caller, total_supply: amount }.into()); + assert_last_event::(Event::Issued { asset_id: asset_id.into(), owner: caller, amount }.into()); } burn { diff --git a/frame/assets/src/functions.rs b/frame/assets/src/functions.rs index 2cdf2d7c4..b540f02b8 100644 --- a/frame/assets/src/functions.rs +++ b/frame/assets/src/functions.rs @@ -354,18 +354,15 @@ impl, I: 'static> Pallet { if let Some(check_issuer) = maybe_check_issuer { ensure!(check_issuer == details.issuer, Error::::NoPermission); } - debug_assert!( - T::Balance::max_value() - details.supply >= amount, - "checked in prep; qed" - ); + debug_assert!(details.supply.checked_add(&amount).is_some(), "checked in prep; qed"); + details.supply = details.supply.saturating_add(amount); + Ok(()) })?; - Self::deposit_event(Event::Issued { - asset_id: id, - owner: beneficiary.clone(), - total_supply: amount, - }); + + Self::deposit_event(Event::Issued { asset_id: id, owner: beneficiary.clone(), amount }); + Ok(()) } diff --git a/frame/assets/src/lib.rs b/frame/assets/src/lib.rs index 93e416c3b..c2480a206 100644 --- a/frame/assets/src/lib.rs +++ b/frame/assets/src/lib.rs @@ -151,9 +151,7 @@ pub use types::*; use scale_info::TypeInfo; use sp_runtime::{ - traits::{ - AtLeast32BitUnsigned, Bounded, CheckedAdd, CheckedSub, Saturating, StaticLookup, Zero, - }, + traits::{AtLeast32BitUnsigned, CheckedAdd, CheckedSub, Saturating, StaticLookup, Zero}, ArithmeticError, TokenError, }; use sp_std::{borrow::Borrow, prelude::*}; @@ -427,7 +425,7 @@ pub mod pallet { *amount, |details| -> DispatchResult { debug_assert!( - T::Balance::max_value() - details.supply >= *amount, + details.supply.checked_add(&amount).is_some(), "checked in prep; qed" ); details.supply = details.supply.saturating_add(*amount); @@ -445,7 +443,7 @@ pub mod pallet { /// Some asset class was created. Created { asset_id: T::AssetId, creator: T::AccountId, owner: T::AccountId }, /// Some assets were issued. - Issued { asset_id: T::AssetId, owner: T::AccountId, total_supply: T::Balance }, + Issued { asset_id: T::AssetId, owner: T::AccountId, amount: T::Balance }, /// Some assets were transferred. Transferred { asset_id: T::AssetId, From e3938747cdc201a6c3fbf920d0e983e2b7be01c7 Mon Sep 17 00:00:00 2001 From: Davide Galassi Date: Wed, 25 Jan 2023 12:57:59 +0100 Subject: [PATCH 050/558] Babe: bad epoch index with skipped epochs and warp sync (#13135) * Detect and correct epoch-index for skipped epochs * Code refactory * Epoch index should be also be fixed for secondary claims with VRF * Fix typo * Make clippy happy * Fix typo * Trigger pipeline --- client/consensus/babe/src/authorship.rs | 21 +++- client/consensus/babe/src/lib.rs | 75 +++++++----- client/consensus/babe/src/tests.rs | 141 +++++++++++++++++----- client/consensus/babe/src/verification.rs | 17 ++- 4 files changed, 187 insertions(+), 67 deletions(-) diff --git a/client/consensus/babe/src/authorship.rs b/client/consensus/babe/src/authorship.rs index b39153faa..3a92aa5c6 100644 --- a/client/consensus/babe/src/authorship.rs +++ b/client/consensus/babe/src/authorship.rs @@ -20,6 +20,7 @@ use super::Epoch; use codec::Encode; +use sc_consensus_epochs::Epoch as EpochT; use schnorrkel::{keys::PublicKey, vrf::VRFInOut}; use sp_application_crypto::AppKey; use sp_consensus_babe::{ @@ -135,18 +136,23 @@ fn claim_secondary_slot( keystore: &SyncCryptoStorePtr, author_secondary_vrf: bool, ) -> Option<(PreDigest, AuthorityId)> { - let Epoch { authorities, randomness, epoch_index, .. } = epoch; + let Epoch { authorities, randomness, mut epoch_index, .. } = epoch; if authorities.is_empty() { return None } + if epoch.end_slot() <= slot { + // Slot doesn't strictly belong to the epoch, create a clone with fixed values. + epoch_index = epoch.clone_for_slot(slot).epoch_index; + } + let expected_author = secondary_slot_author(slot, authorities, *randomness)?; for (authority_id, authority_index) in keys { if authority_id == expected_author { let pre_digest = if author_secondary_vrf { - let transcript_data = make_transcript_data(randomness, slot, *epoch_index); + let transcript_data = make_transcript_data(randomness, slot, epoch_index); let result = SyncCryptoStore::sr25519_vrf_sign( &**keystore, AuthorityId::ID, @@ -238,11 +244,16 @@ fn claim_primary_slot( keystore: &SyncCryptoStorePtr, keys: &[(AuthorityId, usize)], ) -> Option<(PreDigest, AuthorityId)> { - let Epoch { authorities, randomness, epoch_index, .. } = epoch; + let Epoch { authorities, randomness, mut epoch_index, .. } = epoch; + + if epoch.end_slot() <= slot { + // Slot doesn't strictly belong to the epoch, create a clone with fixed values. + epoch_index = epoch.clone_for_slot(slot).epoch_index; + } for (authority_id, authority_index) in keys { - let transcript = make_transcript(randomness, slot, *epoch_index); - let transcript_data = make_transcript_data(randomness, slot, *epoch_index); + let transcript = make_transcript(randomness, slot, epoch_index); + let transcript_data = make_transcript_data(randomness, slot, epoch_index); let result = SyncCryptoStore::sr25519_vrf_sign( &**keystore, AuthorityId::ID, diff --git a/client/consensus/babe/src/lib.rs b/client/consensus/babe/src/lib.rs index c9e171dbe..5ba523665 100644 --- a/client/consensus/babe/src/lib.rs +++ b/client/consensus/babe/src/lib.rs @@ -209,8 +209,9 @@ impl From for Epoch { } impl Epoch { - /// Create the genesis epoch (epoch #0). This is defined to start at the slot of - /// the first block, so that has to be provided. + /// Create the genesis epoch (epoch #0). + /// + /// This is defined to start at the slot of the first block, so that has to be provided. pub fn genesis(genesis_config: &BabeConfiguration, slot: Slot) -> Epoch { Epoch { epoch_index: 0, @@ -224,6 +225,38 @@ impl Epoch { }, } } + + /// Clone and tweak epoch information to refer to the specified slot. + /// + /// All the information which depends on the slot value is recomputed and assigned + /// to the returned epoch instance. + /// + /// The `slot` must be greater than or equal the original epoch start slot, + /// if is less this operation is equivalent to a simple clone. + pub fn clone_for_slot(&self, slot: Slot) -> Epoch { + let mut epoch = self.clone(); + + let skipped_epochs = *slot.saturating_sub(self.start_slot) / self.duration; + + let epoch_index = epoch.epoch_index.checked_add(skipped_epochs).expect( + "epoch number is u64; it should be strictly smaller than number of slots; \ + slots relate in some way to wall clock time; \ + if u64 is not enough we should crash for safety; qed.", + ); + + let start_slot = skipped_epochs + .checked_mul(epoch.duration) + .and_then(|skipped_slots| epoch.start_slot.checked_add(skipped_slots)) + .expect( + "slot number is u64; it should relate in some way to wall clock time; \ + if u64 is not enough we should crash for safety; qed.", + ); + + epoch.epoch_index = epoch_index; + epoch.start_slot = Slot::from(start_slot); + + epoch + } } /// Errors encountered by the babe authorship task. @@ -1540,45 +1573,27 @@ where }; if viable_epoch.as_ref().end_slot() <= slot { - // some epochs must have been skipped as our current slot - // fits outside the current epoch. we will figure out - // which epoch it belongs to and we will re-use the same - // data for that epoch - let mut epoch_data = viable_epoch.as_mut(); - let skipped_epochs = - *slot.saturating_sub(epoch_data.start_slot) / epoch_data.duration; - - // NOTE: notice that we are only updating a local copy of the `Epoch`, this + // Some epochs must have been skipped as our current slot fits outside the + // current epoch. We will figure out which epoch it belongs to and we will + // re-use the same data for that epoch. + // Notice that we are only updating a local copy of the `Epoch`, this // makes it so that when we insert the next epoch into `EpochChanges` below // (after incrementing it), it will use the correct epoch index and start slot. - // we do not update the original epoch that will be re-used because there might + // We do not update the original epoch that will be re-used because there might // be other forks (that we haven't imported) where the epoch isn't skipped, and - // to import those forks we want to keep the original epoch data. not updating + // to import those forks we want to keep the original epoch data. Not updating // the original epoch works because when we search the tree for which epoch to // use for a given slot, we will search in-depth with the predicate // `epoch.start_slot <= slot` which will still match correctly without updating // `start_slot` to the correct value as below. - let epoch_index = epoch_data.epoch_index.checked_add(skipped_epochs).expect( - "epoch number is u64; it should be strictly smaller than number of slots; \ - slots relate in some way to wall clock time; \ - if u64 is not enough we should crash for safety; qed.", - ); - - let start_slot = skipped_epochs - .checked_mul(epoch_data.duration) - .and_then(|skipped_slots| epoch_data.start_slot.checked_add(skipped_slots)) - .expect( - "slot number is u64; it should relate in some way to wall clock time; \ - if u64 is not enough we should crash for safety; qed.", - ); + let epoch = viable_epoch.as_mut(); + let prev_index = epoch.epoch_index; + *epoch = epoch.clone_for_slot(slot); warn!( target: LOG_TARGET, - "👶 Epoch(s) skipped: from {} to {}", epoch_data.epoch_index, epoch_index, + "👶 Epoch(s) skipped: from {} to {}", prev_index, epoch.epoch_index, ); - - epoch_data.epoch_index = epoch_index; - epoch_data.start_slot = Slot::from(start_slot); } log!( diff --git a/client/consensus/babe/src/tests.rs b/client/consensus/babe/src/tests.rs index 304bc9fc2..0dd0b59dd 100644 --- a/client/consensus/babe/src/tests.rs +++ b/client/consensus/babe/src/tests.rs @@ -20,7 +20,6 @@ use super::*; use authorship::claim_slot; -use log::debug; use rand_chacha::{ rand_core::{RngCore, SeedableRng}, ChaChaRng, @@ -35,9 +34,10 @@ use sp_application_crypto::key_types::BABE; use sp_consensus::{DisableProofRecording, NoNetwork as DummyOracle, Proposal}; use sp_consensus_babe::{ inherents::InherentDataProvider, make_transcript, make_transcript_data, AllowedSlots, - AuthorityPair, Slot, + AuthorityId, AuthorityPair, Slot, }; use sp_consensus_slots::SlotDuration; +use sp_consensus_vrf::schnorrkel::VRFOutput; use sp_core::crypto::Pair; use sp_keyring::Sr25519Keyring; use sp_keystore::{ @@ -553,52 +553,133 @@ fn sig_is_not_pre_digest() { } #[test] -fn can_author_block() { - sp_tracing::try_init_simple(); +fn claim_epoch_slots() { + // We don't require the full claim information, thus as a shorter alias we're + // going to use a simple integer value. Generally not verbose enough, but good enough + // to be used within the narrow scope of a single test. + // 0 -> None (i.e. unable to claim the slot), + // 1 -> Primary + // 2 -> Secondary + // 3 -> Secondary with VRF + const EPOCH_DURATION: u64 = 10; let authority = Sr25519Keyring::Alice; let keystore = create_keystore(authority); - let mut i = 0; - let epoch = Epoch { + let mut epoch = Epoch { start_slot: 0.into(), authorities: vec![(authority.public().into(), 1)], randomness: [0; 32], epoch_index: 1, - duration: 100, + duration: EPOCH_DURATION, config: BabeEpochConfiguration { c: (3, 10), allowed_slots: AllowedSlots::PrimaryAndSecondaryPlainSlots, }, }; - let mut config = crate::BabeConfiguration { - slot_duration: 1000, - epoch_length: 100, - c: (3, 10), - authorities: Vec::new(), + let claim_slot_wrap = |s, e| match claim_slot(Slot::from(s as u64), &e, &keystore) { + None => 0, + Some((PreDigest::Primary(_), _)) => 1, + Some((PreDigest::SecondaryPlain(_), _)) => 2, + Some((PreDigest::SecondaryVRF(_), _)) => 3, + }; + + // With secondary mechanism we should be able to claim all slots. + let claims: Vec<_> = (0..EPOCH_DURATION) + .into_iter() + .map(|slot| claim_slot_wrap(slot, epoch.clone())) + .collect(); + assert_eq!(claims, [1, 2, 2, 1, 2, 2, 2, 2, 2, 1]); + + // With secondary with VRF mechanism we should be able to claim all the slots. + epoch.config.allowed_slots = AllowedSlots::PrimaryAndSecondaryVRFSlots; + let claims: Vec<_> = (0..EPOCH_DURATION) + .into_iter() + .map(|slot| claim_slot_wrap(slot, epoch.clone())) + .collect(); + assert_eq!(claims, [1, 3, 3, 1, 3, 3, 3, 3, 3, 1]); + + // Otherwise with only vrf-based primary slots we are able to claim a subset of the slots. + epoch.config.allowed_slots = AllowedSlots::PrimarySlots; + let claims: Vec<_> = (0..EPOCH_DURATION) + .into_iter() + .map(|slot| claim_slot_wrap(slot, epoch.clone())) + .collect(); + assert_eq!(claims, [1, 0, 0, 1, 0, 0, 0, 0, 0, 1]); +} + +#[test] +fn claim_vrf_check() { + let authority = Sr25519Keyring::Alice; + let keystore = create_keystore(authority); + + let public = authority.public(); + + let epoch = Epoch { + start_slot: 0.into(), + authorities: vec![(public.into(), 1)], randomness: [0; 32], - allowed_slots: AllowedSlots::PrimaryAndSecondaryPlainSlots, + epoch_index: 1, + duration: 10, + config: BabeEpochConfiguration { + c: (3, 10), + allowed_slots: AllowedSlots::PrimaryAndSecondaryVRFSlots, + }, }; - // with secondary slots enabled it should never be empty - match claim_slot(i.into(), &epoch, &keystore) { - None => i += 1, - Some(s) => debug!(target: LOG_TARGET, "Authored block {:?}", s.0), - } + // We leverage the predictability of claim types given a constant randomness. - // otherwise with only vrf-based primary slots we might need to try a couple - // of times. - config.allowed_slots = AllowedSlots::PrimarySlots; - loop { - match claim_slot(i.into(), &epoch, &keystore) { - None => i += 1, - Some(s) => { - debug!(target: LOG_TARGET, "Authored block {:?}", s.0); - break - }, - } - } + // We expect a Primary claim for slot 0 + + let pre_digest = match claim_slot(0.into(), &epoch, &keystore).unwrap().0 { + PreDigest::Primary(d) => d, + v => panic!("Unexpected pre-digest variant {:?}", v), + }; + let transcript = make_transcript_data(&epoch.randomness.clone(), 0.into(), epoch.epoch_index); + let sign = SyncCryptoStore::sr25519_vrf_sign(&*keystore, AuthorityId::ID, &public, transcript) + .unwrap() + .unwrap(); + assert_eq!(pre_digest.vrf_output, VRFOutput(sign.output)); + + // We expect a SecondaryVRF claim for slot 1 + let pre_digest = match claim_slot(1.into(), &epoch, &keystore).unwrap().0 { + PreDigest::SecondaryVRF(d) => d, + v => panic!("Unexpected pre-digest variant {:?}", v), + }; + let transcript = make_transcript_data(&epoch.randomness.clone(), 1.into(), epoch.epoch_index); + let sign = SyncCryptoStore::sr25519_vrf_sign(&*keystore, AuthorityId::ID, &public, transcript) + .unwrap() + .unwrap(); + assert_eq!(pre_digest.vrf_output, VRFOutput(sign.output)); + + // Check that correct epoch index has been used if epochs are skipped (primary VRF) + let slot = Slot::from(103); + let claim = match claim_slot(slot, &epoch, &keystore).unwrap().0 { + PreDigest::Primary(d) => d, + v => panic!("Unexpected claim variant {:?}", v), + }; + let fixed_epoch = epoch.clone_for_slot(slot); + let transcript = make_transcript_data(&epoch.randomness.clone(), slot, fixed_epoch.epoch_index); + let sign = SyncCryptoStore::sr25519_vrf_sign(&*keystore, AuthorityId::ID, &public, transcript) + .unwrap() + .unwrap(); + assert_eq!(fixed_epoch.epoch_index, 11); + assert_eq!(claim.vrf_output, VRFOutput(sign.output)); + + // Check that correct epoch index has been used if epochs are skipped (secondary VRF) + let slot = Slot::from(100); + let pre_digest = match claim_slot(slot, &epoch, &keystore).unwrap().0 { + PreDigest::SecondaryVRF(d) => d, + v => panic!("Unexpected claim variant {:?}", v), + }; + let fixed_epoch = epoch.clone_for_slot(slot); + let transcript = make_transcript_data(&epoch.randomness.clone(), slot, fixed_epoch.epoch_index); + let sign = SyncCryptoStore::sr25519_vrf_sign(&*keystore, AuthorityId::ID, &public, transcript) + .unwrap() + .unwrap(); + assert_eq!(fixed_epoch.epoch_index, 11); + assert_eq!(pre_digest.vrf_output, VRFOutput(sign.output)); } // Propose and import a new BABE block on top of the given parent. diff --git a/client/consensus/babe/src/verification.rs b/client/consensus/babe/src/verification.rs index e77a70c8e..8e8f5d998 100644 --- a/client/consensus/babe/src/verification.rs +++ b/client/consensus/babe/src/verification.rs @@ -22,6 +22,7 @@ use crate::{ babe_err, find_pre_digest, BlockT, Epoch, Error, LOG_TARGET, }; use log::{debug, trace}; +use sc_consensus_epochs::Epoch as EpochT; use sc_consensus_slots::CheckedHeader; use sp_consensus_babe::{ digests::{ @@ -155,10 +156,16 @@ fn check_primary_header( c: (u64, u64), ) -> Result<(), Error> { let author = &epoch.authorities[pre_digest.authority_index as usize].0; + let mut epoch_index = epoch.epoch_index; + + if epoch.end_slot() <= pre_digest.slot { + // Slot doesn't strictly belong to this epoch, create a clone with fixed values. + epoch_index = epoch.clone_for_slot(pre_digest.slot).epoch_index; + } if AuthorityPair::verify(&signature, pre_hash, author) { let (inout, _) = { - let transcript = make_transcript(&epoch.randomness, pre_digest.slot, epoch.epoch_index); + let transcript = make_transcript(&epoch.randomness, pre_digest.slot, epoch_index); schnorrkel::PublicKey::from_bytes(author.as_slice()) .and_then(|p| { @@ -228,8 +235,14 @@ fn check_secondary_vrf_header( return Err(Error::InvalidAuthor(expected_author.clone(), author.clone())) } + let mut epoch_index = epoch.epoch_index; + if epoch.end_slot() <= pre_digest.slot { + // Slot doesn't strictly belong to this epoch, create a clone with fixed values. + epoch_index = epoch.clone_for_slot(pre_digest.slot).epoch_index; + } + if AuthorityPair::verify(&signature, pre_hash.as_ref(), author) { - let transcript = make_transcript(&epoch.randomness, pre_digest.slot, epoch.epoch_index); + let transcript = make_transcript(&epoch.randomness, pre_digest.slot, epoch_index); schnorrkel::PublicKey::from_bytes(author.as_slice()) .and_then(|p| p.vrf_verify(transcript, &pre_digest.vrf_output, &pre_digest.vrf_proof)) From 1a1600b827020cae62c9b856cb9ba4b823ab080a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alexander=20Thei=C3=9Fen?= Date: Wed, 25 Jan 2023 10:48:40 -0300 Subject: [PATCH 051/558] contracts: Deprecate random interface (#13204) * Deprecate random interface * Revert change to runtime file * Fix docs * Fix tests * Rename to not_deprecated * Apply suggestions from code review Co-authored-by: Sasha Gryaznov * Deprecate `set_rent_allowance` Co-authored-by: Sasha Gryaznov --- frame/contracts/proc-macro/src/lib.rs | 55 ++++- frame/contracts/src/benchmarking/sandbox.rs | 13 +- frame/contracts/src/exec.rs | 31 +++ frame/contracts/src/lib.rs | 9 +- frame/contracts/src/wasm/mod.rs | 217 +++++++++++++++++--- frame/contracts/src/wasm/prepare.rs | 72 +++++-- frame/contracts/src/wasm/runtime.rs | 132 ++++++------ 7 files changed, 416 insertions(+), 113 deletions(-) diff --git a/frame/contracts/proc-macro/src/lib.rs b/frame/contracts/proc-macro/src/lib.rs index d0247d720..eaf437c15 100644 --- a/frame/contracts/proc-macro/src/lib.rs +++ b/frame/contracts/proc-macro/src/lib.rs @@ -162,6 +162,8 @@ struct HostFn { returns: HostFnReturn, is_stable: bool, alias_to: Option, + /// Formulating the predicate inverted makes the expression using it simpler. + not_deprecated: bool, } enum HostFnReturn { @@ -199,13 +201,14 @@ impl HostFn { // process attributes let msg = - "only #[version()], #[unstable] and #[prefixed_alias] attributes are allowed."; + "only #[version()], #[unstable], #[prefixed_alias] and #[deprecated] attributes are allowed."; let span = item.span(); let mut attrs = item.attrs.clone(); attrs.retain(|a| !a.path.is_ident("doc")); let mut maybe_module = None; let mut is_stable = true; let mut alias_to = None; + let mut not_deprecated = true; while let Some(attr) = attrs.pop() { let ident = attr.path.get_ident().ok_or(err(span, msg))?.to_string(); match ident.as_str() { @@ -230,12 +233,22 @@ impl HostFn { item.sig.ident.span(), ); }, + "deprecated" => { + if !not_deprecated { + return Err(err(span, "#[deprecated] can only be specified once")) + } + not_deprecated = false; + }, _ => return Err(err(span, msg)), } } let name = item.sig.ident.to_string(); - // process arguments: The first and second arg are treated differently (ctx, memory) + if !(is_stable || not_deprecated) { + return Err(err(span, "#[deprecated] is mutually exclusive with #[unstable]")) + } + + // process arguments: The first and second args are treated differently (ctx, memory) // they must exist and be `ctx: _` and `memory: _`. let msg = "Every function must start with two inferred parameters: ctx: _ and memory: _"; let special_args = item @@ -330,6 +343,7 @@ impl HostFn { returns, is_stable, alias_to, + not_deprecated, }) }, _ => Err(err(span, &msg)), @@ -510,7 +524,12 @@ fn expand_impls(def: &mut EnvDef) -> TokenStream2 { quote! { impl<'a, E: Ext> crate::wasm::Environment> for Env { - fn define(store: &mut ::wasmi::Store>, linker: &mut ::wasmi::Linker>, allow_unstable: bool) -> Result<(), ::wasmi::errors::LinkerError> { + fn define( + store: &mut ::wasmi::Store>, + linker: &mut ::wasmi::Linker>, + allow_unstable: AllowUnstableInterface, + allow_deprecated: AllowDeprecatedInterface, + ) -> Result<(),::wasmi::errors::LinkerError> { #impls Ok(()) } @@ -518,7 +537,12 @@ fn expand_impls(def: &mut EnvDef) -> TokenStream2 { impl crate::wasm::Environment<()> for Env { - fn define(store: &mut ::wasmi::Store<()>, linker: &mut ::wasmi::Linker<()>, allow_unstable: bool) -> Result<(), ::wasmi::errors::LinkerError> { + fn define( + store: &mut ::wasmi::Store<()>, + linker: &mut ::wasmi::Linker<()>, + allow_unstable: AllowUnstableInterface, + allow_deprecated: AllowDeprecatedInterface, + ) -> Result<(), ::wasmi::errors::LinkerError> { #dummy_impls Ok(()) } @@ -542,6 +566,7 @@ fn expand_functions( &f.item.sig.output ); let is_stable = f.is_stable; + let not_deprecated = f.not_deprecated; // If we don't expand blocks (implementing for `()`) we change a few things: // - We replace any code by unreachable! @@ -582,9 +607,13 @@ fn expand_functions( }; quote! { - // We need to allow unstable functions when runtime benchmarks are performed because - // we generate the weights even when those interfaces are not enabled. - if ::core::cfg!(feature = "runtime-benchmarks") || #is_stable || allow_unstable { + // We need to allow all interfaces when runtime benchmarks are performed because + // we generate the weights even when those interfaces are not enabled. This + // is necessary as the decision whether we allow unstable or deprecated functions + // is a decision made at runtime. Generation of the weights happens statically. + if ::core::cfg!(feature = "runtime-benchmarks") || + ((#is_stable || __allow_unstable__) && (#not_deprecated || __allow_deprecated__)) + { #allow_unused linker.define(#module, #name, ::wasmi::Func::wrap(&mut*store, |mut __caller__: ::wasmi::Caller<#host_state>, #( #params, )*| -> #wasm_output { let mut func = #inner; @@ -596,6 +625,8 @@ fn expand_functions( } }); quote! { + let __allow_unstable__ = matches!(allow_unstable, AllowUnstableInterface::Yes); + let __allow_deprecated__ = matches!(allow_deprecated, AllowDeprecatedInterface::Yes); #( #impls )* } } @@ -691,6 +722,16 @@ fn expand_functions( /// `...` modules each having its `Api` trait containing functions holding documentation for every /// host function defined by the macro. /// +/// # Deprecated Interfaces +/// +/// An interface can be annotated with `#[deprecated]`. It is mutually exclusive with `#[unstable]`. +/// Deprecated interfaces have the following properties: +/// - New contract codes utilizing those interfaces cannot be uploaded. +/// - New contracts from existing codes utilizing those interfaces cannot be instantiated. +/// - Existing contracts containing those interfaces still work. +/// +/// Those interfaces will eventually be removed. +/// /// To build up these docs, run: /// /// ```nocompile diff --git a/frame/contracts/src/benchmarking/sandbox.rs b/frame/contracts/src/benchmarking/sandbox.rs index 5d080bad9..a6a97d11a 100644 --- a/frame/contracts/src/benchmarking/sandbox.rs +++ b/frame/contracts/src/benchmarking/sandbox.rs @@ -19,7 +19,9 @@ /// ! sandbox to execute the wasm code. This is because we do not need the full /// ! environment that provides the seal interface as imported functions. use super::{code::WasmModule, Config}; -use crate::wasm::{Environment, PrefabWasmModule}; +use crate::wasm::{ + AllowDeprecatedInterface, AllowUnstableInterface, Environment, PrefabWasmModule, +}; use wasmi::{errors::LinkerError, Func, Linker, StackLimits, Store}; /// Minimal execution environment without any imported functions. @@ -49,6 +51,8 @@ impl From<&WasmModule> for Sandbox { (), memory, StackLimits::default(), + // We are testing with an empty environment anyways + AllowDeprecatedInterface::No, ) .expect("Failed to create benchmarking Sandbox instance"); let entry_point = instance.get_export(&store, "call").unwrap().into_func().unwrap(); @@ -59,7 +63,12 @@ impl From<&WasmModule> for Sandbox { struct EmptyEnv; impl Environment<()> for EmptyEnv { - fn define(_: &mut Store<()>, _: &mut Linker<()>, _: bool) -> Result<(), LinkerError> { + fn define( + _: &mut Store<()>, + _: &mut Linker<()>, + _: AllowUnstableInterface, + _: AllowDeprecatedInterface, + ) -> Result<(), LinkerError> { Ok(()) } } diff --git a/frame/contracts/src/exec.rs b/frame/contracts/src/exec.rs index b5b630653..7a07b01d2 100644 --- a/frame/contracts/src/exec.rs +++ b/frame/contracts/src/exec.rs @@ -3440,4 +3440,35 @@ mod tests { )); }); } + + /// This works even though random interface is deprecated, as the check to ban deprecated + /// functions happens in the wasm stack which is mocked for exec tests. + #[test] + fn randomness_works() { + let subject = b"nice subject".as_ref(); + let code_hash = MockLoader::insert(Call, move |ctx, _| { + let rand = ::Randomness::random(subject); + assert_eq!(rand, ctx.ext.random(subject)); + exec_success() + }); + + ExtBuilder::default().build().execute_with(|| { + let schedule = ::Schedule::get(); + place_contract(&BOB, code_hash); + + let mut storage_meter = storage::meter::Meter::new(&ALICE, Some(0), 0).unwrap(); + let result = MockStack::run_call( + ALICE, + BOB, + &mut GasMeter::::new(GAS_LIMIT), + &mut storage_meter, + &schedule, + 0, + vec![], + None, + Determinism::Deterministic, + ); + assert_matches!(result, Ok(_)); + }); + } } diff --git a/frame/contracts/src/lib.rs b/frame/contracts/src/lib.rs index 672c4517d..84a72b701 100644 --- a/frame/contracts/src/lib.rs +++ b/frame/contracts/src/lib.rs @@ -216,7 +216,14 @@ pub mod pallet { /// The time implementation used to supply timestamps to contracts through `seal_now`. type Time: Time; - /// The generator used to supply randomness to contracts through `seal_random` + /// The generator used to supply randomness to contracts through `seal_random`. + /// + /// # Deprecated + /// + /// Codes using the randomness functionality cannot be uploaded. Neither can contracts + /// be instantiated from existing codes that use this deprecated functionality. It will + /// be removed eventually. Hence for new `pallet-contracts` deployments it is okay + /// to supply a dummy implementation for this type (because it is never used). type Randomness: Randomness; /// The currency in which fees are paid and contract balances are held. diff --git a/frame/contracts/src/wasm/mod.rs b/frame/contracts/src/wasm/mod.rs index c2b061176..6b9cefcdd 100644 --- a/frame/contracts/src/wasm/mod.rs +++ b/frame/contracts/src/wasm/mod.rs @@ -24,14 +24,16 @@ mod runtime; #[cfg(feature = "runtime-benchmarks")] pub use crate::wasm::code_cache::reinstrument; +#[cfg(doc)] +pub use crate::wasm::runtime::api_doc; pub use crate::wasm::{ prepare::TryInstantiate, - runtime::{CallFlags, Environment, ReturnCode, Runtime, RuntimeCosts}, + runtime::{ + AllowDeprecatedInterface, AllowUnstableInterface, CallFlags, Environment, ReturnCode, + Runtime, RuntimeCosts, + }, }; -#[cfg(doc)] -pub use crate::wasm::runtime::api_doc; - use crate::{ exec::{ExecResult, Executable, ExportedFunction, Ext}, gas::GasMeter, @@ -205,6 +207,7 @@ impl PrefabWasmModule { host_state: H, memory: (u32, u32), stack_limits: StackLimits, + allow_deprecated: AllowDeprecatedInterface, ) -> Result<(Store, Memory, Instance), wasmi::Error> where E: Environment, @@ -220,7 +223,16 @@ impl PrefabWasmModule { let module = Module::new(&engine, code)?; let mut store = Store::new(&engine, host_state); let mut linker = Linker::new(); - E::define(&mut store, &mut linker, T::UnsafeUnstableInterface::get())?; + E::define( + &mut store, + &mut linker, + if T::UnsafeUnstableInterface::get() { + AllowUnstableInterface::Yes + } else { + AllowUnstableInterface::No + }, + allow_deprecated, + )?; let memory = Memory::new(&mut store, MemoryType::new(memory.0, Some(memory.1))?).expect( "The limits defined in our `Schedule` limit the amount of memory well below u32::MAX; qed", ); @@ -233,20 +245,14 @@ impl PrefabWasmModule { Ok((store, memory, instance)) } - /// Create and store the module without checking nor instrumenting the passed code. - /// - /// # Note - /// - /// This is useful for benchmarking where we don't want instrumentation to skew - /// our results. This also does not collect any deposit from the `owner`. + /// See [`Self::from_code_unchecked`]. #[cfg(feature = "runtime-benchmarks")] pub fn store_code_unchecked( original_code: Vec, schedule: &Schedule, owner: T::AccountId, ) -> DispatchResult { - let executable = prepare::benchmarking::prepare(original_code, schedule, owner) - .map_err::(Into::into)?; + let executable = Self::from_code_unchecked(original_code, schedule, owner)?; code_cache::store(executable, false) } @@ -255,6 +261,23 @@ impl PrefabWasmModule { pub fn decrement_version(&mut self) { self.instruction_weights_version = self.instruction_weights_version.checked_sub(1).unwrap(); } + + /// Create the module without checking nor instrumenting the passed code. + /// + /// # Note + /// + /// This is useful for benchmarking where we don't want instrumentation to skew + /// our results. This also does not collect any deposit from the `owner`. Also useful + /// during testing when we want to deploy codes that do not pass the instantiation checks. + #[cfg(any(test, feature = "runtime-benchmarks"))] + fn from_code_unchecked( + original_code: Vec, + schedule: &Schedule, + owner: T::AccountId, + ) -> Result { + prepare::benchmarking::prepare(original_code, schedule, owner) + .map_err::(Into::into) + } } impl OwnerInfo { @@ -294,6 +317,10 @@ impl Executable for PrefabWasmModule { runtime, (self.initial, self.maximum), StackLimits::default(), + match function { + ExportedFunction::Constructor => AllowDeprecatedInterface::No, + ExportedFunction::Call => AllowDeprecatedInterface::Yes, + }, ) .map_err(|msg| { log::debug!(target: "runtime::contracts", "failed to instantiate code: {}", msg); @@ -629,38 +656,77 @@ mod tests { } } + /// Execute the supplied code. + /// + /// Not used directly but through the wrapper functions defined below. fn execute_internal>( wat: &str, input_data: Vec, mut ext: E, + entry_point: &ExportedFunction, unstable_interface: bool, + skip_checks: bool, ) -> ExecResult { type RuntimeConfig = ::T; RuntimeConfig::set_unstable_interface(unstable_interface); let wasm = wat::parse_str(wat).unwrap(); let schedule = crate::Schedule::default(); - let executable = PrefabWasmModule::::from_code( - wasm, - &schedule, - ALICE, - Determinism::Deterministic, - TryInstantiate::Skip, - ) - .map_err(|err| err.0)?; - executable.execute(ext.borrow_mut(), &ExportedFunction::Call, input_data) + let executable = if skip_checks { + PrefabWasmModule::::from_code_unchecked(wasm, &schedule, ALICE)? + } else { + PrefabWasmModule::::from_code( + wasm, + &schedule, + ALICE, + Determinism::Deterministic, + TryInstantiate::Instantiate, + ) + .map_err(|err| err.0)? + }; + executable.execute(ext.borrow_mut(), entry_point, input_data) } + /// Execute the suppplied code. fn execute>(wat: &str, input_data: Vec, ext: E) -> ExecResult { - execute_internal(wat, input_data, ext, true) + execute_internal(wat, input_data, ext, &ExportedFunction::Call, true, false) } + /// Execute the supplied code with disabled unstable functions. + /// + /// In our test config unstable functions are disabled so that we can test them. + /// In order to test that code using them is properly rejected we temporarily disable + /// them when this test is run. #[cfg(not(feature = "runtime-benchmarks"))] fn execute_no_unstable>( wat: &str, input_data: Vec, ext: E, ) -> ExecResult { - execute_internal(wat, input_data, ext, false) + execute_internal(wat, input_data, ext, &ExportedFunction::Call, false, false) + } + + /// Execute code without validating it first. + /// + /// This is mainly useful in order to test code which uses deprecated functions. Those + /// would fail when validating the code. + fn execute_unvalidated>( + wat: &str, + input_data: Vec, + ext: E, + ) -> ExecResult { + execute_internal(wat, input_data, ext, &ExportedFunction::Call, false, true) + } + + /// Execute instantiation entry point of code without validating it first. + /// + /// Same as `execute_unvalidated` except that the `deploy` entry point is ran. + #[cfg(not(feature = "runtime-benchmarks"))] + fn execute_instantiate_unvalidated>( + wat: &str, + input_data: Vec, + ext: E, + ) -> ExecResult { + execute_internal(wat, input_data, ext, &ExportedFunction::Constructor, false, true) } const CODE_TRANSFER: &str = r#" @@ -1861,7 +1927,7 @@ mod tests { #[test] fn random() { - let output = execute(CODE_RANDOM, vec![], MockExt::default()).unwrap(); + let output = execute_unvalidated(CODE_RANDOM, vec![], MockExt::default()).unwrap(); // The mock ext just returns the same data that was passed as the subject. assert_eq!( @@ -1931,7 +1997,7 @@ mod tests { #[test] fn random_v1() { - let output = execute(CODE_RANDOM_V1, vec![], MockExt::default()).unwrap(); + let output = execute_unvalidated(CODE_RANDOM_V1, vec![], MockExt::default()).unwrap(); // The mock ext just returns the same data that was passed as the subject. assert_eq!( @@ -3007,6 +3073,29 @@ mod tests { execute(CODE, vec![], &mut mock_ext).unwrap(); } + /// Code with deprecated functions cannot be uploaded or instantiated. However, we + /// need to make sure that it still can be re-instrumented. + #[test] + fn can_reinstrument_deprecated() { + const CODE_RANDOM: &str = r#" +(module + (import "seal0" "random" (func $seal_random (param i32 i32 i32 i32))) + (func (export "call")) + (func (export "deploy")) +) + "#; + let wasm = wat::parse_str(CODE_RANDOM).unwrap(); + let schedule = crate::Schedule::::default(); + #[cfg(not(feature = "runtime-benchmarks"))] + assert_err!(execute(CODE_RANDOM, vec![], MockExt::default()), >::CodeRejected); + self::prepare::reinstrument::( + &wasm, + &schedule, + Determinism::Deterministic, + ) + .unwrap(); + } + /// This test check that an unstable interface cannot be deployed. In case of runtime /// benchmarks we always allow unstable interfaces. This is why this test does not /// work when this feature is enabled. @@ -3026,4 +3115,80 @@ mod tests { ); assert_ok!(execute(CANNOT_DEPLOY_UNSTABLE, vec![], MockExt::default())); } + + /// The random interface is deprecated and hence new contracts using it should not be deployed. + /// In case of runtime benchmarks we always allow deprecated interfaces. This is why this + /// test doesn't work if this feature is enabled. + #[cfg(not(feature = "runtime-benchmarks"))] + #[test] + fn cannot_deploy_deprecated() { + const CODE_RANDOM_0: &str = r#" +(module + (import "seal0" "seal_random" (func $seal_random (param i32 i32 i32 i32))) + (func (export "call")) + (func (export "deploy")) +) + "#; + const CODE_RANDOM_1: &str = r#" +(module + (import "seal1" "seal_random" (func $seal_random (param i32 i32 i32 i32))) + (func (export "call")) + (func (export "deploy")) +) + "#; + const CODE_RANDOM_2: &str = r#" +(module + (import "seal0" "random" (func $seal_random (param i32 i32 i32 i32))) + (func (export "call")) + (func (export "deploy")) +) + "#; + const CODE_RANDOM_3: &str = r#" +(module + (import "seal1" "random" (func $seal_random (param i32 i32 i32 i32))) + (func (export "call")) + (func (export "deploy")) +) + "#; + + assert_ok!(execute_unvalidated(CODE_RANDOM_0, vec![], MockExt::default())); + assert_err!( + execute_instantiate_unvalidated(CODE_RANDOM_0, vec![], MockExt::default()), + >::CodeRejected, + ); + assert_err!( + execute(CODE_RANDOM_0, vec![], MockExt::default()), + >::CodeRejected, + ); + + assert_ok!(execute_unvalidated(CODE_RANDOM_1, vec![], MockExt::default())); + assert_err!( + execute_instantiate_unvalidated(CODE_RANDOM_1, vec![], MockExt::default()), + >::CodeRejected, + ); + assert_err!( + execute(CODE_RANDOM_1, vec![], MockExt::default()), + >::CodeRejected, + ); + + assert_ok!(execute_unvalidated(CODE_RANDOM_2, vec![], MockExt::default())); + assert_err!( + execute_instantiate_unvalidated(CODE_RANDOM_2, vec![], MockExt::default()), + >::CodeRejected, + ); + assert_err!( + execute(CODE_RANDOM_2, vec![], MockExt::default()), + >::CodeRejected, + ); + + assert_ok!(execute_unvalidated(CODE_RANDOM_3, vec![], MockExt::default())); + assert_err!( + execute_instantiate_unvalidated(CODE_RANDOM_3, vec![], MockExt::default()), + >::CodeRejected, + ); + assert_err!( + execute(CODE_RANDOM_3, vec![], MockExt::default()), + >::CodeRejected, + ); + } } diff --git a/frame/contracts/src/wasm/prepare.rs b/frame/contracts/src/wasm/prepare.rs index dfe25bc3a..787b97d31 100644 --- a/frame/contracts/src/wasm/prepare.rs +++ b/frame/contracts/src/wasm/prepare.rs @@ -22,7 +22,9 @@ use crate::{ chain_extension::ChainExtension, storage::meter::Diff, - wasm::{Determinism, Environment, OwnerInfo, PrefabWasmModule}, + wasm::{ + runtime::AllowDeprecatedInterface, Determinism, Environment, OwnerInfo, PrefabWasmModule, + }, AccountIdOf, CodeVec, Config, Error, Schedule, }; use codec::{Encode, MaxEncodedLen}; @@ -54,6 +56,14 @@ pub enum TryInstantiate { Skip, } +/// The reason why a contract is instrumented. +enum InstrumentReason { + /// A new code is uploaded. + New, + /// Existing code is re-instrumented. + Reinstrument, +} + struct ContractModule<'a, T: Config> { /// A deserialized module. The module is valid (this is Guaranteed by `new` method). module: elements::Module, @@ -381,6 +391,7 @@ fn instrument( schedule: &Schedule, determinism: Determinism, try_instantiate: TryInstantiate, + reason: InstrumentReason, ) -> Result<(Vec, (u32, u32)), (DispatchError, &'static str)> where E: Environment<()>, @@ -454,11 +465,20 @@ where // We don't actually ever run any code so we can get away with a minimal stack which // reduces the amount of memory that needs to be zeroed. let stack_limits = StackLimits::new(1, 1, 0).expect("initial <= max; qed"); - PrefabWasmModule::::instantiate::(&code, (), (initial, maximum), stack_limits) - .map_err(|err| { - log::debug!(target: "runtime::contracts", "{}", err); - (Error::::CodeRejected.into(), "new code rejected after instrumentation") - })?; + PrefabWasmModule::::instantiate::( + &code, + (), + (initial, maximum), + stack_limits, + match reason { + InstrumentReason::New => AllowDeprecatedInterface::No, + InstrumentReason::Reinstrument => AllowDeprecatedInterface::Yes, + }, + ) + .map_err(|err| { + log::debug!(target: "runtime::contracts", "{}", err); + (Error::::CodeRejected.into(), "new code rejected after instrumentation") + })?; } Ok((code, (initial, maximum))) @@ -486,8 +506,13 @@ where E: Environment<()>, T: Config, { - let (code, (initial, maximum)) = - instrument::(original_code.as_ref(), schedule, determinism, try_instantiate)?; + let (code, (initial, maximum)) = instrument::( + original_code.as_ref(), + schedule, + determinism, + try_instantiate, + InstrumentReason::New, + )?; let original_code_len = original_code.len(); @@ -532,21 +557,30 @@ where E: Environment<()>, T: Config, { - instrument::(original_code, schedule, determinism, TryInstantiate::Skip) - .map_err(|(err, msg)| { - log::error!(target: "runtime::contracts", "CodeRejected during reinstrument: {}", msg); - err - }) - .map(|(code, _)| code) + instrument::( + original_code, + schedule, + determinism, + // This function was triggered by an interaction with an existing contract code + // that will try to instantiate anyways. Failing here would not help + // as the contract is already on chain. + TryInstantiate::Skip, + InstrumentReason::Reinstrument, + ) + .map_err(|(err, msg)| { + log::error!(target: "runtime::contracts", "CodeRejected during reinstrument: {}", msg); + err + }) + .map(|(code, _)| code) } -/// Alternate (possibly unsafe) preparation functions used only for benchmarking. +/// Alternate (possibly unsafe) preparation functions used only for benchmarking and testing. /// /// For benchmarking we need to construct special contracts that might not pass our /// sanity checks or need to skip instrumentation for correct results. We hide functions -/// allowing this behind a feature that is only set during benchmarking to prevent usage -/// in production code. -#[cfg(feature = "runtime-benchmarks")] +/// allowing this behind a feature that is only set during benchmarking or testing to +/// prevent usage in production code. +#[cfg(any(test, feature = "runtime-benchmarks"))] pub mod benchmarking { use super::*; @@ -600,7 +634,7 @@ mod tests { #[allow(unreachable_code)] mod env { use super::*; - use crate::wasm::runtime::TrapReason; + use crate::wasm::runtime::{AllowDeprecatedInterface, AllowUnstableInterface, TrapReason}; // Define test environment for tests. We need ImportSatisfyCheck // implementation from it. So actual implementations doesn't matter. diff --git a/frame/contracts/src/wasm/runtime.rs b/frame/contracts/src/wasm/runtime.rs index 745583b33..9cdf0bf09 100644 --- a/frame/contracts/src/wasm/runtime.rs +++ b/frame/contracts/src/wasm/runtime.rs @@ -37,6 +37,22 @@ use wasmi::{core::HostError, errors::LinkerError, Linker, Memory, Store}; /// The maximum nesting depth a contract can use when encoding types. const MAX_DECODE_NESTING: u32 = 256; +/// Passed to [`Environment`] to determine whether it should expose deprecated interfaces. +pub enum AllowDeprecatedInterface { + /// No deprecated interfaces are exposed. + No, + /// Deprecated interfaces are exposed. + Yes, +} + +/// Passed to [`Environment`] to determine whether it should expose unstable interfaces. +pub enum AllowUnstableInterface { + /// No unstable interfaces are exposed. + No, + /// Unstable interfaces are exposed. + Yes, +} + /// Trait implemented by the [`define_env`](pallet_contracts_proc_macro::define_env) macro for the /// emitted `Env` struct. pub trait Environment { @@ -45,14 +61,15 @@ pub trait Environment { fn define( store: &mut Store, linker: &mut Linker, - allow_unstable: bool, + allow_unstable: AllowUnstableInterface, + allow_deprecated: AllowDeprecatedInterface, ) -> Result<(), LinkerError>; } /// Type of a storage key. #[allow(dead_code)] enum KeyType { - /// Deprecated fix sized key `[u8;32]`. + /// Legacy fix sized key `[u8;32]`. Fix, /// Variable sized key used in transparent hashing, /// cannot be larger than MaxStorageKeyLen. @@ -91,12 +108,8 @@ pub enum ReturnCode { CalleeReverted = 2, /// The passed key does not exist in storage. KeyNotFound = 3, - /// Deprecated and no longer returned: There is only the minimum balance. - _BelowSubsistenceThreshold = 4, /// See [`Error::TransferFailed`]. TransferFailed = 5, - /// Deprecated and no longer returned: Endowment is no longer required. - _EndowmentTooLow = 6, /// No code could be found at the supplied code hash. CodeNotFound = 7, /// The contract that was called is no contract (a plain account). @@ -1280,7 +1293,7 @@ pub mod env { /// Make a call to another contract. /// - /// # Deprecation + /// # New version available /// /// This is equivalent to calling the newer version of this function with /// `flags` set to `ALLOW_REENTRY`. See the newer version for documentation. @@ -1418,7 +1431,7 @@ pub mod env { /// Instantiate a contract with the specified code hash. /// - /// # Deprecation + /// # New version available /// /// This is equivalent to calling the newer version of this function. The newer version /// drops the now unnecessary length fields. @@ -1538,7 +1551,7 @@ pub mod env { /// Remove the calling account and transfer remaining balance. /// - /// # Deprecation + /// # New version available /// /// This is equivalent to calling the newer version of this function. The newer version /// drops the now unnecessary length fields. @@ -1879,12 +1892,8 @@ pub mod env { /// space at `out_ptr` is less than the size of the value a trap is triggered. /// /// The data is encoded as `T::Hash`. - /// - /// # Deprecation - /// - /// This function is deprecated. Users should migrate to the [`super::seal1::Api::random()`] - /// version. #[prefixed_alias] + #[deprecated] fn random( ctx: _, memory: _, @@ -1931,6 +1940,7 @@ pub mod env { /// commitment. #[version(1)] #[prefixed_alias] + #[deprecated] fn random( ctx: _, memory: _, @@ -2001,10 +2011,11 @@ pub mod env { /// `out_ptr`. This call overwrites it with the size of the value. If the available /// space at `out_ptr` is less than the size of the value a trap is triggered. /// - /// # Deprecation + /// # Note /// /// There is no longer a tombstone deposit. This function always returns `0`. #[prefixed_alias] + #[deprecated] fn tombstone_deposit( ctx: _, memory: _, @@ -2030,6 +2041,7 @@ pub mod env { /// The state rent functionality was removed. This is stub only exists for /// backwards compatiblity #[prefixed_alias] + #[deprecated] fn restore_to( ctx: _, memory: _, @@ -2054,6 +2066,7 @@ pub mod env { /// backwards compatiblity #[version(1)] #[prefixed_alias] + #[deprecated] fn restore_to( ctx: _, memory: _, @@ -2067,49 +2080,6 @@ pub mod env { Ok(()) } - /// Deposit a contract event with the data buffer and optional list of topics. There is a limit - /// on the maximum number of topics specified by `event_topics`. - /// - /// - `topics_ptr`: a pointer to the buffer of topics encoded as `Vec`. The value of - /// this is ignored if `topics_len` is set to `0`. The topics list can't contain duplicates. - /// - `topics_len`: the length of the topics buffer. Pass 0 if you want to pass an empty - /// vector. - /// - `data_ptr`: a pointer to a raw data buffer which will saved along the event. - /// - `data_len`: the length of the data buffer. - #[prefixed_alias] - fn deposit_event( - ctx: _, - memory: _, - topics_ptr: u32, - topics_len: u32, - data_ptr: u32, - data_len: u32, - ) -> Result<(), TrapReason> { - let num_topic = topics_len - .checked_div(sp_std::mem::size_of::>() as u32) - .ok_or("Zero sized topics are not allowed")?; - ctx.charge_gas(RuntimeCosts::DepositEvent { num_topic, len: data_len })?; - if data_len > ctx.ext.max_value_size() { - return Err(Error::::ValueTooLarge.into()) - } - - let topics: Vec::T>> = match topics_len { - 0 => Vec::new(), - _ => ctx.read_sandbox_memory_as_unbounded(memory, topics_ptr, topics_len)?, - }; - - // If there are more than `event_topics`, then trap. - if topics.len() > ctx.ext.schedule().limits.event_topics as usize { - return Err(Error::::TooManyTopics.into()) - } - - let event_data = ctx.read_sandbox_memory(memory, data_ptr, data_len)?; - - ctx.ext.deposit_event(topics, event_data); - - Ok(()) - } - /// Was used to set rent allowance of the contract. /// /// # Note @@ -2117,6 +2087,7 @@ pub mod env { /// The state rent functionality was removed. This is stub only exists for /// backwards compatiblity. #[prefixed_alias] + #[deprecated] fn set_rent_allowance( ctx: _, memory: _, @@ -2135,6 +2106,7 @@ pub mod env { /// backwards compatiblity. #[version(1)] #[prefixed_alias] + #[deprecated] fn set_rent_allowance(ctx: _, _memory: _, _value_ptr: u32) -> Result<(), TrapReason> { ctx.charge_gas(RuntimeCosts::DebugMessage)?; Ok(()) @@ -2147,6 +2119,7 @@ pub mod env { /// The state rent functionality was removed. This is stub only exists for /// backwards compatiblity. #[prefixed_alias] + #[deprecated] fn rent_allowance(ctx: _, memory: _, out_ptr: u32, out_len_ptr: u32) -> Result<(), TrapReason> { ctx.charge_gas(RuntimeCosts::Balance)?; let rent_allowance = >::max_value().encode(); @@ -2160,6 +2133,49 @@ pub mod env { )?) } + /// Deposit a contract event with the data buffer and optional list of topics. There is a limit + /// on the maximum number of topics specified by `event_topics`. + /// + /// - `topics_ptr`: a pointer to the buffer of topics encoded as `Vec`. The value of + /// this is ignored if `topics_len` is set to `0`. The topics list can't contain duplicates. + /// - `topics_len`: the length of the topics buffer. Pass 0 if you want to pass an empty + /// vector. + /// - `data_ptr`: a pointer to a raw data buffer which will saved along the event. + /// - `data_len`: the length of the data buffer. + #[prefixed_alias] + fn deposit_event( + ctx: _, + memory: _, + topics_ptr: u32, + topics_len: u32, + data_ptr: u32, + data_len: u32, + ) -> Result<(), TrapReason> { + let num_topic = topics_len + .checked_div(sp_std::mem::size_of::>() as u32) + .ok_or("Zero sized topics are not allowed")?; + ctx.charge_gas(RuntimeCosts::DepositEvent { num_topic, len: data_len })?; + if data_len > ctx.ext.max_value_size() { + return Err(Error::::ValueTooLarge.into()) + } + + let topics: Vec::T>> = match topics_len { + 0 => Vec::new(), + _ => ctx.read_sandbox_memory_as_unbounded(memory, topics_ptr, topics_len)?, + }; + + // If there are more than `event_topics`, then trap. + if topics.len() > ctx.ext.schedule().limits.event_topics as usize { + return Err(Error::::TooManyTopics.into()) + } + + let event_data = ctx.read_sandbox_memory(memory, data_ptr, data_len)?; + + ctx.ext.deposit_event(topics, event_data); + + Ok(()) + } + /// Stores the current block number of the current contract into the supplied buffer. /// /// The value is stored to linear memory at the address pointed to by `out_ptr`. From d74eab8801538ac1218d89833f572697c3fae77b Mon Sep 17 00:00:00 2001 From: Sasha Gryaznov Date: Wed, 25 Jan 2023 20:12:29 +0200 Subject: [PATCH 052/558] [contracts] Add upfront weight of merkle trie proofs for storage reading functions (#13236) * Add upfront weight of merkle trie proofs for storage reading functions * drive-by fixes --- frame/contracts/proc-macro/src/lib.rs | 17 ++++++++++------- frame/contracts/src/lib.rs | 2 +- frame/contracts/src/schedule.rs | 10 +++++----- 3 files changed, 16 insertions(+), 13 deletions(-) diff --git a/frame/contracts/proc-macro/src/lib.rs b/frame/contracts/proc-macro/src/lib.rs index eaf437c15..de7b9b881 100644 --- a/frame/contracts/proc-macro/src/lib.rs +++ b/frame/contracts/proc-macro/src/lib.rs @@ -124,21 +124,24 @@ fn format_weight(field: &Ident) -> TokenStream2 { quote_spanned! { field.span() => &if self.#field.ref_time() > 1_000_000_000 { format!( - "{:.1?} ms", - Fixed::saturating_from_rational(self.#field.ref_time(), 1_000_000_000).to_float() + "{:.1?} ms, {} bytes", + Fixed::saturating_from_rational(self.#field.ref_time(), 1_000_000_000).to_float(), + self.#field.proof_size() ) } else if self.#field.ref_time() > 1_000_000 { format!( - "{:.1?} µs", - Fixed::saturating_from_rational(self.#field.ref_time(), 1_000_000).to_float() + "{:.1?} µs, {} bytes", + Fixed::saturating_from_rational(self.#field.ref_time(), 1_000_000).to_float(), + self.#field.proof_size() ) } else if self.#field.ref_time() > 1_000 { format!( - "{:.1?} ns", - Fixed::saturating_from_rational(self.#field.ref_time(), 1_000).to_float() + "{:.1?} ns, {} bytes", + Fixed::saturating_from_rational(self.#field.ref_time(), 1_000).to_float(), + self.#field.proof_size() ) } else { - format!("{} ps", self.#field.ref_time()) + format!("{} ps, {} bytes", self.#field.ref_time(), self.#field.proof_size()) } } } diff --git a/frame/contracts/src/lib.rs b/frame/contracts/src/lib.rs index 84a72b701..f97b4b790 100644 --- a/frame/contracts/src/lib.rs +++ b/frame/contracts/src/lib.rs @@ -78,7 +78,7 @@ //! //! * [`ink`](https://github.com/paritytech/ink) is //! an [`eDSL`](https://wiki.haskell.org/Embedded_domain_specific_language) that enables writing -//! WebAssembly based smart contracts in the Rust programming language. This is a work in progress. +//! WebAssembly based smart contracts in the Rust programming language. #![cfg_attr(not(feature = "std"), no_std)] #![cfg_attr(feature = "runtime-benchmarks", recursion_limit = "512")] diff --git a/frame/contracts/src/schedule.rs b/frame/contracts/src/schedule.rs index eabd18c81..7e09d55b4 100644 --- a/frame/contracts/src/schedule.rs +++ b/frame/contracts/src/schedule.rs @@ -644,23 +644,23 @@ impl Default for HostFnWeights { 1 )), debug_message: to_weight!(cost_batched!(seal_debug_message)), - set_storage: to_weight!(cost_batched!(seal_set_storage)), + set_storage: to_weight!(cost_batched!(seal_set_storage), 1024u64), set_code_hash: to_weight!(cost_batched!(seal_set_code_hash)), set_storage_per_new_byte: to_weight!(cost_byte_batched!(seal_set_storage_per_new_kb)), set_storage_per_old_byte: to_weight!( cost_byte_batched!(seal_set_storage_per_old_kb), 1u64 ), - clear_storage: to_weight!(cost_batched!(seal_clear_storage)), + clear_storage: to_weight!(cost_batched!(seal_clear_storage), 1024u64), clear_storage_per_byte: to_weight!(cost_byte_batched!(seal_clear_storage_per_kb), 1u64), - contains_storage: to_weight!(cost_batched!(seal_contains_storage)), + contains_storage: to_weight!(cost_batched!(seal_contains_storage), 1024u64), contains_storage_per_byte: to_weight!( cost_byte_batched!(seal_contains_storage_per_kb), 1u64 ), - get_storage: to_weight!(cost_batched!(seal_get_storage)), + get_storage: to_weight!(cost_batched!(seal_get_storage), 1024u64), get_storage_per_byte: to_weight!(cost_byte_batched!(seal_get_storage_per_kb), 1u64), - take_storage: to_weight!(cost_batched!(seal_take_storage)), + take_storage: to_weight!(cost_batched!(seal_take_storage), 1024u64), take_storage_per_byte: to_weight!(cost_byte_batched!(seal_take_storage_per_kb), 1u64), transfer: to_weight!(cost_batched!(seal_transfer)), call: to_weight!(cost_batched!(seal_call)), From d22e68ccb67788a1f1ad5a044568abd3e0a2c904 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bastian=20K=C3=B6cher?= Date: Wed, 25 Jan 2023 23:49:03 +0100 Subject: [PATCH 053/558] CI: Rewrite `check-each-crate` in python (#13238) * CI: Rewrite `check-each-crate` in python This is a much better readable version of the script and it should also work on Macos and not siltently fail ;) * Fix dumb bugs and print everything to stderr * Don't buffer Python output * :facepalm: * :facepalm: :facepalm: * Use check all until we have more macos runners * Update scripts/ci/gitlab/check-each-crate.py Co-authored-by: Oliver Tale-Yazdi * Update scripts/ci/gitlab/pipeline/test.yml Co-authored-by: Vladimir Istyufeev Co-authored-by: Oliver Tale-Yazdi --- scripts/ci/gitlab/check-each-crate.py | 57 +++++++++++++++++++++++++++ scripts/ci/gitlab/check-each-crate.sh | 54 ------------------------- scripts/ci/gitlab/pipeline/test.yml | 5 ++- 3 files changed, 60 insertions(+), 56 deletions(-) create mode 100755 scripts/ci/gitlab/check-each-crate.py delete mode 100755 scripts/ci/gitlab/check-each-crate.sh diff --git a/scripts/ci/gitlab/check-each-crate.py b/scripts/ci/gitlab/check-each-crate.py new file mode 100755 index 000000000..adad4f5bd --- /dev/null +++ b/scripts/ci/gitlab/check-each-crate.py @@ -0,0 +1,57 @@ +#!/usr/bin/env python3 + +# A script that checks each workspace crate individually. +# It's relevant to check workspace crates individually because otherwise their compilation problems +# due to feature misconfigurations won't be caught, as exemplified by +# https://github.com/paritytech/substrate/issues/12705 +# +# `check-each-crate.py target_group groups_total` +# +# - `target_group`: Integer starting from 1, the group this script should execute. +# - `groups_total`: Integer starting from 1, total number of groups. + +import subprocess, sys + +# Get all crates +output = subprocess.check_output(["cargo", "tree", "--locked", "--workspace", "--depth", "0", "--prefix", "none"]) + +# Convert the output into a proper list +crates = [] +for line in output.splitlines(): + if line != b"": + crates.append(line.decode('utf8').split(" ")[0]) + +# Make the list unique and sorted +crates = list(set(crates)) +crates.sort() + +target_group = int(sys.argv[1]) - 1 +groups_total = int(sys.argv[2]) + +if len(crates) == 0: + print("No crates detected!", file=sys.stderr) + sys.exit(1) + +print(f"Total crates: {len(crates)}", file=sys.stderr) + +crates_per_group = len(crates) // groups_total + +# If this is the last runner, we need to take care of crates +# after the group that we lost because of the integer division. +if target_group + 1 == groups_total: + overflow_crates = len(crates) % groups_total +else: + overflow_crates = 0 + +print(f"Crates per group: {crates_per_group}", file=sys.stderr) + +# Check each crate +for i in range(0, crates_per_group + overflow_crates): + crate = crates_per_group * target_group + i + + print(f"Checking {crates[crate]}", file=sys.stderr) + + res = subprocess.run(["cargo", "check", "--locked", "-p", crates[crate]]) + + if res.returncode != 0: + sys.exit(1) diff --git a/scripts/ci/gitlab/check-each-crate.sh b/scripts/ci/gitlab/check-each-crate.sh deleted file mode 100755 index 24cad6700..000000000 --- a/scripts/ci/gitlab/check-each-crate.sh +++ /dev/null @@ -1,54 +0,0 @@ -#!/usr/bin/env bash - -## A script that checks each workspace crate individually. -## It's relevant to check workspace crates individually because otherwise their compilation problems -## due to feature misconfigurations won't be caught, as exemplified by -## https://github.com/paritytech/substrate/issues/12705 - -set -Eeu -o pipefail -shopt -s inherit_errexit - -set -vx - -target_group="$1" -groups_total="$2" - -readarray -t workspace_crates < <(\ - cargo tree --workspace --depth 0 --prefix none | - awk '{ if (length($1) == 0 || substr($1, 1, 1) == "[") { skip } else { print $1 } }' | - sort | - uniq -) - -crates_total=${#workspace_crates[*]} -if [ "$crates_total" -lt 1 ]; then - >&2 echo "No crates detected for $PWD" - exit 1 -fi - -if [ "$crates_total" -lt "$groups_total" ]; then - # `crates_total / groups_total` would result in 0, so round it up to 1 - crates_per_group=1 -else - # We add `crates_total % groups_total > 0` (which evaluates to 1 in case there's a remainder for - # `crates_total % groups_total`) to round UP `crates_total / groups_total` 's - # potentially-fractional result to the nearest integer. Doing that ensures that we'll not miss any - # crate in case `crates_total / groups_total` would normally result in a fractional number, since - # in those cases Bash would round DOWN the result to the nearest integer. For example, if - # `crates_total = 5` and `groups_total = 2`, then `crates_total / groups_total` would round down - # to 2; since the loop below would then step by 2, we'd miss the 5th crate. - crates_per_group=$(( (crates_total / groups_total) + (crates_total % groups_total > 0) )) -fi - -group=1 -for ((i=0; i < crates_total; i += crates_per_group)); do - if [ $group -eq "$target_group" ]; then - crates_in_group=("${workspace_crates[@]:$i:$crates_per_group}") - echo "crates in the group: ${crates_in_group[*]}" >/dev/null # >/dev/null due to "set -x" - for crate in "${crates_in_group[@]}"; do - cargo check --locked --release -p "$crate" - done - break - fi - group=$(( group + 1 )) -done diff --git a/scripts/ci/gitlab/pipeline/test.yml b/scripts/ci/gitlab/pipeline/test.yml index f00857ffa..02e05752f 100644 --- a/scripts/ci/gitlab/pipeline/test.yml +++ b/scripts/ci/gitlab/pipeline/test.yml @@ -411,7 +411,7 @@ cargo-check-each-crate: CI_JOB_NAME: cargo-check-each-crate script: - rusty-cachier snapshot create - - time ./scripts/ci/gitlab/check-each-crate.sh "$CI_NODE_INDEX" "$CI_NODE_TOTAL" + - PYTHONUNBUFFERED=x time ./scripts/ci/gitlab/check-each-crate.py "$CI_NODE_INDEX" "$CI_NODE_TOTAL" # need to update cache only from one job - if [ "$CI_NODE_INDEX" == 1 ]; then rusty-cachier cache upload; fi parallel: 2 @@ -449,6 +449,7 @@ cargo-check-each-crate-macos: script: # TODO: enable rusty-cachier once it supports Mac # TODO: use parallel jobs, as per cargo-check-each-crate, once more Mac runners are available - - time ./scripts/ci/gitlab/check-each-crate.sh 1 1 + # - time ./scripts/ci/gitlab/check-each-crate.py 1 1 + - time cargo check --workspace --locked tags: - osx From d02922585673cb626df11a39927502d2ba040ff9 Mon Sep 17 00:00:00 2001 From: zjb0807 Date: Thu, 26 Jan 2023 12:12:19 +1300 Subject: [PATCH 054/558] Fix try-runtime with create-snapshot (#13223) * Fix try-runtime with create-snapshot * Apply review suggestions --- .../frame/try-runtime/cli/src/commands/create_snapshot.rs | 2 +- utils/frame/try-runtime/cli/src/commands/execute_block.rs | 2 +- utils/frame/try-runtime/cli/src/commands/follow_chain.rs | 2 +- .../frame/try-runtime/cli/src/commands/offchain_worker.rs | 2 +- .../try-runtime/cli/src/commands/on_runtime_upgrade.rs | 2 +- utils/frame/try-runtime/cli/src/lib.rs | 7 +++++-- 6 files changed, 10 insertions(+), 7 deletions(-) diff --git a/utils/frame/try-runtime/cli/src/commands/create_snapshot.rs b/utils/frame/try-runtime/cli/src/commands/create_snapshot.rs index ef39c3d98..8d6144f84 100644 --- a/utils/frame/try-runtime/cli/src/commands/create_snapshot.rs +++ b/utils/frame/try-runtime/cli/src/commands/create_snapshot.rs @@ -71,7 +71,7 @@ where let executor = build_executor::(&shared); let _ = State::Live(command.from) - .into_ext::(&shared, &executor, Some(path.into())) + .into_ext::(&shared, &executor, Some(path.into()), false) .await?; Ok(()) diff --git a/utils/frame/try-runtime/cli/src/commands/execute_block.rs b/utils/frame/try-runtime/cli/src/commands/execute_block.rs index ee5e21af5..e054c8ab8 100644 --- a/utils/frame/try-runtime/cli/src/commands/execute_block.rs +++ b/utils/frame/try-runtime/cli/src/commands/execute_block.rs @@ -99,7 +99,7 @@ where HostFns: HostFunctions, { let executor = build_executor::(&shared); - let ext = command.state.into_ext::(&shared, &executor, None).await?; + let ext = command.state.into_ext::(&shared, &executor, None, true).await?; // get the block number associated with this block. let block_ws_uri = command.block_ws_uri::(); diff --git a/utils/frame/try-runtime/cli/src/commands/follow_chain.rs b/utils/frame/try-runtime/cli/src/commands/follow_chain.rs index e4f166fe7..d887757b1 100644 --- a/utils/frame/try-runtime/cli/src/commands/follow_chain.rs +++ b/utils/frame/try-runtime/cli/src/commands/follow_chain.rs @@ -137,7 +137,7 @@ where pallet: vec![], child_tree: true, }); - let ext = state.into_ext::(&shared, &executor, None).await?; + let ext = state.into_ext::(&shared, &executor, None, true).await?; maybe_state_ext = Some(ext); } diff --git a/utils/frame/try-runtime/cli/src/commands/offchain_worker.rs b/utils/frame/try-runtime/cli/src/commands/offchain_worker.rs index c55de7da6..985b04bfb 100644 --- a/utils/frame/try-runtime/cli/src/commands/offchain_worker.rs +++ b/utils/frame/try-runtime/cli/src/commands/offchain_worker.rs @@ -78,7 +78,7 @@ where { let executor = build_executor(&shared); // we first build the externalities with the remote code. - let ext = command.state.into_ext::(&shared, &executor, None).await?; + let ext = command.state.into_ext::(&shared, &executor, None, true).await?; let header_ws_uri = command.header_ws_uri::(); diff --git a/utils/frame/try-runtime/cli/src/commands/on_runtime_upgrade.rs b/utils/frame/try-runtime/cli/src/commands/on_runtime_upgrade.rs index 81f81d6a5..61b5847ed 100644 --- a/utils/frame/try-runtime/cli/src/commands/on_runtime_upgrade.rs +++ b/utils/frame/try-runtime/cli/src/commands/on_runtime_upgrade.rs @@ -62,7 +62,7 @@ where HostFns: HostFunctions, { let executor = build_executor(&shared); - let ext = command.state.into_ext::(&shared, &executor, None).await?; + let ext = command.state.into_ext::(&shared, &executor, None, true).await?; let (_, encoded_result) = state_machine_call_with_proof::( &ext, diff --git a/utils/frame/try-runtime/cli/src/lib.rs b/utils/frame/try-runtime/cli/src/lib.rs index aac2ad423..dea20febb 100644 --- a/utils/frame/try-runtime/cli/src/lib.rs +++ b/utils/frame/try-runtime/cli/src/lib.rs @@ -609,6 +609,7 @@ impl State { shared: &SharedParams, executor: &WasmExecutor, state_snapshot: Option, + try_runtime_check: bool, ) -> sc_cli::Result> where Block::Hash: FromStr, @@ -707,8 +708,10 @@ impl State { } // whatever runtime we have in store now must have been compiled with try-runtime feature. - if !ensure_try_runtime::(&executor, &mut ext) { - return Err("given runtime is NOT compiled with try-runtime feature!".into()) + if try_runtime_check { + if !ensure_try_runtime::(&executor, &mut ext) { + return Err("given runtime is NOT compiled with try-runtime feature!".into()) + } } Ok(ext) From 8b4331c27bba6d9f178b8be093e39153350b9be8 Mon Sep 17 00:00:00 2001 From: Kian Paimani <5588131+kianenigma@users.noreply.github.com> Date: Wed, 25 Jan 2023 20:59:19 -0300 Subject: [PATCH 055/558] reduce exec time of fast-unstake benchmarks (#13120) * reduce exec time of fast-unstake benchmarks * fix test * fmt * fix patch the tests * fix patch the tests * ".git/.scripts/commands/bench/bench.sh" pallet dev pallet_fast_unstake * add batch size as well * update some benches to be better * fix one last test * fix * ".git/.scripts/commands/bench/bench.sh" pallet dev pallet_fast_unstake * reduce time even more * ".git/.scripts/commands/bench/bench.sh" pallet dev pallet_fast_unstake * fix tests * nit * remove * improve the weight calc further * ".git/.scripts/commands/bench/bench.sh" pallet dev pallet_fast_unstake * fix benchmarks * ".git/.scripts/commands/bench/bench.sh" pallet dev pallet_fast_unstake * update * fix * fix * fmt * ".git/.scripts/commands/bench/bench.sh" pallet dev pallet_fast_unstake * ".git/.scripts/commands/bench/bench.sh" pallet dev pallet_fast_unstake * lots of changes again... * smaller input * update * fmt * ".git/.scripts/commands/bench/bench.sh" pallet dev pallet_fast_unstake * cleanup * small simplification * fmt * reduce exec time a bit * fix * ".git/.scripts/commands/bench/bench.sh" pallet dev pallet_fast_unstake * test * ".git/.scripts/commands/bench/bench.sh" pallet dev pallet_fast_unstake * increase again * ".git/.scripts/commands/bench/bench.sh" pallet dev pallet_fast_unstake * review comments * fmt * fix * ".git/.scripts/commands/bench/bench.sh" pallet dev pallet_fast_unstake Co-authored-by: command-bot <> --- bin/node/runtime/src/lib.rs | 5 +- frame/fast-unstake/src/benchmarking.rs | 38 +++--- frame/fast-unstake/src/lib.rs | 126 ++++++++++++------ frame/fast-unstake/src/mock.rs | 3 + frame/fast-unstake/src/tests.rs | 173 ++++++------------------- frame/fast-unstake/src/weights.rs | 155 ++++++++++++---------- 6 files changed, 237 insertions(+), 263 deletions(-) diff --git a/bin/node/runtime/src/lib.rs b/bin/node/runtime/src/lib.rs index 0ac6ade91..1f85d118e 100644 --- a/bin/node/runtime/src/lib.rs +++ b/bin/node/runtime/src/lib.rs @@ -591,10 +591,13 @@ impl pallet_staking::Config for Runtime { impl pallet_fast_unstake::Config for Runtime { type RuntimeEvent = RuntimeEvent; type ControlOrigin = frame_system::EnsureRoot; - type BatchSize = ConstU32<128>; + type BatchSize = ConstU32<64>; type Deposit = ConstU128<{ DOLLARS }>; type Currency = Balances; type Staking = Staking; + type MaxErasToCheckPerBlock = ConstU32<1>; + #[cfg(feature = "runtime-benchmarks")] + type MaxBackersPerValidator = MaxNominatorRewardedPerValidator; type WeightInfo = (); } diff --git a/frame/fast-unstake/src/benchmarking.rs b/frame/fast-unstake/src/benchmarking.rs index b4a5e21dc..de85502f7 100644 --- a/frame/fast-unstake/src/benchmarking.rs +++ b/frame/fast-unstake/src/benchmarking.rs @@ -31,13 +31,11 @@ use sp_staking::{EraIndex, StakingInterface}; use sp_std::prelude::*; const USER_SEED: u32 = 0; -const DEFAULT_BACKER_PER_VALIDATOR: u32 = 128; -const MAX_VALIDATORS: u32 = 128; type CurrencyOf = ::Currency; -fn create_unexposed_nominators() -> Vec { - (0..T::BatchSize::get()) +fn create_unexposed_batch(batch_size: u32) -> Vec { + (0..batch_size) .map(|i| { let account = frame_benchmarking::account::("unexposed_nominator", i, USER_SEED); @@ -76,7 +74,7 @@ fn setup_staking(v: u32, until: EraIndex) { .collect::>(); for era in 0..=until { - let others = (0..DEFAULT_BACKER_PER_VALIDATOR) + let others = (0..T::MaxBackersPerValidator::get()) .map(|s| { let who = frame_benchmarking::account::("nominator", era, s); let value = ed; @@ -97,8 +95,10 @@ fn on_idle_full_block() { benchmarks! { // on_idle, we don't check anyone, but fully unbond them. on_idle_unstake { + let b in 1 .. T::BatchSize::get(); + ErasToCheckPerBlock::::put(1); - for who in create_unexposed_nominators::() { + for who in create_unexposed_batch::(b).into_iter() { assert_ok!(FastUnstake::::register_fast_unstake( RawOrigin::Signed(who.clone()).into(), )); @@ -114,7 +114,7 @@ benchmarks! { checked, stashes, .. - }) if checked.len() == 1 && stashes.len() as u32 == T::BatchSize::get() + }) if checked.len() == 1 && stashes.len() as u32 == b )); } : { @@ -123,25 +123,23 @@ benchmarks! { verify { assert!(matches!( fast_unstake_events::().last(), - Some(Event::BatchFinished) + Some(Event::BatchFinished { size: b }) )); } - // on_idle, when we check some number of eras, + // on_idle, when we check some number of eras and the queue is already set. on_idle_check { - // number of eras multiplied by validators in that era. - let x in (T::Staking::bonding_duration() * 1) .. (T::Staking::bonding_duration() * MAX_VALIDATORS); - - let u = T::Staking::bonding_duration(); - let v = x / u; + let v in 1 .. 256; + let b in 1 .. T::BatchSize::get(); + let u = T::MaxErasToCheckPerBlock::get().min(T::Staking::bonding_duration()); ErasToCheckPerBlock::::put(u); T::Staking::set_current_era(u); - // setup staking with v validators and u eras of data (0..=u) + // setup staking with v validators and u eras of data (0..=u+1) setup_staking::(v, u); - let stashes = create_unexposed_nominators::().into_iter().map(|s| { + let stashes = create_unexposed_batch::(b).into_iter().map(|s| { assert_ok!(FastUnstake::::register_fast_unstake( RawOrigin::Signed(s.clone()).into(), )); @@ -150,6 +148,8 @@ benchmarks! { // no one is queued thus far. assert_eq!(Head::::get(), None); + + Head::::put(UnstakeRequest { stashes: stashes.clone().try_into().unwrap(), checked: Default::default() }); } : { on_idle_full_block::(); @@ -167,7 +167,7 @@ benchmarks! { register_fast_unstake { ErasToCheckPerBlock::::put(1); - let who = create_unexposed_nominators::().get(0).cloned().unwrap(); + let who = create_unexposed_batch::(1).get(0).cloned().unwrap(); whitelist_account!(who); assert_eq!(Queue::::count(), 0); @@ -179,7 +179,7 @@ benchmarks! { deregister { ErasToCheckPerBlock::::put(1); - let who = create_unexposed_nominators::().get(0).cloned().unwrap(); + let who = create_unexposed_batch::(1).get(0).cloned().unwrap(); assert_ok!(FastUnstake::::register_fast_unstake( RawOrigin::Signed(who.clone()).into(), )); @@ -194,7 +194,7 @@ benchmarks! { control { let origin = ::ControlOrigin::successful_origin(); } - : _(origin, 128) + : _(origin, T::MaxErasToCheckPerBlock::get()) verify {} impl_benchmark_test_suite!(Pallet, crate::mock::ExtBuilder::default().build(), crate::mock::Runtime) diff --git a/frame/fast-unstake/src/lib.rs b/frame/fast-unstake/src/lib.rs index c0bffc442..82454a4b8 100644 --- a/frame/fast-unstake/src/lib.rs +++ b/frame/fast-unstake/src/lib.rs @@ -86,12 +86,9 @@ pub mod pallet { traits::{Defensive, ReservableCurrency, StorageVersion}, }; use frame_system::pallet_prelude::*; - use sp_runtime::{ - traits::{Saturating, Zero}, - DispatchResult, - }; + use sp_runtime::{traits::Zero, DispatchResult}; use sp_staking::{EraIndex, StakingInterface}; - use sp_std::{collections::btree_set::BTreeSet, prelude::*, vec::Vec}; + use sp_std::{prelude::*, vec::Vec}; pub use weights::WeightInfo; #[derive(scale_info::TypeInfo, codec::Encode, codec::Decode, codec::MaxEncodedLen)] @@ -138,6 +135,14 @@ pub mod pallet { /// The weight information of this pallet. type WeightInfo: WeightInfo; + + /// Maximum value for `ErasToCheckPerBlock`. This should be as close as possible, but more + /// than the actual value, in order to have accurate benchmarks. + type MaxErasToCheckPerBlock: Get; + + /// Use only for benchmarking. + #[cfg(feature = "runtime-benchmarks")] + type MaxBackersPerValidator: Get; } /// The current "head of the queue" being unstaked. @@ -173,11 +178,11 @@ pub mod pallet { InternalError, /// A batch was partially checked for the given eras, but the process did not finish. BatchChecked { eras: Vec }, - /// A batch was terminated. + /// A batch of a given size was terminated. /// /// This is always follows by a number of `Unstaked` or `Slashed` events, marking the end /// of the batch. A new batch will be created upon next block. - BatchFinished, + BatchFinished { size: u32 }, } #[pallet::error] @@ -208,6 +213,31 @@ pub mod pallet { Self::do_on_idle(remaining_weight) } + + fn integrity_test() { + sp_std::if_std! { + sp_io::TestExternalities::new_empty().execute_with(|| { + // ensure that the value of `ErasToCheckPerBlock` is less than + // `T::MaxErasToCheckPerBlock`. + assert!( + ErasToCheckPerBlock::::get() <= T::MaxErasToCheckPerBlock::get(), + "the value of `ErasToCheckPerBlock` is greater than `T::MaxErasToCheckPerBlock`", + ); + }); + } + } + + #[cfg(feature = "try-runtime")] + fn try_state(_n: T::BlockNumber) -> Result<(), &'static str> { + // ensure that the value of `ErasToCheckPerBlock` is less than + // `T::MaxErasToCheckPerBlock`. + assert!( + ErasToCheckPerBlock::::get() <= T::MaxErasToCheckPerBlock::get(), + "the value of `ErasToCheckPerBlock` is greater than `T::MaxErasToCheckPerBlock`", + ); + + Ok(()) + } } #[pallet::call] @@ -288,9 +318,10 @@ pub mod pallet { /// Dispatch origin must be signed by the [`Config::ControlOrigin`]. #[pallet::call_index(2)] #[pallet::weight(::WeightInfo::control())] - pub fn control(origin: OriginFor, unchecked_eras_to_check: EraIndex) -> DispatchResult { + pub fn control(origin: OriginFor, eras_to_check: EraIndex) -> DispatchResult { let _ = T::ControlOrigin::ensure_origin(origin)?; - ErasToCheckPerBlock::::put(unchecked_eras_to_check); + ensure!(eras_to_check <= T::MaxErasToCheckPerBlock::get(), Error::::CallNotAllowed); + ErasToCheckPerBlock::::put(eras_to_check); Ok(()) } } @@ -324,27 +355,38 @@ pub mod pallet { /// found out to have no eras to check. At the end of a check cycle, even if they are fully /// checked, we don't finish the process. pub(crate) fn do_on_idle(remaining_weight: Weight) -> Weight { - let mut eras_to_check_per_block = ErasToCheckPerBlock::::get(); + // any weight that is unaccounted for + let mut unaccounted_weight = Weight::from_ref_time(0); + + let eras_to_check_per_block = ErasToCheckPerBlock::::get(); if eras_to_check_per_block.is_zero() { - return T::DbWeight::get().reads(1) + return T::DbWeight::get().reads(1).saturating_add(unaccounted_weight) } // NOTE: here we're assuming that the number of validators has only ever increased, // meaning that the number of exposures to check is either this per era, or less. let validator_count = T::Staking::desired_validator_count(); + let (next_batch_size, reads_from_queue) = Head::::get() + .map_or((Queue::::count().min(T::BatchSize::get()), true), |head| { + (head.stashes.len() as u32, false) + }); // determine the number of eras to check. This is based on both `ErasToCheckPerBlock` // and `remaining_weight` passed on to us from the runtime executive. - let max_weight = |v, u| { - ::WeightInfo::on_idle_check(v * u) - .max(::WeightInfo::on_idle_unstake()) + let max_weight = |v, b| { + // NOTE: this potentially under-counts by up to `BatchSize` reads from the queue. + ::WeightInfo::on_idle_check(v, b) + .max(::WeightInfo::on_idle_unstake(b)) + .saturating_add(if reads_from_queue { + T::DbWeight::get().reads(next_batch_size.into()) + } else { + Zero::zero() + }) }; - while max_weight(validator_count, eras_to_check_per_block).any_gt(remaining_weight) { - eras_to_check_per_block.saturating_dec(); - if eras_to_check_per_block.is_zero() { - log!(debug, "early existing because eras_to_check_per_block is zero"); - return T::DbWeight::get().reads(2) - } + + if max_weight(validator_count, next_batch_size).any_gt(remaining_weight) { + log!(debug, "early exit because eras_to_check_per_block is zero"); + return T::DbWeight::get().reads(3).saturating_add(unaccounted_weight) } if T::Staking::election_ongoing() { @@ -352,7 +394,7 @@ pub mod pallet { // there is an ongoing election -- we better not do anything. Imagine someone is not // exposed anywhere in the last era, and the snapshot for the election is already // taken. In this time period, we don't want to accidentally unstake them. - return T::DbWeight::get().reads(2) + return T::DbWeight::get().reads(4).saturating_add(unaccounted_weight) } let UnstakeRequest { stashes, mut checked } = match Head::::take().or_else(|| { @@ -362,6 +404,9 @@ pub mod pallet { .collect::>() .try_into() .expect("take ensures bound is met; qed"); + unaccounted_weight.saturating_accrue( + T::DbWeight::get().reads_writes(stashes.len() as u64, stashes.len() as u64), + ); if stashes.is_empty() { None } else { @@ -377,10 +422,11 @@ pub mod pallet { log!( debug, - "checking {:?} stashes, eras_to_check_per_block = {:?}, remaining_weight = {:?}", + "checking {:?} stashes, eras_to_check_per_block = {:?}, checked {:?}, remaining_weight = {:?}", stashes.len(), eras_to_check_per_block, - remaining_weight + checked, + remaining_weight, ); // the range that we're allowed to check in this round. @@ -431,11 +477,10 @@ pub mod pallet { } }; - let check_stash = |stash, deposit, eras_checked: &mut BTreeSet| { - let is_exposed = unchecked_eras_to_check.iter().any(|e| { - eras_checked.insert(*e); - T::Staking::is_exposed_in_era(&stash, e) - }); + let check_stash = |stash, deposit| { + let is_exposed = unchecked_eras_to_check + .iter() + .any(|e| T::Staking::is_exposed_in_era(&stash, e)); if is_exposed { T::Currency::slash_reserved(&stash, deposit); @@ -448,20 +493,16 @@ pub mod pallet { }; if unchecked_eras_to_check.is_empty() { - // `stash` is not exposed in any era now -- we can let go of them now. + // `stashes` are not exposed in any era now -- we can let go of them now. + let size = stashes.len() as u32; stashes.into_iter().for_each(|(stash, deposit)| unstake_stash(stash, deposit)); - Self::deposit_event(Event::::BatchFinished); - ::WeightInfo::on_idle_unstake() + Self::deposit_event(Event::::BatchFinished { size }); + ::WeightInfo::on_idle_unstake(size).saturating_add(unaccounted_weight) } else { - // eras checked so far. - let mut eras_checked = BTreeSet::::new(); - let pre_length = stashes.len(); let stashes: BoundedVec<(T::AccountId, BalanceOf), T::BatchSize> = stashes .into_iter() - .filter(|(stash, deposit)| { - check_stash(stash.clone(), *deposit, &mut eras_checked) - }) + .filter(|(stash, deposit)| check_stash(stash.clone(), *deposit)) .collect::>() .try_into() .expect("filter can only lessen the length; still in bound; qed"); @@ -469,8 +510,8 @@ pub mod pallet { log!( debug, - "checked {:?} eras, pre stashes: {:?}, post: {:?}", - eras_checked.len(), + "checked {:?}, pre stashes: {:?}, post: {:?}", + unchecked_eras_to_check, pre_length, post_length, ); @@ -478,7 +519,7 @@ pub mod pallet { match checked.try_extend(unchecked_eras_to_check.clone().into_iter()) { Ok(_) => if stashes.is_empty() { - Self::deposit_event(Event::::BatchFinished); + Self::deposit_event(Event::::BatchFinished { size: 0 }); } else { Head::::put(UnstakeRequest { stashes, checked }); Self::deposit_event(Event::::BatchChecked { @@ -491,9 +532,8 @@ pub mod pallet { }, } - ::WeightInfo::on_idle_check( - validator_count * eras_checked.len() as u32, - ) + ::WeightInfo::on_idle_check(validator_count, pre_length as u32) + .saturating_add(unaccounted_weight) } } } diff --git a/frame/fast-unstake/src/mock.rs b/frame/fast-unstake/src/mock.rs index 3f974e5e1..38cf41fde 100644 --- a/frame/fast-unstake/src/mock.rs +++ b/frame/fast-unstake/src/mock.rs @@ -185,6 +185,9 @@ impl fast_unstake::Config for Runtime { type ControlOrigin = frame_system::EnsureRoot; type BatchSize = BatchSize; type WeightInfo = (); + type MaxErasToCheckPerBlock = ConstU32<16>; + #[cfg(feature = "runtime-benchmarks")] + type MaxBackersPerValidator = ConstU32<128>; } type Block = frame_system::mocking::MockBlock; diff --git a/frame/fast-unstake/src/tests.rs b/frame/fast-unstake/src/tests.rs index 522aa1d0f..3e62146ae 100644 --- a/frame/fast-unstake/src/tests.rs +++ b/frame/fast-unstake/src/tests.rs @@ -18,7 +18,7 @@ //! Tests for pallet-fast-unstake. use super::*; -use crate::{mock::*, types::*, weights::WeightInfo, Event}; +use crate::{mock::*, types::*, Event}; use frame_support::{assert_noop, assert_ok, bounded_vec, pallet_prelude::*, traits::Currency}; use pallet_staking::{CurrentEra, RewardDestination}; @@ -236,120 +236,6 @@ mod on_idle { }); } - #[test] - fn respects_weight() { - ExtBuilder::default().build_and_execute(|| { - // we want to check all eras in one block... - ErasToCheckPerBlock::::put(BondingDuration::get() + 1); - CurrentEra::::put(BondingDuration::get()); - - // given - assert_ok!(FastUnstake::register_fast_unstake(RuntimeOrigin::signed(2))); - assert_eq!(Queue::::get(1), Some(Deposit::get())); - - assert_eq!(Queue::::count(), 1); - assert_eq!(Head::::get(), None); - - // when: call fast unstake with not enough weight to process the whole thing, just one - // era. - let remaining_weight = ::WeightInfo::on_idle_check( - pallet_staking::ValidatorCount::::get() * 1, - ); - assert_eq!(FastUnstake::on_idle(0, remaining_weight), remaining_weight); - - // then - assert_eq!( - fast_unstake_events_since_last_call(), - vec![Event::BatchChecked { eras: vec![3] }] - ); - assert_eq!( - Head::::get(), - Some(UnstakeRequest { - stashes: bounded_vec![(1, Deposit::get())], - checked: bounded_vec![3] - }) - ); - - // when: another 1 era. - let remaining_weight = ::WeightInfo::on_idle_check( - pallet_staking::ValidatorCount::::get() * 1, - ); - assert_eq!(FastUnstake::on_idle(0, remaining_weight), remaining_weight); - - // then: - assert_eq!( - fast_unstake_events_since_last_call(), - vec![Event::BatchChecked { eras: bounded_vec![2] }] - ); - assert_eq!( - Head::::get(), - Some(UnstakeRequest { - stashes: bounded_vec![(1, Deposit::get())], - checked: bounded_vec![3, 2] - }) - ); - - // when: then 5 eras, we only need 2 more. - let remaining_weight = ::WeightInfo::on_idle_check( - pallet_staking::ValidatorCount::::get() * 5, - ); - assert_eq!( - FastUnstake::on_idle(0, remaining_weight), - // note the amount of weight consumed: 2 eras worth of weight. - ::WeightInfo::on_idle_check( - pallet_staking::ValidatorCount::::get() * 2, - ) - ); - - // then: - assert_eq!( - fast_unstake_events_since_last_call(), - vec![Event::BatchChecked { eras: vec![1, 0] }] - ); - assert_eq!( - Head::::get(), - Some(UnstakeRequest { - stashes: bounded_vec![(1, Deposit::get())], - checked: bounded_vec![3, 2, 1, 0] - }) - ); - - // when: not enough weight to unstake: - let remaining_weight = - ::WeightInfo::on_idle_unstake() - Weight::from_ref_time(1); - assert_eq!(FastUnstake::on_idle(0, remaining_weight), Weight::from_ref_time(0)); - - // then nothing happens: - assert_eq!(fast_unstake_events_since_last_call(), vec![]); - assert_eq!( - Head::::get(), - Some(UnstakeRequest { - stashes: bounded_vec![(1, Deposit::get())], - checked: bounded_vec![3, 2, 1, 0] - }) - ); - - // when: enough weight to get over at least one iteration: then we are unblocked and can - // unstake. - let remaining_weight = ::WeightInfo::on_idle_check( - pallet_staking::ValidatorCount::::get() * 1, - ); - assert_eq!( - FastUnstake::on_idle(0, remaining_weight), - ::WeightInfo::on_idle_unstake() - ); - - // then we finish the unbonding: - assert_eq!( - fast_unstake_events_since_last_call(), - vec![Event::Unstaked { stash: 1, result: Ok(()) }, Event::BatchFinished], - ); - assert_eq!(Head::::get(), None,); - - assert_unstaked(&1); - }); - } - #[test] fn if_head_not_set_one_random_fetched_from_queue() { ExtBuilder::default().build_and_execute(|| { @@ -410,7 +296,7 @@ mod on_idle { vec![ Event::BatchChecked { eras: vec![3, 2, 1, 0] }, Event::Unstaked { stash: 1, result: Ok(()) }, - Event::BatchFinished, + Event::BatchFinished { size: 1 }, Event::BatchChecked { eras: vec![3, 2, 1, 0] } ] ); @@ -458,10 +344,10 @@ mod on_idle { vec![ Event::BatchChecked { eras: vec![3, 2, 1, 0] }, Event::Unstaked { stash: 1, result: Ok(()) }, - Event::BatchFinished, + Event::BatchFinished { size: 1 }, Event::BatchChecked { eras: vec![3, 2, 1, 0] }, Event::Unstaked { stash: 3, result: Ok(()) }, - Event::BatchFinished, + Event::BatchFinished { size: 1 }, ] ); @@ -503,7 +389,7 @@ mod on_idle { vec![ Event::BatchChecked { eras: vec![3, 2, 1, 0] }, Event::Unstaked { stash: 1, result: Ok(()) }, - Event::BatchFinished + Event::BatchFinished { size: 1 } ] ); assert_unstaked(&1); @@ -545,7 +431,7 @@ mod on_idle { vec![ Event::BatchChecked { eras: vec![3, 2, 1, 0] }, Event::Unstaked { stash: 1, result: Ok(()) }, - Event::BatchFinished + Event::BatchFinished { size: 1 } ] ); assert_unstaked(&1); @@ -620,7 +506,7 @@ mod on_idle { Event::BatchChecked { eras: vec![1] }, Event::BatchChecked { eras: vec![0] }, Event::Unstaked { stash: 1, result: Ok(()) }, - Event::BatchFinished + Event::BatchFinished { size: 1 } ] ); assert_unstaked(&1); @@ -704,7 +590,7 @@ mod on_idle { Event::BatchChecked { eras: vec![0] }, Event::BatchChecked { eras: vec![4] }, Event::Unstaked { stash: 1, result: Ok(()) }, - Event::BatchFinished + Event::BatchFinished { size: 1 } ] ); assert_unstaked(&1); @@ -799,7 +685,7 @@ mod on_idle { Event::BatchChecked { eras: vec![4] }, Event::BatchChecked { eras: vec![1] }, Event::Unstaked { stash: 1, result: Ok(()) }, - Event::BatchFinished + Event::BatchFinished { size: 1 } ] ); @@ -843,7 +729,7 @@ mod on_idle { Event::BatchChecked { eras: vec![3] }, Event::BatchChecked { eras: vec![2] }, Event::Slashed { stash: exposed, amount: Deposit::get() }, - Event::BatchFinished + Event::BatchFinished { size: 0 } ] ); }); @@ -879,7 +765,7 @@ mod on_idle { vec![ Event::BatchChecked { eras: vec![3, 2] }, Event::Slashed { stash: exposed, amount: Deposit::get() }, - Event::BatchFinished + Event::BatchFinished { size: 0 } ] ); }); @@ -910,7 +796,10 @@ mod on_idle { assert_eq!( fast_unstake_events_since_last_call(), - vec![Event::Slashed { stash: 100, amount: Deposit::get() }, Event::BatchFinished] + vec![ + Event::Slashed { stash: 100, amount: Deposit::get() }, + Event::BatchFinished { size: 0 } + ] ); }); } @@ -946,7 +835,7 @@ mod on_idle { vec![ Event::BatchChecked { eras: vec![3, 2, 1, 0] }, Event::Unstaked { stash: 42, result: Ok(()) }, - Event::BatchFinished + Event::BatchFinished { size: 1 } ] ); }); @@ -1001,7 +890,7 @@ mod batched { Event::Unstaked { stash: 1, result: Ok(()) }, Event::Unstaked { stash: 5, result: Ok(()) }, Event::Unstaked { stash: 7, result: Ok(()) }, - Event::BatchFinished + Event::BatchFinished { size: 3 } ] ); }); @@ -1067,7 +956,7 @@ mod batched { Event::Unstaked { stash: 1, result: Ok(()) }, Event::Unstaked { stash: 5, result: Ok(()) }, Event::Unstaked { stash: 7, result: Ok(()) }, - Event::BatchFinished + Event::BatchFinished { size: 3 } ] ); }); @@ -1132,7 +1021,7 @@ mod batched { Event::BatchChecked { eras: vec![1, 0] }, Event::Unstaked { stash: 1, result: Ok(()) }, Event::Unstaked { stash: 3, result: Ok(()) }, - Event::BatchFinished + Event::BatchFinished { size: 2 } ] ); }); @@ -1191,10 +1080,32 @@ mod batched { Event::Slashed { stash: 666, amount: Deposit::get() }, Event::BatchChecked { eras: vec![3] }, Event::Slashed { stash: 667, amount: Deposit::get() }, - Event::BatchFinished, + Event::BatchFinished { size: 0 }, Event::BatchChecked { eras: vec![3] } ] ); }); } } + +#[test] +fn kusama_estimate() { + use crate::WeightInfo; + let block_time = frame_support::weights::Weight::from_ref_time( + frame_support::weights::constants::WEIGHT_REF_TIME_PER_SECOND * 2, + ) + .ref_time() as f32; + let on_idle = crate::weights::SubstrateWeight::::on_idle_check(1000, 64).ref_time() as f32; + dbg!(block_time, on_idle, on_idle / block_time); +} + +#[test] +fn polkadot_estimate() { + use crate::WeightInfo; + let block_time = frame_support::weights::Weight::from_ref_time( + frame_support::weights::constants::WEIGHT_REF_TIME_PER_SECOND * 2, + ) + .ref_time() as f32; + let on_idle = crate::weights::SubstrateWeight::::on_idle_check(300, 64).ref_time() as f32; + dbg!(block_time, on_idle, on_idle / block_time); +} diff --git a/frame/fast-unstake/src/weights.rs b/frame/fast-unstake/src/weights.rs index 6001250e8..f7d0cacba 100644 --- a/frame/fast-unstake/src/weights.rs +++ b/frame/fast-unstake/src/weights.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2022 Parity Technologies (UK) Ltd. +// Copyright (C) 2023 Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); @@ -18,24 +18,25 @@ //! Autogenerated weights for pallet_fast_unstake //! //! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev -//! DATE: 2022-11-07, STEPS: `50`, REPEAT: 20, LOW RANGE: `[]`, HIGH RANGE: `[]` -//! HOSTNAME: `bm2`, CPU: `Intel(R) Core(TM) i7-7700K CPU @ 4.20GHz` +//! DATE: 2023-01-25, STEPS: `50`, REPEAT: 20, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! HOSTNAME: `runner-b3zmxxc-project-145-concurrent-0`, CPU: `Intel(R) Xeon(R) CPU @ 2.60GHz` //! EXECUTION: Some(Wasm), WASM-EXECUTION: Compiled, CHAIN: Some("dev"), DB CACHE: 1024 // Executed Command: -// ./target/production/substrate +// target/production/substrate // benchmark // pallet -// --chain=dev // --steps=50 // --repeat=20 -// --pallet=pallet_fast_unstake // --extrinsic=* // --execution=wasm // --wasm-execution=compiled // --heap-pages=4096 -// --output=./frame/fast-unstake/src/weights.rs +// --json-file=/builds/parity/mirrors/substrate/.git/.artifacts/bench.json +// --pallet=pallet_fast_unstake +// --chain=dev // --header=./HEADER-APACHE2 +// --output=./frame/fast-unstake/src/weights.rs // --template=./.maintain/frame-weight-template.hbs #![cfg_attr(rustfmt, rustfmt_skip)] @@ -47,8 +48,8 @@ use sp_std::marker::PhantomData; /// Weight functions needed for pallet_fast_unstake. pub trait WeightInfo { - fn on_idle_unstake() -> Weight; - fn on_idle_check(x: u32, ) -> Weight; + fn on_idle_unstake(b: u32, ) -> Weight; + fn on_idle_check(v: u32, b: u32, ) -> Weight; fn register_fast_unstake() -> Weight; fn deregister() -> Weight; fn control() -> Weight; @@ -59,8 +60,9 @@ pub struct SubstrateWeight(PhantomData); impl WeightInfo for SubstrateWeight { // Storage: FastUnstake ErasToCheckPerBlock (r:1 w:0) // Storage: Staking ValidatorCount (r:1 w:0) - // Storage: ElectionProviderMultiPhase CurrentPhase (r:1 w:0) // Storage: FastUnstake Head (r:1 w:1) + // Storage: FastUnstake CounterForQueue (r:1 w:0) + // Storage: ElectionProviderMultiPhase CurrentPhase (r:1 w:0) // Storage: Staking CurrentEra (r:1 w:0) // Storage: Staking SlashingSpans (r:1 w:0) // Storage: Staking Bonded (r:1 w:1) @@ -70,29 +72,36 @@ impl WeightInfo for SubstrateWeight { // Storage: Balances Locks (r:1 w:1) // Storage: Staking Ledger (r:0 w:1) // Storage: Staking Payee (r:0 w:1) - fn on_idle_unstake() -> Weight { - // Minimum execution time: 82_426 nanoseconds. - Weight::from_ref_time(83_422_000 as u64) - .saturating_add(T::DbWeight::get().reads(11 as u64)) - .saturating_add(T::DbWeight::get().writes(6 as u64)) + /// The range of component `b` is `[1, 64]`. + fn on_idle_unstake(b: u32, ) -> Weight { + // Minimum execution time: 92_833 nanoseconds. + Weight::from_ref_time(62_136_346) + // Standard Error: 25_541 + .saturating_add(Weight::from_ref_time(42_904_859).saturating_mul(b.into())) + .saturating_add(T::DbWeight::get().reads(6)) + .saturating_add(T::DbWeight::get().reads((6_u64).saturating_mul(b.into()))) + .saturating_add(T::DbWeight::get().writes(1)) + .saturating_add(T::DbWeight::get().writes((5_u64).saturating_mul(b.into()))) } // Storage: FastUnstake ErasToCheckPerBlock (r:1 w:0) // Storage: Staking ValidatorCount (r:1 w:0) - // Storage: ElectionProviderMultiPhase CurrentPhase (r:1 w:0) // Storage: FastUnstake Head (r:1 w:1) - // Storage: FastUnstake Queue (r:2 w:1) - // Storage: FastUnstake CounterForQueue (r:1 w:1) + // Storage: FastUnstake CounterForQueue (r:1 w:0) + // Storage: ElectionProviderMultiPhase CurrentPhase (r:1 w:0) // Storage: Staking CurrentEra (r:1 w:0) - // Storage: Staking ErasStakers (r:1344 w:0) - /// The range of component `x` is `[672, 86016]`. - fn on_idle_check(x: u32, ) -> Weight { - // Minimum execution time: 13_932_777 nanoseconds. - Weight::from_ref_time(13_996_029_000 as u64) - // Standard Error: 16_878 - .saturating_add(Weight::from_ref_time(18_113_540 as u64).saturating_mul(x as u64)) - .saturating_add(T::DbWeight::get().reads(345 as u64)) - .saturating_add(T::DbWeight::get().reads((1 as u64).saturating_mul(x as u64))) - .saturating_add(T::DbWeight::get().writes(3 as u64)) + // Storage: Staking ErasStakers (r:2 w:0) + /// The range of component `v` is `[1, 256]`. + /// The range of component `b` is `[1, 64]`. + fn on_idle_check(v: u32, b: u32, ) -> Weight { + // Minimum execution time: 1_775_293 nanoseconds. + Weight::from_ref_time(1_787_133_000) + // Standard Error: 17_109_142 + .saturating_add(Weight::from_ref_time(546_766_552).saturating_mul(v.into())) + // Standard Error: 68_455_625 + .saturating_add(Weight::from_ref_time(2_135_980_830).saturating_mul(b.into())) + .saturating_add(T::DbWeight::get().reads(7)) + .saturating_add(T::DbWeight::get().reads((1_u64).saturating_mul(v.into()))) + .saturating_add(T::DbWeight::get().writes(1)) } // Storage: FastUnstake ErasToCheckPerBlock (r:1 w:0) // Storage: Staking Ledger (r:1 w:1) @@ -109,10 +118,10 @@ impl WeightInfo for SubstrateWeight { // Storage: Balances Locks (r:1 w:1) // Storage: FastUnstake CounterForQueue (r:1 w:1) fn register_fast_unstake() -> Weight { - // Minimum execution time: 120_190 nanoseconds. - Weight::from_ref_time(121_337_000 as u64) - .saturating_add(T::DbWeight::get().reads(14 as u64)) - .saturating_add(T::DbWeight::get().writes(9 as u64)) + // Minimum execution time: 124_849 nanoseconds. + Weight::from_ref_time(128_176_000) + .saturating_add(T::DbWeight::get().reads(14)) + .saturating_add(T::DbWeight::get().writes(9)) } // Storage: FastUnstake ErasToCheckPerBlock (r:1 w:0) // Storage: Staking Ledger (r:1 w:0) @@ -120,16 +129,16 @@ impl WeightInfo for SubstrateWeight { // Storage: FastUnstake Head (r:1 w:0) // Storage: FastUnstake CounterForQueue (r:1 w:1) fn deregister() -> Weight { - // Minimum execution time: 49_897 nanoseconds. - Weight::from_ref_time(50_080_000 as u64) - .saturating_add(T::DbWeight::get().reads(5 as u64)) - .saturating_add(T::DbWeight::get().writes(2 as u64)) + // Minimum execution time: 48_246 nanoseconds. + Weight::from_ref_time(49_720_000) + .saturating_add(T::DbWeight::get().reads(5)) + .saturating_add(T::DbWeight::get().writes(2)) } // Storage: FastUnstake ErasToCheckPerBlock (r:0 w:1) fn control() -> Weight { - // Minimum execution time: 4_814 nanoseconds. - Weight::from_ref_time(4_997_000 as u64) - .saturating_add(T::DbWeight::get().writes(1 as u64)) + // Minimum execution time: 4_611 nanoseconds. + Weight::from_ref_time(4_844_000) + .saturating_add(T::DbWeight::get().writes(1)) } } @@ -137,8 +146,9 @@ impl WeightInfo for SubstrateWeight { impl WeightInfo for () { // Storage: FastUnstake ErasToCheckPerBlock (r:1 w:0) // Storage: Staking ValidatorCount (r:1 w:0) - // Storage: ElectionProviderMultiPhase CurrentPhase (r:1 w:0) // Storage: FastUnstake Head (r:1 w:1) + // Storage: FastUnstake CounterForQueue (r:1 w:0) + // Storage: ElectionProviderMultiPhase CurrentPhase (r:1 w:0) // Storage: Staking CurrentEra (r:1 w:0) // Storage: Staking SlashingSpans (r:1 w:0) // Storage: Staking Bonded (r:1 w:1) @@ -148,29 +158,36 @@ impl WeightInfo for () { // Storage: Balances Locks (r:1 w:1) // Storage: Staking Ledger (r:0 w:1) // Storage: Staking Payee (r:0 w:1) - fn on_idle_unstake() -> Weight { - // Minimum execution time: 82_426 nanoseconds. - Weight::from_ref_time(83_422_000 as u64) - .saturating_add(RocksDbWeight::get().reads(11 as u64)) - .saturating_add(RocksDbWeight::get().writes(6 as u64)) + /// The range of component `b` is `[1, 64]`. + fn on_idle_unstake(b: u32, ) -> Weight { + // Minimum execution time: 92_833 nanoseconds. + Weight::from_ref_time(62_136_346) + // Standard Error: 25_541 + .saturating_add(Weight::from_ref_time(42_904_859).saturating_mul(b.into())) + .saturating_add(RocksDbWeight::get().reads(6)) + .saturating_add(RocksDbWeight::get().reads((6_u64).saturating_mul(b.into()))) + .saturating_add(RocksDbWeight::get().writes(1)) + .saturating_add(RocksDbWeight::get().writes((5_u64).saturating_mul(b.into()))) } // Storage: FastUnstake ErasToCheckPerBlock (r:1 w:0) // Storage: Staking ValidatorCount (r:1 w:0) - // Storage: ElectionProviderMultiPhase CurrentPhase (r:1 w:0) // Storage: FastUnstake Head (r:1 w:1) - // Storage: FastUnstake Queue (r:2 w:1) - // Storage: FastUnstake CounterForQueue (r:1 w:1) + // Storage: FastUnstake CounterForQueue (r:1 w:0) + // Storage: ElectionProviderMultiPhase CurrentPhase (r:1 w:0) // Storage: Staking CurrentEra (r:1 w:0) - // Storage: Staking ErasStakers (r:1344 w:0) - /// The range of component `x` is `[672, 86016]`. - fn on_idle_check(x: u32, ) -> Weight { - // Minimum execution time: 13_932_777 nanoseconds. - Weight::from_ref_time(13_996_029_000 as u64) - // Standard Error: 16_878 - .saturating_add(Weight::from_ref_time(18_113_540 as u64).saturating_mul(x as u64)) - .saturating_add(RocksDbWeight::get().reads(345 as u64)) - .saturating_add(RocksDbWeight::get().reads((1 as u64).saturating_mul(x as u64))) - .saturating_add(RocksDbWeight::get().writes(3 as u64)) + // Storage: Staking ErasStakers (r:2 w:0) + /// The range of component `v` is `[1, 256]`. + /// The range of component `b` is `[1, 64]`. + fn on_idle_check(v: u32, b: u32, ) -> Weight { + // Minimum execution time: 1_775_293 nanoseconds. + Weight::from_ref_time(1_787_133_000) + // Standard Error: 17_109_142 + .saturating_add(Weight::from_ref_time(546_766_552).saturating_mul(v.into())) + // Standard Error: 68_455_625 + .saturating_add(Weight::from_ref_time(2_135_980_830).saturating_mul(b.into())) + .saturating_add(RocksDbWeight::get().reads(7)) + .saturating_add(RocksDbWeight::get().reads((1_u64).saturating_mul(v.into()))) + .saturating_add(RocksDbWeight::get().writes(1)) } // Storage: FastUnstake ErasToCheckPerBlock (r:1 w:0) // Storage: Staking Ledger (r:1 w:1) @@ -187,10 +204,10 @@ impl WeightInfo for () { // Storage: Balances Locks (r:1 w:1) // Storage: FastUnstake CounterForQueue (r:1 w:1) fn register_fast_unstake() -> Weight { - // Minimum execution time: 120_190 nanoseconds. - Weight::from_ref_time(121_337_000 as u64) - .saturating_add(RocksDbWeight::get().reads(14 as u64)) - .saturating_add(RocksDbWeight::get().writes(9 as u64)) + // Minimum execution time: 124_849 nanoseconds. + Weight::from_ref_time(128_176_000) + .saturating_add(RocksDbWeight::get().reads(14)) + .saturating_add(RocksDbWeight::get().writes(9)) } // Storage: FastUnstake ErasToCheckPerBlock (r:1 w:0) // Storage: Staking Ledger (r:1 w:0) @@ -198,15 +215,15 @@ impl WeightInfo for () { // Storage: FastUnstake Head (r:1 w:0) // Storage: FastUnstake CounterForQueue (r:1 w:1) fn deregister() -> Weight { - // Minimum execution time: 49_897 nanoseconds. - Weight::from_ref_time(50_080_000 as u64) - .saturating_add(RocksDbWeight::get().reads(5 as u64)) - .saturating_add(RocksDbWeight::get().writes(2 as u64)) + // Minimum execution time: 48_246 nanoseconds. + Weight::from_ref_time(49_720_000) + .saturating_add(RocksDbWeight::get().reads(5)) + .saturating_add(RocksDbWeight::get().writes(2)) } // Storage: FastUnstake ErasToCheckPerBlock (r:0 w:1) fn control() -> Weight { - // Minimum execution time: 4_814 nanoseconds. - Weight::from_ref_time(4_997_000 as u64) - .saturating_add(RocksDbWeight::get().writes(1 as u64)) + // Minimum execution time: 4_611 nanoseconds. + Weight::from_ref_time(4_844_000) + .saturating_add(RocksDbWeight::get().writes(1)) } } From 9db957cea255802d50231347ebef393d0707b909 Mon Sep 17 00:00:00 2001 From: Koute Date: Thu, 26 Jan 2023 14:38:00 +0900 Subject: [PATCH 056/558] Rework the trie cache (#12982) * Rework the trie cache * Align `state-machine` tests * Bump `schnellru` to 0.1.1 * Fix off-by-one * Align to review comments * Bump `ahash` to 0.8.2 * Bump `schnellru` to 0.2.0 * Bump `schnellru` to 0.2.1 * Remove unnecessary bound * Remove unnecessary loop when calculating maximum memory usage * Remove unnecessary `mut`s --- Cargo.lock | 8 +- client/db/src/bench.rs | 2 +- client/db/src/lib.rs | 2 +- client/finality-grandpa/Cargo.toml | 2 +- client/network-gossip/Cargo.toml | 2 +- primitives/state-machine/src/trie_backend.rs | 20 +- primitives/trie/Cargo.toml | 6 +- primitives/trie/src/cache/mod.rs | 576 ++++++++--- primitives/trie/src/cache/shared_cache.rs | 948 +++++++++++-------- 9 files changed, 1016 insertions(+), 550 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 53e5d4882..7c412030d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -8383,7 +8383,7 @@ dependencies = [ name = "sc-finality-grandpa" version = "0.10.0-dev" dependencies = [ - "ahash 0.7.6", + "ahash 0.8.2", "array-bytes", "assert_matches", "async-trait", @@ -8583,7 +8583,7 @@ dependencies = [ name = "sc-network-gossip" version = "0.10.0-dev" dependencies = [ - "ahash 0.7.6", + "ahash 0.8.2", "futures", "futures-timer", "libp2p", @@ -10352,18 +10352,18 @@ dependencies = [ name = "sp-trie" version = "7.0.0" dependencies = [ - "ahash 0.7.6", + "ahash 0.8.2", "array-bytes", "criterion", "hash-db", "hashbrown 0.12.3", "lazy_static", - "lru", "memory-db", "nohash-hasher", "parity-scale-codec", "parking_lot 0.12.1", "scale-info", + "schnellru", "sp-core", "sp-runtime", "sp-std", diff --git a/client/db/src/bench.rs b/client/db/src/bench.rs index 13d91fff0..e53209f27 100644 --- a/client/db/src/bench.rs +++ b/client/db/src/bench.rs @@ -110,7 +110,7 @@ impl BenchmarkingState { proof_recorder_root: Cell::new(root), enable_tracking, // Enable the cache, but do not sync anything to the shared state. - shared_trie_cache: SharedTrieCache::new(CacheSize::Maximum(0)), + shared_trie_cache: SharedTrieCache::new(CacheSize::new(0)), }; state.add_whitelist_to_tracker(); diff --git a/client/db/src/lib.rs b/client/db/src/lib.rs index 09ccfef1c..f217eb648 100644 --- a/client/db/src/lib.rs +++ b/client/db/src/lib.rs @@ -1243,7 +1243,7 @@ impl Backend { blocks_pruning: config.blocks_pruning, genesis_state: RwLock::new(None), shared_trie_cache: config.trie_cache_maximum_size.map(|maximum_size| { - SharedTrieCache::new(sp_trie::cache::CacheSize::Maximum(maximum_size)) + SharedTrieCache::new(sp_trie::cache::CacheSize::new(maximum_size)) }), }; diff --git a/client/finality-grandpa/Cargo.toml b/client/finality-grandpa/Cargo.toml index 9a31a1d4b..8a4e0449f 100644 --- a/client/finality-grandpa/Cargo.toml +++ b/client/finality-grandpa/Cargo.toml @@ -14,7 +14,7 @@ readme = "README.md" targets = ["x86_64-unknown-linux-gnu"] [dependencies] -ahash = "0.7.6" +ahash = "0.8.2" array-bytes = "4.1" async-trait = "0.1.57" dyn-clone = "1.0" diff --git a/client/network-gossip/Cargo.toml b/client/network-gossip/Cargo.toml index ef2306276..7811e86c0 100644 --- a/client/network-gossip/Cargo.toml +++ b/client/network-gossip/Cargo.toml @@ -14,7 +14,7 @@ readme = "README.md" targets = ["x86_64-unknown-linux-gnu"] [dependencies] -ahash = "0.7.6" +ahash = "0.8.2" futures = "0.3.21" futures-timer = "3.0.1" libp2p = "0.50.0" diff --git a/primitives/state-machine/src/trie_backend.rs b/primitives/state-machine/src/trie_backend.rs index da4250b6b..ff5cce24c 100644 --- a/primitives/state-machine/src/trie_backend.rs +++ b/primitives/state-machine/src/trie_backend.rs @@ -412,19 +412,19 @@ pub mod tests { fn $name() { let parameters = vec![ (StateVersion::V0, None, None), - (StateVersion::V0, Some(SharedCache::new(CacheSize::Unlimited)), None), + (StateVersion::V0, Some(SharedCache::new(CacheSize::unlimited())), None), (StateVersion::V0, None, Some(Recorder::default())), ( StateVersion::V0, - Some(SharedCache::new(CacheSize::Unlimited)), + Some(SharedCache::new(CacheSize::unlimited())), Some(Recorder::default()), ), (StateVersion::V1, None, None), - (StateVersion::V1, Some(SharedCache::new(CacheSize::Unlimited)), None), + (StateVersion::V1, Some(SharedCache::new(CacheSize::unlimited())), None), (StateVersion::V1, None, Some(Recorder::default())), ( StateVersion::V1, - Some(SharedCache::new(CacheSize::Unlimited)), + Some(SharedCache::new(CacheSize::unlimited())), Some(Recorder::default()), ), ]; @@ -760,7 +760,7 @@ pub mod tests { .clone() .for_each(|i| assert_eq!(trie.storage(&[i]).unwrap().unwrap(), vec![i; size_content])); - for cache in [Some(SharedTrieCache::new(CacheSize::Unlimited)), None] { + for cache in [Some(SharedTrieCache::new(CacheSize::unlimited())), None] { // Run multiple times to have a different cache conditions. for i in 0..5 { if let Some(cache) = &cache { @@ -793,7 +793,7 @@ pub mod tests { proof_record_works_with_iter_inner(StateVersion::V1); } fn proof_record_works_with_iter_inner(state_version: StateVersion) { - for cache in [Some(SharedTrieCache::new(CacheSize::Unlimited)), None] { + for cache in [Some(SharedTrieCache::new(CacheSize::unlimited())), None] { // Run multiple times to have a different cache conditions. for i in 0..5 { if let Some(cache) = &cache { @@ -870,7 +870,7 @@ pub mod tests { assert_eq!(in_memory.child_storage(child_info_2, &[i]).unwrap().unwrap(), vec![i]) }); - for cache in [Some(SharedTrieCache::new(CacheSize::Unlimited)), None] { + for cache in [Some(SharedTrieCache::new(CacheSize::unlimited())), None] { // Run multiple times to have a different cache conditions. for i in 0..5 { eprintln!("Running with cache {}, iteration {}", cache.is_some(), i); @@ -1002,7 +1002,7 @@ pub mod tests { nodes }; - let cache = SharedTrieCache::::new(CacheSize::Unlimited); + let cache = SharedTrieCache::::new(CacheSize::unlimited()); { let local_cache = cache.local_cache(); let mut trie_cache = local_cache.as_trie_db_cache(child_1_root); @@ -1093,7 +1093,7 @@ pub mod tests { #[test] fn new_data_is_added_to_the_cache() { - let shared_cache = SharedTrieCache::new(CacheSize::Unlimited); + let shared_cache = SharedTrieCache::new(CacheSize::unlimited()); let new_data = vec![ (&b"new_data0"[..], Some(&b"0"[..])), (&b"new_data1"[..], Some(&b"1"[..])), @@ -1159,7 +1159,7 @@ pub mod tests { assert_eq!(in_memory.child_storage(child_info_1, &key).unwrap().unwrap(), child_trie_1_val); assert_eq!(in_memory.child_storage(child_info_2, &key).unwrap().unwrap(), child_trie_2_val); - for cache in [Some(SharedTrieCache::new(CacheSize::Unlimited)), None] { + for cache in [Some(SharedTrieCache::new(CacheSize::unlimited())), None] { // Run multiple times to have a different cache conditions. for i in 0..5 { eprintln!("Running with cache {}, iteration {}", cache.is_some(), i); diff --git a/primitives/trie/Cargo.toml b/primitives/trie/Cargo.toml index 3f045a1cb..78b0b5d1b 100644 --- a/primitives/trie/Cargo.toml +++ b/primitives/trie/Cargo.toml @@ -18,12 +18,11 @@ name = "bench" harness = false [dependencies] -ahash = { version = "0.7.6", optional = true } +ahash = { version = "0.8.2", optional = true } codec = { package = "parity-scale-codec", version = "3.2.2", default-features = false } hashbrown = { version = "0.12.3", optional = true } hash-db = { version = "0.15.2", default-features = false } lazy_static = { version = "1.4.0", optional = true } -lru = { version = "0.8.1", optional = true } memory-db = { version = "0.31.0", default-features = false } nohash-hasher = { version = "0.2.0", optional = true } parking_lot = { version = "0.12.1", optional = true } @@ -34,6 +33,7 @@ trie-db = { version = "0.24.0", default-features = false } trie-root = { version = "0.17.0", default-features = false } sp-core = { version = "7.0.0", default-features = false, path = "../core" } sp-std = { version = "5.0.0", default-features = false, path = "../std" } +schnellru = { version = "0.2.1", optional = true } [dev-dependencies] array-bytes = "4.1" @@ -50,7 +50,7 @@ std = [ "hashbrown", "hash-db/std", "lazy_static", - "lru", + "schnellru", "memory-db/std", "nohash-hasher", "parking_lot", diff --git a/primitives/trie/src/cache/mod.rs b/primitives/trie/src/cache/mod.rs index 85539cf62..3c1e5b8d0 100644 --- a/primitives/trie/src/cache/mod.rs +++ b/primitives/trie/src/cache/mod.rs @@ -36,13 +36,17 @@ use crate::{Error, NodeCodec}; use hash_db::Hasher; -use hashbrown::HashSet; use nohash_hasher::BuildNoHashHasher; -use parking_lot::{Mutex, MutexGuard, RwLockReadGuard}; -use shared_cache::{SharedValueCache, ValueCacheKey}; +use parking_lot::{Mutex, MutexGuard}; +use schnellru::LruMap; +use shared_cache::{ValueCacheKey, ValueCacheRef}; use std::{ - collections::{hash_map::Entry as MapEntry, HashMap}, - sync::Arc, + collections::HashMap, + sync::{ + atomic::{AtomicU64, Ordering}, + Arc, + }, + time::Duration, }; use trie_db::{node::NodeOwned, CachedValue}; @@ -50,29 +54,267 @@ mod shared_cache; pub use shared_cache::SharedTrieCache; -use self::shared_cache::{SharedTrieCacheInner, ValueCacheKeyHash}; +use self::shared_cache::ValueCacheKeyHash; const LOG_TARGET: &str = "trie-cache"; -/// The size of the cache. +/// The maximum amount of time we'll wait trying to acquire the shared cache lock +/// when the local cache is dropped and synchronized with the share cache. +/// +/// This is just a failsafe; normally this should never trigger. +const SHARED_CACHE_WRITE_LOCK_TIMEOUT: Duration = Duration::from_millis(100); + +/// The maximum number of existing keys in the shared cache that a single local cache +/// can promote to the front of the LRU cache in one go. +/// +/// If we have a big shared cache and the local cache hits all of those keys we don't +/// want to spend forever bumping all of them. +const SHARED_NODE_CACHE_MAX_PROMOTED_KEYS: u32 = 1792; +/// Same as [`SHARED_NODE_CACHE_MAX_PROMOTED_KEYS`]. +const SHARED_VALUE_CACHE_MAX_PROMOTED_KEYS: u32 = 1792; + +/// The maximum portion of the shared cache (in percent) that a single local +/// cache can replace in one go. +/// +/// We don't want a single local cache instance to have the ability to replace +/// everything in the shared cache. +const SHARED_NODE_CACHE_MAX_REPLACE_PERCENT: usize = 33; +/// Same as [`SHARED_NODE_CACHE_MAX_REPLACE_PERCENT`]. +const SHARED_VALUE_CACHE_MAX_REPLACE_PERCENT: usize = 33; + +/// The maximum inline capacity of the local cache, in bytes. +/// +/// This is just an upper limit; since the maps are resized in powers of two +/// their actual size will most likely not exactly match this. +const LOCAL_NODE_CACHE_MAX_INLINE_SIZE: usize = 512 * 1024; +/// Same as [`LOCAL_NODE_CACHE_MAX_INLINE_SIZE`]. +const LOCAL_VALUE_CACHE_MAX_INLINE_SIZE: usize = 512 * 1024; + +/// The maximum size of the memory allocated on the heap by the local cache, in bytes. +const LOCAL_NODE_CACHE_MAX_HEAP_SIZE: usize = 2 * 1024 * 1024; +/// Same as [`LOCAL_NODE_CACHE_MAX_HEAP_SIZE`]. +const LOCAL_VALUE_CACHE_MAX_HEAP_SIZE: usize = 4 * 1024 * 1024; + +/// The size of the shared cache. #[derive(Debug, Clone, Copy)] -pub enum CacheSize { - /// Do not limit the cache size. - Unlimited, - /// Let the cache in maximum use the given amount of bytes. - Maximum(usize), -} +pub struct CacheSize(usize); impl CacheSize { - /// Returns `true` if the `current_size` exceeds the allowed size. - fn exceeds(&self, current_size: usize) -> bool { - match self { - Self::Unlimited => false, - Self::Maximum(max) => *max < current_size, + /// An unlimited cache size. + pub const fn unlimited() -> Self { + CacheSize(usize::MAX) + } + + /// A cache size `bytes` big. + pub const fn new(bytes: usize) -> Self { + CacheSize(bytes) + } +} + +/// A limiter for the local node cache. This makes sure the local cache doesn't grow too big. +#[derive(Default)] +pub struct LocalNodeCacheLimiter { + /// The current size (in bytes) of data allocated by this cache on the heap. + /// + /// This doesn't include the size of the map itself. + current_heap_size: usize, +} + +impl schnellru::Limiter> for LocalNodeCacheLimiter +where + H: AsRef<[u8]> + std::fmt::Debug, +{ + type KeyToInsert<'a> = H; + type LinkType = u32; + + #[inline] + fn is_over_the_limit(&self, length: usize) -> bool { + // Only enforce the limit if there's more than one element to make sure + // we can always add a new element to the cache. + if length <= 1 { + return false } + + self.current_heap_size > LOCAL_NODE_CACHE_MAX_HEAP_SIZE + } + + #[inline] + fn on_insert<'a>( + &mut self, + _length: usize, + key: H, + cached_node: NodeCached, + ) -> Option<(H, NodeCached)> { + self.current_heap_size += cached_node.heap_size(); + Some((key, cached_node)) + } + + #[inline] + fn on_replace( + &mut self, + _length: usize, + _old_key: &mut H, + _new_key: H, + old_node: &mut NodeCached, + new_node: &mut NodeCached, + ) -> bool { + debug_assert_eq!(_old_key.as_ref().len(), _new_key.as_ref().len()); + self.current_heap_size = + self.current_heap_size + new_node.heap_size() - old_node.heap_size(); + true + } + + #[inline] + fn on_removed(&mut self, _key: &mut H, cached_node: &mut NodeCached) { + self.current_heap_size -= cached_node.heap_size(); + } + + #[inline] + fn on_cleared(&mut self) { + self.current_heap_size = 0; + } + + #[inline] + fn on_grow(&mut self, new_memory_usage: usize) -> bool { + new_memory_usage <= LOCAL_NODE_CACHE_MAX_INLINE_SIZE + } +} + +/// A limiter for the local value cache. This makes sure the local cache doesn't grow too big. +#[derive(Default)] +pub struct LocalValueCacheLimiter { + /// The current size (in bytes) of data allocated by this cache on the heap. + /// + /// This doesn't include the size of the map itself. + current_heap_size: usize, +} + +impl schnellru::Limiter, CachedValue> for LocalValueCacheLimiter +where + H: AsRef<[u8]>, +{ + type KeyToInsert<'a> = ValueCacheRef<'a, H>; + type LinkType = u32; + + #[inline] + fn is_over_the_limit(&self, length: usize) -> bool { + // Only enforce the limit if there's more than one element to make sure + // we can always add a new element to the cache. + if length <= 1 { + return false + } + + self.current_heap_size > LOCAL_VALUE_CACHE_MAX_HEAP_SIZE + } + + #[inline] + fn on_insert( + &mut self, + _length: usize, + key: Self::KeyToInsert<'_>, + value: CachedValue, + ) -> Option<(ValueCacheKey, CachedValue)> { + self.current_heap_size += key.storage_key.len(); + Some((key.into(), value)) + } + + #[inline] + fn on_replace( + &mut self, + _length: usize, + _old_key: &mut ValueCacheKey, + _new_key: ValueCacheRef, + _old_value: &mut CachedValue, + _new_value: &mut CachedValue, + ) -> bool { + debug_assert_eq!(_old_key.storage_key.len(), _new_key.storage_key.len()); + true + } + + #[inline] + fn on_removed(&mut self, key: &mut ValueCacheKey, _: &mut CachedValue) { + self.current_heap_size -= key.storage_key.len(); + } + + #[inline] + fn on_cleared(&mut self) { + self.current_heap_size = 0; + } + + #[inline] + fn on_grow(&mut self, new_memory_usage: usize) -> bool { + new_memory_usage <= LOCAL_VALUE_CACHE_MAX_INLINE_SIZE + } +} + +/// A struct to gather hit/miss stats to aid in debugging the performance of the cache. +#[derive(Default)] +struct HitStats { + shared_hits: AtomicU64, + shared_fetch_attempts: AtomicU64, + local_hits: AtomicU64, + local_fetch_attempts: AtomicU64, +} + +impl std::fmt::Display for HitStats { + fn fmt(&self, fmt: &mut std::fmt::Formatter) -> std::fmt::Result { + let shared_hits = self.shared_hits.load(Ordering::Relaxed); + let shared_fetch_attempts = self.shared_fetch_attempts.load(Ordering::Relaxed); + let local_hits = self.local_hits.load(Ordering::Relaxed); + let local_fetch_attempts = self.local_fetch_attempts.load(Ordering::Relaxed); + if shared_fetch_attempts == 0 && local_hits == 0 { + write!(fmt, "empty") + } else { + let percent_local = (local_hits as f32 / local_fetch_attempts as f32) * 100.0; + let percent_shared = (shared_hits as f32 / shared_fetch_attempts as f32) * 100.0; + write!( + fmt, + "local hit rate = {}% [{}/{}], shared hit rate = {}% [{}/{}]", + percent_local as u32, + local_hits, + local_fetch_attempts, + percent_shared as u32, + shared_hits, + shared_fetch_attempts + ) + } + } +} + +/// A struct to gather hit/miss stats for the node cache and the value cache. +#[derive(Default)] +struct TrieHitStats { + node_cache: HitStats, + value_cache: HitStats, +} + +/// An internal struct to store the cached trie nodes. +pub(crate) struct NodeCached { + /// The cached node. + pub node: NodeOwned, + /// Whether this node was fetched from the shared cache or not. + pub is_from_shared_cache: bool, +} + +impl NodeCached { + /// Returns the number of bytes allocated on the heap by this node. + fn heap_size(&self) -> usize { + self.node.size_in_bytes() - std::mem::size_of::>() } } +type NodeCacheMap = LruMap, LocalNodeCacheLimiter, schnellru::RandomState>; + +type ValueCacheMap = LruMap< + ValueCacheKey, + CachedValue, + LocalValueCacheLimiter, + BuildNoHashHasher>, +>; + +type ValueAccessSet = + LruMap>; + /// The local trie cache. /// /// This cache should be used per state instance created by the backend. One state instance is @@ -86,21 +328,13 @@ impl CacheSize { pub struct LocalTrieCache { /// The shared trie cache that created this instance. shared: SharedTrieCache, + /// The local cache for the trie nodes. - node_cache: Mutex>>, - /// Keeps track of all the trie nodes accessed in the shared cache. - /// - /// This will be used to ensure that these nodes are brought to the front of the lru when this - /// local instance is merged back to the shared cache. - shared_node_cache_access: Mutex>, + node_cache: Mutex>, + /// The local cache for the values. - value_cache: Mutex< - HashMap< - ValueCacheKey<'static, H::Out>, - CachedValue, - BuildNoHashHasher>, - >, - >, + value_cache: Mutex>, + /// Keeps track of all values accessed in the shared cache. /// /// This will be used to ensure that these nodes are brought to the front of the lru when this @@ -109,8 +343,9 @@ pub struct LocalTrieCache { /// as we only use this set to update the lru position it is fine, even if we bring the wrong /// value to the top. The important part is that we always get the correct value from the value /// cache for a given key. - shared_value_cache_access: - Mutex>>, + shared_value_cache_access: Mutex, + + stats: TrieHitStats, } impl LocalTrieCache { @@ -118,19 +353,18 @@ impl LocalTrieCache { /// /// The given `storage_root` needs to be the storage root of the trie this cache is used for. pub fn as_trie_db_cache(&self, storage_root: H::Out) -> TrieCache<'_, H> { - let shared_inner = self.shared.read_lock_inner(); - let value_cache = ValueCache::ForStorageRoot { storage_root, local_value_cache: self.value_cache.lock(), shared_value_cache_access: self.shared_value_cache_access.lock(), + buffered_value: None, }; TrieCache { - shared_inner, + shared_cache: self.shared.clone(), local_cache: self.node_cache.lock(), value_cache, - shared_node_cache_access: self.shared_node_cache_access.lock(), + stats: &self.stats, } } @@ -143,63 +377,89 @@ impl LocalTrieCache { /// would break because of this. pub fn as_trie_db_mut_cache(&self) -> TrieCache<'_, H> { TrieCache { - shared_inner: self.shared.read_lock_inner(), + shared_cache: self.shared.clone(), local_cache: self.node_cache.lock(), value_cache: ValueCache::Fresh(Default::default()), - shared_node_cache_access: self.shared_node_cache_access.lock(), + stats: &self.stats, } } } impl Drop for LocalTrieCache { fn drop(&mut self) { - let mut shared_inner = self.shared.write_lock_inner(); + tracing::debug!( + target: LOG_TARGET, + "Local node trie cache dropped: {}", + self.stats.node_cache + ); + + tracing::debug!( + target: LOG_TARGET, + "Local value trie cache dropped: {}", + self.stats.value_cache + ); - shared_inner - .node_cache_mut() - .update(self.node_cache.lock().drain(), self.shared_node_cache_access.lock().drain()); + let mut shared_inner = match self.shared.write_lock_inner() { + Some(inner) => inner, + None => { + tracing::warn!( + target: LOG_TARGET, + "Timeout while trying to acquire a write lock for the shared trie cache" + ); + return + }, + }; - shared_inner - .value_cache_mut() - .update(self.value_cache.lock().drain(), self.shared_value_cache_access.lock().drain()); + shared_inner.node_cache_mut().update(self.node_cache.get_mut().drain()); + + shared_inner.value_cache_mut().update( + self.value_cache.get_mut().drain(), + self.shared_value_cache_access.get_mut().drain().map(|(key, ())| key), + ); } } /// The abstraction of the value cache for the [`TrieCache`]. -enum ValueCache<'a, H> { +enum ValueCache<'a, H: Hasher> { /// The value cache is fresh, aka not yet associated to any storage root. /// This is used for example when a new trie is being build, to cache new values. - Fresh(HashMap, CachedValue>), + Fresh(HashMap, CachedValue>), /// The value cache is already bound to a specific storage root. ForStorageRoot { - shared_value_cache_access: MutexGuard< - 'a, - HashSet>, - >, - local_value_cache: MutexGuard< - 'a, - HashMap< - ValueCacheKey<'static, H>, - CachedValue, - nohash_hasher::BuildNoHashHasher>, - >, - >, - storage_root: H, + shared_value_cache_access: MutexGuard<'a, ValueAccessSet>, + local_value_cache: MutexGuard<'a, ValueCacheMap>, + storage_root: H::Out, + // The shared value cache needs to be temporarily locked when reading from it + // so we need to clone the value that is returned, but we need to be able to + // return a reference to the value, so we just buffer it here. + buffered_value: Option>, }, } -impl + std::hash::Hash + Eq + Clone + Copy> ValueCache<'_, H> { +impl ValueCache<'_, H> { /// Get the value for the given `key`. fn get<'a>( &'a mut self, key: &[u8], - shared_value_cache: &'a SharedValueCache, - ) -> Option<&CachedValue> { - match self { - Self::Fresh(map) => map.get(key), - Self::ForStorageRoot { local_value_cache, shared_value_cache_access, storage_root } => { - let key = ValueCacheKey::new_ref(key, *storage_root); + shared_cache: &SharedTrieCache, + stats: &HitStats, + ) -> Option<&CachedValue> { + stats.local_fetch_attempts.fetch_add(1, Ordering::Relaxed); + match self { + Self::Fresh(map) => + if let Some(value) = map.get(key) { + stats.local_hits.fetch_add(1, Ordering::Relaxed); + Some(value) + } else { + None + }, + Self::ForStorageRoot { + local_value_cache, + shared_value_cache_access, + storage_root, + buffered_value, + } => { // We first need to look up in the local cache and then the shared cache. // It can happen that some value is cached in the shared cache, but the // weak reference of the data can not be upgraded anymore. This for example @@ -207,35 +467,39 @@ impl + std::hash::Hash + Eq + Clone + Copy> ValueCache<'_, H> { // // So, the logic of the trie would lookup the data and the node and store both // in our local caches. - local_value_cache - .get(unsafe { - // SAFETY - // - // We need to convert the lifetime to make the compiler happy. However, as - // we only use the `key` to looking up the value this lifetime conversion is - // safe. - std::mem::transmute::<&ValueCacheKey<'_, H>, &ValueCacheKey<'static, H>>( - &key, - ) - }) - .or_else(|| { - shared_value_cache.get(&key).map(|v| { - shared_value_cache_access.insert(key.get_hash()); - v - }) - }) + + let hash = ValueCacheKey::hash_data(key, storage_root); + + if let Some(value) = local_value_cache + .peek_by_hash(hash.raw(), |existing_key, _| { + existing_key.is_eq(storage_root, key) + }) { + stats.local_hits.fetch_add(1, Ordering::Relaxed); + + return Some(value) + } + + stats.shared_fetch_attempts.fetch_add(1, Ordering::Relaxed); + if let Some(value) = shared_cache.peek_value_by_hash(hash, storage_root, key) { + stats.shared_hits.fetch_add(1, Ordering::Relaxed); + shared_value_cache_access.insert(hash, ()); + *buffered_value = Some(value.clone()); + return buffered_value.as_ref() + } + + None }, } } /// Insert some new `value` under the given `key`. - fn insert(&mut self, key: &[u8], value: CachedValue) { + fn insert(&mut self, key: &[u8], value: CachedValue) { match self { Self::Fresh(map) => { map.insert(key.into(), value); }, Self::ForStorageRoot { local_value_cache, storage_root, .. } => { - local_value_cache.insert(ValueCacheKey::new_value(key, *storage_root), value); + local_value_cache.insert(ValueCacheRef::new(key, *storage_root), value); }, } } @@ -247,10 +511,10 @@ impl + std::hash::Hash + Eq + Clone + Copy> ValueCache<'_, H> { /// be merged back into the [`LocalTrieCache`] with [`Self::merge_into`] after all operations are /// done. pub struct TrieCache<'a, H: Hasher> { - shared_inner: RwLockReadGuard<'a, SharedTrieCacheInner>, - shared_node_cache_access: MutexGuard<'a, HashSet>, - local_cache: MutexGuard<'a, HashMap>>, - value_cache: ValueCache<'a, H::Out>, + shared_cache: SharedTrieCache, + local_cache: MutexGuard<'a, NodeCacheMap>, + value_cache: ValueCache<'a, H>, + stats: &'a TrieHitStats, } impl<'a, H: Hasher> TrieCache<'a, H> { @@ -267,16 +531,11 @@ impl<'a, H: Hasher> TrieCache<'a, H> { let mut value_cache = local.value_cache.lock(); let partial_hash = ValueCacheKey::hash_partial_data(&storage_root); - cache - .into_iter() - .map(|(k, v)| { - let hash = - ValueCacheKeyHash::from_hasher_and_storage_key(partial_hash.clone(), &k); - (ValueCacheKey::Value { storage_key: k, storage_root, hash }, v) - }) - .for_each(|(k, v)| { - value_cache.insert(k, v); - }); + cache.into_iter().for_each(|(k, v)| { + let hash = ValueCacheKeyHash::from_hasher_and_storage_key(partial_hash.clone(), &k); + let k = ValueCacheRef { storage_root, storage_key: &k, hash }; + value_cache.insert(k, v); + }); } } } @@ -287,53 +546,85 @@ impl<'a, H: Hasher> trie_db::TrieCache> for TrieCache<'a, H> { hash: H::Out, fetch_node: &mut dyn FnMut() -> trie_db::Result, H::Out, Error>, ) -> trie_db::Result<&NodeOwned, H::Out, Error> { - if let Some(res) = self.shared_inner.node_cache().get(&hash) { - tracing::trace!(target: LOG_TARGET, ?hash, "Serving node from shared cache"); - self.shared_node_cache_access.insert(hash); - return Ok(res) - } + let mut is_local_cache_hit = true; + self.stats.node_cache.local_fetch_attempts.fetch_add(1, Ordering::Relaxed); - match self.local_cache.entry(hash) { - MapEntry::Occupied(res) => { - tracing::trace!(target: LOG_TARGET, ?hash, "Serving node from local cache"); - Ok(res.into_mut()) - }, - MapEntry::Vacant(vacant) => { - let node = (*fetch_node)(); + // First try to grab the node from the local cache. + let node = self.local_cache.get_or_insert_fallible(hash, || { + is_local_cache_hit = false; - tracing::trace!( - target: LOG_TARGET, - ?hash, - fetch_successful = node.is_ok(), - "Node not found, needed to fetch it." - ); + // It was not in the local cache; try the shared cache. + self.stats.node_cache.shared_fetch_attempts.fetch_add(1, Ordering::Relaxed); + if let Some(node) = self.shared_cache.peek_node(&hash) { + self.stats.node_cache.shared_hits.fetch_add(1, Ordering::Relaxed); + tracing::trace!(target: LOG_TARGET, ?hash, "Serving node from shared cache"); - Ok(vacant.insert(node?)) - }, + return Ok(NodeCached:: { node: node.clone(), is_from_shared_cache: true }) + } + + // It was not in the shared cache; try fetching it from the database. + match fetch_node() { + Ok(node) => { + tracing::trace!(target: LOG_TARGET, ?hash, "Serving node from database"); + Ok(NodeCached:: { node, is_from_shared_cache: false }) + }, + Err(error) => { + tracing::trace!(target: LOG_TARGET, ?hash, "Serving node from database failed"); + Err(error) + }, + } + }); + + if is_local_cache_hit { + tracing::trace!(target: LOG_TARGET, ?hash, "Serving node from local cache"); + self.stats.node_cache.local_hits.fetch_add(1, Ordering::Relaxed); } + + Ok(&node? + .expect("you can always insert at least one element into the local cache; qed") + .node) } fn get_node(&mut self, hash: &H::Out) -> Option<&NodeOwned> { - if let Some(node) = self.shared_inner.node_cache().get(hash) { - tracing::trace!(target: LOG_TARGET, ?hash, "Getting node from shared cache"); - self.shared_node_cache_access.insert(*hash); - return Some(node) - } + let mut is_local_cache_hit = true; + self.stats.node_cache.local_fetch_attempts.fetch_add(1, Ordering::Relaxed); - let res = self.local_cache.get(hash); + // First try to grab the node from the local cache. + let cached_node = self.local_cache.get_or_insert_fallible(*hash, || { + is_local_cache_hit = false; - tracing::trace!( - target: LOG_TARGET, - ?hash, - found = res.is_some(), - "Getting node from local cache" - ); + // It was not in the local cache; try the shared cache. + self.stats.node_cache.shared_fetch_attempts.fetch_add(1, Ordering::Relaxed); + if let Some(node) = self.shared_cache.peek_node(&hash) { + self.stats.node_cache.shared_hits.fetch_add(1, Ordering::Relaxed); + tracing::trace!(target: LOG_TARGET, ?hash, "Serving node from shared cache"); - res + Ok(NodeCached:: { node: node.clone(), is_from_shared_cache: true }) + } else { + tracing::trace!(target: LOG_TARGET, ?hash, "Serving node from cache failed"); + + Err(()) + } + }); + + if is_local_cache_hit { + tracing::trace!(target: LOG_TARGET, ?hash, "Serving node from local cache"); + self.stats.node_cache.local_hits.fetch_add(1, Ordering::Relaxed); + } + + match cached_node { + Ok(Some(cached_node)) => Some(&cached_node.node), + Ok(None) => { + unreachable!( + "you can always insert at least one element into the local cache; qed" + ); + }, + Err(()) => None, + } } fn lookup_value_for_key(&mut self, key: &[u8]) -> Option<&CachedValue> { - let res = self.value_cache.get(key, self.shared_inner.value_cache()); + let res = self.value_cache.get(key, &self.shared_cache, &self.stats.value_cache); tracing::trace!( target: LOG_TARGET, @@ -352,7 +643,7 @@ impl<'a, H: Hasher> trie_db::TrieCache> for TrieCache<'a, H> { "Caching value for key", ); - self.value_cache.insert(key.into(), data); + self.value_cache.insert(key, data); } } @@ -369,7 +660,7 @@ mod tests { const TEST_DATA: &[(&[u8], &[u8])] = &[(b"key1", b"val1"), (b"key2", &[2; 64]), (b"key3", b"val3"), (b"key4", &[4; 64])]; const CACHE_SIZE_RAW: usize = 1024 * 10; - const CACHE_SIZE: CacheSize = CacheSize::Maximum(CACHE_SIZE_RAW); + const CACHE_SIZE: CacheSize = CacheSize::new(CACHE_SIZE_RAW); fn create_trie() -> (MemoryDB, TrieHash) { let mut db = MemoryDB::default(); @@ -418,7 +709,7 @@ mod tests { let fake_data = Bytes::from(&b"fake_data"[..]); let local_cache = shared_cache.local_cache(); - shared_cache.write_lock_inner().value_cache_mut().lru.put( + shared_cache.write_lock_inner().unwrap().value_cache_mut().lru.insert( ValueCacheKey::new_value(TEST_DATA[1].0, root), (fake_data.clone(), Default::default()).into(), ); @@ -591,7 +882,7 @@ mod tests { .lru .iter() .map(|d| d.0) - .all(|l| TEST_DATA.iter().any(|d| l.storage_key().unwrap() == d.0))); + .all(|l| TEST_DATA.iter().any(|d| &*l.storage_key == d.0))); // Run this in a loop. The first time we check that with the filled value cache, // the expected values are at the top of the LRU. @@ -617,7 +908,7 @@ mod tests { .iter() .take(2) .map(|d| d.0) - .all(|l| { TEST_DATA.iter().take(2).any(|d| l.storage_key().unwrap() == d.0) })); + .all(|l| { TEST_DATA.iter().take(2).any(|d| &*l.storage_key == d.0) })); // Delete the value cache, so that we access the nodes. shared_cache.reset_value_cache(); @@ -684,9 +975,6 @@ mod tests { } } - let node_cache_size = shared_cache.read_lock_inner().node_cache().size_in_bytes; - let value_cache_size = shared_cache.read_lock_inner().value_cache().size_in_bytes; - - assert!(node_cache_size + value_cache_size < CACHE_SIZE_RAW); + assert!(shared_cache.used_memory_size() < CACHE_SIZE_RAW); } } diff --git a/primitives/trie/src/cache/shared_cache.rs b/primitives/trie/src/cache/shared_cache.rs index 9d4d36b83..8c60d5043 100644 --- a/primitives/trie/src/cache/shared_cache.rs +++ b/primitives/trie/src/cache/shared_cache.rs @@ -17,15 +17,14 @@ ///! Provides the [`SharedNodeCache`], the [`SharedValueCache`] and the [`SharedTrieCache`] ///! that combines both caches and is exported to the outside. -use super::{CacheSize, LOG_TARGET}; +use super::{CacheSize, NodeCached}; use hash_db::Hasher; use hashbrown::{hash_set::Entry as SetEntry, HashSet}; -use lru::LruCache; use nohash_hasher::BuildNoHashHasher; -use parking_lot::{RwLock, RwLockReadGuard, RwLockWriteGuard}; +use parking_lot::{Mutex, RwLock, RwLockWriteGuard}; +use schnellru::LruMap; use std::{ hash::{BuildHasher, Hasher as _}, - mem, sync::Arc, }; use trie_db::{node::NodeOwned, CachedValue}; @@ -34,94 +33,300 @@ lazy_static::lazy_static! { static ref RANDOM_STATE: ahash::RandomState = ahash::RandomState::default(); } -/// No hashing [`LruCache`]. -type NoHashingLruCache = LruCache>; +pub struct SharedNodeCacheLimiter { + /// The maximum size (in bytes) the cache can hold inline. + /// + /// This space is always consumed whether there are any items in the map or not. + max_inline_size: usize, + + /// The maximum size (in bytes) the cache can hold on the heap. + max_heap_size: usize, + + /// The current size (in bytes) of data allocated by this cache on the heap. + /// + /// This doesn't include the size of the map itself. + heap_size: usize, + + /// A counter with the number of elements that got evicted from the cache. + /// + /// Reset to zero before every update. + items_evicted: usize, + + /// The maximum number of elements that we allow to be evicted. + /// + /// Reset on every update. + max_items_evicted: usize, +} + +impl schnellru::Limiter> for SharedNodeCacheLimiter +where + H: AsRef<[u8]>, +{ + type KeyToInsert<'a> = H; + type LinkType = u32; + + #[inline] + fn is_over_the_limit(&self, _length: usize) -> bool { + // Once we hit the limit of max items evicted this will return `false` and prevent + // any further evictions, but this is fine because the outer loop which inserts + // items into this cache will just detect this and stop inserting new items. + self.items_evicted <= self.max_items_evicted && self.heap_size > self.max_heap_size + } + + #[inline] + fn on_insert( + &mut self, + _length: usize, + key: Self::KeyToInsert<'_>, + node: NodeOwned, + ) -> Option<(H, NodeOwned)> { + let new_item_heap_size = node.size_in_bytes() - std::mem::size_of::>(); + if new_item_heap_size > self.max_heap_size { + // Item's too big to add even if the cache's empty; bail. + return None + } + + self.heap_size += new_item_heap_size; + Some((key, node)) + } + + #[inline] + fn on_replace( + &mut self, + _length: usize, + _old_key: &mut H, + _new_key: H, + old_node: &mut NodeOwned, + new_node: &mut NodeOwned, + ) -> bool { + debug_assert_eq!(_old_key.as_ref(), _new_key.as_ref()); + + let new_item_heap_size = new_node.size_in_bytes() - std::mem::size_of::>(); + if new_item_heap_size > self.max_heap_size { + // Item's too big to add even if the cache's empty; bail. + return false + } + + let old_item_heap_size = old_node.size_in_bytes() - std::mem::size_of::>(); + self.heap_size = self.heap_size - old_item_heap_size + new_item_heap_size; + true + } + + #[inline] + fn on_cleared(&mut self) { + self.heap_size = 0; + } + + #[inline] + fn on_removed(&mut self, _: &mut H, node: &mut NodeOwned) { + self.heap_size -= node.size_in_bytes() - std::mem::size_of::>(); + self.items_evicted += 1; + } + + #[inline] + fn on_grow(&mut self, new_memory_usage: usize) -> bool { + new_memory_usage <= self.max_inline_size + } +} + +pub struct SharedValueCacheLimiter { + /// The maximum size (in bytes) the cache can hold inline. + /// + /// This space is always consumed whether there are any items in the map or not. + max_inline_size: usize, + + /// The maximum size (in bytes) the cache can hold on the heap. + max_heap_size: usize, + + /// The current size (in bytes) of data allocated by this cache on the heap. + /// + /// This doesn't include the size of the map itself. + heap_size: usize, + + /// A set with all of the keys deduplicated to save on memory. + known_storage_keys: HashSet>, + + /// A counter with the number of elements that got evicted from the cache. + /// + /// Reset to zero before every update. + items_evicted: usize, + + /// The maximum number of elements that we allow to be evicted. + /// + /// Reset on every update. + max_items_evicted: usize, +} + +impl schnellru::Limiter, CachedValue> for SharedValueCacheLimiter +where + H: AsRef<[u8]>, +{ + type KeyToInsert<'a> = ValueCacheKey; + type LinkType = u32; + + #[inline] + fn is_over_the_limit(&self, _length: usize) -> bool { + self.items_evicted <= self.max_items_evicted && self.heap_size > self.max_heap_size + } + + #[inline] + fn on_insert( + &mut self, + _length: usize, + mut key: Self::KeyToInsert<'_>, + value: CachedValue, + ) -> Option<(ValueCacheKey, CachedValue)> { + match self.known_storage_keys.entry(key.storage_key.clone()) { + SetEntry::Vacant(entry) => { + let new_item_heap_size = key.storage_key.len(); + if new_item_heap_size > self.max_heap_size { + // Item's too big to add even if the cache's empty; bail. + return None + } + + self.heap_size += new_item_heap_size; + entry.insert(); + }, + SetEntry::Occupied(entry) => { + key.storage_key = entry.get().clone(); + }, + } + + Some((key, value)) + } + + #[inline] + fn on_replace( + &mut self, + _length: usize, + _old_key: &mut ValueCacheKey, + _new_key: ValueCacheKey, + _old_value: &mut CachedValue, + _new_value: &mut CachedValue, + ) -> bool { + debug_assert_eq!(_new_key.storage_key, _old_key.storage_key); + true + } + + #[inline] + fn on_removed(&mut self, key: &mut ValueCacheKey, _: &mut CachedValue) { + if Arc::strong_count(&key.storage_key) == 2 { + // There are only two instances of this key: + // 1) one memoized in `known_storage_keys`, + // 2) one inside the map. + // + // This means that after this remove goes through the `Arc` will be deallocated. + self.heap_size -= key.storage_key.len(); + self.known_storage_keys.remove(&key.storage_key); + } + self.items_evicted += 1; + } + + #[inline] + fn on_cleared(&mut self) { + self.heap_size = 0; + self.known_storage_keys.clear(); + } + + #[inline] + fn on_grow(&mut self, new_memory_usage: usize) -> bool { + new_memory_usage <= self.max_inline_size + } +} + +type SharedNodeCacheMap = + LruMap, SharedNodeCacheLimiter, schnellru::RandomState>; /// The shared node cache. /// -/// Internally this stores all cached nodes in a [`LruCache`]. It ensures that when updating the +/// Internally this stores all cached nodes in a [`LruMap`]. It ensures that when updating the /// cache, that the cache stays within its allowed bounds. -pub(super) struct SharedNodeCache { +pub(super) struct SharedNodeCache +where + H: AsRef<[u8]>, +{ /// The cached nodes, ordered by least recently used. - pub(super) lru: LruCache>, - /// The size of [`Self::lru`] in bytes. - pub(super) size_in_bytes: usize, - /// The maximum cache size of [`Self::lru`]. - maximum_cache_size: CacheSize, + pub(super) lru: SharedNodeCacheMap, } impl + Eq + std::hash::Hash> SharedNodeCache { /// Create a new instance. - fn new(cache_size: CacheSize) -> Self { - Self { lru: LruCache::unbounded(), size_in_bytes: 0, maximum_cache_size: cache_size } + fn new(max_inline_size: usize, max_heap_size: usize) -> Self { + Self { + lru: LruMap::new(SharedNodeCacheLimiter { + max_inline_size, + max_heap_size, + heap_size: 0, + items_evicted: 0, + max_items_evicted: 0, // Will be set during `update`. + }), + } } - /// Get the node for `key`. - /// - /// This doesn't change the least recently order in the internal [`LruCache`]. - pub fn get(&self, key: &H) -> Option<&NodeOwned> { - self.lru.peek(key) - } + /// Update the cache with the `list` of nodes which were either newly added or accessed. + pub fn update(&mut self, list: impl IntoIterator)>) { + let mut access_count = 0; + let mut add_count = 0; - /// Update the cache with the `added` nodes and the `accessed` nodes. - /// - /// The `added` nodes are the ones that have been collected by doing operations on the trie and - /// now should be stored in the shared cache. The `accessed` nodes are only referenced by hash - /// and represent the nodes that were retrieved from this shared cache through [`Self::get`]. - /// These `accessed` nodes are being put to the front of the internal [`LruCache`] like the - /// `added` ones. - /// - /// After the internal [`LruCache`] was updated, it is ensured that the internal [`LruCache`] is - /// inside its bounds ([`Self::maximum_size_in_bytes`]). - pub fn update( - &mut self, - added: impl IntoIterator)>, - accessed: impl IntoIterator, - ) { - let update_size_in_bytes = |size_in_bytes: &mut usize, key: &H, node: &NodeOwned| { - if let Some(new_size_in_bytes) = - size_in_bytes.checked_sub(key.as_ref().len() + node.size_in_bytes()) - { - *size_in_bytes = new_size_in_bytes; - } else { - *size_in_bytes = 0; - tracing::error!(target: LOG_TARGET, "`SharedNodeCache` underflow detected!",); - } - }; + self.lru.limiter_mut().items_evicted = 0; + self.lru.limiter_mut().max_items_evicted = + self.lru.len() * 100 / super::SHARED_NODE_CACHE_MAX_REPLACE_PERCENT; - accessed.into_iter().for_each(|key| { - // Access every node in the lru to put it to the front. - self.lru.get(&key); - }); - added.into_iter().for_each(|(key, node)| { - self.size_in_bytes += key.as_ref().len() + node.size_in_bytes(); + for (key, cached_node) in list { + if cached_node.is_from_shared_cache { + if self.lru.get(&key).is_some() { + access_count += 1; - if let Some((r_key, r_node)) = self.lru.push(key, node) { - update_size_in_bytes(&mut self.size_in_bytes, &r_key, &r_node); - } + if access_count >= super::SHARED_NODE_CACHE_MAX_PROMOTED_KEYS { + // Stop when we've promoted a large enough number of items. + break + } - // Directly ensure that we respect the maximum size. By doing it directly here we ensure - // that the internal map of the [`LruCache`] doesn't grow too much. - while self.maximum_cache_size.exceeds(self.size_in_bytes) { - // This should always be `Some(_)`, otherwise something is wrong! - if let Some((key, node)) = self.lru.pop_lru() { - update_size_in_bytes(&mut self.size_in_bytes, &key, &node); + continue } } - }); + + self.lru.insert(key, cached_node.node); + add_count += 1; + + if self.lru.limiter().items_evicted > self.lru.limiter().max_items_evicted { + // Stop when we've evicted a big enough chunk of the shared cache. + break + } + } + + tracing::debug!( + target: super::LOG_TARGET, + "Updated the shared node cache: {} accesses, {} new values, {}/{} evicted (length = {}, inline size={}/{}, heap size={}/{})", + access_count, + add_count, + self.lru.limiter().items_evicted, + self.lru.limiter().max_items_evicted, + self.lru.len(), + self.lru.memory_usage(), + self.lru.limiter().max_inline_size, + self.lru.limiter().heap_size, + self.lru.limiter().max_heap_size, + ); } /// Reset the cache. fn reset(&mut self) { - self.size_in_bytes = 0; self.lru.clear(); } } /// The hash of [`ValueCacheKey`]. -#[derive(Eq, Clone, Copy)] +#[derive(PartialEq, Eq, Clone, Copy, Hash)] +#[repr(transparent)] pub struct ValueCacheKeyHash(u64); +impl ValueCacheKeyHash { + pub fn raw(self) -> u64 { + self.0 + } +} + impl ValueCacheKeyHash { pub fn from_hasher_and_storage_key( mut hasher: impl std::hash::Hasher, @@ -133,88 +338,75 @@ impl ValueCacheKeyHash { } } -impl PartialEq for ValueCacheKeyHash { - fn eq(&self, other: &Self) -> bool { - self.0 == other.0 - } +impl nohash_hasher::IsEnabled for ValueCacheKeyHash {} + +/// The key type that is being used to address a [`CachedValue`]. +#[derive(Eq)] +pub(super) struct ValueCacheKey { + /// The storage root of the trie this key belongs to. + pub storage_root: H, + /// The key to access the value in the storage. + pub storage_key: Arc<[u8]>, + /// The hash that identifies this instance of `storage_root` and `storage_key`. + pub hash: ValueCacheKeyHash, } -impl std::hash::Hash for ValueCacheKeyHash { - fn hash(&self, state: &mut Hasher) { - state.write_u64(self.0); +/// A borrowed variant of [`ValueCacheKey`]. +pub(super) struct ValueCacheRef<'a, H> { + /// The storage root of the trie this key belongs to. + pub storage_root: H, + /// The key to access the value in the storage. + pub storage_key: &'a [u8], + /// The hash that identifies this instance of `storage_root` and `storage_key`. + pub hash: ValueCacheKeyHash, +} + +impl<'a, H> ValueCacheRef<'a, H> { + pub fn new(storage_key: &'a [u8], storage_root: H) -> Self + where + H: AsRef<[u8]>, + { + let hash = ValueCacheKey::::hash_data(&storage_key, &storage_root); + Self { storage_root, storage_key, hash } } } -impl nohash_hasher::IsEnabled for ValueCacheKeyHash {} +impl<'a, H> From> for ValueCacheKey { + fn from(value: ValueCacheRef<'a, H>) -> Self { + ValueCacheKey { + storage_root: value.storage_root, + storage_key: value.storage_key.into(), + hash: value.hash, + } + } +} -/// A type that can only be constructed inside of this file. -/// -/// It "requires" that the user has read the docs to prevent fuck ups. -#[derive(Eq, PartialEq)] -pub(super) struct IReadTheDocumentation(()); +impl<'a, H: std::hash::Hash> std::hash::Hash for ValueCacheRef<'a, H> { + fn hash(&self, state: &mut Hasher) { + self.hash.hash(state) + } +} -/// The key type that is being used to address a [`CachedValue`]. -/// -/// This type is implemented as `enum` to improve the performance when accessing the value cache. -/// The problem being that we need to calculate the `hash` of [`Self`] in worst case three times -/// when trying to find a value in the value cache. First to lookup the local cache, then the shared -/// cache and if we found it in the shared cache a third time to insert it into the list of accessed -/// values. To work around each variant stores the `hash` to identify a unique combination of -/// `storage_key` and `storage_root`. However, be aware that this `hash` can lead to collisions when -/// there are two different `storage_key` and `storage_root` pairs that map to the same `hash`. This -/// type also has the `Hash` variant. This variant should only be used for the use case of updating -/// the lru for a key. Because when using only the `Hash` variant to getting a value from a hash map -/// it could happen that a wrong value is returned when there is another key in the same hash map -/// that maps to the same `hash`. The [`PartialEq`] implementation is written in a way that when one -/// of the two compared instances is the `Hash` variant, we will only compare the hashes. This -/// ensures that we can use the `Hash` variant to bring values up in the lru. -#[derive(Eq)] -pub(super) enum ValueCacheKey<'a, H> { - /// Variant that stores the `storage_key` by value. - Value { - /// The storage root of the trie this key belongs to. - storage_root: H, - /// The key to access the value in the storage. - storage_key: Arc<[u8]>, - /// The hash that identifying this instance of `storage_root` and `storage_key`. - hash: ValueCacheKeyHash, - }, - /// Variant that only references the `storage_key`. - Ref { - /// The storage root of the trie this key belongs to. - storage_root: H, - /// The key to access the value in the storage. - storage_key: &'a [u8], - /// The hash that identifying this instance of `storage_root` and `storage_key`. - hash: ValueCacheKeyHash, - }, - /// Variant that only stores the hash that represents the `storage_root` and `storage_key`. - /// - /// This should be used by caution, because it can lead to accessing the wrong value in a - /// hash map/set when there exists two different `storage_root`s and `storage_key`s that - /// map to the same `hash`. - Hash { hash: ValueCacheKeyHash, _i_read_the_documentation: IReadTheDocumentation }, +impl<'a, H> PartialEq> for ValueCacheRef<'a, H> +where + H: AsRef<[u8]>, +{ + fn eq(&self, rhs: &ValueCacheKey) -> bool { + self.storage_root.as_ref() == rhs.storage_root.as_ref() && + self.storage_key == &*rhs.storage_key + } } -impl<'a, H> ValueCacheKey<'a, H> { +impl ValueCacheKey { /// Constructs [`Self::Value`]. + #[cfg(test)] // Only used in tests. pub fn new_value(storage_key: impl Into>, storage_root: H) -> Self where H: AsRef<[u8]>, { let storage_key = storage_key.into(); let hash = Self::hash_data(&storage_key, &storage_root); - Self::Value { storage_root, storage_key, hash } - } - - /// Constructs [`Self::Ref`]. - pub fn new_ref(storage_key: &'a [u8], storage_root: H) -> Self - where - H: AsRef<[u8]>, - { - let storage_key = storage_key.into(); - let hash = Self::hash_data(storage_key, &storage_root); - Self::Ref { storage_root, storage_key, hash } + Self { storage_root, storage_key, hash } } /// Returns a hasher prepared to build the final hash to identify [`Self`]. @@ -241,231 +433,133 @@ impl<'a, H> ValueCacheKey<'a, H> { ValueCacheKeyHash::from_hasher_and_storage_key(hasher, key) } - /// Returns the `hash` that identifies the current instance. - pub fn get_hash(&self) -> ValueCacheKeyHash { - match self { - Self::Value { hash, .. } | Self::Ref { hash, .. } | Self::Hash { hash, .. } => *hash, - } - } - - /// Returns the stored storage root. - pub fn storage_root(&self) -> Option<&H> { - match self { - Self::Value { storage_root, .. } | Self::Ref { storage_root, .. } => Some(storage_root), - Self::Hash { .. } => None, - } - } - - /// Returns the stored storage key. - pub fn storage_key(&self) -> Option<&[u8]> { - match self { - Self::Ref { storage_key, .. } => Some(&storage_key), - Self::Value { storage_key, .. } => Some(storage_key), - Self::Hash { .. } => None, - } + /// Checks whether the key is equal to the given `storage_key` and `storage_root`. + #[inline] + pub fn is_eq(&self, storage_root: &H, storage_key: &[u8]) -> bool + where + H: PartialEq, + { + self.storage_root == *storage_root && *self.storage_key == *storage_key } } -// Implement manually to ensure that the `Value` and `Hash` are treated equally. -impl std::hash::Hash for ValueCacheKey<'_, H> { +// Implement manually so that only `hash` is accessed. +impl std::hash::Hash for ValueCacheKey { fn hash(&self, state: &mut Hasher) { - self.get_hash().hash(state) + self.hash.hash(state) } } -impl nohash_hasher::IsEnabled for ValueCacheKey<'_, H> {} +impl nohash_hasher::IsEnabled for ValueCacheKey {} -// Implement manually to ensure that the `Value` and `Hash` are treated equally. -impl PartialEq for ValueCacheKey<'_, H> { +// Implement manually to not have to compare `hash`. +impl PartialEq for ValueCacheKey { + #[inline] fn eq(&self, other: &Self) -> bool { - // First check if `self` or `other` is only the `Hash`. - // Then we only compare the `hash`. So, there could actually be some collision - // if two different storage roots and keys are mapping to the same key. See the - // [`ValueCacheKey`] docs for more information. - match (self, other) { - (Self::Hash { hash, .. }, Self::Hash { hash: other_hash, .. }) => hash == other_hash, - (Self::Hash { hash, .. }, _) => *hash == other.get_hash(), - (_, Self::Hash { hash: other_hash, .. }) => self.get_hash() == *other_hash, - // If both are not the `Hash` variant, we compare all the values. - _ => - self.get_hash() == other.get_hash() && - self.storage_root() == other.storage_root() && - self.storage_key() == other.storage_key(), - } + self.is_eq(&other.storage_root, &other.storage_key) } } +type SharedValueCacheMap = schnellru::LruMap< + ValueCacheKey, + CachedValue, + SharedValueCacheLimiter, + BuildNoHashHasher>, +>; + /// The shared value cache. /// /// The cache ensures that it stays in the configured size bounds. -pub(super) struct SharedValueCache { +pub(super) struct SharedValueCache +where + H: AsRef<[u8]>, +{ /// The cached nodes, ordered by least recently used. - pub(super) lru: NoHashingLruCache, CachedValue>, - /// The size of [`Self::lru`] in bytes. - pub(super) size_in_bytes: usize, - /// The maximum cache size of [`Self::lru`]. - maximum_cache_size: CacheSize, - /// All known storage keys that are stored in [`Self::lru`]. - /// - /// This is used to de-duplicate keys in memory that use the - /// same [`SharedValueCache::storage_key`], but have a different - /// [`SharedValueCache::storage_root`]. - known_storage_keys: HashSet>, + pub(super) lru: SharedValueCacheMap, } impl> SharedValueCache { /// Create a new instance. - fn new(cache_size: CacheSize) -> Self { + fn new(max_inline_size: usize, max_heap_size: usize) -> Self { Self { - lru: NoHashingLruCache::unbounded_with_hasher(Default::default()), - size_in_bytes: 0, - maximum_cache_size: cache_size, - known_storage_keys: Default::default(), + lru: schnellru::LruMap::with_hasher( + SharedValueCacheLimiter { + max_inline_size, + max_heap_size, + heap_size: 0, + known_storage_keys: Default::default(), + items_evicted: 0, + max_items_evicted: 0, // Will be set during `update`. + }, + Default::default(), + ), } } - /// Get the [`CachedValue`] for `key`. - /// - /// This doesn't change the least recently order in the internal [`LruCache`]. - pub fn get<'a>(&'a self, key: &ValueCacheKey) -> Option<&'a CachedValue> { - debug_assert!( - !matches!(key, ValueCacheKey::Hash { .. }), - "`get` can not be called with `Hash` variant as this may returns the wrong value." - ); - - self.lru.peek(unsafe { - // SAFETY - // - // We need to convert the lifetime to make the compiler happy. However, as - // we only use the `key` to looking up the value this lifetime conversion is - // safe. - mem::transmute::<&ValueCacheKey<'_, H>, &ValueCacheKey<'static, H>>(key) - }) - } - /// Update the cache with the `added` values and the `accessed` values. /// /// The `added` values are the ones that have been collected by doing operations on the trie and /// now should be stored in the shared cache. The `accessed` values are only referenced by the - /// [`ValueCacheKeyHash`] and represent the values that were retrieved from this shared cache - /// through [`Self::get`]. These `accessed` values are being put to the front of the internal - /// [`LruCache`] like the `added` ones. - /// - /// After the internal [`LruCache`] was updated, it is ensured that the internal [`LruCache`] is - /// inside its bounds ([`Self::maximum_size_in_bytes`]). + /// [`ValueCacheKeyHash`] and represent the values that were retrieved from this shared cache. + /// These `accessed` values are being put to the front of the internal [`LruMap`] like the + /// `added` ones. pub fn update( &mut self, - added: impl IntoIterator, CachedValue)>, + added: impl IntoIterator, CachedValue)>, accessed: impl IntoIterator, ) { - // The base size in memory per ([`ValueCacheKey`], [`CachedValue`]). - let base_size = mem::size_of::>() + mem::size_of::>(); - let known_keys_entry_size = mem::size_of::>(); - - let update_size_in_bytes = - |size_in_bytes: &mut usize, r_key: Arc<[u8]>, known_keys: &mut HashSet>| { - // If the `strong_count == 2`, it means this is the last instance of the key. - // One being `r_key` and the other being stored in `known_storage_keys`. - let last_instance = Arc::strong_count(&r_key) == 2; - - let key_len = if last_instance { - known_keys.remove(&r_key); - r_key.len() + known_keys_entry_size - } else { - // The key is still in `keys`, because it is still used by another - // `ValueCacheKey`. - 0 - }; - - if let Some(new_size_in_bytes) = size_in_bytes.checked_sub(key_len + base_size) { - *size_in_bytes = new_size_in_bytes; - } else { - *size_in_bytes = 0; - tracing::error!(target: LOG_TARGET, "`SharedValueCache` underflow detected!",); - } - }; - - accessed.into_iter().for_each(|key| { - // Access every node in the lru to put it to the front. - // As we are using the `Hash` variant here, it may leads to putting the wrong value to - // the top. However, the only consequence of this is that we may prune a recently used - // value to early. - self.lru.get(&ValueCacheKey::Hash { - hash: key, - _i_read_the_documentation: IReadTheDocumentation(()), - }); - }); - - added.into_iter().for_each(|(key, value)| { - let (storage_root, storage_key, key_hash) = match key { - ValueCacheKey::Hash { .. } => { - // Ignore the hash variant and try the next. - tracing::error!( - target: LOG_TARGET, - "`SharedValueCached::update` was called with a key to add \ - that uses the `Hash` variant. This would lead to potential hash collision!", - ); - return - }, - ValueCacheKey::Ref { storage_key, storage_root, hash } => - (storage_root, storage_key.into(), hash), - ValueCacheKey::Value { storage_root, storage_key, hash } => - (storage_root, storage_key, hash), - }; - - let (size_update, storage_key) = - match self.known_storage_keys.entry(storage_key.clone()) { - SetEntry::Vacant(v) => { - let len = v.get().len(); - v.insert(); - - // If the key was unknown, we need to also take its length and the size of - // the entry of `known_keys` into account. - (len + base_size + known_keys_entry_size, storage_key) - }, - SetEntry::Occupied(o) => { - // Key is known - (base_size, o.get().clone()) - }, - }; - - self.size_in_bytes += size_update; - - if let Some((r_key, _)) = self - .lru - .push(ValueCacheKey::Value { storage_key, storage_root, hash: key_hash }, value) - { - if let ValueCacheKey::Value { storage_key, .. } = r_key { - update_size_in_bytes( - &mut self.size_in_bytes, - storage_key, - &mut self.known_storage_keys, - ); - } - } + let mut access_count = 0; + let mut add_count = 0; - // Directly ensure that we respect the maximum size. By doing it directly here we - // ensure that the internal map of the [`LruCache`] doesn't grow too much. - while self.maximum_cache_size.exceeds(self.size_in_bytes) { - // This should always be `Some(_)`, otherwise something is wrong! - if let Some((r_key, _)) = self.lru.pop_lru() { - if let ValueCacheKey::Value { storage_key, .. } = r_key { - update_size_in_bytes( - &mut self.size_in_bytes, - storage_key, - &mut self.known_storage_keys, - ); - } - } + for hash in accessed { + // Access every node in the map to put it to the front. + // + // Since we are only comparing the hashes here it may lead us to promoting the wrong + // values as the most recently accessed ones. However this is harmless as the only + // consequence is that we may accidentally prune a recently used value too early. + self.lru.get_by_hash(hash.raw(), |existing_key, _| existing_key.hash == hash); + access_count += 1; + } + + // Insert all of the new items which were *not* found in the shared cache. + // + // Limit how many items we'll replace in the shared cache in one go so that + // we don't evict the whole shared cache nor we keep spinning our wheels + // evicting items which we've added ourselves in previous iterations of this loop. + + self.lru.limiter_mut().items_evicted = 0; + self.lru.limiter_mut().max_items_evicted = + self.lru.len() * 100 / super::SHARED_VALUE_CACHE_MAX_REPLACE_PERCENT; + + for (key, value) in added { + self.lru.insert(key, value); + add_count += 1; + + if self.lru.limiter().items_evicted > self.lru.limiter().max_items_evicted { + // Stop when we've evicted a big enough chunk of the shared cache. + break } - }); + } + + tracing::debug!( + target: super::LOG_TARGET, + "Updated the shared value cache: {} accesses, {} new values, {}/{} evicted (length = {}, known_storage_keys = {}, inline size={}/{}, heap size={}/{})", + access_count, + add_count, + self.lru.limiter().items_evicted, + self.lru.limiter().max_items_evicted, + self.lru.len(), + self.lru.limiter().known_storage_keys.len(), + self.lru.memory_usage(), + self.lru.limiter().max_inline_size, + self.lru.limiter().heap_size, + self.lru.limiter().max_heap_size + ); } /// Reset the cache. fn reset(&mut self) { - self.size_in_bytes = 0; self.lru.clear(); - self.known_storage_keys.clear(); } } @@ -477,6 +571,7 @@ pub(super) struct SharedTrieCacheInner { impl SharedTrieCacheInner { /// Returns a reference to the [`SharedValueCache`]. + #[cfg(test)] pub(super) fn value_cache(&self) -> &SharedValueCache { &self.value_cache } @@ -487,6 +582,7 @@ impl SharedTrieCacheInner { } /// Returns a reference to the [`SharedNodeCache`]. + #[cfg(test)] pub(super) fn node_cache(&self) -> &SharedNodeCache { &self.node_cache } @@ -517,23 +613,50 @@ impl Clone for SharedTrieCache { impl SharedTrieCache { /// Create a new [`SharedTrieCache`]. pub fn new(cache_size: CacheSize) -> Self { - let (node_cache_size, value_cache_size) = match cache_size { - CacheSize::Maximum(max) => { - // Allocate 20% for the value cache. - let value_cache_size_in_bytes = (max as f32 * 0.20) as usize; - - ( - CacheSize::Maximum(max - value_cache_size_in_bytes), - CacheSize::Maximum(value_cache_size_in_bytes), - ) - }, - CacheSize::Unlimited => (CacheSize::Unlimited, CacheSize::Unlimited), - }; + let total_budget = cache_size.0; + + // Split our memory budget between the two types of caches. + let value_cache_budget = (total_budget as f32 * 0.20) as usize; // 20% for the value cache + let node_cache_budget = total_budget - value_cache_budget; // 80% for the node cache + + // Split our memory budget between what we'll be holding inline in the map, + // and what we'll be holding on the heap. + let value_cache_inline_budget = (value_cache_budget as f32 * 0.70) as usize; + let node_cache_inline_budget = (node_cache_budget as f32 * 0.70) as usize; + + // Calculate how much memory the maps will be allowed to hold inline given our budget. + let value_cache_max_inline_size = + SharedValueCacheMap::::memory_usage_for_memory_budget( + value_cache_inline_budget, + ); + + let node_cache_max_inline_size = + SharedNodeCacheMap::::memory_usage_for_memory_budget(node_cache_inline_budget); + + // And this is how much data we'll at most keep on the heap for each cache. + let value_cache_max_heap_size = value_cache_budget - value_cache_max_inline_size; + let node_cache_max_heap_size = node_cache_budget - node_cache_max_inline_size; + + tracing::debug!( + target: super::LOG_TARGET, + "Configured a shared trie cache with a budget of ~{} bytes (node_cache_max_inline_size = {}, node_cache_max_heap_size = {}, value_cache_max_inline_size = {}, value_cache_max_heap_size = {})", + total_budget, + node_cache_max_inline_size, + node_cache_max_heap_size, + value_cache_max_inline_size, + value_cache_max_heap_size, + ); Self { inner: Arc::new(RwLock::new(SharedTrieCacheInner { - node_cache: SharedNodeCache::new(node_cache_size), - value_cache: SharedValueCache::new(value_cache_size), + node_cache: SharedNodeCache::new( + node_cache_max_inline_size, + node_cache_max_heap_size, + ), + value_cache: SharedValueCache::new( + value_cache_max_inline_size, + value_cache_max_heap_size, + ), })), } } @@ -544,16 +667,50 @@ impl SharedTrieCache { shared: self.clone(), node_cache: Default::default(), value_cache: Default::default(), - shared_node_cache_access: Default::default(), - shared_value_cache_access: Default::default(), + shared_value_cache_access: Mutex::new(super::ValueAccessSet::with_hasher( + schnellru::ByLength::new(super::SHARED_VALUE_CACHE_MAX_PROMOTED_KEYS), + Default::default(), + )), + stats: Default::default(), } } + /// Get a copy of the node for `key`. + /// + /// This will temporarily lock the shared cache for reading. + /// + /// This doesn't change the least recently order in the internal [`LruMap`]. + #[inline] + pub fn peek_node(&self, key: &H::Out) -> Option> { + self.inner.read().node_cache.lru.peek(key).cloned() + } + + /// Get a copy of the [`CachedValue`] for `key`. + /// + /// This will temporarily lock the shared cache for reading. + /// + /// This doesn't reorder any of the elements in the internal [`LruMap`]. + pub fn peek_value_by_hash( + &self, + hash: ValueCacheKeyHash, + storage_root: &H::Out, + storage_key: &[u8], + ) -> Option> { + self.inner + .read() + .value_cache + .lru + .peek_by_hash(hash.0, |existing_key, _| existing_key.is_eq(storage_root, storage_key)) + .cloned() + } + /// Returns the used memory size of this cache in bytes. pub fn used_memory_size(&self) -> usize { let inner = self.inner.read(); - let value_cache_size = inner.value_cache.size_in_bytes; - let node_cache_size = inner.node_cache.size_in_bytes; + let value_cache_size = + inner.value_cache.lru.memory_usage() + inner.value_cache.lru.limiter().heap_size; + let node_cache_size = + inner.node_cache.lru.memory_usage() + inner.node_cache.lru.limiter().heap_size; node_cache_size + value_cache_size } @@ -575,13 +732,19 @@ impl SharedTrieCache { } /// Returns the read locked inner. - pub(super) fn read_lock_inner(&self) -> RwLockReadGuard<'_, SharedTrieCacheInner> { + #[cfg(test)] + pub(super) fn read_lock_inner( + &self, + ) -> parking_lot::RwLockReadGuard<'_, SharedTrieCacheInner> { self.inner.read() } /// Returns the write locked inner. - pub(super) fn write_lock_inner(&self) -> RwLockWriteGuard<'_, SharedTrieCacheInner> { - self.inner.write() + pub(super) fn write_lock_inner(&self) -> Option>> { + // This should never happen, but we *really* don't want to deadlock. So let's have it + // timeout, just in case. At worst it'll do nothing, and at best it'll avert a catastrophe + // and notify us that there's a problem. + self.inner.try_write_for(super::SHARED_CACHE_WRITE_LOCK_TIMEOUT) } } @@ -592,12 +755,7 @@ mod tests { #[test] fn shared_value_cache_works() { - let base_size = mem::size_of::>() + mem::size_of::>(); - let arc_size = mem::size_of::>(); - - let mut cache = SharedValueCache::::new(CacheSize::Maximum( - (base_size + arc_size + 10) * 10, - )); + let mut cache = SharedValueCache::::new(usize::MAX, 10 * 10); let key = vec![0; 10]; @@ -613,65 +771,85 @@ mod tests { ); // Ensure that the basics are working - assert_eq!(1, cache.known_storage_keys.len()); - assert_eq!(3, Arc::strong_count(cache.known_storage_keys.get(&key[..]).unwrap())); - assert_eq!(base_size * 2 + key.len() + arc_size, cache.size_in_bytes); + assert_eq!(1, cache.lru.limiter_mut().known_storage_keys.len()); + assert_eq!( + 3, // Two instances inside the cache + one extra in `known_storage_keys`. + Arc::strong_count(cache.lru.limiter_mut().known_storage_keys.get(&key[..]).unwrap()) + ); + assert_eq!(key.len(), cache.lru.limiter().heap_size); + assert_eq!(cache.lru.len(), 2); + assert_eq!(cache.lru.peek_newest().unwrap().0.storage_root, root1); + assert_eq!(cache.lru.peek_oldest().unwrap().0.storage_root, root0); + assert!(cache.lru.limiter().heap_size <= cache.lru.limiter().max_heap_size); + assert_eq!(cache.lru.limiter().heap_size, 10); // Just accessing a key should not change anything on the size and number of entries. cache.update(vec![], vec![ValueCacheKey::hash_data(&key[..], &root0)]); - assert_eq!(1, cache.known_storage_keys.len()); - assert_eq!(3, Arc::strong_count(cache.known_storage_keys.get(&key[..]).unwrap())); - assert_eq!(base_size * 2 + key.len() + arc_size, cache.size_in_bytes); - - // Add 9 other entries and this should move out the key for `root1`. + assert_eq!(1, cache.lru.limiter_mut().known_storage_keys.len()); + assert_eq!( + 3, + Arc::strong_count(cache.lru.limiter_mut().known_storage_keys.get(&key[..]).unwrap()) + ); + assert_eq!(key.len(), cache.lru.limiter().heap_size); + assert_eq!(cache.lru.len(), 2); + assert_eq!(cache.lru.peek_newest().unwrap().0.storage_root, root0); + assert_eq!(cache.lru.peek_oldest().unwrap().0.storage_root, root1); + assert!(cache.lru.limiter().heap_size <= cache.lru.limiter().max_heap_size); + assert_eq!(cache.lru.limiter().heap_size, 10); + + // Updating the cache again with exactly the same data should not change anything. cache.update( - (1..10) + vec![ + (ValueCacheKey::new_value(&key[..], root1), CachedValue::NonExisting), + (ValueCacheKey::new_value(&key[..], root0), CachedValue::NonExisting), + ], + vec![], + ); + assert_eq!(1, cache.lru.limiter_mut().known_storage_keys.len()); + assert_eq!( + 3, + Arc::strong_count(cache.lru.limiter_mut().known_storage_keys.get(&key[..]).unwrap()) + ); + assert_eq!(key.len(), cache.lru.limiter().heap_size); + assert_eq!(cache.lru.len(), 2); + assert_eq!(cache.lru.peek_newest().unwrap().0.storage_root, root0); + assert_eq!(cache.lru.peek_oldest().unwrap().0.storage_root, root1); + assert!(cache.lru.limiter().heap_size <= cache.lru.limiter().max_heap_size); + assert_eq!(cache.lru.limiter().items_evicted, 0); + assert_eq!(cache.lru.limiter().heap_size, 10); + + // Add 10 other entries and this should move out two of the initial entries. + cache.update( + (1..11) .map(|i| vec![i; 10]) .map(|key| (ValueCacheKey::new_value(&key[..], root0), CachedValue::NonExisting)), vec![], ); - assert_eq!(10, cache.known_storage_keys.len()); - assert_eq!(2, Arc::strong_count(cache.known_storage_keys.get(&key[..]).unwrap())); - assert_eq!((base_size + key.len() + arc_size) * 10, cache.size_in_bytes); + assert_eq!(cache.lru.limiter().items_evicted, 2); + assert_eq!(10, cache.lru.len()); + assert_eq!(10, cache.lru.limiter_mut().known_storage_keys.len()); + assert!(cache.lru.limiter_mut().known_storage_keys.get(&key[..]).is_none()); + assert_eq!(key.len() * 10, cache.lru.limiter().heap_size); + assert_eq!(cache.lru.len(), 10); + assert!(cache.lru.limiter().heap_size <= cache.lru.limiter().max_heap_size); + assert_eq!(cache.lru.limiter().heap_size, 100); + assert!(matches!( - cache.get(&ValueCacheKey::new_ref(&key, root0)).unwrap(), + cache.lru.peek(&ValueCacheKey::new_value(&[1; 10][..], root0)).unwrap(), CachedValue::::NonExisting )); - assert!(cache.get(&ValueCacheKey::new_ref(&key, root1)).is_none()); + + assert!(cache.lru.peek(&ValueCacheKey::new_value(&[1; 10][..], root1)).is_none(),); + + assert!(cache.lru.peek(&ValueCacheKey::new_value(&key[..], root0)).is_none()); + assert!(cache.lru.peek(&ValueCacheKey::new_value(&key[..], root1)).is_none()); cache.update( vec![(ValueCacheKey::new_value(vec![10; 10], root0), CachedValue::NonExisting)], vec![], ); - assert!(cache.known_storage_keys.get(&key[..]).is_none()); - } - - #[test] - fn value_cache_key_eq_works() { - let storage_key = &b"something"[..]; - let storage_key2 = &b"something2"[..]; - let storage_root = Hash::random(); - - let value = ValueCacheKey::new_value(storage_key, storage_root); - // Ref gets the same hash, but a different storage key - let ref_ = - ValueCacheKey::Ref { storage_root, storage_key: storage_key2, hash: value.get_hash() }; - let hash = ValueCacheKey::Hash { - hash: value.get_hash(), - _i_read_the_documentation: IReadTheDocumentation(()), - }; - - // Ensure that the hash variants is equal to `value`, `ref_` and itself. - assert!(hash == value); - assert!(value == hash); - assert!(hash == ref_); - assert!(ref_ == hash); - assert!(hash == hash); - - // But when we compare `value` and `ref_` the different storage key is detected. - assert!(value != ref_); - assert!(ref_ != value); + assert!(cache.lru.limiter_mut().known_storage_keys.get(&key[..]).is_none()); } } From 64a1a360eaf5c467c0f836a1ca4ae4cde8663e43 Mon Sep 17 00:00:00 2001 From: Andrei Sandu <54316454+sandreim@users.noreply.github.com> Date: Thu, 26 Jan 2023 08:22:51 +0200 Subject: [PATCH 057/558] Add task type label to metrics (#13240) Signed-off-by: Andrei Sandu Signed-off-by: Andrei Sandu --- client/service/src/task_manager/mod.rs | 41 +++++++++++++++++++------- 1 file changed, 30 insertions(+), 11 deletions(-) diff --git a/client/service/src/task_manager/mod.rs b/client/service/src/task_manager/mod.rs index d79212257..f2987e684 100644 --- a/client/service/src/task_manager/mod.rs +++ b/client/service/src/task_manager/mod.rs @@ -129,12 +129,20 @@ impl SpawnTaskHandle { GroupName::Default => DEFAULT_GROUP_NAME, }; + let task_type_label = match task_type { + TaskType::Blocking => "blocking", + TaskType::Async => "async", + }; + // Note that we increase the started counter here and not within the future. This way, // we could properly visualize on Prometheus situations where the spawning doesn't work. if let Some(metrics) = &self.metrics { - metrics.tasks_spawned.with_label_values(&[name, group]).inc(); + metrics.tasks_spawned.with_label_values(&[name, group, task_type_label]).inc(); // We do a dummy increase in order for the task to show up in metrics. - metrics.tasks_ended.with_label_values(&[name, "finished", group]).inc_by(0); + metrics + .tasks_ended + .with_label_values(&[name, "finished", group, task_type_label]) + .inc_by(0); } let future = async move { @@ -145,8 +153,10 @@ impl SpawnTaskHandle { if let Some(metrics) = metrics { // Add some wrappers around `task`. let task = { - let poll_duration = metrics.poll_duration.with_label_values(&[name, group]); - let poll_start = metrics.poll_start.with_label_values(&[name, group]); + let poll_duration = + metrics.poll_duration.with_label_values(&[name, group, task_type_label]); + let poll_start = + metrics.poll_start.with_label_values(&[name, group, task_type_label]); let inner = prometheus_future::with_poll_durations(poll_duration, poll_start, task); // The logic of `AssertUnwindSafe` here is ok considering that we throw @@ -157,15 +167,24 @@ impl SpawnTaskHandle { match select(on_exit, task).await { Either::Right((Err(payload), _)) => { - metrics.tasks_ended.with_label_values(&[name, "panic", group]).inc(); + metrics + .tasks_ended + .with_label_values(&[name, "panic", group, task_type_label]) + .inc(); panic::resume_unwind(payload) }, Either::Right((Ok(()), _)) => { - metrics.tasks_ended.with_label_values(&[name, "finished", group]).inc(); + metrics + .tasks_ended + .with_label_values(&[name, "finished", group, task_type_label]) + .inc(); }, Either::Left(((), _)) => { // The `on_exit` has triggered. - metrics.tasks_ended.with_label_values(&[name, "interrupted", group]).inc(); + metrics + .tasks_ended + .with_label_values(&[name, "interrupted", group, task_type_label]) + .inc(); }, } } else { @@ -433,28 +452,28 @@ impl Metrics { buckets: exponential_buckets(0.001, 4.0, 9) .expect("function parameters are constant and always valid; qed"), }, - &["task_name", "task_group"] + &["task_name", "task_group", "kind"] )?, registry)?, poll_start: register(CounterVec::new( Opts::new( "substrate_tasks_polling_started_total", "Total number of times we started invoking Future::poll" ), - &["task_name", "task_group"] + &["task_name", "task_group", "kind"] )?, registry)?, tasks_spawned: register(CounterVec::new( Opts::new( "substrate_tasks_spawned_total", "Total number of tasks that have been spawned on the Service" ), - &["task_name", "task_group"] + &["task_name", "task_group", "kind"] )?, registry)?, tasks_ended: register(CounterVec::new( Opts::new( "substrate_tasks_ended_total", "Total number of tasks for which Future::poll has returned Ready(()) or panicked" ), - &["task_name", "reason", "task_group"] + &["task_name", "reason", "task_group", "kind"] )?, registry)?, }) } From f4bc99303e6faa0ae0461b4606c1699b4463ff4b Mon Sep 17 00:00:00 2001 From: Michal Kucharczyk <1728078+michalkucharczyk@users.noreply.github.com> Date: Thu, 26 Jan 2023 08:59:18 +0100 Subject: [PATCH 058/558] upgrade nix to 0.26.1 (#13230) --- Cargo.lock | 15 +-------------- bin/node/cli/Cargo.toml | 2 +- 2 files changed, 2 insertions(+), 15 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 7c412030d..43f67695f 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4640,19 +4640,6 @@ dependencies = [ "tokio", ] -[[package]] -name = "nix" -version = "0.23.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f3790c00a0150112de0f4cd161e3d7fc4b2d8a5542ffc35f099a2562aecb35c" -dependencies = [ - "bitflags", - "cc", - "cfg-if", - "libc", - "memoffset 0.6.5", -] - [[package]] name = "nix" version = "0.24.3" @@ -4731,7 +4718,7 @@ dependencies = [ "jsonrpsee", "kitchensink-runtime", "log", - "nix 0.23.2", + "nix 0.26.1", "node-executor", "node-inspect", "node-primitives", diff --git a/bin/node/cli/Cargo.toml b/bin/node/cli/Cargo.toml index 8a883ee01..b55b35c05 100644 --- a/bin/node/cli/Cargo.toml +++ b/bin/node/cli/Cargo.toml @@ -117,7 +117,7 @@ sp-blockchain = { version = "4.0.0-dev", path = "../../../primitives/blockchain" futures = "0.3.21" tempfile = "3.1.0" assert_cmd = "2.0.2" -nix = "0.23" +nix = { version = "0.26.1", features = ["signal"] } serde_json = "1.0" regex = "1.6.0" platforms = "2.0" From 2dbf62591fd35725c6e39f8ddfd27c3695dab594 Mon Sep 17 00:00:00 2001 From: Stephen Shelton Date: Thu, 26 Jan 2023 07:39:20 -0700 Subject: [PATCH 059/558] Add WeightToFee and LengthToFee impls to transaction-payment Runtime API (#13110) * Add WeightToFee and LengthToFee impls to RPC * Remove RPC additions * Update frame/transaction-payment/rpc/runtime-api/src/lib.rs Co-authored-by: joe petrowski <25483142+joepetrowski@users.noreply.github.com> * Add comments to length_to_fee and weight_to_fee Co-authored-by: joe petrowski <25483142+joepetrowski@users.noreply.github.com> Co-authored-by: Oliver Tale-Yazdi --- bin/node-template/runtime/src/lib.rs | 12 ++++++++++++ bin/node/runtime/src/lib.rs | 12 ++++++++++++ frame/transaction-payment/rpc/runtime-api/src/lib.rs | 12 ++++++++++-- frame/transaction-payment/src/lib.rs | 7 +++++-- 4 files changed, 39 insertions(+), 4 deletions(-) diff --git a/bin/node-template/runtime/src/lib.rs b/bin/node-template/runtime/src/lib.rs index f4372af52..baba5d9b0 100644 --- a/bin/node-template/runtime/src/lib.rs +++ b/bin/node-template/runtime/src/lib.rs @@ -477,6 +477,12 @@ impl_runtime_apis! { ) -> pallet_transaction_payment::FeeDetails { TransactionPayment::query_fee_details(uxt, len) } + fn query_weight_to_fee(weight: Weight) -> Balance { + TransactionPayment::weight_to_fee(weight) + } + fn query_length_to_fee(length: u32) -> Balance { + TransactionPayment::length_to_fee(length) + } } impl pallet_transaction_payment_rpc_runtime_api::TransactionPaymentCallApi @@ -494,6 +500,12 @@ impl_runtime_apis! { ) -> pallet_transaction_payment::FeeDetails { TransactionPayment::query_call_fee_details(call, len) } + fn query_weight_to_fee(weight: Weight) -> Balance { + TransactionPayment::weight_to_fee(weight) + } + fn query_length_to_fee(length: u32) -> Balance { + TransactionPayment::length_to_fee(length) + } } #[cfg(feature = "runtime-benchmarks")] diff --git a/bin/node/runtime/src/lib.rs b/bin/node/runtime/src/lib.rs index 1f85d118e..0121f1ee3 100644 --- a/bin/node/runtime/src/lib.rs +++ b/bin/node/runtime/src/lib.rs @@ -2120,6 +2120,12 @@ impl_runtime_apis! { fn query_fee_details(uxt: ::Extrinsic, len: u32) -> FeeDetails { TransactionPayment::query_fee_details(uxt, len) } + fn query_weight_to_fee(weight: Weight) -> Balance { + TransactionPayment::weight_to_fee(weight) + } + fn query_length_to_fee(length: u32) -> Balance { + TransactionPayment::length_to_fee(length) + } } impl pallet_transaction_payment_rpc_runtime_api::TransactionPaymentCallApi @@ -2131,6 +2137,12 @@ impl_runtime_apis! { fn query_call_fee_details(call: RuntimeCall, len: u32) -> FeeDetails { TransactionPayment::query_call_fee_details(call, len) } + fn query_weight_to_fee(weight: Weight) -> Balance { + TransactionPayment::weight_to_fee(weight) + } + fn query_length_to_fee(length: u32) -> Balance { + TransactionPayment::length_to_fee(length) + } } impl pallet_mmr::primitives::MmrApi< diff --git a/frame/transaction-payment/rpc/runtime-api/src/lib.rs b/frame/transaction-payment/rpc/runtime-api/src/lib.rs index 10fd2a9e6..760f9e369 100644 --- a/frame/transaction-payment/rpc/runtime-api/src/lib.rs +++ b/frame/transaction-payment/rpc/runtime-api/src/lib.rs @@ -25,7 +25,7 @@ use sp_runtime::traits::MaybeDisplay; pub use pallet_transaction_payment::{FeeDetails, InclusionFee, RuntimeDispatchInfo}; sp_api::decl_runtime_apis! { - #[api_version(2)] + #[api_version(3)] pub trait TransactionPaymentApi where Balance: Codec + MaybeDisplay, { @@ -33,9 +33,11 @@ sp_api::decl_runtime_apis! { fn query_info(uxt: Block::Extrinsic, len: u32) -> RuntimeDispatchInfo; fn query_info(uxt: Block::Extrinsic, len: u32) -> RuntimeDispatchInfo; fn query_fee_details(uxt: Block::Extrinsic, len: u32) -> FeeDetails; + fn query_weight_to_fee(weight: sp_weights::Weight) -> Balance; + fn query_length_to_fee(length: u32) -> Balance; } - #[api_version(2)] + #[api_version(3)] pub trait TransactionPaymentCallApi where Balance: Codec + MaybeDisplay, @@ -46,5 +48,11 @@ sp_api::decl_runtime_apis! { /// Query fee details of a given encoded `Call`. fn query_call_fee_details(call: Call, len: u32) -> FeeDetails; + + /// Query the output of the current `WeightToFee` given some input. + fn query_weight_to_fee(weight: sp_weights::Weight) -> Balance; + + /// Query the output of the current `LengthToFee` given some input. + fn query_length_to_fee(length: u32) -> Balance; } } diff --git a/frame/transaction-payment/src/lib.rs b/frame/transaction-payment/src/lib.rs index 13adbf89c..fb4381c52 100644 --- a/frame/transaction-payment/src/lib.rs +++ b/frame/transaction-payment/src/lib.rs @@ -612,11 +612,14 @@ where } } - fn length_to_fee(length: u32) -> BalanceOf { + /// Compute the length portion of a fee by invoking the configured `LengthToFee` impl. + pub fn length_to_fee(length: u32) -> BalanceOf { T::LengthToFee::weight_to_fee(&Weight::from_ref_time(length as u64)) } - fn weight_to_fee(weight: Weight) -> BalanceOf { + /// Compute the unadjusted portion of the weight fee by invoking the configured `WeightToFee` + /// impl. Note that the input `weight` is capped by the maximum block weight before computation. + pub fn weight_to_fee(weight: Weight) -> BalanceOf { // cap the weight to the maximum defined in runtime, otherwise it will be the // `Bounded` maximum of its data type, which is not desired. let capped_weight = weight.min(T::BlockWeights::get().max_block); From 495cc0e2b3b06bd3f6b746416fc0d635adcefa7c Mon Sep 17 00:00:00 2001 From: Oliver Tale-Yazdi Date: Thu, 26 Jan 2023 17:13:38 +0100 Subject: [PATCH 060/558] Correct arithmetical semantic of `PerDispatchClass` (#13194) * Fix PerDispatchClass arithmetic Signed-off-by: Oliver Tale-Yazdi * Test PerDispatchClass arithmetic Signed-off-by: Oliver Tale-Yazdi * Add helpers for Weight Signed-off-by: Oliver Tale-Yazdi * Remove stale doc Signed-off-by: Oliver Tale-Yazdi * Dont mention Polkadot in Substrate Signed-off-by: Oliver Tale-Yazdi * Tests Signed-off-by: Oliver Tale-Yazdi * fmt Signed-off-by: Oliver Tale-Yazdi * Remove saturating_ prefix Signed-off-by: Oliver Tale-Yazdi * Fix tests Signed-off-by: Oliver Tale-Yazdi * Fix usage Signed-off-by: Oliver Tale-Yazdi Signed-off-by: Oliver Tale-Yazdi --- frame/support/src/dispatch.rs | 209 ++++++++++++++++++-- frame/support/src/weights.rs | 10 +- frame/system/src/extensions/check_weight.rs | 6 +- frame/system/src/lib.rs | 2 +- primitives/weights/src/lib.rs | 7 - primitives/weights/src/weight_v2.rs | 109 ++++++++++ 6 files changed, 307 insertions(+), 36 deletions(-) diff --git a/frame/support/src/dispatch.rs b/frame/support/src/dispatch.rs index 93cf08c13..291abdd12 100644 --- a/frame/support/src/dispatch.rs +++ b/frame/support/src/dispatch.rs @@ -423,33 +423,35 @@ impl PerDispatchClass { impl PerDispatchClass { /// Returns the total weight consumed by all extrinsics in the block. + /// + /// Saturates on overflow. pub fn total(&self) -> Weight { let mut sum = Weight::zero(); for class in DispatchClass::all() { - sum = sum.saturating_add(*self.get(*class)); + sum.saturating_accrue(*self.get(*class)); } sum } - /// Add some weight of a specific dispatch class, saturating at the numeric bounds of `Weight`. - pub fn add(&mut self, weight: Weight, class: DispatchClass) { - let value = self.get_mut(class); - *value = value.saturating_add(weight); + /// Add some weight to the given class. Saturates at the numeric bounds. + pub fn add(mut self, weight: Weight, class: DispatchClass) -> Self { + self.accrue(weight, class); + self + } + + /// Increase the weight of the given class. Saturates at the numeric bounds. + pub fn accrue(&mut self, weight: Weight, class: DispatchClass) { + self.get_mut(class).saturating_accrue(weight); } - /// Try to add some weight of a specific dispatch class, returning Err(()) if overflow would - /// occur. - pub fn checked_add(&mut self, weight: Weight, class: DispatchClass) -> Result<(), ()> { - let value = self.get_mut(class); - *value = value.checked_add(&weight).ok_or(())?; - Ok(()) + /// Try to increase the weight of the given class. Saturates at the numeric bounds. + pub fn checked_accrue(&mut self, weight: Weight, class: DispatchClass) -> Result<(), ()> { + self.get_mut(class).checked_accrue(weight).ok_or(()) } - /// Subtract some weight of a specific dispatch class, saturating at the numeric bounds of - /// `Weight`. - pub fn sub(&mut self, weight: Weight, class: DispatchClass) { - let value = self.get_mut(class); - *value = value.saturating_sub(weight); + /// Reduce the weight of the given class. Saturates at the numeric bounds. + pub fn reduce(&mut self, weight: Weight, class: DispatchClass) { + self.get_mut(class).saturating_reduce(weight); } } @@ -3693,3 +3695,178 @@ mod weight_tests { assert_eq!(extract_actual_pays_fee(&Ok((Some(1000), Pays::Yes).into()), &pre), Pays::No); } } + +#[cfg(test)] +mod per_dispatch_class_tests { + use super::*; + use sp_runtime::traits::Zero; + use DispatchClass::*; + + #[test] + fn add_works() { + let a = PerDispatchClass { + normal: (5, 10).into(), + operational: (20, 30).into(), + mandatory: Weight::MAX, + }; + assert_eq!( + a.clone() + .add((20, 5).into(), Normal) + .add((10, 10).into(), Operational) + .add((u64::MAX, 3).into(), Mandatory), + PerDispatchClass { + normal: (25, 15).into(), + operational: (30, 40).into(), + mandatory: Weight::MAX + } + ); + let b = a + .add(Weight::MAX, Normal) + .add(Weight::MAX, Operational) + .add(Weight::MAX, Mandatory); + assert_eq!( + b, + PerDispatchClass { + normal: Weight::MAX, + operational: Weight::MAX, + mandatory: Weight::MAX + } + ); + assert_eq!(b.total(), Weight::MAX); + } + + #[test] + fn accrue_works() { + let mut a = PerDispatchClass::default(); + + a.accrue((10, 15).into(), Normal); + assert_eq!(a.normal, (10, 15).into()); + assert_eq!(a.total(), (10, 15).into()); + + a.accrue((20, 25).into(), Operational); + assert_eq!(a.operational, (20, 25).into()); + assert_eq!(a.total(), (30, 40).into()); + + a.accrue((30, 35).into(), Mandatory); + assert_eq!(a.mandatory, (30, 35).into()); + assert_eq!(a.total(), (60, 75).into()); + + a.accrue((u64::MAX, 10).into(), Operational); + assert_eq!(a.operational, (u64::MAX, 35).into()); + assert_eq!(a.total(), (u64::MAX, 85).into()); + + a.accrue((10, u64::MAX).into(), Normal); + assert_eq!(a.normal, (20, u64::MAX).into()); + assert_eq!(a.total(), Weight::MAX); + } + + #[test] + fn reduce_works() { + let mut a = PerDispatchClass { + normal: (10, u64::MAX).into(), + mandatory: (u64::MAX, 10).into(), + operational: (20, 20).into(), + }; + + a.reduce((5, 100).into(), Normal); + assert_eq!(a.normal, (5, u64::MAX - 100).into()); + assert_eq!(a.total(), (u64::MAX, u64::MAX - 70).into()); + + a.reduce((15, 5).into(), Operational); + assert_eq!(a.operational, (5, 15).into()); + assert_eq!(a.total(), (u64::MAX, u64::MAX - 75).into()); + + a.reduce((50, 0).into(), Mandatory); + assert_eq!(a.mandatory, (u64::MAX - 50, 10).into()); + assert_eq!(a.total(), (u64::MAX - 40, u64::MAX - 75).into()); + + a.reduce((u64::MAX, 100).into(), Operational); + assert!(a.operational.is_zero()); + assert_eq!(a.total(), (u64::MAX - 45, u64::MAX - 90).into()); + + a.reduce((5, u64::MAX).into(), Normal); + assert!(a.normal.is_zero()); + assert_eq!(a.total(), (u64::MAX - 50, 10).into()); + } + + #[test] + fn checked_accrue_works() { + let mut a = PerDispatchClass::default(); + + a.checked_accrue((1, 2).into(), Normal).unwrap(); + a.checked_accrue((3, 4).into(), Operational).unwrap(); + a.checked_accrue((5, 6).into(), Mandatory).unwrap(); + a.checked_accrue((7, 8).into(), Operational).unwrap(); + a.checked_accrue((9, 0).into(), Normal).unwrap(); + + assert_eq!( + a, + PerDispatchClass { + normal: (10, 2).into(), + operational: (10, 12).into(), + mandatory: (5, 6).into(), + } + ); + + a.checked_accrue((u64::MAX - 10, u64::MAX - 2).into(), Normal).unwrap(); + a.checked_accrue((0, 0).into(), Normal).unwrap(); + a.checked_accrue((1, 0).into(), Normal).unwrap_err(); + a.checked_accrue((0, 1).into(), Normal).unwrap_err(); + + assert_eq!( + a, + PerDispatchClass { + normal: Weight::MAX, + operational: (10, 12).into(), + mandatory: (5, 6).into(), + } + ); + } + + #[test] + fn checked_accrue_does_not_modify_on_error() { + let mut a = PerDispatchClass { + normal: 0.into(), + operational: Weight::MAX / 2 + 2.into(), + mandatory: 10.into(), + }; + + a.checked_accrue(Weight::MAX / 2, Operational).unwrap_err(); + a.checked_accrue(Weight::MAX - 9.into(), Mandatory).unwrap_err(); + a.checked_accrue(Weight::MAX, Normal).unwrap(); // This one works + + assert_eq!( + a, + PerDispatchClass { + normal: Weight::MAX, + operational: Weight::MAX / 2 + 2.into(), + mandatory: 10.into(), + } + ); + } + + #[test] + fn total_works() { + assert!(PerDispatchClass::default().total().is_zero()); + + assert_eq!( + PerDispatchClass { + normal: 0.into(), + operational: (10, 20).into(), + mandatory: (20, u64::MAX).into(), + } + .total(), + (30, u64::MAX).into() + ); + + assert_eq!( + PerDispatchClass { + normal: (u64::MAX - 10, 10).into(), + operational: (3, u64::MAX).into(), + mandatory: (4, u64::MAX).into(), + } + .total(), + (u64::MAX - 3, u64::MAX).into() + ); + } +} diff --git a/frame/support/src/weights.rs b/frame/support/src/weights.rs index 9ff49b97b..d52380b83 100644 --- a/frame/support/src/weights.rs +++ b/frame/support/src/weights.rs @@ -15,15 +15,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -//! Re-exports `sp-weights` public API, and contains benchmarked weight constants specific to -//! FRAME. -//! -//! Latest machine specification used to benchmark are: -//! - Digital Ocean: ubuntu-s-2vcpu-4gb-ams3-01 -//! - 2x Intel(R) Xeon(R) CPU E5-2650 v4 @ 2.20GHz -//! - 4GB RAM -//! - Ubuntu 19.10 (GNU/Linux 5.3.0-18-generic x86_64) -//! - rustc 1.42.0 (b8cedc004 2020-03-09) +//! Re-exports `sp-weights` public API, and contains benchmarked weight constants specific to FRAME. mod block_weights; mod extrinsic_weights; diff --git a/frame/system/src/extensions/check_weight.rs b/frame/system/src/extensions/check_weight.rs index 757b2197b..590d05474 100644 --- a/frame/system/src/extensions/check_weight.rs +++ b/frame/system/src/extensions/check_weight.rs @@ -135,10 +135,10 @@ where // add the weight. If class is unlimited, use saturating add instead of checked one. if limit_per_class.max_total.is_none() && limit_per_class.reserved.is_none() { - all_weight.add(extrinsic_weight, info.class) + all_weight.accrue(extrinsic_weight, info.class) } else { all_weight - .checked_add(extrinsic_weight, info.class) + .checked_accrue(extrinsic_weight, info.class) .map_err(|_| InvalidTransaction::ExhaustsResources)?; } @@ -229,7 +229,7 @@ where let unspent = post_info.calc_unspent(info); if unspent.any_gt(Weight::zero()) { crate::BlockWeight::::mutate(|current_weight| { - current_weight.sub(unspent, info.class); + current_weight.reduce(unspent, info.class); }) } diff --git a/frame/system/src/lib.rs b/frame/system/src/lib.rs index 4bd5cf629..c5d170600 100644 --- a/frame/system/src/lib.rs +++ b/frame/system/src/lib.rs @@ -1321,7 +1321,7 @@ impl Pallet { /// Another potential use-case could be for the `on_initialize` and `on_finalize` hooks. pub fn register_extra_weight_unchecked(weight: Weight, class: DispatchClass) { BlockWeight::::mutate(|current_weight| { - current_weight.add(weight, class); + current_weight.accrue(weight, class); }); } diff --git a/primitives/weights/src/lib.rs b/primitives/weights/src/lib.rs index 928080d13..59852815d 100644 --- a/primitives/weights/src/lib.rs +++ b/primitives/weights/src/lib.rs @@ -16,13 +16,6 @@ // limitations under the License. //! # Primitives for transaction weighting. -//! -//! Latest machine specification used to benchmark are: -//! - Digital Ocean: ubuntu-s-2vcpu-4gb-ams3-01 -//! - 2x Intel(R) Xeon(R) CPU E5-2650 v4 @ 2.20GHz -//! - 4GB RAM -//! - Ubuntu 19.10 (GNU/Linux 5.3.0-18-generic x86_64) -//! - rustc 1.42.0 (b8cedc004 2020-03-09) #![cfg_attr(not(feature = "std"), no_std)] diff --git a/primitives/weights/src/weight_v2.rs b/primitives/weights/src/weight_v2.rs index 2933d8009..33e61b695 100644 --- a/primitives/weights/src/weight_v2.rs +++ b/primitives/weights/src/weight_v2.rs @@ -117,6 +117,11 @@ impl Weight { Self { ref_time, proof_size } } + /// Construct [`Weight`] from the same weight for all parts. + pub const fn from_all(value: u64) -> Self { + Self { ref_time: value, proof_size: value } + } + /// Saturating [`Weight`] addition. Computes `self + rhs`, saturating at the numeric bounds of /// all fields instead of overflowing. pub const fn saturating_add(self, rhs: Self) -> Self { @@ -167,6 +172,11 @@ impl Weight { *self = self.saturating_add(amount); } + /// Reduce [`Weight`] by `amount` via saturating subtraction. + pub fn saturating_reduce(&mut self, amount: Self) { + *self = self.saturating_sub(amount); + } + /// Checked [`Weight`] addition. Computes `self + rhs`, returning `None` if overflow occurred. pub const fn checked_add(&self, rhs: &Self) -> Option { let ref_time = match self.ref_time.checked_add(rhs.ref_time) { @@ -222,6 +232,16 @@ impl Weight { Some(Self { ref_time, proof_size }) } + /// Try to increase `self` by `amount` via checked addition. + pub fn checked_accrue(&mut self, amount: Self) -> Option<()> { + self.checked_add(&amount).map(|new_self| *self = new_self) + } + + /// Try to reduce `self` by `amount` via checked subtraction. + pub fn checked_reduce(&mut self, amount: Self) -> Option<()> { + self.checked_sub(&amount).map(|new_self| *self = new_self) + } + /// Return a [`Weight`] where all fields are zero. pub const fn zero() -> Self { Self { ref_time: 0, proof_size: 0 } @@ -352,6 +372,20 @@ where } } +#[cfg(any(test, feature = "std", feature = "runtime-benchmarks"))] +impl From for Weight { + fn from(value: u64) -> Self { + Self::from_parts(value, value) + } +} + +#[cfg(any(test, feature = "std", feature = "runtime-benchmarks"))] +impl From<(u64, u64)> for Weight { + fn from(value: (u64, u64)) -> Self { + Self::from_parts(value.0, value.1) + } +} + macro_rules! weight_mul_per_impl { ($($t:ty),* $(,)?) => { $( @@ -459,4 +493,79 @@ mod tests { assert!(!Weight::from_parts(0, 1).is_zero()); assert!(!Weight::MAX.is_zero()); } + + #[test] + fn from_parts_works() { + assert_eq!(Weight::from_parts(0, 0), Weight { ref_time: 0, proof_size: 0 }); + assert_eq!(Weight::from_parts(5, 5), Weight { ref_time: 5, proof_size: 5 }); + assert_eq!( + Weight::from_parts(u64::MAX, u64::MAX), + Weight { ref_time: u64::MAX, proof_size: u64::MAX } + ); + } + + #[test] + fn from_all_works() { + assert_eq!(Weight::from_all(0), Weight::from_parts(0, 0)); + assert_eq!(Weight::from_all(5), Weight::from_parts(5, 5)); + assert_eq!(Weight::from_all(u64::MAX), Weight::from_parts(u64::MAX, u64::MAX)); + } + + #[test] + fn from_u64_works() { + assert_eq!(Weight::from_all(0), 0_u64.into()); + assert_eq!(Weight::from_all(123), 123_u64.into()); + assert_eq!(Weight::from_all(u64::MAX), u64::MAX.into()); + } + + #[test] + fn from_u64_pair_works() { + assert_eq!(Weight::from_parts(0, 1), (0, 1).into()); + assert_eq!(Weight::from_parts(123, 321), (123u64, 321u64).into()); + assert_eq!(Weight::from_parts(u64::MAX, 0), (u64::MAX, 0).into()); + } + + #[test] + fn saturating_reduce_works() { + let mut weight = Weight::from_parts(10, 20); + weight.saturating_reduce(Weight::from_all(5)); + assert_eq!(weight, Weight::from_parts(5, 15)); + weight.saturating_reduce(Weight::from_all(5)); + assert_eq!(weight, Weight::from_parts(0, 10)); + weight.saturating_reduce(Weight::from_all(11)); + assert!(weight.is_zero()); + weight.saturating_reduce(Weight::from_all(u64::MAX)); + assert!(weight.is_zero()); + } + + #[test] + fn checked_accrue_works() { + let mut weight = Weight::from_parts(10, 20); + assert!(weight.checked_accrue(Weight::from_all(2)).is_some()); + assert_eq!(weight, Weight::from_parts(12, 22)); + assert!(weight.checked_accrue(Weight::from_parts(u64::MAX, 0)).is_none()); + assert!(weight.checked_accrue(Weight::from_parts(0, u64::MAX)).is_none()); + assert_eq!(weight, Weight::from_parts(12, 22)); + assert!(weight + .checked_accrue(Weight::from_parts(u64::MAX - 12, u64::MAX - 22)) + .is_some()); + assert_eq!(weight, Weight::MAX); + assert!(weight.checked_accrue(Weight::from_parts(1, 0)).is_none()); + assert!(weight.checked_accrue(Weight::from_parts(0, 1)).is_none()); + assert_eq!(weight, Weight::MAX); + } + + #[test] + fn checked_reduce_works() { + let mut weight = Weight::from_parts(10, 20); + assert!(weight.checked_reduce(Weight::from_all(2)).is_some()); + assert_eq!(weight, Weight::from_parts(8, 18)); + assert!(weight.checked_reduce(Weight::from_parts(9, 0)).is_none()); + assert!(weight.checked_reduce(Weight::from_parts(0, 19)).is_none()); + assert_eq!(weight, Weight::from_parts(8, 18)); + assert!(weight.checked_reduce(Weight::from_parts(8, 0)).is_some()); + assert_eq!(weight, Weight::from_parts(0, 18)); + assert!(weight.checked_reduce(Weight::from_parts(0, 18)).is_some()); + assert!(weight.is_zero()); + } } From 65d1d03ff6551afeebf4bb4ca8b972db5e8869a6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bastian=20K=C3=B6cher?= Date: Thu, 26 Jan 2023 21:37:51 +0100 Subject: [PATCH 061/558] Aura: Fix warp syncing (#13221) * Aura: Fix warp syncing We need to set the fork choice rule! When using Cumulus this is done by the `ParachainsBlockImport`, but for standalone chains we still need this! Closes: https://github.com/paritytech/substrate/issues/13220 * Improve fork choice --- client/consensus/aura/src/import_queue.rs | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/client/consensus/aura/src/import_queue.rs b/client/consensus/aura/src/import_queue.rs index 3ac26345d..4c8722c31 100644 --- a/client/consensus/aura/src/import_queue.rs +++ b/client/consensus/aura/src/import_queue.rs @@ -186,16 +186,15 @@ where &mut self, mut block: BlockImportParams, ) -> Result<(BlockImportParams, Option)>>), String> { - // When importing whole state we don't verify the seal as the state is not available. - if block.with_state() { - return Ok((block, Default::default())) - } - - // Skip checks that include execution, if being told so. + // Skip checks that include execution, if being told so or when importing only state. // // This is done for example when gap syncing and it is expected that the block after the gap // was checked/chosen properly, e.g. by warp syncing to this block using a finality proof. - if block.state_action.skip_execution_checks() { + // Or when we are importing state only and can not verify the seal. + if block.with_state() || block.state_action.skip_execution_checks() { + // When we are importing only the state of a block, it will be the best block. + block.fork_choice = Some(ForkChoiceStrategy::Custom(block.with_state())); + return Ok((block, Default::default())) } From ff9921a260a67e3a71f25c8b402cd5c7da787a96 Mon Sep 17 00:00:00 2001 From: Shawn Tabrizi Date: Thu, 26 Jan 2023 19:35:39 -0300 Subject: [PATCH 062/558] Add Proof Size to Weight Output (#11637) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * initial impl * add template test * linear fit proof size * always record proof when tracking storage * calculate worst case pov * remove duplicate worst case * cargo run --quiet --profile=production --features=runtime-benchmarks --manifest-path=bin/node/cli/Cargo.toml -- benchmark pallet --chain=dev --steps=50 --repeat=20 --pallet=pallet_assets --extrinsic=* --execution=wasm --wasm-execution=compiled --heap-pages=4096 --output=./frame/assets/src/weights.rs --template=./.maintain/frame-weight-template.hbs * more comment output * add cli for worst case map size * update name * clap does not support underscores * rename * expose worst case map values * improve some comments * cargo run --quiet --profile=production --features=runtime-benchmarks --manifest-path=bin/node/cli/Cargo.toml -- benchmark pallet --chain=dev --steps=50 --repeat=20 --pallet=pallet_assets --extrinsic=* --execution=wasm --wasm-execution=compiled --heap-pages=4096 --output=./frame/assets/src/weights.rs --template=./.maintain/frame-weight-template.hbs * update template * cargo run --quiet --profile=production --features=runtime-benchmarks --manifest-path=bin/node/cli/Cargo.toml -- benchmark pallet --chain=dev --steps=50 --repeat=20 --pallet=pallet_assets --extrinsic=* --execution=wasm --wasm-execution=compiled --heap-pages=4096 --output=./frame/assets/src/weights.rs --template=./.maintain/frame-weight-template.hbs * fix fmt * more fmt * more fmt * Dont panic when there is no proof Signed-off-by: Oliver Tale-Yazdi * Fix test features Signed-off-by: Oliver Tale-Yazdi * Whitelist :extrinsic_index Signed-off-by: Oliver Tale-Yazdi * Use whitelist when recording proof Signed-off-by: Oliver Tale-Yazdi * Add logs Signed-off-by: Oliver Tale-Yazdi * Add PoV testing pallet Signed-off-by: Oliver Tale-Yazdi * Deploy PoV testing pallet Signed-off-by: Oliver Tale-Yazdi * Storage benches reside in the PoV pallet Signed-off-by: Oliver Tale-Yazdi * Linear regress PoV per component Splits the PoV calculation into "measured" and "estimated". The measured part is reported by the Proof recorder and linear regressed over all components at once. The estimated part is calculated as worst-case by using the max PoV size per storage access and calculating one linear regress per component. This gives each component a (possibly) independent PoV. For now the measured size will always be lower than the PoV on Polkadot since it is measured on an empty snapshot. The measured part is therefor only used as diagnostic for debugging. Signed-off-by: Oliver Tale-Yazdi * Put PoV into the weight templates Signed-off-by: Oliver Tale-Yazdi * fmt Signed-off-by: Oliver Tale-Yazdi * Extra alanysis choise for PoV Signed-off-by: Oliver Tale-Yazdi * Add+Fix tests Signed-off-by: Oliver Tale-Yazdi * Make benches faster Signed-off-by: Oliver Tale-Yazdi * Cleanup Signed-off-by: Oliver Tale-Yazdi * Use same template comments Signed-off-by: Oliver Tale-Yazdi * ".git/.scripts/bench-bot.sh" pallet dev pallet_balances * ".git/.scripts/bench-bot.sh" pallet dev pallet_democracy * Update referenda mock BlockWeights Signed-off-by: Oliver Tale-Yazdi * Take measured value size into account Signed-off-by: Oliver Tale-Yazdi * clippy Signed-off-by: Oliver Tale-Yazdi * ".git/.scripts/bench-bot.sh" pallet dev pallet_scheduler * WIP Signed-off-by: Oliver Tale-Yazdi * proof_size: None Signed-off-by: Oliver Tale-Yazdi * WIP Signed-off-by: Oliver Tale-Yazdi * WIP Signed-off-by: Oliver Tale-Yazdi * WIP Signed-off-by: Oliver Tale-Yazdi * ugly, but works Signed-off-by: Oliver Tale-Yazdi * WIP Signed-off-by: Oliver Tale-Yazdi * WIP Signed-off-by: Oliver Tale-Yazdi * WIP Signed-off-by: Oliver Tale-Yazdi * wup Signed-off-by: Oliver Tale-Yazdi * WIP Signed-off-by: Oliver Tale-Yazdi * WIP Signed-off-by: Oliver Tale-Yazdi * WIP Signed-off-by: Oliver Tale-Yazdi * WIP Signed-off-by: Oliver Tale-Yazdi * Add pov_mode attribute to the benchmarks! macro Signed-off-by: Oliver Tale-Yazdi * Use pov_mode attribute in PoV benchmarking Signed-off-by: Oliver Tale-Yazdi * Update tests Signed-off-by: Oliver Tale-Yazdi * Scheduler, Whitelist: Add pov_mode attr Signed-off-by: Oliver Tale-Yazdi * Update PoV weights * Add CLI arg: default-pov-mode Signed-off-by: Oliver Tale-Yazdi * Fix tests Signed-off-by: Oliver Tale-Yazdi * fmt Signed-off-by: Oliver Tale-Yazdi * fix Signed-off-by: Oliver Tale-Yazdi * Revert "Update PoV weights" This reverts commit 2f3ac2387396470b118122a6ff8fa4ee12216f4b. * Revert "WIP" This reverts commit c34b538cd2bc45da4544e887180184e30957904a. * Revert first approach This reverts commit range 8ddaa2fffe5930f225a30bee314d0b7c94c344dd^..4c84f8748e5395852a9e0e25b0404953fee1a59e Signed-off-by: Oliver Tale-Yazdi * Clippy Signed-off-by: Oliver Tale-Yazdi * Add extra benchmarks Signed-off-by: Oliver Tale-Yazdi * ".git/.scripts/commands/bench/bench.sh" pallet dev pallet_alliance * ".git/.scripts/commands/bench/bench.sh" pallet dev pallet_whitelist * ".git/.scripts/commands/bench/bench.sh" pallet dev pallet_scheduler * fmt Signed-off-by: Oliver Tale-Yazdi * Clippy Signed-off-by: Oliver Tale-Yazdi * Clippy 🤦 Signed-off-by: Oliver Tale-Yazdi * Add reference benchmarks Signed-off-by: Oliver Tale-Yazdi * Fix doc comments Signed-off-by: Oliver Tale-Yazdi * Undo logging Signed-off-by: Oliver Tale-Yazdi * Add 'Ignored' pov_mode Signed-off-by: Oliver Tale-Yazdi * Allow multiple attributes per benchmark Turns out that the current benchmarking syntax does not support multiple attributes per bench 🤦. Changing it to support that since otherwise the `pov_mode` would conflict with the others. Signed-off-by: Oliver Tale-Yazdi * Validate pov_mode syntax Signed-off-by: Oliver Tale-Yazdi * Ignore PoV for all contract benchmarks Signed-off-by: Oliver Tale-Yazdi * Test Signed-off-by: Oliver Tale-Yazdi * test Signed-off-by: Oliver Tale-Yazdi * Bump macro recursion limit Signed-off-by: Oliver Tale-Yazdi * fmt Signed-off-by: Oliver Tale-Yazdi * Update contract weights They dont have a PoV component anymore. Signed-off-by: Oliver Tale-Yazdi * fix test ffs Signed-off-by: Oliver Tale-Yazdi * pov_mode is unsupported in V2 syntax Signed-off-by: Oliver Tale-Yazdi * Fix pallet ui tests Signed-off-by: Oliver Tale-Yazdi * update pallet ui Signed-off-by: Oliver Tale-Yazdi * Fix pallet ui tests Signed-off-by: Oliver Tale-Yazdi * Update weights Signed-off-by: Oliver Tale-Yazdi Signed-off-by: Oliver Tale-Yazdi Co-authored-by: Parity Bot Co-authored-by: Oliver Tale-Yazdi Co-authored-by: command-bot <> Co-authored-by: Your Name --- .maintain/frame-weight-template.hbs | 35 +- Cargo.lock | 15 + Cargo.toml | 1 + bin/node/runtime/Cargo.toml | 8 +- bin/node/runtime/src/lib.rs | 6 + client/db/src/bench.rs | 4 + client/db/src/upgrade.rs | 2 +- client/db/src/utils.rs | 7 +- frame/alliance/src/weights.rs | 1174 ++-- frame/assets/src/weights.rs | 1061 ++-- frame/bags-list/src/weights.rs | 149 +- frame/balances/src/weights.rs | 201 +- frame/benchmarking/Cargo.toml | 1 + frame/benchmarking/pov/Cargo.toml | 43 + frame/benchmarking/pov/src/benchmarking.rs | 371 ++ frame/benchmarking/pov/src/lib.rs | 131 + frame/benchmarking/pov/src/tests.rs | 211 + frame/benchmarking/pov/src/weights.rs | 807 +++ frame/benchmarking/src/baseline.rs | 63 +- frame/benchmarking/src/lib.rs | 132 +- frame/benchmarking/src/utils.rs | 1 + frame/benchmarking/src/weights.rs | 147 +- frame/bounties/src/weights.rs | 450 +- frame/child-bounties/src/weights.rs | 411 +- frame/collective/src/weights.rs | 659 ++- frame/contracts/src/benchmarking/mod.rs | 120 + frame/contracts/src/lib.rs | 2 +- frame/contracts/src/weights.rs | 4964 +++++++++++------ frame/conviction-voting/src/weights.rs | 351 +- frame/democracy/src/weights.rs | 995 ++-- .../src/weights.rs | 553 +- frame/elections-phragmen/src/weights.rs | 649 ++- frame/identity/src/weights.rs | 829 +-- frame/im-online/src/weights.rs | 77 +- frame/indices/src/weights.rs | 157 +- frame/lottery/src/weights.rs | 290 +- frame/membership/src/weights.rs | 397 +- frame/message-queue/src/weights.rs | 318 +- frame/multisig/src/weights.rs | 277 +- frame/nfts/src/weights.rs | 1814 +++--- frame/nis/src/weights.rs | 522 +- frame/nomination-pools/src/weights.rs | 1199 ++-- frame/preimage/src/weights.rs | 389 +- frame/proxy/src/weights.rs | 431 +- frame/ranked-collective/src/weights.rs | 317 +- frame/recovery/src/weights.rs | 345 +- frame/referenda/src/mock.rs | 2 +- frame/referenda/src/weights.rs | 1162 ++-- frame/remark/src/weights.rs | 27 +- frame/scheduler/src/benchmarking.rs | 4 + frame/scheduler/src/weights.rs | 351 +- frame/session/src/weights.rs | 85 +- frame/staking/src/weights.rs | 1914 ++++--- frame/state-trie-migration/src/weights.rs | 185 +- frame/support/procedural/src/benchmark.rs | 3 + frame/support/src/storage/transactional.rs | 2 + frame/support/src/weights/block_weights.rs | 20 +- .../support/src/weights/extrinsic_weights.rs | 20 +- frame/system/src/weights.rs | 173 +- frame/timestamp/src/weights.rs | 53 +- frame/tips/src/weights.rs | 281 +- frame/transaction-storage/src/weights.rs | 151 +- frame/treasury/src/weights.rs | 241 +- frame/uniques/src/weights.rs | 1127 ++-- frame/utility/src/weights.rs | 99 +- frame/vesting/src/weights.rs | 441 +- frame/whitelist/src/benchmarking.rs | 4 + frame/whitelist/src/weights.rs | 172 +- primitives/storage/src/lib.rs | 2 + scripts/run_all_benchmarks.sh | 2 + .../benchmarking-cli/src/pallet/command.rs | 178 +- .../frame/benchmarking-cli/src/pallet/mod.rs | 31 +- .../benchmarking-cli/src/pallet/template.hbs | 15 +- .../benchmarking-cli/src/pallet/writer.rs | 868 ++- 74 files changed, 19128 insertions(+), 9571 deletions(-) create mode 100644 frame/benchmarking/pov/Cargo.toml create mode 100644 frame/benchmarking/pov/src/benchmarking.rs create mode 100644 frame/benchmarking/pov/src/lib.rs create mode 100644 frame/benchmarking/pov/src/tests.rs create mode 100644 frame/benchmarking/pov/src/weights.rs diff --git a/.maintain/frame-weight-template.hbs b/.maintain/frame-weight-template.hbs index 9c9e29780..ff69fcb4a 100644 --- a/.maintain/frame-weight-template.hbs +++ b/.maintain/frame-weight-template.hbs @@ -2,7 +2,8 @@ //! Autogenerated weights for {{pallet}} //! //! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION {{version}} -//! DATE: {{date}}, STEPS: `{{cmd.steps}}`, REPEAT: {{cmd.repeat}}, LOW RANGE: `{{cmd.lowest_range_values}}`, HIGH RANGE: `{{cmd.highest_range_values}}` +//! DATE: {{date}}, STEPS: `{{cmd.steps}}`, REPEAT: `{{cmd.repeat}}`, LOW RANGE: `{{cmd.lowest_range_values}}`, HIGH RANGE: `{{cmd.highest_range_values}}` +//! WORST CASE MAP SIZE: `{{cmd.worst_case_map_values}}` //! HOSTNAME: `{{hostname}}`, CPU: `{{cpuname}}` //! EXECUTION: {{cmd.execution}}, WASM-EXECUTION: {{cmd.wasm_execution}}, CHAIN: {{cmd.chain}}, DB CACHE: {{cmd.db_cache}} @@ -38,7 +39,7 @@ impl WeightInfo for SubstrateWeight { {{/if}} {{#each benchmarks as |benchmark|}} {{#each benchmark.comments as |comment|}} - // {{comment}} + /// {{comment}} {{/each}} {{#each benchmark.component_ranges as |range|}} /// The range of component `{{range.name}}` is `[{{range.min}}, {{range.max}}]`. @@ -48,24 +49,34 @@ impl WeightInfo for SubstrateWeight { {{~#each benchmark.components as |c| ~}} {{~#if (not c.is_used)}}_{{/if}}{{c.name}}: u32, {{/each~}} ) -> Weight { + // Proof Size summary in bytes: + // Measured: `{{benchmark.base_recorded_proof_size}}{{#each benchmark.component_recorded_proof_size as |cp|}} + {{cp.name}} * ({{cp.slope}} ±{{underscore cp.error}}){{/each}}` + // Estimated: `{{benchmark.base_calculated_proof_size}}{{#each benchmark.component_calculated_proof_size as |cp|}} + {{cp.name}} * ({{cp.slope}} ±{{underscore cp.error}}){{/each}}` // Minimum execution time: {{underscore benchmark.min_execution_time}} nanoseconds. + {{#if (ne benchmark.base_calculated_proof_size "0")}} + Weight::from_parts({{underscore benchmark.base_weight}}, {{benchmark.base_calculated_proof_size}}) + {{else}} Weight::from_ref_time({{underscore benchmark.base_weight}}) + {{/if}} {{#each benchmark.component_weight as |cw|}} // Standard Error: {{underscore cw.error}} .saturating_add(Weight::from_ref_time({{underscore cw.slope}}).saturating_mul({{cw.name}}.into())) {{/each}} {{#if (ne benchmark.base_reads "0")}} - .saturating_add(T::DbWeight::get().reads({{benchmark.base_reads}})) + .saturating_add(T::DbWeight::get().reads({{benchmark.base_reads}}_u64)) {{/if}} {{#each benchmark.component_reads as |cr|}} .saturating_add(T::DbWeight::get().reads(({{cr.slope}}_u64).saturating_mul({{cr.name}}.into()))) {{/each}} {{#if (ne benchmark.base_writes "0")}} - .saturating_add(T::DbWeight::get().writes({{benchmark.base_writes}})) + .saturating_add(T::DbWeight::get().writes({{benchmark.base_writes}}_u64)) {{/if}} {{#each benchmark.component_writes as |cw|}} .saturating_add(T::DbWeight::get().writes(({{cw.slope}}_u64).saturating_mul({{cw.name}}.into()))) {{/each}} + {{#each benchmark.component_calculated_proof_size as |cp|}} + .saturating_add(Weight::from_proof_size({{cp.slope}}).saturating_mul({{cp.name}}.into())) + {{/each}} } {{/each}} } @@ -74,7 +85,7 @@ impl WeightInfo for SubstrateWeight { impl WeightInfo for () { {{#each benchmarks as |benchmark|}} {{#each benchmark.comments as |comment|}} - // {{comment}} + /// {{comment}} {{/each}} {{#each benchmark.component_ranges as |range|}} /// The range of component `{{range.name}}` is `[{{range.min}}, {{range.max}}]`. @@ -84,24 +95,34 @@ impl WeightInfo for () { {{~#each benchmark.components as |c| ~}} {{~#if (not c.is_used)}}_{{/if}}{{c.name}}: u32, {{/each~}} ) -> Weight { + // Proof Size summary in bytes: + // Measured: `{{benchmark.base_recorded_proof_size}}{{#each benchmark.component_recorded_proof_size as |cp|}} + {{cp.name}} * ({{cp.slope}} ±{{underscore cp.error}}){{/each}}` + // Estimated: `{{benchmark.base_calculated_proof_size}}{{#each benchmark.component_calculated_proof_size as |cp|}} + {{cp.name}} * ({{cp.slope}} ±{{underscore cp.error}}){{/each}}` // Minimum execution time: {{underscore benchmark.min_execution_time}} nanoseconds. + {{#if (ne benchmark.base_calculated_proof_size "0")}} + Weight::from_parts({{underscore benchmark.base_weight}}, {{benchmark.base_calculated_proof_size}}) + {{else}} Weight::from_ref_time({{underscore benchmark.base_weight}}) + {{/if}} {{#each benchmark.component_weight as |cw|}} // Standard Error: {{underscore cw.error}} .saturating_add(Weight::from_ref_time({{underscore cw.slope}}).saturating_mul({{cw.name}}.into())) {{/each}} {{#if (ne benchmark.base_reads "0")}} - .saturating_add(RocksDbWeight::get().reads({{benchmark.base_reads}})) + .saturating_add(RocksDbWeight::get().reads({{benchmark.base_reads}}_u64)) {{/if}} {{#each benchmark.component_reads as |cr|}} .saturating_add(RocksDbWeight::get().reads(({{cr.slope}}_u64).saturating_mul({{cr.name}}.into()))) {{/each}} {{#if (ne benchmark.base_writes "0")}} - .saturating_add(RocksDbWeight::get().writes({{benchmark.base_writes}})) + .saturating_add(RocksDbWeight::get().writes({{benchmark.base_writes}}_u64)) {{/if}} {{#each benchmark.component_writes as |cw|}} .saturating_add(RocksDbWeight::get().writes(({{cw.slope}}_u64).saturating_mul({{cw.name}}.into()))) {{/each}} + {{#each benchmark.component_calculated_proof_size as |cp|}} + .saturating_add(Weight::from_proof_size({{cp.slope}}).saturating_mul({{cp.name}}.into())) + {{/each}} } {{/each}} } diff --git a/Cargo.lock b/Cargo.lock index 43f67695f..7bbe54692 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2203,6 +2203,20 @@ dependencies = [ "thousands", ] +[[package]] +name = "frame-benchmarking-pallet-pov" +version = "4.0.0-dev" +dependencies = [ + "frame-benchmarking", + "frame-support", + "frame-system", + "parity-scale-codec", + "scale-info", + "sp-io", + "sp-runtime", + "sp-std", +] + [[package]] name = "frame-election-provider-solution-type" version = "4.0.0-dev" @@ -3453,6 +3467,7 @@ name = "kitchensink-runtime" version = "3.0.0-dev" dependencies = [ "frame-benchmarking", + "frame-benchmarking-pallet-pov", "frame-election-provider-support", "frame-executive", "frame-support", diff --git a/Cargo.toml b/Cargo.toml index f7eedadd1..bb5230516 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -88,6 +88,7 @@ members = [ "frame/beefy-mmr", "frame/beefy-mmr/primitives", "frame/benchmarking", + "frame/benchmarking/pov", "frame/bounties", "frame/child-bounties", "frame/collective", diff --git a/bin/node/runtime/Cargo.toml b/bin/node/runtime/Cargo.toml index 76fca730e..1ff8eb0ab 100644 --- a/bin/node/runtime/Cargo.toml +++ b/bin/node/runtime/Cargo.toml @@ -43,7 +43,8 @@ sp-io = { version = "7.0.0", default-features = false, path = "../../../primitiv # frame dependencies frame-executive = { version = "4.0.0-dev", default-features = false, path = "../../../frame/executive" } -frame-benchmarking = { version = "4.0.0-dev", default-features = false, path = "../../../frame/benchmarking", optional = true } +frame-benchmarking = { version = "4.0.0-dev", default-features = false, path = "../../../frame/benchmarking" } +frame-benchmarking-pallet-pov = { version = "4.0.0-dev", default-features = false, path = "../../../frame/benchmarking/pov" } frame-support = { version = "4.0.0-dev", default-features = false, path = "../../../frame/support" } frame-system = { version = "4.0.0-dev", default-features = false, path = "../../../frame/system" } frame-system-benchmarking = { version = "4.0.0-dev", default-features = false, path = "../../../frame/system/benchmarking", optional = true } @@ -178,7 +179,8 @@ std = [ "sp-session/std", "pallet-sudo/std", "frame-support/std", - "frame-benchmarking?/std", + "frame-benchmarking/std", + "frame-benchmarking-pallet-pov/std", "frame-system-rpc-runtime-api/std", "frame-system/std", "pallet-election-provider-multi-phase/std", @@ -209,6 +211,7 @@ std = [ ] runtime-benchmarks = [ "frame-benchmarking/runtime-benchmarks", + "frame-benchmarking-pallet-pov/runtime-benchmarks", "frame-support/runtime-benchmarks", "frame-system/runtime-benchmarks", "sp-runtime/runtime-benchmarks", @@ -263,6 +266,7 @@ runtime-benchmarks = [ ] try-runtime = [ "frame-try-runtime/try-runtime", + "frame-benchmarking-pallet-pov/try-runtime", "frame-executive/try-runtime", "frame-system/try-runtime", "frame-support/try-runtime", diff --git a/bin/node/runtime/src/lib.rs b/bin/node/runtime/src/lib.rs index 0121f1ee3..bd8a395d1 100644 --- a/bin/node/runtime/src/lib.rs +++ b/bin/node/runtime/src/lib.rs @@ -1696,6 +1696,10 @@ impl pallet_alliance::Config for Runtime { type RetirementPeriod = RetirementPeriod; } +impl frame_benchmarking_pallet_pov::Config for Runtime { + type RuntimeEvent = RuntimeEvent; +} + construct_runtime!( pub enum Runtime where Block = Block, @@ -1762,6 +1766,7 @@ construct_runtime!( RankedCollective: pallet_ranked_collective, FastUnstake: pallet_fast_unstake, MessageQueue: pallet_message_queue, + Pov: frame_benchmarking_pallet_pov, } ); @@ -1830,6 +1835,7 @@ mod mmr { mod benches { frame_benchmarking::define_benchmarks!( [frame_benchmarking, BaselineBench::] + [frame_benchmarking_pallet_pov, Pov] [pallet_alliance, Alliance] [pallet_assets, Assets] [pallet_babe, Babe] diff --git a/client/db/src/bench.rs b/client/db/src/bench.rs index e53209f27..a74476287 100644 --- a/client/db/src/bench.rs +++ b/client/db/src/bench.rs @@ -606,10 +606,14 @@ impl StateBackend> for BenchmarkingState { let proof_recorder_root = self.proof_recorder_root.get(); if proof_recorder_root == Default::default() || proof_size == 1 { // empty trie + log::debug!(target: "benchmark", "Some proof size: {}", &proof_size); proof_size } else { if let Some(size) = proof.encoded_compact_size::>(proof_recorder_root) { size as u32 + } else if proof_recorder_root == self.root.get() { + log::debug!(target: "benchmark", "No changes - no proof"); + 0 } else { panic!( "proof rec root {:?}, root {:?}, genesis {:?}, rec_len {:?}", diff --git a/client/db/src/upgrade.rs b/client/db/src/upgrade.rs index 51750bf68..98517acad 100644 --- a/client/db/src/upgrade.rs +++ b/client/db/src/upgrade.rs @@ -190,7 +190,7 @@ fn version_file_path(path: &Path) -> PathBuf { file_path } -#[cfg(test)] +#[cfg(all(test, feature = "rocksdb"))] mod tests { use super::*; use crate::{tests::Block, DatabaseSource}; diff --git a/client/db/src/utils.rs b/client/db/src/utils.rs index 567950d08..e99851101 100644 --- a/client/db/src/utils.rs +++ b/client/db/src/utils.rs @@ -583,12 +583,12 @@ mod tests { use super::*; use codec::Input; use sp_runtime::testing::{Block as RawBlock, ExtrinsicWrapper}; - use std::path::PathBuf; type Block = RawBlock>; - #[cfg(any(feature = "rocksdb", test))] + #[cfg(feature = "rocksdb")] #[test] fn database_type_subdir_migration() { + use std::path::PathBuf; type Block = RawBlock>; fn check_dir_for_db_type( @@ -685,6 +685,7 @@ mod tests { assert_eq!(joined.remaining_len().unwrap(), Some(0)); } + #[cfg(feature = "rocksdb")] #[test] fn test_open_database_auto_new() { let db_dir = tempfile::TempDir::new().unwrap(); @@ -730,6 +731,7 @@ mod tests { } } + #[cfg(feature = "rocksdb")] #[test] fn test_open_database_rocksdb_new() { let db_dir = tempfile::TempDir::new().unwrap(); @@ -780,6 +782,7 @@ mod tests { } } + #[cfg(feature = "rocksdb")] #[test] fn test_open_database_paritydb_new() { let db_dir = tempfile::TempDir::new().unwrap(); diff --git a/frame/alliance/src/weights.rs b/frame/alliance/src/weights.rs index 1cfc83dfc..9ff6ce612 100644 --- a/frame/alliance/src/weights.rs +++ b/frame/alliance/src/weights.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2022 Parity Technologies (UK) Ltd. +// Copyright (C) 2023 Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); @@ -18,25 +18,25 @@ //! Autogenerated weights for pallet_alliance //! //! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev -//! DATE: 2022-12-22, STEPS: `50`, REPEAT: 20, LOW RANGE: `[]`, HIGH RANGE: `[]` -//! HOSTNAME: `bm3`, CPU: `Intel(R) Core(TM) i7-7700K CPU @ 4.20GHz` +//! DATE: 2023-01-24, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! WORST CASE MAP SIZE: `1000000` +//! HOSTNAME: `bm2`, CPU: `Intel(R) Core(TM) i7-7700K CPU @ 4.20GHz` //! EXECUTION: Some(Wasm), WASM-EXECUTION: Compiled, CHAIN: Some("dev"), DB CACHE: 1024 // Executed Command: -// /home/benchbot/cargo_target_dir/production/substrate +// ./target/production/substrate // benchmark // pallet +// --chain=dev // --steps=50 // --repeat=20 +// --pallet=pallet_alliance // --extrinsic=* // --execution=wasm // --wasm-execution=compiled // --heap-pages=4096 -// --json-file=/var/lib/gitlab-runner/builds/zyw4fam_/0/parity/mirrors/substrate/.git/.artifacts/bench.json -// --pallet=pallet_alliance -// --chain=dev -// --header=./HEADER-APACHE2 // --output=./frame/alliance/src/weights.rs +// --header=./HEADER-APACHE2 // --template=./.maintain/frame-weight-template.hbs #![cfg_attr(rustfmt, rustfmt_skip)] @@ -73,530 +73,832 @@ pub trait WeightInfo { /// Weights for pallet_alliance using the Substrate node and recommended hardware. pub struct SubstrateWeight(PhantomData); impl WeightInfo for SubstrateWeight { - // Storage: Alliance Members (r:1 w:0) - // Storage: AllianceMotion ProposalOf (r:1 w:1) - // Storage: AllianceMotion Proposals (r:1 w:1) - // Storage: AllianceMotion ProposalCount (r:1 w:1) - // Storage: AllianceMotion Voting (r:0 w:1) + /// Storage: Alliance Members (r:1 w:0) + /// Proof: Alliance Members (max_values: None, max_size: Some(3211), added: 5686, mode: MaxEncodedLen) + /// Storage: AllianceMotion ProposalOf (r:1 w:1) + /// Proof Skipped: AllianceMotion ProposalOf (max_values: None, max_size: None, mode: Measured) + /// Storage: AllianceMotion Proposals (r:1 w:1) + /// Proof Skipped: AllianceMotion Proposals (max_values: Some(1), max_size: None, mode: Measured) + /// Storage: AllianceMotion ProposalCount (r:1 w:1) + /// Proof Skipped: AllianceMotion ProposalCount (max_values: Some(1), max_size: None, mode: Measured) + /// Storage: AllianceMotion Voting (r:0 w:1) + /// Proof Skipped: AllianceMotion Voting (max_values: None, max_size: None, mode: Measured) /// The range of component `b` is `[1, 1024]`. /// The range of component `m` is `[2, 100]`. /// The range of component `p` is `[1, 100]`. - fn propose_proposed(_b: u32, m: u32, p: u32, ) -> Weight { - // Minimum execution time: 45_945 nanoseconds. - Weight::from_ref_time(44_195_340) - // Standard Error: 2_212 - .saturating_add(Weight::from_ref_time(73_715).saturating_mul(m.into())) - // Standard Error: 2_184 - .saturating_add(Weight::from_ref_time(172_373).saturating_mul(p.into())) - .saturating_add(T::DbWeight::get().reads(4)) - .saturating_add(T::DbWeight::get().writes(4)) - } - // Storage: Alliance Members (r:1 w:0) - // Storage: AllianceMotion Voting (r:1 w:1) + fn propose_proposed(b: u32, m: u32, p: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `692 + m * (32 ±0) + p * (36 ±0)` + // Estimated: `11659 + m * (132 ±0) + p * (144 ±0)` + // Minimum execution time: 28_849 nanoseconds. + Weight::from_parts(29_823_933, 11659) + // Standard Error: 74 + .saturating_add(Weight::from_ref_time(830).saturating_mul(b.into())) + // Standard Error: 775 + .saturating_add(Weight::from_ref_time(22_980).saturating_mul(m.into())) + // Standard Error: 765 + .saturating_add(Weight::from_ref_time(90_520).saturating_mul(p.into())) + .saturating_add(T::DbWeight::get().reads(4_u64)) + .saturating_add(T::DbWeight::get().writes(4_u64)) + .saturating_add(Weight::from_proof_size(132).saturating_mul(m.into())) + .saturating_add(Weight::from_proof_size(144).saturating_mul(p.into())) + } + /// Storage: Alliance Members (r:1 w:0) + /// Proof: Alliance Members (max_values: None, max_size: Some(3211), added: 5686, mode: MaxEncodedLen) + /// Storage: AllianceMotion Voting (r:1 w:1) + /// Proof Skipped: AllianceMotion Voting (max_values: None, max_size: None, mode: Measured) /// The range of component `m` is `[5, 100]`. fn vote(m: u32, ) -> Weight { - // Minimum execution time: 41_578 nanoseconds. - Weight::from_ref_time(44_299_385) - // Standard Error: 3_727 - .saturating_add(Weight::from_ref_time(138_681).saturating_mul(m.into())) - .saturating_add(T::DbWeight::get().reads(2)) - .saturating_add(T::DbWeight::get().writes(1)) - } - // Storage: Alliance Members (r:1 w:0) - // Storage: AllianceMotion Voting (r:1 w:1) - // Storage: AllianceMotion Members (r:1 w:0) - // Storage: AllianceMotion Proposals (r:1 w:1) - // Storage: AllianceMotion ProposalOf (r:0 w:1) + // Proof Size summary in bytes: + // Measured: `1177 + m * (64 ±0)` + // Estimated: `9337 + m * (64 ±0)` + // Minimum execution time: 23_570 nanoseconds. + Weight::from_parts(25_473_196, 9337) + // Standard Error: 824 + .saturating_add(Weight::from_ref_time(54_603).saturating_mul(m.into())) + .saturating_add(T::DbWeight::get().reads(2_u64)) + .saturating_add(T::DbWeight::get().writes(1_u64)) + .saturating_add(Weight::from_proof_size(64).saturating_mul(m.into())) + } + /// Storage: Alliance Members (r:1 w:0) + /// Proof: Alliance Members (max_values: None, max_size: Some(3211), added: 5686, mode: MaxEncodedLen) + /// Storage: AllianceMotion Voting (r:1 w:1) + /// Proof Skipped: AllianceMotion Voting (max_values: None, max_size: None, mode: Measured) + /// Storage: AllianceMotion Members (r:1 w:0) + /// Proof Skipped: AllianceMotion Members (max_values: Some(1), max_size: None, mode: Measured) + /// Storage: AllianceMotion Proposals (r:1 w:1) + /// Proof Skipped: AllianceMotion Proposals (max_values: Some(1), max_size: None, mode: Measured) + /// Storage: AllianceMotion ProposalOf (r:0 w:1) + /// Proof Skipped: AllianceMotion ProposalOf (max_values: None, max_size: None, mode: Measured) /// The range of component `m` is `[4, 100]`. /// The range of component `p` is `[1, 100]`. fn close_early_disapproved(m: u32, p: u32, ) -> Weight { - // Minimum execution time: 53_033 nanoseconds. - Weight::from_ref_time(44_608_804) - // Standard Error: 2_306 - .saturating_add(Weight::from_ref_time(102_842).saturating_mul(m.into())) - // Standard Error: 2_248 - .saturating_add(Weight::from_ref_time(172_018).saturating_mul(p.into())) - .saturating_add(T::DbWeight::get().reads(4)) - .saturating_add(T::DbWeight::get().writes(3)) - } - // Storage: Alliance Members (r:1 w:0) - // Storage: AllianceMotion Voting (r:1 w:1) - // Storage: AllianceMotion Members (r:1 w:0) - // Storage: AllianceMotion ProposalOf (r:1 w:1) - // Storage: AllianceMotion Proposals (r:1 w:1) + // Proof Size summary in bytes: + // Measured: `730 + m * (96 ±0) + p * (36 ±0)` + // Estimated: `11979 + m * (388 ±0) + p * (148 ±0)` + // Minimum execution time: 35_373 nanoseconds. + Weight::from_parts(32_763_656, 11979) + // Standard Error: 2_041 + .saturating_add(Weight::from_ref_time(52_915).saturating_mul(m.into())) + // Standard Error: 1_991 + .saturating_add(Weight::from_ref_time(78_594).saturating_mul(p.into())) + .saturating_add(T::DbWeight::get().reads(4_u64)) + .saturating_add(T::DbWeight::get().writes(3_u64)) + .saturating_add(Weight::from_proof_size(388).saturating_mul(m.into())) + .saturating_add(Weight::from_proof_size(148).saturating_mul(p.into())) + } + /// Storage: Alliance Members (r:1 w:0) + /// Proof: Alliance Members (max_values: None, max_size: Some(3211), added: 5686, mode: MaxEncodedLen) + /// Storage: AllianceMotion Voting (r:1 w:1) + /// Proof Skipped: AllianceMotion Voting (max_values: None, max_size: None, mode: Measured) + /// Storage: AllianceMotion Members (r:1 w:0) + /// Proof Skipped: AllianceMotion Members (max_values: Some(1), max_size: None, mode: Measured) + /// Storage: AllianceMotion ProposalOf (r:1 w:1) + /// Proof Skipped: AllianceMotion ProposalOf (max_values: None, max_size: None, mode: Measured) + /// Storage: AllianceMotion Proposals (r:1 w:1) + /// Proof Skipped: AllianceMotion Proposals (max_values: Some(1), max_size: None, mode: Measured) /// The range of component `b` is `[1, 1024]`. /// The range of component `m` is `[4, 100]`. /// The range of component `p` is `[1, 100]`. fn close_early_approved(b: u32, m: u32, p: u32, ) -> Weight { - // Minimum execution time: 61_157 nanoseconds. - Weight::from_ref_time(53_647_510) - // Standard Error: 235 - .saturating_add(Weight::from_ref_time(88).saturating_mul(b.into())) - // Standard Error: 2_486 - .saturating_add(Weight::from_ref_time(103_912).saturating_mul(m.into())) - // Standard Error: 2_424 - .saturating_add(Weight::from_ref_time(197_471).saturating_mul(p.into())) - .saturating_add(T::DbWeight::get().reads(5)) - .saturating_add(T::DbWeight::get().writes(3)) - } - // Storage: Alliance Members (r:1 w:0) - // Storage: AllianceMotion Voting (r:1 w:1) - // Storage: AllianceMotion Members (r:1 w:0) - // Storage: AllianceMotion Prime (r:1 w:0) - // Storage: AllianceMotion Proposals (r:1 w:1) - // Storage: AllianceMotion ProposalOf (r:0 w:1) + // Proof Size summary in bytes: + // Measured: `1139 + m * (96 ±0) + p * (41 ±0)` + // Estimated: `15730 + m * (388 ±0) + p * (164 ±0)` + // Minimum execution time: 44_590 nanoseconds. + Weight::from_parts(43_367_913, 15730) + // Standard Error: 71 + .saturating_add(Weight::from_ref_time(819).saturating_mul(b.into())) + // Standard Error: 751 + .saturating_add(Weight::from_ref_time(39_124).saturating_mul(m.into())) + // Standard Error: 732 + .saturating_add(Weight::from_ref_time(90_469).saturating_mul(p.into())) + .saturating_add(T::DbWeight::get().reads(5_u64)) + .saturating_add(T::DbWeight::get().writes(3_u64)) + .saturating_add(Weight::from_proof_size(388).saturating_mul(m.into())) + .saturating_add(Weight::from_proof_size(164).saturating_mul(p.into())) + } + /// Storage: Alliance Members (r:1 w:0) + /// Proof: Alliance Members (max_values: None, max_size: Some(3211), added: 5686, mode: MaxEncodedLen) + /// Storage: AllianceMotion Voting (r:1 w:1) + /// Proof Skipped: AllianceMotion Voting (max_values: None, max_size: None, mode: Measured) + /// Storage: AllianceMotion Members (r:1 w:0) + /// Proof Skipped: AllianceMotion Members (max_values: Some(1), max_size: None, mode: Measured) + /// Storage: AllianceMotion Prime (r:1 w:0) + /// Proof Skipped: AllianceMotion Prime (max_values: Some(1), max_size: None, mode: Measured) + /// Storage: AllianceMotion Proposals (r:1 w:1) + /// Proof Skipped: AllianceMotion Proposals (max_values: Some(1), max_size: None, mode: Measured) + /// Storage: AllianceMotion ProposalOf (r:0 w:1) + /// Proof Skipped: AllianceMotion ProposalOf (max_values: None, max_size: None, mode: Measured) /// The range of component `m` is `[2, 100]`. /// The range of component `p` is `[1, 100]`. fn close_disapproved(m: u32, p: u32, ) -> Weight { - // Minimum execution time: 53_830 nanoseconds. - Weight::from_ref_time(43_926_698) - // Standard Error: 2_495 - .saturating_add(Weight::from_ref_time(112_814).saturating_mul(m.into())) - // Standard Error: 2_465 - .saturating_add(Weight::from_ref_time(186_422).saturating_mul(p.into())) - .saturating_add(T::DbWeight::get().reads(5)) - .saturating_add(T::DbWeight::get().writes(3)) - } - // Storage: Alliance Members (r:1 w:0) - // Storage: AllianceMotion Voting (r:1 w:1) - // Storage: AllianceMotion Members (r:1 w:0) - // Storage: AllianceMotion Prime (r:1 w:0) - // Storage: AllianceMotion Proposals (r:1 w:1) - // Storage: AllianceMotion ProposalOf (r:0 w:1) + // Proof Size summary in bytes: + // Measured: `730 + m * (96 ±0) + p * (36 ±0)` + // Estimated: `13201 + m * (485 ±0) + p * (185 ±0)` + // Minimum execution time: 36_796 nanoseconds. + Weight::from_parts(34_578_765, 13201) + // Standard Error: 1_037 + .saturating_add(Weight::from_ref_time(43_508).saturating_mul(m.into())) + // Standard Error: 1_024 + .saturating_add(Weight::from_ref_time(77_084).saturating_mul(p.into())) + .saturating_add(T::DbWeight::get().reads(5_u64)) + .saturating_add(T::DbWeight::get().writes(3_u64)) + .saturating_add(Weight::from_proof_size(485).saturating_mul(m.into())) + .saturating_add(Weight::from_proof_size(185).saturating_mul(p.into())) + } + /// Storage: Alliance Members (r:1 w:0) + /// Proof: Alliance Members (max_values: None, max_size: Some(3211), added: 5686, mode: MaxEncodedLen) + /// Storage: AllianceMotion Voting (r:1 w:1) + /// Proof Skipped: AllianceMotion Voting (max_values: None, max_size: None, mode: Measured) + /// Storage: AllianceMotion Members (r:1 w:0) + /// Proof Skipped: AllianceMotion Members (max_values: Some(1), max_size: None, mode: Measured) + /// Storage: AllianceMotion Prime (r:1 w:0) + /// Proof Skipped: AllianceMotion Prime (max_values: Some(1), max_size: None, mode: Measured) + /// Storage: AllianceMotion Proposals (r:1 w:1) + /// Proof Skipped: AllianceMotion Proposals (max_values: Some(1), max_size: None, mode: Measured) + /// Storage: AllianceMotion ProposalOf (r:0 w:1) + /// Proof Skipped: AllianceMotion ProposalOf (max_values: None, max_size: None, mode: Measured) /// The range of component `b` is `[1, 1024]`. /// The range of component `m` is `[5, 100]`. /// The range of component `p` is `[1, 100]`. - fn close_approved(_b: u32, m: u32, p: u32, ) -> Weight { - // Minimum execution time: 53_665 nanoseconds. - Weight::from_ref_time(44_914_913) - // Standard Error: 2_244 - .saturating_add(Weight::from_ref_time(105_848).saturating_mul(m.into())) - // Standard Error: 2_164 - .saturating_add(Weight::from_ref_time(190_830).saturating_mul(p.into())) - .saturating_add(T::DbWeight::get().reads(5)) - .saturating_add(T::DbWeight::get().writes(3)) - } - // Storage: Alliance Members (r:2 w:2) - // Storage: AllianceMotion Members (r:1 w:1) + fn close_approved(b: u32, m: u32, p: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `757 + m * (96 ±0) + p * (36 ±0)` + // Estimated: `13146 + m * (480 ±0) + p * (185 ±0)` + // Minimum execution time: 36_897 nanoseconds. + Weight::from_parts(34_169_666, 13146) + // Standard Error: 47 + .saturating_add(Weight::from_ref_time(972).saturating_mul(b.into())) + // Standard Error: 510 + .saturating_add(Weight::from_ref_time(38_084).saturating_mul(m.into())) + // Standard Error: 492 + .saturating_add(Weight::from_ref_time(78_576).saturating_mul(p.into())) + .saturating_add(T::DbWeight::get().reads(5_u64)) + .saturating_add(T::DbWeight::get().writes(3_u64)) + .saturating_add(Weight::from_proof_size(480).saturating_mul(m.into())) + .saturating_add(Weight::from_proof_size(185).saturating_mul(p.into())) + } + /// Storage: Alliance Members (r:2 w:2) + /// Proof: Alliance Members (max_values: None, max_size: Some(3211), added: 5686, mode: MaxEncodedLen) + /// Storage: AllianceMotion Members (r:1 w:1) + /// Proof Skipped: AllianceMotion Members (max_values: Some(1), max_size: None, mode: Measured) /// The range of component `m` is `[1, 100]`. /// The range of component `z` is `[0, 100]`. fn init_members(m: u32, z: u32, ) -> Weight { - // Minimum execution time: 46_486 nanoseconds. - Weight::from_ref_time(34_596_922) - // Standard Error: 1_807 - .saturating_add(Weight::from_ref_time(143_225).saturating_mul(m.into())) - // Standard Error: 1_786 - .saturating_add(Weight::from_ref_time(125_729).saturating_mul(z.into())) - .saturating_add(T::DbWeight::get().reads(3)) - .saturating_add(T::DbWeight::get().writes(3)) - } - // Storage: Alliance Members (r:2 w:2) - // Storage: AllianceMotion Proposals (r:1 w:0) - // Storage: Alliance DepositOf (r:101 w:50) - // Storage: System Account (r:50 w:50) - // Storage: AllianceMotion Members (r:0 w:1) - // Storage: AllianceMotion Prime (r:0 w:1) + // Proof Size summary in bytes: + // Measured: `217` + // Estimated: `12084` + // Minimum execution time: 29_313 nanoseconds. + Weight::from_parts(20_502_244, 12084) + // Standard Error: 304 + .saturating_add(Weight::from_ref_time(107_994).saturating_mul(m.into())) + // Standard Error: 300 + .saturating_add(Weight::from_ref_time(92_645).saturating_mul(z.into())) + .saturating_add(T::DbWeight::get().reads(3_u64)) + .saturating_add(T::DbWeight::get().writes(3_u64)) + } + /// Storage: Alliance Members (r:2 w:2) + /// Proof: Alliance Members (max_values: None, max_size: Some(3211), added: 5686, mode: MaxEncodedLen) + /// Storage: AllianceMotion Proposals (r:1 w:0) + /// Proof Skipped: AllianceMotion Proposals (max_values: Some(1), max_size: None, mode: Measured) + /// Storage: Alliance DepositOf (r:200 w:50) + /// Proof: Alliance DepositOf (max_values: None, max_size: Some(64), added: 2539, mode: MaxEncodedLen) + /// Storage: System Account (r:50 w:50) + /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) + /// Storage: AllianceMotion Members (r:0 w:1) + /// Proof Skipped: AllianceMotion Members (max_values: Some(1), max_size: None, mode: Measured) + /// Storage: AllianceMotion Prime (r:0 w:1) + /// Proof Skipped: AllianceMotion Prime (max_values: Some(1), max_size: None, mode: Measured) /// The range of component `x` is `[1, 100]`. /// The range of component `y` is `[0, 100]`. /// The range of component `z` is `[0, 50]`. fn disband(x: u32, y: u32, z: u32, ) -> Weight { - // Minimum execution time: 251_640 nanoseconds. - Weight::from_ref_time(252_446_000) - // Standard Error: 20_419 - .saturating_add(Weight::from_ref_time(486_667).saturating_mul(x.into())) - // Standard Error: 20_320 - .saturating_add(Weight::from_ref_time(498_890).saturating_mul(y.into())) - // Standard Error: 40_604 - .saturating_add(Weight::from_ref_time(9_411_242).saturating_mul(z.into())) - .saturating_add(T::DbWeight::get().reads(3)) + // Proof Size summary in bytes: + // Measured: `0 + x * (50 ±0) + y * (51 ±0) + z * (283 ±0)` + // Estimated: `32201 + x * (2587 ±0) + y * (2590 ±0) + z * (3209 ±1)` + // Minimum execution time: 232_895 nanoseconds. + Weight::from_parts(233_860_000, 32201) + // Standard Error: 19_092 + .saturating_add(Weight::from_ref_time(445_664).saturating_mul(x.into())) + // Standard Error: 19_000 + .saturating_add(Weight::from_ref_time(432_571).saturating_mul(y.into())) + // Standard Error: 37_965 + .saturating_add(Weight::from_ref_time(9_386_151).saturating_mul(z.into())) + .saturating_add(T::DbWeight::get().reads(3_u64)) .saturating_add(T::DbWeight::get().reads((1_u64).saturating_mul(x.into()))) .saturating_add(T::DbWeight::get().reads((1_u64).saturating_mul(y.into()))) .saturating_add(T::DbWeight::get().reads((1_u64).saturating_mul(z.into()))) - .saturating_add(T::DbWeight::get().writes(4)) + .saturating_add(T::DbWeight::get().writes(4_u64)) .saturating_add(T::DbWeight::get().writes((2_u64).saturating_mul(z.into()))) + .saturating_add(Weight::from_proof_size(2587).saturating_mul(x.into())) + .saturating_add(Weight::from_proof_size(2590).saturating_mul(y.into())) + .saturating_add(Weight::from_proof_size(3209).saturating_mul(z.into())) } - // Storage: Alliance Rule (r:0 w:1) + /// Storage: Alliance Rule (r:0 w:1) + /// Proof: Alliance Rule (max_values: Some(1), max_size: Some(87), added: 582, mode: MaxEncodedLen) fn set_rule() -> Weight { - // Minimum execution time: 18_867 nanoseconds. - Weight::from_ref_time(19_155_000) - .saturating_add(T::DbWeight::get().writes(1)) - } - // Storage: Alliance Announcements (r:1 w:1) + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 9_156 nanoseconds. + Weight::from_ref_time(9_376_000) + .saturating_add(T::DbWeight::get().writes(1_u64)) + } + /// Storage: Alliance Announcements (r:1 w:1) + /// Proof: Alliance Announcements (max_values: Some(1), max_size: Some(8702), added: 9197, mode: MaxEncodedLen) fn announce() -> Weight { - // Minimum execution time: 21_942 nanoseconds. - Weight::from_ref_time(22_319_000) - .saturating_add(T::DbWeight::get().reads(1)) - .saturating_add(T::DbWeight::get().writes(1)) - } - // Storage: Alliance Announcements (r:1 w:1) + // Proof Size summary in bytes: + // Measured: `246` + // Estimated: `9197` + // Minimum execution time: 12_163 nanoseconds. + Weight::from_parts(12_397_000, 9197) + .saturating_add(T::DbWeight::get().reads(1_u64)) + .saturating_add(T::DbWeight::get().writes(1_u64)) + } + /// Storage: Alliance Announcements (r:1 w:1) + /// Proof: Alliance Announcements (max_values: Some(1), max_size: Some(8702), added: 9197, mode: MaxEncodedLen) fn remove_announcement() -> Weight { - // Minimum execution time: 23_419 nanoseconds. - Weight::from_ref_time(23_854_000) - .saturating_add(T::DbWeight::get().reads(1)) - .saturating_add(T::DbWeight::get().writes(1)) - } - // Storage: Alliance Members (r:3 w:1) - // Storage: Alliance UnscrupulousAccounts (r:1 w:0) - // Storage: System Account (r:1 w:1) - // Storage: Alliance DepositOf (r:0 w:1) + // Proof Size summary in bytes: + // Measured: `351` + // Estimated: `9197` + // Minimum execution time: 12_778 nanoseconds. + Weight::from_parts(12_991_000, 9197) + .saturating_add(T::DbWeight::get().reads(1_u64)) + .saturating_add(T::DbWeight::get().writes(1_u64)) + } + /// Storage: Alliance Members (r:3 w:1) + /// Proof: Alliance Members (max_values: None, max_size: Some(3211), added: 5686, mode: MaxEncodedLen) + /// Storage: Alliance UnscrupulousAccounts (r:1 w:0) + /// Proof: Alliance UnscrupulousAccounts (max_values: Some(1), max_size: Some(3202), added: 3697, mode: MaxEncodedLen) + /// Storage: System Account (r:1 w:1) + /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) + /// Storage: Alliance DepositOf (r:0 w:1) + /// Proof: Alliance DepositOf (max_values: None, max_size: Some(64), added: 2539, mode: MaxEncodedLen) fn join_alliance() -> Weight { - // Minimum execution time: 53_016 nanoseconds. - Weight::from_ref_time(53_768_000) - .saturating_add(T::DbWeight::get().reads(5)) - .saturating_add(T::DbWeight::get().writes(3)) - } - // Storage: Alliance Members (r:3 w:1) - // Storage: Alliance UnscrupulousAccounts (r:1 w:0) + // Proof Size summary in bytes: + // Measured: `562` + // Estimated: `23358` + // Minimum execution time: 40_216 nanoseconds. + Weight::from_parts(40_535_000, 23358) + .saturating_add(T::DbWeight::get().reads(5_u64)) + .saturating_add(T::DbWeight::get().writes(3_u64)) + } + /// Storage: Alliance Members (r:3 w:1) + /// Proof: Alliance Members (max_values: None, max_size: Some(3211), added: 5686, mode: MaxEncodedLen) + /// Storage: Alliance UnscrupulousAccounts (r:1 w:0) + /// Proof: Alliance UnscrupulousAccounts (max_values: Some(1), max_size: Some(3202), added: 3697, mode: MaxEncodedLen) fn nominate_ally() -> Weight { - // Minimum execution time: 40_019 nanoseconds. - Weight::from_ref_time(41_087_000) - .saturating_add(T::DbWeight::get().reads(4)) - .saturating_add(T::DbWeight::get().writes(1)) - } - // Storage: Alliance Members (r:2 w:2) - // Storage: AllianceMotion Proposals (r:1 w:0) - // Storage: AllianceMotion Members (r:0 w:1) - // Storage: AllianceMotion Prime (r:0 w:1) + // Proof Size summary in bytes: + // Measured: `429` + // Estimated: `20755` + // Minimum execution time: 28_027 nanoseconds. + Weight::from_parts(28_392_000, 20755) + .saturating_add(T::DbWeight::get().reads(4_u64)) + .saturating_add(T::DbWeight::get().writes(1_u64)) + } + /// Storage: Alliance Members (r:2 w:2) + /// Proof: Alliance Members (max_values: None, max_size: Some(3211), added: 5686, mode: MaxEncodedLen) + /// Storage: AllianceMotion Proposals (r:1 w:0) + /// Proof Skipped: AllianceMotion Proposals (max_values: Some(1), max_size: None, mode: Measured) + /// Storage: AllianceMotion Members (r:0 w:1) + /// Proof Skipped: AllianceMotion Members (max_values: Some(1), max_size: None, mode: Measured) + /// Storage: AllianceMotion Prime (r:0 w:1) + /// Proof Skipped: AllianceMotion Prime (max_values: Some(1), max_size: None, mode: Measured) fn elevate_ally() -> Weight { - // Minimum execution time: 35_973 nanoseconds. - Weight::from_ref_time(36_365_000) - .saturating_add(T::DbWeight::get().reads(3)) - .saturating_add(T::DbWeight::get().writes(4)) - } - // Storage: Alliance Members (r:4 w:2) - // Storage: AllianceMotion Proposals (r:1 w:0) - // Storage: AllianceMotion Members (r:0 w:1) - // Storage: AllianceMotion Prime (r:0 w:1) - // Storage: Alliance RetiringMembers (r:0 w:1) + // Proof Size summary in bytes: + // Measured: `505` + // Estimated: `13382` + // Minimum execution time: 24_427 nanoseconds. + Weight::from_parts(24_952_000, 13382) + .saturating_add(T::DbWeight::get().reads(3_u64)) + .saturating_add(T::DbWeight::get().writes(4_u64)) + } + /// Storage: Alliance Members (r:4 w:2) + /// Proof: Alliance Members (max_values: None, max_size: Some(3211), added: 5686, mode: MaxEncodedLen) + /// Storage: AllianceMotion Proposals (r:1 w:0) + /// Proof Skipped: AllianceMotion Proposals (max_values: Some(1), max_size: None, mode: Measured) + /// Storage: AllianceMotion Members (r:0 w:1) + /// Proof Skipped: AllianceMotion Members (max_values: Some(1), max_size: None, mode: Measured) + /// Storage: AllianceMotion Prime (r:0 w:1) + /// Proof Skipped: AllianceMotion Prime (max_values: Some(1), max_size: None, mode: Measured) + /// Storage: Alliance RetiringMembers (r:0 w:1) + /// Proof: Alliance RetiringMembers (max_values: None, max_size: Some(52), added: 2527, mode: MaxEncodedLen) fn give_retirement_notice() -> Weight { - // Minimum execution time: 44_949 nanoseconds. - Weight::from_ref_time(46_284_000) - .saturating_add(T::DbWeight::get().reads(5)) - .saturating_add(T::DbWeight::get().writes(5)) - } - // Storage: Alliance RetiringMembers (r:1 w:1) - // Storage: Alliance Members (r:1 w:1) - // Storage: Alliance DepositOf (r:1 w:1) - // Storage: System Account (r:1 w:1) + // Proof Size summary in bytes: + // Measured: `505` + // Estimated: `24754` + // Minimum execution time: 33_163 nanoseconds. + Weight::from_parts(33_556_000, 24754) + .saturating_add(T::DbWeight::get().reads(5_u64)) + .saturating_add(T::DbWeight::get().writes(5_u64)) + } + /// Storage: Alliance RetiringMembers (r:1 w:1) + /// Proof: Alliance RetiringMembers (max_values: None, max_size: Some(52), added: 2527, mode: MaxEncodedLen) + /// Storage: Alliance Members (r:1 w:1) + /// Proof: Alliance Members (max_values: None, max_size: Some(3211), added: 5686, mode: MaxEncodedLen) + /// Storage: Alliance DepositOf (r:1 w:1) + /// Proof: Alliance DepositOf (max_values: None, max_size: Some(64), added: 2539, mode: MaxEncodedLen) + /// Storage: System Account (r:1 w:1) + /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) fn retire() -> Weight { - // Minimum execution time: 45_989 nanoseconds. - Weight::from_ref_time(46_586_000) - .saturating_add(T::DbWeight::get().reads(4)) - .saturating_add(T::DbWeight::get().writes(4)) - } - // Storage: Alliance Members (r:3 w:1) - // Storage: AllianceMotion Proposals (r:1 w:0) - // Storage: Alliance DepositOf (r:1 w:1) - // Storage: System Account (r:1 w:1) - // Storage: AllianceMotion Members (r:0 w:1) - // Storage: AllianceMotion Prime (r:0 w:1) + // Proof Size summary in bytes: + // Measured: `750` + // Estimated: `13355` + // Minimum execution time: 32_980 nanoseconds. + Weight::from_parts(33_452_000, 13355) + .saturating_add(T::DbWeight::get().reads(4_u64)) + .saturating_add(T::DbWeight::get().writes(4_u64)) + } + /// Storage: Alliance Members (r:3 w:1) + /// Proof: Alliance Members (max_values: None, max_size: Some(3211), added: 5686, mode: MaxEncodedLen) + /// Storage: AllianceMotion Proposals (r:1 w:0) + /// Proof Skipped: AllianceMotion Proposals (max_values: Some(1), max_size: None, mode: Measured) + /// Storage: Alliance DepositOf (r:1 w:1) + /// Proof: Alliance DepositOf (max_values: None, max_size: Some(64), added: 2539, mode: MaxEncodedLen) + /// Storage: System Account (r:1 w:1) + /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) + /// Storage: AllianceMotion Members (r:0 w:1) + /// Proof Skipped: AllianceMotion Members (max_values: Some(1), max_size: None, mode: Measured) + /// Storage: AllianceMotion Prime (r:0 w:1) + /// Proof Skipped: AllianceMotion Prime (max_values: Some(1), max_size: None, mode: Measured) fn kick_member() -> Weight { - // Minimum execution time: 67_333 nanoseconds. - Weight::from_ref_time(68_302_000) - .saturating_add(T::DbWeight::get().reads(6)) - .saturating_add(T::DbWeight::get().writes(5)) - } - // Storage: Alliance UnscrupulousAccounts (r:1 w:1) - // Storage: Alliance UnscrupulousWebsites (r:1 w:1) + // Proof Size summary in bytes: + // Measured: `801` + // Estimated: `25098` + // Minimum execution time: 54_660 nanoseconds. + Weight::from_parts(55_186_000, 25098) + .saturating_add(T::DbWeight::get().reads(6_u64)) + .saturating_add(T::DbWeight::get().writes(5_u64)) + } + /// Storage: Alliance UnscrupulousAccounts (r:1 w:1) + /// Proof: Alliance UnscrupulousAccounts (max_values: Some(1), max_size: Some(3202), added: 3697, mode: MaxEncodedLen) + /// Storage: Alliance UnscrupulousWebsites (r:1 w:1) + /// Proof: Alliance UnscrupulousWebsites (max_values: Some(1), max_size: Some(25702), added: 26197, mode: MaxEncodedLen) /// The range of component `n` is `[0, 100]`. /// The range of component `l` is `[0, 255]`. fn add_unscrupulous_items(n: u32, l: u32, ) -> Weight { - // Minimum execution time: 16_911 nanoseconds. - Weight::from_ref_time(17_204_000) - // Standard Error: 2_688 - .saturating_add(Weight::from_ref_time(1_324_527).saturating_mul(n.into())) - // Standard Error: 1_052 - .saturating_add(Weight::from_ref_time(73_828).saturating_mul(l.into())) - .saturating_add(T::DbWeight::get().reads(2)) - .saturating_add(T::DbWeight::get().writes(2)) - } - // Storage: Alliance UnscrupulousAccounts (r:1 w:1) - // Storage: Alliance UnscrupulousWebsites (r:1 w:1) + // Proof Size summary in bytes: + // Measured: `246` + // Estimated: `29894` + // Minimum execution time: 7_709 nanoseconds. + Weight::from_parts(7_773_000, 29894) + // Standard Error: 2_645 + .saturating_add(Weight::from_ref_time(1_266_755).saturating_mul(n.into())) + // Standard Error: 1_036 + .saturating_add(Weight::from_ref_time(67_359).saturating_mul(l.into())) + .saturating_add(T::DbWeight::get().reads(2_u64)) + .saturating_add(T::DbWeight::get().writes(2_u64)) + } + /// Storage: Alliance UnscrupulousAccounts (r:1 w:1) + /// Proof: Alliance UnscrupulousAccounts (max_values: Some(1), max_size: Some(3202), added: 3697, mode: MaxEncodedLen) + /// Storage: Alliance UnscrupulousWebsites (r:1 w:1) + /// Proof: Alliance UnscrupulousWebsites (max_values: Some(1), max_size: Some(25702), added: 26197, mode: MaxEncodedLen) /// The range of component `n` is `[0, 100]`. /// The range of component `l` is `[0, 255]`. fn remove_unscrupulous_items(n: u32, l: u32, ) -> Weight { - // Minimum execution time: 17_117 nanoseconds. - Weight::from_ref_time(17_291_000) - // Standard Error: 166_132 - .saturating_add(Weight::from_ref_time(13_173_211).saturating_mul(n.into())) - // Standard Error: 65_064 - .saturating_add(Weight::from_ref_time(478_626).saturating_mul(l.into())) - .saturating_add(T::DbWeight::get().reads(2)) - .saturating_add(T::DbWeight::get().writes(2)) - } - // Storage: Alliance Members (r:3 w:2) - // Storage: AllianceMotion Proposals (r:1 w:0) - // Storage: AllianceMotion Members (r:0 w:1) - // Storage: AllianceMotion Prime (r:0 w:1) + // Proof Size summary in bytes: + // Measured: `0 + n * (289 ±0) + l * (100 ±0)` + // Estimated: `29894` + // Minimum execution time: 7_438 nanoseconds. + Weight::from_parts(7_570_000, 29894) + // Standard Error: 165_072 + .saturating_add(Weight::from_ref_time(13_026_975).saturating_mul(n.into())) + // Standard Error: 64_649 + .saturating_add(Weight::from_ref_time(485_565).saturating_mul(l.into())) + .saturating_add(T::DbWeight::get().reads(2_u64)) + .saturating_add(T::DbWeight::get().writes(2_u64)) + } + /// Storage: Alliance Members (r:3 w:2) + /// Proof: Alliance Members (max_values: None, max_size: Some(3211), added: 5686, mode: MaxEncodedLen) + /// Storage: AllianceMotion Proposals (r:1 w:0) + /// Proof Skipped: AllianceMotion Proposals (max_values: Some(1), max_size: None, mode: Measured) + /// Storage: AllianceMotion Members (r:0 w:1) + /// Proof Skipped: AllianceMotion Members (max_values: Some(1), max_size: None, mode: Measured) + /// Storage: AllianceMotion Prime (r:0 w:1) + /// Proof Skipped: AllianceMotion Prime (max_values: Some(1), max_size: None, mode: Measured) fn abdicate_fellow_status() -> Weight { - // Minimum execution time: 43_347 nanoseconds. - Weight::from_ref_time(43_986_000) - .saturating_add(T::DbWeight::get().reads(4)) - .saturating_add(T::DbWeight::get().writes(4)) + // Proof Size summary in bytes: + // Measured: `505` + // Estimated: `19068` + // Minimum execution time: 31_326 nanoseconds. + Weight::from_parts(31_768_000, 19068) + .saturating_add(T::DbWeight::get().reads(4_u64)) + .saturating_add(T::DbWeight::get().writes(4_u64)) } } // For backwards compatibility and tests impl WeightInfo for () { - // Storage: Alliance Members (r:1 w:0) - // Storage: AllianceMotion ProposalOf (r:1 w:1) - // Storage: AllianceMotion Proposals (r:1 w:1) - // Storage: AllianceMotion ProposalCount (r:1 w:1) - // Storage: AllianceMotion Voting (r:0 w:1) + /// Storage: Alliance Members (r:1 w:0) + /// Proof: Alliance Members (max_values: None, max_size: Some(3211), added: 5686, mode: MaxEncodedLen) + /// Storage: AllianceMotion ProposalOf (r:1 w:1) + /// Proof Skipped: AllianceMotion ProposalOf (max_values: None, max_size: None, mode: Measured) + /// Storage: AllianceMotion Proposals (r:1 w:1) + /// Proof Skipped: AllianceMotion Proposals (max_values: Some(1), max_size: None, mode: Measured) + /// Storage: AllianceMotion ProposalCount (r:1 w:1) + /// Proof Skipped: AllianceMotion ProposalCount (max_values: Some(1), max_size: None, mode: Measured) + /// Storage: AllianceMotion Voting (r:0 w:1) + /// Proof Skipped: AllianceMotion Voting (max_values: None, max_size: None, mode: Measured) /// The range of component `b` is `[1, 1024]`. /// The range of component `m` is `[2, 100]`. /// The range of component `p` is `[1, 100]`. - fn propose_proposed(_b: u32, m: u32, p: u32, ) -> Weight { - // Minimum execution time: 45_945 nanoseconds. - Weight::from_ref_time(44_195_340) - // Standard Error: 2_212 - .saturating_add(Weight::from_ref_time(73_715).saturating_mul(m.into())) - // Standard Error: 2_184 - .saturating_add(Weight::from_ref_time(172_373).saturating_mul(p.into())) - .saturating_add(RocksDbWeight::get().reads(4)) - .saturating_add(RocksDbWeight::get().writes(4)) - } - // Storage: Alliance Members (r:1 w:0) - // Storage: AllianceMotion Voting (r:1 w:1) + fn propose_proposed(b: u32, m: u32, p: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `692 + m * (32 ±0) + p * (36 ±0)` + // Estimated: `11659 + m * (132 ±0) + p * (144 ±0)` + // Minimum execution time: 28_849 nanoseconds. + Weight::from_parts(29_823_933, 11659) + // Standard Error: 74 + .saturating_add(Weight::from_ref_time(830).saturating_mul(b.into())) + // Standard Error: 775 + .saturating_add(Weight::from_ref_time(22_980).saturating_mul(m.into())) + // Standard Error: 765 + .saturating_add(Weight::from_ref_time(90_520).saturating_mul(p.into())) + .saturating_add(RocksDbWeight::get().reads(4_u64)) + .saturating_add(RocksDbWeight::get().writes(4_u64)) + .saturating_add(Weight::from_proof_size(132).saturating_mul(m.into())) + .saturating_add(Weight::from_proof_size(144).saturating_mul(p.into())) + } + /// Storage: Alliance Members (r:1 w:0) + /// Proof: Alliance Members (max_values: None, max_size: Some(3211), added: 5686, mode: MaxEncodedLen) + /// Storage: AllianceMotion Voting (r:1 w:1) + /// Proof Skipped: AllianceMotion Voting (max_values: None, max_size: None, mode: Measured) /// The range of component `m` is `[5, 100]`. fn vote(m: u32, ) -> Weight { - // Minimum execution time: 41_578 nanoseconds. - Weight::from_ref_time(44_299_385) - // Standard Error: 3_727 - .saturating_add(Weight::from_ref_time(138_681).saturating_mul(m.into())) - .saturating_add(RocksDbWeight::get().reads(2)) - .saturating_add(RocksDbWeight::get().writes(1)) - } - // Storage: Alliance Members (r:1 w:0) - // Storage: AllianceMotion Voting (r:1 w:1) - // Storage: AllianceMotion Members (r:1 w:0) - // Storage: AllianceMotion Proposals (r:1 w:1) - // Storage: AllianceMotion ProposalOf (r:0 w:1) + // Proof Size summary in bytes: + // Measured: `1177 + m * (64 ±0)` + // Estimated: `9337 + m * (64 ±0)` + // Minimum execution time: 23_570 nanoseconds. + Weight::from_parts(25_473_196, 9337) + // Standard Error: 824 + .saturating_add(Weight::from_ref_time(54_603).saturating_mul(m.into())) + .saturating_add(RocksDbWeight::get().reads(2_u64)) + .saturating_add(RocksDbWeight::get().writes(1_u64)) + .saturating_add(Weight::from_proof_size(64).saturating_mul(m.into())) + } + /// Storage: Alliance Members (r:1 w:0) + /// Proof: Alliance Members (max_values: None, max_size: Some(3211), added: 5686, mode: MaxEncodedLen) + /// Storage: AllianceMotion Voting (r:1 w:1) + /// Proof Skipped: AllianceMotion Voting (max_values: None, max_size: None, mode: Measured) + /// Storage: AllianceMotion Members (r:1 w:0) + /// Proof Skipped: AllianceMotion Members (max_values: Some(1), max_size: None, mode: Measured) + /// Storage: AllianceMotion Proposals (r:1 w:1) + /// Proof Skipped: AllianceMotion Proposals (max_values: Some(1), max_size: None, mode: Measured) + /// Storage: AllianceMotion ProposalOf (r:0 w:1) + /// Proof Skipped: AllianceMotion ProposalOf (max_values: None, max_size: None, mode: Measured) /// The range of component `m` is `[4, 100]`. /// The range of component `p` is `[1, 100]`. fn close_early_disapproved(m: u32, p: u32, ) -> Weight { - // Minimum execution time: 53_033 nanoseconds. - Weight::from_ref_time(44_608_804) - // Standard Error: 2_306 - .saturating_add(Weight::from_ref_time(102_842).saturating_mul(m.into())) - // Standard Error: 2_248 - .saturating_add(Weight::from_ref_time(172_018).saturating_mul(p.into())) - .saturating_add(RocksDbWeight::get().reads(4)) - .saturating_add(RocksDbWeight::get().writes(3)) - } - // Storage: Alliance Members (r:1 w:0) - // Storage: AllianceMotion Voting (r:1 w:1) - // Storage: AllianceMotion Members (r:1 w:0) - // Storage: AllianceMotion ProposalOf (r:1 w:1) - // Storage: AllianceMotion Proposals (r:1 w:1) + // Proof Size summary in bytes: + // Measured: `730 + m * (96 ±0) + p * (36 ±0)` + // Estimated: `11979 + m * (388 ±0) + p * (148 ±0)` + // Minimum execution time: 35_373 nanoseconds. + Weight::from_parts(32_763_656, 11979) + // Standard Error: 2_041 + .saturating_add(Weight::from_ref_time(52_915).saturating_mul(m.into())) + // Standard Error: 1_991 + .saturating_add(Weight::from_ref_time(78_594).saturating_mul(p.into())) + .saturating_add(RocksDbWeight::get().reads(4_u64)) + .saturating_add(RocksDbWeight::get().writes(3_u64)) + .saturating_add(Weight::from_proof_size(388).saturating_mul(m.into())) + .saturating_add(Weight::from_proof_size(148).saturating_mul(p.into())) + } + /// Storage: Alliance Members (r:1 w:0) + /// Proof: Alliance Members (max_values: None, max_size: Some(3211), added: 5686, mode: MaxEncodedLen) + /// Storage: AllianceMotion Voting (r:1 w:1) + /// Proof Skipped: AllianceMotion Voting (max_values: None, max_size: None, mode: Measured) + /// Storage: AllianceMotion Members (r:1 w:0) + /// Proof Skipped: AllianceMotion Members (max_values: Some(1), max_size: None, mode: Measured) + /// Storage: AllianceMotion ProposalOf (r:1 w:1) + /// Proof Skipped: AllianceMotion ProposalOf (max_values: None, max_size: None, mode: Measured) + /// Storage: AllianceMotion Proposals (r:1 w:1) + /// Proof Skipped: AllianceMotion Proposals (max_values: Some(1), max_size: None, mode: Measured) /// The range of component `b` is `[1, 1024]`. /// The range of component `m` is `[4, 100]`. /// The range of component `p` is `[1, 100]`. fn close_early_approved(b: u32, m: u32, p: u32, ) -> Weight { - // Minimum execution time: 61_157 nanoseconds. - Weight::from_ref_time(53_647_510) - // Standard Error: 235 - .saturating_add(Weight::from_ref_time(88).saturating_mul(b.into())) - // Standard Error: 2_486 - .saturating_add(Weight::from_ref_time(103_912).saturating_mul(m.into())) - // Standard Error: 2_424 - .saturating_add(Weight::from_ref_time(197_471).saturating_mul(p.into())) - .saturating_add(RocksDbWeight::get().reads(5)) - .saturating_add(RocksDbWeight::get().writes(3)) - } - // Storage: Alliance Members (r:1 w:0) - // Storage: AllianceMotion Voting (r:1 w:1) - // Storage: AllianceMotion Members (r:1 w:0) - // Storage: AllianceMotion Prime (r:1 w:0) - // Storage: AllianceMotion Proposals (r:1 w:1) - // Storage: AllianceMotion ProposalOf (r:0 w:1) + // Proof Size summary in bytes: + // Measured: `1139 + m * (96 ±0) + p * (41 ±0)` + // Estimated: `15730 + m * (388 ±0) + p * (164 ±0)` + // Minimum execution time: 44_590 nanoseconds. + Weight::from_parts(43_367_913, 15730) + // Standard Error: 71 + .saturating_add(Weight::from_ref_time(819).saturating_mul(b.into())) + // Standard Error: 751 + .saturating_add(Weight::from_ref_time(39_124).saturating_mul(m.into())) + // Standard Error: 732 + .saturating_add(Weight::from_ref_time(90_469).saturating_mul(p.into())) + .saturating_add(RocksDbWeight::get().reads(5_u64)) + .saturating_add(RocksDbWeight::get().writes(3_u64)) + .saturating_add(Weight::from_proof_size(388).saturating_mul(m.into())) + .saturating_add(Weight::from_proof_size(164).saturating_mul(p.into())) + } + /// Storage: Alliance Members (r:1 w:0) + /// Proof: Alliance Members (max_values: None, max_size: Some(3211), added: 5686, mode: MaxEncodedLen) + /// Storage: AllianceMotion Voting (r:1 w:1) + /// Proof Skipped: AllianceMotion Voting (max_values: None, max_size: None, mode: Measured) + /// Storage: AllianceMotion Members (r:1 w:0) + /// Proof Skipped: AllianceMotion Members (max_values: Some(1), max_size: None, mode: Measured) + /// Storage: AllianceMotion Prime (r:1 w:0) + /// Proof Skipped: AllianceMotion Prime (max_values: Some(1), max_size: None, mode: Measured) + /// Storage: AllianceMotion Proposals (r:1 w:1) + /// Proof Skipped: AllianceMotion Proposals (max_values: Some(1), max_size: None, mode: Measured) + /// Storage: AllianceMotion ProposalOf (r:0 w:1) + /// Proof Skipped: AllianceMotion ProposalOf (max_values: None, max_size: None, mode: Measured) /// The range of component `m` is `[2, 100]`. /// The range of component `p` is `[1, 100]`. fn close_disapproved(m: u32, p: u32, ) -> Weight { - // Minimum execution time: 53_830 nanoseconds. - Weight::from_ref_time(43_926_698) - // Standard Error: 2_495 - .saturating_add(Weight::from_ref_time(112_814).saturating_mul(m.into())) - // Standard Error: 2_465 - .saturating_add(Weight::from_ref_time(186_422).saturating_mul(p.into())) - .saturating_add(RocksDbWeight::get().reads(5)) - .saturating_add(RocksDbWeight::get().writes(3)) - } - // Storage: Alliance Members (r:1 w:0) - // Storage: AllianceMotion Voting (r:1 w:1) - // Storage: AllianceMotion Members (r:1 w:0) - // Storage: AllianceMotion Prime (r:1 w:0) - // Storage: AllianceMotion Proposals (r:1 w:1) - // Storage: AllianceMotion ProposalOf (r:0 w:1) + // Proof Size summary in bytes: + // Measured: `730 + m * (96 ±0) + p * (36 ±0)` + // Estimated: `13201 + m * (485 ±0) + p * (185 ±0)` + // Minimum execution time: 36_796 nanoseconds. + Weight::from_parts(34_578_765, 13201) + // Standard Error: 1_037 + .saturating_add(Weight::from_ref_time(43_508).saturating_mul(m.into())) + // Standard Error: 1_024 + .saturating_add(Weight::from_ref_time(77_084).saturating_mul(p.into())) + .saturating_add(RocksDbWeight::get().reads(5_u64)) + .saturating_add(RocksDbWeight::get().writes(3_u64)) + .saturating_add(Weight::from_proof_size(485).saturating_mul(m.into())) + .saturating_add(Weight::from_proof_size(185).saturating_mul(p.into())) + } + /// Storage: Alliance Members (r:1 w:0) + /// Proof: Alliance Members (max_values: None, max_size: Some(3211), added: 5686, mode: MaxEncodedLen) + /// Storage: AllianceMotion Voting (r:1 w:1) + /// Proof Skipped: AllianceMotion Voting (max_values: None, max_size: None, mode: Measured) + /// Storage: AllianceMotion Members (r:1 w:0) + /// Proof Skipped: AllianceMotion Members (max_values: Some(1), max_size: None, mode: Measured) + /// Storage: AllianceMotion Prime (r:1 w:0) + /// Proof Skipped: AllianceMotion Prime (max_values: Some(1), max_size: None, mode: Measured) + /// Storage: AllianceMotion Proposals (r:1 w:1) + /// Proof Skipped: AllianceMotion Proposals (max_values: Some(1), max_size: None, mode: Measured) + /// Storage: AllianceMotion ProposalOf (r:0 w:1) + /// Proof Skipped: AllianceMotion ProposalOf (max_values: None, max_size: None, mode: Measured) /// The range of component `b` is `[1, 1024]`. /// The range of component `m` is `[5, 100]`. /// The range of component `p` is `[1, 100]`. - fn close_approved(_b: u32, m: u32, p: u32, ) -> Weight { - // Minimum execution time: 53_665 nanoseconds. - Weight::from_ref_time(44_914_913) - // Standard Error: 2_244 - .saturating_add(Weight::from_ref_time(105_848).saturating_mul(m.into())) - // Standard Error: 2_164 - .saturating_add(Weight::from_ref_time(190_830).saturating_mul(p.into())) - .saturating_add(RocksDbWeight::get().reads(5)) - .saturating_add(RocksDbWeight::get().writes(3)) - } - // Storage: Alliance Members (r:2 w:2) - // Storage: AllianceMotion Members (r:1 w:1) + fn close_approved(b: u32, m: u32, p: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `757 + m * (96 ±0) + p * (36 ±0)` + // Estimated: `13146 + m * (480 ±0) + p * (185 ±0)` + // Minimum execution time: 36_897 nanoseconds. + Weight::from_parts(34_169_666, 13146) + // Standard Error: 47 + .saturating_add(Weight::from_ref_time(972).saturating_mul(b.into())) + // Standard Error: 510 + .saturating_add(Weight::from_ref_time(38_084).saturating_mul(m.into())) + // Standard Error: 492 + .saturating_add(Weight::from_ref_time(78_576).saturating_mul(p.into())) + .saturating_add(RocksDbWeight::get().reads(5_u64)) + .saturating_add(RocksDbWeight::get().writes(3_u64)) + .saturating_add(Weight::from_proof_size(480).saturating_mul(m.into())) + .saturating_add(Weight::from_proof_size(185).saturating_mul(p.into())) + } + /// Storage: Alliance Members (r:2 w:2) + /// Proof: Alliance Members (max_values: None, max_size: Some(3211), added: 5686, mode: MaxEncodedLen) + /// Storage: AllianceMotion Members (r:1 w:1) + /// Proof Skipped: AllianceMotion Members (max_values: Some(1), max_size: None, mode: Measured) /// The range of component `m` is `[1, 100]`. /// The range of component `z` is `[0, 100]`. fn init_members(m: u32, z: u32, ) -> Weight { - // Minimum execution time: 46_486 nanoseconds. - Weight::from_ref_time(34_596_922) - // Standard Error: 1_807 - .saturating_add(Weight::from_ref_time(143_225).saturating_mul(m.into())) - // Standard Error: 1_786 - .saturating_add(Weight::from_ref_time(125_729).saturating_mul(z.into())) - .saturating_add(RocksDbWeight::get().reads(3)) - .saturating_add(RocksDbWeight::get().writes(3)) - } - // Storage: Alliance Members (r:2 w:2) - // Storage: AllianceMotion Proposals (r:1 w:0) - // Storage: Alliance DepositOf (r:101 w:50) - // Storage: System Account (r:50 w:50) - // Storage: AllianceMotion Members (r:0 w:1) - // Storage: AllianceMotion Prime (r:0 w:1) + // Proof Size summary in bytes: + // Measured: `217` + // Estimated: `12084` + // Minimum execution time: 29_313 nanoseconds. + Weight::from_parts(20_502_244, 12084) + // Standard Error: 304 + .saturating_add(Weight::from_ref_time(107_994).saturating_mul(m.into())) + // Standard Error: 300 + .saturating_add(Weight::from_ref_time(92_645).saturating_mul(z.into())) + .saturating_add(RocksDbWeight::get().reads(3_u64)) + .saturating_add(RocksDbWeight::get().writes(3_u64)) + } + /// Storage: Alliance Members (r:2 w:2) + /// Proof: Alliance Members (max_values: None, max_size: Some(3211), added: 5686, mode: MaxEncodedLen) + /// Storage: AllianceMotion Proposals (r:1 w:0) + /// Proof Skipped: AllianceMotion Proposals (max_values: Some(1), max_size: None, mode: Measured) + /// Storage: Alliance DepositOf (r:200 w:50) + /// Proof: Alliance DepositOf (max_values: None, max_size: Some(64), added: 2539, mode: MaxEncodedLen) + /// Storage: System Account (r:50 w:50) + /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) + /// Storage: AllianceMotion Members (r:0 w:1) + /// Proof Skipped: AllianceMotion Members (max_values: Some(1), max_size: None, mode: Measured) + /// Storage: AllianceMotion Prime (r:0 w:1) + /// Proof Skipped: AllianceMotion Prime (max_values: Some(1), max_size: None, mode: Measured) /// The range of component `x` is `[1, 100]`. /// The range of component `y` is `[0, 100]`. /// The range of component `z` is `[0, 50]`. fn disband(x: u32, y: u32, z: u32, ) -> Weight { - // Minimum execution time: 251_640 nanoseconds. - Weight::from_ref_time(252_446_000) - // Standard Error: 20_419 - .saturating_add(Weight::from_ref_time(486_667).saturating_mul(x.into())) - // Standard Error: 20_320 - .saturating_add(Weight::from_ref_time(498_890).saturating_mul(y.into())) - // Standard Error: 40_604 - .saturating_add(Weight::from_ref_time(9_411_242).saturating_mul(z.into())) - .saturating_add(RocksDbWeight::get().reads(3)) + // Proof Size summary in bytes: + // Measured: `0 + x * (50 ±0) + y * (51 ±0) + z * (283 ±0)` + // Estimated: `32201 + x * (2587 ±0) + y * (2590 ±0) + z * (3209 ±1)` + // Minimum execution time: 232_895 nanoseconds. + Weight::from_parts(233_860_000, 32201) + // Standard Error: 19_092 + .saturating_add(Weight::from_ref_time(445_664).saturating_mul(x.into())) + // Standard Error: 19_000 + .saturating_add(Weight::from_ref_time(432_571).saturating_mul(y.into())) + // Standard Error: 37_965 + .saturating_add(Weight::from_ref_time(9_386_151).saturating_mul(z.into())) + .saturating_add(RocksDbWeight::get().reads(3_u64)) .saturating_add(RocksDbWeight::get().reads((1_u64).saturating_mul(x.into()))) .saturating_add(RocksDbWeight::get().reads((1_u64).saturating_mul(y.into()))) .saturating_add(RocksDbWeight::get().reads((1_u64).saturating_mul(z.into()))) - .saturating_add(RocksDbWeight::get().writes(4)) + .saturating_add(RocksDbWeight::get().writes(4_u64)) .saturating_add(RocksDbWeight::get().writes((2_u64).saturating_mul(z.into()))) + .saturating_add(Weight::from_proof_size(2587).saturating_mul(x.into())) + .saturating_add(Weight::from_proof_size(2590).saturating_mul(y.into())) + .saturating_add(Weight::from_proof_size(3209).saturating_mul(z.into())) } - // Storage: Alliance Rule (r:0 w:1) + /// Storage: Alliance Rule (r:0 w:1) + /// Proof: Alliance Rule (max_values: Some(1), max_size: Some(87), added: 582, mode: MaxEncodedLen) fn set_rule() -> Weight { - // Minimum execution time: 18_867 nanoseconds. - Weight::from_ref_time(19_155_000) - .saturating_add(RocksDbWeight::get().writes(1)) - } - // Storage: Alliance Announcements (r:1 w:1) + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 9_156 nanoseconds. + Weight::from_ref_time(9_376_000) + .saturating_add(RocksDbWeight::get().writes(1_u64)) + } + /// Storage: Alliance Announcements (r:1 w:1) + /// Proof: Alliance Announcements (max_values: Some(1), max_size: Some(8702), added: 9197, mode: MaxEncodedLen) fn announce() -> Weight { - // Minimum execution time: 21_942 nanoseconds. - Weight::from_ref_time(22_319_000) - .saturating_add(RocksDbWeight::get().reads(1)) - .saturating_add(RocksDbWeight::get().writes(1)) - } - // Storage: Alliance Announcements (r:1 w:1) + // Proof Size summary in bytes: + // Measured: `246` + // Estimated: `9197` + // Minimum execution time: 12_163 nanoseconds. + Weight::from_parts(12_397_000, 9197) + .saturating_add(RocksDbWeight::get().reads(1_u64)) + .saturating_add(RocksDbWeight::get().writes(1_u64)) + } + /// Storage: Alliance Announcements (r:1 w:1) + /// Proof: Alliance Announcements (max_values: Some(1), max_size: Some(8702), added: 9197, mode: MaxEncodedLen) fn remove_announcement() -> Weight { - // Minimum execution time: 23_419 nanoseconds. - Weight::from_ref_time(23_854_000) - .saturating_add(RocksDbWeight::get().reads(1)) - .saturating_add(RocksDbWeight::get().writes(1)) - } - // Storage: Alliance Members (r:3 w:1) - // Storage: Alliance UnscrupulousAccounts (r:1 w:0) - // Storage: System Account (r:1 w:1) - // Storage: Alliance DepositOf (r:0 w:1) + // Proof Size summary in bytes: + // Measured: `351` + // Estimated: `9197` + // Minimum execution time: 12_778 nanoseconds. + Weight::from_parts(12_991_000, 9197) + .saturating_add(RocksDbWeight::get().reads(1_u64)) + .saturating_add(RocksDbWeight::get().writes(1_u64)) + } + /// Storage: Alliance Members (r:3 w:1) + /// Proof: Alliance Members (max_values: None, max_size: Some(3211), added: 5686, mode: MaxEncodedLen) + /// Storage: Alliance UnscrupulousAccounts (r:1 w:0) + /// Proof: Alliance UnscrupulousAccounts (max_values: Some(1), max_size: Some(3202), added: 3697, mode: MaxEncodedLen) + /// Storage: System Account (r:1 w:1) + /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) + /// Storage: Alliance DepositOf (r:0 w:1) + /// Proof: Alliance DepositOf (max_values: None, max_size: Some(64), added: 2539, mode: MaxEncodedLen) fn join_alliance() -> Weight { - // Minimum execution time: 53_016 nanoseconds. - Weight::from_ref_time(53_768_000) - .saturating_add(RocksDbWeight::get().reads(5)) - .saturating_add(RocksDbWeight::get().writes(3)) - } - // Storage: Alliance Members (r:3 w:1) - // Storage: Alliance UnscrupulousAccounts (r:1 w:0) + // Proof Size summary in bytes: + // Measured: `562` + // Estimated: `23358` + // Minimum execution time: 40_216 nanoseconds. + Weight::from_parts(40_535_000, 23358) + .saturating_add(RocksDbWeight::get().reads(5_u64)) + .saturating_add(RocksDbWeight::get().writes(3_u64)) + } + /// Storage: Alliance Members (r:3 w:1) + /// Proof: Alliance Members (max_values: None, max_size: Some(3211), added: 5686, mode: MaxEncodedLen) + /// Storage: Alliance UnscrupulousAccounts (r:1 w:0) + /// Proof: Alliance UnscrupulousAccounts (max_values: Some(1), max_size: Some(3202), added: 3697, mode: MaxEncodedLen) fn nominate_ally() -> Weight { - // Minimum execution time: 40_019 nanoseconds. - Weight::from_ref_time(41_087_000) - .saturating_add(RocksDbWeight::get().reads(4)) - .saturating_add(RocksDbWeight::get().writes(1)) - } - // Storage: Alliance Members (r:2 w:2) - // Storage: AllianceMotion Proposals (r:1 w:0) - // Storage: AllianceMotion Members (r:0 w:1) - // Storage: AllianceMotion Prime (r:0 w:1) + // Proof Size summary in bytes: + // Measured: `429` + // Estimated: `20755` + // Minimum execution time: 28_027 nanoseconds. + Weight::from_parts(28_392_000, 20755) + .saturating_add(RocksDbWeight::get().reads(4_u64)) + .saturating_add(RocksDbWeight::get().writes(1_u64)) + } + /// Storage: Alliance Members (r:2 w:2) + /// Proof: Alliance Members (max_values: None, max_size: Some(3211), added: 5686, mode: MaxEncodedLen) + /// Storage: AllianceMotion Proposals (r:1 w:0) + /// Proof Skipped: AllianceMotion Proposals (max_values: Some(1), max_size: None, mode: Measured) + /// Storage: AllianceMotion Members (r:0 w:1) + /// Proof Skipped: AllianceMotion Members (max_values: Some(1), max_size: None, mode: Measured) + /// Storage: AllianceMotion Prime (r:0 w:1) + /// Proof Skipped: AllianceMotion Prime (max_values: Some(1), max_size: None, mode: Measured) fn elevate_ally() -> Weight { - // Minimum execution time: 35_973 nanoseconds. - Weight::from_ref_time(36_365_000) - .saturating_add(RocksDbWeight::get().reads(3)) - .saturating_add(RocksDbWeight::get().writes(4)) - } - // Storage: Alliance Members (r:4 w:2) - // Storage: AllianceMotion Proposals (r:1 w:0) - // Storage: AllianceMotion Members (r:0 w:1) - // Storage: AllianceMotion Prime (r:0 w:1) - // Storage: Alliance RetiringMembers (r:0 w:1) + // Proof Size summary in bytes: + // Measured: `505` + // Estimated: `13382` + // Minimum execution time: 24_427 nanoseconds. + Weight::from_parts(24_952_000, 13382) + .saturating_add(RocksDbWeight::get().reads(3_u64)) + .saturating_add(RocksDbWeight::get().writes(4_u64)) + } + /// Storage: Alliance Members (r:4 w:2) + /// Proof: Alliance Members (max_values: None, max_size: Some(3211), added: 5686, mode: MaxEncodedLen) + /// Storage: AllianceMotion Proposals (r:1 w:0) + /// Proof Skipped: AllianceMotion Proposals (max_values: Some(1), max_size: None, mode: Measured) + /// Storage: AllianceMotion Members (r:0 w:1) + /// Proof Skipped: AllianceMotion Members (max_values: Some(1), max_size: None, mode: Measured) + /// Storage: AllianceMotion Prime (r:0 w:1) + /// Proof Skipped: AllianceMotion Prime (max_values: Some(1), max_size: None, mode: Measured) + /// Storage: Alliance RetiringMembers (r:0 w:1) + /// Proof: Alliance RetiringMembers (max_values: None, max_size: Some(52), added: 2527, mode: MaxEncodedLen) fn give_retirement_notice() -> Weight { - // Minimum execution time: 44_949 nanoseconds. - Weight::from_ref_time(46_284_000) - .saturating_add(RocksDbWeight::get().reads(5)) - .saturating_add(RocksDbWeight::get().writes(5)) - } - // Storage: Alliance RetiringMembers (r:1 w:1) - // Storage: Alliance Members (r:1 w:1) - // Storage: Alliance DepositOf (r:1 w:1) - // Storage: System Account (r:1 w:1) + // Proof Size summary in bytes: + // Measured: `505` + // Estimated: `24754` + // Minimum execution time: 33_163 nanoseconds. + Weight::from_parts(33_556_000, 24754) + .saturating_add(RocksDbWeight::get().reads(5_u64)) + .saturating_add(RocksDbWeight::get().writes(5_u64)) + } + /// Storage: Alliance RetiringMembers (r:1 w:1) + /// Proof: Alliance RetiringMembers (max_values: None, max_size: Some(52), added: 2527, mode: MaxEncodedLen) + /// Storage: Alliance Members (r:1 w:1) + /// Proof: Alliance Members (max_values: None, max_size: Some(3211), added: 5686, mode: MaxEncodedLen) + /// Storage: Alliance DepositOf (r:1 w:1) + /// Proof: Alliance DepositOf (max_values: None, max_size: Some(64), added: 2539, mode: MaxEncodedLen) + /// Storage: System Account (r:1 w:1) + /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) fn retire() -> Weight { - // Minimum execution time: 45_989 nanoseconds. - Weight::from_ref_time(46_586_000) - .saturating_add(RocksDbWeight::get().reads(4)) - .saturating_add(RocksDbWeight::get().writes(4)) - } - // Storage: Alliance Members (r:3 w:1) - // Storage: AllianceMotion Proposals (r:1 w:0) - // Storage: Alliance DepositOf (r:1 w:1) - // Storage: System Account (r:1 w:1) - // Storage: AllianceMotion Members (r:0 w:1) - // Storage: AllianceMotion Prime (r:0 w:1) + // Proof Size summary in bytes: + // Measured: `750` + // Estimated: `13355` + // Minimum execution time: 32_980 nanoseconds. + Weight::from_parts(33_452_000, 13355) + .saturating_add(RocksDbWeight::get().reads(4_u64)) + .saturating_add(RocksDbWeight::get().writes(4_u64)) + } + /// Storage: Alliance Members (r:3 w:1) + /// Proof: Alliance Members (max_values: None, max_size: Some(3211), added: 5686, mode: MaxEncodedLen) + /// Storage: AllianceMotion Proposals (r:1 w:0) + /// Proof Skipped: AllianceMotion Proposals (max_values: Some(1), max_size: None, mode: Measured) + /// Storage: Alliance DepositOf (r:1 w:1) + /// Proof: Alliance DepositOf (max_values: None, max_size: Some(64), added: 2539, mode: MaxEncodedLen) + /// Storage: System Account (r:1 w:1) + /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) + /// Storage: AllianceMotion Members (r:0 w:1) + /// Proof Skipped: AllianceMotion Members (max_values: Some(1), max_size: None, mode: Measured) + /// Storage: AllianceMotion Prime (r:0 w:1) + /// Proof Skipped: AllianceMotion Prime (max_values: Some(1), max_size: None, mode: Measured) fn kick_member() -> Weight { - // Minimum execution time: 67_333 nanoseconds. - Weight::from_ref_time(68_302_000) - .saturating_add(RocksDbWeight::get().reads(6)) - .saturating_add(RocksDbWeight::get().writes(5)) - } - // Storage: Alliance UnscrupulousAccounts (r:1 w:1) - // Storage: Alliance UnscrupulousWebsites (r:1 w:1) + // Proof Size summary in bytes: + // Measured: `801` + // Estimated: `25098` + // Minimum execution time: 54_660 nanoseconds. + Weight::from_parts(55_186_000, 25098) + .saturating_add(RocksDbWeight::get().reads(6_u64)) + .saturating_add(RocksDbWeight::get().writes(5_u64)) + } + /// Storage: Alliance UnscrupulousAccounts (r:1 w:1) + /// Proof: Alliance UnscrupulousAccounts (max_values: Some(1), max_size: Some(3202), added: 3697, mode: MaxEncodedLen) + /// Storage: Alliance UnscrupulousWebsites (r:1 w:1) + /// Proof: Alliance UnscrupulousWebsites (max_values: Some(1), max_size: Some(25702), added: 26197, mode: MaxEncodedLen) /// The range of component `n` is `[0, 100]`. /// The range of component `l` is `[0, 255]`. fn add_unscrupulous_items(n: u32, l: u32, ) -> Weight { - // Minimum execution time: 16_911 nanoseconds. - Weight::from_ref_time(17_204_000) - // Standard Error: 2_688 - .saturating_add(Weight::from_ref_time(1_324_527).saturating_mul(n.into())) - // Standard Error: 1_052 - .saturating_add(Weight::from_ref_time(73_828).saturating_mul(l.into())) - .saturating_add(RocksDbWeight::get().reads(2)) - .saturating_add(RocksDbWeight::get().writes(2)) - } - // Storage: Alliance UnscrupulousAccounts (r:1 w:1) - // Storage: Alliance UnscrupulousWebsites (r:1 w:1) + // Proof Size summary in bytes: + // Measured: `246` + // Estimated: `29894` + // Minimum execution time: 7_709 nanoseconds. + Weight::from_parts(7_773_000, 29894) + // Standard Error: 2_645 + .saturating_add(Weight::from_ref_time(1_266_755).saturating_mul(n.into())) + // Standard Error: 1_036 + .saturating_add(Weight::from_ref_time(67_359).saturating_mul(l.into())) + .saturating_add(RocksDbWeight::get().reads(2_u64)) + .saturating_add(RocksDbWeight::get().writes(2_u64)) + } + /// Storage: Alliance UnscrupulousAccounts (r:1 w:1) + /// Proof: Alliance UnscrupulousAccounts (max_values: Some(1), max_size: Some(3202), added: 3697, mode: MaxEncodedLen) + /// Storage: Alliance UnscrupulousWebsites (r:1 w:1) + /// Proof: Alliance UnscrupulousWebsites (max_values: Some(1), max_size: Some(25702), added: 26197, mode: MaxEncodedLen) /// The range of component `n` is `[0, 100]`. /// The range of component `l` is `[0, 255]`. fn remove_unscrupulous_items(n: u32, l: u32, ) -> Weight { - // Minimum execution time: 17_117 nanoseconds. - Weight::from_ref_time(17_291_000) - // Standard Error: 166_132 - .saturating_add(Weight::from_ref_time(13_173_211).saturating_mul(n.into())) - // Standard Error: 65_064 - .saturating_add(Weight::from_ref_time(478_626).saturating_mul(l.into())) - .saturating_add(RocksDbWeight::get().reads(2)) - .saturating_add(RocksDbWeight::get().writes(2)) - } - // Storage: Alliance Members (r:3 w:2) - // Storage: AllianceMotion Proposals (r:1 w:0) - // Storage: AllianceMotion Members (r:0 w:1) - // Storage: AllianceMotion Prime (r:0 w:1) + // Proof Size summary in bytes: + // Measured: `0 + n * (289 ±0) + l * (100 ±0)` + // Estimated: `29894` + // Minimum execution time: 7_438 nanoseconds. + Weight::from_parts(7_570_000, 29894) + // Standard Error: 165_072 + .saturating_add(Weight::from_ref_time(13_026_975).saturating_mul(n.into())) + // Standard Error: 64_649 + .saturating_add(Weight::from_ref_time(485_565).saturating_mul(l.into())) + .saturating_add(RocksDbWeight::get().reads(2_u64)) + .saturating_add(RocksDbWeight::get().writes(2_u64)) + } + /// Storage: Alliance Members (r:3 w:2) + /// Proof: Alliance Members (max_values: None, max_size: Some(3211), added: 5686, mode: MaxEncodedLen) + /// Storage: AllianceMotion Proposals (r:1 w:0) + /// Proof Skipped: AllianceMotion Proposals (max_values: Some(1), max_size: None, mode: Measured) + /// Storage: AllianceMotion Members (r:0 w:1) + /// Proof Skipped: AllianceMotion Members (max_values: Some(1), max_size: None, mode: Measured) + /// Storage: AllianceMotion Prime (r:0 w:1) + /// Proof Skipped: AllianceMotion Prime (max_values: Some(1), max_size: None, mode: Measured) fn abdicate_fellow_status() -> Weight { - // Minimum execution time: 43_347 nanoseconds. - Weight::from_ref_time(43_986_000) - .saturating_add(RocksDbWeight::get().reads(4)) - .saturating_add(RocksDbWeight::get().writes(4)) + // Proof Size summary in bytes: + // Measured: `505` + // Estimated: `19068` + // Minimum execution time: 31_326 nanoseconds. + Weight::from_parts(31_768_000, 19068) + .saturating_add(RocksDbWeight::get().reads(4_u64)) + .saturating_add(RocksDbWeight::get().writes(4_u64)) } } diff --git a/frame/assets/src/weights.rs b/frame/assets/src/weights.rs index 747198ae3..c2028105c 100644 --- a/frame/assets/src/weights.rs +++ b/frame/assets/src/weights.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2022 Parity Technologies (UK) Ltd. +// Copyright (C) 2023 Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); @@ -18,7 +18,8 @@ //! Autogenerated weights for pallet_assets //! //! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev -//! DATE: 2022-11-07, STEPS: `50`, REPEAT: 20, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! DATE: 2023-01-24, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! WORST CASE MAP SIZE: `1000000` //! HOSTNAME: `bm2`, CPU: `Intel(R) Core(TM) i7-7700K CPU @ 4.20GHz` //! EXECUTION: Some(Wasm), WASM-EXECUTION: Compiled, CHAIN: Some("dev"), DB CACHE: 1024 @@ -50,8 +51,8 @@ pub trait WeightInfo { fn create() -> Weight; fn force_create() -> Weight; fn start_destroy() -> Weight; - fn destroy_accounts(c: u32) -> Weight; - fn destroy_approvals(m: u32) -> Weight; + fn destroy_accounts(c: u32, ) -> Weight; + fn destroy_approvals(a: u32, ) -> Weight; fn finish_destroy() -> Weight; fn mint() -> Weight; fn burn() -> Weight; @@ -78,464 +79,728 @@ pub trait WeightInfo { /// Weights for pallet_assets using the Substrate node and recommended hardware. pub struct SubstrateWeight(PhantomData); impl WeightInfo for SubstrateWeight { - // Storage: Assets Asset (r:1 w:1) + /// Storage: Assets Asset (r:1 w:1) + /// Proof: Assets Asset (max_values: None, max_size: Some(210), added: 2685, mode: MaxEncodedLen) + /// Storage: System Account (r:1 w:1) + /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) fn create() -> Weight { - // Minimum execution time: 33_241 nanoseconds. - Weight::from_ref_time(33_873_000 as u64) - .saturating_add(T::DbWeight::get().reads(1 as u64)) - .saturating_add(T::DbWeight::get().writes(1 as u64)) - } - // Storage: Assets Asset (r:1 w:1) + // Proof Size summary in bytes: + // Measured: `325` + // Estimated: `5288` + // Minimum execution time: 23_623 nanoseconds. + Weight::from_parts(24_072_000, 5288) + .saturating_add(T::DbWeight::get().reads(2_u64)) + .saturating_add(T::DbWeight::get().writes(2_u64)) + } + /// Storage: Assets Asset (r:1 w:1) + /// Proof: Assets Asset (max_values: None, max_size: Some(210), added: 2685, mode: MaxEncodedLen) fn force_create() -> Weight { - // Minimum execution time: 19_883 nanoseconds. - Weight::from_ref_time(20_651_000 as u64) - .saturating_add(T::DbWeight::get().reads(1 as u64)) - .saturating_add(T::DbWeight::get().writes(1 as u64)) - } - - // Storage: Assets Asset (r:1 w:1) + // Proof Size summary in bytes: + // Measured: `153` + // Estimated: `2685` + // Minimum execution time: 13_145 nanoseconds. + Weight::from_parts(13_572_000, 2685) + .saturating_add(T::DbWeight::get().reads(1_u64)) + .saturating_add(T::DbWeight::get().writes(1_u64)) + } + /// Storage: Assets Asset (r:1 w:1) + /// Proof: Assets Asset (max_values: None, max_size: Some(210), added: 2685, mode: MaxEncodedLen) fn start_destroy() -> Weight { - Weight::from_ref_time(31_000_000 as u64) - .saturating_add(T::DbWeight::get().reads(1 as u64)) - .saturating_add(T::DbWeight::get().writes(1 as u64)) - } - - // Storage: Assets Asset (r:1 w:1) - // Storage: Assets Account (r:1 w:0) - // Storage: System Account (r:20 w:20) + // Proof Size summary in bytes: + // Measured: `417` + // Estimated: `2685` + // Minimum execution time: 13_420 nanoseconds. + Weight::from_parts(13_649_000, 2685) + .saturating_add(T::DbWeight::get().reads(1_u64)) + .saturating_add(T::DbWeight::get().writes(1_u64)) + } + /// Storage: Assets Asset (r:1 w:1) + /// Proof: Assets Asset (max_values: None, max_size: Some(210), added: 2685, mode: MaxEncodedLen) + /// Storage: Assets Account (r:1001 w:1000) + /// Proof: Assets Account (max_values: None, max_size: Some(102), added: 2577, mode: MaxEncodedLen) + /// Storage: System Account (r:1000 w:1000) + /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) /// The range of component `c` is `[0, 1000]`. fn destroy_accounts(c: u32, ) -> Weight { - Weight::from_ref_time(37_000_000 as u64) - // Standard Error: 19_301 - .saturating_add(Weight::from_ref_time(25_467_908 as u64).saturating_mul(c as u64)) - .saturating_add(T::DbWeight::get().reads(2 as u64)) - .saturating_add(T::DbWeight::get().reads((2 as u64).saturating_mul(c as u64))) - .saturating_add(T::DbWeight::get().writes(1 as u64)) - .saturating_add(T::DbWeight::get().writes((2 as u64).saturating_mul(c as u64))) - } - - // Storage: Assets Asset (r:1 w:1) - // Storage: Assets Approvals (r:1 w:0) + // Proof Size summary in bytes: + // Measured: `25 + c * (240 ±0)` + // Estimated: `5262 + c * (5180 ±0)` + // Minimum execution time: 17_565 nanoseconds. + Weight::from_parts(17_757_000, 5262) + // Standard Error: 15_192 + .saturating_add(Weight::from_ref_time(13_799_167).saturating_mul(c.into())) + .saturating_add(T::DbWeight::get().reads(2_u64)) + .saturating_add(T::DbWeight::get().reads((2_u64).saturating_mul(c.into()))) + .saturating_add(T::DbWeight::get().writes(1_u64)) + .saturating_add(T::DbWeight::get().writes((2_u64).saturating_mul(c.into()))) + .saturating_add(Weight::from_proof_size(5180).saturating_mul(c.into())) + } + /// Storage: Assets Asset (r:1 w:1) + /// Proof: Assets Asset (max_values: None, max_size: Some(210), added: 2685, mode: MaxEncodedLen) + /// Storage: Assets Approvals (r:1001 w:1000) + /// Proof: Assets Approvals (max_values: None, max_size: Some(148), added: 2623, mode: MaxEncodedLen) /// The range of component `a` is `[0, 1000]`. fn destroy_approvals(a: u32, ) -> Weight { - Weight::from_ref_time(39_000_000 as u64) - // Standard Error: 14_298 - .saturating_add(Weight::from_ref_time(27_632_144 as u64).saturating_mul(a as u64)) - .saturating_add(T::DbWeight::get().reads(2 as u64)) - .saturating_add(T::DbWeight::get().reads((1 as u64).saturating_mul(a as u64))) - .saturating_add(T::DbWeight::get().writes(1 as u64)) - .saturating_add(T::DbWeight::get().writes((1 as u64).saturating_mul(a as u64))) - } - - // Storage: Assets Asset (r:1 w:1) - // Storage: Assets Metadata (r:1 w:0) + // Proof Size summary in bytes: + // Measured: `554 + a * (86 ±0)` + // Estimated: `5308 + a * (2623 ±0)` + // Minimum execution time: 18_251 nanoseconds. + Weight::from_parts(18_359_000, 5308) + // Standard Error: 10_051 + .saturating_add(Weight::from_ref_time(13_613_342).saturating_mul(a.into())) + .saturating_add(T::DbWeight::get().reads(2_u64)) + .saturating_add(T::DbWeight::get().reads((1_u64).saturating_mul(a.into()))) + .saturating_add(T::DbWeight::get().writes(1_u64)) + .saturating_add(T::DbWeight::get().writes((1_u64).saturating_mul(a.into()))) + .saturating_add(Weight::from_proof_size(2623).saturating_mul(a.into())) + } + /// Storage: Assets Asset (r:1 w:1) + /// Proof: Assets Asset (max_values: None, max_size: Some(210), added: 2685, mode: MaxEncodedLen) + /// Storage: Assets Metadata (r:1 w:0) + /// Proof: Assets Metadata (max_values: None, max_size: Some(140), added: 2615, mode: MaxEncodedLen) fn finish_destroy() -> Weight { - Weight::from_ref_time(33_000_000 as u64) - .saturating_add(T::DbWeight::get().reads(2 as u64)) - .saturating_add(T::DbWeight::get().writes(1 as u64)) - } - - // Storage: Assets Asset (r:1 w:1) - // Storage: Assets Account (r:1 w:1) + // Proof Size summary in bytes: + // Measured: `383` + // Estimated: `5300` + // Minimum execution time: 14_344 nanoseconds. + Weight::from_parts(14_619_000, 5300) + .saturating_add(T::DbWeight::get().reads(2_u64)) + .saturating_add(T::DbWeight::get().writes(1_u64)) + } + /// Storage: Assets Asset (r:1 w:1) + /// Proof: Assets Asset (max_values: None, max_size: Some(210), added: 2685, mode: MaxEncodedLen) + /// Storage: Assets Account (r:1 w:1) + /// Proof: Assets Account (max_values: None, max_size: Some(102), added: 2577, mode: MaxEncodedLen) fn mint() -> Weight { - // Minimum execution time: 36_782 nanoseconds. - Weight::from_ref_time(37_340_000 as u64) - .saturating_add(T::DbWeight::get().reads(2 as u64)) - .saturating_add(T::DbWeight::get().writes(2 as u64)) - } - // Storage: Assets Asset (r:1 w:1) - // Storage: Assets Account (r:1 w:1) + // Proof Size summary in bytes: + // Measured: `383` + // Estimated: `5262` + // Minimum execution time: 24_992 nanoseconds. + Weight::from_parts(25_784_000, 5262) + .saturating_add(T::DbWeight::get().reads(2_u64)) + .saturating_add(T::DbWeight::get().writes(2_u64)) + } + /// Storage: Assets Asset (r:1 w:1) + /// Proof: Assets Asset (max_values: None, max_size: Some(210), added: 2685, mode: MaxEncodedLen) + /// Storage: Assets Account (r:1 w:1) + /// Proof: Assets Account (max_values: None, max_size: Some(102), added: 2577, mode: MaxEncodedLen) fn burn() -> Weight { - // Minimum execution time: 44_425 nanoseconds. - Weight::from_ref_time(45_485_000 as u64) - .saturating_add(T::DbWeight::get().reads(2 as u64)) - .saturating_add(T::DbWeight::get().writes(2 as u64)) - } - // Storage: Assets Asset (r:1 w:1) - // Storage: Assets Account (r:2 w:2) - // Storage: System Account (r:1 w:1) + // Proof Size summary in bytes: + // Measured: `491` + // Estimated: `5262` + // Minimum execution time: 31_233 nanoseconds. + Weight::from_parts(31_511_000, 5262) + .saturating_add(T::DbWeight::get().reads(2_u64)) + .saturating_add(T::DbWeight::get().writes(2_u64)) + } + /// Storage: Assets Asset (r:1 w:1) + /// Proof: Assets Asset (max_values: None, max_size: Some(210), added: 2685, mode: MaxEncodedLen) + /// Storage: Assets Account (r:2 w:2) + /// Proof: Assets Account (max_values: None, max_size: Some(102), added: 2577, mode: MaxEncodedLen) + /// Storage: System Account (r:1 w:1) + /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) fn transfer() -> Weight { - // Minimum execution time: 58_294 nanoseconds. - Weight::from_ref_time(59_447_000 as u64) - .saturating_add(T::DbWeight::get().reads(4 as u64)) - .saturating_add(T::DbWeight::get().writes(4 as u64)) - } - // Storage: Assets Asset (r:1 w:1) - // Storage: Assets Account (r:2 w:2) - // Storage: System Account (r:1 w:1) + // Proof Size summary in bytes: + // Measured: `530` + // Estimated: `10442` + // Minimum execution time: 43_002 nanoseconds. + Weight::from_parts(43_533_000, 10442) + .saturating_add(T::DbWeight::get().reads(4_u64)) + .saturating_add(T::DbWeight::get().writes(4_u64)) + } + /// Storage: Assets Asset (r:1 w:1) + /// Proof: Assets Asset (max_values: None, max_size: Some(210), added: 2685, mode: MaxEncodedLen) + /// Storage: Assets Account (r:2 w:2) + /// Proof: Assets Account (max_values: None, max_size: Some(102), added: 2577, mode: MaxEncodedLen) + /// Storage: System Account (r:1 w:1) + /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) fn transfer_keep_alive() -> Weight { - // Minimum execution time: 46_704 nanoseconds. - Weight::from_ref_time(47_521_000 as u64) - .saturating_add(T::DbWeight::get().reads(4 as u64)) - .saturating_add(T::DbWeight::get().writes(4 as u64)) - } - // Storage: Assets Asset (r:1 w:1) - // Storage: Assets Account (r:2 w:2) - // Storage: System Account (r:1 w:1) + // Proof Size summary in bytes: + // Measured: `530` + // Estimated: `10442` + // Minimum execution time: 38_220 nanoseconds. + Weight::from_parts(38_639_000, 10442) + .saturating_add(T::DbWeight::get().reads(4_u64)) + .saturating_add(T::DbWeight::get().writes(4_u64)) + } + /// Storage: Assets Asset (r:1 w:1) + /// Proof: Assets Asset (max_values: None, max_size: Some(210), added: 2685, mode: MaxEncodedLen) + /// Storage: Assets Account (r:2 w:2) + /// Proof: Assets Account (max_values: None, max_size: Some(102), added: 2577, mode: MaxEncodedLen) + /// Storage: System Account (r:1 w:1) + /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) fn force_transfer() -> Weight { - // Minimum execution time: 57_647 nanoseconds. - Weight::from_ref_time(58_417_000 as u64) - .saturating_add(T::DbWeight::get().reads(4 as u64)) - .saturating_add(T::DbWeight::get().writes(4 as u64)) - } - // Storage: Assets Asset (r:1 w:0) - // Storage: Assets Account (r:1 w:1) + // Proof Size summary in bytes: + // Measured: `530` + // Estimated: `10442` + // Minimum execution time: 43_171 nanoseconds. + Weight::from_parts(43_543_000, 10442) + .saturating_add(T::DbWeight::get().reads(4_u64)) + .saturating_add(T::DbWeight::get().writes(4_u64)) + } + /// Storage: Assets Asset (r:1 w:0) + /// Proof: Assets Asset (max_values: None, max_size: Some(210), added: 2685, mode: MaxEncodedLen) + /// Storage: Assets Account (r:1 w:1) + /// Proof: Assets Account (max_values: None, max_size: Some(102), added: 2577, mode: MaxEncodedLen) fn freeze() -> Weight { - // Minimum execution time: 26_827 nanoseconds. - Weight::from_ref_time(27_373_000 as u64) - .saturating_add(T::DbWeight::get().reads(2 as u64)) - .saturating_add(T::DbWeight::get().writes(1 as u64)) - } - // Storage: Assets Asset (r:1 w:0) - // Storage: Assets Account (r:1 w:1) + // Proof Size summary in bytes: + // Measured: `491` + // Estimated: `5262` + // Minimum execution time: 16_972 nanoseconds. + Weight::from_parts(17_498_000, 5262) + .saturating_add(T::DbWeight::get().reads(2_u64)) + .saturating_add(T::DbWeight::get().writes(1_u64)) + } + /// Storage: Assets Asset (r:1 w:0) + /// Proof: Assets Asset (max_values: None, max_size: Some(210), added: 2685, mode: MaxEncodedLen) + /// Storage: Assets Account (r:1 w:1) + /// Proof: Assets Account (max_values: None, max_size: Some(102), added: 2577, mode: MaxEncodedLen) fn thaw() -> Weight { - // Minimum execution time: 26_291 nanoseconds. - Weight::from_ref_time(26_854_000 as u64) - .saturating_add(T::DbWeight::get().reads(2 as u64)) - .saturating_add(T::DbWeight::get().writes(1 as u64)) - } - // Storage: Assets Asset (r:1 w:1) + // Proof Size summary in bytes: + // Measured: `491` + // Estimated: `5262` + // Minimum execution time: 17_471 nanoseconds. + Weight::from_parts(17_842_000, 5262) + .saturating_add(T::DbWeight::get().reads(2_u64)) + .saturating_add(T::DbWeight::get().writes(1_u64)) + } + /// Storage: Assets Asset (r:1 w:1) + /// Proof: Assets Asset (max_values: None, max_size: Some(210), added: 2685, mode: MaxEncodedLen) fn freeze_asset() -> Weight { - // Minimum execution time: 22_694 nanoseconds. - Weight::from_ref_time(23_613_000 as u64) - .saturating_add(T::DbWeight::get().reads(1 as u64)) - .saturating_add(T::DbWeight::get().writes(1 as u64)) - } - // Storage: Assets Asset (r:1 w:1) + // Proof Size summary in bytes: + // Measured: `417` + // Estimated: `2685` + // Minimum execution time: 13_497 nanoseconds. + Weight::from_parts(13_719_000, 2685) + .saturating_add(T::DbWeight::get().reads(1_u64)) + .saturating_add(T::DbWeight::get().writes(1_u64)) + } + /// Storage: Assets Asset (r:1 w:1) + /// Proof: Assets Asset (max_values: None, max_size: Some(210), added: 2685, mode: MaxEncodedLen) fn thaw_asset() -> Weight { - // Minimum execution time: 22_572 nanoseconds. - Weight::from_ref_time(24_121_000 as u64) - .saturating_add(T::DbWeight::get().reads(1 as u64)) - .saturating_add(T::DbWeight::get().writes(1 as u64)) - } - // Storage: Assets Asset (r:1 w:1) - // Storage: Assets Metadata (r:1 w:0) + // Proof Size summary in bytes: + // Measured: `417` + // Estimated: `2685` + // Minimum execution time: 13_424 nanoseconds. + Weight::from_parts(13_764_000, 2685) + .saturating_add(T::DbWeight::get().reads(1_u64)) + .saturating_add(T::DbWeight::get().writes(1_u64)) + } + /// Storage: Assets Asset (r:1 w:1) + /// Proof: Assets Asset (max_values: None, max_size: Some(210), added: 2685, mode: MaxEncodedLen) + /// Storage: Assets Metadata (r:1 w:0) + /// Proof: Assets Metadata (max_values: None, max_size: Some(140), added: 2615, mode: MaxEncodedLen) fn transfer_ownership() -> Weight { - // Minimum execution time: 23_949 nanoseconds. - Weight::from_ref_time(24_347_000 as u64) - .saturating_add(T::DbWeight::get().reads(2 as u64)) - .saturating_add(T::DbWeight::get().writes(1 as u64)) - } - // Storage: Assets Asset (r:1 w:1) + // Proof Size summary in bytes: + // Measured: `383` + // Estimated: `5300` + // Minimum execution time: 15_028 nanoseconds. + Weight::from_parts(15_366_000, 5300) + .saturating_add(T::DbWeight::get().reads(2_u64)) + .saturating_add(T::DbWeight::get().writes(1_u64)) + } + /// Storage: Assets Asset (r:1 w:1) + /// Proof: Assets Asset (max_values: None, max_size: Some(210), added: 2685, mode: MaxEncodedLen) fn set_team() -> Weight { - // Minimum execution time: 23_102 nanoseconds. - Weight::from_ref_time(23_518_000 as u64) - .saturating_add(T::DbWeight::get().reads(1 as u64)) - .saturating_add(T::DbWeight::get().writes(1 as u64)) - } - // Storage: Assets Asset (r:1 w:0) - // Storage: Assets Metadata (r:1 w:1) + // Proof Size summary in bytes: + // Measured: `383` + // Estimated: `2685` + // Minimum execution time: 14_197 nanoseconds. + Weight::from_parts(14_437_000, 2685) + .saturating_add(T::DbWeight::get().reads(1_u64)) + .saturating_add(T::DbWeight::get().writes(1_u64)) + } + /// Storage: Assets Asset (r:1 w:0) + /// Proof: Assets Asset (max_values: None, max_size: Some(210), added: 2685, mode: MaxEncodedLen) + /// Storage: Assets Metadata (r:1 w:1) + /// Proof: Assets Metadata (max_values: None, max_size: Some(140), added: 2615, mode: MaxEncodedLen) /// The range of component `n` is `[0, 50]`. /// The range of component `s` is `[0, 50]`. - fn set_metadata(_n: u32, s: u32, ) -> Weight { - // Minimum execution time: 41_032 nanoseconds. - Weight::from_ref_time(42_845_624 as u64) - // Standard Error: 1_274 - .saturating_add(Weight::from_ref_time(1_875 as u64).saturating_mul(s as u64)) - .saturating_add(T::DbWeight::get().reads(2 as u64)) - .saturating_add(T::DbWeight::get().writes(1 as u64)) - } - // Storage: Assets Asset (r:1 w:0) - // Storage: Assets Metadata (r:1 w:1) + fn set_metadata(n: u32, s: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `383` + // Estimated: `5300` + // Minimum execution time: 23_707 nanoseconds. + Weight::from_parts(24_466_834, 5300) + // Standard Error: 635 + .saturating_add(Weight::from_ref_time(730).saturating_mul(n.into())) + // Standard Error: 635 + .saturating_add(Weight::from_ref_time(3_975).saturating_mul(s.into())) + .saturating_add(T::DbWeight::get().reads(2_u64)) + .saturating_add(T::DbWeight::get().writes(1_u64)) + } + /// Storage: Assets Asset (r:1 w:0) + /// Proof: Assets Asset (max_values: None, max_size: Some(210), added: 2685, mode: MaxEncodedLen) + /// Storage: Assets Metadata (r:1 w:1) + /// Proof: Assets Metadata (max_values: None, max_size: Some(140), added: 2615, mode: MaxEncodedLen) fn clear_metadata() -> Weight { - // Minimum execution time: 42_570 nanoseconds. - Weight::from_ref_time(42_957_000 as u64) - .saturating_add(T::DbWeight::get().reads(2 as u64)) - .saturating_add(T::DbWeight::get().writes(1 as u64)) - } - // Storage: Assets Asset (r:1 w:0) - // Storage: Assets Metadata (r:1 w:1) + // Proof Size summary in bytes: + // Measured: `579` + // Estimated: `5300` + // Minimum execution time: 23_997 nanoseconds. + Weight::from_parts(24_812_000, 5300) + .saturating_add(T::DbWeight::get().reads(2_u64)) + .saturating_add(T::DbWeight::get().writes(1_u64)) + } + /// Storage: Assets Asset (r:1 w:0) + /// Proof: Assets Asset (max_values: None, max_size: Some(210), added: 2685, mode: MaxEncodedLen) + /// Storage: Assets Metadata (r:1 w:1) + /// Proof: Assets Metadata (max_values: None, max_size: Some(140), added: 2615, mode: MaxEncodedLen) /// The range of component `n` is `[0, 50]`. /// The range of component `s` is `[0, 50]`. - fn force_set_metadata(n: u32, s: u32, ) -> Weight { - // Minimum execution time: 22_768 nanoseconds. - Weight::from_ref_time(23_868_816 as u64) - // Standard Error: 612 - .saturating_add(Weight::from_ref_time(1_602 as u64).saturating_mul(n as u64)) - // Standard Error: 612 - .saturating_add(Weight::from_ref_time(2_097 as u64).saturating_mul(s as u64)) - .saturating_add(T::DbWeight::get().reads(2 as u64)) - .saturating_add(T::DbWeight::get().writes(1 as u64)) - } - // Storage: Assets Asset (r:1 w:0) - // Storage: Assets Metadata (r:1 w:1) + fn force_set_metadata(_n: u32, s: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `190` + // Estimated: `5300` + // Minimum execution time: 14_028 nanoseconds. + Weight::from_parts(15_217_669, 5300) + // Standard Error: 2_266 + .saturating_add(Weight::from_ref_time(2_045).saturating_mul(s.into())) + .saturating_add(T::DbWeight::get().reads(2_u64)) + .saturating_add(T::DbWeight::get().writes(1_u64)) + } + /// Storage: Assets Asset (r:1 w:0) + /// Proof: Assets Asset (max_values: None, max_size: Some(210), added: 2685, mode: MaxEncodedLen) + /// Storage: Assets Metadata (r:1 w:1) + /// Proof: Assets Metadata (max_values: None, max_size: Some(140), added: 2615, mode: MaxEncodedLen) fn force_clear_metadata() -> Weight { - // Minimum execution time: 41_863 nanoseconds. - Weight::from_ref_time(42_643_000 as u64) - .saturating_add(T::DbWeight::get().reads(2 as u64)) - .saturating_add(T::DbWeight::get().writes(1 as u64)) - } - // Storage: Assets Asset (r:1 w:1) + // Proof Size summary in bytes: + // Measured: `579` + // Estimated: `5300` + // Minimum execution time: 23_754 nanoseconds. + Weight::from_parts(24_154_000, 5300) + .saturating_add(T::DbWeight::get().reads(2_u64)) + .saturating_add(T::DbWeight::get().writes(1_u64)) + } + /// Storage: Assets Asset (r:1 w:1) + /// Proof: Assets Asset (max_values: None, max_size: Some(210), added: 2685, mode: MaxEncodedLen) fn force_asset_status() -> Weight { - // Minimum execution time: 21_747 nanoseconds. - Weight::from_ref_time(22_595_000 as u64) - .saturating_add(T::DbWeight::get().reads(1 as u64)) - .saturating_add(T::DbWeight::get().writes(1 as u64)) - } - // Storage: Assets Asset (r:1 w:1) - // Storage: Assets Approvals (r:1 w:1) + // Proof Size summary in bytes: + // Measured: `383` + // Estimated: `2685` + // Minimum execution time: 13_128 nanoseconds. + Weight::from_parts(13_428_000, 2685) + .saturating_add(T::DbWeight::get().reads(1_u64)) + .saturating_add(T::DbWeight::get().writes(1_u64)) + } + /// Storage: Assets Asset (r:1 w:1) + /// Proof: Assets Asset (max_values: None, max_size: Some(210), added: 2685, mode: MaxEncodedLen) + /// Storage: Assets Approvals (r:1 w:1) + /// Proof: Assets Approvals (max_values: None, max_size: Some(148), added: 2623, mode: MaxEncodedLen) fn approve_transfer() -> Weight { - // Minimum execution time: 45_602 nanoseconds. - Weight::from_ref_time(46_004_000 as u64) - .saturating_add(T::DbWeight::get().reads(2 as u64)) - .saturating_add(T::DbWeight::get().writes(2 as u64)) - } - // Storage: Assets Approvals (r:1 w:1) - // Storage: Assets Asset (r:1 w:1) - // Storage: Assets Account (r:2 w:2) - // Storage: System Account (r:1 w:1) + // Proof Size summary in bytes: + // Measured: `417` + // Estimated: `5308` + // Minimum execution time: 27_224 nanoseconds. + Weight::from_parts(27_665_000, 5308) + .saturating_add(T::DbWeight::get().reads(2_u64)) + .saturating_add(T::DbWeight::get().writes(2_u64)) + } + /// Storage: Assets Asset (r:1 w:1) + /// Proof: Assets Asset (max_values: None, max_size: Some(210), added: 2685, mode: MaxEncodedLen) + /// Storage: Assets Approvals (r:1 w:1) + /// Proof: Assets Approvals (max_values: None, max_size: Some(148), added: 2623, mode: MaxEncodedLen) + /// Storage: Assets Account (r:2 w:2) + /// Proof: Assets Account (max_values: None, max_size: Some(102), added: 2577, mode: MaxEncodedLen) + /// Storage: System Account (r:1 w:1) + /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) fn transfer_approved() -> Weight { - // Minimum execution time: 70_944 nanoseconds. - Weight::from_ref_time(71_722_000 as u64) - .saturating_add(T::DbWeight::get().reads(5 as u64)) - .saturating_add(T::DbWeight::get().writes(5 as u64)) - } - // Storage: Assets Asset (r:1 w:1) - // Storage: Assets Approvals (r:1 w:1) + // Proof Size summary in bytes: + // Measured: `700` + // Estimated: `13065` + // Minimum execution time: 55_837 nanoseconds. + Weight::from_parts(56_636_000, 13065) + .saturating_add(T::DbWeight::get().reads(5_u64)) + .saturating_add(T::DbWeight::get().writes(5_u64)) + } + /// Storage: Assets Asset (r:1 w:1) + /// Proof: Assets Asset (max_values: None, max_size: Some(210), added: 2685, mode: MaxEncodedLen) + /// Storage: Assets Approvals (r:1 w:1) + /// Proof: Assets Approvals (max_values: None, max_size: Some(148), added: 2623, mode: MaxEncodedLen) fn cancel_approval() -> Weight { - // Minimum execution time: 46_316 nanoseconds. - Weight::from_ref_time(46_910_000 as u64) - .saturating_add(T::DbWeight::get().reads(2 as u64)) - .saturating_add(T::DbWeight::get().writes(2 as u64)) - } - // Storage: Assets Asset (r:1 w:1) - // Storage: Assets Approvals (r:1 w:1) + // Proof Size summary in bytes: + // Measured: `587` + // Estimated: `5308` + // Minimum execution time: 28_915 nanoseconds. + Weight::from_parts(29_325_000, 5308) + .saturating_add(T::DbWeight::get().reads(2_u64)) + .saturating_add(T::DbWeight::get().writes(2_u64)) + } + /// Storage: Assets Asset (r:1 w:1) + /// Proof: Assets Asset (max_values: None, max_size: Some(210), added: 2685, mode: MaxEncodedLen) + /// Storage: Assets Approvals (r:1 w:1) + /// Proof: Assets Approvals (max_values: None, max_size: Some(148), added: 2623, mode: MaxEncodedLen) fn force_cancel_approval() -> Weight { - // Minimum execution time: 47_145 nanoseconds. - Weight::from_ref_time(47_611_000 as u64) - .saturating_add(T::DbWeight::get().reads(2 as u64)) - .saturating_add(T::DbWeight::get().writes(2 as u64)) + // Proof Size summary in bytes: + // Measured: `587` + // Estimated: `5308` + // Minimum execution time: 29_103 nanoseconds. + Weight::from_parts(29_599_000, 5308) + .saturating_add(T::DbWeight::get().reads(2_u64)) + .saturating_add(T::DbWeight::get().writes(2_u64)) } } // For backwards compatibility and tests impl WeightInfo for () { - // Storage: Assets Asset (r:1 w:1) + /// Storage: Assets Asset (r:1 w:1) + /// Proof: Assets Asset (max_values: None, max_size: Some(210), added: 2685, mode: MaxEncodedLen) + /// Storage: System Account (r:1 w:1) + /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) fn create() -> Weight { - // Minimum execution time: 33_241 nanoseconds. - Weight::from_ref_time(33_873_000 as u64) - .saturating_add(RocksDbWeight::get().reads(1 as u64)) - .saturating_add(RocksDbWeight::get().writes(1 as u64)) - } - // Storage: Assets Asset (r:1 w:1) + // Proof Size summary in bytes: + // Measured: `325` + // Estimated: `5288` + // Minimum execution time: 23_623 nanoseconds. + Weight::from_parts(24_072_000, 5288) + .saturating_add(RocksDbWeight::get().reads(2_u64)) + .saturating_add(RocksDbWeight::get().writes(2_u64)) + } + /// Storage: Assets Asset (r:1 w:1) + /// Proof: Assets Asset (max_values: None, max_size: Some(210), added: 2685, mode: MaxEncodedLen) fn force_create() -> Weight { - // Minimum execution time: 19_883 nanoseconds. - Weight::from_ref_time(20_651_000 as u64) - .saturating_add(RocksDbWeight::get().reads(1 as u64)) - .saturating_add(RocksDbWeight::get().writes(1 as u64)) - } - - // Storage: Assets Asset (r:1 w:1) + // Proof Size summary in bytes: + // Measured: `153` + // Estimated: `2685` + // Minimum execution time: 13_145 nanoseconds. + Weight::from_parts(13_572_000, 2685) + .saturating_add(RocksDbWeight::get().reads(1_u64)) + .saturating_add(RocksDbWeight::get().writes(1_u64)) + } + /// Storage: Assets Asset (r:1 w:1) + /// Proof: Assets Asset (max_values: None, max_size: Some(210), added: 2685, mode: MaxEncodedLen) fn start_destroy() -> Weight { - Weight::from_ref_time(31_000_000 as u64) - .saturating_add(RocksDbWeight::get().reads(1 as u64)) - .saturating_add(RocksDbWeight::get().writes(1 as u64)) - } - - // Storage: Assets Asset (r:1 w:1) - // Storage: Assets Account (r:1 w:0) - // Storage: System Account (r:20 w:20) + // Proof Size summary in bytes: + // Measured: `417` + // Estimated: `2685` + // Minimum execution time: 13_420 nanoseconds. + Weight::from_parts(13_649_000, 2685) + .saturating_add(RocksDbWeight::get().reads(1_u64)) + .saturating_add(RocksDbWeight::get().writes(1_u64)) + } + /// Storage: Assets Asset (r:1 w:1) + /// Proof: Assets Asset (max_values: None, max_size: Some(210), added: 2685, mode: MaxEncodedLen) + /// Storage: Assets Account (r:1001 w:1000) + /// Proof: Assets Account (max_values: None, max_size: Some(102), added: 2577, mode: MaxEncodedLen) + /// Storage: System Account (r:1000 w:1000) + /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) /// The range of component `c` is `[0, 1000]`. fn destroy_accounts(c: u32, ) -> Weight { - Weight::from_ref_time(37_000_000 as u64) - // Standard Error: 19_301 - .saturating_add(Weight::from_ref_time(25_467_908 as u64).saturating_mul(c as u64)) - .saturating_add(RocksDbWeight::get().reads(2 as u64)) - .saturating_add(RocksDbWeight::get().reads((2 as u64).saturating_mul(c as u64))) - .saturating_add(RocksDbWeight::get().writes(1 as u64)) - .saturating_add(RocksDbWeight::get().writes((2 as u64).saturating_mul(c as u64))) - } - - // Storage: Assets Asset (r:1 w:1) - // Storage: Assets Approvals (r:1 w:0) + // Proof Size summary in bytes: + // Measured: `25 + c * (240 ±0)` + // Estimated: `5262 + c * (5180 ±0)` + // Minimum execution time: 17_565 nanoseconds. + Weight::from_parts(17_757_000, 5262) + // Standard Error: 15_192 + .saturating_add(Weight::from_ref_time(13_799_167).saturating_mul(c.into())) + .saturating_add(RocksDbWeight::get().reads(2_u64)) + .saturating_add(RocksDbWeight::get().reads((2_u64).saturating_mul(c.into()))) + .saturating_add(RocksDbWeight::get().writes(1_u64)) + .saturating_add(RocksDbWeight::get().writes((2_u64).saturating_mul(c.into()))) + .saturating_add(Weight::from_proof_size(5180).saturating_mul(c.into())) + } + /// Storage: Assets Asset (r:1 w:1) + /// Proof: Assets Asset (max_values: None, max_size: Some(210), added: 2685, mode: MaxEncodedLen) + /// Storage: Assets Approvals (r:1001 w:1000) + /// Proof: Assets Approvals (max_values: None, max_size: Some(148), added: 2623, mode: MaxEncodedLen) /// The range of component `a` is `[0, 1000]`. fn destroy_approvals(a: u32, ) -> Weight { - Weight::from_ref_time(39_000_000 as u64) - // Standard Error: 14_298 - .saturating_add(Weight::from_ref_time(27_632_144 as u64).saturating_mul(a as u64)) - .saturating_add(RocksDbWeight::get().reads(2 as u64)) - .saturating_add(RocksDbWeight::get().reads((1 as u64).saturating_mul(a as u64))) - .saturating_add(RocksDbWeight::get().writes(1 as u64)) - .saturating_add(RocksDbWeight::get().writes((1 as u64).saturating_mul(a as u64))) - } - - // Storage: Assets Asset (r:1 w:1) - // Storage: Assets Metadata (r:1 w:0) + // Proof Size summary in bytes: + // Measured: `554 + a * (86 ±0)` + // Estimated: `5308 + a * (2623 ±0)` + // Minimum execution time: 18_251 nanoseconds. + Weight::from_parts(18_359_000, 5308) + // Standard Error: 10_051 + .saturating_add(Weight::from_ref_time(13_613_342).saturating_mul(a.into())) + .saturating_add(RocksDbWeight::get().reads(2_u64)) + .saturating_add(RocksDbWeight::get().reads((1_u64).saturating_mul(a.into()))) + .saturating_add(RocksDbWeight::get().writes(1_u64)) + .saturating_add(RocksDbWeight::get().writes((1_u64).saturating_mul(a.into()))) + .saturating_add(Weight::from_proof_size(2623).saturating_mul(a.into())) + } + /// Storage: Assets Asset (r:1 w:1) + /// Proof: Assets Asset (max_values: None, max_size: Some(210), added: 2685, mode: MaxEncodedLen) + /// Storage: Assets Metadata (r:1 w:0) + /// Proof: Assets Metadata (max_values: None, max_size: Some(140), added: 2615, mode: MaxEncodedLen) fn finish_destroy() -> Weight { - Weight::from_ref_time(33_000_000 as u64) - .saturating_add(RocksDbWeight::get().reads(2 as u64)) - .saturating_add(RocksDbWeight::get().writes(1 as u64)) - } - - // Storage: Assets Asset (r:1 w:1) - // Storage: Assets Account (r:1 w:1) + // Proof Size summary in bytes: + // Measured: `383` + // Estimated: `5300` + // Minimum execution time: 14_344 nanoseconds. + Weight::from_parts(14_619_000, 5300) + .saturating_add(RocksDbWeight::get().reads(2_u64)) + .saturating_add(RocksDbWeight::get().writes(1_u64)) + } + /// Storage: Assets Asset (r:1 w:1) + /// Proof: Assets Asset (max_values: None, max_size: Some(210), added: 2685, mode: MaxEncodedLen) + /// Storage: Assets Account (r:1 w:1) + /// Proof: Assets Account (max_values: None, max_size: Some(102), added: 2577, mode: MaxEncodedLen) fn mint() -> Weight { - // Minimum execution time: 36_782 nanoseconds. - Weight::from_ref_time(37_340_000 as u64) - .saturating_add(RocksDbWeight::get().reads(2 as u64)) - .saturating_add(RocksDbWeight::get().writes(2 as u64)) - } - // Storage: Assets Asset (r:1 w:1) - // Storage: Assets Account (r:1 w:1) + // Proof Size summary in bytes: + // Measured: `383` + // Estimated: `5262` + // Minimum execution time: 24_992 nanoseconds. + Weight::from_parts(25_784_000, 5262) + .saturating_add(RocksDbWeight::get().reads(2_u64)) + .saturating_add(RocksDbWeight::get().writes(2_u64)) + } + /// Storage: Assets Asset (r:1 w:1) + /// Proof: Assets Asset (max_values: None, max_size: Some(210), added: 2685, mode: MaxEncodedLen) + /// Storage: Assets Account (r:1 w:1) + /// Proof: Assets Account (max_values: None, max_size: Some(102), added: 2577, mode: MaxEncodedLen) fn burn() -> Weight { - // Minimum execution time: 44_425 nanoseconds. - Weight::from_ref_time(45_485_000 as u64) - .saturating_add(RocksDbWeight::get().reads(2 as u64)) - .saturating_add(RocksDbWeight::get().writes(2 as u64)) - } - // Storage: Assets Asset (r:1 w:1) - // Storage: Assets Account (r:2 w:2) - // Storage: System Account (r:1 w:1) + // Proof Size summary in bytes: + // Measured: `491` + // Estimated: `5262` + // Minimum execution time: 31_233 nanoseconds. + Weight::from_parts(31_511_000, 5262) + .saturating_add(RocksDbWeight::get().reads(2_u64)) + .saturating_add(RocksDbWeight::get().writes(2_u64)) + } + /// Storage: Assets Asset (r:1 w:1) + /// Proof: Assets Asset (max_values: None, max_size: Some(210), added: 2685, mode: MaxEncodedLen) + /// Storage: Assets Account (r:2 w:2) + /// Proof: Assets Account (max_values: None, max_size: Some(102), added: 2577, mode: MaxEncodedLen) + /// Storage: System Account (r:1 w:1) + /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) fn transfer() -> Weight { - // Minimum execution time: 58_294 nanoseconds. - Weight::from_ref_time(59_447_000 as u64) - .saturating_add(RocksDbWeight::get().reads(4 as u64)) - .saturating_add(RocksDbWeight::get().writes(4 as u64)) - } - // Storage: Assets Asset (r:1 w:1) - // Storage: Assets Account (r:2 w:2) - // Storage: System Account (r:1 w:1) + // Proof Size summary in bytes: + // Measured: `530` + // Estimated: `10442` + // Minimum execution time: 43_002 nanoseconds. + Weight::from_parts(43_533_000, 10442) + .saturating_add(RocksDbWeight::get().reads(4_u64)) + .saturating_add(RocksDbWeight::get().writes(4_u64)) + } + /// Storage: Assets Asset (r:1 w:1) + /// Proof: Assets Asset (max_values: None, max_size: Some(210), added: 2685, mode: MaxEncodedLen) + /// Storage: Assets Account (r:2 w:2) + /// Proof: Assets Account (max_values: None, max_size: Some(102), added: 2577, mode: MaxEncodedLen) + /// Storage: System Account (r:1 w:1) + /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) fn transfer_keep_alive() -> Weight { - // Minimum execution time: 46_704 nanoseconds. - Weight::from_ref_time(47_521_000 as u64) - .saturating_add(RocksDbWeight::get().reads(4 as u64)) - .saturating_add(RocksDbWeight::get().writes(4 as u64)) - } - // Storage: Assets Asset (r:1 w:1) - // Storage: Assets Account (r:2 w:2) - // Storage: System Account (r:1 w:1) + // Proof Size summary in bytes: + // Measured: `530` + // Estimated: `10442` + // Minimum execution time: 38_220 nanoseconds. + Weight::from_parts(38_639_000, 10442) + .saturating_add(RocksDbWeight::get().reads(4_u64)) + .saturating_add(RocksDbWeight::get().writes(4_u64)) + } + /// Storage: Assets Asset (r:1 w:1) + /// Proof: Assets Asset (max_values: None, max_size: Some(210), added: 2685, mode: MaxEncodedLen) + /// Storage: Assets Account (r:2 w:2) + /// Proof: Assets Account (max_values: None, max_size: Some(102), added: 2577, mode: MaxEncodedLen) + /// Storage: System Account (r:1 w:1) + /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) fn force_transfer() -> Weight { - // Minimum execution time: 57_647 nanoseconds. - Weight::from_ref_time(58_417_000 as u64) - .saturating_add(RocksDbWeight::get().reads(4 as u64)) - .saturating_add(RocksDbWeight::get().writes(4 as u64)) - } - // Storage: Assets Asset (r:1 w:0) - // Storage: Assets Account (r:1 w:1) + // Proof Size summary in bytes: + // Measured: `530` + // Estimated: `10442` + // Minimum execution time: 43_171 nanoseconds. + Weight::from_parts(43_543_000, 10442) + .saturating_add(RocksDbWeight::get().reads(4_u64)) + .saturating_add(RocksDbWeight::get().writes(4_u64)) + } + /// Storage: Assets Asset (r:1 w:0) + /// Proof: Assets Asset (max_values: None, max_size: Some(210), added: 2685, mode: MaxEncodedLen) + /// Storage: Assets Account (r:1 w:1) + /// Proof: Assets Account (max_values: None, max_size: Some(102), added: 2577, mode: MaxEncodedLen) fn freeze() -> Weight { - // Minimum execution time: 26_827 nanoseconds. - Weight::from_ref_time(27_373_000 as u64) - .saturating_add(RocksDbWeight::get().reads(2 as u64)) - .saturating_add(RocksDbWeight::get().writes(1 as u64)) - } - // Storage: Assets Asset (r:1 w:0) - // Storage: Assets Account (r:1 w:1) + // Proof Size summary in bytes: + // Measured: `491` + // Estimated: `5262` + // Minimum execution time: 16_972 nanoseconds. + Weight::from_parts(17_498_000, 5262) + .saturating_add(RocksDbWeight::get().reads(2_u64)) + .saturating_add(RocksDbWeight::get().writes(1_u64)) + } + /// Storage: Assets Asset (r:1 w:0) + /// Proof: Assets Asset (max_values: None, max_size: Some(210), added: 2685, mode: MaxEncodedLen) + /// Storage: Assets Account (r:1 w:1) + /// Proof: Assets Account (max_values: None, max_size: Some(102), added: 2577, mode: MaxEncodedLen) fn thaw() -> Weight { - // Minimum execution time: 26_291 nanoseconds. - Weight::from_ref_time(26_854_000 as u64) - .saturating_add(RocksDbWeight::get().reads(2 as u64)) - .saturating_add(RocksDbWeight::get().writes(1 as u64)) - } - // Storage: Assets Asset (r:1 w:1) + // Proof Size summary in bytes: + // Measured: `491` + // Estimated: `5262` + // Minimum execution time: 17_471 nanoseconds. + Weight::from_parts(17_842_000, 5262) + .saturating_add(RocksDbWeight::get().reads(2_u64)) + .saturating_add(RocksDbWeight::get().writes(1_u64)) + } + /// Storage: Assets Asset (r:1 w:1) + /// Proof: Assets Asset (max_values: None, max_size: Some(210), added: 2685, mode: MaxEncodedLen) fn freeze_asset() -> Weight { - // Minimum execution time: 22_694 nanoseconds. - Weight::from_ref_time(23_613_000 as u64) - .saturating_add(RocksDbWeight::get().reads(1 as u64)) - .saturating_add(RocksDbWeight::get().writes(1 as u64)) - } - // Storage: Assets Asset (r:1 w:1) + // Proof Size summary in bytes: + // Measured: `417` + // Estimated: `2685` + // Minimum execution time: 13_497 nanoseconds. + Weight::from_parts(13_719_000, 2685) + .saturating_add(RocksDbWeight::get().reads(1_u64)) + .saturating_add(RocksDbWeight::get().writes(1_u64)) + } + /// Storage: Assets Asset (r:1 w:1) + /// Proof: Assets Asset (max_values: None, max_size: Some(210), added: 2685, mode: MaxEncodedLen) fn thaw_asset() -> Weight { - // Minimum execution time: 22_572 nanoseconds. - Weight::from_ref_time(24_121_000 as u64) - .saturating_add(RocksDbWeight::get().reads(1 as u64)) - .saturating_add(RocksDbWeight::get().writes(1 as u64)) - } - // Storage: Assets Asset (r:1 w:1) - // Storage: Assets Metadata (r:1 w:0) + // Proof Size summary in bytes: + // Measured: `417` + // Estimated: `2685` + // Minimum execution time: 13_424 nanoseconds. + Weight::from_parts(13_764_000, 2685) + .saturating_add(RocksDbWeight::get().reads(1_u64)) + .saturating_add(RocksDbWeight::get().writes(1_u64)) + } + /// Storage: Assets Asset (r:1 w:1) + /// Proof: Assets Asset (max_values: None, max_size: Some(210), added: 2685, mode: MaxEncodedLen) + /// Storage: Assets Metadata (r:1 w:0) + /// Proof: Assets Metadata (max_values: None, max_size: Some(140), added: 2615, mode: MaxEncodedLen) fn transfer_ownership() -> Weight { - // Minimum execution time: 23_949 nanoseconds. - Weight::from_ref_time(24_347_000 as u64) - .saturating_add(RocksDbWeight::get().reads(2 as u64)) - .saturating_add(RocksDbWeight::get().writes(1 as u64)) - } - // Storage: Assets Asset (r:1 w:1) + // Proof Size summary in bytes: + // Measured: `383` + // Estimated: `5300` + // Minimum execution time: 15_028 nanoseconds. + Weight::from_parts(15_366_000, 5300) + .saturating_add(RocksDbWeight::get().reads(2_u64)) + .saturating_add(RocksDbWeight::get().writes(1_u64)) + } + /// Storage: Assets Asset (r:1 w:1) + /// Proof: Assets Asset (max_values: None, max_size: Some(210), added: 2685, mode: MaxEncodedLen) fn set_team() -> Weight { - // Minimum execution time: 23_102 nanoseconds. - Weight::from_ref_time(23_518_000 as u64) - .saturating_add(RocksDbWeight::get().reads(1 as u64)) - .saturating_add(RocksDbWeight::get().writes(1 as u64)) - } - // Storage: Assets Asset (r:1 w:0) - // Storage: Assets Metadata (r:1 w:1) + // Proof Size summary in bytes: + // Measured: `383` + // Estimated: `2685` + // Minimum execution time: 14_197 nanoseconds. + Weight::from_parts(14_437_000, 2685) + .saturating_add(RocksDbWeight::get().reads(1_u64)) + .saturating_add(RocksDbWeight::get().writes(1_u64)) + } + /// Storage: Assets Asset (r:1 w:0) + /// Proof: Assets Asset (max_values: None, max_size: Some(210), added: 2685, mode: MaxEncodedLen) + /// Storage: Assets Metadata (r:1 w:1) + /// Proof: Assets Metadata (max_values: None, max_size: Some(140), added: 2615, mode: MaxEncodedLen) /// The range of component `n` is `[0, 50]`. /// The range of component `s` is `[0, 50]`. - fn set_metadata(_n: u32, s: u32, ) -> Weight { - // Minimum execution time: 41_032 nanoseconds. - Weight::from_ref_time(42_845_624 as u64) - // Standard Error: 1_274 - .saturating_add(Weight::from_ref_time(1_875 as u64).saturating_mul(s as u64)) - .saturating_add(RocksDbWeight::get().reads(2 as u64)) - .saturating_add(RocksDbWeight::get().writes(1 as u64)) - } - // Storage: Assets Asset (r:1 w:0) - // Storage: Assets Metadata (r:1 w:1) + fn set_metadata(n: u32, s: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `383` + // Estimated: `5300` + // Minimum execution time: 23_707 nanoseconds. + Weight::from_parts(24_466_834, 5300) + // Standard Error: 635 + .saturating_add(Weight::from_ref_time(730).saturating_mul(n.into())) + // Standard Error: 635 + .saturating_add(Weight::from_ref_time(3_975).saturating_mul(s.into())) + .saturating_add(RocksDbWeight::get().reads(2_u64)) + .saturating_add(RocksDbWeight::get().writes(1_u64)) + } + /// Storage: Assets Asset (r:1 w:0) + /// Proof: Assets Asset (max_values: None, max_size: Some(210), added: 2685, mode: MaxEncodedLen) + /// Storage: Assets Metadata (r:1 w:1) + /// Proof: Assets Metadata (max_values: None, max_size: Some(140), added: 2615, mode: MaxEncodedLen) fn clear_metadata() -> Weight { - // Minimum execution time: 42_570 nanoseconds. - Weight::from_ref_time(42_957_000 as u64) - .saturating_add(RocksDbWeight::get().reads(2 as u64)) - .saturating_add(RocksDbWeight::get().writes(1 as u64)) - } - // Storage: Assets Asset (r:1 w:0) - // Storage: Assets Metadata (r:1 w:1) + // Proof Size summary in bytes: + // Measured: `579` + // Estimated: `5300` + // Minimum execution time: 23_997 nanoseconds. + Weight::from_parts(24_812_000, 5300) + .saturating_add(RocksDbWeight::get().reads(2_u64)) + .saturating_add(RocksDbWeight::get().writes(1_u64)) + } + /// Storage: Assets Asset (r:1 w:0) + /// Proof: Assets Asset (max_values: None, max_size: Some(210), added: 2685, mode: MaxEncodedLen) + /// Storage: Assets Metadata (r:1 w:1) + /// Proof: Assets Metadata (max_values: None, max_size: Some(140), added: 2615, mode: MaxEncodedLen) /// The range of component `n` is `[0, 50]`. /// The range of component `s` is `[0, 50]`. - fn force_set_metadata(n: u32, s: u32, ) -> Weight { - // Minimum execution time: 22_768 nanoseconds. - Weight::from_ref_time(23_868_816 as u64) - // Standard Error: 612 - .saturating_add(Weight::from_ref_time(1_602 as u64).saturating_mul(n as u64)) - // Standard Error: 612 - .saturating_add(Weight::from_ref_time(2_097 as u64).saturating_mul(s as u64)) - .saturating_add(RocksDbWeight::get().reads(2 as u64)) - .saturating_add(RocksDbWeight::get().writes(1 as u64)) - } - // Storage: Assets Asset (r:1 w:0) - // Storage: Assets Metadata (r:1 w:1) + fn force_set_metadata(_n: u32, s: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `190` + // Estimated: `5300` + // Minimum execution time: 14_028 nanoseconds. + Weight::from_parts(15_217_669, 5300) + // Standard Error: 2_266 + .saturating_add(Weight::from_ref_time(2_045).saturating_mul(s.into())) + .saturating_add(RocksDbWeight::get().reads(2_u64)) + .saturating_add(RocksDbWeight::get().writes(1_u64)) + } + /// Storage: Assets Asset (r:1 w:0) + /// Proof: Assets Asset (max_values: None, max_size: Some(210), added: 2685, mode: MaxEncodedLen) + /// Storage: Assets Metadata (r:1 w:1) + /// Proof: Assets Metadata (max_values: None, max_size: Some(140), added: 2615, mode: MaxEncodedLen) fn force_clear_metadata() -> Weight { - // Minimum execution time: 41_863 nanoseconds. - Weight::from_ref_time(42_643_000 as u64) - .saturating_add(RocksDbWeight::get().reads(2 as u64)) - .saturating_add(RocksDbWeight::get().writes(1 as u64)) - } - // Storage: Assets Asset (r:1 w:1) + // Proof Size summary in bytes: + // Measured: `579` + // Estimated: `5300` + // Minimum execution time: 23_754 nanoseconds. + Weight::from_parts(24_154_000, 5300) + .saturating_add(RocksDbWeight::get().reads(2_u64)) + .saturating_add(RocksDbWeight::get().writes(1_u64)) + } + /// Storage: Assets Asset (r:1 w:1) + /// Proof: Assets Asset (max_values: None, max_size: Some(210), added: 2685, mode: MaxEncodedLen) fn force_asset_status() -> Weight { - // Minimum execution time: 21_747 nanoseconds. - Weight::from_ref_time(22_595_000 as u64) - .saturating_add(RocksDbWeight::get().reads(1 as u64)) - .saturating_add(RocksDbWeight::get().writes(1 as u64)) - } - // Storage: Assets Asset (r:1 w:1) - // Storage: Assets Approvals (r:1 w:1) + // Proof Size summary in bytes: + // Measured: `383` + // Estimated: `2685` + // Minimum execution time: 13_128 nanoseconds. + Weight::from_parts(13_428_000, 2685) + .saturating_add(RocksDbWeight::get().reads(1_u64)) + .saturating_add(RocksDbWeight::get().writes(1_u64)) + } + /// Storage: Assets Asset (r:1 w:1) + /// Proof: Assets Asset (max_values: None, max_size: Some(210), added: 2685, mode: MaxEncodedLen) + /// Storage: Assets Approvals (r:1 w:1) + /// Proof: Assets Approvals (max_values: None, max_size: Some(148), added: 2623, mode: MaxEncodedLen) fn approve_transfer() -> Weight { - // Minimum execution time: 45_602 nanoseconds. - Weight::from_ref_time(46_004_000 as u64) - .saturating_add(RocksDbWeight::get().reads(2 as u64)) - .saturating_add(RocksDbWeight::get().writes(2 as u64)) - } - // Storage: Assets Approvals (r:1 w:1) - // Storage: Assets Asset (r:1 w:1) - // Storage: Assets Account (r:2 w:2) - // Storage: System Account (r:1 w:1) + // Proof Size summary in bytes: + // Measured: `417` + // Estimated: `5308` + // Minimum execution time: 27_224 nanoseconds. + Weight::from_parts(27_665_000, 5308) + .saturating_add(RocksDbWeight::get().reads(2_u64)) + .saturating_add(RocksDbWeight::get().writes(2_u64)) + } + /// Storage: Assets Asset (r:1 w:1) + /// Proof: Assets Asset (max_values: None, max_size: Some(210), added: 2685, mode: MaxEncodedLen) + /// Storage: Assets Approvals (r:1 w:1) + /// Proof: Assets Approvals (max_values: None, max_size: Some(148), added: 2623, mode: MaxEncodedLen) + /// Storage: Assets Account (r:2 w:2) + /// Proof: Assets Account (max_values: None, max_size: Some(102), added: 2577, mode: MaxEncodedLen) + /// Storage: System Account (r:1 w:1) + /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) fn transfer_approved() -> Weight { - // Minimum execution time: 70_944 nanoseconds. - Weight::from_ref_time(71_722_000 as u64) - .saturating_add(RocksDbWeight::get().reads(5 as u64)) - .saturating_add(RocksDbWeight::get().writes(5 as u64)) - } - // Storage: Assets Asset (r:1 w:1) - // Storage: Assets Approvals (r:1 w:1) + // Proof Size summary in bytes: + // Measured: `700` + // Estimated: `13065` + // Minimum execution time: 55_837 nanoseconds. + Weight::from_parts(56_636_000, 13065) + .saturating_add(RocksDbWeight::get().reads(5_u64)) + .saturating_add(RocksDbWeight::get().writes(5_u64)) + } + /// Storage: Assets Asset (r:1 w:1) + /// Proof: Assets Asset (max_values: None, max_size: Some(210), added: 2685, mode: MaxEncodedLen) + /// Storage: Assets Approvals (r:1 w:1) + /// Proof: Assets Approvals (max_values: None, max_size: Some(148), added: 2623, mode: MaxEncodedLen) fn cancel_approval() -> Weight { - // Minimum execution time: 46_316 nanoseconds. - Weight::from_ref_time(46_910_000 as u64) - .saturating_add(RocksDbWeight::get().reads(2 as u64)) - .saturating_add(RocksDbWeight::get().writes(2 as u64)) - } - // Storage: Assets Asset (r:1 w:1) - // Storage: Assets Approvals (r:1 w:1) + // Proof Size summary in bytes: + // Measured: `587` + // Estimated: `5308` + // Minimum execution time: 28_915 nanoseconds. + Weight::from_parts(29_325_000, 5308) + .saturating_add(RocksDbWeight::get().reads(2_u64)) + .saturating_add(RocksDbWeight::get().writes(2_u64)) + } + /// Storage: Assets Asset (r:1 w:1) + /// Proof: Assets Asset (max_values: None, max_size: Some(210), added: 2685, mode: MaxEncodedLen) + /// Storage: Assets Approvals (r:1 w:1) + /// Proof: Assets Approvals (max_values: None, max_size: Some(148), added: 2623, mode: MaxEncodedLen) fn force_cancel_approval() -> Weight { - // Minimum execution time: 47_145 nanoseconds. - Weight::from_ref_time(47_611_000 as u64) - .saturating_add(RocksDbWeight::get().reads(2 as u64)) - .saturating_add(RocksDbWeight::get().writes(2 as u64)) + // Proof Size summary in bytes: + // Measured: `587` + // Estimated: `5308` + // Minimum execution time: 29_103 nanoseconds. + Weight::from_parts(29_599_000, 5308) + .saturating_add(RocksDbWeight::get().reads(2_u64)) + .saturating_add(RocksDbWeight::get().writes(2_u64)) } } diff --git a/frame/bags-list/src/weights.rs b/frame/bags-list/src/weights.rs index a298dc317..d06f7b0db 100644 --- a/frame/bags-list/src/weights.rs +++ b/frame/bags-list/src/weights.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2022 Parity Technologies (UK) Ltd. +// Copyright (C) 2023 Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); @@ -18,7 +18,8 @@ //! Autogenerated weights for pallet_bags_list //! //! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev -//! DATE: 2022-11-07, STEPS: `50`, REPEAT: 20, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! DATE: 2023-01-24, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! WORST CASE MAP SIZE: `1000000` //! HOSTNAME: `bm2`, CPU: `Intel(R) Core(TM) i7-7700K CPU @ 4.20GHz` //! EXECUTION: Some(Wasm), WASM-EXECUTION: Compiled, CHAIN: Some("dev"), DB CACHE: 1024 @@ -55,70 +56,114 @@ pub trait WeightInfo { /// Weights for pallet_bags_list using the Substrate node and recommended hardware. pub struct SubstrateWeight(PhantomData); impl WeightInfo for SubstrateWeight { - // Storage: Staking Bonded (r:1 w:0) - // Storage: Staking Ledger (r:1 w:0) - // Storage: VoterList ListNodes (r:4 w:4) - // Storage: VoterList ListBags (r:1 w:1) + /// Storage: Staking Bonded (r:1 w:0) + /// Proof: Staking Bonded (max_values: None, max_size: Some(72), added: 2547, mode: MaxEncodedLen) + /// Storage: Staking Ledger (r:1 w:0) + /// Proof: Staking Ledger (max_values: None, max_size: Some(1091), added: 3566, mode: MaxEncodedLen) + /// Storage: VoterList ListNodes (r:4 w:4) + /// Proof: VoterList ListNodes (max_values: None, max_size: Some(154), added: 2629, mode: MaxEncodedLen) + /// Storage: VoterList ListBags (r:1 w:1) + /// Proof: VoterList ListBags (max_values: None, max_size: Some(82), added: 2557, mode: MaxEncodedLen) fn rebag_non_terminal() -> Weight { - // Minimum execution time: 73_553 nanoseconds. - Weight::from_ref_time(74_366_000 as u64) - .saturating_add(T::DbWeight::get().reads(7 as u64)) - .saturating_add(T::DbWeight::get().writes(5 as u64)) + // Proof Size summary in bytes: + // Measured: `1916` + // Estimated: `19186` + // Minimum execution time: 54_348 nanoseconds. + Weight::from_parts(55_024_000, 19186) + .saturating_add(T::DbWeight::get().reads(7_u64)) + .saturating_add(T::DbWeight::get().writes(5_u64)) } - // Storage: Staking Bonded (r:1 w:0) - // Storage: Staking Ledger (r:1 w:0) - // Storage: VoterList ListNodes (r:3 w:3) - // Storage: VoterList ListBags (r:2 w:2) + /// Storage: Staking Bonded (r:1 w:0) + /// Proof: Staking Bonded (max_values: None, max_size: Some(72), added: 2547, mode: MaxEncodedLen) + /// Storage: Staking Ledger (r:1 w:0) + /// Proof: Staking Ledger (max_values: None, max_size: Some(1091), added: 3566, mode: MaxEncodedLen) + /// Storage: VoterList ListNodes (r:3 w:3) + /// Proof: VoterList ListNodes (max_values: None, max_size: Some(154), added: 2629, mode: MaxEncodedLen) + /// Storage: VoterList ListBags (r:2 w:2) + /// Proof: VoterList ListBags (max_values: None, max_size: Some(82), added: 2557, mode: MaxEncodedLen) fn rebag_terminal() -> Weight { - // Minimum execution time: 72_700 nanoseconds. - Weight::from_ref_time(73_322_000 as u64) - .saturating_add(T::DbWeight::get().reads(7 as u64)) - .saturating_add(T::DbWeight::get().writes(5 as u64)) + // Proof Size summary in bytes: + // Measured: `1810` + // Estimated: `19114` + // Minimum execution time: 53_306 nanoseconds. + Weight::from_parts(54_263_000, 19114) + .saturating_add(T::DbWeight::get().reads(7_u64)) + .saturating_add(T::DbWeight::get().writes(5_u64)) } - // Storage: VoterList ListNodes (r:4 w:4) - // Storage: Staking Bonded (r:2 w:0) - // Storage: Staking Ledger (r:2 w:0) - // Storage: VoterList CounterForListNodes (r:1 w:1) - // Storage: VoterList ListBags (r:1 w:1) + /// Storage: VoterList ListNodes (r:4 w:4) + /// Proof: VoterList ListNodes (max_values: None, max_size: Some(154), added: 2629, mode: MaxEncodedLen) + /// Storage: Staking Bonded (r:2 w:0) + /// Proof: Staking Bonded (max_values: None, max_size: Some(72), added: 2547, mode: MaxEncodedLen) + /// Storage: Staking Ledger (r:2 w:0) + /// Proof: Staking Ledger (max_values: None, max_size: Some(1091), added: 3566, mode: MaxEncodedLen) + /// Storage: VoterList CounterForListNodes (r:1 w:1) + /// Proof: VoterList CounterForListNodes (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) + /// Storage: VoterList ListBags (r:1 w:1) + /// Proof: VoterList ListBags (max_values: None, max_size: Some(82), added: 2557, mode: MaxEncodedLen) fn put_in_front_of() -> Weight { - // Minimum execution time: 71_691 nanoseconds. - Weight::from_ref_time(72_798_000 as u64) - .saturating_add(T::DbWeight::get().reads(10 as u64)) - .saturating_add(T::DbWeight::get().writes(6 as u64)) + // Proof Size summary in bytes: + // Measured: `2154` + // Estimated: `25798` + // Minimum execution time: 59_513 nanoseconds. + Weight::from_parts(60_717_000, 25798) + .saturating_add(T::DbWeight::get().reads(10_u64)) + .saturating_add(T::DbWeight::get().writes(6_u64)) } } // For backwards compatibility and tests impl WeightInfo for () { - // Storage: Staking Bonded (r:1 w:0) - // Storage: Staking Ledger (r:1 w:0) - // Storage: VoterList ListNodes (r:4 w:4) - // Storage: VoterList ListBags (r:1 w:1) + /// Storage: Staking Bonded (r:1 w:0) + /// Proof: Staking Bonded (max_values: None, max_size: Some(72), added: 2547, mode: MaxEncodedLen) + /// Storage: Staking Ledger (r:1 w:0) + /// Proof: Staking Ledger (max_values: None, max_size: Some(1091), added: 3566, mode: MaxEncodedLen) + /// Storage: VoterList ListNodes (r:4 w:4) + /// Proof: VoterList ListNodes (max_values: None, max_size: Some(154), added: 2629, mode: MaxEncodedLen) + /// Storage: VoterList ListBags (r:1 w:1) + /// Proof: VoterList ListBags (max_values: None, max_size: Some(82), added: 2557, mode: MaxEncodedLen) fn rebag_non_terminal() -> Weight { - // Minimum execution time: 73_553 nanoseconds. - Weight::from_ref_time(74_366_000 as u64) - .saturating_add(RocksDbWeight::get().reads(7 as u64)) - .saturating_add(RocksDbWeight::get().writes(5 as u64)) + // Proof Size summary in bytes: + // Measured: `1916` + // Estimated: `19186` + // Minimum execution time: 54_348 nanoseconds. + Weight::from_parts(55_024_000, 19186) + .saturating_add(RocksDbWeight::get().reads(7_u64)) + .saturating_add(RocksDbWeight::get().writes(5_u64)) } - // Storage: Staking Bonded (r:1 w:0) - // Storage: Staking Ledger (r:1 w:0) - // Storage: VoterList ListNodes (r:3 w:3) - // Storage: VoterList ListBags (r:2 w:2) + /// Storage: Staking Bonded (r:1 w:0) + /// Proof: Staking Bonded (max_values: None, max_size: Some(72), added: 2547, mode: MaxEncodedLen) + /// Storage: Staking Ledger (r:1 w:0) + /// Proof: Staking Ledger (max_values: None, max_size: Some(1091), added: 3566, mode: MaxEncodedLen) + /// Storage: VoterList ListNodes (r:3 w:3) + /// Proof: VoterList ListNodes (max_values: None, max_size: Some(154), added: 2629, mode: MaxEncodedLen) + /// Storage: VoterList ListBags (r:2 w:2) + /// Proof: VoterList ListBags (max_values: None, max_size: Some(82), added: 2557, mode: MaxEncodedLen) fn rebag_terminal() -> Weight { - // Minimum execution time: 72_700 nanoseconds. - Weight::from_ref_time(73_322_000 as u64) - .saturating_add(RocksDbWeight::get().reads(7 as u64)) - .saturating_add(RocksDbWeight::get().writes(5 as u64)) + // Proof Size summary in bytes: + // Measured: `1810` + // Estimated: `19114` + // Minimum execution time: 53_306 nanoseconds. + Weight::from_parts(54_263_000, 19114) + .saturating_add(RocksDbWeight::get().reads(7_u64)) + .saturating_add(RocksDbWeight::get().writes(5_u64)) } - // Storage: VoterList ListNodes (r:4 w:4) - // Storage: Staking Bonded (r:2 w:0) - // Storage: Staking Ledger (r:2 w:0) - // Storage: VoterList CounterForListNodes (r:1 w:1) - // Storage: VoterList ListBags (r:1 w:1) + /// Storage: VoterList ListNodes (r:4 w:4) + /// Proof: VoterList ListNodes (max_values: None, max_size: Some(154), added: 2629, mode: MaxEncodedLen) + /// Storage: Staking Bonded (r:2 w:0) + /// Proof: Staking Bonded (max_values: None, max_size: Some(72), added: 2547, mode: MaxEncodedLen) + /// Storage: Staking Ledger (r:2 w:0) + /// Proof: Staking Ledger (max_values: None, max_size: Some(1091), added: 3566, mode: MaxEncodedLen) + /// Storage: VoterList CounterForListNodes (r:1 w:1) + /// Proof: VoterList CounterForListNodes (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) + /// Storage: VoterList ListBags (r:1 w:1) + /// Proof: VoterList ListBags (max_values: None, max_size: Some(82), added: 2557, mode: MaxEncodedLen) fn put_in_front_of() -> Weight { - // Minimum execution time: 71_691 nanoseconds. - Weight::from_ref_time(72_798_000 as u64) - .saturating_add(RocksDbWeight::get().reads(10 as u64)) - .saturating_add(RocksDbWeight::get().writes(6 as u64)) + // Proof Size summary in bytes: + // Measured: `2154` + // Estimated: `25798` + // Minimum execution time: 59_513 nanoseconds. + Weight::from_parts(60_717_000, 25798) + .saturating_add(RocksDbWeight::get().reads(10_u64)) + .saturating_add(RocksDbWeight::get().writes(6_u64)) } } diff --git a/frame/balances/src/weights.rs b/frame/balances/src/weights.rs index 6324745fd..3266fcbcd 100644 --- a/frame/balances/src/weights.rs +++ b/frame/balances/src/weights.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2022 Parity Technologies (UK) Ltd. +// Copyright (C) 2023 Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); @@ -18,7 +18,8 @@ //! Autogenerated weights for pallet_balances //! //! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev -//! DATE: 2022-11-07, STEPS: `50`, REPEAT: 20, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! DATE: 2023-01-24, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! WORST CASE MAP SIZE: `1000000` //! HOSTNAME: `bm2`, CPU: `Intel(R) Core(TM) i7-7700K CPU @ 4.20GHz` //! EXECUTION: Some(Wasm), WASM-EXECUTION: Compiled, CHAIN: Some("dev"), DB CACHE: 1024 @@ -59,106 +60,162 @@ pub trait WeightInfo { /// Weights for pallet_balances using the Substrate node and recommended hardware. pub struct SubstrateWeight(PhantomData); impl WeightInfo for SubstrateWeight { - // Storage: System Account (r:1 w:1) + /// Storage: System Account (r:1 w:1) + /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) fn transfer() -> Weight { - // Minimum execution time: 48_134 nanoseconds. - Weight::from_ref_time(48_811_000 as u64) - .saturating_add(T::DbWeight::get().reads(1 as u64)) - .saturating_add(T::DbWeight::get().writes(1 as u64)) + // Proof Size summary in bytes: + // Measured: `1723` + // Estimated: `2603` + // Minimum execution time: 47_557 nanoseconds. + Weight::from_parts(48_314_000, 2603) + .saturating_add(T::DbWeight::get().reads(1_u64)) + .saturating_add(T::DbWeight::get().writes(1_u64)) } - // Storage: System Account (r:1 w:1) + /// Storage: System Account (r:1 w:1) + /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) fn transfer_keep_alive() -> Weight { - // Minimum execution time: 36_586 nanoseconds. - Weight::from_ref_time(36_966_000 as u64) - .saturating_add(T::DbWeight::get().reads(1 as u64)) - .saturating_add(T::DbWeight::get().writes(1 as u64)) + // Proof Size summary in bytes: + // Measured: `1607` + // Estimated: `2603` + // Minimum execution time: 36_372 nanoseconds. + Weight::from_parts(37_432_000, 2603) + .saturating_add(T::DbWeight::get().reads(1_u64)) + .saturating_add(T::DbWeight::get().writes(1_u64)) } - // Storage: System Account (r:1 w:1) + /// Storage: System Account (r:1 w:1) + /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) fn set_balance_creating() -> Weight { - // Minimum execution time: 28_486 nanoseconds. - Weight::from_ref_time(28_940_000 as u64) - .saturating_add(T::DbWeight::get().reads(1 as u64)) - .saturating_add(T::DbWeight::get().writes(1 as u64)) + // Proof Size summary in bytes: + // Measured: `1757` + // Estimated: `2603` + // Minimum execution time: 26_671 nanoseconds. + Weight::from_parts(28_287_000, 2603) + .saturating_add(T::DbWeight::get().reads(1_u64)) + .saturating_add(T::DbWeight::get().writes(1_u64)) } - // Storage: System Account (r:1 w:1) + /// Storage: System Account (r:1 w:1) + /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) fn set_balance_killing() -> Weight { - // Minimum execution time: 31_225 nanoseconds. - Weight::from_ref_time(31_946_000 as u64) - .saturating_add(T::DbWeight::get().reads(1 as u64)) - .saturating_add(T::DbWeight::get().writes(1 as u64)) + // Proof Size summary in bytes: + // Measured: `1757` + // Estimated: `2603` + // Minimum execution time: 30_122 nanoseconds. + Weight::from_parts(30_615_000, 2603) + .saturating_add(T::DbWeight::get().reads(1_u64)) + .saturating_add(T::DbWeight::get().writes(1_u64)) } - // Storage: System Account (r:2 w:2) + /// Storage: System Account (r:2 w:2) + /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) fn force_transfer() -> Weight { - // Minimum execution time: 47_347 nanoseconds. - Weight::from_ref_time(48_005_000 as u64) - .saturating_add(T::DbWeight::get().reads(2 as u64)) - .saturating_add(T::DbWeight::get().writes(2 as u64)) + // Proof Size summary in bytes: + // Measured: `1719` + // Estimated: `5206` + // Minimum execution time: 47_891 nanoseconds. + Weight::from_parts(48_496_000, 5206) + .saturating_add(T::DbWeight::get().reads(2_u64)) + .saturating_add(T::DbWeight::get().writes(2_u64)) } - // Storage: System Account (r:1 w:1) + /// Storage: System Account (r:1 w:1) + /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) fn transfer_all() -> Weight { - // Minimum execution time: 41_668 nanoseconds. - Weight::from_ref_time(42_232_000 as u64) - .saturating_add(T::DbWeight::get().reads(1 as u64)) - .saturating_add(T::DbWeight::get().writes(1 as u64)) + // Proof Size summary in bytes: + // Measured: `1607` + // Estimated: `2603` + // Minimum execution time: 42_470 nanoseconds. + Weight::from_parts(42_950_000, 2603) + .saturating_add(T::DbWeight::get().reads(1_u64)) + .saturating_add(T::DbWeight::get().writes(1_u64)) } - // Storage: System Account (r:1 w:1) + /// Storage: System Account (r:1 w:1) + /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) fn force_unreserve() -> Weight { - // Minimum execution time: 23_741 nanoseconds. - Weight::from_ref_time(24_073_000 as u64) - .saturating_add(T::DbWeight::get().reads(1 as u64)) - .saturating_add(T::DbWeight::get().writes(1 as u64)) + // Proof Size summary in bytes: + // Measured: `1641` + // Estimated: `2603` + // Minimum execution time: 23_230 nanoseconds. + Weight::from_parts(23_951_000, 2603) + .saturating_add(T::DbWeight::get().reads(1_u64)) + .saturating_add(T::DbWeight::get().writes(1_u64)) } } // For backwards compatibility and tests impl WeightInfo for () { - // Storage: System Account (r:1 w:1) + /// Storage: System Account (r:1 w:1) + /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) fn transfer() -> Weight { - // Minimum execution time: 48_134 nanoseconds. - Weight::from_ref_time(48_811_000 as u64) - .saturating_add(RocksDbWeight::get().reads(1 as u64)) - .saturating_add(RocksDbWeight::get().writes(1 as u64)) + // Proof Size summary in bytes: + // Measured: `1723` + // Estimated: `2603` + // Minimum execution time: 47_557 nanoseconds. + Weight::from_parts(48_314_000, 2603) + .saturating_add(RocksDbWeight::get().reads(1_u64)) + .saturating_add(RocksDbWeight::get().writes(1_u64)) } - // Storage: System Account (r:1 w:1) + /// Storage: System Account (r:1 w:1) + /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) fn transfer_keep_alive() -> Weight { - // Minimum execution time: 36_586 nanoseconds. - Weight::from_ref_time(36_966_000 as u64) - .saturating_add(RocksDbWeight::get().reads(1 as u64)) - .saturating_add(RocksDbWeight::get().writes(1 as u64)) + // Proof Size summary in bytes: + // Measured: `1607` + // Estimated: `2603` + // Minimum execution time: 36_372 nanoseconds. + Weight::from_parts(37_432_000, 2603) + .saturating_add(RocksDbWeight::get().reads(1_u64)) + .saturating_add(RocksDbWeight::get().writes(1_u64)) } - // Storage: System Account (r:1 w:1) + /// Storage: System Account (r:1 w:1) + /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) fn set_balance_creating() -> Weight { - // Minimum execution time: 28_486 nanoseconds. - Weight::from_ref_time(28_940_000 as u64) - .saturating_add(RocksDbWeight::get().reads(1 as u64)) - .saturating_add(RocksDbWeight::get().writes(1 as u64)) + // Proof Size summary in bytes: + // Measured: `1757` + // Estimated: `2603` + // Minimum execution time: 26_671 nanoseconds. + Weight::from_parts(28_287_000, 2603) + .saturating_add(RocksDbWeight::get().reads(1_u64)) + .saturating_add(RocksDbWeight::get().writes(1_u64)) } - // Storage: System Account (r:1 w:1) + /// Storage: System Account (r:1 w:1) + /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) fn set_balance_killing() -> Weight { - // Minimum execution time: 31_225 nanoseconds. - Weight::from_ref_time(31_946_000 as u64) - .saturating_add(RocksDbWeight::get().reads(1 as u64)) - .saturating_add(RocksDbWeight::get().writes(1 as u64)) + // Proof Size summary in bytes: + // Measured: `1757` + // Estimated: `2603` + // Minimum execution time: 30_122 nanoseconds. + Weight::from_parts(30_615_000, 2603) + .saturating_add(RocksDbWeight::get().reads(1_u64)) + .saturating_add(RocksDbWeight::get().writes(1_u64)) } - // Storage: System Account (r:2 w:2) + /// Storage: System Account (r:2 w:2) + /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) fn force_transfer() -> Weight { - // Minimum execution time: 47_347 nanoseconds. - Weight::from_ref_time(48_005_000 as u64) - .saturating_add(RocksDbWeight::get().reads(2 as u64)) - .saturating_add(RocksDbWeight::get().writes(2 as u64)) + // Proof Size summary in bytes: + // Measured: `1719` + // Estimated: `5206` + // Minimum execution time: 47_891 nanoseconds. + Weight::from_parts(48_496_000, 5206) + .saturating_add(RocksDbWeight::get().reads(2_u64)) + .saturating_add(RocksDbWeight::get().writes(2_u64)) } - // Storage: System Account (r:1 w:1) + /// Storage: System Account (r:1 w:1) + /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) fn transfer_all() -> Weight { - // Minimum execution time: 41_668 nanoseconds. - Weight::from_ref_time(42_232_000 as u64) - .saturating_add(RocksDbWeight::get().reads(1 as u64)) - .saturating_add(RocksDbWeight::get().writes(1 as u64)) + // Proof Size summary in bytes: + // Measured: `1607` + // Estimated: `2603` + // Minimum execution time: 42_470 nanoseconds. + Weight::from_parts(42_950_000, 2603) + .saturating_add(RocksDbWeight::get().reads(1_u64)) + .saturating_add(RocksDbWeight::get().writes(1_u64)) } - // Storage: System Account (r:1 w:1) + /// Storage: System Account (r:1 w:1) + /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) fn force_unreserve() -> Weight { - // Minimum execution time: 23_741 nanoseconds. - Weight::from_ref_time(24_073_000 as u64) - .saturating_add(RocksDbWeight::get().reads(1 as u64)) - .saturating_add(RocksDbWeight::get().writes(1 as u64)) + // Proof Size summary in bytes: + // Measured: `1641` + // Estimated: `2603` + // Minimum execution time: 23_230 nanoseconds. + Weight::from_parts(23_951_000, 2603) + .saturating_add(RocksDbWeight::get().reads(1_u64)) + .saturating_add(RocksDbWeight::get().writes(1_u64)) } } diff --git a/frame/benchmarking/Cargo.toml b/frame/benchmarking/Cargo.toml index 168b26057..b2a9621eb 100644 --- a/frame/benchmarking/Cargo.toml +++ b/frame/benchmarking/Cargo.toml @@ -56,4 +56,5 @@ std = [ ] runtime-benchmarks = [ "frame-system/runtime-benchmarks", + "frame-support/runtime-benchmarks", ] diff --git a/frame/benchmarking/pov/Cargo.toml b/frame/benchmarking/pov/Cargo.toml new file mode 100644 index 000000000..d885821bc --- /dev/null +++ b/frame/benchmarking/pov/Cargo.toml @@ -0,0 +1,43 @@ +[package] +name = "frame-benchmarking-pallet-pov" +version = "4.0.0-dev" +authors = ["Parity Technologies "] +edition = "2021" +license = "Apache-2.0" +homepage = "https://substrate.io" +repository = "https://github.com/paritytech/substrate/" +description = "Pallet for testing FRAME PoV benchmarking" + +[package.metadata.docs.rs] +targets = ["x86_64-unknown-linux-gnu"] + +[dependencies] +codec = { package = "parity-scale-codec", version = "3.0.0", default-features = false } +scale-info = { version = "2.1.1", default-features = false, features = ["derive"] } +frame-benchmarking = { version = "4.0.0-dev", default-features = false, path = "../" } +frame-support = { version = "4.0.0-dev", default-features = false, path = "../../support" } +frame-system = { version = "4.0.0-dev", default-features = false, path = "../../system" } +sp-io = { version = "7.0.0", default-features = false, path = "../../../primitives/io" } +sp-runtime = { version = "7.0.0", default-features = false, path = "../../../primitives/runtime" } +sp-std = { version = "5.0.0", default-features = false, path = "../../../primitives/std" } + +[features] +default = ["std"] +std = [ + "codec/std", + "frame-benchmarking/std", + "frame-support/std", + "frame-system/std", + "scale-info/std", + "sp-io/std", + "sp-runtime/std", + "sp-std/std", +] +runtime-benchmarks = [ + "frame-system/runtime-benchmarks", + "frame-benchmarking/runtime-benchmarks", +] +try-runtime = [ + "frame-support/try-runtime", + "frame-system/try-runtime", +] diff --git a/frame/benchmarking/pov/src/benchmarking.rs b/frame/benchmarking/pov/src/benchmarking.rs new file mode 100644 index 000000000..608077e2d --- /dev/null +++ b/frame/benchmarking/pov/src/benchmarking.rs @@ -0,0 +1,371 @@ +// This file is part of Substrate. + +// Copyright (C) 2022 Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +//! All benchmarks in this file are just for debugging the PoV calculation logic, they are unused. + +#![cfg(feature = "runtime-benchmarks")] + +use super::*; + +use frame_support::traits::UnfilteredDispatchable; +use frame_system::{Pallet as System, RawOrigin}; +use sp_runtime::traits::Hash; + +frame_benchmarking::benchmarks! { + storage_single_value_read { + Value::::put(123); + }: { + assert_eq!(Value::::get(), Some(123)); + } + + #[pov_mode = Ignored] + storage_single_value_ignored_read { + Value::::put(123); + }: { + assert_eq!(Value::::get(), Some(123)); + } + + storage_single_value_read_twice { + Value::::put(123); + }: { + assert_eq!(Value::::get(), Some(123)); + assert_eq!(Value::::get(), Some(123)); + } + + storage_single_value_write { + }: { + Value::::put(123); + } verify { + assert_eq!(Value::::get(), Some(123)); + } + + storage_single_value_kill { + Value::::put(123); + }: { + Value::::kill(); + } verify { + assert!(!Value::::exists()); + } + + // This benchmark and the following are testing a storage map with adjacent storage items. + // + // First a storage map is filled and a specific number of other storage items is + // created. Then the one value is read from the map. This demonstrates that the number of other + // nodes in the Trie influences the proof size. The number of inserted nodes can be interpreted + // as the number of `StorageMap`/`StorageValue` in the whole runtime. + #[pov_mode = Measured] + storage_1m_map_read_one_value_two_additional_layers { + (0..(1<<10)).for_each(|i| Map1M::::insert(i, i)); + // Assume there are 16-256 other storage items. + (0..(1u32<<4)).for_each(|i| { + let k = T::Hashing::hash(&i.to_be_bytes()); + frame_support::storage::unhashed::put(k.as_ref(), &i); + }); + }: { + assert_eq!(Map1M::::get(1<<9), Some(1<<9)); + } + + #[pov_mode = Measured] + storage_1m_map_read_one_value_three_additional_layers { + (0..(1<<10)).for_each(|i| Map1M::::insert(i, i)); + // Assume there are 256-4096 other storage items. + (0..(1u32<<8)).for_each(|i| { + let k = T::Hashing::hash(&i.to_be_bytes()); + frame_support::storage::unhashed::put(k.as_ref(), &i); + }); + }: { + assert_eq!(Map1M::::get(1<<9), Some(1<<9)); + } + + #[pov_mode = Measured] + storage_1m_map_read_one_value_four_additional_layers { + (0..(1<<10)).for_each(|i| Map1M::::insert(i, i)); + // Assume there are 4096-65536 other storage items. + (0..(1u32<<12)).for_each(|i| { + let k = T::Hashing::hash(&i.to_be_bytes()); + frame_support::storage::unhashed::put(k.as_ref(), &i); + }); + }: { + assert_eq!(Map1M::::get(1<<9), Some(1<<9)); + } + + // Reads from both storage maps each `n` and `m` times. Should result in two linear components. + storage_map_read_per_component { + let n in 0 .. 100; + let m in 0 .. 100; + + (0..m*10).for_each(|i| Map1M::::insert(i, i)); + (0..n*10).for_each(|i| Map16M::::insert(i, i)); + }: { + (0..m).for_each(|i| + assert_eq!(Map1M::::get(i*10), Some(i*10))); + (0..n).for_each(|i| + assert_eq!(Map16M::::get(i*10), Some(i*10))); + } + + #[pov_mode = MaxEncodedLen { + Pov::Map1M: Ignored + }] + storage_map_read_per_component_one_ignored { + let n in 0 .. 100; + let m in 0 .. 100; + + (0..m*10).for_each(|i| Map1M::::insert(i, i)); + (0..n*10).for_each(|i| Map16M::::insert(i, i)); + }: { + (0..m).for_each(|i| + assert_eq!(Map1M::::get(i*10), Some(i*10))); + (0..n).for_each(|i| + assert_eq!(Map16M::::get(i*10), Some(i*10))); + } + + // Reads the same value from a storage map. Should not result in a component. + storage_1m_map_one_entry_repeated_read { + let n in 0 .. 100; + Map1M::::insert(0, 0); + }: { + (0..n).for_each(|i| + assert_eq!(Map1M::::get(0), Some(0))); + } + + // Reads the same values from a storage map. Should result in a `1x` linear component. + storage_1m_map_multiple_entry_repeated_read { + let n in 0 .. 100; + (0..n).for_each(|i| Map1M::::insert(i, i)); + }: { + (0..n).for_each(|i| { + // Reading the same value 10 times does nothing. + (0..10).for_each(|j| + assert_eq!(Map1M::::get(i), Some(i))); + }); + } + + storage_1m_double_map_read_per_component { + let n in 0 .. 1024; + (0..(1<<10)).for_each(|i| DoubleMap1M::::insert(i, i, i)); + }: { + (0..n).for_each(|i| + assert_eq!(DoubleMap1M::::get(i, i), Some(i))); + } + + storage_value_bounded_read { + }: { + assert!(BoundedValue::::get().is_none()); + } + + // Reading unbounded values will produce no mathematical worst case PoV size for this component. + storage_value_unbounded_read { + }: { + assert!(UnboundedValue::::get().is_none()); + } + + #[pov_mode = Ignored] + storage_value_unbounded_ignored_read { + }: { + assert!(UnboundedValue::::get().is_none()); + } + + // Same as above, but we still expect a mathematical worst case PoV size for the bounded one. + storage_value_bounded_and_unbounded_read { + }: { + assert!(UnboundedValue::::get().is_none()); + assert!(BoundedValue::::get().is_none()); + } + + #[pov_mode = Measured] + measured_storage_value_read_linear_size { + let l in 0 .. 1<<22; + let v: sp_runtime::BoundedVec = sp_std::vec![0u8; l as usize].try_into().unwrap(); + LargeValue::::put(&v); + }: { + assert!(LargeValue::::get().is_some()); + } + + #[pov_mode = MaxEncodedLen] + mel_storage_value_read_linear_size { + let l in 0 .. 1<<22; + let v: sp_runtime::BoundedVec = sp_std::vec![0u8; l as usize].try_into().unwrap(); + LargeValue::::put(&v); + }: { + assert!(LargeValue::::get().is_some()); + } + + #[pov_mode = Measured] + measured_storage_double_value_read_linear_size { + let l in 0 .. 1<<22; + let v: sp_runtime::BoundedVec = sp_std::vec![0u8; l as usize].try_into().unwrap(); + LargeValue::::put(&v); + LargeValue2::::put(&v); + }: { + assert!(LargeValue::::get().is_some()); + assert!(LargeValue2::::get().is_some()); + } + + #[pov_mode = MaxEncodedLen] + mel_storage_double_value_read_linear_size { + let l in 0 .. 1<<22; + let v: sp_runtime::BoundedVec = sp_std::vec![0u8; l as usize].try_into().unwrap(); + LargeValue::::put(&v); + LargeValue2::::put(&v); + }: { + assert!(LargeValue::::get().is_some()); + assert!(LargeValue2::::get().is_some()); + } + + #[pov_mode = MaxEncodedLen { + Pov::LargeValue2: Measured + }] + mel_mixed_storage_double_value_read_linear_size { + let l in 0 .. 1<<22; + let v: sp_runtime::BoundedVec = sp_std::vec![0u8; l as usize].try_into().unwrap(); + LargeValue::::put(&v); + LargeValue2::::put(&v); + }: { + assert!(LargeValue::::get().is_some()); + assert!(LargeValue2::::get().is_some()); + } + + #[pov_mode = Measured { + Pov::LargeValue2: MaxEncodedLen + }] + measured_mixed_storage_double_value_read_linear_size { + let l in 0 .. 1<<22; + let v: sp_runtime::BoundedVec = sp_std::vec![0u8; l as usize].try_into().unwrap(); + LargeValue::::put(&v); + LargeValue2::::put(&v); + }: { + assert!(LargeValue::::get().is_some()); + assert!(LargeValue2::::get().is_some()); + } + + #[pov_mode = Measured] + storage_map_unbounded_both_measured_read { + let i in 0 .. 1000; + + UnboundedMap::::insert(i, sp_std::vec![0; i as usize]); + UnboundedMap2::::insert(i, sp_std::vec![0; i as usize]); + }: { + assert!(UnboundedMap::::get(i).is_some()); + assert!(UnboundedMap2::::get(i).is_some()); + } + + #[pov_mode = MaxEncodedLen { + Pov::UnboundedMap: Measured + }] + storage_map_partial_unbounded_read { + let i in 0 .. 1000; + + Map1M::::insert(i, 0); + UnboundedMap::::insert(i, sp_std::vec![0; i as usize]); + }: { + assert!(Map1M::::get(i).is_some()); + assert!(UnboundedMap::::get(i).is_some()); + } + + #[pov_mode = MaxEncodedLen { + Pov::UnboundedMap: Ignored + }] + storage_map_partial_unbounded_ignored_read { + let i in 0 .. 1000; + + Map1M::::insert(i, 0); + UnboundedMap::::insert(i, sp_std::vec![0; i as usize]); + }: { + assert!(Map1M::::get(i).is_some()); + assert!(UnboundedMap::::get(i).is_some()); + } + + // Emitting an event will not incur any PoV. + emit_event { + // Emit a single event. + let call = Call::::emit_event { }; + }: { call.dispatch_bypass_filter(RawOrigin::Root.into()).unwrap(); } + verify { + assert_eq!(System::::events().len(), 1); + } + + // A No-OP will not incur any PoV. + noop { + let call = Call::::noop { }; + }: { + call.dispatch_bypass_filter(RawOrigin::Root.into()).unwrap(); + } + + impl_benchmark_test_suite!( + Pallet, + mock::new_test_ext(), + mock::Test, + ); +} + +#[cfg(test)] +mod mock { + use sp_runtime::testing::H256; + + type AccountId = u64; + type AccountIndex = u32; + type BlockNumber = u64; + + type UncheckedExtrinsic = frame_system::mocking::MockUncheckedExtrinsic; + type Block = frame_system::mocking::MockBlock; + + frame_support::construct_runtime!( + pub enum Test where + Block = Block, + NodeBlock = Block, + UncheckedExtrinsic = UncheckedExtrinsic, + { + System: frame_system::{Pallet, Call, Config, Storage, Event}, + Baseline: crate::{Pallet, Call, Storage, Event}, + } + ); + + impl frame_system::Config for Test { + type BaseCallFilter = frame_support::traits::Everything; + type BlockWeights = (); + type BlockLength = (); + type DbWeight = (); + type RuntimeOrigin = RuntimeOrigin; + type Index = AccountIndex; + type BlockNumber = BlockNumber; + type RuntimeCall = RuntimeCall; + type Hash = H256; + type Hashing = ::sp_runtime::traits::BlakeTwo256; + type AccountId = AccountId; + type Lookup = sp_runtime::traits::IdentityLookup; + type Header = sp_runtime::testing::Header; + type RuntimeEvent = RuntimeEvent; + type BlockHashCount = (); + type Version = (); + type PalletInfo = PalletInfo; + type AccountData = (); + type OnNewAccount = (); + type OnKilledAccount = (); + type SystemWeightInfo = (); + type SS58Prefix = (); + type OnSetCode = (); + type MaxConsumers = frame_support::traits::ConstU32<16>; + } + + impl crate::Config for Test { + type RuntimeEvent = RuntimeEvent; + } + + pub fn new_test_ext() -> sp_io::TestExternalities { + frame_system::GenesisConfig::default().build_storage::().unwrap().into() + } +} diff --git a/frame/benchmarking/pov/src/lib.rs b/frame/benchmarking/pov/src/lib.rs new file mode 100644 index 000000000..6618ce60d --- /dev/null +++ b/frame/benchmarking/pov/src/lib.rs @@ -0,0 +1,131 @@ +// This file is part of Substrate. + +// Copyright (C) 2020-2022 Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +//! End-to-end testing pallet for PoV benchmarking. Should only be deployed in a testing runtime. + +#![cfg_attr(not(feature = "std"), no_std)] + +mod benchmarking; +mod tests; +mod weights; + +pub use pallet::*; + +#[frame_support::pallet] +pub mod pallet { + use frame_support::pallet_prelude::*; + use frame_system::pallet_prelude::*; + use sp_std::prelude::*; + + #[pallet::pallet] + #[pallet::generate_store(pub(super) trait Store)] + pub struct Pallet(PhantomData); + + #[pallet::config] + pub trait Config: frame_system::Config { + type RuntimeEvent: From> + IsType<::RuntimeEvent>; + } + + #[pallet::storage] + pub(crate) type Value = StorageValue; + + #[pallet::storage] + pub(crate) type Value2 = StorageValue; + + /// A value without a MEL bound. + #[pallet::storage] + #[pallet::unbounded] + pub(crate) type UnboundedValue = + StorageValue, QueryKind = OptionQuery>; + + /// A value with a MEL bound of 32 byte. + #[pallet::storage] + pub(crate) type BoundedValue = + StorageValue>, QueryKind = OptionQuery>; + + /// 4MiB value. + #[pallet::storage] + pub(crate) type LargeValue = + StorageValue>, QueryKind = OptionQuery>; + + #[pallet::storage] + pub(crate) type LargeValue2 = + StorageValue>, QueryKind = OptionQuery>; + + /// A map with a maximum of 1M entries. + #[pallet::storage] + pub(crate) type Map1M = StorageMap< + Hasher = Blake2_256, + Key = u32, + Value = u32, + QueryKind = OptionQuery, + MaxValues = ConstU32<1_000_000>, + >; + + /// A map with a maximum of 16M entries. + #[pallet::storage] + pub(crate) type Map16M = StorageMap< + Hasher = Blake2_256, + Key = u32, + Value = u32, + QueryKind = OptionQuery, + MaxValues = ConstU32<16_000_000>, + >; + + #[pallet::storage] + pub(crate) type DoubleMap1M = StorageDoubleMap< + Hasher1 = Blake2_256, + Hasher2 = Blake2_256, + Key1 = u32, + Key2 = u32, + Value = u32, + QueryKind = OptionQuery, + MaxValues = ConstU32<1_000_000>, + >; + + #[pallet::storage] + #[pallet::unbounded] + pub(crate) type UnboundedMap = + StorageMap, QueryKind = OptionQuery>; + + #[pallet::storage] + #[pallet::unbounded] + pub(crate) type UnboundedMap2 = + StorageMap, QueryKind = OptionQuery>; + + #[pallet::event] + #[pallet::generate_deposit(pub(super) fn deposit_event)] + pub enum Event { + TestEvent, + } + + #[pallet::call] + impl Pallet { + #[pallet::call_index(0)] + #[pallet::weight(0)] + pub fn emit_event(_origin: OriginFor) -> DispatchResult { + Self::deposit_event(Event::TestEvent); + Ok(()) + } + + #[pallet::call_index(1)] + #[pallet::weight(0)] + pub fn noop(_origin: OriginFor) -> DispatchResult { + Ok(()) + } + } +} diff --git a/frame/benchmarking/pov/src/tests.rs b/frame/benchmarking/pov/src/tests.rs new file mode 100644 index 000000000..1100fdc46 --- /dev/null +++ b/frame/benchmarking/pov/src/tests.rs @@ -0,0 +1,211 @@ +// This file is part of Substrate. + +// Copyright (C) 2022 Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +//! Test the produces weight functions. + +#![cfg(test)] + +use super::weights::WeightInfo; +use mock::Test as T; +type W = crate::weights::SubstrateWeight; + +#[test] +fn writing_is_free() { + let w = W::storage_single_value_write().proof_size(); + assert_eq!(w, 0, "Writing is free"); +} + +#[test] +fn killing_is_free() { + // NOTE: This only applies to state version 1. + let w = W::storage_single_value_kill().proof_size(); + assert_eq!(w, 0, "Killing is free"); +} + +#[test] +fn reading_twice_is_the_same_as_once() { + let w = W::storage_single_value_read().proof_size(); + let w2 = W::storage_single_value_read_twice().proof_size(); + assert_eq!(w, w2, "Reading twice is the same as once"); +} + +#[test] +fn storage_single_value_ignored_read_no_pov() { + let w = W::storage_single_value_ignored_read(); + assert_eq!(w.proof_size(), 0, "Ignored PoV does not result in PoV"); +} + +#[test] +fn storage_single_value_ignored_some_read_has_pov() { + let w = W::storage_single_value_ignored_some_read(); + assert!(w.proof_size() != 0, "Ignored some does result in PoV"); +} + +/// Reading the same value from a map does not increase the PoV. +#[test] +fn storage_1m_map_one_entry_repeated_read_const() { + let weight = W::storage_1m_map_one_entry_repeated_read; + let w0 = weight(0).proof_size(); + assert!(w0 > 0, "There is a base weight"); + + let w1 = weight(1).proof_size(); + assert_eq!(w0, w1, "Component does not matter"); +} + +/// Reading multiple values multiple times from a map increases the PoV by the number of reads. +#[test] +fn storage_1m_map_multiple_entry_repeated_read_single_linear() { + let weight = W::storage_1m_map_multiple_entry_repeated_read; + let w0 = weight(0).proof_size(); + + let w1 = weight(1).proof_size() - w0; + assert!(w1 > 0, "Component matters"); + + let wm = weight(1000).proof_size(); + assert_eq!(w1 * 1000 + w0, wm, "x scales linearly"); +} + +/// Check that reading two maps at once increases the PoV linearly per map. +#[test] +fn storage_map_read_per_component_double_linear() { + let weight = W::storage_map_read_per_component; + let w00 = weight(0, 0).proof_size(); + + let w10 = weight(1, 0).proof_size() - w00; + let w01 = weight(0, 1).proof_size() - w00; + assert!(w10 > 0 && w01 > 0, "Components matter"); + assert!(w10 != w01, "Each map has its own component"); + + let wm0 = weight(1000, 0).proof_size(); + let w0m = weight(0, 1000).proof_size(); + assert_eq!(w00 + w10 * 1000, wm0, "x scales linearly"); + assert_eq!(w00 + w01 * 1000, w0m, "y scales linearly"); + + let wmm = weight(1000, 1000).proof_size(); + assert_eq!(wmm + w00, wm0 + w0m, "x + y scales linearly"); +} + +/// The proof size estimation takes the measured sizes into account and therefore increases with the +/// number of layers. +#[test] +fn additional_layers_do_not_matter() { + let w2 = W::storage_1m_map_read_one_value_two_additional_layers().proof_size(); + let w3 = W::storage_1m_map_read_one_value_three_additional_layers().proof_size(); + let w4 = W::storage_1m_map_read_one_value_four_additional_layers().proof_size(); + assert!(w3 > w2 && w4 > w3, "Additional layers do matter"); +} + +/// Check that the measured value size instead of the MEL is used. +#[test] +fn linear_measured_size_works() { + let weight = W::measured_storage_value_read_linear_size; + + let w0 = weight(0).proof_size(); + let w1 = weight(1).proof_size() - w0; + + assert_eq!(w1, 1, "x scales with a factor of 1"); + let wm = weight(1000).proof_size(); + assert_eq!(w1 * 1000 + w0, wm, "x scales linearly"); +} + +// vice-versa of above `linear_measured_size_works`. +#[test] +fn linear_mel_size_works() { + let weight = W::mel_storage_value_read_linear_size; + + let w1 = weight(1).proof_size(); + let wm = weight(1000).proof_size(); + assert_eq!(w1, wm, "PoV size is const"); +} + +/// Although there is no estimation possible, it uses the recorded proof size as best effort. +#[test] +fn unbounded_read_best_effort() { + let w = W::storage_value_unbounded_read().proof_size(); + assert!(w > 0, "There is a weight"); +} + +/// For mixed unbounded and bounded reads, the bounded part still increases the PoV. +#[test] +fn partial_unbounded_read_best_effort() { + let w_unbounded = W::storage_value_unbounded_read().proof_size(); + let w_bounded = W::storage_value_bounded_read().proof_size(); + let w_partial = W::storage_value_bounded_and_unbounded_read().proof_size(); + + assert_eq!(w_bounded + w_unbounded, w_partial, "The bounded part increases the PoV"); +} + +#[test] +fn emit_event_is_free() { + let w = W::emit_event().proof_size(); + assert_eq!(w, 0, "Emitting an event is free"); +} + +#[test] +fn noop_is_free() { + let w = W::noop().proof_size(); + assert_eq!(w, 0, "Noop is free"); +} + +mod mock { + use sp_runtime::testing::H256; + + type UncheckedExtrinsic = frame_system::mocking::MockUncheckedExtrinsic; + type Block = frame_system::mocking::MockBlock; + + frame_support::construct_runtime!( + pub enum Test where + Block = Block, + NodeBlock = Block, + UncheckedExtrinsic = UncheckedExtrinsic, + { + System: frame_system::{Pallet, Call, Config, Storage, Event}, + Baseline: crate::{Pallet, Call, Storage, Event}, + } + ); + + impl frame_system::Config for Test { + type BaseCallFilter = frame_support::traits::Everything; + type BlockWeights = (); + type BlockLength = (); + type DbWeight = (); + type RuntimeOrigin = RuntimeOrigin; + type Index = u32; + type BlockNumber = u64; + type RuntimeCall = RuntimeCall; + type Hash = H256; + type Hashing = ::sp_runtime::traits::BlakeTwo256; + type AccountId = u32; + type Lookup = sp_runtime::traits::IdentityLookup; + type Header = sp_runtime::testing::Header; + type RuntimeEvent = RuntimeEvent; + type BlockHashCount = (); + type Version = (); + type PalletInfo = PalletInfo; + type AccountData = (); + type OnNewAccount = (); + type OnKilledAccount = (); + type SystemWeightInfo = (); + type SS58Prefix = (); + type OnSetCode = (); + type MaxConsumers = frame_support::traits::ConstU32<16>; + } + + impl crate::Config for Test { + type RuntimeEvent = RuntimeEvent; + } +} diff --git a/frame/benchmarking/pov/src/weights.rs b/frame/benchmarking/pov/src/weights.rs new file mode 100644 index 000000000..e890e9b2e --- /dev/null +++ b/frame/benchmarking/pov/src/weights.rs @@ -0,0 +1,807 @@ + +//! Autogenerated weights for frame_benchmarking_pallet_pov +//! +//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev +//! DATE: 2023-01-23, STEPS: `50`, REPEAT: `1`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! WORST CASE MAP SIZE: `1000000` +//! HOSTNAME: `oty-parity`, CPU: `11th Gen Intel(R) Core(TM) i7-1165G7 @ 2.80GHz` +//! EXECUTION: None, WASM-EXECUTION: Compiled, CHAIN: None, DB CACHE: 1024 + +// Executed Command: +// ./target/release/substrate +// benchmark +// pallet +// --dev +// --pallet +// frame-benchmarking-pallet-pov +// --extrinsic +// +// --steps +// 50 +// --repeat +// 1 +// --template=.maintain/frame-weight-template.hbs +// --output=frame/benchmarking/pov/src/weights.rs + +#![cfg_attr(rustfmt, rustfmt_skip)] +#![allow(unused_parens)] +#![allow(unused_imports)] + +use frame_support::{traits::Get, weights::{Weight, constants::RocksDbWeight}}; +use sp_std::marker::PhantomData; + +/// Weight functions needed for frame_benchmarking_pallet_pov. +pub trait WeightInfo { + fn storage_single_value_read() -> Weight; + fn storage_single_value_ignored_read() -> Weight; + fn storage_single_value_ignored_some_read() -> Weight; + fn storage_single_value_read_twice() -> Weight; + fn storage_single_value_write() -> Weight; + fn storage_single_value_kill() -> Weight; + fn storage_1m_map_read_one_value_two_additional_layers() -> Weight; + fn storage_1m_map_read_one_value_three_additional_layers() -> Weight; + fn storage_1m_map_read_one_value_four_additional_layers() -> Weight; + fn storage_map_read_per_component(n: u32, m: u32, ) -> Weight; + fn storage_map_read_per_component_one_ignored(n: u32, m: u32, ) -> Weight; + fn storage_1m_map_one_entry_repeated_read(n: u32, ) -> Weight; + fn storage_1m_map_multiple_entry_repeated_read(n: u32, ) -> Weight; + fn storage_1m_double_map_read_per_component(n: u32, ) -> Weight; + fn storage_value_bounded_read() -> Weight; + fn storage_value_unbounded_read() -> Weight; + fn storage_value_unbounded_ignored_read() -> Weight; + fn storage_value_bounded_and_unbounded_read() -> Weight; + fn measured_storage_value_read_linear_size(l: u32, ) -> Weight; + fn mel_storage_value_read_linear_size(l: u32, ) -> Weight; + fn measured_storage_double_value_read_linear_size(l: u32, ) -> Weight; + fn mel_storage_double_value_read_linear_size(l: u32, ) -> Weight; + fn mel_mixed_storage_double_value_read_linear_size(l: u32, ) -> Weight; + fn measured_mixed_storage_double_value_read_linear_size(l: u32, ) -> Weight; + fn storage_map_unbounded_both_measured_read(i: u32, ) -> Weight; + fn storage_map_partial_unbounded_read(i: u32, ) -> Weight; + fn storage_map_partial_unbounded_ignored_read(i: u32, ) -> Weight; + fn emit_event() -> Weight; + fn noop() -> Weight; +} + +/// Weights for frame_benchmarking_pallet_pov using the Substrate node and recommended hardware. +pub struct SubstrateWeight(PhantomData); +impl WeightInfo for SubstrateWeight { + /// Storage: Pov Value (r:1 w:0) + /// Proof: Pov Value (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) + fn storage_single_value_read() -> Weight { + // Proof Size summary in bytes: + // Measured: `136` + // Estimated: `499` + // Minimum execution time: 3_291 nanoseconds. + Weight::from_parts(3_291_000, 499) + .saturating_add(T::DbWeight::get().reads(1_u64)) + } + /// Storage: Pov Value (r:1 w:0) + /// Proof: Pov Value (max_values: Some(1), max_size: Some(4), added: 499, mode: Ignored) + fn storage_single_value_ignored_read() -> Weight { + // Proof Size summary in bytes: + // Measured: `136` + // Estimated: `0` + // Minimum execution time: 2_918 nanoseconds. + Weight::from_ref_time(2_918_000) + .saturating_add(T::DbWeight::get().reads(1_u64)) + } + /// Storage: Pov Value (r:1 w:0) + /// Proof: Pov Value (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) + /// Storage: Pov Value2 (r:1 w:0) + /// Proof: Pov Value2 (max_values: Some(1), max_size: Some(4), added: 499, mode: Ignored) + fn storage_single_value_ignored_some_read() -> Weight { + // Proof Size summary in bytes: + // Measured: `160` + // Estimated: `659` + // Minimum execution time: 4_056 nanoseconds. + Weight::from_parts(4_056_000, 659) + .saturating_add(T::DbWeight::get().reads(2_u64)) + } + /// Storage: Pov Value (r:1 w:0) + /// Proof: Pov Value (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) + fn storage_single_value_read_twice() -> Weight { + // Proof Size summary in bytes: + // Measured: `136` + // Estimated: `499` + // Minimum execution time: 3_552 nanoseconds. + Weight::from_parts(3_552_000, 499) + .saturating_add(T::DbWeight::get().reads(1_u64)) + } + /// Storage: Pov Value (r:0 w:1) + /// Proof: Pov Value (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) + fn storage_single_value_write() -> Weight { + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 795 nanoseconds. + Weight::from_ref_time(795_000) + .saturating_add(T::DbWeight::get().writes(1_u64)) + } + /// Storage: Pov Value (r:0 w:1) + /// Proof: Pov Value (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) + fn storage_single_value_kill() -> Weight { + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 865 nanoseconds. + Weight::from_ref_time(865_000) + .saturating_add(T::DbWeight::get().writes(1_u64)) + } + /// Storage: Pov Map1M (r:1 w:0) + /// Proof: Pov Map1M (max_values: Some(1000000), max_size: Some(36), added: 2511, mode: Measured) + fn storage_1m_map_read_one_value_two_additional_layers() -> Weight { + // Proof Size summary in bytes: + // Measured: `1275` + // Estimated: `3750` + // Minimum execution time: 9_571 nanoseconds. + Weight::from_parts(9_571_000, 3750) + .saturating_add(T::DbWeight::get().reads(1_u64)) + } + /// Storage: Pov Map1M (r:1 w:0) + /// Proof: Pov Map1M (max_values: Some(1000000), max_size: Some(36), added: 2511, mode: Measured) + fn storage_1m_map_read_one_value_three_additional_layers() -> Weight { + // Proof Size summary in bytes: + // Measured: `1544` + // Estimated: `4019` + // Minimum execution time: 14_597 nanoseconds. + Weight::from_parts(14_597_000, 4019) + .saturating_add(T::DbWeight::get().reads(1_u64)) + } + /// Storage: Pov Map1M (r:1 w:0) + /// Proof: Pov Map1M (max_values: Some(1000000), max_size: Some(36), added: 2511, mode: Measured) + fn storage_1m_map_read_one_value_four_additional_layers() -> Weight { + // Proof Size summary in bytes: + // Measured: `2044` + // Estimated: `4519` + // Minimum execution time: 19_793 nanoseconds. + Weight::from_parts(19_793_000, 4519) + .saturating_add(T::DbWeight::get().reads(1_u64)) + } + /// Storage: Pov Map1M (r:100 w:0) + /// Proof: Pov Map1M (max_values: Some(1000000), max_size: Some(36), added: 2511, mode: MaxEncodedLen) + /// Storage: Pov Map16M (r:100 w:0) + /// Proof: Pov Map16M (max_values: Some(16000000), max_size: Some(36), added: 3006, mode: MaxEncodedLen) + /// The range of component `n` is `[0, 100]`. + /// The range of component `m` is `[0, 100]`. + fn storage_map_read_per_component(n: u32, m: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `515 + n * (188 ±0) + m * (188 ±0)` + // Estimated: `0 + n * (3006 ±0) + m * (2511 ±0)` + // Minimum execution time: 266_160 nanoseconds. + Weight::from_ref_time(190_477_747) + // Standard Error: 96_405 + .saturating_add(Weight::from_ref_time(1_049_993).saturating_mul(n.into())) + // Standard Error: 96_405 + .saturating_add(Weight::from_ref_time(1_202_546).saturating_mul(m.into())) + .saturating_add(T::DbWeight::get().reads((1_u64).saturating_mul(n.into()))) + .saturating_add(T::DbWeight::get().reads((1_u64).saturating_mul(m.into()))) + .saturating_add(Weight::from_proof_size(3006).saturating_mul(n.into())) + .saturating_add(Weight::from_proof_size(2511).saturating_mul(m.into())) + } + /// Storage: Pov Map1M (r:100 w:0) + /// Proof: Pov Map1M (max_values: Some(1000000), max_size: Some(36), added: 2511, mode: Ignored) + /// Storage: Pov Map16M (r:100 w:0) + /// Proof: Pov Map16M (max_values: Some(16000000), max_size: Some(36), added: 3006, mode: MaxEncodedLen) + /// The range of component `n` is `[0, 100]`. + /// The range of component `m` is `[0, 100]`. + fn storage_map_read_per_component_one_ignored(n: u32, m: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `515 + n * (188 ±0) + m * (188 ±0)` + // Estimated: `695 + n * (3195 ±2) + m * (189 ±2)` + // Minimum execution time: 265_113 nanoseconds. + Weight::from_parts(215_943_973, 695) + // Standard Error: 199_907 + .saturating_add(Weight::from_ref_time(988_371).saturating_mul(n.into())) + // Standard Error: 199_907 + .saturating_add(Weight::from_ref_time(1_145_693).saturating_mul(m.into())) + .saturating_add(T::DbWeight::get().reads((1_u64).saturating_mul(n.into()))) + .saturating_add(T::DbWeight::get().reads((1_u64).saturating_mul(m.into()))) + .saturating_add(Weight::from_proof_size(3195).saturating_mul(n.into())) + .saturating_add(Weight::from_proof_size(189).saturating_mul(m.into())) + } + /// Storage: Pov Map1M (r:1 w:0) + /// Proof: Pov Map1M (max_values: Some(1000000), max_size: Some(36), added: 2511, mode: MaxEncodedLen) + /// The range of component `n` is `[0, 100]`. + fn storage_1m_map_one_entry_repeated_read(n: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `170` + // Estimated: `2511` + // Minimum execution time: 119 nanoseconds. + Weight::from_parts(3_649_405, 2511) + // Standard Error: 4_472 + .saturating_add(Weight::from_ref_time(432_147).saturating_mul(n.into())) + .saturating_add(T::DbWeight::get().reads(1_u64)) + } + /// Storage: Pov Map1M (r:100 w:0) + /// Proof: Pov Map1M (max_values: Some(1000000), max_size: Some(36), added: 2511, mode: MaxEncodedLen) + /// The range of component `n` is `[0, 100]`. + fn storage_1m_map_multiple_entry_repeated_read(n: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `147 + n * (40 ±0)` + // Estimated: `0 + n * (2511 ±0)` + // Minimum execution time: 106 nanoseconds. + Weight::from_ref_time(59_892_609) + // Standard Error: 418_004 + .saturating_add(Weight::from_ref_time(5_919_793).saturating_mul(n.into())) + .saturating_add(T::DbWeight::get().reads((1_u64).saturating_mul(n.into()))) + .saturating_add(Weight::from_proof_size(2511).saturating_mul(n.into())) + } + /// Storage: Pov DoubleMap1M (r:1024 w:0) + /// Proof: Pov DoubleMap1M (max_values: Some(1000000), max_size: Some(68), added: 2543, mode: MaxEncodedLen) + /// The range of component `n` is `[0, 1024]`. + fn storage_1m_double_map_read_per_component(n: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `21938 + n * (57 ±0)` + // Estimated: `0 + n * (2543 ±0)` + // Minimum execution time: 550 nanoseconds. + Weight::from_ref_time(65_431_603) + // Standard Error: 66_935 + .saturating_add(Weight::from_ref_time(2_825_371).saturating_mul(n.into())) + .saturating_add(T::DbWeight::get().reads((1_u64).saturating_mul(n.into()))) + .saturating_add(Weight::from_proof_size(2543).saturating_mul(n.into())) + } + /// Storage: Pov BoundedValue (r:1 w:0) + /// Proof: Pov BoundedValue (max_values: Some(1), max_size: Some(33), added: 528, mode: MaxEncodedLen) + fn storage_value_bounded_read() -> Weight { + // Proof Size summary in bytes: + // Measured: `109` + // Estimated: `528` + // Minimum execution time: 2_541 nanoseconds. + Weight::from_parts(2_541_000, 528) + .saturating_add(T::DbWeight::get().reads(1_u64)) + } + /// Storage: Pov UnboundedValue (r:1 w:0) + /// Proof Skipped: Pov UnboundedValue (max_values: Some(1), max_size: None, mode: Measured) + fn storage_value_unbounded_read() -> Weight { + // Proof Size summary in bytes: + // Measured: `109` + // Estimated: `604` + // Minimum execution time: 2_226 nanoseconds. + Weight::from_parts(2_226_000, 604) + .saturating_add(T::DbWeight::get().reads(1_u64)) + } + /// Storage: Pov UnboundedValue (r:1 w:0) + /// Proof Skipped: Pov UnboundedValue (max_values: Some(1), max_size: None, mode: Ignored) + fn storage_value_unbounded_ignored_read() -> Weight { + // Proof Size summary in bytes: + // Measured: `109` + // Estimated: `0` + // Minimum execution time: 2_100 nanoseconds. + Weight::from_ref_time(2_100_000) + .saturating_add(T::DbWeight::get().reads(1_u64)) + } + /// Storage: Pov UnboundedValue (r:1 w:0) + /// Proof Skipped: Pov UnboundedValue (max_values: Some(1), max_size: None, mode: Measured) + /// Storage: Pov BoundedValue (r:1 w:0) + /// Proof: Pov BoundedValue (max_values: Some(1), max_size: Some(33), added: 528, mode: MaxEncodedLen) + fn storage_value_bounded_and_unbounded_read() -> Weight { + // Proof Size summary in bytes: + // Measured: `109` + // Estimated: `1132` + // Minimum execution time: 2_808 nanoseconds. + Weight::from_parts(2_808_000, 1132) + .saturating_add(T::DbWeight::get().reads(2_u64)) + } + /// Storage: Pov LargeValue (r:1 w:0) + /// Proof: Pov LargeValue (max_values: Some(1), max_size: Some(4194308), added: 4194803, mode: Measured) + /// The range of component `l` is `[0, 4194304]`. + fn measured_storage_value_read_linear_size(l: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `174 + l * (1 ±0)` + // Estimated: `666 + l * (1 ±0)` + // Minimum execution time: 3_385 nanoseconds. + Weight::from_parts(3_385_000, 666) + // Standard Error: 10 + .saturating_add(Weight::from_ref_time(356).saturating_mul(l.into())) + .saturating_add(T::DbWeight::get().reads(1_u64)) + .saturating_add(Weight::from_proof_size(1).saturating_mul(l.into())) + } + /// Storage: Pov LargeValue (r:1 w:0) + /// Proof: Pov LargeValue (max_values: Some(1), max_size: Some(4194308), added: 4194803, mode: MaxEncodedLen) + /// The range of component `l` is `[0, 4194304]`. + fn mel_storage_value_read_linear_size(l: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `174 + l * (1 ±0)` + // Estimated: `4194803` + // Minimum execution time: 3_342 nanoseconds. + Weight::from_parts(3_342_000, 4194803) + // Standard Error: 6 + .saturating_add(Weight::from_ref_time(345).saturating_mul(l.into())) + .saturating_add(T::DbWeight::get().reads(1_u64)) + } + /// Storage: Pov LargeValue (r:1 w:0) + /// Proof: Pov LargeValue (max_values: Some(1), max_size: Some(4194308), added: 4194803, mode: Measured) + /// Storage: Pov LargeValue2 (r:1 w:0) + /// Proof: Pov LargeValue2 (max_values: Some(1), max_size: Some(4194308), added: 4194803, mode: Measured) + /// The range of component `l` is `[0, 4194304]`. + fn measured_storage_double_value_read_linear_size(l: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `235 + l * (2 ±0)` + // Estimated: `1448 + l * (4 ±0)` + // Minimum execution time: 4_519 nanoseconds. + Weight::from_parts(4_519_000, 1448) + // Standard Error: 9 + .saturating_add(Weight::from_ref_time(560).saturating_mul(l.into())) + .saturating_add(T::DbWeight::get().reads(2_u64)) + .saturating_add(Weight::from_proof_size(4).saturating_mul(l.into())) + } + /// Storage: Pov LargeValue (r:1 w:0) + /// Proof: Pov LargeValue (max_values: Some(1), max_size: Some(4194308), added: 4194803, mode: MaxEncodedLen) + /// Storage: Pov LargeValue2 (r:1 w:0) + /// Proof: Pov LargeValue2 (max_values: Some(1), max_size: Some(4194308), added: 4194803, mode: MaxEncodedLen) + /// The range of component `l` is `[0, 4194304]`. + fn mel_storage_double_value_read_linear_size(l: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `235 + l * (2 ±0)` + // Estimated: `8389606` + // Minimum execution time: 4_462 nanoseconds. + Weight::from_parts(4_462_000, 8389606) + // Standard Error: 7 + .saturating_add(Weight::from_ref_time(538).saturating_mul(l.into())) + .saturating_add(T::DbWeight::get().reads(2_u64)) + } + /// Storage: Pov LargeValue (r:1 w:0) + /// Proof: Pov LargeValue (max_values: Some(1), max_size: Some(4194308), added: 4194803, mode: MaxEncodedLen) + /// Storage: Pov LargeValue2 (r:1 w:0) + /// Proof: Pov LargeValue2 (max_values: Some(1), max_size: Some(4194308), added: 4194803, mode: Measured) + /// The range of component `l` is `[0, 4194304]`. + fn mel_mixed_storage_double_value_read_linear_size(l: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `235 + l * (2 ±0)` + // Estimated: `4195527 + l * (2 ±0)` + // Minimum execution time: 4_552 nanoseconds. + Weight::from_parts(4_552_000, 4195527) + // Standard Error: 6 + .saturating_add(Weight::from_ref_time(507).saturating_mul(l.into())) + .saturating_add(T::DbWeight::get().reads(2_u64)) + .saturating_add(Weight::from_proof_size(2).saturating_mul(l.into())) + } + /// Storage: Pov LargeValue (r:1 w:0) + /// Proof: Pov LargeValue (max_values: Some(1), max_size: Some(4194308), added: 4194803, mode: Measured) + /// Storage: Pov LargeValue2 (r:1 w:0) + /// Proof: Pov LargeValue2 (max_values: Some(1), max_size: Some(4194308), added: 4194803, mode: MaxEncodedLen) + /// The range of component `l` is `[0, 4194304]`. + fn measured_mixed_storage_double_value_read_linear_size(l: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `235 + l * (2 ±0)` + // Estimated: `4195527 + l * (2 ±0)` + // Minimum execution time: 4_236 nanoseconds. + Weight::from_parts(4_236_000, 4195527) + // Standard Error: 8 + .saturating_add(Weight::from_ref_time(517).saturating_mul(l.into())) + .saturating_add(T::DbWeight::get().reads(2_u64)) + .saturating_add(Weight::from_proof_size(2).saturating_mul(l.into())) + } + /// Storage: Pov UnboundedMap (r:1 w:0) + /// Proof Skipped: Pov UnboundedMap (max_values: None, max_size: None, mode: Measured) + /// Storage: Pov UnboundedMap2 (r:1 w:0) + /// Proof Skipped: Pov UnboundedMap2 (max_values: None, max_size: None, mode: Measured) + /// The range of component `i` is `[0, 1000]`. + fn storage_map_unbounded_both_measured_read(i: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `293 + i * (8 ±0)` + // Estimated: `5524 + i * (16 ±0)` + // Minimum execution time: 5_649 nanoseconds. + Weight::from_parts(6_111_237, 5524) + // Standard Error: 1_060 + .saturating_add(Weight::from_ref_time(2_693).saturating_mul(i.into())) + .saturating_add(T::DbWeight::get().reads(2_u64)) + .saturating_add(Weight::from_proof_size(16).saturating_mul(i.into())) + } + /// Storage: Pov Map1M (r:1 w:0) + /// Proof: Pov Map1M (max_values: Some(1000000), max_size: Some(36), added: 2511, mode: MaxEncodedLen) + /// Storage: Pov UnboundedMap (r:1 w:0) + /// Proof Skipped: Pov UnboundedMap (max_values: None, max_size: None, mode: Measured) + /// The range of component `i` is `[0, 1000]`. + fn storage_map_partial_unbounded_read(i: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `260 + i * (4 ±0)` + // Estimated: `5243 + i * (4 ±0)` + // Minimum execution time: 5_997 nanoseconds. + Weight::from_parts(7_996_508, 5243) + .saturating_add(T::DbWeight::get().reads(2_u64)) + .saturating_add(Weight::from_proof_size(4).saturating_mul(i.into())) + } + /// Storage: Pov Map1M (r:1 w:0) + /// Proof: Pov Map1M (max_values: Some(1000000), max_size: Some(36), added: 2511, mode: MaxEncodedLen) + /// Storage: Pov UnboundedMap (r:1 w:0) + /// Proof Skipped: Pov UnboundedMap (max_values: None, max_size: None, mode: Ignored) + /// The range of component `i` is `[0, 1000]`. + fn storage_map_partial_unbounded_ignored_read(i: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `260 + i * (4 ±0)` + // Estimated: `2768 + i * (4 ±0)` + // Minimum execution time: 5_679 nanoseconds. + Weight::from_parts(6_496_804, 2768) + // Standard Error: 611 + .saturating_add(Weight::from_ref_time(930).saturating_mul(i.into())) + .saturating_add(T::DbWeight::get().reads(2_u64)) + .saturating_add(Weight::from_proof_size(4).saturating_mul(i.into())) + } + fn emit_event() -> Weight { + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 7_087 nanoseconds. + Weight::from_ref_time(7_087_000) + } + fn noop() -> Weight { + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 2_719 nanoseconds. + Weight::from_ref_time(2_719_000) + } +} + +// For backwards compatibility and tests +impl WeightInfo for () { + /// Storage: Pov Value (r:1 w:0) + /// Proof: Pov Value (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) + fn storage_single_value_read() -> Weight { + // Proof Size summary in bytes: + // Measured: `136` + // Estimated: `499` + // Minimum execution time: 3_291 nanoseconds. + Weight::from_parts(3_291_000, 499) + .saturating_add(RocksDbWeight::get().reads(1_u64)) + } + /// Storage: Pov Value (r:1 w:0) + /// Proof: Pov Value (max_values: Some(1), max_size: Some(4), added: 499, mode: Ignored) + fn storage_single_value_ignored_read() -> Weight { + // Proof Size summary in bytes: + // Measured: `136` + // Estimated: `0` + // Minimum execution time: 2_918 nanoseconds. + Weight::from_ref_time(2_918_000) + .saturating_add(RocksDbWeight::get().reads(1_u64)) + } + /// Storage: Pov Value (r:1 w:0) + /// Proof: Pov Value (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) + /// Storage: Pov Value2 (r:1 w:0) + /// Proof: Pov Value2 (max_values: Some(1), max_size: Some(4), added: 499, mode: Ignored) + fn storage_single_value_ignored_some_read() -> Weight { + // Proof Size summary in bytes: + // Measured: `160` + // Estimated: `659` + // Minimum execution time: 4_056 nanoseconds. + Weight::from_parts(4_056_000, 659) + .saturating_add(RocksDbWeight::get().reads(2_u64)) + } + /// Storage: Pov Value (r:1 w:0) + /// Proof: Pov Value (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) + fn storage_single_value_read_twice() -> Weight { + // Proof Size summary in bytes: + // Measured: `136` + // Estimated: `499` + // Minimum execution time: 3_552 nanoseconds. + Weight::from_parts(3_552_000, 499) + .saturating_add(RocksDbWeight::get().reads(1_u64)) + } + /// Storage: Pov Value (r:0 w:1) + /// Proof: Pov Value (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) + fn storage_single_value_write() -> Weight { + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 795 nanoseconds. + Weight::from_ref_time(795_000) + .saturating_add(RocksDbWeight::get().writes(1_u64)) + } + /// Storage: Pov Value (r:0 w:1) + /// Proof: Pov Value (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) + fn storage_single_value_kill() -> Weight { + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 865 nanoseconds. + Weight::from_ref_time(865_000) + .saturating_add(RocksDbWeight::get().writes(1_u64)) + } + /// Storage: Pov Map1M (r:1 w:0) + /// Proof: Pov Map1M (max_values: Some(1000000), max_size: Some(36), added: 2511, mode: Measured) + fn storage_1m_map_read_one_value_two_additional_layers() -> Weight { + // Proof Size summary in bytes: + // Measured: `1275` + // Estimated: `3750` + // Minimum execution time: 9_571 nanoseconds. + Weight::from_parts(9_571_000, 3750) + .saturating_add(RocksDbWeight::get().reads(1_u64)) + } + /// Storage: Pov Map1M (r:1 w:0) + /// Proof: Pov Map1M (max_values: Some(1000000), max_size: Some(36), added: 2511, mode: Measured) + fn storage_1m_map_read_one_value_three_additional_layers() -> Weight { + // Proof Size summary in bytes: + // Measured: `1544` + // Estimated: `4019` + // Minimum execution time: 14_597 nanoseconds. + Weight::from_parts(14_597_000, 4019) + .saturating_add(RocksDbWeight::get().reads(1_u64)) + } + /// Storage: Pov Map1M (r:1 w:0) + /// Proof: Pov Map1M (max_values: Some(1000000), max_size: Some(36), added: 2511, mode: Measured) + fn storage_1m_map_read_one_value_four_additional_layers() -> Weight { + // Proof Size summary in bytes: + // Measured: `2044` + // Estimated: `4519` + // Minimum execution time: 19_793 nanoseconds. + Weight::from_parts(19_793_000, 4519) + .saturating_add(RocksDbWeight::get().reads(1_u64)) + } + /// Storage: Pov Map1M (r:100 w:0) + /// Proof: Pov Map1M (max_values: Some(1000000), max_size: Some(36), added: 2511, mode: MaxEncodedLen) + /// Storage: Pov Map16M (r:100 w:0) + /// Proof: Pov Map16M (max_values: Some(16000000), max_size: Some(36), added: 3006, mode: MaxEncodedLen) + /// The range of component `n` is `[0, 100]`. + /// The range of component `m` is `[0, 100]`. + fn storage_map_read_per_component(n: u32, m: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `515 + n * (188 ±0) + m * (188 ±0)` + // Estimated: `0 + n * (3006 ±0) + m * (2511 ±0)` + // Minimum execution time: 266_160 nanoseconds. + Weight::from_ref_time(190_477_747) + // Standard Error: 96_405 + .saturating_add(Weight::from_ref_time(1_049_993).saturating_mul(n.into())) + // Standard Error: 96_405 + .saturating_add(Weight::from_ref_time(1_202_546).saturating_mul(m.into())) + .saturating_add(RocksDbWeight::get().reads((1_u64).saturating_mul(n.into()))) + .saturating_add(RocksDbWeight::get().reads((1_u64).saturating_mul(m.into()))) + .saturating_add(Weight::from_proof_size(3006).saturating_mul(n.into())) + .saturating_add(Weight::from_proof_size(2511).saturating_mul(m.into())) + } + /// Storage: Pov Map1M (r:100 w:0) + /// Proof: Pov Map1M (max_values: Some(1000000), max_size: Some(36), added: 2511, mode: Ignored) + /// Storage: Pov Map16M (r:100 w:0) + /// Proof: Pov Map16M (max_values: Some(16000000), max_size: Some(36), added: 3006, mode: MaxEncodedLen) + /// The range of component `n` is `[0, 100]`. + /// The range of component `m` is `[0, 100]`. + fn storage_map_read_per_component_one_ignored(n: u32, m: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `515 + n * (188 ±0) + m * (188 ±0)` + // Estimated: `695 + n * (3195 ±2) + m * (189 ±2)` + // Minimum execution time: 265_113 nanoseconds. + Weight::from_parts(215_943_973, 695) + // Standard Error: 199_907 + .saturating_add(Weight::from_ref_time(988_371).saturating_mul(n.into())) + // Standard Error: 199_907 + .saturating_add(Weight::from_ref_time(1_145_693).saturating_mul(m.into())) + .saturating_add(RocksDbWeight::get().reads((1_u64).saturating_mul(n.into()))) + .saturating_add(RocksDbWeight::get().reads((1_u64).saturating_mul(m.into()))) + .saturating_add(Weight::from_proof_size(3195).saturating_mul(n.into())) + .saturating_add(Weight::from_proof_size(189).saturating_mul(m.into())) + } + /// Storage: Pov Map1M (r:1 w:0) + /// Proof: Pov Map1M (max_values: Some(1000000), max_size: Some(36), added: 2511, mode: MaxEncodedLen) + /// The range of component `n` is `[0, 100]`. + fn storage_1m_map_one_entry_repeated_read(n: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `170` + // Estimated: `2511` + // Minimum execution time: 119 nanoseconds. + Weight::from_parts(3_649_405, 2511) + // Standard Error: 4_472 + .saturating_add(Weight::from_ref_time(432_147).saturating_mul(n.into())) + .saturating_add(RocksDbWeight::get().reads(1_u64)) + } + /// Storage: Pov Map1M (r:100 w:0) + /// Proof: Pov Map1M (max_values: Some(1000000), max_size: Some(36), added: 2511, mode: MaxEncodedLen) + /// The range of component `n` is `[0, 100]`. + fn storage_1m_map_multiple_entry_repeated_read(n: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `147 + n * (40 ±0)` + // Estimated: `0 + n * (2511 ±0)` + // Minimum execution time: 106 nanoseconds. + Weight::from_ref_time(59_892_609) + // Standard Error: 418_004 + .saturating_add(Weight::from_ref_time(5_919_793).saturating_mul(n.into())) + .saturating_add(RocksDbWeight::get().reads((1_u64).saturating_mul(n.into()))) + .saturating_add(Weight::from_proof_size(2511).saturating_mul(n.into())) + } + /// Storage: Pov DoubleMap1M (r:1024 w:0) + /// Proof: Pov DoubleMap1M (max_values: Some(1000000), max_size: Some(68), added: 2543, mode: MaxEncodedLen) + /// The range of component `n` is `[0, 1024]`. + fn storage_1m_double_map_read_per_component(n: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `21938 + n * (57 ±0)` + // Estimated: `0 + n * (2543 ±0)` + // Minimum execution time: 550 nanoseconds. + Weight::from_ref_time(65_431_603) + // Standard Error: 66_935 + .saturating_add(Weight::from_ref_time(2_825_371).saturating_mul(n.into())) + .saturating_add(RocksDbWeight::get().reads((1_u64).saturating_mul(n.into()))) + .saturating_add(Weight::from_proof_size(2543).saturating_mul(n.into())) + } + /// Storage: Pov BoundedValue (r:1 w:0) + /// Proof: Pov BoundedValue (max_values: Some(1), max_size: Some(33), added: 528, mode: MaxEncodedLen) + fn storage_value_bounded_read() -> Weight { + // Proof Size summary in bytes: + // Measured: `109` + // Estimated: `528` + // Minimum execution time: 2_541 nanoseconds. + Weight::from_parts(2_541_000, 528) + .saturating_add(RocksDbWeight::get().reads(1_u64)) + } + /// Storage: Pov UnboundedValue (r:1 w:0) + /// Proof Skipped: Pov UnboundedValue (max_values: Some(1), max_size: None, mode: Measured) + fn storage_value_unbounded_read() -> Weight { + // Proof Size summary in bytes: + // Measured: `109` + // Estimated: `604` + // Minimum execution time: 2_226 nanoseconds. + Weight::from_parts(2_226_000, 604) + .saturating_add(RocksDbWeight::get().reads(1_u64)) + } + /// Storage: Pov UnboundedValue (r:1 w:0) + /// Proof Skipped: Pov UnboundedValue (max_values: Some(1), max_size: None, mode: Ignored) + fn storage_value_unbounded_ignored_read() -> Weight { + // Proof Size summary in bytes: + // Measured: `109` + // Estimated: `0` + // Minimum execution time: 2_100 nanoseconds. + Weight::from_ref_time(2_100_000) + .saturating_add(RocksDbWeight::get().reads(1_u64)) + } + /// Storage: Pov UnboundedValue (r:1 w:0) + /// Proof Skipped: Pov UnboundedValue (max_values: Some(1), max_size: None, mode: Measured) + /// Storage: Pov BoundedValue (r:1 w:0) + /// Proof: Pov BoundedValue (max_values: Some(1), max_size: Some(33), added: 528, mode: MaxEncodedLen) + fn storage_value_bounded_and_unbounded_read() -> Weight { + // Proof Size summary in bytes: + // Measured: `109` + // Estimated: `1132` + // Minimum execution time: 2_808 nanoseconds. + Weight::from_parts(2_808_000, 1132) + .saturating_add(RocksDbWeight::get().reads(2_u64)) + } + /// Storage: Pov LargeValue (r:1 w:0) + /// Proof: Pov LargeValue (max_values: Some(1), max_size: Some(4194308), added: 4194803, mode: Measured) + /// The range of component `l` is `[0, 4194304]`. + fn measured_storage_value_read_linear_size(l: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `174 + l * (1 ±0)` + // Estimated: `666 + l * (1 ±0)` + // Minimum execution time: 3_385 nanoseconds. + Weight::from_parts(3_385_000, 666) + // Standard Error: 10 + .saturating_add(Weight::from_ref_time(356).saturating_mul(l.into())) + .saturating_add(RocksDbWeight::get().reads(1_u64)) + .saturating_add(Weight::from_proof_size(1).saturating_mul(l.into())) + } + /// Storage: Pov LargeValue (r:1 w:0) + /// Proof: Pov LargeValue (max_values: Some(1), max_size: Some(4194308), added: 4194803, mode: MaxEncodedLen) + /// The range of component `l` is `[0, 4194304]`. + fn mel_storage_value_read_linear_size(l: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `174 + l * (1 ±0)` + // Estimated: `4194803` + // Minimum execution time: 3_342 nanoseconds. + Weight::from_parts(3_342_000, 4194803) + // Standard Error: 6 + .saturating_add(Weight::from_ref_time(345).saturating_mul(l.into())) + .saturating_add(RocksDbWeight::get().reads(1_u64)) + } + /// Storage: Pov LargeValue (r:1 w:0) + /// Proof: Pov LargeValue (max_values: Some(1), max_size: Some(4194308), added: 4194803, mode: Measured) + /// Storage: Pov LargeValue2 (r:1 w:0) + /// Proof: Pov LargeValue2 (max_values: Some(1), max_size: Some(4194308), added: 4194803, mode: Measured) + /// The range of component `l` is `[0, 4194304]`. + fn measured_storage_double_value_read_linear_size(l: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `235 + l * (2 ±0)` + // Estimated: `1448 + l * (4 ±0)` + // Minimum execution time: 4_519 nanoseconds. + Weight::from_parts(4_519_000, 1448) + // Standard Error: 9 + .saturating_add(Weight::from_ref_time(560).saturating_mul(l.into())) + .saturating_add(RocksDbWeight::get().reads(2_u64)) + .saturating_add(Weight::from_proof_size(4).saturating_mul(l.into())) + } + /// Storage: Pov LargeValue (r:1 w:0) + /// Proof: Pov LargeValue (max_values: Some(1), max_size: Some(4194308), added: 4194803, mode: MaxEncodedLen) + /// Storage: Pov LargeValue2 (r:1 w:0) + /// Proof: Pov LargeValue2 (max_values: Some(1), max_size: Some(4194308), added: 4194803, mode: MaxEncodedLen) + /// The range of component `l` is `[0, 4194304]`. + fn mel_storage_double_value_read_linear_size(l: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `235 + l * (2 ±0)` + // Estimated: `8389606` + // Minimum execution time: 4_462 nanoseconds. + Weight::from_parts(4_462_000, 8389606) + // Standard Error: 7 + .saturating_add(Weight::from_ref_time(538).saturating_mul(l.into())) + .saturating_add(RocksDbWeight::get().reads(2_u64)) + } + /// Storage: Pov LargeValue (r:1 w:0) + /// Proof: Pov LargeValue (max_values: Some(1), max_size: Some(4194308), added: 4194803, mode: MaxEncodedLen) + /// Storage: Pov LargeValue2 (r:1 w:0) + /// Proof: Pov LargeValue2 (max_values: Some(1), max_size: Some(4194308), added: 4194803, mode: Measured) + /// The range of component `l` is `[0, 4194304]`. + fn mel_mixed_storage_double_value_read_linear_size(l: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `235 + l * (2 ±0)` + // Estimated: `4195527 + l * (2 ±0)` + // Minimum execution time: 4_552 nanoseconds. + Weight::from_parts(4_552_000, 4195527) + // Standard Error: 6 + .saturating_add(Weight::from_ref_time(507).saturating_mul(l.into())) + .saturating_add(RocksDbWeight::get().reads(2_u64)) + .saturating_add(Weight::from_proof_size(2).saturating_mul(l.into())) + } + /// Storage: Pov LargeValue (r:1 w:0) + /// Proof: Pov LargeValue (max_values: Some(1), max_size: Some(4194308), added: 4194803, mode: Measured) + /// Storage: Pov LargeValue2 (r:1 w:0) + /// Proof: Pov LargeValue2 (max_values: Some(1), max_size: Some(4194308), added: 4194803, mode: MaxEncodedLen) + /// The range of component `l` is `[0, 4194304]`. + fn measured_mixed_storage_double_value_read_linear_size(l: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `235 + l * (2 ±0)` + // Estimated: `4195527 + l * (2 ±0)` + // Minimum execution time: 4_236 nanoseconds. + Weight::from_parts(4_236_000, 4195527) + // Standard Error: 8 + .saturating_add(Weight::from_ref_time(517).saturating_mul(l.into())) + .saturating_add(RocksDbWeight::get().reads(2_u64)) + .saturating_add(Weight::from_proof_size(2).saturating_mul(l.into())) + } + /// Storage: Pov UnboundedMap (r:1 w:0) + /// Proof Skipped: Pov UnboundedMap (max_values: None, max_size: None, mode: Measured) + /// Storage: Pov UnboundedMap2 (r:1 w:0) + /// Proof Skipped: Pov UnboundedMap2 (max_values: None, max_size: None, mode: Measured) + /// The range of component `i` is `[0, 1000]`. + fn storage_map_unbounded_both_measured_read(i: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `293 + i * (8 ±0)` + // Estimated: `5524 + i * (16 ±0)` + // Minimum execution time: 5_649 nanoseconds. + Weight::from_parts(6_111_237, 5524) + // Standard Error: 1_060 + .saturating_add(Weight::from_ref_time(2_693).saturating_mul(i.into())) + .saturating_add(RocksDbWeight::get().reads(2_u64)) + .saturating_add(Weight::from_proof_size(16).saturating_mul(i.into())) + } + /// Storage: Pov Map1M (r:1 w:0) + /// Proof: Pov Map1M (max_values: Some(1000000), max_size: Some(36), added: 2511, mode: MaxEncodedLen) + /// Storage: Pov UnboundedMap (r:1 w:0) + /// Proof Skipped: Pov UnboundedMap (max_values: None, max_size: None, mode: Measured) + /// The range of component `i` is `[0, 1000]`. + fn storage_map_partial_unbounded_read(i: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `260 + i * (4 ±0)` + // Estimated: `5243 + i * (4 ±0)` + // Minimum execution time: 5_997 nanoseconds. + Weight::from_parts(7_996_508, 5243) + .saturating_add(RocksDbWeight::get().reads(2_u64)) + .saturating_add(Weight::from_proof_size(4).saturating_mul(i.into())) + } + /// Storage: Pov Map1M (r:1 w:0) + /// Proof: Pov Map1M (max_values: Some(1000000), max_size: Some(36), added: 2511, mode: MaxEncodedLen) + /// Storage: Pov UnboundedMap (r:1 w:0) + /// Proof Skipped: Pov UnboundedMap (max_values: None, max_size: None, mode: Ignored) + /// The range of component `i` is `[0, 1000]`. + fn storage_map_partial_unbounded_ignored_read(i: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `260 + i * (4 ±0)` + // Estimated: `2768 + i * (4 ±0)` + // Minimum execution time: 5_679 nanoseconds. + Weight::from_parts(6_496_804, 2768) + // Standard Error: 611 + .saturating_add(Weight::from_ref_time(930).saturating_mul(i.into())) + .saturating_add(RocksDbWeight::get().reads(2_u64)) + .saturating_add(Weight::from_proof_size(4).saturating_mul(i.into())) + } + fn emit_event() -> Weight { + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 7_087 nanoseconds. + Weight::from_ref_time(7_087_000) + } + fn noop() -> Weight { + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 2_719 nanoseconds. + Weight::from_ref_time(2_719_000) + } +} diff --git a/frame/benchmarking/src/baseline.rs b/frame/benchmarking/src/baseline.rs index 5fd845551..0d511cbff 100644 --- a/frame/benchmarking/src/baseline.rs +++ b/frame/benchmarking/src/baseline.rs @@ -16,29 +16,25 @@ // limitations under the License. //! A set of benchmarks which can establish a global baseline for all other -//! benchmarking. +//! benchmarking. These benchmarks do not require a pallet to be deployed. #![cfg(feature = "runtime-benchmarks")] +use super::*; use crate::benchmarks; -use codec::Encode; use frame_system::Pallet as System; -use sp_application_crypto::KeyTypeId; use sp_runtime::{ traits::{AppVerify, Hash}, RuntimeAppPublic, }; -use sp_std::prelude::*; -pub const TEST_KEY_TYPE_ID: KeyTypeId = KeyTypeId(*b"test"); +mod crypto { + use sp_application_crypto::{app_crypto, sr25519, KeyTypeId}; -mod app_sr25519 { - use super::TEST_KEY_TYPE_ID; - use sp_application_crypto::{app_crypto, sr25519}; + pub const TEST_KEY_TYPE_ID: KeyTypeId = KeyTypeId(*b"test"); app_crypto!(sr25519, TEST_KEY_TYPE_ID); } - -type SignerId = app_sr25519::Public; +pub type SignerId = crypto::Public; pub struct Pallet(System); pub trait Config: frame_system::Config {} @@ -81,7 +77,6 @@ benchmarks! { } hashing { - let i in 0 .. 100; let mut hash = T::Hash::default(); }: { (0..=100_000u32).for_each(|j| hash = T::Hashing::hash(&j.to_be_bytes())); @@ -106,53 +101,17 @@ benchmarks! { }); } - #[skip_meta] - storage_read { - let i in 0 .. 1_000; - let mut people = Vec::new(); - (0..i).for_each(|j| { - let hash = T::Hashing::hash(&j.to_be_bytes()).encode(); - frame_support::storage::unhashed::put(&hash, &hash); - people.push(hash); - }); - }: { - people.iter().for_each(|hash| { - // This does a storage read - let value = frame_support::storage::unhashed::get(hash); - assert_eq!(value, Some(hash.to_vec())); - }); - } - - #[skip_meta] - storage_write { - let i in 0 .. 1_000; - let mut hashes = Vec::new(); - (0..i).for_each(|j| { - let hash = T::Hashing::hash(&j.to_be_bytes()); - hashes.push(hash.encode()); - }); - }: { - hashes.iter().for_each(|hash| { - // This does a storage write - frame_support::storage::unhashed::put(hash, hash); - }); - } verify { - hashes.iter().for_each(|hash| { - let value = frame_support::storage::unhashed::get(hash); - assert_eq!(value, Some(hash.to_vec())); - }); - } - impl_benchmark_test_suite!( Pallet, - crate::baseline::mock::new_test_ext(), - crate::baseline::mock::Test, + mock::new_test_ext(), + mock::Test, ); } #[cfg(test)] pub mod mock { - use sp_runtime::{testing::H256, traits::IdentityLookup}; + use super::*; + use sp_runtime::testing::H256; type AccountId = u64; type AccountIndex = u32; @@ -183,7 +142,7 @@ pub mod mock { type Hash = H256; type Hashing = ::sp_runtime::traits::BlakeTwo256; type AccountId = AccountId; - type Lookup = IdentityLookup; + type Lookup = sp_runtime::traits::IdentityLookup; type Header = sp_runtime::testing::Header; type RuntimeEvent = RuntimeEvent; type BlockHashCount = (); diff --git a/frame/benchmarking/src/lib.rs b/frame/benchmarking/src/lib.rs index e4ebd2f22..525370e23 100644 --- a/frame/benchmarking/src/lib.rs +++ b/frame/benchmarking/src/lib.rs @@ -47,7 +47,7 @@ pub use sp_runtime::traits::Zero; pub use sp_runtime::StateVersion; #[doc(hidden)] pub use sp_std::{self, boxed::Box, prelude::Vec, str, vec}; -pub use sp_storage::TrackedStorageKey; +pub use sp_storage::{well_known_keys, TrackedStorageKey}; pub use utils::*; /// Whitelist the given account. @@ -211,6 +211,7 @@ macro_rules! benchmarks { ( ) ( ) ( ) + ( ) $( $rest )* ); } @@ -231,6 +232,7 @@ macro_rules! benchmarks_instance { ( ) ( ) ( ) + ( ) $( $rest )* ); } @@ -251,6 +253,7 @@ macro_rules! benchmarks_instance_pallet { ( ) ( ) ( ) + ( ) $( $rest )* ); } @@ -268,6 +271,7 @@ macro_rules! benchmarks_iter { ( $( $names:tt )* ) ( $( $names_extra:tt )* ) ( $( $names_skip_meta:tt )* ) + ( $( $pov_name:ident: $( $storage:path = $pov_mode:ident )*; )* ) impl_benchmark_test_suite!( $bench_module:ident, $new_test_ext:expr, @@ -282,6 +286,7 @@ macro_rules! benchmarks_iter { ( $( $names )* ) ( $( $names_extra )* ) ( $( $names_skip_meta )* ) + ( $( $pov_name: $( $storage = $pov_mode )*; )* ) $( $rest )* } }; @@ -293,6 +298,7 @@ macro_rules! benchmarks_iter { ( $( $names:tt )* ) ( $( $names_extra:tt )* ) ( $( $names_skip_meta:tt )* ) + ( $( $pov_name:ident: $( $storage:path = $pov_mode:ident )*; )* ) impl_benchmark_test_suite!( $bench_module:ident, $new_test_ext:expr, @@ -307,6 +313,7 @@ macro_rules! benchmarks_iter { ( $( $names )* ) ( $( $names_extra )* ) ( $( $names_skip_meta )* ) + ( $( $pov_name: $( $storage = $pov_mode )*; )* ) $( $rest )* } }; @@ -318,6 +325,7 @@ macro_rules! benchmarks_iter { ( $( $names:tt )* ) ( $( $names_extra:tt )* ) ( $( $names_skip_meta:tt )* ) + ( $( $pov_name:ident: $( $storage:path = $pov_mode:ident )*; )* ) where_clause { where $( $where_bound:tt )* } $( $rest:tt )* ) => { @@ -328,6 +336,7 @@ macro_rules! benchmarks_iter { ( $( $names )* ) ( $( $names_extra )* ) ( $( $names_skip_meta )* ) + ( $( $pov_name: $( $storage = $pov_mode )*; )* ) $( $rest )* } }; @@ -339,7 +348,9 @@ macro_rules! benchmarks_iter { ( $( $names:tt )* ) ( $( $names_extra:tt )* ) ( $( $names_skip_meta:tt )* ) + ( $( $pov_name:ident: $( $storage:path = $pov_mode:ident )*; )* ) #[skip_meta] + $( #[ $($attributes:tt)+ ] )* $name:ident $( $rest:tt )* ) => { @@ -350,6 +361,8 @@ macro_rules! benchmarks_iter { ( $( $names )* ) ( $( $names_extra )* ) ( $( $names_skip_meta )* $name ) + ( $( $pov_name: $( $storage = $pov_mode )*; )* ) + $( #[ $( $attributes )+ ] )* $name $( $rest )* } @@ -362,7 +375,9 @@ macro_rules! benchmarks_iter { ( $( $names:tt )* ) ( $( $names_extra:tt )* ) ( $( $names_skip_meta:tt )* ) + ( $( $pov_name:ident: $( $storage:path = $pov_mode:ident )*; )* ) #[extra] + $( #[ $($attributes:tt)+ ] )* $name:ident $( $rest:tt )* ) => { @@ -373,6 +388,35 @@ macro_rules! benchmarks_iter { ( $( $names )* ) ( $( $names_extra )* $name ) ( $( $names_skip_meta )* ) + ( $( $pov_name: $( $storage = $pov_mode )*; )* ) + $( #[ $( $attributes )+ ] )* + $name + $( $rest )* + } + }; + // detect and extract `#[pov_mode = Mode { Pallet::Storage: Mode ... }]` tag: + ( + { $($bench_module:ident, $new_test_ext:expr, $test:path $(, $( $args:tt )* )?)? } + { $( $instance:ident: $instance_bound:tt )? } + { $( $where_clause:tt )* } + ( $( $names:tt )* ) + ( $( $names_extra:tt )* ) + ( $( $names_skip_meta:tt )* ) + ( $( $old_pov_name:ident: $( $old_storage:path = $old_pov_mode:ident )*; )* ) + #[pov_mode = $mode:ident $( { $( $storage:path: $pov_mode:ident )* } )?] + $( #[ $($attributes:tt)+ ] )* + $name:ident + $( $rest:tt )* + ) => { + $crate::benchmarks_iter! { + { $($bench_module, $new_test_ext, $test $(, $( $args )* )?)? } + { $( $instance: $instance_bound )? } + { $( $where_clause )* } + ( $( $names )* ) + ( $( $names_extra )* ) + ( $( $names_skip_meta )* ) + ( $name: ALL = $mode $($( $storage = $pov_mode )*)?; $( $old_pov_name: $( $old_storage = $old_pov_mode )*; )* ) + $( #[ $( $attributes )+ ] )* $name $( $rest )* } @@ -385,6 +429,7 @@ macro_rules! benchmarks_iter { ( $( $names:tt )* ) // This contains $( $( { $instance } )? $name:ident )* ( $( $names_extra:tt )* ) ( $( $names_skip_meta:tt )* ) + ( $( $pov_name:ident: $( $storage:path = $pov_mode:ident )*; )* ) $name:ident { $( $code:tt )* }: _ $(< $origin_type:ty>)? ( $origin:expr $( , $arg:expr )* ) verify $postcode:block $( $rest:tt )* @@ -396,6 +441,7 @@ macro_rules! benchmarks_iter { ( $( $names )* ) ( $( $names_extra )* ) ( $( $names_skip_meta )* ) + ( $( $pov_name: $( $storage = $pov_mode )*; )* ) $name { $( $code )* }: $name $(< $origin_type >)? ( $origin $( , $arg )* ) verify $postcode $( $rest )* @@ -409,6 +455,7 @@ macro_rules! benchmarks_iter { ( $( $names:tt )* ) ( $( $names_extra:tt )* ) ( $( $names_skip_meta:tt )* ) + ( $( $pov_name:ident: $( $storage:path = $pov_mode:ident )*; )* ) $name:ident { $( $code:tt )* }: $dispatch:ident $(<$origin_type:ty>)? ( $origin:expr $( , $arg:expr )* ) verify $postcode:block $( $rest:tt )* @@ -421,6 +468,7 @@ macro_rules! benchmarks_iter { ( $( $names )* ) ( $( $names_extra )* ) ( $( $names_skip_meta )* ) + ( $( $pov_name: $( $storage = $pov_mode )*; )* ) $name { $( $code )* let __call = Call::< @@ -455,6 +503,7 @@ macro_rules! benchmarks_iter { ( $( $names:tt )* ) ( $( $names_extra:tt )* ) ( $( $names_skip_meta:tt )* ) + ( $( $pov_name:ident: $( $storage:path = $pov_mode:ident )*; )* ) $name:ident { $( $code:tt )* }: $eval:block verify $postcode:block $( $rest:tt )* @@ -483,6 +532,7 @@ macro_rules! benchmarks_iter { ( $( $names )* { $( $instance )? } $name ) ( $( $names_extra )* ) ( $( $names_skip_meta )* ) + ( $( $pov_name: $( $storage = $pov_mode )*; )* ) $( $rest )* ); }; @@ -494,6 +544,7 @@ macro_rules! benchmarks_iter { ( $( $names:tt )* ) ( $( $names_extra:tt )* ) ( $( $names_skip_meta:tt )* ) + ( $( $pov_name:ident: $( $storage:path = $pov_mode:ident )*; )* ) ) => { $crate::selected_benchmark!( { $( $where_clause)* } @@ -506,6 +557,7 @@ macro_rules! benchmarks_iter { ( $( $names )* ) ( $( $names_extra ),* ) ( $( $names_skip_meta ),* ) + ( $( $pov_name: $( $storage = $pov_mode )*; )* ) ); $crate::impl_test_function!( ( $( $names )* ) @@ -525,6 +577,7 @@ macro_rules! benchmarks_iter { ( $( $names:tt )* ) ( $( $names_extra:tt )* ) ( $( $names_skip_meta:tt )* ) + ( $( $pov_name:ident: $( $storage:path = $pov_mode:ident )*; )* ) ) => { $crate::selected_benchmark!( { $( $where_clause)* } @@ -537,6 +590,7 @@ macro_rules! benchmarks_iter { ( $( $names )* ) ( $( $names_extra ),* ) ( $( $names_skip_meta ),* ) + ( $( $pov_name: $( $storage = $pov_mode )*; )* ) ); }; // add verify block to _() format @@ -547,6 +601,7 @@ macro_rules! benchmarks_iter { ( $( $names:tt )* ) ( $( $names_extra:tt )* ) ( $( $names_skip_meta:tt )* ) + ( $( $pov_name:ident: $( $storage:path = $pov_mode:ident )*; )* ) $name:ident { $( $code:tt )* }: _ $(<$origin_type:ty>)? ( $origin:expr $( , $arg:expr )* ) $( $rest:tt )* ) => { @@ -557,6 +612,7 @@ macro_rules! benchmarks_iter { ( $( $names )* ) ( $( $names_extra )* ) ( $( $names_skip_meta )* ) + ( $( $pov_name: $( $storage = $pov_mode )*; )* ) $name { $( $code )* }: _ $(<$origin_type>)? ( $origin $( , $arg )* ) verify { } $( $rest )* @@ -570,6 +626,7 @@ macro_rules! benchmarks_iter { ( $( $names:tt )* ) ( $( $names_extra:tt )* ) ( $( $names_skip_meta:tt )* ) + ( $( $pov_name:ident: $( $storage:path = $pov_mode:ident )*; )* ) $name:ident { $( $code:tt )* }: $dispatch:ident $(<$origin_type:ty>)? ( $origin:expr $( , $arg:expr )* ) $( $rest:tt )* ) => { @@ -580,6 +637,7 @@ macro_rules! benchmarks_iter { ( $( $names )* ) ( $( $names_extra )* ) ( $( $names_skip_meta )* ) + ( $( $pov_name: $( $storage = $pov_mode )*; )* ) $name { $( $code )* }: $dispatch $(<$origin_type>)? ( $origin $( , $arg )* ) verify { } $( $rest )* @@ -593,6 +651,7 @@ macro_rules! benchmarks_iter { ( $( $names:tt )* ) ( $( $names_extra:tt )* ) ( $( $names_skip_meta:tt )* ) + ( $( $pov_name:ident: $( $storage:path = $pov_mode:ident )*; )* ) $name:ident { $( $code:tt )* }: $(<$origin_type:ty>)? $eval:block $( $rest:tt )* ) => { @@ -603,6 +662,7 @@ macro_rules! benchmarks_iter { ( $( $names )* ) ( $( $names_extra )* ) ( $( $names_skip_meta )* ) + ( $( $pov_name: $( $storage = $pov_mode )*; )* ) $name { $( $code )* }: $(<$origin_type>)? $eval verify { } $( $rest )* @@ -981,6 +1041,7 @@ macro_rules! impl_benchmark { ( $( { $( $name_inst:ident )? } $name:ident )* ) ( $( $name_extra:ident ),* ) ( $( $name_skip_meta:ident ),* ) + ( $( $pov_name:ident: $( $storage:path = $pov_mode:ident )*; )* ) ) => { // We only need to implement benchmarks for the runtime-benchmarks feature or testing. #[cfg(any(feature = "runtime-benchmarks", test))] @@ -989,23 +1050,37 @@ macro_rules! impl_benchmark { where T: frame_system::Config, $( $where_clause )* { fn benchmarks(extra: bool) -> $crate::Vec<$crate::BenchmarkMetadata> { + $($crate::validate_pov_mode!( + $pov_name: $( $storage = $pov_mode )*; + );)* let mut all_names = $crate::vec![ $( stringify!($name).as_ref() ),* ]; if !extra { let extra = [ $( stringify!($name_extra).as_ref() ),* ]; all_names.retain(|x| !extra.contains(x)); } + let pov_modes: $crate::Vec<($crate::Vec, $crate::Vec<($crate::Vec, $crate::Vec)>)> = $crate::vec![ + $( + (stringify!($pov_name).as_bytes().to_vec(), + $crate::vec![ + $( ( stringify!($storage).as_bytes().to_vec(), + stringify!($pov_mode).as_bytes().to_vec() ), )* + ]), + )* + ]; all_names.into_iter().map(|benchmark| { let selected_benchmark = match benchmark { $( stringify!($name) => SelectedBenchmark::$name, )* _ => panic!("all benchmarks should be selectable"), }; + let name = benchmark.as_bytes().to_vec(); let components = < SelectedBenchmark as $crate::BenchmarkingSetup >::components(&selected_benchmark); $crate::BenchmarkMetadata { - name: benchmark.as_bytes().to_vec(), + name: name.clone(), components, + pov_modes: pov_modes.iter().find(|p| p.0 == name).map(|p| p.1.clone()).unwrap_or_default(), } }).collect::<$crate::Vec<_>>() } @@ -1037,8 +1112,13 @@ macro_rules! impl_benchmark { $crate::frame_support::storage::transactional::TRANSACTION_LEVEL_KEY.into() ); whitelist.push(transactional_layer_key); + // Whitelist the `:extrinsic_index`. + let extrinsic_index = $crate::TrackedStorageKey::new( + $crate::well_known_keys::EXTRINSIC_INDEX.into() + ); + whitelist.push(extrinsic_index); - $crate::benchmarking::set_whitelist(whitelist); + $crate::benchmarking::set_whitelist(whitelist.clone()); let mut results: $crate::Vec<$crate::BenchmarkResult> = $crate::Vec::new(); @@ -1062,15 +1142,22 @@ macro_rules! impl_benchmark { // This will enable worst case scenario for reading from the database. $crate::benchmarking::commit_db(); + // Access all whitelisted keys to get them into the proof recorder since the + // recorder does now have a whitelist. + for key in &whitelist { + $crate::frame_support::storage::unhashed::get_raw(&key.key); + } + // Reset the read/write counter so we don't count operations in the setup process. $crate::benchmarking::reset_read_write_count(); // Time the extrinsic logic. $crate::log::trace!( target: "benchmark", - "Start Benchmark: {} ({:?})", + "Start Benchmark: {} ({:?}) verify {}", extrinsic, - c + c, + verify ); let start_pov = $crate::benchmarking::proof_size(); @@ -1099,6 +1186,10 @@ macro_rules! impl_benchmark { target: "benchmark", "Read/Write Count {:?}", read_write_count ); + $crate::log::trace!( + target: "benchmark", + "Proof sizes: before {:?} after {:?} diff {}", &start_pov, &end_pov, &diff_pov + ); // Time the storage root recalculation. let start_storage_root = $crate::benchmarking::current_time(); @@ -1421,6 +1512,37 @@ macro_rules! impl_benchmark_test_suite { } } +/// Validates the passed `pov_mode`s. +/// +/// Checks that: +/// - a top-level `ignored` is exclusive +/// - all modes are valid +#[macro_export] +macro_rules! validate_pov_mode { + () => {}; + ( $_bench:ident: ; ) => { }; + ( $_bench:ident: $_car:path = Ignored ; ) => { }; + ( $bench:ident: $_car:path = Ignored $( $storage:path = $_pov_mode:ident )+; ) => { + compile_error!( + concat!(concat!("`pov_mode = Ignored` is exclusive. Please remove the attribute from keys: ", $( stringify!($storage) )+), " on benchmark '", stringify!($bench), "'")); + }; + ( $bench:ident: $car:path = Measured $( $storage:path = $pov_mode:ident )*; ) => { + $crate::validate_pov_mode!( + $bench: $( $storage = $pov_mode )*; + ); + }; + ( $bench:ident: $car:path = MaxEncodedLen $( $storage:path = $pov_mode:ident )*; ) => { + $crate::validate_pov_mode!( + $bench: $( $storage = $pov_mode )*; + ); + }; + ( $bench:ident: $key:path = $unknown:ident $( $_storage:path = $_pov_mode:ident )*; ) => { + compile_error!( + concat!("Unknown pov_mode '", stringify!($unknown) ,"' for benchmark '", stringify!($bench), "' on key '", stringify!($key), "'. Must be one of: Ignored, Measured, MaxEncodedLen") + ); + }; +} + // Takes all arguments from `impl_benchmark_test_suite` and three additional arguments. // // Can be configured to generate one #[test] fn per bench case or diff --git a/frame/benchmarking/src/utils.rs b/frame/benchmarking/src/utils.rs index 654b1c34c..6ad9ac7bb 100644 --- a/frame/benchmarking/src/utils.rs +++ b/frame/benchmarking/src/utils.rs @@ -227,6 +227,7 @@ pub struct BenchmarkList { pub struct BenchmarkMetadata { pub name: Vec, pub components: Vec<(BenchmarkParameter, u32, u32)>, + pub pov_modes: Vec<(Vec, Vec)>, } sp_api::decl_runtime_apis! { diff --git a/frame/benchmarking/src/weights.rs b/frame/benchmarking/src/weights.rs index 5e5a2e7ee..dea959471 100644 --- a/frame/benchmarking/src/weights.rs +++ b/frame/benchmarking/src/weights.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2022 Parity Technologies (UK) Ltd. +// Copyright (C) 2023 Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); @@ -18,7 +18,8 @@ //! Autogenerated weights for frame_benchmarking //! //! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev -//! DATE: 2022-11-07, STEPS: `50`, REPEAT: 20, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! DATE: 2023-01-24, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! WORST CASE MAP SIZE: `1000000` //! HOSTNAME: `bm2`, CPU: `Intel(R) Core(TM) i7-7700K CPU @ 4.20GHz` //! EXECUTION: Some(Wasm), WASM-EXECUTION: Compiled, CHAIN: Some("dev"), DB CACHE: 1024 @@ -51,10 +52,8 @@ pub trait WeightInfo { fn subtraction(i: u32, ) -> Weight; fn multiplication(i: u32, ) -> Weight; fn division(i: u32, ) -> Weight; - fn hashing(i: u32, ) -> Weight; + fn hashing() -> Weight; fn sr25519_verification(i: u32, ) -> Weight; - fn storage_read(i: u32, ) -> Weight; - fn storage_write(i: u32, ) -> Weight; } /// Weights for frame_benchmarking using the Substrate node and recommended hardware. @@ -62,53 +61,52 @@ pub struct SubstrateWeight(PhantomData); impl WeightInfo for SubstrateWeight { /// The range of component `i` is `[0, 1000000]`. fn addition(_i: u32, ) -> Weight { - // Minimum execution time: 108 nanoseconds. - Weight::from_ref_time(137_610 as u64) + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 138 nanoseconds. + Weight::from_ref_time(199_805) } /// The range of component `i` is `[0, 1000000]`. fn subtraction(_i: u32, ) -> Weight { - // Minimum execution time: 104 nanoseconds. - Weight::from_ref_time(133_508 as u64) + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 142 nanoseconds. + Weight::from_ref_time(201_435) } /// The range of component `i` is `[0, 1000000]`. fn multiplication(_i: u32, ) -> Weight { - // Minimum execution time: 110 nanoseconds. - Weight::from_ref_time(140_230 as u64) + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 138 nanoseconds. + Weight::from_ref_time(207_037) } /// The range of component `i` is `[0, 1000000]`. fn division(_i: u32, ) -> Weight { - // Minimum execution time: 96 nanoseconds. - Weight::from_ref_time(136_059 as u64) - } - /// The range of component `i` is `[0, 100]`. - fn hashing(_i: u32, ) -> Weight { - // Minimum execution time: 21_804_747 nanoseconds. - Weight::from_ref_time(22_013_681_386 as u64) + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 151 nanoseconds. + Weight::from_ref_time(205_150) + } + fn hashing() -> Weight { + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 21_950_884 nanoseconds. + Weight::from_ref_time(21_994_001_000) } /// The range of component `i` is `[0, 100]`. fn sr25519_verification(i: u32, ) -> Weight { - // Minimum execution time: 136 nanoseconds. - Weight::from_ref_time(156_000 as u64) - // Standard Error: 4_531 - .saturating_add(Weight::from_ref_time(46_817_640 as u64).saturating_mul(i as u64)) - } - // Storage: Skipped Metadata (r:0 w:0) - /// The range of component `i` is `[0, 1000]`. - fn storage_read(i: u32, ) -> Weight { - // Minimum execution time: 125 nanoseconds. - Weight::from_ref_time(135_000 as u64) - // Standard Error: 3_651 - .saturating_add(Weight::from_ref_time(2_021_172 as u64).saturating_mul(i as u64)) - .saturating_add(T::DbWeight::get().reads((1 as u64).saturating_mul(i as u64))) - } - // Storage: Skipped Metadata (r:0 w:0) - /// The range of component `i` is `[0, 1000]`. - fn storage_write(i: u32, ) -> Weight { - // Minimum execution time: 120 nanoseconds. - Weight::from_ref_time(131_000 as u64) - // Standard Error: 348 - .saturating_add(Weight::from_ref_time(377_243 as u64).saturating_mul(i as u64)) - .saturating_add(T::DbWeight::get().writes((1 as u64).saturating_mul(i as u64))) + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 168 nanoseconds. + Weight::from_ref_time(1_680_898) + // Standard Error: 10_291 + .saturating_add(Weight::from_ref_time(46_867_301).saturating_mul(i.into())) } } @@ -116,52 +114,51 @@ impl WeightInfo for SubstrateWeight { impl WeightInfo for () { /// The range of component `i` is `[0, 1000000]`. fn addition(_i: u32, ) -> Weight { - // Minimum execution time: 108 nanoseconds. - Weight::from_ref_time(137_610 as u64) + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 138 nanoseconds. + Weight::from_ref_time(199_805) } /// The range of component `i` is `[0, 1000000]`. fn subtraction(_i: u32, ) -> Weight { - // Minimum execution time: 104 nanoseconds. - Weight::from_ref_time(133_508 as u64) + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 142 nanoseconds. + Weight::from_ref_time(201_435) } /// The range of component `i` is `[0, 1000000]`. fn multiplication(_i: u32, ) -> Weight { - // Minimum execution time: 110 nanoseconds. - Weight::from_ref_time(140_230 as u64) + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 138 nanoseconds. + Weight::from_ref_time(207_037) } /// The range of component `i` is `[0, 1000000]`. fn division(_i: u32, ) -> Weight { - // Minimum execution time: 96 nanoseconds. - Weight::from_ref_time(136_059 as u64) - } - /// The range of component `i` is `[0, 100]`. - fn hashing(_i: u32, ) -> Weight { - // Minimum execution time: 21_804_747 nanoseconds. - Weight::from_ref_time(22_013_681_386 as u64) + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 151 nanoseconds. + Weight::from_ref_time(205_150) + } + fn hashing() -> Weight { + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 21_950_884 nanoseconds. + Weight::from_ref_time(21_994_001_000) } /// The range of component `i` is `[0, 100]`. fn sr25519_verification(i: u32, ) -> Weight { - // Minimum execution time: 136 nanoseconds. - Weight::from_ref_time(156_000 as u64) - // Standard Error: 4_531 - .saturating_add(Weight::from_ref_time(46_817_640 as u64).saturating_mul(i as u64)) - } - // Storage: Skipped Metadata (r:0 w:0) - /// The range of component `i` is `[0, 1000]`. - fn storage_read(i: u32, ) -> Weight { - // Minimum execution time: 125 nanoseconds. - Weight::from_ref_time(135_000 as u64) - // Standard Error: 3_651 - .saturating_add(Weight::from_ref_time(2_021_172 as u64).saturating_mul(i as u64)) - .saturating_add(RocksDbWeight::get().reads((1 as u64).saturating_mul(i as u64))) - } - // Storage: Skipped Metadata (r:0 w:0) - /// The range of component `i` is `[0, 1000]`. - fn storage_write(i: u32, ) -> Weight { - // Minimum execution time: 120 nanoseconds. - Weight::from_ref_time(131_000 as u64) - // Standard Error: 348 - .saturating_add(Weight::from_ref_time(377_243 as u64).saturating_mul(i as u64)) - .saturating_add(RocksDbWeight::get().writes((1 as u64).saturating_mul(i as u64))) + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 168 nanoseconds. + Weight::from_ref_time(1_680_898) + // Standard Error: 10_291 + .saturating_add(Weight::from_ref_time(46_867_301).saturating_mul(i.into())) } } diff --git a/frame/bounties/src/weights.rs b/frame/bounties/src/weights.rs index 34e78bfa5..1ae8ab298 100644 --- a/frame/bounties/src/weights.rs +++ b/frame/bounties/src/weights.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2022 Parity Technologies (UK) Ltd. +// Copyright (C) 2023 Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); @@ -18,25 +18,25 @@ //! Autogenerated weights for pallet_bounties //! //! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev -//! DATE: 2022-11-30, STEPS: `50`, REPEAT: 20, LOW RANGE: `[]`, HIGH RANGE: `[]` -//! HOSTNAME: `bm3`, CPU: `Intel(R) Core(TM) i7-7700K CPU @ 4.20GHz` +//! DATE: 2023-01-24, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! WORST CASE MAP SIZE: `1000000` +//! HOSTNAME: `bm2`, CPU: `Intel(R) Core(TM) i7-7700K CPU @ 4.20GHz` //! EXECUTION: Some(Wasm), WASM-EXECUTION: Compiled, CHAIN: Some("dev"), DB CACHE: 1024 // Executed Command: -// /home/benchbot/cargo_target_dir/production/substrate +// ./target/production/substrate // benchmark // pallet +// --chain=dev // --steps=50 // --repeat=20 +// --pallet=pallet_bounties // --extrinsic=* // --execution=wasm // --wasm-execution=compiled // --heap-pages=4096 -// --json-file=/var/lib/gitlab-runner/builds/zyw4fam_/0/parity/mirrors/substrate/.git/.artifacts/bench.json -// --pallet=pallet_bounties -// --chain=dev -// --header=./HEADER-APACHE2 // --output=./frame/bounties/src/weights.rs +// --header=./HEADER-APACHE2 // --template=./.maintain/frame-weight-template.hbs #![cfg_attr(rustfmt, rustfmt_skip)] @@ -64,214 +64,340 @@ pub trait WeightInfo { /// Weights for pallet_bounties using the Substrate node and recommended hardware. pub struct SubstrateWeight(PhantomData); impl WeightInfo for SubstrateWeight { - // Storage: Bounties BountyCount (r:1 w:1) - // Storage: System Account (r:1 w:1) - // Storage: Bounties BountyDescriptions (r:0 w:1) - // Storage: Bounties Bounties (r:0 w:1) + /// Storage: Bounties BountyCount (r:1 w:1) + /// Proof: Bounties BountyCount (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) + /// Storage: System Account (r:1 w:1) + /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) + /// Storage: Bounties BountyDescriptions (r:0 w:1) + /// Proof: Bounties BountyDescriptions (max_values: None, max_size: Some(314), added: 2789, mode: MaxEncodedLen) + /// Storage: Bounties Bounties (r:0 w:1) + /// Proof: Bounties Bounties (max_values: None, max_size: Some(177), added: 2652, mode: MaxEncodedLen) /// The range of component `d` is `[0, 300]`. fn propose_bounty(d: u32, ) -> Weight { - // Minimum execution time: 33_366 nanoseconds. - Weight::from_ref_time(34_444_773) - // Standard Error: 1_161 - .saturating_add(Weight::from_ref_time(4_723).saturating_mul(d.into())) - .saturating_add(T::DbWeight::get().reads(2)) - .saturating_add(T::DbWeight::get().writes(4)) + // Proof Size summary in bytes: + // Measured: `308` + // Estimated: `3102` + // Minimum execution time: 22_787 nanoseconds. + Weight::from_parts(23_898_632, 3102) + // Standard Error: 141 + .saturating_add(Weight::from_ref_time(568).saturating_mul(d.into())) + .saturating_add(T::DbWeight::get().reads(2_u64)) + .saturating_add(T::DbWeight::get().writes(4_u64)) } - // Storage: Bounties Bounties (r:1 w:1) - // Storage: Bounties BountyApprovals (r:1 w:1) + /// Storage: Bounties Bounties (r:1 w:1) + /// Proof: Bounties Bounties (max_values: None, max_size: Some(177), added: 2652, mode: MaxEncodedLen) + /// Storage: Bounties BountyApprovals (r:1 w:1) + /// Proof: Bounties BountyApprovals (max_values: Some(1), max_size: Some(402), added: 897, mode: MaxEncodedLen) fn approve_bounty() -> Weight { - // Minimum execution time: 14_478 nanoseconds. - Weight::from_ref_time(14_763_000) - .saturating_add(T::DbWeight::get().reads(2)) - .saturating_add(T::DbWeight::get().writes(2)) + // Proof Size summary in bytes: + // Measured: `400` + // Estimated: `3549` + // Minimum execution time: 10_526 nanoseconds. + Weight::from_parts(10_729_000, 3549) + .saturating_add(T::DbWeight::get().reads(2_u64)) + .saturating_add(T::DbWeight::get().writes(2_u64)) } - // Storage: Bounties Bounties (r:1 w:1) + /// Storage: Bounties Bounties (r:1 w:1) + /// Proof: Bounties Bounties (max_values: None, max_size: Some(177), added: 2652, mode: MaxEncodedLen) fn propose_curator() -> Weight { - // Minimum execution time: 13_376 nanoseconds. - Weight::from_ref_time(13_705_000) - .saturating_add(T::DbWeight::get().reads(1)) - .saturating_add(T::DbWeight::get().writes(1)) + // Proof Size summary in bytes: + // Measured: `420` + // Estimated: `2652` + // Minimum execution time: 9_193 nanoseconds. + Weight::from_parts(9_455_000, 2652) + .saturating_add(T::DbWeight::get().reads(1_u64)) + .saturating_add(T::DbWeight::get().writes(1_u64)) } - // Storage: Bounties Bounties (r:1 w:1) - // Storage: System Account (r:1 w:1) + /// Storage: Bounties Bounties (r:1 w:1) + /// Proof: Bounties Bounties (max_values: None, max_size: Some(177), added: 2652, mode: MaxEncodedLen) + /// Storage: System Account (r:1 w:1) + /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) fn unassign_curator() -> Weight { - // Minimum execution time: 38_072 nanoseconds. - Weight::from_ref_time(38_676_000) - .saturating_add(T::DbWeight::get().reads(2)) - .saturating_add(T::DbWeight::get().writes(2)) + // Proof Size summary in bytes: + // Measured: `628` + // Estimated: `5255` + // Minimum execution time: 22_592 nanoseconds. + Weight::from_parts(22_952_000, 5255) + .saturating_add(T::DbWeight::get().reads(2_u64)) + .saturating_add(T::DbWeight::get().writes(2_u64)) } - // Storage: Bounties Bounties (r:1 w:1) - // Storage: System Account (r:1 w:1) + /// Storage: Bounties Bounties (r:1 w:1) + /// Proof: Bounties Bounties (max_values: None, max_size: Some(177), added: 2652, mode: MaxEncodedLen) + /// Storage: System Account (r:1 w:1) + /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) fn accept_curator() -> Weight { - // Minimum execution time: 33_207 nanoseconds. - Weight::from_ref_time(34_415_000) - .saturating_add(T::DbWeight::get().reads(2)) - .saturating_add(T::DbWeight::get().writes(2)) + // Proof Size summary in bytes: + // Measured: `624` + // Estimated: `5255` + // Minimum execution time: 20_920 nanoseconds. + Weight::from_parts(21_369_000, 5255) + .saturating_add(T::DbWeight::get().reads(2_u64)) + .saturating_add(T::DbWeight::get().writes(2_u64)) } - // Storage: Bounties Bounties (r:1 w:1) - // Storage: ChildBounties ParentChildBounties (r:1 w:0) + /// Storage: Bounties Bounties (r:1 w:1) + /// Proof: Bounties Bounties (max_values: None, max_size: Some(177), added: 2652, mode: MaxEncodedLen) + /// Storage: ChildBounties ParentChildBounties (r:1 w:0) + /// Proof: ChildBounties ParentChildBounties (max_values: None, max_size: Some(16), added: 2491, mode: MaxEncodedLen) fn award_bounty() -> Weight { - // Minimum execution time: 28_033 nanoseconds. - Weight::from_ref_time(28_343_000) - .saturating_add(T::DbWeight::get().reads(2)) - .saturating_add(T::DbWeight::get().writes(1)) + // Proof Size summary in bytes: + // Measured: `570` + // Estimated: `5143` + // Minimum execution time: 17_853 nanoseconds. + Weight::from_parts(18_280_000, 5143) + .saturating_add(T::DbWeight::get().reads(2_u64)) + .saturating_add(T::DbWeight::get().writes(1_u64)) } - // Storage: Bounties Bounties (r:1 w:1) - // Storage: System Account (r:3 w:3) - // Storage: ChildBounties ChildrenCuratorFees (r:1 w:1) - // Storage: Bounties BountyDescriptions (r:0 w:1) + /// Storage: Bounties Bounties (r:1 w:1) + /// Proof: Bounties Bounties (max_values: None, max_size: Some(177), added: 2652, mode: MaxEncodedLen) + /// Storage: System Account (r:3 w:3) + /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) + /// Storage: ChildBounties ChildrenCuratorFees (r:1 w:1) + /// Proof: ChildBounties ChildrenCuratorFees (max_values: None, max_size: Some(28), added: 2503, mode: MaxEncodedLen) + /// Storage: Bounties BountyDescriptions (r:0 w:1) + /// Proof: Bounties BountyDescriptions (max_values: None, max_size: Some(314), added: 2789, mode: MaxEncodedLen) fn claim_bounty() -> Weight { - // Minimum execution time: 75_855 nanoseconds. - Weight::from_ref_time(76_318_000) - .saturating_add(T::DbWeight::get().reads(5)) - .saturating_add(T::DbWeight::get().writes(6)) + // Proof Size summary in bytes: + // Measured: `998` + // Estimated: `12964` + // Minimum execution time: 67_538 nanoseconds. + Weight::from_parts(67_974_000, 12964) + .saturating_add(T::DbWeight::get().reads(5_u64)) + .saturating_add(T::DbWeight::get().writes(6_u64)) } - // Storage: Bounties Bounties (r:1 w:1) - // Storage: ChildBounties ParentChildBounties (r:1 w:0) - // Storage: System Account (r:1 w:1) - // Storage: Bounties BountyDescriptions (r:0 w:1) + /// Storage: Bounties Bounties (r:1 w:1) + /// Proof: Bounties Bounties (max_values: None, max_size: Some(177), added: 2652, mode: MaxEncodedLen) + /// Storage: ChildBounties ParentChildBounties (r:1 w:0) + /// Proof: ChildBounties ParentChildBounties (max_values: None, max_size: Some(16), added: 2491, mode: MaxEncodedLen) + /// Storage: System Account (r:1 w:1) + /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) + /// Storage: Bounties BountyDescriptions (r:0 w:1) + /// Proof: Bounties BountyDescriptions (max_values: None, max_size: Some(314), added: 2789, mode: MaxEncodedLen) fn close_bounty_proposed() -> Weight { - // Minimum execution time: 41_955 nanoseconds. - Weight::from_ref_time(42_733_000) - .saturating_add(T::DbWeight::get().reads(3)) - .saturating_add(T::DbWeight::get().writes(3)) + // Proof Size summary in bytes: + // Measured: `646` + // Estimated: `7746` + // Minimum execution time: 28_380 nanoseconds. + Weight::from_parts(28_859_000, 7746) + .saturating_add(T::DbWeight::get().reads(3_u64)) + .saturating_add(T::DbWeight::get().writes(3_u64)) } - // Storage: Bounties Bounties (r:1 w:1) - // Storage: ChildBounties ParentChildBounties (r:1 w:0) - // Storage: System Account (r:2 w:2) - // Storage: Bounties BountyDescriptions (r:0 w:1) + /// Storage: Bounties Bounties (r:1 w:1) + /// Proof: Bounties Bounties (max_values: None, max_size: Some(177), added: 2652, mode: MaxEncodedLen) + /// Storage: ChildBounties ParentChildBounties (r:1 w:0) + /// Proof: ChildBounties ParentChildBounties (max_values: None, max_size: Some(16), added: 2491, mode: MaxEncodedLen) + /// Storage: System Account (r:2 w:2) + /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) + /// Storage: Bounties BountyDescriptions (r:0 w:1) + /// Proof: Bounties BountyDescriptions (max_values: None, max_size: Some(314), added: 2789, mode: MaxEncodedLen) fn close_bounty_active() -> Weight { - // Minimum execution time: 58_267 nanoseconds. - Weight::from_ref_time(59_604_000) - .saturating_add(T::DbWeight::get().reads(4)) - .saturating_add(T::DbWeight::get().writes(4)) + // Proof Size summary in bytes: + // Measured: `914` + // Estimated: `10349` + // Minimum execution time: 47_739 nanoseconds. + Weight::from_parts(48_388_000, 10349) + .saturating_add(T::DbWeight::get().reads(4_u64)) + .saturating_add(T::DbWeight::get().writes(4_u64)) } - // Storage: Bounties Bounties (r:1 w:1) + /// Storage: Bounties Bounties (r:1 w:1) + /// Proof: Bounties Bounties (max_values: None, max_size: Some(177), added: 2652, mode: MaxEncodedLen) fn extend_bounty_expiry() -> Weight { - // Minimum execution time: 24_893 nanoseconds. - Weight::from_ref_time(25_299_000) - .saturating_add(T::DbWeight::get().reads(1)) - .saturating_add(T::DbWeight::get().writes(1)) + // Proof Size summary in bytes: + // Measured: `456` + // Estimated: `2652` + // Minimum execution time: 14_188 nanoseconds. + Weight::from_parts(14_801_000, 2652) + .saturating_add(T::DbWeight::get().reads(1_u64)) + .saturating_add(T::DbWeight::get().writes(1_u64)) } - // Storage: Bounties BountyApprovals (r:1 w:1) - // Storage: Bounties Bounties (r:2 w:2) - // Storage: System Account (r:4 w:4) + /// Storage: Bounties BountyApprovals (r:1 w:1) + /// Proof: Bounties BountyApprovals (max_values: Some(1), max_size: Some(402), added: 897, mode: MaxEncodedLen) + /// Storage: Bounties Bounties (r:100 w:100) + /// Proof: Bounties Bounties (max_values: None, max_size: Some(177), added: 2652, mode: MaxEncodedLen) + /// Storage: System Account (r:200 w:200) + /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) /// The range of component `b` is `[0, 100]`. fn spend_funds(b: u32, ) -> Weight { - // Minimum execution time: 8_846 nanoseconds. - Weight::from_ref_time(20_166_004) - // Standard Error: 28_485 - .saturating_add(Weight::from_ref_time(26_712_253).saturating_mul(b.into())) - .saturating_add(T::DbWeight::get().reads(1)) + // Proof Size summary in bytes: + // Measured: `31 + b * (360 ±0)` + // Estimated: `897 + b * (7858 ±0)` + // Minimum execution time: 4_685 nanoseconds. + Weight::from_parts(9_932_840, 897) + // Standard Error: 14_301 + .saturating_add(Weight::from_ref_time(27_178_347).saturating_mul(b.into())) + .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(T::DbWeight::get().reads((3_u64).saturating_mul(b.into()))) - .saturating_add(T::DbWeight::get().writes(1)) + .saturating_add(T::DbWeight::get().writes(1_u64)) .saturating_add(T::DbWeight::get().writes((3_u64).saturating_mul(b.into()))) + .saturating_add(Weight::from_proof_size(7858).saturating_mul(b.into())) } } // For backwards compatibility and tests impl WeightInfo for () { - // Storage: Bounties BountyCount (r:1 w:1) - // Storage: System Account (r:1 w:1) - // Storage: Bounties BountyDescriptions (r:0 w:1) - // Storage: Bounties Bounties (r:0 w:1) + /// Storage: Bounties BountyCount (r:1 w:1) + /// Proof: Bounties BountyCount (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) + /// Storage: System Account (r:1 w:1) + /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) + /// Storage: Bounties BountyDescriptions (r:0 w:1) + /// Proof: Bounties BountyDescriptions (max_values: None, max_size: Some(314), added: 2789, mode: MaxEncodedLen) + /// Storage: Bounties Bounties (r:0 w:1) + /// Proof: Bounties Bounties (max_values: None, max_size: Some(177), added: 2652, mode: MaxEncodedLen) /// The range of component `d` is `[0, 300]`. fn propose_bounty(d: u32, ) -> Weight { - // Minimum execution time: 33_366 nanoseconds. - Weight::from_ref_time(34_444_773) - // Standard Error: 1_161 - .saturating_add(Weight::from_ref_time(4_723).saturating_mul(d.into())) - .saturating_add(RocksDbWeight::get().reads(2)) - .saturating_add(RocksDbWeight::get().writes(4)) + // Proof Size summary in bytes: + // Measured: `308` + // Estimated: `3102` + // Minimum execution time: 22_787 nanoseconds. + Weight::from_parts(23_898_632, 3102) + // Standard Error: 141 + .saturating_add(Weight::from_ref_time(568).saturating_mul(d.into())) + .saturating_add(RocksDbWeight::get().reads(2_u64)) + .saturating_add(RocksDbWeight::get().writes(4_u64)) } - // Storage: Bounties Bounties (r:1 w:1) - // Storage: Bounties BountyApprovals (r:1 w:1) + /// Storage: Bounties Bounties (r:1 w:1) + /// Proof: Bounties Bounties (max_values: None, max_size: Some(177), added: 2652, mode: MaxEncodedLen) + /// Storage: Bounties BountyApprovals (r:1 w:1) + /// Proof: Bounties BountyApprovals (max_values: Some(1), max_size: Some(402), added: 897, mode: MaxEncodedLen) fn approve_bounty() -> Weight { - // Minimum execution time: 14_478 nanoseconds. - Weight::from_ref_time(14_763_000) - .saturating_add(RocksDbWeight::get().reads(2)) - .saturating_add(RocksDbWeight::get().writes(2)) + // Proof Size summary in bytes: + // Measured: `400` + // Estimated: `3549` + // Minimum execution time: 10_526 nanoseconds. + Weight::from_parts(10_729_000, 3549) + .saturating_add(RocksDbWeight::get().reads(2_u64)) + .saturating_add(RocksDbWeight::get().writes(2_u64)) } - // Storage: Bounties Bounties (r:1 w:1) + /// Storage: Bounties Bounties (r:1 w:1) + /// Proof: Bounties Bounties (max_values: None, max_size: Some(177), added: 2652, mode: MaxEncodedLen) fn propose_curator() -> Weight { - // Minimum execution time: 13_376 nanoseconds. - Weight::from_ref_time(13_705_000) - .saturating_add(RocksDbWeight::get().reads(1)) - .saturating_add(RocksDbWeight::get().writes(1)) + // Proof Size summary in bytes: + // Measured: `420` + // Estimated: `2652` + // Minimum execution time: 9_193 nanoseconds. + Weight::from_parts(9_455_000, 2652) + .saturating_add(RocksDbWeight::get().reads(1_u64)) + .saturating_add(RocksDbWeight::get().writes(1_u64)) } - // Storage: Bounties Bounties (r:1 w:1) - // Storage: System Account (r:1 w:1) + /// Storage: Bounties Bounties (r:1 w:1) + /// Proof: Bounties Bounties (max_values: None, max_size: Some(177), added: 2652, mode: MaxEncodedLen) + /// Storage: System Account (r:1 w:1) + /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) fn unassign_curator() -> Weight { - // Minimum execution time: 38_072 nanoseconds. - Weight::from_ref_time(38_676_000) - .saturating_add(RocksDbWeight::get().reads(2)) - .saturating_add(RocksDbWeight::get().writes(2)) + // Proof Size summary in bytes: + // Measured: `628` + // Estimated: `5255` + // Minimum execution time: 22_592 nanoseconds. + Weight::from_parts(22_952_000, 5255) + .saturating_add(RocksDbWeight::get().reads(2_u64)) + .saturating_add(RocksDbWeight::get().writes(2_u64)) } - // Storage: Bounties Bounties (r:1 w:1) - // Storage: System Account (r:1 w:1) + /// Storage: Bounties Bounties (r:1 w:1) + /// Proof: Bounties Bounties (max_values: None, max_size: Some(177), added: 2652, mode: MaxEncodedLen) + /// Storage: System Account (r:1 w:1) + /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) fn accept_curator() -> Weight { - // Minimum execution time: 33_207 nanoseconds. - Weight::from_ref_time(34_415_000) - .saturating_add(RocksDbWeight::get().reads(2)) - .saturating_add(RocksDbWeight::get().writes(2)) + // Proof Size summary in bytes: + // Measured: `624` + // Estimated: `5255` + // Minimum execution time: 20_920 nanoseconds. + Weight::from_parts(21_369_000, 5255) + .saturating_add(RocksDbWeight::get().reads(2_u64)) + .saturating_add(RocksDbWeight::get().writes(2_u64)) } - // Storage: Bounties Bounties (r:1 w:1) - // Storage: ChildBounties ParentChildBounties (r:1 w:0) + /// Storage: Bounties Bounties (r:1 w:1) + /// Proof: Bounties Bounties (max_values: None, max_size: Some(177), added: 2652, mode: MaxEncodedLen) + /// Storage: ChildBounties ParentChildBounties (r:1 w:0) + /// Proof: ChildBounties ParentChildBounties (max_values: None, max_size: Some(16), added: 2491, mode: MaxEncodedLen) fn award_bounty() -> Weight { - // Minimum execution time: 28_033 nanoseconds. - Weight::from_ref_time(28_343_000) - .saturating_add(RocksDbWeight::get().reads(2)) - .saturating_add(RocksDbWeight::get().writes(1)) + // Proof Size summary in bytes: + // Measured: `570` + // Estimated: `5143` + // Minimum execution time: 17_853 nanoseconds. + Weight::from_parts(18_280_000, 5143) + .saturating_add(RocksDbWeight::get().reads(2_u64)) + .saturating_add(RocksDbWeight::get().writes(1_u64)) } - // Storage: Bounties Bounties (r:1 w:1) - // Storage: System Account (r:3 w:3) - // Storage: ChildBounties ChildrenCuratorFees (r:1 w:1) - // Storage: Bounties BountyDescriptions (r:0 w:1) + /// Storage: Bounties Bounties (r:1 w:1) + /// Proof: Bounties Bounties (max_values: None, max_size: Some(177), added: 2652, mode: MaxEncodedLen) + /// Storage: System Account (r:3 w:3) + /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) + /// Storage: ChildBounties ChildrenCuratorFees (r:1 w:1) + /// Proof: ChildBounties ChildrenCuratorFees (max_values: None, max_size: Some(28), added: 2503, mode: MaxEncodedLen) + /// Storage: Bounties BountyDescriptions (r:0 w:1) + /// Proof: Bounties BountyDescriptions (max_values: None, max_size: Some(314), added: 2789, mode: MaxEncodedLen) fn claim_bounty() -> Weight { - // Minimum execution time: 75_855 nanoseconds. - Weight::from_ref_time(76_318_000) - .saturating_add(RocksDbWeight::get().reads(5)) - .saturating_add(RocksDbWeight::get().writes(6)) + // Proof Size summary in bytes: + // Measured: `998` + // Estimated: `12964` + // Minimum execution time: 67_538 nanoseconds. + Weight::from_parts(67_974_000, 12964) + .saturating_add(RocksDbWeight::get().reads(5_u64)) + .saturating_add(RocksDbWeight::get().writes(6_u64)) } - // Storage: Bounties Bounties (r:1 w:1) - // Storage: ChildBounties ParentChildBounties (r:1 w:0) - // Storage: System Account (r:1 w:1) - // Storage: Bounties BountyDescriptions (r:0 w:1) + /// Storage: Bounties Bounties (r:1 w:1) + /// Proof: Bounties Bounties (max_values: None, max_size: Some(177), added: 2652, mode: MaxEncodedLen) + /// Storage: ChildBounties ParentChildBounties (r:1 w:0) + /// Proof: ChildBounties ParentChildBounties (max_values: None, max_size: Some(16), added: 2491, mode: MaxEncodedLen) + /// Storage: System Account (r:1 w:1) + /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) + /// Storage: Bounties BountyDescriptions (r:0 w:1) + /// Proof: Bounties BountyDescriptions (max_values: None, max_size: Some(314), added: 2789, mode: MaxEncodedLen) fn close_bounty_proposed() -> Weight { - // Minimum execution time: 41_955 nanoseconds. - Weight::from_ref_time(42_733_000) - .saturating_add(RocksDbWeight::get().reads(3)) - .saturating_add(RocksDbWeight::get().writes(3)) + // Proof Size summary in bytes: + // Measured: `646` + // Estimated: `7746` + // Minimum execution time: 28_380 nanoseconds. + Weight::from_parts(28_859_000, 7746) + .saturating_add(RocksDbWeight::get().reads(3_u64)) + .saturating_add(RocksDbWeight::get().writes(3_u64)) } - // Storage: Bounties Bounties (r:1 w:1) - // Storage: ChildBounties ParentChildBounties (r:1 w:0) - // Storage: System Account (r:2 w:2) - // Storage: Bounties BountyDescriptions (r:0 w:1) + /// Storage: Bounties Bounties (r:1 w:1) + /// Proof: Bounties Bounties (max_values: None, max_size: Some(177), added: 2652, mode: MaxEncodedLen) + /// Storage: ChildBounties ParentChildBounties (r:1 w:0) + /// Proof: ChildBounties ParentChildBounties (max_values: None, max_size: Some(16), added: 2491, mode: MaxEncodedLen) + /// Storage: System Account (r:2 w:2) + /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) + /// Storage: Bounties BountyDescriptions (r:0 w:1) + /// Proof: Bounties BountyDescriptions (max_values: None, max_size: Some(314), added: 2789, mode: MaxEncodedLen) fn close_bounty_active() -> Weight { - // Minimum execution time: 58_267 nanoseconds. - Weight::from_ref_time(59_604_000) - .saturating_add(RocksDbWeight::get().reads(4)) - .saturating_add(RocksDbWeight::get().writes(4)) + // Proof Size summary in bytes: + // Measured: `914` + // Estimated: `10349` + // Minimum execution time: 47_739 nanoseconds. + Weight::from_parts(48_388_000, 10349) + .saturating_add(RocksDbWeight::get().reads(4_u64)) + .saturating_add(RocksDbWeight::get().writes(4_u64)) } - // Storage: Bounties Bounties (r:1 w:1) + /// Storage: Bounties Bounties (r:1 w:1) + /// Proof: Bounties Bounties (max_values: None, max_size: Some(177), added: 2652, mode: MaxEncodedLen) fn extend_bounty_expiry() -> Weight { - // Minimum execution time: 24_893 nanoseconds. - Weight::from_ref_time(25_299_000) - .saturating_add(RocksDbWeight::get().reads(1)) - .saturating_add(RocksDbWeight::get().writes(1)) + // Proof Size summary in bytes: + // Measured: `456` + // Estimated: `2652` + // Minimum execution time: 14_188 nanoseconds. + Weight::from_parts(14_801_000, 2652) + .saturating_add(RocksDbWeight::get().reads(1_u64)) + .saturating_add(RocksDbWeight::get().writes(1_u64)) } - // Storage: Bounties BountyApprovals (r:1 w:1) - // Storage: Bounties Bounties (r:2 w:2) - // Storage: System Account (r:4 w:4) + /// Storage: Bounties BountyApprovals (r:1 w:1) + /// Proof: Bounties BountyApprovals (max_values: Some(1), max_size: Some(402), added: 897, mode: MaxEncodedLen) + /// Storage: Bounties Bounties (r:100 w:100) + /// Proof: Bounties Bounties (max_values: None, max_size: Some(177), added: 2652, mode: MaxEncodedLen) + /// Storage: System Account (r:200 w:200) + /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) /// The range of component `b` is `[0, 100]`. fn spend_funds(b: u32, ) -> Weight { - // Minimum execution time: 8_846 nanoseconds. - Weight::from_ref_time(20_166_004) - // Standard Error: 28_485 - .saturating_add(Weight::from_ref_time(26_712_253).saturating_mul(b.into())) - .saturating_add(RocksDbWeight::get().reads(1)) + // Proof Size summary in bytes: + // Measured: `31 + b * (360 ±0)` + // Estimated: `897 + b * (7858 ±0)` + // Minimum execution time: 4_685 nanoseconds. + Weight::from_parts(9_932_840, 897) + // Standard Error: 14_301 + .saturating_add(Weight::from_ref_time(27_178_347).saturating_mul(b.into())) + .saturating_add(RocksDbWeight::get().reads(1_u64)) .saturating_add(RocksDbWeight::get().reads((3_u64).saturating_mul(b.into()))) - .saturating_add(RocksDbWeight::get().writes(1)) + .saturating_add(RocksDbWeight::get().writes(1_u64)) .saturating_add(RocksDbWeight::get().writes((3_u64).saturating_mul(b.into()))) + .saturating_add(Weight::from_proof_size(7858).saturating_mul(b.into())) } } diff --git a/frame/child-bounties/src/weights.rs b/frame/child-bounties/src/weights.rs index 235c84320..77ab24488 100644 --- a/frame/child-bounties/src/weights.rs +++ b/frame/child-bounties/src/weights.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2022 Parity Technologies (UK) Ltd. +// Copyright (C) 2023 Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); @@ -18,7 +18,8 @@ //! Autogenerated weights for pallet_child_bounties //! //! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev -//! DATE: 2022-11-07, STEPS: `50`, REPEAT: 20, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! DATE: 2023-01-24, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! WORST CASE MAP SIZE: `1000000` //! HOSTNAME: `bm2`, CPU: `Intel(R) Core(TM) i7-7700K CPU @ 4.20GHz` //! EXECUTION: Some(Wasm), WASM-EXECUTION: Compiled, CHAIN: Some("dev"), DB CACHE: 1024 @@ -60,176 +61,290 @@ pub trait WeightInfo { /// Weights for pallet_child_bounties using the Substrate node and recommended hardware. pub struct SubstrateWeight(PhantomData); impl WeightInfo for SubstrateWeight { - // Storage: ChildBounties ParentChildBounties (r:1 w:1) - // Storage: Bounties Bounties (r:1 w:0) - // Storage: System Account (r:2 w:2) - // Storage: ChildBounties ChildBountyCount (r:1 w:1) - // Storage: ChildBounties ChildBountyDescriptions (r:0 w:1) - // Storage: ChildBounties ChildBounties (r:0 w:1) + /// Storage: ChildBounties ParentChildBounties (r:1 w:1) + /// Proof: ChildBounties ParentChildBounties (max_values: None, max_size: Some(16), added: 2491, mode: MaxEncodedLen) + /// Storage: Bounties Bounties (r:1 w:0) + /// Proof: Bounties Bounties (max_values: None, max_size: Some(177), added: 2652, mode: MaxEncodedLen) + /// Storage: System Account (r:2 w:2) + /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) + /// Storage: ChildBounties ChildBountyCount (r:1 w:1) + /// Proof: ChildBounties ChildBountyCount (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) + /// Storage: ChildBounties ChildBountyDescriptions (r:0 w:1) + /// Proof: ChildBounties ChildBountyDescriptions (max_values: None, max_size: Some(314), added: 2789, mode: MaxEncodedLen) + /// Storage: ChildBounties ChildBounties (r:0 w:1) + /// Proof: ChildBounties ChildBounties (max_values: None, max_size: Some(145), added: 2620, mode: MaxEncodedLen) /// The range of component `d` is `[0, 300]`. fn add_child_bounty(d: u32, ) -> Weight { - // Minimum execution time: 59_121 nanoseconds. - Weight::from_ref_time(60_212_235 as u64) - // Standard Error: 149 - .saturating_add(Weight::from_ref_time(412 as u64).saturating_mul(d as u64)) - .saturating_add(T::DbWeight::get().reads(5 as u64)) - .saturating_add(T::DbWeight::get().writes(6 as u64)) - } - // Storage: Bounties Bounties (r:1 w:0) - // Storage: ChildBounties ChildBounties (r:1 w:1) - // Storage: ChildBounties ChildrenCuratorFees (r:1 w:1) + // Proof Size summary in bytes: + // Measured: `742` + // Estimated: `10848` + // Minimum execution time: 46_743 nanoseconds. + Weight::from_parts(47_762_924, 10848) + // Standard Error: 135 + .saturating_add(Weight::from_ref_time(599).saturating_mul(d.into())) + .saturating_add(T::DbWeight::get().reads(5_u64)) + .saturating_add(T::DbWeight::get().writes(6_u64)) + } + /// Storage: Bounties Bounties (r:1 w:0) + /// Proof: Bounties Bounties (max_values: None, max_size: Some(177), added: 2652, mode: MaxEncodedLen) + /// Storage: ChildBounties ChildBounties (r:1 w:1) + /// Proof: ChildBounties ChildBounties (max_values: None, max_size: Some(145), added: 2620, mode: MaxEncodedLen) + /// Storage: ChildBounties ChildrenCuratorFees (r:1 w:1) + /// Proof: ChildBounties ChildrenCuratorFees (max_values: None, max_size: Some(28), added: 2503, mode: MaxEncodedLen) fn propose_curator() -> Weight { - // Minimum execution time: 20_785 nanoseconds. - Weight::from_ref_time(21_000_000 as u64) - .saturating_add(T::DbWeight::get().reads(3 as u64)) - .saturating_add(T::DbWeight::get().writes(2 as u64)) - } - // Storage: Bounties Bounties (r:1 w:0) - // Storage: ChildBounties ChildBounties (r:1 w:1) - // Storage: System Account (r:1 w:1) + // Proof Size summary in bytes: + // Measured: `796` + // Estimated: `7775` + // Minimum execution time: 16_417 nanoseconds. + Weight::from_parts(16_712_000, 7775) + .saturating_add(T::DbWeight::get().reads(3_u64)) + .saturating_add(T::DbWeight::get().writes(2_u64)) + } + /// Storage: Bounties Bounties (r:1 w:0) + /// Proof: Bounties Bounties (max_values: None, max_size: Some(177), added: 2652, mode: MaxEncodedLen) + /// Storage: ChildBounties ChildBounties (r:1 w:1) + /// Proof: ChildBounties ChildBounties (max_values: None, max_size: Some(145), added: 2620, mode: MaxEncodedLen) + /// Storage: System Account (r:1 w:1) + /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) fn accept_curator() -> Weight { - // Minimum execution time: 37_874 nanoseconds. - Weight::from_ref_time(38_322_000 as u64) - .saturating_add(T::DbWeight::get().reads(3 as u64)) - .saturating_add(T::DbWeight::get().writes(2 as u64)) - } - // Storage: ChildBounties ChildBounties (r:1 w:1) - // Storage: Bounties Bounties (r:1 w:0) - // Storage: System Account (r:1 w:1) + // Proof Size summary in bytes: + // Measured: `974` + // Estimated: `7875` + // Minimum execution time: 25_548 nanoseconds. + Weight::from_parts(25_919_000, 7875) + .saturating_add(T::DbWeight::get().reads(3_u64)) + .saturating_add(T::DbWeight::get().writes(2_u64)) + } + /// Storage: ChildBounties ChildBounties (r:1 w:1) + /// Proof: ChildBounties ChildBounties (max_values: None, max_size: Some(145), added: 2620, mode: MaxEncodedLen) + /// Storage: Bounties Bounties (r:1 w:0) + /// Proof: Bounties Bounties (max_values: None, max_size: Some(177), added: 2652, mode: MaxEncodedLen) + /// Storage: System Account (r:1 w:1) + /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) fn unassign_curator() -> Weight { - // Minimum execution time: 43_385 nanoseconds. - Weight::from_ref_time(43_774_000 as u64) - .saturating_add(T::DbWeight::get().reads(3 as u64)) - .saturating_add(T::DbWeight::get().writes(2 as u64)) + // Proof Size summary in bytes: + // Measured: `974` + // Estimated: `7875` + // Minimum execution time: 27_645 nanoseconds. + Weight::from_parts(27_947_000, 7875) + .saturating_add(T::DbWeight::get().reads(3_u64)) + .saturating_add(T::DbWeight::get().writes(2_u64)) } - // Storage: Bounties Bounties (r:1 w:0) - // Storage: ChildBounties ChildBounties (r:1 w:1) + /// Storage: Bounties Bounties (r:1 w:0) + /// Proof: Bounties Bounties (max_values: None, max_size: Some(177), added: 2652, mode: MaxEncodedLen) + /// Storage: ChildBounties ChildBounties (r:1 w:1) + /// Proof: ChildBounties ChildBounties (max_values: None, max_size: Some(145), added: 2620, mode: MaxEncodedLen) fn award_child_bounty() -> Weight { - // Minimum execution time: 31_390 nanoseconds. - Weight::from_ref_time(31_802_000 as u64) - .saturating_add(T::DbWeight::get().reads(2 as u64)) - .saturating_add(T::DbWeight::get().writes(1 as u64)) - } - // Storage: ChildBounties ChildBounties (r:1 w:1) - // Storage: System Account (r:3 w:3) - // Storage: ChildBounties ParentChildBounties (r:1 w:1) - // Storage: ChildBounties ChildBountyDescriptions (r:0 w:1) + // Proof Size summary in bytes: + // Measured: `839` + // Estimated: `5272` + // Minimum execution time: 20_002 nanoseconds. + Weight::from_parts(20_512_000, 5272) + .saturating_add(T::DbWeight::get().reads(2_u64)) + .saturating_add(T::DbWeight::get().writes(1_u64)) + } + /// Storage: ChildBounties ChildBounties (r:1 w:1) + /// Proof: ChildBounties ChildBounties (max_values: None, max_size: Some(145), added: 2620, mode: MaxEncodedLen) + /// Storage: System Account (r:3 w:3) + /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) + /// Storage: ChildBounties ParentChildBounties (r:1 w:1) + /// Proof: ChildBounties ParentChildBounties (max_values: None, max_size: Some(16), added: 2491, mode: MaxEncodedLen) + /// Storage: ChildBounties ChildBountyDescriptions (r:0 w:1) + /// Proof: ChildBounties ChildBountyDescriptions (max_values: None, max_size: Some(314), added: 2789, mode: MaxEncodedLen) fn claim_child_bounty() -> Weight { - // Minimum execution time: 74_956 nanoseconds. - Weight::from_ref_time(75_850_000 as u64) - .saturating_add(T::DbWeight::get().reads(5 as u64)) - .saturating_add(T::DbWeight::get().writes(6 as u64)) - } - // Storage: Bounties Bounties (r:1 w:0) - // Storage: ChildBounties ChildBounties (r:1 w:1) - // Storage: ChildBounties ChildrenCuratorFees (r:1 w:1) - // Storage: ChildBounties ParentChildBounties (r:1 w:1) - // Storage: System Account (r:2 w:2) - // Storage: ChildBounties ChildBountyDescriptions (r:0 w:1) + // Proof Size summary in bytes: + // Measured: `744` + // Estimated: `12920` + // Minimum execution time: 63_752 nanoseconds. + Weight::from_parts(64_179_000, 12920) + .saturating_add(T::DbWeight::get().reads(5_u64)) + .saturating_add(T::DbWeight::get().writes(6_u64)) + } + /// Storage: Bounties Bounties (r:1 w:0) + /// Proof: Bounties Bounties (max_values: None, max_size: Some(177), added: 2652, mode: MaxEncodedLen) + /// Storage: ChildBounties ChildBounties (r:1 w:1) + /// Proof: ChildBounties ChildBounties (max_values: None, max_size: Some(145), added: 2620, mode: MaxEncodedLen) + /// Storage: ChildBounties ChildrenCuratorFees (r:1 w:1) + /// Proof: ChildBounties ChildrenCuratorFees (max_values: None, max_size: Some(28), added: 2503, mode: MaxEncodedLen) + /// Storage: ChildBounties ParentChildBounties (r:1 w:1) + /// Proof: ChildBounties ParentChildBounties (max_values: None, max_size: Some(16), added: 2491, mode: MaxEncodedLen) + /// Storage: System Account (r:2 w:2) + /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) + /// Storage: ChildBounties ChildBountyDescriptions (r:0 w:1) + /// Proof: ChildBounties ChildBountyDescriptions (max_values: None, max_size: Some(314), added: 2789, mode: MaxEncodedLen) fn close_child_bounty_added() -> Weight { - // Minimum execution time: 57_215 nanoseconds. - Weight::from_ref_time(58_285_000 as u64) - .saturating_add(T::DbWeight::get().reads(6 as u64)) - .saturating_add(T::DbWeight::get().writes(6 as u64)) - } - // Storage: Bounties Bounties (r:1 w:0) - // Storage: ChildBounties ChildBounties (r:1 w:1) - // Storage: System Account (r:3 w:3) - // Storage: ChildBounties ChildrenCuratorFees (r:1 w:1) - // Storage: ChildBounties ParentChildBounties (r:1 w:1) - // Storage: ChildBounties ChildBountyDescriptions (r:0 w:1) + // Proof Size summary in bytes: + // Measured: `1106` + // Estimated: `15472` + // Minimum execution time: 47_388 nanoseconds. + Weight::from_parts(47_946_000, 15472) + .saturating_add(T::DbWeight::get().reads(6_u64)) + .saturating_add(T::DbWeight::get().writes(6_u64)) + } + /// Storage: Bounties Bounties (r:1 w:0) + /// Proof: Bounties Bounties (max_values: None, max_size: Some(177), added: 2652, mode: MaxEncodedLen) + /// Storage: ChildBounties ChildBounties (r:1 w:1) + /// Proof: ChildBounties ChildBounties (max_values: None, max_size: Some(145), added: 2620, mode: MaxEncodedLen) + /// Storage: System Account (r:3 w:3) + /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) + /// Storage: ChildBounties ChildrenCuratorFees (r:1 w:1) + /// Proof: ChildBounties ChildrenCuratorFees (max_values: None, max_size: Some(28), added: 2503, mode: MaxEncodedLen) + /// Storage: ChildBounties ParentChildBounties (r:1 w:1) + /// Proof: ChildBounties ParentChildBounties (max_values: None, max_size: Some(16), added: 2491, mode: MaxEncodedLen) + /// Storage: ChildBounties ChildBountyDescriptions (r:0 w:1) + /// Proof: ChildBounties ChildBountyDescriptions (max_values: None, max_size: Some(314), added: 2789, mode: MaxEncodedLen) fn close_child_bounty_active() -> Weight { - // Minimum execution time: 67_641 nanoseconds. - Weight::from_ref_time(69_184_000 as u64) - .saturating_add(T::DbWeight::get().reads(7 as u64)) - .saturating_add(T::DbWeight::get().writes(7 as u64)) + // Proof Size summary in bytes: + // Measured: `1325` + // Estimated: `18075` + // Minimum execution time: 58_008 nanoseconds. + Weight::from_parts(58_586_000, 18075) + .saturating_add(T::DbWeight::get().reads(7_u64)) + .saturating_add(T::DbWeight::get().writes(7_u64)) } } // For backwards compatibility and tests impl WeightInfo for () { - // Storage: ChildBounties ParentChildBounties (r:1 w:1) - // Storage: Bounties Bounties (r:1 w:0) - // Storage: System Account (r:2 w:2) - // Storage: ChildBounties ChildBountyCount (r:1 w:1) - // Storage: ChildBounties ChildBountyDescriptions (r:0 w:1) - // Storage: ChildBounties ChildBounties (r:0 w:1) + /// Storage: ChildBounties ParentChildBounties (r:1 w:1) + /// Proof: ChildBounties ParentChildBounties (max_values: None, max_size: Some(16), added: 2491, mode: MaxEncodedLen) + /// Storage: Bounties Bounties (r:1 w:0) + /// Proof: Bounties Bounties (max_values: None, max_size: Some(177), added: 2652, mode: MaxEncodedLen) + /// Storage: System Account (r:2 w:2) + /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) + /// Storage: ChildBounties ChildBountyCount (r:1 w:1) + /// Proof: ChildBounties ChildBountyCount (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) + /// Storage: ChildBounties ChildBountyDescriptions (r:0 w:1) + /// Proof: ChildBounties ChildBountyDescriptions (max_values: None, max_size: Some(314), added: 2789, mode: MaxEncodedLen) + /// Storage: ChildBounties ChildBounties (r:0 w:1) + /// Proof: ChildBounties ChildBounties (max_values: None, max_size: Some(145), added: 2620, mode: MaxEncodedLen) /// The range of component `d` is `[0, 300]`. fn add_child_bounty(d: u32, ) -> Weight { - // Minimum execution time: 59_121 nanoseconds. - Weight::from_ref_time(60_212_235 as u64) - // Standard Error: 149 - .saturating_add(Weight::from_ref_time(412 as u64).saturating_mul(d as u64)) - .saturating_add(RocksDbWeight::get().reads(5 as u64)) - .saturating_add(RocksDbWeight::get().writes(6 as u64)) - } - // Storage: Bounties Bounties (r:1 w:0) - // Storage: ChildBounties ChildBounties (r:1 w:1) - // Storage: ChildBounties ChildrenCuratorFees (r:1 w:1) + // Proof Size summary in bytes: + // Measured: `742` + // Estimated: `10848` + // Minimum execution time: 46_743 nanoseconds. + Weight::from_parts(47_762_924, 10848) + // Standard Error: 135 + .saturating_add(Weight::from_ref_time(599).saturating_mul(d.into())) + .saturating_add(RocksDbWeight::get().reads(5_u64)) + .saturating_add(RocksDbWeight::get().writes(6_u64)) + } + /// Storage: Bounties Bounties (r:1 w:0) + /// Proof: Bounties Bounties (max_values: None, max_size: Some(177), added: 2652, mode: MaxEncodedLen) + /// Storage: ChildBounties ChildBounties (r:1 w:1) + /// Proof: ChildBounties ChildBounties (max_values: None, max_size: Some(145), added: 2620, mode: MaxEncodedLen) + /// Storage: ChildBounties ChildrenCuratorFees (r:1 w:1) + /// Proof: ChildBounties ChildrenCuratorFees (max_values: None, max_size: Some(28), added: 2503, mode: MaxEncodedLen) fn propose_curator() -> Weight { - // Minimum execution time: 20_785 nanoseconds. - Weight::from_ref_time(21_000_000 as u64) - .saturating_add(RocksDbWeight::get().reads(3 as u64)) - .saturating_add(RocksDbWeight::get().writes(2 as u64)) - } - // Storage: Bounties Bounties (r:1 w:0) - // Storage: ChildBounties ChildBounties (r:1 w:1) - // Storage: System Account (r:1 w:1) + // Proof Size summary in bytes: + // Measured: `796` + // Estimated: `7775` + // Minimum execution time: 16_417 nanoseconds. + Weight::from_parts(16_712_000, 7775) + .saturating_add(RocksDbWeight::get().reads(3_u64)) + .saturating_add(RocksDbWeight::get().writes(2_u64)) + } + /// Storage: Bounties Bounties (r:1 w:0) + /// Proof: Bounties Bounties (max_values: None, max_size: Some(177), added: 2652, mode: MaxEncodedLen) + /// Storage: ChildBounties ChildBounties (r:1 w:1) + /// Proof: ChildBounties ChildBounties (max_values: None, max_size: Some(145), added: 2620, mode: MaxEncodedLen) + /// Storage: System Account (r:1 w:1) + /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) fn accept_curator() -> Weight { - // Minimum execution time: 37_874 nanoseconds. - Weight::from_ref_time(38_322_000 as u64) - .saturating_add(RocksDbWeight::get().reads(3 as u64)) - .saturating_add(RocksDbWeight::get().writes(2 as u64)) - } - // Storage: ChildBounties ChildBounties (r:1 w:1) - // Storage: Bounties Bounties (r:1 w:0) - // Storage: System Account (r:1 w:1) + // Proof Size summary in bytes: + // Measured: `974` + // Estimated: `7875` + // Minimum execution time: 25_548 nanoseconds. + Weight::from_parts(25_919_000, 7875) + .saturating_add(RocksDbWeight::get().reads(3_u64)) + .saturating_add(RocksDbWeight::get().writes(2_u64)) + } + /// Storage: ChildBounties ChildBounties (r:1 w:1) + /// Proof: ChildBounties ChildBounties (max_values: None, max_size: Some(145), added: 2620, mode: MaxEncodedLen) + /// Storage: Bounties Bounties (r:1 w:0) + /// Proof: Bounties Bounties (max_values: None, max_size: Some(177), added: 2652, mode: MaxEncodedLen) + /// Storage: System Account (r:1 w:1) + /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) fn unassign_curator() -> Weight { - // Minimum execution time: 43_385 nanoseconds. - Weight::from_ref_time(43_774_000 as u64) - .saturating_add(RocksDbWeight::get().reads(3 as u64)) - .saturating_add(RocksDbWeight::get().writes(2 as u64)) + // Proof Size summary in bytes: + // Measured: `974` + // Estimated: `7875` + // Minimum execution time: 27_645 nanoseconds. + Weight::from_parts(27_947_000, 7875) + .saturating_add(RocksDbWeight::get().reads(3_u64)) + .saturating_add(RocksDbWeight::get().writes(2_u64)) } - // Storage: Bounties Bounties (r:1 w:0) - // Storage: ChildBounties ChildBounties (r:1 w:1) + /// Storage: Bounties Bounties (r:1 w:0) + /// Proof: Bounties Bounties (max_values: None, max_size: Some(177), added: 2652, mode: MaxEncodedLen) + /// Storage: ChildBounties ChildBounties (r:1 w:1) + /// Proof: ChildBounties ChildBounties (max_values: None, max_size: Some(145), added: 2620, mode: MaxEncodedLen) fn award_child_bounty() -> Weight { - // Minimum execution time: 31_390 nanoseconds. - Weight::from_ref_time(31_802_000 as u64) - .saturating_add(RocksDbWeight::get().reads(2 as u64)) - .saturating_add(RocksDbWeight::get().writes(1 as u64)) - } - // Storage: ChildBounties ChildBounties (r:1 w:1) - // Storage: System Account (r:3 w:3) - // Storage: ChildBounties ParentChildBounties (r:1 w:1) - // Storage: ChildBounties ChildBountyDescriptions (r:0 w:1) + // Proof Size summary in bytes: + // Measured: `839` + // Estimated: `5272` + // Minimum execution time: 20_002 nanoseconds. + Weight::from_parts(20_512_000, 5272) + .saturating_add(RocksDbWeight::get().reads(2_u64)) + .saturating_add(RocksDbWeight::get().writes(1_u64)) + } + /// Storage: ChildBounties ChildBounties (r:1 w:1) + /// Proof: ChildBounties ChildBounties (max_values: None, max_size: Some(145), added: 2620, mode: MaxEncodedLen) + /// Storage: System Account (r:3 w:3) + /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) + /// Storage: ChildBounties ParentChildBounties (r:1 w:1) + /// Proof: ChildBounties ParentChildBounties (max_values: None, max_size: Some(16), added: 2491, mode: MaxEncodedLen) + /// Storage: ChildBounties ChildBountyDescriptions (r:0 w:1) + /// Proof: ChildBounties ChildBountyDescriptions (max_values: None, max_size: Some(314), added: 2789, mode: MaxEncodedLen) fn claim_child_bounty() -> Weight { - // Minimum execution time: 74_956 nanoseconds. - Weight::from_ref_time(75_850_000 as u64) - .saturating_add(RocksDbWeight::get().reads(5 as u64)) - .saturating_add(RocksDbWeight::get().writes(6 as u64)) - } - // Storage: Bounties Bounties (r:1 w:0) - // Storage: ChildBounties ChildBounties (r:1 w:1) - // Storage: ChildBounties ChildrenCuratorFees (r:1 w:1) - // Storage: ChildBounties ParentChildBounties (r:1 w:1) - // Storage: System Account (r:2 w:2) - // Storage: ChildBounties ChildBountyDescriptions (r:0 w:1) + // Proof Size summary in bytes: + // Measured: `744` + // Estimated: `12920` + // Minimum execution time: 63_752 nanoseconds. + Weight::from_parts(64_179_000, 12920) + .saturating_add(RocksDbWeight::get().reads(5_u64)) + .saturating_add(RocksDbWeight::get().writes(6_u64)) + } + /// Storage: Bounties Bounties (r:1 w:0) + /// Proof: Bounties Bounties (max_values: None, max_size: Some(177), added: 2652, mode: MaxEncodedLen) + /// Storage: ChildBounties ChildBounties (r:1 w:1) + /// Proof: ChildBounties ChildBounties (max_values: None, max_size: Some(145), added: 2620, mode: MaxEncodedLen) + /// Storage: ChildBounties ChildrenCuratorFees (r:1 w:1) + /// Proof: ChildBounties ChildrenCuratorFees (max_values: None, max_size: Some(28), added: 2503, mode: MaxEncodedLen) + /// Storage: ChildBounties ParentChildBounties (r:1 w:1) + /// Proof: ChildBounties ParentChildBounties (max_values: None, max_size: Some(16), added: 2491, mode: MaxEncodedLen) + /// Storage: System Account (r:2 w:2) + /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) + /// Storage: ChildBounties ChildBountyDescriptions (r:0 w:1) + /// Proof: ChildBounties ChildBountyDescriptions (max_values: None, max_size: Some(314), added: 2789, mode: MaxEncodedLen) fn close_child_bounty_added() -> Weight { - // Minimum execution time: 57_215 nanoseconds. - Weight::from_ref_time(58_285_000 as u64) - .saturating_add(RocksDbWeight::get().reads(6 as u64)) - .saturating_add(RocksDbWeight::get().writes(6 as u64)) - } - // Storage: Bounties Bounties (r:1 w:0) - // Storage: ChildBounties ChildBounties (r:1 w:1) - // Storage: System Account (r:3 w:3) - // Storage: ChildBounties ChildrenCuratorFees (r:1 w:1) - // Storage: ChildBounties ParentChildBounties (r:1 w:1) - // Storage: ChildBounties ChildBountyDescriptions (r:0 w:1) + // Proof Size summary in bytes: + // Measured: `1106` + // Estimated: `15472` + // Minimum execution time: 47_388 nanoseconds. + Weight::from_parts(47_946_000, 15472) + .saturating_add(RocksDbWeight::get().reads(6_u64)) + .saturating_add(RocksDbWeight::get().writes(6_u64)) + } + /// Storage: Bounties Bounties (r:1 w:0) + /// Proof: Bounties Bounties (max_values: None, max_size: Some(177), added: 2652, mode: MaxEncodedLen) + /// Storage: ChildBounties ChildBounties (r:1 w:1) + /// Proof: ChildBounties ChildBounties (max_values: None, max_size: Some(145), added: 2620, mode: MaxEncodedLen) + /// Storage: System Account (r:3 w:3) + /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) + /// Storage: ChildBounties ChildrenCuratorFees (r:1 w:1) + /// Proof: ChildBounties ChildrenCuratorFees (max_values: None, max_size: Some(28), added: 2503, mode: MaxEncodedLen) + /// Storage: ChildBounties ParentChildBounties (r:1 w:1) + /// Proof: ChildBounties ParentChildBounties (max_values: None, max_size: Some(16), added: 2491, mode: MaxEncodedLen) + /// Storage: ChildBounties ChildBountyDescriptions (r:0 w:1) + /// Proof: ChildBounties ChildBountyDescriptions (max_values: None, max_size: Some(314), added: 2789, mode: MaxEncodedLen) fn close_child_bounty_active() -> Weight { - // Minimum execution time: 67_641 nanoseconds. - Weight::from_ref_time(69_184_000 as u64) - .saturating_add(RocksDbWeight::get().reads(7 as u64)) - .saturating_add(RocksDbWeight::get().writes(7 as u64)) + // Proof Size summary in bytes: + // Measured: `1325` + // Estimated: `18075` + // Minimum execution time: 58_008 nanoseconds. + Weight::from_parts(58_586_000, 18075) + .saturating_add(RocksDbWeight::get().reads(7_u64)) + .saturating_add(RocksDbWeight::get().writes(7_u64)) } } diff --git a/frame/collective/src/weights.rs b/frame/collective/src/weights.rs index 052550de7..fe6a22201 100644 --- a/frame/collective/src/weights.rs +++ b/frame/collective/src/weights.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2022 Parity Technologies (UK) Ltd. +// Copyright (C) 2023 Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); @@ -18,7 +18,8 @@ //! Autogenerated weights for pallet_collective //! //! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev -//! DATE: 2022-11-07, STEPS: `50`, REPEAT: 20, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! DATE: 2023-01-24, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! WORST CASE MAP SIZE: `1000000` //! HOSTNAME: `bm2`, CPU: `Intel(R) Core(TM) i7-7700K CPU @ 4.20GHz` //! EXECUTION: Some(Wasm), WASM-EXECUTION: Compiled, CHAIN: Some("dev"), DB CACHE: 1024 @@ -62,326 +63,492 @@ pub trait WeightInfo { /// Weights for pallet_collective using the Substrate node and recommended hardware. pub struct SubstrateWeight(PhantomData); impl WeightInfo for SubstrateWeight { - // Storage: Council Members (r:1 w:1) - // Storage: Council Proposals (r:1 w:0) - // Storage: Council Prime (r:0 w:1) - // Storage: Council Voting (r:100 w:100) + /// Storage: Council Members (r:1 w:1) + /// Proof Skipped: Council Members (max_values: Some(1), max_size: None, mode: Measured) + /// Storage: Council Proposals (r:1 w:0) + /// Proof Skipped: Council Proposals (max_values: Some(1), max_size: None, mode: Measured) + /// Storage: Council Voting (r:100 w:100) + /// Proof Skipped: Council Voting (max_values: None, max_size: None, mode: Measured) + /// Storage: Council Prime (r:0 w:1) + /// Proof Skipped: Council Prime (max_values: Some(1), max_size: None, mode: Measured) /// The range of component `m` is `[0, 100]`. /// The range of component `n` is `[0, 100]`. /// The range of component `p` is `[0, 100]`. fn set_members(m: u32, _n: u32, p: u32, ) -> Weight { - // Minimum execution time: 18_895 nanoseconds. - Weight::from_ref_time(19_254_000 as u64) - // Standard Error: 63_540 - .saturating_add(Weight::from_ref_time(5_061_801 as u64).saturating_mul(m as u64)) - // Standard Error: 63_540 - .saturating_add(Weight::from_ref_time(7_588_981 as u64).saturating_mul(p as u64)) - .saturating_add(T::DbWeight::get().reads(2 as u64)) - .saturating_add(T::DbWeight::get().reads((1 as u64).saturating_mul(p as u64))) - .saturating_add(T::DbWeight::get().writes(2 as u64)) - .saturating_add(T::DbWeight::get().writes((1 as u64).saturating_mul(p as u64))) + // Proof Size summary in bytes: + // Measured: `0 + m * (3233 ±0) + p * (3223 ±0)` + // Estimated: `16586 + m * (7809 ±24) + p * (10238 ±24)` + // Minimum execution time: 17_093 nanoseconds. + Weight::from_parts(17_284_000, 16586) + // Standard Error: 64_700 + .saturating_add(Weight::from_ref_time(5_143_145).saturating_mul(m.into())) + // Standard Error: 64_700 + .saturating_add(Weight::from_ref_time(7_480_941).saturating_mul(p.into())) + .saturating_add(T::DbWeight::get().reads(2_u64)) + .saturating_add(T::DbWeight::get().reads((1_u64).saturating_mul(p.into()))) + .saturating_add(T::DbWeight::get().writes(2_u64)) + .saturating_add(T::DbWeight::get().writes((1_u64).saturating_mul(p.into()))) + .saturating_add(Weight::from_proof_size(7809).saturating_mul(m.into())) + .saturating_add(Weight::from_proof_size(10238).saturating_mul(p.into())) } - // Storage: Council Members (r:1 w:0) - /// The range of component `b` is `[1, 1024]`. + /// Storage: Council Members (r:1 w:0) + /// Proof Skipped: Council Members (max_values: Some(1), max_size: None, mode: Measured) + /// The range of component `b` is `[2, 1024]`. /// The range of component `m` is `[1, 100]`. fn execute(b: u32, m: u32, ) -> Weight { - // Minimum execution time: 24_469 nanoseconds. - Weight::from_ref_time(23_961_134 as u64) - // Standard Error: 43 - .saturating_add(Weight::from_ref_time(1_677 as u64).saturating_mul(b as u64)) - // Standard Error: 450 - .saturating_add(Weight::from_ref_time(18_645 as u64).saturating_mul(m as u64)) - .saturating_add(T::DbWeight::get().reads(1 as u64)) + // Proof Size summary in bytes: + // Measured: `234 + m * (32 ±0)` + // Estimated: `730 + m * (32 ±0)` + // Minimum execution time: 15_972 nanoseconds. + Weight::from_parts(14_971_445, 730) + // Standard Error: 32 + .saturating_add(Weight::from_ref_time(1_775).saturating_mul(b.into())) + // Standard Error: 334 + .saturating_add(Weight::from_ref_time(17_052).saturating_mul(m.into())) + .saturating_add(T::DbWeight::get().reads(1_u64)) + .saturating_add(Weight::from_proof_size(32).saturating_mul(m.into())) } - // Storage: Council Members (r:1 w:0) - // Storage: Council ProposalOf (r:1 w:0) - /// The range of component `b` is `[1, 1024]`. + /// Storage: Council Members (r:1 w:0) + /// Proof Skipped: Council Members (max_values: Some(1), max_size: None, mode: Measured) + /// Storage: Council ProposalOf (r:1 w:0) + /// Proof Skipped: Council ProposalOf (max_values: None, max_size: None, mode: Measured) + /// The range of component `b` is `[2, 1024]`. /// The range of component `m` is `[1, 100]`. fn propose_execute(b: u32, m: u32, ) -> Weight { - // Minimum execution time: 26_476 nanoseconds. - Weight::from_ref_time(25_829_298 as u64) - // Standard Error: 49 - .saturating_add(Weight::from_ref_time(1_741 as u64).saturating_mul(b as u64)) - // Standard Error: 515 - .saturating_add(Weight::from_ref_time(29_436 as u64).saturating_mul(m as u64)) - .saturating_add(T::DbWeight::get().reads(2 as u64)) + // Proof Size summary in bytes: + // Measured: `234 + m * (32 ±0)` + // Estimated: `3440 + m * (64 ±0)` + // Minimum execution time: 17_950 nanoseconds. + Weight::from_parts(17_019_558, 3440) + // Standard Error: 41 + .saturating_add(Weight::from_ref_time(1_807).saturating_mul(b.into())) + // Standard Error: 432 + .saturating_add(Weight::from_ref_time(27_986).saturating_mul(m.into())) + .saturating_add(T::DbWeight::get().reads(2_u64)) + .saturating_add(Weight::from_proof_size(64).saturating_mul(m.into())) } - // Storage: Council Members (r:1 w:0) - // Storage: Council ProposalOf (r:1 w:1) - // Storage: Council Proposals (r:1 w:1) - // Storage: Council ProposalCount (r:1 w:1) - // Storage: Council Voting (r:0 w:1) - /// The range of component `b` is `[1, 1024]`. + /// Storage: Council Members (r:1 w:0) + /// Proof Skipped: Council Members (max_values: Some(1), max_size: None, mode: Measured) + /// Storage: Council ProposalOf (r:1 w:1) + /// Proof Skipped: Council ProposalOf (max_values: None, max_size: None, mode: Measured) + /// Storage: Council Proposals (r:1 w:1) + /// Proof Skipped: Council Proposals (max_values: Some(1), max_size: None, mode: Measured) + /// Storage: Council ProposalCount (r:1 w:1) + /// Proof Skipped: Council ProposalCount (max_values: Some(1), max_size: None, mode: Measured) + /// Storage: Council Voting (r:0 w:1) + /// Proof Skipped: Council Voting (max_values: None, max_size: None, mode: Measured) + /// The range of component `b` is `[2, 1024]`. /// The range of component `m` is `[2, 100]`. /// The range of component `p` is `[1, 100]`. fn propose_proposed(b: u32, m: u32, p: u32, ) -> Weight { - // Minimum execution time: 33_585 nanoseconds. - Weight::from_ref_time(33_092_289 as u64) - // Standard Error: 173 - .saturating_add(Weight::from_ref_time(4_266 as u64).saturating_mul(b as u64)) - // Standard Error: 1_812 - .saturating_add(Weight::from_ref_time(29_262 as u64).saturating_mul(m as u64)) - // Standard Error: 1_789 - .saturating_add(Weight::from_ref_time(181_285 as u64).saturating_mul(p as u64)) - .saturating_add(T::DbWeight::get().reads(4 as u64)) - .saturating_add(T::DbWeight::get().writes(4 as u64)) + // Proof Size summary in bytes: + // Measured: `556 + m * (32 ±0) + p * (36 ±0)` + // Estimated: `6355 + m * (165 ±0) + p * (180 ±0)` + // Minimum execution time: 24_817 nanoseconds. + Weight::from_parts(24_778_955, 6355) + // Standard Error: 73 + .saturating_add(Weight::from_ref_time(2_355).saturating_mul(b.into())) + // Standard Error: 765 + .saturating_add(Weight::from_ref_time(20_518).saturating_mul(m.into())) + // Standard Error: 755 + .saturating_add(Weight::from_ref_time(85_670).saturating_mul(p.into())) + .saturating_add(T::DbWeight::get().reads(4_u64)) + .saturating_add(T::DbWeight::get().writes(4_u64)) + .saturating_add(Weight::from_proof_size(165).saturating_mul(m.into())) + .saturating_add(Weight::from_proof_size(180).saturating_mul(p.into())) } - // Storage: Council Members (r:1 w:0) - // Storage: Council Voting (r:1 w:1) + /// Storage: Council Members (r:1 w:0) + /// Proof Skipped: Council Members (max_values: Some(1), max_size: None, mode: Measured) + /// Storage: Council Voting (r:1 w:1) + /// Proof Skipped: Council Voting (max_values: None, max_size: None, mode: Measured) /// The range of component `m` is `[5, 100]`. fn vote(m: u32, ) -> Weight { - // Minimum execution time: 36_374 nanoseconds. - Weight::from_ref_time(38_950_243 as u64) - // Standard Error: 2_583 - .saturating_add(Weight::from_ref_time(65_345 as u64).saturating_mul(m as u64)) - .saturating_add(T::DbWeight::get().reads(2 as u64)) - .saturating_add(T::DbWeight::get().writes(1 as u64)) + // Proof Size summary in bytes: + // Measured: `1006 + m * (64 ±0)` + // Estimated: `4980 + m * (128 ±0)` + // Minimum execution time: 19_790 nanoseconds. + Weight::from_parts(20_528_275, 4980) + // Standard Error: 651 + .saturating_add(Weight::from_ref_time(48_856).saturating_mul(m.into())) + .saturating_add(T::DbWeight::get().reads(2_u64)) + .saturating_add(T::DbWeight::get().writes(1_u64)) + .saturating_add(Weight::from_proof_size(128).saturating_mul(m.into())) } - // Storage: Council Voting (r:1 w:1) - // Storage: Council Members (r:1 w:0) - // Storage: Council Proposals (r:1 w:1) - // Storage: Council ProposalOf (r:0 w:1) + /// Storage: Council Voting (r:1 w:1) + /// Proof Skipped: Council Voting (max_values: None, max_size: None, mode: Measured) + /// Storage: Council Members (r:1 w:0) + /// Proof Skipped: Council Members (max_values: Some(1), max_size: None, mode: Measured) + /// Storage: Council Proposals (r:1 w:1) + /// Proof Skipped: Council Proposals (max_values: Some(1), max_size: None, mode: Measured) + /// Storage: Council ProposalOf (r:0 w:1) + /// Proof Skipped: Council ProposalOf (max_values: None, max_size: None, mode: Measured) /// The range of component `m` is `[4, 100]`. /// The range of component `p` is `[1, 100]`. fn close_early_disapproved(m: u32, p: u32, ) -> Weight { - // Minimum execution time: 36_066 nanoseconds. - Weight::from_ref_time(38_439_655 as u64) - // Standard Error: 1_281 - .saturating_add(Weight::from_ref_time(17_045 as u64).saturating_mul(m as u64)) - // Standard Error: 1_249 - .saturating_add(Weight::from_ref_time(164_998 as u64).saturating_mul(p as u64)) - .saturating_add(T::DbWeight::get().reads(3 as u64)) - .saturating_add(T::DbWeight::get().writes(3 as u64)) + // Proof Size summary in bytes: + // Measured: `626 + m * (64 ±0) + p * (36 ±0)` + // Estimated: `5893 + m * (260 ±0) + p * (144 ±0)` + // Minimum execution time: 25_564 nanoseconds. + Weight::from_parts(25_535_497, 5893) + // Standard Error: 610 + .saturating_add(Weight::from_ref_time(27_956).saturating_mul(m.into())) + // Standard Error: 595 + .saturating_add(Weight::from_ref_time(84_835).saturating_mul(p.into())) + .saturating_add(T::DbWeight::get().reads(3_u64)) + .saturating_add(T::DbWeight::get().writes(3_u64)) + .saturating_add(Weight::from_proof_size(260).saturating_mul(m.into())) + .saturating_add(Weight::from_proof_size(144).saturating_mul(p.into())) } - // Storage: Council Voting (r:1 w:1) - // Storage: Council Members (r:1 w:0) - // Storage: Council ProposalOf (r:1 w:1) - // Storage: Council Proposals (r:1 w:1) - /// The range of component `b` is `[1, 1024]`. + /// Storage: Council Voting (r:1 w:1) + /// Proof Skipped: Council Voting (max_values: None, max_size: None, mode: Measured) + /// Storage: Council Members (r:1 w:0) + /// Proof Skipped: Council Members (max_values: Some(1), max_size: None, mode: Measured) + /// Storage: Council ProposalOf (r:1 w:1) + /// Proof Skipped: Council ProposalOf (max_values: None, max_size: None, mode: Measured) + /// Storage: Council Proposals (r:1 w:1) + /// Proof Skipped: Council Proposals (max_values: Some(1), max_size: None, mode: Measured) + /// The range of component `b` is `[2, 1024]`. /// The range of component `m` is `[4, 100]`. /// The range of component `p` is `[1, 100]`. fn close_early_approved(b: u32, m: u32, p: u32, ) -> Weight { - // Minimum execution time: 47_753 nanoseconds. - Weight::from_ref_time(46_507_829 as u64) - // Standard Error: 149 - .saturating_add(Weight::from_ref_time(2_159 as u64).saturating_mul(b as u64)) - // Standard Error: 1_581 - .saturating_add(Weight::from_ref_time(37_842 as u64).saturating_mul(m as u64)) - // Standard Error: 1_541 - .saturating_add(Weight::from_ref_time(173_395 as u64).saturating_mul(p as u64)) - .saturating_add(T::DbWeight::get().reads(4 as u64)) - .saturating_add(T::DbWeight::get().writes(3 as u64)) + // Proof Size summary in bytes: + // Measured: `962 + b * (1 ±0) + m * (64 ±0) + p * (40 ±0)` + // Estimated: `9164 + b * (4 ±0) + m * (264 ±0) + p * (160 ±0)` + // Minimum execution time: 36_515 nanoseconds. + Weight::from_parts(36_626_648, 9164) + // Standard Error: 98 + .saturating_add(Weight::from_ref_time(2_295).saturating_mul(b.into())) + // Standard Error: 1_036 + .saturating_add(Weight::from_ref_time(22_182).saturating_mul(m.into())) + // Standard Error: 1_010 + .saturating_add(Weight::from_ref_time(100_034).saturating_mul(p.into())) + .saturating_add(T::DbWeight::get().reads(4_u64)) + .saturating_add(T::DbWeight::get().writes(3_u64)) + .saturating_add(Weight::from_proof_size(4).saturating_mul(b.into())) + .saturating_add(Weight::from_proof_size(264).saturating_mul(m.into())) + .saturating_add(Weight::from_proof_size(160).saturating_mul(p.into())) } - // Storage: Council Voting (r:1 w:1) - // Storage: Council Members (r:1 w:0) - // Storage: Council Prime (r:1 w:0) - // Storage: Council Proposals (r:1 w:1) - // Storage: Council ProposalOf (r:0 w:1) + /// Storage: Council Voting (r:1 w:1) + /// Proof Skipped: Council Voting (max_values: None, max_size: None, mode: Measured) + /// Storage: Council Members (r:1 w:0) + /// Proof Skipped: Council Members (max_values: Some(1), max_size: None, mode: Measured) + /// Storage: Council Prime (r:1 w:0) + /// Proof Skipped: Council Prime (max_values: Some(1), max_size: None, mode: Measured) + /// Storage: Council Proposals (r:1 w:1) + /// Proof Skipped: Council Proposals (max_values: Some(1), max_size: None, mode: Measured) + /// Storage: Council ProposalOf (r:0 w:1) + /// Proof Skipped: Council ProposalOf (max_values: None, max_size: None, mode: Measured) /// The range of component `m` is `[4, 100]`. /// The range of component `p` is `[1, 100]`. fn close_disapproved(m: u32, p: u32, ) -> Weight { - // Minimum execution time: 39_416 nanoseconds. - Weight::from_ref_time(39_610_161 as u64) - // Standard Error: 1_231 - .saturating_add(Weight::from_ref_time(32_991 as u64).saturating_mul(m as u64)) - // Standard Error: 1_200 - .saturating_add(Weight::from_ref_time(170_773 as u64).saturating_mul(p as u64)) - .saturating_add(T::DbWeight::get().reads(4 as u64)) - .saturating_add(T::DbWeight::get().writes(3 as u64)) + // Proof Size summary in bytes: + // Measured: `646 + m * (64 ±0) + p * (36 ±0)` + // Estimated: `7095 + m * (325 ±0) + p * (180 ±0)` + // Minimum execution time: 28_858 nanoseconds. + Weight::from_parts(28_050_047, 7095) + // Standard Error: 614 + .saturating_add(Weight::from_ref_time(34_031).saturating_mul(m.into())) + // Standard Error: 599 + .saturating_add(Weight::from_ref_time(85_744).saturating_mul(p.into())) + .saturating_add(T::DbWeight::get().reads(4_u64)) + .saturating_add(T::DbWeight::get().writes(3_u64)) + .saturating_add(Weight::from_proof_size(325).saturating_mul(m.into())) + .saturating_add(Weight::from_proof_size(180).saturating_mul(p.into())) } - // Storage: Council Voting (r:1 w:1) - // Storage: Council Members (r:1 w:0) - // Storage: Council Prime (r:1 w:0) - // Storage: Council ProposalOf (r:1 w:1) - // Storage: Council Proposals (r:1 w:1) - /// The range of component `b` is `[1, 1024]`. + /// Storage: Council Voting (r:1 w:1) + /// Proof Skipped: Council Voting (max_values: None, max_size: None, mode: Measured) + /// Storage: Council Members (r:1 w:0) + /// Proof Skipped: Council Members (max_values: Some(1), max_size: None, mode: Measured) + /// Storage: Council Prime (r:1 w:0) + /// Proof Skipped: Council Prime (max_values: Some(1), max_size: None, mode: Measured) + /// Storage: Council ProposalOf (r:1 w:1) + /// Proof Skipped: Council ProposalOf (max_values: None, max_size: None, mode: Measured) + /// Storage: Council Proposals (r:1 w:1) + /// Proof Skipped: Council Proposals (max_values: Some(1), max_size: None, mode: Measured) + /// The range of component `b` is `[2, 1024]`. /// The range of component `m` is `[4, 100]`. /// The range of component `p` is `[1, 100]`. fn close_approved(b: u32, m: u32, p: u32, ) -> Weight { - // Minimum execution time: 49_840 nanoseconds. - Weight::from_ref_time(48_542_914 as u64) - // Standard Error: 136 - .saturating_add(Weight::from_ref_time(2_650 as u64).saturating_mul(b as u64)) - // Standard Error: 1_442 - .saturating_add(Weight::from_ref_time(37_898 as u64).saturating_mul(m as u64)) - // Standard Error: 1_406 - .saturating_add(Weight::from_ref_time(182_176 as u64).saturating_mul(p as u64)) - .saturating_add(T::DbWeight::get().reads(5 as u64)) - .saturating_add(T::DbWeight::get().writes(3 as u64)) + // Proof Size summary in bytes: + // Measured: `982 + b * (1 ±0) + m * (64 ±0) + p * (40 ±0)` + // Estimated: `10565 + b * (5 ±0) + m * (330 ±0) + p * (200 ±0)` + // Minimum execution time: 38_608 nanoseconds. + Weight::from_parts(39_948_329, 10565) + // Standard Error: 84 + .saturating_add(Weight::from_ref_time(2_045).saturating_mul(b.into())) + // Standard Error: 895 + .saturating_add(Weight::from_ref_time(22_669).saturating_mul(m.into())) + // Standard Error: 872 + .saturating_add(Weight::from_ref_time(95_525).saturating_mul(p.into())) + .saturating_add(T::DbWeight::get().reads(5_u64)) + .saturating_add(T::DbWeight::get().writes(3_u64)) + .saturating_add(Weight::from_proof_size(5).saturating_mul(b.into())) + .saturating_add(Weight::from_proof_size(330).saturating_mul(m.into())) + .saturating_add(Weight::from_proof_size(200).saturating_mul(p.into())) } - // Storage: Council Proposals (r:1 w:1) - // Storage: Council Voting (r:0 w:1) - // Storage: Council ProposalOf (r:0 w:1) + /// Storage: Council Proposals (r:1 w:1) + /// Proof Skipped: Council Proposals (max_values: Some(1), max_size: None, mode: Measured) + /// Storage: Council Voting (r:0 w:1) + /// Proof Skipped: Council Voting (max_values: None, max_size: None, mode: Measured) + /// Storage: Council ProposalOf (r:0 w:1) + /// Proof Skipped: Council ProposalOf (max_values: None, max_size: None, mode: Measured) /// The range of component `p` is `[1, 100]`. fn disapprove_proposal(p: u32, ) -> Weight { - // Minimum execution time: 24_199 nanoseconds. - Weight::from_ref_time(26_869_176 as u64) - // Standard Error: 1_609 - .saturating_add(Weight::from_ref_time(163_341 as u64).saturating_mul(p as u64)) - .saturating_add(T::DbWeight::get().reads(1 as u64)) - .saturating_add(T::DbWeight::get().writes(3 as u64)) + // Proof Size summary in bytes: + // Measured: `391 + p * (32 ±0)` + // Estimated: `1668 + p * (96 ±0)` + // Minimum execution time: 14_785 nanoseconds. + Weight::from_parts(16_393_818, 1668) + // Standard Error: 612 + .saturating_add(Weight::from_ref_time(76_786).saturating_mul(p.into())) + .saturating_add(T::DbWeight::get().reads(1_u64)) + .saturating_add(T::DbWeight::get().writes(3_u64)) + .saturating_add(Weight::from_proof_size(96).saturating_mul(p.into())) } } // For backwards compatibility and tests impl WeightInfo for () { - // Storage: Council Members (r:1 w:1) - // Storage: Council Proposals (r:1 w:0) - // Storage: Council Prime (r:0 w:1) - // Storage: Council Voting (r:100 w:100) + /// Storage: Council Members (r:1 w:1) + /// Proof Skipped: Council Members (max_values: Some(1), max_size: None, mode: Measured) + /// Storage: Council Proposals (r:1 w:0) + /// Proof Skipped: Council Proposals (max_values: Some(1), max_size: None, mode: Measured) + /// Storage: Council Voting (r:100 w:100) + /// Proof Skipped: Council Voting (max_values: None, max_size: None, mode: Measured) + /// Storage: Council Prime (r:0 w:1) + /// Proof Skipped: Council Prime (max_values: Some(1), max_size: None, mode: Measured) /// The range of component `m` is `[0, 100]`. /// The range of component `n` is `[0, 100]`. /// The range of component `p` is `[0, 100]`. fn set_members(m: u32, _n: u32, p: u32, ) -> Weight { - // Minimum execution time: 18_895 nanoseconds. - Weight::from_ref_time(19_254_000 as u64) - // Standard Error: 63_540 - .saturating_add(Weight::from_ref_time(5_061_801 as u64).saturating_mul(m as u64)) - // Standard Error: 63_540 - .saturating_add(Weight::from_ref_time(7_588_981 as u64).saturating_mul(p as u64)) - .saturating_add(RocksDbWeight::get().reads(2 as u64)) - .saturating_add(RocksDbWeight::get().reads((1 as u64).saturating_mul(p as u64))) - .saturating_add(RocksDbWeight::get().writes(2 as u64)) - .saturating_add(RocksDbWeight::get().writes((1 as u64).saturating_mul(p as u64))) + // Proof Size summary in bytes: + // Measured: `0 + m * (3233 ±0) + p * (3223 ±0)` + // Estimated: `16586 + m * (7809 ±24) + p * (10238 ±24)` + // Minimum execution time: 17_093 nanoseconds. + Weight::from_parts(17_284_000, 16586) + // Standard Error: 64_700 + .saturating_add(Weight::from_ref_time(5_143_145).saturating_mul(m.into())) + // Standard Error: 64_700 + .saturating_add(Weight::from_ref_time(7_480_941).saturating_mul(p.into())) + .saturating_add(RocksDbWeight::get().reads(2_u64)) + .saturating_add(RocksDbWeight::get().reads((1_u64).saturating_mul(p.into()))) + .saturating_add(RocksDbWeight::get().writes(2_u64)) + .saturating_add(RocksDbWeight::get().writes((1_u64).saturating_mul(p.into()))) + .saturating_add(Weight::from_proof_size(7809).saturating_mul(m.into())) + .saturating_add(Weight::from_proof_size(10238).saturating_mul(p.into())) } - // Storage: Council Members (r:1 w:0) - /// The range of component `b` is `[1, 1024]`. + /// Storage: Council Members (r:1 w:0) + /// Proof Skipped: Council Members (max_values: Some(1), max_size: None, mode: Measured) + /// The range of component `b` is `[2, 1024]`. /// The range of component `m` is `[1, 100]`. fn execute(b: u32, m: u32, ) -> Weight { - // Minimum execution time: 24_469 nanoseconds. - Weight::from_ref_time(23_961_134 as u64) - // Standard Error: 43 - .saturating_add(Weight::from_ref_time(1_677 as u64).saturating_mul(b as u64)) - // Standard Error: 450 - .saturating_add(Weight::from_ref_time(18_645 as u64).saturating_mul(m as u64)) - .saturating_add(RocksDbWeight::get().reads(1 as u64)) + // Proof Size summary in bytes: + // Measured: `234 + m * (32 ±0)` + // Estimated: `730 + m * (32 ±0)` + // Minimum execution time: 15_972 nanoseconds. + Weight::from_parts(14_971_445, 730) + // Standard Error: 32 + .saturating_add(Weight::from_ref_time(1_775).saturating_mul(b.into())) + // Standard Error: 334 + .saturating_add(Weight::from_ref_time(17_052).saturating_mul(m.into())) + .saturating_add(RocksDbWeight::get().reads(1_u64)) + .saturating_add(Weight::from_proof_size(32).saturating_mul(m.into())) } - // Storage: Council Members (r:1 w:0) - // Storage: Council ProposalOf (r:1 w:0) - /// The range of component `b` is `[1, 1024]`. + /// Storage: Council Members (r:1 w:0) + /// Proof Skipped: Council Members (max_values: Some(1), max_size: None, mode: Measured) + /// Storage: Council ProposalOf (r:1 w:0) + /// Proof Skipped: Council ProposalOf (max_values: None, max_size: None, mode: Measured) + /// The range of component `b` is `[2, 1024]`. /// The range of component `m` is `[1, 100]`. fn propose_execute(b: u32, m: u32, ) -> Weight { - // Minimum execution time: 26_476 nanoseconds. - Weight::from_ref_time(25_829_298 as u64) - // Standard Error: 49 - .saturating_add(Weight::from_ref_time(1_741 as u64).saturating_mul(b as u64)) - // Standard Error: 515 - .saturating_add(Weight::from_ref_time(29_436 as u64).saturating_mul(m as u64)) - .saturating_add(RocksDbWeight::get().reads(2 as u64)) + // Proof Size summary in bytes: + // Measured: `234 + m * (32 ±0)` + // Estimated: `3440 + m * (64 ±0)` + // Minimum execution time: 17_950 nanoseconds. + Weight::from_parts(17_019_558, 3440) + // Standard Error: 41 + .saturating_add(Weight::from_ref_time(1_807).saturating_mul(b.into())) + // Standard Error: 432 + .saturating_add(Weight::from_ref_time(27_986).saturating_mul(m.into())) + .saturating_add(RocksDbWeight::get().reads(2_u64)) + .saturating_add(Weight::from_proof_size(64).saturating_mul(m.into())) } - // Storage: Council Members (r:1 w:0) - // Storage: Council ProposalOf (r:1 w:1) - // Storage: Council Proposals (r:1 w:1) - // Storage: Council ProposalCount (r:1 w:1) - // Storage: Council Voting (r:0 w:1) - /// The range of component `b` is `[1, 1024]`. + /// Storage: Council Members (r:1 w:0) + /// Proof Skipped: Council Members (max_values: Some(1), max_size: None, mode: Measured) + /// Storage: Council ProposalOf (r:1 w:1) + /// Proof Skipped: Council ProposalOf (max_values: None, max_size: None, mode: Measured) + /// Storage: Council Proposals (r:1 w:1) + /// Proof Skipped: Council Proposals (max_values: Some(1), max_size: None, mode: Measured) + /// Storage: Council ProposalCount (r:1 w:1) + /// Proof Skipped: Council ProposalCount (max_values: Some(1), max_size: None, mode: Measured) + /// Storage: Council Voting (r:0 w:1) + /// Proof Skipped: Council Voting (max_values: None, max_size: None, mode: Measured) + /// The range of component `b` is `[2, 1024]`. /// The range of component `m` is `[2, 100]`. /// The range of component `p` is `[1, 100]`. fn propose_proposed(b: u32, m: u32, p: u32, ) -> Weight { - // Minimum execution time: 33_585 nanoseconds. - Weight::from_ref_time(33_092_289 as u64) - // Standard Error: 173 - .saturating_add(Weight::from_ref_time(4_266 as u64).saturating_mul(b as u64)) - // Standard Error: 1_812 - .saturating_add(Weight::from_ref_time(29_262 as u64).saturating_mul(m as u64)) - // Standard Error: 1_789 - .saturating_add(Weight::from_ref_time(181_285 as u64).saturating_mul(p as u64)) - .saturating_add(RocksDbWeight::get().reads(4 as u64)) - .saturating_add(RocksDbWeight::get().writes(4 as u64)) + // Proof Size summary in bytes: + // Measured: `556 + m * (32 ±0) + p * (36 ±0)` + // Estimated: `6355 + m * (165 ±0) + p * (180 ±0)` + // Minimum execution time: 24_817 nanoseconds. + Weight::from_parts(24_778_955, 6355) + // Standard Error: 73 + .saturating_add(Weight::from_ref_time(2_355).saturating_mul(b.into())) + // Standard Error: 765 + .saturating_add(Weight::from_ref_time(20_518).saturating_mul(m.into())) + // Standard Error: 755 + .saturating_add(Weight::from_ref_time(85_670).saturating_mul(p.into())) + .saturating_add(RocksDbWeight::get().reads(4_u64)) + .saturating_add(RocksDbWeight::get().writes(4_u64)) + .saturating_add(Weight::from_proof_size(165).saturating_mul(m.into())) + .saturating_add(Weight::from_proof_size(180).saturating_mul(p.into())) } - // Storage: Council Members (r:1 w:0) - // Storage: Council Voting (r:1 w:1) + /// Storage: Council Members (r:1 w:0) + /// Proof Skipped: Council Members (max_values: Some(1), max_size: None, mode: Measured) + /// Storage: Council Voting (r:1 w:1) + /// Proof Skipped: Council Voting (max_values: None, max_size: None, mode: Measured) /// The range of component `m` is `[5, 100]`. fn vote(m: u32, ) -> Weight { - // Minimum execution time: 36_374 nanoseconds. - Weight::from_ref_time(38_950_243 as u64) - // Standard Error: 2_583 - .saturating_add(Weight::from_ref_time(65_345 as u64).saturating_mul(m as u64)) - .saturating_add(RocksDbWeight::get().reads(2 as u64)) - .saturating_add(RocksDbWeight::get().writes(1 as u64)) + // Proof Size summary in bytes: + // Measured: `1006 + m * (64 ±0)` + // Estimated: `4980 + m * (128 ±0)` + // Minimum execution time: 19_790 nanoseconds. + Weight::from_parts(20_528_275, 4980) + // Standard Error: 651 + .saturating_add(Weight::from_ref_time(48_856).saturating_mul(m.into())) + .saturating_add(RocksDbWeight::get().reads(2_u64)) + .saturating_add(RocksDbWeight::get().writes(1_u64)) + .saturating_add(Weight::from_proof_size(128).saturating_mul(m.into())) } - // Storage: Council Voting (r:1 w:1) - // Storage: Council Members (r:1 w:0) - // Storage: Council Proposals (r:1 w:1) - // Storage: Council ProposalOf (r:0 w:1) + /// Storage: Council Voting (r:1 w:1) + /// Proof Skipped: Council Voting (max_values: None, max_size: None, mode: Measured) + /// Storage: Council Members (r:1 w:0) + /// Proof Skipped: Council Members (max_values: Some(1), max_size: None, mode: Measured) + /// Storage: Council Proposals (r:1 w:1) + /// Proof Skipped: Council Proposals (max_values: Some(1), max_size: None, mode: Measured) + /// Storage: Council ProposalOf (r:0 w:1) + /// Proof Skipped: Council ProposalOf (max_values: None, max_size: None, mode: Measured) /// The range of component `m` is `[4, 100]`. /// The range of component `p` is `[1, 100]`. fn close_early_disapproved(m: u32, p: u32, ) -> Weight { - // Minimum execution time: 36_066 nanoseconds. - Weight::from_ref_time(38_439_655 as u64) - // Standard Error: 1_281 - .saturating_add(Weight::from_ref_time(17_045 as u64).saturating_mul(m as u64)) - // Standard Error: 1_249 - .saturating_add(Weight::from_ref_time(164_998 as u64).saturating_mul(p as u64)) - .saturating_add(RocksDbWeight::get().reads(3 as u64)) - .saturating_add(RocksDbWeight::get().writes(3 as u64)) + // Proof Size summary in bytes: + // Measured: `626 + m * (64 ±0) + p * (36 ±0)` + // Estimated: `5893 + m * (260 ±0) + p * (144 ±0)` + // Minimum execution time: 25_564 nanoseconds. + Weight::from_parts(25_535_497, 5893) + // Standard Error: 610 + .saturating_add(Weight::from_ref_time(27_956).saturating_mul(m.into())) + // Standard Error: 595 + .saturating_add(Weight::from_ref_time(84_835).saturating_mul(p.into())) + .saturating_add(RocksDbWeight::get().reads(3_u64)) + .saturating_add(RocksDbWeight::get().writes(3_u64)) + .saturating_add(Weight::from_proof_size(260).saturating_mul(m.into())) + .saturating_add(Weight::from_proof_size(144).saturating_mul(p.into())) } - // Storage: Council Voting (r:1 w:1) - // Storage: Council Members (r:1 w:0) - // Storage: Council ProposalOf (r:1 w:1) - // Storage: Council Proposals (r:1 w:1) - /// The range of component `b` is `[1, 1024]`. + /// Storage: Council Voting (r:1 w:1) + /// Proof Skipped: Council Voting (max_values: None, max_size: None, mode: Measured) + /// Storage: Council Members (r:1 w:0) + /// Proof Skipped: Council Members (max_values: Some(1), max_size: None, mode: Measured) + /// Storage: Council ProposalOf (r:1 w:1) + /// Proof Skipped: Council ProposalOf (max_values: None, max_size: None, mode: Measured) + /// Storage: Council Proposals (r:1 w:1) + /// Proof Skipped: Council Proposals (max_values: Some(1), max_size: None, mode: Measured) + /// The range of component `b` is `[2, 1024]`. /// The range of component `m` is `[4, 100]`. /// The range of component `p` is `[1, 100]`. fn close_early_approved(b: u32, m: u32, p: u32, ) -> Weight { - // Minimum execution time: 47_753 nanoseconds. - Weight::from_ref_time(46_507_829 as u64) - // Standard Error: 149 - .saturating_add(Weight::from_ref_time(2_159 as u64).saturating_mul(b as u64)) - // Standard Error: 1_581 - .saturating_add(Weight::from_ref_time(37_842 as u64).saturating_mul(m as u64)) - // Standard Error: 1_541 - .saturating_add(Weight::from_ref_time(173_395 as u64).saturating_mul(p as u64)) - .saturating_add(RocksDbWeight::get().reads(4 as u64)) - .saturating_add(RocksDbWeight::get().writes(3 as u64)) + // Proof Size summary in bytes: + // Measured: `962 + b * (1 ±0) + m * (64 ±0) + p * (40 ±0)` + // Estimated: `9164 + b * (4 ±0) + m * (264 ±0) + p * (160 ±0)` + // Minimum execution time: 36_515 nanoseconds. + Weight::from_parts(36_626_648, 9164) + // Standard Error: 98 + .saturating_add(Weight::from_ref_time(2_295).saturating_mul(b.into())) + // Standard Error: 1_036 + .saturating_add(Weight::from_ref_time(22_182).saturating_mul(m.into())) + // Standard Error: 1_010 + .saturating_add(Weight::from_ref_time(100_034).saturating_mul(p.into())) + .saturating_add(RocksDbWeight::get().reads(4_u64)) + .saturating_add(RocksDbWeight::get().writes(3_u64)) + .saturating_add(Weight::from_proof_size(4).saturating_mul(b.into())) + .saturating_add(Weight::from_proof_size(264).saturating_mul(m.into())) + .saturating_add(Weight::from_proof_size(160).saturating_mul(p.into())) } - // Storage: Council Voting (r:1 w:1) - // Storage: Council Members (r:1 w:0) - // Storage: Council Prime (r:1 w:0) - // Storage: Council Proposals (r:1 w:1) - // Storage: Council ProposalOf (r:0 w:1) + /// Storage: Council Voting (r:1 w:1) + /// Proof Skipped: Council Voting (max_values: None, max_size: None, mode: Measured) + /// Storage: Council Members (r:1 w:0) + /// Proof Skipped: Council Members (max_values: Some(1), max_size: None, mode: Measured) + /// Storage: Council Prime (r:1 w:0) + /// Proof Skipped: Council Prime (max_values: Some(1), max_size: None, mode: Measured) + /// Storage: Council Proposals (r:1 w:1) + /// Proof Skipped: Council Proposals (max_values: Some(1), max_size: None, mode: Measured) + /// Storage: Council ProposalOf (r:0 w:1) + /// Proof Skipped: Council ProposalOf (max_values: None, max_size: None, mode: Measured) /// The range of component `m` is `[4, 100]`. /// The range of component `p` is `[1, 100]`. fn close_disapproved(m: u32, p: u32, ) -> Weight { - // Minimum execution time: 39_416 nanoseconds. - Weight::from_ref_time(39_610_161 as u64) - // Standard Error: 1_231 - .saturating_add(Weight::from_ref_time(32_991 as u64).saturating_mul(m as u64)) - // Standard Error: 1_200 - .saturating_add(Weight::from_ref_time(170_773 as u64).saturating_mul(p as u64)) - .saturating_add(RocksDbWeight::get().reads(4 as u64)) - .saturating_add(RocksDbWeight::get().writes(3 as u64)) + // Proof Size summary in bytes: + // Measured: `646 + m * (64 ±0) + p * (36 ±0)` + // Estimated: `7095 + m * (325 ±0) + p * (180 ±0)` + // Minimum execution time: 28_858 nanoseconds. + Weight::from_parts(28_050_047, 7095) + // Standard Error: 614 + .saturating_add(Weight::from_ref_time(34_031).saturating_mul(m.into())) + // Standard Error: 599 + .saturating_add(Weight::from_ref_time(85_744).saturating_mul(p.into())) + .saturating_add(RocksDbWeight::get().reads(4_u64)) + .saturating_add(RocksDbWeight::get().writes(3_u64)) + .saturating_add(Weight::from_proof_size(325).saturating_mul(m.into())) + .saturating_add(Weight::from_proof_size(180).saturating_mul(p.into())) } - // Storage: Council Voting (r:1 w:1) - // Storage: Council Members (r:1 w:0) - // Storage: Council Prime (r:1 w:0) - // Storage: Council ProposalOf (r:1 w:1) - // Storage: Council Proposals (r:1 w:1) - /// The range of component `b` is `[1, 1024]`. + /// Storage: Council Voting (r:1 w:1) + /// Proof Skipped: Council Voting (max_values: None, max_size: None, mode: Measured) + /// Storage: Council Members (r:1 w:0) + /// Proof Skipped: Council Members (max_values: Some(1), max_size: None, mode: Measured) + /// Storage: Council Prime (r:1 w:0) + /// Proof Skipped: Council Prime (max_values: Some(1), max_size: None, mode: Measured) + /// Storage: Council ProposalOf (r:1 w:1) + /// Proof Skipped: Council ProposalOf (max_values: None, max_size: None, mode: Measured) + /// Storage: Council Proposals (r:1 w:1) + /// Proof Skipped: Council Proposals (max_values: Some(1), max_size: None, mode: Measured) + /// The range of component `b` is `[2, 1024]`. /// The range of component `m` is `[4, 100]`. /// The range of component `p` is `[1, 100]`. fn close_approved(b: u32, m: u32, p: u32, ) -> Weight { - // Minimum execution time: 49_840 nanoseconds. - Weight::from_ref_time(48_542_914 as u64) - // Standard Error: 136 - .saturating_add(Weight::from_ref_time(2_650 as u64).saturating_mul(b as u64)) - // Standard Error: 1_442 - .saturating_add(Weight::from_ref_time(37_898 as u64).saturating_mul(m as u64)) - // Standard Error: 1_406 - .saturating_add(Weight::from_ref_time(182_176 as u64).saturating_mul(p as u64)) - .saturating_add(RocksDbWeight::get().reads(5 as u64)) - .saturating_add(RocksDbWeight::get().writes(3 as u64)) + // Proof Size summary in bytes: + // Measured: `982 + b * (1 ±0) + m * (64 ±0) + p * (40 ±0)` + // Estimated: `10565 + b * (5 ±0) + m * (330 ±0) + p * (200 ±0)` + // Minimum execution time: 38_608 nanoseconds. + Weight::from_parts(39_948_329, 10565) + // Standard Error: 84 + .saturating_add(Weight::from_ref_time(2_045).saturating_mul(b.into())) + // Standard Error: 895 + .saturating_add(Weight::from_ref_time(22_669).saturating_mul(m.into())) + // Standard Error: 872 + .saturating_add(Weight::from_ref_time(95_525).saturating_mul(p.into())) + .saturating_add(RocksDbWeight::get().reads(5_u64)) + .saturating_add(RocksDbWeight::get().writes(3_u64)) + .saturating_add(Weight::from_proof_size(5).saturating_mul(b.into())) + .saturating_add(Weight::from_proof_size(330).saturating_mul(m.into())) + .saturating_add(Weight::from_proof_size(200).saturating_mul(p.into())) } - // Storage: Council Proposals (r:1 w:1) - // Storage: Council Voting (r:0 w:1) - // Storage: Council ProposalOf (r:0 w:1) + /// Storage: Council Proposals (r:1 w:1) + /// Proof Skipped: Council Proposals (max_values: Some(1), max_size: None, mode: Measured) + /// Storage: Council Voting (r:0 w:1) + /// Proof Skipped: Council Voting (max_values: None, max_size: None, mode: Measured) + /// Storage: Council ProposalOf (r:0 w:1) + /// Proof Skipped: Council ProposalOf (max_values: None, max_size: None, mode: Measured) /// The range of component `p` is `[1, 100]`. fn disapprove_proposal(p: u32, ) -> Weight { - // Minimum execution time: 24_199 nanoseconds. - Weight::from_ref_time(26_869_176 as u64) - // Standard Error: 1_609 - .saturating_add(Weight::from_ref_time(163_341 as u64).saturating_mul(p as u64)) - .saturating_add(RocksDbWeight::get().reads(1 as u64)) - .saturating_add(RocksDbWeight::get().writes(3 as u64)) + // Proof Size summary in bytes: + // Measured: `391 + p * (32 ±0)` + // Estimated: `1668 + p * (96 ±0)` + // Minimum execution time: 14_785 nanoseconds. + Weight::from_parts(16_393_818, 1668) + // Standard Error: 612 + .saturating_add(Weight::from_ref_time(76_786).saturating_mul(p.into())) + .saturating_add(RocksDbWeight::get().reads(1_u64)) + .saturating_add(RocksDbWeight::get().writes(3_u64)) + .saturating_add(Weight::from_proof_size(96).saturating_mul(p.into())) } } diff --git a/frame/contracts/src/benchmarking/mod.rs b/frame/contracts/src/benchmarking/mod.rs index a750a6b17..2bd807dfb 100644 --- a/frame/contracts/src/benchmarking/mod.rs +++ b/frame/contracts/src/benchmarking/mod.rs @@ -205,11 +205,13 @@ benchmarks! { } // The base weight consumed on processing contracts deletion queue. + #[pov_mode = Ignored] on_process_deletion_queue_batch {}: { Storage::::process_deletion_queue_batch(Weight::MAX) } #[skip_meta] + #[pov_mode = Ignored] on_initialize_per_trie_key { let k in 0..1024; let instance = Contract::::with_storage(WasmModule::dummy(), k, T::Schedule::get().limits.payload_len)?; @@ -218,6 +220,7 @@ benchmarks! { Storage::::process_deletion_queue_batch(Weight::MAX) } + #[pov_mode = Ignored] on_initialize_per_queue_item { let q in 0..1024.min(T::DeletionQueueDepth::get()); for i in 0 .. q { @@ -232,6 +235,7 @@ benchmarks! { // This benchmarks the additional weight that is charged when a contract is executed the // first time after a new schedule was deployed: For every new schedule a contract needs // to re-run the instrumentation once. + #[pov_mode = Ignored] reinstrument { let c in 0 .. Perbill::from_percent(49).mul_ceil(T::MaxCodeLen::get()); let WasmModule { code, hash, .. } = WasmModule::::sized(c, Location::Call); @@ -248,6 +252,7 @@ benchmarks! { // is responsible. This is achieved by generating all code to the `deploy` function // which is in the wasm module but not executed on `call`. // The results are supposed to be used as `call_with_code_kb(c) - call_with_code_kb(0)`. + #[pov_mode = Ignored] call_with_code_per_byte { let c in 0 .. T::MaxCodeLen::get(); let instance = Contract::::with_caller( @@ -273,6 +278,7 @@ benchmarks! { // // We cannot let `c` grow to the maximum code size because the code is not allowed // to be larger than the maximum size **after instrumentation**. + #[pov_mode = Ignored] instantiate_with_code { let c in 0 .. Perbill::from_percent(49).mul_ceil(T::MaxCodeLen::get()); let i in 0 .. code::max_pages::() * 64 * 1024; @@ -304,6 +310,7 @@ benchmarks! { // Instantiate uses a dummy contract constructor to measure the overhead of the instantiate. // `i`: Size of the input in kilobytes. // `s`: Size of the salt in kilobytes. + #[pov_mode = Ignored] instantiate { let i in 0 .. code::max_pages::() * 64 * 1024; let s in 0 .. code::max_pages::() * 64 * 1024; @@ -335,6 +342,7 @@ benchmarks! { // part of `seal_input`. The costs for invoking a contract of a specific size are not part // of this benchmark because we cannot know the size of the contract when issuing a call // transaction. See `invoke_per_code_kb` for this. + #[pov_mode = Ignored] call { let data = vec![42u8; 1024]; let instance = Contract::::with_caller( @@ -367,6 +375,7 @@ benchmarks! { // // We cannot let `c` grow to the maximum code size because the code is not allowed // to be larger than the maximum size **after instrumentation**. + #[pov_mode = Ignored] upload_code { let c in 0 .. Perbill::from_percent(49).mul_ceil(T::MaxCodeLen::get()); let caller = whitelisted_caller(); @@ -383,6 +392,7 @@ benchmarks! { // Removing code does not depend on the size of the contract because all the information // needed to verify the removal claim (refcount, owner) is stored in a separate storage // item (`OwnerInfoOf`). + #[pov_mode = Ignored] remove_code { let caller = whitelisted_caller(); T::Currency::make_free_balance_be(&caller, caller_funding::()); @@ -399,6 +409,7 @@ benchmarks! { assert!(>::code_removed(&hash)); } + #[pov_mode = Ignored] set_code { let instance = >::with_caller( whitelisted_caller(), WasmModule::dummy(), vec![], @@ -413,6 +424,7 @@ benchmarks! { assert_eq!(instance.info()?.code_hash, hash); } + #[pov_mode = Ignored] seal_caller { let r in 0 .. API_BENCHMARK_BATCHES; let instance = Contract::::new(WasmModule::getter( @@ -421,6 +433,7 @@ benchmarks! { let origin = RawOrigin::Signed(instance.caller.clone()); }: call(origin, instance.addr, 0u32.into(), Weight::MAX, None, vec![]) + #[pov_mode = Ignored] seal_is_contract { let r in 0 .. API_BENCHMARK_BATCHES; let accounts = (0 .. r * API_BENCHMARK_BATCH_SIZE) @@ -458,6 +471,7 @@ benchmarks! { let origin = RawOrigin::Signed(instance.caller.clone()); }: call(origin, instance.addr, 0u32.into(), Weight::MAX, None, vec![]) + #[pov_mode = Ignored] seal_code_hash { let r in 0 .. API_BENCHMARK_BATCHES; let accounts = (0 .. r * API_BENCHMARK_BATCH_SIZE) @@ -503,6 +517,7 @@ benchmarks! { let origin = RawOrigin::Signed(instance.caller.clone()); }: call(origin, instance.addr, 0u32.into(), Weight::MAX, None, vec![]) + #[pov_mode = Ignored] seal_own_code_hash { let r in 0 .. API_BENCHMARK_BATCHES; let instance = Contract::::new(WasmModule::getter( @@ -511,6 +526,7 @@ benchmarks! { let origin = RawOrigin::Signed(instance.caller.clone()); }: call(origin, instance.addr, 0u32.into(), Weight::MAX, None, vec![]) + #[pov_mode = Ignored] seal_caller_is_origin { let r in 0 .. API_BENCHMARK_BATCHES; let code = WasmModule::::from(ModuleDefinition { @@ -531,6 +547,7 @@ benchmarks! { let origin = RawOrigin::Signed(instance.caller.clone()); }: call(origin, instance.addr, 0u32.into(), Weight::MAX, None, vec![]) + #[pov_mode = Ignored] seal_address { let r in 0 .. API_BENCHMARK_BATCHES; let instance = Contract::::new(WasmModule::getter( @@ -539,6 +556,7 @@ benchmarks! { let origin = RawOrigin::Signed(instance.caller.clone()); }: call(origin, instance.addr, 0u32.into(), Weight::MAX, None, vec![]) + #[pov_mode = Ignored] seal_gas_left { let r in 0 .. API_BENCHMARK_BATCHES; let instance = Contract::::new(WasmModule::getter( @@ -547,6 +565,7 @@ benchmarks! { let origin = RawOrigin::Signed(instance.caller.clone()); }: call(origin, instance.addr, 0u32.into(), Weight::MAX, None, vec![]) + #[pov_mode = Ignored] seal_balance { let r in 0 .. API_BENCHMARK_BATCHES; let instance = Contract::::new(WasmModule::getter( @@ -555,6 +574,7 @@ benchmarks! { let origin = RawOrigin::Signed(instance.caller.clone()); }: call(origin, instance.addr, 0u32.into(), Weight::MAX, None, vec![]) + #[pov_mode = Ignored] seal_value_transferred { let r in 0 .. API_BENCHMARK_BATCHES; let instance = Contract::::new(WasmModule::getter( @@ -563,6 +583,7 @@ benchmarks! { let origin = RawOrigin::Signed(instance.caller.clone()); }: call(origin, instance.addr, 0u32.into(), Weight::MAX, None, vec![]) + #[pov_mode = Ignored] seal_minimum_balance { let r in 0 .. API_BENCHMARK_BATCHES; let instance = Contract::::new(WasmModule::getter( @@ -571,6 +592,7 @@ benchmarks! { let origin = RawOrigin::Signed(instance.caller.clone()); }: call(origin, instance.addr, 0u32.into(), Weight::MAX, None, vec![]) + #[pov_mode = Ignored] seal_block_number { let r in 0 .. API_BENCHMARK_BATCHES; let instance = Contract::::new(WasmModule::getter( @@ -579,6 +601,7 @@ benchmarks! { let origin = RawOrigin::Signed(instance.caller.clone()); }: call(origin, instance.addr, 0u32.into(), Weight::MAX, None, vec![]) + #[pov_mode = Ignored] seal_now { let r in 0 .. API_BENCHMARK_BATCHES; let instance = Contract::::new(WasmModule::getter( @@ -587,6 +610,7 @@ benchmarks! { let origin = RawOrigin::Signed(instance.caller.clone()); }: call(origin, instance.addr, 0u32.into(), Weight::MAX, None, vec![]) + #[pov_mode = Ignored] seal_weight_to_fee { let r in 0 .. API_BENCHMARK_BATCHES; let pages = code::max_pages::(); @@ -614,6 +638,7 @@ benchmarks! { let origin = RawOrigin::Signed(instance.caller.clone()); }: call(origin, instance.addr, 0u32.into(), Weight::MAX, None, vec![]) + #[pov_mode = Ignored] seal_gas { let r in 0 .. API_BENCHMARK_BATCHES; let code = WasmModule::::from(ModuleDefinition { @@ -634,6 +659,7 @@ benchmarks! { }: call(origin, instance.addr, 0u32.into(), Weight::MAX, None, vec![]) + #[pov_mode = Ignored] seal_input { let r in 0 .. API_BENCHMARK_BATCHES; let code = WasmModule::::from(ModuleDefinition { @@ -661,6 +687,7 @@ benchmarks! { let origin = RawOrigin::Signed(instance.caller.clone()); }: call(origin, instance.addr, 0u32.into(), Weight::MAX, None, vec![]) + #[pov_mode = Ignored] seal_input_per_kb { let n in 0 .. code::max_pages::() * 64; let pages = code::max_pages::(); @@ -694,6 +721,7 @@ benchmarks! { // We cannot call `seal_return` multiple times. Therefore our weight determination is not // as precise as with other APIs. Because this function can only be called once per // contract it cannot be used as an attack vector. + #[pov_mode = Ignored] seal_return { let r in 0 .. 1; let code = WasmModule::::from(ModuleDefinition { @@ -716,6 +744,7 @@ benchmarks! { let origin = RawOrigin::Signed(instance.caller.clone()); }: call(origin, instance.addr, 0u32.into(), Weight::MAX, None, vec![]) + #[pov_mode = Ignored] seal_return_per_kb { let n in 0 .. code::max_pages::() * 64; let code = WasmModule::::from(ModuleDefinition { @@ -740,6 +769,7 @@ benchmarks! { }: call(origin, instance.addr, 0u32.into(), Weight::MAX, None, vec![]) // The same argument as for `seal_return` is true here. + #[pov_mode = Ignored] seal_terminate { let r in 0 .. 1; let beneficiary = account::("beneficiary", 0, 0); @@ -782,6 +812,7 @@ benchmarks! { // We benchmark only for the maximum subject length. We assume that this is some lowish // number (< 1 KB). Therefore we are not overcharging too much in case a smaller subject is // used. + #[pov_mode = Ignored] seal_random { let r in 0 .. API_BENCHMARK_BATCHES; let pages = code::max_pages::(); @@ -816,6 +847,7 @@ benchmarks! { // Overhead of calling the function without any topic. // We benchmark for the worst case (largest event). + #[pov_mode = Ignored] seal_deposit_event { let r in 0 .. API_BENCHMARK_BATCHES; let code = WasmModule::::from(ModuleDefinition { @@ -842,6 +874,7 @@ benchmarks! { // Benchmark the overhead that topics generate. // `t`: Number of topics // `n`: Size of event payload in kb + #[pov_mode = Ignored] seal_deposit_event_per_topic_and_kb { let t in 0 .. T::Schedule::get().limits.event_topics; let n in 0 .. T::Schedule::get().limits.payload_len / 1024; @@ -880,6 +913,7 @@ benchmarks! { // The size of the supplied message does not influence the weight because as it is never // processed during on-chain execution: It is only ever read during debugging which happens // when the contract is called as RPC where weights do not matter. + #[pov_mode = Ignored] seal_debug_message { let r in 0 .. API_BENCHMARK_BATCHES; let max_bytes = code::max_pages::() * 64 * 1024; @@ -910,6 +944,7 @@ benchmarks! { // because re-writing at an existing key is always more expensive than writing // it at a virgin key. #[skip_meta] + #[pov_mode = Ignored] seal_set_storage { let r in 0 .. API_BENCHMARK_BATCHES/2; let max_key_len = T::MaxStorageKeyLen::get(); @@ -958,6 +993,7 @@ benchmarks! { }: call(origin, instance.addr, 0u32.into(), Weight::MAX, None, vec![]) #[skip_meta] + #[pov_mode = Ignored] seal_set_storage_per_new_kb { let n in 0 .. T::Schedule::get().limits.payload_len / 2048; // half of the max payload_len in kb let max_key_len = T::MaxStorageKeyLen::get(); @@ -1006,6 +1042,7 @@ benchmarks! { }: call(origin, instance.addr, 0u32.into(), Weight::MAX, None, vec![]) #[skip_meta] + #[pov_mode = Ignored] seal_set_storage_per_old_kb { let n in 0 .. T::Schedule::get().limits.payload_len / 2048; // half of the max payload_len in kb let max_key_len = T::MaxStorageKeyLen::get(); @@ -1058,6 +1095,7 @@ benchmarks! { // deleting a non existing key. We generate keys of a maximum length, and have to // reduce batch size in order to make resulting contract code size less than MaxCodeLen. #[skip_meta] + #[pov_mode = Ignored] seal_clear_storage { let r in 0 .. API_BENCHMARK_BATCHES/2; let max_key_len = T::MaxStorageKeyLen::get(); @@ -1105,6 +1143,7 @@ benchmarks! { }: call(origin, instance.addr, 0u32.into(), Weight::MAX, None, vec![]) #[skip_meta] + #[pov_mode = Ignored] seal_clear_storage_per_kb { let n in 0 .. T::Schedule::get().limits.payload_len / 2048; // half of the max payload_len in kb let max_key_len = T::MaxStorageKeyLen::get(); @@ -1152,6 +1191,7 @@ benchmarks! { // We make sure that all storage accesses are to unique keys. #[skip_meta] + #[pov_mode = Ignored] seal_get_storage { let r in 0 .. API_BENCHMARK_BATCHES/2; let max_key_len = T::MaxStorageKeyLen::get(); @@ -1206,6 +1246,7 @@ benchmarks! { }: call(origin, instance.addr, 0u32.into(), Weight::MAX, None, vec![]) #[skip_meta] + #[pov_mode = Ignored] seal_get_storage_per_kb { let n in 0 .. T::Schedule::get().limits.payload_len / 2048; // half of the max payload_len in kb let max_key_len = T::MaxStorageKeyLen::get(); @@ -1261,6 +1302,7 @@ benchmarks! { // We make sure that all storage accesses are to unique keys. #[skip_meta] + #[pov_mode = Ignored] seal_contains_storage { let r in 0 .. API_BENCHMARK_BATCHES/2; let max_key_len = T::MaxStorageKeyLen::get(); @@ -1309,6 +1351,7 @@ benchmarks! { }: call(origin, instance.addr, 0u32.into(), Weight::MAX, None, vec![]) #[skip_meta] + #[pov_mode = Ignored] seal_contains_storage_per_kb { let n in 0 .. T::Schedule::get().limits.payload_len / 2048; // half of the max payload_len in kb let max_key_len = T::MaxStorageKeyLen::get(); @@ -1356,6 +1399,7 @@ benchmarks! { }: call(origin, instance.addr, 0u32.into(), Weight::MAX, None, vec![]) #[skip_meta] + #[pov_mode = Ignored] seal_take_storage { let r in 0 .. API_BENCHMARK_BATCHES/2; let max_key_len = T::MaxStorageKeyLen::get(); @@ -1410,6 +1454,7 @@ benchmarks! { }: call(origin, instance.addr, 0u32.into(), Weight::MAX, None, vec![]) #[skip_meta] + #[pov_mode = Ignored] seal_take_storage_per_kb { let n in 0 .. T::Schedule::get().limits.payload_len / 2048; // half of the max payload_len in kb let max_key_len = T::MaxStorageKeyLen::get(); @@ -1464,6 +1509,7 @@ benchmarks! { }: call(origin, instance.addr, 0u32.into(), Weight::MAX, None, vec![]) // We transfer to unique accounts. + #[pov_mode = Ignored] seal_transfer { let r in 0 .. API_BENCHMARK_BATCHES; let accounts = (0..r * API_BENCHMARK_BATCH_SIZE) @@ -1517,6 +1563,7 @@ benchmarks! { } // We call unique accounts. + #[pov_mode = Ignored] seal_call { let r in 0 .. API_BENCHMARK_BATCHES; let dummy_code = WasmModule::::dummy_with_bytes(0); @@ -1575,6 +1622,7 @@ benchmarks! { let origin = RawOrigin::Signed(instance.caller.clone()); }: call(origin, instance.addr, 0u32.into(), Weight::MAX, None, vec![]) + #[pov_mode = Ignored] seal_delegate_call { let r in 0 .. API_BENCHMARK_BATCHES; let hashes = (0..r * API_BENCHMARK_BATCH_SIZE) @@ -1627,6 +1675,7 @@ benchmarks! { let origin = RawOrigin::Signed(instance.caller); }: call(origin, callee, 0u32.into(), Weight::MAX, None, vec![]) + #[pov_mode = Ignored] seal_call_per_transfer_clone_kb { let t in 0 .. 1; let c in 0 .. code::max_pages::() * 64; @@ -1685,6 +1734,7 @@ benchmarks! { }: call(origin, instance.addr, 0u32.into(), Weight::MAX, None, bytes) // We assume that every instantiate sends at least the minimum balance. + #[pov_mode = Ignored] seal_instantiate { let r in 0 .. API_BENCHMARK_BATCHES; let hashes = (0..r * API_BENCHMARK_BATCH_SIZE) @@ -1798,6 +1848,7 @@ benchmarks! { } } + #[pov_mode = Ignored] seal_instantiate_per_transfer_input_salt_kb { let t in 0 .. 1; let i in 0 .. (code::max_pages::() - 1) * 64; @@ -1892,6 +1943,7 @@ benchmarks! { }: call(origin, instance.addr, 0u32.into(), Weight::MAX, None, vec![]) // Only the overhead of calling the function itself with minimal arguments. + #[pov_mode = Ignored] seal_hash_sha2_256 { let r in 0 .. 1; let instance = Contract::::new(WasmModule::hasher( @@ -1901,6 +1953,7 @@ benchmarks! { }: call(origin, instance.addr, 0u32.into(), Weight::MAX, None, vec![]) // `n`: Input to hash in kilobytes + #[pov_mode = Ignored] seal_hash_sha2_256_per_kb { let n in 0 .. code::max_pages::() * 64; let instance = Contract::::new(WasmModule::hasher( @@ -1910,6 +1963,7 @@ benchmarks! { }: call(origin, instance.addr, 0u32.into(), Weight::MAX, None, vec![]) // Only the overhead of calling the function itself with minimal arguments. + #[pov_mode = Ignored] seal_hash_keccak_256 { let r in 0 .. 1; let instance = Contract::::new(WasmModule::hasher( @@ -1919,6 +1973,7 @@ benchmarks! { }: call(origin, instance.addr, 0u32.into(), Weight::MAX, None, vec![]) // `n`: Input to hash in kilobytes + #[pov_mode = Ignored] seal_hash_keccak_256_per_kb { let n in 0 .. code::max_pages::() * 64; let instance = Contract::::new(WasmModule::hasher( @@ -1928,6 +1983,7 @@ benchmarks! { }: call(origin, instance.addr, 0u32.into(), Weight::MAX, None, vec![]) // Only the overhead of calling the function itself with minimal arguments. + #[pov_mode = Ignored] seal_hash_blake2_256 { let r in 0 .. 1; let instance = Contract::::new(WasmModule::hasher( @@ -1937,6 +1993,7 @@ benchmarks! { }: call(origin, instance.addr, 0u32.into(), Weight::MAX, None, vec![]) // `n`: Input to hash in kilobytes + #[pov_mode = Ignored] seal_hash_blake2_256_per_kb { let n in 0 .. code::max_pages::() * 64; let instance = Contract::::new(WasmModule::hasher( @@ -1946,6 +2003,7 @@ benchmarks! { }: call(origin, instance.addr, 0u32.into(), Weight::MAX, None, vec![]) // Only the overhead of calling the function itself with minimal arguments. + #[pov_mode = Ignored] seal_hash_blake2_128 { let r in 0 .. 1; let instance = Contract::::new(WasmModule::hasher( @@ -1955,6 +2013,7 @@ benchmarks! { }: call(origin, instance.addr, 0u32.into(), Weight::MAX, None, vec![]) // `n`: Input to hash in kilobytes + #[pov_mode = Ignored] seal_hash_blake2_128_per_kb { let n in 0 .. code::max_pages::() * 64; let instance = Contract::::new(WasmModule::hasher( @@ -1965,6 +2024,7 @@ benchmarks! { // Only calling the function itself with valid arguments. // It generates different private keys and signatures for the message "Hello world". + #[pov_mode = Ignored] seal_ecdsa_recover { let r in 0 .. 1; @@ -2013,6 +2073,7 @@ benchmarks! { // Only calling the function itself for the list of // generated different ECDSA keys. + #[pov_mode = Ignored] seal_ecdsa_to_eth_address { let r in 0 .. 1; let key_type = sp_core::crypto::KeyTypeId(*b"code"); @@ -2048,6 +2109,7 @@ benchmarks! { let origin = RawOrigin::Signed(instance.caller.clone()); }: call(origin, instance.addr, 0u32.into(), Weight::MAX, None, vec![]) + #[pov_mode = Ignored] seal_set_code_hash { let r in 0 .. API_BENCHMARK_BATCHES; let code_hashes = (0..r * API_BENCHMARK_BATCH_SIZE) @@ -2088,6 +2150,7 @@ benchmarks! { let origin = RawOrigin::Signed(instance.caller.clone()); }: call(origin, instance.addr, 0u32.into(), Weight::MAX, None, vec![]) + #[pov_mode = Ignored] seal_reentrance_count { let r in 0 .. API_BENCHMARK_BATCHES; let code = WasmModule::::from(ModuleDefinition { @@ -2108,6 +2171,7 @@ benchmarks! { let origin = RawOrigin::Signed(instance.caller.clone()); }: call(origin, instance.addr, 0u32.into(), Weight::MAX, None, vec![]) + #[pov_mode = Ignored] seal_account_reentrance_count { let r in 0 .. API_BENCHMARK_BATCHES; let dummy_code = WasmModule::::dummy_with_bytes(0); @@ -2141,6 +2205,7 @@ benchmarks! { let origin = RawOrigin::Signed(instance.caller.clone()); }: call(origin, instance.addr, 0u32.into(), Weight::MAX, None, vec![]) + #[pov_mode = Ignored] seal_instantiation_nonce { let r in 0 .. API_BENCHMARK_BATCHES; let code = WasmModule::::from(ModuleDefinition { @@ -2171,6 +2236,7 @@ benchmarks! { // The weight that would result from the respective benchmark we call: `w_bench`. // // w_i{32,64}const = w_drop = w_bench / 2 + #[pov_mode = Ignored] instr_i64const { let r in 0 .. INSTR_BENCHMARK_BATCHES; let mut sbox = Sandbox::from(&WasmModule::::from(ModuleDefinition { @@ -2185,6 +2251,7 @@ benchmarks! { } // w_i{32,64}load = w_bench - 2 * w_param + #[pov_mode = Ignored] instr_i64load { let r in 0 .. INSTR_BENCHMARK_BATCHES; let mut sbox = Sandbox::from(&WasmModule::::from(ModuleDefinition { @@ -2201,6 +2268,7 @@ benchmarks! { } // w_i{32,64}store{...} = w_bench - 2 * w_param + #[pov_mode = Ignored] instr_i64store { let r in 0 .. INSTR_BENCHMARK_BATCHES; let mut sbox = Sandbox::from(&WasmModule::::from(ModuleDefinition { @@ -2217,6 +2285,7 @@ benchmarks! { } // w_select = w_bench - 4 * w_param + #[pov_mode = Ignored] instr_select { let r in 0 .. INSTR_BENCHMARK_BATCHES; let mut sbox = Sandbox::from(&WasmModule::::from(ModuleDefinition { @@ -2234,6 +2303,7 @@ benchmarks! { } // w_if = w_bench - 3 * w_param + #[pov_mode = Ignored] instr_if { let r in 0 .. INSTR_BENCHMARK_BATCHES; let mut sbox = Sandbox::from(&WasmModule::::from(ModuleDefinition { @@ -2254,6 +2324,7 @@ benchmarks! { // w_br = w_bench - 2 * w_param // Block instructions are not counted. + #[pov_mode = Ignored] instr_br { let r in 0 .. INSTR_BENCHMARK_BATCHES; let mut sbox = Sandbox::from(&WasmModule::::from(ModuleDefinition { @@ -2280,6 +2351,7 @@ benchmarks! { // w_br_if = w_bench - 3 * w_param // Block instructions are not counted. + #[pov_mode = Ignored] instr_br_if { let r in 0 .. INSTR_BENCHMARK_BATCHES; let mut sbox = Sandbox::from(&WasmModule::::from(ModuleDefinition { @@ -2307,6 +2379,7 @@ benchmarks! { // w_br_table = w_bench - 3 * w_param // Block instructions are not counted. + #[pov_mode = Ignored] instr_br_table { let r in 0 .. INSTR_BENCHMARK_BATCHES; let table = Box::new(BrTableData { @@ -2337,6 +2410,7 @@ benchmarks! { } // w_br_table_per_entry = w_bench + #[pov_mode = Ignored] instr_br_table_per_entry { let e in 1 .. T::Schedule::get().limits.br_table_size; let entry: Vec = [0, 1].iter() @@ -2371,6 +2445,7 @@ benchmarks! { } // w_call = w_bench - 2 * w_param + #[pov_mode = Ignored] instr_call { let r in 0 .. INSTR_BENCHMARK_BATCHES; let mut sbox = Sandbox::from(&WasmModule::::from(ModuleDefinition { @@ -2391,6 +2466,7 @@ benchmarks! { } // w_call_indrect = w_bench - 3 * w_param + #[pov_mode = Ignored] instr_call_indirect { let r in 0 .. INSTR_BENCHMARK_BATCHES; let num_elements = T::Schedule::get().limits.table_size; @@ -2421,6 +2497,7 @@ benchmarks! { // Calling a function indirectly causes it to go through a thunk function whose runtime // linearly depend on the amount of parameters to this function. // Please note that this is not necessary with a direct call. + #[pov_mode = Ignored] instr_call_indirect_per_param { let p in 0 .. T::Schedule::get().limits.parameters; let num_elements = T::Schedule::get().limits.table_size; @@ -2450,6 +2527,7 @@ benchmarks! { } // w_per_local = w_bench + #[pov_mode = Ignored] instr_call_per_local { let l in 0 .. T::Schedule::get().limits.locals; let mut aux_body = body::plain(vec![ @@ -2468,6 +2546,7 @@ benchmarks! { } // w_local_get = w_bench - 1 * w_param + #[pov_mode = Ignored] instr_local_get { let r in 0 .. INSTR_BENCHMARK_BATCHES; let max_locals = T::Schedule::get().limits.locals; @@ -2485,6 +2564,7 @@ benchmarks! { } // w_local_set = w_bench - 1 * w_param + #[pov_mode = Ignored] instr_local_set { let r in 0 .. INSTR_BENCHMARK_BATCHES; let max_locals = T::Schedule::get().limits.locals; @@ -2502,6 +2582,7 @@ benchmarks! { } // w_local_tee = w_bench - 2 * w_param + #[pov_mode = Ignored] instr_local_tee { let r in 0 .. INSTR_BENCHMARK_BATCHES; let max_locals = T::Schedule::get().limits.locals; @@ -2520,6 +2601,7 @@ benchmarks! { } // w_global_get = w_bench - 1 * w_param + #[pov_mode = Ignored] instr_global_get { let r in 0 .. INSTR_BENCHMARK_BATCHES; let max_globals = T::Schedule::get().limits.globals; @@ -2536,6 +2618,7 @@ benchmarks! { } // w_global_set = w_bench - 1 * w_param + #[pov_mode = Ignored] instr_global_set { let r in 0 .. INSTR_BENCHMARK_BATCHES; let max_globals = T::Schedule::get().limits.globals; @@ -2552,6 +2635,7 @@ benchmarks! { } // w_memory_get = w_bench - 1 * w_param + #[pov_mode = Ignored] instr_memory_current { let r in 0 .. INSTR_BENCHMARK_BATCHES; let mut sbox = Sandbox::from(&WasmModule::::from(ModuleDefinition { @@ -2571,6 +2655,7 @@ benchmarks! { // Therefore the repeat count is limited by the maximum memory any contract can have. // Using a contract with more memory will skew the benchmark because the runtime of grow // depends on how much memory is already allocated. + #[pov_mode = Ignored] instr_memory_grow { let r in 0 .. 1; let max_pages = ImportedMemory::max::().max_pages; @@ -2593,6 +2678,7 @@ benchmarks! { // Unary numeric instructions. // All use w = w_bench - 2 * w_param. + #[pov_mode = Ignored] instr_i64clz { let r in 0 .. INSTR_BENCHMARK_BATCHES; let mut sbox = Sandbox::from(&WasmModule::::unary_instr( @@ -2603,6 +2689,7 @@ benchmarks! { sbox.invoke(); } + #[pov_mode = Ignored] instr_i64ctz { let r in 0 .. INSTR_BENCHMARK_BATCHES; let mut sbox = Sandbox::from(&WasmModule::::unary_instr( @@ -2613,6 +2700,7 @@ benchmarks! { sbox.invoke(); } + #[pov_mode = Ignored] instr_i64popcnt { let r in 0 .. INSTR_BENCHMARK_BATCHES; let mut sbox = Sandbox::from(&WasmModule::::unary_instr( @@ -2623,6 +2711,7 @@ benchmarks! { sbox.invoke(); } + #[pov_mode = Ignored] instr_i64eqz { let r in 0 .. INSTR_BENCHMARK_BATCHES; let mut sbox = Sandbox::from(&WasmModule::::unary_instr( @@ -2633,6 +2722,7 @@ benchmarks! { sbox.invoke(); } + #[pov_mode = Ignored] instr_i64extendsi32 { let r in 0 .. INSTR_BENCHMARK_BATCHES; let mut sbox = Sandbox::from(&WasmModule::::from(ModuleDefinition { @@ -2647,6 +2737,7 @@ benchmarks! { sbox.invoke(); } + #[pov_mode = Ignored] instr_i64extendui32 { let r in 0 .. INSTR_BENCHMARK_BATCHES; let mut sbox = Sandbox::from(&WasmModule::::from(ModuleDefinition { @@ -2661,6 +2752,7 @@ benchmarks! { sbox.invoke(); } + #[pov_mode = Ignored] instr_i32wrapi64 { let r in 0 .. INSTR_BENCHMARK_BATCHES; let mut sbox = Sandbox::from(&WasmModule::::unary_instr( @@ -2674,6 +2766,7 @@ benchmarks! { // Binary numeric instructions. // All use w = w_bench - 3 * w_param. + #[pov_mode = Ignored] instr_i64eq { let r in 0 .. INSTR_BENCHMARK_BATCHES; let mut sbox = Sandbox::from(&WasmModule::::binary_instr( @@ -2684,6 +2777,7 @@ benchmarks! { sbox.invoke(); } + #[pov_mode = Ignored] instr_i64ne { let r in 0 .. INSTR_BENCHMARK_BATCHES; let mut sbox = Sandbox::from(&WasmModule::::binary_instr( @@ -2694,6 +2788,7 @@ benchmarks! { sbox.invoke(); } + #[pov_mode = Ignored] instr_i64lts { let r in 0 .. INSTR_BENCHMARK_BATCHES; let mut sbox = Sandbox::from(&WasmModule::::binary_instr( @@ -2704,6 +2799,7 @@ benchmarks! { sbox.invoke(); } + #[pov_mode = Ignored] instr_i64ltu { let r in 0 .. INSTR_BENCHMARK_BATCHES; let mut sbox = Sandbox::from(&WasmModule::::binary_instr( @@ -2714,6 +2810,7 @@ benchmarks! { sbox.invoke(); } + #[pov_mode = Ignored] instr_i64gts { let r in 0 .. INSTR_BENCHMARK_BATCHES; let mut sbox = Sandbox::from(&WasmModule::::binary_instr( @@ -2724,6 +2821,7 @@ benchmarks! { sbox.invoke(); } + #[pov_mode = Ignored] instr_i64gtu { let r in 0 .. INSTR_BENCHMARK_BATCHES; let mut sbox = Sandbox::from(&WasmModule::::binary_instr( @@ -2734,6 +2832,7 @@ benchmarks! { sbox.invoke(); } + #[pov_mode = Ignored] instr_i64les { let r in 0 .. INSTR_BENCHMARK_BATCHES; let mut sbox = Sandbox::from(&WasmModule::::binary_instr( @@ -2744,6 +2843,7 @@ benchmarks! { sbox.invoke(); } + #[pov_mode = Ignored] instr_i64leu { let r in 0 .. INSTR_BENCHMARK_BATCHES; let mut sbox = Sandbox::from(&WasmModule::::binary_instr( @@ -2754,6 +2854,7 @@ benchmarks! { sbox.invoke(); } + #[pov_mode = Ignored] instr_i64ges { let r in 0 .. INSTR_BENCHMARK_BATCHES; let mut sbox = Sandbox::from(&WasmModule::::binary_instr( @@ -2764,6 +2865,7 @@ benchmarks! { sbox.invoke(); } + #[pov_mode = Ignored] instr_i64geu { let r in 0 .. INSTR_BENCHMARK_BATCHES; let mut sbox = Sandbox::from(&WasmModule::::binary_instr( @@ -2774,6 +2876,7 @@ benchmarks! { sbox.invoke(); } + #[pov_mode = Ignored] instr_i64add { let r in 0 .. INSTR_BENCHMARK_BATCHES; let mut sbox = Sandbox::from(&WasmModule::::binary_instr( @@ -2784,6 +2887,7 @@ benchmarks! { sbox.invoke(); } + #[pov_mode = Ignored] instr_i64sub { let r in 0 .. INSTR_BENCHMARK_BATCHES; let mut sbox = Sandbox::from(&WasmModule::::binary_instr( @@ -2794,6 +2898,7 @@ benchmarks! { sbox.invoke(); } + #[pov_mode = Ignored] instr_i64mul { let r in 0 .. INSTR_BENCHMARK_BATCHES; let mut sbox = Sandbox::from(&WasmModule::::binary_instr( @@ -2804,6 +2909,7 @@ benchmarks! { sbox.invoke(); } + #[pov_mode = Ignored] instr_i64divs { let r in 0 .. INSTR_BENCHMARK_BATCHES; let mut sbox = Sandbox::from(&WasmModule::::binary_instr( @@ -2814,6 +2920,7 @@ benchmarks! { sbox.invoke(); } + #[pov_mode = Ignored] instr_i64divu { let r in 0 .. INSTR_BENCHMARK_BATCHES; let mut sbox = Sandbox::from(&WasmModule::::binary_instr( @@ -2824,6 +2931,7 @@ benchmarks! { sbox.invoke(); } + #[pov_mode = Ignored] instr_i64rems { let r in 0 .. INSTR_BENCHMARK_BATCHES; let mut sbox = Sandbox::from(&WasmModule::::binary_instr( @@ -2834,6 +2942,7 @@ benchmarks! { sbox.invoke(); } + #[pov_mode = Ignored] instr_i64remu { let r in 0 .. INSTR_BENCHMARK_BATCHES; let mut sbox = Sandbox::from(&WasmModule::::binary_instr( @@ -2844,6 +2953,7 @@ benchmarks! { sbox.invoke(); } + #[pov_mode = Ignored] instr_i64and { let r in 0 .. INSTR_BENCHMARK_BATCHES; let mut sbox = Sandbox::from(&WasmModule::::binary_instr( @@ -2854,6 +2964,7 @@ benchmarks! { sbox.invoke(); } + #[pov_mode = Ignored] instr_i64or { let r in 0 .. INSTR_BENCHMARK_BATCHES; let mut sbox = Sandbox::from(&WasmModule::::binary_instr( @@ -2864,6 +2975,7 @@ benchmarks! { sbox.invoke(); } + #[pov_mode = Ignored] instr_i64xor { let r in 0 .. INSTR_BENCHMARK_BATCHES; let mut sbox = Sandbox::from(&WasmModule::::binary_instr( @@ -2874,6 +2986,7 @@ benchmarks! { sbox.invoke(); } + #[pov_mode = Ignored] instr_i64shl { let r in 0 .. INSTR_BENCHMARK_BATCHES; let mut sbox = Sandbox::from(&WasmModule::::binary_instr( @@ -2884,6 +2997,7 @@ benchmarks! { sbox.invoke(); } + #[pov_mode = Ignored] instr_i64shrs { let r in 0 .. INSTR_BENCHMARK_BATCHES; let mut sbox = Sandbox::from(&WasmModule::::binary_instr( @@ -2894,6 +3008,7 @@ benchmarks! { sbox.invoke(); } + #[pov_mode = Ignored] instr_i64shru { let r in 0 .. INSTR_BENCHMARK_BATCHES; let mut sbox = Sandbox::from(&WasmModule::::binary_instr( @@ -2904,6 +3019,7 @@ benchmarks! { sbox.invoke(); } + #[pov_mode = Ignored] instr_i64rotl { let r in 0 .. INSTR_BENCHMARK_BATCHES; let mut sbox = Sandbox::from(&WasmModule::::binary_instr( @@ -2914,6 +3030,7 @@ benchmarks! { sbox.invoke(); } + #[pov_mode = Ignored] instr_i64rotr { let r in 0 .. INSTR_BENCHMARK_BATCHES; let mut sbox = Sandbox::from(&WasmModule::::binary_instr( @@ -2931,6 +3048,7 @@ benchmarks! { // --features runtime-benchmarks -- benchmark pallet --extra --dev --execution=native \ // -p pallet_contracts -e print_schedule --no-median-slopes --no-min-squares #[extra] + #[pov_mode = Ignored] print_schedule { #[cfg(feature = "std")] { @@ -2956,6 +3074,7 @@ benchmarks! { // `g` is used to enable gas instrumentation to compare the performance impact of // that instrumentation at runtime. #[extra] + #[pov_mode = Ignored] ink_erc20_transfer { let g in 0 .. 1; let gas_metering = g != 0; @@ -2994,6 +3113,7 @@ benchmarks! { // `g` is used to enable gas instrumentation to compare the performance impact of // that instrumentation at runtime. #[extra] + #[pov_mode = Ignored] solang_erc20_transfer { let g in 0 .. 1; let gas_metering = g != 0; diff --git a/frame/contracts/src/lib.rs b/frame/contracts/src/lib.rs index f97b4b790..c067b2644 100644 --- a/frame/contracts/src/lib.rs +++ b/frame/contracts/src/lib.rs @@ -81,7 +81,7 @@ //! WebAssembly based smart contracts in the Rust programming language. #![cfg_attr(not(feature = "std"), no_std)] -#![cfg_attr(feature = "runtime-benchmarks", recursion_limit = "512")] +#![cfg_attr(feature = "runtime-benchmarks", recursion_limit = "1024")] #[macro_use] mod gas; diff --git a/frame/contracts/src/weights.rs b/frame/contracts/src/weights.rs index 7ae1a9c59..d9ec1b10b 100644 --- a/frame/contracts/src/weights.rs +++ b/frame/contracts/src/weights.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2022 Parity Technologies (UK) Ltd. +// Copyright (C) 2023 Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); @@ -18,25 +18,25 @@ //! Autogenerated weights for pallet_contracts //! //! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev -//! DATE: 2023-01-10, STEPS: `50`, REPEAT: 20, LOW RANGE: `[]`, HIGH RANGE: `[]` -//! HOSTNAME: `bm3`, CPU: `Intel(R) Core(TM) i7-7700K CPU @ 4.20GHz` +//! DATE: 2023-01-24, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! WORST CASE MAP SIZE: `1000000` +//! HOSTNAME: `bm2`, CPU: `Intel(R) Core(TM) i7-7700K CPU @ 4.20GHz` //! EXECUTION: Some(Wasm), WASM-EXECUTION: Compiled, CHAIN: Some("dev"), DB CACHE: 1024 // Executed Command: -// /home/benchbot/cargo_target_dir/production/substrate +// ./target/production/substrate // benchmark // pallet +// --chain=dev // --steps=50 // --repeat=20 +// --pallet=pallet_contracts // --extrinsic=* // --execution=wasm // --wasm-execution=compiled // --heap-pages=4096 -// --json-file=/var/lib/gitlab-runner/builds/zyw4fam_/0/parity/mirrors/substrate/.git/.artifacts/bench.json -// --pallet=pallet_contracts -// --chain=dev -// --header=./HEADER-APACHE2 // --output=./frame/contracts/src/weights.rs +// --header=./HEADER-APACHE2 // --template=./.maintain/frame-weight-template.hbs #![cfg_attr(rustfmt, rustfmt_skip)] @@ -170,2554 +170,3810 @@ pub trait WeightInfo { /// Weights for pallet_contracts using the Substrate node and recommended hardware. pub struct SubstrateWeight(PhantomData); impl WeightInfo for SubstrateWeight { - // Storage: Contracts DeletionQueue (r:1 w:0) + /// Storage: Contracts DeletionQueue (r:1 w:0) + /// Proof: Contracts DeletionQueue (max_values: Some(1), max_size: Some(16642), added: 17137, mode: Ignored) fn on_process_deletion_queue_batch() -> Weight { - // Minimum execution time: 3_326 nanoseconds. - Weight::from_ref_time(3_433_000) - .saturating_add(T::DbWeight::get().reads(1)) - } - // Storage: Skipped Metadata (r:0 w:0) + // Proof Size summary in bytes: + // Measured: `109` + // Estimated: `0` + // Minimum execution time: 2_305 nanoseconds. + Weight::from_ref_time(2_560_000) + .saturating_add(T::DbWeight::get().reads(1_u64)) + } + /// Storage: Skipped Metadata (r:0 w:0) + /// Proof Skipped: Skipped Metadata (max_values: None, max_size: None, mode: Ignored) /// The range of component `k` is `[0, 1024]`. fn on_initialize_per_trie_key(k: u32, ) -> Weight { - // Minimum execution time: 15_703 nanoseconds. - Weight::from_ref_time(15_288_927) - // Standard Error: 951 - .saturating_add(Weight::from_ref_time(940_816).saturating_mul(k.into())) - .saturating_add(T::DbWeight::get().reads(1)) - .saturating_add(T::DbWeight::get().writes(1)) + // Proof Size summary in bytes: + // Measured: `481 + k * (69 ±0)` + // Estimated: `0` + // Minimum execution time: 9_311 nanoseconds. + Weight::from_ref_time(5_419_288) + // Standard Error: 562 + .saturating_add(Weight::from_ref_time(911_962).saturating_mul(k.into())) + .saturating_add(T::DbWeight::get().reads(1_u64)) + .saturating_add(T::DbWeight::get().writes(1_u64)) .saturating_add(T::DbWeight::get().writes((1_u64).saturating_mul(k.into()))) } - // Storage: Contracts DeletionQueue (r:1 w:0) + /// Storage: Contracts DeletionQueue (r:1 w:1) + /// Proof: Contracts DeletionQueue (max_values: Some(1), max_size: Some(16642), added: 17137, mode: Ignored) /// The range of component `q` is `[0, 128]`. fn on_initialize_per_queue_item(q: u32, ) -> Weight { - // Minimum execution time: 3_548 nanoseconds. - Weight::from_ref_time(15_764_121) - // Standard Error: 3_339 - .saturating_add(Weight::from_ref_time(1_214_890).saturating_mul(q.into())) - .saturating_add(T::DbWeight::get().reads(1)) - .saturating_add(T::DbWeight::get().writes(1)) - } - // Storage: Contracts PristineCode (r:1 w:0) - // Storage: Contracts CodeStorage (r:0 w:1) - /// The range of component `c` is `[0, 64226]`. + // Proof Size summary in bytes: + // Measured: `281 + q * (33 ±0)` + // Estimated: `0` + // Minimum execution time: 2_288 nanoseconds. + Weight::from_ref_time(9_442_437) + // Standard Error: 2_720 + .saturating_add(Weight::from_ref_time(1_076_950).saturating_mul(q.into())) + .saturating_add(T::DbWeight::get().reads(1_u64)) + .saturating_add(T::DbWeight::get().writes(1_u64)) + } + /// Storage: Contracts PristineCode (r:1 w:0) + /// Proof: Contracts PristineCode (max_values: None, max_size: Some(125988), added: 128463, mode: Ignored) + /// Storage: Contracts CodeStorage (r:0 w:1) + /// Proof: Contracts CodeStorage (max_values: None, max_size: Some(126001), added: 128476, mode: Ignored) + /// The range of component `c` is `[0, 61717]`. fn reinstrument(c: u32, ) -> Weight { - // Minimum execution time: 29_801 nanoseconds. - Weight::from_ref_time(27_932_706) - // Standard Error: 49 - .saturating_add(Weight::from_ref_time(50_532).saturating_mul(c.into())) - .saturating_add(T::DbWeight::get().reads(1)) - .saturating_add(T::DbWeight::get().writes(1)) - } - // Storage: Contracts ContractInfoOf (r:1 w:1) - // Storage: Contracts CodeStorage (r:1 w:0) - // Storage: Timestamp Now (r:1 w:0) - // Storage: System Account (r:1 w:1) - // Storage: System EventTopics (r:2 w:2) - /// The range of component `c` is `[0, 131072]`. + // Proof Size summary in bytes: + // Measured: `270 + c * (1 ±0)` + // Estimated: `0` + // Minimum execution time: 27_539 nanoseconds. + Weight::from_ref_time(23_554_889) + // Standard Error: 56 + .saturating_add(Weight::from_ref_time(46_766).saturating_mul(c.into())) + .saturating_add(T::DbWeight::get().reads(1_u64)) + .saturating_add(T::DbWeight::get().writes(1_u64)) + } + /// Storage: Contracts ContractInfoOf (r:1 w:1) + /// Proof: Contracts ContractInfoOf (max_values: None, max_size: Some(258), added: 2733, mode: Ignored) + /// Storage: Contracts CodeStorage (r:1 w:0) + /// Proof: Contracts CodeStorage (max_values: None, max_size: Some(126001), added: 128476, mode: Ignored) + /// Storage: Timestamp Now (r:1 w:0) + /// Proof: Timestamp Now (max_values: Some(1), max_size: Some(8), added: 503, mode: Ignored) + /// Storage: System Account (r:1 w:1) + /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: Ignored) + /// Storage: System EventTopics (r:2 w:2) + /// Proof Skipped: System EventTopics (max_values: None, max_size: None, mode: Ignored) + /// The range of component `c` is `[0, 125952]`. fn call_with_code_per_byte(c: u32, ) -> Weight { - // Minimum execution time: 307_894 nanoseconds. - Weight::from_ref_time(322_346_319) - // Standard Error: 25 - .saturating_add(Weight::from_ref_time(30_678).saturating_mul(c.into())) - .saturating_add(T::DbWeight::get().reads(6)) - .saturating_add(T::DbWeight::get().writes(4)) - } - // Storage: Contracts CodeStorage (r:1 w:1) - // Storage: Contracts Nonce (r:1 w:1) - // Storage: Contracts ContractInfoOf (r:1 w:1) - // Storage: Timestamp Now (r:1 w:0) - // Storage: System Account (r:1 w:1) - // Storage: System EventTopics (r:3 w:3) - // Storage: Contracts PristineCode (r:0 w:1) - // Storage: Contracts OwnerInfoOf (r:0 w:1) - /// The range of component `c` is `[0, 64226]`. + // Proof Size summary in bytes: + // Measured: `771` + // Estimated: `0` + // Minimum execution time: 297_710 nanoseconds. + Weight::from_ref_time(307_327_529) + // Standard Error: 18 + .saturating_add(Weight::from_ref_time(29_849).saturating_mul(c.into())) + .saturating_add(T::DbWeight::get().reads(6_u64)) + .saturating_add(T::DbWeight::get().writes(4_u64)) + } + /// Storage: Contracts CodeStorage (r:1 w:1) + /// Proof: Contracts CodeStorage (max_values: None, max_size: Some(126001), added: 128476, mode: Ignored) + /// Storage: Contracts Nonce (r:1 w:1) + /// Proof: Contracts Nonce (max_values: Some(1), max_size: Some(8), added: 503, mode: Ignored) + /// Storage: Contracts ContractInfoOf (r:1 w:1) + /// Proof: Contracts ContractInfoOf (max_values: None, max_size: Some(258), added: 2733, mode: Ignored) + /// Storage: Timestamp Now (r:1 w:0) + /// Proof: Timestamp Now (max_values: Some(1), max_size: Some(8), added: 503, mode: Ignored) + /// Storage: System Account (r:1 w:1) + /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: Ignored) + /// Storage: System EventTopics (r:3 w:3) + /// Proof Skipped: System EventTopics (max_values: None, max_size: None, mode: Ignored) + /// Storage: Contracts PristineCode (r:0 w:1) + /// Proof: Contracts PristineCode (max_values: None, max_size: Some(125988), added: 128463, mode: Ignored) + /// Storage: Contracts OwnerInfoOf (r:0 w:1) + /// Proof: Contracts OwnerInfoOf (max_values: None, max_size: Some(88), added: 2563, mode: Ignored) + /// The range of component `c` is `[0, 61717]`. /// The range of component `i` is `[0, 1048576]`. /// The range of component `s` is `[0, 1048576]`. fn instantiate_with_code(c: u32, i: u32, s: u32, ) -> Weight { - // Minimum execution time: 3_610_424 nanoseconds. - Weight::from_ref_time(583_426_386) - // Standard Error: 277 - .saturating_add(Weight::from_ref_time(90_224).saturating_mul(c.into())) + // Proof Size summary in bytes: + // Measured: `257` + // Estimated: `0` + // Minimum execution time: 3_586_223 nanoseconds. + Weight::from_ref_time(561_614_281) + // Standard Error: 274 + .saturating_add(Weight::from_ref_time(87_557).saturating_mul(c.into())) // Standard Error: 16 - .saturating_add(Weight::from_ref_time(1_325).saturating_mul(i.into())) + .saturating_add(Weight::from_ref_time(1_307).saturating_mul(i.into())) // Standard Error: 16 - .saturating_add(Weight::from_ref_time(1_727).saturating_mul(s.into())) - .saturating_add(T::DbWeight::get().reads(8)) - .saturating_add(T::DbWeight::get().writes(9)) - } - // Storage: Contracts CodeStorage (r:1 w:1) - // Storage: Contracts Nonce (r:1 w:1) - // Storage: Contracts ContractInfoOf (r:1 w:1) - // Storage: Timestamp Now (r:1 w:0) - // Storage: System Account (r:1 w:1) - // Storage: Contracts OwnerInfoOf (r:1 w:1) - // Storage: System EventTopics (r:2 w:2) + .saturating_add(Weight::from_ref_time(1_721).saturating_mul(s.into())) + .saturating_add(T::DbWeight::get().reads(8_u64)) + .saturating_add(T::DbWeight::get().writes(9_u64)) + } + /// Storage: Contracts CodeStorage (r:1 w:1) + /// Proof: Contracts CodeStorage (max_values: None, max_size: Some(126001), added: 128476, mode: Ignored) + /// Storage: Contracts Nonce (r:1 w:1) + /// Proof: Contracts Nonce (max_values: Some(1), max_size: Some(8), added: 503, mode: Ignored) + /// Storage: Contracts ContractInfoOf (r:1 w:1) + /// Proof: Contracts ContractInfoOf (max_values: None, max_size: Some(258), added: 2733, mode: Ignored) + /// Storage: Timestamp Now (r:1 w:0) + /// Proof: Timestamp Now (max_values: Some(1), max_size: Some(8), added: 503, mode: Ignored) + /// Storage: System Account (r:1 w:1) + /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: Ignored) + /// Storage: Contracts OwnerInfoOf (r:1 w:1) + /// Proof: Contracts OwnerInfoOf (max_values: None, max_size: Some(88), added: 2563, mode: Ignored) + /// Storage: System EventTopics (r:2 w:2) + /// Proof Skipped: System EventTopics (max_values: None, max_size: None, mode: Ignored) /// The range of component `i` is `[0, 1048576]`. /// The range of component `s` is `[0, 1048576]`. fn instantiate(i: u32, s: u32, ) -> Weight { - // Minimum execution time: 1_892_996 nanoseconds. - Weight::from_ref_time(202_958_196) + // Proof Size summary in bytes: + // Measured: `533` + // Estimated: `0` + // Minimum execution time: 1_885_437 nanoseconds. + Weight::from_ref_time(199_943_867) // Standard Error: 8 - .saturating_add(Weight::from_ref_time(1_627).saturating_mul(i.into())) + .saturating_add(Weight::from_ref_time(1_611).saturating_mul(i.into())) // Standard Error: 8 - .saturating_add(Weight::from_ref_time(1_755).saturating_mul(s.into())) - .saturating_add(T::DbWeight::get().reads(8)) - .saturating_add(T::DbWeight::get().writes(7)) - } - // Storage: Contracts ContractInfoOf (r:1 w:1) - // Storage: Contracts CodeStorage (r:1 w:0) - // Storage: Timestamp Now (r:1 w:0) - // Storage: System Account (r:1 w:1) - // Storage: System EventTopics (r:2 w:2) + .saturating_add(Weight::from_ref_time(1_737).saturating_mul(s.into())) + .saturating_add(T::DbWeight::get().reads(8_u64)) + .saturating_add(T::DbWeight::get().writes(7_u64)) + } + /// Storage: Contracts ContractInfoOf (r:1 w:1) + /// Proof: Contracts ContractInfoOf (max_values: None, max_size: Some(258), added: 2733, mode: Ignored) + /// Storage: Contracts CodeStorage (r:1 w:0) + /// Proof: Contracts CodeStorage (max_values: None, max_size: Some(126001), added: 128476, mode: Ignored) + /// Storage: Timestamp Now (r:1 w:0) + /// Proof: Timestamp Now (max_values: Some(1), max_size: Some(8), added: 503, mode: Ignored) + /// Storage: System Account (r:1 w:1) + /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: Ignored) + /// Storage: System EventTopics (r:2 w:2) + /// Proof Skipped: System EventTopics (max_values: None, max_size: None, mode: Ignored) fn call() -> Weight { - // Minimum execution time: 157_347 nanoseconds. - Weight::from_ref_time(159_084_000) - .saturating_add(T::DbWeight::get().reads(6)) - .saturating_add(T::DbWeight::get().writes(4)) - } - // Storage: Contracts CodeStorage (r:1 w:1) - // Storage: System EventTopics (r:1 w:1) - // Storage: Contracts PristineCode (r:0 w:1) - // Storage: Contracts OwnerInfoOf (r:0 w:1) - /// The range of component `c` is `[0, 64226]`. + // Proof Size summary in bytes: + // Measured: `823` + // Estimated: `0` + // Minimum execution time: 150_604 nanoseconds. + Weight::from_ref_time(151_777_000) + .saturating_add(T::DbWeight::get().reads(6_u64)) + .saturating_add(T::DbWeight::get().writes(4_u64)) + } + /// Storage: Contracts CodeStorage (r:1 w:1) + /// Proof: Contracts CodeStorage (max_values: None, max_size: Some(126001), added: 128476, mode: Ignored) + /// Storage: System EventTopics (r:1 w:1) + /// Proof Skipped: System EventTopics (max_values: None, max_size: None, mode: Ignored) + /// Storage: Contracts PristineCode (r:0 w:1) + /// Proof: Contracts PristineCode (max_values: None, max_size: Some(125988), added: 128463, mode: Ignored) + /// Storage: Contracts OwnerInfoOf (r:0 w:1) + /// Proof: Contracts OwnerInfoOf (max_values: None, max_size: Some(88), added: 2563, mode: Ignored) + /// The range of component `c` is `[0, 61717]`. fn upload_code(c: u32, ) -> Weight { - // Minimum execution time: 299_987 nanoseconds. - Weight::from_ref_time(305_274_879) - // Standard Error: 72 - .saturating_add(Weight::from_ref_time(91_916).saturating_mul(c.into())) - .saturating_add(T::DbWeight::get().reads(2)) - .saturating_add(T::DbWeight::get().writes(4)) - } - // Storage: Contracts OwnerInfoOf (r:1 w:1) - // Storage: System EventTopics (r:1 w:1) - // Storage: Contracts CodeStorage (r:0 w:1) - // Storage: Contracts PristineCode (r:0 w:1) + // Proof Size summary in bytes: + // Measured: `109` + // Estimated: `0` + // Minimum execution time: 295_505 nanoseconds. + Weight::from_ref_time(305_609_098) + // Standard Error: 58 + .saturating_add(Weight::from_ref_time(88_676).saturating_mul(c.into())) + .saturating_add(T::DbWeight::get().reads(2_u64)) + .saturating_add(T::DbWeight::get().writes(4_u64)) + } + /// Storage: Contracts OwnerInfoOf (r:1 w:1) + /// Proof: Contracts OwnerInfoOf (max_values: None, max_size: Some(88), added: 2563, mode: Ignored) + /// Storage: System EventTopics (r:1 w:1) + /// Proof Skipped: System EventTopics (max_values: None, max_size: None, mode: Ignored) + /// Storage: Contracts CodeStorage (r:0 w:1) + /// Proof: Contracts CodeStorage (max_values: None, max_size: Some(126001), added: 128476, mode: Ignored) + /// Storage: Contracts PristineCode (r:0 w:1) + /// Proof: Contracts PristineCode (max_values: None, max_size: Some(125988), added: 128463, mode: Ignored) fn remove_code() -> Weight { - // Minimum execution time: 40_795 nanoseconds. - Weight::from_ref_time(41_297_000) - .saturating_add(T::DbWeight::get().reads(2)) - .saturating_add(T::DbWeight::get().writes(4)) - } - // Storage: Contracts ContractInfoOf (r:1 w:1) - // Storage: Contracts OwnerInfoOf (r:2 w:2) - // Storage: System EventTopics (r:3 w:3) + // Proof Size summary in bytes: + // Measured: `287` + // Estimated: `0` + // Minimum execution time: 25_949 nanoseconds. + Weight::from_ref_time(26_316_000) + .saturating_add(T::DbWeight::get().reads(2_u64)) + .saturating_add(T::DbWeight::get().writes(4_u64)) + } + /// Storage: Contracts ContractInfoOf (r:1 w:1) + /// Proof: Contracts ContractInfoOf (max_values: None, max_size: Some(258), added: 2733, mode: Ignored) + /// Storage: Contracts OwnerInfoOf (r:2 w:2) + /// Proof: Contracts OwnerInfoOf (max_values: None, max_size: Some(88), added: 2563, mode: Ignored) + /// Storage: System EventTopics (r:3 w:3) + /// Proof Skipped: System EventTopics (max_values: None, max_size: None, mode: Ignored) fn set_code() -> Weight { - // Minimum execution time: 41_957 nanoseconds. - Weight::from_ref_time(42_536_000) - .saturating_add(T::DbWeight::get().reads(6)) - .saturating_add(T::DbWeight::get().writes(6)) - } - // Storage: System Account (r:1 w:0) - // Storage: Contracts ContractInfoOf (r:1 w:1) - // Storage: Contracts CodeStorage (r:1 w:0) - // Storage: Timestamp Now (r:1 w:0) - // Storage: System EventTopics (r:2 w:2) + // Proof Size summary in bytes: + // Measured: `634` + // Estimated: `0` + // Minimum execution time: 29_005 nanoseconds. + Weight::from_ref_time(29_370_000) + .saturating_add(T::DbWeight::get().reads(6_u64)) + .saturating_add(T::DbWeight::get().writes(6_u64)) + } + /// Storage: System Account (r:1 w:0) + /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: Ignored) + /// Storage: Contracts ContractInfoOf (r:1 w:1) + /// Proof: Contracts ContractInfoOf (max_values: None, max_size: Some(258), added: 2733, mode: Ignored) + /// Storage: Contracts CodeStorage (r:1 w:0) + /// Proof: Contracts CodeStorage (max_values: None, max_size: Some(126001), added: 128476, mode: Ignored) + /// Storage: Timestamp Now (r:1 w:0) + /// Proof: Timestamp Now (max_values: Some(1), max_size: Some(8), added: 503, mode: Ignored) + /// Storage: System EventTopics (r:2 w:2) + /// Proof Skipped: System EventTopics (max_values: None, max_size: None, mode: Ignored) /// The range of component `r` is `[0, 20]`. fn seal_caller(r: u32, ) -> Weight { - // Minimum execution time: 293_544 nanoseconds. - Weight::from_ref_time(298_981_222) - // Standard Error: 39_139 - .saturating_add(Weight::from_ref_time(17_278_436).saturating_mul(r.into())) - .saturating_add(T::DbWeight::get().reads(6)) - .saturating_add(T::DbWeight::get().writes(3)) - } - // Storage: System Account (r:1 w:0) - // Storage: Contracts ContractInfoOf (r:1 w:1) - // Storage: Contracts CodeStorage (r:1 w:0) - // Storage: Timestamp Now (r:1 w:0) - // Storage: System EventTopics (r:2 w:2) + // Proof Size summary in bytes: + // Measured: `845 + r * (480 ±0)` + // Estimated: `0` + // Minimum execution time: 281_880 nanoseconds. + Weight::from_ref_time(289_637_700) + // Standard Error: 22_662 + .saturating_add(Weight::from_ref_time(16_866_274).saturating_mul(r.into())) + .saturating_add(T::DbWeight::get().reads(6_u64)) + .saturating_add(T::DbWeight::get().writes(3_u64)) + } + /// Storage: System Account (r:1 w:0) + /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: Ignored) + /// Storage: Contracts ContractInfoOf (r:1601 w:1) + /// Proof: Contracts ContractInfoOf (max_values: None, max_size: Some(258), added: 2733, mode: Ignored) + /// Storage: Contracts CodeStorage (r:1 w:0) + /// Proof: Contracts CodeStorage (max_values: None, max_size: Some(126001), added: 128476, mode: Ignored) + /// Storage: Timestamp Now (r:1 w:0) + /// Proof: Timestamp Now (max_values: Some(1), max_size: Some(8), added: 503, mode: Ignored) + /// Storage: System EventTopics (r:2 w:2) + /// Proof Skipped: System EventTopics (max_values: None, max_size: None, mode: Ignored) /// The range of component `r` is `[0, 20]`. fn seal_is_contract(r: u32, ) -> Weight { - // Minimum execution time: 293_500 nanoseconds. - Weight::from_ref_time(237_825_284) - // Standard Error: 450_234 - .saturating_add(Weight::from_ref_time(198_995_806).saturating_mul(r.into())) - .saturating_add(T::DbWeight::get().reads(6)) + // Proof Size summary in bytes: + // Measured: `882 + r * (19218 ±0)` + // Estimated: `0` + // Minimum execution time: 285_590 nanoseconds. + Weight::from_ref_time(231_277_523) + // Standard Error: 425_084 + .saturating_add(Weight::from_ref_time(192_985_377).saturating_mul(r.into())) + .saturating_add(T::DbWeight::get().reads(6_u64)) .saturating_add(T::DbWeight::get().reads((80_u64).saturating_mul(r.into()))) - .saturating_add(T::DbWeight::get().writes(3)) - } - // Storage: System Account (r:1 w:0) - // Storage: Contracts ContractInfoOf (r:1 w:1) - // Storage: Contracts CodeStorage (r:1 w:0) - // Storage: Timestamp Now (r:1 w:0) - // Storage: System EventTopics (r:2 w:2) + .saturating_add(T::DbWeight::get().writes(3_u64)) + } + /// Storage: System Account (r:1 w:0) + /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: Ignored) + /// Storage: Contracts ContractInfoOf (r:1601 w:1) + /// Proof: Contracts ContractInfoOf (max_values: None, max_size: Some(258), added: 2733, mode: Ignored) + /// Storage: Contracts CodeStorage (r:1 w:0) + /// Proof: Contracts CodeStorage (max_values: None, max_size: Some(126001), added: 128476, mode: Ignored) + /// Storage: Timestamp Now (r:1 w:0) + /// Proof: Timestamp Now (max_values: Some(1), max_size: Some(8), added: 503, mode: Ignored) + /// Storage: System EventTopics (r:2 w:2) + /// Proof Skipped: System EventTopics (max_values: None, max_size: None, mode: Ignored) /// The range of component `r` is `[0, 20]`. fn seal_code_hash(r: u32, ) -> Weight { - // Minimum execution time: 295_851 nanoseconds. - Weight::from_ref_time(258_113_447) - // Standard Error: 408_747 - .saturating_add(Weight::from_ref_time(237_490_216).saturating_mul(r.into())) - .saturating_add(T::DbWeight::get().reads(6)) + // Proof Size summary in bytes: + // Measured: `889 + r * (19539 ±0)` + // Estimated: `0` + // Minimum execution time: 282_966 nanoseconds. + Weight::from_ref_time(236_127_328) + // Standard Error: 405_193 + .saturating_add(Weight::from_ref_time(235_541_377).saturating_mul(r.into())) + .saturating_add(T::DbWeight::get().reads(6_u64)) .saturating_add(T::DbWeight::get().reads((80_u64).saturating_mul(r.into()))) - .saturating_add(T::DbWeight::get().writes(3)) - } - // Storage: System Account (r:1 w:0) - // Storage: Contracts ContractInfoOf (r:1 w:1) - // Storage: Contracts CodeStorage (r:1 w:0) - // Storage: Timestamp Now (r:1 w:0) - // Storage: System EventTopics (r:2 w:2) + .saturating_add(T::DbWeight::get().writes(3_u64)) + } + /// Storage: System Account (r:1 w:0) + /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: Ignored) + /// Storage: Contracts ContractInfoOf (r:1 w:1) + /// Proof: Contracts ContractInfoOf (max_values: None, max_size: Some(258), added: 2733, mode: Ignored) + /// Storage: Contracts CodeStorage (r:1 w:0) + /// Proof: Contracts CodeStorage (max_values: None, max_size: Some(126001), added: 128476, mode: Ignored) + /// Storage: Timestamp Now (r:1 w:0) + /// Proof: Timestamp Now (max_values: Some(1), max_size: Some(8), added: 503, mode: Ignored) + /// Storage: System EventTopics (r:2 w:2) + /// Proof Skipped: System EventTopics (max_values: None, max_size: None, mode: Ignored) /// The range of component `r` is `[0, 20]`. fn seal_own_code_hash(r: u32, ) -> Weight { - // Minimum execution time: 294_614 nanoseconds. - Weight::from_ref_time(301_528_998) - // Standard Error: 40_613 - .saturating_add(Weight::from_ref_time(20_815_088).saturating_mul(r.into())) - .saturating_add(T::DbWeight::get().reads(6)) - .saturating_add(T::DbWeight::get().writes(3)) - } - // Storage: System Account (r:1 w:0) - // Storage: Contracts ContractInfoOf (r:1 w:1) - // Storage: Contracts CodeStorage (r:1 w:0) - // Storage: Timestamp Now (r:1 w:0) - // Storage: System EventTopics (r:2 w:2) + // Proof Size summary in bytes: + // Measured: `852 + r * (480 ±0)` + // Estimated: `0` + // Minimum execution time: 286_021 nanoseconds. + Weight::from_ref_time(290_200_599) + // Standard Error: 19_224 + .saturating_add(Weight::from_ref_time(20_692_099).saturating_mul(r.into())) + .saturating_add(T::DbWeight::get().reads(6_u64)) + .saturating_add(T::DbWeight::get().writes(3_u64)) + } + /// Storage: System Account (r:1 w:0) + /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: Ignored) + /// Storage: Contracts ContractInfoOf (r:1 w:1) + /// Proof: Contracts ContractInfoOf (max_values: None, max_size: Some(258), added: 2733, mode: Ignored) + /// Storage: Contracts CodeStorage (r:1 w:0) + /// Proof: Contracts CodeStorage (max_values: None, max_size: Some(126001), added: 128476, mode: Ignored) + /// Storage: Timestamp Now (r:1 w:0) + /// Proof: Timestamp Now (max_values: Some(1), max_size: Some(8), added: 503, mode: Ignored) + /// Storage: System EventTopics (r:2 w:2) + /// Proof Skipped: System EventTopics (max_values: None, max_size: None, mode: Ignored) /// The range of component `r` is `[0, 20]`. fn seal_caller_is_origin(r: u32, ) -> Weight { - // Minimum execution time: 293_088 nanoseconds. - Weight::from_ref_time(299_120_595) - // Standard Error: 23_277 - .saturating_add(Weight::from_ref_time(11_130_334).saturating_mul(r.into())) - .saturating_add(T::DbWeight::get().reads(6)) - .saturating_add(T::DbWeight::get().writes(3)) - } - // Storage: System Account (r:1 w:0) - // Storage: Contracts ContractInfoOf (r:1 w:1) - // Storage: Contracts CodeStorage (r:1 w:0) - // Storage: Timestamp Now (r:1 w:0) - // Storage: System EventTopics (r:2 w:2) + // Proof Size summary in bytes: + // Measured: `842 + r * (240 ±0)` + // Estimated: `0` + // Minimum execution time: 284_175 nanoseconds. + Weight::from_ref_time(286_665_694) + // Standard Error: 14_104 + .saturating_add(Weight::from_ref_time(11_196_944).saturating_mul(r.into())) + .saturating_add(T::DbWeight::get().reads(6_u64)) + .saturating_add(T::DbWeight::get().writes(3_u64)) + } + /// Storage: System Account (r:1 w:0) + /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: Ignored) + /// Storage: Contracts ContractInfoOf (r:1 w:1) + /// Proof: Contracts ContractInfoOf (max_values: None, max_size: Some(258), added: 2733, mode: Ignored) + /// Storage: Contracts CodeStorage (r:1 w:0) + /// Proof: Contracts CodeStorage (max_values: None, max_size: Some(126001), added: 128476, mode: Ignored) + /// Storage: Timestamp Now (r:1 w:0) + /// Proof: Timestamp Now (max_values: Some(1), max_size: Some(8), added: 503, mode: Ignored) + /// Storage: System EventTopics (r:2 w:2) + /// Proof Skipped: System EventTopics (max_values: None, max_size: None, mode: Ignored) /// The range of component `r` is `[0, 20]`. fn seal_address(r: u32, ) -> Weight { - // Minimum execution time: 295_110 nanoseconds. - Weight::from_ref_time(305_295_787) - // Standard Error: 48_521 - .saturating_add(Weight::from_ref_time(16_610_806).saturating_mul(r.into())) - .saturating_add(T::DbWeight::get().reads(6)) - .saturating_add(T::DbWeight::get().writes(3)) - } - // Storage: System Account (r:1 w:0) - // Storage: Contracts ContractInfoOf (r:1 w:1) - // Storage: Contracts CodeStorage (r:1 w:0) - // Storage: Timestamp Now (r:1 w:0) - // Storage: System EventTopics (r:2 w:2) + // Proof Size summary in bytes: + // Measured: `846 + r * (480 ±0)` + // Estimated: `0` + // Minimum execution time: 285_315 nanoseconds. + Weight::from_ref_time(289_734_189) + // Standard Error: 15_980 + .saturating_add(Weight::from_ref_time(16_940_657).saturating_mul(r.into())) + .saturating_add(T::DbWeight::get().reads(6_u64)) + .saturating_add(T::DbWeight::get().writes(3_u64)) + } + /// Storage: System Account (r:1 w:0) + /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: Ignored) + /// Storage: Contracts ContractInfoOf (r:1 w:1) + /// Proof: Contracts ContractInfoOf (max_values: None, max_size: Some(258), added: 2733, mode: Ignored) + /// Storage: Contracts CodeStorage (r:1 w:0) + /// Proof: Contracts CodeStorage (max_values: None, max_size: Some(126001), added: 128476, mode: Ignored) + /// Storage: Timestamp Now (r:1 w:0) + /// Proof: Timestamp Now (max_values: Some(1), max_size: Some(8), added: 503, mode: Ignored) + /// Storage: System EventTopics (r:2 w:2) + /// Proof Skipped: System EventTopics (max_values: None, max_size: None, mode: Ignored) /// The range of component `r` is `[0, 20]`. fn seal_gas_left(r: u32, ) -> Weight { - // Minimum execution time: 293_414 nanoseconds. - Weight::from_ref_time(299_960_283) - // Standard Error: 32_003 - .saturating_add(Weight::from_ref_time(16_652_433).saturating_mul(r.into())) - .saturating_add(T::DbWeight::get().reads(6)) - .saturating_add(T::DbWeight::get().writes(3)) - } - // Storage: System Account (r:1 w:0) - // Storage: Contracts ContractInfoOf (r:1 w:1) - // Storage: Contracts CodeStorage (r:1 w:0) - // Storage: Timestamp Now (r:1 w:0) - // Storage: System EventTopics (r:2 w:2) + // Proof Size summary in bytes: + // Measured: `847 + r * (480 ±0)` + // Estimated: `0` + // Minimum execution time: 285_708 nanoseconds. + Weight::from_ref_time(289_872_393) + // Standard Error: 16_551 + .saturating_add(Weight::from_ref_time(16_672_944).saturating_mul(r.into())) + .saturating_add(T::DbWeight::get().reads(6_u64)) + .saturating_add(T::DbWeight::get().writes(3_u64)) + } + /// Storage: System Account (r:2 w:0) + /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: Ignored) + /// Storage: Contracts ContractInfoOf (r:1 w:1) + /// Proof: Contracts ContractInfoOf (max_values: None, max_size: Some(258), added: 2733, mode: Ignored) + /// Storage: Contracts CodeStorage (r:1 w:0) + /// Proof: Contracts CodeStorage (max_values: None, max_size: Some(126001), added: 128476, mode: Ignored) + /// Storage: Timestamp Now (r:1 w:0) + /// Proof: Timestamp Now (max_values: Some(1), max_size: Some(8), added: 503, mode: Ignored) + /// Storage: System EventTopics (r:2 w:2) + /// Proof Skipped: System EventTopics (max_values: None, max_size: None, mode: Ignored) /// The range of component `r` is `[0, 20]`. fn seal_balance(r: u32, ) -> Weight { - // Minimum execution time: 292_429 nanoseconds. - Weight::from_ref_time(301_025_128) - // Standard Error: 92_155 - .saturating_add(Weight::from_ref_time(92_731_719).saturating_mul(r.into())) - .saturating_add(T::DbWeight::get().reads(7)) - .saturating_add(T::DbWeight::get().writes(3)) - } - // Storage: System Account (r:1 w:0) - // Storage: Contracts ContractInfoOf (r:1 w:1) - // Storage: Contracts CodeStorage (r:1 w:0) - // Storage: Timestamp Now (r:1 w:0) - // Storage: System EventTopics (r:2 w:2) + // Proof Size summary in bytes: + // Measured: `1017 + r * (480 ±0)` + // Estimated: `0` + // Minimum execution time: 285_698 nanoseconds. + Weight::from_ref_time(295_636_093) + // Standard Error: 97_582 + .saturating_add(Weight::from_ref_time(92_891_252).saturating_mul(r.into())) + .saturating_add(T::DbWeight::get().reads(7_u64)) + .saturating_add(T::DbWeight::get().writes(3_u64)) + } + /// Storage: System Account (r:1 w:0) + /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: Ignored) + /// Storage: Contracts ContractInfoOf (r:1 w:1) + /// Proof: Contracts ContractInfoOf (max_values: None, max_size: Some(258), added: 2733, mode: Ignored) + /// Storage: Contracts CodeStorage (r:1 w:0) + /// Proof: Contracts CodeStorage (max_values: None, max_size: Some(126001), added: 128476, mode: Ignored) + /// Storage: Timestamp Now (r:1 w:0) + /// Proof: Timestamp Now (max_values: Some(1), max_size: Some(8), added: 503, mode: Ignored) + /// Storage: System EventTopics (r:2 w:2) + /// Proof Skipped: System EventTopics (max_values: None, max_size: None, mode: Ignored) /// The range of component `r` is `[0, 20]`. fn seal_value_transferred(r: u32, ) -> Weight { - // Minimum execution time: 293_516 nanoseconds. - Weight::from_ref_time(300_071_428) - // Standard Error: 25_871 - .saturating_add(Weight::from_ref_time(16_599_369).saturating_mul(r.into())) - .saturating_add(T::DbWeight::get().reads(6)) - .saturating_add(T::DbWeight::get().writes(3)) - } - // Storage: System Account (r:1 w:0) - // Storage: Contracts ContractInfoOf (r:1 w:1) - // Storage: Contracts CodeStorage (r:1 w:0) - // Storage: Timestamp Now (r:1 w:0) - // Storage: System EventTopics (r:2 w:2) + // Proof Size summary in bytes: + // Measured: `856 + r * (480 ±0)` + // Estimated: `0` + // Minimum execution time: 282_057 nanoseconds. + Weight::from_ref_time(289_304_621) + // Standard Error: 17_818 + .saturating_add(Weight::from_ref_time(16_725_632).saturating_mul(r.into())) + .saturating_add(T::DbWeight::get().reads(6_u64)) + .saturating_add(T::DbWeight::get().writes(3_u64)) + } + /// Storage: System Account (r:1 w:0) + /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: Ignored) + /// Storage: Contracts ContractInfoOf (r:1 w:1) + /// Proof: Contracts ContractInfoOf (max_values: None, max_size: Some(258), added: 2733, mode: Ignored) + /// Storage: Contracts CodeStorage (r:1 w:0) + /// Proof: Contracts CodeStorage (max_values: None, max_size: Some(126001), added: 128476, mode: Ignored) + /// Storage: Timestamp Now (r:1 w:0) + /// Proof: Timestamp Now (max_values: Some(1), max_size: Some(8), added: 503, mode: Ignored) + /// Storage: System EventTopics (r:2 w:2) + /// Proof Skipped: System EventTopics (max_values: None, max_size: None, mode: Ignored) /// The range of component `r` is `[0, 20]`. fn seal_minimum_balance(r: u32, ) -> Weight { - // Minimum execution time: 293_666 nanoseconds. - Weight::from_ref_time(299_803_599) - // Standard Error: 24_508 - .saturating_add(Weight::from_ref_time(16_500_973).saturating_mul(r.into())) - .saturating_add(T::DbWeight::get().reads(6)) - .saturating_add(T::DbWeight::get().writes(3)) - } - // Storage: System Account (r:1 w:0) - // Storage: Contracts ContractInfoOf (r:1 w:1) - // Storage: Contracts CodeStorage (r:1 w:0) - // Storage: Timestamp Now (r:1 w:0) - // Storage: System EventTopics (r:2 w:2) + // Proof Size summary in bytes: + // Measured: `854 + r * (480 ±0)` + // Estimated: `0` + // Minimum execution time: 282_478 nanoseconds. + Weight::from_ref_time(289_682_366) + // Standard Error: 20_379 + .saturating_add(Weight::from_ref_time(16_517_079).saturating_mul(r.into())) + .saturating_add(T::DbWeight::get().reads(6_u64)) + .saturating_add(T::DbWeight::get().writes(3_u64)) + } + /// Storage: System Account (r:1 w:0) + /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: Ignored) + /// Storage: Contracts ContractInfoOf (r:1 w:1) + /// Proof: Contracts ContractInfoOf (max_values: None, max_size: Some(258), added: 2733, mode: Ignored) + /// Storage: Contracts CodeStorage (r:1 w:0) + /// Proof: Contracts CodeStorage (max_values: None, max_size: Some(126001), added: 128476, mode: Ignored) + /// Storage: Timestamp Now (r:1 w:0) + /// Proof: Timestamp Now (max_values: Some(1), max_size: Some(8), added: 503, mode: Ignored) + /// Storage: System EventTopics (r:2 w:2) + /// Proof Skipped: System EventTopics (max_values: None, max_size: None, mode: Ignored) /// The range of component `r` is `[0, 20]`. fn seal_block_number(r: u32, ) -> Weight { - // Minimum execution time: 289_193 nanoseconds. - Weight::from_ref_time(297_946_784) - // Standard Error: 43_719 - .saturating_add(Weight::from_ref_time(16_442_254).saturating_mul(r.into())) - .saturating_add(T::DbWeight::get().reads(6)) - .saturating_add(T::DbWeight::get().writes(3)) - } - // Storage: System Account (r:1 w:0) - // Storage: Contracts ContractInfoOf (r:1 w:1) - // Storage: Contracts CodeStorage (r:1 w:0) - // Storage: Timestamp Now (r:1 w:0) - // Storage: System EventTopics (r:2 w:2) + // Proof Size summary in bytes: + // Measured: `851 + r * (480 ±0)` + // Estimated: `0` + // Minimum execution time: 283_826 nanoseconds. + Weight::from_ref_time(289_935_300) + // Standard Error: 15_180 + .saturating_add(Weight::from_ref_time(16_268_515).saturating_mul(r.into())) + .saturating_add(T::DbWeight::get().reads(6_u64)) + .saturating_add(T::DbWeight::get().writes(3_u64)) + } + /// Storage: System Account (r:1 w:0) + /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: Ignored) + /// Storage: Contracts ContractInfoOf (r:1 w:1) + /// Proof: Contracts ContractInfoOf (max_values: None, max_size: Some(258), added: 2733, mode: Ignored) + /// Storage: Contracts CodeStorage (r:1 w:0) + /// Proof: Contracts CodeStorage (max_values: None, max_size: Some(126001), added: 128476, mode: Ignored) + /// Storage: Timestamp Now (r:1 w:0) + /// Proof: Timestamp Now (max_values: Some(1), max_size: Some(8), added: 503, mode: Ignored) + /// Storage: System EventTopics (r:2 w:2) + /// Proof Skipped: System EventTopics (max_values: None, max_size: None, mode: Ignored) /// The range of component `r` is `[0, 20]`. fn seal_now(r: u32, ) -> Weight { - // Minimum execution time: 288_897 nanoseconds. - Weight::from_ref_time(298_856_658) - // Standard Error: 31_003 - .saturating_add(Weight::from_ref_time(16_490_508).saturating_mul(r.into())) - .saturating_add(T::DbWeight::get().reads(6)) - .saturating_add(T::DbWeight::get().writes(3)) - } - // Storage: System Account (r:1 w:0) - // Storage: Contracts ContractInfoOf (r:1 w:1) - // Storage: Contracts CodeStorage (r:1 w:0) - // Storage: Timestamp Now (r:1 w:0) - // Storage: System EventTopics (r:2 w:2) - // Storage: TransactionPayment NextFeeMultiplier (r:1 w:0) + // Proof Size summary in bytes: + // Measured: `842 + r * (480 ±0)` + // Estimated: `0` + // Minimum execution time: 285_455 nanoseconds. + Weight::from_ref_time(289_682_526) + // Standard Error: 18_667 + .saturating_add(Weight::from_ref_time(16_502_025).saturating_mul(r.into())) + .saturating_add(T::DbWeight::get().reads(6_u64)) + .saturating_add(T::DbWeight::get().writes(3_u64)) + } + /// Storage: System Account (r:1 w:0) + /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: Ignored) + /// Storage: Contracts ContractInfoOf (r:1 w:1) + /// Proof: Contracts ContractInfoOf (max_values: None, max_size: Some(258), added: 2733, mode: Ignored) + /// Storage: Contracts CodeStorage (r:1 w:0) + /// Proof: Contracts CodeStorage (max_values: None, max_size: Some(126001), added: 128476, mode: Ignored) + /// Storage: Timestamp Now (r:1 w:0) + /// Proof: Timestamp Now (max_values: Some(1), max_size: Some(8), added: 503, mode: Ignored) + /// Storage: TransactionPayment NextFeeMultiplier (r:1 w:0) + /// Proof: TransactionPayment NextFeeMultiplier (max_values: Some(1), max_size: Some(16), added: 511, mode: Ignored) + /// Storage: System EventTopics (r:2 w:2) + /// Proof Skipped: System EventTopics (max_values: None, max_size: None, mode: Ignored) /// The range of component `r` is `[0, 20]`. fn seal_weight_to_fee(r: u32, ) -> Weight { - // Minimum execution time: 289_850 nanoseconds. - Weight::from_ref_time(302_898_449) - // Standard Error: 92_822 - .saturating_add(Weight::from_ref_time(86_376_874).saturating_mul(r.into())) - .saturating_add(T::DbWeight::get().reads(7)) - .saturating_add(T::DbWeight::get().writes(3)) - } - // Storage: System Account (r:1 w:0) - // Storage: Contracts ContractInfoOf (r:1 w:1) - // Storage: Contracts CodeStorage (r:1 w:0) - // Storage: Timestamp Now (r:1 w:0) - // Storage: System EventTopics (r:2 w:2) + // Proof Size summary in bytes: + // Measured: `919 + r * (800 ±0)` + // Estimated: `0` + // Minimum execution time: 286_106 nanoseconds. + Weight::from_ref_time(294_493_680) + // Standard Error: 76_469 + .saturating_add(Weight::from_ref_time(87_055_837).saturating_mul(r.into())) + .saturating_add(T::DbWeight::get().reads(7_u64)) + .saturating_add(T::DbWeight::get().writes(3_u64)) + } + /// Storage: System Account (r:1 w:0) + /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: Ignored) + /// Storage: Contracts ContractInfoOf (r:1 w:1) + /// Proof: Contracts ContractInfoOf (max_values: None, max_size: Some(258), added: 2733, mode: Ignored) + /// Storage: Contracts CodeStorage (r:1 w:0) + /// Proof: Contracts CodeStorage (max_values: None, max_size: Some(126001), added: 128476, mode: Ignored) + /// Storage: Timestamp Now (r:1 w:0) + /// Proof: Timestamp Now (max_values: Some(1), max_size: Some(8), added: 503, mode: Ignored) + /// Storage: System EventTopics (r:2 w:2) + /// Proof Skipped: System EventTopics (max_values: None, max_size: None, mode: Ignored) /// The range of component `r` is `[0, 20]`. fn seal_gas(r: u32, ) -> Weight { - // Minimum execution time: 145_105 nanoseconds. - Weight::from_ref_time(152_388_934) - // Standard Error: 29_988 - .saturating_add(Weight::from_ref_time(7_761_947).saturating_mul(r.into())) - .saturating_add(T::DbWeight::get().reads(6)) - .saturating_add(T::DbWeight::get().writes(3)) - } - // Storage: System Account (r:1 w:0) - // Storage: Contracts ContractInfoOf (r:1 w:1) - // Storage: Contracts CodeStorage (r:1 w:0) - // Storage: Timestamp Now (r:1 w:0) - // Storage: System EventTopics (r:2 w:2) + // Proof Size summary in bytes: + // Measured: `809 + r * (320 ±0)` + // Estimated: `0` + // Minimum execution time: 137_877 nanoseconds. + Weight::from_ref_time(141_863_027) + // Standard Error: 10_200 + .saturating_add(Weight::from_ref_time(7_925_232).saturating_mul(r.into())) + .saturating_add(T::DbWeight::get().reads(6_u64)) + .saturating_add(T::DbWeight::get().writes(3_u64)) + } + /// Storage: System Account (r:1 w:0) + /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: Ignored) + /// Storage: Contracts ContractInfoOf (r:1 w:1) + /// Proof: Contracts ContractInfoOf (max_values: None, max_size: Some(258), added: 2733, mode: Ignored) + /// Storage: Contracts CodeStorage (r:1 w:0) + /// Proof: Contracts CodeStorage (max_values: None, max_size: Some(126001), added: 128476, mode: Ignored) + /// Storage: Timestamp Now (r:1 w:0) + /// Proof: Timestamp Now (max_values: Some(1), max_size: Some(8), added: 503, mode: Ignored) + /// Storage: System EventTopics (r:2 w:2) + /// Proof Skipped: System EventTopics (max_values: None, max_size: None, mode: Ignored) /// The range of component `r` is `[0, 20]`. fn seal_input(r: u32, ) -> Weight { - // Minimum execution time: 289_319 nanoseconds. - Weight::from_ref_time(298_390_911) - // Standard Error: 27_329 - .saturating_add(Weight::from_ref_time(14_225_527).saturating_mul(r.into())) - .saturating_add(T::DbWeight::get().reads(6)) - .saturating_add(T::DbWeight::get().writes(3)) - } - // Storage: System Account (r:1 w:0) - // Storage: Contracts ContractInfoOf (r:1 w:1) - // Storage: Contracts CodeStorage (r:1 w:0) - // Storage: Timestamp Now (r:1 w:0) - // Storage: System EventTopics (r:2 w:2) + // Proof Size summary in bytes: + // Measured: `844 + r * (480 ±0)` + // Estimated: `0` + // Minimum execution time: 282_034 nanoseconds. + Weight::from_ref_time(289_388_799) + // Standard Error: 21_999 + .saturating_add(Weight::from_ref_time(15_039_420).saturating_mul(r.into())) + .saturating_add(T::DbWeight::get().reads(6_u64)) + .saturating_add(T::DbWeight::get().writes(3_u64)) + } + /// Storage: System Account (r:1 w:0) + /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: Ignored) + /// Storage: Contracts ContractInfoOf (r:1 w:1) + /// Proof: Contracts ContractInfoOf (max_values: None, max_size: Some(258), added: 2733, mode: Ignored) + /// Storage: Contracts CodeStorage (r:1 w:0) + /// Proof: Contracts CodeStorage (max_values: None, max_size: Some(126001), added: 128476, mode: Ignored) + /// Storage: Timestamp Now (r:1 w:0) + /// Proof: Timestamp Now (max_values: Some(1), max_size: Some(8), added: 503, mode: Ignored) + /// Storage: System EventTopics (r:2 w:2) + /// Proof Skipped: System EventTopics (max_values: None, max_size: None, mode: Ignored) /// The range of component `n` is `[0, 1024]`. fn seal_input_per_kb(n: u32, ) -> Weight { - // Minimum execution time: 305_538 nanoseconds. - Weight::from_ref_time(339_734_596) - // Standard Error: 4_636 - .saturating_add(Weight::from_ref_time(9_592_149).saturating_mul(n.into())) - .saturating_add(T::DbWeight::get().reads(6)) - .saturating_add(T::DbWeight::get().writes(3)) - } - // Storage: System Account (r:1 w:0) - // Storage: Contracts ContractInfoOf (r:1 w:1) - // Storage: Contracts CodeStorage (r:1 w:0) - // Storage: Timestamp Now (r:1 w:0) - // Storage: System EventTopics (r:2 w:2) + // Proof Size summary in bytes: + // Measured: `1324` + // Estimated: `0` + // Minimum execution time: 303_229 nanoseconds. + Weight::from_ref_time(321_863_704) + // Standard Error: 2_754 + .saturating_add(Weight::from_ref_time(9_545_103).saturating_mul(n.into())) + .saturating_add(T::DbWeight::get().reads(6_u64)) + .saturating_add(T::DbWeight::get().writes(3_u64)) + } + /// Storage: System Account (r:1 w:0) + /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: Ignored) + /// Storage: Contracts ContractInfoOf (r:1 w:1) + /// Proof: Contracts ContractInfoOf (max_values: None, max_size: Some(258), added: 2733, mode: Ignored) + /// Storage: Contracts CodeStorage (r:1 w:0) + /// Proof: Contracts CodeStorage (max_values: None, max_size: Some(126001), added: 128476, mode: Ignored) + /// Storage: Timestamp Now (r:1 w:0) + /// Proof: Timestamp Now (max_values: Some(1), max_size: Some(8), added: 503, mode: Ignored) + /// Storage: System EventTopics (r:2 w:2) + /// Proof Skipped: System EventTopics (max_values: None, max_size: None, mode: Ignored) /// The range of component `r` is `[0, 1]`. fn seal_return(r: u32, ) -> Weight { - // Minimum execution time: 287_189 nanoseconds. - Weight::from_ref_time(295_472_383) - // Standard Error: 314_142 - .saturating_add(Weight::from_ref_time(214_016).saturating_mul(r.into())) - .saturating_add(T::DbWeight::get().reads(6)) - .saturating_add(T::DbWeight::get().writes(3)) - } - // Storage: System Account (r:1 w:0) - // Storage: Contracts ContractInfoOf (r:1 w:1) - // Storage: Contracts CodeStorage (r:1 w:0) - // Storage: Timestamp Now (r:1 w:0) - // Storage: System EventTopics (r:2 w:2) + // Proof Size summary in bytes: + // Measured: `832 + r * (45 ±0)` + // Estimated: `0` + // Minimum execution time: 278_824 nanoseconds. + Weight::from_ref_time(285_019_861) + // Standard Error: 101_646 + .saturating_add(Weight::from_ref_time(1_757_938).saturating_mul(r.into())) + .saturating_add(T::DbWeight::get().reads(6_u64)) + .saturating_add(T::DbWeight::get().writes(3_u64)) + } + /// Storage: System Account (r:1 w:0) + /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: Ignored) + /// Storage: Contracts ContractInfoOf (r:1 w:1) + /// Proof: Contracts ContractInfoOf (max_values: None, max_size: Some(258), added: 2733, mode: Ignored) + /// Storage: Contracts CodeStorage (r:1 w:0) + /// Proof: Contracts CodeStorage (max_values: None, max_size: Some(126001), added: 128476, mode: Ignored) + /// Storage: Timestamp Now (r:1 w:0) + /// Proof: Timestamp Now (max_values: Some(1), max_size: Some(8), added: 503, mode: Ignored) + /// Storage: System EventTopics (r:2 w:2) + /// Proof Skipped: System EventTopics (max_values: None, max_size: None, mode: Ignored) /// The range of component `n` is `[0, 1024]`. fn seal_return_per_kb(n: u32, ) -> Weight { - // Minimum execution time: 289_380 nanoseconds. - Weight::from_ref_time(296_302_404) - // Standard Error: 889 - .saturating_add(Weight::from_ref_time(190_017).saturating_mul(n.into())) - .saturating_add(T::DbWeight::get().reads(6)) - .saturating_add(T::DbWeight::get().writes(3)) - } - // Storage: System Account (r:1 w:0) - // Storage: Contracts ContractInfoOf (r:1 w:1) - // Storage: Contracts CodeStorage (r:1 w:0) - // Storage: Timestamp Now (r:1 w:0) - // Storage: System EventTopics (r:2 w:2) - // Storage: Contracts DeletionQueue (r:1 w:1) - // Storage: Contracts OwnerInfoOf (r:1 w:1) + // Proof Size summary in bytes: + // Measured: `842` + // Estimated: `0` + // Minimum execution time: 286_316 nanoseconds. + Weight::from_ref_time(287_206_936) + // Standard Error: 589 + .saturating_add(Weight::from_ref_time(186_684).saturating_mul(n.into())) + .saturating_add(T::DbWeight::get().reads(6_u64)) + .saturating_add(T::DbWeight::get().writes(3_u64)) + } + /// Storage: System Account (r:3 w:3) + /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: Ignored) + /// Storage: Contracts ContractInfoOf (r:1 w:1) + /// Proof: Contracts ContractInfoOf (max_values: None, max_size: Some(258), added: 2733, mode: Ignored) + /// Storage: Contracts CodeStorage (r:1 w:0) + /// Proof: Contracts CodeStorage (max_values: None, max_size: Some(126001), added: 128476, mode: Ignored) + /// Storage: Timestamp Now (r:1 w:0) + /// Proof: Timestamp Now (max_values: Some(1), max_size: Some(8), added: 503, mode: Ignored) + /// Storage: Contracts DeletionQueue (r:1 w:1) + /// Proof: Contracts DeletionQueue (max_values: Some(1), max_size: Some(16642), added: 17137, mode: Ignored) + /// Storage: Contracts OwnerInfoOf (r:1 w:1) + /// Proof: Contracts OwnerInfoOf (max_values: None, max_size: Some(88), added: 2563, mode: Ignored) + /// Storage: System EventTopics (r:3 w:3) + /// Proof Skipped: System EventTopics (max_values: None, max_size: None, mode: Ignored) /// The range of component `r` is `[0, 1]`. fn seal_terminate(r: u32, ) -> Weight { - // Minimum execution time: 292_383 nanoseconds. - Weight::from_ref_time(297_557_691) - // Standard Error: 251_228 - .saturating_add(Weight::from_ref_time(56_688_008).saturating_mul(r.into())) - .saturating_add(T::DbWeight::get().reads(6)) + // Proof Size summary in bytes: + // Measured: `874 + r * (280 ±0)` + // Estimated: `0` + // Minimum execution time: 282_428 nanoseconds. + Weight::from_ref_time(287_101_148) + // Standard Error: 145_605 + .saturating_add(Weight::from_ref_time(58_079_551).saturating_mul(r.into())) + .saturating_add(T::DbWeight::get().reads(6_u64)) .saturating_add(T::DbWeight::get().reads((5_u64).saturating_mul(r.into()))) - .saturating_add(T::DbWeight::get().writes(3)) + .saturating_add(T::DbWeight::get().writes(3_u64)) .saturating_add(T::DbWeight::get().writes((6_u64).saturating_mul(r.into()))) } - // Storage: System Account (r:1 w:0) - // Storage: Contracts ContractInfoOf (r:1 w:1) - // Storage: Contracts CodeStorage (r:1 w:0) - // Storage: Timestamp Now (r:1 w:0) - // Storage: System EventTopics (r:2 w:2) - // Storage: RandomnessCollectiveFlip RandomMaterial (r:1 w:0) + /// Storage: System Account (r:1 w:0) + /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: Ignored) + /// Storage: Contracts ContractInfoOf (r:1 w:1) + /// Proof: Contracts ContractInfoOf (max_values: None, max_size: Some(258), added: 2733, mode: Ignored) + /// Storage: Contracts CodeStorage (r:1 w:0) + /// Proof: Contracts CodeStorage (max_values: None, max_size: Some(126001), added: 128476, mode: Ignored) + /// Storage: Timestamp Now (r:1 w:0) + /// Proof: Timestamp Now (max_values: Some(1), max_size: Some(8), added: 503, mode: Ignored) + /// Storage: RandomnessCollectiveFlip RandomMaterial (r:1 w:0) + /// Proof: RandomnessCollectiveFlip RandomMaterial (max_values: Some(1), max_size: Some(2594), added: 3089, mode: Ignored) + /// Storage: System EventTopics (r:2 w:2) + /// Proof Skipped: System EventTopics (max_values: None, max_size: None, mode: Ignored) /// The range of component `r` is `[0, 20]`. fn seal_random(r: u32, ) -> Weight { - // Minimum execution time: 293_019 nanoseconds. - Weight::from_ref_time(301_747_439) - // Standard Error: 92_043 - .saturating_add(Weight::from_ref_time(109_861_144).saturating_mul(r.into())) - .saturating_add(T::DbWeight::get().reads(7)) - .saturating_add(T::DbWeight::get().writes(3)) - } - // Storage: System Account (r:1 w:0) - // Storage: Contracts ContractInfoOf (r:1 w:1) - // Storage: Contracts CodeStorage (r:1 w:0) - // Storage: Timestamp Now (r:1 w:0) - // Storage: System EventTopics (r:2 w:2) + // Proof Size summary in bytes: + // Measured: `889 + r * (800 ±0)` + // Estimated: `0` + // Minimum execution time: 281_516 nanoseconds. + Weight::from_ref_time(292_759_183) + // Standard Error: 152_698 + .saturating_add(Weight::from_ref_time(112_729_555).saturating_mul(r.into())) + .saturating_add(T::DbWeight::get().reads(7_u64)) + .saturating_add(T::DbWeight::get().writes(3_u64)) + } + /// Storage: System Account (r:1 w:0) + /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: Ignored) + /// Storage: Contracts ContractInfoOf (r:1 w:1) + /// Proof: Contracts ContractInfoOf (max_values: None, max_size: Some(258), added: 2733, mode: Ignored) + /// Storage: Contracts CodeStorage (r:1 w:0) + /// Proof: Contracts CodeStorage (max_values: None, max_size: Some(126001), added: 128476, mode: Ignored) + /// Storage: Timestamp Now (r:1 w:0) + /// Proof: Timestamp Now (max_values: Some(1), max_size: Some(8), added: 503, mode: Ignored) + /// Storage: System EventTopics (r:2 w:2) + /// Proof Skipped: System EventTopics (max_values: None, max_size: None, mode: Ignored) /// The range of component `r` is `[0, 20]`. fn seal_deposit_event(r: u32, ) -> Weight { - // Minimum execution time: 287_586 nanoseconds. - Weight::from_ref_time(304_751_302) - // Standard Error: 127_698 - .saturating_add(Weight::from_ref_time(221_525_053).saturating_mul(r.into())) - .saturating_add(T::DbWeight::get().reads(6)) - .saturating_add(T::DbWeight::get().writes(3)) - } - // Storage: System Account (r:1 w:0) - // Storage: Contracts ContractInfoOf (r:1 w:1) - // Storage: Contracts CodeStorage (r:1 w:0) - // Storage: Timestamp Now (r:1 w:0) - // Storage: System EventTopics (r:2 w:2) + // Proof Size summary in bytes: + // Measured: `842 + r * (800 ±0)` + // Estimated: `0` + // Minimum execution time: 280_100 nanoseconds. + Weight::from_ref_time(299_862_082) + // Standard Error: 95_658 + .saturating_add(Weight::from_ref_time(234_211_246).saturating_mul(r.into())) + .saturating_add(T::DbWeight::get().reads(6_u64)) + .saturating_add(T::DbWeight::get().writes(3_u64)) + } + /// Storage: System Account (r:1 w:0) + /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: Ignored) + /// Storage: Contracts ContractInfoOf (r:1 w:1) + /// Proof: Contracts ContractInfoOf (max_values: None, max_size: Some(258), added: 2733, mode: Ignored) + /// Storage: Contracts CodeStorage (r:1 w:0) + /// Proof: Contracts CodeStorage (max_values: None, max_size: Some(126001), added: 128476, mode: Ignored) + /// Storage: Timestamp Now (r:1 w:0) + /// Proof: Timestamp Now (max_values: Some(1), max_size: Some(8), added: 503, mode: Ignored) + /// Storage: System EventTopics (r:322 w:322) + /// Proof Skipped: System EventTopics (max_values: None, max_size: None, mode: Ignored) /// The range of component `t` is `[0, 4]`. /// The range of component `n` is `[0, 16]`. fn seal_deposit_event_per_topic_and_kb(t: u32, n: u32, ) -> Weight { - // Minimum execution time: 1_182_204 nanoseconds. - Weight::from_ref_time(508_607_957) - // Standard Error: 503_533 - .saturating_add(Weight::from_ref_time(173_998_655).saturating_mul(t.into())) - // Standard Error: 138_294 - .saturating_add(Weight::from_ref_time(67_991_373).saturating_mul(n.into())) - .saturating_add(T::DbWeight::get().reads(6)) + // Proof Size summary in bytes: + // Measured: `1778 + t * (2608 ±0) + n * (8 ±0)` + // Estimated: `0` + // Minimum execution time: 1_197_721 nanoseconds. + Weight::from_ref_time(508_692_255) + // Standard Error: 538_596 + .saturating_add(Weight::from_ref_time(174_792_656).saturating_mul(t.into())) + // Standard Error: 147_924 + .saturating_add(Weight::from_ref_time(67_443_118).saturating_mul(n.into())) + .saturating_add(T::DbWeight::get().reads(6_u64)) .saturating_add(T::DbWeight::get().reads((80_u64).saturating_mul(t.into()))) - .saturating_add(T::DbWeight::get().writes(3)) + .saturating_add(T::DbWeight::get().writes(3_u64)) .saturating_add(T::DbWeight::get().writes((80_u64).saturating_mul(t.into()))) } - // Storage: System Account (r:1 w:0) - // Storage: Contracts ContractInfoOf (r:1 w:1) - // Storage: Contracts CodeStorage (r:1 w:0) - // Storage: Timestamp Now (r:1 w:0) - // Storage: System EventTopics (r:2 w:2) + /// Storage: System Account (r:1 w:0) + /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: Ignored) + /// Storage: Contracts ContractInfoOf (r:1 w:1) + /// Proof: Contracts ContractInfoOf (max_values: None, max_size: Some(258), added: 2733, mode: Ignored) + /// Storage: Contracts CodeStorage (r:1 w:0) + /// Proof: Contracts CodeStorage (max_values: None, max_size: Some(126001), added: 128476, mode: Ignored) + /// Storage: Timestamp Now (r:1 w:0) + /// Proof: Timestamp Now (max_values: Some(1), max_size: Some(8), added: 503, mode: Ignored) + /// Storage: System EventTopics (r:2 w:2) + /// Proof Skipped: System EventTopics (max_values: None, max_size: None, mode: Ignored) /// The range of component `r` is `[0, 20]`. fn seal_debug_message(r: u32, ) -> Weight { - // Minimum execution time: 157_706 nanoseconds. - Weight::from_ref_time(161_895_583) - // Standard Error: 14_952 - .saturating_add(Weight::from_ref_time(12_990_237).saturating_mul(r.into())) - .saturating_add(T::DbWeight::get().reads(6)) - .saturating_add(T::DbWeight::get().writes(3)) - } - // Storage: Skipped Metadata (r:0 w:0) + // Proof Size summary in bytes: + // Measured: `841 + r * (800 ±0)` + // Estimated: `0` + // Minimum execution time: 149_687 nanoseconds. + Weight::from_ref_time(153_589_818) + // Standard Error: 13_361 + .saturating_add(Weight::from_ref_time(13_379_131).saturating_mul(r.into())) + .saturating_add(T::DbWeight::get().reads(6_u64)) + .saturating_add(T::DbWeight::get().writes(3_u64)) + } + /// Storage: Skipped Metadata (r:0 w:0) + /// Proof Skipped: Skipped Metadata (max_values: None, max_size: None, mode: Ignored) /// The range of component `r` is `[0, 10]`. fn seal_set_storage(r: u32, ) -> Weight { - // Minimum execution time: 290_063 nanoseconds. - Weight::from_ref_time(256_404_853) - // Standard Error: 436_195 - .saturating_add(Weight::from_ref_time(406_569_743).saturating_mul(r.into())) - .saturating_add(T::DbWeight::get().reads(6)) + // Proof Size summary in bytes: + // Measured: `883 + r * (23417 ±0)` + // Estimated: `0` + // Minimum execution time: 281_920 nanoseconds. + Weight::from_ref_time(242_057_723) + // Standard Error: 464_911 + .saturating_add(Weight::from_ref_time(404_673_309).saturating_mul(r.into())) + .saturating_add(T::DbWeight::get().reads(6_u64)) .saturating_add(T::DbWeight::get().reads((80_u64).saturating_mul(r.into()))) - .saturating_add(T::DbWeight::get().writes(3)) + .saturating_add(T::DbWeight::get().writes(3_u64)) .saturating_add(T::DbWeight::get().writes((80_u64).saturating_mul(r.into()))) } - // Storage: Skipped Metadata (r:0 w:0) + /// Storage: Skipped Metadata (r:0 w:0) + /// Proof Skipped: Skipped Metadata (max_values: None, max_size: None, mode: Ignored) /// The range of component `n` is `[0, 8]`. fn seal_set_storage_per_new_kb(n: u32, ) -> Weight { - // Minimum execution time: 426_295 nanoseconds. - Weight::from_ref_time(582_989_911) - // Standard Error: 1_404_141 - .saturating_add(Weight::from_ref_time(89_545_963).saturating_mul(n.into())) - .saturating_add(T::DbWeight::get().reads(52)) + // Proof Size summary in bytes: + // Measured: `12583 + n * (11969 ±0)` + // Estimated: `0` + // Minimum execution time: 423_923 nanoseconds. + Weight::from_ref_time(573_806_626) + // Standard Error: 1_371_107 + .saturating_add(Weight::from_ref_time(85_963_445).saturating_mul(n.into())) + .saturating_add(T::DbWeight::get().reads(52_u64)) .saturating_add(T::DbWeight::get().reads((7_u64).saturating_mul(n.into()))) - .saturating_add(T::DbWeight::get().writes(50)) + .saturating_add(T::DbWeight::get().writes(50_u64)) .saturating_add(T::DbWeight::get().writes((7_u64).saturating_mul(n.into()))) } - // Storage: Skipped Metadata (r:0 w:0) + /// Storage: Skipped Metadata (r:0 w:0) + /// Proof Skipped: Skipped Metadata (max_values: None, max_size: None, mode: Ignored) /// The range of component `n` is `[0, 8]`. fn seal_set_storage_per_old_kb(n: u32, ) -> Weight { - // Minimum execution time: 425_062 nanoseconds. - Weight::from_ref_time(552_380_900) - // Standard Error: 1_140_169 - .saturating_add(Weight::from_ref_time(64_085_108).saturating_mul(n.into())) - .saturating_add(T::DbWeight::get().reads(51)) + // Proof Size summary in bytes: + // Measured: `15138 + n * (175775 ±0)` + // Estimated: `0` + // Minimum execution time: 424_048 nanoseconds. + Weight::from_ref_time(542_298_050) + // Standard Error: 1_092_010 + .saturating_add(Weight::from_ref_time(60_111_206).saturating_mul(n.into())) + .saturating_add(T::DbWeight::get().reads(51_u64)) .saturating_add(T::DbWeight::get().reads((7_u64).saturating_mul(n.into()))) - .saturating_add(T::DbWeight::get().writes(49)) + .saturating_add(T::DbWeight::get().writes(49_u64)) .saturating_add(T::DbWeight::get().writes((7_u64).saturating_mul(n.into()))) } - // Storage: Skipped Metadata (r:0 w:0) + /// Storage: Skipped Metadata (r:0 w:0) + /// Proof Skipped: Skipped Metadata (max_values: None, max_size: None, mode: Ignored) /// The range of component `r` is `[0, 10]`. fn seal_clear_storage(r: u32, ) -> Weight { - // Minimum execution time: 294_421 nanoseconds. - Weight::from_ref_time(260_897_072) - // Standard Error: 474_135 - .saturating_add(Weight::from_ref_time(403_191_862).saturating_mul(r.into())) - .saturating_add(T::DbWeight::get().reads(6)) + // Proof Size summary in bytes: + // Measured: `876 + r * (23098 ±0)` + // Estimated: `0` + // Minimum execution time: 285_714 nanoseconds. + Weight::from_ref_time(245_068_941) + // Standard Error: 417_796 + .saturating_add(Weight::from_ref_time(394_288_572).saturating_mul(r.into())) + .saturating_add(T::DbWeight::get().reads(6_u64)) .saturating_add(T::DbWeight::get().reads((80_u64).saturating_mul(r.into()))) - .saturating_add(T::DbWeight::get().writes(3)) + .saturating_add(T::DbWeight::get().writes(3_u64)) .saturating_add(T::DbWeight::get().writes((80_u64).saturating_mul(r.into()))) } - // Storage: Skipped Metadata (r:0 w:0) + /// Storage: Skipped Metadata (r:0 w:0) + /// Proof Skipped: Skipped Metadata (max_values: None, max_size: None, mode: Ignored) /// The range of component `n` is `[0, 8]`. fn seal_clear_storage_per_kb(n: u32, ) -> Weight { - // Minimum execution time: 391_743 nanoseconds. - Weight::from_ref_time(535_327_545) - // Standard Error: 1_297_157 - .saturating_add(Weight::from_ref_time(67_222_981).saturating_mul(n.into())) - .saturating_add(T::DbWeight::get().reads(51)) + // Proof Size summary in bytes: + // Measured: `14863 + n * (175768 ±0)` + // Estimated: `0` + // Minimum execution time: 385_278 nanoseconds. + Weight::from_ref_time(522_656_525) + // Standard Error: 1_259_587 + .saturating_add(Weight::from_ref_time(62_799_142).saturating_mul(n.into())) + .saturating_add(T::DbWeight::get().reads(51_u64)) .saturating_add(T::DbWeight::get().reads((7_u64).saturating_mul(n.into()))) - .saturating_add(T::DbWeight::get().writes(48)) + .saturating_add(T::DbWeight::get().writes(48_u64)) .saturating_add(T::DbWeight::get().writes((7_u64).saturating_mul(n.into()))) } - // Storage: Skipped Metadata (r:0 w:0) + /// Storage: Skipped Metadata (r:0 w:0) + /// Proof Skipped: Skipped Metadata (max_values: None, max_size: None, mode: Ignored) /// The range of component `r` is `[0, 10]`. fn seal_get_storage(r: u32, ) -> Weight { - // Minimum execution time: 294_947 nanoseconds. - Weight::from_ref_time(266_280_887) - // Standard Error: 382_477 - .saturating_add(Weight::from_ref_time(323_306_898).saturating_mul(r.into())) - .saturating_add(T::DbWeight::get().reads(6)) + // Proof Size summary in bytes: + // Measured: `878 + r * (23740 ±0)` + // Estimated: `0` + // Minimum execution time: 282_513 nanoseconds. + Weight::from_ref_time(256_242_753) + // Standard Error: 362_571 + .saturating_add(Weight::from_ref_time(317_951_687).saturating_mul(r.into())) + .saturating_add(T::DbWeight::get().reads(6_u64)) .saturating_add(T::DbWeight::get().reads((80_u64).saturating_mul(r.into()))) - .saturating_add(T::DbWeight::get().writes(3)) + .saturating_add(T::DbWeight::get().writes(3_u64)) } - // Storage: Skipped Metadata (r:0 w:0) + /// Storage: Skipped Metadata (r:0 w:0) + /// Proof Skipped: Skipped Metadata (max_values: None, max_size: None, mode: Ignored) /// The range of component `n` is `[0, 8]`. fn seal_get_storage_per_kb(n: u32, ) -> Weight { - // Minimum execution time: 375_487 nanoseconds. - Weight::from_ref_time(497_019_365) - // Standard Error: 1_141_672 - .saturating_add(Weight::from_ref_time(152_354_482).saturating_mul(n.into())) - .saturating_add(T::DbWeight::get().reads(51)) + // Proof Size summary in bytes: + // Measured: `15469 + n * (175775 ±0)` + // Estimated: `0` + // Minimum execution time: 370_576 nanoseconds. + Weight::from_ref_time(487_764_999) + // Standard Error: 1_073_165 + .saturating_add(Weight::from_ref_time(147_588_190).saturating_mul(n.into())) + .saturating_add(T::DbWeight::get().reads(51_u64)) .saturating_add(T::DbWeight::get().reads((7_u64).saturating_mul(n.into()))) - .saturating_add(T::DbWeight::get().writes(3)) + .saturating_add(T::DbWeight::get().writes(3_u64)) } - // Storage: Skipped Metadata (r:0 w:0) + /// Storage: Skipped Metadata (r:0 w:0) + /// Proof Skipped: Skipped Metadata (max_values: None, max_size: None, mode: Ignored) /// The range of component `r` is `[0, 10]`. fn seal_contains_storage(r: u32, ) -> Weight { - // Minimum execution time: 294_716 nanoseconds. - Weight::from_ref_time(266_187_186) - // Standard Error: 395_323 - .saturating_add(Weight::from_ref_time(309_907_221).saturating_mul(r.into())) - .saturating_add(T::DbWeight::get().reads(6)) + // Proof Size summary in bytes: + // Measured: `871 + r * (23100 ±0)` + // Estimated: `0` + // Minimum execution time: 285_917 nanoseconds. + Weight::from_ref_time(259_066_807) + // Standard Error: 340_183 + .saturating_add(Weight::from_ref_time(306_291_698).saturating_mul(r.into())) + .saturating_add(T::DbWeight::get().reads(6_u64)) .saturating_add(T::DbWeight::get().reads((80_u64).saturating_mul(r.into()))) - .saturating_add(T::DbWeight::get().writes(3)) + .saturating_add(T::DbWeight::get().writes(3_u64)) } - // Storage: Skipped Metadata (r:0 w:0) + /// Storage: Skipped Metadata (r:0 w:0) + /// Proof Skipped: Skipped Metadata (max_values: None, max_size: None, mode: Ignored) /// The range of component `n` is `[0, 8]`. fn seal_contains_storage_per_kb(n: u32, ) -> Weight { - // Minimum execution time: 371_667 nanoseconds. - Weight::from_ref_time(478_075_482) - // Standard Error: 979_116 - .saturating_add(Weight::from_ref_time(62_075_707).saturating_mul(n.into())) - .saturating_add(T::DbWeight::get().reads(51)) + // Proof Size summary in bytes: + // Measured: `14814 + n * (175782 ±0)` + // Estimated: `0` + // Minimum execution time: 366_225 nanoseconds. + Weight::from_ref_time(470_470_223) + // Standard Error: 953_976 + .saturating_add(Weight::from_ref_time(57_748_742).saturating_mul(n.into())) + .saturating_add(T::DbWeight::get().reads(51_u64)) .saturating_add(T::DbWeight::get().reads((7_u64).saturating_mul(n.into()))) - .saturating_add(T::DbWeight::get().writes(3)) + .saturating_add(T::DbWeight::get().writes(3_u64)) } - // Storage: Skipped Metadata (r:0 w:0) + /// Storage: Skipped Metadata (r:0 w:0) + /// Proof Skipped: Skipped Metadata (max_values: None, max_size: None, mode: Ignored) /// The range of component `r` is `[0, 10]`. fn seal_take_storage(r: u32, ) -> Weight { - // Minimum execution time: 292_147 nanoseconds. - Weight::from_ref_time(254_374_598) - // Standard Error: 442_367 - .saturating_add(Weight::from_ref_time(413_674_705).saturating_mul(r.into())) - .saturating_add(T::DbWeight::get().reads(6)) + // Proof Size summary in bytes: + // Measured: `879 + r * (23740 ±0)` + // Estimated: `0` + // Minimum execution time: 286_867 nanoseconds. + Weight::from_ref_time(244_403_664) + // Standard Error: 435_431 + .saturating_add(Weight::from_ref_time(409_282_991).saturating_mul(r.into())) + .saturating_add(T::DbWeight::get().reads(6_u64)) .saturating_add(T::DbWeight::get().reads((80_u64).saturating_mul(r.into()))) - .saturating_add(T::DbWeight::get().writes(3)) + .saturating_add(T::DbWeight::get().writes(3_u64)) .saturating_add(T::DbWeight::get().writes((80_u64).saturating_mul(r.into()))) } - // Storage: Skipped Metadata (r:0 w:0) + /// Storage: Skipped Metadata (r:0 w:0) + /// Proof Skipped: Skipped Metadata (max_values: None, max_size: None, mode: Ignored) /// The range of component `n` is `[0, 8]`. fn seal_take_storage_per_kb(n: u32, ) -> Weight { - // Minimum execution time: 398_554 nanoseconds. - Weight::from_ref_time(553_715_911) - // Standard Error: 1_415_272 - .saturating_add(Weight::from_ref_time(158_254_544).saturating_mul(n.into())) - .saturating_add(T::DbWeight::get().reads(51)) + // Proof Size summary in bytes: + // Measured: `15470 + n * (175775 ±0)` + // Estimated: `0` + // Minimum execution time: 393_392 nanoseconds. + Weight::from_ref_time(540_938_487) + // Standard Error: 1_361_411 + .saturating_add(Weight::from_ref_time(153_456_560).saturating_mul(n.into())) + .saturating_add(T::DbWeight::get().reads(51_u64)) .saturating_add(T::DbWeight::get().reads((7_u64).saturating_mul(n.into()))) - .saturating_add(T::DbWeight::get().writes(48)) + .saturating_add(T::DbWeight::get().writes(48_u64)) .saturating_add(T::DbWeight::get().writes((7_u64).saturating_mul(n.into()))) } - // Storage: System Account (r:1 w:0) - // Storage: Contracts ContractInfoOf (r:1 w:1) - // Storage: Contracts CodeStorage (r:1 w:0) - // Storage: Timestamp Now (r:1 w:0) - // Storage: System EventTopics (r:2 w:2) + /// Storage: System Account (r:1602 w:1601) + /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: Ignored) + /// Storage: Contracts ContractInfoOf (r:1 w:1) + /// Proof: Contracts ContractInfoOf (max_values: None, max_size: Some(258), added: 2733, mode: Ignored) + /// Storage: Contracts CodeStorage (r:1 w:0) + /// Proof: Contracts CodeStorage (max_values: None, max_size: Some(126001), added: 128476, mode: Ignored) + /// Storage: Timestamp Now (r:1 w:0) + /// Proof: Timestamp Now (max_values: Some(1), max_size: Some(8), added: 503, mode: Ignored) + /// Storage: System EventTopics (r:2 w:2) + /// Proof Skipped: System EventTopics (max_values: None, max_size: None, mode: Ignored) /// The range of component `r` is `[0, 20]`. fn seal_transfer(r: u32, ) -> Weight { - // Minimum execution time: 295_081 nanoseconds. - Weight::from_ref_time(253_461_581) - // Standard Error: 740_198 - .saturating_add(Weight::from_ref_time(1_369_153_623).saturating_mul(r.into())) - .saturating_add(T::DbWeight::get().reads(7)) + // Proof Size summary in bytes: + // Measured: `1393 + r * (3602 ±0)` + // Estimated: `0` + // Minimum execution time: 286_766 nanoseconds. + Weight::from_ref_time(221_458_774) + // Standard Error: 714_182 + .saturating_add(Weight::from_ref_time(1_402_610_222).saturating_mul(r.into())) + .saturating_add(T::DbWeight::get().reads(7_u64)) .saturating_add(T::DbWeight::get().reads((80_u64).saturating_mul(r.into()))) - .saturating_add(T::DbWeight::get().writes(4)) + .saturating_add(T::DbWeight::get().writes(4_u64)) .saturating_add(T::DbWeight::get().writes((80_u64).saturating_mul(r.into()))) } - // Storage: System Account (r:1 w:0) - // Storage: Contracts ContractInfoOf (r:1 w:1) - // Storage: Contracts CodeStorage (r:1 w:0) - // Storage: Timestamp Now (r:1 w:0) - // Storage: System EventTopics (r:2 w:2) + /// Storage: System Account (r:1 w:0) + /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: Ignored) + /// Storage: Contracts ContractInfoOf (r:1601 w:1601) + /// Proof: Contracts ContractInfoOf (max_values: None, max_size: Some(258), added: 2733, mode: Ignored) + /// Storage: Contracts CodeStorage (r:2 w:0) + /// Proof: Contracts CodeStorage (max_values: None, max_size: Some(126001), added: 128476, mode: Ignored) + /// Storage: Timestamp Now (r:1 w:0) + /// Proof: Timestamp Now (max_values: Some(1), max_size: Some(8), added: 503, mode: Ignored) + /// Storage: System EventTopics (r:1602 w:1602) + /// Proof Skipped: System EventTopics (max_values: None, max_size: None, mode: Ignored) /// The range of component `r` is `[0, 20]`. fn seal_call(r: u32, ) -> Weight { - // Minimum execution time: 296_089 nanoseconds. - Weight::from_ref_time(297_193_000) - // Standard Error: 6_571_547 - .saturating_add(Weight::from_ref_time(21_080_280_397).saturating_mul(r.into())) - .saturating_add(T::DbWeight::get().reads(7)) + // Proof Size summary in bytes: + // Measured: `1551 + r * (20511 ±0)` + // Estimated: `0` + // Minimum execution time: 287_158 nanoseconds. + Weight::from_ref_time(288_377_000) + // Standard Error: 6_108_706 + .saturating_add(Weight::from_ref_time(21_691_098_517).saturating_mul(r.into())) + .saturating_add(T::DbWeight::get().reads(7_u64)) .saturating_add(T::DbWeight::get().reads((160_u64).saturating_mul(r.into()))) - .saturating_add(T::DbWeight::get().writes(3)) + .saturating_add(T::DbWeight::get().writes(3_u64)) .saturating_add(T::DbWeight::get().writes((160_u64).saturating_mul(r.into()))) } - // Storage: System Account (r:1 w:0) - // Storage: Contracts ContractInfoOf (r:1 w:1) - // Storage: Contracts CodeStorage (r:1 w:0) - // Storage: Timestamp Now (r:1 w:0) - // Storage: System EventTopics (r:2 w:2) + /// Storage: System Account (r:1 w:0) + /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: Ignored) + /// Storage: Contracts ContractInfoOf (r:1 w:1) + /// Proof: Contracts ContractInfoOf (max_values: None, max_size: Some(258), added: 2733, mode: Ignored) + /// Storage: Contracts CodeStorage (r:1536 w:0) + /// Proof: Contracts CodeStorage (max_values: None, max_size: Some(126001), added: 128476, mode: Ignored) + /// Storage: Timestamp Now (r:1 w:0) + /// Proof: Timestamp Now (max_values: Some(1), max_size: Some(8), added: 503, mode: Ignored) + /// Storage: System EventTopics (r:1537 w:1537) + /// Proof Skipped: System EventTopics (max_values: None, max_size: None, mode: Ignored) /// The range of component `r` is `[0, 20]`. fn seal_delegate_call(r: u32, ) -> Weight { - // Minimum execution time: 296_828 nanoseconds. - Weight::from_ref_time(297_685_000) - // Standard Error: 6_864_880 - .saturating_add(Weight::from_ref_time(20_770_321_946).saturating_mul(r.into())) - .saturating_add(T::DbWeight::get().reads(6)) + // Proof Size summary in bytes: + // Measured: `0 + r * (71670 ±0)` + // Estimated: `0` + // Minimum execution time: 287_380 nanoseconds. + Weight::from_ref_time(288_241_000) + // Standard Error: 7_007_658 + .saturating_add(Weight::from_ref_time(21_428_850_764).saturating_mul(r.into())) + .saturating_add(T::DbWeight::get().reads(6_u64)) .saturating_add(T::DbWeight::get().reads((150_u64).saturating_mul(r.into()))) - .saturating_add(T::DbWeight::get().writes(3)) + .saturating_add(T::DbWeight::get().writes(3_u64)) .saturating_add(T::DbWeight::get().writes((75_u64).saturating_mul(r.into()))) } - // Storage: System Account (r:1 w:0) - // Storage: Contracts ContractInfoOf (r:81 w:81) - // Storage: Contracts CodeStorage (r:2 w:0) - // Storage: Timestamp Now (r:1 w:0) - // Storage: System EventTopics (r:82 w:82) + /// Storage: System Account (r:82 w:81) + /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: Ignored) + /// Storage: Contracts ContractInfoOf (r:81 w:81) + /// Proof: Contracts ContractInfoOf (max_values: None, max_size: Some(258), added: 2733, mode: Ignored) + /// Storage: Contracts CodeStorage (r:2 w:0) + /// Proof: Contracts CodeStorage (max_values: None, max_size: Some(126001), added: 128476, mode: Ignored) + /// Storage: Timestamp Now (r:1 w:0) + /// Proof: Timestamp Now (max_values: Some(1), max_size: Some(8), added: 503, mode: Ignored) + /// Storage: System EventTopics (r:82 w:82) + /// Proof Skipped: System EventTopics (max_values: None, max_size: None, mode: Ignored) /// The range of component `t` is `[0, 1]`. /// The range of component `c` is `[0, 1024]`. fn seal_call_per_transfer_clone_kb(t: u32, c: u32, ) -> Weight { - // Minimum execution time: 9_824_671 nanoseconds. - Weight::from_ref_time(8_713_861_450) - // Standard Error: 7_388_925 - .saturating_add(Weight::from_ref_time(1_327_819_806).saturating_mul(t.into())) - // Standard Error: 11_079 - .saturating_add(Weight::from_ref_time(9_789_514).saturating_mul(c.into())) - .saturating_add(T::DbWeight::get().reads(167)) + // Proof Size summary in bytes: + // Measured: `21611 + t * (15369 ±0)` + // Estimated: `0` + // Minimum execution time: 10_644_986 nanoseconds. + Weight::from_ref_time(9_596_635_640) + // Standard Error: 6_393_384 + .saturating_add(Weight::from_ref_time(1_304_764_528).saturating_mul(t.into())) + // Standard Error: 9_586 + .saturating_add(Weight::from_ref_time(9_663_819).saturating_mul(c.into())) + .saturating_add(T::DbWeight::get().reads(167_u64)) .saturating_add(T::DbWeight::get().reads((81_u64).saturating_mul(t.into()))) - .saturating_add(T::DbWeight::get().writes(163)) + .saturating_add(T::DbWeight::get().writes(163_u64)) .saturating_add(T::DbWeight::get().writes((81_u64).saturating_mul(t.into()))) } - // Storage: System Account (r:1 w:0) - // Storage: Contracts ContractInfoOf (r:1 w:1) - // Storage: Contracts CodeStorage (r:1 w:0) - // Storage: Timestamp Now (r:1 w:0) - // Storage: System EventTopics (r:2 w:2) - // Storage: Contracts Nonce (r:1 w:1) - // Storage: Contracts OwnerInfoOf (r:80 w:80) + /// Storage: System Account (r:1602 w:1602) + /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: Ignored) + /// Storage: Contracts ContractInfoOf (r:1601 w:1601) + /// Proof: Contracts ContractInfoOf (max_values: None, max_size: Some(258), added: 2733, mode: Ignored) + /// Storage: Contracts CodeStorage (r:1601 w:1600) + /// Proof: Contracts CodeStorage (max_values: None, max_size: Some(126001), added: 128476, mode: Ignored) + /// Storage: Timestamp Now (r:1 w:0) + /// Proof: Timestamp Now (max_values: Some(1), max_size: Some(8), added: 503, mode: Ignored) + /// Storage: Contracts Nonce (r:1 w:1) + /// Proof: Contracts Nonce (max_values: Some(1), max_size: Some(8), added: 503, mode: Ignored) + /// Storage: Contracts OwnerInfoOf (r:1600 w:1600) + /// Proof: Contracts OwnerInfoOf (max_values: None, max_size: Some(88), added: 2563, mode: Ignored) + /// Storage: System EventTopics (r:1602 w:1602) + /// Proof Skipped: System EventTopics (max_values: None, max_size: None, mode: Ignored) /// The range of component `r` is `[0, 20]`. fn seal_instantiate(r: u32, ) -> Weight { - // Minimum execution time: 293_374 nanoseconds. - Weight::from_ref_time(302_067_000) - // Standard Error: 20_137_097 - .saturating_add(Weight::from_ref_time(26_444_812_817).saturating_mul(r.into())) - .saturating_add(T::DbWeight::get().reads(8)) + // Proof Size summary in bytes: + // Measured: `1613 + r * (25576 ±0)` + // Estimated: `0` + // Minimum execution time: 284_948 nanoseconds. + Weight::from_ref_time(289_276_000) + // Standard Error: 18_674_951 + .saturating_add(Weight::from_ref_time(27_090_355_673).saturating_mul(r.into())) + .saturating_add(T::DbWeight::get().reads(8_u64)) .saturating_add(T::DbWeight::get().reads((400_u64).saturating_mul(r.into()))) - .saturating_add(T::DbWeight::get().writes(5)) + .saturating_add(T::DbWeight::get().writes(5_u64)) .saturating_add(T::DbWeight::get().writes((400_u64).saturating_mul(r.into()))) } - // Storage: System Account (r:81 w:81) - // Storage: Contracts ContractInfoOf (r:81 w:81) - // Storage: Contracts CodeStorage (r:2 w:1) - // Storage: Timestamp Now (r:1 w:0) - // Storage: Contracts Nonce (r:1 w:1) - // Storage: Contracts OwnerInfoOf (r:1 w:1) - // Storage: System EventTopics (r:82 w:82) + /// Storage: System Account (r:82 w:82) + /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: Ignored) + /// Storage: Contracts ContractInfoOf (r:81 w:81) + /// Proof: Contracts ContractInfoOf (max_values: None, max_size: Some(258), added: 2733, mode: Ignored) + /// Storage: Contracts CodeStorage (r:2 w:1) + /// Proof: Contracts CodeStorage (max_values: None, max_size: Some(126001), added: 128476, mode: Ignored) + /// Storage: Timestamp Now (r:1 w:0) + /// Proof: Timestamp Now (max_values: Some(1), max_size: Some(8), added: 503, mode: Ignored) + /// Storage: Contracts Nonce (r:1 w:1) + /// Proof: Contracts Nonce (max_values: Some(1), max_size: Some(8), added: 503, mode: Ignored) + /// Storage: Contracts OwnerInfoOf (r:1 w:1) + /// Proof: Contracts OwnerInfoOf (max_values: None, max_size: Some(88), added: 2563, mode: Ignored) + /// Storage: System EventTopics (r:82 w:82) + /// Proof Skipped: System EventTopics (max_values: None, max_size: None, mode: Ignored) /// The range of component `t` is `[0, 1]`. /// The range of component `i` is `[0, 960]`. /// The range of component `s` is `[0, 960]`. fn seal_instantiate_per_transfer_input_salt_kb(t: u32, i: u32, s: u32, ) -> Weight { - // Minimum execution time: 126_456_770 nanoseconds. - Weight::from_ref_time(9_223_715_752) - // Standard Error: 96_040_318 - .saturating_add(Weight::from_ref_time(19_550_519).saturating_mul(t.into())) - // Standard Error: 156_614 - .saturating_add(Weight::from_ref_time(122_958_298).saturating_mul(i.into())) - // Standard Error: 156_614 - .saturating_add(Weight::from_ref_time(123_316_079).saturating_mul(s.into())) - .saturating_add(T::DbWeight::get().reads(249)) + // Proof Size summary in bytes: + // Measured: `5666 + t * (17 ±0)` + // Estimated: `0` + // Minimum execution time: 127_857_642 nanoseconds. + Weight::from_ref_time(11_399_054_049) + // Standard Error: 95_033_651 + .saturating_add(Weight::from_ref_time(434_246_236).saturating_mul(t.into())) + // Standard Error: 154_973 + .saturating_add(Weight::from_ref_time(121_130_672).saturating_mul(i.into())) + // Standard Error: 154_973 + .saturating_add(Weight::from_ref_time(121_554_853).saturating_mul(s.into())) + .saturating_add(T::DbWeight::get().reads(249_u64)) .saturating_add(T::DbWeight::get().reads((1_u64).saturating_mul(t.into()))) - .saturating_add(T::DbWeight::get().writes(247)) + .saturating_add(T::DbWeight::get().writes(247_u64)) .saturating_add(T::DbWeight::get().writes((1_u64).saturating_mul(t.into()))) } - // Storage: System Account (r:1 w:0) - // Storage: Contracts ContractInfoOf (r:1 w:1) - // Storage: Contracts CodeStorage (r:1 w:0) - // Storage: Timestamp Now (r:1 w:0) - // Storage: System EventTopics (r:2 w:2) + /// Storage: System Account (r:1 w:0) + /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: Ignored) + /// Storage: Contracts ContractInfoOf (r:1 w:1) + /// Proof: Contracts ContractInfoOf (max_values: None, max_size: Some(258), added: 2733, mode: Ignored) + /// Storage: Contracts CodeStorage (r:1 w:0) + /// Proof: Contracts CodeStorage (max_values: None, max_size: Some(126001), added: 128476, mode: Ignored) + /// Storage: Timestamp Now (r:1 w:0) + /// Proof: Timestamp Now (max_values: Some(1), max_size: Some(8), added: 503, mode: Ignored) + /// Storage: System EventTopics (r:2 w:2) + /// Proof Skipped: System EventTopics (max_values: None, max_size: None, mode: Ignored) /// The range of component `r` is `[0, 1]`. fn seal_hash_sha2_256(r: u32, ) -> Weight { - // Minimum execution time: 289_275 nanoseconds. - Weight::from_ref_time(297_803_961) - // Standard Error: 303_405 - .saturating_add(Weight::from_ref_time(40_800_138).saturating_mul(r.into())) - .saturating_add(T::DbWeight::get().reads(6)) - .saturating_add(T::DbWeight::get().writes(3)) - } - // Storage: System Account (r:1 w:0) - // Storage: Contracts ContractInfoOf (r:1 w:1) - // Storage: Contracts CodeStorage (r:1 w:0) - // Storage: Timestamp Now (r:1 w:0) - // Storage: System EventTopics (r:2 w:2) + // Proof Size summary in bytes: + // Measured: `839 + r * (642 ±0)` + // Estimated: `0` + // Minimum execution time: 280_949 nanoseconds. + Weight::from_ref_time(286_538_475) + // Standard Error: 124_866 + .saturating_add(Weight::from_ref_time(42_531_824).saturating_mul(r.into())) + .saturating_add(T::DbWeight::get().reads(6_u64)) + .saturating_add(T::DbWeight::get().writes(3_u64)) + } + /// Storage: System Account (r:1 w:0) + /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: Ignored) + /// Storage: Contracts ContractInfoOf (r:1 w:1) + /// Proof: Contracts ContractInfoOf (max_values: None, max_size: Some(258), added: 2733, mode: Ignored) + /// Storage: Contracts CodeStorage (r:1 w:0) + /// Proof: Contracts CodeStorage (max_values: None, max_size: Some(126001), added: 128476, mode: Ignored) + /// Storage: Timestamp Now (r:1 w:0) + /// Proof: Timestamp Now (max_values: Some(1), max_size: Some(8), added: 503, mode: Ignored) + /// Storage: System EventTopics (r:2 w:2) + /// Proof Skipped: System EventTopics (max_values: None, max_size: None, mode: Ignored) /// The range of component `n` is `[0, 1024]`. fn seal_hash_sha2_256_per_kb(n: u32, ) -> Weight { - // Minimum execution time: 336_077 nanoseconds. - Weight::from_ref_time(336_846_000) - // Standard Error: 51_610 - .saturating_add(Weight::from_ref_time(322_208_787).saturating_mul(n.into())) - .saturating_add(T::DbWeight::get().reads(6)) - .saturating_add(T::DbWeight::get().writes(3)) - } - // Storage: System Account (r:1 w:0) - // Storage: Contracts ContractInfoOf (r:1 w:1) - // Storage: Contracts CodeStorage (r:1 w:0) - // Storage: Timestamp Now (r:1 w:0) - // Storage: System EventTopics (r:2 w:2) + // Proof Size summary in bytes: + // Measured: `1641` + // Estimated: `0` + // Minimum execution time: 328_414 nanoseconds. + Weight::from_ref_time(329_293_000) + // Standard Error: 50_816 + .saturating_add(Weight::from_ref_time(318_312_506).saturating_mul(n.into())) + .saturating_add(T::DbWeight::get().reads(6_u64)) + .saturating_add(T::DbWeight::get().writes(3_u64)) + } + /// Storage: System Account (r:1 w:0) + /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: Ignored) + /// Storage: Contracts ContractInfoOf (r:1 w:1) + /// Proof: Contracts ContractInfoOf (max_values: None, max_size: Some(258), added: 2733, mode: Ignored) + /// Storage: Contracts CodeStorage (r:1 w:0) + /// Proof: Contracts CodeStorage (max_values: None, max_size: Some(126001), added: 128476, mode: Ignored) + /// Storage: Timestamp Now (r:1 w:0) + /// Proof: Timestamp Now (max_values: Some(1), max_size: Some(8), added: 503, mode: Ignored) + /// Storage: System EventTopics (r:2 w:2) + /// Proof Skipped: System EventTopics (max_values: None, max_size: None, mode: Ignored) /// The range of component `r` is `[0, 1]`. fn seal_hash_keccak_256(r: u32, ) -> Weight { - // Minimum execution time: 290_189 nanoseconds. - Weight::from_ref_time(298_454_259) - // Standard Error: 252_946 - .saturating_add(Weight::from_ref_time(55_034_240).saturating_mul(r.into())) - .saturating_add(T::DbWeight::get().reads(6)) - .saturating_add(T::DbWeight::get().writes(3)) - } - // Storage: System Account (r:1 w:0) - // Storage: Contracts ContractInfoOf (r:1 w:1) - // Storage: Contracts CodeStorage (r:1 w:0) - // Storage: Timestamp Now (r:1 w:0) - // Storage: System EventTopics (r:2 w:2) + // Proof Size summary in bytes: + // Measured: `841 + r * (642 ±0)` + // Estimated: `0` + // Minimum execution time: 282_208 nanoseconds. + Weight::from_ref_time(286_848_187) + // Standard Error: 106_214 + .saturating_add(Weight::from_ref_time(56_342_512).saturating_mul(r.into())) + .saturating_add(T::DbWeight::get().reads(6_u64)) + .saturating_add(T::DbWeight::get().writes(3_u64)) + } + /// Storage: System Account (r:1 w:0) + /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: Ignored) + /// Storage: Contracts ContractInfoOf (r:1 w:1) + /// Proof: Contracts ContractInfoOf (max_values: None, max_size: Some(258), added: 2733, mode: Ignored) + /// Storage: Contracts CodeStorage (r:1 w:0) + /// Proof: Contracts CodeStorage (max_values: None, max_size: Some(126001), added: 128476, mode: Ignored) + /// Storage: Timestamp Now (r:1 w:0) + /// Proof: Timestamp Now (max_values: Some(1), max_size: Some(8), added: 503, mode: Ignored) + /// Storage: System EventTopics (r:2 w:2) + /// Proof Skipped: System EventTopics (max_values: None, max_size: None, mode: Ignored) /// The range of component `n` is `[0, 1024]`. fn seal_hash_keccak_256_per_kb(n: u32, ) -> Weight { - // Minimum execution time: 350_030 nanoseconds. - Weight::from_ref_time(351_356_000) - // Standard Error: 57_422 - .saturating_add(Weight::from_ref_time(256_912_844).saturating_mul(n.into())) - .saturating_add(T::DbWeight::get().reads(6)) - .saturating_add(T::DbWeight::get().writes(3)) - } - // Storage: System Account (r:1 w:0) - // Storage: Contracts ContractInfoOf (r:1 w:1) - // Storage: Contracts CodeStorage (r:1 w:0) - // Storage: Timestamp Now (r:1 w:0) - // Storage: System EventTopics (r:2 w:2) + // Proof Size summary in bytes: + // Measured: `1643` + // Estimated: `0` + // Minimum execution time: 342_478 nanoseconds. + Weight::from_ref_time(342_947_000) + // Standard Error: 60_809 + .saturating_add(Weight::from_ref_time(255_492_149).saturating_mul(n.into())) + .saturating_add(T::DbWeight::get().reads(6_u64)) + .saturating_add(T::DbWeight::get().writes(3_u64)) + } + /// Storage: System Account (r:1 w:0) + /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: Ignored) + /// Storage: Contracts ContractInfoOf (r:1 w:1) + /// Proof: Contracts ContractInfoOf (max_values: None, max_size: Some(258), added: 2733, mode: Ignored) + /// Storage: Contracts CodeStorage (r:1 w:0) + /// Proof: Contracts CodeStorage (max_values: None, max_size: Some(126001), added: 128476, mode: Ignored) + /// Storage: Timestamp Now (r:1 w:0) + /// Proof: Timestamp Now (max_values: Some(1), max_size: Some(8), added: 503, mode: Ignored) + /// Storage: System EventTopics (r:2 w:2) + /// Proof Skipped: System EventTopics (max_values: None, max_size: None, mode: Ignored) /// The range of component `r` is `[0, 1]`. fn seal_hash_blake2_256(r: u32, ) -> Weight { - // Minimum execution time: 288_480 nanoseconds. - Weight::from_ref_time(297_613_263) - // Standard Error: 456_736 - .saturating_add(Weight::from_ref_time(38_100_936).saturating_mul(r.into())) - .saturating_add(T::DbWeight::get().reads(6)) - .saturating_add(T::DbWeight::get().writes(3)) - } - // Storage: System Account (r:1 w:0) - // Storage: Contracts ContractInfoOf (r:1 w:1) - // Storage: Contracts CodeStorage (r:1 w:0) - // Storage: Timestamp Now (r:1 w:0) - // Storage: System EventTopics (r:2 w:2) + // Proof Size summary in bytes: + // Measured: `841 + r * (642 ±0)` + // Estimated: `0` + // Minimum execution time: 279_059 nanoseconds. + Weight::from_ref_time(285_413_659) + // Standard Error: 123_081 + .saturating_add(Weight::from_ref_time(33_154_840).saturating_mul(r.into())) + .saturating_add(T::DbWeight::get().reads(6_u64)) + .saturating_add(T::DbWeight::get().writes(3_u64)) + } + /// Storage: System Account (r:1 w:0) + /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: Ignored) + /// Storage: Contracts ContractInfoOf (r:1 w:1) + /// Proof: Contracts ContractInfoOf (max_values: None, max_size: Some(258), added: 2733, mode: Ignored) + /// Storage: Contracts CodeStorage (r:1 w:0) + /// Proof: Contracts CodeStorage (max_values: None, max_size: Some(126001), added: 128476, mode: Ignored) + /// Storage: Timestamp Now (r:1 w:0) + /// Proof: Timestamp Now (max_values: Some(1), max_size: Some(8), added: 503, mode: Ignored) + /// Storage: System EventTopics (r:2 w:2) + /// Proof Skipped: System EventTopics (max_values: None, max_size: None, mode: Ignored) /// The range of component `n` is `[0, 1024]`. fn seal_hash_blake2_256_per_kb(n: u32, ) -> Weight { - // Minimum execution time: 321_195 nanoseconds. - Weight::from_ref_time(325_561_000) - // Standard Error: 46_289 - .saturating_add(Weight::from_ref_time(99_536_902).saturating_mul(n.into())) - .saturating_add(T::DbWeight::get().reads(6)) - .saturating_add(T::DbWeight::get().writes(3)) - } - // Storage: System Account (r:1 w:0) - // Storage: Contracts ContractInfoOf (r:1 w:1) - // Storage: Contracts CodeStorage (r:1 w:0) - // Storage: Timestamp Now (r:1 w:0) - // Storage: System EventTopics (r:2 w:2) + // Proof Size summary in bytes: + // Measured: `1643` + // Estimated: `0` + // Minimum execution time: 317_518 nanoseconds. + Weight::from_ref_time(318_178_000) + // Standard Error: 60_074 + .saturating_add(Weight::from_ref_time(99_403_819).saturating_mul(n.into())) + .saturating_add(T::DbWeight::get().reads(6_u64)) + .saturating_add(T::DbWeight::get().writes(3_u64)) + } + /// Storage: System Account (r:1 w:0) + /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: Ignored) + /// Storage: Contracts ContractInfoOf (r:1 w:1) + /// Proof: Contracts ContractInfoOf (max_values: None, max_size: Some(258), added: 2733, mode: Ignored) + /// Storage: Contracts CodeStorage (r:1 w:0) + /// Proof: Contracts CodeStorage (max_values: None, max_size: Some(126001), added: 128476, mode: Ignored) + /// Storage: Timestamp Now (r:1 w:0) + /// Proof: Timestamp Now (max_values: Some(1), max_size: Some(8), added: 503, mode: Ignored) + /// Storage: System EventTopics (r:2 w:2) + /// Proof Skipped: System EventTopics (max_values: None, max_size: None, mode: Ignored) /// The range of component `r` is `[0, 1]`. fn seal_hash_blake2_128(r: u32, ) -> Weight { - // Minimum execution time: 287_659 nanoseconds. - Weight::from_ref_time(295_963_340) - // Standard Error: 433_889 - .saturating_add(Weight::from_ref_time(30_251_859).saturating_mul(r.into())) - .saturating_add(T::DbWeight::get().reads(6)) - .saturating_add(T::DbWeight::get().writes(3)) - } - // Storage: System Account (r:1 w:0) - // Storage: Contracts ContractInfoOf (r:1 w:1) - // Storage: Contracts CodeStorage (r:1 w:0) - // Storage: Timestamp Now (r:1 w:0) - // Storage: System EventTopics (r:2 w:2) + // Proof Size summary in bytes: + // Measured: `841 + r * (679 ±0)` + // Estimated: `0` + // Minimum execution time: 280_145 nanoseconds. + Weight::from_ref_time(285_483_032) + // Standard Error: 106_113 + .saturating_add(Weight::from_ref_time(33_475_067).saturating_mul(r.into())) + .saturating_add(T::DbWeight::get().reads(6_u64)) + .saturating_add(T::DbWeight::get().writes(3_u64)) + } + /// Storage: System Account (r:1 w:0) + /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: Ignored) + /// Storage: Contracts ContractInfoOf (r:1 w:1) + /// Proof: Contracts ContractInfoOf (max_values: None, max_size: Some(258), added: 2733, mode: Ignored) + /// Storage: Contracts CodeStorage (r:1 w:0) + /// Proof: Contracts CodeStorage (max_values: None, max_size: Some(126001), added: 128476, mode: Ignored) + /// Storage: Timestamp Now (r:1 w:0) + /// Proof: Timestamp Now (max_values: Some(1), max_size: Some(8), added: 503, mode: Ignored) + /// Storage: System EventTopics (r:2 w:2) + /// Proof Skipped: System EventTopics (max_values: None, max_size: None, mode: Ignored) /// The range of component `n` is `[0, 1024]`. fn seal_hash_blake2_128_per_kb(n: u32, ) -> Weight { - // Minimum execution time: 324_298 nanoseconds. - Weight::from_ref_time(325_738_000) - // Standard Error: 48_563 - .saturating_add(Weight::from_ref_time(99_479_838).saturating_mul(n.into())) - .saturating_add(T::DbWeight::get().reads(6)) - .saturating_add(T::DbWeight::get().writes(3)) - } - // Storage: System Account (r:1 w:0) - // Storage: Contracts ContractInfoOf (r:1 w:1) - // Storage: Contracts CodeStorage (r:1 w:0) - // Storage: Timestamp Now (r:1 w:0) - // Storage: System EventTopics (r:2 w:2) + // Proof Size summary in bytes: + // Measured: `1643` + // Estimated: `0` + // Minimum execution time: 318_141 nanoseconds. + Weight::from_ref_time(318_699_000) + // Standard Error: 55_136 + .saturating_add(Weight::from_ref_time(99_275_434).saturating_mul(n.into())) + .saturating_add(T::DbWeight::get().reads(6_u64)) + .saturating_add(T::DbWeight::get().writes(3_u64)) + } + /// Storage: System Account (r:1 w:0) + /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: Ignored) + /// Storage: Contracts ContractInfoOf (r:1 w:1) + /// Proof: Contracts ContractInfoOf (max_values: None, max_size: Some(258), added: 2733, mode: Ignored) + /// Storage: Contracts CodeStorage (r:1 w:0) + /// Proof: Contracts CodeStorage (max_values: None, max_size: Some(126001), added: 128476, mode: Ignored) + /// Storage: Timestamp Now (r:1 w:0) + /// Proof: Timestamp Now (max_values: Some(1), max_size: Some(8), added: 503, mode: Ignored) + /// Storage: System EventTopics (r:2 w:2) + /// Proof Skipped: System EventTopics (max_values: None, max_size: None, mode: Ignored) /// The range of component `r` is `[0, 1]`. fn seal_ecdsa_recover(r: u32, ) -> Weight { - // Minimum execution time: 294_257 nanoseconds. - Weight::from_ref_time(299_467_620) - // Standard Error: 589_749 - .saturating_add(Weight::from_ref_time(3_015_389_579).saturating_mul(r.into())) - .saturating_add(T::DbWeight::get().reads(6)) - .saturating_add(T::DbWeight::get().writes(3)) - } - // Storage: System Account (r:1 w:0) - // Storage: Contracts ContractInfoOf (r:1 w:1) - // Storage: Contracts CodeStorage (r:1 w:0) - // Storage: Timestamp Now (r:1 w:0) - // Storage: System EventTopics (r:2 w:2) + // Proof Size summary in bytes: + // Measured: `885 + r * (6083 ±0)` + // Estimated: `0` + // Minimum execution time: 282_474 nanoseconds. + Weight::from_ref_time(288_078_802) + // Standard Error: 302_968 + .saturating_add(Weight::from_ref_time(2_944_967_597).saturating_mul(r.into())) + .saturating_add(T::DbWeight::get().reads(6_u64)) + .saturating_add(T::DbWeight::get().writes(3_u64)) + } + /// Storage: System Account (r:1 w:0) + /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: Ignored) + /// Storage: Contracts ContractInfoOf (r:1 w:1) + /// Proof: Contracts ContractInfoOf (max_values: None, max_size: Some(258), added: 2733, mode: Ignored) + /// Storage: Contracts CodeStorage (r:1 w:0) + /// Proof: Contracts CodeStorage (max_values: None, max_size: Some(126001), added: 128476, mode: Ignored) + /// Storage: Timestamp Now (r:1 w:0) + /// Proof: Timestamp Now (max_values: Some(1), max_size: Some(8), added: 503, mode: Ignored) + /// Storage: System EventTopics (r:2 w:2) + /// Proof Skipped: System EventTopics (max_values: None, max_size: None, mode: Ignored) /// The range of component `r` is `[0, 1]`. fn seal_ecdsa_to_eth_address(r: u32, ) -> Weight { - // Minimum execution time: 289_480 nanoseconds. - Weight::from_ref_time(298_762_412) - // Standard Error: 397_004 - .saturating_add(Weight::from_ref_time(743_937_087).saturating_mul(r.into())) - .saturating_add(T::DbWeight::get().reads(6)) - .saturating_add(T::DbWeight::get().writes(3)) - } - // Storage: System Account (r:1 w:0) - // Storage: Contracts ContractInfoOf (r:1 w:1) - // Storage: Contracts CodeStorage (r:1 w:0) - // Storage: Timestamp Now (r:1 w:0) - // Storage: System EventTopics (r:2 w:2) - // Storage: Contracts OwnerInfoOf (r:16 w:16) + // Proof Size summary in bytes: + // Measured: `854 + r * (3362 ±0)` + // Estimated: `0` + // Minimum execution time: 281_514 nanoseconds. + Weight::from_ref_time(287_458_651) + // Standard Error: 146_715 + .saturating_add(Weight::from_ref_time(731_367_948).saturating_mul(r.into())) + .saturating_add(T::DbWeight::get().reads(6_u64)) + .saturating_add(T::DbWeight::get().writes(3_u64)) + } + /// Storage: System Account (r:1 w:0) + /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: Ignored) + /// Storage: Contracts ContractInfoOf (r:1 w:1) + /// Proof: Contracts ContractInfoOf (max_values: None, max_size: Some(258), added: 2733, mode: Ignored) + /// Storage: Contracts CodeStorage (r:1536 w:0) + /// Proof: Contracts CodeStorage (max_values: None, max_size: Some(126001), added: 128476, mode: Ignored) + /// Storage: Timestamp Now (r:1 w:0) + /// Proof: Timestamp Now (max_values: Some(1), max_size: Some(8), added: 503, mode: Ignored) + /// Storage: Contracts OwnerInfoOf (r:1536 w:1536) + /// Proof: Contracts OwnerInfoOf (max_values: None, max_size: Some(88), added: 2563, mode: Ignored) + /// Storage: System EventTopics (r:1538 w:1538) + /// Proof Skipped: System EventTopics (max_values: None, max_size: None, mode: Ignored) /// The range of component `r` is `[0, 20]`. fn seal_set_code_hash(r: u32, ) -> Weight { - // Minimum execution time: 293_494 nanoseconds. - Weight::from_ref_time(297_756_000) - // Standard Error: 2_731_227 - .saturating_add(Weight::from_ref_time(1_387_380_436).saturating_mul(r.into())) - .saturating_add(T::DbWeight::get().reads(6)) + // Proof Size summary in bytes: + // Measured: `0 + r * (79300 ±0)` + // Estimated: `0` + // Minimum execution time: 282_591 nanoseconds. + Weight::from_ref_time(286_842_000) + // Standard Error: 2_645_254 + .saturating_add(Weight::from_ref_time(1_394_535_676).saturating_mul(r.into())) + .saturating_add(T::DbWeight::get().reads(6_u64)) .saturating_add(T::DbWeight::get().reads((225_u64).saturating_mul(r.into()))) - .saturating_add(T::DbWeight::get().writes(3)) + .saturating_add(T::DbWeight::get().writes(3_u64)) .saturating_add(T::DbWeight::get().writes((150_u64).saturating_mul(r.into()))) } - // Storage: System Account (r:1 w:0) - // Storage: Contracts ContractInfoOf (r:1 w:1) - // Storage: Contracts CodeStorage (r:1 w:0) - // Storage: Timestamp Now (r:1 w:0) - // Storage: System EventTopics (r:2 w:2) + /// Storage: System Account (r:1 w:0) + /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: Ignored) + /// Storage: Contracts ContractInfoOf (r:1 w:1) + /// Proof: Contracts ContractInfoOf (max_values: None, max_size: Some(258), added: 2733, mode: Ignored) + /// Storage: Contracts CodeStorage (r:1 w:0) + /// Proof: Contracts CodeStorage (max_values: None, max_size: Some(126001), added: 128476, mode: Ignored) + /// Storage: Timestamp Now (r:1 w:0) + /// Proof: Timestamp Now (max_values: Some(1), max_size: Some(8), added: 503, mode: Ignored) + /// Storage: System EventTopics (r:2 w:2) + /// Proof Skipped: System EventTopics (max_values: None, max_size: None, mode: Ignored) /// The range of component `r` is `[0, 20]`. fn seal_reentrance_count(r: u32, ) -> Weight { - // Minimum execution time: 295_339 nanoseconds. - Weight::from_ref_time(301_577_907) - // Standard Error: 23_836 - .saturating_add(Weight::from_ref_time(10_876_508).saturating_mul(r.into())) - .saturating_add(T::DbWeight::get().reads(6)) - .saturating_add(T::DbWeight::get().writes(3)) - } - // Storage: System Account (r:1 w:0) - // Storage: Contracts ContractInfoOf (r:1 w:1) - // Storage: Contracts CodeStorage (r:1 w:0) - // Storage: Timestamp Now (r:1 w:0) - // Storage: System EventTopics (r:2 w:2) + // Proof Size summary in bytes: + // Measured: `837 + r * (240 ±0)` + // Estimated: `0` + // Minimum execution time: 286_631 nanoseconds. + Weight::from_ref_time(288_787_650) + // Standard Error: 29_802 + .saturating_add(Weight::from_ref_time(11_115_811).saturating_mul(r.into())) + .saturating_add(T::DbWeight::get().reads(6_u64)) + .saturating_add(T::DbWeight::get().writes(3_u64)) + } + /// Storage: System Account (r:1 w:0) + /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: Ignored) + /// Storage: Contracts ContractInfoOf (r:1 w:1) + /// Proof: Contracts ContractInfoOf (max_values: None, max_size: Some(258), added: 2733, mode: Ignored) + /// Storage: Contracts CodeStorage (r:1 w:0) + /// Proof: Contracts CodeStorage (max_values: None, max_size: Some(126001), added: 128476, mode: Ignored) + /// Storage: Timestamp Now (r:1 w:0) + /// Proof: Timestamp Now (max_values: Some(1), max_size: Some(8), added: 503, mode: Ignored) + /// Storage: System EventTopics (r:2 w:2) + /// Proof Skipped: System EventTopics (max_values: None, max_size: None, mode: Ignored) /// The range of component `r` is `[0, 20]`. fn seal_account_reentrance_count(r: u32, ) -> Weight { - // Minimum execution time: 297_096 nanoseconds. - Weight::from_ref_time(336_319_823) - // Standard Error: 128_941 - .saturating_add(Weight::from_ref_time(25_211_374).saturating_mul(r.into())) - .saturating_add(T::DbWeight::get().reads(6)) - .saturating_add(T::DbWeight::get().writes(3)) - } - // Storage: System Account (r:1 w:0) - // Storage: Contracts ContractInfoOf (r:1 w:1) - // Storage: Contracts CodeStorage (r:1 w:0) - // Storage: Timestamp Now (r:1 w:0) - // Storage: System EventTopics (r:2 w:2) - // Storage: Contracts Nonce (r:1 w:1) + // Proof Size summary in bytes: + // Measured: `2056 + r * (3153 ±0)` + // Estimated: `0` + // Minimum execution time: 287_775 nanoseconds. + Weight::from_ref_time(319_806_123) + // Standard Error: 111_808 + .saturating_add(Weight::from_ref_time(17_641_181).saturating_mul(r.into())) + .saturating_add(T::DbWeight::get().reads(6_u64)) + .saturating_add(T::DbWeight::get().writes(3_u64)) + } + /// Storage: System Account (r:1 w:0) + /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: Ignored) + /// Storage: Contracts ContractInfoOf (r:1 w:1) + /// Proof: Contracts ContractInfoOf (max_values: None, max_size: Some(258), added: 2733, mode: Ignored) + /// Storage: Contracts CodeStorage (r:1 w:0) + /// Proof: Contracts CodeStorage (max_values: None, max_size: Some(126001), added: 128476, mode: Ignored) + /// Storage: Timestamp Now (r:1 w:0) + /// Proof: Timestamp Now (max_values: Some(1), max_size: Some(8), added: 503, mode: Ignored) + /// Storage: Contracts Nonce (r:1 w:1) + /// Proof: Contracts Nonce (max_values: Some(1), max_size: Some(8), added: 503, mode: Ignored) + /// Storage: System EventTopics (r:2 w:2) + /// Proof Skipped: System EventTopics (max_values: None, max_size: None, mode: Ignored) /// The range of component `r` is `[0, 20]`. fn seal_instantiation_nonce(r: u32, ) -> Weight { - // Minimum execution time: 293_819 nanoseconds. - Weight::from_ref_time(302_765_659) - // Standard Error: 28_518 - .saturating_add(Weight::from_ref_time(9_325_552).saturating_mul(r.into())) - .saturating_add(T::DbWeight::get().reads(7)) - .saturating_add(T::DbWeight::get().writes(4)) + // Proof Size summary in bytes: + // Measured: `840 + r * (240 ±0)` + // Estimated: `0` + // Minimum execution time: 285_077 nanoseconds. + Weight::from_ref_time(289_980_475) + // Standard Error: 14_535 + .saturating_add(Weight::from_ref_time(9_295_346).saturating_mul(r.into())) + .saturating_add(T::DbWeight::get().reads(7_u64)) + .saturating_add(T::DbWeight::get().writes(4_u64)) } /// The range of component `r` is `[0, 50]`. fn instr_i64const(r: u32, ) -> Weight { - // Minimum execution time: 805 nanoseconds. - Weight::from_ref_time(1_002_066) - // Standard Error: 609 - .saturating_add(Weight::from_ref_time(346_608).saturating_mul(r.into())) + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 777 nanoseconds. + Weight::from_ref_time(1_014_498) + // Standard Error: 154 + .saturating_add(Weight::from_ref_time(405_551).saturating_mul(r.into())) } /// The range of component `r` is `[0, 50]`. fn instr_i64load(r: u32, ) -> Weight { - // Minimum execution time: 917 nanoseconds. - Weight::from_ref_time(1_247_710) - // Standard Error: 1_069 - .saturating_add(Weight::from_ref_time(984_608).saturating_mul(r.into())) + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 862 nanoseconds. + Weight::from_ref_time(1_345_826) + // Standard Error: 457 + .saturating_add(Weight::from_ref_time(1_033_909).saturating_mul(r.into())) } /// The range of component `r` is `[0, 50]`. fn instr_i64store(r: u32, ) -> Weight { - // Minimum execution time: 917 nanoseconds. - Weight::from_ref_time(1_244_315) - // Standard Error: 524 - .saturating_add(Weight::from_ref_time(882_167).saturating_mul(r.into())) + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 892 nanoseconds. + Weight::from_ref_time(1_233_601) + // Standard Error: 341 + .saturating_add(Weight::from_ref_time(885_275).saturating_mul(r.into())) } /// The range of component `r` is `[0, 50]`. fn instr_select(r: u32, ) -> Weight { - // Minimum execution time: 791 nanoseconds. - Weight::from_ref_time(1_119_053) - // Standard Error: 379 - .saturating_add(Weight::from_ref_time(956_169).saturating_mul(r.into())) + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 771 nanoseconds. + Weight::from_ref_time(1_099_906) + // Standard Error: 261 + .saturating_add(Weight::from_ref_time(1_092_031).saturating_mul(r.into())) } /// The range of component `r` is `[0, 50]`. fn instr_if(r: u32, ) -> Weight { - // Minimum execution time: 782 nanoseconds. - Weight::from_ref_time(773_373) - // Standard Error: 463 - .saturating_add(Weight::from_ref_time(1_299_306).saturating_mul(r.into())) + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 785 nanoseconds. + Weight::from_ref_time(929_328) + // Standard Error: 333 + .saturating_add(Weight::from_ref_time(1_374_749).saturating_mul(r.into())) } /// The range of component `r` is `[0, 50]`. fn instr_br(r: u32, ) -> Weight { - // Minimum execution time: 743 nanoseconds. - Weight::from_ref_time(1_273_896) - // Standard Error: 925 - .saturating_add(Weight::from_ref_time(527_015).saturating_mul(r.into())) + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 772 nanoseconds. + Weight::from_ref_time(979_702) + // Standard Error: 351 + .saturating_add(Weight::from_ref_time(621_385).saturating_mul(r.into())) } /// The range of component `r` is `[0, 50]`. fn instr_br_if(r: u32, ) -> Weight { - // Minimum execution time: 761 nanoseconds. - Weight::from_ref_time(929_565) - // Standard Error: 1_275 - .saturating_add(Weight::from_ref_time(805_592).saturating_mul(r.into())) + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 766 nanoseconds. + Weight::from_ref_time(1_303_783) + // Standard Error: 1_556 + .saturating_add(Weight::from_ref_time(841_842).saturating_mul(r.into())) } /// The range of component `r` is `[0, 50]`. fn instr_br_table(r: u32, ) -> Weight { - // Minimum execution time: 813 nanoseconds. - Weight::from_ref_time(703_802) - // Standard Error: 1_665 - .saturating_add(Weight::from_ref_time(1_074_321).saturating_mul(r.into())) + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 792 nanoseconds. + Weight::from_ref_time(1_093_901) + // Standard Error: 1_383 + .saturating_add(Weight::from_ref_time(1_145_435).saturating_mul(r.into())) } /// The range of component `e` is `[1, 256]`. fn instr_br_table_per_entry(e: u32, ) -> Weight { - // Minimum execution time: 2_347 nanoseconds. - Weight::from_ref_time(2_752_638) - // Standard Error: 69 - .saturating_add(Weight::from_ref_time(4_771).saturating_mul(e.into())) + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 2_526 nanoseconds. + Weight::from_ref_time(2_872_561) + // Standard Error: 60 + .saturating_add(Weight::from_ref_time(4_365).saturating_mul(e.into())) } /// The range of component `r` is `[0, 50]`. fn instr_call(r: u32, ) -> Weight { - // Minimum execution time: 826 nanoseconds. - Weight::from_ref_time(1_784_596) - // Standard Error: 1_646 - .saturating_add(Weight::from_ref_time(2_189_993).saturating_mul(r.into())) + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 834 nanoseconds. + Weight::from_ref_time(1_431_876) + // Standard Error: 1_448 + .saturating_add(Weight::from_ref_time(2_268_715).saturating_mul(r.into())) } /// The range of component `r` is `[0, 50]`. fn instr_call_indirect(r: u32, ) -> Weight { - // Minimum execution time: 926 nanoseconds. - Weight::from_ref_time(1_898_358) - // Standard Error: 2_678 - .saturating_add(Weight::from_ref_time(2_811_379).saturating_mul(r.into())) + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 920 nanoseconds. + Weight::from_ref_time(2_167_004) + // Standard Error: 2_060 + .saturating_add(Weight::from_ref_time(2_921_443).saturating_mul(r.into())) } /// The range of component `p` is `[0, 128]`. fn instr_call_indirect_per_param(p: u32, ) -> Weight { - // Minimum execution time: 4_428 nanoseconds. - Weight::from_ref_time(5_257_445) - // Standard Error: 306 - .saturating_add(Weight::from_ref_time(178_882).saturating_mul(p.into())) + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 4_624 nanoseconds. + Weight::from_ref_time(5_534_325) + // Standard Error: 326 + .saturating_add(Weight::from_ref_time(184_307).saturating_mul(p.into())) } /// The range of component `l` is `[0, 1024]`. fn instr_call_per_local(l: u32, ) -> Weight { - // Minimum execution time: 2_945 nanoseconds. - Weight::from_ref_time(4_240_619) - // Standard Error: 39 - .saturating_add(Weight::from_ref_time(46_640).saturating_mul(l.into())) + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 3_062 nanoseconds. + Weight::from_ref_time(4_432_879) + // Standard Error: 64 + .saturating_add(Weight::from_ref_time(46_196).saturating_mul(l.into())) } /// The range of component `r` is `[0, 50]`. fn instr_local_get(r: u32, ) -> Weight { - // Minimum execution time: 2_204 nanoseconds. - Weight::from_ref_time(2_430_272) - // Standard Error: 270 - .saturating_add(Weight::from_ref_time(365_718).saturating_mul(r.into())) + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 2_036 nanoseconds. + Weight::from_ref_time(2_318_877) + // Standard Error: 172 + .saturating_add(Weight::from_ref_time(500_498).saturating_mul(r.into())) } /// The range of component `r` is `[0, 50]`. fn instr_local_set(r: u32, ) -> Weight { - // Minimum execution time: 2_117 nanoseconds. - Weight::from_ref_time(2_429_354) - // Standard Error: 240 - .saturating_add(Weight::from_ref_time(381_830).saturating_mul(r.into())) + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 2_027 nanoseconds. + Weight::from_ref_time(2_355_900) + // Standard Error: 220 + .saturating_add(Weight::from_ref_time(461_393).saturating_mul(r.into())) } /// The range of component `r` is `[0, 50]`. fn instr_local_tee(r: u32, ) -> Weight { - // Minimum execution time: 2_123 nanoseconds. - Weight::from_ref_time(2_460_016) - // Standard Error: 262 - .saturating_add(Weight::from_ref_time(526_554).saturating_mul(r.into())) + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 2_038 nanoseconds. + Weight::from_ref_time(2_350_330) + // Standard Error: 224 + .saturating_add(Weight::from_ref_time(586_808).saturating_mul(r.into())) } /// The range of component `r` is `[0, 50]`. fn instr_global_get(r: u32, ) -> Weight { - // Minimum execution time: 892 nanoseconds. - Weight::from_ref_time(1_234_618) - // Standard Error: 485 - .saturating_add(Weight::from_ref_time(813_721).saturating_mul(r.into())) + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 897 nanoseconds. + Weight::from_ref_time(1_267_115) + // Standard Error: 189 + .saturating_add(Weight::from_ref_time(884_926).saturating_mul(r.into())) } /// The range of component `r` is `[0, 50]`. fn instr_global_set(r: u32, ) -> Weight { - // Minimum execution time: 901 nanoseconds. - Weight::from_ref_time(1_182_838) - // Standard Error: 482 - .saturating_add(Weight::from_ref_time(831_308).saturating_mul(r.into())) + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 892 nanoseconds. + Weight::from_ref_time(1_202_122) + // Standard Error: 286 + .saturating_add(Weight::from_ref_time(885_157).saturating_mul(r.into())) } /// The range of component `r` is `[0, 50]`. fn instr_memory_current(r: u32, ) -> Weight { - // Minimum execution time: 929 nanoseconds. - Weight::from_ref_time(1_221_388) - // Standard Error: 434 - .saturating_add(Weight::from_ref_time(693_301).saturating_mul(r.into())) + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 852 nanoseconds. + Weight::from_ref_time(1_132_479) + // Standard Error: 224 + .saturating_add(Weight::from_ref_time(719_603).saturating_mul(r.into())) } /// The range of component `r` is `[0, 1]`. fn instr_memory_grow(r: u32, ) -> Weight { - // Minimum execution time: 808 nanoseconds. - Weight::from_ref_time(906_944) - // Standard Error: 6_164 - .saturating_add(Weight::from_ref_time(187_445_255).saturating_mul(r.into())) + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 786 nanoseconds. + Weight::from_ref_time(874_044) + // Standard Error: 91_309 + .saturating_add(Weight::from_ref_time(181_849_955).saturating_mul(r.into())) } /// The range of component `r` is `[0, 50]`. fn instr_i64clz(r: u32, ) -> Weight { - // Minimum execution time: 807 nanoseconds. - Weight::from_ref_time(1_064_776) - // Standard Error: 287 - .saturating_add(Weight::from_ref_time(507_278).saturating_mul(r.into())) + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 763 nanoseconds. + Weight::from_ref_time(1_055_236) + // Standard Error: 207 + .saturating_add(Weight::from_ref_time(554_985).saturating_mul(r.into())) } /// The range of component `r` is `[0, 50]`. fn instr_i64ctz(r: u32, ) -> Weight { - // Minimum execution time: 776 nanoseconds. - Weight::from_ref_time(1_033_205) - // Standard Error: 443 - .saturating_add(Weight::from_ref_time(512_445).saturating_mul(r.into())) + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 756 nanoseconds. + Weight::from_ref_time(1_053_050) + // Standard Error: 165 + .saturating_add(Weight::from_ref_time(555_401).saturating_mul(r.into())) } /// The range of component `r` is `[0, 50]`. fn instr_i64popcnt(r: u32, ) -> Weight { - // Minimum execution time: 909 nanoseconds. - Weight::from_ref_time(1_089_083) - // Standard Error: 335 - .saturating_add(Weight::from_ref_time(510_392).saturating_mul(r.into())) + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 791 nanoseconds. + Weight::from_ref_time(1_080_240) + // Standard Error: 164 + .saturating_add(Weight::from_ref_time(554_698).saturating_mul(r.into())) } /// The range of component `r` is `[0, 50]`. fn instr_i64eqz(r: u32, ) -> Weight { - // Minimum execution time: 791 nanoseconds. - Weight::from_ref_time(1_063_118) - // Standard Error: 210 - .saturating_add(Weight::from_ref_time(522_817).saturating_mul(r.into())) + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 766 nanoseconds. + Weight::from_ref_time(1_074_739) + // Standard Error: 178 + .saturating_add(Weight::from_ref_time(565_891).saturating_mul(r.into())) } /// The range of component `r` is `[0, 50]`. fn instr_i64extendsi32(r: u32, ) -> Weight { - // Minimum execution time: 789 nanoseconds. - Weight::from_ref_time(1_056_006) - // Standard Error: 240 - .saturating_add(Weight::from_ref_time(505_198).saturating_mul(r.into())) + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 781 nanoseconds. + Weight::from_ref_time(1_077_122) + // Standard Error: 177 + .saturating_add(Weight::from_ref_time(548_846).saturating_mul(r.into())) } /// The range of component `r` is `[0, 50]`. fn instr_i64extendui32(r: u32, ) -> Weight { - // Minimum execution time: 804 nanoseconds. - Weight::from_ref_time(1_087_882) - // Standard Error: 234 - .saturating_add(Weight::from_ref_time(503_830).saturating_mul(r.into())) + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 793 nanoseconds. + Weight::from_ref_time(1_086_278) + // Standard Error: 163 + .saturating_add(Weight::from_ref_time(548_765).saturating_mul(r.into())) } /// The range of component `r` is `[0, 50]`. fn instr_i32wrapi64(r: u32, ) -> Weight { - // Minimum execution time: 756 nanoseconds. - Weight::from_ref_time(1_050_994) - // Standard Error: 172 - .saturating_add(Weight::from_ref_time(503_343).saturating_mul(r.into())) + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 769 nanoseconds. + Weight::from_ref_time(1_096_044) + // Standard Error: 134 + .saturating_add(Weight::from_ref_time(547_353).saturating_mul(r.into())) } /// The range of component `r` is `[0, 50]`. fn instr_i64eq(r: u32, ) -> Weight { - // Minimum execution time: 775 nanoseconds. - Weight::from_ref_time(1_061_228) - // Standard Error: 232 - .saturating_add(Weight::from_ref_time(730_751).saturating_mul(r.into())) + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 766 nanoseconds. + Weight::from_ref_time(1_072_610) + // Standard Error: 161 + .saturating_add(Weight::from_ref_time(774_895).saturating_mul(r.into())) } /// The range of component `r` is `[0, 50]`. fn instr_i64ne(r: u32, ) -> Weight { - // Minimum execution time: 809 nanoseconds. - Weight::from_ref_time(1_080_957) - // Standard Error: 320 - .saturating_add(Weight::from_ref_time(732_246).saturating_mul(r.into())) + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 751 nanoseconds. + Weight::from_ref_time(1_038_676) + // Standard Error: 158 + .saturating_add(Weight::from_ref_time(775_194).saturating_mul(r.into())) } /// The range of component `r` is `[0, 50]`. fn instr_i64lts(r: u32, ) -> Weight { - // Minimum execution time: 808 nanoseconds. - Weight::from_ref_time(1_055_752) - // Standard Error: 496 - .saturating_add(Weight::from_ref_time(740_084).saturating_mul(r.into())) + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 798 nanoseconds. + Weight::from_ref_time(1_089_712) + // Standard Error: 144 + .saturating_add(Weight::from_ref_time(774_377).saturating_mul(r.into())) } /// The range of component `r` is `[0, 50]`. fn instr_i64ltu(r: u32, ) -> Weight { - // Minimum execution time: 792 nanoseconds. - Weight::from_ref_time(1_111_972) - // Standard Error: 270 - .saturating_add(Weight::from_ref_time(741_581).saturating_mul(r.into())) + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 758 nanoseconds. + Weight::from_ref_time(1_078_460) + // Standard Error: 180 + .saturating_add(Weight::from_ref_time(779_861).saturating_mul(r.into())) } /// The range of component `r` is `[0, 50]`. fn instr_i64gts(r: u32, ) -> Weight { - // Minimum execution time: 862 nanoseconds. - Weight::from_ref_time(1_111_246) - // Standard Error: 263 - .saturating_add(Weight::from_ref_time(746_026).saturating_mul(r.into())) + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 758 nanoseconds. + Weight::from_ref_time(1_089_007) + // Standard Error: 164 + .saturating_add(Weight::from_ref_time(779_372).saturating_mul(r.into())) } /// The range of component `r` is `[0, 50]`. fn instr_i64gtu(r: u32, ) -> Weight { - // Minimum execution time: 810 nanoseconds. - Weight::from_ref_time(1_094_933) - // Standard Error: 271 - .saturating_add(Weight::from_ref_time(743_645).saturating_mul(r.into())) + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 772 nanoseconds. + Weight::from_ref_time(1_077_512) + // Standard Error: 165 + .saturating_add(Weight::from_ref_time(779_513).saturating_mul(r.into())) } /// The range of component `r` is `[0, 50]`. fn instr_i64les(r: u32, ) -> Weight { - // Minimum execution time: 769 nanoseconds. - Weight::from_ref_time(819_933) - // Standard Error: 4_528 - .saturating_add(Weight::from_ref_time(751_599).saturating_mul(r.into())) + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 760 nanoseconds. + Weight::from_ref_time(1_078_546) + // Standard Error: 168 + .saturating_add(Weight::from_ref_time(779_138).saturating_mul(r.into())) } /// The range of component `r` is `[0, 50]`. fn instr_i64leu(r: u32, ) -> Weight { - // Minimum execution time: 797 nanoseconds. - Weight::from_ref_time(1_107_352) - // Standard Error: 344 - .saturating_add(Weight::from_ref_time(738_684).saturating_mul(r.into())) + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 757 nanoseconds. + Weight::from_ref_time(1_080_251) + // Standard Error: 168 + .saturating_add(Weight::from_ref_time(779_391).saturating_mul(r.into())) } /// The range of component `r` is `[0, 50]`. fn instr_i64ges(r: u32, ) -> Weight { - // Minimum execution time: 784 nanoseconds. - Weight::from_ref_time(1_068_487) - // Standard Error: 3_796 - .saturating_add(Weight::from_ref_time(741_207).saturating_mul(r.into())) + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 786 nanoseconds. + Weight::from_ref_time(1_072_690) + // Standard Error: 219 + .saturating_add(Weight::from_ref_time(780_381).saturating_mul(r.into())) } /// The range of component `r` is `[0, 50]`. fn instr_i64geu(r: u32, ) -> Weight { - // Minimum execution time: 813 nanoseconds. - Weight::from_ref_time(1_067_278) - // Standard Error: 459 - .saturating_add(Weight::from_ref_time(737_841).saturating_mul(r.into())) + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 771 nanoseconds. + Weight::from_ref_time(1_063_735) + // Standard Error: 162 + .saturating_add(Weight::from_ref_time(779_906).saturating_mul(r.into())) } /// The range of component `r` is `[0, 50]`. fn instr_i64add(r: u32, ) -> Weight { - // Minimum execution time: 798 nanoseconds. - Weight::from_ref_time(1_100_254) - // Standard Error: 297 - .saturating_add(Weight::from_ref_time(717_622).saturating_mul(r.into())) + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 785 nanoseconds. + Weight::from_ref_time(1_059_585) + // Standard Error: 155 + .saturating_add(Weight::from_ref_time(756_828).saturating_mul(r.into())) } /// The range of component `r` is `[0, 50]`. fn instr_i64sub(r: u32, ) -> Weight { - // Minimum execution time: 778 nanoseconds. - Weight::from_ref_time(1_063_748) - // Standard Error: 378 - .saturating_add(Weight::from_ref_time(713_577).saturating_mul(r.into())) + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 800 nanoseconds. + Weight::from_ref_time(1_066_659) + // Standard Error: 154 + .saturating_add(Weight::from_ref_time(754_318).saturating_mul(r.into())) } /// The range of component `r` is `[0, 50]`. fn instr_i64mul(r: u32, ) -> Weight { - // Minimum execution time: 780 nanoseconds. - Weight::from_ref_time(1_088_755) - // Standard Error: 316 - .saturating_add(Weight::from_ref_time(714_127).saturating_mul(r.into())) + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 769 nanoseconds. + Weight::from_ref_time(1_078_854) + // Standard Error: 172 + .saturating_add(Weight::from_ref_time(754_183).saturating_mul(r.into())) } /// The range of component `r` is `[0, 50]`. fn instr_i64divs(r: u32, ) -> Weight { - // Minimum execution time: 776 nanoseconds. - Weight::from_ref_time(1_062_591) - // Standard Error: 610 - .saturating_add(Weight::from_ref_time(1_355_430).saturating_mul(r.into())) + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 770 nanoseconds. + Weight::from_ref_time(1_057_476) + // Standard Error: 191 + .saturating_add(Weight::from_ref_time(1_443_902).saturating_mul(r.into())) } /// The range of component `r` is `[0, 50]`. fn instr_i64divu(r: u32, ) -> Weight { - // Minimum execution time: 801 nanoseconds. - Weight::from_ref_time(1_092_395) - // Standard Error: 423 - .saturating_add(Weight::from_ref_time(1_282_356).saturating_mul(r.into())) + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 764 nanoseconds. + Weight::from_ref_time(1_063_821) + // Standard Error: 193 + .saturating_add(Weight::from_ref_time(1_324_496).saturating_mul(r.into())) } /// The range of component `r` is `[0, 50]`. fn instr_i64rems(r: u32, ) -> Weight { - // Minimum execution time: 822 nanoseconds. - Weight::from_ref_time(1_083_079) - // Standard Error: 562 - .saturating_add(Weight::from_ref_time(1_398_522).saturating_mul(r.into())) + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 747 nanoseconds. + Weight::from_ref_time(1_093_209) + // Standard Error: 270 + .saturating_add(Weight::from_ref_time(1_447_180).saturating_mul(r.into())) } /// The range of component `r` is `[0, 50]`. fn instr_i64remu(r: u32, ) -> Weight { - // Minimum execution time: 798 nanoseconds. - Weight::from_ref_time(1_088_997) - // Standard Error: 401 - .saturating_add(Weight::from_ref_time(1_283_442).saturating_mul(r.into())) + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 805 nanoseconds. + Weight::from_ref_time(1_033_953) + // Standard Error: 152 + .saturating_add(Weight::from_ref_time(1_336_911).saturating_mul(r.into())) } /// The range of component `r` is `[0, 50]`. fn instr_i64and(r: u32, ) -> Weight { - // Minimum execution time: 815 nanoseconds. - Weight::from_ref_time(1_081_903) - // Standard Error: 264 - .saturating_add(Weight::from_ref_time(718_069).saturating_mul(r.into())) + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 766 nanoseconds. + Weight::from_ref_time(1_059_430) + // Standard Error: 160 + .saturating_add(Weight::from_ref_time(757_265).saturating_mul(r.into())) } /// The range of component `r` is `[0, 50]`. fn instr_i64or(r: u32, ) -> Weight { - // Minimum execution time: 813 nanoseconds. - Weight::from_ref_time(1_091_426) - // Standard Error: 296 - .saturating_add(Weight::from_ref_time(717_894).saturating_mul(r.into())) + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 760 nanoseconds. + Weight::from_ref_time(1_077_376) + // Standard Error: 160 + .saturating_add(Weight::from_ref_time(755_800).saturating_mul(r.into())) } /// The range of component `r` is `[0, 50]`. fn instr_i64xor(r: u32, ) -> Weight { - // Minimum execution time: 794 nanoseconds. - Weight::from_ref_time(1_040_144) - // Standard Error: 400 - .saturating_add(Weight::from_ref_time(719_886).saturating_mul(r.into())) + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 798 nanoseconds. + Weight::from_ref_time(1_070_570) + // Standard Error: 157 + .saturating_add(Weight::from_ref_time(756_839).saturating_mul(r.into())) } /// The range of component `r` is `[0, 50]`. fn instr_i64shl(r: u32, ) -> Weight { - // Minimum execution time: 774 nanoseconds. - Weight::from_ref_time(1_092_466) - // Standard Error: 4_173 - .saturating_add(Weight::from_ref_time(739_873).saturating_mul(r.into())) + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 761 nanoseconds. + Weight::from_ref_time(1_074_645) + // Standard Error: 169 + .saturating_add(Weight::from_ref_time(771_486).saturating_mul(r.into())) } /// The range of component `r` is `[0, 50]`. fn instr_i64shrs(r: u32, ) -> Weight { - // Minimum execution time: 793 nanoseconds. - Weight::from_ref_time(1_105_611) - // Standard Error: 394 - .saturating_add(Weight::from_ref_time(735_358).saturating_mul(r.into())) + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 779 nanoseconds. + Weight::from_ref_time(1_107_671) + // Standard Error: 185 + .saturating_add(Weight::from_ref_time(769_168).saturating_mul(r.into())) } /// The range of component `r` is `[0, 50]`. fn instr_i64shru(r: u32, ) -> Weight { - // Minimum execution time: 805 nanoseconds. - Weight::from_ref_time(1_130_462) - // Standard Error: 327 - .saturating_add(Weight::from_ref_time(734_198).saturating_mul(r.into())) + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 755 nanoseconds. + Weight::from_ref_time(1_075_769) + // Standard Error: 164 + .saturating_add(Weight::from_ref_time(770_334).saturating_mul(r.into())) } /// The range of component `r` is `[0, 50]`. fn instr_i64rotl(r: u32, ) -> Weight { - // Minimum execution time: 831 nanoseconds. - Weight::from_ref_time(1_118_894) - // Standard Error: 389 - .saturating_add(Weight::from_ref_time(733_834).saturating_mul(r.into())) + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 767 nanoseconds. + Weight::from_ref_time(608_749) + // Standard Error: 2_059 + .saturating_add(Weight::from_ref_time(804_228).saturating_mul(r.into())) } /// The range of component `r` is `[0, 50]`. fn instr_i64rotr(r: u32, ) -> Weight { - // Minimum execution time: 772 nanoseconds. - Weight::from_ref_time(1_123_733) - // Standard Error: 337 - .saturating_add(Weight::from_ref_time(733_923).saturating_mul(r.into())) + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 779 nanoseconds. + Weight::from_ref_time(1_054_998) + // Standard Error: 191 + .saturating_add(Weight::from_ref_time(770_225).saturating_mul(r.into())) } } // For backwards compatibility and tests impl WeightInfo for () { - // Storage: Contracts DeletionQueue (r:1 w:0) + /// Storage: Contracts DeletionQueue (r:1 w:0) + /// Proof: Contracts DeletionQueue (max_values: Some(1), max_size: Some(16642), added: 17137, mode: Ignored) fn on_process_deletion_queue_batch() -> Weight { - // Minimum execution time: 3_326 nanoseconds. - Weight::from_ref_time(3_433_000) - .saturating_add(RocksDbWeight::get().reads(1)) - } - // Storage: Skipped Metadata (r:0 w:0) + // Proof Size summary in bytes: + // Measured: `109` + // Estimated: `0` + // Minimum execution time: 2_305 nanoseconds. + Weight::from_ref_time(2_560_000) + .saturating_add(RocksDbWeight::get().reads(1_u64)) + } + /// Storage: Skipped Metadata (r:0 w:0) + /// Proof Skipped: Skipped Metadata (max_values: None, max_size: None, mode: Ignored) /// The range of component `k` is `[0, 1024]`. fn on_initialize_per_trie_key(k: u32, ) -> Weight { - // Minimum execution time: 15_703 nanoseconds. - Weight::from_ref_time(15_288_927) - // Standard Error: 951 - .saturating_add(Weight::from_ref_time(940_816).saturating_mul(k.into())) - .saturating_add(RocksDbWeight::get().reads(1)) - .saturating_add(RocksDbWeight::get().writes(1)) + // Proof Size summary in bytes: + // Measured: `481 + k * (69 ±0)` + // Estimated: `0` + // Minimum execution time: 9_311 nanoseconds. + Weight::from_ref_time(5_419_288) + // Standard Error: 562 + .saturating_add(Weight::from_ref_time(911_962).saturating_mul(k.into())) + .saturating_add(RocksDbWeight::get().reads(1_u64)) + .saturating_add(RocksDbWeight::get().writes(1_u64)) .saturating_add(RocksDbWeight::get().writes((1_u64).saturating_mul(k.into()))) } - // Storage: Contracts DeletionQueue (r:1 w:0) + /// Storage: Contracts DeletionQueue (r:1 w:1) + /// Proof: Contracts DeletionQueue (max_values: Some(1), max_size: Some(16642), added: 17137, mode: Ignored) /// The range of component `q` is `[0, 128]`. fn on_initialize_per_queue_item(q: u32, ) -> Weight { - // Minimum execution time: 3_548 nanoseconds. - Weight::from_ref_time(15_764_121) - // Standard Error: 3_339 - .saturating_add(Weight::from_ref_time(1_214_890).saturating_mul(q.into())) - .saturating_add(RocksDbWeight::get().reads(1)) - .saturating_add(RocksDbWeight::get().writes(1)) - } - // Storage: Contracts PristineCode (r:1 w:0) - // Storage: Contracts CodeStorage (r:0 w:1) - /// The range of component `c` is `[0, 64226]`. + // Proof Size summary in bytes: + // Measured: `281 + q * (33 ±0)` + // Estimated: `0` + // Minimum execution time: 2_288 nanoseconds. + Weight::from_ref_time(9_442_437) + // Standard Error: 2_720 + .saturating_add(Weight::from_ref_time(1_076_950).saturating_mul(q.into())) + .saturating_add(RocksDbWeight::get().reads(1_u64)) + .saturating_add(RocksDbWeight::get().writes(1_u64)) + } + /// Storage: Contracts PristineCode (r:1 w:0) + /// Proof: Contracts PristineCode (max_values: None, max_size: Some(125988), added: 128463, mode: Ignored) + /// Storage: Contracts CodeStorage (r:0 w:1) + /// Proof: Contracts CodeStorage (max_values: None, max_size: Some(126001), added: 128476, mode: Ignored) + /// The range of component `c` is `[0, 61717]`. fn reinstrument(c: u32, ) -> Weight { - // Minimum execution time: 29_801 nanoseconds. - Weight::from_ref_time(27_932_706) - // Standard Error: 49 - .saturating_add(Weight::from_ref_time(50_532).saturating_mul(c.into())) - .saturating_add(RocksDbWeight::get().reads(1)) - .saturating_add(RocksDbWeight::get().writes(1)) - } - // Storage: Contracts ContractInfoOf (r:1 w:1) - // Storage: Contracts CodeStorage (r:1 w:0) - // Storage: Timestamp Now (r:1 w:0) - // Storage: System Account (r:1 w:1) - // Storage: System EventTopics (r:2 w:2) - /// The range of component `c` is `[0, 131072]`. + // Proof Size summary in bytes: + // Measured: `270 + c * (1 ±0)` + // Estimated: `0` + // Minimum execution time: 27_539 nanoseconds. + Weight::from_ref_time(23_554_889) + // Standard Error: 56 + .saturating_add(Weight::from_ref_time(46_766).saturating_mul(c.into())) + .saturating_add(RocksDbWeight::get().reads(1_u64)) + .saturating_add(RocksDbWeight::get().writes(1_u64)) + } + /// Storage: Contracts ContractInfoOf (r:1 w:1) + /// Proof: Contracts ContractInfoOf (max_values: None, max_size: Some(258), added: 2733, mode: Ignored) + /// Storage: Contracts CodeStorage (r:1 w:0) + /// Proof: Contracts CodeStorage (max_values: None, max_size: Some(126001), added: 128476, mode: Ignored) + /// Storage: Timestamp Now (r:1 w:0) + /// Proof: Timestamp Now (max_values: Some(1), max_size: Some(8), added: 503, mode: Ignored) + /// Storage: System Account (r:1 w:1) + /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: Ignored) + /// Storage: System EventTopics (r:2 w:2) + /// Proof Skipped: System EventTopics (max_values: None, max_size: None, mode: Ignored) + /// The range of component `c` is `[0, 125952]`. fn call_with_code_per_byte(c: u32, ) -> Weight { - // Minimum execution time: 307_894 nanoseconds. - Weight::from_ref_time(322_346_319) - // Standard Error: 25 - .saturating_add(Weight::from_ref_time(30_678).saturating_mul(c.into())) - .saturating_add(RocksDbWeight::get().reads(6)) - .saturating_add(RocksDbWeight::get().writes(4)) - } - // Storage: Contracts CodeStorage (r:1 w:1) - // Storage: Contracts Nonce (r:1 w:1) - // Storage: Contracts ContractInfoOf (r:1 w:1) - // Storage: Timestamp Now (r:1 w:0) - // Storage: System Account (r:1 w:1) - // Storage: System EventTopics (r:3 w:3) - // Storage: Contracts PristineCode (r:0 w:1) - // Storage: Contracts OwnerInfoOf (r:0 w:1) - /// The range of component `c` is `[0, 64226]`. + // Proof Size summary in bytes: + // Measured: `771` + // Estimated: `0` + // Minimum execution time: 297_710 nanoseconds. + Weight::from_ref_time(307_327_529) + // Standard Error: 18 + .saturating_add(Weight::from_ref_time(29_849).saturating_mul(c.into())) + .saturating_add(RocksDbWeight::get().reads(6_u64)) + .saturating_add(RocksDbWeight::get().writes(4_u64)) + } + /// Storage: Contracts CodeStorage (r:1 w:1) + /// Proof: Contracts CodeStorage (max_values: None, max_size: Some(126001), added: 128476, mode: Ignored) + /// Storage: Contracts Nonce (r:1 w:1) + /// Proof: Contracts Nonce (max_values: Some(1), max_size: Some(8), added: 503, mode: Ignored) + /// Storage: Contracts ContractInfoOf (r:1 w:1) + /// Proof: Contracts ContractInfoOf (max_values: None, max_size: Some(258), added: 2733, mode: Ignored) + /// Storage: Timestamp Now (r:1 w:0) + /// Proof: Timestamp Now (max_values: Some(1), max_size: Some(8), added: 503, mode: Ignored) + /// Storage: System Account (r:1 w:1) + /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: Ignored) + /// Storage: System EventTopics (r:3 w:3) + /// Proof Skipped: System EventTopics (max_values: None, max_size: None, mode: Ignored) + /// Storage: Contracts PristineCode (r:0 w:1) + /// Proof: Contracts PristineCode (max_values: None, max_size: Some(125988), added: 128463, mode: Ignored) + /// Storage: Contracts OwnerInfoOf (r:0 w:1) + /// Proof: Contracts OwnerInfoOf (max_values: None, max_size: Some(88), added: 2563, mode: Ignored) + /// The range of component `c` is `[0, 61717]`. /// The range of component `i` is `[0, 1048576]`. /// The range of component `s` is `[0, 1048576]`. fn instantiate_with_code(c: u32, i: u32, s: u32, ) -> Weight { - // Minimum execution time: 3_610_424 nanoseconds. - Weight::from_ref_time(583_426_386) - // Standard Error: 277 - .saturating_add(Weight::from_ref_time(90_224).saturating_mul(c.into())) + // Proof Size summary in bytes: + // Measured: `257` + // Estimated: `0` + // Minimum execution time: 3_586_223 nanoseconds. + Weight::from_ref_time(561_614_281) + // Standard Error: 274 + .saturating_add(Weight::from_ref_time(87_557).saturating_mul(c.into())) // Standard Error: 16 - .saturating_add(Weight::from_ref_time(1_325).saturating_mul(i.into())) + .saturating_add(Weight::from_ref_time(1_307).saturating_mul(i.into())) // Standard Error: 16 - .saturating_add(Weight::from_ref_time(1_727).saturating_mul(s.into())) - .saturating_add(RocksDbWeight::get().reads(8)) - .saturating_add(RocksDbWeight::get().writes(9)) - } - // Storage: Contracts CodeStorage (r:1 w:1) - // Storage: Contracts Nonce (r:1 w:1) - // Storage: Contracts ContractInfoOf (r:1 w:1) - // Storage: Timestamp Now (r:1 w:0) - // Storage: System Account (r:1 w:1) - // Storage: Contracts OwnerInfoOf (r:1 w:1) - // Storage: System EventTopics (r:2 w:2) + .saturating_add(Weight::from_ref_time(1_721).saturating_mul(s.into())) + .saturating_add(RocksDbWeight::get().reads(8_u64)) + .saturating_add(RocksDbWeight::get().writes(9_u64)) + } + /// Storage: Contracts CodeStorage (r:1 w:1) + /// Proof: Contracts CodeStorage (max_values: None, max_size: Some(126001), added: 128476, mode: Ignored) + /// Storage: Contracts Nonce (r:1 w:1) + /// Proof: Contracts Nonce (max_values: Some(1), max_size: Some(8), added: 503, mode: Ignored) + /// Storage: Contracts ContractInfoOf (r:1 w:1) + /// Proof: Contracts ContractInfoOf (max_values: None, max_size: Some(258), added: 2733, mode: Ignored) + /// Storage: Timestamp Now (r:1 w:0) + /// Proof: Timestamp Now (max_values: Some(1), max_size: Some(8), added: 503, mode: Ignored) + /// Storage: System Account (r:1 w:1) + /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: Ignored) + /// Storage: Contracts OwnerInfoOf (r:1 w:1) + /// Proof: Contracts OwnerInfoOf (max_values: None, max_size: Some(88), added: 2563, mode: Ignored) + /// Storage: System EventTopics (r:2 w:2) + /// Proof Skipped: System EventTopics (max_values: None, max_size: None, mode: Ignored) /// The range of component `i` is `[0, 1048576]`. /// The range of component `s` is `[0, 1048576]`. fn instantiate(i: u32, s: u32, ) -> Weight { - // Minimum execution time: 1_892_996 nanoseconds. - Weight::from_ref_time(202_958_196) + // Proof Size summary in bytes: + // Measured: `533` + // Estimated: `0` + // Minimum execution time: 1_885_437 nanoseconds. + Weight::from_ref_time(199_943_867) // Standard Error: 8 - .saturating_add(Weight::from_ref_time(1_627).saturating_mul(i.into())) + .saturating_add(Weight::from_ref_time(1_611).saturating_mul(i.into())) // Standard Error: 8 - .saturating_add(Weight::from_ref_time(1_755).saturating_mul(s.into())) - .saturating_add(RocksDbWeight::get().reads(8)) - .saturating_add(RocksDbWeight::get().writes(7)) - } - // Storage: Contracts ContractInfoOf (r:1 w:1) - // Storage: Contracts CodeStorage (r:1 w:0) - // Storage: Timestamp Now (r:1 w:0) - // Storage: System Account (r:1 w:1) - // Storage: System EventTopics (r:2 w:2) + .saturating_add(Weight::from_ref_time(1_737).saturating_mul(s.into())) + .saturating_add(RocksDbWeight::get().reads(8_u64)) + .saturating_add(RocksDbWeight::get().writes(7_u64)) + } + /// Storage: Contracts ContractInfoOf (r:1 w:1) + /// Proof: Contracts ContractInfoOf (max_values: None, max_size: Some(258), added: 2733, mode: Ignored) + /// Storage: Contracts CodeStorage (r:1 w:0) + /// Proof: Contracts CodeStorage (max_values: None, max_size: Some(126001), added: 128476, mode: Ignored) + /// Storage: Timestamp Now (r:1 w:0) + /// Proof: Timestamp Now (max_values: Some(1), max_size: Some(8), added: 503, mode: Ignored) + /// Storage: System Account (r:1 w:1) + /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: Ignored) + /// Storage: System EventTopics (r:2 w:2) + /// Proof Skipped: System EventTopics (max_values: None, max_size: None, mode: Ignored) fn call() -> Weight { - // Minimum execution time: 157_347 nanoseconds. - Weight::from_ref_time(159_084_000) - .saturating_add(RocksDbWeight::get().reads(6)) - .saturating_add(RocksDbWeight::get().writes(4)) - } - // Storage: Contracts CodeStorage (r:1 w:1) - // Storage: System EventTopics (r:1 w:1) - // Storage: Contracts PristineCode (r:0 w:1) - // Storage: Contracts OwnerInfoOf (r:0 w:1) - /// The range of component `c` is `[0, 64226]`. + // Proof Size summary in bytes: + // Measured: `823` + // Estimated: `0` + // Minimum execution time: 150_604 nanoseconds. + Weight::from_ref_time(151_777_000) + .saturating_add(RocksDbWeight::get().reads(6_u64)) + .saturating_add(RocksDbWeight::get().writes(4_u64)) + } + /// Storage: Contracts CodeStorage (r:1 w:1) + /// Proof: Contracts CodeStorage (max_values: None, max_size: Some(126001), added: 128476, mode: Ignored) + /// Storage: System EventTopics (r:1 w:1) + /// Proof Skipped: System EventTopics (max_values: None, max_size: None, mode: Ignored) + /// Storage: Contracts PristineCode (r:0 w:1) + /// Proof: Contracts PristineCode (max_values: None, max_size: Some(125988), added: 128463, mode: Ignored) + /// Storage: Contracts OwnerInfoOf (r:0 w:1) + /// Proof: Contracts OwnerInfoOf (max_values: None, max_size: Some(88), added: 2563, mode: Ignored) + /// The range of component `c` is `[0, 61717]`. fn upload_code(c: u32, ) -> Weight { - // Minimum execution time: 299_987 nanoseconds. - Weight::from_ref_time(305_274_879) - // Standard Error: 72 - .saturating_add(Weight::from_ref_time(91_916).saturating_mul(c.into())) - .saturating_add(RocksDbWeight::get().reads(2)) - .saturating_add(RocksDbWeight::get().writes(4)) - } - // Storage: Contracts OwnerInfoOf (r:1 w:1) - // Storage: System EventTopics (r:1 w:1) - // Storage: Contracts CodeStorage (r:0 w:1) - // Storage: Contracts PristineCode (r:0 w:1) + // Proof Size summary in bytes: + // Measured: `109` + // Estimated: `0` + // Minimum execution time: 295_505 nanoseconds. + Weight::from_ref_time(305_609_098) + // Standard Error: 58 + .saturating_add(Weight::from_ref_time(88_676).saturating_mul(c.into())) + .saturating_add(RocksDbWeight::get().reads(2_u64)) + .saturating_add(RocksDbWeight::get().writes(4_u64)) + } + /// Storage: Contracts OwnerInfoOf (r:1 w:1) + /// Proof: Contracts OwnerInfoOf (max_values: None, max_size: Some(88), added: 2563, mode: Ignored) + /// Storage: System EventTopics (r:1 w:1) + /// Proof Skipped: System EventTopics (max_values: None, max_size: None, mode: Ignored) + /// Storage: Contracts CodeStorage (r:0 w:1) + /// Proof: Contracts CodeStorage (max_values: None, max_size: Some(126001), added: 128476, mode: Ignored) + /// Storage: Contracts PristineCode (r:0 w:1) + /// Proof: Contracts PristineCode (max_values: None, max_size: Some(125988), added: 128463, mode: Ignored) fn remove_code() -> Weight { - // Minimum execution time: 40_795 nanoseconds. - Weight::from_ref_time(41_297_000) - .saturating_add(RocksDbWeight::get().reads(2)) - .saturating_add(RocksDbWeight::get().writes(4)) - } - // Storage: Contracts ContractInfoOf (r:1 w:1) - // Storage: Contracts OwnerInfoOf (r:2 w:2) - // Storage: System EventTopics (r:3 w:3) + // Proof Size summary in bytes: + // Measured: `287` + // Estimated: `0` + // Minimum execution time: 25_949 nanoseconds. + Weight::from_ref_time(26_316_000) + .saturating_add(RocksDbWeight::get().reads(2_u64)) + .saturating_add(RocksDbWeight::get().writes(4_u64)) + } + /// Storage: Contracts ContractInfoOf (r:1 w:1) + /// Proof: Contracts ContractInfoOf (max_values: None, max_size: Some(258), added: 2733, mode: Ignored) + /// Storage: Contracts OwnerInfoOf (r:2 w:2) + /// Proof: Contracts OwnerInfoOf (max_values: None, max_size: Some(88), added: 2563, mode: Ignored) + /// Storage: System EventTopics (r:3 w:3) + /// Proof Skipped: System EventTopics (max_values: None, max_size: None, mode: Ignored) fn set_code() -> Weight { - // Minimum execution time: 41_957 nanoseconds. - Weight::from_ref_time(42_536_000) - .saturating_add(RocksDbWeight::get().reads(6)) - .saturating_add(RocksDbWeight::get().writes(6)) - } - // Storage: System Account (r:1 w:0) - // Storage: Contracts ContractInfoOf (r:1 w:1) - // Storage: Contracts CodeStorage (r:1 w:0) - // Storage: Timestamp Now (r:1 w:0) - // Storage: System EventTopics (r:2 w:2) + // Proof Size summary in bytes: + // Measured: `634` + // Estimated: `0` + // Minimum execution time: 29_005 nanoseconds. + Weight::from_ref_time(29_370_000) + .saturating_add(RocksDbWeight::get().reads(6_u64)) + .saturating_add(RocksDbWeight::get().writes(6_u64)) + } + /// Storage: System Account (r:1 w:0) + /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: Ignored) + /// Storage: Contracts ContractInfoOf (r:1 w:1) + /// Proof: Contracts ContractInfoOf (max_values: None, max_size: Some(258), added: 2733, mode: Ignored) + /// Storage: Contracts CodeStorage (r:1 w:0) + /// Proof: Contracts CodeStorage (max_values: None, max_size: Some(126001), added: 128476, mode: Ignored) + /// Storage: Timestamp Now (r:1 w:0) + /// Proof: Timestamp Now (max_values: Some(1), max_size: Some(8), added: 503, mode: Ignored) + /// Storage: System EventTopics (r:2 w:2) + /// Proof Skipped: System EventTopics (max_values: None, max_size: None, mode: Ignored) /// The range of component `r` is `[0, 20]`. fn seal_caller(r: u32, ) -> Weight { - // Minimum execution time: 293_544 nanoseconds. - Weight::from_ref_time(298_981_222) - // Standard Error: 39_139 - .saturating_add(Weight::from_ref_time(17_278_436).saturating_mul(r.into())) - .saturating_add(RocksDbWeight::get().reads(6)) - .saturating_add(RocksDbWeight::get().writes(3)) - } - // Storage: System Account (r:1 w:0) - // Storage: Contracts ContractInfoOf (r:1 w:1) - // Storage: Contracts CodeStorage (r:1 w:0) - // Storage: Timestamp Now (r:1 w:0) - // Storage: System EventTopics (r:2 w:2) + // Proof Size summary in bytes: + // Measured: `845 + r * (480 ±0)` + // Estimated: `0` + // Minimum execution time: 281_880 nanoseconds. + Weight::from_ref_time(289_637_700) + // Standard Error: 22_662 + .saturating_add(Weight::from_ref_time(16_866_274).saturating_mul(r.into())) + .saturating_add(RocksDbWeight::get().reads(6_u64)) + .saturating_add(RocksDbWeight::get().writes(3_u64)) + } + /// Storage: System Account (r:1 w:0) + /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: Ignored) + /// Storage: Contracts ContractInfoOf (r:1601 w:1) + /// Proof: Contracts ContractInfoOf (max_values: None, max_size: Some(258), added: 2733, mode: Ignored) + /// Storage: Contracts CodeStorage (r:1 w:0) + /// Proof: Contracts CodeStorage (max_values: None, max_size: Some(126001), added: 128476, mode: Ignored) + /// Storage: Timestamp Now (r:1 w:0) + /// Proof: Timestamp Now (max_values: Some(1), max_size: Some(8), added: 503, mode: Ignored) + /// Storage: System EventTopics (r:2 w:2) + /// Proof Skipped: System EventTopics (max_values: None, max_size: None, mode: Ignored) /// The range of component `r` is `[0, 20]`. fn seal_is_contract(r: u32, ) -> Weight { - // Minimum execution time: 293_500 nanoseconds. - Weight::from_ref_time(237_825_284) - // Standard Error: 450_234 - .saturating_add(Weight::from_ref_time(198_995_806).saturating_mul(r.into())) - .saturating_add(RocksDbWeight::get().reads(6)) + // Proof Size summary in bytes: + // Measured: `882 + r * (19218 ±0)` + // Estimated: `0` + // Minimum execution time: 285_590 nanoseconds. + Weight::from_ref_time(231_277_523) + // Standard Error: 425_084 + .saturating_add(Weight::from_ref_time(192_985_377).saturating_mul(r.into())) + .saturating_add(RocksDbWeight::get().reads(6_u64)) .saturating_add(RocksDbWeight::get().reads((80_u64).saturating_mul(r.into()))) - .saturating_add(RocksDbWeight::get().writes(3)) - } - // Storage: System Account (r:1 w:0) - // Storage: Contracts ContractInfoOf (r:1 w:1) - // Storage: Contracts CodeStorage (r:1 w:0) - // Storage: Timestamp Now (r:1 w:0) - // Storage: System EventTopics (r:2 w:2) + .saturating_add(RocksDbWeight::get().writes(3_u64)) + } + /// Storage: System Account (r:1 w:0) + /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: Ignored) + /// Storage: Contracts ContractInfoOf (r:1601 w:1) + /// Proof: Contracts ContractInfoOf (max_values: None, max_size: Some(258), added: 2733, mode: Ignored) + /// Storage: Contracts CodeStorage (r:1 w:0) + /// Proof: Contracts CodeStorage (max_values: None, max_size: Some(126001), added: 128476, mode: Ignored) + /// Storage: Timestamp Now (r:1 w:0) + /// Proof: Timestamp Now (max_values: Some(1), max_size: Some(8), added: 503, mode: Ignored) + /// Storage: System EventTopics (r:2 w:2) + /// Proof Skipped: System EventTopics (max_values: None, max_size: None, mode: Ignored) /// The range of component `r` is `[0, 20]`. fn seal_code_hash(r: u32, ) -> Weight { - // Minimum execution time: 295_851 nanoseconds. - Weight::from_ref_time(258_113_447) - // Standard Error: 408_747 - .saturating_add(Weight::from_ref_time(237_490_216).saturating_mul(r.into())) - .saturating_add(RocksDbWeight::get().reads(6)) + // Proof Size summary in bytes: + // Measured: `889 + r * (19539 ±0)` + // Estimated: `0` + // Minimum execution time: 282_966 nanoseconds. + Weight::from_ref_time(236_127_328) + // Standard Error: 405_193 + .saturating_add(Weight::from_ref_time(235_541_377).saturating_mul(r.into())) + .saturating_add(RocksDbWeight::get().reads(6_u64)) .saturating_add(RocksDbWeight::get().reads((80_u64).saturating_mul(r.into()))) - .saturating_add(RocksDbWeight::get().writes(3)) - } - // Storage: System Account (r:1 w:0) - // Storage: Contracts ContractInfoOf (r:1 w:1) - // Storage: Contracts CodeStorage (r:1 w:0) - // Storage: Timestamp Now (r:1 w:0) - // Storage: System EventTopics (r:2 w:2) + .saturating_add(RocksDbWeight::get().writes(3_u64)) + } + /// Storage: System Account (r:1 w:0) + /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: Ignored) + /// Storage: Contracts ContractInfoOf (r:1 w:1) + /// Proof: Contracts ContractInfoOf (max_values: None, max_size: Some(258), added: 2733, mode: Ignored) + /// Storage: Contracts CodeStorage (r:1 w:0) + /// Proof: Contracts CodeStorage (max_values: None, max_size: Some(126001), added: 128476, mode: Ignored) + /// Storage: Timestamp Now (r:1 w:0) + /// Proof: Timestamp Now (max_values: Some(1), max_size: Some(8), added: 503, mode: Ignored) + /// Storage: System EventTopics (r:2 w:2) + /// Proof Skipped: System EventTopics (max_values: None, max_size: None, mode: Ignored) /// The range of component `r` is `[0, 20]`. fn seal_own_code_hash(r: u32, ) -> Weight { - // Minimum execution time: 294_614 nanoseconds. - Weight::from_ref_time(301_528_998) - // Standard Error: 40_613 - .saturating_add(Weight::from_ref_time(20_815_088).saturating_mul(r.into())) - .saturating_add(RocksDbWeight::get().reads(6)) - .saturating_add(RocksDbWeight::get().writes(3)) - } - // Storage: System Account (r:1 w:0) - // Storage: Contracts ContractInfoOf (r:1 w:1) - // Storage: Contracts CodeStorage (r:1 w:0) - // Storage: Timestamp Now (r:1 w:0) - // Storage: System EventTopics (r:2 w:2) + // Proof Size summary in bytes: + // Measured: `852 + r * (480 ±0)` + // Estimated: `0` + // Minimum execution time: 286_021 nanoseconds. + Weight::from_ref_time(290_200_599) + // Standard Error: 19_224 + .saturating_add(Weight::from_ref_time(20_692_099).saturating_mul(r.into())) + .saturating_add(RocksDbWeight::get().reads(6_u64)) + .saturating_add(RocksDbWeight::get().writes(3_u64)) + } + /// Storage: System Account (r:1 w:0) + /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: Ignored) + /// Storage: Contracts ContractInfoOf (r:1 w:1) + /// Proof: Contracts ContractInfoOf (max_values: None, max_size: Some(258), added: 2733, mode: Ignored) + /// Storage: Contracts CodeStorage (r:1 w:0) + /// Proof: Contracts CodeStorage (max_values: None, max_size: Some(126001), added: 128476, mode: Ignored) + /// Storage: Timestamp Now (r:1 w:0) + /// Proof: Timestamp Now (max_values: Some(1), max_size: Some(8), added: 503, mode: Ignored) + /// Storage: System EventTopics (r:2 w:2) + /// Proof Skipped: System EventTopics (max_values: None, max_size: None, mode: Ignored) /// The range of component `r` is `[0, 20]`. fn seal_caller_is_origin(r: u32, ) -> Weight { - // Minimum execution time: 293_088 nanoseconds. - Weight::from_ref_time(299_120_595) - // Standard Error: 23_277 - .saturating_add(Weight::from_ref_time(11_130_334).saturating_mul(r.into())) - .saturating_add(RocksDbWeight::get().reads(6)) - .saturating_add(RocksDbWeight::get().writes(3)) - } - // Storage: System Account (r:1 w:0) - // Storage: Contracts ContractInfoOf (r:1 w:1) - // Storage: Contracts CodeStorage (r:1 w:0) - // Storage: Timestamp Now (r:1 w:0) - // Storage: System EventTopics (r:2 w:2) + // Proof Size summary in bytes: + // Measured: `842 + r * (240 ±0)` + // Estimated: `0` + // Minimum execution time: 284_175 nanoseconds. + Weight::from_ref_time(286_665_694) + // Standard Error: 14_104 + .saturating_add(Weight::from_ref_time(11_196_944).saturating_mul(r.into())) + .saturating_add(RocksDbWeight::get().reads(6_u64)) + .saturating_add(RocksDbWeight::get().writes(3_u64)) + } + /// Storage: System Account (r:1 w:0) + /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: Ignored) + /// Storage: Contracts ContractInfoOf (r:1 w:1) + /// Proof: Contracts ContractInfoOf (max_values: None, max_size: Some(258), added: 2733, mode: Ignored) + /// Storage: Contracts CodeStorage (r:1 w:0) + /// Proof: Contracts CodeStorage (max_values: None, max_size: Some(126001), added: 128476, mode: Ignored) + /// Storage: Timestamp Now (r:1 w:0) + /// Proof: Timestamp Now (max_values: Some(1), max_size: Some(8), added: 503, mode: Ignored) + /// Storage: System EventTopics (r:2 w:2) + /// Proof Skipped: System EventTopics (max_values: None, max_size: None, mode: Ignored) /// The range of component `r` is `[0, 20]`. fn seal_address(r: u32, ) -> Weight { - // Minimum execution time: 295_110 nanoseconds. - Weight::from_ref_time(305_295_787) - // Standard Error: 48_521 - .saturating_add(Weight::from_ref_time(16_610_806).saturating_mul(r.into())) - .saturating_add(RocksDbWeight::get().reads(6)) - .saturating_add(RocksDbWeight::get().writes(3)) - } - // Storage: System Account (r:1 w:0) - // Storage: Contracts ContractInfoOf (r:1 w:1) - // Storage: Contracts CodeStorage (r:1 w:0) - // Storage: Timestamp Now (r:1 w:0) - // Storage: System EventTopics (r:2 w:2) + // Proof Size summary in bytes: + // Measured: `846 + r * (480 ±0)` + // Estimated: `0` + // Minimum execution time: 285_315 nanoseconds. + Weight::from_ref_time(289_734_189) + // Standard Error: 15_980 + .saturating_add(Weight::from_ref_time(16_940_657).saturating_mul(r.into())) + .saturating_add(RocksDbWeight::get().reads(6_u64)) + .saturating_add(RocksDbWeight::get().writes(3_u64)) + } + /// Storage: System Account (r:1 w:0) + /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: Ignored) + /// Storage: Contracts ContractInfoOf (r:1 w:1) + /// Proof: Contracts ContractInfoOf (max_values: None, max_size: Some(258), added: 2733, mode: Ignored) + /// Storage: Contracts CodeStorage (r:1 w:0) + /// Proof: Contracts CodeStorage (max_values: None, max_size: Some(126001), added: 128476, mode: Ignored) + /// Storage: Timestamp Now (r:1 w:0) + /// Proof: Timestamp Now (max_values: Some(1), max_size: Some(8), added: 503, mode: Ignored) + /// Storage: System EventTopics (r:2 w:2) + /// Proof Skipped: System EventTopics (max_values: None, max_size: None, mode: Ignored) /// The range of component `r` is `[0, 20]`. fn seal_gas_left(r: u32, ) -> Weight { - // Minimum execution time: 293_414 nanoseconds. - Weight::from_ref_time(299_960_283) - // Standard Error: 32_003 - .saturating_add(Weight::from_ref_time(16_652_433).saturating_mul(r.into())) - .saturating_add(RocksDbWeight::get().reads(6)) - .saturating_add(RocksDbWeight::get().writes(3)) - } - // Storage: System Account (r:1 w:0) - // Storage: Contracts ContractInfoOf (r:1 w:1) - // Storage: Contracts CodeStorage (r:1 w:0) - // Storage: Timestamp Now (r:1 w:0) - // Storage: System EventTopics (r:2 w:2) + // Proof Size summary in bytes: + // Measured: `847 + r * (480 ±0)` + // Estimated: `0` + // Minimum execution time: 285_708 nanoseconds. + Weight::from_ref_time(289_872_393) + // Standard Error: 16_551 + .saturating_add(Weight::from_ref_time(16_672_944).saturating_mul(r.into())) + .saturating_add(RocksDbWeight::get().reads(6_u64)) + .saturating_add(RocksDbWeight::get().writes(3_u64)) + } + /// Storage: System Account (r:2 w:0) + /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: Ignored) + /// Storage: Contracts ContractInfoOf (r:1 w:1) + /// Proof: Contracts ContractInfoOf (max_values: None, max_size: Some(258), added: 2733, mode: Ignored) + /// Storage: Contracts CodeStorage (r:1 w:0) + /// Proof: Contracts CodeStorage (max_values: None, max_size: Some(126001), added: 128476, mode: Ignored) + /// Storage: Timestamp Now (r:1 w:0) + /// Proof: Timestamp Now (max_values: Some(1), max_size: Some(8), added: 503, mode: Ignored) + /// Storage: System EventTopics (r:2 w:2) + /// Proof Skipped: System EventTopics (max_values: None, max_size: None, mode: Ignored) /// The range of component `r` is `[0, 20]`. fn seal_balance(r: u32, ) -> Weight { - // Minimum execution time: 292_429 nanoseconds. - Weight::from_ref_time(301_025_128) - // Standard Error: 92_155 - .saturating_add(Weight::from_ref_time(92_731_719).saturating_mul(r.into())) - .saturating_add(RocksDbWeight::get().reads(7)) - .saturating_add(RocksDbWeight::get().writes(3)) - } - // Storage: System Account (r:1 w:0) - // Storage: Contracts ContractInfoOf (r:1 w:1) - // Storage: Contracts CodeStorage (r:1 w:0) - // Storage: Timestamp Now (r:1 w:0) - // Storage: System EventTopics (r:2 w:2) + // Proof Size summary in bytes: + // Measured: `1017 + r * (480 ±0)` + // Estimated: `0` + // Minimum execution time: 285_698 nanoseconds. + Weight::from_ref_time(295_636_093) + // Standard Error: 97_582 + .saturating_add(Weight::from_ref_time(92_891_252).saturating_mul(r.into())) + .saturating_add(RocksDbWeight::get().reads(7_u64)) + .saturating_add(RocksDbWeight::get().writes(3_u64)) + } + /// Storage: System Account (r:1 w:0) + /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: Ignored) + /// Storage: Contracts ContractInfoOf (r:1 w:1) + /// Proof: Contracts ContractInfoOf (max_values: None, max_size: Some(258), added: 2733, mode: Ignored) + /// Storage: Contracts CodeStorage (r:1 w:0) + /// Proof: Contracts CodeStorage (max_values: None, max_size: Some(126001), added: 128476, mode: Ignored) + /// Storage: Timestamp Now (r:1 w:0) + /// Proof: Timestamp Now (max_values: Some(1), max_size: Some(8), added: 503, mode: Ignored) + /// Storage: System EventTopics (r:2 w:2) + /// Proof Skipped: System EventTopics (max_values: None, max_size: None, mode: Ignored) /// The range of component `r` is `[0, 20]`. fn seal_value_transferred(r: u32, ) -> Weight { - // Minimum execution time: 293_516 nanoseconds. - Weight::from_ref_time(300_071_428) - // Standard Error: 25_871 - .saturating_add(Weight::from_ref_time(16_599_369).saturating_mul(r.into())) - .saturating_add(RocksDbWeight::get().reads(6)) - .saturating_add(RocksDbWeight::get().writes(3)) - } - // Storage: System Account (r:1 w:0) - // Storage: Contracts ContractInfoOf (r:1 w:1) - // Storage: Contracts CodeStorage (r:1 w:0) - // Storage: Timestamp Now (r:1 w:0) - // Storage: System EventTopics (r:2 w:2) + // Proof Size summary in bytes: + // Measured: `856 + r * (480 ±0)` + // Estimated: `0` + // Minimum execution time: 282_057 nanoseconds. + Weight::from_ref_time(289_304_621) + // Standard Error: 17_818 + .saturating_add(Weight::from_ref_time(16_725_632).saturating_mul(r.into())) + .saturating_add(RocksDbWeight::get().reads(6_u64)) + .saturating_add(RocksDbWeight::get().writes(3_u64)) + } + /// Storage: System Account (r:1 w:0) + /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: Ignored) + /// Storage: Contracts ContractInfoOf (r:1 w:1) + /// Proof: Contracts ContractInfoOf (max_values: None, max_size: Some(258), added: 2733, mode: Ignored) + /// Storage: Contracts CodeStorage (r:1 w:0) + /// Proof: Contracts CodeStorage (max_values: None, max_size: Some(126001), added: 128476, mode: Ignored) + /// Storage: Timestamp Now (r:1 w:0) + /// Proof: Timestamp Now (max_values: Some(1), max_size: Some(8), added: 503, mode: Ignored) + /// Storage: System EventTopics (r:2 w:2) + /// Proof Skipped: System EventTopics (max_values: None, max_size: None, mode: Ignored) /// The range of component `r` is `[0, 20]`. fn seal_minimum_balance(r: u32, ) -> Weight { - // Minimum execution time: 293_666 nanoseconds. - Weight::from_ref_time(299_803_599) - // Standard Error: 24_508 - .saturating_add(Weight::from_ref_time(16_500_973).saturating_mul(r.into())) - .saturating_add(RocksDbWeight::get().reads(6)) - .saturating_add(RocksDbWeight::get().writes(3)) - } - // Storage: System Account (r:1 w:0) - // Storage: Contracts ContractInfoOf (r:1 w:1) - // Storage: Contracts CodeStorage (r:1 w:0) - // Storage: Timestamp Now (r:1 w:0) - // Storage: System EventTopics (r:2 w:2) + // Proof Size summary in bytes: + // Measured: `854 + r * (480 ±0)` + // Estimated: `0` + // Minimum execution time: 282_478 nanoseconds. + Weight::from_ref_time(289_682_366) + // Standard Error: 20_379 + .saturating_add(Weight::from_ref_time(16_517_079).saturating_mul(r.into())) + .saturating_add(RocksDbWeight::get().reads(6_u64)) + .saturating_add(RocksDbWeight::get().writes(3_u64)) + } + /// Storage: System Account (r:1 w:0) + /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: Ignored) + /// Storage: Contracts ContractInfoOf (r:1 w:1) + /// Proof: Contracts ContractInfoOf (max_values: None, max_size: Some(258), added: 2733, mode: Ignored) + /// Storage: Contracts CodeStorage (r:1 w:0) + /// Proof: Contracts CodeStorage (max_values: None, max_size: Some(126001), added: 128476, mode: Ignored) + /// Storage: Timestamp Now (r:1 w:0) + /// Proof: Timestamp Now (max_values: Some(1), max_size: Some(8), added: 503, mode: Ignored) + /// Storage: System EventTopics (r:2 w:2) + /// Proof Skipped: System EventTopics (max_values: None, max_size: None, mode: Ignored) /// The range of component `r` is `[0, 20]`. fn seal_block_number(r: u32, ) -> Weight { - // Minimum execution time: 289_193 nanoseconds. - Weight::from_ref_time(297_946_784) - // Standard Error: 43_719 - .saturating_add(Weight::from_ref_time(16_442_254).saturating_mul(r.into())) - .saturating_add(RocksDbWeight::get().reads(6)) - .saturating_add(RocksDbWeight::get().writes(3)) - } - // Storage: System Account (r:1 w:0) - // Storage: Contracts ContractInfoOf (r:1 w:1) - // Storage: Contracts CodeStorage (r:1 w:0) - // Storage: Timestamp Now (r:1 w:0) - // Storage: System EventTopics (r:2 w:2) + // Proof Size summary in bytes: + // Measured: `851 + r * (480 ±0)` + // Estimated: `0` + // Minimum execution time: 283_826 nanoseconds. + Weight::from_ref_time(289_935_300) + // Standard Error: 15_180 + .saturating_add(Weight::from_ref_time(16_268_515).saturating_mul(r.into())) + .saturating_add(RocksDbWeight::get().reads(6_u64)) + .saturating_add(RocksDbWeight::get().writes(3_u64)) + } + /// Storage: System Account (r:1 w:0) + /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: Ignored) + /// Storage: Contracts ContractInfoOf (r:1 w:1) + /// Proof: Contracts ContractInfoOf (max_values: None, max_size: Some(258), added: 2733, mode: Ignored) + /// Storage: Contracts CodeStorage (r:1 w:0) + /// Proof: Contracts CodeStorage (max_values: None, max_size: Some(126001), added: 128476, mode: Ignored) + /// Storage: Timestamp Now (r:1 w:0) + /// Proof: Timestamp Now (max_values: Some(1), max_size: Some(8), added: 503, mode: Ignored) + /// Storage: System EventTopics (r:2 w:2) + /// Proof Skipped: System EventTopics (max_values: None, max_size: None, mode: Ignored) /// The range of component `r` is `[0, 20]`. fn seal_now(r: u32, ) -> Weight { - // Minimum execution time: 288_897 nanoseconds. - Weight::from_ref_time(298_856_658) - // Standard Error: 31_003 - .saturating_add(Weight::from_ref_time(16_490_508).saturating_mul(r.into())) - .saturating_add(RocksDbWeight::get().reads(6)) - .saturating_add(RocksDbWeight::get().writes(3)) - } - // Storage: System Account (r:1 w:0) - // Storage: Contracts ContractInfoOf (r:1 w:1) - // Storage: Contracts CodeStorage (r:1 w:0) - // Storage: Timestamp Now (r:1 w:0) - // Storage: System EventTopics (r:2 w:2) - // Storage: TransactionPayment NextFeeMultiplier (r:1 w:0) + // Proof Size summary in bytes: + // Measured: `842 + r * (480 ±0)` + // Estimated: `0` + // Minimum execution time: 285_455 nanoseconds. + Weight::from_ref_time(289_682_526) + // Standard Error: 18_667 + .saturating_add(Weight::from_ref_time(16_502_025).saturating_mul(r.into())) + .saturating_add(RocksDbWeight::get().reads(6_u64)) + .saturating_add(RocksDbWeight::get().writes(3_u64)) + } + /// Storage: System Account (r:1 w:0) + /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: Ignored) + /// Storage: Contracts ContractInfoOf (r:1 w:1) + /// Proof: Contracts ContractInfoOf (max_values: None, max_size: Some(258), added: 2733, mode: Ignored) + /// Storage: Contracts CodeStorage (r:1 w:0) + /// Proof: Contracts CodeStorage (max_values: None, max_size: Some(126001), added: 128476, mode: Ignored) + /// Storage: Timestamp Now (r:1 w:0) + /// Proof: Timestamp Now (max_values: Some(1), max_size: Some(8), added: 503, mode: Ignored) + /// Storage: TransactionPayment NextFeeMultiplier (r:1 w:0) + /// Proof: TransactionPayment NextFeeMultiplier (max_values: Some(1), max_size: Some(16), added: 511, mode: Ignored) + /// Storage: System EventTopics (r:2 w:2) + /// Proof Skipped: System EventTopics (max_values: None, max_size: None, mode: Ignored) /// The range of component `r` is `[0, 20]`. fn seal_weight_to_fee(r: u32, ) -> Weight { - // Minimum execution time: 289_850 nanoseconds. - Weight::from_ref_time(302_898_449) - // Standard Error: 92_822 - .saturating_add(Weight::from_ref_time(86_376_874).saturating_mul(r.into())) - .saturating_add(RocksDbWeight::get().reads(7)) - .saturating_add(RocksDbWeight::get().writes(3)) - } - // Storage: System Account (r:1 w:0) - // Storage: Contracts ContractInfoOf (r:1 w:1) - // Storage: Contracts CodeStorage (r:1 w:0) - // Storage: Timestamp Now (r:1 w:0) - // Storage: System EventTopics (r:2 w:2) + // Proof Size summary in bytes: + // Measured: `919 + r * (800 ±0)` + // Estimated: `0` + // Minimum execution time: 286_106 nanoseconds. + Weight::from_ref_time(294_493_680) + // Standard Error: 76_469 + .saturating_add(Weight::from_ref_time(87_055_837).saturating_mul(r.into())) + .saturating_add(RocksDbWeight::get().reads(7_u64)) + .saturating_add(RocksDbWeight::get().writes(3_u64)) + } + /// Storage: System Account (r:1 w:0) + /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: Ignored) + /// Storage: Contracts ContractInfoOf (r:1 w:1) + /// Proof: Contracts ContractInfoOf (max_values: None, max_size: Some(258), added: 2733, mode: Ignored) + /// Storage: Contracts CodeStorage (r:1 w:0) + /// Proof: Contracts CodeStorage (max_values: None, max_size: Some(126001), added: 128476, mode: Ignored) + /// Storage: Timestamp Now (r:1 w:0) + /// Proof: Timestamp Now (max_values: Some(1), max_size: Some(8), added: 503, mode: Ignored) + /// Storage: System EventTopics (r:2 w:2) + /// Proof Skipped: System EventTopics (max_values: None, max_size: None, mode: Ignored) /// The range of component `r` is `[0, 20]`. fn seal_gas(r: u32, ) -> Weight { - // Minimum execution time: 145_105 nanoseconds. - Weight::from_ref_time(152_388_934) - // Standard Error: 29_988 - .saturating_add(Weight::from_ref_time(7_761_947).saturating_mul(r.into())) - .saturating_add(RocksDbWeight::get().reads(6)) - .saturating_add(RocksDbWeight::get().writes(3)) - } - // Storage: System Account (r:1 w:0) - // Storage: Contracts ContractInfoOf (r:1 w:1) - // Storage: Contracts CodeStorage (r:1 w:0) - // Storage: Timestamp Now (r:1 w:0) - // Storage: System EventTopics (r:2 w:2) + // Proof Size summary in bytes: + // Measured: `809 + r * (320 ±0)` + // Estimated: `0` + // Minimum execution time: 137_877 nanoseconds. + Weight::from_ref_time(141_863_027) + // Standard Error: 10_200 + .saturating_add(Weight::from_ref_time(7_925_232).saturating_mul(r.into())) + .saturating_add(RocksDbWeight::get().reads(6_u64)) + .saturating_add(RocksDbWeight::get().writes(3_u64)) + } + /// Storage: System Account (r:1 w:0) + /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: Ignored) + /// Storage: Contracts ContractInfoOf (r:1 w:1) + /// Proof: Contracts ContractInfoOf (max_values: None, max_size: Some(258), added: 2733, mode: Ignored) + /// Storage: Contracts CodeStorage (r:1 w:0) + /// Proof: Contracts CodeStorage (max_values: None, max_size: Some(126001), added: 128476, mode: Ignored) + /// Storage: Timestamp Now (r:1 w:0) + /// Proof: Timestamp Now (max_values: Some(1), max_size: Some(8), added: 503, mode: Ignored) + /// Storage: System EventTopics (r:2 w:2) + /// Proof Skipped: System EventTopics (max_values: None, max_size: None, mode: Ignored) /// The range of component `r` is `[0, 20]`. fn seal_input(r: u32, ) -> Weight { - // Minimum execution time: 289_319 nanoseconds. - Weight::from_ref_time(298_390_911) - // Standard Error: 27_329 - .saturating_add(Weight::from_ref_time(14_225_527).saturating_mul(r.into())) - .saturating_add(RocksDbWeight::get().reads(6)) - .saturating_add(RocksDbWeight::get().writes(3)) - } - // Storage: System Account (r:1 w:0) - // Storage: Contracts ContractInfoOf (r:1 w:1) - // Storage: Contracts CodeStorage (r:1 w:0) - // Storage: Timestamp Now (r:1 w:0) - // Storage: System EventTopics (r:2 w:2) + // Proof Size summary in bytes: + // Measured: `844 + r * (480 ±0)` + // Estimated: `0` + // Minimum execution time: 282_034 nanoseconds. + Weight::from_ref_time(289_388_799) + // Standard Error: 21_999 + .saturating_add(Weight::from_ref_time(15_039_420).saturating_mul(r.into())) + .saturating_add(RocksDbWeight::get().reads(6_u64)) + .saturating_add(RocksDbWeight::get().writes(3_u64)) + } + /// Storage: System Account (r:1 w:0) + /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: Ignored) + /// Storage: Contracts ContractInfoOf (r:1 w:1) + /// Proof: Contracts ContractInfoOf (max_values: None, max_size: Some(258), added: 2733, mode: Ignored) + /// Storage: Contracts CodeStorage (r:1 w:0) + /// Proof: Contracts CodeStorage (max_values: None, max_size: Some(126001), added: 128476, mode: Ignored) + /// Storage: Timestamp Now (r:1 w:0) + /// Proof: Timestamp Now (max_values: Some(1), max_size: Some(8), added: 503, mode: Ignored) + /// Storage: System EventTopics (r:2 w:2) + /// Proof Skipped: System EventTopics (max_values: None, max_size: None, mode: Ignored) /// The range of component `n` is `[0, 1024]`. fn seal_input_per_kb(n: u32, ) -> Weight { - // Minimum execution time: 305_538 nanoseconds. - Weight::from_ref_time(339_734_596) - // Standard Error: 4_636 - .saturating_add(Weight::from_ref_time(9_592_149).saturating_mul(n.into())) - .saturating_add(RocksDbWeight::get().reads(6)) - .saturating_add(RocksDbWeight::get().writes(3)) - } - // Storage: System Account (r:1 w:0) - // Storage: Contracts ContractInfoOf (r:1 w:1) - // Storage: Contracts CodeStorage (r:1 w:0) - // Storage: Timestamp Now (r:1 w:0) - // Storage: System EventTopics (r:2 w:2) + // Proof Size summary in bytes: + // Measured: `1324` + // Estimated: `0` + // Minimum execution time: 303_229 nanoseconds. + Weight::from_ref_time(321_863_704) + // Standard Error: 2_754 + .saturating_add(Weight::from_ref_time(9_545_103).saturating_mul(n.into())) + .saturating_add(RocksDbWeight::get().reads(6_u64)) + .saturating_add(RocksDbWeight::get().writes(3_u64)) + } + /// Storage: System Account (r:1 w:0) + /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: Ignored) + /// Storage: Contracts ContractInfoOf (r:1 w:1) + /// Proof: Contracts ContractInfoOf (max_values: None, max_size: Some(258), added: 2733, mode: Ignored) + /// Storage: Contracts CodeStorage (r:1 w:0) + /// Proof: Contracts CodeStorage (max_values: None, max_size: Some(126001), added: 128476, mode: Ignored) + /// Storage: Timestamp Now (r:1 w:0) + /// Proof: Timestamp Now (max_values: Some(1), max_size: Some(8), added: 503, mode: Ignored) + /// Storage: System EventTopics (r:2 w:2) + /// Proof Skipped: System EventTopics (max_values: None, max_size: None, mode: Ignored) /// The range of component `r` is `[0, 1]`. fn seal_return(r: u32, ) -> Weight { - // Minimum execution time: 287_189 nanoseconds. - Weight::from_ref_time(295_472_383) - // Standard Error: 314_142 - .saturating_add(Weight::from_ref_time(214_016).saturating_mul(r.into())) - .saturating_add(RocksDbWeight::get().reads(6)) - .saturating_add(RocksDbWeight::get().writes(3)) - } - // Storage: System Account (r:1 w:0) - // Storage: Contracts ContractInfoOf (r:1 w:1) - // Storage: Contracts CodeStorage (r:1 w:0) - // Storage: Timestamp Now (r:1 w:0) - // Storage: System EventTopics (r:2 w:2) + // Proof Size summary in bytes: + // Measured: `832 + r * (45 ±0)` + // Estimated: `0` + // Minimum execution time: 278_824 nanoseconds. + Weight::from_ref_time(285_019_861) + // Standard Error: 101_646 + .saturating_add(Weight::from_ref_time(1_757_938).saturating_mul(r.into())) + .saturating_add(RocksDbWeight::get().reads(6_u64)) + .saturating_add(RocksDbWeight::get().writes(3_u64)) + } + /// Storage: System Account (r:1 w:0) + /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: Ignored) + /// Storage: Contracts ContractInfoOf (r:1 w:1) + /// Proof: Contracts ContractInfoOf (max_values: None, max_size: Some(258), added: 2733, mode: Ignored) + /// Storage: Contracts CodeStorage (r:1 w:0) + /// Proof: Contracts CodeStorage (max_values: None, max_size: Some(126001), added: 128476, mode: Ignored) + /// Storage: Timestamp Now (r:1 w:0) + /// Proof: Timestamp Now (max_values: Some(1), max_size: Some(8), added: 503, mode: Ignored) + /// Storage: System EventTopics (r:2 w:2) + /// Proof Skipped: System EventTopics (max_values: None, max_size: None, mode: Ignored) /// The range of component `n` is `[0, 1024]`. fn seal_return_per_kb(n: u32, ) -> Weight { - // Minimum execution time: 289_380 nanoseconds. - Weight::from_ref_time(296_302_404) - // Standard Error: 889 - .saturating_add(Weight::from_ref_time(190_017).saturating_mul(n.into())) - .saturating_add(RocksDbWeight::get().reads(6)) - .saturating_add(RocksDbWeight::get().writes(3)) - } - // Storage: System Account (r:1 w:0) - // Storage: Contracts ContractInfoOf (r:1 w:1) - // Storage: Contracts CodeStorage (r:1 w:0) - // Storage: Timestamp Now (r:1 w:0) - // Storage: System EventTopics (r:2 w:2) - // Storage: Contracts DeletionQueue (r:1 w:1) - // Storage: Contracts OwnerInfoOf (r:1 w:1) + // Proof Size summary in bytes: + // Measured: `842` + // Estimated: `0` + // Minimum execution time: 286_316 nanoseconds. + Weight::from_ref_time(287_206_936) + // Standard Error: 589 + .saturating_add(Weight::from_ref_time(186_684).saturating_mul(n.into())) + .saturating_add(RocksDbWeight::get().reads(6_u64)) + .saturating_add(RocksDbWeight::get().writes(3_u64)) + } + /// Storage: System Account (r:3 w:3) + /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: Ignored) + /// Storage: Contracts ContractInfoOf (r:1 w:1) + /// Proof: Contracts ContractInfoOf (max_values: None, max_size: Some(258), added: 2733, mode: Ignored) + /// Storage: Contracts CodeStorage (r:1 w:0) + /// Proof: Contracts CodeStorage (max_values: None, max_size: Some(126001), added: 128476, mode: Ignored) + /// Storage: Timestamp Now (r:1 w:0) + /// Proof: Timestamp Now (max_values: Some(1), max_size: Some(8), added: 503, mode: Ignored) + /// Storage: Contracts DeletionQueue (r:1 w:1) + /// Proof: Contracts DeletionQueue (max_values: Some(1), max_size: Some(16642), added: 17137, mode: Ignored) + /// Storage: Contracts OwnerInfoOf (r:1 w:1) + /// Proof: Contracts OwnerInfoOf (max_values: None, max_size: Some(88), added: 2563, mode: Ignored) + /// Storage: System EventTopics (r:3 w:3) + /// Proof Skipped: System EventTopics (max_values: None, max_size: None, mode: Ignored) /// The range of component `r` is `[0, 1]`. fn seal_terminate(r: u32, ) -> Weight { - // Minimum execution time: 292_383 nanoseconds. - Weight::from_ref_time(297_557_691) - // Standard Error: 251_228 - .saturating_add(Weight::from_ref_time(56_688_008).saturating_mul(r.into())) - .saturating_add(RocksDbWeight::get().reads(6)) + // Proof Size summary in bytes: + // Measured: `874 + r * (280 ±0)` + // Estimated: `0` + // Minimum execution time: 282_428 nanoseconds. + Weight::from_ref_time(287_101_148) + // Standard Error: 145_605 + .saturating_add(Weight::from_ref_time(58_079_551).saturating_mul(r.into())) + .saturating_add(RocksDbWeight::get().reads(6_u64)) .saturating_add(RocksDbWeight::get().reads((5_u64).saturating_mul(r.into()))) - .saturating_add(RocksDbWeight::get().writes(3)) + .saturating_add(RocksDbWeight::get().writes(3_u64)) .saturating_add(RocksDbWeight::get().writes((6_u64).saturating_mul(r.into()))) } - // Storage: System Account (r:1 w:0) - // Storage: Contracts ContractInfoOf (r:1 w:1) - // Storage: Contracts CodeStorage (r:1 w:0) - // Storage: Timestamp Now (r:1 w:0) - // Storage: System EventTopics (r:2 w:2) - // Storage: RandomnessCollectiveFlip RandomMaterial (r:1 w:0) + /// Storage: System Account (r:1 w:0) + /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: Ignored) + /// Storage: Contracts ContractInfoOf (r:1 w:1) + /// Proof: Contracts ContractInfoOf (max_values: None, max_size: Some(258), added: 2733, mode: Ignored) + /// Storage: Contracts CodeStorage (r:1 w:0) + /// Proof: Contracts CodeStorage (max_values: None, max_size: Some(126001), added: 128476, mode: Ignored) + /// Storage: Timestamp Now (r:1 w:0) + /// Proof: Timestamp Now (max_values: Some(1), max_size: Some(8), added: 503, mode: Ignored) + /// Storage: RandomnessCollectiveFlip RandomMaterial (r:1 w:0) + /// Proof: RandomnessCollectiveFlip RandomMaterial (max_values: Some(1), max_size: Some(2594), added: 3089, mode: Ignored) + /// Storage: System EventTopics (r:2 w:2) + /// Proof Skipped: System EventTopics (max_values: None, max_size: None, mode: Ignored) /// The range of component `r` is `[0, 20]`. fn seal_random(r: u32, ) -> Weight { - // Minimum execution time: 293_019 nanoseconds. - Weight::from_ref_time(301_747_439) - // Standard Error: 92_043 - .saturating_add(Weight::from_ref_time(109_861_144).saturating_mul(r.into())) - .saturating_add(RocksDbWeight::get().reads(7)) - .saturating_add(RocksDbWeight::get().writes(3)) - } - // Storage: System Account (r:1 w:0) - // Storage: Contracts ContractInfoOf (r:1 w:1) - // Storage: Contracts CodeStorage (r:1 w:0) - // Storage: Timestamp Now (r:1 w:0) - // Storage: System EventTopics (r:2 w:2) + // Proof Size summary in bytes: + // Measured: `889 + r * (800 ±0)` + // Estimated: `0` + // Minimum execution time: 281_516 nanoseconds. + Weight::from_ref_time(292_759_183) + // Standard Error: 152_698 + .saturating_add(Weight::from_ref_time(112_729_555).saturating_mul(r.into())) + .saturating_add(RocksDbWeight::get().reads(7_u64)) + .saturating_add(RocksDbWeight::get().writes(3_u64)) + } + /// Storage: System Account (r:1 w:0) + /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: Ignored) + /// Storage: Contracts ContractInfoOf (r:1 w:1) + /// Proof: Contracts ContractInfoOf (max_values: None, max_size: Some(258), added: 2733, mode: Ignored) + /// Storage: Contracts CodeStorage (r:1 w:0) + /// Proof: Contracts CodeStorage (max_values: None, max_size: Some(126001), added: 128476, mode: Ignored) + /// Storage: Timestamp Now (r:1 w:0) + /// Proof: Timestamp Now (max_values: Some(1), max_size: Some(8), added: 503, mode: Ignored) + /// Storage: System EventTopics (r:2 w:2) + /// Proof Skipped: System EventTopics (max_values: None, max_size: None, mode: Ignored) /// The range of component `r` is `[0, 20]`. fn seal_deposit_event(r: u32, ) -> Weight { - // Minimum execution time: 287_586 nanoseconds. - Weight::from_ref_time(304_751_302) - // Standard Error: 127_698 - .saturating_add(Weight::from_ref_time(221_525_053).saturating_mul(r.into())) - .saturating_add(RocksDbWeight::get().reads(6)) - .saturating_add(RocksDbWeight::get().writes(3)) - } - // Storage: System Account (r:1 w:0) - // Storage: Contracts ContractInfoOf (r:1 w:1) - // Storage: Contracts CodeStorage (r:1 w:0) - // Storage: Timestamp Now (r:1 w:0) - // Storage: System EventTopics (r:2 w:2) + // Proof Size summary in bytes: + // Measured: `842 + r * (800 ±0)` + // Estimated: `0` + // Minimum execution time: 280_100 nanoseconds. + Weight::from_ref_time(299_862_082) + // Standard Error: 95_658 + .saturating_add(Weight::from_ref_time(234_211_246).saturating_mul(r.into())) + .saturating_add(RocksDbWeight::get().reads(6_u64)) + .saturating_add(RocksDbWeight::get().writes(3_u64)) + } + /// Storage: System Account (r:1 w:0) + /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: Ignored) + /// Storage: Contracts ContractInfoOf (r:1 w:1) + /// Proof: Contracts ContractInfoOf (max_values: None, max_size: Some(258), added: 2733, mode: Ignored) + /// Storage: Contracts CodeStorage (r:1 w:0) + /// Proof: Contracts CodeStorage (max_values: None, max_size: Some(126001), added: 128476, mode: Ignored) + /// Storage: Timestamp Now (r:1 w:0) + /// Proof: Timestamp Now (max_values: Some(1), max_size: Some(8), added: 503, mode: Ignored) + /// Storage: System EventTopics (r:322 w:322) + /// Proof Skipped: System EventTopics (max_values: None, max_size: None, mode: Ignored) /// The range of component `t` is `[0, 4]`. /// The range of component `n` is `[0, 16]`. fn seal_deposit_event_per_topic_and_kb(t: u32, n: u32, ) -> Weight { - // Minimum execution time: 1_182_204 nanoseconds. - Weight::from_ref_time(508_607_957) - // Standard Error: 503_533 - .saturating_add(Weight::from_ref_time(173_998_655).saturating_mul(t.into())) - // Standard Error: 138_294 - .saturating_add(Weight::from_ref_time(67_991_373).saturating_mul(n.into())) - .saturating_add(RocksDbWeight::get().reads(6)) + // Proof Size summary in bytes: + // Measured: `1778 + t * (2608 ±0) + n * (8 ±0)` + // Estimated: `0` + // Minimum execution time: 1_197_721 nanoseconds. + Weight::from_ref_time(508_692_255) + // Standard Error: 538_596 + .saturating_add(Weight::from_ref_time(174_792_656).saturating_mul(t.into())) + // Standard Error: 147_924 + .saturating_add(Weight::from_ref_time(67_443_118).saturating_mul(n.into())) + .saturating_add(RocksDbWeight::get().reads(6_u64)) .saturating_add(RocksDbWeight::get().reads((80_u64).saturating_mul(t.into()))) - .saturating_add(RocksDbWeight::get().writes(3)) + .saturating_add(RocksDbWeight::get().writes(3_u64)) .saturating_add(RocksDbWeight::get().writes((80_u64).saturating_mul(t.into()))) } - // Storage: System Account (r:1 w:0) - // Storage: Contracts ContractInfoOf (r:1 w:1) - // Storage: Contracts CodeStorage (r:1 w:0) - // Storage: Timestamp Now (r:1 w:0) - // Storage: System EventTopics (r:2 w:2) + /// Storage: System Account (r:1 w:0) + /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: Ignored) + /// Storage: Contracts ContractInfoOf (r:1 w:1) + /// Proof: Contracts ContractInfoOf (max_values: None, max_size: Some(258), added: 2733, mode: Ignored) + /// Storage: Contracts CodeStorage (r:1 w:0) + /// Proof: Contracts CodeStorage (max_values: None, max_size: Some(126001), added: 128476, mode: Ignored) + /// Storage: Timestamp Now (r:1 w:0) + /// Proof: Timestamp Now (max_values: Some(1), max_size: Some(8), added: 503, mode: Ignored) + /// Storage: System EventTopics (r:2 w:2) + /// Proof Skipped: System EventTopics (max_values: None, max_size: None, mode: Ignored) /// The range of component `r` is `[0, 20]`. fn seal_debug_message(r: u32, ) -> Weight { - // Minimum execution time: 157_706 nanoseconds. - Weight::from_ref_time(161_895_583) - // Standard Error: 14_952 - .saturating_add(Weight::from_ref_time(12_990_237).saturating_mul(r.into())) - .saturating_add(RocksDbWeight::get().reads(6)) - .saturating_add(RocksDbWeight::get().writes(3)) - } - // Storage: Skipped Metadata (r:0 w:0) + // Proof Size summary in bytes: + // Measured: `841 + r * (800 ±0)` + // Estimated: `0` + // Minimum execution time: 149_687 nanoseconds. + Weight::from_ref_time(153_589_818) + // Standard Error: 13_361 + .saturating_add(Weight::from_ref_time(13_379_131).saturating_mul(r.into())) + .saturating_add(RocksDbWeight::get().reads(6_u64)) + .saturating_add(RocksDbWeight::get().writes(3_u64)) + } + /// Storage: Skipped Metadata (r:0 w:0) + /// Proof Skipped: Skipped Metadata (max_values: None, max_size: None, mode: Ignored) /// The range of component `r` is `[0, 10]`. fn seal_set_storage(r: u32, ) -> Weight { - // Minimum execution time: 290_063 nanoseconds. - Weight::from_ref_time(256_404_853) - // Standard Error: 436_195 - .saturating_add(Weight::from_ref_time(406_569_743).saturating_mul(r.into())) - .saturating_add(RocksDbWeight::get().reads(6)) + // Proof Size summary in bytes: + // Measured: `883 + r * (23417 ±0)` + // Estimated: `0` + // Minimum execution time: 281_920 nanoseconds. + Weight::from_ref_time(242_057_723) + // Standard Error: 464_911 + .saturating_add(Weight::from_ref_time(404_673_309).saturating_mul(r.into())) + .saturating_add(RocksDbWeight::get().reads(6_u64)) .saturating_add(RocksDbWeight::get().reads((80_u64).saturating_mul(r.into()))) - .saturating_add(RocksDbWeight::get().writes(3)) + .saturating_add(RocksDbWeight::get().writes(3_u64)) .saturating_add(RocksDbWeight::get().writes((80_u64).saturating_mul(r.into()))) } - // Storage: Skipped Metadata (r:0 w:0) + /// Storage: Skipped Metadata (r:0 w:0) + /// Proof Skipped: Skipped Metadata (max_values: None, max_size: None, mode: Ignored) /// The range of component `n` is `[0, 8]`. fn seal_set_storage_per_new_kb(n: u32, ) -> Weight { - // Minimum execution time: 426_295 nanoseconds. - Weight::from_ref_time(582_989_911) - // Standard Error: 1_404_141 - .saturating_add(Weight::from_ref_time(89_545_963).saturating_mul(n.into())) - .saturating_add(RocksDbWeight::get().reads(52)) + // Proof Size summary in bytes: + // Measured: `12583 + n * (11969 ±0)` + // Estimated: `0` + // Minimum execution time: 423_923 nanoseconds. + Weight::from_ref_time(573_806_626) + // Standard Error: 1_371_107 + .saturating_add(Weight::from_ref_time(85_963_445).saturating_mul(n.into())) + .saturating_add(RocksDbWeight::get().reads(52_u64)) .saturating_add(RocksDbWeight::get().reads((7_u64).saturating_mul(n.into()))) - .saturating_add(RocksDbWeight::get().writes(50)) + .saturating_add(RocksDbWeight::get().writes(50_u64)) .saturating_add(RocksDbWeight::get().writes((7_u64).saturating_mul(n.into()))) } - // Storage: Skipped Metadata (r:0 w:0) + /// Storage: Skipped Metadata (r:0 w:0) + /// Proof Skipped: Skipped Metadata (max_values: None, max_size: None, mode: Ignored) /// The range of component `n` is `[0, 8]`. fn seal_set_storage_per_old_kb(n: u32, ) -> Weight { - // Minimum execution time: 425_062 nanoseconds. - Weight::from_ref_time(552_380_900) - // Standard Error: 1_140_169 - .saturating_add(Weight::from_ref_time(64_085_108).saturating_mul(n.into())) - .saturating_add(RocksDbWeight::get().reads(51)) + // Proof Size summary in bytes: + // Measured: `15138 + n * (175775 ±0)` + // Estimated: `0` + // Minimum execution time: 424_048 nanoseconds. + Weight::from_ref_time(542_298_050) + // Standard Error: 1_092_010 + .saturating_add(Weight::from_ref_time(60_111_206).saturating_mul(n.into())) + .saturating_add(RocksDbWeight::get().reads(51_u64)) .saturating_add(RocksDbWeight::get().reads((7_u64).saturating_mul(n.into()))) - .saturating_add(RocksDbWeight::get().writes(49)) + .saturating_add(RocksDbWeight::get().writes(49_u64)) .saturating_add(RocksDbWeight::get().writes((7_u64).saturating_mul(n.into()))) } - // Storage: Skipped Metadata (r:0 w:0) + /// Storage: Skipped Metadata (r:0 w:0) + /// Proof Skipped: Skipped Metadata (max_values: None, max_size: None, mode: Ignored) /// The range of component `r` is `[0, 10]`. fn seal_clear_storage(r: u32, ) -> Weight { - // Minimum execution time: 294_421 nanoseconds. - Weight::from_ref_time(260_897_072) - // Standard Error: 474_135 - .saturating_add(Weight::from_ref_time(403_191_862).saturating_mul(r.into())) - .saturating_add(RocksDbWeight::get().reads(6)) + // Proof Size summary in bytes: + // Measured: `876 + r * (23098 ±0)` + // Estimated: `0` + // Minimum execution time: 285_714 nanoseconds. + Weight::from_ref_time(245_068_941) + // Standard Error: 417_796 + .saturating_add(Weight::from_ref_time(394_288_572).saturating_mul(r.into())) + .saturating_add(RocksDbWeight::get().reads(6_u64)) .saturating_add(RocksDbWeight::get().reads((80_u64).saturating_mul(r.into()))) - .saturating_add(RocksDbWeight::get().writes(3)) + .saturating_add(RocksDbWeight::get().writes(3_u64)) .saturating_add(RocksDbWeight::get().writes((80_u64).saturating_mul(r.into()))) } - // Storage: Skipped Metadata (r:0 w:0) + /// Storage: Skipped Metadata (r:0 w:0) + /// Proof Skipped: Skipped Metadata (max_values: None, max_size: None, mode: Ignored) /// The range of component `n` is `[0, 8]`. fn seal_clear_storage_per_kb(n: u32, ) -> Weight { - // Minimum execution time: 391_743 nanoseconds. - Weight::from_ref_time(535_327_545) - // Standard Error: 1_297_157 - .saturating_add(Weight::from_ref_time(67_222_981).saturating_mul(n.into())) - .saturating_add(RocksDbWeight::get().reads(51)) + // Proof Size summary in bytes: + // Measured: `14863 + n * (175768 ±0)` + // Estimated: `0` + // Minimum execution time: 385_278 nanoseconds. + Weight::from_ref_time(522_656_525) + // Standard Error: 1_259_587 + .saturating_add(Weight::from_ref_time(62_799_142).saturating_mul(n.into())) + .saturating_add(RocksDbWeight::get().reads(51_u64)) .saturating_add(RocksDbWeight::get().reads((7_u64).saturating_mul(n.into()))) - .saturating_add(RocksDbWeight::get().writes(48)) + .saturating_add(RocksDbWeight::get().writes(48_u64)) .saturating_add(RocksDbWeight::get().writes((7_u64).saturating_mul(n.into()))) } - // Storage: Skipped Metadata (r:0 w:0) + /// Storage: Skipped Metadata (r:0 w:0) + /// Proof Skipped: Skipped Metadata (max_values: None, max_size: None, mode: Ignored) /// The range of component `r` is `[0, 10]`. fn seal_get_storage(r: u32, ) -> Weight { - // Minimum execution time: 294_947 nanoseconds. - Weight::from_ref_time(266_280_887) - // Standard Error: 382_477 - .saturating_add(Weight::from_ref_time(323_306_898).saturating_mul(r.into())) - .saturating_add(RocksDbWeight::get().reads(6)) + // Proof Size summary in bytes: + // Measured: `878 + r * (23740 ±0)` + // Estimated: `0` + // Minimum execution time: 282_513 nanoseconds. + Weight::from_ref_time(256_242_753) + // Standard Error: 362_571 + .saturating_add(Weight::from_ref_time(317_951_687).saturating_mul(r.into())) + .saturating_add(RocksDbWeight::get().reads(6_u64)) .saturating_add(RocksDbWeight::get().reads((80_u64).saturating_mul(r.into()))) - .saturating_add(RocksDbWeight::get().writes(3)) + .saturating_add(RocksDbWeight::get().writes(3_u64)) } - // Storage: Skipped Metadata (r:0 w:0) + /// Storage: Skipped Metadata (r:0 w:0) + /// Proof Skipped: Skipped Metadata (max_values: None, max_size: None, mode: Ignored) /// The range of component `n` is `[0, 8]`. fn seal_get_storage_per_kb(n: u32, ) -> Weight { - // Minimum execution time: 375_487 nanoseconds. - Weight::from_ref_time(497_019_365) - // Standard Error: 1_141_672 - .saturating_add(Weight::from_ref_time(152_354_482).saturating_mul(n.into())) - .saturating_add(RocksDbWeight::get().reads(51)) + // Proof Size summary in bytes: + // Measured: `15469 + n * (175775 ±0)` + // Estimated: `0` + // Minimum execution time: 370_576 nanoseconds. + Weight::from_ref_time(487_764_999) + // Standard Error: 1_073_165 + .saturating_add(Weight::from_ref_time(147_588_190).saturating_mul(n.into())) + .saturating_add(RocksDbWeight::get().reads(51_u64)) .saturating_add(RocksDbWeight::get().reads((7_u64).saturating_mul(n.into()))) - .saturating_add(RocksDbWeight::get().writes(3)) + .saturating_add(RocksDbWeight::get().writes(3_u64)) } - // Storage: Skipped Metadata (r:0 w:0) + /// Storage: Skipped Metadata (r:0 w:0) + /// Proof Skipped: Skipped Metadata (max_values: None, max_size: None, mode: Ignored) /// The range of component `r` is `[0, 10]`. fn seal_contains_storage(r: u32, ) -> Weight { - // Minimum execution time: 294_716 nanoseconds. - Weight::from_ref_time(266_187_186) - // Standard Error: 395_323 - .saturating_add(Weight::from_ref_time(309_907_221).saturating_mul(r.into())) - .saturating_add(RocksDbWeight::get().reads(6)) + // Proof Size summary in bytes: + // Measured: `871 + r * (23100 ±0)` + // Estimated: `0` + // Minimum execution time: 285_917 nanoseconds. + Weight::from_ref_time(259_066_807) + // Standard Error: 340_183 + .saturating_add(Weight::from_ref_time(306_291_698).saturating_mul(r.into())) + .saturating_add(RocksDbWeight::get().reads(6_u64)) .saturating_add(RocksDbWeight::get().reads((80_u64).saturating_mul(r.into()))) - .saturating_add(RocksDbWeight::get().writes(3)) + .saturating_add(RocksDbWeight::get().writes(3_u64)) } - // Storage: Skipped Metadata (r:0 w:0) + /// Storage: Skipped Metadata (r:0 w:0) + /// Proof Skipped: Skipped Metadata (max_values: None, max_size: None, mode: Ignored) /// The range of component `n` is `[0, 8]`. fn seal_contains_storage_per_kb(n: u32, ) -> Weight { - // Minimum execution time: 371_667 nanoseconds. - Weight::from_ref_time(478_075_482) - // Standard Error: 979_116 - .saturating_add(Weight::from_ref_time(62_075_707).saturating_mul(n.into())) - .saturating_add(RocksDbWeight::get().reads(51)) + // Proof Size summary in bytes: + // Measured: `14814 + n * (175782 ±0)` + // Estimated: `0` + // Minimum execution time: 366_225 nanoseconds. + Weight::from_ref_time(470_470_223) + // Standard Error: 953_976 + .saturating_add(Weight::from_ref_time(57_748_742).saturating_mul(n.into())) + .saturating_add(RocksDbWeight::get().reads(51_u64)) .saturating_add(RocksDbWeight::get().reads((7_u64).saturating_mul(n.into()))) - .saturating_add(RocksDbWeight::get().writes(3)) + .saturating_add(RocksDbWeight::get().writes(3_u64)) } - // Storage: Skipped Metadata (r:0 w:0) + /// Storage: Skipped Metadata (r:0 w:0) + /// Proof Skipped: Skipped Metadata (max_values: None, max_size: None, mode: Ignored) /// The range of component `r` is `[0, 10]`. fn seal_take_storage(r: u32, ) -> Weight { - // Minimum execution time: 292_147 nanoseconds. - Weight::from_ref_time(254_374_598) - // Standard Error: 442_367 - .saturating_add(Weight::from_ref_time(413_674_705).saturating_mul(r.into())) - .saturating_add(RocksDbWeight::get().reads(6)) + // Proof Size summary in bytes: + // Measured: `879 + r * (23740 ±0)` + // Estimated: `0` + // Minimum execution time: 286_867 nanoseconds. + Weight::from_ref_time(244_403_664) + // Standard Error: 435_431 + .saturating_add(Weight::from_ref_time(409_282_991).saturating_mul(r.into())) + .saturating_add(RocksDbWeight::get().reads(6_u64)) .saturating_add(RocksDbWeight::get().reads((80_u64).saturating_mul(r.into()))) - .saturating_add(RocksDbWeight::get().writes(3)) + .saturating_add(RocksDbWeight::get().writes(3_u64)) .saturating_add(RocksDbWeight::get().writes((80_u64).saturating_mul(r.into()))) } - // Storage: Skipped Metadata (r:0 w:0) + /// Storage: Skipped Metadata (r:0 w:0) + /// Proof Skipped: Skipped Metadata (max_values: None, max_size: None, mode: Ignored) /// The range of component `n` is `[0, 8]`. fn seal_take_storage_per_kb(n: u32, ) -> Weight { - // Minimum execution time: 398_554 nanoseconds. - Weight::from_ref_time(553_715_911) - // Standard Error: 1_415_272 - .saturating_add(Weight::from_ref_time(158_254_544).saturating_mul(n.into())) - .saturating_add(RocksDbWeight::get().reads(51)) + // Proof Size summary in bytes: + // Measured: `15470 + n * (175775 ±0)` + // Estimated: `0` + // Minimum execution time: 393_392 nanoseconds. + Weight::from_ref_time(540_938_487) + // Standard Error: 1_361_411 + .saturating_add(Weight::from_ref_time(153_456_560).saturating_mul(n.into())) + .saturating_add(RocksDbWeight::get().reads(51_u64)) .saturating_add(RocksDbWeight::get().reads((7_u64).saturating_mul(n.into()))) - .saturating_add(RocksDbWeight::get().writes(48)) + .saturating_add(RocksDbWeight::get().writes(48_u64)) .saturating_add(RocksDbWeight::get().writes((7_u64).saturating_mul(n.into()))) } - // Storage: System Account (r:1 w:0) - // Storage: Contracts ContractInfoOf (r:1 w:1) - // Storage: Contracts CodeStorage (r:1 w:0) - // Storage: Timestamp Now (r:1 w:0) - // Storage: System EventTopics (r:2 w:2) + /// Storage: System Account (r:1602 w:1601) + /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: Ignored) + /// Storage: Contracts ContractInfoOf (r:1 w:1) + /// Proof: Contracts ContractInfoOf (max_values: None, max_size: Some(258), added: 2733, mode: Ignored) + /// Storage: Contracts CodeStorage (r:1 w:0) + /// Proof: Contracts CodeStorage (max_values: None, max_size: Some(126001), added: 128476, mode: Ignored) + /// Storage: Timestamp Now (r:1 w:0) + /// Proof: Timestamp Now (max_values: Some(1), max_size: Some(8), added: 503, mode: Ignored) + /// Storage: System EventTopics (r:2 w:2) + /// Proof Skipped: System EventTopics (max_values: None, max_size: None, mode: Ignored) /// The range of component `r` is `[0, 20]`. fn seal_transfer(r: u32, ) -> Weight { - // Minimum execution time: 295_081 nanoseconds. - Weight::from_ref_time(253_461_581) - // Standard Error: 740_198 - .saturating_add(Weight::from_ref_time(1_369_153_623).saturating_mul(r.into())) - .saturating_add(RocksDbWeight::get().reads(7)) + // Proof Size summary in bytes: + // Measured: `1393 + r * (3602 ±0)` + // Estimated: `0` + // Minimum execution time: 286_766 nanoseconds. + Weight::from_ref_time(221_458_774) + // Standard Error: 714_182 + .saturating_add(Weight::from_ref_time(1_402_610_222).saturating_mul(r.into())) + .saturating_add(RocksDbWeight::get().reads(7_u64)) .saturating_add(RocksDbWeight::get().reads((80_u64).saturating_mul(r.into()))) - .saturating_add(RocksDbWeight::get().writes(4)) + .saturating_add(RocksDbWeight::get().writes(4_u64)) .saturating_add(RocksDbWeight::get().writes((80_u64).saturating_mul(r.into()))) } - // Storage: System Account (r:1 w:0) - // Storage: Contracts ContractInfoOf (r:1 w:1) - // Storage: Contracts CodeStorage (r:1 w:0) - // Storage: Timestamp Now (r:1 w:0) - // Storage: System EventTopics (r:2 w:2) + /// Storage: System Account (r:1 w:0) + /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: Ignored) + /// Storage: Contracts ContractInfoOf (r:1601 w:1601) + /// Proof: Contracts ContractInfoOf (max_values: None, max_size: Some(258), added: 2733, mode: Ignored) + /// Storage: Contracts CodeStorage (r:2 w:0) + /// Proof: Contracts CodeStorage (max_values: None, max_size: Some(126001), added: 128476, mode: Ignored) + /// Storage: Timestamp Now (r:1 w:0) + /// Proof: Timestamp Now (max_values: Some(1), max_size: Some(8), added: 503, mode: Ignored) + /// Storage: System EventTopics (r:1602 w:1602) + /// Proof Skipped: System EventTopics (max_values: None, max_size: None, mode: Ignored) /// The range of component `r` is `[0, 20]`. fn seal_call(r: u32, ) -> Weight { - // Minimum execution time: 296_089 nanoseconds. - Weight::from_ref_time(297_193_000) - // Standard Error: 6_571_547 - .saturating_add(Weight::from_ref_time(21_080_280_397).saturating_mul(r.into())) - .saturating_add(RocksDbWeight::get().reads(7)) + // Proof Size summary in bytes: + // Measured: `1551 + r * (20511 ±0)` + // Estimated: `0` + // Minimum execution time: 287_158 nanoseconds. + Weight::from_ref_time(288_377_000) + // Standard Error: 6_108_706 + .saturating_add(Weight::from_ref_time(21_691_098_517).saturating_mul(r.into())) + .saturating_add(RocksDbWeight::get().reads(7_u64)) .saturating_add(RocksDbWeight::get().reads((160_u64).saturating_mul(r.into()))) - .saturating_add(RocksDbWeight::get().writes(3)) + .saturating_add(RocksDbWeight::get().writes(3_u64)) .saturating_add(RocksDbWeight::get().writes((160_u64).saturating_mul(r.into()))) } - // Storage: System Account (r:1 w:0) - // Storage: Contracts ContractInfoOf (r:1 w:1) - // Storage: Contracts CodeStorage (r:1 w:0) - // Storage: Timestamp Now (r:1 w:0) - // Storage: System EventTopics (r:2 w:2) + /// Storage: System Account (r:1 w:0) + /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: Ignored) + /// Storage: Contracts ContractInfoOf (r:1 w:1) + /// Proof: Contracts ContractInfoOf (max_values: None, max_size: Some(258), added: 2733, mode: Ignored) + /// Storage: Contracts CodeStorage (r:1536 w:0) + /// Proof: Contracts CodeStorage (max_values: None, max_size: Some(126001), added: 128476, mode: Ignored) + /// Storage: Timestamp Now (r:1 w:0) + /// Proof: Timestamp Now (max_values: Some(1), max_size: Some(8), added: 503, mode: Ignored) + /// Storage: System EventTopics (r:1537 w:1537) + /// Proof Skipped: System EventTopics (max_values: None, max_size: None, mode: Ignored) /// The range of component `r` is `[0, 20]`. fn seal_delegate_call(r: u32, ) -> Weight { - // Minimum execution time: 296_828 nanoseconds. - Weight::from_ref_time(297_685_000) - // Standard Error: 6_864_880 - .saturating_add(Weight::from_ref_time(20_770_321_946).saturating_mul(r.into())) - .saturating_add(RocksDbWeight::get().reads(6)) + // Proof Size summary in bytes: + // Measured: `0 + r * (71670 ±0)` + // Estimated: `0` + // Minimum execution time: 287_380 nanoseconds. + Weight::from_ref_time(288_241_000) + // Standard Error: 7_007_658 + .saturating_add(Weight::from_ref_time(21_428_850_764).saturating_mul(r.into())) + .saturating_add(RocksDbWeight::get().reads(6_u64)) .saturating_add(RocksDbWeight::get().reads((150_u64).saturating_mul(r.into()))) - .saturating_add(RocksDbWeight::get().writes(3)) + .saturating_add(RocksDbWeight::get().writes(3_u64)) .saturating_add(RocksDbWeight::get().writes((75_u64).saturating_mul(r.into()))) } - // Storage: System Account (r:1 w:0) - // Storage: Contracts ContractInfoOf (r:81 w:81) - // Storage: Contracts CodeStorage (r:2 w:0) - // Storage: Timestamp Now (r:1 w:0) - // Storage: System EventTopics (r:82 w:82) + /// Storage: System Account (r:82 w:81) + /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: Ignored) + /// Storage: Contracts ContractInfoOf (r:81 w:81) + /// Proof: Contracts ContractInfoOf (max_values: None, max_size: Some(258), added: 2733, mode: Ignored) + /// Storage: Contracts CodeStorage (r:2 w:0) + /// Proof: Contracts CodeStorage (max_values: None, max_size: Some(126001), added: 128476, mode: Ignored) + /// Storage: Timestamp Now (r:1 w:0) + /// Proof: Timestamp Now (max_values: Some(1), max_size: Some(8), added: 503, mode: Ignored) + /// Storage: System EventTopics (r:82 w:82) + /// Proof Skipped: System EventTopics (max_values: None, max_size: None, mode: Ignored) /// The range of component `t` is `[0, 1]`. /// The range of component `c` is `[0, 1024]`. fn seal_call_per_transfer_clone_kb(t: u32, c: u32, ) -> Weight { - // Minimum execution time: 9_824_671 nanoseconds. - Weight::from_ref_time(8_713_861_450) - // Standard Error: 7_388_925 - .saturating_add(Weight::from_ref_time(1_327_819_806).saturating_mul(t.into())) - // Standard Error: 11_079 - .saturating_add(Weight::from_ref_time(9_789_514).saturating_mul(c.into())) - .saturating_add(RocksDbWeight::get().reads(167)) + // Proof Size summary in bytes: + // Measured: `21611 + t * (15369 ±0)` + // Estimated: `0` + // Minimum execution time: 10_644_986 nanoseconds. + Weight::from_ref_time(9_596_635_640) + // Standard Error: 6_393_384 + .saturating_add(Weight::from_ref_time(1_304_764_528).saturating_mul(t.into())) + // Standard Error: 9_586 + .saturating_add(Weight::from_ref_time(9_663_819).saturating_mul(c.into())) + .saturating_add(RocksDbWeight::get().reads(167_u64)) .saturating_add(RocksDbWeight::get().reads((81_u64).saturating_mul(t.into()))) - .saturating_add(RocksDbWeight::get().writes(163)) + .saturating_add(RocksDbWeight::get().writes(163_u64)) .saturating_add(RocksDbWeight::get().writes((81_u64).saturating_mul(t.into()))) } - // Storage: System Account (r:1 w:0) - // Storage: Contracts ContractInfoOf (r:1 w:1) - // Storage: Contracts CodeStorage (r:1 w:0) - // Storage: Timestamp Now (r:1 w:0) - // Storage: System EventTopics (r:2 w:2) - // Storage: Contracts Nonce (r:1 w:1) - // Storage: Contracts OwnerInfoOf (r:80 w:80) + /// Storage: System Account (r:1602 w:1602) + /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: Ignored) + /// Storage: Contracts ContractInfoOf (r:1601 w:1601) + /// Proof: Contracts ContractInfoOf (max_values: None, max_size: Some(258), added: 2733, mode: Ignored) + /// Storage: Contracts CodeStorage (r:1601 w:1600) + /// Proof: Contracts CodeStorage (max_values: None, max_size: Some(126001), added: 128476, mode: Ignored) + /// Storage: Timestamp Now (r:1 w:0) + /// Proof: Timestamp Now (max_values: Some(1), max_size: Some(8), added: 503, mode: Ignored) + /// Storage: Contracts Nonce (r:1 w:1) + /// Proof: Contracts Nonce (max_values: Some(1), max_size: Some(8), added: 503, mode: Ignored) + /// Storage: Contracts OwnerInfoOf (r:1600 w:1600) + /// Proof: Contracts OwnerInfoOf (max_values: None, max_size: Some(88), added: 2563, mode: Ignored) + /// Storage: System EventTopics (r:1602 w:1602) + /// Proof Skipped: System EventTopics (max_values: None, max_size: None, mode: Ignored) /// The range of component `r` is `[0, 20]`. fn seal_instantiate(r: u32, ) -> Weight { - // Minimum execution time: 293_374 nanoseconds. - Weight::from_ref_time(302_067_000) - // Standard Error: 20_137_097 - .saturating_add(Weight::from_ref_time(26_444_812_817).saturating_mul(r.into())) - .saturating_add(RocksDbWeight::get().reads(8)) + // Proof Size summary in bytes: + // Measured: `1613 + r * (25576 ±0)` + // Estimated: `0` + // Minimum execution time: 284_948 nanoseconds. + Weight::from_ref_time(289_276_000) + // Standard Error: 18_674_951 + .saturating_add(Weight::from_ref_time(27_090_355_673).saturating_mul(r.into())) + .saturating_add(RocksDbWeight::get().reads(8_u64)) .saturating_add(RocksDbWeight::get().reads((400_u64).saturating_mul(r.into()))) - .saturating_add(RocksDbWeight::get().writes(5)) + .saturating_add(RocksDbWeight::get().writes(5_u64)) .saturating_add(RocksDbWeight::get().writes((400_u64).saturating_mul(r.into()))) } - // Storage: System Account (r:81 w:81) - // Storage: Contracts ContractInfoOf (r:81 w:81) - // Storage: Contracts CodeStorage (r:2 w:1) - // Storage: Timestamp Now (r:1 w:0) - // Storage: Contracts Nonce (r:1 w:1) - // Storage: Contracts OwnerInfoOf (r:1 w:1) - // Storage: System EventTopics (r:82 w:82) + /// Storage: System Account (r:82 w:82) + /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: Ignored) + /// Storage: Contracts ContractInfoOf (r:81 w:81) + /// Proof: Contracts ContractInfoOf (max_values: None, max_size: Some(258), added: 2733, mode: Ignored) + /// Storage: Contracts CodeStorage (r:2 w:1) + /// Proof: Contracts CodeStorage (max_values: None, max_size: Some(126001), added: 128476, mode: Ignored) + /// Storage: Timestamp Now (r:1 w:0) + /// Proof: Timestamp Now (max_values: Some(1), max_size: Some(8), added: 503, mode: Ignored) + /// Storage: Contracts Nonce (r:1 w:1) + /// Proof: Contracts Nonce (max_values: Some(1), max_size: Some(8), added: 503, mode: Ignored) + /// Storage: Contracts OwnerInfoOf (r:1 w:1) + /// Proof: Contracts OwnerInfoOf (max_values: None, max_size: Some(88), added: 2563, mode: Ignored) + /// Storage: System EventTopics (r:82 w:82) + /// Proof Skipped: System EventTopics (max_values: None, max_size: None, mode: Ignored) /// The range of component `t` is `[0, 1]`. /// The range of component `i` is `[0, 960]`. /// The range of component `s` is `[0, 960]`. fn seal_instantiate_per_transfer_input_salt_kb(t: u32, i: u32, s: u32, ) -> Weight { - // Minimum execution time: 126_456_770 nanoseconds. - Weight::from_ref_time(9_223_715_752) - // Standard Error: 96_040_318 - .saturating_add(Weight::from_ref_time(19_550_519).saturating_mul(t.into())) - // Standard Error: 156_614 - .saturating_add(Weight::from_ref_time(122_958_298).saturating_mul(i.into())) - // Standard Error: 156_614 - .saturating_add(Weight::from_ref_time(123_316_079).saturating_mul(s.into())) - .saturating_add(RocksDbWeight::get().reads(249)) + // Proof Size summary in bytes: + // Measured: `5666 + t * (17 ±0)` + // Estimated: `0` + // Minimum execution time: 127_857_642 nanoseconds. + Weight::from_ref_time(11_399_054_049) + // Standard Error: 95_033_651 + .saturating_add(Weight::from_ref_time(434_246_236).saturating_mul(t.into())) + // Standard Error: 154_973 + .saturating_add(Weight::from_ref_time(121_130_672).saturating_mul(i.into())) + // Standard Error: 154_973 + .saturating_add(Weight::from_ref_time(121_554_853).saturating_mul(s.into())) + .saturating_add(RocksDbWeight::get().reads(249_u64)) .saturating_add(RocksDbWeight::get().reads((1_u64).saturating_mul(t.into()))) - .saturating_add(RocksDbWeight::get().writes(247)) + .saturating_add(RocksDbWeight::get().writes(247_u64)) .saturating_add(RocksDbWeight::get().writes((1_u64).saturating_mul(t.into()))) } - // Storage: System Account (r:1 w:0) - // Storage: Contracts ContractInfoOf (r:1 w:1) - // Storage: Contracts CodeStorage (r:1 w:0) - // Storage: Timestamp Now (r:1 w:0) - // Storage: System EventTopics (r:2 w:2) + /// Storage: System Account (r:1 w:0) + /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: Ignored) + /// Storage: Contracts ContractInfoOf (r:1 w:1) + /// Proof: Contracts ContractInfoOf (max_values: None, max_size: Some(258), added: 2733, mode: Ignored) + /// Storage: Contracts CodeStorage (r:1 w:0) + /// Proof: Contracts CodeStorage (max_values: None, max_size: Some(126001), added: 128476, mode: Ignored) + /// Storage: Timestamp Now (r:1 w:0) + /// Proof: Timestamp Now (max_values: Some(1), max_size: Some(8), added: 503, mode: Ignored) + /// Storage: System EventTopics (r:2 w:2) + /// Proof Skipped: System EventTopics (max_values: None, max_size: None, mode: Ignored) /// The range of component `r` is `[0, 1]`. fn seal_hash_sha2_256(r: u32, ) -> Weight { - // Minimum execution time: 289_275 nanoseconds. - Weight::from_ref_time(297_803_961) - // Standard Error: 303_405 - .saturating_add(Weight::from_ref_time(40_800_138).saturating_mul(r.into())) - .saturating_add(RocksDbWeight::get().reads(6)) - .saturating_add(RocksDbWeight::get().writes(3)) - } - // Storage: System Account (r:1 w:0) - // Storage: Contracts ContractInfoOf (r:1 w:1) - // Storage: Contracts CodeStorage (r:1 w:0) - // Storage: Timestamp Now (r:1 w:0) - // Storage: System EventTopics (r:2 w:2) + // Proof Size summary in bytes: + // Measured: `839 + r * (642 ±0)` + // Estimated: `0` + // Minimum execution time: 280_949 nanoseconds. + Weight::from_ref_time(286_538_475) + // Standard Error: 124_866 + .saturating_add(Weight::from_ref_time(42_531_824).saturating_mul(r.into())) + .saturating_add(RocksDbWeight::get().reads(6_u64)) + .saturating_add(RocksDbWeight::get().writes(3_u64)) + } + /// Storage: System Account (r:1 w:0) + /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: Ignored) + /// Storage: Contracts ContractInfoOf (r:1 w:1) + /// Proof: Contracts ContractInfoOf (max_values: None, max_size: Some(258), added: 2733, mode: Ignored) + /// Storage: Contracts CodeStorage (r:1 w:0) + /// Proof: Contracts CodeStorage (max_values: None, max_size: Some(126001), added: 128476, mode: Ignored) + /// Storage: Timestamp Now (r:1 w:0) + /// Proof: Timestamp Now (max_values: Some(1), max_size: Some(8), added: 503, mode: Ignored) + /// Storage: System EventTopics (r:2 w:2) + /// Proof Skipped: System EventTopics (max_values: None, max_size: None, mode: Ignored) /// The range of component `n` is `[0, 1024]`. fn seal_hash_sha2_256_per_kb(n: u32, ) -> Weight { - // Minimum execution time: 336_077 nanoseconds. - Weight::from_ref_time(336_846_000) - // Standard Error: 51_610 - .saturating_add(Weight::from_ref_time(322_208_787).saturating_mul(n.into())) - .saturating_add(RocksDbWeight::get().reads(6)) - .saturating_add(RocksDbWeight::get().writes(3)) - } - // Storage: System Account (r:1 w:0) - // Storage: Contracts ContractInfoOf (r:1 w:1) - // Storage: Contracts CodeStorage (r:1 w:0) - // Storage: Timestamp Now (r:1 w:0) - // Storage: System EventTopics (r:2 w:2) + // Proof Size summary in bytes: + // Measured: `1641` + // Estimated: `0` + // Minimum execution time: 328_414 nanoseconds. + Weight::from_ref_time(329_293_000) + // Standard Error: 50_816 + .saturating_add(Weight::from_ref_time(318_312_506).saturating_mul(n.into())) + .saturating_add(RocksDbWeight::get().reads(6_u64)) + .saturating_add(RocksDbWeight::get().writes(3_u64)) + } + /// Storage: System Account (r:1 w:0) + /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: Ignored) + /// Storage: Contracts ContractInfoOf (r:1 w:1) + /// Proof: Contracts ContractInfoOf (max_values: None, max_size: Some(258), added: 2733, mode: Ignored) + /// Storage: Contracts CodeStorage (r:1 w:0) + /// Proof: Contracts CodeStorage (max_values: None, max_size: Some(126001), added: 128476, mode: Ignored) + /// Storage: Timestamp Now (r:1 w:0) + /// Proof: Timestamp Now (max_values: Some(1), max_size: Some(8), added: 503, mode: Ignored) + /// Storage: System EventTopics (r:2 w:2) + /// Proof Skipped: System EventTopics (max_values: None, max_size: None, mode: Ignored) /// The range of component `r` is `[0, 1]`. fn seal_hash_keccak_256(r: u32, ) -> Weight { - // Minimum execution time: 290_189 nanoseconds. - Weight::from_ref_time(298_454_259) - // Standard Error: 252_946 - .saturating_add(Weight::from_ref_time(55_034_240).saturating_mul(r.into())) - .saturating_add(RocksDbWeight::get().reads(6)) - .saturating_add(RocksDbWeight::get().writes(3)) - } - // Storage: System Account (r:1 w:0) - // Storage: Contracts ContractInfoOf (r:1 w:1) - // Storage: Contracts CodeStorage (r:1 w:0) - // Storage: Timestamp Now (r:1 w:0) - // Storage: System EventTopics (r:2 w:2) + // Proof Size summary in bytes: + // Measured: `841 + r * (642 ±0)` + // Estimated: `0` + // Minimum execution time: 282_208 nanoseconds. + Weight::from_ref_time(286_848_187) + // Standard Error: 106_214 + .saturating_add(Weight::from_ref_time(56_342_512).saturating_mul(r.into())) + .saturating_add(RocksDbWeight::get().reads(6_u64)) + .saturating_add(RocksDbWeight::get().writes(3_u64)) + } + /// Storage: System Account (r:1 w:0) + /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: Ignored) + /// Storage: Contracts ContractInfoOf (r:1 w:1) + /// Proof: Contracts ContractInfoOf (max_values: None, max_size: Some(258), added: 2733, mode: Ignored) + /// Storage: Contracts CodeStorage (r:1 w:0) + /// Proof: Contracts CodeStorage (max_values: None, max_size: Some(126001), added: 128476, mode: Ignored) + /// Storage: Timestamp Now (r:1 w:0) + /// Proof: Timestamp Now (max_values: Some(1), max_size: Some(8), added: 503, mode: Ignored) + /// Storage: System EventTopics (r:2 w:2) + /// Proof Skipped: System EventTopics (max_values: None, max_size: None, mode: Ignored) /// The range of component `n` is `[0, 1024]`. fn seal_hash_keccak_256_per_kb(n: u32, ) -> Weight { - // Minimum execution time: 350_030 nanoseconds. - Weight::from_ref_time(351_356_000) - // Standard Error: 57_422 - .saturating_add(Weight::from_ref_time(256_912_844).saturating_mul(n.into())) - .saturating_add(RocksDbWeight::get().reads(6)) - .saturating_add(RocksDbWeight::get().writes(3)) - } - // Storage: System Account (r:1 w:0) - // Storage: Contracts ContractInfoOf (r:1 w:1) - // Storage: Contracts CodeStorage (r:1 w:0) - // Storage: Timestamp Now (r:1 w:0) - // Storage: System EventTopics (r:2 w:2) + // Proof Size summary in bytes: + // Measured: `1643` + // Estimated: `0` + // Minimum execution time: 342_478 nanoseconds. + Weight::from_ref_time(342_947_000) + // Standard Error: 60_809 + .saturating_add(Weight::from_ref_time(255_492_149).saturating_mul(n.into())) + .saturating_add(RocksDbWeight::get().reads(6_u64)) + .saturating_add(RocksDbWeight::get().writes(3_u64)) + } + /// Storage: System Account (r:1 w:0) + /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: Ignored) + /// Storage: Contracts ContractInfoOf (r:1 w:1) + /// Proof: Contracts ContractInfoOf (max_values: None, max_size: Some(258), added: 2733, mode: Ignored) + /// Storage: Contracts CodeStorage (r:1 w:0) + /// Proof: Contracts CodeStorage (max_values: None, max_size: Some(126001), added: 128476, mode: Ignored) + /// Storage: Timestamp Now (r:1 w:0) + /// Proof: Timestamp Now (max_values: Some(1), max_size: Some(8), added: 503, mode: Ignored) + /// Storage: System EventTopics (r:2 w:2) + /// Proof Skipped: System EventTopics (max_values: None, max_size: None, mode: Ignored) /// The range of component `r` is `[0, 1]`. fn seal_hash_blake2_256(r: u32, ) -> Weight { - // Minimum execution time: 288_480 nanoseconds. - Weight::from_ref_time(297_613_263) - // Standard Error: 456_736 - .saturating_add(Weight::from_ref_time(38_100_936).saturating_mul(r.into())) - .saturating_add(RocksDbWeight::get().reads(6)) - .saturating_add(RocksDbWeight::get().writes(3)) - } - // Storage: System Account (r:1 w:0) - // Storage: Contracts ContractInfoOf (r:1 w:1) - // Storage: Contracts CodeStorage (r:1 w:0) - // Storage: Timestamp Now (r:1 w:0) - // Storage: System EventTopics (r:2 w:2) + // Proof Size summary in bytes: + // Measured: `841 + r * (642 ±0)` + // Estimated: `0` + // Minimum execution time: 279_059 nanoseconds. + Weight::from_ref_time(285_413_659) + // Standard Error: 123_081 + .saturating_add(Weight::from_ref_time(33_154_840).saturating_mul(r.into())) + .saturating_add(RocksDbWeight::get().reads(6_u64)) + .saturating_add(RocksDbWeight::get().writes(3_u64)) + } + /// Storage: System Account (r:1 w:0) + /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: Ignored) + /// Storage: Contracts ContractInfoOf (r:1 w:1) + /// Proof: Contracts ContractInfoOf (max_values: None, max_size: Some(258), added: 2733, mode: Ignored) + /// Storage: Contracts CodeStorage (r:1 w:0) + /// Proof: Contracts CodeStorage (max_values: None, max_size: Some(126001), added: 128476, mode: Ignored) + /// Storage: Timestamp Now (r:1 w:0) + /// Proof: Timestamp Now (max_values: Some(1), max_size: Some(8), added: 503, mode: Ignored) + /// Storage: System EventTopics (r:2 w:2) + /// Proof Skipped: System EventTopics (max_values: None, max_size: None, mode: Ignored) /// The range of component `n` is `[0, 1024]`. fn seal_hash_blake2_256_per_kb(n: u32, ) -> Weight { - // Minimum execution time: 321_195 nanoseconds. - Weight::from_ref_time(325_561_000) - // Standard Error: 46_289 - .saturating_add(Weight::from_ref_time(99_536_902).saturating_mul(n.into())) - .saturating_add(RocksDbWeight::get().reads(6)) - .saturating_add(RocksDbWeight::get().writes(3)) - } - // Storage: System Account (r:1 w:0) - // Storage: Contracts ContractInfoOf (r:1 w:1) - // Storage: Contracts CodeStorage (r:1 w:0) - // Storage: Timestamp Now (r:1 w:0) - // Storage: System EventTopics (r:2 w:2) + // Proof Size summary in bytes: + // Measured: `1643` + // Estimated: `0` + // Minimum execution time: 317_518 nanoseconds. + Weight::from_ref_time(318_178_000) + // Standard Error: 60_074 + .saturating_add(Weight::from_ref_time(99_403_819).saturating_mul(n.into())) + .saturating_add(RocksDbWeight::get().reads(6_u64)) + .saturating_add(RocksDbWeight::get().writes(3_u64)) + } + /// Storage: System Account (r:1 w:0) + /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: Ignored) + /// Storage: Contracts ContractInfoOf (r:1 w:1) + /// Proof: Contracts ContractInfoOf (max_values: None, max_size: Some(258), added: 2733, mode: Ignored) + /// Storage: Contracts CodeStorage (r:1 w:0) + /// Proof: Contracts CodeStorage (max_values: None, max_size: Some(126001), added: 128476, mode: Ignored) + /// Storage: Timestamp Now (r:1 w:0) + /// Proof: Timestamp Now (max_values: Some(1), max_size: Some(8), added: 503, mode: Ignored) + /// Storage: System EventTopics (r:2 w:2) + /// Proof Skipped: System EventTopics (max_values: None, max_size: None, mode: Ignored) /// The range of component `r` is `[0, 1]`. fn seal_hash_blake2_128(r: u32, ) -> Weight { - // Minimum execution time: 287_659 nanoseconds. - Weight::from_ref_time(295_963_340) - // Standard Error: 433_889 - .saturating_add(Weight::from_ref_time(30_251_859).saturating_mul(r.into())) - .saturating_add(RocksDbWeight::get().reads(6)) - .saturating_add(RocksDbWeight::get().writes(3)) - } - // Storage: System Account (r:1 w:0) - // Storage: Contracts ContractInfoOf (r:1 w:1) - // Storage: Contracts CodeStorage (r:1 w:0) - // Storage: Timestamp Now (r:1 w:0) - // Storage: System EventTopics (r:2 w:2) + // Proof Size summary in bytes: + // Measured: `841 + r * (679 ±0)` + // Estimated: `0` + // Minimum execution time: 280_145 nanoseconds. + Weight::from_ref_time(285_483_032) + // Standard Error: 106_113 + .saturating_add(Weight::from_ref_time(33_475_067).saturating_mul(r.into())) + .saturating_add(RocksDbWeight::get().reads(6_u64)) + .saturating_add(RocksDbWeight::get().writes(3_u64)) + } + /// Storage: System Account (r:1 w:0) + /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: Ignored) + /// Storage: Contracts ContractInfoOf (r:1 w:1) + /// Proof: Contracts ContractInfoOf (max_values: None, max_size: Some(258), added: 2733, mode: Ignored) + /// Storage: Contracts CodeStorage (r:1 w:0) + /// Proof: Contracts CodeStorage (max_values: None, max_size: Some(126001), added: 128476, mode: Ignored) + /// Storage: Timestamp Now (r:1 w:0) + /// Proof: Timestamp Now (max_values: Some(1), max_size: Some(8), added: 503, mode: Ignored) + /// Storage: System EventTopics (r:2 w:2) + /// Proof Skipped: System EventTopics (max_values: None, max_size: None, mode: Ignored) /// The range of component `n` is `[0, 1024]`. fn seal_hash_blake2_128_per_kb(n: u32, ) -> Weight { - // Minimum execution time: 324_298 nanoseconds. - Weight::from_ref_time(325_738_000) - // Standard Error: 48_563 - .saturating_add(Weight::from_ref_time(99_479_838).saturating_mul(n.into())) - .saturating_add(RocksDbWeight::get().reads(6)) - .saturating_add(RocksDbWeight::get().writes(3)) - } - // Storage: System Account (r:1 w:0) - // Storage: Contracts ContractInfoOf (r:1 w:1) - // Storage: Contracts CodeStorage (r:1 w:0) - // Storage: Timestamp Now (r:1 w:0) - // Storage: System EventTopics (r:2 w:2) + // Proof Size summary in bytes: + // Measured: `1643` + // Estimated: `0` + // Minimum execution time: 318_141 nanoseconds. + Weight::from_ref_time(318_699_000) + // Standard Error: 55_136 + .saturating_add(Weight::from_ref_time(99_275_434).saturating_mul(n.into())) + .saturating_add(RocksDbWeight::get().reads(6_u64)) + .saturating_add(RocksDbWeight::get().writes(3_u64)) + } + /// Storage: System Account (r:1 w:0) + /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: Ignored) + /// Storage: Contracts ContractInfoOf (r:1 w:1) + /// Proof: Contracts ContractInfoOf (max_values: None, max_size: Some(258), added: 2733, mode: Ignored) + /// Storage: Contracts CodeStorage (r:1 w:0) + /// Proof: Contracts CodeStorage (max_values: None, max_size: Some(126001), added: 128476, mode: Ignored) + /// Storage: Timestamp Now (r:1 w:0) + /// Proof: Timestamp Now (max_values: Some(1), max_size: Some(8), added: 503, mode: Ignored) + /// Storage: System EventTopics (r:2 w:2) + /// Proof Skipped: System EventTopics (max_values: None, max_size: None, mode: Ignored) /// The range of component `r` is `[0, 1]`. fn seal_ecdsa_recover(r: u32, ) -> Weight { - // Minimum execution time: 294_257 nanoseconds. - Weight::from_ref_time(299_467_620) - // Standard Error: 589_749 - .saturating_add(Weight::from_ref_time(3_015_389_579).saturating_mul(r.into())) - .saturating_add(RocksDbWeight::get().reads(6)) - .saturating_add(RocksDbWeight::get().writes(3)) - } - // Storage: System Account (r:1 w:0) - // Storage: Contracts ContractInfoOf (r:1 w:1) - // Storage: Contracts CodeStorage (r:1 w:0) - // Storage: Timestamp Now (r:1 w:0) - // Storage: System EventTopics (r:2 w:2) + // Proof Size summary in bytes: + // Measured: `885 + r * (6083 ±0)` + // Estimated: `0` + // Minimum execution time: 282_474 nanoseconds. + Weight::from_ref_time(288_078_802) + // Standard Error: 302_968 + .saturating_add(Weight::from_ref_time(2_944_967_597).saturating_mul(r.into())) + .saturating_add(RocksDbWeight::get().reads(6_u64)) + .saturating_add(RocksDbWeight::get().writes(3_u64)) + } + /// Storage: System Account (r:1 w:0) + /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: Ignored) + /// Storage: Contracts ContractInfoOf (r:1 w:1) + /// Proof: Contracts ContractInfoOf (max_values: None, max_size: Some(258), added: 2733, mode: Ignored) + /// Storage: Contracts CodeStorage (r:1 w:0) + /// Proof: Contracts CodeStorage (max_values: None, max_size: Some(126001), added: 128476, mode: Ignored) + /// Storage: Timestamp Now (r:1 w:0) + /// Proof: Timestamp Now (max_values: Some(1), max_size: Some(8), added: 503, mode: Ignored) + /// Storage: System EventTopics (r:2 w:2) + /// Proof Skipped: System EventTopics (max_values: None, max_size: None, mode: Ignored) /// The range of component `r` is `[0, 1]`. fn seal_ecdsa_to_eth_address(r: u32, ) -> Weight { - // Minimum execution time: 289_480 nanoseconds. - Weight::from_ref_time(298_762_412) - // Standard Error: 397_004 - .saturating_add(Weight::from_ref_time(743_937_087).saturating_mul(r.into())) - .saturating_add(RocksDbWeight::get().reads(6)) - .saturating_add(RocksDbWeight::get().writes(3)) - } - // Storage: System Account (r:1 w:0) - // Storage: Contracts ContractInfoOf (r:1 w:1) - // Storage: Contracts CodeStorage (r:1 w:0) - // Storage: Timestamp Now (r:1 w:0) - // Storage: System EventTopics (r:2 w:2) - // Storage: Contracts OwnerInfoOf (r:16 w:16) + // Proof Size summary in bytes: + // Measured: `854 + r * (3362 ±0)` + // Estimated: `0` + // Minimum execution time: 281_514 nanoseconds. + Weight::from_ref_time(287_458_651) + // Standard Error: 146_715 + .saturating_add(Weight::from_ref_time(731_367_948).saturating_mul(r.into())) + .saturating_add(RocksDbWeight::get().reads(6_u64)) + .saturating_add(RocksDbWeight::get().writes(3_u64)) + } + /// Storage: System Account (r:1 w:0) + /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: Ignored) + /// Storage: Contracts ContractInfoOf (r:1 w:1) + /// Proof: Contracts ContractInfoOf (max_values: None, max_size: Some(258), added: 2733, mode: Ignored) + /// Storage: Contracts CodeStorage (r:1536 w:0) + /// Proof: Contracts CodeStorage (max_values: None, max_size: Some(126001), added: 128476, mode: Ignored) + /// Storage: Timestamp Now (r:1 w:0) + /// Proof: Timestamp Now (max_values: Some(1), max_size: Some(8), added: 503, mode: Ignored) + /// Storage: Contracts OwnerInfoOf (r:1536 w:1536) + /// Proof: Contracts OwnerInfoOf (max_values: None, max_size: Some(88), added: 2563, mode: Ignored) + /// Storage: System EventTopics (r:1538 w:1538) + /// Proof Skipped: System EventTopics (max_values: None, max_size: None, mode: Ignored) /// The range of component `r` is `[0, 20]`. fn seal_set_code_hash(r: u32, ) -> Weight { - // Minimum execution time: 293_494 nanoseconds. - Weight::from_ref_time(297_756_000) - // Standard Error: 2_731_227 - .saturating_add(Weight::from_ref_time(1_387_380_436).saturating_mul(r.into())) - .saturating_add(RocksDbWeight::get().reads(6)) + // Proof Size summary in bytes: + // Measured: `0 + r * (79300 ±0)` + // Estimated: `0` + // Minimum execution time: 282_591 nanoseconds. + Weight::from_ref_time(286_842_000) + // Standard Error: 2_645_254 + .saturating_add(Weight::from_ref_time(1_394_535_676).saturating_mul(r.into())) + .saturating_add(RocksDbWeight::get().reads(6_u64)) .saturating_add(RocksDbWeight::get().reads((225_u64).saturating_mul(r.into()))) - .saturating_add(RocksDbWeight::get().writes(3)) + .saturating_add(RocksDbWeight::get().writes(3_u64)) .saturating_add(RocksDbWeight::get().writes((150_u64).saturating_mul(r.into()))) } - // Storage: System Account (r:1 w:0) - // Storage: Contracts ContractInfoOf (r:1 w:1) - // Storage: Contracts CodeStorage (r:1 w:0) - // Storage: Timestamp Now (r:1 w:0) - // Storage: System EventTopics (r:2 w:2) + /// Storage: System Account (r:1 w:0) + /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: Ignored) + /// Storage: Contracts ContractInfoOf (r:1 w:1) + /// Proof: Contracts ContractInfoOf (max_values: None, max_size: Some(258), added: 2733, mode: Ignored) + /// Storage: Contracts CodeStorage (r:1 w:0) + /// Proof: Contracts CodeStorage (max_values: None, max_size: Some(126001), added: 128476, mode: Ignored) + /// Storage: Timestamp Now (r:1 w:0) + /// Proof: Timestamp Now (max_values: Some(1), max_size: Some(8), added: 503, mode: Ignored) + /// Storage: System EventTopics (r:2 w:2) + /// Proof Skipped: System EventTopics (max_values: None, max_size: None, mode: Ignored) /// The range of component `r` is `[0, 20]`. fn seal_reentrance_count(r: u32, ) -> Weight { - // Minimum execution time: 295_339 nanoseconds. - Weight::from_ref_time(301_577_907) - // Standard Error: 23_836 - .saturating_add(Weight::from_ref_time(10_876_508).saturating_mul(r.into())) - .saturating_add(RocksDbWeight::get().reads(6)) - .saturating_add(RocksDbWeight::get().writes(3)) - } - // Storage: System Account (r:1 w:0) - // Storage: Contracts ContractInfoOf (r:1 w:1) - // Storage: Contracts CodeStorage (r:1 w:0) - // Storage: Timestamp Now (r:1 w:0) - // Storage: System EventTopics (r:2 w:2) + // Proof Size summary in bytes: + // Measured: `837 + r * (240 ±0)` + // Estimated: `0` + // Minimum execution time: 286_631 nanoseconds. + Weight::from_ref_time(288_787_650) + // Standard Error: 29_802 + .saturating_add(Weight::from_ref_time(11_115_811).saturating_mul(r.into())) + .saturating_add(RocksDbWeight::get().reads(6_u64)) + .saturating_add(RocksDbWeight::get().writes(3_u64)) + } + /// Storage: System Account (r:1 w:0) + /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: Ignored) + /// Storage: Contracts ContractInfoOf (r:1 w:1) + /// Proof: Contracts ContractInfoOf (max_values: None, max_size: Some(258), added: 2733, mode: Ignored) + /// Storage: Contracts CodeStorage (r:1 w:0) + /// Proof: Contracts CodeStorage (max_values: None, max_size: Some(126001), added: 128476, mode: Ignored) + /// Storage: Timestamp Now (r:1 w:0) + /// Proof: Timestamp Now (max_values: Some(1), max_size: Some(8), added: 503, mode: Ignored) + /// Storage: System EventTopics (r:2 w:2) + /// Proof Skipped: System EventTopics (max_values: None, max_size: None, mode: Ignored) /// The range of component `r` is `[0, 20]`. fn seal_account_reentrance_count(r: u32, ) -> Weight { - // Minimum execution time: 297_096 nanoseconds. - Weight::from_ref_time(336_319_823) - // Standard Error: 128_941 - .saturating_add(Weight::from_ref_time(25_211_374).saturating_mul(r.into())) - .saturating_add(RocksDbWeight::get().reads(6)) - .saturating_add(RocksDbWeight::get().writes(3)) - } - // Storage: System Account (r:1 w:0) - // Storage: Contracts ContractInfoOf (r:1 w:1) - // Storage: Contracts CodeStorage (r:1 w:0) - // Storage: Timestamp Now (r:1 w:0) - // Storage: System EventTopics (r:2 w:2) - // Storage: Contracts Nonce (r:1 w:1) + // Proof Size summary in bytes: + // Measured: `2056 + r * (3153 ±0)` + // Estimated: `0` + // Minimum execution time: 287_775 nanoseconds. + Weight::from_ref_time(319_806_123) + // Standard Error: 111_808 + .saturating_add(Weight::from_ref_time(17_641_181).saturating_mul(r.into())) + .saturating_add(RocksDbWeight::get().reads(6_u64)) + .saturating_add(RocksDbWeight::get().writes(3_u64)) + } + /// Storage: System Account (r:1 w:0) + /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: Ignored) + /// Storage: Contracts ContractInfoOf (r:1 w:1) + /// Proof: Contracts ContractInfoOf (max_values: None, max_size: Some(258), added: 2733, mode: Ignored) + /// Storage: Contracts CodeStorage (r:1 w:0) + /// Proof: Contracts CodeStorage (max_values: None, max_size: Some(126001), added: 128476, mode: Ignored) + /// Storage: Timestamp Now (r:1 w:0) + /// Proof: Timestamp Now (max_values: Some(1), max_size: Some(8), added: 503, mode: Ignored) + /// Storage: Contracts Nonce (r:1 w:1) + /// Proof: Contracts Nonce (max_values: Some(1), max_size: Some(8), added: 503, mode: Ignored) + /// Storage: System EventTopics (r:2 w:2) + /// Proof Skipped: System EventTopics (max_values: None, max_size: None, mode: Ignored) /// The range of component `r` is `[0, 20]`. fn seal_instantiation_nonce(r: u32, ) -> Weight { - // Minimum execution time: 293_819 nanoseconds. - Weight::from_ref_time(302_765_659) - // Standard Error: 28_518 - .saturating_add(Weight::from_ref_time(9_325_552).saturating_mul(r.into())) - .saturating_add(RocksDbWeight::get().reads(7)) - .saturating_add(RocksDbWeight::get().writes(4)) + // Proof Size summary in bytes: + // Measured: `840 + r * (240 ±0)` + // Estimated: `0` + // Minimum execution time: 285_077 nanoseconds. + Weight::from_ref_time(289_980_475) + // Standard Error: 14_535 + .saturating_add(Weight::from_ref_time(9_295_346).saturating_mul(r.into())) + .saturating_add(RocksDbWeight::get().reads(7_u64)) + .saturating_add(RocksDbWeight::get().writes(4_u64)) } /// The range of component `r` is `[0, 50]`. fn instr_i64const(r: u32, ) -> Weight { - // Minimum execution time: 805 nanoseconds. - Weight::from_ref_time(1_002_066) - // Standard Error: 609 - .saturating_add(Weight::from_ref_time(346_608).saturating_mul(r.into())) + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 777 nanoseconds. + Weight::from_ref_time(1_014_498) + // Standard Error: 154 + .saturating_add(Weight::from_ref_time(405_551).saturating_mul(r.into())) } /// The range of component `r` is `[0, 50]`. fn instr_i64load(r: u32, ) -> Weight { - // Minimum execution time: 917 nanoseconds. - Weight::from_ref_time(1_247_710) - // Standard Error: 1_069 - .saturating_add(Weight::from_ref_time(984_608).saturating_mul(r.into())) + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 862 nanoseconds. + Weight::from_ref_time(1_345_826) + // Standard Error: 457 + .saturating_add(Weight::from_ref_time(1_033_909).saturating_mul(r.into())) } /// The range of component `r` is `[0, 50]`. fn instr_i64store(r: u32, ) -> Weight { - // Minimum execution time: 917 nanoseconds. - Weight::from_ref_time(1_244_315) - // Standard Error: 524 - .saturating_add(Weight::from_ref_time(882_167).saturating_mul(r.into())) + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 892 nanoseconds. + Weight::from_ref_time(1_233_601) + // Standard Error: 341 + .saturating_add(Weight::from_ref_time(885_275).saturating_mul(r.into())) } /// The range of component `r` is `[0, 50]`. fn instr_select(r: u32, ) -> Weight { - // Minimum execution time: 791 nanoseconds. - Weight::from_ref_time(1_119_053) - // Standard Error: 379 - .saturating_add(Weight::from_ref_time(956_169).saturating_mul(r.into())) + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 771 nanoseconds. + Weight::from_ref_time(1_099_906) + // Standard Error: 261 + .saturating_add(Weight::from_ref_time(1_092_031).saturating_mul(r.into())) } /// The range of component `r` is `[0, 50]`. fn instr_if(r: u32, ) -> Weight { - // Minimum execution time: 782 nanoseconds. - Weight::from_ref_time(773_373) - // Standard Error: 463 - .saturating_add(Weight::from_ref_time(1_299_306).saturating_mul(r.into())) + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 785 nanoseconds. + Weight::from_ref_time(929_328) + // Standard Error: 333 + .saturating_add(Weight::from_ref_time(1_374_749).saturating_mul(r.into())) } /// The range of component `r` is `[0, 50]`. fn instr_br(r: u32, ) -> Weight { - // Minimum execution time: 743 nanoseconds. - Weight::from_ref_time(1_273_896) - // Standard Error: 925 - .saturating_add(Weight::from_ref_time(527_015).saturating_mul(r.into())) + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 772 nanoseconds. + Weight::from_ref_time(979_702) + // Standard Error: 351 + .saturating_add(Weight::from_ref_time(621_385).saturating_mul(r.into())) } /// The range of component `r` is `[0, 50]`. fn instr_br_if(r: u32, ) -> Weight { - // Minimum execution time: 761 nanoseconds. - Weight::from_ref_time(929_565) - // Standard Error: 1_275 - .saturating_add(Weight::from_ref_time(805_592).saturating_mul(r.into())) + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 766 nanoseconds. + Weight::from_ref_time(1_303_783) + // Standard Error: 1_556 + .saturating_add(Weight::from_ref_time(841_842).saturating_mul(r.into())) } /// The range of component `r` is `[0, 50]`. fn instr_br_table(r: u32, ) -> Weight { - // Minimum execution time: 813 nanoseconds. - Weight::from_ref_time(703_802) - // Standard Error: 1_665 - .saturating_add(Weight::from_ref_time(1_074_321).saturating_mul(r.into())) + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 792 nanoseconds. + Weight::from_ref_time(1_093_901) + // Standard Error: 1_383 + .saturating_add(Weight::from_ref_time(1_145_435).saturating_mul(r.into())) } /// The range of component `e` is `[1, 256]`. fn instr_br_table_per_entry(e: u32, ) -> Weight { - // Minimum execution time: 2_347 nanoseconds. - Weight::from_ref_time(2_752_638) - // Standard Error: 69 - .saturating_add(Weight::from_ref_time(4_771).saturating_mul(e.into())) + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 2_526 nanoseconds. + Weight::from_ref_time(2_872_561) + // Standard Error: 60 + .saturating_add(Weight::from_ref_time(4_365).saturating_mul(e.into())) } /// The range of component `r` is `[0, 50]`. fn instr_call(r: u32, ) -> Weight { - // Minimum execution time: 826 nanoseconds. - Weight::from_ref_time(1_784_596) - // Standard Error: 1_646 - .saturating_add(Weight::from_ref_time(2_189_993).saturating_mul(r.into())) + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 834 nanoseconds. + Weight::from_ref_time(1_431_876) + // Standard Error: 1_448 + .saturating_add(Weight::from_ref_time(2_268_715).saturating_mul(r.into())) } /// The range of component `r` is `[0, 50]`. fn instr_call_indirect(r: u32, ) -> Weight { - // Minimum execution time: 926 nanoseconds. - Weight::from_ref_time(1_898_358) - // Standard Error: 2_678 - .saturating_add(Weight::from_ref_time(2_811_379).saturating_mul(r.into())) + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 920 nanoseconds. + Weight::from_ref_time(2_167_004) + // Standard Error: 2_060 + .saturating_add(Weight::from_ref_time(2_921_443).saturating_mul(r.into())) } /// The range of component `p` is `[0, 128]`. fn instr_call_indirect_per_param(p: u32, ) -> Weight { - // Minimum execution time: 4_428 nanoseconds. - Weight::from_ref_time(5_257_445) - // Standard Error: 306 - .saturating_add(Weight::from_ref_time(178_882).saturating_mul(p.into())) + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 4_624 nanoseconds. + Weight::from_ref_time(5_534_325) + // Standard Error: 326 + .saturating_add(Weight::from_ref_time(184_307).saturating_mul(p.into())) } /// The range of component `l` is `[0, 1024]`. fn instr_call_per_local(l: u32, ) -> Weight { - // Minimum execution time: 2_945 nanoseconds. - Weight::from_ref_time(4_240_619) - // Standard Error: 39 - .saturating_add(Weight::from_ref_time(46_640).saturating_mul(l.into())) + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 3_062 nanoseconds. + Weight::from_ref_time(4_432_879) + // Standard Error: 64 + .saturating_add(Weight::from_ref_time(46_196).saturating_mul(l.into())) } /// The range of component `r` is `[0, 50]`. fn instr_local_get(r: u32, ) -> Weight { - // Minimum execution time: 2_204 nanoseconds. - Weight::from_ref_time(2_430_272) - // Standard Error: 270 - .saturating_add(Weight::from_ref_time(365_718).saturating_mul(r.into())) + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 2_036 nanoseconds. + Weight::from_ref_time(2_318_877) + // Standard Error: 172 + .saturating_add(Weight::from_ref_time(500_498).saturating_mul(r.into())) } /// The range of component `r` is `[0, 50]`. fn instr_local_set(r: u32, ) -> Weight { - // Minimum execution time: 2_117 nanoseconds. - Weight::from_ref_time(2_429_354) - // Standard Error: 240 - .saturating_add(Weight::from_ref_time(381_830).saturating_mul(r.into())) + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 2_027 nanoseconds. + Weight::from_ref_time(2_355_900) + // Standard Error: 220 + .saturating_add(Weight::from_ref_time(461_393).saturating_mul(r.into())) } /// The range of component `r` is `[0, 50]`. fn instr_local_tee(r: u32, ) -> Weight { - // Minimum execution time: 2_123 nanoseconds. - Weight::from_ref_time(2_460_016) - // Standard Error: 262 - .saturating_add(Weight::from_ref_time(526_554).saturating_mul(r.into())) + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 2_038 nanoseconds. + Weight::from_ref_time(2_350_330) + // Standard Error: 224 + .saturating_add(Weight::from_ref_time(586_808).saturating_mul(r.into())) } /// The range of component `r` is `[0, 50]`. fn instr_global_get(r: u32, ) -> Weight { - // Minimum execution time: 892 nanoseconds. - Weight::from_ref_time(1_234_618) - // Standard Error: 485 - .saturating_add(Weight::from_ref_time(813_721).saturating_mul(r.into())) + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 897 nanoseconds. + Weight::from_ref_time(1_267_115) + // Standard Error: 189 + .saturating_add(Weight::from_ref_time(884_926).saturating_mul(r.into())) } /// The range of component `r` is `[0, 50]`. fn instr_global_set(r: u32, ) -> Weight { - // Minimum execution time: 901 nanoseconds. - Weight::from_ref_time(1_182_838) - // Standard Error: 482 - .saturating_add(Weight::from_ref_time(831_308).saturating_mul(r.into())) + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 892 nanoseconds. + Weight::from_ref_time(1_202_122) + // Standard Error: 286 + .saturating_add(Weight::from_ref_time(885_157).saturating_mul(r.into())) } /// The range of component `r` is `[0, 50]`. fn instr_memory_current(r: u32, ) -> Weight { - // Minimum execution time: 929 nanoseconds. - Weight::from_ref_time(1_221_388) - // Standard Error: 434 - .saturating_add(Weight::from_ref_time(693_301).saturating_mul(r.into())) + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 852 nanoseconds. + Weight::from_ref_time(1_132_479) + // Standard Error: 224 + .saturating_add(Weight::from_ref_time(719_603).saturating_mul(r.into())) } /// The range of component `r` is `[0, 1]`. fn instr_memory_grow(r: u32, ) -> Weight { - // Minimum execution time: 808 nanoseconds. - Weight::from_ref_time(906_944) - // Standard Error: 6_164 - .saturating_add(Weight::from_ref_time(187_445_255).saturating_mul(r.into())) + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 786 nanoseconds. + Weight::from_ref_time(874_044) + // Standard Error: 91_309 + .saturating_add(Weight::from_ref_time(181_849_955).saturating_mul(r.into())) } /// The range of component `r` is `[0, 50]`. fn instr_i64clz(r: u32, ) -> Weight { - // Minimum execution time: 807 nanoseconds. - Weight::from_ref_time(1_064_776) - // Standard Error: 287 - .saturating_add(Weight::from_ref_time(507_278).saturating_mul(r.into())) + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 763 nanoseconds. + Weight::from_ref_time(1_055_236) + // Standard Error: 207 + .saturating_add(Weight::from_ref_time(554_985).saturating_mul(r.into())) } /// The range of component `r` is `[0, 50]`. fn instr_i64ctz(r: u32, ) -> Weight { - // Minimum execution time: 776 nanoseconds. - Weight::from_ref_time(1_033_205) - // Standard Error: 443 - .saturating_add(Weight::from_ref_time(512_445).saturating_mul(r.into())) + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 756 nanoseconds. + Weight::from_ref_time(1_053_050) + // Standard Error: 165 + .saturating_add(Weight::from_ref_time(555_401).saturating_mul(r.into())) } /// The range of component `r` is `[0, 50]`. fn instr_i64popcnt(r: u32, ) -> Weight { - // Minimum execution time: 909 nanoseconds. - Weight::from_ref_time(1_089_083) - // Standard Error: 335 - .saturating_add(Weight::from_ref_time(510_392).saturating_mul(r.into())) + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 791 nanoseconds. + Weight::from_ref_time(1_080_240) + // Standard Error: 164 + .saturating_add(Weight::from_ref_time(554_698).saturating_mul(r.into())) } /// The range of component `r` is `[0, 50]`. fn instr_i64eqz(r: u32, ) -> Weight { - // Minimum execution time: 791 nanoseconds. - Weight::from_ref_time(1_063_118) - // Standard Error: 210 - .saturating_add(Weight::from_ref_time(522_817).saturating_mul(r.into())) + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 766 nanoseconds. + Weight::from_ref_time(1_074_739) + // Standard Error: 178 + .saturating_add(Weight::from_ref_time(565_891).saturating_mul(r.into())) } /// The range of component `r` is `[0, 50]`. fn instr_i64extendsi32(r: u32, ) -> Weight { - // Minimum execution time: 789 nanoseconds. - Weight::from_ref_time(1_056_006) - // Standard Error: 240 - .saturating_add(Weight::from_ref_time(505_198).saturating_mul(r.into())) + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 781 nanoseconds. + Weight::from_ref_time(1_077_122) + // Standard Error: 177 + .saturating_add(Weight::from_ref_time(548_846).saturating_mul(r.into())) } /// The range of component `r` is `[0, 50]`. fn instr_i64extendui32(r: u32, ) -> Weight { - // Minimum execution time: 804 nanoseconds. - Weight::from_ref_time(1_087_882) - // Standard Error: 234 - .saturating_add(Weight::from_ref_time(503_830).saturating_mul(r.into())) + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 793 nanoseconds. + Weight::from_ref_time(1_086_278) + // Standard Error: 163 + .saturating_add(Weight::from_ref_time(548_765).saturating_mul(r.into())) } /// The range of component `r` is `[0, 50]`. fn instr_i32wrapi64(r: u32, ) -> Weight { - // Minimum execution time: 756 nanoseconds. - Weight::from_ref_time(1_050_994) - // Standard Error: 172 - .saturating_add(Weight::from_ref_time(503_343).saturating_mul(r.into())) + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 769 nanoseconds. + Weight::from_ref_time(1_096_044) + // Standard Error: 134 + .saturating_add(Weight::from_ref_time(547_353).saturating_mul(r.into())) } /// The range of component `r` is `[0, 50]`. fn instr_i64eq(r: u32, ) -> Weight { - // Minimum execution time: 775 nanoseconds. - Weight::from_ref_time(1_061_228) - // Standard Error: 232 - .saturating_add(Weight::from_ref_time(730_751).saturating_mul(r.into())) + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 766 nanoseconds. + Weight::from_ref_time(1_072_610) + // Standard Error: 161 + .saturating_add(Weight::from_ref_time(774_895).saturating_mul(r.into())) } /// The range of component `r` is `[0, 50]`. fn instr_i64ne(r: u32, ) -> Weight { - // Minimum execution time: 809 nanoseconds. - Weight::from_ref_time(1_080_957) - // Standard Error: 320 - .saturating_add(Weight::from_ref_time(732_246).saturating_mul(r.into())) + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 751 nanoseconds. + Weight::from_ref_time(1_038_676) + // Standard Error: 158 + .saturating_add(Weight::from_ref_time(775_194).saturating_mul(r.into())) } /// The range of component `r` is `[0, 50]`. fn instr_i64lts(r: u32, ) -> Weight { - // Minimum execution time: 808 nanoseconds. - Weight::from_ref_time(1_055_752) - // Standard Error: 496 - .saturating_add(Weight::from_ref_time(740_084).saturating_mul(r.into())) + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 798 nanoseconds. + Weight::from_ref_time(1_089_712) + // Standard Error: 144 + .saturating_add(Weight::from_ref_time(774_377).saturating_mul(r.into())) } /// The range of component `r` is `[0, 50]`. fn instr_i64ltu(r: u32, ) -> Weight { - // Minimum execution time: 792 nanoseconds. - Weight::from_ref_time(1_111_972) - // Standard Error: 270 - .saturating_add(Weight::from_ref_time(741_581).saturating_mul(r.into())) + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 758 nanoseconds. + Weight::from_ref_time(1_078_460) + // Standard Error: 180 + .saturating_add(Weight::from_ref_time(779_861).saturating_mul(r.into())) } /// The range of component `r` is `[0, 50]`. fn instr_i64gts(r: u32, ) -> Weight { - // Minimum execution time: 862 nanoseconds. - Weight::from_ref_time(1_111_246) - // Standard Error: 263 - .saturating_add(Weight::from_ref_time(746_026).saturating_mul(r.into())) + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 758 nanoseconds. + Weight::from_ref_time(1_089_007) + // Standard Error: 164 + .saturating_add(Weight::from_ref_time(779_372).saturating_mul(r.into())) } /// The range of component `r` is `[0, 50]`. fn instr_i64gtu(r: u32, ) -> Weight { - // Minimum execution time: 810 nanoseconds. - Weight::from_ref_time(1_094_933) - // Standard Error: 271 - .saturating_add(Weight::from_ref_time(743_645).saturating_mul(r.into())) + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 772 nanoseconds. + Weight::from_ref_time(1_077_512) + // Standard Error: 165 + .saturating_add(Weight::from_ref_time(779_513).saturating_mul(r.into())) } /// The range of component `r` is `[0, 50]`. fn instr_i64les(r: u32, ) -> Weight { - // Minimum execution time: 769 nanoseconds. - Weight::from_ref_time(819_933) - // Standard Error: 4_528 - .saturating_add(Weight::from_ref_time(751_599).saturating_mul(r.into())) + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 760 nanoseconds. + Weight::from_ref_time(1_078_546) + // Standard Error: 168 + .saturating_add(Weight::from_ref_time(779_138).saturating_mul(r.into())) } /// The range of component `r` is `[0, 50]`. fn instr_i64leu(r: u32, ) -> Weight { - // Minimum execution time: 797 nanoseconds. - Weight::from_ref_time(1_107_352) - // Standard Error: 344 - .saturating_add(Weight::from_ref_time(738_684).saturating_mul(r.into())) + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 757 nanoseconds. + Weight::from_ref_time(1_080_251) + // Standard Error: 168 + .saturating_add(Weight::from_ref_time(779_391).saturating_mul(r.into())) } /// The range of component `r` is `[0, 50]`. fn instr_i64ges(r: u32, ) -> Weight { - // Minimum execution time: 784 nanoseconds. - Weight::from_ref_time(1_068_487) - // Standard Error: 3_796 - .saturating_add(Weight::from_ref_time(741_207).saturating_mul(r.into())) + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 786 nanoseconds. + Weight::from_ref_time(1_072_690) + // Standard Error: 219 + .saturating_add(Weight::from_ref_time(780_381).saturating_mul(r.into())) } /// The range of component `r` is `[0, 50]`. fn instr_i64geu(r: u32, ) -> Weight { - // Minimum execution time: 813 nanoseconds. - Weight::from_ref_time(1_067_278) - // Standard Error: 459 - .saturating_add(Weight::from_ref_time(737_841).saturating_mul(r.into())) + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 771 nanoseconds. + Weight::from_ref_time(1_063_735) + // Standard Error: 162 + .saturating_add(Weight::from_ref_time(779_906).saturating_mul(r.into())) } /// The range of component `r` is `[0, 50]`. fn instr_i64add(r: u32, ) -> Weight { - // Minimum execution time: 798 nanoseconds. - Weight::from_ref_time(1_100_254) - // Standard Error: 297 - .saturating_add(Weight::from_ref_time(717_622).saturating_mul(r.into())) + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 785 nanoseconds. + Weight::from_ref_time(1_059_585) + // Standard Error: 155 + .saturating_add(Weight::from_ref_time(756_828).saturating_mul(r.into())) } /// The range of component `r` is `[0, 50]`. fn instr_i64sub(r: u32, ) -> Weight { - // Minimum execution time: 778 nanoseconds. - Weight::from_ref_time(1_063_748) - // Standard Error: 378 - .saturating_add(Weight::from_ref_time(713_577).saturating_mul(r.into())) + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 800 nanoseconds. + Weight::from_ref_time(1_066_659) + // Standard Error: 154 + .saturating_add(Weight::from_ref_time(754_318).saturating_mul(r.into())) } /// The range of component `r` is `[0, 50]`. fn instr_i64mul(r: u32, ) -> Weight { - // Minimum execution time: 780 nanoseconds. - Weight::from_ref_time(1_088_755) - // Standard Error: 316 - .saturating_add(Weight::from_ref_time(714_127).saturating_mul(r.into())) + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 769 nanoseconds. + Weight::from_ref_time(1_078_854) + // Standard Error: 172 + .saturating_add(Weight::from_ref_time(754_183).saturating_mul(r.into())) } /// The range of component `r` is `[0, 50]`. fn instr_i64divs(r: u32, ) -> Weight { - // Minimum execution time: 776 nanoseconds. - Weight::from_ref_time(1_062_591) - // Standard Error: 610 - .saturating_add(Weight::from_ref_time(1_355_430).saturating_mul(r.into())) + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 770 nanoseconds. + Weight::from_ref_time(1_057_476) + // Standard Error: 191 + .saturating_add(Weight::from_ref_time(1_443_902).saturating_mul(r.into())) } /// The range of component `r` is `[0, 50]`. fn instr_i64divu(r: u32, ) -> Weight { - // Minimum execution time: 801 nanoseconds. - Weight::from_ref_time(1_092_395) - // Standard Error: 423 - .saturating_add(Weight::from_ref_time(1_282_356).saturating_mul(r.into())) + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 764 nanoseconds. + Weight::from_ref_time(1_063_821) + // Standard Error: 193 + .saturating_add(Weight::from_ref_time(1_324_496).saturating_mul(r.into())) } /// The range of component `r` is `[0, 50]`. fn instr_i64rems(r: u32, ) -> Weight { - // Minimum execution time: 822 nanoseconds. - Weight::from_ref_time(1_083_079) - // Standard Error: 562 - .saturating_add(Weight::from_ref_time(1_398_522).saturating_mul(r.into())) + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 747 nanoseconds. + Weight::from_ref_time(1_093_209) + // Standard Error: 270 + .saturating_add(Weight::from_ref_time(1_447_180).saturating_mul(r.into())) } /// The range of component `r` is `[0, 50]`. fn instr_i64remu(r: u32, ) -> Weight { - // Minimum execution time: 798 nanoseconds. - Weight::from_ref_time(1_088_997) - // Standard Error: 401 - .saturating_add(Weight::from_ref_time(1_283_442).saturating_mul(r.into())) + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 805 nanoseconds. + Weight::from_ref_time(1_033_953) + // Standard Error: 152 + .saturating_add(Weight::from_ref_time(1_336_911).saturating_mul(r.into())) } /// The range of component `r` is `[0, 50]`. fn instr_i64and(r: u32, ) -> Weight { - // Minimum execution time: 815 nanoseconds. - Weight::from_ref_time(1_081_903) - // Standard Error: 264 - .saturating_add(Weight::from_ref_time(718_069).saturating_mul(r.into())) + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 766 nanoseconds. + Weight::from_ref_time(1_059_430) + // Standard Error: 160 + .saturating_add(Weight::from_ref_time(757_265).saturating_mul(r.into())) } /// The range of component `r` is `[0, 50]`. fn instr_i64or(r: u32, ) -> Weight { - // Minimum execution time: 813 nanoseconds. - Weight::from_ref_time(1_091_426) - // Standard Error: 296 - .saturating_add(Weight::from_ref_time(717_894).saturating_mul(r.into())) + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 760 nanoseconds. + Weight::from_ref_time(1_077_376) + // Standard Error: 160 + .saturating_add(Weight::from_ref_time(755_800).saturating_mul(r.into())) } /// The range of component `r` is `[0, 50]`. fn instr_i64xor(r: u32, ) -> Weight { - // Minimum execution time: 794 nanoseconds. - Weight::from_ref_time(1_040_144) - // Standard Error: 400 - .saturating_add(Weight::from_ref_time(719_886).saturating_mul(r.into())) + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 798 nanoseconds. + Weight::from_ref_time(1_070_570) + // Standard Error: 157 + .saturating_add(Weight::from_ref_time(756_839).saturating_mul(r.into())) } /// The range of component `r` is `[0, 50]`. fn instr_i64shl(r: u32, ) -> Weight { - // Minimum execution time: 774 nanoseconds. - Weight::from_ref_time(1_092_466) - // Standard Error: 4_173 - .saturating_add(Weight::from_ref_time(739_873).saturating_mul(r.into())) + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 761 nanoseconds. + Weight::from_ref_time(1_074_645) + // Standard Error: 169 + .saturating_add(Weight::from_ref_time(771_486).saturating_mul(r.into())) } /// The range of component `r` is `[0, 50]`. fn instr_i64shrs(r: u32, ) -> Weight { - // Minimum execution time: 793 nanoseconds. - Weight::from_ref_time(1_105_611) - // Standard Error: 394 - .saturating_add(Weight::from_ref_time(735_358).saturating_mul(r.into())) + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 779 nanoseconds. + Weight::from_ref_time(1_107_671) + // Standard Error: 185 + .saturating_add(Weight::from_ref_time(769_168).saturating_mul(r.into())) } /// The range of component `r` is `[0, 50]`. fn instr_i64shru(r: u32, ) -> Weight { - // Minimum execution time: 805 nanoseconds. - Weight::from_ref_time(1_130_462) - // Standard Error: 327 - .saturating_add(Weight::from_ref_time(734_198).saturating_mul(r.into())) + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 755 nanoseconds. + Weight::from_ref_time(1_075_769) + // Standard Error: 164 + .saturating_add(Weight::from_ref_time(770_334).saturating_mul(r.into())) } /// The range of component `r` is `[0, 50]`. fn instr_i64rotl(r: u32, ) -> Weight { - // Minimum execution time: 831 nanoseconds. - Weight::from_ref_time(1_118_894) - // Standard Error: 389 - .saturating_add(Weight::from_ref_time(733_834).saturating_mul(r.into())) + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 767 nanoseconds. + Weight::from_ref_time(608_749) + // Standard Error: 2_059 + .saturating_add(Weight::from_ref_time(804_228).saturating_mul(r.into())) } /// The range of component `r` is `[0, 50]`. fn instr_i64rotr(r: u32, ) -> Weight { - // Minimum execution time: 772 nanoseconds. - Weight::from_ref_time(1_123_733) - // Standard Error: 337 - .saturating_add(Weight::from_ref_time(733_923).saturating_mul(r.into())) + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 779 nanoseconds. + Weight::from_ref_time(1_054_998) + // Standard Error: 191 + .saturating_add(Weight::from_ref_time(770_225).saturating_mul(r.into())) } } diff --git a/frame/conviction-voting/src/weights.rs b/frame/conviction-voting/src/weights.rs index e50842449..c5c712383 100644 --- a/frame/conviction-voting/src/weights.rs +++ b/frame/conviction-voting/src/weights.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2022 Parity Technologies (UK) Ltd. +// Copyright (C) 2023 Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); @@ -18,7 +18,8 @@ //! Autogenerated weights for pallet_conviction_voting //! //! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev -//! DATE: 2022-11-07, STEPS: `50`, REPEAT: 20, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! DATE: 2023-01-24, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! WORST CASE MAP SIZE: `1000000` //! HOSTNAME: `bm2`, CPU: `Intel(R) Core(TM) i7-7700K CPU @ 4.20GHz` //! EXECUTION: Some(Wasm), WASM-EXECUTION: Compiled, CHAIN: Some("dev"), DB CACHE: 1024 @@ -59,164 +60,262 @@ pub trait WeightInfo { /// Weights for pallet_conviction_voting using the Substrate node and recommended hardware. pub struct SubstrateWeight(PhantomData); impl WeightInfo for SubstrateWeight { - // Storage: Referenda ReferendumInfoFor (r:1 w:1) - // Storage: ConvictionVoting VotingFor (r:1 w:1) - // Storage: ConvictionVoting ClassLocksFor (r:1 w:1) - // Storage: Balances Locks (r:1 w:1) - // Storage: Scheduler Agenda (r:2 w:2) + /// Storage: Referenda ReferendumInfoFor (r:1 w:1) + /// Proof: Referenda ReferendumInfoFor (max_values: None, max_size: Some(366), added: 2841, mode: MaxEncodedLen) + /// Storage: ConvictionVoting VotingFor (r:1 w:1) + /// Proof: ConvictionVoting VotingFor (max_values: None, max_size: Some(27241), added: 29716, mode: MaxEncodedLen) + /// Storage: ConvictionVoting ClassLocksFor (r:1 w:1) + /// Proof: ConvictionVoting ClassLocksFor (max_values: None, max_size: Some(59), added: 2534, mode: MaxEncodedLen) + /// Storage: Balances Locks (r:1 w:1) + /// Proof: Balances Locks (max_values: None, max_size: Some(1299), added: 3774, mode: MaxEncodedLen) + /// Storage: Scheduler Agenda (r:2 w:2) + /// Proof: Scheduler Agenda (max_values: None, max_size: Some(107022), added: 109497, mode: MaxEncodedLen) fn vote_new() -> Weight { - // Minimum execution time: 131_633 nanoseconds. - Weight::from_ref_time(132_742_000 as u64) - .saturating_add(T::DbWeight::get().reads(6 as u64)) - .saturating_add(T::DbWeight::get().writes(6 as u64)) + // Proof Size summary in bytes: + // Measured: `13168` + // Estimated: `257859` + // Minimum execution time: 85_569 nanoseconds. + Weight::from_parts(86_492_000, 257859) + .saturating_add(T::DbWeight::get().reads(6_u64)) + .saturating_add(T::DbWeight::get().writes(6_u64)) } - // Storage: Referenda ReferendumInfoFor (r:1 w:1) - // Storage: ConvictionVoting VotingFor (r:1 w:1) - // Storage: ConvictionVoting ClassLocksFor (r:1 w:1) - // Storage: Balances Locks (r:1 w:1) - // Storage: Scheduler Agenda (r:2 w:2) + /// Storage: Referenda ReferendumInfoFor (r:1 w:1) + /// Proof: Referenda ReferendumInfoFor (max_values: None, max_size: Some(366), added: 2841, mode: MaxEncodedLen) + /// Storage: ConvictionVoting VotingFor (r:1 w:1) + /// Proof: ConvictionVoting VotingFor (max_values: None, max_size: Some(27241), added: 29716, mode: MaxEncodedLen) + /// Storage: ConvictionVoting ClassLocksFor (r:1 w:1) + /// Proof: ConvictionVoting ClassLocksFor (max_values: None, max_size: Some(59), added: 2534, mode: MaxEncodedLen) + /// Storage: Balances Locks (r:1 w:1) + /// Proof: Balances Locks (max_values: None, max_size: Some(1299), added: 3774, mode: MaxEncodedLen) + /// Storage: Scheduler Agenda (r:2 w:2) + /// Proof: Scheduler Agenda (max_values: None, max_size: Some(107022), added: 109497, mode: MaxEncodedLen) fn vote_existing() -> Weight { - // Minimum execution time: 176_240 nanoseconds. - Weight::from_ref_time(183_274_000 as u64) - .saturating_add(T::DbWeight::get().reads(6 as u64)) - .saturating_add(T::DbWeight::get().writes(6 as u64)) + // Proof Size summary in bytes: + // Measured: `20342` + // Estimated: `257859` + // Minimum execution time: 212_727 nanoseconds. + Weight::from_parts(213_741_000, 257859) + .saturating_add(T::DbWeight::get().reads(6_u64)) + .saturating_add(T::DbWeight::get().writes(6_u64)) } - // Storage: ConvictionVoting VotingFor (r:1 w:1) - // Storage: Referenda ReferendumInfoFor (r:1 w:1) - // Storage: Scheduler Agenda (r:2 w:2) + /// Storage: ConvictionVoting VotingFor (r:1 w:1) + /// Proof: ConvictionVoting VotingFor (max_values: None, max_size: Some(27241), added: 29716, mode: MaxEncodedLen) + /// Storage: Referenda ReferendumInfoFor (r:1 w:1) + /// Proof: Referenda ReferendumInfoFor (max_values: None, max_size: Some(366), added: 2841, mode: MaxEncodedLen) + /// Storage: Scheduler Agenda (r:2 w:2) + /// Proof: Scheduler Agenda (max_values: None, max_size: Some(107022), added: 109497, mode: MaxEncodedLen) fn remove_vote() -> Weight { - // Minimum execution time: 158_880 nanoseconds. - Weight::from_ref_time(164_648_000 as u64) - .saturating_add(T::DbWeight::get().reads(4 as u64)) - .saturating_add(T::DbWeight::get().writes(4 as u64)) + // Proof Size summary in bytes: + // Measured: `20062` + // Estimated: `251551` + // Minimum execution time: 200_513 nanoseconds. + Weight::from_parts(201_589_000, 251551) + .saturating_add(T::DbWeight::get().reads(4_u64)) + .saturating_add(T::DbWeight::get().writes(4_u64)) } - // Storage: ConvictionVoting VotingFor (r:1 w:1) - // Storage: Referenda ReferendumInfoFor (r:1 w:0) + /// Storage: ConvictionVoting VotingFor (r:1 w:1) + /// Proof: ConvictionVoting VotingFor (max_values: None, max_size: Some(27241), added: 29716, mode: MaxEncodedLen) + /// Storage: Referenda ReferendumInfoFor (r:1 w:0) + /// Proof: Referenda ReferendumInfoFor (max_values: None, max_size: Some(366), added: 2841, mode: MaxEncodedLen) fn remove_other_vote() -> Weight { - // Minimum execution time: 60_330 nanoseconds. - Weight::from_ref_time(61_588_000 as u64) - .saturating_add(T::DbWeight::get().reads(2 as u64)) - .saturating_add(T::DbWeight::get().writes(1 as u64)) + // Proof Size summary in bytes: + // Measured: `12738` + // Estimated: `32557` + // Minimum execution time: 43_083 nanoseconds. + Weight::from_parts(43_763_000, 32557) + .saturating_add(T::DbWeight::get().reads(2_u64)) + .saturating_add(T::DbWeight::get().writes(1_u64)) } - // Storage: ConvictionVoting VotingFor (r:2 w:2) - // Storage: ConvictionVoting ClassLocksFor (r:1 w:1) - // Storage: Balances Locks (r:1 w:1) - // Storage: Referenda ReferendumInfoFor (r:1 w:1) - // Storage: Scheduler Agenda (r:2 w:2) + /// Storage: ConvictionVoting VotingFor (r:2 w:2) + /// Proof: ConvictionVoting VotingFor (max_values: None, max_size: Some(27241), added: 29716, mode: MaxEncodedLen) + /// Storage: Referenda ReferendumInfoFor (r:1 w:1) + /// Proof: Referenda ReferendumInfoFor (max_values: None, max_size: Some(366), added: 2841, mode: MaxEncodedLen) + /// Storage: Scheduler Agenda (r:2 w:2) + /// Proof: Scheduler Agenda (max_values: None, max_size: Some(107022), added: 109497, mode: MaxEncodedLen) + /// Storage: ConvictionVoting ClassLocksFor (r:1 w:1) + /// Proof: ConvictionVoting ClassLocksFor (max_values: None, max_size: Some(59), added: 2534, mode: MaxEncodedLen) + /// Storage: Balances Locks (r:1 w:1) + /// Proof: Balances Locks (max_values: None, max_size: Some(1299), added: 3774, mode: MaxEncodedLen) /// The range of component `r` is `[0, 1]`. fn delegate(r: u32, ) -> Weight { - // Minimum execution time: 63_088 nanoseconds. - Weight::from_ref_time(67_803_536 as u64) - // Standard Error: 197_102 - .saturating_add(Weight::from_ref_time(31_557_563 as u64).saturating_mul(r as u64)) - .saturating_add(T::DbWeight::get().reads(4 as u64)) - .saturating_add(T::DbWeight::get().reads((3 as u64).saturating_mul(r as u64))) - .saturating_add(T::DbWeight::get().writes(4 as u64)) - .saturating_add(T::DbWeight::get().writes((3 as u64).saturating_mul(r as u64))) + // Proof Size summary in bytes: + // Measured: `272 + r * (1689 ±0)` + // Estimated: `176657 + r * (110917 ±0)` + // Minimum execution time: 33_246 nanoseconds. + Weight::from_parts(34_560_391, 176657) + // Standard Error: 63_925 + .saturating_add(Weight::from_ref_time(34_500_408).saturating_mul(r.into())) + .saturating_add(T::DbWeight::get().reads(4_u64)) + .saturating_add(T::DbWeight::get().reads((3_u64).saturating_mul(r.into()))) + .saturating_add(T::DbWeight::get().writes(4_u64)) + .saturating_add(T::DbWeight::get().writes((3_u64).saturating_mul(r.into()))) + .saturating_add(Weight::from_proof_size(110917).saturating_mul(r.into())) } - // Storage: ConvictionVoting VotingFor (r:2 w:2) - // Storage: Referenda ReferendumInfoFor (r:1 w:1) - // Storage: Scheduler Agenda (r:2 w:2) + /// Storage: ConvictionVoting VotingFor (r:2 w:2) + /// Proof: ConvictionVoting VotingFor (max_values: None, max_size: Some(27241), added: 29716, mode: MaxEncodedLen) + /// Storage: Referenda ReferendumInfoFor (r:1 w:1) + /// Proof: Referenda ReferendumInfoFor (max_values: None, max_size: Some(366), added: 2841, mode: MaxEncodedLen) + /// Storage: Scheduler Agenda (r:2 w:2) + /// Proof: Scheduler Agenda (max_values: None, max_size: Some(107022), added: 109497, mode: MaxEncodedLen) /// The range of component `r` is `[0, 1]`. fn undelegate(r: u32, ) -> Weight { - // Minimum execution time: 45_150 nanoseconds. - Weight::from_ref_time(51_547_530 as u64) - // Standard Error: 771_127 - .saturating_add(Weight::from_ref_time(26_927_969 as u64).saturating_mul(r as u64)) - .saturating_add(T::DbWeight::get().reads(2 as u64)) - .saturating_add(T::DbWeight::get().reads((3 as u64).saturating_mul(r as u64))) - .saturating_add(T::DbWeight::get().writes(2 as u64)) - .saturating_add(T::DbWeight::get().writes((3 as u64).saturating_mul(r as u64))) + // Proof Size summary in bytes: + // Measured: `470 + r * (1407 ±0)` + // Estimated: `170349 + r * (110917 ±0)` + // Minimum execution time: 20_508 nanoseconds. + Weight::from_parts(21_240_024, 170349) + // Standard Error: 37_314 + .saturating_add(Weight::from_ref_time(30_890_875).saturating_mul(r.into())) + .saturating_add(T::DbWeight::get().reads(2_u64)) + .saturating_add(T::DbWeight::get().reads((3_u64).saturating_mul(r.into()))) + .saturating_add(T::DbWeight::get().writes(2_u64)) + .saturating_add(T::DbWeight::get().writes((3_u64).saturating_mul(r.into()))) + .saturating_add(Weight::from_proof_size(110917).saturating_mul(r.into())) } - // Storage: ConvictionVoting VotingFor (r:1 w:1) - // Storage: ConvictionVoting ClassLocksFor (r:1 w:1) - // Storage: Balances Locks (r:1 w:1) + /// Storage: ConvictionVoting VotingFor (r:1 w:1) + /// Proof: ConvictionVoting VotingFor (max_values: None, max_size: Some(27241), added: 29716, mode: MaxEncodedLen) + /// Storage: ConvictionVoting ClassLocksFor (r:1 w:1) + /// Proof: ConvictionVoting ClassLocksFor (max_values: None, max_size: Some(59), added: 2534, mode: MaxEncodedLen) + /// Storage: Balances Locks (r:1 w:1) + /// Proof: Balances Locks (max_values: None, max_size: Some(1299), added: 3774, mode: MaxEncodedLen) fn unlock() -> Weight { - // Minimum execution time: 75_067 nanoseconds. - Weight::from_ref_time(76_888_000 as u64) - .saturating_add(T::DbWeight::get().reads(3 as u64)) - .saturating_add(T::DbWeight::get().writes(3 as u64)) + // Proof Size summary in bytes: + // Measured: `11797` + // Estimated: `36024` + // Minimum execution time: 50_305 nanoseconds. + Weight::from_parts(51_226_000, 36024) + .saturating_add(T::DbWeight::get().reads(3_u64)) + .saturating_add(T::DbWeight::get().writes(3_u64)) } } // For backwards compatibility and tests impl WeightInfo for () { - // Storage: Referenda ReferendumInfoFor (r:1 w:1) - // Storage: ConvictionVoting VotingFor (r:1 w:1) - // Storage: ConvictionVoting ClassLocksFor (r:1 w:1) - // Storage: Balances Locks (r:1 w:1) - // Storage: Scheduler Agenda (r:2 w:2) + /// Storage: Referenda ReferendumInfoFor (r:1 w:1) + /// Proof: Referenda ReferendumInfoFor (max_values: None, max_size: Some(366), added: 2841, mode: MaxEncodedLen) + /// Storage: ConvictionVoting VotingFor (r:1 w:1) + /// Proof: ConvictionVoting VotingFor (max_values: None, max_size: Some(27241), added: 29716, mode: MaxEncodedLen) + /// Storage: ConvictionVoting ClassLocksFor (r:1 w:1) + /// Proof: ConvictionVoting ClassLocksFor (max_values: None, max_size: Some(59), added: 2534, mode: MaxEncodedLen) + /// Storage: Balances Locks (r:1 w:1) + /// Proof: Balances Locks (max_values: None, max_size: Some(1299), added: 3774, mode: MaxEncodedLen) + /// Storage: Scheduler Agenda (r:2 w:2) + /// Proof: Scheduler Agenda (max_values: None, max_size: Some(107022), added: 109497, mode: MaxEncodedLen) fn vote_new() -> Weight { - // Minimum execution time: 131_633 nanoseconds. - Weight::from_ref_time(132_742_000 as u64) - .saturating_add(RocksDbWeight::get().reads(6 as u64)) - .saturating_add(RocksDbWeight::get().writes(6 as u64)) + // Proof Size summary in bytes: + // Measured: `13168` + // Estimated: `257859` + // Minimum execution time: 85_569 nanoseconds. + Weight::from_parts(86_492_000, 257859) + .saturating_add(RocksDbWeight::get().reads(6_u64)) + .saturating_add(RocksDbWeight::get().writes(6_u64)) } - // Storage: Referenda ReferendumInfoFor (r:1 w:1) - // Storage: ConvictionVoting VotingFor (r:1 w:1) - // Storage: ConvictionVoting ClassLocksFor (r:1 w:1) - // Storage: Balances Locks (r:1 w:1) - // Storage: Scheduler Agenda (r:2 w:2) + /// Storage: Referenda ReferendumInfoFor (r:1 w:1) + /// Proof: Referenda ReferendumInfoFor (max_values: None, max_size: Some(366), added: 2841, mode: MaxEncodedLen) + /// Storage: ConvictionVoting VotingFor (r:1 w:1) + /// Proof: ConvictionVoting VotingFor (max_values: None, max_size: Some(27241), added: 29716, mode: MaxEncodedLen) + /// Storage: ConvictionVoting ClassLocksFor (r:1 w:1) + /// Proof: ConvictionVoting ClassLocksFor (max_values: None, max_size: Some(59), added: 2534, mode: MaxEncodedLen) + /// Storage: Balances Locks (r:1 w:1) + /// Proof: Balances Locks (max_values: None, max_size: Some(1299), added: 3774, mode: MaxEncodedLen) + /// Storage: Scheduler Agenda (r:2 w:2) + /// Proof: Scheduler Agenda (max_values: None, max_size: Some(107022), added: 109497, mode: MaxEncodedLen) fn vote_existing() -> Weight { - // Minimum execution time: 176_240 nanoseconds. - Weight::from_ref_time(183_274_000 as u64) - .saturating_add(RocksDbWeight::get().reads(6 as u64)) - .saturating_add(RocksDbWeight::get().writes(6 as u64)) + // Proof Size summary in bytes: + // Measured: `20342` + // Estimated: `257859` + // Minimum execution time: 212_727 nanoseconds. + Weight::from_parts(213_741_000, 257859) + .saturating_add(RocksDbWeight::get().reads(6_u64)) + .saturating_add(RocksDbWeight::get().writes(6_u64)) } - // Storage: ConvictionVoting VotingFor (r:1 w:1) - // Storage: Referenda ReferendumInfoFor (r:1 w:1) - // Storage: Scheduler Agenda (r:2 w:2) + /// Storage: ConvictionVoting VotingFor (r:1 w:1) + /// Proof: ConvictionVoting VotingFor (max_values: None, max_size: Some(27241), added: 29716, mode: MaxEncodedLen) + /// Storage: Referenda ReferendumInfoFor (r:1 w:1) + /// Proof: Referenda ReferendumInfoFor (max_values: None, max_size: Some(366), added: 2841, mode: MaxEncodedLen) + /// Storage: Scheduler Agenda (r:2 w:2) + /// Proof: Scheduler Agenda (max_values: None, max_size: Some(107022), added: 109497, mode: MaxEncodedLen) fn remove_vote() -> Weight { - // Minimum execution time: 158_880 nanoseconds. - Weight::from_ref_time(164_648_000 as u64) - .saturating_add(RocksDbWeight::get().reads(4 as u64)) - .saturating_add(RocksDbWeight::get().writes(4 as u64)) + // Proof Size summary in bytes: + // Measured: `20062` + // Estimated: `251551` + // Minimum execution time: 200_513 nanoseconds. + Weight::from_parts(201_589_000, 251551) + .saturating_add(RocksDbWeight::get().reads(4_u64)) + .saturating_add(RocksDbWeight::get().writes(4_u64)) } - // Storage: ConvictionVoting VotingFor (r:1 w:1) - // Storage: Referenda ReferendumInfoFor (r:1 w:0) + /// Storage: ConvictionVoting VotingFor (r:1 w:1) + /// Proof: ConvictionVoting VotingFor (max_values: None, max_size: Some(27241), added: 29716, mode: MaxEncodedLen) + /// Storage: Referenda ReferendumInfoFor (r:1 w:0) + /// Proof: Referenda ReferendumInfoFor (max_values: None, max_size: Some(366), added: 2841, mode: MaxEncodedLen) fn remove_other_vote() -> Weight { - // Minimum execution time: 60_330 nanoseconds. - Weight::from_ref_time(61_588_000 as u64) - .saturating_add(RocksDbWeight::get().reads(2 as u64)) - .saturating_add(RocksDbWeight::get().writes(1 as u64)) + // Proof Size summary in bytes: + // Measured: `12738` + // Estimated: `32557` + // Minimum execution time: 43_083 nanoseconds. + Weight::from_parts(43_763_000, 32557) + .saturating_add(RocksDbWeight::get().reads(2_u64)) + .saturating_add(RocksDbWeight::get().writes(1_u64)) } - // Storage: ConvictionVoting VotingFor (r:2 w:2) - // Storage: ConvictionVoting ClassLocksFor (r:1 w:1) - // Storage: Balances Locks (r:1 w:1) - // Storage: Referenda ReferendumInfoFor (r:1 w:1) - // Storage: Scheduler Agenda (r:2 w:2) + /// Storage: ConvictionVoting VotingFor (r:2 w:2) + /// Proof: ConvictionVoting VotingFor (max_values: None, max_size: Some(27241), added: 29716, mode: MaxEncodedLen) + /// Storage: Referenda ReferendumInfoFor (r:1 w:1) + /// Proof: Referenda ReferendumInfoFor (max_values: None, max_size: Some(366), added: 2841, mode: MaxEncodedLen) + /// Storage: Scheduler Agenda (r:2 w:2) + /// Proof: Scheduler Agenda (max_values: None, max_size: Some(107022), added: 109497, mode: MaxEncodedLen) + /// Storage: ConvictionVoting ClassLocksFor (r:1 w:1) + /// Proof: ConvictionVoting ClassLocksFor (max_values: None, max_size: Some(59), added: 2534, mode: MaxEncodedLen) + /// Storage: Balances Locks (r:1 w:1) + /// Proof: Balances Locks (max_values: None, max_size: Some(1299), added: 3774, mode: MaxEncodedLen) /// The range of component `r` is `[0, 1]`. fn delegate(r: u32, ) -> Weight { - // Minimum execution time: 63_088 nanoseconds. - Weight::from_ref_time(67_803_536 as u64) - // Standard Error: 197_102 - .saturating_add(Weight::from_ref_time(31_557_563 as u64).saturating_mul(r as u64)) - .saturating_add(RocksDbWeight::get().reads(4 as u64)) - .saturating_add(RocksDbWeight::get().reads((3 as u64).saturating_mul(r as u64))) - .saturating_add(RocksDbWeight::get().writes(4 as u64)) - .saturating_add(RocksDbWeight::get().writes((3 as u64).saturating_mul(r as u64))) + // Proof Size summary in bytes: + // Measured: `272 + r * (1689 ±0)` + // Estimated: `176657 + r * (110917 ±0)` + // Minimum execution time: 33_246 nanoseconds. + Weight::from_parts(34_560_391, 176657) + // Standard Error: 63_925 + .saturating_add(Weight::from_ref_time(34_500_408).saturating_mul(r.into())) + .saturating_add(RocksDbWeight::get().reads(4_u64)) + .saturating_add(RocksDbWeight::get().reads((3_u64).saturating_mul(r.into()))) + .saturating_add(RocksDbWeight::get().writes(4_u64)) + .saturating_add(RocksDbWeight::get().writes((3_u64).saturating_mul(r.into()))) + .saturating_add(Weight::from_proof_size(110917).saturating_mul(r.into())) } - // Storage: ConvictionVoting VotingFor (r:2 w:2) - // Storage: Referenda ReferendumInfoFor (r:1 w:1) - // Storage: Scheduler Agenda (r:2 w:2) + /// Storage: ConvictionVoting VotingFor (r:2 w:2) + /// Proof: ConvictionVoting VotingFor (max_values: None, max_size: Some(27241), added: 29716, mode: MaxEncodedLen) + /// Storage: Referenda ReferendumInfoFor (r:1 w:1) + /// Proof: Referenda ReferendumInfoFor (max_values: None, max_size: Some(366), added: 2841, mode: MaxEncodedLen) + /// Storage: Scheduler Agenda (r:2 w:2) + /// Proof: Scheduler Agenda (max_values: None, max_size: Some(107022), added: 109497, mode: MaxEncodedLen) /// The range of component `r` is `[0, 1]`. fn undelegate(r: u32, ) -> Weight { - // Minimum execution time: 45_150 nanoseconds. - Weight::from_ref_time(51_547_530 as u64) - // Standard Error: 771_127 - .saturating_add(Weight::from_ref_time(26_927_969 as u64).saturating_mul(r as u64)) - .saturating_add(RocksDbWeight::get().reads(2 as u64)) - .saturating_add(RocksDbWeight::get().reads((3 as u64).saturating_mul(r as u64))) - .saturating_add(RocksDbWeight::get().writes(2 as u64)) - .saturating_add(RocksDbWeight::get().writes((3 as u64).saturating_mul(r as u64))) + // Proof Size summary in bytes: + // Measured: `470 + r * (1407 ±0)` + // Estimated: `170349 + r * (110917 ±0)` + // Minimum execution time: 20_508 nanoseconds. + Weight::from_parts(21_240_024, 170349) + // Standard Error: 37_314 + .saturating_add(Weight::from_ref_time(30_890_875).saturating_mul(r.into())) + .saturating_add(RocksDbWeight::get().reads(2_u64)) + .saturating_add(RocksDbWeight::get().reads((3_u64).saturating_mul(r.into()))) + .saturating_add(RocksDbWeight::get().writes(2_u64)) + .saturating_add(RocksDbWeight::get().writes((3_u64).saturating_mul(r.into()))) + .saturating_add(Weight::from_proof_size(110917).saturating_mul(r.into())) } - // Storage: ConvictionVoting VotingFor (r:1 w:1) - // Storage: ConvictionVoting ClassLocksFor (r:1 w:1) - // Storage: Balances Locks (r:1 w:1) + /// Storage: ConvictionVoting VotingFor (r:1 w:1) + /// Proof: ConvictionVoting VotingFor (max_values: None, max_size: Some(27241), added: 29716, mode: MaxEncodedLen) + /// Storage: ConvictionVoting ClassLocksFor (r:1 w:1) + /// Proof: ConvictionVoting ClassLocksFor (max_values: None, max_size: Some(59), added: 2534, mode: MaxEncodedLen) + /// Storage: Balances Locks (r:1 w:1) + /// Proof: Balances Locks (max_values: None, max_size: Some(1299), added: 3774, mode: MaxEncodedLen) fn unlock() -> Weight { - // Minimum execution time: 75_067 nanoseconds. - Weight::from_ref_time(76_888_000 as u64) - .saturating_add(RocksDbWeight::get().reads(3 as u64)) - .saturating_add(RocksDbWeight::get().writes(3 as u64)) + // Proof Size summary in bytes: + // Measured: `11797` + // Estimated: `36024` + // Minimum execution time: 50_305 nanoseconds. + Weight::from_parts(51_226_000, 36024) + .saturating_add(RocksDbWeight::get().reads(3_u64)) + .saturating_add(RocksDbWeight::get().writes(3_u64)) } } diff --git a/frame/democracy/src/weights.rs b/frame/democracy/src/weights.rs index db3969d40..985fe8fbd 100644 --- a/frame/democracy/src/weights.rs +++ b/frame/democracy/src/weights.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2022 Parity Technologies (UK) Ltd. +// Copyright (C) 2023 Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); @@ -18,7 +18,8 @@ //! Autogenerated weights for pallet_democracy //! //! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev -//! DATE: 2022-11-07, STEPS: `50`, REPEAT: 20, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! DATE: 2023-01-24, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! WORST CASE MAP SIZE: `1000000` //! HOSTNAME: `bm2`, CPU: `Intel(R) Core(TM) i7-7700K CPU @ 4.20GHz` //! EXECUTION: Some(Wasm), WASM-EXECUTION: Compiled, CHAIN: Some("dev"), DB CACHE: 1024 @@ -74,438 +75,692 @@ pub trait WeightInfo { /// Weights for pallet_democracy using the Substrate node and recommended hardware. pub struct SubstrateWeight(PhantomData); impl WeightInfo for SubstrateWeight { - // Storage: Democracy PublicPropCount (r:1 w:1) - // Storage: Democracy PublicProps (r:1 w:1) - // Storage: Democracy Blacklist (r:1 w:0) - // Storage: Democracy DepositOf (r:0 w:1) + /// Storage: Democracy PublicPropCount (r:1 w:1) + /// Proof: Democracy PublicPropCount (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) + /// Storage: Democracy PublicProps (r:1 w:1) + /// Proof: Democracy PublicProps (max_values: Some(1), max_size: Some(16702), added: 17197, mode: MaxEncodedLen) + /// Storage: Democracy Blacklist (r:1 w:0) + /// Proof: Democracy Blacklist (max_values: None, max_size: Some(3238), added: 5713, mode: MaxEncodedLen) + /// Storage: Democracy DepositOf (r:0 w:1) + /// Proof: Democracy DepositOf (max_values: None, max_size: Some(3230), added: 5705, mode: MaxEncodedLen) fn propose() -> Weight { - // Minimum execution time: 56_868 nanoseconds. - Weight::from_ref_time(57_788_000 as u64) - .saturating_add(T::DbWeight::get().reads(3 as u64)) - .saturating_add(T::DbWeight::get().writes(3 as u64)) - } - // Storage: Democracy DepositOf (r:1 w:1) + // Proof Size summary in bytes: + // Measured: `4864` + // Estimated: `23409` + // Minimum execution time: 34_509 nanoseconds. + Weight::from_parts(34_781_000, 23409) + .saturating_add(T::DbWeight::get().reads(3_u64)) + .saturating_add(T::DbWeight::get().writes(3_u64)) + } + /// Storage: Democracy DepositOf (r:1 w:1) + /// Proof: Democracy DepositOf (max_values: None, max_size: Some(3230), added: 5705, mode: MaxEncodedLen) fn second() -> Weight { - // Minimum execution time: 49_328 nanoseconds. - Weight::from_ref_time(49_764_000 as u64) - .saturating_add(T::DbWeight::get().reads(1 as u64)) - .saturating_add(T::DbWeight::get().writes(1 as u64)) - } - // Storage: Democracy ReferendumInfoOf (r:1 w:1) - // Storage: Democracy VotingOf (r:1 w:1) - // Storage: Balances Locks (r:1 w:1) + // Proof Size summary in bytes: + // Measured: `3620` + // Estimated: `5705` + // Minimum execution time: 31_151 nanoseconds. + Weight::from_parts(31_566_000, 5705) + .saturating_add(T::DbWeight::get().reads(1_u64)) + .saturating_add(T::DbWeight::get().writes(1_u64)) + } + /// Storage: Democracy ReferendumInfoOf (r:1 w:1) + /// Proof: Democracy ReferendumInfoOf (max_values: None, max_size: Some(201), added: 2676, mode: MaxEncodedLen) + /// Storage: Democracy VotingOf (r:1 w:1) + /// Proof: Democracy VotingOf (max_values: None, max_size: Some(3795), added: 6270, mode: MaxEncodedLen) + /// Storage: Balances Locks (r:1 w:1) + /// Proof: Balances Locks (max_values: None, max_size: Some(1299), added: 3774, mode: MaxEncodedLen) fn vote_new() -> Weight { - // Minimum execution time: 60_323 nanoseconds. - Weight::from_ref_time(61_389_000 as u64) - .saturating_add(T::DbWeight::get().reads(3 as u64)) - .saturating_add(T::DbWeight::get().writes(3 as u64)) - } - // Storage: Democracy ReferendumInfoOf (r:1 w:1) - // Storage: Democracy VotingOf (r:1 w:1) - // Storage: Balances Locks (r:1 w:1) + // Proof Size summary in bytes: + // Measured: `3555` + // Estimated: `12720` + // Minimum execution time: 42_618 nanoseconds. + Weight::from_parts(43_231_000, 12720) + .saturating_add(T::DbWeight::get().reads(3_u64)) + .saturating_add(T::DbWeight::get().writes(3_u64)) + } + /// Storage: Democracy ReferendumInfoOf (r:1 w:1) + /// Proof: Democracy ReferendumInfoOf (max_values: None, max_size: Some(201), added: 2676, mode: MaxEncodedLen) + /// Storage: Democracy VotingOf (r:1 w:1) + /// Proof: Democracy VotingOf (max_values: None, max_size: Some(3795), added: 6270, mode: MaxEncodedLen) + /// Storage: Balances Locks (r:1 w:1) + /// Proof: Balances Locks (max_values: None, max_size: Some(1299), added: 3774, mode: MaxEncodedLen) fn vote_existing() -> Weight { - // Minimum execution time: 60_612 nanoseconds. - Weight::from_ref_time(61_282_000 as u64) - .saturating_add(T::DbWeight::get().reads(3 as u64)) - .saturating_add(T::DbWeight::get().writes(3 as u64)) - } - // Storage: Democracy ReferendumInfoOf (r:1 w:1) - // Storage: Democracy Cancellations (r:1 w:1) + // Proof Size summary in bytes: + // Measured: `3577` + // Estimated: `12720` + // Minimum execution time: 42_875 nanoseconds. + Weight::from_parts(43_338_000, 12720) + .saturating_add(T::DbWeight::get().reads(3_u64)) + .saturating_add(T::DbWeight::get().writes(3_u64)) + } + /// Storage: Democracy ReferendumInfoOf (r:1 w:1) + /// Proof: Democracy ReferendumInfoOf (max_values: None, max_size: Some(201), added: 2676, mode: MaxEncodedLen) + /// Storage: Democracy Cancellations (r:1 w:1) + /// Proof: Democracy Cancellations (max_values: None, max_size: Some(33), added: 2508, mode: MaxEncodedLen) fn emergency_cancel() -> Weight { - // Minimum execution time: 24_780 nanoseconds. - Weight::from_ref_time(25_194_000 as u64) - .saturating_add(T::DbWeight::get().reads(2 as u64)) - .saturating_add(T::DbWeight::get().writes(2 as u64)) - } - // Storage: Democracy PublicProps (r:1 w:1) - // Storage: Democracy DepositOf (r:1 w:1) - // Storage: System Account (r:1 w:1) - // Storage: Democracy NextExternal (r:1 w:1) - // Storage: Democracy ReferendumInfoOf (r:1 w:1) - // Storage: Democracy Blacklist (r:0 w:1) + // Proof Size summary in bytes: + // Measured: `320` + // Estimated: `5184` + // Minimum execution time: 16_543 nanoseconds. + Weight::from_parts(16_762_000, 5184) + .saturating_add(T::DbWeight::get().reads(2_u64)) + .saturating_add(T::DbWeight::get().writes(2_u64)) + } + /// Storage: Democracy PublicProps (r:1 w:1) + /// Proof: Democracy PublicProps (max_values: Some(1), max_size: Some(16702), added: 17197, mode: MaxEncodedLen) + /// Storage: Democracy DepositOf (r:1 w:1) + /// Proof: Democracy DepositOf (max_values: None, max_size: Some(3230), added: 5705, mode: MaxEncodedLen) + /// Storage: System Account (r:1 w:1) + /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) + /// Storage: Democracy NextExternal (r:1 w:1) + /// Proof: Democracy NextExternal (max_values: Some(1), max_size: Some(132), added: 627, mode: MaxEncodedLen) + /// Storage: Democracy ReferendumInfoOf (r:1 w:1) + /// Proof: Democracy ReferendumInfoOf (max_values: None, max_size: Some(201), added: 2676, mode: MaxEncodedLen) + /// Storage: Democracy Blacklist (r:0 w:1) + /// Proof: Democracy Blacklist (max_values: None, max_size: Some(3238), added: 5713, mode: MaxEncodedLen) fn blacklist() -> Weight { - // Minimum execution time: 85_177 nanoseconds. - Weight::from_ref_time(91_733_000 as u64) - .saturating_add(T::DbWeight::get().reads(5 as u64)) - .saturating_add(T::DbWeight::get().writes(6 as u64)) - } - // Storage: Democracy NextExternal (r:1 w:1) - // Storage: Democracy Blacklist (r:1 w:0) + // Proof Size summary in bytes: + // Measured: `5958` + // Estimated: `28808` + // Minimum execution time: 70_135 nanoseconds. + Weight::from_parts(70_616_000, 28808) + .saturating_add(T::DbWeight::get().reads(5_u64)) + .saturating_add(T::DbWeight::get().writes(6_u64)) + } + /// Storage: Democracy NextExternal (r:1 w:1) + /// Proof: Democracy NextExternal (max_values: Some(1), max_size: Some(132), added: 627, mode: MaxEncodedLen) + /// Storage: Democracy Blacklist (r:1 w:0) + /// Proof: Democracy Blacklist (max_values: None, max_size: Some(3238), added: 5713, mode: MaxEncodedLen) fn external_propose() -> Weight { - // Minimum execution time: 19_483 nanoseconds. - Weight::from_ref_time(19_914_000 as u64) - .saturating_add(T::DbWeight::get().reads(2 as u64)) - .saturating_add(T::DbWeight::get().writes(1 as u64)) - } - // Storage: Democracy NextExternal (r:0 w:1) + // Proof Size summary in bytes: + // Measured: `3448` + // Estimated: `6340` + // Minimum execution time: 12_580 nanoseconds. + Weight::from_parts(12_987_000, 6340) + .saturating_add(T::DbWeight::get().reads(2_u64)) + .saturating_add(T::DbWeight::get().writes(1_u64)) + } + /// Storage: Democracy NextExternal (r:0 w:1) + /// Proof: Democracy NextExternal (max_values: Some(1), max_size: Some(132), added: 627, mode: MaxEncodedLen) fn external_propose_majority() -> Weight { - // Minimum execution time: 4_963 nanoseconds. - Weight::from_ref_time(5_250_000 as u64) - .saturating_add(T::DbWeight::get().writes(1 as u64)) - } - // Storage: Democracy NextExternal (r:0 w:1) + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 3_320 nanoseconds. + Weight::from_ref_time(3_513_000) + .saturating_add(T::DbWeight::get().writes(1_u64)) + } + /// Storage: Democracy NextExternal (r:0 w:1) + /// Proof: Democracy NextExternal (max_values: Some(1), max_size: Some(132), added: 627, mode: MaxEncodedLen) fn external_propose_default() -> Weight { - // Minimum execution time: 5_075 nanoseconds. - Weight::from_ref_time(5_187_000 as u64) - .saturating_add(T::DbWeight::get().writes(1 as u64)) - } - // Storage: Democracy NextExternal (r:1 w:1) - // Storage: Democracy ReferendumCount (r:1 w:1) - // Storage: Democracy ReferendumInfoOf (r:0 w:1) + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 3_407 nanoseconds. + Weight::from_ref_time(3_565_000) + .saturating_add(T::DbWeight::get().writes(1_u64)) + } + /// Storage: Democracy NextExternal (r:1 w:1) + /// Proof: Democracy NextExternal (max_values: Some(1), max_size: Some(132), added: 627, mode: MaxEncodedLen) + /// Storage: Democracy ReferendumCount (r:1 w:1) + /// Proof: Democracy ReferendumCount (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) + /// Storage: Democracy ReferendumInfoOf (r:0 w:1) + /// Proof: Democracy ReferendumInfoOf (max_values: None, max_size: Some(201), added: 2676, mode: MaxEncodedLen) fn fast_track() -> Weight { - // Minimum execution time: 23_956 nanoseconds. - Weight::from_ref_time(24_814_000 as u64) - .saturating_add(T::DbWeight::get().reads(2 as u64)) - .saturating_add(T::DbWeight::get().writes(3 as u64)) - } - // Storage: Democracy NextExternal (r:1 w:1) - // Storage: Democracy Blacklist (r:1 w:1) + // Proof Size summary in bytes: + // Measured: `212` + // Estimated: `1126` + // Minimum execution time: 16_831 nanoseconds. + Weight::from_parts(17_155_000, 1126) + .saturating_add(T::DbWeight::get().reads(2_u64)) + .saturating_add(T::DbWeight::get().writes(3_u64)) + } + /// Storage: Democracy NextExternal (r:1 w:1) + /// Proof: Democracy NextExternal (max_values: Some(1), max_size: Some(132), added: 627, mode: MaxEncodedLen) + /// Storage: Democracy Blacklist (r:1 w:1) + /// Proof: Democracy Blacklist (max_values: None, max_size: Some(3238), added: 5713, mode: MaxEncodedLen) fn veto_external() -> Weight { - // Minimum execution time: 31_472 nanoseconds. - Weight::from_ref_time(31_770_000 as u64) - .saturating_add(T::DbWeight::get().reads(2 as u64)) - .saturating_add(T::DbWeight::get().writes(2 as u64)) - } - // Storage: Democracy PublicProps (r:1 w:1) - // Storage: Democracy DepositOf (r:1 w:1) - // Storage: System Account (r:1 w:1) + // Proof Size summary in bytes: + // Measured: `3477` + // Estimated: `6340` + // Minimum execution time: 22_072 nanoseconds. + Weight::from_parts(22_517_000, 6340) + .saturating_add(T::DbWeight::get().reads(2_u64)) + .saturating_add(T::DbWeight::get().writes(2_u64)) + } + /// Storage: Democracy PublicProps (r:1 w:1) + /// Proof: Democracy PublicProps (max_values: Some(1), max_size: Some(16702), added: 17197, mode: MaxEncodedLen) + /// Storage: Democracy DepositOf (r:1 w:1) + /// Proof: Democracy DepositOf (max_values: None, max_size: Some(3230), added: 5705, mode: MaxEncodedLen) + /// Storage: System Account (r:1 w:1) + /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) fn cancel_proposal() -> Weight { - // Minimum execution time: 73_811 nanoseconds. - Weight::from_ref_time(78_943_000 as u64) - .saturating_add(T::DbWeight::get().reads(3 as u64)) - .saturating_add(T::DbWeight::get().writes(3 as u64)) - } - // Storage: Democracy ReferendumInfoOf (r:0 w:1) + // Proof Size summary in bytes: + // Measured: `5837` + // Estimated: `25505` + // Minimum execution time: 56_925 nanoseconds. + Weight::from_parts(57_253_000, 25505) + .saturating_add(T::DbWeight::get().reads(3_u64)) + .saturating_add(T::DbWeight::get().writes(3_u64)) + } + /// Storage: Democracy ReferendumInfoOf (r:0 w:1) + /// Proof: Democracy ReferendumInfoOf (max_values: None, max_size: Some(201), added: 2676, mode: MaxEncodedLen) fn cancel_referendum() -> Weight { - // Minimum execution time: 16_074 nanoseconds. - Weight::from_ref_time(16_409_000 as u64) - .saturating_add(T::DbWeight::get().writes(1 as u64)) - } - // Storage: Democracy LowestUnbaked (r:1 w:1) - // Storage: Democracy ReferendumCount (r:1 w:0) - // Storage: Democracy ReferendumInfoOf (r:2 w:0) + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 8_582 nanoseconds. + Weight::from_ref_time(8_754_000) + .saturating_add(T::DbWeight::get().writes(1_u64)) + } + /// Storage: Democracy LowestUnbaked (r:1 w:1) + /// Proof: Democracy LowestUnbaked (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) + /// Storage: Democracy ReferendumCount (r:1 w:0) + /// Proof: Democracy ReferendumCount (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) + /// Storage: Democracy ReferendumInfoOf (r:99 w:0) + /// Proof: Democracy ReferendumInfoOf (max_values: None, max_size: Some(201), added: 2676, mode: MaxEncodedLen) /// The range of component `r` is `[0, 99]`. fn on_initialize_base(r: u32, ) -> Weight { - // Minimum execution time: 7_430 nanoseconds. - Weight::from_ref_time(12_086_064 as u64) - // Standard Error: 3_474 - .saturating_add(Weight::from_ref_time(2_283_457 as u64).saturating_mul(r as u64)) - .saturating_add(T::DbWeight::get().reads(2 as u64)) - .saturating_add(T::DbWeight::get().reads((1 as u64).saturating_mul(r as u64))) - .saturating_add(T::DbWeight::get().writes(1 as u64)) - } - // Storage: Democracy LowestUnbaked (r:1 w:1) - // Storage: Democracy ReferendumCount (r:1 w:0) - // Storage: Democracy LastTabledWasExternal (r:1 w:0) - // Storage: Democracy NextExternal (r:1 w:0) - // Storage: Democracy PublicProps (r:1 w:0) - // Storage: Democracy ReferendumInfoOf (r:2 w:0) + // Proof Size summary in bytes: + // Measured: `207 + r * (117 ±0)` + // Estimated: `998 + r * (2676 ±0)` + // Minimum execution time: 6_665 nanoseconds. + Weight::from_parts(9_219_932, 998) + // Standard Error: 4_236 + .saturating_add(Weight::from_ref_time(2_194_623).saturating_mul(r.into())) + .saturating_add(T::DbWeight::get().reads(2_u64)) + .saturating_add(T::DbWeight::get().reads((1_u64).saturating_mul(r.into()))) + .saturating_add(T::DbWeight::get().writes(1_u64)) + .saturating_add(Weight::from_proof_size(2676).saturating_mul(r.into())) + } + /// Storage: Democracy LowestUnbaked (r:1 w:1) + /// Proof: Democracy LowestUnbaked (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) + /// Storage: Democracy ReferendumCount (r:1 w:0) + /// Proof: Democracy ReferendumCount (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) + /// Storage: Democracy LastTabledWasExternal (r:1 w:0) + /// Proof: Democracy LastTabledWasExternal (max_values: Some(1), max_size: Some(1), added: 496, mode: MaxEncodedLen) + /// Storage: Democracy NextExternal (r:1 w:0) + /// Proof: Democracy NextExternal (max_values: Some(1), max_size: Some(132), added: 627, mode: MaxEncodedLen) + /// Storage: Democracy PublicProps (r:1 w:0) + /// Proof: Democracy PublicProps (max_values: Some(1), max_size: Some(16702), added: 17197, mode: MaxEncodedLen) + /// Storage: Democracy ReferendumInfoOf (r:99 w:0) + /// Proof: Democracy ReferendumInfoOf (max_values: None, max_size: Some(201), added: 2676, mode: MaxEncodedLen) /// The range of component `r` is `[0, 99]`. fn on_initialize_base_with_launch_period(r: u32, ) -> Weight { - // Minimum execution time: 9_882 nanoseconds. - Weight::from_ref_time(14_566_711 as u64) - // Standard Error: 3_354 - .saturating_add(Weight::from_ref_time(2_282_038 as u64).saturating_mul(r as u64)) - .saturating_add(T::DbWeight::get().reads(5 as u64)) - .saturating_add(T::DbWeight::get().reads((1 as u64).saturating_mul(r as u64))) - .saturating_add(T::DbWeight::get().writes(1 as u64)) - } - // Storage: Democracy VotingOf (r:3 w:3) - // Storage: Balances Locks (r:1 w:1) - // Storage: Democracy ReferendumInfoOf (r:2 w:2) + // Proof Size summary in bytes: + // Measured: `207 + r * (117 ±0)` + // Estimated: `19318 + r * (2676 ±0)` + // Minimum execution time: 9_842 nanoseconds. + Weight::from_parts(11_932_535, 19318) + // Standard Error: 4_413 + .saturating_add(Weight::from_ref_time(2_199_644).saturating_mul(r.into())) + .saturating_add(T::DbWeight::get().reads(5_u64)) + .saturating_add(T::DbWeight::get().reads((1_u64).saturating_mul(r.into()))) + .saturating_add(T::DbWeight::get().writes(1_u64)) + .saturating_add(Weight::from_proof_size(2676).saturating_mul(r.into())) + } + /// Storage: Democracy VotingOf (r:3 w:3) + /// Proof: Democracy VotingOf (max_values: None, max_size: Some(3795), added: 6270, mode: MaxEncodedLen) + /// Storage: Democracy ReferendumInfoOf (r:99 w:99) + /// Proof: Democracy ReferendumInfoOf (max_values: None, max_size: Some(201), added: 2676, mode: MaxEncodedLen) + /// Storage: Balances Locks (r:1 w:1) + /// Proof: Balances Locks (max_values: None, max_size: Some(1299), added: 3774, mode: MaxEncodedLen) /// The range of component `r` is `[0, 99]`. fn delegate(r: u32, ) -> Weight { - // Minimum execution time: 48_840 nanoseconds. - Weight::from_ref_time(56_403_092 as u64) - // Standard Error: 6_093 - .saturating_add(Weight::from_ref_time(3_344_243 as u64).saturating_mul(r as u64)) - .saturating_add(T::DbWeight::get().reads(4 as u64)) - .saturating_add(T::DbWeight::get().reads((1 as u64).saturating_mul(r as u64))) - .saturating_add(T::DbWeight::get().writes(4 as u64)) - .saturating_add(T::DbWeight::get().writes((1 as u64).saturating_mul(r as u64))) - } - // Storage: Democracy VotingOf (r:2 w:2) - // Storage: Democracy ReferendumInfoOf (r:2 w:2) + // Proof Size summary in bytes: + // Measured: `948 + r * (139 ±0)` + // Estimated: `22584 + r * (2676 ±0)` + // Minimum execution time: 34_740 nanoseconds. + Weight::from_parts(38_366_374, 22584) + // Standard Error: 4_868 + .saturating_add(Weight::from_ref_time(3_286_516).saturating_mul(r.into())) + .saturating_add(T::DbWeight::get().reads(4_u64)) + .saturating_add(T::DbWeight::get().reads((1_u64).saturating_mul(r.into()))) + .saturating_add(T::DbWeight::get().writes(4_u64)) + .saturating_add(T::DbWeight::get().writes((1_u64).saturating_mul(r.into()))) + .saturating_add(Weight::from_proof_size(2676).saturating_mul(r.into())) + } + /// Storage: Democracy VotingOf (r:2 w:2) + /// Proof: Democracy VotingOf (max_values: None, max_size: Some(3795), added: 6270, mode: MaxEncodedLen) + /// Storage: Democracy ReferendumInfoOf (r:99 w:99) + /// Proof: Democracy ReferendumInfoOf (max_values: None, max_size: Some(201), added: 2676, mode: MaxEncodedLen) /// The range of component `r` is `[0, 99]`. fn undelegate(r: u32, ) -> Weight { - // Minimum execution time: 30_483 nanoseconds. - Weight::from_ref_time(32_035_405 as u64) - // Standard Error: 4_383 - .saturating_add(Weight::from_ref_time(3_347_667 as u64).saturating_mul(r as u64)) - .saturating_add(T::DbWeight::get().reads(2 as u64)) - .saturating_add(T::DbWeight::get().reads((1 as u64).saturating_mul(r as u64))) - .saturating_add(T::DbWeight::get().writes(2 as u64)) - .saturating_add(T::DbWeight::get().writes((1 as u64).saturating_mul(r as u64))) - } - // Storage: Democracy PublicProps (r:0 w:1) + // Proof Size summary in bytes: + // Measured: `547 + r * (139 ±0)` + // Estimated: `12540 + r * (2676 ±0)` + // Minimum execution time: 19_516 nanoseconds. + Weight::from_parts(21_629_605, 12540) + // Standard Error: 4_401 + .saturating_add(Weight::from_ref_time(3_238_187).saturating_mul(r.into())) + .saturating_add(T::DbWeight::get().reads(2_u64)) + .saturating_add(T::DbWeight::get().reads((1_u64).saturating_mul(r.into()))) + .saturating_add(T::DbWeight::get().writes(2_u64)) + .saturating_add(T::DbWeight::get().writes((1_u64).saturating_mul(r.into()))) + .saturating_add(Weight::from_proof_size(2676).saturating_mul(r.into())) + } + /// Storage: Democracy PublicProps (r:0 w:1) + /// Proof: Democracy PublicProps (max_values: Some(1), max_size: Some(16702), added: 17197, mode: MaxEncodedLen) fn clear_public_proposals() -> Weight { - // Minimum execution time: 6_421 nanoseconds. - Weight::from_ref_time(6_638_000 as u64) - .saturating_add(T::DbWeight::get().writes(1 as u64)) - } - // Storage: Democracy VotingOf (r:1 w:1) - // Storage: Balances Locks (r:1 w:1) - // Storage: System Account (r:1 w:1) + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 3_291 nanoseconds. + Weight::from_ref_time(3_485_000) + .saturating_add(T::DbWeight::get().writes(1_u64)) + } + /// Storage: Democracy VotingOf (r:1 w:1) + /// Proof: Democracy VotingOf (max_values: None, max_size: Some(3795), added: 6270, mode: MaxEncodedLen) + /// Storage: Balances Locks (r:1 w:1) + /// Proof: Balances Locks (max_values: None, max_size: Some(1299), added: 3774, mode: MaxEncodedLen) + /// Storage: System Account (r:1 w:1) + /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) /// The range of component `r` is `[0, 99]`. fn unlock_remove(r: u32, ) -> Weight { - // Minimum execution time: 30_291 nanoseconds. - Weight::from_ref_time(37_071_950 as u64) - // Standard Error: 1_619 - .saturating_add(Weight::from_ref_time(59_302 as u64).saturating_mul(r as u64)) - .saturating_add(T::DbWeight::get().reads(3 as u64)) - .saturating_add(T::DbWeight::get().writes(3 as u64)) - } - // Storage: Democracy VotingOf (r:1 w:1) - // Storage: Balances Locks (r:1 w:1) - // Storage: System Account (r:1 w:1) + // Proof Size summary in bytes: + // Measured: `617` + // Estimated: `12647` + // Minimum execution time: 19_357 nanoseconds. + Weight::from_parts(24_014_517, 12647) + // Standard Error: 994 + .saturating_add(Weight::from_ref_time(17_096).saturating_mul(r.into())) + .saturating_add(T::DbWeight::get().reads(3_u64)) + .saturating_add(T::DbWeight::get().writes(3_u64)) + } + /// Storage: Democracy VotingOf (r:1 w:1) + /// Proof: Democracy VotingOf (max_values: None, max_size: Some(3795), added: 6270, mode: MaxEncodedLen) + /// Storage: Balances Locks (r:1 w:1) + /// Proof: Balances Locks (max_values: None, max_size: Some(1299), added: 3774, mode: MaxEncodedLen) + /// Storage: System Account (r:1 w:1) + /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) /// The range of component `r` is `[0, 99]`. fn unlock_set(r: u32, ) -> Weight { - // Minimum execution time: 34_888 nanoseconds. - Weight::from_ref_time(36_418_789 as u64) - // Standard Error: 906 - .saturating_add(Weight::from_ref_time(109_602 as u64).saturating_mul(r as u64)) - .saturating_add(T::DbWeight::get().reads(3 as u64)) - .saturating_add(T::DbWeight::get().writes(3 as u64)) - } - // Storage: Democracy ReferendumInfoOf (r:1 w:1) - // Storage: Democracy VotingOf (r:1 w:1) + // Proof Size summary in bytes: + // Measured: `618 + r * (22 ±0)` + // Estimated: `12647` + // Minimum execution time: 22_340 nanoseconds. + Weight::from_parts(23_355_734, 12647) + // Standard Error: 548 + .saturating_add(Weight::from_ref_time(64_308).saturating_mul(r.into())) + .saturating_add(T::DbWeight::get().reads(3_u64)) + .saturating_add(T::DbWeight::get().writes(3_u64)) + } + /// Storage: Democracy ReferendumInfoOf (r:1 w:1) + /// Proof: Democracy ReferendumInfoOf (max_values: None, max_size: Some(201), added: 2676, mode: MaxEncodedLen) + /// Storage: Democracy VotingOf (r:1 w:1) + /// Proof: Democracy VotingOf (max_values: None, max_size: Some(3795), added: 6270, mode: MaxEncodedLen) /// The range of component `r` is `[1, 100]`. fn remove_vote(r: u32, ) -> Weight { - // Minimum execution time: 18_739 nanoseconds. - Weight::from_ref_time(21_004_077 as u64) - // Standard Error: 1_075 - .saturating_add(Weight::from_ref_time(116_457 as u64).saturating_mul(r as u64)) - .saturating_add(T::DbWeight::get().reads(2 as u64)) - .saturating_add(T::DbWeight::get().writes(2 as u64)) - } - // Storage: Democracy ReferendumInfoOf (r:1 w:1) - // Storage: Democracy VotingOf (r:1 w:1) + // Proof Size summary in bytes: + // Measured: `781 + r * (26 ±0)` + // Estimated: `8946` + // Minimum execution time: 14_542 nanoseconds. + Weight::from_parts(16_411_916, 8946) + // Standard Error: 839 + .saturating_add(Weight::from_ref_time(73_268).saturating_mul(r.into())) + .saturating_add(T::DbWeight::get().reads(2_u64)) + .saturating_add(T::DbWeight::get().writes(2_u64)) + } + /// Storage: Democracy ReferendumInfoOf (r:1 w:1) + /// Proof: Democracy ReferendumInfoOf (max_values: None, max_size: Some(201), added: 2676, mode: MaxEncodedLen) + /// Storage: Democracy VotingOf (r:1 w:1) + /// Proof: Democracy VotingOf (max_values: None, max_size: Some(3795), added: 6270, mode: MaxEncodedLen) /// The range of component `r` is `[1, 100]`. fn remove_other_vote(r: u32, ) -> Weight { - // Minimum execution time: 18_514 nanoseconds. - Weight::from_ref_time(21_030_667 as u64) - // Standard Error: 1_102 - .saturating_add(Weight::from_ref_time(118_039 as u64).saturating_mul(r as u64)) - .saturating_add(T::DbWeight::get().reads(2 as u64)) - .saturating_add(T::DbWeight::get().writes(2 as u64)) + // Proof Size summary in bytes: + // Measured: `781 + r * (26 ±0)` + // Estimated: `8946` + // Minimum execution time: 14_463 nanoseconds. + Weight::from_parts(16_302_901, 8946) + // Standard Error: 809 + .saturating_add(Weight::from_ref_time(73_692).saturating_mul(r.into())) + .saturating_add(T::DbWeight::get().reads(2_u64)) + .saturating_add(T::DbWeight::get().writes(2_u64)) } } // For backwards compatibility and tests impl WeightInfo for () { - // Storage: Democracy PublicPropCount (r:1 w:1) - // Storage: Democracy PublicProps (r:1 w:1) - // Storage: Democracy Blacklist (r:1 w:0) - // Storage: Democracy DepositOf (r:0 w:1) + /// Storage: Democracy PublicPropCount (r:1 w:1) + /// Proof: Democracy PublicPropCount (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) + /// Storage: Democracy PublicProps (r:1 w:1) + /// Proof: Democracy PublicProps (max_values: Some(1), max_size: Some(16702), added: 17197, mode: MaxEncodedLen) + /// Storage: Democracy Blacklist (r:1 w:0) + /// Proof: Democracy Blacklist (max_values: None, max_size: Some(3238), added: 5713, mode: MaxEncodedLen) + /// Storage: Democracy DepositOf (r:0 w:1) + /// Proof: Democracy DepositOf (max_values: None, max_size: Some(3230), added: 5705, mode: MaxEncodedLen) fn propose() -> Weight { - // Minimum execution time: 56_868 nanoseconds. - Weight::from_ref_time(57_788_000 as u64) - .saturating_add(RocksDbWeight::get().reads(3 as u64)) - .saturating_add(RocksDbWeight::get().writes(3 as u64)) - } - // Storage: Democracy DepositOf (r:1 w:1) + // Proof Size summary in bytes: + // Measured: `4864` + // Estimated: `23409` + // Minimum execution time: 34_509 nanoseconds. + Weight::from_parts(34_781_000, 23409) + .saturating_add(RocksDbWeight::get().reads(3_u64)) + .saturating_add(RocksDbWeight::get().writes(3_u64)) + } + /// Storage: Democracy DepositOf (r:1 w:1) + /// Proof: Democracy DepositOf (max_values: None, max_size: Some(3230), added: 5705, mode: MaxEncodedLen) fn second() -> Weight { - // Minimum execution time: 49_328 nanoseconds. - Weight::from_ref_time(49_764_000 as u64) - .saturating_add(RocksDbWeight::get().reads(1 as u64)) - .saturating_add(RocksDbWeight::get().writes(1 as u64)) - } - // Storage: Democracy ReferendumInfoOf (r:1 w:1) - // Storage: Democracy VotingOf (r:1 w:1) - // Storage: Balances Locks (r:1 w:1) + // Proof Size summary in bytes: + // Measured: `3620` + // Estimated: `5705` + // Minimum execution time: 31_151 nanoseconds. + Weight::from_parts(31_566_000, 5705) + .saturating_add(RocksDbWeight::get().reads(1_u64)) + .saturating_add(RocksDbWeight::get().writes(1_u64)) + } + /// Storage: Democracy ReferendumInfoOf (r:1 w:1) + /// Proof: Democracy ReferendumInfoOf (max_values: None, max_size: Some(201), added: 2676, mode: MaxEncodedLen) + /// Storage: Democracy VotingOf (r:1 w:1) + /// Proof: Democracy VotingOf (max_values: None, max_size: Some(3795), added: 6270, mode: MaxEncodedLen) + /// Storage: Balances Locks (r:1 w:1) + /// Proof: Balances Locks (max_values: None, max_size: Some(1299), added: 3774, mode: MaxEncodedLen) fn vote_new() -> Weight { - // Minimum execution time: 60_323 nanoseconds. - Weight::from_ref_time(61_389_000 as u64) - .saturating_add(RocksDbWeight::get().reads(3 as u64)) - .saturating_add(RocksDbWeight::get().writes(3 as u64)) - } - // Storage: Democracy ReferendumInfoOf (r:1 w:1) - // Storage: Democracy VotingOf (r:1 w:1) - // Storage: Balances Locks (r:1 w:1) + // Proof Size summary in bytes: + // Measured: `3555` + // Estimated: `12720` + // Minimum execution time: 42_618 nanoseconds. + Weight::from_parts(43_231_000, 12720) + .saturating_add(RocksDbWeight::get().reads(3_u64)) + .saturating_add(RocksDbWeight::get().writes(3_u64)) + } + /// Storage: Democracy ReferendumInfoOf (r:1 w:1) + /// Proof: Democracy ReferendumInfoOf (max_values: None, max_size: Some(201), added: 2676, mode: MaxEncodedLen) + /// Storage: Democracy VotingOf (r:1 w:1) + /// Proof: Democracy VotingOf (max_values: None, max_size: Some(3795), added: 6270, mode: MaxEncodedLen) + /// Storage: Balances Locks (r:1 w:1) + /// Proof: Balances Locks (max_values: None, max_size: Some(1299), added: 3774, mode: MaxEncodedLen) fn vote_existing() -> Weight { - // Minimum execution time: 60_612 nanoseconds. - Weight::from_ref_time(61_282_000 as u64) - .saturating_add(RocksDbWeight::get().reads(3 as u64)) - .saturating_add(RocksDbWeight::get().writes(3 as u64)) - } - // Storage: Democracy ReferendumInfoOf (r:1 w:1) - // Storage: Democracy Cancellations (r:1 w:1) + // Proof Size summary in bytes: + // Measured: `3577` + // Estimated: `12720` + // Minimum execution time: 42_875 nanoseconds. + Weight::from_parts(43_338_000, 12720) + .saturating_add(RocksDbWeight::get().reads(3_u64)) + .saturating_add(RocksDbWeight::get().writes(3_u64)) + } + /// Storage: Democracy ReferendumInfoOf (r:1 w:1) + /// Proof: Democracy ReferendumInfoOf (max_values: None, max_size: Some(201), added: 2676, mode: MaxEncodedLen) + /// Storage: Democracy Cancellations (r:1 w:1) + /// Proof: Democracy Cancellations (max_values: None, max_size: Some(33), added: 2508, mode: MaxEncodedLen) fn emergency_cancel() -> Weight { - // Minimum execution time: 24_780 nanoseconds. - Weight::from_ref_time(25_194_000 as u64) - .saturating_add(RocksDbWeight::get().reads(2 as u64)) - .saturating_add(RocksDbWeight::get().writes(2 as u64)) - } - // Storage: Democracy PublicProps (r:1 w:1) - // Storage: Democracy DepositOf (r:1 w:1) - // Storage: System Account (r:1 w:1) - // Storage: Democracy NextExternal (r:1 w:1) - // Storage: Democracy ReferendumInfoOf (r:1 w:1) - // Storage: Democracy Blacklist (r:0 w:1) + // Proof Size summary in bytes: + // Measured: `320` + // Estimated: `5184` + // Minimum execution time: 16_543 nanoseconds. + Weight::from_parts(16_762_000, 5184) + .saturating_add(RocksDbWeight::get().reads(2_u64)) + .saturating_add(RocksDbWeight::get().writes(2_u64)) + } + /// Storage: Democracy PublicProps (r:1 w:1) + /// Proof: Democracy PublicProps (max_values: Some(1), max_size: Some(16702), added: 17197, mode: MaxEncodedLen) + /// Storage: Democracy DepositOf (r:1 w:1) + /// Proof: Democracy DepositOf (max_values: None, max_size: Some(3230), added: 5705, mode: MaxEncodedLen) + /// Storage: System Account (r:1 w:1) + /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) + /// Storage: Democracy NextExternal (r:1 w:1) + /// Proof: Democracy NextExternal (max_values: Some(1), max_size: Some(132), added: 627, mode: MaxEncodedLen) + /// Storage: Democracy ReferendumInfoOf (r:1 w:1) + /// Proof: Democracy ReferendumInfoOf (max_values: None, max_size: Some(201), added: 2676, mode: MaxEncodedLen) + /// Storage: Democracy Blacklist (r:0 w:1) + /// Proof: Democracy Blacklist (max_values: None, max_size: Some(3238), added: 5713, mode: MaxEncodedLen) fn blacklist() -> Weight { - // Minimum execution time: 85_177 nanoseconds. - Weight::from_ref_time(91_733_000 as u64) - .saturating_add(RocksDbWeight::get().reads(5 as u64)) - .saturating_add(RocksDbWeight::get().writes(6 as u64)) - } - // Storage: Democracy NextExternal (r:1 w:1) - // Storage: Democracy Blacklist (r:1 w:0) + // Proof Size summary in bytes: + // Measured: `5958` + // Estimated: `28808` + // Minimum execution time: 70_135 nanoseconds. + Weight::from_parts(70_616_000, 28808) + .saturating_add(RocksDbWeight::get().reads(5_u64)) + .saturating_add(RocksDbWeight::get().writes(6_u64)) + } + /// Storage: Democracy NextExternal (r:1 w:1) + /// Proof: Democracy NextExternal (max_values: Some(1), max_size: Some(132), added: 627, mode: MaxEncodedLen) + /// Storage: Democracy Blacklist (r:1 w:0) + /// Proof: Democracy Blacklist (max_values: None, max_size: Some(3238), added: 5713, mode: MaxEncodedLen) fn external_propose() -> Weight { - // Minimum execution time: 19_483 nanoseconds. - Weight::from_ref_time(19_914_000 as u64) - .saturating_add(RocksDbWeight::get().reads(2 as u64)) - .saturating_add(RocksDbWeight::get().writes(1 as u64)) - } - // Storage: Democracy NextExternal (r:0 w:1) + // Proof Size summary in bytes: + // Measured: `3448` + // Estimated: `6340` + // Minimum execution time: 12_580 nanoseconds. + Weight::from_parts(12_987_000, 6340) + .saturating_add(RocksDbWeight::get().reads(2_u64)) + .saturating_add(RocksDbWeight::get().writes(1_u64)) + } + /// Storage: Democracy NextExternal (r:0 w:1) + /// Proof: Democracy NextExternal (max_values: Some(1), max_size: Some(132), added: 627, mode: MaxEncodedLen) fn external_propose_majority() -> Weight { - // Minimum execution time: 4_963 nanoseconds. - Weight::from_ref_time(5_250_000 as u64) - .saturating_add(RocksDbWeight::get().writes(1 as u64)) - } - // Storage: Democracy NextExternal (r:0 w:1) + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 3_320 nanoseconds. + Weight::from_ref_time(3_513_000) + .saturating_add(RocksDbWeight::get().writes(1_u64)) + } + /// Storage: Democracy NextExternal (r:0 w:1) + /// Proof: Democracy NextExternal (max_values: Some(1), max_size: Some(132), added: 627, mode: MaxEncodedLen) fn external_propose_default() -> Weight { - // Minimum execution time: 5_075 nanoseconds. - Weight::from_ref_time(5_187_000 as u64) - .saturating_add(RocksDbWeight::get().writes(1 as u64)) - } - // Storage: Democracy NextExternal (r:1 w:1) - // Storage: Democracy ReferendumCount (r:1 w:1) - // Storage: Democracy ReferendumInfoOf (r:0 w:1) + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 3_407 nanoseconds. + Weight::from_ref_time(3_565_000) + .saturating_add(RocksDbWeight::get().writes(1_u64)) + } + /// Storage: Democracy NextExternal (r:1 w:1) + /// Proof: Democracy NextExternal (max_values: Some(1), max_size: Some(132), added: 627, mode: MaxEncodedLen) + /// Storage: Democracy ReferendumCount (r:1 w:1) + /// Proof: Democracy ReferendumCount (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) + /// Storage: Democracy ReferendumInfoOf (r:0 w:1) + /// Proof: Democracy ReferendumInfoOf (max_values: None, max_size: Some(201), added: 2676, mode: MaxEncodedLen) fn fast_track() -> Weight { - // Minimum execution time: 23_956 nanoseconds. - Weight::from_ref_time(24_814_000 as u64) - .saturating_add(RocksDbWeight::get().reads(2 as u64)) - .saturating_add(RocksDbWeight::get().writes(3 as u64)) - } - // Storage: Democracy NextExternal (r:1 w:1) - // Storage: Democracy Blacklist (r:1 w:1) + // Proof Size summary in bytes: + // Measured: `212` + // Estimated: `1126` + // Minimum execution time: 16_831 nanoseconds. + Weight::from_parts(17_155_000, 1126) + .saturating_add(RocksDbWeight::get().reads(2_u64)) + .saturating_add(RocksDbWeight::get().writes(3_u64)) + } + /// Storage: Democracy NextExternal (r:1 w:1) + /// Proof: Democracy NextExternal (max_values: Some(1), max_size: Some(132), added: 627, mode: MaxEncodedLen) + /// Storage: Democracy Blacklist (r:1 w:1) + /// Proof: Democracy Blacklist (max_values: None, max_size: Some(3238), added: 5713, mode: MaxEncodedLen) fn veto_external() -> Weight { - // Minimum execution time: 31_472 nanoseconds. - Weight::from_ref_time(31_770_000 as u64) - .saturating_add(RocksDbWeight::get().reads(2 as u64)) - .saturating_add(RocksDbWeight::get().writes(2 as u64)) - } - // Storage: Democracy PublicProps (r:1 w:1) - // Storage: Democracy DepositOf (r:1 w:1) - // Storage: System Account (r:1 w:1) + // Proof Size summary in bytes: + // Measured: `3477` + // Estimated: `6340` + // Minimum execution time: 22_072 nanoseconds. + Weight::from_parts(22_517_000, 6340) + .saturating_add(RocksDbWeight::get().reads(2_u64)) + .saturating_add(RocksDbWeight::get().writes(2_u64)) + } + /// Storage: Democracy PublicProps (r:1 w:1) + /// Proof: Democracy PublicProps (max_values: Some(1), max_size: Some(16702), added: 17197, mode: MaxEncodedLen) + /// Storage: Democracy DepositOf (r:1 w:1) + /// Proof: Democracy DepositOf (max_values: None, max_size: Some(3230), added: 5705, mode: MaxEncodedLen) + /// Storage: System Account (r:1 w:1) + /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) fn cancel_proposal() -> Weight { - // Minimum execution time: 73_811 nanoseconds. - Weight::from_ref_time(78_943_000 as u64) - .saturating_add(RocksDbWeight::get().reads(3 as u64)) - .saturating_add(RocksDbWeight::get().writes(3 as u64)) - } - // Storage: Democracy ReferendumInfoOf (r:0 w:1) + // Proof Size summary in bytes: + // Measured: `5837` + // Estimated: `25505` + // Minimum execution time: 56_925 nanoseconds. + Weight::from_parts(57_253_000, 25505) + .saturating_add(RocksDbWeight::get().reads(3_u64)) + .saturating_add(RocksDbWeight::get().writes(3_u64)) + } + /// Storage: Democracy ReferendumInfoOf (r:0 w:1) + /// Proof: Democracy ReferendumInfoOf (max_values: None, max_size: Some(201), added: 2676, mode: MaxEncodedLen) fn cancel_referendum() -> Weight { - // Minimum execution time: 16_074 nanoseconds. - Weight::from_ref_time(16_409_000 as u64) - .saturating_add(RocksDbWeight::get().writes(1 as u64)) - } - // Storage: Democracy LowestUnbaked (r:1 w:1) - // Storage: Democracy ReferendumCount (r:1 w:0) - // Storage: Democracy ReferendumInfoOf (r:2 w:0) + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 8_582 nanoseconds. + Weight::from_ref_time(8_754_000) + .saturating_add(RocksDbWeight::get().writes(1_u64)) + } + /// Storage: Democracy LowestUnbaked (r:1 w:1) + /// Proof: Democracy LowestUnbaked (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) + /// Storage: Democracy ReferendumCount (r:1 w:0) + /// Proof: Democracy ReferendumCount (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) + /// Storage: Democracy ReferendumInfoOf (r:99 w:0) + /// Proof: Democracy ReferendumInfoOf (max_values: None, max_size: Some(201), added: 2676, mode: MaxEncodedLen) /// The range of component `r` is `[0, 99]`. fn on_initialize_base(r: u32, ) -> Weight { - // Minimum execution time: 7_430 nanoseconds. - Weight::from_ref_time(12_086_064 as u64) - // Standard Error: 3_474 - .saturating_add(Weight::from_ref_time(2_283_457 as u64).saturating_mul(r as u64)) - .saturating_add(RocksDbWeight::get().reads(2 as u64)) - .saturating_add(RocksDbWeight::get().reads((1 as u64).saturating_mul(r as u64))) - .saturating_add(RocksDbWeight::get().writes(1 as u64)) - } - // Storage: Democracy LowestUnbaked (r:1 w:1) - // Storage: Democracy ReferendumCount (r:1 w:0) - // Storage: Democracy LastTabledWasExternal (r:1 w:0) - // Storage: Democracy NextExternal (r:1 w:0) - // Storage: Democracy PublicProps (r:1 w:0) - // Storage: Democracy ReferendumInfoOf (r:2 w:0) + // Proof Size summary in bytes: + // Measured: `207 + r * (117 ±0)` + // Estimated: `998 + r * (2676 ±0)` + // Minimum execution time: 6_665 nanoseconds. + Weight::from_parts(9_219_932, 998) + // Standard Error: 4_236 + .saturating_add(Weight::from_ref_time(2_194_623).saturating_mul(r.into())) + .saturating_add(RocksDbWeight::get().reads(2_u64)) + .saturating_add(RocksDbWeight::get().reads((1_u64).saturating_mul(r.into()))) + .saturating_add(RocksDbWeight::get().writes(1_u64)) + .saturating_add(Weight::from_proof_size(2676).saturating_mul(r.into())) + } + /// Storage: Democracy LowestUnbaked (r:1 w:1) + /// Proof: Democracy LowestUnbaked (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) + /// Storage: Democracy ReferendumCount (r:1 w:0) + /// Proof: Democracy ReferendumCount (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) + /// Storage: Democracy LastTabledWasExternal (r:1 w:0) + /// Proof: Democracy LastTabledWasExternal (max_values: Some(1), max_size: Some(1), added: 496, mode: MaxEncodedLen) + /// Storage: Democracy NextExternal (r:1 w:0) + /// Proof: Democracy NextExternal (max_values: Some(1), max_size: Some(132), added: 627, mode: MaxEncodedLen) + /// Storage: Democracy PublicProps (r:1 w:0) + /// Proof: Democracy PublicProps (max_values: Some(1), max_size: Some(16702), added: 17197, mode: MaxEncodedLen) + /// Storage: Democracy ReferendumInfoOf (r:99 w:0) + /// Proof: Democracy ReferendumInfoOf (max_values: None, max_size: Some(201), added: 2676, mode: MaxEncodedLen) /// The range of component `r` is `[0, 99]`. fn on_initialize_base_with_launch_period(r: u32, ) -> Weight { - // Minimum execution time: 9_882 nanoseconds. - Weight::from_ref_time(14_566_711 as u64) - // Standard Error: 3_354 - .saturating_add(Weight::from_ref_time(2_282_038 as u64).saturating_mul(r as u64)) - .saturating_add(RocksDbWeight::get().reads(5 as u64)) - .saturating_add(RocksDbWeight::get().reads((1 as u64).saturating_mul(r as u64))) - .saturating_add(RocksDbWeight::get().writes(1 as u64)) - } - // Storage: Democracy VotingOf (r:3 w:3) - // Storage: Balances Locks (r:1 w:1) - // Storage: Democracy ReferendumInfoOf (r:2 w:2) + // Proof Size summary in bytes: + // Measured: `207 + r * (117 ±0)` + // Estimated: `19318 + r * (2676 ±0)` + // Minimum execution time: 9_842 nanoseconds. + Weight::from_parts(11_932_535, 19318) + // Standard Error: 4_413 + .saturating_add(Weight::from_ref_time(2_199_644).saturating_mul(r.into())) + .saturating_add(RocksDbWeight::get().reads(5_u64)) + .saturating_add(RocksDbWeight::get().reads((1_u64).saturating_mul(r.into()))) + .saturating_add(RocksDbWeight::get().writes(1_u64)) + .saturating_add(Weight::from_proof_size(2676).saturating_mul(r.into())) + } + /// Storage: Democracy VotingOf (r:3 w:3) + /// Proof: Democracy VotingOf (max_values: None, max_size: Some(3795), added: 6270, mode: MaxEncodedLen) + /// Storage: Democracy ReferendumInfoOf (r:99 w:99) + /// Proof: Democracy ReferendumInfoOf (max_values: None, max_size: Some(201), added: 2676, mode: MaxEncodedLen) + /// Storage: Balances Locks (r:1 w:1) + /// Proof: Balances Locks (max_values: None, max_size: Some(1299), added: 3774, mode: MaxEncodedLen) /// The range of component `r` is `[0, 99]`. fn delegate(r: u32, ) -> Weight { - // Minimum execution time: 48_840 nanoseconds. - Weight::from_ref_time(56_403_092 as u64) - // Standard Error: 6_093 - .saturating_add(Weight::from_ref_time(3_344_243 as u64).saturating_mul(r as u64)) - .saturating_add(RocksDbWeight::get().reads(4 as u64)) - .saturating_add(RocksDbWeight::get().reads((1 as u64).saturating_mul(r as u64))) - .saturating_add(RocksDbWeight::get().writes(4 as u64)) - .saturating_add(RocksDbWeight::get().writes((1 as u64).saturating_mul(r as u64))) - } - // Storage: Democracy VotingOf (r:2 w:2) - // Storage: Democracy ReferendumInfoOf (r:2 w:2) + // Proof Size summary in bytes: + // Measured: `948 + r * (139 ±0)` + // Estimated: `22584 + r * (2676 ±0)` + // Minimum execution time: 34_740 nanoseconds. + Weight::from_parts(38_366_374, 22584) + // Standard Error: 4_868 + .saturating_add(Weight::from_ref_time(3_286_516).saturating_mul(r.into())) + .saturating_add(RocksDbWeight::get().reads(4_u64)) + .saturating_add(RocksDbWeight::get().reads((1_u64).saturating_mul(r.into()))) + .saturating_add(RocksDbWeight::get().writes(4_u64)) + .saturating_add(RocksDbWeight::get().writes((1_u64).saturating_mul(r.into()))) + .saturating_add(Weight::from_proof_size(2676).saturating_mul(r.into())) + } + /// Storage: Democracy VotingOf (r:2 w:2) + /// Proof: Democracy VotingOf (max_values: None, max_size: Some(3795), added: 6270, mode: MaxEncodedLen) + /// Storage: Democracy ReferendumInfoOf (r:99 w:99) + /// Proof: Democracy ReferendumInfoOf (max_values: None, max_size: Some(201), added: 2676, mode: MaxEncodedLen) /// The range of component `r` is `[0, 99]`. fn undelegate(r: u32, ) -> Weight { - // Minimum execution time: 30_483 nanoseconds. - Weight::from_ref_time(32_035_405 as u64) - // Standard Error: 4_383 - .saturating_add(Weight::from_ref_time(3_347_667 as u64).saturating_mul(r as u64)) - .saturating_add(RocksDbWeight::get().reads(2 as u64)) - .saturating_add(RocksDbWeight::get().reads((1 as u64).saturating_mul(r as u64))) - .saturating_add(RocksDbWeight::get().writes(2 as u64)) - .saturating_add(RocksDbWeight::get().writes((1 as u64).saturating_mul(r as u64))) - } - // Storage: Democracy PublicProps (r:0 w:1) + // Proof Size summary in bytes: + // Measured: `547 + r * (139 ±0)` + // Estimated: `12540 + r * (2676 ±0)` + // Minimum execution time: 19_516 nanoseconds. + Weight::from_parts(21_629_605, 12540) + // Standard Error: 4_401 + .saturating_add(Weight::from_ref_time(3_238_187).saturating_mul(r.into())) + .saturating_add(RocksDbWeight::get().reads(2_u64)) + .saturating_add(RocksDbWeight::get().reads((1_u64).saturating_mul(r.into()))) + .saturating_add(RocksDbWeight::get().writes(2_u64)) + .saturating_add(RocksDbWeight::get().writes((1_u64).saturating_mul(r.into()))) + .saturating_add(Weight::from_proof_size(2676).saturating_mul(r.into())) + } + /// Storage: Democracy PublicProps (r:0 w:1) + /// Proof: Democracy PublicProps (max_values: Some(1), max_size: Some(16702), added: 17197, mode: MaxEncodedLen) fn clear_public_proposals() -> Weight { - // Minimum execution time: 6_421 nanoseconds. - Weight::from_ref_time(6_638_000 as u64) - .saturating_add(RocksDbWeight::get().writes(1 as u64)) - } - // Storage: Democracy VotingOf (r:1 w:1) - // Storage: Balances Locks (r:1 w:1) - // Storage: System Account (r:1 w:1) + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 3_291 nanoseconds. + Weight::from_ref_time(3_485_000) + .saturating_add(RocksDbWeight::get().writes(1_u64)) + } + /// Storage: Democracy VotingOf (r:1 w:1) + /// Proof: Democracy VotingOf (max_values: None, max_size: Some(3795), added: 6270, mode: MaxEncodedLen) + /// Storage: Balances Locks (r:1 w:1) + /// Proof: Balances Locks (max_values: None, max_size: Some(1299), added: 3774, mode: MaxEncodedLen) + /// Storage: System Account (r:1 w:1) + /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) /// The range of component `r` is `[0, 99]`. fn unlock_remove(r: u32, ) -> Weight { - // Minimum execution time: 30_291 nanoseconds. - Weight::from_ref_time(37_071_950 as u64) - // Standard Error: 1_619 - .saturating_add(Weight::from_ref_time(59_302 as u64).saturating_mul(r as u64)) - .saturating_add(RocksDbWeight::get().reads(3 as u64)) - .saturating_add(RocksDbWeight::get().writes(3 as u64)) - } - // Storage: Democracy VotingOf (r:1 w:1) - // Storage: Balances Locks (r:1 w:1) - // Storage: System Account (r:1 w:1) + // Proof Size summary in bytes: + // Measured: `617` + // Estimated: `12647` + // Minimum execution time: 19_357 nanoseconds. + Weight::from_parts(24_014_517, 12647) + // Standard Error: 994 + .saturating_add(Weight::from_ref_time(17_096).saturating_mul(r.into())) + .saturating_add(RocksDbWeight::get().reads(3_u64)) + .saturating_add(RocksDbWeight::get().writes(3_u64)) + } + /// Storage: Democracy VotingOf (r:1 w:1) + /// Proof: Democracy VotingOf (max_values: None, max_size: Some(3795), added: 6270, mode: MaxEncodedLen) + /// Storage: Balances Locks (r:1 w:1) + /// Proof: Balances Locks (max_values: None, max_size: Some(1299), added: 3774, mode: MaxEncodedLen) + /// Storage: System Account (r:1 w:1) + /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) /// The range of component `r` is `[0, 99]`. fn unlock_set(r: u32, ) -> Weight { - // Minimum execution time: 34_888 nanoseconds. - Weight::from_ref_time(36_418_789 as u64) - // Standard Error: 906 - .saturating_add(Weight::from_ref_time(109_602 as u64).saturating_mul(r as u64)) - .saturating_add(RocksDbWeight::get().reads(3 as u64)) - .saturating_add(RocksDbWeight::get().writes(3 as u64)) - } - // Storage: Democracy ReferendumInfoOf (r:1 w:1) - // Storage: Democracy VotingOf (r:1 w:1) + // Proof Size summary in bytes: + // Measured: `618 + r * (22 ±0)` + // Estimated: `12647` + // Minimum execution time: 22_340 nanoseconds. + Weight::from_parts(23_355_734, 12647) + // Standard Error: 548 + .saturating_add(Weight::from_ref_time(64_308).saturating_mul(r.into())) + .saturating_add(RocksDbWeight::get().reads(3_u64)) + .saturating_add(RocksDbWeight::get().writes(3_u64)) + } + /// Storage: Democracy ReferendumInfoOf (r:1 w:1) + /// Proof: Democracy ReferendumInfoOf (max_values: None, max_size: Some(201), added: 2676, mode: MaxEncodedLen) + /// Storage: Democracy VotingOf (r:1 w:1) + /// Proof: Democracy VotingOf (max_values: None, max_size: Some(3795), added: 6270, mode: MaxEncodedLen) /// The range of component `r` is `[1, 100]`. fn remove_vote(r: u32, ) -> Weight { - // Minimum execution time: 18_739 nanoseconds. - Weight::from_ref_time(21_004_077 as u64) - // Standard Error: 1_075 - .saturating_add(Weight::from_ref_time(116_457 as u64).saturating_mul(r as u64)) - .saturating_add(RocksDbWeight::get().reads(2 as u64)) - .saturating_add(RocksDbWeight::get().writes(2 as u64)) - } - // Storage: Democracy ReferendumInfoOf (r:1 w:1) - // Storage: Democracy VotingOf (r:1 w:1) + // Proof Size summary in bytes: + // Measured: `781 + r * (26 ±0)` + // Estimated: `8946` + // Minimum execution time: 14_542 nanoseconds. + Weight::from_parts(16_411_916, 8946) + // Standard Error: 839 + .saturating_add(Weight::from_ref_time(73_268).saturating_mul(r.into())) + .saturating_add(RocksDbWeight::get().reads(2_u64)) + .saturating_add(RocksDbWeight::get().writes(2_u64)) + } + /// Storage: Democracy ReferendumInfoOf (r:1 w:1) + /// Proof: Democracy ReferendumInfoOf (max_values: None, max_size: Some(201), added: 2676, mode: MaxEncodedLen) + /// Storage: Democracy VotingOf (r:1 w:1) + /// Proof: Democracy VotingOf (max_values: None, max_size: Some(3795), added: 6270, mode: MaxEncodedLen) /// The range of component `r` is `[1, 100]`. fn remove_other_vote(r: u32, ) -> Weight { - // Minimum execution time: 18_514 nanoseconds. - Weight::from_ref_time(21_030_667 as u64) - // Standard Error: 1_102 - .saturating_add(Weight::from_ref_time(118_039 as u64).saturating_mul(r as u64)) - .saturating_add(RocksDbWeight::get().reads(2 as u64)) - .saturating_add(RocksDbWeight::get().writes(2 as u64)) + // Proof Size summary in bytes: + // Measured: `781 + r * (26 ±0)` + // Estimated: `8946` + // Minimum execution time: 14_463 nanoseconds. + Weight::from_parts(16_302_901, 8946) + // Standard Error: 809 + .saturating_add(Weight::from_ref_time(73_692).saturating_mul(r.into())) + .saturating_add(RocksDbWeight::get().reads(2_u64)) + .saturating_add(RocksDbWeight::get().writes(2_u64)) } } diff --git a/frame/election-provider-multi-phase/src/weights.rs b/frame/election-provider-multi-phase/src/weights.rs index 221fd5837..51bb7b0f1 100644 --- a/frame/election-provider-multi-phase/src/weights.rs +++ b/frame/election-provider-multi-phase/src/weights.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2022 Parity Technologies (UK) Ltd. +// Copyright (C) 2023 Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); @@ -18,7 +18,8 @@ //! Autogenerated weights for pallet_election_provider_multi_phase //! //! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev -//! DATE: 2022-11-07, STEPS: `50`, REPEAT: 20, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! DATE: 2023-01-24, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! WORST CASE MAP SIZE: `1000000` //! HOSTNAME: `bm2`, CPU: `Intel(R) Core(TM) i7-7700K CPU @ 4.20GHz` //! EXECUTION: Some(Wasm), WASM-EXECUTION: Compiled, CHAIN: Some("dev"), DB CACHE: 1024 @@ -62,262 +63,422 @@ pub trait WeightInfo { /// Weights for pallet_election_provider_multi_phase using the Substrate node and recommended hardware. pub struct SubstrateWeight(PhantomData); impl WeightInfo for SubstrateWeight { - // Storage: Staking CurrentEra (r:1 w:0) - // Storage: Staking CurrentPlannedSession (r:1 w:0) - // Storage: Staking ErasStartSessionIndex (r:1 w:0) - // Storage: Babe EpochIndex (r:1 w:0) - // Storage: Babe GenesisSlot (r:1 w:0) - // Storage: Babe CurrentSlot (r:1 w:0) - // Storage: Staking ForceEra (r:1 w:0) - // Storage: ElectionProviderMultiPhase CurrentPhase (r:1 w:0) + /// Storage: Staking CurrentEra (r:1 w:0) + /// Proof: Staking CurrentEra (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) + /// Storage: Staking CurrentPlannedSession (r:1 w:0) + /// Proof: Staking CurrentPlannedSession (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) + /// Storage: Staking ErasStartSessionIndex (r:1 w:0) + /// Proof: Staking ErasStartSessionIndex (max_values: None, max_size: Some(16), added: 2491, mode: MaxEncodedLen) + /// Storage: Babe EpochIndex (r:1 w:0) + /// Proof: Babe EpochIndex (max_values: Some(1), max_size: Some(8), added: 503, mode: MaxEncodedLen) + /// Storage: Babe GenesisSlot (r:1 w:0) + /// Proof: Babe GenesisSlot (max_values: Some(1), max_size: Some(8), added: 503, mode: MaxEncodedLen) + /// Storage: Babe CurrentSlot (r:1 w:0) + /// Proof: Babe CurrentSlot (max_values: Some(1), max_size: Some(8), added: 503, mode: MaxEncodedLen) + /// Storage: Staking ForceEra (r:1 w:0) + /// Proof: Staking ForceEra (max_values: Some(1), max_size: Some(1), added: 496, mode: MaxEncodedLen) + /// Storage: ElectionProviderMultiPhase CurrentPhase (r:1 w:0) + /// Proof Skipped: ElectionProviderMultiPhase CurrentPhase (max_values: Some(1), max_size: None, mode: Measured) fn on_initialize_nothing() -> Weight { - // Minimum execution time: 17_309 nanoseconds. - Weight::from_ref_time(17_646_000 as u64) - .saturating_add(T::DbWeight::get().reads(8 as u64)) + // Proof Size summary in bytes: + // Measured: `994` + // Estimated: `6983` + // Minimum execution time: 17_801 nanoseconds. + Weight::from_parts(18_364_000, 6983) + .saturating_add(T::DbWeight::get().reads(8_u64)) } - // Storage: ElectionProviderMultiPhase Round (r:1 w:0) - // Storage: ElectionProviderMultiPhase CurrentPhase (r:0 w:1) + /// Storage: ElectionProviderMultiPhase Round (r:1 w:0) + /// Proof Skipped: ElectionProviderMultiPhase Round (max_values: Some(1), max_size: None, mode: Measured) + /// Storage: ElectionProviderMultiPhase CurrentPhase (r:1 w:1) + /// Proof Skipped: ElectionProviderMultiPhase CurrentPhase (max_values: Some(1), max_size: None, mode: Measured) fn on_initialize_open_signed() -> Weight { - // Minimum execution time: 17_992 nanoseconds. - Weight::from_ref_time(18_426_000 as u64) - .saturating_add(T::DbWeight::get().reads(1 as u64)) - .saturating_add(T::DbWeight::get().writes(1 as u64)) + // Proof Size summary in bytes: + // Measured: `114` + // Estimated: `1218` + // Minimum execution time: 12_814 nanoseconds. + Weight::from_parts(13_154_000, 1218) + .saturating_add(T::DbWeight::get().reads(2_u64)) + .saturating_add(T::DbWeight::get().writes(1_u64)) } - // Storage: ElectionProviderMultiPhase Round (r:1 w:0) - // Storage: ElectionProviderMultiPhase CurrentPhase (r:0 w:1) + /// Storage: ElectionProviderMultiPhase Round (r:1 w:0) + /// Proof Skipped: ElectionProviderMultiPhase Round (max_values: Some(1), max_size: None, mode: Measured) + /// Storage: ElectionProviderMultiPhase CurrentPhase (r:1 w:1) + /// Proof Skipped: ElectionProviderMultiPhase CurrentPhase (max_values: Some(1), max_size: None, mode: Measured) fn on_initialize_open_unsigned() -> Weight { - // Minimum execution time: 17_340 nanoseconds. - Weight::from_ref_time(17_881_000 as u64) - .saturating_add(T::DbWeight::get().reads(1 as u64)) - .saturating_add(T::DbWeight::get().writes(1 as u64)) + // Proof Size summary in bytes: + // Measured: `114` + // Estimated: `1218` + // Minimum execution time: 14_565 nanoseconds. + Weight::from_parts(15_097_000, 1218) + .saturating_add(T::DbWeight::get().reads(2_u64)) + .saturating_add(T::DbWeight::get().writes(1_u64)) } - // Storage: System Account (r:1 w:1) - // Storage: ElectionProviderMultiPhase QueuedSolution (r:0 w:1) + /// Storage: System Account (r:1 w:1) + /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) + /// Storage: ElectionProviderMultiPhase QueuedSolution (r:0 w:1) + /// Proof Skipped: ElectionProviderMultiPhase QueuedSolution (max_values: Some(1), max_size: None, mode: Measured) fn finalize_signed_phase_accept_solution() -> Weight { - // Minimum execution time: 35_571 nanoseconds. - Weight::from_ref_time(35_989_000 as u64) - .saturating_add(T::DbWeight::get().reads(1 as u64)) - .saturating_add(T::DbWeight::get().writes(2 as u64)) + // Proof Size summary in bytes: + // Measured: `206` + // Estimated: `2809` + // Minimum execution time: 23_341 nanoseconds. + Weight::from_parts(23_770_000, 2809) + .saturating_add(T::DbWeight::get().reads(1_u64)) + .saturating_add(T::DbWeight::get().writes(2_u64)) } - // Storage: System Account (r:1 w:1) + /// Storage: System Account (r:1 w:1) + /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) fn finalize_signed_phase_reject_solution() -> Weight { - // Minimum execution time: 27_403 nanoseconds. - Weight::from_ref_time(27_879_000 as u64) - .saturating_add(T::DbWeight::get().reads(1 as u64)) - .saturating_add(T::DbWeight::get().writes(1 as u64)) + // Proof Size summary in bytes: + // Measured: `206` + // Estimated: `2603` + // Minimum execution time: 16_662 nanoseconds. + Weight::from_parts(16_898_000, 2603) + .saturating_add(T::DbWeight::get().reads(1_u64)) + .saturating_add(T::DbWeight::get().writes(1_u64)) } - // Storage: ElectionProviderMultiPhase SnapshotMetadata (r:0 w:1) - // Storage: ElectionProviderMultiPhase DesiredTargets (r:0 w:1) - // Storage: ElectionProviderMultiPhase Snapshot (r:0 w:1) + /// Storage: ElectionProviderMultiPhase SnapshotMetadata (r:0 w:1) + /// Proof Skipped: ElectionProviderMultiPhase SnapshotMetadata (max_values: Some(1), max_size: None, mode: Measured) + /// Storage: ElectionProviderMultiPhase DesiredTargets (r:0 w:1) + /// Proof Skipped: ElectionProviderMultiPhase DesiredTargets (max_values: Some(1), max_size: None, mode: Measured) + /// Storage: ElectionProviderMultiPhase Snapshot (r:0 w:1) + /// Proof Skipped: ElectionProviderMultiPhase Snapshot (max_values: Some(1), max_size: None, mode: Measured) /// The range of component `v` is `[1000, 2000]`. /// The range of component `t` is `[500, 1000]`. fn create_snapshot_internal(v: u32, _t: u32, ) -> Weight { - // Minimum execution time: 571_900 nanoseconds. - Weight::from_ref_time(589_170_000 as u64) - // Standard Error: 7_123 - .saturating_add(Weight::from_ref_time(384_767 as u64).saturating_mul(v as u64)) - .saturating_add(T::DbWeight::get().writes(3 as u64)) + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 215_168 nanoseconds. + Weight::from_ref_time(219_887_000) + // Standard Error: 1_444 + .saturating_add(Weight::from_ref_time(146_388).saturating_mul(v.into())) + .saturating_add(T::DbWeight::get().writes(3_u64)) } - // Storage: ElectionProviderMultiPhase SignedSubmissionIndices (r:1 w:1) - // Storage: ElectionProviderMultiPhase SignedSubmissionNextIndex (r:1 w:1) - // Storage: ElectionProviderMultiPhase SnapshotMetadata (r:1 w:1) - // Storage: ElectionProviderMultiPhase SignedSubmissionsMap (r:1 w:0) - // Storage: ElectionProviderMultiPhase QueuedSolution (r:1 w:1) - // Storage: ElectionProviderMultiPhase Round (r:1 w:1) - // Storage: ElectionProviderMultiPhase DesiredTargets (r:0 w:1) - // Storage: ElectionProviderMultiPhase Snapshot (r:0 w:1) - // Storage: ElectionProviderMultiPhase CurrentPhase (r:0 w:1) + /// Storage: ElectionProviderMultiPhase SignedSubmissionIndices (r:1 w:1) + /// Proof Skipped: ElectionProviderMultiPhase SignedSubmissionIndices (max_values: Some(1), max_size: None, mode: Measured) + /// Storage: ElectionProviderMultiPhase SignedSubmissionNextIndex (r:1 w:1) + /// Proof Skipped: ElectionProviderMultiPhase SignedSubmissionNextIndex (max_values: Some(1), max_size: None, mode: Measured) + /// Storage: ElectionProviderMultiPhase SnapshotMetadata (r:1 w:1) + /// Proof Skipped: ElectionProviderMultiPhase SnapshotMetadata (max_values: Some(1), max_size: None, mode: Measured) + /// Storage: ElectionProviderMultiPhase SignedSubmissionsMap (r:1 w:0) + /// Proof Skipped: ElectionProviderMultiPhase SignedSubmissionsMap (max_values: None, max_size: None, mode: Measured) + /// Storage: ElectionProviderMultiPhase QueuedSolution (r:1 w:1) + /// Proof Skipped: ElectionProviderMultiPhase QueuedSolution (max_values: Some(1), max_size: None, mode: Measured) + /// Storage: ElectionProviderMultiPhase Round (r:1 w:1) + /// Proof Skipped: ElectionProviderMultiPhase Round (max_values: Some(1), max_size: None, mode: Measured) + /// Storage: ElectionProviderMultiPhase CurrentPhase (r:1 w:1) + /// Proof Skipped: ElectionProviderMultiPhase CurrentPhase (max_values: Some(1), max_size: None, mode: Measured) + /// Storage: ElectionProviderMultiPhase DesiredTargets (r:0 w:1) + /// Proof Skipped: ElectionProviderMultiPhase DesiredTargets (max_values: Some(1), max_size: None, mode: Measured) + /// Storage: ElectionProviderMultiPhase Snapshot (r:0 w:1) + /// Proof Skipped: ElectionProviderMultiPhase Snapshot (max_values: Some(1), max_size: None, mode: Measured) /// The range of component `a` is `[500, 800]`. /// The range of component `d` is `[200, 400]`. fn elect_queued(a: u32, d: u32, ) -> Weight { - // Minimum execution time: 1_296_481 nanoseconds. - Weight::from_ref_time(1_076_121_575 as u64) - // Standard Error: 5_708 - .saturating_add(Weight::from_ref_time(474_995 as u64).saturating_mul(a as u64)) - // Standard Error: 8_556 - .saturating_add(Weight::from_ref_time(39_224 as u64).saturating_mul(d as u64)) - .saturating_add(T::DbWeight::get().reads(6 as u64)) - .saturating_add(T::DbWeight::get().writes(8 as u64)) + // Proof Size summary in bytes: + // Measured: `368 + a * (768 ±0) + d * (48 ±0)` + // Estimated: `9540 + a * (6912 ±0) + d * (441 ±0)` + // Minimum execution time: 268_021 nanoseconds. + Weight::from_parts(72_491_937, 9540) + // Standard Error: 2_910 + .saturating_add(Weight::from_ref_time(303_955).saturating_mul(a.into())) + // Standard Error: 4_363 + .saturating_add(Weight::from_ref_time(167_369).saturating_mul(d.into())) + .saturating_add(T::DbWeight::get().reads(7_u64)) + .saturating_add(T::DbWeight::get().writes(8_u64)) + .saturating_add(Weight::from_proof_size(6912).saturating_mul(a.into())) + .saturating_add(Weight::from_proof_size(441).saturating_mul(d.into())) } - // Storage: ElectionProviderMultiPhase CurrentPhase (r:1 w:0) - // Storage: ElectionProviderMultiPhase SnapshotMetadata (r:1 w:0) - // Storage: TransactionPayment NextFeeMultiplier (r:1 w:0) - // Storage: ElectionProviderMultiPhase SignedSubmissionIndices (r:1 w:1) - // Storage: ElectionProviderMultiPhase SignedSubmissionNextIndex (r:1 w:1) - // Storage: ElectionProviderMultiPhase SignedSubmissionsMap (r:0 w:1) + /// Storage: ElectionProviderMultiPhase CurrentPhase (r:1 w:0) + /// Proof Skipped: ElectionProviderMultiPhase CurrentPhase (max_values: Some(1), max_size: None, mode: Measured) + /// Storage: ElectionProviderMultiPhase SnapshotMetadata (r:1 w:0) + /// Proof Skipped: ElectionProviderMultiPhase SnapshotMetadata (max_values: Some(1), max_size: None, mode: Measured) + /// Storage: TransactionPayment NextFeeMultiplier (r:1 w:0) + /// Proof: TransactionPayment NextFeeMultiplier (max_values: Some(1), max_size: Some(16), added: 511, mode: MaxEncodedLen) + /// Storage: ElectionProviderMultiPhase SignedSubmissionIndices (r:1 w:1) + /// Proof Skipped: ElectionProviderMultiPhase SignedSubmissionIndices (max_values: Some(1), max_size: None, mode: Measured) + /// Storage: ElectionProviderMultiPhase SignedSubmissionNextIndex (r:1 w:1) + /// Proof Skipped: ElectionProviderMultiPhase SignedSubmissionNextIndex (max_values: Some(1), max_size: None, mode: Measured) + /// Storage: ElectionProviderMultiPhase SignedSubmissionsMap (r:0 w:1) + /// Proof Skipped: ElectionProviderMultiPhase SignedSubmissionsMap (max_values: None, max_size: None, mode: Measured) fn submit() -> Weight { - // Minimum execution time: 58_716 nanoseconds. - Weight::from_ref_time(59_480_000 as u64) - .saturating_add(T::DbWeight::get().reads(5 as u64)) - .saturating_add(T::DbWeight::get().writes(3 as u64)) + // Proof Size summary in bytes: + // Measured: `924` + // Estimated: `7111` + // Minimum execution time: 44_177 nanoseconds. + Weight::from_parts(44_663_000, 7111) + .saturating_add(T::DbWeight::get().reads(5_u64)) + .saturating_add(T::DbWeight::get().writes(3_u64)) } - // Storage: ElectionProviderMultiPhase CurrentPhase (r:1 w:0) - // Storage: ElectionProviderMultiPhase Round (r:1 w:0) - // Storage: ElectionProviderMultiPhase DesiredTargets (r:1 w:0) - // Storage: ElectionProviderMultiPhase QueuedSolution (r:1 w:1) - // Storage: ElectionProviderMultiPhase SnapshotMetadata (r:1 w:0) - // Storage: ElectionProviderMultiPhase MinimumUntrustedScore (r:1 w:0) - // Storage: ElectionProviderMultiPhase Snapshot (r:1 w:0) + /// Storage: ElectionProviderMultiPhase CurrentPhase (r:1 w:0) + /// Proof Skipped: ElectionProviderMultiPhase CurrentPhase (max_values: Some(1), max_size: None, mode: Measured) + /// Storage: ElectionProviderMultiPhase Round (r:1 w:0) + /// Proof Skipped: ElectionProviderMultiPhase Round (max_values: Some(1), max_size: None, mode: Measured) + /// Storage: ElectionProviderMultiPhase DesiredTargets (r:1 w:0) + /// Proof Skipped: ElectionProviderMultiPhase DesiredTargets (max_values: Some(1), max_size: None, mode: Measured) + /// Storage: ElectionProviderMultiPhase QueuedSolution (r:1 w:1) + /// Proof Skipped: ElectionProviderMultiPhase QueuedSolution (max_values: Some(1), max_size: None, mode: Measured) + /// Storage: ElectionProviderMultiPhase SnapshotMetadata (r:1 w:0) + /// Proof Skipped: ElectionProviderMultiPhase SnapshotMetadata (max_values: Some(1), max_size: None, mode: Measured) + /// Storage: ElectionProviderMultiPhase MinimumUntrustedScore (r:1 w:0) + /// Proof Skipped: ElectionProviderMultiPhase MinimumUntrustedScore (max_values: Some(1), max_size: None, mode: Measured) + /// Storage: ElectionProviderMultiPhase Snapshot (r:1 w:0) + /// Proof Skipped: ElectionProviderMultiPhase Snapshot (max_values: Some(1), max_size: None, mode: Measured) /// The range of component `v` is `[1000, 2000]`. /// The range of component `t` is `[500, 1000]`. /// The range of component `a` is `[500, 800]`. /// The range of component `d` is `[200, 400]`. - fn submit_unsigned(v: u32, _t: u32, a: u32, _d: u32, ) -> Weight { - // Minimum execution time: 5_540_737 nanoseconds. - Weight::from_ref_time(5_567_381_000 as u64) - // Standard Error: 18_563 - .saturating_add(Weight::from_ref_time(603_280 as u64).saturating_mul(v as u64)) - // Standard Error: 55_009 - .saturating_add(Weight::from_ref_time(3_164_053 as u64).saturating_mul(a as u64)) - .saturating_add(T::DbWeight::get().reads(7 as u64)) - .saturating_add(T::DbWeight::get().writes(1 as u64)) + fn submit_unsigned(v: u32, t: u32, a: u32, _d: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `251 + v * (553 ±0) + t * (32 ±0)` + // Estimated: `5222 + v * (3871 ±0) + t * (224 ±0)` + // Minimum execution time: 4_425_457 nanoseconds. + Weight::from_parts(4_445_889_000, 5222) + // Standard Error: 13_250 + .saturating_add(Weight::from_ref_time(48_844).saturating_mul(v.into())) + // Standard Error: 39_266 + .saturating_add(Weight::from_ref_time(4_144_034).saturating_mul(a.into())) + .saturating_add(T::DbWeight::get().reads(7_u64)) + .saturating_add(T::DbWeight::get().writes(1_u64)) + .saturating_add(Weight::from_proof_size(3871).saturating_mul(v.into())) + .saturating_add(Weight::from_proof_size(224).saturating_mul(t.into())) } - // Storage: ElectionProviderMultiPhase Round (r:1 w:0) - // Storage: ElectionProviderMultiPhase DesiredTargets (r:1 w:0) - // Storage: ElectionProviderMultiPhase MinimumUntrustedScore (r:1 w:0) - // Storage: ElectionProviderMultiPhase Snapshot (r:1 w:0) + /// Storage: ElectionProviderMultiPhase Round (r:1 w:0) + /// Proof Skipped: ElectionProviderMultiPhase Round (max_values: Some(1), max_size: None, mode: Measured) + /// Storage: ElectionProviderMultiPhase DesiredTargets (r:1 w:0) + /// Proof Skipped: ElectionProviderMultiPhase DesiredTargets (max_values: Some(1), max_size: None, mode: Measured) + /// Storage: ElectionProviderMultiPhase MinimumUntrustedScore (r:1 w:0) + /// Proof Skipped: ElectionProviderMultiPhase MinimumUntrustedScore (max_values: Some(1), max_size: None, mode: Measured) + /// Storage: ElectionProviderMultiPhase Snapshot (r:1 w:0) + /// Proof Skipped: ElectionProviderMultiPhase Snapshot (max_values: Some(1), max_size: None, mode: Measured) /// The range of component `v` is `[1000, 2000]`. /// The range of component `t` is `[500, 1000]`. /// The range of component `a` is `[500, 800]`. /// The range of component `d` is `[200, 400]`. - fn feasibility_check(v: u32, _t: u32, a: u32, _d: u32, ) -> Weight { - // Minimum execution time: 5_021_808 nanoseconds. - Weight::from_ref_time(5_051_856_000 as u64) - // Standard Error: 16_650 - .saturating_add(Weight::from_ref_time(683_344 as u64).saturating_mul(v as u64)) - // Standard Error: 49_342 - .saturating_add(Weight::from_ref_time(2_190_098 as u64).saturating_mul(a as u64)) - .saturating_add(T::DbWeight::get().reads(4 as u64)) + fn feasibility_check(v: u32, t: u32, a: u32, _d: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `226 + v * (553 ±0) + t * (32 ±0)` + // Estimated: `2884 + v * (2212 ±0) + t * (128 ±0)` + // Minimum execution time: 3_812_071 nanoseconds. + Weight::from_parts(3_826_375_000, 2884) + // Standard Error: 11_601 + .saturating_add(Weight::from_ref_time(145_309).saturating_mul(v.into())) + // Standard Error: 34_378 + .saturating_add(Weight::from_ref_time(3_223_977).saturating_mul(a.into())) + .saturating_add(T::DbWeight::get().reads(4_u64)) + .saturating_add(Weight::from_proof_size(2212).saturating_mul(v.into())) + .saturating_add(Weight::from_proof_size(128).saturating_mul(t.into())) } } // For backwards compatibility and tests impl WeightInfo for () { - // Storage: Staking CurrentEra (r:1 w:0) - // Storage: Staking CurrentPlannedSession (r:1 w:0) - // Storage: Staking ErasStartSessionIndex (r:1 w:0) - // Storage: Babe EpochIndex (r:1 w:0) - // Storage: Babe GenesisSlot (r:1 w:0) - // Storage: Babe CurrentSlot (r:1 w:0) - // Storage: Staking ForceEra (r:1 w:0) - // Storage: ElectionProviderMultiPhase CurrentPhase (r:1 w:0) + /// Storage: Staking CurrentEra (r:1 w:0) + /// Proof: Staking CurrentEra (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) + /// Storage: Staking CurrentPlannedSession (r:1 w:0) + /// Proof: Staking CurrentPlannedSession (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) + /// Storage: Staking ErasStartSessionIndex (r:1 w:0) + /// Proof: Staking ErasStartSessionIndex (max_values: None, max_size: Some(16), added: 2491, mode: MaxEncodedLen) + /// Storage: Babe EpochIndex (r:1 w:0) + /// Proof: Babe EpochIndex (max_values: Some(1), max_size: Some(8), added: 503, mode: MaxEncodedLen) + /// Storage: Babe GenesisSlot (r:1 w:0) + /// Proof: Babe GenesisSlot (max_values: Some(1), max_size: Some(8), added: 503, mode: MaxEncodedLen) + /// Storage: Babe CurrentSlot (r:1 w:0) + /// Proof: Babe CurrentSlot (max_values: Some(1), max_size: Some(8), added: 503, mode: MaxEncodedLen) + /// Storage: Staking ForceEra (r:1 w:0) + /// Proof: Staking ForceEra (max_values: Some(1), max_size: Some(1), added: 496, mode: MaxEncodedLen) + /// Storage: ElectionProviderMultiPhase CurrentPhase (r:1 w:0) + /// Proof Skipped: ElectionProviderMultiPhase CurrentPhase (max_values: Some(1), max_size: None, mode: Measured) fn on_initialize_nothing() -> Weight { - // Minimum execution time: 17_309 nanoseconds. - Weight::from_ref_time(17_646_000 as u64) - .saturating_add(RocksDbWeight::get().reads(8 as u64)) + // Proof Size summary in bytes: + // Measured: `994` + // Estimated: `6983` + // Minimum execution time: 17_801 nanoseconds. + Weight::from_parts(18_364_000, 6983) + .saturating_add(RocksDbWeight::get().reads(8_u64)) } - // Storage: ElectionProviderMultiPhase Round (r:1 w:0) - // Storage: ElectionProviderMultiPhase CurrentPhase (r:0 w:1) + /// Storage: ElectionProviderMultiPhase Round (r:1 w:0) + /// Proof Skipped: ElectionProviderMultiPhase Round (max_values: Some(1), max_size: None, mode: Measured) + /// Storage: ElectionProviderMultiPhase CurrentPhase (r:1 w:1) + /// Proof Skipped: ElectionProviderMultiPhase CurrentPhase (max_values: Some(1), max_size: None, mode: Measured) fn on_initialize_open_signed() -> Weight { - // Minimum execution time: 17_992 nanoseconds. - Weight::from_ref_time(18_426_000 as u64) - .saturating_add(RocksDbWeight::get().reads(1 as u64)) - .saturating_add(RocksDbWeight::get().writes(1 as u64)) + // Proof Size summary in bytes: + // Measured: `114` + // Estimated: `1218` + // Minimum execution time: 12_814 nanoseconds. + Weight::from_parts(13_154_000, 1218) + .saturating_add(RocksDbWeight::get().reads(2_u64)) + .saturating_add(RocksDbWeight::get().writes(1_u64)) } - // Storage: ElectionProviderMultiPhase Round (r:1 w:0) - // Storage: ElectionProviderMultiPhase CurrentPhase (r:0 w:1) + /// Storage: ElectionProviderMultiPhase Round (r:1 w:0) + /// Proof Skipped: ElectionProviderMultiPhase Round (max_values: Some(1), max_size: None, mode: Measured) + /// Storage: ElectionProviderMultiPhase CurrentPhase (r:1 w:1) + /// Proof Skipped: ElectionProviderMultiPhase CurrentPhase (max_values: Some(1), max_size: None, mode: Measured) fn on_initialize_open_unsigned() -> Weight { - // Minimum execution time: 17_340 nanoseconds. - Weight::from_ref_time(17_881_000 as u64) - .saturating_add(RocksDbWeight::get().reads(1 as u64)) - .saturating_add(RocksDbWeight::get().writes(1 as u64)) + // Proof Size summary in bytes: + // Measured: `114` + // Estimated: `1218` + // Minimum execution time: 14_565 nanoseconds. + Weight::from_parts(15_097_000, 1218) + .saturating_add(RocksDbWeight::get().reads(2_u64)) + .saturating_add(RocksDbWeight::get().writes(1_u64)) } - // Storage: System Account (r:1 w:1) - // Storage: ElectionProviderMultiPhase QueuedSolution (r:0 w:1) + /// Storage: System Account (r:1 w:1) + /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) + /// Storage: ElectionProviderMultiPhase QueuedSolution (r:0 w:1) + /// Proof Skipped: ElectionProviderMultiPhase QueuedSolution (max_values: Some(1), max_size: None, mode: Measured) fn finalize_signed_phase_accept_solution() -> Weight { - // Minimum execution time: 35_571 nanoseconds. - Weight::from_ref_time(35_989_000 as u64) - .saturating_add(RocksDbWeight::get().reads(1 as u64)) - .saturating_add(RocksDbWeight::get().writes(2 as u64)) + // Proof Size summary in bytes: + // Measured: `206` + // Estimated: `2809` + // Minimum execution time: 23_341 nanoseconds. + Weight::from_parts(23_770_000, 2809) + .saturating_add(RocksDbWeight::get().reads(1_u64)) + .saturating_add(RocksDbWeight::get().writes(2_u64)) } - // Storage: System Account (r:1 w:1) + /// Storage: System Account (r:1 w:1) + /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) fn finalize_signed_phase_reject_solution() -> Weight { - // Minimum execution time: 27_403 nanoseconds. - Weight::from_ref_time(27_879_000 as u64) - .saturating_add(RocksDbWeight::get().reads(1 as u64)) - .saturating_add(RocksDbWeight::get().writes(1 as u64)) + // Proof Size summary in bytes: + // Measured: `206` + // Estimated: `2603` + // Minimum execution time: 16_662 nanoseconds. + Weight::from_parts(16_898_000, 2603) + .saturating_add(RocksDbWeight::get().reads(1_u64)) + .saturating_add(RocksDbWeight::get().writes(1_u64)) } - // Storage: ElectionProviderMultiPhase SnapshotMetadata (r:0 w:1) - // Storage: ElectionProviderMultiPhase DesiredTargets (r:0 w:1) - // Storage: ElectionProviderMultiPhase Snapshot (r:0 w:1) + /// Storage: ElectionProviderMultiPhase SnapshotMetadata (r:0 w:1) + /// Proof Skipped: ElectionProviderMultiPhase SnapshotMetadata (max_values: Some(1), max_size: None, mode: Measured) + /// Storage: ElectionProviderMultiPhase DesiredTargets (r:0 w:1) + /// Proof Skipped: ElectionProviderMultiPhase DesiredTargets (max_values: Some(1), max_size: None, mode: Measured) + /// Storage: ElectionProviderMultiPhase Snapshot (r:0 w:1) + /// Proof Skipped: ElectionProviderMultiPhase Snapshot (max_values: Some(1), max_size: None, mode: Measured) /// The range of component `v` is `[1000, 2000]`. /// The range of component `t` is `[500, 1000]`. fn create_snapshot_internal(v: u32, _t: u32, ) -> Weight { - // Minimum execution time: 571_900 nanoseconds. - Weight::from_ref_time(589_170_000 as u64) - // Standard Error: 7_123 - .saturating_add(Weight::from_ref_time(384_767 as u64).saturating_mul(v as u64)) - .saturating_add(RocksDbWeight::get().writes(3 as u64)) + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 215_168 nanoseconds. + Weight::from_ref_time(219_887_000) + // Standard Error: 1_444 + .saturating_add(Weight::from_ref_time(146_388).saturating_mul(v.into())) + .saturating_add(RocksDbWeight::get().writes(3_u64)) } - // Storage: ElectionProviderMultiPhase SignedSubmissionIndices (r:1 w:1) - // Storage: ElectionProviderMultiPhase SignedSubmissionNextIndex (r:1 w:1) - // Storage: ElectionProviderMultiPhase SnapshotMetadata (r:1 w:1) - // Storage: ElectionProviderMultiPhase SignedSubmissionsMap (r:1 w:0) - // Storage: ElectionProviderMultiPhase QueuedSolution (r:1 w:1) - // Storage: ElectionProviderMultiPhase Round (r:1 w:1) - // Storage: ElectionProviderMultiPhase DesiredTargets (r:0 w:1) - // Storage: ElectionProviderMultiPhase Snapshot (r:0 w:1) - // Storage: ElectionProviderMultiPhase CurrentPhase (r:0 w:1) + /// Storage: ElectionProviderMultiPhase SignedSubmissionIndices (r:1 w:1) + /// Proof Skipped: ElectionProviderMultiPhase SignedSubmissionIndices (max_values: Some(1), max_size: None, mode: Measured) + /// Storage: ElectionProviderMultiPhase SignedSubmissionNextIndex (r:1 w:1) + /// Proof Skipped: ElectionProviderMultiPhase SignedSubmissionNextIndex (max_values: Some(1), max_size: None, mode: Measured) + /// Storage: ElectionProviderMultiPhase SnapshotMetadata (r:1 w:1) + /// Proof Skipped: ElectionProviderMultiPhase SnapshotMetadata (max_values: Some(1), max_size: None, mode: Measured) + /// Storage: ElectionProviderMultiPhase SignedSubmissionsMap (r:1 w:0) + /// Proof Skipped: ElectionProviderMultiPhase SignedSubmissionsMap (max_values: None, max_size: None, mode: Measured) + /// Storage: ElectionProviderMultiPhase QueuedSolution (r:1 w:1) + /// Proof Skipped: ElectionProviderMultiPhase QueuedSolution (max_values: Some(1), max_size: None, mode: Measured) + /// Storage: ElectionProviderMultiPhase Round (r:1 w:1) + /// Proof Skipped: ElectionProviderMultiPhase Round (max_values: Some(1), max_size: None, mode: Measured) + /// Storage: ElectionProviderMultiPhase CurrentPhase (r:1 w:1) + /// Proof Skipped: ElectionProviderMultiPhase CurrentPhase (max_values: Some(1), max_size: None, mode: Measured) + /// Storage: ElectionProviderMultiPhase DesiredTargets (r:0 w:1) + /// Proof Skipped: ElectionProviderMultiPhase DesiredTargets (max_values: Some(1), max_size: None, mode: Measured) + /// Storage: ElectionProviderMultiPhase Snapshot (r:0 w:1) + /// Proof Skipped: ElectionProviderMultiPhase Snapshot (max_values: Some(1), max_size: None, mode: Measured) /// The range of component `a` is `[500, 800]`. /// The range of component `d` is `[200, 400]`. fn elect_queued(a: u32, d: u32, ) -> Weight { - // Minimum execution time: 1_296_481 nanoseconds. - Weight::from_ref_time(1_076_121_575 as u64) - // Standard Error: 5_708 - .saturating_add(Weight::from_ref_time(474_995 as u64).saturating_mul(a as u64)) - // Standard Error: 8_556 - .saturating_add(Weight::from_ref_time(39_224 as u64).saturating_mul(d as u64)) - .saturating_add(RocksDbWeight::get().reads(6 as u64)) - .saturating_add(RocksDbWeight::get().writes(8 as u64)) + // Proof Size summary in bytes: + // Measured: `368 + a * (768 ±0) + d * (48 ±0)` + // Estimated: `9540 + a * (6912 ±0) + d * (441 ±0)` + // Minimum execution time: 268_021 nanoseconds. + Weight::from_parts(72_491_937, 9540) + // Standard Error: 2_910 + .saturating_add(Weight::from_ref_time(303_955).saturating_mul(a.into())) + // Standard Error: 4_363 + .saturating_add(Weight::from_ref_time(167_369).saturating_mul(d.into())) + .saturating_add(RocksDbWeight::get().reads(7_u64)) + .saturating_add(RocksDbWeight::get().writes(8_u64)) + .saturating_add(Weight::from_proof_size(6912).saturating_mul(a.into())) + .saturating_add(Weight::from_proof_size(441).saturating_mul(d.into())) } - // Storage: ElectionProviderMultiPhase CurrentPhase (r:1 w:0) - // Storage: ElectionProviderMultiPhase SnapshotMetadata (r:1 w:0) - // Storage: TransactionPayment NextFeeMultiplier (r:1 w:0) - // Storage: ElectionProviderMultiPhase SignedSubmissionIndices (r:1 w:1) - // Storage: ElectionProviderMultiPhase SignedSubmissionNextIndex (r:1 w:1) - // Storage: ElectionProviderMultiPhase SignedSubmissionsMap (r:0 w:1) + /// Storage: ElectionProviderMultiPhase CurrentPhase (r:1 w:0) + /// Proof Skipped: ElectionProviderMultiPhase CurrentPhase (max_values: Some(1), max_size: None, mode: Measured) + /// Storage: ElectionProviderMultiPhase SnapshotMetadata (r:1 w:0) + /// Proof Skipped: ElectionProviderMultiPhase SnapshotMetadata (max_values: Some(1), max_size: None, mode: Measured) + /// Storage: TransactionPayment NextFeeMultiplier (r:1 w:0) + /// Proof: TransactionPayment NextFeeMultiplier (max_values: Some(1), max_size: Some(16), added: 511, mode: MaxEncodedLen) + /// Storage: ElectionProviderMultiPhase SignedSubmissionIndices (r:1 w:1) + /// Proof Skipped: ElectionProviderMultiPhase SignedSubmissionIndices (max_values: Some(1), max_size: None, mode: Measured) + /// Storage: ElectionProviderMultiPhase SignedSubmissionNextIndex (r:1 w:1) + /// Proof Skipped: ElectionProviderMultiPhase SignedSubmissionNextIndex (max_values: Some(1), max_size: None, mode: Measured) + /// Storage: ElectionProviderMultiPhase SignedSubmissionsMap (r:0 w:1) + /// Proof Skipped: ElectionProviderMultiPhase SignedSubmissionsMap (max_values: None, max_size: None, mode: Measured) fn submit() -> Weight { - // Minimum execution time: 58_716 nanoseconds. - Weight::from_ref_time(59_480_000 as u64) - .saturating_add(RocksDbWeight::get().reads(5 as u64)) - .saturating_add(RocksDbWeight::get().writes(3 as u64)) + // Proof Size summary in bytes: + // Measured: `924` + // Estimated: `7111` + // Minimum execution time: 44_177 nanoseconds. + Weight::from_parts(44_663_000, 7111) + .saturating_add(RocksDbWeight::get().reads(5_u64)) + .saturating_add(RocksDbWeight::get().writes(3_u64)) } - // Storage: ElectionProviderMultiPhase CurrentPhase (r:1 w:0) - // Storage: ElectionProviderMultiPhase Round (r:1 w:0) - // Storage: ElectionProviderMultiPhase DesiredTargets (r:1 w:0) - // Storage: ElectionProviderMultiPhase QueuedSolution (r:1 w:1) - // Storage: ElectionProviderMultiPhase SnapshotMetadata (r:1 w:0) - // Storage: ElectionProviderMultiPhase MinimumUntrustedScore (r:1 w:0) - // Storage: ElectionProviderMultiPhase Snapshot (r:1 w:0) + /// Storage: ElectionProviderMultiPhase CurrentPhase (r:1 w:0) + /// Proof Skipped: ElectionProviderMultiPhase CurrentPhase (max_values: Some(1), max_size: None, mode: Measured) + /// Storage: ElectionProviderMultiPhase Round (r:1 w:0) + /// Proof Skipped: ElectionProviderMultiPhase Round (max_values: Some(1), max_size: None, mode: Measured) + /// Storage: ElectionProviderMultiPhase DesiredTargets (r:1 w:0) + /// Proof Skipped: ElectionProviderMultiPhase DesiredTargets (max_values: Some(1), max_size: None, mode: Measured) + /// Storage: ElectionProviderMultiPhase QueuedSolution (r:1 w:1) + /// Proof Skipped: ElectionProviderMultiPhase QueuedSolution (max_values: Some(1), max_size: None, mode: Measured) + /// Storage: ElectionProviderMultiPhase SnapshotMetadata (r:1 w:0) + /// Proof Skipped: ElectionProviderMultiPhase SnapshotMetadata (max_values: Some(1), max_size: None, mode: Measured) + /// Storage: ElectionProviderMultiPhase MinimumUntrustedScore (r:1 w:0) + /// Proof Skipped: ElectionProviderMultiPhase MinimumUntrustedScore (max_values: Some(1), max_size: None, mode: Measured) + /// Storage: ElectionProviderMultiPhase Snapshot (r:1 w:0) + /// Proof Skipped: ElectionProviderMultiPhase Snapshot (max_values: Some(1), max_size: None, mode: Measured) /// The range of component `v` is `[1000, 2000]`. /// The range of component `t` is `[500, 1000]`. /// The range of component `a` is `[500, 800]`. /// The range of component `d` is `[200, 400]`. - fn submit_unsigned(v: u32, _t: u32, a: u32, _d: u32, ) -> Weight { - // Minimum execution time: 5_540_737 nanoseconds. - Weight::from_ref_time(5_567_381_000 as u64) - // Standard Error: 18_563 - .saturating_add(Weight::from_ref_time(603_280 as u64).saturating_mul(v as u64)) - // Standard Error: 55_009 - .saturating_add(Weight::from_ref_time(3_164_053 as u64).saturating_mul(a as u64)) - .saturating_add(RocksDbWeight::get().reads(7 as u64)) - .saturating_add(RocksDbWeight::get().writes(1 as u64)) + fn submit_unsigned(v: u32, t: u32, a: u32, _d: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `251 + v * (553 ±0) + t * (32 ±0)` + // Estimated: `5222 + v * (3871 ±0) + t * (224 ±0)` + // Minimum execution time: 4_425_457 nanoseconds. + Weight::from_parts(4_445_889_000, 5222) + // Standard Error: 13_250 + .saturating_add(Weight::from_ref_time(48_844).saturating_mul(v.into())) + // Standard Error: 39_266 + .saturating_add(Weight::from_ref_time(4_144_034).saturating_mul(a.into())) + .saturating_add(RocksDbWeight::get().reads(7_u64)) + .saturating_add(RocksDbWeight::get().writes(1_u64)) + .saturating_add(Weight::from_proof_size(3871).saturating_mul(v.into())) + .saturating_add(Weight::from_proof_size(224).saturating_mul(t.into())) } - // Storage: ElectionProviderMultiPhase Round (r:1 w:0) - // Storage: ElectionProviderMultiPhase DesiredTargets (r:1 w:0) - // Storage: ElectionProviderMultiPhase MinimumUntrustedScore (r:1 w:0) - // Storage: ElectionProviderMultiPhase Snapshot (r:1 w:0) + /// Storage: ElectionProviderMultiPhase Round (r:1 w:0) + /// Proof Skipped: ElectionProviderMultiPhase Round (max_values: Some(1), max_size: None, mode: Measured) + /// Storage: ElectionProviderMultiPhase DesiredTargets (r:1 w:0) + /// Proof Skipped: ElectionProviderMultiPhase DesiredTargets (max_values: Some(1), max_size: None, mode: Measured) + /// Storage: ElectionProviderMultiPhase MinimumUntrustedScore (r:1 w:0) + /// Proof Skipped: ElectionProviderMultiPhase MinimumUntrustedScore (max_values: Some(1), max_size: None, mode: Measured) + /// Storage: ElectionProviderMultiPhase Snapshot (r:1 w:0) + /// Proof Skipped: ElectionProviderMultiPhase Snapshot (max_values: Some(1), max_size: None, mode: Measured) /// The range of component `v` is `[1000, 2000]`. /// The range of component `t` is `[500, 1000]`. /// The range of component `a` is `[500, 800]`. /// The range of component `d` is `[200, 400]`. - fn feasibility_check(v: u32, _t: u32, a: u32, _d: u32, ) -> Weight { - // Minimum execution time: 5_021_808 nanoseconds. - Weight::from_ref_time(5_051_856_000 as u64) - // Standard Error: 16_650 - .saturating_add(Weight::from_ref_time(683_344 as u64).saturating_mul(v as u64)) - // Standard Error: 49_342 - .saturating_add(Weight::from_ref_time(2_190_098 as u64).saturating_mul(a as u64)) - .saturating_add(RocksDbWeight::get().reads(4 as u64)) + fn feasibility_check(v: u32, t: u32, a: u32, _d: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `226 + v * (553 ±0) + t * (32 ±0)` + // Estimated: `2884 + v * (2212 ±0) + t * (128 ±0)` + // Minimum execution time: 3_812_071 nanoseconds. + Weight::from_parts(3_826_375_000, 2884) + // Standard Error: 11_601 + .saturating_add(Weight::from_ref_time(145_309).saturating_mul(v.into())) + // Standard Error: 34_378 + .saturating_add(Weight::from_ref_time(3_223_977).saturating_mul(a.into())) + .saturating_add(RocksDbWeight::get().reads(4_u64)) + .saturating_add(Weight::from_proof_size(2212).saturating_mul(v.into())) + .saturating_add(Weight::from_proof_size(128).saturating_mul(t.into())) } } diff --git a/frame/elections-phragmen/src/weights.rs b/frame/elections-phragmen/src/weights.rs index ddc55b087..dc5d937e3 100644 --- a/frame/elections-phragmen/src/weights.rs +++ b/frame/elections-phragmen/src/weights.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2022 Parity Technologies (UK) Ltd. +// Copyright (C) 2023 Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); @@ -18,7 +18,8 @@ //! Autogenerated weights for pallet_elections_phragmen //! //! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev -//! DATE: 2022-11-07, STEPS: `50`, REPEAT: 20, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! DATE: 2023-01-24, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! WORST CASE MAP SIZE: `1000000` //! HOSTNAME: `bm2`, CPU: `Intel(R) Core(TM) i7-7700K CPU @ 4.20GHz` //! EXECUTION: Some(Wasm), WASM-EXECUTION: Compiled, CHAIN: Some("dev"), DB CACHE: 1024 @@ -64,306 +65,494 @@ pub trait WeightInfo { /// Weights for pallet_elections_phragmen using the Substrate node and recommended hardware. pub struct SubstrateWeight(PhantomData); impl WeightInfo for SubstrateWeight { - // Storage: Elections Candidates (r:1 w:0) - // Storage: Elections Members (r:1 w:0) - // Storage: Elections RunnersUp (r:1 w:0) - // Storage: Elections Voting (r:1 w:1) - // Storage: Balances Locks (r:1 w:1) + /// Storage: Elections Candidates (r:1 w:0) + /// Proof Skipped: Elections Candidates (max_values: Some(1), max_size: None, mode: Measured) + /// Storage: Elections Members (r:1 w:0) + /// Proof Skipped: Elections Members (max_values: Some(1), max_size: None, mode: Measured) + /// Storage: Elections RunnersUp (r:1 w:0) + /// Proof Skipped: Elections RunnersUp (max_values: Some(1), max_size: None, mode: Measured) + /// Storage: Elections Voting (r:1 w:1) + /// Proof Skipped: Elections Voting (max_values: None, max_size: None, mode: Measured) + /// Storage: Balances Locks (r:1 w:1) + /// Proof: Balances Locks (max_values: None, max_size: Some(1299), added: 3774, mode: MaxEncodedLen) /// The range of component `v` is `[1, 16]`. fn vote_equal(v: u32, ) -> Weight { - // Minimum execution time: 38_496 nanoseconds. - Weight::from_ref_time(39_424_348 as u64) - // Standard Error: 3_547 - .saturating_add(Weight::from_ref_time(119_971 as u64).saturating_mul(v as u64)) - .saturating_add(T::DbWeight::get().reads(5 as u64)) - .saturating_add(T::DbWeight::get().writes(2 as u64)) + // Proof Size summary in bytes: + // Measured: `499 + v * (80 ±0)` + // Estimated: `9726 + v * (320 ±0)` + // Minimum execution time: 25_407 nanoseconds. + Weight::from_parts(26_035_742, 9726) + // Standard Error: 2_162 + .saturating_add(Weight::from_ref_time(120_321).saturating_mul(v.into())) + .saturating_add(T::DbWeight::get().reads(5_u64)) + .saturating_add(T::DbWeight::get().writes(2_u64)) + .saturating_add(Weight::from_proof_size(320).saturating_mul(v.into())) } - // Storage: Elections Candidates (r:1 w:0) - // Storage: Elections Members (r:1 w:0) - // Storage: Elections RunnersUp (r:1 w:0) - // Storage: Elections Voting (r:1 w:1) - // Storage: Balances Locks (r:1 w:1) + /// Storage: Elections Candidates (r:1 w:0) + /// Proof Skipped: Elections Candidates (max_values: Some(1), max_size: None, mode: Measured) + /// Storage: Elections Members (r:1 w:0) + /// Proof Skipped: Elections Members (max_values: Some(1), max_size: None, mode: Measured) + /// Storage: Elections RunnersUp (r:1 w:0) + /// Proof Skipped: Elections RunnersUp (max_values: Some(1), max_size: None, mode: Measured) + /// Storage: Elections Voting (r:1 w:1) + /// Proof Skipped: Elections Voting (max_values: None, max_size: None, mode: Measured) + /// Storage: Balances Locks (r:1 w:1) + /// Proof: Balances Locks (max_values: None, max_size: Some(1299), added: 3774, mode: MaxEncodedLen) /// The range of component `v` is `[2, 16]`. fn vote_more(v: u32, ) -> Weight { - // Minimum execution time: 49_459 nanoseconds. - Weight::from_ref_time(50_225_486 as u64) - // Standard Error: 3_160 - .saturating_add(Weight::from_ref_time(170_360 as u64).saturating_mul(v as u64)) - .saturating_add(T::DbWeight::get().reads(5 as u64)) - .saturating_add(T::DbWeight::get().writes(2 as u64)) + // Proof Size summary in bytes: + // Measured: `467 + v * (80 ±0)` + // Estimated: `9598 + v * (320 ±0)` + // Minimum execution time: 34_477 nanoseconds. + Weight::from_parts(35_197_694, 9598) + // Standard Error: 2_089 + .saturating_add(Weight::from_ref_time(116_792).saturating_mul(v.into())) + .saturating_add(T::DbWeight::get().reads(5_u64)) + .saturating_add(T::DbWeight::get().writes(2_u64)) + .saturating_add(Weight::from_proof_size(320).saturating_mul(v.into())) } - // Storage: Elections Candidates (r:1 w:0) - // Storage: Elections Members (r:1 w:0) - // Storage: Elections RunnersUp (r:1 w:0) - // Storage: Elections Voting (r:1 w:1) - // Storage: Balances Locks (r:1 w:1) + /// Storage: Elections Candidates (r:1 w:0) + /// Proof Skipped: Elections Candidates (max_values: Some(1), max_size: None, mode: Measured) + /// Storage: Elections Members (r:1 w:0) + /// Proof Skipped: Elections Members (max_values: Some(1), max_size: None, mode: Measured) + /// Storage: Elections RunnersUp (r:1 w:0) + /// Proof Skipped: Elections RunnersUp (max_values: Some(1), max_size: None, mode: Measured) + /// Storage: Elections Voting (r:1 w:1) + /// Proof Skipped: Elections Voting (max_values: None, max_size: None, mode: Measured) + /// Storage: Balances Locks (r:1 w:1) + /// Proof: Balances Locks (max_values: None, max_size: Some(1299), added: 3774, mode: MaxEncodedLen) /// The range of component `v` is `[2, 16]`. fn vote_less(v: u32, ) -> Weight { - // Minimum execution time: 48_712 nanoseconds. - Weight::from_ref_time(49_463_298 as u64) - // Standard Error: 2_678 - .saturating_add(Weight::from_ref_time(231_771 as u64).saturating_mul(v as u64)) - .saturating_add(T::DbWeight::get().reads(5 as u64)) - .saturating_add(T::DbWeight::get().writes(2 as u64)) + // Proof Size summary in bytes: + // Measured: `499 + v * (80 ±0)` + // Estimated: `9726 + v * (320 ±0)` + // Minimum execution time: 34_573 nanoseconds. + Weight::from_parts(35_254_508, 9726) + // Standard Error: 2_076 + .saturating_add(Weight::from_ref_time(110_656).saturating_mul(v.into())) + .saturating_add(T::DbWeight::get().reads(5_u64)) + .saturating_add(T::DbWeight::get().writes(2_u64)) + .saturating_add(Weight::from_proof_size(320).saturating_mul(v.into())) } - // Storage: Elections Voting (r:1 w:1) - // Storage: Balances Locks (r:1 w:1) + /// Storage: Elections Voting (r:1 w:1) + /// Proof Skipped: Elections Voting (max_values: None, max_size: None, mode: Measured) + /// Storage: Balances Locks (r:1 w:1) + /// Proof: Balances Locks (max_values: None, max_size: Some(1299), added: 3774, mode: MaxEncodedLen) fn remove_voter() -> Weight { - // Minimum execution time: 48_359 nanoseconds. - Weight::from_ref_time(48_767_000 as u64) - .saturating_add(T::DbWeight::get().reads(2 as u64)) - .saturating_add(T::DbWeight::get().writes(2 as u64)) + // Proof Size summary in bytes: + // Measured: `989` + // Estimated: `7238` + // Minimum execution time: 31_469 nanoseconds. + Weight::from_parts(31_877_000, 7238) + .saturating_add(T::DbWeight::get().reads(2_u64)) + .saturating_add(T::DbWeight::get().writes(2_u64)) } - // Storage: Elections Candidates (r:1 w:1) - // Storage: Elections Members (r:1 w:0) - // Storage: Elections RunnersUp (r:1 w:0) + /// Storage: Elections Candidates (r:1 w:1) + /// Proof Skipped: Elections Candidates (max_values: Some(1), max_size: None, mode: Measured) + /// Storage: Elections Members (r:1 w:0) + /// Proof Skipped: Elections Members (max_values: Some(1), max_size: None, mode: Measured) + /// Storage: Elections RunnersUp (r:1 w:0) + /// Proof Skipped: Elections RunnersUp (max_values: Some(1), max_size: None, mode: Measured) /// The range of component `c` is `[1, 1000]`. fn submit_candidacy(c: u32, ) -> Weight { - // Minimum execution time: 43_369 nanoseconds. - Weight::from_ref_time(49_587_113 as u64) - // Standard Error: 1_008 - .saturating_add(Weight::from_ref_time(77_752 as u64).saturating_mul(c as u64)) - .saturating_add(T::DbWeight::get().reads(3 as u64)) - .saturating_add(T::DbWeight::get().writes(1 as u64)) + // Proof Size summary in bytes: + // Measured: `1687 + c * (48 ±0)` + // Estimated: `6540 + c * (144 ±0)` + // Minimum execution time: 26_969 nanoseconds. + Weight::from_parts(28_584_266, 6540) + // Standard Error: 93 + .saturating_add(Weight::from_ref_time(52_393).saturating_mul(c.into())) + .saturating_add(T::DbWeight::get().reads(3_u64)) + .saturating_add(T::DbWeight::get().writes(1_u64)) + .saturating_add(Weight::from_proof_size(144).saturating_mul(c.into())) } - // Storage: Elections Candidates (r:1 w:1) + /// Storage: Elections Candidates (r:1 w:1) + /// Proof Skipped: Elections Candidates (max_values: Some(1), max_size: None, mode: Measured) /// The range of component `c` is `[1, 1000]`. fn renounce_candidacy_candidate(c: u32, ) -> Weight { - // Minimum execution time: 41_321 nanoseconds. - Weight::from_ref_time(50_803_289 as u64) - // Standard Error: 1_159 - .saturating_add(Weight::from_ref_time(57_239 as u64).saturating_mul(c as u64)) - .saturating_add(T::DbWeight::get().reads(1 as u64)) - .saturating_add(T::DbWeight::get().writes(1 as u64)) + // Proof Size summary in bytes: + // Measured: `348 + c * (48 ±0)` + // Estimated: `830 + c * (48 ±0)` + // Minimum execution time: 23_635 nanoseconds. + Weight::from_parts(23_482_193, 830) + // Standard Error: 103 + .saturating_add(Weight::from_ref_time(33_759).saturating_mul(c.into())) + .saturating_add(T::DbWeight::get().reads(1_u64)) + .saturating_add(T::DbWeight::get().writes(1_u64)) + .saturating_add(Weight::from_proof_size(48).saturating_mul(c.into())) } - // Storage: Elections Members (r:1 w:1) - // Storage: Elections RunnersUp (r:1 w:1) - // Storage: Council Prime (r:1 w:1) - // Storage: Council Proposals (r:1 w:0) - // Storage: Council Members (r:0 w:1) + /// Storage: Elections Members (r:1 w:1) + /// Proof Skipped: Elections Members (max_values: Some(1), max_size: None, mode: Measured) + /// Storage: Elections RunnersUp (r:1 w:1) + /// Proof Skipped: Elections RunnersUp (max_values: Some(1), max_size: None, mode: Measured) + /// Storage: Council Prime (r:1 w:1) + /// Proof Skipped: Council Prime (max_values: Some(1), max_size: None, mode: Measured) + /// Storage: Council Proposals (r:1 w:0) + /// Proof Skipped: Council Proposals (max_values: Some(1), max_size: None, mode: Measured) + /// Storage: Council Members (r:0 w:1) + /// Proof Skipped: Council Members (max_values: Some(1), max_size: None, mode: Measured) fn renounce_candidacy_members() -> Weight { - // Minimum execution time: 53_542 nanoseconds. - Weight::from_ref_time(54_481_000 as u64) - .saturating_add(T::DbWeight::get().reads(4 as u64)) - .saturating_add(T::DbWeight::get().writes(4 as u64)) + // Proof Size summary in bytes: + // Measured: `2027` + // Estimated: `12115` + // Minimum execution time: 39_124 nanoseconds. + Weight::from_parts(39_575_000, 12115) + .saturating_add(T::DbWeight::get().reads(4_u64)) + .saturating_add(T::DbWeight::get().writes(4_u64)) } - // Storage: Elections RunnersUp (r:1 w:1) + /// Storage: Elections RunnersUp (r:1 w:1) + /// Proof Skipped: Elections RunnersUp (max_values: Some(1), max_size: None, mode: Measured) fn renounce_candidacy_runners_up() -> Weight { - // Minimum execution time: 41_825 nanoseconds. - Weight::from_ref_time(42_248_000 as u64) - .saturating_add(T::DbWeight::get().reads(1 as u64)) - .saturating_add(T::DbWeight::get().writes(1 as u64)) + // Proof Size summary in bytes: + // Measured: `975` + // Estimated: `1470` + // Minimum execution time: 25_377 nanoseconds. + Weight::from_parts(25_696_000, 1470) + .saturating_add(T::DbWeight::get().reads(1_u64)) + .saturating_add(T::DbWeight::get().writes(1_u64)) } - // Storage: Benchmark Override (r:0 w:0) + /// Storage: Benchmark Override (r:0 w:0) + /// Proof Skipped: Benchmark Override (max_values: None, max_size: None, mode: Measured) fn remove_member_without_replacement() -> Weight { + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` // Minimum execution time: 2_000_000_000 nanoseconds. - Weight::from_ref_time(2_000_000_000_000 as u64) + Weight::from_ref_time(2_000_000_000_000) } - // Storage: Elections Members (r:1 w:1) - // Storage: System Account (r:1 w:1) - // Storage: Elections RunnersUp (r:1 w:1) - // Storage: Council Prime (r:1 w:1) - // Storage: Council Proposals (r:1 w:0) - // Storage: Council Members (r:0 w:1) + /// Storage: Elections Members (r:1 w:1) + /// Proof Skipped: Elections Members (max_values: Some(1), max_size: None, mode: Measured) + /// Storage: System Account (r:1 w:1) + /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) + /// Storage: Elections RunnersUp (r:1 w:1) + /// Proof Skipped: Elections RunnersUp (max_values: Some(1), max_size: None, mode: Measured) + /// Storage: Council Prime (r:1 w:1) + /// Proof Skipped: Council Prime (max_values: Some(1), max_size: None, mode: Measured) + /// Storage: Council Proposals (r:1 w:0) + /// Proof Skipped: Council Proposals (max_values: Some(1), max_size: None, mode: Measured) + /// Storage: Council Members (r:0 w:1) + /// Proof Skipped: Council Members (max_values: Some(1), max_size: None, mode: Measured) fn remove_member_with_replacement() -> Weight { - // Minimum execution time: 62_600 nanoseconds. - Weight::from_ref_time(63_152_000 as u64) - .saturating_add(T::DbWeight::get().reads(5 as u64)) - .saturating_add(T::DbWeight::get().writes(5 as u64)) + // Proof Size summary in bytes: + // Measured: `2027` + // Estimated: `14718` + // Minimum execution time: 44_919 nanoseconds. + Weight::from_parts(45_548_000, 14718) + .saturating_add(T::DbWeight::get().reads(5_u64)) + .saturating_add(T::DbWeight::get().writes(5_u64)) } - // Storage: Elections Voting (r:5001 w:5000) - // Storage: Elections Members (r:1 w:0) - // Storage: Elections RunnersUp (r:1 w:0) - // Storage: Elections Candidates (r:1 w:0) - // Storage: Balances Locks (r:5000 w:5000) - // Storage: System Account (r:5000 w:5000) + /// Storage: Elections Voting (r:10001 w:10000) + /// Proof Skipped: Elections Voting (max_values: None, max_size: None, mode: Measured) + /// Storage: Elections Members (r:1 w:0) + /// Proof Skipped: Elections Members (max_values: Some(1), max_size: None, mode: Measured) + /// Storage: Elections RunnersUp (r:1 w:0) + /// Proof Skipped: Elections RunnersUp (max_values: Some(1), max_size: None, mode: Measured) + /// Storage: Elections Candidates (r:1 w:0) + /// Proof Skipped: Elections Candidates (max_values: Some(1), max_size: None, mode: Measured) + /// Storage: Balances Locks (r:10000 w:10000) + /// Proof: Balances Locks (max_values: None, max_size: Some(1299), added: 3774, mode: MaxEncodedLen) + /// Storage: System Account (r:10000 w:10000) + /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) /// The range of component `v` is `[5000, 10000]`. /// The range of component `d` is `[0, 5000]`. fn clean_defunct_voters(v: u32, _d: u32, ) -> Weight { - // Minimum execution time: 297_149_264 nanoseconds. - Weight::from_ref_time(297_898_499_000 as u64) - // Standard Error: 263_819 - .saturating_add(Weight::from_ref_time(37_914_985 as u64).saturating_mul(v as u64)) - .saturating_add(T::DbWeight::get().reads(4 as u64)) - .saturating_add(T::DbWeight::get().reads((3 as u64).saturating_mul(v as u64))) - .saturating_add(T::DbWeight::get().writes((3 as u64).saturating_mul(v as u64))) + // Proof Size summary in bytes: + // Measured: `36028 + v * (872 ±0)` + // Estimated: `149172 + v * (12340 ±0)` + // Minimum execution time: 297_544_939 nanoseconds. + Weight::from_parts(298_088_024_000, 149172) + // Standard Error: 264_599 + .saturating_add(Weight::from_ref_time(38_142_857).saturating_mul(v.into())) + .saturating_add(T::DbWeight::get().reads(4_u64)) + .saturating_add(T::DbWeight::get().reads((3_u64).saturating_mul(v.into()))) + .saturating_add(T::DbWeight::get().writes((3_u64).saturating_mul(v.into()))) + .saturating_add(Weight::from_proof_size(12340).saturating_mul(v.into())) } - // Storage: Elections Candidates (r:1 w:1) - // Storage: Elections Members (r:1 w:1) - // Storage: Elections RunnersUp (r:1 w:1) - // Storage: Elections Voting (r:10001 w:0) - // Storage: Council Proposals (r:1 w:0) - // Storage: Elections ElectionRounds (r:1 w:1) - // Storage: Council Members (r:0 w:1) - // Storage: Council Prime (r:0 w:1) - // Storage: System Account (r:1 w:1) + /// Storage: Elections Candidates (r:1 w:1) + /// Proof Skipped: Elections Candidates (max_values: Some(1), max_size: None, mode: Measured) + /// Storage: Elections Members (r:1 w:1) + /// Proof Skipped: Elections Members (max_values: Some(1), max_size: None, mode: Measured) + /// Storage: Elections RunnersUp (r:1 w:1) + /// Proof Skipped: Elections RunnersUp (max_values: Some(1), max_size: None, mode: Measured) + /// Storage: Elections Voting (r:10001 w:0) + /// Proof Skipped: Elections Voting (max_values: None, max_size: None, mode: Measured) + /// Storage: Council Proposals (r:1 w:0) + /// Proof Skipped: Council Proposals (max_values: Some(1), max_size: None, mode: Measured) + /// Storage: System Account (r:980 w:980) + /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) + /// Storage: Elections ElectionRounds (r:1 w:1) + /// Proof Skipped: Elections ElectionRounds (max_values: Some(1), max_size: None, mode: Measured) + /// Storage: Council Members (r:0 w:1) + /// Proof Skipped: Council Members (max_values: Some(1), max_size: None, mode: Measured) + /// Storage: Council Prime (r:0 w:1) + /// Proof Skipped: Council Prime (max_values: Some(1), max_size: None, mode: Measured) /// The range of component `c` is `[1, 1000]`. /// The range of component `v` is `[1, 10000]`. /// The range of component `e` is `[10000, 160000]`. fn election_phragmen(c: u32, v: u32, e: u32, ) -> Weight { - // Minimum execution time: 22_034_317 nanoseconds. - Weight::from_ref_time(22_110_020_000 as u64) - // Standard Error: 235_528 - .saturating_add(Weight::from_ref_time(25_553_585 as u64).saturating_mul(v as u64)) - // Standard Error: 15_114 - .saturating_add(Weight::from_ref_time(1_032_330 as u64).saturating_mul(e as u64)) - .saturating_add(T::DbWeight::get().reads(280 as u64)) - .saturating_add(T::DbWeight::get().reads((1 as u64).saturating_mul(c as u64))) - .saturating_add(T::DbWeight::get().reads((1 as u64).saturating_mul(v as u64))) - .saturating_add(T::DbWeight::get().writes(6 as u64)) - .saturating_add(T::DbWeight::get().writes((1 as u64).saturating_mul(c as u64))) + // Proof Size summary in bytes: + // Measured: `0 + v * (639 ±0) + e * (28 ±0)` + // Estimated: `5350105 + c * (2582 ±0) + v * (5721 ±6) + e * (123 ±0)` + // Minimum execution time: 21_844_965 nanoseconds. + Weight::from_parts(21_979_826_000, 5350105) + // Standard Error: 229_799 + .saturating_add(Weight::from_ref_time(24_976_612).saturating_mul(v.into())) + // Standard Error: 14_747 + .saturating_add(Weight::from_ref_time(1_025_848).saturating_mul(e.into())) + .saturating_add(T::DbWeight::get().reads(280_u64)) + .saturating_add(T::DbWeight::get().reads((1_u64).saturating_mul(c.into()))) + .saturating_add(T::DbWeight::get().reads((1_u64).saturating_mul(v.into()))) + .saturating_add(T::DbWeight::get().writes(6_u64)) + .saturating_add(T::DbWeight::get().writes((1_u64).saturating_mul(c.into()))) + .saturating_add(Weight::from_proof_size(2582).saturating_mul(c.into())) + .saturating_add(Weight::from_proof_size(5721).saturating_mul(v.into())) + .saturating_add(Weight::from_proof_size(123).saturating_mul(e.into())) } } // For backwards compatibility and tests impl WeightInfo for () { - // Storage: Elections Candidates (r:1 w:0) - // Storage: Elections Members (r:1 w:0) - // Storage: Elections RunnersUp (r:1 w:0) - // Storage: Elections Voting (r:1 w:1) - // Storage: Balances Locks (r:1 w:1) + /// Storage: Elections Candidates (r:1 w:0) + /// Proof Skipped: Elections Candidates (max_values: Some(1), max_size: None, mode: Measured) + /// Storage: Elections Members (r:1 w:0) + /// Proof Skipped: Elections Members (max_values: Some(1), max_size: None, mode: Measured) + /// Storage: Elections RunnersUp (r:1 w:0) + /// Proof Skipped: Elections RunnersUp (max_values: Some(1), max_size: None, mode: Measured) + /// Storage: Elections Voting (r:1 w:1) + /// Proof Skipped: Elections Voting (max_values: None, max_size: None, mode: Measured) + /// Storage: Balances Locks (r:1 w:1) + /// Proof: Balances Locks (max_values: None, max_size: Some(1299), added: 3774, mode: MaxEncodedLen) /// The range of component `v` is `[1, 16]`. fn vote_equal(v: u32, ) -> Weight { - // Minimum execution time: 38_496 nanoseconds. - Weight::from_ref_time(39_424_348 as u64) - // Standard Error: 3_547 - .saturating_add(Weight::from_ref_time(119_971 as u64).saturating_mul(v as u64)) - .saturating_add(RocksDbWeight::get().reads(5 as u64)) - .saturating_add(RocksDbWeight::get().writes(2 as u64)) + // Proof Size summary in bytes: + // Measured: `499 + v * (80 ±0)` + // Estimated: `9726 + v * (320 ±0)` + // Minimum execution time: 25_407 nanoseconds. + Weight::from_parts(26_035_742, 9726) + // Standard Error: 2_162 + .saturating_add(Weight::from_ref_time(120_321).saturating_mul(v.into())) + .saturating_add(RocksDbWeight::get().reads(5_u64)) + .saturating_add(RocksDbWeight::get().writes(2_u64)) + .saturating_add(Weight::from_proof_size(320).saturating_mul(v.into())) } - // Storage: Elections Candidates (r:1 w:0) - // Storage: Elections Members (r:1 w:0) - // Storage: Elections RunnersUp (r:1 w:0) - // Storage: Elections Voting (r:1 w:1) - // Storage: Balances Locks (r:1 w:1) + /// Storage: Elections Candidates (r:1 w:0) + /// Proof Skipped: Elections Candidates (max_values: Some(1), max_size: None, mode: Measured) + /// Storage: Elections Members (r:1 w:0) + /// Proof Skipped: Elections Members (max_values: Some(1), max_size: None, mode: Measured) + /// Storage: Elections RunnersUp (r:1 w:0) + /// Proof Skipped: Elections RunnersUp (max_values: Some(1), max_size: None, mode: Measured) + /// Storage: Elections Voting (r:1 w:1) + /// Proof Skipped: Elections Voting (max_values: None, max_size: None, mode: Measured) + /// Storage: Balances Locks (r:1 w:1) + /// Proof: Balances Locks (max_values: None, max_size: Some(1299), added: 3774, mode: MaxEncodedLen) /// The range of component `v` is `[2, 16]`. fn vote_more(v: u32, ) -> Weight { - // Minimum execution time: 49_459 nanoseconds. - Weight::from_ref_time(50_225_486 as u64) - // Standard Error: 3_160 - .saturating_add(Weight::from_ref_time(170_360 as u64).saturating_mul(v as u64)) - .saturating_add(RocksDbWeight::get().reads(5 as u64)) - .saturating_add(RocksDbWeight::get().writes(2 as u64)) + // Proof Size summary in bytes: + // Measured: `467 + v * (80 ±0)` + // Estimated: `9598 + v * (320 ±0)` + // Minimum execution time: 34_477 nanoseconds. + Weight::from_parts(35_197_694, 9598) + // Standard Error: 2_089 + .saturating_add(Weight::from_ref_time(116_792).saturating_mul(v.into())) + .saturating_add(RocksDbWeight::get().reads(5_u64)) + .saturating_add(RocksDbWeight::get().writes(2_u64)) + .saturating_add(Weight::from_proof_size(320).saturating_mul(v.into())) } - // Storage: Elections Candidates (r:1 w:0) - // Storage: Elections Members (r:1 w:0) - // Storage: Elections RunnersUp (r:1 w:0) - // Storage: Elections Voting (r:1 w:1) - // Storage: Balances Locks (r:1 w:1) + /// Storage: Elections Candidates (r:1 w:0) + /// Proof Skipped: Elections Candidates (max_values: Some(1), max_size: None, mode: Measured) + /// Storage: Elections Members (r:1 w:0) + /// Proof Skipped: Elections Members (max_values: Some(1), max_size: None, mode: Measured) + /// Storage: Elections RunnersUp (r:1 w:0) + /// Proof Skipped: Elections RunnersUp (max_values: Some(1), max_size: None, mode: Measured) + /// Storage: Elections Voting (r:1 w:1) + /// Proof Skipped: Elections Voting (max_values: None, max_size: None, mode: Measured) + /// Storage: Balances Locks (r:1 w:1) + /// Proof: Balances Locks (max_values: None, max_size: Some(1299), added: 3774, mode: MaxEncodedLen) /// The range of component `v` is `[2, 16]`. fn vote_less(v: u32, ) -> Weight { - // Minimum execution time: 48_712 nanoseconds. - Weight::from_ref_time(49_463_298 as u64) - // Standard Error: 2_678 - .saturating_add(Weight::from_ref_time(231_771 as u64).saturating_mul(v as u64)) - .saturating_add(RocksDbWeight::get().reads(5 as u64)) - .saturating_add(RocksDbWeight::get().writes(2 as u64)) + // Proof Size summary in bytes: + // Measured: `499 + v * (80 ±0)` + // Estimated: `9726 + v * (320 ±0)` + // Minimum execution time: 34_573 nanoseconds. + Weight::from_parts(35_254_508, 9726) + // Standard Error: 2_076 + .saturating_add(Weight::from_ref_time(110_656).saturating_mul(v.into())) + .saturating_add(RocksDbWeight::get().reads(5_u64)) + .saturating_add(RocksDbWeight::get().writes(2_u64)) + .saturating_add(Weight::from_proof_size(320).saturating_mul(v.into())) } - // Storage: Elections Voting (r:1 w:1) - // Storage: Balances Locks (r:1 w:1) + /// Storage: Elections Voting (r:1 w:1) + /// Proof Skipped: Elections Voting (max_values: None, max_size: None, mode: Measured) + /// Storage: Balances Locks (r:1 w:1) + /// Proof: Balances Locks (max_values: None, max_size: Some(1299), added: 3774, mode: MaxEncodedLen) fn remove_voter() -> Weight { - // Minimum execution time: 48_359 nanoseconds. - Weight::from_ref_time(48_767_000 as u64) - .saturating_add(RocksDbWeight::get().reads(2 as u64)) - .saturating_add(RocksDbWeight::get().writes(2 as u64)) + // Proof Size summary in bytes: + // Measured: `989` + // Estimated: `7238` + // Minimum execution time: 31_469 nanoseconds. + Weight::from_parts(31_877_000, 7238) + .saturating_add(RocksDbWeight::get().reads(2_u64)) + .saturating_add(RocksDbWeight::get().writes(2_u64)) } - // Storage: Elections Candidates (r:1 w:1) - // Storage: Elections Members (r:1 w:0) - // Storage: Elections RunnersUp (r:1 w:0) + /// Storage: Elections Candidates (r:1 w:1) + /// Proof Skipped: Elections Candidates (max_values: Some(1), max_size: None, mode: Measured) + /// Storage: Elections Members (r:1 w:0) + /// Proof Skipped: Elections Members (max_values: Some(1), max_size: None, mode: Measured) + /// Storage: Elections RunnersUp (r:1 w:0) + /// Proof Skipped: Elections RunnersUp (max_values: Some(1), max_size: None, mode: Measured) /// The range of component `c` is `[1, 1000]`. fn submit_candidacy(c: u32, ) -> Weight { - // Minimum execution time: 43_369 nanoseconds. - Weight::from_ref_time(49_587_113 as u64) - // Standard Error: 1_008 - .saturating_add(Weight::from_ref_time(77_752 as u64).saturating_mul(c as u64)) - .saturating_add(RocksDbWeight::get().reads(3 as u64)) - .saturating_add(RocksDbWeight::get().writes(1 as u64)) + // Proof Size summary in bytes: + // Measured: `1687 + c * (48 ±0)` + // Estimated: `6540 + c * (144 ±0)` + // Minimum execution time: 26_969 nanoseconds. + Weight::from_parts(28_584_266, 6540) + // Standard Error: 93 + .saturating_add(Weight::from_ref_time(52_393).saturating_mul(c.into())) + .saturating_add(RocksDbWeight::get().reads(3_u64)) + .saturating_add(RocksDbWeight::get().writes(1_u64)) + .saturating_add(Weight::from_proof_size(144).saturating_mul(c.into())) } - // Storage: Elections Candidates (r:1 w:1) + /// Storage: Elections Candidates (r:1 w:1) + /// Proof Skipped: Elections Candidates (max_values: Some(1), max_size: None, mode: Measured) /// The range of component `c` is `[1, 1000]`. fn renounce_candidacy_candidate(c: u32, ) -> Weight { - // Minimum execution time: 41_321 nanoseconds. - Weight::from_ref_time(50_803_289 as u64) - // Standard Error: 1_159 - .saturating_add(Weight::from_ref_time(57_239 as u64).saturating_mul(c as u64)) - .saturating_add(RocksDbWeight::get().reads(1 as u64)) - .saturating_add(RocksDbWeight::get().writes(1 as u64)) + // Proof Size summary in bytes: + // Measured: `348 + c * (48 ±0)` + // Estimated: `830 + c * (48 ±0)` + // Minimum execution time: 23_635 nanoseconds. + Weight::from_parts(23_482_193, 830) + // Standard Error: 103 + .saturating_add(Weight::from_ref_time(33_759).saturating_mul(c.into())) + .saturating_add(RocksDbWeight::get().reads(1_u64)) + .saturating_add(RocksDbWeight::get().writes(1_u64)) + .saturating_add(Weight::from_proof_size(48).saturating_mul(c.into())) } - // Storage: Elections Members (r:1 w:1) - // Storage: Elections RunnersUp (r:1 w:1) - // Storage: Council Prime (r:1 w:1) - // Storage: Council Proposals (r:1 w:0) - // Storage: Council Members (r:0 w:1) + /// Storage: Elections Members (r:1 w:1) + /// Proof Skipped: Elections Members (max_values: Some(1), max_size: None, mode: Measured) + /// Storage: Elections RunnersUp (r:1 w:1) + /// Proof Skipped: Elections RunnersUp (max_values: Some(1), max_size: None, mode: Measured) + /// Storage: Council Prime (r:1 w:1) + /// Proof Skipped: Council Prime (max_values: Some(1), max_size: None, mode: Measured) + /// Storage: Council Proposals (r:1 w:0) + /// Proof Skipped: Council Proposals (max_values: Some(1), max_size: None, mode: Measured) + /// Storage: Council Members (r:0 w:1) + /// Proof Skipped: Council Members (max_values: Some(1), max_size: None, mode: Measured) fn renounce_candidacy_members() -> Weight { - // Minimum execution time: 53_542 nanoseconds. - Weight::from_ref_time(54_481_000 as u64) - .saturating_add(RocksDbWeight::get().reads(4 as u64)) - .saturating_add(RocksDbWeight::get().writes(4 as u64)) + // Proof Size summary in bytes: + // Measured: `2027` + // Estimated: `12115` + // Minimum execution time: 39_124 nanoseconds. + Weight::from_parts(39_575_000, 12115) + .saturating_add(RocksDbWeight::get().reads(4_u64)) + .saturating_add(RocksDbWeight::get().writes(4_u64)) } - // Storage: Elections RunnersUp (r:1 w:1) + /// Storage: Elections RunnersUp (r:1 w:1) + /// Proof Skipped: Elections RunnersUp (max_values: Some(1), max_size: None, mode: Measured) fn renounce_candidacy_runners_up() -> Weight { - // Minimum execution time: 41_825 nanoseconds. - Weight::from_ref_time(42_248_000 as u64) - .saturating_add(RocksDbWeight::get().reads(1 as u64)) - .saturating_add(RocksDbWeight::get().writes(1 as u64)) + // Proof Size summary in bytes: + // Measured: `975` + // Estimated: `1470` + // Minimum execution time: 25_377 nanoseconds. + Weight::from_parts(25_696_000, 1470) + .saturating_add(RocksDbWeight::get().reads(1_u64)) + .saturating_add(RocksDbWeight::get().writes(1_u64)) } - // Storage: Benchmark Override (r:0 w:0) + /// Storage: Benchmark Override (r:0 w:0) + /// Proof Skipped: Benchmark Override (max_values: None, max_size: None, mode: Measured) fn remove_member_without_replacement() -> Weight { + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` // Minimum execution time: 2_000_000_000 nanoseconds. - Weight::from_ref_time(2_000_000_000_000 as u64) + Weight::from_ref_time(2_000_000_000_000) } - // Storage: Elections Members (r:1 w:1) - // Storage: System Account (r:1 w:1) - // Storage: Elections RunnersUp (r:1 w:1) - // Storage: Council Prime (r:1 w:1) - // Storage: Council Proposals (r:1 w:0) - // Storage: Council Members (r:0 w:1) + /// Storage: Elections Members (r:1 w:1) + /// Proof Skipped: Elections Members (max_values: Some(1), max_size: None, mode: Measured) + /// Storage: System Account (r:1 w:1) + /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) + /// Storage: Elections RunnersUp (r:1 w:1) + /// Proof Skipped: Elections RunnersUp (max_values: Some(1), max_size: None, mode: Measured) + /// Storage: Council Prime (r:1 w:1) + /// Proof Skipped: Council Prime (max_values: Some(1), max_size: None, mode: Measured) + /// Storage: Council Proposals (r:1 w:0) + /// Proof Skipped: Council Proposals (max_values: Some(1), max_size: None, mode: Measured) + /// Storage: Council Members (r:0 w:1) + /// Proof Skipped: Council Members (max_values: Some(1), max_size: None, mode: Measured) fn remove_member_with_replacement() -> Weight { - // Minimum execution time: 62_600 nanoseconds. - Weight::from_ref_time(63_152_000 as u64) - .saturating_add(RocksDbWeight::get().reads(5 as u64)) - .saturating_add(RocksDbWeight::get().writes(5 as u64)) + // Proof Size summary in bytes: + // Measured: `2027` + // Estimated: `14718` + // Minimum execution time: 44_919 nanoseconds. + Weight::from_parts(45_548_000, 14718) + .saturating_add(RocksDbWeight::get().reads(5_u64)) + .saturating_add(RocksDbWeight::get().writes(5_u64)) } - // Storage: Elections Voting (r:5001 w:5000) - // Storage: Elections Members (r:1 w:0) - // Storage: Elections RunnersUp (r:1 w:0) - // Storage: Elections Candidates (r:1 w:0) - // Storage: Balances Locks (r:5000 w:5000) - // Storage: System Account (r:5000 w:5000) + /// Storage: Elections Voting (r:10001 w:10000) + /// Proof Skipped: Elections Voting (max_values: None, max_size: None, mode: Measured) + /// Storage: Elections Members (r:1 w:0) + /// Proof Skipped: Elections Members (max_values: Some(1), max_size: None, mode: Measured) + /// Storage: Elections RunnersUp (r:1 w:0) + /// Proof Skipped: Elections RunnersUp (max_values: Some(1), max_size: None, mode: Measured) + /// Storage: Elections Candidates (r:1 w:0) + /// Proof Skipped: Elections Candidates (max_values: Some(1), max_size: None, mode: Measured) + /// Storage: Balances Locks (r:10000 w:10000) + /// Proof: Balances Locks (max_values: None, max_size: Some(1299), added: 3774, mode: MaxEncodedLen) + /// Storage: System Account (r:10000 w:10000) + /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) /// The range of component `v` is `[5000, 10000]`. /// The range of component `d` is `[0, 5000]`. fn clean_defunct_voters(v: u32, _d: u32, ) -> Weight { - // Minimum execution time: 297_149_264 nanoseconds. - Weight::from_ref_time(297_898_499_000 as u64) - // Standard Error: 263_819 - .saturating_add(Weight::from_ref_time(37_914_985 as u64).saturating_mul(v as u64)) - .saturating_add(RocksDbWeight::get().reads(4 as u64)) - .saturating_add(RocksDbWeight::get().reads((3 as u64).saturating_mul(v as u64))) - .saturating_add(RocksDbWeight::get().writes((3 as u64).saturating_mul(v as u64))) + // Proof Size summary in bytes: + // Measured: `36028 + v * (872 ±0)` + // Estimated: `149172 + v * (12340 ±0)` + // Minimum execution time: 297_544_939 nanoseconds. + Weight::from_parts(298_088_024_000, 149172) + // Standard Error: 264_599 + .saturating_add(Weight::from_ref_time(38_142_857).saturating_mul(v.into())) + .saturating_add(RocksDbWeight::get().reads(4_u64)) + .saturating_add(RocksDbWeight::get().reads((3_u64).saturating_mul(v.into()))) + .saturating_add(RocksDbWeight::get().writes((3_u64).saturating_mul(v.into()))) + .saturating_add(Weight::from_proof_size(12340).saturating_mul(v.into())) } - // Storage: Elections Candidates (r:1 w:1) - // Storage: Elections Members (r:1 w:1) - // Storage: Elections RunnersUp (r:1 w:1) - // Storage: Elections Voting (r:10001 w:0) - // Storage: Council Proposals (r:1 w:0) - // Storage: Elections ElectionRounds (r:1 w:1) - // Storage: Council Members (r:0 w:1) - // Storage: Council Prime (r:0 w:1) - // Storage: System Account (r:1 w:1) + /// Storage: Elections Candidates (r:1 w:1) + /// Proof Skipped: Elections Candidates (max_values: Some(1), max_size: None, mode: Measured) + /// Storage: Elections Members (r:1 w:1) + /// Proof Skipped: Elections Members (max_values: Some(1), max_size: None, mode: Measured) + /// Storage: Elections RunnersUp (r:1 w:1) + /// Proof Skipped: Elections RunnersUp (max_values: Some(1), max_size: None, mode: Measured) + /// Storage: Elections Voting (r:10001 w:0) + /// Proof Skipped: Elections Voting (max_values: None, max_size: None, mode: Measured) + /// Storage: Council Proposals (r:1 w:0) + /// Proof Skipped: Council Proposals (max_values: Some(1), max_size: None, mode: Measured) + /// Storage: System Account (r:980 w:980) + /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) + /// Storage: Elections ElectionRounds (r:1 w:1) + /// Proof Skipped: Elections ElectionRounds (max_values: Some(1), max_size: None, mode: Measured) + /// Storage: Council Members (r:0 w:1) + /// Proof Skipped: Council Members (max_values: Some(1), max_size: None, mode: Measured) + /// Storage: Council Prime (r:0 w:1) + /// Proof Skipped: Council Prime (max_values: Some(1), max_size: None, mode: Measured) /// The range of component `c` is `[1, 1000]`. /// The range of component `v` is `[1, 10000]`. /// The range of component `e` is `[10000, 160000]`. fn election_phragmen(c: u32, v: u32, e: u32, ) -> Weight { - // Minimum execution time: 22_034_317 nanoseconds. - Weight::from_ref_time(22_110_020_000 as u64) - // Standard Error: 235_528 - .saturating_add(Weight::from_ref_time(25_553_585 as u64).saturating_mul(v as u64)) - // Standard Error: 15_114 - .saturating_add(Weight::from_ref_time(1_032_330 as u64).saturating_mul(e as u64)) - .saturating_add(RocksDbWeight::get().reads(280 as u64)) - .saturating_add(RocksDbWeight::get().reads((1 as u64).saturating_mul(c as u64))) - .saturating_add(RocksDbWeight::get().reads((1 as u64).saturating_mul(v as u64))) - .saturating_add(RocksDbWeight::get().writes(6 as u64)) - .saturating_add(RocksDbWeight::get().writes((1 as u64).saturating_mul(c as u64))) + // Proof Size summary in bytes: + // Measured: `0 + v * (639 ±0) + e * (28 ±0)` + // Estimated: `5350105 + c * (2582 ±0) + v * (5721 ±6) + e * (123 ±0)` + // Minimum execution time: 21_844_965 nanoseconds. + Weight::from_parts(21_979_826_000, 5350105) + // Standard Error: 229_799 + .saturating_add(Weight::from_ref_time(24_976_612).saturating_mul(v.into())) + // Standard Error: 14_747 + .saturating_add(Weight::from_ref_time(1_025_848).saturating_mul(e.into())) + .saturating_add(RocksDbWeight::get().reads(280_u64)) + .saturating_add(RocksDbWeight::get().reads((1_u64).saturating_mul(c.into()))) + .saturating_add(RocksDbWeight::get().reads((1_u64).saturating_mul(v.into()))) + .saturating_add(RocksDbWeight::get().writes(6_u64)) + .saturating_add(RocksDbWeight::get().writes((1_u64).saturating_mul(c.into()))) + .saturating_add(Weight::from_proof_size(2582).saturating_mul(c.into())) + .saturating_add(Weight::from_proof_size(5721).saturating_mul(v.into())) + .saturating_add(Weight::from_proof_size(123).saturating_mul(e.into())) } } diff --git a/frame/identity/src/weights.rs b/frame/identity/src/weights.rs index 1f2e8f98e..7bcbf539d 100644 --- a/frame/identity/src/weights.rs +++ b/frame/identity/src/weights.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2022 Parity Technologies (UK) Ltd. +// Copyright (C) 2023 Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); @@ -18,7 +18,8 @@ //! Autogenerated weights for pallet_identity //! //! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev -//! DATE: 2022-11-07, STEPS: `50`, REPEAT: 20, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! DATE: 2023-01-24, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! WORST CASE MAP SIZE: `1000000` //! HOSTNAME: `bm2`, CPU: `Intel(R) Core(TM) i7-7700K CPU @ 4.20GHz` //! EXECUTION: Some(Wasm), WASM-EXECUTION: Compiled, CHAIN: Some("dev"), DB CACHE: 1024 @@ -68,420 +69,584 @@ pub trait WeightInfo { /// Weights for pallet_identity using the Substrate node and recommended hardware. pub struct SubstrateWeight(PhantomData); impl WeightInfo for SubstrateWeight { - // Storage: Identity Registrars (r:1 w:1) + /// Storage: Identity Registrars (r:1 w:1) + /// Proof: Identity Registrars (max_values: Some(1), max_size: Some(1141), added: 1636, mode: MaxEncodedLen) /// The range of component `r` is `[1, 19]`. fn add_registrar(r: u32, ) -> Weight { - // Minimum execution time: 20_269 nanoseconds. - Weight::from_ref_time(21_910_543 as u64) - // Standard Error: 4_604 - .saturating_add(Weight::from_ref_time(223_104 as u64).saturating_mul(r as u64)) - .saturating_add(T::DbWeight::get().reads(1 as u64)) - .saturating_add(T::DbWeight::get().writes(1 as u64)) - } - // Storage: Identity IdentityOf (r:1 w:1) + // Proof Size summary in bytes: + // Measured: `64 + r * (57 ±0)` + // Estimated: `1636` + // Minimum execution time: 10_964 nanoseconds. + Weight::from_parts(11_800_935, 1636) + // Standard Error: 1_334 + .saturating_add(Weight::from_ref_time(96_038).saturating_mul(r.into())) + .saturating_add(T::DbWeight::get().reads(1_u64)) + .saturating_add(T::DbWeight::get().writes(1_u64)) + } + /// Storage: Identity IdentityOf (r:1 w:1) + /// Proof: Identity IdentityOf (max_values: None, max_size: Some(7538), added: 10013, mode: MaxEncodedLen) /// The range of component `r` is `[1, 20]`. /// The range of component `x` is `[0, 100]`. fn set_identity(r: u32, x: u32, ) -> Weight { - // Minimum execution time: 41_872 nanoseconds. - Weight::from_ref_time(40_230_216 as u64) - // Standard Error: 2_342 - .saturating_add(Weight::from_ref_time(145_168 as u64).saturating_mul(r as u64)) - // Standard Error: 457 - .saturating_add(Weight::from_ref_time(291_732 as u64).saturating_mul(x as u64)) - .saturating_add(T::DbWeight::get().reads(1 as u64)) - .saturating_add(T::DbWeight::get().writes(1 as u64)) - } - // Storage: Identity IdentityOf (r:1 w:0) - // Storage: Identity SubsOf (r:1 w:1) - // Storage: Identity SuperOf (r:2 w:2) + // Proof Size summary in bytes: + // Measured: `474 + r * (5 ±0)` + // Estimated: `10013` + // Minimum execution time: 26_400 nanoseconds. + Weight::from_parts(26_060_549, 10013) + // Standard Error: 1_561 + .saturating_add(Weight::from_ref_time(72_083).saturating_mul(r.into())) + // Standard Error: 304 + .saturating_add(Weight::from_ref_time(306_994).saturating_mul(x.into())) + .saturating_add(T::DbWeight::get().reads(1_u64)) + .saturating_add(T::DbWeight::get().writes(1_u64)) + } + /// Storage: Identity IdentityOf (r:1 w:0) + /// Proof: Identity IdentityOf (max_values: None, max_size: Some(7538), added: 10013, mode: MaxEncodedLen) + /// Storage: Identity SubsOf (r:1 w:1) + /// Proof: Identity SubsOf (max_values: None, max_size: Some(3258), added: 5733, mode: MaxEncodedLen) + /// Storage: Identity SuperOf (r:100 w:100) + /// Proof: Identity SuperOf (max_values: None, max_size: Some(114), added: 2589, mode: MaxEncodedLen) /// The range of component `s` is `[0, 100]`. fn set_subs_new(s: u32, ) -> Weight { - // Minimum execution time: 12_024 nanoseconds. - Weight::from_ref_time(32_550_819 as u64) - // Standard Error: 5_057 - .saturating_add(Weight::from_ref_time(2_521_245 as u64).saturating_mul(s as u64)) - .saturating_add(T::DbWeight::get().reads(2 as u64)) - .saturating_add(T::DbWeight::get().reads((1 as u64).saturating_mul(s as u64))) - .saturating_add(T::DbWeight::get().writes(1 as u64)) - .saturating_add(T::DbWeight::get().writes((1 as u64).saturating_mul(s as u64))) - } - // Storage: Identity IdentityOf (r:1 w:0) - // Storage: Identity SubsOf (r:1 w:1) - // Storage: Identity SuperOf (r:0 w:2) + // Proof Size summary in bytes: + // Measured: `101` + // Estimated: `15746 + s * (2589 ±0)` + // Minimum execution time: 8_492 nanoseconds. + Weight::from_parts(21_645_924, 15746) + // Standard Error: 3_452 + .saturating_add(Weight::from_ref_time(2_442_604).saturating_mul(s.into())) + .saturating_add(T::DbWeight::get().reads(2_u64)) + .saturating_add(T::DbWeight::get().reads((1_u64).saturating_mul(s.into()))) + .saturating_add(T::DbWeight::get().writes(1_u64)) + .saturating_add(T::DbWeight::get().writes((1_u64).saturating_mul(s.into()))) + .saturating_add(Weight::from_proof_size(2589).saturating_mul(s.into())) + } + /// Storage: Identity IdentityOf (r:1 w:0) + /// Proof: Identity IdentityOf (max_values: None, max_size: Some(7538), added: 10013, mode: MaxEncodedLen) + /// Storage: Identity SubsOf (r:1 w:1) + /// Proof: Identity SubsOf (max_values: None, max_size: Some(3258), added: 5733, mode: MaxEncodedLen) + /// Storage: Identity SuperOf (r:0 w:100) + /// Proof: Identity SuperOf (max_values: None, max_size: Some(114), added: 2589, mode: MaxEncodedLen) /// The range of component `p` is `[0, 100]`. fn set_subs_old(p: u32, ) -> Weight { - // Minimum execution time: 12_232 nanoseconds. - Weight::from_ref_time(34_009_761 as u64) - // Standard Error: 5_047 - .saturating_add(Weight::from_ref_time(1_113_100 as u64).saturating_mul(p as u64)) - .saturating_add(T::DbWeight::get().reads(2 as u64)) - .saturating_add(T::DbWeight::get().writes(1 as u64)) - .saturating_add(T::DbWeight::get().writes((1 as u64).saturating_mul(p as u64))) - } - // Storage: Identity SubsOf (r:1 w:1) - // Storage: Identity IdentityOf (r:1 w:1) - // Storage: Identity SuperOf (r:0 w:100) + // Proof Size summary in bytes: + // Measured: `226 + p * (32 ±0)` + // Estimated: `15746` + // Minimum execution time: 8_488 nanoseconds. + Weight::from_parts(20_202_601, 15746) + // Standard Error: 2_834 + .saturating_add(Weight::from_ref_time(1_082_941).saturating_mul(p.into())) + .saturating_add(T::DbWeight::get().reads(2_u64)) + .saturating_add(T::DbWeight::get().writes(1_u64)) + .saturating_add(T::DbWeight::get().writes((1_u64).saturating_mul(p.into()))) + } + /// Storage: Identity SubsOf (r:1 w:1) + /// Proof: Identity SubsOf (max_values: None, max_size: Some(3258), added: 5733, mode: MaxEncodedLen) + /// Storage: Identity IdentityOf (r:1 w:1) + /// Proof: Identity IdentityOf (max_values: None, max_size: Some(7538), added: 10013, mode: MaxEncodedLen) + /// Storage: Identity SuperOf (r:0 w:100) + /// Proof: Identity SuperOf (max_values: None, max_size: Some(114), added: 2589, mode: MaxEncodedLen) /// The range of component `r` is `[1, 20]`. /// The range of component `s` is `[0, 100]`. /// The range of component `x` is `[0, 100]`. fn clear_identity(r: u32, s: u32, x: u32, ) -> Weight { - // Minimum execution time: 57_144 nanoseconds. - Weight::from_ref_time(41_559_247 as u64) - // Standard Error: 9_996 - .saturating_add(Weight::from_ref_time(146_770 as u64).saturating_mul(r as u64)) - // Standard Error: 1_952 - .saturating_add(Weight::from_ref_time(1_086_673 as u64).saturating_mul(s as u64)) - // Standard Error: 1_952 - .saturating_add(Weight::from_ref_time(162_481 as u64).saturating_mul(x as u64)) - .saturating_add(T::DbWeight::get().reads(2 as u64)) - .saturating_add(T::DbWeight::get().writes(2 as u64)) - .saturating_add(T::DbWeight::get().writes((1 as u64).saturating_mul(s as u64))) - } - // Storage: Identity Registrars (r:1 w:0) - // Storage: Identity IdentityOf (r:1 w:1) + // Proof Size summary in bytes: + // Measured: `533 + r * (5 ±0) + s * (32 ±0) + x * (66 ±0)` + // Estimated: `15746` + // Minimum execution time: 41_319 nanoseconds. + Weight::from_parts(25_850_055, 15746) + // Standard Error: 4_144 + .saturating_add(Weight::from_ref_time(59_619).saturating_mul(r.into())) + // Standard Error: 809 + .saturating_add(Weight::from_ref_time(1_076_550).saturating_mul(s.into())) + // Standard Error: 809 + .saturating_add(Weight::from_ref_time(163_191).saturating_mul(x.into())) + .saturating_add(T::DbWeight::get().reads(2_u64)) + .saturating_add(T::DbWeight::get().writes(2_u64)) + .saturating_add(T::DbWeight::get().writes((1_u64).saturating_mul(s.into()))) + } + /// Storage: Identity Registrars (r:1 w:0) + /// Proof: Identity Registrars (max_values: Some(1), max_size: Some(1141), added: 1636, mode: MaxEncodedLen) + /// Storage: Identity IdentityOf (r:1 w:1) + /// Proof: Identity IdentityOf (max_values: None, max_size: Some(7538), added: 10013, mode: MaxEncodedLen) /// The range of component `r` is `[1, 20]`. /// The range of component `x` is `[0, 100]`. fn request_judgement(r: u32, x: u32, ) -> Weight { - // Minimum execution time: 44_726 nanoseconds. - Weight::from_ref_time(41_637_308 as u64) - // Standard Error: 1_907 - .saturating_add(Weight::from_ref_time(219_078 as u64).saturating_mul(r as u64)) - // Standard Error: 372 - .saturating_add(Weight::from_ref_time(309_888 as u64).saturating_mul(x as u64)) - .saturating_add(T::DbWeight::get().reads(2 as u64)) - .saturating_add(T::DbWeight::get().writes(1 as u64)) - } - // Storage: Identity IdentityOf (r:1 w:1) + // Proof Size summary in bytes: + // Measured: `431 + r * (57 ±0) + x * (66 ±0)` + // Estimated: `11649` + // Minimum execution time: 28_118 nanoseconds. + Weight::from_parts(27_359_471, 11649) + // Standard Error: 2_707 + .saturating_add(Weight::from_ref_time(107_279).saturating_mul(r.into())) + // Standard Error: 528 + .saturating_add(Weight::from_ref_time(325_165).saturating_mul(x.into())) + .saturating_add(T::DbWeight::get().reads(2_u64)) + .saturating_add(T::DbWeight::get().writes(1_u64)) + } + /// Storage: Identity IdentityOf (r:1 w:1) + /// Proof: Identity IdentityOf (max_values: None, max_size: Some(7538), added: 10013, mode: MaxEncodedLen) /// The range of component `r` is `[1, 20]`. /// The range of component `x` is `[0, 100]`. fn cancel_request(r: u32, x: u32, ) -> Weight { - // Minimum execution time: 39_719 nanoseconds. - Weight::from_ref_time(38_008_751 as u64) - // Standard Error: 2_394 - .saturating_add(Weight::from_ref_time(181_870 as u64).saturating_mul(r as u64)) - // Standard Error: 467 - .saturating_add(Weight::from_ref_time(314_990 as u64).saturating_mul(x as u64)) - .saturating_add(T::DbWeight::get().reads(1 as u64)) - .saturating_add(T::DbWeight::get().writes(1 as u64)) - } - // Storage: Identity Registrars (r:1 w:1) + // Proof Size summary in bytes: + // Measured: `430 + x * (66 ±0)` + // Estimated: `10013` + // Minimum execution time: 24_817 nanoseconds. + Weight::from_parts(24_749_808, 10013) + // Standard Error: 1_938 + .saturating_add(Weight::from_ref_time(63_396).saturating_mul(r.into())) + // Standard Error: 378 + .saturating_add(Weight::from_ref_time(327_083).saturating_mul(x.into())) + .saturating_add(T::DbWeight::get().reads(1_u64)) + .saturating_add(T::DbWeight::get().writes(1_u64)) + } + /// Storage: Identity Registrars (r:1 w:1) + /// Proof: Identity Registrars (max_values: Some(1), max_size: Some(1141), added: 1636, mode: MaxEncodedLen) /// The range of component `r` is `[1, 19]`. fn set_fee(r: u32, ) -> Weight { - // Minimum execution time: 10_634 nanoseconds. - Weight::from_ref_time(11_383_704 as u64) - // Standard Error: 2_250 - .saturating_add(Weight::from_ref_time(193_094 as u64).saturating_mul(r as u64)) - .saturating_add(T::DbWeight::get().reads(1 as u64)) - .saturating_add(T::DbWeight::get().writes(1 as u64)) - } - // Storage: Identity Registrars (r:1 w:1) + // Proof Size summary in bytes: + // Measured: `121 + r * (57 ±0)` + // Estimated: `1636` + // Minimum execution time: 6_664 nanoseconds. + Weight::from_parts(7_286_307, 1636) + // Standard Error: 1_560 + .saturating_add(Weight::from_ref_time(96_416).saturating_mul(r.into())) + .saturating_add(T::DbWeight::get().reads(1_u64)) + .saturating_add(T::DbWeight::get().writes(1_u64)) + } + /// Storage: Identity Registrars (r:1 w:1) + /// Proof: Identity Registrars (max_values: Some(1), max_size: Some(1141), added: 1636, mode: MaxEncodedLen) /// The range of component `r` is `[1, 19]`. fn set_account_id(r: u32, ) -> Weight { - // Minimum execution time: 10_840 nanoseconds. - Weight::from_ref_time(11_638_740 as u64) - // Standard Error: 1_985 - .saturating_add(Weight::from_ref_time(193_016 as u64).saturating_mul(r as u64)) - .saturating_add(T::DbWeight::get().reads(1 as u64)) - .saturating_add(T::DbWeight::get().writes(1 as u64)) - } - // Storage: Identity Registrars (r:1 w:1) + // Proof Size summary in bytes: + // Measured: `121 + r * (57 ±0)` + // Estimated: `1636` + // Minimum execution time: 7_054 nanoseconds. + Weight::from_parts(7_382_954, 1636) + // Standard Error: 1_621 + .saturating_add(Weight::from_ref_time(101_595).saturating_mul(r.into())) + .saturating_add(T::DbWeight::get().reads(1_u64)) + .saturating_add(T::DbWeight::get().writes(1_u64)) + } + /// Storage: Identity Registrars (r:1 w:1) + /// Proof: Identity Registrars (max_values: Some(1), max_size: Some(1141), added: 1636, mode: MaxEncodedLen) /// The range of component `r` is `[1, 19]`. fn set_fields(r: u32, ) -> Weight { - // Minimum execution time: 10_748 nanoseconds. - Weight::from_ref_time(11_346_901 as u64) - // Standard Error: 2_132 - .saturating_add(Weight::from_ref_time(196_630 as u64).saturating_mul(r as u64)) - .saturating_add(T::DbWeight::get().reads(1 as u64)) - .saturating_add(T::DbWeight::get().writes(1 as u64)) - } - // Storage: Identity Registrars (r:1 w:0) - // Storage: Identity IdentityOf (r:1 w:1) + // Proof Size summary in bytes: + // Measured: `121 + r * (57 ±0)` + // Estimated: `1636` + // Minimum execution time: 6_659 nanoseconds. + Weight::from_parts(7_188_883, 1636) + // Standard Error: 1_377 + .saturating_add(Weight::from_ref_time(98_965).saturating_mul(r.into())) + .saturating_add(T::DbWeight::get().reads(1_u64)) + .saturating_add(T::DbWeight::get().writes(1_u64)) + } + /// Storage: Identity Registrars (r:1 w:0) + /// Proof: Identity Registrars (max_values: Some(1), max_size: Some(1141), added: 1636, mode: MaxEncodedLen) + /// Storage: Identity IdentityOf (r:1 w:1) + /// Proof: Identity IdentityOf (max_values: None, max_size: Some(7538), added: 10013, mode: MaxEncodedLen) /// The range of component `r` is `[1, 19]`. /// The range of component `x` is `[0, 100]`. fn provide_judgement(r: u32, x: u32, ) -> Weight { - // Minimum execution time: 33_682 nanoseconds. - Weight::from_ref_time(31_336_603 as u64) - // Standard Error: 3_056 - .saturating_add(Weight::from_ref_time(200_403 as u64).saturating_mul(r as u64)) - // Standard Error: 565 - .saturating_add(Weight::from_ref_time(525_142 as u64).saturating_mul(x as u64)) - .saturating_add(T::DbWeight::get().reads(2 as u64)) - .saturating_add(T::DbWeight::get().writes(1 as u64)) - } - // Storage: Identity SubsOf (r:1 w:1) - // Storage: Identity IdentityOf (r:1 w:1) - // Storage: System Account (r:1 w:1) - // Storage: Identity SuperOf (r:0 w:100) + // Proof Size summary in bytes: + // Measured: `509 + r * (57 ±0) + x * (66 ±0)` + // Estimated: `11649` + // Minimum execution time: 21_567 nanoseconds. + Weight::from_parts(21_015_310, 11649) + // Standard Error: 2_516 + .saturating_add(Weight::from_ref_time(123_992).saturating_mul(r.into())) + // Standard Error: 465 + .saturating_add(Weight::from_ref_time(552_116).saturating_mul(x.into())) + .saturating_add(T::DbWeight::get().reads(2_u64)) + .saturating_add(T::DbWeight::get().writes(1_u64)) + } + /// Storage: Identity SubsOf (r:1 w:1) + /// Proof: Identity SubsOf (max_values: None, max_size: Some(3258), added: 5733, mode: MaxEncodedLen) + /// Storage: Identity IdentityOf (r:1 w:1) + /// Proof: Identity IdentityOf (max_values: None, max_size: Some(7538), added: 10013, mode: MaxEncodedLen) + /// Storage: System Account (r:1 w:1) + /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) + /// Storage: Identity SuperOf (r:0 w:100) + /// Proof: Identity SuperOf (max_values: None, max_size: Some(114), added: 2589, mode: MaxEncodedLen) /// The range of component `r` is `[1, 20]`. /// The range of component `s` is `[0, 100]`. /// The range of component `x` is `[0, 100]`. fn kill_identity(r: u32, s: u32, x: u32, ) -> Weight { - // Minimum execution time: 68_794 nanoseconds. - Weight::from_ref_time(52_114_486 as u64) - // Standard Error: 4_808 - .saturating_add(Weight::from_ref_time(153_462 as u64).saturating_mul(r as u64)) - // Standard Error: 939 - .saturating_add(Weight::from_ref_time(1_084_612 as u64).saturating_mul(s as u64)) - // Standard Error: 939 - .saturating_add(Weight::from_ref_time(170_112 as u64).saturating_mul(x as u64)) - .saturating_add(T::DbWeight::get().reads(3 as u64)) - .saturating_add(T::DbWeight::get().writes(3 as u64)) - .saturating_add(T::DbWeight::get().writes((1 as u64).saturating_mul(s as u64))) - } - // Storage: Identity IdentityOf (r:1 w:0) - // Storage: Identity SuperOf (r:1 w:1) - // Storage: Identity SubsOf (r:1 w:1) + // Proof Size summary in bytes: + // Measured: `772 + r * (5 ±0) + s * (32 ±0) + x * (66 ±0)` + // Estimated: `18349` + // Minimum execution time: 52_881 nanoseconds. + Weight::from_parts(38_504_388, 18349) + // Standard Error: 3_909 + .saturating_add(Weight::from_ref_time(51_452).saturating_mul(r.into())) + // Standard Error: 763 + .saturating_add(Weight::from_ref_time(1_069_924).saturating_mul(s.into())) + // Standard Error: 763 + .saturating_add(Weight::from_ref_time(164_906).saturating_mul(x.into())) + .saturating_add(T::DbWeight::get().reads(3_u64)) + .saturating_add(T::DbWeight::get().writes(3_u64)) + .saturating_add(T::DbWeight::get().writes((1_u64).saturating_mul(s.into()))) + } + /// Storage: Identity IdentityOf (r:1 w:0) + /// Proof: Identity IdentityOf (max_values: None, max_size: Some(7538), added: 10013, mode: MaxEncodedLen) + /// Storage: Identity SuperOf (r:1 w:1) + /// Proof: Identity SuperOf (max_values: None, max_size: Some(114), added: 2589, mode: MaxEncodedLen) + /// Storage: Identity SubsOf (r:1 w:1) + /// Proof: Identity SubsOf (max_values: None, max_size: Some(3258), added: 5733, mode: MaxEncodedLen) /// The range of component `s` is `[0, 99]`. fn add_sub(s: u32, ) -> Weight { - // Minimum execution time: 37_914 nanoseconds. - Weight::from_ref_time(43_488_083 as u64) - // Standard Error: 1_631 - .saturating_add(Weight::from_ref_time(118_845 as u64).saturating_mul(s as u64)) - .saturating_add(T::DbWeight::get().reads(3 as u64)) - .saturating_add(T::DbWeight::get().writes(2 as u64)) - } - // Storage: Identity IdentityOf (r:1 w:0) - // Storage: Identity SuperOf (r:1 w:1) + // Proof Size summary in bytes: + // Measured: `507 + s * (36 ±0)` + // Estimated: `18335` + // Minimum execution time: 24_556 nanoseconds. + Weight::from_parts(28_641_160, 18335) + // Standard Error: 1_327 + .saturating_add(Weight::from_ref_time(66_150).saturating_mul(s.into())) + .saturating_add(T::DbWeight::get().reads(3_u64)) + .saturating_add(T::DbWeight::get().writes(2_u64)) + } + /// Storage: Identity IdentityOf (r:1 w:0) + /// Proof: Identity IdentityOf (max_values: None, max_size: Some(7538), added: 10013, mode: MaxEncodedLen) + /// Storage: Identity SuperOf (r:1 w:1) + /// Proof: Identity SuperOf (max_values: None, max_size: Some(114), added: 2589, mode: MaxEncodedLen) /// The range of component `s` is `[1, 100]`. fn rename_sub(s: u32, ) -> Weight { - // Minimum execution time: 16_124 nanoseconds. - Weight::from_ref_time(18_580_462 as u64) - // Standard Error: 688 - .saturating_add(Weight::from_ref_time(67_220 as u64).saturating_mul(s as u64)) - .saturating_add(T::DbWeight::get().reads(2 as u64)) - .saturating_add(T::DbWeight::get().writes(1 as u64)) - } - // Storage: Identity IdentityOf (r:1 w:0) - // Storage: Identity SuperOf (r:1 w:1) - // Storage: Identity SubsOf (r:1 w:1) + // Proof Size summary in bytes: + // Measured: `623 + s * (3 ±0)` + // Estimated: `12602` + // Minimum execution time: 11_347 nanoseconds. + Weight::from_parts(13_299_367, 12602) + // Standard Error: 525 + .saturating_add(Weight::from_ref_time(16_472).saturating_mul(s.into())) + .saturating_add(T::DbWeight::get().reads(2_u64)) + .saturating_add(T::DbWeight::get().writes(1_u64)) + } + /// Storage: Identity IdentityOf (r:1 w:0) + /// Proof: Identity IdentityOf (max_values: None, max_size: Some(7538), added: 10013, mode: MaxEncodedLen) + /// Storage: Identity SuperOf (r:1 w:1) + /// Proof: Identity SuperOf (max_values: None, max_size: Some(114), added: 2589, mode: MaxEncodedLen) + /// Storage: Identity SubsOf (r:1 w:1) + /// Proof: Identity SubsOf (max_values: None, max_size: Some(3258), added: 5733, mode: MaxEncodedLen) /// The range of component `s` is `[1, 100]`. fn remove_sub(s: u32, ) -> Weight { - // Minimum execution time: 41_517 nanoseconds. - Weight::from_ref_time(45_123_530 as u64) - // Standard Error: 1_530 - .saturating_add(Weight::from_ref_time(105_429 as u64).saturating_mul(s as u64)) - .saturating_add(T::DbWeight::get().reads(3 as u64)) - .saturating_add(T::DbWeight::get().writes(2 as u64)) - } - // Storage: Identity SuperOf (r:1 w:1) - // Storage: Identity SubsOf (r:1 w:1) + // Proof Size summary in bytes: + // Measured: `702 + s * (35 ±0)` + // Estimated: `18335` + // Minimum execution time: 27_810 nanoseconds. + Weight::from_parts(30_347_763, 18335) + // Standard Error: 928 + .saturating_add(Weight::from_ref_time(55_342).saturating_mul(s.into())) + .saturating_add(T::DbWeight::get().reads(3_u64)) + .saturating_add(T::DbWeight::get().writes(2_u64)) + } + /// Storage: Identity SuperOf (r:1 w:1) + /// Proof: Identity SuperOf (max_values: None, max_size: Some(114), added: 2589, mode: MaxEncodedLen) + /// Storage: Identity SubsOf (r:1 w:1) + /// Proof: Identity SubsOf (max_values: None, max_size: Some(3258), added: 5733, mode: MaxEncodedLen) /// The range of component `s` is `[0, 99]`. fn quit_sub(s: u32, ) -> Weight { - // Minimum execution time: 30_171 nanoseconds. - Weight::from_ref_time(33_355_514 as u64) - // Standard Error: 1_286 - .saturating_add(Weight::from_ref_time(114_716 as u64).saturating_mul(s as u64)) - .saturating_add(T::DbWeight::get().reads(2 as u64)) - .saturating_add(T::DbWeight::get().writes(2 as u64)) + // Proof Size summary in bytes: + // Measured: `628 + s * (37 ±0)` + // Estimated: `8322` + // Minimum execution time: 17_601 nanoseconds. + Weight::from_parts(19_794_971, 8322) + // Standard Error: 934 + .saturating_add(Weight::from_ref_time(59_289).saturating_mul(s.into())) + .saturating_add(T::DbWeight::get().reads(2_u64)) + .saturating_add(T::DbWeight::get().writes(2_u64)) } } // For backwards compatibility and tests impl WeightInfo for () { - // Storage: Identity Registrars (r:1 w:1) + /// Storage: Identity Registrars (r:1 w:1) + /// Proof: Identity Registrars (max_values: Some(1), max_size: Some(1141), added: 1636, mode: MaxEncodedLen) /// The range of component `r` is `[1, 19]`. fn add_registrar(r: u32, ) -> Weight { - // Minimum execution time: 20_269 nanoseconds. - Weight::from_ref_time(21_910_543 as u64) - // Standard Error: 4_604 - .saturating_add(Weight::from_ref_time(223_104 as u64).saturating_mul(r as u64)) - .saturating_add(RocksDbWeight::get().reads(1 as u64)) - .saturating_add(RocksDbWeight::get().writes(1 as u64)) - } - // Storage: Identity IdentityOf (r:1 w:1) + // Proof Size summary in bytes: + // Measured: `64 + r * (57 ±0)` + // Estimated: `1636` + // Minimum execution time: 10_964 nanoseconds. + Weight::from_parts(11_800_935, 1636) + // Standard Error: 1_334 + .saturating_add(Weight::from_ref_time(96_038).saturating_mul(r.into())) + .saturating_add(RocksDbWeight::get().reads(1_u64)) + .saturating_add(RocksDbWeight::get().writes(1_u64)) + } + /// Storage: Identity IdentityOf (r:1 w:1) + /// Proof: Identity IdentityOf (max_values: None, max_size: Some(7538), added: 10013, mode: MaxEncodedLen) /// The range of component `r` is `[1, 20]`. /// The range of component `x` is `[0, 100]`. fn set_identity(r: u32, x: u32, ) -> Weight { - // Minimum execution time: 41_872 nanoseconds. - Weight::from_ref_time(40_230_216 as u64) - // Standard Error: 2_342 - .saturating_add(Weight::from_ref_time(145_168 as u64).saturating_mul(r as u64)) - // Standard Error: 457 - .saturating_add(Weight::from_ref_time(291_732 as u64).saturating_mul(x as u64)) - .saturating_add(RocksDbWeight::get().reads(1 as u64)) - .saturating_add(RocksDbWeight::get().writes(1 as u64)) - } - // Storage: Identity IdentityOf (r:1 w:0) - // Storage: Identity SubsOf (r:1 w:1) - // Storage: Identity SuperOf (r:2 w:2) + // Proof Size summary in bytes: + // Measured: `474 + r * (5 ±0)` + // Estimated: `10013` + // Minimum execution time: 26_400 nanoseconds. + Weight::from_parts(26_060_549, 10013) + // Standard Error: 1_561 + .saturating_add(Weight::from_ref_time(72_083).saturating_mul(r.into())) + // Standard Error: 304 + .saturating_add(Weight::from_ref_time(306_994).saturating_mul(x.into())) + .saturating_add(RocksDbWeight::get().reads(1_u64)) + .saturating_add(RocksDbWeight::get().writes(1_u64)) + } + /// Storage: Identity IdentityOf (r:1 w:0) + /// Proof: Identity IdentityOf (max_values: None, max_size: Some(7538), added: 10013, mode: MaxEncodedLen) + /// Storage: Identity SubsOf (r:1 w:1) + /// Proof: Identity SubsOf (max_values: None, max_size: Some(3258), added: 5733, mode: MaxEncodedLen) + /// Storage: Identity SuperOf (r:100 w:100) + /// Proof: Identity SuperOf (max_values: None, max_size: Some(114), added: 2589, mode: MaxEncodedLen) /// The range of component `s` is `[0, 100]`. fn set_subs_new(s: u32, ) -> Weight { - // Minimum execution time: 12_024 nanoseconds. - Weight::from_ref_time(32_550_819 as u64) - // Standard Error: 5_057 - .saturating_add(Weight::from_ref_time(2_521_245 as u64).saturating_mul(s as u64)) - .saturating_add(RocksDbWeight::get().reads(2 as u64)) - .saturating_add(RocksDbWeight::get().reads((1 as u64).saturating_mul(s as u64))) - .saturating_add(RocksDbWeight::get().writes(1 as u64)) - .saturating_add(RocksDbWeight::get().writes((1 as u64).saturating_mul(s as u64))) - } - // Storage: Identity IdentityOf (r:1 w:0) - // Storage: Identity SubsOf (r:1 w:1) - // Storage: Identity SuperOf (r:0 w:2) + // Proof Size summary in bytes: + // Measured: `101` + // Estimated: `15746 + s * (2589 ±0)` + // Minimum execution time: 8_492 nanoseconds. + Weight::from_parts(21_645_924, 15746) + // Standard Error: 3_452 + .saturating_add(Weight::from_ref_time(2_442_604).saturating_mul(s.into())) + .saturating_add(RocksDbWeight::get().reads(2_u64)) + .saturating_add(RocksDbWeight::get().reads((1_u64).saturating_mul(s.into()))) + .saturating_add(RocksDbWeight::get().writes(1_u64)) + .saturating_add(RocksDbWeight::get().writes((1_u64).saturating_mul(s.into()))) + .saturating_add(Weight::from_proof_size(2589).saturating_mul(s.into())) + } + /// Storage: Identity IdentityOf (r:1 w:0) + /// Proof: Identity IdentityOf (max_values: None, max_size: Some(7538), added: 10013, mode: MaxEncodedLen) + /// Storage: Identity SubsOf (r:1 w:1) + /// Proof: Identity SubsOf (max_values: None, max_size: Some(3258), added: 5733, mode: MaxEncodedLen) + /// Storage: Identity SuperOf (r:0 w:100) + /// Proof: Identity SuperOf (max_values: None, max_size: Some(114), added: 2589, mode: MaxEncodedLen) /// The range of component `p` is `[0, 100]`. fn set_subs_old(p: u32, ) -> Weight { - // Minimum execution time: 12_232 nanoseconds. - Weight::from_ref_time(34_009_761 as u64) - // Standard Error: 5_047 - .saturating_add(Weight::from_ref_time(1_113_100 as u64).saturating_mul(p as u64)) - .saturating_add(RocksDbWeight::get().reads(2 as u64)) - .saturating_add(RocksDbWeight::get().writes(1 as u64)) - .saturating_add(RocksDbWeight::get().writes((1 as u64).saturating_mul(p as u64))) - } - // Storage: Identity SubsOf (r:1 w:1) - // Storage: Identity IdentityOf (r:1 w:1) - // Storage: Identity SuperOf (r:0 w:100) + // Proof Size summary in bytes: + // Measured: `226 + p * (32 ±0)` + // Estimated: `15746` + // Minimum execution time: 8_488 nanoseconds. + Weight::from_parts(20_202_601, 15746) + // Standard Error: 2_834 + .saturating_add(Weight::from_ref_time(1_082_941).saturating_mul(p.into())) + .saturating_add(RocksDbWeight::get().reads(2_u64)) + .saturating_add(RocksDbWeight::get().writes(1_u64)) + .saturating_add(RocksDbWeight::get().writes((1_u64).saturating_mul(p.into()))) + } + /// Storage: Identity SubsOf (r:1 w:1) + /// Proof: Identity SubsOf (max_values: None, max_size: Some(3258), added: 5733, mode: MaxEncodedLen) + /// Storage: Identity IdentityOf (r:1 w:1) + /// Proof: Identity IdentityOf (max_values: None, max_size: Some(7538), added: 10013, mode: MaxEncodedLen) + /// Storage: Identity SuperOf (r:0 w:100) + /// Proof: Identity SuperOf (max_values: None, max_size: Some(114), added: 2589, mode: MaxEncodedLen) /// The range of component `r` is `[1, 20]`. /// The range of component `s` is `[0, 100]`. /// The range of component `x` is `[0, 100]`. fn clear_identity(r: u32, s: u32, x: u32, ) -> Weight { - // Minimum execution time: 57_144 nanoseconds. - Weight::from_ref_time(41_559_247 as u64) - // Standard Error: 9_996 - .saturating_add(Weight::from_ref_time(146_770 as u64).saturating_mul(r as u64)) - // Standard Error: 1_952 - .saturating_add(Weight::from_ref_time(1_086_673 as u64).saturating_mul(s as u64)) - // Standard Error: 1_952 - .saturating_add(Weight::from_ref_time(162_481 as u64).saturating_mul(x as u64)) - .saturating_add(RocksDbWeight::get().reads(2 as u64)) - .saturating_add(RocksDbWeight::get().writes(2 as u64)) - .saturating_add(RocksDbWeight::get().writes((1 as u64).saturating_mul(s as u64))) - } - // Storage: Identity Registrars (r:1 w:0) - // Storage: Identity IdentityOf (r:1 w:1) + // Proof Size summary in bytes: + // Measured: `533 + r * (5 ±0) + s * (32 ±0) + x * (66 ±0)` + // Estimated: `15746` + // Minimum execution time: 41_319 nanoseconds. + Weight::from_parts(25_850_055, 15746) + // Standard Error: 4_144 + .saturating_add(Weight::from_ref_time(59_619).saturating_mul(r.into())) + // Standard Error: 809 + .saturating_add(Weight::from_ref_time(1_076_550).saturating_mul(s.into())) + // Standard Error: 809 + .saturating_add(Weight::from_ref_time(163_191).saturating_mul(x.into())) + .saturating_add(RocksDbWeight::get().reads(2_u64)) + .saturating_add(RocksDbWeight::get().writes(2_u64)) + .saturating_add(RocksDbWeight::get().writes((1_u64).saturating_mul(s.into()))) + } + /// Storage: Identity Registrars (r:1 w:0) + /// Proof: Identity Registrars (max_values: Some(1), max_size: Some(1141), added: 1636, mode: MaxEncodedLen) + /// Storage: Identity IdentityOf (r:1 w:1) + /// Proof: Identity IdentityOf (max_values: None, max_size: Some(7538), added: 10013, mode: MaxEncodedLen) /// The range of component `r` is `[1, 20]`. /// The range of component `x` is `[0, 100]`. fn request_judgement(r: u32, x: u32, ) -> Weight { - // Minimum execution time: 44_726 nanoseconds. - Weight::from_ref_time(41_637_308 as u64) - // Standard Error: 1_907 - .saturating_add(Weight::from_ref_time(219_078 as u64).saturating_mul(r as u64)) - // Standard Error: 372 - .saturating_add(Weight::from_ref_time(309_888 as u64).saturating_mul(x as u64)) - .saturating_add(RocksDbWeight::get().reads(2 as u64)) - .saturating_add(RocksDbWeight::get().writes(1 as u64)) - } - // Storage: Identity IdentityOf (r:1 w:1) + // Proof Size summary in bytes: + // Measured: `431 + r * (57 ±0) + x * (66 ±0)` + // Estimated: `11649` + // Minimum execution time: 28_118 nanoseconds. + Weight::from_parts(27_359_471, 11649) + // Standard Error: 2_707 + .saturating_add(Weight::from_ref_time(107_279).saturating_mul(r.into())) + // Standard Error: 528 + .saturating_add(Weight::from_ref_time(325_165).saturating_mul(x.into())) + .saturating_add(RocksDbWeight::get().reads(2_u64)) + .saturating_add(RocksDbWeight::get().writes(1_u64)) + } + /// Storage: Identity IdentityOf (r:1 w:1) + /// Proof: Identity IdentityOf (max_values: None, max_size: Some(7538), added: 10013, mode: MaxEncodedLen) /// The range of component `r` is `[1, 20]`. /// The range of component `x` is `[0, 100]`. fn cancel_request(r: u32, x: u32, ) -> Weight { - // Minimum execution time: 39_719 nanoseconds. - Weight::from_ref_time(38_008_751 as u64) - // Standard Error: 2_394 - .saturating_add(Weight::from_ref_time(181_870 as u64).saturating_mul(r as u64)) - // Standard Error: 467 - .saturating_add(Weight::from_ref_time(314_990 as u64).saturating_mul(x as u64)) - .saturating_add(RocksDbWeight::get().reads(1 as u64)) - .saturating_add(RocksDbWeight::get().writes(1 as u64)) - } - // Storage: Identity Registrars (r:1 w:1) + // Proof Size summary in bytes: + // Measured: `430 + x * (66 ±0)` + // Estimated: `10013` + // Minimum execution time: 24_817 nanoseconds. + Weight::from_parts(24_749_808, 10013) + // Standard Error: 1_938 + .saturating_add(Weight::from_ref_time(63_396).saturating_mul(r.into())) + // Standard Error: 378 + .saturating_add(Weight::from_ref_time(327_083).saturating_mul(x.into())) + .saturating_add(RocksDbWeight::get().reads(1_u64)) + .saturating_add(RocksDbWeight::get().writes(1_u64)) + } + /// Storage: Identity Registrars (r:1 w:1) + /// Proof: Identity Registrars (max_values: Some(1), max_size: Some(1141), added: 1636, mode: MaxEncodedLen) /// The range of component `r` is `[1, 19]`. fn set_fee(r: u32, ) -> Weight { - // Minimum execution time: 10_634 nanoseconds. - Weight::from_ref_time(11_383_704 as u64) - // Standard Error: 2_250 - .saturating_add(Weight::from_ref_time(193_094 as u64).saturating_mul(r as u64)) - .saturating_add(RocksDbWeight::get().reads(1 as u64)) - .saturating_add(RocksDbWeight::get().writes(1 as u64)) - } - // Storage: Identity Registrars (r:1 w:1) + // Proof Size summary in bytes: + // Measured: `121 + r * (57 ±0)` + // Estimated: `1636` + // Minimum execution time: 6_664 nanoseconds. + Weight::from_parts(7_286_307, 1636) + // Standard Error: 1_560 + .saturating_add(Weight::from_ref_time(96_416).saturating_mul(r.into())) + .saturating_add(RocksDbWeight::get().reads(1_u64)) + .saturating_add(RocksDbWeight::get().writes(1_u64)) + } + /// Storage: Identity Registrars (r:1 w:1) + /// Proof: Identity Registrars (max_values: Some(1), max_size: Some(1141), added: 1636, mode: MaxEncodedLen) /// The range of component `r` is `[1, 19]`. fn set_account_id(r: u32, ) -> Weight { - // Minimum execution time: 10_840 nanoseconds. - Weight::from_ref_time(11_638_740 as u64) - // Standard Error: 1_985 - .saturating_add(Weight::from_ref_time(193_016 as u64).saturating_mul(r as u64)) - .saturating_add(RocksDbWeight::get().reads(1 as u64)) - .saturating_add(RocksDbWeight::get().writes(1 as u64)) - } - // Storage: Identity Registrars (r:1 w:1) + // Proof Size summary in bytes: + // Measured: `121 + r * (57 ±0)` + // Estimated: `1636` + // Minimum execution time: 7_054 nanoseconds. + Weight::from_parts(7_382_954, 1636) + // Standard Error: 1_621 + .saturating_add(Weight::from_ref_time(101_595).saturating_mul(r.into())) + .saturating_add(RocksDbWeight::get().reads(1_u64)) + .saturating_add(RocksDbWeight::get().writes(1_u64)) + } + /// Storage: Identity Registrars (r:1 w:1) + /// Proof: Identity Registrars (max_values: Some(1), max_size: Some(1141), added: 1636, mode: MaxEncodedLen) /// The range of component `r` is `[1, 19]`. fn set_fields(r: u32, ) -> Weight { - // Minimum execution time: 10_748 nanoseconds. - Weight::from_ref_time(11_346_901 as u64) - // Standard Error: 2_132 - .saturating_add(Weight::from_ref_time(196_630 as u64).saturating_mul(r as u64)) - .saturating_add(RocksDbWeight::get().reads(1 as u64)) - .saturating_add(RocksDbWeight::get().writes(1 as u64)) - } - // Storage: Identity Registrars (r:1 w:0) - // Storage: Identity IdentityOf (r:1 w:1) + // Proof Size summary in bytes: + // Measured: `121 + r * (57 ±0)` + // Estimated: `1636` + // Minimum execution time: 6_659 nanoseconds. + Weight::from_parts(7_188_883, 1636) + // Standard Error: 1_377 + .saturating_add(Weight::from_ref_time(98_965).saturating_mul(r.into())) + .saturating_add(RocksDbWeight::get().reads(1_u64)) + .saturating_add(RocksDbWeight::get().writes(1_u64)) + } + /// Storage: Identity Registrars (r:1 w:0) + /// Proof: Identity Registrars (max_values: Some(1), max_size: Some(1141), added: 1636, mode: MaxEncodedLen) + /// Storage: Identity IdentityOf (r:1 w:1) + /// Proof: Identity IdentityOf (max_values: None, max_size: Some(7538), added: 10013, mode: MaxEncodedLen) /// The range of component `r` is `[1, 19]`. /// The range of component `x` is `[0, 100]`. fn provide_judgement(r: u32, x: u32, ) -> Weight { - // Minimum execution time: 33_682 nanoseconds. - Weight::from_ref_time(31_336_603 as u64) - // Standard Error: 3_056 - .saturating_add(Weight::from_ref_time(200_403 as u64).saturating_mul(r as u64)) - // Standard Error: 565 - .saturating_add(Weight::from_ref_time(525_142 as u64).saturating_mul(x as u64)) - .saturating_add(RocksDbWeight::get().reads(2 as u64)) - .saturating_add(RocksDbWeight::get().writes(1 as u64)) - } - // Storage: Identity SubsOf (r:1 w:1) - // Storage: Identity IdentityOf (r:1 w:1) - // Storage: System Account (r:1 w:1) - // Storage: Identity SuperOf (r:0 w:100) + // Proof Size summary in bytes: + // Measured: `509 + r * (57 ±0) + x * (66 ±0)` + // Estimated: `11649` + // Minimum execution time: 21_567 nanoseconds. + Weight::from_parts(21_015_310, 11649) + // Standard Error: 2_516 + .saturating_add(Weight::from_ref_time(123_992).saturating_mul(r.into())) + // Standard Error: 465 + .saturating_add(Weight::from_ref_time(552_116).saturating_mul(x.into())) + .saturating_add(RocksDbWeight::get().reads(2_u64)) + .saturating_add(RocksDbWeight::get().writes(1_u64)) + } + /// Storage: Identity SubsOf (r:1 w:1) + /// Proof: Identity SubsOf (max_values: None, max_size: Some(3258), added: 5733, mode: MaxEncodedLen) + /// Storage: Identity IdentityOf (r:1 w:1) + /// Proof: Identity IdentityOf (max_values: None, max_size: Some(7538), added: 10013, mode: MaxEncodedLen) + /// Storage: System Account (r:1 w:1) + /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) + /// Storage: Identity SuperOf (r:0 w:100) + /// Proof: Identity SuperOf (max_values: None, max_size: Some(114), added: 2589, mode: MaxEncodedLen) /// The range of component `r` is `[1, 20]`. /// The range of component `s` is `[0, 100]`. /// The range of component `x` is `[0, 100]`. fn kill_identity(r: u32, s: u32, x: u32, ) -> Weight { - // Minimum execution time: 68_794 nanoseconds. - Weight::from_ref_time(52_114_486 as u64) - // Standard Error: 4_808 - .saturating_add(Weight::from_ref_time(153_462 as u64).saturating_mul(r as u64)) - // Standard Error: 939 - .saturating_add(Weight::from_ref_time(1_084_612 as u64).saturating_mul(s as u64)) - // Standard Error: 939 - .saturating_add(Weight::from_ref_time(170_112 as u64).saturating_mul(x as u64)) - .saturating_add(RocksDbWeight::get().reads(3 as u64)) - .saturating_add(RocksDbWeight::get().writes(3 as u64)) - .saturating_add(RocksDbWeight::get().writes((1 as u64).saturating_mul(s as u64))) - } - // Storage: Identity IdentityOf (r:1 w:0) - // Storage: Identity SuperOf (r:1 w:1) - // Storage: Identity SubsOf (r:1 w:1) + // Proof Size summary in bytes: + // Measured: `772 + r * (5 ±0) + s * (32 ±0) + x * (66 ±0)` + // Estimated: `18349` + // Minimum execution time: 52_881 nanoseconds. + Weight::from_parts(38_504_388, 18349) + // Standard Error: 3_909 + .saturating_add(Weight::from_ref_time(51_452).saturating_mul(r.into())) + // Standard Error: 763 + .saturating_add(Weight::from_ref_time(1_069_924).saturating_mul(s.into())) + // Standard Error: 763 + .saturating_add(Weight::from_ref_time(164_906).saturating_mul(x.into())) + .saturating_add(RocksDbWeight::get().reads(3_u64)) + .saturating_add(RocksDbWeight::get().writes(3_u64)) + .saturating_add(RocksDbWeight::get().writes((1_u64).saturating_mul(s.into()))) + } + /// Storage: Identity IdentityOf (r:1 w:0) + /// Proof: Identity IdentityOf (max_values: None, max_size: Some(7538), added: 10013, mode: MaxEncodedLen) + /// Storage: Identity SuperOf (r:1 w:1) + /// Proof: Identity SuperOf (max_values: None, max_size: Some(114), added: 2589, mode: MaxEncodedLen) + /// Storage: Identity SubsOf (r:1 w:1) + /// Proof: Identity SubsOf (max_values: None, max_size: Some(3258), added: 5733, mode: MaxEncodedLen) /// The range of component `s` is `[0, 99]`. fn add_sub(s: u32, ) -> Weight { - // Minimum execution time: 37_914 nanoseconds. - Weight::from_ref_time(43_488_083 as u64) - // Standard Error: 1_631 - .saturating_add(Weight::from_ref_time(118_845 as u64).saturating_mul(s as u64)) - .saturating_add(RocksDbWeight::get().reads(3 as u64)) - .saturating_add(RocksDbWeight::get().writes(2 as u64)) - } - // Storage: Identity IdentityOf (r:1 w:0) - // Storage: Identity SuperOf (r:1 w:1) + // Proof Size summary in bytes: + // Measured: `507 + s * (36 ±0)` + // Estimated: `18335` + // Minimum execution time: 24_556 nanoseconds. + Weight::from_parts(28_641_160, 18335) + // Standard Error: 1_327 + .saturating_add(Weight::from_ref_time(66_150).saturating_mul(s.into())) + .saturating_add(RocksDbWeight::get().reads(3_u64)) + .saturating_add(RocksDbWeight::get().writes(2_u64)) + } + /// Storage: Identity IdentityOf (r:1 w:0) + /// Proof: Identity IdentityOf (max_values: None, max_size: Some(7538), added: 10013, mode: MaxEncodedLen) + /// Storage: Identity SuperOf (r:1 w:1) + /// Proof: Identity SuperOf (max_values: None, max_size: Some(114), added: 2589, mode: MaxEncodedLen) /// The range of component `s` is `[1, 100]`. fn rename_sub(s: u32, ) -> Weight { - // Minimum execution time: 16_124 nanoseconds. - Weight::from_ref_time(18_580_462 as u64) - // Standard Error: 688 - .saturating_add(Weight::from_ref_time(67_220 as u64).saturating_mul(s as u64)) - .saturating_add(RocksDbWeight::get().reads(2 as u64)) - .saturating_add(RocksDbWeight::get().writes(1 as u64)) - } - // Storage: Identity IdentityOf (r:1 w:0) - // Storage: Identity SuperOf (r:1 w:1) - // Storage: Identity SubsOf (r:1 w:1) + // Proof Size summary in bytes: + // Measured: `623 + s * (3 ±0)` + // Estimated: `12602` + // Minimum execution time: 11_347 nanoseconds. + Weight::from_parts(13_299_367, 12602) + // Standard Error: 525 + .saturating_add(Weight::from_ref_time(16_472).saturating_mul(s.into())) + .saturating_add(RocksDbWeight::get().reads(2_u64)) + .saturating_add(RocksDbWeight::get().writes(1_u64)) + } + /// Storage: Identity IdentityOf (r:1 w:0) + /// Proof: Identity IdentityOf (max_values: None, max_size: Some(7538), added: 10013, mode: MaxEncodedLen) + /// Storage: Identity SuperOf (r:1 w:1) + /// Proof: Identity SuperOf (max_values: None, max_size: Some(114), added: 2589, mode: MaxEncodedLen) + /// Storage: Identity SubsOf (r:1 w:1) + /// Proof: Identity SubsOf (max_values: None, max_size: Some(3258), added: 5733, mode: MaxEncodedLen) /// The range of component `s` is `[1, 100]`. fn remove_sub(s: u32, ) -> Weight { - // Minimum execution time: 41_517 nanoseconds. - Weight::from_ref_time(45_123_530 as u64) - // Standard Error: 1_530 - .saturating_add(Weight::from_ref_time(105_429 as u64).saturating_mul(s as u64)) - .saturating_add(RocksDbWeight::get().reads(3 as u64)) - .saturating_add(RocksDbWeight::get().writes(2 as u64)) - } - // Storage: Identity SuperOf (r:1 w:1) - // Storage: Identity SubsOf (r:1 w:1) + // Proof Size summary in bytes: + // Measured: `702 + s * (35 ±0)` + // Estimated: `18335` + // Minimum execution time: 27_810 nanoseconds. + Weight::from_parts(30_347_763, 18335) + // Standard Error: 928 + .saturating_add(Weight::from_ref_time(55_342).saturating_mul(s.into())) + .saturating_add(RocksDbWeight::get().reads(3_u64)) + .saturating_add(RocksDbWeight::get().writes(2_u64)) + } + /// Storage: Identity SuperOf (r:1 w:1) + /// Proof: Identity SuperOf (max_values: None, max_size: Some(114), added: 2589, mode: MaxEncodedLen) + /// Storage: Identity SubsOf (r:1 w:1) + /// Proof: Identity SubsOf (max_values: None, max_size: Some(3258), added: 5733, mode: MaxEncodedLen) /// The range of component `s` is `[0, 99]`. fn quit_sub(s: u32, ) -> Weight { - // Minimum execution time: 30_171 nanoseconds. - Weight::from_ref_time(33_355_514 as u64) - // Standard Error: 1_286 - .saturating_add(Weight::from_ref_time(114_716 as u64).saturating_mul(s as u64)) - .saturating_add(RocksDbWeight::get().reads(2 as u64)) - .saturating_add(RocksDbWeight::get().writes(2 as u64)) + // Proof Size summary in bytes: + // Measured: `628 + s * (37 ±0)` + // Estimated: `8322` + // Minimum execution time: 17_601 nanoseconds. + Weight::from_parts(19_794_971, 8322) + // Standard Error: 934 + .saturating_add(Weight::from_ref_time(59_289).saturating_mul(s.into())) + .saturating_add(RocksDbWeight::get().reads(2_u64)) + .saturating_add(RocksDbWeight::get().writes(2_u64)) } } diff --git a/frame/im-online/src/weights.rs b/frame/im-online/src/weights.rs index f81db997c..4ea275a33 100644 --- a/frame/im-online/src/weights.rs +++ b/frame/im-online/src/weights.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2022 Parity Technologies (UK) Ltd. +// Copyright (C) 2023 Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); @@ -18,7 +18,8 @@ //! Autogenerated weights for pallet_im_online //! //! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev -//! DATE: 2022-11-07, STEPS: `50`, REPEAT: 20, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! DATE: 2023-01-24, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! WORST CASE MAP SIZE: `1000000` //! HOSTNAME: `bm2`, CPU: `Intel(R) Core(TM) i7-7700K CPU @ 4.20GHz` //! EXECUTION: Some(Wasm), WASM-EXECUTION: Compiled, CHAIN: Some("dev"), DB CACHE: 1024 @@ -53,42 +54,62 @@ pub trait WeightInfo { /// Weights for pallet_im_online using the Substrate node and recommended hardware. pub struct SubstrateWeight(PhantomData); impl WeightInfo for SubstrateWeight { - // Storage: Session Validators (r:1 w:0) - // Storage: Session CurrentIndex (r:1 w:0) - // Storage: ImOnline ReceivedHeartbeats (r:1 w:1) - // Storage: ImOnline AuthoredBlocks (r:1 w:0) - // Storage: ImOnline Keys (r:1 w:0) + /// Storage: Session Validators (r:1 w:0) + /// Proof Skipped: Session Validators (max_values: Some(1), max_size: None, mode: Measured) + /// Storage: Session CurrentIndex (r:1 w:0) + /// Proof Skipped: Session CurrentIndex (max_values: Some(1), max_size: None, mode: Measured) + /// Storage: ImOnline Keys (r:1 w:0) + /// Proof: ImOnline Keys (max_values: Some(1), max_size: Some(320002), added: 320497, mode: MaxEncodedLen) + /// Storage: ImOnline ReceivedHeartbeats (r:1 w:1) + /// Proof: ImOnline ReceivedHeartbeats (max_values: None, max_size: Some(10021032), added: 10023507, mode: MaxEncodedLen) + /// Storage: ImOnline AuthoredBlocks (r:1 w:0) + /// Proof: ImOnline AuthoredBlocks (max_values: None, max_size: Some(56), added: 2531, mode: MaxEncodedLen) /// The range of component `k` is `[1, 1000]`. /// The range of component `e` is `[1, 100]`. fn validate_unsigned_and_then_heartbeat(k: u32, e: u32, ) -> Weight { - // Minimum execution time: 101_380 nanoseconds. - Weight::from_ref_time(82_735_670 as u64) - // Standard Error: 121 - .saturating_add(Weight::from_ref_time(21_279 as u64).saturating_mul(k as u64)) - // Standard Error: 1_222 - .saturating_add(Weight::from_ref_time(296_343 as u64).saturating_mul(e as u64)) - .saturating_add(T::DbWeight::get().reads(4 as u64)) - .saturating_add(T::DbWeight::get().writes(1 as u64)) + // Proof Size summary in bytes: + // Measured: `359 + k * (32 ±0)` + // Estimated: `10345712 + e * (25 ±0) + k * (64 ±0)` + // Minimum execution time: 91_116 nanoseconds. + Weight::from_parts(72_526_877, 10345712) + // Standard Error: 95 + .saturating_add(Weight::from_ref_time(20_461).saturating_mul(k.into())) + // Standard Error: 967 + .saturating_add(Weight::from_ref_time(307_869).saturating_mul(e.into())) + .saturating_add(T::DbWeight::get().reads(4_u64)) + .saturating_add(T::DbWeight::get().writes(1_u64)) + .saturating_add(Weight::from_proof_size(25).saturating_mul(e.into())) + .saturating_add(Weight::from_proof_size(64).saturating_mul(k.into())) } } // For backwards compatibility and tests impl WeightInfo for () { - // Storage: Session Validators (r:1 w:0) - // Storage: Session CurrentIndex (r:1 w:0) - // Storage: ImOnline ReceivedHeartbeats (r:1 w:1) - // Storage: ImOnline AuthoredBlocks (r:1 w:0) - // Storage: ImOnline Keys (r:1 w:0) + /// Storage: Session Validators (r:1 w:0) + /// Proof Skipped: Session Validators (max_values: Some(1), max_size: None, mode: Measured) + /// Storage: Session CurrentIndex (r:1 w:0) + /// Proof Skipped: Session CurrentIndex (max_values: Some(1), max_size: None, mode: Measured) + /// Storage: ImOnline Keys (r:1 w:0) + /// Proof: ImOnline Keys (max_values: Some(1), max_size: Some(320002), added: 320497, mode: MaxEncodedLen) + /// Storage: ImOnline ReceivedHeartbeats (r:1 w:1) + /// Proof: ImOnline ReceivedHeartbeats (max_values: None, max_size: Some(10021032), added: 10023507, mode: MaxEncodedLen) + /// Storage: ImOnline AuthoredBlocks (r:1 w:0) + /// Proof: ImOnline AuthoredBlocks (max_values: None, max_size: Some(56), added: 2531, mode: MaxEncodedLen) /// The range of component `k` is `[1, 1000]`. /// The range of component `e` is `[1, 100]`. fn validate_unsigned_and_then_heartbeat(k: u32, e: u32, ) -> Weight { - // Minimum execution time: 101_380 nanoseconds. - Weight::from_ref_time(82_735_670 as u64) - // Standard Error: 121 - .saturating_add(Weight::from_ref_time(21_279 as u64).saturating_mul(k as u64)) - // Standard Error: 1_222 - .saturating_add(Weight::from_ref_time(296_343 as u64).saturating_mul(e as u64)) - .saturating_add(RocksDbWeight::get().reads(4 as u64)) - .saturating_add(RocksDbWeight::get().writes(1 as u64)) + // Proof Size summary in bytes: + // Measured: `359 + k * (32 ±0)` + // Estimated: `10345712 + e * (25 ±0) + k * (64 ±0)` + // Minimum execution time: 91_116 nanoseconds. + Weight::from_parts(72_526_877, 10345712) + // Standard Error: 95 + .saturating_add(Weight::from_ref_time(20_461).saturating_mul(k.into())) + // Standard Error: 967 + .saturating_add(Weight::from_ref_time(307_869).saturating_mul(e.into())) + .saturating_add(RocksDbWeight::get().reads(4_u64)) + .saturating_add(RocksDbWeight::get().writes(1_u64)) + .saturating_add(Weight::from_proof_size(25).saturating_mul(e.into())) + .saturating_add(Weight::from_proof_size(64).saturating_mul(k.into())) } } diff --git a/frame/indices/src/weights.rs b/frame/indices/src/weights.rs index 7b974875c..36dd79ece 100644 --- a/frame/indices/src/weights.rs +++ b/frame/indices/src/weights.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2022 Parity Technologies (UK) Ltd. +// Copyright (C) 2023 Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); @@ -18,7 +18,8 @@ //! Autogenerated weights for pallet_indices //! //! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev -//! DATE: 2022-11-07, STEPS: `50`, REPEAT: 20, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! DATE: 2023-01-24, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! WORST CASE MAP SIZE: `1000000` //! HOSTNAME: `bm2`, CPU: `Intel(R) Core(TM) i7-7700K CPU @ 4.20GHz` //! EXECUTION: Some(Wasm), WASM-EXECUTION: Compiled, CHAIN: Some("dev"), DB CACHE: 1024 @@ -57,82 +58,126 @@ pub trait WeightInfo { /// Weights for pallet_indices using the Substrate node and recommended hardware. pub struct SubstrateWeight(PhantomData); impl WeightInfo for SubstrateWeight { - // Storage: Indices Accounts (r:1 w:1) + /// Storage: Indices Accounts (r:1 w:1) + /// Proof: Indices Accounts (max_values: None, max_size: Some(69), added: 2544, mode: MaxEncodedLen) fn claim() -> Weight { - // Minimum execution time: 31_756 nanoseconds. - Weight::from_ref_time(32_252_000 as u64) - .saturating_add(T::DbWeight::get().reads(1 as u64)) - .saturating_add(T::DbWeight::get().writes(1 as u64)) + // Proof Size summary in bytes: + // Measured: `76` + // Estimated: `2544` + // Minimum execution time: 19_738 nanoseconds. + Weight::from_parts(20_029_000, 2544) + .saturating_add(T::DbWeight::get().reads(1_u64)) + .saturating_add(T::DbWeight::get().writes(1_u64)) } - // Storage: Indices Accounts (r:1 w:1) - // Storage: System Account (r:1 w:1) + /// Storage: Indices Accounts (r:1 w:1) + /// Proof: Indices Accounts (max_values: None, max_size: Some(69), added: 2544, mode: MaxEncodedLen) + /// Storage: System Account (r:1 w:1) + /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) fn transfer() -> Weight { - // Minimum execution time: 38_686 nanoseconds. - Weight::from_ref_time(39_402_000 as u64) - .saturating_add(T::DbWeight::get().reads(2 as u64)) - .saturating_add(T::DbWeight::get().writes(2 as u64)) + // Proof Size summary in bytes: + // Measured: `339` + // Estimated: `5147` + // Minimum execution time: 24_494 nanoseconds. + Weight::from_parts(24_794_000, 5147) + .saturating_add(T::DbWeight::get().reads(2_u64)) + .saturating_add(T::DbWeight::get().writes(2_u64)) } - // Storage: Indices Accounts (r:1 w:1) + /// Storage: Indices Accounts (r:1 w:1) + /// Proof: Indices Accounts (max_values: None, max_size: Some(69), added: 2544, mode: MaxEncodedLen) fn free() -> Weight { - // Minimum execution time: 33_752 nanoseconds. - Weight::from_ref_time(34_348_000 as u64) - .saturating_add(T::DbWeight::get().reads(1 as u64)) - .saturating_add(T::DbWeight::get().writes(1 as u64)) + // Proof Size summary in bytes: + // Measured: `204` + // Estimated: `2544` + // Minimum execution time: 20_041 nanoseconds. + Weight::from_parts(20_473_000, 2544) + .saturating_add(T::DbWeight::get().reads(1_u64)) + .saturating_add(T::DbWeight::get().writes(1_u64)) } - // Storage: Indices Accounts (r:1 w:1) - // Storage: System Account (r:1 w:1) + /// Storage: Indices Accounts (r:1 w:1) + /// Proof: Indices Accounts (max_values: None, max_size: Some(69), added: 2544, mode: MaxEncodedLen) + /// Storage: System Account (r:1 w:1) + /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) fn force_transfer() -> Weight { - // Minimum execution time: 32_739 nanoseconds. - Weight::from_ref_time(33_151_000 as u64) - .saturating_add(T::DbWeight::get().reads(2 as u64)) - .saturating_add(T::DbWeight::get().writes(2 as u64)) + // Proof Size summary in bytes: + // Measured: `339` + // Estimated: `5147` + // Minimum execution time: 21_874 nanoseconds. + Weight::from_parts(22_438_000, 5147) + .saturating_add(T::DbWeight::get().reads(2_u64)) + .saturating_add(T::DbWeight::get().writes(2_u64)) } - // Storage: Indices Accounts (r:1 w:1) + /// Storage: Indices Accounts (r:1 w:1) + /// Proof: Indices Accounts (max_values: None, max_size: Some(69), added: 2544, mode: MaxEncodedLen) fn freeze() -> Weight { - // Minimum execution time: 40_369 nanoseconds. - Weight::from_ref_time(40_982_000 as u64) - .saturating_add(T::DbWeight::get().reads(1 as u64)) - .saturating_add(T::DbWeight::get().writes(1 as u64)) + // Proof Size summary in bytes: + // Measured: `204` + // Estimated: `2544` + // Minimum execution time: 22_407 nanoseconds. + Weight::from_parts(22_768_000, 2544) + .saturating_add(T::DbWeight::get().reads(1_u64)) + .saturating_add(T::DbWeight::get().writes(1_u64)) } } // For backwards compatibility and tests impl WeightInfo for () { - // Storage: Indices Accounts (r:1 w:1) + /// Storage: Indices Accounts (r:1 w:1) + /// Proof: Indices Accounts (max_values: None, max_size: Some(69), added: 2544, mode: MaxEncodedLen) fn claim() -> Weight { - // Minimum execution time: 31_756 nanoseconds. - Weight::from_ref_time(32_252_000 as u64) - .saturating_add(RocksDbWeight::get().reads(1 as u64)) - .saturating_add(RocksDbWeight::get().writes(1 as u64)) + // Proof Size summary in bytes: + // Measured: `76` + // Estimated: `2544` + // Minimum execution time: 19_738 nanoseconds. + Weight::from_parts(20_029_000, 2544) + .saturating_add(RocksDbWeight::get().reads(1_u64)) + .saturating_add(RocksDbWeight::get().writes(1_u64)) } - // Storage: Indices Accounts (r:1 w:1) - // Storage: System Account (r:1 w:1) + /// Storage: Indices Accounts (r:1 w:1) + /// Proof: Indices Accounts (max_values: None, max_size: Some(69), added: 2544, mode: MaxEncodedLen) + /// Storage: System Account (r:1 w:1) + /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) fn transfer() -> Weight { - // Minimum execution time: 38_686 nanoseconds. - Weight::from_ref_time(39_402_000 as u64) - .saturating_add(RocksDbWeight::get().reads(2 as u64)) - .saturating_add(RocksDbWeight::get().writes(2 as u64)) + // Proof Size summary in bytes: + // Measured: `339` + // Estimated: `5147` + // Minimum execution time: 24_494 nanoseconds. + Weight::from_parts(24_794_000, 5147) + .saturating_add(RocksDbWeight::get().reads(2_u64)) + .saturating_add(RocksDbWeight::get().writes(2_u64)) } - // Storage: Indices Accounts (r:1 w:1) + /// Storage: Indices Accounts (r:1 w:1) + /// Proof: Indices Accounts (max_values: None, max_size: Some(69), added: 2544, mode: MaxEncodedLen) fn free() -> Weight { - // Minimum execution time: 33_752 nanoseconds. - Weight::from_ref_time(34_348_000 as u64) - .saturating_add(RocksDbWeight::get().reads(1 as u64)) - .saturating_add(RocksDbWeight::get().writes(1 as u64)) + // Proof Size summary in bytes: + // Measured: `204` + // Estimated: `2544` + // Minimum execution time: 20_041 nanoseconds. + Weight::from_parts(20_473_000, 2544) + .saturating_add(RocksDbWeight::get().reads(1_u64)) + .saturating_add(RocksDbWeight::get().writes(1_u64)) } - // Storage: Indices Accounts (r:1 w:1) - // Storage: System Account (r:1 w:1) + /// Storage: Indices Accounts (r:1 w:1) + /// Proof: Indices Accounts (max_values: None, max_size: Some(69), added: 2544, mode: MaxEncodedLen) + /// Storage: System Account (r:1 w:1) + /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) fn force_transfer() -> Weight { - // Minimum execution time: 32_739 nanoseconds. - Weight::from_ref_time(33_151_000 as u64) - .saturating_add(RocksDbWeight::get().reads(2 as u64)) - .saturating_add(RocksDbWeight::get().writes(2 as u64)) + // Proof Size summary in bytes: + // Measured: `339` + // Estimated: `5147` + // Minimum execution time: 21_874 nanoseconds. + Weight::from_parts(22_438_000, 5147) + .saturating_add(RocksDbWeight::get().reads(2_u64)) + .saturating_add(RocksDbWeight::get().writes(2_u64)) } - // Storage: Indices Accounts (r:1 w:1) + /// Storage: Indices Accounts (r:1 w:1) + /// Proof: Indices Accounts (max_values: None, max_size: Some(69), added: 2544, mode: MaxEncodedLen) fn freeze() -> Weight { - // Minimum execution time: 40_369 nanoseconds. - Weight::from_ref_time(40_982_000 as u64) - .saturating_add(RocksDbWeight::get().reads(1 as u64)) - .saturating_add(RocksDbWeight::get().writes(1 as u64)) + // Proof Size summary in bytes: + // Measured: `204` + // Estimated: `2544` + // Minimum execution time: 22_407 nanoseconds. + Weight::from_parts(22_768_000, 2544) + .saturating_add(RocksDbWeight::get().reads(1_u64)) + .saturating_add(RocksDbWeight::get().writes(1_u64)) } } diff --git a/frame/lottery/src/weights.rs b/frame/lottery/src/weights.rs index e9ee528cc..e2cf9ea2b 100644 --- a/frame/lottery/src/weights.rs +++ b/frame/lottery/src/weights.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2022 Parity Technologies (UK) Ltd. +// Copyright (C) 2023 Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); @@ -18,25 +18,25 @@ //! Autogenerated weights for pallet_lottery //! //! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev -//! DATE: 2022-12-08, STEPS: `50`, REPEAT: 20, LOW RANGE: `[]`, HIGH RANGE: `[]` -//! HOSTNAME: `bm3`, CPU: `Intel(R) Core(TM) i7-7700K CPU @ 4.20GHz` +//! DATE: 2023-01-24, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! WORST CASE MAP SIZE: `1000000` +//! HOSTNAME: `bm2`, CPU: `Intel(R) Core(TM) i7-7700K CPU @ 4.20GHz` //! EXECUTION: Some(Wasm), WASM-EXECUTION: Compiled, CHAIN: Some("dev"), DB CACHE: 1024 // Executed Command: -// /home/benchbot/cargo_target_dir/production/substrate +// ./target/production/substrate // benchmark // pallet +// --chain=dev // --steps=50 // --repeat=20 +// --pallet=pallet_lottery // --extrinsic=* // --execution=wasm // --wasm-execution=compiled // --heap-pages=4096 -// --json-file=/var/lib/gitlab-runner/builds/zyw4fam_/0/parity/mirrors/substrate/.git/.artifacts/bench.json -// --pallet=pallet_lottery -// --chain=dev -// --header=./HEADER-APACHE2 // --output=./frame/lottery/src/weights.rs +// --header=./HEADER-APACHE2 // --template=./.maintain/frame-weight-template.hbs #![cfg_attr(rustfmt, rustfmt_skip)] @@ -59,130 +59,212 @@ pub trait WeightInfo { /// Weights for pallet_lottery using the Substrate node and recommended hardware. pub struct SubstrateWeight(PhantomData); impl WeightInfo for SubstrateWeight { - // Storage: Lottery Lottery (r:1 w:0) - // Storage: Lottery CallIndices (r:1 w:0) - // Storage: Lottery TicketsCount (r:1 w:1) - // Storage: Lottery Participants (r:1 w:1) - // Storage: Lottery LotteryIndex (r:1 w:0) - // Storage: System Account (r:1 w:1) - // Storage: Lottery Tickets (r:0 w:1) + /// Storage: Lottery Lottery (r:1 w:0) + /// Proof: Lottery Lottery (max_values: Some(1), max_size: Some(29), added: 524, mode: MaxEncodedLen) + /// Storage: Lottery CallIndices (r:1 w:0) + /// Proof: Lottery CallIndices (max_values: Some(1), max_size: Some(21), added: 516, mode: MaxEncodedLen) + /// Storage: Lottery TicketsCount (r:1 w:1) + /// Proof: Lottery TicketsCount (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) + /// Storage: Lottery Participants (r:1 w:1) + /// Proof: Lottery Participants (max_values: None, max_size: Some(65), added: 2540, mode: MaxEncodedLen) + /// Storage: Lottery LotteryIndex (r:1 w:0) + /// Proof: Lottery LotteryIndex (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) + /// Storage: System Account (r:1 w:1) + /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) + /// Storage: Lottery Tickets (r:0 w:1) + /// Proof: Lottery Tickets (max_values: None, max_size: Some(44), added: 2519, mode: MaxEncodedLen) fn buy_ticket() -> Weight { - // Minimum execution time: 52_479 nanoseconds. - Weight::from_ref_time(53_225_000) - .saturating_add(T::DbWeight::get().reads(6)) - .saturating_add(T::DbWeight::get().writes(4)) + // Proof Size summary in bytes: + // Measured: `484` + // Estimated: `7181` + // Minimum execution time: 62_125 nanoseconds. + Weight::from_parts(63_145_000, 7181) + .saturating_add(T::DbWeight::get().reads(6_u64)) + .saturating_add(T::DbWeight::get().writes(4_u64)) } - // Storage: Lottery CallIndices (r:0 w:1) + /// Storage: Lottery CallIndices (r:0 w:1) + /// Proof: Lottery CallIndices (max_values: Some(1), max_size: Some(21), added: 516, mode: MaxEncodedLen) /// The range of component `n` is `[0, 10]`. fn set_calls(n: u32, ) -> Weight { - // Minimum execution time: 14_433 nanoseconds. - Weight::from_ref_time(15_660_780) - // Standard Error: 5_894 - .saturating_add(Weight::from_ref_time(290_482).saturating_mul(n.into())) - .saturating_add(T::DbWeight::get().writes(1)) + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 7_650 nanoseconds. + Weight::from_ref_time(8_344_960) + // Standard Error: 2_629 + .saturating_add(Weight::from_ref_time(268_557).saturating_mul(n.into())) + .saturating_add(T::DbWeight::get().writes(1_u64)) } - // Storage: Lottery Lottery (r:1 w:1) - // Storage: Lottery LotteryIndex (r:1 w:1) - // Storage: System Account (r:1 w:1) + /// Storage: Lottery Lottery (r:1 w:1) + /// Proof: Lottery Lottery (max_values: Some(1), max_size: Some(29), added: 524, mode: MaxEncodedLen) + /// Storage: Lottery LotteryIndex (r:1 w:1) + /// Proof: Lottery LotteryIndex (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) + /// Storage: System Account (r:1 w:1) + /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) fn start_lottery() -> Weight { - // Minimum execution time: 43_683 nanoseconds. - Weight::from_ref_time(44_580_000) - .saturating_add(T::DbWeight::get().reads(3)) - .saturating_add(T::DbWeight::get().writes(3)) + // Proof Size summary in bytes: + // Measured: `161` + // Estimated: `3626` + // Minimum execution time: 31_324 nanoseconds. + Weight::from_parts(31_985_000, 3626) + .saturating_add(T::DbWeight::get().reads(3_u64)) + .saturating_add(T::DbWeight::get().writes(3_u64)) } - // Storage: Lottery Lottery (r:1 w:1) + /// Storage: Lottery Lottery (r:1 w:1) + /// Proof: Lottery Lottery (max_values: Some(1), max_size: Some(29), added: 524, mode: MaxEncodedLen) fn stop_repeat() -> Weight { - // Minimum execution time: 10_514 nanoseconds. - Weight::from_ref_time(10_821_000) - .saturating_add(T::DbWeight::get().reads(1)) - .saturating_add(T::DbWeight::get().writes(1)) + // Proof Size summary in bytes: + // Measured: `219` + // Estimated: `524` + // Minimum execution time: 7_124 nanoseconds. + Weight::from_parts(7_285_000, 524) + .saturating_add(T::DbWeight::get().reads(1_u64)) + .saturating_add(T::DbWeight::get().writes(1_u64)) } - // Storage: RandomnessCollectiveFlip RandomMaterial (r:1 w:0) - // Storage: Lottery Lottery (r:1 w:1) - // Storage: System Account (r:2 w:2) - // Storage: Lottery TicketsCount (r:1 w:1) - // Storage: Lottery Tickets (r:1 w:0) + /// Storage: RandomnessCollectiveFlip RandomMaterial (r:1 w:0) + /// Proof: RandomnessCollectiveFlip RandomMaterial (max_values: Some(1), max_size: Some(2594), added: 3089, mode: MaxEncodedLen) + /// Storage: Lottery Lottery (r:1 w:1) + /// Proof: Lottery Lottery (max_values: Some(1), max_size: Some(29), added: 524, mode: MaxEncodedLen) + /// Storage: System Account (r:2 w:2) + /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) + /// Storage: Lottery TicketsCount (r:1 w:1) + /// Proof: Lottery TicketsCount (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) + /// Storage: Lottery Tickets (r:1 w:0) + /// Proof: Lottery Tickets (max_values: None, max_size: Some(44), added: 2519, mode: MaxEncodedLen) fn on_initialize_end() -> Weight { - // Minimum execution time: 60_254 nanoseconds. - Weight::from_ref_time(61_924_000) - .saturating_add(T::DbWeight::get().reads(6)) - .saturating_add(T::DbWeight::get().writes(4)) + // Proof Size summary in bytes: + // Measured: `556` + // Estimated: `11837` + // Minimum execution time: 50_745 nanoseconds. + Weight::from_parts(52_232_000, 11837) + .saturating_add(T::DbWeight::get().reads(6_u64)) + .saturating_add(T::DbWeight::get().writes(4_u64)) } - // Storage: RandomnessCollectiveFlip RandomMaterial (r:1 w:0) - // Storage: Lottery Lottery (r:1 w:1) - // Storage: System Account (r:2 w:2) - // Storage: Lottery TicketsCount (r:1 w:1) - // Storage: Lottery Tickets (r:1 w:0) - // Storage: Lottery LotteryIndex (r:1 w:1) + /// Storage: RandomnessCollectiveFlip RandomMaterial (r:1 w:0) + /// Proof: RandomnessCollectiveFlip RandomMaterial (max_values: Some(1), max_size: Some(2594), added: 3089, mode: MaxEncodedLen) + /// Storage: Lottery Lottery (r:1 w:1) + /// Proof: Lottery Lottery (max_values: Some(1), max_size: Some(29), added: 524, mode: MaxEncodedLen) + /// Storage: System Account (r:2 w:2) + /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) + /// Storage: Lottery TicketsCount (r:1 w:1) + /// Proof: Lottery TicketsCount (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) + /// Storage: Lottery Tickets (r:1 w:0) + /// Proof: Lottery Tickets (max_values: None, max_size: Some(44), added: 2519, mode: MaxEncodedLen) + /// Storage: Lottery LotteryIndex (r:1 w:1) + /// Proof: Lottery LotteryIndex (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) fn on_initialize_repeat() -> Weight { - // Minimum execution time: 61_552 nanoseconds. - Weight::from_ref_time(62_152_000) - .saturating_add(T::DbWeight::get().reads(7)) - .saturating_add(T::DbWeight::get().writes(5)) + // Proof Size summary in bytes: + // Measured: `556` + // Estimated: `12336` + // Minimum execution time: 52_437 nanoseconds. + Weight::from_parts(53_063_000, 12336) + .saturating_add(T::DbWeight::get().reads(7_u64)) + .saturating_add(T::DbWeight::get().writes(5_u64)) } } // For backwards compatibility and tests impl WeightInfo for () { - // Storage: Lottery Lottery (r:1 w:0) - // Storage: Lottery CallIndices (r:1 w:0) - // Storage: Lottery TicketsCount (r:1 w:1) - // Storage: Lottery Participants (r:1 w:1) - // Storage: Lottery LotteryIndex (r:1 w:0) - // Storage: System Account (r:1 w:1) - // Storage: Lottery Tickets (r:0 w:1) + /// Storage: Lottery Lottery (r:1 w:0) + /// Proof: Lottery Lottery (max_values: Some(1), max_size: Some(29), added: 524, mode: MaxEncodedLen) + /// Storage: Lottery CallIndices (r:1 w:0) + /// Proof: Lottery CallIndices (max_values: Some(1), max_size: Some(21), added: 516, mode: MaxEncodedLen) + /// Storage: Lottery TicketsCount (r:1 w:1) + /// Proof: Lottery TicketsCount (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) + /// Storage: Lottery Participants (r:1 w:1) + /// Proof: Lottery Participants (max_values: None, max_size: Some(65), added: 2540, mode: MaxEncodedLen) + /// Storage: Lottery LotteryIndex (r:1 w:0) + /// Proof: Lottery LotteryIndex (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) + /// Storage: System Account (r:1 w:1) + /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) + /// Storage: Lottery Tickets (r:0 w:1) + /// Proof: Lottery Tickets (max_values: None, max_size: Some(44), added: 2519, mode: MaxEncodedLen) fn buy_ticket() -> Weight { - // Minimum execution time: 52_479 nanoseconds. - Weight::from_ref_time(53_225_000) - .saturating_add(RocksDbWeight::get().reads(6)) - .saturating_add(RocksDbWeight::get().writes(4)) + // Proof Size summary in bytes: + // Measured: `484` + // Estimated: `7181` + // Minimum execution time: 62_125 nanoseconds. + Weight::from_parts(63_145_000, 7181) + .saturating_add(RocksDbWeight::get().reads(6_u64)) + .saturating_add(RocksDbWeight::get().writes(4_u64)) } - // Storage: Lottery CallIndices (r:0 w:1) + /// Storage: Lottery CallIndices (r:0 w:1) + /// Proof: Lottery CallIndices (max_values: Some(1), max_size: Some(21), added: 516, mode: MaxEncodedLen) /// The range of component `n` is `[0, 10]`. fn set_calls(n: u32, ) -> Weight { - // Minimum execution time: 14_433 nanoseconds. - Weight::from_ref_time(15_660_780) - // Standard Error: 5_894 - .saturating_add(Weight::from_ref_time(290_482).saturating_mul(n.into())) - .saturating_add(RocksDbWeight::get().writes(1)) + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 7_650 nanoseconds. + Weight::from_ref_time(8_344_960) + // Standard Error: 2_629 + .saturating_add(Weight::from_ref_time(268_557).saturating_mul(n.into())) + .saturating_add(RocksDbWeight::get().writes(1_u64)) } - // Storage: Lottery Lottery (r:1 w:1) - // Storage: Lottery LotteryIndex (r:1 w:1) - // Storage: System Account (r:1 w:1) + /// Storage: Lottery Lottery (r:1 w:1) + /// Proof: Lottery Lottery (max_values: Some(1), max_size: Some(29), added: 524, mode: MaxEncodedLen) + /// Storage: Lottery LotteryIndex (r:1 w:1) + /// Proof: Lottery LotteryIndex (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) + /// Storage: System Account (r:1 w:1) + /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) fn start_lottery() -> Weight { - // Minimum execution time: 43_683 nanoseconds. - Weight::from_ref_time(44_580_000) - .saturating_add(RocksDbWeight::get().reads(3)) - .saturating_add(RocksDbWeight::get().writes(3)) + // Proof Size summary in bytes: + // Measured: `161` + // Estimated: `3626` + // Minimum execution time: 31_324 nanoseconds. + Weight::from_parts(31_985_000, 3626) + .saturating_add(RocksDbWeight::get().reads(3_u64)) + .saturating_add(RocksDbWeight::get().writes(3_u64)) } - // Storage: Lottery Lottery (r:1 w:1) + /// Storage: Lottery Lottery (r:1 w:1) + /// Proof: Lottery Lottery (max_values: Some(1), max_size: Some(29), added: 524, mode: MaxEncodedLen) fn stop_repeat() -> Weight { - // Minimum execution time: 10_514 nanoseconds. - Weight::from_ref_time(10_821_000) - .saturating_add(RocksDbWeight::get().reads(1)) - .saturating_add(RocksDbWeight::get().writes(1)) + // Proof Size summary in bytes: + // Measured: `219` + // Estimated: `524` + // Minimum execution time: 7_124 nanoseconds. + Weight::from_parts(7_285_000, 524) + .saturating_add(RocksDbWeight::get().reads(1_u64)) + .saturating_add(RocksDbWeight::get().writes(1_u64)) } - // Storage: RandomnessCollectiveFlip RandomMaterial (r:1 w:0) - // Storage: Lottery Lottery (r:1 w:1) - // Storage: System Account (r:2 w:2) - // Storage: Lottery TicketsCount (r:1 w:1) - // Storage: Lottery Tickets (r:1 w:0) + /// Storage: RandomnessCollectiveFlip RandomMaterial (r:1 w:0) + /// Proof: RandomnessCollectiveFlip RandomMaterial (max_values: Some(1), max_size: Some(2594), added: 3089, mode: MaxEncodedLen) + /// Storage: Lottery Lottery (r:1 w:1) + /// Proof: Lottery Lottery (max_values: Some(1), max_size: Some(29), added: 524, mode: MaxEncodedLen) + /// Storage: System Account (r:2 w:2) + /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) + /// Storage: Lottery TicketsCount (r:1 w:1) + /// Proof: Lottery TicketsCount (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) + /// Storage: Lottery Tickets (r:1 w:0) + /// Proof: Lottery Tickets (max_values: None, max_size: Some(44), added: 2519, mode: MaxEncodedLen) fn on_initialize_end() -> Weight { - // Minimum execution time: 60_254 nanoseconds. - Weight::from_ref_time(61_924_000) - .saturating_add(RocksDbWeight::get().reads(6)) - .saturating_add(RocksDbWeight::get().writes(4)) + // Proof Size summary in bytes: + // Measured: `556` + // Estimated: `11837` + // Minimum execution time: 50_745 nanoseconds. + Weight::from_parts(52_232_000, 11837) + .saturating_add(RocksDbWeight::get().reads(6_u64)) + .saturating_add(RocksDbWeight::get().writes(4_u64)) } - // Storage: RandomnessCollectiveFlip RandomMaterial (r:1 w:0) - // Storage: Lottery Lottery (r:1 w:1) - // Storage: System Account (r:2 w:2) - // Storage: Lottery TicketsCount (r:1 w:1) - // Storage: Lottery Tickets (r:1 w:0) - // Storage: Lottery LotteryIndex (r:1 w:1) + /// Storage: RandomnessCollectiveFlip RandomMaterial (r:1 w:0) + /// Proof: RandomnessCollectiveFlip RandomMaterial (max_values: Some(1), max_size: Some(2594), added: 3089, mode: MaxEncodedLen) + /// Storage: Lottery Lottery (r:1 w:1) + /// Proof: Lottery Lottery (max_values: Some(1), max_size: Some(29), added: 524, mode: MaxEncodedLen) + /// Storage: System Account (r:2 w:2) + /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) + /// Storage: Lottery TicketsCount (r:1 w:1) + /// Proof: Lottery TicketsCount (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) + /// Storage: Lottery Tickets (r:1 w:0) + /// Proof: Lottery Tickets (max_values: None, max_size: Some(44), added: 2519, mode: MaxEncodedLen) + /// Storage: Lottery LotteryIndex (r:1 w:1) + /// Proof: Lottery LotteryIndex (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) fn on_initialize_repeat() -> Weight { - // Minimum execution time: 61_552 nanoseconds. - Weight::from_ref_time(62_152_000) - .saturating_add(RocksDbWeight::get().reads(7)) - .saturating_add(RocksDbWeight::get().writes(5)) + // Proof Size summary in bytes: + // Measured: `556` + // Estimated: `12336` + // Minimum execution time: 52_437 nanoseconds. + Weight::from_parts(53_063_000, 12336) + .saturating_add(RocksDbWeight::get().reads(7_u64)) + .saturating_add(RocksDbWeight::get().writes(5_u64)) } } diff --git a/frame/membership/src/weights.rs b/frame/membership/src/weights.rs index 11574bc8f..1be50afc5 100644 --- a/frame/membership/src/weights.rs +++ b/frame/membership/src/weights.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2022 Parity Technologies (UK) Ltd. +// Copyright (C) 2023 Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); @@ -18,7 +18,8 @@ //! Autogenerated weights for pallet_membership //! //! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev -//! DATE: 2022-11-07, STEPS: `50`, REPEAT: 20, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! DATE: 2023-01-24, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! WORST CASE MAP SIZE: `1000000` //! HOSTNAME: `bm2`, CPU: `Intel(R) Core(TM) i7-7700K CPU @ 4.20GHz` //! EXECUTION: Some(Wasm), WASM-EXECUTION: Compiled, CHAIN: Some("dev"), DB CACHE: 1024 @@ -59,190 +60,302 @@ pub trait WeightInfo { /// Weights for pallet_membership using the Substrate node and recommended hardware. pub struct SubstrateWeight(PhantomData); impl WeightInfo for SubstrateWeight { - // Storage: TechnicalMembership Members (r:1 w:1) - // Storage: TechnicalCommittee Proposals (r:1 w:0) - // Storage: TechnicalCommittee Members (r:0 w:1) - // Storage: TechnicalCommittee Prime (r:0 w:1) + /// Storage: TechnicalMembership Members (r:1 w:1) + /// Proof: TechnicalMembership Members (max_values: Some(1), max_size: Some(3202), added: 3697, mode: MaxEncodedLen) + /// Storage: TechnicalCommittee Proposals (r:1 w:0) + /// Proof Skipped: TechnicalCommittee Proposals (max_values: Some(1), max_size: None, mode: Measured) + /// Storage: TechnicalCommittee Members (r:0 w:1) + /// Proof Skipped: TechnicalCommittee Members (max_values: Some(1), max_size: None, mode: Measured) + /// Storage: TechnicalCommittee Prime (r:0 w:1) + /// Proof Skipped: TechnicalCommittee Prime (max_values: Some(1), max_size: None, mode: Measured) /// The range of component `m` is `[1, 99]`. fn add_member(m: u32, ) -> Weight { - // Minimum execution time: 23_796 nanoseconds. - Weight::from_ref_time(24_829_996 as u64) - // Standard Error: 723 - .saturating_add(Weight::from_ref_time(48_467 as u64).saturating_mul(m as u64)) - .saturating_add(T::DbWeight::get().reads(2 as u64)) - .saturating_add(T::DbWeight::get().writes(3 as u64)) + // Proof Size summary in bytes: + // Measured: `238 + m * (64 ±0)` + // Estimated: `4903 + m * (192 ±0)` + // Minimum execution time: 15_673 nanoseconds. + Weight::from_parts(16_830_288, 4903) + // Standard Error: 570 + .saturating_add(Weight::from_ref_time(41_959).saturating_mul(m.into())) + .saturating_add(T::DbWeight::get().reads(2_u64)) + .saturating_add(T::DbWeight::get().writes(3_u64)) + .saturating_add(Weight::from_proof_size(192).saturating_mul(m.into())) } - // Storage: TechnicalMembership Members (r:1 w:1) - // Storage: TechnicalCommittee Proposals (r:1 w:0) - // Storage: TechnicalMembership Prime (r:1 w:0) - // Storage: TechnicalCommittee Members (r:0 w:1) - // Storage: TechnicalCommittee Prime (r:0 w:1) + /// Storage: TechnicalMembership Members (r:1 w:1) + /// Proof: TechnicalMembership Members (max_values: Some(1), max_size: Some(3202), added: 3697, mode: MaxEncodedLen) + /// Storage: TechnicalCommittee Proposals (r:1 w:0) + /// Proof Skipped: TechnicalCommittee Proposals (max_values: Some(1), max_size: None, mode: Measured) + /// Storage: TechnicalMembership Prime (r:1 w:0) + /// Proof: TechnicalMembership Prime (max_values: Some(1), max_size: Some(32), added: 527, mode: MaxEncodedLen) + /// Storage: TechnicalCommittee Members (r:0 w:1) + /// Proof Skipped: TechnicalCommittee Members (max_values: Some(1), max_size: None, mode: Measured) + /// Storage: TechnicalCommittee Prime (r:0 w:1) + /// Proof Skipped: TechnicalCommittee Prime (max_values: Some(1), max_size: None, mode: Measured) /// The range of component `m` is `[2, 100]`. fn remove_member(m: u32, ) -> Weight { - // Minimum execution time: 27_255 nanoseconds. - Weight::from_ref_time(28_234_490 as u64) - // Standard Error: 833 - .saturating_add(Weight::from_ref_time(34_894 as u64).saturating_mul(m as u64)) - .saturating_add(T::DbWeight::get().reads(3 as u64)) - .saturating_add(T::DbWeight::get().writes(3 as u64)) + // Proof Size summary in bytes: + // Measured: `342 + m * (64 ±0)` + // Estimated: `5742 + m * (192 ±0)` + // Minimum execution time: 18_231 nanoseconds. + Weight::from_parts(19_081_297, 5742) + // Standard Error: 571 + .saturating_add(Weight::from_ref_time(41_331).saturating_mul(m.into())) + .saturating_add(T::DbWeight::get().reads(3_u64)) + .saturating_add(T::DbWeight::get().writes(3_u64)) + .saturating_add(Weight::from_proof_size(192).saturating_mul(m.into())) } - // Storage: TechnicalMembership Members (r:1 w:1) - // Storage: TechnicalCommittee Proposals (r:1 w:0) - // Storage: TechnicalMembership Prime (r:1 w:0) - // Storage: TechnicalCommittee Members (r:0 w:1) - // Storage: TechnicalCommittee Prime (r:0 w:1) + /// Storage: TechnicalMembership Members (r:1 w:1) + /// Proof: TechnicalMembership Members (max_values: Some(1), max_size: Some(3202), added: 3697, mode: MaxEncodedLen) + /// Storage: TechnicalCommittee Proposals (r:1 w:0) + /// Proof Skipped: TechnicalCommittee Proposals (max_values: Some(1), max_size: None, mode: Measured) + /// Storage: TechnicalMembership Prime (r:1 w:0) + /// Proof: TechnicalMembership Prime (max_values: Some(1), max_size: Some(32), added: 527, mode: MaxEncodedLen) + /// Storage: TechnicalCommittee Members (r:0 w:1) + /// Proof Skipped: TechnicalCommittee Members (max_values: Some(1), max_size: None, mode: Measured) + /// Storage: TechnicalCommittee Prime (r:0 w:1) + /// Proof Skipped: TechnicalCommittee Prime (max_values: Some(1), max_size: None, mode: Measured) /// The range of component `m` is `[2, 100]`. fn swap_member(m: u32, ) -> Weight { - // Minimum execution time: 26_626 nanoseconds. - Weight::from_ref_time(27_989_042 as u64) - // Standard Error: 729 - .saturating_add(Weight::from_ref_time(51_567 as u64).saturating_mul(m as u64)) - .saturating_add(T::DbWeight::get().reads(3 as u64)) - .saturating_add(T::DbWeight::get().writes(3 as u64)) + // Proof Size summary in bytes: + // Measured: `342 + m * (64 ±0)` + // Estimated: `5742 + m * (192 ±0)` + // Minimum execution time: 18_517 nanoseconds. + Weight::from_parts(19_388_310, 5742) + // Standard Error: 625 + .saturating_add(Weight::from_ref_time(51_422).saturating_mul(m.into())) + .saturating_add(T::DbWeight::get().reads(3_u64)) + .saturating_add(T::DbWeight::get().writes(3_u64)) + .saturating_add(Weight::from_proof_size(192).saturating_mul(m.into())) } - // Storage: TechnicalMembership Members (r:1 w:1) - // Storage: TechnicalCommittee Proposals (r:1 w:0) - // Storage: TechnicalMembership Prime (r:1 w:0) - // Storage: TechnicalCommittee Members (r:0 w:1) - // Storage: TechnicalCommittee Prime (r:0 w:1) + /// Storage: TechnicalMembership Members (r:1 w:1) + /// Proof: TechnicalMembership Members (max_values: Some(1), max_size: Some(3202), added: 3697, mode: MaxEncodedLen) + /// Storage: TechnicalCommittee Proposals (r:1 w:0) + /// Proof Skipped: TechnicalCommittee Proposals (max_values: Some(1), max_size: None, mode: Measured) + /// Storage: TechnicalMembership Prime (r:1 w:0) + /// Proof: TechnicalMembership Prime (max_values: Some(1), max_size: Some(32), added: 527, mode: MaxEncodedLen) + /// Storage: TechnicalCommittee Members (r:0 w:1) + /// Proof Skipped: TechnicalCommittee Members (max_values: Some(1), max_size: None, mode: Measured) + /// Storage: TechnicalCommittee Prime (r:0 w:1) + /// Proof Skipped: TechnicalCommittee Prime (max_values: Some(1), max_size: None, mode: Measured) /// The range of component `m` is `[1, 100]`. fn reset_member(m: u32, ) -> Weight { - // Minimum execution time: 25_412 nanoseconds. - Weight::from_ref_time(27_713_414 as u64) - // Standard Error: 883 - .saturating_add(Weight::from_ref_time(157_085 as u64).saturating_mul(m as u64)) - .saturating_add(T::DbWeight::get().reads(3 as u64)) - .saturating_add(T::DbWeight::get().writes(3 as u64)) + // Proof Size summary in bytes: + // Measured: `342 + m * (64 ±0)` + // Estimated: `5742 + m * (192 ±0)` + // Minimum execution time: 17_628 nanoseconds. + Weight::from_parts(19_258_882, 5742) + // Standard Error: 820 + .saturating_add(Weight::from_ref_time(153_956).saturating_mul(m.into())) + .saturating_add(T::DbWeight::get().reads(3_u64)) + .saturating_add(T::DbWeight::get().writes(3_u64)) + .saturating_add(Weight::from_proof_size(192).saturating_mul(m.into())) } - // Storage: TechnicalMembership Members (r:1 w:1) - // Storage: TechnicalCommittee Proposals (r:1 w:0) - // Storage: TechnicalMembership Prime (r:1 w:1) - // Storage: TechnicalCommittee Members (r:0 w:1) - // Storage: TechnicalCommittee Prime (r:0 w:1) + /// Storage: TechnicalMembership Members (r:1 w:1) + /// Proof: TechnicalMembership Members (max_values: Some(1), max_size: Some(3202), added: 3697, mode: MaxEncodedLen) + /// Storage: TechnicalCommittee Proposals (r:1 w:0) + /// Proof Skipped: TechnicalCommittee Proposals (max_values: Some(1), max_size: None, mode: Measured) + /// Storage: TechnicalMembership Prime (r:1 w:1) + /// Proof: TechnicalMembership Prime (max_values: Some(1), max_size: Some(32), added: 527, mode: MaxEncodedLen) + /// Storage: TechnicalCommittee Members (r:0 w:1) + /// Proof Skipped: TechnicalCommittee Members (max_values: Some(1), max_size: None, mode: Measured) + /// Storage: TechnicalCommittee Prime (r:0 w:1) + /// Proof Skipped: TechnicalCommittee Prime (max_values: Some(1), max_size: None, mode: Measured) /// The range of component `m` is `[1, 100]`. fn change_key(m: u32, ) -> Weight { - // Minimum execution time: 27_122 nanoseconds. - Weight::from_ref_time(28_477_394 as u64) - // Standard Error: 801 - .saturating_add(Weight::from_ref_time(56_383 as u64).saturating_mul(m as u64)) - .saturating_add(T::DbWeight::get().reads(3 as u64)) - .saturating_add(T::DbWeight::get().writes(4 as u64)) + // Proof Size summary in bytes: + // Measured: `342 + m * (64 ±0)` + // Estimated: `5742 + m * (192 ±0)` + // Minimum execution time: 19_031 nanoseconds. + Weight::from_parts(20_264_948, 5742) + // Standard Error: 707 + .saturating_add(Weight::from_ref_time(51_060).saturating_mul(m.into())) + .saturating_add(T::DbWeight::get().reads(3_u64)) + .saturating_add(T::DbWeight::get().writes(4_u64)) + .saturating_add(Weight::from_proof_size(192).saturating_mul(m.into())) } - // Storage: TechnicalMembership Members (r:1 w:0) - // Storage: TechnicalMembership Prime (r:0 w:1) - // Storage: TechnicalCommittee Prime (r:0 w:1) + /// Storage: TechnicalMembership Members (r:1 w:0) + /// Proof: TechnicalMembership Members (max_values: Some(1), max_size: Some(3202), added: 3697, mode: MaxEncodedLen) + /// Storage: TechnicalMembership Prime (r:0 w:1) + /// Proof: TechnicalMembership Prime (max_values: Some(1), max_size: Some(32), added: 527, mode: MaxEncodedLen) + /// Storage: TechnicalCommittee Prime (r:0 w:1) + /// Proof Skipped: TechnicalCommittee Prime (max_values: Some(1), max_size: None, mode: Measured) /// The range of component `m` is `[1, 100]`. fn set_prime(m: u32, ) -> Weight { - // Minimum execution time: 9_368 nanoseconds. - Weight::from_ref_time(10_133_132 as u64) - // Standard Error: 366 - .saturating_add(Weight::from_ref_time(17_708 as u64).saturating_mul(m as u64)) - .saturating_add(T::DbWeight::get().reads(1 as u64)) - .saturating_add(T::DbWeight::get().writes(2 as u64)) + // Proof Size summary in bytes: + // Measured: `64 + m * (32 ±0)` + // Estimated: `3761 + m * (32 ±0)` + // Minimum execution time: 6_897 nanoseconds. + Weight::from_parts(7_455_387, 3761) + // Standard Error: 326 + .saturating_add(Weight::from_ref_time(16_653).saturating_mul(m.into())) + .saturating_add(T::DbWeight::get().reads(1_u64)) + .saturating_add(T::DbWeight::get().writes(2_u64)) + .saturating_add(Weight::from_proof_size(32).saturating_mul(m.into())) } - // Storage: TechnicalMembership Prime (r:0 w:1) - // Storage: TechnicalCommittee Prime (r:0 w:1) + /// Storage: TechnicalMembership Prime (r:0 w:1) + /// Proof: TechnicalMembership Prime (max_values: Some(1), max_size: Some(32), added: 527, mode: MaxEncodedLen) + /// Storage: TechnicalCommittee Prime (r:0 w:1) + /// Proof Skipped: TechnicalCommittee Prime (max_values: Some(1), max_size: None, mode: Measured) /// The range of component `m` is `[1, 100]`. fn clear_prime(m: u32, ) -> Weight { - // Minimum execution time: 5_546 nanoseconds. - Weight::from_ref_time(5_962_740 as u64) - // Standard Error: 186 - .saturating_add(Weight::from_ref_time(2_096 as u64).saturating_mul(m as u64)) - .saturating_add(T::DbWeight::get().writes(2 as u64)) + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 3_400 nanoseconds. + Weight::from_ref_time(3_703_421) + // Standard Error: 119 + .saturating_add(Weight::from_ref_time(915).saturating_mul(m.into())) + .saturating_add(T::DbWeight::get().writes(2_u64)) } } // For backwards compatibility and tests impl WeightInfo for () { - // Storage: TechnicalMembership Members (r:1 w:1) - // Storage: TechnicalCommittee Proposals (r:1 w:0) - // Storage: TechnicalCommittee Members (r:0 w:1) - // Storage: TechnicalCommittee Prime (r:0 w:1) + /// Storage: TechnicalMembership Members (r:1 w:1) + /// Proof: TechnicalMembership Members (max_values: Some(1), max_size: Some(3202), added: 3697, mode: MaxEncodedLen) + /// Storage: TechnicalCommittee Proposals (r:1 w:0) + /// Proof Skipped: TechnicalCommittee Proposals (max_values: Some(1), max_size: None, mode: Measured) + /// Storage: TechnicalCommittee Members (r:0 w:1) + /// Proof Skipped: TechnicalCommittee Members (max_values: Some(1), max_size: None, mode: Measured) + /// Storage: TechnicalCommittee Prime (r:0 w:1) + /// Proof Skipped: TechnicalCommittee Prime (max_values: Some(1), max_size: None, mode: Measured) /// The range of component `m` is `[1, 99]`. fn add_member(m: u32, ) -> Weight { - // Minimum execution time: 23_796 nanoseconds. - Weight::from_ref_time(24_829_996 as u64) - // Standard Error: 723 - .saturating_add(Weight::from_ref_time(48_467 as u64).saturating_mul(m as u64)) - .saturating_add(RocksDbWeight::get().reads(2 as u64)) - .saturating_add(RocksDbWeight::get().writes(3 as u64)) + // Proof Size summary in bytes: + // Measured: `238 + m * (64 ±0)` + // Estimated: `4903 + m * (192 ±0)` + // Minimum execution time: 15_673 nanoseconds. + Weight::from_parts(16_830_288, 4903) + // Standard Error: 570 + .saturating_add(Weight::from_ref_time(41_959).saturating_mul(m.into())) + .saturating_add(RocksDbWeight::get().reads(2_u64)) + .saturating_add(RocksDbWeight::get().writes(3_u64)) + .saturating_add(Weight::from_proof_size(192).saturating_mul(m.into())) } - // Storage: TechnicalMembership Members (r:1 w:1) - // Storage: TechnicalCommittee Proposals (r:1 w:0) - // Storage: TechnicalMembership Prime (r:1 w:0) - // Storage: TechnicalCommittee Members (r:0 w:1) - // Storage: TechnicalCommittee Prime (r:0 w:1) + /// Storage: TechnicalMembership Members (r:1 w:1) + /// Proof: TechnicalMembership Members (max_values: Some(1), max_size: Some(3202), added: 3697, mode: MaxEncodedLen) + /// Storage: TechnicalCommittee Proposals (r:1 w:0) + /// Proof Skipped: TechnicalCommittee Proposals (max_values: Some(1), max_size: None, mode: Measured) + /// Storage: TechnicalMembership Prime (r:1 w:0) + /// Proof: TechnicalMembership Prime (max_values: Some(1), max_size: Some(32), added: 527, mode: MaxEncodedLen) + /// Storage: TechnicalCommittee Members (r:0 w:1) + /// Proof Skipped: TechnicalCommittee Members (max_values: Some(1), max_size: None, mode: Measured) + /// Storage: TechnicalCommittee Prime (r:0 w:1) + /// Proof Skipped: TechnicalCommittee Prime (max_values: Some(1), max_size: None, mode: Measured) /// The range of component `m` is `[2, 100]`. fn remove_member(m: u32, ) -> Weight { - // Minimum execution time: 27_255 nanoseconds. - Weight::from_ref_time(28_234_490 as u64) - // Standard Error: 833 - .saturating_add(Weight::from_ref_time(34_894 as u64).saturating_mul(m as u64)) - .saturating_add(RocksDbWeight::get().reads(3 as u64)) - .saturating_add(RocksDbWeight::get().writes(3 as u64)) + // Proof Size summary in bytes: + // Measured: `342 + m * (64 ±0)` + // Estimated: `5742 + m * (192 ±0)` + // Minimum execution time: 18_231 nanoseconds. + Weight::from_parts(19_081_297, 5742) + // Standard Error: 571 + .saturating_add(Weight::from_ref_time(41_331).saturating_mul(m.into())) + .saturating_add(RocksDbWeight::get().reads(3_u64)) + .saturating_add(RocksDbWeight::get().writes(3_u64)) + .saturating_add(Weight::from_proof_size(192).saturating_mul(m.into())) } - // Storage: TechnicalMembership Members (r:1 w:1) - // Storage: TechnicalCommittee Proposals (r:1 w:0) - // Storage: TechnicalMembership Prime (r:1 w:0) - // Storage: TechnicalCommittee Members (r:0 w:1) - // Storage: TechnicalCommittee Prime (r:0 w:1) + /// Storage: TechnicalMembership Members (r:1 w:1) + /// Proof: TechnicalMembership Members (max_values: Some(1), max_size: Some(3202), added: 3697, mode: MaxEncodedLen) + /// Storage: TechnicalCommittee Proposals (r:1 w:0) + /// Proof Skipped: TechnicalCommittee Proposals (max_values: Some(1), max_size: None, mode: Measured) + /// Storage: TechnicalMembership Prime (r:1 w:0) + /// Proof: TechnicalMembership Prime (max_values: Some(1), max_size: Some(32), added: 527, mode: MaxEncodedLen) + /// Storage: TechnicalCommittee Members (r:0 w:1) + /// Proof Skipped: TechnicalCommittee Members (max_values: Some(1), max_size: None, mode: Measured) + /// Storage: TechnicalCommittee Prime (r:0 w:1) + /// Proof Skipped: TechnicalCommittee Prime (max_values: Some(1), max_size: None, mode: Measured) /// The range of component `m` is `[2, 100]`. fn swap_member(m: u32, ) -> Weight { - // Minimum execution time: 26_626 nanoseconds. - Weight::from_ref_time(27_989_042 as u64) - // Standard Error: 729 - .saturating_add(Weight::from_ref_time(51_567 as u64).saturating_mul(m as u64)) - .saturating_add(RocksDbWeight::get().reads(3 as u64)) - .saturating_add(RocksDbWeight::get().writes(3 as u64)) + // Proof Size summary in bytes: + // Measured: `342 + m * (64 ±0)` + // Estimated: `5742 + m * (192 ±0)` + // Minimum execution time: 18_517 nanoseconds. + Weight::from_parts(19_388_310, 5742) + // Standard Error: 625 + .saturating_add(Weight::from_ref_time(51_422).saturating_mul(m.into())) + .saturating_add(RocksDbWeight::get().reads(3_u64)) + .saturating_add(RocksDbWeight::get().writes(3_u64)) + .saturating_add(Weight::from_proof_size(192).saturating_mul(m.into())) } - // Storage: TechnicalMembership Members (r:1 w:1) - // Storage: TechnicalCommittee Proposals (r:1 w:0) - // Storage: TechnicalMembership Prime (r:1 w:0) - // Storage: TechnicalCommittee Members (r:0 w:1) - // Storage: TechnicalCommittee Prime (r:0 w:1) + /// Storage: TechnicalMembership Members (r:1 w:1) + /// Proof: TechnicalMembership Members (max_values: Some(1), max_size: Some(3202), added: 3697, mode: MaxEncodedLen) + /// Storage: TechnicalCommittee Proposals (r:1 w:0) + /// Proof Skipped: TechnicalCommittee Proposals (max_values: Some(1), max_size: None, mode: Measured) + /// Storage: TechnicalMembership Prime (r:1 w:0) + /// Proof: TechnicalMembership Prime (max_values: Some(1), max_size: Some(32), added: 527, mode: MaxEncodedLen) + /// Storage: TechnicalCommittee Members (r:0 w:1) + /// Proof Skipped: TechnicalCommittee Members (max_values: Some(1), max_size: None, mode: Measured) + /// Storage: TechnicalCommittee Prime (r:0 w:1) + /// Proof Skipped: TechnicalCommittee Prime (max_values: Some(1), max_size: None, mode: Measured) /// The range of component `m` is `[1, 100]`. fn reset_member(m: u32, ) -> Weight { - // Minimum execution time: 25_412 nanoseconds. - Weight::from_ref_time(27_713_414 as u64) - // Standard Error: 883 - .saturating_add(Weight::from_ref_time(157_085 as u64).saturating_mul(m as u64)) - .saturating_add(RocksDbWeight::get().reads(3 as u64)) - .saturating_add(RocksDbWeight::get().writes(3 as u64)) + // Proof Size summary in bytes: + // Measured: `342 + m * (64 ±0)` + // Estimated: `5742 + m * (192 ±0)` + // Minimum execution time: 17_628 nanoseconds. + Weight::from_parts(19_258_882, 5742) + // Standard Error: 820 + .saturating_add(Weight::from_ref_time(153_956).saturating_mul(m.into())) + .saturating_add(RocksDbWeight::get().reads(3_u64)) + .saturating_add(RocksDbWeight::get().writes(3_u64)) + .saturating_add(Weight::from_proof_size(192).saturating_mul(m.into())) } - // Storage: TechnicalMembership Members (r:1 w:1) - // Storage: TechnicalCommittee Proposals (r:1 w:0) - // Storage: TechnicalMembership Prime (r:1 w:1) - // Storage: TechnicalCommittee Members (r:0 w:1) - // Storage: TechnicalCommittee Prime (r:0 w:1) + /// Storage: TechnicalMembership Members (r:1 w:1) + /// Proof: TechnicalMembership Members (max_values: Some(1), max_size: Some(3202), added: 3697, mode: MaxEncodedLen) + /// Storage: TechnicalCommittee Proposals (r:1 w:0) + /// Proof Skipped: TechnicalCommittee Proposals (max_values: Some(1), max_size: None, mode: Measured) + /// Storage: TechnicalMembership Prime (r:1 w:1) + /// Proof: TechnicalMembership Prime (max_values: Some(1), max_size: Some(32), added: 527, mode: MaxEncodedLen) + /// Storage: TechnicalCommittee Members (r:0 w:1) + /// Proof Skipped: TechnicalCommittee Members (max_values: Some(1), max_size: None, mode: Measured) + /// Storage: TechnicalCommittee Prime (r:0 w:1) + /// Proof Skipped: TechnicalCommittee Prime (max_values: Some(1), max_size: None, mode: Measured) /// The range of component `m` is `[1, 100]`. fn change_key(m: u32, ) -> Weight { - // Minimum execution time: 27_122 nanoseconds. - Weight::from_ref_time(28_477_394 as u64) - // Standard Error: 801 - .saturating_add(Weight::from_ref_time(56_383 as u64).saturating_mul(m as u64)) - .saturating_add(RocksDbWeight::get().reads(3 as u64)) - .saturating_add(RocksDbWeight::get().writes(4 as u64)) + // Proof Size summary in bytes: + // Measured: `342 + m * (64 ±0)` + // Estimated: `5742 + m * (192 ±0)` + // Minimum execution time: 19_031 nanoseconds. + Weight::from_parts(20_264_948, 5742) + // Standard Error: 707 + .saturating_add(Weight::from_ref_time(51_060).saturating_mul(m.into())) + .saturating_add(RocksDbWeight::get().reads(3_u64)) + .saturating_add(RocksDbWeight::get().writes(4_u64)) + .saturating_add(Weight::from_proof_size(192).saturating_mul(m.into())) } - // Storage: TechnicalMembership Members (r:1 w:0) - // Storage: TechnicalMembership Prime (r:0 w:1) - // Storage: TechnicalCommittee Prime (r:0 w:1) + /// Storage: TechnicalMembership Members (r:1 w:0) + /// Proof: TechnicalMembership Members (max_values: Some(1), max_size: Some(3202), added: 3697, mode: MaxEncodedLen) + /// Storage: TechnicalMembership Prime (r:0 w:1) + /// Proof: TechnicalMembership Prime (max_values: Some(1), max_size: Some(32), added: 527, mode: MaxEncodedLen) + /// Storage: TechnicalCommittee Prime (r:0 w:1) + /// Proof Skipped: TechnicalCommittee Prime (max_values: Some(1), max_size: None, mode: Measured) /// The range of component `m` is `[1, 100]`. fn set_prime(m: u32, ) -> Weight { - // Minimum execution time: 9_368 nanoseconds. - Weight::from_ref_time(10_133_132 as u64) - // Standard Error: 366 - .saturating_add(Weight::from_ref_time(17_708 as u64).saturating_mul(m as u64)) - .saturating_add(RocksDbWeight::get().reads(1 as u64)) - .saturating_add(RocksDbWeight::get().writes(2 as u64)) + // Proof Size summary in bytes: + // Measured: `64 + m * (32 ±0)` + // Estimated: `3761 + m * (32 ±0)` + // Minimum execution time: 6_897 nanoseconds. + Weight::from_parts(7_455_387, 3761) + // Standard Error: 326 + .saturating_add(Weight::from_ref_time(16_653).saturating_mul(m.into())) + .saturating_add(RocksDbWeight::get().reads(1_u64)) + .saturating_add(RocksDbWeight::get().writes(2_u64)) + .saturating_add(Weight::from_proof_size(32).saturating_mul(m.into())) } - // Storage: TechnicalMembership Prime (r:0 w:1) - // Storage: TechnicalCommittee Prime (r:0 w:1) + /// Storage: TechnicalMembership Prime (r:0 w:1) + /// Proof: TechnicalMembership Prime (max_values: Some(1), max_size: Some(32), added: 527, mode: MaxEncodedLen) + /// Storage: TechnicalCommittee Prime (r:0 w:1) + /// Proof Skipped: TechnicalCommittee Prime (max_values: Some(1), max_size: None, mode: Measured) /// The range of component `m` is `[1, 100]`. fn clear_prime(m: u32, ) -> Weight { - // Minimum execution time: 5_546 nanoseconds. - Weight::from_ref_time(5_962_740 as u64) - // Standard Error: 186 - .saturating_add(Weight::from_ref_time(2_096 as u64).saturating_mul(m as u64)) - .saturating_add(RocksDbWeight::get().writes(2 as u64)) + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 3_400 nanoseconds. + Weight::from_ref_time(3_703_421) + // Standard Error: 119 + .saturating_add(Weight::from_ref_time(915).saturating_mul(m.into())) + .saturating_add(RocksDbWeight::get().writes(2_u64)) } } diff --git a/frame/message-queue/src/weights.rs b/frame/message-queue/src/weights.rs index cd9268ffd..325fc2083 100644 --- a/frame/message-queue/src/weights.rs +++ b/frame/message-queue/src/weights.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2022 Parity Technologies (UK) Ltd. +// Copyright (C) 2023 Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); @@ -18,25 +18,25 @@ //! Autogenerated weights for pallet_message_queue //! //! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev -//! DATE: 2022-12-08, STEPS: `50`, REPEAT: 20, LOW RANGE: `[]`, HIGH RANGE: `[]` -//! HOSTNAME: `bm3`, CPU: `Intel(R) Core(TM) i7-7700K CPU @ 4.20GHz` +//! DATE: 2023-01-24, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! WORST CASE MAP SIZE: `1000000` +//! HOSTNAME: `bm2`, CPU: `Intel(R) Core(TM) i7-7700K CPU @ 4.20GHz` //! EXECUTION: Some(Wasm), WASM-EXECUTION: Compiled, CHAIN: Some("dev"), DB CACHE: 1024 // Executed Command: -// /home/benchbot/cargo_target_dir/production/substrate +// ./target/production/substrate // benchmark // pallet +// --chain=dev // --steps=50 // --repeat=20 +// --pallet=pallet_message_queue // --extrinsic=* // --execution=wasm // --wasm-execution=compiled // --heap-pages=4096 -// --json-file=/var/lib/gitlab-runner/builds/zyw4fam_/0/parity/mirrors/substrate/.git/.artifacts/bench.json -// --pallet=pallet_message_queue -// --chain=dev -// --header=./HEADER-APACHE2 // --output=./frame/message-queue/src/weights.rs +// --header=./HEADER-APACHE2 // --template=./.maintain/frame-weight-template.hbs #![cfg_attr(rustfmt, rustfmt_skip)] @@ -63,154 +63,244 @@ pub trait WeightInfo { /// Weights for pallet_message_queue using the Substrate node and recommended hardware. pub struct SubstrateWeight(PhantomData); impl WeightInfo for SubstrateWeight { - // Storage: MessageQueue ServiceHead (r:1 w:0) - // Storage: MessageQueue BookStateFor (r:2 w:2) + /// Storage: MessageQueue ServiceHead (r:1 w:0) + /// Proof: MessageQueue ServiceHead (max_values: Some(1), max_size: Some(5), added: 500, mode: MaxEncodedLen) + /// Storage: MessageQueue BookStateFor (r:2 w:2) + /// Proof: MessageQueue BookStateFor (max_values: None, max_size: Some(52), added: 2527, mode: MaxEncodedLen) fn ready_ring_knit() -> Weight { - // Minimum execution time: 12_330 nanoseconds. - Weight::from_ref_time(12_711_000) - .saturating_add(T::DbWeight::get().reads(3)) - .saturating_add(T::DbWeight::get().writes(2)) + // Proof Size summary in bytes: + // Measured: `837` + // Estimated: `5554` + // Minimum execution time: 12_676 nanoseconds. + Weight::from_parts(13_113_000, 5554) + .saturating_add(T::DbWeight::get().reads(3_u64)) + .saturating_add(T::DbWeight::get().writes(2_u64)) } - // Storage: MessageQueue BookStateFor (r:2 w:2) - // Storage: MessageQueue ServiceHead (r:1 w:1) + /// Storage: MessageQueue BookStateFor (r:2 w:2) + /// Proof: MessageQueue BookStateFor (max_values: None, max_size: Some(52), added: 2527, mode: MaxEncodedLen) + /// Storage: MessageQueue ServiceHead (r:1 w:1) + /// Proof: MessageQueue ServiceHead (max_values: Some(1), max_size: Some(5), added: 500, mode: MaxEncodedLen) fn ready_ring_unknit() -> Weight { - // Minimum execution time: 12_322 nanoseconds. - Weight::from_ref_time(12_560_000) - .saturating_add(T::DbWeight::get().reads(3)) - .saturating_add(T::DbWeight::get().writes(3)) + // Proof Size summary in bytes: + // Measured: `837` + // Estimated: `5554` + // Minimum execution time: 12_654 nanoseconds. + Weight::from_parts(12_969_000, 5554) + .saturating_add(T::DbWeight::get().reads(3_u64)) + .saturating_add(T::DbWeight::get().writes(3_u64)) } - // Storage: MessageQueue BookStateFor (r:1 w:1) + /// Storage: MessageQueue BookStateFor (r:1 w:1) + /// Proof: MessageQueue BookStateFor (max_values: None, max_size: Some(52), added: 2527, mode: MaxEncodedLen) fn service_queue_base() -> Weight { - // Minimum execution time: 4_652 nanoseconds. - Weight::from_ref_time(4_848_000) - .saturating_add(T::DbWeight::get().reads(1)) - .saturating_add(T::DbWeight::get().writes(1)) + // Proof Size summary in bytes: + // Measured: `576` + // Estimated: `2527` + // Minimum execution time: 5_096 nanoseconds. + Weight::from_parts(5_280_000, 2527) + .saturating_add(T::DbWeight::get().reads(1_u64)) + .saturating_add(T::DbWeight::get().writes(1_u64)) } - // Storage: MessageQueue Pages (r:1 w:1) + /// Storage: MessageQueue Pages (r:1 w:1) + /// Proof: MessageQueue Pages (max_values: None, max_size: Some(65585), added: 68060, mode: MaxEncodedLen) fn service_page_base_completion() -> Weight { - // Minimum execution time: 7_115 nanoseconds. - Weight::from_ref_time(7_407_000) - .saturating_add(T::DbWeight::get().reads(1)) - .saturating_add(T::DbWeight::get().writes(1)) + // Proof Size summary in bytes: + // Measured: `648` + // Estimated: `68060` + // Minimum execution time: 7_291 nanoseconds. + Weight::from_parts(7_564_000, 68060) + .saturating_add(T::DbWeight::get().reads(1_u64)) + .saturating_add(T::DbWeight::get().writes(1_u64)) } - // Storage: MessageQueue Pages (r:1 w:1) + /// Storage: MessageQueue Pages (r:1 w:1) + /// Proof: MessageQueue Pages (max_values: None, max_size: Some(65585), added: 68060, mode: MaxEncodedLen) fn service_page_base_no_completion() -> Weight { - // Minimum execution time: 6_974 nanoseconds. - Weight::from_ref_time(7_200_000) - .saturating_add(T::DbWeight::get().reads(1)) - .saturating_add(T::DbWeight::get().writes(1)) + // Proof Size summary in bytes: + // Measured: `648` + // Estimated: `68060` + // Minimum execution time: 7_401 nanoseconds. + Weight::from_parts(7_681_000, 68060) + .saturating_add(T::DbWeight::get().reads(1_u64)) + .saturating_add(T::DbWeight::get().writes(1_u64)) } fn service_page_item() -> Weight { - // Minimum execution time: 79_657 nanoseconds. - Weight::from_ref_time(80_050_000) + // Proof Size summary in bytes: + // Measured: `972` + // Estimated: `0` + // Minimum execution time: 79_412 nanoseconds. + Weight::from_ref_time(79_816_000) } - // Storage: MessageQueue ServiceHead (r:1 w:1) - // Storage: MessageQueue BookStateFor (r:1 w:0) + /// Storage: MessageQueue ServiceHead (r:1 w:1) + /// Proof: MessageQueue ServiceHead (max_values: Some(1), max_size: Some(5), added: 500, mode: MaxEncodedLen) + /// Storage: MessageQueue BookStateFor (r:1 w:0) + /// Proof: MessageQueue BookStateFor (max_values: None, max_size: Some(52), added: 2527, mode: MaxEncodedLen) fn bump_service_head() -> Weight { - // Minimum execution time: 7_598 nanoseconds. - Weight::from_ref_time(8_118_000) - .saturating_add(T::DbWeight::get().reads(2)) - .saturating_add(T::DbWeight::get().writes(1)) + // Proof Size summary in bytes: + // Measured: `712` + // Estimated: `3027` + // Minimum execution time: 8_258 nanoseconds. + Weight::from_parts(8_438_000, 3027) + .saturating_add(T::DbWeight::get().reads(2_u64)) + .saturating_add(T::DbWeight::get().writes(1_u64)) } - // Storage: MessageQueue BookStateFor (r:1 w:1) - // Storage: MessageQueue Pages (r:1 w:1) + /// Storage: MessageQueue BookStateFor (r:1 w:1) + /// Proof: MessageQueue BookStateFor (max_values: None, max_size: Some(52), added: 2527, mode: MaxEncodedLen) + /// Storage: MessageQueue Pages (r:1 w:1) + /// Proof: MessageQueue Pages (max_values: None, max_size: Some(65585), added: 68060, mode: MaxEncodedLen) fn reap_page() -> Weight { - // Minimum execution time: 60_562 nanoseconds. - Weight::from_ref_time(61_430_000) - .saturating_add(T::DbWeight::get().reads(2)) - .saturating_add(T::DbWeight::get().writes(2)) + // Proof Size summary in bytes: + // Measured: `66827` + // Estimated: `70587` + // Minimum execution time: 61_361 nanoseconds. + Weight::from_parts(62_103_000, 70587) + .saturating_add(T::DbWeight::get().reads(2_u64)) + .saturating_add(T::DbWeight::get().writes(2_u64)) } - // Storage: MessageQueue BookStateFor (r:1 w:1) - // Storage: MessageQueue Pages (r:1 w:1) + /// Storage: MessageQueue BookStateFor (r:1 w:1) + /// Proof: MessageQueue BookStateFor (max_values: None, max_size: Some(52), added: 2527, mode: MaxEncodedLen) + /// Storage: MessageQueue Pages (r:1 w:1) + /// Proof: MessageQueue Pages (max_values: None, max_size: Some(65585), added: 68060, mode: MaxEncodedLen) fn execute_overweight_page_removed() -> Weight { - // Minimum execution time: 74_582 nanoseconds. - Weight::from_ref_time(75_445_000) - .saturating_add(T::DbWeight::get().reads(2)) - .saturating_add(T::DbWeight::get().writes(2)) + // Proof Size summary in bytes: + // Measured: `66827` + // Estimated: `70587` + // Minimum execution time: 75_153 nanoseconds. + Weight::from_parts(76_093_000, 70587) + .saturating_add(T::DbWeight::get().reads(2_u64)) + .saturating_add(T::DbWeight::get().writes(2_u64)) } - // Storage: MessageQueue BookStateFor (r:1 w:1) - // Storage: MessageQueue Pages (r:1 w:1) + /// Storage: MessageQueue BookStateFor (r:1 w:1) + /// Proof: MessageQueue BookStateFor (max_values: None, max_size: Some(52), added: 2527, mode: MaxEncodedLen) + /// Storage: MessageQueue Pages (r:1 w:1) + /// Proof: MessageQueue Pages (max_values: None, max_size: Some(65585), added: 68060, mode: MaxEncodedLen) fn execute_overweight_page_updated() -> Weight { - // Minimum execution time: 87_526 nanoseconds. - Weight::from_ref_time(88_055_000) - .saturating_add(T::DbWeight::get().reads(2)) - .saturating_add(T::DbWeight::get().writes(2)) + // Proof Size summary in bytes: + // Measured: `66827` + // Estimated: `70587` + // Minimum execution time: 88_272 nanoseconds. + Weight::from_parts(89_373_000, 70587) + .saturating_add(T::DbWeight::get().reads(2_u64)) + .saturating_add(T::DbWeight::get().writes(2_u64)) } } // For backwards compatibility and tests impl WeightInfo for () { - // Storage: MessageQueue ServiceHead (r:1 w:0) - // Storage: MessageQueue BookStateFor (r:2 w:2) + /// Storage: MessageQueue ServiceHead (r:1 w:0) + /// Proof: MessageQueue ServiceHead (max_values: Some(1), max_size: Some(5), added: 500, mode: MaxEncodedLen) + /// Storage: MessageQueue BookStateFor (r:2 w:2) + /// Proof: MessageQueue BookStateFor (max_values: None, max_size: Some(52), added: 2527, mode: MaxEncodedLen) fn ready_ring_knit() -> Weight { - // Minimum execution time: 12_330 nanoseconds. - Weight::from_ref_time(12_711_000) - .saturating_add(RocksDbWeight::get().reads(3)) - .saturating_add(RocksDbWeight::get().writes(2)) + // Proof Size summary in bytes: + // Measured: `837` + // Estimated: `5554` + // Minimum execution time: 12_676 nanoseconds. + Weight::from_parts(13_113_000, 5554) + .saturating_add(RocksDbWeight::get().reads(3_u64)) + .saturating_add(RocksDbWeight::get().writes(2_u64)) } - // Storage: MessageQueue BookStateFor (r:2 w:2) - // Storage: MessageQueue ServiceHead (r:1 w:1) + /// Storage: MessageQueue BookStateFor (r:2 w:2) + /// Proof: MessageQueue BookStateFor (max_values: None, max_size: Some(52), added: 2527, mode: MaxEncodedLen) + /// Storage: MessageQueue ServiceHead (r:1 w:1) + /// Proof: MessageQueue ServiceHead (max_values: Some(1), max_size: Some(5), added: 500, mode: MaxEncodedLen) fn ready_ring_unknit() -> Weight { - // Minimum execution time: 12_322 nanoseconds. - Weight::from_ref_time(12_560_000) - .saturating_add(RocksDbWeight::get().reads(3)) - .saturating_add(RocksDbWeight::get().writes(3)) + // Proof Size summary in bytes: + // Measured: `837` + // Estimated: `5554` + // Minimum execution time: 12_654 nanoseconds. + Weight::from_parts(12_969_000, 5554) + .saturating_add(RocksDbWeight::get().reads(3_u64)) + .saturating_add(RocksDbWeight::get().writes(3_u64)) } - // Storage: MessageQueue BookStateFor (r:1 w:1) + /// Storage: MessageQueue BookStateFor (r:1 w:1) + /// Proof: MessageQueue BookStateFor (max_values: None, max_size: Some(52), added: 2527, mode: MaxEncodedLen) fn service_queue_base() -> Weight { - // Minimum execution time: 4_652 nanoseconds. - Weight::from_ref_time(4_848_000) - .saturating_add(RocksDbWeight::get().reads(1)) - .saturating_add(RocksDbWeight::get().writes(1)) + // Proof Size summary in bytes: + // Measured: `576` + // Estimated: `2527` + // Minimum execution time: 5_096 nanoseconds. + Weight::from_parts(5_280_000, 2527) + .saturating_add(RocksDbWeight::get().reads(1_u64)) + .saturating_add(RocksDbWeight::get().writes(1_u64)) } - // Storage: MessageQueue Pages (r:1 w:1) + /// Storage: MessageQueue Pages (r:1 w:1) + /// Proof: MessageQueue Pages (max_values: None, max_size: Some(65585), added: 68060, mode: MaxEncodedLen) fn service_page_base_completion() -> Weight { - // Minimum execution time: 7_115 nanoseconds. - Weight::from_ref_time(7_407_000) - .saturating_add(RocksDbWeight::get().reads(1)) - .saturating_add(RocksDbWeight::get().writes(1)) + // Proof Size summary in bytes: + // Measured: `648` + // Estimated: `68060` + // Minimum execution time: 7_291 nanoseconds. + Weight::from_parts(7_564_000, 68060) + .saturating_add(RocksDbWeight::get().reads(1_u64)) + .saturating_add(RocksDbWeight::get().writes(1_u64)) } - // Storage: MessageQueue Pages (r:1 w:1) + /// Storage: MessageQueue Pages (r:1 w:1) + /// Proof: MessageQueue Pages (max_values: None, max_size: Some(65585), added: 68060, mode: MaxEncodedLen) fn service_page_base_no_completion() -> Weight { - // Minimum execution time: 6_974 nanoseconds. - Weight::from_ref_time(7_200_000) - .saturating_add(RocksDbWeight::get().reads(1)) - .saturating_add(RocksDbWeight::get().writes(1)) + // Proof Size summary in bytes: + // Measured: `648` + // Estimated: `68060` + // Minimum execution time: 7_401 nanoseconds. + Weight::from_parts(7_681_000, 68060) + .saturating_add(RocksDbWeight::get().reads(1_u64)) + .saturating_add(RocksDbWeight::get().writes(1_u64)) } fn service_page_item() -> Weight { - // Minimum execution time: 79_657 nanoseconds. - Weight::from_ref_time(80_050_000) + // Proof Size summary in bytes: + // Measured: `972` + // Estimated: `0` + // Minimum execution time: 79_412 nanoseconds. + Weight::from_ref_time(79_816_000) } - // Storage: MessageQueue ServiceHead (r:1 w:1) - // Storage: MessageQueue BookStateFor (r:1 w:0) + /// Storage: MessageQueue ServiceHead (r:1 w:1) + /// Proof: MessageQueue ServiceHead (max_values: Some(1), max_size: Some(5), added: 500, mode: MaxEncodedLen) + /// Storage: MessageQueue BookStateFor (r:1 w:0) + /// Proof: MessageQueue BookStateFor (max_values: None, max_size: Some(52), added: 2527, mode: MaxEncodedLen) fn bump_service_head() -> Weight { - // Minimum execution time: 7_598 nanoseconds. - Weight::from_ref_time(8_118_000) - .saturating_add(RocksDbWeight::get().reads(2)) - .saturating_add(RocksDbWeight::get().writes(1)) + // Proof Size summary in bytes: + // Measured: `712` + // Estimated: `3027` + // Minimum execution time: 8_258 nanoseconds. + Weight::from_parts(8_438_000, 3027) + .saturating_add(RocksDbWeight::get().reads(2_u64)) + .saturating_add(RocksDbWeight::get().writes(1_u64)) } - // Storage: MessageQueue BookStateFor (r:1 w:1) - // Storage: MessageQueue Pages (r:1 w:1) + /// Storage: MessageQueue BookStateFor (r:1 w:1) + /// Proof: MessageQueue BookStateFor (max_values: None, max_size: Some(52), added: 2527, mode: MaxEncodedLen) + /// Storage: MessageQueue Pages (r:1 w:1) + /// Proof: MessageQueue Pages (max_values: None, max_size: Some(65585), added: 68060, mode: MaxEncodedLen) fn reap_page() -> Weight { - // Minimum execution time: 60_562 nanoseconds. - Weight::from_ref_time(61_430_000) - .saturating_add(RocksDbWeight::get().reads(2)) - .saturating_add(RocksDbWeight::get().writes(2)) + // Proof Size summary in bytes: + // Measured: `66827` + // Estimated: `70587` + // Minimum execution time: 61_361 nanoseconds. + Weight::from_parts(62_103_000, 70587) + .saturating_add(RocksDbWeight::get().reads(2_u64)) + .saturating_add(RocksDbWeight::get().writes(2_u64)) } - // Storage: MessageQueue BookStateFor (r:1 w:1) - // Storage: MessageQueue Pages (r:1 w:1) + /// Storage: MessageQueue BookStateFor (r:1 w:1) + /// Proof: MessageQueue BookStateFor (max_values: None, max_size: Some(52), added: 2527, mode: MaxEncodedLen) + /// Storage: MessageQueue Pages (r:1 w:1) + /// Proof: MessageQueue Pages (max_values: None, max_size: Some(65585), added: 68060, mode: MaxEncodedLen) fn execute_overweight_page_removed() -> Weight { - // Minimum execution time: 74_582 nanoseconds. - Weight::from_ref_time(75_445_000) - .saturating_add(RocksDbWeight::get().reads(2)) - .saturating_add(RocksDbWeight::get().writes(2)) + // Proof Size summary in bytes: + // Measured: `66827` + // Estimated: `70587` + // Minimum execution time: 75_153 nanoseconds. + Weight::from_parts(76_093_000, 70587) + .saturating_add(RocksDbWeight::get().reads(2_u64)) + .saturating_add(RocksDbWeight::get().writes(2_u64)) } - // Storage: MessageQueue BookStateFor (r:1 w:1) - // Storage: MessageQueue Pages (r:1 w:1) + /// Storage: MessageQueue BookStateFor (r:1 w:1) + /// Proof: MessageQueue BookStateFor (max_values: None, max_size: Some(52), added: 2527, mode: MaxEncodedLen) + /// Storage: MessageQueue Pages (r:1 w:1) + /// Proof: MessageQueue Pages (max_values: None, max_size: Some(65585), added: 68060, mode: MaxEncodedLen) fn execute_overweight_page_updated() -> Weight { - // Minimum execution time: 87_526 nanoseconds. - Weight::from_ref_time(88_055_000) - .saturating_add(RocksDbWeight::get().reads(2)) - .saturating_add(RocksDbWeight::get().writes(2)) + // Proof Size summary in bytes: + // Measured: `66827` + // Estimated: `70587` + // Minimum execution time: 88_272 nanoseconds. + Weight::from_parts(89_373_000, 70587) + .saturating_add(RocksDbWeight::get().reads(2_u64)) + .saturating_add(RocksDbWeight::get().writes(2_u64)) } } diff --git a/frame/multisig/src/weights.rs b/frame/multisig/src/weights.rs index 1f435cb9f..46bf911f2 100644 --- a/frame/multisig/src/weights.rs +++ b/frame/multisig/src/weights.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2022 Parity Technologies (UK) Ltd. +// Copyright (C) 2023 Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); @@ -18,7 +18,8 @@ //! Autogenerated weights for pallet_multisig //! //! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev -//! DATE: 2022-11-07, STEPS: `50`, REPEAT: 20, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! DATE: 2023-01-24, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! WORST CASE MAP SIZE: `1000000` //! HOSTNAME: `bm2`, CPU: `Intel(R) Core(TM) i7-7700K CPU @ 4.20GHz` //! EXECUTION: Some(Wasm), WASM-EXECUTION: Compiled, CHAIN: Some("dev"), DB CACHE: 1024 @@ -61,82 +62,108 @@ pub struct SubstrateWeight(PhantomData); impl WeightInfo for SubstrateWeight { /// The range of component `z` is `[0, 10000]`. fn as_multi_threshold_1(z: u32, ) -> Weight { - // Minimum execution time: 20_447 nanoseconds. - Weight::from_ref_time(20_896_236 as u64) - // Standard Error: 2 - .saturating_add(Weight::from_ref_time(568 as u64).saturating_mul(z as u64)) + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 12_086 nanoseconds. + Weight::from_ref_time(12_464_828) + // Standard Error: 1 + .saturating_add(Weight::from_ref_time(494).saturating_mul(z.into())) } - // Storage: Multisig Multisigs (r:1 w:1) - // Storage: unknown [0x3a65787472696e7369635f696e646578] (r:1 w:0) + /// Storage: Multisig Multisigs (r:1 w:1) + /// Proof: Multisig Multisigs (max_values: None, max_size: Some(3346), added: 5821, mode: MaxEncodedLen) /// The range of component `s` is `[2, 100]`. /// The range of component `z` is `[0, 10000]`. fn as_multi_create(s: u32, z: u32, ) -> Weight { - // Minimum execution time: 54_987 nanoseconds. - Weight::from_ref_time(42_525_077 as u64) - // Standard Error: 562 - .saturating_add(Weight::from_ref_time(136_064 as u64).saturating_mul(s as u64)) - // Standard Error: 5 - .saturating_add(Weight::from_ref_time(1_508 as u64).saturating_mul(z as u64)) - .saturating_add(T::DbWeight::get().reads(2 as u64)) - .saturating_add(T::DbWeight::get().writes(1 as u64)) + // Proof Size summary in bytes: + // Measured: `352 + s * (2 ±0)` + // Estimated: `5821` + // Minimum execution time: 35_377 nanoseconds. + Weight::from_parts(29_088_956, 5821) + // Standard Error: 335 + .saturating_add(Weight::from_ref_time(67_846).saturating_mul(s.into())) + // Standard Error: 3 + .saturating_add(Weight::from_ref_time(1_523).saturating_mul(z.into())) + .saturating_add(T::DbWeight::get().reads(1_u64)) + .saturating_add(T::DbWeight::get().writes(1_u64)) } - // Storage: Multisig Multisigs (r:1 w:1) + /// Storage: Multisig Multisigs (r:1 w:1) + /// Proof: Multisig Multisigs (max_values: None, max_size: Some(3346), added: 5821, mode: MaxEncodedLen) /// The range of component `s` is `[3, 100]`. /// The range of component `z` is `[0, 10000]`. fn as_multi_approve(s: u32, z: u32, ) -> Weight { - // Minimum execution time: 42_573 nanoseconds. - Weight::from_ref_time(30_585_734 as u64) - // Standard Error: 637 - .saturating_add(Weight::from_ref_time(128_012 as u64).saturating_mul(s as u64)) - // Standard Error: 6 - .saturating_add(Weight::from_ref_time(1_507 as u64).saturating_mul(z as u64)) - .saturating_add(T::DbWeight::get().reads(1 as u64)) - .saturating_add(T::DbWeight::get().writes(1 as u64)) + // Proof Size summary in bytes: + // Measured: `351` + // Estimated: `5821` + // Minimum execution time: 26_138 nanoseconds. + Weight::from_parts(20_479_380, 5821) + // Standard Error: 259 + .saturating_add(Weight::from_ref_time(64_116).saturating_mul(s.into())) + // Standard Error: 2 + .saturating_add(Weight::from_ref_time(1_520).saturating_mul(z.into())) + .saturating_add(T::DbWeight::get().reads(1_u64)) + .saturating_add(T::DbWeight::get().writes(1_u64)) } - // Storage: Multisig Multisigs (r:1 w:1) - // Storage: System Account (r:1 w:1) + /// Storage: Multisig Multisigs (r:1 w:1) + /// Proof: Multisig Multisigs (max_values: None, max_size: Some(3346), added: 5821, mode: MaxEncodedLen) + /// Storage: System Account (r:1 w:1) + /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) /// The range of component `s` is `[2, 100]`. /// The range of component `z` is `[0, 10000]`. fn as_multi_complete(s: u32, z: u32, ) -> Weight { - // Minimum execution time: 57_143 nanoseconds. - Weight::from_ref_time(43_921_674 as u64) - // Standard Error: 704 - .saturating_add(Weight::from_ref_time(153_474 as u64).saturating_mul(s as u64)) - // Standard Error: 6 - .saturating_add(Weight::from_ref_time(1_536 as u64).saturating_mul(z as u64)) - .saturating_add(T::DbWeight::get().reads(2 as u64)) - .saturating_add(T::DbWeight::get().writes(2 as u64)) + // Proof Size summary in bytes: + // Measured: `489 + s * (33 ±0)` + // Estimated: `8424` + // Minimum execution time: 40_323 nanoseconds. + Weight::from_parts(32_311_615, 8424) + // Standard Error: 401 + .saturating_add(Weight::from_ref_time(85_999).saturating_mul(s.into())) + // Standard Error: 3 + .saturating_add(Weight::from_ref_time(1_534).saturating_mul(z.into())) + .saturating_add(T::DbWeight::get().reads(2_u64)) + .saturating_add(T::DbWeight::get().writes(2_u64)) } - // Storage: Multisig Multisigs (r:1 w:1) - // Storage: unknown [0x3a65787472696e7369635f696e646578] (r:1 w:0) + /// Storage: Multisig Multisigs (r:1 w:1) + /// Proof: Multisig Multisigs (max_values: None, max_size: Some(3346), added: 5821, mode: MaxEncodedLen) /// The range of component `s` is `[2, 100]`. fn approve_as_multi_create(s: u32, ) -> Weight { - // Minimum execution time: 39_088 nanoseconds. - Weight::from_ref_time(41_258_697 as u64) - // Standard Error: 1_038 - .saturating_add(Weight::from_ref_time(126_040 as u64).saturating_mul(s as u64)) - .saturating_add(T::DbWeight::get().reads(2 as u64)) - .saturating_add(T::DbWeight::get().writes(1 as u64)) + // Proof Size summary in bytes: + // Measured: `359 + s * (2 ±0)` + // Estimated: `5821` + // Minimum execution time: 26_938 nanoseconds. + Weight::from_parts(27_802_216, 5821) + // Standard Error: 342 + .saturating_add(Weight::from_ref_time(69_282).saturating_mul(s.into())) + .saturating_add(T::DbWeight::get().reads(1_u64)) + .saturating_add(T::DbWeight::get().writes(1_u64)) } - // Storage: Multisig Multisigs (r:1 w:1) + /// Storage: Multisig Multisigs (r:1 w:1) + /// Proof: Multisig Multisigs (max_values: None, max_size: Some(3346), added: 5821, mode: MaxEncodedLen) /// The range of component `s` is `[2, 100]`. fn approve_as_multi_approve(s: u32, ) -> Weight { - // Minimum execution time: 26_872 nanoseconds. - Weight::from_ref_time(28_625_218 as u64) - // Standard Error: 793 - .saturating_add(Weight::from_ref_time(128_542 as u64).saturating_mul(s as u64)) - .saturating_add(T::DbWeight::get().reads(1 as u64)) - .saturating_add(T::DbWeight::get().writes(1 as u64)) + // Proof Size summary in bytes: + // Measured: `351` + // Estimated: `5821` + // Minimum execution time: 18_050 nanoseconds. + Weight::from_parts(19_095_404, 5821) + // Standard Error: 419 + .saturating_add(Weight::from_ref_time(66_914).saturating_mul(s.into())) + .saturating_add(T::DbWeight::get().reads(1_u64)) + .saturating_add(T::DbWeight::get().writes(1_u64)) } - // Storage: Multisig Multisigs (r:1 w:1) + /// Storage: Multisig Multisigs (r:1 w:1) + /// Proof: Multisig Multisigs (max_values: None, max_size: Some(3346), added: 5821, mode: MaxEncodedLen) /// The range of component `s` is `[2, 100]`. fn cancel_as_multi(s: u32, ) -> Weight { - // Minimum execution time: 37_636 nanoseconds. - Weight::from_ref_time(39_614_705 as u64) - // Standard Error: 850 - .saturating_add(Weight::from_ref_time(136_222 as u64).saturating_mul(s as u64)) - .saturating_add(T::DbWeight::get().reads(1 as u64)) - .saturating_add(T::DbWeight::get().writes(1 as u64)) + // Proof Size summary in bytes: + // Measured: `555 + s * (1 ±0)` + // Estimated: `5821` + // Minimum execution time: 27_508 nanoseconds. + Weight::from_parts(28_702_686, 5821) + // Standard Error: 466 + .saturating_add(Weight::from_ref_time(69_419).saturating_mul(s.into())) + .saturating_add(T::DbWeight::get().reads(1_u64)) + .saturating_add(T::DbWeight::get().writes(1_u64)) } } @@ -144,81 +171,107 @@ impl WeightInfo for SubstrateWeight { impl WeightInfo for () { /// The range of component `z` is `[0, 10000]`. fn as_multi_threshold_1(z: u32, ) -> Weight { - // Minimum execution time: 20_447 nanoseconds. - Weight::from_ref_time(20_896_236 as u64) - // Standard Error: 2 - .saturating_add(Weight::from_ref_time(568 as u64).saturating_mul(z as u64)) + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 12_086 nanoseconds. + Weight::from_ref_time(12_464_828) + // Standard Error: 1 + .saturating_add(Weight::from_ref_time(494).saturating_mul(z.into())) } - // Storage: Multisig Multisigs (r:1 w:1) - // Storage: unknown [0x3a65787472696e7369635f696e646578] (r:1 w:0) + /// Storage: Multisig Multisigs (r:1 w:1) + /// Proof: Multisig Multisigs (max_values: None, max_size: Some(3346), added: 5821, mode: MaxEncodedLen) /// The range of component `s` is `[2, 100]`. /// The range of component `z` is `[0, 10000]`. fn as_multi_create(s: u32, z: u32, ) -> Weight { - // Minimum execution time: 54_987 nanoseconds. - Weight::from_ref_time(42_525_077 as u64) - // Standard Error: 562 - .saturating_add(Weight::from_ref_time(136_064 as u64).saturating_mul(s as u64)) - // Standard Error: 5 - .saturating_add(Weight::from_ref_time(1_508 as u64).saturating_mul(z as u64)) - .saturating_add(RocksDbWeight::get().reads(2 as u64)) - .saturating_add(RocksDbWeight::get().writes(1 as u64)) + // Proof Size summary in bytes: + // Measured: `352 + s * (2 ±0)` + // Estimated: `5821` + // Minimum execution time: 35_377 nanoseconds. + Weight::from_parts(29_088_956, 5821) + // Standard Error: 335 + .saturating_add(Weight::from_ref_time(67_846).saturating_mul(s.into())) + // Standard Error: 3 + .saturating_add(Weight::from_ref_time(1_523).saturating_mul(z.into())) + .saturating_add(RocksDbWeight::get().reads(1_u64)) + .saturating_add(RocksDbWeight::get().writes(1_u64)) } - // Storage: Multisig Multisigs (r:1 w:1) + /// Storage: Multisig Multisigs (r:1 w:1) + /// Proof: Multisig Multisigs (max_values: None, max_size: Some(3346), added: 5821, mode: MaxEncodedLen) /// The range of component `s` is `[3, 100]`. /// The range of component `z` is `[0, 10000]`. fn as_multi_approve(s: u32, z: u32, ) -> Weight { - // Minimum execution time: 42_573 nanoseconds. - Weight::from_ref_time(30_585_734 as u64) - // Standard Error: 637 - .saturating_add(Weight::from_ref_time(128_012 as u64).saturating_mul(s as u64)) - // Standard Error: 6 - .saturating_add(Weight::from_ref_time(1_507 as u64).saturating_mul(z as u64)) - .saturating_add(RocksDbWeight::get().reads(1 as u64)) - .saturating_add(RocksDbWeight::get().writes(1 as u64)) + // Proof Size summary in bytes: + // Measured: `351` + // Estimated: `5821` + // Minimum execution time: 26_138 nanoseconds. + Weight::from_parts(20_479_380, 5821) + // Standard Error: 259 + .saturating_add(Weight::from_ref_time(64_116).saturating_mul(s.into())) + // Standard Error: 2 + .saturating_add(Weight::from_ref_time(1_520).saturating_mul(z.into())) + .saturating_add(RocksDbWeight::get().reads(1_u64)) + .saturating_add(RocksDbWeight::get().writes(1_u64)) } - // Storage: Multisig Multisigs (r:1 w:1) - // Storage: System Account (r:1 w:1) + /// Storage: Multisig Multisigs (r:1 w:1) + /// Proof: Multisig Multisigs (max_values: None, max_size: Some(3346), added: 5821, mode: MaxEncodedLen) + /// Storage: System Account (r:1 w:1) + /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) /// The range of component `s` is `[2, 100]`. /// The range of component `z` is `[0, 10000]`. fn as_multi_complete(s: u32, z: u32, ) -> Weight { - // Minimum execution time: 57_143 nanoseconds. - Weight::from_ref_time(43_921_674 as u64) - // Standard Error: 704 - .saturating_add(Weight::from_ref_time(153_474 as u64).saturating_mul(s as u64)) - // Standard Error: 6 - .saturating_add(Weight::from_ref_time(1_536 as u64).saturating_mul(z as u64)) - .saturating_add(RocksDbWeight::get().reads(2 as u64)) - .saturating_add(RocksDbWeight::get().writes(2 as u64)) + // Proof Size summary in bytes: + // Measured: `489 + s * (33 ±0)` + // Estimated: `8424` + // Minimum execution time: 40_323 nanoseconds. + Weight::from_parts(32_311_615, 8424) + // Standard Error: 401 + .saturating_add(Weight::from_ref_time(85_999).saturating_mul(s.into())) + // Standard Error: 3 + .saturating_add(Weight::from_ref_time(1_534).saturating_mul(z.into())) + .saturating_add(RocksDbWeight::get().reads(2_u64)) + .saturating_add(RocksDbWeight::get().writes(2_u64)) } - // Storage: Multisig Multisigs (r:1 w:1) - // Storage: unknown [0x3a65787472696e7369635f696e646578] (r:1 w:0) + /// Storage: Multisig Multisigs (r:1 w:1) + /// Proof: Multisig Multisigs (max_values: None, max_size: Some(3346), added: 5821, mode: MaxEncodedLen) /// The range of component `s` is `[2, 100]`. fn approve_as_multi_create(s: u32, ) -> Weight { - // Minimum execution time: 39_088 nanoseconds. - Weight::from_ref_time(41_258_697 as u64) - // Standard Error: 1_038 - .saturating_add(Weight::from_ref_time(126_040 as u64).saturating_mul(s as u64)) - .saturating_add(RocksDbWeight::get().reads(2 as u64)) - .saturating_add(RocksDbWeight::get().writes(1 as u64)) + // Proof Size summary in bytes: + // Measured: `359 + s * (2 ±0)` + // Estimated: `5821` + // Minimum execution time: 26_938 nanoseconds. + Weight::from_parts(27_802_216, 5821) + // Standard Error: 342 + .saturating_add(Weight::from_ref_time(69_282).saturating_mul(s.into())) + .saturating_add(RocksDbWeight::get().reads(1_u64)) + .saturating_add(RocksDbWeight::get().writes(1_u64)) } - // Storage: Multisig Multisigs (r:1 w:1) + /// Storage: Multisig Multisigs (r:1 w:1) + /// Proof: Multisig Multisigs (max_values: None, max_size: Some(3346), added: 5821, mode: MaxEncodedLen) /// The range of component `s` is `[2, 100]`. fn approve_as_multi_approve(s: u32, ) -> Weight { - // Minimum execution time: 26_872 nanoseconds. - Weight::from_ref_time(28_625_218 as u64) - // Standard Error: 793 - .saturating_add(Weight::from_ref_time(128_542 as u64).saturating_mul(s as u64)) - .saturating_add(RocksDbWeight::get().reads(1 as u64)) - .saturating_add(RocksDbWeight::get().writes(1 as u64)) + // Proof Size summary in bytes: + // Measured: `351` + // Estimated: `5821` + // Minimum execution time: 18_050 nanoseconds. + Weight::from_parts(19_095_404, 5821) + // Standard Error: 419 + .saturating_add(Weight::from_ref_time(66_914).saturating_mul(s.into())) + .saturating_add(RocksDbWeight::get().reads(1_u64)) + .saturating_add(RocksDbWeight::get().writes(1_u64)) } - // Storage: Multisig Multisigs (r:1 w:1) + /// Storage: Multisig Multisigs (r:1 w:1) + /// Proof: Multisig Multisigs (max_values: None, max_size: Some(3346), added: 5821, mode: MaxEncodedLen) /// The range of component `s` is `[2, 100]`. fn cancel_as_multi(s: u32, ) -> Weight { - // Minimum execution time: 37_636 nanoseconds. - Weight::from_ref_time(39_614_705 as u64) - // Standard Error: 850 - .saturating_add(Weight::from_ref_time(136_222 as u64).saturating_mul(s as u64)) - .saturating_add(RocksDbWeight::get().reads(1 as u64)) - .saturating_add(RocksDbWeight::get().writes(1 as u64)) + // Proof Size summary in bytes: + // Measured: `555 + s * (1 ±0)` + // Estimated: `5821` + // Minimum execution time: 27_508 nanoseconds. + Weight::from_parts(28_702_686, 5821) + // Standard Error: 466 + .saturating_add(Weight::from_ref_time(69_419).saturating_mul(s.into())) + .saturating_add(RocksDbWeight::get().reads(1_u64)) + .saturating_add(RocksDbWeight::get().writes(1_u64)) } } diff --git a/frame/nfts/src/weights.rs b/frame/nfts/src/weights.rs index f05f8ca51..81be145b6 100644 --- a/frame/nfts/src/weights.rs +++ b/frame/nfts/src/weights.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2022 Parity Technologies (UK) Ltd. +// Copyright (C) 2023 Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); @@ -18,25 +18,25 @@ //! Autogenerated weights for pallet_nfts //! //! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev -//! DATE: 2022-12-22, STEPS: `50`, REPEAT: 20, LOW RANGE: `[]`, HIGH RANGE: `[]` -//! HOSTNAME: `bm3`, CPU: `Intel(R) Core(TM) i7-7700K CPU @ 4.20GHz` +//! DATE: 2023-01-24, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! WORST CASE MAP SIZE: `1000000` +//! HOSTNAME: `bm2`, CPU: `Intel(R) Core(TM) i7-7700K CPU @ 4.20GHz` //! EXECUTION: Some(Wasm), WASM-EXECUTION: Compiled, CHAIN: Some("dev"), DB CACHE: 1024 // Executed Command: -// /home/benchbot/cargo_target_dir/production/substrate +// ./target/production/substrate // benchmark // pallet +// --chain=dev // --steps=50 // --repeat=20 +// --pallet=pallet_nfts // --extrinsic=* // --execution=wasm // --wasm-execution=compiled // --heap-pages=4096 -// --json-file=/var/lib/gitlab-runner/builds/zyw4fam_/0/parity/mirrors/substrate/.git/.artifacts/bench.json -// --pallet=pallet_nfts -// --chain=dev -// --header=./HEADER-APACHE2 // --output=./frame/nfts/src/weights.rs +// --header=./HEADER-APACHE2 // --template=./.maintain/frame-weight-template.hbs #![cfg_attr(rustfmt, rustfmt_skip)] @@ -90,762 +90,1264 @@ pub trait WeightInfo { /// Weights for pallet_nfts using the Substrate node and recommended hardware. pub struct SubstrateWeight(PhantomData); impl WeightInfo for SubstrateWeight { - // Storage: Nfts NextCollectionId (r:1 w:1) - // Storage: Nfts Collection (r:1 w:1) - // Storage: Nfts CollectionRoleOf (r:0 w:1) - // Storage: Nfts CollectionConfigOf (r:0 w:1) - // Storage: Nfts CollectionAccount (r:0 w:1) + /// Storage: Nfts NextCollectionId (r:1 w:1) + /// Proof: Nfts NextCollectionId (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) + /// Storage: Nfts Collection (r:1 w:1) + /// Proof: Nfts Collection (max_values: None, max_size: Some(80), added: 2555, mode: MaxEncodedLen) + /// Storage: Nfts CollectionRoleOf (r:0 w:1) + /// Proof: Nfts CollectionRoleOf (max_values: None, max_size: Some(69), added: 2544, mode: MaxEncodedLen) + /// Storage: Nfts CollectionConfigOf (r:0 w:1) + /// Proof: Nfts CollectionConfigOf (max_values: None, max_size: Some(73), added: 2548, mode: MaxEncodedLen) + /// Storage: Nfts CollectionAccount (r:0 w:1) + /// Proof: Nfts CollectionAccount (max_values: None, max_size: Some(68), added: 2543, mode: MaxEncodedLen) fn create() -> Weight { - // Minimum execution time: 44_312 nanoseconds. - Weight::from_ref_time(44_871_000) - .saturating_add(T::DbWeight::get().reads(2)) - .saturating_add(T::DbWeight::get().writes(5)) - } - // Storage: Nfts NextCollectionId (r:1 w:1) - // Storage: Nfts Collection (r:1 w:1) - // Storage: Nfts CollectionRoleOf (r:0 w:1) - // Storage: Nfts CollectionConfigOf (r:0 w:1) - // Storage: Nfts CollectionAccount (r:0 w:1) + // Proof Size summary in bytes: + // Measured: `214` + // Estimated: `3054` + // Minimum execution time: 32_467 nanoseconds. + Weight::from_parts(33_236_000, 3054) + .saturating_add(T::DbWeight::get().reads(2_u64)) + .saturating_add(T::DbWeight::get().writes(5_u64)) + } + /// Storage: Nfts NextCollectionId (r:1 w:1) + /// Proof: Nfts NextCollectionId (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) + /// Storage: Nfts Collection (r:1 w:1) + /// Proof: Nfts Collection (max_values: None, max_size: Some(80), added: 2555, mode: MaxEncodedLen) + /// Storage: Nfts CollectionRoleOf (r:0 w:1) + /// Proof: Nfts CollectionRoleOf (max_values: None, max_size: Some(69), added: 2544, mode: MaxEncodedLen) + /// Storage: Nfts CollectionConfigOf (r:0 w:1) + /// Proof: Nfts CollectionConfigOf (max_values: None, max_size: Some(73), added: 2548, mode: MaxEncodedLen) + /// Storage: Nfts CollectionAccount (r:0 w:1) + /// Proof: Nfts CollectionAccount (max_values: None, max_size: Some(68), added: 2543, mode: MaxEncodedLen) fn force_create() -> Weight { - // Minimum execution time: 31_654 nanoseconds. - Weight::from_ref_time(32_078_000) - .saturating_add(T::DbWeight::get().reads(2)) - .saturating_add(T::DbWeight::get().writes(5)) - } - // Storage: Nfts Collection (r:1 w:1) - // Storage: Nfts Item (r:1001 w:1000) - // Storage: Nfts Attribute (r:1001 w:1000) - // Storage: Nfts ItemMetadataOf (r:0 w:1000) - // Storage: Nfts CollectionRoleOf (r:0 w:1) - // Storage: Nfts CollectionMetadataOf (r:0 w:1) - // Storage: Nfts CollectionConfigOf (r:0 w:1) - // Storage: Nfts ItemConfigOf (r:0 w:1000) - // Storage: Nfts Account (r:0 w:1000) - // Storage: Nfts CollectionAccount (r:0 w:1) + // Proof Size summary in bytes: + // Measured: `42` + // Estimated: `3054` + // Minimum execution time: 22_198 nanoseconds. + Weight::from_parts(22_776_000, 3054) + .saturating_add(T::DbWeight::get().reads(2_u64)) + .saturating_add(T::DbWeight::get().writes(5_u64)) + } + /// Storage: Nfts Collection (r:1 w:1) + /// Proof: Nfts Collection (max_values: None, max_size: Some(80), added: 2555, mode: MaxEncodedLen) + /// Storage: Nfts Item (r:1001 w:1000) + /// Proof: Nfts Item (max_values: None, max_size: Some(861), added: 3336, mode: MaxEncodedLen) + /// Storage: Nfts ItemMetadataOf (r:1001 w:1000) + /// Proof: Nfts ItemMetadataOf (max_values: None, max_size: Some(140), added: 2615, mode: MaxEncodedLen) + /// Storage: Nfts Attribute (r:1001 w:1000) + /// Proof: Nfts Attribute (max_values: None, max_size: Some(446), added: 2921, mode: MaxEncodedLen) + /// Storage: Nfts CollectionRoleOf (r:0 w:1) + /// Proof: Nfts CollectionRoleOf (max_values: None, max_size: Some(69), added: 2544, mode: MaxEncodedLen) + /// Storage: Nfts CollectionMetadataOf (r:0 w:1) + /// Proof: Nfts CollectionMetadataOf (max_values: None, max_size: Some(87), added: 2562, mode: MaxEncodedLen) + /// Storage: Nfts CollectionConfigOf (r:0 w:1) + /// Proof: Nfts CollectionConfigOf (max_values: None, max_size: Some(73), added: 2548, mode: MaxEncodedLen) + /// Storage: Nfts ItemConfigOf (r:0 w:1000) + /// Proof: Nfts ItemConfigOf (max_values: None, max_size: Some(48), added: 2523, mode: MaxEncodedLen) + /// Storage: Nfts Account (r:0 w:1000) + /// Proof: Nfts Account (max_values: None, max_size: Some(88), added: 2563, mode: MaxEncodedLen) + /// Storage: Nfts CollectionAccount (r:0 w:1) + /// Proof: Nfts CollectionAccount (max_values: None, max_size: Some(68), added: 2543, mode: MaxEncodedLen) /// The range of component `n` is `[0, 1000]`. /// The range of component `m` is `[0, 1000]`. /// The range of component `a` is `[0, 1000]`. fn destroy(n: u32, m: u32, a: u32, ) -> Weight { - // Minimum execution time: 19_183_393 nanoseconds. - Weight::from_ref_time(17_061_526_855) - // Standard Error: 16_689 - .saturating_add(Weight::from_ref_time(353_523).saturating_mul(n.into())) - // Standard Error: 16_689 - .saturating_add(Weight::from_ref_time(1_861_080).saturating_mul(m.into())) - // Standard Error: 16_689 - .saturating_add(Weight::from_ref_time(8_858_987).saturating_mul(a.into())) - .saturating_add(T::DbWeight::get().reads(1003)) + // Proof Size summary in bytes: + // Measured: `172781 + m * (127 ±0) + a * (402 ±0)` + // Estimated: `3347427 + a * (2921 ±0) + m * (2615 ±0)` + // Minimum execution time: 24_021_657 nanoseconds. + Weight::from_parts(16_029_391_606, 3347427) + // Standard Error: 20_364 + .saturating_add(Weight::from_ref_time(300_580).saturating_mul(n.into())) + // Standard Error: 20_364 + .saturating_add(Weight::from_ref_time(7_748_502).saturating_mul(m.into())) + // Standard Error: 20_364 + .saturating_add(Weight::from_ref_time(9_183_566).saturating_mul(a.into())) + .saturating_add(T::DbWeight::get().reads(1004_u64)) + .saturating_add(T::DbWeight::get().reads((1_u64).saturating_mul(m.into()))) .saturating_add(T::DbWeight::get().reads((1_u64).saturating_mul(a.into()))) - .saturating_add(T::DbWeight::get().writes(3005)) + .saturating_add(T::DbWeight::get().writes(3005_u64)) .saturating_add(T::DbWeight::get().writes((1_u64).saturating_mul(m.into()))) .saturating_add(T::DbWeight::get().writes((1_u64).saturating_mul(a.into()))) - } - // Storage: Nfts CollectionConfigOf (r:1 w:0) - // Storage: Nfts Item (r:1 w:1) - // Storage: Nfts Collection (r:1 w:1) - // Storage: Nfts CollectionRoleOf (r:1 w:0) - // Storage: Nfts ItemConfigOf (r:1 w:1) - // Storage: Nfts Account (r:0 w:1) + .saturating_add(Weight::from_proof_size(2921).saturating_mul(a.into())) + .saturating_add(Weight::from_proof_size(2615).saturating_mul(m.into())) + } + /// Storage: Nfts CollectionConfigOf (r:1 w:0) + /// Proof: Nfts CollectionConfigOf (max_values: None, max_size: Some(73), added: 2548, mode: MaxEncodedLen) + /// Storage: Nfts Item (r:1 w:1) + /// Proof: Nfts Item (max_values: None, max_size: Some(861), added: 3336, mode: MaxEncodedLen) + /// Storage: Nfts Collection (r:1 w:1) + /// Proof: Nfts Collection (max_values: None, max_size: Some(80), added: 2555, mode: MaxEncodedLen) + /// Storage: Nfts CollectionRoleOf (r:1 w:0) + /// Proof: Nfts CollectionRoleOf (max_values: None, max_size: Some(69), added: 2544, mode: MaxEncodedLen) + /// Storage: Nfts ItemConfigOf (r:1 w:1) + /// Proof: Nfts ItemConfigOf (max_values: None, max_size: Some(48), added: 2523, mode: MaxEncodedLen) + /// Storage: Nfts Account (r:0 w:1) + /// Proof: Nfts Account (max_values: None, max_size: Some(88), added: 2563, mode: MaxEncodedLen) fn mint() -> Weight { - // Minimum execution time: 57_753 nanoseconds. - Weight::from_ref_time(58_313_000) - .saturating_add(T::DbWeight::get().reads(5)) - .saturating_add(T::DbWeight::get().writes(4)) - } - // Storage: Nfts CollectionRoleOf (r:1 w:0) - // Storage: Nfts Item (r:1 w:1) - // Storage: Nfts Collection (r:1 w:1) - // Storage: Nfts CollectionConfigOf (r:1 w:0) - // Storage: Nfts ItemConfigOf (r:1 w:1) - // Storage: Nfts Account (r:0 w:1) + // Proof Size summary in bytes: + // Measured: `448` + // Estimated: `13506` + // Minimum execution time: 42_634 nanoseconds. + Weight::from_parts(43_231_000, 13506) + .saturating_add(T::DbWeight::get().reads(5_u64)) + .saturating_add(T::DbWeight::get().writes(4_u64)) + } + /// Storage: Nfts CollectionRoleOf (r:1 w:0) + /// Proof: Nfts CollectionRoleOf (max_values: None, max_size: Some(69), added: 2544, mode: MaxEncodedLen) + /// Storage: Nfts Item (r:1 w:1) + /// Proof: Nfts Item (max_values: None, max_size: Some(861), added: 3336, mode: MaxEncodedLen) + /// Storage: Nfts Collection (r:1 w:1) + /// Proof: Nfts Collection (max_values: None, max_size: Some(80), added: 2555, mode: MaxEncodedLen) + /// Storage: Nfts CollectionConfigOf (r:1 w:0) + /// Proof: Nfts CollectionConfigOf (max_values: None, max_size: Some(73), added: 2548, mode: MaxEncodedLen) + /// Storage: Nfts ItemConfigOf (r:1 w:1) + /// Proof: Nfts ItemConfigOf (max_values: None, max_size: Some(48), added: 2523, mode: MaxEncodedLen) + /// Storage: Nfts Account (r:0 w:1) + /// Proof: Nfts Account (max_values: None, max_size: Some(88), added: 2563, mode: MaxEncodedLen) fn force_mint() -> Weight { - // Minimum execution time: 56_429 nanoseconds. - Weight::from_ref_time(57_202_000) - .saturating_add(T::DbWeight::get().reads(5)) - .saturating_add(T::DbWeight::get().writes(4)) - } - // Storage: Nfts Collection (r:1 w:1) - // Storage: Nfts Item (r:1 w:1) - // Storage: Nfts CollectionRoleOf (r:1 w:0) - // Storage: Nfts ItemConfigOf (r:1 w:1) - // Storage: Nfts Account (r:0 w:1) - // Storage: Nfts ItemPriceOf (r:0 w:1) - // Storage: Nfts ItemAttributesApprovalsOf (r:0 w:1) - // Storage: Nfts PendingSwapOf (r:0 w:1) + // Proof Size summary in bytes: + // Measured: `448` + // Estimated: `13506` + // Minimum execution time: 41_686 nanoseconds. + Weight::from_parts(41_991_000, 13506) + .saturating_add(T::DbWeight::get().reads(5_u64)) + .saturating_add(T::DbWeight::get().writes(4_u64)) + } + /// Storage: Nfts ItemConfigOf (r:1 w:1) + /// Proof: Nfts ItemConfigOf (max_values: None, max_size: Some(48), added: 2523, mode: MaxEncodedLen) + /// Storage: Nfts Collection (r:1 w:1) + /// Proof: Nfts Collection (max_values: None, max_size: Some(80), added: 2555, mode: MaxEncodedLen) + /// Storage: Nfts Item (r:1 w:1) + /// Proof: Nfts Item (max_values: None, max_size: Some(861), added: 3336, mode: MaxEncodedLen) + /// Storage: Nfts CollectionRoleOf (r:1 w:0) + /// Proof: Nfts CollectionRoleOf (max_values: None, max_size: Some(69), added: 2544, mode: MaxEncodedLen) + /// Storage: Nfts ItemMetadataOf (r:1 w:0) + /// Proof: Nfts ItemMetadataOf (max_values: None, max_size: Some(140), added: 2615, mode: MaxEncodedLen) + /// Storage: Nfts Account (r:0 w:1) + /// Proof: Nfts Account (max_values: None, max_size: Some(88), added: 2563, mode: MaxEncodedLen) + /// Storage: Nfts ItemPriceOf (r:0 w:1) + /// Proof: Nfts ItemPriceOf (max_values: None, max_size: Some(89), added: 2564, mode: MaxEncodedLen) + /// Storage: Nfts ItemAttributesApprovalsOf (r:0 w:1) + /// Proof: Nfts ItemAttributesApprovalsOf (max_values: None, max_size: Some(681), added: 3156, mode: MaxEncodedLen) + /// Storage: Nfts PendingSwapOf (r:0 w:1) + /// Proof: Nfts PendingSwapOf (max_values: None, max_size: Some(71), added: 2546, mode: MaxEncodedLen) fn burn() -> Weight { - // Minimum execution time: 59_681 nanoseconds. - Weight::from_ref_time(60_058_000) - .saturating_add(T::DbWeight::get().reads(4)) - .saturating_add(T::DbWeight::get().writes(7)) - } - // Storage: Nfts Collection (r:1 w:0) - // Storage: Nfts CollectionConfigOf (r:1 w:0) - // Storage: Nfts ItemConfigOf (r:1 w:0) - // Storage: Nfts Item (r:1 w:1) - // Storage: Nfts CollectionRoleOf (r:1 w:0) - // Storage: System Account (r:1 w:1) - // Storage: Nfts Account (r:0 w:2) - // Storage: Nfts ItemPriceOf (r:0 w:1) - // Storage: Nfts PendingSwapOf (r:0 w:1) + // Proof Size summary in bytes: + // Measured: `647` + // Estimated: `13573` + // Minimum execution time: 45_192 nanoseconds. + Weight::from_parts(45_792_000, 13573) + .saturating_add(T::DbWeight::get().reads(5_u64)) + .saturating_add(T::DbWeight::get().writes(7_u64)) + } + /// Storage: Nfts Collection (r:1 w:0) + /// Proof: Nfts Collection (max_values: None, max_size: Some(80), added: 2555, mode: MaxEncodedLen) + /// Storage: Nfts CollectionConfigOf (r:1 w:0) + /// Proof: Nfts CollectionConfigOf (max_values: None, max_size: Some(73), added: 2548, mode: MaxEncodedLen) + /// Storage: Nfts ItemConfigOf (r:1 w:0) + /// Proof: Nfts ItemConfigOf (max_values: None, max_size: Some(48), added: 2523, mode: MaxEncodedLen) + /// Storage: Nfts Item (r:1 w:1) + /// Proof: Nfts Item (max_values: None, max_size: Some(861), added: 3336, mode: MaxEncodedLen) + /// Storage: Nfts CollectionRoleOf (r:1 w:0) + /// Proof: Nfts CollectionRoleOf (max_values: None, max_size: Some(69), added: 2544, mode: MaxEncodedLen) + /// Storage: System Account (r:1 w:1) + /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) + /// Storage: Nfts Account (r:0 w:2) + /// Proof: Nfts Account (max_values: None, max_size: Some(88), added: 2563, mode: MaxEncodedLen) + /// Storage: Nfts ItemPriceOf (r:0 w:1) + /// Proof: Nfts ItemPriceOf (max_values: None, max_size: Some(89), added: 2564, mode: MaxEncodedLen) + /// Storage: Nfts PendingSwapOf (r:0 w:1) + /// Proof: Nfts PendingSwapOf (max_values: None, max_size: Some(71), added: 2546, mode: MaxEncodedLen) fn transfer() -> Weight { - // Minimum execution time: 66_085 nanoseconds. - Weight::from_ref_time(67_065_000) - .saturating_add(T::DbWeight::get().reads(6)) - .saturating_add(T::DbWeight::get().writes(6)) - } - // Storage: Nfts Collection (r:1 w:0) - // Storage: Nfts CollectionConfigOf (r:1 w:0) - // Storage: Nfts Item (r:102 w:102) + // Proof Size summary in bytes: + // Measured: `882` + // Estimated: `16109` + // Minimum execution time: 51_962 nanoseconds. + Weight::from_parts(52_367_000, 16109) + .saturating_add(T::DbWeight::get().reads(6_u64)) + .saturating_add(T::DbWeight::get().writes(6_u64)) + } + /// Storage: Nfts Collection (r:1 w:0) + /// Proof: Nfts Collection (max_values: None, max_size: Some(80), added: 2555, mode: MaxEncodedLen) + /// Storage: Nfts CollectionConfigOf (r:1 w:0) + /// Proof: Nfts CollectionConfigOf (max_values: None, max_size: Some(73), added: 2548, mode: MaxEncodedLen) + /// Storage: Nfts Item (r:5000 w:5000) + /// Proof: Nfts Item (max_values: None, max_size: Some(861), added: 3336, mode: MaxEncodedLen) /// The range of component `i` is `[0, 5000]`. fn redeposit(i: u32, ) -> Weight { - // Minimum execution time: 25_949 nanoseconds. - Weight::from_ref_time(26_106_000) - // Standard Error: 10_326 - .saturating_add(Weight::from_ref_time(11_496_776).saturating_mul(i.into())) - .saturating_add(T::DbWeight::get().reads(2)) + // Proof Size summary in bytes: + // Measured: `756 + i * (140 ±0)` + // Estimated: `5103 + i * (3336 ±0)` + // Minimum execution time: 15_512 nanoseconds. + Weight::from_parts(15_731_000, 5103) + // Standard Error: 9_495 + .saturating_add(Weight::from_ref_time(11_462_413).saturating_mul(i.into())) + .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().reads((1_u64).saturating_mul(i.into()))) .saturating_add(T::DbWeight::get().writes((1_u64).saturating_mul(i.into()))) + .saturating_add(Weight::from_proof_size(3336).saturating_mul(i.into())) } - // Storage: Nfts CollectionRoleOf (r:1 w:0) - // Storage: Nfts ItemConfigOf (r:1 w:1) + /// Storage: Nfts CollectionRoleOf (r:1 w:0) + /// Proof: Nfts CollectionRoleOf (max_values: None, max_size: Some(69), added: 2544, mode: MaxEncodedLen) + /// Storage: Nfts ItemConfigOf (r:1 w:1) + /// Proof: Nfts ItemConfigOf (max_values: None, max_size: Some(48), added: 2523, mode: MaxEncodedLen) fn lock_item_transfer() -> Weight { - // Minimum execution time: 30_080 nanoseconds. - Weight::from_ref_time(30_825_000) - .saturating_add(T::DbWeight::get().reads(2)) - .saturating_add(T::DbWeight::get().writes(1)) - } - // Storage: Nfts CollectionRoleOf (r:1 w:0) - // Storage: Nfts ItemConfigOf (r:1 w:1) + // Proof Size summary in bytes: + // Measured: `401` + // Estimated: `5067` + // Minimum execution time: 19_273 nanoseconds. + Weight::from_parts(19_508_000, 5067) + .saturating_add(T::DbWeight::get().reads(2_u64)) + .saturating_add(T::DbWeight::get().writes(1_u64)) + } + /// Storage: Nfts CollectionRoleOf (r:1 w:0) + /// Proof: Nfts CollectionRoleOf (max_values: None, max_size: Some(69), added: 2544, mode: MaxEncodedLen) + /// Storage: Nfts ItemConfigOf (r:1 w:1) + /// Proof: Nfts ItemConfigOf (max_values: None, max_size: Some(48), added: 2523, mode: MaxEncodedLen) fn unlock_item_transfer() -> Weight { - // Minimum execution time: 30_612 nanoseconds. - Weight::from_ref_time(31_422_000) - .saturating_add(T::DbWeight::get().reads(2)) - .saturating_add(T::DbWeight::get().writes(1)) - } - // Storage: Nfts CollectionRoleOf (r:1 w:0) - // Storage: Nfts CollectionConfigOf (r:1 w:1) + // Proof Size summary in bytes: + // Measured: `401` + // Estimated: `5067` + // Minimum execution time: 19_022 nanoseconds. + Weight::from_parts(19_430_000, 5067) + .saturating_add(T::DbWeight::get().reads(2_u64)) + .saturating_add(T::DbWeight::get().writes(1_u64)) + } + /// Storage: Nfts CollectionRoleOf (r:1 w:0) + /// Proof: Nfts CollectionRoleOf (max_values: None, max_size: Some(69), added: 2544, mode: MaxEncodedLen) + /// Storage: Nfts CollectionConfigOf (r:1 w:1) + /// Proof: Nfts CollectionConfigOf (max_values: None, max_size: Some(73), added: 2548, mode: MaxEncodedLen) fn lock_collection() -> Weight { - // Minimum execution time: 27_470 nanoseconds. - Weight::from_ref_time(28_015_000) - .saturating_add(T::DbWeight::get().reads(2)) - .saturating_add(T::DbWeight::get().writes(1)) - } - // Storage: Nfts OwnershipAcceptance (r:1 w:1) - // Storage: Nfts Collection (r:1 w:1) - // Storage: Nfts CollectionAccount (r:0 w:2) + // Proof Size summary in bytes: + // Measured: `289` + // Estimated: `5092` + // Minimum execution time: 17_593 nanoseconds. + Weight::from_parts(17_950_000, 5092) + .saturating_add(T::DbWeight::get().reads(2_u64)) + .saturating_add(T::DbWeight::get().writes(1_u64)) + } + /// Storage: Nfts OwnershipAcceptance (r:1 w:1) + /// Proof: Nfts OwnershipAcceptance (max_values: None, max_size: Some(52), added: 2527, mode: MaxEncodedLen) + /// Storage: Nfts Collection (r:1 w:1) + /// Proof: Nfts Collection (max_values: None, max_size: Some(80), added: 2555, mode: MaxEncodedLen) + /// Storage: Nfts CollectionAccount (r:0 w:2) + /// Proof: Nfts CollectionAccount (max_values: None, max_size: Some(68), added: 2543, mode: MaxEncodedLen) fn transfer_ownership() -> Weight { - // Minimum execution time: 33_750 nanoseconds. - Weight::from_ref_time(34_139_000) - .saturating_add(T::DbWeight::get().reads(2)) - .saturating_add(T::DbWeight::get().writes(4)) - } - // Storage: Nfts Collection (r:1 w:1) - // Storage: Nfts CollectionRoleOf (r:0 w:4) + // Proof Size summary in bytes: + // Measured: `381` + // Estimated: `5082` + // Minimum execution time: 22_068 nanoseconds. + Weight::from_parts(22_235_000, 5082) + .saturating_add(T::DbWeight::get().reads(2_u64)) + .saturating_add(T::DbWeight::get().writes(4_u64)) + } + /// Storage: Nfts Collection (r:1 w:1) + /// Proof: Nfts Collection (max_values: None, max_size: Some(80), added: 2555, mode: MaxEncodedLen) + /// Storage: Nfts CollectionRoleOf (r:0 w:4) + /// Proof: Nfts CollectionRoleOf (max_values: None, max_size: Some(69), added: 2544, mode: MaxEncodedLen) fn set_team() -> Weight { - // Minimum execution time: 36_565 nanoseconds. - Weight::from_ref_time(37_464_000) - .saturating_add(T::DbWeight::get().reads(1)) - .saturating_add(T::DbWeight::get().writes(5)) - } - // Storage: Nfts Collection (r:1 w:1) - // Storage: Nfts CollectionAccount (r:0 w:2) + // Proof Size summary in bytes: + // Measured: `362` + // Estimated: `2555` + // Minimum execution time: 25_056 nanoseconds. + Weight::from_parts(25_767_000, 2555) + .saturating_add(T::DbWeight::get().reads(1_u64)) + .saturating_add(T::DbWeight::get().writes(5_u64)) + } + /// Storage: Nfts Collection (r:1 w:1) + /// Proof: Nfts Collection (max_values: None, max_size: Some(80), added: 2555, mode: MaxEncodedLen) + /// Storage: Nfts CollectionAccount (r:0 w:2) + /// Proof: Nfts CollectionAccount (max_values: None, max_size: Some(68), added: 2543, mode: MaxEncodedLen) fn force_collection_owner() -> Weight { - // Minimum execution time: 29_028 nanoseconds. - Weight::from_ref_time(29_479_000) - .saturating_add(T::DbWeight::get().reads(1)) - .saturating_add(T::DbWeight::get().writes(3)) - } - // Storage: Nfts Collection (r:1 w:0) - // Storage: Nfts CollectionConfigOf (r:0 w:1) + // Proof Size summary in bytes: + // Measured: `304` + // Estimated: `2555` + // Minimum execution time: 17_398 nanoseconds. + Weight::from_parts(17_684_000, 2555) + .saturating_add(T::DbWeight::get().reads(1_u64)) + .saturating_add(T::DbWeight::get().writes(3_u64)) + } + /// Storage: Nfts Collection (r:1 w:0) + /// Proof: Nfts Collection (max_values: None, max_size: Some(80), added: 2555, mode: MaxEncodedLen) + /// Storage: Nfts CollectionConfigOf (r:0 w:1) + /// Proof: Nfts CollectionConfigOf (max_values: None, max_size: Some(73), added: 2548, mode: MaxEncodedLen) fn force_collection_config() -> Weight { - // Minimum execution time: 24_695 nanoseconds. - Weight::from_ref_time(25_304_000) - .saturating_add(T::DbWeight::get().reads(1)) - .saturating_add(T::DbWeight::get().writes(1)) - } - // Storage: Nfts Collection (r:1 w:0) - // Storage: Nfts ItemConfigOf (r:1 w:1) + // Proof Size summary in bytes: + // Measured: `242` + // Estimated: `2555` + // Minimum execution time: 14_054 nanoseconds. + Weight::from_parts(14_243_000, 2555) + .saturating_add(T::DbWeight::get().reads(1_u64)) + .saturating_add(T::DbWeight::get().writes(1_u64)) + } + /// Storage: Nfts Collection (r:1 w:0) + /// Proof: Nfts Collection (max_values: None, max_size: Some(80), added: 2555, mode: MaxEncodedLen) + /// Storage: Nfts ItemConfigOf (r:1 w:1) + /// Proof: Nfts ItemConfigOf (max_values: None, max_size: Some(48), added: 2523, mode: MaxEncodedLen) fn lock_item_properties() -> Weight { - // Minimum execution time: 28_910 nanoseconds. - Weight::from_ref_time(29_186_000) - .saturating_add(T::DbWeight::get().reads(2)) - .saturating_add(T::DbWeight::get().writes(1)) - } - // Storage: Nfts Collection (r:1 w:1) - // Storage: Nfts CollectionConfigOf (r:1 w:0) - // Storage: Nfts ItemConfigOf (r:1 w:0) - // Storage: Nfts Attribute (r:1 w:1) + // Proof Size summary in bytes: + // Measured: `445` + // Estimated: `5078` + // Minimum execution time: 17_662 nanoseconds. + Weight::from_parts(18_073_000, 5078) + .saturating_add(T::DbWeight::get().reads(2_u64)) + .saturating_add(T::DbWeight::get().writes(1_u64)) + } + /// Storage: Nfts Collection (r:1 w:1) + /// Proof: Nfts Collection (max_values: None, max_size: Some(80), added: 2555, mode: MaxEncodedLen) + /// Storage: Nfts CollectionConfigOf (r:1 w:0) + /// Proof: Nfts CollectionConfigOf (max_values: None, max_size: Some(73), added: 2548, mode: MaxEncodedLen) + /// Storage: Nfts ItemConfigOf (r:1 w:0) + /// Proof: Nfts ItemConfigOf (max_values: None, max_size: Some(48), added: 2523, mode: MaxEncodedLen) + /// Storage: Nfts Attribute (r:1 w:1) + /// Proof: Nfts Attribute (max_values: None, max_size: Some(446), added: 2921, mode: MaxEncodedLen) fn set_attribute() -> Weight { - // Minimum execution time: 56_407 nanoseconds. - Weight::from_ref_time(58_176_000) - .saturating_add(T::DbWeight::get().reads(4)) - .saturating_add(T::DbWeight::get().writes(2)) - } - // Storage: Nfts Collection (r:1 w:1) - // Storage: Nfts Attribute (r:1 w:1) + // Proof Size summary in bytes: + // Measured: `474` + // Estimated: `10547` + // Minimum execution time: 40_098 nanoseconds. + Weight::from_parts(40_649_000, 10547) + .saturating_add(T::DbWeight::get().reads(4_u64)) + .saturating_add(T::DbWeight::get().writes(2_u64)) + } + /// Storage: Nfts Collection (r:1 w:1) + /// Proof: Nfts Collection (max_values: None, max_size: Some(80), added: 2555, mode: MaxEncodedLen) + /// Storage: Nfts Attribute (r:1 w:1) + /// Proof: Nfts Attribute (max_values: None, max_size: Some(446), added: 2921, mode: MaxEncodedLen) fn force_set_attribute() -> Weight { - // Minimum execution time: 36_402 nanoseconds. - Weight::from_ref_time(37_034_000) - .saturating_add(T::DbWeight::get().reads(2)) - .saturating_add(T::DbWeight::get().writes(2)) - } - // Storage: Nfts Attribute (r:1 w:1) - // Storage: Nfts Collection (r:1 w:1) - // Storage: Nfts ItemConfigOf (r:1 w:0) + // Proof Size summary in bytes: + // Measured: `337` + // Estimated: `5476` + // Minimum execution time: 25_178 nanoseconds. + Weight::from_parts(25_473_000, 5476) + .saturating_add(T::DbWeight::get().reads(2_u64)) + .saturating_add(T::DbWeight::get().writes(2_u64)) + } + /// Storage: Nfts Attribute (r:1 w:1) + /// Proof: Nfts Attribute (max_values: None, max_size: Some(446), added: 2921, mode: MaxEncodedLen) + /// Storage: Nfts Collection (r:1 w:1) + /// Proof: Nfts Collection (max_values: None, max_size: Some(80), added: 2555, mode: MaxEncodedLen) + /// Storage: Nfts ItemConfigOf (r:1 w:0) + /// Proof: Nfts ItemConfigOf (max_values: None, max_size: Some(48), added: 2523, mode: MaxEncodedLen) fn clear_attribute() -> Weight { - // Minimum execution time: 52_022 nanoseconds. - Weight::from_ref_time(54_059_000) - .saturating_add(T::DbWeight::get().reads(3)) - .saturating_add(T::DbWeight::get().writes(2)) - } - // Storage: Nfts Item (r:1 w:0) - // Storage: Nfts ItemAttributesApprovalsOf (r:1 w:1) + // Proof Size summary in bytes: + // Measured: `916` + // Estimated: `7999` + // Minimum execution time: 35_202 nanoseconds. + Weight::from_parts(35_518_000, 7999) + .saturating_add(T::DbWeight::get().reads(3_u64)) + .saturating_add(T::DbWeight::get().writes(2_u64)) + } + /// Storage: Nfts Item (r:1 w:0) + /// Proof: Nfts Item (max_values: None, max_size: Some(861), added: 3336, mode: MaxEncodedLen) + /// Storage: Nfts ItemAttributesApprovalsOf (r:1 w:1) + /// Proof: Nfts ItemAttributesApprovalsOf (max_values: None, max_size: Some(681), added: 3156, mode: MaxEncodedLen) fn approve_item_attributes() -> Weight { - // Minimum execution time: 28_475 nanoseconds. - Weight::from_ref_time(29_162_000) - .saturating_add(T::DbWeight::get().reads(2)) - .saturating_add(T::DbWeight::get().writes(1)) - } - // Storage: Nfts Item (r:1 w:0) - // Storage: Nfts ItemAttributesApprovalsOf (r:1 w:1) - // Storage: Nfts Attribute (r:1 w:0) - // Storage: System Account (r:1 w:1) + // Proof Size summary in bytes: + // Measured: `379` + // Estimated: `6492` + // Minimum execution time: 17_260 nanoseconds. + Weight::from_parts(17_498_000, 6492) + .saturating_add(T::DbWeight::get().reads(2_u64)) + .saturating_add(T::DbWeight::get().writes(1_u64)) + } + /// Storage: Nfts Item (r:1 w:0) + /// Proof: Nfts Item (max_values: None, max_size: Some(861), added: 3336, mode: MaxEncodedLen) + /// Storage: Nfts ItemAttributesApprovalsOf (r:1 w:1) + /// Proof: Nfts ItemAttributesApprovalsOf (max_values: None, max_size: Some(681), added: 3156, mode: MaxEncodedLen) + /// Storage: Nfts Attribute (r:1001 w:1000) + /// Proof: Nfts Attribute (max_values: None, max_size: Some(446), added: 2921, mode: MaxEncodedLen) + /// Storage: System Account (r:1 w:1) + /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) /// The range of component `n` is `[0, 1000]`. fn cancel_item_attributes_approval(n: u32, ) -> Weight { - // Minimum execution time: 37_529 nanoseconds. - Weight::from_ref_time(38_023_000) - // Standard Error: 8_136 - .saturating_add(Weight::from_ref_time(7_452_872).saturating_mul(n.into())) - .saturating_add(T::DbWeight::get().reads(4)) + // Proof Size summary in bytes: + // Measured: `865 + n * (367 ±0)` + // Estimated: `12016 + n * (2921 ±0)` + // Minimum execution time: 25_579 nanoseconds. + Weight::from_parts(25_846_000, 12016) + // Standard Error: 7_759 + .saturating_add(Weight::from_ref_time(7_159_200).saturating_mul(n.into())) + .saturating_add(T::DbWeight::get().reads(4_u64)) .saturating_add(T::DbWeight::get().reads((1_u64).saturating_mul(n.into()))) - .saturating_add(T::DbWeight::get().writes(2)) + .saturating_add(T::DbWeight::get().writes(2_u64)) .saturating_add(T::DbWeight::get().writes((1_u64).saturating_mul(n.into()))) - } - // Storage: Nfts Collection (r:1 w:1) - // Storage: Nfts ItemConfigOf (r:1 w:0) - // Storage: Nfts CollectionConfigOf (r:1 w:0) - // Storage: Nfts ItemMetadataOf (r:1 w:1) + .saturating_add(Weight::from_proof_size(2921).saturating_mul(n.into())) + } + /// Storage: Nfts Collection (r:1 w:1) + /// Proof: Nfts Collection (max_values: None, max_size: Some(80), added: 2555, mode: MaxEncodedLen) + /// Storage: Nfts ItemConfigOf (r:1 w:0) + /// Proof: Nfts ItemConfigOf (max_values: None, max_size: Some(48), added: 2523, mode: MaxEncodedLen) + /// Storage: Nfts CollectionConfigOf (r:1 w:0) + /// Proof: Nfts CollectionConfigOf (max_values: None, max_size: Some(73), added: 2548, mode: MaxEncodedLen) + /// Storage: Nfts ItemMetadataOf (r:1 w:1) + /// Proof: Nfts ItemMetadataOf (max_values: None, max_size: Some(140), added: 2615, mode: MaxEncodedLen) fn set_metadata() -> Weight { - // Minimum execution time: 49_300 nanoseconds. - Weight::from_ref_time(49_790_000) - .saturating_add(T::DbWeight::get().reads(4)) - .saturating_add(T::DbWeight::get().writes(2)) - } - // Storage: Nfts Collection (r:1 w:1) - // Storage: Nfts ItemConfigOf (r:1 w:0) - // Storage: Nfts ItemMetadataOf (r:1 w:1) + // Proof Size summary in bytes: + // Measured: `474` + // Estimated: `10241` + // Minimum execution time: 33_285 nanoseconds. + Weight::from_parts(33_692_000, 10241) + .saturating_add(T::DbWeight::get().reads(4_u64)) + .saturating_add(T::DbWeight::get().writes(2_u64)) + } + /// Storage: Nfts ItemMetadataOf (r:1 w:1) + /// Proof: Nfts ItemMetadataOf (max_values: None, max_size: Some(140), added: 2615, mode: MaxEncodedLen) + /// Storage: Nfts Collection (r:1 w:1) + /// Proof: Nfts Collection (max_values: None, max_size: Some(80), added: 2555, mode: MaxEncodedLen) + /// Storage: Nfts ItemConfigOf (r:1 w:0) + /// Proof: Nfts ItemConfigOf (max_values: None, max_size: Some(48), added: 2523, mode: MaxEncodedLen) fn clear_metadata() -> Weight { - // Minimum execution time: 47_248 nanoseconds. - Weight::from_ref_time(48_094_000) - .saturating_add(T::DbWeight::get().reads(3)) - .saturating_add(T::DbWeight::get().writes(2)) - } - // Storage: Nfts CollectionConfigOf (r:1 w:0) - // Storage: Nfts Collection (r:1 w:1) - // Storage: Nfts CollectionMetadataOf (r:1 w:1) + // Proof Size summary in bytes: + // Measured: `609` + // Estimated: `7693` + // Minimum execution time: 30_670 nanoseconds. + Weight::from_parts(31_282_000, 7693) + .saturating_add(T::DbWeight::get().reads(3_u64)) + .saturating_add(T::DbWeight::get().writes(2_u64)) + } + /// Storage: Nfts CollectionConfigOf (r:1 w:0) + /// Proof: Nfts CollectionConfigOf (max_values: None, max_size: Some(73), added: 2548, mode: MaxEncodedLen) + /// Storage: Nfts Collection (r:1 w:1) + /// Proof: Nfts Collection (max_values: None, max_size: Some(80), added: 2555, mode: MaxEncodedLen) + /// Storage: Nfts CollectionMetadataOf (r:1 w:1) + /// Proof: Nfts CollectionMetadataOf (max_values: None, max_size: Some(87), added: 2562, mode: MaxEncodedLen) fn set_collection_metadata() -> Weight { - // Minimum execution time: 44_137 nanoseconds. - Weight::from_ref_time(44_905_000) - .saturating_add(T::DbWeight::get().reads(3)) - .saturating_add(T::DbWeight::get().writes(2)) - } - // Storage: Nfts Collection (r:1 w:0) - // Storage: Nfts CollectionConfigOf (r:1 w:0) - // Storage: Nfts CollectionMetadataOf (r:1 w:1) + // Proof Size summary in bytes: + // Measured: `333` + // Estimated: `7665` + // Minimum execution time: 28_313 nanoseconds. + Weight::from_parts(28_724_000, 7665) + .saturating_add(T::DbWeight::get().reads(3_u64)) + .saturating_add(T::DbWeight::get().writes(2_u64)) + } + /// Storage: Nfts Collection (r:1 w:0) + /// Proof: Nfts Collection (max_values: None, max_size: Some(80), added: 2555, mode: MaxEncodedLen) + /// Storage: Nfts CollectionConfigOf (r:1 w:0) + /// Proof: Nfts CollectionConfigOf (max_values: None, max_size: Some(73), added: 2548, mode: MaxEncodedLen) + /// Storage: Nfts CollectionMetadataOf (r:1 w:1) + /// Proof: Nfts CollectionMetadataOf (max_values: None, max_size: Some(87), added: 2562, mode: MaxEncodedLen) fn clear_collection_metadata() -> Weight { - // Minimum execution time: 43_005 nanoseconds. - Weight::from_ref_time(43_898_000) - .saturating_add(T::DbWeight::get().reads(3)) - .saturating_add(T::DbWeight::get().writes(1)) - } - // Storage: Nfts Item (r:1 w:1) - // Storage: Nfts CollectionConfigOf (r:1 w:0) - // Storage: Nfts CollectionRoleOf (r:1 w:0) + // Proof Size summary in bytes: + // Measured: `476` + // Estimated: `7665` + // Minimum execution time: 27_034 nanoseconds. + Weight::from_parts(27_655_000, 7665) + .saturating_add(T::DbWeight::get().reads(3_u64)) + .saturating_add(T::DbWeight::get().writes(1_u64)) + } + /// Storage: Nfts Item (r:1 w:1) + /// Proof: Nfts Item (max_values: None, max_size: Some(861), added: 3336, mode: MaxEncodedLen) + /// Storage: Nfts CollectionConfigOf (r:1 w:0) + /// Proof: Nfts CollectionConfigOf (max_values: None, max_size: Some(73), added: 2548, mode: MaxEncodedLen) + /// Storage: Nfts CollectionRoleOf (r:1 w:0) + /// Proof: Nfts CollectionRoleOf (max_values: None, max_size: Some(69), added: 2544, mode: MaxEncodedLen) fn approve_transfer() -> Weight { - // Minimum execution time: 36_344 nanoseconds. - Weight::from_ref_time(36_954_000) - .saturating_add(T::DbWeight::get().reads(3)) - .saturating_add(T::DbWeight::get().writes(1)) - } - // Storage: Nfts Item (r:1 w:1) - // Storage: Nfts CollectionRoleOf (r:1 w:0) + // Proof Size summary in bytes: + // Measured: `466` + // Estimated: `8428` + // Minimum execution time: 23_408 nanoseconds. + Weight::from_parts(23_916_000, 8428) + .saturating_add(T::DbWeight::get().reads(3_u64)) + .saturating_add(T::DbWeight::get().writes(1_u64)) + } + /// Storage: Nfts Item (r:1 w:1) + /// Proof: Nfts Item (max_values: None, max_size: Some(861), added: 3336, mode: MaxEncodedLen) + /// Storage: Nfts CollectionRoleOf (r:1 w:0) + /// Proof: Nfts CollectionRoleOf (max_values: None, max_size: Some(69), added: 2544, mode: MaxEncodedLen) fn cancel_approval() -> Weight { - // Minimum execution time: 32_418 nanoseconds. - Weight::from_ref_time(33_029_000) - .saturating_add(T::DbWeight::get().reads(2)) - .saturating_add(T::DbWeight::get().writes(1)) - } - // Storage: Nfts Item (r:1 w:1) - // Storage: Nfts CollectionRoleOf (r:1 w:0) + // Proof Size summary in bytes: + // Measured: `474` + // Estimated: `5880` + // Minimum execution time: 21_177 nanoseconds. + Weight::from_parts(21_492_000, 5880) + .saturating_add(T::DbWeight::get().reads(2_u64)) + .saturating_add(T::DbWeight::get().writes(1_u64)) + } + /// Storage: Nfts Item (r:1 w:1) + /// Proof: Nfts Item (max_values: None, max_size: Some(861), added: 3336, mode: MaxEncodedLen) + /// Storage: Nfts CollectionRoleOf (r:1 w:0) + /// Proof: Nfts CollectionRoleOf (max_values: None, max_size: Some(69), added: 2544, mode: MaxEncodedLen) fn clear_all_transfer_approvals() -> Weight { - // Minimum execution time: 31_448 nanoseconds. - Weight::from_ref_time(31_979_000) - .saturating_add(T::DbWeight::get().reads(2)) - .saturating_add(T::DbWeight::get().writes(1)) - } - // Storage: Nfts OwnershipAcceptance (r:1 w:1) + // Proof Size summary in bytes: + // Measured: `474` + // Estimated: `5880` + // Minimum execution time: 20_279 nanoseconds. + Weight::from_parts(20_919_000, 5880) + .saturating_add(T::DbWeight::get().reads(2_u64)) + .saturating_add(T::DbWeight::get().writes(1_u64)) + } + /// Storage: Nfts OwnershipAcceptance (r:1 w:1) + /// Proof: Nfts OwnershipAcceptance (max_values: None, max_size: Some(52), added: 2527, mode: MaxEncodedLen) fn set_accept_ownership() -> Weight { - // Minimum execution time: 27_487 nanoseconds. - Weight::from_ref_time(28_080_000) - .saturating_add(T::DbWeight::get().reads(1)) - .saturating_add(T::DbWeight::get().writes(1)) - } - // Storage: Nfts CollectionConfigOf (r:1 w:1) - // Storage: Nfts Collection (r:1 w:0) + // Proof Size summary in bytes: + // Measured: `42` + // Estimated: `2527` + // Minimum execution time: 14_921 nanoseconds. + Weight::from_parts(15_382_000, 2527) + .saturating_add(T::DbWeight::get().reads(1_u64)) + .saturating_add(T::DbWeight::get().writes(1_u64)) + } + /// Storage: Nfts CollectionConfigOf (r:1 w:1) + /// Proof: Nfts CollectionConfigOf (max_values: None, max_size: Some(73), added: 2548, mode: MaxEncodedLen) + /// Storage: Nfts Collection (r:1 w:0) + /// Proof: Nfts Collection (max_values: None, max_size: Some(80), added: 2555, mode: MaxEncodedLen) fn set_collection_max_supply() -> Weight { - // Minimum execution time: 28_235 nanoseconds. - Weight::from_ref_time(28_967_000) - .saturating_add(T::DbWeight::get().reads(2)) - .saturating_add(T::DbWeight::get().writes(1)) - } - // Storage: Nfts Collection (r:1 w:0) - // Storage: Nfts CollectionConfigOf (r:1 w:1) + // Proof Size summary in bytes: + // Measured: `333` + // Estimated: `5103` + // Minimum execution time: 18_201 nanoseconds. + Weight::from_parts(18_628_000, 5103) + .saturating_add(T::DbWeight::get().reads(2_u64)) + .saturating_add(T::DbWeight::get().writes(1_u64)) + } + /// Storage: Nfts Collection (r:1 w:0) + /// Proof: Nfts Collection (max_values: None, max_size: Some(80), added: 2555, mode: MaxEncodedLen) + /// Storage: Nfts CollectionConfigOf (r:1 w:1) + /// Proof: Nfts CollectionConfigOf (max_values: None, max_size: Some(73), added: 2548, mode: MaxEncodedLen) fn update_mint_settings() -> Weight { - // Minimum execution time: 28_172 nanoseconds. - Weight::from_ref_time(28_636_000) - .saturating_add(T::DbWeight::get().reads(2)) - .saturating_add(T::DbWeight::get().writes(1)) - } - // Storage: Nfts Item (r:1 w:0) - // Storage: Nfts CollectionConfigOf (r:1 w:0) - // Storage: Nfts ItemConfigOf (r:1 w:0) - // Storage: Nfts ItemPriceOf (r:0 w:1) + // Proof Size summary in bytes: + // Measured: `333` + // Estimated: `5103` + // Minimum execution time: 16_870 nanoseconds. + Weight::from_parts(17_318_000, 5103) + .saturating_add(T::DbWeight::get().reads(2_u64)) + .saturating_add(T::DbWeight::get().writes(1_u64)) + } + /// Storage: Nfts Item (r:1 w:0) + /// Proof: Nfts Item (max_values: None, max_size: Some(861), added: 3336, mode: MaxEncodedLen) + /// Storage: Nfts CollectionConfigOf (r:1 w:0) + /// Proof: Nfts CollectionConfigOf (max_values: None, max_size: Some(73), added: 2548, mode: MaxEncodedLen) + /// Storage: Nfts ItemConfigOf (r:1 w:0) + /// Proof: Nfts ItemConfigOf (max_values: None, max_size: Some(48), added: 2523, mode: MaxEncodedLen) + /// Storage: Nfts ItemPriceOf (r:0 w:1) + /// Proof: Nfts ItemPriceOf (max_values: None, max_size: Some(89), added: 2564, mode: MaxEncodedLen) fn set_price() -> Weight { - // Minimum execution time: 35_336 nanoseconds. - Weight::from_ref_time(36_026_000) - .saturating_add(T::DbWeight::get().reads(3)) - .saturating_add(T::DbWeight::get().writes(1)) - } - // Storage: Nfts Item (r:1 w:1) - // Storage: Nfts ItemPriceOf (r:1 w:1) - // Storage: Nfts Collection (r:1 w:0) - // Storage: Nfts CollectionConfigOf (r:1 w:0) - // Storage: Nfts ItemConfigOf (r:1 w:0) - // Storage: System Account (r:1 w:1) - // Storage: Nfts Account (r:0 w:2) - // Storage: Nfts PendingSwapOf (r:0 w:1) + // Proof Size summary in bytes: + // Measured: `516` + // Estimated: `8407` + // Minimum execution time: 22_604 nanoseconds. + Weight::from_parts(22_867_000, 8407) + .saturating_add(T::DbWeight::get().reads(3_u64)) + .saturating_add(T::DbWeight::get().writes(1_u64)) + } + /// Storage: Nfts Item (r:1 w:1) + /// Proof: Nfts Item (max_values: None, max_size: Some(861), added: 3336, mode: MaxEncodedLen) + /// Storage: Nfts ItemPriceOf (r:1 w:1) + /// Proof: Nfts ItemPriceOf (max_values: None, max_size: Some(89), added: 2564, mode: MaxEncodedLen) + /// Storage: Nfts Collection (r:1 w:0) + /// Proof: Nfts Collection (max_values: None, max_size: Some(80), added: 2555, mode: MaxEncodedLen) + /// Storage: Nfts CollectionConfigOf (r:1 w:0) + /// Proof: Nfts CollectionConfigOf (max_values: None, max_size: Some(73), added: 2548, mode: MaxEncodedLen) + /// Storage: Nfts ItemConfigOf (r:1 w:0) + /// Proof: Nfts ItemConfigOf (max_values: None, max_size: Some(48), added: 2523, mode: MaxEncodedLen) + /// Storage: System Account (r:1 w:1) + /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) + /// Storage: Nfts Account (r:0 w:2) + /// Proof: Nfts Account (max_values: None, max_size: Some(88), added: 2563, mode: MaxEncodedLen) + /// Storage: Nfts PendingSwapOf (r:0 w:1) + /// Proof: Nfts PendingSwapOf (max_values: None, max_size: Some(71), added: 2546, mode: MaxEncodedLen) fn buy_item() -> Weight { - // Minimum execution time: 70_971 nanoseconds. - Weight::from_ref_time(72_036_000) - .saturating_add(T::DbWeight::get().reads(6)) - .saturating_add(T::DbWeight::get().writes(6)) + // Proof Size summary in bytes: + // Measured: `934` + // Estimated: `16129` + // Minimum execution time: 56_849 nanoseconds. + Weight::from_parts(57_336_000, 16129) + .saturating_add(T::DbWeight::get().reads(6_u64)) + .saturating_add(T::DbWeight::get().writes(6_u64)) } /// The range of component `n` is `[0, 10]`. fn pay_tips(n: u32, ) -> Weight { - // Minimum execution time: 5_151 nanoseconds. - Weight::from_ref_time(11_822_888) - // Standard Error: 38_439 - .saturating_add(Weight::from_ref_time(3_511_844).saturating_mul(n.into())) - } - // Storage: Nfts Item (r:2 w:0) - // Storage: Nfts PendingSwapOf (r:0 w:1) + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 2_308 nanoseconds. + Weight::from_ref_time(4_805_401) + // Standard Error: 13_875 + .saturating_add(Weight::from_ref_time(3_167_190).saturating_mul(n.into())) + } + /// Storage: Nfts Item (r:2 w:0) + /// Proof: Nfts Item (max_values: None, max_size: Some(861), added: 3336, mode: MaxEncodedLen) + /// Storage: Nfts PendingSwapOf (r:0 w:1) + /// Proof: Nfts PendingSwapOf (max_values: None, max_size: Some(71), added: 2546, mode: MaxEncodedLen) fn create_swap() -> Weight { - // Minimum execution time: 33_027 nanoseconds. - Weight::from_ref_time(33_628_000) - .saturating_add(T::DbWeight::get().reads(2)) - .saturating_add(T::DbWeight::get().writes(1)) - } - // Storage: Nfts PendingSwapOf (r:1 w:1) - // Storage: Nfts Item (r:1 w:0) + // Proof Size summary in bytes: + // Measured: `524` + // Estimated: `6672` + // Minimum execution time: 20_395 nanoseconds. + Weight::from_parts(20_716_000, 6672) + .saturating_add(T::DbWeight::get().reads(2_u64)) + .saturating_add(T::DbWeight::get().writes(1_u64)) + } + /// Storage: Nfts PendingSwapOf (r:1 w:1) + /// Proof: Nfts PendingSwapOf (max_values: None, max_size: Some(71), added: 2546, mode: MaxEncodedLen) + /// Storage: Nfts Item (r:1 w:0) + /// Proof: Nfts Item (max_values: None, max_size: Some(861), added: 3336, mode: MaxEncodedLen) fn cancel_swap() -> Weight { - // Minimum execution time: 35_890 nanoseconds. - Weight::from_ref_time(36_508_000) - .saturating_add(T::DbWeight::get().reads(2)) - .saturating_add(T::DbWeight::get().writes(1)) - } - // Storage: Nfts Item (r:2 w:2) - // Storage: Nfts PendingSwapOf (r:1 w:2) - // Storage: Nfts Collection (r:1 w:0) - // Storage: Nfts CollectionConfigOf (r:1 w:0) - // Storage: Nfts ItemConfigOf (r:2 w:0) - // Storage: System Account (r:1 w:1) - // Storage: Nfts Account (r:0 w:4) - // Storage: Nfts ItemPriceOf (r:0 w:2) + // Proof Size summary in bytes: + // Measured: `511` + // Estimated: `5882` + // Minimum execution time: 19_936 nanoseconds. + Weight::from_parts(20_344_000, 5882) + .saturating_add(T::DbWeight::get().reads(2_u64)) + .saturating_add(T::DbWeight::get().writes(1_u64)) + } + /// Storage: Nfts Item (r:2 w:2) + /// Proof: Nfts Item (max_values: None, max_size: Some(861), added: 3336, mode: MaxEncodedLen) + /// Storage: Nfts PendingSwapOf (r:1 w:2) + /// Proof: Nfts PendingSwapOf (max_values: None, max_size: Some(71), added: 2546, mode: MaxEncodedLen) + /// Storage: Nfts Collection (r:1 w:0) + /// Proof: Nfts Collection (max_values: None, max_size: Some(80), added: 2555, mode: MaxEncodedLen) + /// Storage: Nfts CollectionConfigOf (r:1 w:0) + /// Proof: Nfts CollectionConfigOf (max_values: None, max_size: Some(73), added: 2548, mode: MaxEncodedLen) + /// Storage: Nfts ItemConfigOf (r:2 w:0) + /// Proof: Nfts ItemConfigOf (max_values: None, max_size: Some(48), added: 2523, mode: MaxEncodedLen) + /// Storage: System Account (r:1 w:1) + /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) + /// Storage: Nfts Account (r:0 w:4) + /// Proof: Nfts Account (max_values: None, max_size: Some(88), added: 2563, mode: MaxEncodedLen) + /// Storage: Nfts ItemPriceOf (r:0 w:2) + /// Proof: Nfts ItemPriceOf (max_values: None, max_size: Some(89), added: 2564, mode: MaxEncodedLen) fn claim_swap() -> Weight { - // Minimum execution time: 101_076 nanoseconds. - Weight::from_ref_time(101_863_000) - .saturating_add(T::DbWeight::get().reads(8)) - .saturating_add(T::DbWeight::get().writes(11)) + // Proof Size summary in bytes: + // Measured: `1097` + // Estimated: `21970` + // Minimum execution time: 80_884 nanoseconds. + Weight::from_parts(81_643_000, 21970) + .saturating_add(T::DbWeight::get().reads(8_u64)) + .saturating_add(T::DbWeight::get().writes(11_u64)) } } // For backwards compatibility and tests impl WeightInfo for () { - // Storage: Nfts NextCollectionId (r:1 w:1) - // Storage: Nfts Collection (r:1 w:1) - // Storage: Nfts CollectionRoleOf (r:0 w:1) - // Storage: Nfts CollectionConfigOf (r:0 w:1) - // Storage: Nfts CollectionAccount (r:0 w:1) + /// Storage: Nfts NextCollectionId (r:1 w:1) + /// Proof: Nfts NextCollectionId (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) + /// Storage: Nfts Collection (r:1 w:1) + /// Proof: Nfts Collection (max_values: None, max_size: Some(80), added: 2555, mode: MaxEncodedLen) + /// Storage: Nfts CollectionRoleOf (r:0 w:1) + /// Proof: Nfts CollectionRoleOf (max_values: None, max_size: Some(69), added: 2544, mode: MaxEncodedLen) + /// Storage: Nfts CollectionConfigOf (r:0 w:1) + /// Proof: Nfts CollectionConfigOf (max_values: None, max_size: Some(73), added: 2548, mode: MaxEncodedLen) + /// Storage: Nfts CollectionAccount (r:0 w:1) + /// Proof: Nfts CollectionAccount (max_values: None, max_size: Some(68), added: 2543, mode: MaxEncodedLen) fn create() -> Weight { - // Minimum execution time: 44_312 nanoseconds. - Weight::from_ref_time(44_871_000) - .saturating_add(RocksDbWeight::get().reads(2)) - .saturating_add(RocksDbWeight::get().writes(5)) - } - // Storage: Nfts NextCollectionId (r:1 w:1) - // Storage: Nfts Collection (r:1 w:1) - // Storage: Nfts CollectionRoleOf (r:0 w:1) - // Storage: Nfts CollectionConfigOf (r:0 w:1) - // Storage: Nfts CollectionAccount (r:0 w:1) + // Proof Size summary in bytes: + // Measured: `214` + // Estimated: `3054` + // Minimum execution time: 32_467 nanoseconds. + Weight::from_parts(33_236_000, 3054) + .saturating_add(RocksDbWeight::get().reads(2_u64)) + .saturating_add(RocksDbWeight::get().writes(5_u64)) + } + /// Storage: Nfts NextCollectionId (r:1 w:1) + /// Proof: Nfts NextCollectionId (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) + /// Storage: Nfts Collection (r:1 w:1) + /// Proof: Nfts Collection (max_values: None, max_size: Some(80), added: 2555, mode: MaxEncodedLen) + /// Storage: Nfts CollectionRoleOf (r:0 w:1) + /// Proof: Nfts CollectionRoleOf (max_values: None, max_size: Some(69), added: 2544, mode: MaxEncodedLen) + /// Storage: Nfts CollectionConfigOf (r:0 w:1) + /// Proof: Nfts CollectionConfigOf (max_values: None, max_size: Some(73), added: 2548, mode: MaxEncodedLen) + /// Storage: Nfts CollectionAccount (r:0 w:1) + /// Proof: Nfts CollectionAccount (max_values: None, max_size: Some(68), added: 2543, mode: MaxEncodedLen) fn force_create() -> Weight { - // Minimum execution time: 31_654 nanoseconds. - Weight::from_ref_time(32_078_000) - .saturating_add(RocksDbWeight::get().reads(2)) - .saturating_add(RocksDbWeight::get().writes(5)) - } - // Storage: Nfts Collection (r:1 w:1) - // Storage: Nfts Item (r:1001 w:1000) - // Storage: Nfts Attribute (r:1001 w:1000) - // Storage: Nfts ItemMetadataOf (r:0 w:1000) - // Storage: Nfts CollectionRoleOf (r:0 w:1) - // Storage: Nfts CollectionMetadataOf (r:0 w:1) - // Storage: Nfts CollectionConfigOf (r:0 w:1) - // Storage: Nfts ItemConfigOf (r:0 w:1000) - // Storage: Nfts Account (r:0 w:1000) - // Storage: Nfts CollectionAccount (r:0 w:1) + // Proof Size summary in bytes: + // Measured: `42` + // Estimated: `3054` + // Minimum execution time: 22_198 nanoseconds. + Weight::from_parts(22_776_000, 3054) + .saturating_add(RocksDbWeight::get().reads(2_u64)) + .saturating_add(RocksDbWeight::get().writes(5_u64)) + } + /// Storage: Nfts Collection (r:1 w:1) + /// Proof: Nfts Collection (max_values: None, max_size: Some(80), added: 2555, mode: MaxEncodedLen) + /// Storage: Nfts Item (r:1001 w:1000) + /// Proof: Nfts Item (max_values: None, max_size: Some(861), added: 3336, mode: MaxEncodedLen) + /// Storage: Nfts ItemMetadataOf (r:1001 w:1000) + /// Proof: Nfts ItemMetadataOf (max_values: None, max_size: Some(140), added: 2615, mode: MaxEncodedLen) + /// Storage: Nfts Attribute (r:1001 w:1000) + /// Proof: Nfts Attribute (max_values: None, max_size: Some(446), added: 2921, mode: MaxEncodedLen) + /// Storage: Nfts CollectionRoleOf (r:0 w:1) + /// Proof: Nfts CollectionRoleOf (max_values: None, max_size: Some(69), added: 2544, mode: MaxEncodedLen) + /// Storage: Nfts CollectionMetadataOf (r:0 w:1) + /// Proof: Nfts CollectionMetadataOf (max_values: None, max_size: Some(87), added: 2562, mode: MaxEncodedLen) + /// Storage: Nfts CollectionConfigOf (r:0 w:1) + /// Proof: Nfts CollectionConfigOf (max_values: None, max_size: Some(73), added: 2548, mode: MaxEncodedLen) + /// Storage: Nfts ItemConfigOf (r:0 w:1000) + /// Proof: Nfts ItemConfigOf (max_values: None, max_size: Some(48), added: 2523, mode: MaxEncodedLen) + /// Storage: Nfts Account (r:0 w:1000) + /// Proof: Nfts Account (max_values: None, max_size: Some(88), added: 2563, mode: MaxEncodedLen) + /// Storage: Nfts CollectionAccount (r:0 w:1) + /// Proof: Nfts CollectionAccount (max_values: None, max_size: Some(68), added: 2543, mode: MaxEncodedLen) /// The range of component `n` is `[0, 1000]`. /// The range of component `m` is `[0, 1000]`. /// The range of component `a` is `[0, 1000]`. fn destroy(n: u32, m: u32, a: u32, ) -> Weight { - // Minimum execution time: 19_183_393 nanoseconds. - Weight::from_ref_time(17_061_526_855) - // Standard Error: 16_689 - .saturating_add(Weight::from_ref_time(353_523).saturating_mul(n.into())) - // Standard Error: 16_689 - .saturating_add(Weight::from_ref_time(1_861_080).saturating_mul(m.into())) - // Standard Error: 16_689 - .saturating_add(Weight::from_ref_time(8_858_987).saturating_mul(a.into())) - .saturating_add(RocksDbWeight::get().reads(1003)) + // Proof Size summary in bytes: + // Measured: `172781 + m * (127 ±0) + a * (402 ±0)` + // Estimated: `3347427 + a * (2921 ±0) + m * (2615 ±0)` + // Minimum execution time: 24_021_657 nanoseconds. + Weight::from_parts(16_029_391_606, 3347427) + // Standard Error: 20_364 + .saturating_add(Weight::from_ref_time(300_580).saturating_mul(n.into())) + // Standard Error: 20_364 + .saturating_add(Weight::from_ref_time(7_748_502).saturating_mul(m.into())) + // Standard Error: 20_364 + .saturating_add(Weight::from_ref_time(9_183_566).saturating_mul(a.into())) + .saturating_add(RocksDbWeight::get().reads(1004_u64)) + .saturating_add(RocksDbWeight::get().reads((1_u64).saturating_mul(m.into()))) .saturating_add(RocksDbWeight::get().reads((1_u64).saturating_mul(a.into()))) - .saturating_add(RocksDbWeight::get().writes(3005)) + .saturating_add(RocksDbWeight::get().writes(3005_u64)) .saturating_add(RocksDbWeight::get().writes((1_u64).saturating_mul(m.into()))) .saturating_add(RocksDbWeight::get().writes((1_u64).saturating_mul(a.into()))) - } - // Storage: Nfts CollectionConfigOf (r:1 w:0) - // Storage: Nfts Item (r:1 w:1) - // Storage: Nfts Collection (r:1 w:1) - // Storage: Nfts CollectionRoleOf (r:1 w:0) - // Storage: Nfts ItemConfigOf (r:1 w:1) - // Storage: Nfts Account (r:0 w:1) + .saturating_add(Weight::from_proof_size(2921).saturating_mul(a.into())) + .saturating_add(Weight::from_proof_size(2615).saturating_mul(m.into())) + } + /// Storage: Nfts CollectionConfigOf (r:1 w:0) + /// Proof: Nfts CollectionConfigOf (max_values: None, max_size: Some(73), added: 2548, mode: MaxEncodedLen) + /// Storage: Nfts Item (r:1 w:1) + /// Proof: Nfts Item (max_values: None, max_size: Some(861), added: 3336, mode: MaxEncodedLen) + /// Storage: Nfts Collection (r:1 w:1) + /// Proof: Nfts Collection (max_values: None, max_size: Some(80), added: 2555, mode: MaxEncodedLen) + /// Storage: Nfts CollectionRoleOf (r:1 w:0) + /// Proof: Nfts CollectionRoleOf (max_values: None, max_size: Some(69), added: 2544, mode: MaxEncodedLen) + /// Storage: Nfts ItemConfigOf (r:1 w:1) + /// Proof: Nfts ItemConfigOf (max_values: None, max_size: Some(48), added: 2523, mode: MaxEncodedLen) + /// Storage: Nfts Account (r:0 w:1) + /// Proof: Nfts Account (max_values: None, max_size: Some(88), added: 2563, mode: MaxEncodedLen) fn mint() -> Weight { - // Minimum execution time: 57_753 nanoseconds. - Weight::from_ref_time(58_313_000) - .saturating_add(RocksDbWeight::get().reads(5)) - .saturating_add(RocksDbWeight::get().writes(4)) - } - // Storage: Nfts CollectionRoleOf (r:1 w:0) - // Storage: Nfts Item (r:1 w:1) - // Storage: Nfts Collection (r:1 w:1) - // Storage: Nfts CollectionConfigOf (r:1 w:0) - // Storage: Nfts ItemConfigOf (r:1 w:1) - // Storage: Nfts Account (r:0 w:1) + // Proof Size summary in bytes: + // Measured: `448` + // Estimated: `13506` + // Minimum execution time: 42_634 nanoseconds. + Weight::from_parts(43_231_000, 13506) + .saturating_add(RocksDbWeight::get().reads(5_u64)) + .saturating_add(RocksDbWeight::get().writes(4_u64)) + } + /// Storage: Nfts CollectionRoleOf (r:1 w:0) + /// Proof: Nfts CollectionRoleOf (max_values: None, max_size: Some(69), added: 2544, mode: MaxEncodedLen) + /// Storage: Nfts Item (r:1 w:1) + /// Proof: Nfts Item (max_values: None, max_size: Some(861), added: 3336, mode: MaxEncodedLen) + /// Storage: Nfts Collection (r:1 w:1) + /// Proof: Nfts Collection (max_values: None, max_size: Some(80), added: 2555, mode: MaxEncodedLen) + /// Storage: Nfts CollectionConfigOf (r:1 w:0) + /// Proof: Nfts CollectionConfigOf (max_values: None, max_size: Some(73), added: 2548, mode: MaxEncodedLen) + /// Storage: Nfts ItemConfigOf (r:1 w:1) + /// Proof: Nfts ItemConfigOf (max_values: None, max_size: Some(48), added: 2523, mode: MaxEncodedLen) + /// Storage: Nfts Account (r:0 w:1) + /// Proof: Nfts Account (max_values: None, max_size: Some(88), added: 2563, mode: MaxEncodedLen) fn force_mint() -> Weight { - // Minimum execution time: 56_429 nanoseconds. - Weight::from_ref_time(57_202_000) - .saturating_add(RocksDbWeight::get().reads(5)) - .saturating_add(RocksDbWeight::get().writes(4)) - } - // Storage: Nfts Collection (r:1 w:1) - // Storage: Nfts Item (r:1 w:1) - // Storage: Nfts CollectionRoleOf (r:1 w:0) - // Storage: Nfts ItemConfigOf (r:1 w:1) - // Storage: Nfts Account (r:0 w:1) - // Storage: Nfts ItemPriceOf (r:0 w:1) - // Storage: Nfts ItemAttributesApprovalsOf (r:0 w:1) - // Storage: Nfts PendingSwapOf (r:0 w:1) + // Proof Size summary in bytes: + // Measured: `448` + // Estimated: `13506` + // Minimum execution time: 41_686 nanoseconds. + Weight::from_parts(41_991_000, 13506) + .saturating_add(RocksDbWeight::get().reads(5_u64)) + .saturating_add(RocksDbWeight::get().writes(4_u64)) + } + /// Storage: Nfts ItemConfigOf (r:1 w:1) + /// Proof: Nfts ItemConfigOf (max_values: None, max_size: Some(48), added: 2523, mode: MaxEncodedLen) + /// Storage: Nfts Collection (r:1 w:1) + /// Proof: Nfts Collection (max_values: None, max_size: Some(80), added: 2555, mode: MaxEncodedLen) + /// Storage: Nfts Item (r:1 w:1) + /// Proof: Nfts Item (max_values: None, max_size: Some(861), added: 3336, mode: MaxEncodedLen) + /// Storage: Nfts CollectionRoleOf (r:1 w:0) + /// Proof: Nfts CollectionRoleOf (max_values: None, max_size: Some(69), added: 2544, mode: MaxEncodedLen) + /// Storage: Nfts ItemMetadataOf (r:1 w:0) + /// Proof: Nfts ItemMetadataOf (max_values: None, max_size: Some(140), added: 2615, mode: MaxEncodedLen) + /// Storage: Nfts Account (r:0 w:1) + /// Proof: Nfts Account (max_values: None, max_size: Some(88), added: 2563, mode: MaxEncodedLen) + /// Storage: Nfts ItemPriceOf (r:0 w:1) + /// Proof: Nfts ItemPriceOf (max_values: None, max_size: Some(89), added: 2564, mode: MaxEncodedLen) + /// Storage: Nfts ItemAttributesApprovalsOf (r:0 w:1) + /// Proof: Nfts ItemAttributesApprovalsOf (max_values: None, max_size: Some(681), added: 3156, mode: MaxEncodedLen) + /// Storage: Nfts PendingSwapOf (r:0 w:1) + /// Proof: Nfts PendingSwapOf (max_values: None, max_size: Some(71), added: 2546, mode: MaxEncodedLen) fn burn() -> Weight { - // Minimum execution time: 59_681 nanoseconds. - Weight::from_ref_time(60_058_000) - .saturating_add(RocksDbWeight::get().reads(4)) - .saturating_add(RocksDbWeight::get().writes(7)) - } - // Storage: Nfts Collection (r:1 w:0) - // Storage: Nfts CollectionConfigOf (r:1 w:0) - // Storage: Nfts ItemConfigOf (r:1 w:0) - // Storage: Nfts Item (r:1 w:1) - // Storage: Nfts CollectionRoleOf (r:1 w:0) - // Storage: System Account (r:1 w:1) - // Storage: Nfts Account (r:0 w:2) - // Storage: Nfts ItemPriceOf (r:0 w:1) - // Storage: Nfts PendingSwapOf (r:0 w:1) + // Proof Size summary in bytes: + // Measured: `647` + // Estimated: `13573` + // Minimum execution time: 45_192 nanoseconds. + Weight::from_parts(45_792_000, 13573) + .saturating_add(RocksDbWeight::get().reads(5_u64)) + .saturating_add(RocksDbWeight::get().writes(7_u64)) + } + /// Storage: Nfts Collection (r:1 w:0) + /// Proof: Nfts Collection (max_values: None, max_size: Some(80), added: 2555, mode: MaxEncodedLen) + /// Storage: Nfts CollectionConfigOf (r:1 w:0) + /// Proof: Nfts CollectionConfigOf (max_values: None, max_size: Some(73), added: 2548, mode: MaxEncodedLen) + /// Storage: Nfts ItemConfigOf (r:1 w:0) + /// Proof: Nfts ItemConfigOf (max_values: None, max_size: Some(48), added: 2523, mode: MaxEncodedLen) + /// Storage: Nfts Item (r:1 w:1) + /// Proof: Nfts Item (max_values: None, max_size: Some(861), added: 3336, mode: MaxEncodedLen) + /// Storage: Nfts CollectionRoleOf (r:1 w:0) + /// Proof: Nfts CollectionRoleOf (max_values: None, max_size: Some(69), added: 2544, mode: MaxEncodedLen) + /// Storage: System Account (r:1 w:1) + /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) + /// Storage: Nfts Account (r:0 w:2) + /// Proof: Nfts Account (max_values: None, max_size: Some(88), added: 2563, mode: MaxEncodedLen) + /// Storage: Nfts ItemPriceOf (r:0 w:1) + /// Proof: Nfts ItemPriceOf (max_values: None, max_size: Some(89), added: 2564, mode: MaxEncodedLen) + /// Storage: Nfts PendingSwapOf (r:0 w:1) + /// Proof: Nfts PendingSwapOf (max_values: None, max_size: Some(71), added: 2546, mode: MaxEncodedLen) fn transfer() -> Weight { - // Minimum execution time: 66_085 nanoseconds. - Weight::from_ref_time(67_065_000) - .saturating_add(RocksDbWeight::get().reads(6)) - .saturating_add(RocksDbWeight::get().writes(6)) - } - // Storage: Nfts Collection (r:1 w:0) - // Storage: Nfts CollectionConfigOf (r:1 w:0) - // Storage: Nfts Item (r:102 w:102) + // Proof Size summary in bytes: + // Measured: `882` + // Estimated: `16109` + // Minimum execution time: 51_962 nanoseconds. + Weight::from_parts(52_367_000, 16109) + .saturating_add(RocksDbWeight::get().reads(6_u64)) + .saturating_add(RocksDbWeight::get().writes(6_u64)) + } + /// Storage: Nfts Collection (r:1 w:0) + /// Proof: Nfts Collection (max_values: None, max_size: Some(80), added: 2555, mode: MaxEncodedLen) + /// Storage: Nfts CollectionConfigOf (r:1 w:0) + /// Proof: Nfts CollectionConfigOf (max_values: None, max_size: Some(73), added: 2548, mode: MaxEncodedLen) + /// Storage: Nfts Item (r:5000 w:5000) + /// Proof: Nfts Item (max_values: None, max_size: Some(861), added: 3336, mode: MaxEncodedLen) /// The range of component `i` is `[0, 5000]`. fn redeposit(i: u32, ) -> Weight { - // Minimum execution time: 25_949 nanoseconds. - Weight::from_ref_time(26_106_000) - // Standard Error: 10_326 - .saturating_add(Weight::from_ref_time(11_496_776).saturating_mul(i.into())) - .saturating_add(RocksDbWeight::get().reads(2)) + // Proof Size summary in bytes: + // Measured: `756 + i * (140 ±0)` + // Estimated: `5103 + i * (3336 ±0)` + // Minimum execution time: 15_512 nanoseconds. + Weight::from_parts(15_731_000, 5103) + // Standard Error: 9_495 + .saturating_add(Weight::from_ref_time(11_462_413).saturating_mul(i.into())) + .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().reads((1_u64).saturating_mul(i.into()))) .saturating_add(RocksDbWeight::get().writes((1_u64).saturating_mul(i.into()))) + .saturating_add(Weight::from_proof_size(3336).saturating_mul(i.into())) } - // Storage: Nfts CollectionRoleOf (r:1 w:0) - // Storage: Nfts ItemConfigOf (r:1 w:1) + /// Storage: Nfts CollectionRoleOf (r:1 w:0) + /// Proof: Nfts CollectionRoleOf (max_values: None, max_size: Some(69), added: 2544, mode: MaxEncodedLen) + /// Storage: Nfts ItemConfigOf (r:1 w:1) + /// Proof: Nfts ItemConfigOf (max_values: None, max_size: Some(48), added: 2523, mode: MaxEncodedLen) fn lock_item_transfer() -> Weight { - // Minimum execution time: 30_080 nanoseconds. - Weight::from_ref_time(30_825_000) - .saturating_add(RocksDbWeight::get().reads(2)) - .saturating_add(RocksDbWeight::get().writes(1)) - } - // Storage: Nfts CollectionRoleOf (r:1 w:0) - // Storage: Nfts ItemConfigOf (r:1 w:1) + // Proof Size summary in bytes: + // Measured: `401` + // Estimated: `5067` + // Minimum execution time: 19_273 nanoseconds. + Weight::from_parts(19_508_000, 5067) + .saturating_add(RocksDbWeight::get().reads(2_u64)) + .saturating_add(RocksDbWeight::get().writes(1_u64)) + } + /// Storage: Nfts CollectionRoleOf (r:1 w:0) + /// Proof: Nfts CollectionRoleOf (max_values: None, max_size: Some(69), added: 2544, mode: MaxEncodedLen) + /// Storage: Nfts ItemConfigOf (r:1 w:1) + /// Proof: Nfts ItemConfigOf (max_values: None, max_size: Some(48), added: 2523, mode: MaxEncodedLen) fn unlock_item_transfer() -> Weight { - // Minimum execution time: 30_612 nanoseconds. - Weight::from_ref_time(31_422_000) - .saturating_add(RocksDbWeight::get().reads(2)) - .saturating_add(RocksDbWeight::get().writes(1)) - } - // Storage: Nfts CollectionRoleOf (r:1 w:0) - // Storage: Nfts CollectionConfigOf (r:1 w:1) + // Proof Size summary in bytes: + // Measured: `401` + // Estimated: `5067` + // Minimum execution time: 19_022 nanoseconds. + Weight::from_parts(19_430_000, 5067) + .saturating_add(RocksDbWeight::get().reads(2_u64)) + .saturating_add(RocksDbWeight::get().writes(1_u64)) + } + /// Storage: Nfts CollectionRoleOf (r:1 w:0) + /// Proof: Nfts CollectionRoleOf (max_values: None, max_size: Some(69), added: 2544, mode: MaxEncodedLen) + /// Storage: Nfts CollectionConfigOf (r:1 w:1) + /// Proof: Nfts CollectionConfigOf (max_values: None, max_size: Some(73), added: 2548, mode: MaxEncodedLen) fn lock_collection() -> Weight { - // Minimum execution time: 27_470 nanoseconds. - Weight::from_ref_time(28_015_000) - .saturating_add(RocksDbWeight::get().reads(2)) - .saturating_add(RocksDbWeight::get().writes(1)) - } - // Storage: Nfts OwnershipAcceptance (r:1 w:1) - // Storage: Nfts Collection (r:1 w:1) - // Storage: Nfts CollectionAccount (r:0 w:2) + // Proof Size summary in bytes: + // Measured: `289` + // Estimated: `5092` + // Minimum execution time: 17_593 nanoseconds. + Weight::from_parts(17_950_000, 5092) + .saturating_add(RocksDbWeight::get().reads(2_u64)) + .saturating_add(RocksDbWeight::get().writes(1_u64)) + } + /// Storage: Nfts OwnershipAcceptance (r:1 w:1) + /// Proof: Nfts OwnershipAcceptance (max_values: None, max_size: Some(52), added: 2527, mode: MaxEncodedLen) + /// Storage: Nfts Collection (r:1 w:1) + /// Proof: Nfts Collection (max_values: None, max_size: Some(80), added: 2555, mode: MaxEncodedLen) + /// Storage: Nfts CollectionAccount (r:0 w:2) + /// Proof: Nfts CollectionAccount (max_values: None, max_size: Some(68), added: 2543, mode: MaxEncodedLen) fn transfer_ownership() -> Weight { - // Minimum execution time: 33_750 nanoseconds. - Weight::from_ref_time(34_139_000) - .saturating_add(RocksDbWeight::get().reads(2)) - .saturating_add(RocksDbWeight::get().writes(4)) - } - // Storage: Nfts Collection (r:1 w:1) - // Storage: Nfts CollectionRoleOf (r:0 w:4) + // Proof Size summary in bytes: + // Measured: `381` + // Estimated: `5082` + // Minimum execution time: 22_068 nanoseconds. + Weight::from_parts(22_235_000, 5082) + .saturating_add(RocksDbWeight::get().reads(2_u64)) + .saturating_add(RocksDbWeight::get().writes(4_u64)) + } + /// Storage: Nfts Collection (r:1 w:1) + /// Proof: Nfts Collection (max_values: None, max_size: Some(80), added: 2555, mode: MaxEncodedLen) + /// Storage: Nfts CollectionRoleOf (r:0 w:4) + /// Proof: Nfts CollectionRoleOf (max_values: None, max_size: Some(69), added: 2544, mode: MaxEncodedLen) fn set_team() -> Weight { - // Minimum execution time: 36_565 nanoseconds. - Weight::from_ref_time(37_464_000) - .saturating_add(RocksDbWeight::get().reads(1)) - .saturating_add(RocksDbWeight::get().writes(5)) - } - // Storage: Nfts Collection (r:1 w:1) - // Storage: Nfts CollectionAccount (r:0 w:2) + // Proof Size summary in bytes: + // Measured: `362` + // Estimated: `2555` + // Minimum execution time: 25_056 nanoseconds. + Weight::from_parts(25_767_000, 2555) + .saturating_add(RocksDbWeight::get().reads(1_u64)) + .saturating_add(RocksDbWeight::get().writes(5_u64)) + } + /// Storage: Nfts Collection (r:1 w:1) + /// Proof: Nfts Collection (max_values: None, max_size: Some(80), added: 2555, mode: MaxEncodedLen) + /// Storage: Nfts CollectionAccount (r:0 w:2) + /// Proof: Nfts CollectionAccount (max_values: None, max_size: Some(68), added: 2543, mode: MaxEncodedLen) fn force_collection_owner() -> Weight { - // Minimum execution time: 29_028 nanoseconds. - Weight::from_ref_time(29_479_000) - .saturating_add(RocksDbWeight::get().reads(1)) - .saturating_add(RocksDbWeight::get().writes(3)) - } - // Storage: Nfts Collection (r:1 w:0) - // Storage: Nfts CollectionConfigOf (r:0 w:1) + // Proof Size summary in bytes: + // Measured: `304` + // Estimated: `2555` + // Minimum execution time: 17_398 nanoseconds. + Weight::from_parts(17_684_000, 2555) + .saturating_add(RocksDbWeight::get().reads(1_u64)) + .saturating_add(RocksDbWeight::get().writes(3_u64)) + } + /// Storage: Nfts Collection (r:1 w:0) + /// Proof: Nfts Collection (max_values: None, max_size: Some(80), added: 2555, mode: MaxEncodedLen) + /// Storage: Nfts CollectionConfigOf (r:0 w:1) + /// Proof: Nfts CollectionConfigOf (max_values: None, max_size: Some(73), added: 2548, mode: MaxEncodedLen) fn force_collection_config() -> Weight { - // Minimum execution time: 24_695 nanoseconds. - Weight::from_ref_time(25_304_000) - .saturating_add(RocksDbWeight::get().reads(1)) - .saturating_add(RocksDbWeight::get().writes(1)) - } - // Storage: Nfts Collection (r:1 w:0) - // Storage: Nfts ItemConfigOf (r:1 w:1) + // Proof Size summary in bytes: + // Measured: `242` + // Estimated: `2555` + // Minimum execution time: 14_054 nanoseconds. + Weight::from_parts(14_243_000, 2555) + .saturating_add(RocksDbWeight::get().reads(1_u64)) + .saturating_add(RocksDbWeight::get().writes(1_u64)) + } + /// Storage: Nfts Collection (r:1 w:0) + /// Proof: Nfts Collection (max_values: None, max_size: Some(80), added: 2555, mode: MaxEncodedLen) + /// Storage: Nfts ItemConfigOf (r:1 w:1) + /// Proof: Nfts ItemConfigOf (max_values: None, max_size: Some(48), added: 2523, mode: MaxEncodedLen) fn lock_item_properties() -> Weight { - // Minimum execution time: 28_910 nanoseconds. - Weight::from_ref_time(29_186_000) - .saturating_add(RocksDbWeight::get().reads(2)) - .saturating_add(RocksDbWeight::get().writes(1)) - } - // Storage: Nfts Collection (r:1 w:1) - // Storage: Nfts CollectionConfigOf (r:1 w:0) - // Storage: Nfts ItemConfigOf (r:1 w:0) - // Storage: Nfts Attribute (r:1 w:1) + // Proof Size summary in bytes: + // Measured: `445` + // Estimated: `5078` + // Minimum execution time: 17_662 nanoseconds. + Weight::from_parts(18_073_000, 5078) + .saturating_add(RocksDbWeight::get().reads(2_u64)) + .saturating_add(RocksDbWeight::get().writes(1_u64)) + } + /// Storage: Nfts Collection (r:1 w:1) + /// Proof: Nfts Collection (max_values: None, max_size: Some(80), added: 2555, mode: MaxEncodedLen) + /// Storage: Nfts CollectionConfigOf (r:1 w:0) + /// Proof: Nfts CollectionConfigOf (max_values: None, max_size: Some(73), added: 2548, mode: MaxEncodedLen) + /// Storage: Nfts ItemConfigOf (r:1 w:0) + /// Proof: Nfts ItemConfigOf (max_values: None, max_size: Some(48), added: 2523, mode: MaxEncodedLen) + /// Storage: Nfts Attribute (r:1 w:1) + /// Proof: Nfts Attribute (max_values: None, max_size: Some(446), added: 2921, mode: MaxEncodedLen) fn set_attribute() -> Weight { - // Minimum execution time: 56_407 nanoseconds. - Weight::from_ref_time(58_176_000) - .saturating_add(RocksDbWeight::get().reads(4)) - .saturating_add(RocksDbWeight::get().writes(2)) - } - // Storage: Nfts Collection (r:1 w:1) - // Storage: Nfts Attribute (r:1 w:1) + // Proof Size summary in bytes: + // Measured: `474` + // Estimated: `10547` + // Minimum execution time: 40_098 nanoseconds. + Weight::from_parts(40_649_000, 10547) + .saturating_add(RocksDbWeight::get().reads(4_u64)) + .saturating_add(RocksDbWeight::get().writes(2_u64)) + } + /// Storage: Nfts Collection (r:1 w:1) + /// Proof: Nfts Collection (max_values: None, max_size: Some(80), added: 2555, mode: MaxEncodedLen) + /// Storage: Nfts Attribute (r:1 w:1) + /// Proof: Nfts Attribute (max_values: None, max_size: Some(446), added: 2921, mode: MaxEncodedLen) fn force_set_attribute() -> Weight { - // Minimum execution time: 36_402 nanoseconds. - Weight::from_ref_time(37_034_000) - .saturating_add(RocksDbWeight::get().reads(2)) - .saturating_add(RocksDbWeight::get().writes(2)) - } - // Storage: Nfts Attribute (r:1 w:1) - // Storage: Nfts Collection (r:1 w:1) - // Storage: Nfts ItemConfigOf (r:1 w:0) + // Proof Size summary in bytes: + // Measured: `337` + // Estimated: `5476` + // Minimum execution time: 25_178 nanoseconds. + Weight::from_parts(25_473_000, 5476) + .saturating_add(RocksDbWeight::get().reads(2_u64)) + .saturating_add(RocksDbWeight::get().writes(2_u64)) + } + /// Storage: Nfts Attribute (r:1 w:1) + /// Proof: Nfts Attribute (max_values: None, max_size: Some(446), added: 2921, mode: MaxEncodedLen) + /// Storage: Nfts Collection (r:1 w:1) + /// Proof: Nfts Collection (max_values: None, max_size: Some(80), added: 2555, mode: MaxEncodedLen) + /// Storage: Nfts ItemConfigOf (r:1 w:0) + /// Proof: Nfts ItemConfigOf (max_values: None, max_size: Some(48), added: 2523, mode: MaxEncodedLen) fn clear_attribute() -> Weight { - // Minimum execution time: 52_022 nanoseconds. - Weight::from_ref_time(54_059_000) - .saturating_add(RocksDbWeight::get().reads(3)) - .saturating_add(RocksDbWeight::get().writes(2)) - } - // Storage: Nfts Item (r:1 w:0) - // Storage: Nfts ItemAttributesApprovalsOf (r:1 w:1) + // Proof Size summary in bytes: + // Measured: `916` + // Estimated: `7999` + // Minimum execution time: 35_202 nanoseconds. + Weight::from_parts(35_518_000, 7999) + .saturating_add(RocksDbWeight::get().reads(3_u64)) + .saturating_add(RocksDbWeight::get().writes(2_u64)) + } + /// Storage: Nfts Item (r:1 w:0) + /// Proof: Nfts Item (max_values: None, max_size: Some(861), added: 3336, mode: MaxEncodedLen) + /// Storage: Nfts ItemAttributesApprovalsOf (r:1 w:1) + /// Proof: Nfts ItemAttributesApprovalsOf (max_values: None, max_size: Some(681), added: 3156, mode: MaxEncodedLen) fn approve_item_attributes() -> Weight { - // Minimum execution time: 28_475 nanoseconds. - Weight::from_ref_time(29_162_000) - .saturating_add(RocksDbWeight::get().reads(2)) - .saturating_add(RocksDbWeight::get().writes(1)) - } - // Storage: Nfts Item (r:1 w:0) - // Storage: Nfts ItemAttributesApprovalsOf (r:1 w:1) - // Storage: Nfts Attribute (r:1 w:0) - // Storage: System Account (r:1 w:1) + // Proof Size summary in bytes: + // Measured: `379` + // Estimated: `6492` + // Minimum execution time: 17_260 nanoseconds. + Weight::from_parts(17_498_000, 6492) + .saturating_add(RocksDbWeight::get().reads(2_u64)) + .saturating_add(RocksDbWeight::get().writes(1_u64)) + } + /// Storage: Nfts Item (r:1 w:0) + /// Proof: Nfts Item (max_values: None, max_size: Some(861), added: 3336, mode: MaxEncodedLen) + /// Storage: Nfts ItemAttributesApprovalsOf (r:1 w:1) + /// Proof: Nfts ItemAttributesApprovalsOf (max_values: None, max_size: Some(681), added: 3156, mode: MaxEncodedLen) + /// Storage: Nfts Attribute (r:1001 w:1000) + /// Proof: Nfts Attribute (max_values: None, max_size: Some(446), added: 2921, mode: MaxEncodedLen) + /// Storage: System Account (r:1 w:1) + /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) /// The range of component `n` is `[0, 1000]`. fn cancel_item_attributes_approval(n: u32, ) -> Weight { - // Minimum execution time: 37_529 nanoseconds. - Weight::from_ref_time(38_023_000) - // Standard Error: 8_136 - .saturating_add(Weight::from_ref_time(7_452_872).saturating_mul(n.into())) - .saturating_add(RocksDbWeight::get().reads(4)) + // Proof Size summary in bytes: + // Measured: `865 + n * (367 ±0)` + // Estimated: `12016 + n * (2921 ±0)` + // Minimum execution time: 25_579 nanoseconds. + Weight::from_parts(25_846_000, 12016) + // Standard Error: 7_759 + .saturating_add(Weight::from_ref_time(7_159_200).saturating_mul(n.into())) + .saturating_add(RocksDbWeight::get().reads(4_u64)) .saturating_add(RocksDbWeight::get().reads((1_u64).saturating_mul(n.into()))) - .saturating_add(RocksDbWeight::get().writes(2)) + .saturating_add(RocksDbWeight::get().writes(2_u64)) .saturating_add(RocksDbWeight::get().writes((1_u64).saturating_mul(n.into()))) - } - // Storage: Nfts Collection (r:1 w:1) - // Storage: Nfts ItemConfigOf (r:1 w:0) - // Storage: Nfts CollectionConfigOf (r:1 w:0) - // Storage: Nfts ItemMetadataOf (r:1 w:1) + .saturating_add(Weight::from_proof_size(2921).saturating_mul(n.into())) + } + /// Storage: Nfts Collection (r:1 w:1) + /// Proof: Nfts Collection (max_values: None, max_size: Some(80), added: 2555, mode: MaxEncodedLen) + /// Storage: Nfts ItemConfigOf (r:1 w:0) + /// Proof: Nfts ItemConfigOf (max_values: None, max_size: Some(48), added: 2523, mode: MaxEncodedLen) + /// Storage: Nfts CollectionConfigOf (r:1 w:0) + /// Proof: Nfts CollectionConfigOf (max_values: None, max_size: Some(73), added: 2548, mode: MaxEncodedLen) + /// Storage: Nfts ItemMetadataOf (r:1 w:1) + /// Proof: Nfts ItemMetadataOf (max_values: None, max_size: Some(140), added: 2615, mode: MaxEncodedLen) fn set_metadata() -> Weight { - // Minimum execution time: 49_300 nanoseconds. - Weight::from_ref_time(49_790_000) - .saturating_add(RocksDbWeight::get().reads(4)) - .saturating_add(RocksDbWeight::get().writes(2)) - } - // Storage: Nfts Collection (r:1 w:1) - // Storage: Nfts ItemConfigOf (r:1 w:0) - // Storage: Nfts ItemMetadataOf (r:1 w:1) + // Proof Size summary in bytes: + // Measured: `474` + // Estimated: `10241` + // Minimum execution time: 33_285 nanoseconds. + Weight::from_parts(33_692_000, 10241) + .saturating_add(RocksDbWeight::get().reads(4_u64)) + .saturating_add(RocksDbWeight::get().writes(2_u64)) + } + /// Storage: Nfts ItemMetadataOf (r:1 w:1) + /// Proof: Nfts ItemMetadataOf (max_values: None, max_size: Some(140), added: 2615, mode: MaxEncodedLen) + /// Storage: Nfts Collection (r:1 w:1) + /// Proof: Nfts Collection (max_values: None, max_size: Some(80), added: 2555, mode: MaxEncodedLen) + /// Storage: Nfts ItemConfigOf (r:1 w:0) + /// Proof: Nfts ItemConfigOf (max_values: None, max_size: Some(48), added: 2523, mode: MaxEncodedLen) fn clear_metadata() -> Weight { - // Minimum execution time: 47_248 nanoseconds. - Weight::from_ref_time(48_094_000) - .saturating_add(RocksDbWeight::get().reads(3)) - .saturating_add(RocksDbWeight::get().writes(2)) - } - // Storage: Nfts CollectionConfigOf (r:1 w:0) - // Storage: Nfts Collection (r:1 w:1) - // Storage: Nfts CollectionMetadataOf (r:1 w:1) + // Proof Size summary in bytes: + // Measured: `609` + // Estimated: `7693` + // Minimum execution time: 30_670 nanoseconds. + Weight::from_parts(31_282_000, 7693) + .saturating_add(RocksDbWeight::get().reads(3_u64)) + .saturating_add(RocksDbWeight::get().writes(2_u64)) + } + /// Storage: Nfts CollectionConfigOf (r:1 w:0) + /// Proof: Nfts CollectionConfigOf (max_values: None, max_size: Some(73), added: 2548, mode: MaxEncodedLen) + /// Storage: Nfts Collection (r:1 w:1) + /// Proof: Nfts Collection (max_values: None, max_size: Some(80), added: 2555, mode: MaxEncodedLen) + /// Storage: Nfts CollectionMetadataOf (r:1 w:1) + /// Proof: Nfts CollectionMetadataOf (max_values: None, max_size: Some(87), added: 2562, mode: MaxEncodedLen) fn set_collection_metadata() -> Weight { - // Minimum execution time: 44_137 nanoseconds. - Weight::from_ref_time(44_905_000) - .saturating_add(RocksDbWeight::get().reads(3)) - .saturating_add(RocksDbWeight::get().writes(2)) - } - // Storage: Nfts Collection (r:1 w:0) - // Storage: Nfts CollectionConfigOf (r:1 w:0) - // Storage: Nfts CollectionMetadataOf (r:1 w:1) + // Proof Size summary in bytes: + // Measured: `333` + // Estimated: `7665` + // Minimum execution time: 28_313 nanoseconds. + Weight::from_parts(28_724_000, 7665) + .saturating_add(RocksDbWeight::get().reads(3_u64)) + .saturating_add(RocksDbWeight::get().writes(2_u64)) + } + /// Storage: Nfts Collection (r:1 w:0) + /// Proof: Nfts Collection (max_values: None, max_size: Some(80), added: 2555, mode: MaxEncodedLen) + /// Storage: Nfts CollectionConfigOf (r:1 w:0) + /// Proof: Nfts CollectionConfigOf (max_values: None, max_size: Some(73), added: 2548, mode: MaxEncodedLen) + /// Storage: Nfts CollectionMetadataOf (r:1 w:1) + /// Proof: Nfts CollectionMetadataOf (max_values: None, max_size: Some(87), added: 2562, mode: MaxEncodedLen) fn clear_collection_metadata() -> Weight { - // Minimum execution time: 43_005 nanoseconds. - Weight::from_ref_time(43_898_000) - .saturating_add(RocksDbWeight::get().reads(3)) - .saturating_add(RocksDbWeight::get().writes(1)) - } - // Storage: Nfts Item (r:1 w:1) - // Storage: Nfts CollectionConfigOf (r:1 w:0) - // Storage: Nfts CollectionRoleOf (r:1 w:0) + // Proof Size summary in bytes: + // Measured: `476` + // Estimated: `7665` + // Minimum execution time: 27_034 nanoseconds. + Weight::from_parts(27_655_000, 7665) + .saturating_add(RocksDbWeight::get().reads(3_u64)) + .saturating_add(RocksDbWeight::get().writes(1_u64)) + } + /// Storage: Nfts Item (r:1 w:1) + /// Proof: Nfts Item (max_values: None, max_size: Some(861), added: 3336, mode: MaxEncodedLen) + /// Storage: Nfts CollectionConfigOf (r:1 w:0) + /// Proof: Nfts CollectionConfigOf (max_values: None, max_size: Some(73), added: 2548, mode: MaxEncodedLen) + /// Storage: Nfts CollectionRoleOf (r:1 w:0) + /// Proof: Nfts CollectionRoleOf (max_values: None, max_size: Some(69), added: 2544, mode: MaxEncodedLen) fn approve_transfer() -> Weight { - // Minimum execution time: 36_344 nanoseconds. - Weight::from_ref_time(36_954_000) - .saturating_add(RocksDbWeight::get().reads(3)) - .saturating_add(RocksDbWeight::get().writes(1)) - } - // Storage: Nfts Item (r:1 w:1) - // Storage: Nfts CollectionRoleOf (r:1 w:0) + // Proof Size summary in bytes: + // Measured: `466` + // Estimated: `8428` + // Minimum execution time: 23_408 nanoseconds. + Weight::from_parts(23_916_000, 8428) + .saturating_add(RocksDbWeight::get().reads(3_u64)) + .saturating_add(RocksDbWeight::get().writes(1_u64)) + } + /// Storage: Nfts Item (r:1 w:1) + /// Proof: Nfts Item (max_values: None, max_size: Some(861), added: 3336, mode: MaxEncodedLen) + /// Storage: Nfts CollectionRoleOf (r:1 w:0) + /// Proof: Nfts CollectionRoleOf (max_values: None, max_size: Some(69), added: 2544, mode: MaxEncodedLen) fn cancel_approval() -> Weight { - // Minimum execution time: 32_418 nanoseconds. - Weight::from_ref_time(33_029_000) - .saturating_add(RocksDbWeight::get().reads(2)) - .saturating_add(RocksDbWeight::get().writes(1)) - } - // Storage: Nfts Item (r:1 w:1) - // Storage: Nfts CollectionRoleOf (r:1 w:0) + // Proof Size summary in bytes: + // Measured: `474` + // Estimated: `5880` + // Minimum execution time: 21_177 nanoseconds. + Weight::from_parts(21_492_000, 5880) + .saturating_add(RocksDbWeight::get().reads(2_u64)) + .saturating_add(RocksDbWeight::get().writes(1_u64)) + } + /// Storage: Nfts Item (r:1 w:1) + /// Proof: Nfts Item (max_values: None, max_size: Some(861), added: 3336, mode: MaxEncodedLen) + /// Storage: Nfts CollectionRoleOf (r:1 w:0) + /// Proof: Nfts CollectionRoleOf (max_values: None, max_size: Some(69), added: 2544, mode: MaxEncodedLen) fn clear_all_transfer_approvals() -> Weight { - // Minimum execution time: 31_448 nanoseconds. - Weight::from_ref_time(31_979_000) - .saturating_add(RocksDbWeight::get().reads(2)) - .saturating_add(RocksDbWeight::get().writes(1)) - } - // Storage: Nfts OwnershipAcceptance (r:1 w:1) + // Proof Size summary in bytes: + // Measured: `474` + // Estimated: `5880` + // Minimum execution time: 20_279 nanoseconds. + Weight::from_parts(20_919_000, 5880) + .saturating_add(RocksDbWeight::get().reads(2_u64)) + .saturating_add(RocksDbWeight::get().writes(1_u64)) + } + /// Storage: Nfts OwnershipAcceptance (r:1 w:1) + /// Proof: Nfts OwnershipAcceptance (max_values: None, max_size: Some(52), added: 2527, mode: MaxEncodedLen) fn set_accept_ownership() -> Weight { - // Minimum execution time: 27_487 nanoseconds. - Weight::from_ref_time(28_080_000) - .saturating_add(RocksDbWeight::get().reads(1)) - .saturating_add(RocksDbWeight::get().writes(1)) - } - // Storage: Nfts CollectionConfigOf (r:1 w:1) - // Storage: Nfts Collection (r:1 w:0) + // Proof Size summary in bytes: + // Measured: `42` + // Estimated: `2527` + // Minimum execution time: 14_921 nanoseconds. + Weight::from_parts(15_382_000, 2527) + .saturating_add(RocksDbWeight::get().reads(1_u64)) + .saturating_add(RocksDbWeight::get().writes(1_u64)) + } + /// Storage: Nfts CollectionConfigOf (r:1 w:1) + /// Proof: Nfts CollectionConfigOf (max_values: None, max_size: Some(73), added: 2548, mode: MaxEncodedLen) + /// Storage: Nfts Collection (r:1 w:0) + /// Proof: Nfts Collection (max_values: None, max_size: Some(80), added: 2555, mode: MaxEncodedLen) fn set_collection_max_supply() -> Weight { - // Minimum execution time: 28_235 nanoseconds. - Weight::from_ref_time(28_967_000) - .saturating_add(RocksDbWeight::get().reads(2)) - .saturating_add(RocksDbWeight::get().writes(1)) - } - // Storage: Nfts Collection (r:1 w:0) - // Storage: Nfts CollectionConfigOf (r:1 w:1) + // Proof Size summary in bytes: + // Measured: `333` + // Estimated: `5103` + // Minimum execution time: 18_201 nanoseconds. + Weight::from_parts(18_628_000, 5103) + .saturating_add(RocksDbWeight::get().reads(2_u64)) + .saturating_add(RocksDbWeight::get().writes(1_u64)) + } + /// Storage: Nfts Collection (r:1 w:0) + /// Proof: Nfts Collection (max_values: None, max_size: Some(80), added: 2555, mode: MaxEncodedLen) + /// Storage: Nfts CollectionConfigOf (r:1 w:1) + /// Proof: Nfts CollectionConfigOf (max_values: None, max_size: Some(73), added: 2548, mode: MaxEncodedLen) fn update_mint_settings() -> Weight { - // Minimum execution time: 28_172 nanoseconds. - Weight::from_ref_time(28_636_000) - .saturating_add(RocksDbWeight::get().reads(2)) - .saturating_add(RocksDbWeight::get().writes(1)) - } - // Storage: Nfts Item (r:1 w:0) - // Storage: Nfts CollectionConfigOf (r:1 w:0) - // Storage: Nfts ItemConfigOf (r:1 w:0) - // Storage: Nfts ItemPriceOf (r:0 w:1) + // Proof Size summary in bytes: + // Measured: `333` + // Estimated: `5103` + // Minimum execution time: 16_870 nanoseconds. + Weight::from_parts(17_318_000, 5103) + .saturating_add(RocksDbWeight::get().reads(2_u64)) + .saturating_add(RocksDbWeight::get().writes(1_u64)) + } + /// Storage: Nfts Item (r:1 w:0) + /// Proof: Nfts Item (max_values: None, max_size: Some(861), added: 3336, mode: MaxEncodedLen) + /// Storage: Nfts CollectionConfigOf (r:1 w:0) + /// Proof: Nfts CollectionConfigOf (max_values: None, max_size: Some(73), added: 2548, mode: MaxEncodedLen) + /// Storage: Nfts ItemConfigOf (r:1 w:0) + /// Proof: Nfts ItemConfigOf (max_values: None, max_size: Some(48), added: 2523, mode: MaxEncodedLen) + /// Storage: Nfts ItemPriceOf (r:0 w:1) + /// Proof: Nfts ItemPriceOf (max_values: None, max_size: Some(89), added: 2564, mode: MaxEncodedLen) fn set_price() -> Weight { - // Minimum execution time: 35_336 nanoseconds. - Weight::from_ref_time(36_026_000) - .saturating_add(RocksDbWeight::get().reads(3)) - .saturating_add(RocksDbWeight::get().writes(1)) - } - // Storage: Nfts Item (r:1 w:1) - // Storage: Nfts ItemPriceOf (r:1 w:1) - // Storage: Nfts Collection (r:1 w:0) - // Storage: Nfts CollectionConfigOf (r:1 w:0) - // Storage: Nfts ItemConfigOf (r:1 w:0) - // Storage: System Account (r:1 w:1) - // Storage: Nfts Account (r:0 w:2) - // Storage: Nfts PendingSwapOf (r:0 w:1) + // Proof Size summary in bytes: + // Measured: `516` + // Estimated: `8407` + // Minimum execution time: 22_604 nanoseconds. + Weight::from_parts(22_867_000, 8407) + .saturating_add(RocksDbWeight::get().reads(3_u64)) + .saturating_add(RocksDbWeight::get().writes(1_u64)) + } + /// Storage: Nfts Item (r:1 w:1) + /// Proof: Nfts Item (max_values: None, max_size: Some(861), added: 3336, mode: MaxEncodedLen) + /// Storage: Nfts ItemPriceOf (r:1 w:1) + /// Proof: Nfts ItemPriceOf (max_values: None, max_size: Some(89), added: 2564, mode: MaxEncodedLen) + /// Storage: Nfts Collection (r:1 w:0) + /// Proof: Nfts Collection (max_values: None, max_size: Some(80), added: 2555, mode: MaxEncodedLen) + /// Storage: Nfts CollectionConfigOf (r:1 w:0) + /// Proof: Nfts CollectionConfigOf (max_values: None, max_size: Some(73), added: 2548, mode: MaxEncodedLen) + /// Storage: Nfts ItemConfigOf (r:1 w:0) + /// Proof: Nfts ItemConfigOf (max_values: None, max_size: Some(48), added: 2523, mode: MaxEncodedLen) + /// Storage: System Account (r:1 w:1) + /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) + /// Storage: Nfts Account (r:0 w:2) + /// Proof: Nfts Account (max_values: None, max_size: Some(88), added: 2563, mode: MaxEncodedLen) + /// Storage: Nfts PendingSwapOf (r:0 w:1) + /// Proof: Nfts PendingSwapOf (max_values: None, max_size: Some(71), added: 2546, mode: MaxEncodedLen) fn buy_item() -> Weight { - // Minimum execution time: 70_971 nanoseconds. - Weight::from_ref_time(72_036_000) - .saturating_add(RocksDbWeight::get().reads(6)) - .saturating_add(RocksDbWeight::get().writes(6)) + // Proof Size summary in bytes: + // Measured: `934` + // Estimated: `16129` + // Minimum execution time: 56_849 nanoseconds. + Weight::from_parts(57_336_000, 16129) + .saturating_add(RocksDbWeight::get().reads(6_u64)) + .saturating_add(RocksDbWeight::get().writes(6_u64)) } /// The range of component `n` is `[0, 10]`. fn pay_tips(n: u32, ) -> Weight { - // Minimum execution time: 5_151 nanoseconds. - Weight::from_ref_time(11_822_888) - // Standard Error: 38_439 - .saturating_add(Weight::from_ref_time(3_511_844).saturating_mul(n.into())) - } - // Storage: Nfts Item (r:2 w:0) - // Storage: Nfts PendingSwapOf (r:0 w:1) + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 2_308 nanoseconds. + Weight::from_ref_time(4_805_401) + // Standard Error: 13_875 + .saturating_add(Weight::from_ref_time(3_167_190).saturating_mul(n.into())) + } + /// Storage: Nfts Item (r:2 w:0) + /// Proof: Nfts Item (max_values: None, max_size: Some(861), added: 3336, mode: MaxEncodedLen) + /// Storage: Nfts PendingSwapOf (r:0 w:1) + /// Proof: Nfts PendingSwapOf (max_values: None, max_size: Some(71), added: 2546, mode: MaxEncodedLen) fn create_swap() -> Weight { - // Minimum execution time: 33_027 nanoseconds. - Weight::from_ref_time(33_628_000) - .saturating_add(RocksDbWeight::get().reads(2)) - .saturating_add(RocksDbWeight::get().writes(1)) - } - // Storage: Nfts PendingSwapOf (r:1 w:1) - // Storage: Nfts Item (r:1 w:0) + // Proof Size summary in bytes: + // Measured: `524` + // Estimated: `6672` + // Minimum execution time: 20_395 nanoseconds. + Weight::from_parts(20_716_000, 6672) + .saturating_add(RocksDbWeight::get().reads(2_u64)) + .saturating_add(RocksDbWeight::get().writes(1_u64)) + } + /// Storage: Nfts PendingSwapOf (r:1 w:1) + /// Proof: Nfts PendingSwapOf (max_values: None, max_size: Some(71), added: 2546, mode: MaxEncodedLen) + /// Storage: Nfts Item (r:1 w:0) + /// Proof: Nfts Item (max_values: None, max_size: Some(861), added: 3336, mode: MaxEncodedLen) fn cancel_swap() -> Weight { - // Minimum execution time: 35_890 nanoseconds. - Weight::from_ref_time(36_508_000) - .saturating_add(RocksDbWeight::get().reads(2)) - .saturating_add(RocksDbWeight::get().writes(1)) - } - // Storage: Nfts Item (r:2 w:2) - // Storage: Nfts PendingSwapOf (r:1 w:2) - // Storage: Nfts Collection (r:1 w:0) - // Storage: Nfts CollectionConfigOf (r:1 w:0) - // Storage: Nfts ItemConfigOf (r:2 w:0) - // Storage: System Account (r:1 w:1) - // Storage: Nfts Account (r:0 w:4) - // Storage: Nfts ItemPriceOf (r:0 w:2) + // Proof Size summary in bytes: + // Measured: `511` + // Estimated: `5882` + // Minimum execution time: 19_936 nanoseconds. + Weight::from_parts(20_344_000, 5882) + .saturating_add(RocksDbWeight::get().reads(2_u64)) + .saturating_add(RocksDbWeight::get().writes(1_u64)) + } + /// Storage: Nfts Item (r:2 w:2) + /// Proof: Nfts Item (max_values: None, max_size: Some(861), added: 3336, mode: MaxEncodedLen) + /// Storage: Nfts PendingSwapOf (r:1 w:2) + /// Proof: Nfts PendingSwapOf (max_values: None, max_size: Some(71), added: 2546, mode: MaxEncodedLen) + /// Storage: Nfts Collection (r:1 w:0) + /// Proof: Nfts Collection (max_values: None, max_size: Some(80), added: 2555, mode: MaxEncodedLen) + /// Storage: Nfts CollectionConfigOf (r:1 w:0) + /// Proof: Nfts CollectionConfigOf (max_values: None, max_size: Some(73), added: 2548, mode: MaxEncodedLen) + /// Storage: Nfts ItemConfigOf (r:2 w:0) + /// Proof: Nfts ItemConfigOf (max_values: None, max_size: Some(48), added: 2523, mode: MaxEncodedLen) + /// Storage: System Account (r:1 w:1) + /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) + /// Storage: Nfts Account (r:0 w:4) + /// Proof: Nfts Account (max_values: None, max_size: Some(88), added: 2563, mode: MaxEncodedLen) + /// Storage: Nfts ItemPriceOf (r:0 w:2) + /// Proof: Nfts ItemPriceOf (max_values: None, max_size: Some(89), added: 2564, mode: MaxEncodedLen) fn claim_swap() -> Weight { - // Minimum execution time: 101_076 nanoseconds. - Weight::from_ref_time(101_863_000) - .saturating_add(RocksDbWeight::get().reads(8)) - .saturating_add(RocksDbWeight::get().writes(11)) + // Proof Size summary in bytes: + // Measured: `1097` + // Estimated: `21970` + // Minimum execution time: 80_884 nanoseconds. + Weight::from_parts(81_643_000, 21970) + .saturating_add(RocksDbWeight::get().reads(8_u64)) + .saturating_add(RocksDbWeight::get().writes(11_u64)) } } diff --git a/frame/nis/src/weights.rs b/frame/nis/src/weights.rs index 769ff7955..07221f358 100644 --- a/frame/nis/src/weights.rs +++ b/frame/nis/src/weights.rs @@ -1,31 +1,43 @@ +// This file is part of Substrate. + +// Copyright (C) 2023 Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. //! Autogenerated weights for pallet_nis //! //! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev -//! DATE: 2022-12-15, STEPS: `5`, REPEAT: 2, LOW RANGE: `[]`, HIGH RANGE: `[]` -//! HOSTNAME: `Workhorse.local`, CPU: `` +//! DATE: 2023-01-24, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! WORST CASE MAP SIZE: `1000000` +//! HOSTNAME: `bm2`, CPU: `Intel(R) Core(TM) i7-7700K CPU @ 4.20GHz` //! EXECUTION: Some(Wasm), WASM-EXECUTION: Compiled, CHAIN: Some("dev"), DB CACHE: 1024 // Executed Command: -// ../../../target/release/substrate +// ./target/production/substrate // benchmark // pallet -// --chain -// dev -// --steps -// 5 -// --repeat -// 2 -// --pallet -// pallet_nis -// --extrinsic -// * +// --chain=dev +// --steps=50 +// --repeat=20 +// --pallet=pallet_nis +// --extrinsic=* // --execution=wasm // --wasm-execution=compiled -// --output -// ../../../frame/nis/src/weights.rs -// --template -// ../../../.maintain/frame-weight-template.hbs +// --heap-pages=4096 +// --output=./frame/nis/src/weights.rs +// --header=./HEADER-APACHE2 +// --template=./.maintain/frame-weight-template.hbs #![cfg_attr(rustfmt, rustfmt_skip)] #![allow(unused_parens)] @@ -52,224 +64,364 @@ pub trait WeightInfo { /// Weights for pallet_nis using the Substrate node and recommended hardware. pub struct SubstrateWeight(PhantomData); impl WeightInfo for SubstrateWeight { - // Storage: Nis Queues (r:1 w:1) - // Storage: Balances Reserves (r:1 w:1) - // Storage: Nis QueueTotals (r:1 w:1) + /// Storage: Nis Queues (r:1 w:1) + /// Proof: Nis Queues (max_values: None, max_size: Some(48022), added: 50497, mode: MaxEncodedLen) + /// Storage: Balances Reserves (r:1 w:1) + /// Proof: Balances Reserves (max_values: None, max_size: Some(1249), added: 3724, mode: MaxEncodedLen) + /// Storage: Nis QueueTotals (r:1 w:1) + /// Proof: Nis QueueTotals (max_values: Some(1), max_size: Some(6002), added: 6497, mode: MaxEncodedLen) /// The range of component `l` is `[0, 999]`. fn place_bid(l: u32, ) -> Weight { - // Minimum execution time: 47_000 nanoseconds. - Weight::from_ref_time(53_822_030) - // Standard Error: 4_869 - .saturating_add(Weight::from_ref_time(47_431).saturating_mul(l.into())) - .saturating_add(T::DbWeight::get().reads(3)) - .saturating_add(T::DbWeight::get().writes(3)) + // Proof Size summary in bytes: + // Measured: `6245 + l * (48 ±0)` + // Estimated: `60718` + // Minimum execution time: 28_814 nanoseconds. + Weight::from_parts(35_245_917, 60718) + // Standard Error: 189 + .saturating_add(Weight::from_ref_time(45_322).saturating_mul(l.into())) + .saturating_add(T::DbWeight::get().reads(3_u64)) + .saturating_add(T::DbWeight::get().writes(3_u64)) } - // Storage: Nis Queues (r:1 w:1) - // Storage: Balances Reserves (r:1 w:1) - // Storage: Nis QueueTotals (r:1 w:1) + /// Storage: Nis Queues (r:1 w:1) + /// Proof: Nis Queues (max_values: None, max_size: Some(48022), added: 50497, mode: MaxEncodedLen) + /// Storage: Balances Reserves (r:1 w:1) + /// Proof: Balances Reserves (max_values: None, max_size: Some(1249), added: 3724, mode: MaxEncodedLen) + /// Storage: Nis QueueTotals (r:1 w:1) + /// Proof: Nis QueueTotals (max_values: Some(1), max_size: Some(6002), added: 6497, mode: MaxEncodedLen) fn place_bid_max() -> Weight { - // Minimum execution time: 109_000 nanoseconds. - Weight::from_ref_time(109_000_000) - .saturating_add(T::DbWeight::get().reads(3)) - .saturating_add(T::DbWeight::get().writes(3)) + // Proof Size summary in bytes: + // Measured: `54247` + // Estimated: `60718` + // Minimum execution time: 80_332 nanoseconds. + Weight::from_parts(81_050_000, 60718) + .saturating_add(T::DbWeight::get().reads(3_u64)) + .saturating_add(T::DbWeight::get().writes(3_u64)) } - // Storage: Nis Queues (r:1 w:1) - // Storage: Nis QueueTotals (r:1 w:1) - // Storage: Balances Reserves (r:1 w:1) + /// Storage: Nis Queues (r:1 w:1) + /// Proof: Nis Queues (max_values: None, max_size: Some(48022), added: 50497, mode: MaxEncodedLen) + /// Storage: Nis QueueTotals (r:1 w:1) + /// Proof: Nis QueueTotals (max_values: Some(1), max_size: Some(6002), added: 6497, mode: MaxEncodedLen) + /// Storage: Balances Reserves (r:1 w:1) + /// Proof: Balances Reserves (max_values: None, max_size: Some(1249), added: 3724, mode: MaxEncodedLen) /// The range of component `l` is `[1, 1000]`. fn retract_bid(l: u32, ) -> Weight { - // Minimum execution time: 50_000 nanoseconds. - Weight::from_ref_time(54_479_879) - // Standard Error: 4_891 - .saturating_add(Weight::from_ref_time(38_224).saturating_mul(l.into())) - .saturating_add(T::DbWeight::get().reads(3)) - .saturating_add(T::DbWeight::get().writes(3)) + // Proof Size summary in bytes: + // Measured: `6245 + l * (48 ±0)` + // Estimated: `60718` + // Minimum execution time: 34_426 nanoseconds. + Weight::from_parts(36_434_166, 60718) + // Standard Error: 135 + .saturating_add(Weight::from_ref_time(33_923).saturating_mul(l.into())) + .saturating_add(T::DbWeight::get().reads(3_u64)) + .saturating_add(T::DbWeight::get().writes(3_u64)) } - // Storage: Nis Summary (r:1 w:0) - // Storage: System Account (r:1 w:1) + /// Storage: Nis Summary (r:1 w:0) + /// Proof: Nis Summary (max_values: Some(1), max_size: Some(40), added: 535, mode: MaxEncodedLen) + /// Storage: System Account (r:1 w:1) + /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) fn fund_deficit() -> Weight { - // Minimum execution time: 57_000 nanoseconds. - Weight::from_ref_time(62_000_000) - .saturating_add(T::DbWeight::get().reads(2)) - .saturating_add(T::DbWeight::get().writes(1)) + // Proof Size summary in bytes: + // Measured: `222` + // Estimated: `3138` + // Minimum execution time: 32_566 nanoseconds. + Weight::from_parts(32_880_000, 3138) + .saturating_add(T::DbWeight::get().reads(2_u64)) + .saturating_add(T::DbWeight::get().writes(1_u64)) } - // Storage: Nis Receipts (r:1 w:1) - // Storage: Nis Summary (r:1 w:1) - // Storage: System Account (r:1 w:0) - // Storage: Balances Reserves (r:1 w:1) + /// Storage: Nis Receipts (r:1 w:1) + /// Proof: Nis Receipts (max_values: None, max_size: Some(81), added: 2556, mode: MaxEncodedLen) + /// Storage: Nis Summary (r:1 w:1) + /// Proof: Nis Summary (max_values: Some(1), max_size: Some(40), added: 535, mode: MaxEncodedLen) + /// Storage: System Account (r:1 w:0) + /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) + /// Storage: Balances Reserves (r:1 w:1) + /// Proof: Balances Reserves (max_values: None, max_size: Some(1249), added: 3724, mode: MaxEncodedLen) fn thaw_private() -> Weight { - // Minimum execution time: 84_000 nanoseconds. - Weight::from_ref_time(85_000_000) - .saturating_add(T::DbWeight::get().reads(4)) - .saturating_add(T::DbWeight::get().writes(3)) + // Proof Size summary in bytes: + // Measured: `423` + // Estimated: `9418` + // Minimum execution time: 46_212 nanoseconds. + Weight::from_parts(46_748_000, 9418) + .saturating_add(T::DbWeight::get().reads(4_u64)) + .saturating_add(T::DbWeight::get().writes(3_u64)) } - // Storage: Nis Receipts (r:1 w:1) - // Storage: Nis Summary (r:1 w:1) - // Storage: Assets Asset (r:1 w:1) - // Storage: Assets Account (r:1 w:1) - // Storage: System Account (r:1 w:1) + /// Storage: Nis Receipts (r:1 w:1) + /// Proof: Nis Receipts (max_values: None, max_size: Some(81), added: 2556, mode: MaxEncodedLen) + /// Storage: Nis Summary (r:1 w:1) + /// Proof: Nis Summary (max_values: Some(1), max_size: Some(40), added: 535, mode: MaxEncodedLen) + /// Storage: Assets Asset (r:1 w:1) + /// Proof: Assets Asset (max_values: None, max_size: Some(210), added: 2685, mode: MaxEncodedLen) + /// Storage: Assets Account (r:1 w:1) + /// Proof: Assets Account (max_values: None, max_size: Some(102), added: 2577, mode: MaxEncodedLen) + /// Storage: System Account (r:1 w:1) + /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) fn thaw_communal() -> Weight { - // Minimum execution time: 108_000 nanoseconds. - Weight::from_ref_time(115_000_000) - .saturating_add(T::DbWeight::get().reads(5)) - .saturating_add(T::DbWeight::get().writes(5)) + // Proof Size summary in bytes: + // Measured: `868` + // Estimated: `10956` + // Minimum execution time: 68_791 nanoseconds. + Weight::from_parts(69_504_000, 10956) + .saturating_add(T::DbWeight::get().reads(5_u64)) + .saturating_add(T::DbWeight::get().writes(5_u64)) } - // Storage: Nis Receipts (r:1 w:1) - // Storage: Nis Summary (r:1 w:1) - // Storage: System Account (r:1 w:1) - // Storage: Assets Asset (r:1 w:1) - // Storage: Assets Account (r:1 w:1) - // Storage: Balances Reserves (r:1 w:1) + /// Storage: Nis Receipts (r:1 w:1) + /// Proof: Nis Receipts (max_values: None, max_size: Some(81), added: 2556, mode: MaxEncodedLen) + /// Storage: Nis Summary (r:1 w:1) + /// Proof: Nis Summary (max_values: Some(1), max_size: Some(40), added: 535, mode: MaxEncodedLen) + /// Storage: System Account (r:1 w:1) + /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) + /// Storage: Assets Asset (r:1 w:1) + /// Proof: Assets Asset (max_values: None, max_size: Some(210), added: 2685, mode: MaxEncodedLen) + /// Storage: Assets Account (r:1 w:1) + /// Proof: Assets Account (max_values: None, max_size: Some(102), added: 2577, mode: MaxEncodedLen) + /// Storage: Balances Reserves (r:1 w:1) + /// Proof: Balances Reserves (max_values: None, max_size: Some(1249), added: 3724, mode: MaxEncodedLen) fn privatize() -> Weight { - // Minimum execution time: 107_000 nanoseconds. - Weight::from_ref_time(110_000_000) - .saturating_add(T::DbWeight::get().reads(6)) - .saturating_add(T::DbWeight::get().writes(6)) + // Proof Size summary in bytes: + // Measured: `930` + // Estimated: `14680` + // Minimum execution time: 76_784 nanoseconds. + Weight::from_parts(77_575_000, 14680) + .saturating_add(T::DbWeight::get().reads(6_u64)) + .saturating_add(T::DbWeight::get().writes(6_u64)) } - // Storage: Nis Receipts (r:1 w:1) - // Storage: Balances Reserves (r:1 w:1) - // Storage: System Account (r:1 w:1) - // Storage: Nis Summary (r:1 w:1) - // Storage: Assets Asset (r:1 w:1) - // Storage: Assets Account (r:1 w:1) + /// Storage: Nis Receipts (r:1 w:1) + /// Proof: Nis Receipts (max_values: None, max_size: Some(81), added: 2556, mode: MaxEncodedLen) + /// Storage: Balances Reserves (r:1 w:1) + /// Proof: Balances Reserves (max_values: None, max_size: Some(1249), added: 3724, mode: MaxEncodedLen) + /// Storage: System Account (r:1 w:1) + /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) + /// Storage: Nis Summary (r:1 w:1) + /// Proof: Nis Summary (max_values: Some(1), max_size: Some(40), added: 535, mode: MaxEncodedLen) + /// Storage: Assets Asset (r:1 w:1) + /// Proof: Assets Asset (max_values: None, max_size: Some(210), added: 2685, mode: MaxEncodedLen) + /// Storage: Assets Account (r:1 w:1) + /// Proof: Assets Account (max_values: None, max_size: Some(102), added: 2577, mode: MaxEncodedLen) fn communify() -> Weight { - // Minimum execution time: 89_000 nanoseconds. - Weight::from_ref_time(89_000_000) - .saturating_add(T::DbWeight::get().reads(6)) - .saturating_add(T::DbWeight::get().writes(6)) + // Proof Size summary in bytes: + // Measured: `769` + // Estimated: `14680` + // Minimum execution time: 64_543 nanoseconds. + Weight::from_parts(65_258_000, 14680) + .saturating_add(T::DbWeight::get().reads(6_u64)) + .saturating_add(T::DbWeight::get().writes(6_u64)) } - // Storage: Nis Summary (r:1 w:1) - // Storage: System Account (r:1 w:0) - // Storage: Nis QueueTotals (r:1 w:1) + /// Storage: Nis Summary (r:1 w:1) + /// Proof: Nis Summary (max_values: Some(1), max_size: Some(40), added: 535, mode: MaxEncodedLen) + /// Storage: System Account (r:1 w:0) + /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) + /// Storage: Nis QueueTotals (r:1 w:1) + /// Proof: Nis QueueTotals (max_values: Some(1), max_size: Some(6002), added: 6497, mode: MaxEncodedLen) fn process_queues() -> Weight { - // Minimum execution time: 34_000 nanoseconds. - Weight::from_ref_time(38_000_000) - .saturating_add(T::DbWeight::get().reads(3)) - .saturating_add(T::DbWeight::get().writes(2)) + // Proof Size summary in bytes: + // Measured: `6655` + // Estimated: `9635` + // Minimum execution time: 21_379 nanoseconds. + Weight::from_parts(21_736_000, 9635) + .saturating_add(T::DbWeight::get().reads(3_u64)) + .saturating_add(T::DbWeight::get().writes(2_u64)) } - // Storage: Nis Queues (r:1 w:1) + /// Storage: Nis Queues (r:1 w:1) + /// Proof: Nis Queues (max_values: None, max_size: Some(48022), added: 50497, mode: MaxEncodedLen) fn process_queue() -> Weight { - // Minimum execution time: 6_000 nanoseconds. - Weight::from_ref_time(7_000_000) - .saturating_add(T::DbWeight::get().reads(1)) - .saturating_add(T::DbWeight::get().writes(1)) + // Proof Size summary in bytes: + // Measured: `42` + // Estimated: `50497` + // Minimum execution time: 4_302 nanoseconds. + Weight::from_parts(4_440_000, 50497) + .saturating_add(T::DbWeight::get().reads(1_u64)) + .saturating_add(T::DbWeight::get().writes(1_u64)) } - // Storage: Nis Receipts (r:0 w:1) + /// Storage: Nis Receipts (r:0 w:1) + /// Proof: Nis Receipts (max_values: None, max_size: Some(81), added: 2556, mode: MaxEncodedLen) fn process_bid() -> Weight { - // Minimum execution time: 14_000 nanoseconds. - Weight::from_ref_time(15_000_000) - .saturating_add(T::DbWeight::get().writes(1)) + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 6_758 nanoseconds. + Weight::from_ref_time(6_911_000) + .saturating_add(T::DbWeight::get().writes(1_u64)) } } // For backwards compatibility and tests impl WeightInfo for () { - // Storage: Nis Queues (r:1 w:1) - // Storage: Balances Reserves (r:1 w:1) - // Storage: Nis QueueTotals (r:1 w:1) + /// Storage: Nis Queues (r:1 w:1) + /// Proof: Nis Queues (max_values: None, max_size: Some(48022), added: 50497, mode: MaxEncodedLen) + /// Storage: Balances Reserves (r:1 w:1) + /// Proof: Balances Reserves (max_values: None, max_size: Some(1249), added: 3724, mode: MaxEncodedLen) + /// Storage: Nis QueueTotals (r:1 w:1) + /// Proof: Nis QueueTotals (max_values: Some(1), max_size: Some(6002), added: 6497, mode: MaxEncodedLen) /// The range of component `l` is `[0, 999]`. fn place_bid(l: u32, ) -> Weight { - // Minimum execution time: 47_000 nanoseconds. - Weight::from_ref_time(53_822_030) - // Standard Error: 4_869 - .saturating_add(Weight::from_ref_time(47_431).saturating_mul(l.into())) - .saturating_add(RocksDbWeight::get().reads(3)) - .saturating_add(RocksDbWeight::get().writes(3)) + // Proof Size summary in bytes: + // Measured: `6245 + l * (48 ±0)` + // Estimated: `60718` + // Minimum execution time: 28_814 nanoseconds. + Weight::from_parts(35_245_917, 60718) + // Standard Error: 189 + .saturating_add(Weight::from_ref_time(45_322).saturating_mul(l.into())) + .saturating_add(RocksDbWeight::get().reads(3_u64)) + .saturating_add(RocksDbWeight::get().writes(3_u64)) } - // Storage: Nis Queues (r:1 w:1) - // Storage: Balances Reserves (r:1 w:1) - // Storage: Nis QueueTotals (r:1 w:1) + /// Storage: Nis Queues (r:1 w:1) + /// Proof: Nis Queues (max_values: None, max_size: Some(48022), added: 50497, mode: MaxEncodedLen) + /// Storage: Balances Reserves (r:1 w:1) + /// Proof: Balances Reserves (max_values: None, max_size: Some(1249), added: 3724, mode: MaxEncodedLen) + /// Storage: Nis QueueTotals (r:1 w:1) + /// Proof: Nis QueueTotals (max_values: Some(1), max_size: Some(6002), added: 6497, mode: MaxEncodedLen) fn place_bid_max() -> Weight { - // Minimum execution time: 109_000 nanoseconds. - Weight::from_ref_time(109_000_000) - .saturating_add(RocksDbWeight::get().reads(3)) - .saturating_add(RocksDbWeight::get().writes(3)) + // Proof Size summary in bytes: + // Measured: `54247` + // Estimated: `60718` + // Minimum execution time: 80_332 nanoseconds. + Weight::from_parts(81_050_000, 60718) + .saturating_add(RocksDbWeight::get().reads(3_u64)) + .saturating_add(RocksDbWeight::get().writes(3_u64)) } - // Storage: Nis Queues (r:1 w:1) - // Storage: Nis QueueTotals (r:1 w:1) - // Storage: Balances Reserves (r:1 w:1) + /// Storage: Nis Queues (r:1 w:1) + /// Proof: Nis Queues (max_values: None, max_size: Some(48022), added: 50497, mode: MaxEncodedLen) + /// Storage: Nis QueueTotals (r:1 w:1) + /// Proof: Nis QueueTotals (max_values: Some(1), max_size: Some(6002), added: 6497, mode: MaxEncodedLen) + /// Storage: Balances Reserves (r:1 w:1) + /// Proof: Balances Reserves (max_values: None, max_size: Some(1249), added: 3724, mode: MaxEncodedLen) /// The range of component `l` is `[1, 1000]`. fn retract_bid(l: u32, ) -> Weight { - // Minimum execution time: 50_000 nanoseconds. - Weight::from_ref_time(54_479_879) - // Standard Error: 4_891 - .saturating_add(Weight::from_ref_time(38_224).saturating_mul(l.into())) - .saturating_add(RocksDbWeight::get().reads(3)) - .saturating_add(RocksDbWeight::get().writes(3)) + // Proof Size summary in bytes: + // Measured: `6245 + l * (48 ±0)` + // Estimated: `60718` + // Minimum execution time: 34_426 nanoseconds. + Weight::from_parts(36_434_166, 60718) + // Standard Error: 135 + .saturating_add(Weight::from_ref_time(33_923).saturating_mul(l.into())) + .saturating_add(RocksDbWeight::get().reads(3_u64)) + .saturating_add(RocksDbWeight::get().writes(3_u64)) } - // Storage: Nis Summary (r:1 w:0) - // Storage: System Account (r:1 w:1) + /// Storage: Nis Summary (r:1 w:0) + /// Proof: Nis Summary (max_values: Some(1), max_size: Some(40), added: 535, mode: MaxEncodedLen) + /// Storage: System Account (r:1 w:1) + /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) fn fund_deficit() -> Weight { - // Minimum execution time: 57_000 nanoseconds. - Weight::from_ref_time(62_000_000) - .saturating_add(RocksDbWeight::get().reads(2)) - .saturating_add(RocksDbWeight::get().writes(1)) + // Proof Size summary in bytes: + // Measured: `222` + // Estimated: `3138` + // Minimum execution time: 32_566 nanoseconds. + Weight::from_parts(32_880_000, 3138) + .saturating_add(RocksDbWeight::get().reads(2_u64)) + .saturating_add(RocksDbWeight::get().writes(1_u64)) } - // Storage: Nis Receipts (r:1 w:1) - // Storage: Nis Summary (r:1 w:1) - // Storage: System Account (r:1 w:0) - // Storage: Balances Reserves (r:1 w:1) + /// Storage: Nis Receipts (r:1 w:1) + /// Proof: Nis Receipts (max_values: None, max_size: Some(81), added: 2556, mode: MaxEncodedLen) + /// Storage: Nis Summary (r:1 w:1) + /// Proof: Nis Summary (max_values: Some(1), max_size: Some(40), added: 535, mode: MaxEncodedLen) + /// Storage: System Account (r:1 w:0) + /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) + /// Storage: Balances Reserves (r:1 w:1) + /// Proof: Balances Reserves (max_values: None, max_size: Some(1249), added: 3724, mode: MaxEncodedLen) fn thaw_private() -> Weight { - // Minimum execution time: 84_000 nanoseconds. - Weight::from_ref_time(85_000_000) - .saturating_add(RocksDbWeight::get().reads(4)) - .saturating_add(RocksDbWeight::get().writes(3)) + // Proof Size summary in bytes: + // Measured: `423` + // Estimated: `9418` + // Minimum execution time: 46_212 nanoseconds. + Weight::from_parts(46_748_000, 9418) + .saturating_add(RocksDbWeight::get().reads(4_u64)) + .saturating_add(RocksDbWeight::get().writes(3_u64)) } - // Storage: Nis Receipts (r:1 w:1) - // Storage: Nis Summary (r:1 w:1) - // Storage: Assets Asset (r:1 w:1) - // Storage: Assets Account (r:1 w:1) - // Storage: System Account (r:1 w:1) + /// Storage: Nis Receipts (r:1 w:1) + /// Proof: Nis Receipts (max_values: None, max_size: Some(81), added: 2556, mode: MaxEncodedLen) + /// Storage: Nis Summary (r:1 w:1) + /// Proof: Nis Summary (max_values: Some(1), max_size: Some(40), added: 535, mode: MaxEncodedLen) + /// Storage: Assets Asset (r:1 w:1) + /// Proof: Assets Asset (max_values: None, max_size: Some(210), added: 2685, mode: MaxEncodedLen) + /// Storage: Assets Account (r:1 w:1) + /// Proof: Assets Account (max_values: None, max_size: Some(102), added: 2577, mode: MaxEncodedLen) + /// Storage: System Account (r:1 w:1) + /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) fn thaw_communal() -> Weight { - // Minimum execution time: 108_000 nanoseconds. - Weight::from_ref_time(115_000_000) - .saturating_add(RocksDbWeight::get().reads(5)) - .saturating_add(RocksDbWeight::get().writes(5)) + // Proof Size summary in bytes: + // Measured: `868` + // Estimated: `10956` + // Minimum execution time: 68_791 nanoseconds. + Weight::from_parts(69_504_000, 10956) + .saturating_add(RocksDbWeight::get().reads(5_u64)) + .saturating_add(RocksDbWeight::get().writes(5_u64)) } - // Storage: Nis Receipts (r:1 w:1) - // Storage: Nis Summary (r:1 w:1) - // Storage: System Account (r:1 w:1) - // Storage: Assets Asset (r:1 w:1) - // Storage: Assets Account (r:1 w:1) - // Storage: Balances Reserves (r:1 w:1) + /// Storage: Nis Receipts (r:1 w:1) + /// Proof: Nis Receipts (max_values: None, max_size: Some(81), added: 2556, mode: MaxEncodedLen) + /// Storage: Nis Summary (r:1 w:1) + /// Proof: Nis Summary (max_values: Some(1), max_size: Some(40), added: 535, mode: MaxEncodedLen) + /// Storage: System Account (r:1 w:1) + /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) + /// Storage: Assets Asset (r:1 w:1) + /// Proof: Assets Asset (max_values: None, max_size: Some(210), added: 2685, mode: MaxEncodedLen) + /// Storage: Assets Account (r:1 w:1) + /// Proof: Assets Account (max_values: None, max_size: Some(102), added: 2577, mode: MaxEncodedLen) + /// Storage: Balances Reserves (r:1 w:1) + /// Proof: Balances Reserves (max_values: None, max_size: Some(1249), added: 3724, mode: MaxEncodedLen) fn privatize() -> Weight { - // Minimum execution time: 107_000 nanoseconds. - Weight::from_ref_time(110_000_000) - .saturating_add(RocksDbWeight::get().reads(6)) - .saturating_add(RocksDbWeight::get().writes(6)) + // Proof Size summary in bytes: + // Measured: `930` + // Estimated: `14680` + // Minimum execution time: 76_784 nanoseconds. + Weight::from_parts(77_575_000, 14680) + .saturating_add(RocksDbWeight::get().reads(6_u64)) + .saturating_add(RocksDbWeight::get().writes(6_u64)) } - // Storage: Nis Receipts (r:1 w:1) - // Storage: Balances Reserves (r:1 w:1) - // Storage: System Account (r:1 w:1) - // Storage: Nis Summary (r:1 w:1) - // Storage: Assets Asset (r:1 w:1) - // Storage: Assets Account (r:1 w:1) + /// Storage: Nis Receipts (r:1 w:1) + /// Proof: Nis Receipts (max_values: None, max_size: Some(81), added: 2556, mode: MaxEncodedLen) + /// Storage: Balances Reserves (r:1 w:1) + /// Proof: Balances Reserves (max_values: None, max_size: Some(1249), added: 3724, mode: MaxEncodedLen) + /// Storage: System Account (r:1 w:1) + /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) + /// Storage: Nis Summary (r:1 w:1) + /// Proof: Nis Summary (max_values: Some(1), max_size: Some(40), added: 535, mode: MaxEncodedLen) + /// Storage: Assets Asset (r:1 w:1) + /// Proof: Assets Asset (max_values: None, max_size: Some(210), added: 2685, mode: MaxEncodedLen) + /// Storage: Assets Account (r:1 w:1) + /// Proof: Assets Account (max_values: None, max_size: Some(102), added: 2577, mode: MaxEncodedLen) fn communify() -> Weight { - // Minimum execution time: 89_000 nanoseconds. - Weight::from_ref_time(89_000_000) - .saturating_add(RocksDbWeight::get().reads(6)) - .saturating_add(RocksDbWeight::get().writes(6)) + // Proof Size summary in bytes: + // Measured: `769` + // Estimated: `14680` + // Minimum execution time: 64_543 nanoseconds. + Weight::from_parts(65_258_000, 14680) + .saturating_add(RocksDbWeight::get().reads(6_u64)) + .saturating_add(RocksDbWeight::get().writes(6_u64)) } - // Storage: Nis Summary (r:1 w:1) - // Storage: System Account (r:1 w:0) - // Storage: Nis QueueTotals (r:1 w:1) + /// Storage: Nis Summary (r:1 w:1) + /// Proof: Nis Summary (max_values: Some(1), max_size: Some(40), added: 535, mode: MaxEncodedLen) + /// Storage: System Account (r:1 w:0) + /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) + /// Storage: Nis QueueTotals (r:1 w:1) + /// Proof: Nis QueueTotals (max_values: Some(1), max_size: Some(6002), added: 6497, mode: MaxEncodedLen) fn process_queues() -> Weight { - // Minimum execution time: 34_000 nanoseconds. - Weight::from_ref_time(38_000_000) - .saturating_add(RocksDbWeight::get().reads(3)) - .saturating_add(RocksDbWeight::get().writes(2)) + // Proof Size summary in bytes: + // Measured: `6655` + // Estimated: `9635` + // Minimum execution time: 21_379 nanoseconds. + Weight::from_parts(21_736_000, 9635) + .saturating_add(RocksDbWeight::get().reads(3_u64)) + .saturating_add(RocksDbWeight::get().writes(2_u64)) } - // Storage: Nis Queues (r:1 w:1) + /// Storage: Nis Queues (r:1 w:1) + /// Proof: Nis Queues (max_values: None, max_size: Some(48022), added: 50497, mode: MaxEncodedLen) fn process_queue() -> Weight { - // Minimum execution time: 6_000 nanoseconds. - Weight::from_ref_time(7_000_000) - .saturating_add(RocksDbWeight::get().reads(1)) - .saturating_add(RocksDbWeight::get().writes(1)) + // Proof Size summary in bytes: + // Measured: `42` + // Estimated: `50497` + // Minimum execution time: 4_302 nanoseconds. + Weight::from_parts(4_440_000, 50497) + .saturating_add(RocksDbWeight::get().reads(1_u64)) + .saturating_add(RocksDbWeight::get().writes(1_u64)) } - // Storage: Nis Receipts (r:0 w:1) + /// Storage: Nis Receipts (r:0 w:1) + /// Proof: Nis Receipts (max_values: None, max_size: Some(81), added: 2556, mode: MaxEncodedLen) fn process_bid() -> Weight { - // Minimum execution time: 14_000 nanoseconds. - Weight::from_ref_time(15_000_000) - .saturating_add(RocksDbWeight::get().writes(1)) + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 6_758 nanoseconds. + Weight::from_ref_time(6_911_000) + .saturating_add(RocksDbWeight::get().writes(1_u64)) } } diff --git a/frame/nomination-pools/src/weights.rs b/frame/nomination-pools/src/weights.rs index 1062b1749..ae7301594 100644 --- a/frame/nomination-pools/src/weights.rs +++ b/frame/nomination-pools/src/weights.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2022 Parity Technologies (UK) Ltd. +// Copyright (C) 2023 Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); @@ -18,7 +18,8 @@ //! Autogenerated weights for pallet_nomination_pools //! //! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev -//! DATE: 2022-11-07, STEPS: `50`, REPEAT: 20, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! DATE: 2023-01-24, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! WORST CASE MAP SIZE: `1000000` //! HOSTNAME: `bm2`, CPU: `Intel(R) Core(TM) i7-7700K CPU @ 4.20GHz` //! EXECUTION: Some(Wasm), WASM-EXECUTION: Compiled, CHAIN: Some("dev"), DB CACHE: 1024 @@ -67,492 +68,854 @@ pub trait WeightInfo { /// Weights for pallet_nomination_pools using the Substrate node and recommended hardware. pub struct SubstrateWeight(PhantomData); impl WeightInfo for SubstrateWeight { - // Storage: NominationPools MinJoinBond (r:1 w:0) - // Storage: NominationPools PoolMembers (r:1 w:1) - // Storage: NominationPools BondedPools (r:1 w:1) - // Storage: Staking Bonded (r:1 w:0) - // Storage: Staking Ledger (r:1 w:1) - // Storage: NominationPools RewardPools (r:1 w:1) - // Storage: System Account (r:2 w:1) - // Storage: NominationPools MaxPoolMembersPerPool (r:1 w:0) - // Storage: NominationPools MaxPoolMembers (r:1 w:0) - // Storage: NominationPools CounterForPoolMembers (r:1 w:1) - // Storage: Balances Locks (r:1 w:1) - // Storage: VoterList ListNodes (r:3 w:3) - // Storage: VoterList ListBags (r:2 w:2) + /// Storage: NominationPools MinJoinBond (r:1 w:0) + /// Proof: NominationPools MinJoinBond (max_values: Some(1), max_size: Some(16), added: 511, mode: MaxEncodedLen) + /// Storage: NominationPools PoolMembers (r:1 w:1) + /// Proof: NominationPools PoolMembers (max_values: None, max_size: Some(237), added: 2712, mode: MaxEncodedLen) + /// Storage: NominationPools BondedPools (r:1 w:1) + /// Proof: NominationPools BondedPools (max_values: None, max_size: Some(164), added: 2639, mode: MaxEncodedLen) + /// Storage: Staking Bonded (r:1 w:0) + /// Proof: Staking Bonded (max_values: None, max_size: Some(72), added: 2547, mode: MaxEncodedLen) + /// Storage: Staking Ledger (r:1 w:1) + /// Proof: Staking Ledger (max_values: None, max_size: Some(1091), added: 3566, mode: MaxEncodedLen) + /// Storage: NominationPools RewardPools (r:1 w:1) + /// Proof: NominationPools RewardPools (max_values: None, max_size: Some(60), added: 2535, mode: MaxEncodedLen) + /// Storage: System Account (r:2 w:1) + /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) + /// Storage: NominationPools MaxPoolMembersPerPool (r:1 w:0) + /// Proof: NominationPools MaxPoolMembersPerPool (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) + /// Storage: NominationPools MaxPoolMembers (r:1 w:0) + /// Proof: NominationPools MaxPoolMembers (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) + /// Storage: NominationPools CounterForPoolMembers (r:1 w:1) + /// Proof: NominationPools CounterForPoolMembers (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) + /// Storage: Balances Locks (r:1 w:1) + /// Proof: Balances Locks (max_values: None, max_size: Some(1299), added: 3774, mode: MaxEncodedLen) + /// Storage: VoterList ListNodes (r:3 w:3) + /// Proof: VoterList ListNodes (max_values: None, max_size: Some(154), added: 2629, mode: MaxEncodedLen) + /// Storage: VoterList ListBags (r:2 w:2) + /// Proof: VoterList ListBags (max_values: None, max_size: Some(82), added: 2557, mode: MaxEncodedLen) fn join() -> Weight { - // Minimum execution time: 159_948 nanoseconds. - Weight::from_ref_time(161_133_000 as u64) - .saturating_add(T::DbWeight::get().reads(17 as u64)) - .saturating_add(T::DbWeight::get().writes(12 as u64)) + // Proof Size summary in bytes: + // Measured: `3573` + // Estimated: `37988` + // Minimum execution time: 140_155 nanoseconds. + Weight::from_parts(141_098_000, 37988) + .saturating_add(T::DbWeight::get().reads(17_u64)) + .saturating_add(T::DbWeight::get().writes(12_u64)) } - // Storage: NominationPools PoolMembers (r:1 w:1) - // Storage: NominationPools BondedPools (r:1 w:1) - // Storage: NominationPools RewardPools (r:1 w:1) - // Storage: System Account (r:3 w:2) - // Storage: Staking Bonded (r:1 w:0) - // Storage: Staking Ledger (r:1 w:1) - // Storage: Balances Locks (r:1 w:1) - // Storage: VoterList ListNodes (r:3 w:3) - // Storage: VoterList ListBags (r:2 w:2) + /// Storage: NominationPools PoolMembers (r:1 w:1) + /// Proof: NominationPools PoolMembers (max_values: None, max_size: Some(237), added: 2712, mode: MaxEncodedLen) + /// Storage: NominationPools BondedPools (r:1 w:1) + /// Proof: NominationPools BondedPools (max_values: None, max_size: Some(164), added: 2639, mode: MaxEncodedLen) + /// Storage: NominationPools RewardPools (r:1 w:1) + /// Proof: NominationPools RewardPools (max_values: None, max_size: Some(60), added: 2535, mode: MaxEncodedLen) + /// Storage: System Account (r:3 w:2) + /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) + /// Storage: Staking Bonded (r:1 w:0) + /// Proof: Staking Bonded (max_values: None, max_size: Some(72), added: 2547, mode: MaxEncodedLen) + /// Storage: Staking Ledger (r:1 w:1) + /// Proof: Staking Ledger (max_values: None, max_size: Some(1091), added: 3566, mode: MaxEncodedLen) + /// Storage: Balances Locks (r:1 w:1) + /// Proof: Balances Locks (max_values: None, max_size: Some(1299), added: 3774, mode: MaxEncodedLen) + /// Storage: VoterList ListNodes (r:3 w:3) + /// Proof: VoterList ListNodes (max_values: None, max_size: Some(154), added: 2629, mode: MaxEncodedLen) + /// Storage: VoterList ListBags (r:2 w:2) + /// Proof: VoterList ListBags (max_values: None, max_size: Some(82), added: 2557, mode: MaxEncodedLen) fn bond_extra_transfer() -> Weight { - // Minimum execution time: 155_517 nanoseconds. - Weight::from_ref_time(159_101_000 as u64) - .saturating_add(T::DbWeight::get().reads(14 as u64)) - .saturating_add(T::DbWeight::get().writes(12 as u64)) + // Proof Size summary in bytes: + // Measured: `3615` + // Estimated: `38583` + // Minimum execution time: 136_048 nanoseconds. + Weight::from_parts(136_767_000, 38583) + .saturating_add(T::DbWeight::get().reads(14_u64)) + .saturating_add(T::DbWeight::get().writes(12_u64)) } - // Storage: NominationPools PoolMembers (r:1 w:1) - // Storage: NominationPools BondedPools (r:1 w:1) - // Storage: NominationPools RewardPools (r:1 w:1) - // Storage: System Account (r:3 w:3) - // Storage: Staking Bonded (r:1 w:0) - // Storage: Staking Ledger (r:1 w:1) - // Storage: Balances Locks (r:1 w:1) - // Storage: VoterList ListNodes (r:3 w:3) - // Storage: VoterList ListBags (r:2 w:2) + /// Storage: NominationPools PoolMembers (r:1 w:1) + /// Proof: NominationPools PoolMembers (max_values: None, max_size: Some(237), added: 2712, mode: MaxEncodedLen) + /// Storage: NominationPools BondedPools (r:1 w:1) + /// Proof: NominationPools BondedPools (max_values: None, max_size: Some(164), added: 2639, mode: MaxEncodedLen) + /// Storage: NominationPools RewardPools (r:1 w:1) + /// Proof: NominationPools RewardPools (max_values: None, max_size: Some(60), added: 2535, mode: MaxEncodedLen) + /// Storage: System Account (r:3 w:3) + /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) + /// Storage: Staking Bonded (r:1 w:0) + /// Proof: Staking Bonded (max_values: None, max_size: Some(72), added: 2547, mode: MaxEncodedLen) + /// Storage: Staking Ledger (r:1 w:1) + /// Proof: Staking Ledger (max_values: None, max_size: Some(1091), added: 3566, mode: MaxEncodedLen) + /// Storage: Balances Locks (r:1 w:1) + /// Proof: Balances Locks (max_values: None, max_size: Some(1299), added: 3774, mode: MaxEncodedLen) + /// Storage: VoterList ListNodes (r:3 w:3) + /// Proof: VoterList ListNodes (max_values: None, max_size: Some(154), added: 2629, mode: MaxEncodedLen) + /// Storage: VoterList ListBags (r:2 w:2) + /// Proof: VoterList ListBags (max_values: None, max_size: Some(82), added: 2557, mode: MaxEncodedLen) fn bond_extra_reward() -> Weight { - // Minimum execution time: 172_788 nanoseconds. - Weight::from_ref_time(174_212_000 as u64) - .saturating_add(T::DbWeight::get().reads(14 as u64)) - .saturating_add(T::DbWeight::get().writes(13 as u64)) + // Proof Size summary in bytes: + // Measured: `3615` + // Estimated: `38583` + // Minimum execution time: 151_473 nanoseconds. + Weight::from_parts(152_375_000, 38583) + .saturating_add(T::DbWeight::get().reads(14_u64)) + .saturating_add(T::DbWeight::get().writes(13_u64)) } - // Storage: NominationPools PoolMembers (r:1 w:1) - // Storage: NominationPools BondedPools (r:1 w:1) - // Storage: NominationPools RewardPools (r:1 w:1) - // Storage: System Account (r:1 w:1) + /// Storage: NominationPools PoolMembers (r:1 w:1) + /// Proof: NominationPools PoolMembers (max_values: None, max_size: Some(237), added: 2712, mode: MaxEncodedLen) + /// Storage: NominationPools BondedPools (r:1 w:1) + /// Proof: NominationPools BondedPools (max_values: None, max_size: Some(164), added: 2639, mode: MaxEncodedLen) + /// Storage: NominationPools RewardPools (r:1 w:1) + /// Proof: NominationPools RewardPools (max_values: None, max_size: Some(60), added: 2535, mode: MaxEncodedLen) + /// Storage: System Account (r:1 w:1) + /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) fn claim_payout() -> Weight { - // Minimum execution time: 64_560 nanoseconds. - Weight::from_ref_time(64_950_000 as u64) - .saturating_add(T::DbWeight::get().reads(4 as u64)) - .saturating_add(T::DbWeight::get().writes(4 as u64)) + // Proof Size summary in bytes: + // Measured: `1189` + // Estimated: `10489` + // Minimum execution time: 51_146 nanoseconds. + Weight::from_parts(51_570_000, 10489) + .saturating_add(T::DbWeight::get().reads(4_u64)) + .saturating_add(T::DbWeight::get().writes(4_u64)) } - // Storage: NominationPools PoolMembers (r:1 w:1) - // Storage: NominationPools BondedPools (r:1 w:1) - // Storage: NominationPools RewardPools (r:1 w:1) - // Storage: Staking Bonded (r:1 w:0) - // Storage: Staking Ledger (r:1 w:1) - // Storage: System Account (r:2 w:1) - // Storage: Staking CurrentEra (r:1 w:0) - // Storage: Staking Nominators (r:1 w:0) - // Storage: Staking MinNominatorBond (r:1 w:0) - // Storage: Balances Locks (r:1 w:1) - // Storage: VoterList ListNodes (r:3 w:3) - // Storage: VoterList ListBags (r:2 w:2) - // Storage: NominationPools SubPoolsStorage (r:1 w:1) - // Storage: NominationPools CounterForSubPoolsStorage (r:1 w:1) + /// Storage: NominationPools PoolMembers (r:1 w:1) + /// Proof: NominationPools PoolMembers (max_values: None, max_size: Some(237), added: 2712, mode: MaxEncodedLen) + /// Storage: NominationPools BondedPools (r:1 w:1) + /// Proof: NominationPools BondedPools (max_values: None, max_size: Some(164), added: 2639, mode: MaxEncodedLen) + /// Storage: NominationPools RewardPools (r:1 w:1) + /// Proof: NominationPools RewardPools (max_values: None, max_size: Some(60), added: 2535, mode: MaxEncodedLen) + /// Storage: Staking Bonded (r:1 w:0) + /// Proof: Staking Bonded (max_values: None, max_size: Some(72), added: 2547, mode: MaxEncodedLen) + /// Storage: Staking Ledger (r:1 w:1) + /// Proof: Staking Ledger (max_values: None, max_size: Some(1091), added: 3566, mode: MaxEncodedLen) + /// Storage: System Account (r:2 w:1) + /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) + /// Storage: Staking CurrentEra (r:1 w:0) + /// Proof: Staking CurrentEra (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) + /// Storage: Staking Nominators (r:1 w:0) + /// Proof: Staking Nominators (max_values: None, max_size: Some(558), added: 3033, mode: MaxEncodedLen) + /// Storage: Staking MinNominatorBond (r:1 w:0) + /// Proof: Staking MinNominatorBond (max_values: Some(1), max_size: Some(16), added: 511, mode: MaxEncodedLen) + /// Storage: Balances Locks (r:1 w:1) + /// Proof: Balances Locks (max_values: None, max_size: Some(1299), added: 3774, mode: MaxEncodedLen) + /// Storage: VoterList ListNodes (r:3 w:3) + /// Proof: VoterList ListNodes (max_values: None, max_size: Some(154), added: 2629, mode: MaxEncodedLen) + /// Storage: VoterList ListBags (r:2 w:2) + /// Proof: VoterList ListBags (max_values: None, max_size: Some(82), added: 2557, mode: MaxEncodedLen) + /// Storage: NominationPools SubPoolsStorage (r:1 w:1) + /// Proof: NominationPools SubPoolsStorage (max_values: None, max_size: Some(24382), added: 26857, mode: MaxEncodedLen) + /// Storage: NominationPools CounterForSubPoolsStorage (r:1 w:1) + /// Proof: NominationPools CounterForSubPoolsStorage (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) fn unbond() -> Weight { - // Minimum execution time: 161_398 nanoseconds. - Weight::from_ref_time(162_991_000 as u64) - .saturating_add(T::DbWeight::get().reads(18 as u64)) - .saturating_add(T::DbWeight::get().writes(13 as u64)) + // Proof Size summary in bytes: + // Measured: `3858` + // Estimated: `67379` + // Minimum execution time: 141_400 nanoseconds. + Weight::from_parts(141_822_000, 67379) + .saturating_add(T::DbWeight::get().reads(18_u64)) + .saturating_add(T::DbWeight::get().writes(13_u64)) } - // Storage: NominationPools BondedPools (r:1 w:0) - // Storage: Staking Bonded (r:1 w:0) - // Storage: Staking Ledger (r:1 w:1) - // Storage: Staking CurrentEra (r:1 w:0) - // Storage: Balances Locks (r:1 w:1) + /// Storage: NominationPools BondedPools (r:1 w:0) + /// Proof: NominationPools BondedPools (max_values: None, max_size: Some(164), added: 2639, mode: MaxEncodedLen) + /// Storage: Staking Bonded (r:1 w:0) + /// Proof: Staking Bonded (max_values: None, max_size: Some(72), added: 2547, mode: MaxEncodedLen) + /// Storage: Staking Ledger (r:1 w:1) + /// Proof: Staking Ledger (max_values: None, max_size: Some(1091), added: 3566, mode: MaxEncodedLen) + /// Storage: Staking CurrentEra (r:1 w:0) + /// Proof: Staking CurrentEra (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) + /// Storage: Balances Locks (r:1 w:1) + /// Proof: Balances Locks (max_values: None, max_size: Some(1299), added: 3774, mode: MaxEncodedLen) /// The range of component `s` is `[0, 100]`. fn pool_withdraw_unbonded(s: u32, ) -> Weight { - // Minimum execution time: 66_036 nanoseconds. - Weight::from_ref_time(67_183_304 as u64) - // Standard Error: 565 - .saturating_add(Weight::from_ref_time(57_830 as u64).saturating_mul(s as u64)) - .saturating_add(T::DbWeight::get().reads(5 as u64)) - .saturating_add(T::DbWeight::get().writes(2 as u64)) + // Proof Size summary in bytes: + // Measured: `1779` + // Estimated: `13025` + // Minimum execution time: 49_021 nanoseconds. + Weight::from_parts(49_954_282, 13025) + // Standard Error: 378 + .saturating_add(Weight::from_ref_time(5_165).saturating_mul(s.into())) + .saturating_add(T::DbWeight::get().reads(5_u64)) + .saturating_add(T::DbWeight::get().writes(2_u64)) } - // Storage: NominationPools PoolMembers (r:1 w:1) - // Storage: Staking CurrentEra (r:1 w:0) - // Storage: NominationPools BondedPools (r:1 w:1) - // Storage: NominationPools SubPoolsStorage (r:1 w:1) - // Storage: Staking Bonded (r:1 w:0) - // Storage: Staking Ledger (r:1 w:1) - // Storage: Balances Locks (r:1 w:1) - // Storage: System Account (r:1 w:1) - // Storage: NominationPools CounterForPoolMembers (r:1 w:1) + /// Storage: NominationPools PoolMembers (r:1 w:1) + /// Proof: NominationPools PoolMembers (max_values: None, max_size: Some(237), added: 2712, mode: MaxEncodedLen) + /// Storage: Staking CurrentEra (r:1 w:0) + /// Proof: Staking CurrentEra (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) + /// Storage: NominationPools BondedPools (r:1 w:1) + /// Proof: NominationPools BondedPools (max_values: None, max_size: Some(164), added: 2639, mode: MaxEncodedLen) + /// Storage: NominationPools SubPoolsStorage (r:1 w:1) + /// Proof: NominationPools SubPoolsStorage (max_values: None, max_size: Some(24382), added: 26857, mode: MaxEncodedLen) + /// Storage: Staking Bonded (r:1 w:0) + /// Proof: Staking Bonded (max_values: None, max_size: Some(72), added: 2547, mode: MaxEncodedLen) + /// Storage: Staking Ledger (r:1 w:1) + /// Proof: Staking Ledger (max_values: None, max_size: Some(1091), added: 3566, mode: MaxEncodedLen) + /// Storage: Balances Locks (r:1 w:1) + /// Proof: Balances Locks (max_values: None, max_size: Some(1299), added: 3774, mode: MaxEncodedLen) + /// Storage: System Account (r:1 w:1) + /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) + /// Storage: NominationPools CounterForPoolMembers (r:1 w:1) + /// Proof: NominationPools CounterForPoolMembers (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) /// The range of component `s` is `[0, 100]`. fn withdraw_unbonded_update(s: u32, ) -> Weight { - // Minimum execution time: 111_156 nanoseconds. - Weight::from_ref_time(112_507_059 as u64) - // Standard Error: 655 - .saturating_add(Weight::from_ref_time(53_711 as u64).saturating_mul(s as u64)) - .saturating_add(T::DbWeight::get().reads(9 as u64)) - .saturating_add(T::DbWeight::get().writes(7 as u64)) + // Proof Size summary in bytes: + // Measured: `2303` + // Estimated: `45696` + // Minimum execution time: 92_473 nanoseconds. + Weight::from_parts(93_901_972, 45696) + // Standard Error: 618 + .saturating_add(Weight::from_ref_time(12_032).saturating_mul(s.into())) + .saturating_add(T::DbWeight::get().reads(9_u64)) + .saturating_add(T::DbWeight::get().writes(7_u64)) } - // Storage: NominationPools PoolMembers (r:1 w:1) - // Storage: Staking CurrentEra (r:1 w:0) - // Storage: NominationPools BondedPools (r:1 w:1) - // Storage: NominationPools SubPoolsStorage (r:1 w:1) - // Storage: Staking Bonded (r:1 w:1) - // Storage: Staking Ledger (r:1 w:1) - // Storage: Staking SlashingSpans (r:1 w:0) - // Storage: Staking Validators (r:1 w:0) - // Storage: Staking Nominators (r:1 w:0) - // Storage: System Account (r:2 w:2) - // Storage: Balances Locks (r:1 w:1) - // Storage: NominationPools CounterForPoolMembers (r:1 w:1) - // Storage: NominationPools ReversePoolIdLookup (r:1 w:1) - // Storage: NominationPools CounterForReversePoolIdLookup (r:1 w:1) - // Storage: NominationPools RewardPools (r:1 w:1) - // Storage: NominationPools CounterForRewardPools (r:1 w:1) - // Storage: NominationPools CounterForSubPoolsStorage (r:1 w:1) - // Storage: NominationPools Metadata (r:1 w:1) - // Storage: NominationPools CounterForBondedPools (r:1 w:1) - // Storage: Staking Payee (r:0 w:1) + /// Storage: NominationPools PoolMembers (r:1 w:1) + /// Proof: NominationPools PoolMembers (max_values: None, max_size: Some(237), added: 2712, mode: MaxEncodedLen) + /// Storage: Staking CurrentEra (r:1 w:0) + /// Proof: Staking CurrentEra (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) + /// Storage: NominationPools BondedPools (r:1 w:1) + /// Proof: NominationPools BondedPools (max_values: None, max_size: Some(164), added: 2639, mode: MaxEncodedLen) + /// Storage: NominationPools SubPoolsStorage (r:1 w:1) + /// Proof: NominationPools SubPoolsStorage (max_values: None, max_size: Some(24382), added: 26857, mode: MaxEncodedLen) + /// Storage: Staking Bonded (r:1 w:1) + /// Proof: Staking Bonded (max_values: None, max_size: Some(72), added: 2547, mode: MaxEncodedLen) + /// Storage: Staking Ledger (r:1 w:1) + /// Proof: Staking Ledger (max_values: None, max_size: Some(1091), added: 3566, mode: MaxEncodedLen) + /// Storage: Staking SlashingSpans (r:1 w:0) + /// Proof Skipped: Staking SlashingSpans (max_values: None, max_size: None, mode: Measured) + /// Storage: Staking Validators (r:1 w:0) + /// Proof: Staking Validators (max_values: None, max_size: Some(45), added: 2520, mode: MaxEncodedLen) + /// Storage: Staking Nominators (r:1 w:0) + /// Proof: Staking Nominators (max_values: None, max_size: Some(558), added: 3033, mode: MaxEncodedLen) + /// Storage: System Account (r:2 w:2) + /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) + /// Storage: Balances Locks (r:1 w:1) + /// Proof: Balances Locks (max_values: None, max_size: Some(1299), added: 3774, mode: MaxEncodedLen) + /// Storage: NominationPools CounterForPoolMembers (r:1 w:1) + /// Proof: NominationPools CounterForPoolMembers (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) + /// Storage: NominationPools ReversePoolIdLookup (r:1 w:1) + /// Proof: NominationPools ReversePoolIdLookup (max_values: None, max_size: Some(44), added: 2519, mode: MaxEncodedLen) + /// Storage: NominationPools CounterForReversePoolIdLookup (r:1 w:1) + /// Proof: NominationPools CounterForReversePoolIdLookup (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) + /// Storage: NominationPools RewardPools (r:1 w:1) + /// Proof: NominationPools RewardPools (max_values: None, max_size: Some(60), added: 2535, mode: MaxEncodedLen) + /// Storage: NominationPools CounterForRewardPools (r:1 w:1) + /// Proof: NominationPools CounterForRewardPools (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) + /// Storage: NominationPools CounterForSubPoolsStorage (r:1 w:1) + /// Proof: NominationPools CounterForSubPoolsStorage (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) + /// Storage: NominationPools Metadata (r:1 w:1) + /// Proof: NominationPools Metadata (max_values: None, max_size: Some(270), added: 2745, mode: MaxEncodedLen) + /// Storage: NominationPools CounterForBondedPools (r:1 w:1) + /// Proof: NominationPools CounterForBondedPools (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) + /// Storage: Staking Payee (r:0 w:1) + /// Proof: Staking Payee (max_values: None, max_size: Some(73), added: 2548, mode: MaxEncodedLen) /// The range of component `s` is `[0, 100]`. - fn withdraw_unbonded_kill(s: u32, ) -> Weight { - // Minimum execution time: 168_270 nanoseconds. - Weight::from_ref_time(170_059_380 as u64) - // Standard Error: 1_506 - .saturating_add(Weight::from_ref_time(1_258 as u64).saturating_mul(s as u64)) - .saturating_add(T::DbWeight::get().reads(20 as u64)) - .saturating_add(T::DbWeight::get().writes(17 as u64)) + fn withdraw_unbonded_kill(_s: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `2690` + // Estimated: `68812` + // Minimum execution time: 150_063 nanoseconds. + Weight::from_parts(152_321_387, 68812) + .saturating_add(T::DbWeight::get().reads(20_u64)) + .saturating_add(T::DbWeight::get().writes(17_u64)) } - // Storage: NominationPools LastPoolId (r:1 w:1) - // Storage: Staking MinNominatorBond (r:1 w:0) - // Storage: NominationPools MinCreateBond (r:1 w:0) - // Storage: NominationPools MinJoinBond (r:1 w:0) - // Storage: NominationPools MaxPools (r:1 w:0) - // Storage: NominationPools CounterForBondedPools (r:1 w:1) - // Storage: NominationPools PoolMembers (r:1 w:1) - // Storage: NominationPools MaxPoolMembersPerPool (r:1 w:0) - // Storage: NominationPools MaxPoolMembers (r:1 w:0) - // Storage: NominationPools CounterForPoolMembers (r:1 w:1) - // Storage: System Account (r:2 w:2) - // Storage: Staking Bonded (r:1 w:1) - // Storage: Staking Ledger (r:1 w:1) - // Storage: Staking CurrentEra (r:1 w:0) - // Storage: Balances Locks (r:1 w:1) - // Storage: NominationPools RewardPools (r:1 w:1) - // Storage: NominationPools CounterForRewardPools (r:1 w:1) - // Storage: NominationPools ReversePoolIdLookup (r:1 w:1) - // Storage: NominationPools CounterForReversePoolIdLookup (r:1 w:1) - // Storage: NominationPools BondedPools (r:1 w:1) - // Storage: Staking Payee (r:0 w:1) + /// Storage: NominationPools LastPoolId (r:1 w:1) + /// Proof: NominationPools LastPoolId (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) + /// Storage: Staking MinNominatorBond (r:1 w:0) + /// Proof: Staking MinNominatorBond (max_values: Some(1), max_size: Some(16), added: 511, mode: MaxEncodedLen) + /// Storage: NominationPools MinCreateBond (r:1 w:0) + /// Proof: NominationPools MinCreateBond (max_values: Some(1), max_size: Some(16), added: 511, mode: MaxEncodedLen) + /// Storage: NominationPools MinJoinBond (r:1 w:0) + /// Proof: NominationPools MinJoinBond (max_values: Some(1), max_size: Some(16), added: 511, mode: MaxEncodedLen) + /// Storage: NominationPools MaxPools (r:1 w:0) + /// Proof: NominationPools MaxPools (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) + /// Storage: NominationPools CounterForBondedPools (r:1 w:1) + /// Proof: NominationPools CounterForBondedPools (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) + /// Storage: NominationPools PoolMembers (r:1 w:1) + /// Proof: NominationPools PoolMembers (max_values: None, max_size: Some(237), added: 2712, mode: MaxEncodedLen) + /// Storage: NominationPools MaxPoolMembersPerPool (r:1 w:0) + /// Proof: NominationPools MaxPoolMembersPerPool (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) + /// Storage: NominationPools MaxPoolMembers (r:1 w:0) + /// Proof: NominationPools MaxPoolMembers (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) + /// Storage: NominationPools CounterForPoolMembers (r:1 w:1) + /// Proof: NominationPools CounterForPoolMembers (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) + /// Storage: System Account (r:2 w:2) + /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) + /// Storage: Staking Bonded (r:1 w:1) + /// Proof: Staking Bonded (max_values: None, max_size: Some(72), added: 2547, mode: MaxEncodedLen) + /// Storage: Staking Ledger (r:1 w:1) + /// Proof: Staking Ledger (max_values: None, max_size: Some(1091), added: 3566, mode: MaxEncodedLen) + /// Storage: Staking CurrentEra (r:1 w:0) + /// Proof: Staking CurrentEra (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) + /// Storage: Balances Locks (r:1 w:1) + /// Proof: Balances Locks (max_values: None, max_size: Some(1299), added: 3774, mode: MaxEncodedLen) + /// Storage: NominationPools RewardPools (r:1 w:1) + /// Proof: NominationPools RewardPools (max_values: None, max_size: Some(60), added: 2535, mode: MaxEncodedLen) + /// Storage: NominationPools CounterForRewardPools (r:1 w:1) + /// Proof: NominationPools CounterForRewardPools (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) + /// Storage: NominationPools ReversePoolIdLookup (r:1 w:1) + /// Proof: NominationPools ReversePoolIdLookup (max_values: None, max_size: Some(44), added: 2519, mode: MaxEncodedLen) + /// Storage: NominationPools CounterForReversePoolIdLookup (r:1 w:1) + /// Proof: NominationPools CounterForReversePoolIdLookup (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) + /// Storage: NominationPools BondedPools (r:1 w:1) + /// Proof: NominationPools BondedPools (max_values: None, max_size: Some(164), added: 2639, mode: MaxEncodedLen) + /// Storage: Staking Payee (r:0 w:1) + /// Proof: Staking Payee (max_values: None, max_size: Some(73), added: 2548, mode: MaxEncodedLen) fn create() -> Weight { - // Minimum execution time: 146_153 nanoseconds. - Weight::from_ref_time(146_955_000 as u64) - .saturating_add(T::DbWeight::get().reads(21 as u64)) - .saturating_add(T::DbWeight::get().writes(15 as u64)) + // Proof Size summary in bytes: + // Measured: `1321` + // Estimated: `31522` + // Minimum execution time: 131_430 nanoseconds. + Weight::from_parts(132_214_000, 31522) + .saturating_add(T::DbWeight::get().reads(21_u64)) + .saturating_add(T::DbWeight::get().writes(15_u64)) } - // Storage: NominationPools BondedPools (r:1 w:0) - // Storage: Staking Bonded (r:1 w:0) - // Storage: Staking Ledger (r:1 w:0) - // Storage: Staking MinNominatorBond (r:1 w:0) - // Storage: Staking Nominators (r:1 w:1) - // Storage: Staking MaxNominatorsCount (r:1 w:0) - // Storage: Staking Validators (r:2 w:0) - // Storage: Staking CurrentEra (r:1 w:0) - // Storage: VoterList ListNodes (r:1 w:1) - // Storage: VoterList ListBags (r:1 w:1) - // Storage: VoterList CounterForListNodes (r:1 w:1) - // Storage: Staking CounterForNominators (r:1 w:1) + /// Storage: NominationPools BondedPools (r:1 w:0) + /// Proof: NominationPools BondedPools (max_values: None, max_size: Some(164), added: 2639, mode: MaxEncodedLen) + /// Storage: Staking Bonded (r:1 w:0) + /// Proof: Staking Bonded (max_values: None, max_size: Some(72), added: 2547, mode: MaxEncodedLen) + /// Storage: Staking Ledger (r:1 w:0) + /// Proof: Staking Ledger (max_values: None, max_size: Some(1091), added: 3566, mode: MaxEncodedLen) + /// Storage: Staking MinNominatorBond (r:1 w:0) + /// Proof: Staking MinNominatorBond (max_values: Some(1), max_size: Some(16), added: 511, mode: MaxEncodedLen) + /// Storage: Staking Nominators (r:1 w:1) + /// Proof: Staking Nominators (max_values: None, max_size: Some(558), added: 3033, mode: MaxEncodedLen) + /// Storage: Staking MaxNominatorsCount (r:1 w:0) + /// Proof: Staking MaxNominatorsCount (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) + /// Storage: Staking Validators (r:17 w:0) + /// Proof: Staking Validators (max_values: None, max_size: Some(45), added: 2520, mode: MaxEncodedLen) + /// Storage: Staking CurrentEra (r:1 w:0) + /// Proof: Staking CurrentEra (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) + /// Storage: VoterList ListNodes (r:1 w:1) + /// Proof: VoterList ListNodes (max_values: None, max_size: Some(154), added: 2629, mode: MaxEncodedLen) + /// Storage: VoterList ListBags (r:1 w:1) + /// Proof: VoterList ListBags (max_values: None, max_size: Some(82), added: 2557, mode: MaxEncodedLen) + /// Storage: VoterList CounterForListNodes (r:1 w:1) + /// Proof: VoterList CounterForListNodes (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) + /// Storage: Staking CounterForNominators (r:1 w:1) + /// Proof: Staking CounterForNominators (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) /// The range of component `n` is `[1, 16]`. fn nominate(n: u32, ) -> Weight { - // Minimum execution time: 71_380 nanoseconds. - Weight::from_ref_time(71_060_388 as u64) - // Standard Error: 2_587 - .saturating_add(Weight::from_ref_time(1_185_729 as u64).saturating_mul(n as u64)) - .saturating_add(T::DbWeight::get().reads(12 as u64)) - .saturating_add(T::DbWeight::get().reads((1 as u64).saturating_mul(n as u64))) - .saturating_add(T::DbWeight::get().writes(5 as u64)) + // Proof Size summary in bytes: + // Measured: `1909` + // Estimated: `21998 + n * (2520 ±0)` + // Minimum execution time: 61_798 nanoseconds. + Weight::from_parts(61_504_758, 21998) + // Standard Error: 4_046 + .saturating_add(Weight::from_ref_time(1_159_175).saturating_mul(n.into())) + .saturating_add(T::DbWeight::get().reads(12_u64)) + .saturating_add(T::DbWeight::get().reads((1_u64).saturating_mul(n.into()))) + .saturating_add(T::DbWeight::get().writes(5_u64)) + .saturating_add(Weight::from_proof_size(2520).saturating_mul(n.into())) } - // Storage: NominationPools BondedPools (r:1 w:1) - // Storage: Staking Bonded (r:1 w:0) - // Storage: Staking Ledger (r:1 w:0) + /// Storage: NominationPools BondedPools (r:1 w:1) + /// Proof: NominationPools BondedPools (max_values: None, max_size: Some(164), added: 2639, mode: MaxEncodedLen) + /// Storage: Staking Bonded (r:1 w:0) + /// Proof: Staking Bonded (max_values: None, max_size: Some(72), added: 2547, mode: MaxEncodedLen) + /// Storage: Staking Ledger (r:1 w:0) + /// Proof: Staking Ledger (max_values: None, max_size: Some(1091), added: 3566, mode: MaxEncodedLen) fn set_state() -> Weight { - // Minimum execution time: 46_275 nanoseconds. - Weight::from_ref_time(46_689_000 as u64) - .saturating_add(T::DbWeight::get().reads(3 as u64)) - .saturating_add(T::DbWeight::get().writes(1 as u64)) + // Proof Size summary in bytes: + // Measured: `1498` + // Estimated: `8752` + // Minimum execution time: 32_433 nanoseconds. + Weight::from_parts(32_894_000, 8752) + .saturating_add(T::DbWeight::get().reads(3_u64)) + .saturating_add(T::DbWeight::get().writes(1_u64)) } - // Storage: NominationPools BondedPools (r:1 w:0) - // Storage: NominationPools Metadata (r:1 w:1) - // Storage: NominationPools CounterForMetadata (r:1 w:1) + /// Storage: NominationPools BondedPools (r:1 w:0) + /// Proof: NominationPools BondedPools (max_values: None, max_size: Some(164), added: 2639, mode: MaxEncodedLen) + /// Storage: NominationPools Metadata (r:1 w:1) + /// Proof: NominationPools Metadata (max_values: None, max_size: Some(270), added: 2745, mode: MaxEncodedLen) + /// Storage: NominationPools CounterForMetadata (r:1 w:1) + /// Proof: NominationPools CounterForMetadata (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) /// The range of component `n` is `[1, 256]`. fn set_metadata(n: u32, ) -> Weight { - // Minimum execution time: 19_246 nanoseconds. - Weight::from_ref_time(20_415_018 as u64) - // Standard Error: 95 - .saturating_add(Weight::from_ref_time(2_040 as u64).saturating_mul(n as u64)) - .saturating_add(T::DbWeight::get().reads(3 as u64)) - .saturating_add(T::DbWeight::get().writes(2 as u64)) + // Proof Size summary in bytes: + // Measured: `559` + // Estimated: `5883` + // Minimum execution time: 13_608 nanoseconds. + Weight::from_parts(13_966_346, 5883) + // Standard Error: 44 + .saturating_add(Weight::from_ref_time(1_511).saturating_mul(n.into())) + .saturating_add(T::DbWeight::get().reads(3_u64)) + .saturating_add(T::DbWeight::get().writes(2_u64)) } - // Storage: NominationPools MinJoinBond (r:0 w:1) - // Storage: NominationPools MaxPoolMembers (r:0 w:1) - // Storage: NominationPools MaxPoolMembersPerPool (r:0 w:1) - // Storage: NominationPools MinCreateBond (r:0 w:1) - // Storage: NominationPools MaxPools (r:0 w:1) + /// Storage: NominationPools MinJoinBond (r:0 w:1) + /// Proof: NominationPools MinJoinBond (max_values: Some(1), max_size: Some(16), added: 511, mode: MaxEncodedLen) + /// Storage: NominationPools MaxPoolMembers (r:0 w:1) + /// Proof: NominationPools MaxPoolMembers (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) + /// Storage: NominationPools MaxPoolMembersPerPool (r:0 w:1) + /// Proof: NominationPools MaxPoolMembersPerPool (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) + /// Storage: NominationPools MinCreateBond (r:0 w:1) + /// Proof: NominationPools MinCreateBond (max_values: Some(1), max_size: Some(16), added: 511, mode: MaxEncodedLen) + /// Storage: NominationPools MaxPools (r:0 w:1) + /// Proof: NominationPools MaxPools (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) fn set_configs() -> Weight { - // Minimum execution time: 9_231 nanoseconds. - Weight::from_ref_time(9_526_000 as u64) - .saturating_add(T::DbWeight::get().writes(5 as u64)) + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 5_832 nanoseconds. + Weight::from_ref_time(6_117_000) + .saturating_add(T::DbWeight::get().writes(5_u64)) } - // Storage: NominationPools BondedPools (r:1 w:1) + /// Storage: NominationPools BondedPools (r:1 w:1) + /// Proof: NominationPools BondedPools (max_values: None, max_size: Some(164), added: 2639, mode: MaxEncodedLen) fn update_roles() -> Weight { - // Minimum execution time: 31_246 nanoseconds. - Weight::from_ref_time(31_762_000 as u64) - .saturating_add(T::DbWeight::get().reads(1 as u64)) - .saturating_add(T::DbWeight::get().writes(1 as u64)) + // Proof Size summary in bytes: + // Measured: `559` + // Estimated: `2639` + // Minimum execution time: 18_160 nanoseconds. + Weight::from_parts(18_567_000, 2639) + .saturating_add(T::DbWeight::get().reads(1_u64)) + .saturating_add(T::DbWeight::get().writes(1_u64)) } - // Storage: NominationPools BondedPools (r:1 w:0) - // Storage: Staking Bonded (r:1 w:0) - // Storage: Staking Ledger (r:1 w:0) - // Storage: Staking Validators (r:1 w:0) - // Storage: Staking Nominators (r:1 w:1) - // Storage: Staking CounterForNominators (r:1 w:1) - // Storage: VoterList ListNodes (r:1 w:1) - // Storage: VoterList ListBags (r:1 w:1) - // Storage: VoterList CounterForListNodes (r:1 w:1) + /// Storage: NominationPools BondedPools (r:1 w:0) + /// Proof: NominationPools BondedPools (max_values: None, max_size: Some(164), added: 2639, mode: MaxEncodedLen) + /// Storage: Staking Bonded (r:1 w:0) + /// Proof: Staking Bonded (max_values: None, max_size: Some(72), added: 2547, mode: MaxEncodedLen) + /// Storage: Staking Ledger (r:1 w:0) + /// Proof: Staking Ledger (max_values: None, max_size: Some(1091), added: 3566, mode: MaxEncodedLen) + /// Storage: Staking Validators (r:1 w:0) + /// Proof: Staking Validators (max_values: None, max_size: Some(45), added: 2520, mode: MaxEncodedLen) + /// Storage: Staking Nominators (r:1 w:1) + /// Proof: Staking Nominators (max_values: None, max_size: Some(558), added: 3033, mode: MaxEncodedLen) + /// Storage: Staking CounterForNominators (r:1 w:1) + /// Proof: Staking CounterForNominators (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) + /// Storage: VoterList ListNodes (r:1 w:1) + /// Proof: VoterList ListNodes (max_values: None, max_size: Some(154), added: 2629, mode: MaxEncodedLen) + /// Storage: VoterList ListBags (r:1 w:1) + /// Proof: VoterList ListBags (max_values: None, max_size: Some(82), added: 2557, mode: MaxEncodedLen) + /// Storage: VoterList CounterForListNodes (r:1 w:1) + /// Proof: VoterList CounterForListNodes (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) fn chill() -> Weight { - // Minimum execution time: 73_812 nanoseconds. - Weight::from_ref_time(74_790_000 as u64) - .saturating_add(T::DbWeight::get().reads(9 as u64)) - .saturating_add(T::DbWeight::get().writes(5 as u64)) + // Proof Size summary in bytes: + // Measured: `2136` + // Estimated: `20489` + // Minimum execution time: 58_991 nanoseconds. + Weight::from_parts(59_528_000, 20489) + .saturating_add(T::DbWeight::get().reads(9_u64)) + .saturating_add(T::DbWeight::get().writes(5_u64)) } } // For backwards compatibility and tests impl WeightInfo for () { - // Storage: NominationPools MinJoinBond (r:1 w:0) - // Storage: NominationPools PoolMembers (r:1 w:1) - // Storage: NominationPools BondedPools (r:1 w:1) - // Storage: Staking Bonded (r:1 w:0) - // Storage: Staking Ledger (r:1 w:1) - // Storage: NominationPools RewardPools (r:1 w:1) - // Storage: System Account (r:2 w:1) - // Storage: NominationPools MaxPoolMembersPerPool (r:1 w:0) - // Storage: NominationPools MaxPoolMembers (r:1 w:0) - // Storage: NominationPools CounterForPoolMembers (r:1 w:1) - // Storage: Balances Locks (r:1 w:1) - // Storage: VoterList ListNodes (r:3 w:3) - // Storage: VoterList ListBags (r:2 w:2) + /// Storage: NominationPools MinJoinBond (r:1 w:0) + /// Proof: NominationPools MinJoinBond (max_values: Some(1), max_size: Some(16), added: 511, mode: MaxEncodedLen) + /// Storage: NominationPools PoolMembers (r:1 w:1) + /// Proof: NominationPools PoolMembers (max_values: None, max_size: Some(237), added: 2712, mode: MaxEncodedLen) + /// Storage: NominationPools BondedPools (r:1 w:1) + /// Proof: NominationPools BondedPools (max_values: None, max_size: Some(164), added: 2639, mode: MaxEncodedLen) + /// Storage: Staking Bonded (r:1 w:0) + /// Proof: Staking Bonded (max_values: None, max_size: Some(72), added: 2547, mode: MaxEncodedLen) + /// Storage: Staking Ledger (r:1 w:1) + /// Proof: Staking Ledger (max_values: None, max_size: Some(1091), added: 3566, mode: MaxEncodedLen) + /// Storage: NominationPools RewardPools (r:1 w:1) + /// Proof: NominationPools RewardPools (max_values: None, max_size: Some(60), added: 2535, mode: MaxEncodedLen) + /// Storage: System Account (r:2 w:1) + /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) + /// Storage: NominationPools MaxPoolMembersPerPool (r:1 w:0) + /// Proof: NominationPools MaxPoolMembersPerPool (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) + /// Storage: NominationPools MaxPoolMembers (r:1 w:0) + /// Proof: NominationPools MaxPoolMembers (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) + /// Storage: NominationPools CounterForPoolMembers (r:1 w:1) + /// Proof: NominationPools CounterForPoolMembers (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) + /// Storage: Balances Locks (r:1 w:1) + /// Proof: Balances Locks (max_values: None, max_size: Some(1299), added: 3774, mode: MaxEncodedLen) + /// Storage: VoterList ListNodes (r:3 w:3) + /// Proof: VoterList ListNodes (max_values: None, max_size: Some(154), added: 2629, mode: MaxEncodedLen) + /// Storage: VoterList ListBags (r:2 w:2) + /// Proof: VoterList ListBags (max_values: None, max_size: Some(82), added: 2557, mode: MaxEncodedLen) fn join() -> Weight { - // Minimum execution time: 159_948 nanoseconds. - Weight::from_ref_time(161_133_000 as u64) - .saturating_add(RocksDbWeight::get().reads(17 as u64)) - .saturating_add(RocksDbWeight::get().writes(12 as u64)) + // Proof Size summary in bytes: + // Measured: `3573` + // Estimated: `37988` + // Minimum execution time: 140_155 nanoseconds. + Weight::from_parts(141_098_000, 37988) + .saturating_add(RocksDbWeight::get().reads(17_u64)) + .saturating_add(RocksDbWeight::get().writes(12_u64)) } - // Storage: NominationPools PoolMembers (r:1 w:1) - // Storage: NominationPools BondedPools (r:1 w:1) - // Storage: NominationPools RewardPools (r:1 w:1) - // Storage: System Account (r:3 w:2) - // Storage: Staking Bonded (r:1 w:0) - // Storage: Staking Ledger (r:1 w:1) - // Storage: Balances Locks (r:1 w:1) - // Storage: VoterList ListNodes (r:3 w:3) - // Storage: VoterList ListBags (r:2 w:2) + /// Storage: NominationPools PoolMembers (r:1 w:1) + /// Proof: NominationPools PoolMembers (max_values: None, max_size: Some(237), added: 2712, mode: MaxEncodedLen) + /// Storage: NominationPools BondedPools (r:1 w:1) + /// Proof: NominationPools BondedPools (max_values: None, max_size: Some(164), added: 2639, mode: MaxEncodedLen) + /// Storage: NominationPools RewardPools (r:1 w:1) + /// Proof: NominationPools RewardPools (max_values: None, max_size: Some(60), added: 2535, mode: MaxEncodedLen) + /// Storage: System Account (r:3 w:2) + /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) + /// Storage: Staking Bonded (r:1 w:0) + /// Proof: Staking Bonded (max_values: None, max_size: Some(72), added: 2547, mode: MaxEncodedLen) + /// Storage: Staking Ledger (r:1 w:1) + /// Proof: Staking Ledger (max_values: None, max_size: Some(1091), added: 3566, mode: MaxEncodedLen) + /// Storage: Balances Locks (r:1 w:1) + /// Proof: Balances Locks (max_values: None, max_size: Some(1299), added: 3774, mode: MaxEncodedLen) + /// Storage: VoterList ListNodes (r:3 w:3) + /// Proof: VoterList ListNodes (max_values: None, max_size: Some(154), added: 2629, mode: MaxEncodedLen) + /// Storage: VoterList ListBags (r:2 w:2) + /// Proof: VoterList ListBags (max_values: None, max_size: Some(82), added: 2557, mode: MaxEncodedLen) fn bond_extra_transfer() -> Weight { - // Minimum execution time: 155_517 nanoseconds. - Weight::from_ref_time(159_101_000 as u64) - .saturating_add(RocksDbWeight::get().reads(14 as u64)) - .saturating_add(RocksDbWeight::get().writes(12 as u64)) + // Proof Size summary in bytes: + // Measured: `3615` + // Estimated: `38583` + // Minimum execution time: 136_048 nanoseconds. + Weight::from_parts(136_767_000, 38583) + .saturating_add(RocksDbWeight::get().reads(14_u64)) + .saturating_add(RocksDbWeight::get().writes(12_u64)) } - // Storage: NominationPools PoolMembers (r:1 w:1) - // Storage: NominationPools BondedPools (r:1 w:1) - // Storage: NominationPools RewardPools (r:1 w:1) - // Storage: System Account (r:3 w:3) - // Storage: Staking Bonded (r:1 w:0) - // Storage: Staking Ledger (r:1 w:1) - // Storage: Balances Locks (r:1 w:1) - // Storage: VoterList ListNodes (r:3 w:3) - // Storage: VoterList ListBags (r:2 w:2) + /// Storage: NominationPools PoolMembers (r:1 w:1) + /// Proof: NominationPools PoolMembers (max_values: None, max_size: Some(237), added: 2712, mode: MaxEncodedLen) + /// Storage: NominationPools BondedPools (r:1 w:1) + /// Proof: NominationPools BondedPools (max_values: None, max_size: Some(164), added: 2639, mode: MaxEncodedLen) + /// Storage: NominationPools RewardPools (r:1 w:1) + /// Proof: NominationPools RewardPools (max_values: None, max_size: Some(60), added: 2535, mode: MaxEncodedLen) + /// Storage: System Account (r:3 w:3) + /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) + /// Storage: Staking Bonded (r:1 w:0) + /// Proof: Staking Bonded (max_values: None, max_size: Some(72), added: 2547, mode: MaxEncodedLen) + /// Storage: Staking Ledger (r:1 w:1) + /// Proof: Staking Ledger (max_values: None, max_size: Some(1091), added: 3566, mode: MaxEncodedLen) + /// Storage: Balances Locks (r:1 w:1) + /// Proof: Balances Locks (max_values: None, max_size: Some(1299), added: 3774, mode: MaxEncodedLen) + /// Storage: VoterList ListNodes (r:3 w:3) + /// Proof: VoterList ListNodes (max_values: None, max_size: Some(154), added: 2629, mode: MaxEncodedLen) + /// Storage: VoterList ListBags (r:2 w:2) + /// Proof: VoterList ListBags (max_values: None, max_size: Some(82), added: 2557, mode: MaxEncodedLen) fn bond_extra_reward() -> Weight { - // Minimum execution time: 172_788 nanoseconds. - Weight::from_ref_time(174_212_000 as u64) - .saturating_add(RocksDbWeight::get().reads(14 as u64)) - .saturating_add(RocksDbWeight::get().writes(13 as u64)) + // Proof Size summary in bytes: + // Measured: `3615` + // Estimated: `38583` + // Minimum execution time: 151_473 nanoseconds. + Weight::from_parts(152_375_000, 38583) + .saturating_add(RocksDbWeight::get().reads(14_u64)) + .saturating_add(RocksDbWeight::get().writes(13_u64)) } - // Storage: NominationPools PoolMembers (r:1 w:1) - // Storage: NominationPools BondedPools (r:1 w:1) - // Storage: NominationPools RewardPools (r:1 w:1) - // Storage: System Account (r:1 w:1) + /// Storage: NominationPools PoolMembers (r:1 w:1) + /// Proof: NominationPools PoolMembers (max_values: None, max_size: Some(237), added: 2712, mode: MaxEncodedLen) + /// Storage: NominationPools BondedPools (r:1 w:1) + /// Proof: NominationPools BondedPools (max_values: None, max_size: Some(164), added: 2639, mode: MaxEncodedLen) + /// Storage: NominationPools RewardPools (r:1 w:1) + /// Proof: NominationPools RewardPools (max_values: None, max_size: Some(60), added: 2535, mode: MaxEncodedLen) + /// Storage: System Account (r:1 w:1) + /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) fn claim_payout() -> Weight { - // Minimum execution time: 64_560 nanoseconds. - Weight::from_ref_time(64_950_000 as u64) - .saturating_add(RocksDbWeight::get().reads(4 as u64)) - .saturating_add(RocksDbWeight::get().writes(4 as u64)) + // Proof Size summary in bytes: + // Measured: `1189` + // Estimated: `10489` + // Minimum execution time: 51_146 nanoseconds. + Weight::from_parts(51_570_000, 10489) + .saturating_add(RocksDbWeight::get().reads(4_u64)) + .saturating_add(RocksDbWeight::get().writes(4_u64)) } - // Storage: NominationPools PoolMembers (r:1 w:1) - // Storage: NominationPools BondedPools (r:1 w:1) - // Storage: NominationPools RewardPools (r:1 w:1) - // Storage: Staking Bonded (r:1 w:0) - // Storage: Staking Ledger (r:1 w:1) - // Storage: System Account (r:2 w:1) - // Storage: Staking CurrentEra (r:1 w:0) - // Storage: Staking Nominators (r:1 w:0) - // Storage: Staking MinNominatorBond (r:1 w:0) - // Storage: Balances Locks (r:1 w:1) - // Storage: VoterList ListNodes (r:3 w:3) - // Storage: VoterList ListBags (r:2 w:2) - // Storage: NominationPools SubPoolsStorage (r:1 w:1) - // Storage: NominationPools CounterForSubPoolsStorage (r:1 w:1) + /// Storage: NominationPools PoolMembers (r:1 w:1) + /// Proof: NominationPools PoolMembers (max_values: None, max_size: Some(237), added: 2712, mode: MaxEncodedLen) + /// Storage: NominationPools BondedPools (r:1 w:1) + /// Proof: NominationPools BondedPools (max_values: None, max_size: Some(164), added: 2639, mode: MaxEncodedLen) + /// Storage: NominationPools RewardPools (r:1 w:1) + /// Proof: NominationPools RewardPools (max_values: None, max_size: Some(60), added: 2535, mode: MaxEncodedLen) + /// Storage: Staking Bonded (r:1 w:0) + /// Proof: Staking Bonded (max_values: None, max_size: Some(72), added: 2547, mode: MaxEncodedLen) + /// Storage: Staking Ledger (r:1 w:1) + /// Proof: Staking Ledger (max_values: None, max_size: Some(1091), added: 3566, mode: MaxEncodedLen) + /// Storage: System Account (r:2 w:1) + /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) + /// Storage: Staking CurrentEra (r:1 w:0) + /// Proof: Staking CurrentEra (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) + /// Storage: Staking Nominators (r:1 w:0) + /// Proof: Staking Nominators (max_values: None, max_size: Some(558), added: 3033, mode: MaxEncodedLen) + /// Storage: Staking MinNominatorBond (r:1 w:0) + /// Proof: Staking MinNominatorBond (max_values: Some(1), max_size: Some(16), added: 511, mode: MaxEncodedLen) + /// Storage: Balances Locks (r:1 w:1) + /// Proof: Balances Locks (max_values: None, max_size: Some(1299), added: 3774, mode: MaxEncodedLen) + /// Storage: VoterList ListNodes (r:3 w:3) + /// Proof: VoterList ListNodes (max_values: None, max_size: Some(154), added: 2629, mode: MaxEncodedLen) + /// Storage: VoterList ListBags (r:2 w:2) + /// Proof: VoterList ListBags (max_values: None, max_size: Some(82), added: 2557, mode: MaxEncodedLen) + /// Storage: NominationPools SubPoolsStorage (r:1 w:1) + /// Proof: NominationPools SubPoolsStorage (max_values: None, max_size: Some(24382), added: 26857, mode: MaxEncodedLen) + /// Storage: NominationPools CounterForSubPoolsStorage (r:1 w:1) + /// Proof: NominationPools CounterForSubPoolsStorage (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) fn unbond() -> Weight { - // Minimum execution time: 161_398 nanoseconds. - Weight::from_ref_time(162_991_000 as u64) - .saturating_add(RocksDbWeight::get().reads(18 as u64)) - .saturating_add(RocksDbWeight::get().writes(13 as u64)) + // Proof Size summary in bytes: + // Measured: `3858` + // Estimated: `67379` + // Minimum execution time: 141_400 nanoseconds. + Weight::from_parts(141_822_000, 67379) + .saturating_add(RocksDbWeight::get().reads(18_u64)) + .saturating_add(RocksDbWeight::get().writes(13_u64)) } - // Storage: NominationPools BondedPools (r:1 w:0) - // Storage: Staking Bonded (r:1 w:0) - // Storage: Staking Ledger (r:1 w:1) - // Storage: Staking CurrentEra (r:1 w:0) - // Storage: Balances Locks (r:1 w:1) + /// Storage: NominationPools BondedPools (r:1 w:0) + /// Proof: NominationPools BondedPools (max_values: None, max_size: Some(164), added: 2639, mode: MaxEncodedLen) + /// Storage: Staking Bonded (r:1 w:0) + /// Proof: Staking Bonded (max_values: None, max_size: Some(72), added: 2547, mode: MaxEncodedLen) + /// Storage: Staking Ledger (r:1 w:1) + /// Proof: Staking Ledger (max_values: None, max_size: Some(1091), added: 3566, mode: MaxEncodedLen) + /// Storage: Staking CurrentEra (r:1 w:0) + /// Proof: Staking CurrentEra (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) + /// Storage: Balances Locks (r:1 w:1) + /// Proof: Balances Locks (max_values: None, max_size: Some(1299), added: 3774, mode: MaxEncodedLen) /// The range of component `s` is `[0, 100]`. fn pool_withdraw_unbonded(s: u32, ) -> Weight { - // Minimum execution time: 66_036 nanoseconds. - Weight::from_ref_time(67_183_304 as u64) - // Standard Error: 565 - .saturating_add(Weight::from_ref_time(57_830 as u64).saturating_mul(s as u64)) - .saturating_add(RocksDbWeight::get().reads(5 as u64)) - .saturating_add(RocksDbWeight::get().writes(2 as u64)) + // Proof Size summary in bytes: + // Measured: `1779` + // Estimated: `13025` + // Minimum execution time: 49_021 nanoseconds. + Weight::from_parts(49_954_282, 13025) + // Standard Error: 378 + .saturating_add(Weight::from_ref_time(5_165).saturating_mul(s.into())) + .saturating_add(RocksDbWeight::get().reads(5_u64)) + .saturating_add(RocksDbWeight::get().writes(2_u64)) } - // Storage: NominationPools PoolMembers (r:1 w:1) - // Storage: Staking CurrentEra (r:1 w:0) - // Storage: NominationPools BondedPools (r:1 w:1) - // Storage: NominationPools SubPoolsStorage (r:1 w:1) - // Storage: Staking Bonded (r:1 w:0) - // Storage: Staking Ledger (r:1 w:1) - // Storage: Balances Locks (r:1 w:1) - // Storage: System Account (r:1 w:1) - // Storage: NominationPools CounterForPoolMembers (r:1 w:1) + /// Storage: NominationPools PoolMembers (r:1 w:1) + /// Proof: NominationPools PoolMembers (max_values: None, max_size: Some(237), added: 2712, mode: MaxEncodedLen) + /// Storage: Staking CurrentEra (r:1 w:0) + /// Proof: Staking CurrentEra (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) + /// Storage: NominationPools BondedPools (r:1 w:1) + /// Proof: NominationPools BondedPools (max_values: None, max_size: Some(164), added: 2639, mode: MaxEncodedLen) + /// Storage: NominationPools SubPoolsStorage (r:1 w:1) + /// Proof: NominationPools SubPoolsStorage (max_values: None, max_size: Some(24382), added: 26857, mode: MaxEncodedLen) + /// Storage: Staking Bonded (r:1 w:0) + /// Proof: Staking Bonded (max_values: None, max_size: Some(72), added: 2547, mode: MaxEncodedLen) + /// Storage: Staking Ledger (r:1 w:1) + /// Proof: Staking Ledger (max_values: None, max_size: Some(1091), added: 3566, mode: MaxEncodedLen) + /// Storage: Balances Locks (r:1 w:1) + /// Proof: Balances Locks (max_values: None, max_size: Some(1299), added: 3774, mode: MaxEncodedLen) + /// Storage: System Account (r:1 w:1) + /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) + /// Storage: NominationPools CounterForPoolMembers (r:1 w:1) + /// Proof: NominationPools CounterForPoolMembers (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) /// The range of component `s` is `[0, 100]`. fn withdraw_unbonded_update(s: u32, ) -> Weight { - // Minimum execution time: 111_156 nanoseconds. - Weight::from_ref_time(112_507_059 as u64) - // Standard Error: 655 - .saturating_add(Weight::from_ref_time(53_711 as u64).saturating_mul(s as u64)) - .saturating_add(RocksDbWeight::get().reads(9 as u64)) - .saturating_add(RocksDbWeight::get().writes(7 as u64)) + // Proof Size summary in bytes: + // Measured: `2303` + // Estimated: `45696` + // Minimum execution time: 92_473 nanoseconds. + Weight::from_parts(93_901_972, 45696) + // Standard Error: 618 + .saturating_add(Weight::from_ref_time(12_032).saturating_mul(s.into())) + .saturating_add(RocksDbWeight::get().reads(9_u64)) + .saturating_add(RocksDbWeight::get().writes(7_u64)) } - // Storage: NominationPools PoolMembers (r:1 w:1) - // Storage: Staking CurrentEra (r:1 w:0) - // Storage: NominationPools BondedPools (r:1 w:1) - // Storage: NominationPools SubPoolsStorage (r:1 w:1) - // Storage: Staking Bonded (r:1 w:1) - // Storage: Staking Ledger (r:1 w:1) - // Storage: Staking SlashingSpans (r:1 w:0) - // Storage: Staking Validators (r:1 w:0) - // Storage: Staking Nominators (r:1 w:0) - // Storage: System Account (r:2 w:2) - // Storage: Balances Locks (r:1 w:1) - // Storage: NominationPools CounterForPoolMembers (r:1 w:1) - // Storage: NominationPools ReversePoolIdLookup (r:1 w:1) - // Storage: NominationPools CounterForReversePoolIdLookup (r:1 w:1) - // Storage: NominationPools RewardPools (r:1 w:1) - // Storage: NominationPools CounterForRewardPools (r:1 w:1) - // Storage: NominationPools CounterForSubPoolsStorage (r:1 w:1) - // Storage: NominationPools Metadata (r:1 w:1) - // Storage: NominationPools CounterForBondedPools (r:1 w:1) - // Storage: Staking Payee (r:0 w:1) + /// Storage: NominationPools PoolMembers (r:1 w:1) + /// Proof: NominationPools PoolMembers (max_values: None, max_size: Some(237), added: 2712, mode: MaxEncodedLen) + /// Storage: Staking CurrentEra (r:1 w:0) + /// Proof: Staking CurrentEra (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) + /// Storage: NominationPools BondedPools (r:1 w:1) + /// Proof: NominationPools BondedPools (max_values: None, max_size: Some(164), added: 2639, mode: MaxEncodedLen) + /// Storage: NominationPools SubPoolsStorage (r:1 w:1) + /// Proof: NominationPools SubPoolsStorage (max_values: None, max_size: Some(24382), added: 26857, mode: MaxEncodedLen) + /// Storage: Staking Bonded (r:1 w:1) + /// Proof: Staking Bonded (max_values: None, max_size: Some(72), added: 2547, mode: MaxEncodedLen) + /// Storage: Staking Ledger (r:1 w:1) + /// Proof: Staking Ledger (max_values: None, max_size: Some(1091), added: 3566, mode: MaxEncodedLen) + /// Storage: Staking SlashingSpans (r:1 w:0) + /// Proof Skipped: Staking SlashingSpans (max_values: None, max_size: None, mode: Measured) + /// Storage: Staking Validators (r:1 w:0) + /// Proof: Staking Validators (max_values: None, max_size: Some(45), added: 2520, mode: MaxEncodedLen) + /// Storage: Staking Nominators (r:1 w:0) + /// Proof: Staking Nominators (max_values: None, max_size: Some(558), added: 3033, mode: MaxEncodedLen) + /// Storage: System Account (r:2 w:2) + /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) + /// Storage: Balances Locks (r:1 w:1) + /// Proof: Balances Locks (max_values: None, max_size: Some(1299), added: 3774, mode: MaxEncodedLen) + /// Storage: NominationPools CounterForPoolMembers (r:1 w:1) + /// Proof: NominationPools CounterForPoolMembers (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) + /// Storage: NominationPools ReversePoolIdLookup (r:1 w:1) + /// Proof: NominationPools ReversePoolIdLookup (max_values: None, max_size: Some(44), added: 2519, mode: MaxEncodedLen) + /// Storage: NominationPools CounterForReversePoolIdLookup (r:1 w:1) + /// Proof: NominationPools CounterForReversePoolIdLookup (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) + /// Storage: NominationPools RewardPools (r:1 w:1) + /// Proof: NominationPools RewardPools (max_values: None, max_size: Some(60), added: 2535, mode: MaxEncodedLen) + /// Storage: NominationPools CounterForRewardPools (r:1 w:1) + /// Proof: NominationPools CounterForRewardPools (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) + /// Storage: NominationPools CounterForSubPoolsStorage (r:1 w:1) + /// Proof: NominationPools CounterForSubPoolsStorage (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) + /// Storage: NominationPools Metadata (r:1 w:1) + /// Proof: NominationPools Metadata (max_values: None, max_size: Some(270), added: 2745, mode: MaxEncodedLen) + /// Storage: NominationPools CounterForBondedPools (r:1 w:1) + /// Proof: NominationPools CounterForBondedPools (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) + /// Storage: Staking Payee (r:0 w:1) + /// Proof: Staking Payee (max_values: None, max_size: Some(73), added: 2548, mode: MaxEncodedLen) /// The range of component `s` is `[0, 100]`. - fn withdraw_unbonded_kill(s: u32, ) -> Weight { - // Minimum execution time: 168_270 nanoseconds. - Weight::from_ref_time(170_059_380 as u64) - // Standard Error: 1_506 - .saturating_add(Weight::from_ref_time(1_258 as u64).saturating_mul(s as u64)) - .saturating_add(RocksDbWeight::get().reads(20 as u64)) - .saturating_add(RocksDbWeight::get().writes(17 as u64)) + fn withdraw_unbonded_kill(_s: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `2690` + // Estimated: `68812` + // Minimum execution time: 150_063 nanoseconds. + Weight::from_parts(152_321_387, 68812) + .saturating_add(RocksDbWeight::get().reads(20_u64)) + .saturating_add(RocksDbWeight::get().writes(17_u64)) } - // Storage: NominationPools LastPoolId (r:1 w:1) - // Storage: Staking MinNominatorBond (r:1 w:0) - // Storage: NominationPools MinCreateBond (r:1 w:0) - // Storage: NominationPools MinJoinBond (r:1 w:0) - // Storage: NominationPools MaxPools (r:1 w:0) - // Storage: NominationPools CounterForBondedPools (r:1 w:1) - // Storage: NominationPools PoolMembers (r:1 w:1) - // Storage: NominationPools MaxPoolMembersPerPool (r:1 w:0) - // Storage: NominationPools MaxPoolMembers (r:1 w:0) - // Storage: NominationPools CounterForPoolMembers (r:1 w:1) - // Storage: System Account (r:2 w:2) - // Storage: Staking Bonded (r:1 w:1) - // Storage: Staking Ledger (r:1 w:1) - // Storage: Staking CurrentEra (r:1 w:0) - // Storage: Balances Locks (r:1 w:1) - // Storage: NominationPools RewardPools (r:1 w:1) - // Storage: NominationPools CounterForRewardPools (r:1 w:1) - // Storage: NominationPools ReversePoolIdLookup (r:1 w:1) - // Storage: NominationPools CounterForReversePoolIdLookup (r:1 w:1) - // Storage: NominationPools BondedPools (r:1 w:1) - // Storage: Staking Payee (r:0 w:1) + /// Storage: NominationPools LastPoolId (r:1 w:1) + /// Proof: NominationPools LastPoolId (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) + /// Storage: Staking MinNominatorBond (r:1 w:0) + /// Proof: Staking MinNominatorBond (max_values: Some(1), max_size: Some(16), added: 511, mode: MaxEncodedLen) + /// Storage: NominationPools MinCreateBond (r:1 w:0) + /// Proof: NominationPools MinCreateBond (max_values: Some(1), max_size: Some(16), added: 511, mode: MaxEncodedLen) + /// Storage: NominationPools MinJoinBond (r:1 w:0) + /// Proof: NominationPools MinJoinBond (max_values: Some(1), max_size: Some(16), added: 511, mode: MaxEncodedLen) + /// Storage: NominationPools MaxPools (r:1 w:0) + /// Proof: NominationPools MaxPools (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) + /// Storage: NominationPools CounterForBondedPools (r:1 w:1) + /// Proof: NominationPools CounterForBondedPools (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) + /// Storage: NominationPools PoolMembers (r:1 w:1) + /// Proof: NominationPools PoolMembers (max_values: None, max_size: Some(237), added: 2712, mode: MaxEncodedLen) + /// Storage: NominationPools MaxPoolMembersPerPool (r:1 w:0) + /// Proof: NominationPools MaxPoolMembersPerPool (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) + /// Storage: NominationPools MaxPoolMembers (r:1 w:0) + /// Proof: NominationPools MaxPoolMembers (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) + /// Storage: NominationPools CounterForPoolMembers (r:1 w:1) + /// Proof: NominationPools CounterForPoolMembers (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) + /// Storage: System Account (r:2 w:2) + /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) + /// Storage: Staking Bonded (r:1 w:1) + /// Proof: Staking Bonded (max_values: None, max_size: Some(72), added: 2547, mode: MaxEncodedLen) + /// Storage: Staking Ledger (r:1 w:1) + /// Proof: Staking Ledger (max_values: None, max_size: Some(1091), added: 3566, mode: MaxEncodedLen) + /// Storage: Staking CurrentEra (r:1 w:0) + /// Proof: Staking CurrentEra (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) + /// Storage: Balances Locks (r:1 w:1) + /// Proof: Balances Locks (max_values: None, max_size: Some(1299), added: 3774, mode: MaxEncodedLen) + /// Storage: NominationPools RewardPools (r:1 w:1) + /// Proof: NominationPools RewardPools (max_values: None, max_size: Some(60), added: 2535, mode: MaxEncodedLen) + /// Storage: NominationPools CounterForRewardPools (r:1 w:1) + /// Proof: NominationPools CounterForRewardPools (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) + /// Storage: NominationPools ReversePoolIdLookup (r:1 w:1) + /// Proof: NominationPools ReversePoolIdLookup (max_values: None, max_size: Some(44), added: 2519, mode: MaxEncodedLen) + /// Storage: NominationPools CounterForReversePoolIdLookup (r:1 w:1) + /// Proof: NominationPools CounterForReversePoolIdLookup (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) + /// Storage: NominationPools BondedPools (r:1 w:1) + /// Proof: NominationPools BondedPools (max_values: None, max_size: Some(164), added: 2639, mode: MaxEncodedLen) + /// Storage: Staking Payee (r:0 w:1) + /// Proof: Staking Payee (max_values: None, max_size: Some(73), added: 2548, mode: MaxEncodedLen) fn create() -> Weight { - // Minimum execution time: 146_153 nanoseconds. - Weight::from_ref_time(146_955_000 as u64) - .saturating_add(RocksDbWeight::get().reads(21 as u64)) - .saturating_add(RocksDbWeight::get().writes(15 as u64)) + // Proof Size summary in bytes: + // Measured: `1321` + // Estimated: `31522` + // Minimum execution time: 131_430 nanoseconds. + Weight::from_parts(132_214_000, 31522) + .saturating_add(RocksDbWeight::get().reads(21_u64)) + .saturating_add(RocksDbWeight::get().writes(15_u64)) } - // Storage: NominationPools BondedPools (r:1 w:0) - // Storage: Staking Bonded (r:1 w:0) - // Storage: Staking Ledger (r:1 w:0) - // Storage: Staking MinNominatorBond (r:1 w:0) - // Storage: Staking Nominators (r:1 w:1) - // Storage: Staking MaxNominatorsCount (r:1 w:0) - // Storage: Staking Validators (r:2 w:0) - // Storage: Staking CurrentEra (r:1 w:0) - // Storage: VoterList ListNodes (r:1 w:1) - // Storage: VoterList ListBags (r:1 w:1) - // Storage: VoterList CounterForListNodes (r:1 w:1) - // Storage: Staking CounterForNominators (r:1 w:1) + /// Storage: NominationPools BondedPools (r:1 w:0) + /// Proof: NominationPools BondedPools (max_values: None, max_size: Some(164), added: 2639, mode: MaxEncodedLen) + /// Storage: Staking Bonded (r:1 w:0) + /// Proof: Staking Bonded (max_values: None, max_size: Some(72), added: 2547, mode: MaxEncodedLen) + /// Storage: Staking Ledger (r:1 w:0) + /// Proof: Staking Ledger (max_values: None, max_size: Some(1091), added: 3566, mode: MaxEncodedLen) + /// Storage: Staking MinNominatorBond (r:1 w:0) + /// Proof: Staking MinNominatorBond (max_values: Some(1), max_size: Some(16), added: 511, mode: MaxEncodedLen) + /// Storage: Staking Nominators (r:1 w:1) + /// Proof: Staking Nominators (max_values: None, max_size: Some(558), added: 3033, mode: MaxEncodedLen) + /// Storage: Staking MaxNominatorsCount (r:1 w:0) + /// Proof: Staking MaxNominatorsCount (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) + /// Storage: Staking Validators (r:17 w:0) + /// Proof: Staking Validators (max_values: None, max_size: Some(45), added: 2520, mode: MaxEncodedLen) + /// Storage: Staking CurrentEra (r:1 w:0) + /// Proof: Staking CurrentEra (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) + /// Storage: VoterList ListNodes (r:1 w:1) + /// Proof: VoterList ListNodes (max_values: None, max_size: Some(154), added: 2629, mode: MaxEncodedLen) + /// Storage: VoterList ListBags (r:1 w:1) + /// Proof: VoterList ListBags (max_values: None, max_size: Some(82), added: 2557, mode: MaxEncodedLen) + /// Storage: VoterList CounterForListNodes (r:1 w:1) + /// Proof: VoterList CounterForListNodes (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) + /// Storage: Staking CounterForNominators (r:1 w:1) + /// Proof: Staking CounterForNominators (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) /// The range of component `n` is `[1, 16]`. fn nominate(n: u32, ) -> Weight { - // Minimum execution time: 71_380 nanoseconds. - Weight::from_ref_time(71_060_388 as u64) - // Standard Error: 2_587 - .saturating_add(Weight::from_ref_time(1_185_729 as u64).saturating_mul(n as u64)) - .saturating_add(RocksDbWeight::get().reads(12 as u64)) - .saturating_add(RocksDbWeight::get().reads((1 as u64).saturating_mul(n as u64))) - .saturating_add(RocksDbWeight::get().writes(5 as u64)) + // Proof Size summary in bytes: + // Measured: `1909` + // Estimated: `21998 + n * (2520 ±0)` + // Minimum execution time: 61_798 nanoseconds. + Weight::from_parts(61_504_758, 21998) + // Standard Error: 4_046 + .saturating_add(Weight::from_ref_time(1_159_175).saturating_mul(n.into())) + .saturating_add(RocksDbWeight::get().reads(12_u64)) + .saturating_add(RocksDbWeight::get().reads((1_u64).saturating_mul(n.into()))) + .saturating_add(RocksDbWeight::get().writes(5_u64)) + .saturating_add(Weight::from_proof_size(2520).saturating_mul(n.into())) } - // Storage: NominationPools BondedPools (r:1 w:1) - // Storage: Staking Bonded (r:1 w:0) - // Storage: Staking Ledger (r:1 w:0) + /// Storage: NominationPools BondedPools (r:1 w:1) + /// Proof: NominationPools BondedPools (max_values: None, max_size: Some(164), added: 2639, mode: MaxEncodedLen) + /// Storage: Staking Bonded (r:1 w:0) + /// Proof: Staking Bonded (max_values: None, max_size: Some(72), added: 2547, mode: MaxEncodedLen) + /// Storage: Staking Ledger (r:1 w:0) + /// Proof: Staking Ledger (max_values: None, max_size: Some(1091), added: 3566, mode: MaxEncodedLen) fn set_state() -> Weight { - // Minimum execution time: 46_275 nanoseconds. - Weight::from_ref_time(46_689_000 as u64) - .saturating_add(RocksDbWeight::get().reads(3 as u64)) - .saturating_add(RocksDbWeight::get().writes(1 as u64)) + // Proof Size summary in bytes: + // Measured: `1498` + // Estimated: `8752` + // Minimum execution time: 32_433 nanoseconds. + Weight::from_parts(32_894_000, 8752) + .saturating_add(RocksDbWeight::get().reads(3_u64)) + .saturating_add(RocksDbWeight::get().writes(1_u64)) } - // Storage: NominationPools BondedPools (r:1 w:0) - // Storage: NominationPools Metadata (r:1 w:1) - // Storage: NominationPools CounterForMetadata (r:1 w:1) + /// Storage: NominationPools BondedPools (r:1 w:0) + /// Proof: NominationPools BondedPools (max_values: None, max_size: Some(164), added: 2639, mode: MaxEncodedLen) + /// Storage: NominationPools Metadata (r:1 w:1) + /// Proof: NominationPools Metadata (max_values: None, max_size: Some(270), added: 2745, mode: MaxEncodedLen) + /// Storage: NominationPools CounterForMetadata (r:1 w:1) + /// Proof: NominationPools CounterForMetadata (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) /// The range of component `n` is `[1, 256]`. fn set_metadata(n: u32, ) -> Weight { - // Minimum execution time: 19_246 nanoseconds. - Weight::from_ref_time(20_415_018 as u64) - // Standard Error: 95 - .saturating_add(Weight::from_ref_time(2_040 as u64).saturating_mul(n as u64)) - .saturating_add(RocksDbWeight::get().reads(3 as u64)) - .saturating_add(RocksDbWeight::get().writes(2 as u64)) + // Proof Size summary in bytes: + // Measured: `559` + // Estimated: `5883` + // Minimum execution time: 13_608 nanoseconds. + Weight::from_parts(13_966_346, 5883) + // Standard Error: 44 + .saturating_add(Weight::from_ref_time(1_511).saturating_mul(n.into())) + .saturating_add(RocksDbWeight::get().reads(3_u64)) + .saturating_add(RocksDbWeight::get().writes(2_u64)) } - // Storage: NominationPools MinJoinBond (r:0 w:1) - // Storage: NominationPools MaxPoolMembers (r:0 w:1) - // Storage: NominationPools MaxPoolMembersPerPool (r:0 w:1) - // Storage: NominationPools MinCreateBond (r:0 w:1) - // Storage: NominationPools MaxPools (r:0 w:1) + /// Storage: NominationPools MinJoinBond (r:0 w:1) + /// Proof: NominationPools MinJoinBond (max_values: Some(1), max_size: Some(16), added: 511, mode: MaxEncodedLen) + /// Storage: NominationPools MaxPoolMembers (r:0 w:1) + /// Proof: NominationPools MaxPoolMembers (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) + /// Storage: NominationPools MaxPoolMembersPerPool (r:0 w:1) + /// Proof: NominationPools MaxPoolMembersPerPool (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) + /// Storage: NominationPools MinCreateBond (r:0 w:1) + /// Proof: NominationPools MinCreateBond (max_values: Some(1), max_size: Some(16), added: 511, mode: MaxEncodedLen) + /// Storage: NominationPools MaxPools (r:0 w:1) + /// Proof: NominationPools MaxPools (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) fn set_configs() -> Weight { - // Minimum execution time: 9_231 nanoseconds. - Weight::from_ref_time(9_526_000 as u64) - .saturating_add(RocksDbWeight::get().writes(5 as u64)) + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 5_832 nanoseconds. + Weight::from_ref_time(6_117_000) + .saturating_add(RocksDbWeight::get().writes(5_u64)) } - // Storage: NominationPools BondedPools (r:1 w:1) + /// Storage: NominationPools BondedPools (r:1 w:1) + /// Proof: NominationPools BondedPools (max_values: None, max_size: Some(164), added: 2639, mode: MaxEncodedLen) fn update_roles() -> Weight { - // Minimum execution time: 31_246 nanoseconds. - Weight::from_ref_time(31_762_000 as u64) - .saturating_add(RocksDbWeight::get().reads(1 as u64)) - .saturating_add(RocksDbWeight::get().writes(1 as u64)) + // Proof Size summary in bytes: + // Measured: `559` + // Estimated: `2639` + // Minimum execution time: 18_160 nanoseconds. + Weight::from_parts(18_567_000, 2639) + .saturating_add(RocksDbWeight::get().reads(1_u64)) + .saturating_add(RocksDbWeight::get().writes(1_u64)) } - // Storage: NominationPools BondedPools (r:1 w:0) - // Storage: Staking Bonded (r:1 w:0) - // Storage: Staking Ledger (r:1 w:0) - // Storage: Staking Validators (r:1 w:0) - // Storage: Staking Nominators (r:1 w:1) - // Storage: Staking CounterForNominators (r:1 w:1) - // Storage: VoterList ListNodes (r:1 w:1) - // Storage: VoterList ListBags (r:1 w:1) - // Storage: VoterList CounterForListNodes (r:1 w:1) + /// Storage: NominationPools BondedPools (r:1 w:0) + /// Proof: NominationPools BondedPools (max_values: None, max_size: Some(164), added: 2639, mode: MaxEncodedLen) + /// Storage: Staking Bonded (r:1 w:0) + /// Proof: Staking Bonded (max_values: None, max_size: Some(72), added: 2547, mode: MaxEncodedLen) + /// Storage: Staking Ledger (r:1 w:0) + /// Proof: Staking Ledger (max_values: None, max_size: Some(1091), added: 3566, mode: MaxEncodedLen) + /// Storage: Staking Validators (r:1 w:0) + /// Proof: Staking Validators (max_values: None, max_size: Some(45), added: 2520, mode: MaxEncodedLen) + /// Storage: Staking Nominators (r:1 w:1) + /// Proof: Staking Nominators (max_values: None, max_size: Some(558), added: 3033, mode: MaxEncodedLen) + /// Storage: Staking CounterForNominators (r:1 w:1) + /// Proof: Staking CounterForNominators (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) + /// Storage: VoterList ListNodes (r:1 w:1) + /// Proof: VoterList ListNodes (max_values: None, max_size: Some(154), added: 2629, mode: MaxEncodedLen) + /// Storage: VoterList ListBags (r:1 w:1) + /// Proof: VoterList ListBags (max_values: None, max_size: Some(82), added: 2557, mode: MaxEncodedLen) + /// Storage: VoterList CounterForListNodes (r:1 w:1) + /// Proof: VoterList CounterForListNodes (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) fn chill() -> Weight { - // Minimum execution time: 73_812 nanoseconds. - Weight::from_ref_time(74_790_000 as u64) - .saturating_add(RocksDbWeight::get().reads(9 as u64)) - .saturating_add(RocksDbWeight::get().writes(5 as u64)) + // Proof Size summary in bytes: + // Measured: `2136` + // Estimated: `20489` + // Minimum execution time: 58_991 nanoseconds. + Weight::from_parts(59_528_000, 20489) + .saturating_add(RocksDbWeight::get().reads(9_u64)) + .saturating_add(RocksDbWeight::get().writes(5_u64)) } } diff --git a/frame/preimage/src/weights.rs b/frame/preimage/src/weights.rs index e73c98689..394c76956 100644 --- a/frame/preimage/src/weights.rs +++ b/frame/preimage/src/weights.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2022 Parity Technologies (UK) Ltd. +// Copyright (C) 2023 Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); @@ -18,7 +18,8 @@ //! Autogenerated weights for pallet_preimage //! //! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev -//! DATE: 2022-11-07, STEPS: `50`, REPEAT: 20, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! DATE: 2023-01-24, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! WORST CASE MAP SIZE: `1000000` //! HOSTNAME: `bm2`, CPU: `Intel(R) Core(TM) i7-7700K CPU @ 4.20GHz` //! EXECUTION: Some(Wasm), WASM-EXECUTION: Compiled, CHAIN: Some("dev"), DB CACHE: 1024 @@ -64,206 +65,314 @@ pub trait WeightInfo { /// Weights for pallet_preimage using the Substrate node and recommended hardware. pub struct SubstrateWeight(PhantomData); impl WeightInfo for SubstrateWeight { - // Storage: Preimage StatusFor (r:1 w:1) - // Storage: Preimage PreimageFor (r:0 w:1) + /// Storage: Preimage StatusFor (r:1 w:1) + /// Proof: Preimage StatusFor (max_values: None, max_size: Some(91), added: 2566, mode: MaxEncodedLen) + /// Storage: Preimage PreimageFor (r:0 w:1) + /// Proof: Preimage PreimageFor (max_values: None, max_size: Some(4194344), added: 4196819, mode: MaxEncodedLen) /// The range of component `s` is `[0, 4194304]`. fn note_preimage(s: u32, ) -> Weight { - // Minimum execution time: 33_810 nanoseconds. - Weight::from_ref_time(34_299_000 as u64) + // Proof Size summary in bytes: + // Measured: `175` + // Estimated: `2566` + // Minimum execution time: 23_484 nanoseconds. + Weight::from_parts(23_828_000, 2566) // Standard Error: 0 - .saturating_add(Weight::from_ref_time(1_703 as u64).saturating_mul(s as u64)) - .saturating_add(T::DbWeight::get().reads(1 as u64)) - .saturating_add(T::DbWeight::get().writes(2 as u64)) + .saturating_add(Weight::from_ref_time(1_705).saturating_mul(s.into())) + .saturating_add(T::DbWeight::get().reads(1_u64)) + .saturating_add(T::DbWeight::get().writes(2_u64)) } - // Storage: Preimage StatusFor (r:1 w:1) - // Storage: Preimage PreimageFor (r:0 w:1) + /// Storage: Preimage StatusFor (r:1 w:1) + /// Proof: Preimage StatusFor (max_values: None, max_size: Some(91), added: 2566, mode: MaxEncodedLen) + /// Storage: Preimage PreimageFor (r:0 w:1) + /// Proof: Preimage PreimageFor (max_values: None, max_size: Some(4194344), added: 4196819, mode: MaxEncodedLen) /// The range of component `s` is `[0, 4194304]`. fn note_requested_preimage(s: u32, ) -> Weight { - // Minimum execution time: 24_398 nanoseconds. - Weight::from_ref_time(24_839_000 as u64) + // Proof Size summary in bytes: + // Measured: `106` + // Estimated: `2566` + // Minimum execution time: 14_812 nanoseconds. + Weight::from_parts(14_949_000, 2566) // Standard Error: 0 - .saturating_add(Weight::from_ref_time(1_702 as u64).saturating_mul(s as u64)) - .saturating_add(T::DbWeight::get().reads(1 as u64)) - .saturating_add(T::DbWeight::get().writes(2 as u64)) + .saturating_add(Weight::from_ref_time(1_707).saturating_mul(s.into())) + .saturating_add(T::DbWeight::get().reads(1_u64)) + .saturating_add(T::DbWeight::get().writes(2_u64)) } - // Storage: Preimage StatusFor (r:1 w:1) - // Storage: Preimage PreimageFor (r:0 w:1) + /// Storage: Preimage StatusFor (r:1 w:1) + /// Proof: Preimage StatusFor (max_values: None, max_size: Some(91), added: 2566, mode: MaxEncodedLen) + /// Storage: Preimage PreimageFor (r:0 w:1) + /// Proof: Preimage PreimageFor (max_values: None, max_size: Some(4194344), added: 4196819, mode: MaxEncodedLen) /// The range of component `s` is `[0, 4194304]`. fn note_no_deposit_preimage(s: u32, ) -> Weight { - // Minimum execution time: 22_235 nanoseconds. - Weight::from_ref_time(22_473_000 as u64) + // Proof Size summary in bytes: + // Measured: `106` + // Estimated: `2566` + // Minimum execution time: 14_185 nanoseconds. + Weight::from_parts(14_398_000, 2566) // Standard Error: 0 - .saturating_add(Weight::from_ref_time(1_703 as u64).saturating_mul(s as u64)) - .saturating_add(T::DbWeight::get().reads(1 as u64)) - .saturating_add(T::DbWeight::get().writes(2 as u64)) + .saturating_add(Weight::from_ref_time(1_709).saturating_mul(s.into())) + .saturating_add(T::DbWeight::get().reads(1_u64)) + .saturating_add(T::DbWeight::get().writes(2_u64)) } - // Storage: Preimage StatusFor (r:1 w:1) - // Storage: Preimage PreimageFor (r:0 w:1) + /// Storage: Preimage StatusFor (r:1 w:1) + /// Proof: Preimage StatusFor (max_values: None, max_size: Some(91), added: 2566, mode: MaxEncodedLen) + /// Storage: Preimage PreimageFor (r:0 w:1) + /// Proof: Preimage PreimageFor (max_values: None, max_size: Some(4194344), added: 4196819, mode: MaxEncodedLen) fn unnote_preimage() -> Weight { - // Minimum execution time: 43_241 nanoseconds. - Weight::from_ref_time(44_470_000 as u64) - .saturating_add(T::DbWeight::get().reads(1 as u64)) - .saturating_add(T::DbWeight::get().writes(2 as u64)) + // Proof Size summary in bytes: + // Measured: `353` + // Estimated: `2566` + // Minimum execution time: 29_917 nanoseconds. + Weight::from_parts(30_691_000, 2566) + .saturating_add(T::DbWeight::get().reads(1_u64)) + .saturating_add(T::DbWeight::get().writes(2_u64)) } - // Storage: Preimage StatusFor (r:1 w:1) - // Storage: Preimage PreimageFor (r:0 w:1) + /// Storage: Preimage StatusFor (r:1 w:1) + /// Proof: Preimage StatusFor (max_values: None, max_size: Some(91), added: 2566, mode: MaxEncodedLen) + /// Storage: Preimage PreimageFor (r:0 w:1) + /// Proof: Preimage PreimageFor (max_values: None, max_size: Some(4194344), added: 4196819, mode: MaxEncodedLen) fn unnote_no_deposit_preimage() -> Weight { - // Minimum execution time: 29_529 nanoseconds. - Weight::from_ref_time(30_364_000 as u64) - .saturating_add(T::DbWeight::get().reads(1 as u64)) - .saturating_add(T::DbWeight::get().writes(2 as u64)) + // Proof Size summary in bytes: + // Measured: `144` + // Estimated: `2566` + // Minimum execution time: 19_281 nanoseconds. + Weight::from_parts(20_121_000, 2566) + .saturating_add(T::DbWeight::get().reads(1_u64)) + .saturating_add(T::DbWeight::get().writes(2_u64)) } - // Storage: Preimage StatusFor (r:1 w:1) + /// Storage: Preimage StatusFor (r:1 w:1) + /// Proof: Preimage StatusFor (max_values: None, max_size: Some(91), added: 2566, mode: MaxEncodedLen) fn request_preimage() -> Weight { - // Minimum execution time: 28_914 nanoseconds. - Weight::from_ref_time(30_103_000 as u64) - .saturating_add(T::DbWeight::get().reads(1 as u64)) - .saturating_add(T::DbWeight::get().writes(1 as u64)) + // Proof Size summary in bytes: + // Measured: `220` + // Estimated: `2566` + // Minimum execution time: 17_192 nanoseconds. + Weight::from_parts(18_637_000, 2566) + .saturating_add(T::DbWeight::get().reads(1_u64)) + .saturating_add(T::DbWeight::get().writes(1_u64)) } - // Storage: Preimage StatusFor (r:1 w:1) + /// Storage: Preimage StatusFor (r:1 w:1) + /// Proof: Preimage StatusFor (max_values: None, max_size: Some(91), added: 2566, mode: MaxEncodedLen) fn request_no_deposit_preimage() -> Weight { - // Minimum execution time: 14_479 nanoseconds. - Weight::from_ref_time(15_244_000 as u64) - .saturating_add(T::DbWeight::get().reads(1 as u64)) - .saturating_add(T::DbWeight::get().writes(1 as u64)) + // Proof Size summary in bytes: + // Measured: `144` + // Estimated: `2566` + // Minimum execution time: 10_139 nanoseconds. + Weight::from_parts(10_773_000, 2566) + .saturating_add(T::DbWeight::get().reads(1_u64)) + .saturating_add(T::DbWeight::get().writes(1_u64)) } - // Storage: Preimage StatusFor (r:1 w:1) + /// Storage: Preimage StatusFor (r:1 w:1) + /// Proof: Preimage StatusFor (max_values: None, max_size: Some(91), added: 2566, mode: MaxEncodedLen) fn request_unnoted_preimage() -> Weight { - // Minimum execution time: 20_171 nanoseconds. - Weight::from_ref_time(20_806_000 as u64) - .saturating_add(T::DbWeight::get().reads(1 as u64)) - .saturating_add(T::DbWeight::get().writes(1 as u64)) + // Proof Size summary in bytes: + // Measured: `42` + // Estimated: `2566` + // Minimum execution time: 11_721 nanoseconds. + Weight::from_parts(12_313_000, 2566) + .saturating_add(T::DbWeight::get().reads(1_u64)) + .saturating_add(T::DbWeight::get().writes(1_u64)) } - // Storage: Preimage StatusFor (r:1 w:1) + /// Storage: Preimage StatusFor (r:1 w:1) + /// Proof: Preimage StatusFor (max_values: None, max_size: Some(91), added: 2566, mode: MaxEncodedLen) fn request_requested_preimage() -> Weight { - // Minimum execution time: 9_756 nanoseconds. - Weight::from_ref_time(10_115_000 as u64) - .saturating_add(T::DbWeight::get().reads(1 as u64)) - .saturating_add(T::DbWeight::get().writes(1 as u64)) + // Proof Size summary in bytes: + // Measured: `106` + // Estimated: `2566` + // Minimum execution time: 7_263 nanoseconds. + Weight::from_parts(7_547_000, 2566) + .saturating_add(T::DbWeight::get().reads(1_u64)) + .saturating_add(T::DbWeight::get().writes(1_u64)) } - // Storage: Preimage StatusFor (r:1 w:1) - // Storage: Preimage PreimageFor (r:0 w:1) + /// Storage: Preimage StatusFor (r:1 w:1) + /// Proof: Preimage StatusFor (max_values: None, max_size: Some(91), added: 2566, mode: MaxEncodedLen) + /// Storage: Preimage PreimageFor (r:0 w:1) + /// Proof: Preimage PreimageFor (max_values: None, max_size: Some(4194344), added: 4196819, mode: MaxEncodedLen) fn unrequest_preimage() -> Weight { - // Minimum execution time: 28_379 nanoseconds. - Weight::from_ref_time(29_778_000 as u64) - .saturating_add(T::DbWeight::get().reads(1 as u64)) - .saturating_add(T::DbWeight::get().writes(2 as u64)) + // Proof Size summary in bytes: + // Measured: `144` + // Estimated: `2566` + // Minimum execution time: 17_785 nanoseconds. + Weight::from_parts(18_501_000, 2566) + .saturating_add(T::DbWeight::get().reads(1_u64)) + .saturating_add(T::DbWeight::get().writes(2_u64)) } - // Storage: Preimage StatusFor (r:1 w:1) + /// Storage: Preimage StatusFor (r:1 w:1) + /// Proof: Preimage StatusFor (max_values: None, max_size: Some(91), added: 2566, mode: MaxEncodedLen) fn unrequest_unnoted_preimage() -> Weight { - // Minimum execution time: 9_595 nanoseconds. - Weight::from_ref_time(9_888_000 as u64) - .saturating_add(T::DbWeight::get().reads(1 as u64)) - .saturating_add(T::DbWeight::get().writes(1 as u64)) + // Proof Size summary in bytes: + // Measured: `106` + // Estimated: `2566` + // Minimum execution time: 7_090 nanoseconds. + Weight::from_parts(7_319_000, 2566) + .saturating_add(T::DbWeight::get().reads(1_u64)) + .saturating_add(T::DbWeight::get().writes(1_u64)) } - // Storage: Preimage StatusFor (r:1 w:1) + /// Storage: Preimage StatusFor (r:1 w:1) + /// Proof: Preimage StatusFor (max_values: None, max_size: Some(91), added: 2566, mode: MaxEncodedLen) fn unrequest_multi_referenced_preimage() -> Weight { - // Minimum execution time: 9_642 nanoseconds. - Weight::from_ref_time(9_985_000 as u64) - .saturating_add(T::DbWeight::get().reads(1 as u64)) - .saturating_add(T::DbWeight::get().writes(1 as u64)) + // Proof Size summary in bytes: + // Measured: `106` + // Estimated: `2566` + // Minimum execution time: 7_253 nanoseconds. + Weight::from_parts(7_508_000, 2566) + .saturating_add(T::DbWeight::get().reads(1_u64)) + .saturating_add(T::DbWeight::get().writes(1_u64)) } } // For backwards compatibility and tests impl WeightInfo for () { - // Storage: Preimage StatusFor (r:1 w:1) - // Storage: Preimage PreimageFor (r:0 w:1) + /// Storage: Preimage StatusFor (r:1 w:1) + /// Proof: Preimage StatusFor (max_values: None, max_size: Some(91), added: 2566, mode: MaxEncodedLen) + /// Storage: Preimage PreimageFor (r:0 w:1) + /// Proof: Preimage PreimageFor (max_values: None, max_size: Some(4194344), added: 4196819, mode: MaxEncodedLen) /// The range of component `s` is `[0, 4194304]`. fn note_preimage(s: u32, ) -> Weight { - // Minimum execution time: 33_810 nanoseconds. - Weight::from_ref_time(34_299_000 as u64) + // Proof Size summary in bytes: + // Measured: `175` + // Estimated: `2566` + // Minimum execution time: 23_484 nanoseconds. + Weight::from_parts(23_828_000, 2566) // Standard Error: 0 - .saturating_add(Weight::from_ref_time(1_703 as u64).saturating_mul(s as u64)) - .saturating_add(RocksDbWeight::get().reads(1 as u64)) - .saturating_add(RocksDbWeight::get().writes(2 as u64)) + .saturating_add(Weight::from_ref_time(1_705).saturating_mul(s.into())) + .saturating_add(RocksDbWeight::get().reads(1_u64)) + .saturating_add(RocksDbWeight::get().writes(2_u64)) } - // Storage: Preimage StatusFor (r:1 w:1) - // Storage: Preimage PreimageFor (r:0 w:1) + /// Storage: Preimage StatusFor (r:1 w:1) + /// Proof: Preimage StatusFor (max_values: None, max_size: Some(91), added: 2566, mode: MaxEncodedLen) + /// Storage: Preimage PreimageFor (r:0 w:1) + /// Proof: Preimage PreimageFor (max_values: None, max_size: Some(4194344), added: 4196819, mode: MaxEncodedLen) /// The range of component `s` is `[0, 4194304]`. fn note_requested_preimage(s: u32, ) -> Weight { - // Minimum execution time: 24_398 nanoseconds. - Weight::from_ref_time(24_839_000 as u64) + // Proof Size summary in bytes: + // Measured: `106` + // Estimated: `2566` + // Minimum execution time: 14_812 nanoseconds. + Weight::from_parts(14_949_000, 2566) // Standard Error: 0 - .saturating_add(Weight::from_ref_time(1_702 as u64).saturating_mul(s as u64)) - .saturating_add(RocksDbWeight::get().reads(1 as u64)) - .saturating_add(RocksDbWeight::get().writes(2 as u64)) + .saturating_add(Weight::from_ref_time(1_707).saturating_mul(s.into())) + .saturating_add(RocksDbWeight::get().reads(1_u64)) + .saturating_add(RocksDbWeight::get().writes(2_u64)) } - // Storage: Preimage StatusFor (r:1 w:1) - // Storage: Preimage PreimageFor (r:0 w:1) + /// Storage: Preimage StatusFor (r:1 w:1) + /// Proof: Preimage StatusFor (max_values: None, max_size: Some(91), added: 2566, mode: MaxEncodedLen) + /// Storage: Preimage PreimageFor (r:0 w:1) + /// Proof: Preimage PreimageFor (max_values: None, max_size: Some(4194344), added: 4196819, mode: MaxEncodedLen) /// The range of component `s` is `[0, 4194304]`. fn note_no_deposit_preimage(s: u32, ) -> Weight { - // Minimum execution time: 22_235 nanoseconds. - Weight::from_ref_time(22_473_000 as u64) + // Proof Size summary in bytes: + // Measured: `106` + // Estimated: `2566` + // Minimum execution time: 14_185 nanoseconds. + Weight::from_parts(14_398_000, 2566) // Standard Error: 0 - .saturating_add(Weight::from_ref_time(1_703 as u64).saturating_mul(s as u64)) - .saturating_add(RocksDbWeight::get().reads(1 as u64)) - .saturating_add(RocksDbWeight::get().writes(2 as u64)) + .saturating_add(Weight::from_ref_time(1_709).saturating_mul(s.into())) + .saturating_add(RocksDbWeight::get().reads(1_u64)) + .saturating_add(RocksDbWeight::get().writes(2_u64)) } - // Storage: Preimage StatusFor (r:1 w:1) - // Storage: Preimage PreimageFor (r:0 w:1) + /// Storage: Preimage StatusFor (r:1 w:1) + /// Proof: Preimage StatusFor (max_values: None, max_size: Some(91), added: 2566, mode: MaxEncodedLen) + /// Storage: Preimage PreimageFor (r:0 w:1) + /// Proof: Preimage PreimageFor (max_values: None, max_size: Some(4194344), added: 4196819, mode: MaxEncodedLen) fn unnote_preimage() -> Weight { - // Minimum execution time: 43_241 nanoseconds. - Weight::from_ref_time(44_470_000 as u64) - .saturating_add(RocksDbWeight::get().reads(1 as u64)) - .saturating_add(RocksDbWeight::get().writes(2 as u64)) + // Proof Size summary in bytes: + // Measured: `353` + // Estimated: `2566` + // Minimum execution time: 29_917 nanoseconds. + Weight::from_parts(30_691_000, 2566) + .saturating_add(RocksDbWeight::get().reads(1_u64)) + .saturating_add(RocksDbWeight::get().writes(2_u64)) } - // Storage: Preimage StatusFor (r:1 w:1) - // Storage: Preimage PreimageFor (r:0 w:1) + /// Storage: Preimage StatusFor (r:1 w:1) + /// Proof: Preimage StatusFor (max_values: None, max_size: Some(91), added: 2566, mode: MaxEncodedLen) + /// Storage: Preimage PreimageFor (r:0 w:1) + /// Proof: Preimage PreimageFor (max_values: None, max_size: Some(4194344), added: 4196819, mode: MaxEncodedLen) fn unnote_no_deposit_preimage() -> Weight { - // Minimum execution time: 29_529 nanoseconds. - Weight::from_ref_time(30_364_000 as u64) - .saturating_add(RocksDbWeight::get().reads(1 as u64)) - .saturating_add(RocksDbWeight::get().writes(2 as u64)) + // Proof Size summary in bytes: + // Measured: `144` + // Estimated: `2566` + // Minimum execution time: 19_281 nanoseconds. + Weight::from_parts(20_121_000, 2566) + .saturating_add(RocksDbWeight::get().reads(1_u64)) + .saturating_add(RocksDbWeight::get().writes(2_u64)) } - // Storage: Preimage StatusFor (r:1 w:1) + /// Storage: Preimage StatusFor (r:1 w:1) + /// Proof: Preimage StatusFor (max_values: None, max_size: Some(91), added: 2566, mode: MaxEncodedLen) fn request_preimage() -> Weight { - // Minimum execution time: 28_914 nanoseconds. - Weight::from_ref_time(30_103_000 as u64) - .saturating_add(RocksDbWeight::get().reads(1 as u64)) - .saturating_add(RocksDbWeight::get().writes(1 as u64)) + // Proof Size summary in bytes: + // Measured: `220` + // Estimated: `2566` + // Minimum execution time: 17_192 nanoseconds. + Weight::from_parts(18_637_000, 2566) + .saturating_add(RocksDbWeight::get().reads(1_u64)) + .saturating_add(RocksDbWeight::get().writes(1_u64)) } - // Storage: Preimage StatusFor (r:1 w:1) + /// Storage: Preimage StatusFor (r:1 w:1) + /// Proof: Preimage StatusFor (max_values: None, max_size: Some(91), added: 2566, mode: MaxEncodedLen) fn request_no_deposit_preimage() -> Weight { - // Minimum execution time: 14_479 nanoseconds. - Weight::from_ref_time(15_244_000 as u64) - .saturating_add(RocksDbWeight::get().reads(1 as u64)) - .saturating_add(RocksDbWeight::get().writes(1 as u64)) + // Proof Size summary in bytes: + // Measured: `144` + // Estimated: `2566` + // Minimum execution time: 10_139 nanoseconds. + Weight::from_parts(10_773_000, 2566) + .saturating_add(RocksDbWeight::get().reads(1_u64)) + .saturating_add(RocksDbWeight::get().writes(1_u64)) } - // Storage: Preimage StatusFor (r:1 w:1) + /// Storage: Preimage StatusFor (r:1 w:1) + /// Proof: Preimage StatusFor (max_values: None, max_size: Some(91), added: 2566, mode: MaxEncodedLen) fn request_unnoted_preimage() -> Weight { - // Minimum execution time: 20_171 nanoseconds. - Weight::from_ref_time(20_806_000 as u64) - .saturating_add(RocksDbWeight::get().reads(1 as u64)) - .saturating_add(RocksDbWeight::get().writes(1 as u64)) + // Proof Size summary in bytes: + // Measured: `42` + // Estimated: `2566` + // Minimum execution time: 11_721 nanoseconds. + Weight::from_parts(12_313_000, 2566) + .saturating_add(RocksDbWeight::get().reads(1_u64)) + .saturating_add(RocksDbWeight::get().writes(1_u64)) } - // Storage: Preimage StatusFor (r:1 w:1) + /// Storage: Preimage StatusFor (r:1 w:1) + /// Proof: Preimage StatusFor (max_values: None, max_size: Some(91), added: 2566, mode: MaxEncodedLen) fn request_requested_preimage() -> Weight { - // Minimum execution time: 9_756 nanoseconds. - Weight::from_ref_time(10_115_000 as u64) - .saturating_add(RocksDbWeight::get().reads(1 as u64)) - .saturating_add(RocksDbWeight::get().writes(1 as u64)) + // Proof Size summary in bytes: + // Measured: `106` + // Estimated: `2566` + // Minimum execution time: 7_263 nanoseconds. + Weight::from_parts(7_547_000, 2566) + .saturating_add(RocksDbWeight::get().reads(1_u64)) + .saturating_add(RocksDbWeight::get().writes(1_u64)) } - // Storage: Preimage StatusFor (r:1 w:1) - // Storage: Preimage PreimageFor (r:0 w:1) + /// Storage: Preimage StatusFor (r:1 w:1) + /// Proof: Preimage StatusFor (max_values: None, max_size: Some(91), added: 2566, mode: MaxEncodedLen) + /// Storage: Preimage PreimageFor (r:0 w:1) + /// Proof: Preimage PreimageFor (max_values: None, max_size: Some(4194344), added: 4196819, mode: MaxEncodedLen) fn unrequest_preimage() -> Weight { - // Minimum execution time: 28_379 nanoseconds. - Weight::from_ref_time(29_778_000 as u64) - .saturating_add(RocksDbWeight::get().reads(1 as u64)) - .saturating_add(RocksDbWeight::get().writes(2 as u64)) + // Proof Size summary in bytes: + // Measured: `144` + // Estimated: `2566` + // Minimum execution time: 17_785 nanoseconds. + Weight::from_parts(18_501_000, 2566) + .saturating_add(RocksDbWeight::get().reads(1_u64)) + .saturating_add(RocksDbWeight::get().writes(2_u64)) } - // Storage: Preimage StatusFor (r:1 w:1) + /// Storage: Preimage StatusFor (r:1 w:1) + /// Proof: Preimage StatusFor (max_values: None, max_size: Some(91), added: 2566, mode: MaxEncodedLen) fn unrequest_unnoted_preimage() -> Weight { - // Minimum execution time: 9_595 nanoseconds. - Weight::from_ref_time(9_888_000 as u64) - .saturating_add(RocksDbWeight::get().reads(1 as u64)) - .saturating_add(RocksDbWeight::get().writes(1 as u64)) + // Proof Size summary in bytes: + // Measured: `106` + // Estimated: `2566` + // Minimum execution time: 7_090 nanoseconds. + Weight::from_parts(7_319_000, 2566) + .saturating_add(RocksDbWeight::get().reads(1_u64)) + .saturating_add(RocksDbWeight::get().writes(1_u64)) } - // Storage: Preimage StatusFor (r:1 w:1) + /// Storage: Preimage StatusFor (r:1 w:1) + /// Proof: Preimage StatusFor (max_values: None, max_size: Some(91), added: 2566, mode: MaxEncodedLen) fn unrequest_multi_referenced_preimage() -> Weight { - // Minimum execution time: 9_642 nanoseconds. - Weight::from_ref_time(9_985_000 as u64) - .saturating_add(RocksDbWeight::get().reads(1 as u64)) - .saturating_add(RocksDbWeight::get().writes(1 as u64)) + // Proof Size summary in bytes: + // Measured: `106` + // Estimated: `2566` + // Minimum execution time: 7_253 nanoseconds. + Weight::from_parts(7_508_000, 2566) + .saturating_add(RocksDbWeight::get().reads(1_u64)) + .saturating_add(RocksDbWeight::get().writes(1_u64)) } } diff --git a/frame/proxy/src/weights.rs b/frame/proxy/src/weights.rs index 706810d34..53e368aca 100644 --- a/frame/proxy/src/weights.rs +++ b/frame/proxy/src/weights.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2022 Parity Technologies (UK) Ltd. +// Copyright (C) 2023 Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); @@ -18,7 +18,8 @@ //! Autogenerated weights for pallet_proxy //! //! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev -//! DATE: 2022-11-07, STEPS: `50`, REPEAT: 20, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! DATE: 2023-01-24, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! WORST CASE MAP SIZE: `1000000` //! HOSTNAME: `bm2`, CPU: `Intel(R) Core(TM) i7-7700K CPU @ 4.20GHz` //! EXECUTION: Some(Wasm), WASM-EXECUTION: Compiled, CHAIN: Some("dev"), DB CACHE: 1024 @@ -62,244 +63,334 @@ pub trait WeightInfo { /// Weights for pallet_proxy using the Substrate node and recommended hardware. pub struct SubstrateWeight(PhantomData); impl WeightInfo for SubstrateWeight { - // Storage: Proxy Proxies (r:1 w:0) + /// Storage: Proxy Proxies (r:1 w:0) + /// Proof: Proxy Proxies (max_values: None, max_size: Some(1241), added: 3716, mode: MaxEncodedLen) /// The range of component `p` is `[1, 31]`. fn proxy(p: u32, ) -> Weight { - // Minimum execution time: 24_285 nanoseconds. - Weight::from_ref_time(25_355_667 as u64) - // Standard Error: 1_468 - .saturating_add(Weight::from_ref_time(38_185 as u64).saturating_mul(p as u64)) - .saturating_add(T::DbWeight::get().reads(1 as u64)) + // Proof Size summary in bytes: + // Measured: `193 + p * (37 ±0)` + // Estimated: `3716` + // Minimum execution time: 14_461 nanoseconds. + Weight::from_parts(14_913_927, 3716) + // Standard Error: 1_174 + .saturating_add(Weight::from_ref_time(36_087).saturating_mul(p.into())) + .saturating_add(T::DbWeight::get().reads(1_u64)) } - // Storage: Proxy Proxies (r:1 w:0) - // Storage: Proxy Announcements (r:1 w:1) - // Storage: System Account (r:1 w:1) + /// Storage: Proxy Proxies (r:1 w:0) + /// Proof: Proxy Proxies (max_values: None, max_size: Some(1241), added: 3716, mode: MaxEncodedLen) + /// Storage: Proxy Announcements (r:1 w:1) + /// Proof: Proxy Announcements (max_values: None, max_size: Some(2233), added: 4708, mode: MaxEncodedLen) + /// Storage: System Account (r:1 w:1) + /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) /// The range of component `a` is `[0, 31]`. /// The range of component `p` is `[1, 31]`. fn proxy_announced(a: u32, p: u32, ) -> Weight { - // Minimum execution time: 44_948 nanoseconds. - Weight::from_ref_time(44_762_064 as u64) - // Standard Error: 1_778 - .saturating_add(Weight::from_ref_time(118_940 as u64).saturating_mul(a as u64)) - // Standard Error: 1_837 - .saturating_add(Weight::from_ref_time(51_232 as u64).saturating_mul(p as u64)) - .saturating_add(T::DbWeight::get().reads(3 as u64)) - .saturating_add(T::DbWeight::get().writes(2 as u64)) + // Proof Size summary in bytes: + // Measured: `584 + a * (68 ±0) + p * (37 ±0)` + // Estimated: `11027` + // Minimum execution time: 31_523 nanoseconds. + Weight::from_parts(31_116_270, 11027) + // Standard Error: 1_789 + .saturating_add(Weight::from_ref_time(135_656).saturating_mul(a.into())) + // Standard Error: 1_849 + .saturating_add(Weight::from_ref_time(53_893).saturating_mul(p.into())) + .saturating_add(T::DbWeight::get().reads(3_u64)) + .saturating_add(T::DbWeight::get().writes(2_u64)) } - // Storage: Proxy Announcements (r:1 w:1) - // Storage: System Account (r:1 w:1) + /// Storage: Proxy Announcements (r:1 w:1) + /// Proof: Proxy Announcements (max_values: None, max_size: Some(2233), added: 4708, mode: MaxEncodedLen) + /// Storage: System Account (r:1 w:1) + /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) /// The range of component `a` is `[0, 31]`. /// The range of component `p` is `[1, 31]`. fn remove_announcement(a: u32, p: u32, ) -> Weight { - // Minimum execution time: 31_274 nanoseconds. - Weight::from_ref_time(32_219_165 as u64) - // Standard Error: 1_832 - .saturating_add(Weight::from_ref_time(132_454 as u64).saturating_mul(a as u64)) - // Standard Error: 1_893 - .saturating_add(Weight::from_ref_time(9_077 as u64).saturating_mul(p as u64)) - .saturating_add(T::DbWeight::get().reads(2 as u64)) - .saturating_add(T::DbWeight::get().writes(2 as u64)) + // Proof Size summary in bytes: + // Measured: `467 + a * (68 ±0)` + // Estimated: `7311` + // Minimum execution time: 19_363 nanoseconds. + Weight::from_parts(20_282_191, 7311) + // Standard Error: 1_084 + .saturating_add(Weight::from_ref_time(133_825).saturating_mul(a.into())) + // Standard Error: 1_120 + .saturating_add(Weight::from_ref_time(3_434).saturating_mul(p.into())) + .saturating_add(T::DbWeight::get().reads(2_u64)) + .saturating_add(T::DbWeight::get().writes(2_u64)) } - // Storage: Proxy Announcements (r:1 w:1) - // Storage: System Account (r:1 w:1) + /// Storage: Proxy Announcements (r:1 w:1) + /// Proof: Proxy Announcements (max_values: None, max_size: Some(2233), added: 4708, mode: MaxEncodedLen) + /// Storage: System Account (r:1 w:1) + /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) /// The range of component `a` is `[0, 31]`. /// The range of component `p` is `[1, 31]`. fn reject_announcement(a: u32, p: u32, ) -> Weight { - // Minimum execution time: 31_219 nanoseconds. - Weight::from_ref_time(32_439_563 as u64) - // Standard Error: 1_829 - .saturating_add(Weight::from_ref_time(120_251 as u64).saturating_mul(a as u64)) - // Standard Error: 1_890 - .saturating_add(Weight::from_ref_time(8_689 as u64).saturating_mul(p as u64)) - .saturating_add(T::DbWeight::get().reads(2 as u64)) - .saturating_add(T::DbWeight::get().writes(2 as u64)) + // Proof Size summary in bytes: + // Measured: `467 + a * (68 ±0)` + // Estimated: `7311` + // Minimum execution time: 19_363 nanoseconds. + Weight::from_parts(20_211_584, 7311) + // Standard Error: 1_171 + .saturating_add(Weight::from_ref_time(136_984).saturating_mul(a.into())) + // Standard Error: 1_210 + .saturating_add(Weight::from_ref_time(3_686).saturating_mul(p.into())) + .saturating_add(T::DbWeight::get().reads(2_u64)) + .saturating_add(T::DbWeight::get().writes(2_u64)) } - // Storage: Proxy Proxies (r:1 w:0) - // Storage: Proxy Announcements (r:1 w:1) - // Storage: System Account (r:1 w:1) + /// Storage: Proxy Proxies (r:1 w:0) + /// Proof: Proxy Proxies (max_values: None, max_size: Some(1241), added: 3716, mode: MaxEncodedLen) + /// Storage: Proxy Announcements (r:1 w:1) + /// Proof: Proxy Announcements (max_values: None, max_size: Some(2233), added: 4708, mode: MaxEncodedLen) + /// Storage: System Account (r:1 w:1) + /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) /// The range of component `a` is `[0, 31]`. /// The range of component `p` is `[1, 31]`. fn announce(a: u32, p: u32, ) -> Weight { - // Minimum execution time: 40_388 nanoseconds. - Weight::from_ref_time(40_718_245 as u64) - // Standard Error: 1_821 - .saturating_add(Weight::from_ref_time(129_674 as u64).saturating_mul(a as u64)) - // Standard Error: 1_882 - .saturating_add(Weight::from_ref_time(56_001 as u64).saturating_mul(p as u64)) - .saturating_add(T::DbWeight::get().reads(3 as u64)) - .saturating_add(T::DbWeight::get().writes(2 as u64)) + // Proof Size summary in bytes: + // Measured: `516 + a * (68 ±0) + p * (37 ±0)` + // Estimated: `11027` + // Minimum execution time: 27_811 nanoseconds. + Weight::from_parts(27_965_813, 11027) + // Standard Error: 1_987 + .saturating_add(Weight::from_ref_time(124_133).saturating_mul(a.into())) + // Standard Error: 2_053 + .saturating_add(Weight::from_ref_time(54_692).saturating_mul(p.into())) + .saturating_add(T::DbWeight::get().reads(3_u64)) + .saturating_add(T::DbWeight::get().writes(2_u64)) } - // Storage: Proxy Proxies (r:1 w:1) + /// Storage: Proxy Proxies (r:1 w:1) + /// Proof: Proxy Proxies (max_values: None, max_size: Some(1241), added: 3716, mode: MaxEncodedLen) /// The range of component `p` is `[1, 31]`. fn add_proxy(p: u32, ) -> Weight { - // Minimum execution time: 33_997 nanoseconds. - Weight::from_ref_time(34_840_036 as u64) - // Standard Error: 1_659 - .saturating_add(Weight::from_ref_time(71_349 as u64).saturating_mul(p as u64)) - .saturating_add(T::DbWeight::get().reads(1 as u64)) - .saturating_add(T::DbWeight::get().writes(1 as u64)) + // Proof Size summary in bytes: + // Measured: `193 + p * (37 ±0)` + // Estimated: `3716` + // Minimum execution time: 20_922 nanoseconds. + Weight::from_parts(21_551_797, 3716) + // Standard Error: 1_425 + .saturating_add(Weight::from_ref_time(58_434).saturating_mul(p.into())) + .saturating_add(T::DbWeight::get().reads(1_u64)) + .saturating_add(T::DbWeight::get().writes(1_u64)) } - // Storage: Proxy Proxies (r:1 w:1) + /// Storage: Proxy Proxies (r:1 w:1) + /// Proof: Proxy Proxies (max_values: None, max_size: Some(1241), added: 3716, mode: MaxEncodedLen) /// The range of component `p` is `[1, 31]`. fn remove_proxy(p: u32, ) -> Weight { - // Minimum execution time: 33_900 nanoseconds. - Weight::from_ref_time(35_069_110 as u64) - // Standard Error: 1_848 - .saturating_add(Weight::from_ref_time(82_380 as u64).saturating_mul(p as u64)) - .saturating_add(T::DbWeight::get().reads(1 as u64)) - .saturating_add(T::DbWeight::get().writes(1 as u64)) + // Proof Size summary in bytes: + // Measured: `193 + p * (37 ±0)` + // Estimated: `3716` + // Minimum execution time: 20_812 nanoseconds. + Weight::from_parts(21_660_732, 3716) + // Standard Error: 1_438 + .saturating_add(Weight::from_ref_time(68_740).saturating_mul(p.into())) + .saturating_add(T::DbWeight::get().reads(1_u64)) + .saturating_add(T::DbWeight::get().writes(1_u64)) } - // Storage: Proxy Proxies (r:1 w:1) + /// Storage: Proxy Proxies (r:1 w:1) + /// Proof: Proxy Proxies (max_values: None, max_size: Some(1241), added: 3716, mode: MaxEncodedLen) /// The range of component `p` is `[1, 31]`. fn remove_proxies(p: u32, ) -> Weight { - // Minimum execution time: 29_627 nanoseconds. - Weight::from_ref_time(30_641_642 as u64) - // Standard Error: 1_495 - .saturating_add(Weight::from_ref_time(51_919 as u64).saturating_mul(p as u64)) - .saturating_add(T::DbWeight::get().reads(1 as u64)) - .saturating_add(T::DbWeight::get().writes(1 as u64)) + // Proof Size summary in bytes: + // Measured: `193 + p * (37 ±0)` + // Estimated: `3716` + // Minimum execution time: 16_786 nanoseconds. + Weight::from_parts(17_249_958, 3716) + // Standard Error: 1_007 + .saturating_add(Weight::from_ref_time(37_546).saturating_mul(p.into())) + .saturating_add(T::DbWeight::get().reads(1_u64)) + .saturating_add(T::DbWeight::get().writes(1_u64)) } - // Storage: unknown [0x3a65787472696e7369635f696e646578] (r:1 w:0) - // Storage: Proxy Proxies (r:1 w:1) + /// Storage: Proxy Proxies (r:1 w:1) + /// Proof: Proxy Proxies (max_values: None, max_size: Some(1241), added: 3716, mode: MaxEncodedLen) /// The range of component `p` is `[1, 31]`. fn create_pure(p: u32, ) -> Weight { - // Minimum execution time: 37_761 nanoseconds. - Weight::from_ref_time(38_748_697 as u64) - // Standard Error: 1_594 - .saturating_add(Weight::from_ref_time(19_022 as u64).saturating_mul(p as u64)) - .saturating_add(T::DbWeight::get().reads(2 as u64)) - .saturating_add(T::DbWeight::get().writes(1 as u64)) + // Proof Size summary in bytes: + // Measured: `173` + // Estimated: `3716` + // Minimum execution time: 22_764 nanoseconds. + Weight::from_parts(23_539_039, 3716) + // Standard Error: 814 + .saturating_add(Weight::from_ref_time(144).saturating_mul(p.into())) + .saturating_add(T::DbWeight::get().reads(1_u64)) + .saturating_add(T::DbWeight::get().writes(1_u64)) } - // Storage: Proxy Proxies (r:1 w:1) + /// Storage: Proxy Proxies (r:1 w:1) + /// Proof: Proxy Proxies (max_values: None, max_size: Some(1241), added: 3716, mode: MaxEncodedLen) /// The range of component `p` is `[0, 30]`. fn kill_pure(p: u32, ) -> Weight { - // Minimum execution time: 31_145 nanoseconds. - Weight::from_ref_time(31_933_568 as u64) - // Standard Error: 1_492 - .saturating_add(Weight::from_ref_time(50_250 as u64).saturating_mul(p as u64)) - .saturating_add(T::DbWeight::get().reads(1 as u64)) - .saturating_add(T::DbWeight::get().writes(1 as u64)) + // Proof Size summary in bytes: + // Measured: `230 + p * (37 ±0)` + // Estimated: `3716` + // Minimum execution time: 17_720 nanoseconds. + Weight::from_parts(18_428_849, 3716) + // Standard Error: 1_093 + .saturating_add(Weight::from_ref_time(34_600).saturating_mul(p.into())) + .saturating_add(T::DbWeight::get().reads(1_u64)) + .saturating_add(T::DbWeight::get().writes(1_u64)) } } // For backwards compatibility and tests impl WeightInfo for () { - // Storage: Proxy Proxies (r:1 w:0) + /// Storage: Proxy Proxies (r:1 w:0) + /// Proof: Proxy Proxies (max_values: None, max_size: Some(1241), added: 3716, mode: MaxEncodedLen) /// The range of component `p` is `[1, 31]`. fn proxy(p: u32, ) -> Weight { - // Minimum execution time: 24_285 nanoseconds. - Weight::from_ref_time(25_355_667 as u64) - // Standard Error: 1_468 - .saturating_add(Weight::from_ref_time(38_185 as u64).saturating_mul(p as u64)) - .saturating_add(RocksDbWeight::get().reads(1 as u64)) + // Proof Size summary in bytes: + // Measured: `193 + p * (37 ±0)` + // Estimated: `3716` + // Minimum execution time: 14_461 nanoseconds. + Weight::from_parts(14_913_927, 3716) + // Standard Error: 1_174 + .saturating_add(Weight::from_ref_time(36_087).saturating_mul(p.into())) + .saturating_add(RocksDbWeight::get().reads(1_u64)) } - // Storage: Proxy Proxies (r:1 w:0) - // Storage: Proxy Announcements (r:1 w:1) - // Storage: System Account (r:1 w:1) + /// Storage: Proxy Proxies (r:1 w:0) + /// Proof: Proxy Proxies (max_values: None, max_size: Some(1241), added: 3716, mode: MaxEncodedLen) + /// Storage: Proxy Announcements (r:1 w:1) + /// Proof: Proxy Announcements (max_values: None, max_size: Some(2233), added: 4708, mode: MaxEncodedLen) + /// Storage: System Account (r:1 w:1) + /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) /// The range of component `a` is `[0, 31]`. /// The range of component `p` is `[1, 31]`. fn proxy_announced(a: u32, p: u32, ) -> Weight { - // Minimum execution time: 44_948 nanoseconds. - Weight::from_ref_time(44_762_064 as u64) - // Standard Error: 1_778 - .saturating_add(Weight::from_ref_time(118_940 as u64).saturating_mul(a as u64)) - // Standard Error: 1_837 - .saturating_add(Weight::from_ref_time(51_232 as u64).saturating_mul(p as u64)) - .saturating_add(RocksDbWeight::get().reads(3 as u64)) - .saturating_add(RocksDbWeight::get().writes(2 as u64)) + // Proof Size summary in bytes: + // Measured: `584 + a * (68 ±0) + p * (37 ±0)` + // Estimated: `11027` + // Minimum execution time: 31_523 nanoseconds. + Weight::from_parts(31_116_270, 11027) + // Standard Error: 1_789 + .saturating_add(Weight::from_ref_time(135_656).saturating_mul(a.into())) + // Standard Error: 1_849 + .saturating_add(Weight::from_ref_time(53_893).saturating_mul(p.into())) + .saturating_add(RocksDbWeight::get().reads(3_u64)) + .saturating_add(RocksDbWeight::get().writes(2_u64)) } - // Storage: Proxy Announcements (r:1 w:1) - // Storage: System Account (r:1 w:1) + /// Storage: Proxy Announcements (r:1 w:1) + /// Proof: Proxy Announcements (max_values: None, max_size: Some(2233), added: 4708, mode: MaxEncodedLen) + /// Storage: System Account (r:1 w:1) + /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) /// The range of component `a` is `[0, 31]`. /// The range of component `p` is `[1, 31]`. fn remove_announcement(a: u32, p: u32, ) -> Weight { - // Minimum execution time: 31_274 nanoseconds. - Weight::from_ref_time(32_219_165 as u64) - // Standard Error: 1_832 - .saturating_add(Weight::from_ref_time(132_454 as u64).saturating_mul(a as u64)) - // Standard Error: 1_893 - .saturating_add(Weight::from_ref_time(9_077 as u64).saturating_mul(p as u64)) - .saturating_add(RocksDbWeight::get().reads(2 as u64)) - .saturating_add(RocksDbWeight::get().writes(2 as u64)) + // Proof Size summary in bytes: + // Measured: `467 + a * (68 ±0)` + // Estimated: `7311` + // Minimum execution time: 19_363 nanoseconds. + Weight::from_parts(20_282_191, 7311) + // Standard Error: 1_084 + .saturating_add(Weight::from_ref_time(133_825).saturating_mul(a.into())) + // Standard Error: 1_120 + .saturating_add(Weight::from_ref_time(3_434).saturating_mul(p.into())) + .saturating_add(RocksDbWeight::get().reads(2_u64)) + .saturating_add(RocksDbWeight::get().writes(2_u64)) } - // Storage: Proxy Announcements (r:1 w:1) - // Storage: System Account (r:1 w:1) + /// Storage: Proxy Announcements (r:1 w:1) + /// Proof: Proxy Announcements (max_values: None, max_size: Some(2233), added: 4708, mode: MaxEncodedLen) + /// Storage: System Account (r:1 w:1) + /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) /// The range of component `a` is `[0, 31]`. /// The range of component `p` is `[1, 31]`. fn reject_announcement(a: u32, p: u32, ) -> Weight { - // Minimum execution time: 31_219 nanoseconds. - Weight::from_ref_time(32_439_563 as u64) - // Standard Error: 1_829 - .saturating_add(Weight::from_ref_time(120_251 as u64).saturating_mul(a as u64)) - // Standard Error: 1_890 - .saturating_add(Weight::from_ref_time(8_689 as u64).saturating_mul(p as u64)) - .saturating_add(RocksDbWeight::get().reads(2 as u64)) - .saturating_add(RocksDbWeight::get().writes(2 as u64)) + // Proof Size summary in bytes: + // Measured: `467 + a * (68 ±0)` + // Estimated: `7311` + // Minimum execution time: 19_363 nanoseconds. + Weight::from_parts(20_211_584, 7311) + // Standard Error: 1_171 + .saturating_add(Weight::from_ref_time(136_984).saturating_mul(a.into())) + // Standard Error: 1_210 + .saturating_add(Weight::from_ref_time(3_686).saturating_mul(p.into())) + .saturating_add(RocksDbWeight::get().reads(2_u64)) + .saturating_add(RocksDbWeight::get().writes(2_u64)) } - // Storage: Proxy Proxies (r:1 w:0) - // Storage: Proxy Announcements (r:1 w:1) - // Storage: System Account (r:1 w:1) + /// Storage: Proxy Proxies (r:1 w:0) + /// Proof: Proxy Proxies (max_values: None, max_size: Some(1241), added: 3716, mode: MaxEncodedLen) + /// Storage: Proxy Announcements (r:1 w:1) + /// Proof: Proxy Announcements (max_values: None, max_size: Some(2233), added: 4708, mode: MaxEncodedLen) + /// Storage: System Account (r:1 w:1) + /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) /// The range of component `a` is `[0, 31]`. /// The range of component `p` is `[1, 31]`. fn announce(a: u32, p: u32, ) -> Weight { - // Minimum execution time: 40_388 nanoseconds. - Weight::from_ref_time(40_718_245 as u64) - // Standard Error: 1_821 - .saturating_add(Weight::from_ref_time(129_674 as u64).saturating_mul(a as u64)) - // Standard Error: 1_882 - .saturating_add(Weight::from_ref_time(56_001 as u64).saturating_mul(p as u64)) - .saturating_add(RocksDbWeight::get().reads(3 as u64)) - .saturating_add(RocksDbWeight::get().writes(2 as u64)) + // Proof Size summary in bytes: + // Measured: `516 + a * (68 ±0) + p * (37 ±0)` + // Estimated: `11027` + // Minimum execution time: 27_811 nanoseconds. + Weight::from_parts(27_965_813, 11027) + // Standard Error: 1_987 + .saturating_add(Weight::from_ref_time(124_133).saturating_mul(a.into())) + // Standard Error: 2_053 + .saturating_add(Weight::from_ref_time(54_692).saturating_mul(p.into())) + .saturating_add(RocksDbWeight::get().reads(3_u64)) + .saturating_add(RocksDbWeight::get().writes(2_u64)) } - // Storage: Proxy Proxies (r:1 w:1) + /// Storage: Proxy Proxies (r:1 w:1) + /// Proof: Proxy Proxies (max_values: None, max_size: Some(1241), added: 3716, mode: MaxEncodedLen) /// The range of component `p` is `[1, 31]`. fn add_proxy(p: u32, ) -> Weight { - // Minimum execution time: 33_997 nanoseconds. - Weight::from_ref_time(34_840_036 as u64) - // Standard Error: 1_659 - .saturating_add(Weight::from_ref_time(71_349 as u64).saturating_mul(p as u64)) - .saturating_add(RocksDbWeight::get().reads(1 as u64)) - .saturating_add(RocksDbWeight::get().writes(1 as u64)) + // Proof Size summary in bytes: + // Measured: `193 + p * (37 ±0)` + // Estimated: `3716` + // Minimum execution time: 20_922 nanoseconds. + Weight::from_parts(21_551_797, 3716) + // Standard Error: 1_425 + .saturating_add(Weight::from_ref_time(58_434).saturating_mul(p.into())) + .saturating_add(RocksDbWeight::get().reads(1_u64)) + .saturating_add(RocksDbWeight::get().writes(1_u64)) } - // Storage: Proxy Proxies (r:1 w:1) + /// Storage: Proxy Proxies (r:1 w:1) + /// Proof: Proxy Proxies (max_values: None, max_size: Some(1241), added: 3716, mode: MaxEncodedLen) /// The range of component `p` is `[1, 31]`. fn remove_proxy(p: u32, ) -> Weight { - // Minimum execution time: 33_900 nanoseconds. - Weight::from_ref_time(35_069_110 as u64) - // Standard Error: 1_848 - .saturating_add(Weight::from_ref_time(82_380 as u64).saturating_mul(p as u64)) - .saturating_add(RocksDbWeight::get().reads(1 as u64)) - .saturating_add(RocksDbWeight::get().writes(1 as u64)) + // Proof Size summary in bytes: + // Measured: `193 + p * (37 ±0)` + // Estimated: `3716` + // Minimum execution time: 20_812 nanoseconds. + Weight::from_parts(21_660_732, 3716) + // Standard Error: 1_438 + .saturating_add(Weight::from_ref_time(68_740).saturating_mul(p.into())) + .saturating_add(RocksDbWeight::get().reads(1_u64)) + .saturating_add(RocksDbWeight::get().writes(1_u64)) } - // Storage: Proxy Proxies (r:1 w:1) + /// Storage: Proxy Proxies (r:1 w:1) + /// Proof: Proxy Proxies (max_values: None, max_size: Some(1241), added: 3716, mode: MaxEncodedLen) /// The range of component `p` is `[1, 31]`. fn remove_proxies(p: u32, ) -> Weight { - // Minimum execution time: 29_627 nanoseconds. - Weight::from_ref_time(30_641_642 as u64) - // Standard Error: 1_495 - .saturating_add(Weight::from_ref_time(51_919 as u64).saturating_mul(p as u64)) - .saturating_add(RocksDbWeight::get().reads(1 as u64)) - .saturating_add(RocksDbWeight::get().writes(1 as u64)) + // Proof Size summary in bytes: + // Measured: `193 + p * (37 ±0)` + // Estimated: `3716` + // Minimum execution time: 16_786 nanoseconds. + Weight::from_parts(17_249_958, 3716) + // Standard Error: 1_007 + .saturating_add(Weight::from_ref_time(37_546).saturating_mul(p.into())) + .saturating_add(RocksDbWeight::get().reads(1_u64)) + .saturating_add(RocksDbWeight::get().writes(1_u64)) } - // Storage: unknown [0x3a65787472696e7369635f696e646578] (r:1 w:0) - // Storage: Proxy Proxies (r:1 w:1) + /// Storage: Proxy Proxies (r:1 w:1) + /// Proof: Proxy Proxies (max_values: None, max_size: Some(1241), added: 3716, mode: MaxEncodedLen) /// The range of component `p` is `[1, 31]`. fn create_pure(p: u32, ) -> Weight { - // Minimum execution time: 37_761 nanoseconds. - Weight::from_ref_time(38_748_697 as u64) - // Standard Error: 1_594 - .saturating_add(Weight::from_ref_time(19_022 as u64).saturating_mul(p as u64)) - .saturating_add(RocksDbWeight::get().reads(2 as u64)) - .saturating_add(RocksDbWeight::get().writes(1 as u64)) + // Proof Size summary in bytes: + // Measured: `173` + // Estimated: `3716` + // Minimum execution time: 22_764 nanoseconds. + Weight::from_parts(23_539_039, 3716) + // Standard Error: 814 + .saturating_add(Weight::from_ref_time(144).saturating_mul(p.into())) + .saturating_add(RocksDbWeight::get().reads(1_u64)) + .saturating_add(RocksDbWeight::get().writes(1_u64)) } - // Storage: Proxy Proxies (r:1 w:1) + /// Storage: Proxy Proxies (r:1 w:1) + /// Proof: Proxy Proxies (max_values: None, max_size: Some(1241), added: 3716, mode: MaxEncodedLen) /// The range of component `p` is `[0, 30]`. fn kill_pure(p: u32, ) -> Weight { - // Minimum execution time: 31_145 nanoseconds. - Weight::from_ref_time(31_933_568 as u64) - // Standard Error: 1_492 - .saturating_add(Weight::from_ref_time(50_250 as u64).saturating_mul(p as u64)) - .saturating_add(RocksDbWeight::get().reads(1 as u64)) - .saturating_add(RocksDbWeight::get().writes(1 as u64)) + // Proof Size summary in bytes: + // Measured: `230 + p * (37 ±0)` + // Estimated: `3716` + // Minimum execution time: 17_720 nanoseconds. + Weight::from_parts(18_428_849, 3716) + // Standard Error: 1_093 + .saturating_add(Weight::from_ref_time(34_600).saturating_mul(p.into())) + .saturating_add(RocksDbWeight::get().reads(1_u64)) + .saturating_add(RocksDbWeight::get().writes(1_u64)) } } diff --git a/frame/ranked-collective/src/weights.rs b/frame/ranked-collective/src/weights.rs index c054d2004..2d5ed3afd 100644 --- a/frame/ranked-collective/src/weights.rs +++ b/frame/ranked-collective/src/weights.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2022 Parity Technologies (UK) Ltd. +// Copyright (C) 2023 Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); @@ -18,7 +18,8 @@ //! Autogenerated weights for pallet_ranked_collective //! //! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev -//! DATE: 2022-11-07, STEPS: `50`, REPEAT: 20, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! DATE: 2023-01-24, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! WORST CASE MAP SIZE: `1000000` //! HOSTNAME: `bm2`, CPU: `Intel(R) Core(TM) i7-7700K CPU @ 4.20GHz` //! EXECUTION: Some(Wasm), WASM-EXECUTION: Compiled, CHAIN: Some("dev"), DB CACHE: 1024 @@ -58,154 +59,238 @@ pub trait WeightInfo { /// Weights for pallet_ranked_collective using the Substrate node and recommended hardware. pub struct SubstrateWeight(PhantomData); impl WeightInfo for SubstrateWeight { - // Storage: RankedCollective Members (r:1 w:1) - // Storage: RankedCollective MemberCount (r:1 w:1) - // Storage: RankedCollective IndexToId (r:0 w:1) - // Storage: RankedCollective IdToIndex (r:0 w:1) + /// Storage: RankedCollective Members (r:1 w:1) + /// Proof: RankedCollective Members (max_values: None, max_size: Some(42), added: 2517, mode: MaxEncodedLen) + /// Storage: RankedCollective MemberCount (r:1 w:1) + /// Proof: RankedCollective MemberCount (max_values: None, max_size: Some(14), added: 2489, mode: MaxEncodedLen) + /// Storage: RankedCollective IndexToId (r:0 w:1) + /// Proof: RankedCollective IndexToId (max_values: None, max_size: Some(54), added: 2529, mode: MaxEncodedLen) + /// Storage: RankedCollective IdToIndex (r:0 w:1) + /// Proof: RankedCollective IdToIndex (max_values: None, max_size: Some(54), added: 2529, mode: MaxEncodedLen) fn add_member() -> Weight { - // Minimum execution time: 24_344 nanoseconds. - Weight::from_ref_time(24_856_000 as u64) - .saturating_add(T::DbWeight::get().reads(2 as u64)) - .saturating_add(T::DbWeight::get().writes(4 as u64)) + // Proof Size summary in bytes: + // Measured: `109` + // Estimated: `5006` + // Minimum execution time: 16_334 nanoseconds. + Weight::from_parts(16_784_000, 5006) + .saturating_add(T::DbWeight::get().reads(2_u64)) + .saturating_add(T::DbWeight::get().writes(4_u64)) } - // Storage: RankedCollective Members (r:1 w:1) - // Storage: RankedCollective MemberCount (r:1 w:1) - // Storage: RankedCollective IdToIndex (r:1 w:1) - // Storage: RankedCollective IndexToId (r:1 w:1) + /// Storage: RankedCollective Members (r:1 w:1) + /// Proof: RankedCollective Members (max_values: None, max_size: Some(42), added: 2517, mode: MaxEncodedLen) + /// Storage: RankedCollective MemberCount (r:11 w:11) + /// Proof: RankedCollective MemberCount (max_values: None, max_size: Some(14), added: 2489, mode: MaxEncodedLen) + /// Storage: RankedCollective IdToIndex (r:11 w:11) + /// Proof: RankedCollective IdToIndex (max_values: None, max_size: Some(54), added: 2529, mode: MaxEncodedLen) + /// Storage: RankedCollective IndexToId (r:11 w:11) + /// Proof: RankedCollective IndexToId (max_values: None, max_size: Some(54), added: 2529, mode: MaxEncodedLen) /// The range of component `r` is `[0, 10]`. fn remove_member(r: u32, ) -> Weight { - // Minimum execution time: 36_881 nanoseconds. - Weight::from_ref_time(39_284_238 as u64) - // Standard Error: 16_355 - .saturating_add(Weight::from_ref_time(11_385_424 as u64).saturating_mul(r as u64)) - .saturating_add(T::DbWeight::get().reads(4 as u64)) - .saturating_add(T::DbWeight::get().reads((3 as u64).saturating_mul(r as u64))) - .saturating_add(T::DbWeight::get().writes(4 as u64)) - .saturating_add(T::DbWeight::get().writes((3 as u64).saturating_mul(r as u64))) + // Proof Size summary in bytes: + // Measured: `583 + r * (281 ±0)` + // Estimated: `10064 + r * (7547 ±0)` + // Minimum execution time: 26_177 nanoseconds. + Weight::from_parts(29_245_248, 10064) + // Standard Error: 18_611 + .saturating_add(Weight::from_ref_time(10_916_516).saturating_mul(r.into())) + .saturating_add(T::DbWeight::get().reads(4_u64)) + .saturating_add(T::DbWeight::get().reads((3_u64).saturating_mul(r.into()))) + .saturating_add(T::DbWeight::get().writes(4_u64)) + .saturating_add(T::DbWeight::get().writes((3_u64).saturating_mul(r.into()))) + .saturating_add(Weight::from_proof_size(7547).saturating_mul(r.into())) } - // Storage: RankedCollective Members (r:1 w:1) - // Storage: RankedCollective MemberCount (r:1 w:1) - // Storage: RankedCollective IndexToId (r:0 w:1) - // Storage: RankedCollective IdToIndex (r:0 w:1) + /// Storage: RankedCollective Members (r:1 w:1) + /// Proof: RankedCollective Members (max_values: None, max_size: Some(42), added: 2517, mode: MaxEncodedLen) + /// Storage: RankedCollective MemberCount (r:1 w:1) + /// Proof: RankedCollective MemberCount (max_values: None, max_size: Some(14), added: 2489, mode: MaxEncodedLen) + /// Storage: RankedCollective IndexToId (r:0 w:1) + /// Proof: RankedCollective IndexToId (max_values: None, max_size: Some(54), added: 2529, mode: MaxEncodedLen) + /// Storage: RankedCollective IdToIndex (r:0 w:1) + /// Proof: RankedCollective IdToIndex (max_values: None, max_size: Some(54), added: 2529, mode: MaxEncodedLen) /// The range of component `r` is `[0, 10]`. fn promote_member(r: u32, ) -> Weight { - // Minimum execution time: 27_444 nanoseconds. - Weight::from_ref_time(28_576_394 as u64) - // Standard Error: 4_818 - .saturating_add(Weight::from_ref_time(519_056 as u64).saturating_mul(r as u64)) - .saturating_add(T::DbWeight::get().reads(2 as u64)) - .saturating_add(T::DbWeight::get().writes(4 as u64)) + // Proof Size summary in bytes: + // Measured: `281 + r * (17 ±0)` + // Estimated: `5006` + // Minimum execution time: 18_953 nanoseconds. + Weight::from_parts(19_570_567, 5006) + // Standard Error: 4_156 + .saturating_add(Weight::from_ref_time(263_843).saturating_mul(r.into())) + .saturating_add(T::DbWeight::get().reads(2_u64)) + .saturating_add(T::DbWeight::get().writes(4_u64)) } - // Storage: RankedCollective Members (r:1 w:1) - // Storage: RankedCollective MemberCount (r:1 w:1) - // Storage: RankedCollective IdToIndex (r:1 w:1) - // Storage: RankedCollective IndexToId (r:1 w:1) + /// Storage: RankedCollective Members (r:1 w:1) + /// Proof: RankedCollective Members (max_values: None, max_size: Some(42), added: 2517, mode: MaxEncodedLen) + /// Storage: RankedCollective MemberCount (r:1 w:1) + /// Proof: RankedCollective MemberCount (max_values: None, max_size: Some(14), added: 2489, mode: MaxEncodedLen) + /// Storage: RankedCollective IdToIndex (r:1 w:1) + /// Proof: RankedCollective IdToIndex (max_values: None, max_size: Some(54), added: 2529, mode: MaxEncodedLen) + /// Storage: RankedCollective IndexToId (r:1 w:1) + /// Proof: RankedCollective IndexToId (max_values: None, max_size: Some(54), added: 2529, mode: MaxEncodedLen) /// The range of component `r` is `[0, 10]`. fn demote_member(r: u32, ) -> Weight { - // Minimum execution time: 36_539 nanoseconds. - Weight::from_ref_time(39_339_893 as u64) - // Standard Error: 16_526 - .saturating_add(Weight::from_ref_time(807_457 as u64).saturating_mul(r as u64)) - .saturating_add(T::DbWeight::get().reads(4 as u64)) - .saturating_add(T::DbWeight::get().writes(4 as u64)) + // Proof Size summary in bytes: + // Measured: `599 + r * (72 ±0)` + // Estimated: `10064` + // Minimum execution time: 26_243 nanoseconds. + Weight::from_parts(28_532_816, 10064) + // Standard Error: 22_689 + .saturating_add(Weight::from_ref_time(614_464).saturating_mul(r.into())) + .saturating_add(T::DbWeight::get().reads(4_u64)) + .saturating_add(T::DbWeight::get().writes(4_u64)) } - // Storage: RankedCollective Members (r:1 w:0) - // Storage: RankedPolls ReferendumInfoFor (r:1 w:1) - // Storage: RankedCollective Voting (r:1 w:1) - // Storage: Scheduler Agenda (r:2 w:2) + /// Storage: RankedCollective Members (r:1 w:0) + /// Proof: RankedCollective Members (max_values: None, max_size: Some(42), added: 2517, mode: MaxEncodedLen) + /// Storage: RankedPolls ReferendumInfoFor (r:1 w:1) + /// Proof: RankedPolls ReferendumInfoFor (max_values: None, max_size: Some(330), added: 2805, mode: MaxEncodedLen) + /// Storage: RankedCollective Voting (r:1 w:1) + /// Proof: RankedCollective Voting (max_values: None, max_size: Some(65), added: 2540, mode: MaxEncodedLen) + /// Storage: Scheduler Agenda (r:2 w:2) + /// Proof: Scheduler Agenda (max_values: None, max_size: Some(107022), added: 109497, mode: MaxEncodedLen) fn vote() -> Weight { - // Minimum execution time: 50_548 nanoseconds. - Weight::from_ref_time(51_276_000 as u64) - .saturating_add(T::DbWeight::get().reads(5 as u64)) - .saturating_add(T::DbWeight::get().writes(4 as u64)) + // Proof Size summary in bytes: + // Measured: `626` + // Estimated: `226856` + // Minimum execution time: 41_121 nanoseconds. + Weight::from_parts(41_606_000, 226856) + .saturating_add(T::DbWeight::get().reads(5_u64)) + .saturating_add(T::DbWeight::get().writes(4_u64)) } - // Storage: RankedPolls ReferendumInfoFor (r:1 w:0) - // Storage: RankedCollective VotingCleanup (r:1 w:0) - // Storage: RankedCollective Voting (r:0 w:2) + /// Storage: RankedPolls ReferendumInfoFor (r:1 w:0) + /// Proof: RankedPolls ReferendumInfoFor (max_values: None, max_size: Some(330), added: 2805, mode: MaxEncodedLen) + /// Storage: RankedCollective VotingCleanup (r:1 w:0) + /// Proof: RankedCollective VotingCleanup (max_values: None, max_size: Some(114), added: 2589, mode: MaxEncodedLen) + /// Storage: RankedCollective Voting (r:0 w:100) + /// Proof: RankedCollective Voting (max_values: None, max_size: Some(65), added: 2540, mode: MaxEncodedLen) /// The range of component `n` is `[0, 100]`. fn cleanup_poll(n: u32, ) -> Weight { - // Minimum execution time: 16_222 nanoseconds. - Weight::from_ref_time(22_982_955 as u64) - // Standard Error: 3_863 - .saturating_add(Weight::from_ref_time(1_074_054 as u64).saturating_mul(n as u64)) - .saturating_add(T::DbWeight::get().reads(2 as u64)) - .saturating_add(T::DbWeight::get().writes((1 as u64).saturating_mul(n as u64))) + // Proof Size summary in bytes: + // Measured: `461 + n * (50 ±0)` + // Estimated: `5394` + // Minimum execution time: 13_245 nanoseconds. + Weight::from_parts(17_420_271, 5394) + // Standard Error: 1_503 + .saturating_add(Weight::from_ref_time(952_500).saturating_mul(n.into())) + .saturating_add(T::DbWeight::get().reads(2_u64)) + .saturating_add(T::DbWeight::get().writes((1_u64).saturating_mul(n.into()))) } } // For backwards compatibility and tests impl WeightInfo for () { - // Storage: RankedCollective Members (r:1 w:1) - // Storage: RankedCollective MemberCount (r:1 w:1) - // Storage: RankedCollective IndexToId (r:0 w:1) - // Storage: RankedCollective IdToIndex (r:0 w:1) + /// Storage: RankedCollective Members (r:1 w:1) + /// Proof: RankedCollective Members (max_values: None, max_size: Some(42), added: 2517, mode: MaxEncodedLen) + /// Storage: RankedCollective MemberCount (r:1 w:1) + /// Proof: RankedCollective MemberCount (max_values: None, max_size: Some(14), added: 2489, mode: MaxEncodedLen) + /// Storage: RankedCollective IndexToId (r:0 w:1) + /// Proof: RankedCollective IndexToId (max_values: None, max_size: Some(54), added: 2529, mode: MaxEncodedLen) + /// Storage: RankedCollective IdToIndex (r:0 w:1) + /// Proof: RankedCollective IdToIndex (max_values: None, max_size: Some(54), added: 2529, mode: MaxEncodedLen) fn add_member() -> Weight { - // Minimum execution time: 24_344 nanoseconds. - Weight::from_ref_time(24_856_000 as u64) - .saturating_add(RocksDbWeight::get().reads(2 as u64)) - .saturating_add(RocksDbWeight::get().writes(4 as u64)) + // Proof Size summary in bytes: + // Measured: `109` + // Estimated: `5006` + // Minimum execution time: 16_334 nanoseconds. + Weight::from_parts(16_784_000, 5006) + .saturating_add(RocksDbWeight::get().reads(2_u64)) + .saturating_add(RocksDbWeight::get().writes(4_u64)) } - // Storage: RankedCollective Members (r:1 w:1) - // Storage: RankedCollective MemberCount (r:1 w:1) - // Storage: RankedCollective IdToIndex (r:1 w:1) - // Storage: RankedCollective IndexToId (r:1 w:1) + /// Storage: RankedCollective Members (r:1 w:1) + /// Proof: RankedCollective Members (max_values: None, max_size: Some(42), added: 2517, mode: MaxEncodedLen) + /// Storage: RankedCollective MemberCount (r:11 w:11) + /// Proof: RankedCollective MemberCount (max_values: None, max_size: Some(14), added: 2489, mode: MaxEncodedLen) + /// Storage: RankedCollective IdToIndex (r:11 w:11) + /// Proof: RankedCollective IdToIndex (max_values: None, max_size: Some(54), added: 2529, mode: MaxEncodedLen) + /// Storage: RankedCollective IndexToId (r:11 w:11) + /// Proof: RankedCollective IndexToId (max_values: None, max_size: Some(54), added: 2529, mode: MaxEncodedLen) /// The range of component `r` is `[0, 10]`. fn remove_member(r: u32, ) -> Weight { - // Minimum execution time: 36_881 nanoseconds. - Weight::from_ref_time(39_284_238 as u64) - // Standard Error: 16_355 - .saturating_add(Weight::from_ref_time(11_385_424 as u64).saturating_mul(r as u64)) - .saturating_add(RocksDbWeight::get().reads(4 as u64)) - .saturating_add(RocksDbWeight::get().reads((3 as u64).saturating_mul(r as u64))) - .saturating_add(RocksDbWeight::get().writes(4 as u64)) - .saturating_add(RocksDbWeight::get().writes((3 as u64).saturating_mul(r as u64))) + // Proof Size summary in bytes: + // Measured: `583 + r * (281 ±0)` + // Estimated: `10064 + r * (7547 ±0)` + // Minimum execution time: 26_177 nanoseconds. + Weight::from_parts(29_245_248, 10064) + // Standard Error: 18_611 + .saturating_add(Weight::from_ref_time(10_916_516).saturating_mul(r.into())) + .saturating_add(RocksDbWeight::get().reads(4_u64)) + .saturating_add(RocksDbWeight::get().reads((3_u64).saturating_mul(r.into()))) + .saturating_add(RocksDbWeight::get().writes(4_u64)) + .saturating_add(RocksDbWeight::get().writes((3_u64).saturating_mul(r.into()))) + .saturating_add(Weight::from_proof_size(7547).saturating_mul(r.into())) } - // Storage: RankedCollective Members (r:1 w:1) - // Storage: RankedCollective MemberCount (r:1 w:1) - // Storage: RankedCollective IndexToId (r:0 w:1) - // Storage: RankedCollective IdToIndex (r:0 w:1) + /// Storage: RankedCollective Members (r:1 w:1) + /// Proof: RankedCollective Members (max_values: None, max_size: Some(42), added: 2517, mode: MaxEncodedLen) + /// Storage: RankedCollective MemberCount (r:1 w:1) + /// Proof: RankedCollective MemberCount (max_values: None, max_size: Some(14), added: 2489, mode: MaxEncodedLen) + /// Storage: RankedCollective IndexToId (r:0 w:1) + /// Proof: RankedCollective IndexToId (max_values: None, max_size: Some(54), added: 2529, mode: MaxEncodedLen) + /// Storage: RankedCollective IdToIndex (r:0 w:1) + /// Proof: RankedCollective IdToIndex (max_values: None, max_size: Some(54), added: 2529, mode: MaxEncodedLen) /// The range of component `r` is `[0, 10]`. fn promote_member(r: u32, ) -> Weight { - // Minimum execution time: 27_444 nanoseconds. - Weight::from_ref_time(28_576_394 as u64) - // Standard Error: 4_818 - .saturating_add(Weight::from_ref_time(519_056 as u64).saturating_mul(r as u64)) - .saturating_add(RocksDbWeight::get().reads(2 as u64)) - .saturating_add(RocksDbWeight::get().writes(4 as u64)) + // Proof Size summary in bytes: + // Measured: `281 + r * (17 ±0)` + // Estimated: `5006` + // Minimum execution time: 18_953 nanoseconds. + Weight::from_parts(19_570_567, 5006) + // Standard Error: 4_156 + .saturating_add(Weight::from_ref_time(263_843).saturating_mul(r.into())) + .saturating_add(RocksDbWeight::get().reads(2_u64)) + .saturating_add(RocksDbWeight::get().writes(4_u64)) } - // Storage: RankedCollective Members (r:1 w:1) - // Storage: RankedCollective MemberCount (r:1 w:1) - // Storage: RankedCollective IdToIndex (r:1 w:1) - // Storage: RankedCollective IndexToId (r:1 w:1) + /// Storage: RankedCollective Members (r:1 w:1) + /// Proof: RankedCollective Members (max_values: None, max_size: Some(42), added: 2517, mode: MaxEncodedLen) + /// Storage: RankedCollective MemberCount (r:1 w:1) + /// Proof: RankedCollective MemberCount (max_values: None, max_size: Some(14), added: 2489, mode: MaxEncodedLen) + /// Storage: RankedCollective IdToIndex (r:1 w:1) + /// Proof: RankedCollective IdToIndex (max_values: None, max_size: Some(54), added: 2529, mode: MaxEncodedLen) + /// Storage: RankedCollective IndexToId (r:1 w:1) + /// Proof: RankedCollective IndexToId (max_values: None, max_size: Some(54), added: 2529, mode: MaxEncodedLen) /// The range of component `r` is `[0, 10]`. fn demote_member(r: u32, ) -> Weight { - // Minimum execution time: 36_539 nanoseconds. - Weight::from_ref_time(39_339_893 as u64) - // Standard Error: 16_526 - .saturating_add(Weight::from_ref_time(807_457 as u64).saturating_mul(r as u64)) - .saturating_add(RocksDbWeight::get().reads(4 as u64)) - .saturating_add(RocksDbWeight::get().writes(4 as u64)) + // Proof Size summary in bytes: + // Measured: `599 + r * (72 ±0)` + // Estimated: `10064` + // Minimum execution time: 26_243 nanoseconds. + Weight::from_parts(28_532_816, 10064) + // Standard Error: 22_689 + .saturating_add(Weight::from_ref_time(614_464).saturating_mul(r.into())) + .saturating_add(RocksDbWeight::get().reads(4_u64)) + .saturating_add(RocksDbWeight::get().writes(4_u64)) } - // Storage: RankedCollective Members (r:1 w:0) - // Storage: RankedPolls ReferendumInfoFor (r:1 w:1) - // Storage: RankedCollective Voting (r:1 w:1) - // Storage: Scheduler Agenda (r:2 w:2) + /// Storage: RankedCollective Members (r:1 w:0) + /// Proof: RankedCollective Members (max_values: None, max_size: Some(42), added: 2517, mode: MaxEncodedLen) + /// Storage: RankedPolls ReferendumInfoFor (r:1 w:1) + /// Proof: RankedPolls ReferendumInfoFor (max_values: None, max_size: Some(330), added: 2805, mode: MaxEncodedLen) + /// Storage: RankedCollective Voting (r:1 w:1) + /// Proof: RankedCollective Voting (max_values: None, max_size: Some(65), added: 2540, mode: MaxEncodedLen) + /// Storage: Scheduler Agenda (r:2 w:2) + /// Proof: Scheduler Agenda (max_values: None, max_size: Some(107022), added: 109497, mode: MaxEncodedLen) fn vote() -> Weight { - // Minimum execution time: 50_548 nanoseconds. - Weight::from_ref_time(51_276_000 as u64) - .saturating_add(RocksDbWeight::get().reads(5 as u64)) - .saturating_add(RocksDbWeight::get().writes(4 as u64)) + // Proof Size summary in bytes: + // Measured: `626` + // Estimated: `226856` + // Minimum execution time: 41_121 nanoseconds. + Weight::from_parts(41_606_000, 226856) + .saturating_add(RocksDbWeight::get().reads(5_u64)) + .saturating_add(RocksDbWeight::get().writes(4_u64)) } - // Storage: RankedPolls ReferendumInfoFor (r:1 w:0) - // Storage: RankedCollective VotingCleanup (r:1 w:0) - // Storage: RankedCollective Voting (r:0 w:2) + /// Storage: RankedPolls ReferendumInfoFor (r:1 w:0) + /// Proof: RankedPolls ReferendumInfoFor (max_values: None, max_size: Some(330), added: 2805, mode: MaxEncodedLen) + /// Storage: RankedCollective VotingCleanup (r:1 w:0) + /// Proof: RankedCollective VotingCleanup (max_values: None, max_size: Some(114), added: 2589, mode: MaxEncodedLen) + /// Storage: RankedCollective Voting (r:0 w:100) + /// Proof: RankedCollective Voting (max_values: None, max_size: Some(65), added: 2540, mode: MaxEncodedLen) /// The range of component `n` is `[0, 100]`. fn cleanup_poll(n: u32, ) -> Weight { - // Minimum execution time: 16_222 nanoseconds. - Weight::from_ref_time(22_982_955 as u64) - // Standard Error: 3_863 - .saturating_add(Weight::from_ref_time(1_074_054 as u64).saturating_mul(n as u64)) - .saturating_add(RocksDbWeight::get().reads(2 as u64)) - .saturating_add(RocksDbWeight::get().writes((1 as u64).saturating_mul(n as u64))) + // Proof Size summary in bytes: + // Measured: `461 + n * (50 ±0)` + // Estimated: `5394` + // Minimum execution time: 13_245 nanoseconds. + Weight::from_parts(17_420_271, 5394) + // Standard Error: 1_503 + .saturating_add(Weight::from_ref_time(952_500).saturating_mul(n.into())) + .saturating_add(RocksDbWeight::get().reads(2_u64)) + .saturating_add(RocksDbWeight::get().writes((1_u64).saturating_mul(n.into()))) } } diff --git a/frame/recovery/src/weights.rs b/frame/recovery/src/weights.rs index 39a8d09a3..73f61654a 100644 --- a/frame/recovery/src/weights.rs +++ b/frame/recovery/src/weights.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2022 Parity Technologies (UK) Ltd. +// Copyright (C) 2023 Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); @@ -18,7 +18,8 @@ //! Autogenerated weights for pallet_recovery //! //! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev -//! DATE: 2022-11-07, STEPS: `50`, REPEAT: 20, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! DATE: 2023-01-24, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! WORST CASE MAP SIZE: `1000000` //! HOSTNAME: `bm2`, CPU: `Intel(R) Core(TM) i7-7700K CPU @ 4.20GHz` //! EXECUTION: Some(Wasm), WASM-EXECUTION: Compiled, CHAIN: Some("dev"), DB CACHE: 1024 @@ -61,172 +62,256 @@ pub trait WeightInfo { /// Weights for pallet_recovery using the Substrate node and recommended hardware. pub struct SubstrateWeight(PhantomData); impl WeightInfo for SubstrateWeight { - // Storage: Recovery Proxy (r:1 w:0) + /// Storage: Recovery Proxy (r:1 w:0) + /// Proof: Recovery Proxy (max_values: None, max_size: Some(80), added: 2555, mode: MaxEncodedLen) fn as_recovered() -> Weight { - // Minimum execution time: 10_672 nanoseconds. - Weight::from_ref_time(10_946_000 as u64) - .saturating_add(T::DbWeight::get().reads(1 as u64)) + // Proof Size summary in bytes: + // Measured: `281` + // Estimated: `2555` + // Minimum execution time: 8_866 nanoseconds. + Weight::from_parts(9_065_000, 2555) + .saturating_add(T::DbWeight::get().reads(1_u64)) } - // Storage: Recovery Proxy (r:0 w:1) + /// Storage: Recovery Proxy (r:0 w:1) + /// Proof: Recovery Proxy (max_values: None, max_size: Some(80), added: 2555, mode: MaxEncodedLen) fn set_recovered() -> Weight { - // Minimum execution time: 17_092 nanoseconds. - Weight::from_ref_time(17_660_000 as u64) - .saturating_add(T::DbWeight::get().writes(1 as u64)) + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 8_893 nanoseconds. + Weight::from_ref_time(9_177_000) + .saturating_add(T::DbWeight::get().writes(1_u64)) } - // Storage: Recovery Recoverable (r:1 w:1) + /// Storage: Recovery Recoverable (r:1 w:1) + /// Proof: Recovery Recoverable (max_values: None, max_size: Some(351), added: 2826, mode: MaxEncodedLen) /// The range of component `n` is `[1, 9]`. fn create_recovery(n: u32, ) -> Weight { - // Minimum execution time: 32_800 nanoseconds. - Weight::from_ref_time(33_769_078 as u64) - // Standard Error: 4_075 - .saturating_add(Weight::from_ref_time(252_382 as u64).saturating_mul(n as u64)) - .saturating_add(T::DbWeight::get().reads(1 as u64)) - .saturating_add(T::DbWeight::get().writes(1 as u64)) - } - // Storage: Recovery Recoverable (r:1 w:0) - // Storage: Recovery ActiveRecoveries (r:1 w:1) + // Proof Size summary in bytes: + // Measured: `175` + // Estimated: `2826` + // Minimum execution time: 20_662 nanoseconds. + Weight::from_parts(21_378_064, 2826) + // Standard Error: 3_350 + .saturating_add(Weight::from_ref_time(83_738).saturating_mul(n.into())) + .saturating_add(T::DbWeight::get().reads(1_u64)) + .saturating_add(T::DbWeight::get().writes(1_u64)) + } + /// Storage: Recovery Recoverable (r:1 w:0) + /// Proof: Recovery Recoverable (max_values: None, max_size: Some(351), added: 2826, mode: MaxEncodedLen) + /// Storage: Recovery ActiveRecoveries (r:1 w:1) + /// Proof: Recovery ActiveRecoveries (max_values: None, max_size: Some(389), added: 2864, mode: MaxEncodedLen) fn initiate_recovery() -> Weight { - // Minimum execution time: 39_224 nanoseconds. - Weight::from_ref_time(39_663_000 as u64) - .saturating_add(T::DbWeight::get().reads(2 as u64)) - .saturating_add(T::DbWeight::get().writes(1 as u64)) + // Proof Size summary in bytes: + // Measured: `272` + // Estimated: `5690` + // Minimum execution time: 24_805 nanoseconds. + Weight::from_parts(25_273_000, 5690) + .saturating_add(T::DbWeight::get().reads(2_u64)) + .saturating_add(T::DbWeight::get().writes(1_u64)) } - // Storage: Recovery Recoverable (r:1 w:0) - // Storage: Recovery ActiveRecoveries (r:1 w:1) + /// Storage: Recovery Recoverable (r:1 w:0) + /// Proof: Recovery Recoverable (max_values: None, max_size: Some(351), added: 2826, mode: MaxEncodedLen) + /// Storage: Recovery ActiveRecoveries (r:1 w:1) + /// Proof: Recovery ActiveRecoveries (max_values: None, max_size: Some(389), added: 2864, mode: MaxEncodedLen) /// The range of component `n` is `[1, 9]`. fn vouch_recovery(n: u32, ) -> Weight { - // Minimum execution time: 27_158 nanoseconds. - Weight::from_ref_time(28_130_506 as u64) - // Standard Error: 4_523 - .saturating_add(Weight::from_ref_time(321_436 as u64).saturating_mul(n as u64)) - .saturating_add(T::DbWeight::get().reads(2 as u64)) - .saturating_add(T::DbWeight::get().writes(1 as u64)) - } - // Storage: Recovery Recoverable (r:1 w:0) - // Storage: Recovery ActiveRecoveries (r:1 w:0) - // Storage: Recovery Proxy (r:1 w:1) + // Proof Size summary in bytes: + // Measured: `423 + n * (64 ±0)` + // Estimated: `5690` + // Minimum execution time: 17_837 nanoseconds. + Weight::from_parts(18_429_664, 5690) + // Standard Error: 3_187 + .saturating_add(Weight::from_ref_time(143_648).saturating_mul(n.into())) + .saturating_add(T::DbWeight::get().reads(2_u64)) + .saturating_add(T::DbWeight::get().writes(1_u64)) + } + /// Storage: Recovery Recoverable (r:1 w:0) + /// Proof: Recovery Recoverable (max_values: None, max_size: Some(351), added: 2826, mode: MaxEncodedLen) + /// Storage: Recovery ActiveRecoveries (r:1 w:0) + /// Proof: Recovery ActiveRecoveries (max_values: None, max_size: Some(389), added: 2864, mode: MaxEncodedLen) + /// Storage: Recovery Proxy (r:1 w:1) + /// Proof: Recovery Proxy (max_values: None, max_size: Some(80), added: 2555, mode: MaxEncodedLen) /// The range of component `n` is `[1, 9]`. fn claim_recovery(n: u32, ) -> Weight { - // Minimum execution time: 36_269 nanoseconds. - Weight::from_ref_time(36_966_173 as u64) - // Standard Error: 5_016 - .saturating_add(Weight::from_ref_time(223_069 as u64).saturating_mul(n as u64)) - .saturating_add(T::DbWeight::get().reads(3 as u64)) - .saturating_add(T::DbWeight::get().writes(1 as u64)) - } - // Storage: Recovery ActiveRecoveries (r:1 w:1) - // Storage: System Account (r:1 w:1) + // Proof Size summary in bytes: + // Measured: `455 + n * (64 ±0)` + // Estimated: `8245` + // Minimum execution time: 21_960 nanoseconds. + Weight::from_parts(22_529_644, 8245) + // Standard Error: 2_945 + .saturating_add(Weight::from_ref_time(85_604).saturating_mul(n.into())) + .saturating_add(T::DbWeight::get().reads(3_u64)) + .saturating_add(T::DbWeight::get().writes(1_u64)) + } + /// Storage: Recovery ActiveRecoveries (r:1 w:1) + /// Proof: Recovery ActiveRecoveries (max_values: None, max_size: Some(389), added: 2864, mode: MaxEncodedLen) + /// Storage: System Account (r:1 w:1) + /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) /// The range of component `n` is `[1, 9]`. fn close_recovery(n: u32, ) -> Weight { - // Minimum execution time: 40_213 nanoseconds. - Weight::from_ref_time(41_140_968 as u64) - // Standard Error: 3_822 - .saturating_add(Weight::from_ref_time(163_217 as u64).saturating_mul(n as u64)) - .saturating_add(T::DbWeight::get().reads(2 as u64)) - .saturating_add(T::DbWeight::get().writes(2 as u64)) - } - // Storage: Recovery ActiveRecoveries (r:1 w:0) - // Storage: Recovery Recoverable (r:1 w:1) + // Proof Size summary in bytes: + // Measured: `576 + n * (32 ±0)` + // Estimated: `5467` + // Minimum execution time: 26_054 nanoseconds. + Weight::from_parts(26_724_866, 5467) + // Standard Error: 2_645 + .saturating_add(Weight::from_ref_time(104_301).saturating_mul(n.into())) + .saturating_add(T::DbWeight::get().reads(2_u64)) + .saturating_add(T::DbWeight::get().writes(2_u64)) + } + /// Storage: Recovery ActiveRecoveries (r:1 w:0) + /// Proof: Recovery ActiveRecoveries (max_values: None, max_size: Some(389), added: 2864, mode: MaxEncodedLen) + /// Storage: Recovery Recoverable (r:1 w:1) + /// Proof: Recovery Recoverable (max_values: None, max_size: Some(351), added: 2826, mode: MaxEncodedLen) /// The range of component `n` is `[1, 9]`. fn remove_recovery(n: u32, ) -> Weight { - // Minimum execution time: 38_740 nanoseconds. - Weight::from_ref_time(39_710_400 as u64) - // Standard Error: 5_554 - .saturating_add(Weight::from_ref_time(224_200 as u64).saturating_mul(n as u64)) - .saturating_add(T::DbWeight::get().reads(2 as u64)) - .saturating_add(T::DbWeight::get().writes(1 as u64)) - } - // Storage: Recovery Proxy (r:1 w:1) + // Proof Size summary in bytes: + // Measured: `302 + n * (32 ±0)` + // Estimated: `5690` + // Minimum execution time: 25_110 nanoseconds. + Weight::from_parts(25_805_837, 5690) + // Standard Error: 2_732 + .saturating_add(Weight::from_ref_time(73_458).saturating_mul(n.into())) + .saturating_add(T::DbWeight::get().reads(2_u64)) + .saturating_add(T::DbWeight::get().writes(1_u64)) + } + /// Storage: Recovery Proxy (r:1 w:1) + /// Proof: Recovery Proxy (max_values: None, max_size: Some(80), added: 2555, mode: MaxEncodedLen) fn cancel_recovered() -> Weight { - // Minimum execution time: 20_316 nanoseconds. - Weight::from_ref_time(20_912_000 as u64) - .saturating_add(T::DbWeight::get().reads(1 as u64)) - .saturating_add(T::DbWeight::get().writes(1 as u64)) + // Proof Size summary in bytes: + // Measured: `281` + // Estimated: `2555` + // Minimum execution time: 11_061 nanoseconds. + Weight::from_parts(11_291_000, 2555) + .saturating_add(T::DbWeight::get().reads(1_u64)) + .saturating_add(T::DbWeight::get().writes(1_u64)) } } // For backwards compatibility and tests impl WeightInfo for () { - // Storage: Recovery Proxy (r:1 w:0) + /// Storage: Recovery Proxy (r:1 w:0) + /// Proof: Recovery Proxy (max_values: None, max_size: Some(80), added: 2555, mode: MaxEncodedLen) fn as_recovered() -> Weight { - // Minimum execution time: 10_672 nanoseconds. - Weight::from_ref_time(10_946_000 as u64) - .saturating_add(RocksDbWeight::get().reads(1 as u64)) + // Proof Size summary in bytes: + // Measured: `281` + // Estimated: `2555` + // Minimum execution time: 8_866 nanoseconds. + Weight::from_parts(9_065_000, 2555) + .saturating_add(RocksDbWeight::get().reads(1_u64)) } - // Storage: Recovery Proxy (r:0 w:1) + /// Storage: Recovery Proxy (r:0 w:1) + /// Proof: Recovery Proxy (max_values: None, max_size: Some(80), added: 2555, mode: MaxEncodedLen) fn set_recovered() -> Weight { - // Minimum execution time: 17_092 nanoseconds. - Weight::from_ref_time(17_660_000 as u64) - .saturating_add(RocksDbWeight::get().writes(1 as u64)) + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 8_893 nanoseconds. + Weight::from_ref_time(9_177_000) + .saturating_add(RocksDbWeight::get().writes(1_u64)) } - // Storage: Recovery Recoverable (r:1 w:1) + /// Storage: Recovery Recoverable (r:1 w:1) + /// Proof: Recovery Recoverable (max_values: None, max_size: Some(351), added: 2826, mode: MaxEncodedLen) /// The range of component `n` is `[1, 9]`. fn create_recovery(n: u32, ) -> Weight { - // Minimum execution time: 32_800 nanoseconds. - Weight::from_ref_time(33_769_078 as u64) - // Standard Error: 4_075 - .saturating_add(Weight::from_ref_time(252_382 as u64).saturating_mul(n as u64)) - .saturating_add(RocksDbWeight::get().reads(1 as u64)) - .saturating_add(RocksDbWeight::get().writes(1 as u64)) - } - // Storage: Recovery Recoverable (r:1 w:0) - // Storage: Recovery ActiveRecoveries (r:1 w:1) + // Proof Size summary in bytes: + // Measured: `175` + // Estimated: `2826` + // Minimum execution time: 20_662 nanoseconds. + Weight::from_parts(21_378_064, 2826) + // Standard Error: 3_350 + .saturating_add(Weight::from_ref_time(83_738).saturating_mul(n.into())) + .saturating_add(RocksDbWeight::get().reads(1_u64)) + .saturating_add(RocksDbWeight::get().writes(1_u64)) + } + /// Storage: Recovery Recoverable (r:1 w:0) + /// Proof: Recovery Recoverable (max_values: None, max_size: Some(351), added: 2826, mode: MaxEncodedLen) + /// Storage: Recovery ActiveRecoveries (r:1 w:1) + /// Proof: Recovery ActiveRecoveries (max_values: None, max_size: Some(389), added: 2864, mode: MaxEncodedLen) fn initiate_recovery() -> Weight { - // Minimum execution time: 39_224 nanoseconds. - Weight::from_ref_time(39_663_000 as u64) - .saturating_add(RocksDbWeight::get().reads(2 as u64)) - .saturating_add(RocksDbWeight::get().writes(1 as u64)) + // Proof Size summary in bytes: + // Measured: `272` + // Estimated: `5690` + // Minimum execution time: 24_805 nanoseconds. + Weight::from_parts(25_273_000, 5690) + .saturating_add(RocksDbWeight::get().reads(2_u64)) + .saturating_add(RocksDbWeight::get().writes(1_u64)) } - // Storage: Recovery Recoverable (r:1 w:0) - // Storage: Recovery ActiveRecoveries (r:1 w:1) + /// Storage: Recovery Recoverable (r:1 w:0) + /// Proof: Recovery Recoverable (max_values: None, max_size: Some(351), added: 2826, mode: MaxEncodedLen) + /// Storage: Recovery ActiveRecoveries (r:1 w:1) + /// Proof: Recovery ActiveRecoveries (max_values: None, max_size: Some(389), added: 2864, mode: MaxEncodedLen) /// The range of component `n` is `[1, 9]`. fn vouch_recovery(n: u32, ) -> Weight { - // Minimum execution time: 27_158 nanoseconds. - Weight::from_ref_time(28_130_506 as u64) - // Standard Error: 4_523 - .saturating_add(Weight::from_ref_time(321_436 as u64).saturating_mul(n as u64)) - .saturating_add(RocksDbWeight::get().reads(2 as u64)) - .saturating_add(RocksDbWeight::get().writes(1 as u64)) - } - // Storage: Recovery Recoverable (r:1 w:0) - // Storage: Recovery ActiveRecoveries (r:1 w:0) - // Storage: Recovery Proxy (r:1 w:1) + // Proof Size summary in bytes: + // Measured: `423 + n * (64 ±0)` + // Estimated: `5690` + // Minimum execution time: 17_837 nanoseconds. + Weight::from_parts(18_429_664, 5690) + // Standard Error: 3_187 + .saturating_add(Weight::from_ref_time(143_648).saturating_mul(n.into())) + .saturating_add(RocksDbWeight::get().reads(2_u64)) + .saturating_add(RocksDbWeight::get().writes(1_u64)) + } + /// Storage: Recovery Recoverable (r:1 w:0) + /// Proof: Recovery Recoverable (max_values: None, max_size: Some(351), added: 2826, mode: MaxEncodedLen) + /// Storage: Recovery ActiveRecoveries (r:1 w:0) + /// Proof: Recovery ActiveRecoveries (max_values: None, max_size: Some(389), added: 2864, mode: MaxEncodedLen) + /// Storage: Recovery Proxy (r:1 w:1) + /// Proof: Recovery Proxy (max_values: None, max_size: Some(80), added: 2555, mode: MaxEncodedLen) /// The range of component `n` is `[1, 9]`. fn claim_recovery(n: u32, ) -> Weight { - // Minimum execution time: 36_269 nanoseconds. - Weight::from_ref_time(36_966_173 as u64) - // Standard Error: 5_016 - .saturating_add(Weight::from_ref_time(223_069 as u64).saturating_mul(n as u64)) - .saturating_add(RocksDbWeight::get().reads(3 as u64)) - .saturating_add(RocksDbWeight::get().writes(1 as u64)) - } - // Storage: Recovery ActiveRecoveries (r:1 w:1) - // Storage: System Account (r:1 w:1) + // Proof Size summary in bytes: + // Measured: `455 + n * (64 ±0)` + // Estimated: `8245` + // Minimum execution time: 21_960 nanoseconds. + Weight::from_parts(22_529_644, 8245) + // Standard Error: 2_945 + .saturating_add(Weight::from_ref_time(85_604).saturating_mul(n.into())) + .saturating_add(RocksDbWeight::get().reads(3_u64)) + .saturating_add(RocksDbWeight::get().writes(1_u64)) + } + /// Storage: Recovery ActiveRecoveries (r:1 w:1) + /// Proof: Recovery ActiveRecoveries (max_values: None, max_size: Some(389), added: 2864, mode: MaxEncodedLen) + /// Storage: System Account (r:1 w:1) + /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) /// The range of component `n` is `[1, 9]`. fn close_recovery(n: u32, ) -> Weight { - // Minimum execution time: 40_213 nanoseconds. - Weight::from_ref_time(41_140_968 as u64) - // Standard Error: 3_822 - .saturating_add(Weight::from_ref_time(163_217 as u64).saturating_mul(n as u64)) - .saturating_add(RocksDbWeight::get().reads(2 as u64)) - .saturating_add(RocksDbWeight::get().writes(2 as u64)) - } - // Storage: Recovery ActiveRecoveries (r:1 w:0) - // Storage: Recovery Recoverable (r:1 w:1) + // Proof Size summary in bytes: + // Measured: `576 + n * (32 ±0)` + // Estimated: `5467` + // Minimum execution time: 26_054 nanoseconds. + Weight::from_parts(26_724_866, 5467) + // Standard Error: 2_645 + .saturating_add(Weight::from_ref_time(104_301).saturating_mul(n.into())) + .saturating_add(RocksDbWeight::get().reads(2_u64)) + .saturating_add(RocksDbWeight::get().writes(2_u64)) + } + /// Storage: Recovery ActiveRecoveries (r:1 w:0) + /// Proof: Recovery ActiveRecoveries (max_values: None, max_size: Some(389), added: 2864, mode: MaxEncodedLen) + /// Storage: Recovery Recoverable (r:1 w:1) + /// Proof: Recovery Recoverable (max_values: None, max_size: Some(351), added: 2826, mode: MaxEncodedLen) /// The range of component `n` is `[1, 9]`. fn remove_recovery(n: u32, ) -> Weight { - // Minimum execution time: 38_740 nanoseconds. - Weight::from_ref_time(39_710_400 as u64) - // Standard Error: 5_554 - .saturating_add(Weight::from_ref_time(224_200 as u64).saturating_mul(n as u64)) - .saturating_add(RocksDbWeight::get().reads(2 as u64)) - .saturating_add(RocksDbWeight::get().writes(1 as u64)) - } - // Storage: Recovery Proxy (r:1 w:1) + // Proof Size summary in bytes: + // Measured: `302 + n * (32 ±0)` + // Estimated: `5690` + // Minimum execution time: 25_110 nanoseconds. + Weight::from_parts(25_805_837, 5690) + // Standard Error: 2_732 + .saturating_add(Weight::from_ref_time(73_458).saturating_mul(n.into())) + .saturating_add(RocksDbWeight::get().reads(2_u64)) + .saturating_add(RocksDbWeight::get().writes(1_u64)) + } + /// Storage: Recovery Proxy (r:1 w:1) + /// Proof: Recovery Proxy (max_values: None, max_size: Some(80), added: 2555, mode: MaxEncodedLen) fn cancel_recovered() -> Weight { - // Minimum execution time: 20_316 nanoseconds. - Weight::from_ref_time(20_912_000 as u64) - .saturating_add(RocksDbWeight::get().reads(1 as u64)) - .saturating_add(RocksDbWeight::get().writes(1 as u64)) + // Proof Size summary in bytes: + // Measured: `281` + // Estimated: `2555` + // Minimum execution time: 11_061 nanoseconds. + Weight::from_parts(11_291_000, 2555) + .saturating_add(RocksDbWeight::get().reads(1_u64)) + .saturating_add(RocksDbWeight::get().writes(1_u64)) } } diff --git a/frame/referenda/src/mock.rs b/frame/referenda/src/mock.rs index e957027e5..82ae508d5 100644 --- a/frame/referenda/src/mock.rs +++ b/frame/referenda/src/mock.rs @@ -62,7 +62,7 @@ impl Contains for BaseFilter { } parameter_types! { - pub MaxWeight: Weight = Weight::from_ref_time(2_000_000_000_000); + pub MaxWeight: Weight = Weight::from_parts(2_000_000_000_000, u64::MAX); } impl frame_system::Config for Test { type BaseCallFilter = BaseFilter; diff --git a/frame/referenda/src/weights.rs b/frame/referenda/src/weights.rs index f0eae517a..68b44e8be 100644 --- a/frame/referenda/src/weights.rs +++ b/frame/referenda/src/weights.rs @@ -1,3 +1,8 @@ +// This file is part of Substrate. + +// Copyright (C) 2023 Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: Apache-2.0 + // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at @@ -13,22 +18,25 @@ //! Autogenerated weights for pallet_referenda //! //! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev -//! DATE: 2022-11-27, STEPS: `20`, REPEAT: 1, LOW RANGE: `[]`, HIGH RANGE: `[]` -//! HOSTNAME: `cob`, CPU: `` -//! EXECUTION: None, WASM-EXECUTION: Compiled, CHAIN: Some("dev"), DB CACHE: 1024 +//! DATE: 2023-01-24, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! WORST CASE MAP SIZE: `1000000` +//! HOSTNAME: `bm2`, CPU: `Intel(R) Core(TM) i7-7700K CPU @ 4.20GHz` +//! EXECUTION: Some(Wasm), WASM-EXECUTION: Compiled, CHAIN: Some("dev"), DB CACHE: 1024 // Executed Command: -// ./target/release/substrate +// ./target/production/substrate // benchmark // pallet // --chain=dev -// --steps=20 -// --repeat=1 -// --pallet=pallet-referenda +// --steps=50 +// --repeat=20 +// --pallet=pallet_referenda // --extrinsic=* +// --execution=wasm // --wasm-execution=compiled // --heap-pages=4096 -// --output=./frame/referenda/src/._weights.rs +// --output=./frame/referenda/src/weights.rs +// --header=./HEADER-APACHE2 // --template=./.maintain/frame-weight-template.hbs #![cfg_attr(rustfmt, rustfmt_skip)] @@ -73,482 +81,788 @@ pub trait WeightInfo { /// Weights for pallet_referenda using the Substrate node and recommended hardware. pub struct SubstrateWeight(PhantomData); impl WeightInfo for SubstrateWeight { - // Storage: Referenda ReferendumCount (r:1 w:1) - // Storage: Scheduler Agenda (r:1 w:1) - // Storage: Referenda ReferendumInfoFor (r:0 w:1) + /// Storage: Referenda ReferendumCount (r:1 w:1) + /// Proof: Referenda ReferendumCount (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) + /// Storage: Scheduler Agenda (r:1 w:1) + /// Proof: Scheduler Agenda (max_values: None, max_size: Some(107022), added: 109497, mode: MaxEncodedLen) + /// Storage: Referenda ReferendumInfoFor (r:0 w:1) + /// Proof: Referenda ReferendumInfoFor (max_values: None, max_size: Some(366), added: 2841, mode: MaxEncodedLen) fn submit() -> Weight { - // Minimum execution time: 29_000 nanoseconds. - Weight::from_ref_time(29_000_000) - .saturating_add(T::DbWeight::get().reads(2)) - .saturating_add(T::DbWeight::get().writes(3)) - } - // Storage: Referenda ReferendumInfoFor (r:1 w:1) - // Storage: Scheduler Agenda (r:2 w:2) + // Proof Size summary in bytes: + // Measured: `251` + // Estimated: `109996` + // Minimum execution time: 32_207 nanoseconds. + Weight::from_parts(32_639_000, 109996) + .saturating_add(T::DbWeight::get().reads(2_u64)) + .saturating_add(T::DbWeight::get().writes(3_u64)) + } + /// Storage: Referenda ReferendumInfoFor (r:1 w:1) + /// Proof: Referenda ReferendumInfoFor (max_values: None, max_size: Some(366), added: 2841, mode: MaxEncodedLen) + /// Storage: Scheduler Agenda (r:2 w:2) + /// Proof: Scheduler Agenda (max_values: None, max_size: Some(107022), added: 109497, mode: MaxEncodedLen) fn place_decision_deposit_preparing() -> Weight { - // Minimum execution time: 35_000 nanoseconds. - Weight::from_ref_time(35_000_000) - .saturating_add(T::DbWeight::get().reads(3)) - .saturating_add(T::DbWeight::get().writes(3)) - } - // Storage: Referenda ReferendumInfoFor (r:1 w:1) - // Storage: Referenda DecidingCount (r:1 w:0) - // Storage: Referenda TrackQueue (r:1 w:1) + // Proof Size summary in bytes: + // Measured: `536` + // Estimated: `221835` + // Minimum execution time: 43_766 nanoseconds. + Weight::from_parts(44_494_000, 221835) + .saturating_add(T::DbWeight::get().reads(3_u64)) + .saturating_add(T::DbWeight::get().writes(3_u64)) + } + /// Storage: Referenda ReferendumInfoFor (r:1 w:1) + /// Proof: Referenda ReferendumInfoFor (max_values: None, max_size: Some(366), added: 2841, mode: MaxEncodedLen) + /// Storage: Referenda DecidingCount (r:1 w:0) + /// Proof: Referenda DecidingCount (max_values: None, max_size: Some(14), added: 2489, mode: MaxEncodedLen) + /// Storage: Referenda TrackQueue (r:1 w:1) + /// Proof: Referenda TrackQueue (max_values: None, max_size: Some(2012), added: 4487, mode: MaxEncodedLen) fn place_decision_deposit_queued() -> Weight { - // Minimum execution time: 40_000 nanoseconds. - Weight::from_ref_time(40_000_000) - .saturating_add(T::DbWeight::get().reads(3)) - .saturating_add(T::DbWeight::get().writes(2)) - } - // Storage: Referenda ReferendumInfoFor (r:1 w:1) - // Storage: Referenda DecidingCount (r:1 w:0) - // Storage: Referenda TrackQueue (r:1 w:1) + // Proof Size summary in bytes: + // Measured: `3203` + // Estimated: `9817` + // Minimum execution time: 41_561 nanoseconds. + Weight::from_parts(42_180_000, 9817) + .saturating_add(T::DbWeight::get().reads(3_u64)) + .saturating_add(T::DbWeight::get().writes(2_u64)) + } + /// Storage: Referenda ReferendumInfoFor (r:1 w:1) + /// Proof: Referenda ReferendumInfoFor (max_values: None, max_size: Some(366), added: 2841, mode: MaxEncodedLen) + /// Storage: Referenda DecidingCount (r:1 w:0) + /// Proof: Referenda DecidingCount (max_values: None, max_size: Some(14), added: 2489, mode: MaxEncodedLen) + /// Storage: Referenda TrackQueue (r:1 w:1) + /// Proof: Referenda TrackQueue (max_values: None, max_size: Some(2012), added: 4487, mode: MaxEncodedLen) fn place_decision_deposit_not_queued() -> Weight { - // Minimum execution time: 39_000 nanoseconds. - Weight::from_ref_time(39_000_000) - .saturating_add(T::DbWeight::get().reads(3)) - .saturating_add(T::DbWeight::get().writes(2)) - } - // Storage: Referenda ReferendumInfoFor (r:1 w:1) - // Storage: Referenda DecidingCount (r:1 w:1) - // Storage: Scheduler Agenda (r:2 w:2) + // Proof Size summary in bytes: + // Measured: `3223` + // Estimated: `9817` + // Minimum execution time: 41_039 nanoseconds. + Weight::from_parts(41_673_000, 9817) + .saturating_add(T::DbWeight::get().reads(3_u64)) + .saturating_add(T::DbWeight::get().writes(2_u64)) + } + /// Storage: Referenda ReferendumInfoFor (r:1 w:1) + /// Proof: Referenda ReferendumInfoFor (max_values: None, max_size: Some(366), added: 2841, mode: MaxEncodedLen) + /// Storage: Referenda DecidingCount (r:1 w:1) + /// Proof: Referenda DecidingCount (max_values: None, max_size: Some(14), added: 2489, mode: MaxEncodedLen) + /// Storage: Scheduler Agenda (r:2 w:2) + /// Proof: Scheduler Agenda (max_values: None, max_size: Some(107022), added: 109497, mode: MaxEncodedLen) fn place_decision_deposit_passing() -> Weight { - // Minimum execution time: 43_000 nanoseconds. - Weight::from_ref_time(43_000_000) - .saturating_add(T::DbWeight::get().reads(4)) - .saturating_add(T::DbWeight::get().writes(4)) - } - // Storage: Referenda ReferendumInfoFor (r:1 w:1) - // Storage: Referenda DecidingCount (r:1 w:1) - // Storage: Scheduler Agenda (r:2 w:2) + // Proof Size summary in bytes: + // Measured: `536` + // Estimated: `224324` + // Minimum execution time: 52_922 nanoseconds. + Weight::from_parts(53_395_000, 224324) + .saturating_add(T::DbWeight::get().reads(4_u64)) + .saturating_add(T::DbWeight::get().writes(4_u64)) + } + /// Storage: Referenda ReferendumInfoFor (r:1 w:1) + /// Proof: Referenda ReferendumInfoFor (max_values: None, max_size: Some(366), added: 2841, mode: MaxEncodedLen) + /// Storage: Referenda DecidingCount (r:1 w:1) + /// Proof: Referenda DecidingCount (max_values: None, max_size: Some(14), added: 2489, mode: MaxEncodedLen) + /// Storage: Scheduler Agenda (r:2 w:2) + /// Proof: Scheduler Agenda (max_values: None, max_size: Some(107022), added: 109497, mode: MaxEncodedLen) fn place_decision_deposit_failing() -> Weight { - // Minimum execution time: 84_000 nanoseconds. - Weight::from_ref_time(84_000_000) - .saturating_add(T::DbWeight::get().reads(4)) - .saturating_add(T::DbWeight::get().writes(4)) - } - // Storage: Referenda ReferendumInfoFor (r:1 w:1) + // Proof Size summary in bytes: + // Measured: `536` + // Estimated: `224324` + // Minimum execution time: 51_050 nanoseconds. + Weight::from_parts(51_736_000, 224324) + .saturating_add(T::DbWeight::get().reads(4_u64)) + .saturating_add(T::DbWeight::get().writes(4_u64)) + } + /// Storage: Referenda ReferendumInfoFor (r:1 w:1) + /// Proof: Referenda ReferendumInfoFor (max_values: None, max_size: Some(366), added: 2841, mode: MaxEncodedLen) fn refund_decision_deposit() -> Weight { - // Minimum execution time: 25_000 nanoseconds. - Weight::from_ref_time(25_000_000) - .saturating_add(T::DbWeight::get().reads(1)) - .saturating_add(T::DbWeight::get().writes(1)) - } - // Storage: Referenda ReferendumInfoFor (r:1 w:1) + // Proof Size summary in bytes: + // Measured: `415` + // Estimated: `2841` + // Minimum execution time: 24_102 nanoseconds. + Weight::from_parts(24_372_000, 2841) + .saturating_add(T::DbWeight::get().reads(1_u64)) + .saturating_add(T::DbWeight::get().writes(1_u64)) + } + /// Storage: Referenda ReferendumInfoFor (r:1 w:1) + /// Proof: Referenda ReferendumInfoFor (max_values: None, max_size: Some(366), added: 2841, mode: MaxEncodedLen) fn refund_submission_deposit() -> Weight { - // Minimum execution time: 25_000 nanoseconds. - Weight::from_ref_time(25_000_000) - .saturating_add(T::DbWeight::get().reads(1)) - .saturating_add(T::DbWeight::get().writes(1)) - } - // Storage: Referenda ReferendumInfoFor (r:1 w:1) - // Storage: Scheduler Agenda (r:2 w:2) + // Proof Size summary in bytes: + // Measured: `405` + // Estimated: `2841` + // Minimum execution time: 24_162 nanoseconds. + Weight::from_parts(24_547_000, 2841) + .saturating_add(T::DbWeight::get().reads(1_u64)) + .saturating_add(T::DbWeight::get().writes(1_u64)) + } + /// Storage: Referenda ReferendumInfoFor (r:1 w:1) + /// Proof: Referenda ReferendumInfoFor (max_values: None, max_size: Some(366), added: 2841, mode: MaxEncodedLen) + /// Storage: Scheduler Agenda (r:2 w:2) + /// Proof: Scheduler Agenda (max_values: None, max_size: Some(107022), added: 109497, mode: MaxEncodedLen) fn cancel() -> Weight { - // Minimum execution time: 26_000 nanoseconds. - Weight::from_ref_time(26_000_000) - .saturating_add(T::DbWeight::get().reads(3)) - .saturating_add(T::DbWeight::get().writes(3)) - } - // Storage: Referenda ReferendumInfoFor (r:1 w:1) - // Storage: Scheduler Agenda (r:2 w:2) + // Proof Size summary in bytes: + // Measured: `412` + // Estimated: `221835` + // Minimum execution time: 32_247 nanoseconds. + Weight::from_parts(32_731_000, 221835) + .saturating_add(T::DbWeight::get().reads(3_u64)) + .saturating_add(T::DbWeight::get().writes(3_u64)) + } + /// Storage: Referenda ReferendumInfoFor (r:1 w:1) + /// Proof: Referenda ReferendumInfoFor (max_values: None, max_size: Some(366), added: 2841, mode: MaxEncodedLen) + /// Storage: Scheduler Agenda (r:2 w:2) + /// Proof: Scheduler Agenda (max_values: None, max_size: Some(107022), added: 109497, mode: MaxEncodedLen) fn kill() -> Weight { - // Minimum execution time: 47_000 nanoseconds. - Weight::from_ref_time(47_000_000) - .saturating_add(T::DbWeight::get().reads(3)) - .saturating_add(T::DbWeight::get().writes(3)) - } - // Storage: Referenda TrackQueue (r:1 w:0) - // Storage: Referenda DecidingCount (r:1 w:1) + // Proof Size summary in bytes: + // Measured: `717` + // Estimated: `221835` + // Minimum execution time: 59_900 nanoseconds. + Weight::from_parts(60_659_000, 221835) + .saturating_add(T::DbWeight::get().reads(3_u64)) + .saturating_add(T::DbWeight::get().writes(3_u64)) + } + /// Storage: Referenda TrackQueue (r:1 w:0) + /// Proof: Referenda TrackQueue (max_values: None, max_size: Some(2012), added: 4487, mode: MaxEncodedLen) + /// Storage: Referenda DecidingCount (r:1 w:1) + /// Proof: Referenda DecidingCount (max_values: None, max_size: Some(14), added: 2489, mode: MaxEncodedLen) fn one_fewer_deciding_queue_empty() -> Weight { - // Minimum execution time: 8_000 nanoseconds. - Weight::from_ref_time(8_000_000) - .saturating_add(T::DbWeight::get().reads(2)) - .saturating_add(T::DbWeight::get().writes(1)) - } - // Storage: Referenda TrackQueue (r:1 w:1) - // Storage: Referenda ReferendumInfoFor (r:1 w:1) - // Storage: Scheduler Agenda (r:2 w:2) + // Proof Size summary in bytes: + // Measured: `174` + // Estimated: `6976` + // Minimum execution time: 9_322 nanoseconds. + Weight::from_parts(9_638_000, 6976) + .saturating_add(T::DbWeight::get().reads(2_u64)) + .saturating_add(T::DbWeight::get().writes(1_u64)) + } + /// Storage: Referenda TrackQueue (r:1 w:1) + /// Proof: Referenda TrackQueue (max_values: None, max_size: Some(2012), added: 4487, mode: MaxEncodedLen) + /// Storage: Referenda ReferendumInfoFor (r:1 w:1) + /// Proof: Referenda ReferendumInfoFor (max_values: None, max_size: Some(366), added: 2841, mode: MaxEncodedLen) + /// Storage: Scheduler Agenda (r:2 w:2) + /// Proof: Scheduler Agenda (max_values: None, max_size: Some(107022), added: 109497, mode: MaxEncodedLen) fn one_fewer_deciding_failing() -> Weight { - // Minimum execution time: 88_000 nanoseconds. - Weight::from_ref_time(88_000_000) - .saturating_add(T::DbWeight::get().reads(4)) - .saturating_add(T::DbWeight::get().writes(4)) - } - // Storage: Referenda TrackQueue (r:1 w:1) - // Storage: Referenda ReferendumInfoFor (r:1 w:1) - // Storage: Scheduler Agenda (r:2 w:2) + // Proof Size summary in bytes: + // Measured: `4661` + // Estimated: `226322` + // Minimum execution time: 76_976 nanoseconds. + Weight::from_parts(77_597_000, 226322) + .saturating_add(T::DbWeight::get().reads(4_u64)) + .saturating_add(T::DbWeight::get().writes(4_u64)) + } + /// Storage: Referenda TrackQueue (r:1 w:1) + /// Proof: Referenda TrackQueue (max_values: None, max_size: Some(2012), added: 4487, mode: MaxEncodedLen) + /// Storage: Referenda ReferendumInfoFor (r:1 w:1) + /// Proof: Referenda ReferendumInfoFor (max_values: None, max_size: Some(366), added: 2841, mode: MaxEncodedLen) + /// Storage: Scheduler Agenda (r:2 w:2) + /// Proof: Scheduler Agenda (max_values: None, max_size: Some(107022), added: 109497, mode: MaxEncodedLen) fn one_fewer_deciding_passing() -> Weight { - // Minimum execution time: 75_000 nanoseconds. - Weight::from_ref_time(75_000_000) - .saturating_add(T::DbWeight::get().reads(4)) - .saturating_add(T::DbWeight::get().writes(4)) - } - // Storage: Referenda ReferendumInfoFor (r:1 w:1) - // Storage: Referenda TrackQueue (r:1 w:1) - // Storage: Scheduler Agenda (r:1 w:1) + // Proof Size summary in bytes: + // Measured: `4661` + // Estimated: `226322` + // Minimum execution time: 78_405 nanoseconds. + Weight::from_parts(78_972_000, 226322) + .saturating_add(T::DbWeight::get().reads(4_u64)) + .saturating_add(T::DbWeight::get().writes(4_u64)) + } + /// Storage: Referenda ReferendumInfoFor (r:1 w:1) + /// Proof: Referenda ReferendumInfoFor (max_values: None, max_size: Some(366), added: 2841, mode: MaxEncodedLen) + /// Storage: Referenda TrackQueue (r:1 w:1) + /// Proof: Referenda TrackQueue (max_values: None, max_size: Some(2012), added: 4487, mode: MaxEncodedLen) + /// Storage: Scheduler Agenda (r:1 w:1) + /// Proof: Scheduler Agenda (max_values: None, max_size: Some(107022), added: 109497, mode: MaxEncodedLen) fn nudge_referendum_requeued_insertion() -> Weight { - // Minimum execution time: 72_000 nanoseconds. - Weight::from_ref_time(72_000_000) - .saturating_add(T::DbWeight::get().reads(3)) - .saturating_add(T::DbWeight::get().writes(3)) - } - // Storage: Referenda ReferendumInfoFor (r:1 w:1) - // Storage: Referenda TrackQueue (r:1 w:1) - // Storage: Scheduler Agenda (r:1 w:1) + // Proof Size summary in bytes: + // Measured: `4682` + // Estimated: `116825` + // Minimum execution time: 51_360 nanoseconds. + Weight::from_parts(51_737_000, 116825) + .saturating_add(T::DbWeight::get().reads(3_u64)) + .saturating_add(T::DbWeight::get().writes(3_u64)) + } + /// Storage: Referenda ReferendumInfoFor (r:1 w:1) + /// Proof: Referenda ReferendumInfoFor (max_values: None, max_size: Some(366), added: 2841, mode: MaxEncodedLen) + /// Storage: Referenda TrackQueue (r:1 w:1) + /// Proof: Referenda TrackQueue (max_values: None, max_size: Some(2012), added: 4487, mode: MaxEncodedLen) + /// Storage: Scheduler Agenda (r:1 w:1) + /// Proof: Scheduler Agenda (max_values: None, max_size: Some(107022), added: 109497, mode: MaxEncodedLen) fn nudge_referendum_requeued_slide() -> Weight { - // Minimum execution time: 56_000 nanoseconds. - Weight::from_ref_time(56_000_000) - .saturating_add(T::DbWeight::get().reads(3)) - .saturating_add(T::DbWeight::get().writes(3)) - } - // Storage: Referenda ReferendumInfoFor (r:1 w:1) - // Storage: Referenda DecidingCount (r:1 w:0) - // Storage: Referenda TrackQueue (r:1 w:1) - // Storage: Scheduler Agenda (r:1 w:1) + // Proof Size summary in bytes: + // Measured: `4668` + // Estimated: `116825` + // Minimum execution time: 50_485 nanoseconds. + Weight::from_parts(51_601_000, 116825) + .saturating_add(T::DbWeight::get().reads(3_u64)) + .saturating_add(T::DbWeight::get().writes(3_u64)) + } + /// Storage: Referenda ReferendumInfoFor (r:1 w:1) + /// Proof: Referenda ReferendumInfoFor (max_values: None, max_size: Some(366), added: 2841, mode: MaxEncodedLen) + /// Storage: Referenda DecidingCount (r:1 w:0) + /// Proof: Referenda DecidingCount (max_values: None, max_size: Some(14), added: 2489, mode: MaxEncodedLen) + /// Storage: Referenda TrackQueue (r:1 w:1) + /// Proof: Referenda TrackQueue (max_values: None, max_size: Some(2012), added: 4487, mode: MaxEncodedLen) + /// Storage: Scheduler Agenda (r:1 w:1) + /// Proof: Scheduler Agenda (max_values: None, max_size: Some(107022), added: 109497, mode: MaxEncodedLen) fn nudge_referendum_queued() -> Weight { - // Minimum execution time: 55_000 nanoseconds. - Weight::from_ref_time(55_000_000) - .saturating_add(T::DbWeight::get().reads(4)) - .saturating_add(T::DbWeight::get().writes(3)) - } - // Storage: Referenda ReferendumInfoFor (r:1 w:1) - // Storage: Referenda DecidingCount (r:1 w:0) - // Storage: Referenda TrackQueue (r:1 w:1) - // Storage: Scheduler Agenda (r:1 w:1) + // Proof Size summary in bytes: + // Measured: `4642` + // Estimated: `119314` + // Minimum execution time: 53_075 nanoseconds. + Weight::from_parts(54_014_000, 119314) + .saturating_add(T::DbWeight::get().reads(4_u64)) + .saturating_add(T::DbWeight::get().writes(3_u64)) + } + /// Storage: Referenda ReferendumInfoFor (r:1 w:1) + /// Proof: Referenda ReferendumInfoFor (max_values: None, max_size: Some(366), added: 2841, mode: MaxEncodedLen) + /// Storage: Referenda DecidingCount (r:1 w:0) + /// Proof: Referenda DecidingCount (max_values: None, max_size: Some(14), added: 2489, mode: MaxEncodedLen) + /// Storage: Referenda TrackQueue (r:1 w:1) + /// Proof: Referenda TrackQueue (max_values: None, max_size: Some(2012), added: 4487, mode: MaxEncodedLen) + /// Storage: Scheduler Agenda (r:1 w:1) + /// Proof: Scheduler Agenda (max_values: None, max_size: Some(107022), added: 109497, mode: MaxEncodedLen) fn nudge_referendum_not_queued() -> Weight { - // Minimum execution time: 60_000 nanoseconds. - Weight::from_ref_time(60_000_000) - .saturating_add(T::DbWeight::get().reads(4)) - .saturating_add(T::DbWeight::get().writes(3)) - } - // Storage: Referenda ReferendumInfoFor (r:1 w:1) - // Storage: Scheduler Agenda (r:1 w:1) + // Proof Size summary in bytes: + // Measured: `4676` + // Estimated: `119314` + // Minimum execution time: 52_916 nanoseconds. + Weight::from_parts(53_716_000, 119314) + .saturating_add(T::DbWeight::get().reads(4_u64)) + .saturating_add(T::DbWeight::get().writes(3_u64)) + } + /// Storage: Referenda ReferendumInfoFor (r:1 w:1) + /// Proof: Referenda ReferendumInfoFor (max_values: None, max_size: Some(366), added: 2841, mode: MaxEncodedLen) + /// Storage: Scheduler Agenda (r:1 w:1) + /// Proof: Scheduler Agenda (max_values: None, max_size: Some(107022), added: 109497, mode: MaxEncodedLen) fn nudge_referendum_no_deposit() -> Weight { - // Minimum execution time: 22_000 nanoseconds. - Weight::from_ref_time(22_000_000) - .saturating_add(T::DbWeight::get().reads(2)) - .saturating_add(T::DbWeight::get().writes(2)) - } - // Storage: Referenda ReferendumInfoFor (r:1 w:1) - // Storage: Scheduler Agenda (r:1 w:1) + // Proof Size summary in bytes: + // Measured: `364` + // Estimated: `112338` + // Minimum execution time: 21_920 nanoseconds. + Weight::from_parts(22_172_000, 112338) + .saturating_add(T::DbWeight::get().reads(2_u64)) + .saturating_add(T::DbWeight::get().writes(2_u64)) + } + /// Storage: Referenda ReferendumInfoFor (r:1 w:1) + /// Proof: Referenda ReferendumInfoFor (max_values: None, max_size: Some(366), added: 2841, mode: MaxEncodedLen) + /// Storage: Scheduler Agenda (r:1 w:1) + /// Proof: Scheduler Agenda (max_values: None, max_size: Some(107022), added: 109497, mode: MaxEncodedLen) fn nudge_referendum_preparing() -> Weight { - // Minimum execution time: 21_000 nanoseconds. - Weight::from_ref_time(21_000_000) - .saturating_add(T::DbWeight::get().reads(2)) - .saturating_add(T::DbWeight::get().writes(2)) - } - // Storage: Referenda ReferendumInfoFor (r:1 w:1) + // Proof Size summary in bytes: + // Measured: `412` + // Estimated: `112338` + // Minimum execution time: 22_094 nanoseconds. + Weight::from_parts(22_314_000, 112338) + .saturating_add(T::DbWeight::get().reads(2_u64)) + .saturating_add(T::DbWeight::get().writes(2_u64)) + } + /// Storage: Referenda ReferendumInfoFor (r:1 w:1) + /// Proof: Referenda ReferendumInfoFor (max_values: None, max_size: Some(366), added: 2841, mode: MaxEncodedLen) fn nudge_referendum_timed_out() -> Weight { - // Minimum execution time: 17_000 nanoseconds. - Weight::from_ref_time(17_000_000) - .saturating_add(T::DbWeight::get().reads(1)) - .saturating_add(T::DbWeight::get().writes(1)) - } - // Storage: Referenda ReferendumInfoFor (r:1 w:1) - // Storage: Referenda DecidingCount (r:1 w:1) - // Storage: Scheduler Agenda (r:1 w:1) + // Proof Size summary in bytes: + // Measured: `310` + // Estimated: `2841` + // Minimum execution time: 15_696 nanoseconds. + Weight::from_parts(15_964_000, 2841) + .saturating_add(T::DbWeight::get().reads(1_u64)) + .saturating_add(T::DbWeight::get().writes(1_u64)) + } + /// Storage: Referenda ReferendumInfoFor (r:1 w:1) + /// Proof: Referenda ReferendumInfoFor (max_values: None, max_size: Some(366), added: 2841, mode: MaxEncodedLen) + /// Storage: Referenda DecidingCount (r:1 w:1) + /// Proof: Referenda DecidingCount (max_values: None, max_size: Some(14), added: 2489, mode: MaxEncodedLen) + /// Storage: Scheduler Agenda (r:1 w:1) + /// Proof: Scheduler Agenda (max_values: None, max_size: Some(107022), added: 109497, mode: MaxEncodedLen) fn nudge_referendum_begin_deciding_failing() -> Weight { - // Minimum execution time: 29_000 nanoseconds. - Weight::from_ref_time(29_000_000) - .saturating_add(T::DbWeight::get().reads(3)) - .saturating_add(T::DbWeight::get().writes(3)) - } - // Storage: Referenda ReferendumInfoFor (r:1 w:1) - // Storage: Referenda DecidingCount (r:1 w:1) - // Storage: Scheduler Agenda (r:1 w:1) + // Proof Size summary in bytes: + // Measured: `412` + // Estimated: `114827` + // Minimum execution time: 30_604 nanoseconds. + Weight::from_parts(31_126_000, 114827) + .saturating_add(T::DbWeight::get().reads(3_u64)) + .saturating_add(T::DbWeight::get().writes(3_u64)) + } + /// Storage: Referenda ReferendumInfoFor (r:1 w:1) + /// Proof: Referenda ReferendumInfoFor (max_values: None, max_size: Some(366), added: 2841, mode: MaxEncodedLen) + /// Storage: Referenda DecidingCount (r:1 w:1) + /// Proof: Referenda DecidingCount (max_values: None, max_size: Some(14), added: 2489, mode: MaxEncodedLen) + /// Storage: Scheduler Agenda (r:1 w:1) + /// Proof: Scheduler Agenda (max_values: None, max_size: Some(107022), added: 109497, mode: MaxEncodedLen) fn nudge_referendum_begin_deciding_passing() -> Weight { - // Minimum execution time: 39_000 nanoseconds. - Weight::from_ref_time(39_000_000) - .saturating_add(T::DbWeight::get().reads(3)) - .saturating_add(T::DbWeight::get().writes(3)) - } - // Storage: Referenda ReferendumInfoFor (r:1 w:1) - // Storage: Scheduler Agenda (r:1 w:1) + // Proof Size summary in bytes: + // Measured: `412` + // Estimated: `114827` + // Minimum execution time: 32_961 nanoseconds. + Weight::from_parts(33_295_000, 114827) + .saturating_add(T::DbWeight::get().reads(3_u64)) + .saturating_add(T::DbWeight::get().writes(3_u64)) + } + /// Storage: Referenda ReferendumInfoFor (r:1 w:1) + /// Proof: Referenda ReferendumInfoFor (max_values: None, max_size: Some(366), added: 2841, mode: MaxEncodedLen) + /// Storage: Scheduler Agenda (r:1 w:1) + /// Proof: Scheduler Agenda (max_values: None, max_size: Some(107022), added: 109497, mode: MaxEncodedLen) fn nudge_referendum_begin_confirming() -> Weight { - // Minimum execution time: 31_000 nanoseconds. - Weight::from_ref_time(31_000_000) - .saturating_add(T::DbWeight::get().reads(2)) - .saturating_add(T::DbWeight::get().writes(2)) - } - // Storage: Referenda ReferendumInfoFor (r:1 w:1) - // Storage: Scheduler Agenda (r:1 w:1) + // Proof Size summary in bytes: + // Measured: `465` + // Estimated: `112338` + // Minimum execution time: 27_072 nanoseconds. + Weight::from_parts(27_405_000, 112338) + .saturating_add(T::DbWeight::get().reads(2_u64)) + .saturating_add(T::DbWeight::get().writes(2_u64)) + } + /// Storage: Referenda ReferendumInfoFor (r:1 w:1) + /// Proof: Referenda ReferendumInfoFor (max_values: None, max_size: Some(366), added: 2841, mode: MaxEncodedLen) + /// Storage: Scheduler Agenda (r:1 w:1) + /// Proof: Scheduler Agenda (max_values: None, max_size: Some(107022), added: 109497, mode: MaxEncodedLen) fn nudge_referendum_end_confirming() -> Weight { - // Minimum execution time: 30_000 nanoseconds. - Weight::from_ref_time(30_000_000) - .saturating_add(T::DbWeight::get().reads(2)) - .saturating_add(T::DbWeight::get().writes(2)) - } - // Storage: Referenda ReferendumInfoFor (r:1 w:1) - // Storage: Scheduler Agenda (r:1 w:1) + // Proof Size summary in bytes: + // Measured: `448` + // Estimated: `112338` + // Minimum execution time: 27_056 nanoseconds. + Weight::from_parts(27_768_000, 112338) + .saturating_add(T::DbWeight::get().reads(2_u64)) + .saturating_add(T::DbWeight::get().writes(2_u64)) + } + /// Storage: Referenda ReferendumInfoFor (r:1 w:1) + /// Proof: Referenda ReferendumInfoFor (max_values: None, max_size: Some(366), added: 2841, mode: MaxEncodedLen) + /// Storage: Scheduler Agenda (r:1 w:1) + /// Proof: Scheduler Agenda (max_values: None, max_size: Some(107022), added: 109497, mode: MaxEncodedLen) fn nudge_referendum_continue_not_confirming() -> Weight { - // Minimum execution time: 28_000 nanoseconds. - Weight::from_ref_time(28_000_000) - .saturating_add(T::DbWeight::get().reads(2)) - .saturating_add(T::DbWeight::get().writes(2)) - } - // Storage: Referenda ReferendumInfoFor (r:1 w:1) - // Storage: Scheduler Agenda (r:1 w:1) + // Proof Size summary in bytes: + // Measured: `465` + // Estimated: `112338` + // Minimum execution time: 24_599 nanoseconds. + Weight::from_parts(25_170_000, 112338) + .saturating_add(T::DbWeight::get().reads(2_u64)) + .saturating_add(T::DbWeight::get().writes(2_u64)) + } + /// Storage: Referenda ReferendumInfoFor (r:1 w:1) + /// Proof: Referenda ReferendumInfoFor (max_values: None, max_size: Some(366), added: 2841, mode: MaxEncodedLen) + /// Storage: Scheduler Agenda (r:1 w:1) + /// Proof: Scheduler Agenda (max_values: None, max_size: Some(107022), added: 109497, mode: MaxEncodedLen) fn nudge_referendum_continue_confirming() -> Weight { - // Minimum execution time: 30_000 nanoseconds. - Weight::from_ref_time(30_000_000) - .saturating_add(T::DbWeight::get().reads(2)) - .saturating_add(T::DbWeight::get().writes(2)) - } - // Storage: Referenda ReferendumInfoFor (r:1 w:1) - // Storage: Scheduler Agenda (r:2 w:2) - // Storage: Scheduler Lookup (r:1 w:1) + // Proof Size summary in bytes: + // Measured: `469` + // Estimated: `112338` + // Minimum execution time: 23_737 nanoseconds. + Weight::from_parts(24_184_000, 112338) + .saturating_add(T::DbWeight::get().reads(2_u64)) + .saturating_add(T::DbWeight::get().writes(2_u64)) + } + /// Storage: Referenda ReferendumInfoFor (r:1 w:1) + /// Proof: Referenda ReferendumInfoFor (max_values: None, max_size: Some(366), added: 2841, mode: MaxEncodedLen) + /// Storage: Scheduler Agenda (r:2 w:2) + /// Proof: Scheduler Agenda (max_values: None, max_size: Some(107022), added: 109497, mode: MaxEncodedLen) + /// Storage: Scheduler Lookup (r:1 w:1) + /// Proof: Scheduler Lookup (max_values: None, max_size: Some(48), added: 2523, mode: MaxEncodedLen) fn nudge_referendum_approved() -> Weight { - // Minimum execution time: 45_000 nanoseconds. - Weight::from_ref_time(45_000_000) - .saturating_add(T::DbWeight::get().reads(4)) - .saturating_add(T::DbWeight::get().writes(4)) - } - // Storage: Referenda ReferendumInfoFor (r:1 w:1) - // Storage: Scheduler Agenda (r:1 w:1) + // Proof Size summary in bytes: + // Measured: `469` + // Estimated: `224358` + // Minimum execution time: 37_880 nanoseconds. + Weight::from_parts(38_537_000, 224358) + .saturating_add(T::DbWeight::get().reads(4_u64)) + .saturating_add(T::DbWeight::get().writes(4_u64)) + } + /// Storage: Referenda ReferendumInfoFor (r:1 w:1) + /// Proof: Referenda ReferendumInfoFor (max_values: None, max_size: Some(366), added: 2841, mode: MaxEncodedLen) + /// Storage: Scheduler Agenda (r:1 w:1) + /// Proof: Scheduler Agenda (max_values: None, max_size: Some(107022), added: 109497, mode: MaxEncodedLen) fn nudge_referendum_rejected() -> Weight { - // Minimum execution time: 30_000 nanoseconds. - Weight::from_ref_time(30_000_000) - .saturating_add(T::DbWeight::get().reads(2)) - .saturating_add(T::DbWeight::get().writes(2)) + // Proof Size summary in bytes: + // Measured: `465` + // Estimated: `112338` + // Minimum execution time: 26_898 nanoseconds. + Weight::from_parts(27_496_000, 112338) + .saturating_add(T::DbWeight::get().reads(2_u64)) + .saturating_add(T::DbWeight::get().writes(2_u64)) } } // For backwards compatibility and tests impl WeightInfo for () { - // Storage: Referenda ReferendumCount (r:1 w:1) - // Storage: Scheduler Agenda (r:1 w:1) - // Storage: Referenda ReferendumInfoFor (r:0 w:1) + /// Storage: Referenda ReferendumCount (r:1 w:1) + /// Proof: Referenda ReferendumCount (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) + /// Storage: Scheduler Agenda (r:1 w:1) + /// Proof: Scheduler Agenda (max_values: None, max_size: Some(107022), added: 109497, mode: MaxEncodedLen) + /// Storage: Referenda ReferendumInfoFor (r:0 w:1) + /// Proof: Referenda ReferendumInfoFor (max_values: None, max_size: Some(366), added: 2841, mode: MaxEncodedLen) fn submit() -> Weight { - // Minimum execution time: 29_000 nanoseconds. - Weight::from_ref_time(29_000_000) - .saturating_add(RocksDbWeight::get().reads(2)) - .saturating_add(RocksDbWeight::get().writes(3)) - } - // Storage: Referenda ReferendumInfoFor (r:1 w:1) - // Storage: Scheduler Agenda (r:2 w:2) + // Proof Size summary in bytes: + // Measured: `251` + // Estimated: `109996` + // Minimum execution time: 32_207 nanoseconds. + Weight::from_parts(32_639_000, 109996) + .saturating_add(RocksDbWeight::get().reads(2_u64)) + .saturating_add(RocksDbWeight::get().writes(3_u64)) + } + /// Storage: Referenda ReferendumInfoFor (r:1 w:1) + /// Proof: Referenda ReferendumInfoFor (max_values: None, max_size: Some(366), added: 2841, mode: MaxEncodedLen) + /// Storage: Scheduler Agenda (r:2 w:2) + /// Proof: Scheduler Agenda (max_values: None, max_size: Some(107022), added: 109497, mode: MaxEncodedLen) fn place_decision_deposit_preparing() -> Weight { - // Minimum execution time: 35_000 nanoseconds. - Weight::from_ref_time(35_000_000) - .saturating_add(RocksDbWeight::get().reads(3)) - .saturating_add(RocksDbWeight::get().writes(3)) - } - // Storage: Referenda ReferendumInfoFor (r:1 w:1) - // Storage: Referenda DecidingCount (r:1 w:0) - // Storage: Referenda TrackQueue (r:1 w:1) + // Proof Size summary in bytes: + // Measured: `536` + // Estimated: `221835` + // Minimum execution time: 43_766 nanoseconds. + Weight::from_parts(44_494_000, 221835) + .saturating_add(RocksDbWeight::get().reads(3_u64)) + .saturating_add(RocksDbWeight::get().writes(3_u64)) + } + /// Storage: Referenda ReferendumInfoFor (r:1 w:1) + /// Proof: Referenda ReferendumInfoFor (max_values: None, max_size: Some(366), added: 2841, mode: MaxEncodedLen) + /// Storage: Referenda DecidingCount (r:1 w:0) + /// Proof: Referenda DecidingCount (max_values: None, max_size: Some(14), added: 2489, mode: MaxEncodedLen) + /// Storage: Referenda TrackQueue (r:1 w:1) + /// Proof: Referenda TrackQueue (max_values: None, max_size: Some(2012), added: 4487, mode: MaxEncodedLen) fn place_decision_deposit_queued() -> Weight { - // Minimum execution time: 40_000 nanoseconds. - Weight::from_ref_time(40_000_000) - .saturating_add(RocksDbWeight::get().reads(3)) - .saturating_add(RocksDbWeight::get().writes(2)) - } - // Storage: Referenda ReferendumInfoFor (r:1 w:1) - // Storage: Referenda DecidingCount (r:1 w:0) - // Storage: Referenda TrackQueue (r:1 w:1) + // Proof Size summary in bytes: + // Measured: `3203` + // Estimated: `9817` + // Minimum execution time: 41_561 nanoseconds. + Weight::from_parts(42_180_000, 9817) + .saturating_add(RocksDbWeight::get().reads(3_u64)) + .saturating_add(RocksDbWeight::get().writes(2_u64)) + } + /// Storage: Referenda ReferendumInfoFor (r:1 w:1) + /// Proof: Referenda ReferendumInfoFor (max_values: None, max_size: Some(366), added: 2841, mode: MaxEncodedLen) + /// Storage: Referenda DecidingCount (r:1 w:0) + /// Proof: Referenda DecidingCount (max_values: None, max_size: Some(14), added: 2489, mode: MaxEncodedLen) + /// Storage: Referenda TrackQueue (r:1 w:1) + /// Proof: Referenda TrackQueue (max_values: None, max_size: Some(2012), added: 4487, mode: MaxEncodedLen) fn place_decision_deposit_not_queued() -> Weight { - // Minimum execution time: 39_000 nanoseconds. - Weight::from_ref_time(39_000_000) - .saturating_add(RocksDbWeight::get().reads(3)) - .saturating_add(RocksDbWeight::get().writes(2)) - } - // Storage: Referenda ReferendumInfoFor (r:1 w:1) - // Storage: Referenda DecidingCount (r:1 w:1) - // Storage: Scheduler Agenda (r:2 w:2) + // Proof Size summary in bytes: + // Measured: `3223` + // Estimated: `9817` + // Minimum execution time: 41_039 nanoseconds. + Weight::from_parts(41_673_000, 9817) + .saturating_add(RocksDbWeight::get().reads(3_u64)) + .saturating_add(RocksDbWeight::get().writes(2_u64)) + } + /// Storage: Referenda ReferendumInfoFor (r:1 w:1) + /// Proof: Referenda ReferendumInfoFor (max_values: None, max_size: Some(366), added: 2841, mode: MaxEncodedLen) + /// Storage: Referenda DecidingCount (r:1 w:1) + /// Proof: Referenda DecidingCount (max_values: None, max_size: Some(14), added: 2489, mode: MaxEncodedLen) + /// Storage: Scheduler Agenda (r:2 w:2) + /// Proof: Scheduler Agenda (max_values: None, max_size: Some(107022), added: 109497, mode: MaxEncodedLen) fn place_decision_deposit_passing() -> Weight { - // Minimum execution time: 43_000 nanoseconds. - Weight::from_ref_time(43_000_000) - .saturating_add(RocksDbWeight::get().reads(4)) - .saturating_add(RocksDbWeight::get().writes(4)) - } - // Storage: Referenda ReferendumInfoFor (r:1 w:1) - // Storage: Referenda DecidingCount (r:1 w:1) - // Storage: Scheduler Agenda (r:2 w:2) + // Proof Size summary in bytes: + // Measured: `536` + // Estimated: `224324` + // Minimum execution time: 52_922 nanoseconds. + Weight::from_parts(53_395_000, 224324) + .saturating_add(RocksDbWeight::get().reads(4_u64)) + .saturating_add(RocksDbWeight::get().writes(4_u64)) + } + /// Storage: Referenda ReferendumInfoFor (r:1 w:1) + /// Proof: Referenda ReferendumInfoFor (max_values: None, max_size: Some(366), added: 2841, mode: MaxEncodedLen) + /// Storage: Referenda DecidingCount (r:1 w:1) + /// Proof: Referenda DecidingCount (max_values: None, max_size: Some(14), added: 2489, mode: MaxEncodedLen) + /// Storage: Scheduler Agenda (r:2 w:2) + /// Proof: Scheduler Agenda (max_values: None, max_size: Some(107022), added: 109497, mode: MaxEncodedLen) fn place_decision_deposit_failing() -> Weight { - // Minimum execution time: 84_000 nanoseconds. - Weight::from_ref_time(84_000_000) - .saturating_add(RocksDbWeight::get().reads(4)) - .saturating_add(RocksDbWeight::get().writes(4)) - } - // Storage: Referenda ReferendumInfoFor (r:1 w:1) + // Proof Size summary in bytes: + // Measured: `536` + // Estimated: `224324` + // Minimum execution time: 51_050 nanoseconds. + Weight::from_parts(51_736_000, 224324) + .saturating_add(RocksDbWeight::get().reads(4_u64)) + .saturating_add(RocksDbWeight::get().writes(4_u64)) + } + /// Storage: Referenda ReferendumInfoFor (r:1 w:1) + /// Proof: Referenda ReferendumInfoFor (max_values: None, max_size: Some(366), added: 2841, mode: MaxEncodedLen) fn refund_decision_deposit() -> Weight { - // Minimum execution time: 25_000 nanoseconds. - Weight::from_ref_time(25_000_000) - .saturating_add(RocksDbWeight::get().reads(1)) - .saturating_add(RocksDbWeight::get().writes(1)) - } - // Storage: Referenda ReferendumInfoFor (r:1 w:1) + // Proof Size summary in bytes: + // Measured: `415` + // Estimated: `2841` + // Minimum execution time: 24_102 nanoseconds. + Weight::from_parts(24_372_000, 2841) + .saturating_add(RocksDbWeight::get().reads(1_u64)) + .saturating_add(RocksDbWeight::get().writes(1_u64)) + } + /// Storage: Referenda ReferendumInfoFor (r:1 w:1) + /// Proof: Referenda ReferendumInfoFor (max_values: None, max_size: Some(366), added: 2841, mode: MaxEncodedLen) fn refund_submission_deposit() -> Weight { - // Minimum execution time: 25_000 nanoseconds. - Weight::from_ref_time(25_000_000) - .saturating_add(RocksDbWeight::get().reads(1)) - .saturating_add(RocksDbWeight::get().writes(1)) - } - // Storage: Referenda ReferendumInfoFor (r:1 w:1) - // Storage: Scheduler Agenda (r:2 w:2) + // Proof Size summary in bytes: + // Measured: `405` + // Estimated: `2841` + // Minimum execution time: 24_162 nanoseconds. + Weight::from_parts(24_547_000, 2841) + .saturating_add(RocksDbWeight::get().reads(1_u64)) + .saturating_add(RocksDbWeight::get().writes(1_u64)) + } + /// Storage: Referenda ReferendumInfoFor (r:1 w:1) + /// Proof: Referenda ReferendumInfoFor (max_values: None, max_size: Some(366), added: 2841, mode: MaxEncodedLen) + /// Storage: Scheduler Agenda (r:2 w:2) + /// Proof: Scheduler Agenda (max_values: None, max_size: Some(107022), added: 109497, mode: MaxEncodedLen) fn cancel() -> Weight { - // Minimum execution time: 26_000 nanoseconds. - Weight::from_ref_time(26_000_000) - .saturating_add(RocksDbWeight::get().reads(3)) - .saturating_add(RocksDbWeight::get().writes(3)) - } - // Storage: Referenda ReferendumInfoFor (r:1 w:1) - // Storage: Scheduler Agenda (r:2 w:2) + // Proof Size summary in bytes: + // Measured: `412` + // Estimated: `221835` + // Minimum execution time: 32_247 nanoseconds. + Weight::from_parts(32_731_000, 221835) + .saturating_add(RocksDbWeight::get().reads(3_u64)) + .saturating_add(RocksDbWeight::get().writes(3_u64)) + } + /// Storage: Referenda ReferendumInfoFor (r:1 w:1) + /// Proof: Referenda ReferendumInfoFor (max_values: None, max_size: Some(366), added: 2841, mode: MaxEncodedLen) + /// Storage: Scheduler Agenda (r:2 w:2) + /// Proof: Scheduler Agenda (max_values: None, max_size: Some(107022), added: 109497, mode: MaxEncodedLen) fn kill() -> Weight { - // Minimum execution time: 47_000 nanoseconds. - Weight::from_ref_time(47_000_000) - .saturating_add(RocksDbWeight::get().reads(3)) - .saturating_add(RocksDbWeight::get().writes(3)) - } - // Storage: Referenda TrackQueue (r:1 w:0) - // Storage: Referenda DecidingCount (r:1 w:1) + // Proof Size summary in bytes: + // Measured: `717` + // Estimated: `221835` + // Minimum execution time: 59_900 nanoseconds. + Weight::from_parts(60_659_000, 221835) + .saturating_add(RocksDbWeight::get().reads(3_u64)) + .saturating_add(RocksDbWeight::get().writes(3_u64)) + } + /// Storage: Referenda TrackQueue (r:1 w:0) + /// Proof: Referenda TrackQueue (max_values: None, max_size: Some(2012), added: 4487, mode: MaxEncodedLen) + /// Storage: Referenda DecidingCount (r:1 w:1) + /// Proof: Referenda DecidingCount (max_values: None, max_size: Some(14), added: 2489, mode: MaxEncodedLen) fn one_fewer_deciding_queue_empty() -> Weight { - // Minimum execution time: 8_000 nanoseconds. - Weight::from_ref_time(8_000_000) - .saturating_add(RocksDbWeight::get().reads(2)) - .saturating_add(RocksDbWeight::get().writes(1)) - } - // Storage: Referenda TrackQueue (r:1 w:1) - // Storage: Referenda ReferendumInfoFor (r:1 w:1) - // Storage: Scheduler Agenda (r:2 w:2) + // Proof Size summary in bytes: + // Measured: `174` + // Estimated: `6976` + // Minimum execution time: 9_322 nanoseconds. + Weight::from_parts(9_638_000, 6976) + .saturating_add(RocksDbWeight::get().reads(2_u64)) + .saturating_add(RocksDbWeight::get().writes(1_u64)) + } + /// Storage: Referenda TrackQueue (r:1 w:1) + /// Proof: Referenda TrackQueue (max_values: None, max_size: Some(2012), added: 4487, mode: MaxEncodedLen) + /// Storage: Referenda ReferendumInfoFor (r:1 w:1) + /// Proof: Referenda ReferendumInfoFor (max_values: None, max_size: Some(366), added: 2841, mode: MaxEncodedLen) + /// Storage: Scheduler Agenda (r:2 w:2) + /// Proof: Scheduler Agenda (max_values: None, max_size: Some(107022), added: 109497, mode: MaxEncodedLen) fn one_fewer_deciding_failing() -> Weight { - // Minimum execution time: 88_000 nanoseconds. - Weight::from_ref_time(88_000_000) - .saturating_add(RocksDbWeight::get().reads(4)) - .saturating_add(RocksDbWeight::get().writes(4)) - } - // Storage: Referenda TrackQueue (r:1 w:1) - // Storage: Referenda ReferendumInfoFor (r:1 w:1) - // Storage: Scheduler Agenda (r:2 w:2) + // Proof Size summary in bytes: + // Measured: `4661` + // Estimated: `226322` + // Minimum execution time: 76_976 nanoseconds. + Weight::from_parts(77_597_000, 226322) + .saturating_add(RocksDbWeight::get().reads(4_u64)) + .saturating_add(RocksDbWeight::get().writes(4_u64)) + } + /// Storage: Referenda TrackQueue (r:1 w:1) + /// Proof: Referenda TrackQueue (max_values: None, max_size: Some(2012), added: 4487, mode: MaxEncodedLen) + /// Storage: Referenda ReferendumInfoFor (r:1 w:1) + /// Proof: Referenda ReferendumInfoFor (max_values: None, max_size: Some(366), added: 2841, mode: MaxEncodedLen) + /// Storage: Scheduler Agenda (r:2 w:2) + /// Proof: Scheduler Agenda (max_values: None, max_size: Some(107022), added: 109497, mode: MaxEncodedLen) fn one_fewer_deciding_passing() -> Weight { - // Minimum execution time: 75_000 nanoseconds. - Weight::from_ref_time(75_000_000) - .saturating_add(RocksDbWeight::get().reads(4)) - .saturating_add(RocksDbWeight::get().writes(4)) - } - // Storage: Referenda ReferendumInfoFor (r:1 w:1) - // Storage: Referenda TrackQueue (r:1 w:1) - // Storage: Scheduler Agenda (r:1 w:1) + // Proof Size summary in bytes: + // Measured: `4661` + // Estimated: `226322` + // Minimum execution time: 78_405 nanoseconds. + Weight::from_parts(78_972_000, 226322) + .saturating_add(RocksDbWeight::get().reads(4_u64)) + .saturating_add(RocksDbWeight::get().writes(4_u64)) + } + /// Storage: Referenda ReferendumInfoFor (r:1 w:1) + /// Proof: Referenda ReferendumInfoFor (max_values: None, max_size: Some(366), added: 2841, mode: MaxEncodedLen) + /// Storage: Referenda TrackQueue (r:1 w:1) + /// Proof: Referenda TrackQueue (max_values: None, max_size: Some(2012), added: 4487, mode: MaxEncodedLen) + /// Storage: Scheduler Agenda (r:1 w:1) + /// Proof: Scheduler Agenda (max_values: None, max_size: Some(107022), added: 109497, mode: MaxEncodedLen) fn nudge_referendum_requeued_insertion() -> Weight { - // Minimum execution time: 72_000 nanoseconds. - Weight::from_ref_time(72_000_000) - .saturating_add(RocksDbWeight::get().reads(3)) - .saturating_add(RocksDbWeight::get().writes(3)) - } - // Storage: Referenda ReferendumInfoFor (r:1 w:1) - // Storage: Referenda TrackQueue (r:1 w:1) - // Storage: Scheduler Agenda (r:1 w:1) + // Proof Size summary in bytes: + // Measured: `4682` + // Estimated: `116825` + // Minimum execution time: 51_360 nanoseconds. + Weight::from_parts(51_737_000, 116825) + .saturating_add(RocksDbWeight::get().reads(3_u64)) + .saturating_add(RocksDbWeight::get().writes(3_u64)) + } + /// Storage: Referenda ReferendumInfoFor (r:1 w:1) + /// Proof: Referenda ReferendumInfoFor (max_values: None, max_size: Some(366), added: 2841, mode: MaxEncodedLen) + /// Storage: Referenda TrackQueue (r:1 w:1) + /// Proof: Referenda TrackQueue (max_values: None, max_size: Some(2012), added: 4487, mode: MaxEncodedLen) + /// Storage: Scheduler Agenda (r:1 w:1) + /// Proof: Scheduler Agenda (max_values: None, max_size: Some(107022), added: 109497, mode: MaxEncodedLen) fn nudge_referendum_requeued_slide() -> Weight { - // Minimum execution time: 56_000 nanoseconds. - Weight::from_ref_time(56_000_000) - .saturating_add(RocksDbWeight::get().reads(3)) - .saturating_add(RocksDbWeight::get().writes(3)) - } - // Storage: Referenda ReferendumInfoFor (r:1 w:1) - // Storage: Referenda DecidingCount (r:1 w:0) - // Storage: Referenda TrackQueue (r:1 w:1) - // Storage: Scheduler Agenda (r:1 w:1) + // Proof Size summary in bytes: + // Measured: `4668` + // Estimated: `116825` + // Minimum execution time: 50_485 nanoseconds. + Weight::from_parts(51_601_000, 116825) + .saturating_add(RocksDbWeight::get().reads(3_u64)) + .saturating_add(RocksDbWeight::get().writes(3_u64)) + } + /// Storage: Referenda ReferendumInfoFor (r:1 w:1) + /// Proof: Referenda ReferendumInfoFor (max_values: None, max_size: Some(366), added: 2841, mode: MaxEncodedLen) + /// Storage: Referenda DecidingCount (r:1 w:0) + /// Proof: Referenda DecidingCount (max_values: None, max_size: Some(14), added: 2489, mode: MaxEncodedLen) + /// Storage: Referenda TrackQueue (r:1 w:1) + /// Proof: Referenda TrackQueue (max_values: None, max_size: Some(2012), added: 4487, mode: MaxEncodedLen) + /// Storage: Scheduler Agenda (r:1 w:1) + /// Proof: Scheduler Agenda (max_values: None, max_size: Some(107022), added: 109497, mode: MaxEncodedLen) fn nudge_referendum_queued() -> Weight { - // Minimum execution time: 55_000 nanoseconds. - Weight::from_ref_time(55_000_000) - .saturating_add(RocksDbWeight::get().reads(4)) - .saturating_add(RocksDbWeight::get().writes(3)) - } - // Storage: Referenda ReferendumInfoFor (r:1 w:1) - // Storage: Referenda DecidingCount (r:1 w:0) - // Storage: Referenda TrackQueue (r:1 w:1) - // Storage: Scheduler Agenda (r:1 w:1) + // Proof Size summary in bytes: + // Measured: `4642` + // Estimated: `119314` + // Minimum execution time: 53_075 nanoseconds. + Weight::from_parts(54_014_000, 119314) + .saturating_add(RocksDbWeight::get().reads(4_u64)) + .saturating_add(RocksDbWeight::get().writes(3_u64)) + } + /// Storage: Referenda ReferendumInfoFor (r:1 w:1) + /// Proof: Referenda ReferendumInfoFor (max_values: None, max_size: Some(366), added: 2841, mode: MaxEncodedLen) + /// Storage: Referenda DecidingCount (r:1 w:0) + /// Proof: Referenda DecidingCount (max_values: None, max_size: Some(14), added: 2489, mode: MaxEncodedLen) + /// Storage: Referenda TrackQueue (r:1 w:1) + /// Proof: Referenda TrackQueue (max_values: None, max_size: Some(2012), added: 4487, mode: MaxEncodedLen) + /// Storage: Scheduler Agenda (r:1 w:1) + /// Proof: Scheduler Agenda (max_values: None, max_size: Some(107022), added: 109497, mode: MaxEncodedLen) fn nudge_referendum_not_queued() -> Weight { - // Minimum execution time: 60_000 nanoseconds. - Weight::from_ref_time(60_000_000) - .saturating_add(RocksDbWeight::get().reads(4)) - .saturating_add(RocksDbWeight::get().writes(3)) - } - // Storage: Referenda ReferendumInfoFor (r:1 w:1) - // Storage: Scheduler Agenda (r:1 w:1) + // Proof Size summary in bytes: + // Measured: `4676` + // Estimated: `119314` + // Minimum execution time: 52_916 nanoseconds. + Weight::from_parts(53_716_000, 119314) + .saturating_add(RocksDbWeight::get().reads(4_u64)) + .saturating_add(RocksDbWeight::get().writes(3_u64)) + } + /// Storage: Referenda ReferendumInfoFor (r:1 w:1) + /// Proof: Referenda ReferendumInfoFor (max_values: None, max_size: Some(366), added: 2841, mode: MaxEncodedLen) + /// Storage: Scheduler Agenda (r:1 w:1) + /// Proof: Scheduler Agenda (max_values: None, max_size: Some(107022), added: 109497, mode: MaxEncodedLen) fn nudge_referendum_no_deposit() -> Weight { - // Minimum execution time: 22_000 nanoseconds. - Weight::from_ref_time(22_000_000) - .saturating_add(RocksDbWeight::get().reads(2)) - .saturating_add(RocksDbWeight::get().writes(2)) - } - // Storage: Referenda ReferendumInfoFor (r:1 w:1) - // Storage: Scheduler Agenda (r:1 w:1) + // Proof Size summary in bytes: + // Measured: `364` + // Estimated: `112338` + // Minimum execution time: 21_920 nanoseconds. + Weight::from_parts(22_172_000, 112338) + .saturating_add(RocksDbWeight::get().reads(2_u64)) + .saturating_add(RocksDbWeight::get().writes(2_u64)) + } + /// Storage: Referenda ReferendumInfoFor (r:1 w:1) + /// Proof: Referenda ReferendumInfoFor (max_values: None, max_size: Some(366), added: 2841, mode: MaxEncodedLen) + /// Storage: Scheduler Agenda (r:1 w:1) + /// Proof: Scheduler Agenda (max_values: None, max_size: Some(107022), added: 109497, mode: MaxEncodedLen) fn nudge_referendum_preparing() -> Weight { - // Minimum execution time: 21_000 nanoseconds. - Weight::from_ref_time(21_000_000) - .saturating_add(RocksDbWeight::get().reads(2)) - .saturating_add(RocksDbWeight::get().writes(2)) - } - // Storage: Referenda ReferendumInfoFor (r:1 w:1) + // Proof Size summary in bytes: + // Measured: `412` + // Estimated: `112338` + // Minimum execution time: 22_094 nanoseconds. + Weight::from_parts(22_314_000, 112338) + .saturating_add(RocksDbWeight::get().reads(2_u64)) + .saturating_add(RocksDbWeight::get().writes(2_u64)) + } + /// Storage: Referenda ReferendumInfoFor (r:1 w:1) + /// Proof: Referenda ReferendumInfoFor (max_values: None, max_size: Some(366), added: 2841, mode: MaxEncodedLen) fn nudge_referendum_timed_out() -> Weight { - // Minimum execution time: 17_000 nanoseconds. - Weight::from_ref_time(17_000_000) - .saturating_add(RocksDbWeight::get().reads(1)) - .saturating_add(RocksDbWeight::get().writes(1)) - } - // Storage: Referenda ReferendumInfoFor (r:1 w:1) - // Storage: Referenda DecidingCount (r:1 w:1) - // Storage: Scheduler Agenda (r:1 w:1) + // Proof Size summary in bytes: + // Measured: `310` + // Estimated: `2841` + // Minimum execution time: 15_696 nanoseconds. + Weight::from_parts(15_964_000, 2841) + .saturating_add(RocksDbWeight::get().reads(1_u64)) + .saturating_add(RocksDbWeight::get().writes(1_u64)) + } + /// Storage: Referenda ReferendumInfoFor (r:1 w:1) + /// Proof: Referenda ReferendumInfoFor (max_values: None, max_size: Some(366), added: 2841, mode: MaxEncodedLen) + /// Storage: Referenda DecidingCount (r:1 w:1) + /// Proof: Referenda DecidingCount (max_values: None, max_size: Some(14), added: 2489, mode: MaxEncodedLen) + /// Storage: Scheduler Agenda (r:1 w:1) + /// Proof: Scheduler Agenda (max_values: None, max_size: Some(107022), added: 109497, mode: MaxEncodedLen) fn nudge_referendum_begin_deciding_failing() -> Weight { - // Minimum execution time: 29_000 nanoseconds. - Weight::from_ref_time(29_000_000) - .saturating_add(RocksDbWeight::get().reads(3)) - .saturating_add(RocksDbWeight::get().writes(3)) - } - // Storage: Referenda ReferendumInfoFor (r:1 w:1) - // Storage: Referenda DecidingCount (r:1 w:1) - // Storage: Scheduler Agenda (r:1 w:1) + // Proof Size summary in bytes: + // Measured: `412` + // Estimated: `114827` + // Minimum execution time: 30_604 nanoseconds. + Weight::from_parts(31_126_000, 114827) + .saturating_add(RocksDbWeight::get().reads(3_u64)) + .saturating_add(RocksDbWeight::get().writes(3_u64)) + } + /// Storage: Referenda ReferendumInfoFor (r:1 w:1) + /// Proof: Referenda ReferendumInfoFor (max_values: None, max_size: Some(366), added: 2841, mode: MaxEncodedLen) + /// Storage: Referenda DecidingCount (r:1 w:1) + /// Proof: Referenda DecidingCount (max_values: None, max_size: Some(14), added: 2489, mode: MaxEncodedLen) + /// Storage: Scheduler Agenda (r:1 w:1) + /// Proof: Scheduler Agenda (max_values: None, max_size: Some(107022), added: 109497, mode: MaxEncodedLen) fn nudge_referendum_begin_deciding_passing() -> Weight { - // Minimum execution time: 39_000 nanoseconds. - Weight::from_ref_time(39_000_000) - .saturating_add(RocksDbWeight::get().reads(3)) - .saturating_add(RocksDbWeight::get().writes(3)) - } - // Storage: Referenda ReferendumInfoFor (r:1 w:1) - // Storage: Scheduler Agenda (r:1 w:1) + // Proof Size summary in bytes: + // Measured: `412` + // Estimated: `114827` + // Minimum execution time: 32_961 nanoseconds. + Weight::from_parts(33_295_000, 114827) + .saturating_add(RocksDbWeight::get().reads(3_u64)) + .saturating_add(RocksDbWeight::get().writes(3_u64)) + } + /// Storage: Referenda ReferendumInfoFor (r:1 w:1) + /// Proof: Referenda ReferendumInfoFor (max_values: None, max_size: Some(366), added: 2841, mode: MaxEncodedLen) + /// Storage: Scheduler Agenda (r:1 w:1) + /// Proof: Scheduler Agenda (max_values: None, max_size: Some(107022), added: 109497, mode: MaxEncodedLen) fn nudge_referendum_begin_confirming() -> Weight { - // Minimum execution time: 31_000 nanoseconds. - Weight::from_ref_time(31_000_000) - .saturating_add(RocksDbWeight::get().reads(2)) - .saturating_add(RocksDbWeight::get().writes(2)) - } - // Storage: Referenda ReferendumInfoFor (r:1 w:1) - // Storage: Scheduler Agenda (r:1 w:1) + // Proof Size summary in bytes: + // Measured: `465` + // Estimated: `112338` + // Minimum execution time: 27_072 nanoseconds. + Weight::from_parts(27_405_000, 112338) + .saturating_add(RocksDbWeight::get().reads(2_u64)) + .saturating_add(RocksDbWeight::get().writes(2_u64)) + } + /// Storage: Referenda ReferendumInfoFor (r:1 w:1) + /// Proof: Referenda ReferendumInfoFor (max_values: None, max_size: Some(366), added: 2841, mode: MaxEncodedLen) + /// Storage: Scheduler Agenda (r:1 w:1) + /// Proof: Scheduler Agenda (max_values: None, max_size: Some(107022), added: 109497, mode: MaxEncodedLen) fn nudge_referendum_end_confirming() -> Weight { - // Minimum execution time: 30_000 nanoseconds. - Weight::from_ref_time(30_000_000) - .saturating_add(RocksDbWeight::get().reads(2)) - .saturating_add(RocksDbWeight::get().writes(2)) - } - // Storage: Referenda ReferendumInfoFor (r:1 w:1) - // Storage: Scheduler Agenda (r:1 w:1) + // Proof Size summary in bytes: + // Measured: `448` + // Estimated: `112338` + // Minimum execution time: 27_056 nanoseconds. + Weight::from_parts(27_768_000, 112338) + .saturating_add(RocksDbWeight::get().reads(2_u64)) + .saturating_add(RocksDbWeight::get().writes(2_u64)) + } + /// Storage: Referenda ReferendumInfoFor (r:1 w:1) + /// Proof: Referenda ReferendumInfoFor (max_values: None, max_size: Some(366), added: 2841, mode: MaxEncodedLen) + /// Storage: Scheduler Agenda (r:1 w:1) + /// Proof: Scheduler Agenda (max_values: None, max_size: Some(107022), added: 109497, mode: MaxEncodedLen) fn nudge_referendum_continue_not_confirming() -> Weight { - // Minimum execution time: 28_000 nanoseconds. - Weight::from_ref_time(28_000_000) - .saturating_add(RocksDbWeight::get().reads(2)) - .saturating_add(RocksDbWeight::get().writes(2)) - } - // Storage: Referenda ReferendumInfoFor (r:1 w:1) - // Storage: Scheduler Agenda (r:1 w:1) + // Proof Size summary in bytes: + // Measured: `465` + // Estimated: `112338` + // Minimum execution time: 24_599 nanoseconds. + Weight::from_parts(25_170_000, 112338) + .saturating_add(RocksDbWeight::get().reads(2_u64)) + .saturating_add(RocksDbWeight::get().writes(2_u64)) + } + /// Storage: Referenda ReferendumInfoFor (r:1 w:1) + /// Proof: Referenda ReferendumInfoFor (max_values: None, max_size: Some(366), added: 2841, mode: MaxEncodedLen) + /// Storage: Scheduler Agenda (r:1 w:1) + /// Proof: Scheduler Agenda (max_values: None, max_size: Some(107022), added: 109497, mode: MaxEncodedLen) fn nudge_referendum_continue_confirming() -> Weight { - // Minimum execution time: 30_000 nanoseconds. - Weight::from_ref_time(30_000_000) - .saturating_add(RocksDbWeight::get().reads(2)) - .saturating_add(RocksDbWeight::get().writes(2)) - } - // Storage: Referenda ReferendumInfoFor (r:1 w:1) - // Storage: Scheduler Agenda (r:2 w:2) - // Storage: Scheduler Lookup (r:1 w:1) + // Proof Size summary in bytes: + // Measured: `469` + // Estimated: `112338` + // Minimum execution time: 23_737 nanoseconds. + Weight::from_parts(24_184_000, 112338) + .saturating_add(RocksDbWeight::get().reads(2_u64)) + .saturating_add(RocksDbWeight::get().writes(2_u64)) + } + /// Storage: Referenda ReferendumInfoFor (r:1 w:1) + /// Proof: Referenda ReferendumInfoFor (max_values: None, max_size: Some(366), added: 2841, mode: MaxEncodedLen) + /// Storage: Scheduler Agenda (r:2 w:2) + /// Proof: Scheduler Agenda (max_values: None, max_size: Some(107022), added: 109497, mode: MaxEncodedLen) + /// Storage: Scheduler Lookup (r:1 w:1) + /// Proof: Scheduler Lookup (max_values: None, max_size: Some(48), added: 2523, mode: MaxEncodedLen) fn nudge_referendum_approved() -> Weight { - // Minimum execution time: 45_000 nanoseconds. - Weight::from_ref_time(45_000_000) - .saturating_add(RocksDbWeight::get().reads(4)) - .saturating_add(RocksDbWeight::get().writes(4)) - } - // Storage: Referenda ReferendumInfoFor (r:1 w:1) - // Storage: Scheduler Agenda (r:1 w:1) + // Proof Size summary in bytes: + // Measured: `469` + // Estimated: `224358` + // Minimum execution time: 37_880 nanoseconds. + Weight::from_parts(38_537_000, 224358) + .saturating_add(RocksDbWeight::get().reads(4_u64)) + .saturating_add(RocksDbWeight::get().writes(4_u64)) + } + /// Storage: Referenda ReferendumInfoFor (r:1 w:1) + /// Proof: Referenda ReferendumInfoFor (max_values: None, max_size: Some(366), added: 2841, mode: MaxEncodedLen) + /// Storage: Scheduler Agenda (r:1 w:1) + /// Proof: Scheduler Agenda (max_values: None, max_size: Some(107022), added: 109497, mode: MaxEncodedLen) fn nudge_referendum_rejected() -> Weight { - // Minimum execution time: 30_000 nanoseconds. - Weight::from_ref_time(30_000_000) - .saturating_add(RocksDbWeight::get().reads(2)) - .saturating_add(RocksDbWeight::get().writes(2)) + // Proof Size summary in bytes: + // Measured: `465` + // Estimated: `112338` + // Minimum execution time: 26_898 nanoseconds. + Weight::from_parts(27_496_000, 112338) + .saturating_add(RocksDbWeight::get().reads(2_u64)) + .saturating_add(RocksDbWeight::get().writes(2_u64)) } } diff --git a/frame/remark/src/weights.rs b/frame/remark/src/weights.rs index 0d739657c..9142ed04c 100644 --- a/frame/remark/src/weights.rs +++ b/frame/remark/src/weights.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2022 Parity Technologies (UK) Ltd. +// Copyright (C) 2023 Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); @@ -18,7 +18,8 @@ //! Autogenerated weights for pallet_remark //! //! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev -//! DATE: 2022-11-07, STEPS: `50`, REPEAT: 20, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! DATE: 2023-01-24, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! WORST CASE MAP SIZE: `1000000` //! HOSTNAME: `bm2`, CPU: `Intel(R) Core(TM) i7-7700K CPU @ 4.20GHz` //! EXECUTION: Some(Wasm), WASM-EXECUTION: Compiled, CHAIN: Some("dev"), DB CACHE: 1024 @@ -53,26 +54,28 @@ pub trait WeightInfo { /// Weights for pallet_remark using the Substrate node and recommended hardware. pub struct SubstrateWeight(PhantomData); impl WeightInfo for SubstrateWeight { - // Storage: unknown [0x3a65787472696e7369635f696e646578] (r:1 w:0) /// The range of component `l` is `[1, 1048576]`. fn store(l: u32, ) -> Weight { - // Minimum execution time: 17_017 nanoseconds. - Weight::from_ref_time(8_269_935 as u64) + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 8_404 nanoseconds. + Weight::from_ref_time(343_031) // Standard Error: 1 - .saturating_add(Weight::from_ref_time(1_407 as u64).saturating_mul(l as u64)) - .saturating_add(T::DbWeight::get().reads(1 as u64)) + .saturating_add(Weight::from_ref_time(1_404).saturating_mul(l.into())) } } // For backwards compatibility and tests impl WeightInfo for () { - // Storage: unknown [0x3a65787472696e7369635f696e646578] (r:1 w:0) /// The range of component `l` is `[1, 1048576]`. fn store(l: u32, ) -> Weight { - // Minimum execution time: 17_017 nanoseconds. - Weight::from_ref_time(8_269_935 as u64) + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 8_404 nanoseconds. + Weight::from_ref_time(343_031) // Standard Error: 1 - .saturating_add(Weight::from_ref_time(1_407 as u64).saturating_mul(l as u64)) - .saturating_add(RocksDbWeight::get().reads(1 as u64)) + .saturating_add(Weight::from_ref_time(1_404).saturating_mul(l.into())) } } diff --git a/frame/scheduler/src/benchmarking.rs b/frame/scheduler/src/benchmarking.rs index ca98cabc3..8e2876290 100644 --- a/frame/scheduler/src/benchmarking.rs +++ b/frame/scheduler/src/benchmarking.rs @@ -160,6 +160,10 @@ benchmarks! { // `service_task` when the task is a non-periodic, non-named, fetched call (with a known // preimage length) and which is not dispatched (e.g. due to being overweight). + #[pov_mode = MaxEncodedLen { + // Use measured PoV size for the Preimages since we pass in a length witness. + Preimage::PreimageFor: Measured + }] service_task_fetched { let s in (BoundedInline::bound() as u32) .. (T::Preimages::MAX_LENGTH as u32); let now = BLOCK_NUMBER.into(); diff --git a/frame/scheduler/src/weights.rs b/frame/scheduler/src/weights.rs index 5b86e7a14..9be3bfaef 100644 --- a/frame/scheduler/src/weights.rs +++ b/frame/scheduler/src/weights.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2022 Parity Technologies (UK) Ltd. +// Copyright (C) 2023 Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); @@ -18,7 +18,8 @@ //! Autogenerated weights for pallet_scheduler //! //! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev -//! DATE: 2022-11-07, STEPS: `50`, REPEAT: 20, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! DATE: 2023-01-24, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! WORST CASE MAP SIZE: `1000000` //! HOSTNAME: `bm2`, CPU: `Intel(R) Core(TM) i7-7700K CPU @ 4.20GHz` //! EXECUTION: Some(Wasm), WASM-EXECUTION: Compiled, CHAIN: Some("dev"), DB CACHE: 1024 @@ -64,194 +65,292 @@ pub trait WeightInfo { /// Weights for pallet_scheduler using the Substrate node and recommended hardware. pub struct SubstrateWeight(PhantomData); impl WeightInfo for SubstrateWeight { - // Storage: Scheduler IncompleteSince (r:1 w:1) + /// Storage: Scheduler IncompleteSince (r:1 w:1) + /// Proof: Scheduler IncompleteSince (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) fn service_agendas_base() -> Weight { - // Minimum execution time: 5_131 nanoseconds. - Weight::from_ref_time(5_286_000 as u64) - .saturating_add(T::DbWeight::get().reads(1 as u64)) - .saturating_add(T::DbWeight::get().writes(1 as u64)) + // Proof Size summary in bytes: + // Measured: `30` + // Estimated: `499` + // Minimum execution time: 3_670 nanoseconds. + Weight::from_parts(3_838_000, 499) + .saturating_add(T::DbWeight::get().reads(1_u64)) + .saturating_add(T::DbWeight::get().writes(1_u64)) } - // Storage: Scheduler Agenda (r:1 w:1) + /// Storage: Scheduler Agenda (r:1 w:1) + /// Proof: Scheduler Agenda (max_values: None, max_size: Some(107022), added: 109497, mode: MaxEncodedLen) /// The range of component `s` is `[0, 512]`. fn service_agenda_base(s: u32, ) -> Weight { - // Minimum execution time: 4_111 nanoseconds. - Weight::from_ref_time(8_763_440 as u64) - // Standard Error: 783 - .saturating_add(Weight::from_ref_time(372_339 as u64).saturating_mul(s as u64)) - .saturating_add(T::DbWeight::get().reads(1 as u64)) - .saturating_add(T::DbWeight::get().writes(1 as u64)) + // Proof Size summary in bytes: + // Measured: `112 + s * (177 ±0)` + // Estimated: `109497` + // Minimum execution time: 3_079 nanoseconds. + Weight::from_parts(7_087_647, 109497) + // Standard Error: 658 + .saturating_add(Weight::from_ref_time(279_320).saturating_mul(s.into())) + .saturating_add(T::DbWeight::get().reads(1_u64)) + .saturating_add(T::DbWeight::get().writes(1_u64)) } fn service_task_base() -> Weight { - // Minimum execution time: 10_880 nanoseconds. - Weight::from_ref_time(11_194_000 as u64) + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 5_192 nanoseconds. + Weight::from_ref_time(5_528_000) } - // Storage: Preimage PreimageFor (r:1 w:1) - // Storage: Preimage StatusFor (r:1 w:1) + /// Storage: Preimage PreimageFor (r:1 w:1) + /// Proof: Preimage PreimageFor (max_values: None, max_size: Some(4194344), added: 4196819, mode: Measured) + /// Storage: Preimage StatusFor (r:1 w:1) + /// Proof: Preimage StatusFor (max_values: None, max_size: Some(91), added: 2566, mode: MaxEncodedLen) /// The range of component `s` is `[128, 4194304]`. fn service_task_fetched(s: u32, ) -> Weight { - // Minimum execution time: 25_347 nanoseconds. - Weight::from_ref_time(25_717_000 as u64) + // Proof Size summary in bytes: + // Measured: `211 + s * (1 ±0)` + // Estimated: `5252 + s * (1 ±0)` + // Minimum execution time: 17_284 nanoseconds. + Weight::from_parts(17_574_000, 5252) // Standard Error: 0 - .saturating_add(Weight::from_ref_time(1_128 as u64).saturating_mul(s as u64)) - .saturating_add(T::DbWeight::get().reads(2 as u64)) - .saturating_add(T::DbWeight::get().writes(2 as u64)) + .saturating_add(Weight::from_ref_time(1_126).saturating_mul(s.into())) + .saturating_add(T::DbWeight::get().reads(2_u64)) + .saturating_add(T::DbWeight::get().writes(2_u64)) + .saturating_add(Weight::from_proof_size(1).saturating_mul(s.into())) } - // Storage: Scheduler Lookup (r:0 w:1) + /// Storage: Scheduler Lookup (r:0 w:1) + /// Proof: Scheduler Lookup (max_values: None, max_size: Some(48), added: 2523, mode: MaxEncodedLen) fn service_task_named() -> Weight { - // Minimum execution time: 12_894 nanoseconds. - Weight::from_ref_time(13_108_000 as u64) - .saturating_add(T::DbWeight::get().writes(1 as u64)) + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 7_020 nanoseconds. + Weight::from_ref_time(7_262_000) + .saturating_add(T::DbWeight::get().writes(1_u64)) } fn service_task_periodic() -> Weight { - // Minimum execution time: 10_667 nanoseconds. - Weight::from_ref_time(10_908_000 as u64) + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 5_187 nanoseconds. + Weight::from_ref_time(5_368_000) } fn execute_dispatch_signed() -> Weight { - // Minimum execution time: 4_124 nanoseconds. - Weight::from_ref_time(4_680_000 as u64) + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 2_313 nanoseconds. + Weight::from_ref_time(2_404_000) } fn execute_dispatch_unsigned() -> Weight { - // Minimum execution time: 4_156 nanoseconds. - Weight::from_ref_time(4_361_000 as u64) + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 2_187 nanoseconds. + Weight::from_ref_time(2_362_000) } - // Storage: Scheduler Agenda (r:1 w:1) + /// Storage: Scheduler Agenda (r:1 w:1) + /// Proof: Scheduler Agenda (max_values: None, max_size: Some(107022), added: 109497, mode: MaxEncodedLen) /// The range of component `s` is `[0, 511]`. fn schedule(s: u32, ) -> Weight { - // Minimum execution time: 20_504 nanoseconds. - Weight::from_ref_time(27_066_818 as u64) - // Standard Error: 1_114 - .saturating_add(Weight::from_ref_time(372_897 as u64).saturating_mul(s as u64)) - .saturating_add(T::DbWeight::get().reads(1 as u64)) - .saturating_add(T::DbWeight::get().writes(1 as u64)) + // Proof Size summary in bytes: + // Measured: `112 + s * (177 ±0)` + // Estimated: `109497` + // Minimum execution time: 11_971 nanoseconds. + Weight::from_parts(16_060_361, 109497) + // Standard Error: 665 + .saturating_add(Weight::from_ref_time(286_324).saturating_mul(s.into())) + .saturating_add(T::DbWeight::get().reads(1_u64)) + .saturating_add(T::DbWeight::get().writes(1_u64)) } - // Storage: Scheduler Agenda (r:1 w:1) - // Storage: Scheduler Lookup (r:0 w:1) + /// Storage: Scheduler Agenda (r:1 w:1) + /// Proof: Scheduler Agenda (max_values: None, max_size: Some(107022), added: 109497, mode: MaxEncodedLen) + /// Storage: Scheduler Lookup (r:0 w:1) + /// Proof: Scheduler Lookup (max_values: None, max_size: Some(48), added: 2523, mode: MaxEncodedLen) /// The range of component `s` is `[1, 512]`. fn cancel(s: u32, ) -> Weight { - // Minimum execution time: 21_686 nanoseconds. - Weight::from_ref_time(25_696_496 as u64) - // Standard Error: 1_261 - .saturating_add(Weight::from_ref_time(362_498 as u64).saturating_mul(s as u64)) - .saturating_add(T::DbWeight::get().reads(1 as u64)) - .saturating_add(T::DbWeight::get().writes(2 as u64)) + // Proof Size summary in bytes: + // Measured: `112 + s * (177 ±0)` + // Estimated: `109497` + // Minimum execution time: 15_594 nanoseconds. + Weight::from_parts(17_191_501, 109497) + // Standard Error: 626 + .saturating_add(Weight::from_ref_time(425_572).saturating_mul(s.into())) + .saturating_add(T::DbWeight::get().reads(1_u64)) + .saturating_add(T::DbWeight::get().writes(2_u64)) } - // Storage: Scheduler Lookup (r:1 w:1) - // Storage: Scheduler Agenda (r:1 w:1) + /// Storage: Scheduler Lookup (r:1 w:1) + /// Proof: Scheduler Lookup (max_values: None, max_size: Some(48), added: 2523, mode: MaxEncodedLen) + /// Storage: Scheduler Agenda (r:1 w:1) + /// Proof: Scheduler Agenda (max_values: None, max_size: Some(107022), added: 109497, mode: MaxEncodedLen) /// The range of component `s` is `[0, 511]`. fn schedule_named(s: u32, ) -> Weight { - // Minimum execution time: 23_084 nanoseconds. - Weight::from_ref_time(31_255_518 as u64) - // Standard Error: 1_258 - .saturating_add(Weight::from_ref_time(382_534 as u64).saturating_mul(s as u64)) - .saturating_add(T::DbWeight::get().reads(2 as u64)) - .saturating_add(T::DbWeight::get().writes(2 as u64)) + // Proof Size summary in bytes: + // Measured: `627 + s * (178 ±0)` + // Estimated: `112020` + // Minimum execution time: 15_127 nanoseconds. + Weight::from_parts(20_932_642, 112020) + // Standard Error: 692 + .saturating_add(Weight::from_ref_time(288_344).saturating_mul(s.into())) + .saturating_add(T::DbWeight::get().reads(2_u64)) + .saturating_add(T::DbWeight::get().writes(2_u64)) } - // Storage: Scheduler Lookup (r:1 w:1) - // Storage: Scheduler Agenda (r:1 w:1) + /// Storage: Scheduler Lookup (r:1 w:1) + /// Proof: Scheduler Lookup (max_values: None, max_size: Some(48), added: 2523, mode: MaxEncodedLen) + /// Storage: Scheduler Agenda (r:1 w:1) + /// Proof: Scheduler Agenda (max_values: None, max_size: Some(107022), added: 109497, mode: MaxEncodedLen) /// The range of component `s` is `[1, 512]`. fn cancel_named(s: u32, ) -> Weight { - // Minimum execution time: 23_862 nanoseconds. - Weight::from_ref_time(28_591_336 as u64) - // Standard Error: 742 - .saturating_add(Weight::from_ref_time(369_305 as u64).saturating_mul(s as u64)) - .saturating_add(T::DbWeight::get().reads(2 as u64)) - .saturating_add(T::DbWeight::get().writes(2 as u64)) + // Proof Size summary in bytes: + // Measured: `740 + s * (177 ±0)` + // Estimated: `112020` + // Minimum execution time: 16_859 nanoseconds. + Weight::from_parts(19_736_937, 112020) + // Standard Error: 676 + .saturating_add(Weight::from_ref_time(429_770).saturating_mul(s.into())) + .saturating_add(T::DbWeight::get().reads(2_u64)) + .saturating_add(T::DbWeight::get().writes(2_u64)) } } // For backwards compatibility and tests impl WeightInfo for () { - // Storage: Scheduler IncompleteSince (r:1 w:1) + /// Storage: Scheduler IncompleteSince (r:1 w:1) + /// Proof: Scheduler IncompleteSince (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) fn service_agendas_base() -> Weight { - // Minimum execution time: 5_131 nanoseconds. - Weight::from_ref_time(5_286_000 as u64) - .saturating_add(RocksDbWeight::get().reads(1 as u64)) - .saturating_add(RocksDbWeight::get().writes(1 as u64)) + // Proof Size summary in bytes: + // Measured: `30` + // Estimated: `499` + // Minimum execution time: 3_670 nanoseconds. + Weight::from_parts(3_838_000, 499) + .saturating_add(RocksDbWeight::get().reads(1_u64)) + .saturating_add(RocksDbWeight::get().writes(1_u64)) } - // Storage: Scheduler Agenda (r:1 w:1) + /// Storage: Scheduler Agenda (r:1 w:1) + /// Proof: Scheduler Agenda (max_values: None, max_size: Some(107022), added: 109497, mode: MaxEncodedLen) /// The range of component `s` is `[0, 512]`. fn service_agenda_base(s: u32, ) -> Weight { - // Minimum execution time: 4_111 nanoseconds. - Weight::from_ref_time(8_763_440 as u64) - // Standard Error: 783 - .saturating_add(Weight::from_ref_time(372_339 as u64).saturating_mul(s as u64)) - .saturating_add(RocksDbWeight::get().reads(1 as u64)) - .saturating_add(RocksDbWeight::get().writes(1 as u64)) + // Proof Size summary in bytes: + // Measured: `112 + s * (177 ±0)` + // Estimated: `109497` + // Minimum execution time: 3_079 nanoseconds. + Weight::from_parts(7_087_647, 109497) + // Standard Error: 658 + .saturating_add(Weight::from_ref_time(279_320).saturating_mul(s.into())) + .saturating_add(RocksDbWeight::get().reads(1_u64)) + .saturating_add(RocksDbWeight::get().writes(1_u64)) } fn service_task_base() -> Weight { - // Minimum execution time: 10_880 nanoseconds. - Weight::from_ref_time(11_194_000 as u64) + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 5_192 nanoseconds. + Weight::from_ref_time(5_528_000) } - // Storage: Preimage PreimageFor (r:1 w:1) - // Storage: Preimage StatusFor (r:1 w:1) + /// Storage: Preimage PreimageFor (r:1 w:1) + /// Proof: Preimage PreimageFor (max_values: None, max_size: Some(4194344), added: 4196819, mode: Measured) + /// Storage: Preimage StatusFor (r:1 w:1) + /// Proof: Preimage StatusFor (max_values: None, max_size: Some(91), added: 2566, mode: MaxEncodedLen) /// The range of component `s` is `[128, 4194304]`. fn service_task_fetched(s: u32, ) -> Weight { - // Minimum execution time: 25_347 nanoseconds. - Weight::from_ref_time(25_717_000 as u64) + // Proof Size summary in bytes: + // Measured: `211 + s * (1 ±0)` + // Estimated: `5252 + s * (1 ±0)` + // Minimum execution time: 17_284 nanoseconds. + Weight::from_parts(17_574_000, 5252) // Standard Error: 0 - .saturating_add(Weight::from_ref_time(1_128 as u64).saturating_mul(s as u64)) - .saturating_add(RocksDbWeight::get().reads(2 as u64)) - .saturating_add(RocksDbWeight::get().writes(2 as u64)) + .saturating_add(Weight::from_ref_time(1_126).saturating_mul(s.into())) + .saturating_add(RocksDbWeight::get().reads(2_u64)) + .saturating_add(RocksDbWeight::get().writes(2_u64)) + .saturating_add(Weight::from_proof_size(1).saturating_mul(s.into())) } - // Storage: Scheduler Lookup (r:0 w:1) + /// Storage: Scheduler Lookup (r:0 w:1) + /// Proof: Scheduler Lookup (max_values: None, max_size: Some(48), added: 2523, mode: MaxEncodedLen) fn service_task_named() -> Weight { - // Minimum execution time: 12_894 nanoseconds. - Weight::from_ref_time(13_108_000 as u64) - .saturating_add(RocksDbWeight::get().writes(1 as u64)) + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 7_020 nanoseconds. + Weight::from_ref_time(7_262_000) + .saturating_add(RocksDbWeight::get().writes(1_u64)) } fn service_task_periodic() -> Weight { - // Minimum execution time: 10_667 nanoseconds. - Weight::from_ref_time(10_908_000 as u64) + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 5_187 nanoseconds. + Weight::from_ref_time(5_368_000) } fn execute_dispatch_signed() -> Weight { - // Minimum execution time: 4_124 nanoseconds. - Weight::from_ref_time(4_680_000 as u64) + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 2_313 nanoseconds. + Weight::from_ref_time(2_404_000) } fn execute_dispatch_unsigned() -> Weight { - // Minimum execution time: 4_156 nanoseconds. - Weight::from_ref_time(4_361_000 as u64) + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 2_187 nanoseconds. + Weight::from_ref_time(2_362_000) } - // Storage: Scheduler Agenda (r:1 w:1) + /// Storage: Scheduler Agenda (r:1 w:1) + /// Proof: Scheduler Agenda (max_values: None, max_size: Some(107022), added: 109497, mode: MaxEncodedLen) /// The range of component `s` is `[0, 511]`. fn schedule(s: u32, ) -> Weight { - // Minimum execution time: 20_504 nanoseconds. - Weight::from_ref_time(27_066_818 as u64) - // Standard Error: 1_114 - .saturating_add(Weight::from_ref_time(372_897 as u64).saturating_mul(s as u64)) - .saturating_add(RocksDbWeight::get().reads(1 as u64)) - .saturating_add(RocksDbWeight::get().writes(1 as u64)) + // Proof Size summary in bytes: + // Measured: `112 + s * (177 ±0)` + // Estimated: `109497` + // Minimum execution time: 11_971 nanoseconds. + Weight::from_parts(16_060_361, 109497) + // Standard Error: 665 + .saturating_add(Weight::from_ref_time(286_324).saturating_mul(s.into())) + .saturating_add(RocksDbWeight::get().reads(1_u64)) + .saturating_add(RocksDbWeight::get().writes(1_u64)) } - // Storage: Scheduler Agenda (r:1 w:1) - // Storage: Scheduler Lookup (r:0 w:1) + /// Storage: Scheduler Agenda (r:1 w:1) + /// Proof: Scheduler Agenda (max_values: None, max_size: Some(107022), added: 109497, mode: MaxEncodedLen) + /// Storage: Scheduler Lookup (r:0 w:1) + /// Proof: Scheduler Lookup (max_values: None, max_size: Some(48), added: 2523, mode: MaxEncodedLen) /// The range of component `s` is `[1, 512]`. fn cancel(s: u32, ) -> Weight { - // Minimum execution time: 21_686 nanoseconds. - Weight::from_ref_time(25_696_496 as u64) - // Standard Error: 1_261 - .saturating_add(Weight::from_ref_time(362_498 as u64).saturating_mul(s as u64)) - .saturating_add(RocksDbWeight::get().reads(1 as u64)) - .saturating_add(RocksDbWeight::get().writes(2 as u64)) + // Proof Size summary in bytes: + // Measured: `112 + s * (177 ±0)` + // Estimated: `109497` + // Minimum execution time: 15_594 nanoseconds. + Weight::from_parts(17_191_501, 109497) + // Standard Error: 626 + .saturating_add(Weight::from_ref_time(425_572).saturating_mul(s.into())) + .saturating_add(RocksDbWeight::get().reads(1_u64)) + .saturating_add(RocksDbWeight::get().writes(2_u64)) } - // Storage: Scheduler Lookup (r:1 w:1) - // Storage: Scheduler Agenda (r:1 w:1) + /// Storage: Scheduler Lookup (r:1 w:1) + /// Proof: Scheduler Lookup (max_values: None, max_size: Some(48), added: 2523, mode: MaxEncodedLen) + /// Storage: Scheduler Agenda (r:1 w:1) + /// Proof: Scheduler Agenda (max_values: None, max_size: Some(107022), added: 109497, mode: MaxEncodedLen) /// The range of component `s` is `[0, 511]`. fn schedule_named(s: u32, ) -> Weight { - // Minimum execution time: 23_084 nanoseconds. - Weight::from_ref_time(31_255_518 as u64) - // Standard Error: 1_258 - .saturating_add(Weight::from_ref_time(382_534 as u64).saturating_mul(s as u64)) - .saturating_add(RocksDbWeight::get().reads(2 as u64)) - .saturating_add(RocksDbWeight::get().writes(2 as u64)) + // Proof Size summary in bytes: + // Measured: `627 + s * (178 ±0)` + // Estimated: `112020` + // Minimum execution time: 15_127 nanoseconds. + Weight::from_parts(20_932_642, 112020) + // Standard Error: 692 + .saturating_add(Weight::from_ref_time(288_344).saturating_mul(s.into())) + .saturating_add(RocksDbWeight::get().reads(2_u64)) + .saturating_add(RocksDbWeight::get().writes(2_u64)) } - // Storage: Scheduler Lookup (r:1 w:1) - // Storage: Scheduler Agenda (r:1 w:1) + /// Storage: Scheduler Lookup (r:1 w:1) + /// Proof: Scheduler Lookup (max_values: None, max_size: Some(48), added: 2523, mode: MaxEncodedLen) + /// Storage: Scheduler Agenda (r:1 w:1) + /// Proof: Scheduler Agenda (max_values: None, max_size: Some(107022), added: 109497, mode: MaxEncodedLen) /// The range of component `s` is `[1, 512]`. fn cancel_named(s: u32, ) -> Weight { - // Minimum execution time: 23_862 nanoseconds. - Weight::from_ref_time(28_591_336 as u64) - // Standard Error: 742 - .saturating_add(Weight::from_ref_time(369_305 as u64).saturating_mul(s as u64)) - .saturating_add(RocksDbWeight::get().reads(2 as u64)) - .saturating_add(RocksDbWeight::get().writes(2 as u64)) + // Proof Size summary in bytes: + // Measured: `740 + s * (177 ±0)` + // Estimated: `112020` + // Minimum execution time: 16_859 nanoseconds. + Weight::from_parts(19_736_937, 112020) + // Standard Error: 676 + .saturating_add(Weight::from_ref_time(429_770).saturating_mul(s.into())) + .saturating_add(RocksDbWeight::get().reads(2_u64)) + .saturating_add(RocksDbWeight::get().writes(2_u64)) } } diff --git a/frame/session/src/weights.rs b/frame/session/src/weights.rs index d29413a33..aea9d9dbf 100644 --- a/frame/session/src/weights.rs +++ b/frame/session/src/weights.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2022 Parity Technologies (UK) Ltd. +// Copyright (C) 2023 Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); @@ -18,7 +18,8 @@ //! Autogenerated weights for pallet_session //! //! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev -//! DATE: 2022-11-07, STEPS: `50`, REPEAT: 20, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! DATE: 2023-01-24, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! WORST CASE MAP SIZE: `1000000` //! HOSTNAME: `bm2`, CPU: `Intel(R) Core(TM) i7-7700K CPU @ 4.20GHz` //! EXECUTION: Some(Wasm), WASM-EXECUTION: Compiled, CHAIN: Some("dev"), DB CACHE: 1024 @@ -54,44 +55,68 @@ pub trait WeightInfo { /// Weights for pallet_session using the Substrate node and recommended hardware. pub struct SubstrateWeight(PhantomData); impl WeightInfo for SubstrateWeight { - // Storage: Staking Ledger (r:1 w:0) - // Storage: Session NextKeys (r:1 w:1) - // Storage: Session KeyOwner (r:4 w:4) + /// Storage: Staking Ledger (r:1 w:0) + /// Proof: Staking Ledger (max_values: None, max_size: Some(1091), added: 3566, mode: MaxEncodedLen) + /// Storage: Session NextKeys (r:1 w:1) + /// Proof Skipped: Session NextKeys (max_values: None, max_size: None, mode: Measured) + /// Storage: Session KeyOwner (r:4 w:4) + /// Proof Skipped: Session KeyOwner (max_values: None, max_size: None, mode: Measured) fn set_keys() -> Weight { - // Minimum execution time: 59_046 nanoseconds. - Weight::from_ref_time(59_934_000 as u64) - .saturating_add(T::DbWeight::get().reads(6 as u64)) - .saturating_add(T::DbWeight::get().writes(5 as u64)) + // Proof Size summary in bytes: + // Measured: `1955` + // Estimated: `19851` + // Minimum execution time: 40_867 nanoseconds. + Weight::from_parts(41_319_000, 19851) + .saturating_add(T::DbWeight::get().reads(6_u64)) + .saturating_add(T::DbWeight::get().writes(5_u64)) } - // Storage: Staking Ledger (r:1 w:0) - // Storage: Session NextKeys (r:1 w:1) - // Storage: Session KeyOwner (r:0 w:4) + /// Storage: Staking Ledger (r:1 w:0) + /// Proof: Staking Ledger (max_values: None, max_size: Some(1091), added: 3566, mode: MaxEncodedLen) + /// Storage: Session NextKeys (r:1 w:1) + /// Proof Skipped: Session NextKeys (max_values: None, max_size: None, mode: Measured) + /// Storage: Session KeyOwner (r:0 w:4) + /// Proof Skipped: Session KeyOwner (max_values: None, max_size: None, mode: Measured) fn purge_keys() -> Weight { - // Minimum execution time: 48_872 nanoseconds. - Weight::from_ref_time(49_666_000 as u64) - .saturating_add(T::DbWeight::get().reads(2 as u64)) - .saturating_add(T::DbWeight::get().writes(5 as u64)) + // Proof Size summary in bytes: + // Measured: `1854` + // Estimated: `9749` + // Minimum execution time: 30_286 nanoseconds. + Weight::from_parts(30_620_000, 9749) + .saturating_add(T::DbWeight::get().reads(2_u64)) + .saturating_add(T::DbWeight::get().writes(5_u64)) } } // For backwards compatibility and tests impl WeightInfo for () { - // Storage: Staking Ledger (r:1 w:0) - // Storage: Session NextKeys (r:1 w:1) - // Storage: Session KeyOwner (r:4 w:4) + /// Storage: Staking Ledger (r:1 w:0) + /// Proof: Staking Ledger (max_values: None, max_size: Some(1091), added: 3566, mode: MaxEncodedLen) + /// Storage: Session NextKeys (r:1 w:1) + /// Proof Skipped: Session NextKeys (max_values: None, max_size: None, mode: Measured) + /// Storage: Session KeyOwner (r:4 w:4) + /// Proof Skipped: Session KeyOwner (max_values: None, max_size: None, mode: Measured) fn set_keys() -> Weight { - // Minimum execution time: 59_046 nanoseconds. - Weight::from_ref_time(59_934_000 as u64) - .saturating_add(RocksDbWeight::get().reads(6 as u64)) - .saturating_add(RocksDbWeight::get().writes(5 as u64)) + // Proof Size summary in bytes: + // Measured: `1955` + // Estimated: `19851` + // Minimum execution time: 40_867 nanoseconds. + Weight::from_parts(41_319_000, 19851) + .saturating_add(RocksDbWeight::get().reads(6_u64)) + .saturating_add(RocksDbWeight::get().writes(5_u64)) } - // Storage: Staking Ledger (r:1 w:0) - // Storage: Session NextKeys (r:1 w:1) - // Storage: Session KeyOwner (r:0 w:4) + /// Storage: Staking Ledger (r:1 w:0) + /// Proof: Staking Ledger (max_values: None, max_size: Some(1091), added: 3566, mode: MaxEncodedLen) + /// Storage: Session NextKeys (r:1 w:1) + /// Proof Skipped: Session NextKeys (max_values: None, max_size: None, mode: Measured) + /// Storage: Session KeyOwner (r:0 w:4) + /// Proof Skipped: Session KeyOwner (max_values: None, max_size: None, mode: Measured) fn purge_keys() -> Weight { - // Minimum execution time: 48_872 nanoseconds. - Weight::from_ref_time(49_666_000 as u64) - .saturating_add(RocksDbWeight::get().reads(2 as u64)) - .saturating_add(RocksDbWeight::get().writes(5 as u64)) + // Proof Size summary in bytes: + // Measured: `1854` + // Estimated: `9749` + // Minimum execution time: 30_286 nanoseconds. + Weight::from_parts(30_620_000, 9749) + .saturating_add(RocksDbWeight::get().reads(2_u64)) + .saturating_add(RocksDbWeight::get().writes(5_u64)) } } diff --git a/frame/staking/src/weights.rs b/frame/staking/src/weights.rs index 9c283f5a0..af2afa2b5 100644 --- a/frame/staking/src/weights.rs +++ b/frame/staking/src/weights.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2022 Parity Technologies (UK) Ltd. +// Copyright (C) 2023 Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); @@ -18,25 +18,25 @@ //! Autogenerated weights for pallet_staking //! //! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev -//! DATE: 2022-12-25, STEPS: `50`, REPEAT: 20, LOW RANGE: `[]`, HIGH RANGE: `[]` -//! HOSTNAME: `bm3`, CPU: `Intel(R) Core(TM) i7-7700K CPU @ 4.20GHz` +//! DATE: 2023-01-25, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! WORST CASE MAP SIZE: `1000000` +//! HOSTNAME: `bm2`, CPU: `Intel(R) Core(TM) i7-7700K CPU @ 4.20GHz` //! EXECUTION: Some(Wasm), WASM-EXECUTION: Compiled, CHAIN: Some("dev"), DB CACHE: 1024 // Executed Command: -// /home/benchbot/cargo_target_dir/production/substrate +// ./target/production/substrate // benchmark // pallet +// --chain=dev // --steps=50 // --repeat=20 +// --pallet=pallet_staking // --extrinsic=* // --execution=wasm // --wasm-execution=compiled // --heap-pages=4096 -// --json-file=/var/lib/gitlab-runner/builds/zyw4fam_/0/parity/mirrors/substrate/.git/.artifacts/bench.json -// --pallet=pallet_staking -// --chain=dev -// --header=./HEADER-APACHE2 // --output=./frame/staking/src/weights.rs +// --header=./HEADER-APACHE2 // --template=./.maintain/frame-weight-template.hbs #![cfg_attr(rustfmt, rustfmt_skip)] @@ -83,838 +83,1404 @@ pub trait WeightInfo { /// Weights for pallet_staking using the Substrate node and recommended hardware. pub struct SubstrateWeight(PhantomData); impl WeightInfo for SubstrateWeight { - // Storage: Staking Bonded (r:1 w:1) - // Storage: Staking Ledger (r:1 w:1) - // Storage: Staking CurrentEra (r:1 w:0) - // Storage: Balances Locks (r:1 w:1) - // Storage: Staking Payee (r:0 w:1) + /// Storage: Staking Bonded (r:1 w:1) + /// Proof: Staking Bonded (max_values: None, max_size: Some(72), added: 2547, mode: MaxEncodedLen) + /// Storage: Staking Ledger (r:1 w:1) + /// Proof: Staking Ledger (max_values: None, max_size: Some(1091), added: 3566, mode: MaxEncodedLen) + /// Storage: Staking CurrentEra (r:1 w:0) + /// Proof: Staking CurrentEra (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) + /// Storage: Balances Locks (r:1 w:1) + /// Proof: Balances Locks (max_values: None, max_size: Some(1299), added: 3774, mode: MaxEncodedLen) + /// Storage: Staking Payee (r:0 w:1) + /// Proof: Staking Payee (max_values: None, max_size: Some(73), added: 2548, mode: MaxEncodedLen) fn bond() -> Weight { - // Minimum execution time: 54_884 nanoseconds. - Weight::from_ref_time(55_487_000) - .saturating_add(T::DbWeight::get().reads(4)) - .saturating_add(T::DbWeight::get().writes(4)) - } - // Storage: Staking Bonded (r:1 w:0) - // Storage: Staking Ledger (r:1 w:1) - // Storage: Balances Locks (r:1 w:1) - // Storage: VoterList ListNodes (r:3 w:3) - // Storage: VoterList ListBags (r:2 w:2) + // Proof Size summary in bytes: + // Measured: `1079` + // Estimated: `10386` + // Minimum execution time: 40_015 nanoseconds. + Weight::from_parts(40_601_000, 10386) + .saturating_add(T::DbWeight::get().reads(4_u64)) + .saturating_add(T::DbWeight::get().writes(4_u64)) + } + /// Storage: Staking Bonded (r:1 w:0) + /// Proof: Staking Bonded (max_values: None, max_size: Some(72), added: 2547, mode: MaxEncodedLen) + /// Storage: Staking Ledger (r:1 w:1) + /// Proof: Staking Ledger (max_values: None, max_size: Some(1091), added: 3566, mode: MaxEncodedLen) + /// Storage: Balances Locks (r:1 w:1) + /// Proof: Balances Locks (max_values: None, max_size: Some(1299), added: 3774, mode: MaxEncodedLen) + /// Storage: VoterList ListNodes (r:3 w:3) + /// Proof: VoterList ListNodes (max_values: None, max_size: Some(154), added: 2629, mode: MaxEncodedLen) + /// Storage: VoterList ListBags (r:2 w:2) + /// Proof: VoterList ListBags (max_values: None, max_size: Some(82), added: 2557, mode: MaxEncodedLen) fn bond_extra() -> Weight { - // Minimum execution time: 95_115 nanoseconds. - Weight::from_ref_time(96_213_000) - .saturating_add(T::DbWeight::get().reads(8)) - .saturating_add(T::DbWeight::get().writes(7)) - } - // Storage: Staking Ledger (r:1 w:1) - // Storage: Staking Nominators (r:1 w:0) - // Storage: Staking MinNominatorBond (r:1 w:0) - // Storage: Staking CurrentEra (r:1 w:0) - // Storage: Balances Locks (r:1 w:1) - // Storage: System Account (r:1 w:1) - // Storage: VoterList ListNodes (r:3 w:3) - // Storage: Staking Bonded (r:1 w:0) - // Storage: VoterList ListBags (r:2 w:2) + // Proof Size summary in bytes: + // Measured: `2252` + // Estimated: `22888` + // Minimum execution time: 74_781 nanoseconds. + Weight::from_parts(75_188_000, 22888) + .saturating_add(T::DbWeight::get().reads(8_u64)) + .saturating_add(T::DbWeight::get().writes(7_u64)) + } + /// Storage: Staking Ledger (r:1 w:1) + /// Proof: Staking Ledger (max_values: None, max_size: Some(1091), added: 3566, mode: MaxEncodedLen) + /// Storage: Staking Nominators (r:1 w:0) + /// Proof: Staking Nominators (max_values: None, max_size: Some(558), added: 3033, mode: MaxEncodedLen) + /// Storage: Staking MinNominatorBond (r:1 w:0) + /// Proof: Staking MinNominatorBond (max_values: Some(1), max_size: Some(16), added: 511, mode: MaxEncodedLen) + /// Storage: Staking CurrentEra (r:1 w:0) + /// Proof: Staking CurrentEra (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) + /// Storage: Balances Locks (r:1 w:1) + /// Proof: Balances Locks (max_values: None, max_size: Some(1299), added: 3774, mode: MaxEncodedLen) + /// Storage: System Account (r:1 w:1) + /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) + /// Storage: VoterList ListNodes (r:3 w:3) + /// Proof: VoterList ListNodes (max_values: None, max_size: Some(154), added: 2629, mode: MaxEncodedLen) + /// Storage: Staking Bonded (r:1 w:0) + /// Proof: Staking Bonded (max_values: None, max_size: Some(72), added: 2547, mode: MaxEncodedLen) + /// Storage: VoterList ListBags (r:2 w:2) + /// Proof: VoterList ListBags (max_values: None, max_size: Some(82), added: 2557, mode: MaxEncodedLen) fn unbond() -> Weight { - // Minimum execution time: 102_031 nanoseconds. - Weight::from_ref_time(102_842_000) - .saturating_add(T::DbWeight::get().reads(12)) - .saturating_add(T::DbWeight::get().writes(8)) - } - // Storage: Staking Ledger (r:1 w:1) - // Storage: Staking CurrentEra (r:1 w:0) - // Storage: Balances Locks (r:1 w:1) - // Storage: System Account (r:1 w:1) + // Proof Size summary in bytes: + // Measured: `2457` + // Estimated: `29534` + // Minimum execution time: 81_299 nanoseconds. + Weight::from_parts(82_242_000, 29534) + .saturating_add(T::DbWeight::get().reads(12_u64)) + .saturating_add(T::DbWeight::get().writes(8_u64)) + } + /// Storage: Staking Ledger (r:1 w:1) + /// Proof: Staking Ledger (max_values: None, max_size: Some(1091), added: 3566, mode: MaxEncodedLen) + /// Storage: Staking CurrentEra (r:1 w:0) + /// Proof: Staking CurrentEra (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) + /// Storage: Balances Locks (r:1 w:1) + /// Proof: Balances Locks (max_values: None, max_size: Some(1299), added: 3774, mode: MaxEncodedLen) + /// Storage: System Account (r:1 w:1) + /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) /// The range of component `s` is `[0, 100]`. fn withdraw_unbonded_update(s: u32, ) -> Weight { - // Minimum execution time: 46_569 nanoseconds. - Weight::from_ref_time(48_034_493) - // Standard Error: 654 - .saturating_add(Weight::from_ref_time(63_628).saturating_mul(s.into())) - .saturating_add(T::DbWeight::get().reads(4)) - .saturating_add(T::DbWeight::get().writes(3)) - } - // Storage: Staking Ledger (r:1 w:1) - // Storage: Staking CurrentEra (r:1 w:0) - // Storage: Staking Bonded (r:1 w:1) - // Storage: Staking SlashingSpans (r:1 w:0) - // Storage: Staking Validators (r:1 w:0) - // Storage: Staking Nominators (r:1 w:1) - // Storage: Staking CounterForNominators (r:1 w:1) - // Storage: VoterList ListNodes (r:2 w:2) - // Storage: VoterList ListBags (r:1 w:1) - // Storage: VoterList CounterForListNodes (r:1 w:1) - // Storage: System Account (r:1 w:1) - // Storage: Balances Locks (r:1 w:1) - // Storage: Staking Payee (r:0 w:1) - // Storage: Staking SpanSlash (r:0 w:2) + // Proof Size summary in bytes: + // Measured: `1085` + // Estimated: `10442` + // Minimum execution time: 31_479 nanoseconds. + Weight::from_parts(32_410_035, 10442) + // Standard Error: 313 + .saturating_add(Weight::from_ref_time(9_090).saturating_mul(s.into())) + .saturating_add(T::DbWeight::get().reads(4_u64)) + .saturating_add(T::DbWeight::get().writes(3_u64)) + } + /// Storage: Staking Ledger (r:1 w:1) + /// Proof: Staking Ledger (max_values: None, max_size: Some(1091), added: 3566, mode: MaxEncodedLen) + /// Storage: Staking CurrentEra (r:1 w:0) + /// Proof: Staking CurrentEra (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) + /// Storage: Staking Bonded (r:1 w:1) + /// Proof: Staking Bonded (max_values: None, max_size: Some(72), added: 2547, mode: MaxEncodedLen) + /// Storage: Staking SlashingSpans (r:1 w:1) + /// Proof Skipped: Staking SlashingSpans (max_values: None, max_size: None, mode: Measured) + /// Storage: Staking Validators (r:1 w:0) + /// Proof: Staking Validators (max_values: None, max_size: Some(45), added: 2520, mode: MaxEncodedLen) + /// Storage: Staking Nominators (r:1 w:1) + /// Proof: Staking Nominators (max_values: None, max_size: Some(558), added: 3033, mode: MaxEncodedLen) + /// Storage: Staking CounterForNominators (r:1 w:1) + /// Proof: Staking CounterForNominators (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) + /// Storage: VoterList ListNodes (r:2 w:2) + /// Proof: VoterList ListNodes (max_values: None, max_size: Some(154), added: 2629, mode: MaxEncodedLen) + /// Storage: VoterList ListBags (r:1 w:1) + /// Proof: VoterList ListBags (max_values: None, max_size: Some(82), added: 2557, mode: MaxEncodedLen) + /// Storage: VoterList CounterForListNodes (r:1 w:1) + /// Proof: VoterList CounterForListNodes (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) + /// Storage: System Account (r:1 w:1) + /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) + /// Storage: Balances Locks (r:1 w:1) + /// Proof: Balances Locks (max_values: None, max_size: Some(1299), added: 3774, mode: MaxEncodedLen) + /// Storage: Staking Payee (r:0 w:1) + /// Proof: Staking Payee (max_values: None, max_size: Some(73), added: 2548, mode: MaxEncodedLen) + /// Storage: Staking SpanSlash (r:0 w:100) + /// Proof: Staking SpanSlash (max_values: None, max_size: Some(76), added: 2551, mode: MaxEncodedLen) /// The range of component `s` is `[0, 100]`. fn withdraw_unbonded_kill(s: u32, ) -> Weight { - // Minimum execution time: 90_154 nanoseconds. - Weight::from_ref_time(95_725_631) - // Standard Error: 2_491 - .saturating_add(Weight::from_ref_time(1_110_795).saturating_mul(s.into())) - .saturating_add(T::DbWeight::get().reads(13)) - .saturating_add(T::DbWeight::get().writes(12)) + // Proof Size summary in bytes: + // Measured: `2486 + s * (4 ±0)` + // Estimated: `32303 + s * (4 ±0)` + // Minimum execution time: 71_968 nanoseconds. + Weight::from_parts(76_631_804, 32303) + // Standard Error: 1_613 + .saturating_add(Weight::from_ref_time(1_058_968).saturating_mul(s.into())) + .saturating_add(T::DbWeight::get().reads(13_u64)) + .saturating_add(T::DbWeight::get().writes(12_u64)) .saturating_add(T::DbWeight::get().writes((1_u64).saturating_mul(s.into()))) + .saturating_add(Weight::from_proof_size(4).saturating_mul(s.into())) } - // Storage: Staking Ledger (r:1 w:0) - // Storage: Staking MinValidatorBond (r:1 w:0) - // Storage: Staking MinCommission (r:1 w:0) - // Storage: Staking Validators (r:1 w:1) - // Storage: Staking MaxValidatorsCount (r:1 w:0) - // Storage: Staking Nominators (r:1 w:0) - // Storage: Staking Bonded (r:1 w:0) - // Storage: VoterList ListNodes (r:1 w:1) - // Storage: VoterList ListBags (r:1 w:1) - // Storage: VoterList CounterForListNodes (r:1 w:1) - // Storage: Staking CounterForValidators (r:1 w:1) + /// Storage: Staking Ledger (r:1 w:0) + /// Proof: Staking Ledger (max_values: None, max_size: Some(1091), added: 3566, mode: MaxEncodedLen) + /// Storage: Staking MinValidatorBond (r:1 w:0) + /// Proof: Staking MinValidatorBond (max_values: Some(1), max_size: Some(16), added: 511, mode: MaxEncodedLen) + /// Storage: Staking MinCommission (r:1 w:0) + /// Proof: Staking MinCommission (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) + /// Storage: Staking Validators (r:1 w:1) + /// Proof: Staking Validators (max_values: None, max_size: Some(45), added: 2520, mode: MaxEncodedLen) + /// Storage: Staking MaxValidatorsCount (r:1 w:0) + /// Proof: Staking MaxValidatorsCount (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) + /// Storage: Staking Nominators (r:1 w:0) + /// Proof: Staking Nominators (max_values: None, max_size: Some(558), added: 3033, mode: MaxEncodedLen) + /// Storage: Staking Bonded (r:1 w:0) + /// Proof: Staking Bonded (max_values: None, max_size: Some(72), added: 2547, mode: MaxEncodedLen) + /// Storage: VoterList ListNodes (r:1 w:1) + /// Proof: VoterList ListNodes (max_values: None, max_size: Some(154), added: 2629, mode: MaxEncodedLen) + /// Storage: VoterList ListBags (r:1 w:1) + /// Proof: VoterList ListBags (max_values: None, max_size: Some(82), added: 2557, mode: MaxEncodedLen) + /// Storage: VoterList CounterForListNodes (r:1 w:1) + /// Proof: VoterList CounterForListNodes (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) + /// Storage: Staking CounterForValidators (r:1 w:1) + /// Proof: Staking CounterForValidators (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) fn validate() -> Weight { - // Minimum execution time: 67_978 nanoseconds. - Weight::from_ref_time(69_153_000) - .saturating_add(T::DbWeight::get().reads(11)) - .saturating_add(T::DbWeight::get().writes(5)) + // Proof Size summary in bytes: + // Measured: `1446` + // Estimated: `19359` + // Minimum execution time: 51_963 nanoseconds. + Weight::from_parts(52_418_000, 19359) + .saturating_add(T::DbWeight::get().reads(11_u64)) + .saturating_add(T::DbWeight::get().writes(5_u64)) } - // Storage: Staking Ledger (r:1 w:0) - // Storage: Staking Nominators (r:1 w:1) + /// Storage: Staking Ledger (r:1 w:0) + /// Proof: Staking Ledger (max_values: None, max_size: Some(1091), added: 3566, mode: MaxEncodedLen) + /// Storage: Staking Nominators (r:128 w:128) + /// Proof: Staking Nominators (max_values: None, max_size: Some(558), added: 3033, mode: MaxEncodedLen) /// The range of component `k` is `[1, 128]`. fn kick(k: u32, ) -> Weight { - // Minimum execution time: 45_328 nanoseconds. - Weight::from_ref_time(47_719_103) - // Standard Error: 14_458 - .saturating_add(Weight::from_ref_time(6_999_252).saturating_mul(k.into())) - .saturating_add(T::DbWeight::get().reads(1)) + // Proof Size summary in bytes: + // Measured: `1292 + k * (601 ±0)` + // Estimated: `3566 + k * (3033 ±0)` + // Minimum execution time: 25_685 nanoseconds. + Weight::from_parts(25_290_286, 3566) + // Standard Error: 5_164 + .saturating_add(Weight::from_ref_time(6_445_608).saturating_mul(k.into())) + .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(T::DbWeight::get().reads((1_u64).saturating_mul(k.into()))) .saturating_add(T::DbWeight::get().writes((1_u64).saturating_mul(k.into()))) + .saturating_add(Weight::from_proof_size(3033).saturating_mul(k.into())) } - // Storage: Staking Ledger (r:1 w:0) - // Storage: Staking MinNominatorBond (r:1 w:0) - // Storage: Staking Nominators (r:1 w:1) - // Storage: Staking MaxNominatorsCount (r:1 w:0) - // Storage: Staking Validators (r:2 w:0) - // Storage: Staking CurrentEra (r:1 w:0) - // Storage: Staking Bonded (r:1 w:0) - // Storage: VoterList ListNodes (r:2 w:2) - // Storage: VoterList ListBags (r:1 w:1) - // Storage: VoterList CounterForListNodes (r:1 w:1) - // Storage: Staking CounterForNominators (r:1 w:1) + /// Storage: Staking Ledger (r:1 w:0) + /// Proof: Staking Ledger (max_values: None, max_size: Some(1091), added: 3566, mode: MaxEncodedLen) + /// Storage: Staking MinNominatorBond (r:1 w:0) + /// Proof: Staking MinNominatorBond (max_values: Some(1), max_size: Some(16), added: 511, mode: MaxEncodedLen) + /// Storage: Staking Nominators (r:1 w:1) + /// Proof: Staking Nominators (max_values: None, max_size: Some(558), added: 3033, mode: MaxEncodedLen) + /// Storage: Staking MaxNominatorsCount (r:1 w:0) + /// Proof: Staking MaxNominatorsCount (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) + /// Storage: Staking Validators (r:17 w:0) + /// Proof: Staking Validators (max_values: None, max_size: Some(45), added: 2520, mode: MaxEncodedLen) + /// Storage: Staking CurrentEra (r:1 w:0) + /// Proof: Staking CurrentEra (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) + /// Storage: Staking Bonded (r:1 w:0) + /// Proof: Staking Bonded (max_values: None, max_size: Some(72), added: 2547, mode: MaxEncodedLen) + /// Storage: VoterList ListNodes (r:2 w:2) + /// Proof: VoterList ListNodes (max_values: None, max_size: Some(154), added: 2629, mode: MaxEncodedLen) + /// Storage: VoterList ListBags (r:1 w:1) + /// Proof: VoterList ListBags (max_values: None, max_size: Some(82), added: 2557, mode: MaxEncodedLen) + /// Storage: VoterList CounterForListNodes (r:1 w:1) + /// Proof: VoterList CounterForListNodes (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) + /// Storage: Staking CounterForNominators (r:1 w:1) + /// Proof: Staking CounterForNominators (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) /// The range of component `n` is `[1, 16]`. fn nominate(n: u32, ) -> Weight { - // Minimum execution time: 74_650 nanoseconds. - Weight::from_ref_time(74_350_075) - // Standard Error: 10_527 - .saturating_add(Weight::from_ref_time(2_878_737).saturating_mul(n.into())) - .saturating_add(T::DbWeight::get().reads(12)) + // Proof Size summary in bytes: + // Measured: `1984 + n * (105 ±0)` + // Estimated: `21988 + n * (2520 ±0)` + // Minimum execution time: 59_542 nanoseconds. + Weight::from_parts(57_558_678, 21988) + // Standard Error: 10_364 + .saturating_add(Weight::from_ref_time(2_759_713).saturating_mul(n.into())) + .saturating_add(T::DbWeight::get().reads(12_u64)) .saturating_add(T::DbWeight::get().reads((1_u64).saturating_mul(n.into()))) - .saturating_add(T::DbWeight::get().writes(6)) - } - // Storage: Staking Ledger (r:1 w:0) - // Storage: Staking Validators (r:1 w:0) - // Storage: Staking Nominators (r:1 w:1) - // Storage: Staking CounterForNominators (r:1 w:1) - // Storage: VoterList ListNodes (r:2 w:2) - // Storage: VoterList ListBags (r:1 w:1) - // Storage: VoterList CounterForListNodes (r:1 w:1) + .saturating_add(T::DbWeight::get().writes(6_u64)) + .saturating_add(Weight::from_proof_size(2520).saturating_mul(n.into())) + } + /// Storage: Staking Ledger (r:1 w:0) + /// Proof: Staking Ledger (max_values: None, max_size: Some(1091), added: 3566, mode: MaxEncodedLen) + /// Storage: Staking Validators (r:1 w:0) + /// Proof: Staking Validators (max_values: None, max_size: Some(45), added: 2520, mode: MaxEncodedLen) + /// Storage: Staking Nominators (r:1 w:1) + /// Proof: Staking Nominators (max_values: None, max_size: Some(558), added: 3033, mode: MaxEncodedLen) + /// Storage: Staking CounterForNominators (r:1 w:1) + /// Proof: Staking CounterForNominators (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) + /// Storage: VoterList ListNodes (r:2 w:2) + /// Proof: VoterList ListNodes (max_values: None, max_size: Some(154), added: 2629, mode: MaxEncodedLen) + /// Storage: VoterList ListBags (r:1 w:1) + /// Proof: VoterList ListBags (max_values: None, max_size: Some(82), added: 2557, mode: MaxEncodedLen) + /// Storage: VoterList CounterForListNodes (r:1 w:1) + /// Proof: VoterList CounterForListNodes (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) fn chill() -> Weight { - // Minimum execution time: 67_790 nanoseconds. - Weight::from_ref_time(68_738_000) - .saturating_add(T::DbWeight::get().reads(8)) - .saturating_add(T::DbWeight::get().writes(6)) + // Proof Size summary in bytes: + // Measured: `1876` + // Estimated: `17932` + // Minimum execution time: 52_132 nanoseconds. + Weight::from_parts(52_648_000, 17932) + .saturating_add(T::DbWeight::get().reads(8_u64)) + .saturating_add(T::DbWeight::get().writes(6_u64)) } - // Storage: Staking Ledger (r:1 w:0) - // Storage: Staking Payee (r:0 w:1) + /// Storage: Staking Ledger (r:1 w:0) + /// Proof: Staking Ledger (max_values: None, max_size: Some(1091), added: 3566, mode: MaxEncodedLen) + /// Storage: Staking Payee (r:0 w:1) + /// Proof: Staking Payee (max_values: None, max_size: Some(73), added: 2548, mode: MaxEncodedLen) fn set_payee() -> Weight { - // Minimum execution time: 19_237 nanoseconds. - Weight::from_ref_time(19_534_000) - .saturating_add(T::DbWeight::get().reads(1)) - .saturating_add(T::DbWeight::get().writes(1)) + // Proof Size summary in bytes: + // Measured: `840` + // Estimated: `3566` + // Minimum execution time: 13_399 nanoseconds. + Weight::from_parts(13_567_000, 3566) + .saturating_add(T::DbWeight::get().reads(1_u64)) + .saturating_add(T::DbWeight::get().writes(1_u64)) } - // Storage: Staking Bonded (r:1 w:1) - // Storage: Staking Ledger (r:2 w:2) + /// Storage: Staking Bonded (r:1 w:1) + /// Proof: Staking Bonded (max_values: None, max_size: Some(72), added: 2547, mode: MaxEncodedLen) + /// Storage: Staking Ledger (r:2 w:2) + /// Proof: Staking Ledger (max_values: None, max_size: Some(1091), added: 3566, mode: MaxEncodedLen) fn set_controller() -> Weight { - // Minimum execution time: 27_288 nanoseconds. - Weight::from_ref_time(27_667_000) - .saturating_add(T::DbWeight::get().reads(3)) - .saturating_add(T::DbWeight::get().writes(3)) + // Proof Size summary in bytes: + // Measured: `939` + // Estimated: `9679` + // Minimum execution time: 20_425 nanoseconds. + Weight::from_parts(20_713_000, 9679) + .saturating_add(T::DbWeight::get().reads(3_u64)) + .saturating_add(T::DbWeight::get().writes(3_u64)) } - // Storage: Staking ValidatorCount (r:0 w:1) + /// Storage: Staking ValidatorCount (r:0 w:1) + /// Proof: Staking ValidatorCount (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) fn set_validator_count() -> Weight { - // Minimum execution time: 5_155 nanoseconds. - Weight::from_ref_time(5_464_000) - .saturating_add(T::DbWeight::get().writes(1)) + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 3_069 nanoseconds. + Weight::from_ref_time(3_176_000) + .saturating_add(T::DbWeight::get().writes(1_u64)) } - // Storage: Staking ForceEra (r:0 w:1) + /// Storage: Staking ForceEra (r:0 w:1) + /// Proof: Staking ForceEra (max_values: Some(1), max_size: Some(1), added: 496, mode: MaxEncodedLen) fn force_no_eras() -> Weight { - // Minimum execution time: 5_405 nanoseconds. - Weight::from_ref_time(5_670_000) - .saturating_add(T::DbWeight::get().writes(1)) + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 11_386 nanoseconds. + Weight::from_ref_time(11_672_000) + .saturating_add(T::DbWeight::get().writes(1_u64)) } - // Storage: Staking ForceEra (r:0 w:1) + /// Storage: Staking ForceEra (r:0 w:1) + /// Proof: Staking ForceEra (max_values: Some(1), max_size: Some(1), added: 496, mode: MaxEncodedLen) fn force_new_era() -> Weight { - // Minimum execution time: 5_459 nanoseconds. - Weight::from_ref_time(5_616_000) - .saturating_add(T::DbWeight::get().writes(1)) + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 11_591 nanoseconds. + Weight::from_ref_time(11_799_000) + .saturating_add(T::DbWeight::get().writes(1_u64)) } - // Storage: Staking ForceEra (r:0 w:1) + /// Storage: Staking ForceEra (r:0 w:1) + /// Proof: Staking ForceEra (max_values: Some(1), max_size: Some(1), added: 496, mode: MaxEncodedLen) fn force_new_era_always() -> Weight { - // Minimum execution time: 5_476 nanoseconds. - Weight::from_ref_time(5_692_000) - .saturating_add(T::DbWeight::get().writes(1)) + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 11_553 nanoseconds. + Weight::from_ref_time(11_871_000) + .saturating_add(T::DbWeight::get().writes(1_u64)) } - // Storage: Staking Invulnerables (r:0 w:1) + /// Storage: Staking Invulnerables (r:0 w:1) + /// Proof Skipped: Staking Invulnerables (max_values: Some(1), max_size: None, mode: Measured) /// The range of component `v` is `[0, 1000]`. fn set_invulnerables(v: u32, ) -> Weight { - // Minimum execution time: 5_544 nanoseconds. - Weight::from_ref_time(6_513_190) - // Standard Error: 76 - .saturating_add(Weight::from_ref_time(9_975).saturating_mul(v.into())) - .saturating_add(T::DbWeight::get().writes(1)) - } - // Storage: Staking Bonded (r:1 w:1) - // Storage: Staking SlashingSpans (r:1 w:0) - // Storage: Staking Validators (r:1 w:0) - // Storage: Staking Nominators (r:1 w:1) - // Storage: Staking CounterForNominators (r:1 w:1) - // Storage: VoterList ListNodes (r:2 w:2) - // Storage: VoterList ListBags (r:1 w:1) - // Storage: VoterList CounterForListNodes (r:1 w:1) - // Storage: System Account (r:1 w:1) - // Storage: Balances Locks (r:1 w:1) - // Storage: Staking Ledger (r:0 w:1) - // Storage: Staking Payee (r:0 w:1) - // Storage: Staking SpanSlash (r:0 w:2) + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 3_292 nanoseconds. + Weight::from_ref_time(3_754_352) + // Standard Error: 40 + .saturating_add(Weight::from_ref_time(9_838).saturating_mul(v.into())) + .saturating_add(T::DbWeight::get().writes(1_u64)) + } + /// Storage: Staking Bonded (r:1 w:1) + /// Proof: Staking Bonded (max_values: None, max_size: Some(72), added: 2547, mode: MaxEncodedLen) + /// Storage: Staking SlashingSpans (r:1 w:1) + /// Proof Skipped: Staking SlashingSpans (max_values: None, max_size: None, mode: Measured) + /// Storage: Staking Validators (r:1 w:0) + /// Proof: Staking Validators (max_values: None, max_size: Some(45), added: 2520, mode: MaxEncodedLen) + /// Storage: Staking Nominators (r:1 w:1) + /// Proof: Staking Nominators (max_values: None, max_size: Some(558), added: 3033, mode: MaxEncodedLen) + /// Storage: Staking CounterForNominators (r:1 w:1) + /// Proof: Staking CounterForNominators (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) + /// Storage: VoterList ListNodes (r:2 w:2) + /// Proof: VoterList ListNodes (max_values: None, max_size: Some(154), added: 2629, mode: MaxEncodedLen) + /// Storage: VoterList ListBags (r:1 w:1) + /// Proof: VoterList ListBags (max_values: None, max_size: Some(82), added: 2557, mode: MaxEncodedLen) + /// Storage: VoterList CounterForListNodes (r:1 w:1) + /// Proof: VoterList CounterForListNodes (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) + /// Storage: System Account (r:1 w:1) + /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) + /// Storage: Balances Locks (r:1 w:1) + /// Proof: Balances Locks (max_values: None, max_size: Some(1299), added: 3774, mode: MaxEncodedLen) + /// Storage: Staking Ledger (r:0 w:1) + /// Proof: Staking Ledger (max_values: None, max_size: Some(1091), added: 3566, mode: MaxEncodedLen) + /// Storage: Staking Payee (r:0 w:1) + /// Proof: Staking Payee (max_values: None, max_size: Some(73), added: 2548, mode: MaxEncodedLen) + /// Storage: Staking SpanSlash (r:0 w:100) + /// Proof: Staking SpanSlash (max_values: None, max_size: Some(76), added: 2551, mode: MaxEncodedLen) /// The range of component `s` is `[0, 100]`. fn force_unstake(s: u32, ) -> Weight { - // Minimum execution time: 82_414 nanoseconds. - Weight::from_ref_time(88_511_246) - // Standard Error: 2_622 - .saturating_add(Weight::from_ref_time(1_131_814).saturating_mul(s.into())) - .saturating_add(T::DbWeight::get().reads(11)) - .saturating_add(T::DbWeight::get().writes(12)) + // Proof Size summary in bytes: + // Measured: `2178 + s * (4 ±0)` + // Estimated: `27930 + s * (4 ±0)` + // Minimum execution time: 65_307 nanoseconds. + Weight::from_parts(70_227_980, 27930) + // Standard Error: 2_113 + .saturating_add(Weight::from_ref_time(1_059_856).saturating_mul(s.into())) + .saturating_add(T::DbWeight::get().reads(11_u64)) + .saturating_add(T::DbWeight::get().writes(12_u64)) .saturating_add(T::DbWeight::get().writes((1_u64).saturating_mul(s.into()))) + .saturating_add(Weight::from_proof_size(4).saturating_mul(s.into())) } - // Storage: Staking UnappliedSlashes (r:1 w:1) + /// Storage: Staking UnappliedSlashes (r:1 w:1) + /// Proof Skipped: Staking UnappliedSlashes (max_values: None, max_size: None, mode: Measured) /// The range of component `s` is `[1, 1000]`. fn cancel_deferred_slash(s: u32, ) -> Weight { - // Minimum execution time: 94_197 nanoseconds. - Weight::from_ref_time(903_418_326) - // Standard Error: 59_354 - .saturating_add(Weight::from_ref_time(4_948_354).saturating_mul(s.into())) - .saturating_add(T::DbWeight::get().reads(1)) - .saturating_add(T::DbWeight::get().writes(1)) - } - // Storage: Staking CurrentEra (r:1 w:0) - // Storage: Staking ErasValidatorReward (r:1 w:0) - // Storage: Staking Bonded (r:1 w:0) - // Storage: Staking Ledger (r:1 w:1) - // Storage: Staking ErasStakersClipped (r:1 w:0) - // Storage: Staking ErasRewardPoints (r:1 w:0) - // Storage: Staking ErasValidatorPrefs (r:1 w:0) - // Storage: Staking Payee (r:1 w:0) - // Storage: System Account (r:1 w:1) + // Proof Size summary in bytes: + // Measured: `66671` + // Estimated: `69146` + // Minimum execution time: 89_123 nanoseconds. + Weight::from_parts(890_989_741, 69146) + // Standard Error: 58_282 + .saturating_add(Weight::from_ref_time(4_920_413).saturating_mul(s.into())) + .saturating_add(T::DbWeight::get().reads(1_u64)) + .saturating_add(T::DbWeight::get().writes(1_u64)) + } + /// Storage: Staking CurrentEra (r:1 w:0) + /// Proof: Staking CurrentEra (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) + /// Storage: Staking ErasValidatorReward (r:1 w:0) + /// Proof: Staking ErasValidatorReward (max_values: None, max_size: Some(28), added: 2503, mode: MaxEncodedLen) + /// Storage: Staking Bonded (r:257 w:0) + /// Proof: Staking Bonded (max_values: None, max_size: Some(72), added: 2547, mode: MaxEncodedLen) + /// Storage: Staking Ledger (r:1 w:1) + /// Proof: Staking Ledger (max_values: None, max_size: Some(1091), added: 3566, mode: MaxEncodedLen) + /// Storage: Staking ErasStakersClipped (r:1 w:0) + /// Proof Skipped: Staking ErasStakersClipped (max_values: None, max_size: None, mode: Measured) + /// Storage: Staking ErasRewardPoints (r:1 w:0) + /// Proof Skipped: Staking ErasRewardPoints (max_values: None, max_size: None, mode: Measured) + /// Storage: Staking ErasValidatorPrefs (r:1 w:0) + /// Proof: Staking ErasValidatorPrefs (max_values: None, max_size: Some(57), added: 2532, mode: MaxEncodedLen) + /// Storage: Staking Payee (r:257 w:0) + /// Proof: Staking Payee (max_values: None, max_size: Some(73), added: 2548, mode: MaxEncodedLen) + /// Storage: System Account (r:257 w:257) + /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) /// The range of component `n` is `[0, 256]`. fn payout_stakers_dead_controller(n: u32, ) -> Weight { - // Minimum execution time: 133_065 nanoseconds. - Weight::from_ref_time(197_555_906) - // Standard Error: 19_561 - .saturating_add(Weight::from_ref_time(22_683_426).saturating_mul(n.into())) - .saturating_add(T::DbWeight::get().reads(9)) + // Proof Size summary in bytes: + // Measured: `20345 + n * (143 ±0)` + // Estimated: `54756 + n * (8024 ±1)` + // Minimum execution time: 73_652 nanoseconds. + Weight::from_parts(127_839_483, 54756) + // Standard Error: 14_195 + .saturating_add(Weight::from_ref_time(21_932_079).saturating_mul(n.into())) + .saturating_add(T::DbWeight::get().reads(9_u64)) .saturating_add(T::DbWeight::get().reads((3_u64).saturating_mul(n.into()))) - .saturating_add(T::DbWeight::get().writes(2)) + .saturating_add(T::DbWeight::get().writes(2_u64)) .saturating_add(T::DbWeight::get().writes((1_u64).saturating_mul(n.into()))) + .saturating_add(Weight::from_proof_size(8024).saturating_mul(n.into())) } - // Storage: Staking CurrentEra (r:1 w:0) - // Storage: Staking ErasValidatorReward (r:1 w:0) - // Storage: Staking Bonded (r:1 w:0) - // Storage: Staking Ledger (r:1 w:1) - // Storage: Staking ErasStakersClipped (r:1 w:0) - // Storage: Staking ErasRewardPoints (r:1 w:0) - // Storage: Staking ErasValidatorPrefs (r:1 w:0) - // Storage: Staking Payee (r:1 w:0) - // Storage: System Account (r:1 w:1) - // Storage: Balances Locks (r:1 w:1) + /// Storage: Staking CurrentEra (r:1 w:0) + /// Proof: Staking CurrentEra (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) + /// Storage: Staking ErasValidatorReward (r:1 w:0) + /// Proof: Staking ErasValidatorReward (max_values: None, max_size: Some(28), added: 2503, mode: MaxEncodedLen) + /// Storage: Staking Bonded (r:257 w:0) + /// Proof: Staking Bonded (max_values: None, max_size: Some(72), added: 2547, mode: MaxEncodedLen) + /// Storage: Staking Ledger (r:257 w:257) + /// Proof: Staking Ledger (max_values: None, max_size: Some(1091), added: 3566, mode: MaxEncodedLen) + /// Storage: Staking ErasStakersClipped (r:1 w:0) + /// Proof Skipped: Staking ErasStakersClipped (max_values: None, max_size: None, mode: Measured) + /// Storage: Staking ErasRewardPoints (r:1 w:0) + /// Proof Skipped: Staking ErasRewardPoints (max_values: None, max_size: None, mode: Measured) + /// Storage: Staking ErasValidatorPrefs (r:1 w:0) + /// Proof: Staking ErasValidatorPrefs (max_values: None, max_size: Some(57), added: 2532, mode: MaxEncodedLen) + /// Storage: Staking Payee (r:257 w:0) + /// Proof: Staking Payee (max_values: None, max_size: Some(73), added: 2548, mode: MaxEncodedLen) + /// Storage: System Account (r:257 w:257) + /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) + /// Storage: Balances Locks (r:257 w:257) + /// Proof: Balances Locks (max_values: None, max_size: Some(1299), added: 3774, mode: MaxEncodedLen) /// The range of component `n` is `[0, 256]`. fn payout_stakers_alive_staked(n: u32, ) -> Weight { - // Minimum execution time: 164_719 nanoseconds. - Weight::from_ref_time(226_304_276) - // Standard Error: 31_675 - .saturating_add(Weight::from_ref_time(32_622_427).saturating_mul(n.into())) - .saturating_add(T::DbWeight::get().reads(10)) + // Proof Size summary in bytes: + // Measured: `35099 + n * (465 ±0)` + // Estimated: `83594 + n * (16026 ±0)` + // Minimum execution time: 94_560 nanoseconds. + Weight::from_parts(154_033_219, 83594) + // Standard Error: 26_663 + .saturating_add(Weight::from_ref_time(31_269_223).saturating_mul(n.into())) + .saturating_add(T::DbWeight::get().reads(10_u64)) .saturating_add(T::DbWeight::get().reads((5_u64).saturating_mul(n.into()))) - .saturating_add(T::DbWeight::get().writes(3)) + .saturating_add(T::DbWeight::get().writes(3_u64)) .saturating_add(T::DbWeight::get().writes((3_u64).saturating_mul(n.into()))) + .saturating_add(Weight::from_proof_size(16026).saturating_mul(n.into())) } - // Storage: Staking Ledger (r:1 w:1) - // Storage: Balances Locks (r:1 w:1) - // Storage: System Account (r:1 w:1) - // Storage: VoterList ListNodes (r:3 w:3) - // Storage: Staking Bonded (r:1 w:0) - // Storage: VoterList ListBags (r:2 w:2) + /// Storage: Staking Ledger (r:1 w:1) + /// Proof: Staking Ledger (max_values: None, max_size: Some(1091), added: 3566, mode: MaxEncodedLen) + /// Storage: Balances Locks (r:1 w:1) + /// Proof: Balances Locks (max_values: None, max_size: Some(1299), added: 3774, mode: MaxEncodedLen) + /// Storage: System Account (r:1 w:1) + /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) + /// Storage: VoterList ListNodes (r:3 w:3) + /// Proof: VoterList ListNodes (max_values: None, max_size: Some(154), added: 2629, mode: MaxEncodedLen) + /// Storage: Staking Bonded (r:1 w:0) + /// Proof: Staking Bonded (max_values: None, max_size: Some(72), added: 2547, mode: MaxEncodedLen) + /// Storage: VoterList ListBags (r:2 w:2) + /// Proof: VoterList ListBags (max_values: None, max_size: Some(82), added: 2557, mode: MaxEncodedLen) /// The range of component `l` is `[1, 32]`. fn rebond(l: u32, ) -> Weight { - // Minimum execution time: 95_631 nanoseconds. - Weight::from_ref_time(96_861_556) - // Standard Error: 2_114 - .saturating_add(Weight::from_ref_time(37_543).saturating_mul(l.into())) - .saturating_add(T::DbWeight::get().reads(9)) - .saturating_add(T::DbWeight::get().writes(8)) - } - // Storage: System Account (r:1 w:1) - // Storage: Staking Bonded (r:1 w:1) - // Storage: Staking Ledger (r:1 w:1) - // Storage: Staking SlashingSpans (r:1 w:1) - // Storage: Staking Validators (r:1 w:0) - // Storage: Staking Nominators (r:1 w:1) - // Storage: Staking CounterForNominators (r:1 w:1) - // Storage: VoterList ListNodes (r:2 w:2) - // Storage: VoterList ListBags (r:1 w:1) - // Storage: VoterList CounterForListNodes (r:1 w:1) - // Storage: Balances Locks (r:1 w:1) - // Storage: Staking Payee (r:0 w:1) - // Storage: Staking SpanSlash (r:0 w:1) + // Proof Size summary in bytes: + // Measured: `2253 + l * (7 ±0)` + // Estimated: `25491` + // Minimum execution time: 74_764 nanoseconds. + Weight::from_parts(75_814_067, 25491) + // Standard Error: 1_217 + .saturating_add(Weight::from_ref_time(64_725).saturating_mul(l.into())) + .saturating_add(T::DbWeight::get().reads(9_u64)) + .saturating_add(T::DbWeight::get().writes(8_u64)) + } + /// Storage: System Account (r:1 w:1) + /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) + /// Storage: Staking Bonded (r:1 w:1) + /// Proof: Staking Bonded (max_values: None, max_size: Some(72), added: 2547, mode: MaxEncodedLen) + /// Storage: Staking Ledger (r:1 w:1) + /// Proof: Staking Ledger (max_values: None, max_size: Some(1091), added: 3566, mode: MaxEncodedLen) + /// Storage: Staking SlashingSpans (r:1 w:1) + /// Proof Skipped: Staking SlashingSpans (max_values: None, max_size: None, mode: Measured) + /// Storage: Staking Validators (r:1 w:0) + /// Proof: Staking Validators (max_values: None, max_size: Some(45), added: 2520, mode: MaxEncodedLen) + /// Storage: Staking Nominators (r:1 w:1) + /// Proof: Staking Nominators (max_values: None, max_size: Some(558), added: 3033, mode: MaxEncodedLen) + /// Storage: Staking CounterForNominators (r:1 w:1) + /// Proof: Staking CounterForNominators (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) + /// Storage: VoterList ListNodes (r:2 w:2) + /// Proof: VoterList ListNodes (max_values: None, max_size: Some(154), added: 2629, mode: MaxEncodedLen) + /// Storage: VoterList ListBags (r:1 w:1) + /// Proof: VoterList ListBags (max_values: None, max_size: Some(82), added: 2557, mode: MaxEncodedLen) + /// Storage: VoterList CounterForListNodes (r:1 w:1) + /// Proof: VoterList CounterForListNodes (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) + /// Storage: Balances Locks (r:1 w:1) + /// Proof: Balances Locks (max_values: None, max_size: Some(1299), added: 3774, mode: MaxEncodedLen) + /// Storage: Staking Payee (r:0 w:1) + /// Proof: Staking Payee (max_values: None, max_size: Some(73), added: 2548, mode: MaxEncodedLen) + /// Storage: Staking SpanSlash (r:0 w:100) + /// Proof: Staking SpanSlash (max_values: None, max_size: Some(76), added: 2551, mode: MaxEncodedLen) /// The range of component `s` is `[1, 100]`. fn reap_stash(s: u32, ) -> Weight { - // Minimum execution time: 95_251 nanoseconds. - Weight::from_ref_time(97_818_954) - // Standard Error: 2_356 - .saturating_add(Weight::from_ref_time(1_104_695).saturating_mul(s.into())) - .saturating_add(T::DbWeight::get().reads(12)) - .saturating_add(T::DbWeight::get().writes(12)) + // Proof Size summary in bytes: + // Measured: `2486 + s * (4 ±0)` + // Estimated: `31810 + s * (4 ±0)` + // Minimum execution time: 77_611 nanoseconds. + Weight::from_parts(79_760_034, 31810) + // Standard Error: 1_597 + .saturating_add(Weight::from_ref_time(1_039_268).saturating_mul(s.into())) + .saturating_add(T::DbWeight::get().reads(12_u64)) + .saturating_add(T::DbWeight::get().writes(12_u64)) .saturating_add(T::DbWeight::get().writes((1_u64).saturating_mul(s.into()))) + .saturating_add(Weight::from_proof_size(4).saturating_mul(s.into())) } - // Storage: VoterList CounterForListNodes (r:1 w:0) - // Storage: VoterList ListBags (r:200 w:0) - // Storage: VoterList ListNodes (r:101 w:0) - // Storage: Staking Nominators (r:101 w:0) - // Storage: Staking Validators (r:2 w:0) - // Storage: Staking Bonded (r:101 w:0) - // Storage: Staking Ledger (r:101 w:0) - // Storage: Staking CounterForValidators (r:1 w:0) - // Storage: Staking ValidatorCount (r:1 w:0) - // Storage: Staking MinimumValidatorCount (r:1 w:0) - // Storage: Staking CurrentEra (r:1 w:1) - // Storage: Staking ErasStakersClipped (r:0 w:1) - // Storage: Staking ErasValidatorPrefs (r:0 w:1) - // Storage: Staking ErasStakers (r:0 w:1) - // Storage: Staking ErasTotalStake (r:0 w:1) - // Storage: Staking ErasStartSessionIndex (r:0 w:1) - // Storage: Staking MinimumActiveStake (r:0 w:1) + /// Storage: VoterList CounterForListNodes (r:1 w:0) + /// Proof: VoterList CounterForListNodes (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) + /// Storage: VoterList ListBags (r:200 w:0) + /// Proof: VoterList ListBags (max_values: None, max_size: Some(82), added: 2557, mode: MaxEncodedLen) + /// Storage: VoterList ListNodes (r:110 w:0) + /// Proof: VoterList ListNodes (max_values: None, max_size: Some(154), added: 2629, mode: MaxEncodedLen) + /// Storage: Staking Nominators (r:110 w:0) + /// Proof: Staking Nominators (max_values: None, max_size: Some(558), added: 3033, mode: MaxEncodedLen) + /// Storage: Staking Validators (r:11 w:0) + /// Proof: Staking Validators (max_values: None, max_size: Some(45), added: 2520, mode: MaxEncodedLen) + /// Storage: Staking Bonded (r:110 w:0) + /// Proof: Staking Bonded (max_values: None, max_size: Some(72), added: 2547, mode: MaxEncodedLen) + /// Storage: Staking Ledger (r:110 w:0) + /// Proof: Staking Ledger (max_values: None, max_size: Some(1091), added: 3566, mode: MaxEncodedLen) + /// Storage: Staking CounterForValidators (r:1 w:0) + /// Proof: Staking CounterForValidators (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) + /// Storage: Staking ValidatorCount (r:1 w:0) + /// Proof: Staking ValidatorCount (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) + /// Storage: Staking MinimumValidatorCount (r:1 w:0) + /// Proof: Staking MinimumValidatorCount (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) + /// Storage: Staking CurrentEra (r:1 w:1) + /// Proof: Staking CurrentEra (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) + /// Storage: Staking ErasStakersClipped (r:0 w:10) + /// Proof Skipped: Staking ErasStakersClipped (max_values: None, max_size: None, mode: Measured) + /// Storage: Staking ErasValidatorPrefs (r:0 w:10) + /// Proof: Staking ErasValidatorPrefs (max_values: None, max_size: Some(57), added: 2532, mode: MaxEncodedLen) + /// Storage: Staking ErasStakers (r:0 w:10) + /// Proof Skipped: Staking ErasStakers (max_values: None, max_size: None, mode: Measured) + /// Storage: Staking ErasTotalStake (r:0 w:1) + /// Proof: Staking ErasTotalStake (max_values: None, max_size: Some(28), added: 2503, mode: MaxEncodedLen) + /// Storage: Staking ErasStartSessionIndex (r:0 w:1) + /// Proof: Staking ErasStartSessionIndex (max_values: None, max_size: Some(16), added: 2491, mode: MaxEncodedLen) + /// Storage: Staking MinimumActiveStake (r:0 w:1) + /// Proof: Staking MinimumActiveStake (max_values: Some(1), max_size: Some(16), added: 511, mode: MaxEncodedLen) /// The range of component `v` is `[1, 10]`. /// The range of component `n` is `[0, 100]`. fn new_era(v: u32, n: u32, ) -> Weight { - // Minimum execution time: 512_923 nanoseconds. - Weight::from_ref_time(514_740_000) - // Standard Error: 1_790_238 - .saturating_add(Weight::from_ref_time(59_320_539).saturating_mul(v.into())) - // Standard Error: 178_387 - .saturating_add(Weight::from_ref_time(13_902_705).saturating_mul(n.into())) - .saturating_add(T::DbWeight::get().reads(206)) + // Proof Size summary in bytes: + // Measured: `0 + v * (3662 ±0) + n * (816 ±0)` + // Estimated: `528203 + v * (16743 ±0) + n * (12947 ±0)` + // Minimum execution time: 489_824 nanoseconds. + Weight::from_parts(491_687_000, 528203) + // Standard Error: 1_787_577 + .saturating_add(Weight::from_ref_time(58_719_498).saturating_mul(v.into())) + // Standard Error: 178_122 + .saturating_add(Weight::from_ref_time(13_273_555).saturating_mul(n.into())) + .saturating_add(T::DbWeight::get().reads(206_u64)) .saturating_add(T::DbWeight::get().reads((5_u64).saturating_mul(v.into()))) .saturating_add(T::DbWeight::get().reads((4_u64).saturating_mul(n.into()))) - .saturating_add(T::DbWeight::get().writes(4)) + .saturating_add(T::DbWeight::get().writes(4_u64)) .saturating_add(T::DbWeight::get().writes((3_u64).saturating_mul(v.into()))) + .saturating_add(Weight::from_proof_size(16743).saturating_mul(v.into())) + .saturating_add(Weight::from_proof_size(12947).saturating_mul(n.into())) } - // Storage: VoterList CounterForListNodes (r:1 w:0) - // Storage: VoterList ListBags (r:200 w:0) - // Storage: VoterList ListNodes (r:1500 w:0) - // Storage: Staking Nominators (r:1500 w:0) - // Storage: Staking Validators (r:500 w:0) - // Storage: Staking Bonded (r:1500 w:0) - // Storage: Staking Ledger (r:1500 w:0) - // Storage: Staking MinimumActiveStake (r:0 w:1) + /// Storage: VoterList CounterForListNodes (r:1 w:0) + /// Proof: VoterList CounterForListNodes (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) + /// Storage: VoterList ListBags (r:200 w:0) + /// Proof: VoterList ListBags (max_values: None, max_size: Some(82), added: 2557, mode: MaxEncodedLen) + /// Storage: VoterList ListNodes (r:2000 w:0) + /// Proof: VoterList ListNodes (max_values: None, max_size: Some(154), added: 2629, mode: MaxEncodedLen) + /// Storage: Staking Nominators (r:2000 w:0) + /// Proof: Staking Nominators (max_values: None, max_size: Some(558), added: 3033, mode: MaxEncodedLen) + /// Storage: Staking Validators (r:1000 w:0) + /// Proof: Staking Validators (max_values: None, max_size: Some(45), added: 2520, mode: MaxEncodedLen) + /// Storage: Staking Bonded (r:2000 w:0) + /// Proof: Staking Bonded (max_values: None, max_size: Some(72), added: 2547, mode: MaxEncodedLen) + /// Storage: Staking Ledger (r:2000 w:0) + /// Proof: Staking Ledger (max_values: None, max_size: Some(1091), added: 3566, mode: MaxEncodedLen) + /// Storage: Staking MinimumActiveStake (r:0 w:1) + /// Proof: Staking MinimumActiveStake (max_values: Some(1), max_size: Some(16), added: 511, mode: MaxEncodedLen) /// The range of component `v` is `[500, 1000]`. /// The range of component `n` is `[500, 1000]`. fn get_npos_voters(v: u32, n: u32, ) -> Weight { - // Minimum execution time: 24_913_316 nanoseconds. - Weight::from_ref_time(25_053_596_000) - // Standard Error: 324_610 - .saturating_add(Weight::from_ref_time(3_454_859).saturating_mul(v.into())) - // Standard Error: 324_610 - .saturating_add(Weight::from_ref_time(3_020_267).saturating_mul(n.into())) - .saturating_add(T::DbWeight::get().reads(201)) + // Proof Size summary in bytes: + // Measured: `3167 + v * (459 ±0) + n * (1007 ±0)` + // Estimated: `511899 + v * (14295 ±0) + n * (11775 ±0)` + // Minimum execution time: 23_373_467 nanoseconds. + Weight::from_parts(23_497_257_000, 511899) + // Standard Error: 299_205 + .saturating_add(Weight::from_ref_time(3_434_000).saturating_mul(v.into())) + // Standard Error: 299_205 + .saturating_add(Weight::from_ref_time(2_568_954).saturating_mul(n.into())) + .saturating_add(T::DbWeight::get().reads(201_u64)) .saturating_add(T::DbWeight::get().reads((5_u64).saturating_mul(v.into()))) .saturating_add(T::DbWeight::get().reads((4_u64).saturating_mul(n.into()))) - .saturating_add(T::DbWeight::get().writes(1)) + .saturating_add(T::DbWeight::get().writes(1_u64)) + .saturating_add(Weight::from_proof_size(14295).saturating_mul(v.into())) + .saturating_add(Weight::from_proof_size(11775).saturating_mul(n.into())) } - // Storage: Staking CounterForValidators (r:1 w:0) - // Storage: Staking Validators (r:501 w:0) + /// Storage: Staking CounterForValidators (r:1 w:0) + /// Proof: Staking CounterForValidators (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) + /// Storage: Staking Validators (r:1001 w:0) + /// Proof: Staking Validators (max_values: None, max_size: Some(45), added: 2520, mode: MaxEncodedLen) /// The range of component `v` is `[500, 1000]`. fn get_npos_targets(v: u32, ) -> Weight { - // Minimum execution time: 4_916_401 nanoseconds. - Weight::from_ref_time(81_160_966) - // Standard Error: 23_829 - .saturating_add(Weight::from_ref_time(9_883_413).saturating_mul(v.into())) - .saturating_add(T::DbWeight::get().reads(2)) + // Proof Size summary in bytes: + // Measured: `983 + v * (50 ±0)` + // Estimated: `3019 + v * (2520 ±0)` + // Minimum execution time: 3_882_120 nanoseconds. + Weight::from_parts(3_951_993_000, 3019) + // Standard Error: 46_729 + .saturating_add(Weight::from_ref_time(2_856_043).saturating_mul(v.into())) + .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().reads((1_u64).saturating_mul(v.into()))) + .saturating_add(Weight::from_proof_size(2520).saturating_mul(v.into())) } - // Storage: Staking MinCommission (r:0 w:1) - // Storage: Staking MinValidatorBond (r:0 w:1) - // Storage: Staking MaxValidatorsCount (r:0 w:1) - // Storage: Staking ChillThreshold (r:0 w:1) - // Storage: Staking MaxNominatorsCount (r:0 w:1) - // Storage: Staking MinNominatorBond (r:0 w:1) + /// Storage: Staking MinCommission (r:0 w:1) + /// Proof: Staking MinCommission (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) + /// Storage: Staking MinValidatorBond (r:0 w:1) + /// Proof: Staking MinValidatorBond (max_values: Some(1), max_size: Some(16), added: 511, mode: MaxEncodedLen) + /// Storage: Staking MaxValidatorsCount (r:0 w:1) + /// Proof: Staking MaxValidatorsCount (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) + /// Storage: Staking ChillThreshold (r:0 w:1) + /// Proof: Staking ChillThreshold (max_values: Some(1), max_size: Some(1), added: 496, mode: MaxEncodedLen) + /// Storage: Staking MaxNominatorsCount (r:0 w:1) + /// Proof: Staking MaxNominatorsCount (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) + /// Storage: Staking MinNominatorBond (r:0 w:1) + /// Proof: Staking MinNominatorBond (max_values: Some(1), max_size: Some(16), added: 511, mode: MaxEncodedLen) fn set_staking_configs_all_set() -> Weight { - // Minimum execution time: 10_937 nanoseconds. - Weight::from_ref_time(11_324_000) - .saturating_add(T::DbWeight::get().writes(6)) - } - // Storage: Staking MinCommission (r:0 w:1) - // Storage: Staking MinValidatorBond (r:0 w:1) - // Storage: Staking MaxValidatorsCount (r:0 w:1) - // Storage: Staking ChillThreshold (r:0 w:1) - // Storage: Staking MaxNominatorsCount (r:0 w:1) - // Storage: Staking MinNominatorBond (r:0 w:1) + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 8_427 nanoseconds. + Weight::from_ref_time(8_794_000) + .saturating_add(T::DbWeight::get().writes(6_u64)) + } + /// Storage: Staking MinCommission (r:0 w:1) + /// Proof: Staking MinCommission (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) + /// Storage: Staking MinValidatorBond (r:0 w:1) + /// Proof: Staking MinValidatorBond (max_values: Some(1), max_size: Some(16), added: 511, mode: MaxEncodedLen) + /// Storage: Staking MaxValidatorsCount (r:0 w:1) + /// Proof: Staking MaxValidatorsCount (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) + /// Storage: Staking ChillThreshold (r:0 w:1) + /// Proof: Staking ChillThreshold (max_values: Some(1), max_size: Some(1), added: 496, mode: MaxEncodedLen) + /// Storage: Staking MaxNominatorsCount (r:0 w:1) + /// Proof: Staking MaxNominatorsCount (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) + /// Storage: Staking MinNominatorBond (r:0 w:1) + /// Proof: Staking MinNominatorBond (max_values: Some(1), max_size: Some(16), added: 511, mode: MaxEncodedLen) fn set_staking_configs_all_remove() -> Weight { - // Minimum execution time: 9_424 nanoseconds. - Weight::from_ref_time(10_021_000) - .saturating_add(T::DbWeight::get().writes(6)) - } - // Storage: Staking Ledger (r:1 w:0) - // Storage: Staking Nominators (r:1 w:1) - // Storage: Staking ChillThreshold (r:1 w:0) - // Storage: Staking MaxNominatorsCount (r:1 w:0) - // Storage: Staking CounterForNominators (r:1 w:1) - // Storage: Staking MinNominatorBond (r:1 w:0) - // Storage: Staking Validators (r:1 w:0) - // Storage: VoterList ListNodes (r:2 w:2) - // Storage: VoterList ListBags (r:1 w:1) - // Storage: VoterList CounterForListNodes (r:1 w:1) + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 7_620 nanoseconds. + Weight::from_ref_time(7_901_000) + .saturating_add(T::DbWeight::get().writes(6_u64)) + } + /// Storage: Staking Ledger (r:1 w:0) + /// Proof: Staking Ledger (max_values: None, max_size: Some(1091), added: 3566, mode: MaxEncodedLen) + /// Storage: Staking Nominators (r:1 w:1) + /// Proof: Staking Nominators (max_values: None, max_size: Some(558), added: 3033, mode: MaxEncodedLen) + /// Storage: Staking ChillThreshold (r:1 w:0) + /// Proof: Staking ChillThreshold (max_values: Some(1), max_size: Some(1), added: 496, mode: MaxEncodedLen) + /// Storage: Staking MaxNominatorsCount (r:1 w:0) + /// Proof: Staking MaxNominatorsCount (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) + /// Storage: Staking CounterForNominators (r:1 w:1) + /// Proof: Staking CounterForNominators (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) + /// Storage: Staking MinNominatorBond (r:1 w:0) + /// Proof: Staking MinNominatorBond (max_values: Some(1), max_size: Some(16), added: 511, mode: MaxEncodedLen) + /// Storage: Staking Validators (r:1 w:0) + /// Proof: Staking Validators (max_values: None, max_size: Some(45), added: 2520, mode: MaxEncodedLen) + /// Storage: VoterList ListNodes (r:2 w:2) + /// Proof: VoterList ListNodes (max_values: None, max_size: Some(154), added: 2629, mode: MaxEncodedLen) + /// Storage: VoterList ListBags (r:1 w:1) + /// Proof: VoterList ListBags (max_values: None, max_size: Some(82), added: 2557, mode: MaxEncodedLen) + /// Storage: VoterList CounterForListNodes (r:1 w:1) + /// Proof: VoterList CounterForListNodes (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) fn chill_other() -> Weight { - // Minimum execution time: 84_495 nanoseconds. - Weight::from_ref_time(85_559_000) - .saturating_add(T::DbWeight::get().reads(11)) - .saturating_add(T::DbWeight::get().writes(6)) + // Proof Size summary in bytes: + // Measured: `2031` + // Estimated: `19438` + // Minimum execution time: 66_188 nanoseconds. + Weight::from_parts(66_767_000, 19438) + .saturating_add(T::DbWeight::get().reads(11_u64)) + .saturating_add(T::DbWeight::get().writes(6_u64)) } - // Storage: Staking MinCommission (r:1 w:0) - // Storage: Staking Validators (r:1 w:1) + /// Storage: Staking MinCommission (r:1 w:0) + /// Proof: Staking MinCommission (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) + /// Storage: Staking Validators (r:1 w:1) + /// Proof: Staking Validators (max_values: None, max_size: Some(45), added: 2520, mode: MaxEncodedLen) fn force_apply_min_commission() -> Weight { - // Minimum execution time: 20_385 nanoseconds. - Weight::from_ref_time(20_824_000) - .saturating_add(T::DbWeight::get().reads(2)) - .saturating_add(T::DbWeight::get().writes(1)) + // Proof Size summary in bytes: + // Measured: `694` + // Estimated: `3019` + // Minimum execution time: 14_703 nanoseconds. + Weight::from_parts(15_031_000, 3019) + .saturating_add(T::DbWeight::get().reads(2_u64)) + .saturating_add(T::DbWeight::get().writes(1_u64)) } - // Storage: Staking MinCommission (r:0 w:1) + /// Storage: Staking MinCommission (r:0 w:1) + /// Proof: Staking MinCommission (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) fn set_min_commission() -> Weight { - // Minimum execution time: 6_995 nanoseconds. - Weight::from_ref_time(7_213_000) - .saturating_add(T::DbWeight::get().writes(1)) + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 4_518 nanoseconds. + Weight::from_ref_time(4_656_000) + .saturating_add(T::DbWeight::get().writes(1_u64)) } } // For backwards compatibility and tests impl WeightInfo for () { - // Storage: Staking Bonded (r:1 w:1) - // Storage: Staking Ledger (r:1 w:1) - // Storage: Staking CurrentEra (r:1 w:0) - // Storage: Balances Locks (r:1 w:1) - // Storage: Staking Payee (r:0 w:1) + /// Storage: Staking Bonded (r:1 w:1) + /// Proof: Staking Bonded (max_values: None, max_size: Some(72), added: 2547, mode: MaxEncodedLen) + /// Storage: Staking Ledger (r:1 w:1) + /// Proof: Staking Ledger (max_values: None, max_size: Some(1091), added: 3566, mode: MaxEncodedLen) + /// Storage: Staking CurrentEra (r:1 w:0) + /// Proof: Staking CurrentEra (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) + /// Storage: Balances Locks (r:1 w:1) + /// Proof: Balances Locks (max_values: None, max_size: Some(1299), added: 3774, mode: MaxEncodedLen) + /// Storage: Staking Payee (r:0 w:1) + /// Proof: Staking Payee (max_values: None, max_size: Some(73), added: 2548, mode: MaxEncodedLen) fn bond() -> Weight { - // Minimum execution time: 54_884 nanoseconds. - Weight::from_ref_time(55_487_000) - .saturating_add(RocksDbWeight::get().reads(4)) - .saturating_add(RocksDbWeight::get().writes(4)) - } - // Storage: Staking Bonded (r:1 w:0) - // Storage: Staking Ledger (r:1 w:1) - // Storage: Balances Locks (r:1 w:1) - // Storage: VoterList ListNodes (r:3 w:3) - // Storage: VoterList ListBags (r:2 w:2) + // Proof Size summary in bytes: + // Measured: `1079` + // Estimated: `10386` + // Minimum execution time: 40_015 nanoseconds. + Weight::from_parts(40_601_000, 10386) + .saturating_add(RocksDbWeight::get().reads(4_u64)) + .saturating_add(RocksDbWeight::get().writes(4_u64)) + } + /// Storage: Staking Bonded (r:1 w:0) + /// Proof: Staking Bonded (max_values: None, max_size: Some(72), added: 2547, mode: MaxEncodedLen) + /// Storage: Staking Ledger (r:1 w:1) + /// Proof: Staking Ledger (max_values: None, max_size: Some(1091), added: 3566, mode: MaxEncodedLen) + /// Storage: Balances Locks (r:1 w:1) + /// Proof: Balances Locks (max_values: None, max_size: Some(1299), added: 3774, mode: MaxEncodedLen) + /// Storage: VoterList ListNodes (r:3 w:3) + /// Proof: VoterList ListNodes (max_values: None, max_size: Some(154), added: 2629, mode: MaxEncodedLen) + /// Storage: VoterList ListBags (r:2 w:2) + /// Proof: VoterList ListBags (max_values: None, max_size: Some(82), added: 2557, mode: MaxEncodedLen) fn bond_extra() -> Weight { - // Minimum execution time: 95_115 nanoseconds. - Weight::from_ref_time(96_213_000) - .saturating_add(RocksDbWeight::get().reads(8)) - .saturating_add(RocksDbWeight::get().writes(7)) - } - // Storage: Staking Ledger (r:1 w:1) - // Storage: Staking Nominators (r:1 w:0) - // Storage: Staking MinNominatorBond (r:1 w:0) - // Storage: Staking CurrentEra (r:1 w:0) - // Storage: Balances Locks (r:1 w:1) - // Storage: System Account (r:1 w:1) - // Storage: VoterList ListNodes (r:3 w:3) - // Storage: Staking Bonded (r:1 w:0) - // Storage: VoterList ListBags (r:2 w:2) + // Proof Size summary in bytes: + // Measured: `2252` + // Estimated: `22888` + // Minimum execution time: 74_781 nanoseconds. + Weight::from_parts(75_188_000, 22888) + .saturating_add(RocksDbWeight::get().reads(8_u64)) + .saturating_add(RocksDbWeight::get().writes(7_u64)) + } + /// Storage: Staking Ledger (r:1 w:1) + /// Proof: Staking Ledger (max_values: None, max_size: Some(1091), added: 3566, mode: MaxEncodedLen) + /// Storage: Staking Nominators (r:1 w:0) + /// Proof: Staking Nominators (max_values: None, max_size: Some(558), added: 3033, mode: MaxEncodedLen) + /// Storage: Staking MinNominatorBond (r:1 w:0) + /// Proof: Staking MinNominatorBond (max_values: Some(1), max_size: Some(16), added: 511, mode: MaxEncodedLen) + /// Storage: Staking CurrentEra (r:1 w:0) + /// Proof: Staking CurrentEra (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) + /// Storage: Balances Locks (r:1 w:1) + /// Proof: Balances Locks (max_values: None, max_size: Some(1299), added: 3774, mode: MaxEncodedLen) + /// Storage: System Account (r:1 w:1) + /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) + /// Storage: VoterList ListNodes (r:3 w:3) + /// Proof: VoterList ListNodes (max_values: None, max_size: Some(154), added: 2629, mode: MaxEncodedLen) + /// Storage: Staking Bonded (r:1 w:0) + /// Proof: Staking Bonded (max_values: None, max_size: Some(72), added: 2547, mode: MaxEncodedLen) + /// Storage: VoterList ListBags (r:2 w:2) + /// Proof: VoterList ListBags (max_values: None, max_size: Some(82), added: 2557, mode: MaxEncodedLen) fn unbond() -> Weight { - // Minimum execution time: 102_031 nanoseconds. - Weight::from_ref_time(102_842_000) - .saturating_add(RocksDbWeight::get().reads(12)) - .saturating_add(RocksDbWeight::get().writes(8)) - } - // Storage: Staking Ledger (r:1 w:1) - // Storage: Staking CurrentEra (r:1 w:0) - // Storage: Balances Locks (r:1 w:1) - // Storage: System Account (r:1 w:1) + // Proof Size summary in bytes: + // Measured: `2457` + // Estimated: `29534` + // Minimum execution time: 81_299 nanoseconds. + Weight::from_parts(82_242_000, 29534) + .saturating_add(RocksDbWeight::get().reads(12_u64)) + .saturating_add(RocksDbWeight::get().writes(8_u64)) + } + /// Storage: Staking Ledger (r:1 w:1) + /// Proof: Staking Ledger (max_values: None, max_size: Some(1091), added: 3566, mode: MaxEncodedLen) + /// Storage: Staking CurrentEra (r:1 w:0) + /// Proof: Staking CurrentEra (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) + /// Storage: Balances Locks (r:1 w:1) + /// Proof: Balances Locks (max_values: None, max_size: Some(1299), added: 3774, mode: MaxEncodedLen) + /// Storage: System Account (r:1 w:1) + /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) /// The range of component `s` is `[0, 100]`. fn withdraw_unbonded_update(s: u32, ) -> Weight { - // Minimum execution time: 46_569 nanoseconds. - Weight::from_ref_time(48_034_493) - // Standard Error: 654 - .saturating_add(Weight::from_ref_time(63_628).saturating_mul(s.into())) - .saturating_add(RocksDbWeight::get().reads(4)) - .saturating_add(RocksDbWeight::get().writes(3)) - } - // Storage: Staking Ledger (r:1 w:1) - // Storage: Staking CurrentEra (r:1 w:0) - // Storage: Staking Bonded (r:1 w:1) - // Storage: Staking SlashingSpans (r:1 w:0) - // Storage: Staking Validators (r:1 w:0) - // Storage: Staking Nominators (r:1 w:1) - // Storage: Staking CounterForNominators (r:1 w:1) - // Storage: VoterList ListNodes (r:2 w:2) - // Storage: VoterList ListBags (r:1 w:1) - // Storage: VoterList CounterForListNodes (r:1 w:1) - // Storage: System Account (r:1 w:1) - // Storage: Balances Locks (r:1 w:1) - // Storage: Staking Payee (r:0 w:1) - // Storage: Staking SpanSlash (r:0 w:2) + // Proof Size summary in bytes: + // Measured: `1085` + // Estimated: `10442` + // Minimum execution time: 31_479 nanoseconds. + Weight::from_parts(32_410_035, 10442) + // Standard Error: 313 + .saturating_add(Weight::from_ref_time(9_090).saturating_mul(s.into())) + .saturating_add(RocksDbWeight::get().reads(4_u64)) + .saturating_add(RocksDbWeight::get().writes(3_u64)) + } + /// Storage: Staking Ledger (r:1 w:1) + /// Proof: Staking Ledger (max_values: None, max_size: Some(1091), added: 3566, mode: MaxEncodedLen) + /// Storage: Staking CurrentEra (r:1 w:0) + /// Proof: Staking CurrentEra (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) + /// Storage: Staking Bonded (r:1 w:1) + /// Proof: Staking Bonded (max_values: None, max_size: Some(72), added: 2547, mode: MaxEncodedLen) + /// Storage: Staking SlashingSpans (r:1 w:1) + /// Proof Skipped: Staking SlashingSpans (max_values: None, max_size: None, mode: Measured) + /// Storage: Staking Validators (r:1 w:0) + /// Proof: Staking Validators (max_values: None, max_size: Some(45), added: 2520, mode: MaxEncodedLen) + /// Storage: Staking Nominators (r:1 w:1) + /// Proof: Staking Nominators (max_values: None, max_size: Some(558), added: 3033, mode: MaxEncodedLen) + /// Storage: Staking CounterForNominators (r:1 w:1) + /// Proof: Staking CounterForNominators (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) + /// Storage: VoterList ListNodes (r:2 w:2) + /// Proof: VoterList ListNodes (max_values: None, max_size: Some(154), added: 2629, mode: MaxEncodedLen) + /// Storage: VoterList ListBags (r:1 w:1) + /// Proof: VoterList ListBags (max_values: None, max_size: Some(82), added: 2557, mode: MaxEncodedLen) + /// Storage: VoterList CounterForListNodes (r:1 w:1) + /// Proof: VoterList CounterForListNodes (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) + /// Storage: System Account (r:1 w:1) + /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) + /// Storage: Balances Locks (r:1 w:1) + /// Proof: Balances Locks (max_values: None, max_size: Some(1299), added: 3774, mode: MaxEncodedLen) + /// Storage: Staking Payee (r:0 w:1) + /// Proof: Staking Payee (max_values: None, max_size: Some(73), added: 2548, mode: MaxEncodedLen) + /// Storage: Staking SpanSlash (r:0 w:100) + /// Proof: Staking SpanSlash (max_values: None, max_size: Some(76), added: 2551, mode: MaxEncodedLen) /// The range of component `s` is `[0, 100]`. fn withdraw_unbonded_kill(s: u32, ) -> Weight { - // Minimum execution time: 90_154 nanoseconds. - Weight::from_ref_time(95_725_631) - // Standard Error: 2_491 - .saturating_add(Weight::from_ref_time(1_110_795).saturating_mul(s.into())) - .saturating_add(RocksDbWeight::get().reads(13)) - .saturating_add(RocksDbWeight::get().writes(12)) + // Proof Size summary in bytes: + // Measured: `2486 + s * (4 ±0)` + // Estimated: `32303 + s * (4 ±0)` + // Minimum execution time: 71_968 nanoseconds. + Weight::from_parts(76_631_804, 32303) + // Standard Error: 1_613 + .saturating_add(Weight::from_ref_time(1_058_968).saturating_mul(s.into())) + .saturating_add(RocksDbWeight::get().reads(13_u64)) + .saturating_add(RocksDbWeight::get().writes(12_u64)) .saturating_add(RocksDbWeight::get().writes((1_u64).saturating_mul(s.into()))) + .saturating_add(Weight::from_proof_size(4).saturating_mul(s.into())) } - // Storage: Staking Ledger (r:1 w:0) - // Storage: Staking MinValidatorBond (r:1 w:0) - // Storage: Staking MinCommission (r:1 w:0) - // Storage: Staking Validators (r:1 w:1) - // Storage: Staking MaxValidatorsCount (r:1 w:0) - // Storage: Staking Nominators (r:1 w:0) - // Storage: Staking Bonded (r:1 w:0) - // Storage: VoterList ListNodes (r:1 w:1) - // Storage: VoterList ListBags (r:1 w:1) - // Storage: VoterList CounterForListNodes (r:1 w:1) - // Storage: Staking CounterForValidators (r:1 w:1) + /// Storage: Staking Ledger (r:1 w:0) + /// Proof: Staking Ledger (max_values: None, max_size: Some(1091), added: 3566, mode: MaxEncodedLen) + /// Storage: Staking MinValidatorBond (r:1 w:0) + /// Proof: Staking MinValidatorBond (max_values: Some(1), max_size: Some(16), added: 511, mode: MaxEncodedLen) + /// Storage: Staking MinCommission (r:1 w:0) + /// Proof: Staking MinCommission (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) + /// Storage: Staking Validators (r:1 w:1) + /// Proof: Staking Validators (max_values: None, max_size: Some(45), added: 2520, mode: MaxEncodedLen) + /// Storage: Staking MaxValidatorsCount (r:1 w:0) + /// Proof: Staking MaxValidatorsCount (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) + /// Storage: Staking Nominators (r:1 w:0) + /// Proof: Staking Nominators (max_values: None, max_size: Some(558), added: 3033, mode: MaxEncodedLen) + /// Storage: Staking Bonded (r:1 w:0) + /// Proof: Staking Bonded (max_values: None, max_size: Some(72), added: 2547, mode: MaxEncodedLen) + /// Storage: VoterList ListNodes (r:1 w:1) + /// Proof: VoterList ListNodes (max_values: None, max_size: Some(154), added: 2629, mode: MaxEncodedLen) + /// Storage: VoterList ListBags (r:1 w:1) + /// Proof: VoterList ListBags (max_values: None, max_size: Some(82), added: 2557, mode: MaxEncodedLen) + /// Storage: VoterList CounterForListNodes (r:1 w:1) + /// Proof: VoterList CounterForListNodes (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) + /// Storage: Staking CounterForValidators (r:1 w:1) + /// Proof: Staking CounterForValidators (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) fn validate() -> Weight { - // Minimum execution time: 67_978 nanoseconds. - Weight::from_ref_time(69_153_000) - .saturating_add(RocksDbWeight::get().reads(11)) - .saturating_add(RocksDbWeight::get().writes(5)) + // Proof Size summary in bytes: + // Measured: `1446` + // Estimated: `19359` + // Minimum execution time: 51_963 nanoseconds. + Weight::from_parts(52_418_000, 19359) + .saturating_add(RocksDbWeight::get().reads(11_u64)) + .saturating_add(RocksDbWeight::get().writes(5_u64)) } - // Storage: Staking Ledger (r:1 w:0) - // Storage: Staking Nominators (r:1 w:1) + /// Storage: Staking Ledger (r:1 w:0) + /// Proof: Staking Ledger (max_values: None, max_size: Some(1091), added: 3566, mode: MaxEncodedLen) + /// Storage: Staking Nominators (r:128 w:128) + /// Proof: Staking Nominators (max_values: None, max_size: Some(558), added: 3033, mode: MaxEncodedLen) /// The range of component `k` is `[1, 128]`. fn kick(k: u32, ) -> Weight { - // Minimum execution time: 45_328 nanoseconds. - Weight::from_ref_time(47_719_103) - // Standard Error: 14_458 - .saturating_add(Weight::from_ref_time(6_999_252).saturating_mul(k.into())) - .saturating_add(RocksDbWeight::get().reads(1)) + // Proof Size summary in bytes: + // Measured: `1292 + k * (601 ±0)` + // Estimated: `3566 + k * (3033 ±0)` + // Minimum execution time: 25_685 nanoseconds. + Weight::from_parts(25_290_286, 3566) + // Standard Error: 5_164 + .saturating_add(Weight::from_ref_time(6_445_608).saturating_mul(k.into())) + .saturating_add(RocksDbWeight::get().reads(1_u64)) .saturating_add(RocksDbWeight::get().reads((1_u64).saturating_mul(k.into()))) .saturating_add(RocksDbWeight::get().writes((1_u64).saturating_mul(k.into()))) + .saturating_add(Weight::from_proof_size(3033).saturating_mul(k.into())) } - // Storage: Staking Ledger (r:1 w:0) - // Storage: Staking MinNominatorBond (r:1 w:0) - // Storage: Staking Nominators (r:1 w:1) - // Storage: Staking MaxNominatorsCount (r:1 w:0) - // Storage: Staking Validators (r:2 w:0) - // Storage: Staking CurrentEra (r:1 w:0) - // Storage: Staking Bonded (r:1 w:0) - // Storage: VoterList ListNodes (r:2 w:2) - // Storage: VoterList ListBags (r:1 w:1) - // Storage: VoterList CounterForListNodes (r:1 w:1) - // Storage: Staking CounterForNominators (r:1 w:1) + /// Storage: Staking Ledger (r:1 w:0) + /// Proof: Staking Ledger (max_values: None, max_size: Some(1091), added: 3566, mode: MaxEncodedLen) + /// Storage: Staking MinNominatorBond (r:1 w:0) + /// Proof: Staking MinNominatorBond (max_values: Some(1), max_size: Some(16), added: 511, mode: MaxEncodedLen) + /// Storage: Staking Nominators (r:1 w:1) + /// Proof: Staking Nominators (max_values: None, max_size: Some(558), added: 3033, mode: MaxEncodedLen) + /// Storage: Staking MaxNominatorsCount (r:1 w:0) + /// Proof: Staking MaxNominatorsCount (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) + /// Storage: Staking Validators (r:17 w:0) + /// Proof: Staking Validators (max_values: None, max_size: Some(45), added: 2520, mode: MaxEncodedLen) + /// Storage: Staking CurrentEra (r:1 w:0) + /// Proof: Staking CurrentEra (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) + /// Storage: Staking Bonded (r:1 w:0) + /// Proof: Staking Bonded (max_values: None, max_size: Some(72), added: 2547, mode: MaxEncodedLen) + /// Storage: VoterList ListNodes (r:2 w:2) + /// Proof: VoterList ListNodes (max_values: None, max_size: Some(154), added: 2629, mode: MaxEncodedLen) + /// Storage: VoterList ListBags (r:1 w:1) + /// Proof: VoterList ListBags (max_values: None, max_size: Some(82), added: 2557, mode: MaxEncodedLen) + /// Storage: VoterList CounterForListNodes (r:1 w:1) + /// Proof: VoterList CounterForListNodes (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) + /// Storage: Staking CounterForNominators (r:1 w:1) + /// Proof: Staking CounterForNominators (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) /// The range of component `n` is `[1, 16]`. fn nominate(n: u32, ) -> Weight { - // Minimum execution time: 74_650 nanoseconds. - Weight::from_ref_time(74_350_075) - // Standard Error: 10_527 - .saturating_add(Weight::from_ref_time(2_878_737).saturating_mul(n.into())) - .saturating_add(RocksDbWeight::get().reads(12)) + // Proof Size summary in bytes: + // Measured: `1984 + n * (105 ±0)` + // Estimated: `21988 + n * (2520 ±0)` + // Minimum execution time: 59_542 nanoseconds. + Weight::from_parts(57_558_678, 21988) + // Standard Error: 10_364 + .saturating_add(Weight::from_ref_time(2_759_713).saturating_mul(n.into())) + .saturating_add(RocksDbWeight::get().reads(12_u64)) .saturating_add(RocksDbWeight::get().reads((1_u64).saturating_mul(n.into()))) - .saturating_add(RocksDbWeight::get().writes(6)) - } - // Storage: Staking Ledger (r:1 w:0) - // Storage: Staking Validators (r:1 w:0) - // Storage: Staking Nominators (r:1 w:1) - // Storage: Staking CounterForNominators (r:1 w:1) - // Storage: VoterList ListNodes (r:2 w:2) - // Storage: VoterList ListBags (r:1 w:1) - // Storage: VoterList CounterForListNodes (r:1 w:1) + .saturating_add(RocksDbWeight::get().writes(6_u64)) + .saturating_add(Weight::from_proof_size(2520).saturating_mul(n.into())) + } + /// Storage: Staking Ledger (r:1 w:0) + /// Proof: Staking Ledger (max_values: None, max_size: Some(1091), added: 3566, mode: MaxEncodedLen) + /// Storage: Staking Validators (r:1 w:0) + /// Proof: Staking Validators (max_values: None, max_size: Some(45), added: 2520, mode: MaxEncodedLen) + /// Storage: Staking Nominators (r:1 w:1) + /// Proof: Staking Nominators (max_values: None, max_size: Some(558), added: 3033, mode: MaxEncodedLen) + /// Storage: Staking CounterForNominators (r:1 w:1) + /// Proof: Staking CounterForNominators (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) + /// Storage: VoterList ListNodes (r:2 w:2) + /// Proof: VoterList ListNodes (max_values: None, max_size: Some(154), added: 2629, mode: MaxEncodedLen) + /// Storage: VoterList ListBags (r:1 w:1) + /// Proof: VoterList ListBags (max_values: None, max_size: Some(82), added: 2557, mode: MaxEncodedLen) + /// Storage: VoterList CounterForListNodes (r:1 w:1) + /// Proof: VoterList CounterForListNodes (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) fn chill() -> Weight { - // Minimum execution time: 67_790 nanoseconds. - Weight::from_ref_time(68_738_000) - .saturating_add(RocksDbWeight::get().reads(8)) - .saturating_add(RocksDbWeight::get().writes(6)) + // Proof Size summary in bytes: + // Measured: `1876` + // Estimated: `17932` + // Minimum execution time: 52_132 nanoseconds. + Weight::from_parts(52_648_000, 17932) + .saturating_add(RocksDbWeight::get().reads(8_u64)) + .saturating_add(RocksDbWeight::get().writes(6_u64)) } - // Storage: Staking Ledger (r:1 w:0) - // Storage: Staking Payee (r:0 w:1) + /// Storage: Staking Ledger (r:1 w:0) + /// Proof: Staking Ledger (max_values: None, max_size: Some(1091), added: 3566, mode: MaxEncodedLen) + /// Storage: Staking Payee (r:0 w:1) + /// Proof: Staking Payee (max_values: None, max_size: Some(73), added: 2548, mode: MaxEncodedLen) fn set_payee() -> Weight { - // Minimum execution time: 19_237 nanoseconds. - Weight::from_ref_time(19_534_000) - .saturating_add(RocksDbWeight::get().reads(1)) - .saturating_add(RocksDbWeight::get().writes(1)) + // Proof Size summary in bytes: + // Measured: `840` + // Estimated: `3566` + // Minimum execution time: 13_399 nanoseconds. + Weight::from_parts(13_567_000, 3566) + .saturating_add(RocksDbWeight::get().reads(1_u64)) + .saturating_add(RocksDbWeight::get().writes(1_u64)) } - // Storage: Staking Bonded (r:1 w:1) - // Storage: Staking Ledger (r:2 w:2) + /// Storage: Staking Bonded (r:1 w:1) + /// Proof: Staking Bonded (max_values: None, max_size: Some(72), added: 2547, mode: MaxEncodedLen) + /// Storage: Staking Ledger (r:2 w:2) + /// Proof: Staking Ledger (max_values: None, max_size: Some(1091), added: 3566, mode: MaxEncodedLen) fn set_controller() -> Weight { - // Minimum execution time: 27_288 nanoseconds. - Weight::from_ref_time(27_667_000) - .saturating_add(RocksDbWeight::get().reads(3)) - .saturating_add(RocksDbWeight::get().writes(3)) + // Proof Size summary in bytes: + // Measured: `939` + // Estimated: `9679` + // Minimum execution time: 20_425 nanoseconds. + Weight::from_parts(20_713_000, 9679) + .saturating_add(RocksDbWeight::get().reads(3_u64)) + .saturating_add(RocksDbWeight::get().writes(3_u64)) } - // Storage: Staking ValidatorCount (r:0 w:1) + /// Storage: Staking ValidatorCount (r:0 w:1) + /// Proof: Staking ValidatorCount (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) fn set_validator_count() -> Weight { - // Minimum execution time: 5_155 nanoseconds. - Weight::from_ref_time(5_464_000) - .saturating_add(RocksDbWeight::get().writes(1)) + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 3_069 nanoseconds. + Weight::from_ref_time(3_176_000) + .saturating_add(RocksDbWeight::get().writes(1_u64)) } - // Storage: Staking ForceEra (r:0 w:1) + /// Storage: Staking ForceEra (r:0 w:1) + /// Proof: Staking ForceEra (max_values: Some(1), max_size: Some(1), added: 496, mode: MaxEncodedLen) fn force_no_eras() -> Weight { - // Minimum execution time: 5_405 nanoseconds. - Weight::from_ref_time(5_670_000) - .saturating_add(RocksDbWeight::get().writes(1)) + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 11_386 nanoseconds. + Weight::from_ref_time(11_672_000) + .saturating_add(RocksDbWeight::get().writes(1_u64)) } - // Storage: Staking ForceEra (r:0 w:1) + /// Storage: Staking ForceEra (r:0 w:1) + /// Proof: Staking ForceEra (max_values: Some(1), max_size: Some(1), added: 496, mode: MaxEncodedLen) fn force_new_era() -> Weight { - // Minimum execution time: 5_459 nanoseconds. - Weight::from_ref_time(5_616_000) - .saturating_add(RocksDbWeight::get().writes(1)) + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 11_591 nanoseconds. + Weight::from_ref_time(11_799_000) + .saturating_add(RocksDbWeight::get().writes(1_u64)) } - // Storage: Staking ForceEra (r:0 w:1) + /// Storage: Staking ForceEra (r:0 w:1) + /// Proof: Staking ForceEra (max_values: Some(1), max_size: Some(1), added: 496, mode: MaxEncodedLen) fn force_new_era_always() -> Weight { - // Minimum execution time: 5_476 nanoseconds. - Weight::from_ref_time(5_692_000) - .saturating_add(RocksDbWeight::get().writes(1)) + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 11_553 nanoseconds. + Weight::from_ref_time(11_871_000) + .saturating_add(RocksDbWeight::get().writes(1_u64)) } - // Storage: Staking Invulnerables (r:0 w:1) + /// Storage: Staking Invulnerables (r:0 w:1) + /// Proof Skipped: Staking Invulnerables (max_values: Some(1), max_size: None, mode: Measured) /// The range of component `v` is `[0, 1000]`. fn set_invulnerables(v: u32, ) -> Weight { - // Minimum execution time: 5_544 nanoseconds. - Weight::from_ref_time(6_513_190) - // Standard Error: 76 - .saturating_add(Weight::from_ref_time(9_975).saturating_mul(v.into())) - .saturating_add(RocksDbWeight::get().writes(1)) - } - // Storage: Staking Bonded (r:1 w:1) - // Storage: Staking SlashingSpans (r:1 w:0) - // Storage: Staking Validators (r:1 w:0) - // Storage: Staking Nominators (r:1 w:1) - // Storage: Staking CounterForNominators (r:1 w:1) - // Storage: VoterList ListNodes (r:2 w:2) - // Storage: VoterList ListBags (r:1 w:1) - // Storage: VoterList CounterForListNodes (r:1 w:1) - // Storage: System Account (r:1 w:1) - // Storage: Balances Locks (r:1 w:1) - // Storage: Staking Ledger (r:0 w:1) - // Storage: Staking Payee (r:0 w:1) - // Storage: Staking SpanSlash (r:0 w:2) + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 3_292 nanoseconds. + Weight::from_ref_time(3_754_352) + // Standard Error: 40 + .saturating_add(Weight::from_ref_time(9_838).saturating_mul(v.into())) + .saturating_add(RocksDbWeight::get().writes(1_u64)) + } + /// Storage: Staking Bonded (r:1 w:1) + /// Proof: Staking Bonded (max_values: None, max_size: Some(72), added: 2547, mode: MaxEncodedLen) + /// Storage: Staking SlashingSpans (r:1 w:1) + /// Proof Skipped: Staking SlashingSpans (max_values: None, max_size: None, mode: Measured) + /// Storage: Staking Validators (r:1 w:0) + /// Proof: Staking Validators (max_values: None, max_size: Some(45), added: 2520, mode: MaxEncodedLen) + /// Storage: Staking Nominators (r:1 w:1) + /// Proof: Staking Nominators (max_values: None, max_size: Some(558), added: 3033, mode: MaxEncodedLen) + /// Storage: Staking CounterForNominators (r:1 w:1) + /// Proof: Staking CounterForNominators (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) + /// Storage: VoterList ListNodes (r:2 w:2) + /// Proof: VoterList ListNodes (max_values: None, max_size: Some(154), added: 2629, mode: MaxEncodedLen) + /// Storage: VoterList ListBags (r:1 w:1) + /// Proof: VoterList ListBags (max_values: None, max_size: Some(82), added: 2557, mode: MaxEncodedLen) + /// Storage: VoterList CounterForListNodes (r:1 w:1) + /// Proof: VoterList CounterForListNodes (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) + /// Storage: System Account (r:1 w:1) + /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) + /// Storage: Balances Locks (r:1 w:1) + /// Proof: Balances Locks (max_values: None, max_size: Some(1299), added: 3774, mode: MaxEncodedLen) + /// Storage: Staking Ledger (r:0 w:1) + /// Proof: Staking Ledger (max_values: None, max_size: Some(1091), added: 3566, mode: MaxEncodedLen) + /// Storage: Staking Payee (r:0 w:1) + /// Proof: Staking Payee (max_values: None, max_size: Some(73), added: 2548, mode: MaxEncodedLen) + /// Storage: Staking SpanSlash (r:0 w:100) + /// Proof: Staking SpanSlash (max_values: None, max_size: Some(76), added: 2551, mode: MaxEncodedLen) /// The range of component `s` is `[0, 100]`. fn force_unstake(s: u32, ) -> Weight { - // Minimum execution time: 82_414 nanoseconds. - Weight::from_ref_time(88_511_246) - // Standard Error: 2_622 - .saturating_add(Weight::from_ref_time(1_131_814).saturating_mul(s.into())) - .saturating_add(RocksDbWeight::get().reads(11)) - .saturating_add(RocksDbWeight::get().writes(12)) + // Proof Size summary in bytes: + // Measured: `2178 + s * (4 ±0)` + // Estimated: `27930 + s * (4 ±0)` + // Minimum execution time: 65_307 nanoseconds. + Weight::from_parts(70_227_980, 27930) + // Standard Error: 2_113 + .saturating_add(Weight::from_ref_time(1_059_856).saturating_mul(s.into())) + .saturating_add(RocksDbWeight::get().reads(11_u64)) + .saturating_add(RocksDbWeight::get().writes(12_u64)) .saturating_add(RocksDbWeight::get().writes((1_u64).saturating_mul(s.into()))) + .saturating_add(Weight::from_proof_size(4).saturating_mul(s.into())) } - // Storage: Staking UnappliedSlashes (r:1 w:1) + /// Storage: Staking UnappliedSlashes (r:1 w:1) + /// Proof Skipped: Staking UnappliedSlashes (max_values: None, max_size: None, mode: Measured) /// The range of component `s` is `[1, 1000]`. fn cancel_deferred_slash(s: u32, ) -> Weight { - // Minimum execution time: 94_197 nanoseconds. - Weight::from_ref_time(903_418_326) - // Standard Error: 59_354 - .saturating_add(Weight::from_ref_time(4_948_354).saturating_mul(s.into())) - .saturating_add(RocksDbWeight::get().reads(1)) - .saturating_add(RocksDbWeight::get().writes(1)) - } - // Storage: Staking CurrentEra (r:1 w:0) - // Storage: Staking ErasValidatorReward (r:1 w:0) - // Storage: Staking Bonded (r:1 w:0) - // Storage: Staking Ledger (r:1 w:1) - // Storage: Staking ErasStakersClipped (r:1 w:0) - // Storage: Staking ErasRewardPoints (r:1 w:0) - // Storage: Staking ErasValidatorPrefs (r:1 w:0) - // Storage: Staking Payee (r:1 w:0) - // Storage: System Account (r:1 w:1) + // Proof Size summary in bytes: + // Measured: `66671` + // Estimated: `69146` + // Minimum execution time: 89_123 nanoseconds. + Weight::from_parts(890_989_741, 69146) + // Standard Error: 58_282 + .saturating_add(Weight::from_ref_time(4_920_413).saturating_mul(s.into())) + .saturating_add(RocksDbWeight::get().reads(1_u64)) + .saturating_add(RocksDbWeight::get().writes(1_u64)) + } + /// Storage: Staking CurrentEra (r:1 w:0) + /// Proof: Staking CurrentEra (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) + /// Storage: Staking ErasValidatorReward (r:1 w:0) + /// Proof: Staking ErasValidatorReward (max_values: None, max_size: Some(28), added: 2503, mode: MaxEncodedLen) + /// Storage: Staking Bonded (r:257 w:0) + /// Proof: Staking Bonded (max_values: None, max_size: Some(72), added: 2547, mode: MaxEncodedLen) + /// Storage: Staking Ledger (r:1 w:1) + /// Proof: Staking Ledger (max_values: None, max_size: Some(1091), added: 3566, mode: MaxEncodedLen) + /// Storage: Staking ErasStakersClipped (r:1 w:0) + /// Proof Skipped: Staking ErasStakersClipped (max_values: None, max_size: None, mode: Measured) + /// Storage: Staking ErasRewardPoints (r:1 w:0) + /// Proof Skipped: Staking ErasRewardPoints (max_values: None, max_size: None, mode: Measured) + /// Storage: Staking ErasValidatorPrefs (r:1 w:0) + /// Proof: Staking ErasValidatorPrefs (max_values: None, max_size: Some(57), added: 2532, mode: MaxEncodedLen) + /// Storage: Staking Payee (r:257 w:0) + /// Proof: Staking Payee (max_values: None, max_size: Some(73), added: 2548, mode: MaxEncodedLen) + /// Storage: System Account (r:257 w:257) + /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) /// The range of component `n` is `[0, 256]`. fn payout_stakers_dead_controller(n: u32, ) -> Weight { - // Minimum execution time: 133_065 nanoseconds. - Weight::from_ref_time(197_555_906) - // Standard Error: 19_561 - .saturating_add(Weight::from_ref_time(22_683_426).saturating_mul(n.into())) - .saturating_add(RocksDbWeight::get().reads(9)) + // Proof Size summary in bytes: + // Measured: `20345 + n * (143 ±0)` + // Estimated: `54756 + n * (8024 ±1)` + // Minimum execution time: 73_652 nanoseconds. + Weight::from_parts(127_839_483, 54756) + // Standard Error: 14_195 + .saturating_add(Weight::from_ref_time(21_932_079).saturating_mul(n.into())) + .saturating_add(RocksDbWeight::get().reads(9_u64)) .saturating_add(RocksDbWeight::get().reads((3_u64).saturating_mul(n.into()))) - .saturating_add(RocksDbWeight::get().writes(2)) + .saturating_add(RocksDbWeight::get().writes(2_u64)) .saturating_add(RocksDbWeight::get().writes((1_u64).saturating_mul(n.into()))) + .saturating_add(Weight::from_proof_size(8024).saturating_mul(n.into())) } - // Storage: Staking CurrentEra (r:1 w:0) - // Storage: Staking ErasValidatorReward (r:1 w:0) - // Storage: Staking Bonded (r:1 w:0) - // Storage: Staking Ledger (r:1 w:1) - // Storage: Staking ErasStakersClipped (r:1 w:0) - // Storage: Staking ErasRewardPoints (r:1 w:0) - // Storage: Staking ErasValidatorPrefs (r:1 w:0) - // Storage: Staking Payee (r:1 w:0) - // Storage: System Account (r:1 w:1) - // Storage: Balances Locks (r:1 w:1) + /// Storage: Staking CurrentEra (r:1 w:0) + /// Proof: Staking CurrentEra (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) + /// Storage: Staking ErasValidatorReward (r:1 w:0) + /// Proof: Staking ErasValidatorReward (max_values: None, max_size: Some(28), added: 2503, mode: MaxEncodedLen) + /// Storage: Staking Bonded (r:257 w:0) + /// Proof: Staking Bonded (max_values: None, max_size: Some(72), added: 2547, mode: MaxEncodedLen) + /// Storage: Staking Ledger (r:257 w:257) + /// Proof: Staking Ledger (max_values: None, max_size: Some(1091), added: 3566, mode: MaxEncodedLen) + /// Storage: Staking ErasStakersClipped (r:1 w:0) + /// Proof Skipped: Staking ErasStakersClipped (max_values: None, max_size: None, mode: Measured) + /// Storage: Staking ErasRewardPoints (r:1 w:0) + /// Proof Skipped: Staking ErasRewardPoints (max_values: None, max_size: None, mode: Measured) + /// Storage: Staking ErasValidatorPrefs (r:1 w:0) + /// Proof: Staking ErasValidatorPrefs (max_values: None, max_size: Some(57), added: 2532, mode: MaxEncodedLen) + /// Storage: Staking Payee (r:257 w:0) + /// Proof: Staking Payee (max_values: None, max_size: Some(73), added: 2548, mode: MaxEncodedLen) + /// Storage: System Account (r:257 w:257) + /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) + /// Storage: Balances Locks (r:257 w:257) + /// Proof: Balances Locks (max_values: None, max_size: Some(1299), added: 3774, mode: MaxEncodedLen) /// The range of component `n` is `[0, 256]`. fn payout_stakers_alive_staked(n: u32, ) -> Weight { - // Minimum execution time: 164_719 nanoseconds. - Weight::from_ref_time(226_304_276) - // Standard Error: 31_675 - .saturating_add(Weight::from_ref_time(32_622_427).saturating_mul(n.into())) - .saturating_add(RocksDbWeight::get().reads(10)) + // Proof Size summary in bytes: + // Measured: `35099 + n * (465 ±0)` + // Estimated: `83594 + n * (16026 ±0)` + // Minimum execution time: 94_560 nanoseconds. + Weight::from_parts(154_033_219, 83594) + // Standard Error: 26_663 + .saturating_add(Weight::from_ref_time(31_269_223).saturating_mul(n.into())) + .saturating_add(RocksDbWeight::get().reads(10_u64)) .saturating_add(RocksDbWeight::get().reads((5_u64).saturating_mul(n.into()))) - .saturating_add(RocksDbWeight::get().writes(3)) + .saturating_add(RocksDbWeight::get().writes(3_u64)) .saturating_add(RocksDbWeight::get().writes((3_u64).saturating_mul(n.into()))) + .saturating_add(Weight::from_proof_size(16026).saturating_mul(n.into())) } - // Storage: Staking Ledger (r:1 w:1) - // Storage: Balances Locks (r:1 w:1) - // Storage: System Account (r:1 w:1) - // Storage: VoterList ListNodes (r:3 w:3) - // Storage: Staking Bonded (r:1 w:0) - // Storage: VoterList ListBags (r:2 w:2) + /// Storage: Staking Ledger (r:1 w:1) + /// Proof: Staking Ledger (max_values: None, max_size: Some(1091), added: 3566, mode: MaxEncodedLen) + /// Storage: Balances Locks (r:1 w:1) + /// Proof: Balances Locks (max_values: None, max_size: Some(1299), added: 3774, mode: MaxEncodedLen) + /// Storage: System Account (r:1 w:1) + /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) + /// Storage: VoterList ListNodes (r:3 w:3) + /// Proof: VoterList ListNodes (max_values: None, max_size: Some(154), added: 2629, mode: MaxEncodedLen) + /// Storage: Staking Bonded (r:1 w:0) + /// Proof: Staking Bonded (max_values: None, max_size: Some(72), added: 2547, mode: MaxEncodedLen) + /// Storage: VoterList ListBags (r:2 w:2) + /// Proof: VoterList ListBags (max_values: None, max_size: Some(82), added: 2557, mode: MaxEncodedLen) /// The range of component `l` is `[1, 32]`. fn rebond(l: u32, ) -> Weight { - // Minimum execution time: 95_631 nanoseconds. - Weight::from_ref_time(96_861_556) - // Standard Error: 2_114 - .saturating_add(Weight::from_ref_time(37_543).saturating_mul(l.into())) - .saturating_add(RocksDbWeight::get().reads(9)) - .saturating_add(RocksDbWeight::get().writes(8)) - } - // Storage: System Account (r:1 w:1) - // Storage: Staking Bonded (r:1 w:1) - // Storage: Staking Ledger (r:1 w:1) - // Storage: Staking SlashingSpans (r:1 w:1) - // Storage: Staking Validators (r:1 w:0) - // Storage: Staking Nominators (r:1 w:1) - // Storage: Staking CounterForNominators (r:1 w:1) - // Storage: VoterList ListNodes (r:2 w:2) - // Storage: VoterList ListBags (r:1 w:1) - // Storage: VoterList CounterForListNodes (r:1 w:1) - // Storage: Balances Locks (r:1 w:1) - // Storage: Staking Payee (r:0 w:1) - // Storage: Staking SpanSlash (r:0 w:1) + // Proof Size summary in bytes: + // Measured: `2253 + l * (7 ±0)` + // Estimated: `25491` + // Minimum execution time: 74_764 nanoseconds. + Weight::from_parts(75_814_067, 25491) + // Standard Error: 1_217 + .saturating_add(Weight::from_ref_time(64_725).saturating_mul(l.into())) + .saturating_add(RocksDbWeight::get().reads(9_u64)) + .saturating_add(RocksDbWeight::get().writes(8_u64)) + } + /// Storage: System Account (r:1 w:1) + /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) + /// Storage: Staking Bonded (r:1 w:1) + /// Proof: Staking Bonded (max_values: None, max_size: Some(72), added: 2547, mode: MaxEncodedLen) + /// Storage: Staking Ledger (r:1 w:1) + /// Proof: Staking Ledger (max_values: None, max_size: Some(1091), added: 3566, mode: MaxEncodedLen) + /// Storage: Staking SlashingSpans (r:1 w:1) + /// Proof Skipped: Staking SlashingSpans (max_values: None, max_size: None, mode: Measured) + /// Storage: Staking Validators (r:1 w:0) + /// Proof: Staking Validators (max_values: None, max_size: Some(45), added: 2520, mode: MaxEncodedLen) + /// Storage: Staking Nominators (r:1 w:1) + /// Proof: Staking Nominators (max_values: None, max_size: Some(558), added: 3033, mode: MaxEncodedLen) + /// Storage: Staking CounterForNominators (r:1 w:1) + /// Proof: Staking CounterForNominators (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) + /// Storage: VoterList ListNodes (r:2 w:2) + /// Proof: VoterList ListNodes (max_values: None, max_size: Some(154), added: 2629, mode: MaxEncodedLen) + /// Storage: VoterList ListBags (r:1 w:1) + /// Proof: VoterList ListBags (max_values: None, max_size: Some(82), added: 2557, mode: MaxEncodedLen) + /// Storage: VoterList CounterForListNodes (r:1 w:1) + /// Proof: VoterList CounterForListNodes (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) + /// Storage: Balances Locks (r:1 w:1) + /// Proof: Balances Locks (max_values: None, max_size: Some(1299), added: 3774, mode: MaxEncodedLen) + /// Storage: Staking Payee (r:0 w:1) + /// Proof: Staking Payee (max_values: None, max_size: Some(73), added: 2548, mode: MaxEncodedLen) + /// Storage: Staking SpanSlash (r:0 w:100) + /// Proof: Staking SpanSlash (max_values: None, max_size: Some(76), added: 2551, mode: MaxEncodedLen) /// The range of component `s` is `[1, 100]`. fn reap_stash(s: u32, ) -> Weight { - // Minimum execution time: 95_251 nanoseconds. - Weight::from_ref_time(97_818_954) - // Standard Error: 2_356 - .saturating_add(Weight::from_ref_time(1_104_695).saturating_mul(s.into())) - .saturating_add(RocksDbWeight::get().reads(12)) - .saturating_add(RocksDbWeight::get().writes(12)) + // Proof Size summary in bytes: + // Measured: `2486 + s * (4 ±0)` + // Estimated: `31810 + s * (4 ±0)` + // Minimum execution time: 77_611 nanoseconds. + Weight::from_parts(79_760_034, 31810) + // Standard Error: 1_597 + .saturating_add(Weight::from_ref_time(1_039_268).saturating_mul(s.into())) + .saturating_add(RocksDbWeight::get().reads(12_u64)) + .saturating_add(RocksDbWeight::get().writes(12_u64)) .saturating_add(RocksDbWeight::get().writes((1_u64).saturating_mul(s.into()))) + .saturating_add(Weight::from_proof_size(4).saturating_mul(s.into())) } - // Storage: VoterList CounterForListNodes (r:1 w:0) - // Storage: VoterList ListBags (r:200 w:0) - // Storage: VoterList ListNodes (r:101 w:0) - // Storage: Staking Nominators (r:101 w:0) - // Storage: Staking Validators (r:2 w:0) - // Storage: Staking Bonded (r:101 w:0) - // Storage: Staking Ledger (r:101 w:0) - // Storage: Staking CounterForValidators (r:1 w:0) - // Storage: Staking ValidatorCount (r:1 w:0) - // Storage: Staking MinimumValidatorCount (r:1 w:0) - // Storage: Staking CurrentEra (r:1 w:1) - // Storage: Staking ErasStakersClipped (r:0 w:1) - // Storage: Staking ErasValidatorPrefs (r:0 w:1) - // Storage: Staking ErasStakers (r:0 w:1) - // Storage: Staking ErasTotalStake (r:0 w:1) - // Storage: Staking ErasStartSessionIndex (r:0 w:1) - // Storage: Staking MinimumActiveStake (r:0 w:1) + /// Storage: VoterList CounterForListNodes (r:1 w:0) + /// Proof: VoterList CounterForListNodes (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) + /// Storage: VoterList ListBags (r:200 w:0) + /// Proof: VoterList ListBags (max_values: None, max_size: Some(82), added: 2557, mode: MaxEncodedLen) + /// Storage: VoterList ListNodes (r:110 w:0) + /// Proof: VoterList ListNodes (max_values: None, max_size: Some(154), added: 2629, mode: MaxEncodedLen) + /// Storage: Staking Nominators (r:110 w:0) + /// Proof: Staking Nominators (max_values: None, max_size: Some(558), added: 3033, mode: MaxEncodedLen) + /// Storage: Staking Validators (r:11 w:0) + /// Proof: Staking Validators (max_values: None, max_size: Some(45), added: 2520, mode: MaxEncodedLen) + /// Storage: Staking Bonded (r:110 w:0) + /// Proof: Staking Bonded (max_values: None, max_size: Some(72), added: 2547, mode: MaxEncodedLen) + /// Storage: Staking Ledger (r:110 w:0) + /// Proof: Staking Ledger (max_values: None, max_size: Some(1091), added: 3566, mode: MaxEncodedLen) + /// Storage: Staking CounterForValidators (r:1 w:0) + /// Proof: Staking CounterForValidators (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) + /// Storage: Staking ValidatorCount (r:1 w:0) + /// Proof: Staking ValidatorCount (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) + /// Storage: Staking MinimumValidatorCount (r:1 w:0) + /// Proof: Staking MinimumValidatorCount (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) + /// Storage: Staking CurrentEra (r:1 w:1) + /// Proof: Staking CurrentEra (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) + /// Storage: Staking ErasStakersClipped (r:0 w:10) + /// Proof Skipped: Staking ErasStakersClipped (max_values: None, max_size: None, mode: Measured) + /// Storage: Staking ErasValidatorPrefs (r:0 w:10) + /// Proof: Staking ErasValidatorPrefs (max_values: None, max_size: Some(57), added: 2532, mode: MaxEncodedLen) + /// Storage: Staking ErasStakers (r:0 w:10) + /// Proof Skipped: Staking ErasStakers (max_values: None, max_size: None, mode: Measured) + /// Storage: Staking ErasTotalStake (r:0 w:1) + /// Proof: Staking ErasTotalStake (max_values: None, max_size: Some(28), added: 2503, mode: MaxEncodedLen) + /// Storage: Staking ErasStartSessionIndex (r:0 w:1) + /// Proof: Staking ErasStartSessionIndex (max_values: None, max_size: Some(16), added: 2491, mode: MaxEncodedLen) + /// Storage: Staking MinimumActiveStake (r:0 w:1) + /// Proof: Staking MinimumActiveStake (max_values: Some(1), max_size: Some(16), added: 511, mode: MaxEncodedLen) /// The range of component `v` is `[1, 10]`. /// The range of component `n` is `[0, 100]`. fn new_era(v: u32, n: u32, ) -> Weight { - // Minimum execution time: 512_923 nanoseconds. - Weight::from_ref_time(514_740_000) - // Standard Error: 1_790_238 - .saturating_add(Weight::from_ref_time(59_320_539).saturating_mul(v.into())) - // Standard Error: 178_387 - .saturating_add(Weight::from_ref_time(13_902_705).saturating_mul(n.into())) - .saturating_add(RocksDbWeight::get().reads(206)) + // Proof Size summary in bytes: + // Measured: `0 + v * (3662 ±0) + n * (816 ±0)` + // Estimated: `528203 + v * (16743 ±0) + n * (12947 ±0)` + // Minimum execution time: 489_824 nanoseconds. + Weight::from_parts(491_687_000, 528203) + // Standard Error: 1_787_577 + .saturating_add(Weight::from_ref_time(58_719_498).saturating_mul(v.into())) + // Standard Error: 178_122 + .saturating_add(Weight::from_ref_time(13_273_555).saturating_mul(n.into())) + .saturating_add(RocksDbWeight::get().reads(206_u64)) .saturating_add(RocksDbWeight::get().reads((5_u64).saturating_mul(v.into()))) .saturating_add(RocksDbWeight::get().reads((4_u64).saturating_mul(n.into()))) - .saturating_add(RocksDbWeight::get().writes(4)) + .saturating_add(RocksDbWeight::get().writes(4_u64)) .saturating_add(RocksDbWeight::get().writes((3_u64).saturating_mul(v.into()))) + .saturating_add(Weight::from_proof_size(16743).saturating_mul(v.into())) + .saturating_add(Weight::from_proof_size(12947).saturating_mul(n.into())) } - // Storage: VoterList CounterForListNodes (r:1 w:0) - // Storage: VoterList ListBags (r:200 w:0) - // Storage: VoterList ListNodes (r:1500 w:0) - // Storage: Staking Nominators (r:1500 w:0) - // Storage: Staking Validators (r:500 w:0) - // Storage: Staking Bonded (r:1500 w:0) - // Storage: Staking Ledger (r:1500 w:0) - // Storage: Staking MinimumActiveStake (r:0 w:1) + /// Storage: VoterList CounterForListNodes (r:1 w:0) + /// Proof: VoterList CounterForListNodes (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) + /// Storage: VoterList ListBags (r:200 w:0) + /// Proof: VoterList ListBags (max_values: None, max_size: Some(82), added: 2557, mode: MaxEncodedLen) + /// Storage: VoterList ListNodes (r:2000 w:0) + /// Proof: VoterList ListNodes (max_values: None, max_size: Some(154), added: 2629, mode: MaxEncodedLen) + /// Storage: Staking Nominators (r:2000 w:0) + /// Proof: Staking Nominators (max_values: None, max_size: Some(558), added: 3033, mode: MaxEncodedLen) + /// Storage: Staking Validators (r:1000 w:0) + /// Proof: Staking Validators (max_values: None, max_size: Some(45), added: 2520, mode: MaxEncodedLen) + /// Storage: Staking Bonded (r:2000 w:0) + /// Proof: Staking Bonded (max_values: None, max_size: Some(72), added: 2547, mode: MaxEncodedLen) + /// Storage: Staking Ledger (r:2000 w:0) + /// Proof: Staking Ledger (max_values: None, max_size: Some(1091), added: 3566, mode: MaxEncodedLen) + /// Storage: Staking MinimumActiveStake (r:0 w:1) + /// Proof: Staking MinimumActiveStake (max_values: Some(1), max_size: Some(16), added: 511, mode: MaxEncodedLen) /// The range of component `v` is `[500, 1000]`. /// The range of component `n` is `[500, 1000]`. fn get_npos_voters(v: u32, n: u32, ) -> Weight { - // Minimum execution time: 24_913_316 nanoseconds. - Weight::from_ref_time(25_053_596_000) - // Standard Error: 324_610 - .saturating_add(Weight::from_ref_time(3_454_859).saturating_mul(v.into())) - // Standard Error: 324_610 - .saturating_add(Weight::from_ref_time(3_020_267).saturating_mul(n.into())) - .saturating_add(RocksDbWeight::get().reads(201)) + // Proof Size summary in bytes: + // Measured: `3167 + v * (459 ±0) + n * (1007 ±0)` + // Estimated: `511899 + v * (14295 ±0) + n * (11775 ±0)` + // Minimum execution time: 23_373_467 nanoseconds. + Weight::from_parts(23_497_257_000, 511899) + // Standard Error: 299_205 + .saturating_add(Weight::from_ref_time(3_434_000).saturating_mul(v.into())) + // Standard Error: 299_205 + .saturating_add(Weight::from_ref_time(2_568_954).saturating_mul(n.into())) + .saturating_add(RocksDbWeight::get().reads(201_u64)) .saturating_add(RocksDbWeight::get().reads((5_u64).saturating_mul(v.into()))) .saturating_add(RocksDbWeight::get().reads((4_u64).saturating_mul(n.into()))) - .saturating_add(RocksDbWeight::get().writes(1)) + .saturating_add(RocksDbWeight::get().writes(1_u64)) + .saturating_add(Weight::from_proof_size(14295).saturating_mul(v.into())) + .saturating_add(Weight::from_proof_size(11775).saturating_mul(n.into())) } - // Storage: Staking CounterForValidators (r:1 w:0) - // Storage: Staking Validators (r:501 w:0) + /// Storage: Staking CounterForValidators (r:1 w:0) + /// Proof: Staking CounterForValidators (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) + /// Storage: Staking Validators (r:1001 w:0) + /// Proof: Staking Validators (max_values: None, max_size: Some(45), added: 2520, mode: MaxEncodedLen) /// The range of component `v` is `[500, 1000]`. fn get_npos_targets(v: u32, ) -> Weight { - // Minimum execution time: 4_916_401 nanoseconds. - Weight::from_ref_time(81_160_966) - // Standard Error: 23_829 - .saturating_add(Weight::from_ref_time(9_883_413).saturating_mul(v.into())) - .saturating_add(RocksDbWeight::get().reads(2)) + // Proof Size summary in bytes: + // Measured: `983 + v * (50 ±0)` + // Estimated: `3019 + v * (2520 ±0)` + // Minimum execution time: 3_882_120 nanoseconds. + Weight::from_parts(3_951_993_000, 3019) + // Standard Error: 46_729 + .saturating_add(Weight::from_ref_time(2_856_043).saturating_mul(v.into())) + .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().reads((1_u64).saturating_mul(v.into()))) + .saturating_add(Weight::from_proof_size(2520).saturating_mul(v.into())) } - // Storage: Staking MinCommission (r:0 w:1) - // Storage: Staking MinValidatorBond (r:0 w:1) - // Storage: Staking MaxValidatorsCount (r:0 w:1) - // Storage: Staking ChillThreshold (r:0 w:1) - // Storage: Staking MaxNominatorsCount (r:0 w:1) - // Storage: Staking MinNominatorBond (r:0 w:1) + /// Storage: Staking MinCommission (r:0 w:1) + /// Proof: Staking MinCommission (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) + /// Storage: Staking MinValidatorBond (r:0 w:1) + /// Proof: Staking MinValidatorBond (max_values: Some(1), max_size: Some(16), added: 511, mode: MaxEncodedLen) + /// Storage: Staking MaxValidatorsCount (r:0 w:1) + /// Proof: Staking MaxValidatorsCount (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) + /// Storage: Staking ChillThreshold (r:0 w:1) + /// Proof: Staking ChillThreshold (max_values: Some(1), max_size: Some(1), added: 496, mode: MaxEncodedLen) + /// Storage: Staking MaxNominatorsCount (r:0 w:1) + /// Proof: Staking MaxNominatorsCount (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) + /// Storage: Staking MinNominatorBond (r:0 w:1) + /// Proof: Staking MinNominatorBond (max_values: Some(1), max_size: Some(16), added: 511, mode: MaxEncodedLen) fn set_staking_configs_all_set() -> Weight { - // Minimum execution time: 10_937 nanoseconds. - Weight::from_ref_time(11_324_000) - .saturating_add(RocksDbWeight::get().writes(6)) - } - // Storage: Staking MinCommission (r:0 w:1) - // Storage: Staking MinValidatorBond (r:0 w:1) - // Storage: Staking MaxValidatorsCount (r:0 w:1) - // Storage: Staking ChillThreshold (r:0 w:1) - // Storage: Staking MaxNominatorsCount (r:0 w:1) - // Storage: Staking MinNominatorBond (r:0 w:1) + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 8_427 nanoseconds. + Weight::from_ref_time(8_794_000) + .saturating_add(RocksDbWeight::get().writes(6_u64)) + } + /// Storage: Staking MinCommission (r:0 w:1) + /// Proof: Staking MinCommission (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) + /// Storage: Staking MinValidatorBond (r:0 w:1) + /// Proof: Staking MinValidatorBond (max_values: Some(1), max_size: Some(16), added: 511, mode: MaxEncodedLen) + /// Storage: Staking MaxValidatorsCount (r:0 w:1) + /// Proof: Staking MaxValidatorsCount (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) + /// Storage: Staking ChillThreshold (r:0 w:1) + /// Proof: Staking ChillThreshold (max_values: Some(1), max_size: Some(1), added: 496, mode: MaxEncodedLen) + /// Storage: Staking MaxNominatorsCount (r:0 w:1) + /// Proof: Staking MaxNominatorsCount (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) + /// Storage: Staking MinNominatorBond (r:0 w:1) + /// Proof: Staking MinNominatorBond (max_values: Some(1), max_size: Some(16), added: 511, mode: MaxEncodedLen) fn set_staking_configs_all_remove() -> Weight { - // Minimum execution time: 9_424 nanoseconds. - Weight::from_ref_time(10_021_000) - .saturating_add(RocksDbWeight::get().writes(6)) - } - // Storage: Staking Ledger (r:1 w:0) - // Storage: Staking Nominators (r:1 w:1) - // Storage: Staking ChillThreshold (r:1 w:0) - // Storage: Staking MaxNominatorsCount (r:1 w:0) - // Storage: Staking CounterForNominators (r:1 w:1) - // Storage: Staking MinNominatorBond (r:1 w:0) - // Storage: Staking Validators (r:1 w:0) - // Storage: VoterList ListNodes (r:2 w:2) - // Storage: VoterList ListBags (r:1 w:1) - // Storage: VoterList CounterForListNodes (r:1 w:1) + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 7_620 nanoseconds. + Weight::from_ref_time(7_901_000) + .saturating_add(RocksDbWeight::get().writes(6_u64)) + } + /// Storage: Staking Ledger (r:1 w:0) + /// Proof: Staking Ledger (max_values: None, max_size: Some(1091), added: 3566, mode: MaxEncodedLen) + /// Storage: Staking Nominators (r:1 w:1) + /// Proof: Staking Nominators (max_values: None, max_size: Some(558), added: 3033, mode: MaxEncodedLen) + /// Storage: Staking ChillThreshold (r:1 w:0) + /// Proof: Staking ChillThreshold (max_values: Some(1), max_size: Some(1), added: 496, mode: MaxEncodedLen) + /// Storage: Staking MaxNominatorsCount (r:1 w:0) + /// Proof: Staking MaxNominatorsCount (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) + /// Storage: Staking CounterForNominators (r:1 w:1) + /// Proof: Staking CounterForNominators (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) + /// Storage: Staking MinNominatorBond (r:1 w:0) + /// Proof: Staking MinNominatorBond (max_values: Some(1), max_size: Some(16), added: 511, mode: MaxEncodedLen) + /// Storage: Staking Validators (r:1 w:0) + /// Proof: Staking Validators (max_values: None, max_size: Some(45), added: 2520, mode: MaxEncodedLen) + /// Storage: VoterList ListNodes (r:2 w:2) + /// Proof: VoterList ListNodes (max_values: None, max_size: Some(154), added: 2629, mode: MaxEncodedLen) + /// Storage: VoterList ListBags (r:1 w:1) + /// Proof: VoterList ListBags (max_values: None, max_size: Some(82), added: 2557, mode: MaxEncodedLen) + /// Storage: VoterList CounterForListNodes (r:1 w:1) + /// Proof: VoterList CounterForListNodes (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) fn chill_other() -> Weight { - // Minimum execution time: 84_495 nanoseconds. - Weight::from_ref_time(85_559_000) - .saturating_add(RocksDbWeight::get().reads(11)) - .saturating_add(RocksDbWeight::get().writes(6)) + // Proof Size summary in bytes: + // Measured: `2031` + // Estimated: `19438` + // Minimum execution time: 66_188 nanoseconds. + Weight::from_parts(66_767_000, 19438) + .saturating_add(RocksDbWeight::get().reads(11_u64)) + .saturating_add(RocksDbWeight::get().writes(6_u64)) } - // Storage: Staking MinCommission (r:1 w:0) - // Storage: Staking Validators (r:1 w:1) + /// Storage: Staking MinCommission (r:1 w:0) + /// Proof: Staking MinCommission (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) + /// Storage: Staking Validators (r:1 w:1) + /// Proof: Staking Validators (max_values: None, max_size: Some(45), added: 2520, mode: MaxEncodedLen) fn force_apply_min_commission() -> Weight { - // Minimum execution time: 20_385 nanoseconds. - Weight::from_ref_time(20_824_000) - .saturating_add(RocksDbWeight::get().reads(2)) - .saturating_add(RocksDbWeight::get().writes(1)) + // Proof Size summary in bytes: + // Measured: `694` + // Estimated: `3019` + // Minimum execution time: 14_703 nanoseconds. + Weight::from_parts(15_031_000, 3019) + .saturating_add(RocksDbWeight::get().reads(2_u64)) + .saturating_add(RocksDbWeight::get().writes(1_u64)) } - // Storage: Staking MinCommission (r:0 w:1) + /// Storage: Staking MinCommission (r:0 w:1) + /// Proof: Staking MinCommission (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) fn set_min_commission() -> Weight { - // Minimum execution time: 6_995 nanoseconds. - Weight::from_ref_time(7_213_000) - .saturating_add(RocksDbWeight::get().writes(1)) + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 4_518 nanoseconds. + Weight::from_ref_time(4_656_000) + .saturating_add(RocksDbWeight::get().writes(1_u64)) } } diff --git a/frame/state-trie-migration/src/weights.rs b/frame/state-trie-migration/src/weights.rs index 7414bb903..29b7f7ad4 100644 --- a/frame/state-trie-migration/src/weights.rs +++ b/frame/state-trie-migration/src/weights.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2022 Parity Technologies (UK) Ltd. +// Copyright (C) 2023 Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); @@ -18,7 +18,8 @@ //! Autogenerated weights for pallet_state_trie_migration //! //! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev -//! DATE: 2022-11-07, STEPS: `50`, REPEAT: 20, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! DATE: 2023-01-25, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! WORST CASE MAP SIZE: `1000000` //! HOSTNAME: `bm2`, CPU: `Intel(R) Core(TM) i7-7700K CPU @ 4.20GHz` //! EXECUTION: Some(Wasm), WASM-EXECUTION: Compiled, CHAIN: Some("dev"), DB CACHE: 1024 @@ -59,100 +60,156 @@ pub trait WeightInfo { /// Weights for pallet_state_trie_migration using the Substrate node and recommended hardware. pub struct SubstrateWeight(PhantomData); impl WeightInfo for SubstrateWeight { - // Storage: StateTrieMigration SignedMigrationMaxLimits (r:1 w:0) - // Storage: StateTrieMigration MigrationProcess (r:1 w:1) + /// Storage: StateTrieMigration SignedMigrationMaxLimits (r:1 w:0) + /// Proof: StateTrieMigration SignedMigrationMaxLimits (max_values: Some(1), max_size: Some(8), added: 503, mode: MaxEncodedLen) + /// Storage: StateTrieMigration MigrationProcess (r:1 w:1) + /// Proof: StateTrieMigration MigrationProcess (max_values: Some(1), max_size: Some(1042), added: 1537, mode: MaxEncodedLen) fn continue_migrate() -> Weight { - // Minimum execution time: 23_874 nanoseconds. - Weight::from_ref_time(24_127_000 as u64) - .saturating_add(T::DbWeight::get().reads(2 as u64)) - .saturating_add(T::DbWeight::get().writes(1 as u64)) + // Proof Size summary in bytes: + // Measured: `108` + // Estimated: `2040` + // Minimum execution time: 15_563 nanoseconds. + Weight::from_parts(15_783_000, 2040) + .saturating_add(T::DbWeight::get().reads(2_u64)) + .saturating_add(T::DbWeight::get().writes(1_u64)) } - // Storage: StateTrieMigration SignedMigrationMaxLimits (r:1 w:0) + /// Storage: StateTrieMigration SignedMigrationMaxLimits (r:1 w:0) + /// Proof: StateTrieMigration SignedMigrationMaxLimits (max_values: Some(1), max_size: Some(8), added: 503, mode: MaxEncodedLen) fn continue_migrate_wrong_witness() -> Weight { - // Minimum execution time: 6_119 nanoseconds. - Weight::from_ref_time(6_325_000 as u64) - .saturating_add(T::DbWeight::get().reads(1 as u64)) + // Proof Size summary in bytes: + // Measured: `76` + // Estimated: `503` + // Minimum execution time: 4_347 nanoseconds. + Weight::from_parts(4_558_000, 503) + .saturating_add(T::DbWeight::get().reads(1_u64)) } fn migrate_custom_top_success() -> Weight { - // Minimum execution time: 20_365 nanoseconds. - Weight::from_ref_time(20_790_000 as u64) + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 8_817 nanoseconds. + Weight::from_ref_time(9_027_000) } - // Storage: unknown [0x666f6f] (r:1 w:1) + /// Storage: unknown `0x666f6f` (r:1 w:1) + /// Proof Skipped: unknown `0x666f6f` (r:1 w:1) fn migrate_custom_top_fail() -> Weight { - // Minimum execution time: 38_979 nanoseconds. - Weight::from_ref_time(40_271_000 as u64) - .saturating_add(T::DbWeight::get().reads(1 as u64)) - .saturating_add(T::DbWeight::get().writes(1 as u64)) + // Proof Size summary in bytes: + // Measured: `144` + // Estimated: `2619` + // Minimum execution time: 23_854 nanoseconds. + Weight::from_parts(24_850_000, 2619) + .saturating_add(T::DbWeight::get().reads(1_u64)) + .saturating_add(T::DbWeight::get().writes(1_u64)) } fn migrate_custom_child_success() -> Weight { - // Minimum execution time: 21_217 nanoseconds. - Weight::from_ref_time(21_526_000 as u64) + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 9_020 nanoseconds. + Weight::from_ref_time(9_234_000) } - // Storage: unknown [0x666f6f] (r:1 w:1) + /// Storage: unknown `0x666f6f` (r:1 w:1) + /// Proof Skipped: unknown `0x666f6f` (r:1 w:1) fn migrate_custom_child_fail() -> Weight { - // Minimum execution time: 43_853 nanoseconds. - Weight::from_ref_time(44_693_000 as u64) - .saturating_add(T::DbWeight::get().reads(1 as u64)) - .saturating_add(T::DbWeight::get().writes(1 as u64)) + // Proof Size summary in bytes: + // Measured: `136` + // Estimated: `2611` + // Minimum execution time: 24_136 nanoseconds. + Weight::from_parts(24_810_000, 2611) + .saturating_add(T::DbWeight::get().reads(1_u64)) + .saturating_add(T::DbWeight::get().writes(1_u64)) } - // Storage: unknown [0x6b6579] (r:1 w:1) + /// Storage: unknown `0x6b6579` (r:1 w:1) + /// Proof Skipped: unknown `0x6b6579` (r:1 w:1) /// The range of component `v` is `[1, 4194304]`. fn process_top_key(v: u32, ) -> Weight { - // Minimum execution time: 5_575 nanoseconds. - Weight::from_ref_time(5_719_000 as u64) - // Standard Error: 3 - .saturating_add(Weight::from_ref_time(1_404 as u64).saturating_mul(v as u64)) - .saturating_add(T::DbWeight::get().reads(1 as u64)) - .saturating_add(T::DbWeight::get().writes(1 as u64)) + // Proof Size summary in bytes: + // Measured: `228 + v * (1 ±0)` + // Estimated: `2700 + v * (1 ±0)` + // Minimum execution time: 5_279 nanoseconds. + Weight::from_parts(5_517_000, 2700) + // Standard Error: 1 + .saturating_add(Weight::from_ref_time(1_230).saturating_mul(v.into())) + .saturating_add(T::DbWeight::get().reads(1_u64)) + .saturating_add(T::DbWeight::get().writes(1_u64)) + .saturating_add(Weight::from_proof_size(1).saturating_mul(v.into())) } } // For backwards compatibility and tests impl WeightInfo for () { - // Storage: StateTrieMigration SignedMigrationMaxLimits (r:1 w:0) - // Storage: StateTrieMigration MigrationProcess (r:1 w:1) + /// Storage: StateTrieMigration SignedMigrationMaxLimits (r:1 w:0) + /// Proof: StateTrieMigration SignedMigrationMaxLimits (max_values: Some(1), max_size: Some(8), added: 503, mode: MaxEncodedLen) + /// Storage: StateTrieMigration MigrationProcess (r:1 w:1) + /// Proof: StateTrieMigration MigrationProcess (max_values: Some(1), max_size: Some(1042), added: 1537, mode: MaxEncodedLen) fn continue_migrate() -> Weight { - // Minimum execution time: 23_874 nanoseconds. - Weight::from_ref_time(24_127_000 as u64) - .saturating_add(RocksDbWeight::get().reads(2 as u64)) - .saturating_add(RocksDbWeight::get().writes(1 as u64)) + // Proof Size summary in bytes: + // Measured: `108` + // Estimated: `2040` + // Minimum execution time: 15_563 nanoseconds. + Weight::from_parts(15_783_000, 2040) + .saturating_add(RocksDbWeight::get().reads(2_u64)) + .saturating_add(RocksDbWeight::get().writes(1_u64)) } - // Storage: StateTrieMigration SignedMigrationMaxLimits (r:1 w:0) + /// Storage: StateTrieMigration SignedMigrationMaxLimits (r:1 w:0) + /// Proof: StateTrieMigration SignedMigrationMaxLimits (max_values: Some(1), max_size: Some(8), added: 503, mode: MaxEncodedLen) fn continue_migrate_wrong_witness() -> Weight { - // Minimum execution time: 6_119 nanoseconds. - Weight::from_ref_time(6_325_000 as u64) - .saturating_add(RocksDbWeight::get().reads(1 as u64)) + // Proof Size summary in bytes: + // Measured: `76` + // Estimated: `503` + // Minimum execution time: 4_347 nanoseconds. + Weight::from_parts(4_558_000, 503) + .saturating_add(RocksDbWeight::get().reads(1_u64)) } fn migrate_custom_top_success() -> Weight { - // Minimum execution time: 20_365 nanoseconds. - Weight::from_ref_time(20_790_000 as u64) + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 8_817 nanoseconds. + Weight::from_ref_time(9_027_000) } - // Storage: unknown [0x666f6f] (r:1 w:1) + /// Storage: unknown `0x666f6f` (r:1 w:1) + /// Proof Skipped: unknown `0x666f6f` (r:1 w:1) fn migrate_custom_top_fail() -> Weight { - // Minimum execution time: 38_979 nanoseconds. - Weight::from_ref_time(40_271_000 as u64) - .saturating_add(RocksDbWeight::get().reads(1 as u64)) - .saturating_add(RocksDbWeight::get().writes(1 as u64)) + // Proof Size summary in bytes: + // Measured: `144` + // Estimated: `2619` + // Minimum execution time: 23_854 nanoseconds. + Weight::from_parts(24_850_000, 2619) + .saturating_add(RocksDbWeight::get().reads(1_u64)) + .saturating_add(RocksDbWeight::get().writes(1_u64)) } fn migrate_custom_child_success() -> Weight { - // Minimum execution time: 21_217 nanoseconds. - Weight::from_ref_time(21_526_000 as u64) + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 9_020 nanoseconds. + Weight::from_ref_time(9_234_000) } - // Storage: unknown [0x666f6f] (r:1 w:1) + /// Storage: unknown `0x666f6f` (r:1 w:1) + /// Proof Skipped: unknown `0x666f6f` (r:1 w:1) fn migrate_custom_child_fail() -> Weight { - // Minimum execution time: 43_853 nanoseconds. - Weight::from_ref_time(44_693_000 as u64) - .saturating_add(RocksDbWeight::get().reads(1 as u64)) - .saturating_add(RocksDbWeight::get().writes(1 as u64)) + // Proof Size summary in bytes: + // Measured: `136` + // Estimated: `2611` + // Minimum execution time: 24_136 nanoseconds. + Weight::from_parts(24_810_000, 2611) + .saturating_add(RocksDbWeight::get().reads(1_u64)) + .saturating_add(RocksDbWeight::get().writes(1_u64)) } - // Storage: unknown [0x6b6579] (r:1 w:1) + /// Storage: unknown `0x6b6579` (r:1 w:1) + /// Proof Skipped: unknown `0x6b6579` (r:1 w:1) /// The range of component `v` is `[1, 4194304]`. fn process_top_key(v: u32, ) -> Weight { - // Minimum execution time: 5_575 nanoseconds. - Weight::from_ref_time(5_719_000 as u64) - // Standard Error: 3 - .saturating_add(Weight::from_ref_time(1_404 as u64).saturating_mul(v as u64)) - .saturating_add(RocksDbWeight::get().reads(1 as u64)) - .saturating_add(RocksDbWeight::get().writes(1 as u64)) + // Proof Size summary in bytes: + // Measured: `228 + v * (1 ±0)` + // Estimated: `2700 + v * (1 ±0)` + // Minimum execution time: 5_279 nanoseconds. + Weight::from_parts(5_517_000, 2700) + // Standard Error: 1 + .saturating_add(Weight::from_ref_time(1_230).saturating_mul(v.into())) + .saturating_add(RocksDbWeight::get().reads(1_u64)) + .saturating_add(RocksDbWeight::get().writes(1_u64)) + .saturating_add(Weight::from_proof_size(1).saturating_mul(v.into())) } } diff --git a/frame/support/procedural/src/benchmark.rs b/frame/support/procedural/src/benchmark.rs index 43e3e47de..f3bd5c2cf 100644 --- a/frame/support/procedural/src/benchmark.rs +++ b/frame/support/procedural/src/benchmark.rs @@ -438,6 +438,9 @@ pub fn benchmarks( #krate::BenchmarkMetadata { name: benchmark.as_bytes().to_vec(), components, + // TODO: Not supported by V2 syntax as of yet. + // https://github.com/paritytech/substrate/issues/13132 + pov_modes: vec![], } }).collect::<#krate::Vec<_>>() } diff --git a/frame/support/src/storage/transactional.rs b/frame/support/src/storage/transactional.rs index 909d5909e..b7ae22d86 100644 --- a/frame/support/src/storage/transactional.rs +++ b/frame/support/src/storage/transactional.rs @@ -32,6 +32,8 @@ use sp_runtime::{DispatchError, TransactionOutcome, TransactionalError}; /// The type that is being used to store the current number of active layers. pub type Layer = u32; /// The key that is holds the current number of active layers. +/// +/// Encodes to `0x3a7472616e73616374696f6e5f6c6576656c3a`. pub const TRANSACTION_LEVEL_KEY: &[u8] = b":transaction_level:"; /// The maximum number of nested layers. pub const TRANSACTIONAL_LIMIT: Layer = 255; diff --git a/frame/support/src/weights/block_weights.rs b/frame/support/src/weights/block_weights.rs index b68c1fb50..dc01107ce 100644 --- a/frame/support/src/weights/block_weights.rs +++ b/frame/support/src/weights/block_weights.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2022 Parity Technologies (UK) Ltd. +// Copyright (C) 2023 Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); @@ -16,7 +16,7 @@ // limitations under the License. //! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev -//! DATE: 2022-11-07 (Y/M/D) +//! DATE: 2023-01-25 (Y/M/D) //! HOSTNAME: `bm2`, CPU: `Intel(R) Core(TM) i7-7700K CPU @ 4.20GHz` //! //! SHORT-NAME: `block`, LONG-NAME: `BlockExecution`, RUNTIME: `Development` @@ -44,17 +44,17 @@ parameter_types! { /// Calculated by multiplying the *Average* with `1.0` and adding `0`. /// /// Stats nanoseconds: - /// Min, Max: 351_000, 392_617 - /// Average: 358_523 - /// Median: 359_836 - /// Std-Dev: 6698.67 + /// Min, Max: 377_722, 414_752 + /// Average: 381_015 + /// Median: 379_751 + /// Std-Dev: 5462.64 /// /// Percentiles nanoseconds: - /// 99th: 390_723 - /// 95th: 365_799 - /// 75th: 361_582 + /// 99th: 413_074 + /// 95th: 384_876 + /// 75th: 380_642 pub const BlockExecutionWeight: Weight = - Weight::from_ref_time(WEIGHT_REF_TIME_PER_NANOS.saturating_mul(358_523)); + Weight::from_ref_time(WEIGHT_REF_TIME_PER_NANOS.saturating_mul(381_015)); } #[cfg(test)] diff --git a/frame/support/src/weights/extrinsic_weights.rs b/frame/support/src/weights/extrinsic_weights.rs index ced1fb916..3d25d5a40 100644 --- a/frame/support/src/weights/extrinsic_weights.rs +++ b/frame/support/src/weights/extrinsic_weights.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2022 Parity Technologies (UK) Ltd. +// Copyright (C) 2023 Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); @@ -16,7 +16,7 @@ // limitations under the License. //! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev -//! DATE: 2022-11-07 (Y/M/D) +//! DATE: 2023-01-25 (Y/M/D) //! HOSTNAME: `bm2`, CPU: `Intel(R) Core(TM) i7-7700K CPU @ 4.20GHz` //! //! SHORT-NAME: `extrinsic`, LONG-NAME: `ExtrinsicBase`, RUNTIME: `Development` @@ -44,17 +44,17 @@ parameter_types! { /// Calculated by multiplying the *Average* with `1.0` and adding `0`. /// /// Stats nanoseconds: - /// Min, Max: 98_722, 101_420 - /// Average: 98_974 - /// Median: 98_951 - /// Std-Dev: 271.62 + /// Min, Max: 99_481, 103_304 + /// Average: 99_840 + /// Median: 99_795 + /// Std-Dev: 376.17 /// /// Percentiles nanoseconds: - /// 99th: 99_202 - /// 95th: 99_163 - /// 75th: 99_030 + /// 99th: 100_078 + /// 95th: 100_051 + /// 75th: 99_916 pub const ExtrinsicBaseWeight: Weight = - Weight::from_ref_time(WEIGHT_REF_TIME_PER_NANOS.saturating_mul(98_974)); + Weight::from_ref_time(WEIGHT_REF_TIME_PER_NANOS.saturating_mul(99_840)); } #[cfg(test)] diff --git a/frame/system/src/weights.rs b/frame/system/src/weights.rs index 696a6a09b..d189afd4a 100644 --- a/frame/system/src/weights.rs +++ b/frame/system/src/weights.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2022 Parity Technologies (UK) Ltd. +// Copyright (C) 2023 Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); @@ -18,7 +18,8 @@ //! Autogenerated weights for frame_system //! //! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev -//! DATE: 2022-11-07, STEPS: `50`, REPEAT: 20, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! DATE: 2023-01-24, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! WORST CASE MAP SIZE: `1000000` //! HOSTNAME: `bm2`, CPU: `Intel(R) Core(TM) i7-7700K CPU @ 4.20GHz` //! EXECUTION: Some(Wasm), WASM-EXECUTION: Compiled, CHAIN: Some("dev"), DB CACHE: 1024 @@ -60,52 +61,76 @@ pub struct SubstrateWeight(PhantomData); impl WeightInfo for SubstrateWeight { /// The range of component `b` is `[0, 3932160]`. fn remark(b: u32, ) -> Weight { - // Minimum execution time: 3_951 nanoseconds. - Weight::from_ref_time(1_307_232 as u64) + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 2_018 nanoseconds. + Weight::from_ref_time(2_091_000) // Standard Error: 0 - .saturating_add(Weight::from_ref_time(363 as u64).saturating_mul(b as u64)) + .saturating_add(Weight::from_ref_time(362).saturating_mul(b.into())) } /// The range of component `b` is `[0, 3932160]`. fn remark_with_event(b: u32, ) -> Weight { - // Minimum execution time: 14_880 nanoseconds. - Weight::from_ref_time(15_173_000 as u64) + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 7_449 nanoseconds. + Weight::from_ref_time(7_748_000) // Standard Error: 0 - .saturating_add(Weight::from_ref_time(1_424 as u64).saturating_mul(b as u64)) + .saturating_add(Weight::from_ref_time(1_423).saturating_mul(b.into())) } - // Storage: System Digest (r:1 w:1) - // Storage: unknown [0x3a686561707061676573] (r:0 w:1) + /// Storage: System Digest (r:1 w:1) + /// Proof Skipped: System Digest (max_values: Some(1), max_size: None, mode: Measured) + /// Storage: unknown `0x3a686561707061676573` (r:0 w:1) + /// Proof Skipped: unknown `0x3a686561707061676573` (r:0 w:1) fn set_heap_pages() -> Weight { - // Minimum execution time: 9_819 nanoseconds. - Weight::from_ref_time(10_513_000 as u64) - .saturating_add(T::DbWeight::get().reads(1 as u64)) - .saturating_add(T::DbWeight::get().writes(2 as u64)) + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `495` + // Minimum execution time: 4_440 nanoseconds. + Weight::from_parts(4_605_000, 495) + .saturating_add(T::DbWeight::get().reads(1_u64)) + .saturating_add(T::DbWeight::get().writes(2_u64)) } - // Storage: Skipped Metadata (r:0 w:0) + /// Storage: Skipped Metadata (r:0 w:0) + /// Proof Skipped: Skipped Metadata (max_values: None, max_size: None, mode: Measured) /// The range of component `i` is `[0, 1000]`. fn set_storage(i: u32, ) -> Weight { - // Minimum execution time: 4_038 nanoseconds. - Weight::from_ref_time(4_098_000 as u64) - // Standard Error: 710 - .saturating_add(Weight::from_ref_time(620_813 as u64).saturating_mul(i as u64)) - .saturating_add(T::DbWeight::get().writes((1 as u64).saturating_mul(i as u64))) + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 1_981 nanoseconds. + Weight::from_ref_time(2_114_000) + // Standard Error: 804 + .saturating_add(Weight::from_ref_time(631_438).saturating_mul(i.into())) + .saturating_add(T::DbWeight::get().writes((1_u64).saturating_mul(i.into()))) } - // Storage: Skipped Metadata (r:0 w:0) + /// Storage: Skipped Metadata (r:0 w:0) + /// Proof Skipped: Skipped Metadata (max_values: None, max_size: None, mode: Measured) /// The range of component `i` is `[0, 1000]`. fn kill_storage(i: u32, ) -> Weight { - // Minimum execution time: 3_972 nanoseconds. - Weight::from_ref_time(4_082_000 as u64) - // Standard Error: 884 - .saturating_add(Weight::from_ref_time(536_923 as u64).saturating_mul(i as u64)) - .saturating_add(T::DbWeight::get().writes((1 as u64).saturating_mul(i as u64))) + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 2_061 nanoseconds. + Weight::from_ref_time(2_153_000) + // Standard Error: 952 + .saturating_add(Weight::from_ref_time(502_629).saturating_mul(i.into())) + .saturating_add(T::DbWeight::get().writes((1_u64).saturating_mul(i.into()))) } - // Storage: Skipped Metadata (r:0 w:0) + /// Storage: Skipped Metadata (r:0 w:0) + /// Proof Skipped: Skipped Metadata (max_values: None, max_size: None, mode: Measured) /// The range of component `p` is `[0, 1000]`. fn kill_prefix(p: u32, ) -> Weight { - // Minimum execution time: 5_703 nanoseconds. - Weight::from_ref_time(5_763_000 as u64) - // Standard Error: 1_248 - .saturating_add(Weight::from_ref_time(1_126_062 as u64).saturating_mul(p as u64)) - .saturating_add(T::DbWeight::get().writes((1 as u64).saturating_mul(p as u64))) + // Proof Size summary in bytes: + // Measured: `116 + p * (69 ±0)` + // Estimated: `121 + p * (70 ±0)` + // Minimum execution time: 4_026 nanoseconds. + Weight::from_parts(4_174_000, 121) + // Standard Error: 1_148 + .saturating_add(Weight::from_ref_time(1_093_099).saturating_mul(p.into())) + .saturating_add(T::DbWeight::get().writes((1_u64).saturating_mul(p.into()))) + .saturating_add(Weight::from_proof_size(70).saturating_mul(p.into())) } } @@ -113,51 +138,75 @@ impl WeightInfo for SubstrateWeight { impl WeightInfo for () { /// The range of component `b` is `[0, 3932160]`. fn remark(b: u32, ) -> Weight { - // Minimum execution time: 3_951 nanoseconds. - Weight::from_ref_time(1_307_232 as u64) + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 2_018 nanoseconds. + Weight::from_ref_time(2_091_000) // Standard Error: 0 - .saturating_add(Weight::from_ref_time(363 as u64).saturating_mul(b as u64)) + .saturating_add(Weight::from_ref_time(362).saturating_mul(b.into())) } /// The range of component `b` is `[0, 3932160]`. fn remark_with_event(b: u32, ) -> Weight { - // Minimum execution time: 14_880 nanoseconds. - Weight::from_ref_time(15_173_000 as u64) + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 7_449 nanoseconds. + Weight::from_ref_time(7_748_000) // Standard Error: 0 - .saturating_add(Weight::from_ref_time(1_424 as u64).saturating_mul(b as u64)) + .saturating_add(Weight::from_ref_time(1_423).saturating_mul(b.into())) } - // Storage: System Digest (r:1 w:1) - // Storage: unknown [0x3a686561707061676573] (r:0 w:1) + /// Storage: System Digest (r:1 w:1) + /// Proof Skipped: System Digest (max_values: Some(1), max_size: None, mode: Measured) + /// Storage: unknown `0x3a686561707061676573` (r:0 w:1) + /// Proof Skipped: unknown `0x3a686561707061676573` (r:0 w:1) fn set_heap_pages() -> Weight { - // Minimum execution time: 9_819 nanoseconds. - Weight::from_ref_time(10_513_000 as u64) - .saturating_add(RocksDbWeight::get().reads(1 as u64)) - .saturating_add(RocksDbWeight::get().writes(2 as u64)) + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `495` + // Minimum execution time: 4_440 nanoseconds. + Weight::from_parts(4_605_000, 495) + .saturating_add(RocksDbWeight::get().reads(1_u64)) + .saturating_add(RocksDbWeight::get().writes(2_u64)) } - // Storage: Skipped Metadata (r:0 w:0) + /// Storage: Skipped Metadata (r:0 w:0) + /// Proof Skipped: Skipped Metadata (max_values: None, max_size: None, mode: Measured) /// The range of component `i` is `[0, 1000]`. fn set_storage(i: u32, ) -> Weight { - // Minimum execution time: 4_038 nanoseconds. - Weight::from_ref_time(4_098_000 as u64) - // Standard Error: 710 - .saturating_add(Weight::from_ref_time(620_813 as u64).saturating_mul(i as u64)) - .saturating_add(RocksDbWeight::get().writes((1 as u64).saturating_mul(i as u64))) + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 1_981 nanoseconds. + Weight::from_ref_time(2_114_000) + // Standard Error: 804 + .saturating_add(Weight::from_ref_time(631_438).saturating_mul(i.into())) + .saturating_add(RocksDbWeight::get().writes((1_u64).saturating_mul(i.into()))) } - // Storage: Skipped Metadata (r:0 w:0) + /// Storage: Skipped Metadata (r:0 w:0) + /// Proof Skipped: Skipped Metadata (max_values: None, max_size: None, mode: Measured) /// The range of component `i` is `[0, 1000]`. fn kill_storage(i: u32, ) -> Weight { - // Minimum execution time: 3_972 nanoseconds. - Weight::from_ref_time(4_082_000 as u64) - // Standard Error: 884 - .saturating_add(Weight::from_ref_time(536_923 as u64).saturating_mul(i as u64)) - .saturating_add(RocksDbWeight::get().writes((1 as u64).saturating_mul(i as u64))) + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 2_061 nanoseconds. + Weight::from_ref_time(2_153_000) + // Standard Error: 952 + .saturating_add(Weight::from_ref_time(502_629).saturating_mul(i.into())) + .saturating_add(RocksDbWeight::get().writes((1_u64).saturating_mul(i.into()))) } - // Storage: Skipped Metadata (r:0 w:0) + /// Storage: Skipped Metadata (r:0 w:0) + /// Proof Skipped: Skipped Metadata (max_values: None, max_size: None, mode: Measured) /// The range of component `p` is `[0, 1000]`. fn kill_prefix(p: u32, ) -> Weight { - // Minimum execution time: 5_703 nanoseconds. - Weight::from_ref_time(5_763_000 as u64) - // Standard Error: 1_248 - .saturating_add(Weight::from_ref_time(1_126_062 as u64).saturating_mul(p as u64)) - .saturating_add(RocksDbWeight::get().writes((1 as u64).saturating_mul(p as u64))) + // Proof Size summary in bytes: + // Measured: `116 + p * (69 ±0)` + // Estimated: `121 + p * (70 ±0)` + // Minimum execution time: 4_026 nanoseconds. + Weight::from_parts(4_174_000, 121) + // Standard Error: 1_148 + .saturating_add(Weight::from_ref_time(1_093_099).saturating_mul(p.into())) + .saturating_add(RocksDbWeight::get().writes((1_u64).saturating_mul(p.into()))) + .saturating_add(Weight::from_proof_size(70).saturating_mul(p.into())) } } diff --git a/frame/timestamp/src/weights.rs b/frame/timestamp/src/weights.rs index 521239209..4c7dfd5ed 100644 --- a/frame/timestamp/src/weights.rs +++ b/frame/timestamp/src/weights.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2022 Parity Technologies (UK) Ltd. +// Copyright (C) 2023 Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); @@ -18,7 +18,8 @@ //! Autogenerated weights for pallet_timestamp //! //! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev -//! DATE: 2022-11-07, STEPS: `50`, REPEAT: 20, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! DATE: 2023-01-25, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! WORST CASE MAP SIZE: `1000000` //! HOSTNAME: `bm2`, CPU: `Intel(R) Core(TM) i7-7700K CPU @ 4.20GHz` //! EXECUTION: Some(Wasm), WASM-EXECUTION: Compiled, CHAIN: Some("dev"), DB CACHE: 1024 @@ -54,32 +55,48 @@ pub trait WeightInfo { /// Weights for pallet_timestamp using the Substrate node and recommended hardware. pub struct SubstrateWeight(PhantomData); impl WeightInfo for SubstrateWeight { - // Storage: Timestamp Now (r:1 w:1) - // Storage: Babe CurrentSlot (r:1 w:0) + /// Storage: Timestamp Now (r:1 w:1) + /// Proof: Timestamp Now (max_values: Some(1), max_size: Some(8), added: 503, mode: MaxEncodedLen) + /// Storage: Babe CurrentSlot (r:1 w:0) + /// Proof: Babe CurrentSlot (max_values: Some(1), max_size: Some(8), added: 503, mode: MaxEncodedLen) fn set() -> Weight { - // Minimum execution time: 11_331 nanoseconds. - Weight::from_ref_time(11_584_000 as u64) - .saturating_add(T::DbWeight::get().reads(2 as u64)) - .saturating_add(T::DbWeight::get().writes(1 as u64)) + // Proof Size summary in bytes: + // Measured: `312` + // Estimated: `1006` + // Minimum execution time: 9_106 nanoseconds. + Weight::from_parts(9_258_000, 1006) + .saturating_add(T::DbWeight::get().reads(2_u64)) + .saturating_add(T::DbWeight::get().writes(1_u64)) } fn on_finalize() -> Weight { - // Minimum execution time: 5_280 nanoseconds. - Weight::from_ref_time(5_412_000 as u64) + // Proof Size summary in bytes: + // Measured: `161` + // Estimated: `0` + // Minimum execution time: 3_927 nanoseconds. + Weight::from_ref_time(4_078_000) } } // For backwards compatibility and tests impl WeightInfo for () { - // Storage: Timestamp Now (r:1 w:1) - // Storage: Babe CurrentSlot (r:1 w:0) + /// Storage: Timestamp Now (r:1 w:1) + /// Proof: Timestamp Now (max_values: Some(1), max_size: Some(8), added: 503, mode: MaxEncodedLen) + /// Storage: Babe CurrentSlot (r:1 w:0) + /// Proof: Babe CurrentSlot (max_values: Some(1), max_size: Some(8), added: 503, mode: MaxEncodedLen) fn set() -> Weight { - // Minimum execution time: 11_331 nanoseconds. - Weight::from_ref_time(11_584_000 as u64) - .saturating_add(RocksDbWeight::get().reads(2 as u64)) - .saturating_add(RocksDbWeight::get().writes(1 as u64)) + // Proof Size summary in bytes: + // Measured: `312` + // Estimated: `1006` + // Minimum execution time: 9_106 nanoseconds. + Weight::from_parts(9_258_000, 1006) + .saturating_add(RocksDbWeight::get().reads(2_u64)) + .saturating_add(RocksDbWeight::get().writes(1_u64)) } fn on_finalize() -> Weight { - // Minimum execution time: 5_280 nanoseconds. - Weight::from_ref_time(5_412_000 as u64) + // Proof Size summary in bytes: + // Measured: `161` + // Estimated: `0` + // Minimum execution time: 3_927 nanoseconds. + Weight::from_ref_time(4_078_000) } } diff --git a/frame/tips/src/weights.rs b/frame/tips/src/weights.rs index 1aa3fd8fa..a568aaa87 100644 --- a/frame/tips/src/weights.rs +++ b/frame/tips/src/weights.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2022 Parity Technologies (UK) Ltd. +// Copyright (C) 2023 Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); @@ -18,7 +18,8 @@ //! Autogenerated weights for pallet_tips //! //! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev -//! DATE: 2022-11-07, STEPS: `50`, REPEAT: 20, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! DATE: 2023-01-25, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! WORST CASE MAP SIZE: `1000000` //! HOSTNAME: `bm2`, CPU: `Intel(R) Core(TM) i7-7700K CPU @ 4.20GHz` //! EXECUTION: Some(Wasm), WASM-EXECUTION: Compiled, CHAIN: Some("dev"), DB CACHE: 1024 @@ -58,146 +59,218 @@ pub trait WeightInfo { /// Weights for pallet_tips using the Substrate node and recommended hardware. pub struct SubstrateWeight(PhantomData); impl WeightInfo for SubstrateWeight { - // Storage: Tips Reasons (r:1 w:1) - // Storage: Tips Tips (r:1 w:1) + /// Storage: Tips Reasons (r:1 w:1) + /// Proof Skipped: Tips Reasons (max_values: None, max_size: None, mode: Measured) + /// Storage: Tips Tips (r:1 w:1) + /// Proof Skipped: Tips Tips (max_values: None, max_size: None, mode: Measured) /// The range of component `r` is `[0, 300]`. fn report_awesome(r: u32, ) -> Weight { - // Minimum execution time: 35_458 nanoseconds. - Weight::from_ref_time(36_920_009 as u64) - // Standard Error: 252 - .saturating_add(Weight::from_ref_time(1_835 as u64).saturating_mul(r as u64)) - .saturating_add(T::DbWeight::get().reads(2 as u64)) - .saturating_add(T::DbWeight::get().writes(2 as u64)) + // Proof Size summary in bytes: + // Measured: `4` + // Estimated: `4958` + // Minimum execution time: 23_262 nanoseconds. + Weight::from_parts(24_104_224, 4958) + // Standard Error: 148 + .saturating_add(Weight::from_ref_time(1_963).saturating_mul(r.into())) + .saturating_add(T::DbWeight::get().reads(2_u64)) + .saturating_add(T::DbWeight::get().writes(2_u64)) } - // Storage: Tips Tips (r:1 w:1) - // Storage: Tips Reasons (r:0 w:1) + /// Storage: Tips Tips (r:1 w:1) + /// Proof Skipped: Tips Tips (max_values: None, max_size: None, mode: Measured) + /// Storage: Tips Reasons (r:0 w:1) + /// Proof Skipped: Tips Reasons (max_values: None, max_size: None, mode: Measured) fn retract_tip() -> Weight { - // Minimum execution time: 34_322 nanoseconds. - Weight::from_ref_time(35_292_000 as u64) - .saturating_add(T::DbWeight::get().reads(1 as u64)) - .saturating_add(T::DbWeight::get().writes(2 as u64)) + // Proof Size summary in bytes: + // Measured: `253` + // Estimated: `2981` + // Minimum execution time: 22_282 nanoseconds. + Weight::from_parts(22_737_000, 2981) + .saturating_add(T::DbWeight::get().reads(1_u64)) + .saturating_add(T::DbWeight::get().writes(2_u64)) } - // Storage: Elections Members (r:1 w:0) - // Storage: Tips Reasons (r:1 w:1) - // Storage: Tips Tips (r:0 w:1) + /// Storage: Elections Members (r:1 w:0) + /// Proof Skipped: Elections Members (max_values: Some(1), max_size: None, mode: Measured) + /// Storage: Tips Reasons (r:1 w:1) + /// Proof Skipped: Tips Reasons (max_values: None, max_size: None, mode: Measured) + /// Storage: Tips Tips (r:0 w:1) + /// Proof Skipped: Tips Tips (max_values: None, max_size: None, mode: Measured) /// The range of component `r` is `[0, 300]`. /// The range of component `t` is `[1, 13]`. fn tip_new(r: u32, t: u32, ) -> Weight { - // Minimum execution time: 26_691 nanoseconds. - Weight::from_ref_time(27_313_497 as u64) - // Standard Error: 141 - .saturating_add(Weight::from_ref_time(818 as u64).saturating_mul(r as u64)) - // Standard Error: 3_352 - .saturating_add(Weight::from_ref_time(108_557 as u64).saturating_mul(t as u64)) - .saturating_add(T::DbWeight::get().reads(2 as u64)) - .saturating_add(T::DbWeight::get().writes(2 as u64)) + // Proof Size summary in bytes: + // Measured: `558 + t * (64 ±0)` + // Estimated: `4644 + t * (192 ±0)` + // Minimum execution time: 18_382 nanoseconds. + Weight::from_parts(18_195_288, 4644) + // Standard Error: 103 + .saturating_add(Weight::from_ref_time(2_096).saturating_mul(r.into())) + // Standard Error: 2_469 + .saturating_add(Weight::from_ref_time(97_092).saturating_mul(t.into())) + .saturating_add(T::DbWeight::get().reads(2_u64)) + .saturating_add(T::DbWeight::get().writes(2_u64)) + .saturating_add(Weight::from_proof_size(192).saturating_mul(t.into())) } - // Storage: Elections Members (r:1 w:0) - // Storage: Tips Tips (r:1 w:1) + /// Storage: Elections Members (r:1 w:0) + /// Proof Skipped: Elections Members (max_values: Some(1), max_size: None, mode: Measured) + /// Storage: Tips Tips (r:1 w:1) + /// Proof Skipped: Tips Tips (max_values: None, max_size: None, mode: Measured) /// The range of component `t` is `[1, 13]`. fn tip(t: u32, ) -> Weight { - // Minimum execution time: 17_464 nanoseconds. - Weight::from_ref_time(17_621_090 as u64) - // Standard Error: 3_702 - .saturating_add(Weight::from_ref_time(269_919 as u64).saturating_mul(t as u64)) - .saturating_add(T::DbWeight::get().reads(2 as u64)) - .saturating_add(T::DbWeight::get().writes(1 as u64)) + // Proof Size summary in bytes: + // Measured: `811 + t * (112 ±0)` + // Estimated: `4592 + t * (224 ±0)` + // Minimum execution time: 13_564 nanoseconds. + Weight::from_parts(13_867_280, 4592) + // Standard Error: 4_245 + .saturating_add(Weight::from_ref_time(206_587).saturating_mul(t.into())) + .saturating_add(T::DbWeight::get().reads(2_u64)) + .saturating_add(T::DbWeight::get().writes(1_u64)) + .saturating_add(Weight::from_proof_size(224).saturating_mul(t.into())) } - // Storage: Tips Tips (r:1 w:1) - // Storage: Elections Members (r:1 w:0) - // Storage: System Account (r:1 w:1) - // Storage: Tips Reasons (r:0 w:1) + /// Storage: Tips Tips (r:1 w:1) + /// Proof Skipped: Tips Tips (max_values: None, max_size: None, mode: Measured) + /// Storage: Elections Members (r:1 w:0) + /// Proof Skipped: Elections Members (max_values: Some(1), max_size: None, mode: Measured) + /// Storage: System Account (r:1 w:1) + /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) + /// Storage: Tips Reasons (r:0 w:1) + /// Proof Skipped: Tips Reasons (max_values: None, max_size: None, mode: Measured) /// The range of component `t` is `[1, 13]`. fn close_tip(t: u32, ) -> Weight { - // Minimum execution time: 52_221 nanoseconds. - Weight::from_ref_time(53_168_303 as u64) - // Standard Error: 6_591 - .saturating_add(Weight::from_ref_time(243_706 as u64).saturating_mul(t as u64)) - .saturating_add(T::DbWeight::get().reads(3 as u64)) - .saturating_add(T::DbWeight::get().writes(3 as u64)) + // Proof Size summary in bytes: + // Measured: `850 + t * (112 ±0)` + // Estimated: `8096 + t * (336 ±0)` + // Minimum execution time: 39_902 nanoseconds. + Weight::from_parts(40_747_650, 8096) + // Standard Error: 5_322 + .saturating_add(Weight::from_ref_time(144_298).saturating_mul(t.into())) + .saturating_add(T::DbWeight::get().reads(3_u64)) + .saturating_add(T::DbWeight::get().writes(3_u64)) + .saturating_add(Weight::from_proof_size(336).saturating_mul(t.into())) } - // Storage: Tips Tips (r:1 w:1) - // Storage: Tips Reasons (r:0 w:1) + /// Storage: Tips Tips (r:1 w:1) + /// Proof Skipped: Tips Tips (max_values: None, max_size: None, mode: Measured) + /// Storage: Tips Reasons (r:0 w:1) + /// Proof Skipped: Tips Reasons (max_values: None, max_size: None, mode: Measured) /// The range of component `t` is `[1, 13]`. fn slash_tip(t: u32, ) -> Weight { - // Minimum execution time: 22_911 nanoseconds. - Weight::from_ref_time(23_750_488 as u64) - // Standard Error: 2_561 - .saturating_add(Weight::from_ref_time(12_282 as u64).saturating_mul(t as u64)) - .saturating_add(T::DbWeight::get().reads(1 as u64)) - .saturating_add(T::DbWeight::get().writes(2 as u64)) + // Proof Size summary in bytes: + // Measured: `301` + // Estimated: `3077` + // Minimum execution time: 13_511 nanoseconds. + Weight::from_parts(14_114_101, 3077) + // Standard Error: 1_815 + .saturating_add(Weight::from_ref_time(7_825).saturating_mul(t.into())) + .saturating_add(T::DbWeight::get().reads(1_u64)) + .saturating_add(T::DbWeight::get().writes(2_u64)) } } // For backwards compatibility and tests impl WeightInfo for () { - // Storage: Tips Reasons (r:1 w:1) - // Storage: Tips Tips (r:1 w:1) + /// Storage: Tips Reasons (r:1 w:1) + /// Proof Skipped: Tips Reasons (max_values: None, max_size: None, mode: Measured) + /// Storage: Tips Tips (r:1 w:1) + /// Proof Skipped: Tips Tips (max_values: None, max_size: None, mode: Measured) /// The range of component `r` is `[0, 300]`. fn report_awesome(r: u32, ) -> Weight { - // Minimum execution time: 35_458 nanoseconds. - Weight::from_ref_time(36_920_009 as u64) - // Standard Error: 252 - .saturating_add(Weight::from_ref_time(1_835 as u64).saturating_mul(r as u64)) - .saturating_add(RocksDbWeight::get().reads(2 as u64)) - .saturating_add(RocksDbWeight::get().writes(2 as u64)) + // Proof Size summary in bytes: + // Measured: `4` + // Estimated: `4958` + // Minimum execution time: 23_262 nanoseconds. + Weight::from_parts(24_104_224, 4958) + // Standard Error: 148 + .saturating_add(Weight::from_ref_time(1_963).saturating_mul(r.into())) + .saturating_add(RocksDbWeight::get().reads(2_u64)) + .saturating_add(RocksDbWeight::get().writes(2_u64)) } - // Storage: Tips Tips (r:1 w:1) - // Storage: Tips Reasons (r:0 w:1) + /// Storage: Tips Tips (r:1 w:1) + /// Proof Skipped: Tips Tips (max_values: None, max_size: None, mode: Measured) + /// Storage: Tips Reasons (r:0 w:1) + /// Proof Skipped: Tips Reasons (max_values: None, max_size: None, mode: Measured) fn retract_tip() -> Weight { - // Minimum execution time: 34_322 nanoseconds. - Weight::from_ref_time(35_292_000 as u64) - .saturating_add(RocksDbWeight::get().reads(1 as u64)) - .saturating_add(RocksDbWeight::get().writes(2 as u64)) + // Proof Size summary in bytes: + // Measured: `253` + // Estimated: `2981` + // Minimum execution time: 22_282 nanoseconds. + Weight::from_parts(22_737_000, 2981) + .saturating_add(RocksDbWeight::get().reads(1_u64)) + .saturating_add(RocksDbWeight::get().writes(2_u64)) } - // Storage: Elections Members (r:1 w:0) - // Storage: Tips Reasons (r:1 w:1) - // Storage: Tips Tips (r:0 w:1) + /// Storage: Elections Members (r:1 w:0) + /// Proof Skipped: Elections Members (max_values: Some(1), max_size: None, mode: Measured) + /// Storage: Tips Reasons (r:1 w:1) + /// Proof Skipped: Tips Reasons (max_values: None, max_size: None, mode: Measured) + /// Storage: Tips Tips (r:0 w:1) + /// Proof Skipped: Tips Tips (max_values: None, max_size: None, mode: Measured) /// The range of component `r` is `[0, 300]`. /// The range of component `t` is `[1, 13]`. fn tip_new(r: u32, t: u32, ) -> Weight { - // Minimum execution time: 26_691 nanoseconds. - Weight::from_ref_time(27_313_497 as u64) - // Standard Error: 141 - .saturating_add(Weight::from_ref_time(818 as u64).saturating_mul(r as u64)) - // Standard Error: 3_352 - .saturating_add(Weight::from_ref_time(108_557 as u64).saturating_mul(t as u64)) - .saturating_add(RocksDbWeight::get().reads(2 as u64)) - .saturating_add(RocksDbWeight::get().writes(2 as u64)) + // Proof Size summary in bytes: + // Measured: `558 + t * (64 ±0)` + // Estimated: `4644 + t * (192 ±0)` + // Minimum execution time: 18_382 nanoseconds. + Weight::from_parts(18_195_288, 4644) + // Standard Error: 103 + .saturating_add(Weight::from_ref_time(2_096).saturating_mul(r.into())) + // Standard Error: 2_469 + .saturating_add(Weight::from_ref_time(97_092).saturating_mul(t.into())) + .saturating_add(RocksDbWeight::get().reads(2_u64)) + .saturating_add(RocksDbWeight::get().writes(2_u64)) + .saturating_add(Weight::from_proof_size(192).saturating_mul(t.into())) } - // Storage: Elections Members (r:1 w:0) - // Storage: Tips Tips (r:1 w:1) + /// Storage: Elections Members (r:1 w:0) + /// Proof Skipped: Elections Members (max_values: Some(1), max_size: None, mode: Measured) + /// Storage: Tips Tips (r:1 w:1) + /// Proof Skipped: Tips Tips (max_values: None, max_size: None, mode: Measured) /// The range of component `t` is `[1, 13]`. fn tip(t: u32, ) -> Weight { - // Minimum execution time: 17_464 nanoseconds. - Weight::from_ref_time(17_621_090 as u64) - // Standard Error: 3_702 - .saturating_add(Weight::from_ref_time(269_919 as u64).saturating_mul(t as u64)) - .saturating_add(RocksDbWeight::get().reads(2 as u64)) - .saturating_add(RocksDbWeight::get().writes(1 as u64)) + // Proof Size summary in bytes: + // Measured: `811 + t * (112 ±0)` + // Estimated: `4592 + t * (224 ±0)` + // Minimum execution time: 13_564 nanoseconds. + Weight::from_parts(13_867_280, 4592) + // Standard Error: 4_245 + .saturating_add(Weight::from_ref_time(206_587).saturating_mul(t.into())) + .saturating_add(RocksDbWeight::get().reads(2_u64)) + .saturating_add(RocksDbWeight::get().writes(1_u64)) + .saturating_add(Weight::from_proof_size(224).saturating_mul(t.into())) } - // Storage: Tips Tips (r:1 w:1) - // Storage: Elections Members (r:1 w:0) - // Storage: System Account (r:1 w:1) - // Storage: Tips Reasons (r:0 w:1) + /// Storage: Tips Tips (r:1 w:1) + /// Proof Skipped: Tips Tips (max_values: None, max_size: None, mode: Measured) + /// Storage: Elections Members (r:1 w:0) + /// Proof Skipped: Elections Members (max_values: Some(1), max_size: None, mode: Measured) + /// Storage: System Account (r:1 w:1) + /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) + /// Storage: Tips Reasons (r:0 w:1) + /// Proof Skipped: Tips Reasons (max_values: None, max_size: None, mode: Measured) /// The range of component `t` is `[1, 13]`. fn close_tip(t: u32, ) -> Weight { - // Minimum execution time: 52_221 nanoseconds. - Weight::from_ref_time(53_168_303 as u64) - // Standard Error: 6_591 - .saturating_add(Weight::from_ref_time(243_706 as u64).saturating_mul(t as u64)) - .saturating_add(RocksDbWeight::get().reads(3 as u64)) - .saturating_add(RocksDbWeight::get().writes(3 as u64)) + // Proof Size summary in bytes: + // Measured: `850 + t * (112 ±0)` + // Estimated: `8096 + t * (336 ±0)` + // Minimum execution time: 39_902 nanoseconds. + Weight::from_parts(40_747_650, 8096) + // Standard Error: 5_322 + .saturating_add(Weight::from_ref_time(144_298).saturating_mul(t.into())) + .saturating_add(RocksDbWeight::get().reads(3_u64)) + .saturating_add(RocksDbWeight::get().writes(3_u64)) + .saturating_add(Weight::from_proof_size(336).saturating_mul(t.into())) } - // Storage: Tips Tips (r:1 w:1) - // Storage: Tips Reasons (r:0 w:1) + /// Storage: Tips Tips (r:1 w:1) + /// Proof Skipped: Tips Tips (max_values: None, max_size: None, mode: Measured) + /// Storage: Tips Reasons (r:0 w:1) + /// Proof Skipped: Tips Reasons (max_values: None, max_size: None, mode: Measured) /// The range of component `t` is `[1, 13]`. fn slash_tip(t: u32, ) -> Weight { - // Minimum execution time: 22_911 nanoseconds. - Weight::from_ref_time(23_750_488 as u64) - // Standard Error: 2_561 - .saturating_add(Weight::from_ref_time(12_282 as u64).saturating_mul(t as u64)) - .saturating_add(RocksDbWeight::get().reads(1 as u64)) - .saturating_add(RocksDbWeight::get().writes(2 as u64)) + // Proof Size summary in bytes: + // Measured: `301` + // Estimated: `3077` + // Minimum execution time: 13_511 nanoseconds. + Weight::from_parts(14_114_101, 3077) + // Standard Error: 1_815 + .saturating_add(Weight::from_ref_time(7_825).saturating_mul(t.into())) + .saturating_add(RocksDbWeight::get().reads(1_u64)) + .saturating_add(RocksDbWeight::get().writes(2_u64)) } } diff --git a/frame/transaction-storage/src/weights.rs b/frame/transaction-storage/src/weights.rs index 16d12aa75..9ee7b37b3 100644 --- a/frame/transaction-storage/src/weights.rs +++ b/frame/transaction-storage/src/weights.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2022 Parity Technologies (UK) Ltd. +// Copyright (C) 2023 Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); @@ -18,7 +18,8 @@ //! Autogenerated weights for pallet_transaction_storage //! //! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev -//! DATE: 2022-11-07, STEPS: `50`, REPEAT: 20, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! DATE: 2023-01-25, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! WORST CASE MAP SIZE: `1000000` //! HOSTNAME: `bm2`, CPU: `Intel(R) Core(TM) i7-7700K CPU @ 4.20GHz` //! EXECUTION: Some(Wasm), WASM-EXECUTION: Compiled, CHAIN: Some("dev"), DB CACHE: 1024 @@ -55,78 +56,116 @@ pub trait WeightInfo { /// Weights for pallet_transaction_storage using the Substrate node and recommended hardware. pub struct SubstrateWeight(PhantomData); impl WeightInfo for SubstrateWeight { - // Storage: TransactionStorage ByteFee (r:1 w:0) - // Storage: TransactionStorage EntryFee (r:1 w:0) - // Storage: unknown [0x3a65787472696e7369635f696e646578] (r:1 w:0) - // Storage: TransactionStorage BlockTransactions (r:1 w:1) + /// Storage: TransactionStorage ByteFee (r:1 w:0) + /// Proof: TransactionStorage ByteFee (max_values: Some(1), max_size: Some(16), added: 511, mode: MaxEncodedLen) + /// Storage: TransactionStorage EntryFee (r:1 w:0) + /// Proof: TransactionStorage EntryFee (max_values: Some(1), max_size: Some(16), added: 511, mode: MaxEncodedLen) + /// Storage: TransactionStorage BlockTransactions (r:1 w:1) + /// Proof: TransactionStorage BlockTransactions (max_values: Some(1), max_size: Some(36866), added: 37361, mode: MaxEncodedLen) /// The range of component `l` is `[1, 8388608]`. fn store(l: u32, ) -> Weight { - // Minimum execution time: 46_730 nanoseconds. - Weight::from_ref_time(46_922_000 as u64) + // Proof Size summary in bytes: + // Measured: `176` + // Estimated: `38383` + // Minimum execution time: 28_702 nanoseconds. + Weight::from_parts(29_164_000, 38383) // Standard Error: 2 - .saturating_add(Weight::from_ref_time(5_601 as u64).saturating_mul(l as u64)) - .saturating_add(T::DbWeight::get().reads(4 as u64)) - .saturating_add(T::DbWeight::get().writes(1 as u64)) + .saturating_add(Weight::from_ref_time(5_601).saturating_mul(l.into())) + .saturating_add(T::DbWeight::get().reads(3_u64)) + .saturating_add(T::DbWeight::get().writes(1_u64)) } - // Storage: TransactionStorage Transactions (r:1 w:0) - // Storage: unknown [0x3a65787472696e7369635f696e646578] (r:1 w:0) - // Storage: TransactionStorage ByteFee (r:1 w:0) - // Storage: TransactionStorage EntryFee (r:1 w:0) - // Storage: TransactionStorage BlockTransactions (r:1 w:1) + /// Storage: TransactionStorage Transactions (r:1 w:0) + /// Proof: TransactionStorage Transactions (max_values: None, max_size: Some(36886), added: 39361, mode: MaxEncodedLen) + /// Storage: TransactionStorage ByteFee (r:1 w:0) + /// Proof: TransactionStorage ByteFee (max_values: Some(1), max_size: Some(16), added: 511, mode: MaxEncodedLen) + /// Storage: TransactionStorage EntryFee (r:1 w:0) + /// Proof: TransactionStorage EntryFee (max_values: Some(1), max_size: Some(16), added: 511, mode: MaxEncodedLen) + /// Storage: TransactionStorage BlockTransactions (r:1 w:1) + /// Proof: TransactionStorage BlockTransactions (max_values: Some(1), max_size: Some(36866), added: 37361, mode: MaxEncodedLen) fn renew() -> Weight { - // Minimum execution time: 56_802 nanoseconds. - Weight::from_ref_time(58_670_000 as u64) - .saturating_add(T::DbWeight::get().reads(5 as u64)) - .saturating_add(T::DbWeight::get().writes(1 as u64)) + // Proof Size summary in bytes: + // Measured: `358` + // Estimated: `77744` + // Minimum execution time: 36_219 nanoseconds. + Weight::from_parts(36_979_000, 77744) + .saturating_add(T::DbWeight::get().reads(4_u64)) + .saturating_add(T::DbWeight::get().writes(1_u64)) } - // Storage: TransactionStorage ProofChecked (r:1 w:1) - // Storage: TransactionStorage StoragePeriod (r:1 w:0) - // Storage: TransactionStorage ChunkCount (r:1 w:0) - // Storage: System ParentHash (r:1 w:0) - // Storage: TransactionStorage Transactions (r:1 w:0) + /// Storage: TransactionStorage ProofChecked (r:1 w:1) + /// Proof: TransactionStorage ProofChecked (max_values: Some(1), max_size: Some(1), added: 496, mode: MaxEncodedLen) + /// Storage: TransactionStorage StoragePeriod (r:1 w:0) + /// Proof: TransactionStorage StoragePeriod (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) + /// Storage: TransactionStorage ChunkCount (r:1 w:0) + /// Proof: TransactionStorage ChunkCount (max_values: None, max_size: Some(24), added: 2499, mode: MaxEncodedLen) + /// Storage: System ParentHash (r:1 w:0) + /// Proof: System ParentHash (max_values: Some(1), max_size: Some(32), added: 527, mode: MaxEncodedLen) + /// Storage: TransactionStorage Transactions (r:1 w:0) + /// Proof: TransactionStorage Transactions (max_values: None, max_size: Some(36886), added: 39361, mode: MaxEncodedLen) fn check_proof_max() -> Weight { - // Minimum execution time: 74_016 nanoseconds. - Weight::from_ref_time(94_111_000 as u64) - .saturating_add(T::DbWeight::get().reads(5 as u64)) - .saturating_add(T::DbWeight::get().writes(1 as u64)) + // Proof Size summary in bytes: + // Measured: `37177` + // Estimated: `43382` + // Minimum execution time: 55_793 nanoseconds. + Weight::from_parts(57_128_000, 43382) + .saturating_add(T::DbWeight::get().reads(5_u64)) + .saturating_add(T::DbWeight::get().writes(1_u64)) } } // For backwards compatibility and tests impl WeightInfo for () { - // Storage: TransactionStorage ByteFee (r:1 w:0) - // Storage: TransactionStorage EntryFee (r:1 w:0) - // Storage: unknown [0x3a65787472696e7369635f696e646578] (r:1 w:0) - // Storage: TransactionStorage BlockTransactions (r:1 w:1) + /// Storage: TransactionStorage ByteFee (r:1 w:0) + /// Proof: TransactionStorage ByteFee (max_values: Some(1), max_size: Some(16), added: 511, mode: MaxEncodedLen) + /// Storage: TransactionStorage EntryFee (r:1 w:0) + /// Proof: TransactionStorage EntryFee (max_values: Some(1), max_size: Some(16), added: 511, mode: MaxEncodedLen) + /// Storage: TransactionStorage BlockTransactions (r:1 w:1) + /// Proof: TransactionStorage BlockTransactions (max_values: Some(1), max_size: Some(36866), added: 37361, mode: MaxEncodedLen) /// The range of component `l` is `[1, 8388608]`. fn store(l: u32, ) -> Weight { - // Minimum execution time: 46_730 nanoseconds. - Weight::from_ref_time(46_922_000 as u64) + // Proof Size summary in bytes: + // Measured: `176` + // Estimated: `38383` + // Minimum execution time: 28_702 nanoseconds. + Weight::from_parts(29_164_000, 38383) // Standard Error: 2 - .saturating_add(Weight::from_ref_time(5_601 as u64).saturating_mul(l as u64)) - .saturating_add(RocksDbWeight::get().reads(4 as u64)) - .saturating_add(RocksDbWeight::get().writes(1 as u64)) + .saturating_add(Weight::from_ref_time(5_601).saturating_mul(l.into())) + .saturating_add(RocksDbWeight::get().reads(3_u64)) + .saturating_add(RocksDbWeight::get().writes(1_u64)) } - // Storage: TransactionStorage Transactions (r:1 w:0) - // Storage: unknown [0x3a65787472696e7369635f696e646578] (r:1 w:0) - // Storage: TransactionStorage ByteFee (r:1 w:0) - // Storage: TransactionStorage EntryFee (r:1 w:0) - // Storage: TransactionStorage BlockTransactions (r:1 w:1) + /// Storage: TransactionStorage Transactions (r:1 w:0) + /// Proof: TransactionStorage Transactions (max_values: None, max_size: Some(36886), added: 39361, mode: MaxEncodedLen) + /// Storage: TransactionStorage ByteFee (r:1 w:0) + /// Proof: TransactionStorage ByteFee (max_values: Some(1), max_size: Some(16), added: 511, mode: MaxEncodedLen) + /// Storage: TransactionStorage EntryFee (r:1 w:0) + /// Proof: TransactionStorage EntryFee (max_values: Some(1), max_size: Some(16), added: 511, mode: MaxEncodedLen) + /// Storage: TransactionStorage BlockTransactions (r:1 w:1) + /// Proof: TransactionStorage BlockTransactions (max_values: Some(1), max_size: Some(36866), added: 37361, mode: MaxEncodedLen) fn renew() -> Weight { - // Minimum execution time: 56_802 nanoseconds. - Weight::from_ref_time(58_670_000 as u64) - .saturating_add(RocksDbWeight::get().reads(5 as u64)) - .saturating_add(RocksDbWeight::get().writes(1 as u64)) + // Proof Size summary in bytes: + // Measured: `358` + // Estimated: `77744` + // Minimum execution time: 36_219 nanoseconds. + Weight::from_parts(36_979_000, 77744) + .saturating_add(RocksDbWeight::get().reads(4_u64)) + .saturating_add(RocksDbWeight::get().writes(1_u64)) } - // Storage: TransactionStorage ProofChecked (r:1 w:1) - // Storage: TransactionStorage StoragePeriod (r:1 w:0) - // Storage: TransactionStorage ChunkCount (r:1 w:0) - // Storage: System ParentHash (r:1 w:0) - // Storage: TransactionStorage Transactions (r:1 w:0) + /// Storage: TransactionStorage ProofChecked (r:1 w:1) + /// Proof: TransactionStorage ProofChecked (max_values: Some(1), max_size: Some(1), added: 496, mode: MaxEncodedLen) + /// Storage: TransactionStorage StoragePeriod (r:1 w:0) + /// Proof: TransactionStorage StoragePeriod (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) + /// Storage: TransactionStorage ChunkCount (r:1 w:0) + /// Proof: TransactionStorage ChunkCount (max_values: None, max_size: Some(24), added: 2499, mode: MaxEncodedLen) + /// Storage: System ParentHash (r:1 w:0) + /// Proof: System ParentHash (max_values: Some(1), max_size: Some(32), added: 527, mode: MaxEncodedLen) + /// Storage: TransactionStorage Transactions (r:1 w:0) + /// Proof: TransactionStorage Transactions (max_values: None, max_size: Some(36886), added: 39361, mode: MaxEncodedLen) fn check_proof_max() -> Weight { - // Minimum execution time: 74_016 nanoseconds. - Weight::from_ref_time(94_111_000 as u64) - .saturating_add(RocksDbWeight::get().reads(5 as u64)) - .saturating_add(RocksDbWeight::get().writes(1 as u64)) + // Proof Size summary in bytes: + // Measured: `37177` + // Estimated: `43382` + // Minimum execution time: 55_793 nanoseconds. + Weight::from_parts(57_128_000, 43382) + .saturating_add(RocksDbWeight::get().reads(5_u64)) + .saturating_add(RocksDbWeight::get().writes(1_u64)) } } diff --git a/frame/treasury/src/weights.rs b/frame/treasury/src/weights.rs index 3ee071ac7..aff593d88 100644 --- a/frame/treasury/src/weights.rs +++ b/frame/treasury/src/weights.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2022 Parity Technologies (UK) Ltd. +// Copyright (C) 2023 Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); @@ -18,7 +18,8 @@ //! Autogenerated weights for pallet_treasury //! //! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev -//! DATE: 2022-11-07, STEPS: `50`, REPEAT: 20, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! DATE: 2023-01-25, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! WORST CASE MAP SIZE: `1000000` //! HOSTNAME: `bm2`, CPU: `Intel(R) Core(TM) i7-7700K CPU @ 4.20GHz` //! EXECUTION: Some(Wasm), WASM-EXECUTION: Compiled, CHAIN: Some("dev"), DB CACHE: 1024 @@ -58,114 +59,194 @@ pub trait WeightInfo { /// Weights for pallet_treasury using the Substrate node and recommended hardware. pub struct SubstrateWeight(PhantomData); impl WeightInfo for SubstrateWeight { + /// Storage: Treasury ProposalCount (r:1 w:1) + /// Proof: Treasury ProposalCount (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) + /// Storage: Treasury Approvals (r:1 w:1) + /// Proof: Treasury Approvals (max_values: Some(1), max_size: Some(402), added: 897, mode: MaxEncodedLen) + /// Storage: Treasury Proposals (r:0 w:1) + /// Proof: Treasury Proposals (max_values: None, max_size: Some(108), added: 2583, mode: MaxEncodedLen) fn spend() -> Weight { - // Minimum execution time: 137 nanoseconds. - Weight::from_ref_time(153_000 as u64) + // Proof Size summary in bytes: + // Measured: `42` + // Estimated: `1396` + // Minimum execution time: 14_277 nanoseconds. + Weight::from_parts(14_749_000, 1396) + .saturating_add(T::DbWeight::get().reads(2_u64)) + .saturating_add(T::DbWeight::get().writes(3_u64)) } - // Storage: Treasury ProposalCount (r:1 w:1) - // Storage: Treasury Proposals (r:0 w:1) + /// Storage: Treasury ProposalCount (r:1 w:1) + /// Proof: Treasury ProposalCount (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) + /// Storage: Treasury Proposals (r:0 w:1) + /// Proof: Treasury Proposals (max_values: None, max_size: Some(108), added: 2583, mode: MaxEncodedLen) fn propose_spend() -> Weight { - // Minimum execution time: 31_437 nanoseconds. - Weight::from_ref_time(32_241_000 as u64) - .saturating_add(T::DbWeight::get().reads(1 as u64)) - .saturating_add(T::DbWeight::get().writes(2 as u64)) + // Proof Size summary in bytes: + // Measured: `175` + // Estimated: `499` + // Minimum execution time: 23_297 nanoseconds. + Weight::from_parts(23_585_000, 499) + .saturating_add(T::DbWeight::get().reads(1_u64)) + .saturating_add(T::DbWeight::get().writes(2_u64)) } - // Storage: Treasury Proposals (r:1 w:1) - // Storage: System Account (r:1 w:1) + /// Storage: Treasury Proposals (r:1 w:1) + /// Proof: Treasury Proposals (max_values: None, max_size: Some(108), added: 2583, mode: MaxEncodedLen) + /// Storage: System Account (r:1 w:1) + /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) fn reject_proposal() -> Weight { - // Minimum execution time: 38_351 nanoseconds. - Weight::from_ref_time(38_828_000 as u64) - .saturating_add(T::DbWeight::get().reads(2 as u64)) - .saturating_add(T::DbWeight::get().writes(2 as u64)) + // Proof Size summary in bytes: + // Measured: `365` + // Estimated: `5186` + // Minimum execution time: 23_996 nanoseconds. + Weight::from_parts(24_548_000, 5186) + .saturating_add(T::DbWeight::get().reads(2_u64)) + .saturating_add(T::DbWeight::get().writes(2_u64)) } - // Storage: Treasury Proposals (r:1 w:0) - // Storage: Treasury Approvals (r:1 w:1) + /// Storage: Treasury Proposals (r:1 w:0) + /// Proof: Treasury Proposals (max_values: None, max_size: Some(108), added: 2583, mode: MaxEncodedLen) + /// Storage: Treasury Approvals (r:1 w:1) + /// Proof: Treasury Approvals (max_values: Some(1), max_size: Some(402), added: 897, mode: MaxEncodedLen) /// The range of component `p` is `[0, 99]`. fn approve_proposal(p: u32, ) -> Weight { - // Minimum execution time: 11_937 nanoseconds. - Weight::from_ref_time(15_541_763 as u64) - // Standard Error: 1_036 - .saturating_add(Weight::from_ref_time(128_326 as u64).saturating_mul(p as u64)) - .saturating_add(T::DbWeight::get().reads(2 as u64)) - .saturating_add(T::DbWeight::get().writes(1 as u64)) + // Proof Size summary in bytes: + // Measured: `501 + p * (8 ±0)` + // Estimated: `3480` + // Minimum execution time: 9_366 nanoseconds. + Weight::from_parts(11_731_455, 3480) + // Standard Error: 761 + .saturating_add(Weight::from_ref_time(21_665).saturating_mul(p.into())) + .saturating_add(T::DbWeight::get().reads(2_u64)) + .saturating_add(T::DbWeight::get().writes(1_u64)) } - // Storage: Treasury Approvals (r:1 w:1) + /// Storage: Treasury Approvals (r:1 w:1) + /// Proof: Treasury Approvals (max_values: Some(1), max_size: Some(402), added: 897, mode: MaxEncodedLen) fn remove_approval() -> Weight { - // Minimum execution time: 9_611 nanoseconds. - Weight::from_ref_time(10_012_000 as u64) - .saturating_add(T::DbWeight::get().reads(1 as u64)) - .saturating_add(T::DbWeight::get().writes(1 as u64)) + // Proof Size summary in bytes: + // Measured: `127` + // Estimated: `897` + // Minimum execution time: 7_012 nanoseconds. + Weight::from_parts(7_270_000, 897) + .saturating_add(T::DbWeight::get().reads(1_u64)) + .saturating_add(T::DbWeight::get().writes(1_u64)) } - // Storage: Treasury Approvals (r:1 w:1) - // Storage: Bounties BountyApprovals (r:1 w:1) - // Storage: Treasury Proposals (r:2 w:2) - // Storage: System Account (r:4 w:4) + /// Storage: Treasury Deactivated (r:1 w:1) + /// Proof: Treasury Deactivated (max_values: Some(1), max_size: Some(16), added: 511, mode: MaxEncodedLen) + /// Storage: Treasury Approvals (r:1 w:1) + /// Proof: Treasury Approvals (max_values: Some(1), max_size: Some(402), added: 897, mode: MaxEncodedLen) + /// Storage: Treasury Proposals (r:100 w:100) + /// Proof: Treasury Proposals (max_values: None, max_size: Some(108), added: 2583, mode: MaxEncodedLen) + /// Storage: System Account (r:200 w:200) + /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) + /// Storage: Bounties BountyApprovals (r:1 w:1) + /// Proof: Bounties BountyApprovals (max_values: Some(1), max_size: Some(402), added: 897, mode: MaxEncodedLen) /// The range of component `p` is `[0, 100]`. fn on_initialize_proposals(p: u32, ) -> Weight { - // Minimum execution time: 43_016 nanoseconds. - Weight::from_ref_time(56_538_751 as u64) - // Standard Error: 14_890 - .saturating_add(Weight::from_ref_time(26_789_120 as u64).saturating_mul(p as u64)) - .saturating_add(T::DbWeight::get().reads(2 as u64)) - .saturating_add(T::DbWeight::get().reads((3 as u64).saturating_mul(p as u64))) - .saturating_add(T::DbWeight::get().writes(2 as u64)) - .saturating_add(T::DbWeight::get().writes((3 as u64).saturating_mul(p as u64))) + // Proof Size summary in bytes: + // Measured: `415 + p * (314 ±0)` + // Estimated: `2305 + p * (7789 ±0)` + // Minimum execution time: 37_834 nanoseconds. + Weight::from_parts(47_496_917, 2305) + // Standard Error: 12_505 + .saturating_add(Weight::from_ref_time(26_902_474).saturating_mul(p.into())) + .saturating_add(T::DbWeight::get().reads(3_u64)) + .saturating_add(T::DbWeight::get().reads((3_u64).saturating_mul(p.into()))) + .saturating_add(T::DbWeight::get().writes(3_u64)) + .saturating_add(T::DbWeight::get().writes((3_u64).saturating_mul(p.into()))) + .saturating_add(Weight::from_proof_size(7789).saturating_mul(p.into())) } } // For backwards compatibility and tests impl WeightInfo for () { + /// Storage: Treasury ProposalCount (r:1 w:1) + /// Proof: Treasury ProposalCount (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) + /// Storage: Treasury Approvals (r:1 w:1) + /// Proof: Treasury Approvals (max_values: Some(1), max_size: Some(402), added: 897, mode: MaxEncodedLen) + /// Storage: Treasury Proposals (r:0 w:1) + /// Proof: Treasury Proposals (max_values: None, max_size: Some(108), added: 2583, mode: MaxEncodedLen) fn spend() -> Weight { - // Minimum execution time: 137 nanoseconds. - Weight::from_ref_time(153_000 as u64) + // Proof Size summary in bytes: + // Measured: `42` + // Estimated: `1396` + // Minimum execution time: 14_277 nanoseconds. + Weight::from_parts(14_749_000, 1396) + .saturating_add(RocksDbWeight::get().reads(2_u64)) + .saturating_add(RocksDbWeight::get().writes(3_u64)) } - // Storage: Treasury ProposalCount (r:1 w:1) - // Storage: Treasury Proposals (r:0 w:1) + /// Storage: Treasury ProposalCount (r:1 w:1) + /// Proof: Treasury ProposalCount (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) + /// Storage: Treasury Proposals (r:0 w:1) + /// Proof: Treasury Proposals (max_values: None, max_size: Some(108), added: 2583, mode: MaxEncodedLen) fn propose_spend() -> Weight { - // Minimum execution time: 31_437 nanoseconds. - Weight::from_ref_time(32_241_000 as u64) - .saturating_add(RocksDbWeight::get().reads(1 as u64)) - .saturating_add(RocksDbWeight::get().writes(2 as u64)) + // Proof Size summary in bytes: + // Measured: `175` + // Estimated: `499` + // Minimum execution time: 23_297 nanoseconds. + Weight::from_parts(23_585_000, 499) + .saturating_add(RocksDbWeight::get().reads(1_u64)) + .saturating_add(RocksDbWeight::get().writes(2_u64)) } - // Storage: Treasury Proposals (r:1 w:1) - // Storage: System Account (r:1 w:1) + /// Storage: Treasury Proposals (r:1 w:1) + /// Proof: Treasury Proposals (max_values: None, max_size: Some(108), added: 2583, mode: MaxEncodedLen) + /// Storage: System Account (r:1 w:1) + /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) fn reject_proposal() -> Weight { - // Minimum execution time: 38_351 nanoseconds. - Weight::from_ref_time(38_828_000 as u64) - .saturating_add(RocksDbWeight::get().reads(2 as u64)) - .saturating_add(RocksDbWeight::get().writes(2 as u64)) + // Proof Size summary in bytes: + // Measured: `365` + // Estimated: `5186` + // Minimum execution time: 23_996 nanoseconds. + Weight::from_parts(24_548_000, 5186) + .saturating_add(RocksDbWeight::get().reads(2_u64)) + .saturating_add(RocksDbWeight::get().writes(2_u64)) } - // Storage: Treasury Proposals (r:1 w:0) - // Storage: Treasury Approvals (r:1 w:1) + /// Storage: Treasury Proposals (r:1 w:0) + /// Proof: Treasury Proposals (max_values: None, max_size: Some(108), added: 2583, mode: MaxEncodedLen) + /// Storage: Treasury Approvals (r:1 w:1) + /// Proof: Treasury Approvals (max_values: Some(1), max_size: Some(402), added: 897, mode: MaxEncodedLen) /// The range of component `p` is `[0, 99]`. fn approve_proposal(p: u32, ) -> Weight { - // Minimum execution time: 11_937 nanoseconds. - Weight::from_ref_time(15_541_763 as u64) - // Standard Error: 1_036 - .saturating_add(Weight::from_ref_time(128_326 as u64).saturating_mul(p as u64)) - .saturating_add(RocksDbWeight::get().reads(2 as u64)) - .saturating_add(RocksDbWeight::get().writes(1 as u64)) + // Proof Size summary in bytes: + // Measured: `501 + p * (8 ±0)` + // Estimated: `3480` + // Minimum execution time: 9_366 nanoseconds. + Weight::from_parts(11_731_455, 3480) + // Standard Error: 761 + .saturating_add(Weight::from_ref_time(21_665).saturating_mul(p.into())) + .saturating_add(RocksDbWeight::get().reads(2_u64)) + .saturating_add(RocksDbWeight::get().writes(1_u64)) } - // Storage: Treasury Approvals (r:1 w:1) + /// Storage: Treasury Approvals (r:1 w:1) + /// Proof: Treasury Approvals (max_values: Some(1), max_size: Some(402), added: 897, mode: MaxEncodedLen) fn remove_approval() -> Weight { - // Minimum execution time: 9_611 nanoseconds. - Weight::from_ref_time(10_012_000 as u64) - .saturating_add(RocksDbWeight::get().reads(1 as u64)) - .saturating_add(RocksDbWeight::get().writes(1 as u64)) + // Proof Size summary in bytes: + // Measured: `127` + // Estimated: `897` + // Minimum execution time: 7_012 nanoseconds. + Weight::from_parts(7_270_000, 897) + .saturating_add(RocksDbWeight::get().reads(1_u64)) + .saturating_add(RocksDbWeight::get().writes(1_u64)) } - // Storage: Treasury Approvals (r:1 w:1) - // Storage: Bounties BountyApprovals (r:1 w:1) - // Storage: Treasury Proposals (r:2 w:2) - // Storage: System Account (r:4 w:4) + /// Storage: Treasury Deactivated (r:1 w:1) + /// Proof: Treasury Deactivated (max_values: Some(1), max_size: Some(16), added: 511, mode: MaxEncodedLen) + /// Storage: Treasury Approvals (r:1 w:1) + /// Proof: Treasury Approvals (max_values: Some(1), max_size: Some(402), added: 897, mode: MaxEncodedLen) + /// Storage: Treasury Proposals (r:100 w:100) + /// Proof: Treasury Proposals (max_values: None, max_size: Some(108), added: 2583, mode: MaxEncodedLen) + /// Storage: System Account (r:200 w:200) + /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) + /// Storage: Bounties BountyApprovals (r:1 w:1) + /// Proof: Bounties BountyApprovals (max_values: Some(1), max_size: Some(402), added: 897, mode: MaxEncodedLen) /// The range of component `p` is `[0, 100]`. fn on_initialize_proposals(p: u32, ) -> Weight { - // Minimum execution time: 43_016 nanoseconds. - Weight::from_ref_time(56_538_751 as u64) - // Standard Error: 14_890 - .saturating_add(Weight::from_ref_time(26_789_120 as u64).saturating_mul(p as u64)) - .saturating_add(RocksDbWeight::get().reads(2 as u64)) - .saturating_add(RocksDbWeight::get().reads((3 as u64).saturating_mul(p as u64))) - .saturating_add(RocksDbWeight::get().writes(2 as u64)) - .saturating_add(RocksDbWeight::get().writes((3 as u64).saturating_mul(p as u64))) + // Proof Size summary in bytes: + // Measured: `415 + p * (314 ±0)` + // Estimated: `2305 + p * (7789 ±0)` + // Minimum execution time: 37_834 nanoseconds. + Weight::from_parts(47_496_917, 2305) + // Standard Error: 12_505 + .saturating_add(Weight::from_ref_time(26_902_474).saturating_mul(p.into())) + .saturating_add(RocksDbWeight::get().reads(3_u64)) + .saturating_add(RocksDbWeight::get().reads((3_u64).saturating_mul(p.into()))) + .saturating_add(RocksDbWeight::get().writes(3_u64)) + .saturating_add(RocksDbWeight::get().writes((3_u64).saturating_mul(p.into()))) + .saturating_add(Weight::from_proof_size(7789).saturating_mul(p.into())) } } diff --git a/frame/uniques/src/weights.rs b/frame/uniques/src/weights.rs index 8a8e1090b..3c1b102ba 100644 --- a/frame/uniques/src/weights.rs +++ b/frame/uniques/src/weights.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2022 Parity Technologies (UK) Ltd. +// Copyright (C) 2023 Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); @@ -18,7 +18,8 @@ //! Autogenerated weights for pallet_uniques //! //! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev -//! DATE: 2022-11-07, STEPS: `50`, REPEAT: 20, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! DATE: 2023-01-25, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! WORST CASE MAP SIZE: `1000000` //! HOSTNAME: `bm2`, CPU: `Intel(R) Core(TM) i7-7700K CPU @ 4.20GHz` //! EXECUTION: Some(Wasm), WASM-EXECUTION: Compiled, CHAIN: Some("dev"), DB CACHE: 1024 @@ -78,486 +79,776 @@ pub trait WeightInfo { /// Weights for pallet_uniques using the Substrate node and recommended hardware. pub struct SubstrateWeight(PhantomData); impl WeightInfo for SubstrateWeight { - // Storage: Uniques Class (r:1 w:1) - // Storage: Uniques ClassAccount (r:0 w:1) + /// Storage: Uniques Class (r:1 w:1) + /// Proof: Uniques Class (max_values: None, max_size: Some(178), added: 2653, mode: MaxEncodedLen) + /// Storage: Uniques ClassAccount (r:0 w:1) + /// Proof: Uniques ClassAccount (max_values: None, max_size: Some(68), added: 2543, mode: MaxEncodedLen) fn create() -> Weight { - // Minimum execution time: 35_358 nanoseconds. - Weight::from_ref_time(35_935_000 as u64) - .saturating_add(T::DbWeight::get().reads(1 as u64)) - .saturating_add(T::DbWeight::get().writes(2 as u64)) - } - // Storage: Uniques Class (r:1 w:1) - // Storage: Uniques ClassAccount (r:0 w:1) + // Proof Size summary in bytes: + // Measured: `281` + // Estimated: `2653` + // Minimum execution time: 24_242 nanoseconds. + Weight::from_parts(24_682_000, 2653) + .saturating_add(T::DbWeight::get().reads(1_u64)) + .saturating_add(T::DbWeight::get().writes(2_u64)) + } + /// Storage: Uniques Class (r:1 w:1) + /// Proof: Uniques Class (max_values: None, max_size: Some(178), added: 2653, mode: MaxEncodedLen) + /// Storage: Uniques ClassAccount (r:0 w:1) + /// Proof: Uniques ClassAccount (max_values: None, max_size: Some(68), added: 2543, mode: MaxEncodedLen) fn force_create() -> Weight { - // Minimum execution time: 22_767 nanoseconds. - Weight::from_ref_time(23_235_000 as u64) - .saturating_add(T::DbWeight::get().reads(1 as u64)) - .saturating_add(T::DbWeight::get().writes(2 as u64)) - } - // Storage: Uniques Class (r:1 w:1) - // Storage: Uniques Asset (r:1 w:0) - // Storage: Uniques ClassAccount (r:0 w:1) - // Storage: Uniques Attribute (r:0 w:1000) - // Storage: Uniques ClassMetadataOf (r:0 w:1) - // Storage: Uniques InstanceMetadataOf (r:0 w:1000) - // Storage: Uniques CollectionMaxSupply (r:0 w:1) - // Storage: Uniques Account (r:0 w:20) + // Proof Size summary in bytes: + // Measured: `109` + // Estimated: `2653` + // Minimum execution time: 14_145 nanoseconds. + Weight::from_parts(14_598_000, 2653) + .saturating_add(T::DbWeight::get().reads(1_u64)) + .saturating_add(T::DbWeight::get().writes(2_u64)) + } + /// Storage: Uniques Class (r:1 w:1) + /// Proof: Uniques Class (max_values: None, max_size: Some(178), added: 2653, mode: MaxEncodedLen) + /// Storage: Uniques Asset (r:1001 w:1000) + /// Proof: Uniques Asset (max_values: None, max_size: Some(122), added: 2597, mode: MaxEncodedLen) + /// Storage: Uniques ClassAccount (r:0 w:1) + /// Proof: Uniques ClassAccount (max_values: None, max_size: Some(68), added: 2543, mode: MaxEncodedLen) + /// Storage: Uniques Attribute (r:0 w:1000) + /// Proof: Uniques Attribute (max_values: None, max_size: Some(364), added: 2839, mode: MaxEncodedLen) + /// Storage: Uniques ClassMetadataOf (r:0 w:1) + /// Proof: Uniques ClassMetadataOf (max_values: None, max_size: Some(88), added: 2563, mode: MaxEncodedLen) + /// Storage: Uniques InstanceMetadataOf (r:0 w:1000) + /// Proof: Uniques InstanceMetadataOf (max_values: None, max_size: Some(108), added: 2583, mode: MaxEncodedLen) + /// Storage: Uniques Account (r:0 w:1000) + /// Proof: Uniques Account (max_values: None, max_size: Some(88), added: 2563, mode: MaxEncodedLen) + /// Storage: Uniques CollectionMaxSupply (r:0 w:1) + /// Proof: Uniques CollectionMaxSupply (max_values: None, max_size: Some(24), added: 2499, mode: MaxEncodedLen) /// The range of component `n` is `[0, 1000]`. /// The range of component `m` is `[0, 1000]`. /// The range of component `a` is `[0, 1000]`. fn destroy(n: u32, m: u32, a: u32, ) -> Weight { - // Minimum execution time: 2_453_194 nanoseconds. - Weight::from_ref_time(2_469_109_000 as u64) - // Standard Error: 27_900 - .saturating_add(Weight::from_ref_time(8_974_176 as u64).saturating_mul(n as u64)) - // Standard Error: 27_900 - .saturating_add(Weight::from_ref_time(344_842 as u64).saturating_mul(m as u64)) - // Standard Error: 27_900 - .saturating_add(Weight::from_ref_time(185_438 as u64).saturating_mul(a as u64)) - .saturating_add(T::DbWeight::get().reads(2 as u64)) - .saturating_add(T::DbWeight::get().reads((1 as u64).saturating_mul(n as u64))) - .saturating_add(T::DbWeight::get().writes(4 as u64)) - .saturating_add(T::DbWeight::get().writes((2 as u64).saturating_mul(n as u64))) - .saturating_add(T::DbWeight::get().writes((1 as u64).saturating_mul(m as u64))) - .saturating_add(T::DbWeight::get().writes((1 as u64).saturating_mul(a as u64))) - } - // Storage: Uniques Asset (r:1 w:1) - // Storage: Uniques Class (r:1 w:1) - // Storage: Uniques CollectionMaxSupply (r:1 w:0) - // Storage: Uniques Account (r:0 w:1) + // Proof Size summary in bytes: + // Measured: `450 + n * (108 ±0) + m * (56 ±0) + a * (107 ±0)` + // Estimated: `5250 + n * (2597 ±0)` + // Minimum execution time: 2_404_081 nanoseconds. + Weight::from_parts(2_419_004_000, 5250) + // Standard Error: 27_175 + .saturating_add(Weight::from_ref_time(8_616_904).saturating_mul(n.into())) + // Standard Error: 27_175 + .saturating_add(Weight::from_ref_time(334_249).saturating_mul(m.into())) + // Standard Error: 27_175 + .saturating_add(Weight::from_ref_time(213_038).saturating_mul(a.into())) + .saturating_add(T::DbWeight::get().reads(2_u64)) + .saturating_add(T::DbWeight::get().reads((1_u64).saturating_mul(n.into()))) + .saturating_add(T::DbWeight::get().writes(4_u64)) + .saturating_add(T::DbWeight::get().writes((2_u64).saturating_mul(n.into()))) + .saturating_add(T::DbWeight::get().writes((1_u64).saturating_mul(m.into()))) + .saturating_add(T::DbWeight::get().writes((1_u64).saturating_mul(a.into()))) + .saturating_add(Weight::from_proof_size(2597).saturating_mul(n.into())) + } + /// Storage: Uniques Asset (r:1 w:1) + /// Proof: Uniques Asset (max_values: None, max_size: Some(122), added: 2597, mode: MaxEncodedLen) + /// Storage: Uniques Class (r:1 w:1) + /// Proof: Uniques Class (max_values: None, max_size: Some(178), added: 2653, mode: MaxEncodedLen) + /// Storage: Uniques CollectionMaxSupply (r:1 w:0) + /// Proof: Uniques CollectionMaxSupply (max_values: None, max_size: Some(24), added: 2499, mode: MaxEncodedLen) + /// Storage: Uniques Account (r:0 w:1) + /// Proof: Uniques Account (max_values: None, max_size: Some(88), added: 2563, mode: MaxEncodedLen) fn mint() -> Weight { - // Minimum execution time: 45_115 nanoseconds. - Weight::from_ref_time(45_746_000 as u64) - .saturating_add(T::DbWeight::get().reads(3 as u64)) - .saturating_add(T::DbWeight::get().writes(3 as u64)) - } - // Storage: Uniques Class (r:1 w:1) - // Storage: Uniques Asset (r:1 w:1) - // Storage: Uniques Account (r:0 w:1) - // Storage: Uniques ItemPriceOf (r:0 w:1) + // Proof Size summary in bytes: + // Measured: `381` + // Estimated: `7749` + // Minimum execution time: 29_326 nanoseconds. + Weight::from_parts(29_671_000, 7749) + .saturating_add(T::DbWeight::get().reads(3_u64)) + .saturating_add(T::DbWeight::get().writes(3_u64)) + } + /// Storage: Uniques Class (r:1 w:1) + /// Proof: Uniques Class (max_values: None, max_size: Some(178), added: 2653, mode: MaxEncodedLen) + /// Storage: Uniques Asset (r:1 w:1) + /// Proof: Uniques Asset (max_values: None, max_size: Some(122), added: 2597, mode: MaxEncodedLen) + /// Storage: Uniques Account (r:0 w:1) + /// Proof: Uniques Account (max_values: None, max_size: Some(88), added: 2563, mode: MaxEncodedLen) + /// Storage: Uniques ItemPriceOf (r:0 w:1) + /// Proof: Uniques ItemPriceOf (max_values: None, max_size: Some(89), added: 2564, mode: MaxEncodedLen) fn burn() -> Weight { - // Minimum execution time: 46_447 nanoseconds. - Weight::from_ref_time(46_994_000 as u64) - .saturating_add(T::DbWeight::get().reads(2 as u64)) - .saturating_add(T::DbWeight::get().writes(4 as u64)) - } - // Storage: Uniques Class (r:1 w:0) - // Storage: Uniques Asset (r:1 w:1) - // Storage: Uniques Account (r:0 w:2) - // Storage: Uniques ItemPriceOf (r:0 w:1) + // Proof Size summary in bytes: + // Measured: `559` + // Estimated: `5250` + // Minimum execution time: 30_497 nanoseconds. + Weight::from_parts(30_714_000, 5250) + .saturating_add(T::DbWeight::get().reads(2_u64)) + .saturating_add(T::DbWeight::get().writes(4_u64)) + } + /// Storage: Uniques Class (r:1 w:0) + /// Proof: Uniques Class (max_values: None, max_size: Some(178), added: 2653, mode: MaxEncodedLen) + /// Storage: Uniques Asset (r:1 w:1) + /// Proof: Uniques Asset (max_values: None, max_size: Some(122), added: 2597, mode: MaxEncodedLen) + /// Storage: Uniques Account (r:0 w:2) + /// Proof: Uniques Account (max_values: None, max_size: Some(88), added: 2563, mode: MaxEncodedLen) + /// Storage: Uniques ItemPriceOf (r:0 w:1) + /// Proof: Uniques ItemPriceOf (max_values: None, max_size: Some(89), added: 2564, mode: MaxEncodedLen) fn transfer() -> Weight { - // Minimum execution time: 35_953 nanoseconds. - Weight::from_ref_time(36_375_000 as u64) - .saturating_add(T::DbWeight::get().reads(2 as u64)) - .saturating_add(T::DbWeight::get().writes(4 as u64)) - } - // Storage: Uniques Class (r:1 w:1) - // Storage: Uniques Asset (r:102 w:102) + // Proof Size summary in bytes: + // Measured: `559` + // Estimated: `5250` + // Minimum execution time: 24_212 nanoseconds. + Weight::from_parts(24_681_000, 5250) + .saturating_add(T::DbWeight::get().reads(2_u64)) + .saturating_add(T::DbWeight::get().writes(4_u64)) + } + /// Storage: Uniques Class (r:1 w:1) + /// Proof: Uniques Class (max_values: None, max_size: Some(178), added: 2653, mode: MaxEncodedLen) + /// Storage: Uniques Asset (r:5000 w:5000) + /// Proof: Uniques Asset (max_values: None, max_size: Some(122), added: 2597, mode: MaxEncodedLen) /// The range of component `i` is `[0, 5000]`. fn redeposit(i: u32, ) -> Weight { - // Minimum execution time: 24_238 nanoseconds. - Weight::from_ref_time(24_788_000 as u64) - // Standard Error: 9_232 - .saturating_add(Weight::from_ref_time(11_322_011 as u64).saturating_mul(i as u64)) - .saturating_add(T::DbWeight::get().reads(1 as u64)) - .saturating_add(T::DbWeight::get().reads((1 as u64).saturating_mul(i as u64))) - .saturating_add(T::DbWeight::get().writes(1 as u64)) - .saturating_add(T::DbWeight::get().writes((1 as u64).saturating_mul(i as u64))) - } - // Storage: Uniques Asset (r:1 w:1) - // Storage: Uniques Class (r:1 w:0) + // Proof Size summary in bytes: + // Measured: `837 + i * (108 ±0)` + // Estimated: `2653 + i * (2597 ±0)` + // Minimum execution time: 13_633 nanoseconds. + Weight::from_parts(13_797_000, 2653) + // Standard Error: 9_293 + .saturating_add(Weight::from_ref_time(11_163_914).saturating_mul(i.into())) + .saturating_add(T::DbWeight::get().reads(1_u64)) + .saturating_add(T::DbWeight::get().reads((1_u64).saturating_mul(i.into()))) + .saturating_add(T::DbWeight::get().writes(1_u64)) + .saturating_add(T::DbWeight::get().writes((1_u64).saturating_mul(i.into()))) + .saturating_add(Weight::from_proof_size(2597).saturating_mul(i.into())) + } + /// Storage: Uniques Asset (r:1 w:1) + /// Proof: Uniques Asset (max_values: None, max_size: Some(122), added: 2597, mode: MaxEncodedLen) + /// Storage: Uniques Class (r:1 w:0) + /// Proof: Uniques Class (max_values: None, max_size: Some(178), added: 2653, mode: MaxEncodedLen) fn freeze() -> Weight { - // Minimum execution time: 28_595 nanoseconds. - Weight::from_ref_time(29_280_000 as u64) - .saturating_add(T::DbWeight::get().reads(2 as u64)) - .saturating_add(T::DbWeight::get().writes(1 as u64)) - } - // Storage: Uniques Asset (r:1 w:1) - // Storage: Uniques Class (r:1 w:0) + // Proof Size summary in bytes: + // Measured: `559` + // Estimated: `5250` + // Minimum execution time: 17_126 nanoseconds. + Weight::from_parts(17_572_000, 5250) + .saturating_add(T::DbWeight::get().reads(2_u64)) + .saturating_add(T::DbWeight::get().writes(1_u64)) + } + /// Storage: Uniques Asset (r:1 w:1) + /// Proof: Uniques Asset (max_values: None, max_size: Some(122), added: 2597, mode: MaxEncodedLen) + /// Storage: Uniques Class (r:1 w:0) + /// Proof: Uniques Class (max_values: None, max_size: Some(178), added: 2653, mode: MaxEncodedLen) fn thaw() -> Weight { - // Minimum execution time: 28_581 nanoseconds. - Weight::from_ref_time(29_038_000 as u64) - .saturating_add(T::DbWeight::get().reads(2 as u64)) - .saturating_add(T::DbWeight::get().writes(1 as u64)) - } - // Storage: Uniques Class (r:1 w:1) + // Proof Size summary in bytes: + // Measured: `559` + // Estimated: `5250` + // Minimum execution time: 17_209 nanoseconds. + Weight::from_parts(17_411_000, 5250) + .saturating_add(T::DbWeight::get().reads(2_u64)) + .saturating_add(T::DbWeight::get().writes(1_u64)) + } + /// Storage: Uniques Class (r:1 w:1) + /// Proof: Uniques Class (max_values: None, max_size: Some(178), added: 2653, mode: MaxEncodedLen) fn freeze_collection() -> Weight { - // Minimum execution time: 24_298 nanoseconds. - Weight::from_ref_time(24_742_000 as u64) - .saturating_add(T::DbWeight::get().reads(1 as u64)) - .saturating_add(T::DbWeight::get().writes(1 as u64)) - } - // Storage: Uniques Class (r:1 w:1) + // Proof Size summary in bytes: + // Measured: `381` + // Estimated: `2653` + // Minimum execution time: 13_048 nanoseconds. + Weight::from_parts(13_589_000, 2653) + .saturating_add(T::DbWeight::get().reads(1_u64)) + .saturating_add(T::DbWeight::get().writes(1_u64)) + } + /// Storage: Uniques Class (r:1 w:1) + /// Proof: Uniques Class (max_values: None, max_size: Some(178), added: 2653, mode: MaxEncodedLen) fn thaw_collection() -> Weight { - // Minimum execution time: 24_004 nanoseconds. - Weight::from_ref_time(24_536_000 as u64) - .saturating_add(T::DbWeight::get().reads(1 as u64)) - .saturating_add(T::DbWeight::get().writes(1 as u64)) - } - // Storage: Uniques OwnershipAcceptance (r:1 w:1) - // Storage: Uniques Class (r:1 w:1) - // Storage: Uniques ClassAccount (r:0 w:2) + // Proof Size summary in bytes: + // Measured: `381` + // Estimated: `2653` + // Minimum execution time: 12_908 nanoseconds. + Weight::from_parts(13_098_000, 2653) + .saturating_add(T::DbWeight::get().reads(1_u64)) + .saturating_add(T::DbWeight::get().writes(1_u64)) + } + /// Storage: Uniques OwnershipAcceptance (r:1 w:1) + /// Proof: Uniques OwnershipAcceptance (max_values: None, max_size: Some(52), added: 2527, mode: MaxEncodedLen) + /// Storage: Uniques Class (r:1 w:1) + /// Proof: Uniques Class (max_values: None, max_size: Some(178), added: 2653, mode: MaxEncodedLen) + /// Storage: Uniques ClassAccount (r:0 w:2) + /// Proof: Uniques ClassAccount (max_values: None, max_size: Some(68), added: 2543, mode: MaxEncodedLen) fn transfer_ownership() -> Weight { - // Minimum execution time: 32_599 nanoseconds. - Weight::from_ref_time(33_201_000 as u64) - .saturating_add(T::DbWeight::get().reads(2 as u64)) - .saturating_add(T::DbWeight::get().writes(4 as u64)) - } - // Storage: Uniques Class (r:1 w:1) + // Proof Size summary in bytes: + // Measured: `455` + // Estimated: `5180` + // Minimum execution time: 20_629 nanoseconds. + Weight::from_parts(21_448_000, 5180) + .saturating_add(T::DbWeight::get().reads(2_u64)) + .saturating_add(T::DbWeight::get().writes(4_u64)) + } + /// Storage: Uniques Class (r:1 w:1) + /// Proof: Uniques Class (max_values: None, max_size: Some(178), added: 2653, mode: MaxEncodedLen) fn set_team() -> Weight { - // Minimum execution time: 25_137 nanoseconds. - Weight::from_ref_time(25_877_000 as u64) - .saturating_add(T::DbWeight::get().reads(1 as u64)) - .saturating_add(T::DbWeight::get().writes(1 as u64)) - } - // Storage: Uniques Class (r:1 w:1) - // Storage: Uniques ClassAccount (r:0 w:1) + // Proof Size summary in bytes: + // Measured: `381` + // Estimated: `2653` + // Minimum execution time: 13_740 nanoseconds. + Weight::from_parts(14_020_000, 2653) + .saturating_add(T::DbWeight::get().reads(1_u64)) + .saturating_add(T::DbWeight::get().writes(1_u64)) + } + /// Storage: Uniques Class (r:1 w:1) + /// Proof: Uniques Class (max_values: None, max_size: Some(178), added: 2653, mode: MaxEncodedLen) + /// Storage: Uniques ClassAccount (r:0 w:1) + /// Proof: Uniques ClassAccount (max_values: None, max_size: Some(68), added: 2543, mode: MaxEncodedLen) fn force_item_status() -> Weight { - // Minimum execution time: 27_736 nanoseconds. - Weight::from_ref_time(28_279_000 as u64) - .saturating_add(T::DbWeight::get().reads(1 as u64)) - .saturating_add(T::DbWeight::get().writes(2 as u64)) - } - // Storage: Uniques Class (r:1 w:1) - // Storage: Uniques InstanceMetadataOf (r:1 w:0) - // Storage: Uniques Attribute (r:1 w:1) + // Proof Size summary in bytes: + // Measured: `381` + // Estimated: `2653` + // Minimum execution time: 16_293 nanoseconds. + Weight::from_parts(16_509_000, 2653) + .saturating_add(T::DbWeight::get().reads(1_u64)) + .saturating_add(T::DbWeight::get().writes(2_u64)) + } + /// Storage: Uniques Class (r:1 w:1) + /// Proof: Uniques Class (max_values: None, max_size: Some(178), added: 2653, mode: MaxEncodedLen) + /// Storage: Uniques InstanceMetadataOf (r:1 w:0) + /// Proof: Uniques InstanceMetadataOf (max_values: None, max_size: Some(108), added: 2583, mode: MaxEncodedLen) + /// Storage: Uniques Attribute (r:1 w:1) + /// Proof: Uniques Attribute (max_values: None, max_size: Some(364), added: 2839, mode: MaxEncodedLen) fn set_attribute() -> Weight { - // Minimum execution time: 51_195 nanoseconds. - Weight::from_ref_time(51_674_000 as u64) - .saturating_add(T::DbWeight::get().reads(3 as u64)) - .saturating_add(T::DbWeight::get().writes(2 as u64)) - } - // Storage: Uniques Class (r:1 w:1) - // Storage: Uniques InstanceMetadataOf (r:1 w:0) - // Storage: Uniques Attribute (r:1 w:1) + // Proof Size summary in bytes: + // Measured: `611` + // Estimated: `8075` + // Minimum execution time: 33_560 nanoseconds. + Weight::from_parts(34_263_000, 8075) + .saturating_add(T::DbWeight::get().reads(3_u64)) + .saturating_add(T::DbWeight::get().writes(2_u64)) + } + /// Storage: Uniques Class (r:1 w:1) + /// Proof: Uniques Class (max_values: None, max_size: Some(178), added: 2653, mode: MaxEncodedLen) + /// Storage: Uniques InstanceMetadataOf (r:1 w:0) + /// Proof: Uniques InstanceMetadataOf (max_values: None, max_size: Some(108), added: 2583, mode: MaxEncodedLen) + /// Storage: Uniques Attribute (r:1 w:1) + /// Proof: Uniques Attribute (max_values: None, max_size: Some(364), added: 2839, mode: MaxEncodedLen) fn clear_attribute() -> Weight { - // Minimum execution time: 50_159 nanoseconds. - Weight::from_ref_time(51_412_000 as u64) - .saturating_add(T::DbWeight::get().reads(3 as u64)) - .saturating_add(T::DbWeight::get().writes(2 as u64)) - } - // Storage: Uniques Class (r:1 w:1) - // Storage: Uniques InstanceMetadataOf (r:1 w:1) + // Proof Size summary in bytes: + // Measured: `1031` + // Estimated: `8075` + // Minimum execution time: 31_955 nanoseconds. + Weight::from_parts(32_447_000, 8075) + .saturating_add(T::DbWeight::get().reads(3_u64)) + .saturating_add(T::DbWeight::get().writes(2_u64)) + } + /// Storage: Uniques Class (r:1 w:1) + /// Proof: Uniques Class (max_values: None, max_size: Some(178), added: 2653, mode: MaxEncodedLen) + /// Storage: Uniques InstanceMetadataOf (r:1 w:1) + /// Proof: Uniques InstanceMetadataOf (max_values: None, max_size: Some(108), added: 2583, mode: MaxEncodedLen) fn set_metadata() -> Weight { - // Minimum execution time: 42_608 nanoseconds. - Weight::from_ref_time(42_880_000 as u64) - .saturating_add(T::DbWeight::get().reads(2 as u64)) - .saturating_add(T::DbWeight::get().writes(2 as u64)) - } - // Storage: Uniques Class (r:1 w:1) - // Storage: Uniques InstanceMetadataOf (r:1 w:1) + // Proof Size summary in bytes: + // Measured: `447` + // Estimated: `5236` + // Minimum execution time: 25_520 nanoseconds. + Weight::from_parts(25_843_000, 5236) + .saturating_add(T::DbWeight::get().reads(2_u64)) + .saturating_add(T::DbWeight::get().writes(2_u64)) + } + /// Storage: Uniques Class (r:1 w:1) + /// Proof: Uniques Class (max_values: None, max_size: Some(178), added: 2653, mode: MaxEncodedLen) + /// Storage: Uniques InstanceMetadataOf (r:1 w:1) + /// Proof: Uniques InstanceMetadataOf (max_values: None, max_size: Some(108), added: 2583, mode: MaxEncodedLen) fn clear_metadata() -> Weight { - // Minimum execution time: 43_239 nanoseconds. - Weight::from_ref_time(43_752_000 as u64) - .saturating_add(T::DbWeight::get().reads(2 as u64)) - .saturating_add(T::DbWeight::get().writes(2 as u64)) - } - // Storage: Uniques Class (r:1 w:1) - // Storage: Uniques ClassMetadataOf (r:1 w:1) + // Proof Size summary in bytes: + // Measured: `611` + // Estimated: `5236` + // Minimum execution time: 25_812 nanoseconds. + Weight::from_parts(26_141_000, 5236) + .saturating_add(T::DbWeight::get().reads(2_u64)) + .saturating_add(T::DbWeight::get().writes(2_u64)) + } + /// Storage: Uniques Class (r:1 w:1) + /// Proof: Uniques Class (max_values: None, max_size: Some(178), added: 2653, mode: MaxEncodedLen) + /// Storage: Uniques ClassMetadataOf (r:1 w:1) + /// Proof: Uniques ClassMetadataOf (max_values: None, max_size: Some(88), added: 2563, mode: MaxEncodedLen) fn set_collection_metadata() -> Weight { - // Minimum execution time: 41_224 nanoseconds. - Weight::from_ref_time(41_974_000 as u64) - .saturating_add(T::DbWeight::get().reads(2 as u64)) - .saturating_add(T::DbWeight::get().writes(2 as u64)) - } - // Storage: Uniques Class (r:1 w:0) - // Storage: Uniques ClassMetadataOf (r:1 w:1) + // Proof Size summary in bytes: + // Measured: `381` + // Estimated: `5216` + // Minimum execution time: 25_055 nanoseconds. + Weight::from_parts(25_244_000, 5216) + .saturating_add(T::DbWeight::get().reads(2_u64)) + .saturating_add(T::DbWeight::get().writes(2_u64)) + } + /// Storage: Uniques Class (r:1 w:0) + /// Proof: Uniques Class (max_values: None, max_size: Some(178), added: 2653, mode: MaxEncodedLen) + /// Storage: Uniques ClassMetadataOf (r:1 w:1) + /// Proof: Uniques ClassMetadataOf (max_values: None, max_size: Some(88), added: 2563, mode: MaxEncodedLen) fn clear_collection_metadata() -> Weight { - // Minimum execution time: 40_836 nanoseconds. - Weight::from_ref_time(41_864_000 as u64) - .saturating_add(T::DbWeight::get().reads(2 as u64)) - .saturating_add(T::DbWeight::get().writes(1 as u64)) - } - // Storage: Uniques Class (r:1 w:0) - // Storage: Uniques Asset (r:1 w:1) + // Proof Size summary in bytes: + // Measured: `525` + // Estimated: `5216` + // Minimum execution time: 23_311 nanoseconds. + Weight::from_parts(23_682_000, 5216) + .saturating_add(T::DbWeight::get().reads(2_u64)) + .saturating_add(T::DbWeight::get().writes(1_u64)) + } + /// Storage: Uniques Class (r:1 w:0) + /// Proof: Uniques Class (max_values: None, max_size: Some(178), added: 2653, mode: MaxEncodedLen) + /// Storage: Uniques Asset (r:1 w:1) + /// Proof: Uniques Asset (max_values: None, max_size: Some(122), added: 2597, mode: MaxEncodedLen) fn approve_transfer() -> Weight { - // Minimum execution time: 29_558 nanoseconds. - Weight::from_ref_time(29_948_000 as u64) - .saturating_add(T::DbWeight::get().reads(2 as u64)) - .saturating_add(T::DbWeight::get().writes(1 as u64)) - } - // Storage: Uniques Class (r:1 w:0) - // Storage: Uniques Asset (r:1 w:1) + // Proof Size summary in bytes: + // Measured: `559` + // Estimated: `5250` + // Minimum execution time: 17_709 nanoseconds. + Weight::from_parts(18_308_000, 5250) + .saturating_add(T::DbWeight::get().reads(2_u64)) + .saturating_add(T::DbWeight::get().writes(1_u64)) + } + /// Storage: Uniques Class (r:1 w:0) + /// Proof: Uniques Class (max_values: None, max_size: Some(178), added: 2653, mode: MaxEncodedLen) + /// Storage: Uniques Asset (r:1 w:1) + /// Proof: Uniques Asset (max_values: None, max_size: Some(122), added: 2597, mode: MaxEncodedLen) fn cancel_approval() -> Weight { - // Minimum execution time: 29_694 nanoseconds. - Weight::from_ref_time(30_156_000 as u64) - .saturating_add(T::DbWeight::get().reads(2 as u64)) - .saturating_add(T::DbWeight::get().writes(1 as u64)) - } - // Storage: Uniques OwnershipAcceptance (r:1 w:1) + // Proof Size summary in bytes: + // Measured: `592` + // Estimated: `5250` + // Minimum execution time: 17_656 nanoseconds. + Weight::from_parts(18_039_000, 5250) + .saturating_add(T::DbWeight::get().reads(2_u64)) + .saturating_add(T::DbWeight::get().writes(1_u64)) + } + /// Storage: Uniques OwnershipAcceptance (r:1 w:1) + /// Proof: Uniques OwnershipAcceptance (max_values: None, max_size: Some(52), added: 2527, mode: MaxEncodedLen) fn set_accept_ownership() -> Weight { - // Minimum execution time: 27_819 nanoseconds. - Weight::from_ref_time(28_245_000 as u64) - .saturating_add(T::DbWeight::get().reads(1 as u64)) - .saturating_add(T::DbWeight::get().writes(1 as u64)) - } - // Storage: Uniques CollectionMaxSupply (r:1 w:1) - // Storage: Uniques Class (r:1 w:0) + // Proof Size summary in bytes: + // Measured: `109` + // Estimated: `2527` + // Minimum execution time: 14_615 nanoseconds. + Weight::from_parts(14_931_000, 2527) + .saturating_add(T::DbWeight::get().reads(1_u64)) + .saturating_add(T::DbWeight::get().writes(1_u64)) + } + /// Storage: Uniques CollectionMaxSupply (r:1 w:1) + /// Proof: Uniques CollectionMaxSupply (max_values: None, max_size: Some(24), added: 2499, mode: MaxEncodedLen) + /// Storage: Uniques Class (r:1 w:0) + /// Proof: Uniques Class (max_values: None, max_size: Some(178), added: 2653, mode: MaxEncodedLen) fn set_collection_max_supply() -> Weight { - // Minimum execution time: 26_317 nanoseconds. - Weight::from_ref_time(26_893_000 as u64) - .saturating_add(T::DbWeight::get().reads(2 as u64)) - .saturating_add(T::DbWeight::get().writes(1 as u64)) - } - // Storage: Uniques Asset (r:1 w:0) - // Storage: Uniques ItemPriceOf (r:0 w:1) + // Proof Size summary in bytes: + // Measured: `381` + // Estimated: `5152` + // Minimum execution time: 14_974 nanoseconds. + Weight::from_parts(15_314_000, 5152) + .saturating_add(T::DbWeight::get().reads(2_u64)) + .saturating_add(T::DbWeight::get().writes(1_u64)) + } + /// Storage: Uniques Asset (r:1 w:0) + /// Proof: Uniques Asset (max_values: None, max_size: Some(122), added: 2597, mode: MaxEncodedLen) + /// Storage: Uniques ItemPriceOf (r:0 w:1) + /// Proof: Uniques ItemPriceOf (max_values: None, max_size: Some(89), added: 2564, mode: MaxEncodedLen) fn set_price() -> Weight { - // Minimum execution time: 26_546 nanoseconds. - Weight::from_ref_time(27_142_000 as u64) - .saturating_add(T::DbWeight::get().reads(1 as u64)) - .saturating_add(T::DbWeight::get().writes(1 as u64)) - } - // Storage: Uniques Asset (r:1 w:1) - // Storage: Uniques ItemPriceOf (r:1 w:1) - // Storage: Uniques Class (r:1 w:0) - // Storage: Uniques Account (r:0 w:2) + // Proof Size summary in bytes: + // Measured: `358` + // Estimated: `2597` + // Minimum execution time: 15_444 nanoseconds. + Weight::from_parts(15_886_000, 2597) + .saturating_add(T::DbWeight::get().reads(1_u64)) + .saturating_add(T::DbWeight::get().writes(1_u64)) + } + /// Storage: Uniques Asset (r:1 w:1) + /// Proof: Uniques Asset (max_values: None, max_size: Some(122), added: 2597, mode: MaxEncodedLen) + /// Storage: Uniques ItemPriceOf (r:1 w:1) + /// Proof: Uniques ItemPriceOf (max_values: None, max_size: Some(89), added: 2564, mode: MaxEncodedLen) + /// Storage: Uniques Class (r:1 w:0) + /// Proof: Uniques Class (max_values: None, max_size: Some(178), added: 2653, mode: MaxEncodedLen) + /// Storage: Uniques Account (r:0 w:2) + /// Proof: Uniques Account (max_values: None, max_size: Some(88), added: 2563, mode: MaxEncodedLen) fn buy_item() -> Weight { - // Minimum execution time: 49_238 nanoseconds. - Weight::from_ref_time(50_444_000 as u64) - .saturating_add(T::DbWeight::get().reads(3 as u64)) - .saturating_add(T::DbWeight::get().writes(4 as u64)) + // Proof Size summary in bytes: + // Measured: `703` + // Estimated: `7814` + // Minimum execution time: 35_628 nanoseconds. + Weight::from_parts(35_886_000, 7814) + .saturating_add(T::DbWeight::get().reads(3_u64)) + .saturating_add(T::DbWeight::get().writes(4_u64)) } } // For backwards compatibility and tests impl WeightInfo for () { - // Storage: Uniques Class (r:1 w:1) - // Storage: Uniques ClassAccount (r:0 w:1) + /// Storage: Uniques Class (r:1 w:1) + /// Proof: Uniques Class (max_values: None, max_size: Some(178), added: 2653, mode: MaxEncodedLen) + /// Storage: Uniques ClassAccount (r:0 w:1) + /// Proof: Uniques ClassAccount (max_values: None, max_size: Some(68), added: 2543, mode: MaxEncodedLen) fn create() -> Weight { - // Minimum execution time: 35_358 nanoseconds. - Weight::from_ref_time(35_935_000 as u64) - .saturating_add(RocksDbWeight::get().reads(1 as u64)) - .saturating_add(RocksDbWeight::get().writes(2 as u64)) - } - // Storage: Uniques Class (r:1 w:1) - // Storage: Uniques ClassAccount (r:0 w:1) + // Proof Size summary in bytes: + // Measured: `281` + // Estimated: `2653` + // Minimum execution time: 24_242 nanoseconds. + Weight::from_parts(24_682_000, 2653) + .saturating_add(RocksDbWeight::get().reads(1_u64)) + .saturating_add(RocksDbWeight::get().writes(2_u64)) + } + /// Storage: Uniques Class (r:1 w:1) + /// Proof: Uniques Class (max_values: None, max_size: Some(178), added: 2653, mode: MaxEncodedLen) + /// Storage: Uniques ClassAccount (r:0 w:1) + /// Proof: Uniques ClassAccount (max_values: None, max_size: Some(68), added: 2543, mode: MaxEncodedLen) fn force_create() -> Weight { - // Minimum execution time: 22_767 nanoseconds. - Weight::from_ref_time(23_235_000 as u64) - .saturating_add(RocksDbWeight::get().reads(1 as u64)) - .saturating_add(RocksDbWeight::get().writes(2 as u64)) - } - // Storage: Uniques Class (r:1 w:1) - // Storage: Uniques Asset (r:1 w:0) - // Storage: Uniques ClassAccount (r:0 w:1) - // Storage: Uniques Attribute (r:0 w:1000) - // Storage: Uniques ClassMetadataOf (r:0 w:1) - // Storage: Uniques InstanceMetadataOf (r:0 w:1000) - // Storage: Uniques CollectionMaxSupply (r:0 w:1) - // Storage: Uniques Account (r:0 w:20) + // Proof Size summary in bytes: + // Measured: `109` + // Estimated: `2653` + // Minimum execution time: 14_145 nanoseconds. + Weight::from_parts(14_598_000, 2653) + .saturating_add(RocksDbWeight::get().reads(1_u64)) + .saturating_add(RocksDbWeight::get().writes(2_u64)) + } + /// Storage: Uniques Class (r:1 w:1) + /// Proof: Uniques Class (max_values: None, max_size: Some(178), added: 2653, mode: MaxEncodedLen) + /// Storage: Uniques Asset (r:1001 w:1000) + /// Proof: Uniques Asset (max_values: None, max_size: Some(122), added: 2597, mode: MaxEncodedLen) + /// Storage: Uniques ClassAccount (r:0 w:1) + /// Proof: Uniques ClassAccount (max_values: None, max_size: Some(68), added: 2543, mode: MaxEncodedLen) + /// Storage: Uniques Attribute (r:0 w:1000) + /// Proof: Uniques Attribute (max_values: None, max_size: Some(364), added: 2839, mode: MaxEncodedLen) + /// Storage: Uniques ClassMetadataOf (r:0 w:1) + /// Proof: Uniques ClassMetadataOf (max_values: None, max_size: Some(88), added: 2563, mode: MaxEncodedLen) + /// Storage: Uniques InstanceMetadataOf (r:0 w:1000) + /// Proof: Uniques InstanceMetadataOf (max_values: None, max_size: Some(108), added: 2583, mode: MaxEncodedLen) + /// Storage: Uniques Account (r:0 w:1000) + /// Proof: Uniques Account (max_values: None, max_size: Some(88), added: 2563, mode: MaxEncodedLen) + /// Storage: Uniques CollectionMaxSupply (r:0 w:1) + /// Proof: Uniques CollectionMaxSupply (max_values: None, max_size: Some(24), added: 2499, mode: MaxEncodedLen) /// The range of component `n` is `[0, 1000]`. /// The range of component `m` is `[0, 1000]`. /// The range of component `a` is `[0, 1000]`. fn destroy(n: u32, m: u32, a: u32, ) -> Weight { - // Minimum execution time: 2_453_194 nanoseconds. - Weight::from_ref_time(2_469_109_000 as u64) - // Standard Error: 27_900 - .saturating_add(Weight::from_ref_time(8_974_176 as u64).saturating_mul(n as u64)) - // Standard Error: 27_900 - .saturating_add(Weight::from_ref_time(344_842 as u64).saturating_mul(m as u64)) - // Standard Error: 27_900 - .saturating_add(Weight::from_ref_time(185_438 as u64).saturating_mul(a as u64)) - .saturating_add(RocksDbWeight::get().reads(2 as u64)) - .saturating_add(RocksDbWeight::get().reads((1 as u64).saturating_mul(n as u64))) - .saturating_add(RocksDbWeight::get().writes(4 as u64)) - .saturating_add(RocksDbWeight::get().writes((2 as u64).saturating_mul(n as u64))) - .saturating_add(RocksDbWeight::get().writes((1 as u64).saturating_mul(m as u64))) - .saturating_add(RocksDbWeight::get().writes((1 as u64).saturating_mul(a as u64))) - } - // Storage: Uniques Asset (r:1 w:1) - // Storage: Uniques Class (r:1 w:1) - // Storage: Uniques CollectionMaxSupply (r:1 w:0) - // Storage: Uniques Account (r:0 w:1) + // Proof Size summary in bytes: + // Measured: `450 + n * (108 ±0) + m * (56 ±0) + a * (107 ±0)` + // Estimated: `5250 + n * (2597 ±0)` + // Minimum execution time: 2_404_081 nanoseconds. + Weight::from_parts(2_419_004_000, 5250) + // Standard Error: 27_175 + .saturating_add(Weight::from_ref_time(8_616_904).saturating_mul(n.into())) + // Standard Error: 27_175 + .saturating_add(Weight::from_ref_time(334_249).saturating_mul(m.into())) + // Standard Error: 27_175 + .saturating_add(Weight::from_ref_time(213_038).saturating_mul(a.into())) + .saturating_add(RocksDbWeight::get().reads(2_u64)) + .saturating_add(RocksDbWeight::get().reads((1_u64).saturating_mul(n.into()))) + .saturating_add(RocksDbWeight::get().writes(4_u64)) + .saturating_add(RocksDbWeight::get().writes((2_u64).saturating_mul(n.into()))) + .saturating_add(RocksDbWeight::get().writes((1_u64).saturating_mul(m.into()))) + .saturating_add(RocksDbWeight::get().writes((1_u64).saturating_mul(a.into()))) + .saturating_add(Weight::from_proof_size(2597).saturating_mul(n.into())) + } + /// Storage: Uniques Asset (r:1 w:1) + /// Proof: Uniques Asset (max_values: None, max_size: Some(122), added: 2597, mode: MaxEncodedLen) + /// Storage: Uniques Class (r:1 w:1) + /// Proof: Uniques Class (max_values: None, max_size: Some(178), added: 2653, mode: MaxEncodedLen) + /// Storage: Uniques CollectionMaxSupply (r:1 w:0) + /// Proof: Uniques CollectionMaxSupply (max_values: None, max_size: Some(24), added: 2499, mode: MaxEncodedLen) + /// Storage: Uniques Account (r:0 w:1) + /// Proof: Uniques Account (max_values: None, max_size: Some(88), added: 2563, mode: MaxEncodedLen) fn mint() -> Weight { - // Minimum execution time: 45_115 nanoseconds. - Weight::from_ref_time(45_746_000 as u64) - .saturating_add(RocksDbWeight::get().reads(3 as u64)) - .saturating_add(RocksDbWeight::get().writes(3 as u64)) - } - // Storage: Uniques Class (r:1 w:1) - // Storage: Uniques Asset (r:1 w:1) - // Storage: Uniques Account (r:0 w:1) - // Storage: Uniques ItemPriceOf (r:0 w:1) + // Proof Size summary in bytes: + // Measured: `381` + // Estimated: `7749` + // Minimum execution time: 29_326 nanoseconds. + Weight::from_parts(29_671_000, 7749) + .saturating_add(RocksDbWeight::get().reads(3_u64)) + .saturating_add(RocksDbWeight::get().writes(3_u64)) + } + /// Storage: Uniques Class (r:1 w:1) + /// Proof: Uniques Class (max_values: None, max_size: Some(178), added: 2653, mode: MaxEncodedLen) + /// Storage: Uniques Asset (r:1 w:1) + /// Proof: Uniques Asset (max_values: None, max_size: Some(122), added: 2597, mode: MaxEncodedLen) + /// Storage: Uniques Account (r:0 w:1) + /// Proof: Uniques Account (max_values: None, max_size: Some(88), added: 2563, mode: MaxEncodedLen) + /// Storage: Uniques ItemPriceOf (r:0 w:1) + /// Proof: Uniques ItemPriceOf (max_values: None, max_size: Some(89), added: 2564, mode: MaxEncodedLen) fn burn() -> Weight { - // Minimum execution time: 46_447 nanoseconds. - Weight::from_ref_time(46_994_000 as u64) - .saturating_add(RocksDbWeight::get().reads(2 as u64)) - .saturating_add(RocksDbWeight::get().writes(4 as u64)) - } - // Storage: Uniques Class (r:1 w:0) - // Storage: Uniques Asset (r:1 w:1) - // Storage: Uniques Account (r:0 w:2) - // Storage: Uniques ItemPriceOf (r:0 w:1) + // Proof Size summary in bytes: + // Measured: `559` + // Estimated: `5250` + // Minimum execution time: 30_497 nanoseconds. + Weight::from_parts(30_714_000, 5250) + .saturating_add(RocksDbWeight::get().reads(2_u64)) + .saturating_add(RocksDbWeight::get().writes(4_u64)) + } + /// Storage: Uniques Class (r:1 w:0) + /// Proof: Uniques Class (max_values: None, max_size: Some(178), added: 2653, mode: MaxEncodedLen) + /// Storage: Uniques Asset (r:1 w:1) + /// Proof: Uniques Asset (max_values: None, max_size: Some(122), added: 2597, mode: MaxEncodedLen) + /// Storage: Uniques Account (r:0 w:2) + /// Proof: Uniques Account (max_values: None, max_size: Some(88), added: 2563, mode: MaxEncodedLen) + /// Storage: Uniques ItemPriceOf (r:0 w:1) + /// Proof: Uniques ItemPriceOf (max_values: None, max_size: Some(89), added: 2564, mode: MaxEncodedLen) fn transfer() -> Weight { - // Minimum execution time: 35_953 nanoseconds. - Weight::from_ref_time(36_375_000 as u64) - .saturating_add(RocksDbWeight::get().reads(2 as u64)) - .saturating_add(RocksDbWeight::get().writes(4 as u64)) - } - // Storage: Uniques Class (r:1 w:1) - // Storage: Uniques Asset (r:102 w:102) + // Proof Size summary in bytes: + // Measured: `559` + // Estimated: `5250` + // Minimum execution time: 24_212 nanoseconds. + Weight::from_parts(24_681_000, 5250) + .saturating_add(RocksDbWeight::get().reads(2_u64)) + .saturating_add(RocksDbWeight::get().writes(4_u64)) + } + /// Storage: Uniques Class (r:1 w:1) + /// Proof: Uniques Class (max_values: None, max_size: Some(178), added: 2653, mode: MaxEncodedLen) + /// Storage: Uniques Asset (r:5000 w:5000) + /// Proof: Uniques Asset (max_values: None, max_size: Some(122), added: 2597, mode: MaxEncodedLen) /// The range of component `i` is `[0, 5000]`. fn redeposit(i: u32, ) -> Weight { - // Minimum execution time: 24_238 nanoseconds. - Weight::from_ref_time(24_788_000 as u64) - // Standard Error: 9_232 - .saturating_add(Weight::from_ref_time(11_322_011 as u64).saturating_mul(i as u64)) - .saturating_add(RocksDbWeight::get().reads(1 as u64)) - .saturating_add(RocksDbWeight::get().reads((1 as u64).saturating_mul(i as u64))) - .saturating_add(RocksDbWeight::get().writes(1 as u64)) - .saturating_add(RocksDbWeight::get().writes((1 as u64).saturating_mul(i as u64))) - } - // Storage: Uniques Asset (r:1 w:1) - // Storage: Uniques Class (r:1 w:0) + // Proof Size summary in bytes: + // Measured: `837 + i * (108 ±0)` + // Estimated: `2653 + i * (2597 ±0)` + // Minimum execution time: 13_633 nanoseconds. + Weight::from_parts(13_797_000, 2653) + // Standard Error: 9_293 + .saturating_add(Weight::from_ref_time(11_163_914).saturating_mul(i.into())) + .saturating_add(RocksDbWeight::get().reads(1_u64)) + .saturating_add(RocksDbWeight::get().reads((1_u64).saturating_mul(i.into()))) + .saturating_add(RocksDbWeight::get().writes(1_u64)) + .saturating_add(RocksDbWeight::get().writes((1_u64).saturating_mul(i.into()))) + .saturating_add(Weight::from_proof_size(2597).saturating_mul(i.into())) + } + /// Storage: Uniques Asset (r:1 w:1) + /// Proof: Uniques Asset (max_values: None, max_size: Some(122), added: 2597, mode: MaxEncodedLen) + /// Storage: Uniques Class (r:1 w:0) + /// Proof: Uniques Class (max_values: None, max_size: Some(178), added: 2653, mode: MaxEncodedLen) fn freeze() -> Weight { - // Minimum execution time: 28_595 nanoseconds. - Weight::from_ref_time(29_280_000 as u64) - .saturating_add(RocksDbWeight::get().reads(2 as u64)) - .saturating_add(RocksDbWeight::get().writes(1 as u64)) - } - // Storage: Uniques Asset (r:1 w:1) - // Storage: Uniques Class (r:1 w:0) + // Proof Size summary in bytes: + // Measured: `559` + // Estimated: `5250` + // Minimum execution time: 17_126 nanoseconds. + Weight::from_parts(17_572_000, 5250) + .saturating_add(RocksDbWeight::get().reads(2_u64)) + .saturating_add(RocksDbWeight::get().writes(1_u64)) + } + /// Storage: Uniques Asset (r:1 w:1) + /// Proof: Uniques Asset (max_values: None, max_size: Some(122), added: 2597, mode: MaxEncodedLen) + /// Storage: Uniques Class (r:1 w:0) + /// Proof: Uniques Class (max_values: None, max_size: Some(178), added: 2653, mode: MaxEncodedLen) fn thaw() -> Weight { - // Minimum execution time: 28_581 nanoseconds. - Weight::from_ref_time(29_038_000 as u64) - .saturating_add(RocksDbWeight::get().reads(2 as u64)) - .saturating_add(RocksDbWeight::get().writes(1 as u64)) - } - // Storage: Uniques Class (r:1 w:1) + // Proof Size summary in bytes: + // Measured: `559` + // Estimated: `5250` + // Minimum execution time: 17_209 nanoseconds. + Weight::from_parts(17_411_000, 5250) + .saturating_add(RocksDbWeight::get().reads(2_u64)) + .saturating_add(RocksDbWeight::get().writes(1_u64)) + } + /// Storage: Uniques Class (r:1 w:1) + /// Proof: Uniques Class (max_values: None, max_size: Some(178), added: 2653, mode: MaxEncodedLen) fn freeze_collection() -> Weight { - // Minimum execution time: 24_298 nanoseconds. - Weight::from_ref_time(24_742_000 as u64) - .saturating_add(RocksDbWeight::get().reads(1 as u64)) - .saturating_add(RocksDbWeight::get().writes(1 as u64)) - } - // Storage: Uniques Class (r:1 w:1) + // Proof Size summary in bytes: + // Measured: `381` + // Estimated: `2653` + // Minimum execution time: 13_048 nanoseconds. + Weight::from_parts(13_589_000, 2653) + .saturating_add(RocksDbWeight::get().reads(1_u64)) + .saturating_add(RocksDbWeight::get().writes(1_u64)) + } + /// Storage: Uniques Class (r:1 w:1) + /// Proof: Uniques Class (max_values: None, max_size: Some(178), added: 2653, mode: MaxEncodedLen) fn thaw_collection() -> Weight { - // Minimum execution time: 24_004 nanoseconds. - Weight::from_ref_time(24_536_000 as u64) - .saturating_add(RocksDbWeight::get().reads(1 as u64)) - .saturating_add(RocksDbWeight::get().writes(1 as u64)) - } - // Storage: Uniques OwnershipAcceptance (r:1 w:1) - // Storage: Uniques Class (r:1 w:1) - // Storage: Uniques ClassAccount (r:0 w:2) + // Proof Size summary in bytes: + // Measured: `381` + // Estimated: `2653` + // Minimum execution time: 12_908 nanoseconds. + Weight::from_parts(13_098_000, 2653) + .saturating_add(RocksDbWeight::get().reads(1_u64)) + .saturating_add(RocksDbWeight::get().writes(1_u64)) + } + /// Storage: Uniques OwnershipAcceptance (r:1 w:1) + /// Proof: Uniques OwnershipAcceptance (max_values: None, max_size: Some(52), added: 2527, mode: MaxEncodedLen) + /// Storage: Uniques Class (r:1 w:1) + /// Proof: Uniques Class (max_values: None, max_size: Some(178), added: 2653, mode: MaxEncodedLen) + /// Storage: Uniques ClassAccount (r:0 w:2) + /// Proof: Uniques ClassAccount (max_values: None, max_size: Some(68), added: 2543, mode: MaxEncodedLen) fn transfer_ownership() -> Weight { - // Minimum execution time: 32_599 nanoseconds. - Weight::from_ref_time(33_201_000 as u64) - .saturating_add(RocksDbWeight::get().reads(2 as u64)) - .saturating_add(RocksDbWeight::get().writes(4 as u64)) - } - // Storage: Uniques Class (r:1 w:1) + // Proof Size summary in bytes: + // Measured: `455` + // Estimated: `5180` + // Minimum execution time: 20_629 nanoseconds. + Weight::from_parts(21_448_000, 5180) + .saturating_add(RocksDbWeight::get().reads(2_u64)) + .saturating_add(RocksDbWeight::get().writes(4_u64)) + } + /// Storage: Uniques Class (r:1 w:1) + /// Proof: Uniques Class (max_values: None, max_size: Some(178), added: 2653, mode: MaxEncodedLen) fn set_team() -> Weight { - // Minimum execution time: 25_137 nanoseconds. - Weight::from_ref_time(25_877_000 as u64) - .saturating_add(RocksDbWeight::get().reads(1 as u64)) - .saturating_add(RocksDbWeight::get().writes(1 as u64)) - } - // Storage: Uniques Class (r:1 w:1) - // Storage: Uniques ClassAccount (r:0 w:1) + // Proof Size summary in bytes: + // Measured: `381` + // Estimated: `2653` + // Minimum execution time: 13_740 nanoseconds. + Weight::from_parts(14_020_000, 2653) + .saturating_add(RocksDbWeight::get().reads(1_u64)) + .saturating_add(RocksDbWeight::get().writes(1_u64)) + } + /// Storage: Uniques Class (r:1 w:1) + /// Proof: Uniques Class (max_values: None, max_size: Some(178), added: 2653, mode: MaxEncodedLen) + /// Storage: Uniques ClassAccount (r:0 w:1) + /// Proof: Uniques ClassAccount (max_values: None, max_size: Some(68), added: 2543, mode: MaxEncodedLen) fn force_item_status() -> Weight { - // Minimum execution time: 27_736 nanoseconds. - Weight::from_ref_time(28_279_000 as u64) - .saturating_add(RocksDbWeight::get().reads(1 as u64)) - .saturating_add(RocksDbWeight::get().writes(2 as u64)) - } - // Storage: Uniques Class (r:1 w:1) - // Storage: Uniques InstanceMetadataOf (r:1 w:0) - // Storage: Uniques Attribute (r:1 w:1) + // Proof Size summary in bytes: + // Measured: `381` + // Estimated: `2653` + // Minimum execution time: 16_293 nanoseconds. + Weight::from_parts(16_509_000, 2653) + .saturating_add(RocksDbWeight::get().reads(1_u64)) + .saturating_add(RocksDbWeight::get().writes(2_u64)) + } + /// Storage: Uniques Class (r:1 w:1) + /// Proof: Uniques Class (max_values: None, max_size: Some(178), added: 2653, mode: MaxEncodedLen) + /// Storage: Uniques InstanceMetadataOf (r:1 w:0) + /// Proof: Uniques InstanceMetadataOf (max_values: None, max_size: Some(108), added: 2583, mode: MaxEncodedLen) + /// Storage: Uniques Attribute (r:1 w:1) + /// Proof: Uniques Attribute (max_values: None, max_size: Some(364), added: 2839, mode: MaxEncodedLen) fn set_attribute() -> Weight { - // Minimum execution time: 51_195 nanoseconds. - Weight::from_ref_time(51_674_000 as u64) - .saturating_add(RocksDbWeight::get().reads(3 as u64)) - .saturating_add(RocksDbWeight::get().writes(2 as u64)) - } - // Storage: Uniques Class (r:1 w:1) - // Storage: Uniques InstanceMetadataOf (r:1 w:0) - // Storage: Uniques Attribute (r:1 w:1) + // Proof Size summary in bytes: + // Measured: `611` + // Estimated: `8075` + // Minimum execution time: 33_560 nanoseconds. + Weight::from_parts(34_263_000, 8075) + .saturating_add(RocksDbWeight::get().reads(3_u64)) + .saturating_add(RocksDbWeight::get().writes(2_u64)) + } + /// Storage: Uniques Class (r:1 w:1) + /// Proof: Uniques Class (max_values: None, max_size: Some(178), added: 2653, mode: MaxEncodedLen) + /// Storage: Uniques InstanceMetadataOf (r:1 w:0) + /// Proof: Uniques InstanceMetadataOf (max_values: None, max_size: Some(108), added: 2583, mode: MaxEncodedLen) + /// Storage: Uniques Attribute (r:1 w:1) + /// Proof: Uniques Attribute (max_values: None, max_size: Some(364), added: 2839, mode: MaxEncodedLen) fn clear_attribute() -> Weight { - // Minimum execution time: 50_159 nanoseconds. - Weight::from_ref_time(51_412_000 as u64) - .saturating_add(RocksDbWeight::get().reads(3 as u64)) - .saturating_add(RocksDbWeight::get().writes(2 as u64)) - } - // Storage: Uniques Class (r:1 w:1) - // Storage: Uniques InstanceMetadataOf (r:1 w:1) + // Proof Size summary in bytes: + // Measured: `1031` + // Estimated: `8075` + // Minimum execution time: 31_955 nanoseconds. + Weight::from_parts(32_447_000, 8075) + .saturating_add(RocksDbWeight::get().reads(3_u64)) + .saturating_add(RocksDbWeight::get().writes(2_u64)) + } + /// Storage: Uniques Class (r:1 w:1) + /// Proof: Uniques Class (max_values: None, max_size: Some(178), added: 2653, mode: MaxEncodedLen) + /// Storage: Uniques InstanceMetadataOf (r:1 w:1) + /// Proof: Uniques InstanceMetadataOf (max_values: None, max_size: Some(108), added: 2583, mode: MaxEncodedLen) fn set_metadata() -> Weight { - // Minimum execution time: 42_608 nanoseconds. - Weight::from_ref_time(42_880_000 as u64) - .saturating_add(RocksDbWeight::get().reads(2 as u64)) - .saturating_add(RocksDbWeight::get().writes(2 as u64)) - } - // Storage: Uniques Class (r:1 w:1) - // Storage: Uniques InstanceMetadataOf (r:1 w:1) + // Proof Size summary in bytes: + // Measured: `447` + // Estimated: `5236` + // Minimum execution time: 25_520 nanoseconds. + Weight::from_parts(25_843_000, 5236) + .saturating_add(RocksDbWeight::get().reads(2_u64)) + .saturating_add(RocksDbWeight::get().writes(2_u64)) + } + /// Storage: Uniques Class (r:1 w:1) + /// Proof: Uniques Class (max_values: None, max_size: Some(178), added: 2653, mode: MaxEncodedLen) + /// Storage: Uniques InstanceMetadataOf (r:1 w:1) + /// Proof: Uniques InstanceMetadataOf (max_values: None, max_size: Some(108), added: 2583, mode: MaxEncodedLen) fn clear_metadata() -> Weight { - // Minimum execution time: 43_239 nanoseconds. - Weight::from_ref_time(43_752_000 as u64) - .saturating_add(RocksDbWeight::get().reads(2 as u64)) - .saturating_add(RocksDbWeight::get().writes(2 as u64)) - } - // Storage: Uniques Class (r:1 w:1) - // Storage: Uniques ClassMetadataOf (r:1 w:1) + // Proof Size summary in bytes: + // Measured: `611` + // Estimated: `5236` + // Minimum execution time: 25_812 nanoseconds. + Weight::from_parts(26_141_000, 5236) + .saturating_add(RocksDbWeight::get().reads(2_u64)) + .saturating_add(RocksDbWeight::get().writes(2_u64)) + } + /// Storage: Uniques Class (r:1 w:1) + /// Proof: Uniques Class (max_values: None, max_size: Some(178), added: 2653, mode: MaxEncodedLen) + /// Storage: Uniques ClassMetadataOf (r:1 w:1) + /// Proof: Uniques ClassMetadataOf (max_values: None, max_size: Some(88), added: 2563, mode: MaxEncodedLen) fn set_collection_metadata() -> Weight { - // Minimum execution time: 41_224 nanoseconds. - Weight::from_ref_time(41_974_000 as u64) - .saturating_add(RocksDbWeight::get().reads(2 as u64)) - .saturating_add(RocksDbWeight::get().writes(2 as u64)) - } - // Storage: Uniques Class (r:1 w:0) - // Storage: Uniques ClassMetadataOf (r:1 w:1) + // Proof Size summary in bytes: + // Measured: `381` + // Estimated: `5216` + // Minimum execution time: 25_055 nanoseconds. + Weight::from_parts(25_244_000, 5216) + .saturating_add(RocksDbWeight::get().reads(2_u64)) + .saturating_add(RocksDbWeight::get().writes(2_u64)) + } + /// Storage: Uniques Class (r:1 w:0) + /// Proof: Uniques Class (max_values: None, max_size: Some(178), added: 2653, mode: MaxEncodedLen) + /// Storage: Uniques ClassMetadataOf (r:1 w:1) + /// Proof: Uniques ClassMetadataOf (max_values: None, max_size: Some(88), added: 2563, mode: MaxEncodedLen) fn clear_collection_metadata() -> Weight { - // Minimum execution time: 40_836 nanoseconds. - Weight::from_ref_time(41_864_000 as u64) - .saturating_add(RocksDbWeight::get().reads(2 as u64)) - .saturating_add(RocksDbWeight::get().writes(1 as u64)) - } - // Storage: Uniques Class (r:1 w:0) - // Storage: Uniques Asset (r:1 w:1) + // Proof Size summary in bytes: + // Measured: `525` + // Estimated: `5216` + // Minimum execution time: 23_311 nanoseconds. + Weight::from_parts(23_682_000, 5216) + .saturating_add(RocksDbWeight::get().reads(2_u64)) + .saturating_add(RocksDbWeight::get().writes(1_u64)) + } + /// Storage: Uniques Class (r:1 w:0) + /// Proof: Uniques Class (max_values: None, max_size: Some(178), added: 2653, mode: MaxEncodedLen) + /// Storage: Uniques Asset (r:1 w:1) + /// Proof: Uniques Asset (max_values: None, max_size: Some(122), added: 2597, mode: MaxEncodedLen) fn approve_transfer() -> Weight { - // Minimum execution time: 29_558 nanoseconds. - Weight::from_ref_time(29_948_000 as u64) - .saturating_add(RocksDbWeight::get().reads(2 as u64)) - .saturating_add(RocksDbWeight::get().writes(1 as u64)) - } - // Storage: Uniques Class (r:1 w:0) - // Storage: Uniques Asset (r:1 w:1) + // Proof Size summary in bytes: + // Measured: `559` + // Estimated: `5250` + // Minimum execution time: 17_709 nanoseconds. + Weight::from_parts(18_308_000, 5250) + .saturating_add(RocksDbWeight::get().reads(2_u64)) + .saturating_add(RocksDbWeight::get().writes(1_u64)) + } + /// Storage: Uniques Class (r:1 w:0) + /// Proof: Uniques Class (max_values: None, max_size: Some(178), added: 2653, mode: MaxEncodedLen) + /// Storage: Uniques Asset (r:1 w:1) + /// Proof: Uniques Asset (max_values: None, max_size: Some(122), added: 2597, mode: MaxEncodedLen) fn cancel_approval() -> Weight { - // Minimum execution time: 29_694 nanoseconds. - Weight::from_ref_time(30_156_000 as u64) - .saturating_add(RocksDbWeight::get().reads(2 as u64)) - .saturating_add(RocksDbWeight::get().writes(1 as u64)) - } - // Storage: Uniques OwnershipAcceptance (r:1 w:1) + // Proof Size summary in bytes: + // Measured: `592` + // Estimated: `5250` + // Minimum execution time: 17_656 nanoseconds. + Weight::from_parts(18_039_000, 5250) + .saturating_add(RocksDbWeight::get().reads(2_u64)) + .saturating_add(RocksDbWeight::get().writes(1_u64)) + } + /// Storage: Uniques OwnershipAcceptance (r:1 w:1) + /// Proof: Uniques OwnershipAcceptance (max_values: None, max_size: Some(52), added: 2527, mode: MaxEncodedLen) fn set_accept_ownership() -> Weight { - // Minimum execution time: 27_819 nanoseconds. - Weight::from_ref_time(28_245_000 as u64) - .saturating_add(RocksDbWeight::get().reads(1 as u64)) - .saturating_add(RocksDbWeight::get().writes(1 as u64)) - } - // Storage: Uniques CollectionMaxSupply (r:1 w:1) - // Storage: Uniques Class (r:1 w:0) + // Proof Size summary in bytes: + // Measured: `109` + // Estimated: `2527` + // Minimum execution time: 14_615 nanoseconds. + Weight::from_parts(14_931_000, 2527) + .saturating_add(RocksDbWeight::get().reads(1_u64)) + .saturating_add(RocksDbWeight::get().writes(1_u64)) + } + /// Storage: Uniques CollectionMaxSupply (r:1 w:1) + /// Proof: Uniques CollectionMaxSupply (max_values: None, max_size: Some(24), added: 2499, mode: MaxEncodedLen) + /// Storage: Uniques Class (r:1 w:0) + /// Proof: Uniques Class (max_values: None, max_size: Some(178), added: 2653, mode: MaxEncodedLen) fn set_collection_max_supply() -> Weight { - // Minimum execution time: 26_317 nanoseconds. - Weight::from_ref_time(26_893_000 as u64) - .saturating_add(RocksDbWeight::get().reads(2 as u64)) - .saturating_add(RocksDbWeight::get().writes(1 as u64)) - } - // Storage: Uniques Asset (r:1 w:0) - // Storage: Uniques ItemPriceOf (r:0 w:1) + // Proof Size summary in bytes: + // Measured: `381` + // Estimated: `5152` + // Minimum execution time: 14_974 nanoseconds. + Weight::from_parts(15_314_000, 5152) + .saturating_add(RocksDbWeight::get().reads(2_u64)) + .saturating_add(RocksDbWeight::get().writes(1_u64)) + } + /// Storage: Uniques Asset (r:1 w:0) + /// Proof: Uniques Asset (max_values: None, max_size: Some(122), added: 2597, mode: MaxEncodedLen) + /// Storage: Uniques ItemPriceOf (r:0 w:1) + /// Proof: Uniques ItemPriceOf (max_values: None, max_size: Some(89), added: 2564, mode: MaxEncodedLen) fn set_price() -> Weight { - // Minimum execution time: 26_546 nanoseconds. - Weight::from_ref_time(27_142_000 as u64) - .saturating_add(RocksDbWeight::get().reads(1 as u64)) - .saturating_add(RocksDbWeight::get().writes(1 as u64)) - } - // Storage: Uniques Asset (r:1 w:1) - // Storage: Uniques ItemPriceOf (r:1 w:1) - // Storage: Uniques Class (r:1 w:0) - // Storage: Uniques Account (r:0 w:2) + // Proof Size summary in bytes: + // Measured: `358` + // Estimated: `2597` + // Minimum execution time: 15_444 nanoseconds. + Weight::from_parts(15_886_000, 2597) + .saturating_add(RocksDbWeight::get().reads(1_u64)) + .saturating_add(RocksDbWeight::get().writes(1_u64)) + } + /// Storage: Uniques Asset (r:1 w:1) + /// Proof: Uniques Asset (max_values: None, max_size: Some(122), added: 2597, mode: MaxEncodedLen) + /// Storage: Uniques ItemPriceOf (r:1 w:1) + /// Proof: Uniques ItemPriceOf (max_values: None, max_size: Some(89), added: 2564, mode: MaxEncodedLen) + /// Storage: Uniques Class (r:1 w:0) + /// Proof: Uniques Class (max_values: None, max_size: Some(178), added: 2653, mode: MaxEncodedLen) + /// Storage: Uniques Account (r:0 w:2) + /// Proof: Uniques Account (max_values: None, max_size: Some(88), added: 2563, mode: MaxEncodedLen) fn buy_item() -> Weight { - // Minimum execution time: 49_238 nanoseconds. - Weight::from_ref_time(50_444_000 as u64) - .saturating_add(RocksDbWeight::get().reads(3 as u64)) - .saturating_add(RocksDbWeight::get().writes(4 as u64)) + // Proof Size summary in bytes: + // Measured: `703` + // Estimated: `7814` + // Minimum execution time: 35_628 nanoseconds. + Weight::from_parts(35_886_000, 7814) + .saturating_add(RocksDbWeight::get().reads(3_u64)) + .saturating_add(RocksDbWeight::get().writes(4_u64)) } } diff --git a/frame/utility/src/weights.rs b/frame/utility/src/weights.rs index eac94e44b..7b8c261d1 100644 --- a/frame/utility/src/weights.rs +++ b/frame/utility/src/weights.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2022 Parity Technologies (UK) Ltd. +// Copyright (C) 2023 Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); @@ -18,7 +18,8 @@ //! Autogenerated weights for pallet_utility //! //! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev -//! DATE: 2022-11-07, STEPS: `50`, REPEAT: 20, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! DATE: 2023-01-25, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! WORST CASE MAP SIZE: `1000000` //! HOSTNAME: `bm2`, CPU: `Intel(R) Core(TM) i7-7700K CPU @ 4.20GHz` //! EXECUTION: Some(Wasm), WASM-EXECUTION: Compiled, CHAIN: Some("dev"), DB CACHE: 1024 @@ -59,32 +60,47 @@ pub struct SubstrateWeight(PhantomData); impl WeightInfo for SubstrateWeight { /// The range of component `c` is `[0, 1000]`. fn batch(c: u32, ) -> Weight { - // Minimum execution time: 14_470 nanoseconds. - Weight::from_ref_time(17_443_346 as u64) - // Standard Error: 2_037 - .saturating_add(Weight::from_ref_time(3_510_555 as u64).saturating_mul(c as u64)) + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 6_742 nanoseconds. + Weight::from_ref_time(16_087_635) + // Standard Error: 2_411 + .saturating_add(Weight::from_ref_time(3_665_506).saturating_mul(c.into())) } fn as_derivative() -> Weight { - // Minimum execution time: 6_799 nanoseconds. - Weight::from_ref_time(6_976_000 as u64) + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 4_802 nanoseconds. + Weight::from_ref_time(5_269_000) } /// The range of component `c` is `[0, 1000]`. fn batch_all(c: u32, ) -> Weight { - // Minimum execution time: 14_630 nanoseconds. - Weight::from_ref_time(24_580_656 as u64) - // Standard Error: 2_202 - .saturating_add(Weight::from_ref_time(3_584_516 as u64).saturating_mul(c as u64)) + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 7_100 nanoseconds. + Weight::from_ref_time(14_090_381) + // Standard Error: 1_917 + .saturating_add(Weight::from_ref_time(3_744_891).saturating_mul(c.into())) } fn dispatch_as() -> Weight { - // Minimum execution time: 16_597 nanoseconds. - Weight::from_ref_time(16_950_000 as u64) + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 8_840 nanoseconds. + Weight::from_ref_time(9_280_000) } /// The range of component `c` is `[0, 1000]`. fn force_batch(c: u32, ) -> Weight { - // Minimum execution time: 13_885 nanoseconds. - Weight::from_ref_time(20_147_978 as u64) - // Standard Error: 2_232 - .saturating_add(Weight::from_ref_time(3_516_969 as u64).saturating_mul(c as u64)) + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 7_245 nanoseconds. + Weight::from_ref_time(14_292_923) + // Standard Error: 1_803 + .saturating_add(Weight::from_ref_time(3_645_950).saturating_mul(c.into())) } } @@ -92,31 +108,46 @@ impl WeightInfo for SubstrateWeight { impl WeightInfo for () { /// The range of component `c` is `[0, 1000]`. fn batch(c: u32, ) -> Weight { - // Minimum execution time: 14_470 nanoseconds. - Weight::from_ref_time(17_443_346 as u64) - // Standard Error: 2_037 - .saturating_add(Weight::from_ref_time(3_510_555 as u64).saturating_mul(c as u64)) + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 6_742 nanoseconds. + Weight::from_ref_time(16_087_635) + // Standard Error: 2_411 + .saturating_add(Weight::from_ref_time(3_665_506).saturating_mul(c.into())) } fn as_derivative() -> Weight { - // Minimum execution time: 6_799 nanoseconds. - Weight::from_ref_time(6_976_000 as u64) + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 4_802 nanoseconds. + Weight::from_ref_time(5_269_000) } /// The range of component `c` is `[0, 1000]`. fn batch_all(c: u32, ) -> Weight { - // Minimum execution time: 14_630 nanoseconds. - Weight::from_ref_time(24_580_656 as u64) - // Standard Error: 2_202 - .saturating_add(Weight::from_ref_time(3_584_516 as u64).saturating_mul(c as u64)) + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 7_100 nanoseconds. + Weight::from_ref_time(14_090_381) + // Standard Error: 1_917 + .saturating_add(Weight::from_ref_time(3_744_891).saturating_mul(c.into())) } fn dispatch_as() -> Weight { - // Minimum execution time: 16_597 nanoseconds. - Weight::from_ref_time(16_950_000 as u64) + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 8_840 nanoseconds. + Weight::from_ref_time(9_280_000) } /// The range of component `c` is `[0, 1000]`. fn force_batch(c: u32, ) -> Weight { - // Minimum execution time: 13_885 nanoseconds. - Weight::from_ref_time(20_147_978 as u64) - // Standard Error: 2_232 - .saturating_add(Weight::from_ref_time(3_516_969 as u64).saturating_mul(c as u64)) + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 7_245 nanoseconds. + Weight::from_ref_time(14_292_923) + // Standard Error: 1_803 + .saturating_add(Weight::from_ref_time(3_645_950).saturating_mul(c.into())) } } diff --git a/frame/vesting/src/weights.rs b/frame/vesting/src/weights.rs index 546244541..a6a70fb56 100644 --- a/frame/vesting/src/weights.rs +++ b/frame/vesting/src/weights.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2022 Parity Technologies (UK) Ltd. +// Copyright (C) 2023 Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); @@ -18,7 +18,8 @@ //! Autogenerated weights for pallet_vesting //! //! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev -//! DATE: 2022-11-07, STEPS: `50`, REPEAT: 20, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! DATE: 2023-01-25, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! WORST CASE MAP SIZE: `1000000` //! HOSTNAME: `bm2`, CPU: `Intel(R) Core(TM) i7-7700K CPU @ 4.20GHz` //! EXECUTION: Some(Wasm), WASM-EXECUTION: Compiled, CHAIN: Some("dev"), DB CACHE: 1024 @@ -60,244 +61,336 @@ pub trait WeightInfo { /// Weights for pallet_vesting using the Substrate node and recommended hardware. pub struct SubstrateWeight(PhantomData); impl WeightInfo for SubstrateWeight { - // Storage: Vesting Vesting (r:1 w:1) - // Storage: Balances Locks (r:1 w:1) + /// Storage: Vesting Vesting (r:1 w:1) + /// Proof: Vesting Vesting (max_values: None, max_size: Some(1057), added: 3532, mode: MaxEncodedLen) + /// Storage: Balances Locks (r:1 w:1) + /// Proof: Balances Locks (max_values: None, max_size: Some(1299), added: 3774, mode: MaxEncodedLen) /// The range of component `l` is `[0, 49]`. /// The range of component `s` is `[1, 28]`. fn vest_locked(l: u32, s: u32, ) -> Weight { - // Minimum execution time: 45_113 nanoseconds. - Weight::from_ref_time(44_114_539 as u64) - // Standard Error: 958 - .saturating_add(Weight::from_ref_time(56_239 as u64).saturating_mul(l as u64)) - // Standard Error: 1_704 - .saturating_add(Weight::from_ref_time(64_926 as u64).saturating_mul(s as u64)) - .saturating_add(T::DbWeight::get().reads(2 as u64)) - .saturating_add(T::DbWeight::get().writes(2 as u64)) + // Proof Size summary in bytes: + // Measured: `444 + l * (25 ±0) + s * (36 ±0)` + // Estimated: `7306` + // Minimum execution time: 28_062 nanoseconds. + Weight::from_parts(26_857_563, 7306) + // Standard Error: 623 + .saturating_add(Weight::from_ref_time(55_988).saturating_mul(l.into())) + // Standard Error: 1_109 + .saturating_add(Weight::from_ref_time(59_714).saturating_mul(s.into())) + .saturating_add(T::DbWeight::get().reads(2_u64)) + .saturating_add(T::DbWeight::get().writes(2_u64)) } - // Storage: Vesting Vesting (r:1 w:1) - // Storage: Balances Locks (r:1 w:1) + /// Storage: Vesting Vesting (r:1 w:1) + /// Proof: Vesting Vesting (max_values: None, max_size: Some(1057), added: 3532, mode: MaxEncodedLen) + /// Storage: Balances Locks (r:1 w:1) + /// Proof: Balances Locks (max_values: None, max_size: Some(1299), added: 3774, mode: MaxEncodedLen) /// The range of component `l` is `[0, 49]`. /// The range of component `s` is `[1, 28]`. fn vest_unlocked(l: u32, s: u32, ) -> Weight { - // Minimum execution time: 43_918 nanoseconds. - Weight::from_ref_time(43_452_573 as u64) - // Standard Error: 984 - .saturating_add(Weight::from_ref_time(50_162 as u64).saturating_mul(l as u64)) - // Standard Error: 1_752 - .saturating_add(Weight::from_ref_time(42_080 as u64).saturating_mul(s as u64)) - .saturating_add(T::DbWeight::get().reads(2 as u64)) - .saturating_add(T::DbWeight::get().writes(2 as u64)) + // Proof Size summary in bytes: + // Measured: `444 + l * (25 ±0) + s * (36 ±0)` + // Estimated: `7306` + // Minimum execution time: 27_027 nanoseconds. + Weight::from_parts(26_509_364, 7306) + // Standard Error: 815 + .saturating_add(Weight::from_ref_time(54_711).saturating_mul(l.into())) + // Standard Error: 1_451 + .saturating_add(Weight::from_ref_time(32_792).saturating_mul(s.into())) + .saturating_add(T::DbWeight::get().reads(2_u64)) + .saturating_add(T::DbWeight::get().writes(2_u64)) } - // Storage: Vesting Vesting (r:1 w:1) - // Storage: Balances Locks (r:1 w:1) - // Storage: System Account (r:1 w:1) + /// Storage: Vesting Vesting (r:1 w:1) + /// Proof: Vesting Vesting (max_values: None, max_size: Some(1057), added: 3532, mode: MaxEncodedLen) + /// Storage: Balances Locks (r:1 w:1) + /// Proof: Balances Locks (max_values: None, max_size: Some(1299), added: 3774, mode: MaxEncodedLen) + /// Storage: System Account (r:1 w:1) + /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) /// The range of component `l` is `[0, 49]`. /// The range of component `s` is `[1, 28]`. fn vest_other_locked(l: u32, s: u32, ) -> Weight { - // Minimum execution time: 43_603 nanoseconds. - Weight::from_ref_time(42_696_097 as u64) - // Standard Error: 996 - .saturating_add(Weight::from_ref_time(65_316 as u64).saturating_mul(l as u64)) - // Standard Error: 1_772 - .saturating_add(Weight::from_ref_time(65_862 as u64).saturating_mul(s as u64)) - .saturating_add(T::DbWeight::get().reads(3 as u64)) - .saturating_add(T::DbWeight::get().writes(3 as u64)) + // Proof Size summary in bytes: + // Measured: `579 + l * (25 ±0) + s * (36 ±0)` + // Estimated: `9909` + // Minimum execution time: 29_554 nanoseconds. + Weight::from_parts(28_269_203, 9909) + // Standard Error: 623 + .saturating_add(Weight::from_ref_time(59_058).saturating_mul(l.into())) + // Standard Error: 1_108 + .saturating_add(Weight::from_ref_time(63_429).saturating_mul(s.into())) + .saturating_add(T::DbWeight::get().reads(3_u64)) + .saturating_add(T::DbWeight::get().writes(3_u64)) } - // Storage: Vesting Vesting (r:1 w:1) - // Storage: Balances Locks (r:1 w:1) - // Storage: System Account (r:1 w:1) + /// Storage: Vesting Vesting (r:1 w:1) + /// Proof: Vesting Vesting (max_values: None, max_size: Some(1057), added: 3532, mode: MaxEncodedLen) + /// Storage: Balances Locks (r:1 w:1) + /// Proof: Balances Locks (max_values: None, max_size: Some(1299), added: 3774, mode: MaxEncodedLen) + /// Storage: System Account (r:1 w:1) + /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) /// The range of component `l` is `[0, 49]`. /// The range of component `s` is `[1, 28]`. fn vest_other_unlocked(l: u32, s: u32, ) -> Weight { - // Minimum execution time: 43_099 nanoseconds. - Weight::from_ref_time(42_937_914 as u64) - // Standard Error: 884 - .saturating_add(Weight::from_ref_time(52_079 as u64).saturating_mul(l as u64)) - // Standard Error: 1_573 - .saturating_add(Weight::from_ref_time(36_274 as u64).saturating_mul(s as u64)) - .saturating_add(T::DbWeight::get().reads(3 as u64)) - .saturating_add(T::DbWeight::get().writes(3 as u64)) + // Proof Size summary in bytes: + // Measured: `579 + l * (25 ±0) + s * (36 ±0)` + // Estimated: `9909` + // Minimum execution time: 28_546 nanoseconds. + Weight::from_parts(28_299_251, 9909) + // Standard Error: 786 + .saturating_add(Weight::from_ref_time(53_401).saturating_mul(l.into())) + // Standard Error: 1_399 + .saturating_add(Weight::from_ref_time(29_713).saturating_mul(s.into())) + .saturating_add(T::DbWeight::get().reads(3_u64)) + .saturating_add(T::DbWeight::get().writes(3_u64)) } - // Storage: Vesting Vesting (r:1 w:1) - // Storage: System Account (r:1 w:1) - // Storage: Balances Locks (r:1 w:1) + /// Storage: Vesting Vesting (r:1 w:1) + /// Proof: Vesting Vesting (max_values: None, max_size: Some(1057), added: 3532, mode: MaxEncodedLen) + /// Storage: System Account (r:1 w:1) + /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) + /// Storage: Balances Locks (r:1 w:1) + /// Proof: Balances Locks (max_values: None, max_size: Some(1299), added: 3774, mode: MaxEncodedLen) /// The range of component `l` is `[0, 49]`. /// The range of component `s` is `[0, 27]`. fn vested_transfer(l: u32, s: u32, ) -> Weight { - // Minimum execution time: 59_023 nanoseconds. - Weight::from_ref_time(59_606_862 as u64) - // Standard Error: 2_078 - .saturating_add(Weight::from_ref_time(55_335 as u64).saturating_mul(l as u64)) - // Standard Error: 3_698 - .saturating_add(Weight::from_ref_time(26_743 as u64).saturating_mul(s as u64)) - .saturating_add(T::DbWeight::get().reads(3 as u64)) - .saturating_add(T::DbWeight::get().writes(3 as u64)) + // Proof Size summary in bytes: + // Measured: `650 + l * (25 ±0) + s * (36 ±0)` + // Estimated: `9909` + // Minimum execution time: 43_436 nanoseconds. + Weight::from_parts(44_885_707, 9909) + // Standard Error: 1_516 + .saturating_add(Weight::from_ref_time(59_066).saturating_mul(l.into())) + // Standard Error: 2_698 + .saturating_add(Weight::from_ref_time(32_053).saturating_mul(s.into())) + .saturating_add(T::DbWeight::get().reads(3_u64)) + .saturating_add(T::DbWeight::get().writes(3_u64)) } - // Storage: Vesting Vesting (r:1 w:1) - // Storage: System Account (r:2 w:2) - // Storage: Balances Locks (r:1 w:1) + /// Storage: Vesting Vesting (r:1 w:1) + /// Proof: Vesting Vesting (max_values: None, max_size: Some(1057), added: 3532, mode: MaxEncodedLen) + /// Storage: System Account (r:2 w:2) + /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) + /// Storage: Balances Locks (r:1 w:1) + /// Proof: Balances Locks (max_values: None, max_size: Some(1299), added: 3774, mode: MaxEncodedLen) /// The range of component `l` is `[0, 49]`. /// The range of component `s` is `[0, 27]`. fn force_vested_transfer(l: u32, s: u32, ) -> Weight { - // Minimum execution time: 58_249 nanoseconds. - Weight::from_ref_time(59_025_976 as u64) - // Standard Error: 2_078 - .saturating_add(Weight::from_ref_time(55_736 as u64).saturating_mul(l as u64)) - // Standard Error: 3_697 - .saturating_add(Weight::from_ref_time(24_903 as u64).saturating_mul(s as u64)) - .saturating_add(T::DbWeight::get().reads(4 as u64)) - .saturating_add(T::DbWeight::get().writes(4 as u64)) + // Proof Size summary in bytes: + // Measured: `785 + l * (25 ±0) + s * (36 ±0)` + // Estimated: `12512` + // Minimum execution time: 45_805 nanoseconds. + Weight::from_parts(46_869_490, 12512) + // Standard Error: 1_445 + .saturating_add(Weight::from_ref_time(52_654).saturating_mul(l.into())) + // Standard Error: 2_571 + .saturating_add(Weight::from_ref_time(34_202).saturating_mul(s.into())) + .saturating_add(T::DbWeight::get().reads(4_u64)) + .saturating_add(T::DbWeight::get().writes(4_u64)) } - // Storage: Vesting Vesting (r:1 w:1) - // Storage: Balances Locks (r:1 w:1) - // Storage: System Account (r:1 w:1) + /// Storage: Vesting Vesting (r:1 w:1) + /// Proof: Vesting Vesting (max_values: None, max_size: Some(1057), added: 3532, mode: MaxEncodedLen) + /// Storage: Balances Locks (r:1 w:1) + /// Proof: Balances Locks (max_values: None, max_size: Some(1299), added: 3774, mode: MaxEncodedLen) + /// Storage: System Account (r:1 w:1) + /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) /// The range of component `l` is `[0, 49]`. /// The range of component `s` is `[2, 28]`. fn not_unlocking_merge_schedules(l: u32, s: u32, ) -> Weight { - // Minimum execution time: 45_279 nanoseconds. - Weight::from_ref_time(44_197_440 as u64) - // Standard Error: 946 - .saturating_add(Weight::from_ref_time(62_308 as u64).saturating_mul(l as u64)) - // Standard Error: 1_747 - .saturating_add(Weight::from_ref_time(64_473 as u64).saturating_mul(s as u64)) - .saturating_add(T::DbWeight::get().reads(3 as u64)) - .saturating_add(T::DbWeight::get().writes(3 as u64)) + // Proof Size summary in bytes: + // Measured: `577 + l * (25 ±0) + s * (36 ±0)` + // Estimated: `9909` + // Minimum execution time: 30_460 nanoseconds. + Weight::from_parts(29_407_637, 9909) + // Standard Error: 794 + .saturating_add(Weight::from_ref_time(63_757).saturating_mul(l.into())) + // Standard Error: 1_466 + .saturating_add(Weight::from_ref_time(56_032).saturating_mul(s.into())) + .saturating_add(T::DbWeight::get().reads(3_u64)) + .saturating_add(T::DbWeight::get().writes(3_u64)) } - // Storage: Vesting Vesting (r:1 w:1) - // Storage: Balances Locks (r:1 w:1) - // Storage: System Account (r:1 w:1) + /// Storage: Vesting Vesting (r:1 w:1) + /// Proof: Vesting Vesting (max_values: None, max_size: Some(1057), added: 3532, mode: MaxEncodedLen) + /// Storage: Balances Locks (r:1 w:1) + /// Proof: Balances Locks (max_values: None, max_size: Some(1299), added: 3774, mode: MaxEncodedLen) + /// Storage: System Account (r:1 w:1) + /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) /// The range of component `l` is `[0, 49]`. /// The range of component `s` is `[2, 28]`. fn unlocking_merge_schedules(l: u32, s: u32, ) -> Weight { - // Minimum execution time: 44_925 nanoseconds. - Weight::from_ref_time(44_219_676 as u64) - // Standard Error: 889 - .saturating_add(Weight::from_ref_time(60_311 as u64).saturating_mul(l as u64)) - // Standard Error: 1_641 - .saturating_add(Weight::from_ref_time(63_095 as u64).saturating_mul(s as u64)) - .saturating_add(T::DbWeight::get().reads(3 as u64)) - .saturating_add(T::DbWeight::get().writes(3 as u64)) + // Proof Size summary in bytes: + // Measured: `577 + l * (25 ±0) + s * (36 ±0)` + // Estimated: `9909` + // Minimum execution time: 30_413 nanoseconds. + Weight::from_parts(29_350_467, 9909) + // Standard Error: 724 + .saturating_add(Weight::from_ref_time(65_366).saturating_mul(l.into())) + // Standard Error: 1_337 + .saturating_add(Weight::from_ref_time(53_799).saturating_mul(s.into())) + .saturating_add(T::DbWeight::get().reads(3_u64)) + .saturating_add(T::DbWeight::get().writes(3_u64)) } } // For backwards compatibility and tests impl WeightInfo for () { - // Storage: Vesting Vesting (r:1 w:1) - // Storage: Balances Locks (r:1 w:1) + /// Storage: Vesting Vesting (r:1 w:1) + /// Proof: Vesting Vesting (max_values: None, max_size: Some(1057), added: 3532, mode: MaxEncodedLen) + /// Storage: Balances Locks (r:1 w:1) + /// Proof: Balances Locks (max_values: None, max_size: Some(1299), added: 3774, mode: MaxEncodedLen) /// The range of component `l` is `[0, 49]`. /// The range of component `s` is `[1, 28]`. fn vest_locked(l: u32, s: u32, ) -> Weight { - // Minimum execution time: 45_113 nanoseconds. - Weight::from_ref_time(44_114_539 as u64) - // Standard Error: 958 - .saturating_add(Weight::from_ref_time(56_239 as u64).saturating_mul(l as u64)) - // Standard Error: 1_704 - .saturating_add(Weight::from_ref_time(64_926 as u64).saturating_mul(s as u64)) - .saturating_add(RocksDbWeight::get().reads(2 as u64)) - .saturating_add(RocksDbWeight::get().writes(2 as u64)) + // Proof Size summary in bytes: + // Measured: `444 + l * (25 ±0) + s * (36 ±0)` + // Estimated: `7306` + // Minimum execution time: 28_062 nanoseconds. + Weight::from_parts(26_857_563, 7306) + // Standard Error: 623 + .saturating_add(Weight::from_ref_time(55_988).saturating_mul(l.into())) + // Standard Error: 1_109 + .saturating_add(Weight::from_ref_time(59_714).saturating_mul(s.into())) + .saturating_add(RocksDbWeight::get().reads(2_u64)) + .saturating_add(RocksDbWeight::get().writes(2_u64)) } - // Storage: Vesting Vesting (r:1 w:1) - // Storage: Balances Locks (r:1 w:1) + /// Storage: Vesting Vesting (r:1 w:1) + /// Proof: Vesting Vesting (max_values: None, max_size: Some(1057), added: 3532, mode: MaxEncodedLen) + /// Storage: Balances Locks (r:1 w:1) + /// Proof: Balances Locks (max_values: None, max_size: Some(1299), added: 3774, mode: MaxEncodedLen) /// The range of component `l` is `[0, 49]`. /// The range of component `s` is `[1, 28]`. fn vest_unlocked(l: u32, s: u32, ) -> Weight { - // Minimum execution time: 43_918 nanoseconds. - Weight::from_ref_time(43_452_573 as u64) - // Standard Error: 984 - .saturating_add(Weight::from_ref_time(50_162 as u64).saturating_mul(l as u64)) - // Standard Error: 1_752 - .saturating_add(Weight::from_ref_time(42_080 as u64).saturating_mul(s as u64)) - .saturating_add(RocksDbWeight::get().reads(2 as u64)) - .saturating_add(RocksDbWeight::get().writes(2 as u64)) + // Proof Size summary in bytes: + // Measured: `444 + l * (25 ±0) + s * (36 ±0)` + // Estimated: `7306` + // Minimum execution time: 27_027 nanoseconds. + Weight::from_parts(26_509_364, 7306) + // Standard Error: 815 + .saturating_add(Weight::from_ref_time(54_711).saturating_mul(l.into())) + // Standard Error: 1_451 + .saturating_add(Weight::from_ref_time(32_792).saturating_mul(s.into())) + .saturating_add(RocksDbWeight::get().reads(2_u64)) + .saturating_add(RocksDbWeight::get().writes(2_u64)) } - // Storage: Vesting Vesting (r:1 w:1) - // Storage: Balances Locks (r:1 w:1) - // Storage: System Account (r:1 w:1) + /// Storage: Vesting Vesting (r:1 w:1) + /// Proof: Vesting Vesting (max_values: None, max_size: Some(1057), added: 3532, mode: MaxEncodedLen) + /// Storage: Balances Locks (r:1 w:1) + /// Proof: Balances Locks (max_values: None, max_size: Some(1299), added: 3774, mode: MaxEncodedLen) + /// Storage: System Account (r:1 w:1) + /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) /// The range of component `l` is `[0, 49]`. /// The range of component `s` is `[1, 28]`. fn vest_other_locked(l: u32, s: u32, ) -> Weight { - // Minimum execution time: 43_603 nanoseconds. - Weight::from_ref_time(42_696_097 as u64) - // Standard Error: 996 - .saturating_add(Weight::from_ref_time(65_316 as u64).saturating_mul(l as u64)) - // Standard Error: 1_772 - .saturating_add(Weight::from_ref_time(65_862 as u64).saturating_mul(s as u64)) - .saturating_add(RocksDbWeight::get().reads(3 as u64)) - .saturating_add(RocksDbWeight::get().writes(3 as u64)) + // Proof Size summary in bytes: + // Measured: `579 + l * (25 ±0) + s * (36 ±0)` + // Estimated: `9909` + // Minimum execution time: 29_554 nanoseconds. + Weight::from_parts(28_269_203, 9909) + // Standard Error: 623 + .saturating_add(Weight::from_ref_time(59_058).saturating_mul(l.into())) + // Standard Error: 1_108 + .saturating_add(Weight::from_ref_time(63_429).saturating_mul(s.into())) + .saturating_add(RocksDbWeight::get().reads(3_u64)) + .saturating_add(RocksDbWeight::get().writes(3_u64)) } - // Storage: Vesting Vesting (r:1 w:1) - // Storage: Balances Locks (r:1 w:1) - // Storage: System Account (r:1 w:1) + /// Storage: Vesting Vesting (r:1 w:1) + /// Proof: Vesting Vesting (max_values: None, max_size: Some(1057), added: 3532, mode: MaxEncodedLen) + /// Storage: Balances Locks (r:1 w:1) + /// Proof: Balances Locks (max_values: None, max_size: Some(1299), added: 3774, mode: MaxEncodedLen) + /// Storage: System Account (r:1 w:1) + /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) /// The range of component `l` is `[0, 49]`. /// The range of component `s` is `[1, 28]`. fn vest_other_unlocked(l: u32, s: u32, ) -> Weight { - // Minimum execution time: 43_099 nanoseconds. - Weight::from_ref_time(42_937_914 as u64) - // Standard Error: 884 - .saturating_add(Weight::from_ref_time(52_079 as u64).saturating_mul(l as u64)) - // Standard Error: 1_573 - .saturating_add(Weight::from_ref_time(36_274 as u64).saturating_mul(s as u64)) - .saturating_add(RocksDbWeight::get().reads(3 as u64)) - .saturating_add(RocksDbWeight::get().writes(3 as u64)) + // Proof Size summary in bytes: + // Measured: `579 + l * (25 ±0) + s * (36 ±0)` + // Estimated: `9909` + // Minimum execution time: 28_546 nanoseconds. + Weight::from_parts(28_299_251, 9909) + // Standard Error: 786 + .saturating_add(Weight::from_ref_time(53_401).saturating_mul(l.into())) + // Standard Error: 1_399 + .saturating_add(Weight::from_ref_time(29_713).saturating_mul(s.into())) + .saturating_add(RocksDbWeight::get().reads(3_u64)) + .saturating_add(RocksDbWeight::get().writes(3_u64)) } - // Storage: Vesting Vesting (r:1 w:1) - // Storage: System Account (r:1 w:1) - // Storage: Balances Locks (r:1 w:1) + /// Storage: Vesting Vesting (r:1 w:1) + /// Proof: Vesting Vesting (max_values: None, max_size: Some(1057), added: 3532, mode: MaxEncodedLen) + /// Storage: System Account (r:1 w:1) + /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) + /// Storage: Balances Locks (r:1 w:1) + /// Proof: Balances Locks (max_values: None, max_size: Some(1299), added: 3774, mode: MaxEncodedLen) /// The range of component `l` is `[0, 49]`. /// The range of component `s` is `[0, 27]`. fn vested_transfer(l: u32, s: u32, ) -> Weight { - // Minimum execution time: 59_023 nanoseconds. - Weight::from_ref_time(59_606_862 as u64) - // Standard Error: 2_078 - .saturating_add(Weight::from_ref_time(55_335 as u64).saturating_mul(l as u64)) - // Standard Error: 3_698 - .saturating_add(Weight::from_ref_time(26_743 as u64).saturating_mul(s as u64)) - .saturating_add(RocksDbWeight::get().reads(3 as u64)) - .saturating_add(RocksDbWeight::get().writes(3 as u64)) + // Proof Size summary in bytes: + // Measured: `650 + l * (25 ±0) + s * (36 ±0)` + // Estimated: `9909` + // Minimum execution time: 43_436 nanoseconds. + Weight::from_parts(44_885_707, 9909) + // Standard Error: 1_516 + .saturating_add(Weight::from_ref_time(59_066).saturating_mul(l.into())) + // Standard Error: 2_698 + .saturating_add(Weight::from_ref_time(32_053).saturating_mul(s.into())) + .saturating_add(RocksDbWeight::get().reads(3_u64)) + .saturating_add(RocksDbWeight::get().writes(3_u64)) } - // Storage: Vesting Vesting (r:1 w:1) - // Storage: System Account (r:2 w:2) - // Storage: Balances Locks (r:1 w:1) + /// Storage: Vesting Vesting (r:1 w:1) + /// Proof: Vesting Vesting (max_values: None, max_size: Some(1057), added: 3532, mode: MaxEncodedLen) + /// Storage: System Account (r:2 w:2) + /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) + /// Storage: Balances Locks (r:1 w:1) + /// Proof: Balances Locks (max_values: None, max_size: Some(1299), added: 3774, mode: MaxEncodedLen) /// The range of component `l` is `[0, 49]`. /// The range of component `s` is `[0, 27]`. fn force_vested_transfer(l: u32, s: u32, ) -> Weight { - // Minimum execution time: 58_249 nanoseconds. - Weight::from_ref_time(59_025_976 as u64) - // Standard Error: 2_078 - .saturating_add(Weight::from_ref_time(55_736 as u64).saturating_mul(l as u64)) - // Standard Error: 3_697 - .saturating_add(Weight::from_ref_time(24_903 as u64).saturating_mul(s as u64)) - .saturating_add(RocksDbWeight::get().reads(4 as u64)) - .saturating_add(RocksDbWeight::get().writes(4 as u64)) + // Proof Size summary in bytes: + // Measured: `785 + l * (25 ±0) + s * (36 ±0)` + // Estimated: `12512` + // Minimum execution time: 45_805 nanoseconds. + Weight::from_parts(46_869_490, 12512) + // Standard Error: 1_445 + .saturating_add(Weight::from_ref_time(52_654).saturating_mul(l.into())) + // Standard Error: 2_571 + .saturating_add(Weight::from_ref_time(34_202).saturating_mul(s.into())) + .saturating_add(RocksDbWeight::get().reads(4_u64)) + .saturating_add(RocksDbWeight::get().writes(4_u64)) } - // Storage: Vesting Vesting (r:1 w:1) - // Storage: Balances Locks (r:1 w:1) - // Storage: System Account (r:1 w:1) + /// Storage: Vesting Vesting (r:1 w:1) + /// Proof: Vesting Vesting (max_values: None, max_size: Some(1057), added: 3532, mode: MaxEncodedLen) + /// Storage: Balances Locks (r:1 w:1) + /// Proof: Balances Locks (max_values: None, max_size: Some(1299), added: 3774, mode: MaxEncodedLen) + /// Storage: System Account (r:1 w:1) + /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) /// The range of component `l` is `[0, 49]`. /// The range of component `s` is `[2, 28]`. fn not_unlocking_merge_schedules(l: u32, s: u32, ) -> Weight { - // Minimum execution time: 45_279 nanoseconds. - Weight::from_ref_time(44_197_440 as u64) - // Standard Error: 946 - .saturating_add(Weight::from_ref_time(62_308 as u64).saturating_mul(l as u64)) - // Standard Error: 1_747 - .saturating_add(Weight::from_ref_time(64_473 as u64).saturating_mul(s as u64)) - .saturating_add(RocksDbWeight::get().reads(3 as u64)) - .saturating_add(RocksDbWeight::get().writes(3 as u64)) + // Proof Size summary in bytes: + // Measured: `577 + l * (25 ±0) + s * (36 ±0)` + // Estimated: `9909` + // Minimum execution time: 30_460 nanoseconds. + Weight::from_parts(29_407_637, 9909) + // Standard Error: 794 + .saturating_add(Weight::from_ref_time(63_757).saturating_mul(l.into())) + // Standard Error: 1_466 + .saturating_add(Weight::from_ref_time(56_032).saturating_mul(s.into())) + .saturating_add(RocksDbWeight::get().reads(3_u64)) + .saturating_add(RocksDbWeight::get().writes(3_u64)) } - // Storage: Vesting Vesting (r:1 w:1) - // Storage: Balances Locks (r:1 w:1) - // Storage: System Account (r:1 w:1) + /// Storage: Vesting Vesting (r:1 w:1) + /// Proof: Vesting Vesting (max_values: None, max_size: Some(1057), added: 3532, mode: MaxEncodedLen) + /// Storage: Balances Locks (r:1 w:1) + /// Proof: Balances Locks (max_values: None, max_size: Some(1299), added: 3774, mode: MaxEncodedLen) + /// Storage: System Account (r:1 w:1) + /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) /// The range of component `l` is `[0, 49]`. /// The range of component `s` is `[2, 28]`. fn unlocking_merge_schedules(l: u32, s: u32, ) -> Weight { - // Minimum execution time: 44_925 nanoseconds. - Weight::from_ref_time(44_219_676 as u64) - // Standard Error: 889 - .saturating_add(Weight::from_ref_time(60_311 as u64).saturating_mul(l as u64)) - // Standard Error: 1_641 - .saturating_add(Weight::from_ref_time(63_095 as u64).saturating_mul(s as u64)) - .saturating_add(RocksDbWeight::get().reads(3 as u64)) - .saturating_add(RocksDbWeight::get().writes(3 as u64)) + // Proof Size summary in bytes: + // Measured: `577 + l * (25 ±0) + s * (36 ±0)` + // Estimated: `9909` + // Minimum execution time: 30_413 nanoseconds. + Weight::from_parts(29_350_467, 9909) + // Standard Error: 724 + .saturating_add(Weight::from_ref_time(65_366).saturating_mul(l.into())) + // Standard Error: 1_337 + .saturating_add(Weight::from_ref_time(53_799).saturating_mul(s.into())) + .saturating_add(RocksDbWeight::get().reads(3_u64)) + .saturating_add(RocksDbWeight::get().writes(3_u64)) } } diff --git a/frame/whitelist/src/benchmarking.rs b/frame/whitelist/src/benchmarking.rs index 923adc6cc..569b13c12 100644 --- a/frame/whitelist/src/benchmarking.rs +++ b/frame/whitelist/src/benchmarking.rs @@ -62,6 +62,10 @@ benchmarks! { // We benchmark with the maximum possible size for a call. // If the resulting weight is too big, maybe it worth having a weight which depends // on the size of the call, with a new witness in parameter. + #[pov_mode = MaxEncodedLen { + // Use measured PoV size for the Preimages since we pass in a length witness. + Preimage::PreimageFor: Measured + }] dispatch_whitelisted_call { // NOTE: we remove `10` because we need some bytes to encode the variants and vec length let n in 1 .. T::Preimages::MAX_LENGTH as u32 - 10; diff --git a/frame/whitelist/src/weights.rs b/frame/whitelist/src/weights.rs index efd48d657..d5bc37c57 100644 --- a/frame/whitelist/src/weights.rs +++ b/frame/whitelist/src/weights.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2022 Parity Technologies (UK) Ltd. +// Copyright (C) 2023 Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); @@ -18,25 +18,25 @@ //! Autogenerated weights for pallet_whitelist //! //! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev -//! DATE: 2022-12-05, STEPS: `50`, REPEAT: 20, LOW RANGE: `[]`, HIGH RANGE: `[]` -//! HOSTNAME: `bm3`, CPU: `Intel(R) Core(TM) i7-7700K CPU @ 4.20GHz` +//! DATE: 2023-01-25, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! WORST CASE MAP SIZE: `1000000` +//! HOSTNAME: `bm2`, CPU: `Intel(R) Core(TM) i7-7700K CPU @ 4.20GHz` //! EXECUTION: Some(Wasm), WASM-EXECUTION: Compiled, CHAIN: Some("dev"), DB CACHE: 1024 // Executed Command: -// /home/benchbot/cargo_target_dir/production/substrate +// ./target/production/substrate // benchmark // pallet +// --chain=dev // --steps=50 // --repeat=20 +// --pallet=pallet_whitelist // --extrinsic=* // --execution=wasm // --wasm-execution=compiled // --heap-pages=4096 -// --json-file=/var/lib/gitlab-runner/builds/zyw4fam_/0/parity/mirrors/substrate/.git/.artifacts/bench.json -// --pallet=pallet_whitelist -// --chain=dev -// --header=./HEADER-APACHE2 // --output=./frame/whitelist/src/weights.rs +// --header=./HEADER-APACHE2 // --template=./.maintain/frame-weight-template.hbs #![cfg_attr(rustfmt, rustfmt_skip)] @@ -57,86 +57,130 @@ pub trait WeightInfo { /// Weights for pallet_whitelist using the Substrate node and recommended hardware. pub struct SubstrateWeight(PhantomData); impl WeightInfo for SubstrateWeight { - // Storage: Whitelist WhitelistedCall (r:1 w:1) - // Storage: Preimage StatusFor (r:1 w:1) + /// Storage: Whitelist WhitelistedCall (r:1 w:1) + /// Proof: Whitelist WhitelistedCall (max_values: None, max_size: Some(40), added: 2515, mode: MaxEncodedLen) + /// Storage: Preimage StatusFor (r:1 w:1) + /// Proof: Preimage StatusFor (max_values: None, max_size: Some(91), added: 2566, mode: MaxEncodedLen) fn whitelist_call() -> Weight { - // Minimum execution time: 26_261 nanoseconds. - Weight::from_ref_time(26_842_000) - .saturating_add(T::DbWeight::get().reads(2)) - .saturating_add(T::DbWeight::get().writes(2)) + // Proof Size summary in bytes: + // Measured: `217` + // Estimated: `5081` + // Minimum execution time: 18_618 nanoseconds. + Weight::from_parts(19_133_000, 5081) + .saturating_add(T::DbWeight::get().reads(2_u64)) + .saturating_add(T::DbWeight::get().writes(2_u64)) } - // Storage: Whitelist WhitelistedCall (r:1 w:1) - // Storage: Preimage StatusFor (r:1 w:1) + /// Storage: Whitelist WhitelistedCall (r:1 w:1) + /// Proof: Whitelist WhitelistedCall (max_values: None, max_size: Some(40), added: 2515, mode: MaxEncodedLen) + /// Storage: Preimage StatusFor (r:1 w:1) + /// Proof: Preimage StatusFor (max_values: None, max_size: Some(91), added: 2566, mode: MaxEncodedLen) fn remove_whitelisted_call() -> Weight { - // Minimum execution time: 25_092 nanoseconds. - Weight::from_ref_time(25_903_000) - .saturating_add(T::DbWeight::get().reads(2)) - .saturating_add(T::DbWeight::get().writes(2)) + // Proof Size summary in bytes: + // Measured: `346` + // Estimated: `5081` + // Minimum execution time: 16_685 nanoseconds. + Weight::from_parts(17_325_000, 5081) + .saturating_add(T::DbWeight::get().reads(2_u64)) + .saturating_add(T::DbWeight::get().writes(2_u64)) } - // Storage: Whitelist WhitelistedCall (r:1 w:1) - // Storage: Preimage PreimageFor (r:1 w:1) - // Storage: Preimage StatusFor (r:1 w:1) + /// Storage: Whitelist WhitelistedCall (r:1 w:1) + /// Proof: Whitelist WhitelistedCall (max_values: None, max_size: Some(40), added: 2515, mode: MaxEncodedLen) + /// Storage: Preimage PreimageFor (r:1 w:1) + /// Proof: Preimage PreimageFor (max_values: None, max_size: Some(4194344), added: 4196819, mode: Measured) + /// Storage: Preimage StatusFor (r:1 w:1) + /// Proof: Preimage StatusFor (max_values: None, max_size: Some(91), added: 2566, mode: MaxEncodedLen) /// The range of component `n` is `[1, 4194294]`. fn dispatch_whitelisted_call(n: u32, ) -> Weight { - // Minimum execution time: 36_685 nanoseconds. - Weight::from_ref_time(37_167_000) + // Proof Size summary in bytes: + // Measured: `454 + n * (1 ±0)` + // Estimated: `8007 + n * (1 ±0)` + // Minimum execution time: 27_539 nanoseconds. + Weight::from_parts(27_950_000, 8007) // Standard Error: 0 - .saturating_add(Weight::from_ref_time(1_144).saturating_mul(n.into())) - .saturating_add(T::DbWeight::get().reads(3)) - .saturating_add(T::DbWeight::get().writes(3)) + .saturating_add(Weight::from_ref_time(1_134).saturating_mul(n.into())) + .saturating_add(T::DbWeight::get().reads(3_u64)) + .saturating_add(T::DbWeight::get().writes(3_u64)) + .saturating_add(Weight::from_proof_size(1).saturating_mul(n.into())) } - // Storage: Whitelist WhitelistedCall (r:1 w:1) - // Storage: Preimage StatusFor (r:1 w:1) + /// Storage: Whitelist WhitelistedCall (r:1 w:1) + /// Proof: Whitelist WhitelistedCall (max_values: None, max_size: Some(40), added: 2515, mode: MaxEncodedLen) + /// Storage: Preimage StatusFor (r:1 w:1) + /// Proof: Preimage StatusFor (max_values: None, max_size: Some(91), added: 2566, mode: MaxEncodedLen) /// The range of component `n` is `[1, 10000]`. fn dispatch_whitelisted_call_with_preimage(n: u32, ) -> Weight { - // Minimum execution time: 29_187 nanoseconds. - Weight::from_ref_time(29_896_714) - // Standard Error: 6 - .saturating_add(Weight::from_ref_time(1_505).saturating_mul(n.into())) - .saturating_add(T::DbWeight::get().reads(2)) - .saturating_add(T::DbWeight::get().writes(2)) + // Proof Size summary in bytes: + // Measured: `346` + // Estimated: `5081` + // Minimum execution time: 20_581 nanoseconds. + Weight::from_parts(21_762_318, 5081) + // Standard Error: 4 + .saturating_add(Weight::from_ref_time(1_480).saturating_mul(n.into())) + .saturating_add(T::DbWeight::get().reads(2_u64)) + .saturating_add(T::DbWeight::get().writes(2_u64)) } } // For backwards compatibility and tests impl WeightInfo for () { - // Storage: Whitelist WhitelistedCall (r:1 w:1) - // Storage: Preimage StatusFor (r:1 w:1) + /// Storage: Whitelist WhitelistedCall (r:1 w:1) + /// Proof: Whitelist WhitelistedCall (max_values: None, max_size: Some(40), added: 2515, mode: MaxEncodedLen) + /// Storage: Preimage StatusFor (r:1 w:1) + /// Proof: Preimage StatusFor (max_values: None, max_size: Some(91), added: 2566, mode: MaxEncodedLen) fn whitelist_call() -> Weight { - // Minimum execution time: 26_261 nanoseconds. - Weight::from_ref_time(26_842_000) - .saturating_add(RocksDbWeight::get().reads(2)) - .saturating_add(RocksDbWeight::get().writes(2)) + // Proof Size summary in bytes: + // Measured: `217` + // Estimated: `5081` + // Minimum execution time: 18_618 nanoseconds. + Weight::from_parts(19_133_000, 5081) + .saturating_add(RocksDbWeight::get().reads(2_u64)) + .saturating_add(RocksDbWeight::get().writes(2_u64)) } - // Storage: Whitelist WhitelistedCall (r:1 w:1) - // Storage: Preimage StatusFor (r:1 w:1) + /// Storage: Whitelist WhitelistedCall (r:1 w:1) + /// Proof: Whitelist WhitelistedCall (max_values: None, max_size: Some(40), added: 2515, mode: MaxEncodedLen) + /// Storage: Preimage StatusFor (r:1 w:1) + /// Proof: Preimage StatusFor (max_values: None, max_size: Some(91), added: 2566, mode: MaxEncodedLen) fn remove_whitelisted_call() -> Weight { - // Minimum execution time: 25_092 nanoseconds. - Weight::from_ref_time(25_903_000) - .saturating_add(RocksDbWeight::get().reads(2)) - .saturating_add(RocksDbWeight::get().writes(2)) + // Proof Size summary in bytes: + // Measured: `346` + // Estimated: `5081` + // Minimum execution time: 16_685 nanoseconds. + Weight::from_parts(17_325_000, 5081) + .saturating_add(RocksDbWeight::get().reads(2_u64)) + .saturating_add(RocksDbWeight::get().writes(2_u64)) } - // Storage: Whitelist WhitelistedCall (r:1 w:1) - // Storage: Preimage PreimageFor (r:1 w:1) - // Storage: Preimage StatusFor (r:1 w:1) + /// Storage: Whitelist WhitelistedCall (r:1 w:1) + /// Proof: Whitelist WhitelistedCall (max_values: None, max_size: Some(40), added: 2515, mode: MaxEncodedLen) + /// Storage: Preimage PreimageFor (r:1 w:1) + /// Proof: Preimage PreimageFor (max_values: None, max_size: Some(4194344), added: 4196819, mode: Measured) + /// Storage: Preimage StatusFor (r:1 w:1) + /// Proof: Preimage StatusFor (max_values: None, max_size: Some(91), added: 2566, mode: MaxEncodedLen) /// The range of component `n` is `[1, 4194294]`. fn dispatch_whitelisted_call(n: u32, ) -> Weight { - // Minimum execution time: 36_685 nanoseconds. - Weight::from_ref_time(37_167_000) + // Proof Size summary in bytes: + // Measured: `454 + n * (1 ±0)` + // Estimated: `8007 + n * (1 ±0)` + // Minimum execution time: 27_539 nanoseconds. + Weight::from_parts(27_950_000, 8007) // Standard Error: 0 - .saturating_add(Weight::from_ref_time(1_144).saturating_mul(n.into())) - .saturating_add(RocksDbWeight::get().reads(3)) - .saturating_add(RocksDbWeight::get().writes(3)) + .saturating_add(Weight::from_ref_time(1_134).saturating_mul(n.into())) + .saturating_add(RocksDbWeight::get().reads(3_u64)) + .saturating_add(RocksDbWeight::get().writes(3_u64)) + .saturating_add(Weight::from_proof_size(1).saturating_mul(n.into())) } - // Storage: Whitelist WhitelistedCall (r:1 w:1) - // Storage: Preimage StatusFor (r:1 w:1) + /// Storage: Whitelist WhitelistedCall (r:1 w:1) + /// Proof: Whitelist WhitelistedCall (max_values: None, max_size: Some(40), added: 2515, mode: MaxEncodedLen) + /// Storage: Preimage StatusFor (r:1 w:1) + /// Proof: Preimage StatusFor (max_values: None, max_size: Some(91), added: 2566, mode: MaxEncodedLen) /// The range of component `n` is `[1, 10000]`. fn dispatch_whitelisted_call_with_preimage(n: u32, ) -> Weight { - // Minimum execution time: 29_187 nanoseconds. - Weight::from_ref_time(29_896_714) - // Standard Error: 6 - .saturating_add(Weight::from_ref_time(1_505).saturating_mul(n.into())) - .saturating_add(RocksDbWeight::get().reads(2)) - .saturating_add(RocksDbWeight::get().writes(2)) + // Proof Size summary in bytes: + // Measured: `346` + // Estimated: `5081` + // Minimum execution time: 20_581 nanoseconds. + Weight::from_parts(21_762_318, 5081) + // Standard Error: 4 + .saturating_add(Weight::from_ref_time(1_480).saturating_mul(n.into())) + .saturating_add(RocksDbWeight::get().reads(2_u64)) + .saturating_add(RocksDbWeight::get().writes(2_u64)) } } diff --git a/primitives/storage/src/lib.rs b/primitives/storage/src/lib.rs index 237787710..68d7b9063 100644 --- a/primitives/storage/src/lib.rs +++ b/primitives/storage/src/lib.rs @@ -200,6 +200,8 @@ pub mod well_known_keys { pub const HEAP_PAGES: &[u8] = b":heappages"; /// Current extrinsic index (u32) is stored under this key. + /// + /// Encodes to `0x3a65787472696e7369635f696e646578`. pub const EXTRINSIC_INDEX: &[u8] = b":extrinsic_index"; /// Prefix of child storage keys. diff --git a/scripts/run_all_benchmarks.sh b/scripts/run_all_benchmarks.sh index dd5d2e182..f122be577 100755 --- a/scripts/run_all_benchmarks.sh +++ b/scripts/run_all_benchmarks.sh @@ -74,6 +74,8 @@ EXCLUDED_PALLETS=( "pallet_grandpa" "pallet_mmr" "pallet_offences" + # Only used for testing, does not need real weights. + "frame_benchmarking_pallet_pov" ) # Load all pallet names in an array. diff --git a/utils/frame/benchmarking-cli/src/pallet/command.rs b/utils/frame/benchmarking-cli/src/pallet/command.rs index 242f0e685..ec1be2761 100644 --- a/utils/frame/benchmarking-cli/src/pallet/command.rs +++ b/utils/frame/benchmarking-cli/src/pallet/command.rs @@ -38,7 +38,7 @@ use sp_externalities::Extensions; use sp_keystore::{testing::KeyStore, KeystoreExt, SyncCryptoStorePtr}; use sp_runtime::traits::{Block as BlockT, Header as HeaderT}; use sp_state_machine::StateMachine; -use std::{collections::HashMap, fmt::Debug, fs, sync::Arc, time}; +use std::{collections::HashMap, fmt::Debug, fs, str::FromStr, sync::Arc, time}; /// Logging target const LOG_TARGET: &'static str = "frame::benchmark::pallet"; @@ -54,6 +54,34 @@ pub(crate) struct ComponentRange { max: u32, } +/// How the PoV size of a storage item should be estimated. +#[derive(clap::ValueEnum, Debug, Eq, PartialEq, Clone, Copy)] +pub enum PovEstimationMode { + /// Use the maximal encoded length as provided by [`codec::MaxEncodedLen`]. + MaxEncodedLen, + /// Measure the accessed value size in the pallet benchmarking and add some trie overhead. + Measured, + /// Do not estimate the PoV size for this storage item or benchmark. + Ignored, +} + +impl FromStr for PovEstimationMode { + type Err = &'static str; + + fn from_str(s: &str) -> std::result::Result { + match s { + "MaxEncodedLen" => Ok(Self::MaxEncodedLen), + "Measured" => Ok(Self::Measured), + "Ignored" => Ok(Self::Ignored), + _ => unreachable!("The benchmark! macro should have prevented this"), + } + } +} + +/// Maps (pallet, benchmark) -> ((pallet, storage) -> PovEstimationMode) +pub(crate) type PovModesMap = + HashMap<(Vec, Vec), HashMap<(String, String), PovEstimationMode>>; + // This takes multiple benchmark batches and combines all the results where the pallet, instance, // and benchmark are the same. fn combine_batches( @@ -165,11 +193,19 @@ impl PalletCmd { let state_with_tracking = BenchmarkingState::::new( genesis_storage.clone(), cache_size, - self.record_proof, + // Record proof size + true, + // Enable storage tracking true, )?; - let state_without_tracking = - BenchmarkingState::::new(genesis_storage, cache_size, self.record_proof, false)?; + let state_without_tracking = BenchmarkingState::::new( + genesis_storage, + cache_size, + // Do not record proof size + false, + // Do not enable storage tracking + false, + )?; let executor = NativeElseWasmExecutor::::new( execution_method_from_cli(self.wasm_method, self.wasmtime_instantiation_strategy), self.heap_pages, @@ -222,10 +258,27 @@ impl PalletCmd { item.pallet.clone(), benchmark.name.clone(), benchmark.components.clone(), + benchmark.pov_modes.clone(), )) } } }); + // Convert `Vec` to `String` for better readability. + let benchmarks_to_run: Vec<_> = benchmarks_to_run + .into_iter() + .map(|b| { + ( + b.0, + b.1, + b.2, + b.3.into_iter() + .map(|(p, s)| { + (String::from_utf8(p).unwrap(), String::from_utf8(s).unwrap()) + }) + .collect(), + ) + }) + .collect(); if benchmarks_to_run.is_empty() { return Err("No benchmarks found which match your input.".into()) @@ -243,8 +296,9 @@ impl PalletCmd { let mut timer = time::SystemTime::now(); // Maps (pallet, extrinsic) to its component ranges. let mut component_ranges = HashMap::<(Vec, Vec), Vec>::new(); + let pov_modes = Self::parse_pov_modes(&benchmarks_to_run)?; - for (pallet, extrinsic, components) in benchmarks_to_run { + for (pallet, extrinsic, components, _) in benchmarks_to_run.clone() { log::info!( target: LOG_TARGET, "Starting benchmark: {}::{}", @@ -429,7 +483,7 @@ impl PalletCmd { // Combine all of the benchmark results, so that benchmarks of the same pallet/function // are together. let batches = combine_batches(batches, batches_db); - self.output(&batches, &storage_info, &component_ranges) + self.output(&batches, &storage_info, &component_ranges, pov_modes) } fn output( @@ -437,21 +491,31 @@ impl PalletCmd { batches: &[BenchmarkBatchSplitResults], storage_info: &[StorageInfo], component_ranges: &HashMap<(Vec, Vec), Vec>, + pov_modes: PovModesMap, ) -> Result<()> { // Jsonify the result and write it to a file or stdout if desired. if !self.jsonify(&batches)? { // Print the summary only if `jsonify` did not write to stdout. - self.print_summary(&batches, &storage_info) + self.print_summary(&batches, &storage_info, pov_modes.clone()) } // Create the weights.rs file. if let Some(output_path) = &self.output { - writer::write_results(&batches, &storage_info, &component_ranges, output_path, self)?; + writer::write_results( + &batches, + &storage_info, + &component_ranges, + pov_modes, + self.default_pov_mode, + output_path, + self, + )?; } Ok(()) } + /// Re-analyze a batch historic benchmark timing data. Will not take the PoV into account. fn output_from_results(&self, batches: &[BenchmarkBatchSplitResults]) -> Result<()> { let mut component_ranges = HashMap::<(Vec, Vec), HashMap>::new(); @@ -484,7 +548,7 @@ impl PalletCmd { }) .collect(); - self.output(batches, &[], &component_ranges) + self.output(batches, &[], &component_ranges, Default::default()) } /// Jsonifies the passed batches and writes them to stdout or into a file. @@ -507,7 +571,12 @@ impl PalletCmd { } /// Prints the results as human-readable summary without raw timing data. - fn print_summary(&self, batches: &[BenchmarkBatchSplitResults], storage_info: &[StorageInfo]) { + fn print_summary( + &self, + batches: &[BenchmarkBatchSplitResults], + storage_info: &[StorageInfo], + pov_modes: PovModesMap, + ) { for batch in batches.iter() { // Print benchmark metadata println!( @@ -526,13 +595,32 @@ impl PalletCmd { } if !self.no_storage_info { - let mut comments: Vec = Default::default(); - writer::add_storage_comments(&mut comments, &batch.db_results, storage_info); + let mut storage_per_prefix = HashMap::, Vec>::new(); + let pov_mode = pov_modes + .get(&(batch.pallet.clone(), batch.benchmark.clone())) + .cloned() + .unwrap_or_default(); + + let comments = writer::process_storage_results( + &mut storage_per_prefix, + &batch.db_results, + storage_info, + &pov_mode, + self.default_pov_mode, + self.worst_case_map_values, + self.additional_trie_layers, + ); println!("Raw Storage Info\n========"); for comment in comments { println!("{}", comment); } println!(); + + println!("-- Proof Sizes --\n"); + for result in batch.db_results.iter() { + println!("{} bytes", result.proof_size); + } + println!(); } // Conduct analysis. @@ -553,6 +641,11 @@ impl PalletCmd { { println!("Writes = {:?}", analysis); } + if let Some(analysis) = + Analysis::median_slopes(&batch.db_results, BenchmarkSelector::ProofSize) + { + println!("Recorded proof Size = {:?}", analysis); + } println!(); } if !self.no_min_squares { @@ -572,10 +665,60 @@ impl PalletCmd { { println!("Writes = {:?}", analysis); } + if let Some(analysis) = + Analysis::min_squares_iqr(&batch.db_results, BenchmarkSelector::ProofSize) + { + println!("Recorded proof Size = {:?}", analysis); + } println!(); } } } + + /// Parses the PoV modes per benchmark that were specified by the `#[pov_mode]` attribute. + fn parse_pov_modes( + benchmarks: &Vec<( + Vec, + Vec, + Vec<(BenchmarkParameter, u32, u32)>, + Vec<(String, String)>, + )>, + ) -> Result { + use std::collections::hash_map::Entry; + let mut parsed = PovModesMap::new(); + + for (pallet, call, _components, pov_modes) in benchmarks { + for (pallet_storage, mode) in pov_modes { + let mode = PovEstimationMode::from_str(&mode)?; + let splits = pallet_storage.split("::").collect::>(); + if splits.is_empty() || splits.len() > 2 { + return Err(format!( + "Expected 'Pallet::Storage' as storage name but got: {}", + pallet_storage + ) + .into()) + } + let (pov_pallet, pov_storage) = (splits[0], splits.get(1).unwrap_or(&"ALL")); + + match parsed + .entry((pallet.clone(), call.clone())) + .or_default() + .entry((pov_pallet.to_string(), pov_storage.to_string())) + { + Entry::Occupied(_) => + return Err(format!( + "Cannot specify pov_mode tag twice for the same key: {}", + pallet_storage + ) + .into()), + Entry::Vacant(e) => { + e.insert(mode); + }, + } + } + } + Ok(parsed) + } } impl CliConfiguration for PalletCmd { @@ -592,9 +735,16 @@ impl CliConfiguration for PalletCmd { } /// List the benchmarks available in the runtime, in a CSV friendly format. -fn list_benchmark(benchmarks_to_run: Vec<(Vec, Vec, Vec<(BenchmarkParameter, u32, u32)>)>) { +fn list_benchmark( + benchmarks_to_run: Vec<( + Vec, + Vec, + Vec<(BenchmarkParameter, u32, u32)>, + Vec<(String, String)>, + )>, +) { println!("pallet, benchmark"); - for (pallet, extrinsic, _components) in benchmarks_to_run { + for (pallet, extrinsic, _, _) in benchmarks_to_run { println!("{}, {}", String::from_utf8_lossy(&pallet), String::from_utf8_lossy(&extrinsic)); } } diff --git a/utils/frame/benchmarking-cli/src/pallet/mod.rs b/utils/frame/benchmarking-cli/src/pallet/mod.rs index b10f531bc..1fcf0cc11 100644 --- a/utils/frame/benchmarking-cli/src/pallet/mod.rs +++ b/utils/frame/benchmarking-cli/src/pallet/mod.rs @@ -103,6 +103,14 @@ pub struct PalletCmd { #[arg(long)] pub output_analysis: Option, + /// Which analysis function to use when analyzing measured proof sizes. + #[arg(long, default_value("median-slopes"))] + pub output_pov_analysis: Option, + + /// The PoV estimation mode of a benchmark if no `pov_mode` attribute is present. + #[arg(long, default_value("max-encoded-len"), value_enum)] + pub default_pov_mode: command::PovEstimationMode, + /// Set the heap pages while running benchmarks. If not set, the default value from the client /// is used. #[arg(long)] @@ -117,10 +125,6 @@ pub struct PalletCmd { #[arg(long)] pub extra: bool, - /// Estimate PoV size. - #[arg(long)] - pub record_proof: bool, - #[allow(missing_docs)] #[clap(flatten)] pub shared_params: sc_cli::SharedParams, @@ -167,6 +171,25 @@ pub struct PalletCmd { #[arg(long)] pub no_storage_info: bool, + /// The assumed default maximum size of any `StorageMap`. + /// + /// When the maximum size of a map is not defined by the runtime developer, + /// this value is used as a worst case scenario. It will affect the calculated worst case + /// PoV size for accessing a value in a map, since the PoV will need to include the trie + /// nodes down to the underlying value. + #[clap(long = "map-size", default_value = "1000000")] + pub worst_case_map_values: u32, + + /// Adjust the PoV estimation by adding additional trie layers to it. + /// + /// This should be set to `log16(n)` where `n` is the number of top-level storage items in the + /// runtime, eg. `StorageMap`s and `StorageValue`s. A value of 2 to 3 is usually sufficient. + /// Each layer will result in an additional 495 bytes PoV per distinct top-level access. + /// Therefore multiple `StorageMap` accesses only suffer from this increase once. The exact + /// number of storage items depends on the runtime and the deployed pallets. + #[clap(long, default_value = "0")] + pub additional_trie_layers: u8, + /// A path to a `.json` file with existing benchmark results generated with `--json` or /// `--json-file`. When specified the benchmarks are not actually executed, and the data for /// the analysis is read from this file. diff --git a/utils/frame/benchmarking-cli/src/pallet/template.hbs b/utils/frame/benchmarking-cli/src/pallet/template.hbs index 1a69a3ae2..00dd1a353 100644 --- a/utils/frame/benchmarking-cli/src/pallet/template.hbs +++ b/utils/frame/benchmarking-cli/src/pallet/template.hbs @@ -2,7 +2,8 @@ //! Autogenerated weights for `{{pallet}}` //! //! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION {{version}} -//! DATE: {{date}}, STEPS: `{{cmd.steps}}`, REPEAT: {{cmd.repeat}}, LOW RANGE: `{{cmd.lowest_range_values}}`, HIGH RANGE: `{{cmd.highest_range_values}}` +//! DATE: {{date}}, STEPS: `{{cmd.steps}}`, REPEAT: `{{cmd.repeat}}`, LOW RANGE: `{{cmd.lowest_range_values}}`, HIGH RANGE: `{{cmd.highest_range_values}}` +//! WORST CASE MAP SIZE: `{{cmd.worst_case_map_values}}` //! HOSTNAME: `{{hostname}}`, CPU: `{{cpuname}}` //! EXECUTION: {{cmd.execution}}, WASM-EXECUTION: {{cmd.wasm_execution}}, CHAIN: {{cmd.chain}}, DB CACHE: {{cmd.db_cache}} @@ -23,7 +24,7 @@ pub struct WeightInfo(PhantomData); impl {{pallet}}::WeightInfo for WeightInfo { {{#each benchmarks as |benchmark|}} {{#each benchmark.comments as |comment|}} - // {{comment}} + /// {{comment}} {{/each}} {{#each benchmark.component_ranges as |range|}} /// The range of component `{{range.name}}` is `[{{range.min}}, {{range.max}}]`. @@ -33,8 +34,15 @@ impl {{pallet}}::WeightInfo for WeightInfo { {{~#each benchmark.components as |c| ~}} {{~#if (not c.is_used)}}_{{/if}}{{c.name}}: u32, {{/each~}} ) -> Weight { + // Proof Size summary in bytes: + // Measured: `{{benchmark.base_recorded_proof_size}}{{#each benchmark.component_recorded_proof_size as |cp|}} + {{cp.name}} * ({{cp.slope}} ±{{underscore cp.error}}){{/each}}` + // Estimated: `{{benchmark.base_calculated_proof_size}}{{#each benchmark.component_calculated_proof_size as |cp|}} + {{cp.name}} * ({{cp.slope}} ±{{underscore cp.error}}){{/each}}` // Minimum execution time: {{underscore benchmark.min_execution_time}} nanoseconds. + {{#if (ne benchmark.base_calculated_proof_size "0")}} + Weight::from_parts({{underscore benchmark.base_weight}}, {{benchmark.base_calculated_proof_size}}) + {{else}} Weight::from_ref_time({{underscore benchmark.base_weight}}) + {{/if}} {{#each benchmark.component_weight as |cw|}} // Standard Error: {{underscore cw.error}} .saturating_add(Weight::from_ref_time({{underscore cw.slope}}).saturating_mul({{cw.name}}.into())) @@ -51,6 +59,9 @@ impl {{pallet}}::WeightInfo for WeightInfo { {{#each benchmark.component_writes as |cw|}} .saturating_add(T::DbWeight::get().writes(({{cw.slope}}_u64).saturating_mul({{cw.name}}.into()))) {{/each}} + {{#each benchmark.component_calculated_proof_size as |cp|}} + .saturating_add(Weight::from_proof_size({{cp.slope}}).saturating_mul({{cp.name}}.into())) + {{/each}} } {{/each}} } diff --git a/utils/frame/benchmarking-cli/src/pallet/writer.rs b/utils/frame/benchmarking-cli/src/pallet/writer.rs index a52bbcd22..9359b2725 100644 --- a/utils/frame/benchmarking-cli/src/pallet/writer.rs +++ b/utils/frame/benchmarking-cli/src/pallet/writer.rs @@ -27,7 +27,11 @@ use inflector::Inflector; use itertools::Itertools; use serde::Serialize; -use crate::{pallet::command::ComponentRange, shared::UnderscoreHelper, PalletCmd}; +use crate::{ + pallet::command::{ComponentRange, PovEstimationMode, PovModesMap}, + shared::UnderscoreHelper, + PalletCmd, +}; use frame_benchmarking::{ Analysis, AnalysisChoice, BenchmarkBatchSplitResults, BenchmarkResult, BenchmarkSelector, }; @@ -54,7 +58,7 @@ struct TemplateData { } // This was the final data we have about each benchmark. -#[derive(Serialize, Default, Debug, Clone)] +#[derive(Serialize, Default, Debug, Clone, PartialEq)] struct BenchmarkData { name: String, components: Vec, @@ -64,9 +68,15 @@ struct BenchmarkData { base_reads: u128, #[serde(serialize_with = "string_serialize")] base_writes: u128, + #[serde(serialize_with = "string_serialize")] + base_calculated_proof_size: u128, + #[serde(serialize_with = "string_serialize")] + base_recorded_proof_size: u128, component_weight: Vec, component_reads: Vec, component_writes: Vec, + component_calculated_proof_size: Vec, + component_recorded_proof_size: Vec, component_ranges: Vec, comments: Vec, #[serde(serialize_with = "string_serialize")] @@ -85,6 +95,8 @@ struct CmdData { chain: String, db_cache: u32, analysis_choice: String, + worst_case_map_values: u32, + additional_trie_layers: u8, } // This encodes the component name and whether that component is used. @@ -122,7 +134,12 @@ fn map_results( batches: &[BenchmarkBatchSplitResults], storage_info: &[StorageInfo], component_ranges: &HashMap<(Vec, Vec), Vec>, + pov_modes: PovModesMap, + default_pov_mode: PovEstimationMode, analysis_choice: &AnalysisChoice, + pov_analysis_choice: &AnalysisChoice, + worst_case_map_values: u32, + additional_trie_layers: u8, ) -> Result>, std::io::Error> { // Skip if batches is empty. if batches.is_empty() { @@ -139,8 +156,17 @@ fn map_results( let pallet_string = String::from_utf8(batch.pallet.clone()).unwrap(); let instance_string = String::from_utf8(batch.instance.clone()).unwrap(); - let benchmark_data = - get_benchmark_data(batch, storage_info, &component_ranges, analysis_choice); + let benchmark_data = get_benchmark_data( + batch, + storage_info, + &component_ranges, + pov_modes.clone(), + default_pov_mode, + analysis_choice, + pov_analysis_choice, + worst_case_map_values, + additional_trie_layers, + ); let pallet_benchmarks = all_benchmarks.entry((pallet_string, instance_string)).or_default(); pallet_benchmarks.push(benchmark_data); } @@ -164,17 +190,24 @@ fn get_benchmark_data( storage_info: &[StorageInfo], // Per extrinsic component ranges. component_ranges: &HashMap<(Vec, Vec), Vec>, + pov_modes: PovModesMap, + default_pov_mode: PovEstimationMode, analysis_choice: &AnalysisChoice, + pov_analysis_choice: &AnalysisChoice, + worst_case_map_values: u32, + additional_trie_layers: u8, ) -> BenchmarkData { - // You can use this to put any additional comments with the benchmarking output. - let mut comments = Vec::::new(); - // Analyze benchmarks to get the linear regression. let analysis_function = match analysis_choice { AnalysisChoice::MinSquares => Analysis::min_squares_iqr, AnalysisChoice::MedianSlopes => Analysis::median_slopes, AnalysisChoice::Max => Analysis::max, }; + let pov_analysis_function = match pov_analysis_choice { + AnalysisChoice::MinSquares => Analysis::min_squares_iqr, + AnalysisChoice::MedianSlopes => Analysis::median_slopes, + AnalysisChoice::Max => Analysis::max, + }; let extrinsic_time = analysis_function(&batch.time_results, BenchmarkSelector::ExtrinsicTime) .expect("analysis function should return an extrinsic time for valid inputs"); @@ -182,6 +215,9 @@ fn get_benchmark_data( .expect("analysis function should return the number of reads for valid inputs"); let writes = analysis_function(&batch.db_results, BenchmarkSelector::Writes) .expect("analysis function should return the number of writes for valid inputs"); + let recorded_proof_size = + pov_analysis_function(&batch.db_results, BenchmarkSelector::ProofSize) + .expect("analysis function should return proof sizes for valid inputs"); // Analysis data may include components that are not used, this filters out anything whose value // is zero. @@ -189,6 +225,8 @@ fn get_benchmark_data( let mut used_extrinsic_time = Vec::new(); let mut used_reads = Vec::new(); let mut used_writes = Vec::new(); + let mut used_calculated_proof_size = Vec::::new(); + let mut used_recorded_proof_size = Vec::::new(); extrinsic_time .slopes @@ -229,6 +267,76 @@ fn get_benchmark_data( used_writes.push(ComponentSlope { name: name.clone(), slope, error }); } }); + recorded_proof_size + .slopes + .into_iter() + .zip(recorded_proof_size.names.iter()) + .zip(extract_errors(&recorded_proof_size.errors)) + .for_each(|((slope, name), error)| { + if !slope.is_zero() { + // These are only for comments, so don't touch the `used_components`. + used_recorded_proof_size.push(ComponentSlope { name: name.clone(), slope, error }); + } + }); + + // We add additional comments showing which storage items were touched. + // We find the worst case proof size, and use that as the final proof size result. + let mut storage_per_prefix = HashMap::, Vec>::new(); + let pov_mode = pov_modes + .get(&(batch.pallet.clone(), batch.benchmark.clone())) + .cloned() + .unwrap_or_default(); + let comments = process_storage_results( + &mut storage_per_prefix, + &batch.db_results, + storage_info, + &pov_mode, + default_pov_mode, + worst_case_map_values, + additional_trie_layers, + ); + + let proof_size_per_components = storage_per_prefix + .iter() + .map(|(prefix, results)| { + let proof_size = analysis_function(results, BenchmarkSelector::ProofSize) + .expect("analysis function should return proof sizes for valid inputs"); + let slope = proof_size + .slopes + .into_iter() + .zip(proof_size.names.iter()) + .zip(extract_errors(&proof_size.errors)) + .map(|((slope, name), error)| ComponentSlope { name: name.clone(), slope, error }) + .collect::>(); + (prefix.clone(), slope, proof_size.base) + }) + .collect::>(); + + let mut base_calculated_proof_size = 0; + // Sum up the proof sizes per component + for (_, slope, base) in proof_size_per_components.iter() { + base_calculated_proof_size += base; + for component in slope.iter() { + let mut found = false; + for used_component in used_calculated_proof_size.iter_mut() { + if used_component.name == component.name { + used_component.slope += component.slope; + found = true; + break + } + } + if !found && !component.slope.is_zero() { + if !used_components.contains(&&component.name) { + used_components.push(&component.name); + } + used_calculated_proof_size.push(ComponentSlope { + name: component.name.clone(), + slope: component.slope, + error: component.error, + }); + } + } + } // This puts a marker on any component which is entirely unused in the weight formula. let components = batch.time_results[0] @@ -241,8 +349,6 @@ fn get_benchmark_data( }) .collect::>(); - // We add additional comments showing which storage items were touched. - add_storage_comments(&mut comments, &batch.db_results, storage_info); let component_ranges = component_ranges .get(&(batch.pallet.clone(), batch.benchmark.clone())) .map(|c| c.clone()) @@ -254,9 +360,13 @@ fn get_benchmark_data( base_weight: extrinsic_time.base, base_reads: reads.base, base_writes: writes.base, + base_calculated_proof_size, + base_recorded_proof_size: recorded_proof_size.base, component_weight: used_extrinsic_time, component_reads: used_reads, component_writes: used_writes, + component_calculated_proof_size: used_calculated_proof_size, + component_recorded_proof_size: used_recorded_proof_size, component_ranges, comments, min_execution_time: extrinsic_time.minimum, @@ -268,6 +378,8 @@ pub(crate) fn write_results( batches: &[BenchmarkBatchSplitResults], storage_info: &[StorageInfo], component_ranges: &HashMap<(Vec, Vec), Vec>, + pov_modes: PovModesMap, + default_pov_mode: PovEstimationMode, path: &PathBuf, cmd: &PalletCmd, ) -> Result<(), std::io::Error> { @@ -295,6 +407,15 @@ pub(crate) fn write_results( // Which analysis function should be used when outputting benchmarks let analysis_choice: AnalysisChoice = cmd.output_analysis.clone().try_into().map_err(io_error)?; + let pov_analysis_choice: AnalysisChoice = + cmd.output_pov_analysis.clone().try_into().map_err(io_error)?; + + if cmd.additional_trie_layers > 4 { + println!( + "WARNING: `additional_trie_layers` is unexpectedly large. It assumes {} storage items.", + 16f64.powi(cmd.additional_trie_layers as i32) + ) + } // Capture individual args let cmd_data = CmdData { @@ -307,6 +428,8 @@ pub(crate) fn write_results( chain: format!("{:?}", cmd.shared_params.chain), db_cache: cmd.database_cache_size, analysis_choice: format!("{:?}", analysis_choice), + worst_case_map_values: cmd.worst_case_map_values, + additional_trie_layers: cmd.additional_trie_layers, }; // New Handlebars instance with helpers. @@ -317,7 +440,17 @@ pub(crate) fn write_results( handlebars.register_escape_fn(|s| -> String { s.to_string() }); // Organize results by pallet into a JSON map - let all_results = map_results(batches, storage_info, component_ranges, &analysis_choice)?; + let all_results = map_results( + batches, + storage_info, + component_ranges, + pov_modes, + default_pov_mode, + &analysis_choice, + &pov_analysis_choice, + cmd.worst_case_map_values, + cmd.additional_trie_layers, + )?; let mut created_files = Vec::new(); for ((pallet, instance), results) in all_results.iter() { @@ -365,14 +498,21 @@ pub(crate) fn write_results( Ok(()) } -// This function looks at the keys touched during the benchmark, and the storage info we collected -// from the pallets, and creates comments with information about the storage keys touched during -// each benchmark. -pub(crate) fn add_storage_comments( - comments: &mut Vec, +/// This function looks at the keys touched during the benchmark, and the storage info we collected +/// from the pallets, and creates comments with information about the storage keys touched during +/// each benchmark. +/// +/// It returns informational comments for human consumption. +pub(crate) fn process_storage_results( + storage_per_prefix: &mut HashMap, Vec>, results: &[BenchmarkResult], storage_info: &[StorageInfo], -) { + pov_modes: &HashMap<(String, String), PovEstimationMode>, + default_pov_mode: PovEstimationMode, + worst_case_map_values: u32, + additional_trie_layers: u8, +) -> Vec { + let mut comments = Vec::new(); let mut storage_info_map = storage_info .iter() .map(|info| (info.prefix.clone(), info)) @@ -399,48 +539,246 @@ pub(crate) fn add_storage_comments( storage_info_map.insert(benchmark_override.prefix.clone(), &benchmark_override); // This tracks the keys we already identified, so we only generate a single comment. - let mut identified = HashSet::>::new(); + let mut identified_prefix = HashSet::>::new(); + let mut identified_key = HashSet::>::new(); - for result in results { + // TODO Emit a warning for unused `pov_mode` attributes. + + // We have to iterate in reverse order to catch the largest values for read/write since the + // components start low and then increase and only the first value is used. + for result in results.iter().rev() { for (key, reads, writes, whitelisted) in &result.keys { // skip keys which are whitelisted if *whitelisted { continue } + let prefix_length = key.len().min(32); let prefix = key[0..prefix_length].to_vec(); - if identified.contains(&prefix) { - // skip adding comments for keys we already identified - continue - } else { - // track newly identified keys - identified.insert(prefix.clone()); + let is_key_identified = identified_key.contains(key); + let is_prefix_identified = identified_prefix.contains(&prefix); + + let mut prefix_result = result.clone(); + let key_info = storage_info_map.get(&prefix); + let max_size = key_info.and_then(|k| k.max_size); + + let override_pov_mode = match key_info { + Some(StorageInfo { pallet_name, storage_name, .. }) => { + let pallet_name = + String::from_utf8(pallet_name.clone()).expect("encoded from string"); + let storage_name = + String::from_utf8(storage_name.clone()).expect("encoded from string"); + + // Is there an override for the storage key? + pov_modes.get(&(pallet_name.clone(), storage_name)).or( + // .. or for the storage prefix? + pov_modes.get(&(pallet_name, "ALL".to_string())).or( + // .. or for the benchmark? + pov_modes.get(&("ALL".to_string(), "ALL".to_string())), + ), + ) + }, + None => None, + }; + let is_all_ignored = pov_modes.get(&("ALL".to_string(), "ALL".to_string())) == + Some(&PovEstimationMode::Ignored); + if is_all_ignored && override_pov_mode != Some(&PovEstimationMode::Ignored) { + panic!("The syntax currently does not allow to exclude single keys from a top-level `Ignored` pov-mode."); } - match storage_info_map.get(&prefix) { - Some(key_info) => { - let comment = format!( - "Storage: {} {} (r:{} w:{})", - String::from_utf8(key_info.pallet_name.clone()) - .expect("encoded from string"), - String::from_utf8(key_info.storage_name.clone()) - .expect("encoded from string"), - reads, - writes, - ); - comments.push(comment) + + let pov_overhead = single_read_pov_overhead( + key_info.and_then(|i| i.max_values), + worst_case_map_values, + ); + + let used_pov_mode = match (override_pov_mode, max_size, default_pov_mode) { + // All is ignored by default and no override: + (None, _, PovEstimationMode::Ignored) => { + prefix_result.proof_size = 0; + PovEstimationMode::Ignored + }, + // Some is ignored by override, maybe all: + (Some(PovEstimationMode::Ignored), _, _) => { + // If this is applied to All keys, then we also remove the base weight and just set all to zero. + if is_all_ignored { + prefix_result.proof_size = 0; + } else { + // Otherwise we just don't *increase* `proof_size` for this key. + } + PovEstimationMode::Ignored + }, + (Some(PovEstimationMode::Measured), _, _)| + (None, _, PovEstimationMode::Measured) | + // Use best effort in this case since failing would be really annoying. + (None, None, PovEstimationMode::MaxEncodedLen) => { + // We add the overhead for a single read each time. In a more advanced version + // we could take node re-using into account and over-estimate a bit less. + prefix_result.proof_size += pov_overhead * *reads; + PovEstimationMode::Measured + }, + (Some(PovEstimationMode::MaxEncodedLen), Some(max_size), _) | + (None, Some(max_size), PovEstimationMode::MaxEncodedLen) => { + prefix_result.proof_size = (pov_overhead + max_size) * *reads; + PovEstimationMode::MaxEncodedLen + }, + (Some(PovEstimationMode::MaxEncodedLen), None, _) => { + panic!("Key does not have MEL bound but MEL PoV estimation mode was specified {:?}", &key); + }, + }; + // Add the additional trie layer overhead for every new prefix. + if *reads > 0 { + prefix_result.proof_size += 15 * 33 * additional_trie_layers as u32; + } + storage_per_prefix.entry(prefix.clone()).or_default().push(prefix_result); + + match (is_key_identified, is_prefix_identified) { + // We already did everything, move on... + (true, true) => continue, + (false, true) => { + // track newly identified key + identified_key.insert(key.clone()); }, - None => { - let comment = format!( - "Storage: unknown [0x{}] (r:{} w:{})", - HexDisplay::from(key), - reads, - writes, - ); - comments.push(comment) + (false, false) => { + // track newly identified key and prefix + identified_key.insert(key.clone()); + identified_prefix.insert(prefix.clone()); }, + // Not possible. If the key is known, the prefix is too. + (true, false) => unreachable!(), + } + + // For any new prefix, we should write some comment about the number of reads and + // writes. + if !is_prefix_identified { + match key_info { + Some(key_info) => { + let comment = format!( + "Storage: {} {} (r:{} w:{})", + String::from_utf8(key_info.pallet_name.clone()) + .expect("encoded from string"), + String::from_utf8(key_info.storage_name.clone()) + .expect("encoded from string"), + reads, + writes, + ); + comments.push(comment) + }, + None => { + let comment = format!( + "Storage: unknown `0x{}` (r:{} w:{})", + HexDisplay::from(key), + reads, + writes, + ); + comments.push(comment) + }, + } + } + + // For any new key, we should add the PoV impact. + if !is_key_identified { + match key_info { + Some(key_info) => { + match worst_case_pov( + key_info.max_values, + key_info.max_size, + !is_prefix_identified, + worst_case_map_values, + ) { + Some(new_pov) => { + let comment = format!( + "Proof: {} {} (max_values: {:?}, max_size: {:?}, added: {}, mode: {:?})", + String::from_utf8(key_info.pallet_name.clone()) + .expect("encoded from string"), + String::from_utf8(key_info.storage_name.clone()) + .expect("encoded from string"), + key_info.max_values, + key_info.max_size, + new_pov, + used_pov_mode, + ); + comments.push(comment) + }, + None => { + let pallet = String::from_utf8(key_info.pallet_name.clone()) + .expect("encoded from string"); + let item = String::from_utf8(key_info.storage_name.clone()) + .expect("encoded from string"); + let comment = format!( + "Proof Skipped: {} {} (max_values: {:?}, max_size: {:?}, mode: {:?})", + pallet, item, key_info.max_values, key_info.max_size, + used_pov_mode, + ); + comments.push(comment); + }, + } + }, + None => { + let comment = format!( + "Proof Skipped: unknown `0x{}` (r:{} w:{})", + HexDisplay::from(key), + reads, + writes, + ); + comments.push(comment) + }, + } } } } + + comments +} + +/// The PoV overhead when reading a key the first time out of a map with `max_values` entries. +fn single_read_pov_overhead(max_values: Option, worst_case_map_values: u32) -> u32 { + let max_values = max_values.unwrap_or(worst_case_map_values); + let depth: u32 = easy_log_16(max_values); + // Normally we have 16 entries of 32 byte hashes per tree layer. In the new trie + // layout the hashes are prefixed by their compact length, hence 33 instead. The proof + // compaction can compress one node per layer since we send the value itself, + // therefore we end up with a size of `15 * 33` per layer. + depth * 15 * 33 +} + +/// Given the max values and max size of some storage item, calculate the worst +/// case PoV. +/// +/// # Arguments +/// * `max_values`: The maximum number of values in the storage item. `None` for unbounded items. +/// * `max_size`: The maximum size of the value in the storage. `None` for unbounded items. +fn worst_case_pov( + max_values: Option, + max_size: Option, + is_new_prefix: bool, + worst_case_map_values: u32, +) -> Option { + if let Some(max_size) = max_size { + let trie_size: u32 = if is_new_prefix { + single_read_pov_overhead(max_values, worst_case_map_values) + } else { + 0 + }; + + Some(trie_size + max_size) + } else { + None + } +} + +/// A simple match statement which outputs the log 16 of some value. +fn easy_log_16(i: u32) -> u32 { + match i { + i if i == 0 => 0, + i if i <= 16 => 1, + i if i <= 256 => 2, + i if i <= 4_096 => 3, + i if i <= 65_536 => 4, + i if i <= 1_048_576 => 5, + i if i <= 16_777_216 => 6, + i if i <= 268_435_456 => 7, + _ => 8, + } } // A helper to join a string of vectors. @@ -497,15 +835,16 @@ mod test { let mut results = Vec::new(); for i in 0..5 { results.push(BenchmarkResult { - components: vec![(param, i), (BenchmarkParameter::z, 0)], + components: vec![(param, i)], extrinsic_time: (base + slope * i).into(), storage_root_time: (base + slope * i).into(), reads: (base + slope * i).into(), repeat_reads: 0, writes: (base + slope * i).into(), repeat_writes: 0, - proof_size: 0, - keys: vec![], + proof_size: (i + 1) * 1024, + // All R/W come from this key: + keys: vec![(b"bounded".to_vec(), (base + slope * i), (base + slope * i), false)], }) } @@ -518,13 +857,31 @@ mod test { } } + fn test_storage_info() -> Vec { + vec![StorageInfo { + pallet_name: b"bounded".to_vec(), + storage_name: b"bounded".to_vec(), + prefix: b"bounded".to_vec(), + max_values: Some(1 << 20), + max_size: Some(32), + }] + } + + fn test_pov_mode() -> PovModesMap { + let mut map = PovModesMap::new(); + map.entry((b"scheduler".to_vec(), b"first_benchmark".to_vec())) + .or_default() + .insert(("scheduler".into(), "mel".into()), PovEstimationMode::MaxEncodedLen); + map.entry((b"scheduler".to_vec(), b"first_benchmark".to_vec())) + .or_default() + .insert(("scheduler".into(), "measured".into()), PovEstimationMode::Measured); + map + } + fn check_data(benchmark: &BenchmarkData, component: &str, base: u128, slope: u128) { assert_eq!( benchmark.components, - vec![ - Component { name: component.to_string(), is_used: true }, - Component { name: "z".to_string(), is_used: false }, - ], + vec![Component { name: component.to_string(), is_used: true },], ); // Weights multiplied by 1,000 assert_eq!(benchmark.base_weight, base * 1_000); @@ -543,6 +900,294 @@ mod test { benchmark.component_writes, vec![ComponentSlope { name: component.to_string(), slope, error: 0 }] ); + // Measure PoV is correct + assert_eq!(benchmark.base_recorded_proof_size, 1024); + assert_eq!( + benchmark.component_recorded_proof_size, + vec![ComponentSlope { name: component.to_string(), slope: 1024, error: 0 }] + ); + } + + /// We measure a linear proof size but select `pov_mode = MEL` with a present MEL bound for the + /// type. This should result in the measured PoV being ignored and the MEL used instead. + #[test] + fn pov_mode_mel_constant_works() { + let mut results = Vec::new(); + for i in 0..5 { + results.push(BenchmarkResult { + components: vec![(BenchmarkParameter::s, i)], + extrinsic_time: 0, + storage_root_time: 0, + reads: 1, + repeat_reads: 777, + writes: 888, + repeat_writes: 999, + proof_size: i * 1024, + keys: vec![(b"mel".to_vec(), 1, 1, false)], + }) + } + + let data = BenchmarkBatchSplitResults { + pallet: b"scheduler".to_vec(), + instance: b"instance".to_vec(), + benchmark: b"first_benchmark".to_vec(), + time_results: results.clone(), + db_results: results, + }; + + let storage_info = vec![StorageInfo { + pallet_name: b"scheduler".to_vec(), + storage_name: b"mel".to_vec(), + prefix: b"mel".to_vec(), + max_values: None, + max_size: Some(1 << 22), // MEL of 4 MiB + }]; + + let mapped_results = map_results( + &[data], + &storage_info, + &Default::default(), + test_pov_mode(), + PovEstimationMode::MaxEncodedLen, + &AnalysisChoice::default(), + &AnalysisChoice::MedianSlopes, + 1_000_000, + 0, + ) + .unwrap(); + let result = + mapped_results.get(&("scheduler".to_string(), "instance".to_string())).unwrap()[0] + .clone(); + + let base = result.base_calculated_proof_size; + assert!(result.component_calculated_proof_size.is_empty(), "There is no slope"); + // It's a map with 5 layers overhead: + assert_eq!(base, (1 << 22) + 15 * 33 * 5); + } + + /// Record a small linear proof size but since MEL is selected and available it should be used + /// instead. + #[test] + fn pov_mode_mel_linear_works() { + let mut results = Vec::new(); + for i in 0..5 { + results.push(BenchmarkResult { + components: vec![(BenchmarkParameter::s, i)], + extrinsic_time: 0, + storage_root_time: 0, + reads: 123, + repeat_reads: 777, + writes: 888, + repeat_writes: 999, + proof_size: i * 1024, + keys: vec![("mel".as_bytes().to_vec(), i, 1, false)], + }) + } + + let data = BenchmarkBatchSplitResults { + pallet: b"scheduler".to_vec(), + instance: b"instance".to_vec(), + benchmark: b"first_benchmark".to_vec(), + time_results: results.clone(), + db_results: results, + }; + + let storage_info = vec![StorageInfo { + pallet_name: b"scheduler".to_vec(), + storage_name: b"mel".to_vec(), + prefix: b"mel".to_vec(), + max_values: None, + max_size: Some(1 << 22), // MEL of 4 MiB + }]; + + let mapped_results = map_results( + &[data], + &storage_info, + &Default::default(), + test_pov_mode(), + PovEstimationMode::MaxEncodedLen, + &AnalysisChoice::default(), + &AnalysisChoice::MedianSlopes, + 1_000_000, + 0, + ) + .unwrap(); + let result = + mapped_results.get(&("scheduler".to_string(), "instance".to_string())).unwrap()[0] + .clone(); + + let base = result.base_calculated_proof_size; + assert_eq!(result.component_calculated_proof_size.len(), 1, "There is a slope"); + let slope = result.component_calculated_proof_size[0].clone().slope; + assert_eq!(base, 0); + // It's a map with 5 layers overhead: + assert_eq!(slope, (1 << 22) + 15 * 33 * 5); + } + + #[test] + fn pov_mode_measured_const_works() { + let mut results = Vec::new(); + for i in 0..5 { + results.push(BenchmarkResult { + components: vec![(BenchmarkParameter::s, i)], + extrinsic_time: 0, + storage_root_time: 0, + reads: 123, + repeat_reads: 777, + writes: 888, + repeat_writes: 999, + proof_size: 1024, + keys: vec![("measured".as_bytes().to_vec(), 1, 1, false)], + }) + } + + let data = BenchmarkBatchSplitResults { + pallet: b"scheduler".to_vec(), + instance: b"instance".to_vec(), + benchmark: b"first_benchmark".to_vec(), + time_results: results.clone(), + db_results: results, + }; + + let storage_info = vec![StorageInfo { + pallet_name: b"scheduler".to_vec(), + storage_name: b"measured".to_vec(), + prefix: b"measured".to_vec(), + max_values: None, + max_size: Some(1 << 22), // MEL of 4 MiB + }]; + + let mapped_results = map_results( + &[data], + &storage_info, + &Default::default(), + test_pov_mode(), + PovEstimationMode::MaxEncodedLen, + &AnalysisChoice::default(), + &AnalysisChoice::MedianSlopes, + 1_000_000, + 0, + ) + .unwrap(); + let result = + mapped_results.get(&("scheduler".to_string(), "instance".to_string())).unwrap()[0] + .clone(); + + let base = result.base_calculated_proof_size; + assert!(result.component_calculated_proof_size.is_empty(), "There is no slope"); + // 5 Trie layers overhead because of the 1M max elements in that map: + assert_eq!(base, 1024 + 15 * 33 * 5); + } + + #[test] + fn pov_mode_measured_linear_works() { + let mut results = Vec::new(); + for i in 0..5 { + results.push(BenchmarkResult { + components: vec![(BenchmarkParameter::s, i)], + extrinsic_time: 0, + storage_root_time: 0, + reads: 123, + repeat_reads: 777, + writes: 888, + repeat_writes: 999, + proof_size: i * 1024, + keys: vec![("measured".as_bytes().to_vec(), i, 1, false)], + }) + } + + let data = BenchmarkBatchSplitResults { + pallet: b"scheduler".to_vec(), + instance: b"instance".to_vec(), + benchmark: b"first_benchmark".to_vec(), + time_results: results.clone(), + db_results: results, + }; + + let storage_info = vec![StorageInfo { + pallet_name: b"scheduler".to_vec(), + storage_name: b"measured".to_vec(), + prefix: b"measured".to_vec(), + max_values: None, + max_size: Some(1 << 22), // MEL of 4 MiB + }]; + + let mapped_results = map_results( + &[data], + &storage_info, + &Default::default(), + test_pov_mode(), + PovEstimationMode::MaxEncodedLen, + &AnalysisChoice::default(), + &AnalysisChoice::MedianSlopes, + 1_000_000, + 0, + ) + .unwrap(); + let result = + mapped_results.get(&("scheduler".to_string(), "instance".to_string())).unwrap()[0] + .clone(); + + let base = result.base_calculated_proof_size; + assert_eq!(result.component_calculated_proof_size.len(), 1, "There is a slope"); + let slope = result.component_calculated_proof_size[0].clone().slope; + assert_eq!(base, 0); + // It's a map with 5 layers overhead: + assert_eq!(slope, 1024 + 15 * 33 * 5); + } + + #[test] + fn pov_mode_ignored_linear_works() { + let mut results = Vec::new(); + for i in 0..5 { + results.push(BenchmarkResult { + components: vec![(BenchmarkParameter::s, i)], + extrinsic_time: 0, + storage_root_time: 0, + reads: 123, + repeat_reads: 777, + writes: 888, + repeat_writes: 999, + proof_size: i * 1024, + keys: vec![("ignored".as_bytes().to_vec(), i, 1, false)], + }) + } + + let data = BenchmarkBatchSplitResults { + pallet: b"scheduler".to_vec(), + instance: b"instance".to_vec(), + benchmark: b"first_benchmark".to_vec(), + time_results: results.clone(), + db_results: results, + }; + + let storage_info = vec![StorageInfo { + pallet_name: b"scheduler".to_vec(), + storage_name: b"ignored".to_vec(), + prefix: b"ignored".to_vec(), + max_values: None, + max_size: Some(1 << 22), // MEL of 4 MiB + }]; + + let mapped_results = map_results( + &[data], + &storage_info, + &Default::default(), + test_pov_mode(), + PovEstimationMode::Ignored, + &AnalysisChoice::default(), + &AnalysisChoice::MedianSlopes, + 1_000_000, + 0, + ) + .unwrap(); + let result = + mapped_results.get(&("scheduler".to_string(), "instance".to_string())).unwrap()[0] + .clone(); + + let base = result.base_calculated_proof_size; + assert!(result.component_calculated_proof_size.is_empty(), "There is no slope"); + assert_eq!(base, 0); } #[test] @@ -552,10 +1197,16 @@ mod test { test_data(b"first", b"first", BenchmarkParameter::a, 10, 3), test_data(b"first", b"second", BenchmarkParameter::b, 9, 2), test_data(b"second", b"first", BenchmarkParameter::c, 3, 4), + test_data(b"bounded", b"bounded", BenchmarkParameter::d, 4, 6), ], - &[], + &test_storage_info(), &Default::default(), + Default::default(), + PovEstimationMode::MaxEncodedLen, &AnalysisChoice::default(), + &AnalysisChoice::MedianSlopes, + 1_000_000, + 0, ) .unwrap(); @@ -576,5 +1227,118 @@ mod test { .unwrap()[0]; assert_eq!(second_pallet_benchmark.name, "first_benchmark"); check_data(second_pallet_benchmark, "c", 3, 4); + + let bounded_pallet_benchmark = &mapped_results + .get(&("bounded_pallet".to_string(), "instance".to_string())) + .unwrap()[0]; + assert_eq!(bounded_pallet_benchmark.name, "bounded_benchmark"); + check_data(bounded_pallet_benchmark, "d", 4, 6); + // (5 * 15 * 33 + 32) * 4 = 10028 + assert_eq!(bounded_pallet_benchmark.base_calculated_proof_size, 10028); + // (5 * 15 * 33 + 32) * 6 = 15042 + assert_eq!( + bounded_pallet_benchmark.component_calculated_proof_size, + vec![ComponentSlope { name: "d".into(), slope: 15042, error: 0 }] + ); + } + + #[test] + fn additional_trie_layers_work() { + let mapped_results = map_results( + &[test_data(b"first", b"first", BenchmarkParameter::a, 10, 3)], + &test_storage_info(), + &Default::default(), + Default::default(), + PovEstimationMode::MaxEncodedLen, + &AnalysisChoice::default(), + &AnalysisChoice::MedianSlopes, + 1_000_000, + 2, + ) + .unwrap(); + let with_layer = &mapped_results + .get(&("first_pallet".to_string(), "instance".to_string())) + .unwrap()[0]; + let mapped_results = map_results( + &[test_data(b"first", b"first", BenchmarkParameter::a, 10, 3)], + &test_storage_info(), + &Default::default(), + Default::default(), + PovEstimationMode::MaxEncodedLen, + &AnalysisChoice::default(), + &AnalysisChoice::MedianSlopes, + 1_000_000, + 0, + ) + .unwrap(); + let without_layer = &mapped_results + .get(&("first_pallet".to_string(), "instance".to_string())) + .unwrap()[0]; + + assert_eq!( + without_layer.base_calculated_proof_size + 2 * 15 * 33, + with_layer.base_calculated_proof_size + ); + // The additional trie layers ONLY affect the base weight, not the components. + assert_eq!( + without_layer.component_calculated_proof_size, + with_layer.component_calculated_proof_size + ); + } + + #[test] + fn template_works() { + let all_results = map_results( + &[ + test_data(b"first", b"first", BenchmarkParameter::a, 10, 3), + test_data(b"first", b"second", BenchmarkParameter::b, 9, 2), + test_data(b"second", b"first", BenchmarkParameter::c, 3, 4), + ], + &test_storage_info(), + &Default::default(), + Default::default(), + PovEstimationMode::MaxEncodedLen, + &AnalysisChoice::default(), + &AnalysisChoice::MedianSlopes, + 1_000_000, + 0, + ) + .unwrap(); + + // New Handlebars instance with helpers. + let mut handlebars = handlebars::Handlebars::new(); + handlebars.register_helper("underscore", Box::new(UnderscoreHelper)); + handlebars.register_helper("join", Box::new(JoinHelper)); + // Don't HTML escape any characters. + handlebars.register_escape_fn(|s| -> String { s.to_string() }); + + for ((_pallet, _instance), results) in all_results.iter() { + let hbs_data = TemplateData { benchmarks: results.clone(), ..Default::default() }; + + let output = handlebars.render_template(&TEMPLATE, &hbs_data); + assert!(output.is_ok()); + println!("{:?}", output); + } + } + + #[test] + fn easy_log_16_works() { + assert_eq!(easy_log_16(0), 0); + assert_eq!(easy_log_16(1), 1); + assert_eq!(easy_log_16(16), 1); + assert_eq!(easy_log_16(17), 2); + assert_eq!(easy_log_16(16u32.pow(2)), 2); + assert_eq!(easy_log_16(16u32.pow(2) + 1), 3); + assert_eq!(easy_log_16(16u32.pow(3)), 3); + assert_eq!(easy_log_16(16u32.pow(3) + 1), 4); + assert_eq!(easy_log_16(16u32.pow(4)), 4); + assert_eq!(easy_log_16(16u32.pow(4) + 1), 5); + assert_eq!(easy_log_16(16u32.pow(5)), 5); + assert_eq!(easy_log_16(16u32.pow(5) + 1), 6); + assert_eq!(easy_log_16(16u32.pow(6)), 6); + assert_eq!(easy_log_16(16u32.pow(6) + 1), 7); + assert_eq!(easy_log_16(16u32.pow(7)), 7); + assert_eq!(easy_log_16(16u32.pow(7) + 1), 8); + assert_eq!(easy_log_16(u32::MAX), 8); } } From 248fdf0d4b5e3758cfdadb283b5eca5f0731e466 Mon Sep 17 00:00:00 2001 From: Dan Henton Date: Sat, 28 Jan 2023 00:31:17 +1300 Subject: [PATCH 063/558] Use non-binary pronouns in comments. (#13209) * use non binary pronouns in comments * cargo fmt * fix the use of "it" with "they" when dealing about an opperations identity --- bin/utils/subkey/README.md | 2 +- client/beefy/src/tests.rs | 7 ++++--- client/consensus/babe/src/lib.rs | 2 +- docs/README.adoc | 4 ++-- frame/contracts/src/exec.rs | 4 ++-- frame/elections-phragmen/src/benchmarking.rs | 2 +- frame/nfts/src/features/transfer.rs | 2 +- frame/nomination-pools/src/migration.rs | 2 +- frame/nomination-pools/test-staking/src/lib.rs | 2 +- frame/root-offences/src/tests.rs | 4 ++-- frame/staking/src/tests.rs | 2 +- frame/uniques/src/functions.rs | 4 ++-- 12 files changed, 19 insertions(+), 18 deletions(-) diff --git a/bin/utils/subkey/README.md b/bin/utils/subkey/README.md index e762a42c3..768f1335c 100644 --- a/bin/utils/subkey/README.md +++ b/bin/utils/subkey/README.md @@ -255,7 +255,7 @@ Secret Key URI `0x8c9a73097f235b84021a446bc2826a00c690ea0be3e0d81a84931cb4146d66 SS58 Address: 1bobYxBPjZWRPbVo35aSwci1u5Zmq8P6J2jpa4kkudBZMqE ``` -`Bob` now got a nice address starting with his name: 1**bob**YxBPjZWRPbVo35aSwci1u5Zmq8P6J2jpa4kkudBZMqE. +`Bob` now got a nice address starting with their name: 1**bob**YxBPjZWRPbVo35aSwci1u5Zmq8P6J2jpa4kkudBZMqE. **Note**: While `Bob`, having a short name (3 chars), got a result rather quickly, it will take much longer for `Alice` who has a much longer name, thus the chances to generate a random address that contains the chain `alice` will be much smaller. diff --git a/client/beefy/src/tests.rs b/client/beefy/src/tests.rs index 66d3a210d..2642035ba 100644 --- a/client/beefy/src/tests.rs +++ b/client/beefy/src/tests.rs @@ -151,7 +151,7 @@ impl BeefyTestNet { ) -> Vec { let mut all_hashes = Vec::with_capacity(count + 1); - // make sure genesis is the only block in network, so we can insert genesis at he beginning + // make sure genesis is the only block in network, so we can insert genesis at the beginning // of hashes, otherwise indexing would be broken assert!(self.peer(0).client().as_backend().blockchain().hash(1).unwrap().is_none()); @@ -926,7 +926,8 @@ async fn on_demand_beefy_justification_sync() { ) .await; - // Spawn Dave, he's now way behind voting and can only catch up through on-demand justif sync. + // Spawn Dave, they are now way behind voting and can only catch up through on-demand justif + // sync. tokio::spawn(dave_task); // give Dave a chance to spawn and init. run_for(Duration::from_millis(400), &net).await; @@ -937,7 +938,7 @@ async fn on_demand_beefy_justification_sync() { client.finalize_block(hashes[1], None).unwrap(); // Give Dave task some cpu cycles to process the finality notification, run_for(Duration::from_millis(100), &net).await; - // freshly spun up Dave now needs to listen for gossip to figure out the state of his peers. + // freshly spun up Dave now needs to listen for gossip to figure out the state of their peers. // Have the other peers do some gossip so Dave finds out about their progress. finalize_block_and_wait_for_beefy(&net, fast_peers, &[hashes[25]], &[25]).await; diff --git a/client/consensus/babe/src/lib.rs b/client/consensus/babe/src/lib.rs index 5ba523665..0eac4aca0 100644 --- a/client/consensus/babe/src/lib.rs +++ b/client/consensus/babe/src/lib.rs @@ -1494,7 +1494,7 @@ where // this way we can revert it if there's any error let mut old_epoch_changes = None; - // Use an extra scope to make the compiler happy, because otherwise he complains about the + // Use an extra scope to make the compiler happy, because otherwise it complains about the // mutex, even if we dropped it... let mut epoch_changes = { let mut epoch_changes = self.epoch_changes.shared_data_locked(); diff --git a/docs/README.adoc b/docs/README.adoc index 5d7b0b52c..8e43757d1 100644 --- a/docs/README.adoc +++ b/docs/README.adoc @@ -297,7 +297,7 @@ Detailed logs may be shown by running the node with the following environment va If you want to see the multi-node consensus algorithm in action locally, then you can create a local testnet with two validator nodes for Alice and Bob, who are the initial authorities of the genesis chain specification that have been endowed with a testnet DOTs. We'll give each node a name and expose them so they are listed on link:https://telemetry.polkadot.io/#/Local%20Testnet[Telemetry]. You'll need two terminal windows open. -We'll start Alice's Substrate node first on default TCP port 30333 with her chain database stored locally at `/tmp/alice`. The Bootnode ID of her node is `QmRpheLN4JWdAnY7HGJfWFNbfkQCb6tFf4vvA6hgjMZKrR`, which is generated from the `--node-key` value that we specify below: +We'll start Alice's Substrate node first on default TCP port 30333 with their chain database stored locally at `/tmp/alice`. The Bootnode ID of Alice's node is `QmRpheLN4JWdAnY7HGJfWFNbfkQCb6tFf4vvA6hgjMZKrR`, which is generated from the `--node-key` value that we specify below: [source, shell] cargo run --release \-- \ @@ -308,7 +308,7 @@ cargo run --release \-- \ --telemetry-url 'ws://telemetry.polkadot.io:1024 0' \ --validator -In the second terminal, we'll run the following to start Bob's Substrate node on a different TCP port of 30334, and with his chain database stored locally at `/tmp/bob`. We'll specify a value for the `--bootnodes` option that will connect his node to Alice's Bootnode ID on TCP port 30333: +In the second terminal, we'll run the following to start Bob's Substrate node on a different TCP port of 30334, and with their chain database stored locally at `/tmp/bob`. We'll specify a value for the `--bootnodes` option that will connect Bob's node to Alice's Bootnode ID on TCP port 30333: [source, shell] cargo run --release \-- \ diff --git a/frame/contracts/src/exec.rs b/frame/contracts/src/exec.rs index 7a07b01d2..3eb59354d 100644 --- a/frame/contracts/src/exec.rs +++ b/frame/contracts/src/exec.rs @@ -2056,9 +2056,9 @@ mod tests { #[test] fn code_hash_returns_proper_values() { let code_bob = MockLoader::insert(Call, |ctx, _| { - // ALICE is not a contract and hence she does not have a code_hash + // ALICE is not a contract and hence they do not have a code_hash assert!(ctx.ext.code_hash(&ALICE).is_none()); - // BOB is a contract and hence he has a code_hash + // BOB is a contract and hence it has a code_hash assert!(ctx.ext.code_hash(&BOB).is_some()); exec_success() }); diff --git a/frame/elections-phragmen/src/benchmarking.rs b/frame/elections-phragmen/src/benchmarking.rs index 06ac8d7c6..53157deaa 100644 --- a/frame/elections-phragmen/src/benchmarking.rs +++ b/frame/elections-phragmen/src/benchmarking.rs @@ -32,7 +32,7 @@ const BALANCE_FACTOR: u32 = 250; /// grab new account with infinite balance. fn endowed_account(name: &'static str, index: u32) -> T::AccountId { let account: T::AccountId = account(name, index, 0); - // Fund each account with at-least his stake but still a sane amount as to not mess up + // Fund each account with at-least their stake but still a sane amount as to not mess up // the vote calculation. let amount = default_stake::(T::MaxVoters::get()) * BalanceOf::::from(BALANCE_FACTOR); let _ = T::Currency::make_free_balance_be(&account, amount); diff --git a/frame/nfts/src/features/transfer.rs b/frame/nfts/src/features/transfer.rs index 7d6ae3553..3a34f0b8c 100644 --- a/frame/nfts/src/features/transfer.rs +++ b/frame/nfts/src/features/transfer.rs @@ -64,7 +64,7 @@ impl, I: 'static> Pallet { details.owner = dest; // The approved accounts have to be reset to None, because otherwise pre-approve attack - // would be possible, where the owner can approve his second account before making the + // would be possible, where the owner can approve their second account before making the // transaction and then claiming the item back. details.approvals.clear(); diff --git a/frame/nomination-pools/src/migration.rs b/frame/nomination-pools/src/migration.rs index b73141c95..4a9b6ca0f 100644 --- a/frame/nomination-pools/src/migration.rs +++ b/frame/nomination-pools/src/migration.rs @@ -63,7 +63,7 @@ pub mod v1 { /// Trivial migration which makes the roles of each pool optional. /// - /// Note: The depositor is not optional since he can never change. + /// Note: The depositor is not optional since they can never change. pub struct MigrateToV1(sp_std::marker::PhantomData); impl OnRuntimeUpgrade for MigrateToV1 { fn on_runtime_upgrade() -> Weight { diff --git a/frame/nomination-pools/test-staking/src/lib.rs b/frame/nomination-pools/test-staking/src/lib.rs index 00e0e40ce..933e42578 100644 --- a/frame/nomination-pools/test-staking/src/lib.rs +++ b/frame/nomination-pools/test-staking/src/lib.rs @@ -531,7 +531,7 @@ fn pool_slash_proportional() { #[test] fn pool_slash_non_proportional_only_bonded_pool() { - // A typical example where a pool member unbonds in era 99, and he can get away with a slash + // A typical example where a pool member unbonds in era 99, and they can get away with a slash // that happened in era 100, as long as the pool has enough active bond to cover the slash. If // everything else in the slashing/staking system works, this should always be the case. // Nonetheless, `ledger.slash` has been written such that it will slash greedily from any chunk diff --git a/frame/root-offences/src/tests.rs b/frame/root-offences/src/tests.rs index a8b7d0a6d..b0df4fcee 100644 --- a/frame/root-offences/src/tests.rs +++ b/frame/root-offences/src/tests.rs @@ -44,7 +44,7 @@ fn create_offence_works_given_root_origin() { // the slash should be applied right away. assert_eq!(Balances::free_balance(11), 500); - // the other validator should keep his balance, because we only created + // the other validator should keep their balance, because we only created // an offences for the first validator. assert_eq!(Balances::free_balance(21), 1000); }) @@ -68,7 +68,7 @@ fn create_offence_wont_slash_non_active_validators() { // so 31 didn't get slashed. assert_eq!(Balances::free_balance(31), 500); - // but 11 is an active validator so he got slashed. + // but 11 is an active validator so they got slashed. assert_eq!(Balances::free_balance(11), 800); }) } diff --git a/frame/staking/src/tests.rs b/frame/staking/src/tests.rs index acd418952..9635717d4 100644 --- a/frame/staking/src/tests.rs +++ b/frame/staking/src/tests.rs @@ -1901,7 +1901,7 @@ fn wrong_vote_is_moot() { #[test] fn bond_with_no_staked_value() { // Behavior when someone bonds with no staked value. - // Particularly when she votes and the candidate is elected. + // Particularly when they votes and the candidate is elected. ExtBuilder::default() .validator_count(3) .existential_deposit(5) diff --git a/frame/uniques/src/functions.rs b/frame/uniques/src/functions.rs index 03dba16ea..f73e5c479 100644 --- a/frame/uniques/src/functions.rs +++ b/frame/uniques/src/functions.rs @@ -50,8 +50,8 @@ impl, I: 'static> Pallet { details.owner = dest; // The approved account has to be reset to None, because otherwise pre-approve attack would - // be possible, where the owner can approve his second account before making the transaction - // and then claiming the item back. + // be possible, where the owner can approve their second account before making the + // transaction and then claiming the item back. details.approved = None; Item::::insert(&collection, &item, &details); From ce2cee35f8f0fc5968ea6ffaffa6660dcd008804 Mon Sep 17 00:00:00 2001 From: Sam Johnson Date: Fri, 27 Jan 2023 15:59:39 -0500 Subject: [PATCH 064/558] migrate new benchmarking syntax from `frame_support::benchmarking` to `frame_benchmarking::v2` (#13235) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * * re-export frame_support::benchmarking in frame_benchmarking:: * prefer use frame_benchmarking::*; in examples, etc * switch to frame_benchmarking::v2 * completely migrate new benchmarking code out of frame_support * fix doc links * remove unneeded return Co-authored-by: Bastian Köcher * remove another unneeded return Co-authored-by: Bastian Köcher * properly export all macros in v1 * refactor existing frame_benchmarking imports to use ::v1 --------- Co-authored-by: Bastian Köcher --- Cargo.lock | 3 +- .../pallets/template/src/benchmarking.rs | 2 +- frame/alliance/src/benchmarking.rs | 2 +- frame/assets/src/benchmarking.rs | 2 +- frame/babe/src/benchmarking.rs | 2 +- frame/bags-list/src/benchmarks.rs | 6 +- frame/balances/src/benchmarking.rs | 3 +- frame/benchmarking/Cargo.toml | 2 + frame/benchmarking/src/lib.rs | 2107 ++--------------- frame/benchmarking/src/v1.rs | 2043 ++++++++++++++++ frame/bounties/src/benchmarking.rs | 4 +- frame/child-bounties/src/benchmarking.rs | 2 +- frame/collective/src/benchmarking.rs | 2 +- frame/contracts/src/benchmarking/mod.rs | 2 +- frame/conviction-voting/src/benchmarking.rs | 2 +- frame/democracy/src/benchmarking.rs | 2 +- .../benchmarking/src/lib.rs | 2 +- frame/elections-phragmen/src/benchmarking.rs | 2 +- frame/examples/basic/src/benchmarking.rs | 7 +- frame/fast-unstake/src/benchmarking.rs | 2 +- frame/grandpa/src/benchmarking.rs | 2 +- frame/identity/src/benchmarking.rs | 2 +- frame/im-online/src/benchmarking.rs | 2 +- frame/indices/src/benchmarking.rs | 2 +- frame/lottery/src/benchmarking.rs | 2 +- frame/membership/src/lib.rs | 2 +- .../merkle-mountain-range/src/benchmarking.rs | 2 +- frame/message-queue/src/benchmarking.rs | 4 +- frame/multisig/src/benchmarking.rs | 2 +- frame/nfts/src/benchmarking.rs | 2 +- frame/nis/src/benchmarking.rs | 2 +- .../nomination-pools/benchmarking/src/lib.rs | 4 +- frame/offences/benchmarking/src/lib.rs | 2 +- frame/preimage/src/benchmarking.rs | 2 +- frame/proxy/src/benchmarking.rs | 2 +- frame/ranked-collective/src/benchmarking.rs | 2 +- frame/recovery/src/benchmarking.rs | 2 +- frame/referenda/src/benchmarking.rs | 2 +- frame/remark/src/benchmarking.rs | 2 +- frame/scheduler/src/benchmarking.rs | 2 +- frame/session/benchmarking/src/lib.rs | 2 +- frame/staking/src/benchmarking.rs | 2 +- frame/support/Cargo.toml | 1 - frame/support/procedural/src/benchmark.rs | 2 +- frame/support/procedural/src/lib.rs | 10 +- frame/support/src/lib.rs | 223 -- .../test/tests/benchmark_ui/bad_param_name.rs | 2 +- .../benchmark_ui/bad_param_name_too_long.rs | 4 +- .../benchmark_ui/bad_param_name_upper_case.rs | 2 +- .../tests/benchmark_ui/bad_param_range.rs | 2 +- .../test/tests/benchmark_ui/bad_params.rs | 2 +- .../test/tests/benchmark_ui/dup_block.rs | 2 +- .../tests/benchmark_ui/dup_extrinsic_call.rs | 2 +- .../test/tests/benchmark_ui/extra_extra.rs | 2 +- .../tests/benchmark_ui/extra_skip_meta.rs | 2 +- .../benchmark_ui/extrinsic_call_out_of_fn.rs | 2 +- .../test/tests/benchmark_ui/missing_call.rs | 2 +- .../test/tests/benchmark_ui/missing_origin.rs | 2 +- .../tests/benchmark_ui/pass/valid_basic.rs | 2 +- .../tests/benchmark_ui/unrecognized_option.rs | 2 +- frame/system/benchmarking/src/lib.rs | 2 +- frame/timestamp/src/benchmarking.rs | 2 +- frame/tips/src/benchmarking.rs | 2 +- frame/transaction-storage/src/benchmarking.rs | 2 +- frame/treasury/src/benchmarking.rs | 2 +- frame/uniques/src/benchmarking.rs | 2 +- frame/utility/src/benchmarking.rs | 2 +- frame/vesting/src/benchmarking.rs | 2 +- frame/whitelist/src/benchmarking.rs | 2 +- 69 files changed, 2291 insertions(+), 2240 deletions(-) create mode 100644 frame/benchmarking/src/v1.rs diff --git a/Cargo.lock b/Cargo.lock index 7bbe54692..6aafb0e17 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2138,6 +2138,7 @@ version = "4.0.0-dev" dependencies = [ "array-bytes", "frame-support", + "frame-support-procedural", "frame-system", "linregress", "log", @@ -2155,6 +2156,7 @@ dependencies = [ "sp-runtime-interface", "sp-std", "sp-storage", + "static_assertions", ] [[package]] @@ -2351,7 +2353,6 @@ dependencies = [ "sp-std", "sp-tracing", "sp-weights", - "static_assertions", "tt-call", ] diff --git a/bin/node-template/pallets/template/src/benchmarking.rs b/bin/node-template/pallets/template/src/benchmarking.rs index d496a9fc8..179084997 100644 --- a/bin/node-template/pallets/template/src/benchmarking.rs +++ b/bin/node-template/pallets/template/src/benchmarking.rs @@ -4,7 +4,7 @@ use super::*; #[allow(unused)] use crate::Pallet as Template; -use frame_benchmarking::{benchmarks, whitelisted_caller}; +use frame_benchmarking::v1::{benchmarks, whitelisted_caller}; use frame_system::RawOrigin; benchmarks! { diff --git a/frame/alliance/src/benchmarking.rs b/frame/alliance/src/benchmarking.rs index a34b76bd9..f312e032b 100644 --- a/frame/alliance/src/benchmarking.rs +++ b/frame/alliance/src/benchmarking.rs @@ -25,7 +25,7 @@ use sp_std::{ prelude::*, }; -use frame_benchmarking::{account, benchmarks_instance_pallet}; +use frame_benchmarking::v1::{account, benchmarks_instance_pallet}; use frame_support::traits::{EnsureOrigin, Get, UnfilteredDispatchable}; use frame_system::{Pallet as System, RawOrigin as SystemOrigin}; diff --git a/frame/assets/src/benchmarking.rs b/frame/assets/src/benchmarking.rs index 235d1d937..9acf69f1e 100644 --- a/frame/assets/src/benchmarking.rs +++ b/frame/assets/src/benchmarking.rs @@ -20,7 +20,7 @@ #![cfg(feature = "runtime-benchmarks")] use super::*; -use frame_benchmarking::{ +use frame_benchmarking::v1::{ account, benchmarks_instance_pallet, whitelist_account, whitelisted_caller, }; use frame_support::{ diff --git a/frame/babe/src/benchmarking.rs b/frame/babe/src/benchmarking.rs index 20002c97f..70605e4cf 100644 --- a/frame/babe/src/benchmarking.rs +++ b/frame/babe/src/benchmarking.rs @@ -20,7 +20,7 @@ #![cfg(feature = "runtime-benchmarks")] use super::*; -use frame_benchmarking::benchmarks; +use frame_benchmarking::v1::benchmarks; type Header = sp_runtime::generic::Header; diff --git a/frame/bags-list/src/benchmarks.rs b/frame/bags-list/src/benchmarks.rs index 1f66697cb..74ae5f31a 100644 --- a/frame/bags-list/src/benchmarks.rs +++ b/frame/bags-list/src/benchmarks.rs @@ -19,13 +19,15 @@ use super::*; use crate::list::List; -use frame_benchmarking::{account, whitelist_account, whitelisted_caller}; +use frame_benchmarking::v1::{ + account, benchmarks_instance_pallet, whitelist_account, whitelisted_caller, +}; use frame_election_provider_support::ScoreProvider; use frame_support::{assert_ok, traits::Get}; use frame_system::RawOrigin as SystemOrigin; use sp_runtime::traits::One; -frame_benchmarking::benchmarks_instance_pallet! { +benchmarks_instance_pallet! { rebag_non_terminal { // An expensive case for rebag-ing (rebag a non-terminal node): // diff --git a/frame/balances/src/benchmarking.rs b/frame/balances/src/benchmarking.rs index f85ff43b7..d80aa3ec8 100644 --- a/frame/balances/src/benchmarking.rs +++ b/frame/balances/src/benchmarking.rs @@ -22,8 +22,7 @@ use super::*; use crate::Pallet as Balances; -use frame_benchmarking::{account, impl_benchmark_test_suite, whitelisted_caller}; -use frame_support::benchmarking::*; +use frame_benchmarking::v2::*; use frame_system::RawOrigin; const SEED: u32 = 0; diff --git a/frame/benchmarking/Cargo.toml b/frame/benchmarking/Cargo.toml index b2a9621eb..4ff2077d7 100644 --- a/frame/benchmarking/Cargo.toml +++ b/frame/benchmarking/Cargo.toml @@ -20,6 +20,7 @@ paste = "1.0" scale-info = { version = "2.1.1", default-features = false, features = ["derive"] } serde = { version = "1.0.136", optional = true } frame-support = { version = "4.0.0-dev", default-features = false, path = "../support" } +frame-support-procedural = { version = "4.0.0-dev", default-features = false, path = "../support/procedural" } frame-system = { version = "4.0.0-dev", default-features = false, path = "../system" } sp-api = { version = "4.0.0-dev", default-features = false, path = "../../primitives/api" } sp-application-crypto = { version = "7.0.0", default-features = false, path = "../../primitives/application-crypto" } @@ -29,6 +30,7 @@ sp-runtime = { version = "7.0.0", default-features = false, path = "../../primit sp-runtime-interface = { version = "7.0.0", default-features = false, path = "../../primitives/runtime-interface" } sp-std = { version = "5.0.0", default-features = false, path = "../../primitives/std" } sp-storage = { version = "7.0.0", default-features = false, path = "../../primitives/storage" } +static_assertions = "1.1.0" [dev-dependencies] array-bytes = "4.1" diff --git a/frame/benchmarking/src/lib.rs b/frame/benchmarking/src/lib.rs index 525370e23..a3bf809f6 100644 --- a/frame/benchmarking/src/lib.rs +++ b/frame/benchmarking/src/lib.rs @@ -50,2005 +50,226 @@ pub use sp_std::{self, boxed::Box, prelude::Vec, str, vec}; pub use sp_storage::{well_known_keys, TrackedStorageKey}; pub use utils::*; -/// Whitelist the given account. -#[macro_export] -macro_rules! whitelist { - ($acc:ident) => { - frame_benchmarking::benchmarking::add_to_whitelist( - frame_system::Account::::hashed_key_for(&$acc).into(), - ); - }; -} +pub mod v1; +pub use v1::*; -/// Construct pallet benchmarks for weighing dispatchables. -/// -/// Works around the idea of complexity parameters, named by a single letter (which is usually -/// upper cased in complexity notation but is lower-cased for use in this macro). -/// -/// Complexity parameters ("parameters") have a range which is a `u32` pair. Every time a benchmark -/// is prepared and run, this parameter takes a concrete value within the range. There is an -/// associated instancing block, which is a single expression that is evaluated during -/// preparation. It may use `?` (`i.e. `return Err(...)`) to bail with a string error. Here's a -/// few examples: -/// -/// ```ignore -/// // These two are equivalent: -/// let x in 0 .. 10; -/// let x in 0 .. 10 => (); -/// // This one calls a setup function and might return an error (which would be terminal). -/// let y in 0 .. 10 => setup(y)?; -/// // This one uses a code block to do lots of stuff: -/// let z in 0 .. 10 => { -/// let a = z * z / 5; -/// let b = do_something(a)?; -/// combine_into(z, b); -/// } -/// ``` +/// Contains macros, structs, and traits associated with v2 of the pallet benchmarking syntax. /// -/// Note that due to parsing restrictions, if the `from` expression is not a single token (i.e. a -/// literal or constant), then it must be parenthesized. +/// The [`v2::benchmarks`] and [`v2::instance_benchmarks`] macros can be used to designate a +/// module as a benchmarking module that can contain benchmarks and benchmark tests. The +/// `#[benchmarks]` variant will set up a regular, non-instance benchmarking module, and the +/// `#[instance_benchmarks]` variant will set up the module in instance benchmarking mode. /// -/// The macro allows for a number of "arms", each representing an individual benchmark. Using the -/// simple syntax, the associated dispatchable function maps 1:1 with the benchmark and the name of -/// the benchmark is the same as that of the associated function. However, extended syntax allows -/// for arbitrary expressions to be evaluated in a benchmark (including for example, -/// `on_initialize`). +/// Benchmarking modules should be gated behind a `#[cfg(feature = "runtime-benchmarks")]` +/// feature gate to ensure benchmarking code that is only compiled when the +/// `runtime-benchmarks` feature is enabled is not referenced. /// -/// Note that the ranges are *inclusive* on both sides. This is in contrast to ranges in Rust which -/// are left-inclusive right-exclusive. +/// The following is the general syntax for a benchmarks (or instance benchmarks) module: /// -/// Each arm may also have a block of code which is run prior to any instancing and a block of code -/// which is run afterwards. All code blocks may draw upon the specific value of each parameter -/// at any time. Local variables are shared between the two pre- and post- code blocks, but do not -/// leak from the interior of any instancing expressions. +/// ## General Syntax /// -/// Example: /// ```ignore -/// benchmarks! { -/// where_clause { where T::A: From } // Optional line to give additional bound on `T`. -/// -/// // first dispatchable: foo; this is a user dispatchable and operates on a `u8` vector of -/// // size `l` -/// foo { -/// let caller = account::(b"caller", 0, benchmarks_seed); -/// let l in 1 .. MAX_LENGTH => initialize_l(l); -/// }: _(RuntimeOrigin::Signed(caller), vec![0u8; l]) -/// -/// // second dispatchable: bar; this is a root dispatchable and accepts a `u8` vector of size -/// // `l`. -/// // In this case, we explicitly name the call using `bar` instead of `_`. -/// bar { -/// let l in 1 .. MAX_LENGTH => initialize_l(l); -/// }: bar(RuntimeOrigin::Root, vec![0u8; l]) -/// -/// // third dispatchable: baz; this is a user dispatchable. It isn't dependent on length like the -/// // other two but has its own complexity `c` that needs setting up. It uses `caller` (in the -/// // pre-instancing block) within the code block. This is only allowed in the param instancers -/// // of arms. -/// baz1 { -/// let caller = account::(b"caller", 0, benchmarks_seed); -/// let c = 0 .. 10 => setup_c(&caller, c); -/// }: baz(RuntimeOrigin::Signed(caller)) -/// -/// // this is a second benchmark of the baz dispatchable with a different setup. -/// baz2 { -/// let caller = account::(b"caller", 0, benchmarks_seed); -/// let c = 0 .. 10 => setup_c_in_some_other_way(&caller, c); -/// }: baz(RuntimeOrigin::Signed(caller)) -/// -/// // You may optionally specify the origin type if it can't be determined automatically like -/// // this. -/// baz3 { -/// let caller = account::(b"caller", 0, benchmarks_seed); -/// let l in 1 .. MAX_LENGTH => initialize_l(l); -/// }: baz(RuntimeOrigin::Signed(caller), vec![0u8; l]) -/// -/// // this is benchmarking some code that is not a dispatchable. -/// populate_a_set { -/// let x in 0 .. 10_000; -/// let mut m = Vec::::new(); -/// for i in 0..x { -/// m.insert(i); -/// } -/// }: { m.into_iter().collect::() } -/// } -/// ``` -/// -/// Test functions are automatically generated for each benchmark and are accessible to you when you -/// run `cargo test`. All tests are named `test_benchmark_`, implemented on the -/// Pallet struct, and run them in a test externalities environment. The test function runs your -/// benchmark just like a regular benchmark, but only testing at the lowest and highest values for -/// each component. The function will return `Ok(())` if the benchmarks return no errors. -/// -/// It is also possible to generate one #[test] function per benchmark by calling the -/// `impl_benchmark_test_suite` macro inside the `benchmarks` block. The functions will be named -/// `bench_` and can be run via `cargo test`. -/// You will see one line of output per benchmark. This approach will give you more understandable -/// error messages and allows for parallel benchmark execution. -/// -/// You can optionally add a `verify` code block at the end of a benchmark to test any final state -/// of your benchmark in a unit test. For example: -/// -/// ```ignore -/// sort_vector { -/// let x in 1 .. 10000; -/// let mut m = Vec::::new(); -/// for i in (0..x).rev() { -/// m.push(i); -/// } -/// }: { -/// m.sort(); -/// } verify { -/// ensure!(m[0] == 0, "You forgot to sort!") -/// } -/// ``` -/// -/// These `verify` blocks will not affect your benchmark results! -/// -/// You can construct benchmark by using the `impl_benchmark_test_suite` macro or -/// by manually implementing them like so: -/// -/// ```ignore -/// #[test] -/// fn test_benchmarks() { -/// new_test_ext().execute_with(|| { -/// assert_ok!(Pallet::::test_benchmark_dummy()); -/// assert_err!(Pallet::::test_benchmark_other_name(), "Bad origin"); -/// assert_ok!(Pallet::::test_benchmark_sort_vector()); -/// assert_err!(Pallet::::test_benchmark_broken_benchmark(), "You forgot to sort!"); -/// }); -/// } -/// ``` -#[macro_export] -macro_rules! benchmarks { - ( - $( $rest:tt )* - ) => { - $crate::benchmarks_iter!( - { } - { } - { } - ( ) - ( ) - ( ) - ( ) - $( $rest )* - ); - } -} - -/// Same as [`benchmarks`] but for instantiable module. -/// -/// NOTE: For pallet declared with [`frame_support::pallet`], use [`benchmarks_instance_pallet`]. -#[macro_export] -macro_rules! benchmarks_instance { - ( - $( $rest:tt )* - ) => { - $crate::benchmarks_iter!( - { } - { I: Instance } - { } - ( ) - ( ) - ( ) - ( ) - $( $rest )* - ); - } -} - -/// Same as [`benchmarks`] but for instantiable pallet declared [`frame_support::pallet`]. -/// -/// NOTE: For pallet declared with `decl_module!`, use [`benchmarks_instance`]. -#[macro_export] -macro_rules! benchmarks_instance_pallet { - ( - $( $rest:tt )* - ) => { - $crate::benchmarks_iter!( - { } - { I: 'static } - { } - ( ) - ( ) - ( ) - ( ) - $( $rest )* - ); - } -} - -#[macro_export] -#[doc(hidden)] -macro_rules! benchmarks_iter { - // detect and extract `impl_benchmark_test_suite` call: - // - with a semi-colon - ( - { } - { $( $instance:ident: $instance_bound:tt )? } - { $( $where_clause:tt )* } - ( $( $names:tt )* ) - ( $( $names_extra:tt )* ) - ( $( $names_skip_meta:tt )* ) - ( $( $pov_name:ident: $( $storage:path = $pov_mode:ident )*; )* ) - impl_benchmark_test_suite!( - $bench_module:ident, - $new_test_ext:expr, - $test:path - $(, $( $args:tt )* )?); - $( $rest:tt )* - ) => { - $crate::benchmarks_iter! { - { $bench_module, $new_test_ext, $test $(, $( $args )* )? } - { $( $instance: $instance_bound )? } - { $( $where_clause )* } - ( $( $names )* ) - ( $( $names_extra )* ) - ( $( $names_skip_meta )* ) - ( $( $pov_name: $( $storage = $pov_mode )*; )* ) - $( $rest )* - } - }; - // - without a semicolon - ( - { } - { $( $instance:ident: $instance_bound:tt )? } - { $( $where_clause:tt )* } - ( $( $names:tt )* ) - ( $( $names_extra:tt )* ) - ( $( $names_skip_meta:tt )* ) - ( $( $pov_name:ident: $( $storage:path = $pov_mode:ident )*; )* ) - impl_benchmark_test_suite!( - $bench_module:ident, - $new_test_ext:expr, - $test:path - $(, $( $args:tt )* )?) - $( $rest:tt )* - ) => { - $crate::benchmarks_iter! { - { $bench_module, $new_test_ext, $test $(, $( $args )* )? } - { $( $instance: $instance_bound )? } - { $( $where_clause )* } - ( $( $names )* ) - ( $( $names_extra )* ) - ( $( $names_skip_meta )* ) - ( $( $pov_name: $( $storage = $pov_mode )*; )* ) - $( $rest )* - } - }; - // detect and extract where clause: - ( - { $($bench_module:ident, $new_test_ext:expr, $test:path $(, $( $args:tt )* )?)? } - { $( $instance:ident: $instance_bound:tt )? } - { $( $where_clause:tt )* } - ( $( $names:tt )* ) - ( $( $names_extra:tt )* ) - ( $( $names_skip_meta:tt )* ) - ( $( $pov_name:ident: $( $storage:path = $pov_mode:ident )*; )* ) - where_clause { where $( $where_bound:tt )* } - $( $rest:tt )* - ) => { - $crate::benchmarks_iter! { - { $($bench_module, $new_test_ext, $test $(, $( $args )* )?)? } - { $( $instance: $instance_bound)? } - { $( $where_bound )* } - ( $( $names )* ) - ( $( $names_extra )* ) - ( $( $names_skip_meta )* ) - ( $( $pov_name: $( $storage = $pov_mode )*; )* ) - $( $rest )* - } - }; - // detect and extract `#[skip_meta]` tag: - ( - { $($bench_module:ident, $new_test_ext:expr, $test:path $(, $( $args:tt )* )?)? } - { $( $instance:ident: $instance_bound:tt )? } - { $( $where_clause:tt )* } - ( $( $names:tt )* ) - ( $( $names_extra:tt )* ) - ( $( $names_skip_meta:tt )* ) - ( $( $pov_name:ident: $( $storage:path = $pov_mode:ident )*; )* ) - #[skip_meta] - $( #[ $($attributes:tt)+ ] )* - $name:ident - $( $rest:tt )* - ) => { - $crate::benchmarks_iter! { - { $($bench_module, $new_test_ext, $test $(, $( $args )* )?)? } - { $( $instance: $instance_bound )? } - { $( $where_clause )* } - ( $( $names )* ) - ( $( $names_extra )* ) - ( $( $names_skip_meta )* $name ) - ( $( $pov_name: $( $storage = $pov_mode )*; )* ) - $( #[ $( $attributes )+ ] )* - $name - $( $rest )* - } - }; - // detect and extract `#[extra]` tag: - ( - { $($bench_module:ident, $new_test_ext:expr, $test:path $(, $( $args:tt )* )?)? } - { $( $instance:ident: $instance_bound:tt )? } - { $( $where_clause:tt )* } - ( $( $names:tt )* ) - ( $( $names_extra:tt )* ) - ( $( $names_skip_meta:tt )* ) - ( $( $pov_name:ident: $( $storage:path = $pov_mode:ident )*; )* ) - #[extra] - $( #[ $($attributes:tt)+ ] )* - $name:ident - $( $rest:tt )* - ) => { - $crate::benchmarks_iter! { - { $($bench_module, $new_test_ext, $test $(, $( $args )* )?)? } - { $( $instance: $instance_bound )? } - { $( $where_clause )* } - ( $( $names )* ) - ( $( $names_extra )* $name ) - ( $( $names_skip_meta )* ) - ( $( $pov_name: $( $storage = $pov_mode )*; )* ) - $( #[ $( $attributes )+ ] )* - $name - $( $rest )* - } - }; - // detect and extract `#[pov_mode = Mode { Pallet::Storage: Mode ... }]` tag: - ( - { $($bench_module:ident, $new_test_ext:expr, $test:path $(, $( $args:tt )* )?)? } - { $( $instance:ident: $instance_bound:tt )? } - { $( $where_clause:tt )* } - ( $( $names:tt )* ) - ( $( $names_extra:tt )* ) - ( $( $names_skip_meta:tt )* ) - ( $( $old_pov_name:ident: $( $old_storage:path = $old_pov_mode:ident )*; )* ) - #[pov_mode = $mode:ident $( { $( $storage:path: $pov_mode:ident )* } )?] - $( #[ $($attributes:tt)+ ] )* - $name:ident - $( $rest:tt )* - ) => { - $crate::benchmarks_iter! { - { $($bench_module, $new_test_ext, $test $(, $( $args )* )?)? } - { $( $instance: $instance_bound )? } - { $( $where_clause )* } - ( $( $names )* ) - ( $( $names_extra )* ) - ( $( $names_skip_meta )* ) - ( $name: ALL = $mode $($( $storage = $pov_mode )*)?; $( $old_pov_name: $( $old_storage = $old_pov_mode )*; )* ) - $( #[ $( $attributes )+ ] )* - $name - $( $rest )* - } - }; - // mutation arm: - ( - { $($bench_module:ident, $new_test_ext:expr, $test:path $(, $( $args:tt )* )?)? } - { $( $instance:ident: $instance_bound:tt )? } - { $( $where_clause:tt )* } - ( $( $names:tt )* ) // This contains $( $( { $instance } )? $name:ident )* - ( $( $names_extra:tt )* ) - ( $( $names_skip_meta:tt )* ) - ( $( $pov_name:ident: $( $storage:path = $pov_mode:ident )*; )* ) - $name:ident { $( $code:tt )* }: _ $(< $origin_type:ty>)? ( $origin:expr $( , $arg:expr )* ) - verify $postcode:block - $( $rest:tt )* - ) => { - $crate::benchmarks_iter! { - { $($bench_module, $new_test_ext, $test $(, $( $args )* )?)? } - { $( $instance: $instance_bound )? } - { $( $where_clause )* } - ( $( $names )* ) - ( $( $names_extra )* ) - ( $( $names_skip_meta )* ) - ( $( $pov_name: $( $storage = $pov_mode )*; )* ) - $name { $( $code )* }: $name $(< $origin_type >)? ( $origin $( , $arg )* ) - verify $postcode - $( $rest )* - } - }; - // mutation arm: - ( - { $($bench_module:ident, $new_test_ext:expr, $test:path $(, $( $args:tt )* )?)? } - { $( $instance:ident: $instance_bound:tt )? } - { $( $where_clause:tt )* } - ( $( $names:tt )* ) - ( $( $names_extra:tt )* ) - ( $( $names_skip_meta:tt )* ) - ( $( $pov_name:ident: $( $storage:path = $pov_mode:ident )*; )* ) - $name:ident { $( $code:tt )* }: $dispatch:ident $(<$origin_type:ty>)? ( $origin:expr $( , $arg:expr )* ) - verify $postcode:block - $( $rest:tt )* - ) => { - $crate::paste::paste! { - $crate::benchmarks_iter! { - { $($bench_module, $new_test_ext, $test $(, $( $args )* )?)? } - { $( $instance: $instance_bound )? } - { $( $where_clause )* } - ( $( $names )* ) - ( $( $names_extra )* ) - ( $( $names_skip_meta )* ) - ( $( $pov_name: $( $storage = $pov_mode )*; )* ) - $name { - $( $code )* - let __call = Call::< - T - $( , $instance )? - >:: [< new_call_variant_ $dispatch >] ( - $($arg),* - ); - let __benchmarked_call_encoded = $crate::frame_support::codec::Encode::encode( - &__call - ); - }: { - let __call_decoded = < - Call - as $crate::frame_support::codec::Decode - >::decode(&mut &__benchmarked_call_encoded[..]) - .expect("call is encoded above, encoding must be correct"); - let __origin = $crate::to_origin!($origin $(, $origin_type)?); - as $crate::frame_support::traits::UnfilteredDispatchable - >::dispatch_bypass_filter(__call_decoded, __origin)?; - } - verify $postcode - $( $rest )* - } - } - }; - // iteration arm: - ( - { $($bench_module:ident, $new_test_ext:expr, $test:path $(, $( $args:tt )* )?)? } - { $( $instance:ident: $instance_bound:tt )? } - { $( $where_clause:tt )* } - ( $( $names:tt )* ) - ( $( $names_extra:tt )* ) - ( $( $names_skip_meta:tt )* ) - ( $( $pov_name:ident: $( $storage:path = $pov_mode:ident )*; )* ) - $name:ident { $( $code:tt )* }: $eval:block - verify $postcode:block - $( $rest:tt )* - ) => { - $crate::benchmark_backend! { - { $( $instance: $instance_bound )? } - $name - { $( $where_clause )* } - { } - { $eval } - { $( $code )* } - $postcode - } - - #[cfg(test)] - $crate::impl_benchmark_test!( - { $( $where_clause )* } - { $( $instance: $instance_bound )? } - $name - ); - - $crate::benchmarks_iter!( - { $($bench_module, $new_test_ext, $test $(, $( $args )* )?)? } - { $( $instance: $instance_bound )? } - { $( $where_clause )* } - ( $( $names )* { $( $instance )? } $name ) - ( $( $names_extra )* ) - ( $( $names_skip_meta )* ) - ( $( $pov_name: $( $storage = $pov_mode )*; )* ) - $( $rest )* - ); - }; - // iteration-exit arm which generates a #[test] function for each case. - ( - { $bench_module:ident, $new_test_ext:expr, $test:path $(, $( $args:tt )* )? } - { $( $instance:ident: $instance_bound:tt )? } - { $( $where_clause:tt )* } - ( $( $names:tt )* ) - ( $( $names_extra:tt )* ) - ( $( $names_skip_meta:tt )* ) - ( $( $pov_name:ident: $( $storage:path = $pov_mode:ident )*; )* ) - ) => { - $crate::selected_benchmark!( - { $( $where_clause)* } - { $( $instance: $instance_bound )? } - $( $names )* - ); - $crate::impl_benchmark!( - { $( $where_clause )* } - { $( $instance: $instance_bound )? } - ( $( $names )* ) - ( $( $names_extra ),* ) - ( $( $names_skip_meta ),* ) - ( $( $pov_name: $( $storage = $pov_mode )*; )* ) - ); - $crate::impl_test_function!( - ( $( $names )* ) - ( $( $names_extra )* ) - ( $( $names_skip_meta )* ) - $bench_module, - $new_test_ext, - $test - $(, $( $args )* )? - ); - }; - // iteration-exit arm which doesn't generate a #[test] function for all cases. - ( - { } - { $( $instance:ident: $instance_bound:tt )? } - { $( $where_clause:tt )* } - ( $( $names:tt )* ) - ( $( $names_extra:tt )* ) - ( $( $names_skip_meta:tt )* ) - ( $( $pov_name:ident: $( $storage:path = $pov_mode:ident )*; )* ) - ) => { - $crate::selected_benchmark!( - { $( $where_clause)* } - { $( $instance: $instance_bound )? } - $( $names )* - ); - $crate::impl_benchmark!( - { $( $where_clause )* } - { $( $instance: $instance_bound )? } - ( $( $names )* ) - ( $( $names_extra ),* ) - ( $( $names_skip_meta ),* ) - ( $( $pov_name: $( $storage = $pov_mode )*; )* ) - ); - }; - // add verify block to _() format - ( - { $($bench_module:ident, $new_test_ext:expr, $test:path $(, $( $args:tt )* )?)? } - { $( $instance:ident: $instance_bound:tt )? } - { $( $where_clause:tt )* } - ( $( $names:tt )* ) - ( $( $names_extra:tt )* ) - ( $( $names_skip_meta:tt )* ) - ( $( $pov_name:ident: $( $storage:path = $pov_mode:ident )*; )* ) - $name:ident { $( $code:tt )* }: _ $(<$origin_type:ty>)? ( $origin:expr $( , $arg:expr )* ) - $( $rest:tt )* - ) => { - $crate::benchmarks_iter! { - { $($bench_module, $new_test_ext, $test $(, $( $args )* )?)? } - { $( $instance: $instance_bound )? } - { $( $where_clause )* } - ( $( $names )* ) - ( $( $names_extra )* ) - ( $( $names_skip_meta )* ) - ( $( $pov_name: $( $storage = $pov_mode )*; )* ) - $name { $( $code )* }: _ $(<$origin_type>)? ( $origin $( , $arg )* ) - verify { } - $( $rest )* - } - }; - // add verify block to name() format - ( - { $($bench_module:ident, $new_test_ext:expr, $test:path $(, $( $args:tt )* )?)? } - { $( $instance:ident: $instance_bound:tt )? } - { $( $where_clause:tt )* } - ( $( $names:tt )* ) - ( $( $names_extra:tt )* ) - ( $( $names_skip_meta:tt )* ) - ( $( $pov_name:ident: $( $storage:path = $pov_mode:ident )*; )* ) - $name:ident { $( $code:tt )* }: $dispatch:ident $(<$origin_type:ty>)? ( $origin:expr $( , $arg:expr )* ) - $( $rest:tt )* - ) => { - $crate::benchmarks_iter! { - { $($bench_module, $new_test_ext, $test $(, $( $args )* )?)? } - { $( $instance: $instance_bound )? } - { $( $where_clause )* } - ( $( $names )* ) - ( $( $names_extra )* ) - ( $( $names_skip_meta )* ) - ( $( $pov_name: $( $storage = $pov_mode )*; )* ) - $name { $( $code )* }: $dispatch $(<$origin_type>)? ( $origin $( , $arg )* ) - verify { } - $( $rest )* - } - }; - // add verify block to {} format - ( - { $($bench_module:ident, $new_test_ext:expr, $test:path $(, $( $args:tt )* )?)? } - { $( $instance:ident: $instance_bound:tt )? } - { $( $where_clause:tt )* } - ( $( $names:tt )* ) - ( $( $names_extra:tt )* ) - ( $( $names_skip_meta:tt )* ) - ( $( $pov_name:ident: $( $storage:path = $pov_mode:ident )*; )* ) - $name:ident { $( $code:tt )* }: $(<$origin_type:ty>)? $eval:block - $( $rest:tt )* - ) => { - $crate::benchmarks_iter!( - { $($bench_module, $new_test_ext, $test $(, $( $args )* )?)? } - { $( $instance: $instance_bound )? } - { $( $where_clause )* } - ( $( $names )* ) - ( $( $names_extra )* ) - ( $( $names_skip_meta )* ) - ( $( $pov_name: $( $storage = $pov_mode )*; )* ) - $name { $( $code )* }: $(<$origin_type>)? $eval - verify { } - $( $rest )* - ); - }; -} - -#[macro_export] -#[doc(hidden)] -macro_rules! to_origin { - ($origin:expr) => { - $origin.into() - }; - ($origin:expr, $origin_type:ty) => { - <::RuntimeOrigin as From<$origin_type>>::from($origin) - }; -} - -#[macro_export] -#[doc(hidden)] -macro_rules! benchmark_backend { - // parsing arms - ( - { $( $instance:ident: $instance_bound:tt )? } - $name:ident - { $( $where_clause:tt )* } - { $( PRE { $( $pre_parsed:tt )* } )* } - { $eval:block } - { - let $pre_id:tt : $pre_ty:ty = $pre_ex:expr; - $( $rest:tt )* - } - $postcode:block - ) => { - $crate::benchmark_backend! { - { $( $instance: $instance_bound )? } - $name - { $( $where_clause )* } - { - $( PRE { $( $pre_parsed )* } )* - PRE { $pre_id , $pre_ty , $pre_ex } - } - { $eval } - { $( $rest )* } - $postcode - } - }; - ( - { $( $instance:ident: $instance_bound:tt )? } - $name:ident - { $( $where_clause:tt )* } - { $( $parsed:tt )* } - { $eval:block } - { - let $param:ident in ( $param_from:expr ) .. $param_to:expr => $param_instancer:expr; - $( $rest:tt )* - } - $postcode:block - ) => { - $crate::benchmark_backend! { - { $( $instance: $instance_bound )? } - $name - { $( $where_clause )* } - { - $( $parsed )* - PARAM { $param , $param_from , $param_to , $param_instancer } - } - { $eval } - { $( $rest )* } - $postcode - } - }; - // mutation arm to look after a single tt for param_from. - ( - { $( $instance:ident: $instance_bound:tt )? } - $name:ident - { $( $where_clause:tt )* } - { $( $parsed:tt )* } - { $eval:block } - { - let $param:ident in $param_from:tt .. $param_to:expr => $param_instancer:expr ; - $( $rest:tt )* - } - $postcode:block - ) => { - $crate::benchmark_backend! { - { $( $instance: $instance_bound )? } - $name - { $( $where_clause )* } - { $( $parsed )* } - { $eval } - { - let $param in ( $param_from ) .. $param_to => $param_instancer; - $( $rest )* - } - $postcode - } - }; - // mutation arm to look after the default tail of `=> ()` - ( - { $( $instance:ident: $instance_bound:tt )? } - $name:ident - { $( $where_clause:tt )* } - { $( $parsed:tt )* } - { $eval:block } - { - let $param:ident in $param_from:tt .. $param_to:expr; - $( $rest:tt )* - } - $postcode:block - ) => { - $crate::benchmark_backend! { - { $( $instance: $instance_bound )? } - $name - { $( $where_clause )* } - { $( $parsed )* } - { $eval } - { - let $param in $param_from .. $param_to => (); - $( $rest )* - } - $postcode - } - }; - // mutation arm to look after `let _ =` - ( - { $( $instance:ident: $instance_bound:tt )? } - $name:ident - { $( $where_clause:tt )* } - { $( $parsed:tt )* } - { $eval:block } - { - let $pre_id:tt = $pre_ex:expr; - $( $rest:tt )* - } - $postcode:block - ) => { - $crate::benchmark_backend! { - { $( $instance: $instance_bound )? } - $name - { $( $where_clause )* } - { $( $parsed )* } - { $eval } - { - let $pre_id : _ = $pre_ex; - $( $rest )* - } - $postcode - } - }; - // actioning arm - ( - { $( $instance:ident: $instance_bound:tt )? } - $name:ident - { $( $where_clause:tt )* } - { - $( PRE { $pre_id:tt , $pre_ty:ty , $pre_ex:expr } )* - $( PARAM { $param:ident , $param_from:expr , $param_to:expr , $param_instancer:expr } )* - } - { $eval:block } - { $( $post:tt )* } - $postcode:block - ) => { - #[allow(non_camel_case_types)] - struct $name; - #[allow(unused_variables)] - impl, $instance: $instance_bound )? > - $crate::BenchmarkingSetup for $name - where $( $where_clause )* - { - fn components(&self) -> $crate::Vec<($crate::BenchmarkParameter, u32, u32)> { - $crate::vec! [ - $( - ($crate::BenchmarkParameter::$param, $param_from, $param_to) - ),* - ] - } - - fn instance( - &self, - components: &[($crate::BenchmarkParameter, u32)], - verify: bool - ) -> Result<$crate::Box Result<(), $crate::BenchmarkError>>, $crate::BenchmarkError> { - $( - // Prepare instance - let $param = components.iter() - .find(|&c| c.0 == $crate::BenchmarkParameter::$param) - .ok_or("Could not find component in benchmark preparation.")? - .1; - )* - $( - let $pre_id : $pre_ty = $pre_ex; - )* - $( $param_instancer ; )* - $( $post )* - - Ok($crate::Box::new(move || -> Result<(), $crate::BenchmarkError> { - $eval; - if verify { - $postcode; - } - Ok(()) - })) - } - } - }; -} - -// Creates #[test] functions for the given bench cases. -#[macro_export] -#[doc(hidden)] -macro_rules! impl_bench_case_tests { - ( - { $module:ident, $new_test_exec:expr, $exec_name:ident, $test:path, $extra:expr } - { $( $names_extra:tt )* } - $( { $( $bench_inst:ident )? } $bench:ident )* - ) - => { - $crate::impl_bench_name_tests!( - $module, $new_test_exec, $exec_name, $test, $extra, - { $( $names_extra )* }, - $( { $bench } )+ - ); - } -} - -// Creates a #[test] function for the given bench name. -#[macro_export] -#[doc(hidden)] -macro_rules! impl_bench_name_tests { - // recursion anchor - ( - $module:ident, $new_test_exec:expr, $exec_name:ident, $test:path, $extra:expr, - { $( $names_extra:tt )* }, - { $name:ident } - ) => { - $crate::paste::paste! { - #[test] - fn [] () { - $new_test_exec.$exec_name(|| { - // Skip all #[extra] benchmarks if $extra is false. - if !($extra) { - let disabled = $crate::vec![ $( stringify!($names_extra).as_ref() ),* ]; - if disabled.contains(&stringify!($name)) { - $crate::log::error!( - "INFO: extra benchmark skipped - {}", - stringify!($name), - ); - return (); - } - } - - // Same per-case logic as when all cases are run in the - // same function. - match std::panic::catch_unwind(|| { - $module::<$test>::[< test_benchmark_ $name >] () - }) { - Err(err) => { - panic!("{}: {:?}", stringify!($name), err); - }, - Ok(Err(err)) => { - match err { - $crate::BenchmarkError::Stop(err) => { - panic!("{}: {:?}", stringify!($name), err); - }, - $crate::BenchmarkError::Override(_) => { - // This is still considered a success condition. - $crate::log::error!( - "WARNING: benchmark error overrided - {}", - stringify!($name), - ); - }, - $crate::BenchmarkError::Skip => { - // This is considered a success condition. - $crate::log::error!( - "WARNING: benchmark error skipped - {}", - stringify!($name), - ); - }, - $crate::BenchmarkError::Weightless => { - // This is considered a success condition. - $crate::log::error!( - "WARNING: benchmark weightless skipped - {}", - stringify!($name), - ); - } - } - }, - Ok(Ok(())) => (), - } - }); - } - } - }; - // recursion tail - ( - $module:ident, $new_test_exec:expr, $exec_name:ident, $test:path, $extra:expr, - { $( $names_extra:tt )* }, - { $name:ident } $( { $rest:ident } )+ - ) => { - // car - $crate::impl_bench_name_tests!($module, $new_test_exec, $exec_name, $test, $extra, - { $( $names_extra )* }, { $name }); - // cdr - $crate::impl_bench_name_tests!($module, $new_test_exec, $exec_name, $test, $extra, - { $( $names_extra )* }, $( { $rest } )+); - }; -} - -// Creates a `SelectedBenchmark` enum implementing `BenchmarkingSetup`. -// -// Every variant must implement [`BenchmarkingSetup`]. -// -// ```nocompile -// -// struct Transfer; -// impl BenchmarkingSetup for Transfer { ... } -// -// struct SetBalance; -// impl BenchmarkingSetup for SetBalance { ... } -// -// selected_benchmark!({} Transfer {} SetBalance); -// ``` -#[macro_export] -#[doc(hidden)] -macro_rules! selected_benchmark { - ( - { $( $where_clause:tt )* } - { $( $instance:ident: $instance_bound:tt )? } - $( { $( $bench_inst:ident )? } $bench:ident )* - ) => { - // The list of available benchmarks for this pallet. - #[allow(non_camel_case_types)] - enum SelectedBenchmark { - $( $bench, )* - } - - // Allow us to select a benchmark from the list of available benchmarks. - impl, $instance: $instance_bound )? > - $crate::BenchmarkingSetup for SelectedBenchmark - where $( $where_clause )* - { - fn components(&self) -> $crate::Vec<($crate::BenchmarkParameter, u32, u32)> { - match self { - $( - Self::$bench => < - $bench as $crate::BenchmarkingSetup - >::components(&$bench), - )* - } - } - - fn instance( - &self, - components: &[($crate::BenchmarkParameter, u32)], - verify: bool - ) -> Result<$crate::Box Result<(), $crate::BenchmarkError>>, $crate::BenchmarkError> { - match self { - $( - Self::$bench => < - $bench as $crate::BenchmarkingSetup - >::instance(&$bench, components, verify), - )* - } - } - } - }; -} - -#[macro_export] -#[doc(hidden)] -macro_rules! impl_benchmark { - ( - { $( $where_clause:tt )* } - { $( $instance:ident: $instance_bound:tt )? } - ( $( { $( $name_inst:ident )? } $name:ident )* ) - ( $( $name_extra:ident ),* ) - ( $( $name_skip_meta:ident ),* ) - ( $( $pov_name:ident: $( $storage:path = $pov_mode:ident )*; )* ) - ) => { - // We only need to implement benchmarks for the runtime-benchmarks feature or testing. - #[cfg(any(feature = "runtime-benchmarks", test))] - impl, $instance: $instance_bound )? > - $crate::Benchmarking for Pallet - where T: frame_system::Config, $( $where_clause )* - { - fn benchmarks(extra: bool) -> $crate::Vec<$crate::BenchmarkMetadata> { - $($crate::validate_pov_mode!( - $pov_name: $( $storage = $pov_mode )*; - );)* - let mut all_names = $crate::vec![ $( stringify!($name).as_ref() ),* ]; - if !extra { - let extra = [ $( stringify!($name_extra).as_ref() ),* ]; - all_names.retain(|x| !extra.contains(x)); - } - let pov_modes: $crate::Vec<($crate::Vec, $crate::Vec<($crate::Vec, $crate::Vec)>)> = $crate::vec![ - $( - (stringify!($pov_name).as_bytes().to_vec(), - $crate::vec![ - $( ( stringify!($storage).as_bytes().to_vec(), - stringify!($pov_mode).as_bytes().to_vec() ), )* - ]), - )* - ]; - all_names.into_iter().map(|benchmark| { - let selected_benchmark = match benchmark { - $( stringify!($name) => SelectedBenchmark::$name, )* - _ => panic!("all benchmarks should be selectable"), - }; - let name = benchmark.as_bytes().to_vec(); - let components = < - SelectedBenchmark as $crate::BenchmarkingSetup - >::components(&selected_benchmark); - - $crate::BenchmarkMetadata { - name: name.clone(), - components, - pov_modes: pov_modes.iter().find(|p| p.0 == name).map(|p| p.1.clone()).unwrap_or_default(), - } - }).collect::<$crate::Vec<_>>() - } - - fn run_benchmark( - extrinsic: &[u8], - c: &[($crate::BenchmarkParameter, u32)], - whitelist: &[$crate::TrackedStorageKey], - verify: bool, - internal_repeats: u32, - ) -> Result<$crate::Vec<$crate::BenchmarkResult>, $crate::BenchmarkError> { - // Map the input to the selected benchmark. - let extrinsic = $crate::str::from_utf8(extrinsic) - .map_err(|_| "`extrinsic` is not a valid utf8 string!")?; - let selected_benchmark = match extrinsic { - $( stringify!($name) => SelectedBenchmark::$name, )* - _ => return Err("Could not find extrinsic.".into()), - }; - - // Add whitelist to DB including whitelisted caller - let mut whitelist = whitelist.to_vec(); - let whitelisted_caller_key = - as $crate::frame_support::storage::StorageMap<_,_>>::hashed_key_for( - $crate::whitelisted_caller::() - ); - whitelist.push(whitelisted_caller_key.into()); - // Whitelist the transactional layer. - let transactional_layer_key = $crate::TrackedStorageKey::new( - $crate::frame_support::storage::transactional::TRANSACTION_LEVEL_KEY.into() - ); - whitelist.push(transactional_layer_key); - // Whitelist the `:extrinsic_index`. - let extrinsic_index = $crate::TrackedStorageKey::new( - $crate::well_known_keys::EXTRINSIC_INDEX.into() - ); - whitelist.push(extrinsic_index); - - $crate::benchmarking::set_whitelist(whitelist.clone()); - - let mut results: $crate::Vec<$crate::BenchmarkResult> = $crate::Vec::new(); - - // Always do at least one internal repeat... - for _ in 0 .. internal_repeats.max(1) { - // Always reset the state after the benchmark. - $crate::defer!($crate::benchmarking::wipe_db()); - - // Set up the externalities environment for the setup we want to - // benchmark. - let closure_to_benchmark = < - SelectedBenchmark as $crate::BenchmarkingSetup - >::instance(&selected_benchmark, c, verify)?; - - // Set the block number to at least 1 so events are deposited. - if $crate::Zero::is_zero(&frame_system::Pallet::::block_number()) { - frame_system::Pallet::::set_block_number(1u32.into()); - } - - // Commit the externalities to the database, flushing the DB cache. - // This will enable worst case scenario for reading from the database. - $crate::benchmarking::commit_db(); - - // Access all whitelisted keys to get them into the proof recorder since the - // recorder does now have a whitelist. - for key in &whitelist { - $crate::frame_support::storage::unhashed::get_raw(&key.key); - } - - // Reset the read/write counter so we don't count operations in the setup process. - $crate::benchmarking::reset_read_write_count(); - - // Time the extrinsic logic. - $crate::log::trace!( - target: "benchmark", - "Start Benchmark: {} ({:?}) verify {}", - extrinsic, - c, - verify - ); - - let start_pov = $crate::benchmarking::proof_size(); - let start_extrinsic = $crate::benchmarking::current_time(); - - closure_to_benchmark()?; - - let finish_extrinsic = $crate::benchmarking::current_time(); - let end_pov = $crate::benchmarking::proof_size(); - - // Calculate the diff caused by the benchmark. - let elapsed_extrinsic = finish_extrinsic.saturating_sub(start_extrinsic); - let diff_pov = match (start_pov, end_pov) { - (Some(start), Some(end)) => end.saturating_sub(start), - _ => Default::default(), - }; - - // Commit the changes to get proper write count - $crate::benchmarking::commit_db(); - $crate::log::trace!( - target: "benchmark", - "End Benchmark: {} ns", elapsed_extrinsic - ); - let read_write_count = $crate::benchmarking::read_write_count(); - $crate::log::trace!( - target: "benchmark", - "Read/Write Count {:?}", read_write_count - ); - $crate::log::trace!( - target: "benchmark", - "Proof sizes: before {:?} after {:?} diff {}", &start_pov, &end_pov, &diff_pov - ); - - // Time the storage root recalculation. - let start_storage_root = $crate::benchmarking::current_time(); - $crate::storage_root($crate::StateVersion::V1); - let finish_storage_root = $crate::benchmarking::current_time(); - let elapsed_storage_root = finish_storage_root - start_storage_root; - - let skip_meta = [ $( stringify!($name_skip_meta).as_ref() ),* ]; - let read_and_written_keys = if skip_meta.contains(&extrinsic) { - $crate::vec![(b"Skipped Metadata".to_vec(), 0, 0, false)] - } else { - $crate::benchmarking::get_read_and_written_keys() - }; - - results.push($crate::BenchmarkResult { - components: c.to_vec(), - extrinsic_time: elapsed_extrinsic, - storage_root_time: elapsed_storage_root, - reads: read_write_count.0, - repeat_reads: read_write_count.1, - writes: read_write_count.2, - repeat_writes: read_write_count.3, - proof_size: diff_pov, - keys: read_and_written_keys, - }); - } - - return Ok(results); - } - } - - #[cfg(test)] - impl, $instance: $instance_bound )? > - Pallet - where T: frame_system::Config, $( $where_clause )* - { - /// Test a particular benchmark by name. - /// - /// This isn't called `test_benchmark_by_name` just in case some end-user eventually - /// writes a benchmark, itself called `by_name`; the function would be shadowed in - /// that case. - /// - /// This is generally intended to be used by child test modules such as those created - /// by the `impl_benchmark_test_suite` macro. However, it is not an error if a pallet - /// author chooses not to implement benchmarks. - #[allow(unused)] - fn test_bench_by_name(name: &[u8]) -> Result<(), $crate::BenchmarkError> { - let name = $crate::str::from_utf8(name) - .map_err(|_| -> $crate::BenchmarkError { "`name` is not a valid utf8 string!".into() })?; - match name { - $( stringify!($name) => { - $crate::paste::paste! { Self::[< test_benchmark_ $name >]() } - } )* - _ => Err("Could not find test for requested benchmark.".into()), - } - } - } - }; -} - -// This creates a unit test for one benchmark of the main benchmark macro. -// It runs the benchmark using the `high` and `low` value for each component -// and ensure that everything completes successfully. -// Instances each component with six values which can be controlled with the -// env variable `VALUES_PER_COMPONENT`. -#[macro_export] -#[doc(hidden)] -macro_rules! impl_benchmark_test { - ( - { $( $where_clause:tt )* } - { $( $instance:ident: $instance_bound:tt )? } - $name:ident - ) => { - $crate::paste::item! { - #[cfg(test)] - impl, $instance: $instance_bound )? > - Pallet - where T: frame_system::Config, $( $where_clause )* - { - #[allow(unused)] - fn [] () -> Result<(), $crate::BenchmarkError> { - let selected_benchmark = SelectedBenchmark::$name; - let components = < - SelectedBenchmark as $crate::BenchmarkingSetup - >::components(&selected_benchmark); - - let execute_benchmark = | - c: $crate::Vec<($crate::BenchmarkParameter, u32)> - | -> Result<(), $crate::BenchmarkError> { - // Always reset the state after the benchmark. - $crate::defer!($crate::benchmarking::wipe_db()); - - // Set up the benchmark, return execution + verification function. - let closure_to_verify = < - SelectedBenchmark as $crate::BenchmarkingSetup - >::instance(&selected_benchmark, &c, true)?; - - // Set the block number to at least 1 so events are deposited. - if $crate::Zero::is_zero(&frame_system::Pallet::::block_number()) { - frame_system::Pallet::::set_block_number(1u32.into()); - } - - // Run execution + verification - closure_to_verify() - }; - - if components.is_empty() { - execute_benchmark(Default::default())?; - } else { - let num_values: u32 = if let Ok(ev) = std::env::var("VALUES_PER_COMPONENT") { - ev.parse().map_err(|_| { - $crate::BenchmarkError::Stop( - "Could not parse env var `VALUES_PER_COMPONENT` as u32." - ) - })? - } else { - 6 - }; - - if num_values < 2 { - return Err("`VALUES_PER_COMPONENT` must be at least 2".into()); - } - - for (name, low, high) in components.clone().into_iter() { - // Test the lowest, highest (if its different from the lowest) - // and up to num_values-2 more equidistant values in between. - // For 0..10 and num_values=6 this would mean: [0, 2, 4, 6, 8, 10] - - let mut values = $crate::vec![low]; - let diff = (high - low).min(num_values - 1); - let slope = (high - low) as f32 / diff as f32; - - for i in 1..=diff { - let value = ((low as f32 + slope * i as f32) as u32) - .clamp(low, high); - values.push(value); - } - - for component_value in values { - // Select the max value for all the other components. - let c: $crate::Vec<($crate::BenchmarkParameter, u32)> = components - .iter() - .map(|(n, _, h)| - if *n == name { - (*n, component_value) - } else { - (*n, *h) - } - ) - .collect(); - - execute_benchmark(c)?; - } - } - } - Ok(()) - } - } - } - }; -} - -/// This creates a test suite which runs the module's benchmarks. +/// #![cfg(feature = "runtime-benchmarks")] /// -/// When called in `pallet_example_basic` as +/// use super::{mock_helpers::*, Pallet as MyPallet}; +/// use frame_benchmarking::v2::*; /// -/// ```rust,ignore -/// impl_benchmark_test_suite!(Pallet, crate::tests::new_test_ext(), crate::tests::Test); -/// ``` -/// -/// It expands to the equivalent of: -/// -/// ```rust,ignore -/// #[cfg(test)] -/// mod tests { +/// #[benchmarks] +/// mod benchmarks { /// use super::*; -/// use crate::tests::{new_test_ext, Test}; -/// use frame_support::assert_ok; /// -/// #[test] -/// fn test_benchmarks() { -/// new_test_ext().execute_with(|| { -/// assert_ok!(test_benchmark_accumulate_dummy::()); -/// assert_ok!(test_benchmark_set_dummy::()); -/// assert_ok!(test_benchmark_sort_vector::()); -/// }); -/// } -/// } -/// ``` -/// -/// When called inside the `benchmarks` macro of the `pallet_example_basic` as -/// -/// ```rust,ignore -/// benchmarks! { -/// // Benchmarks omitted for brevity +/// #[benchmark] +/// fn bench_name_1(x: Linear<7, 1_000>, y: Linear<1_000, 100_0000>) { +/// // setup code +/// let z = x + y; +/// let caller = whitelisted_caller(); /// -/// impl_benchmark_test_suite!(Pallet, crate::tests::new_test_ext(), crate::tests::Test); -/// } -/// ``` +/// #[extrinsic_call] +/// extrinsic_name(SystemOrigin::Signed(caller), other, arguments); /// -/// It expands to the equivalent of: -/// -/// ```rust,ignore -/// #[cfg(test)] -/// mod benchmarking { -/// use super::*; -/// use crate::tests::{new_test_ext, Test}; -/// use frame_support::assert_ok; -/// -/// #[test] -/// fn bench_accumulate_dummy() { -/// new_test_ext().execute_with(|| { -/// assert_ok!(test_benchmark_accumulate_dummy::()); -/// }) +/// // verification code +/// assert_eq!(MyPallet::::my_var(), z); /// } /// -/// #[test] -/// fn bench_set_dummy() { -/// new_test_ext().execute_with(|| { -/// assert_ok!(test_benchmark_set_dummy::()); -/// }) -/// } +/// #[benchmark] +/// fn bench_name_2() { +/// // setup code +/// let caller = whitelisted_caller(); /// -/// #[test] -/// fn bench_sort_vector() { -/// new_test_ext().execute_with(|| { -/// assert_ok!(test_benchmark_sort_vector::()); -/// }) +/// #[block] +/// { +/// something(some, thing); +/// my_extrinsic(RawOrigin::Signed(caller), some, argument); +/// something_else(foo, bar); +/// } +/// +/// // verification code +/// assert_eq!(MyPallet::::something(), 37); /// } /// } /// ``` /// -/// ## Arguments +/// ## Benchmark Definitions /// -/// The first argument, `module`, must be the path to this crate's module. +/// Within a `#[benchmarks]` or `#[instance_benchmarks]` module, you can define individual +/// benchmarks using the `#[benchmark]` attribute, as shown in the example above. /// -/// The second argument, `new_test_ext`, must be a function call which returns either a -/// `sp_io::TestExternalities`, or some other type with a similar interface. +/// The `#[benchmark]` attribute expects a function definition with a blank return type and +/// zero or more arguments whose names are valid +/// [BenchmarkParameter](`crate::BenchmarkParameter`) parameters, such as `x`, `y`, `a`, `b`, +/// etc., and whose param types must implement [ParamRange](`v2::ParamRange`). At the moment +/// the only valid type that implements [ParamRange](`v2::ParamRange`) is +/// [Linear](`v2::Linear`). /// -/// Note that this function call is _not_ evaluated at compile time, but is instead copied textually -/// into each appropriate invocation site. +/// The valid syntax for defining a [Linear](`v2::Linear`)is `Linear` where `A`, and `B` +/// are valid integer literals (that fit in a `u32`), such that `B` >= `A`. /// -/// The third argument, `test`, must be the path to the runtime. The item to which this must refer -/// will generally take the form: +/// Note that the benchmark function definition does not actually expand as a function +/// definition, but rather is used to automatically create a number of impls and structs +/// required by the benchmarking engine. For this reason, the visibility of the function +/// definition as well as the return type are not used for any purpose and are discarded by the +/// expansion code. /// -/// ```rust,ignore -/// frame_support::construct_runtime!( -/// pub enum Test where ... -/// { ... } -/// ); -/// ``` +/// Also note that the `// setup code` and `// verification code` comments shown above are not +/// required and are included simply for demonstration purposes. /// -/// There is an optional fourth argument, with keyword syntax: `benchmarks_path = -/// path_to_benchmarks_invocation`. In the typical case in which this macro is in the same module as -/// the `benchmarks!` invocation, you don't need to supply this. However, if the -/// `impl_benchmark_test_suite!` invocation is in a different module than the `benchmarks!` -/// invocation, then you should provide the path to the module containing the `benchmarks!` -/// invocation: +/// ### `#[extrinsic_call]` and `#[block]` /// -/// ```rust,ignore -/// mod benches { -/// benchmarks!{ -/// ... -/// } -/// } +/// Within the benchmark function body, either an `#[extrinsic_call]` or a `#[block]` +/// annotation is required. These attributes should be attached to a block (shown in +/// `bench_name_2` above) or a one-line function call (shown in `bench_name_1` above, in `syn` +/// parlance this should be an `ExprCall`), respectively. /// -/// mod tests { -/// // because of macro syntax limitations, neither Pallet nor benches can be paths, but both have -/// // to be idents in the scope of `impl_benchmark_test_suite`. -/// use crate::{benches, Pallet}; +/// The `#[block]` syntax is broad and will benchmark any code contained within the block the +/// attribute is attached to. If `#[block]` is attached to something other than a block, a +/// compiler error will be emitted. /// -/// impl_benchmark_test_suite!(Pallet, new_test_ext(), Test, benchmarks_path = benches); +/// The one-line `#[extrinsic_call]` syntax must consist of a function call to an extrinsic, +/// where the first argument is the origin. If `#[extrinsic_call]` is attached to an item that +/// doesn't meet these requirements, a compiler error will be emitted. /// -/// // new_test_ext and the Test item are defined later in this module -/// } -/// ``` -/// -/// There is an optional fifth argument, with keyword syntax: `extra = true` or `extra = false`. -/// By default, this generates a test suite which iterates over all benchmarks, including those -/// marked with the `#[extra]` annotation. Setting `extra = false` excludes those. -/// -/// There is an optional sixth argument, with keyword syntax: `exec_name = custom_exec_name`. -/// By default, this macro uses `execute_with` for this parameter. This argument, if set, is subject -/// to these restrictions: -/// -/// - It must be the name of a method applied to the output of the `new_test_ext` argument. -/// - That method must have a signature capable of receiving a single argument of the form `impl -/// FnOnce()`. -// ## Notes (not for rustdoc) -// -// The biggest challenge for this macro is communicating the actual test functions to be run. We -// can't just build an array of function pointers to each test function and iterate over it, because -// the test functions are parameterized by the `Test` type. That's incompatible with -// monomorphization: if it were legal, then even if the compiler detected and monomorphized the -// functions into only the types of the callers, which implementation would the function pointer -// point to? There would need to be some kind of syntax for selecting the destination of the pointer -// according to a generic argument, and in general it would be a huge mess and not worth it. -// -// Instead, we're going to steal a trick from `fn run_benchmark`: generate a function which is -// itself parametrized by `Test`, which accepts a `&[u8]` parameter containing the name of the -// benchmark, and dispatches based on that to the appropriate real test implementation. Then, we can -// just iterate over the `Benchmarking::benchmarks` list to run the actual implementations. -#[macro_export] -macro_rules! impl_benchmark_test_suite { - ( - $bench_module:ident, - $new_test_ext:expr, - $test:path - $(, $( $rest:tt )* )? - ) => { - $crate::impl_test_function!( - () - () - () - $bench_module, - $new_test_ext, - $test - $(, $( $rest )* )? - ); - } -} - -/// Validates the passed `pov_mode`s. -/// -/// Checks that: -/// - a top-level `ignored` is exclusive -/// - all modes are valid -#[macro_export] -macro_rules! validate_pov_mode { - () => {}; - ( $_bench:ident: ; ) => { }; - ( $_bench:ident: $_car:path = Ignored ; ) => { }; - ( $bench:ident: $_car:path = Ignored $( $storage:path = $_pov_mode:ident )+; ) => { - compile_error!( - concat!(concat!("`pov_mode = Ignored` is exclusive. Please remove the attribute from keys: ", $( stringify!($storage) )+), " on benchmark '", stringify!($bench), "'")); - }; - ( $bench:ident: $car:path = Measured $( $storage:path = $pov_mode:ident )*; ) => { - $crate::validate_pov_mode!( - $bench: $( $storage = $pov_mode )*; - ); - }; - ( $bench:ident: $car:path = MaxEncodedLen $( $storage:path = $pov_mode:ident )*; ) => { - $crate::validate_pov_mode!( - $bench: $( $storage = $pov_mode )*; - ); - }; - ( $bench:ident: $key:path = $unknown:ident $( $_storage:path = $_pov_mode:ident )*; ) => { - compile_error!( - concat!("Unknown pov_mode '", stringify!($unknown) ,"' for benchmark '", stringify!($bench), "' on key '", stringify!($key), "'. Must be one of: Ignored, Measured, MaxEncodedLen") - ); - }; -} - -// Takes all arguments from `impl_benchmark_test_suite` and three additional arguments. -// -// Can be configured to generate one #[test] fn per bench case or -// one #[test] fn for all bench cases. -// This depends on whether or not the first argument contains a non-empty list of bench names. -#[macro_export] -#[doc(hidden)] -macro_rules! impl_test_function { - // user might or might not have set some keyword arguments; set the defaults - // - // The weird syntax indicates that `rest` comes only after a comma, which is otherwise optional - ( - ( $( $names:tt )* ) - ( $( $names_extra:tt )* ) - ( $( $names_skip_meta:tt )* ) - - $bench_module:ident, - $new_test_ext:expr, - $test:path - $(, $( $rest:tt )* )? - ) => { - $crate::impl_test_function!( - @cases: - ( $( $names )* ) - ( $( $names_extra )* ) - ( $( $names_skip_meta )* ) - @selected: - $bench_module, - $new_test_ext, - $test, - benchmarks_path = super, - extra = true, - exec_name = execute_with, - @user: - $( $( $rest )* )? - ); - }; - // pick off the benchmarks_path keyword argument - ( - @cases: - ( $( $names:tt )* ) - ( $( $names_extra:tt )* ) - ( $( $names_skip_meta:tt )* ) - @selected: - $bench_module:ident, - $new_test_ext:expr, - $test:path, - benchmarks_path = $old:ident, - extra = $extra:expr, - exec_name = $exec_name:ident, - @user: - benchmarks_path = $benchmarks_path:ident - $(, $( $rest:tt )* )? - ) => { - $crate::impl_test_function!( - @cases: - ( $( $names )* ) - ( $( $names_extra )* ) - ( $( $names_skip_meta )* ) - @selected: - $bench_module, - $new_test_ext, - $test, - benchmarks_path = $benchmarks_path, - extra = $extra, - exec_name = $exec_name, - @user: - $( $( $rest )* )? - ); - }; - // pick off the extra keyword argument - ( - @cases: - ( $( $names:tt )* ) - ( $( $names_extra:tt )* ) - ( $( $names_skip_meta:tt )* ) - @selected: - $bench_module:ident, - $new_test_ext:expr, - $test:path, - benchmarks_path = $benchmarks_path:ident, - extra = $old:expr, - exec_name = $exec_name:ident, - @user: - extra = $extra:expr - $(, $( $rest:tt )* )? - ) => { - $crate::impl_test_function!( - @cases: - ( $( $names )* ) - ( $( $names_extra )* ) - ( $( $names_skip_meta )* ) - @selected: - $bench_module, - $new_test_ext, - $test, - benchmarks_path = $benchmarks_path, - extra = $extra, - exec_name = $exec_name, - @user: - $( $( $rest )* )? - ); - }; - // pick off the exec_name keyword argument - ( - @cases: - ( $( $names:tt )* ) - ( $( $names_extra:tt )* ) - ( $( $names_skip_meta:tt )* ) - @selected: - $bench_module:ident, - $new_test_ext:expr, - $test:path, - benchmarks_path = $benchmarks_path:ident, - extra = $extra:expr, - exec_name = $old:ident, - @user: - exec_name = $exec_name:ident - $(, $( $rest:tt )* )? - ) => { - $crate::impl_test_function!( - @cases: - ( $( $names )* ) - ( $( $names_extra )* ) - ( $( $names_skip_meta )* ) - @selected: - $bench_module, - $new_test_ext, - $test, - benchmarks_path = $benchmarks_path, - extra = $extra, - exec_name = $exec_name, - @user: - $( $( $rest )* )? - ); - }; - // iteration-exit arm which generates a #[test] function for each case. - ( - @cases: - ( $( $names:tt )+ ) - ( $( $names_extra:tt )* ) - ( $( $names_skip_meta:tt )* ) - @selected: - $bench_module:ident, - $new_test_ext:expr, - $test:path, - benchmarks_path = $path_to_benchmarks_invocation:ident, - extra = $extra:expr, - exec_name = $exec_name:ident, - @user: - $(,)? - ) => { - $crate::impl_bench_case_tests!( - { $bench_module, $new_test_ext, $exec_name, $test, $extra } - { $( $names_extra:tt )* } - $($names)+ - ); - }; - // iteration-exit arm which generates one #[test] function for all cases. - ( - @cases: - () - () - () - @selected: - $bench_module:ident, - $new_test_ext:expr, - $test:path, - benchmarks_path = $path_to_benchmarks_invocation:ident, - extra = $extra:expr, - exec_name = $exec_name:ident, - @user: - $(,)? - ) => { - #[cfg(test)] - mod benchmark_tests { - use super::$bench_module; - - #[test] - fn test_benchmarks() { - $new_test_ext.$exec_name(|| { - use $crate::Benchmarking; - - let mut anything_failed = false; - println!("failing benchmark tests:"); - for benchmark_metadata in $bench_module::<$test>::benchmarks($extra) { - let benchmark_name = &benchmark_metadata.name; - match std::panic::catch_unwind(|| { - $bench_module::<$test>::test_bench_by_name(benchmark_name) - }) { - Err(err) => { - println!( - "{}: {:?}", - $crate::str::from_utf8(benchmark_name) - .expect("benchmark name is always a valid string!"), - err, - ); - anything_failed = true; - }, - Ok(Err(err)) => { - match err { - $crate::BenchmarkError::Stop(err) => { - println!( - "{}: {:?}", - $crate::str::from_utf8(benchmark_name) - .expect("benchmark name is always a valid string!"), - err, - ); - anything_failed = true; - }, - $crate::BenchmarkError::Override(_) => { - // This is still considered a success condition. - $crate::log::error!( - "WARNING: benchmark error overrided - {}", - $crate::str::from_utf8(benchmark_name) - .expect("benchmark name is always a valid string!"), - ); - }, - $crate::BenchmarkError::Skip => { - // This is considered a success condition. - $crate::log::error!( - "WARNING: benchmark error skipped - {}", - $crate::str::from_utf8(benchmark_name) - .expect("benchmark name is always a valid string!"), - ); - } - $crate::BenchmarkError::Weightless => { - // This is considered a success condition. - $crate::log::error!( - "WARNING: benchmark weightless skipped - {}", - $crate::str::from_utf8(benchmark_name) - .expect("benchmark name is always a valid string!"), - ); - } - } - }, - Ok(Ok(())) => (), - } - } - assert!(!anything_failed); - }); - } - } - }; -} - -/// show error message and debugging info for the case of an error happening -/// during a benchmark -pub fn show_benchmark_debug_info( - instance_string: &[u8], - benchmark: &[u8], - components: &[(BenchmarkParameter, u32)], - verify: &bool, - error_message: &str, -) -> sp_runtime::RuntimeString { - sp_runtime::format_runtime_string!( - "\n* Pallet: {}\n\ - * Benchmark: {}\n\ - * Components: {:?}\n\ - * Verify: {:?}\n\ - * Error message: {}", - sp_std::str::from_utf8(instance_string) - .expect("it's all just strings ran through the wasm interface. qed"), - sp_std::str::from_utf8(benchmark) - .expect("it's all just strings ran through the wasm interface. qed"), - components, - verify, - error_message, - ) -} - -/// This macro adds pallet benchmarks to a `Vec` object. -/// -/// First create an object that holds in the input parameters for the benchmark: +/// As a short-hand, you may substitute the name of the extrinsic call with `_`, such as the +/// following: /// /// ```ignore -/// let params = (&config, &whitelist); +/// #[extrinsic_call] +/// _(RawOrigin::Signed(whitelisted_caller()), 0u32.into(), 0); /// ``` /// -/// The `whitelist` is a parameter you pass to control the DB read/write tracking. -/// We use a vector of [TrackedStorageKey](./struct.TrackedStorageKey.html), which is a simple -/// struct used to set if a key has been read or written to. +/// The underscore will be substituted with the name of the benchmark (i.e. the name of the +/// function in the benchmark function definition). /// -/// For values that should be skipped entirely, we can just pass `key.into()`. For example: +/// Regardless of whether `#[extrinsic_call]` or `#[block]` is used, this attribute also serves +/// the purpose of designating the boundary between the setup code portion of the benchmark +/// (everything before the `#[extrinsic_call]` or `#[block]` attribute) and the verification +/// stage (everything after the item that the `#[extrinsic_call]` or `#[block]` attribute is +/// attached to). The setup code section should contain any code that needs to execute before +/// the measured portion of the benchmark executes. The verification section is where you can +/// perform assertions to verify that the extrinsic call (or whatever is happening in your +/// block, if you used the `#[block]` syntax) executed successfully. /// -/// ``` -/// use frame_benchmarking::TrackedStorageKey; -/// let whitelist: Vec = vec![ -/// // Block Number -/// array_bytes::hex_into_unchecked("26aa394eea5630e07c48ae0c9558cef702a5c1b19ab7a04f536c519aca4983ac"), -/// // Total Issuance -/// array_bytes::hex_into_unchecked("c2261276cc9d1f8598ea4b6a74b15c2f57c875e4cff74148e4628f264b974c80"), -/// // Execution Phase -/// array_bytes::hex_into_unchecked("26aa394eea5630e07c48ae0c9558cef7ff553b5a9862a516939d82b3d3d8661a"), -/// // Event Count -/// array_bytes::hex_into_unchecked("26aa394eea5630e07c48ae0c9558cef70a98fdbe9ce6c55837576c60c7af3850"), -/// ]; -/// ``` +/// Note that neither `#[extrinsic_call]` nor `#[block]` are real attribute macros and are +/// instead consumed by the outer macro pattern as part of the enclosing benchmark function +/// definition. This is why we are able to use `#[extrinsic_call]` and `#[block]` within a +/// function definition even though this behavior has not been stabilized +/// yet—`#[extrinsic_call]` and `#[block]` are parsed and consumed as part of the benchmark +/// definition parsing code, so they never expand as their own attribute macros. /// -/// Then define a mutable local variable to hold your `BenchmarkBatch` object: +/// ### Optional Attributes /// -/// ```ignore -/// let mut batches = Vec::::new(); -/// ```` +/// The keywords `extra` and `skip_meta` can be provided as optional arguments to the +/// `#[benchmark]` attribute, i.e. `#[benchmark(extra, skip_meta)]`. Including either of these +/// will enable the `extra` or `skip_meta` option, respectively. These options enable the same +/// behavior they did in the old benchmarking syntax in `frame_benchmarking`, namely: /// -/// Then add the pallets you want to benchmark to this object, using their crate name and generated -/// module struct: +/// #### `extra` /// -/// ```ignore -/// add_benchmark!(params, batches, pallet_balances, Balances); -/// add_benchmark!(params, batches, pallet_session, SessionBench::); -/// add_benchmark!(params, batches, frame_system, SystemBench::); -/// ... -/// ``` +/// Specifies that this benchmark should not normally run. To run benchmarks marked with +/// `extra`, you will need to invoke the `frame-benchmarking-cli` with `--extra`. /// -/// At the end of `dispatch_benchmark`, you should return this batches object. +/// #### `skip_meta` /// -/// In the case where you have multiple instances of a pallet that you need to separately benchmark, -/// the name of your module struct will be used as a suffix to your outputted weight file. For -/// example: +/// Specifies that the benchmarking framework should not analyze the storage keys that +/// benchmarked code read or wrote. This useful to suppress the prints in the form of unknown +/// 0x… in case a storage key that does not have metadata. Note that this skips the analysis +/// of all accesses, not just ones without metadata. /// -/// ```ignore -/// add_benchmark!(params, batches, pallet_balances, Balances); // pallet_balances.rs -/// add_benchmark!(params, batches, pallet_collective, Council); // pallet_collective_council.rs -/// add_benchmark!(params, batches, pallet_collective, TechnicalCommittee); // pallet_collective_technical_committee.rs -/// ``` +/// ## Where Clause /// -/// You can manipulate this suffixed string by using a type alias if needed. For example: +/// Some pallets require a where clause specifying constraints on their generics to make +/// writing benchmarks feasible. To accomodate this situation, you can provide such a where +/// clause as the (only) argument to the `#[benchmarks]` or `#[instance_benchmarks]` attribute +/// macros. Below is an example of this taken from the `message-queue` pallet. /// /// ```ignore -/// type Council2 = TechnicalCommittee; -/// add_benchmark!(params, batches, pallet_collective, Council2); // pallet_collective_council_2.rs +/// #[benchmarks( +/// where +/// <::MessageProcessor as ProcessMessage>::Origin: From + PartialEq, +/// ::Size: From, +/// )] +/// mod benchmarks { +/// use super::*; +/// // ... +/// } /// ``` -#[macro_export] -macro_rules! add_benchmark { - ( $params:ident, $batches:ident, $name:path, $( $location:tt )* ) => ( - let name_string = stringify!($name).as_bytes(); - let instance_string = stringify!( $( $location )* ).as_bytes(); - let (config, whitelist) = $params; - let $crate::BenchmarkConfig { - pallet, - benchmark, - selected_components, - verify, - internal_repeats, - } = config; - if &pallet[..] == &name_string[..] { - let benchmark_result = $( $location )*::run_benchmark( - &benchmark[..], - &selected_components[..], - whitelist, - *verify, - *internal_repeats, - ); - - let final_results = match benchmark_result { - Ok(results) => Some(results), - Err($crate::BenchmarkError::Override(mut result)) => { - // Insert override warning as the first storage key. - $crate::log::error!( - "WARNING: benchmark error overrided - {}", - $crate::str::from_utf8(benchmark) - .expect("benchmark name is always a valid string!") - ); - result.keys.insert(0, - (b"Benchmark Override".to_vec(), 0, 0, false) - ); - Some($crate::vec![result]) - }, - Err($crate::BenchmarkError::Stop(e)) => { - $crate::show_benchmark_debug_info( - instance_string, - benchmark, - selected_components, - verify, - e, - ); - return Err(e.into()); - }, - Err($crate::BenchmarkError::Skip) => { - $crate::log::error!( - "WARNING: benchmark error skipped - {}", - $crate::str::from_utf8(benchmark) - .expect("benchmark name is always a valid string!") - ); - None - }, - Err($crate::BenchmarkError::Weightless) => { - $crate::log::error!( - "WARNING: benchmark weightless skipped - {}", - $crate::str::from_utf8(benchmark) - .expect("benchmark name is always a valid string!") - ); - Some(vec![$crate::BenchmarkResult { - components: selected_components.clone(), - .. Default::default() - }]) - } - }; - - if let Some(final_results) = final_results { - $batches.push($crate::BenchmarkBatch { - pallet: name_string.to_vec(), - instance: instance_string.to_vec(), - benchmark: benchmark.clone(), - results: final_results, - }); - } - } - ) -} - -/// Callback for `define_benchmarks` to call `add_benchmark`. -#[macro_export] -macro_rules! cb_add_benchmarks { - // anchor - ( $params:ident, $batches:ident, [ $name:path, $( $location:tt )* ] ) => { - $crate::add_benchmark!( $params, $batches, $name, $( $location )* ); - }; - // recursion tail - ( $params:ident, $batches:ident, [ $name:path, $( $location:tt )* ] $([ $names:path, $( $locations:tt )* ])+ ) => { - $crate::cb_add_benchmarks!( $params, $batches, [ $name, $( $location )* ] ); - $crate::cb_add_benchmarks!( $params, $batches, $([ $names, $( $locations )* ])+ ); - } -} - -/// This macro allows users to easily generate a list of benchmarks for the pallets configured -/// in the runtime. -/// -/// To use this macro, first create a an object to store the list: /// -/// ```ignore -/// let mut list = Vec::::new(); -/// ``` +/// ## Benchmark Tests /// -/// Then pass this `list` to the macro, along with the `extra` boolean, the pallet crate, and -/// pallet struct: +/// Benchmark tests can be generated using the old syntax in `frame_benchmarking`, +/// including the `frame_benchmarking::impl_benchmark_test_suite` macro. /// +/// An example is shown below (taken from the `message-queue` pallet's `benchmarking` module): /// ```ignore -/// list_benchmark!(list, extra, pallet_balances, Balances); -/// list_benchmark!(list, extra, pallet_session, SessionBench::); -/// list_benchmark!(list, extra, frame_system, SystemBench::); +/// #[benchmarks] +/// mod benchmarks { +/// use super::*; +/// // ... +/// impl_benchmark_test_suite!( +/// MessageQueue, +/// crate::mock::new_test_ext::(), +/// crate::integration_test::Test +/// ); +/// } /// ``` -/// -/// This should match what exists with the `add_benchmark!` macro. -#[macro_export] -macro_rules! list_benchmark { - ( $list:ident, $extra:ident, $name:path, $( $location:tt )* ) => ( - let pallet_string = stringify!($name).as_bytes(); - let instance_string = stringify!( $( $location )* ).as_bytes(); - let benchmarks = $( $location )*::benchmarks($extra); - let pallet_benchmarks = BenchmarkList { - pallet: pallet_string.to_vec(), - instance: instance_string.to_vec(), - benchmarks: benchmarks.to_vec(), - }; - $list.push(pallet_benchmarks) - ) -} - -/// Callback for `define_benchmarks` to call `list_benchmark`. -#[macro_export] -macro_rules! cb_list_benchmarks { - // anchor - ( $list:ident, $extra:ident, [ $name:path, $( $location:tt )* ] ) => { - $crate::list_benchmark!( $list, $extra, $name, $( $location )* ); - }; - // recursion tail - ( $list:ident, $extra:ident, [ $name:path, $( $location:tt )* ] $([ $names:path, $( $locations:tt )* ])+ ) => { - $crate::cb_list_benchmarks!( $list, $extra, [ $name, $( $location )* ] ); - $crate::cb_list_benchmarks!( $list, $extra, $([ $names, $( $locations )* ])+ ); +pub mod v2 { + pub use super::*; + pub use frame_support_procedural::{ + benchmark, benchmarks, block, extrinsic_call, instance_benchmarks, + }; + + // Used in #[benchmark] implementation to ensure that benchmark function arguments + // implement [`ParamRange`]. + #[doc(hidden)] + pub use static_assertions::assert_impl_all; + + /// Used by the new benchmarking code to specify that a benchmarking variable is linear + /// over some specified range, i.e. `Linear<0, 1_000>` means that the corresponding variable + /// is allowed to range from `0` to `1000`, inclusive. + /// + /// See [`v2`] for more info. + pub struct Linear; + + /// Trait that must be implemented by all structs that can be used as parameter range types + /// in the new benchmarking code (i.e. `Linear<0, 1_000>`). Right now there is just + /// [`Linear`] but this could later be extended to support additional non-linear parameter + /// ranges. + /// + /// See [`v2`] for more info. + pub trait ParamRange { + /// Represents the (inclusive) starting number of this `ParamRange`. + fn start(&self) -> u32; + + /// Represents the (inclusive) ending number of this `ParamRange`. + fn end(&self) -> u32; } -} -/// Defines pallet configs that `add_benchmarks` and `list_benchmarks` use. -/// Should be preferred instead of having a repetitive list of configs -/// in `add_benchmark` and `list_benchmark`. -#[macro_export] -macro_rules! define_benchmarks { - ( $([ $names:path, $( $locations:tt )* ])* ) => { - /// Calls `list_benchmark` with all configs from `define_benchmarks` - /// and passes the first two parameters on. - /// - /// Use as: - /// ```ignore - /// list_benchmarks!(list, extra); - /// ``` - #[macro_export] - macro_rules! list_benchmarks { - ( $list:ident, $extra:ident ) => { - $crate::cb_list_benchmarks!( $list, $extra, $([ $names, $( $locations )* ])+ ); - } + impl ParamRange for Linear { + fn start(&self) -> u32 { + A } - /// Calls `add_benchmark` with all configs from `define_benchmarks` - /// and passes the first two parameters on. - /// - /// Use as: - /// ```ignore - /// add_benchmarks!(params, batches); - /// ``` - #[macro_export] - macro_rules! add_benchmarks { - ( $params:ident, $batches:ident ) => { - $crate::cb_add_benchmarks!( $params, $batches, $([ $names, $( $locations )* ])+ ); - } + fn end(&self) -> u32 { + B } } } diff --git a/frame/benchmarking/src/v1.rs b/frame/benchmarking/src/v1.rs new file mode 100644 index 000000000..089083f85 --- /dev/null +++ b/frame/benchmarking/src/v1.rs @@ -0,0 +1,2043 @@ +// This file is part of Substrate. + +// Copyright (C) 2020-2023 Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +//! Macros for benchmarking a FRAME runtime. + +pub use super::*; + +/// Whitelist the given account. +#[macro_export] +macro_rules! whitelist { + ($acc:ident) => { + frame_benchmarking::benchmarking::add_to_whitelist( + frame_system::Account::::hashed_key_for(&$acc).into(), + ); + }; +} + +/// Construct pallet benchmarks for weighing dispatchables. +/// +/// Works around the idea of complexity parameters, named by a single letter (which is usually +/// upper cased in complexity notation but is lower-cased for use in this macro). +/// +/// Complexity parameters ("parameters") have a range which is a `u32` pair. Every time a benchmark +/// is prepared and run, this parameter takes a concrete value within the range. There is an +/// associated instancing block, which is a single expression that is evaluated during +/// preparation. It may use `?` (`i.e. `return Err(...)`) to bail with a string error. Here's a +/// few examples: +/// +/// ```ignore +/// // These two are equivalent: +/// let x in 0 .. 10; +/// let x in 0 .. 10 => (); +/// // This one calls a setup function and might return an error (which would be terminal). +/// let y in 0 .. 10 => setup(y)?; +/// // This one uses a code block to do lots of stuff: +/// let z in 0 .. 10 => { +/// let a = z * z / 5; +/// let b = do_something(a)?; +/// combine_into(z, b); +/// } +/// ``` +/// +/// Note that due to parsing restrictions, if the `from` expression is not a single token (i.e. a +/// literal or constant), then it must be parenthesized. +/// +/// The macro allows for a number of "arms", each representing an individual benchmark. Using the +/// simple syntax, the associated dispatchable function maps 1:1 with the benchmark and the name of +/// the benchmark is the same as that of the associated function. However, extended syntax allows +/// for arbitrary expressions to be evaluated in a benchmark (including for example, +/// `on_initialize`). +/// +/// Note that the ranges are *inclusive* on both sides. This is in contrast to ranges in Rust which +/// are left-inclusive right-exclusive. +/// +/// Each arm may also have a block of code which is run prior to any instancing and a block of code +/// which is run afterwards. All code blocks may draw upon the specific value of each parameter +/// at any time. Local variables are shared between the two pre- and post- code blocks, but do not +/// leak from the interior of any instancing expressions. +/// +/// Example: +/// ```ignore +/// benchmarks! { +/// where_clause { where T::A: From } // Optional line to give additional bound on `T`. +/// +/// // first dispatchable: foo; this is a user dispatchable and operates on a `u8` vector of +/// // size `l` +/// foo { +/// let caller = account::(b"caller", 0, benchmarks_seed); +/// let l in 1 .. MAX_LENGTH => initialize_l(l); +/// }: _(RuntimeOrigin::Signed(caller), vec![0u8; l]) +/// +/// // second dispatchable: bar; this is a root dispatchable and accepts a `u8` vector of size +/// // `l`. +/// // In this case, we explicitly name the call using `bar` instead of `_`. +/// bar { +/// let l in 1 .. MAX_LENGTH => initialize_l(l); +/// }: bar(RuntimeOrigin::Root, vec![0u8; l]) +/// +/// // third dispatchable: baz; this is a user dispatchable. It isn't dependent on length like the +/// // other two but has its own complexity `c` that needs setting up. It uses `caller` (in the +/// // pre-instancing block) within the code block. This is only allowed in the param instancers +/// // of arms. +/// baz1 { +/// let caller = account::(b"caller", 0, benchmarks_seed); +/// let c = 0 .. 10 => setup_c(&caller, c); +/// }: baz(RuntimeOrigin::Signed(caller)) +/// +/// // this is a second benchmark of the baz dispatchable with a different setup. +/// baz2 { +/// let caller = account::(b"caller", 0, benchmarks_seed); +/// let c = 0 .. 10 => setup_c_in_some_other_way(&caller, c); +/// }: baz(RuntimeOrigin::Signed(caller)) +/// +/// // You may optionally specify the origin type if it can't be determined automatically like +/// // this. +/// baz3 { +/// let caller = account::(b"caller", 0, benchmarks_seed); +/// let l in 1 .. MAX_LENGTH => initialize_l(l); +/// }: baz(RuntimeOrigin::Signed(caller), vec![0u8; l]) +/// +/// // this is benchmarking some code that is not a dispatchable. +/// populate_a_set { +/// let x in 0 .. 10_000; +/// let mut m = Vec::::new(); +/// for i in 0..x { +/// m.insert(i); +/// } +/// }: { m.into_iter().collect::() } +/// } +/// ``` +/// +/// Test functions are automatically generated for each benchmark and are accessible to you when you +/// run `cargo test`. All tests are named `test_benchmark_`, implemented on the +/// Pallet struct, and run them in a test externalities environment. The test function runs your +/// benchmark just like a regular benchmark, but only testing at the lowest and highest values for +/// each component. The function will return `Ok(())` if the benchmarks return no errors. +/// +/// It is also possible to generate one #[test] function per benchmark by calling the +/// `impl_benchmark_test_suite` macro inside the `benchmarks` block. The functions will be named +/// `bench_` and can be run via `cargo test`. +/// You will see one line of output per benchmark. This approach will give you more understandable +/// error messages and allows for parallel benchmark execution. +/// +/// You can optionally add a `verify` code block at the end of a benchmark to test any final state +/// of your benchmark in a unit test. For example: +/// +/// ```ignore +/// sort_vector { +/// let x in 1 .. 10000; +/// let mut m = Vec::::new(); +/// for i in (0..x).rev() { +/// m.push(i); +/// } +/// }: { +/// m.sort(); +/// } verify { +/// ensure!(m[0] == 0, "You forgot to sort!") +/// } +/// ``` +/// +/// These `verify` blocks will not affect your benchmark results! +/// +/// You can construct benchmark by using the `impl_benchmark_test_suite` macro or +/// by manually implementing them like so: +/// +/// ```ignore +/// #[test] +/// fn test_benchmarks() { +/// new_test_ext().execute_with(|| { +/// assert_ok!(Pallet::::test_benchmark_dummy()); +/// assert_err!(Pallet::::test_benchmark_other_name(), "Bad origin"); +/// assert_ok!(Pallet::::test_benchmark_sort_vector()); +/// assert_err!(Pallet::::test_benchmark_broken_benchmark(), "You forgot to sort!"); +/// }); +/// } +/// ``` +#[macro_export] +macro_rules! benchmarks { + ( + $( $rest:tt )* + ) => { + $crate::benchmarks_iter!( + { } + { } + { } + ( ) + ( ) + ( ) + ( ) + $( $rest )* + ); + } +} + +/// Same as [`benchmarks`] but for instantiable module. +/// +/// NOTE: For pallet declared with [`frame_support::pallet`], use [`benchmarks_instance_pallet`]. +#[macro_export] +macro_rules! benchmarks_instance { + ( + $( $rest:tt )* + ) => { + $crate::benchmarks_iter!( + { } + { I: Instance } + { } + ( ) + ( ) + ( ) + ( ) + $( $rest )* + ); + } +} + +/// Same as [`benchmarks`] but for instantiable pallet declared [`frame_support::pallet`]. +/// +/// NOTE: For pallet declared with `decl_module!`, use [`benchmarks_instance`]. +#[macro_export] +macro_rules! benchmarks_instance_pallet { + ( + $( $rest:tt )* + ) => { + $crate::benchmarks_iter!( + { } + { I: 'static } + { } + ( ) + ( ) + ( ) + ( ) + $( $rest )* + ); + } +} + +#[macro_export] +#[doc(hidden)] +macro_rules! benchmarks_iter { + // detect and extract `impl_benchmark_test_suite` call: + // - with a semi-colon + ( + { } + { $( $instance:ident: $instance_bound:tt )? } + { $( $where_clause:tt )* } + ( $( $names:tt )* ) + ( $( $names_extra:tt )* ) + ( $( $names_skip_meta:tt )* ) + ( $( $pov_name:ident: $( $storage:path = $pov_mode:ident )*; )* ) + impl_benchmark_test_suite!( + $bench_module:ident, + $new_test_ext:expr, + $test:path + $(, $( $args:tt )* )?); + $( $rest:tt )* + ) => { + $crate::benchmarks_iter! { + { $bench_module, $new_test_ext, $test $(, $( $args )* )? } + { $( $instance: $instance_bound )? } + { $( $where_clause )* } + ( $( $names )* ) + ( $( $names_extra )* ) + ( $( $names_skip_meta )* ) + ( $( $pov_name: $( $storage = $pov_mode )*; )* ) + $( $rest )* + } + }; + // - without a semicolon + ( + { } + { $( $instance:ident: $instance_bound:tt )? } + { $( $where_clause:tt )* } + ( $( $names:tt )* ) + ( $( $names_extra:tt )* ) + ( $( $names_skip_meta:tt )* ) + ( $( $pov_name:ident: $( $storage:path = $pov_mode:ident )*; )* ) + impl_benchmark_test_suite!( + $bench_module:ident, + $new_test_ext:expr, + $test:path + $(, $( $args:tt )* )?) + $( $rest:tt )* + ) => { + $crate::benchmarks_iter! { + { $bench_module, $new_test_ext, $test $(, $( $args )* )? } + { $( $instance: $instance_bound )? } + { $( $where_clause )* } + ( $( $names )* ) + ( $( $names_extra )* ) + ( $( $names_skip_meta )* ) + ( $( $pov_name: $( $storage = $pov_mode )*; )* ) + $( $rest )* + } + }; + // detect and extract where clause: + ( + { $($bench_module:ident, $new_test_ext:expr, $test:path $(, $( $args:tt )* )?)? } + { $( $instance:ident: $instance_bound:tt )? } + { $( $where_clause:tt )* } + ( $( $names:tt )* ) + ( $( $names_extra:tt )* ) + ( $( $names_skip_meta:tt )* ) + ( $( $pov_name:ident: $( $storage:path = $pov_mode:ident )*; )* ) + where_clause { where $( $where_bound:tt )* } + $( $rest:tt )* + ) => { + $crate::benchmarks_iter! { + { $($bench_module, $new_test_ext, $test $(, $( $args )* )?)? } + { $( $instance: $instance_bound)? } + { $( $where_bound )* } + ( $( $names )* ) + ( $( $names_extra )* ) + ( $( $names_skip_meta )* ) + ( $( $pov_name: $( $storage = $pov_mode )*; )* ) + $( $rest )* + } + }; + // detect and extract `#[skip_meta]` tag: + ( + { $($bench_module:ident, $new_test_ext:expr, $test:path $(, $( $args:tt )* )?)? } + { $( $instance:ident: $instance_bound:tt )? } + { $( $where_clause:tt )* } + ( $( $names:tt )* ) + ( $( $names_extra:tt )* ) + ( $( $names_skip_meta:tt )* ) + ( $( $pov_name:ident: $( $storage:path = $pov_mode:ident )*; )* ) + #[skip_meta] + $( #[ $($attributes:tt)+ ] )* + $name:ident + $( $rest:tt )* + ) => { + $crate::benchmarks_iter! { + { $($bench_module, $new_test_ext, $test $(, $( $args )* )?)? } + { $( $instance: $instance_bound )? } + { $( $where_clause )* } + ( $( $names )* ) + ( $( $names_extra )* ) + ( $( $names_skip_meta )* $name ) + ( $( $pov_name: $( $storage = $pov_mode )*; )* ) + $( #[ $( $attributes )+ ] )* + $name + $( $rest )* + } + }; + // detect and extract `#[extra]` tag: + ( + { $($bench_module:ident, $new_test_ext:expr, $test:path $(, $( $args:tt )* )?)? } + { $( $instance:ident: $instance_bound:tt )? } + { $( $where_clause:tt )* } + ( $( $names:tt )* ) + ( $( $names_extra:tt )* ) + ( $( $names_skip_meta:tt )* ) + ( $( $pov_name:ident: $( $storage:path = $pov_mode:ident )*; )* ) + #[extra] + $( #[ $($attributes:tt)+ ] )* + $name:ident + $( $rest:tt )* + ) => { + $crate::benchmarks_iter! { + { $($bench_module, $new_test_ext, $test $(, $( $args )* )?)? } + { $( $instance: $instance_bound )? } + { $( $where_clause )* } + ( $( $names )* ) + ( $( $names_extra )* $name ) + ( $( $names_skip_meta )* ) + ( $( $pov_name: $( $storage = $pov_mode )*; )* ) + $( #[ $( $attributes )+ ] )* + $name + $( $rest )* + } + }; + // detect and extract `#[pov_mode = Mode { Pallet::Storage: Mode ... }]` tag: + ( + { $($bench_module:ident, $new_test_ext:expr, $test:path $(, $( $args:tt )* )?)? } + { $( $instance:ident: $instance_bound:tt )? } + { $( $where_clause:tt )* } + ( $( $names:tt )* ) + ( $( $names_extra:tt )* ) + ( $( $names_skip_meta:tt )* ) + ( $( $old_pov_name:ident: $( $old_storage:path = $old_pov_mode:ident )*; )* ) + #[pov_mode = $mode:ident $( { $( $storage:path: $pov_mode:ident )* } )?] + $( #[ $($attributes:tt)+ ] )* + $name:ident + $( $rest:tt )* + ) => { + $crate::benchmarks_iter! { + { $($bench_module, $new_test_ext, $test $(, $( $args )* )?)? } + { $( $instance: $instance_bound )? } + { $( $where_clause )* } + ( $( $names )* ) + ( $( $names_extra )* ) + ( $( $names_skip_meta )* ) + ( $name: ALL = $mode $($( $storage = $pov_mode )*)?; $( $old_pov_name: $( $old_storage = $old_pov_mode )*; )* ) + $( #[ $( $attributes )+ ] )* + $name + $( $rest )* + } + }; + // mutation arm: + ( + { $($bench_module:ident, $new_test_ext:expr, $test:path $(, $( $args:tt )* )?)? } + { $( $instance:ident: $instance_bound:tt )? } + { $( $where_clause:tt )* } + ( $( $names:tt )* ) // This contains $( $( { $instance } )? $name:ident )* + ( $( $names_extra:tt )* ) + ( $( $names_skip_meta:tt )* ) + ( $( $pov_name:ident: $( $storage:path = $pov_mode:ident )*; )* ) + $name:ident { $( $code:tt )* }: _ $(< $origin_type:ty>)? ( $origin:expr $( , $arg:expr )* ) + verify $postcode:block + $( $rest:tt )* + ) => { + $crate::benchmarks_iter! { + { $($bench_module, $new_test_ext, $test $(, $( $args )* )?)? } + { $( $instance: $instance_bound )? } + { $( $where_clause )* } + ( $( $names )* ) + ( $( $names_extra )* ) + ( $( $names_skip_meta )* ) + ( $( $pov_name: $( $storage = $pov_mode )*; )* ) + $name { $( $code )* }: $name $(< $origin_type >)? ( $origin $( , $arg )* ) + verify $postcode + $( $rest )* + } + }; + // mutation arm: + ( + { $($bench_module:ident, $new_test_ext:expr, $test:path $(, $( $args:tt )* )?)? } + { $( $instance:ident: $instance_bound:tt )? } + { $( $where_clause:tt )* } + ( $( $names:tt )* ) + ( $( $names_extra:tt )* ) + ( $( $names_skip_meta:tt )* ) + ( $( $pov_name:ident: $( $storage:path = $pov_mode:ident )*; )* ) + $name:ident { $( $code:tt )* }: $dispatch:ident $(<$origin_type:ty>)? ( $origin:expr $( , $arg:expr )* ) + verify $postcode:block + $( $rest:tt )* + ) => { + $crate::paste::paste! { + $crate::benchmarks_iter! { + { $($bench_module, $new_test_ext, $test $(, $( $args )* )?)? } + { $( $instance: $instance_bound )? } + { $( $where_clause )* } + ( $( $names )* ) + ( $( $names_extra )* ) + ( $( $names_skip_meta )* ) + ( $( $pov_name: $( $storage = $pov_mode )*; )* ) + $name { + $( $code )* + let __call = Call::< + T + $( , $instance )? + >:: [< new_call_variant_ $dispatch >] ( + $($arg),* + ); + let __benchmarked_call_encoded = $crate::frame_support::codec::Encode::encode( + &__call + ); + }: { + let __call_decoded = < + Call + as $crate::frame_support::codec::Decode + >::decode(&mut &__benchmarked_call_encoded[..]) + .expect("call is encoded above, encoding must be correct"); + let __origin = $crate::to_origin!($origin $(, $origin_type)?); + as $crate::frame_support::traits::UnfilteredDispatchable + >::dispatch_bypass_filter(__call_decoded, __origin)?; + } + verify $postcode + $( $rest )* + } + } + }; + // iteration arm: + ( + { $($bench_module:ident, $new_test_ext:expr, $test:path $(, $( $args:tt )* )?)? } + { $( $instance:ident: $instance_bound:tt )? } + { $( $where_clause:tt )* } + ( $( $names:tt )* ) + ( $( $names_extra:tt )* ) + ( $( $names_skip_meta:tt )* ) + ( $( $pov_name:ident: $( $storage:path = $pov_mode:ident )*; )* ) + $name:ident { $( $code:tt )* }: $eval:block + verify $postcode:block + $( $rest:tt )* + ) => { + $crate::benchmark_backend! { + { $( $instance: $instance_bound )? } + $name + { $( $where_clause )* } + { } + { $eval } + { $( $code )* } + $postcode + } + + #[cfg(test)] + $crate::impl_benchmark_test!( + { $( $where_clause )* } + { $( $instance: $instance_bound )? } + $name + ); + + $crate::benchmarks_iter!( + { $($bench_module, $new_test_ext, $test $(, $( $args )* )?)? } + { $( $instance: $instance_bound )? } + { $( $where_clause )* } + ( $( $names )* { $( $instance )? } $name ) + ( $( $names_extra )* ) + ( $( $names_skip_meta )* ) + ( $( $pov_name: $( $storage = $pov_mode )*; )* ) + $( $rest )* + ); + }; + // iteration-exit arm which generates a #[test] function for each case. + ( + { $bench_module:ident, $new_test_ext:expr, $test:path $(, $( $args:tt )* )? } + { $( $instance:ident: $instance_bound:tt )? } + { $( $where_clause:tt )* } + ( $( $names:tt )* ) + ( $( $names_extra:tt )* ) + ( $( $names_skip_meta:tt )* ) + ( $( $pov_name:ident: $( $storage:path = $pov_mode:ident )*; )* ) + ) => { + $crate::selected_benchmark!( + { $( $where_clause)* } + { $( $instance: $instance_bound )? } + $( $names )* + ); + $crate::impl_benchmark!( + { $( $where_clause )* } + { $( $instance: $instance_bound )? } + ( $( $names )* ) + ( $( $names_extra ),* ) + ( $( $names_skip_meta ),* ) + ( $( $pov_name: $( $storage = $pov_mode )*; )* ) + ); + $crate::impl_test_function!( + ( $( $names )* ) + ( $( $names_extra )* ) + ( $( $names_skip_meta )* ) + $bench_module, + $new_test_ext, + $test + $(, $( $args )* )? + ); + }; + // iteration-exit arm which doesn't generate a #[test] function for all cases. + ( + { } + { $( $instance:ident: $instance_bound:tt )? } + { $( $where_clause:tt )* } + ( $( $names:tt )* ) + ( $( $names_extra:tt )* ) + ( $( $names_skip_meta:tt )* ) + ( $( $pov_name:ident: $( $storage:path = $pov_mode:ident )*; )* ) + ) => { + $crate::selected_benchmark!( + { $( $where_clause)* } + { $( $instance: $instance_bound )? } + $( $names )* + ); + $crate::impl_benchmark!( + { $( $where_clause )* } + { $( $instance: $instance_bound )? } + ( $( $names )* ) + ( $( $names_extra ),* ) + ( $( $names_skip_meta ),* ) + ( $( $pov_name: $( $storage = $pov_mode )*; )* ) + ); + }; + // add verify block to _() format + ( + { $($bench_module:ident, $new_test_ext:expr, $test:path $(, $( $args:tt )* )?)? } + { $( $instance:ident: $instance_bound:tt )? } + { $( $where_clause:tt )* } + ( $( $names:tt )* ) + ( $( $names_extra:tt )* ) + ( $( $names_skip_meta:tt )* ) + ( $( $pov_name:ident: $( $storage:path = $pov_mode:ident )*; )* ) + $name:ident { $( $code:tt )* }: _ $(<$origin_type:ty>)? ( $origin:expr $( , $arg:expr )* ) + $( $rest:tt )* + ) => { + $crate::benchmarks_iter! { + { $($bench_module, $new_test_ext, $test $(, $( $args )* )?)? } + { $( $instance: $instance_bound )? } + { $( $where_clause )* } + ( $( $names )* ) + ( $( $names_extra )* ) + ( $( $names_skip_meta )* ) + ( $( $pov_name: $( $storage = $pov_mode )*; )* ) + $name { $( $code )* }: _ $(<$origin_type>)? ( $origin $( , $arg )* ) + verify { } + $( $rest )* + } + }; + // add verify block to name() format + ( + { $($bench_module:ident, $new_test_ext:expr, $test:path $(, $( $args:tt )* )?)? } + { $( $instance:ident: $instance_bound:tt )? } + { $( $where_clause:tt )* } + ( $( $names:tt )* ) + ( $( $names_extra:tt )* ) + ( $( $names_skip_meta:tt )* ) + ( $( $pov_name:ident: $( $storage:path = $pov_mode:ident )*; )* ) + $name:ident { $( $code:tt )* }: $dispatch:ident $(<$origin_type:ty>)? ( $origin:expr $( , $arg:expr )* ) + $( $rest:tt )* + ) => { + $crate::benchmarks_iter! { + { $($bench_module, $new_test_ext, $test $(, $( $args )* )?)? } + { $( $instance: $instance_bound )? } + { $( $where_clause )* } + ( $( $names )* ) + ( $( $names_extra )* ) + ( $( $names_skip_meta )* ) + ( $( $pov_name: $( $storage = $pov_mode )*; )* ) + $name { $( $code )* }: $dispatch $(<$origin_type>)? ( $origin $( , $arg )* ) + verify { } + $( $rest )* + } + }; + // add verify block to {} format + ( + { $($bench_module:ident, $new_test_ext:expr, $test:path $(, $( $args:tt )* )?)? } + { $( $instance:ident: $instance_bound:tt )? } + { $( $where_clause:tt )* } + ( $( $names:tt )* ) + ( $( $names_extra:tt )* ) + ( $( $names_skip_meta:tt )* ) + ( $( $pov_name:ident: $( $storage:path = $pov_mode:ident )*; )* ) + $name:ident { $( $code:tt )* }: $(<$origin_type:ty>)? $eval:block + $( $rest:tt )* + ) => { + $crate::benchmarks_iter!( + { $($bench_module, $new_test_ext, $test $(, $( $args )* )?)? } + { $( $instance: $instance_bound )? } + { $( $where_clause )* } + ( $( $names )* ) + ( $( $names_extra )* ) + ( $( $names_skip_meta )* ) + ( $( $pov_name: $( $storage = $pov_mode )*; )* ) + $name { $( $code )* }: $(<$origin_type>)? $eval + verify { } + $( $rest )* + ); + }; +} + +#[macro_export] +#[doc(hidden)] +macro_rules! to_origin { + ($origin:expr) => { + $origin.into() + }; + ($origin:expr, $origin_type:ty) => { + <::RuntimeOrigin as From<$origin_type>>::from($origin) + }; +} + +#[macro_export] +#[doc(hidden)] +macro_rules! benchmark_backend { + // parsing arms + ( + { $( $instance:ident: $instance_bound:tt )? } + $name:ident + { $( $where_clause:tt )* } + { $( PRE { $( $pre_parsed:tt )* } )* } + { $eval:block } + { + let $pre_id:tt : $pre_ty:ty = $pre_ex:expr; + $( $rest:tt )* + } + $postcode:block + ) => { + $crate::benchmark_backend! { + { $( $instance: $instance_bound )? } + $name + { $( $where_clause )* } + { + $( PRE { $( $pre_parsed )* } )* + PRE { $pre_id , $pre_ty , $pre_ex } + } + { $eval } + { $( $rest )* } + $postcode + } + }; + ( + { $( $instance:ident: $instance_bound:tt )? } + $name:ident + { $( $where_clause:tt )* } + { $( $parsed:tt )* } + { $eval:block } + { + let $param:ident in ( $param_from:expr ) .. $param_to:expr => $param_instancer:expr; + $( $rest:tt )* + } + $postcode:block + ) => { + $crate::benchmark_backend! { + { $( $instance: $instance_bound )? } + $name + { $( $where_clause )* } + { + $( $parsed )* + PARAM { $param , $param_from , $param_to , $param_instancer } + } + { $eval } + { $( $rest )* } + $postcode + } + }; + // mutation arm to look after a single tt for param_from. + ( + { $( $instance:ident: $instance_bound:tt )? } + $name:ident + { $( $where_clause:tt )* } + { $( $parsed:tt )* } + { $eval:block } + { + let $param:ident in $param_from:tt .. $param_to:expr => $param_instancer:expr ; + $( $rest:tt )* + } + $postcode:block + ) => { + $crate::benchmark_backend! { + { $( $instance: $instance_bound )? } + $name + { $( $where_clause )* } + { $( $parsed )* } + { $eval } + { + let $param in ( $param_from ) .. $param_to => $param_instancer; + $( $rest )* + } + $postcode + } + }; + // mutation arm to look after the default tail of `=> ()` + ( + { $( $instance:ident: $instance_bound:tt )? } + $name:ident + { $( $where_clause:tt )* } + { $( $parsed:tt )* } + { $eval:block } + { + let $param:ident in $param_from:tt .. $param_to:expr; + $( $rest:tt )* + } + $postcode:block + ) => { + $crate::benchmark_backend! { + { $( $instance: $instance_bound )? } + $name + { $( $where_clause )* } + { $( $parsed )* } + { $eval } + { + let $param in $param_from .. $param_to => (); + $( $rest )* + } + $postcode + } + }; + // mutation arm to look after `let _ =` + ( + { $( $instance:ident: $instance_bound:tt )? } + $name:ident + { $( $where_clause:tt )* } + { $( $parsed:tt )* } + { $eval:block } + { + let $pre_id:tt = $pre_ex:expr; + $( $rest:tt )* + } + $postcode:block + ) => { + $crate::benchmark_backend! { + { $( $instance: $instance_bound )? } + $name + { $( $where_clause )* } + { $( $parsed )* } + { $eval } + { + let $pre_id : _ = $pre_ex; + $( $rest )* + } + $postcode + } + }; + // actioning arm + ( + { $( $instance:ident: $instance_bound:tt )? } + $name:ident + { $( $where_clause:tt )* } + { + $( PRE { $pre_id:tt , $pre_ty:ty , $pre_ex:expr } )* + $( PARAM { $param:ident , $param_from:expr , $param_to:expr , $param_instancer:expr } )* + } + { $eval:block } + { $( $post:tt )* } + $postcode:block + ) => { + #[allow(non_camel_case_types)] + struct $name; + #[allow(unused_variables)] + impl, $instance: $instance_bound )? > + $crate::BenchmarkingSetup for $name + where $( $where_clause )* + { + fn components(&self) -> $crate::Vec<($crate::BenchmarkParameter, u32, u32)> { + $crate::vec! [ + $( + ($crate::BenchmarkParameter::$param, $param_from, $param_to) + ),* + ] + } + + fn instance( + &self, + components: &[($crate::BenchmarkParameter, u32)], + verify: bool + ) -> Result<$crate::Box Result<(), $crate::BenchmarkError>>, $crate::BenchmarkError> { + $( + // Prepare instance + let $param = components.iter() + .find(|&c| c.0 == $crate::BenchmarkParameter::$param) + .ok_or("Could not find component in benchmark preparation.")? + .1; + )* + $( + let $pre_id : $pre_ty = $pre_ex; + )* + $( $param_instancer ; )* + $( $post )* + + Ok($crate::Box::new(move || -> Result<(), $crate::BenchmarkError> { + $eval; + if verify { + $postcode; + } + Ok(()) + })) + } + } + }; +} + +// Creates #[test] functions for the given bench cases. +#[macro_export] +#[doc(hidden)] +macro_rules! impl_bench_case_tests { + ( + { $module:ident, $new_test_exec:expr, $exec_name:ident, $test:path, $extra:expr } + { $( $names_extra:tt )* } + $( { $( $bench_inst:ident )? } $bench:ident )* + ) + => { + $crate::impl_bench_name_tests!( + $module, $new_test_exec, $exec_name, $test, $extra, + { $( $names_extra )* }, + $( { $bench } )+ + ); + } +} + +// Creates a #[test] function for the given bench name. +#[macro_export] +#[doc(hidden)] +macro_rules! impl_bench_name_tests { + // recursion anchor + ( + $module:ident, $new_test_exec:expr, $exec_name:ident, $test:path, $extra:expr, + { $( $names_extra:tt )* }, + { $name:ident } + ) => { + $crate::paste::paste! { + #[test] + fn [] () { + $new_test_exec.$exec_name(|| { + // Skip all #[extra] benchmarks if $extra is false. + if !($extra) { + let disabled = $crate::vec![ $( stringify!($names_extra).as_ref() ),* ]; + if disabled.contains(&stringify!($name)) { + $crate::log::error!( + "INFO: extra benchmark skipped - {}", + stringify!($name), + ); + return (); + } + } + + // Same per-case logic as when all cases are run in the + // same function. + match std::panic::catch_unwind(|| { + $module::<$test>::[< test_benchmark_ $name >] () + }) { + Err(err) => { + panic!("{}: {:?}", stringify!($name), err); + }, + Ok(Err(err)) => { + match err { + $crate::BenchmarkError::Stop(err) => { + panic!("{}: {:?}", stringify!($name), err); + }, + $crate::BenchmarkError::Override(_) => { + // This is still considered a success condition. + $crate::log::error!( + "WARNING: benchmark error overrided - {}", + stringify!($name), + ); + }, + $crate::BenchmarkError::Skip => { + // This is considered a success condition. + $crate::log::error!( + "WARNING: benchmark error skipped - {}", + stringify!($name), + ); + }, + $crate::BenchmarkError::Weightless => { + // This is considered a success condition. + $crate::log::error!( + "WARNING: benchmark weightless skipped - {}", + stringify!($name), + ); + } + } + }, + Ok(Ok(())) => (), + } + }); + } + } + }; + // recursion tail + ( + $module:ident, $new_test_exec:expr, $exec_name:ident, $test:path, $extra:expr, + { $( $names_extra:tt )* }, + { $name:ident } $( { $rest:ident } )+ + ) => { + // car + $crate::impl_bench_name_tests!($module, $new_test_exec, $exec_name, $test, $extra, + { $( $names_extra )* }, { $name }); + // cdr + $crate::impl_bench_name_tests!($module, $new_test_exec, $exec_name, $test, $extra, + { $( $names_extra )* }, $( { $rest } )+); + }; +} + +// Creates a `SelectedBenchmark` enum implementing `BenchmarkingSetup`. +// +// Every variant must implement [`BenchmarkingSetup`]. +// +// ```nocompile +// +// struct Transfer; +// impl BenchmarkingSetup for Transfer { ... } +// +// struct SetBalance; +// impl BenchmarkingSetup for SetBalance { ... } +// +// selected_benchmark!({} Transfer {} SetBalance); +// ``` +#[macro_export] +#[doc(hidden)] +macro_rules! selected_benchmark { + ( + { $( $where_clause:tt )* } + { $( $instance:ident: $instance_bound:tt )? } + $( { $( $bench_inst:ident )? } $bench:ident )* + ) => { + // The list of available benchmarks for this pallet. + #[allow(non_camel_case_types)] + enum SelectedBenchmark { + $( $bench, )* + } + + // Allow us to select a benchmark from the list of available benchmarks. + impl, $instance: $instance_bound )? > + $crate::BenchmarkingSetup for SelectedBenchmark + where $( $where_clause )* + { + fn components(&self) -> $crate::Vec<($crate::BenchmarkParameter, u32, u32)> { + match self { + $( + Self::$bench => < + $bench as $crate::BenchmarkingSetup + >::components(&$bench), + )* + } + } + + fn instance( + &self, + components: &[($crate::BenchmarkParameter, u32)], + verify: bool + ) -> Result<$crate::Box Result<(), $crate::BenchmarkError>>, $crate::BenchmarkError> { + match self { + $( + Self::$bench => < + $bench as $crate::BenchmarkingSetup + >::instance(&$bench, components, verify), + )* + } + } + } + }; +} + +#[macro_export] +#[doc(hidden)] +macro_rules! impl_benchmark { + ( + { $( $where_clause:tt )* } + { $( $instance:ident: $instance_bound:tt )? } + ( $( { $( $name_inst:ident )? } $name:ident )* ) + ( $( $name_extra:ident ),* ) + ( $( $name_skip_meta:ident ),* ) + ( $( $pov_name:ident: $( $storage:path = $pov_mode:ident )*; )* ) + ) => { + // We only need to implement benchmarks for the runtime-benchmarks feature or testing. + #[cfg(any(feature = "runtime-benchmarks", test))] + impl, $instance: $instance_bound )? > + $crate::Benchmarking for Pallet + where T: frame_system::Config, $( $where_clause )* + { + fn benchmarks(extra: bool) -> $crate::Vec<$crate::BenchmarkMetadata> { + $($crate::validate_pov_mode!( + $pov_name: $( $storage = $pov_mode )*; + );)* + let mut all_names = $crate::vec![ $( stringify!($name).as_ref() ),* ]; + if !extra { + let extra = [ $( stringify!($name_extra).as_ref() ),* ]; + all_names.retain(|x| !extra.contains(x)); + } + let pov_modes: $crate::Vec<($crate::Vec, $crate::Vec<($crate::Vec, $crate::Vec)>)> = $crate::vec![ + $( + (stringify!($pov_name).as_bytes().to_vec(), + $crate::vec![ + $( ( stringify!($storage).as_bytes().to_vec(), + stringify!($pov_mode).as_bytes().to_vec() ), )* + ]), + )* + ]; + all_names.into_iter().map(|benchmark| { + let selected_benchmark = match benchmark { + $( stringify!($name) => SelectedBenchmark::$name, )* + _ => panic!("all benchmarks should be selectable"), + }; + let name = benchmark.as_bytes().to_vec(); + let components = < + SelectedBenchmark as $crate::BenchmarkingSetup + >::components(&selected_benchmark); + + $crate::BenchmarkMetadata { + name: name.clone(), + components, + pov_modes: pov_modes.iter().find(|p| p.0 == name).map(|p| p.1.clone()).unwrap_or_default(), + } + }).collect::<$crate::Vec<_>>() + } + + fn run_benchmark( + extrinsic: &[u8], + c: &[($crate::BenchmarkParameter, u32)], + whitelist: &[$crate::TrackedStorageKey], + verify: bool, + internal_repeats: u32, + ) -> Result<$crate::Vec<$crate::BenchmarkResult>, $crate::BenchmarkError> { + // Map the input to the selected benchmark. + let extrinsic = $crate::str::from_utf8(extrinsic) + .map_err(|_| "`extrinsic` is not a valid utf8 string!")?; + let selected_benchmark = match extrinsic { + $( stringify!($name) => SelectedBenchmark::$name, )* + _ => return Err("Could not find extrinsic.".into()), + }; + + // Add whitelist to DB including whitelisted caller + let mut whitelist = whitelist.to_vec(); + let whitelisted_caller_key = + as $crate::frame_support::storage::StorageMap<_,_>>::hashed_key_for( + $crate::whitelisted_caller::() + ); + whitelist.push(whitelisted_caller_key.into()); + // Whitelist the transactional layer. + let transactional_layer_key = $crate::TrackedStorageKey::new( + $crate::frame_support::storage::transactional::TRANSACTION_LEVEL_KEY.into() + ); + whitelist.push(transactional_layer_key); + // Whitelist the `:extrinsic_index`. + let extrinsic_index = $crate::TrackedStorageKey::new( + $crate::well_known_keys::EXTRINSIC_INDEX.into() + ); + whitelist.push(extrinsic_index); + + $crate::benchmarking::set_whitelist(whitelist.clone()); + + let mut results: $crate::Vec<$crate::BenchmarkResult> = $crate::Vec::new(); + + // Always do at least one internal repeat... + for _ in 0 .. internal_repeats.max(1) { + // Always reset the state after the benchmark. + $crate::defer!($crate::benchmarking::wipe_db()); + + // Set up the externalities environment for the setup we want to + // benchmark. + let closure_to_benchmark = < + SelectedBenchmark as $crate::BenchmarkingSetup + >::instance(&selected_benchmark, c, verify)?; + + // Set the block number to at least 1 so events are deposited. + if $crate::Zero::is_zero(&frame_system::Pallet::::block_number()) { + frame_system::Pallet::::set_block_number(1u32.into()); + } + + // Commit the externalities to the database, flushing the DB cache. + // This will enable worst case scenario for reading from the database. + $crate::benchmarking::commit_db(); + + // Access all whitelisted keys to get them into the proof recorder since the + // recorder does now have a whitelist. + for key in &whitelist { + $crate::frame_support::storage::unhashed::get_raw(&key.key); + } + + // Reset the read/write counter so we don't count operations in the setup process. + $crate::benchmarking::reset_read_write_count(); + + // Time the extrinsic logic. + $crate::log::trace!( + target: "benchmark", + "Start Benchmark: {} ({:?}) verify {}", + extrinsic, + c, + verify + ); + + let start_pov = $crate::benchmarking::proof_size(); + let start_extrinsic = $crate::benchmarking::current_time(); + + closure_to_benchmark()?; + + let finish_extrinsic = $crate::benchmarking::current_time(); + let end_pov = $crate::benchmarking::proof_size(); + + // Calculate the diff caused by the benchmark. + let elapsed_extrinsic = finish_extrinsic.saturating_sub(start_extrinsic); + let diff_pov = match (start_pov, end_pov) { + (Some(start), Some(end)) => end.saturating_sub(start), + _ => Default::default(), + }; + + // Commit the changes to get proper write count + $crate::benchmarking::commit_db(); + $crate::log::trace!( + target: "benchmark", + "End Benchmark: {} ns", elapsed_extrinsic + ); + let read_write_count = $crate::benchmarking::read_write_count(); + $crate::log::trace!( + target: "benchmark", + "Read/Write Count {:?}", read_write_count + ); + $crate::log::trace!( + target: "benchmark", + "Proof sizes: before {:?} after {:?} diff {}", &start_pov, &end_pov, &diff_pov + ); + + // Time the storage root recalculation. + let start_storage_root = $crate::benchmarking::current_time(); + $crate::storage_root($crate::StateVersion::V1); + let finish_storage_root = $crate::benchmarking::current_time(); + let elapsed_storage_root = finish_storage_root - start_storage_root; + + let skip_meta = [ $( stringify!($name_skip_meta).as_ref() ),* ]; + let read_and_written_keys = if skip_meta.contains(&extrinsic) { + $crate::vec![(b"Skipped Metadata".to_vec(), 0, 0, false)] + } else { + $crate::benchmarking::get_read_and_written_keys() + }; + + results.push($crate::BenchmarkResult { + components: c.to_vec(), + extrinsic_time: elapsed_extrinsic, + storage_root_time: elapsed_storage_root, + reads: read_write_count.0, + repeat_reads: read_write_count.1, + writes: read_write_count.2, + repeat_writes: read_write_count.3, + proof_size: diff_pov, + keys: read_and_written_keys, + }); + } + + return Ok(results); + } + } + + #[cfg(test)] + impl, $instance: $instance_bound )? > + Pallet + where T: frame_system::Config, $( $where_clause )* + { + /// Test a particular benchmark by name. + /// + /// This isn't called `test_benchmark_by_name` just in case some end-user eventually + /// writes a benchmark, itself called `by_name`; the function would be shadowed in + /// that case. + /// + /// This is generally intended to be used by child test modules such as those created + /// by the `impl_benchmark_test_suite` macro. However, it is not an error if a pallet + /// author chooses not to implement benchmarks. + #[allow(unused)] + fn test_bench_by_name(name: &[u8]) -> Result<(), $crate::BenchmarkError> { + let name = $crate::str::from_utf8(name) + .map_err(|_| -> $crate::BenchmarkError { "`name` is not a valid utf8 string!".into() })?; + match name { + $( stringify!($name) => { + $crate::paste::paste! { Self::[< test_benchmark_ $name >]() } + } )* + _ => Err("Could not find test for requested benchmark.".into()), + } + } + } + }; +} + +// This creates a unit test for one benchmark of the main benchmark macro. +// It runs the benchmark using the `high` and `low` value for each component +// and ensure that everything completes successfully. +// Instances each component with six values which can be controlled with the +// env variable `VALUES_PER_COMPONENT`. +#[macro_export] +#[doc(hidden)] +macro_rules! impl_benchmark_test { + ( + { $( $where_clause:tt )* } + { $( $instance:ident: $instance_bound:tt )? } + $name:ident + ) => { + $crate::paste::item! { + #[cfg(test)] + impl, $instance: $instance_bound )? > + Pallet + where T: frame_system::Config, $( $where_clause )* + { + #[allow(unused)] + fn [] () -> Result<(), $crate::BenchmarkError> { + let selected_benchmark = SelectedBenchmark::$name; + let components = < + SelectedBenchmark as $crate::BenchmarkingSetup + >::components(&selected_benchmark); + + let execute_benchmark = | + c: $crate::Vec<($crate::BenchmarkParameter, u32)> + | -> Result<(), $crate::BenchmarkError> { + // Always reset the state after the benchmark. + $crate::defer!($crate::benchmarking::wipe_db()); + + // Set up the benchmark, return execution + verification function. + let closure_to_verify = < + SelectedBenchmark as $crate::BenchmarkingSetup + >::instance(&selected_benchmark, &c, true)?; + + // Set the block number to at least 1 so events are deposited. + if $crate::Zero::is_zero(&frame_system::Pallet::::block_number()) { + frame_system::Pallet::::set_block_number(1u32.into()); + } + + // Run execution + verification + closure_to_verify() + }; + + if components.is_empty() { + execute_benchmark(Default::default())?; + } else { + let num_values: u32 = if let Ok(ev) = std::env::var("VALUES_PER_COMPONENT") { + ev.parse().map_err(|_| { + $crate::BenchmarkError::Stop( + "Could not parse env var `VALUES_PER_COMPONENT` as u32." + ) + })? + } else { + 6 + }; + + if num_values < 2 { + return Err("`VALUES_PER_COMPONENT` must be at least 2".into()); + } + + for (name, low, high) in components.clone().into_iter() { + // Test the lowest, highest (if its different from the lowest) + // and up to num_values-2 more equidistant values in between. + // For 0..10 and num_values=6 this would mean: [0, 2, 4, 6, 8, 10] + + let mut values = $crate::vec![low]; + let diff = (high - low).min(num_values - 1); + let slope = (high - low) as f32 / diff as f32; + + for i in 1..=diff { + let value = ((low as f32 + slope * i as f32) as u32) + .clamp(low, high); + values.push(value); + } + + for component_value in values { + // Select the max value for all the other components. + let c: $crate::Vec<($crate::BenchmarkParameter, u32)> = components + .iter() + .map(|(n, _, h)| + if *n == name { + (*n, component_value) + } else { + (*n, *h) + } + ) + .collect(); + + execute_benchmark(c)?; + } + } + } + Ok(()) + } + } + } + }; +} + +/// This creates a test suite which runs the module's benchmarks. +/// +/// When called in `pallet_example_basic` as +/// +/// ```rust,ignore +/// impl_benchmark_test_suite!(Pallet, crate::tests::new_test_ext(), crate::tests::Test); +/// ``` +/// +/// It expands to the equivalent of: +/// +/// ```rust,ignore +/// #[cfg(test)] +/// mod tests { +/// use super::*; +/// use crate::tests::{new_test_ext, Test}; +/// use frame_support::assert_ok; +/// +/// #[test] +/// fn test_benchmarks() { +/// new_test_ext().execute_with(|| { +/// assert_ok!(test_benchmark_accumulate_dummy::()); +/// assert_ok!(test_benchmark_set_dummy::()); +/// assert_ok!(test_benchmark_sort_vector::()); +/// }); +/// } +/// } +/// ``` +/// +/// When called inside the `benchmarks` macro of the `pallet_example_basic` as +/// +/// ```rust,ignore +/// benchmarks! { +/// // Benchmarks omitted for brevity +/// +/// impl_benchmark_test_suite!(Pallet, crate::tests::new_test_ext(), crate::tests::Test); +/// } +/// ``` +/// +/// It expands to the equivalent of: +/// +/// ```rust,ignore +/// #[cfg(test)] +/// mod benchmarking { +/// use super::*; +/// use crate::tests::{new_test_ext, Test}; +/// use frame_support::assert_ok; +/// +/// #[test] +/// fn bench_accumulate_dummy() { +/// new_test_ext().execute_with(|| { +/// assert_ok!(test_benchmark_accumulate_dummy::()); +/// }) +/// } +/// +/// #[test] +/// fn bench_set_dummy() { +/// new_test_ext().execute_with(|| { +/// assert_ok!(test_benchmark_set_dummy::()); +/// }) +/// } +/// +/// #[test] +/// fn bench_sort_vector() { +/// new_test_ext().execute_with(|| { +/// assert_ok!(test_benchmark_sort_vector::()); +/// }) +/// } +/// } +/// ``` +/// +/// ## Arguments +/// +/// The first argument, `module`, must be the path to this crate's module. +/// +/// The second argument, `new_test_ext`, must be a function call which returns either a +/// `sp_io::TestExternalities`, or some other type with a similar interface. +/// +/// Note that this function call is _not_ evaluated at compile time, but is instead copied textually +/// into each appropriate invocation site. +/// +/// The third argument, `test`, must be the path to the runtime. The item to which this must refer +/// will generally take the form: +/// +/// ```rust,ignore +/// frame_support::construct_runtime!( +/// pub enum Test where ... +/// { ... } +/// ); +/// ``` +/// +/// There is an optional fourth argument, with keyword syntax: `benchmarks_path = +/// path_to_benchmarks_invocation`. In the typical case in which this macro is in the same module as +/// the `benchmarks!` invocation, you don't need to supply this. However, if the +/// `impl_benchmark_test_suite!` invocation is in a different module than the `benchmarks!` +/// invocation, then you should provide the path to the module containing the `benchmarks!` +/// invocation: +/// +/// ```rust,ignore +/// mod benches { +/// benchmarks!{ +/// ... +/// } +/// } +/// +/// mod tests { +/// // because of macro syntax limitations, neither Pallet nor benches can be paths, but both have +/// // to be idents in the scope of `impl_benchmark_test_suite`. +/// use crate::{benches, Pallet}; +/// +/// impl_benchmark_test_suite!(Pallet, new_test_ext(), Test, benchmarks_path = benches); +/// +/// // new_test_ext and the Test item are defined later in this module +/// } +/// ``` +/// +/// There is an optional fifth argument, with keyword syntax: `extra = true` or `extra = false`. +/// By default, this generates a test suite which iterates over all benchmarks, including those +/// marked with the `#[extra]` annotation. Setting `extra = false` excludes those. +/// +/// There is an optional sixth argument, with keyword syntax: `exec_name = custom_exec_name`. +/// By default, this macro uses `execute_with` for this parameter. This argument, if set, is subject +/// to these restrictions: +/// +/// - It must be the name of a method applied to the output of the `new_test_ext` argument. +/// - That method must have a signature capable of receiving a single argument of the form `impl +/// FnOnce()`. +// ## Notes (not for rustdoc) +// +// The biggest challenge for this macro is communicating the actual test functions to be run. We +// can't just build an array of function pointers to each test function and iterate over it, because +// the test functions are parameterized by the `Test` type. That's incompatible with +// monomorphization: if it were legal, then even if the compiler detected and monomorphized the +// functions into only the types of the callers, which implementation would the function pointer +// point to? There would need to be some kind of syntax for selecting the destination of the pointer +// according to a generic argument, and in general it would be a huge mess and not worth it. +// +// Instead, we're going to steal a trick from `fn run_benchmark`: generate a function which is +// itself parametrized by `Test`, which accepts a `&[u8]` parameter containing the name of the +// benchmark, and dispatches based on that to the appropriate real test implementation. Then, we can +// just iterate over the `Benchmarking::benchmarks` list to run the actual implementations. +#[macro_export] +macro_rules! impl_benchmark_test_suite { + ( + $bench_module:ident, + $new_test_ext:expr, + $test:path + $(, $( $rest:tt )* )? + ) => { + $crate::impl_test_function!( + () + () + () + $bench_module, + $new_test_ext, + $test + $(, $( $rest )* )? + ); + } +} + +/// Validates the passed `pov_mode`s. +/// +/// Checks that: +/// - a top-level `ignored` is exclusive +/// - all modes are valid +#[macro_export] +macro_rules! validate_pov_mode { + () => {}; + ( $_bench:ident: ; ) => { }; + ( $_bench:ident: $_car:path = Ignored ; ) => { }; + ( $bench:ident: $_car:path = Ignored $( $storage:path = $_pov_mode:ident )+; ) => { + compile_error!( + concat!(concat!("`pov_mode = Ignored` is exclusive. Please remove the attribute from keys: ", $( stringify!($storage) )+), " on benchmark '", stringify!($bench), "'")); + }; + ( $bench:ident: $car:path = Measured $( $storage:path = $pov_mode:ident )*; ) => { + $crate::validate_pov_mode!( + $bench: $( $storage = $pov_mode )*; + ); + }; + ( $bench:ident: $car:path = MaxEncodedLen $( $storage:path = $pov_mode:ident )*; ) => { + $crate::validate_pov_mode!( + $bench: $( $storage = $pov_mode )*; + ); + }; + ( $bench:ident: $key:path = $unknown:ident $( $_storage:path = $_pov_mode:ident )*; ) => { + compile_error!( + concat!("Unknown pov_mode '", stringify!($unknown) ,"' for benchmark '", stringify!($bench), "' on key '", stringify!($key), "'. Must be one of: Ignored, Measured, MaxEncodedLen") + ); + }; +} + +// Takes all arguments from `impl_benchmark_test_suite` and three additional arguments. +// +// Can be configured to generate one #[test] fn per bench case or +// one #[test] fn for all bench cases. +// This depends on whether or not the first argument contains a non-empty list of bench names. +#[macro_export] +#[doc(hidden)] +macro_rules! impl_test_function { + // user might or might not have set some keyword arguments; set the defaults + // + // The weird syntax indicates that `rest` comes only after a comma, which is otherwise optional + ( + ( $( $names:tt )* ) + ( $( $names_extra:tt )* ) + ( $( $names_skip_meta:tt )* ) + + $bench_module:ident, + $new_test_ext:expr, + $test:path + $(, $( $rest:tt )* )? + ) => { + $crate::impl_test_function!( + @cases: + ( $( $names )* ) + ( $( $names_extra )* ) + ( $( $names_skip_meta )* ) + @selected: + $bench_module, + $new_test_ext, + $test, + benchmarks_path = super, + extra = true, + exec_name = execute_with, + @user: + $( $( $rest )* )? + ); + }; + // pick off the benchmarks_path keyword argument + ( + @cases: + ( $( $names:tt )* ) + ( $( $names_extra:tt )* ) + ( $( $names_skip_meta:tt )* ) + @selected: + $bench_module:ident, + $new_test_ext:expr, + $test:path, + benchmarks_path = $old:ident, + extra = $extra:expr, + exec_name = $exec_name:ident, + @user: + benchmarks_path = $benchmarks_path:ident + $(, $( $rest:tt )* )? + ) => { + $crate::impl_test_function!( + @cases: + ( $( $names )* ) + ( $( $names_extra )* ) + ( $( $names_skip_meta )* ) + @selected: + $bench_module, + $new_test_ext, + $test, + benchmarks_path = $benchmarks_path, + extra = $extra, + exec_name = $exec_name, + @user: + $( $( $rest )* )? + ); + }; + // pick off the extra keyword argument + ( + @cases: + ( $( $names:tt )* ) + ( $( $names_extra:tt )* ) + ( $( $names_skip_meta:tt )* ) + @selected: + $bench_module:ident, + $new_test_ext:expr, + $test:path, + benchmarks_path = $benchmarks_path:ident, + extra = $old:expr, + exec_name = $exec_name:ident, + @user: + extra = $extra:expr + $(, $( $rest:tt )* )? + ) => { + $crate::impl_test_function!( + @cases: + ( $( $names )* ) + ( $( $names_extra )* ) + ( $( $names_skip_meta )* ) + @selected: + $bench_module, + $new_test_ext, + $test, + benchmarks_path = $benchmarks_path, + extra = $extra, + exec_name = $exec_name, + @user: + $( $( $rest )* )? + ); + }; + // pick off the exec_name keyword argument + ( + @cases: + ( $( $names:tt )* ) + ( $( $names_extra:tt )* ) + ( $( $names_skip_meta:tt )* ) + @selected: + $bench_module:ident, + $new_test_ext:expr, + $test:path, + benchmarks_path = $benchmarks_path:ident, + extra = $extra:expr, + exec_name = $old:ident, + @user: + exec_name = $exec_name:ident + $(, $( $rest:tt )* )? + ) => { + $crate::impl_test_function!( + @cases: + ( $( $names )* ) + ( $( $names_extra )* ) + ( $( $names_skip_meta )* ) + @selected: + $bench_module, + $new_test_ext, + $test, + benchmarks_path = $benchmarks_path, + extra = $extra, + exec_name = $exec_name, + @user: + $( $( $rest )* )? + ); + }; + // iteration-exit arm which generates a #[test] function for each case. + ( + @cases: + ( $( $names:tt )+ ) + ( $( $names_extra:tt )* ) + ( $( $names_skip_meta:tt )* ) + @selected: + $bench_module:ident, + $new_test_ext:expr, + $test:path, + benchmarks_path = $path_to_benchmarks_invocation:ident, + extra = $extra:expr, + exec_name = $exec_name:ident, + @user: + $(,)? + ) => { + $crate::impl_bench_case_tests!( + { $bench_module, $new_test_ext, $exec_name, $test, $extra } + { $( $names_extra:tt )* } + $($names)+ + ); + }; + // iteration-exit arm which generates one #[test] function for all cases. + ( + @cases: + () + () + () + @selected: + $bench_module:ident, + $new_test_ext:expr, + $test:path, + benchmarks_path = $path_to_benchmarks_invocation:ident, + extra = $extra:expr, + exec_name = $exec_name:ident, + @user: + $(,)? + ) => { + #[cfg(test)] + mod benchmark_tests { + use super::$bench_module; + + #[test] + fn test_benchmarks() { + $new_test_ext.$exec_name(|| { + use $crate::Benchmarking; + + let mut anything_failed = false; + println!("failing benchmark tests:"); + for benchmark_metadata in $bench_module::<$test>::benchmarks($extra) { + let benchmark_name = &benchmark_metadata.name; + match std::panic::catch_unwind(|| { + $bench_module::<$test>::test_bench_by_name(benchmark_name) + }) { + Err(err) => { + println!( + "{}: {:?}", + $crate::str::from_utf8(benchmark_name) + .expect("benchmark name is always a valid string!"), + err, + ); + anything_failed = true; + }, + Ok(Err(err)) => { + match err { + $crate::BenchmarkError::Stop(err) => { + println!( + "{}: {:?}", + $crate::str::from_utf8(benchmark_name) + .expect("benchmark name is always a valid string!"), + err, + ); + anything_failed = true; + }, + $crate::BenchmarkError::Override(_) => { + // This is still considered a success condition. + $crate::log::error!( + "WARNING: benchmark error overrided - {}", + $crate::str::from_utf8(benchmark_name) + .expect("benchmark name is always a valid string!"), + ); + }, + $crate::BenchmarkError::Skip => { + // This is considered a success condition. + $crate::log::error!( + "WARNING: benchmark error skipped - {}", + $crate::str::from_utf8(benchmark_name) + .expect("benchmark name is always a valid string!"), + ); + } + $crate::BenchmarkError::Weightless => { + // This is considered a success condition. + $crate::log::error!( + "WARNING: benchmark weightless skipped - {}", + $crate::str::from_utf8(benchmark_name) + .expect("benchmark name is always a valid string!"), + ); + } + } + }, + Ok(Ok(())) => (), + } + } + assert!(!anything_failed); + }); + } + } + }; +} + +/// show error message and debugging info for the case of an error happening +/// during a benchmark +pub fn show_benchmark_debug_info( + instance_string: &[u8], + benchmark: &[u8], + components: &[(BenchmarkParameter, u32)], + verify: &bool, + error_message: &str, +) -> sp_runtime::RuntimeString { + sp_runtime::format_runtime_string!( + "\n* Pallet: {}\n\ + * Benchmark: {}\n\ + * Components: {:?}\n\ + * Verify: {:?}\n\ + * Error message: {}", + sp_std::str::from_utf8(instance_string) + .expect("it's all just strings ran through the wasm interface. qed"), + sp_std::str::from_utf8(benchmark) + .expect("it's all just strings ran through the wasm interface. qed"), + components, + verify, + error_message, + ) +} + +/// This macro adds pallet benchmarks to a `Vec` object. +/// +/// First create an object that holds in the input parameters for the benchmark: +/// +/// ```ignore +/// let params = (&config, &whitelist); +/// ``` +/// +/// The `whitelist` is a parameter you pass to control the DB read/write tracking. +/// We use a vector of [TrackedStorageKey](./struct.TrackedStorageKey.html), which is a simple +/// struct used to set if a key has been read or written to. +/// +/// For values that should be skipped entirely, we can just pass `key.into()`. For example: +/// +/// ``` +/// use frame_benchmarking::TrackedStorageKey; +/// let whitelist: Vec = vec![ +/// // Block Number +/// array_bytes::hex_into_unchecked("26aa394eea5630e07c48ae0c9558cef702a5c1b19ab7a04f536c519aca4983ac"), +/// // Total Issuance +/// array_bytes::hex_into_unchecked("c2261276cc9d1f8598ea4b6a74b15c2f57c875e4cff74148e4628f264b974c80"), +/// // Execution Phase +/// array_bytes::hex_into_unchecked("26aa394eea5630e07c48ae0c9558cef7ff553b5a9862a516939d82b3d3d8661a"), +/// // Event Count +/// array_bytes::hex_into_unchecked("26aa394eea5630e07c48ae0c9558cef70a98fdbe9ce6c55837576c60c7af3850"), +/// ]; +/// ``` +/// +/// Then define a mutable local variable to hold your `BenchmarkBatch` object: +/// +/// ```ignore +/// let mut batches = Vec::::new(); +/// ```` +/// +/// Then add the pallets you want to benchmark to this object, using their crate name and generated +/// module struct: +/// +/// ```ignore +/// add_benchmark!(params, batches, pallet_balances, Balances); +/// add_benchmark!(params, batches, pallet_session, SessionBench::); +/// add_benchmark!(params, batches, frame_system, SystemBench::); +/// ... +/// ``` +/// +/// At the end of `dispatch_benchmark`, you should return this batches object. +/// +/// In the case where you have multiple instances of a pallet that you need to separately benchmark, +/// the name of your module struct will be used as a suffix to your outputted weight file. For +/// example: +/// +/// ```ignore +/// add_benchmark!(params, batches, pallet_balances, Balances); // pallet_balances.rs +/// add_benchmark!(params, batches, pallet_collective, Council); // pallet_collective_council.rs +/// add_benchmark!(params, batches, pallet_collective, TechnicalCommittee); // pallet_collective_technical_committee.rs +/// ``` +/// +/// You can manipulate this suffixed string by using a type alias if needed. For example: +/// +/// ```ignore +/// type Council2 = TechnicalCommittee; +/// add_benchmark!(params, batches, pallet_collective, Council2); // pallet_collective_council_2.rs +/// ``` +#[macro_export] +macro_rules! add_benchmark { + ( $params:ident, $batches:ident, $name:path, $( $location:tt )* ) => ( + let name_string = stringify!($name).as_bytes(); + let instance_string = stringify!( $( $location )* ).as_bytes(); + let (config, whitelist) = $params; + let $crate::BenchmarkConfig { + pallet, + benchmark, + selected_components, + verify, + internal_repeats, + } = config; + if &pallet[..] == &name_string[..] { + let benchmark_result = $( $location )*::run_benchmark( + &benchmark[..], + &selected_components[..], + whitelist, + *verify, + *internal_repeats, + ); + + let final_results = match benchmark_result { + Ok(results) => Some(results), + Err($crate::BenchmarkError::Override(mut result)) => { + // Insert override warning as the first storage key. + $crate::log::error!( + "WARNING: benchmark error overrided - {}", + $crate::str::from_utf8(benchmark) + .expect("benchmark name is always a valid string!") + ); + result.keys.insert(0, + (b"Benchmark Override".to_vec(), 0, 0, false) + ); + Some($crate::vec![result]) + }, + Err($crate::BenchmarkError::Stop(e)) => { + $crate::show_benchmark_debug_info( + instance_string, + benchmark, + selected_components, + verify, + e, + ); + return Err(e.into()); + }, + Err($crate::BenchmarkError::Skip) => { + $crate::log::error!( + "WARNING: benchmark error skipped - {}", + $crate::str::from_utf8(benchmark) + .expect("benchmark name is always a valid string!") + ); + None + }, + Err($crate::BenchmarkError::Weightless) => { + $crate::log::error!( + "WARNING: benchmark weightless skipped - {}", + $crate::str::from_utf8(benchmark) + .expect("benchmark name is always a valid string!") + ); + Some(vec![$crate::BenchmarkResult { + components: selected_components.clone(), + .. Default::default() + }]) + } + }; + + if let Some(final_results) = final_results { + $batches.push($crate::BenchmarkBatch { + pallet: name_string.to_vec(), + instance: instance_string.to_vec(), + benchmark: benchmark.clone(), + results: final_results, + }); + } + } + ) +} + +/// Callback for `define_benchmarks` to call `add_benchmark`. +#[macro_export] +macro_rules! cb_add_benchmarks { + // anchor + ( $params:ident, $batches:ident, [ $name:path, $( $location:tt )* ] ) => { + $crate::add_benchmark!( $params, $batches, $name, $( $location )* ); + }; + // recursion tail + ( $params:ident, $batches:ident, [ $name:path, $( $location:tt )* ] $([ $names:path, $( $locations:tt )* ])+ ) => { + $crate::cb_add_benchmarks!( $params, $batches, [ $name, $( $location )* ] ); + $crate::cb_add_benchmarks!( $params, $batches, $([ $names, $( $locations )* ])+ ); + } +} + +/// This macro allows users to easily generate a list of benchmarks for the pallets configured +/// in the runtime. +/// +/// To use this macro, first create a an object to store the list: +/// +/// ```ignore +/// let mut list = Vec::::new(); +/// ``` +/// +/// Then pass this `list` to the macro, along with the `extra` boolean, the pallet crate, and +/// pallet struct: +/// +/// ```ignore +/// list_benchmark!(list, extra, pallet_balances, Balances); +/// list_benchmark!(list, extra, pallet_session, SessionBench::); +/// list_benchmark!(list, extra, frame_system, SystemBench::); +/// ``` +/// +/// This should match what exists with the `add_benchmark!` macro. +#[macro_export] +macro_rules! list_benchmark { + ( $list:ident, $extra:ident, $name:path, $( $location:tt )* ) => ( + let pallet_string = stringify!($name).as_bytes(); + let instance_string = stringify!( $( $location )* ).as_bytes(); + let benchmarks = $( $location )*::benchmarks($extra); + let pallet_benchmarks = BenchmarkList { + pallet: pallet_string.to_vec(), + instance: instance_string.to_vec(), + benchmarks: benchmarks.to_vec(), + }; + $list.push(pallet_benchmarks) + ) +} + +/// Callback for `define_benchmarks` to call `list_benchmark`. +#[macro_export] +macro_rules! cb_list_benchmarks { + // anchor + ( $list:ident, $extra:ident, [ $name:path, $( $location:tt )* ] ) => { + $crate::list_benchmark!( $list, $extra, $name, $( $location )* ); + }; + // recursion tail + ( $list:ident, $extra:ident, [ $name:path, $( $location:tt )* ] $([ $names:path, $( $locations:tt )* ])+ ) => { + $crate::cb_list_benchmarks!( $list, $extra, [ $name, $( $location )* ] ); + $crate::cb_list_benchmarks!( $list, $extra, $([ $names, $( $locations )* ])+ ); + } +} + +/// Defines pallet configs that `add_benchmarks` and `list_benchmarks` use. +/// Should be preferred instead of having a repetitive list of configs +/// in `add_benchmark` and `list_benchmark`. +#[macro_export] +macro_rules! define_benchmarks { + ( $([ $names:path, $( $locations:tt )* ])* ) => { + /// Calls `list_benchmark` with all configs from `define_benchmarks` + /// and passes the first two parameters on. + /// + /// Use as: + /// ```ignore + /// list_benchmarks!(list, extra); + /// ``` + #[macro_export] + macro_rules! list_benchmarks { + ( $list:ident, $extra:ident ) => { + $crate::cb_list_benchmarks!( $list, $extra, $([ $names, $( $locations )* ])+ ); + } + } + + /// Calls `add_benchmark` with all configs from `define_benchmarks` + /// and passes the first two parameters on. + /// + /// Use as: + /// ```ignore + /// add_benchmarks!(params, batches); + /// ``` + #[macro_export] + macro_rules! add_benchmarks { + ( $params:ident, $batches:ident ) => { + $crate::cb_add_benchmarks!( $params, $batches, $([ $names, $( $locations )* ])+ ); + } + } + } +} + +pub use add_benchmark; +pub use benchmark_backend; +pub use benchmarks; +pub use benchmarks_instance; +pub use benchmarks_instance_pallet; +pub use benchmarks_iter; +pub use cb_add_benchmarks; +pub use cb_list_benchmarks; +pub use define_benchmarks; +pub use impl_bench_case_tests; +pub use impl_bench_name_tests; +pub use impl_benchmark; +pub use impl_benchmark_test; +pub use impl_benchmark_test_suite; +pub use impl_test_function; +pub use list_benchmark; +pub use selected_benchmark; +pub use to_origin; +pub use whitelist; diff --git a/frame/bounties/src/benchmarking.rs b/frame/bounties/src/benchmarking.rs index adadb3411..a43372978 100644 --- a/frame/bounties/src/benchmarking.rs +++ b/frame/bounties/src/benchmarking.rs @@ -21,7 +21,9 @@ use super::*; -use frame_benchmarking::{account, benchmarks_instance_pallet, whitelisted_caller, BenchmarkError}; +use frame_benchmarking::v1::{ + account, benchmarks_instance_pallet, whitelisted_caller, BenchmarkError, +}; use frame_system::RawOrigin; use sp_runtime::traits::Bounded; diff --git a/frame/child-bounties/src/benchmarking.rs b/frame/child-bounties/src/benchmarking.rs index 04a8583f2..e7f18c164 100644 --- a/frame/child-bounties/src/benchmarking.rs +++ b/frame/child-bounties/src/benchmarking.rs @@ -21,7 +21,7 @@ use super::*; -use frame_benchmarking::{account, benchmarks, whitelisted_caller, BenchmarkError}; +use frame_benchmarking::v1::{account, benchmarks, whitelisted_caller, BenchmarkError}; use frame_system::RawOrigin; use crate::Pallet as ChildBounties; diff --git a/frame/collective/src/benchmarking.rs b/frame/collective/src/benchmarking.rs index 75a724623..5c9e55862 100644 --- a/frame/collective/src/benchmarking.rs +++ b/frame/collective/src/benchmarking.rs @@ -23,7 +23,7 @@ use crate::Pallet as Collective; use sp_runtime::traits::Bounded; use sp_std::mem::size_of; -use frame_benchmarking::{account, benchmarks_instance_pallet, whitelisted_caller}; +use frame_benchmarking::v1::{account, benchmarks_instance_pallet, whitelisted_caller}; use frame_system::{Call as SystemCall, Pallet as System, RawOrigin as SystemOrigin}; const SEED: u32 = 0; diff --git a/frame/contracts/src/benchmarking/mod.rs b/frame/contracts/src/benchmarking/mod.rs index 2bd807dfb..708d92766 100644 --- a/frame/contracts/src/benchmarking/mod.rs +++ b/frame/contracts/src/benchmarking/mod.rs @@ -37,7 +37,7 @@ use crate::{ Pallet as Contracts, *, }; use codec::{Encode, MaxEncodedLen}; -use frame_benchmarking::{account, benchmarks, whitelisted_caller}; +use frame_benchmarking::v1::{account, benchmarks, whitelisted_caller}; use frame_support::weights::Weight; use frame_system::RawOrigin; use sp_runtime::{ diff --git a/frame/conviction-voting/src/benchmarking.rs b/frame/conviction-voting/src/benchmarking.rs index 117bb7fe2..8c168a34a 100644 --- a/frame/conviction-voting/src/benchmarking.rs +++ b/frame/conviction-voting/src/benchmarking.rs @@ -20,7 +20,7 @@ use super::*; use assert_matches::assert_matches; -use frame_benchmarking::{account, benchmarks_instance_pallet, whitelist_account}; +use frame_benchmarking::v1::{account, benchmarks_instance_pallet, whitelist_account}; use frame_support::{ dispatch::RawOrigin, traits::{fungible, Currency, Get}, diff --git a/frame/democracy/src/benchmarking.rs b/frame/democracy/src/benchmarking.rs index 424192e25..66d9e2a00 100644 --- a/frame/democracy/src/benchmarking.rs +++ b/frame/democracy/src/benchmarking.rs @@ -19,7 +19,7 @@ use super::*; -use frame_benchmarking::{account, benchmarks, whitelist_account}; +use frame_benchmarking::v1::{account, benchmarks, whitelist_account}; use frame_support::{ assert_noop, assert_ok, traits::{Currency, EnsureOrigin, Get, OnInitialize, UnfilteredDispatchable}, diff --git a/frame/election-provider-support/benchmarking/src/lib.rs b/frame/election-provider-support/benchmarking/src/lib.rs index 547e35bed..5323513da 100644 --- a/frame/election-provider-support/benchmarking/src/lib.rs +++ b/frame/election-provider-support/benchmarking/src/lib.rs @@ -22,7 +22,7 @@ #![cfg_attr(not(feature = "std"), no_std)] use codec::Decode; -use frame_benchmarking::{benchmarks, Vec}; +use frame_benchmarking::v1::{benchmarks, Vec}; use frame_election_provider_support::{NposSolver, PhragMMS, SequentialPhragmen}; pub struct Pallet(frame_system::Pallet); diff --git a/frame/elections-phragmen/src/benchmarking.rs b/frame/elections-phragmen/src/benchmarking.rs index 53157deaa..1fc500a8e 100644 --- a/frame/elections-phragmen/src/benchmarking.rs +++ b/frame/elections-phragmen/src/benchmarking.rs @@ -21,7 +21,7 @@ use super::*; -use frame_benchmarking::{account, benchmarks, whitelist, BenchmarkError, BenchmarkResult}; +use frame_benchmarking::v1::{account, benchmarks, whitelist, BenchmarkError, BenchmarkResult}; use frame_support::{dispatch::DispatchResultWithPostInfo, traits::OnInitialize}; use frame_system::RawOrigin; diff --git a/frame/examples/basic/src/benchmarking.rs b/frame/examples/basic/src/benchmarking.rs index 699e56685..a9e2013b0 100644 --- a/frame/examples/basic/src/benchmarking.rs +++ b/frame/examples/basic/src/benchmarking.rs @@ -21,8 +21,11 @@ #![cfg(feature = "runtime-benchmarks")] use crate::*; -use frame_benchmarking::{impl_benchmark_test_suite, whitelisted_caller}; -use frame_support::benchmarking::{benchmarks, Linear}; +use frame_benchmarking::v1::{ + impl_benchmark_test_suite, + v2::{benchmarks, Linear}, + whitelisted_caller, +}; use frame_system::RawOrigin; // To actually run this benchmark on pallet-example-basic, we need to put this pallet into the diff --git a/frame/fast-unstake/src/benchmarking.rs b/frame/fast-unstake/src/benchmarking.rs index de85502f7..77488b644 100644 --- a/frame/fast-unstake/src/benchmarking.rs +++ b/frame/fast-unstake/src/benchmarking.rs @@ -20,7 +20,7 @@ #![cfg(feature = "runtime-benchmarks")] use crate::{types::*, Pallet as FastUnstake, *}; -use frame_benchmarking::{benchmarks, whitelist_account}; +use frame_benchmarking::v1::{benchmarks, whitelist_account}; use frame_support::{ assert_ok, traits::{Currency, EnsureOrigin, Get, Hooks}, diff --git a/frame/grandpa/src/benchmarking.rs b/frame/grandpa/src/benchmarking.rs index 124085914..6eee12ad9 100644 --- a/frame/grandpa/src/benchmarking.rs +++ b/frame/grandpa/src/benchmarking.rs @@ -18,7 +18,7 @@ //! Benchmarks for the GRANDPA pallet. use super::{Pallet as Grandpa, *}; -use frame_benchmarking::benchmarks; +use frame_benchmarking::v1::benchmarks; use frame_system::RawOrigin; use sp_core::H256; diff --git a/frame/identity/src/benchmarking.rs b/frame/identity/src/benchmarking.rs index 3584eb954..356bcb2e8 100644 --- a/frame/identity/src/benchmarking.rs +++ b/frame/identity/src/benchmarking.rs @@ -22,7 +22,7 @@ use super::*; use crate::Pallet as Identity; -use frame_benchmarking::{account, benchmarks, whitelisted_caller}; +use frame_benchmarking::v1::{account, benchmarks, whitelisted_caller}; use frame_support::{ ensure, traits::{EnsureOrigin, Get}, diff --git a/frame/im-online/src/benchmarking.rs b/frame/im-online/src/benchmarking.rs index edc21043c..e4d1a6465 100644 --- a/frame/im-online/src/benchmarking.rs +++ b/frame/im-online/src/benchmarking.rs @@ -21,7 +21,7 @@ use super::*; -use frame_benchmarking::benchmarks; +use frame_benchmarking::v1::benchmarks; use frame_support::{traits::UnfilteredDispatchable, WeakBoundedVec}; use frame_system::RawOrigin; use sp_core::{offchain::OpaqueMultiaddr, OpaquePeerId}; diff --git a/frame/indices/src/benchmarking.rs b/frame/indices/src/benchmarking.rs index f462f2228..ce1ea9f4d 100644 --- a/frame/indices/src/benchmarking.rs +++ b/frame/indices/src/benchmarking.rs @@ -20,7 +20,7 @@ #![cfg(feature = "runtime-benchmarks")] use super::*; -use frame_benchmarking::{account, benchmarks, whitelisted_caller}; +use frame_benchmarking::v1::{account, benchmarks, whitelisted_caller}; use frame_system::RawOrigin; use sp_runtime::traits::Bounded; diff --git a/frame/lottery/src/benchmarking.rs b/frame/lottery/src/benchmarking.rs index fba722a07..658dd25a9 100644 --- a/frame/lottery/src/benchmarking.rs +++ b/frame/lottery/src/benchmarking.rs @@ -21,7 +21,7 @@ use super::*; -use frame_benchmarking::{account, benchmarks, whitelisted_caller}; +use frame_benchmarking::v1::{account, benchmarks, whitelisted_caller}; use frame_support::{ storage::bounded_vec::BoundedVec, traits::{EnsureOrigin, OnInitialize}, diff --git a/frame/membership/src/lib.rs b/frame/membership/src/lib.rs index 934abb996..6d973a15f 100644 --- a/frame/membership/src/lib.rs +++ b/frame/membership/src/lib.rs @@ -362,7 +362,7 @@ impl, I: 'static> SortedMembers for Pallet { #[cfg(feature = "runtime-benchmarks")] mod benchmark { use super::{Pallet as Membership, *}; - use frame_benchmarking::{account, benchmarks_instance_pallet, whitelist}; + use frame_benchmarking::v1::{account, benchmarks_instance_pallet, whitelist}; use frame_support::{assert_ok, traits::EnsureOrigin}; use frame_system::RawOrigin; diff --git a/frame/merkle-mountain-range/src/benchmarking.rs b/frame/merkle-mountain-range/src/benchmarking.rs index d24364a55..7731c0e50 100644 --- a/frame/merkle-mountain-range/src/benchmarking.rs +++ b/frame/merkle-mountain-range/src/benchmarking.rs @@ -20,7 +20,7 @@ #![cfg(feature = "runtime-benchmarks")] use crate::*; -use frame_benchmarking::benchmarks_instance_pallet; +use frame_benchmarking::v1::benchmarks_instance_pallet; use frame_support::traits::OnInitialize; benchmarks_instance_pallet! { diff --git a/frame/message-queue/src/benchmarking.rs b/frame/message-queue/src/benchmarking.rs index 9651c81e5..97ca71ce9 100644 --- a/frame/message-queue/src/benchmarking.rs +++ b/frame/message-queue/src/benchmarking.rs @@ -22,8 +22,8 @@ use super::{mock_helpers::*, Pallet as MessageQueue, *}; -use frame_benchmarking::{impl_benchmark_test_suite, whitelisted_caller}; -use frame_support::{benchmarking::*, traits::Get}; +use frame_benchmarking::v2::*; +use frame_support::traits::Get; use frame_system::RawOrigin; use sp_std::prelude::*; diff --git a/frame/multisig/src/benchmarking.rs b/frame/multisig/src/benchmarking.rs index d5faf9ae8..8993908b2 100644 --- a/frame/multisig/src/benchmarking.rs +++ b/frame/multisig/src/benchmarking.rs @@ -20,7 +20,7 @@ #![cfg(feature = "runtime-benchmarks")] use super::*; -use frame_benchmarking::{account, benchmarks}; +use frame_benchmarking::v1::{account, benchmarks}; use frame_system::RawOrigin; use sp_runtime::traits::Bounded; diff --git a/frame/nfts/src/benchmarking.rs b/frame/nfts/src/benchmarking.rs index 6517445da..17ff28b82 100644 --- a/frame/nfts/src/benchmarking.rs +++ b/frame/nfts/src/benchmarking.rs @@ -21,7 +21,7 @@ use super::*; use enumflags2::{BitFlag, BitFlags}; -use frame_benchmarking::{ +use frame_benchmarking::v1::{ account, benchmarks_instance_pallet, whitelist_account, whitelisted_caller, }; use frame_support::{ diff --git a/frame/nis/src/benchmarking.rs b/frame/nis/src/benchmarking.rs index b45c982bd..b01d414c4 100644 --- a/frame/nis/src/benchmarking.rs +++ b/frame/nis/src/benchmarking.rs @@ -20,7 +20,7 @@ #![cfg(feature = "runtime-benchmarks")] use super::*; -use frame_benchmarking::{account, benchmarks, whitelisted_caller}; +use frame_benchmarking::v1::{account, benchmarks, whitelisted_caller}; use frame_support::traits::{nonfungible::Inspect, Currency, EnsureOrigin, Get}; use frame_system::RawOrigin; use sp_arithmetic::Perquintill; diff --git a/frame/nomination-pools/benchmarking/src/lib.rs b/frame/nomination-pools/benchmarking/src/lib.rs index 9b0635391..32708ff53 100644 --- a/frame/nomination-pools/benchmarking/src/lib.rs +++ b/frame/nomination-pools/benchmarking/src/lib.rs @@ -23,7 +23,9 @@ #[cfg(test)] mod mock; -use frame_benchmarking::{account, frame_support::traits::Currency, vec, whitelist_account, Vec}; +use frame_benchmarking::v1::{ + account, frame_support::traits::Currency, vec, whitelist_account, Vec, +}; use frame_election_provider_support::SortedListProvider; use frame_support::{assert_ok, ensure, traits::Get}; use frame_system::RawOrigin as RuntimeOrigin; diff --git a/frame/offences/benchmarking/src/lib.rs b/frame/offences/benchmarking/src/lib.rs index 7e586a96b..b71321906 100644 --- a/frame/offences/benchmarking/src/lib.rs +++ b/frame/offences/benchmarking/src/lib.rs @@ -24,7 +24,7 @@ mod mock; use sp_std::{prelude::*, vec}; -use frame_benchmarking::{account, benchmarks}; +use frame_benchmarking::v1::{account, benchmarks}; use frame_support::traits::{Currency, Get, ValidatorSet, ValidatorSetWithIdentification}; use frame_system::{Config as SystemConfig, Pallet as System, RawOrigin}; diff --git a/frame/preimage/src/benchmarking.rs b/frame/preimage/src/benchmarking.rs index 8a61d7d78..f9526b67d 100644 --- a/frame/preimage/src/benchmarking.rs +++ b/frame/preimage/src/benchmarking.rs @@ -18,7 +18,7 @@ //! Preimage pallet benchmarking. use super::*; -use frame_benchmarking::{account, benchmarks, whitelist_account}; +use frame_benchmarking::v1::{account, benchmarks, whitelist_account}; use frame_support::assert_ok; use frame_system::RawOrigin; use sp_runtime::traits::Bounded; diff --git a/frame/proxy/src/benchmarking.rs b/frame/proxy/src/benchmarking.rs index 58c0cb730..ef3df02dc 100644 --- a/frame/proxy/src/benchmarking.rs +++ b/frame/proxy/src/benchmarking.rs @@ -21,7 +21,7 @@ use super::*; use crate::Pallet as Proxy; -use frame_benchmarking::{account, benchmarks, whitelisted_caller}; +use frame_benchmarking::v1::{account, benchmarks, whitelisted_caller}; use frame_system::RawOrigin; use sp_runtime::traits::Bounded; diff --git a/frame/ranked-collective/src/benchmarking.rs b/frame/ranked-collective/src/benchmarking.rs index eb629b330..48df7f1f1 100644 --- a/frame/ranked-collective/src/benchmarking.rs +++ b/frame/ranked-collective/src/benchmarking.rs @@ -21,7 +21,7 @@ use super::*; #[allow(unused_imports)] use crate::Pallet as RankedCollective; -use frame_benchmarking::{account, benchmarks_instance_pallet, whitelisted_caller}; +use frame_benchmarking::v1::{account, benchmarks_instance_pallet, whitelisted_caller}; use frame_support::{assert_ok, dispatch::UnfilteredDispatchable}; use frame_system::RawOrigin as SystemOrigin; diff --git a/frame/recovery/src/benchmarking.rs b/frame/recovery/src/benchmarking.rs index 870543d9b..48ca7dad7 100644 --- a/frame/recovery/src/benchmarking.rs +++ b/frame/recovery/src/benchmarking.rs @@ -20,7 +20,7 @@ use super::*; use crate::Pallet; -use frame_benchmarking::{account, benchmarks, whitelisted_caller}; +use frame_benchmarking::v1::{account, benchmarks, whitelisted_caller}; use frame_support::traits::{Currency, Get}; use frame_system::RawOrigin; use sp_runtime::traits::Bounded; diff --git a/frame/referenda/src/benchmarking.rs b/frame/referenda/src/benchmarking.rs index e57b5f985..db5299e0d 100644 --- a/frame/referenda/src/benchmarking.rs +++ b/frame/referenda/src/benchmarking.rs @@ -20,7 +20,7 @@ use super::*; use crate::Pallet as Referenda; use assert_matches::assert_matches; -use frame_benchmarking::{account, benchmarks_instance_pallet, whitelist_account}; +use frame_benchmarking::v1::{account, benchmarks_instance_pallet, whitelist_account}; use frame_support::{ assert_ok, dispatch::UnfilteredDispatchable, diff --git a/frame/remark/src/benchmarking.rs b/frame/remark/src/benchmarking.rs index c0db8d5d3..ee93cb24a 100644 --- a/frame/remark/src/benchmarking.rs +++ b/frame/remark/src/benchmarking.rs @@ -20,7 +20,7 @@ #![cfg(feature = "runtime-benchmarks")] use super::*; -use frame_benchmarking::{benchmarks, whitelisted_caller}; +use frame_benchmarking::v1::{benchmarks, whitelisted_caller}; use frame_system::{EventRecord, Pallet as System, RawOrigin}; use sp_std::*; diff --git a/frame/scheduler/src/benchmarking.rs b/frame/scheduler/src/benchmarking.rs index 8e2876290..9ec82f5c8 100644 --- a/frame/scheduler/src/benchmarking.rs +++ b/frame/scheduler/src/benchmarking.rs @@ -18,7 +18,7 @@ //! Scheduler pallet benchmarking. use super::*; -use frame_benchmarking::{account, benchmarks}; +use frame_benchmarking::v1::{account, benchmarks}; use frame_support::{ ensure, traits::{schedule::Priority, BoundedInline}, diff --git a/frame/session/benchmarking/src/lib.rs b/frame/session/benchmarking/src/lib.rs index 9e478ada5..a6c25f5e1 100644 --- a/frame/session/benchmarking/src/lib.rs +++ b/frame/session/benchmarking/src/lib.rs @@ -26,7 +26,7 @@ mod mock; use sp_runtime::traits::{One, StaticLookup, TrailingZeroInput}; use sp_std::{prelude::*, vec}; -use frame_benchmarking::benchmarks; +use frame_benchmarking::v1::benchmarks; use frame_support::{ codec::Decode, traits::{Get, KeyOwnerProofSystem, OnInitialize}, diff --git a/frame/staking/src/benchmarking.rs b/frame/staking/src/benchmarking.rs index b3d32b26e..a4366bbd8 100644 --- a/frame/staking/src/benchmarking.rs +++ b/frame/staking/src/benchmarking.rs @@ -35,7 +35,7 @@ use sp_runtime::{ use sp_staking::SessionIndex; use sp_std::prelude::*; -pub use frame_benchmarking::{ +pub use frame_benchmarking::v1::{ account, benchmarks, impl_benchmark_test_suite, whitelist_account, whitelisted_caller, }; use frame_system::RawOrigin; diff --git a/frame/support/Cargo.toml b/frame/support/Cargo.toml index ae2fc3589..4f62ae42e 100644 --- a/frame/support/Cargo.toml +++ b/frame/support/Cargo.toml @@ -27,7 +27,6 @@ sp-arithmetic = { version = "6.0.0", default-features = false, path = "../../pri sp-inherents = { version = "4.0.0-dev", default-features = false, path = "../../primitives/inherents" } sp-staking = { version = "4.0.0-dev", default-features = false, path = "../../primitives/staking" } sp-weights = { version = "4.0.0", default-features = false, path = "../../primitives/weights" } -static_assertions = "1.1.0" tt-call = "1.0.8" frame-support-procedural = { version = "4.0.0-dev", default-features = false, path = "./procedural" } paste = "1.0" diff --git a/frame/support/procedural/src/benchmark.rs b/frame/support/procedural/src/benchmark.rs index f3bd5c2cf..fa7fd4492 100644 --- a/frame/support/procedural/src/benchmark.rs +++ b/frame/support/procedural/src/benchmark.rs @@ -638,7 +638,7 @@ fn expand_benchmark( Ok(ident) => ident, Err(err) => return err.to_compile_error().into(), }; - let home = quote!(#krate::frame_support::benchmarking); + let home = quote!(#krate::v2); let codec = quote!(#krate::frame_support::codec); let traits = quote!(#krate::frame_support::traits); let setup_stmts = benchmark_def.setup_stmts; diff --git a/frame/support/procedural/src/lib.rs b/frame/support/procedural/src/lib.rs index 9bdbbd1f2..8fa46b48d 100644 --- a/frame/support/procedural/src/lib.rs +++ b/frame/support/procedural/src/lib.rs @@ -483,7 +483,7 @@ pub fn pallet(attr: TokenStream, item: TokenStream) -> TokenStream { /// An attribute macro that can be attached to a (non-empty) module declaration. Doing so will /// designate that module as a benchmarking module. /// -/// See `frame_support::benchmarking` for more info. +/// See `frame_benchmarking::v2` for more info. #[proc_macro_attribute] pub fn benchmarks(attr: TokenStream, tokens: TokenStream) -> TokenStream { match benchmark::benchmarks(attr, tokens, false) { @@ -495,7 +495,7 @@ pub fn benchmarks(attr: TokenStream, tokens: TokenStream) -> TokenStream { /// An attribute macro that can be attached to a (non-empty) module declaration. Doing so will /// designate that module as an instance benchmarking module. /// -/// See `frame_support::benchmarking` for more info. +/// See `frame_benchmarking::v2` for more info. #[proc_macro_attribute] pub fn instance_benchmarks(attr: TokenStream, tokens: TokenStream) -> TokenStream { match benchmark::benchmarks(attr, tokens, true) { @@ -508,7 +508,7 @@ pub fn instance_benchmarks(attr: TokenStream, tokens: TokenStream) -> TokenStrea /// attached to a function definition containing an `#[extrinsic_call]` or `#[block]` /// attribute. /// -/// See `frame_support::benchmarking` for more info. +/// See `frame_benchmarking::v2` for more info. #[proc_macro_attribute] pub fn benchmark(_attrs: TokenStream, _tokens: TokenStream) -> TokenStream { quote!(compile_error!( @@ -521,7 +521,7 @@ pub fn benchmark(_attrs: TokenStream, _tokens: TokenStream) -> TokenStream { /// used as a boundary designating where the benchmark setup code ends, and the benchmark /// verification code begins. /// -/// See `frame_support::benchmarking` for more info. +/// See `frame_benchmarking::v2` for more info. #[proc_macro_attribute] pub fn extrinsic_call(_attrs: TokenStream, _tokens: TokenStream) -> TokenStream { quote!(compile_error!( @@ -534,7 +534,7 @@ pub fn extrinsic_call(_attrs: TokenStream, _tokens: TokenStream) -> TokenStream /// enclosing benchmark function, This attribute is also used as a boundary designating where /// the benchmark setup code ends, and the benchmark verification code begins. /// -/// See `frame_support::benchmarking` for more info. +/// See `frame_benchmarking::v2` for more info. #[proc_macro_attribute] pub fn block(_attrs: TokenStream, _tokens: TokenStream) -> TokenStream { quote!(compile_error!( diff --git a/frame/support/src/lib.rs b/frame/support/src/lib.rs index 40bc878cf..902893972 100644 --- a/frame/support/src/lib.rs +++ b/frame/support/src/lib.rs @@ -2745,228 +2745,5 @@ pub mod pallet_macros { }; } -/// Contains macros, structs, and traits associated with v2 of the pallet benchmarking syntax. -/// This module contains macros, structs, and traits associated with v2 of the pallet -/// benchmarking syntax. -/// -/// The [`benchmarking::benchmarks`] and [`benchmarking::instance_benchmarks`] macros can be -/// used to designate a module as a benchmarking module that can contain benchmarks and -/// benchmark tests. The `#[benchmarks]` variant will set up a regular, non-instance -/// benchmarking module, and the `#[instance_benchmarks]` variant will set up the module in -/// instance benchmarking mode. -/// -/// Benchmarking modules should be gated behind a `#[cfg(feature = "runtime-benchmarks")]` -/// feature gate to ensure benchmarking code that is only compiled when the -/// `runtime-benchmarks` feature is enabled is not referenced. -/// -/// The following is the general syntax for a benchmarks (or instance benchmarks) module: -/// -/// ## General Syntax -/// -/// ```ignore -/// #![cfg(feature = "runtime-benchmarks")] -/// -/// use super::{mock_helpers::*, Pallet as MyPallet}; -/// use frame_support::benchmarking::*; -/// use frame_benchmarking::whitelisted_caller; -/// -/// #[benchmarks] -/// mod benchmarks { -/// use super::*; -/// -/// #[benchmark] -/// fn bench_name_1(x: Linear<7, 1_000>, y: Linear<1_000, 100_0000>) { -/// // setup code -/// let z = x + y; -/// let caller = whitelisted_caller(); -/// -/// #[extrinsic_call] -/// extrinsic_name(SystemOrigin::Signed(caller), other, arguments); -/// -/// // verification code -/// assert_eq!(MyPallet::::my_var(), z); -/// } -/// -/// #[benchmark] -/// fn bench_name_2() { -/// // setup code -/// let caller = whitelisted_caller(); -/// -/// #[block] -/// { -/// something(some, thing); -/// my_extrinsic(RawOrigin::Signed(caller), some, argument); -/// something_else(foo, bar); -/// } -/// -/// // verification code -/// assert_eq!(MyPallet::::something(), 37); -/// } -/// } -/// ``` -/// -/// ## Benchmark Definitions -/// -/// Within a `#[benchmarks]` or `#[instance_benchmarks]` module, you can define individual -/// benchmarks using the `#[benchmark]` attribute, as shown in the example above. -/// -/// The `#[benchmark]` attribute expects a function definition with a blank return type and -/// zero or more arguments whose names are valid `frame_benchmarking::BenchmarkParamater` -/// parameters, such as `x`, `y`, `a`, `b`, etc., and whose param types must implement -/// [`benchmarking::ParamRange`]. At the moment the only valid type that implements -/// [`benchmarking::ParamRange`] is [`benchmarking::Linear`]. -/// -/// The valid syntax for defining a `Linear` is `Linear` where `A`, and `B` are -/// valid integer literals (that fit in a `u32`), such that `B` >= `A`. -/// -/// Note that the benchmark function definition does not actually expand as a function -/// definition, but rather is used to automatically create a number of impls and structs -/// required by the benchmarking engine. For this reason, the visibility of the function -/// definition as well as the return type are not used for any purpose and are discarded by the -/// expansion code. -/// -/// Also note that the `// setup code` and `// verification code` comments shown above are not -/// required and are included simply for demonstration purposes. -/// -/// ### `#[extrinsic_call]` and `#[block]` -/// -/// Within the benchmark function body, either an `#[extrinsic_call]` or a `#[block]` -/// annotation is required. These attributes should be attached to a block (shown in -/// `bench_name_2` above) or a one-line function call (shown in `bench_name_1` above, in `syn` -/// parlance this should be an `ExprCall`), respectively. -/// -/// The `#[block]` syntax is broad and will benchmark any code contained within the block the -/// attribute is attached to. If `#[block]` is attached to something other than a block, a -/// compiler error will be emitted. -/// -/// The one-line `#[extrinsic_call]` syntax must consist of a function call to an extrinsic, -/// where the first argument is the origin. If `#[extrinsic_call]` is attached to an item that -/// doesn't meet these requirements, a compiler error will be emitted. -/// -/// As a short-hand, you may substitute the name of the extrinsic call with `_`, such as the -/// following: -/// -/// ```ignore -/// #[extrinsic_call] -/// _(RawOrigin::Signed(whitelisted_caller()), 0u32.into(), 0); -/// ``` -/// -/// The underscore will be substituted with the name of the benchmark (i.e. the name of the -/// function in the benchmark function definition). -/// -/// Regardless of whether `#[extrinsic_call]` or `#[block]` is used, this attribute also serves -/// the purpose of designating the boundary between the setup code portion of the benchmark -/// (everything before the `#[extrinsic_call]` or `#[block]` attribute) and the verification -/// stage (everything after the item that the `#[extrinsic_call]` or `#[block]` attribute is -/// attached to). The setup code section should contain any code that needs to execute before -/// the measured portion of the benchmark executes. The verification section is where you can -/// perform assertions to verify that the extrinsic call (or whatever is happening in your -/// block, if you used the `#[block]` syntax) executed successfully. -/// -/// Note that neither `#[extrinsic_call]` nor `#[block]` are real attribute macros and are -/// instead consumed by the outer macro pattern as part of the enclosing benchmark function -/// definition. This is why we are able to use `#[extrinsic_call]` and `#[block]` within a -/// function definition even though this behavior has not been stabilized -/// yet—`#[extrinsic_call]` and `#[block]` are parsed and consumed as part of the benchmark -/// definition parsing code, so they never expand as their own attribute macros. -/// -/// ### Optional Attributes -/// -/// The keywords `extra` and `skip_meta` can be provided as optional arguments to the -/// `#[benchmark]` attribute, i.e. `#[benchmark(extra, skip_meta)]`. Including either of these -/// will enable the `extra` or `skip_meta` option, respectively. These options enable the same -/// behavior they did in the old benchmarking syntax in `frame_benchmarking`, namely: -/// -/// #### `extra` -/// -/// Specifies that this benchmark should not normally run. To run benchmarks marked with -/// `extra`, you will need to invoke the `frame-benchmarking-cli` with `--extra`. -/// -/// #### `skip_meta` -/// -/// Specifies that the benchmarking framework should not analyze the storage keys that -/// benchmarked code read or wrote. This useful to suppress the prints in the form of unknown -/// 0x… in case a storage key that does not have metadata. Note that this skips the analysis -/// of all accesses, not just ones without metadata. -/// -/// ## Where Clause -/// -/// Some pallets require a where clause specifying constraints on their generics to make -/// writing benchmarks feasible. To accomodate this situation, you can provide such a where -/// clause as the (only) argument to the `#[benchmarks]` or `#[instance_benchmarks]` attribute -/// macros. Below is an example of this taken from the `message-queue` pallet. -/// -/// ```ignore -/// #[benchmarks( -/// where -/// <::MessageProcessor as ProcessMessage>::Origin: From + PartialEq, -/// ::Size: From, -/// )] -/// mod benchmarks { -/// use super::*; -/// // ... -/// } -/// ``` -/// -/// ## Benchmark Tests -/// -/// Benchmark tests can be generated using the old syntax in `frame_benchmarking`, -/// including the `frame_benchmarking::impl_benchmark_test_suite` macro. -/// -/// An example is shown below (taken from the `message-queue` pallet's `benchmarking` module): -/// ```ignore -/// #[benchmarks] -/// mod benchmarks { -/// use super::*; -/// // ... -/// impl_benchmark_test_suite!( -/// MessageQueue, -/// crate::mock::new_test_ext::(), -/// crate::integration_test::Test -/// ); -/// } -/// ``` -pub mod benchmarking { - pub use frame_support_procedural::{ - benchmark, benchmarks, block, extrinsic_call, instance_benchmarks, - }; - - // Used in #[benchmark] implementation to ensure that benchmark function arguments - // implement [`ParamRange`]. - #[doc(hidden)] - pub use static_assertions::assert_impl_all; - - /// Used by the new benchmarking code to specify that a benchmarking variable is linear - /// over some specified range, i.e. `Linear<0, 1_000>` means that the corresponding variable - /// is allowed to range from `0` to `1000`, inclusive. - /// - /// See [`frame_support::benchmarking`] for more info. - pub struct Linear; - - /// Trait that must be implemented by all structs that can be used as parameter range types - /// in the new benchmarking code (i.e. `Linear<0, 1_000>`). Right now there is just - /// [`Linear`] but this could later be extended to support additional non-linear parameter - /// ranges. - /// - /// See [`frame_support::benchmarking`] for more info. - pub trait ParamRange { - /// Represents the (inclusive) starting number of this [`ParamRange`]. - fn start(&self) -> u32; - - /// Represents the (inclusive) ending number of this [`ParamRange`] - fn end(&self) -> u32; - } - - impl ParamRange for Linear { - fn start(&self) -> u32 { - return A - } - - fn end(&self) -> u32 { - return B - } - } -} - // Generate a macro that will enable/disable code based on `std` feature being active. sp_core::generate_feature_enabled_macro!(std_enabled, feature = "std", $); diff --git a/frame/support/test/tests/benchmark_ui/bad_param_name.rs b/frame/support/test/tests/benchmark_ui/bad_param_name.rs index ad4db4f0a..657e481a9 100644 --- a/frame/support/test/tests/benchmark_ui/bad_param_name.rs +++ b/frame/support/test/tests/benchmark_ui/bad_param_name.rs @@ -1,4 +1,4 @@ -use frame_support::benchmarking::*; +use frame_benchmarking::v2::*; #[allow(unused_imports)] use frame_support_test::Config; diff --git a/frame/support/test/tests/benchmark_ui/bad_param_name_too_long.rs b/frame/support/test/tests/benchmark_ui/bad_param_name_too_long.rs index 50e4dc6fd..f970126d1 100644 --- a/frame/support/test/tests/benchmark_ui/bad_param_name_too_long.rs +++ b/frame/support/test/tests/benchmark_ui/bad_param_name_too_long.rs @@ -1,9 +1,9 @@ -use frame_support::benchmarking::*; +use frame_benchmarking::v2::*; #[allow(unused_imports)] use frame_support_test::Config; #[benchmarks] -mod benches { +mod benchmarks { #[benchmark] fn bench(xx: Linear<1, 2>) { #[block] diff --git a/frame/support/test/tests/benchmark_ui/bad_param_name_upper_case.rs b/frame/support/test/tests/benchmark_ui/bad_param_name_upper_case.rs index 0270582b3..9970f3230 100644 --- a/frame/support/test/tests/benchmark_ui/bad_param_name_upper_case.rs +++ b/frame/support/test/tests/benchmark_ui/bad_param_name_upper_case.rs @@ -1,4 +1,4 @@ -use frame_support::benchmarking::*; +use frame_benchmarking::v2::*; #[allow(unused_imports)] use frame_support_test::Config; diff --git a/frame/support/test/tests/benchmark_ui/bad_param_range.rs b/frame/support/test/tests/benchmark_ui/bad_param_range.rs index aabb9fa74..993f2a000 100644 --- a/frame/support/test/tests/benchmark_ui/bad_param_range.rs +++ b/frame/support/test/tests/benchmark_ui/bad_param_range.rs @@ -1,4 +1,4 @@ -use frame_support::benchmarking::*; +use frame_benchmarking::v2::*; #[allow(unused_imports)] use frame_support_test::Config; diff --git a/frame/support/test/tests/benchmark_ui/bad_params.rs b/frame/support/test/tests/benchmark_ui/bad_params.rs index a0c923698..5049f2eae 100644 --- a/frame/support/test/tests/benchmark_ui/bad_params.rs +++ b/frame/support/test/tests/benchmark_ui/bad_params.rs @@ -1,4 +1,4 @@ -use frame_support::benchmarking::*; +use frame_benchmarking::v2::*; #[allow(unused_imports)] use frame_support_test::Config; diff --git a/frame/support/test/tests/benchmark_ui/dup_block.rs b/frame/support/test/tests/benchmark_ui/dup_block.rs index 4c4a8fc11..2c2ef9db9 100644 --- a/frame/support/test/tests/benchmark_ui/dup_block.rs +++ b/frame/support/test/tests/benchmark_ui/dup_block.rs @@ -1,4 +1,4 @@ -use frame_support::benchmarking::*; +use frame_benchmarking::v2::*; #[allow(unused_imports)] use frame_support_test::Config; diff --git a/frame/support/test/tests/benchmark_ui/dup_extrinsic_call.rs b/frame/support/test/tests/benchmark_ui/dup_extrinsic_call.rs index 1a91b7c16..4d135d1a0 100644 --- a/frame/support/test/tests/benchmark_ui/dup_extrinsic_call.rs +++ b/frame/support/test/tests/benchmark_ui/dup_extrinsic_call.rs @@ -1,4 +1,4 @@ -use frame_support::benchmarking::*; +use frame_benchmarking::v2::*; #[allow(unused_imports)] use frame_support_test::Config; diff --git a/frame/support/test/tests/benchmark_ui/extra_extra.rs b/frame/support/test/tests/benchmark_ui/extra_extra.rs index 021106c7a..1aa6c9ecb 100644 --- a/frame/support/test/tests/benchmark_ui/extra_extra.rs +++ b/frame/support/test/tests/benchmark_ui/extra_extra.rs @@ -1,4 +1,4 @@ -use frame_support::benchmarking::*; +use frame_benchmarking::v2::*; #[allow(unused_imports)] use frame_support_test::Config; diff --git a/frame/support/test/tests/benchmark_ui/extra_skip_meta.rs b/frame/support/test/tests/benchmark_ui/extra_skip_meta.rs index 1940f4cf1..3418c7af7 100644 --- a/frame/support/test/tests/benchmark_ui/extra_skip_meta.rs +++ b/frame/support/test/tests/benchmark_ui/extra_skip_meta.rs @@ -1,4 +1,4 @@ -use frame_support::benchmarking::*; +use frame_benchmarking::v2::*; #[allow(unused_imports)] use frame_support_test::Config; diff --git a/frame/support/test/tests/benchmark_ui/extrinsic_call_out_of_fn.rs b/frame/support/test/tests/benchmark_ui/extrinsic_call_out_of_fn.rs index 4cb6bfc34..ce360ee75 100644 --- a/frame/support/test/tests/benchmark_ui/extrinsic_call_out_of_fn.rs +++ b/frame/support/test/tests/benchmark_ui/extrinsic_call_out_of_fn.rs @@ -1,4 +1,4 @@ -use frame_support::benchmarking::*; +use frame_benchmarking::v2::*; #[extrinsic_call] mod stuff {} diff --git a/frame/support/test/tests/benchmark_ui/missing_call.rs b/frame/support/test/tests/benchmark_ui/missing_call.rs index 743704935..bc04101dd 100644 --- a/frame/support/test/tests/benchmark_ui/missing_call.rs +++ b/frame/support/test/tests/benchmark_ui/missing_call.rs @@ -1,4 +1,4 @@ -use frame_support::benchmarking::*; +use frame_benchmarking::v2::*; #[allow(unused_imports)] use frame_support_test::Config; diff --git a/frame/support/test/tests/benchmark_ui/missing_origin.rs b/frame/support/test/tests/benchmark_ui/missing_origin.rs index aad91bc79..2aaed756b 100644 --- a/frame/support/test/tests/benchmark_ui/missing_origin.rs +++ b/frame/support/test/tests/benchmark_ui/missing_origin.rs @@ -1,4 +1,4 @@ -use frame_support::benchmarking::*; +use frame_benchmarking::v2::*; #[allow(unused_imports)] use frame_support_test::Config; diff --git a/frame/support/test/tests/benchmark_ui/pass/valid_basic.rs b/frame/support/test/tests/benchmark_ui/pass/valid_basic.rs index 5c84d7f76..450ce4f9c 100644 --- a/frame/support/test/tests/benchmark_ui/pass/valid_basic.rs +++ b/frame/support/test/tests/benchmark_ui/pass/valid_basic.rs @@ -1,4 +1,4 @@ -use frame_support::benchmarking::*; +use frame_benchmarking::v2::*; use frame_support_test::Config; #[benchmarks] diff --git a/frame/support/test/tests/benchmark_ui/unrecognized_option.rs b/frame/support/test/tests/benchmark_ui/unrecognized_option.rs index 4c2cea139..18cae4d5d 100644 --- a/frame/support/test/tests/benchmark_ui/unrecognized_option.rs +++ b/frame/support/test/tests/benchmark_ui/unrecognized_option.rs @@ -1,4 +1,4 @@ -use frame_support::benchmarking::*; +use frame_benchmarking::v2::*; #[allow(unused_imports)] use frame_support_test::Config; diff --git a/frame/system/benchmarking/src/lib.rs b/frame/system/benchmarking/src/lib.rs index 0f7603fe1..87d3c7fc7 100644 --- a/frame/system/benchmarking/src/lib.rs +++ b/frame/system/benchmarking/src/lib.rs @@ -20,7 +20,7 @@ #![cfg_attr(not(feature = "std"), no_std)] use codec::Encode; -use frame_benchmarking::{benchmarks, whitelisted_caller}; +use frame_benchmarking::v1::{benchmarks, whitelisted_caller}; use frame_support::{dispatch::DispatchClass, storage, traits::Get}; use frame_system::{Call, Pallet as System, RawOrigin}; use sp_core::storage::well_known_keys; diff --git a/frame/timestamp/src/benchmarking.rs b/frame/timestamp/src/benchmarking.rs index 8974eb956..3a9297fda 100644 --- a/frame/timestamp/src/benchmarking.rs +++ b/frame/timestamp/src/benchmarking.rs @@ -20,7 +20,7 @@ #![cfg(feature = "runtime-benchmarks")] use super::*; -use frame_benchmarking::{benchmarks, TrackedStorageKey}; +use frame_benchmarking::v1::{benchmarks, TrackedStorageKey}; use frame_support::{ensure, traits::OnFinalize}; use frame_system::RawOrigin; diff --git a/frame/tips/src/benchmarking.rs b/frame/tips/src/benchmarking.rs index 312424e57..8ffc53b45 100644 --- a/frame/tips/src/benchmarking.rs +++ b/frame/tips/src/benchmarking.rs @@ -19,7 +19,7 @@ #![cfg(feature = "runtime-benchmarks")] -use frame_benchmarking::{account, benchmarks_instance_pallet, whitelisted_caller}; +use frame_benchmarking::v1::{account, benchmarks_instance_pallet, whitelisted_caller}; use frame_support::ensure; use frame_system::RawOrigin; use sp_runtime::traits::Saturating; diff --git a/frame/transaction-storage/src/benchmarking.rs b/frame/transaction-storage/src/benchmarking.rs index c7fbd00fb..736d2c40d 100644 --- a/frame/transaction-storage/src/benchmarking.rs +++ b/frame/transaction-storage/src/benchmarking.rs @@ -20,7 +20,7 @@ #![cfg(feature = "runtime-benchmarks")] use super::*; -use frame_benchmarking::{benchmarks, whitelisted_caller}; +use frame_benchmarking::v1::{benchmarks, whitelisted_caller}; use frame_support::traits::{Currency, Get, OnFinalize, OnInitialize}; use frame_system::{EventRecord, Pallet as System, RawOrigin}; use sp_runtime::traits::{Bounded, One, Zero}; diff --git a/frame/treasury/src/benchmarking.rs b/frame/treasury/src/benchmarking.rs index d718a5fb8..eb4474450 100644 --- a/frame/treasury/src/benchmarking.rs +++ b/frame/treasury/src/benchmarking.rs @@ -21,7 +21,7 @@ use super::{Pallet as Treasury, *}; -use frame_benchmarking::{account, benchmarks_instance_pallet}; +use frame_benchmarking::v1::{account, benchmarks_instance_pallet}; use frame_support::{ dispatch::UnfilteredDispatchable, ensure, diff --git a/frame/uniques/src/benchmarking.rs b/frame/uniques/src/benchmarking.rs index ab34558f9..5ecb093a5 100644 --- a/frame/uniques/src/benchmarking.rs +++ b/frame/uniques/src/benchmarking.rs @@ -20,7 +20,7 @@ #![cfg(feature = "runtime-benchmarks")] use super::*; -use frame_benchmarking::{ +use frame_benchmarking::v1::{ account, benchmarks_instance_pallet, whitelist_account, whitelisted_caller, }; use frame_support::{ diff --git a/frame/utility/src/benchmarking.rs b/frame/utility/src/benchmarking.rs index 07bc14951..f381b1e5f 100644 --- a/frame/utility/src/benchmarking.rs +++ b/frame/utility/src/benchmarking.rs @@ -20,7 +20,7 @@ #![cfg(feature = "runtime-benchmarks")] use super::*; -use frame_benchmarking::{account, benchmarks, whitelisted_caller}; +use frame_benchmarking::v1::{account, benchmarks, whitelisted_caller}; use frame_system::RawOrigin; const SEED: u32 = 0; diff --git a/frame/vesting/src/benchmarking.rs b/frame/vesting/src/benchmarking.rs index dde5fe3ac..eb0d596b8 100644 --- a/frame/vesting/src/benchmarking.rs +++ b/frame/vesting/src/benchmarking.rs @@ -19,7 +19,7 @@ #![cfg(feature = "runtime-benchmarks")] -use frame_benchmarking::{account, benchmarks, whitelisted_caller}; +use frame_benchmarking::v1::{account, benchmarks, whitelisted_caller}; use frame_support::assert_ok; use frame_system::{Pallet as System, RawOrigin}; use sp_runtime::traits::{Bounded, CheckedDiv, CheckedMul}; diff --git a/frame/whitelist/src/benchmarking.rs b/frame/whitelist/src/benchmarking.rs index 569b13c12..e64842b34 100644 --- a/frame/whitelist/src/benchmarking.rs +++ b/frame/whitelist/src/benchmarking.rs @@ -20,7 +20,7 @@ #![cfg(feature = "runtime-benchmarks")] use super::*; -use frame_benchmarking::benchmarks; +use frame_benchmarking::v1::benchmarks; use frame_support::{ensure, traits::EnsureOrigin}; #[cfg(test)] From 9762613a130235317467f66d6e6f46bfa30a5674 Mon Sep 17 00:00:00 2001 From: Matteo Muraca <56828990+muraca@users.noreply.github.com> Date: Sat, 28 Jan 2023 00:47:17 +0100 Subject: [PATCH 065/558] mutate_exists for StorageValue with ValueQuery (#13245) * mutate_exists for StorageValue with ValueQuery Signed-off-by: muraca * added `#[crate::storage_alias]` to tests Signed-off-by: muraca * added StorageEntryMetadata Signed-off-by: muraca * Update frame/support/src/lib.rs --------- Signed-off-by: muraca Co-authored-by: Oliver Tale-Yazdi --- frame/support/src/lib.rs | 63 ++++++++++++++++++++ frame/support/src/storage/generator/value.rs | 24 ++++++++ frame/support/src/storage/mod.rs | 6 ++ frame/support/src/storage/types/value.rs | 12 ++++ 4 files changed, 105 insertions(+) diff --git a/frame/support/src/lib.rs b/frame/support/src/lib.rs index 902893972..77ca89dc6 100644 --- a/frame/support/src/lib.rs +++ b/frame/support/src/lib.rs @@ -874,6 +874,7 @@ pub mod tests { decl_storage! { trait Store for Module as Test { + pub Value get(fn value): u64; pub Data get(fn data) build(|_| vec![(15u32, 42u64)]): map hasher(twox_64_concat) u32 => u64; pub OptionLinkedMap: map hasher(blake2_128_concat) u32 => Option; @@ -946,6 +947,61 @@ pub mod tests { }); } + #[test] + fn storage_value_mutate_exists_should_work() { + new_test_ext().execute_with(|| { + #[crate::storage_alias] + pub type Value = StorageValue; + + assert!(!Value::exists()); + + Value::mutate_exists(|v| *v = Some(1)); + assert!(Value::exists()); + assert_eq!(Value::get(), Some(1)); + + // removed if mutated to `None` + Value::mutate_exists(|v| *v = None); + assert!(!Value::exists()); + }); + } + + #[test] + fn storage_value_try_mutate_exists_should_work() { + new_test_ext().execute_with(|| { + #[crate::storage_alias] + pub type Value = StorageValue; + + type TestResult = result::Result<(), &'static str>; + + assert!(!Value::exists()); + + // mutated if `Ok` + assert_ok!(Value::try_mutate_exists(|v| -> TestResult { + *v = Some(1); + Ok(()) + })); + assert!(Value::exists()); + assert_eq!(Value::get(), Some(1)); + + // no-op if `Err` + assert_noop!( + Value::try_mutate_exists(|v| -> TestResult { + *v = Some(2); + Err("nah") + }), + "nah" + ); + assert_eq!(Value::get(), Some(1)); + + // removed if mutated to`None` + assert_ok!(Value::try_mutate_exists(|v| -> TestResult { + *v = None; + Ok(()) + })); + assert!(!Value::exists()); + }); + } + #[test] fn map_issue_3318() { new_test_ext().execute_with(|| { @@ -1257,6 +1313,13 @@ pub mod tests { PalletStorageMetadata { prefix: "Test", entries: vec![ + StorageEntryMetadata { + name: "Value", + modifier: StorageEntryModifier::Default, + ty: StorageEntryType::Plain(scale_info::meta_type::()), + default: vec![0, 0, 0, 0, 0, 0, 0, 0], + docs: vec![], + }, StorageEntryMetadata { name: "Data", modifier: StorageEntryModifier::Default, diff --git a/frame/support/src/storage/generator/value.rs b/frame/support/src/storage/generator/value.rs index 55b3487b1..4a1fd5c55 100644 --- a/frame/support/src/storage/generator/value.rs +++ b/frame/support/src/storage/generator/value.rs @@ -118,6 +118,30 @@ impl> storage::StorageValue for G { ret } + fn mutate_exists(f: F) -> R + where + F: FnOnce(&mut Option) -> R, + { + Self::try_mutate_exists(|v| Ok::(f(v))) + .expect("`Never` can not be constructed; qed") + } + + fn try_mutate_exists(f: F) -> Result + where + F: FnOnce(&mut Option) -> Result, + { + let mut val = G::from_query_to_optional_value(Self::get()); + + let ret = f(&mut val); + if ret.is_ok() { + match val { + Some(ref val) => Self::put(val), + None => Self::kill(), + } + } + ret + } + fn take() -> G::Query { let key = Self::storage_value_final_key(); let value = unhashed::get(&key); diff --git a/frame/support/src/storage/mod.rs b/frame/support/src/storage/mod.rs index 8c0d6207c..28f2dee99 100644 --- a/frame/support/src/storage/mod.rs +++ b/frame/support/src/storage/mod.rs @@ -114,6 +114,12 @@ pub trait StorageValue { /// Mutate the value if closure returns `Ok` fn try_mutate Result>(f: F) -> Result; + /// Mutate the value. Deletes the item if mutated to a `None`. + fn mutate_exists) -> R>(f: F) -> R; + + /// Mutate the value if closure returns `Ok`. Deletes the item if mutated to a `None`. + fn try_mutate_exists) -> Result>(f: F) -> Result; + /// Clear the storage value. fn kill(); diff --git a/frame/support/src/storage/types/value.rs b/frame/support/src/storage/types/value.rs index f145e9fb3..15290f1b1 100644 --- a/frame/support/src/storage/types/value.rs +++ b/frame/support/src/storage/types/value.rs @@ -142,6 +142,18 @@ where >::try_mutate(f) } + /// Mutate the value. Deletes the item if mutated to a `None`. + pub fn mutate_exists) -> R>(f: F) -> R { + >::mutate_exists(f) + } + + /// Mutate the value if closure returns `Ok`. Deletes the item if mutated to a `None`. + pub fn try_mutate_exists) -> Result>( + f: F, + ) -> Result { + >::try_mutate_exists(f) + } + /// Clear the storage value. pub fn kill() { >::kill() From 1e6b8921b771debd18de54aae55787c467baeddd Mon Sep 17 00:00:00 2001 From: Jeeyong Um Date: Sat, 28 Jan 2023 09:09:06 +0900 Subject: [PATCH 066/558] Fix derive PassByInner with generics (#13247) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Fix derive PassByInner with generics * Update primitives/runtime-interface/proc-macro/src/pass_by/inner.rs --------- Co-authored-by: Bastian Köcher --- primitives/runtime-interface/proc-macro/src/pass_by/inner.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/primitives/runtime-interface/proc-macro/src/pass_by/inner.rs b/primitives/runtime-interface/proc-macro/src/pass_by/inner.rs index 7a527af12..7c9bd63ab 100644 --- a/primitives/runtime-interface/proc-macro/src/pass_by/inner.rs +++ b/primitives/runtime-interface/proc-macro/src/pass_by/inner.rs @@ -52,7 +52,7 @@ pub fn derive_impl(mut input: DeriveInput) -> Result { #crate_include impl #impl_generics #crate_::pass_by::PassBy for #ident #ty_generics #where_clause { - type PassBy = #crate_::pass_by::Inner<#ident, #inner_ty>; + type PassBy = #crate_::pass_by::Inner; } impl #impl_generics #crate_::pass_by::PassByInner for #ident #ty_generics #where_clause { From ece32a72e934f6fe6705a7d418bbf3e71b4931ad Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jonathas=20Concei=C3=A7=C3=A3o?= Date: Fri, 27 Jan 2023 21:16:57 -0300 Subject: [PATCH 067/558] docs: Fix broken documentation links for pallets (#13241) Fixes broken links to `Config` and `Call`, for READMEs the link has been fixed by adding the missing "/pallet" path, and for rust docs we let the compiler figure out the type's link by itself Signed-off-by: Jonathas-Conceicao --- frame/multisig/README.md | 4 ++-- frame/multisig/src/lib.rs | 3 --- frame/nicks/README.md | 4 ++-- frame/proxy/README.md | 4 ++-- frame/sudo/README.md | 4 ++-- frame/utility/README.md | 4 ++-- frame/vesting/README.md | 4 ++-- 7 files changed, 12 insertions(+), 15 deletions(-) diff --git a/frame/multisig/README.md b/frame/multisig/README.md index 4eab00d10..5f320377d 100644 --- a/frame/multisig/README.md +++ b/frame/multisig/README.md @@ -1,8 +1,8 @@ # Multisig Module A module for doing multisig dispatch. -- [`multisig::Config`](https://docs.rs/pallet-multisig/latest/pallet_multisig/trait.Config.html) -- [`Call`](https://docs.rs/pallet-multisig/latest/pallet_multisig/enum.Call.html) +- [`Config`](https://docs.rs/pallet-multisig/latest/pallet_multisig/pallet/trait.Config.html) +- [`Call`](https://docs.rs/pallet-multisig/latest/pallet_multisig/pallet/enum.Call.html) ## Overview diff --git a/frame/multisig/src/lib.rs b/frame/multisig/src/lib.rs index 076a289e0..03d7ddec6 100644 --- a/frame/multisig/src/lib.rs +++ b/frame/multisig/src/lib.rs @@ -39,9 +39,6 @@ //! number of signed origins. //! * `approve_as_multi` - Approve a call from a composite origin. //! * `cancel_as_multi` - Cancel a call from a composite origin. -//! -//! [`Call`]: ./enum.Call.html -//! [`Config`]: ./trait.Config.html // Ensure we're `no_std` when compiling for Wasm. #![cfg_attr(not(feature = "std"), no_std)] diff --git a/frame/nicks/README.md b/frame/nicks/README.md index a2a897b04..768043ffb 100644 --- a/frame/nicks/README.md +++ b/frame/nicks/README.md @@ -1,7 +1,7 @@ # Nicks Module -- [`nicks::Config`](https://docs.rs/pallet-nicks/latest/pallet_nicks/trait.Config.html) -- [`Call`](https://docs.rs/pallet-nicks/latest/pallet_nicks/enum.Call.html) +- [`Config`](https://docs.rs/pallet-nicks/latest/pallet_nicks/pallet/trait.Config.html) +- [`Call`](https://docs.rs/pallet-nicks/latest/pallet_nicks/pallet/enum.Call.html) ## Overview diff --git a/frame/proxy/README.md b/frame/proxy/README.md index 2eb83fab6..bfe26d9ae 100644 --- a/frame/proxy/README.md +++ b/frame/proxy/README.md @@ -6,8 +6,8 @@ The accounts to which permission is delegated may be requied to announce the act wish to execute some duration prior to execution happens. In this case, the target account may reject the announcement and in doing so, veto the execution. -- [`proxy::Config`](https://docs.rs/pallet-proxy/latest/pallet_proxy/trait.Config.html) -- [`Call`](https://docs.rs/pallet-proxy/latest/pallet_proxy/enum.Call.html) +- [`Config`](https://docs.rs/pallet-proxy/latest/pallet_proxy/pallet/trait.Config.html) +- [`Call`](https://docs.rs/pallet-proxy/latest/pallet_proxy/pallet/enum.Call.html) ## Overview diff --git a/frame/sudo/README.md b/frame/sudo/README.md index 7342832d2..886dc5981 100644 --- a/frame/sudo/README.md +++ b/frame/sudo/README.md @@ -1,7 +1,7 @@ # Sudo Module -- [`sudo::Config`](https://docs.rs/pallet-sudo/latest/pallet_sudo/trait.Config.html) -- [`Call`](https://docs.rs/pallet-sudo/latest/pallet_sudo/enum.Call.html) +- [`Config`](https://docs.rs/pallet-sudo/latest/pallet_sudo/pallet/trait.Config.html) +- [`Call`](https://docs.rs/pallet-sudo/latest/pallet_sudo/pallet/enum.Call.html) ## Overview diff --git a/frame/utility/README.md b/frame/utility/README.md index 1beeb6673..db19b0cf8 100644 --- a/frame/utility/README.md +++ b/frame/utility/README.md @@ -1,8 +1,8 @@ # Utility Module A stateless module with helpers for dispatch management which does no re-authentication. -- [`utility::Config`](https://docs.rs/pallet-utility/latest/pallet_utility/trait.Config.html) -- [`Call`](https://docs.rs/pallet-utility/latest/pallet_utility/enum.Call.html) +- [`utility::Config`](https://docs.rs/pallet-utility/latest/pallet_utility/pallet/trait.Config.html) +- [`Call`](https://docs.rs/pallet-utility/latest/pallet_utility/pallet/enum.Call.html) ## Overview diff --git a/frame/vesting/README.md b/frame/vesting/README.md index b19a60c5b..1f3744d63 100644 --- a/frame/vesting/README.md +++ b/frame/vesting/README.md @@ -1,7 +1,7 @@ # Vesting Module -- [`vesting::Config`](https://docs.rs/pallet-vesting/latest/pallet_vesting/trait.Config.html) -- [`Call`](https://docs.rs/pallet-vesting/latest/pallet_vesting/enum.Call.html) +- [`Config`](https://docs.rs/pallet-vesting/latest/pallet_vesting/pallet/trait.Config.html) +- [`Call`](https://docs.rs/pallet-vesting/latest/pallet_vesting/pallet/enum.Call.html) ## Overview From 0100b3b96984f128a06f9701a3424a3a9cde26fb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bastian=20K=C3=B6cher?= Date: Sat, 28 Jan 2023 20:10:40 +0100 Subject: [PATCH 068/558] sc-finality-grandpa: Warp proof generation can not expect justifications (#13249) When a node is running with `--blocks-pruning` it will also prunes justifications. So, the warp proof generation can not use `expect` for unwrapping the justification. --- client/cli/src/params/pruning_params.rs | 1 + client/finality-grandpa/src/warp_proof.rs | 6 +----- 2 files changed, 2 insertions(+), 5 deletions(-) diff --git a/client/cli/src/params/pruning_params.rs b/client/cli/src/params/pruning_params.rs index fd01ba67b..66282fd08 100644 --- a/client/cli/src/params/pruning_params.rs +++ b/client/cli/src/params/pruning_params.rs @@ -49,6 +49,7 @@ pub struct PruningParams { /// [default: 256] #[arg(alias = "pruning", long, value_name = "PRUNING_MODE")] pub state_pruning: Option, + /// Specify the blocks pruning mode. /// /// This mode specifies when the block's body (including justifications) diff --git a/client/finality-grandpa/src/warp_proof.rs b/client/finality-grandpa/src/warp_proof.rs index c65a705d3..1169083ea 100644 --- a/client/finality-grandpa/src/warp_proof.rs +++ b/client/finality-grandpa/src/warp_proof.rs @@ -135,11 +135,7 @@ impl WarpSyncProof { let justification = blockchain .justifications(header.hash())? .and_then(|just| just.into_justification(GRANDPA_ENGINE_ID)) - .expect( - "header is last in set and contains standard change signal; \ - must have justification; \ - qed.", - ); + .ok_or_else(|| Error::MissingData)?; let justification = GrandpaJustification::::decode(&mut &justification[..])?; From 6c1119016ff6b35e8ea79594edf20c03a39e3740 Mon Sep 17 00:00:00 2001 From: Qinxuan Chen Date: Sun, 29 Jan 2023 03:43:32 +0800 Subject: [PATCH 069/558] update criterion to v0.4.0 (#13142) --- Cargo.lock | 129 ++++++++++++++--------------- bin/node/cli/Cargo.toml | 2 +- bin/node/executor/Cargo.toml | 2 +- client/db/Cargo.toml | 2 +- client/executor/Cargo.toml | 2 +- client/tracing/Cargo.toml | 2 +- client/transaction-pool/Cargo.toml | 2 +- frame/system/Cargo.toml | 2 +- primitives/api/test/Cargo.toml | 2 +- primitives/arithmetic/Cargo.toml | 2 +- primitives/core/Cargo.toml | 4 +- primitives/core/benches/bench.rs | 5 +- primitives/trie/Cargo.toml | 4 +- 13 files changed, 78 insertions(+), 82 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 6aafb0e17..c075f065f 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -167,6 +167,12 @@ dependencies = [ "libc", ] +[[package]] +name = "anes" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4b46cbb362ab8752921c97e041f5e366ee6297bd428a31275b9fcf1e380f7299" + [[package]] name = "ansi_term" version = "0.12.1" @@ -705,10 +711,7 @@ version = "0.2.17" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ba3569f383e8f1598449f1a423e72e99569137b47740b1da11ef19af3d5c3223" dependencies = [ - "lazy_static", "memchr", - "regex-automata", - "serde", ] [[package]] @@ -914,6 +917,33 @@ dependencies = [ "winapi", ] +[[package]] +name = "ciborium" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b0c137568cc60b904a7724001b35ce2630fd00d5d84805fbb608ab89509d788f" +dependencies = [ + "ciborium-io", + "ciborium-ll", + "serde", +] + +[[package]] +name = "ciborium-io" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "346de753af073cc87b52b2083a506b38ac176a44cfb05497b622e27be899b369" + +[[package]] +name = "ciborium-ll" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "213030a2b5a4e0c0892b6652260cf6ccac84827b83a85a534e178e3906c4cf1b" +dependencies = [ + "ciborium-io", + "half", +] + [[package]] name = "cid" version = "0.8.6" @@ -967,13 +997,14 @@ dependencies = [ [[package]] name = "clap" -version = "2.34.0" +version = "3.2.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a0610544180c38b88101fecf2dd634b174a62eef6946f84dfc6a7127512b381c" +checksum = "71655c45cb9845d3270c9d6df84ebe72b4dad3c2ba3f7023ad47c144e4e473a5" dependencies = [ "bitflags", + "clap_lex 0.2.4", + "indexmap", "textwrap", - "unicode-width", ] [[package]] @@ -984,7 +1015,7 @@ checksum = "a7db700bc935f9e43e88d00b0850dae18a63773cfbec6d8e070fccf7fef89a39" dependencies = [ "bitflags", "clap_derive", - "clap_lex", + "clap_lex 0.3.0", "is-terminal", "once_cell", "strsim", @@ -1013,6 +1044,15 @@ dependencies = [ "syn", ] +[[package]] +name = "clap_lex" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2850f2f5a82cbf437dd5af4d49848fbdfc27c157c3d010345776f952765261c5" +dependencies = [ + "os_str_bytes", +] + [[package]] name = "clap_lex" version = "0.3.0" @@ -1243,15 +1283,16 @@ dependencies = [ [[package]] name = "criterion" -version = "0.3.6" +version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b01d6de93b2b6c65e17c634a26653a29d107b3c98c607c765bf38d041531cd8f" +checksum = "e7c76e09c1aae2bc52b3d2f29e13c6572553b30c4aa1b8a49fd70de6412654cb" dependencies = [ + "anes", "atty", "cast", - "clap 2.34.0", + "ciborium", + "clap 3.2.23", "criterion-plot", - "csv", "futures", "itertools", "lazy_static", @@ -1261,7 +1302,6 @@ dependencies = [ "rayon", "regex", "serde", - "serde_cbor", "serde_derive", "serde_json", "tinytemplate", @@ -1271,9 +1311,9 @@ dependencies = [ [[package]] name = "criterion-plot" -version = "0.4.5" +version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2673cc8207403546f45f5fd319a974b1e6983ad1a3ee7e6041650013be041876" +checksum = "6b50826342786a51a89e2da3a28f1c32b06e387201bc2d19791f622c673706b1" dependencies = [ "cast", "itertools", @@ -1380,28 +1420,6 @@ dependencies = [ "subtle", ] -[[package]] -name = "csv" -version = "1.1.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "22813a6dc45b335f9bade10bf7271dc477e81113e89eb251a0bc2a8a81c536e1" -dependencies = [ - "bstr 0.2.17", - "csv-core", - "itoa 0.4.8", - "ryu", - "serde", -] - -[[package]] -name = "csv-core" -version = "0.1.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2b2466559f260f48ad25fe6317b3c8dac77b5bdb5763ac7d9d6103530663bc90" -dependencies = [ - "memchr", -] - [[package]] name = "ctor" version = "0.1.26" @@ -2976,7 +2994,7 @@ checksum = "75f43d41e26995c17e71ee126451dd3941010b0514a81a9d11f3b341debc2399" dependencies = [ "bytes", "fnv", - "itoa 1.0.5", + "itoa", ] [[package]] @@ -3029,7 +3047,7 @@ dependencies = [ "http-body", "httparse", "httpdate", - "itoa 1.0.5", + "itoa", "pin-project-lite 0.2.9", "socket2", "tokio", @@ -3277,12 +3295,6 @@ dependencies = [ "either", ] -[[package]] -name = "itoa" -version = "0.4.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b71991ff56294aa922b450139ee08b3bfc70982c6b2c7562771375cf73542dd4" - [[package]] name = "itoa" version = "1.0.5" @@ -5083,7 +5095,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a652d9771a63711fd3c3deb670acfbe5c30a4072e664d7a3bf5a9e1056ac72c3" dependencies = [ "arrayvec 0.7.2", - "itoa 1.0.5", + "itoa", ] [[package]] @@ -7188,7 +7200,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "83cd1b99916654a69008fd66b4f9397fbe08e6e51dfe23d4417acf5d3b8cb87c" dependencies = [ "dtoa", - "itoa 1.0.5", + "itoa", "parking_lot 0.12.1", "prometheus-client-derive-text-encode", ] @@ -9380,16 +9392,6 @@ dependencies = [ "serde_derive", ] -[[package]] -name = "serde_cbor" -version = "0.11.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2bef2ebfde456fb76bbcf9f59315333decc4fda0b2b44b420243c11e0f5ec1f5" -dependencies = [ - "half", - "serde", -] - [[package]] name = "serde_derive" version = "1.0.152" @@ -9407,7 +9409,7 @@ version = "1.0.91" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "877c235533714907a8c2464236f5c4b2a17262ef1bd71f38f35ea592c8da6883" dependencies = [ - "itoa 1.0.5", + "itoa", "ryu", "serde", ] @@ -10945,12 +10947,9 @@ checksum = "95059e91184749cb66be6dc994f67f182b6d897cb3df74a5bf66b5e709295fd8" [[package]] name = "textwrap" -version = "0.11.0" +version = "0.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d326610f408c7a4eb6f51c37c330e496b08506c9457c9d34287ecc38809fb060" -dependencies = [ - "unicode-width", -] +checksum = "222a222a5bfe1bba4a77b45ec488a741b3cb8872e5e499451fd7d0129c9c7c3d" [[package]] name = "thiserror" @@ -11024,7 +11023,7 @@ version = "0.3.17" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a561bf4617eebd33bca6434b988f39ed798e527f51a1e797d0ee4f61c0a38376" dependencies = [ - "itoa 1.0.5", + "itoa", "serde", "time-core", "time-macros", @@ -11338,9 +11337,9 @@ dependencies = [ [[package]] name = "trie-bench" -version = "0.33.0" +version = "0.34.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c5b26bd2cdd7641c5beb476b314c0cb1f629832bf21a6235f545e2d47bc9d05a" +checksum = "2fbb0a830db7c42ae97ce4e21b30e2cf9dbcc1b4f7853bd1aedad3d806c281d0" dependencies = [ "criterion", "hash-db", diff --git a/bin/node/cli/Cargo.toml b/bin/node/cli/Cargo.toml index b55b35c05..c529c7c9b 100644 --- a/bin/node/cli/Cargo.toml +++ b/bin/node/cli/Cargo.toml @@ -122,7 +122,7 @@ serde_json = "1.0" regex = "1.6.0" platforms = "2.0" soketto = "0.7.1" -criterion = { version = "0.3.5", features = ["async_tokio"] } +criterion = { version = "0.4.0", features = ["async_tokio"] } tokio = { version = "1.22.0", features = ["macros", "time", "parking_lot"] } tokio-util = { version = "0.7.4", features = ["compat"] } wait-timeout = "0.2" diff --git a/bin/node/executor/Cargo.toml b/bin/node/executor/Cargo.toml index edcc4df9c..ffa8d38ee 100644 --- a/bin/node/executor/Cargo.toml +++ b/bin/node/executor/Cargo.toml @@ -26,7 +26,7 @@ sp-tracing = { version = "6.0.0", path = "../../../primitives/tracing" } sp-trie = { version = "7.0.0", path = "../../../primitives/trie" } [dev-dependencies] -criterion = "0.3.0" +criterion = "0.4.0" futures = "0.3.21" wat = "1.0" frame-support = { version = "4.0.0-dev", path = "../../../frame/support" } diff --git a/client/db/Cargo.toml b/client/db/Cargo.toml index a9f5e28c7..7a62cb1ea 100644 --- a/client/db/Cargo.toml +++ b/client/db/Cargo.toml @@ -36,7 +36,7 @@ sp-state-machine = { version = "0.13.0", path = "../../primitives/state-machine" sp-trie = { version = "7.0.0", path = "../../primitives/trie" } [dev-dependencies] -criterion = "0.3.3" +criterion = "0.4.0" kvdb-rocksdb = "0.17.0" rand = "0.8.5" tempfile = "3.1.0" diff --git a/client/executor/Cargo.toml b/client/executor/Cargo.toml index 8bedcd3ed..46b72565a 100644 --- a/client/executor/Cargo.toml +++ b/client/executor/Cargo.toml @@ -46,7 +46,7 @@ sc-tracing = { version = "4.0.0-dev", path = "../tracing" } tracing-subscriber = "0.2.19" paste = "1.0" regex = "1.6.0" -criterion = "0.3" +criterion = "0.4.0" env_logger = "0.9" num_cpus = "1.13.1" tempfile = "3.3.0" diff --git a/client/tracing/Cargo.toml b/client/tracing/Cargo.toml index be6237a34..9312c04e9 100644 --- a/client/tracing/Cargo.toml +++ b/client/tracing/Cargo.toml @@ -39,7 +39,7 @@ sp-runtime = { version = "7.0.0", path = "../../primitives/runtime" } sp-tracing = { version = "6.0.0", path = "../../primitives/tracing" } [dev-dependencies] -criterion = "0.3" +criterion = "0.4.0" [[bench]] name = "bench" diff --git a/client/transaction-pool/Cargo.toml b/client/transaction-pool/Cargo.toml index bbb5ebae4..6338f8127 100644 --- a/client/transaction-pool/Cargo.toml +++ b/client/transaction-pool/Cargo.toml @@ -37,7 +37,7 @@ sp-transaction-pool = { version = "4.0.0-dev", path = "../../primitives/transact [dev-dependencies] array-bytes = "4.1" assert_matches = "1.3.0" -criterion = "0.3" +criterion = "0.4.0" sc-block-builder = { version = "0.10.0-dev", path = "../block-builder" } sp-consensus = { version = "0.10.0-dev", path = "../../primitives/consensus/common" } substrate-test-runtime = { version = "2.0.0", path = "../../test-utils/runtime" } diff --git a/frame/system/Cargo.toml b/frame/system/Cargo.toml index 7f573a9cb..5a6c89aae 100644 --- a/frame/system/Cargo.toml +++ b/frame/system/Cargo.toml @@ -26,7 +26,7 @@ sp-version = { version = "5.0.0", default-features = false, path = "../../primit sp-weights = { version = "4.0.0", default-features = false, path = "../../primitives/weights" } [dev-dependencies] -criterion = "0.3.3" +criterion = "0.4.0" sp-externalities = { version = "0.13.0", path = "../../primitives/externalities" } substrate-test-runtime-client = { version = "2.0.0", path = "../../test-utils/runtime/client" } diff --git a/primitives/api/test/Cargo.toml b/primitives/api/test/Cargo.toml index b3ceb14fe..5b6c144ef 100644 --- a/primitives/api/test/Cargo.toml +++ b/primitives/api/test/Cargo.toml @@ -25,7 +25,7 @@ trybuild = "1.0.74" rustversion = "1.0.6" [dev-dependencies] -criterion = "0.3.0" +criterion = "0.4.0" futures = "0.3.21" log = "0.4.17" sp-core = { version = "7.0.0", path = "../../core" } diff --git a/primitives/arithmetic/Cargo.toml b/primitives/arithmetic/Cargo.toml index bbb4bbd17..b3fc6abc3 100644 --- a/primitives/arithmetic/Cargo.toml +++ b/primitives/arithmetic/Cargo.toml @@ -26,7 +26,7 @@ static_assertions = "1.1.0" sp-std = { version = "5.0.0", default-features = false, path = "../std" } [dev-dependencies] -criterion = "0.3" +criterion = "0.4.0" primitive-types = "0.12.0" sp-core = { version = "7.0.0", features = ["full_crypto"], path = "../core" } rand = "0.8.5" diff --git a/primitives/core/Cargo.toml b/primitives/core/Cargo.toml index f9a522178..5fd838de3 100644 --- a/primitives/core/Cargo.toml +++ b/primitives/core/Cargo.toml @@ -25,7 +25,7 @@ impl-serde = { version = "0.4.0", optional = true } hash-db = { version = "0.15.2", default-features = false } hash256-std-hasher = { version = "0.15.2", default-features = false } base58 = { version = "0.2.0", optional = true } -rand = { version = "0.8.5", optional = true, features = ["small_rng"] } +rand = { version = "0.8.5", features = ["small_rng"], optional = true } substrate-bip39 = { version = "0.4.4", optional = true } tiny-bip39 = { version = "1.0.0", optional = true } regex = { version = "1.6.0", optional = true } @@ -60,7 +60,7 @@ sp-runtime-interface = { version = "7.0.0", default-features = false, path = ".. [dev-dependencies] sp-serializer = { version = "4.0.0-dev", path = "../serializer" } rand = "0.8.5" -criterion = "0.3.3" +criterion = "0.4.0" serde_json = "1.0" sp-core-hashing-proc-macro = { version = "5.0.0", path = "./hashing/proc-macro" } diff --git a/primitives/core/benches/bench.rs b/primitives/core/benches/bench.rs index 53421278d..77e5bb63f 100644 --- a/primitives/core/benches/bench.rs +++ b/primitives/core/benches/bench.rs @@ -12,10 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -#[macro_use] -extern crate criterion; - -use criterion::{black_box, Bencher, BenchmarkId, Criterion}; +use criterion::{black_box, criterion_group, criterion_main, Bencher, BenchmarkId, Criterion}; use sp_core::{ crypto::Pair as _, hashing::{blake2_128, twox_128}, diff --git a/primitives/trie/Cargo.toml b/primitives/trie/Cargo.toml index 78b0b5d1b..1e83a5d6d 100644 --- a/primitives/trie/Cargo.toml +++ b/primitives/trie/Cargo.toml @@ -37,8 +37,8 @@ schnellru = { version = "0.2.1", optional = true } [dev-dependencies] array-bytes = "4.1" -criterion = "0.3.3" -trie-bench = "0.33.0" +criterion = "0.4.0" +trie-bench = "0.34.0" trie-standardmap = "0.15.2" sp-runtime = { version = "7.0.0", path = "../runtime" } From f4caddb2af951cac4ef3fcceb8079aa539f97af9 Mon Sep 17 00:00:00 2001 From: Shawn Tabrizi Date: Sun, 29 Jan 2023 13:23:15 -0300 Subject: [PATCH 070/558] fix up template (#13205) --- bin/node-template/pallets/template/Cargo.toml | 6 +++--- bin/node-template/pallets/template/src/mock.rs | 5 ++--- 2 files changed, 5 insertions(+), 6 deletions(-) diff --git a/bin/node-template/pallets/template/Cargo.toml b/bin/node-template/pallets/template/Cargo.toml index 6dec5c8da..f6607b25c 100644 --- a/bin/node-template/pallets/template/Cargo.toml +++ b/bin/node-template/pallets/template/Cargo.toml @@ -22,9 +22,9 @@ frame-support = { version = "4.0.0-dev", default-features = false, path = "../.. frame-system = { version = "4.0.0-dev", default-features = false, path = "../../../../frame/system" } [dev-dependencies] -sp-core = { version = "7.0.0", default-features = false, path = "../../../../primitives/core" } -sp-io = { version = "7.0.0", default-features = false, path = "../../../../primitives/io" } -sp-runtime = { version = "7.0.0", default-features = false, path = "../../../../primitives/runtime" } +sp-core = { version = "7.0.0", path = "../../../../primitives/core" } +sp-io = { version = "7.0.0", path = "../../../../primitives/io" } +sp-runtime = { version = "7.0.0", path = "../../../../primitives/runtime" } [features] default = ["std"] diff --git a/bin/node-template/pallets/template/src/mock.rs b/bin/node-template/pallets/template/src/mock.rs index 989681fa5..91a1bf6ed 100644 --- a/bin/node-template/pallets/template/src/mock.rs +++ b/bin/node-template/pallets/template/src/mock.rs @@ -1,6 +1,5 @@ use crate as pallet_template; use frame_support::traits::{ConstU16, ConstU64}; -use frame_system as system; use sp_core::H256; use sp_runtime::{ testing::Header, @@ -22,7 +21,7 @@ frame_support::construct_runtime!( } ); -impl system::Config for Test { +impl frame_system::Config for Test { type BaseCallFilter = frame_support::traits::Everything; type BlockWeights = (); type BlockLength = (); @@ -55,5 +54,5 @@ impl pallet_template::Config for Test { // Build genesis storage according to the mock runtime. pub fn new_test_ext() -> sp_io::TestExternalities { - system::GenesisConfig::default().build_storage::().unwrap().into() + frame_system::GenesisConfig::default().build_storage::().unwrap().into() } From fdff2b23009f25dd42d3a12fee6c998864399de3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bastian=20K=C3=B6cher?= Date: Sun, 29 Jan 2023 21:56:10 +0100 Subject: [PATCH 071/558] Remove `uncles` related code (#13216) The code was added without any clear usage. The inherent for example is not benchmarked and not used. --- Cargo.lock | 24 - Cargo.toml | 2 - bin/node/cli/Cargo.toml | 2 - bin/node/cli/src/service.rs | 12 +- bin/node/runtime/src/lib.rs | 6 - client/consensus/uncles/Cargo.toml | 19 - client/consensus/uncles/README.md | 3 - client/consensus/uncles/src/lib.rs | 45 -- frame/authorship/Cargo.toml | 2 - frame/authorship/src/lib.rs | 602 +----------------- frame/babe/src/mock.rs | 20 +- frame/grandpa/src/mock.rs | 20 +- frame/im-online/src/lib.rs | 4 - frame/im-online/src/mock.rs | 2 - frame/im-online/src/tests.rs | 5 +- frame/staking/src/mock.rs | 18 +- frame/staking/src/pallet/impls.rs | 10 - frame/staking/src/tests.rs | 9 +- .../asset-tx-payment/src/mock.rs | 14 +- primitives/authorship/Cargo.toml | 30 - primitives/authorship/README.md | 3 - primitives/authorship/src/lib.rs | 101 --- 22 files changed, 47 insertions(+), 906 deletions(-) delete mode 100644 client/consensus/uncles/Cargo.toml delete mode 100644 client/consensus/uncles/README.md delete mode 100644 client/consensus/uncles/src/lib.rs delete mode 100644 primitives/authorship/Cargo.toml delete mode 100644 primitives/authorship/README.md delete mode 100644 primitives/authorship/src/lib.rs diff --git a/Cargo.lock b/Cargo.lock index c075f065f..29ddf3832 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4772,7 +4772,6 @@ dependencies = [ "sc-consensus-babe", "sc-consensus-epochs", "sc-consensus-slots", - "sc-consensus-uncles", "sc-executor", "sc-finality-grandpa", "sc-keystore", @@ -4792,7 +4791,6 @@ dependencies = [ "soketto", "sp-api", "sp-authority-discovery", - "sp-authorship", "sp-blockchain", "sp-consensus", "sp-consensus-babe", @@ -5379,7 +5377,6 @@ dependencies = [ "impl-trait-for-tuples", "parity-scale-codec", "scale-info", - "sp-authorship", "sp-core", "sp-io", "sp-runtime", @@ -8299,16 +8296,6 @@ dependencies = [ "substrate-test-runtime-client", ] -[[package]] -name = "sc-consensus-uncles" -version = "0.10.0-dev" -dependencies = [ - "sc-client-api", - "sp-authorship", - "sp-runtime", - "thiserror", -] - [[package]] name = "sc-executor" version = "0.10.0-dev" @@ -9714,17 +9701,6 @@ dependencies = [ "sp-std", ] -[[package]] -name = "sp-authorship" -version = "4.0.0-dev" -dependencies = [ - "async-trait", - "parity-scale-codec", - "sp-inherents", - "sp-runtime", - "sp-std", -] - [[package]] name = "sp-beefy" version = "4.0.0-dev" diff --git a/Cargo.toml b/Cargo.toml index bb5230516..b004886e8 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -32,7 +32,6 @@ members = [ "client/consensus/manual-seal", "client/consensus/pow", "client/consensus/slots", - "client/consensus/uncles", "client/db", "client/executor", "client/executor/common", @@ -177,7 +176,6 @@ members = [ "primitives/arithmetic", "primitives/arithmetic/fuzzer", "primitives/authority-discovery", - "primitives/authorship", "primitives/beefy", "primitives/block-builder", "primitives/blockchain", diff --git a/bin/node/cli/Cargo.toml b/bin/node/cli/Cargo.toml index c529c7c9b..9ec06d9d9 100644 --- a/bin/node/cli/Cargo.toml +++ b/bin/node/cli/Cargo.toml @@ -52,7 +52,6 @@ sp-api = { version = "4.0.0-dev", path = "../../../primitives/api" } sp-core = { version = "7.0.0", path = "../../../primitives/core" } sp-runtime = { version = "7.0.0", path = "../../../primitives/runtime" } sp-timestamp = { version = "4.0.0-dev", path = "../../../primitives/timestamp" } -sp-authorship = { version = "4.0.0-dev", path = "../../../primitives/authorship" } sp-inherents = { version = "4.0.0-dev", path = "../../../primitives/inherents" } sp-keyring = { version = "7.0.0", path = "../../../primitives/keyring" } sp-keystore = { version = "0.13.0", path = "../../../primitives/keystore" } @@ -71,7 +70,6 @@ sc-network = { version = "0.10.0-dev", path = "../../../client/network" } sc-network-common = { version = "0.10.0-dev", path = "../../../client/network/common" } sc-consensus-slots = { version = "0.10.0-dev", path = "../../../client/consensus/slots" } sc-consensus-babe = { version = "0.10.0-dev", path = "../../../client/consensus/babe" } -sc-consensus-uncles = { version = "0.10.0-dev", path = "../../../client/consensus/uncles" } grandpa = { version = "0.10.0-dev", package = "sc-finality-grandpa", path = "../../../client/finality-grandpa" } sc-rpc = { version = "4.0.0-dev", path = "../../../client/rpc" } sc-basic-authorship = { version = "0.10.0-dev", path = "../../../client/basic-authorship" } diff --git a/bin/node/cli/src/service.rs b/bin/node/cli/src/service.rs index e32908794..b1e9360b4 100644 --- a/bin/node/cli/src/service.rs +++ b/bin/node/cli/src/service.rs @@ -222,10 +222,7 @@ pub fn new_partial( slot_duration, ); - let uncles = - sp_authorship::InherentDataProvider::<::Header>::check_inherents(); - - Ok((slot, timestamp, uncles)) + Ok((slot, timestamp)) }, &task_manager.spawn_essential_handle(), config.prometheus_registry(), @@ -440,11 +437,6 @@ pub fn new_full_base( create_inherent_data_providers: move |parent, ()| { let client_clone = client_clone.clone(); async move { - let uncles = sc_consensus_uncles::create_uncles_inherent_data_provider( - &*client_clone, - parent, - )?; - let timestamp = sp_timestamp::InherentDataProvider::from_system_time(); let slot = @@ -459,7 +451,7 @@ pub fn new_full_base( &parent, )?; - Ok((slot, timestamp, uncles, storage_proof)) + Ok((slot, timestamp, storage_proof)) } }, force_authoring, diff --git a/bin/node/runtime/src/lib.rs b/bin/node/runtime/src/lib.rs index bd8a395d1..30165cdb6 100644 --- a/bin/node/runtime/src/lib.rs +++ b/bin/node/runtime/src/lib.rs @@ -488,14 +488,8 @@ impl pallet_timestamp::Config for Runtime { type WeightInfo = pallet_timestamp::weights::SubstrateWeight; } -parameter_types! { - pub const UncleGenerations: BlockNumber = 5; -} - impl pallet_authorship::Config for Runtime { type FindAuthor = pallet_session::FindAccountFromAuthorIndex; - type UncleGenerations = UncleGenerations; - type FilterUncle = (); type EventHandler = (Staking, ImOnline); } diff --git a/client/consensus/uncles/Cargo.toml b/client/consensus/uncles/Cargo.toml deleted file mode 100644 index 0be48659f..000000000 --- a/client/consensus/uncles/Cargo.toml +++ /dev/null @@ -1,19 +0,0 @@ -[package] -name = "sc-consensus-uncles" -version = "0.10.0-dev" -authors = ["Parity Technologies "] -description = "Generic uncle inclusion utilities for consensus" -edition = "2021" -license = "GPL-3.0-or-later WITH Classpath-exception-2.0" -homepage = "https://substrate.io" -repository = "https://github.com/paritytech/substrate/" -readme = "README.md" - -[package.metadata.docs.rs] -targets = ["x86_64-unknown-linux-gnu"] - -[dependencies] -thiserror = "1.0.30" -sc-client-api = { version = "4.0.0-dev", path = "../../api" } -sp-authorship = { version = "4.0.0-dev", path = "../../../primitives/authorship" } -sp-runtime = { version = "7.0.0", path = "../../../primitives/runtime" } diff --git a/client/consensus/uncles/README.md b/client/consensus/uncles/README.md deleted file mode 100644 index 1b6fed5b9..000000000 --- a/client/consensus/uncles/README.md +++ /dev/null @@ -1,3 +0,0 @@ -Uncles functionality for Substrate. - -License: GPL-3.0-or-later WITH Classpath-exception-2.0 \ No newline at end of file diff --git a/client/consensus/uncles/src/lib.rs b/client/consensus/uncles/src/lib.rs deleted file mode 100644 index d03c2f8aa..000000000 --- a/client/consensus/uncles/src/lib.rs +++ /dev/null @@ -1,45 +0,0 @@ -// This file is part of Substrate. - -// Copyright (C) 2019-2022 Parity Technologies (UK) Ltd. -// SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 - -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with this program. If not, see . - -//! Uncles functionality for Substrate. - -use sc_client_api::ProvideUncles; -use sp_runtime::{generic::BlockId, traits::Block as BlockT}; - -#[derive(Debug, thiserror::Error)] -pub enum Error { - #[error("Could not retrieve the block hash for block id: {0:?}")] - NoHashForBlockId(BlockId), -} - -/// Maximum uncles generations we may provide to the runtime. -const MAX_UNCLE_GENERATIONS: u32 = 8; - -/// Create a new [`sp_authorship::InherentDataProvider`] at the given block. -pub fn create_uncles_inherent_data_provider( - client: &C, - parent: B::Hash, -) -> Result, sc_client_api::blockchain::Error> -where - B: BlockT, - C: ProvideUncles, -{ - let uncles = client.uncles(parent, MAX_UNCLE_GENERATIONS.into())?; - - Ok(sp_authorship::InherentDataProvider::new(uncles)) -} diff --git a/frame/authorship/Cargo.toml b/frame/authorship/Cargo.toml index 84d00a6fb..a205c51f9 100644 --- a/frame/authorship/Cargo.toml +++ b/frame/authorship/Cargo.toml @@ -20,7 +20,6 @@ impl-trait-for-tuples = "0.2.2" scale-info = { version = "2.1.1", default-features = false, features = ["derive"] } frame-support = { version = "4.0.0-dev", default-features = false, path = "../support" } frame-system = { version = "4.0.0-dev", default-features = false, path = "../system" } -sp-authorship = { version = "4.0.0-dev", default-features = false, path = "../../primitives/authorship" } sp-runtime = { version = "7.0.0", default-features = false, path = "../../primitives/runtime" } sp-std = { version = "5.0.0", default-features = false, path = "../../primitives/std" } @@ -35,7 +34,6 @@ std = [ "frame-support/std", "frame-system/std", "scale-info/std", - "sp-authorship/std", "sp-runtime/std", "sp-std/std", ] diff --git a/frame/authorship/src/lib.rs b/frame/authorship/src/lib.rs index 4065472d9..b5568a6ac 100644 --- a/frame/authorship/src/lib.rs +++ b/frame/authorship/src/lib.rs @@ -17,36 +17,12 @@ //! Authorship tracking for FRAME runtimes. //! -//! This tracks the current author of the block and recent uncles. +//! This tracks the current author of the block. #![cfg_attr(not(feature = "std"), no_std)] -use codec::{Decode, Encode, MaxEncodedLen}; -use frame_support::{ - dispatch, - traits::{Defensive, FindAuthor, Get, VerifySeal}, - BoundedSlice, BoundedVec, -}; -use sp_authorship::{InherentError, UnclesInherentData, INHERENT_IDENTIFIER}; -use sp_runtime::traits::{Header as HeaderT, One, Saturating, UniqueSaturatedInto}; -use sp_std::{collections::btree_set::BTreeSet, prelude::*, result}; - -const MAX_UNCLES: usize = 10; - -struct MaxUncleEntryItems(core::marker::PhantomData); -impl Get for MaxUncleEntryItems { - fn get() -> u32 { - // There can be at most `MAX_UNCLES` of `UncleEntryItem::Uncle` and - // one `UncleEntryItem::InclusionHeight` per one `UncleGenerations`, - // so this gives us `MAX_UNCLES + 1` entries per one generation. - // - // There can be one extra generation worth of uncles (e.g. even - // if `UncleGenerations` is zero the pallet will still hold - // one generation worth of uncles). - let max_generations: u32 = T::UncleGenerations::get().unique_saturated_into(); - (MAX_UNCLES as u32 + 1) * (max_generations + 1) - } -} +use frame_support::traits::FindAuthor; +use sp_std::prelude::*; pub use pallet::*; @@ -56,87 +32,8 @@ pub use pallet::*; pub trait EventHandler { /// Note that the given account ID is the author of the current block. fn note_author(author: Author); - - /// Note that the given account ID authored the given uncle, and how many - /// blocks older than the current block it is (age >= 0, so siblings are allowed) - fn note_uncle(author: Author, age: BlockNumber); -} - -/// Additional filtering on uncles that pass preliminary ancestry checks. -/// -/// This should do work such as checking seals -pub trait FilterUncle { - /// An accumulator of data about uncles included. - /// - /// In practice, this is used to validate uncles against others in the same block. - type Accumulator: Default; - - /// Do additional filtering on a seal-checked uncle block, with the accumulated - /// filter. - fn filter_uncle( - header: &Header, - acc: &mut Self::Accumulator, - ) -> Result, &'static str>; } -impl FilterUncle for () { - type Accumulator = (); - fn filter_uncle(_: &H, _acc: &mut Self::Accumulator) -> Result, &'static str> { - Ok(None) - } -} - -/// A filter on uncles which verifies seals and does no additional checks. -/// This is well-suited to consensus modes such as PoW where the cost of -/// equivocating is high. -pub struct SealVerify(sp_std::marker::PhantomData); - -impl> FilterUncle for SealVerify { - type Accumulator = (); - - fn filter_uncle(header: &Header, _acc: &mut ()) -> Result, &'static str> { - T::verify_seal(header) - } -} - -/// A filter on uncles which verifies seals and ensures that there is only -/// one uncle included per author per height. -/// -/// This does O(n log n) work in the number of uncles included. -pub struct OnePerAuthorPerHeight(sp_std::marker::PhantomData<(T, N)>); - -impl FilterUncle for OnePerAuthorPerHeight -where - Header: HeaderT + PartialEq, - Header::Number: Ord, - Author: Clone + PartialEq + Ord, - T: VerifySeal, -{ - type Accumulator = BTreeSet<(Header::Number, Author)>; - - fn filter_uncle( - header: &Header, - acc: &mut Self::Accumulator, - ) -> Result, &'static str> { - let author = T::verify_seal(header)?; - let number = header.number(); - - if let Some(ref author) = author { - if !acc.insert((*number, author.clone())) { - return Err("more than one uncle per number per author included") - } - } - - Ok(author) - } -} - -#[derive(Encode, Decode, sp_runtime::RuntimeDebug, scale_info::TypeInfo, MaxEncodedLen)] -#[cfg_attr(any(feature = "std", test), derive(PartialEq))] -enum UncleEntryItem { - InclusionHeight(BlockNumber), - Uncle(Hash, Option), -} #[frame_support::pallet] pub mod pallet { use super::*; @@ -147,25 +44,6 @@ pub mod pallet { pub trait Config: frame_system::Config { /// Find the author of a block. type FindAuthor: FindAuthor; - /// The number of blocks back we should accept uncles. - /// This means that we will deal with uncle-parents that are - /// `UncleGenerations + 1` before `now`. - #[pallet::constant] - type UncleGenerations: Get; - /// A filter for uncles within a block. This is for implementing - /// further constraints on what uncles can be included, other than their ancestry. - /// - /// For PoW, as long as the seals are checked, there is no need to use anything - /// but the `VerifySeal` implementation as the filter. This is because the cost of making - /// many equivocating uncles is high. - /// - /// For PoS, there is no such limitation, so a further constraint must be imposed - /// beyond a seal check in order to prevent an arbitrary number of - /// equivocating uncles from being included. - /// - /// The `OnePerAuthorPerHeight` filter is good for many slot-based PoS - /// engines. - type FilterUncle: FilterUncle; /// An event handler for authored blocks. type EventHandler: EventHandler; } @@ -176,16 +54,7 @@ pub mod pallet { #[pallet::hooks] impl Hooks> for Pallet { - fn on_initialize(now: T::BlockNumber) -> Weight { - let uncle_generations = T::UncleGenerations::get(); - // prune uncles that are older than the allowed number of generations. - if uncle_generations <= now { - let minimum_height = now - uncle_generations; - Self::prune_old_uncles(minimum_height) - } - - >::put(false); - + fn on_initialize(_: T::BlockNumber) -> Weight { if let Some(author) = Self::author() { T::EventHandler::note_author(author); } @@ -196,125 +65,12 @@ pub mod pallet { fn on_finalize(_: T::BlockNumber) { // ensure we never go to trie with these values. >::kill(); - >::kill(); } } - #[pallet::storage] - /// Uncles - pub(super) type Uncles = StorageValue< - _, - BoundedVec, MaxUncleEntryItems>, - ValueQuery, - >; - #[pallet::storage] /// Author of current block. pub(super) type Author = StorageValue<_, T::AccountId, OptionQuery>; - - #[pallet::storage] - /// Whether uncles were already set in this block. - pub(super) type DidSetUncles = StorageValue<_, bool, ValueQuery>; - - #[pallet::error] - pub enum Error { - /// The uncle parent not in the chain. - InvalidUncleParent, - /// Uncles already set in the block. - UnclesAlreadySet, - /// Too many uncles. - TooManyUncles, - /// The uncle is genesis. - GenesisUncle, - /// The uncle is too high in chain. - TooHighUncle, - /// The uncle is already included. - UncleAlreadyIncluded, - /// The uncle isn't recent enough to be included. - OldUncle, - } - - #[pallet::call] - impl Pallet { - /// Provide a set of uncles. - #[pallet::call_index(0)] - #[pallet::weight((0, DispatchClass::Mandatory))] - pub fn set_uncles(origin: OriginFor, new_uncles: Vec) -> DispatchResult { - ensure_none(origin)?; - ensure!(new_uncles.len() <= MAX_UNCLES, Error::::TooManyUncles); - - if >::get() { - return Err(Error::::UnclesAlreadySet.into()) - } - >::put(true); - - Self::verify_and_import_uncles(new_uncles) - } - } - - #[pallet::inherent] - impl ProvideInherent for Pallet { - type Call = Call; - type Error = InherentError; - const INHERENT_IDENTIFIER: InherentIdentifier = INHERENT_IDENTIFIER; - - fn create_inherent(data: &InherentData) -> Option { - let uncles = data.uncles().unwrap_or_default(); - let mut new_uncles = Vec::new(); - - if !uncles.is_empty() { - let prev_uncles = >::get(); - let mut existing_hashes: Vec<_> = prev_uncles - .into_iter() - .filter_map(|entry| match entry { - UncleEntryItem::InclusionHeight(_) => None, - UncleEntryItem::Uncle(h, _) => Some(h), - }) - .collect(); - - let mut acc: >::Accumulator = - Default::default(); - - for uncle in uncles { - match Self::verify_uncle(&uncle, &existing_hashes, &mut acc) { - Ok(_) => { - let hash = uncle.hash(); - new_uncles.push(uncle); - existing_hashes.push(hash); - - if new_uncles.len() == MAX_UNCLES { - break - } - }, - Err(_) => { - // skip this uncle - }, - } - } - } - - if new_uncles.is_empty() { - None - } else { - Some(Call::set_uncles { new_uncles }) - } - } - - fn check_inherent( - call: &Self::Call, - _data: &InherentData, - ) -> result::Result<(), Self::Error> { - match call { - Call::set_uncles { ref new_uncles } if new_uncles.len() > MAX_UNCLES => - Err(InherentError::Uncles(Error::::TooManyUncles.as_str().into())), - _ => Ok(()), - } - } - - fn is_inherent(call: &Self::Call) -> bool { - matches!(call, Call::set_uncles { .. }) - } - } } impl Pallet { @@ -323,7 +79,7 @@ impl Pallet { /// This is safe to invoke in `on_initialize` implementations, as well /// as afterwards. pub fn author() -> Option { - // Check the memoized storage value. + // Check the memorized storage value. if let Some(author) = >::get() { return Some(author) } @@ -335,113 +91,22 @@ impl Pallet { a }) } - - fn verify_and_import_uncles(new_uncles: Vec) -> dispatch::DispatchResult { - let now = >::block_number(); - - let mut uncles = >::get(); - uncles - .try_push(UncleEntryItem::InclusionHeight(now)) - .defensive_proof("the list of uncles accepted per generation is bounded, and the number of generations is bounded, so pushing a new element will always succeed") - .map_err(|_| Error::::TooManyUncles)?; - - let mut acc: >::Accumulator = Default::default(); - - for uncle in new_uncles { - let prev_uncles = uncles.iter().filter_map(|entry| match entry { - UncleEntryItem::InclusionHeight(_) => None, - UncleEntryItem::Uncle(h, _) => Some(h), - }); - let maybe_author = Self::verify_uncle(&uncle, prev_uncles, &mut acc)?; - let hash = uncle.hash(); - - if let Some(author) = maybe_author.clone() { - T::EventHandler::note_uncle(author, now - *uncle.number()); - } - uncles.try_push(UncleEntryItem::Uncle(hash, maybe_author)) - .defensive_proof("the list of uncles accepted per generation is bounded, and the number of generations is bounded, so pushing a new element will always succeed") - .map_err(|_| Error::::TooManyUncles)?; - } - - >::put(&uncles); - Ok(()) - } - - fn verify_uncle<'a, I: IntoIterator>( - uncle: &T::Header, - existing_uncles: I, - accumulator: &mut >::Accumulator, - ) -> Result, dispatch::DispatchError> { - let now = >::block_number(); - - let (minimum_height, maximum_height) = { - let uncle_generations = T::UncleGenerations::get(); - let min = now.saturating_sub(uncle_generations); - - (min, now) - }; - - let hash = uncle.hash(); - - if uncle.number() < &One::one() { - return Err(Error::::GenesisUncle.into()) - } - - if uncle.number() > &maximum_height { - return Err(Error::::TooHighUncle.into()) - } - - { - let parent_number = *uncle.number() - One::one(); - let parent_hash = >::block_hash(&parent_number); - if &parent_hash != uncle.parent_hash() { - return Err(Error::::InvalidUncleParent.into()) - } - } - - if uncle.number() < &minimum_height { - return Err(Error::::OldUncle.into()) - } - - let duplicate = existing_uncles.into_iter().any(|h| *h == hash); - let in_chain = >::block_hash(uncle.number()) == hash; - - if duplicate || in_chain { - return Err(Error::::UncleAlreadyIncluded.into()) - } - - // check uncle validity. - T::FilterUncle::filter_uncle(uncle, accumulator).map_err(Into::into) - } - - fn prune_old_uncles(minimum_height: T::BlockNumber) { - let uncles = >::get(); - let prune_entries = uncles.iter().take_while(|item| match item { - UncleEntryItem::Uncle(_, _) => true, - UncleEntryItem::InclusionHeight(height) => height < &minimum_height, - }); - let prune_index = prune_entries.count(); - let pruned_uncles = - >>::try_from(&uncles[prune_index..]) - .expect("after pruning we can't end up with more uncles than we started with"); - - >::put(pruned_uncles); - } } #[cfg(test)] mod tests { use super::*; use crate as pallet_authorship; + use codec::{Decode, Encode}; use frame_support::{ - traits::{ConstU32, ConstU64, OnFinalize, OnInitialize}, + traits::{ConstU32, ConstU64}, ConsensusEngineId, }; use sp_core::H256; use sp_runtime::{ generic::DigestItem, testing::Header, - traits::{BlakeTwo256, IdentityLookup}, + traits::{BlakeTwo256, Header as HeaderT, IdentityLookup}, }; type UncheckedExtrinsic = frame_system::mocking::MockUncheckedExtrinsic; @@ -454,7 +119,7 @@ mod tests { UncheckedExtrinsic = UncheckedExtrinsic, { System: frame_system::{Pallet, Call, Config, Storage, Event}, - Authorship: pallet_authorship::{Pallet, Call, Storage, Inherent}, + Authorship: pallet_authorship::{Pallet, Storage}, } ); @@ -487,8 +152,6 @@ mod tests { impl pallet::Config for Test { type FindAuthor = AuthorGiven; - type UncleGenerations = ConstU64<5>; - type FilterUncle = SealVerify; type EventHandler = (); } @@ -511,34 +174,6 @@ mod tests { } } - pub struct VerifyBlock; - - impl VerifySeal for VerifyBlock { - fn verify_seal(header: &Header) -> Result, &'static str> { - let pre_runtime_digests = header.digest.logs.iter().filter_map(|d| d.as_pre_runtime()); - let seals = header.digest.logs.iter().filter_map(|d| d.as_seal()); - - let author = - AuthorGiven::find_author(pre_runtime_digests).ok_or_else(|| "no author")?; - - for (id, mut seal) in seals { - if id == TEST_ID { - match u64::decode(&mut seal) { - Err(_) => return Err("wrong seal"), - Ok(a) => { - if a != author { - return Err("wrong author in seal") - } - break - }, - } - } - } - - Ok(Some(author)) - } - } - fn seal_header(mut header: Header, author: u64) -> Header { { let digest = header.digest_mut(); @@ -558,196 +193,6 @@ mod tests { t.into() } - #[test] - fn prune_old_uncles_works() { - use UncleEntryItem::*; - new_test_ext().execute_with(|| { - let hash = Default::default(); - let author = Default::default(); - let uncles = vec![ - InclusionHeight(1u64), - Uncle(hash, Some(author)), - Uncle(hash, None), - Uncle(hash, None), - InclusionHeight(2u64), - Uncle(hash, None), - InclusionHeight(3u64), - Uncle(hash, None), - ]; - let uncles = BoundedVec::try_from(uncles).unwrap(); - - ::Uncles::put(uncles); - Authorship::prune_old_uncles(3); - - let uncles = ::Uncles::get(); - assert_eq!(uncles, vec![InclusionHeight(3u64), Uncle(hash, None)]); - }) - } - - #[test] - fn rejects_bad_uncles() { - new_test_ext().execute_with(|| { - let author_a = 69; - - struct CanonChain { - inner: Vec
, - } - - impl CanonChain { - fn best_hash(&self) -> H256 { - self.inner.last().unwrap().hash() - } - - fn canon_hash(&self, index: usize) -> H256 { - self.inner[index].hash() - } - - fn header(&self, index: usize) -> &Header { - &self.inner[index] - } - - fn push(&mut self, header: Header) { - self.inner.push(header) - } - } - - let mut canon_chain = CanonChain { - inner: vec![seal_header( - create_header(0, Default::default(), Default::default()), - 999, - )], - }; - - let initialize_block = |number, hash: H256| { - System::reset_events(); - System::initialize(&number, &hash, &Default::default()) - }; - - for number in 1..8 { - initialize_block(number, canon_chain.best_hash()); - let header = seal_header(System::finalize(), author_a); - canon_chain.push(header); - } - - // initialize so system context is set up correctly. - initialize_block(8, canon_chain.best_hash()); - - // 2 of the same uncle at once - { - let uncle_a = seal_header( - create_header(3, canon_chain.canon_hash(2), [1; 32].into()), - author_a, - ); - assert_eq!( - Authorship::verify_and_import_uncles(vec![uncle_a.clone(), uncle_a.clone()]), - Err(Error::::UncleAlreadyIncluded.into()), - ); - } - - // 2 of the same uncle at different times. - { - let uncle_a = seal_header( - create_header(3, canon_chain.canon_hash(2), [1; 32].into()), - author_a, - ); - - assert!(Authorship::verify_and_import_uncles(vec![uncle_a.clone()]).is_ok()); - - assert_eq!( - Authorship::verify_and_import_uncles(vec![uncle_a.clone()]), - Err(Error::::UncleAlreadyIncluded.into()), - ); - } - - // same uncle as ancestor. - { - let uncle_clone = canon_chain.header(5).clone(); - - assert_eq!( - Authorship::verify_and_import_uncles(vec![uncle_clone]), - Err(Error::::UncleAlreadyIncluded.into()), - ); - } - - // uncle without valid seal. - { - let unsealed = create_header(3, canon_chain.canon_hash(2), [2; 32].into()); - assert_eq!( - Authorship::verify_and_import_uncles(vec![unsealed]), - Err("no author".into()), - ); - } - - // old uncles can't get in. - { - assert_eq!(System::block_number(), 8); - - let gen_2 = seal_header( - create_header(2, canon_chain.canon_hash(1), [3; 32].into()), - author_a, - ); - - assert_eq!( - Authorship::verify_and_import_uncles(vec![gen_2]), - Err(Error::::OldUncle.into()), - ); - } - - // siblings are also allowed - { - let other_8 = seal_header( - create_header(8, canon_chain.canon_hash(7), [1; 32].into()), - author_a, - ); - - assert!(Authorship::verify_and_import_uncles(vec![other_8]).is_ok()); - } - }); - } - - #[test] - fn maximum_bound() { - new_test_ext().execute_with(|| { - let mut max_item_count = 0; - - let mut author_counter = 0; - let mut current_depth = 1; - let mut parent_hash: H256 = [1; 32].into(); - let mut uncles = vec![]; - - // We deliberately run this for more generations than the limit - // so that we can get the `Uncles` to hit its cap. - for _ in 0..<::UncleGenerations as Get>::get() + 3 { - let new_uncles: Vec<_> = (0..MAX_UNCLES) - .map(|_| { - System::reset_events(); - System::initialize(¤t_depth, &parent_hash, &Default::default()); - // Increment the author on every block to make sure the hash's always - // different. - author_counter += 1; - seal_header(System::finalize(), author_counter) - }) - .collect(); - - author_counter += 1; - System::reset_events(); - System::initialize(¤t_depth, &parent_hash, &Default::default()); - Authorship::on_initialize(current_depth); - Authorship::set_uncles(RuntimeOrigin::none(), uncles).unwrap(); - Authorship::on_finalize(current_depth); - max_item_count = - std::cmp::max(max_item_count, ::Uncles::get().len()); - - let new_parent = seal_header(System::finalize(), author_counter); - parent_hash = new_parent.hash(); - uncles = new_uncles; - current_depth += 1; - } - - assert_eq!(max_item_count, MaxUncleEntryItems::::get() as usize); - }); - } - #[test] fn sets_author_lazily() { new_test_ext().execute_with(|| { @@ -762,33 +207,4 @@ mod tests { assert_eq!(Authorship::author(), Some(author)); }); } - - #[test] - fn one_uncle_per_author_per_number() { - type Filter = OnePerAuthorPerHeight; - - let author_a = 42; - let author_b = 43; - - let mut acc: >::Accumulator = Default::default(); - let header_a1 = seal_header(create_header(1, Default::default(), [1; 32].into()), author_a); - let header_b1 = seal_header(create_header(1, Default::default(), [1; 32].into()), author_b); - - let header_a2_1 = - seal_header(create_header(2, Default::default(), [1; 32].into()), author_a); - let header_a2_2 = - seal_header(create_header(2, Default::default(), [2; 32].into()), author_a); - - let mut check_filter = move |uncle| Filter::filter_uncle(uncle, &mut acc); - - // same height, different author is OK. - assert_eq!(check_filter(&header_a1), Ok(Some(author_a))); - assert_eq!(check_filter(&header_b1), Ok(Some(author_b))); - - // same author, different height. - assert_eq!(check_filter(&header_a2_1), Ok(Some(author_a))); - - // same author, same height (author a, height 2) - assert!(check_filter(&header_a2_2).is_err()); - } } diff --git a/frame/babe/src/mock.rs b/frame/babe/src/mock.rs index abae1b7e3..b09dc29f6 100644 --- a/frame/babe/src/mock.rs +++ b/frame/babe/src/mock.rs @@ -52,15 +52,15 @@ frame_support::construct_runtime!( NodeBlock = Block, UncheckedExtrinsic = UncheckedExtrinsic, { - System: frame_system::{Pallet, Call, Config, Storage, Event}, - Authorship: pallet_authorship::{Pallet, Call, Storage, Inherent}, - Balances: pallet_balances::{Pallet, Call, Storage, Config, Event}, - Historical: pallet_session_historical::{Pallet}, - Offences: pallet_offences::{Pallet, Storage, Event}, - Babe: pallet_babe::{Pallet, Call, Storage, Config, ValidateUnsigned}, - Staking: pallet_staking::{Pallet, Call, Storage, Config, Event}, - Session: pallet_session::{Pallet, Call, Storage, Event, Config}, - Timestamp: pallet_timestamp::{Pallet, Call, Storage, Inherent}, + System: frame_system, + Authorship: pallet_authorship, + Balances: pallet_balances, + Historical: pallet_session_historical, + Offences: pallet_offences, + Babe: pallet_babe, + Staking: pallet_staking, + Session: pallet_session, + Timestamp: pallet_timestamp, } ); @@ -124,8 +124,6 @@ impl pallet_session::historical::Config for Test { impl pallet_authorship::Config for Test { type FindAuthor = pallet_session::FindAccountFromAuthorIndex; - type UncleGenerations = ConstU64<0>; - type FilterUncle = (); type EventHandler = (); } diff --git a/frame/grandpa/src/mock.rs b/frame/grandpa/src/mock.rs index c618705af..54f34008a 100644 --- a/frame/grandpa/src/mock.rs +++ b/frame/grandpa/src/mock.rs @@ -51,15 +51,15 @@ frame_support::construct_runtime!( NodeBlock = Block, UncheckedExtrinsic = UncheckedExtrinsic, { - System: frame_system::{Pallet, Call, Config, Storage, Event}, - Authorship: pallet_authorship::{Pallet, Call, Storage, Inherent}, - Timestamp: pallet_timestamp::{Pallet, Call, Storage, Inherent}, - Balances: pallet_balances::{Pallet, Call, Storage, Config, Event}, - Staking: pallet_staking::{Pallet, Call, Config, Storage, Event}, - Session: pallet_session::{Pallet, Call, Storage, Event, Config}, - Grandpa: pallet_grandpa::{Pallet, Call, Storage, Config, Event, ValidateUnsigned}, - Offences: pallet_offences::{Pallet, Storage, Event}, - Historical: pallet_session_historical::{Pallet}, + System: frame_system, + Authorship: pallet_authorship, + Timestamp: pallet_timestamp, + Balances: pallet_balances, + Staking: pallet_staking, + Session: pallet_session, + Grandpa: pallet_grandpa, + Offences: pallet_offences, + Historical: pallet_session_historical, } ); @@ -129,8 +129,6 @@ impl pallet_session::historical::Config for Test { impl pallet_authorship::Config for Test { type FindAuthor = (); - type UncleGenerations = ConstU64<0>; - type FilterUncle = (); type EventHandler = (); } diff --git a/frame/im-online/src/lib.rs b/frame/im-online/src/lib.rs index f23e610a4..8c1f46978 100644 --- a/frame/im-online/src/lib.rs +++ b/frame/im-online/src/lib.rs @@ -607,10 +607,6 @@ impl fn note_author(author: ValidatorId) { Self::note_authorship(author); } - - fn note_uncle(author: ValidatorId, _age: T::BlockNumber) { - Self::note_authorship(author); - } } impl Pallet { diff --git a/frame/im-online/src/mock.rs b/frame/im-online/src/mock.rs index bcb13fa1b..783e68dfe 100644 --- a/frame/im-online/src/mock.rs +++ b/frame/im-online/src/mock.rs @@ -169,8 +169,6 @@ impl pallet_session::historical::Config for Runtime { impl pallet_authorship::Config for Runtime { type FindAuthor = (); - type UncleGenerations = ConstU64<5>; - type FilterUncle = (); type EventHandler = ImOnline; } diff --git a/frame/im-online/src/tests.rs b/frame/im-online/src/tests.rs index 366119278..2c026f717 100644 --- a/frame/im-online/src/tests.rs +++ b/frame/im-online/src/tests.rs @@ -308,11 +308,10 @@ fn should_mark_online_validator_when_block_is_authored() { // when ImOnline::note_author(1); - ImOnline::note_uncle(2, 0); // then assert!(ImOnline::is_online(0)); - assert!(ImOnline::is_online(1)); + assert!(!ImOnline::is_online(1)); assert!(!ImOnline::is_online(2)); }); } @@ -338,7 +337,7 @@ fn should_not_send_a_report_if_already_online() { assert_eq!(Session::current_index(), 2); assert_eq!(Session::validators(), vec![1, 2, 3]); ImOnline::note_author(2); - ImOnline::note_uncle(3, 0); + ImOnline::note_author(3); // when UintAuthorityId::set_all_keys(vec![1, 2, 3]); diff --git a/frame/staking/src/mock.rs b/frame/staking/src/mock.rs index ad27a8291..30db036d0 100644 --- a/frame/staking/src/mock.rs +++ b/frame/staking/src/mock.rs @@ -91,14 +91,14 @@ frame_support::construct_runtime!( NodeBlock = Block, UncheckedExtrinsic = UncheckedExtrinsic, { - System: frame_system::{Pallet, Call, Config, Storage, Event}, - Authorship: pallet_authorship::{Pallet, Call, Storage, Inherent}, - Timestamp: pallet_timestamp::{Pallet, Call, Storage, Inherent}, - Balances: pallet_balances::{Pallet, Call, Storage, Config, Event}, - Staking: pallet_staking::{Pallet, Call, Config, Storage, Event}, - Session: pallet_session::{Pallet, Call, Storage, Event, Config}, - Historical: pallet_session::historical::{Pallet, Storage}, - VoterBagsList: pallet_bags_list::::{Pallet, Call, Storage, Event}, + System: frame_system, + Authorship: pallet_authorship, + Timestamp: pallet_timestamp, + Balances: pallet_balances, + Staking: pallet_staking, + Session: pallet_session, + Historical: pallet_session::historical, + VoterBagsList: pallet_bags_list::, } ); @@ -182,8 +182,6 @@ impl pallet_session::historical::Config for Test { } impl pallet_authorship::Config for Test { type FindAuthor = Author11; - type UncleGenerations = ConstU64<0>; - type FilterUncle = (); type EventHandler = Pallet; } diff --git a/frame/staking/src/pallet/impls.rs b/frame/staking/src/pallet/impls.rs index 7af9b0aaa..e59c2c5a0 100644 --- a/frame/staking/src/pallet/impls.rs +++ b/frame/staking/src/pallet/impls.rs @@ -1217,8 +1217,6 @@ impl historical::SessionManager pallet_authorship::EventHandler for Pallet where T: Config + pallet_authorship::Config + pallet_session::Config, @@ -1226,14 +1224,6 @@ where fn note_author(author: T::AccountId) { Self::reward_by_ids(vec![(author, 20)]) } - fn note_uncle(uncle_author: T::AccountId, _age: T::BlockNumber) { - // defensive-only: block author must exist. - if let Some(block_author) = >::author() { - Self::reward_by_ids(vec![(block_author, 2), (uncle_author, 1)]) - } else { - crate::log!(warn, "block author not set, this should never happen"); - } - } } /// This is intended to be used with `FilterHistoricalOffences`. diff --git a/frame/staking/src/tests.rs b/frame/staking/src/tests.rs index 9635717d4..e200f621b 100644 --- a/frame/staking/src/tests.rs +++ b/frame/staking/src/tests.rs @@ -2237,9 +2237,7 @@ fn reward_from_authorship_event_handler_works() { assert_eq!(>::author(), Some(11)); Pallet::::note_author(11); - Pallet::::note_uncle(21, 1); - // Rewarding the same two times works. - Pallet::::note_uncle(11, 1); + Pallet::::note_author(11); // Not mandatory but must be coherent with rewards assert_eq_uvec!(Session::validators(), vec![11, 21]); @@ -2248,10 +2246,7 @@ fn reward_from_authorship_event_handler_works() { // 11 is rewarded as a block producer and uncle referencer and uncle producer assert_eq!( ErasRewardPoints::::get(active_era()), - EraRewardPoints { - individual: vec![(11, 20 + 2 * 2 + 1), (21, 1)].into_iter().collect(), - total: 26, - }, + EraRewardPoints { individual: vec![(11, 20 * 2)].into_iter().collect(), total: 40 }, ); }) } diff --git a/frame/transaction-payment/asset-tx-payment/src/mock.rs b/frame/transaction-payment/asset-tx-payment/src/mock.rs index ddb02f5a6..60fb2e6c5 100644 --- a/frame/transaction-payment/asset-tx-payment/src/mock.rs +++ b/frame/transaction-payment/asset-tx-payment/src/mock.rs @@ -45,12 +45,12 @@ frame_support::construct_runtime!( NodeBlock = Block, UncheckedExtrinsic = UncheckedExtrinsic, { - System: system::{Pallet, Call, Config, Storage, Event}, - Balances: pallet_balances::{Pallet, Call, Storage, Config, Event}, - TransactionPayment: pallet_transaction_payment::{Pallet, Storage, Event}, - Assets: pallet_assets::{Pallet, Call, Storage, Event}, - Authorship: pallet_authorship::{Pallet, Call, Storage}, - AssetTxPayment: pallet_asset_tx_payment::{Pallet, Event}, + System: system, + Balances: pallet_balances, + TransactionPayment: pallet_transaction_payment, + Assets: pallet_assets, + Authorship: pallet_authorship, + AssetTxPayment: pallet_asset_tx_payment, } ); @@ -187,8 +187,6 @@ impl FindAuthor for HardcodedAuthor { impl pallet_authorship::Config for Runtime { type FindAuthor = HardcodedAuthor; - type UncleGenerations = (); - type FilterUncle = (); type EventHandler = (); } diff --git a/primitives/authorship/Cargo.toml b/primitives/authorship/Cargo.toml deleted file mode 100644 index 31ea3b2d4..000000000 --- a/primitives/authorship/Cargo.toml +++ /dev/null @@ -1,30 +0,0 @@ -[package] -name = "sp-authorship" -version = "4.0.0-dev" -authors = ["Parity Technologies "] -description = "Authorship primitives" -edition = "2021" -license = "Apache-2.0" -homepage = "https://substrate.io" -repository = "https://github.com/paritytech/substrate/" -readme = "README.md" - -[package.metadata.docs.rs] -targets = ["x86_64-unknown-linux-gnu"] - -[dependencies] -async-trait = { version = "0.1.57", optional = true } -codec = { package = "parity-scale-codec", version = "3.2.2", default-features = false, features = ["derive"] } -sp-inherents = { version = "4.0.0-dev", default-features = false, path = "../inherents" } -sp-runtime = { version = "7.0.0", default-features = false, path = "../runtime" } -sp-std = { version = "5.0.0", default-features = false, path = "../std" } - -[features] -default = [ "std" ] -std = [ - "async-trait", - "codec/std", - "sp-inherents/std", - "sp-runtime/std", - "sp-std/std", -] diff --git a/primitives/authorship/README.md b/primitives/authorship/README.md deleted file mode 100644 index 1aa1805cf..000000000 --- a/primitives/authorship/README.md +++ /dev/null @@ -1,3 +0,0 @@ -Authorship Primitives - -License: Apache-2.0 \ No newline at end of file diff --git a/primitives/authorship/src/lib.rs b/primitives/authorship/src/lib.rs deleted file mode 100644 index 6ff6607a8..000000000 --- a/primitives/authorship/src/lib.rs +++ /dev/null @@ -1,101 +0,0 @@ -// This file is part of Substrate. - -// Copyright (C) 2019-2022 Parity Technologies (UK) Ltd. -// SPDX-License-Identifier: Apache-2.0 - -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -//! Authorship Primitives - -#![cfg_attr(not(feature = "std"), no_std)] - -use sp_std::{prelude::*, result::Result}; - -#[cfg(feature = "std")] -use codec::Decode; -use codec::Encode; -use sp_inherents::{Error, InherentData, InherentIdentifier, IsFatalError}; -use sp_runtime::{traits::Header as HeaderT, RuntimeString}; - -/// The identifier for the `uncles` inherent. -pub const INHERENT_IDENTIFIER: InherentIdentifier = *b"uncles00"; - -/// Errors that can occur while checking the authorship inherent. -#[derive(Encode, sp_runtime::RuntimeDebug)] -#[cfg_attr(feature = "std", derive(Decode))] -pub enum InherentError { - Uncles(RuntimeString), -} - -impl IsFatalError for InherentError { - fn is_fatal_error(&self) -> bool { - match self { - InherentError::Uncles(_) => true, - } - } -} - -/// Auxiliary trait to extract uncles inherent data. -pub trait UnclesInherentData { - /// Get uncles. - fn uncles(&self) -> Result, Error>; -} - -impl UnclesInherentData for InherentData { - fn uncles(&self) -> Result, Error> { - Ok(self.get_data(&INHERENT_IDENTIFIER)?.unwrap_or_default()) - } -} - -/// Provider for inherent data. -#[cfg(feature = "std")] -pub struct InherentDataProvider { - uncles: Vec, -} - -#[cfg(feature = "std")] -impl InherentDataProvider { - /// Create a new inherent data provider with the given `uncles`. - pub fn new(uncles: Vec) -> Self { - InherentDataProvider { uncles } - } - - /// Create a new instance that is usable for checking inherents. - /// - /// This will always return an empty vec of uncles. - pub fn check_inherents() -> Self { - Self { uncles: Vec::new() } - } -} - -#[cfg(feature = "std")] -#[async_trait::async_trait] -impl sp_inherents::InherentDataProvider for InherentDataProvider { - async fn provide_inherent_data(&self, inherent_data: &mut InherentData) -> Result<(), Error> { - inherent_data.put_data(INHERENT_IDENTIFIER, &self.uncles) - } - - async fn try_handle_error( - &self, - identifier: &InherentIdentifier, - mut error: &[u8], - ) -> Option> { - if *identifier != INHERENT_IDENTIFIER { - return None - } - - let error = InherentError::decode(&mut error).ok()?; - - Some(Err(Error::Application(Box::from(format!("{:?}", error))))) - } -} From a9c4334d96cbb63aff8e3141a5b3dcad8c636bb7 Mon Sep 17 00:00:00 2001 From: Jeeyong Um Date: Mon, 30 Jan 2023 14:32:09 +0900 Subject: [PATCH 072/558] Implement RIType traits for u8 array with an arbitrary size (#13256) --- primitives/runtime-interface/src/impls.rs | 115 +++++++++------------- 1 file changed, 48 insertions(+), 67 deletions(-) diff --git a/primitives/runtime-interface/src/impls.rs b/primitives/runtime-interface/src/impls.rs index e801931c3..e311b01db 100644 --- a/primitives/runtime-interface/src/impls.rs +++ b/primitives/runtime-interface/src/impls.rs @@ -276,84 +276,65 @@ impl IntoFFIValue for [T] { } } -/// Implement the traits for the `[u8; N]` arrays, where `N` is the input to this macro. -macro_rules! impl_traits_for_arrays { - ( - $( - $n:expr - ),* - $(,)? - ) => { - $( - /// The type is passed as `u32`. - /// - /// The `u32` is the pointer to the array. - impl RIType for [u8; $n] { - type FFIType = u32; - } - - #[cfg(not(feature = "std"))] - impl IntoFFIValue for [u8; $n] { - type Owned = (); +/// The type is passed as `u32`. +/// +/// The `u32` is the pointer to the array. +impl RIType for [u8; N] { + type FFIType = u32; +} - fn into_ffi_value(&self) -> WrappedFFIValue { - (self.as_ptr() as u32).into() - } - } +#[cfg(not(feature = "std"))] +impl IntoFFIValue for [u8; N] { + type Owned = (); - #[cfg(not(feature = "std"))] - impl FromFFIValue for [u8; $n] { - fn from_ffi_value(arg: u32) -> [u8; $n] { - let mut res = [0u8; $n]; - let data = unsafe { Vec::from_raw_parts(arg as *mut u8, $n, $n) }; + fn into_ffi_value(&self) -> WrappedFFIValue { + (self.as_ptr() as u32).into() + } +} - res.copy_from_slice(&data); +#[cfg(not(feature = "std"))] +impl FromFFIValue for [u8; N] { + fn from_ffi_value(arg: u32) -> [u8; N] { + let mut res = [0u8; N]; + let data = unsafe { Vec::from_raw_parts(arg as *mut u8, N, N) }; - res - } - } + res.copy_from_slice(&data); - #[cfg(feature = "std")] - impl FromFFIValue for [u8; $n] { - type SelfInstance = [u8; $n]; + res + } +} - fn from_ffi_value(context: &mut dyn FunctionContext, arg: u32) -> Result<[u8; $n]> { - let mut res = [0u8; $n]; - context.read_memory_into(Pointer::new(arg), &mut res)?; - Ok(res) - } - } +#[cfg(feature = "std")] +impl FromFFIValue for [u8; N] { + type SelfInstance = [u8; N]; - #[cfg(feature = "std")] - impl IntoFFIValue for [u8; $n] { - fn into_ffi_value(self, context: &mut dyn FunctionContext) -> Result { - let addr = context.allocate_memory($n)?; - context.write_memory(addr, &self)?; - Ok(addr.into()) - } - } + fn from_ffi_value(context: &mut dyn FunctionContext, arg: u32) -> Result<[u8; N]> { + let mut res = [0u8; N]; + context.read_memory_into(Pointer::new(arg), &mut res)?; + Ok(res) + } +} - #[cfg(feature = "std")] - impl IntoPreallocatedFFIValue for [u8; $n] { - type SelfInstance = [u8; $n]; - - fn into_preallocated_ffi_value( - self_instance: Self::SelfInstance, - context: &mut dyn FunctionContext, - allocated: u32, - ) -> Result<()> { - context.write_memory(Pointer::new(allocated), &self_instance) - } - } - )* +#[cfg(feature = "std")] +impl IntoFFIValue for [u8; N] { + fn into_ffi_value(self, context: &mut dyn FunctionContext) -> Result { + let addr = context.allocate_memory(N as u32)?; + context.write_memory(addr, &self)?; + Ok(addr.into()) } } -impl_traits_for_arrays! { - 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, - 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, - 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, - 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, +#[cfg(feature = "std")] +impl IntoPreallocatedFFIValue for [u8; N] { + type SelfInstance = [u8; N]; + + fn into_preallocated_ffi_value( + self_instance: Self::SelfInstance, + context: &mut dyn FunctionContext, + allocated: u32, + ) -> Result<()> { + context.write_memory(Pointer::new(allocated), &self_instance) + } } impl PassBy for sp_std::result::Result { From 129fee774a6d185d117a57fd1e81b3d0d05ad747 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Silva?= <123550+andresilva@users.noreply.github.com> Date: Mon, 30 Jan 2023 15:25:11 +0000 Subject: [PATCH 073/558] grandpa: cleanup stale entries in set id session mapping (#13237) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * grandpa: cleanup stale entries in set id session mapping * Update frame/grandpa/src/migrations.rs Co-authored-by: Bastian Köcher * grandpa: remove unused import * grandpa: migration off-by-one * Update frame/grandpa/src/lib.rs Co-authored-by: Anton * Update frame/grandpa/src/lib.rs Co-authored-by: Anton * grandpa: MaxSetIdSessionEntries as u64 * node-template: fix MaxSetIdSessionEntries type --------- Co-authored-by: Bastian Köcher Co-authored-by: Anton --- bin/node-template/runtime/src/lib.rs | 1 + bin/node/runtime/src/lib.rs | 5 +++ frame/grandpa/src/lib.rs | 30 +++++++++++++++--- frame/grandpa/src/migrations.rs | 46 ++++++++++++++++++++++++++++ frame/grandpa/src/mock.rs | 2 ++ frame/grandpa/src/tests.rs | 27 ++++++++++++++++ 6 files changed, 107 insertions(+), 4 deletions(-) diff --git a/bin/node-template/runtime/src/lib.rs b/bin/node-template/runtime/src/lib.rs index baba5d9b0..484045981 100644 --- a/bin/node-template/runtime/src/lib.rs +++ b/bin/node-template/runtime/src/lib.rs @@ -230,6 +230,7 @@ impl pallet_grandpa::Config for Runtime { type WeightInfo = (); type MaxAuthorities = ConstU32<32>; + type MaxSetIdSessionEntries = ConstU64<0>; } impl pallet_timestamp::Config for Runtime { diff --git a/bin/node/runtime/src/lib.rs b/bin/node/runtime/src/lib.rs index 30165cdb6..8e8ecc125 100644 --- a/bin/node/runtime/src/lib.rs +++ b/bin/node/runtime/src/lib.rs @@ -1310,6 +1310,10 @@ impl pallet_authority_discovery::Config for Runtime { type MaxAuthorities = MaxAuthorities; } +parameter_types! { + pub const MaxSetIdSessionEntries: u32 = BondingDuration::get() * SessionsPerEra::get(); +} + impl pallet_grandpa::Config for Runtime { type RuntimeEvent = RuntimeEvent; @@ -1331,6 +1335,7 @@ impl pallet_grandpa::Config for Runtime { type WeightInfo = (); type MaxAuthorities = MaxAuthorities; + type MaxSetIdSessionEntries = MaxSetIdSessionEntries; } parameter_types! { diff --git a/frame/grandpa/src/lib.rs b/frame/grandpa/src/lib.rs index aa09b445c..ea534947d 100644 --- a/frame/grandpa/src/lib.rs +++ b/frame/grandpa/src/lib.rs @@ -121,6 +121,15 @@ pub mod pallet { /// Max Authorities in use #[pallet::constant] type MaxAuthorities: Get; + + /// The maximum number of entries to keep in the set id to session index mapping. + /// + /// Since the `SetIdSession` map is only used for validating equivocations this + /// value should relate to the bonding duration of whatever staking system is + /// being used (if any). If equivocation handling is not enabled then this value + /// can be zero. + #[pallet::constant] + type MaxSetIdSessionEntries: Get; } #[pallet::hooks] @@ -323,6 +332,12 @@ pub mod pallet { /// A mapping from grandpa set ID to the index of the *most recent* session for which its /// members were responsible. /// + /// This is only used for validating equivocation proofs. An equivocation proof must + /// contains a key-ownership proof for a given session, therefore we need a way to tie + /// together sessions and GRANDPA set ids, i.e. we need to validate that a validator + /// was the owner of a given key on a given session, and what the active set ID was + /// during that session. + /// /// TWOX-NOTE: `SetId` is not under user control. #[pallet::storage] #[pallet::getter(fn session_for_set)] @@ -643,10 +658,17 @@ where }; if res.is_ok() { - CurrentSetId::::mutate(|s| { + let current_set_id = CurrentSetId::::mutate(|s| { *s += 1; *s - }) + }); + + let max_set_id_session_entries = T::MaxSetIdSessionEntries::get().max(1); + if current_set_id >= max_set_id_session_entries { + SetIdSession::::remove(current_set_id - max_set_id_session_entries); + } + + current_set_id } else { // either the session module signalled that the validators have changed // or the set was stalled. but since we didn't successfully schedule @@ -659,8 +681,8 @@ where Self::current_set_id() }; - // if we didn't issue a change, we update the mapping to note that the current - // set corresponds to the latest equivalent session (i.e. now). + // update the mapping to note that the current set corresponds to the + // latest equivalent session (i.e. now). let session_index = >::current_index(); SetIdSession::::insert(current_set_id, &session_index); } diff --git a/frame/grandpa/src/migrations.rs b/frame/grandpa/src/migrations.rs index 7795afcd8..f4a28fff1 100644 --- a/frame/grandpa/src/migrations.rs +++ b/frame/grandpa/src/migrations.rs @@ -15,5 +15,51 @@ // See the License for the specific language governing permissions and // limitations under the License. +use frame_support::{ + traits::{Get, OnRuntimeUpgrade}, + weights::Weight, +}; + +use crate::{Config, CurrentSetId, SetIdSession, LOG_TARGET}; + /// Version 4. pub mod v4; + +/// This migration will clean up all stale set id -> session entries from the +/// `SetIdSession` storage map, only the latest `max_set_id_session_entries` +/// will be kept. +/// +/// This migration should be added with a runtime upgrade that introduces the +/// `MaxSetIdSessionEntries` constant to the pallet (although it could also be +/// done later on). +pub struct CleanupSetIdSessionMap(sp_std::marker::PhantomData); +impl OnRuntimeUpgrade for CleanupSetIdSessionMap { + fn on_runtime_upgrade() -> Weight { + // NOTE: since this migration will loop over all stale entries in the + // map we need to set some cutoff value, otherwise the migration might + // take too long to run. for scenarios where there are that many entries + // to cleanup a multiblock migration will be needed instead. + if CurrentSetId::::get() > 25_000 { + log::warn!( + target: LOG_TARGET, + "CleanupSetIdSessionMap migration was aborted since there are too many entries to cleanup." + ); + + return T::DbWeight::get().reads(1) + } + + cleanup_set_id_sesion_map::() + } +} + +fn cleanup_set_id_sesion_map() -> Weight { + let until_set_id = CurrentSetId::::get().saturating_sub(T::MaxSetIdSessionEntries::get()); + + for set_id in 0..=until_set_id { + SetIdSession::::remove(set_id); + } + + T::DbWeight::get() + .reads(1) + .saturating_add(T::DbWeight::get().writes(until_set_id + 1)) +} diff --git a/frame/grandpa/src/mock.rs b/frame/grandpa/src/mock.rs index 54f34008a..7d54966a4 100644 --- a/frame/grandpa/src/mock.rs +++ b/frame/grandpa/src/mock.rs @@ -219,6 +219,7 @@ impl pallet_offences::Config for Test { parameter_types! { pub const ReportLongevity: u64 = BondingDuration::get() as u64 * SessionsPerEra::get() as u64 * Period::get(); + pub const MaxSetIdSessionEntries: u32 = BondingDuration::get() * SessionsPerEra::get(); } impl Config for Test { @@ -239,6 +240,7 @@ impl Config for Test { type WeightInfo = (); type MaxAuthorities = ConstU32<100>; + type MaxSetIdSessionEntries = MaxSetIdSessionEntries; } pub fn grandpa_log(log: ConsensusLog) -> DigestItem { diff --git a/frame/grandpa/src/tests.rs b/frame/grandpa/src/tests.rs index 626decd12..e090dcebb 100644 --- a/frame/grandpa/src/tests.rs +++ b/frame/grandpa/src/tests.rs @@ -781,6 +781,33 @@ fn on_new_session_doesnt_start_new_set_if_schedule_change_failed() { }); } +#[test] +fn cleans_up_old_set_id_session_mappings() { + new_test_ext(vec![(1, 1), (2, 1), (3, 1)]).execute_with(|| { + let max_set_id_session_entries = MaxSetIdSessionEntries::get(); + + start_era(max_set_id_session_entries); + + // we should have a session id mapping for all the set ids from + // `max_set_id_session_entries` eras we have observed + for i in 1..=max_set_id_session_entries { + assert!(Grandpa::session_for_set(i as u64).is_some()); + } + + start_era(max_set_id_session_entries * 2); + + // we should keep tracking the new mappings for new eras + for i in max_set_id_session_entries + 1..=max_set_id_session_entries * 2 { + assert!(Grandpa::session_for_set(i as u64).is_some()); + } + + // but the old ones should have been pruned by now + for i in 1..=max_set_id_session_entries { + assert!(Grandpa::session_for_set(i as u64).is_none()); + } + }); +} + #[test] fn always_schedules_a_change_on_new_session_when_stalled() { new_test_ext(vec![(1, 1), (2, 1), (3, 1)]).execute_with(|| { From 8cfe326e4e33c5077fc67f197d6a13dd871881c7 Mon Sep 17 00:00:00 2001 From: Muharem Ismailov Date: Wed, 1 Feb 2023 03:11:41 +0100 Subject: [PATCH 074/558] Benchmark's successful origin api update (#13146) * try successful origin unimplemented by default * error as a default impl for try_successful_origin * remove successful_origin func of EnsureOrigin trait * default impl -> unimplemented!() * update EnsureOriginWithArg * fix EnsureOriginWithArg * prefix unused arg with underscore * use try_successful_origin instead successful_origin, map err to Weightless * fix tests * remove default impl * unwrap for indirect origin dep * replace unwrap by expect with a message --------- Co-authored-by: parity-processbot <> --- frame/alliance/src/benchmarking.rs | 23 ++- frame/assets/src/benchmarking.rs | 14 +- frame/bounties/src/benchmarking.rs | 6 +- frame/democracy/src/benchmarking.rs | 42 +++-- frame/fast-unstake/src/benchmarking.rs | 5 +- frame/identity/src/benchmarking.rs | 23 ++- frame/lottery/src/benchmarking.rs | 14 +- frame/membership/src/lib.rs | 37 ++-- frame/nfts/src/benchmarking.rs | 11 +- frame/nis/src/benchmarking.rs | 5 +- frame/preimage/src/benchmarking.rs | 116 +++++++++---- frame/ranked-collective/src/benchmarking.rs | 37 ++-- frame/ranked-collective/src/lib.rs | 39 ----- frame/referenda/src/benchmarking.rs | 183 ++++++++++++++------ frame/scheduler/src/benchmarking.rs | 5 +- frame/support/src/traits/dispatch.rs | 38 +--- frame/system/src/tests.rs | 7 +- frame/tips/src/benchmarking.rs | 7 +- frame/treasury/src/benchmarking.rs | 11 +- frame/uniques/src/benchmarking.rs | 8 +- frame/whitelist/src/benchmarking.rs | 14 +- 21 files changed, 402 insertions(+), 243 deletions(-) diff --git a/frame/alliance/src/benchmarking.rs b/frame/alliance/src/benchmarking.rs index f312e032b..01b81ed66 100644 --- a/frame/alliance/src/benchmarking.rs +++ b/frame/alliance/src/benchmarking.rs @@ -25,7 +25,7 @@ use sp_std::{ prelude::*, }; -use frame_benchmarking::v1::{account, benchmarks_instance_pallet}; +use frame_benchmarking::v1::{account, benchmarks_instance_pallet, BenchmarkError}; use frame_support::traits::{EnsureOrigin, Get, UnfilteredDispatchable}; use frame_system::{Pallet as System, RawOrigin as SystemOrigin}; @@ -581,7 +581,8 @@ benchmarks_instance_pallet! { let rule = rule(b"hello world"); let call = Call::::set_rule { rule: rule.clone() }; - let origin = T::AdminOrigin::successful_origin(); + let origin = + T::AdminOrigin::try_successful_origin().map_err(|_| BenchmarkError::Weightless)?; }: { call.dispatch_bypass_filter(origin)? } verify { assert_eq!(Alliance::::rule(), Some(rule.clone())); @@ -594,7 +595,8 @@ benchmarks_instance_pallet! { let announcement = announcement(b"hello world"); let call = Call::::announce { announcement: announcement.clone() }; - let origin = T::AnnouncementOrigin::successful_origin(); + let origin = + T::AnnouncementOrigin::try_successful_origin().map_err(|_| BenchmarkError::Weightless)?; }: { call.dispatch_bypass_filter(origin)? } verify { assert!(Alliance::::announcements().contains(&announcement)); @@ -609,7 +611,8 @@ benchmarks_instance_pallet! { Announcements::::put(announcements); let call = Call::::remove_announcement { announcement: announcement.clone() }; - let origin = T::AnnouncementOrigin::successful_origin(); + let origin = + T::AnnouncementOrigin::try_successful_origin().map_err(|_| BenchmarkError::Weightless)?; }: { call.dispatch_bypass_filter(origin)? } verify { assert!(Alliance::::announcements().is_empty()); @@ -665,7 +668,8 @@ benchmarks_instance_pallet! { let ally1_lookup = T::Lookup::unlookup(ally1.clone()); let call = Call::::elevate_ally { ally: ally1_lookup }; - let origin = T::MembershipManager::successful_origin(); + let origin = + T::MembershipManager::try_successful_origin().map_err(|_| BenchmarkError::Weightless)?; }: { call.dispatch_bypass_filter(origin)? } verify { assert!(!Alliance::::is_ally(&ally1)); @@ -725,7 +729,8 @@ benchmarks_instance_pallet! { let fellow2_lookup = T::Lookup::unlookup(fellow2.clone()); let call = Call::::kick_member { who: fellow2_lookup }; - let origin = T::MembershipManager::successful_origin(); + let origin = + T::MembershipManager::try_successful_origin().map_err(|_| BenchmarkError::Weightless)?; }: { call.dispatch_bypass_filter(origin)? } verify { assert!(!Alliance::::is_member(&fellow2)); @@ -754,7 +759,8 @@ benchmarks_instance_pallet! { unscrupulous_list.extend(websites.into_iter().map(UnscrupulousItem::Website)); let call = Call::::add_unscrupulous_items { items: unscrupulous_list.clone() }; - let origin = T::AnnouncementOrigin::successful_origin(); + let origin = + T::AnnouncementOrigin::try_successful_origin().map_err(|_| BenchmarkError::Weightless)?; }: { call.dispatch_bypass_filter(origin)? } verify { assert_last_event::(Event::UnscrupulousItemAdded { items: unscrupulous_list }.into()); @@ -784,7 +790,8 @@ benchmarks_instance_pallet! { unscrupulous_list.extend(websites.into_iter().map(UnscrupulousItem::Website)); let call = Call::::remove_unscrupulous_items { items: unscrupulous_list.clone() }; - let origin = T::AnnouncementOrigin::successful_origin(); + let origin = + T::AnnouncementOrigin::try_successful_origin().map_err(|_| BenchmarkError::Weightless)?; }: { call.dispatch_bypass_filter(origin)? } verify { assert_last_event::(Event::UnscrupulousItemRemoved { items: unscrupulous_list }.into()); diff --git a/frame/assets/src/benchmarking.rs b/frame/assets/src/benchmarking.rs index 9acf69f1e..cf0ec353f 100644 --- a/frame/assets/src/benchmarking.rs +++ b/frame/assets/src/benchmarking.rs @@ -21,7 +21,7 @@ use super::*; use frame_benchmarking::v1::{ - account, benchmarks_instance_pallet, whitelist_account, whitelisted_caller, + account, benchmarks_instance_pallet, whitelist_account, whitelisted_caller, BenchmarkError, }; use frame_support::{ dispatch::UnfilteredDispatchable, @@ -135,7 +135,8 @@ fn assert_event, I: 'static>(generic_event: >::Runti benchmarks_instance_pallet! { create { let asset_id = default_asset_id::(); - let origin = T::CreateOrigin::successful_origin(&asset_id.into()); + let origin = T::CreateOrigin::try_successful_origin(&asset_id.into()) + .map_err(|_| BenchmarkError::Weightless)?; let caller = T::CreateOrigin::ensure_origin(origin, &asset_id.into()).unwrap(); let caller_lookup = T::Lookup::unlookup(caller.clone()); T::Currency::make_free_balance_be(&caller, DepositBalanceOf::::max_value()); @@ -362,7 +363,8 @@ benchmarks_instance_pallet! { let (asset_id, _, _) = create_default_asset::(true); - let origin = T::ForceOrigin::successful_origin(); + let origin = + T::ForceOrigin::try_successful_origin().map_err(|_| BenchmarkError::Weightless)?; let call = Call::::force_set_metadata { id: asset_id, name: name.clone(), @@ -382,7 +384,8 @@ benchmarks_instance_pallet! { let origin = SystemOrigin::Signed(caller).into(); Assets::::set_metadata(origin, asset_id, dummy.clone(), dummy, 12)?; - let origin = T::ForceOrigin::successful_origin(); + let origin = + T::ForceOrigin::try_successful_origin().map_err(|_| BenchmarkError::Weightless)?; let call = Call::::force_clear_metadata { id: asset_id }; }: { call.dispatch_bypass_filter(origin)? } verify { @@ -392,7 +395,8 @@ benchmarks_instance_pallet! { force_asset_status { let (asset_id, caller, caller_lookup) = create_default_asset::(true); - let origin = T::ForceOrigin::successful_origin(); + let origin = + T::ForceOrigin::try_successful_origin().map_err(|_| BenchmarkError::Weightless)?; let call = Call::::force_asset_status { id: asset_id, owner: caller_lookup.clone(), diff --git a/frame/bounties/src/benchmarking.rs b/frame/bounties/src/benchmarking.rs index a43372978..881f1c912 100644 --- a/frame/bounties/src/benchmarking.rs +++ b/frame/bounties/src/benchmarking.rs @@ -173,7 +173,8 @@ benchmarks_instance_pallet! { let (caller, curator, fee, value, reason) = setup_bounty::(0, 0); Bounties::::propose_bounty(RawOrigin::Signed(caller).into(), value, reason)?; let bounty_id = BountyCount::::get() - 1; - let approve_origin = T::ApproveOrigin::successful_origin(); + let approve_origin = + T::ApproveOrigin::try_successful_origin().map_err(|_| BenchmarkError::Weightless)?; }: close_bounty(approve_origin, bounty_id) close_bounty_active { @@ -181,7 +182,8 @@ benchmarks_instance_pallet! { let (curator_lookup, bounty_id) = create_bounty::()?; Treasury::::on_initialize(T::BlockNumber::zero()); let bounty_id = BountyCount::::get() - 1; - let approve_origin = T::ApproveOrigin::successful_origin(); + let approve_origin = + T::ApproveOrigin::try_successful_origin().map_err(|_| BenchmarkError::Weightless)?; }: close_bounty(approve_origin, bounty_id) verify { assert_last_event::(Event::BountyCanceled { index: bounty_id }.into()) diff --git a/frame/democracy/src/benchmarking.rs b/frame/democracy/src/benchmarking.rs index 66d9e2a00..f2d45b719 100644 --- a/frame/democracy/src/benchmarking.rs +++ b/frame/democracy/src/benchmarking.rs @@ -19,7 +19,7 @@ use super::*; -use frame_benchmarking::v1::{account, benchmarks, whitelist_account}; +use frame_benchmarking::v1::{account, benchmarks, whitelist_account, BenchmarkError}; use frame_support::{ assert_noop, assert_ok, traits::{Currency, EnsureOrigin, Get, OnInitialize, UnfilteredDispatchable}, @@ -177,7 +177,8 @@ benchmarks! { } emergency_cancel { - let origin = T::CancellationOrigin::successful_origin(); + let origin = + T::CancellationOrigin::try_successful_origin().map_err(|_| BenchmarkError::Weightless)?; let ref_index = add_referendum::(0).0; assert_ok!(Democracy::::referendum_status(ref_index)); }: _(origin, ref_index) @@ -200,10 +201,13 @@ benchmarks! { let (ref_index, hash) = add_referendum::(0); assert_ok!(Democracy::::referendum_status(ref_index)); // Place our proposal in the external queue, too. - assert_ok!( - Democracy::::external_propose(T::ExternalOrigin::successful_origin(), make_proposal::(0)) - ); - let origin = T::BlacklistOrigin::successful_origin(); + assert_ok!(Democracy::::external_propose( + T::ExternalOrigin::try_successful_origin() + .expect("ExternalOrigin has no successful origin required for the benchmark"), + make_proposal::(0) + )); + let origin = + T::BlacklistOrigin::try_successful_origin().map_err(|_| BenchmarkError::Weightless)?; }: _(origin, hash, Some(ref_index)) verify { // Referendum has been canceled @@ -215,7 +219,8 @@ benchmarks! { // Worst case scenario, we external propose a previously blacklisted proposal external_propose { - let origin = T::ExternalOrigin::successful_origin(); + let origin = + T::ExternalOrigin::try_successful_origin().map_err(|_| BenchmarkError::Weightless)?; let proposal = make_proposal::(0); // Add proposal to blacklist with block number 0 @@ -233,7 +238,8 @@ benchmarks! { } external_propose_majority { - let origin = T::ExternalMajorityOrigin::successful_origin(); + let origin = T::ExternalMajorityOrigin::try_successful_origin() + .map_err(|_| BenchmarkError::Weightless)?; let proposal = make_proposal::(0); }: _(origin, proposal) verify { @@ -242,7 +248,8 @@ benchmarks! { } external_propose_default { - let origin = T::ExternalDefaultOrigin::successful_origin(); + let origin = T::ExternalDefaultOrigin::try_successful_origin() + .map_err(|_| BenchmarkError::Weightless)?; let proposal = make_proposal::(0); }: _(origin, proposal) verify { @@ -251,13 +258,15 @@ benchmarks! { } fast_track { - let origin_propose = T::ExternalDefaultOrigin::successful_origin(); + let origin_propose = T::ExternalDefaultOrigin::try_successful_origin() + .expect("ExternalDefaultOrigin has no successful origin required for the benchmark"); let proposal = make_proposal::(0); let proposal_hash = proposal.hash(); Democracy::::external_propose_default(origin_propose, proposal)?; // NOTE: Instant origin may invoke a little bit more logic, but may not always succeed. - let origin_fast_track = T::FastTrackOrigin::successful_origin(); + let origin_fast_track = + T::FastTrackOrigin::try_successful_origin().map_err(|_| BenchmarkError::Weightless)?; let voting_period = T::FastTrackVotingPeriod::get(); let delay = 0u32; }: _(origin_fast_track, proposal_hash, voting_period, delay.into()) @@ -269,7 +278,8 @@ benchmarks! { let proposal = make_proposal::(0); let proposal_hash = proposal.hash(); - let origin_propose = T::ExternalDefaultOrigin::successful_origin(); + let origin_propose = T::ExternalDefaultOrigin::try_successful_origin() + .expect("ExternalDefaultOrigin has no successful origin required for the benchmark"); Democracy::::external_propose_default(origin_propose, proposal)?; let mut vetoers: BoundedVec = Default::default(); @@ -279,7 +289,7 @@ benchmarks! { vetoers.sort(); Blacklist::::insert(proposal_hash, (T::BlockNumber::zero(), vetoers)); - let origin = T::VetoOrigin::successful_origin(); + let origin = T::VetoOrigin::try_successful_origin().map_err(|_| BenchmarkError::Weightless)?; ensure!(NextExternal::::get().is_some(), "no external proposal"); }: _(origin, proposal_hash) verify { @@ -293,7 +303,8 @@ benchmarks! { for i in 0 .. T::MaxProposals::get() { add_proposal::(i)?; } - let cancel_origin = T::CancelProposalOrigin::successful_origin(); + let cancel_origin = T::CancelProposalOrigin::try_successful_origin() + .map_err(|_| BenchmarkError::Weightless)?; }: _(cancel_origin, 0) cancel_referendum { @@ -313,7 +324,8 @@ benchmarks! { // Launch external LastTabledWasExternal::::put(false); - let origin = T::ExternalMajorityOrigin::successful_origin(); + let origin = T::ExternalMajorityOrigin::try_successful_origin() + .map_err(|_| BenchmarkError::Weightless)?; let proposal = make_proposal::(r); let call = Call::::external_propose_majority { proposal }; call.dispatch_bypass_filter(origin)?; diff --git a/frame/fast-unstake/src/benchmarking.rs b/frame/fast-unstake/src/benchmarking.rs index 77488b644..e0e4186d7 100644 --- a/frame/fast-unstake/src/benchmarking.rs +++ b/frame/fast-unstake/src/benchmarking.rs @@ -20,7 +20,7 @@ #![cfg(feature = "runtime-benchmarks")] use crate::{types::*, Pallet as FastUnstake, *}; -use frame_benchmarking::v1::{benchmarks, whitelist_account}; +use frame_benchmarking::v1::{benchmarks, whitelist_account, BenchmarkError}; use frame_support::{ assert_ok, traits::{Currency, EnsureOrigin, Get, Hooks}, @@ -192,7 +192,8 @@ benchmarks! { } control { - let origin = ::ControlOrigin::successful_origin(); + let origin = ::ControlOrigin::try_successful_origin() + .map_err(|_| BenchmarkError::Weightless)?; } : _(origin, T::MaxErasToCheckPerBlock::get()) verify {} diff --git a/frame/identity/src/benchmarking.rs b/frame/identity/src/benchmarking.rs index 356bcb2e8..2d485af0f 100644 --- a/frame/identity/src/benchmarking.rs +++ b/frame/identity/src/benchmarking.rs @@ -22,7 +22,7 @@ use super::*; use crate::Pallet as Identity; -use frame_benchmarking::v1::{account, benchmarks, whitelisted_caller}; +use frame_benchmarking::v1::{account, benchmarks, whitelisted_caller, BenchmarkError}; use frame_support::{ ensure, traits::{EnsureOrigin, Get}, @@ -42,7 +42,8 @@ fn add_registrars(r: u32) -> Result<(), &'static str> { let registrar: T::AccountId = account("registrar", i, SEED); let registrar_lookup = T::Lookup::unlookup(registrar.clone()); let _ = T::Currency::make_free_balance_be(®istrar, BalanceOf::::max_value()); - let registrar_origin = T::RegistrarOrigin::successful_origin(); + let registrar_origin = T::RegistrarOrigin::try_successful_origin() + .expect("RegistrarOrigin has no successful origin required for the benchmark"); Identity::::add_registrar(registrar_origin, registrar_lookup)?; Identity::::set_fee(RawOrigin::Signed(registrar.clone()).into(), i, 10u32.into())?; let fields = @@ -121,7 +122,8 @@ benchmarks! { add_registrar { let r in 1 .. T::MaxRegistrars::get() - 1 => add_registrars::(r)?; ensure!(Registrars::::get().len() as u32 == r, "Registrars not set up correctly."); - let origin = T::RegistrarOrigin::successful_origin(); + let origin = + T::RegistrarOrigin::try_successful_origin().map_err(|_| BenchmarkError::Weightless)?; let account = T::Lookup::unlookup(account("registrar", r + 1, SEED)); }: _(origin, account) verify { @@ -280,7 +282,8 @@ benchmarks! { let r in 1 .. T::MaxRegistrars::get() - 1 => add_registrars::(r)?; - let registrar_origin = T::RegistrarOrigin::successful_origin(); + let registrar_origin = T::RegistrarOrigin::try_successful_origin() + .expect("RegistrarOrigin has no successful origin required for the benchmark"); Identity::::add_registrar(registrar_origin, caller_lookup)?; let registrars = Registrars::::get(); ensure!(registrars[r as usize].as_ref().unwrap().fee == 0u32.into(), "Fee already set."); @@ -297,7 +300,8 @@ benchmarks! { let r in 1 .. T::MaxRegistrars::get() - 1 => add_registrars::(r)?; - let registrar_origin = T::RegistrarOrigin::successful_origin(); + let registrar_origin = T::RegistrarOrigin::try_successful_origin() + .expect("RegistrarOrigin has no successful origin required for the benchmark"); Identity::::add_registrar(registrar_origin, caller_lookup)?; let registrars = Registrars::::get(); ensure!(registrars[r as usize].as_ref().unwrap().account == caller, "id not set."); @@ -315,7 +319,8 @@ benchmarks! { let r in 1 .. T::MaxRegistrars::get() - 1 => add_registrars::(r)?; - let registrar_origin = T::RegistrarOrigin::successful_origin(); + let registrar_origin = T::RegistrarOrigin::try_successful_origin() + .expect("RegistrarOrigin has no successful origin required for the benchmark"); Identity::::add_registrar(registrar_origin, caller_lookup)?; let fields = IdentityFields( IdentityField::Display | IdentityField::Legal | IdentityField::Web | IdentityField::Riot @@ -347,7 +352,8 @@ benchmarks! { let info_hash = T::Hashing::hash_of(&info); Identity::::set_identity(user_origin.clone(), Box::new(info))?; - let registrar_origin = T::RegistrarOrigin::successful_origin(); + let registrar_origin = T::RegistrarOrigin::try_successful_origin() + .expect("RegistrarOrigin has no successful origin required for the benchmark"); Identity::::add_registrar(registrar_origin, caller_lookup)?; Identity::::request_judgement(user_origin, r, 10u32.into())?; }: _(RawOrigin::Signed(caller), r, user_lookup, Judgement::Reasonable, info_hash) @@ -385,7 +391,8 @@ benchmarks! { )?; } ensure!(IdentityOf::::contains_key(&target), "Identity not set"); - let origin = T::ForceOrigin::successful_origin(); + let origin = + T::ForceOrigin::try_successful_origin().map_err(|_| BenchmarkError::Weightless)?; }: _(origin, target_lookup) verify { ensure!(!IdentityOf::::contains_key(&target), "Identity not removed"); diff --git a/frame/lottery/src/benchmarking.rs b/frame/lottery/src/benchmarking.rs index 658dd25a9..0db7fabb3 100644 --- a/frame/lottery/src/benchmarking.rs +++ b/frame/lottery/src/benchmarking.rs @@ -21,7 +21,7 @@ use super::*; -use frame_benchmarking::v1::{account, benchmarks, whitelisted_caller}; +use frame_benchmarking::v1::{account, benchmarks, whitelisted_caller, BenchmarkError}; use frame_support::{ storage::bounded_vec::BoundedVec, traits::{EnsureOrigin, OnInitialize}, @@ -43,7 +43,8 @@ fn setup_lottery(repeat: bool) -> Result<(), &'static str> { ]; // Last call will be the match for worst case scenario. calls.push(frame_system::Call::::remark { remark: vec![] }.into()); - let origin = T::ManagerOrigin::successful_origin(); + let origin = T::ManagerOrigin::try_successful_origin() + .expect("ManagerOrigin has no successful origin required for the benchmark"); Lottery::::set_calls(origin.clone(), calls)?; Lottery::::start_lottery(origin, price, length, delay, repeat)?; Ok(()) @@ -76,7 +77,8 @@ benchmarks! { set_calls { let n in 0 .. T::MaxCalls::get() as u32; let calls = vec![frame_system::Call::::remark { remark: vec![] }.into(); n as usize]; - let origin = T::ManagerOrigin::successful_origin(); + let origin = + T::ManagerOrigin::try_successful_origin().map_err(|_| BenchmarkError::Weightless)?; assert!(CallIndices::::get().is_empty()); }: _(origin, calls) verify { @@ -89,7 +91,8 @@ benchmarks! { let price = BalanceOf::::max_value(); let end = 10u32.into(); let payout = 5u32.into(); - let origin = T::ManagerOrigin::successful_origin(); + let origin = + T::ManagerOrigin::try_successful_origin().map_err(|_| BenchmarkError::Weightless)?; }: _(origin, price, end, payout, true) verify { assert!(crate::Lottery::::get().is_some()); @@ -98,7 +101,8 @@ benchmarks! { stop_repeat { setup_lottery::(true)?; assert_eq!(crate::Lottery::::get().unwrap().repeat, true); - let origin = T::ManagerOrigin::successful_origin(); + let origin = + T::ManagerOrigin::try_successful_origin().map_err(|_| BenchmarkError::Weightless)?; }: _(origin) verify { assert_eq!(crate::Lottery::::get().unwrap().repeat, false); diff --git a/frame/membership/src/lib.rs b/frame/membership/src/lib.rs index 6d973a15f..8b39a08ce 100644 --- a/frame/membership/src/lib.rs +++ b/frame/membership/src/lib.rs @@ -362,15 +362,17 @@ impl, I: 'static> SortedMembers for Pallet { #[cfg(feature = "runtime-benchmarks")] mod benchmark { use super::{Pallet as Membership, *}; - use frame_benchmarking::v1::{account, benchmarks_instance_pallet, whitelist}; + use frame_benchmarking::v1::{account, benchmarks_instance_pallet, whitelist, BenchmarkError}; use frame_support::{assert_ok, traits::EnsureOrigin}; use frame_system::RawOrigin; const SEED: u32 = 0; fn set_members, I: 'static>(members: Vec, prime: Option) { - let reset_origin = T::ResetOrigin::successful_origin(); - let prime_origin = T::PrimeOrigin::successful_origin(); + let reset_origin = T::ResetOrigin::try_successful_origin() + .expect("ResetOrigin has no successful origin required for the benchmark"); + let prime_origin = T::PrimeOrigin::try_successful_origin() + .expect("PrimeOrigin has no successful origin required for the benchmark"); assert_ok!(>::reset_members(reset_origin, members.clone())); if let Some(prime) = prime.map(|i| members[i].clone()) { @@ -390,9 +392,11 @@ mod benchmark { let new_member = account::("add", m, SEED); let new_member_lookup = T::Lookup::unlookup(new_member.clone()); }: { - assert_ok!(>::add_member(T::AddOrigin::successful_origin(), new_member_lookup)); - } - verify { + assert_ok!(>::add_member( + T::AddOrigin::try_successful_origin().map_err(|_| BenchmarkError::Weightless)?, + new_member_lookup, + )); + } verify { assert!(>::get().contains(&new_member)); #[cfg(test)] crate::tests::clean(); } @@ -408,7 +412,10 @@ mod benchmark { let to_remove = members.first().cloned().unwrap(); let to_remove_lookup = T::Lookup::unlookup(to_remove.clone()); }: { - assert_ok!(>::remove_member(T::RemoveOrigin::successful_origin(), to_remove_lookup)); + assert_ok!(>::remove_member( + T::RemoveOrigin::try_successful_origin().map_err(|_| BenchmarkError::Weightless)?, + to_remove_lookup, + )); } verify { assert!(!>::get().contains(&to_remove)); // prime is rejigged @@ -428,7 +435,7 @@ mod benchmark { let remove_lookup = T::Lookup::unlookup(remove.clone()); }: { assert_ok!(>::swap_member( - T::SwapOrigin::successful_origin(), + T::SwapOrigin::try_successful_origin().map_err(|_| BenchmarkError::Weightless)?, remove_lookup, add_lookup, )); @@ -448,7 +455,10 @@ mod benchmark { set_members::(members.clone(), Some(members.len() - 1)); let mut new_members = (m..2*m).map(|i| account("member", i, SEED)).collect::>(); }: { - assert_ok!(>::reset_members(T::ResetOrigin::successful_origin(), new_members.clone())); + assert_ok!(>::reset_members( + T::ResetOrigin::try_successful_origin().map_err(|_| BenchmarkError::Weightless)?, + new_members.clone(), + )); } verify { new_members.sort(); assert_eq!(>::get(), new_members); @@ -485,7 +495,10 @@ mod benchmark { let prime_lookup = T::Lookup::unlookup(prime.clone()); set_members::(members, None); }: { - assert_ok!(>::set_prime(T::PrimeOrigin::successful_origin(), prime_lookup)); + assert_ok!(>::set_prime( + T::PrimeOrigin::try_successful_origin().map_err(|_| BenchmarkError::Weightless)?, + prime_lookup, + )); } verify { assert!(>::get().is_some()); assert!(::get_prime().is_some()); @@ -498,7 +511,9 @@ mod benchmark { let prime = members.last().cloned().unwrap(); set_members::(members, None); }: { - assert_ok!(>::clear_prime(T::PrimeOrigin::successful_origin())); + assert_ok!(>::clear_prime( + T::PrimeOrigin::try_successful_origin().map_err(|_| BenchmarkError::Weightless)?, + )); } verify { assert!(>::get().is_none()); assert!(::get_prime().is_none()); diff --git a/frame/nfts/src/benchmarking.rs b/frame/nfts/src/benchmarking.rs index 17ff28b82..a4007f6b2 100644 --- a/frame/nfts/src/benchmarking.rs +++ b/frame/nfts/src/benchmarking.rs @@ -22,7 +22,7 @@ use super::*; use enumflags2::{BitFlag, BitFlags}; use frame_benchmarking::v1::{ - account, benchmarks_instance_pallet, whitelist_account, whitelisted_caller, + account, benchmarks_instance_pallet, whitelist_account, whitelisted_caller, BenchmarkError, }; use frame_support::{ assert_ok, @@ -151,7 +151,8 @@ fn default_item_config() -> ItemConfig { benchmarks_instance_pallet! { create { let collection = T::Helper::collection(0); - let origin = T::CreateOrigin::successful_origin(&collection); + let origin = T::CreateOrigin::try_successful_origin(&collection) + .map_err(|_| BenchmarkError::Weightless)?; let caller = T::CreateOrigin::ensure_origin(origin.clone(), &collection).unwrap(); whitelist_account!(caller); let admin = T::Lookup::unlookup(caller.clone()); @@ -311,7 +312,8 @@ benchmarks_instance_pallet! { force_collection_owner { let (collection, _, _) = create_collection::(); - let origin = T::ForceOrigin::successful_origin(); + let origin = + T::ForceOrigin::try_successful_origin().map_err(|_| BenchmarkError::Weightless)?; let target: T::AccountId = account("target", 0, SEED); let target_lookup = T::Lookup::unlookup(target.clone()); T::Currency::make_free_balance_be(&target, T::Currency::minimum_balance()); @@ -326,7 +328,8 @@ benchmarks_instance_pallet! { force_collection_config { let (collection, caller, _) = create_collection::(); - let origin = T::ForceOrigin::successful_origin(); + let origin = + T::ForceOrigin::try_successful_origin().map_err(|_| BenchmarkError::Weightless)?; let call = Call::::force_collection_config { collection, config: make_collection_config::(CollectionSetting::DepositRequired.into()), diff --git a/frame/nis/src/benchmarking.rs b/frame/nis/src/benchmarking.rs index b01d414c4..10d3a238a 100644 --- a/frame/nis/src/benchmarking.rs +++ b/frame/nis/src/benchmarking.rs @@ -20,7 +20,7 @@ #![cfg(feature = "runtime-benchmarks")] use super::*; -use frame_benchmarking::v1::{account, benchmarks, whitelisted_caller}; +use frame_benchmarking::v1::{account, benchmarks, whitelisted_caller, BenchmarkError}; use frame_support::traits::{nonfungible::Inspect, Currency, EnsureOrigin, Get}; use frame_system::RawOrigin; use sp_arithmetic::Perquintill; @@ -100,7 +100,8 @@ benchmarks! { } fund_deficit { - let origin = T::FundOrigin::successful_origin(); + let origin = + T::FundOrigin::try_successful_origin().map_err(|_| BenchmarkError::Weightless)?; let caller: T::AccountId = whitelisted_caller(); let bid = T::MinBid::get().max(One::one()); T::Currency::make_free_balance_be(&caller, bid); diff --git a/frame/preimage/src/benchmarking.rs b/frame/preimage/src/benchmarking.rs index f9526b67d..56361b88c 100644 --- a/frame/preimage/src/benchmarking.rs +++ b/frame/preimage/src/benchmarking.rs @@ -18,7 +18,7 @@ //! Preimage pallet benchmarking. use super::*; -use frame_benchmarking::v1::{account, benchmarks, whitelist_account}; +use frame_benchmarking::v1::{account, benchmarks, whitelist_account, BenchmarkError}; use frame_support::assert_ok; use frame_system::RawOrigin; use sp_runtime::traits::Bounded; @@ -62,7 +62,11 @@ benchmarks! { let caller = funded_account::("caller", 0); whitelist_account!(caller); let (preimage, hash) = sized_preimage_and_hash::(s); - assert_ok!(Preimage::::request_preimage(T::ManagerOrigin::successful_origin(), hash)); + assert_ok!(Preimage::::request_preimage( + T::ManagerOrigin::try_successful_origin() + .expect("ManagerOrigin has no successful origin required for the benchmark"), + hash, + )); }: note_preimage(RawOrigin::Signed(caller), preimage) verify { assert!(Preimage::::have_preimage(&hash)); @@ -71,9 +75,15 @@ benchmarks! { note_no_deposit_preimage { let s in 0 .. MAX_SIZE; let (preimage, hash) = sized_preimage_and_hash::(s); - assert_ok!(Preimage::::request_preimage(T::ManagerOrigin::successful_origin(), hash)); - }: note_preimage(T::ManagerOrigin::successful_origin(), preimage) - verify { + assert_ok!(Preimage::::request_preimage( + T::ManagerOrigin::try_successful_origin() + .expect("ManagerOrigin has no successful origin required for the benchmark"), + hash, + )); + }: note_preimage( + T::ManagerOrigin::try_successful_origin().map_err(|_| BenchmarkError::Weightless)?, + preimage + ) verify { assert!(Preimage::::have_preimage(&hash)); } @@ -90,9 +100,15 @@ benchmarks! { // Cheap unnote - will not unreserve since there's no deposit held. unnote_no_deposit_preimage { let (preimage, hash) = preimage_and_hash::(); - assert_ok!(Preimage::::note_preimage(T::ManagerOrigin::successful_origin(), preimage)); - }: unnote_preimage(T::ManagerOrigin::successful_origin(), hash) - verify { + assert_ok!(Preimage::::note_preimage( + T::ManagerOrigin::try_successful_origin() + .expect("ManagerOrigin has no successful origin required for the benchmark"), + preimage, + )); + }: unnote_preimage( + T::ManagerOrigin::try_successful_origin().map_err(|_| BenchmarkError::Weightless)?, + hash + ) verify { assert!(!Preimage::::have_preimage(&hash)); } @@ -102,8 +118,10 @@ benchmarks! { let noter = funded_account::("noter", 0); whitelist_account!(noter); assert_ok!(Preimage::::note_preimage(RawOrigin::Signed(noter.clone()).into(), preimage)); - }: _(T::ManagerOrigin::successful_origin(), hash) - verify { + }: _( + T::ManagerOrigin::try_successful_origin().map_err(|_| BenchmarkError::Weightless)?, + hash + ) verify { let deposit = T::BaseDeposit::get() + T::ByteDeposit::get() * MAX_SIZE.into(); let s = RequestStatus::Requested { deposit: Some((noter, deposit)), count: 1, len: Some(MAX_SIZE) }; assert_eq!(StatusFor::::get(&hash), Some(s)); @@ -111,26 +129,40 @@ benchmarks! { // Cheap request - would unreserve the deposit but none was held. request_no_deposit_preimage { let (preimage, hash) = preimage_and_hash::(); - assert_ok!(Preimage::::note_preimage(T::ManagerOrigin::successful_origin(), preimage)); - }: request_preimage(T::ManagerOrigin::successful_origin(), hash) - verify { + assert_ok!(Preimage::::note_preimage( + T::ManagerOrigin::try_successful_origin() + .expect("ManagerOrigin has no successful origin required for the benchmark"), + preimage, + )); + }: request_preimage( + T::ManagerOrigin::try_successful_origin().map_err(|_| BenchmarkError::Weightless)?, + hash + ) verify { let s = RequestStatus::Requested { deposit: None, count: 2, len: Some(MAX_SIZE) }; assert_eq!(StatusFor::::get(&hash), Some(s)); } // Cheap request - the preimage is not yet noted, so deposit to unreserve. request_unnoted_preimage { let (_, hash) = preimage_and_hash::(); - }: request_preimage(T::ManagerOrigin::successful_origin(), hash) - verify { + }: request_preimage( + T::ManagerOrigin::try_successful_origin().map_err(|_| BenchmarkError::Weightless)?, + hash + ) verify { let s = RequestStatus::Requested { deposit: None, count: 1, len: None }; assert_eq!(StatusFor::::get(&hash), Some(s)); } // Cheap request - the preimage is already requested, so just a counter bump. request_requested_preimage { let (_, hash) = preimage_and_hash::(); - assert_ok!(Preimage::::request_preimage(T::ManagerOrigin::successful_origin(), hash)); - }: request_preimage(T::ManagerOrigin::successful_origin(), hash) - verify { + assert_ok!(Preimage::::request_preimage( + T::ManagerOrigin::try_successful_origin() + .expect("ManagerOrigin has no successful origin required for the benchmark"), + hash, + )); + }: request_preimage( + T::ManagerOrigin::try_successful_origin().map_err(|_| BenchmarkError::Weightless)?, + hash + ) verify { let s = RequestStatus::Requested { deposit: None, count: 2, len: None }; assert_eq!(StatusFor::::get(&hash), Some(s)); } @@ -138,27 +170,53 @@ benchmarks! { // Expensive unrequest - last reference and it's noted, so will destroy the preimage. unrequest_preimage { let (preimage, hash) = preimage_and_hash::(); - assert_ok!(Preimage::::request_preimage(T::ManagerOrigin::successful_origin(), hash)); - assert_ok!(Preimage::::note_preimage(T::ManagerOrigin::successful_origin(), preimage)); - }: _(T::ManagerOrigin::successful_origin(), hash) - verify { + assert_ok!(Preimage::::request_preimage( + T::ManagerOrigin::try_successful_origin() + .expect("ManagerOrigin has no successful origin required for the benchmark"), + hash, + )); + assert_ok!(Preimage::::note_preimage( + T::ManagerOrigin::try_successful_origin() + .expect("ManagerOrigin has no successful origin required for the benchmark"), + preimage, + )); + }: _( + T::ManagerOrigin::try_successful_origin().map_err(|_| BenchmarkError::Weightless)?, + hash + ) verify { assert_eq!(StatusFor::::get(&hash), None); } // Cheap unrequest - last reference, but it's not noted. unrequest_unnoted_preimage { let (_, hash) = preimage_and_hash::(); - assert_ok!(Preimage::::request_preimage(T::ManagerOrigin::successful_origin(), hash)); - }: unrequest_preimage(T::ManagerOrigin::successful_origin(), hash) - verify { + assert_ok!(Preimage::::request_preimage( + T::ManagerOrigin::try_successful_origin() + .expect("ManagerOrigin has no successful origin required for the benchmark"), + hash, + )); + }: unrequest_preimage( + T::ManagerOrigin::try_successful_origin().map_err(|_| BenchmarkError::Weightless)?, + hash + ) verify { assert_eq!(StatusFor::::get(&hash), None); } // Cheap unrequest - not the last reference. unrequest_multi_referenced_preimage { let (_, hash) = preimage_and_hash::(); - assert_ok!(Preimage::::request_preimage(T::ManagerOrigin::successful_origin(), hash)); - assert_ok!(Preimage::::request_preimage(T::ManagerOrigin::successful_origin(), hash)); - }: unrequest_preimage(T::ManagerOrigin::successful_origin(), hash) - verify { + assert_ok!(Preimage::::request_preimage( + T::ManagerOrigin::try_successful_origin() + .expect("ManagerOrigin has no successful origin required for the benchmark"), + hash, + )); + assert_ok!(Preimage::::request_preimage( + T::ManagerOrigin::try_successful_origin() + .expect("ManagerOrigin has no successful origin required for the benchmark"), + hash, + )); + }: unrequest_preimage( + T::ManagerOrigin::try_successful_origin().map_err(|_| BenchmarkError::Weightless)?, + hash + ) verify { let s = RequestStatus::Requested { deposit: None, count: 1, len: None }; assert_eq!(StatusFor::::get(&hash), Some(s)); } diff --git a/frame/ranked-collective/src/benchmarking.rs b/frame/ranked-collective/src/benchmarking.rs index 48df7f1f1..ad81325be 100644 --- a/frame/ranked-collective/src/benchmarking.rs +++ b/frame/ranked-collective/src/benchmarking.rs @@ -21,7 +21,9 @@ use super::*; #[allow(unused_imports)] use crate::Pallet as RankedCollective; -use frame_benchmarking::v1::{account, benchmarks_instance_pallet, whitelisted_caller}; +use frame_benchmarking::v1::{ + account, benchmarks_instance_pallet, whitelisted_caller, BenchmarkError, +}; use frame_support::{assert_ok, dispatch::UnfilteredDispatchable}; use frame_system::RawOrigin as SystemOrigin; @@ -35,13 +37,15 @@ fn make_member, I: 'static>(rank: Rank) -> T::AccountId { let who = account::("member", MemberCount::::get(0), SEED); let who_lookup = T::Lookup::unlookup(who.clone()); assert_ok!(Pallet::::add_member( - T::PromoteOrigin::successful_origin(), - who_lookup.clone() + T::PromoteOrigin::try_successful_origin() + .expect("PromoteOrigin has no successful origin required for the benchmark"), + who_lookup.clone(), )); for _ in 0..rank { assert_ok!(Pallet::::promote_member( - T::PromoteOrigin::successful_origin(), - who_lookup.clone() + T::PromoteOrigin::try_successful_origin() + .expect("PromoteOrigin has no successful origin required for the benchmark"), + who_lookup.clone(), )); } who @@ -51,7 +55,8 @@ benchmarks_instance_pallet! { add_member { let who = account::("member", 0, SEED); let who_lookup = T::Lookup::unlookup(who.clone()); - let origin = T::PromoteOrigin::successful_origin(); + let origin = + T::PromoteOrigin::try_successful_origin().map_err(|_| BenchmarkError::Weightless)?; let call = Call::::add_member { who: who_lookup }; }: { call.dispatch_bypass_filter(origin)? } verify { @@ -67,7 +72,8 @@ benchmarks_instance_pallet! { let who_lookup = T::Lookup::unlookup(who.clone()); let last = make_member::(rank); let last_index = (0..=rank).map(|r| IdToIndex::::get(r, &last).unwrap()).collect::>(); - let origin = T::DemoteOrigin::successful_origin(); + let origin = + T::DemoteOrigin::try_successful_origin().map_err(|_| BenchmarkError::Weightless)?; let call = Call::::remove_member { who: who_lookup, min_rank: rank }; }: { call.dispatch_bypass_filter(origin)? } verify { @@ -83,7 +89,8 @@ benchmarks_instance_pallet! { let rank = r as u16; let who = make_member::(rank); let who_lookup = T::Lookup::unlookup(who.clone()); - let origin = T::PromoteOrigin::successful_origin(); + let origin = + T::PromoteOrigin::try_successful_origin().map_err(|_| BenchmarkError::Weightless)?; let call = Call::::promote_member { who: who_lookup }; }: { call.dispatch_bypass_filter(origin)? } verify { @@ -99,7 +106,8 @@ benchmarks_instance_pallet! { let who_lookup = T::Lookup::unlookup(who.clone()); let last = make_member::(rank); let last_index = IdToIndex::::get(rank, &last).unwrap(); - let origin = T::DemoteOrigin::successful_origin(); + let origin = + T::DemoteOrigin::try_successful_origin().map_err(|_| BenchmarkError::Weightless)?; let call = Call::::demote_member { who: who_lookup }; }: { call.dispatch_bypass_filter(origin)? } verify { @@ -115,14 +123,19 @@ benchmarks_instance_pallet! { vote { let caller: T::AccountId = whitelisted_caller(); let caller_lookup = T::Lookup::unlookup(caller.clone()); - assert_ok!(Pallet::::add_member(T::PromoteOrigin::successful_origin(), caller_lookup.clone())); + assert_ok!(Pallet::::add_member( + T::PromoteOrigin::try_successful_origin() + .expect("PromoteOrigin has no successful origin required for the benchmark"), + caller_lookup.clone(), + )); // Create a poll let class = T::Polls::classes().into_iter().next().unwrap(); let rank = T::MinRankOfClass::convert(class.clone()); for _ in 0..rank { assert_ok!(Pallet::::promote_member( - T::PromoteOrigin::successful_origin(), - caller_lookup.clone() + T::PromoteOrigin::try_successful_origin() + .expect("PromoteOrigin has no successful origin required for the benchmark"), + caller_lookup.clone(), )); } diff --git a/frame/ranked-collective/src/lib.rs b/frame/ranked-collective/src/lib.rs index b057a5750..84be1243c 100644 --- a/frame/ranked-collective/src/lib.rs +++ b/frame/ranked-collective/src/lib.rs @@ -259,19 +259,6 @@ impl, I: 'static, const MIN_RANK: u16> EnsureOrigin::get(MIN_RANK, 0).ok_or(())?; Ok(frame_system::RawOrigin::Signed(who).into()) } - - #[cfg(feature = "runtime-benchmarks")] - fn successful_origin() -> T::RuntimeOrigin { - match Self::try_successful_origin() { - Ok(o) => o, - Err(()) => { - let who: T::AccountId = frame_benchmarking::whitelisted_caller(); - crate::Pallet::::do_add_member_to_rank(who.clone(), MIN_RANK) - .expect("failed to add ranked member"); - frame_system::RawOrigin::Signed(who).into() - }, - } - } } /// Guard to ensure that the given origin is a member of the collective. The account ID of the @@ -295,19 +282,6 @@ impl, I: 'static, const MIN_RANK: u16> EnsureOrigin::get(MIN_RANK, 0).ok_or(())?; Ok(frame_system::RawOrigin::Signed(who).into()) } - - #[cfg(feature = "runtime-benchmarks")] - fn successful_origin() -> T::RuntimeOrigin { - match Self::try_successful_origin() { - Ok(o) => o, - Err(()) => { - let who: T::AccountId = frame_benchmarking::whitelisted_caller(); - crate::Pallet::::do_add_member_to_rank(who.clone(), MIN_RANK) - .expect("failed to add ranked member"); - frame_system::RawOrigin::Signed(who).into() - }, - } - } } /// Guard to ensure that the given origin is a member of the collective. The pair of both the @@ -331,19 +305,6 @@ impl, I: 'static, const MIN_RANK: u16> EnsureOrigin::get(MIN_RANK, 0).ok_or(())?; Ok(frame_system::RawOrigin::Signed(who).into()) } - - #[cfg(feature = "runtime-benchmarks")] - fn successful_origin() -> T::RuntimeOrigin { - match Self::try_successful_origin() { - Ok(o) => o, - Err(()) => { - let who: T::AccountId = frame_benchmarking::whitelisted_caller(); - crate::Pallet::::do_add_member_to_rank(who.clone(), MIN_RANK) - .expect("failed to add ranked member"); - frame_system::RawOrigin::Signed(who).into() - }, - } - } } #[frame_support::pallet] diff --git a/frame/referenda/src/benchmarking.rs b/frame/referenda/src/benchmarking.rs index db5299e0d..62b37857f 100644 --- a/frame/referenda/src/benchmarking.rs +++ b/frame/referenda/src/benchmarking.rs @@ -20,7 +20,9 @@ use super::*; use crate::Pallet as Referenda; use assert_matches::assert_matches; -use frame_benchmarking::v1::{account, benchmarks_instance_pallet, whitelist_account}; +use frame_benchmarking::v1::{ + account, benchmarks_instance_pallet, whitelist_account, BenchmarkError, +}; use frame_support::{ assert_ok, dispatch::UnfilteredDispatchable, @@ -48,8 +50,7 @@ fn dummy_call, I: 'static>() -> Bounded<>::RuntimeCa T::Preimages::bound(call).unwrap() } -fn create_referendum, I: 'static>() -> (T::RuntimeOrigin, ReferendumIndex) { - let origin: T::RuntimeOrigin = T::SubmitOrigin::successful_origin(); +fn create_referendum, I: 'static>(origin: T::RuntimeOrigin) -> ReferendumIndex { if let Ok(caller) = frame_system::ensure_signed(origin.clone()) { T::Currency::make_free_balance_be(&caller, BalanceOf::::max_value()); whitelist_account!(caller); @@ -61,7 +62,7 @@ fn create_referendum, I: 'static>() -> (T::RuntimeOrigin, Referendu let call = crate::Call::::submit { proposal_origin, proposal, enactment_moment }; assert_ok!(call.dispatch_bypass_filter(origin.clone())); let index = ReferendumCount::::get() - 1; - (origin, index) + index } fn place_deposit, I: 'static>(index: ReferendumIndex) { @@ -75,6 +76,7 @@ fn nudge, I: 'static>(index: ReferendumIndex) { } fn fill_queue, I: 'static>( + origin: T::RuntimeOrigin, index: ReferendumIndex, spaces: u32, pass_after: u32, @@ -82,7 +84,7 @@ fn fill_queue, I: 'static>( // First, create enough other referendums to fill the track. let mut others = vec![]; for _ in 0..info::(index).max_deciding { - let (_origin, index) = create_referendum::(); + let index = create_referendum::(origin.clone()); place_deposit::(index); others.push(index); } @@ -90,7 +92,7 @@ fn fill_queue, I: 'static>( // We will also need enough referenda which are queued and passing, we want `MaxQueued - 1` // in order to force the maximum amount of work to insert ours into the queue. for _ in spaces..T::MaxQueued::get() { - let (_origin, index) = create_referendum::(); + let index = create_referendum::(origin.clone()); place_deposit::(index); make_passing_after::(index, Perbill::from_percent(pass_after)); others.push(index); @@ -194,7 +196,8 @@ fn is_not_confirming, I: 'static>(index: ReferendumIndex) -> bool { benchmarks_instance_pallet! { submit { - let origin: T::RuntimeOrigin = T::SubmitOrigin::successful_origin(); + let origin = + T::SubmitOrigin::try_successful_origin().map_err(|_| BenchmarkError::Weightless)?; if let Ok(caller) = frame_system::ensure_signed(origin.clone()) { T::Currency::make_free_balance_be(&caller, BalanceOf::::max_value()); whitelist_account!(caller); @@ -210,15 +213,19 @@ benchmarks_instance_pallet! { } place_decision_deposit_preparing { - let (origin, index) = create_referendum::(); + let origin = + T::SubmitOrigin::try_successful_origin().map_err(|_| BenchmarkError::Weightless)?; + let index = create_referendum::(origin.clone()); }: place_decision_deposit(origin, index) verify { assert!(Referenda::::ensure_ongoing(index).unwrap().decision_deposit.is_some()); } place_decision_deposit_queued { - let (origin, index) = create_referendum::(); - fill_queue::(index, 1, 90); + let origin = + T::SubmitOrigin::try_successful_origin().map_err(|_| BenchmarkError::Weightless)?; + let index = create_referendum::(origin.clone()); + fill_queue::(origin.clone(), index, 1, 90); }: place_decision_deposit(origin, index) verify { let track = Referenda::::ensure_ongoing(index).unwrap().track; @@ -227,8 +234,10 @@ benchmarks_instance_pallet! { } place_decision_deposit_not_queued { - let (origin, index) = create_referendum::(); - fill_queue::(index, 0, 90); + let origin = + T::SubmitOrigin::try_successful_origin().map_err(|_| BenchmarkError::Weightless)?; + let index = create_referendum::(origin.clone()); + fill_queue::(origin.clone(), index, 0, 90); let track = Referenda::::ensure_ongoing(index).unwrap().track; assert_eq!(TrackQueue::::get(&track).len() as u32, T::MaxQueued::get()); assert!(TrackQueue::::get(&track).into_iter().all(|(i, _)| i != index)); @@ -239,7 +248,9 @@ benchmarks_instance_pallet! { } place_decision_deposit_passing { - let (origin, index) = create_referendum::(); + let origin = + T::SubmitOrigin::try_successful_origin().map_err(|_| BenchmarkError::Weightless)?; + let index = create_referendum::(origin.clone()); skip_prepare_period::(index); make_passing::(index); }: place_decision_deposit(origin, index) @@ -248,7 +259,9 @@ benchmarks_instance_pallet! { } place_decision_deposit_failing { - let (origin, index) = create_referendum::(); + let origin = + T::SubmitOrigin::try_successful_origin().map_err(|_| BenchmarkError::Weightless)?; + let index = create_referendum::(origin.clone()); skip_prepare_period::(index); }: place_decision_deposit(origin, index) verify { @@ -256,19 +269,31 @@ benchmarks_instance_pallet! { } refund_decision_deposit { - let (origin, index) = create_referendum::(); + let origin = + T::SubmitOrigin::try_successful_origin().map_err(|_| BenchmarkError::Weightless)?; + let index = create_referendum::(origin.clone()); place_deposit::(index); - assert_ok!(Referenda::::cancel(T::CancelOrigin::successful_origin(), index)); + assert_ok!(Referenda::::cancel( + T::CancelOrigin::try_successful_origin() + .expect("CancelOrigin has no successful origin required for the benchmark"), + index, + )); }: _(origin, index) verify { assert_matches!(ReferendumInfoFor::::get(index), Some(ReferendumInfo::Cancelled(_, _, None))); } refund_submission_deposit { - let (origin, index) = create_referendum::(); + let origin = + T::SubmitOrigin::try_successful_origin().map_err(|_| BenchmarkError::Weightless)?; + let index = create_referendum::(origin.clone()); let caller = frame_system::ensure_signed(origin.clone()).unwrap(); let balance = T::Currency::free_balance(&caller); - assert_ok!(Referenda::::cancel(T::CancelOrigin::successful_origin(), index)); + assert_ok!(Referenda::::cancel( + T::CancelOrigin::try_successful_origin() + .expect("CancelOrigin has no successful origin required for the benchmark"), + index, + )); assert_matches!(ReferendumInfoFor::::get(index), Some(ReferendumInfo::Cancelled(_, Some(_), _))); }: _(origin, index) verify { @@ -279,28 +304,42 @@ benchmarks_instance_pallet! { } cancel { - let (_origin, index) = create_referendum::(); + let origin = T::SubmitOrigin::try_successful_origin() + .expect("SubmitOrigin has no successful origin required for the benchmark"); + let index = create_referendum::(origin); place_deposit::(index); - }: _(T::CancelOrigin::successful_origin(), index) - verify { + }: _( + T::CancelOrigin::try_successful_origin().map_err(|_| BenchmarkError::Weightless)?, + index + ) verify { assert_matches!(ReferendumInfoFor::::get(index), Some(ReferendumInfo::Cancelled(..))); } kill { - let (_origin, index) = create_referendum::(); + let origin = T::SubmitOrigin::try_successful_origin() + .expect("SubmitOrigin has no successful origin required for the benchmark"); + let index = create_referendum::(origin); place_deposit::(index); - }: _(T::KillOrigin::successful_origin(), index) - verify { + }: _( + T::KillOrigin::try_successful_origin().map_err(|_| BenchmarkError::Weightless)?, + index + ) verify { assert_matches!(ReferendumInfoFor::::get(index), Some(ReferendumInfo::Killed(..))); } one_fewer_deciding_queue_empty { - let (_origin, index) = create_referendum::(); + let origin = T::SubmitOrigin::try_successful_origin() + .expect("SubmitOrigin has no successful origin required for the benchmark"); + let index = create_referendum::(origin); place_deposit::(index); skip_prepare_period::(index); nudge::(index); let track = Referenda::::ensure_ongoing(index).unwrap().track; - assert_ok!(Referenda::::cancel(T::CancelOrigin::successful_origin(), index)); + assert_ok!(Referenda::::cancel( + T::CancelOrigin::try_successful_origin() + .expect("CancelOrigin has no successful origin required for the benchmark"), + index, + )); assert_eq!(DecidingCount::::get(&track), 1); }: one_fewer_deciding(RawOrigin::Root, track) verify { @@ -308,11 +347,17 @@ benchmarks_instance_pallet! { } one_fewer_deciding_failing { - let (_origin, index) = create_referendum::(); + let origin = T::SubmitOrigin::try_successful_origin() + .expect("SubmitOrigin has no successful origin required for the benchmark"); + let index = create_referendum::(origin.clone()); // No spaces free in the queue. - let queued = fill_queue::(index, 0, 90); + let queued = fill_queue::(origin, index, 0, 90); let track = Referenda::::ensure_ongoing(index).unwrap().track; - assert_ok!(Referenda::::cancel(T::CancelOrigin::successful_origin(), queued[0])); + assert_ok!(Referenda::::cancel( + T::CancelOrigin::try_successful_origin() + .expect("CancelOrigin has no successful origin required for the benchmark"), + queued[0], + )); assert_eq!(TrackQueue::::get(&track).len() as u32, T::MaxQueued::get()); let deciding_count = DecidingCount::::get(&track); }: one_fewer_deciding(RawOrigin::Root, track) @@ -327,11 +372,17 @@ benchmarks_instance_pallet! { } one_fewer_deciding_passing { - let (_origin, index) = create_referendum::(); + let origin = T::SubmitOrigin::try_successful_origin() + .expect("SubmitOrigin has no successful origin required for the benchmark"); + let index = create_referendum::(origin.clone()); // No spaces free in the queue. - let queued = fill_queue::(index, 0, 0); + let queued = fill_queue::(origin, index, 0, 0); let track = Referenda::::ensure_ongoing(index).unwrap().track; - assert_ok!(Referenda::::cancel(T::CancelOrigin::successful_origin(), queued[0])); + assert_ok!(Referenda::::cancel( + T::CancelOrigin::try_successful_origin() + .expect("CancelOrigin has no successful origin required for the benchmark"), + queued[0], + )); assert_eq!(TrackQueue::::get(&track).len() as u32, T::MaxQueued::get()); let deciding_count = DecidingCount::::get(&track); }: one_fewer_deciding(RawOrigin::Root, track) @@ -346,10 +397,12 @@ benchmarks_instance_pallet! { } nudge_referendum_requeued_insertion { + let origin = T::SubmitOrigin::try_successful_origin() + .expect("SubmitOrigin has no successful origin required for the benchmark"); // First create our referendum and place the deposit. It will be failing. - let (_origin, index) = create_referendum::(); + let index = create_referendum::(origin.clone()); place_deposit::(index); - fill_queue::(index, 0, 90); + fill_queue::(origin, index, 0, 90); // Now nudge ours, with the track now full and the queue full of referenda with votes, // ours will not be in the queue. @@ -367,10 +420,12 @@ benchmarks_instance_pallet! { } nudge_referendum_requeued_slide { + let origin = T::SubmitOrigin::try_successful_origin() + .expect("SubmitOrigin has no successful origin required for the benchmark"); // First create our referendum and place the deposit. It will be failing. - let (_origin, index) = create_referendum::(); + let index = create_referendum::(origin.clone()); place_deposit::(index); - fill_queue::(index, 1, 90); + fill_queue::(origin, index, 1, 90); // Now nudge ours, with the track now full, ours will be queued, but with no votes, it // will have the worst position. @@ -393,10 +448,12 @@ benchmarks_instance_pallet! { // free and this failing. It would result in `QUEUE_SIZE - 1` items being shifted for the // insertion at the beginning. + let origin = T::SubmitOrigin::try_successful_origin() + .expect("SubmitOrigin has no successful origin required for the benchmark"); // First create our referendum and place the deposit. It will be failing. - let (_origin, index) = create_referendum::(); + let index = create_referendum::(origin.clone()); place_deposit::(index); - fill_queue::(index, 1, 0); + fill_queue::(origin, index, 1, 0); let track = Referenda::::ensure_ongoing(index).unwrap().track; assert_eq!(TrackQueue::::get(&track).len() as u32, T::MaxQueued::get() - 1); @@ -410,10 +467,12 @@ benchmarks_instance_pallet! { } nudge_referendum_not_queued { + let origin = T::SubmitOrigin::try_successful_origin() + .expect("SubmitOrigin has no successful origin required for the benchmark"); // First create our referendum and place the deposit. It will be failing. - let (_origin, index) = create_referendum::(); + let index = create_referendum::(origin.clone()); place_deposit::(index); - fill_queue::(index, 0, 0); + fill_queue::(origin, index, 0, 0); let track = Referenda::::ensure_ongoing(index).unwrap().track; assert_eq!(TrackQueue::::get(&track).len() as u32, T::MaxQueued::get()); @@ -427,7 +486,9 @@ benchmarks_instance_pallet! { } nudge_referendum_no_deposit { - let (_origin, index) = create_referendum::(); + let origin = T::SubmitOrigin::try_successful_origin() + .expect("SubmitOrigin has no successful origin required for the benchmark"); + let index = create_referendum::(origin); skip_prepare_period::(index); }: nudge_referendum(RawOrigin::Root, index) verify { @@ -436,7 +497,9 @@ benchmarks_instance_pallet! { } nudge_referendum_preparing { - let (_origin, index) = create_referendum::(); + let origin = T::SubmitOrigin::try_successful_origin() + .expect("SubmitOrigin has no successful origin required for the benchmark"); + let index = create_referendum::(origin); place_deposit::(index); }: nudge_referendum(RawOrigin::Root, index) verify { @@ -445,7 +508,9 @@ benchmarks_instance_pallet! { } nudge_referendum_timed_out { - let (_origin, index) = create_referendum::(); + let origin = T::SubmitOrigin::try_successful_origin() + .expect("SubmitOrigin has no successful origin required for the benchmark"); + let index = create_referendum::(origin); skip_timeout_period::(index); }: nudge_referendum(RawOrigin::Root, index) verify { @@ -454,7 +519,9 @@ benchmarks_instance_pallet! { } nudge_referendum_begin_deciding_failing { - let (_origin, index) = create_referendum::(); + let origin = T::SubmitOrigin::try_successful_origin() + .expect("SubmitOrigin has no successful origin required for the benchmark"); + let index = create_referendum::(origin); place_deposit::(index); skip_prepare_period::(index); }: nudge_referendum(RawOrigin::Root, index) @@ -463,7 +530,9 @@ benchmarks_instance_pallet! { } nudge_referendum_begin_deciding_passing { - let (_origin, index) = create_referendum::(); + let origin = T::SubmitOrigin::try_successful_origin() + .expect("SubmitOrigin has no successful origin required for the benchmark"); + let index = create_referendum::(origin); place_deposit::(index); make_passing::(index); skip_prepare_period::(index); @@ -473,7 +542,9 @@ benchmarks_instance_pallet! { } nudge_referendum_begin_confirming { - let (_origin, index) = create_referendum::(); + let origin = T::SubmitOrigin::try_successful_origin() + .expect("SubmitOrigin has no successful origin required for the benchmark"); + let index = create_referendum::(origin); place_deposit::(index); skip_prepare_period::(index); nudge::(index); @@ -485,7 +556,9 @@ benchmarks_instance_pallet! { } nudge_referendum_end_confirming { - let (_origin, index) = create_referendum::(); + let origin = T::SubmitOrigin::try_successful_origin() + .expect("SubmitOrigin has no successful origin required for the benchmark"); + let index = create_referendum::(origin); place_deposit::(index); skip_prepare_period::(index); make_passing::(index); @@ -498,7 +571,9 @@ benchmarks_instance_pallet! { } nudge_referendum_continue_not_confirming { - let (_origin, index) = create_referendum::(); + let origin = T::SubmitOrigin::try_successful_origin() + .expect("SubmitOrigin has no successful origin required for the benchmark"); + let index = create_referendum::(origin); place_deposit::(index); skip_prepare_period::(index); nudge::(index); @@ -512,7 +587,9 @@ benchmarks_instance_pallet! { } nudge_referendum_continue_confirming { - let (_origin, index) = create_referendum::(); + let origin = T::SubmitOrigin::try_successful_origin() + .expect("SubmitOrigin has no successful origin required for the benchmark"); + let index = create_referendum::(origin); place_deposit::(index); make_passing::(index); skip_prepare_period::(index); @@ -525,7 +602,9 @@ benchmarks_instance_pallet! { } nudge_referendum_approved { - let (_origin, index) = create_referendum::(); + let origin = T::SubmitOrigin::try_successful_origin() + .expect("SubmitOrigin has no successful origin required for the benchmark"); + let index = create_referendum::(origin); place_deposit::(index); skip_prepare_period::(index); make_passing::(index); @@ -538,7 +617,9 @@ benchmarks_instance_pallet! { } nudge_referendum_rejected { - let (_origin, index) = create_referendum::(); + let origin = T::SubmitOrigin::try_successful_origin() + .expect("SubmitOrigin has no successful origin required for the benchmark"); + let index = create_referendum::(origin); place_deposit::(index); skip_prepare_period::(index); make_failing::(index); diff --git a/frame/scheduler/src/benchmarking.rs b/frame/scheduler/src/benchmarking.rs index 9ec82f5c8..94a833bd9 100644 --- a/frame/scheduler/src/benchmarking.rs +++ b/frame/scheduler/src/benchmarking.rs @@ -18,7 +18,7 @@ //! Scheduler pallet benchmarking. use super::*; -use frame_benchmarking::v1::{account, benchmarks}; +use frame_benchmarking::v1::{account, benchmarks, BenchmarkError}; use frame_support::{ ensure, traits::{schedule::Priority, BoundedInline}, @@ -244,7 +244,8 @@ benchmarks! { fill_schedule::(when, s)?; assert_eq!(Agenda::::get(when).len(), s as usize); - let schedule_origin = T::ScheduleOrigin::successful_origin(); + let schedule_origin = + T::ScheduleOrigin::try_successful_origin().map_err(|_| BenchmarkError::Weightless)?; }: _>(schedule_origin, when, 0) verify { ensure!( diff --git a/frame/support/src/traits/dispatch.rs b/frame/support/src/traits/dispatch.rs index 36ddf5b50..50c6c2279 100644 --- a/frame/support/src/traits/dispatch.rs +++ b/frame/support/src/traits/dispatch.rs @@ -40,25 +40,12 @@ pub trait EnsureOrigin { /// Perform the origin check. fn try_origin(o: OuterOrigin) -> Result; - /// Returns an outer origin capable of passing `try_origin` check. - /// - /// NOTE: This should generally *NOT* be reimplemented. Instead implement - /// `try_successful_origin`. + /// Attempt to get an outer origin capable of passing `try_origin` check. May return `Err` if it + /// is impossible. /// /// ** Should be used for benchmarking only!!! ** #[cfg(feature = "runtime-benchmarks")] - fn successful_origin() -> OuterOrigin { - Self::try_successful_origin().expect("No origin exists which can satisfy the guard") - } - - /// Attept to get an outer origin capable of passing `try_origin` check. May return `Err` if it - /// is impossible. Default implementation just uses `successful_origin()`. - /// - /// ** Should be used for benchmarking only!!! ** - #[cfg(feature = "runtime-benchmarks")] - fn try_successful_origin() -> Result { - Ok(Self::successful_origin()) - } + fn try_successful_origin() -> Result; } /// [`EnsureOrigin`] implementation that always fails. @@ -171,25 +158,12 @@ pub trait EnsureOriginWithArg { /// Perform the origin check, returning the origin value if unsuccessful. This allows chaining. fn try_origin(o: OuterOrigin, a: &Argument) -> Result; - /// Returns an outer origin capable of passing `try_origin` check. - /// - /// NOTE: This should generally *NOT* be reimplemented. Instead implement - /// `try_successful_origin`. - /// - /// ** Should be used for benchmarking only!!! ** - #[cfg(feature = "runtime-benchmarks")] - fn successful_origin(a: &Argument) -> OuterOrigin { - Self::try_successful_origin(a).expect("No origin exists which can satisfy the guard") - } - - /// Attept to get an outer origin capable of passing `try_origin` check. May return `Err` if it - /// is impossible. Default implementation just uses `successful_origin()`. + /// Attempt to get an outer origin capable of passing `try_origin` check. May return `Err` if it + /// is impossible. /// /// ** Should be used for benchmarking only!!! ** #[cfg(feature = "runtime-benchmarks")] - fn try_successful_origin(a: &Argument) -> Result { - Ok(Self::successful_origin(a)) - } + fn try_successful_origin(a: &Argument) -> Result; } pub struct AsEnsureOriginWithArg(sp_std::marker::PhantomData); diff --git a/frame/system/src/tests.rs b/frame/system/src/tests.rs index 8223f6674..f441e7855 100644 --- a/frame/system/src/tests.rs +++ b/frame/system/src/tests.rs @@ -679,10 +679,13 @@ fn ensure_signed_stuff_works() { #[cfg(feature = "runtime-benchmarks")] { - let successful_origin: RuntimeOrigin = EnsureSigned::successful_origin(); + let successful_origin: RuntimeOrigin = EnsureSigned::try_successful_origin() + .expect("EnsureSigned has no successful origin required for the test"); assert_ok!(EnsureSigned::try_origin(successful_origin)); - let successful_origin: RuntimeOrigin = EnsureSignedBy::::successful_origin(); + let successful_origin: RuntimeOrigin = + EnsureSignedBy::::try_successful_origin() + .expect("EnsureSignedBy has no successful origin required for the test"); assert_ok!(EnsureSignedBy::::try_origin(successful_origin)); } } diff --git a/frame/tips/src/benchmarking.rs b/frame/tips/src/benchmarking.rs index 8ffc53b45..5b1a102b8 100644 --- a/frame/tips/src/benchmarking.rs +++ b/frame/tips/src/benchmarking.rs @@ -19,7 +19,9 @@ #![cfg(feature = "runtime-benchmarks")] -use frame_benchmarking::v1::{account, benchmarks_instance_pallet, whitelisted_caller}; +use frame_benchmarking::v1::{ + account, benchmarks_instance_pallet, whitelisted_caller, BenchmarkError, +}; use frame_support::ensure; use frame_system::RawOrigin; use sp_runtime::traits::Saturating; @@ -196,7 +198,8 @@ benchmarks_instance_pallet! { let reason_hash = T::Hashing::hash(&reason[..]); let hash = T::Hashing::hash_of(&(&reason_hash, &beneficiary)); ensure!(Tips::::contains_key(hash), "tip does not exist"); - let reject_origin = T::RejectOrigin::successful_origin(); + let reject_origin = + T::RejectOrigin::try_successful_origin().map_err(|_| BenchmarkError::Weightless)?; }: _(reject_origin, hash) impl_benchmark_test_suite!(TipsMod, crate::tests::new_test_ext(), crate::tests::Test); diff --git a/frame/treasury/src/benchmarking.rs b/frame/treasury/src/benchmarking.rs index eb4474450..0947618a2 100644 --- a/frame/treasury/src/benchmarking.rs +++ b/frame/treasury/src/benchmarking.rs @@ -21,7 +21,7 @@ use super::{Pallet as Treasury, *}; -use frame_benchmarking::v1::{account, benchmarks_instance_pallet}; +use frame_benchmarking::v1::{account, benchmarks_instance_pallet, BenchmarkError}; use frame_support::{ dispatch::UnfilteredDispatchable, ensure, @@ -99,7 +99,8 @@ benchmarks_instance_pallet! { beneficiary_lookup )?; let proposal_id = Treasury::::proposal_count() - 1; - let reject_origin = T::RejectOrigin::successful_origin(); + let reject_origin = + T::RejectOrigin::try_successful_origin().map_err(|_| BenchmarkError::Weightless)?; }: _(reject_origin, proposal_id) approve_proposal { @@ -112,7 +113,8 @@ benchmarks_instance_pallet! { beneficiary_lookup )?; let proposal_id = Treasury::::proposal_count() - 1; - let approve_origin = T::ApproveOrigin::successful_origin(); + let approve_origin = + T::ApproveOrigin::try_successful_origin().map_err(|_| BenchmarkError::Weightless)?; }: _(approve_origin, proposal_id) remove_approval { @@ -124,7 +126,8 @@ benchmarks_instance_pallet! { )?; let proposal_id = Treasury::::proposal_count() - 1; Treasury::::approve_proposal(RawOrigin::Root.into(), proposal_id)?; - let reject_origin = T::RejectOrigin::successful_origin(); + let reject_origin = + T::RejectOrigin::try_successful_origin().map_err(|_| BenchmarkError::Weightless)?; }: _(reject_origin, proposal_id) on_initialize_proposals { diff --git a/frame/uniques/src/benchmarking.rs b/frame/uniques/src/benchmarking.rs index 5ecb093a5..2656582f0 100644 --- a/frame/uniques/src/benchmarking.rs +++ b/frame/uniques/src/benchmarking.rs @@ -21,7 +21,7 @@ use super::*; use frame_benchmarking::v1::{ - account, benchmarks_instance_pallet, whitelist_account, whitelisted_caller, + account, benchmarks_instance_pallet, whitelist_account, whitelisted_caller, BenchmarkError, }; use frame_support::{ dispatch::UnfilteredDispatchable, @@ -137,7 +137,8 @@ fn assert_last_event, I: 'static>(generic_event: >:: benchmarks_instance_pallet! { create { let collection = T::Helper::collection(0); - let origin = T::CreateOrigin::successful_origin(&collection); + let origin = T::CreateOrigin::try_successful_origin(&collection) + .map_err(|_| BenchmarkError::Weightless)?; let caller = T::CreateOrigin::ensure_origin(origin.clone(), &collection).unwrap(); whitelist_account!(caller); let admin = T::Lookup::unlookup(caller.clone()); @@ -290,7 +291,8 @@ benchmarks_instance_pallet! { force_item_status { let (collection, caller, caller_lookup) = create_collection::(); - let origin = T::ForceOrigin::successful_origin(); + let origin = + T::ForceOrigin::try_successful_origin().map_err(|_| BenchmarkError::Weightless)?; let call = Call::::force_item_status { collection, owner: caller_lookup.clone(), diff --git a/frame/whitelist/src/benchmarking.rs b/frame/whitelist/src/benchmarking.rs index e64842b34..582f4a799 100644 --- a/frame/whitelist/src/benchmarking.rs +++ b/frame/whitelist/src/benchmarking.rs @@ -20,7 +20,7 @@ #![cfg(feature = "runtime-benchmarks")] use super::*; -use frame_benchmarking::v1::benchmarks; +use frame_benchmarking::v1::{benchmarks, BenchmarkError}; use frame_support::{ensure, traits::EnsureOrigin}; #[cfg(test)] @@ -28,7 +28,8 @@ use crate::Pallet as Whitelist; benchmarks! { whitelist_call { - let origin = T::WhitelistOrigin::successful_origin(); + let origin = + T::WhitelistOrigin::try_successful_origin().map_err(|_| BenchmarkError::Weightless)?; let call_hash = Default::default(); }: _(origin, call_hash) verify { @@ -43,7 +44,8 @@ benchmarks! { } remove_whitelisted_call { - let origin = T::WhitelistOrigin::successful_origin(); + let origin = + T::WhitelistOrigin::try_successful_origin().map_err(|_| BenchmarkError::Weightless)?; let call_hash = Default::default(); Pallet::::whitelist_call(origin.clone(), call_hash) .expect("whitelisting call must be successful"); @@ -70,7 +72,8 @@ benchmarks! { // NOTE: we remove `10` because we need some bytes to encode the variants and vec length let n in 1 .. T::Preimages::MAX_LENGTH as u32 - 10; - let origin = T::DispatchWhitelistedOrigin::successful_origin(); + let origin = T::DispatchWhitelistedOrigin::try_successful_origin() + .map_err(|_| BenchmarkError::Weightless)?; let remark = sp_std::vec![1u8; n as usize]; let call: ::RuntimeCall = frame_system::Call::remark { remark }.into(); let call_weight = call.get_dispatch_info().weight; @@ -98,7 +101,8 @@ benchmarks! { dispatch_whitelisted_call_with_preimage { let n in 1 .. 10_000; - let origin = T::DispatchWhitelistedOrigin::successful_origin(); + let origin = T::DispatchWhitelistedOrigin::try_successful_origin() + .map_err(|_| BenchmarkError::Weightless)?; let remark = sp_std::vec![1u8; n as usize]; let call: ::RuntimeCall = frame_system::Call::remark { remark }.into(); From c4b2db5a050dad1129a18ebe9c4e3013038e1ad1 Mon Sep 17 00:00:00 2001 From: Kian Paimani <5588131+kianenigma@users.noreply.github.com> Date: Wed, 1 Feb 2023 10:38:41 -0300 Subject: [PATCH 075/558] hooks default impl missing where clause (#13264) * hooks default impl missing where clause * add tests * Update frame/support/test/tests/pallet_ui/pass/where_clause_missing_hooks.rs --------- Co-authored-by: parity-processbot <> --- .../procedural/src/pallet/expand/hooks.rs | 2 +- .../tests/pallet_ui/duplicate_store_attr.rs | 3 +-- .../pass/where_clause_missing_hooks.rs | 19 +++++++++++++++++++ 3 files changed, 21 insertions(+), 3 deletions(-) create mode 100644 frame/support/test/tests/pallet_ui/pass/where_clause_missing_hooks.rs diff --git a/frame/support/procedural/src/pallet/expand/hooks.rs b/frame/support/procedural/src/pallet/expand/hooks.rs index c41a65b57..987d0691b 100644 --- a/frame/support/procedural/src/pallet/expand/hooks.rs +++ b/frame/support/procedural/src/pallet/expand/hooks.rs @@ -76,7 +76,7 @@ pub fn expand_hooks(def: &mut Def) -> proc_macro2::TokenStream { quote::quote! { impl<#type_impl_gen> #frame_support::traits::Hooks<::BlockNumber> - for Pallet<#type_use_gen> {} + for #pallet_ident<#type_use_gen> #where_clause {} } } else { proc_macro2::TokenStream::new() diff --git a/frame/support/test/tests/pallet_ui/duplicate_store_attr.rs b/frame/support/test/tests/pallet_ui/duplicate_store_attr.rs index d675ddefe..ab318034a 100644 --- a/frame/support/test/tests/pallet_ui/duplicate_store_attr.rs +++ b/frame/support/test/tests/pallet_ui/duplicate_store_attr.rs @@ -22,5 +22,4 @@ mod pallet { type Foo = StorageValue<_, u8>; } -fn main() { -} +fn main() {} diff --git a/frame/support/test/tests/pallet_ui/pass/where_clause_missing_hooks.rs b/frame/support/test/tests/pallet_ui/pass/where_clause_missing_hooks.rs new file mode 100644 index 000000000..bf5f22306 --- /dev/null +++ b/frame/support/test/tests/pallet_ui/pass/where_clause_missing_hooks.rs @@ -0,0 +1,19 @@ +#[frame_support::pallet] +mod pallet { + #[pallet::config] + pub trait Config: frame_system::Config where ::Index: From {} + + #[pallet::pallet] + pub struct Pallet(core::marker::PhantomData); + + #[pallet::call] + impl Pallet where ::Index: From {} + + impl Pallet where ::Index: From { + fn foo(x: u128) { + let _index = ::Index::from(x); + } + } +} + +fn main() {} From b2b2e650f204d9ee2a7b67f5b9cc6ab146ded877 Mon Sep 17 00:00:00 2001 From: Matteo Muraca <56828990+muraca@users.noreply.github.com> Date: Wed, 1 Feb 2023 18:11:34 +0100 Subject: [PATCH 076/558] implemented `contains_prefix` for StorageDoubleMap and StorageNMap (#13232) * implemented `contains_prefix` for StorageDoubleMap and StorageNMap Signed-off-by: muraca * match prefix to next_key Signed-off-by: muraca * warning unexpected behaviour with empty keys Signed-off-by: muraca * clarifications for unhashed::contains_prefixed_key Signed-off-by: muraca * added tests for StorageNMap Signed-off-by: muraca --------- Signed-off-by: muraca --- .../src/storage/generator/double_map.rs | 7 +++ frame/support/src/storage/generator/nmap.rs | 7 +++ frame/support/src/storage/mod.rs | 47 ++++++++++++++++++- frame/support/src/storage/unhashed.rs | 10 ++++ 4 files changed, 70 insertions(+), 1 deletion(-) diff --git a/frame/support/src/storage/generator/double_map.rs b/frame/support/src/storage/generator/double_map.rs index c95dcee9d..b60bb7956 100644 --- a/frame/support/src/storage/generator/double_map.rs +++ b/frame/support/src/storage/generator/double_map.rs @@ -233,6 +233,13 @@ where .into() } + fn contains_prefix(k1: KArg1) -> bool + where + KArg1: EncodeLike, + { + unhashed::contains_prefixed_key(Self::storage_double_map_final_key1(k1).as_ref()) + } + fn iter_prefix_values(k1: KArg1) -> storage::PrefixIterator where KArg1: ?Sized + EncodeLike, diff --git a/frame/support/src/storage/generator/nmap.rs b/frame/support/src/storage/generator/nmap.rs index 79f3d7204..8cf145943 100755 --- a/frame/support/src/storage/generator/nmap.rs +++ b/frame/support/src/storage/generator/nmap.rs @@ -208,6 +208,13 @@ where ) } + fn contains_prefix(partial_key: KP) -> bool + where + K: HasKeyPrefix, + { + unhashed::contains_prefixed_key(&Self::storage_n_map_partial_key(partial_key)) + } + fn iter_prefix_values(partial_key: KP) -> PrefixIterator where K: HasKeyPrefix, diff --git a/frame/support/src/storage/mod.rs b/frame/support/src/storage/mod.rs index 28f2dee99..dd91d8cc2 100644 --- a/frame/support/src/storage/mod.rs +++ b/frame/support/src/storage/mod.rs @@ -563,6 +563,12 @@ pub trait StorageDoubleMap { where KArg1: ?Sized + EncodeLike; + /// Does any value under the first key `k1` (explicitly) exist in storage? + /// Might have unexpected behaviour with empty keys, e.g. `[]`. + fn contains_prefix(k1: KArg1) -> bool + where + KArg1: EncodeLike; + /// Iterate over values that share the first key. fn iter_prefix_values(k1: KArg1) -> PrefixIterator where @@ -739,6 +745,12 @@ pub trait StorageNMap { where K: HasKeyPrefix; + /// Does any value under a `partial_key` prefix (explicitly) exist in storage? + /// Might have unexpected behaviour with empty keys, e.g. `[]`. + fn contains_prefix(partial_key: KP) -> bool + where + K: HasKeyPrefix; + /// Iterate over values that share the partial prefix key. fn iter_prefix_values(partial_key: KP) -> PrefixIterator where @@ -1485,7 +1497,7 @@ pub fn storage_prefix(pallet_name: &[u8], storage_name: &[u8]) -> [u8; 32] { #[cfg(test)] mod test { use super::*; - use crate::{assert_ok, hash::Identity, Twox128}; + use crate::{assert_ok, hash::Identity, pallet_prelude::NMapKey, Twox128}; use bounded_vec::BoundedVec; use frame_support::traits::ConstU32; use generator::StorageValue as _; @@ -1780,6 +1792,39 @@ mod test { #[crate::storage_alias] type FooDoubleMap = StorageDoubleMap>>; + #[crate::storage_alias] + type FooTripleMap = StorageNMap< + Prefix, + (NMapKey, NMapKey, NMapKey), + u64, + >; + + #[test] + fn contains_prefix_works() { + TestExternalities::default().execute_with(|| { + // Test double maps + assert!(FooDoubleMap::iter_prefix_values(1).next().is_none()); + assert_eq!(FooDoubleMap::contains_prefix(1), false); + + assert_ok!(FooDoubleMap::try_append(1, 1, 4)); + assert_ok!(FooDoubleMap::try_append(2, 1, 4)); + assert!(FooDoubleMap::iter_prefix_values(1).next().is_some()); + assert!(FooDoubleMap::contains_prefix(1)); + FooDoubleMap::remove(1, 1); + assert_eq!(FooDoubleMap::contains_prefix(1), false); + + // Test N Maps + assert!(FooTripleMap::iter_prefix_values((1,)).next().is_none()); + assert_eq!(FooTripleMap::contains_prefix((1,)), false); + + FooTripleMap::insert((1, 1, 1), 4); + FooTripleMap::insert((2, 1, 1), 4); + assert!(FooTripleMap::iter_prefix_values((1,)).next().is_some()); + assert!(FooTripleMap::contains_prefix((1,))); + FooTripleMap::remove((1, 1, 1)); + assert_eq!(FooTripleMap::contains_prefix((1,)), false); + }); + } #[test] fn try_append_works() { diff --git a/frame/support/src/storage/unhashed.rs b/frame/support/src/storage/unhashed.rs index 850e93e7d..8388c5f88 100644 --- a/frame/support/src/storage/unhashed.rs +++ b/frame/support/src/storage/unhashed.rs @@ -154,6 +154,16 @@ pub fn clear_prefix( MultiRemovalResults { maybe_cursor, backend: i, unique: i, loops: i } } +/// Returns `true` if the storage contains any key, which starts with a certain prefix, +/// and is longer than said prefix. +/// This means that a key which equals the prefix will not be counted. +pub fn contains_prefixed_key(prefix: &[u8]) -> bool { + match sp_io::storage::next_key(prefix) { + Some(key) => key.starts_with(prefix), + None => false, + } +} + /// Get a Vec of bytes from storage. pub fn get_raw(key: &[u8]) -> Option> { sp_io::storage::get(key).map(|value| value.to_vec()) From 3bc3742d5c0c5269353d7809d9f8f91104a93273 Mon Sep 17 00:00:00 2001 From: Sergej Sakac <73715684+Szegoo@users.noreply.github.com> Date: Wed, 1 Feb 2023 18:17:05 +0100 Subject: [PATCH 077/558] Calling proxy doesn't remove announcement (#13267) * Calling proxy doesn't remove announcement * Update frame/proxy/src/tests.rs Co-authored-by: Kian Paimani <5588131+kianenigma@users.noreply.github.com> * fmt --------- Co-authored-by: Kian Paimani <5588131+kianenigma@users.noreply.github.com> --- frame/proxy/src/lib.rs | 2 -- frame/proxy/src/tests.rs | 17 +++++++++++++++++ 2 files changed, 17 insertions(+), 2 deletions(-) diff --git a/frame/proxy/src/lib.rs b/frame/proxy/src/lib.rs index d98534d16..dfe8c86ca 100644 --- a/frame/proxy/src/lib.rs +++ b/frame/proxy/src/lib.rs @@ -183,8 +183,6 @@ pub mod pallet { /// Dispatch the given `call` from an account that the sender is authorised for through /// `add_proxy`. /// - /// Removes any corresponding announcement(s). - /// /// The dispatch origin for this call must be _Signed_. /// /// Parameters: diff --git a/frame/proxy/src/tests.rs b/frame/proxy/src/tests.rs index 3eb3ab370..0e7db3512 100644 --- a/frame/proxy/src/tests.rs +++ b/frame/proxy/src/tests.rs @@ -288,6 +288,23 @@ fn announcer_must_be_proxy() { }); } +#[test] +fn calling_proxy_doesnt_remove_announcement() { + new_test_ext().execute_with(|| { + assert_ok!(Proxy::add_proxy(RuntimeOrigin::signed(1), 2, ProxyType::Any, 0)); + + let call = Box::new(call_transfer(6, 1)); + let call_hash = BlakeTwo256::hash_of(&call); + + assert_ok!(Proxy::announce(RuntimeOrigin::signed(2), 1, call_hash)); + assert_ok!(Proxy::proxy(RuntimeOrigin::signed(2), 1, None, call)); + + // The announcement is not removed by calling proxy. + let announcements = Announcements::::get(2); + assert_eq!(announcements.0, vec![Announcement { real: 1, call_hash, height: 1 }]); + }); +} + #[test] fn delayed_requires_pre_announcement() { new_test_ext().execute_with(|| { From 68d00e2d5132ef32e564e0320e89dfac57e1c46e Mon Sep 17 00:00:00 2001 From: Sergej Sakac <73715684+Szegoo@users.noreply.github.com> Date: Thu, 2 Feb 2023 07:47:49 +0100 Subject: [PATCH 078/558] Fee and tip represented as asset ID inside `AssetTxFeePaid` (#13083) * fee & tip in the asset ID Balance type * docs * rewrite * update runtime config * docs --- .../asset-tx-payment/src/lib.rs | 26 ++++++++++--------- .../asset-tx-payment/src/payment.rs | 17 ++++++++---- 2 files changed, 26 insertions(+), 17 deletions(-) diff --git a/frame/transaction-payment/asset-tx-payment/src/lib.rs b/frame/transaction-payment/asset-tx-payment/src/lib.rs index 230b30731..645ebb1e1 100644 --- a/frame/transaction-payment/asset-tx-payment/src/lib.rs +++ b/frame/transaction-payment/asset-tx-payment/src/lib.rs @@ -134,8 +134,8 @@ pub mod pallet { /// has been paid by `who` in an asset `asset_id`. AssetTxFeePaid { who: T::AccountId, - actual_fee: BalanceOf, - tip: BalanceOf, + actual_fee: AssetBalanceOf, + tip: AssetBalanceOf, asset_id: Option>, }, } @@ -284,18 +284,20 @@ where let actual_fee = pallet_transaction_payment::Pallet::::compute_actual_fee( len as u32, info, post_info, tip, ); - T::OnChargeAssetTransaction::correct_and_deposit_fee( - &who, - info, - post_info, - actual_fee.into(), - tip.into(), - already_withdrawn.into(), - )?; + + let (converted_fee, converted_tip) = + T::OnChargeAssetTransaction::correct_and_deposit_fee( + &who, + info, + post_info, + actual_fee.into(), + tip.into(), + already_withdrawn.into(), + )?; Pallet::::deposit_event(Event::::AssetTxFeePaid { who, - actual_fee, - tip, + actual_fee: converted_fee, + tip: converted_tip, asset_id, }); }, diff --git a/frame/transaction-payment/asset-tx-payment/src/payment.rs b/frame/transaction-payment/asset-tx-payment/src/payment.rs index 85d1bec4b..ae8253b6e 100644 --- a/frame/transaction-payment/asset-tx-payment/src/payment.rs +++ b/frame/transaction-payment/asset-tx-payment/src/payment.rs @@ -58,6 +58,8 @@ pub trait OnChargeAssetTransaction { /// the corrected amount. /// /// Note: The `fee` already includes the `tip`. + /// + /// Returns the fee and tip in the asset used for payment as (fee, tip). fn correct_and_deposit_fee( who: &T::AccountId, dispatch_info: &DispatchInfoOf, @@ -65,7 +67,7 @@ pub trait OnChargeAssetTransaction { corrected_fee: Self::Balance, tip: Self::Balance, already_withdrawn: Self::LiquidityInfo, - ) -> Result<(), TransactionValidityError>; + ) -> Result<(AssetBalanceOf, AssetBalanceOf), TransactionValidityError>; } /// Allows specifying what to do with the withdrawn asset fees. @@ -132,19 +134,24 @@ where /// Since the predicted fee might have been too high, parts of the fee may be refunded. /// /// Note: The `corrected_fee` already includes the `tip`. + /// + /// Returns the fee and tip in the asset used for payment as (fee, tip). fn correct_and_deposit_fee( who: &T::AccountId, _dispatch_info: &DispatchInfoOf, _post_info: &PostDispatchInfoOf, corrected_fee: Self::Balance, - _tip: Self::Balance, + tip: Self::Balance, paid: Self::LiquidityInfo, - ) -> Result<(), TransactionValidityError> { + ) -> Result<(AssetBalanceOf, AssetBalanceOf), TransactionValidityError> { let min_converted_fee = if corrected_fee.is_zero() { Zero::zero() } else { One::one() }; - // Convert the corrected fee into the asset used for payment. + // Convert the corrected fee and tip into the asset used for payment. let converted_fee = CON::to_asset_balance(corrected_fee, paid.asset()) .map_err(|_| -> TransactionValidityError { InvalidTransaction::Payment.into() })? .max(min_converted_fee); + let converted_tip = CON::to_asset_balance(tip, paid.asset()) + .map_err(|_| -> TransactionValidityError { InvalidTransaction::Payment.into() })?; + // Calculate how much refund we should return. let (final_fee, refund) = paid.split(converted_fee); // Refund to the account that paid the fees. If this fails, the account might have dropped @@ -152,6 +159,6 @@ where let _ = >::resolve(who, refund); // Handle the final fee, e.g. by transferring to the block author or burning. HC::handle_credit(final_fee); - Ok(()) + Ok((converted_fee, converted_tip)) } } From 5d0867c7dcdc50572422611128853ccff63ebd4d Mon Sep 17 00:00:00 2001 From: Davirain Date: Thu, 2 Feb 2023 18:07:48 +0800 Subject: [PATCH 079/558] Move beefy-merkle-tree to utils/binary-merkle-tree and make it generic (#13076) * move BeefyMmrApi to pallet-beefy-mmr * fix test_utils use pallet-beefy-mmr BeefyMmrApi * Move beefy-merkle-tree to utils and Rename to Merkle-tree * fix fmt and test * Update merkle-tree to binary-merkle-tree and Remove Keccak256 mod from merkle-tree * change merkle-tree name to binary-merkle-tree * mirr fix --- Cargo.lock | 11 +-- Cargo.toml | 2 +- frame/beefy-mmr/Cargo.toml | 6 +- frame/beefy-mmr/src/lib.rs | 16 +++- frame/beefy-mmr/src/mock.rs | 2 +- test-utils/runtime/Cargo.toml | 4 +- test-utils/runtime/src/lib.rs | 2 +- .../binary-merkle-tree}/Cargo.toml | 14 ++- .../binary-merkle-tree}/src/lib.rs | 89 ++++++++----------- 9 files changed, 75 insertions(+), 71 deletions(-) rename {frame/beefy-mmr/primitives => utils/binary-merkle-tree}/Cargo.toml (58%) rename {frame/beefy-mmr/primitives => utils/binary-merkle-tree}/src/lib.rs (93%) diff --git a/Cargo.lock b/Cargo.lock index 29ddf3832..a0e9e0e63 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -543,14 +543,14 @@ dependencies = [ ] [[package]] -name = "beefy-merkle-tree" +name = "binary-merkle-tree" version = "4.0.0-dev" dependencies = [ "array-bytes", "env_logger", + "hash-db", "log", - "sp-api", - "sp-beefy", + "sp-core", "sp-runtime", ] @@ -5499,7 +5499,7 @@ name = "pallet-beefy-mmr" version = "4.0.0-dev" dependencies = [ "array-bytes", - "beefy-merkle-tree", + "binary-merkle-tree", "frame-support", "frame-system", "log", @@ -5509,6 +5509,7 @@ dependencies = [ "parity-scale-codec", "scale-info", "serde", + "sp-api", "sp-beefy", "sp-core", "sp-io", @@ -10699,7 +10700,6 @@ dependencies = [ name = "substrate-test-runtime" version = "2.0.0" dependencies = [ - "beefy-merkle-tree", "cfg-if", "frame-support", "frame-system", @@ -10708,6 +10708,7 @@ dependencies = [ "log", "memory-db", "pallet-babe", + "pallet-beefy-mmr", "pallet-timestamp", "parity-scale-codec", "sc-block-builder", diff --git a/Cargo.toml b/Cargo.toml index b004886e8..f87172b5e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -85,7 +85,6 @@ members = [ "frame/balances", "frame/beefy", "frame/beefy-mmr", - "frame/beefy-mmr/primitives", "frame/benchmarking", "frame/benchmarking/pov", "frame/bounties", @@ -246,6 +245,7 @@ members = [ "utils/frame/rpc/client", "utils/prometheus", "utils/wasm-builder", + "utils/binary-merkle-tree", ] # The list of dependencies below (which can be both direct and indirect dependencies) are crates diff --git a/frame/beefy-mmr/Cargo.toml b/frame/beefy-mmr/Cargo.toml index 54da26c39..1d24e821c 100644 --- a/frame/beefy-mmr/Cargo.toml +++ b/frame/beefy-mmr/Cargo.toml @@ -14,7 +14,7 @@ codec = { package = "parity-scale-codec", version = "3.2.2", default-features = log = { version = "0.4.17", default-features = false } scale-info = { version = "2.1.1", default-features = false, features = ["derive"] } serde = { version = "1.0.136", optional = true } -beefy-merkle-tree = { version = "4.0.0-dev", default-features = false, path = "./primitives" } +binary-merkle-tree = { version = "4.0.0-dev", default-features = false, path = "../../utils/binary-merkle-tree" } beefy-primitives = { version = "4.0.0-dev", default-features = false, path = "../../primitives/beefy", package = "sp-beefy" } frame-support = { version = "4.0.0-dev", default-features = false, path = "../support" } frame-system = { version = "4.0.0-dev", default-features = false, path = "../system" } @@ -25,6 +25,7 @@ sp-core = { version = "7.0.0", default-features = false, path = "../../primitive sp-io = { version = "7.0.0", default-features = false, path = "../../primitives/io" } sp-runtime = { version = "7.0.0", default-features = false, path = "../../primitives/runtime" } sp-std = { version = "5.0.0", default-features = false, path = "../../primitives/std" } +sp-api = { version = "4.0.0-dev", default-features = false, path = "../../primitives/api" } [dev-dependencies] array-bytes = "4.1" @@ -34,7 +35,7 @@ sp-staking = { version = "4.0.0-dev", path = "../../primitives/staking" } default = ["std"] std = [ "array-bytes", - "beefy-merkle-tree/std", + "binary-merkle-tree/std", "beefy-primitives/std", "codec/std", "frame-support/std", @@ -49,5 +50,6 @@ std = [ "sp-io/std", "sp-runtime/std", "sp-std/std", + "sp-api/std", ] try-runtime = ["frame-support/try-runtime"] diff --git a/frame/beefy-mmr/src/lib.rs b/frame/beefy-mmr/src/lib.rs index 0b7fc22cd..e5506ecd0 100644 --- a/frame/beefy-mmr/src/lib.rs +++ b/frame/beefy-mmr/src/lib.rs @@ -200,10 +200,24 @@ impl Pallet { .map(T::BeefyAuthorityToMerkleLeaf::convert) .collect::>(); let len = beefy_addresses.len() as u32; - let root = beefy_merkle_tree::merkle_root::<::Hashing, _>( + let root = binary_merkle_tree::merkle_root::<::Hashing, _>( beefy_addresses, ) .into(); BeefyAuthoritySet { id, len, root } } } + +sp_api::decl_runtime_apis! { + /// API useful for BEEFY light clients. + pub trait BeefyMmrApi + where + BeefyAuthoritySet: sp_api::Decode, + { + /// Return the currently active BEEFY authority set proof. + fn authority_set_proof() -> BeefyAuthoritySet; + + /// Return the next/queued BEEFY authority set proof. + fn next_authority_set_proof() -> BeefyNextAuthoritySet; + } +} diff --git a/frame/beefy-mmr/src/mock.rs b/frame/beefy-mmr/src/mock.rs index 0a64ad3fc..2de71cd5b 100644 --- a/frame/beefy-mmr/src/mock.rs +++ b/frame/beefy-mmr/src/mock.rs @@ -147,7 +147,7 @@ impl BeefyDataProvider> for DummyDataProvider { fn extra_data() -> Vec { let mut col = vec![(15, vec![1, 2, 3]), (5, vec![4, 5, 6])]; col.sort(); - beefy_merkle_tree::merkle_root::<::Hashing, _>( + binary_merkle_tree::merkle_root::<::Hashing, _>( col.into_iter().map(|pair| pair.encode()), ) .as_ref() diff --git a/test-utils/runtime/Cargo.toml b/test-utils/runtime/Cargo.toml index 2c943c47f..0852003a6 100644 --- a/test-utils/runtime/Cargo.toml +++ b/test-utils/runtime/Cargo.toml @@ -14,7 +14,7 @@ targets = ["x86_64-unknown-linux-gnu"] [dependencies] beefy-primitives = { version = "4.0.0-dev", default-features = false, path = "../../primitives/beefy", package = "sp-beefy" } -beefy-merkle-tree = { version = "4.0.0-dev", default-features = false, path = "../../frame/beefy-mmr/primitives" } +pallet-beefy-mmr = { version = "4.0.0-dev", default-features = false, path = "../../frame/beefy-mmr" } sp-application-crypto = { version = "7.0.0", default-features = false, path = "../../primitives/application-crypto" } sp-consensus-aura = { version = "0.10.0-dev", default-features = false, path = "../../primitives/consensus/aura" } sp-consensus-babe = { version = "0.10.0-dev", default-features = false, path = "../../primitives/consensus/babe" } @@ -67,7 +67,7 @@ default = [ ] std = [ "beefy-primitives/std", - "beefy-merkle-tree/std", + "pallet-beefy-mmr/std", "sp-application-crypto/std", "sp-consensus-aura/std", "sp-consensus-babe/std", diff --git a/test-utils/runtime/src/lib.rs b/test-utils/runtime/src/lib.rs index 8b64528e2..5fc7c5e85 100644 --- a/test-utils/runtime/src/lib.rs +++ b/test-utils/runtime/src/lib.rs @@ -978,7 +978,7 @@ cfg_if! { } } - impl beefy_merkle_tree::BeefyMmrApi for Runtime { + impl pallet_beefy_mmr::BeefyMmrApi for Runtime { fn authority_set_proof() -> beefy_primitives::mmr::BeefyAuthoritySet { Default::default() } diff --git a/frame/beefy-mmr/primitives/Cargo.toml b/utils/binary-merkle-tree/Cargo.toml similarity index 58% rename from frame/beefy-mmr/primitives/Cargo.toml rename to utils/binary-merkle-tree/Cargo.toml index c087bc2f3..a59d27fb0 100644 --- a/frame/beefy-mmr/primitives/Cargo.toml +++ b/utils/binary-merkle-tree/Cargo.toml @@ -1,5 +1,5 @@ [package] -name = "beefy-merkle-tree" +name = "binary-merkle-tree" version = "4.0.0-dev" authors = ["Parity Technologies "] edition = "2021" @@ -11,20 +11,18 @@ homepage = "https://substrate.io" [dependencies] array-bytes = { version = "4.1", optional = true } log = { version = "0.4", default-features = false, optional = true } - -beefy-primitives = { version = "4.0.0-dev", default-features = false, path = "../../../primitives/beefy", package = "sp-beefy" } -sp-api = { version = "4.0.0-dev", default-features = false, path = "../../../primitives/api" } -sp-runtime = { version = "7.0.0", default-features = false, path = "../../../primitives/runtime" } +hash-db = { version = "0.15.2", default-features = false } [dev-dependencies] array-bytes = "4.1" env_logger = "0.9" +sp-core = { version = "7.0.0", path = "../../primitives/core" } +sp-runtime = { version = "7.0.0", path = "../../primitives/runtime" } [features] debug = ["array-bytes", "log"] default = ["debug", "std"] std = [ - "beefy-primitives/std", - "sp-api/std", - "sp-runtime/std" + "log/std", + "hash-db/std" ] diff --git a/frame/beefy-mmr/primitives/src/lib.rs b/utils/binary-merkle-tree/src/lib.rs similarity index 93% rename from frame/beefy-mmr/primitives/src/lib.rs rename to utils/binary-merkle-tree/src/lib.rs index e6f8acefb..42f4e8a5b 100644 --- a/frame/beefy-mmr/primitives/src/lib.rs +++ b/utils/binary-merkle-tree/src/lib.rs @@ -31,40 +31,42 @@ //! efficient by removing the need to track which side each intermediate hash is concatenated on. //! //! If the number of leaves is not even, last leaf (hash of) is promoted to the upper layer. +#[cfg(not(feature = "std"))] +extern crate alloc; +#[cfg(not(feature = "std"))] +use alloc::vec; +#[cfg(not(feature = "std"))] +use alloc::vec::Vec; -pub use sp_runtime::traits::Keccak256; -use sp_runtime::{app_crypto::sp_core, sp_std, traits::Hash as HashT}; -use sp_std::{vec, vec::Vec}; - -use beefy_primitives::mmr::{BeefyAuthoritySet, BeefyNextAuthoritySet}; +use hash_db::Hasher; /// Construct a root hash of a Binary Merkle Tree created from given leaves. /// /// See crate-level docs for details about Merkle Tree construction. /// /// In case an empty list of leaves is passed the function returns a 0-filled hash. -pub fn merkle_root(leaves: I) -> H::Output +pub fn merkle_root(leaves: I) -> H::Out where - H: HashT, - H::Output: Default + AsRef<[u8]> + PartialOrd, + H: Hasher, + H::Out: Default + AsRef<[u8]> + PartialOrd, I: IntoIterator, I::Item: AsRef<[u8]>, { - let iter = leaves.into_iter().map(|l| ::hash(l.as_ref())); + let iter = leaves.into_iter().map(|l| ::hash(l.as_ref())); merkelize::(iter, &mut ()).into() } -fn merkelize(leaves: I, visitor: &mut V) -> H::Output +fn merkelize(leaves: I, visitor: &mut V) -> H::Out where - H: HashT, - H::Output: Default + AsRef<[u8]> + PartialOrd, - V: Visitor, - I: Iterator, + H: Hasher, + H::Out: Default + AsRef<[u8]> + PartialOrd, + V: Visitor, + I: Iterator, { let upper = Vec::with_capacity((leaves.size_hint().1.unwrap_or(0).saturating_add(1)) / 2); let mut next = match merkelize_row::(leaves, upper, visitor) { Ok(root) => return root, - Err(next) if next.is_empty() => return H::Output::default(), + Err(next) if next.is_empty() => return H::Out::default(), Err(next) => next, }; @@ -139,17 +141,17 @@ impl Visitor for () { /// # Panic /// /// The function will panic if given `leaf_index` is greater than the number of leaves. -pub fn merkle_proof(leaves: I, leaf_index: usize) -> MerkleProof +pub fn merkle_proof(leaves: I, leaf_index: usize) -> MerkleProof where - H: HashT, - H::Output: Default + Copy + AsRef<[u8]> + PartialOrd, + H: Hasher, + H::Out: Default + Copy + AsRef<[u8]> + PartialOrd, I: IntoIterator, I::IntoIter: ExactSizeIterator, T: AsRef<[u8]>, { let mut leaf = None; let iter = leaves.into_iter().enumerate().map(|(idx, l)| { - let hash = ::hash(l.as_ref()); + let hash = ::hash(l.as_ref()); if idx == leaf_index { leaf = Some(l); } @@ -234,28 +236,28 @@ impl<'a, H, T: AsRef<[u8]>> From<&'a T> for Leaf<'a, H> { /// /// The proof must not contain the root hash. pub fn verify_proof<'a, H, P, L>( - root: &'a H::Output, + root: &'a H::Out, proof: P, number_of_leaves: usize, leaf_index: usize, leaf: L, ) -> bool where - H: HashT, - H::Output: PartialEq + AsRef<[u8]> + PartialOrd, - P: IntoIterator, - L: Into>, + H: Hasher, + H::Out: PartialEq + AsRef<[u8]> + PartialOrd, + P: IntoIterator, + L: Into>, { if leaf_index >= number_of_leaves { return false } let leaf_hash = match leaf.into() { - Leaf::Value(content) => ::hash(content), + Leaf::Value(content) => ::hash(content), Leaf::Hash(hash) => hash, }; - let hash_len = ::LENGTH; + let hash_len = ::LENGTH; let mut combined = vec![0_u8; hash_len * 2]; let computed = proof.into_iter().fold(leaf_hash, |a, b| { if a < b { @@ -265,7 +267,7 @@ where combined[..hash_len].copy_from_slice(&b.as_ref()); combined[hash_len..].copy_from_slice(&a.as_ref()); } - let hash = ::hash(&combined); + let hash = ::hash(&combined); #[cfg(feature = "debug")] log::debug!( "[verify_proof]: (a, b) {:?}, {:?} => {:?} ({:?}) hash", @@ -287,20 +289,20 @@ where /// empty iterator) an `Err` with the inner nodes of upper layer is returned. fn merkelize_row( mut iter: I, - mut next: Vec, + mut next: Vec, visitor: &mut V, -) -> Result> +) -> Result> where - H: HashT, - H::Output: AsRef<[u8]> + PartialOrd, - V: Visitor, - I: Iterator, + H: Hasher, + H::Out: AsRef<[u8]> + PartialOrd, + V: Visitor, + I: Iterator, { #[cfg(feature = "debug")] log::debug!("[merkelize_row]"); next.clear(); - let hash_len = ::LENGTH; + let hash_len = ::LENGTH; let mut index = 0; let mut combined = vec![0_u8; hash_len * 2]; loop { @@ -326,7 +328,7 @@ where combined[hash_len..].copy_from_slice(a.as_ref()); } - next.push(::hash(&combined)); + next.push(::hash(&combined)); }, // Odd number of items. Promote the item to the upper layer. (Some(a), None) if !next.is_empty() => { @@ -347,24 +349,11 @@ where } } -sp_api::decl_runtime_apis! { - /// API useful for BEEFY light clients. - pub trait BeefyMmrApi - where - BeefyAuthoritySet: sp_api::Decode, - { - /// Return the currently active BEEFY authority set proof. - fn authority_set_proof() -> BeefyAuthoritySet; - - /// Return the next/queued BEEFY authority set proof. - fn next_authority_set_proof() -> BeefyNextAuthoritySet; - } -} - #[cfg(test)] mod tests { use super::*; - use crate::sp_core::H256; + use sp_core::H256; + use sp_runtime::traits::Keccak256; #[test] fn should_generate_empty_root() { From a69a08e424fca846ded0ce51ed4659342907cc1f Mon Sep 17 00:00:00 2001 From: Alexandru Vasile <60601340+lexnv@users.noreply.github.com> Date: Thu, 2 Feb 2023 19:14:40 +0200 Subject: [PATCH 080/558] Metadata V15: Derive `TypeInfo` for describing runtime types (#13272) * scale_info: Derive TypeInfo for types present in runtime API Signed-off-by: Alexandru Vasile * cargo: Update Cargo.lock Signed-off-by: Alexandru Vasile --------- Signed-off-by: Alexandru Vasile --- Cargo.lock | 3 +++ client/consensus/babe/Cargo.toml | 1 + client/consensus/babe/src/lib.rs | 2 +- frame/benchmarking/src/utils.rs | 13 +++++++------ frame/contracts/primitives/Cargo.toml | 2 ++ frame/contracts/primitives/src/lib.rs | 17 +++++++++-------- frame/support/src/traits/storage.rs | 4 +++- frame/support/src/traits/try_runtime.rs | 4 ++-- frame/transaction-payment/src/types.rs | 8 +++++--- primitives/consensus/babe/src/lib.rs | 6 +++--- primitives/core/src/lib.rs | 2 +- primitives/finality-grandpa/src/lib.rs | 2 +- primitives/inherents/Cargo.toml | 2 ++ primitives/inherents/src/lib.rs | 4 ++-- primitives/merkle-mountain-range/src/lib.rs | 4 ++-- primitives/runtime/src/generic/block.rs | 2 +- primitives/runtime/src/transaction_validity.rs | 4 ++-- test-utils/runtime/src/lib.rs | 2 +- 18 files changed, 48 insertions(+), 34 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index a0e9e0e63..dda127fdd 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -5615,6 +5615,7 @@ version = "7.0.0" dependencies = [ "bitflags", "parity-scale-codec", + "scale-info", "sp-runtime", "sp-std", "sp-weights", @@ -8151,6 +8152,7 @@ dependencies = [ "sc-network", "sc-network-test", "sc-telemetry", + "scale-info", "schnorrkel", "sp-api", "sp-application-crypto", @@ -9959,6 +9961,7 @@ dependencies = [ "futures", "impl-trait-for-tuples", "parity-scale-codec", + "scale-info", "sp-core", "sp-runtime", "sp-std", diff --git a/client/consensus/babe/Cargo.toml b/client/consensus/babe/Cargo.toml index f0ff0080f..8088bef9d 100644 --- a/client/consensus/babe/Cargo.toml +++ b/client/consensus/babe/Cargo.toml @@ -15,6 +15,7 @@ targets = ["x86_64-unknown-linux-gnu"] [dependencies] async-trait = "0.1.57" +scale-info = { version = "2.1.1", features = ["derive"] } codec = { package = "parity-scale-codec", version = "3.2.2", features = ["derive"] } futures = "0.3.21" log = "0.4.17" diff --git a/client/consensus/babe/src/lib.rs b/client/consensus/babe/src/lib.rs index 0eac4aca0..99b29986e 100644 --- a/client/consensus/babe/src/lib.rs +++ b/client/consensus/babe/src/lib.rs @@ -152,7 +152,7 @@ mod tests; const LOG_TARGET: &str = "babe"; /// BABE epoch information -#[derive(Decode, Encode, PartialEq, Eq, Clone, Debug)] +#[derive(Decode, Encode, PartialEq, Eq, Clone, Debug, scale_info::TypeInfo)] pub struct Epoch { /// The epoch index. pub epoch_index: u64, diff --git a/frame/benchmarking/src/utils.rs b/frame/benchmarking/src/utils.rs index 6ad9ac7bb..74e44fbf9 100644 --- a/frame/benchmarking/src/utils.rs +++ b/frame/benchmarking/src/utils.rs @@ -22,6 +22,7 @@ use frame_support::{ pallet_prelude::*, traits::StorageInfo, }; +use scale_info::TypeInfo; #[cfg(feature = "std")] use serde::{Deserialize, Serialize}; use sp_io::hashing::blake2_256; @@ -31,7 +32,7 @@ use sp_storage::TrackedStorageKey; /// An alphabet of possible parameters to use for benchmarking. #[cfg_attr(feature = "std", derive(Serialize, Deserialize))] -#[derive(Encode, Decode, Clone, Copy, PartialEq, Debug)] +#[derive(Encode, Decode, Clone, Copy, PartialEq, Debug, TypeInfo)] #[allow(missing_docs)] #[allow(non_camel_case_types)] pub enum BenchmarkParameter { @@ -72,7 +73,7 @@ impl std::fmt::Display for BenchmarkParameter { /// The results of a single of benchmark. #[cfg_attr(feature = "std", derive(Serialize, Deserialize))] -#[derive(Encode, Decode, Clone, PartialEq, Debug)] +#[derive(Encode, Decode, Clone, PartialEq, Debug, TypeInfo)] pub struct BenchmarkBatch { /// The pallet containing this benchmark. #[cfg_attr(feature = "std", serde(with = "serde_as_str"))] @@ -111,7 +112,7 @@ pub struct BenchmarkBatchSplitResults { /// Contains duration of the function call in nanoseconds along with the benchmark parameters /// used for that benchmark result. #[cfg_attr(feature = "std", derive(Serialize, Deserialize))] -#[derive(Encode, Decode, Default, Clone, PartialEq, Debug)] +#[derive(Encode, Decode, Default, Clone, PartialEq, Debug, TypeInfo)] pub struct BenchmarkResult { pub components: Vec<(BenchmarkParameter, u32)>, pub extrinsic_time: u128, @@ -199,7 +200,7 @@ impl From for BenchmarkError { } /// Configuration used to setup and run runtime benchmarks. -#[derive(Encode, Decode, Default, Clone, PartialEq, Debug)] +#[derive(Encode, Decode, Default, Clone, PartialEq, Debug, TypeInfo)] pub struct BenchmarkConfig { /// The encoded name of the pallet to benchmark. pub pallet: Vec, @@ -216,14 +217,14 @@ pub struct BenchmarkConfig { /// A list of benchmarks available for a particular pallet and instance. /// /// All `Vec` must be valid utf8 strings. -#[derive(Encode, Decode, Default, Clone, PartialEq, Debug)] +#[derive(Encode, Decode, Default, Clone, PartialEq, Debug, TypeInfo)] pub struct BenchmarkList { pub pallet: Vec, pub instance: Vec, pub benchmarks: Vec, } -#[derive(Encode, Decode, Default, Clone, PartialEq, Debug)] +#[derive(Encode, Decode, Default, Clone, PartialEq, Debug, TypeInfo)] pub struct BenchmarkMetadata { pub name: Vec, pub components: Vec<(BenchmarkParameter, u32, u32)>, diff --git a/frame/contracts/primitives/Cargo.toml b/frame/contracts/primitives/Cargo.toml index aafcd912a..2460edc5a 100644 --- a/frame/contracts/primitives/Cargo.toml +++ b/frame/contracts/primitives/Cargo.toml @@ -14,6 +14,7 @@ targets = ["x86_64-unknown-linux-gnu"] [dependencies] bitflags = "1.0" +scale-info = { version = "2.1.1", default-features = false, features = ["derive"] } codec = { package = "parity-scale-codec", version = "3.2.2", default-features = false, features = ["derive"] } # Substrate Dependencies (This crate should not rely on frame) @@ -27,4 +28,5 @@ std = [ "codec/std", "sp-runtime/std", "sp-std/std", + "scale-info/std", ] diff --git a/frame/contracts/primitives/src/lib.rs b/frame/contracts/primitives/src/lib.rs index 4faea9eb3..1811ae09c 100644 --- a/frame/contracts/primitives/src/lib.rs +++ b/frame/contracts/primitives/src/lib.rs @@ -21,6 +21,7 @@ use bitflags::bitflags; use codec::{Decode, Encode}; +use scale_info::TypeInfo; use sp_runtime::{ traits::{Saturating, Zero}, DispatchError, RuntimeDebug, @@ -31,7 +32,7 @@ use sp_weights::Weight; /// Result type of a `bare_call` or `bare_instantiate` call. /// /// It contains the execution result together with some auxiliary information. -#[derive(Eq, PartialEq, Encode, Decode, RuntimeDebug)] +#[derive(Eq, PartialEq, Encode, Decode, RuntimeDebug, TypeInfo)] pub struct ContractResult { /// How much weight was consumed during execution. pub gas_consumed: Weight, @@ -86,7 +87,7 @@ pub type CodeUploadResult = pub type GetStorageResult = Result>, ContractAccessError>; /// The possible errors that can happen querying the storage of a contract. -#[derive(Eq, PartialEq, Encode, Decode, RuntimeDebug)] +#[derive(Eq, PartialEq, Encode, Decode, RuntimeDebug, TypeInfo)] pub enum ContractAccessError { /// The given address doesn't point to a contract. DoesntExist, @@ -96,7 +97,7 @@ pub enum ContractAccessError { bitflags! { /// Flags used by a contract to customize exit behaviour. - #[derive(Encode, Decode)] + #[derive(Encode, Decode, TypeInfo)] pub struct ReturnFlags: u32 { /// If this bit is set all changes made by the contract execution are rolled back. const REVERT = 0x0000_0001; @@ -104,7 +105,7 @@ bitflags! { } /// Output of a contract call or instantiation which ran to completion. -#[derive(PartialEq, Eq, Encode, Decode, RuntimeDebug)] +#[derive(PartialEq, Eq, Encode, Decode, RuntimeDebug, TypeInfo)] pub struct ExecReturnValue { /// Flags passed along by `seal_return`. Empty when `seal_return` was never called. pub flags: ReturnFlags, @@ -120,7 +121,7 @@ impl ExecReturnValue { } /// The result of a successful contract instantiation. -#[derive(PartialEq, Eq, Encode, Decode, RuntimeDebug)] +#[derive(PartialEq, Eq, Encode, Decode, RuntimeDebug, TypeInfo)] pub struct InstantiateReturnValue { /// The output of the called constructor. pub result: ExecReturnValue, @@ -129,7 +130,7 @@ pub struct InstantiateReturnValue { } /// The result of succesfully uploading a contract. -#[derive(PartialEq, Eq, Encode, Decode, RuntimeDebug)] +#[derive(PartialEq, Eq, Encode, Decode, RuntimeDebug, TypeInfo)] pub struct CodeUploadReturnValue { /// The key under which the new code is stored. pub code_hash: CodeHash, @@ -138,7 +139,7 @@ pub struct CodeUploadReturnValue { } /// Reference to an existing code hash or a new wasm module. -#[derive(Eq, PartialEq, Encode, Decode, RuntimeDebug)] +#[derive(Eq, PartialEq, Encode, Decode, RuntimeDebug, TypeInfo)] pub enum Code { /// A wasm module as raw bytes. Upload(Vec), @@ -153,7 +154,7 @@ impl>, Hash> From for Code { } /// The amount of balance that was either charged or refunded in order to pay for storage. -#[derive(Eq, PartialEq, Ord, PartialOrd, Encode, Decode, RuntimeDebug, Clone)] +#[derive(Eq, PartialEq, Ord, PartialOrd, Encode, Decode, RuntimeDebug, Clone, TypeInfo)] pub enum StorageDeposit { /// The transaction reduced storage consumption. /// diff --git a/frame/support/src/traits/storage.rs b/frame/support/src/traits/storage.rs index 24653d189..8285ab988 100644 --- a/frame/support/src/traits/storage.rs +++ b/frame/support/src/traits/storage.rs @@ -53,7 +53,9 @@ pub trait StorageInstance { } /// Metadata about storage from the runtime. -#[derive(codec::Encode, codec::Decode, crate::RuntimeDebug, Eq, PartialEq, Clone)] +#[derive( + codec::Encode, codec::Decode, crate::RuntimeDebug, Eq, PartialEq, Clone, scale_info::TypeInfo, +)] pub struct StorageInfo { /// Encoded string of pallet name. pub pallet_name: Vec, diff --git a/frame/support/src/traits/try_runtime.rs b/frame/support/src/traits/try_runtime.rs index 273af09a1..cb4902201 100644 --- a/frame/support/src/traits/try_runtime.rs +++ b/frame/support/src/traits/try_runtime.rs @@ -22,7 +22,7 @@ use sp_arithmetic::traits::AtLeast32BitUnsigned; use sp_std::prelude::*; /// Which state tests to execute. -#[derive(codec::Encode, codec::Decode, Clone)] +#[derive(codec::Encode, codec::Decode, Clone, scale_info::TypeInfo)] pub enum Select { /// None of them. None, @@ -82,7 +82,7 @@ impl sp_std::str::FromStr for Select { } /// Select which checks should be run when trying a runtime upgrade upgrade. -#[derive(codec::Encode, codec::Decode, Clone, Debug, Copy)] +#[derive(codec::Encode, codec::Decode, Clone, Debug, Copy, scale_info::TypeInfo)] pub enum UpgradeCheckSelect { /// Run no checks. None, diff --git a/frame/transaction-payment/src/types.rs b/frame/transaction-payment/src/types.rs index d1a480b64..d9677861f 100644 --- a/frame/transaction-payment/src/types.rs +++ b/frame/transaction-payment/src/types.rs @@ -21,13 +21,15 @@ use codec::{Decode, Encode}; #[cfg(feature = "std")] use serde::{Deserialize, Serialize}; +use scale_info::TypeInfo; + use sp_runtime::traits::{AtLeast32BitUnsigned, Zero}; use sp_std::prelude::*; use frame_support::dispatch::DispatchClass; /// The base fee and adjusted weight and length fees constitute the _inclusion fee_. -#[derive(Encode, Decode, Clone, Eq, PartialEq)] +#[derive(Encode, Decode, Clone, Eq, PartialEq, TypeInfo)] #[cfg_attr(feature = "std", derive(Debug, Serialize, Deserialize))] #[cfg_attr(feature = "std", serde(rename_all = "camelCase"))] pub struct InclusionFee { @@ -63,7 +65,7 @@ impl InclusionFee { /// - (Optional) `inclusion_fee`: Only the `Pays::Yes` transaction can have the inclusion fee. /// - `tip`: If included in the transaction, the tip will be added on top. Only signed /// transactions can have a tip. -#[derive(Encode, Decode, Clone, Eq, PartialEq)] +#[derive(Encode, Decode, Clone, Eq, PartialEq, TypeInfo)] #[cfg_attr(feature = "std", derive(Debug, Serialize, Deserialize))] #[cfg_attr(feature = "std", serde(rename_all = "camelCase"))] pub struct FeeDetails { @@ -91,7 +93,7 @@ impl FeeDetails { /// Information related to a dispatchable's class, weight, and fee that can be queried from the /// runtime. -#[derive(Eq, PartialEq, Encode, Decode, Default)] +#[derive(Eq, PartialEq, Encode, Decode, Default, TypeInfo)] #[cfg_attr(feature = "std", derive(Debug, Serialize, Deserialize))] #[cfg_attr(feature = "std", serde(rename_all = "camelCase"))] #[cfg_attr( diff --git a/primitives/consensus/babe/src/lib.rs b/primitives/consensus/babe/src/lib.rs index cb44afcb8..10d027bd9 100644 --- a/primitives/consensus/babe/src/lib.rs +++ b/primitives/consensus/babe/src/lib.rs @@ -184,7 +184,7 @@ impl From for BabeConfiguration { } /// Configuration data used by the BABE consensus engine. -#[derive(Clone, PartialEq, Eq, Encode, Decode, RuntimeDebug)] +#[derive(Clone, PartialEq, Eq, Encode, Decode, RuntimeDebug, TypeInfo)] pub struct BabeConfiguration { /// The slot duration in milliseconds for BABE. Currently, only /// the value provided by this type at genesis will be used. @@ -327,7 +327,7 @@ where /// the runtime API boundary this type is unknown and as such we keep this /// opaque representation, implementors of the runtime API will have to make /// sure that all usages of `OpaqueKeyOwnershipProof` refer to the same type. -#[derive(Decode, Encode, PartialEq)] +#[derive(Decode, Encode, PartialEq, TypeInfo)] pub struct OpaqueKeyOwnershipProof(Vec); impl OpaqueKeyOwnershipProof { /// Create a new `OpaqueKeyOwnershipProof` using the given encoded @@ -344,7 +344,7 @@ impl OpaqueKeyOwnershipProof { } /// BABE epoch information -#[derive(Decode, Encode, PartialEq, Eq, Clone, Debug)] +#[derive(Decode, Encode, PartialEq, Eq, Clone, Debug, TypeInfo)] pub struct Epoch { /// The epoch index. pub epoch_index: u64, diff --git a/primitives/core/src/lib.rs b/primitives/core/src/lib.rs index 30d0cc199..80e14b84d 100644 --- a/primitives/core/src/lib.rs +++ b/primitives/core/src/lib.rs @@ -165,7 +165,7 @@ impl sp_std::str::FromStr for Bytes { } /// Stores the encoded `RuntimeMetadata` for the native side as opaque type. -#[derive(Encode, Decode, PartialEq)] +#[derive(Encode, Decode, PartialEq, TypeInfo)] pub struct OpaqueMetadata(Vec); impl OpaqueMetadata { diff --git a/primitives/finality-grandpa/src/lib.rs b/primitives/finality-grandpa/src/lib.rs index 7b3b55f49..f5307ab06 100644 --- a/primitives/finality-grandpa/src/lib.rs +++ b/primitives/finality-grandpa/src/lib.rs @@ -529,7 +529,7 @@ impl<'a> Decode for VersionedAuthorityList<'a> { /// the runtime API boundary this type is unknown and as such we keep this /// opaque representation, implementors of the runtime API will have to make /// sure that all usages of `OpaqueKeyOwnershipProof` refer to the same type. -#[derive(Decode, Encode, PartialEq)] +#[derive(Decode, Encode, PartialEq, TypeInfo)] pub struct OpaqueKeyOwnershipProof(Vec); impl OpaqueKeyOwnershipProof { diff --git a/primitives/inherents/Cargo.toml b/primitives/inherents/Cargo.toml index c901a6e68..fd073db69 100644 --- a/primitives/inherents/Cargo.toml +++ b/primitives/inherents/Cargo.toml @@ -16,6 +16,7 @@ targets = ["x86_64-unknown-linux-gnu"] [dependencies] async-trait = { version = "0.1.57", optional = true } codec = { package = "parity-scale-codec", version = "3.2.2", default-features = false, features = ["derive"] } +scale-info = { version = "2.1.1", default-features = false, features = ["derive"] } impl-trait-for-tuples = "0.2.2" thiserror = { version = "1.0.30", optional = true } sp-core = { version = "7.0.0", default-features = false, path = "../core" } @@ -30,6 +31,7 @@ default = [ "std" ] std = [ "async-trait", "codec/std", + "scale-info/std", "sp-core/std", "sp-runtime/std", "sp-std/std", diff --git a/primitives/inherents/src/lib.rs b/primitives/inherents/src/lib.rs index 59db5ef3e..f05c9a9ad 100644 --- a/primitives/inherents/src/lib.rs +++ b/primitives/inherents/src/lib.rs @@ -204,7 +204,7 @@ pub enum Error { pub type InherentIdentifier = [u8; 8]; /// Inherent data to include in a block. -#[derive(Clone, Default, Encode, Decode)] +#[derive(Clone, Default, Encode, Decode, scale_info::TypeInfo)] pub struct InherentData { /// All inherent data encoded with parity-scale-codec and an identifier. data: BTreeMap>, @@ -276,7 +276,7 @@ impl InherentData { /// /// When a fatal error occurs, all other errors are removed and the implementation needs to /// abort checking inherents. -#[derive(Encode, Decode, Clone)] +#[derive(Encode, Decode, Clone, scale_info::TypeInfo)] pub struct CheckInherentsResult { /// Did the check succeed? okay: bool, diff --git a/primitives/merkle-mountain-range/src/lib.rs b/primitives/merkle-mountain-range/src/lib.rs index 606906185..df0a7f75a 100644 --- a/primitives/merkle-mountain-range/src/lib.rs +++ b/primitives/merkle-mountain-range/src/lib.rs @@ -142,7 +142,7 @@ impl FullLeaf for OpaqueLeaf { /// /// It is different from [`OpaqueLeaf`], because it does implement `Codec` /// and the encoding has to match raw `Vec` encoding. -#[derive(codec::Encode, codec::Decode, RuntimeDebug, PartialEq, Eq)] +#[derive(codec::Encode, codec::Decode, RuntimeDebug, PartialEq, Eq, TypeInfo)] pub struct EncodableOpaqueLeaf(pub Vec); impl EncodableOpaqueLeaf { @@ -361,7 +361,7 @@ pub struct Proof { /// Merkle Mountain Range operation error. #[cfg_attr(feature = "std", derive(thiserror::Error))] -#[derive(RuntimeDebug, codec::Encode, codec::Decode, PartialEq, Eq)] +#[derive(RuntimeDebug, codec::Encode, codec::Decode, PartialEq, Eq, TypeInfo)] pub enum Error { /// Error during translation of a block number into a leaf index. #[cfg_attr(feature = "std", error("Error performing numeric op"))] diff --git a/primitives/runtime/src/generic/block.rs b/primitives/runtime/src/generic/block.rs index 3b0163363..b03fab290 100644 --- a/primitives/runtime/src/generic/block.rs +++ b/primitives/runtime/src/generic/block.rs @@ -78,7 +78,7 @@ impl fmt::Display for BlockId { } /// Abstraction over a substrate block. -#[derive(PartialEq, Eq, Clone, Encode, Decode, RuntimeDebug)] +#[derive(PartialEq, Eq, Clone, Encode, Decode, RuntimeDebug, scale_info::TypeInfo)] #[cfg_attr(feature = "std", derive(Serialize, Deserialize))] #[cfg_attr(feature = "std", serde(rename_all = "camelCase"))] #[cfg_attr(feature = "std", serde(deny_unknown_fields))] diff --git a/primitives/runtime/src/transaction_validity.rs b/primitives/runtime/src/transaction_validity.rs index 3b89644c2..f47459f3e 100644 --- a/primitives/runtime/src/transaction_validity.rs +++ b/primitives/runtime/src/transaction_validity.rs @@ -226,7 +226,7 @@ impl From for TransactionValidity { /// Depending on the source we might apply different validation schemes. /// For instance we can disallow specific kinds of transactions if they were not produced /// by our local node (for instance off-chain workers). -#[derive(Copy, Clone, PartialEq, Eq, Encode, Decode, RuntimeDebug)] +#[derive(Copy, Clone, PartialEq, Eq, Encode, Decode, RuntimeDebug, TypeInfo)] pub enum TransactionSource { /// Transaction is already included in block. /// @@ -251,7 +251,7 @@ pub enum TransactionSource { } /// Information concerning a valid transaction. -#[derive(Clone, PartialEq, Eq, Encode, Decode, RuntimeDebug)] +#[derive(Clone, PartialEq, Eq, Encode, Decode, RuntimeDebug, TypeInfo)] pub struct ValidTransaction { /// Priority of the transaction. /// diff --git a/test-utils/runtime/src/lib.rs b/test-utils/runtime/src/lib.rs index 5fc7c5e85..1c2d6ec8a 100644 --- a/test-utils/runtime/src/lib.rs +++ b/test-utils/runtime/src/lib.rs @@ -297,7 +297,7 @@ pub fn run_tests(mut input: &[u8]) -> Vec { } /// A type that can not be decoded. -#[derive(PartialEq)] +#[derive(PartialEq, TypeInfo)] pub struct DecodeFails { _phantom: PhantomData, } From 9c00c9073829b93113f3cbd4183d940a6164f321 Mon Sep 17 00:00:00 2001 From: Oliver Tale-Yazdi Date: Thu, 2 Feb 2023 18:47:50 +0100 Subject: [PATCH 081/558] benchmarks: EnsureRankedMember must add ranked members (#13297) Signed-off-by: Oliver Tale-Yazdi --- frame/ranked-collective/src/lib.rs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/frame/ranked-collective/src/lib.rs b/frame/ranked-collective/src/lib.rs index 84be1243c..a65d184d6 100644 --- a/frame/ranked-collective/src/lib.rs +++ b/frame/ranked-collective/src/lib.rs @@ -256,8 +256,7 @@ impl, I: 'static, const MIN_RANK: u16> EnsureOrigin Result { - let who = IndexToId::::get(MIN_RANK, 0).ok_or(())?; - Ok(frame_system::RawOrigin::Signed(who).into()) + EnsureRankedMember::::try_successful_origin() } } @@ -279,8 +278,7 @@ impl, I: 'static, const MIN_RANK: u16> EnsureOrigin Result { - let who = IndexToId::::get(MIN_RANK, 0).ok_or(())?; - Ok(frame_system::RawOrigin::Signed(who).into()) + EnsureRankedMember::::try_successful_origin() } } @@ -302,7 +300,9 @@ impl, I: 'static, const MIN_RANK: u16> EnsureOrigin Result { - let who = IndexToId::::get(MIN_RANK, 0).ok_or(())?; + let who = frame_benchmarking::account::("successful_origin", 0, 0); + crate::Pallet::::do_add_member_to_rank(who.clone(), MIN_RANK) + .expect("Could not add members for benchmarks"); Ok(frame_system::RawOrigin::Signed(who).into()) } } From 83de5099a3cae683ccbaf2ab7a4d688e2d602f57 Mon Sep 17 00:00:00 2001 From: Marcin S Date: Thu, 2 Feb 2023 19:41:40 +0100 Subject: [PATCH 082/558] Minor: Update output validity tests (#13190) * Minor: Update output validity tests Quick follow-up to https://github.com/paritytech/substrate/pull/13183. Mainly, I wanted to double check that the `test_return_max_memory_offset` test doesn't pass just because the output length is 0. I also: - Organized these tests into a module. - Added a comment explaining why we don't use the `wasm_export_functions` macro. * Update test based on review comment --- client/executor/runtime-test/src/lib.rs | 67 ++++++++++++-------- client/executor/src/integration_tests/mod.rs | 2 +- 2 files changed, 42 insertions(+), 27 deletions(-) diff --git a/client/executor/runtime-test/src/lib.rs b/client/executor/runtime-test/src/lib.rs index 0aa30e4bc..23584006a 100644 --- a/client/executor/runtime-test/src/lib.rs +++ b/client/executor/runtime-test/src/lib.rs @@ -29,8 +29,6 @@ use sp_runtime::{ print, traits::{BlakeTwo256, Hash}, }; -#[cfg(not(feature = "std"))] -use sp_runtime_interface::pack_ptr_and_len; extern "C" { #[allow(dead_code)] @@ -344,31 +342,48 @@ sp_core::wasm_export_functions! { } } -// Returns a huge len. It should result in an error, and not an allocation. -#[no_mangle] -#[cfg(not(feature = "std"))] -pub extern "C" fn test_return_huge_len(_params: *const u8, _len: usize) -> u64 { - pack_ptr_and_len(0, u32::MAX) -} +// Tests that check output validity. We explicitly return the ptr and len, so we avoid using the +// `wasm_export_functions` macro. +mod output_validity { + #[cfg(not(feature = "std"))] + use super::WASM_PAGE_SIZE; -// Returns an offset right at the edge of the wasm memory boundary. With length 0, it should -// succeed. -#[no_mangle] -#[cfg(not(feature = "std"))] -pub extern "C" fn test_return_max_memory_offset(_params: *const u8, _len: usize) -> u64 { - pack_ptr_and_len((core::arch::wasm32::memory_size(0) * WASM_PAGE_SIZE) as u32, 0) -} + #[cfg(not(feature = "std"))] + use sp_runtime_interface::pack_ptr_and_len; -// Returns an offset right at the edge of the wasm memory boundary. With length 1, it should fail. -#[no_mangle] -#[cfg(not(feature = "std"))] -pub extern "C" fn test_return_max_memory_offset_plus_one(_params: *const u8, _len: usize) -> u64 { - pack_ptr_and_len((core::arch::wasm32::memory_size(0) * WASM_PAGE_SIZE) as u32, 1) -} + // Returns a huge len. It should result in an error, and not an allocation. + #[no_mangle] + #[cfg(not(feature = "std"))] + pub extern "C" fn test_return_huge_len(_params: *const u8, _len: usize) -> u64 { + pack_ptr_and_len(0, u32::MAX) + } -// Returns an output that overflows the u32 range. It should result in an error. -#[no_mangle] -#[cfg(not(feature = "std"))] -pub extern "C" fn test_return_overflow(_params: *const u8, _len: usize) -> u64 { - pack_ptr_and_len(u32::MAX, 1) + // Returns an offset right before the edge of the wasm memory boundary. It should succeed. + #[no_mangle] + #[cfg(not(feature = "std"))] + pub extern "C" fn test_return_max_memory_offset(_params: *const u8, _len: usize) -> u64 { + let output_ptr = (core::arch::wasm32::memory_size(0) * WASM_PAGE_SIZE) as u32 - 1; + let ptr = output_ptr as *mut u8; + unsafe { + ptr.write(u8::MAX); + } + pack_ptr_and_len(output_ptr, 1) + } + + // Returns an offset right after the edge of the wasm memory boundary. It should fail. + #[no_mangle] + #[cfg(not(feature = "std"))] + pub extern "C" fn test_return_max_memory_offset_plus_one( + _params: *const u8, + _len: usize, + ) -> u64 { + pack_ptr_and_len((core::arch::wasm32::memory_size(0) * WASM_PAGE_SIZE) as u32, 1) + } + + // Returns an output that overflows the u32 range. It should result in an error. + #[no_mangle] + #[cfg(not(feature = "std"))] + pub extern "C" fn test_return_overflow(_params: *const u8, _len: usize) -> u64 { + pack_ptr_and_len(u32::MAX, 1) + } } diff --git a/client/executor/src/integration_tests/mod.rs b/client/executor/src/integration_tests/mod.rs index 22e651abe..bb2930803 100644 --- a/client/executor/src/integration_tests/mod.rs +++ b/client/executor/src/integration_tests/mod.rs @@ -811,7 +811,7 @@ fn return_max_memory_offset(wasm_method: WasmExecutionMethod) { assert_eq!( call_in_wasm("test_return_max_memory_offset", &[], wasm_method, &mut ext).unwrap(), - ().encode() + (u8::MAX).encode() ); } From 1d67b0f0a9d21c53ec9515d810fb4d298bffd1f7 Mon Sep 17 00:00:00 2001 From: Adrian Catangiu Date: Fri, 3 Feb 2023 12:16:16 +0200 Subject: [PATCH 083/558] BEEFY: define on-chain beefy-genesis and use it to coordinate voter initialization (#13215) * beefy: add support to configure BEEFY genesis * client/beefy: more flexible test runtime api * client/beefy: add tests for custom BEEFY genesis * client/beefy: ignore old state that didn't account for pallet genesis * client/beefy: fix clippy * frame/beefy: default BEEFY-genesis is block One::one() * frame/beefy: add extra doc comments --------- Co-authored-by: parity-processbot <> --- client/beefy/src/aux_schema.rs | 5 +- client/beefy/src/lib.rs | 34 ++-- client/beefy/src/tests.rs | 273 +++++++++++++++++++++------------ client/beefy/src/worker.rs | 6 +- frame/beefy/src/lib.rs | 21 ++- primitives/beefy/src/lib.rs | 5 +- test-utils/runtime/src/lib.rs | 4 + 7 files changed, 234 insertions(+), 114 deletions(-) diff --git a/client/beefy/src/aux_schema.rs b/client/beefy/src/aux_schema.rs index fafa9948c..217ea4769 100644 --- a/client/beefy/src/aux_schema.rs +++ b/client/beefy/src/aux_schema.rs @@ -28,7 +28,7 @@ use sp_runtime::traits::Block as BlockT; const VERSION_KEY: &[u8] = b"beefy_auxschema_version"; const WORKER_STATE: &[u8] = b"beefy_voter_state"; -const CURRENT_VERSION: u32 = 1; +const CURRENT_VERSION: u32 = 2; pub(crate) fn write_current_version(backend: &B) -> ClientResult<()> { info!(target: LOG_TARGET, "🥩 write aux schema version {:?}", CURRENT_VERSION); @@ -63,7 +63,8 @@ where match version { None => (), - Some(1) => return load_decode::<_, PersistedState>(backend, WORKER_STATE), + Some(1) => (), // version 1 is totally obsolete and should be simply ignored + Some(2) => return load_decode::<_, PersistedState>(backend, WORKER_STATE), other => return Err(ClientError::Backend(format!("Unsupported BEEFY DB version: {:?}", other))), } diff --git a/client/beefy/src/lib.rs b/client/beefy/src/lib.rs index 185f3b1ad..d7de7295a 100644 --- a/client/beefy/src/lib.rs +++ b/client/beefy/src/lib.rs @@ -53,7 +53,7 @@ use sp_keystore::SyncCryptoStorePtr; use sp_mmr_primitives::MmrApi; use sp_runtime::{ generic::BlockId, - traits::{Block, One, Zero}, + traits::{Block, Zero}, }; use std::{collections::VecDeque, marker::PhantomData, sync::Arc}; @@ -346,6 +346,12 @@ where R: ProvideRuntimeApi, R::Api: BeefyApi, { + let beefy_genesis = runtime + .runtime_api() + .beefy_genesis(&BlockId::hash(best_grandpa.hash())) + .ok() + .flatten() + .ok_or_else(|| ClientError::Backend("BEEFY pallet expected to be active.".into()))?; // Walk back the imported blocks and initialize voter either, at the last block with // a BEEFY justification, or at pallet genesis block; voter will resume from there. let blockchain = backend.blockchain(); @@ -378,20 +384,19 @@ where break state } - if *header.number() == One::one() { - // We've reached chain genesis, initialize voter here. - let genesis_num = *header.number(); + if *header.number() == beefy_genesis { + // We've reached BEEFY genesis, initialize voter here. let genesis_set = expect_validator_set(runtime, BlockId::hash(header.hash())) .and_then(genesis_set_sanity_check)?; info!( target: LOG_TARGET, "🥩 Loading BEEFY voter state from genesis on what appears to be first startup. \ Starting voting rounds at block {:?}, genesis validator set {:?}.", - genesis_num, + beefy_genesis, genesis_set, ); - sessions.push_front(Rounds::new(genesis_num, genesis_set)); + sessions.push_front(Rounds::new(beefy_genesis, genesis_set)); break PersistedState::checked_new(best_grandpa, Zero::zero(), sessions, min_block_delta) .ok_or_else(|| ClientError::Backend("Invalid BEEFY chain".into()))? } @@ -448,13 +453,16 @@ where None => break }; let at = BlockId::hash(notif.header.hash()); - if let Some(active) = runtime.runtime_api().validator_set(&at).ok().flatten() { - // Beefy pallet available, return best grandpa at the time. - info!( - target: LOG_TARGET, "🥩 BEEFY pallet available: block {:?} validator set {:?}", - notif.header.number(), active - ); - return Ok(notif.header) + if let Some(start) = runtime.runtime_api().beefy_genesis(&at).ok().flatten() { + if *notif.header.number() >= start { + // Beefy pallet available, return header for best grandpa at the time. + info!( + target: LOG_TARGET, + "🥩 BEEFY pallet available: block {:?} beefy genesis {:?}", + notif.header.number(), start + ); + return Ok(notif.header) + } } }, _ = gossip_engine => { diff --git a/client/beefy/src/tests.rs b/client/beefy/src/tests.rs index 2642035ba..008137d9a 100644 --- a/client/beefy/src/tests.rs +++ b/client/beefy/src/tests.rs @@ -77,8 +77,8 @@ const BAD_MMR_ROOT: MmrRootHash = MmrRootHash::repeat_byte(0x42); type BeefyBlockImport = crate::BeefyBlockImport< Block, substrate_test_runtime_client::Backend, - two_validators::TestApi, - BlockImportAdapter>, + TestApi, + BlockImportAdapter>, >; pub(crate) type BeefyValidatorSet = ValidatorSet; @@ -198,12 +198,12 @@ impl TestNetFactory for BeefyTestNet { Option>, Self::PeerData, ) { + let keys = &[BeefyKeyring::Alice, BeefyKeyring::Bob]; + let validator_set = ValidatorSet::new(make_beefy_ids(keys), 0).unwrap(); + let api = Arc::new(TestApi::with_validator_set(&validator_set)); let inner = BlockImportAdapter::new(client.clone()); - let (block_import, voter_links, rpc_links) = beefy_block_import_and_links( - inner, - client.as_backend(), - Arc::new(two_validators::TestApi {}), - ); + let (block_import, voter_links, rpc_links) = + beefy_block_import_and_links(inner, client.as_backend(), api); let peer_data = PeerData { beefy_rpc_links: Mutex::new(Some(rpc_links)), beefy_voter_links: Mutex::new(Some(voter_links)), @@ -230,79 +230,79 @@ impl TestNetFactory for BeefyTestNet { } } -macro_rules! create_test_api { - ( $api_name:ident, mmr_root: $mmr_root:expr, $($inits:expr),+ ) => { - pub(crate) mod $api_name { - use super::*; - - #[derive(Clone, Default)] - pub(crate) struct TestApi {} +#[derive(Clone)] +pub(crate) struct TestApi { + pub beefy_genesis: u64, + pub validator_set: BeefyValidatorSet, + pub mmr_root_hash: MmrRootHash, +} - // compiler gets confused and warns us about unused inner - #[allow(dead_code)] - pub(crate) struct RuntimeApi { - inner: TestApi, - } +impl TestApi { + pub fn new( + beefy_genesis: u64, + validator_set: &BeefyValidatorSet, + mmr_root_hash: MmrRootHash, + ) -> Self { + TestApi { beefy_genesis, validator_set: validator_set.clone(), mmr_root_hash } + } - impl ProvideRuntimeApi for TestApi { - type Api = RuntimeApi; - fn runtime_api<'a>(&'a self) -> ApiRef<'a, Self::Api> { - RuntimeApi { inner: self.clone() }.into() - } - } - sp_api::mock_impl_runtime_apis! { - impl BeefyApi for RuntimeApi { - fn validator_set() -> Option { - BeefyValidatorSet::new(make_beefy_ids(&[$($inits),+]), 0) - } - } - - impl MmrApi> for RuntimeApi { - fn mmr_root() -> Result { - Ok($mmr_root) - } - - fn generate_proof( - _block_numbers: Vec, - _best_known_block_number: Option - ) -> Result<(Vec, Proof), MmrError> { - unimplemented!() - } - - fn verify_proof(_leaves: Vec, _proof: Proof) -> Result<(), MmrError> { - unimplemented!() - } - - fn verify_proof_stateless( - _root: MmrRootHash, - _leaves: Vec, - _proof: Proof - ) -> Result<(), MmrError> { - unimplemented!() - } - } - } + pub fn with_validator_set(validator_set: &BeefyValidatorSet) -> Self { + TestApi { + beefy_genesis: 1, + validator_set: validator_set.clone(), + mmr_root_hash: GOOD_MMR_ROOT, } - }; + } +} + +// compiler gets confused and warns us about unused inner +#[allow(dead_code)] +pub(crate) struct RuntimeApi { + inner: TestApi, +} + +impl ProvideRuntimeApi for TestApi { + type Api = RuntimeApi; + fn runtime_api(&self) -> ApiRef { + RuntimeApi { inner: self.clone() }.into() + } } +sp_api::mock_impl_runtime_apis! { + impl BeefyApi for RuntimeApi { + fn beefy_genesis() -> Option> { + Some(self.inner.beefy_genesis) + } + + fn validator_set() -> Option { + Some(self.inner.validator_set.clone()) + } + } + + impl MmrApi> for RuntimeApi { + fn mmr_root() -> Result { + Ok(self.inner.mmr_root_hash) + } + + fn generate_proof( + _block_numbers: Vec, + _best_known_block_number: Option + ) -> Result<(Vec, Proof), MmrError> { + unimplemented!() + } + + fn verify_proof(_leaves: Vec, _proof: Proof) -> Result<(), MmrError> { + unimplemented!() + } -create_test_api!(two_validators, mmr_root: GOOD_MMR_ROOT, BeefyKeyring::Alice, BeefyKeyring::Bob); -create_test_api!( - four_validators, - mmr_root: GOOD_MMR_ROOT, - BeefyKeyring::Alice, - BeefyKeyring::Bob, - BeefyKeyring::Charlie, - BeefyKeyring::Dave -); -create_test_api!( - bad_four_validators, - mmr_root: BAD_MMR_ROOT, - BeefyKeyring::Alice, - BeefyKeyring::Bob, - BeefyKeyring::Charlie, - BeefyKeyring::Dave -); + fn verify_proof_stateless( + _root: MmrRootHash, + _leaves: Vec, + _proof: Proof + ) -> Result<(), MmrError> { + unimplemented!() + } + } +} fn add_mmr_digest(header: &mut Header, mmr_hash: MmrRootHash) { header.digest_mut().push(DigestItem::Consensus( @@ -332,9 +332,9 @@ pub(crate) fn create_beefy_keystore(authority: BeefyKeyring) -> SyncCryptoStoreP fn voter_init_setup( net: &mut BeefyTestNet, finality: &mut futures::stream::Fuse>, + api: &TestApi, ) -> sp_blockchain::Result> { let backend = net.peer(0).client().as_backend(); - let api = Arc::new(crate::tests::two_validators::TestApi {}); let known_peers = Arc::new(Mutex::new(KnownPeers::new())); let gossip_validator = Arc::new(crate::communication::gossip::GossipValidator::new(known_peers)); @@ -345,9 +345,9 @@ fn voter_init_setup( None, ); let best_grandpa = - futures::executor::block_on(wait_for_runtime_pallet(&*api, &mut gossip_engine, finality)) + futures::executor::block_on(wait_for_runtime_pallet(api, &mut gossip_engine, finality)) .unwrap(); - load_or_init_voter_state(&*backend, &*api, best_grandpa, 1) + load_or_init_voter_state(&*backend, api, best_grandpa, 1) } // Spawns beefy voters. Returns a future to spawn on the runtime. @@ -357,7 +357,7 @@ fn initialize_beefy( min_block_delta: u32, ) -> impl Future where - API: ProvideRuntimeApi + Default + Sync + Send, + API: ProvideRuntimeApi + Sync + Send, API::Api: BeefyApi + MmrApi>, { let tasks = FuturesUnordered::new(); @@ -544,7 +544,7 @@ async fn beefy_finalizing_blocks() { let mut net = BeefyTestNet::new(2); - let api = Arc::new(two_validators::TestApi {}); + let api = Arc::new(TestApi::with_validator_set(&validator_set)); let beefy_peers = peers.iter().enumerate().map(|(id, key)| (id, key, api.clone())).collect(); tokio::spawn(initialize_beefy(&mut net, beefy_peers, min_block_delta)); @@ -582,7 +582,7 @@ async fn lagging_validators() { let min_block_delta = 1; let mut net = BeefyTestNet::new(2); - let api = Arc::new(two_validators::TestApi {}); + let api = Arc::new(TestApi::with_validator_set(&validator_set)); let beefy_peers = peers.iter().enumerate().map(|(id, key)| (id, key, api.clone())).collect(); tokio::spawn(initialize_beefy(&mut net, beefy_peers, min_block_delta)); @@ -660,7 +660,7 @@ async fn correct_beefy_payload() { let mut net = BeefyTestNet::new(4); // Alice, Bob, Charlie will vote on good payloads - let good_api = Arc::new(four_validators::TestApi {}); + let good_api = Arc::new(TestApi::new(1, &validator_set, GOOD_MMR_ROOT)); let good_peers = [BeefyKeyring::Alice, BeefyKeyring::Bob, BeefyKeyring::Charlie] .iter() .enumerate() @@ -669,7 +669,7 @@ async fn correct_beefy_payload() { tokio::spawn(initialize_beefy(&mut net, good_peers, min_block_delta)); // Dave will vote on bad mmr roots - let bad_api = Arc::new(bad_four_validators::TestApi {}); + let bad_api = Arc::new(TestApi::new(1, &validator_set, BAD_MMR_ROOT)); let bad_peers = vec![(3, &BeefyKeyring::Dave, bad_api)]; tokio::spawn(initialize_beefy(&mut net, bad_peers, min_block_delta)); @@ -714,6 +714,8 @@ async fn beefy_importing_blocks() { sp_tracing::try_init_simple(); let mut net = BeefyTestNet::new(2); + let keys = &[BeefyKeyring::Alice, BeefyKeyring::Bob]; + let good_set = ValidatorSet::new(make_beefy_ids(keys), 0).unwrap(); let client = net.peer(0).client().clone(); let (mut block_import, _, peer_data) = net.make_block_import(client.clone()); @@ -769,9 +771,7 @@ async fn beefy_importing_blocks() { // Import with valid justification. let parent_id = BlockId::Number(1); let block_num = 2; - let keys = &[BeefyKeyring::Alice, BeefyKeyring::Bob]; - let validator_set = ValidatorSet::new(make_beefy_ids(keys), 0).unwrap(); - let proof = crate::justification::tests::new_finality_proof(block_num, &validator_set, keys); + let proof = crate::justification::tests::new_finality_proof(block_num, &good_set, keys); let versioned_proof: VersionedFinalityProof, Signature> = proof.into(); let encoded = versioned_proof.encode(); let justif = Some(Justifications::from((BEEFY_ENGINE_ID, encoded))); @@ -814,8 +814,8 @@ async fn beefy_importing_blocks() { let parent_id = BlockId::Number(2); let block_num = 3; let keys = &[BeefyKeyring::Alice]; - let validator_set = ValidatorSet::new(make_beefy_ids(keys), 1).unwrap(); - let proof = crate::justification::tests::new_finality_proof(block_num, &validator_set, keys); + let bad_set = ValidatorSet::new(make_beefy_ids(keys), 1).unwrap(); + let proof = crate::justification::tests::new_finality_proof(block_num, &bad_set, keys); let versioned_proof: VersionedFinalityProof, Signature> = proof.into(); let encoded = versioned_proof.encode(); let justif = Some(Justifications::from((BEEFY_ENGINE_ID, encoded))); @@ -865,7 +865,7 @@ async fn voter_initialization() { let min_block_delta = 10; let mut net = BeefyTestNet::new(2); - let api = Arc::new(two_validators::TestApi {}); + let api = Arc::new(TestApi::with_validator_set(&validator_set)); let beefy_peers = peers.iter().enumerate().map(|(id, key)| (id, key, api.clone())).collect(); tokio::spawn(initialize_beefy(&mut net, beefy_peers, min_block_delta)); @@ -898,7 +898,7 @@ async fn on_demand_beefy_justification_sync() { let mut net = BeefyTestNet::new(4); // Alice, Bob, Charlie start first and make progress through voting. - let api = Arc::new(four_validators::TestApi {}); + let api = Arc::new(TestApi::with_validator_set(&validator_set)); let fast_peers = [BeefyKeyring::Alice, BeefyKeyring::Bob, BeefyKeyring::Charlie]; let voting_peers = fast_peers.iter().enumerate().map(|(id, key)| (id, key, api.clone())).collect(); @@ -977,8 +977,9 @@ async fn should_initialize_voter_at_genesis() { // finalize 13 without justifications net.peer(0).client().as_client().finalize_block(hashes[13], None).unwrap(); - // load persistent state - nothing in DB, should init at session boundary - let persisted_state = voter_init_setup(&mut net, &mut finality).unwrap(); + let api = TestApi::with_validator_set(&validator_set); + // load persistent state - nothing in DB, should init at genesis + let persisted_state = voter_init_setup(&mut net, &mut finality, &api).unwrap(); // Test initialization at session boundary. // verify voter initialized with two sessions starting at blocks 1 and 10 @@ -1006,6 +1007,54 @@ async fn should_initialize_voter_at_genesis() { assert_eq!(state, persisted_state); } +#[tokio::test] +async fn should_initialize_voter_at_custom_genesis() { + let keys = &[BeefyKeyring::Alice]; + let validator_set = ValidatorSet::new(make_beefy_ids(keys), 0).unwrap(); + let mut net = BeefyTestNet::new(1); + let backend = net.peer(0).client().as_backend(); + // custom pallet genesis is block number 7 + let custom_pallet_genesis = 7; + let api = TestApi::new(custom_pallet_genesis, &validator_set, GOOD_MMR_ROOT); + + // push 15 blocks with `AuthorityChange` digests every 10 blocks + let hashes = net.generate_blocks_and_sync(15, 10, &validator_set, false).await; + + let mut finality = net.peer(0).client().as_client().finality_notification_stream().fuse(); + + // finalize 3, 5, 8 without justifications + net.peer(0).client().as_client().finalize_block(hashes[3], None).unwrap(); + net.peer(0).client().as_client().finalize_block(hashes[5], None).unwrap(); + net.peer(0).client().as_client().finalize_block(hashes[8], None).unwrap(); + + // load persistent state - nothing in DB, should init at genesis + let persisted_state = voter_init_setup(&mut net, &mut finality, &api).unwrap(); + + // Test initialization at session boundary. + // verify voter initialized with single session starting at block `custom_pallet_genesis` (7) + let sessions = persisted_state.voting_oracle().sessions(); + assert_eq!(sessions.len(), 1); + assert_eq!(sessions[0].session_start(), custom_pallet_genesis); + let rounds = persisted_state.active_round().unwrap(); + assert_eq!(rounds.session_start(), custom_pallet_genesis); + assert_eq!(rounds.validator_set_id(), validator_set.id()); + + // verify next vote target is mandatory block 7 + assert_eq!(persisted_state.best_beefy_block(), 0); + assert_eq!(persisted_state.best_grandpa_block(), 8); + assert_eq!( + persisted_state + .voting_oracle() + .voting_target(persisted_state.best_beefy_block(), 13), + Some(custom_pallet_genesis) + ); + + // verify state also saved to db + assert!(verify_persisted_version(&*backend)); + let state = load_persistent(&*backend).unwrap().unwrap(); + assert_eq!(state, persisted_state); +} + #[tokio::test] async fn should_initialize_voter_when_last_final_is_session_boundary() { let keys = &[BeefyKeyring::Alice]; @@ -1038,8 +1087,9 @@ async fn should_initialize_voter_when_last_final_is_session_boundary() { // Test corner-case where session boundary == last beefy finalized, // expect rounds initialized at last beefy finalized 10. + let api = TestApi::with_validator_set(&validator_set); // load persistent state - nothing in DB, should init at session boundary - let persisted_state = voter_init_setup(&mut net, &mut finality).unwrap(); + let persisted_state = voter_init_setup(&mut net, &mut finality, &api).unwrap(); // verify voter initialized with single session starting at block 10 assert_eq!(persisted_state.voting_oracle().sessions().len(), 1); @@ -1095,8 +1145,9 @@ async fn should_initialize_voter_at_latest_finalized() { // Test initialization at last BEEFY finalized. + let api = TestApi::with_validator_set(&validator_set); // load persistent state - nothing in DB, should init at last BEEFY finalized - let persisted_state = voter_init_setup(&mut net, &mut finality).unwrap(); + let persisted_state = voter_init_setup(&mut net, &mut finality, &api).unwrap(); // verify voter initialized with single session starting at block 12 assert_eq!(persisted_state.voting_oracle().sessions().len(), 1); @@ -1119,3 +1170,37 @@ async fn should_initialize_voter_at_latest_finalized() { let state = load_persistent(&*backend).unwrap().unwrap(); assert_eq!(state, persisted_state); } + +#[tokio::test] +async fn beefy_finalizing_after_pallet_genesis() { + sp_tracing::try_init_simple(); + + let peers = [BeefyKeyring::Alice, BeefyKeyring::Bob]; + let validator_set = ValidatorSet::new(make_beefy_ids(&peers), 0).unwrap(); + let session_len = 10; + let min_block_delta = 1; + let pallet_genesis = 15; + + let mut net = BeefyTestNet::new(2); + + let api = Arc::new(TestApi::new(pallet_genesis, &validator_set, GOOD_MMR_ROOT)); + let beefy_peers = peers.iter().enumerate().map(|(id, key)| (id, key, api.clone())).collect(); + tokio::spawn(initialize_beefy(&mut net, beefy_peers, min_block_delta)); + + // push 42 blocks including `AuthorityChange` digests every 10 blocks. + let hashes = net.generate_blocks_and_sync(42, session_len, &validator_set, true).await; + + let net = Arc::new(Mutex::new(net)); + let peers = peers.into_iter().enumerate(); + + // Minimum BEEFY block delta is 1. + + // GRANDPA finalize blocks leading up to BEEFY pallet genesis -> BEEFY should finalize nothing. + finalize_block_and_wait_for_beefy(&net, peers.clone(), &hashes[1..14], &[]).await; + + // GRANDPA finalize block #16 -> BEEFY should finalize #15 (genesis mandatory) and #16. + finalize_block_and_wait_for_beefy(&net, peers.clone(), &[hashes[16]], &[15, 16]).await; + + // GRANDPA finalize #21 -> BEEFY finalize #20 (mandatory) and #21 + finalize_block_and_wait_for_beefy(&net, peers.clone(), &[hashes[21]], &[20, 21]).await; +} diff --git a/client/beefy/src/worker.rs b/client/beefy/src/worker.rs index 19ab52f52..4d057bffc 100644 --- a/client/beefy/src/worker.rs +++ b/client/beefy/src/worker.rs @@ -994,8 +994,8 @@ pub(crate) mod tests { communication::notification::{BeefyBestBlockStream, BeefyVersionedFinalityProofStream}, keystore::tests::Keyring, tests::{ - create_beefy_keystore, get_beefy_streams, make_beefy_ids, two_validators::TestApi, - BeefyPeer, BeefyTestNet, + create_beefy_keystore, get_beefy_streams, make_beefy_ids, BeefyPeer, BeefyTestNet, + TestApi, }, BeefyRPCLinks, KnownPeers, }; @@ -1068,7 +1068,7 @@ pub(crate) mod tests { }; let backend = peer.client().as_backend(); - let api = Arc::new(TestApi {}); + let api = Arc::new(TestApi::with_validator_set(&genesis_validator_set)); let network = peer.network_service().clone(); let known_peers = Arc::new(Mutex::new(KnownPeers::new())); let gossip_validator = Arc::new(GossipValidator::new(known_peers.clone())); diff --git a/frame/beefy/src/lib.rs b/frame/beefy/src/lib.rs index 4cb23107e..86c8763a5 100644 --- a/frame/beefy/src/lib.rs +++ b/frame/beefy/src/lib.rs @@ -49,6 +49,7 @@ pub use pallet::*; pub mod pallet { use super::*; use frame_support::pallet_prelude::*; + use frame_system::pallet_prelude::BlockNumberFor; #[pallet::config] pub trait Config: frame_system::Config { @@ -91,15 +92,32 @@ pub mod pallet { pub(super) type NextAuthorities = StorageValue<_, BoundedVec, ValueQuery>; + /// Block number where BEEFY consensus is enabled/started. + /// If changing this, make sure `Self::ValidatorSetId` is also reset to + /// `GENESIS_AUTHORITY_SET_ID` in the state of the new block number configured here. + #[pallet::storage] + #[pallet::getter(fn genesis_block)] + pub(super) type GenesisBlock = + StorageValue<_, Option>, ValueQuery>; + #[pallet::genesis_config] pub struct GenesisConfig { + /// Initial set of BEEFY authorities. pub authorities: Vec, + /// Block number where BEEFY consensus should start. + /// Should match the session where initial authorities are active. + /// *Note:* Ideally use block number where GRANDPA authorities are changed, + /// to guarantee the client gets a finality notification for exactly this block. + pub genesis_block: Option>, } #[cfg(feature = "std")] impl Default for GenesisConfig { fn default() -> Self { - Self { authorities: Vec::new() } + // BEEFY genesis will be first BEEFY-MANDATORY block, + // use block number one instead of chain-genesis. + let genesis_block = Some(sp_runtime::traits::One::one()); + Self { authorities: Vec::new(), genesis_block } } } @@ -110,6 +128,7 @@ pub mod pallet { // we panic here as runtime maintainers can simply reconfigure genesis and restart // the chain easily .expect("Authorities vec too big"); + >::put(&self.genesis_block); } } } diff --git a/primitives/beefy/src/lib.rs b/primitives/beefy/src/lib.rs index eb7b8db89..8c219040b 100644 --- a/primitives/beefy/src/lib.rs +++ b/primitives/beefy/src/lib.rs @@ -43,7 +43,7 @@ use codec::{Codec, Decode, Encode}; use scale_info::TypeInfo; use sp_application_crypto::RuntimeAppPublic; use sp_core::H256; -use sp_runtime::traits::Hash; +use sp_runtime::traits::{Hash, NumberFor}; use sp_std::prelude::*; /// Key type for BEEFY module. @@ -201,6 +201,9 @@ sp_api::decl_runtime_apis! { /// API necessary for BEEFY voters. pub trait BeefyApi { + /// Return the block number where BEEFY consensus is enabled/started + fn beefy_genesis() -> Option>; + /// Return the current active BEEFY validator set fn validator_set() -> Option>; } diff --git a/test-utils/runtime/src/lib.rs b/test-utils/runtime/src/lib.rs index 1c2d6ec8a..c1a66eb6a 100644 --- a/test-utils/runtime/src/lib.rs +++ b/test-utils/runtime/src/lib.rs @@ -973,6 +973,10 @@ cfg_if! { } impl beefy_primitives::BeefyApi for Runtime { + fn beefy_genesis() -> Option { + None + } + fn validator_set() -> Option> { None } From 12823c2795c99d832bd2535f013c01a6e3f5e605 Mon Sep 17 00:00:00 2001 From: Deepanshu Hooda <43631678+gitofdeepanshu@users.noreply.github.com> Date: Fri, 3 Feb 2023 18:50:12 +0530 Subject: [PATCH 084/558] feat: add event SkepticsChosen event in society (#13291) * feat: add event SkepticsChosen event in society * feat: add test for SkepticsChosen event --- frame/society/src/lib.rs | 10 +++++++--- frame/society/src/tests.rs | 7 +++++++ 2 files changed, 14 insertions(+), 3 deletions(-) diff --git a/frame/society/src/lib.rs b/frame/society/src/lib.rs index 0edf00ff8..18152b9e2 100644 --- a/frame/society/src/lib.rs +++ b/frame/society/src/lib.rs @@ -513,6 +513,8 @@ pub mod pallet { Unfounded { founder: T::AccountId }, /// Some funds were deposited into the society account. Deposit { value: BalanceOf }, + /// A group of members has been choosen as Skeptics + SkepticsChosen { skeptics: Vec }, } /// The first member. @@ -1610,12 +1612,14 @@ impl, I: 'static> Pallet { // Select sqrt(n) random members from the society and make them skeptics. let pick_member = - |_| pick_item(&mut rng, &members[..]).expect("exited if members empty; qed"); - for skeptic in (0..members.len().integer_sqrt()).map(pick_member) { + |_| pick_item(&mut rng, &members[..]).expect("exited if members empty; qed").clone(); + let skeptics = (0..members.len().integer_sqrt()).map(pick_member).collect::>(); + skeptics.iter().for_each(|skeptic| { for Bid { who: c, .. } in candidates.iter() { >::insert(c, skeptic, Vote::Skeptic); } - } + }); + Self::deposit_event(Event::::SkepticsChosen { skeptics }); } /// Attempt to slash the payout of some member. Return the total amount that was deducted. diff --git a/frame/society/src/tests.rs b/frame/society/src/tests.rs index 864735aa1..0b42f7703 100644 --- a/frame/society/src/tests.rs +++ b/frame/society/src/tests.rs @@ -211,6 +211,9 @@ fn payout_works() { #[test] fn basic_new_member_skeptic_works() { EnvBuilder::new().execute(|| { + // NOTE: events are not deposited in the genesis event + System::set_block_number(1); + assert_eq!(Strikes::::get(10), 0); assert_ok!(Society::bid(RuntimeOrigin::signed(20), 0)); run_to_block(4); @@ -218,6 +221,10 @@ fn basic_new_member_skeptic_works() { run_to_block(8); assert_eq!(Society::members(), vec![10]); assert_eq!(Strikes::::get(10), 1); + + System::assert_last_event(mock::RuntimeEvent::Society(crate::Event::SkepticsChosen { + skeptics: vec![10], + })); }); } From 09ae0e08f2ee72bb390b83370b2e1e4048904d18 Mon Sep 17 00:00:00 2001 From: Hussein Ait-Lahcen Date: Fri, 3 Feb 2023 15:21:24 +0100 Subject: [PATCH 085/558] feat(client): significantly increase wasm instance limits (#13298) Following https://github.com/paritytech/substrate/issues/11949, this PR will allow parachains with runtimes bigger than Kusama to use the pooling strategy. --- client/executor/wasmtime/src/runtime.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/client/executor/wasmtime/src/runtime.rs b/client/executor/wasmtime/src/runtime.rs index a45dfa59b..0c3fe228f 100644 --- a/client/executor/wasmtime/src/runtime.rs +++ b/client/executor/wasmtime/src/runtime.rs @@ -376,8 +376,8 @@ fn common_config(semantics: &Semantics) -> std::result::Result Date: Fri, 3 Feb 2023 12:32:55 -0300 Subject: [PATCH 086/558] Remove in-tree bounded types and use bounded-collections crate (#13243) * Remove in-tree bounded types and use bounded-collections crate * Fixes * Bump bounded-collections version * cargo fmt * Bump bounded-collections * Only export non-bounded types at the top level * Fixes * Bump bounded-collections --- Cargo.lock | 17 +- primitives/core/Cargo.toml | 2 + .../core/src/bounded/bounded_btree_map.rs | 625 -------- .../core/src/bounded/bounded_btree_set.rs | 482 ------ primitives/core/src/bounded/bounded_vec.rs | 1305 ----------------- .../core/src/bounded/weak_bounded_vec.rs | 524 ------- primitives/core/src/lib.rs | 244 +-- 7 files changed, 24 insertions(+), 3175 deletions(-) delete mode 100644 primitives/core/src/bounded/bounded_btree_map.rs delete mode 100644 primitives/core/src/bounded/bounded_btree_set.rs delete mode 100644 primitives/core/src/bounded/bounded_vec.rs delete mode 100644 primitives/core/src/bounded/weak_bounded_vec.rs diff --git a/Cargo.lock b/Cargo.lock index dda127fdd..492c5b15d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -699,6 +699,18 @@ version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8d696c370c750c948ada61c69a0ee2cbbb9c50b1019ddb86d9317157a99c2cae" +[[package]] +name = "bounded-collections" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "de2aff4807e40f478132150d80b031f2461d88f061851afcab537d7600c24120" +dependencies = [ + "log", + "parity-scale-codec", + "scale-info", + "serde", +] + [[package]] name = "bs58" version = "0.4.0" @@ -6722,9 +6734,9 @@ dependencies = [ [[package]] name = "parity-scale-codec" -version = "3.2.2" +version = "3.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e7ab01d0f889e957861bc65888d5ccbe82c158d0270136ba46820d43837cdf72" +checksum = "c3840933452adf7b3b9145e27086a5a3376c619dca1a21b1e5a5af0d54979bed" dependencies = [ "arrayvec 0.7.2", "bitvec", @@ -9849,6 +9861,7 @@ dependencies = [ "base58", "bitflags", "blake2", + "bounded-collections", "criterion", "dyn-clonable", "ed25519-zebra", diff --git a/primitives/core/Cargo.toml b/primitives/core/Cargo.toml index 5fd838de3..7c0453775 100644 --- a/primitives/core/Cargo.toml +++ b/primitives/core/Cargo.toml @@ -20,6 +20,7 @@ codec = { package = "parity-scale-codec", version = "3.2.2", default-features = scale-info = { version = "2.1.1", default-features = false, features = ["derive"] } log = { version = "0.4.17", default-features = false } serde = { version = "1.0.136", optional = true, features = ["derive"] } +bounded-collections = { version = "0.1.4", default-features = false } primitive-types = { version = "0.12.0", default-features = false, features = ["codec", "scale-info"] } impl-serde = { version = "0.4.0", optional = true } hash-db = { version = "0.15.2", default-features = false } @@ -80,6 +81,7 @@ std = [ "thiserror", "lazy_static", "parking_lot", + "bounded-collections/std", "primitive-types/std", "primitive-types/serde", "primitive-types/byteorder", diff --git a/primitives/core/src/bounded/bounded_btree_map.rs b/primitives/core/src/bounded/bounded_btree_map.rs deleted file mode 100644 index d2c148d6d..000000000 --- a/primitives/core/src/bounded/bounded_btree_map.rs +++ /dev/null @@ -1,625 +0,0 @@ -// This file is part of Substrate. - -// Copyright (C) 2017-2022 Parity Technologies (UK) Ltd. -// SPDX-License-Identifier: Apache-2.0 - -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -//! Traits, types and structs to support a bounded BTreeMap. - -use crate::{Get, TryCollect}; -use codec::{Decode, Encode, MaxEncodedLen}; -use sp_std::{borrow::Borrow, collections::btree_map::BTreeMap, marker::PhantomData, ops::Deref}; - -/// A bounded map based on a B-Tree. -/// -/// B-Trees represent a fundamental compromise between cache-efficiency and actually minimizing -/// the amount of work performed in a search. See [`BTreeMap`] for more details. -/// -/// Unlike a standard `BTreeMap`, there is an enforced upper limit to the number of items in the -/// map. All internal operations ensure this bound is respected. -#[derive(Encode, scale_info::TypeInfo)] -#[scale_info(skip_type_params(S))] -pub struct BoundedBTreeMap(BTreeMap, PhantomData); - -impl Decode for BoundedBTreeMap -where - K: Decode + Ord, - V: Decode, - S: Get, -{ - fn decode(input: &mut I) -> Result { - let inner = BTreeMap::::decode(input)?; - if inner.len() > S::get() as usize { - return Err("BoundedBTreeMap exceeds its limit".into()) - } - Ok(Self(inner, PhantomData)) - } - - fn skip(input: &mut I) -> Result<(), codec::Error> { - BTreeMap::::skip(input) - } -} - -impl BoundedBTreeMap -where - S: Get, -{ - /// Get the bound of the type in `usize`. - pub fn bound() -> usize { - S::get() as usize - } -} - -impl BoundedBTreeMap -where - K: Ord, - S: Get, -{ - /// Create `Self` from `t` without any checks. - fn unchecked_from(t: BTreeMap) -> Self { - Self(t, Default::default()) - } - - /// Exactly the same semantics as `BTreeMap::retain`. - /// - /// The is a safe `&mut self` borrow because `retain` can only ever decrease the length of the - /// inner map. - pub fn retain bool>(&mut self, f: F) { - self.0.retain(f) - } - - /// Create a new `BoundedBTreeMap`. - /// - /// Does not allocate. - pub fn new() -> Self { - BoundedBTreeMap(BTreeMap::new(), PhantomData) - } - - /// Consume self, and return the inner `BTreeMap`. - /// - /// This is useful when a mutating API of the inner type is desired, and closure-based mutation - /// such as provided by [`try_mutate`][Self::try_mutate] is inconvenient. - pub fn into_inner(self) -> BTreeMap { - debug_assert!(self.0.len() <= Self::bound()); - self.0 - } - - /// Consumes self and mutates self via the given `mutate` function. - /// - /// If the outcome of mutation is within bounds, `Some(Self)` is returned. Else, `None` is - /// returned. - /// - /// This is essentially a *consuming* shorthand [`Self::into_inner`] -> `...` -> - /// [`Self::try_from`]. - pub fn try_mutate(mut self, mut mutate: impl FnMut(&mut BTreeMap)) -> Option { - mutate(&mut self.0); - (self.0.len() <= Self::bound()).then(move || self) - } - - /// Clears the map, removing all elements. - pub fn clear(&mut self) { - self.0.clear() - } - - /// Return a mutable reference to the value corresponding to the key. - /// - /// The key may be any borrowed form of the map's key type, but the ordering on the borrowed - /// form _must_ match the ordering on the key type. - pub fn get_mut(&mut self, key: &Q) -> Option<&mut V> - where - K: Borrow, - Q: Ord + ?Sized, - { - self.0.get_mut(key) - } - - /// Exactly the same semantics as [`BTreeMap::insert`], but returns an `Err` (and is a noop) if - /// the new length of the map exceeds `S`. - /// - /// In the `Err` case, returns the inserted pair so it can be further used without cloning. - pub fn try_insert(&mut self, key: K, value: V) -> Result, (K, V)> { - if self.len() < Self::bound() || self.0.contains_key(&key) { - Ok(self.0.insert(key, value)) - } else { - Err((key, value)) - } - } - - /// Remove a key from the map, returning the value at the key if the key was previously in the - /// map. - /// - /// The key may be any borrowed form of the map's key type, but the ordering on the borrowed - /// form _must_ match the ordering on the key type. - pub fn remove(&mut self, key: &Q) -> Option - where - K: Borrow, - Q: Ord + ?Sized, - { - self.0.remove(key) - } - - /// Remove a key from the map, returning the value at the key if the key was previously in the - /// map. - /// - /// The key may be any borrowed form of the map's key type, but the ordering on the borrowed - /// form _must_ match the ordering on the key type. - pub fn remove_entry(&mut self, key: &Q) -> Option<(K, V)> - where - K: Borrow, - Q: Ord + ?Sized, - { - self.0.remove_entry(key) - } - - /// Gets a mutable iterator over the entries of the map, sorted by key. - /// - /// See [`BTreeMap::iter_mut`] for more information. - pub fn iter_mut(&mut self) -> sp_std::collections::btree_map::IterMut { - self.0.iter_mut() - } - - /// Consume the map, applying `f` to each of it's values and returning a new map. - pub fn map(self, mut f: F) -> BoundedBTreeMap - where - F: FnMut((&K, V)) -> T, - { - BoundedBTreeMap::::unchecked_from( - self.0 - .into_iter() - .map(|(k, v)| { - let t = f((&k, v)); - (k, t) - }) - .collect(), - ) - } - - /// Consume the map, applying `f` to each of it's values as long as it returns successfully. If - /// an `Err(E)` is ever encountered, the mapping is short circuited and the error is returned; - /// otherwise, a new map is returned in the contained `Ok` value. - pub fn try_map(self, mut f: F) -> Result, E> - where - F: FnMut((&K, V)) -> Result, - { - Ok(BoundedBTreeMap::::unchecked_from( - self.0 - .into_iter() - .map(|(k, v)| (f((&k, v)).map(|t| (k, t)))) - .collect::, _>>()?, - )) - } -} - -impl Default for BoundedBTreeMap -where - K: Ord, - S: Get, -{ - fn default() -> Self { - Self::new() - } -} - -impl Clone for BoundedBTreeMap -where - BTreeMap: Clone, -{ - fn clone(&self) -> Self { - BoundedBTreeMap(self.0.clone(), PhantomData) - } -} - -impl sp_std::fmt::Debug for BoundedBTreeMap -where - BTreeMap: sp_std::fmt::Debug, - S: Get, -{ - fn fmt(&self, f: &mut sp_std::fmt::Formatter<'_>) -> sp_std::fmt::Result { - f.debug_tuple("BoundedBTreeMap").field(&self.0).field(&Self::bound()).finish() - } -} - -impl PartialEq> for BoundedBTreeMap -where - BTreeMap: PartialEq, - S1: Get, - S2: Get, -{ - fn eq(&self, other: &BoundedBTreeMap) -> bool { - S1::get() == S2::get() && self.0 == other.0 - } -} - -impl Eq for BoundedBTreeMap -where - BTreeMap: Eq, - S: Get, -{ -} - -impl PartialEq> for BoundedBTreeMap -where - BTreeMap: PartialEq, -{ - fn eq(&self, other: &BTreeMap) -> bool { - self.0 == *other - } -} - -impl PartialOrd for BoundedBTreeMap -where - BTreeMap: PartialOrd, - S: Get, -{ - fn partial_cmp(&self, other: &Self) -> Option { - self.0.partial_cmp(&other.0) - } -} - -impl Ord for BoundedBTreeMap -where - BTreeMap: Ord, - S: Get, -{ - fn cmp(&self, other: &Self) -> sp_std::cmp::Ordering { - self.0.cmp(&other.0) - } -} - -impl IntoIterator for BoundedBTreeMap { - type Item = (K, V); - type IntoIter = sp_std::collections::btree_map::IntoIter; - - fn into_iter(self) -> Self::IntoIter { - self.0.into_iter() - } -} - -impl<'a, K, V, S> IntoIterator for &'a BoundedBTreeMap { - type Item = (&'a K, &'a V); - type IntoIter = sp_std::collections::btree_map::Iter<'a, K, V>; - - fn into_iter(self) -> Self::IntoIter { - self.0.iter() - } -} - -impl<'a, K, V, S> IntoIterator for &'a mut BoundedBTreeMap { - type Item = (&'a K, &'a mut V); - type IntoIter = sp_std::collections::btree_map::IterMut<'a, K, V>; - - fn into_iter(self) -> Self::IntoIter { - self.0.iter_mut() - } -} - -impl MaxEncodedLen for BoundedBTreeMap -where - K: MaxEncodedLen, - V: MaxEncodedLen, - S: Get, -{ - fn max_encoded_len() -> usize { - Self::bound() - .saturating_mul(K::max_encoded_len().saturating_add(V::max_encoded_len())) - .saturating_add(codec::Compact(S::get()).encoded_size()) - } -} - -impl Deref for BoundedBTreeMap -where - K: Ord, -{ - type Target = BTreeMap; - - fn deref(&self) -> &Self::Target { - &self.0 - } -} - -impl AsRef> for BoundedBTreeMap -where - K: Ord, -{ - fn as_ref(&self) -> &BTreeMap { - &self.0 - } -} - -impl From> for BTreeMap -where - K: Ord, -{ - fn from(map: BoundedBTreeMap) -> Self { - map.0 - } -} - -impl TryFrom> for BoundedBTreeMap -where - K: Ord, - S: Get, -{ - type Error = (); - - fn try_from(value: BTreeMap) -> Result { - (value.len() <= Self::bound()) - .then(move || BoundedBTreeMap(value, PhantomData)) - .ok_or(()) - } -} - -impl codec::DecodeLength for BoundedBTreeMap { - fn len(self_encoded: &[u8]) -> Result { - // `BoundedBTreeMap` is stored just a `BTreeMap`, which is stored as a - // `Compact` with its length followed by an iteration of its items. We can just use - // the underlying implementation. - as codec::DecodeLength>::len(self_encoded) - } -} - -impl codec::EncodeLike> for BoundedBTreeMap where - BTreeMap: Encode -{ -} - -impl TryCollect> for I -where - K: Ord, - I: ExactSizeIterator + Iterator, - Bound: Get, -{ - type Error = &'static str; - - fn try_collect(self) -> Result, Self::Error> { - if self.len() > Bound::get() as usize { - Err("iterator length too big") - } else { - Ok(BoundedBTreeMap::::unchecked_from(self.collect::>())) - } - } -} - -#[cfg(test)] -pub mod test { - use super::*; - use crate::ConstU32; - - fn map_from_keys(keys: &[K]) -> BTreeMap - where - K: Ord + Copy, - { - keys.iter().copied().zip(std::iter::repeat(())).collect() - } - - fn boundedmap_from_keys(keys: &[K]) -> BoundedBTreeMap - where - K: Ord + Copy, - S: Get, - { - map_from_keys(keys).try_into().unwrap() - } - - #[test] - fn try_insert_works() { - let mut bounded = boundedmap_from_keys::>(&[1, 2, 3]); - bounded.try_insert(0, ()).unwrap(); - assert_eq!(*bounded, map_from_keys(&[1, 0, 2, 3])); - - assert!(bounded.try_insert(9, ()).is_err()); - assert_eq!(*bounded, map_from_keys(&[1, 0, 2, 3])); - } - - #[test] - fn deref_coercion_works() { - let bounded = boundedmap_from_keys::>(&[1, 2, 3]); - // these methods come from deref-ed vec. - assert_eq!(bounded.len(), 3); - assert!(bounded.iter().next().is_some()); - assert!(!bounded.is_empty()); - } - - #[test] - fn try_mutate_works() { - let bounded = boundedmap_from_keys::>(&[1, 2, 3, 4, 5, 6]); - let bounded = bounded - .try_mutate(|v| { - v.insert(7, ()); - }) - .unwrap(); - assert_eq!(bounded.len(), 7); - assert!(bounded - .try_mutate(|v| { - v.insert(8, ()); - }) - .is_none()); - } - - #[test] - fn btree_map_eq_works() { - let bounded = boundedmap_from_keys::>(&[1, 2, 3, 4, 5, 6]); - assert_eq!(bounded, map_from_keys(&[1, 2, 3, 4, 5, 6])); - } - - #[test] - fn too_big_fail_to_decode() { - let v: Vec<(u32, u32)> = vec![(1, 1), (2, 2), (3, 3), (4, 4), (5, 5)]; - assert_eq!( - BoundedBTreeMap::>::decode(&mut &v.encode()[..]), - Err("BoundedBTreeMap exceeds its limit".into()), - ); - } - - #[test] - fn unequal_eq_impl_insert_works() { - // given a struct with a strange notion of equality - #[derive(Debug)] - struct Unequal(u32, bool); - - impl PartialEq for Unequal { - fn eq(&self, other: &Self) -> bool { - self.0 == other.0 - } - } - impl Eq for Unequal {} - - impl Ord for Unequal { - fn cmp(&self, other: &Self) -> std::cmp::Ordering { - self.0.cmp(&other.0) - } - } - - impl PartialOrd for Unequal { - fn partial_cmp(&self, other: &Self) -> Option { - Some(self.cmp(other)) - } - } - - let mut map = BoundedBTreeMap::>::new(); - - // when the set is full - - for i in 0..4 { - map.try_insert(Unequal(i, false), i).unwrap(); - } - - // can't insert a new distinct member - map.try_insert(Unequal(5, false), 5).unwrap_err(); - - // but _can_ insert a distinct member which compares equal, though per the documentation, - // neither the set length nor the actual member are changed, but the value is - map.try_insert(Unequal(0, true), 6).unwrap(); - assert_eq!(map.len(), 4); - let (zero_key, zero_value) = map.get_key_value(&Unequal(0, true)).unwrap(); - assert_eq!(zero_key.0, 0); - assert_eq!(zero_key.1, false); - assert_eq!(*zero_value, 6); - } - - #[test] - fn eq_works() { - // of same type - let b1 = boundedmap_from_keys::>(&[1, 2]); - let b2 = boundedmap_from_keys::>(&[1, 2]); - assert_eq!(b1, b2); - - // of different type, but same value and bound. - crate::parameter_types! { - B1: u32 = 7; - B2: u32 = 7; - } - let b1 = boundedmap_from_keys::(&[1, 2]); - let b2 = boundedmap_from_keys::(&[1, 2]); - assert_eq!(b1, b2); - } - - #[test] - fn can_be_collected() { - let b1 = boundedmap_from_keys::>(&[1, 2, 3, 4]); - let b2: BoundedBTreeMap> = - b1.iter().map(|(k, v)| (k + 1, *v)).try_collect().unwrap(); - assert_eq!(b2.into_iter().map(|(k, _)| k).collect::>(), vec![2, 3, 4, 5]); - - // can also be collected into a collection of length 4. - let b2: BoundedBTreeMap> = - b1.iter().map(|(k, v)| (k + 1, *v)).try_collect().unwrap(); - assert_eq!(b2.into_iter().map(|(k, _)| k).collect::>(), vec![2, 3, 4, 5]); - - // can be mutated further into iterators that are `ExactSizedIterator`. - let b2: BoundedBTreeMap> = - b1.iter().map(|(k, v)| (k + 1, *v)).rev().skip(2).try_collect().unwrap(); - // note that the binary tree will re-sort this, so rev() is not really seen - assert_eq!(b2.into_iter().map(|(k, _)| k).collect::>(), vec![2, 3]); - - let b2: BoundedBTreeMap> = - b1.iter().map(|(k, v)| (k + 1, *v)).take(2).try_collect().unwrap(); - assert_eq!(b2.into_iter().map(|(k, _)| k).collect::>(), vec![2, 3]); - - // but these won't work - let b2: Result>, _> = - b1.iter().map(|(k, v)| (k + 1, *v)).try_collect(); - assert!(b2.is_err()); - - let b2: Result>, _> = - b1.iter().map(|(k, v)| (k + 1, *v)).skip(2).try_collect(); - assert!(b2.is_err()); - } - - #[test] - fn test_iter_mut() { - let mut b1: BoundedBTreeMap> = - [1, 2, 3, 4].into_iter().map(|k| (k, k)).try_collect().unwrap(); - - let b2: BoundedBTreeMap> = - [1, 2, 3, 4].into_iter().map(|k| (k, k * 2)).try_collect().unwrap(); - - b1.iter_mut().for_each(|(_, v)| *v *= 2); - - assert_eq!(b1, b2); - } - - #[test] - fn map_retains_size() { - let b1 = boundedmap_from_keys::>(&[1, 2]); - let b2 = b1.clone(); - - assert_eq!(b1.len(), b2.map(|(_, _)| 5_u32).len()); - } - - #[test] - fn map_maps_properly() { - let b1: BoundedBTreeMap> = - [1, 2, 3, 4].into_iter().map(|k| (k, k * 2)).try_collect().unwrap(); - let b2: BoundedBTreeMap> = - [1, 2, 3, 4].into_iter().map(|k| (k, k)).try_collect().unwrap(); - - assert_eq!(b1, b2.map(|(_, v)| v * 2)); - } - - #[test] - fn try_map_retains_size() { - let b1 = boundedmap_from_keys::>(&[1, 2]); - let b2 = b1.clone(); - - assert_eq!(b1.len(), b2.try_map::<_, (), _>(|(_, _)| Ok(5_u32)).unwrap().len()); - } - - #[test] - fn try_map_maps_properly() { - let b1: BoundedBTreeMap> = - [1, 2, 3, 4].into_iter().map(|k| (k, k * 2)).try_collect().unwrap(); - let b2: BoundedBTreeMap> = - [1, 2, 3, 4].into_iter().map(|k| (k, k)).try_collect().unwrap(); - - assert_eq!(b1, b2.try_map::<_, (), _>(|(_, v)| Ok(v * 2)).unwrap()); - } - - #[test] - fn try_map_short_circuit() { - let b1: BoundedBTreeMap> = - [1, 2, 3, 4].into_iter().map(|k| (k, k)).try_collect().unwrap(); - - assert_eq!(Err("overflow"), b1.try_map(|(_, v)| v.checked_mul(100).ok_or("overflow"))); - } - - #[test] - fn try_map_ok() { - let b1: BoundedBTreeMap> = - [1, 2, 3, 4].into_iter().map(|k| (k, k)).try_collect().unwrap(); - let b2: BoundedBTreeMap> = - [1, 2, 3, 4].into_iter().map(|k| (k, (k as u16) * 100)).try_collect().unwrap(); - - assert_eq!(Ok(b2), b1.try_map(|(_, v)| (v as u16).checked_mul(100_u16).ok_or("overflow"))); - } -} diff --git a/primitives/core/src/bounded/bounded_btree_set.rs b/primitives/core/src/bounded/bounded_btree_set.rs deleted file mode 100644 index 5feac6b71..000000000 --- a/primitives/core/src/bounded/bounded_btree_set.rs +++ /dev/null @@ -1,482 +0,0 @@ -// This file is part of Substrate. - -// Copyright (C) 2022 Parity Technologies (UK) Ltd. -// SPDX-License-Identifier: Apache-2.0 - -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -//! Traits, types and structs to support a bounded `BTreeSet`. - -use crate::{Get, TryCollect}; -use codec::{Decode, Encode, MaxEncodedLen}; -use sp_std::{borrow::Borrow, collections::btree_set::BTreeSet, marker::PhantomData, ops::Deref}; - -/// A bounded set based on a B-Tree. -/// -/// B-Trees represent a fundamental compromise between cache-efficiency and actually minimizing -/// the amount of work performed in a search. See [`BTreeSet`] for more details. -/// -/// Unlike a standard `BTreeSet`, there is an enforced upper limit to the number of items in the -/// set. All internal operations ensure this bound is respected. -#[derive(Encode, scale_info::TypeInfo)] -#[scale_info(skip_type_params(S))] -pub struct BoundedBTreeSet(BTreeSet, PhantomData); - -impl Decode for BoundedBTreeSet -where - T: Decode + Ord, - S: Get, -{ - fn decode(input: &mut I) -> Result { - let inner = BTreeSet::::decode(input)?; - if inner.len() > S::get() as usize { - return Err("BoundedBTreeSet exceeds its limit".into()) - } - Ok(Self(inner, PhantomData)) - } - - fn skip(input: &mut I) -> Result<(), codec::Error> { - BTreeSet::::skip(input) - } -} - -impl BoundedBTreeSet -where - S: Get, -{ - /// Get the bound of the type in `usize`. - pub fn bound() -> usize { - S::get() as usize - } -} - -impl BoundedBTreeSet -where - T: Ord, - S: Get, -{ - /// Create `Self` from `t` without any checks. - fn unchecked_from(t: BTreeSet) -> Self { - Self(t, Default::default()) - } - - /// Create a new `BoundedBTreeSet`. - /// - /// Does not allocate. - pub fn new() -> Self { - BoundedBTreeSet(BTreeSet::new(), PhantomData) - } - - /// Consume self, and return the inner `BTreeSet`. - /// - /// This is useful when a mutating API of the inner type is desired, and closure-based mutation - /// such as provided by [`try_mutate`][Self::try_mutate] is inconvenient. - pub fn into_inner(self) -> BTreeSet { - debug_assert!(self.0.len() <= Self::bound()); - self.0 - } - - /// Consumes self and mutates self via the given `mutate` function. - /// - /// If the outcome of mutation is within bounds, `Some(Self)` is returned. Else, `None` is - /// returned. - /// - /// This is essentially a *consuming* shorthand [`Self::into_inner`] -> `...` -> - /// [`Self::try_from`]. - pub fn try_mutate(mut self, mut mutate: impl FnMut(&mut BTreeSet)) -> Option { - mutate(&mut self.0); - (self.0.len() <= Self::bound()).then(move || self) - } - - /// Clears the set, removing all elements. - pub fn clear(&mut self) { - self.0.clear() - } - - /// Exactly the same semantics as [`BTreeSet::insert`], but returns an `Err` (and is a noop) if - /// the new length of the set exceeds `S`. - /// - /// In the `Err` case, returns the inserted item so it can be further used without cloning. - pub fn try_insert(&mut self, item: T) -> Result { - if self.len() < Self::bound() || self.0.contains(&item) { - Ok(self.0.insert(item)) - } else { - Err(item) - } - } - - /// Remove an item from the set, returning whether it was previously in the set. - /// - /// The item may be any borrowed form of the set's item type, but the ordering on the borrowed - /// form _must_ match the ordering on the item type. - pub fn remove(&mut self, item: &Q) -> bool - where - T: Borrow, - Q: Ord + ?Sized, - { - self.0.remove(item) - } - - /// Removes and returns the value in the set, if any, that is equal to the given one. - /// - /// The value may be any borrowed form of the set's value type, but the ordering on the borrowed - /// form _must_ match the ordering on the value type. - pub fn take(&mut self, value: &Q) -> Option - where - T: Borrow + Ord, - Q: Ord + ?Sized, - { - self.0.take(value) - } -} - -impl Default for BoundedBTreeSet -where - T: Ord, - S: Get, -{ - fn default() -> Self { - Self::new() - } -} - -impl Clone for BoundedBTreeSet -where - BTreeSet: Clone, -{ - fn clone(&self) -> Self { - BoundedBTreeSet(self.0.clone(), PhantomData) - } -} - -impl sp_std::fmt::Debug for BoundedBTreeSet -where - BTreeSet: sp_std::fmt::Debug, - S: Get, -{ - fn fmt(&self, f: &mut sp_std::fmt::Formatter<'_>) -> sp_std::fmt::Result { - f.debug_tuple("BoundedBTreeSet").field(&self.0).field(&Self::bound()).finish() - } -} - -impl PartialEq> for BoundedBTreeSet -where - BTreeSet: PartialEq, - S1: Get, - S2: Get, -{ - fn eq(&self, other: &BoundedBTreeSet) -> bool { - S1::get() == S2::get() && self.0 == other.0 - } -} - -impl Eq for BoundedBTreeSet -where - BTreeSet: Eq, - S: Get, -{ -} - -impl PartialEq> for BoundedBTreeSet -where - BTreeSet: PartialEq, - S: Get, -{ - fn eq(&self, other: &BTreeSet) -> bool { - self.0 == *other - } -} - -impl PartialOrd for BoundedBTreeSet -where - BTreeSet: PartialOrd, - S: Get, -{ - fn partial_cmp(&self, other: &Self) -> Option { - self.0.partial_cmp(&other.0) - } -} - -impl Ord for BoundedBTreeSet -where - BTreeSet: Ord, - S: Get, -{ - fn cmp(&self, other: &Self) -> sp_std::cmp::Ordering { - self.0.cmp(&other.0) - } -} - -impl IntoIterator for BoundedBTreeSet { - type Item = T; - type IntoIter = sp_std::collections::btree_set::IntoIter; - - fn into_iter(self) -> Self::IntoIter { - self.0.into_iter() - } -} - -impl<'a, T, S> IntoIterator for &'a BoundedBTreeSet { - type Item = &'a T; - type IntoIter = sp_std::collections::btree_set::Iter<'a, T>; - - fn into_iter(self) -> Self::IntoIter { - self.0.iter() - } -} - -impl MaxEncodedLen for BoundedBTreeSet -where - T: MaxEncodedLen, - S: Get, -{ - fn max_encoded_len() -> usize { - Self::bound() - .saturating_mul(T::max_encoded_len()) - .saturating_add(codec::Compact(S::get()).encoded_size()) - } -} - -impl Deref for BoundedBTreeSet -where - T: Ord, -{ - type Target = BTreeSet; - - fn deref(&self) -> &Self::Target { - &self.0 - } -} - -impl AsRef> for BoundedBTreeSet -where - T: Ord, -{ - fn as_ref(&self) -> &BTreeSet { - &self.0 - } -} - -impl From> for BTreeSet -where - T: Ord, -{ - fn from(set: BoundedBTreeSet) -> Self { - set.0 - } -} - -impl TryFrom> for BoundedBTreeSet -where - T: Ord, - S: Get, -{ - type Error = (); - - fn try_from(value: BTreeSet) -> Result { - (value.len() <= Self::bound()) - .then(move || BoundedBTreeSet(value, PhantomData)) - .ok_or(()) - } -} - -impl codec::DecodeLength for BoundedBTreeSet { - fn len(self_encoded: &[u8]) -> Result { - // `BoundedBTreeSet` is stored just a `BTreeSet`, which is stored as a - // `Compact` with its length followed by an iteration of its items. We can just use - // the underlying implementation. - as codec::DecodeLength>::len(self_encoded) - } -} - -impl codec::EncodeLike> for BoundedBTreeSet where BTreeSet: Encode {} - -impl TryCollect> for I -where - T: Ord, - I: ExactSizeIterator + Iterator, - Bound: Get, -{ - type Error = &'static str; - - fn try_collect(self) -> Result, Self::Error> { - if self.len() > Bound::get() as usize { - Err("iterator length too big") - } else { - Ok(BoundedBTreeSet::::unchecked_from(self.collect::>())) - } - } -} - -#[cfg(test)] -pub mod test { - use super::*; - use crate::ConstU32; - - fn set_from_keys(keys: &[T]) -> BTreeSet - where - T: Ord + Copy, - { - keys.iter().copied().collect() - } - - fn boundedset_from_keys(keys: &[T]) -> BoundedBTreeSet - where - T: Ord + Copy, - S: Get, - { - set_from_keys(keys).try_into().unwrap() - } - - #[test] - fn try_insert_works() { - let mut bounded = boundedset_from_keys::>(&[1, 2, 3]); - bounded.try_insert(0).unwrap(); - assert_eq!(*bounded, set_from_keys(&[1, 0, 2, 3])); - - assert!(bounded.try_insert(9).is_err()); - assert_eq!(*bounded, set_from_keys(&[1, 0, 2, 3])); - } - - #[test] - fn deref_coercion_works() { - let bounded = boundedset_from_keys::>(&[1, 2, 3]); - // these methods come from deref-ed vec. - assert_eq!(bounded.len(), 3); - assert!(bounded.iter().next().is_some()); - assert!(!bounded.is_empty()); - } - - #[test] - fn try_mutate_works() { - let bounded = boundedset_from_keys::>(&[1, 2, 3, 4, 5, 6]); - let bounded = bounded - .try_mutate(|v| { - v.insert(7); - }) - .unwrap(); - assert_eq!(bounded.len(), 7); - assert!(bounded - .try_mutate(|v| { - v.insert(8); - }) - .is_none()); - } - - #[test] - fn btree_map_eq_works() { - let bounded = boundedset_from_keys::>(&[1, 2, 3, 4, 5, 6]); - assert_eq!(bounded, set_from_keys(&[1, 2, 3, 4, 5, 6])); - } - - #[test] - fn too_big_fail_to_decode() { - let v: Vec = vec![1, 2, 3, 4, 5]; - assert_eq!( - BoundedBTreeSet::>::decode(&mut &v.encode()[..]), - Err("BoundedBTreeSet exceeds its limit".into()), - ); - } - - #[test] - fn unequal_eq_impl_insert_works() { - // given a struct with a strange notion of equality - #[derive(Debug)] - struct Unequal(u32, bool); - - impl PartialEq for Unequal { - fn eq(&self, other: &Self) -> bool { - self.0 == other.0 - } - } - impl Eq for Unequal {} - - impl Ord for Unequal { - fn cmp(&self, other: &Self) -> std::cmp::Ordering { - self.0.cmp(&other.0) - } - } - - impl PartialOrd for Unequal { - fn partial_cmp(&self, other: &Self) -> Option { - Some(self.cmp(other)) - } - } - - let mut set = BoundedBTreeSet::>::new(); - - // when the set is full - - for i in 0..4 { - set.try_insert(Unequal(i, false)).unwrap(); - } - - // can't insert a new distinct member - set.try_insert(Unequal(5, false)).unwrap_err(); - - // but _can_ insert a distinct member which compares equal, though per the documentation, - // neither the set length nor the actual member are changed - set.try_insert(Unequal(0, true)).unwrap(); - assert_eq!(set.len(), 4); - let zero_item = set.get(&Unequal(0, true)).unwrap(); - assert_eq!(zero_item.0, 0); - assert_eq!(zero_item.1, false); - } - - #[test] - fn eq_works() { - // of same type - let b1 = boundedset_from_keys::>(&[1, 2]); - let b2 = boundedset_from_keys::>(&[1, 2]); - assert_eq!(b1, b2); - - // of different type, but same value and bound. - crate::parameter_types! { - B1: u32 = 7; - B2: u32 = 7; - } - let b1 = boundedset_from_keys::(&[1, 2]); - let b2 = boundedset_from_keys::(&[1, 2]); - assert_eq!(b1, b2); - } - - #[test] - fn can_be_collected() { - let b1 = boundedset_from_keys::>(&[1, 2, 3, 4]); - let b2: BoundedBTreeSet> = b1.iter().map(|k| k + 1).try_collect().unwrap(); - assert_eq!(b2.into_iter().collect::>(), vec![2, 3, 4, 5]); - - // can also be collected into a collection of length 4. - let b2: BoundedBTreeSet> = b1.iter().map(|k| k + 1).try_collect().unwrap(); - assert_eq!(b2.into_iter().collect::>(), vec![2, 3, 4, 5]); - - // can be mutated further into iterators that are `ExactSizedIterator`. - let b2: BoundedBTreeSet> = - b1.iter().map(|k| k + 1).rev().skip(2).try_collect().unwrap(); - // note that the binary tree will re-sort this, so rev() is not really seen - assert_eq!(b2.into_iter().collect::>(), vec![2, 3]); - - let b2: BoundedBTreeSet> = - b1.iter().map(|k| k + 1).take(2).try_collect().unwrap(); - assert_eq!(b2.into_iter().collect::>(), vec![2, 3]); - - // but these worn't work - let b2: Result>, _> = - b1.iter().map(|k| k + 1).try_collect(); - assert!(b2.is_err()); - - let b2: Result>, _> = - b1.iter().map(|k| k + 1).skip(2).try_collect(); - assert!(b2.is_err()); - } -} diff --git a/primitives/core/src/bounded/bounded_vec.rs b/primitives/core/src/bounded/bounded_vec.rs deleted file mode 100644 index 6e1e1c7cf..000000000 --- a/primitives/core/src/bounded/bounded_vec.rs +++ /dev/null @@ -1,1305 +0,0 @@ -// This file is part of Substrate. - -// Copyright (C) 2017-2022 Parity Technologies (UK) Ltd. -// SPDX-License-Identifier: Apache-2.0 - -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -//! Traits, types and structs to support putting a bounded vector into storage, as a raw value, map -//! or a double map. - -use super::WeakBoundedVec; -use crate::{Get, TryCollect}; -use codec::{Decode, Encode, EncodeLike, MaxEncodedLen}; -use core::{ - ops::{Deref, Index, IndexMut, RangeBounds}, - slice::SliceIndex, -}; -#[cfg(feature = "std")] -use serde::{ - de::{Error, SeqAccess, Visitor}, - Deserialize, Deserializer, Serialize, -}; -use sp_std::{marker::PhantomData, prelude::*}; - -/// A bounded vector. -/// -/// It has implementations for efficient append and length decoding, as with a normal `Vec<_>`, once -/// put into storage as a raw value, map or double-map. -/// -/// As the name suggests, the length of the queue is always bounded. All internal operations ensure -/// this bound is respected. -#[cfg_attr(feature = "std", derive(Serialize), serde(transparent))] -#[derive(Encode, scale_info::TypeInfo)] -#[scale_info(skip_type_params(S))] -pub struct BoundedVec( - pub(super) Vec, - #[cfg_attr(feature = "std", serde(skip_serializing))] PhantomData, -); - -/// Create an object through truncation. -pub trait TruncateFrom { - /// Create an object through truncation. - fn truncate_from(unbound: T) -> Self; -} - -#[cfg(feature = "std")] -impl<'de, T, S: Get> Deserialize<'de> for BoundedVec -where - T: Deserialize<'de>, -{ - fn deserialize(deserializer: D) -> Result - where - D: Deserializer<'de>, - { - struct VecVisitor>(PhantomData<(T, S)>); - - impl<'de, T, S: Get> Visitor<'de> for VecVisitor - where - T: Deserialize<'de>, - { - type Value = Vec; - - fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result { - formatter.write_str("a sequence") - } - - fn visit_seq(self, mut seq: A) -> Result - where - A: SeqAccess<'de>, - { - let size = seq.size_hint().unwrap_or(0); - let max = match usize::try_from(S::get()) { - Ok(n) => n, - Err(_) => return Err(A::Error::custom("can't convert to usize")), - }; - if size > max { - Err(A::Error::custom("out of bounds")) - } else { - let mut values = Vec::with_capacity(size); - - while let Some(value) = seq.next_element()? { - values.push(value); - if values.len() > max { - return Err(A::Error::custom("out of bounds")) - } - } - - Ok(values) - } - } - } - - let visitor: VecVisitor = VecVisitor(PhantomData); - deserializer - .deserialize_seq(visitor) - .map(|v| BoundedVec::::try_from(v).map_err(|_| Error::custom("out of bounds")))? - } -} - -/// A bounded slice. -/// -/// Similar to a `BoundedVec`, but not owned and cannot be decoded. -#[derive(Encode)] -pub struct BoundedSlice<'a, T, S>(pub(super) &'a [T], PhantomData); - -// This can be replaced with -// #[derive(scale_info::TypeInfo)] -// #[scale_info(skip_type_params(S))] -// again once this issue is fixed in the rust compiler: https://github.com/rust-lang/rust/issues/96956 -// Tracking issues: https://github.com/paritytech/substrate/issues/11915 -impl<'a, T, S> scale_info::TypeInfo for BoundedSlice<'a, T, S> -where - &'a [T]: scale_info::TypeInfo + 'static, - PhantomData: scale_info::TypeInfo + 'static, - T: scale_info::TypeInfo + 'static, - S: 'static, -{ - type Identity = Self; - - fn type_info() -> ::scale_info::Type { - scale_info::Type::builder() - .path(scale_info::Path::new("BoundedSlice", "sp_runtime::bounded::bounded_vec")) - .type_params(<[_]>::into_vec(Box::new([ - scale_info::TypeParameter::new( - "T", - core::option::Option::Some(::scale_info::meta_type::()), - ), - scale_info::TypeParameter::new("S", ::core::option::Option::None), - ]))) - .docs(&[ - "A bounded slice.", - "", - "Similar to a `BoundedVec`, but not owned and cannot be decoded.", - ]) - .composite( - scale_info::build::Fields::unnamed() - .field(|f| f.ty::<&'static [T]>().type_name("&'static[T]").docs(&[])) - .field(|f| f.ty::>().type_name("PhantomData").docs(&[])), - ) - } -} - -// `BoundedSlice`s encode to something which will always decode into a `BoundedVec`, -// `WeakBoundedVec`, or a `Vec`. -impl<'a, T: Encode + Decode, S: Get> EncodeLike> for BoundedSlice<'a, T, S> {} -impl<'a, T: Encode + Decode, S: Get> EncodeLike> - for BoundedSlice<'a, T, S> -{ -} -impl<'a, T: Encode + Decode, S: Get> EncodeLike> for BoundedSlice<'a, T, S> {} - -impl<'a, T, BoundSelf, BoundRhs> PartialEq> - for BoundedSlice<'a, T, BoundSelf> -where - T: PartialEq, - BoundSelf: Get, - BoundRhs: Get, -{ - fn eq(&self, other: &BoundedSlice<'a, T, BoundRhs>) -> bool { - self.0 == other.0 - } -} - -impl<'a, T, BoundSelf, BoundRhs> PartialEq> - for BoundedSlice<'a, T, BoundSelf> -where - T: PartialEq, - BoundSelf: Get, - BoundRhs: Get, -{ - fn eq(&self, other: &BoundedVec) -> bool { - self.0 == other.0 - } -} - -impl<'a, T, BoundSelf, BoundRhs> PartialEq> - for BoundedSlice<'a, T, BoundSelf> -where - T: PartialEq, - BoundSelf: Get, - BoundRhs: Get, -{ - fn eq(&self, other: &WeakBoundedVec) -> bool { - self.0 == other.0 - } -} - -impl<'a, T, S: Get> Eq for BoundedSlice<'a, T, S> where T: Eq {} - -impl<'a, T, BoundSelf, BoundRhs> PartialOrd> - for BoundedSlice<'a, T, BoundSelf> -where - T: PartialOrd, - BoundSelf: Get, - BoundRhs: Get, -{ - fn partial_cmp(&self, other: &BoundedSlice<'a, T, BoundRhs>) -> Option { - self.0.partial_cmp(other.0) - } -} - -impl<'a, T, BoundSelf, BoundRhs> PartialOrd> - for BoundedSlice<'a, T, BoundSelf> -where - T: PartialOrd, - BoundSelf: Get, - BoundRhs: Get, -{ - fn partial_cmp(&self, other: &BoundedVec) -> Option { - self.0.partial_cmp(&*other.0) - } -} - -impl<'a, T, BoundSelf, BoundRhs> PartialOrd> - for BoundedSlice<'a, T, BoundSelf> -where - T: PartialOrd, - BoundSelf: Get, - BoundRhs: Get, -{ - fn partial_cmp(&self, other: &WeakBoundedVec) -> Option { - self.0.partial_cmp(&*other.0) - } -} - -impl<'a, T: Ord, Bound: Get> Ord for BoundedSlice<'a, T, Bound> { - fn cmp(&self, other: &Self) -> sp_std::cmp::Ordering { - self.0.cmp(&other.0) - } -} - -impl<'a, T, S: Get> TryFrom<&'a [T]> for BoundedSlice<'a, T, S> { - type Error = &'a [T]; - fn try_from(t: &'a [T]) -> Result { - if t.len() <= S::get() as usize { - Ok(BoundedSlice(t, PhantomData)) - } else { - Err(t) - } - } -} - -impl<'a, T, S> From> for &'a [T] { - fn from(t: BoundedSlice<'a, T, S>) -> Self { - t.0 - } -} - -impl<'a, T, S: Get> TruncateFrom<&'a [T]> for BoundedSlice<'a, T, S> { - fn truncate_from(unbound: &'a [T]) -> Self { - BoundedSlice::::truncate_from(unbound) - } -} - -impl<'a, T, S> Clone for BoundedSlice<'a, T, S> { - fn clone(&self) -> Self { - BoundedSlice(self.0, PhantomData) - } -} - -impl<'a, T, S> sp_std::fmt::Debug for BoundedSlice<'a, T, S> -where - &'a [T]: sp_std::fmt::Debug, - S: Get, -{ - fn fmt(&self, f: &mut sp_std::fmt::Formatter<'_>) -> sp_std::fmt::Result { - f.debug_tuple("BoundedSlice").field(&self.0).field(&S::get()).finish() - } -} - -// Since a reference `&T` is always `Copy`, so is `BoundedSlice<'a, T, S>`. -impl<'a, T, S> Copy for BoundedSlice<'a, T, S> {} - -// will allow for all immutable operations of `[T]` on `BoundedSlice`. -impl<'a, T, S> Deref for BoundedSlice<'a, T, S> { - type Target = [T]; - - fn deref(&self) -> &Self::Target { - self.0 - } -} - -impl<'a, T, S> sp_std::iter::IntoIterator for BoundedSlice<'a, T, S> { - type Item = &'a T; - type IntoIter = sp_std::slice::Iter<'a, T>; - fn into_iter(self) -> Self::IntoIter { - self.0.iter() - } -} - -impl<'a, T, S: Get> BoundedSlice<'a, T, S> { - /// Create an instance from the first elements of the given slice (or all of it if it is smaller - /// than the length bound). - pub fn truncate_from(s: &'a [T]) -> Self { - Self(&s[0..(s.len().min(S::get() as usize))], PhantomData) - } -} - -impl> Decode for BoundedVec { - fn decode(input: &mut I) -> Result { - let inner = Vec::::decode(input)?; - if inner.len() > S::get() as usize { - return Err("BoundedVec exceeds its limit".into()) - } - Ok(Self(inner, PhantomData)) - } - - fn skip(input: &mut I) -> Result<(), codec::Error> { - Vec::::skip(input) - } -} - -// `BoundedVec`s encode to something which will always decode as a `Vec`. -impl> EncodeLike> for BoundedVec {} - -impl BoundedVec { - /// Create `Self` from `t` without any checks. - fn unchecked_from(t: Vec) -> Self { - Self(t, Default::default()) - } - - /// Consume self, and return the inner `Vec`. Henceforth, the `Vec<_>` can be altered in an - /// arbitrary way. At some point, if the reverse conversion is required, `TryFrom>` can - /// be used. - /// - /// This is useful for cases if you need access to an internal API of the inner `Vec<_>` which - /// is not provided by the wrapper `BoundedVec`. - pub fn into_inner(self) -> Vec { - self.0 - } - - /// Exactly the same semantics as [`slice::sort_by`]. - /// - /// This is safe since sorting cannot change the number of elements in the vector. - pub fn sort_by(&mut self, compare: F) - where - F: FnMut(&T, &T) -> sp_std::cmp::Ordering, - { - self.0.sort_by(compare) - } - - /// Exactly the same semantics as [`slice::sort_by_key`]. - /// - /// This is safe since sorting cannot change the number of elements in the vector. - pub fn sort_by_key(&mut self, f: F) - where - F: FnMut(&T) -> K, - K: sp_std::cmp::Ord, - { - self.0.sort_by_key(f) - } - - /// Exactly the same semantics as [`slice::sort`]. - /// - /// This is safe since sorting cannot change the number of elements in the vector. - pub fn sort(&mut self) - where - T: sp_std::cmp::Ord, - { - self.0.sort() - } - - /// Exactly the same semantics as `Vec::remove`. - /// - /// # Panics - /// - /// Panics if `index` is out of bounds. - pub fn remove(&mut self, index: usize) -> T { - self.0.remove(index) - } - - /// Exactly the same semantics as `slice::swap_remove`. - /// - /// # Panics - /// - /// Panics if `index` is out of bounds. - pub fn swap_remove(&mut self, index: usize) -> T { - self.0.swap_remove(index) - } - - /// Exactly the same semantics as `Vec::retain`. - pub fn retain bool>(&mut self, f: F) { - self.0.retain(f) - } - - /// Exactly the same semantics as `slice::get_mut`. - pub fn get_mut>( - &mut self, - index: I, - ) -> Option<&mut >::Output> { - self.0.get_mut(index) - } - - /// Exactly the same semantics as `Vec::truncate`. - /// - /// This is safe because `truncate` can never increase the length of the internal vector. - pub fn truncate(&mut self, s: usize) { - self.0.truncate(s); - } - - /// Exactly the same semantics as `Vec::pop`. - /// - /// This is safe since popping can only shrink the inner vector. - pub fn pop(&mut self) -> Option { - self.0.pop() - } - - /// Exactly the same semantics as [`slice::iter_mut`]. - pub fn iter_mut(&mut self) -> core::slice::IterMut<'_, T> { - self.0.iter_mut() - } - - /// Exactly the same semantics as [`slice::last_mut`]. - pub fn last_mut(&mut self) -> Option<&mut T> { - self.0.last_mut() - } - - /// Exact same semantics as [`Vec::drain`]. - pub fn drain(&mut self, range: R) -> sp_std::vec::Drain<'_, T> - where - R: RangeBounds, - { - self.0.drain(range) - } -} - -impl> From> for Vec { - fn from(x: BoundedVec) -> Vec { - x.0 - } -} - -impl> BoundedVec { - /// Pre-allocate `capacity` items in self. - /// - /// If `capacity` is greater than [`Self::bound`], then the minimum of the two is used. - pub fn with_bounded_capacity(capacity: usize) -> Self { - let capacity = capacity.min(Self::bound()); - Self(Vec::with_capacity(capacity), Default::default()) - } - - /// Allocate self with the maximum possible capacity. - pub fn with_max_capacity() -> Self { - Self::with_bounded_capacity(Self::bound()) - } - - /// Consume and truncate the vector `v` in order to create a new instance of `Self` from it. - pub fn truncate_from(mut v: Vec) -> Self { - v.truncate(Self::bound()); - Self::unchecked_from(v) - } - - /// Get the bound of the type in `usize`. - pub fn bound() -> usize { - S::get() as usize - } - - /// Returns true of this collection is full. - pub fn is_full(&self) -> bool { - self.len() >= Self::bound() - } - - /// Forces the insertion of `element` into `self` retaining all items with index at least - /// `index`. - /// - /// If `index == 0` and `self.len() == Self::bound()`, then this is a no-op. - /// - /// If `Self::bound() < index` or `self.len() < index`, then this is also a no-op. - /// - /// Returns `Ok(maybe_removed)` if the item was inserted, where `maybe_removed` is - /// `Some(removed)` if an item was removed to make room for the new one. Returns `Err(())` if - /// `element` cannot be inserted. - pub fn force_insert_keep_right( - &mut self, - index: usize, - mut element: T, - ) -> Result, ()> { - // Check against panics. - if Self::bound() < index || self.len() < index { - Err(()) - } else if self.len() < Self::bound() { - // Cannot panic since self.len() >= index; - self.0.insert(index, element); - Ok(None) - } else { - if index == 0 { - return Err(()) - } - sp_std::mem::swap(&mut self[0], &mut element); - // `[0..index] cannot panic since self.len() >= index. - // `rotate_left(1)` cannot panic because there is at least 1 element. - self[0..index].rotate_left(1); - Ok(Some(element)) - } - } - - /// Forces the insertion of `element` into `self` retaining all items with index at most - /// `index`. - /// - /// If `index == Self::bound()` and `self.len() == Self::bound()`, then this is a no-op. - /// - /// If `Self::bound() < index` or `self.len() < index`, then this is also a no-op. - /// - /// Returns `Ok(maybe_removed)` if the item was inserted, where `maybe_removed` is - /// `Some(removed)` if an item was removed to make room for the new one. Returns `Err(())` if - /// `element` cannot be inserted. - pub fn force_insert_keep_left(&mut self, index: usize, element: T) -> Result, ()> { - // Check against panics. - if Self::bound() < index || self.len() < index || Self::bound() == 0 { - return Err(()) - } - // Noop condition. - if Self::bound() == index && self.len() <= Self::bound() { - return Err(()) - } - let maybe_removed = if self.is_full() { - // defensive-only: since we are at capacity, this is a noop. - self.0.truncate(Self::bound()); - // if we truncate anything, it will be the last one. - self.0.pop() - } else { - None - }; - - // Cannot panic since `self.len() >= index`; - self.0.insert(index, element); - Ok(maybe_removed) - } - - /// Move the position of an item from one location to another in the slice. - /// - /// Except for the item being moved, the order of the slice remains the same. - /// - /// - `index` is the location of the item to be moved. - /// - `insert_position` is the index of the item in the slice which should *immediately follow* - /// the item which is being moved. - /// - /// Returns `true` of the operation was successful, otherwise `false` if a noop. - pub fn slide(&mut self, index: usize, insert_position: usize) -> bool { - // Check against panics. - if self.len() <= index || self.len() < insert_position || index == usize::MAX { - return false - } - // Noop conditions. - if index == insert_position || index + 1 == insert_position { - return false - } - if insert_position < index && index < self.len() { - // --- --- --- === === === === @@@ --- --- --- - // ^-- N ^O^ - // ... - // /-----<<<-----\ - // --- --- --- === === === === @@@ --- --- --- - // >>> >>> >>> >>> - // ... - // --- --- --- @@@ === === === === --- --- --- - // ^N^ - self[insert_position..index + 1].rotate_right(1); - return true - } else if insert_position > 0 && index + 1 < insert_position { - // Note that the apparent asymmetry of these two branches is due to the - // fact that the "new" position is the position to be inserted *before*. - // --- --- --- @@@ === === === === --- --- --- - // ^O^ ^-- N - // ... - // /----->>>-----\ - // --- --- --- @@@ === === === === --- --- --- - // <<< <<< <<< <<< - // ... - // --- --- --- === === === === @@@ --- --- --- - // ^N^ - self[index..insert_position].rotate_left(1); - return true - } - - debug_assert!(false, "all noop conditions should have been covered above"); - false - } - - /// Forces the insertion of `s` into `self` truncating first if necessary. - /// - /// Infallible, but if the bound is zero, then it's a no-op. - pub fn force_push(&mut self, element: T) { - if Self::bound() > 0 { - self.0.truncate(Self::bound() as usize - 1); - self.0.push(element); - } - } - - /// Same as `Vec::resize`, but if `size` is more than [`Self::bound`], then [`Self::bound`] is - /// used. - pub fn bounded_resize(&mut self, size: usize, value: T) - where - T: Clone, - { - let size = size.min(Self::bound()); - self.0.resize(size, value); - } - - /// Exactly the same semantics as [`Vec::extend`], but returns an error and does nothing if the - /// length of the outcome is larger than the bound. - pub fn try_extend( - &mut self, - with: impl IntoIterator + ExactSizeIterator, - ) -> Result<(), ()> { - if with.len().saturating_add(self.len()) <= Self::bound() { - self.0.extend(with); - Ok(()) - } else { - Err(()) - } - } - - /// Exactly the same semantics as [`Vec::append`], but returns an error and does nothing if the - /// length of the outcome is larger than the bound. - pub fn try_append(&mut self, other: &mut Vec) -> Result<(), ()> { - if other.len().saturating_add(self.len()) <= Self::bound() { - self.0.append(other); - Ok(()) - } else { - Err(()) - } - } - - /// Consumes self and mutates self via the given `mutate` function. - /// - /// If the outcome of mutation is within bounds, `Some(Self)` is returned. Else, `None` is - /// returned. - /// - /// This is essentially a *consuming* shorthand [`Self::into_inner`] -> `...` -> - /// [`Self::try_from`]. - pub fn try_mutate(mut self, mut mutate: impl FnMut(&mut Vec)) -> Option { - mutate(&mut self.0); - (self.0.len() <= Self::bound()).then(move || self) - } - - /// Exactly the same semantics as [`Vec::insert`], but returns an `Err` (and is a noop) if the - /// new length of the vector exceeds `S`. - /// - /// # Panics - /// - /// Panics if `index > len`. - pub fn try_insert(&mut self, index: usize, element: T) -> Result<(), T> { - if self.len() < Self::bound() { - self.0.insert(index, element); - Ok(()) - } else { - Err(element) - } - } - - /// Exactly the same semantics as [`Vec::push`], but returns an `Err` (and is a noop) if the - /// new length of the vector exceeds `S`. - /// - /// # Panics - /// - /// Panics if the new capacity exceeds isize::MAX bytes. - pub fn try_push(&mut self, element: T) -> Result<(), T> { - if self.len() < Self::bound() { - self.0.push(element); - Ok(()) - } else { - Err(element) - } - } -} - -impl BoundedVec { - /// Return a [`BoundedSlice`] with the content and bound of [`Self`]. - pub fn as_bounded_slice(&self) -> BoundedSlice { - BoundedSlice(&self.0[..], PhantomData::default()) - } -} - -impl Default for BoundedVec { - fn default() -> Self { - // the bound cannot be below 0, which is satisfied by an empty vector - Self::unchecked_from(Vec::default()) - } -} - -impl sp_std::fmt::Debug for BoundedVec -where - Vec: sp_std::fmt::Debug, - S: Get, -{ - fn fmt(&self, f: &mut sp_std::fmt::Formatter<'_>) -> sp_std::fmt::Result { - f.debug_tuple("BoundedVec").field(&self.0).field(&Self::bound()).finish() - } -} - -impl Clone for BoundedVec -where - T: Clone, -{ - fn clone(&self) -> Self { - // bound is retained - Self::unchecked_from(self.0.clone()) - } -} - -impl> TryFrom> for BoundedVec { - type Error = Vec; - fn try_from(t: Vec) -> Result { - if t.len() <= Self::bound() { - // explicit check just above - Ok(Self::unchecked_from(t)) - } else { - Err(t) - } - } -} - -impl> TruncateFrom> for BoundedVec { - fn truncate_from(unbound: Vec) -> Self { - BoundedVec::::truncate_from(unbound) - } -} - -// It is okay to give a non-mutable reference of the inner vec to anyone. -impl AsRef> for BoundedVec { - fn as_ref(&self) -> &Vec { - &self.0 - } -} - -impl AsRef<[T]> for BoundedVec { - fn as_ref(&self) -> &[T] { - &self.0 - } -} - -impl AsMut<[T]> for BoundedVec { - fn as_mut(&mut self) -> &mut [T] { - &mut self.0 - } -} - -// will allow for all immutable operations of `Vec` on `BoundedVec`. -impl Deref for BoundedVec { - type Target = Vec; - - fn deref(&self) -> &Self::Target { - &self.0 - } -} - -// Allows for indexing similar to a normal `Vec`. Can panic if out of bound. -impl Index for BoundedVec -where - I: SliceIndex<[T]>, -{ - type Output = I::Output; - - #[inline] - fn index(&self, index: I) -> &Self::Output { - self.0.index(index) - } -} - -impl IndexMut for BoundedVec -where - I: SliceIndex<[T]>, -{ - #[inline] - fn index_mut(&mut self, index: I) -> &mut Self::Output { - self.0.index_mut(index) - } -} - -impl sp_std::iter::IntoIterator for BoundedVec { - type Item = T; - type IntoIter = sp_std::vec::IntoIter; - fn into_iter(self) -> Self::IntoIter { - self.0.into_iter() - } -} - -impl<'a, T, S> sp_std::iter::IntoIterator for &'a BoundedVec { - type Item = &'a T; - type IntoIter = sp_std::slice::Iter<'a, T>; - fn into_iter(self) -> Self::IntoIter { - self.0.iter() - } -} - -impl<'a, T, S> sp_std::iter::IntoIterator for &'a mut BoundedVec { - type Item = &'a mut T; - type IntoIter = sp_std::slice::IterMut<'a, T>; - fn into_iter(self) -> Self::IntoIter { - self.0.iter_mut() - } -} - -impl codec::DecodeLength for BoundedVec { - fn len(self_encoded: &[u8]) -> Result { - // `BoundedVec` stored just a `Vec`, thus the length is at the beginning in - // `Compact` form, and same implementation as `Vec` can be used. - as codec::DecodeLength>::len(self_encoded) - } -} - -impl PartialEq> for BoundedVec -where - T: PartialEq, - BoundSelf: Get, - BoundRhs: Get, -{ - fn eq(&self, rhs: &BoundedVec) -> bool { - self.0 == rhs.0 - } -} - -impl PartialEq> for BoundedVec -where - T: PartialEq, - BoundSelf: Get, - BoundRhs: Get, -{ - fn eq(&self, rhs: &WeakBoundedVec) -> bool { - self.0 == rhs.0 - } -} - -impl<'a, T, BoundSelf, BoundRhs> PartialEq> - for BoundedVec -where - T: PartialEq, - BoundSelf: Get, - BoundRhs: Get, -{ - fn eq(&self, rhs: &BoundedSlice<'a, T, BoundRhs>) -> bool { - self.0 == rhs.0 - } -} - -impl<'a, T: PartialEq, S: Get> PartialEq<&'a [T]> for BoundedSlice<'a, T, S> { - fn eq(&self, other: &&'a [T]) -> bool { - &self.0 == other - } -} - -impl> PartialEq> for BoundedVec { - fn eq(&self, other: &Vec) -> bool { - &self.0 == other - } -} - -impl> Eq for BoundedVec where T: Eq {} - -impl PartialOrd> for BoundedVec -where - T: PartialOrd, - BoundSelf: Get, - BoundRhs: Get, -{ - fn partial_cmp(&self, other: &BoundedVec) -> Option { - self.0.partial_cmp(&other.0) - } -} - -impl PartialOrd> for BoundedVec -where - T: PartialOrd, - BoundSelf: Get, - BoundRhs: Get, -{ - fn partial_cmp(&self, other: &WeakBoundedVec) -> Option { - self.0.partial_cmp(&other.0) - } -} - -impl<'a, T, BoundSelf, BoundRhs> PartialOrd> - for BoundedVec -where - T: PartialOrd, - BoundSelf: Get, - BoundRhs: Get, -{ - fn partial_cmp(&self, other: &BoundedSlice<'a, T, BoundRhs>) -> Option { - (&*self.0).partial_cmp(other.0) - } -} - -impl> Ord for BoundedVec { - fn cmp(&self, other: &Self) -> sp_std::cmp::Ordering { - self.0.cmp(&other.0) - } -} - -impl MaxEncodedLen for BoundedVec -where - T: MaxEncodedLen, - S: Get, - BoundedVec: Encode, -{ - fn max_encoded_len() -> usize { - // BoundedVec encodes like Vec which encodes like [T], which is a compact u32 - // plus each item in the slice: - // See: https://docs.substrate.io/reference/scale-codec/ - codec::Compact(S::get()) - .encoded_size() - .saturating_add(Self::bound().saturating_mul(T::max_encoded_len())) - } -} - -impl TryCollect> for I -where - I: ExactSizeIterator + Iterator, - Bound: Get, -{ - type Error = &'static str; - - fn try_collect(self) -> Result, Self::Error> { - if self.len() > Bound::get() as usize { - Err("iterator length too big") - } else { - Ok(BoundedVec::::unchecked_from(self.collect::>())) - } - } -} - -#[cfg(test)] -pub mod test { - use super::*; - use crate::{bounded_vec, ConstU32}; - - #[test] - fn slice_truncate_from_works() { - let bounded = BoundedSlice::>::truncate_from(&[1, 2, 3, 4, 5]); - assert_eq!(bounded.deref(), &[1, 2, 3, 4]); - let bounded = BoundedSlice::>::truncate_from(&[1, 2, 3, 4]); - assert_eq!(bounded.deref(), &[1, 2, 3, 4]); - let bounded = BoundedSlice::>::truncate_from(&[1, 2, 3]); - assert_eq!(bounded.deref(), &[1, 2, 3]); - } - - #[test] - fn slide_works() { - let mut b: BoundedVec> = bounded_vec![0, 1, 2, 3, 4, 5]; - assert!(b.slide(1, 5)); - assert_eq!(*b, vec![0, 2, 3, 4, 1, 5]); - assert!(b.slide(4, 0)); - assert_eq!(*b, vec![1, 0, 2, 3, 4, 5]); - assert!(b.slide(0, 2)); - assert_eq!(*b, vec![0, 1, 2, 3, 4, 5]); - assert!(b.slide(1, 6)); - assert_eq!(*b, vec![0, 2, 3, 4, 5, 1]); - assert!(b.slide(0, 6)); - assert_eq!(*b, vec![2, 3, 4, 5, 1, 0]); - assert!(b.slide(5, 0)); - assert_eq!(*b, vec![0, 2, 3, 4, 5, 1]); - assert!(!b.slide(6, 0)); - assert!(!b.slide(7, 0)); - assert_eq!(*b, vec![0, 2, 3, 4, 5, 1]); - - let mut c: BoundedVec> = bounded_vec![0, 1, 2]; - assert!(!c.slide(1, 5)); - assert_eq!(*c, vec![0, 1, 2]); - assert!(!c.slide(4, 0)); - assert_eq!(*c, vec![0, 1, 2]); - assert!(!c.slide(3, 0)); - assert_eq!(*c, vec![0, 1, 2]); - assert!(c.slide(2, 0)); - assert_eq!(*c, vec![2, 0, 1]); - } - - #[test] - fn slide_noops_work() { - let mut b: BoundedVec> = bounded_vec![0, 1, 2, 3, 4, 5]; - assert!(!b.slide(3, 3)); - assert_eq!(*b, vec![0, 1, 2, 3, 4, 5]); - assert!(!b.slide(3, 4)); - assert_eq!(*b, vec![0, 1, 2, 3, 4, 5]); - } - - #[test] - fn force_insert_keep_left_works() { - let mut b: BoundedVec> = bounded_vec![]; - assert_eq!(b.force_insert_keep_left(1, 10), Err(())); - assert!(b.is_empty()); - - assert_eq!(b.force_insert_keep_left(0, 30), Ok(None)); - assert_eq!(b.force_insert_keep_left(0, 10), Ok(None)); - assert_eq!(b.force_insert_keep_left(1, 20), Ok(None)); - assert_eq!(b.force_insert_keep_left(3, 40), Ok(None)); - assert_eq!(*b, vec![10, 20, 30, 40]); - // at capacity. - assert_eq!(b.force_insert_keep_left(4, 41), Err(())); - assert_eq!(*b, vec![10, 20, 30, 40]); - assert_eq!(b.force_insert_keep_left(3, 31), Ok(Some(40))); - assert_eq!(*b, vec![10, 20, 30, 31]); - assert_eq!(b.force_insert_keep_left(1, 11), Ok(Some(31))); - assert_eq!(*b, vec![10, 11, 20, 30]); - assert_eq!(b.force_insert_keep_left(0, 1), Ok(Some(30))); - assert_eq!(*b, vec![1, 10, 11, 20]); - - let mut z: BoundedVec> = bounded_vec![]; - assert!(z.is_empty()); - assert_eq!(z.force_insert_keep_left(0, 10), Err(())); - assert!(z.is_empty()); - } - - #[test] - fn force_insert_keep_right_works() { - let mut b: BoundedVec> = bounded_vec![]; - assert_eq!(b.force_insert_keep_right(1, 10), Err(())); - assert!(b.is_empty()); - - assert_eq!(b.force_insert_keep_right(0, 30), Ok(None)); - assert_eq!(b.force_insert_keep_right(0, 10), Ok(None)); - assert_eq!(b.force_insert_keep_right(1, 20), Ok(None)); - assert_eq!(b.force_insert_keep_right(3, 40), Ok(None)); - assert_eq!(*b, vec![10, 20, 30, 40]); - - // at capacity. - assert_eq!(b.force_insert_keep_right(0, 0), Err(())); - assert_eq!(*b, vec![10, 20, 30, 40]); - assert_eq!(b.force_insert_keep_right(1, 11), Ok(Some(10))); - assert_eq!(*b, vec![11, 20, 30, 40]); - assert_eq!(b.force_insert_keep_right(3, 31), Ok(Some(11))); - assert_eq!(*b, vec![20, 30, 31, 40]); - assert_eq!(b.force_insert_keep_right(4, 41), Ok(Some(20))); - assert_eq!(*b, vec![30, 31, 40, 41]); - - assert_eq!(b.force_insert_keep_right(5, 69), Err(())); - assert_eq!(*b, vec![30, 31, 40, 41]); - - let mut z: BoundedVec> = bounded_vec![]; - assert!(z.is_empty()); - assert_eq!(z.force_insert_keep_right(0, 10), Err(())); - assert!(z.is_empty()); - } - - #[test] - fn bound_returns_correct_value() { - assert_eq!(BoundedVec::>::bound(), 7); - } - - #[test] - fn try_insert_works() { - let mut bounded: BoundedVec> = bounded_vec![1, 2, 3]; - bounded.try_insert(1, 0).unwrap(); - assert_eq!(*bounded, vec![1, 0, 2, 3]); - - assert!(bounded.try_insert(0, 9).is_err()); - assert_eq!(*bounded, vec![1, 0, 2, 3]); - } - - #[test] - fn constructor_macro_works() { - // With values. Use some brackets to make sure the macro doesn't expand. - let bv: BoundedVec<(u32, u32), ConstU32<3>> = bounded_vec![(1, 2), (1, 2), (1, 2)]; - assert_eq!(bv, vec![(1, 2), (1, 2), (1, 2)]); - - // With repetition. - let bv: BoundedVec<(u32, u32), ConstU32<3>> = bounded_vec![(1, 2); 3]; - assert_eq!(bv, vec![(1, 2), (1, 2), (1, 2)]); - } - - #[test] - #[should_panic(expected = "insertion index (is 9) should be <= len (is 3)")] - fn try_inert_panics_if_oob() { - let mut bounded: BoundedVec> = bounded_vec![1, 2, 3]; - bounded.try_insert(9, 0).unwrap(); - } - - #[test] - fn try_push_works() { - let mut bounded: BoundedVec> = bounded_vec![1, 2, 3]; - bounded.try_push(0).unwrap(); - assert_eq!(*bounded, vec![1, 2, 3, 0]); - - assert!(bounded.try_push(9).is_err()); - } - - #[test] - fn deref_vec_coercion_works() { - let bounded: BoundedVec> = bounded_vec![1, 2, 3]; - // these methods come from deref-ed vec. - assert_eq!(bounded.len(), 3); - assert!(bounded.iter().next().is_some()); - assert!(!bounded.is_empty()); - } - - #[test] - fn deref_slice_coercion_works() { - let bounded = BoundedSlice::>::try_from(&[1, 2, 3][..]).unwrap(); - // these methods come from deref-ed slice. - assert_eq!(bounded.len(), 3); - assert!(bounded.iter().next().is_some()); - assert!(!bounded.is_empty()); - } - - #[test] - fn try_mutate_works() { - let bounded: BoundedVec> = bounded_vec![1, 2, 3, 4, 5, 6]; - let bounded = bounded.try_mutate(|v| v.push(7)).unwrap(); - assert_eq!(bounded.len(), 7); - assert!(bounded.try_mutate(|v| v.push(8)).is_none()); - } - - #[test] - fn slice_indexing_works() { - let bounded: BoundedVec> = bounded_vec![1, 2, 3, 4, 5, 6]; - assert_eq!(&bounded[0..=2], &[1, 2, 3]); - } - - #[test] - fn vec_eq_works() { - let bounded: BoundedVec> = bounded_vec![1, 2, 3, 4, 5, 6]; - assert_eq!(bounded, vec![1, 2, 3, 4, 5, 6]); - } - - #[test] - fn too_big_vec_fail_to_decode() { - let v: Vec = vec![1, 2, 3, 4, 5]; - assert_eq!( - BoundedVec::>::decode(&mut &v.encode()[..]), - Err("BoundedVec exceeds its limit".into()), - ); - } - - #[test] - fn eq_works() { - // of same type - let b1: BoundedVec> = bounded_vec![1, 2, 3]; - let b2: BoundedVec> = bounded_vec![1, 2, 3]; - assert_eq!(b1, b2); - - // of different type, but same value and bound. - crate::parameter_types! { - B1: u32 = 7; - B2: u32 = 7; - } - let b1: BoundedVec = bounded_vec![1, 2, 3]; - let b2: BoundedVec = bounded_vec![1, 2, 3]; - assert_eq!(b1, b2); - } - - #[test] - fn ord_works() { - use std::cmp::Ordering; - let b1: BoundedVec> = bounded_vec![1, 2, 3]; - let b2: BoundedVec> = bounded_vec![1, 3, 2]; - - // ordering for vec is lexicographic. - assert_eq!(b1.cmp(&b2), Ordering::Less); - assert_eq!(b1.cmp(&b2), b1.into_inner().cmp(&b2.into_inner())); - } - - #[test] - fn try_extend_works() { - let mut b: BoundedVec> = bounded_vec![1, 2, 3]; - - assert!(b.try_extend(vec![4].into_iter()).is_ok()); - assert_eq!(*b, vec![1, 2, 3, 4]); - - assert!(b.try_extend(vec![5].into_iter()).is_ok()); - assert_eq!(*b, vec![1, 2, 3, 4, 5]); - - assert!(b.try_extend(vec![6].into_iter()).is_err()); - assert_eq!(*b, vec![1, 2, 3, 4, 5]); - - let mut b: BoundedVec> = bounded_vec![1, 2, 3]; - assert!(b.try_extend(vec![4, 5].into_iter()).is_ok()); - assert_eq!(*b, vec![1, 2, 3, 4, 5]); - - let mut b: BoundedVec> = bounded_vec![1, 2, 3]; - assert!(b.try_extend(vec![4, 5, 6].into_iter()).is_err()); - assert_eq!(*b, vec![1, 2, 3]); - } - - #[test] - fn test_serializer() { - let c: BoundedVec> = bounded_vec![0, 1, 2]; - assert_eq!(serde_json::json!(&c).to_string(), r#"[0,1,2]"#); - } - - #[test] - fn test_deserializer() { - let c: BoundedVec> = serde_json::from_str(r#"[0,1,2]"#).unwrap(); - - assert_eq!(c.len(), 3); - assert_eq!(c[0], 0); - assert_eq!(c[1], 1); - assert_eq!(c[2], 2); - } - - #[test] - fn test_deserializer_failed() { - let c: Result>, serde_json::error::Error> = - serde_json::from_str(r#"[0,1,2,3,4,5]"#); - - match c { - Err(msg) => assert_eq!(msg.to_string(), "out of bounds at line 1 column 11"), - _ => unreachable!("deserializer must raise error"), - } - } - - #[test] - fn bounded_vec_try_from_works() { - assert!(BoundedVec::>::try_from(vec![0]).is_ok()); - assert!(BoundedVec::>::try_from(vec![0, 1]).is_ok()); - assert!(BoundedVec::>::try_from(vec![0, 1, 2]).is_err()); - } - - #[test] - fn bounded_slice_try_from_works() { - assert!(BoundedSlice::>::try_from(&[0][..]).is_ok()); - assert!(BoundedSlice::>::try_from(&[0, 1][..]).is_ok()); - assert!(BoundedSlice::>::try_from(&[0, 1, 2][..]).is_err()); - } - - #[test] - fn can_be_collected() { - let b1: BoundedVec> = bounded_vec![1, 2, 3, 4]; - let b2: BoundedVec> = b1.iter().map(|x| x + 1).try_collect().unwrap(); - assert_eq!(b2, vec![2, 3, 4, 5]); - - // can also be collected into a collection of length 4. - let b2: BoundedVec> = b1.iter().map(|x| x + 1).try_collect().unwrap(); - assert_eq!(b2, vec![2, 3, 4, 5]); - - // can be mutated further into iterators that are `ExactSizedIterator`. - let b2: BoundedVec> = - b1.iter().map(|x| x + 1).rev().try_collect().unwrap(); - assert_eq!(b2, vec![5, 4, 3, 2]); - - let b2: BoundedVec> = - b1.iter().map(|x| x + 1).rev().skip(2).try_collect().unwrap(); - assert_eq!(b2, vec![3, 2]); - let b2: BoundedVec> = - b1.iter().map(|x| x + 1).rev().skip(2).try_collect().unwrap(); - assert_eq!(b2, vec![3, 2]); - - let b2: BoundedVec> = - b1.iter().map(|x| x + 1).rev().take(2).try_collect().unwrap(); - assert_eq!(b2, vec![5, 4]); - let b2: BoundedVec> = - b1.iter().map(|x| x + 1).rev().take(2).try_collect().unwrap(); - assert_eq!(b2, vec![5, 4]); - - // but these worn't work - let b2: Result>, _> = b1.iter().map(|x| x + 1).try_collect(); - assert!(b2.is_err()); - - let b2: Result>, _> = - b1.iter().map(|x| x + 1).rev().take(2).try_collect(); - assert!(b2.is_err()); - } - - #[test] - fn bounded_vec_debug_works() { - let bound = BoundedVec::>::truncate_from(vec![1, 2, 3]); - assert_eq!(format!("{:?}", bound), "BoundedVec([1, 2, 3], 5)"); - } - - #[test] - fn bounded_slice_debug_works() { - let bound = BoundedSlice::>::truncate_from(&[1, 2, 3]); - assert_eq!(format!("{:?}", bound), "BoundedSlice([1, 2, 3], 5)"); - } - - #[test] - fn bounded_vec_sort_by_key_works() { - let mut v: BoundedVec> = bounded_vec![-5, 4, 1, -3, 2]; - // Sort by absolute value. - v.sort_by_key(|k| k.abs()); - assert_eq!(v, vec![1, 2, -3, 4, -5]); - } - - #[test] - fn bounded_vec_truncate_from_works() { - let unbound = vec![1, 2, 3, 4, 5]; - let bound = BoundedVec::>::truncate_from(unbound.clone()); - assert_eq!(bound, vec![1, 2, 3]); - } - - #[test] - fn bounded_slice_truncate_from_works() { - let unbound = [1, 2, 3, 4, 5]; - let bound = BoundedSlice::>::truncate_from(&unbound); - assert_eq!(bound, &[1, 2, 3][..]); - } - - #[test] - fn bounded_slice_partialeq_slice_works() { - let unbound = [1, 2, 3]; - let bound = BoundedSlice::>::truncate_from(&unbound); - - assert_eq!(bound, &unbound[..]); - assert!(bound == &unbound[..]); - } -} diff --git a/primitives/core/src/bounded/weak_bounded_vec.rs b/primitives/core/src/bounded/weak_bounded_vec.rs deleted file mode 100644 index 5aff35f01..000000000 --- a/primitives/core/src/bounded/weak_bounded_vec.rs +++ /dev/null @@ -1,524 +0,0 @@ -// This file is part of Substrate. - -// Copyright (C) 2017-2022 Parity Technologies (UK) Ltd. -// SPDX-License-Identifier: Apache-2.0 - -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -//! Traits, types and structs to support putting a bounded vector into storage, as a raw value, map -//! or a double map. - -use super::{BoundedSlice, BoundedVec}; -use crate::Get; -use codec::{Decode, Encode, MaxEncodedLen}; -use core::{ - ops::{Deref, Index, IndexMut}, - slice::SliceIndex, -}; -#[cfg(feature = "std")] -use serde::{ - de::{Error, SeqAccess, Visitor}, - Deserialize, Deserializer, Serialize, -}; -use sp_std::{marker::PhantomData, prelude::*}; - -/// A weakly bounded vector. -/// -/// It has implementations for efficient append and length decoding, as with a normal `Vec<_>`, once -/// put into storage as a raw value, map or double-map. -/// -/// The length of the vec is not strictly bounded. Decoding a vec with more element that the bound -/// is accepted, and some method allow to bypass the restriction with warnings. -#[cfg_attr(feature = "std", derive(Serialize), serde(transparent))] -#[derive(Encode, scale_info::TypeInfo)] -#[scale_info(skip_type_params(S))] -pub struct WeakBoundedVec( - pub(super) Vec, - #[cfg_attr(feature = "std", serde(skip_serializing))] PhantomData, -); - -#[cfg(feature = "std")] -impl<'de, T, S: Get> Deserialize<'de> for WeakBoundedVec -where - T: Deserialize<'de>, -{ - fn deserialize(deserializer: D) -> Result - where - D: Deserializer<'de>, - { - struct VecVisitor>(PhantomData<(T, S)>); - - impl<'de, T, S: Get> Visitor<'de> for VecVisitor - where - T: Deserialize<'de>, - { - type Value = Vec; - - fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result { - formatter.write_str("a sequence") - } - - fn visit_seq(self, mut seq: A) -> Result - where - A: SeqAccess<'de>, - { - let size = seq.size_hint().unwrap_or(0); - let max = match usize::try_from(S::get()) { - Ok(n) => n, - Err(_) => return Err(A::Error::custom("can't convert to usize")), - }; - if size > max { - log::warn!( - target: "runtime", - "length of a bounded vector while deserializing is not respected.", - ); - } - let mut values = Vec::with_capacity(size); - - while let Some(value) = seq.next_element()? { - values.push(value); - if values.len() > max { - log::warn!( - target: "runtime", - "length of a bounded vector while deserializing is not respected.", - ); - } - } - - Ok(values) - } - } - - let visitor: VecVisitor = VecVisitor(PhantomData); - deserializer.deserialize_seq(visitor).map(|v| { - WeakBoundedVec::::try_from(v).map_err(|_| Error::custom("out of bounds")) - })? - } -} - -impl> Decode for WeakBoundedVec { - fn decode(input: &mut I) -> Result { - let inner = Vec::::decode(input)?; - Ok(Self::force_from(inner, Some("decode"))) - } - - fn skip(input: &mut I) -> Result<(), codec::Error> { - Vec::::skip(input) - } -} - -impl WeakBoundedVec { - /// Create `Self` from `t` without any checks. - fn unchecked_from(t: Vec) -> Self { - Self(t, Default::default()) - } - - /// Consume self, and return the inner `Vec`. Henceforth, the `Vec<_>` can be altered in an - /// arbitrary way. At some point, if the reverse conversion is required, `TryFrom>` can - /// be used. - /// - /// This is useful for cases if you need access to an internal API of the inner `Vec<_>` which - /// is not provided by the wrapper `WeakBoundedVec`. - pub fn into_inner(self) -> Vec { - self.0 - } - - /// Exactly the same semantics as [`Vec::remove`]. - /// - /// # Panics - /// - /// Panics if `index` is out of bounds. - pub fn remove(&mut self, index: usize) -> T { - self.0.remove(index) - } - - /// Exactly the same semantics as [`Vec::swap_remove`]. - /// - /// # Panics - /// - /// Panics if `index` is out of bounds. - pub fn swap_remove(&mut self, index: usize) -> T { - self.0.swap_remove(index) - } - - /// Exactly the same semantics as [`Vec::retain`]. - pub fn retain bool>(&mut self, f: F) { - self.0.retain(f) - } - - /// Exactly the same semantics as [`slice::get_mut`]. - pub fn get_mut>( - &mut self, - index: I, - ) -> Option<&mut >::Output> { - self.0.get_mut(index) - } -} - -impl> WeakBoundedVec { - /// Get the bound of the type in `usize`. - pub fn bound() -> usize { - S::get() as usize - } - - /// Create `Self` from `t` without any checks. Logs warnings if the bound is not being - /// respected. The additional scope can be used to indicate where a potential overflow is - /// happening. - pub fn force_from(t: Vec, scope: Option<&'static str>) -> Self { - if t.len() > Self::bound() { - log::warn!( - target: "runtime", - "length of a bounded vector in scope {} is not respected.", - scope.unwrap_or("UNKNOWN"), - ); - } - - Self::unchecked_from(t) - } - - /// Consumes self and mutates self via the given `mutate` function. - /// - /// If the outcome of mutation is within bounds, `Some(Self)` is returned. Else, `None` is - /// returned. - /// - /// This is essentially a *consuming* shorthand [`Self::into_inner`] -> `...` -> - /// [`Self::try_from`]. - pub fn try_mutate(mut self, mut mutate: impl FnMut(&mut Vec)) -> Option { - mutate(&mut self.0); - (self.0.len() <= Self::bound()).then(move || self) - } - - /// Exactly the same semantics as [`Vec::insert`], but returns an `Err` (and is a noop) if the - /// new length of the vector exceeds `S`. - /// - /// # Panics - /// - /// Panics if `index > len`. - pub fn try_insert(&mut self, index: usize, element: T) -> Result<(), ()> { - if self.len() < Self::bound() { - self.0.insert(index, element); - Ok(()) - } else { - Err(()) - } - } - - /// Exactly the same semantics as [`Vec::push`], but returns an `Err` (and is a noop) if the - /// new length of the vector exceeds `S`. - /// - /// # Panics - /// - /// Panics if the new capacity exceeds isize::MAX bytes. - pub fn try_push(&mut self, element: T) -> Result<(), ()> { - if self.len() < Self::bound() { - self.0.push(element); - Ok(()) - } else { - Err(()) - } - } -} - -impl Default for WeakBoundedVec { - fn default() -> Self { - // the bound cannot be below 0, which is satisfied by an empty vector - Self::unchecked_from(Vec::default()) - } -} - -impl sp_std::fmt::Debug for WeakBoundedVec -where - Vec: sp_std::fmt::Debug, - S: Get, -{ - fn fmt(&self, f: &mut sp_std::fmt::Formatter<'_>) -> sp_std::fmt::Result { - f.debug_tuple("WeakBoundedVec").field(&self.0).field(&Self::bound()).finish() - } -} - -impl Clone for WeakBoundedVec -where - T: Clone, -{ - fn clone(&self) -> Self { - // bound is retained - Self::unchecked_from(self.0.clone()) - } -} - -impl> TryFrom> for WeakBoundedVec { - type Error = (); - fn try_from(t: Vec) -> Result { - if t.len() <= Self::bound() { - // explicit check just above - Ok(Self::unchecked_from(t)) - } else { - Err(()) - } - } -} - -// It is okay to give a non-mutable reference of the inner vec to anyone. -impl AsRef> for WeakBoundedVec { - fn as_ref(&self) -> &Vec { - &self.0 - } -} - -impl AsRef<[T]> for WeakBoundedVec { - fn as_ref(&self) -> &[T] { - &self.0 - } -} - -impl AsMut<[T]> for WeakBoundedVec { - fn as_mut(&mut self) -> &mut [T] { - &mut self.0 - } -} - -// will allow for immutable all operations of `Vec` on `WeakBoundedVec`. -impl Deref for WeakBoundedVec { - type Target = Vec; - - fn deref(&self) -> &Self::Target { - &self.0 - } -} - -// Allows for indexing similar to a normal `Vec`. Can panic if out of bound. -impl Index for WeakBoundedVec -where - I: SliceIndex<[T]>, -{ - type Output = I::Output; - - #[inline] - fn index(&self, index: I) -> &Self::Output { - self.0.index(index) - } -} - -impl IndexMut for WeakBoundedVec -where - I: SliceIndex<[T]>, -{ - #[inline] - fn index_mut(&mut self, index: I) -> &mut Self::Output { - self.0.index_mut(index) - } -} - -impl sp_std::iter::IntoIterator for WeakBoundedVec { - type Item = T; - type IntoIter = sp_std::vec::IntoIter; - fn into_iter(self) -> Self::IntoIter { - self.0.into_iter() - } -} - -impl<'a, T, S> sp_std::iter::IntoIterator for &'a WeakBoundedVec { - type Item = &'a T; - type IntoIter = sp_std::slice::Iter<'a, T>; - fn into_iter(self) -> Self::IntoIter { - self.0.iter() - } -} - -impl<'a, T, S> sp_std::iter::IntoIterator for &'a mut WeakBoundedVec { - type Item = &'a mut T; - type IntoIter = sp_std::slice::IterMut<'a, T>; - fn into_iter(self) -> Self::IntoIter { - self.0.iter_mut() - } -} - -impl codec::DecodeLength for WeakBoundedVec { - fn len(self_encoded: &[u8]) -> Result { - // `WeakBoundedVec` stored just a `Vec`, thus the length is at the beginning in - // `Compact` form, and same implementation as `Vec` can be used. - as codec::DecodeLength>::len(self_encoded) - } -} - -impl PartialEq> for WeakBoundedVec -where - T: PartialEq, - BoundSelf: Get, - BoundRhs: Get, -{ - fn eq(&self, rhs: &WeakBoundedVec) -> bool { - self.0 == rhs.0 - } -} - -impl PartialEq> for WeakBoundedVec -where - T: PartialEq, - BoundSelf: Get, - BoundRhs: Get, -{ - fn eq(&self, rhs: &BoundedVec) -> bool { - self.0 == rhs.0 - } -} - -impl<'a, T, BoundSelf, BoundRhs> PartialEq> - for WeakBoundedVec -where - T: PartialEq, - BoundSelf: Get, - BoundRhs: Get, -{ - fn eq(&self, rhs: &BoundedSlice<'a, T, BoundRhs>) -> bool { - self.0 == rhs.0 - } -} - -impl> PartialEq> for WeakBoundedVec { - fn eq(&self, other: &Vec) -> bool { - &self.0 == other - } -} - -impl> Eq for WeakBoundedVec where T: Eq {} - -impl PartialOrd> - for WeakBoundedVec -where - T: PartialOrd, - BoundSelf: Get, - BoundRhs: Get, -{ - fn partial_cmp(&self, other: &WeakBoundedVec) -> Option { - self.0.partial_cmp(&other.0) - } -} - -impl PartialOrd> for WeakBoundedVec -where - T: PartialOrd, - BoundSelf: Get, - BoundRhs: Get, -{ - fn partial_cmp(&self, other: &BoundedVec) -> Option { - self.0.partial_cmp(&other.0) - } -} - -impl<'a, T, BoundSelf, BoundRhs> PartialOrd> - for WeakBoundedVec -where - T: PartialOrd, - BoundSelf: Get, - BoundRhs: Get, -{ - fn partial_cmp(&self, other: &BoundedSlice<'a, T, BoundRhs>) -> Option { - (&*self.0).partial_cmp(other.0) - } -} - -impl> Ord for WeakBoundedVec { - fn cmp(&self, other: &Self) -> sp_std::cmp::Ordering { - self.0.cmp(&other.0) - } -} - -impl MaxEncodedLen for WeakBoundedVec -where - T: MaxEncodedLen, - S: Get, - WeakBoundedVec: Encode, -{ - fn max_encoded_len() -> usize { - // WeakBoundedVec encodes like Vec which encodes like [T], which is a compact u32 - // plus each item in the slice: - // See: https://docs.substrate.io/reference/scale-codec/ - codec::Compact(S::get()) - .encoded_size() - .saturating_add(Self::bound().saturating_mul(T::max_encoded_len())) - } -} - -#[cfg(test)] -pub mod test { - use super::*; - use crate::ConstU32; - - #[test] - fn bound_returns_correct_value() { - assert_eq!(WeakBoundedVec::>::bound(), 7); - } - - #[test] - fn try_insert_works() { - let mut bounded: WeakBoundedVec> = vec![1, 2, 3].try_into().unwrap(); - bounded.try_insert(1, 0).unwrap(); - assert_eq!(*bounded, vec![1, 0, 2, 3]); - - assert!(bounded.try_insert(0, 9).is_err()); - assert_eq!(*bounded, vec![1, 0, 2, 3]); - } - - #[test] - #[should_panic(expected = "insertion index (is 9) should be <= len (is 3)")] - fn try_inert_panics_if_oob() { - let mut bounded: WeakBoundedVec> = vec![1, 2, 3].try_into().unwrap(); - bounded.try_insert(9, 0).unwrap(); - } - - #[test] - fn try_push_works() { - let mut bounded: WeakBoundedVec> = vec![1, 2, 3].try_into().unwrap(); - bounded.try_push(0).unwrap(); - assert_eq!(*bounded, vec![1, 2, 3, 0]); - - assert!(bounded.try_push(9).is_err()); - } - - #[test] - fn deref_coercion_works() { - let bounded: WeakBoundedVec> = vec![1, 2, 3].try_into().unwrap(); - // these methods come from deref-ed vec. - assert_eq!(bounded.len(), 3); - assert!(bounded.iter().next().is_some()); - assert!(!bounded.is_empty()); - } - - #[test] - fn try_mutate_works() { - let bounded: WeakBoundedVec> = vec![1, 2, 3, 4, 5, 6].try_into().unwrap(); - let bounded = bounded.try_mutate(|v| v.push(7)).unwrap(); - assert_eq!(bounded.len(), 7); - assert!(bounded.try_mutate(|v| v.push(8)).is_none()); - } - - #[test] - fn slice_indexing_works() { - let bounded: WeakBoundedVec> = vec![1, 2, 3, 4, 5, 6].try_into().unwrap(); - assert_eq!(&bounded[0..=2], &[1, 2, 3]); - } - - #[test] - fn vec_eq_works() { - let bounded: WeakBoundedVec> = vec![1, 2, 3, 4, 5, 6].try_into().unwrap(); - assert_eq!(bounded, vec![1, 2, 3, 4, 5, 6]); - } - - #[test] - fn too_big_succeed_to_decode() { - let v: Vec = vec![1, 2, 3, 4, 5]; - let w = WeakBoundedVec::>::decode(&mut &v.encode()[..]).unwrap(); - assert_eq!(v, *w); - } -} diff --git a/primitives/core/src/lib.rs b/primitives/core/src/lib.rs index 80e14b84d..13dbad66b 100644 --- a/primitives/core/src/lib.rs +++ b/primitives/core/src/lib.rs @@ -51,7 +51,6 @@ pub mod hashing; #[cfg(feature = "full_crypto")] pub use hashing::{blake2_128, blake2_256, keccak_256, twox_128, twox_256, twox_64}; -pub mod bounded; pub mod crypto; pub mod hexdisplay; @@ -81,6 +80,13 @@ pub use self::hasher::blake2::Blake2Hasher; pub use self::hasher::keccak::KeccakHasher; pub use hash_db::Hasher; +pub use bounded_collections as bounded; +#[cfg(feature = "std")] +pub use bounded_collections::{bounded_btree_map, bounded_vec}; +pub use bounded_collections::{ + parameter_types, ConstBool, ConstI128, ConstI16, ConstI32, ConstI64, ConstI8, ConstU128, + ConstU16, ConstU32, ConstU64, ConstU8, Get, GetDefault, TryCollect, TypedGet, +}; pub use sp_storage as storage; #[doc(hidden)] @@ -387,242 +393,6 @@ macro_rules! impl_maybe_marker { // everybody. pub const MAX_POSSIBLE_ALLOCATION: u32 = 33554432; // 2^25 bytes, 32 MiB -/// A trait for querying a single value from a type defined in the trait. -/// -/// It is not required that the value is constant. -pub trait TypedGet { - /// The type which is returned. - type Type; - /// Return the current value. - fn get() -> Self::Type; -} - -/// A trait for querying a single value from a type. -/// -/// It is not required that the value is constant. -pub trait Get { - /// Return the current value. - fn get() -> T; -} - -impl Get for () { - fn get() -> T { - T::default() - } -} - -/// Implement Get by returning Default for any type that implements Default. -pub struct GetDefault; -impl Get for GetDefault { - fn get() -> T { - T::default() - } -} - -macro_rules! impl_const_get { - ($name:ident, $t:ty) => { - #[doc = "Const getter for a basic type."] - #[derive($crate::RuntimeDebug)] - pub struct $name; - impl Get<$t> for $name { - fn get() -> $t { - T - } - } - impl Get> for $name { - fn get() -> Option<$t> { - Some(T) - } - } - impl TypedGet for $name { - type Type = $t; - fn get() -> $t { - T - } - } - }; -} - -impl_const_get!(ConstBool, bool); -impl_const_get!(ConstU8, u8); -impl_const_get!(ConstU16, u16); -impl_const_get!(ConstU32, u32); -impl_const_get!(ConstU64, u64); -impl_const_get!(ConstU128, u128); -impl_const_get!(ConstI8, i8); -impl_const_get!(ConstI16, i16); -impl_const_get!(ConstI32, i32); -impl_const_get!(ConstI64, i64); -impl_const_get!(ConstI128, i128); - -/// Try and collect into a collection `C`. -pub trait TryCollect { - /// The error type that gets returned when a collection can't be made from `self`. - type Error; - /// Consume self and try to collect the results into `C`. - /// - /// This is useful in preventing the undesirable `.collect().try_into()` call chain on - /// collections that need to be converted into a bounded type (e.g. `BoundedVec`). - fn try_collect(self) -> Result; -} - -/// Create new implementations of the [`Get`](crate::Get) trait. -/// -/// The so-called parameter type can be created in four different ways: -/// -/// - Using `const` to create a parameter type that provides a `const` getter. It is required that -/// the `value` is const. -/// -/// - Declare the parameter type without `const` to have more freedom when creating the value. -/// -/// NOTE: A more substantial version of this macro is available in `frame_support` crate which -/// allows mutable and persistant variants. -/// -/// # Examples -/// -/// ``` -/// # use sp_core::Get; -/// # use sp_core::parameter_types; -/// // This function cannot be used in a const context. -/// fn non_const_expression() -> u64 { 99 } -/// -/// const FIXED_VALUE: u64 = 10; -/// parameter_types! { -/// pub const Argument: u64 = 42 + FIXED_VALUE; -/// /// Visibility of the type is optional -/// OtherArgument: u64 = non_const_expression(); -/// } -/// -/// trait Config { -/// type Parameter: Get; -/// type OtherParameter: Get; -/// } -/// -/// struct Runtime; -/// impl Config for Runtime { -/// type Parameter = Argument; -/// type OtherParameter = OtherArgument; -/// } -/// ``` -/// -/// # Invalid example: -/// -/// ```compile_fail -/// # use sp_core::Get; -/// # use sp_core::parameter_types; -/// // This function cannot be used in a const context. -/// fn non_const_expression() -> u64 { 99 } -/// -/// parameter_types! { -/// pub const Argument: u64 = non_const_expression(); -/// } -/// ``` -#[macro_export] -macro_rules! parameter_types { - ( - $( #[ $attr:meta ] )* - $vis:vis const $name:ident: $type:ty = $value:expr; - $( $rest:tt )* - ) => ( - $( #[ $attr ] )* - $vis struct $name; - $crate::parameter_types!(@IMPL_CONST $name , $type , $value); - $crate::parameter_types!( $( $rest )* ); - ); - ( - $( #[ $attr:meta ] )* - $vis:vis $name:ident: $type:ty = $value:expr; - $( $rest:tt )* - ) => ( - $( #[ $attr ] )* - $vis struct $name; - $crate::parameter_types!(@IMPL $name, $type, $value); - $crate::parameter_types!( $( $rest )* ); - ); - () => (); - (@IMPL_CONST $name:ident, $type:ty, $value:expr) => { - impl $name { - /// Returns the value of this parameter type. - pub const fn get() -> $type { - $value - } - } - - impl> $crate::Get for $name { - fn get() -> I { - I::from(Self::get()) - } - } - - impl $crate::TypedGet for $name { - type Type = $type; - fn get() -> $type { - Self::get() - } - } - }; - (@IMPL $name:ident, $type:ty, $value:expr) => { - impl $name { - /// Returns the value of this parameter type. - pub fn get() -> $type { - $value - } - } - - impl> $crate::Get for $name { - fn get() -> I { - I::from(Self::get()) - } - } - - impl $crate::TypedGet for $name { - type Type = $type; - fn get() -> $type { - Self::get() - } - } - }; -} - -/// Build a bounded vec from the given literals. -/// -/// The type of the outcome must be known. -/// -/// Will not handle any errors and just panic if the given literals cannot fit in the corresponding -/// bounded vec type. Thus, this is only suitable for testing and non-consensus code. -#[macro_export] -#[cfg(feature = "std")] -macro_rules! bounded_vec { - ($ ($values:expr),* $(,)?) => { - { - $crate::sp_std::vec![$($values),*].try_into().unwrap() - } - }; - ( $value:expr ; $repetition:expr ) => { - { - $crate::sp_std::vec![$value ; $repetition].try_into().unwrap() - } - } -} - -/// Build a bounded btree-map from the given literals. -/// -/// The type of the outcome must be known. -/// -/// Will not handle any errors and just panic if the given literals cannot fit in the corresponding -/// bounded vec type. Thus, this is only suitable for testing and non-consensus code. -#[macro_export] -#[cfg(feature = "std")] -macro_rules! bounded_btree_map { - ($ ( $key:expr => $value:expr ),* $(,)?) => { - { - $crate::TryCollect::<$crate::bounded::BoundedBTreeMap<_, _, _>>::try_collect( - $crate::sp_std::vec![$(($key, $value)),*].into_iter() - ).unwrap() - } - }; -} - /// Generates a macro for checking if a certain feature is enabled. /// /// These feature checking macros can be used to conditionally enable/disable code in a dependent From dfc9223f5d28b258c7762012ae7ef48a7a7f1359 Mon Sep 17 00:00:00 2001 From: Sam Johnson Date: Sun, 5 Feb 2023 15:18:56 -0500 Subject: [PATCH 087/558] update linregress in frame-benchmarking to 0.5.1 (#13310) * update nalgebra in frame-benchmarking to 0.5.0 * upgrade to 0.5.1 to incorporate upstream fix in linregress --- Cargo.lock | 58 +++++++++++++++++------------- frame/benchmarking/Cargo.toml | 2 +- frame/benchmarking/src/analysis.rs | 2 +- 3 files changed, 36 insertions(+), 26 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 492c5b15d..52de03f64 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -765,6 +765,12 @@ version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e3b5ca7a04898ad4bcd41c90c5285445ff5b791899bb1b0abdd2a2aa791211d7" +[[package]] +name = "bytemuck" +version = "1.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c041d3eab048880cb0b86b256447da3f18859a163c3b8d8893f4e6368abe6393" + [[package]] name = "byteorder" version = "1.4.3" @@ -4181,12 +4187,11 @@ dependencies = [ [[package]] name = "linregress" -version = "0.4.4" +version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d6c601a85f5ecd1aba625247bca0031585fb1c446461b142878a16f8245ddeb8" +checksum = "475015a7f8f017edb28d2e69813be23500ad4b32cfe3421c4148efc97324ee52" dependencies = [ "nalgebra", - "statrs", ] [[package]] @@ -4578,9 +4583,9 @@ dependencies = [ [[package]] name = "nalgebra" -version = "0.27.1" +version = "0.32.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "462fffe4002f4f2e1f6a9dcf12cc1a6fc0e15989014efc02a941d3e0f5dc2120" +checksum = "f6515c882ebfddccaa73ead7320ca28036c4bc84c9bcca3cc0cbba8efe89223a" dependencies = [ "approx", "matrixmultiply", @@ -4588,17 +4593,15 @@ dependencies = [ "num-complex", "num-rational", "num-traits", - "rand 0.8.5", - "rand_distr", "simba", "typenum", ] [[package]] name = "nalgebra-macros" -version = "0.1.0" +version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "01fcc0b8149b4632adc89ac3b7b31a12fb6099a0317a4eb2ebff574ef7de7218" +checksum = "d232c68884c0c99810a5a4d333ef7e47689cfd0edc85efc9e54e1e6bf5212766" dependencies = [ "proc-macro2", "quote", @@ -7862,6 +7865,15 @@ dependencies = [ "rustc_version 0.2.3", ] +[[package]] +name = "safe_arch" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "794821e4ccb0d9f979512f9c1973480123f9bd62a90d74ab0f9426fcf8f4a529" +dependencies = [ + "bytemuck", +] + [[package]] name = "same-file" version = "1.0.6" @@ -9522,14 +9534,15 @@ dependencies = [ [[package]] name = "simba" -version = "0.5.1" +version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8e82063457853d00243beda9952e910b82593e4b07ae9f721b9278a99a0d3d5c" +checksum = "50582927ed6f77e4ac020c057f37a268fc6aebc29225050365aacbb9deeeddc4" dependencies = [ "approx", "num-complex", "num-traits", "paste", + "wide", ] [[package]] @@ -10503,19 +10516,6 @@ dependencies = [ "syn", ] -[[package]] -name = "statrs" -version = "0.15.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "05bdbb8e4e78216a85785a85d3ec3183144f98d0097b9281802c019bb07a6f05" -dependencies = [ - "approx", - "lazy_static", - "nalgebra", - "num-traits", - "rand 0.8.5", -] - [[package]] name = "strsim" version = "0.10.0" @@ -12395,6 +12395,16 @@ dependencies = [ "once_cell", ] +[[package]] +name = "wide" +version = "0.7.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "feff0a412894d67223777b6cc8d68c0dab06d52d95e9890d5f2d47f10dd9366c" +dependencies = [ + "bytemuck", + "safe_arch", +] + [[package]] name = "widestring" version = "0.5.1" diff --git a/frame/benchmarking/Cargo.toml b/frame/benchmarking/Cargo.toml index 4ff2077d7..7b8a7c5ea 100644 --- a/frame/benchmarking/Cargo.toml +++ b/frame/benchmarking/Cargo.toml @@ -14,7 +14,7 @@ targets = ["x86_64-unknown-linux-gnu"] [dependencies] codec = { package = "parity-scale-codec", version = "3.2.2", default-features = false } -linregress = { version = "0.4.4", optional = true } +linregress = { version = "0.5.1", optional = true } log = { version = "0.4.17", default-features = false } paste = "1.0" scale-info = { version = "2.1.1", default-features = false, features = ["derive"] } diff --git a/frame/benchmarking/src/analysis.rs b/frame/benchmarking/src/analysis.rs index 0b77a9234..e5afe8103 100644 --- a/frame/benchmarking/src/analysis.rs +++ b/frame/benchmarking/src/analysis.rs @@ -161,7 +161,7 @@ fn raw_linear_regression( data.extend(xs); } let model = linregress::fit_low_level_regression_model(&data, ys.len(), x_vars + 2).ok()?; - Some((model.parameters[0], model.parameters[1..].to_vec(), model.se)) + Some((model.parameters()[0], model.parameters()[1..].to_vec(), model.se().to_vec())) } fn linear_regression( From 550e6a1ac24d7dbff4f7e2458ce20ee4c27c3c57 Mon Sep 17 00:00:00 2001 From: Anton Date: Mon, 6 Feb 2023 10:21:51 +0400 Subject: [PATCH 088/558] [client/network] remove peer entry from `ephemeral_addresses` (#13084) * [client/network] remove peer entry from `ephemeral_addresses` if there are no addresses associated with that peer * refactor as per @bkchr suggestion https://github.com/paritytech/substrate/pull/13084#discussion_r1068028534 * add missing import * fix error --- client/network/src/discovery.rs | 26 ++++++++++++++++---------- client/network/src/service.rs | 2 +- 2 files changed, 17 insertions(+), 11 deletions(-) diff --git a/client/network/src/discovery.rs b/client/network/src/discovery.rs index 7329ebf15..b8805d1b1 100644 --- a/client/network/src/discovery.rs +++ b/client/network/src/discovery.rs @@ -77,7 +77,7 @@ use sc_network_common::{config::ProtocolId, utils::LruHashSet}; use sp_core::hexdisplay::HexDisplay; use std::{ cmp, - collections::{HashMap, HashSet, VecDeque}, + collections::{hash_map::Entry, HashMap, HashSet, VecDeque}, num::NonZeroUsize, task::{Context, Poll}, time::Duration, @@ -318,14 +318,16 @@ impl DiscoveryBehaviour { /// If we didn't know this address before, also generates a `Discovered` event. pub fn add_known_address(&mut self, peer_id: PeerId, addr: Multiaddr) { let addrs_list = self.ephemeral_addresses.entry(peer_id).or_default(); - if !addrs_list.iter().any(|a| *a == addr) { - if let Some(k) = self.kademlia.as_mut() { - k.add_address(&peer_id, addr.clone()); - } + if addrs_list.contains(&addr) { + return + } - self.pending_events.push_back(DiscoveryOut::Discovered(peer_id)); - addrs_list.push(addr); + if let Some(k) = self.kademlia.as_mut() { + k.add_address(&peer_id, addr.clone()); } + + self.pending_events.push_back(DiscoveryOut::Discovered(peer_id)); + addrs_list.push(addr); } /// Add a self-reported address of a remote peer to the k-buckets of the DHT @@ -456,7 +458,7 @@ pub enum DiscoveryOut { /// The DHT yielded results for the record request. /// - /// Returning the result grouped in (key, value) pairs as well as the request duration.. + /// Returning the result grouped in (key, value) pairs as well as the request duration. ValueFound(Vec<(record::Key, Vec)>, Duration), /// The record requested was not found in the DHT. @@ -535,9 +537,13 @@ impl NetworkBehaviour for DiscoveryBehaviour { FromSwarm::DialFailure(e @ DialFailure { peer_id, error, .. }) => { if let Some(peer_id) = peer_id { if let DialError::Transport(errors) = error { - if let Some(list) = self.ephemeral_addresses.get_mut(&peer_id) { + if let Entry::Occupied(mut entry) = self.ephemeral_addresses.entry(peer_id) + { for (addr, _error) in errors { - list.retain(|a| a != addr); + entry.get_mut().retain(|a| a != addr); + } + if entry.get().is_empty() { + entry.remove(); } } } diff --git a/client/network/src/service.rs b/client/network/src/service.rs index f3afb58e8..dbe148c89 100644 --- a/client/network/src/service.rs +++ b/client/network/src/service.rs @@ -534,7 +534,7 @@ where self.network_service.behaviour().user_protocol().num_sync_requests() } - /// Adds an address for a node. + /// Adds an address known to a node. pub fn add_known_address(&mut self, peer_id: PeerId, addr: Multiaddr) { self.network_service.behaviour_mut().add_known_address(peer_id, addr); } From f5e370da7f34622637a343f03420b4242b321891 Mon Sep 17 00:00:00 2001 From: Alexandru Vasile <60601340+lexnv@users.noreply.github.com> Date: Mon, 6 Feb 2023 11:17:22 +0200 Subject: [PATCH 089/558] rpc-spec-v2/tx: Fix error typo (#13307) Signed-off-by: Alexandru Vasile --- client/rpc-spec-v2/src/transaction/error.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client/rpc-spec-v2/src/transaction/error.rs b/client/rpc-spec-v2/src/transaction/error.rs index 72a595999..901d7c58b 100644 --- a/client/rpc-spec-v2/src/transaction/error.rs +++ b/client/rpc-spec-v2/src/transaction/error.rs @@ -68,7 +68,7 @@ impl From for TransactionEvent { Error::Pool(PoolError::TooLowPriority { old, new }) => TransactionEvent::Invalid(TransactionError { error: format!( - "The priority of the transactin is too low (pool {} > current {})", + "The priority of the transaction is too low (pool {} > current {})", old, new ), }), From 51cf2e790f118f6cfe8d94fb9ca96bc580bd7c98 Mon Sep 17 00:00:00 2001 From: Adrian Catangiu Date: Mon, 6 Feb 2023 14:42:54 +0200 Subject: [PATCH 090/558] BEEFY: client support for detecting equivocated votes (#13285) * client/beefy: detect equivocated votes * client/beefy: make sure to persist state after voting * client/beefy: drop never-used aux-schema v2 migration * impl review suggestion --------- Signed-off-by: Adrian Catangiu --- Cargo.lock | 3 +- client/beefy/Cargo.toml | 1 - client/beefy/src/aux_schema.rs | 16 +- client/beefy/src/communication/gossip.rs | 7 +- client/beefy/src/justification.rs | 5 +- client/beefy/src/keystore.rs | 62 +--- client/beefy/src/metrics.rs | 6 + client/beefy/src/round.rs | 411 +++++++++++------------ client/beefy/src/tests.rs | 2 +- client/beefy/src/worker.rs | 133 +++----- primitives/beefy/Cargo.toml | 2 + primitives/beefy/src/commitment.rs | 1 + primitives/beefy/src/lib.rs | 79 ++++- 13 files changed, 373 insertions(+), 355 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 52de03f64..afebaabd6 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -512,7 +512,6 @@ dependencies = [ "sp-mmr-primitives", "sp-runtime", "sp-tracing", - "strum", "substrate-prometheus-endpoint", "substrate-test-runtime-client", "tempfile", @@ -9734,6 +9733,7 @@ name = "sp-beefy" version = "4.0.0-dev" dependencies = [ "array-bytes", + "lazy_static", "parity-scale-codec", "scale-info", "serde", @@ -9745,6 +9745,7 @@ dependencies = [ "sp-mmr-primitives", "sp-runtime", "sp-std", + "strum", ] [[package]] diff --git a/client/beefy/Cargo.toml b/client/beefy/Cargo.toml index d81ff2587..2976d1474 100644 --- a/client/beefy/Cargo.toml +++ b/client/beefy/Cargo.toml @@ -39,7 +39,6 @@ sp-runtime = { version = "7.0.0", path = "../../primitives/runtime" } [dev-dependencies] serde = "1.0.136" -strum = { version = "0.24.1", features = ["derive"] } tempfile = "3.1.0" tokio = "1.22.0" sc-block-builder = { version = "0.10.0-dev", path = "../block-builder" } diff --git a/client/beefy/src/aux_schema.rs b/client/beefy/src/aux_schema.rs index 217ea4769..6e87956da 100644 --- a/client/beefy/src/aux_schema.rs +++ b/client/beefy/src/aux_schema.rs @@ -26,25 +26,25 @@ use sp_blockchain::{Error as ClientError, Result as ClientResult}; use sp_runtime::traits::Block as BlockT; const VERSION_KEY: &[u8] = b"beefy_auxschema_version"; -const WORKER_STATE: &[u8] = b"beefy_voter_state"; +const WORKER_STATE_KEY: &[u8] = b"beefy_voter_state"; const CURRENT_VERSION: u32 = 2; -pub(crate) fn write_current_version(backend: &B) -> ClientResult<()> { +pub(crate) fn write_current_version(backend: &BE) -> ClientResult<()> { info!(target: LOG_TARGET, "🥩 write aux schema version {:?}", CURRENT_VERSION); AuxStore::insert_aux(backend, &[(VERSION_KEY, CURRENT_VERSION.encode().as_slice())], &[]) } /// Write voter state. -pub(crate) fn write_voter_state( - backend: &B, - state: &PersistedState, +pub(crate) fn write_voter_state( + backend: &BE, + state: &PersistedState, ) -> ClientResult<()> { trace!(target: LOG_TARGET, "🥩 persisting {:?}", state); - backend.insert_aux(&[(WORKER_STATE, state.encode().as_slice())], &[]) + AuxStore::insert_aux(backend, &[(WORKER_STATE_KEY, state.encode().as_slice())], &[]) } -fn load_decode(backend: &B, key: &[u8]) -> ClientResult> { +fn load_decode(backend: &BE, key: &[u8]) -> ClientResult> { match backend.get_aux(key)? { None => Ok(None), Some(t) => T::decode(&mut &t[..]) @@ -64,7 +64,7 @@ where match version { None => (), Some(1) => (), // version 1 is totally obsolete and should be simply ignored - Some(2) => return load_decode::<_, PersistedState>(backend, WORKER_STATE), + Some(2) => return load_decode::<_, PersistedState>(backend, WORKER_STATE_KEY), other => return Err(ClientError::Backend(format!("Unsupported BEEFY DB version: {:?}", other))), } diff --git a/client/beefy/src/communication/gossip.rs b/client/beefy/src/communication/gossip.rs index 116a6286e..5d5161021 100644 --- a/client/beefy/src/communication/gossip.rs +++ b/client/beefy/src/communication/gossip.rs @@ -120,7 +120,7 @@ where /// Note a voting round. /// - /// Noting round will start a live `round`. + /// Noting round will track gossiped votes for `round`. pub(crate) fn note_round(&self, round: NumberFor) { debug!(target: LOG_TARGET, "🥩 About to note gossip round #{}", round); self.known_votes.write().insert(round); @@ -242,9 +242,10 @@ mod tests { use sc_network_test::Block; use sp_keystore::{SyncCryptoStore, SyncCryptoStorePtr}; - use crate::keystore::{tests::Keyring, BeefyKeystore}; + use crate::keystore::BeefyKeystore; use beefy_primitives::{ - crypto::Signature, known_payloads, Commitment, MmrRootHash, Payload, VoteMessage, KEY_TYPE, + crypto::Signature, keyring::Keyring, known_payloads, Commitment, MmrRootHash, Payload, + VoteMessage, KEY_TYPE, }; use super::*; diff --git a/client/beefy/src/justification.rs b/client/beefy/src/justification.rs index 7243c6927..6f869015b 100644 --- a/client/beefy/src/justification.rs +++ b/client/beefy/src/justification.rs @@ -81,12 +81,13 @@ fn verify_with_validator_set( #[cfg(test)] pub(crate) mod tests { use beefy_primitives::{ - known_payloads, Commitment, Payload, SignedCommitment, VersionedFinalityProof, + keyring::Keyring, known_payloads, Commitment, Payload, SignedCommitment, + VersionedFinalityProof, }; use substrate_test_runtime_client::runtime::Block; use super::*; - use crate::{keystore::tests::Keyring, tests::make_beefy_ids}; + use crate::tests::make_beefy_ids; pub(crate) fn new_finality_proof( block_num: NumberFor, diff --git a/client/beefy/src/keystore.rs b/client/beefy/src/keystore.rs index d1f5615a9..73788a31b 100644 --- a/client/beefy/src/keystore.rs +++ b/client/beefy/src/keystore.rs @@ -19,7 +19,6 @@ use sp_application_crypto::RuntimeAppPublic; use sp_core::keccak_256; use sp_keystore::{SyncCryptoStore, SyncCryptoStorePtr}; -use sp_runtime::traits::Keccak256; use log::warn; @@ -30,6 +29,9 @@ use beefy_primitives::{ use crate::{error, LOG_TARGET}; +/// Hasher used for BEEFY signatures. +pub(crate) type BeefySignatureHasher = sp_runtime::traits::Keccak256; + /// A BEEFY specific keystore implemented as a `Newtype`. This is basically a /// wrapper around [`sp_keystore::SyncCryptoStore`] and allows to customize /// common cryptographic functionality. @@ -104,7 +106,7 @@ impl BeefyKeystore { /// /// Return `true` if the signature is authentic, `false` otherwise. pub fn verify(public: &Public, sig: &Signature, message: &[u8]) -> bool { - BeefyAuthorityId::::verify(public, sig, message) + BeefyAuthorityId::::verify(public, sig, message) } } @@ -119,63 +121,13 @@ pub mod tests { use std::sync::Arc; use sc_keystore::LocalKeystore; - use sp_core::{ecdsa, keccak_256, Pair}; - use sp_keystore::{SyncCryptoStore, SyncCryptoStorePtr}; + use sp_core::{ecdsa, Pair}; - use beefy_primitives::{crypto, KEY_TYPE}; + use beefy_primitives::{crypto, keyring::Keyring}; - use super::BeefyKeystore; + use super::*; use crate::error::Error; - /// Set of test accounts using [`beefy_primitives::crypto`] types. - #[allow(missing_docs)] - #[derive(Debug, Clone, Copy, PartialEq, Eq, strum::Display, strum::EnumIter)] - pub(crate) enum Keyring { - Alice, - Bob, - Charlie, - Dave, - Eve, - Ferdie, - One, - Two, - } - - impl Keyring { - /// Sign `msg`. - pub fn sign(self, msg: &[u8]) -> crypto::Signature { - let msg = keccak_256(msg); - ecdsa::Pair::from(self).sign_prehashed(&msg).into() - } - - /// Return key pair. - pub fn pair(self) -> crypto::Pair { - ecdsa::Pair::from_string(self.to_seed().as_str(), None).unwrap().into() - } - - /// Return public key. - pub fn public(self) -> crypto::Public { - self.pair().public() - } - - /// Return seed string. - pub fn to_seed(self) -> String { - format!("//{}", self) - } - } - - impl From for crypto::Pair { - fn from(k: Keyring) -> Self { - k.pair() - } - } - - impl From for ecdsa::Pair { - fn from(k: Keyring) -> Self { - k.pair().into() - } - } - fn keystore() -> SyncCryptoStorePtr { Arc::new(LocalKeystore::in_memory()) } diff --git a/client/beefy/src/metrics.rs b/client/beefy/src/metrics.rs index 71e34e24c..55fdecc36 100644 --- a/client/beefy/src/metrics.rs +++ b/client/beefy/src/metrics.rs @@ -30,6 +30,8 @@ pub(crate) struct Metrics { pub beefy_round_concluded: Gauge, /// Best block finalized by BEEFY pub beefy_best_block: Gauge, + /// Best block BEEFY voted on + pub beefy_best_voted: Gauge, /// Next block BEEFY should vote on pub beefy_should_vote_on: Gauge, /// Number of sessions with lagging signed commitment on mandatory block @@ -61,6 +63,10 @@ impl Metrics { Gauge::new("substrate_beefy_best_block", "Best block finalized by BEEFY")?, registry, )?, + beefy_best_voted: register( + Gauge::new("substrate_beefy_best_voted", "Best block voted on by BEEFY")?, + registry, + )?, beefy_should_vote_on: register( Gauge::new("substrate_beefy_should_vote_on", "Next block, BEEFY should vote on")?, registry, diff --git a/client/beefy/src/round.rs b/client/beefy/src/round.rs index 647d42110..9ad4e5d02 100644 --- a/client/beefy/src/round.rs +++ b/client/beefy/src/round.rs @@ -19,39 +19,33 @@ use crate::LOG_TARGET; use beefy_primitives::{ - crypto::{Public, Signature}, - ValidatorSet, ValidatorSetId, + crypto::{AuthorityId, Public, Signature}, + Commitment, SignedCommitment, ValidatorSet, ValidatorSetId, VoteMessage, }; use codec::{Decode, Encode}; -use log::{debug, trace}; +use log::debug; use sp_runtime::traits::{Block, NumberFor}; -use std::{collections::BTreeMap, hash::Hash}; +use std::collections::BTreeMap; /// Tracks for each round which validators have voted/signed and /// whether the local `self` validator has voted/signed. /// /// Does not do any validation on votes or signatures, layers above need to handle that (gossip). #[derive(Debug, Decode, Default, Encode, PartialEq)] -struct RoundTracker { - self_vote: bool, +pub(crate) struct RoundTracker { votes: BTreeMap, } impl RoundTracker { - fn add_vote(&mut self, vote: (Public, Signature), self_vote: bool) -> bool { + fn add_vote(&mut self, vote: (Public, Signature)) -> bool { if self.votes.contains_key(&vote.0) { return false } - self.self_vote = self.self_vote || self_vote; self.votes.insert(vote.0, vote.1); true } - fn has_self_vote(&self) -> bool { - self.self_vote - } - fn is_done(&self, threshold: usize) -> bool { self.votes.len() >= threshold } @@ -63,27 +57,37 @@ pub fn threshold(authorities: usize) -> usize { authorities - faulty } +#[derive(Debug, PartialEq)] +pub enum VoteImportResult { + Ok, + RoundConcluded(SignedCommitment, Signature>), + Equivocation, /* TODO: (EquivocationProof, Public, Signature>) */ + Invalid, + Stale, +} + /// Keeps track of all voting rounds (block numbers) within a session. /// Only round numbers > `best_done` are of interest, all others are considered stale. /// /// Does not do any validation on votes or signatures, layers above need to handle that (gossip). #[derive(Debug, Decode, Encode, PartialEq)] -pub(crate) struct Rounds { - rounds: BTreeMap<(Payload, NumberFor), RoundTracker>, +pub(crate) struct Rounds { + rounds: BTreeMap>, RoundTracker>, + previous_votes: BTreeMap<(Public, NumberFor), VoteMessage, Public, Signature>>, session_start: NumberFor, validator_set: ValidatorSet, mandatory_done: bool, best_done: Option>, } -impl Rounds +impl Rounds where - P: Ord + Hash + Clone, B: Block, { pub(crate) fn new(session_start: NumberFor, validator_set: ValidatorSet) -> Self { Rounds { rounds: BTreeMap::new(), + previous_votes: BTreeMap::new(), session_start, validator_set, mandatory_done: false, @@ -111,60 +115,77 @@ where self.mandatory_done } - pub(crate) fn should_self_vote(&self, round: &(P, NumberFor)) -> bool { - Some(round.1) > self.best_done && - self.rounds.get(round).map(|tracker| !tracker.has_self_vote()).unwrap_or(true) - } - pub(crate) fn add_vote( &mut self, - round: &(P, NumberFor), - vote: (Public, Signature), - self_vote: bool, - ) -> bool { - let num = round.1; + vote: VoteMessage, AuthorityId, Signature>, + ) -> VoteImportResult { + let num = vote.commitment.block_number; + let vote_key = (vote.id.clone(), num); + if num < self.session_start || Some(num) <= self.best_done { debug!(target: LOG_TARGET, "🥩 received vote for old stale round {:?}, ignoring", num); - false - } else if !self.validators().iter().any(|id| vote.0 == *id) { + return VoteImportResult::Stale + } else if vote.commitment.validator_set_id != self.validator_set_id() { + debug!( + target: LOG_TARGET, + "🥩 expected set_id {:?}, ignoring vote {:?}.", + self.validator_set_id(), + vote, + ); + return VoteImportResult::Invalid + } else if !self.validators().iter().any(|id| &vote.id == id) { debug!( target: LOG_TARGET, "🥩 received vote {:?} from validator that is not in the validator set, ignoring", vote ); - false + return VoteImportResult::Invalid + } + + if let Some(previous_vote) = self.previous_votes.get(&vote_key) { + // is the same public key voting for a different payload? + if previous_vote.commitment.payload != vote.commitment.payload { + debug!( + target: LOG_TARGET, + "🥩 detected equivocated vote: 1st: {:?}, 2nd: {:?}", previous_vote, vote + ); + // TODO: build `EquivocationProof` and return it here. + return VoteImportResult::Equivocation + } } else { - self.rounds.entry(round.clone()).or_default().add_vote(vote, self_vote) + // this is the first vote sent by `id` for `num`, all good + self.previous_votes.insert(vote_key, vote.clone()); } + + // add valid vote + let round = self.rounds.entry(vote.commitment.clone()).or_default(); + if round.add_vote((vote.id, vote.signature)) && + round.is_done(threshold(self.validator_set.len())) + { + if let Some(round) = self.rounds.remove_entry(&vote.commitment) { + return VoteImportResult::RoundConcluded(self.signed_commitment(round)) + } + } + VoteImportResult::Ok } - pub(crate) fn should_conclude( + fn signed_commitment( &mut self, - round: &(P, NumberFor), - ) -> Option>> { - let done = self - .rounds - .get(round) - .map(|tracker| tracker.is_done(threshold(self.validator_set.len()))) - .unwrap_or(false); - trace!(target: LOG_TARGET, "🥩 Round #{} done: {}", round.1, done); - - if done { - let signatures = self.rounds.remove(round)?.votes; - Some( - self.validators() - .iter() - .map(|authority_id| signatures.get(authority_id).cloned()) - .collect(), - ) - } else { - None - } + round: (Commitment>, RoundTracker), + ) -> SignedCommitment, Signature> { + let votes = round.1.votes; + let signatures = self + .validators() + .iter() + .map(|authority_id| votes.get(authority_id).cloned()) + .collect(); + SignedCommitment { commitment: round.0, signatures } } pub(crate) fn conclude(&mut self, round_num: NumberFor) { // Remove this and older (now stale) rounds. - self.rounds.retain(|&(_, number), _| number > round_num); + self.rounds.retain(|commitment, _| commitment.block_number > round_num); + self.previous_votes.retain(|&(_, number), _| number > round_num); self.mandatory_done = self.mandatory_done || round_num == self.session_start; self.best_done = self.best_done.max(Some(round_num)); debug!(target: LOG_TARGET, "🥩 Concluded round #{}", round_num); @@ -174,16 +195,17 @@ where #[cfg(test)] mod tests { use sc_network_test::Block; - use sp_core::H256; - use beefy_primitives::{crypto::Public, ValidatorSet}; + use beefy_primitives::{ + crypto::Public, keyring::Keyring, known_payloads::MMR_ROOT_ID, Commitment, Payload, + SignedCommitment, ValidatorSet, VoteMessage, + }; - use super::{threshold, Block as BlockT, Hash, RoundTracker, Rounds}; - use crate::keystore::tests::Keyring; + use super::{threshold, Block as BlockT, RoundTracker, Rounds}; + use crate::round::VoteImportResult; - impl Rounds + impl Rounds where - P: Ord + Hash + Clone, B: BlockT, { pub(crate) fn test_set_mandatory_done(&mut self, done: bool) { @@ -197,26 +219,18 @@ mod tests { let bob_vote = (Keyring::Bob.public(), Keyring::Bob.sign(b"I am committed")); let threshold = 2; - // self vote not added yet - assert!(!rt.has_self_vote()); - // adding new vote allowed - assert!(rt.add_vote(bob_vote.clone(), false)); + assert!(rt.add_vote(bob_vote.clone())); // adding existing vote not allowed - assert!(!rt.add_vote(bob_vote, false)); - - // self vote still not added yet - assert!(!rt.has_self_vote()); + assert!(!rt.add_vote(bob_vote)); // vote is not done assert!(!rt.is_done(threshold)); let alice_vote = (Keyring::Alice.public(), Keyring::Alice.sign(b"I am committed")); // adding new vote (self vote this time) allowed - assert!(rt.add_vote(alice_vote, true)); + assert!(rt.add_vote(alice_vote)); - // self vote registered - assert!(rt.has_self_vote()); // vote is now done assert!(rt.is_done(threshold)); } @@ -242,7 +256,7 @@ mod tests { .unwrap(); let session_start = 1u64.into(); - let rounds = Rounds::::new(session_start, validators); + let rounds = Rounds::::new(session_start, validators); assert_eq!(42, rounds.validator_set_id()); assert_eq!(1, rounds.session_start()); @@ -266,65 +280,56 @@ mod tests { Default::default(), ) .unwrap(); - let round = (H256::from_low_u64_le(1), 1); + let validator_set_id = validators.id(); let session_start = 1u64.into(); - let mut rounds = Rounds::::new(session_start, validators); - - // no self vote yet, should self vote - assert!(rounds.should_self_vote(&round)); - + let mut rounds = Rounds::::new(session_start, validators); + + let payload = Payload::from_single_entry(MMR_ROOT_ID, vec![]); + let block_number = 1; + let commitment = Commitment { block_number, payload, validator_set_id }; + let mut vote = VoteMessage { + id: Keyring::Alice.public(), + commitment: commitment.clone(), + signature: Keyring::Alice.sign(b"I am committed"), + }; // add 1st good vote - assert!(rounds.add_vote( - &round, - (Keyring::Alice.public(), Keyring::Alice.sign(b"I am committed")), - true - )); - // round not concluded - assert!(rounds.should_conclude(&round).is_none()); - // self vote already present, should not self vote - assert!(!rounds.should_self_vote(&round)); - - // double voting not allowed - assert!(!rounds.add_vote( - &round, - (Keyring::Alice.public(), Keyring::Alice.sign(b"I am committed")), - true - )); + assert_eq!(rounds.add_vote(vote.clone()), VoteImportResult::Ok); + // double voting (same vote), ok, no effect + assert_eq!(rounds.add_vote(vote.clone()), VoteImportResult::Ok); + + vote.id = Keyring::Dave.public(); + vote.signature = Keyring::Dave.sign(b"I am committed"); // invalid vote (Dave is not a validator) - assert!(!rounds.add_vote( - &round, - (Keyring::Dave.public(), Keyring::Dave.sign(b"I am committed")), - false - )); - assert!(rounds.should_conclude(&round).is_none()); + assert_eq!(rounds.add_vote(vote.clone()), VoteImportResult::Invalid); + vote.id = Keyring::Bob.public(); + vote.signature = Keyring::Bob.sign(b"I am committed"); // add 2nd good vote - assert!(rounds.add_vote( - &round, - (Keyring::Bob.public(), Keyring::Bob.sign(b"I am committed")), - false - )); - // round not concluded - assert!(rounds.should_conclude(&round).is_none()); - - // add 3rd good vote - assert!(rounds.add_vote( - &round, - (Keyring::Charlie.public(), Keyring::Charlie.sign(b"I am committed")), - false - )); - // round concluded - assert!(rounds.should_conclude(&round).is_some()); - rounds.conclude(round.1); + assert_eq!(rounds.add_vote(vote.clone()), VoteImportResult::Ok); + vote.id = Keyring::Charlie.public(); + vote.signature = Keyring::Charlie.sign(b"I am committed"); + // add 3rd good vote -> round concluded -> signatures present + assert_eq!( + rounds.add_vote(vote.clone()), + VoteImportResult::RoundConcluded(SignedCommitment { + commitment, + signatures: vec![ + Some(Keyring::Alice.sign(b"I am committed")), + Some(Keyring::Bob.sign(b"I am committed")), + Some(Keyring::Charlie.sign(b"I am committed")), + None, + ] + }) + ); + rounds.conclude(block_number); + + vote.id = Keyring::Eve.public(); + vote.signature = Keyring::Eve.sign(b"I am committed"); // Eve is a validator, but round was concluded, adding vote disallowed - assert!(!rounds.add_vote( - &round, - (Keyring::Eve.public(), Keyring::Eve.sign(b"I am committed")), - false - )); + assert_eq!(rounds.add_vote(vote), VoteImportResult::Stale); } #[test] @@ -336,30 +341,39 @@ mod tests { 42, ) .unwrap(); - let alice = (Keyring::Alice.public(), Keyring::Alice.sign(b"I am committed")); + let validator_set_id = validators.id(); + // active rounds starts at block 10 let session_start = 10u64.into(); - let mut rounds = Rounds::::new(session_start, validators); - - let mut vote = (H256::from_low_u64_le(1), 9); + let mut rounds = Rounds::::new(session_start, validators); + + // vote on round 9 + let block_number = 9; + let payload = Payload::from_single_entry(MMR_ROOT_ID, vec![]); + let commitment = Commitment { block_number, payload, validator_set_id }; + let mut vote = VoteMessage { + id: Keyring::Alice.public(), + commitment, + signature: Keyring::Alice.sign(b"I am committed"), + }; // add vote for previous session, should fail - assert!(!rounds.add_vote(&vote, alice.clone(), true)); + assert_eq!(rounds.add_vote(vote.clone()), VoteImportResult::Stale); // no votes present assert!(rounds.rounds.is_empty()); // simulate 11 was concluded rounds.best_done = Some(11); // add votes for current session, but already concluded rounds, should fail - vote.1 = 10; - assert!(!rounds.add_vote(&vote, alice.clone(), true)); - vote.1 = 11; - assert!(!rounds.add_vote(&vote, alice.clone(), true)); + vote.commitment.block_number = 10; + assert_eq!(rounds.add_vote(vote.clone()), VoteImportResult::Stale); + vote.commitment.block_number = 11; + assert_eq!(rounds.add_vote(vote.clone()), VoteImportResult::Stale); // no votes present assert!(rounds.rounds.is_empty()); - // add good vote - vote.1 = 12; - assert!(rounds.add_vote(&vote, alice, true)); + // add vote for active round 12 + vote.commitment.block_number = 12; + assert_eq!(rounds.add_vote(vote), VoteImportResult::Ok); // good vote present assert_eq!(rounds.rounds.len(), 1); } @@ -369,88 +383,73 @@ mod tests { sp_tracing::try_init_simple(); let validators = ValidatorSet::::new( - vec![ - Keyring::Alice.public(), - Keyring::Bob.public(), - Keyring::Charlie.public(), - Keyring::Dave.public(), - ], + vec![Keyring::Alice.public(), Keyring::Bob.public(), Keyring::Charlie.public()], Default::default(), ) .unwrap(); + let validator_set_id = validators.id(); let session_start = 1u64.into(); - let mut rounds = Rounds::::new(session_start, validators); - - // round 1 - assert!(rounds.add_vote( - &(H256::from_low_u64_le(1), 1), - (Keyring::Alice.public(), Keyring::Alice.sign(b"I am committed")), - true, - )); - assert!(rounds.add_vote( - &(H256::from_low_u64_le(1), 1), - (Keyring::Bob.public(), Keyring::Bob.sign(b"I am committed")), - false, - )); - assert!(rounds.add_vote( - &(H256::from_low_u64_le(1), 1), - (Keyring::Charlie.public(), Keyring::Charlie.sign(b"I am committed")), - false, - )); - - // round 2 - assert!(rounds.add_vote( - &(H256::from_low_u64_le(2), 2), - (Keyring::Alice.public(), Keyring::Alice.sign(b"I am again committed")), - true, - )); - assert!(rounds.add_vote( - &(H256::from_low_u64_le(2), 2), - (Keyring::Bob.public(), Keyring::Bob.sign(b"I am again committed")), - false, - )); - assert!(rounds.add_vote( - &(H256::from_low_u64_le(2), 2), - (Keyring::Charlie.public(), Keyring::Charlie.sign(b"I am again committed")), - false, - )); - - // round 3 - assert!(rounds.add_vote( - &(H256::from_low_u64_le(3), 3), - (Keyring::Alice.public(), Keyring::Alice.sign(b"I am still committed")), - true, - )); - assert!(rounds.add_vote( - &(H256::from_low_u64_le(3), 3), - (Keyring::Bob.public(), Keyring::Bob.sign(b"I am still committed")), - false, - )); - assert!(rounds.add_vote( - &(H256::from_low_u64_le(3), 3), - (Keyring::Charlie.public(), Keyring::Charlie.sign(b"I am still committed")), - false, - )); - assert_eq!(3, rounds.rounds.len()); - - // conclude unknown round - assert!(rounds.should_conclude(&(H256::from_low_u64_le(5), 5)).is_none()); - assert_eq!(3, rounds.rounds.len()); - - // conclude round 2 - let signatures = rounds.should_conclude(&(H256::from_low_u64_le(2), 2)).unwrap(); - rounds.conclude(2); + let mut rounds = Rounds::::new(session_start, validators); + + let payload = Payload::from_single_entry(MMR_ROOT_ID, vec![]); + let commitment = Commitment { block_number: 1, payload, validator_set_id }; + let mut alice_vote = VoteMessage { + id: Keyring::Alice.public(), + commitment: commitment.clone(), + signature: Keyring::Alice.sign(b"I am committed"), + }; + let mut bob_vote = VoteMessage { + id: Keyring::Bob.public(), + commitment: commitment.clone(), + signature: Keyring::Bob.sign(b"I am committed"), + }; + let mut charlie_vote = VoteMessage { + id: Keyring::Charlie.public(), + commitment, + signature: Keyring::Charlie.sign(b"I am committed"), + }; + let expected_signatures = vec![ + Some(Keyring::Alice.sign(b"I am committed")), + Some(Keyring::Bob.sign(b"I am committed")), + Some(Keyring::Charlie.sign(b"I am committed")), + ]; + + // round 1 - only 2 out of 3 vote + assert_eq!(rounds.add_vote(alice_vote.clone()), VoteImportResult::Ok); + assert_eq!(rounds.add_vote(charlie_vote.clone()), VoteImportResult::Ok); + // should be 1 active round assert_eq!(1, rounds.rounds.len()); + // round 2 - only Charlie votes + charlie_vote.commitment.block_number = 2; + assert_eq!(rounds.add_vote(charlie_vote.clone()), VoteImportResult::Ok); + // should be 2 active rounds + assert_eq!(2, rounds.rounds.len()); + + // round 3 - all validators vote -> round is concluded + alice_vote.commitment.block_number = 3; + bob_vote.commitment.block_number = 3; + charlie_vote.commitment.block_number = 3; + assert_eq!(rounds.add_vote(alice_vote.clone()), VoteImportResult::Ok); + assert_eq!(rounds.add_vote(bob_vote.clone()), VoteImportResult::Ok); assert_eq!( - signatures, - vec![ - Some(Keyring::Alice.sign(b"I am again committed")), - Some(Keyring::Bob.sign(b"I am again committed")), - Some(Keyring::Charlie.sign(b"I am again committed")), - None - ] + rounds.add_vote(charlie_vote.clone()), + VoteImportResult::RoundConcluded(SignedCommitment { + commitment: charlie_vote.commitment, + signatures: expected_signatures + }) ); + // should be only 2 active since this one auto-concluded + assert_eq!(2, rounds.rounds.len()); + + // conclude round 2 + rounds.conclude(2); + // should be no more active rounds since 2 was officially concluded and round "1" is stale + assert!(rounds.rounds.is_empty()); + + // conclude round 3 + rounds.conclude(3); + assert!(rounds.previous_votes.is_empty()); } } diff --git a/client/beefy/src/tests.rs b/client/beefy/src/tests.rs index 008137d9a..69cc9b2f6 100644 --- a/client/beefy/src/tests.rs +++ b/client/beefy/src/tests.rs @@ -26,12 +26,12 @@ use crate::{ }, gossip_protocol_name, justification::*, - keystore::tests::Keyring as BeefyKeyring, load_or_init_voter_state, wait_for_runtime_pallet, BeefyRPCLinks, BeefyVoterLinks, KnownPeers, PersistedState, }; use beefy_primitives::{ crypto::{AuthorityId, Signature}, + keyring::Keyring as BeefyKeyring, known_payloads, mmr::MmrRootProvider, BeefyApi, Commitment, ConsensusLog, MmrRootHash, Payload, SignedCommitment, ValidatorSet, diff --git a/client/beefy/src/worker.rs b/client/beefy/src/worker.rs index 4d057bffc..be5443d3d 100644 --- a/client/beefy/src/worker.rs +++ b/client/beefy/src/worker.rs @@ -26,13 +26,13 @@ use crate::{ keystore::BeefyKeystore, metric_inc, metric_set, metrics::Metrics, - round::Rounds, + round::{Rounds, VoteImportResult}, BeefyVoterLinks, LOG_TARGET, }; use beefy_primitives::{ crypto::{AuthorityId, Signature}, - Commitment, ConsensusLog, Payload, PayloadProvider, SignedCommitment, ValidatorSet, - VersionedFinalityProof, VoteMessage, BEEFY_ENGINE_ID, + Commitment, ConsensusLog, PayloadProvider, ValidatorSet, VersionedFinalityProof, VoteMessage, + BEEFY_ENGINE_ID, }; use codec::{Codec, Decode, Encode}; use futures::{stream::Fuse, FutureExt, StreamExt}; @@ -55,6 +55,7 @@ use std::{ marker::PhantomData, sync::Arc, }; + /// Bound for the number of buffered future voting rounds. const MAX_BUFFERED_VOTE_ROUNDS: usize = 600; /// Bound for the number of buffered votes per round number. @@ -83,17 +84,14 @@ pub(crate) struct VoterOracle { /// 3. lagging behind GRANDPA: queue has [1, N] elements, where all `mandatory_done == false`. /// In this state, everytime a session gets its mandatory block BEEFY finalized, it's /// popped off the queue, eventually getting to state `2. up-to-date`. - sessions: VecDeque>, + sessions: VecDeque>, /// Min delta in block numbers between two blocks, BEEFY should vote on. min_block_delta: u32, } impl VoterOracle { /// Verify provided `sessions` satisfies requirements, then build `VoterOracle`. - pub fn checked_new( - sessions: VecDeque>, - min_block_delta: u32, - ) -> Option { + pub fn checked_new(sessions: VecDeque>, min_block_delta: u32) -> Option { let mut prev_start = Zero::zero(); let mut prev_validator_id = None; // verifies the @@ -136,13 +134,13 @@ impl VoterOracle { // Return reference to rounds pertaining to first session in the queue. // Voting will always happen at the head of the queue. - fn active_rounds(&self) -> Option<&Rounds> { + fn active_rounds(&self) -> Option<&Rounds> { self.sessions.front() } // Return mutable reference to rounds pertaining to first session in the queue. // Voting will always happen at the head of the queue. - fn active_rounds_mut(&mut self) -> Option<&mut Rounds> { + fn active_rounds_mut(&mut self) -> Option<&mut Rounds> { self.sessions.front_mut() } @@ -157,7 +155,7 @@ impl VoterOracle { } /// Add new observed session to the Oracle. - pub fn add_session(&mut self, rounds: Rounds) { + pub fn add_session(&mut self, rounds: Rounds) { self.sessions.push_back(rounds); // Once we add a new session we can drop/prune previous session if it's been finalized. self.try_prune(); @@ -264,6 +262,8 @@ pub(crate) struct PersistedState { best_grandpa_block_header: ::Header, /// Best block a BEEFY voting round has been concluded for. best_beefy_block: NumberFor, + /// Best block we voted on. + best_voted: NumberFor, /// Chooses which incoming votes to accept and which votes to generate. /// Keeps track of voting seen for current and future rounds. voting_oracle: VoterOracle, @@ -273,12 +273,13 @@ impl PersistedState { pub fn checked_new( grandpa_header: ::Header, best_beefy: NumberFor, - sessions: VecDeque>, + sessions: VecDeque>, min_block_delta: u32, ) -> Option { VoterOracle::checked_new(sessions, min_block_delta).map(|voting_oracle| PersistedState { best_grandpa_block_header: grandpa_header, best_beefy_block: best_beefy, + best_voted: Zero::zero(), voting_oracle, }) } @@ -381,7 +382,7 @@ where &self.persisted_state.voting_oracle } - fn active_rounds(&mut self) -> Option<&Rounds> { + fn active_rounds(&mut self) -> Option<&Rounds> { self.persisted_state.voting_oracle.active_rounds() } @@ -486,12 +487,9 @@ where ) -> Result<(), Error> { let block_num = vote.commitment.block_number; let best_grandpa = self.best_grandpa_block(); + self.gossip_validator.note_round(block_num); match self.voting_oracle().triage_round(block_num, best_grandpa)? { - RoundAction::Process => self.handle_vote( - (vote.commitment.payload, vote.commitment.block_number), - (vote.id, vote.signature), - false, - )?, + RoundAction::Process => self.handle_vote(vote)?, RoundAction::Enqueue => { debug!(target: LOG_TARGET, "🥩 Buffer vote for round: {:?}.", block_num); if self.pending_votes.len() < MAX_BUFFERED_VOTE_ROUNDS { @@ -546,57 +544,47 @@ where fn handle_vote( &mut self, - round: (Payload, NumberFor), - vote: (AuthorityId, Signature), - self_vote: bool, + vote: VoteMessage, AuthorityId, Signature>, ) -> Result<(), Error> { - self.gossip_validator.note_round(round.1); - let rounds = self .persisted_state .voting_oracle .active_rounds_mut() .ok_or(Error::UninitSession)?; - if rounds.add_vote(&round, vote, self_vote) { - if let Some(signatures) = rounds.should_conclude(&round) { - self.gossip_validator.conclude_round(round.1); - - let block_num = round.1; - let commitment = Commitment { - payload: round.0, - block_number: block_num, - validator_set_id: rounds.validator_set_id(), - }; - - let finality_proof = - VersionedFinalityProof::V1(SignedCommitment { commitment, signatures }); - - metric_set!(self, beefy_round_concluded, block_num); + let block_number = vote.commitment.block_number; + match rounds.add_vote(vote) { + VoteImportResult::RoundConcluded(signed_commitment) => { + self.gossip_validator.conclude_round(block_number); + metric_set!(self, beefy_round_concluded, block_number); + let finality_proof = VersionedFinalityProof::V1(signed_commitment); info!( target: LOG_TARGET, - "🥩 Round #{} concluded, finality_proof: {:?}.", round.1, finality_proof + "🥩 Round #{} concluded, finality_proof: {:?}.", block_number, finality_proof ); - // We created the `finality_proof` and know to be valid. // New state is persisted after finalization. self.finalize(finality_proof)?; - } else { - let mandatory_round = self + }, + VoteImportResult::Ok => { + // Persist state after handling mandatory block vote. + if self .voting_oracle() .mandatory_pending() - .map(|p| p.0 == round.1) - .unwrap_or(false); - // Persist state after handling self vote to avoid double voting in case - // of voter restarts. - // Also persist state after handling mandatory block vote. - if self_vote || mandatory_round { + .map(|(mandatory_num, _)| mandatory_num == block_number) + .unwrap_or(false) + { crate::aux_schema::write_voter_state(&*self.backend, &self.persisted_state) .map_err(|e| Error::Backend(e.to_string()))?; } - } - } + }, + VoteImportResult::Equivocation => { + // TODO: report returned `EquivocationProof` to chain through `pallet-beefy`. + () + }, + VoteImportResult::Invalid | VoteImportResult::Stale => (), + }; Ok(()) } @@ -695,11 +683,7 @@ where for (num, votes) in votes_to_handle.into_iter() { debug!(target: LOG_TARGET, "🥩 Handle buffered votes for: {:?}.", num); for v in votes.into_iter() { - if let Err(err) = self.handle_vote( - (v.commitment.payload, v.commitment.block_number), - (v.id, v.signature), - false, - ) { + if let Err(err) = self.handle_vote(v) { error!(target: LOG_TARGET, "🥩 Error handling buffered vote: {}", err); }; } @@ -716,7 +700,9 @@ where .voting_target(self.best_beefy_block(), self.best_grandpa_block()) { metric_set!(self, beefy_should_vote_on, target); - self.do_vote(target)?; + if target > self.persisted_state.best_voted { + self.do_vote(target)?; + } } Ok(()) } @@ -766,13 +752,6 @@ where .voting_oracle .active_rounds_mut() .ok_or(Error::UninitSession)?; - if !rounds.should_self_vote(&(payload.clone(), target_number)) { - debug!( - target: LOG_TARGET, - "🥩 Don't double vote for block number: {:?}", target_number - ); - return Ok(()) - } let (validators, validator_set_id) = (rounds.validators(), rounds.validator_set_id()); let authority_id = if let Some(id) = self.key_store.authority_id(validators) { @@ -812,17 +791,17 @@ where debug!(target: LOG_TARGET, "🥩 Sent vote message: {:?}", message); - if let Err(err) = self.handle_vote( - (message.commitment.payload, message.commitment.block_number), - (message.id, message.signature), - true, - ) { + if let Err(err) = self.handle_vote(message) { error!(target: LOG_TARGET, "🥩 Error handling self vote: {}", err); } self.gossip_engine.gossip_message(topic::(), encoded_message, false); - Ok(()) + // Persist state after vote to avoid double voting in case of voter restarts. + self.persisted_state.best_voted = target_number; + metric_set!(self, beefy_best_voted, target_number); + crate::aux_schema::write_voter_state(&*self.backend, &self.persisted_state) + .map_err(|e| Error::Backend(e.to_string())) } fn process_new_state(&mut self) { @@ -848,8 +827,7 @@ where /// Main loop for BEEFY worker. /// - /// Wait for BEEFY runtime pallet to be available, then start the main async loop - /// which is driven by finality notifications and gossiped votes. + /// Run the main async loop which is driven by finality notifications and gossiped votes. pub(crate) async fn run( mut self, mut block_import_justif: Fuse>>, @@ -992,14 +970,15 @@ pub(crate) mod tests { use super::*; use crate::{ communication::notification::{BeefyBestBlockStream, BeefyVersionedFinalityProofStream}, - keystore::tests::Keyring, tests::{ create_beefy_keystore, get_beefy_streams, make_beefy_ids, BeefyPeer, BeefyTestNet, TestApi, }, BeefyRPCLinks, KnownPeers, }; - use beefy_primitives::{known_payloads, mmr::MmrRootProvider}; + use beefy_primitives::{ + keyring::Keyring, known_payloads, mmr::MmrRootProvider, Payload, SignedCommitment, + }; use futures::{future::poll_fn, task::Poll}; use parking_lot::Mutex; use sc_client_api::{Backend as BackendT, HeaderBackend}; @@ -1007,7 +986,7 @@ pub(crate) mod tests { use sc_network_test::TestNetFactory; use sp_api::HeaderT; use sp_blockchain::Backend as BlockchainBackendT; - use sp_runtime::traits::{One, Zero}; + use sp_runtime::traits::One; use substrate_test_runtime_client::{ runtime::{Block, Digest, DigestItem, Header, H256}, Backend, @@ -1018,7 +997,7 @@ pub(crate) mod tests { &self.voting_oracle } - pub fn active_round(&self) -> Option<&Rounds> { + pub fn active_round(&self) -> Option<&Rounds> { self.voting_oracle.active_rounds() } @@ -1032,7 +1011,7 @@ pub(crate) mod tests { } impl VoterOracle { - pub fn sessions(&self) -> &VecDeque> { + pub fn sessions(&self) -> &VecDeque> { &self.sessions } } @@ -1090,7 +1069,7 @@ pub(crate) mod tests { min_block_delta, ) .unwrap(); - let payload_provider = MmrRootProvider::new(api); + let payload_provider = MmrRootProvider::new(api.clone()); let worker_params = crate::worker::WorkerParams { backend, payload_provider, diff --git a/primitives/beefy/Cargo.toml b/primitives/beefy/Cargo.toml index 6a22f0383..e4154071d 100644 --- a/primitives/beefy/Cargo.toml +++ b/primitives/beefy/Cargo.toml @@ -22,6 +22,8 @@ sp-io = { version = "7.0.0", default-features = false, path = "../io" } sp-mmr-primitives = { version = "4.0.0-dev", default-features = false, path = "../merkle-mountain-range" } sp-runtime = { version = "7.0.0", default-features = false, path = "../runtime" } sp-std = { version = "5.0.0", default-features = false, path = "../std" } +strum = { version = "0.24.1", features = ["derive"] } +lazy_static = "1.4.0" [dev-dependencies] array-bytes = "4.1" diff --git a/primitives/beefy/src/commitment.rs b/primitives/beefy/src/commitment.rs index 5765ff360..8642a83aa 100644 --- a/primitives/beefy/src/commitment.rs +++ b/primitives/beefy/src/commitment.rs @@ -77,6 +77,7 @@ where self.validator_set_id .cmp(&other.validator_set_id) .then_with(|| self.block_number.cmp(&other.block_number)) + .then_with(|| self.payload.cmp(&other.payload)) } } diff --git a/primitives/beefy/src/lib.rs b/primitives/beefy/src/lib.rs index 8c219040b..08467a723 100644 --- a/primitives/beefy/src/lib.rs +++ b/primitives/beefy/src/lib.rs @@ -173,7 +173,7 @@ pub enum ConsensusLog { /// /// A vote message is a direct vote created by a BEEFY node on every voting round /// and is gossiped to its peers. -#[derive(Debug, Decode, Encode, TypeInfo)] +#[derive(Clone, Debug, Decode, Encode, PartialEq, TypeInfo)] pub struct VoteMessage { /// Commit to information extracted from a finalized block pub commitment: Commitment, @@ -209,6 +209,83 @@ sp_api::decl_runtime_apis! { } } +#[cfg(feature = "std")] +/// Test accounts using [`crate::crypto`] types. +pub mod keyring { + use super::*; + use sp_core::{ecdsa, keccak_256, Pair}; + use std::collections::HashMap; + use strum::IntoEnumIterator; + + /// Set of test accounts using [`crate::crypto`] types. + #[allow(missing_docs)] + #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, strum::Display, strum::EnumIter)] + pub enum Keyring { + Alice, + Bob, + Charlie, + Dave, + Eve, + Ferdie, + One, + Two, + } + + impl Keyring { + /// Sign `msg`. + pub fn sign(self, msg: &[u8]) -> crypto::Signature { + // todo: use custom signature hashing type + let msg = keccak_256(msg); + ecdsa::Pair::from(self).sign_prehashed(&msg).into() + } + + /// Return key pair. + pub fn pair(self) -> crypto::Pair { + ecdsa::Pair::from_string(self.to_seed().as_str(), None).unwrap().into() + } + + /// Return public key. + pub fn public(self) -> crypto::Public { + self.pair().public() + } + + /// Return seed string. + pub fn to_seed(self) -> String { + format!("//{}", self) + } + + /// Get Keyring from public key. + pub fn from_public(who: &crypto::Public) -> Option { + Self::iter().find(|&k| &crypto::Public::from(k) == who) + } + } + + lazy_static::lazy_static! { + static ref PRIVATE_KEYS: HashMap = + Keyring::iter().map(|i| (i, i.pair())).collect(); + static ref PUBLIC_KEYS: HashMap = + PRIVATE_KEYS.iter().map(|(&name, pair)| (name, pair.public())).collect(); + } + + impl From for crypto::Pair { + fn from(k: Keyring) -> Self { + k.pair() + } + } + + impl From for ecdsa::Pair { + fn from(k: Keyring) -> Self { + k.pair().into() + } + } + + impl From for crypto::Public { + fn from(k: Keyring) -> Self { + (*PUBLIC_KEYS).get(&k).cloned().unwrap() + } + } +} + #[cfg(test)] mod tests { use super::*; From 47ecdb6ea686807479c4e5107dbdd8b5e855d843 Mon Sep 17 00:00:00 2001 From: Koute Date: Tue, 7 Feb 2023 00:40:50 +0900 Subject: [PATCH 091/558] Bump `wasmtime` to 5.0.0 (and a few other deps) (#13160) * Bump `wasmtime` to 4.0.0 (and a few other deps) * Use `Error::msg` instead of `anyhow!` * Bump `wasmtime` to 5.0.0 * Update `Cargo.lock` * Add `wasmtime` feature to `sp-wasm-interface` dependency --- Cargo.lock | 159 ++++++++---------- client/executor/wasmtime/Cargo.toml | 11 +- client/executor/wasmtime/src/imports.rs | 4 +- .../executor/wasmtime/src/instance_wrapper.rs | 34 ++-- client/executor/wasmtime/src/runtime.rs | 40 +++-- client/executor/wasmtime/src/tests.rs | 35 +++- .../host_function_interface.rs | 6 +- primitives/wasm-interface/Cargo.toml | 4 +- primitives/wasm-interface/src/lib.rs | 3 + utils/wasm-builder/Cargo.toml | 2 +- 10 files changed, 156 insertions(+), 142 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index afebaabd6..a74ad26e7 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -813,15 +813,16 @@ dependencies = [ [[package]] name = "cargo_metadata" -version = "0.14.2" +version = "0.15.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4acbb09d9ee8e23699b9634375c72795d095bf268439da88562cf9b501f181fa" +checksum = "982a0cf6a99c350d7246035613882e376d58cebe571785abc5da4f648d53ac0a" dependencies = [ "camino", "cargo-platform", "semver 1.0.16", "serde", "serde_json", + "thiserror", ] [[package]] @@ -1178,18 +1179,18 @@ checksum = "dcb25d077389e53838a8158c8e99174c5a9d902dee4904320db714f3c653ffba" [[package]] name = "cranelift-bforest" -version = "0.88.2" +version = "0.92.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "52056f6d0584484b57fa6c1a65c1fcb15f3780d8b6a758426d9e3084169b2ddd" +checksum = "2f3d54eab028f5805ae3b26fd60eca3f3a9cfb76b989d9bab173be3f61356cc3" dependencies = [ "cranelift-entity", ] [[package]] name = "cranelift-codegen" -version = "0.88.2" +version = "0.92.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "18fed94c8770dc25d01154c3ffa64ed0b3ba9d583736f305fed7beebe5d9cf74" +checksum = "2be1d5f2c3cca1efb691844bc1988b89c77291f13f778499a3f3c0cf49c0ed61" dependencies = [ "arrayvec 0.7.2", "bumpalo", @@ -1199,6 +1200,7 @@ dependencies = [ "cranelift-entity", "cranelift-isle", "gimli 0.26.2", + "hashbrown 0.12.3", "log", "regalloc2", "smallvec", @@ -1207,33 +1209,33 @@ dependencies = [ [[package]] name = "cranelift-codegen-meta" -version = "0.88.2" +version = "0.92.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1c451b81faf237d11c7e4f3165eeb6bac61112762c5cfe7b4c0fb7241474358f" +checksum = "3f9b1b1089750ce4005893af7ee00bb08a2cf1c9779999c0f7164cbc8ad2e0d2" dependencies = [ "cranelift-codegen-shared", ] [[package]] name = "cranelift-codegen-shared" -version = "0.88.2" +version = "0.92.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e7c940133198426d26128f08be2b40b0bd117b84771fd36798969c4d712d81fc" +checksum = "cc5fbaec51de47297fd7304986fd53c8c0030abbe69728a60d72e1c63559318d" [[package]] name = "cranelift-entity" -version = "0.88.2" +version = "0.92.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "87a0f1b2fdc18776956370cf8d9b009ded3f855350c480c1c52142510961f352" +checksum = "dab984c94593f876090fae92e984bdcc74d9b1acf740ab5f79036001c65cba13" dependencies = [ "serde", ] [[package]] name = "cranelift-frontend" -version = "0.88.2" +version = "0.92.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "34897538b36b216cc8dd324e73263596d51b8cf610da6498322838b2546baf8a" +checksum = "6e0cb3102d21a2fe5f3210af608748ddd0cd09825ac12d42dc56ed5ed8725fe0" dependencies = [ "cranelift-codegen", "log", @@ -1243,15 +1245,15 @@ dependencies = [ [[package]] name = "cranelift-isle" -version = "0.88.2" +version = "0.92.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1b2629a569fae540f16a76b70afcc87ad7decb38dc28fa6c648ac73b51e78470" +checksum = "72101dd1f441d629735143c41e00b3428f9267738176983ef588ff43382af0a0" [[package]] name = "cranelift-native" -version = "0.88.2" +version = "0.92.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "20937dab4e14d3e225c5adfc9c7106bafd4ac669bdb43027b911ff794c6fb318" +checksum = "c22b0d9fcbe3fc5a1af9e7021b44ce42b930bcefac446ce22e02e8f9a0d67120" dependencies = [ "cranelift-codegen", "libc", @@ -1260,9 +1262,9 @@ dependencies = [ [[package]] name = "cranelift-wasm" -version = "0.88.2" +version = "0.92.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "80fc2288957a94fd342a015811479de1837850924166d1f1856d8406e6f3609b" +checksum = "bddebe32fb14fbfd9efa5f130ffb8f4665795de019928dcd7247b136c46f9249" dependencies = [ "cranelift-codegen", "cranelift-entity", @@ -3251,12 +3253,6 @@ dependencies = [ "webrtc-util", ] -[[package]] -name = "io-lifetimes" -version = "0.7.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "59ce5ef949d49ee85593fc4d3f3f95ad61657076395cbbce23e2121fc5542074" - [[package]] name = "io-lifetimes" version = "1.0.3" @@ -3298,8 +3294,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "28dfb6c8100ccc63462345b67d1bbc3679177c75ee4bf59bf29c8b1d110b8189" dependencies = [ "hermit-abi 0.2.6", - "io-lifetimes 1.0.3", - "rustix 0.36.6", + "io-lifetimes", + "rustix", "windows-sys 0.42.0", ] @@ -4193,12 +4189,6 @@ dependencies = [ "nalgebra", ] -[[package]] -name = "linux-raw-sys" -version = "0.0.46" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d4d2456c373231a208ad294c33dc5bff30051eafd954cd4caae83a712b12854d" - [[package]] name = "linux-raw-sys" version = "0.1.4" @@ -4349,7 +4339,7 @@ version = "0.6.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b20a59d985586e4a5aef64564ac77299f8586d8be6cf9106a5a40207e8908efb" dependencies = [ - "rustix 0.36.6", + "rustix", ] [[package]] @@ -7550,9 +7540,9 @@ dependencies = [ [[package]] name = "regalloc2" -version = "0.3.2" +version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d43a209257d978ef079f3d446331d0f1794f5e0fc19b306a199983857833a779" +checksum = "300d4fbfb40c1c66a78ba3ddd41c1110247cf52f97b87d0f2fc9209bd49b030c" dependencies = [ "fxhash", "log", @@ -7747,20 +7737,6 @@ dependencies = [ "nom", ] -[[package]] -name = "rustix" -version = "0.35.13" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "727a1a6d65f786ec22df8a81ca3121107f235970dc1705ed681d3e6e8b9cd5f9" -dependencies = [ - "bitflags", - "errno", - "io-lifetimes 0.7.5", - "libc", - "linux-raw-sys 0.0.46", - "windows-sys 0.42.0", -] - [[package]] name = "rustix" version = "0.36.6" @@ -7769,9 +7745,9 @@ checksum = "4feacf7db682c6c329c4ede12649cd36ecab0f3be5b7d74e6a20304725db4549" dependencies = [ "bitflags", "errno", - "io-lifetimes 1.0.3", + "io-lifetimes", "libc", - "linux-raw-sys 0.1.4", + "linux-raw-sys", "windows-sys 0.42.0", ] @@ -8389,13 +8365,15 @@ dependencies = [ name = "sc-executor-wasmtime" version = "0.10.0-dev" dependencies = [ + "anyhow", + "cargo_metadata", "cfg-if", "libc", "log", "once_cell", "parity-scale-codec", "paste", - "rustix 0.35.13", + "rustix", "sc-allocator", "sc-executor-common", "sc-runtime-test", @@ -10418,6 +10396,7 @@ dependencies = [ name = "sp-wasm-interface" version = "7.0.0" dependencies = [ + "anyhow", "impl-trait-for-tuples", "log", "parity-scale-codec", @@ -11918,11 +11897,12 @@ dependencies = [ [[package]] name = "wasmparser" -version = "0.89.1" +version = "0.96.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ab5d3e08b13876f96dd55608d03cd4883a0545884932d5adf11925876c96daef" +checksum = "adde01ade41ab9a5d10ec8ed0bb954238cf8625b5cd5a13093d6de2ad9c2be1a" dependencies = [ "indexmap", + "url", ] [[package]] @@ -11936,9 +11916,9 @@ dependencies = [ [[package]] name = "wasmtime" -version = "1.0.2" +version = "5.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4ad5af6ba38311282f2a21670d96e78266e8c8e2f38cbcd52c254df6ccbc7731" +checksum = "4e5b183a159484980138cc05231419c536d395a7b25c1802091310ea2f74276a" dependencies = [ "anyhow", "bincode", @@ -11959,23 +11939,23 @@ dependencies = [ "wasmtime-environ", "wasmtime-jit", "wasmtime-runtime", - "windows-sys 0.36.1", + "windows-sys 0.42.0", ] [[package]] name = "wasmtime-asm-macros" -version = "1.0.2" +version = "5.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "45de63ddfc8b9223d1adc8f7b2ee5f35d1f6d112833934ad7ea66e4f4339e597" +checksum = "c0aeb1cb256d76cf07b20264c808351c8b525ece56de1ef4d93f87a0aaf342db" dependencies = [ "cfg-if", ] [[package]] name = "wasmtime-cache" -version = "1.0.2" +version = "5.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bcd849399d17d2270141cfe47fa0d91ee52d5f8ea9b98cf7ddde0d53e5f79882" +checksum = "830570847f905b8f6d2ca635c33cf42ce701dd8e4abd7d1806c631f8f06e9e4b" dependencies = [ "anyhow", "base64", @@ -11983,19 +11963,19 @@ dependencies = [ "directories-next", "file-per-thread-logger", "log", - "rustix 0.35.13", + "rustix", "serde", - "sha2 0.9.9", + "sha2 0.10.6", "toml", - "windows-sys 0.36.1", + "windows-sys 0.42.0", "zstd", ] [[package]] name = "wasmtime-cranelift" -version = "1.0.2" +version = "5.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4bd91339b742ff20bfed4532a27b73c86b5bcbfedd6bea2dcdf2d64471e1b5c6" +checksum = "2f7695d3814dcb508bf4d1c181a86ea6b97a209f6444478e95d86e2ffab8d1a3" dependencies = [ "anyhow", "cranelift-codegen", @@ -12014,9 +11994,9 @@ dependencies = [ [[package]] name = "wasmtime-environ" -version = "1.0.2" +version = "5.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ebb881c61f4f627b5d45c54e629724974f8a8890d455bcbe634330cc27309644" +checksum = "e5a2a5f0fb93aa837a727a48dd1076e8a9f882cc2fee20b433c04a18740ff63b" dependencies = [ "anyhow", "cranelift-entity", @@ -12033,9 +12013,9 @@ dependencies = [ [[package]] name = "wasmtime-jit" -version = "1.0.2" +version = "5.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1985c628011fe26adf5e23a5301bdc79b245e0e338f14bb58b39e4e25e4d8681" +checksum = "01c78f9fb2922dbb5a95f009539d4badb44866caeeb53d156bf2cf4d683c3afd" dependencies = [ "addr2line 0.17.0", "anyhow", @@ -12046,32 +12026,42 @@ dependencies = [ "log", "object 0.29.0", "rustc-demangle", - "rustix 0.35.13", "serde", "target-lexicon", - "thiserror", "wasmtime-environ", "wasmtime-jit-debug", + "wasmtime-jit-icache-coherence", "wasmtime-runtime", - "windows-sys 0.36.1", + "windows-sys 0.42.0", ] [[package]] name = "wasmtime-jit-debug" -version = "1.0.2" +version = "5.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f671b588486f5ccec8c5a3dba6b4c07eac2e66ab8c60e6f4e53717c77f709731" +checksum = "67cacdb52a77b8c8e744e510beeabf0bd698b1c94c59eed33c52b3fbd19639b0" dependencies = [ "object 0.29.0", "once_cell", - "rustix 0.35.13", + "rustix", +] + +[[package]] +name = "wasmtime-jit-icache-coherence" +version = "5.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "08fcba5ebd96da2a9f0747ab6337fe9788adfb3f63fa2c180520d665562d257e" +dependencies = [ + "cfg-if", + "libc", + "windows-sys 0.42.0", ] [[package]] name = "wasmtime-runtime" -version = "1.0.2" +version = "5.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ee8f92ad4b61736339c29361da85769ebc200f184361959d1792832e592a1afd" +checksum = "0793210acf50d4c69182c916abaee1d423dc5d172cdfde6acfea2f9446725940" dependencies = [ "anyhow", "cc", @@ -12084,19 +12074,18 @@ dependencies = [ "memoffset 0.6.5", "paste", "rand 0.8.5", - "rustix 0.35.13", - "thiserror", + "rustix", "wasmtime-asm-macros", "wasmtime-environ", "wasmtime-jit-debug", - "windows-sys 0.36.1", + "windows-sys 0.42.0", ] [[package]] name = "wasmtime-types" -version = "1.0.2" +version = "5.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d23d61cb4c46e837b431196dd06abb11731541021916d03476a178b54dc07aeb" +checksum = "50d015ba8b231248a811e323cf7a525cd3f982d4be0b9e62d27685102e5f12b1" dependencies = [ "cranelift-entity", "serde", diff --git a/client/executor/wasmtime/Cargo.toml b/client/executor/wasmtime/Cargo.toml index 983ca3c44..acd1a5aef 100644 --- a/client/executor/wasmtime/Cargo.toml +++ b/client/executor/wasmtime/Cargo.toml @@ -19,18 +19,18 @@ log = "0.4.17" # When bumping wasmtime do not forget to also bump rustix # to exactly the same version as used by wasmtime! -wasmtime = { version = "1.0.0", default-features = false, features = [ +wasmtime = { version = "5.0.0", default-features = false, features = [ "cache", "cranelift", "jitdump", "parallel-compilation", - "memory-init-cow", - "pooling-allocator", + "pooling-allocator" ] } +anyhow = "1.0.68" sc-allocator = { version = "4.1.0-dev", path = "../../allocator" } sc-executor-common = { version = "0.10.0-dev", path = "../common" } sp-runtime-interface = { version = "7.0.0", path = "../../../primitives/runtime-interface" } -sp-wasm-interface = { version = "7.0.0", path = "../../../primitives/wasm-interface" } +sp-wasm-interface = { version = "7.0.0", path = "../../../primitives/wasm-interface", features = ["wasmtime"] } # Here we include the rustix crate in the exactly same semver-compatible version as used by # wasmtime and enable its 'use-libc' flag. @@ -38,7 +38,7 @@ sp-wasm-interface = { version = "7.0.0", path = "../../../primitives/wasm-interf # By default rustix directly calls the appropriate syscalls completely bypassing libc; # this doesn't have any actual benefits for us besides making it harder to debug memory # problems (since then `mmap` etc. cannot be easily hooked into). -rustix = { version = "0.35.9", default-features = false, features = ["std", "mm", "fs", "param", "use-libc"] } +rustix = { version = "0.36.0", default-features = false, features = ["std", "mm", "fs", "param", "use-libc"] } once_cell = "1.12.0" [dev-dependencies] @@ -48,3 +48,4 @@ sp-io = { version = "7.0.0", path = "../../../primitives/io" } tempfile = "3.3.0" paste = "1.0" codec = { package = "parity-scale-codec", version = "3.2.2" } +cargo_metadata = "0.15.2" diff --git a/client/executor/wasmtime/src/imports.rs b/client/executor/wasmtime/src/imports.rs index c80952a25..fad3195de 100644 --- a/client/executor/wasmtime/src/imports.rs +++ b/client/executor/wasmtime/src/imports.rs @@ -20,7 +20,7 @@ use crate::{host::HostContext, runtime::StoreData}; use sc_executor_common::error::WasmError; use sp_wasm_interface::{FunctionContext, HostFunctions}; use std::collections::HashMap; -use wasmtime::{ExternType, FuncType, ImportType, Linker, Module, Trap}; +use wasmtime::{ExternType, FuncType, ImportType, Linker, Module}; /// Goes over all imports of a module and prepares the given linker for instantiation of the module. /// Returns an error if there are imports that cannot be satisfied. @@ -67,7 +67,7 @@ where log::debug!("Missing import: '{}' {:?}", name, func_ty); linker .func_new("env", &name, func_ty.clone(), move |_, _, _| { - Err(Trap::new(error.clone())) + Err(anyhow::Error::msg(error.clone())) }) .expect("adding a missing import stub can only fail when the item already exists, and it is missing here; qed"); } diff --git a/client/executor/wasmtime/src/instance_wrapper.rs b/client/executor/wasmtime/src/instance_wrapper.rs index feded4008..2117f1ebf 100644 --- a/client/executor/wasmtime/src/instance_wrapper.rs +++ b/client/executor/wasmtime/src/instance_wrapper.rs @@ -76,27 +76,17 @@ impl EntryPoint { .as_mut() .expect("host state cannot be empty while a function is being called; qed"); - // The logic to print out a backtrace is somewhat complicated, - // so let's get wasmtime to print it out for us. - let mut backtrace_string = trap.to_string(); - let suffix = "\nwasm backtrace:"; - if let Some(index) = backtrace_string.find(suffix) { - // Get rid of the error message and just grab the backtrace, - // since we're storing the error message ourselves separately. - backtrace_string.replace_range(0..index + suffix.len(), ""); - } - - let backtrace = Backtrace { backtrace_string }; - if let Some(error) = host_state.take_panic_message() { - Error::AbortedDueToPanic(MessageWithBacktrace { - message: error, - backtrace: Some(backtrace), - }) + let backtrace = trap.downcast_ref::().map(|backtrace| { + // The logic to print out a backtrace is somewhat complicated, + // so let's get wasmtime to print it out for us. + Backtrace { backtrace_string: backtrace.to_string() } + }); + + if let Some(message) = host_state.take_panic_message() { + Error::AbortedDueToPanic(MessageWithBacktrace { message, backtrace }) } else { - Error::AbortedDueToTrap(MessageWithBacktrace { - message: trap.display_reason().to_string(), - backtrace: Some(backtrace), - }) + let message = trap.root_cause().to_string(); + Error::AbortedDueToTrap(MessageWithBacktrace { message, backtrace }) } }) } @@ -106,7 +96,7 @@ impl EntryPoint { ctx: impl AsContext, ) -> std::result::Result { let entrypoint = func - .typed::<(u32, u32), u64, _>(ctx) + .typed::<(u32, u32), u64>(ctx) .map_err(|_| "Invalid signature for direct entry point")?; Ok(Self { call_type: EntryPointType::Direct { entrypoint } }) } @@ -117,7 +107,7 @@ impl EntryPoint { ctx: impl AsContext, ) -> std::result::Result { let dispatcher = dispatcher - .typed::<(u32, u32, u32), u64, _>(ctx) + .typed::<(u32, u32, u32), u64>(ctx) .map_err(|_| "Invalid signature for wrapped entry point")?; Ok(Self { call_type: EntryPointType::Wrapped { func, dispatcher } }) } diff --git a/client/executor/wasmtime/src/runtime.rs b/client/executor/wasmtime/src/runtime.rs index 0c3fe228f..e54070065 100644 --- a/client/executor/wasmtime/src/runtime.rs +++ b/client/executor/wasmtime/src/runtime.rs @@ -366,29 +366,27 @@ fn common_config(semantics: &Semantics) -> std::result::Result String { // We need two limits here since depending on whether the code is compiled in debug // or in release mode the maximum call depth is slightly different. -const CALL_DEPTH_LOWER_LIMIT: usize = 65478; -const CALL_DEPTH_UPPER_LIMIT: usize = 65514; +const CALL_DEPTH_LOWER_LIMIT: usize = 65455; +const CALL_DEPTH_UPPER_LIMIT: usize = 65503; test_wasm_execution!(test_consume_under_1mb_of_stack_does_not_trap); fn test_consume_under_1mb_of_stack_does_not_trap(instantiation_strategy: InstantiationStrategy) { @@ -555,3 +555,34 @@ fn test_instances_without_reuse_are_not_leaked() { instance.call_export("test_empty_return", &[0]).unwrap(); } } + +#[test] +fn test_rustix_version_matches_with_wasmtime() { + let metadata = cargo_metadata::MetadataCommand::new() + .manifest_path("../../../Cargo.toml") + .exec() + .unwrap(); + + let wasmtime_rustix = metadata + .packages + .iter() + .find(|pkg| pkg.name == "wasmtime-runtime") + .unwrap() + .dependencies + .iter() + .find(|dep| dep.name == "rustix") + .unwrap(); + let our_rustix = metadata + .packages + .iter() + .find(|pkg| pkg.name == "sc-executor-wasmtime") + .unwrap() + .dependencies + .iter() + .find(|dep| dep.name == "rustix") + .unwrap(); + + if wasmtime_rustix.req != our_rustix.req { + panic!("our version of rustix ({0}) doesn't match wasmtime's ({1}); bump the version in `sc-executor-wasmtime`'s `Cargo.toml' to '{1}' and try again", our_rustix.req, wasmtime_rustix.req); + } +} diff --git a/primitives/runtime-interface/proc-macro/src/runtime_interface/host_function_interface.rs b/primitives/runtime-interface/proc-macro/src/runtime_interface/host_function_interface.rs index 03da0bed5..280c13667 100644 --- a/primitives/runtime-interface/proc-macro/src/runtime_interface/host_function_interface.rs +++ b/primitives/runtime-interface/proc-macro/src/runtime_interface/host_function_interface.rs @@ -371,14 +371,14 @@ fn generate_host_function_implementation( registry.register_static( #crate_::sp_wasm_interface::Function::name(&#struct_name), |mut caller: #crate_::sp_wasm_interface::wasmtime::Caller, #(#ffi_args_prototype),*| - -> std::result::Result<#ffi_return_ty, #crate_::sp_wasm_interface::wasmtime::Trap> + -> std::result::Result<#ffi_return_ty, #crate_::sp_wasm_interface::anyhow::Error> { T::with_function_context(caller, move |__function_context__| { let result = std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| { #struct_name::call( __function_context__, #(#ffi_names,)* - ).map_err(#crate_::sp_wasm_interface::wasmtime::Trap::new) + ).map_err(#crate_::sp_wasm_interface::anyhow::Error::msg) })); match result { Ok(result) => result, @@ -391,7 +391,7 @@ fn generate_host_function_implementation( } else { "host code panicked while being called by the runtime".to_owned() }; - return Err(#crate_::sp_wasm_interface::wasmtime::Trap::new(message)); + return Err(#crate_::sp_wasm_interface::anyhow::Error::msg(message)); } } }) diff --git a/primitives/wasm-interface/Cargo.toml b/primitives/wasm-interface/Cargo.toml index 69937e6b0..5f914ae3a 100644 --- a/primitives/wasm-interface/Cargo.toml +++ b/primitives/wasm-interface/Cargo.toml @@ -18,9 +18,11 @@ codec = { package = "parity-scale-codec", version = "3.2.2", default-features = impl-trait-for-tuples = "0.2.2" log = { version = "0.4.17", optional = true } wasmi = { version = "0.13", optional = true } -wasmtime = { version = "1.0.0", default-features = false, optional = true } +wasmtime = { version = "5.0.0", default-features = false, optional = true } +anyhow = { version = "1.0.68", optional = true } sp-std = { version = "5.0.0", default-features = false, path = "../std" } [features] default = [ "std" ] std = [ "codec/std", "log", "sp-std/std", "wasmi", "wasmtime" ] +wasmtime = [ "dep:wasmtime", "anyhow" ] diff --git a/primitives/wasm-interface/src/lib.rs b/primitives/wasm-interface/src/lib.rs index 1ecff5a0c..c06eb43c7 100644 --- a/primitives/wasm-interface/src/lib.rs +++ b/primitives/wasm-interface/src/lib.rs @@ -41,6 +41,9 @@ macro_rules! if_wasmtime_is_enabled { if_wasmtime_is_enabled! { // Reexport wasmtime so that its types are accessible from the procedural macro. pub use wasmtime; + + // Wasmtime uses anyhow types but doesn't reexport them. + pub use anyhow; } /// Result type used by traits in this crate. diff --git a/utils/wasm-builder/Cargo.toml b/utils/wasm-builder/Cargo.toml index 84693f5ee..c1ef006b5 100644 --- a/utils/wasm-builder/Cargo.toml +++ b/utils/wasm-builder/Cargo.toml @@ -15,7 +15,7 @@ targets = ["x86_64-unknown-linux-gnu"] [dependencies] ansi_term = "0.12.1" build-helper = "0.1.1" -cargo_metadata = "0.14.2" +cargo_metadata = "0.15.2" strum = { version = "0.24.1", features = ["derive"] } tempfile = "3.1.0" toml = "0.5.4" From fe7f0a90f6d8ee09c18cfcc9927b347fcc2afdc2 Mon Sep 17 00:00:00 2001 From: cuteolaf <53915161+cuteolaf@users.noreply.github.com> Date: Mon, 6 Feb 2023 09:34:52 -0800 Subject: [PATCH 092/558] Fix: Off-chain-worker example (#13300) * fix: divider * update comment --- frame/examples/offchain-worker/src/lib.rs | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/frame/examples/offchain-worker/src/lib.rs b/frame/examples/offchain-worker/src/lib.rs index 46ff7725e..203ea2f27 100644 --- a/frame/examples/offchain-worker/src/lib.rs +++ b/frame/examples/offchain-worker/src/lib.rs @@ -410,15 +410,14 @@ impl Pallet { match res { // The value has been set correctly, which means we can safely send a transaction now. Ok(block_number) => { - // Depending if the block is even or odd we will send a `Signed` or `Unsigned` - // transaction. + // We will send different transactions based on a random number. // Note that this logic doesn't really guarantee that the transactions will be sent // in an alternating fashion (i.e. fairly distributed). Depending on the execution // order and lock acquisition, we may end up for instance sending two `Signed` // transactions in a row. If a strict order is desired, it's better to use // the storage entry for that. (for instance store both block number and a flag // indicating the type of next transaction to send). - let transaction_type = block_number % 3u32.into(); + let transaction_type = block_number % 4u32.into(); if transaction_type == Zero::zero() { TransactionType::Signed } else if transaction_type == T::BlockNumber::from(1u32) { From dd4497d8a459d5e3ecac367b991d5dae1f0940c5 Mon Sep 17 00:00:00 2001 From: Oliver Tale-Yazdi Date: Mon, 6 Feb 2023 19:12:06 +0100 Subject: [PATCH 093/558] Rename `pallet-random-collective-flip` to Insecure... (#13301) * Rename pallet-random-collective-flip to Insecure... Signed-off-by: Oliver Tale-Yazdi * fmt Signed-off-by: Oliver Tale-Yazdi --------- Signed-off-by: Oliver Tale-Yazdi --- Cargo.lock | 36 +++++++++---------- Cargo.toml | 2 +- bin/node-template/runtime/Cargo.toml | 6 ++-- bin/node-template/runtime/src/lib.rs | 4 +-- bin/node/runtime/Cargo.toml | 6 ++-- bin/node/runtime/src/lib.rs | 4 +-- frame/contracts/Cargo.toml | 2 +- frame/contracts/src/tests.rs | 4 +-- .../Cargo.toml | 4 +-- .../README.md | 14 +++++--- .../src/lib.rs | 15 +++++--- 11 files changed, 53 insertions(+), 44 deletions(-) rename frame/{randomness-collective-flip => insecure-randomness-collective-flip}/Cargo.toml (89%) rename frame/{randomness-collective-flip => insecure-randomness-collective-flip}/README.md (59%) rename frame/{randomness-collective-flip => insecure-randomness-collective-flip}/src/lib.rs (93%) diff --git a/Cargo.lock b/Cargo.lock index a74ad26e7..c870fb0a9 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3526,6 +3526,7 @@ dependencies = [ "pallet-identity", "pallet-im-online", "pallet-indices", + "pallet-insecure-randomness-collective-flip", "pallet-lottery", "pallet-membership", "pallet-message-queue", @@ -3540,7 +3541,6 @@ dependencies = [ "pallet-offences-benchmarking", "pallet-preimage", "pallet-proxy", - "pallet-randomness-collective-flip", "pallet-ranked-collective", "pallet-recovery", "pallet-referenda", @@ -4981,7 +4981,7 @@ dependencies = [ "pallet-aura", "pallet-balances", "pallet-grandpa", - "pallet-randomness-collective-flip", + "pallet-insecure-randomness-collective-flip", "pallet-sudo", "pallet-template", "pallet-timestamp", @@ -5591,7 +5591,7 @@ dependencies = [ "pallet-balances", "pallet-contracts-primitives", "pallet-contracts-proc-macro", - "pallet-randomness-collective-flip", + "pallet-insecure-randomness-collective-flip", "pallet-timestamp", "pallet-utility", "parity-scale-codec", @@ -5872,6 +5872,21 @@ dependencies = [ "sp-std", ] +[[package]] +name = "pallet-insecure-randomness-collective-flip" +version = "4.0.0-dev" +dependencies = [ + "frame-support", + "frame-system", + "parity-scale-codec", + "safe-mix", + "scale-info", + "sp-core", + "sp-io", + "sp-runtime", + "sp-std", +] + [[package]] name = "pallet-lottery" version = "4.0.0-dev" @@ -6197,21 +6212,6 @@ dependencies = [ "sp-std", ] -[[package]] -name = "pallet-randomness-collective-flip" -version = "4.0.0-dev" -dependencies = [ - "frame-support", - "frame-system", - "parity-scale-codec", - "safe-mix", - "scale-info", - "sp-core", - "sp-io", - "sp-runtime", - "sp-std", -] - [[package]] name = "pallet-ranked-collective" version = "4.0.0-dev" diff --git a/Cargo.toml b/Cargo.toml index f87172b5e..aaa1c2a21 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -128,7 +128,7 @@ members = [ "frame/nomination-pools/benchmarking", "frame/nomination-pools/test-staking", "frame/nomination-pools/runtime-api", - "frame/randomness-collective-flip", + "frame/insecure-randomness-collective-flip", "frame/ranked-collective", "frame/recovery", "frame/referenda", diff --git a/bin/node-template/runtime/Cargo.toml b/bin/node-template/runtime/Cargo.toml index 5560eb672..dd453d9f1 100644 --- a/bin/node-template/runtime/Cargo.toml +++ b/bin/node-template/runtime/Cargo.toml @@ -20,7 +20,7 @@ pallet-aura = { version = "4.0.0-dev", default-features = false, path = "../../. pallet-balances = { version = "4.0.0-dev", default-features = false, path = "../../../frame/balances" } frame-support = { version = "4.0.0-dev", default-features = false, path = "../../../frame/support" } pallet-grandpa = { version = "4.0.0-dev", default-features = false, path = "../../../frame/grandpa" } -pallet-randomness-collective-flip = { version = "4.0.0-dev", default-features = false, path = "../../../frame/randomness-collective-flip" } +pallet-insecure-randomness-collective-flip = { version = "4.0.0-dev", default-features = false, path = "../../../frame/insecure-randomness-collective-flip" } pallet-sudo = { version = "4.0.0-dev", default-features = false, path = "../../../frame/sudo" } frame-system = { version = "4.0.0-dev", default-features = false, path = "../../../frame/system" } frame-try-runtime = { version = "0.10.0-dev", default-features = false, path = "../../../frame/try-runtime", optional = true } @@ -69,7 +69,7 @@ std = [ "pallet-aura/std", "pallet-balances/std", "pallet-grandpa/std", - "pallet-randomness-collective-flip/std", + "pallet-insecure-randomness-collective-flip/std", "pallet-sudo/std", "pallet-template/std", "pallet-timestamp/std", @@ -107,7 +107,7 @@ try-runtime = [ "pallet-aura/try-runtime", "pallet-balances/try-runtime", "pallet-grandpa/try-runtime", - "pallet-randomness-collective-flip/try-runtime", + "pallet-insecure-randomness-collective-flip/try-runtime", "pallet-sudo/try-runtime", "pallet-template/try-runtime", "pallet-timestamp/try-runtime", diff --git a/bin/node-template/runtime/src/lib.rs b/bin/node-template/runtime/src/lib.rs index 484045981..ff9ac66d6 100644 --- a/bin/node-template/runtime/src/lib.rs +++ b/bin/node-template/runtime/src/lib.rs @@ -205,7 +205,7 @@ impl frame_system::Config for Runtime { type MaxConsumers = frame_support::traits::ConstU32<16>; } -impl pallet_randomness_collective_flip::Config for Runtime {} +impl pallet_insecure_randomness_collective_flip::Config for Runtime {} impl pallet_aura::Config for Runtime { type AuthorityId = AuraId; @@ -290,7 +290,7 @@ construct_runtime!( UncheckedExtrinsic = UncheckedExtrinsic, { System: frame_system, - RandomnessCollectiveFlip: pallet_randomness_collective_flip, + RandomnessCollectiveFlip: pallet_insecure_randomness_collective_flip, Timestamp: pallet_timestamp, Aura: pallet_aura, Grandpa: pallet_grandpa, diff --git a/bin/node/runtime/Cargo.toml b/bin/node/runtime/Cargo.toml index 1ff8eb0ab..1d2e6f057 100644 --- a/bin/node/runtime/Cargo.toml +++ b/bin/node/runtime/Cargo.toml @@ -87,7 +87,7 @@ pallet-offences = { version = "4.0.0-dev", default-features = false, path = "../ pallet-offences-benchmarking = { version = "4.0.0-dev", path = "../../../frame/offences/benchmarking", default-features = false, optional = true } pallet-preimage = { version = "4.0.0-dev", default-features = false, path = "../../../frame/preimage" } pallet-proxy = { version = "4.0.0-dev", default-features = false, path = "../../../frame/proxy" } -pallet-randomness-collective-flip = { version = "4.0.0-dev", default-features = false, path = "../../../frame/randomness-collective-flip" } +pallet-insecure-randomness-collective-flip = { version = "4.0.0-dev", default-features = false, path = "../../../frame/insecure-randomness-collective-flip" } pallet-ranked-collective = { version = "4.0.0-dev", default-features = false, path = "../../../frame/ranked-collective" } pallet-recovery = { version = "4.0.0-dev", default-features = false, path = "../../../frame/recovery" } pallet-referenda = { version = "4.0.0-dev", default-features = false, path = "../../../frame/referenda" } @@ -167,7 +167,7 @@ std = [ "pallet-preimage/std", "pallet-proxy/std", "sp-core/std", - "pallet-randomness-collective-flip/std", + "pallet-insecure-randomness-collective-flip/std", "sp-std/std", "pallet-session/std", "pallet-session-benchmarking?/std", @@ -300,7 +300,7 @@ try-runtime = [ "pallet-offences/try-runtime", "pallet-preimage/try-runtime", "pallet-proxy/try-runtime", - "pallet-randomness-collective-flip/try-runtime", + "pallet-insecure-randomness-collective-flip/try-runtime", "pallet-ranked-collective/try-runtime", "pallet-recovery/try-runtime", "pallet-referenda/try-runtime", diff --git a/bin/node/runtime/src/lib.rs b/bin/node/runtime/src/lib.rs index 8e8ecc125..3f4c46424 100644 --- a/bin/node/runtime/src/lib.rs +++ b/bin/node/runtime/src/lib.rs @@ -233,7 +233,7 @@ impl frame_system::Config for Runtime { type MaxConsumers = ConstU32<16>; } -impl pallet_randomness_collective_flip::Config for Runtime {} +impl pallet_insecure_randomness_collective_flip::Config for Runtime {} impl pallet_utility::Config for Runtime { type RuntimeEvent = RuntimeEvent; @@ -1732,7 +1732,7 @@ construct_runtime!( AuthorityDiscovery: pallet_authority_discovery, Offences: pallet_offences, Historical: pallet_session_historical::{Pallet}, - RandomnessCollectiveFlip: pallet_randomness_collective_flip, + RandomnessCollectiveFlip: pallet_insecure_randomness_collective_flip, Identity: pallet_identity, Society: pallet_society, Recovery: pallet_recovery, diff --git a/frame/contracts/Cargo.toml b/frame/contracts/Cargo.toml index 0ee9e36f1..cd41ae2e3 100644 --- a/frame/contracts/Cargo.toml +++ b/frame/contracts/Cargo.toml @@ -56,7 +56,7 @@ wat = "1" # Substrate Dependencies pallet-balances = { version = "4.0.0-dev", path = "../balances" } pallet-timestamp = { version = "4.0.0-dev", path = "../timestamp" } -pallet-randomness-collective-flip = { version = "4.0.0-dev", path = "../randomness-collective-flip" } +pallet-insecure-randomness-collective-flip = { version = "4.0.0-dev", path = "../insecure-randomness-collective-flip" } pallet-utility = { version = "4.0.0-dev", path = "../utility" } sp-keystore = { version = "0.13.0", path = "../../primitives/keystore" } diff --git a/frame/contracts/src/tests.rs b/frame/contracts/src/tests.rs index ce84da743..973fdefe1 100644 --- a/frame/contracts/src/tests.rs +++ b/frame/contracts/src/tests.rs @@ -67,7 +67,7 @@ frame_support::construct_runtime!( System: frame_system::{Pallet, Call, Config, Storage, Event}, Balances: pallet_balances::{Pallet, Call, Storage, Config, Event}, Timestamp: pallet_timestamp::{Pallet, Call, Storage, Inherent}, - Randomness: pallet_randomness_collective_flip::{Pallet, Storage}, + Randomness: pallet_insecure_randomness_collective_flip::{Pallet, Storage}, Utility: pallet_utility::{Pallet, Call, Storage, Event}, Contracts: pallet_contracts::{Pallet, Call, Storage, Event}, } @@ -311,7 +311,7 @@ impl frame_system::Config for Test { type OnSetCode = (); type MaxConsumers = frame_support::traits::ConstU32<16>; } -impl pallet_randomness_collective_flip::Config for Test {} +impl pallet_insecure_randomness_collective_flip::Config for Test {} impl pallet_balances::Config for Test { type MaxLocks = (); type MaxReserves = (); diff --git a/frame/randomness-collective-flip/Cargo.toml b/frame/insecure-randomness-collective-flip/Cargo.toml similarity index 89% rename from frame/randomness-collective-flip/Cargo.toml rename to frame/insecure-randomness-collective-flip/Cargo.toml index 435c38d63..68ccfadfc 100644 --- a/frame/randomness-collective-flip/Cargo.toml +++ b/frame/insecure-randomness-collective-flip/Cargo.toml @@ -1,12 +1,12 @@ [package] -name = "pallet-randomness-collective-flip" +name = "pallet-insecure-randomness-collective-flip" version = "4.0.0-dev" authors = ["Parity Technologies "] edition = "2021" license = "Apache-2.0" homepage = "https://substrate.io" repository = "https://github.com/paritytech/substrate/" -description = "FRAME randomness collective flip pallet" +description = "Insecure do not use in production: FRAME randomness collective flip pallet" readme = "README.md" [package.metadata.docs.rs] diff --git a/frame/randomness-collective-flip/README.md b/frame/insecure-randomness-collective-flip/README.md similarity index 59% rename from frame/randomness-collective-flip/README.md rename to frame/insecure-randomness-collective-flip/README.md index 0730d4abf..ef02e4b5c 100644 --- a/frame/randomness-collective-flip/README.md +++ b/frame/insecure-randomness-collective-flip/README.md @@ -1,6 +1,10 @@ +# DO NOT USE IN PRODUCTION + +The produced values do not fulfill the cryptographic requirements for random numbers. Should not be used for high-stake production use-cases. + # Randomness Module -The Randomness Collective Flip module provides a [`random`](https://docs.rs/pallet-randomness-collective-flip/latest/pallet_randomness_collective_flip/struct.Module.html#method.random) +The Randomness Collective Flip module provides a [`random`](https://docs.rs/pallet-insecure-randomness-collective-flip/latest/pallet_insecure_randomness_collective_flip/struct.Module.html#method.random) function that generates low-influence random values based on the block hashes from the previous `81` blocks. Low-influence randomness can be useful when defending against relatively weak adversaries. Using this pallet as a randomness source is advisable primarily in low-security @@ -8,7 +12,7 @@ situations like testing. ## Public Functions -See the [`Module`](https://docs.rs/pallet-randomness-collective-flip/latest/pallet_randomness_collective_flip/struct.Module.html) struct for details of publicly available functions. +See the [`Module`](https://docs.rs/pallet-insecure-randomness-collective-flip/latest/pallet_insecure_randomness_collective_flip/struct.Module.html) struct for details of publicly available functions. ## Usage @@ -32,17 +36,17 @@ pub mod pallet { pub struct Pallet(_); #[pallet::config] - pub trait Config: frame_system::Config + pallet_randomness_collective_flip::Config {} + pub trait Config: frame_system::Config + pallet_insecure_randomness_collective_flip::Config {} #[pallet::call] impl Pallet { #[pallet::weight(0)] pub fn random_module_example(origin: OriginFor) -> DispatchResult { - let _random_value = >::random(&b"my context"[..]); + let _random_value = >::random(&b"my context"[..]); Ok(()) } } } ``` -License: Apache-2.0 \ No newline at end of file +License: Apache-2.0 diff --git a/frame/randomness-collective-flip/src/lib.rs b/frame/insecure-randomness-collective-flip/src/lib.rs similarity index 93% rename from frame/randomness-collective-flip/src/lib.rs rename to frame/insecure-randomness-collective-flip/src/lib.rs index d92b05365..1d7ddc371 100644 --- a/frame/randomness-collective-flip/src/lib.rs +++ b/frame/insecure-randomness-collective-flip/src/lib.rs @@ -15,6 +15,11 @@ // See the License for the specific language governing permissions and // limitations under the License. +//! # DO NOT USE IN PRODUCTION +//! +//! The produced values do not fulfill the cryptographic requirements for random numbers. +//! Should not be used for high-stake production use-cases. +//! //! # Randomness Pallet //! //! The Randomness Collective Flip pallet provides a [`random`](./struct.Module.html#method.random) @@ -50,13 +55,13 @@ //! pub struct Pallet(_); //! //! #[pallet::config] -//! pub trait Config: frame_system::Config + pallet_randomness_collective_flip::Config {} +//! pub trait Config: frame_system::Config + pallet_insecure_randomness_collective_flip::Config {} //! //! #[pallet::call] //! impl Pallet { //! #[pallet::weight(0)] //! pub fn random_module_example(origin: OriginFor) -> DispatchResult { -//! let _random_value = >::random(&b"my context"[..]); +//! let _random_value = >::random(&b"my context"[..]); //! Ok(()) //! } //! } @@ -157,7 +162,7 @@ impl Randomness for Pallet { #[cfg(test)] mod tests { use super::*; - use crate as pallet_randomness_collective_flip; + use crate as pallet_insecure_randomness_collective_flip; use sp_core::H256; use sp_runtime::{ @@ -181,7 +186,7 @@ mod tests { UncheckedExtrinsic = UncheckedExtrinsic, { System: frame_system::{Pallet, Call, Config, Storage, Event}, - CollectiveFlip: pallet_randomness_collective_flip::{Pallet, Storage}, + CollectiveFlip: pallet_insecure_randomness_collective_flip::{Pallet, Storage}, } ); @@ -217,7 +222,7 @@ mod tests { type MaxConsumers = ConstU32<16>; } - impl pallet_randomness_collective_flip::Config for Test {} + impl pallet_insecure_randomness_collective_flip::Config for Test {} fn new_test_ext() -> sp_io::TestExternalities { let t = frame_system::GenesisConfig::default().build_storage::().unwrap(); From 41ae90843b070786ab53071479c141a7c6649ac9 Mon Sep 17 00:00:00 2001 From: Michal Kucharczyk <1728078+michalkucharczyk@users.noreply.github.com> Date: Tue, 7 Feb 2023 10:28:18 +0100 Subject: [PATCH 094/558] `zombienet`: warp-sync tests for validators (#13078) * zombienet validators warp sync draft Sketch of warp-sync test for validators. Not tested. Follow-up of: #12769 * yet another warp-sync scenario added - all validators are synced from DB, - some full nodes are synced from DB, - full-node is warp-synced * fixes * fixes * missing files * path fixed --------- Co-authored-by: parity-processbot <> --- scripts/ci/gitlab/pipeline/zombienet.yml | 18 ++ zombienet/0002-validators-warp-sync/README.md | 4 + .../0002-validators-warp-sync/chain-spec.json | 192 ++++++++++++++++++ .../test-validators-warp-sync.toml | 35 ++++ .../test-validators-warp-sync.zndsl | 43 ++++ .../0003-block-building-warp-sync/README.md | 4 + .../chain-spec.json | 192 ++++++++++++++++++ .../test-block-building-warp-sync.toml | 30 +++ .../test-block-building-warp-sync.zndsl | 35 ++++ 9 files changed, 553 insertions(+) create mode 100644 zombienet/0002-validators-warp-sync/README.md create mode 100644 zombienet/0002-validators-warp-sync/chain-spec.json create mode 100644 zombienet/0002-validators-warp-sync/test-validators-warp-sync.toml create mode 100644 zombienet/0002-validators-warp-sync/test-validators-warp-sync.zndsl create mode 100644 zombienet/0003-block-building-warp-sync/README.md create mode 100644 zombienet/0003-block-building-warp-sync/chain-spec.json create mode 100644 zombienet/0003-block-building-warp-sync/test-block-building-warp-sync.toml create mode 100644 zombienet/0003-block-building-warp-sync/test-block-building-warp-sync.zndsl diff --git a/scripts/ci/gitlab/pipeline/zombienet.yml b/scripts/ci/gitlab/pipeline/zombienet.yml index 7dc98c006..3429621e4 100644 --- a/scripts/ci/gitlab/pipeline/zombienet.yml +++ b/scripts/ci/gitlab/pipeline/zombienet.yml @@ -50,3 +50,21 @@ zombienet-0001-basic-warp-sync: - /home/nonroot/zombie-net/scripts/ci/run-test-env-manager.sh --github-remote-dir="${GH_DIR}/0001-basic-warp-sync" --test="test-warp-sync.zndsl" + + +zombienet-0002-validators-warp-sync: + extends: + - .zombienet-common + script: + - /home/nonroot/zombie-net/scripts/ci/run-test-env-manager.sh + --github-remote-dir="${GH_DIR}/0002-validators-warp-sync" + --test="test-validators-warp-sync.zndsl" + + +zombienet-0003-block-building-warp-sync: + extends: + - .zombienet-common + script: + - /home/nonroot/zombie-net/scripts/ci/run-test-env-manager.sh + --github-remote-dir="${GH_DIR}/0003-block-building-warp-sync" + --test="test-block-building-warp-sync.zndsl" diff --git a/zombienet/0002-validators-warp-sync/README.md b/zombienet/0002-validators-warp-sync/README.md new file mode 100644 index 000000000..662360fbf --- /dev/null +++ b/zombienet/0002-validators-warp-sync/README.md @@ -0,0 +1,4 @@ +Refer to ../0001-basic-warp-sync/README.md for more details. This test is nearly a clone. We want to warp-sync validators and make sure they can build blocks. +0001-basic-warp-sync chainspec (copied) and database are reused in this test. + + diff --git a/zombienet/0002-validators-warp-sync/chain-spec.json b/zombienet/0002-validators-warp-sync/chain-spec.json new file mode 100644 index 000000000..8c09e7c7b --- /dev/null +++ b/zombienet/0002-validators-warp-sync/chain-spec.json @@ -0,0 +1,192 @@ +{ + "name": "Local Testnet", + "id": "local_testnet", + "chainType": "Local", + "bootNodes": [ + "/ip4/127.0.0.1/tcp/30333/p2p/12D3KooWFvMbTsNZ8peGS8dbnRvNDBspstupzwYC9NVwbzGCLtDt" + ], + "telemetryEndpoints": null, + "protocolId": null, + "properties": null, + "forkBlocks": null, + "badBlocks": null, + "lightSyncState": null, + "codeSubstitutes": {}, + "genesis": { + "raw": { + "top": { + "0x074b65e262fcd5bd9c785caf7f42e00a4e7b9012096b41c4eb3aaf947f6ea429": "0x0000", + "0x0e7b504e5df47062be129a8958a7a1271689c014e0a5b9a8ca8aafdff753c41c": "0xe8030000000000000000000000000000", + "0x0e7b504e5df47062be129a8958a7a1274e7b9012096b41c4eb3aaf947f6ea429": "0x0000", + "0x0e7b504e5df47062be129a8958a7a127ecf0c2087a354172a7b5a9a7735fe2ff": "0xc0890100", + "0x0e7b504e5df47062be129a8958a7a127fb88d072992a4a52ce055d9181748f1f": "0x0a000000000000000000000000000000", + "0x0f6738a0ee80c8e74cd2c7417c1e25564e7b9012096b41c4eb3aaf947f6ea429": "0x0000", + "0x1809d78346727a0ef58c0fa03bafa3234e7b9012096b41c4eb3aaf947f6ea429": "0x0000", + "0x1a736d37504c2e3fb73dad160c55b2914e7b9012096b41c4eb3aaf947f6ea429": "0x0000", + "0x1cb6f36e027abb2091cfb5110ab5087f4e7b9012096b41c4eb3aaf947f6ea429": "0x0000", + "0x1cb6f36e027abb2091cfb5110ab5087f5e0621c4869aa60c02be9adcc98a0d1d": "0x08d43593c715fdd31c61141abd04a99fd6822c8558854ccde39a5684e7a56da27d01000000000000008eaf04151687736326c9fea17e25fc5287613693c912909cb226aa4794f26a480100000000000000", + "0x1cb6f36e027abb2091cfb5110ab5087f66e8f035c8adbe7f1547b43c51e6f8a4": "0x00000000", + "0x1cb6f36e027abb2091cfb5110ab5087faacf00b9b41fda7a9268821c2a2b3e4c": "0x08d43593c715fdd31c61141abd04a99fd6822c8558854ccde39a5684e7a56da27d01000000000000008eaf04151687736326c9fea17e25fc5287613693c912909cb226aa4794f26a480100000000000000", + "0x1cb6f36e027abb2091cfb5110ab5087fdc6b171b77304263c292cc3ea5ed31ef": "0x0100000000000000040000000000000001", + "0x2099d7f109d6e535fb000bba623fd4404c014e6bf8b8c2c011e7290b85696bb3": "0x08d43593c715fdd31c61141abd04a99fd6822c8558854ccde39a5684e7a56da27d8eaf04151687736326c9fea17e25fc5287613693c912909cb226aa4794f26a48", + "0x2099d7f109d6e535fb000bba623fd4404e7b9012096b41c4eb3aaf947f6ea429": "0x0000", + "0x2099d7f109d6e535fb000bba623fd4409f99a2ce711f3a31b2fc05604c93f179": "0x08d43593c715fdd31c61141abd04a99fd6822c8558854ccde39a5684e7a56da27d8eaf04151687736326c9fea17e25fc5287613693c912909cb226aa4794f26a48", + "0x267ada16405529c2f7ef2727d71edbde4e7b9012096b41c4eb3aaf947f6ea429": "0x0000", + "0x26aa394eea5630e07c48ae0c9558cef734abf5cb34d6244378cddbf18e849d96": "0x00000000071c0d84db3a00", + "0x26aa394eea5630e07c48ae0c9558cef74e7b9012096b41c4eb3aaf947f6ea429": "0x0000", + "0x26aa394eea5630e07c48ae0c9558cef75684a022a34dd8bfa2baaf44f172b710": "0x01", + "0x26aa394eea5630e07c48ae0c9558cef78a42f33323cb5ced3b44dd825fda9fcc": "0x4545454545454545454545454545454545454545454545454545454545454545", + "0x26aa394eea5630e07c48ae0c9558cef7a44704b568d21667356a5a050c118746b4def25cfda6ef3a00000000": "0x4545454545454545454545454545454545454545454545454545454545454545", + "0x26aa394eea5630e07c48ae0c9558cef7a7fd6c28836b9a28522dc924110cf439": "0x01", + "0x26aa394eea5630e07c48ae0c9558cef7b99d880ec681799c0cf30e8886371da9007cbc1270b5b091758f9c42f5915b3e8ac59e11963af19174d0b94d5d78041c233f55d2e19324665bafdfb62925af2d": "0x000000000000000001000000000000000000a0dec5adc9353600000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", + "0x26aa394eea5630e07c48ae0c9558cef7b99d880ec681799c0cf30e8886371da923a05cabf6d3bde7ca3ef0d11596b5611cbd2d43530a44705ad088af313e18f80b53ef16b36177cd4b77b846f2a5f07c": "0x000000000000000001000000000000000000a0dec5adc9353600000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", + "0x26aa394eea5630e07c48ae0c9558cef7b99d880ec681799c0cf30e8886371da932a5935f6edc617ae178fef9eb1e211fbe5ddb1579b72e84524fc29e78609e3caf42e85aa118ebfe0b0ad404b5bdd25f": "0x000000000300000001000000000000000000a0dec5adc935360000000000000000000000000000000000000000000000000064a7b3b6e00d0000000000000000000064a7b3b6e00d0000000000000000", + "0x26aa394eea5630e07c48ae0c9558cef7b99d880ec681799c0cf30e8886371da94f9aea1afa791265fae359272badc1cf8eaf04151687736326c9fea17e25fc5287613693c912909cb226aa4794f26a48": "0x000000000000000001000000000000000000a0dec5adc9353600000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", + "0x26aa394eea5630e07c48ae0c9558cef7b99d880ec681799c0cf30e8886371da95ecffd7b6c0f78751baa9d281e0bfa3a6d6f646c70792f74727372790000000000000000000000000000000000000000": "0x0000000000000000010000000000000000407a10f35a00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", + "0x26aa394eea5630e07c48ae0c9558cef7b99d880ec681799c0cf30e8886371da96f2e33376834a63c86a195bcf685aebbfe65717dad0447d715f660a0a58411de509b42e6efb8375f562f58a554d5860e": "0x000000000300000001000000000000000000a0dec5adc935360000000000000000000000000000000000000000000000000064a7b3b6e00d0000000000000000000064a7b3b6e00d0000000000000000", + "0x26aa394eea5630e07c48ae0c9558cef7b99d880ec681799c0cf30e8886371da98578796c363c105114787203e4d93ca6101191192fc877c24d725b337120fa3edc63d227bbc92705db1e2cb65f56981a": "0x000000000000000001000000000000000000a0dec5adc9353600000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", + "0x26aa394eea5630e07c48ae0c9558cef7b99d880ec681799c0cf30e8886371da9b0edae20838083f2cde1c4080db8cf8090b5ab205c6974c9ea841be688864633dc9ca8a357843eeacf2314649965fe22": "0x000000000000000001000000000000000000a0dec5adc9353600000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", + "0x26aa394eea5630e07c48ae0c9558cef7b99d880ec681799c0cf30e8886371da9b321d16960ce1d9190b61e2421cc60131e07379407fecc4b89eb7dbd287c2c781cfb1907a96947a3eb18e4f8e7198625": "0x000000000000000001000000000000000000a0dec5adc9353600000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", + "0x26aa394eea5630e07c48ae0c9558cef7b99d880ec681799c0cf30e8886371da9de1e86a9a8c739864cf3cc5ec2bea59fd43593c715fdd31c61141abd04a99fd6822c8558854ccde39a5684e7a56da27d": "0x000000000000000001000000000000000000a0dec5adc9353600000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", + "0x26aa394eea5630e07c48ae0c9558cef7b99d880ec681799c0cf30e8886371da9e5e802737cce3a54b0bc9e3d3e6be26e306721211d5404bd9da88e0204360a1a9ab8b87c66c1bc2fcdd37f3c2222cc20": "0x000000000000000001000000000000000000a0dec5adc9353600000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", + "0x26aa394eea5630e07c48ae0c9558cef7b99d880ec681799c0cf30e8886371da9edeaa42c2163f68084a988529a0e2ec5e659a7a1628cdd93febc04a4e0646ea20e9f5f0ce097d9a05290d4a9e054df4e": "0x000000000000000001000000000000000000a0dec5adc9353600000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", + "0x26aa394eea5630e07c48ae0c9558cef7b99d880ec681799c0cf30e8886371da9f3f619a1c2956443880db9cc9a13d058e860f1b1c7227f7c22602f53f15af80747814dffd839719731ee3bba6edc126c": "0x000000000000000001000000000000000000a0dec5adc9353600000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", + "0x26aa394eea5630e07c48ae0c9558cef7f9cce9c888469bb1a0dceaa129672ef8": "0x3104106e6f6465", + "0x2aeddc77fe58c98d50bd37f1b90840f94e7b9012096b41c4eb3aaf947f6ea429": "0x0000", + "0x2b06af9719ac64d755623cda8ddd9b944e7b9012096b41c4eb3aaf947f6ea429": "0x0000", + "0x2b06af9719ac64d755623cda8ddd9b949f99a2ce711f3a31b2fc05604c93f179": "0x08d43593c715fdd31c61141abd04a99fd6822c8558854ccde39a5684e7a56da27d8eaf04151687736326c9fea17e25fc5287613693c912909cb226aa4794f26a48", + "0x2c5de123c468aef7f3ac2ab3a76f87ce4e7b9012096b41c4eb3aaf947f6ea429": "0x0400", + "0x2f85f1e1378cb2d7b83adbaf0b5869c24e7b9012096b41c4eb3aaf947f6ea429": "0x0100", + "0x2f85f1e1378cb2d7b83adbaf0b5869c298ef7dc060436e4ed803af07632b89b65153cb1f00942ff401000000": "0x601de9615313b00e8cea3ef84e79e50b2fb70e2c8a47cff478b9fe8b3fa8f2db02000000", + "0x2f85f1e1378cb2d7b83adbaf0b5869c298ef7dc060436e4ed803af07632b89b6b4def25cfda6ef3a00000000": "0x601de9615313b00e8cea3ef84e79e50b2fb70e2c8a47cff478b9fe8b3fa8f2db02000000", + "0x2f85f1e1378cb2d7b83adbaf0b5869c2ff3ae12770bea2e48d9bde7385e7a25f": "0x0000000002000000", + "0x3a2d6c9353500637d8f8e3e0fa0bb1c54e7b9012096b41c4eb3aaf947f6ea429": "0x0400", + "0x3a2d6c9353500637d8f8e3e0fa0bb1c5ba7fb8745735dc3be2a2c61a72c39e78": "0x00", + "0x3a636f6465": "0x52bc537646db8e0528b52ffd0058dc2705bea5c66d15541040ac6c3ae971bcf5f1040221a8d1f8b7c17e61bf21cce87411480b10bd8daa5e3c2b65f8c4aa364e5a8ddf27c0313827d06a2d6334e074db9ca7d876ac8d052862b9984530f4d340bda4edddd6e4de5ba624536717e1139e14df2d875c7f7086dc3fa3d398b64f50fffc58fbf4fb73ffb0d39cc2b86761cfe6d3073b9c1fcee7fe611b7f4875f9a7d8cfe208fdf493f4d3b07d9a73ad05c8cf39e5c4dde679c3c8391b9ceec4fd78c824d218e4cfb79cb8be9cb301eace3b7f8a274c182edb72e2e83349855e7e4a2a795d7e27998b94f46d92b42eb543afcbcf5f5c9e5bf2d52dfd9b2f93dc33262710e3fc79b943e09fe2d00f8dd7edc4350b92a692a2facb7adddffddd3d89e9babb7b8e0b9d61050b2b4ddf3efdfdb3f629e9edfe9c18c4f8715bebf42d6f6e3ff74be1b9c0c8b9187cb89c83618e5bce6ebf90cb3918729745244063391ee9710cdb87bfbbbffc6047f275725e904518e7f53cec48efa788c4bd67d104a4eb81534cd2bd67cbeebb075b4c32deee59a43da30fd2d0c979410f3db2fb2972cfcad89d0f822c7ab644bade3b59d6eeddc9925eef8f34e8645980eb0fb2f8d9b2cafddec99273d7bf234bee5995292c3051e1fa97dcb31285eb5f76255cff23de917c3f72c8afaeaeac6ef74e96462e7d70864ed2ef48be2c9ef05d8f1cfa6e67cb79dd0ecdabababab5b225d7f4a72cfa6a53d9bcf739903246f521840fe916b813c65e41c0c50b7b60fe76078bafc61ff4c1bbfac7d3887b3bafc37979f73580490ef389f9f5920c8edc3f3cee7075fdc3e4cdba79f1f9c200db97df896f532c31e008355cff83a7dff7e1791b4e5097b400cb89ef19def24533b4524b567fe2c96c02292fe233d5900f2edfed96d8f34d29c2680f3270934e67054b7a4b7bfceaf95fd879b568e777e395e3aa7139fded3ff484f77779fef3ebbbfdeb6364afae9f74c32bf6dd9ef3de714c839e5df7e908f8c208b30f2fbb388847f8a2354c1e170305cb6dcb3b2de7eb0436e9ff9edc33deb3fd24ed29ef50f602cc7dbfda0ac7df8f683331cdba73fd63efefdb2f699b79f45244e492a38b708a9def9f54e6ba3643efdf9d41acdeb964524d396f3ce3fd247fa2f7380e4736a01f2e59c72bcfd606d9fb266c81ce3fc32bc0d3a198071be3f3f8b27cc0f725b7bc6cf3d2be99da45f916a7e3f38c349d21b7211a2597587be4b05e6ceef17b908d1c4dd21112e95f0ceef777111a289bb43225c2ade9ddfffe2224413778744b8f3fb594c4271b83bff48831d3217995ffa73cff86bfb94f3faf3834ed69ef11719fdf9592481f68cdf6ded19ff91063b9c64df908b10cddca5e2ddf9fd2217219ab83b24c29ddf3fc51346dc9d1fb60fdf7e16934cdc9d7fa48ff4913ed24c72cff839a76dd0789903a49e5d06c80dcee7f629f9a7e59ef1f323cd2903720ee774f91b22454f610414ad21610c79c190160c991a32c6102286c0604808865831440c2057860431c469881042da10f28221370c9961881742e6186245881c426630248b1035848421248e214d42de18b224644a081b42bc08a11212032164081943081842d6102203212e102286902e84782184cb902d845821a44ac8154248304520a449080d3d36e8a9ea99a3478e9e387ad4e8f1d233464f0c7a723d5b7aace849414f153d24e839a247aa678b1e287a9ee889ea71a207891e237a44d053821ea89e247a94e81941cf0a7a5ad0c3821d11f448d1d3448f163c3af070a046c68e1a3b69eca0b173c68e193b65ecc060478c9d17ec84b103c64e979d2f76bcd8e982878947069e251e259e241e2b3c31f0c0c083dbb9dab963c78e9d3776e0d891c1ce971d2e3b2ed8e162678b9da99d1becd86087063b74ecccb133831d3976e2d8a963c76aa7063c1be0b9b293c50e163b2cd859c18e0a76a476aed8b162a78a9da89d12ec3cb143821d26768cd82962c769e7879d1b767cd80162c7033b42ec7460070440ec0072c70e13901b00b9da69dab101481640ba00f102880b807001b20490278094000813407e000204902cb51acc20a61390a51a0d80c850fb52bba376558ba3660710a5da1c409280e06a5635388058a9cda026478d8e9a1bb5aada1b35366a30a86d51aba236454d899a096a4c0059a386a546430d57b3528361e76987089d2f74dcd0a9d25943270d1d2a1d18e88ca123860e1b3a73e880a123874e0c74e0d02943270c9d3674ccd0214387063a32d0a9814e171d2b9d3774d0d0a143470d9d17e8cc40270e1d2f3a67e808a143848e077478d0c9a263834e0d3a34e8e0a0d3019da61f6afc80e3c71a3fc2f8d1821f5dfcd8e207173fbc981ef8f1c40f277e90e047133f98f8b1c4ab04f34b8f0c604288573fbebca26a54d0c8f083aa2af9f0c2c71735197e88a1d3c33787ec8e9009f6434d1b36573a869a0bbc2f3a778473d49e422ec2168453e11630203c1bfce0522347784527869a17542d68984219603d7442c086a8f902e34167294cea425023469df26e50e3a5878c70a926d5259b1d6267c8bebc5eb0d303cc03a1d2384307467490a583a70e74a851eaa0033d8e10bfc070a8568c3db8be78687c74f408e2c545b481cb4a8f14e06c51657851856d8c3bc8bc78c5c047d40f2d6476c83421bb416581cb062f2f2f34646ed42d7c24e164f1ec78a9e15223e6c54b63d4e2c38a48c78c1d95a9068d5a11f54aad04b51d5c68b86010adc61a6ad4a0597aa541a39463031033726aa08901c9445e21ade05841c6602307a9f423f827300d1b19f4d4f1657000825718382da827e8b0886ef4808123021c23708ae809030704384ce02c81c3054709215d709cf8f1c1f900ce062a10383e8852b468d0b2c26962c686991a7048b09303ce08704a504bc306861a326ad8b0d9400d0c6ac0c8990187861a3a74385063464fe12ce1c4e003063ec6f831831e40f4f0c04b8c1f67782ce8d104a8460fa79d257afcd003043fcc905981103764a2e8a1030803d005433e007ab179b201818d10f406a01dd48ece868e0956431743dd42bff4e8402743074387ebac74576a0a2a15758aae06b1aadbc07f60c68c971daf2f558a1e5a3aa577aae9a24bea66a077b8946a9868c8a0a1010d1d3435a061636606345e66e2a091c18c1c346bd0a83143c7cc1c3469ec0411ce40860b4d0b605a68585093aa69a131a25ba2c982e605b52d343e8c1bb0c901f6011808604f3c88784551b9c8ae646dc072f820fe88991bbcc0e88ae888e88c70c9d03dc18aa8319851a3f683c8c60c1a325f646e206303992b19ab191a66b0c850f1a282460c9c20707ac00142e60a192b6049302b3255c8a4e03df03f00d181831d4419d4c6b0517a55895334493b30b41534565c4b2e1ccca99682300d980e5e0d6057604cf306e31a3655231b3e62f08241f5e245c66b0c0eb48c408c3f8c1eb08943868b07e2d5454d1aaf2b5e5b6cdc78a9604c0387089c271928402e5e55d86001af6aba8871d4d0a0070f3634d4d8d1c3071b2b21ce268b8d0d3367d4bc01bba1c68d1f2ea859e387979a18c09a6c7880d95073364b3565d0ab9a19d4b2b091e175831a2b180d3859d4d421b6f17ae2d5c4cb891f37a89101784718c3cb0697198e858f261717ae12b846e02ac2b585eb042e295c53b8927081c015e59a72b5c07584ab0617169716ae14b8b270f5e0f2800b0bd710ae255c4eb884706d7129e17ac2b5838b07d70f2e1b5c50b8a270657171c065848b05ae15b8987055e1bac275832b07d7932b042e205c1f7005e15241cf54cd183364b862a8b99a2943ac439cc3e606110e9106e21be21d40bec0711273220a8068b169c3956433c3aca3c6c4b303073cbccee891c30e0e385c841b7899a1e30510aa1a143555b5276a4e4c2d707ee080033b576021d88961a7069c295810af357464086100bfe0203141600387cc13322600bbc0f100e8458f25c02dc030769a00c1787d51b1709d51b3807140acc1cb8b9a06ce11343000c204a40c58075e65d868a9b1012ba9fe00dba1034137448ba047053369bc6cf0838d1e3df464515bd0566ab8ecf0a0d34563f1d9507bc0116205162a544da22e5195a81e783272a2462a9e8b189731052e3b64553eb8689911bee18901048d57171d2d3836e0d48033838e0e325f006143c68b8f06b5a57a458c8b2ae5aa636c72a5d1e303a21db58a1a11b5281f31e058a92800a2850f18ea0a5e50d4cc21dea0a606231a43b8801101f321b4e25b8833a82a10e5a849c2d10296c533e3470c6a5c5073064e08bc1fbc19bc2b5e0c3d33e8b1a367e81a7a072057b4161b1d76b4b8c2a01903e452bb41cf17206a0021a3cb0244059d53a743d7d4d1d075a0d3d2fdd005d1c305080f3d5d741ce87ce87ae878e880e0d1d2edd079a0cba1fb40874377031016e00c61f3464d0daf0d9d1c7ab010b700d7d0b9a1870d73861b367676e0e14167079e2606e060c101838621e3820ea39de8259a896ea29fe82e1dd55f34091a0a97aa5cfc106fc423417304cd123448e41891e38408845884e834163192603402e4400d23c6464c8e980c649e64909029a207971e60f4e822a7889c12e488409c41e4808885468a460b1a15c4ba88c120e60558450503b442d64566860c8c19ab993b66eae8b1460f1af460237644ac043124782cf168e22143ab8adaa56545cb8bda45eb8b2fe245f042f838818f2d3e50f02178103c11b12b625bc4a44627c629c6a81814b12d3113f810c207123e86a8d9e0b9e1d1e1bd018301ac0cd8182316a31763163eacf0d1021f57c8acd00981ec8a0f86efca1783a884480271095087ea0270872a06a8e585e5b5c38b86d612b5205a4c842c08c908b1a835d42cd586fa058d153eb4f808c2070fad21ea0b5a21086f107e69bd117620ecc1d3a2268697453746774617032058d870c026861aaa9a1bd8d450e3854d520d1c364d3638d4dc5113860dae260e1ba69a2f80cca16307101bec28d145f42cd136340d4056d0589aa973681e3acb9ca3710d03902ee6d5acc1b4c184a3c76a5acdaa49032052736ab66066b164a260b2604e31a5983f4ca989c584629e60e6a609261573cbfcc05cc1b46246318998219843cca46965c6309f260c1357c50215983e3c994d4ea8b820a505a1261098400424f08006a49000144e40c001941820090953082300100091282c38c07979c648c16a072a6a528125539a00b1b0640938fd4914a0254b5abf8127529c003dc1f92b5c548401202551a440292a025a00f94c1cbc0c34bf24d4c40914222946455058f0e0956c3e89895a68e1f556b8a80848a8c7c7c0310f03b7700099c7f1e05c090935712224d48400349c3bb848084a1323a0274b80802cf0018e1d2cc50251a02cc10011900b434ba04cb94651805ac04013141ee70b03ad7003067a22c5c992274da4084d71c1060c64d4048a519426464551a0c870ea6021a1264e8c9c3421328a228500301c2b067212454a94254d9c44918200a3282e4871e2e4f6c0a9019429170345500a80814be2d020041c3a7270e660189c19b01ccc82132953806c70e26022175680628128529c2c8132e50255a0894d06a78a8b9e1035b90010d19215a24079120568ca1228536e91901429284481d2821404581c38780a14168a8880a25c40ca120c00057d80f3063f018ab20294a2282e18357122850a14178680961811499902444404c70d7e026589115011910b4e6870dae0222028465088a4b060e44293252b2c8132e53699f2244a1322127ce0c8802f0065ca25020a2292e28013481c361808c80520171290c213a015566892c20f9c35b828c809100b4ebc7091511420222016a240791c35a240b1800d4e1aec044a5114284446558096604068ca94264b88809e084171c116e4448a90ed06070d6e614913a3264f9a18011d0086734606a00835816204250a14191c3338c88914a10a2c013272c109501520189c3298480a01429e404215686214c5050b2c3172024404e54914284b96d0e880367c2083918403c3ca97cb4820e770b83b919cc66355b1aae694f951efc6c6fb273d1bcff3bc27c99b1b8ffc1beadfc75f7f5d6b77adb39bf963effa7d5d2be576e68ffb03e9f4fad1a6def7791fd013fe987a7b5f777fb4995b74ff9add99b2338b226566ffd8fbbe9deea87760f7d755efcfbbfb1b476ef70aced44a99ba7762f56e96d8ac3a99bac74ce64a65cd72cf9d3f0fdbe9d7ec2f661bfe987ecceca4dde9e73895dbbb72f5cf690794fac7edb4d93f76fa71d3ef73f7bc8fb6536766ca9429534ad9fb28f5f6ef83317b53a64c993fca4e653e66a7cced9476bb575ae5cafef19021428430c8eed4e5a20e7a575abbdd0586cc937a5ead1f738fcc9f3b6598bb5377efd1dd2be0eeb0eab5d6dae3d8b4dbdde79c3e67d3f6f6a6d4bbbf6edaecd46beaf5fc2af5c09d3af5599dddbf70c645db43f7daa42ba5ee2ff6f656bb7fee1574265d57ef4a7b093b8d31b37fd3dd41b052a069482b0dc31aef1a760fc309788712a8719ed3bf5ab97b727fdc13e4eb09e21e0176770a1386ddd3b9bb99fba3f9421aeace94db2140a97baeaf7eeef4f3a8473d66af52eade5fb7fb473fef8ffbc6fb3effe6fc981bf600dab4ddbb2937b37fdd1f7bceee7d2008a34054a820303bbfda1dc0cc3e3bef98b9fb989bb9eb66c7ac6eba772c67ee58ecccdd64ef581d77ce9a5dd771d7755d73d779c71db3983b666618666f6731c73033b35831cc95997aadeedd9f7b8e7b7737a51ed0ee80f6cbddbfe6699e6e8fba7703ba76f7a49501ed7937ad1fe5e9cddd1f7f5fc7d49dba376d4f61d20528c0030e9d7f780218051ad6f41ac7a969202774cb7cddb06eeafd79b57e8e0098ce016a351d6f1a86ec734e9eacf6febeef874c7ba594c62adb7ccc95a73bedd0dd457710e6e476b06d427666a6dd4ca33edd41f77228655a29ad94b6f70ca5b45217a5d4044a5db429f5da5e2b58bfaf6777fb6cfe3e9c0efae3e993d99550e7ea6cc3ce6266af71f7cf6b6776f630c63e67c8ede2768ecd364077e8edce727767d51866f7ca732661febc69f3474343536b7dbd5e73ce5adbbd84f6f6aa57efd09d046feaeefe7277eef9f5d7947e1f87cedeee4dab778314ac2085e9eee6ea9f37280382ec3ed929ad15a4947e1ff7d7d49d7d4e0ec3574e873e42483d44e2341e76bb7bddcdeeeeecfc7ddc55669e9f659cf979e8ec79dd4eabd7dab47af5efebaed56937add4a35e53da3da7cf39270c4c7be73c3bca6af72e403b28d33dfba360d7fc79d8dd9336a594b67f4e00dbc7edec3d99bbdd3fff3e76e6767667766f6766a6a0e73ebdbda977f7f4cf999d5266fed89ddb9d32333365fe98bfef6b76ff989d7a5f6f7766767777fed8bddbbbbf23dff7cd6aedafcee97d3319113c99effbbcef930d4000edc950af7e9fabbdfdfbbeefebfebe98f77dfc7d9ff3f77decf4fb78ec9a366d9036edcfdbddbd73ea7ded1fcf39448a2153380f6e0c10496901a805cf8805a448219a0242009e0011013d2192b2c427841e20284446207c0043d323a5a60b72d2a402424252402852d3c3470f1a1e464550aa00dd742d48e981620120a22996031b98284053a634b9f10b00450122ea0e5a806214c50522cb81058c5ce8c14115201696184581423465899322282ddc0cf1e95145ca058c7c3461a1c8475013274e80827c42e8e1c14188f40459d20314d444a8024b5688028585a2262cbc102128413c00f8800807690244e4032868895115a0283a3e3e807922048505274b8c845c686201a027b4ce88a88951d1073d1cfcac00a5888c7190220c00052d710245a8022e4c99111575d0e29ea22220a2292d4871b28408888a142740444b9a143d7102b42448904f083d539c0015fdb0587062d4a4024b58288212058a05661f60c002508880a870608980a200adb00400414208a1e709d0122946533040e4a4039610101520a125559ab480035b418ad09215a240596105294234b3051f0c446401275284849adcc054017a12054acc94169a181501b500c5e87b449102c5c84993a2284d888c96184169a1022e14d1584f80808a96b420e5499426443606c8a88951d1922022291730028a92032bf2596116821150d19227519a2c01226a32a5052943c62640424e9600add084488a51d112a1264ea4088140250a90b338c8922852a014110151594224c5a8c91328424da82c2982b2240a94262c38b18005a42c31aa02c401952840fe03aba3f986d44cf805a466c28dc462c247472c9009b7c20e09e9e8e8ab48ee47484820528b09231d1dbd8e8e68bb33399a4847474794c9d13cbacddb64f2c27c611e1d1d1d1d3192787474243261a48f8f908e909c9190e6d1d16482348f909ac96dde6ecde485f9c20bcd848f90909cc9d13c3a6a2647f36832e1a3a3a323a48e1e1d21cda3598f26d264723499091f1d39133e426a267c74c448481f13a47974e4313942ea98204d24a46682349991281346426a268c8484e44c9026d264c248938905bc342633cf168a9a70ff6aba2dbecb9bc2179ba6703ceccdfb193b61ec6d8ad3fa61f6369d5af6e654377762d95b3bc9d8a6622aae72ea625e8cbd219102ec61fec6fa1bd3105beb6f48a4d0bd3775fb6f48a450bfeb5c0ec6de580fb373b4b709e5b4fe97bd4d97bd7993686fdd14da9ec24d2876c109ea68a220cbde9296425bcbde904881f5f46f93eaf6330db1517b63d949e5b47efa9ebdcd29a7f577f6d6d5766e7a36a8a2065a90800530cca9a9c58c21d4f882082b9f3dea2b9a5002872dbef460a37656517915a572a2534d533447713977a250de975e0555e5509eedb450755fba13d5955367bb266fa25ff6d477757507185faca087266a3ba6cecd37bc08c30c20a4c2b0b1cdbf6cdced8f8b1dbc7842862cdcb0b1cd6d87735a7f39ab6eff1cc138bfec70b7bba9cb1c1c59a4d933361c31c711671c31c611611cc165882e59dc0c8c9c1b4288db0f5ece0d41c5104fcc1a30bbb3b395796543826b58f0c86e3ffffc59fbcc3075fb6f74daa76fffecd86d16fe36cee5c961ce39d9c66a98c3873984d395f5cc2d371260cf8668c26947f0b30d091c10081bb60640bafc2fc840c4b644c4d6cf362bf3cad61609b64773ea809492486310fd0f6e89d41d5dfa25eb006cc6ed2c11b57b26eb7724253b57a2ca562d8eb568a25fb228f9c105278b10f8e01e4da4ae9b0b281f40e625e61c9ad77fde00f293f00738346f3f0f5e6ef8dd3759346f7f8b25f065d9a0f9ac269a5cebdea8de21bebae0b73842f8ad16d87db348ce817ed8e586cf7a10ecc26e3dab45253409ebc33fd2247c0ffe24c16791e1bba8a4fe9c96b936cbb6936bf3590f9265b3a8d43be477e08c269237db8dc9f47f5c3a9f7970bade7f28a8b003166eabf159fbd878dde7cc98b0204667f3d1a38ddbe9b44fe9fd8c3baeef25842a30699f8ea54c689f79bbe7d1c274bb1fd2519edb3117299f9a5bf219b7ab3fe57edffc46deed07694efb74443d45254beef7f5fbe6cf32ebb00559a2094852782dcffb727edf64b51fe99125d2f5823c31c967bfff6cf98267cb79bb497f70762bf16f25d3b34ae677f737e00e425c8fec260cb7accfb9197c18c158fb78df3d1549a85fbe709564c270eb83dcb8fbbd47d68f2cbde73fe21e0c777e7b55f502d6fe793dffd83ee2f3c7daa7f5cc7ff339ed539f7fd63ede23dd16297e25914010fc231d9236f559396cb9f4451dbc07b95cfa1e07ceb8f441fa208834c367b1c00727f8437ec1076b908d92ef3bfab93e259feb03bf3ec86a3d5f56cb8e509ff5ac1db25cfaaddf21e9d2a0eebf0fbf92f3b25c1ca1d56ab56c50f725fd22a4138a906ef841de9f40ffb3a5f7a165f165d52e6431b4e5ed86ef912fd27b89394839935ebfc41cc4fb693be2210976a147fabf48b6555209cb06894f5f7c16910c896314df3df03d5b8a0f5a1bf0bfef94d017ad0d6895d4f7be821ed9fa2fa8d64f49fd6a12fa2c5b7a0f3e089265f836ace70bbe47dab0ec08dd830f5abe1c98e3d2071fecd06b3d68cb794befc3073d6c91e197f386df8d002601c1f767d9a179c57a7ffaa02d5b1f92e0bc1e396f8be43b2ff3ed8618b9fcec359c0168182ebf8b87c8d1446a7568039884410f5924c16d39a75802fbcf13fafd595f562e52bf6479e86439aff760fbe5760ffb32c8a1279e40df6d59af7bef950e7e210d3bd10095eccb22e7edbc9fff89273458c5138edcd6b3c8790d8024c56d3dd80a5926208d10e50e1dd52421cefdaa880024a7db7ab04396480292d31d62725b0fd6b0251a00c986db7a100c5962121eae1259a20948b7f5a02b641da0cbd7fdb8997976aef2a8e97a0f86218b65cbda2e5779e474bd6f91cc3a65ebe787e209fd2d5b4e164356927ecfbacaa39b4588ebfdbc959c17ac618b49e8fb4f71c86f903802d2ed3eebd9f91e34df46498981dbcf17a94e2f892c583c0a0ee4c09405072f138710ecefb7e99e67f7ec9136ddcffbfd07a3ffc1f5ffbec9ee677bedb5c7df5ee59eecfd11f7c8ee821dd90fce59c3d83f3b0f3b1a765dbff7839d38028be17d3357f3dcc976ecd907d791bee95b803c3b3082b387b193b89cf3418ddb405cce7540eace2ee778b04a6264a5ea96485178b774e1062b2eff6bf6303a1597731f58e2ce2d977340bc71752ee780e0c01449686541758798dcaa1feed091eb9646658c2103e296464f408065dcd2688632ba5b168141821f504cddb2286aea561c8eaf2effe56f1dc0a179e98394ecbeebbeb8f300fe6dcb5c08dab8f4e9cfef49da28e9beb8f4bb2f6e598474e977cf7788afaeae1ae9f6776ddc79c4964b5ef2821d3689733bb2fbe24e9184a72233ad512b7927c1eeea5a9b24f3fd4998efd608e4dc105eae51dfce96fd54e6f547bae47592bc6d045e7ea46ef69739ee7cd9eb3ffbf9fd5d3401490adae4fca60d23e76e38e3d6ffde6b7d25cbd607b1be7ef7dffb834d860fba481bd7f32d91aeeb3fb2ecb643af4bc5a8afebc31769e3fa79c70769d80f36699384e270177c7afd82df645f16934c5c8b24b8fe458eff91ae6fd2c317492f48c38f643aee8bf40b76e147f20dee8bec0bd29edc7dd41b3ffc8f643bee8bbbd9d425be5f317c316e48f68fe4eb8ff88bec38e8fbfa6e029214f5b9d63fd260f308f3bf2926f9beda24f11bbedfd0964851e070b749f0c129ce1a46f05d24f8305cb04317093ee86f5c90cac1735cb00b5d64d177a9f45d81685ef0a98864d230820f52710416e3820f7247bb72e0027f0b2e86eb22792ed8627d83e233d75aa0f8618b2cfdb23e64852dd6832451288afd21eb5b248b26b00e8b9fe707ebecd8336aed752beb996b7e5ec029c3482fe76e78c19461ec2ee76e880173ad83eff3bceffb496ae0fbbe2f0d9049708add5cebbabbab5ceb7e921ae8ba67711124fddd73e5225420097c5f0f32333373cf6772dec9af906baf6f3203a3ebc50f1ffc0c8cad677dfdef33307afe7af5bf5edfaf31c05792efbf1659de777d677d7dcfbd7a2cf0abef2495796bfd6f72bbcbfdddfead36a8fef72c1ea0b26c8974599375abee5eff880fdd6ed76675edda3ff9491bfff9fe4ddaf83b59c291cbac537effbd937cbfff2a9151788744b873caadcf621226b77eb5cc3a247c64593fc8ff7bb092966888ab70b86b04735bef6e14ded6d767f1842997f547ba92444322dce69abf91776b5f7f164954dfc8bbad6751c992cb7a9c2111eee41aeb2bcb32ebb83842f7b592f3bf725efe8e2cfbe733b5b5674e8e757e5880ecc1bc7bb0f307a7c86d03bfb625d2ed79fb9899ffe33bc9a1dbfd6693437e5fd33259d25a2793ee43dfd5d5d5975b9b1cf25bed9077cfde7f64f93d95f10a511c0cb7ec1ffabec07075a9d4921544bf3edb72bcfc4d86fd54beb27f8888ca778b90eeb42c2261efdba86febbd6fd9a1f9e5521669d497beb5493273d7bf4524fe1ec9e57cef9da4d29ef7b61cba5dfa5d8974bff9555bde6efd26677777fd598434e423f0f77b437e29b5414aa892f96e83f89b59b3e2d03327768055c6166f787a2663152e32bf6fee52f1441f1f7cf1a5ede37df8fccc7359dc7a2e3243f2b9c673f9bd24f5c93abf4e92ef145b606b04d66b5e904524fcc1c69eb59ef5e04f11c9b425d29d2cb2458295a4326f392feb2749655e16eb2bc99782ae6987fcda8ce077feb4cf35fe792bc9b241f5c1a7e250bdde8321f862fd14e7d2e86f59967f82952cebb35ab644baad89a405daf276c19f248bf49eac33bf340092149759673eeb8ff8d0ed7a3fcb5be490b3fc59e4541afdc726f97e64ed7a6425c119ceff489b6987e6f52c6fc1e1ee8feb8900f426a431a8fb0f6ed9d989a5677c3937830fe0c4d23e7ce9037111229e4b043f12a7f1f399ad6793966a36ef99626c58da2776e9d70c29632c85eec4d233fa3c68a090e4fdfc23fdc2584e2c977ec9685cfa37fed16974f8e004104e4051e17036efa96d92382a821a4010431027b0252dd56cf399626cfc494b6dfb9e6900b6f91ec93400db0d8914e6df50701afd3975e97b7f432285ef3d7b3be234fa9fbd219182f7d3dede69f43dcbc3b9dcfc92a7980632a8800837c230820c2efed21f1283916d483a6071821f7e98a2cb1233b0b18dbfe4b9f4594ce17138dbc4d23e1f5cfa9c436dd06ceaa63f80cc454a1ea457d93e2dfa1da547d73b332eb55de97df43f1bc5b3a5f742ae44d5edacf7a5dbf276a92b612d6cb89e25a2765f1e5d21241fae17450049b9db7d496d59e57a14d6a2e9765f66e052cf46e1336e67a79e50b0f30515f4062df7e672aee9cb053bec48226efc47afabab2b1f6c2c227033632861650d1cfd39e4575797085c0f1a86c8e244d48153125238a3297cc62502b7f30515382a0248ca55c1b1164d36fe3203b77be60030ef949cb3a1896b342f1131f0193696c5f11936b637fea3283f7c000c18bee070b66a7149389c8dbfbcbddb720a48c34af253d2bfc506ec7c31c5958d9fc506c0c81823047758b9bab2f1c7aeb77702d02925bba7f3ab5df7fe951cbbce6b345cece2b8b29e754fc979410fe97b1d9d9776c3c0bf72112f65d77f7a7d67bd7befdefbb3bcaf9ebf7befdfbbfb77e4bca047f2ed90b41bf48f74bff7d37b1bfaf3f6b5a17078df7d5e47ce4b495ab9c6ed06c8dd532ee23d7fe791437ad6bd1997a767ddb3e865ad52df7f5adffdcc7fc0effea67d68cfea774f848b3cd7ea770f9263d3eab7c8dab4caa3fa8fac69f5bb97b50febbbe72db71c72bbe7c145ea3b55e891fcc1eafb43dd9b9ed5ef9f3fc5fa36dffbed6bf37dcfb91ad4b89cab018d2beb597d6f885e96e59ed58e6bfc2c528f9cf7a891ba7625406e9fcf9ffe47cedb91f45924a17be6da08fef4711082c503f8d3af4f6d592ffd7e8a4355926280017787ca0bdcc08eabeb7d11d2f56cd1674bfadd91423529061868951695545b56015924a1a34fc511bc67ae95dd47ceeb911d126a6d92f8d3f7a736a89fdaa1db2dfdf677b353007e0f4e1109f8e1775f49f1998bb8de7bd0458e8074cbf0c1677184ea0aaacf1f9265fdf9e014d9835dced520c6edb8e6b53842f78b64fd16392f08b278807e16eb4592f52d92e7b3888465cb79d996ac6716d90ffe9106990c1f24f9822c92c0367cb043909c9745125a97b956f283e00cc1f08f34872059490c8ce183dfba654d526f688358df2059b27e2ae967cd568be4cb22f956be5e475292f68cc3e95a409e32d22f65b77b9eaee3b9dd773fa47b5913b7fb9bfe695b67e4763ab7fbb17dfabbaeeb9e733a1bc4b923907932cf27207fb0b0673114daa77c1adab8b92573b9f4bbff6af7f7f3bee42ef77b229f4f594b689f7264a17dbe37d23e35f77b15da87733534ddef25d03fd4e6dc85b7dcefb97d6c6e19bb2591fb757fa46fc0263f21a608eef760874df6952171bf07b927bb6b685e5dfe3ed63ef4bfefbfe79ccf0671953fcdda074baee797b21bf6e176ec3e092ebba1fea37fec9200f20e19230d586eff386120bbc029c3487b369f67fc29e92414d0b9b87bcdedfff096e3cd097b46a3009e230826bad0800439dc20031bc760d4b288643e1db17ce9b17d64b73fac3c6d783987a58d4badac67937336009968ec1ab6cf1c3b00b91c1b745dcd30c70d888445871b9e7000a34b1177d85804e7734ec943ce7bc1497b36699f4049f06cf9d7fbca1f8cf6cc96467e5b2f6b1ff0fd6fdae7033f16cb96ac6f3deb0b7b36b6cfc77a7ff023e7055924873f451278dcd096e1b7fe23f986a4915fd683b379baeb23f98224df168be49e95b23bbffacf95b771d3986be5cd9db6f4409fd323f98a30bf9246dd9d21d86195eb3d93652d2b7fb0c9624ecffcbfff9e7d4c52b29cd7238dfcf24f72ec19dbced69ef578bffa0fc5e170389ccd7fc645fc7acee569cdce03eee493b1d6366e7f289bc187db7ff3649ccff34ee110c96bc42b6d5cce5d11e3967ffb0a16f7fd47a769fd8d047c79df5988b40fe7ae7cb9fd46a67882ff7c1681c672d63d11cecdc074fb75fa67dafa8ddcdcb44f3f933c3d6b9d9ef57b47922cce581c615e9d9e4d71d6b37e0a8c47b7b60fe774371d0790c9381ff48e9832e2703818669f30bb7da4670e23e736c0e57e709d72112e1a3977258acb972fd885948bf4e5d2bffbf9a00bb26b24bbfd4e96936888af6ebfd177e9839ea8e4d52473adf4af9cc4fbee5924c1ff3bd24ef2ad64d97d39afff47da28f1bebf6d50f7def3a5245f4f54f2c1f52c73cd9f8a48ba196576fb67dae6d31695700da2cc6e5b3bb9d6efe209b3eb96b9d6967b3691be498d00b9dd7f2673bb2d274e54f2baf49da4422fb5cccecc5ce3205dbc7a7693bd05387f82977332bc60a6d132ccc0bbcb3919eaa0fea0fbd0bc9acf62166f06f1724e863ba693f37e97734c42b02ee7984470653d6363d620037617b9d621d7fa99ccc0c839a62dd72fe7985070cbcadf2f32c9d4c495f1e775d535763b80dc0ee89e1af9ad1d69e437c9c4e170b83b9f92464eef7c4ae7146386ee0f3a39599c7fa4bbeefd5d44e23d73f2eb9fe2fc16bde7a40138b18cf3d9320d387f9224f0052739831657d6b2cbb9a52fae2c1eb688645eced55075653d6b71885ee61a7f8b4886e6d5a5e15a7f8b24cc4b9f4916f927392f9343f3eace771109b5b19ef54f52c6b57e273530724e862362345c846fff28e322ad20fac10e1685174e923d58d833ee597973e7bb2ee764c8e2ca7a361f64ea3db973790e20370876cf3dab5df7471a04596c40bf2d912efd239b3b8b2af8b7bd21d96ef4dbe64ede906cf48f746d9fcafed35f1af95561e4cb29cc777b3bb22d213124c2f57eda3a92da72deee3db2f4db7d93fe47bc6d2ca6e0b6f9479a927c9b641bf74fb521f9517733813c34726ea98ddb5dce2d65b90c63918479fd8fb423cda9c4a70246160d807429b521d29c0570b204e49e913091f8a74842503fdf20b64347977fb200b202e81d2ff8a29d9b8074fbdb863df3f7a79d4742bfff91e6f6b119a1dfdfbd9f4f6bfbb82dbb0ff2a7ef791fb64f5f6fbc2a00590123f8a2bf84e5f2fc72de69c39ed1a70fbe4c18fdfb878e2e1d9a97da0a80dc20b74f7bc824c8394a6d8497a77d4a23df3ee5cc48fbd0f7d7699feffa8fedc3392531ae7fac7fdae639d79fdb27259834ff120097ec88e099bf976002e096d4a1fca71301d3fc69ade328fbffe0961fb478025371fb6b869431f214976f49adae9b20e91af5a5cff405705954d25d6abb6e7e51204319f90b14e42eecf21727e052afecd2f940a0187279faf7a5ce65066282f173da87763f85726eb524bd030a19dcf9a3fffcf4ec87acc6ed9e26d9badbd95228e73e1b23dbe6b482883b3830f586153a9c61732ba048e3baec7e4ed3e603891abdcb3928a86ef9777aed5e80dc3eedd94a96f5d6ffb8c8e77111afe3229d73116f2ed2938b4cae79cfdeb441defb7b5e93b567f57bef67b565add7fbeff3bcaea3d4bdb9487def6792ef76d523eb7b5d3f3bf4dda07e4a96fe4548b7bf497b98028bcbb929a2e829a06e0bd13ede50fcd3494cebff61a6958d85e5c33c2b3bcbed2f6197610f8021978342ea1af5f5279a4d65be006e8b27787742b14e7f0f31f2852a29974b446fc95d54e8d9a463246f433581ce3c9b30da339d9e75397358ac6736582e7d1bcf8ed0d77bf642055f6ef7448c4471fb5930ea1fcff517a07d38f74413b779b84c223ee3ed248ac3a27f9240272438f7441bfd1ef9002b73900fc045910fc045d133be3ced53d621edc329788015b639447c76ec9b9e35ec0131e47ac677bcdec30fce56f25c2682f4c412414081613583191c61632a2ef3cf8ef40fb5f1330466940f4fe0da89266eb71351dc6e9e198ce53febfb6e0ab2489e32185bcfb7f5f3bfd6f741f4e9839d888465025292efa72d83be9f22fd8f0cf23e4a270f6bfa1b15ac19e286d3283c37c83fec197ffdf69e7e29c473ebb31eac64ed591be3f77c9b45d6cbfe536dfdb59e4065de69a98dc2733d1b44bfb32358aeb3677077f7f9eeb3c7985eb610caa17d93dc44c5718650cef422c4236344172210e2f9beeffbbeeffb84be28dce5234beec25d7478867011f0f96ffa67e6f530cf1f8e3d00a6b7c9c4629e5b16461221f331b171e61b1623ebd9477bf67d0c19ebd9f733e437edfb1639eb190cc917f6ef3f32b0960c599bf63d8c1c7bf6fd48f2f4ecfb1739a467dfbbc802f4ec7b912ca167df87a4093dfb1e2451e0daf7ec3f449af67d3f7f8a23f46d59eed95763ba0b0128da92874b17c6e2b22dc51785786e149e1b147eabe4b9b30c6dc973c3cf893dffb70fecf9cbd88d79181869a467fd317248d3622cf8230cf94e9bffca9189e28ffe03f3e3f7c3902c22196d19fe57fe15df4596e3156d19de72e48bc20dfabe1e39d2d8b487cf96b3fb31598e2e11c808c67f1739739aebbb63f7fbf0b97fc2a67dff3d7873bf77912212d1d69e7d7fc443922c6577da3246b2ec67bb882ee3f89e9d43e61879cb9d638cf5997462ccb83c1632def295467e8634adfbafd67194c9fe673c3debc68f2c6796ebd9982582c575e6be7624c039bbdd29ed989cff8948645f8c6cbd18d97a3306237fe7e22a76e8206d752c2fac3d63ffce3fa8b3b567ccb3d6cbcc9fabac72e9e5cbb34507a50e7ed37b1aae75f4e69486d487be77e1e4e2fb96eeee20839f0472b808e55cfade046bc8e4bbe82efd4f4442ffa667fc32aef14f19c930a21d2dbfeffba8571a399805e46286dc1b605b9670895cfa538c71be1091cbcc52e8bc80bad3d9656a4ba4282e7df79f618cf35d88b75c3acb18e74f2e287da702049fa76e39a48cf5112ea202d7c42fdff5e5ec87884effcc9ce6e1f74cbb7eb5338db11c5b3cac073b474906ba30a28ba62bc4c3c5849eb946a74d7186e4bc2209640463498488ffbcdeffc88b046718b2fc27d6347fff58f86a8c446e8b88ff2421517fe96daff72f40fb94d5a7be7f09edd3ad677d68cb1b6f81ef42442eeb5d547253010e42405d21ee7259f4b2c8b23d7b7d2491c953617b4ed417e7943377bbef723b59ff94e0b4ee8938ad7b7e06b2c548ffd6b30ec8d408648ab19c5aa696f6e96f6b342f121fdae7733aa27d3e4b0454fb44b58f4d4f2cb77bbedfe8b4eebbf17295a55c05640463f725a8c045d0184ba6badd3379d23ef5bb670137a56fed530acdeefcfeeeab98506dc95c5c44e259233a9be05af71d4219fb482023602d3dcf184baeba5df7538c4a2255e6531212f497de2644fb94d3a97de8779f0f8b48be5aad106f51a28121b8f042758598ea4e7a3d925e4a7208f363fdc3b66aebcaf176b652e7fa7733f7d9c0df97eeeeddc75dd7f1cc72bcefefc85276f9fbb2eb3aaed1779f1e59c62e7764195eae43b8c66347070d27173d3db2b6c6e8d9dab376a6773ecffe32bc73ea08d7d8081109e480219433847f96c3c577f937936a72573bebc541bbae637ecafcb2aeeb28384da0725ba270fd2759f2dc39e79ce5ec9ad0b37e27857898cbed233deb9fbc85889139bb5c29223d6b4a96425198ea064d8b44a73fa1d333de22813b9fff36c5a8bcb94239977bec596cda92c89ddffd4ce7bdf7294444c65bbce9348020c8df24ebdce6b94de476f9df7739b7ab48c25a8fb450cee5a79c33e43291cbb2d28bf5cc93fdf5a6673b7276bbfca6946b311691944244eefcb6b3fbc299bffbd2f3f83dfe208fe716fc938b7e3ab718e7cf1890e075dd7bd77d10a5f426d67d4929ed485b32b9dd53727631cea746dded3a4b72d9755de74f20b70fbd6077777b7bcb00f697eeee3d6bbfccf432fbf897dd3788a8af5f5d3107807ee9de414e49a2795b095f5d5d6effb2bb8d5a890817c9a76701e98325330d625ba70b2a7391c945f896f4b9b6c6382f7dfa5e7b367ffec806f885970dd0e6394b505582d9ed449313b8ef8201726fc0eaf60c07708740ee12702901167480f38f984c7c694288af7d027039c744976be4d7bfc8fdb84873cd595150b18aeec585f791f1b1bcd4d7e55c1371b0445ccb69006b53a8456c05e1a247bc6050239d02165601d3da2226468232623130323cf50a3855408239aedf2e0196db3fc4c80765c84528002e915bde9044fe4454c6cb3ff4978a5fce5d4af6b8200d295943ca2ee88594e471c12fa4e4cc056b48c9980bb2424ac25cb0155232c805c190923c170c434a861714434ab62ee80a29c9bae02ba4a477c131a46477415848497a419890927ec19890927dc1180c2c2626e3b24446e3ddc9ddd0b580f30aa8286e49a19a8082f280169d86c262ced9ed05ea0caa840f4a24415929d1841249283184a7441b6228a1c496fa8d97734ac8515d31b0bca6568b0360750ac1277189dc12276831e10ae7d42b146364282e505bc46496f012c25c2616de662249092c4b58dd3126b3c41bb5c28493891a6238c74416d8f8728921d8625518170c16e04cc1571954d3edc94dc059c41836146e323373126d24e18593f8726524c87c11d39fad4a9ebaced39673887333b72a798ada72e6b69c35151628d87f3f8e0ffbbcf88f0c4dd9552fc8946d35f323e3f15134b6b574ee3fd9287694d31aca69dd4f4e6ba75c12b95bce2a9eba652f4199ec74923dccccc7bccccfe75156198c2dff636c29b3e5e8d45aaa6664ac66aa50cdcce0bccc30cd3055cd34cd3459c954dd923249d9d1025bcdccd4cc8cac8b4cf6fd4de53f3c686c6c2ccf6a6a2c0d0d0d0f4b79f008656f4566ca66acf7f3a7d37492eae9659c54cf1ff3b3cbf3ebd9a614fb39f52fb2e78e71dad74f29191a1e5353aacb949215999fb132a599b7223369ec0b323b3363a79722a4db39ae75c76c59636c39c2d85206b3e56cb4e5f7bfc8ceb195950b9ef84007d0880304b6594b2db551830db87165fb5c8ba8343c80abc3043656d73147087032e08ab0816c2859e9d2050c4e6063a7b95e7ca79af1c2644714554d6ea585e25ea04e2fd0a714c0616b24a66e4999a46ee75cb3d9d8b2a54c566df9f7b3b9dced9fe5b4388d9d3c5bd6a7ce962394d3f8525bca8e2411e534be6ecbbf50311dd303c83925cddd999f89e241130545c38366522d4eebe741e79cb28ac9c46233333433333c66666a64646256c94aec9564373ff333df3236375f4dcddbd467ffe161797c0d8bc682cf36997d61c6be20f3ad076913283e65125f4f712fd8bbd5b7555b5575eef3a8ea50ac677fe2a9b5bdb853d15792b1de45e6adc4629e6d4a32d6a73e86a45ae69496a9db1ff3e59c1a81d32d1b57c563d556fed34f4eebafa3ac9f6e3f68ad1ad731b69f26ae9fc62a1be7d47c9a5652d3d64f9d1a14fff55552b7ab6e4ff194ffc8c4bac81e05a74d2a1e305e669e9d36be8cccb3d35c6fc563cf4e0bbf63969d063e28533ff6e28c65a7b15ee65f3c2c3badf5330fbbc92ccff7532dfec3c3a2e0b4f93330b6e42e773ecc965566b42553ddf92f5b8e31cb4e73d992bddcf9a22d65a12db9eace07a996dbcfe33f2d5b56962dc7caf3f178b6ac9dadf3eb6521c1348c1c20e780ec3face707adaaa68d454e1b4fd127a7e12ad3d8c44f71fee3ef24d3323631ddae2375f72e5053fec3b62e54d40b7d826a2aae7226ff513a8ab1d5af4fadf0c9296c396979a55ba8a5c97f94beb7e255eb4c4eebffac37b93375d465bafddec5f2fbf7f4bd9fdfb2b63fdb9e65a7cda6a6f2a2e5f6971d75fbbbc97fc62a1bddbbc828eef697dfdf915063e950936d2fa34fd73673d38b97cfab9a39a8692515859bca35531729a7b537a99a3edda64ffed3475831061a1da86105b67e0ae53fae830dbe1061441054d8fa6994ffcca519c0b8b2baf2c2d64f73fe13e40a1ecab85ada62eba752fe339da82307263a70c0d95a4bffd4e23fb76903bf5bdfacff89f21feffba784fd55fc07b42538adb79838ff695914fa2793ffb0ac0afdb3a95a262f789605a7f537f52e5cebf72eb7dfddbd9f8971d2aa0f89f6584515ff61b0b2be9c5a2e2f4d1beb5fb077f173abf52087e2873ffeebcbc974f92b09b333cacb6827d4d2b4bd443b9f467e1496a60df6adfa254b7dc939f04b2297c32f792effcc7f6e283c3f8c9c52e48b9c5d5ce4a422a717d6f3d72a4ef347c169e2f38f22682753cbcea6b6a5cc4e2d4e9bb6fc3bbf9c52f367b30abe3f4f0debfb13a995b258ef5f82ff80e0cf9c361fb4ffd992a5eefcf23d5b724ec6b263674b22d569d4963c7776cd8a01e49cb273255b3dfd92a7a82dd996b3cbb664abeb930463399d9e6ec9565655fed3a50b1595172fe594f29fdb1fc5d8fca793ffdcc4f7befbc207bf64a9af6b3deb4bce755ffdef4b22b79c4e2e7b7b41f4eccc697d7bc1fb723a85f641a95ccbca589c8baa76fc884079b6b2d34a9e6e2175d31480a327fb76da7ddebb9effc3faac16188ab19779feeaaed7088b81791ba6698bc9ccf0a8f9a4a56aa37999272df1d86c6c0d0d199391313c666448598ca4e120f363c8fab15a1c040c6b6c6e38480f1f381cc425be46184ccd746fba8db445df738deb9e7ef8370773b85ce35e56bcfef737f7a77f0bedadab9ce650537846960b1c5b7ff3b1a79ce6b73975e3d9e404c29acaebec6d56fd847a7d2e741c2bd7385c2ee46155415541b1fe6b2a707cb6815fb693d89a4ea3eb4b1e5ff65db64e89a1958176ec26961dab954d1dc5d83e5b9dd6cf36cfb28dfa0b69762b0b9073ca59553faf7bfef71ffafc1ed991569c9256fc236f325fc91b8f3f8ab1252d8936eafdd2b4794d947e47ff567f69daeadfba168bbb5f9ab6ce5b323154ad5f9ab6968cbd252d89b62426116c32bf346d37245288bdccdf78d8db8c5d9a3619ff987f8cbd4d2aa7f9c3d8a5696bf9c3ec14a7f9b3ec8d89d3fc47bb346d9dffcbfecd6597a6addadbe8347fd14ea71b3bcd3fb44bd3e6f9837636396daa7aef8d9dd3ccd129ef998ae7e3aad69793aa658f626cddb3ec518c8d3edbbeafcfb6fae584aae4477a6447ca9c369f92efb4396348b3bfafea8ef44b59f9b79cf5dc31127dcbf5ce62bd7d4104bf5b2dd0bec00a9f3efb4f685f68bd7e3efb8f52d873c73834ef2765530affc624c4f6b22f802efb42f879569e95ff88564914ad67e534ffd02a85e1f753eab3e583560904676eda52d6b24aadb7525fa96539ca69fed47a562cabc47a2bd5be409f6d4a2ccb4eeb9f504efb703d778c44def7cf2546ce2181eb27a927ce2181bb9e37359f9efce766c58f626cb5f5dfaa3259fdd638de723edd9884d858764cd9acf80bec3411aa6c4f3d778c7fdb9673887ecf1de308ccb5b256f6979fd44c629c498ce5e7e459959f5397f9d4b7fca43e29ff994f4ef3af751c65b25b7e52d79ffdffe23ea74fea73e2336ee94d7956de54cf2d3d9cf7e4e13aa82e1d54ec9694aa63a26add9236d128dad4ddd2735e5c8be7bacab5485955b513aea59eaed55339ad7826ee4beec253d6eb7389712a31cedb5fcea7afea32074715a9eb97179066c766c5a9288e15ff52d361ec26e78be9b159695cffd2fc6cfeb2390f9be3d8fc039bdb9bf74c426c9dc454b359712bdc92676ba6b675cbd630b6aeb1b5b53508b6b6b7ee9984786c24c9ca6763e2b1f52fb9cd3b9b8b3697b1790f9b7760737bf36712d24ebbb637fa4731b6ef6bcf7b2b2eeb3acf8e6e6f4c426cfe4c426cb718b7ecb4f94731362621366fd21cf8fec334b6d34f5a7a99edfba4a5d1e63df8fcd57f929886d89262bf44c4d6faa5d096b4f436d6d7e797f94f12d34d28be5c3ffe528e0d7cfe318969664b5a2262837dd2128f2de6976636985f92d9c64f5ae2b179335bc741e6272dbd8d7290f9cc21cce78f215d1c643e0c297290f930f2c541e68fe4c821cc7f91321c64be8b8c7190f92239c341e687240f0e321f24652c647e8bfc38c87c16197290f995ac1c020799ef91351c647e47de7090f994ecc141e63be98383cc6f128783cc3f8a99ad05e8d15f8a6123494c3c36ff256a6ba71d7dfeafb25addf3879dadaed70883f1e78fb111262ac34698c21936c214e3c146986e646c842987868d30e9d8daca9c6684a9deb011a6b1071b6192f960234c6febe7c761234c331b1ba1964536e276ac61236ddf69f3f98fe86c2a9073ca6e0a89b1ec9e3a29f1add05c14ebadb00b4e500714d3101bfd9b9527ffb137b732886013eded86f3eccd7b1944b0b1ec0de9082a64e8ec0d034a503029b18226ae6c371944b0517b3b52c38730b43c75c9c1e6f4bb4f5a0a6d5e8faf55b79f296663799de7e35bd489aae3f134f63c62ddcb4ce13ccc146da2f65dfc1b1229906fff063ef9379b07e9e76ecbbe86e2fe657fc3ddfe9c8b1c7ccecccddf904821e667fed6317ffbfec614b3f98714eab6573914cdbbfe0675fbab6ee7d89bf81cd8b7f63692f6760404dfc6de68aec6dea8ccdefcade778d81bc7622f636f74caa7702c6d82c9be656fd487a5b4875dd2b1d5677d39e658f5a6e66d605ee4f1dd7a98538beaf657279967395131958dbd895f639938ad7fc6de50705a7f8cbdf138adffb3b7251ddbf7a1bd5128af72a886a2b15df53cc5e3c9441e33cf637c2fe6f523d46d575d280e21c40b47257abe100422480d636ff0b0421925c6f9bee3f44b77770edddddd8180618e12351a86620e3a345dfc10c3d02592e0e038b95d25d205e1be42100b1a1d43590c66b090470c1fc0843de0f020268c3131cee79c58c8030b0732618b0576266cbd80e411be154f65a10f2a7068c287818f9a3096450f9b7044c10dbd0963399b1ea10f236a7c846710118387cb0c8be72d3223e390a9537322dca0508e713e75777797c1096169c4ea87231731950c7d60016343191630ca41f82d8d342714a35e1d842010aeea410802217e10be72086761cb0990d2c2708c560521fcae60fd087d00e1e9842d233a2021521312e37ce7aacb5f1391f07750e37c7777770a24a470305bba13864174e5093d2e6690b053639ceffccc3b406a3a3f40a0cd3ef0a0831c0e2cf9383e7adcd8d4d0c878ccc8c46260785c43f3eada24e1b93eaedbd202b608af5e3dfc3aea125fcf2292f18f34c82212d81f6998ef594412c3748c7d0ecf902305402105264fa618e1926653cf3a4bab95e32d5a9ae6985a9a6696f6d1021391cc2ae66c82b7cc288edc1614de64ea59e3dc0027132ea97d7c1ce793771daa00469c3052654a1b56538caabceaf0c1881025108922c2021339bc3079c2822b8e26225a4c1832c5101550f0d20585145410d7984943b0a0c043029e128ed0c152470a5042488615cfc4a5a053021d223c7610e11942045ca30d9d39549849319bbd0c669f336bd54135f322811c26847264630d1e7cb0caea773f4b4452ddb84e2f483a2994735b767eeb33c1c82dd2a8bbad56cbd69b198ce574fad6b784e8a72212f08477f0c90d15df4d8cf5e54d7973c1564bd602bf7ecb96e07f7fc46364949b5b3ef944245f3512c7752b747363be18cb822c0aef5ba4904c268bc53e161945764b16ae7ffdde1391781f8fecc278304484622808bf450ac5ca5819bb2d4a416e9fd67b6194d82d85aeff4796fede772292d0bd1cb742b10b0b613a53c613bce8077e39dedc502af47e0fb6c872bce00bfcd7d3fea14d03fff5e10c403877c07f3d8fafffc872ca108f4f45242f6b44ef28bbe3f82f925ea3f009d7b748a1f00365b2da7a6f916578c177b20caf0b7cd7d719803a77c0773d8b43e1f56c697443f09d74bd8b485ce183e477757555c70dad5078c50767e822e975d156f97018f9bdfb1669e4d73ba8919d9c2c67cb967f5bb29b1bd9188b8de16d7d19b640f0f35bff48b3482a2db2acb7f54e96357c7f3a03c073a7a4b7f5fe21c93d6bfd1107ed90b7ba67916595a1d92292eeb31e299e403b6a67cce4a00890bf2231f2cf0180ff334aa9fd2029891f1b500308aa314b07738cafcb393594408063ac76ccde185d7074808291950608368ca3173433466f8a668391750508698c2214514fb33ad200c20318a4418307b934965890460c52e377830fc8183d393ec861a46f7840c358d360638c0ed0183b30406862ac5b7800c4c892ea800553d0e2780206682cd102348ca08931d6343ad862a4657460c6c802c3832c63ddc28df15b0108478c314e80f0c4c81ac1075d8c303efcf8800e5acec0b2c6194a5db8d8f2410f638d0c3c9861ac29f062fca2e8e089b1b5841523ab08da0c660c7141b0327e977366fc70671c3043cb9d9fc3050f6a9871cbe761dd3c7f09fe439f7f8afff8b3cdf3cf9cffccd6f31f61c17f68cf38cff6f5fc44fcc7d5e3397cfe09e53f391ffb98e757c17f609e3d781e9f5ff6fc28f88f8fe7afe23f33cfa408822deb05e19bb499fd07dfc177e48d55aaf99bf748cbc1dbffff481cabe475f0b3af24cd2a75f7b407c91cab94f3cfc18b648ffeeeebc3480fac07effd072f439256c9929ff33c481f568926f3f56948259b57f2f15978b9207c4cc7d6c3c7dffc8b54f261affcb02989efe36bbe5563afb86c386f639568fef537f68a8c4de9fb9a97d97cec5b1f432ad9c4fee65d37effa98bde236251eff7a16a97463afd46c4ab0bf717deb29a9e4b25704605302df47cb5eb9b129b5bee66dec95984dc9e6e9c3904a32d55ea1b129d5aff9b657aa4dc9e6fb9d66af4c9b52ec6f5ec65ef16c4aaef7f19dbd02635392f9d7cf904a337bc5c6a6d4bdcd07f64a8e4d49f6aff7e04352c9037ba5655382f1ec95974d297c1fdf81bd42c4a6e47dcd8fa4921525245b8ebdf2814dc9e6fddf5ea1d99466fec5fa7ff227a9c481bdd236a5f16fde8a1292cdda2b03b029bdde87556259a5f19560af04f34ab18ff92bd3e6e3497be5c7a6c4fa1aab54f357a6edadd27ca57e257fa5eee9e3904a3656e9cab4d9bc0fabe4bdd2f74aac9fdf83548af91bab646395645e69e69578bc12cdcbfecab4d55825f095c257125fe9f5ae3fe2209047b94b43b2d3f865e4cc69fc3c48149cc63f4356711abf0c399d9cc61f23abd3f863481ea7f1c3902a380de734fe919c4f4e637e1749c469fc22c9c469fc2139999cc60f9213ca69fc2d52e6347e1679c469fc9564c1694d4ee3f7c819e534fe8e7ca7f153b204a7f13b39c569fc4d4e2d4ecb398d1f691ed1ba12017283fcdc73c738c284e25abf0a5cebf6c058765303317ee397b381184ba6fa619cb7ac138a8b14f10f054dfe8f6a4e8f6a2fcfa45d62087e3b8d51dd62d5afbbbb9b54e02245303f14f47abe4530b6a9b856ebe787d97236bebe9c512b98ba8d858bbcec7f2d8e9a358ddf6539b4207b5354546b4671b5519f8dba1dc5b5fe26b6d591868061b3aa4175db73652dc752e6e4d04bfc21309cf3f54114ea5f41f4881b97f841f44926065121626010751abd20ea43cffa893a5b06512d3deb328836f5accb8f36d1b3fe209aa567fded14fbf7be7bfafe9ef39f98a5514eeb87b1ecb40e46479bcbe572b95cee874659772fb7dd8b7b712d5ab468712d2d56fdaa5aacfa55454949494949b5935454545454543b39393939d1262ba7282b2b2b2bab693535353535f534f5f4f4f4f4549f260e377113377113a70277e12edc85bbf0f0f0f0f0f0f0781dad4d342a2a2a2aaafb69136da24d54ae2ef234e322e00fb59c862068cbefd97ca2962d6f7a36653d9bb19ecdb167734eff30ca25866054944b0cc1a828aa16ab7e54542d56fda8a85aacfa514545b9c4108c8a72d9b28ab61cc3a81b2515e54d2d56edffbca9d56255fb7969b1aa179d5c2e97cbb596afcb75aa5a4719ada202499d5b555555552555252525252535a5a6d3749a4efdd3a98a95959595150a3cc5533c35f33a3a6bafe7d3ea55fa51a6fe80538db2060093d314b79c5d3ab2ec926b531c8173458a3c5abba9fba0f97c51e0daa4f1cf6c62f1cfecd234c50880203272a4b9eeb41276fdc18ea47736f98f6cfc88fa0ecddb3f9b6617ff91c9c6b1cbfdbc26a70d5cf3aad9e7d1ef1ec67276995daeff6c721f6532dad44e1e0c40ce017bee1887986b1f0a5c842f5bb23d307a141929b841e1d77cbf47d13e6590f83639d7df6c797d0fa9f17d60017b9c29987f2e314f7ebf77699f92af626fbfdfc5689f3248e639285dcbd7e780742c3debb7a433f5ac9f243da967fd4f3aae67fd3864fb20dbaa67fd3dc8be21bbaa67fd3664d790eda56773d24cd9e43167a60ce9627817e7e2538e854bf916cf79141ee53476b716b0c5aadddddd44aa8f54d6a19792bdcc5cb3e03fb58ea34ce6e57e2587442691d9fd50277573924d5cebff6c39f3d23ebdb47be9597727f5ace7979ef59ca367fd234c25d336960e8ce5f40282b1e78e916f0a5c643ecf8b4596cb392c926ed95e5860753d4bfbf8f45cd3ed7ce1fc5318e2f926b2a9b8083b55d3d7677debc1ef20c6a27ef7a74ced1334ad479111059628ec68ca65d9a245ca072c9ca684e0f2d43e41dee58a1ed13e412ec6d5d597ebdf3e8c657b89baccb5efcb49e54d5eaebf37799337f984729affa4f3b3448ce564bafe93a9a96758faa7b364e99ff6c187fe692184e89f3ea29be8997f3b8d2591eb445eb6ace0d8126d2963fdadb63af27091cf76d4f765375151f97ca69d37a93a8a8b7cefad6e7a96fd6acbff9494df1677529df15f4ad99ca367d496e3d06ca367fe2328999dd481712289930505a5df7bffd510fcd68b1fbee892c17ebc8979988f199a5f24f3b197b132b11818d8f8728921d862d5cfeb6833d163bbab5dec76a7dd77d8c25f6a3d75dbf676e4bdf72fffa6dd1fdd34adfe47acef64fd47ded3efabff512b46bfffa8be077ef7fd51ebfd975a369817addf7fd4bd0763fdf74720cc78043ee85a6ad9446b0567fcdb52cbe6b256705a7fc40261fe1efda3eefb996a6cafbf3deb8fc0ff7ea9651bad151c97bdb1beeffa47fef4996a6c626841cb2dd6d3ee8fbeaf7f3beabe6b59fab72396ad7f3bf2af7ff43dad479f773beae891b7fd0e01363f2840ea03b8d93c538dcded8dfe6dc9b3f90fe11c7d5bd8dcde965c367fa6191b1d9ab4b3f2ba62aab1f9dface02cb53a6aade0dc8ec49f792baf1a5b4d0c667481b0f075139b81195d60cc0b269b81898d2e500616139b898d30957a3132638f99188ceb8fc21f6f40d6d7fd91f74e440f1b9a97752f93592b38b7a3dab2a1b156706e472d5ad9064db69b9a9918cc08fe51f8aebf2db56c36d60a0e8dccde986a6c3ceceda8f5333198d105b2beee8fc2f7bf1dd59f89c18c2e90e57f14fef7b723fa33b1bf1dc93cccdf8e627efcdb11ec5d7f3b7a3df8b723f1bf3f0a9f35637364ec18b31f636330f6066665a395bdec8dcbc6449bf347a17dd08e2dcb2c1b56fbd9da21f4df8e3c1b760821f41f515b3b84f96e6987d0cf7f6433595220fb73fdaf7cb074bdf83cfeeb19e661cfb18f29737a46c690dfb37e1812468ea4cc458a64488e2d16890433671b20b74f10578d4145b8bae38eeb4135280c1a836241b2a09ba0b7422204e5f8d5d51dd7e71060cf1d2351918f21fe21d60fd51ffa7ec8fba1ee87e80f157d53b7bf0809d16422b6e5b39e8865cb9bfa44d596b2ef893e5bc6bc27f26c39764fd4d932ec1913515b52a9b9fe44fddedc6da9ccdc69735873c600e4f629f2bca2aefba12216ab55148645afd758040313f345ee324533333c8a643f34bf48f643343688686868841186e60fc57ec87f08f643ae1fa20fbe92fa4afa8766dbf2f6a4b1a56cdaf279d872e62626634b8fd952068bb1250ccc9631d768cb97cb9623f8546a405b862d5bb2a8d45cfa546a6e2da9d4dccf96df16d7f3efec10ced5d5d5a5b6a45273dd96df16d7bf34f2ae5b2a33b7ad6cd227c09b1eb3c8b44c33f3f26a6c280f598d89c9843d9b45da8e21172132a2b7fba1d7a532c3e2224446dea53f24c2a512bb5d1122a3befe437ea9c844eff95d9e8db5b80895f07645a8d0db15a102738b506951e94bed4dcf66112ae3eda7e2ba5e848a77dde6f46c1669eb47804c9fc8e87b7eda7d5eebf96b6db15ccf1f82a14b7cfe1136fe2bf6fc31d78589bd917763de68e60ebd2e8fe7978557e689a8d0cbe38de89d79a3d81d12e1da1019c15cd91319b52ecd1351f1aecd1bf5ad792222a3f1fa7822a37a7bfccd1bc9ee905f1c2223d725a2d217c736d7e61b7977be0fd2b936bf0749b936ff86ecb836df8664716d7e0d59b9369f86fcb8365f467a5c9bcf830cb9367f8604b9365f866c716d7e8c7c716dd66a23d7605c83e1da0cd764b816e35a0cd768b826e31a0faedd70cd866b355cabd56af3917c4cb7ea26a9154859b1245585941452725f4460031ee478aafae28a10b0ae48828731368e35a717d4000c28a83881162fae08afc8218771e6724eaa885b8eef6b744edd690f358c327777291bae63b1b9ffbbbbf70da3eb724e0a861b5ece5d41c7ed2ee7aea8aa01581241dd1e21c18f449090107fdb2877fe5123cd1e01acedd34dd6f993e79c64cfb9022e5cb8542e5c64df5812b9a38f7fe3e0e978f6bb9c9e7161755688e77a9ee7d93f03acb4769de75531c6f5aa80010c150b1876f0210926a82742d0c515705cd1e50a29afbb2e2d83c7e58aa4eb793ef4522b1db9e08d29b810e38c2fbcb03117ae250fe74ca29e2d5e688163ea06343eb0851561b0ac30e3035dd7759d155bb07458baa2b5250b261e991552d430bb12b028a55356f4205af154831532b8acd0e1735070cbe7f18cc86d69aa62cb16301a872e72d862820f6879838c2b1b6dc1a53fc271691572e8d04ab1cbb92d465cf0726e0b102ab83296fd749bca7f3c2fb3ca9b534ef3ec0b1eae8b6745c5349ba6962a4f4c3cfef37d7f49a47b1b25df69de5447da34807ef79d7da13eb5b10d9ce1870485044c9bf77d47ffb347ded3ee864403e61b245801ce569ffeedc8fbefab3df2fe85a3cfe25c0c1964c1d9e8d3eefb65fe4343701cd94f651f71e73fd934a07befbb299b06d0f79ebe376d1d3992b643eafe3cc0e33f6de3075f4f77faeb5d4fa442eb59df302caa70ab475a01e20b302f3a6883bd5b2414a6c2bcdcf994eaceffaa5ce6e9cc53d807f3627d507c5004ad4d3fab71534e9b0fd3c5a6adee7c5733b55337b5963bbf2389ba17c1fa300f635f807d7db689301ff331f685ef619e6de0b745d2b18f817dec5954e1fb986f62baf3452d777e8cb4f11ec9167bb02389fa55783debfd895468bdebfd871a70033ffcd737e026fef8ad07a7a802eceb4f5185ef611e85a4fa30e1d787b136f4679eaa386d3e8cc4d9d0e974e7cb58a8182975e7c790365e54eeceef4652e634a2a27987868814a0801f2252c1a601ad7f7dab9f48059b06bc5e10fff56c83f9b648baa70ffbef41d8b3a882f81f0c09236dfa916cf5412a2a806d49ad7f3500fcd6b7a882f8af175508bff52caa30da97b5f1774d89644856929dd6226ddca2c01d42974ada34928d9d763422755d5d0072775227c5643763cc47d9181bc7366698819513d440b5851646884ed45063e3e51c1549b0018eded9efd97432a767b418dc510618531da0428a2c7ea82148a25688e7765dd7f97377871b556429430d2f6164810596ee061afe676b7ccefbcfe83f9ffdb276dd8fb8ebdd6ee9765d47ed1af53f27bc9ca30204b77c9e6e066f94c5a8e8c0951e65e5786354d8304367a9b8b2e44d41b798428c18a618c30a8fcbb929d0b8f4726e0a32b8413abf2fc3eb799e57dff33ccff3bc8fa43da39ef7a3ccfb59fb08f15c5a1b07907368bf0da57d3bb8f38914a07f2ad5cc9dc0eacea992e7ce1c0a7077feecce2a140c714b2377caeeb4bad4fb3ec08ad50c99c21e1083975bdbc7610f88c1a9677cc1c9ddaeb6338e31e672ee04572e4f39317af7a5ce0562022f2935c11b9732d9e8c7fa87da28a5b3ac945213b07129a5fea3db8e83cb39135c71c5cb39134071cbf1697b1726f0c10447c891e3e58cf096cb43faa7daf87352545dd8e59c144997c5fe233ddd18633fffa69c1d19eb99900c9c88c1972b1a8050818d5e71e98fb92a2efd9bfee11c0e8793c1463fa77fd8466de95688675c63e45b8eb7dae6fc26c79813a3783907051cb77eb9e5f828005fcc3e77a6902f403a521b92aee58ea45c73bf8d9c7ba2897291eefb3b2e22c7954d27e9144e97a7e8aaae3f38d4fd34724e055d9e6ec9530eb502dca5560ed573c7d8cf391574f1efa4fce7880aa09ac65153b92e5254d75530d534be65e7e5fa774efe43a7a895ffb49414e788102969f5c68e366e8a8bb42dfa7ea8e8b3fec435ff69c535fff9d4a58318dbce2f5c73ce498971cbee72fd5b88b1ac01e01f87fa1ff6e11f0fa54da8c4a9150cb4899a041d536a06680441400353156030482c168e08048a26d7f80114801098b2545a9fcaf42888814a19430821841802000020002030334c0002449b3da1f71f290cec0d6a2eda91cd9c18032ff0dca5a25b87109f947ed71b47ba08981d9bf2eb8274611b9bb453e3e7ac8da1b052cda092de2ca54eb20d304da0082804cda4a1f3c20b1db431f06a0187aaa837b4efb965f92729070114f4bcea59b995635e6ab964472cf4a36ab4b19709c2ca9de559662a421ffc529d1a7b238b3f2aedbd15738daf14666fd4f24795bdb76256f795d26a6f84fde51b60d7db9446a2d08e90226a9d8d122b1a6cb88ea4185e8ab8e828c0b547bbd13679d2e8eb62008f3df7c003341b2a56a1313c5bee8c133f55ef2b6be3a8f64de1d95bb8527d6f859fb221f71f6bcd8b6c8a9d7e5ae19cbd1d83b6ef5dca812c912fb71f29ccf63e3881fc2ddb5d2cbf8e2013a84318cfc5ea0ddb63bb22e7865cad61f213d625e0059b3965d0d0ba019f1b9c8f463543f237858748ee5ea3cc8ce2e9169db250f69c42c4957ef25f05bf927d337805e7a9c127a68ab304437b4fdf1cec75c4976720c5cf675cbc7c9600104ec31deab1daa6da90ea2e68b2d3fb183b47fada1739a348859ad424dfcd9b5a51a636050bdaa24005bb3e09d32a06af898c3ed46bd7ac2df71250bb69135da4b3230e9a600952a3cc8dcc1ef833daf8af458999fb360a2a0b55ccab6b747098d712665194cb54527b1c476b4e091333379dbd6d06867ef58142c14f115b2b940d25b4d25e5dc424683aff6af940c398b7e6d19b796ea7247d57f6f1b29f5a3042643962d66c46aea38ee900a581e4719715dac6a44d115c8616ed20a5c64ae32713d2162995829cb618dba14a67e654b9a7aa0ad972cd3565b969a3ac543a5ca6a717047923c352c8baaad3acab74ac18f070c0da4cd214b5d89b3bab2805208b9e7870c36ad252ece6816850c9a571009a29569705ebaac0e73f4d13750b574673759a8e009465223fd72f4de9a84d861936f12ed4982c4395d1424c35e51c48136a3bb71e1d79b70b521edddfdd699a24380f1faa9b9e956b7b3d41859a768f66a9d89e720e4ef1c4423e57ef896749b13efdcf6ce9e1356c06ce10e06fa93e1305e565f67da0669b831e088c05fa2f19bc2ebf67657f7d691c9ce87d1f5761c8bbe256fe5cb3f027af104fab7322ded5187c0c5e42ee42ce8a270b5b252e3f050235054a7b7bf28ab5bea6c6b4f4964b56e2cb65ff8d2cea2a8a4b3f71ed05c0442127b2aa9aa3d5949d533991365698308826d8568c9a443f916060611e49de48596136deddaaca8651c4e1c762906a01a1b40da9f7888ada2ba960e322b592daa3de0455ba1582813a54d77f8d896cf131506088a9bba9f2e218a5a46f5ec66a4faf1697b1753c30fa2d759189369d38b8d6059246990ab474780e435d3f1772b935304f056102dd56386b9372ef6b781125778254d44995bab3eda025a1b15072bad43cc1afd5682b16ce25195ebae8309c013ed5bbfaa9c7820349259df8681249971dfd2c01ddc3db884b424f3a0126be38a999dec88aa3d05b904b4bd83d2ee6043c1d394cfae52f77a340521e074963371f336152fe576a72774a16cc67fe0188393e5257b75f154c9409184dc7882e8e76c44c617f8bbe191a9eafe4d9243cae15e21df29d5863ca452ef3245a288ba66b90922243c5425aba5c8c845af81354dfb276445e7638df27521ac547dbc3cba9a395ba982649ee347c062f49167dbeb91b7d93f4daeadffcdf3b893749e28ea96747a2251dd12dcddf32d81258ac5e1626fd71cad511914d5d789326ef8c6c39e122d373d120c1974946253fc4bfb63eed5e467fba65f46440959dd836ea6d27bc4a21e599bf34788ad8bd92bc90f8902deffc3049c8b70c9de0629f23c561da895c73531c7471978f3e349ec1402aaeb5a6420bfec49334613ba38eb3267466eb8ce8d59adf4b3e7fa89e2274c0b2c1d5e34047f6fa091d695fe9d1064ba530cecb62fcef245c4b8f43d09f1cfb284d2456bb7aacc819ca1888b7dbe2ca0ff83c8994bef1da1806f9c27a7ebb01f4066e489df2076af97793f05fc779fded8cc8857e1fa2017d627eeefda020eaf90d931dea0bf2ef799e2b75c10b0f8c339059b86124a8db6a81dc435fb43b9890082202d636438c8a86aba073f75df8e3cb00f25ec6f0720a6cd342f71ee185cf3ecc02d4534e7487cf64be6feb6ec18d340400426452f811407ee619e2f0a440bf0d20f1252a42c6506345663530c1585405f964c212073a6ff8b86b0d61a2ae9c0511f5e57ec7bb433e6514688a56697d15b4c70ba4dff9ab8ba44f7a83dc758e4cf30c54e781d89513d83d6c9915014b882ac53f346c1c4b54bdb424b3db0688c95498cb9cd1f3912197cad54afadd7992b8f40e38baf374eb32a5e899740e46de3a43a8f39bfe7c9f77fb734e886a2212d4c175032075d54e4f44663a096901c81b5d71042a0f83841fc18dcfbda063064b2b09936097c6d75db9116b120847d2f7e01baca50e9ee5276548f69e0b460954143ed52ace21ac28e0c83f9e3d68202df581b25a558ab3dea6f7903571f32706ea6f46e7d1e53f6d15283d8ec329093f492b9eb51e5911020f4c6275593de84cc4fa54886d513dfec010bf4ac8111316299d70238b13cb64835f4849c7656d2e0a85cbaf474a1b9ef97cc84dbcad93e7bdf19c407ddfbbdfcb660f84cb303a705bea1c8ca65fb7955df5a2799b464b5499d4ffb010399475a9bf24a1a5aa03ef2c7dd2742b2336842a1b2c0ad87730a7c4ca3f80e15e3855f446d650449477629f313c4f6fb4a4217a4a3923e2f24408ec1d306fe55d3ce2f2f74536516422b51e0064cdd56b59be5baedd1e0d3f181055d0133f7cc1fe93eaa5410f74a11b148f70902a9f4e2676a6b4ac09fe8adc5f2a4f92aa5b8eda56735b937214772fda15e585d2c2a28777e3e755039e14322c5e88726dd471b446e35431e4a16d93141a72c3cc5fc1e0ce7731033d8ed114ad8b28fb8134a6a282c1fc20b6c5bd8caf1dc90d42bb6a4b01633d71b7b117faef2ff0a21c6a04c3e80736d1bbe6e843733cfd43091f91d2457f54f5355960682c21079aa9029fd89d215e307f6a4302057a57c42e62e034e52adf29c32f54f2d3d99d80053b9635ef497c564f733fd658c2b2819f189c57e9ab1ca23f1f2edf474eabbaa0371d966c29c6a30ec7cf09e866b7f88dddf0491ded82d9750976336d482c070633bb3b74b3fb1bd64564faff999dc059947cee8bc9d7815955e5e7a395c65a9988e184cfa2aba11d7a67263257d80c86466de8f6608bbe5ce27fe78cec69c143a2f2fa3064b76765d5c5d29e4f467b4d60793bfa8711d61bcd37bd612e548d115f7e3601fb39192e1fc068ec54b4eb8c9bfbc03f0d32c346d587193b8ea03f89d7c3b0388800ce66685019b0302d438b54cda15e038f79fc73f15bba5e4eefa3854b37d6ed5b9871a4c68d574525fd6e5007eac2a7ab425f0f09b43d6b8bb407bdd007d57dfec15cc52977cc66dbd6aad4cf6ebb25c7c66c7477729dbd1e4102b4911dfc436e9c29b3aad343aa41ef334246749f382e62d8d479cac350015db653088a4e93b24be39fa1beb8e1add7776c9826d28c4e8a96d554e4e62560277dd21e413f73e700c1b10e90a442a210b5c402d206d083c21b9b9a166ce94ac1f82a51040b1f3125afd009698a7b3faa49f4758298e68b352fbaf599bcece122f88bb819e604cc23088b88bd725c06333d184a2fe4285c4adb755ee942c78e0dffe0ca705cd465f6cf41406053e51ba9f5d14894bc014deaee964462db16cd2b60296041e8c4028d9db38eb6acb9a1451d6f72e963f8a96bb4ca7913b7e16a174162118e1d94dcb0593f660ee859b9736261b26643ce67ff9f7cb76152f0f222db9042a421a9d4b642938423414faca9aafca9ac52a4e42fefe1fcb3a9769c4daabe989c05ddc21873df5c3296475701c66f8e8e05e3e4626b0bb90c5c81913b61afcfa5bed9f78b5d667473e9752ece61a6b78bbdc0c0f572d765d9e2566c5112dffe7a7967743bb8568337ee2c091e2cd153c350a1ef642ca8e59a70b18be35a73b227334de8796aa5a5f908ce82566553f0758566843c57684a09a19e832946a089080e73ec42c0e15dffa5d3fc3a22ed6eee702894ef04c694441d3882bab5e3b19b77bb39b9134f94c88af0dacdf39e785c32ce9e98219758ee795b329c4a3283c89609c765679600e120e0da74e43590531452a8370f7acdc890af227ef833ed3f89dd90c8c4212c55c15753ebe2d2d9f8d70034966d14f77a869443d5e7dd4f25387ce285d9fe78cffb02122ba74e6ec3d7316672bde6f746497f635c08deb205c8fba9babffdf05937e10c0ce4b7c43971ab6ccd20005665d40581912a831504cc53d99e2078a2609b21aa271506b5435301ef04c6d6457862dfae833c81651b0f1ba9dcc52a791f2aff85393f5d68a7855ab398698775e08463158fb97bbf52eb4ee24fac01c1a5354f3d257ff4745dd952081fbdd08ea83dcd92f6ae1863a07dd78ba04d4290fed64b1c6014814cd81c90ec593fd8fc8c9e66b6c4284518ca65711cdf049454e5a59a16f34e93c1fd23c2445da0b891957c66f9b7466cbc1fc32b0a382351bc5b631b89834eddb2a8d35d2c4f2261733aaadb940cfacc87c0403e7d8ba238d3f1314c24f4941115f54a5041b2fc58816ae3864ec399039a91f8502577fc5e9918a5c0e752ab49da983b4b752de08a80bf1d76cba9f556393aa8ee96a2d9d76f3a8fec390782b9205334370d4f25d0f29510e2f471c6684044fa496ac299c91e9cf4dad132aba8437a109d4b188b6381dccd4af3d725174dbbca4ba552a85440ef9cd9a2a2ce1b0658827443de4eb17e69e3ebd75c3eacec4710b3ee4c188d391abb468db4fce44553bfd415f77672ba125a64755f54e99547dc8ca391131c80eb8b647d9f2b18d07b17d015be5927b2a40a66bbdc312e16731e49afc6efadd90b887593c8f0b8ab7c48a8837d2918e7b9b2ce4b12cc5211481b78b474713c641762d99a8aa0aa5fb258a62a393fb6320e78f168251db5e5b57aeb0d02ea9adcc80d00245fc80b9b1000f99ebffebcd30bdfd3fe403aa53a590a57f94ebb7d4511a1e9e0442465b695f028e6eba26cab08cce4bd653d01a2494db4487a3dcdc294f84dc59c341ce6f57c976863b57e1c376cd23fea5c09ae5ede3444c64404597f3f3700f12dc40fdbfcc89cb974adddc12545701f469326f207c133d0e620693202b01a031c8f102835953a465db484773104294f553ce22e39a560466b228950fe8594d689cadaff46a6b27f4e6e6b26313ac6365b1a27268a86e41bdb58d392abd83920819849a02631c279e96adda9889d39e3be786535a4ead7082aa2641d98820623ef3a77bcccebe6adc609d7d2eafda7efdb410b95844a8542b575d27ccda880f4c430f8c40f9ca179b09fff92163c85214f868e9a91fe9dcea6fdc336d507b73aaedbf3275509a8ddea008f3a04e5847b6ae6b006c7b4655231cc72021e81cb57cae946139c848cf14f3eac4f842b16b066d8e2354def71ae1498e3b6dac3375d7f1653a41de6187acfef508754220543135350ca52ba889eabb99aed267c66d7a7627acbe02a279711a81bbebaf5eae1c9133973f49e66f9c6835bea8ee90033c0dcb5a3dea5ba9d7c3a65c454448d6eda84c84ecd37120b0d8e18f3bf1491ae04dc9fb8a980d92486aaa7ff1795f166f6ce68079ac865491a2b63250b5eb7728acfc0968cf9f753ed242c871a6a4d98c43393c93d281ecf57fe38bfbe7df0bbf3f5cdd764dc7cffb471273a2c76a9810446a1c374f97bc526978649b85703a35e6a6e02429332ac0d99f08a41bbea26920312ce7a72d4ad4bcfb2682e1d1de0174d36aa0d34abd484d402463d7916b493b31c135971183a4214ae08f2ef4c8d5b16e2ff523225e1d5e2c263d71fa0b1e0e2b9739fbc53e46261cd3e6f7430c2baceab7dacfa6e80f3c78406efc56e47a642c4de674c8b5d7cd35161e98669cd281daea8be38ce44a3a790012e4e78f2da5c1c0ac58c0340db48d6e7a1e13255ead70dd2e4a27c4d3af4bb7a517290d679628884204515a079c8ae88ecb4681903a942059fb88880ddcb8d47415132e20251249bd05aa612adaabd5cc0832e680c9faef141a7ee4abcf6c5613159737bc49cd19fe629a5f1be80b35cf8c79d78d591a96cb121569ae5c61df116e892e4090bded4b1b15628c1f85e2831f5414c8d0aadd23e8950c7d3694885a57ace181a0bd4b93dd6742033b4b982c5cce4c66586c3f3b1f682a9ef939a64b197e260b6c5edfc670847719916f30c7127d7b410344119fa2c0af3f72066cae9a77af860fb5ebbce040579efc9174301ef5ced5da526b5f20689a7905da5c733fcb7f638ffe874cc977a9d16eabcfd6f82734be5bac2249c28ebcc59e54a2bad5c69e546c0080ed310755e92eaa5a74d9537f8921efbc44ac80fa2a7775c46ac323495bf245eec0a02e64b99e2e6f0acc6f7a2625bb6d81124e2f7c857d09d08c9f065f0d70d78e3aabf2c8fe93268e87915e7121768a0c5dae4a80b502002d86d462394dfb084145326330cf6ed07196d69bd5ce3dfbbfbc629e78ff792c4071317a6dd87cd79fab4f436a6ec909a98233ddb29e058b802a16b4c66d537bac5c8aad17412ae3d0c3f3a4f663dabb21d51b393a96c23e41cf7acb5d10044a7e8146bd97e9f43dba6bfb67fefc076d7826dbf2fc01672e0762c4708c5cb2088307d946e52d7cb124cf662a0cb003702adaf04c0345175b811ec78bcd83989788f4e1b4389789fe4dda5ee4d74da8d54fbde74852312ea0c790ec4ce2a98a419d4b735def0dc3f20e6c0177c1f676532c82487b1d5648541255404d0cab3c6bbb9483628e098e6e823a18406276b89152fddabe3ab79a3fb063e06277a710a31197a50d815da9ba850d8633da651e85bf2945d10f58d559a9eb2790fdfb65c841e04bd5afc866cfe55bce1dd1738d1d2526e04e7fd7e59e1a35302c3511cf560c8fbfd080e1da8b05137ebb0e1ee3afc97bf49fc18031abc78bb60536b59514be897565860f2b22db46d52daedf17e0476a962ca623042a2a79ef79b8f2815258014be8610c01b17c89c7a5111ec4bbde5e67255f7ff18b4ef6a23a5e2be22186c1256b5b985809773e3ed2de021e8354add2140e28e298e31c7b19feb0c3fe768aa8c33f9ff2c6895f2fc12c01713c7147e1ed5238b6f429e70f6dd475e5f2f26645005e23ca1bc15f2872c9f985331550a198863031a98da57103656a596bbec59350157ea33a60d0428a78d711bd6a807d49371cc728c1abfb8a6aae18c34d0b2b36fc0533f4c929d6d01572763904996ed490ff3f41c37a44fa6eb8993050330e71b5463223bd8b53788cf6f648d7d2db560290c1051e6450723fb978a0c213f0bd1af931561c271da4881e8397c02f79b1a75b67db28d6f6f20051bdda420fd7c4612b20a3cbf9778f96301c4a70981940a072d2f16feae28627b44a64981e84bd589c105d1406eacd04b583fc2d78b2162e5c2b6b32026ca2794d38464d52211b47f2101ec587dc58e470e0b8bcedc70e31f5e02e2ae12d31f99fbe766657e943ccabf125d627343edde9df62e9de6a5b5838121e3f9fcb21d66a893986b295dbb78e95dca72a153110bcbd2efca9f860203b717b06e1b2a2640dbd133a3c80d3b7f474f176898ceb2b2772e0dab1f460367f61d11795fa715b91d3f17e507b47031d7a432bc7861d1f45db58651491bd0fd197a30a51ffff9fa2ccf40e934aeb4dcfbef2fb76d4e74c7233840be7bb858202832b39cc70328959c10045cbb234e06a2c1281acac44e7a2f84c7f7fa30f4308bbd045612a72f1377d55dae2ba29ef36bc95c195437c1a2cc4313c5528f6790c9120f38157128e41cbf10406a72b85e0507b7e1228a3f2a80e6b85d29554fb1872046ed90deb2d0e170023928856dc52f3671e60d928cf63edf42886fd2d4bcb17359f59a07ce5048edf58f6fd55965d770220ce1fafa445a0c11ddc30c271540d3f8135d170bdba4ded555d8ad4b3534296f2b0a762da4bea83d60d89373d5c742bb07c4cfb76c0b823edb23ecf1871ed27300461a3d38e360ee80d2e338fca6ad682b06481e8e83888d758f9c4f37764f83d8ac023944ead7e0a5664559e8bab6451b120d42088dce7ada71cf4a1c3bed6fcb48193d39482b288abba24643bcec7442d5759e9b3094c52dd1a1390a166713d6a0a37b3c7350481b187e25d71cbfc1feafcda1eb22b5d8730dedcf9be91adb6ecb16144a25631a496c5c66ebcee41d00b4640003eb9b1d394f5f8ee79736e2929ce3791b1cc3df155e6dd80b2bc7f91ad13c1997874e15ac12488acc3f4b56d50582f0423087562b03c5e14cab16d17910cd795ed4c93e496aec1c8e42b259af81988b6fa1b51a5e488049e14d63290d30463343ae3cf120486af08f1c0f37fdceaa01ae14858b4a7b33a1b33c1a12588bd226edc4b4b9e1950f63c6c1b91ed74cdcf08498cac054231dfda990740021df3369b6b917e083f70bd4650c5f4debbf50c304c2c02920f1ff289abb20c24c23f22a7fd0d09b3913960cfa389173cb50b1f33181fd6abcfb34618c82a073353a14dae776fe127aa44a747296904b4dc0cbb7d2462e042552d0f9a7770e103a780df978a841f8cd6e4d18a91eba4e27d15e4e2bbb5fd1ce94b332fc4737a572399f759f2d2de70d92bbdbf391252b54d4f9d2a85d6aa832c5ac0f259bb6e013ba2f6d387fdefbe287834c341e91eaa162ea5470ea969e6aeacab5edc457b0bfcf526f0be7d998d3acb54876ee31fe93b885ca08e03fd1101a9f6283b664cd8b66f7ae81af8c330f168269a3a00a2c0835be103c2365db06607e1b80df50733be2f24002d1af85080efdf2891ce24b98475708d11c44a9ada84e8ec23e5b6540f76a277df5beb0078ff182e145082c0b48e70b745ad2651bbb70bc8e4a44da8bfd261341f8b1b9f0eff3f5bf8f4cecb0fe8637b6414d0fe7dede56801b0c153b3bc732fe6905419785c47e72f5c74dca782f222d82c987d84d46c3f83849577f2f1312d9f7c43231455d231806b26dfb51d8ba02fa1be8a6899e3eccb2f5296a1ce3657f20cd4173ab6d5c6642f29621cbff6f5f3ffef84151fb5da5bee1f5de807e861ef139f4b5d0c8eb07ada9136931878d4dbab9de4a8a783a8672f17e87d089e3d744b63154270cfa78a78fe1cfa704129f5fa478885f56fe8e6cd20e377c600fdf148f36798aa837e0bc17ac305960771d14666f739edab5822869e9f5131173b050a24d269c7bc12eef2af6cfe83165b10677ec60f740aa0e14e8edfa28e6412999eac68e15de1d1fff9b82492156030b289ba7836893213fde4d4ca4d6489c2133a8332edc8f8e483452c05ab127846b65fc5930b858c4f6fd0adc85e18fc675aeafc65d9b9ced088ef36a8602b422833128d4263be2cf94612d8b6809da3fc68f4611417b55ea332ec007171df88497c2d16f589402e30bb79ed859d31d9d83f6904bff7f2431e7120b7517bd94dc57fe1ccc3965094d4c8e8be8a2e5481b575cc5ec4f76122c0318b34f5ea50322def9c366a9a9956b9089d7326c330655b82edbad92b26b29f2fd87ab40b833cdcc711dfc62f9cd7bf9eb0738d4cbeeeec7ddd4b6015639ebc1f7d1c01f716642d54edbb40c3a5c5aa960a7dbe6a063966885fa62d49b28ea00681281ccdce548ebfcbf26e751f1b299a8dc3c21023195d54d3347bd3ce61a69da8605f89c6d161737efe6b5e527544cdf3827f3e62ebf33a7d7520031b79f3b8967ff40822c9467bda6d2bf3c06b0e5b42791c3b04db66ba7aa48f10424ed7cb763ab0cffab6af5e679af3183869c74b4e2f63d2ee48f26712972c2d4e1e4849dc4c75f7a3100f5a165f5e15697dc70a5a3ce1f9d3998e8ee3e110c87ef83a5fbc2a20f64abcebe87293ac719fe5b20ac2afcfa7dd558e9be395d0d972a074512b12391d7a856e1eab046b1542cf803f928923f0350a1d76efe4800c91d65eb9659b051104f7163bedae3d2157fade540eccb48e49522780ed91c88b4882f1e6d5a9e4e956859ef320223ea68a57c7bb565981bb56086007e70335f42c08ff6d1ce6a24850a91251ccf43e9b9617100fee0f149ba9b3bbc944eb403286996ff9774228e77b6f9fcec469e0a3704fee9f7077cbef9c3e93117f8cbd5d915b3c53ad34a76cc7decdc8fce33cb69fd1a3db3d11f9963ec58c302e7288290b5a3614a0be453e048ac50974dcbcf9dbae46a11a2d01ba9d9493d386007defb4108f7504d77601c4925a6daad1c5363942e014ef97b2e7dc4a6bde641f4abcd86c5c178ccae69e39aed0f09980653a7d598a67d87587c06110e005a754854af55ef79fdf2034e5efba7bcfff842e16cb8ad4f524331a30ed6ca4b7d121bc637b804f90307a5c9175644db5e0917add4308549dbd5e51a4b45a208df16be7b50dd54e58545da5ec79bb82174614fea40bba4e5433357ff87c143611e007b7ed55347838820b5364dfd7de952e6fd5bfb344420f8ecf3370f611f1b8ca86c9f1dfc5151f5c3b3de30d55ec60359d2292e926c6a7595f5dbc7300730539f47274c2ffe43306cf8ee6bb2b18693b1dc4095bda26f3360a7d247ec03d03693849c8e3f91a802bad6cc4a9a4620f93c0d3e1a4c744c6cccc1d7a9e955431c43649497d61f3d162d53379f79cee4cbe6bcdc2d61751f09612519cae44ba28c9d9c481806c777a2070465aa43dc7b50ad106e6dadf39cd189d95d647b22a8ca6d931d7cd4da0bf9fe28cc59e705ee3a7e3368e5df8e746a0540ea25c8e769f6dbe1d9b5ece225b7c796066500bf6ebc057520700e429d65a4bf05bd72cead9948242ae7131f21941a627a08c6ee5635749207777ec455eefce4c13a8e956b4509c4a88bb6d7190c73399899730719b5c97974cb960cb578e885a1e3d1e1099dd25a1565d2e3cbce28987d10a8a7d9125c41f0163c50d7bb3bc7eb4a9bec9cc4facbcc08e202dd50f820bd081f446fe007d35b7c60d01b1fa136a46e92cf79aefff226dc939b539589da8912f1a6b4481dfdccad4b23ee88ac7312ca70d9b3f2073aa1a579d8fe7573bc7fa2bef54c696c6fdf7e78d4519e4c70c0c24eb83497faf40dad191c6e70faa3e93ef1d4c0b9eb43ec8716fdaa9ffe9f2dd04c99625a72e36f9ac234b0b681f406702964653482d682f1f31fef22bf1fbfcc0abb32da30bc7887cd6e1080f2d610fdc3c070a05da2ff98797c02832842b37a31486af908247de748b1b685700e2429d718f46ca2b34d19be94d8182562464ef17dbbf66dc9a516a2ef0715879561d631f86bad67db5a330f5d3143db4864349b4e5e439e6a145353bfca7928f2feace6eb5e4f935d923728bcfb8b800028ef0487a83fbe1b680b9a91fbc49d1847f4b0bc6d1d810bd1501affba3eb024258d5b681c5257f7bcf598307101af3bb497916507da43ced9205c51360fdcfa0ec6f9c3d6907a49c3944942ed6b1403321c3f47189122d85e842c1bfea28ac0f786ea654cfca4badd50bc7521af9093e0bb0ce3af8d27af12451912a7e03f9ba0884a04a6f63c436d3e690703b53f8016f8b6c884824a342b9d4bd811eaa40f474e30e1c54b7ce52f91e860f07616aac89882820a69da2f20aa2f5a7e48b6ae869c82ad837cac55af7c9e5a685474730c7618be629379d8a54b4bf5d6354c2dde2d975fc749a699de5bcf32dfe13e06054df534d6608db699e91390cb95e39778d8f185a045c9a6ded78cbb8bebb23c9a1657a9b4865c78a40a224b6ca1b7e087d0ad7113b3ea9dcbf4e5c377949d01abf2c32bba86950ca4d43aa3e271cbfa86608830fed8589fd5f57021ecbd1e54da6438a2454ef04a642a9fd42f4fab6590527517b7dd68956e86bbe299359914199efaf4397ae386da63f6c14e4a36c81f054218576a384b5108b23cbe4cac271234053799dacbd91adcd74df345dce2345c3a9707059cba4dca3537d37ee0ea4cec7a8fe6a79446e06814c687a9c68818f439d39aaeaebcd3e2cfe75774397f32430b66bd3877911495c0b90ec1f01cb2451211e8fac316c1084aca550cac2d1cf43f788f935a21c0a17b13f88290c543e6931996c1e25e5f1cfd818d68f1d718a14012873a7568e4a9c3ace3871dc91a530c3f8b3c08a115bbc421eedf719a144536195e12bdd800777f408e6e9aaf1d3b1b0bb87ffc3c94e084a0819bd9d3abf590030552b44ce0fafb924615dfb78247e8c7fee88ab90e7e2fb0f2f3d0369979b5e7fa727d6eb82cbceccbe6c2cfd363a489149a724e02405120bf626e544ca0fc97abcdba3054a11e20cee642794a24f5032853cbea21072372b64424a011e0df224280a169000737dc72ca147dfb1449e13e8e677e24480e70a695a9813dcdc5b1e3b94335331815de9e233d6bb925f0d7d308a3643d99d31fb370823cab57677ea5ad136b1b45c389b3894ae5f5d2f3b31d2ebb1e1d08906819b867c13fbe7975824bee474b0ed6c4081ae26e36685abb04e98ba00838ccb34b2f802a02014695e898ded7a225c4104411f9f92120a301c48795bcf49412e8bdce70dbf5198df4dc5113a9d52dc7ba1289e4b06191ae36940f6b13dfc92f6f18f669e92e0c3d5136c18859daa4a2557298b148187e29d8705f06809d260a3e5f676e2316ab31df76b55d641610ff61c97b823ed61e45fb25b89ad18d96d9168c1be55674a901cd7174e0498f1a1959721c12bf32c59a6d1b0b7acc96e006f714aaf451669379fb9637e82057cb1e8ba710c43ed117efddc17d4b67ecbec195561f8d195d0b51a654142b29848324853cc777a8fc1a561382e3e232004670bb3cd56762ae8341344618d4d47208f9ae5cc52718b08f889da9a97e06a80f700af8dcf73618428cb7379e7c5b5052b59918b751a4f32224c61a66047a9d609ef5cf7252744b2127b7b0dcd7f4d8d13419201b06df235b024967dc87a922b4aac44fd1ef94ca98805203aced89ba43710323d67c2ffd03dc483c224fe43dbadfcce5aea24ad2bcede482b31e9ab7a13b324162d56635f5aebac9329954e1d1636841199d58b8b0842b667111739611d939f35196b8329c0015252758e78be56e19c3f1d6e7b73d4973a07edee1c8f31d9f1d20c23403b2a69d1afbc501a68c5d04a537f5b6f7f7d8170edf8dfa4ea72f16891514b5d8cbb41345bf16564761d5c89ee29274db5a9a08350f6a2952bd1a9f8631b85a59ce5d056e32625c6c658d19dbc973c83bf165489269f42f7c502bd3c52ebad09cd44b8ab5e1762a868c442724e615c2e6aabeaa3e46c1ab7e6104a9f8312b55e914184f51eface583add07f9da6228d3b514c31ddbaaca1e2c5399ea3000df3342ae2ce01dfebf17217421e038494c4b3f8a52cb91b36c77299fa336043a349a2a5f5d4896a244afe4726e94f2f1cd02e89a8cc400eca22979c08e8fbee7aa0ead455ad470eec33fc85211e56fca0e18f50dbb16823a543c0268c73bdc87d7a13ad4465e7599845368875b45e64e77f9ac29910108941c4df04224bee7e0af26808da05b6f30a0d558ff366302d5b9441918617386790e390d43dd8972b5025c6c003aea8906482493b7eb6987e1c89e66b4cf8d66a6432f225636563d47896aa06320b538703bd7233d2d1529cbe7c7ee2dd4423412b1455299adba9e05be87f440f5e14012d7e44c0a70f243ea516758995fb4641dad229c4b2df55f4ed2569830fb84e55ddc040e7687690240734c5bc05981b9d3ac122aa490e35b08dcf2cb5ca0f9c3affeeca04cbdbedc41302e1f76e456709531f609d1747f497efa36735819d20bb73efffac7e9eff52b0b59e4d3378f92827dfd5044f3097f927bcc27172a13e535873bc7ee971f36e2991b3e5285ca6d09c9b166b6b2383a7107018ab6b541699593207c7925a0c5050de446612391dac6b634082cad3711cadd66844153421af90ca6ec7a352020316d835c08861f77717622817ab926a6137490c2df60c003e46831b23ed3539f988fd966b029475e30ccfdedaa30e92e76fdaf4b1ae25e58bc6623d7b0929aaeac3d7481d9575a46aeda762796905a64186caeb0bd257d996e9fe03c602b168926d04f5b3968ef45f001c25311619ac1114e23fd8b1f382abdc0b49754ab15076aff23f4159bd0fe8d8546f24b812f542c1fab63a0b775563536c8fab6282a5450a59ba64521d033718dbbd107e388c2a55fd0e4711900eb7ffb18f10f7f17ec0bc10d484c1f998edbfa3218494f575857949868393238dd66bb50341c84409381a5c5858a3090a9a5e3b58533aaaedaec495607d3fc465dbb027d826ee187dc2d706a0e058902d099e6bc132daee3c5c5e197d20835cc6942e849f9fd7a20cb3c51f707a9caf9af5c11c19bb3a83385cc47d78e1191d231f8ab3d1d9e783c7a17147e14f2e8cafd98a4cf8f1f9ffe7b1de3e8fd2385fe6e16743bd53565ba155d8b4f40320b9581ece0b38edf24bdebc6ba75c1fcf395c07a846cdd367357e89fe81b277ba6c89bf63ea5ca94e4784b72e43615990f0c0d770aad51c735ee622927c9ac3672eb2a61cb3aceea79f601296b9ee528c6354265d92cf2e03311e97acfc850799408287c1e6ea83720320c2a0e42edff504fcd6828da11b30e5fcaa543794f068cbf16518f78073053f5056c69896d91d70b7f471f180938f2178d0d091153276065fb61fe168fec0a4768f2e52ec14dc5c6281026d6bca4897d4a04f074a38b2710160171b4d2dd41e0245ef7da941f11e1c2d79d6ca006d018b395bfc52b92cb4473910a29ce103749da71e5f1feb322497717a0000ecda2e68e412d0ee340f70aab402bc7fabf24c05ecfed15496fd39a6b658b6f098bce75a34662085a725f364e6fc72b38368ca87693e2ee25a06cab2f13c10633ad73b4610a0edb78bab3efc06f38b29d3540aa3f72bc90efa1e3bf52e0bcd928af40844e9cc78b7df1e7d01f822e608089c4007f1578090ec3b3c1e9b25b89c2b07d48d6c7c7fdb0dc4b3899a32c7456fd13a8082b44dd67413911bf6bc98e8502520b8b90edadeca68de6641375b4cb4535fa9283490fc18de9d4df6e03e9b66b13103cc9ba7d5f3536e4e8814b5d70c2f01c7fb69161128cd4aff928ec7a4ab49e6bab1c7fe7927dc7adda2a08f0d1c37391d73346781c2aefa346499ea2e5d2ede2108fc44a03a8c38166dd0c4a95162db39cd9b834c7d804c4d3ab9eb3635502deab599d5bfb95d72b59c90614a5b0d74eaf9cdfd00982b5aeaeba9ccee837140dac541f5d0f80381e1a6a9730c4f8f7e8b592c539f96e73b7516b998db9b6940b65d5a0f0221eda939af58f93e7d77a24b06c49ff53589f8342ac16f6fc94ff29dfb4a585afee9ea933122e8981223bf3efd4af410088ffca42ee334992d706f527a8d8f9989af067e5a9a2cb1dcf0bea18232260654431dd51726d2483c29add94c217d05bb8f8df072f3290689d49212f3cea2fbc337c2c0a9812ee72cfb1d7acb55f9c35d61e5443e6aff8c447252f4aa7a1c1a61c06ecdca76b8edf64881827726e0601e59b5fb844b99f7ada63a13627730e93ec90310a41ee5c744b0c1870a1844a91155f0f08c4e160f9e8ba0a0decef6d6ced168987d707bfbb50e03073924d44e4238f11b1b9002bcf11b746dce92af119f877e66bce32ede73a19bab999413994040d76bc561c893cec7801149659b8ef301d6256931b573ebcb4b5ce0df690b72f8c242a9586967dfecfd553b34ac214c3dae889f4ff465a916d8849cb108a4dc1bb46d2e4753dc85642762e9d4283287ba83db9657a2e62004f4b0d620a89cb1a8784b54e34aad099ebc1ad7da627aba9597158639a5bfc526e68674a278d7b0587598890192478083dc5015f28582c8c423d6b7df57e2c03e92f99a19231ca14db9243bf11a8bab0a0e52b3bd1d4669e67461f34c00dfd0c48617bd01416c725384c3ab741c7f9c3b6da7fd521d4298563ef8353d0496a901838de4541b4559cbd589b083a13a5fb626ea4c8c042f89535ce839ab143ec88a50e3db04b581c7278f551a8a0c88f2dcc588482abcca00ab84d498b8fe29b401bbbbfa42a4cc226c1723dd88090ae5b22ba8917b5a060f2238fb5182ee2d41fd51783b1ce5e08f16626f5150a104e8d63eb005c41916c3b78ff58b8c4eba0dbf7ccec9aa6d2ccc3187a3a3c3e57243b0440e80824362fcfce297ae40bf0dffc84b19e9a44a14ae41cb024b9744ab18ede9e9e2cac6836fdb89bfc895a99275fac83c1baa87f0efc48166f755a68405144199f467d9e57b559937a996ae5a5f18f8295ee7db7a98a459850aeed35e90a245a0c6575de32d8eb0a2a7de98741e77e219d80119beefd82086712390ec8a2a37e78aa88704aa86a250c2f91d85cfafa9debbb80f82eb54509b6bbbe00e6225374e4a47fb5554c833ca88ba7eec656e9a5a403f7c0843687d500d2e66659f5ac6e26a922e047d0d0c9e25d47b4e48879a0caef173b279536eeea391eaacc1c095d2bce4f5e1a700839dd29cff2c1a158d13d28883594972f14f4bc943dbf18c1486a472445615dbf93f665fd7422e5b5382f6b9d148d0608df5f9209313c53139c979a50c6ad89841cf08101b2c6a84b18fab12f08fbfc2ca794c2c51817d67f36a01b56cb36ad8911d2fe033cc1359926ab888be8b04e6456ecd63805422364c752783210f6ea0dbfbf1607c35e9d40945be8d54d4d7fc90e7571e20cc7f8bda3b6547dd170a3e917606cea48db513251ec0129194d1342637a7ba2387ed867bfe195e002436a2f8f93fd2e79ed9eec513d79d2a185daa39101982a301e09c7c1c225ac2a62d9a3fb42db993f9a3f774e321c6843008dd08f367d3ee56f0543be85e04438800b0a64513803b6ab4a312663f544386e738e6f443e3fc04d0a4f81269f1ce79121e9f9466cad5c5eb475f26834f72cf718638528c3e086107f32ae58b8e29b3c9b8583796e9c240c0faf9435b770d89b007d2397b2746a1d9851f1b9c9b59efeb06afb1620d64dd67687b5745721b673c94adad2a4404c602bddbb2d23658d969318f449527b0f128cac9931845bfc300420011f792e93535fd3ea2126caf2c6b864cbb6bab4d30214e246464078127706dea43bd1db23d6563ced164200c029ecdd9e3330ef7d76092a4c2806c348170dcfdc08625bcb20cdcc4ad32ad2d289bf6af7d119037f605b5afbf130a6092e072dce1beb864023a9f79c5a3a5f98dab3a65515609b404d5ced7bc1375aa94ca605f68846ac4ac88bb194d6f6da7aef92c63733af3c43c30810409578e3d8e22d31d0b06283d6924c9bb4901042a939610c44bd3feb72862b816866ad20a54de52c01758813ca8b6ac36be0c97708225c799a837cb3cccc967080bfaed5163cf3d8382d0a7c5f482bfbcdf0a4ea2513670bc39a5e00fdf19370dc6542f5c3226d04b4d2c0c8a65bae7c481bc49ac926aed78aac0af2cb110aece11b8f2b6fb6cad716f50441029f1ffddefdce253c4708d22604a82b2f5016a63271cd02e506c7ee1c80cb4ced4e47f6383fa6eee82aa0472af9552e176672d5592362440923fda58a8509a950cad878e023c075f585dc3552eaa4af92367ec4659bf5cfd8e6afd04c17df69629aaf19d1855479b5f1d7c722602babecbaa9186e8895c4115b8238bfab23413046e63be7970c971d902fb5ca2ff4dec4a236343bea3abf1845c1b469d73a1582d164635cfcf2a04ec1e215e89b940fca1cbafddc50fca43805600a8e086e85195800ed609459d79690f4a0514c53c0f32c1c1b08222a1950c04972abeed430e0f17eeff06544212756bf8d66c5dac489283fc60846329da5412f28a0a83d30ce860d16f201887c0af84de974d009e0018964aff08d4a9fb14785d023bc804899568df516bfa449263b931b92fb3cd3a4f2708fdd851783d1362ca45ff38a0cded50a959292da82accb2164b0ba6e0d6ae606d2109b53f666359995afa62923bb4c3254b0bef114505603218e9a56528fef28461b3483249d1e9be49f2445b7a3b878eb4cfb1036ffc01f57ad9908c34da333896b780d4f748046462c192b269501842175b0e67d9d29481ba563f82c6b56f3421c637bce9bb65f7017c4cbba2ee7d9616657655095c1319e295d1e00c640eb26f2f431e1ae4587a4343a79b870d4528c8e0f69595a624599e938458ba643a20681980ab6970b017ac0d7efb1b5e83f1b1d1b063926abd58deca36d3565288acc2d4088b8e13896da1e46350c39c5e761abfc4703271e668c1059894bdff08f0614aa4772d9f72e40ca6e04fc34193503ef1c082e75c79095e9839c18185ff4a1551f946cd70456766db1d0a4f39e59c52f62cdbce32ed8a8d3dcc41fbdf155471ab636e474c6eed245703acd50824f44e0d6295915c1412371d0dd2536287265c93a71b58b937d47b6a8671bc106c95d7aab570c244ccba305e40c4d55127dbd9c5a9ba9447fb46c313266191a701c076b04f76ec98206554d22c4669aa6c85c0c762d20bbe3f6a9f1ca611594438f7895240610c58f75aad70cd75470f9a007a30d8b5a66b6cd5bcf836809b187d1ae86248f569bc749900106cc2baf43949fc306c496b9a6a34ec5baddea773dcb7be6e2f04e0e8839f302abd884c807aa696edf973eeaf905e67d6d1ec80e4026696ed140ae4713a22c64b59ea37cbbfe1b35c3ca0e35f07a2990236b360f30ad7e84b23b55ba60505cfdf008b59d44305570b0a6c4b61ed2fb085c6be5482d0a77ff94280fec16f228d04adeeffec810cebc7be2a3943f40c67fd14b0b35c4a3764e1b639249720dd7827cdb22a04e235c9299802b7cfea5b7e9b07a554046400639ac884ba9f7ca5c0315daeae4ad29a4a780f5bd9929780f771215b229624002975ab68241f120a3611a03619244c0cf5d911a6e70449cb23930c93c99c03bb04c229418c1d2547720e3b3f5f11a0e84f0f51fba5af4768708271f0d432dcd8c1f808bb517fca85b7eb18b4ed187e5fe7dfb29277cdf1e9b9278c766e79590745e3c9648a4a94b9f5111b59ff4aad2266bd4dcbb7515195fab0ba485c4b62bd46873652e4fe998bc4fe4ac1a50d4b0eed44010b2540927b78d2ef5310655f3234c947811482fd9c0c6cf8d57d82494189b66697dc8798404ea234f787ffd54f04af4e555e138802ff0328b4c181612b4d80208dbfe6fd60659c42b0cf8af21dfba0e02b4ae205ceb8d55ba19c43d801e50abce08d4456940b0b515ad01552167dd7149183d6d7fcc918d5c22d5a993660a5d87f30da0149d4d0b147d6e525d843a242acd3368170c7568ac5c4dc2c83b5623bc4197daa2e0a13c07552705d4dc3717ea70adad8004b6c51d8766d22d61599fb693ace46d0d7edaf0a79f426834864b8fc06c7faf4e63cff3dfc316a740bde3001f18212b212881d40245ce78e0ec4fbc0ff945338c6d62800ae1449d7404da370037535090fb2dc3507c93e817a9f9b5dc5ba646915052059b0130c385531acd4a53d6ef234cad7165e4983c14920a6b1dd439844c3e49a52035ee80c3f81caf89952fc057db6bc1bff7c84b99672ba5292ae0930bb2ffacb8c8c6eaacceb0de43af7b79575dc5e36931cf83f68165942fb9de822b40f64fb78069f6d9c4665b39d6c5a356dea6c7f23c5f638119327621b2f5921f5a38475344a68af2ac4c0f4c06f1dac3127766da05c31da5ccbcf18b6cf3ecd417178c808cce370407c25219795f8fd540c9cc89977073c9fc4d8638d7f1b18a056b42ba00da1a4ec79674148924d98e4d9dfd73e5260f663507af88dbd72c9b713ed17a919c655c4fca9b71693899e4d54dd0d7fda5dee217f563d1dcec94593801d3a5284a4968d5cda17684dd1ef507c10caf1ca437aceb7d0af69e89afc4bab4a19d5a3cb79d2061cea20e49495ff6ff4f5b6da38298de4dbcf9188b581a927935ae7da7395a63d717c8ac5eb18f1f5915e6dcfb781c4092d23fa99817d889bedeb5190597d71c448f9b70346d64334d17cae44d9fd1cd6abd6a1d1d331ac63e837550d40ae851ee34b314dca48f9de4d3d458cd0c7b422494006ca41798f11c6fefca4879067a76c39bcf38af2791c92ea1e810d041f517ea35690b4c32cccfa2ab67cad629c718014d15d0e79764f1e06fbbee7388d92db5c918955692f2e4e6ce4802bbc02cbc6016b35dabd0b3d7c5e6c50973edb775a83f9f51cc05e6674d572f0bdab7dc19438ace4fcda2c8787ed7cc0094b21c5127c6ed177f6364cdd5e92076b7d9b7f9a4859ac965fd2e454bdb77ab009e205a2b8cf0ec12c3cc6800e2745c3cd5d021e34bfb6cced466231f4446b3b0843de787a01c1c04d7b7380246d95a9d82bd2cae6d967b9edd23a9f8e6a2a61f96d5e277650b2057ad66aa25138cc5e91266e3be80733d781805527e225d91b044f2b3a1cecfabb3ae8e095f8b1eafb2ad148b8c6d410701f6c4dffecb8f431a49e70f337d1dd584b9a6095069ea7d87e14402c5483ccb79c815860349a6fc9eeb639cfd52fe2fcd5808d066a66c6300322d74e199fa8c014c78b5424abc5949dcb81c7e5cae3d6dd813a45c4582bd5bfb8450b03ae76ee5ea2ff5a1a3db944a2d5c638414ed72eec1656b46e2167f77dc9761e4243021bc45f7990b953ad35eda2538395ed7870f350cef1ba503aba18f172dde2d46de2343e22de42d3ea1c2ce175a73bd67f36c108ad619e7fb06bd27262e320e95d21774dc1b8cb65355f3db34c36bbd2f596b1e4334be8265e6d27e6667a8e163336ffaacfec3473d3bbedfd5b8b964210594fd36304cd97cb09daf43e20eaeebe79f08829e0700e685a3f98078c98e302a0f5fdb7d8d3a1ab30521ad8f42dfd27c7f9974f55f2978efe8f1124b194eda34f5fc79a825b8868e3796bf411bfbea58b0afdb042941457f3f321dc53128a36b89dbb2dd85bb9f96faa21961a3448033f29b2ee181443999cc30fea01d5e24ccfd0c5463eb8f51e9a1a8983a30093ba61393e1e8aa5287c92009f1a7058b09acdddee731e7a6c4861afc55c7091f2aa5c8ef698fa39eb79e0af6ba2eabbc5df753a0b36cbdfe42d955a3143cd93271cb8f012652c43e54775a31cc3e3c9b60fe8e495c395937bdfb8422a340a3071b8245a12c4f2480edc87ea8fc93101fed600f825a4314fa0482adf0eacdb07a029c1f2f07de4c4fd3a5c0107a6631078867a3a812c272cd2b97addd7c3b4c0c7c19e13396721783c4f9272ea714669dee5c51d246dee723fecfff678deb7c37756c4538a2b1945250e67c6c117e75d0a619163be94891e7f13ea29b7fad1edb8988bb19ca2af144ac13acdabff82d07dd1a6d72f527d0c1c988f3ed1bf0fa1cdde0bdec42b838035e659a8b2c53e1231ceb843235bfb1d7eb52dac6a6af0dd4feda255bdfbe90a80d5b24ab259f358e4f1fdd42ce99778784529ce1088e9ddb178ac67345e4c80bcce2120715a1ab4e17c0790abaa4b395439b5153ec5ad3fa4018bca598f242c5200df5c3d4ca72f72f4236e70b797355843533b291a40c63ad423ed5c16838c586142cefeaf2f4073fd06fd1198db10d4e327a9454a04fdb0afbd25dbde2d15179920e06664e6986a9551e6d0e750231c91f81065aa5cc56f313373c110c7f022e05a39b2b021b6f59d28733088dc715c31a410483dc68f022e9286016b05ca4a306182b829f5fa3140aaadf5160359b14eae2daaf5d9e0e55d52a97313d46c0ab641de352f092cbacdd880d85e0ee3be096076fa84c23cf3280fe1d4406da0bf9f233e1ea908944b8bd1964c5f8f0f765ac4c0fe9a9e0dbbbe42a6f8fb8b4b9e423fbf56393256669012a4dc005c89325a5ee52f14f98402e22b5bb2ff04692007853f3f3a376c7cf9c23c987b5e443c2d488a05c8295d0f0f84c6ca7360d3f22f3632f0c30377167e8d75b10c515fc21ad07824e0ef2b53971f73edaf1653c5ccca861cc44cae8354b3e92b4d99bbf7e590ab7a982dbfe883b872b2bf4c72841209f0ca3895360cdc5e7e484738d7e4b6886e227c1e2e2f1526d583117d7c654a75688b0c0f1310b6cf6f2c7dd49dc3a84d0010f2b83839852265f1fff3c307404f18b5926493b6de12288b1e703cc5f7f5ea06ef1dd2011202c80ffcea302c6758de23336d03c5d25f3df577790a4de49efd09472ba626a60573fd3320afc9893dbb35cbeb08014003cc61bdff6bb1d5c46590164c2c7a0b82f1707b8f42789a6e9b96640fff53f71c62840ddc5eb34aa4d3f3ffd8b9cc782a993dc2fff8b636b75ee60087bfec016c72cba50c76d30f9da80d8638f029d4db221e00d40a908fb7ca99f32c671387cd3448972d84a69459dd1a17f1b40e5512b04e46cc28b5c8156ad54c13720b0faeb614a97e08bb4a78039ea027bfccff6db0637cdd6e0ec832423ca170ad3deb57ffe3343c34f069a5349fd8eb8de975a56587044472f49b370550fff00b16e9fe39ce7e0afb4b8c0e43e234a4db043c88d04284e5228cca274ff92236ffe0f68ee67be398a65dc407da34fd518f807840732e79b881a06fda39874e17964b0f4742c2714e3e70d92055827725bbc7942d9dc4828d3227c7735267230ed24fbad8e9883907a1e336963125a2366390709741dc89e9448fdd2145be4e6d34a4d1d91470dac20b4eb76b970ca456f8572fc4633fc2598247b792687b1c7b331a3b4461f0012e4e9c9ee9b04dc587dc8371141a655998648ff89e91ea9264bba62c8e825489d9c7b6913443b8b2141bea76f451f489d05cd8241023b948b112a703a173fd65c849ff96ad41837bd93a18a59dd35fee4e6f87a9e5aaf775041f5da2d8112a138c0cb8594454a45ca083886b4e310d0137d663668d80b30a9ecaadb345d561049d459bddf0ae98bcd165f3d1ec3fda3e856e095d7d0800feb329b39f95c9fe291876825792a3203f4cdc3addca09a724f687018a24e104c3dce0bf414d27c0bb2d14bfc1f5860f3bb1d1b84869e0c8949b867e390207d96b9506fdbd486deefac20a0b1fc459983bc0e0b98aee13da113a36ad2fdc654a2f9fd8128409c00ca213bd405f60f97c85f4332738288791db25ca328b4e7b3adb7c0fef638f5cb4b1b31e94e47b9ab1e2e9a13753e1ac41f849edc5777d3e79e911225d79a952d93ac11bc52b67451cd6b87cd75c58e5ec3d2c1790d1637276914bda8b547057b6f09f9056cff04dfd118043bae7b50b13af90a8cf86475d6391ca5b22b0a23d769a35ade080c36174db8df670d0b7555619623dba5f03abe959f440ef0fe46f5c3b243c71d9f7e8a50aea522d08a3a89548150983254d893e3c26c8258f62008eac8e3187c0db5942ae141e62412aec3771cd63e1fa224b42a59847c6e7484ed31b26a1fd8381cc0ac1b86e6873ad8ddff7a50b08836baee156592c2e418a7c6b3294d246683b645f046163430e170d4d989fae7861782e9e85a4a8d677f8bbe23ffc486cbe8efe00a4832bda64d452a3000311df6aaa591268dd750376ae9ae734810613de6931414f2c3d202c82bec35f761dbf0588b62177647db07a0e9f9643862cc51e6403f02914657a27f457d717f19afa00bf6084b6b34731f5b9964ea7cea07d5fc5a85c12a00210272f19b38803a94e9a412f7de54621888be887f213fc7d9c336a07c5cb0bdc4dd73508109047147332fdf6ac19d17d0afb263dea52bf6a651a5a55ca912ffb69c71e36c05dc2484dc2aac4d0f848e18ee5718edeb123600c02b8df0614883dd215e67070e83b980571522882f83a1e9ce8e828ab2bfe34f42558e6a9817745974927ae5333d244821f67d9df9c95495ea16745e861e5f70b72027b74e25f0d125871b44f3463eb9a068bcef7cc2e5516bb9f3328e897121b563e302d606248b579f57b2deaa605316584c1ee99e24c5a257a590124cbde59c00253c501317b40c2afce813c59075b831ad1e47fd01ce1b9ea012264450f8d083cb43541c6b772bb254f0ea63ece3168b9a27ae823339c97bf53bcc40755ef1ad035c9046fc2f43d0730498fc4f82c511b831d1f6f671ffbd0f60aed8d1eda8b0a6c318dd5006684b14fe4a9b0d4289fb208146c66bcfa93837cbdc17268465e11b3bc4ec000eba5b06edffda7bb5b530b8f076b6de88621c368be445efc6934f7da18263e4945fff49415072fdc2432368dc42cd6fde6a9dbc6999eb59bea5f2bc2c1a311322b57dbbc7d66815760a6245b0a8801501fc46d4c86204072ea71310dc2170ece85a1a4e93fdb5f3279ba9ef0d8b214e89733782b3073cd170ba670e47d832f8d44d8cc452cbaaeda04e4bc0c8a5243f98713b02cac0047c88d02d7be6a9e661c47367b2d984668ed2631e89d848a37ff8f8b7466fca64fc614be9c9da3cbc16fce7138d531ba6d1cbc5f82cc7fb4988c4693f6fad48d115a621fa5a1386c3a158c28c25a8ef9dc7afe78faa7a3563650455c591a0d943da8b1854a3284fb24f48f8520da6e6ce0a50ee7b2deced0701ec88adfcbced1801c4f10b605819f80522e20b15084141fbfca155e9687368b7bac253f77f83bbea5c3a0a03ac22aca32e7dfaa5bc33b3ddd928ab92af639675012a1f14c7ac8e4370cde2f184e1ede5465f7e7e567f3ba9313ea13e3b371c0ab55900b58463fa2b623f2c2080b8bd6f0f040552f2a3177953cf405d8d64f1cd6f260cd10bbb1601cf91239eef86da322d064eaba184c4ff115858062197f40b1b91bddc8db374aadd3248036e6107e12d416d5b45e4e0a87f5e7366446c84d16451d93f985e9dc5dbc64394b513bf93b560abd74eb79bac566cdfd7fb0fefe8da81f9fcb266991ede82bcc349e8bf7302175f6abc5a2416043252e160e04c232deba30f24f5b9cec79f61801c199db899468416c198e9adcb709034392d70076c5eaf585cfc1f102ca014081a18ae0a5e32bc6acca708a6c160c57acc3aa20169e2bf91ea91c10aa8a2a58b2d498c69fb5ee647cba92832fa4473bdb495e17f6bf2cdc42a82b2e2bc0f7aa8884b16f147021f23b06554a56a21f9b0d9f235bffb1a1a9edb2e3b012cc3777409541acf5b275561741ece824ac7d026f0c39ff21e284ecaadafa04c34bc72ec88c8082cc287295d9c217ef957de0e1bd90401db8dc3b7361777cd144deb06d680236367a2b055381d645d6330af6005ce078240fe72b7ca0c303286c50f68ac2a419106b6d863a9b97f5cd88fc70aad3b8cb8cad39af5f736e88bd98415ebd8574e562f1c614eacf50215e1c82a930f3e2e82df5a21da4bd7120bd320b8f2eb6cc6d542e995e34d900988f5a94954c33bbca43fb6ade09cc973c836d9230ee66a2c0b4831ad630f49691ee0a1cb1465ec1ae046407f225fa016553e46bfdc4ccf459105440ee49e7da5db84014e2ca337069a8998de3b3cc25aba6db94dac1530fd577f6846e09e4e1f5c9914d6b0c78bf7ff28e9881437cc0e00ac98277405a7e8066963a29156f6a41d2d6a9630c903da922c9d720ff8fba2adf52e117f595cc200ae9ef0e7b51966af7e78c5ca656c549b6d617b8ed4aa9127f7ae7d80630f5536ac75ad2b56300dc9e1bf14a71d6b7850c312c702bfae60d0a6d50f76e07a9e3f6347506f636b13d271468b01ac1605de2217f5f0beb2f2e85c26ab56e6a91a108275b0bf844c958b0af0222ae7ecac2b96ea4f56f9ae0aee3b7845a7dd181ee81575fc1f440eebe579d10078131b2379103566478b904b5f28680a5fc9f3684f84bd7eb99967a9f2278595f330bbc41d10f7088c540d0d19a93b65aabbee34b26f2d6002aa1dd673837adbca65866675b2a125a352fb68384298c38e825f986f052db5bfbd1216aa11855509527d20411442b376f6424aa518d081a9371b6cd032718b22a6f0cc6e9667db39525e5a671575a706e6e5e644bf8b3b158ce06bdd9593f11a0f1d641109c25c9c0ab03b9349633aa57318914c4702a53223eef1923db68cb1c5020d905c01d0dcd38433006ad5255116a3dc5aa7e084d8ab10d1f270d1c786724d6c8760784d109314249181fdeef836d59c79ae748aeb2fadf41ae298d92d0ddba53f1f1f1ff30ea125361a23578ad4b937caa11a384fafa1c4978c19b71ff2f3b609810971bc6be60c0fb31acc3ef21cde2b85fd21aff4958e738b981883b7f30dc7ce85aff73e4ba80d4759108954456b8601d3c93c4010b24ece63159ea22bb69f02b60db70af792a2fa220b1035cc745c6018a5e967edc1bcf2e9a2ead4ce3077810b6f5c2847b43b85511e3ef8dc4dcd31a6158fa8ab3ec035d8bd4f134eeab0bbe1fd52c5c141e06577af66599783ab038af045885f2aa6da28f48408f667f8ea5b59587b5c475a1b1e55262b217157993e50549d4927766119e674a733cbe8caba446bda7cef55a0364a41e1aaa69f965c139b829dc7427e62d36873d7e19081ee1ca699422c92bdf8288e340b8ca33a9a35c58a92263d3b5070c62ba0b6313ba837040f4639a22d34982149ed38fa1204117cc7af4c959e0650dbb161f97ec704463b16a1589a08c096da40464431affdb0d6c0ab84e554db7aea6cbc316ac951f79a8c9459daadaff17f543c9c4859d36334b7518c52e9e06dee6723a41f96840d2386d329eeb316709c1d95caddf882b8cfebf27ee199854b124e1adfcd81b3c1e5f4ccbfe23430cbf72b49bb05c7879ed1aeb2acb1bc7325b900938318c7104d8dd2057eb604426907a637ed7e19c6d7336d83d112b83f3aa5fc6ff0984ad06cc6b576f6ec62c38f37f7b5080ece2c62d3025205eec7ef77c2f5c618669f1e5bf20d792365e6c44d3983f83128179a89c7cce1f7f9add93c736c36eefabbd2f501d1d423199663227edc8b90efa5256ad8c8206dae862489644544aae2b5342f9a2f4b325477667b2fef471db6c1144b15fb04e1f41a87a43f14ba69515b4cab35d5ba326fe8ee6917f867b9bd4e50a8f2230b06c8bba75dfac5785815c846c533e0983e997aac1dc83ff73577f4740df9ca1fcc3bed13b611da3ed055b25b12780f32cbe708a56a79952bc6e3d23c99312246b90d38d3e3c6c2e3c55ff2f04ea1303ae45b3617988af9ecfa86c6808f1b6bdba20c4cfda71e6430c58fb6c0e9c82cc0d0a9a2e63bf8e93eece12a943eb702e748081e9b4d2e37fb920ccbfa6dde536d9f6dee7f7abf2c6c13b0effcd1dd4990a52cc3cc6687ca703675dd1cc074b308e7fa60d57076e56383d5e5f02e1bfe29592fa6ae0e8c90ffbd94e8757f89cbdb196d1502cc598368cba4989beb574a8ac3022354c30a9fcd6a560c9e53c4975fd7757d74334459df724a84d6706975a56094de3e9d39090389951b903fc16fe19e6f8c80716f7b9137f6911e40b5540f57c27a47c68f40475000231e8c613fbde599d0a08d9276c2f58fcb1df2f111428af03f2e9d7d1c942c4fa58c67eae69bd5e51400b097d544b6be51e8c28c66048315e3d5cdf79aebebdca54118d5f32f5e024144027a63c6f162cf51527d130c5a068c8a213318b7f40776de479df22dba62d764b5b57b88c7b864a62d088466307de16f7e2d36751ebc1e2c4d046d2d1b6fb46f14c592589eb6fc5ed105b73692674121b979988efaa673f93b32dfb4641ad085da16e1e3a2cd9a9abf9678d3c6db4ac74c51132a0bb1d858bd9f247c016bf870a2dcb824231408875fda4ef8fa2c52114c7ee586e213834540e622014d6acb682ffcc2cccbfd13571f1266051578669dbd9622319359d66d27f14042feec254952d3549d8b027bad35780315b0657360aa18ef18c10917a0a9937d276cee2e8a4495b1c2d7c6c3ac8a06a69f31324b026b0295fdb3474d5b3c1b606eb62d851a1e2d84fc3576f99a300016aa356b11baba1e0dfec87eb544743ba15c92ed5c3565706ca8b521e5ba8631070dc1d1d63b5307fd2e871acc1268f0da20719202ff0f99f7f0f53aff74b773676415e9b1455e749d291e126bd032798675fd4725a2cb877d0e83d9840c74cc6e93d4c0011ca01a4100e396ad83ad8ba3bf80d3e3a93b4048edd829678d3bf9060af91c49c06c5c92d5d4827b310cc21d75817f02cd8bf4515155f90812d30ee24f69743314677574515b5c0463fd3a49aadec3d61be3067e41755a76cfa96e44d4dce5990ec80cb998ace89e5c8c728af14cbbcad72ef6559e0d65100e879db9934b71c4856f53a49b490509c2984f8612793afe9f285d17338e80a47ce8392239a1c451025f70ff2390538b89a5c1fade36f54e28cb17391294919f780f945fd047b639787c588ff4e9913e77348fd09e8ffcdad96fc16f52f72967b50d2de81ef3037455bec94e6e6f36809804b7306776c639daea9a85fe120c60eb6019526d3493490f65d5c9f4d7260b6dff838b311e1240f849c44e40a8a082a86843461a033df32f2c5f10c13c642b22563f15e83696f71c875294cf5706c5fb10fc7feb9ac57a4bb033ae537d35b204c4a9b0e6569c85a1e4ddc8d48371519ad438a7623eaa621a57934518f2d867b65d6b2582daee8f78be9db3a1076d945cef90d7edc816074cd461f4a71171da3536e38517aa1a7c1bd18433e0d86f82da4eddd6defbdb79452ca5d0c400d950d3e6f5e740975cdbaf8fce0bafc74d1527e6a3d2009cdaad4805cec0431010a42e2484bebc5dc1bc82648c8c802f16f563d929511f2372dbabab4173f59e082b69c28d2dfb188b60555c1e566f563f5fb665d50fe7513f250516bee2322fa4d73376bb0ad185a31b482546188288bad5f42d1b7055b39ad9e18824b4b1f134254c2ef9ba5e2eb0df3d7c777909f766f3368eb9b762b2202cdca2ab38ab0ef25dc8bbffda6159d3d7c0ef45db384be2dc821118538dc02c2f466e122310516687ed3bcb7857a1f74a48505373f852a0e4cb418af5bb8f3ead14bcae8a814abffdc9b8a37472e4a513497545cc446bb40eaa97d282da791bedc219544bafa8f0a8b045ee1ca9aab9b46b33547ab2e54735724faebb5e63edf483aebdece68ebfb66fd75f24559ab8cfa0287c3e170385c08e6d09c035f0c94ecdae541b7d977c6212d0c43f4b34f038be70ebd04bdfcfc9d57443b507e46f78d0f511bdfb9bffd4380fb7dcf0cf913c1d26c212cf594481e99ad79953a532289efe7932c711903e641871ec2768ef7e03fb7ffa15faf52889a427a82c8311ebd3faf8e1f077f9fefc68f553cce589724974cebe1943c3ac4d9cf67b73b1e64d92b566a679633b618b63dc0284f3993415ba7953c3f7aca5717d1a77c89117f4acf522b83ac64a0d287082a673c08959a4a9e59115f10bd3ccd267b96a083e7be9f7d67ff9cc4e78be0f7dfb9af8a526646b495fa871565d00e43258d4b262ac310f2ad34a31495e411162579a445191b221a95f52af9ae9edd82f0b687ceeef80fbed6a3b21c23ba28431c2c5219b4c3952f26309e9030cca68a3fbbfd218005ff7bfc96f715c328b3123746592211934ad04ba6a0fcb47485f1028743ab63c4dde6aa458b8040de6bc3e826eef4bda86851c8b68635ac210882a07f3db0f2c4ad5fdcb4174317efed3b73e842cf81d0bfed1fa29846d31aac9ed1a334dabf3ec6f904b77b6f89b1115a6febad22997b6f0c245555adb5d65a6badb5561c36e24ad1d55e115b9ceb91bd169b35f2077ee1a7f1ecda9b74034caa3c2d74a9e88dfbefdaafe62f15fd7a908ab808a3903f10346b842888e2f88de398c7118fe3388e771ced388e63ddb99eb3d974d10f346b5cf3828bf3675e604bb3863541f333cd6c9a26bed634cdaac5518faf6bab6e6a7ad9d7eb553fd8ce318eaf175996668d7bab47d54d1396c1f0af63fd9a69dacb34807db234611606abb9b437566dbaea982607f60d017009dea233186d26c6625acbec27e1e0dc9a8c6adcb7d5235a53adc6fd593daaae5303415f746b2d3565b058f176496c9408ae180d92967034eecd4ffaa29a878746c3341a8d766d85c1768effd120968afefa6d7f8988e218414ae36a194e998473c63b7767c7eed411b64d2093e1e0e4e4cc664d3bb527043f112747cfececeaecd096684b3c97c77eb3593dd2d1d9d9e1e1b9d1683d3d311f1f5c9251ad4040f8da71c7a67b2abe9687a67b74896bad866bb55bb322cd6c227bcc1a3eb4ffb13f15f7f8d89f5bc340b9abc6fd1bf5a866f0d57b86e53d77c5caa01e55c7251989e8a8e26523cb7bae3022cb219308768b156916b2f29ebbac718d8c4a23f2351a1989a191ad3cb0d247cf4a5befcf4f2da82654ddab9baf4ef6ce1345672a542ccd48aad5665b3134444434a335dd6645452c74ac458b16b0162d5ab468f1657c5bd8da53339b6640b4594dac41b66c02f2201bbed1846c4dee33dab56fab3f43f1b5280ac27e0800fefdedc15f5488f4d3ac71ffdcfaaa5061eb07733f4fb309b5e605e58a3a7487ecd0a7c2244036c25d5bb1b862e887be2811d1ed5654c4a285db6824d228864848e08794f145b24848485506db3986cc26a2dbad286909572b8b92058b580bb3a9fc8c32bed6a88ab08de38988cc1a96bc990618c1772002dcad88450b23b3c6fd5a8f8ef0b5b51e552f329bc4ebf8a2b5bf7bf6778bb6d66a3d8b9f495cadb579cfc0d246ddba3316b4598dd455377f6b43f445599806b06f74a76817481e64fd30d1d769607bdab03a5ba4ea464797cce72e3fdf33a3d8baa88da02fba4b24dc8b180cb3a9c6b03144d8cef147f90343717c91a5098b69d9114ece4c678787d6e3f353030a12f21355615b3144742b62d1c2081f5d5b9182fea21b036f510838216bade5d72a02b25eb2d65a6beda006fe6c91acd7f879eb625d72496bd6c05fe33e07f64350ddf2e8d23d3615b0f315bd17a05ffebefce12f5f20d82ec1570ffa8a6e0c7c05edad6086596b6f40777e8ccb2aabf7265a74e7382f0dda7a07f9b768e27b4b6c7a366bd8372ff89c03fbe641149b4d1fca817d83f3f6e0e782dff96d8da4afb37b13af12ce68ae471fd8af393bd5bd4935b951301e3111b8d136b6641b94c1d1204bc4e0deec6bc6bd595ec525e5d86eb3269d7bab269947c7e78869dc11631c866088437c86a82649fbb9ce5967b9c4d556b0fcc8b004cdcf926399bd963d332c3d2f1d7fc57268268c06334d8f5d530d6e8b5b6f9af6edbe6f1bb797682ce1a01b67f418c750186aa2dfb967dc974ece399b5245659ef50a559e505010134035dfb8194035b4de22dd0d70aa8914ecace68c2ddc52122ee9b7d5cd48b28b8a9d1516b4d45bc344cd05f57a3d23c8a0a939a0ded501add15253a6f6e3fb9644d67ed07a8b2aaff2ec54af17c512d490277dd658121b9cd1cde877e525f9b03caa06c201d56a49fc18fdd45ba4857323029cb3ca8cb8fc86baed77e51971d9d8711a971c4bd1dc719db3feecac9ff3af276b8db465362f7839462f4fdb12aa9209355743894aa1d26bcd95636c24fdf3986fd14400e9a2d3b8d78afd604755c2f51642cdfda061b53acb542bd2ce327d20c7c37842af3527f3d07f646809f5196791b26b748bff64c2d09e2570389e1de7c3e39b66b348d849ecc383d65b647b18e7dcb5d7413e77199e3bf6dbeaac91cffe9df5a6b55366038c6edbe3711cc7711cc7711cc7711cc7711cc7711cc7711cc7711cc7711cc7711cb168e3e91609635ca3618c6bbfadce26264b831f1feffdfcf47a3e3ebd5e8f8bdeb6b7ad4e4bcd56199e1a0d9ce3a9b7c842ddf8aeba6050b3d2b470f4ad67b519361f1eda8f4ea3b550f125e70a638c4b1f2743d78e4d04947ebb91a10ce63c8ebd5a267be537b7dd7078ceb07e20c7c3ec558865e8be619045caf64ac7501ae65ddd5bc6bab7fbc4bb4f50ecd57dba4f3d3eeba9b7483c79f6ca066d2909c7d333e3e1e9e909122228485ba49186e65b346ef1a9b43133bad974199ccbb7a8ac5d6e64afd1b0d49beba191e4575a74973f3aad6ca1a2462dcabec31f259863cfa9b91ec75e2f2ecbb284f59c5697b33c5c9443d27765122b52e915e631c7a6024ab7569bc7ea62386e9964ae694c342c920df4deb255118d2d967bcb8e516ba54f5c6fd86967588bb065aaf8cace0fe44000d66e9178384723866627419a571a6aafb2e72b9cb33d5c943d6334db5e6d076d755769e39cf22e9fae5216af52f8f5b3e346cf5e9ef8142dd2e83430e8e1299e1de4dfb8f2756e1be563073fd06cfa5ce7b43f3bede79cf6abc539edcb4efbfab41fb36fda27edfb540bf47df4733c2e3a46cff1ac16892c1d9f1bc7934e0b63be5b6c15347e94b4efd88f5e6b8ec777ce8fa66fd88f5e5626dbb348d9471f3d8fe8b661f4a1eb30bbcf4e8719a5dd9b0ee7689edd2d137ef3e685bec763354773acb3e3aa84e3d8c39af3713c3a769f33ac45a3e39cb8de46979db51ef59ca387423f3a6d44f78def47c72602467474b4bcb78cbe3e6bbd6df2b1f39cb516edd863a791ddf62c935891b067a2c71e137aecb0d76374db3035ec34cf7db397e716c1dfc01f3a79ee325b2411c561fe7c7739d7fa3882acb67d9f1ef41ad9e2ac35fad14551747b6ea3174791c6c5dd72a45be748ab93d7e2961e74129be6a9c12d02fd737b7ebef556f7d662b3e9739cf4721ad79ed8bf93036c36c57cebaf9bf675fbd77dfb5ac5ebb1f3e7f398cf5c9f3f5976fe64f4670132375d03999b9edd4471c090510db4eba0384cd78e63e632cf91c2ce9f05c47ce63f1faa41cc673f1ac0dc74989b8e73fe7caee339d29cf3e7437f1690e338ae418ee3f8e738280e183e54031cd74171fc7cae7111e839521cc7f1f3a11d68a0e33bce809af2107111e833df4171e0d8f11d5407cd91e6788ed4ace9ec9673d614e7aca9ecaca93e6b1a3b73a4b033475a9e9bf6a0db8bde001d708623e8514435b8b70c666d34f69b331de499e8006e1148e3de40bf4ff4e719e4815e6d20ca81fb145ef108f1c918a9b3f7fa6ffd585b5d35650fd83155af32b82e8a9ad694b484f6f377edda2da0166107a32bc229cdf9bb5e6133837a84773cdd5bfd8dabbab75aedc5b0ab9b45a1abf7aca0abdf1834f62aa6f8695c5e0884d669eb4d28f5d04081ed495276eaa744516b1acd337abbb70e7a3d124dd7dfc09fef0d076c8dd52ef500e7a3862a1e47a8c0c3619db0814956ea81a52188375569b53136f8d6ad1367c04ca5624ef0822e253f304c9a22bc948c9d3ab4d15b8cb5d16f64527052d8eaa51e845ae59945ef4c3eb1a384def982fb6280fce0a578aac8d653ad328e2c09612bbde77dd29638ea1345cce8520f88f45b5bc29523572919dbc9b23fa4802295064037e429256f894f2bc5c30b4e2989afad382b398344554ac684c6e82dead03e2488524826ce2abdb310114ec24e113d85de59d64368081cb687901ca4521c1ec89670aaf1a4d27a29d8b7b7be6342ac4b6f114848ad0b171d78b5fc02480fbc948c65367a67120a718a29466ff127434d255195de1d9c46d8a564d797035010bd45924de53255a54befe39cb64c38ee94142698e8520f5e5fa052187e0c50cbbcf55ae62dd2972bba948c8950f4ce2fc6a0ed25a9ce48e19091e155e68c68268af04c96b18c11b90b9b714144e3842ce36334234378e6074e991e6740087110c93019cb44e19cf9209b717a911973660d4c099c32278468869067ce9489612433251cc28c4e2c53430e9a15341925f98c0d26192e386782d065b68466bc8848f81c09c9408166ce8867ae40abd10c980f4d8f98192be11929ca7811cd2891cfac5006cc2884c606bc4a395bf9e94ed5dedd81533d98dd39ded226c8d381bd114fe55de5e5a97a759fb62c2feb70efbbcabc7b05ea763c7c957b1f0fdcd9a99e6eeadb029f2ecf5e7d4fe0957dc2b96f07c3d75a6b73c618631fc771b4d6da529cfdadfd35fab3c7da9f81bc00202f0dfa96f8626c36651367f382ecd96cca68fdf087718da4c7582b3f59afb5166374976fad78f517e77bd1259eac38e75c7ece18573dce5586f59cdda2376c6dd7f51cd7f9b9680633c7f12eebf312b7c02f5f9f9d77ea52603de7bc5597878bacec94f9f8723287312dd3315849be463104a770ce56657dbc9d9f6497b481ac7b09976b0f43de7a046e4e5bc0ba8d9bab3ceb343078eb349d75c739a61d64e0e95062862dbad4faed955aaf5d5f4d5bbb1e0ce993c4a555a0db03afc2ed49a25eaf52be81ff7a154dd0d77116579cf5ac16c9bd5ea50c8a3324fafaa967444367bdb9c8af67507c95344f415f17aa79d1adde2c0279bd4a2dfc7a06c55709d3b3bf6e33a2d2d7836e462d5814d59bc572f47a958efc7a06c55709d33833da5fc72dbda0afd792968e8c5ab0a8378ba5f6f17a9590fc7a06c55709d338b31d9a8dda5f9f310021fafa0f8d0120b938326a516f164b3d7be1d73328be4a98c699edd07c6a37bebf8e410647f4f59e0364f00287e4e2c8a8de2c967a46e3f17a9562f8f50c8aaf12a671663b349f5a905fd7200241f4759a03221003c60b1c928ba37ab358ea19ad76865eaf12057e3d83e2ab84699cd90ecda716e4a8edf6d737d080127d7dc7021aa02029068c17382417f566b1d4335acdef39cbb93832526a41018ba4a2183718442f86702b906c36ee0ddfb837ace3811b39250a9262c0788143aa378ba59ed16a6ebb275801928ba325a35c0b2516141425dd6210c1187ab10267f37b5d04ba26fa7a8e57b3060e5d054b39250a9262c07881ab378ba59ed16a6ebbc9bc5e2526bf9e41f155c234ce6c87e6530b72d436746361e40297c4c3470cfa3a8e930f2619152ce59428488a01e345bd592cf58c5673dbcde89e00f0eb19145f254ce3cc76683eb520476d433716462e703092969ea060d0d7f5102800cc609251c1524e8982a41830eacd62a967b49adb6e4638b3bc27cceb55b2c0af67507c95308d33dba1f9d4821cb50ddd5818b9c0c148525a9a119544067d3db64b6241000030834946054b39250a9262d49bc552cf6835b7dd8c7049f77cbf9e41f155c234ce6c87e6530b72d436746361e4020723496949c68c1ba0d4d40afabad964ea6958100000cc609251c1524e8982a418305ee0905c1c19b5605174231a5a6143cb7b0ac0af67cb94e4d741cb14c3af8b9609865f7f59a6d4af9796e9855f8759a6f7ebda32e1fc3a8e65a2e1d7679609c9afef58260bfc3acd32b9f0eb3e9629007ebd66998efc7a906502805f77cb64e4d751cb34c3afdb2c530bbf3e649998fcfacd32b1f0eb2c2c930cbf6e64998afcba0bcb54815fc759a69b5f87619996fc7a926522f2eb4a9629e7d7972cd3905f97619994fcfa0ccbb4c2af07c03251e0d76958269ba5baa74f94be356c156aa708e04cc1bffeafbf4e03f6d72dc0f9eb01d8f9eb00f0f9eb3382fe3a13fad7650cfdf50a58fcf525177f3d07e3af2b29fd750ace6a91b68cbf9e74568b743dc6792dd2751867b648d75f9ca245ba8e3b4b8b741de9d416e9ba8b736691ae1f9d348b74dde8ac59a4eb2d4eb748d7599c368b74bde8bc59a4ebb7d3c8225d273a7116e9fad0996491aeaf38972cd275db39c3225d2a1aebecc53832988af346feebe8e9a7d01974029db5f3e7dc3376c5b2abd563a761e7397715f3d8774e9d7357358f7d76eecaf5d873308fc72e3bb7f5f1d8f5193bb78d7aecb073db248fdd3ccb735baab753bef268380c361001cee5d44ae15c4ef56b44f752e589e8ae57352bc420baadcee943b785da6574db295bf5d8df1a599135337ad3bee646b8ab847f30fa559ea59a5279336e8052533396664425594a5a7a824ac2253df62f3b68847bec9fd1cd480dd7cd76eb69d9dc860d4b0c58fb40daacbc38dba83df64f033411dcf0c00c475bb23113c10d6db33133f280164149bb91fd46b9717cf938679ff349578b847d8be5cdd5d42784708a2d25903085ae8717352510117eb50732388ce912c4051282209115e69081d72386ac333a22241ce1252c011128d4c0c584294af092664bf8840b574ee083102e1f2768e120d38da03ba1491256824033259c80133444818035c58820cecc109aa0cd1340bcf940c90d55cc3819ba0a13840a6d94d47082324c3c18a306092134b4095302191680382289102098b9210d162354e0208a287e2051831211ae1491c2f77ddff77df58a13595db6a420458931bd20e80b1282d8f0f402991344208284f033cd0891a5a3049e8e26d23cc4e7127a7cceeeb7abf47ddf057a14896255e487ad11c6bcc981e9f7c1bef2802893132409149128165a5882462b3bd1c2536865245a4bb4866811d1ca45b4f0105a369c3f3014c717599a3272776f37c9bd5d276153f776a9eeedfacd57e92a5da57a95ae52bd4a96eae62c8f3745eae6ecd454b5bb5d92a79bb34f4f96c7cd599dcefaf8ea22b8390fd8b8b90d947a5c5f7d833bf35a97d06abe337af59b118f57c7252dd1bcfa8cab642ac1bcfa007abc3a0607c8c0c7ab6be080085ca5254e9efe78f59f2278158bb556349b6c93aca91b60b8cbb29b013211f3b4f419b9929461c0dac2e117ae7a46d76cca41ed550afa478bdef6ca8a59e2e469cd73fcbac5ba01a85da59cab94e3d76b39388d2ec5253f3f3229f4ec5ce2e3398d0f49c6a2746d498fe7343d262642dbfcdeee121acd34c99c451b2de1e11983e824dcbddd253bbe63863df48ca57bbb4b7496febace87b7f400ee9299e734da62dd227b756fd7730b1a837bbb9e465b191220b09e82af3c20a29e8cfd344086c3c608596276a9cc6b7aa5eece5c404d655ecd05dc5486ea0ba2a065cc4e8b18aa1f231ef49002892198f86063450c1018728200c2abf22901f1c2e585228c40e31466cdd83543ce194957948f1a6648a28627217ed8906f789203c8981c80a0d2040f3acc0026081f6fc2207e8040e583820f614029524723a465e9ab5dad6e8f5bda00575a57ddaa3b874e14d19d6347e3310284fe43b13f4675d809692bc63bfb5773ae18638c6eec1f8a2d768b51119bf12763d65a6badd5daeada005b895759342ec637dc92dbbb4bb1975729fba4a0eb571a37973f2760ce19dd9f874e7edf9773fef2f77d9f8bf9dc95778a637df988e67bab34b1ee172abae39cddd1b2aa156d49e3ae886efdb2ec2097204714f1206a7bb6cc55027368367d4c501122cd6fcbcb4df9bfd3fe0b225c31e2b7dde51d50570682d9e7339b3218fa7590891e65d0fcb6f7011f9cd83f27b3d9f4e126bbf572aa392ecb91c95ce6158699e7380e1c9fe5a01e5c29a63ce1a03a1cc741af0e17d92e5c64f5059900b184807db061041f9cf8d064874f903c7ee5f9f0f47921ab6d83bcb7d5b1e8bef2aef06aef2b0f87153e3f551956ab1e5906dcf442e1a211f4ce4f6015ce7d5074ed74dabfa92a9c9351648b148f87733952eb9fd5778573301d7cd8f2a44bad7f5938b7012ecaca3d9c6bf1c313434ce952eb590be7725479f3e6cd1b2bbef207e22cabd1fdf5defa063897b770917574572dd4b4254aa2dfeeedd3db4f2a4fe5aa9ef54feb09e7729a58c2422fb59ea1b670ce3a815579f76d655e9109469e9cde7e4f34fb41bdfd2eba7317a8ed2786ac968903fb1d84808310fc1af56b3db24cf5abd71564b56dbc55ad16abef8d096d7d6b7bf1ed8729460b153d543867c99f0e4e4e6f26f9c01cb41330ac584211872c16e49088664988e37805a7e3647c895680bebcc8320baef270608ad14205f8611ef766734296592cd83433486966090096f16f997bebf162c24e007cf51ff79910b0d80c724b4c972bfca631415928da105a2643eb9d37329c0a8094c0c9917a5a3d9a4de5cc723c23cc7494b6e8ec7ca1e025c40ecf0f49578e858796f318ee413c5aed19abff70095584e0e7617a024f5a7fdd678773396a7803b5440a13da30712487df3e5c9ae8fd20c148942474e9f6c1f2dba792397383086f8cd031e44dba7d70f8ed63a5c46f9f2b1f2c2d3e44b444fddd3eaf2b9fd795cfabd7f3eaf9bc7a3e3d3c9cb33991850c0f4c7c6872c495df3d25b061099b10ac38a10211e9eeb1f2bba7891d9a10f245ca153247d2ddc3fbdda375e677cf16d70b494f941792297f774fd54baaa7c94baa87ea55d5b3fbdb23f5d7693d9cab5746b6ac382199e2cd90b4ca6f9ad65f29765052f5419a3457e9a6ddf09be6e2062468b2a42cb1cb92ee1e2abf7b9c9e86fcf5dd03d513b5040dcc123431fe9b96d545ebd245c37a39d1aa5e4e34decbc9eaafd3743857a580e255094105104a3062ca6f9a5391d490ac1c74d0ea6242ba694f527ed3a0aad090c58b0b6894880122dd341b7ed3a4fefaa64dfdfd4d1bc1f08c607878d4f070fddd3c5dbdde5f1eadbfceb3c3b96a35441026a0f8e0893354d228bf79a4feeea4b2d2a4f0c31133e9e699aafacd531535044b08266868d811916e9e1a7ef3585d61fdf5cd93c5236664c243e49944fddd3c50533c3ea67886f0ba783c3a1ea7bfbec3c3b9edc48ea9a822524cb540c3ef9d094031460910281cdc2c49f7ce0cbf77ca1c4105c994a91a76a090ee1daadf3b5a7f7def6ced70b5d989d266cadfbd5335ea769a8cba1daaf169076a7cdad98d3b527f5da787732ba24822a48d14179b23a0fcd691009728322d0041c20e27a45b4786df3a5d1948212545083abce1724af7ce93df3b4e7f7def3ced0cd1a203468b98bf5b27eb4aa7cb950e564fa7aaa7c3d3b1faeb3a3a9c2362d3c49724d448c98a4aa77eeb0899a201092c4f5c31a12add3a4e7eeb4021400d1537663e6063048574ebc4f05b47eaaf6f9d291d2a91481b91889bbf7bd625eed4883baea959d6546f4aebafcf76385787f0f1440813c61899c22385e1f74c2acbc9c9852d4e4ca124ddb326bf670228a2862a40a208614c0ee99ebdf07b66f5d7f7ec6a86956646244dd4df3d0bb766e1d64cd4e588ba99a89be5d0c0820845ac10a5072e5f5cf89df332b264ca1a2e19485022dd394c7ee7f040a50405ae3146207993ee1ca9df395a7f7de76ce57069c989a225674a4e931caa9edf39555739505739bb1c29adae9c9d43fb9d133ec1099fe0e0e0d07ee3845538210e1627281ca9bfbe71a672af74e3508541da8441dca8e1e2f92deb0aa1645921542f84d29221ca191f3e64d8c1ab926ed992dfb22aabbfbe6557322c7009191170095994cc876cc8ce6f19545757974cd7e56495a5b7d65c5674142b53feea2ad04a37b1a2cad250597a9725f5d7633d9cab65821882ca961dba9a3ca5b685df312d28944cd165b1c1865dba634a7ec77688b082113b62d0b9a9926e9de4b776faeb5b3f6910490c44128bc5b0fec6b2c0581518e38131abbf1ed3e1dc986508104ce0a16b8824290bbf634e3574545cbe484121871ee98e9de077ac2e2143171790eca6d44049770cc9ef98d45fdfb1a918d5126d9670f317d6f575c1d47c5d5ca0130c7482814e30d80ee7162085cb0f2976f84164f71b56c14441452607246d8c18936ed80abf613ca826ac4ee044854c0ee9861df90ddb3018961518112b517f619f15ecb3827d59e69705fbb260260fe7727c70240c12416cd9c1c41babc26f33688a561b116490a2254cba4d1bf5dbbcfab1e48413aeb658e121dda635f2dbdca6c985c48c8264ca5fb3c95f73eaaf592565424999bb6d4afdf5b287739509146890f232246a2b4d5ae477a95501178e58f9a1042740504977b9f596c8efb28b053655a474ecca2c916e13eab7e9f4d7b7f9640ef97494603e1da598b24b89f5b7ccfa9ccaaacfa9e4edd2eaaf973a9c3b40126b9ca0e1618db92185dfa5539625b4788152c24b0c26a4bb44e17749d543af0c9621b8146193eef284dfa5d45fdfe554ceef92ca0bd9c68b9bbf6457ce22d5e42c2e2d324baba7a5f5d7c91dcee500f2e508167638e10986ad21bfc91ac4944eca0a503790c025dda409bfc9aa024001b36608992b4c82a49b14f29bb4faeb9bbc22b19e90449e9051a40f72c85f12aaaaab8ad46d9287733c49daa059e2e4c7961382fc7e591d200c1145b4803ca52993eed7d5d3ef57d60d26f8b801ca171d2944a5fbd5d3faebfbb5f5e2ca415e517290d794579317d5df57557e41e5d72ebf7a38577b505d4a1429e28d115da92de1f7a895801d2660713a8344d491748f5b16c8ef71851b98b4b015458519a8a4fbf5e3f7cbe9afefd7d36b081ec1e0514cf97bccc263173c62e1b10a8f3c3c5afdf551877320b841480c63c490b01322f5f17b749a8185146a006303971ee91e9f9e84df23d411dd0c312c41262b041de91e7723fc1ea5fefa1ea7462a6c456c83ad886e443522d75fb1cb4accb2ea59ed706e5cd2c4901e42662823432ac26f512a07942f49a69440e5871ebea45b9c72fa2d3ae143161eae7a38d1a149ba45de87f05bb4faeb5bbc12b1701091080e2246bd7e8b50184af4210ec1525d584ad461291ece6de089335474d0d1c2134a52107e873b4c6d21c14b218a3031a43bbcfa1ebfc3ac0d58e04161450b0f880ee90e7b3c7e875a7f7d875b215711619422c229e3efb04a2b6ca215527585505de12e94faeb600fe7ea159e0e2d4360d5d0c312e98edfa09690962c3fd610c9024256bac1ad0f7e835d7fa5da8809a31b42058974873a1dbf43a7bfbec3a7bfe1102d20182d62c474835db0fe825957e0942b100be85402160d52449460f2258874834f7f75bf412850eaaf6f702a074b37487589b4b944dcfcfdbaee4ecddd714d7d5953bd292da9bf417a28618286aa89287a48f737f56f7e7f559fd55fdfdf15ceef0f2bcd47244dd4df0f6aebf3f1776bc85fcf5d9fee86a9526303902e42ac4e904ef13b5bfded42a5851f94b8e92125ddf94a8adf994b0a2c418ad4b0860990746737bfb3d65fdf794bfc9db9bce4285ea6fcddb949aecacabb9da57232fc7802ca8e24ba256914bfb156d5902436403142e40459e9c650fcc65d5d57968082c85615978f74e736bfb3d3d390bfbe3354f83b473dc1609e88d9b88badc245559867b10ee7aa559b34407a3abef850a4eb3776c25039e81842ca933527004937de3df11b4bfdf5df98eaaf8e3656879bbf4eb8488d13d743e1a2acbbc3b95a86479612c82c59410a98d489df57aaea5a31b9bf2fd6022e91ea25eaefbe500ef0f1376bc83bfdf59c0670975205f5667ddb285fc194bf396618a2ebc70a3c64f8b2246de2b7b55a00102427f43802831b2ce9b63daeff6db9feda24346e91f56da3fefa03aad2586fd62ffe3d8af96b9b84e9b643ec1313bfad53056c8c10420424b844e892b27927217e408982cc942d4b6cd9f284e50b1a273a1de9016e4e57b4c7922f639e44118509e9c5b9ba0686105ad0a1c908315c49ef9aaf00981e11212c99024c557a95f8eb38f0affb127acb9ce82dd3e9abb76e6ba2b7eebd75f3047aebdd5b3791e8ada7deba6ea2776cebadeb17f4d6bab74e1ad13b76f5d663af58158faaf382de31a7b7aee382de315b0c7ac3786fdd0683deb0abb76e96a037ece9ad9b3ff48675d1dbecbdf59e2d7a9b5b597a9b5ff436796f9dcca2b7f9f4d66961f42e7b6f9d2684dee5d65bdf71a37739f5d677a0d0bbe4bd7520357a97bab70e8484dee5d35bcf50e84d3ad19b9c7aebb318f426796f9d4704bd49dd5be771d29b7c7aeb2f2bbd5f576ffd9545ef57efadbfb2f47e49bdf557958be8abeaad874588553fbcb8e2458f994da2f3f41ebbdeba57d1fbe5f4d6c75e888ebdb7be0208cee5d4ba4cea2ad5add00a0a940f6d1f3acc6c0a5d08bdc7a9b7ee60f41e796f7db645ef51f7d6675af41e9fdebab6416ff1eaadeb287a8bbdb73ea3a2b7389ba2b7b844efb005bd45dd5b171241eff0eaad8342e81d5ab10e6ee90d5abd75708dde60d65b9711a13728f5d6750f7a7f5d6f5df3a037e8f4d683b4e8fd59bdf5202bbdbfac87f2200882a283601623511ff27ce8a6d9149679d14bb34924cd269066a57768934aef9cf5d64d287a67adb74e12d13b57bd75b288de19eaadc79eacc780e88dbbdefa8bca7ac983de7807bd7110ebe293deb7ebadf7e460bd078bde77eaad0745e96db7de7a5011bdafee15c03583171d4800697325cd497cbe62e4c7679f65bfe1ec349d4ea77b4ab3111fb6f8d05f665398035daca3d9048a6653b6593827d3c9743624f496e9defa96eddeba6c274b72a5080f9539c9745f14fd5f0ef779fd7e979f7fcf3e7b95faecf92ac5f88c6585309fb1ec867c76d06ccab52a7d0f66b3e92ba5d0af2dfd5aa34b157419a5ad1859e13176db8d56b352a489ff3c5f2516ff55a50a840b9fadd98471ff15a1bf5ed1ee65bc1144e9a4e82d9e59e449e1b42a9538691e4b5f29320196b2b09489e505c58ac345d8afc61883a8888b70d5bde1f034deb6eab1ae1be7a2a979accbdcaab13cb5bcca9205b2e441014b59cac298a5e804acdc71428c953b49e81268069aac9465d1db5e5d5d5d5d5d5de1943b5b39a598c4ac14d55ce9944e75b553024599c18856caa674f5945a47f629655bc49f12a8aa572b3f1ab4804a5993a0527fd02554025df172674ded2c3f1d325a962da8284536b652e45a51c246182a452432515926b9955f1ad16e5c510964456f7b7525b228c5234a71a66b34bfb528714cd6f78b5c6599ac0d6f947345dc339e342a7b6a4eac4538173a765c95f1557f5199b2579ede9465caa82da22255e15c88da2b32323c268fca9b9a6faf663a92aed2b6568f1dbb6851ab12682eca0f8af592442a3f5114af704e0cfa7c75f86660448ab637e79c69554697067ffb571df40e1f7c38572cd2ea598de09716c1ec9f69dc100cbf0fbdf13d08826808becf68c6e126a4675c7e58c962d046ed267e9e453dea10c5a9a0ad6f1c4ec4b966d3222449ad5e95212b0d0dbaeb99d6355ba6cf33ba6fe01b2d63dabde1330404b83a5b8fb2676c367dd93400f9e6cd6f1c3add67b483d16f885f91b2571c21b83f82f8419403fc23fadf35b5b4f52dea19cdde1bf8b3df376f42803d3f652c6af03fd75ba4812e82a7067d8b354a54d3b2e72ca2f8f6998681116d7d572b119b4d1f885ad3003a2e00e1c20f016e463b80d3ad21abb5b5625a5355aa32aad7db757d6f5729a7deae5f1ae700ead1f59c8c36d5db1dc0bdbdbea216fca0d19bf6167ccd6d94d72da8325e7fbd8a4f1863a85b84c590062ec24966b5f681f6dcb6123bbe50160a63df007f7e4f31f41c8ae199dfa2153c71a8d634dadeb8c7f5fac65d259427de0f8d756b64abaa76aa4a5dd036a1592a5e2d4700639d2a5230377d8ba55b290fe01c0c45f57d6dcf8ecfd0471bb6a610b551e69fe5b4786934f75bb652958751297b355e9007d1f6642cbbce4fe801dc9cbeb8a58d2b1f04cffab9ad36fb8c73b6fd2dd7a2ecb5cf5b97b6c74ed06c7dfd1aab36cf15004e9ec67cebbc132e8df9e629ddab87d64df00b40ccab93d6afd9047a788a2f7a782e8179ec7412d3d1699c03b8b7ebaf13832ae6e9e7cde8c49d49df396346ebd23fe2579ef884f6df3acb0d51e0d31880d72bf2662ad2b595b69fff88fee342fe23a2190079cd831cfb2549bb23f393e8a4488aa4e8a26f27495288f42092249d244992244992244992c4c9f199a9cb4c9224e943923d64a9494d239dd42167a4e79038a4cb48922449922449922449922435192361a4499664261de8ac9782ecb553c445d97f4e1b2996f072cd45d77f81fe73ddc783fce7a219d4fcc7813c3bf6d1c5fcf432dd6a6151d4bae2a0f3ea70d1cb814edb858b5eaf97bf5e4ee3be5e2ff7e979bd5eafd7ebf57ac972665eea382cc6f3bd5eaf57cfeb4543b7c8836ebdf3d241b7d5fd6bf6ca7939ce4bf6728d6e0bf5afd7ebf57abd5eafd7ebf57abd5e31747f4e30747f4f26fa2a5f24ba3fddbf72d7bfbc76d64bc1e73fa7888b3ef739352efabcda5e2807409ebe2110a4f56ab356c7c32d6ab1b5d8626bb17f19049d043ff0033ff0fbec67cfcff3c901fe2e309f568faef730a9ba53ae9276faeaf9c4dddb122eba8e4bbab71b8446df2a7cabee5548020f540f3ae8a15fbbc4741a97667b9eaaf4005586ace71c6b9175515753ac47d663e7cf75d26d0a3b7fae979ed30bf07360dffc580527bebe66b1a184c7eddb8f6118e210dda367979db11376bedc9a178c2e827ecfcfc76b36ddd0ab79033f017aba9ca17af0a2366e0fba350dd0d3e50c9407d1254e9e9a68d22db2e29b674dabf9a138f14737554afe2ee14e1af5c8fad882f6df22f9354b95247fd1adafeeadbe58af5e170c81b426a8faec3f4cf4b6bb7dadfe7abd17ad36eee9817babb5d6ef73d2def2dbb9da8b737514f475a1223014185bae9ca1d8fc7dd94eb1903f300cc12fc3903f300ca33e10fcb21afb81e0976308c2e6efcb762a7f6018825f7efa2167305ac6e49c79743927f18484fc8161087e590b981cacad923f10fcb2182e536e905a22e74ce6ea03c12f73d1d223e74c43fec03004bf1c953f300cc12f47e50f0c43f0cb4168f8c1bafd40f0cb27a86073b64ba6c879899c57e821e76c06e37b31d8f5808688b77e1b418a2b34a001b3c3e38a4081bcf8e6308761ce91befda56bae1cbdba5895aa522ed14aa215fbdf1d7ef5af32cdb048d7ab93e1f9d5b1de3e7386e863eafce775dfdf33c07afb5e21787cebedcbe07f2478ec7914abcfc6797df55bb357276b48d3222a81c704705b2d8665d4deffdc667cc1effb2c0c58ad479fe31ce22d8390a4bd49f2ae3c2da8bf713f06f2050c9cc2ccfa47adf94a5951b5028be8cb9010222e2229a2158874ecb819290a43a464851344552c3e2822f2e4c663280a16405a90c04209d18e1622ecc06237a445f401113a2f5c3d845022081e0c584154f0a10513ed8344914ec3c614d9a071b89c408b3810e1a0c32945635644515a714249eca981826c9321493497c49001c68b3cdbc2e9c8806481b25c581b9423738891d8f3a5c52c0c0b7287a24c2be2b68308686b684665137980a8105f3ba0e26bcce9563c07e1201494673d00e92a353bbbf2d3c4070cc393412674b20ccd2c6bac9c1cc4038e48e341766b7c195f70ef05f7332fb8f55e6dda10dbc132b93373ec84b22a7dd96dce8ef3bd5d8b5ed4de7b2f112f116a24a9883d59c22024a0f87ae10381b0f98dd0d6efcd366808f26b42c3a277feac944ffa5665dc6a9dc80e38b06ffefa0df1b3571bf9bda0770622a37307e1df10ffe702ac538fac59c3fc8a8af588a719503fbf0ec8e987e2f020e7f0cd14babfe8ce135a3c33ce395f8b03db1efc7dbf5d5cc415875f79686cb8d7f2b2a665dc17ed5ec67b7b86042f5b9248abdb31da46f13194e0a1c3951f8e20a24c5a2b88820b550e3bd82401c142f1d66553aa8cfc96039f4f5da56fcf40073cf67beedb5f7c4fd4c6edb17f32a90f95ed6449eecd7acf11bd653b190f99d355cabd951129222223521455e4a368c8df5d0425dbdd64bb22d9aee8c6c3b91a4c52d0b1c044162042dafcbe59d00684221f38a10b018874dfba7edf96d270e95a808147096fd27d7be2f74debafefdbd64db7b9e936b7db8deaef6ddfa0feeefeea9bd45f27eae15c8e11ca08e11df18147922652277e1369fddd41260a0626929cc9926ea2267e137101e20305365c88981242ba6f5cbf6f4e7f7ddf9e6e43b41081d122e6ef26cad257445df4151196ee1155e91e114ff788acfe3a910ee7aa9a214852c0c1c81a18aa30f19bc8490a0612945c45e124dd444fcfe6371114971f393061e20b5710e926da1149fdf54d3445fe26a2d244da68226efeee21bd1bd2bb213d35a4a786f4d4d0d00ee7f609bcbc312187373a6459e2f7d00b3a2755218871626647ba87b67e0f518006303a6489a2030d27a47b68cdef21abbfbe87ae62bf87b0d20c1149331405e3f710d4d6908fd8d690bfbea24beb86747ac8e9afafe0e15c03c20061c6092423bc014289df2bac9ae84a82ca981c88689921dd2bae92f8bd220b4b6bab0d4f47e449ba57a8f9bd62af58c1a56545142d2ba6ac68b282eaef5e51155b115b115b21f5d76d3d9cc340cc112c167ab0c10d9a1489df362d105c684184351fc80025ddb6237edb7a1f6831028a1e5a745049f70add0aa7bfbe573cc17eaf18127b6203137b621363eb62c3fabb6d595536dedf6db3faeb361dcee588caa18525588c14699225cd6f1b9a188ee872e689d319a874dbb47edbb0b4c023ca09549e5449b70dcd6f9bd414d55fdfb62adb94980e156d623adcfcdd2aba624e2ad43871fd85c2453128adbfae628773357448783494800505efcc6f1552434dfc80626a0a4c1050d2ad62cacc6f1555171082e68a8b10639a48926e15bc32bf555805f15bc595fead020b96460511581a15512a7ca818f277ab80826da16e605b2a78fc75948773475a469280b0638a2921647ea35a47a889828d151d2a40916ef4ca88df6856932294e8a1890f51a8a41bed15f11bd5bafa8d6ea51be5824541a3c0a2a0538e7ea355301eda8447054361280c95faeb670fe76a9b1d08563d429042e607dbfb7d563160a80851040952b89192eed312f1fbb422021b2082d8d1852724dde810bf51a7bfbed12774084c84130c4c04317ff7097b3a614f276c77c276276c779e3b40f1c16e2a4a93273ec6fc3e3da0c5083233f08010c142ba4f31bfcf18d63cb952c1071a76a090ee73f7617e9f527f7d9f53e6ef930a4c1b306efe6eefeaa9e9716d79d6566f4bebaffb0ee754b8e131f1810213bb35a910bf7d04a7204ea08a820c9523e97630bfbd4a032b5ba8a0b0830c4d9049b767fd76abbfbefdcab198381126517fdda1cc29f7313584d7c5731dcfe9af0bf1702ec71122d0a4e08305166ab0f2e5b7908c1dce7039c1c525cc07e9160ae2b75056069c78a932844d1125b449b71010bf85b690902982902982909010d5df2d54650a4199423b5348eaaf07f5700e035d636e6842c9921f74c0fa1d54a138320213676ec04ac2857407fdf03b288745489a10aa70c16221dd423efc1672faeb5be849680898203060c4fcdd4159bda02ebd20acada0aaad20de0eb2faeb413a9c8bc0162740b0a10ae10b0ea997df414e2190828411509cb070440de90ebafa1d046505c5ca4aea863160d21dd4c3ef20a9bfbe83a682a84a266d4a266efe6ea0ae2935535c3ca02c5e8fa7f5d781763817aa41c24409251f9c8025e5e13790140792f0c035029b2e1ba64837d00ebf81a480a1ca13504988d8ca4a375097df401b0808ab0d109136517f37105409e4a3041a52d6ba4a205d09e4f4d76b3c9c7b00970f7690f24215ac255c7ed774a60c3145550a2a84a14a776dcbef5a16053c1c4cb08107066b8a74d7b4fcae69fdf55ddbaa7191b528646d4aad09d5df5dabead5a07ab55d4f4aeba7abe6f4d777ede96f6d08c9e4070cc944ccdf1f72ea879cfa21793f24ef87e4fdfce8702ec79414ab16744873830b4c58fdfe410ae2861b868042450712e9fec9f2fba7eaf0640bcb0a63acae9449f78f0ebf7fa4fefafe99027fff50b569d3c6cddfedd345ea7cd4903a1f2ef2c9278b7cf2e99101a022a814091a0f7349a714325323c00040002317003028180c8a06a328c9b144f90114000e63b65260521e0824f220857110648c210a004000008000008001a588ca00984ddc1a451fbaf9cfb961c8ff7b3289e65cadbdd42222f66c6dd6ab7c1bdf1cf354aef12ef285df769b694d6dc1a333d0b5a3f8c7961c74a0d18c4e3dd24813a7e9f27ff25e9a1dd91c9326343a600a343ef5146b088a2bb11db8e2bd1fc26e9e0acd637d1d476173dfddbcad8b587b7267872f7993e8e7dacf52614b1bdeaafcd3e9be3aa9665dfd2e71d8f38ead7514b0b51baa7d7eab015f23ca901cc98c0413bace4278363584745700bc2f7ca205439e90da644d1af0b2c967765d8da3d3df290edd2f9cf2f48b3bdca118d5d6219b6ae8b4c33159f190f0ad12d23661796dbf0d3d46d64c6096ca0565c41bc82e53925b09e0195e730346a1c0642ac0762c782667a657bd58983e3a8369db82fc14df4c805591c5fbe8a63c420b9f468bce76a6f6e011d37547d5725005fadc4e0fd345637e8e0cb18c4ae6dd1061371328c52b1243f80e949c444a63ba5761a8afe2977ec36594747496e8fef731c433c3eeb1d0d4fcc9fdf6b11859b24e1a25930c61d914d4faba54e98d5ed4fa360a3d63e77d8aa7829e273a89c921b8aed9487bb9631e24bb547289d42e9521f2525d787d157d6f87f49345f26d501e97472c6e0d1eab884ca5d0a58fd4dbf8f3918233d097deff66b59aafda223d13c89e77d3a2ef63da73678d9de076508298328aa1c0f6c8bbaeae0dcca05c424cec1a375b4140a5c3e1a600280a39219be2449bdc19889004b31d38a7abb8d11953520f59b045ae700a7da6a498aed89686f7e93aead269de6e7c31b9abd054b762f40cb42cda90661a5b4a99290828a69def47260d649b6b29dd69b987a21275aada3f4c3616e554ad56832442805a962a68854d48274f4a9379ea14136e4c3e8b34d6377e003f991b3b73e4954f2920b0a5cbaf78b2852ccc7d6f7bc11981e052475c585592380b8d49ed07aad0b6b4f3aee88367545be5f15a2636cf5a225089fc7fc859ff7d41bfda5379385a2293bc6a1e9bc464b9b2a059484323829b897417ddd929969b47ad0a1ddf0339df0f15388d7c1e88a801cce4e69b09988e7b99428bbb2bc51bb64ddf4f022d57f4feb932ef720745e33c403818245b4f786ec3445ff9b020a71299336c04ddadb0b968f1081b6981a7b05a70276f8a3e2475ec520972156ecb28d02b08c266e1ea436434c9b06eb243fd122bf46219028429b3c013b9b6746ab970370f957df6fd2f64f4edc25d7a53f220339b5337a1c145c448f46f9d2f638b9a1288abfe40a3ca171740f87c1da80fc6349a5cb97f64094a383086c0dcae24701e8b0e17ccc5c07accae4233dad06aa3f31c706e046b7e50e7e9012e270c941886884026fa8dd0cb94b8dfd96a9bb854b39853bb78b30b30d209e810e5f256e703a6a041dee10f28cc625cc31ed8dc3520c2f8593c4632196fd7a2a8d9a0739414c4692c11b771f94e059c92aab39bd05bd24ec8189dccbd68b43e246ae26b2169b03bf141a60d200e280b6e6826703a008e9eddaebb7c5c3e5aa63cd390c8884e9e5f203755dec2f9bcaf695e54d5990806af7b6795acc3450fbb5534e322068ed64018e2f83ed9ef25705b193f2eb28fc91190b3c4685e17053dbe12eaaad9e4b683e9e35b32dfa544f1e87e301a1c4a6a5519be059303174506af9813a142da9596c9f9be4025a77ffb80d9de20e5db07286065345dd331684fa9dc4c352704695315322e616356d685f5aa05b043aefcd8882a949190870984a2a0ac1711d1f32bbe380a5169c09fdb553805cb7839203d0e961b5c3b6712d736b0178dc0c8fdc3604458418ef59a1495ec17edbdb0fd93e482a505b98e645884c80cfccaa2ebcc151a4a59593d76476620572e0288eda5954c755846875d40ac7c68114296488107a4231b80358a59fa70b383f0d36f684a5119885bea4c36415da8f7e8e4b551d995b449472234e3e14f58c2e75bbb060e558b7014abdce08fc5bc86dd8d83d18df260375ab882fcb42866eaec9ad651cf204ef9790048beef049781281b24a0b6aa0af03f183259e16a4ae993d60d471ad0220cf65220df03161ea5d63150ac17e5828c4130603d662d2b7bca17cfeb7cb85913df739cc507e3edafa0525d79d0c432ee0611f908d7bbc0a4774701c159ccad6177486e090f8e1c26c2432b74dd77c9109bf448dc2b1a4d2594d3f90b5260b1f31bda13e67ff72b008d1c7b5813333008eabc0ab8e65b7943d79827b130f38fa6b3560131e08352224285d60ad88cf75986348845da5b733b53b4f6b2a722259df684eb8a197029e92f568a2328bff318b4f6bac434abf55c42b357f0955d4e879607e48cb80849dc3cee84f722fe6daedbfd4cc20cbaa08904212285acc9f76b2f9955511db465e06dd0a82d9052fdd53da7c962ca46052153222a0d7e8f7a82fd0cba40458bd8105b97c4fd3bd2728d0b0797559fcbe18279dab5b9a541ae2423f23e971d0fe1d60ef654a3b512932aa0ea8e369d8240358f3d177e410c37ec8b918b4a7e97a243b382ba64e96924e571117f7458b358242b6795f0afb0fa388445fe239e16cf02d1be3e6df042448478e1bc457769af7e9705c560038c239c0132892f0e270e30408e3f8ff30388a864794d9f1d8511e9940a802a88ad15716c87d127116145711ef66089106924c820dc365d58b73e8832248a65ade3ea71a224bf922885053a7b8371eee4c3988b55d516e545bc6e16468f800f27808423fd5291b48db1ea191a76afe23448a6af1277c06994c93b41354182680169c2843ab568f9ce0718fe09d05f3aa1f9b51aeb41cfa0c61be9935ed6841a1da7d44b15439934f91c5c412857b17b4fa0c89dd026c15ae385ea91c9a906505e3acc355dda2e9ea456de8a793d5f3299d80a9230f0ce2c4874d849c8041603445b9e26aed63592e7b51d4bdd4c88c9c39a7cdf40ac46fd4b48daba5523e440b8a4a44147ebe6666fb573dbf1b2359bcc94fbb656bdd1871efcf0d343a5a1cb3f4c545905919fb0e9da13a75a97a7cbe996b30fe5562190c095150871da3088bdfb3b235daf2378c3f1a6df731d7fc8875257a1b79fda8859abab929b323932b3e8059187c810170466ec1a012018f5c16fff5837e1e203cb1a110f85a23d09b23acc203f0818b7cb9b082fa1bd61446f68b5e03d58a54e2bd96a32437b3e817b459c661966891ada83fc2aac6da067c268ad1b58a88c1fa290553f059d3c30638956901de9d54485f5269b10befd432eec6d1a1ab0cc8a15b2453f2562c781526acde7801927b34893f06373533b7d45e6b48c4ba67d09900efee3e23162e238ac260bae8ef40a22b255264c0c3f21ede8b771d884692692e93f0ef41656946de9c9c48df1125a902fedfe302622fdfd23eed8633a1ab3c00a2b65b20fc67d2b913327b3a62b8912564d3781ccfc83713331dd27e782955322330f1ffb9859f8806e76ddde7021ad993051d9d6bd4eec18a7b4644e566a86d4d83864c2c426920dcd9c8895879b326902b2a14d5362e25053a64c2083ffb9b8d56f80b492721ce0ca09e3b13a3efedb415726ac3181ccff810dd5bb28d7e79890af32c6245a2eb3e2976034bd4e6f084d2f5aa0fd257493ff6cedd36c84b4087cd42bdbc44ba029a51dca82990a4767203b454a90ebefba85dbba68e356900edff736e47094354ea2855b898c9edc0ecc225bbb48fd7ee7896122cca36eb0bf3d850d82dae5665cfa9314c8b435b858f38556080139d75cf5da329d6345f4f12ae567c09dfa7213c130a08a4e90d4aa3b149384fed197e3e9477129567cc28f4a3847701d85cefb443f7718209ef39de54064c203cc2e07a9e31d01a934a57080af926fee6e5d37fe1ea521f6c674e5c8b44ed060246968eec115a0bb88e0098dd38be8e0d2a7832ba10eae4b325cf893e13232ffb06dae919aa1bae8d0d1f0b221c40a6ee862a2cb5e4d0714bcfb4a90fa3f3be27e64e92fdd64562c1caf3a87e0e96db1888000949f57014c69543bc7f05861d99cfe52db502d7b1af734c23b289c6182b2422d007b8195c932001c6c3fef3b1b3864f2067bf37cf8579a0c28466851c129e684437b6a7ef03cf06843c5d3cd0fe73ce5b215f54e373ffcb424e03654e12654f6e8fc1f49d92fa264323626bcfaae27342a64a33d4f09e32ed5a62cc85f0aa4c846381b297093222f4d1220fb4f64465e0ca7dcac093f3eaed7c36a7d09d4cc832dc5c15084cae1b96644ed951f18fb6d95930056e34cf3a2ca5b6ed0f6809c9da8b29517da5f008e020bb6ccd9799773b5999cd49eb715def6ec2eac642b4adc5eb185e9adf196977edbbd1331e867a8ce6dcc46cbd39d08edf85e80a3a967fb6a701457642b3b6f5be1855987f1396e9991d913a99868f6a55c23cb36fa9191101754a98a8e72a9f9e4a4cab429fd3a2771ec26d668590e339a39c94d7c687f245174db142e813901572587117075de0f83632315b37d0bf7dd455e649e9be53cf49c7cbecd459dc72821124a79601cc008aab5fec97a88375dfb7681a5aa4fb89d8bc40096e803c76a7ed9b7662c06a3f1327fd75d9dbf0a4f79729b0362ceca7466814a4fe0a9484d16b1b6acfa8cb5ace96f5aaa7f7cca31322ead83cfec42ce1205249bb845c14f9e1b7208f9c389a323aa820a8fe11c95a6a7d5bb5d4e35a7b190a4893578512ea3bed8484ba220a610d20c59aa5dd4c2b01dc5933a08e4bd6993e19922d8c509293b355736992042b58528d3280039fc0c3069eca6881234248338b9553a350df75c7f3956501c5cc0dca0c3f662ca6d9e0cce59f58b491c1aa71572702580c50698b7895da5a3e135e2d7a423024aaf7ca9fa1490b24cddc0e507b86167f263e5cf811e1b086eee70a1da8813507b5a0842b5e38a8fce326bd784108ed6a15f8eb5454850ee1d5a255d5cefb00b5e7a72234919859216dcbf7764b37cf55a32db7f6a6231a9b4c02b7d9478d263aa4ddbec17be80ae16f8bebf5f897e9658d4d7debff96542c658de7725754c5823eb134829f9c0da5cbb9d2c2f7d5f80e06ea244ccb5a3bb789963589fa5bc947dea01e293b509473be1dfe3cd60277f28ed50a59ba77c0c297a28d9e84629d64c80e9b7cd8dac03b02ac31f44477532884c09b00be7de1b71ac0d2c33c2461bcfa848d4983c833663d383393c590b80e9c8d9a95853ab5783c55c9c8c4afafba0347a7d192498e7b38829ddf3b8af967fee0023afbed51f7a5c73008cb8185dd3351a53b62bbd62fc74b02948005829938a905225c98302e795aefd0c2160bd7d113181824a3b8bb4ca79a17108c11faf4be4bcc4963b6b280f107787126fe5ce523a487019daceda0ac19d0a6a982247e0668f07955033fd80f668381a07443e6d854dc63156722aa9d71c9a90824dad985af379e78812b7e0451f161351403e74ad4bf00067c7cb433478e438e06779f3d7a1e4a8d69c1c63c0590bf4ca3e18b9841e8ae086ba00822f9478139895d6ed92e9e2a905b7d2daad989f46def36eeb5a37e2eec5899755eed980d75a2fbf5a1ec6e20b64fffe8681d999cda81e790dd0650bbaebe074bb9e78dd9ed1d4f7ed8e078e4ea2cf3f5cb501ee9b49cce98601063ceab24b4883d670fd72f301ba80ed73332f0edfd2390ff702226192f95a702cd767276ff17a11b87a79bfc81bfca5d02bfbb893eac560d4150bd82989659a32737d06256323d8e61020adc76a3a741101b506a1d501416d1a4abc7b9108bafc12b004f93da056573d9993f7a2822cefcd1034608d60f3be8d22a0a6311121eea938d6e8eda267294070b23e8f64994aafb95edd4708936005ebf2ba4282e2060176d1be11558a6664ac90e4eb923ebd25aee87d0cc9f3a83ff73d935c7e72bb8e794e6025fd5bb33fbcb32b8934164120c8fcb28307face6c2afbdf80ac6a06f640a816ca84a31380a3b138f96debb920689701f19327ae48be359c9b27963eb0f45687fa95f7d888beb4836607bf022c9a71b9d649079a271dd7226be9762643c9730b0cbd1b6d041baa65a447f231f84432ae121000326a32e488a01ce3d051f2d4e8b23e53eb0b4d5cb81712760de61828c35058b8c9ae89eaa85f8293eed0b70104e3e9f0997e4e879232bd392902816b792bf96264f442cf5825da4a24d6cce9a453c8e5188fbf0d508c5d468874c374f7050d8e58726810c81f1ed0062d303d0bc289b5ae7986409d6c586e9331021e874e0afb0e915ab0c8403b0c9e022dd6e974b7b61a60933e2b0c859df4bd48e0839288408cd1e1276550810871c30bd053b50153ff758d83967fd7c1779b9bf0b829c30c98d5c71df838891845b1f4256bc45c2268a33e40408df62df428d7800e4dd0bb0ebe1bdb84c7471986c0549cc2c0e7580fa422decd6a2ff549fb350766b16e063da049a96209dec7134ac46add0c501a430358fbcc7ba82496ad61553a5c3639bc3560d734fb13f0eba8a6f1159dad44f5ecbc6e0501422e8c18c223d80514bdf5e29b1734313cbff05a0820bf9bd124602d479094885b834e18772341028abb2d6616a820739c1305d59fc53323b73b7b3887b2da9def9060bde6570e7f40564ff7e598516f3cd75557c0736d25924e958430a0519a643a183d598fa7c50ae46fa34989ea6fc4fd0b0e5e022ea0b7a585b93f871fe91f0181aa7b8dfdf48a7e78dad35508581b5f75b7bf79898aef7b8724b53680afdbcfa692b72daeb800667fdf778c3232151a33c7dceba9e904e33642b15026e027593b26de1663fd3134c53efd07a705a2a277b6d65845bc8df1b1b0c754631ecc3dc1f622c1f19608e17727da8ed45c7ad38259694776dd713bef6ae7fb8296455859cbd49fe76b64e62ecb46dc35b2015882b80bf432c04c5e97ec9183d0e94053ec7ec53a24c887bbf63b4a30f3ac740cca4f0c4edccf83663764d3dde5118177fdccb8fa922b500b99e0dde9e98efb900cf727b1b715094475dd1506f070f3ccbd5330c7839d0bc2b1355c6351ed1a4b80b8b4fd662f1abe4677a0d39a82504afd79087554074b98613db55a5161c4825d19e5a173b9486a4fe9722201f8ac91b2deb558a83d1560740d10cb71fe45cbb125f1536f657daab26c995ac93ecd5623d0c49c6c29417475c8b7d7da8a49de1ef82e5c50aa85e92e9fa25bd3b59490ec1f182f90220e6c9d3dda03e676f26ef384c027078964f11b3acb9edf188734ab8cdc889bf71abc4041a7fc64e733951f06b49b551777b7f481df974784aced2540054d8bc14ddd38b8a593657a1390701e14b6178848346e0d2431e3b9f0b83b51e02d49bb82aa8d0907e71362838655a8d1c0b798a4fd0858ed8623f4cc7847f758be0bd1a894b489a54176f34af19786657be77fde0a0f66e970f0ab6c323ed33b9a2bd58d8768f535aa686c97b5bd12db1acc955066e47cdbb8b0ee0ffc4b78cf4e1104824ce3ededa5e168f6f0275c41120fe2aa41c4d56795225e82e18437c9f2464bafda3a7acf8825ee5eaf8346b494ff932c0dbfa1873afcaaf0be3f2d304ae5ac84ac7fda4baf5d3cdd94c9fcec4ea83d1ad99600b89ba928719117223081699196c1f058f0e2f458a9bdc172edcc8d6d833ebc5cf929a0ed3937dab80888a2e19ad5d2c6dfac4d842a8fb5515d4e7e278ea572729b1d75d2b8cbe65bd5d20ca72c5e70116d4a919b0c42458a8d1c5c0db95c7147343cd1ce75ae1cf0ddda17484995281ef575a06558fcbcd8f8955b78cedf8f3ad053482da3ca8a8c4268c6388b257d160cad4bb8634a3be743f3403e6b797014b9327ccc02d405f9a2472dc05830a1471a264ea008c968296e2da4bc80fc303462614614dcfcbcd71ac0a1af1cfc79bc233a4ae3c15691501abdc18440c409000d811e89caaf57c4734b1cda101535ae6a08390cc2aa778a44403e75b276fba8b20949531fa481ff8a7176f0e4a8b527acebd2ebc2af8a935aeab85e3b27cbb2643d4aa8d5cf052add460e5d0d50248abd0a309f0bdf883f843ef7e7219a968e64f9d6f7b8cf3334acd044c63a5316e1d0ea89e1ea13c6c0faaefe7c03c4853750c8bf591e6448a6a3dd0734e7ea2652b0b321d516cc98fe5a042bf754a5d6ed54e28ef1de1e6588cc280b1760dfa4b92d322d025f4c6c0f7a1800b8c9a31a8dbf9dc732d59a5caedf273963ca7f5b83ed9bbc38fff45702159e04ad5a41d9980b3d6cfa40f4e6e73096b17843d47887dfdfa867cc0899b16f57b5dc173d407072095ba490be9601d19560e025affd6c3fd7c72b888ae28a0ec089ea787cf622e4038bfc30c9e3b14430b77d3f9e2e9955c9fc53bec34e6b2a7fd9b6ec69a7437cbd112e3a8c52e5115aa5dfe206b53b0407521bc8c62b86fca04e4960f144d7f2f24972c019d6a001e8f5f0884b4f3ea124aab54cbdc12d6d0e7fbe953293a6c1359f3d2593f794c3a2898ed250cacea02ec41ecf08312b999794fafba93070a0883a7b1524cc59fba3bb85a02942bd2e0d4160f1686b98815f8b96ffccd2b406f581661b70e921a0a2ac80c15ff39c9f133aa0204cec49142024cb765397103bf1f92989af2d67116e133ea8a8e86bd323b5c256584661fe1f366d40e3aa20be536254ce39ae4e65f812459779a91ea84a1dc9f0d58533d205820462ad75f484d43151de8b765bb8fb060fd183739f834c8aa4387e483075a3c89f4d8fce0e7a163d0f7bcc9b276611b57122ade7f5a03916a861be49a04068093a094e11deeb2fa00b21795218eb897cf0e59b555f738c92299d7146995ea78b274a69353f632d5c31c39e4cdbdf79a91761bdf08305938617d5efc49f755aa0e6b5c6bf3223c9f9058b33fdf2a88479e96428419e484bfd46a8a741f31cfc7d4d521c11a9a1c680b1bff22b306173095c6d3224274dd22db0e4fd467f9990fff220b758e21b4f406f9acb6eac1c993250de2d35cda3156d71ebb25ef04476e3e0cbea9882590744c3be8cca0ba5146fbe7f3dcef9d7a74ecb14884158704ed2aeff71871939e9874a5c6606af1f80cf558c1857dc4855de7460d4511126c922215c3e742340b31d1b5c3b75c848a71d71e6f411274e3b1e0bbc306801ccafa0c24e30aeeff05bbf99de67a47cb6f9dd2a471819902dbbe4e13f2b700ec818e7d5cfdfb759c0909b0ffb1105e3475cc00d382ed67a2a9321e5eda935e9960ccbc2da161f65fcc009e8efd76dda3fb759ce9ca624b9a51bc1d25470eaec57cd68bdc9e40406221871d87eefc7e074c5b064de1f97a7f874603671b74156e2e9e2b8416234bfabb8b9b1498df26dda1995549ecaa2d6b47e406daf69f5c8824bd11d2bb6581f3f7012b74ee68ea0624c3ef044597e1bbc7bfd047c087376b1bb49a575e6b4ed52531f1ef9d8671e77c8c6f500a0365ddf2ec8f8e58d0177d8d8a52832298678e6f27781d35a3c4fd4c87eb124863e8f644a01a630ffcd5c45a137d57a14fe3a1e2f87d2f10cec055a5753c351b3355520821ae8e90144e65b3d4410b5046da3d54800aed416770de131bea0df5d0e6fa1abac86944d5bcef9a9061f5dc82757e998fdefb399ec5584834dfc4ba4f47db06991bd1c7ea8452443fbd12c9c65979f66093a5ccbe708e1a5498a1722619a93cca0004c355b70edb15d1f5b3a601aa6b8d647cdb147a034da108e24448ff708a5d34b979327258edeeadead7b2edc619199863ed14db7b5b4a7a54adb74f45a1988762a2e1098d81277134d7bae71e526b5473ed7a8729ddb5d0ea3d3a20a57b13c4825aa64ca96e0909c2384d7b0f30fb3de700afb1887675accb6e401711fba90632520fb680e5f28c518b2784dda01017dc01475a245e3560d324047758ea20dadcbe1fa590b641d3051697069985b00ac1cf8a5c1ff7ac920093ab8606e52264cbf63d7e8c034e32699455cf8d1a2ba5a7f80f77c3795940d8de31759c66af7c333103b7fce6182eabd3fe7f051c0a515c513bc87a5ab8b292a6d7b350eabaae8b4584e6d1dc67d85f9bc0c6e912f69209ecf93291272b659e2069d0354c36d4b744bf9d744e05aa98d94d40ef903cf4a33e4751b66cc238a9fba76e1bf2b136819a3be28e6fc16d8293d73b75ddc691a393f7793154d550d2ef70c8c99cf9de43c1fb58b1770835e819d8c590f9c34995e818d4734319dee4a18cbc9a4372002663e275d4283733c6ad80f69361f768fbd9cd1805e454074d487ebd9e398116f14f22810f94f826c01b903e9b463ae9d050e2a6cc801c4ef9e3d7afa8d174f172e44914d8208699d85f0fd77efdf3d7b849e922f52f24d5238bfa59e9be8e411b3cd0ed770f34fa8ee9d7e318632d1761fc03287710e6f86415989ed8e6857b82f81597b2917434d1a72f0423ce20afd758085b152d02e07aba0998e7c9a75993e3701319e70ba52ee49d07e65bb3491c9eb96cd1247779441ad1284a30be8b7a162188d97628898a318ad09485f419f44a2119d7aa6bb5c63b4885cfc65dc284474847c8954964f07f8db960545d6ed93fe8aa96dec8cdbb208c8963dce210dffa58f45a63923feef227c4f48659b27a2452ca04573f369efc7cfaa39e90f7bbc926617be5839d9aec0b85b0765f527ffa39deb652860e5a4a53ab64d6cee1dfd345a0cf0f7f7a54a120d6e3278d78ee1145c456ec76d58f454525baea32bcf14952008939d338160179510ef2ed0324525a2470f499733857cbb5132c3bede02842e5b5347186e8f5b13c3aee74af83818643eb4c23e7f10ee10696b90a07df12444af27d75765c52dfe9211869af2c1dbf0ad9bd768a31c9c3fcdbcc3c39d54a0b24408969c8ba15bc31398af27180bfdd0e47009e8a1a553623bf67d2f281a428e1ee9a70fbb98cbf234b4a0814be188d57bdf432380d3a6a6b1a800cda084184ef999d3843bd8fe96f4ccd7042670597696c382cdffff90137bd36d7bf6cef744ac335c5dd75ccc5250a954a1b600200a758f655bcfca7ca6a2448c340b242dddfbcc5b02ac6c7b62280067d49712319607c255c78b4466610e790eea16ebaf9765fa21c5a198e64260112c05ba9382fcb9573bf730504a0b59cc16f914fb8c640d9009bc94dd1128b260184970ad6d20af4476c883a557250abef836ea1a5820ff10456a84b29c24ca66aecb31dabc89b2d98c4dd50cc571101795fbe9677c2eadbe349b1fd22ad5ba6e6490e96934886376c79567291ccc13c10020c25811ea15691655a0417566a5c163dc6c60336e6cb08ca53e903ee50bf82edd7d5508169b2cc2fb2d9e9e79504455bce4ffa821b3bfa8c1f31faba1f9dfc81875f7b2f2f97fee01ef3f56f6e85c3cb428310494dea4454f03a1f182da2387a597878ae59f070b0b640f124be61e2d7b6459dac9288f438ab90b7f22cd750f74209e10d773bc2495909d36a13d4d4b6a3d5f96f83d9096a87b722d757b542efdf670a1aacfbc40b7f724b207ed9b0edaece34e7d1c4d651552af72881d9803e3f30b266654b461d6259f36125db2a50da697d4b561f8259f3652a5b6db06d3efbd6d24452f97b4ff27e13243ef683da48c43c1cc9b64b5a176d97da9fb86f02816b329924411489c501812d3a8ad606d0146113ecb42bc8b919cbaddae0c66948909a629a246d0683b65dda31d4605f79413619c505c757c809ac4c98c7780d25b9b032f0e313789efa0a4d4e017b329524018d15b9be4dedc4edbd35e8e6568bd34cb99bd4dcbcabd3f9699bd302e137b732c37f56a2c7ff6722c43eea558ceecede84e60ef233ae2c732a8f6c9f92658b9894c8f9099a9b60eeeb0c336dc80f19d7d5f01ff46d5f07393d53373b7ab2b717fa933e5865227e6ce566fcaad56ffc4cdaa0e999ba49e39775b5d99fb4b9d393794c7666ece708a19db4b663659197d7765ccb0571031c520420edda58622d7dec7e859ee4324e8dd874dd0741f3a41cb3edc04cdfb3009baefc32668dd8749d07a1f2641eb3ecc04adfb30095aedc32468dd8771da6509320ea2b50a79f4e6ce337e5499f81713949d4e2aa5969b2ed1e457350cb309e37bf76a8b26acd7e08d4250436914111a5a5020d44082e252c352148e1a84a2286888124553031214c41aaea258d4e08242b0861214911a5aa240d440f2e4510d5ae2ee01b89ff6b6d23e1967470a5b710389bd8a5da7bd22d105c84b6852c3703faab5d09075c08e60a65707108e6cf751226990044e47cae72df795af1f7e3ea1c14bb32978fb2d28a18b0c6a47d24407e86db92a3f3655e1bd88594e436c355481d09f19fba94f2da6039ececa6a2ba1db369304359350a72516ec7cecded51b1f4064ba791aa75bffc0da11fee13708c8b8fe9057483cee7b22cba86e228915ebb46bd2143a590672c82bf340cb89463f793628d9af50fcb3ccf3a6f0b8bada356f807fbe0afeba3c173428774d5b014a5a52d0ba091adaf7b38b308133229f6ab544fb40af14949c3dba11da660d92e10782c84df32ecab758743b1629114645abbf4998a1d0126ffcc3a1d0a04dc094fc10e31d7d7de0fc53c855d810ac644a513cfedad39bb61fa8218009f4549a1f1920122cb3b891ded681030a6e5060c54291c8a20af0068ff9f402097710e4155177efdb30b243496212cd5878d58650fef871238488f6cbdd69070db7f4f8a6dfe1baeea6ecd48060088d15101c0f9d5c2b175389f6f2d540243d884f0d4c47e0373273a72034ec36ae693fb5837a6a3bc959b85c6f026966187a10da300f610d99c0c3e17fb340959320be4a683ed3b530f8496ba55a0a18c527dbd8f347bb0e23647156a450f3431d6b8d4bde9ad9c55d0004012cc62418eb10e39509e6f77f6afa0c86d96fa2c3c27523d82fc5c040b2cf6a34bc06d9c26513a23dd1771a42dacab4a0c038d99f61c00b575eaa0ec8dbe42a773942d82a923816423a58af41b65d0d9528b05dd75c7540561e1611825c33ea0179594483e07d108218e109a7d31460277728bfb887a89155ea5ab6e5ba8f8a83c5a70a6d376ef4bb60dbe55bd971c4df31b61d48189dfdcb5870631e3257e67b5248517ace70b1e4b42253518224f0a8558b7719c4c91b9f837b10637c4cae21c6f8807fee5fb803fa7bfffed9aff4fb7dfddd5fe8f75efcbfdff41f066e8a505cb01b44e6242e3fd2aa67ce67fb8262fc0fd4ca1be1a153cc366ad50a9a5bf58296ab5ed0ecaaf7bfbdeaf5632ad5c303e73572c07558b9880016a73859ad0daa24dc943d1d3b0730078a86e85d311e69d769b1a321ed96873ac9775ba1240f040c27744670d94e8209d3049053f4c9a2850a4137916854b56a84aca506bca0d115e44185048d4dfb3b3d445ff8ba469a36cdbea834cb51a259105e39b72c1132aa7dcc519557243af3b5427e3df49070421cc0fb86d0a65a0059a4a4bfb360f8f49aea084dd5ca39254d8d255aa69a030bf3321dc609fb0966247802d67e10b5461567ff8980bfc97c831d6aad5181183f71c0ed3381b7e2d0b461b383b07dba7e8d2bc92670f843ef8d20f3bd74163e3aebd215d8c890036988238459bfc3263b6c3b889aac2a461f0b643f3cdbb402773be3f5a879120d2393a1c06c8b5a8ff8884d14206f8bb414c7f7bb5b3cf29f8472ec94409bb3e3083f64efc0f9e6d220114a6a02770391b31b86f320afa6d3d819bc6660a35281fc9d170d061a10796ad6000a8c8963684011c2a4395464856a656eef0193c3b662709eeed769bd3e60741b0ec67a837b4feb9f8017f097510e63f42b76fb0a96e306affe1b5299b0e38f9683ff51bd6f4144f551c2889b1838dc22db9a3b1a742e75ee1500231fd565b5adb1efc7afc6600cabdf84713377be876af28f2ccdf3cc6ef971b3ad034709c6ff8b30f1ff76bfc6b81541088d6f53d1f24d25a98f339704038d51be8c4e0ec2745057192a9c444530b38ba8d57ce150d15774a8c0654cf19debe0aaa829a6a8427bbea12b4f1a25b2c63a94b5fe5c9429ec51a617e22b3c2305c05f5104ad034746582c42dccdd1ac5d6ce523a2b383d4e98a83ec19aecd590f1c6097064cf7f0a8bf08d1f78e55b0fa5250c99ea43407d706431cb42248e4122686fc1f674860fd0765139f1526f451b415653c9dc1d2cd5f9fabb4bf2a0d44aea6bf4efd13f89726d653f60e3bfde2c92a1b871196f7f997e85a93fe8ec948d494be0597f2c6a873f780c8ef3c8238380acb747b462003e6463410a1fca9ca27503af986504a7365ea451c3588a5f7fa23c4199a0b1e6661587d821320eb323998176026156a3a0dd04495acf90425724b9c67f441b9413a1b02d145921929d5054711fe604cca4fa26084ead2912aecc4c16a261fabad8747859d19451036ae8d8be5fa1f479d40051d1144f6be80826c7846648a34df0af6f61674cb0d56adce67d8eff71f9ffa18e951bbdf9dfe66d671a0aec902c08e8fe2297c8edf6b823ef1010f7f71a44f2318612539b58ac033df658c0fc3b2747cac106a417305a37baff069cafe733c1a19067707713a6d543abadbed259b5f74247702e053801dca07add35e77ed4f4491c5624ad0abdc05c4d8641e84ef293327c875ab65653d8c81443a571f8e5f74e5a2f240996c441eb6567d64349392e7bfd5e513453d76eee12ea57dda92c2bb35c99f8f44a18ffe04e5aa8be4fa10d8558738bfb1ad28bc5c357cedb7e067861f186dbbb3a245960965927901f5c23f9004a2e27005ecb7c3b9352d4cb0c0b4d2d630d8e32371a64377dbd2af7900d45ecc30950e2643c76ccdb546daec8e6223e2bc9bf8b5425b0a2d24ad75e055472234cb101a81e7180dead1278b1e09abf3c7355aa4d5c0d16e97e81a711646c6975c6c3efbe6e8687a5a20304037f3e9cebda2d2978bd3028f7bcaa1d09e3889a49472e7655bd50428c67731fd43fc2443e0fe4063a48b880b9b75015dc58455fbeb2f81ba2923730cea811425dbd4dff6cb20e76c33e8e4f528506721fffde6ba9dad16a32d3949b4ef1930f8bedc94df49b7c002e4285cc5c8124656ef9021ebe6b9018aa1a7481daef874c19e7d710e52d1640a4809b3490562c75feba6ead42d17c6d2e1fef5aaff8b7d012ca1aa424838d4280e5052d2ff93cc1a20b2394cc889305e324ce2b5c643a06051cc679cbfd0811e503197350f38ded07eadd56cbcc0f9d222a21f14be5741b8ec479bd388ccc517c4d42908d3c398cf37b2ac243a6c78ade57ea7730c51b30273e2e5e637363ec74567635c6056609a3c6b59eecbede479deecd0b2a7a4655f73e3569f2ed324eba9e1ff1520a2b4905af6c0af514daab6ac69232e5637be5091385079cdc2248c063ca1b985149239f1433b06081381f11b81b3a968d90775d6f3e55442a4b8f3f3184fd181e4e6762f0afde111dd22080814703b1e8886fffda8de78dbd5eeba706bb1ed45803c233e05121f04a613dcac7ba7ee2b34ac59a2ca9f439ff06f7fd158f84f8d508a3d4aaca7505c7bd07b54ecaee5bc708619bfaa0ed09709df31c6c4f21310f11bdb3f2bb89df2ba91ee7f7d425116290b19ef5cc3cdf56404b46951c0c8874c678b38986c88b2fb6b2973c13a57269323f5ec79f876612d4b58fb91c44586ac06cedbf7ae14b67fb009cdb3c5318fb90adfbd6c6c48765b0b774cb05656b1ec65762fb6d728b2aad69ad00bc8a0f692348357eb87530467343bffeb1284262a3fb3dff9a8b075fada0b76e05065b0307f14a765f79af62ea7ddb916e23d76c7dee007dca04eb8d8187516c2ffe7f505d3f5abefb8b68c124288937dadb9930f77d395be797c2a4a7e60ab9aced9cc3d4976c77f1682e847b27571f284d8f3a514482b3f37b000b07950c45955f900f31d95c937b99bab8ca47d1e7c83b0bc459f11a19f74fe2c695e72bc8ea395cedac4b38abd2ff977eaf906c0c43eeae22a93032a7870d01e19bfafae85ffa9a0f3e069bc0374bbbe968176173b38e4c5383a35d80e4e863500e21905f53875dd4d78a0a5167e30fe1b87d34b058bcc55e4e22c83fe4f84cac685c81efa79ecae4a310b5946793fd85ad07c983daf78b0522b20105b88e251a85dc8a7348328f4ee44775e3e901eba5928c2e4a900c02be691f12fc17f014a3020f68370e63c242f120f822210281406693e1e897e47446785755bc4c3899b5f5220fb96a21742b45ff1c207c99369b21494b365a757307b070a11cbf3123ecb1bbb242996e907319e05e397b96caa6efbb7658ba41e4135c5f9962f7e0422be3ebe65e6d6cf69234bc15fa008f0780137fab22d3b01f08091f96e154e218c8c7c25fecf1851bcaa7a87a93572ee23fd5c07eb0bee3eb4243ec8d5d3fcfa0ac38931664682b7506be24d5f7927abb13d5220ada0198536889fa4369e97f1b60dea267dc127b17238b89b29de63cf74d19bdeab661fc9728796dd58b981f0f37a98124019be0afeea881ce2a58da5780f662531b30dd0e44035eff384e77c3c53d7204712b82224b5cd96cd69c6e05b61f8a8421f006741af436c36b573e0c32d62067db26a0edbd89616698bd4a2d7a8626f15dc670aaf110701e6f24d03d90aac01b7ee0ce42f957f7fc73b9c40d290d5dc885a1e1b911b9211398e8be81a3454f1994a26a89ea276f884bba7e8290b0bdf5ceff1eb39e70b749210340053aed762ececa2763938c8e3e1624aa79e9a388a5078631b4bf7b1d71b7ff492b03879d24f7d4efe48aced9423421656f270c5dc9b4587fce4a1197487ab5b92b7eb079cdbde043c6b60ce5c3480b4168ea0b335c2e32aebd5c4adfa95ccdb8796f854e097f0abeb4cd2a85d0be6ce5463bb14de77c2eb2d30cc17ea507d6a066e8727d2133c62a286376bbfeb5be8f6d4fe8adc44371685ebf9d843edde3cadb992ad9eae20272ec18fff90f44a602c13a64218756e0affc3ec8919d70f11b86eecadd3e481b5334de453a4ed9ba318a7df66a3366e18e16ffc6386452e0f1cfbe6f61e41b5b6423fb1cd40ae5a884dd68c2e396fc5041cc957f4827e8b796268b7c97c25f80ba7c6f8eb8906c240a52162402c6669a62f6953c77525d241e13a0cfc42e8e9107cddd492b08a452e9845a16eaaa942df3428150915e8e62a0dca2a6af6b7cb3cf2fee2a267cb831de31f1357f1ca9263a3cd0c05c87264e20f192dd59412abdff08bd5bfe8dad4ab0674a81cb631a1a202309ffcee12bd58ed3ab9c01585c3b20eb9aed7ebebae70c6326ed8eb19c1b05d48de221504784f30282bfb3a52e7d0c0ca25601abf79eabe090855f26880bc8b7f1d77c6514ac1f09ab2d29321de96c570d726f6467cde180f895ec3c2fbeae36680c66c08d053d7816d484578de03d64c22457b53b8cfbfa40fab9ad1cfd93a8343d7db99529b5cece2e0853bc7f0c20f348797e8f3bb208f2949e64bd788c9626880e8de2e21d092e906cf31e2bf756d33f27d8cbbf2a0a06916ccac5118b3cf9a15dc265f61f874637a17b3f026abb91651d02e2f74be731cc86d4d32e174bf2bbd84c31a6ae5d3f7e9a129d24422e2b61611f852a505706abe7f21893fd5bfb0c0f606260a06871796246bff54b9b1fb4bf57cfc8cd6d72ff3bf9b31471c84c1a51be4774e5c0a17911e8c35a3f4d281a061e94bb5a75df2b9e1f0f21b6bcb0b38a37a3ef32e549b74181e32230c950bb842061fcd8f6ece4b4178ff27b8b8c17da1eb987ac6c1ac84bb41afa445190652c6d8e21457b8677813399d59d40c9d5e4038edd0eb97992cfd3607ec75d1b5b2ff97ae76d93a041a598b9b42ae5e2a840a72058ab80e854af4e2ecdda6387520473db9c345ba22eaa93d33d6991ffbfc996ee939d17cde47d2d4b73b4d10ad6dc0762b0b626a8a411358f362e89a942c7a8b466cc21770b5061bcf26faa3811df30592b8194850377900d4da4dba5196c6eccab2cb091bf4551eca27df5afccb0a955b5f43ae71df731ec3b02c93ab27a000c7304cb3011bdf2db43bf375f1910d58b6c44e270423b5e480160b8301e05b28c0e2404d403636e0114aef7181ca5dbf17a0e274f34609e59fb6b023a50b969862209d4a0e0d1021305ecb60a13fbb58ce2f81306dd7ab2181409975458129d875694ae7a13c1a35495fc76647f80b310acbb78c4ede3c2812947472f011606919b6ba04dec376b3e734a07b506804377c7ea4f6120c7d851690fbd561c01ae76b8b79e1935c122910b263ec61be06c75bc2c7f611ec1508971615c22641ef0e0e3ae003ddd25a4ccfed87b82815fae19da3573c813479c6997212fe2b215be94be2921a73dec66bb97f25b92510bffa04c25a85bae883704c91cbc82ab3fccda12732d94a36b29823ca4746affa834bc7be26afc809abeb9269b5493fa9e9fd27794d1711a85adc3a93871591dac2ce4cc39a8332dab7290bd7423cbb63678dcf2dd2ecc72f243ec9489834080d2c2ee06f4ea5c20d69b9afee922fe2ef3e4c99e80fa54785d4d32990bee9051f460fe02dda20af7b70bfca7a1e3c1b5c42d2e509961a102745eacb08e8b3019e79dcc019444bd2042d4cef10f1bbc665ee2164051a4adf0d4a278b97dc50d69d387b4fe0a015a2d8d3b95492bbf54b10400af950c9cc5b7910ebaad51e09a0129f5183660db0f579242e6ab4d0e70e98d0590b427ba1969f05e949c2d28061351bf832e63c134bd2945dadf3e36823faa5f19bcce6b8ddb8af81b643b892594f0a4c35ed9b77383083ee8e06ad155ff61359a939f6dc39ad6af842b56b4304c562089a1e5f2b32d21a8a6cbe96cab661da67ce0baed4719fec895af4179814f0741b893bfdc21d275a4ed8a4d0af98d641ff746396e0d669c92fa6ec5094db8a3b10ac697fe3ab8aac1d4318cfa36a0f56e91f6de0579aee033f77e9a757d7284368ce5e8b374a08540c8bfa08d55ca6fa036d8c30ff3d6d8dda98c2b4e9742c68f1ef89c5935b0b930791aa39bc756cb2139b6114d856d9ac9cfa940abbd0abf6d959b56c169cd211c2b1cc72a89f43fb52b0e728ccb64d4b6b4f42e9d600dc0f8542ec300258f2275afd6c32860ea73f82f1d77504183906a9801372d70f56fc6700295b99a83e4cbfa524045a33896df7015237377d9748a0341e5a5faecbdd0d9f902f429913836eddcbeb276ebebbb1d2223beba63e69fe30298f387d87e1f04755200ac8cbc93529bef2bd80962a25ae865e4bafa033bf29b0924957812c228984c2738812d203f65af8ce20c281e940e9c9f0c6b99c4906ff397173ea1f815790e3a55088e78d3fd82f3f7638e30cde40384373c1e02477d9579bd6de1993560dae29e0b12e0624d3639217ed873c6926b3460688d428fad0aeb687db9fcc3b36af336f75806f920060b7c3c01ed9e76b43a67d31330f578187e684b9a7cb0773178070528f9e1be1f3c36a96f3812050cf491a1e414efda1f46dd1c4da13b7dc1614a627d27366d30dfc5e01a52f3212eb208398396a4818d774364bb814be91fd7e27f36f2ed61cbb6f81bae58dfe61a7b19a29978c79f9f1fa23c3da26a9124c81bd8a6e7ee09a5e1f4e6056c1ff6bb2a9131d0e2e5edf6034a59d4dc23beb14d673b55ce77618ad1e91b88a8ce0955291f773ee8be47fca68f44c64612a62db627296d50f74445b47222151142e0235701d166872d863298bad5d9a9bba068a030bedc470fb834cedff4dcca543f36e84515722dd1cdaec754a591e2b2ffe70c334d0c4e12e10f0fe9fbae674b9e743ed0f59d674a45b17c44de1149d4a0078062924580ef266394d70d4f5d436248bd5c5c9156342b9fdf0496988349aea451d356ecc639f8f2322aa0c354933e1aace38c4602b6419d424e27e873cd401f4d74bce890a33e6b831260c899a1be34f709f43f68d58796b80e3fc8d3970d8a29ea3d70353f550561faa2a76315adf09ee7f9414669e1ac9f87261dfdaa4b5ceaef76f9d34f9264711033aaec27ac1e5998e95ba38da9be1f83736f0b9057d0f6477ffff38ab0b9daee2b907688bf35681ab33372cd4431464f335ffd6039d1582957816b91348c0a66623b9422795d797eb7a087edce1b35e0debaa8ab2d53665c8e43880bf0fdeb1a0b38273e5bdfb6c16d52ebcf28db601d146b4bbee54857bec63d10fc2bcb486e7694eeacca8d91307cfbec3935d2d762316c5616d02c1f91553520266aa56949ce2487dd2617ac020d83e9b47f5d677cd0a455a24caaa1244126eee3d6c6ab6d94608dc5d0675658c3861348aac0f46deed4d0e19d1981ecea73f28e2854c62f5f7f5078869f3e18f7e77b2be0843935d50be495e9b5635978c77aade571ee4aac066895332300435e00e79418362cd2cb03f4164a8c1a348b80deec28c1553435e97aeea1a4cd3c0662108b32aeeec32ff109f58914e20c1b742c715e1b50b8b47aee944b64b11a691454887dd9a2316e7f7cdf38d195a2e91aaf97a46fbc29efb5dcbc63c89ffb5143ab8d4b243470a158b07850bf1084295148b422e024817830aa615c8874c208a8537a24385054725d462bfddedb83ccb227cf4b34287970257f545f3d34148e8e6c26fff538458b91ecbfd59229f178a2b395d68da5cf4a30ce4452698f0135306e7d7fcc0348ccaad10b25df686bdc8d131c46309012e115894744663756279538b17891640d460680f95cb89a5ad0434e13905bcf9da7a6fafe672301afef0c3b72d67aedb8ed8f147e68a164c60c4b65906765e564714f0b0d534ee1f1f91da6dfbe05f3109670b76be344f044cc2690898f25de1183e8340e5bd5c2a27a4b585e32b428d90a675a67cf610fe5761228b8e6c029961b36b64a840960ea1c3e9529cb1d6ade5cae23f6bbd671546d28304979c51f7aaac25528ddac359a11f8cd632758f401bfc44fcc19e76ba8832bb11ac80458cd8a6433b88821cbc80374b057dbcf4607d0072504c14434cf61de7feb2515dd597999f9270227dd9d7c9c242e9d5d8436a46d2195accd86d90c28ec236f44864c1363b734f6570ad865fd5edf34f4ffd3e6493b50b84509bff453382097ccf9ada958380befe2966463f86ce83dec5886e8fed484b62352fe2754bb457f411da78891766bcd7832f9eaeac14e4dd693b8c92b34ad0d821e81573b7bbff2205901092429cb9e2d8c841b730abe05b9d32a3f4081928fdab4395960d8a26e70b81d73030ef2a1399f708cc4663780716cc78ab8e56414b0f1139611735caaf157a9f7829f4595c4bcf35fcc61d67507d08f0aca0028e4f508b4b052737bdbf46a0f70a2d9e43d2b24b07dcc6f3052a2470da80d2c50d72204dca81f9246d4034838aeaa262608096de97dfdf00d491f0e8be274f02ca624a3fa3f5fb46af1ea3aa57e5f70ac1d478a4ab7e0f84460013ea1ba3d42517f185f1c7c854681c4218ca810ce011c30d5d205054c58812d1ac946d38671a00b3ff3ed8ad2be3f4f981f8c59712f14b8dcdef20d26d8082b7f139935e9cde131c350ef97b1f3c29f459ac456f6b543bb94b1407cb50d0ae19f0ee428f79935c73999730e15164627a23e8b7fbbc123ecc78bd505090e9a20c2f485f0cb8e074e16edebb753b9c41b74366539b9617f1a0753581eeb44402e93c5cb01ee8215da3f4eabcafc2c639ba18e55f192b29a24f8011a54716beb885ab10a79caa3bb75a690bedad7b9f832fdd5f112d579296fef347fb487e506d2d835fe05988130b3c7ab8058f735360287bee1775848e5400b473d8dee5e5e49e088065727d72673f788ad2d63905209c9c90d5d7970b96494a2733abe136454a48722ed19f392a6caefbe082c109a568ec110cf146020410e6ae3584444cb4d59754ae8b4d22d52c3bf7f2ec907dcda65e4ada081231df4562a7032f2c4d964d10b8a3287c615e89151649e08b97008e464a0867e15286277cb9492b71abf136ff0497ce8808a8c8cbf1e52f8a856dab61e56c1755937951ec7c70dea12eeb5ae848384457212c317d46c942f377f0463b30043b41f0a0f67bb12f4c0807f0c304ea2005dffad0057602fa7c633656d18bf681bda8ae2f7ae73b336c2d034719f0692398353aff7d16ca0671c61d7f5adbc448a1cca51f4de6d571c4751706094f1be1602be5664f6eaa6372071c8ccea8de6a2f06ab83c2273be27727082661878944d76228ea601c1531a01807d7c2f9298f0fa4eb8a4a2f0fa78b91ca7e0a9210dcce16249532a4630746383c072e303df59aa34a019e25af6f2dac4809a77d6f90a29ccfb9f94ca4e3d39473035e61594a49bc296d3ae5b1a804a7323a972a1552b56aa68f12684519f02be794d06c5e9849dd3f5150beac987d80d2572ba1103230c1030ce349618d8519ca772d9921d12a89f2c83103af0fc8b5d189dd90841df9c911ab72d28319cad9b544edabe35886309e107ccc09e96e3284e0c87948b598caa90b022fef9b03726c59b7f23e02bd34ebcc9a185d623f599527957a4846003dda1bbdaba360687323df9d5e9f0a10c911d45c4fb8bdf0e8557fdfb45323174f49bb33036a7046e8deb57e602cf8b890657da8ae5c1cf97c763bfcebb5e62307de5ec47f0a7c307c61702dff064907854ca69a55493da7273bb08222b17eaaee7fda7c0a18644d84505de606da2be649464765d77d111159e96a8c44d66b4d59bf162a1aaecaa8c464fea76cbb117b55a71c378cbdb38edd1e8ca58e00d3c8c97bdd8f12dbf147c13c01a4c9000e26788631d62d3b736036882f57a397a5ee2ebb0a9946b6aaaed0f56182d795941216a947afe632b3848765c00cb0cab8aa3f7797978d3bb54b088a4058bbfbb660377da40625952210c0e8083d1623402060ebdf8dc697ef93118f15190f36c911d9088a78ddb18ef7b6a353d16bfb28952f3df6a520abaf632a00a5115ac909586741495e172c1862cb9af65bd2472be1ad2c5a96093ad05be1d113c4ee6ab50e50d766d3a1021a790b9d20a1b40210d2c9875b8ba95b5b8e1ec3e2bc85d7c5e11ed456d4303b49408a6ed7d5844ddfc2185ed7bba8a265c63e47992ca6fb0e597721f44e1eecc1100b11e6f0b96ea0e4d7a8c35d79e865a32de001606555fe5c25aab513fe434779afb16190168038c20cc496e114c7987343d8adc9976bfb2e689e1e3da4af10f6b6c7f726a9b9abc9339e988169831dbdb1a52af73cf6cc7a83b2f18f6306bcafacb21fa1934c92eeb94f26e9b06b128ceee241f294056a7160f6273c071ad600cf1ad829f164590cd507011264bb0741676e4008d28fece5e0a8f59448e4b35a8b735c1ea2880333f07033d8815bf022e2285b0ca525ce3acac512b4dd1353e1cd97140886b1d48d03a6ca31c2d0b83bf98008c8ddbbd66a6bb192ec0689ed92c93b35f246f69e8e946e6af5e81ce52af6a9c2590a2790e46f6ccf38456a017aca453e93560f09b9981c2e422e2c834864f3545b026987fa5a01d729ba98fe84c0316946fcc4371653dd99f2e560ae0b4bc60162412b0bf3975987ba294b59e2877ece0b71b6c448dc2d99e1f856e1cb4b84699ec1311cea4d29a2e3824bb703480da5c08efee30408bfd6b9272481a39caed7c82402eb3988a6893a3f5f8576a84946a8e2f3600f31f7f2c9fc27aa4a70b6bba8f6609764bb2ae05e43411b34f4ae1e665084258e4c0904698c2d6b9da455f2b3a68c876c6ac722e7cfb22d658f26fe74a9b438d48f51f4529e02b8178f6457c832f4e8ecd3a7c762037552a5ee8b7fca1dee72be675431a99a289668201f6dcd4b37bbce033a3c59e5e115ca00f7702a86df9b5b27915122a7357b4a16bae0ccc855103271dc4dcce6112965d42de6ada72f51909a08351900146c317ee13b27d8bcadc42a860b1b0472ddb272afcdb71e89485044019a3ec47b3ca960463d651eac263959f01380f8f919e2437872c0759b8e5c09264e0dac8e027e662c2abf75c1b1940c21e84e87a754d703713872cf1699547109aec7dcfc3238ff05873f96c1554eaefd0bbe377a29e80de5b85d83700e540b3b9d3ce0b024e41104ba6537aa5820da19a703cdaa62056cecf1c67c309c701df078e6b31026ed56e5e75da84c77bcc89632450232d54fd155fb03b19508b05002a310906a7321517d409d21fef5df3695bbdf86bd0ea43c5562c30411d9ddb6dc52a694640a3f0a930a7a0a49baab77559830c7a7c6777777ff23dbc3af9478632d47f7ec6c867d0c2141fd7d521f305e89359e34a89f2251047586342258a1c41caa2e939821478d31ce58718253da345236ad60819af6464ec125205c6567da9a6823679ae60f3346ee1760de63188691e6d2442a1f46397f985e029991b46dc70b368d944dea038aa0a0f5b943c690109eb08365825dc7d102a232658b9cf1de8a5c2959ca28658c314a12c827121dc3c14475d62d7fb617836c624448d8633f4c4c462c462931a90452d65fdab4171489667452d77b3e856ac618e35c4f6e552aa191f387b9bdb0756d240ff5fbcee380f5e2a3e20ba5336872386b2576d1054432c618a39452f20aba075ac728a594524a19638c2d85849146d05926d823c5467bea07817888c5253e020285fd3c94f1100f4d3efab9d19e192c7105f6386b0510c208218430ce28230ce6466512ccd2a13a91214bb22c215a32549d159fb92f0956fd21843470f5603761a8773295793217c166009b4c5af4d87fb0496d9e73a7a8a87987818ea8e4ba39bc3337ea10c3a28c58ef6059e0cb5f722ea58cceeeebec316e841c9296d25f3e74a34590c51e93f882b9517e652a7882c5abbe22131cfabb7b978461779e654a39290a4b02859a1b151a827232544a29e5bb9498942e4991247f3d967a786c1913844390ec52c2b9514a29254c179bf5c984978c31c618a5941247062ed7faccd15bc818638c314a29a5143782a1b880a3ec22482388e2db8821cd1831677c4c76362f6463e3332c903354df81a33acee6f3bff9f25ff3d001eae8201e1c1d280cf62f5926dd3413a9bd1c1bd4cdac0659616b6bfa4b2468ed30b922b2ca8e48890414230a7a7102f44550955c34fc7e6805917b9c3366934d36516cdc666e34c610b988d18f986a8c928a18e3a909378521a5134fc5fffa6408598b17f8dc286b21e4594c76648c31c618a39452da3cf119a44c22e7d87d85aacb44af46e832665294bde8eeee32b6df444d34708da62db0eedb24accf199dd0969312ca871042093b1a21a53db06c73229b2fe3cbc766b7af611b0efed14b3db0c6a45319d316a94829638cac448c3e027667c87d53e3016a1c4069b7fdba1a3f2e25bfe66103d4d11f9da06c803ca00ed4d16f32c1bc984c5b47eae6954a7ac9f97af17968d55c9ad86f823a7352ba03777a073939f84a7ad13a6e6be277a4eebb40f587ebc507c2258645efe0cf0b9c4570f9a432533024647a073b14935366922435b94919a3141a236f949a4cff9e874205712224b4519b1d2fc28c9874f7f7624449ac0b82094921a1fe190fe551b0852b7f4eefedbf444a229f40a1a6a99238b842f59c2b01d86555ea0cb03451013568d30038f842264d28312c4a8ce314f772b06a7bf79f476c9acc49229988b4eccbcb71c4c663cf0676d99e4b3f622a94d6afb66e07a6e92b70bf34de90e9930336afc9d2073dd18b5adc622946295b40a564e7cd64faf73c14cac66688cfd090f7cc209baf7dc47ebd38434926ca038d5ee4a6324bffa2c52998905e5ed3344eaee2ca575a6f1ad72bcd378de39526378d83ab12b7c90702137fbec3ffe88e27d013101823f1e917bb13adf33f4a2af590118b353edd9afead293881788d2fe9de2ce504b411f85eb77e303e0ebb11f8f1bd3bfdf01adf6b746e7f7dcec738b6dca394524a19638ccd33371aa57494ff6cdfba787ea64419aa47f6cabc828357ef41e95af966d81dd5d97bd97254a7a4ffb68a55ffb83c8440df1bec4a75a70175f7652132a6fb4c4b4d2fa58deb53f6481829e388b0d715eb866c4d7f944d723dc0b9d12b252a425d5ca930702ab141d5dfcbf1cdb81f99a90c650fd8f57a977ac8ff7e6d548fc9b41923b2281bb03303180fadda3ac84af5286dc1da6ca4b4b1e12a090358e9c7d7ecb509975fb46eb78644ca7ac46e8b564bedef8c3126b8628c53ba122637794e789e6f3fa1ad9452c640e73b16b7927c79c1482e47e05b943039355226a794513a9531658c5101793029860e5ec293d2cedddd1fb58355a5f7df4e229fd88169fabf4d9decda1f720e2184117b77c5103b222cbab92f0ec9b51e3b4f2261ba74e9327d90b24a9cb3e3dc4a1fa594b2b4a132cc83d08a3d4a4391509913892e6397b14fe10546fb48caa67ccd378d944d140ff451933b0c7e8f1d9d7f2b976cc881b521e421a2d8cc629c9352938905514a8f95480aa29991668c2065267f9b1f9bf5e933444248299f638c12462965472b40992d605d8c31ff79f43f948cff6c62fcb7ae17d306a580ac173f3edc940ff67a8b2af693ba806e32b8189cc906c8a634b05e1361afb499b48e2483b4aa08114efda0814d01b15e0c5797263e0ce771585420fa8bb4cae6074eed7ad35597083930a8feb04a8e0e1a86e3686afc35000ed51f66dd733562a0fac327f273a35b946d8112030e688cfdc055a4ca773f62aaf29deb266c131554ca29698fd89d9c78ebd35d069a0d8aa46da5196440d9d94da67fcf43a16c6c823c212434434ccbca1333ec68d0abf092242d470fa594d216accf1c359e307a410e9fc2f424e39c93e161d773251e3960a2417c3860abfa0f3940395472a654ef207cc97d471cd6df1a7fe89877cba457c067009aeaa68f729bad1527439aa6df145fba52f7cd2d8618ea4765cdba1f4a23a0f0872cad7187dc7b805576356e7016e1ff91e3d7bec6183fcacb3c59857decbe24ae0435354dbf0c0c5ad008bd015dba74f1ea7567fa68638c1c638cec0426d9a3a0497fe8b0d46362f2bd63a23552168954f9f1715a8b5becbcd483df25d63986b1173b9451c739bfb93941e5c7ee9b1f8397eff0096d71288c8a1f464e760b7fc71328c40176499a9b35967c6c855d2c01d92e409274a90628857579e7d1dda33bc767c618638cf993891bb1ece1aaa8fb84a911a67e94865865e76d4d3f8ce7ad4f19042c71450a32ae1d48265a3254d9c71fa7997bae431f18507f9f506afc2fa6ca0e026d4d3ff30028723dc0fa41a0f5097da40ecf0ff461a21fccd803a13fbbbb7bc78fc31c343b0a9b7e26ff221d7c715075af82ba0fc117047a85754dca01dd57fd7888833a0542106272508fcf60ae63a5ffe8f69f09e6bf5fd5cb7fa41ae767a82428c744fb207860b158ac157d0d869bf1f2428d01a840898ba51a6718a9fe839638b934f1b7971a3fe37cc02a89c657fd7f60553ef683d6ecbd044456d921d92e95d4c5ad8932ebbe10d408975f66e75b13bf582310d57f13105d8947d4e48882d7dce28c524a29638c11670a97bbe646d751805109ab9450a894914b3da27b1258e543f9fcb2f301d6d83918fbc26646f2a098935293e91f6709972b4ae95e7d0521a270c8f88c1118b61ee61553e2476670007d0105e7c0cc10ca8b012966537a703e918f5ef56e08f67ceef28744312487d60acc103cd0c2d74a4f5d00e4386c22e4cffcfb1153f57fef09f5ee6f6f9aa691cf2528930aa69ff14c0330c2144698a65a8475cc3e3e6dd5f6f2f1513b30ac09c576ac176d24875a6f879217ee4b9ea3368e6a26129771cb039511930f8fd8d4f8b00424c6132c5430eb9622076cea129923d9a3f8c8aa7964734286bd76f7ef086f9539ce87a51f70044ba4f8630f5f3efcc861ddbe7663c713a8ffd6f8b3d423da90533d7ae7f3c86684298c30c556034e38c5ad1ddae75da2d8d08bbdf9e582cdcb00d42a197e6d3a195ec0829efa6d3e6775eaee44011e2fc30b51c0db744aa4f42be0bb13c2e36578193a253c3e679504b5f99bf72c45bdcf407b23f7a6eb3e5759157c35cdfec48212e1e324d4fdadb1053627a526d3bfe7a1b00d9680406c8bc79d84c8f0de7b9d12254bf68a26785652fe952ce11f7811b492c2bd005e86cf59cd1550f8de2f8edf6f56b73875fafdd661d5007ebf9b70eac6ef374fab08f0fbddc3291bbfdf4f5895e3f73b0aa74cbfdf3fadaaf9fd06e2548ddf6f2aac2ac0efb7154ed1f8fd0e6a958edf6f214ecdf8fdc6c22ad4afccaf017ebf8b3825e3f77b0bab0ef0fb6dd447ad42c0ef3bcb99b06ac7efbb4e02dc09ab52bfef4fdca755aadf6c67347d402406ea986e0600ddcc4c07b560b07f7a00852e48423dd0228469c70748e899010097030b88cb8145657b80edd4fde778e213f6f69fe366466362c8909941a386c9c60deebbefbaeff41cf75cf7e178fe9cbaa51edfc11e1edc76c3650ae06c38f8ea8fa1fb260cdd0bdd67dac7c191ae905ceced9f38d21047d2c2debe0d8ef4626fdfc491b8705a8bbd7d1a9cd684bdfd199cd623c36932380d88d3acb0b71fc36942eced534ecba215692a4e3b6a9afd14a76d619afd04701a51d3ecefe0342c4cb38f004e0b3a00a751619a7d03701a8ad39e30cdbe0e4ee3699afd02709a0ed3ecd7701aab69f673702423a6d92700472a6a9afd0170a42c4cb34fc391849a66dfe3485698663ff35998ee9b1fddbaefb5eef32aaaf29fb2f98c85531ff6535786bd53d6d1c035a3b2cf2e4edd1862222818d57d2ee22dac62b21169e9a98b62b1154e6d152864a9fb1cd4aa5eed6793b11f4e7d994fdddfcf7c3875823e300a36855318104685539c82ba8fb9302b9c6214d49db20f547f92a01f746547381d006e5718f7f1ab6eadba1f3920ae52f705c02951b2243be2b59272f25f82802a442b2927effc55dd88bde5a95f7624031700ee60506644bf7e99bfeac2eea34b85bdec0802b1b70fbdd89badfb31f0d00fbab69fba0058522327009ce3096d673e7ed57e1e9df992874b5402e52752e12f078027d49344f835c2a036bca5f6f3164ec9f77e6d967a402b4a38cdbac9200832cc1da5a139c51fa462a5967acc1fefce9c29d3027d1b7461a67bf6fa73b0e7ede09784c22a29f377df35274171dbbd79a1eeef600fc21d2c58425e0ef6a012edb727318944ca489dc7218128f6e092c642b4da5ee3966c53ecacb4b721087bf07db0870327f8c649e9d73829d873a9c7ecb6090a3f86236c0802b3391db0454ff5ca838f205bece860c362b9a1ab0366acf37bb6f468d85ee7b373fbb0a9a424519bdc5753e7673fb98fa6cecebe01d4993177c4a662d84f200da8185714a7b01d2cc51ffe7c2deab7afec6b08d1ec49af91b89f29ba52dbf38250448aa854f83bb08ab3879f638d2a3c41fd76d41802d51a0889c33ee3fa39853d01aad82a5330aa6c434424a808a7becdc203a7aa4c014b859f837dec00dbab70cb10e8967ef0e4fab774034fee6bc66d1529bc2a97cd39272966393e9b1a2712d8e5c70ac108203a706a76275a6588a644a938326e071e046da907a8cb8328b450616976a61a50d30ae8e755f8455ab52b081fe38a703bc0af82ae1076762842048876972e15b2480c5305160c62270a3b2b25374c3464b418982f75da155eaed546822e2082cf8af44b78044106aed5e97356383e6705c3e7acfe7356dde7ac6cd4982123062d2979f9527712927de94b9d92d2679fb37ae17356317cce8afb9c55ce6abfe376098e33695028fc18388ffb2f5ee07070a82ef4e3d7eb05d71084ad96d1d18b3d7eb1e746157b21817e04b437b0f2bb0df6463e7f16a2ca4fd8e3fe99e63ed4dc7a6ea6769f2938849c88b23daed791f6f34511f5eb27cd8a3db429ec71048a3fcf13a56391cb81e2eb753464145f928b5e6c0dbf47f7865cb61dac2b5c5b7e98f04e8b75459525ead74fbae30fa7bee8457dd5299c621d13d425aef038d9b6137792008e3f3d8e0e081c7ffa3867fc79acd4235e9103290e903838de86e823e827c6ae0738030714d61ebc7eb185838b91cbbc686f606cb57e34aefc71a855710a512ce510c491177bd35de3ab5571e85539c630047d810138e24fabbce8856e9b669be6f923117b1363b0354540559401a4e1ffbf71e3e78badeee2cfd630571b54e8e745ce434d4ae06abbcf5463cb8615f4e3d76b6f56d59449bffad5a4b2084e1d10a71702ae4ea7ee147f57a7e7f71e5a63c842e3891b628397ba5700fdd41d88e080dee06c70afa5062763050adff4847ef107a855fcc5d2f03387e1aa3c82ca1f9b88ca40ec12e257a603fa404c7810d47120f843839bc1411e2450f832fc32f8618802ece1600c0e965894870d0aabc2180eba60100f7de1e050d3f0938ce0a645e8064e7ca2f84cf9f9b9a1c42d6cd2a567e54da0f09d09744b3574750224799df0b2220bc756dc012ae271c25ed429a27eb149e579e23d3d0ed4aaf8b38329b6ecac3c0bede2a7f26b6fe4de7021d2c21e3fd1162f2aa2c21ebbab7320f6f86100837eb1058de20bbef6a62bbfd6d21b4211da424bebb057a942d4abf8d2db8be051855cd8732bb1ee1536d0c2a499e5452d9d56b39af0ce4ee52dd5f0428d3f4ba05ffca9b0f4a3eb27822abfc4ed4aeb0d08fc5ec8ced5bb6fc9ae366ea32085240cad346e57923302dfdf091d25a854e9c9520f50d7950517d47e6d0d7f3cc5893dbf38152b3f1b716a5d5c742a3f1f754f4761d5328188b9706aab58b1c3af2fbaa2d87eb814a17e3d31e9409cfa62cbc5a9af5f419cfa9c4788535ffca95438c52d22eae7565af5f32b7588fae1a89951fca92cb457e800063552910cd4048d2d4e9dd8c55666165cb17b5d81414cd08f836ef07eed4dec4e34c81a3f762cb435fc469c622c5916faf5e3d756f78a185851bf6671166e827e3cc429ef4e4060779b0d481c13715116e8a6020ae58d1c8c5346da6e24712721a49c95c69d84684a66d3f86b9fb3da2d66d98bdd89867571a1739240af4e12e0df7eeb44407afe5dc1f7d7be243c597e7c589add0f9b1a5535beaac6c7691cc66123d9c7245cb3eef4237ee7035723f2d7c5854e95aa2ab97515fd548ddb1589db55e4785055f83be39299075a16148abf8683e3005665f2d7abf3f73f9b39290d92714998a6794801dd69c2284545285e90b781df6462ef701887530b21dc66d80961af83b0d75c15d42bbf0338f590fd0b66be2144bd3237f110324326eaee76ef6e6feff676ffa1113a8311c3d6ece7d4ad5b82e1051ca71b364c3568cc9091112386c2bc94368d944d4c466f2ef5e330dd6e7709cc61662d1af42c28bfc6dceefe12bb6ef729e95e62a0eda6f87d57d5fcb0dbdd87bb8421057cd7fbb77b994de0b367edc7aa91e6947feeee1b77abdd95800855a0dfd736cd78393835e4dffe93bdc781df886fecf57774c83db3ee7cf8717a924ed215be0b216ef799e0cf55650f29cc370db71d5f76303eec9c3d538c5d0f5e8f982a7c29c828a57c9798c4a4d7d825e10adfa9e0eef0c6468feefe53da9003ab57ffe07fcc70c297330ba1186394d284ea41a1e6464d419a97c327cfdea29412c3b028636904d24b3df8796e14a3a2273e0c2207b1d6374a29a59432c618db05ebf38aefdec88f9a8a42a171dc7a4bda6c6757d9fdd6c4e84fe4dd87d8b418639c73d2dea22d6fee5bd45bd1dca84de5b701428c5146982e3c89b076be3531e70a4e93f4113e3d57e201f17be80453ba54434ef59fd8b70f5cb1d7e6d6a51fde0cfb65ce0e4957ac93bda51b62e745c02eda421e0da1e47c602863e46f1fb8eb41c687ab23fe46eeea48baca6e3958614bc84c76aa9c1897b1883c077bf23df6e4a3d893b5853a28e3fa0af659d7575a8853d8cbff30ef6d4e5658eaf14215146ba12afda769a4730b63b0873fadea16c6e5c022b928afe47bd1ded474517d8b2a3f3641950febe90822dfb4e662d5b00de36690251ab0da8f7158d5b00dfbaef2fb8a8c51e5f79133d991df58a4785265073db3a4b0523fcf1b6b71ea93d9e1d487c3d4c33a9ce256932ad949953cf5c381956ee00f6c6a7bf91ed814cccb1712c406f940ac0afe348d7cf9436a7c3c173c13a623a809aafc60104172bda85236e99d22ec658dfa75508d26823c7a0ad4217f4aee2754f9f0a755dc6a9ad27f35de53f9301bc72d2c3d05f2682c56877ce923a8e93ecfa8fe162d1f05c2d6c82aaaecda4efab1871c0631d8b0bbe6614f76cb3aecc91df65ea874d0d6cc981c82f069177bf25fba06624f9686a0f2fb49fbc497dfadeca589ca2b4c2325ed1a4ba95bf6b6eeeb2c59117bab7133b0275fcb36c8e1d0c05867f30215f49ba9f2bf47710a56f95e0e4ec927717d853d99752dc49efcd975107bf284f51556f50f8bc5626225bf8338b5a9ead3c57a5bbf1a5a6896a5d95a7db02ccdf6c04ed54bd9631c576d7138e36417f49bdf030a7d28ad5fd1ee84fdae5887694ed8c38a75dc624f7e2605fd2ab520bf0eaaf26515f49b55be7c29492cc384b633d88a9df268ad2eedbaf3273d73c23d8a5b76ca9a7f383585819808bef0c354b054c873a542d65261f6ed9287531fb37a38f5b98ff4e1d4d747d2c9d17ef2898c52e1cf1406ba822ea74eeb59b765ddc47100a74a0fd9461e65599665fd5fd72c6866d9c3ecb3eca83a15fea1428ae591b1a00e9f2fc78c8f8d3ce29e8c057970142d9bd977f771fdf88759d079380ae4e14fa08eae87ab951fcc873d1e7e88e91cc9a3c95d0f70739e5651da499ea6c93a2ddb6612f4c37c6a14f6a0ff34cd13c8c3a5581df0a14f2ab606fec7f5f327153e73e73eb16e95574ffde4d12ce2d409e661253dfc8cfbdc0763b52a16613e58cf9be6cb2dc097612c0fd904eae0c2a923188eca14b54af2b46c0279482b5007fc37519e58d4a4d2d9c9a31eb3339966117b5b785a25839a069aa8d4acfbb028f06510d67d33ebac3319f452e567cfcccc2c5bf667533b6bc9035f5a813ca411d4c163a5c2ee301ff68eb6868b34823c6418ab630b5b035f4aa30a7f4615d4ffcb58af0a3356c6246bcd17a78c38d5c3fc24b36a0f7f1e712afb197984b57290fe93473d24a195df3bcf9342f2cad6609143328b0cc2ea80cf9c1cc2d640199471415f062d2adb81611b58689a0679eb66e6c85c12f443d5c9d0e173fd6085023b254b564b20f30d7c23d710429844728c2ee690c400a94020f67aa83affb7aa16ba8999b3ebc8f5bc5a2d14b95b684b0f35d1164e9dd61b4b6751a27ead056ed0d52ad872d5102bb4b235fe730594e10b06cd49a9c9e4cd23e88732d5ee866c8d7f0bb52a87a5f1f77642757f8d3179c56f5467c2aa77412c9cda2a5e04d50f6af988a840e425561550503fc8059311eef070cabf5d9c3aed7812f5834eaa0f55c7e16f62820e6995d734fede3494aae7a2f07fa65f9b09b49dd1b64077c3381a8333f22396ce561172e2a8a8bd198891a8f061aaf93b46977ac4d0b46d7ef940302b2c6931629adbd8830e85edb319323eb26464c9c822bda67dd6efdda576271f5bfd6bd8eadd8e2ffce7e7541c7e79e75e301b0f6774321d5c1a9c263dd6652f349094d841939d1da8e0ca125cb0cdd9bd5b317607444f44396a96cb0a52b297eb78f38f886d1a473f868342e349dd897f57349e76277e58e56fede7a0a02f5d13753dd459d8831f040dae7598a69535a15b321b77f2df1a3f5691385ec8c274c594202afbe7f0bc2a8a2a77a82aa8869580f0b0002654bad30f9727e8a9fe393db43139b05eec6dd53458f267ce077892c00b2780a2490f6b45e3a1d4f8053071558e46b784c6d378cd937005b20026aedabf0026aeda1d9425343a29fd52fc736870529a8b559bdb0cecd9b007677085aaf5e00ea70afd96b469dc565802224fd082dacf251c505cb06a7748d0fdaf5b9bc5e4c072c11765cfa120027af9b0a1499090201042f86a5513f58b6a41e1179b65b0772835a1f8053db65a2d085403048240104b8b080241a01a5d340d7914424643746832b443dfd0465be58ba37ac4a953bf026a8cc161f8f810b9b6e8d76b6f4e2ff4809a2a4c85df437bc3117161556fb12a08e44d2a7426b5bfa85f85232a08b151e6ddea8ceec0f8f30e4c0627c43a974fec7cfae19bbe7e5261d7722d68276169e067f5eb2157510f6d0d7c1c9e4103fa75ab5bada2f1f0db8855d5781853bf6ef5f7cede34511b310d7c58a36b2d760c7ddd1afaba15d3aeee83810f879e40bf6e56d011a8c0840aeb0c20aac1284ee2c62eb81971a9d1680b976ef08d4b409c61bf7c4aaad635769f076af3cbd51f0cfc276cc43615a029157e4701721e226a17bfac20d42e3ea27edde48b55f1503fec9d21232afc9e25885c0bfaf5d0107c085953a0c154bbe3d7d6c077223aeb56b9024f6d22f6e07f5ce3b724015fd590a93dd4aa1a73526a32fd7b5eb76874ddea1935a05a7f01f4858f0637025441ab58a155bd76516bab8071d45b6a0c81c2d20ddc8d6d5d02d23380e82cf558538cbf476e87726009a5f6b3528fd6e1428d5c8c0a4b3ff6bfb81bf483a31ab95861e986fdd8ed7a5e7b873dd82df6206b68ab7c81a5c25ac261bf3b243570edffe1b597d4f68d39fa16fd50d1e18fde2ff8e2d409b6a08e0a9af00e4c41d8f3b335f0fbd43dc4a958e1779626f2963761157ce26a2d9cda2a5db02afc36fa9ce53a15cae8dddae1d4077f2a7c58e448ebc06e52e1cf2c40e8f0676fbabfbb1fb6063e57f8700aa79430810f59ec4116e4610ffe0b3ef48bac0a3fb25a059df4ace0c756dc8952a114ab2f36893d3398013f803e82ec8dac39a0cd5718c0a0ce0c83f6861f3ebcc2a97df804d81af87c621f9cea0adf06f8413875a3c2ff01326115f41112c2a9adc245940ab554b85c2ae48fde28f8397038f511000671ea94e2d60c15ae950ab9637803e7e0d4c9042b8ebd81b50a196d3f75dfb4022a43ab0200b323e8fcd951c685c422f990a6b0caa687c464a82ea949dd8fd4438a5217be738a531ff422b238f57193d3dcdcc888fa3997ba91c984dd476ad5a5375841492d4ef1d4ec8853cc5c482c66c9bc8cd88347ecedcbb05d3c3172ad92a0ff302441bdb731711fac375ef317cddffcab5537bf34de0de7b9ab69b8de781cb6e118688f6cd8f8ccc6bb8d8fcf47a7c7f1f088536e63834279debfc9c4dd112cd530bfbbcf6117b926f4c3a2d47dd3e9e38fbfbef9d1cff4fde7d9645bd85ba34affc37e6a76d4d5a031a3fb4c2fd37defc9e83e0f15a3cb7c62bacf86494033a0de65af0c003a14fe9715d57d2c0aa7e00d6e2b89fb3ca8eed313f7ad95e73e3efad68a0d1a6607b940f11e87e1ab55cc3c5e0c4ad02f3b621ee8a2d014bbece8e326fbabf116006e573e30c110786ab4a005364db30f03125ef72686ffe6c2f01fdd176c1efbf5d77a93813b09f1f6a80a32385a799fb3dad21d11b023051d1e2c75ffe38fb7442f728483ea26e9ee7878310fc31d51b8c2cc5354c4a9d3101d38d126caf8691f3633146193cdc6c0c23b2f226e93a90f6fcae8db05fd3628898fe5d2926a320618b36930193d749bbbbb79316e3eca6b150f41a8cfce07c6f14a060a283f377bd9cb975363f8e285080aff232a0ac2362818803ad355e66e69fc913932fb47766f006b414a2e5b08b9bb7f6fe1eededdc61d68ee1ee72faf296851281b5e158a72499886872a502f6d51108c319cc561140ac53242deeef80298232d998f6188a828089c1886cd396d5c41a8aeeca93999c79cdd3d7fd99bf3677f4fd39c1deca67ceadd67a23d9f605be4a62ab05ed32c033090753f96a6bf3faebe593706d45ffeb7434578f801b2b6ca16a6d4fe21adca59f5ac3a4ca8d72fc9f3421e2fac4a92ac853c26abee7329763fe277334da8425b06d21a5fcec7e128a1b47e356798a6df25778a0feb16e1d82113185a0f4966c57ee1ea805ca3f7472cc6f81873d862bb3bb1b918ff7cf66214babfdf8500eababa7083ba6dc951d7d545a7d22c51c618e30fa2b020fe0c630140a2928ac05cdc0750d7d58527d497baae30ac34c32e30c182164840794161042618c8034b851e72aad0b245123b34d882cb166ae081ed2183c15a931ee7cb462aedd30d81173e3a7861d1d30323aaf0000d586083214461052bf0a4000c29fead28d2c41456149e8085a585a7987eae94d27fd0050c301da7262a432915b02c91456271052be850da052ba84c951c0c5552dd2a2e29ea47a1cc00084940c2105d24618515fcacf81f85c222830758698214a0d0a0084f5c8941fd4c0c797a6044142bb4c0e4f50fc42a28383822092944a1042ef8c7f46f031858e0678b1457086309415491c50b725084235eb005125842f8008b140844c10509b478a9c0083ac8224a9421a8c064052c7801152014094e4c38a009275842093818421255a860c21646208494524a29603efe1447909ad0993e9b18429e40081a26a8e28a294498011458780a200d8b2634a1e352d4efb5d0821cb55d5a5842d69a257091832368e1043d2862d5388c9deabac2e84295755d611ccd1bf8a854b0042030907022075380d2802d88a2f841124f68190285ca228a1ca86c3982082bb610644416cf62b148f099989ffedb000a24559ad0320250115c6042164f88f84901128ae0420411522c9607fc60092ac842062e2eac7041e5a75f8427b60a472062a2488a249a5471e2e708294d3401b40510b0f4e4e04089297494782541c78a19c4ced4b680051e37f104284d749c3cf1411195d5ee20082c28a83882111257acf8b7c4a88ae5ab2b8c292ada335b80bb40a89f8c8e23258e6a503d0c1254f8cd50fd7795850549e86942ca162656fe9453eca4faa338e5bf83532fd5dfc60141f6065688048450b52be99cca51631735da38827ef4a12b0b46a834aa3f34716ab759c56f9dad12418c1bc4bf83b14e8a11f8fdb25392b392bfa520b23bed6a36cd0ef933d511e4c386203f4026426c56cb16ed6bb978a71835838d0e14ff0eeeee6462e61d6c89ddc19c3333c78da61a5136ecb54692dc45d86bced1348d0477b0581a89599cfab6cb163169e7540ff293cc9ab5e64ac6afb79261b392f15f8d19646c1a89967e3f07ccc7bc36a4fecec29d1dffbc36fd98d2cb520fda49622d7d978080a04b972e433576473ccac1ca9da53217a4f4d2750f51c372f6c6bd1669950d4cd3df3ed8b381038fc4b3714170d89afe1b56c79459263bc8ca02876a7f7f6bdf0f593e3f7b8d29cbc77ce85a9adfac5a0972562188f19a73d833a77d89db3824b08b977a68a46f9246fa56890008192f045cc950c9e84e41627c8c4e095c8940c6c7f89c553faa39c80daba37f72386c8db63725ac8b9c0d5bd36d037b1d3b0f870e6850ef7c49b8fa3369776ee0514046272546b7a52031bad3aefabbd44352c8da9ae6536cd2ae2bfdd0497774f611c48686acbdd9da5880426d1c4c38d8eb2cec7cb0c7ff896098860b34fa3bb393a47d64f833c31e435485306633750c8704da4882d6a8ebc2821335d62d4152851c3fbc02856010e5e01574296dda7b85091a407168b66da651ce14d331313486b6c9b4850d7605dcc10a0ae2d407afa87c8581f415f0676e948353ba586db8a810540254c9ec4d12f686fefef73764fc500c380489d81a7e22b008085ba43eaaddb75da01c0cda1a6ef8a7de2c5b8b534df8a196161f5566ee85bc44508761e987d7207ecac54a847f074e1580e115508769b8053b4839ae366e403f1311e80787b4186159960cfbd47cc6985583f636ad8af1a55f2bf205e6b71352408506a87088a6428f63e8da80705543fbfcdbb4fba829497ce1bef764fc6743e6bf18d58cffb25a7ab82a1a5f2a3d8d199c0c27835b4e3ee44affc221698097b8e592b07fc376137bd0bd189d088088f142c0558c183f572d444a975363e4ac62fc6c1ab601fdca8d031aa3c7c6178ed5102b7bf7ecb9e7413813c01ac3e210167b081288e8e0f13b76dddd9d65bf599665998c09676bfcb353961539e2cf03a7369e04fc246152dd1d8baec7c14cd46fdb1b2ed62bdca0faebc0a902547fcd27b0375185f21d5be35e6de480fa0f69d59c1b58ff22fb45f50dc3d4aa97edfd5d1301102faf9562d7dc02125053240075863f1b185c5591a629e2382de793beaf3375b351b798ba65757bb8aa18bf6d1f83dba5217d0c4782e14893f4cd6d5fe2e6f7c675fc2dcde79ecf73766d92b942bfc8114fd235fea08453f3b90577f885dba669f662077f484c89b4dfd83372423a65339048ef836866a872a551f9718208e941e5d78155db05178bc51ac1ba8c822a3f2acb814525f56d91f30a2a2f1121b74d235292341287636b60eb57da2fabb49fbf714a3edc39c334d98e2f28c923751e7b9c7133f3833a7eb9124e4132d2d3b9fbd8cc3aec5b7345923135daccd5fc96cd2afba1f6cb55863d4e3787c4541b07ac7a00a63a56bb33e2b4e58300f34e02dda50bf74bec1b5bb9ecdc5aa0824c38bc1c54a0eb7a15d5cff41aaadf34e1f0729c82ccdf2a84592bed69abb2e79f1a6ccdd57c12c744089bd5fcd65c69118ad691709a94713cd4f8b9bbbb8c08a85b02b253a8db4139f92b59d70a04f34190a5dc9bfffdbb20d97b17815d651d4b8e34d0b65535bad082b5da1b1ac4a11f6a6dd29a33e2b4d4cfdce9477c7eee3ed819e13f75e7fd3935a7f6e9c7e7d4e6706023fdd388bf372cf1b78c43ee02ef6abbcb3c789779975b08389f5f6bcd5586ed66dd94dcf743ed561a49c2d565d4947b330fde8575ee4299b3dba6e95d02ed82aecb8ba2fa4d2f788884306b158d78d1f28275f28f8fbde44eded1b08a5dcb8651edf9afe263bf2dd32a621f39b92dba8a0f4b3460753b1ffacbea72adb98abf252002a8db4101417c1cf6eed285b74b975594400356b1dba659f6b6b9abdc605b9bbc49595e02b72ea6c60ed4fe41a31663cf6de376353921ae7570131081f0ea2708947efb21a56efb21fb3bb650247b94fd16d59c55ce8a3d761fd43826da538ec9f6dc0e3028923daa497ad70092869cdabb8ace958020a062afc94deb6f58fda7c64969d9dddd5e7ac93a1fb8627362327af747d78eb6ad9282fd6e15e209705f2190c562b1e0d27856fd03952727c5dddd7d484b0a129b6a2449d7f95ecd59fde84702bb544e7ec631c95e0829486c6a9236407ce64a35e464dcaebc6309888c2a716858fbb18c93e20363dd3de3cbec7c607f8c1d092bda72ca35081debe07a1108efef770e562d629c0f2cb1f7d681d6bdb1c7ee8ddda8f2b1caed33f6e9c7c23af906d8a57a27a10efeb81a81ff4988c9ae86af2cf706f26824904beddaa51fdeddd6f0c76f23b1ab61ab07b1da7a80eed009ac5446d25db854f88ca4bb7479e903c3ae078c75c0eef3973fbccf5f7e977050e2f18708620948a56fa8442c98ca9a5432442410000000026315000020140a068482f1804cd223dd0714000c7eac486a4a170985610e04318a216388210400001000011019a26d003f377ab5bb613e6773c7b7ad172515035068d34b4714ff6b36f4da4ef25b18a866ebec8c5ed5ae3710351334a1fa1a35d8d8b174c1cb07a3cddcf370caa0a4f18ca71e5e99be454e27a5bbd3b4d9de8b10fe01d9231f0e709c8260969bde5826edd42652b55b6554c5c67c4d7150b093502931c43656081818b8e4e561203835855b6ba3580ab49429c045171e75af299afe39e2b17855625631fe2efcfd492ec320efbd472ac454ac0d48a413ca6d17ac683863bc003292ece4136a8965134b23c12d9811a31c2595432145f489df68ed5a99f95d0190fd1c40642dfdc45792c4d0df4a89242691056950718080448f40d6cd63c0ea76833a10ac5466b185d102a1a4e7355839cb5ebea1d23bd392a94185e4cefe1e6720f9f29400cd831ec228dea517aaa5eaa7fd2751af49dde8252993126f0f9581b70a8d136fe121d0b971d95314e17ec59010fac9bb7099e2554d4da0ea396844a78753a088b156a13c35b0eccd07f5e52c5748f32479dc65943ed57b9e024b7540fa8ab3b360f07ad38f0b4be0d40889f2839aa56db96b347e05a1a45117722763bb0b7ee74d12c4ae40f590b045b8f6bde26caaf3b7f07379cd3891ed63cd382985e53184e3302c1af07ae6302ccb3d0162060a79104d50032e374709d3b2352aa940126a124d79d880190b85fad77cbe4de686e74b00e1da5deb53274074d1fa9f4eff58fe56dfe05ed1b5a4a9dc50998fa3b8bc46dca3da9ac6d4d5e4878a5ce0b424aedccf5ed6175071495bab00615c6cdcc45b2bc936a77e46aec17f6a6741608de59c18565aaddf8f9a98592c3e6b9420226b4510ed8b4a4a44a2c55822d87c10f7293f21a2a941620984b42b51d2a423d1325f18d98a3fb7ecfd3cf49b48e4b259d678e7efbc6f2b21d87ba3151f0b7b8d3b16a8fb5bd8a3de6f575b6f462d367ddb2d0d6d04dfa37731b791b020fce10a45e8bb872140ff05f1fe61eda5fdf7a7cab18fe24cfcadb5fd4971c8d79274cd9c019e3c3c79af8af682a2ea5bc6855f9267ffff6699908005ebd5250ce40f72aa81d73cf979c417a32fa676b7e582309174706aa7ba44be174219de7c7521a5fa8e07609ddd62350b69e245fcb8f7506f9c9f803b330f2be207b12576284b970715b935c7925b216a1fc0e73f6124472a400b158805e52178a5c404a1d65c916f563415684f90bf0fa7309fe49d7c9eba8e64cb5b9784778059d16543dd218e5fd94730d675b81bfe103fef107f1bf9ae58375e39248ecf4625eca9d31ecbc31dff20b894cbdf73e49911aebcb04e34fa05dd270cf385476397d9e7c91acb81c6fc04bfac104d93f05f0f053c667b47348c0523852215fd47c6913073ddc5efb8378a65e143b861c687e438d9cccb30a89f7072af4b2aa5199301ff23dba48c45f6ba662b9a912fbd49533e14de2da404e36389f052a431bf84983ee5c586e2c73345554642e1ca39a4d0220162bd7e23a0b0e6cf0b1f051b8b9c8c9c11da9a91742d3b660994fc5a78fc09fdf2354dc9570ca1f3a798d40b861bc03d4916845d32063d460b76ec4e5674535bb53b1631522eb46873e13419d20412c24cd02ea3b0be9780bb0dd7ad4a4146a9cf9bea45012b4c3d643de2613dab88ed250903698938a8acc6c28cd685da380aef9d36c0f5a0c1815936a268d3e41957e15804be307f9ab066f5d8ef7701c57a22b946c337bfc63cabd55d7e2f1fab31951d9d8aefaa2cc918f74e3519dfba80947184987f5f22e05321df6a8ba96ba6edbaed4521888cb65a410f893c97482b2ed783d89a103583856d1e6108849072e3036ef39f6e76fe4d3a4b6111fa7badec21f45caf9efc9fb8331219df48783a862be17c2466eba2d3679f8e1fa728e84812dcdeb1c4e0d023e7f32e1f0768fbabe1fc896184363d9f314006a945ff67042a23813cc6b53ffaaafeb0f26480e5458c774b0a493fc8f8ead0774708975ca0d9a4069fb21354fc8037cb3c8849c2fa42c7157b89286b2da1f412556e66475199af4e450d6169a8cd68e07a65ffdd4c2857be10f02df592be35e5275857609e02980a7cf32b77ef9dfee5e6ebc6e5c675d3edc675d3f5e6f5e675c3fdc6cd6eb8ff19b99fbff927c4c5f109072ff7473bdd16df1df5eb8de6a5ac0df4edb08ca5579d79dda5ffe12a1d1018cf8bbe172168e0a7bbed5e2abc6d54fe26a8529266ba3c2477e67a6045d4e2ab5ef98173209562a4e71023df1a619548448218499e589ed90975f1c6d5aa394337c2e32f214e547d71aeeef764a028aae6798cce850b3f839ccee4ec37a4be16eb11e22033e67587b863675e130318f1d1602a414f824075f8357b151b85f110723038cb02bd8b462c65d0621e7993f4635267c8ebc5068d3df1cbb41e9b848967f232ed17db6dec092ff3151bc2658e3dfad7841f8a1e56c5e60c4d19dc88b4054a00ece8b87ac0793ebc1000eec281cf8b27615aa01f25e3afd0b8f7ce03c7ef8dd00898ad28e62dcdb11b408e27de71f3321341d9a8deb9f2f2db0471ca006148ec19054ee8302e35d9378e072dfcfd44e840dc61fa2550b52af66a96937c8a8cc5fe737f43c1a2f6591404c2729e645b9487e70efa7ebd05be6287de044badf7666c4ed4feb51193f42bb9613f79e293a96b204aa383eb9f941bed3347eef13eb25af4434358111b3096092ed31eeb06334504a4edf4dbe4ba59e782fc56495355beb7c5d6d9440b7a6e9489855ecce444c882fbbde221ce00c275921c6f93d8ef43ae1d47815c49201edf2aa880b29edd166c1748b11354ac7b365c6a14749454893af162fa8b78e3ab25bbca0ad69dcf3c3ab34d3a48ae6b4bf76e97a6a982c4b8449ac10c405c6cf077dd54940365253560b7ee4c4b4475403c65d28c2c759c53d7097235521fe5f5a8e9429965a43a6f548f83ca70427f05c3a35bed4bf1b4a95da7265b2513beee10942f2ca95c8daa5c073e6030fcbb60f5cff4cb003d2df06405e2d2655def0b0a1a7243423c409eeeb5d22a0f4cd2714d87459b6095b510db21feb7b7b4715c9b615d43bfa65f89bf941b27c80b1f41b48611daa2b11bda36c5bf3224ed2a4ceb1a377d3129b3af4a492815c298e734fd96cd5c056a1ccdb93527c65e4d47f11ac058d550140ca90eb39517638fddaace56822f30768faae5237e1f55c3d08f3a6321bcb90f8a599caf72c97404484e985f8f28e3c813507c10d42ba427ab83cdb76e86856260ed86b23e5a13cfe74041450f6013d516cb0aea6afa82aae6d1ecd1cfaad36d8a85acf24419c50e63da3ec8ee934cc4dd000e177a4df31d369901ab7670dfbd494b27a16dc6c8e8d77f585cfbf0c45bc9c8dc86a4584bbc24c1385289310d0e0cad3efd8a1a862d8b06074838b6a9b52dab0827a2cb60646f16cb4323da13615342c9cbfc866646e513f53a5b4101d92eb63cc811e198a2b76b99d21454e32f02d8a06c56446e4f90783f0d72ad3ca1f7eeb8e6ccfc8361495f79a53e1077d316e7dd8758c5d582199002e666a8a5e5c64aec2e37384615abe9c52a8eb4e915455144c7949fa9b7a7e69d81b275aef28d32a69e4cd4bc11e196cbeec649eb0838cad87536708627419d5799538c0924276af4e10eb867dd6448fcece16ec464d78856b2b40a8b2cb5745685d2544bad13666b4230d19cb5ab9ce9bfabd2a3e7f6df82b02e744bd312434e1c26c68a79f5a8d9c56eeac28360985291f9b020d4a4f30fe1c1da0668eee333f91d4208996271b57e39e7ac239b24d92f61561ee335007e08b3ffb5be396983ea0ed3dfc4f24cf8d3cf8a8f4a662870e057ce0924d031c1ba98bdbbca5dbdb0478352e6478089d96adfb243cd5adb2af412ffa8e85a7cc60d3bf0ed0fed6ebb638efa405bb183551b35f27da1abf6d5864845270eb8c284e1510f8cd8a8212aa13780eca6db6a02a406ccb9c8b7322c2779828efa72238792930b88594e3f7b14bde0bb007708869dc115327065423b7671e35380a8476a70b0c4ac28dc9ae62469a98a29aa88658c1f1fb472ea88de83eee85c72b295165d8922a560e249541274f441cea02cb6b885664052e1022fe209759114609260e06bbe326c2e73e725dc281136b5cf0417abfb3451ae9d83dd52657041ad6e7f70efc4b97ca602623581181f991d06e3e3fa25803a78d12acef6caaf1607127900b7225dc81e81cbf566ab1d005735278e026e041f3d10613981aef008fc75bc277bf243cb94f7af030530431d817a0bf7132e252fb3b6c64c6b5ad53b29290a38096823f22a23961fa0289add9851277f335edb08e90ce950575c2178dff053d927456ddb8c2ef58e68148685b7c4a07a1e429f28a71df96ab8b68d294411dd33ff1137600ab2399b1a2b9a9901fbccaa97fe886263dd8b4ddc7115220416d52dae0c3c04f9388ecca5e76ad5c84d09f60cade4ef45443733f0bcc55e1cf9609b4ac71636e7afcd59e03c36a9e2fe72e2de3108d2f1b75e8556ccf2218a9739721968add49eade57e3ebc15328cd1332c0f0c0e3de997b853c3a6b134da6865fdc8867a7186017dd812a1cb9026f321e49c27f409b26906afb980c5d95ad2042394bb1db3a7a094b4ff2eab417998f6eb7ebbe7bae9369b7521b6c87cd9a666c302ba0e4deea8a6904a0fb8a9b8f920420871230a3a4eb5e4a9282855b3b15f7543117832439fc1e7aaa3166c2bc45db3877a3b80569c606268bf6327093146a0e035134298f03c8edd736412c2ddfea52cb484618163e0b47848662586d1f4e19a6d17da0a561a68af1cf04c62c8760cce06f41f09ea0496fc186c4512cb54d265344864f81c47d58c043438485835d31b1526da80b46a3bda075df8616580db1c5eb11d701de4f37e86a8f11eb472ce4ccedb8884f038e617ccc486b6b4b34d7f39670a0ec2a013ec0b99aee1c2d473e2b1922d5b8deeb2cb9b359ab4ffd1293e64702c53a927288d6ccf579387a48622df94ad0e4540b3c0a4b5007de1ddb3d64caa4bc2bd52c75c9f901a973a0a41850961e52616f6aa27e4cf08c205b20704fa84588c891be84226479e1ac6411715d6dac3c00855e37e8633963f0fc291608a95132c856caa8b9b5a6ec7d62a15ac6fb4fea54299475597523ae48917c093ca6ed24d70138c4e89a67e8df9bcc7b7998fcf8f6c32a4f1d5baeeb2ac984c382b70d3d1c0a154c5dc28d60c2adbc70a01881e7d67c980f2e336c783399058070b2209bd4afd09e929226780fd5baa2ef7ff464031b05824a8ac42e889e85d9e6212732f4c3f30d3d8701ab37ad231a655a1ae023728a303557d5e6e98b68bd7da95b7b15e9ffb6b4de2daade39c9abcbc619885ece1414cb595c3bb9132b41e142c87403adfa706f0daef98af834507c9c0628962ab657a01d6e07e5aa1365d2e248eb7cf47b0a5cb42cc2b3c32f91000102ed721b51bf1602e7cd3d5a4f6606c96b6f3aa1745aa0b1a8ec0285e46f0a01d91c846d82b83100475fa9ed12250e13641b51128e5a69e7e123fea07aeeadb0d1627483797071053b81628c8bb42d0f5bc20cba0c0df9e844854704a116c3714381c7478f64e1daf2838d8fe7fcbb629cbeb2c21c3e060d1d39baf60068327c9284575ec96a8401c4791cc48ca3df2c1a1b421645ace5c1f66139222dce0adad47cea0174ecc3a374aa3d1f2b5e3e8b48748d85e3a940d7352a805bd7c9591e549b6a6330f31d851531a3f2780ea1729b0bb145e920610369e00446da2a428912e1b8509118fb39029bde68cf02093b047533bdf8d114c2ccb956ea7a1b90b6d63684be84d37273b9f06e65553e8ef6db3d2bcdb4e2bfc126edbb54aba3015dbc3e4227b96648af98671ed6d286f375d6dce40cccd60d6b2c34f910ba84dd22aae682a23a4676b7e34f3f9e0f347301ff35be4b1808765246aee8d0193030570510c2d16471e8620985b52f82282c0422a2d603a3d387599e1c247324cd74b6ddc7ca1b1ecb890a1c857454728a2b0474d72a7bb5163fa5bce701076dda943d32e8bea413af0e13253de6aa3fe9ec13016f30b828bf3be7fbcffdf3b36ef11b2a2bf4c8f95fa36aec40f96d5ee2085578b8008530dc2dfe6fa573f21702dcb2686a64fef40160e8738c1810b2ad7c298d37eb0a3953985f0af10e08d9ae67041d50d800c38028a0e38c7b9aad57b24752a67db9f771b5a959570e2efc39c8f900a6290d66f26cab4d3bb540d069ddb31bbb7e96d10cadf7dfd9337b95f65e006f0dbc8f732293c510cb8530fcd93e15e81564ea390a80ac2876b75f24e3ffbc217dd7d05d35bdaa11bc619e7734082034c7c485d2d4041b9610eb221aed05182242cde059461c3c0b818d4a0c03c83bbf864ddfd745f9a937ed2936394287350b4ccac57c6601b65ecfdf0a81c70e6e285dbfa38b2c47654621f002f12c1b33a5d4e23b45da467eeb6578f86f6840cda781b221c3f18b97ee9a2dcfef2a9c7fe16ede06004d067436990090c2585ececc6ee86964e02dec5de68eb3c64cbd016e25746bc18c491188c1d2c7fa4115bb05d1c8285ca51bf7090fe68ac50090f3a140ae70d88afd2d085b8649d146923f1facaa537badbed807b80d662ba08b0b728dd9f2e1193419b1a45bc6bcbd2418c07ae1b037c0b225487c7db855d19d05c4dcbd3537765cba5410df7b2fd08e030a4ac17b207bbd4cb9dfd6baf0e3d31db8918784055bbc3bf077316ea1dbabf74c831e5f7518556550d3e372ac08b28d3082ac26f5b145face5a8253143239e8c214cee939005d1eaf77063aa72b7e1bb6f656eefa5ab54d451f953ed61f788461d8cd50279074d341d1691668feb7ca4858e9d6c907e4e2401c4dc29077416014ac9f1345935b8b59beea17ea7d924471b9efd8c03971ae0d9bd40c663029954e9068f3974208b688caac6b1e90db97312e01d8d8cb97073cce05fcf85ac5fcbe831613a58299f07d9869d42bb3c84f0f448a1d9ea9850a457d590353d8244d423ea95e07fb494530d5ffd538fa03b85484d939dc0d59238cbc4726f7139d3ab699bd6a0aa99ba728aa1226a4235e6c09f038e7d3d3d28ca16ce021a2fbfd85c90e66281bcd2124808e92d5f1f9d46baa3577d93e58ebda38671baa9a212651f8bfc72b61bfc85b80da9f4d6aed850fbe6512c3b1740236bac56463a497df357cace294ae31493ccf5915e9d6313048da035c809e8285a23b711052196a82eca830585de3fc564e83cc0a965dc1b25cc0432e1db47f6d89c0969505151d5bfdfaf4cf02382dec9ea91e409b6411a26b5940a1bafc3da0c14b974a5ae1ba534a5afd108e6341f8f4f86bbabd07300007223fefeef8543fbc207342018e957b88802d6ea17b398ac4cb5a05133f6eab839537f6b0c89133f3a114aecab88bb776e409612419f09dea656c16b5f4125a3cca32a2c0938707e3663d6b8b2c0a982598d9b4c8b30ac2fcf5a71f858c0f121e871a09f8b2b259ddb6219cee464432e93b2143d405bbb5f30dabfb20260b2bad0be4cd45edd8979dc1905b57f50652ccb16ebf50ce68ccacd66f35816138e06406d5a45cfd67d1ff2251c29fb378f11410dcfa5d39886f3d6d2e13838bbbec5936ad912c562945812d4a02926e50fe89ccc29001572fdd5a0bc87c1213e829ac54a8bd5e39294e71e696941c1678698f5c9eb4bbd0c5b3dd834b54bd629eba7d45808c983b1f6556570f3d2ddde0860306105f3ef18b52632fce3f7d8511cd4d129180758890113d29c1f46e3910a1b7fffc8478a7e15a0c8a86510861614af5a86c66240533f6e2f5e79a997bbc05bb70fe5c69bc5c0dbb1242b2018f684a596e6928496b605db8962f91ad2748132247db5855f27989e869b4781c37df347cef335cd61ce99393e9c78dbde19e55099cd48251574931e615d6af68fb1830e742b6141c7347d0fe89f03866d4665cec8e188333df6a8168510f52a929de4180b4dbe97d2302d4dc733d3d057a0d44200eaa8562a464d429005cb804e00d4154df4a7e7572cd8c56c6f35b860416d80f4b164945f3551ee171b0e922ee3dd3746e77432c27457e49c9baf2c463c4eaa5fc1b2e55e84142773acf6cceb70becd25377050d1901fe5dc4f82777651ca2671a0994457372a88617f47e896365fb7d0d2630ef70d250fd8ad269a1d1b8d615d45b0e7947953867fbb8f82093ecc07501ae06f0699fd383e57d23427fda748868d46831683a375a0f2d522b6bb988dd8e5693d693882cad11b7fe74397496c79b7cfacce01232020fa2b13ff0b4017aebb24b37d2cae2ba2c8680ad70684ccc01923369eb6c08b2ccf3050a85baee3b5c7657c13bcbe4df0720766037f093ea6f3a745f7031b3367d24ac6f694e34c533c47838623335f49ad5a5f7b85e1980283841adb9be0a4b930354214391e796c051b3523d11987dbf4ddcc3aab83bc818191c76278c5e9468d01a3e9c3e3d08526d9c53b39e550b80abe04bc2193989f124ca73b3e4d373c7672e2df8d45a78c85608d61c02f81bfde5afab9fb845ddfb173d0e7c7719f65b89e3864524ed14e54887da35e192aa967a49e8712947449ccd465497a1e3f8e5aa4a56122385703260e781dc0e123be0688fcd2b4f655523941ab242aad40c461dc386c160a8ccf39ecc017c52d19ed38d1772b5c70d74e2a53232b020d75c6a0bdcec0d2a7e97114ff66f49aa358b8ab63a4eb451d5230351e20ebcfe4c6470b9825caff1e82b2d7fed3771902c1fbeb6dc2840f31e93b7bb44c18ad3244d152521210510b6e3d507eabfcc8f1169b1c503631c8d29d95fcff95221dc492a06a5d8f55decefef653e219e8d2d9c655507fe0de978befb3b694ecd660db1927c5460fcfea93eab27cbcbee0521ccf339dacac74f9323b149e9e50eba2bbd4e6d6f21c54e2fb489236e3266a0b0db1f27171b70148cf8404324384b17013f43c45f32690e92d3f36d24a32aadf180d936ce5e22a2f52ee46923b44be6ef6fa2e25bc257dc63d0b5ea5d6b7f54ca1e233f12d5a6c3dfac15e865f0aaa33e5d4b3cd44b62b3621c592e312da4c3884468c861b7e9ef5cf32d613a888320b8d506ebd336f404b7532da2bf93ecef13402278d1ab0c3cc38e2faf85a00508c675fc1dda1d407af9c9c9f0cbd45db155167fddc2a4fc702f7dc45e1205759e50691d2bb31c9c6ee900b63ae15f43c32c99c3d7843606f724374933b22405aa81700fabec8d034a6453f305f68eecb3883a579d90febc625194b9a84034fb72a95386aaa662e6b2b1e6353d5e3416ce3ca67e7463845179c43f633199ac4404ccef2ad050cfc2b1c575b368e14d928a0759f34664f3d078e56b17a027f3a62e19cfca5bdc81e5eb0cd7f73aca6a40adc8c45fd6d3ccbdf78362ca2f9e8e2f07e39b571dadfe9b08a959def576d7d7036b8421d45ddf888e6a0cc384bc57126488d1ec6779d9469f4494e60d4467c2e12223a7108ec564e2925b9e8f5c91119c4715a42035b4291474eed855cb294d69f232029db6b03b1a1751d34b038dbf934eb81d46c078fcb474516e9e04e72c4979478a2fb8697715fb18114e0e0320c3bc5e79835ebee5fb3468706b1d93d96fa2a3373ac7e5e28182740483bf5918405222e13641475310e63ac60295206a11fa141d1014101a4fbd0565e8845760fe424294b458a66670dc144aceae853a3009b12770926346606fa01cfd1422cd536bd7a34849d6d331d7370dd0adc365ad720063013bbf393dbc3abab4986280821944fd81837a65850e83eeae9c9e52c5fe403eafebc83775203a14a3d0d351cd0f0b40f6a9bc8340168dec8e6aa8b588885f687a1758a2a894ee7f7e5125d522cbb5f0389764815f3da7467e0e88f3c03ec8d2e15bb01ff53e5fbc22fb4211fc0dbc0472aaa8cee97bafc190b33b3cd1ed1bb281cf6c19c94617c5f496752448656e1abf3e37c3946a25845c2efdc8efe6df563c24334103132586c1aa7627fd95707308f33a693e7849698804bc99b6b0874b5e8ef94414c3320406021455faed68f05376b6290dd288fd50227966070afad681cfbe241ac50155ba00279adfe2a6d842f14c6fe54115efdcd503a264cccfc7dc9095ae909fdbbff6bfb881679b9e3080c4aecffbfcc043d7c2d731a652f60737a209361500c02e0099f2e9befd39d5eabba98626baa854beca1d51cdde819368b67195d12a0c3e7f0bb7df218d6ec85ef0757e764b87d00674148dda870d6e5cbc4d1476b30890f74687b7f64378da481c37183347c4143b0daa1aa4c6cbe35a98d9d61175204c73603e6814dc6cd00953b0f10ce6a5b3cbbba2e703c9fa0879297458793d95ca1575e03ff265f9a9b569383dc5e94df14ba7204f34aa86e51c00df341bc3a8eb57df6b135f16b01bac7da6ca265af0e8ef8e53d2f69dd9cc1b84d0a1337a8cf0901768be4d5a23a82deff1a7f68866d9280d7df16417bfca2f30ce5b117c81c7a2b2cf789ef2372465e31a9524fa965c0b0a2da0c325d0f69720d25bd49038ab1cee418a6e2d4b8a129f9dae8d07e64adaf9cea2485f477f03d72609bb99b0ec600af2a1e0a11946d18910f495872d1fbe0e3090271c03a9e74095da90234ba249616044c3d1038eaa3183a8aa9a8c84d3b673c7d840476830e244d1b19e6fdbd6c05bcf336eafa2128072b3d97dd345061b144c4bbcdafb0b16db59a2bfab1b847ea40d183c1bd7730787bc1f91db15e7b758a966992baa6837608eed4519ba45a1b611564788fce29e0f88d5c11a26746c6ee0a3e3327efd8a31774adafed080d4a49f3b0164903009f97ab9e532770ad0cef7beffcb607216eea8026fb497d6a48e4c0d8706282abeacd1a58bd876d39e5ad8b54e78a5f28881af27653c3539fd06f6d0d66463bd557441440469332b0272793e782eff237c73575fab3a030bfa284526d95d89c1b9c041ff0b68113ba713e191e2a40aaed97bfe44420a1b0954480d8fd5db81aa31e003afc0c292984250ed599b2df105124eb7afef43045f617c544424b07f241d151509992b6f19c09479a5c92185165bc595bc5a2e02b72bdec2eaa386667911b5a54d2c1f9e75886ecda5cc1a5af1ff3203c863f98a20f342da55da62b01ebdc48c93d3cc5b72171df8fcb62b4bcdd533d90d493b07ad2428261984a075387f702adea3ff1446183ca1bd706d870f82d6fdb22c8ef905a7cf0d34ff57570eb262cfa2e6255c331b4028c215f1eef3de545e069eca52d5a3725b57091296aae4b1ffc87987b28b2e591d4717015e6319c4182193e4269ec515993a83aba7c21b935190e56dcbe33aec6d6b501384c590fd7524464c370043c2becd636f574dc18d26ae1c27909b428be71190859d94ba77d6d64151825280178286b0fefe7f6a5cd8af505d0ba29c05bea2556acb0fde62878136fbd99d26e290816ca8afa03477d6be4b0eb4112c5bb7dfdcf962828bee15ebce9ad9bd001771241604289db16074947eaa6201a308eeacb2bbb75360e4ce80c32a2d0de4028c00d3a9ac8af175a42bf564b87579c1cb37f065751b9e53e067a70683c34e02bb34b95736acf6b31605654b86810ee64512a751d395159f52bf1905d156b3ab709f68b94b976cdc737eb6d96d47b816e91230cc51ac78f7de223c70364cc2214a043d4fb9349d26e94309540078b1f1472bbae0e84893f523c31440cda29c352a067983868b1cc4935c0981a7674a89c48fd3daf4524b300b9956e54e9359ca7016a9f6c29b1fedbc99ede995b41617240a0c9ab53a72b7996533f3584d3e2c6a0011346173ec65ec01b23578c0c358c0fc7af9173b081689c5930ee4b510967c602eaa2fbc28f56e66a41854363cad7b2ae0984cfec4fbeb4e96e5d559956ffa7fb02cc8991dc870b08bb8606ad9455950e9406c6b592f9d210950fe58fdb8ac101cff9f60f0b60fdcdebbb473122ef8730552e1479f462dad68a993a7dd1981b21634bbcbe9294d41493c87ffe26c1344572f38ff84900666250402bbe0fc1e86ef30d48521463e973a42085da45dd18ed0c3d59a56446cfe06fb8d63941d79a62f20715800baacf3fb40c769d0a7f0c290dff1764538a7e37f65a996939fba1d841ba9b90abfcc65e757b9488d38f884421da665881021f1c4365f1291e56ef5ec76eeab0d204c54835f82642a13fde36fbb80ebecfe4c8eebb56b13906ed4dd22829b48a0f530b6824a262be77b9171bd08d8c7154f5c0e90fa4691d8fa696a7835020c1841c5d549679d3473b4f07d3774390e8fca31f8c8f30c524a5eff708ec4e0f53b3ac9da378119046f14693918ff3499cbea0e0ffa0d6dcc21857c905c558d5039e1d0505fc174d2faeed90a3d1a24a80c6b50919e7f5af72841c03bd895ea3ec4c84fe85075c307e239f915c38262b98d49612ac3aa4ed0f464480ad3789251b2131012e1bea8efce99b60d8bae984ecd3f6696a61a0541d275d643390998aca62a9789c04de27e5644fe52182ca337614e3961df5ede92c65cbf8a5c6c0cf4045c229310dbfe9ef8a3a42fa34abc7a606e7e69466f8486615ef139d4e257caf13eaf634f6188bd04c037736ea6a7bf783463a23f40802f43bde08c80528aa6422f4b869541100a3ad677c41d28185c86cc4c3b509dc01dd1581a1f09635c547ff7b3bf792cefe3d3104040fea7302438492345dde3ec9cb916a01891323a9c34f5b38ceabe47c1919752dc444711de5670f702142570227e161541c4d1a900e32fc6b09b4e1cf4082e676647ecf0404c70c575bb90384078b340a4f4ae76c5c1ba1088bd518c4fc14d8c32e0cbc65c0a2cee3fc6edd5ccf2228f93a93cd622082c2cd4ee614776d45c67021e28ee6ec77e8c437f85d923ec6375a8d3d1500eaa5d465d08b9eee0ce59b9191dee77e77c37695a57a546d13b9ccf42bc8fc5bbcd2f70952921c20694411858e0225ea94d7d86d48e046833cd401b72ce94a4415233dba5b983e06a662b69f21624abc613d80398ac2177667f56489b48fb45e93c95636dc8c465aab680f040ab0c773e10e05cb129703c1000b5fc0dfc4f32ba4be3d9c738a9d6a4086c0706ffc4d1ec08bb5f9fa7df24a84a3924857410f2cd3e9186b42a0010fc0545b644de56e9ba2f209d9b6f95b8eacddebc8e2dafc10acec466e455d186e27443134b6e27f591f096de8bf365756a7eb12743c33ef03531a8e950b450dbd170524385d6d2ac9469357be0fce6a3401f647c3dbe8e8343f13000f3b624ed16f704855cf53804dc2af0fe7210abd5319f1f6558d5ff40a4323601c95ad2661046d2ec327f660e6b69aedff825b90b6ab01f5354e03d653ebea6270688e20095c6492fb5ebb6268851e95f27e167b77b19797ddaca6518d7283ad3e5eaab8ca880cedc37f667688085d4a29c1d791e002d983b3c31a87ef5229d3083ded95ead402b78f716989e315619b641f11203eb49b1a451b00701383e382e080c84d2c675be7eaf48452879f88d7516ca04baf1d5fa54d610f9798cc7928de56bfaf705389d6ce4438a16b7848ebb9f6ab418369c51f72f5bc16e926628666dc63148df66686deaa9d45aa2f36e54f14538ce105ab35ac04c44567be15387643ff0fdb6cae0ce9c3a85ec15cbe485e0b4e96cb12de946be7319c3527b8cecc58fc1a92e687ca66005e87e91fa5622e978abf8f508316a6ca220445901048167d8e7bedf98347600101d1c120506002028eb1750ab4c2be3473d6e4d0aae7c2cef6f8577a200bb1e6554b26de6e965c2594d76ed3167b341c0c49b48946902a4d808b82e1a36af0594909891cec57a4486581e37c04c89daa2181de7104f1c8308604ddd1e029b747f9b7af0f228b8690cfc0c9b68c5a34fa7c4d078dd5577bdd7bf443d9cca7823fc1635b5c0fae65b621276768c030c35d9d073613591d5a7916ecf5af7cdc0897fb68432d9e93f25ed5318b86a6a0da8e4126a42a011be885f0bf5df2071412bfecef2b8cbdfa90bb7e2ae8702e85bfff1e732bf541dd2cde0f5e39f498b65be3b83933cd624465b722a4e52e3038d4efe310be15d4735ed0570be421de546880fff9d502ea49ddb98dec4eafee9c93fb1bfc77461fdc35fb034092401cf74a125015b6f09e52eed0138c4ecd49ee64380515da49957512dad8ee044eef060b7b1debc48358e596e49f3f82fb38179e4d3fb25cd99af884caab2f6764613ef18030df31784ceab1bb40e9382f4aa696f334f908a370ddc14b70d316e9ae93f82b81b2e66a5742bca027acc94c7f1290aaea9130b9ee841123dcfcf6abbe94fc70621c8af7e09ad8b3cd3258f3621e1622fbd99c661a8431754dd49f7dd1bcc8a40c81628f2c0df6e23fb897841dbc7b82c2ab877739ad4e43f7466a987c20c4a4bf0e286fd1a34c02194c46280ef30eace9cbc62dbb1c21be78599d20979a5d8a4ecb31b5e8125031da50d8b3e645fa062639bd1ba340b2e22fcf06ba4bb2c58424ded7fd7c35068966bc859fe48fbf1c2e770cb89de049fc393e913a487a0c228c6c66f564f60d4c248247a51912a1b023ee9e92d43b3900ace1e1ae30201bdc4f59a8bbe512220a65cab16b6e952e0f98c7750bec33a11b96bb3eab5b03b4e03f338748eb3482e665a6648a6349291d068c522be61da3689a75749551c5c9e82aca10e62b8f05bc3e2882d4a30edb55d8f1b3ffa6f2d10d26f52b9a38e590aaa0115e0992e3406e09270f03a3093b26d5a9feada1e7aabeeccf8fb128be603fd7313c5e9754fd074a6e533d6033b62625ab2cbe7f47642cc719654b3b96f7aef73402b1241399e6f87b63dd6fdad57aa28caa1fc35dc11d0f6abf552a2a6a12b9739d226eb8029cf4a6b57114104a492ad170fa75e807f24c175ecd20a31a3205e51248fbbf7907e1887f013634fc21fb051d6036cf8f6dc220ed39e8d3056beed5f593fa00357b9c17327d14c480683390b57888e0aef319fa56805642d9c58965676340629f1729becedb473059650264b1bf2d6e595e7eda059f595a7edef8792c9e5210ab5322f509ecc5037e759e8242496f119a56d6d7a6b28985d7c10e23dbbbf127227460fc439cd8e9c30874c80c2a1951c1a15259f12644173088300c6b80d8b840cf6bee1f2e0f98414674ee7f323538b29a5f10736aa39743978479579f40c5ef5832fa211e315415578162ec1cfcfc0ae524fbe754e3c15c56adcd7e1d1dcd0cef8b9b7f1278f56762068242138372c5edd1f883f9ec903a12333966a5c71f6293623f7a63648e5e3d7924569765104b58374ad2c2654158b1e94c3ee075fe40b20c52c3da7450fa7a3bbd2830652121f56299af10a5739b87328886e26d810334f30481695bdfdd04650eb668628b8a487a6f045d423a90ad6fbd112059bc812c9791f9c4cdc9d68cbb791b19af03cdb7756b30e9a0d7041b58e091507df2eb039052d947011eab0a72046e9c55bc1a45f708633ce5b5cac508e2018c9a512fd48891f21888094e6ad3ab2bc8cb4ae8c804e0ce9ffcd5004872d3045cce3680e2f676912562a57f6260778ff8100ed544454f6b672e27f5aa0505ba5a648c61c13eb16579c76fa986fd82e6cd63cdfa1606b17d2004f643ae1b0fe0b13f3eac65dfe906376bb02b7e11c17438ca458b01733af6915ee5b8690fcaff7c0af759dd167b599dfc6cf9aabbca6cdcace2a94deebbdfca5c11e917e64cf961cc8c29168c410eca9f3f24fe23cacf51cf6c8317f69c2601b90ca761b92f3e2c063f5df41f9390af587acb014f8a19714c8de40dc0b234c71b2c5a5b8382c0cd9cdd1fc6e1690d48cfbbd17d60e0f325ae3feab4f9b7ac016bad58e96687a308263be84c924c03d663617ef8b2f88ce62381935dbf9a5b275a3ccebc298080083cff471d819e23713fe105c30c203e9ff99ae052fdac47aa6ec924e5a53831a5fdce3251ee85824284e0b1f45676e5cca603a45405eb98fc64f4bf8003d736e869ccd99c66fb4f4b4ef1387e0a145d81eeb6acd91d7ef274b60470a04a32d43fe53f98c2475149ebb0a425a3e87d8a16d664e5073abd6ba43d7748f54757d592b8c4f857b51f9d692a5a1b4620da0d95a48ee1b2626b75cacdb17a34264f91aecdaf6ea5e1a2a381a92af3546c204fac86a40a2f01f606264f4067a902c4261e1af98007c2c46e3148784b0bcd7ec6176197d64b76e104d7236eb577cb88ff4a3f6530b4bd8583a5d100cd02db88c64b6f5a2a456284cd022cfcd2981e8c889eb54464ef1650c1b0ea803974e05d727bc01723ec65d5dbd2c64195bc2f02acfb62165ef97ebcca9295ff6a31bfb96ed8b9c43e70b0e29d3925d43d6d07c1d058398c570da909dd23be25bb2c02c1b9a7f1885a5d0f7bb13317c76a772e698369948411a38a4a8161d0dd8be8eefdb6314702ff4ca8ef02da3500cf1b399dd291d086f008708614a1a5dde41ed364206e13f15fb00e54e5e4fdd7580e5bb09b507c542b09e2f46a82d558e6c640df3164e51d9aac1864cc9b982d6c8057ae2d6fcc31ff22533ccbb95a2df33566e071a306158dc01efd77ca1ff1a7b661af5d15330f2ed55efb73d8008efe6dbea3c03743cf21e8f5a6b0828900370badcd8df6e51610c725378646c5fc300e886b22104343ea1de191b41a05796c850e3b59c32983dd6feabbff874253292c6e464e00442ee1586b0b9452002650b7e147c55ea8aa6ca6129ef227f7b6d7c13442e7b01f48d98c47870328d9f961d9bf49911861d935cb18ce5246b04e0f3058d33e46a9f28d6e156dd62b5c633440c8221d71a5c12bf5b6104acea94e69955e2119ddf65b9fc2ad3d5a0524ec4fd765ec97cb9003135ce31d674d239b0a16a59b8021dd91aa215589618ed0b2e3fe62c57f42d1ed600707b5b764abf009daf8ed9d54d458a4b7d91f51a8961229a4ac01d6cecc9d9c1e0125eec0f2ccd9bc400e3a6640a1683e8596760ae73bcd9469bcef912c2109f0675eca2438a8313e7d4c06e087d1f8a07d8f50fdb963142c75e43d59801921701004f08b3ebee574a90c92066232a037b9fdd097e325f300b6f1d6793961180ca73dc58d9d5654cee84d62027f215736c2462d8e8694ac8039633419c563e7e0eddff8c591c5bec7f5e34d809d010301a55c65272a1d50418c7ed0edd1886e49b62e8e0363bbc8fecbc987fd5bbe31d1805251d88514cdfbf7d1a113504bf64133bbd776d5af1fc3e7785ba58d4334fa0033e20b4ac56974ea8b35bb11b8de12a3f2af067742f0774b2cc99645e6e3ff048887514dae88af3aac78f2257a5c627134c40a14aa10f42c8183d01075a6e60ab94012e58a7164ef0d714ad96b27d0395a7d9df22af6c02461e475fb1c03aca0e515831ce2164b70877ea4f359036289e1c257963aa827eeafd8f1a184f3dbec109f7728a9f03fba01846a9d7121c5bf6957a00a3c95e2e3effc5cc96c214450a383cc00eebde608a37f6e52c53802b6820b1208622bb870204332df8cb425c0230bd3faa6792f514462c0554c96b39301f7bc884c07d0b2b0ded407a6dc6829202e89904385e4a6c2d0bf3550ddf5007e5d4a18311882362ac5792cbf12b0ebc6aa7a358f5b18a72cf9821d7f804ca206bc804a0ae734bc86573c67e1df8b8e5f119274766ce6e6ef295af6706876065bf6b7535256444c58445f70e80901587d9b333a6f9dbf1440173af9e1632565ad9bea741eff3896e64f17d73427b24bcdecc50b80fe31ba345c84fbc7b3d82722a46070faec51879c1e5fd0b854eea3e09e2c0bdb009a06b7d5188c9aa1f904185bb3bf1fe192b7eef5ef7791c7c081d986b0304e3f9d3fd173af2d3bc06736aa03f50ba787f0d497509a0cd7a16629853dc84a8c852cb0c9b1cd72112120e5ac57f339af7448fdfd003dd04ecbefb19fddf55778b869d191581182c84da051f55169196193cd91a6dfa028b2e6411c7309141222c2d61947e5c5230cd69df1391f3312bac67045c1adfa0caa9022f5cab051d5302e36541083110d2abd78e7e1abb6222e1d488dd89a20e75a1112c001c4fc9f8747afa88516463aa48bfbe606a219dc30cef0af2f8d90fbed0344806558cba64997210f779e9e45f2b080c3499416fbb3eac1a978318ed9f4fd34c6fc764a689177105047697acf52d93e8472a3190c6798901b36dadae0864eed48165ce57765d61272031a229d6605be903e38b3ab044730ca7f0bf28c182cb4d892ecfeba20ee72dd13d7bcefcf92a053ca91ffaf301a486ad92fb396cbdebfb3920864116a41a1d9dd27f5ebb53e4dc6a6fb4d656a6f4678b7197132046141040add4b2a135c027da490c22ce2ef5fef5d569ab94275f00b9be8a1055fd0055a19870788f1f37e61e9bc24c8b4f1852ec1a2c3e622a73ac3aa7fe57c0d2ae1bf372047d1dde0b6e46069d71113e34824b9bfd6d7206d706b0df6b389a17441954b45665bad78ec6e24a5d222daa406158ee49db4f3d964f8d7d908a132a805b65b5439f62f2a679c764b7260c2eaecec3a35019a0ad9fa4b35be9dd113fbd9e3858ea8f243c038bf283856a14642f6cba12abd3b9c7df3843e148522d2a5906b93e035ffadf996bdf6c648c45c84aafbada85302d3053b4c9ad653a57490ae578358f6bbea9901146417586e9a11e57cf050a4d574fe8087db4b98035aa6e4c84c416f19832c0737f5578829dfc64c38ce32768adb9c81cc655ac1d12e19d0d696da098f8cc2d10c66a7fb8b7004e71f91376f2f35230988784b8e911eba83ee469078ba79d1cd8097e472ecc0d5f233bb558e6dcb17509010f71c225cab060d5991004bad2014e0ff4eacae4337dc325acb80e63e9c5963b7ad6e52012ef07748b21b7b5be9eb79dc97fd8820c848c8ded194317ba556b9b58e364ef82b6986629f56d4ff65aae25e90e94271f0825996302c5cf2833cb825895c97431e5f417a483b8e7f3a61c3fabec64f8b0688be30a1588d2a337a16eca94e4228e6c5e608586cf6dd13345b408b2650a62073ea6e3038a6e0aece2bf6800513041f4073de9d53888969ae66142d5dfb7e2f0ad8a99607f2deb46db4a1dce01b311608e7562587d1adb9a1e9fcb326025410c629e0aa2148a5863d6a9c76b353c67c43ecb158ff0efaf596570614edba03f98e9f55c31daf8a7f4d8200defa06055e14337a8db6af16049fdd257aba5f76c54556489ad91466f800751c79af635c37aefd3e665d57c830805428f0ecf2fdb2caaaf55e2d8ccb7f96219c2fd5306d931771d8e206a761300e50a937782372767f7edff86c5adea81bb92b8128cd17c23313bca0dc00db8bae083496e215a0de67582f6f20ceb75dac2bb4afd98f2a6cf9dd780eebcd6fcb66203bacfc293944cf93aa72441c23df722f07c07161bebe940464f8a8fbe6f9e3cea2cc834b7c30236d3bd6db17479b14bf63b8442aba68ac1e2573b79536e6155aedfb17b82417273558261b97f9bbaa2a80ea96273f1e8df597d91df824ace3452377c5b96a48bc8b434e838d2d95ec218a35799e81833c067de69ac2836a0f57cb37dc07396ae0bebd0abbb1072bd0fb0a2c30b2d9155b304754b175cd6d94cb4b44758ebaa38dc8aaa5957a9dd012aabf7620fc425d05a30979ef357253b1227b89569b685eac691d45aea740f30701b341b51869572430f1a1c70df06150df65eacc14a1df08d446bd51ba730056b143b086031a500a2a0129211b4f45fc11b311d37b0d9e273fb6b3dbd0f0cea5d13b3a67e5856a51af7835de2fa32904366fad847229dd4af2a72d3ec12cef9e301d8ea34b18c6531867e4127fcacfe419240b427c662b6fbbf0a7b969abd2fe6484fd6da591b8e03cbe21dfae0809d6de5b0418966ea510503b8f3f3599050b6dd7e11924f429dc69b2d1f82d9305a7401e716fbfd53f8d51b0b7fd5f3ec53eb0fe8b76062ed9260a740626f8ccd3cbeb38d646dee589075d039ef7208f83e3373bcec38b05820c05fff0b681ef98a384bfb1b933198f2df981e8445729247cfe186dfc7c756683914e5df31296b162623309c2f86db977e1acb8904f8ef9e3f3665826c1c2ddb5c3411f8424d040afc982042b8845f734d2a7c0bebf0f0da25684e54bcaf4062271a080c3a9b81f601378c9ab74a3a22d8b902256efa8c48c3ad3dfdb295c5fbecf286eab755cb8be7f5205ec61f17bebf8464f817713224da978b44f8dcb9a7cf720626b2a47aa3360b91d712477ed3c61220e6029802a904ef16cba696206f455781a71bb73f0127bbd002670c9ccf1f132ce0a32be28fec58d7418c1f3c5b4b0681c3b73fca26077a88939f4960ee07721f1859e33f9b8b2191022883e724494588741e2fb858c1c823b34c20c2b1b96c609cb396d4ef7891870ac572c5d5c60e60f6709bd613e3809764109305f01adae46105e533602452572f8690f09e8ae449ceaa82386c90d62c22c5236a31aa3589c6ac38f864238ab0bee3a86dfd674db6281557a9e034975d3da35976d6a3a42cd87494821d9dd5ca0cec2753d1a45f6f2dbae438fd077500bd3268389c57793af96d834854b5f02124dca720def1c66d285c63d316ac4bd482bc492147c60720ca0de0c1f5bc4608679e1226a1150a717a45c66468b04714ef17f88ad968169a43c80d6c7cfb8f50dde222eb957e7ddfe5ab517e8d17bc5aa3426097ab67ad2b6cb2d5a56e70494673a465868277ba7d2d3f5654d180fbb6cf727fdadb85260e3383e183ab0db365dd8b661a2189df0a9f93c0fba98640f5699859212933ede180d2056bf8fde789aa1317020d220b5236fb351112c838d25f3bbc054a197e76e4addd2b0edaaa3ba1eae32a39aab52c1a40d04f85e281a69e5f24502deabc3862b004d6ef22c6ef0e97bc0b7d3abccec9f40517bcc06cce9e5e83ca14229120ba21b1e4672f6a60b53543adb6013fd60c89de7bf59622350b5e054260ba5eaf461269d222dbcdbb3b3b1ed46269929396bae63a0ed98db64a35791f5a307c308b242182d1f881335e7470f24145446a9d71065a02759047d439e714783dba45693f8f9de0e490b834bf861c68d9fea284f4cb9c523efe3f9393591e25ec9cfec12f8f37bc8e42dc3664678941ad2768c73927711dea2f42af9256617bd527f339c68c3f95bef715b442c3eef239e07525cc8e68d2590e6d4386b111b42ac60507b9c4048633a390c89ddd42feb4995b8bcc71d27acd4d4452d071d5f1a7327f51b86b8b0a747f1b46faf80789b81f4c323f67256603040095a34d6477f47a17098d4661829be1be804e3d69e95fd3a0a3fdc4457c76de7908b73dfcbd24cb5cf69041be019449a573b5d6a972b1dfaf351e4c20224e70323cde8986d509413cec84b017c7f3de1349a6b78da62b5389060f37d5aa4165bd11c7f6238de1c0c08deb6c760e108897a9e5a53f281e8863d1c4758dc0a15f3e3e927cafb6f5bdcafa920bb86924c119c44e1a482208278e222140bac402068280953cef01e60838b1da8898112ff941a4969c496309ccb300fcdc7ed72457a131f67886587344b15e2c0b8753faf395b432ac1bc82af3a2e0967c250532f0858fb4f6bba4479952b1795a2164a78fa9e3aec4203aa5131281faec613b4489109bfb9d1a6ae516af4f11953d65f3b1bc15f2d66a46ed473caa07e321e697b06483957a0f1aad5dee791b005fc57580cb4b4c0872f597d7d7b5ebe53b9c34983eacd97a18f83cfc5697f8f9d01fc280f2af023efbb1a795135a1fac6d66ae10285bfe7efa17d4911fa650b088dc6e393340ec6cbf9879649c27f59733638e4c1d260fdf808ec9762254e6cb8b66e2694933db23a8052f11526c75083f212ce0b63831f5ffd93acb91f9716d042706c4d6ce06350b72b341cfe54fb30721a97e5a2e203edbeaddc482a634efade976873fe04b93679877fa4e17e1ea812930c4a755365466517cd649e53f9c7dc99406f9687c1620636971419c85cc37539c80823c9c4472e50a7c5e95ec1bd725c02bc768d31154bd70da3b2e7447ae8b438c6387d772904551fb9ec94b8b4ff52c3d9403a6421f946e10197a016be6e401c531497ad9ac3317465d6dc5f676bdecf28a67b05380d5205f05dfcf1ea6bd031f704d3819bb332e8a57cf1d74d66c82470db235f66f96483ee3b6f80b4e6ec1709f26c59aebb28d54b757a3b5e7d4b4ac6f53f596c6d9f0deb1f2ef343311bbeaed4320f22837114d58880d05b250203abbda419731584f001d30e633aa6f8395c04e1597162a0c3ea572e58c5b423eac970b180fa25b770b9380c120bb37e7968ad5da8fb6a58bbe1ec88f368a628a8cee426274f5a20134d933d785697cb3b58859573addaaf3e0adbfe6d182c2aa56d8e52d0270c2b44813560bf3739ee468ebe74e1e8d910885dcc48504a661506fd7744fc0091f8f26d08893dc85b0fc8917e273f129350bc268ac897f1792c2752112c64d3f2c18b4e3fb38782c3354aa0b81689dd087d742895d23c98aa26727e61125f764f4b78d78a6a82c543ed5423d88e395c2ae7e279794d67a6159c0faa5017b0c4bc03d231d53cdfc3abe089dee5c4209fdcb93acd3c802bcda62170d47156adce77cdaec5b6801e07adfe56153b80325d3a37df6fef3bc623423daa63640d08dd15a8b7a2a4f4650951702fcb5307980ef1fa87d53d874931b0d4e7198907b9c0cd1fdf87a79111376184e88554b08143c091235197971cdce96407d2749fc271d286c752fa1981c2871cc049990f6eec5c51a657fcf4ee7862f578562df757d674f80edfa86a826d080405b1ee0246e60e978241a6db45ed6f59a90b82843820294e269e2ea0e6066345992c4a607641c1fb48eabf528b2cd2ed3897fe3be18992734cdf1a1b6a0497443bb6a7688f70e0b3220c5d0c73569e889976c70e0cfdd9111ab1829919d9b8a840e6435dcd7274238cb8689abdba6a0b6abde895655020f6f4d28f7edded0ceedb8d77c64b098026a210ab6a5261ee66b6d73a4369f8001ffcc67b25c6760fbd5a8bb3c4ad8f3f50b00a85ff61a8ec6e96ed2ca5aa537f9b115af98f82f46c15780f44a57409c69884bf8ddc48625c4376874eecaf928426e751d125a4db14af2737e227c98ba713f69909b2b46ff00cdd9c58e647d9b9e1762da520607e8b39baf9b707584ee277c55e51b8ea0e9cb039f2badc3b1acb230d6da22f8525f3054418711c66c69fcae312bc3808f098c17ed489c9861702ecbc2f9b9f4e48bea2914c8c158d1804b30df29af34522307832faf004ec720a0abcb5199a18ecb816e6fc5a14f5090a541d264cf9dfa83c31ead138ca5615b8b72d94b321feedefa56e8c9b4baa64c358a974f9af29134fd304b798243ceb3477b032f2c638f73260c9685623c57ac0b33c450821c78b6be2f9917ef5779b137c3a4e06c28f287d7645cce97c8891825c1d442cfc38af9b9b6e452c90a71f27b55388ff34a4f56b5c5c1cd69fbf19e7947e1aace7b60378e375ee65ddd702f986d9c24c19d1ad712026fe021b3e98b7254c64634364626b2ec625082138c9517441677e0568326eba12b939c04e473641e49c49eb2ecd72bc0cbfbe8aa3a11d91e2c32db1ab2d90dd16b001f8b4d9711d3f684124c6d9bc2a9dd885536613d295d11bc51ac7b2005a4d299a6952a903c06235b2f97dd6c4cb364d41e43bc3b445b4a2afd81b86b222729418a62806ee42af7b877b67f9a1b17ce61f9bc907f2be311b7d74f70f300b4ad76f08480d3b6aca0b48ca68a57a3930f54f438fd046ea82fbda6c6cc76a87ae52a03f006b2b75b0b6f4a832e53d496455c3a42dfa667a8d380a72e8a451812f4964b21b1a67afc228554a8488757a01523d59048a5e4a8b1ee4ef13a45aa4c98dc39ed77642bf094d45c41e83919ef5b28eb06e99e8cf76066173d4b6cf7fe5662d18a8afe4c1c26dea11e79857135d25878abc0a02ec408a62eda2d7a6f861226c9a99b3daaefb4fa3cd07b9199609fb27c0d1af0810bb335c28dda91425542db993dffb7e127725c5002cd793f33197fb82800a076178913b54359a44cf5328e149cde8c1576b83544eaabf341deb45f394da6230e0862a1c7c6c10bdc66358771b5a144dd2db223b595f8cc32bc79225034fc05dbaec1302904c7291091ea791793e7adb41ee4b22564198445745750b274751cfa052e18f550c0b4a60645c6cc6eb748a4836e3e75153787496ff230f6b5fc9abcca6a19ca4288c1185fd1f27988a21f8d1784b0766304d2da89be3781df4b8807fd735387a4299674a0c2e6e005fb953b5ff8b1f6715fe9c71404a246f1cb0f06e96c8897f271a17556c7ba0896568590e2aa41638317bedd969febc50112bfdff8744e43475d6974bdc6118a322f69fdd07493254c9fcb47dc2cf87d89a7cbb65e2d2399dab7667174d7fb21e427b39614e2963155f18b22ebd8ae875c2dcbd8e864b903c13a8d7d9e1fbe7b46df27820759b844fd5bf47cc3a5fe53d3d48a2cb344217916ece7e6c00cb847ed9918a887c5373da3ca224341025ebc9437b903de8bd84d977493f3125774bb0fc9f6fc94e7a4af7397f97d60356babb2939032bef769694d79c291356d658ce5441a67ea118ccafba69a4c8e100d730389aacf46a6a49023bcdd9aef4aecf81a3a0b9144cbe9cf3cedf8d8b4040c3f6dae8e44a38c7acf6e0582b709f90fe38186b3d82ff56b800867b1fc4102bce2ad5ffb856725b82f08a7974f2ac14e52f75422834f1c589c6177166ac552fea5623afd8587d512973a993551454aee3e0ca01b7d9a2ddf44b0a6b563ae335946e1c485a2f149787a4c6e410586f57b1952f53c75be5d87123a0a733d3ae465651a508f172ed5a1768c1cdf1470cd7f1b61fc76c67daff94c3dd33a5de821f08b6d128fc6e72ce24821ea945366e05f0cef469c064ae33bf48912f1b03ff9ea20a5e7d0f2adfdd48d5de69812a8a921ac48f7ff90b832819e2c472c82aa584360633c32185fe92257eed0161b2f8ac8d836c7153f6a51957760b35a1d6d665b58e35a1006dcb37450e0a6bc425ddd4cdb281d5254f991c0a193ae1c1824191b8fc29133cf0f180cc62b2bfee3718f875f909792f7c51c2a228945b5413e15d5515e721576889f942c97657382c29e6bf201584929f4fbd60cb1dedebdc87952e8b516c069a701a15e0bbfed1af9517de5e85ddce3281d07dc20f2a0a65411ca6d54412d5763e3f8443e889a7aa49a831bbfc9c0e84fb1f04feec07c50031c2fe91e2fb02ce28261508e48fcd42027aa03d27da747b3de5f95f95ec1439262726c9fb27a676d70e0ecdb2472570b1e70a40f853569be95cc57bab0d7e57d7a6904c749dd7955cb55d35113aa481be3b98effc3d662f7a595dcbe57cc00cd7abb613a31829756462e2a971b99e8283556ad0469a981590da86f1e14e0c7fef61ea00ddaad8441bcd631e9f68c3ad1e420d59d24d184a3c5e0d1bc3f230a4d24fd4430a2907aebcf28322a0cd9cb368e53a3420f121362dfc4843b190fa026f56f9f006978e6913fe3afa1ee2641ecfd52cb0f1f5f1fa68517d9a4567e0958255c28787f6eb294cc01dde4a82c7c3ddd2934ebde822a2cb0c1d0343a7e4cdbc638a9345a78f9380a96af9ac1131c6d3544e4e734cd33d4e08fe77fbc32638b88b7a61359c79462b131b4a53dc3be738de25aef21dce548b2b462125d6e6e167c409bcfadb1dd3e8fd937017be40b0e2bd58312b13438d9aa92e10148bb1b496aa41d818446d48a688b4fb65947977f6fcb0d6a113a053bbb363a85da5dc6f5f91d54db2892d977f8c2c2e404ed0d403c8d607a6035e915164a5b5ef8bd462a04f24073f528649bab27510f61af5669bd98ac7a15b3d706e751f217e4f4582f9cac9e42a310c3688226076f761f5fbf102f2c9dbe30b573340401dc9edcdd2b3a9459a00021959d441cd1f59b53b2ee7bd62e8382351bbe8b5e3f95e9e282bebe9c48f34a24fe377ca46f559c754e46746ae5a2ec32367eaacf56ea9360b92b31a647542b99da86c3c8a6065c24e5f9d1cac23d41b8810bd2a2ec55f4a52ac3cbe8de1d94916d392497c31c0767828883f938c090f18db508274a4e80d206f0aba97f07f6aaa59b062bfb7c7f5f0f9d381ceb4b9e54103be29ffb4efcd43d6a076e91e7ae3bb79e2b1bc512ae0e54648327e0e3f47d347f5113aec681dfc71800d151773c3aed31c03e45d7f9a7917f144531fb726acec0fa583849fa8d7c36a39a7deb0c8c150884985f402e50b7b384555610150b7afbdda9d5a46c19e6e79ec9e14c835e9685bca997141c73f701cce9d94f4bccd5cfa7e1f25a0411ac7311b08a6e10ab633782e8fc2799debb0454b5adc5dd77ad97aabd3eff8cb373ccf8032ba461549508d74f7062aad45ac7e2012a07bc91ce41a253bfe2463de0e5ea2ef32186121aa3c65fea6c667c55304f776c621c0d99189ab5bd761deaaa021c011a6681084af1e61ffa6420b2a3ba73675505e7428138d47706aaa4b630e1fcde009daea0a84895a12bc1123826f9d69d0f3670778a0320039bc1ba93a43658d41139fd7f296b863e6a6ddf6f5ab75bfe707f1d97eea9fae733268e17e8f703400169994590c6f2da7d55b7a446c4a59e50c62dfac0670f261a87374b894b405cb3623370e0530aaae36982f31f57abf2c2f5de9f0fe8c5d2a226aba51ff0d13108b4f8c9d421ba9030d66aec6d88b0226974e219ccdf3617568483af6fb37485170b6e3977841048aa70bf493fd4a17eab2a20c7d41f4960e91a5af8d09ca7da9a34b62bf95fccb07353915d15cdbb238f53d6dc1b61b6c0ac853ce216c172183d971ebd9e90f19c7ed23efd3e642e95f8488bb55c8d4ec472bff9a8fa8accb8cc5a586990efb70f0b3445113479dd6fd517aa15e36de2204ff7c181ac898bd7129c17017d91cd8f8c57d01cb4ae8995f7043474a9e4fc1062d7f4e8d0d95cadc1a2f4f44999c02d962e8357981ced71fe64968fe72486563b6e1d070aa5f523abca9f9f99ac43fcafaa452c72684492e40ba4e8a2cb3abc21013b23508c34565318e4aada1cf6a645ae0906edf030b6b4936460942c603f5da29ca358473e7d08d0d53c1b5f059559cfae579bc3ada96ccf0fd56cb7a0b921e408d3afdecdd7cf36ea83ab805ede9c4cb1bd30ada118e2828899050829e0fa9afd306d53f0b6b42edb20014e80183d73f9f7d06c69950d436f8cb1be43a12a95a7b1abb94090d790875e92232cb213b516b36f3d9d4a7cbe67d55568fd095c70db0a52b2e0e18dff623b24735d002dad91ad0368e0f6f1b3d4fa3b2384056301a34e6406359f9d408e308c5b70a75db5f395b479ca2b001a2a5104a5f89c922cd4d4482539ddf20d589023bb0f07c70bd3405521b4a86b12036283a768defc7ec32b08a7e63c09cf4316cb6c9e242c56a207c422f4c97cbe0edbc2d3be4c7d3951567afad7bce6a7b8b1f85468f97d63e6fa833944dd5d8d31fd0c5ffe6af822ac9245432b75deeb6296058345f3a8ee36fe42aaef01edd48c541099e3182e76d1f19df1003f14eb997708dc25a42f46fa300ae10edbf73b45f748f002400f93de9fef9fe92c03b48e9e8bb41e92c868328c78eeb53e92908dd85596f04da1383a7b3e6194763d55187e3220780a7a20fd501b31e5cd32052925640135cde8219955aff51ea08c5973acba08816c681569be9120c785e0ed54b9659a071a036ea6b21ef874512193d0141863673402613c2b0ea3c6161312f0007c08161607bd97bc404ddc522b42abdc1a8af10abce2eaa93465d1ca1adaeaeb3535e269beec6977ae59c76d0dd5d5668c92444c02b1f48dc356ae6ac0c9891f597710e8940217ddda28f1d8c815af094c48194d96dec3f397a460e1358919defcbe6b6011f7e04670e96e5928a9e08d2aa04a8852c9ee8faf5dfa748b9b063d7013ed7c8c911b3d8f8da1f3a62a87b4dcb4da545ef17931d8beea0bfc86dcc99160aa56ecd047b54248b2f00ad85d38d7f693afee7fbfa2128d018f99937dce7094db93f6bf0eaafb4d736bb8cbf5b7fb84efb93150127f16a30b353a6a9c3d9273ed790d90ca5cbbea36d80555c025cfa488e25eba5ae7d5c566d8540dcad41c80be280a3def80daccb8be3958f4e144d8a52290017864e4d2fbbeadb30200f88fda044e0c7438d8fde36cc56dab54264ce91140c7cb7ca2835e28748aebdabff0f0c4ff606b69fec88aa00b5dfdc10be5c37db87e3ffe8c858207429d37d1650772c9e7acb15bdc46af3353219b0919c792334a74a3ad9fbfa646340a8961037ab4f5ad1879aedf735e6d092c6b778021529a342acf125a44047eb094a8066fc2803844994b947123c1d4548a7804a9a642a1b99636574b67531884f4068d3f3e84d795368fb21399462c01f79ed4c89f24c58755c8423fd8b821a96257874ce559d8bf92e8f00309587029872e9a0ab108f846a386c245dcd1892b5c3dfd9a86ff085478f350d12b4f9f11b0bcca23d26af3c53ffafc6120f653cc441d675ca61e6384a09a7b75b347b24409633e3bce09011330ca3e3e0a04728a926910a36e0ea91c9b7340c50e2347270ddc2a8da6b816507f0657d532678004a8fc4b13abb08d28a85596f293346d9d8a42a5a70f662fc9cb07a6703bdb242fa139c2d861a9575a8f6207a10f8eee1d6a0d832fcb39f65461e7e734be83f1795bad95d21646a99a9fc5f0bb32d672780c27fdd0bcdd9404d343a7526291ce4238dd835790efd1429da4bf874964572044e403430fa9161dba21f70f703e2f635fe0c77c846b517fd2203039cdcbbf8ad13c6f3af43af3a8c92fc144ad1b4f8bf6a71c1ce9a238d256277e93aac5a4c0521a150bbb01a873c8907479f28d820a51639650d9c6cec2d0885d7724955b670a346725a04e2fb3b33082e55c3191ed6f823abfe671ee28921d919c6a1eb5b11aa0ce8346d739ff81fdf452ae2d74dedecc6bd93c5d0b45f94d5cd1eb40b15ea3a2524197f3ebd198520ab466c97b0231fe8629a8c273399e17e7a23a7bf9b76c281555653a07f986c72bfde7636d4616592a953a79afd01f01286a30f83049e489b395903b0fec128f9f3a074d986f9a4764350f0bcd0e54c6a3cf8b29778b775fa95b50f0af0034e1ca4ed93c4d3206e7541a01c134c62291125625a940b12a8e308e2fab3962764a80735cfc823d0d08aab4728565565d1fc6a205fcdd42228492f1c3a6f17cb0f6b5f9ca0674a8c592f11fc6a98a249a52c1e8f548cc28fbb73bb6324c895f862b2565a60b31ca61cd461f52b523193e27c598477e46d043e87012314e7234649958b8d3ea9fae87ec089af840f1304e061b65736c7d8809075585dad0d3699a7904aa1a3a8cc510ef7d5331ab1aba16bbd2d94d080887f8ce7d5d42f4816d70ed637486f5d00e654ffe14d52c3081021a8cfedc416ba75d7d6b2bc7111729e2409f4a03602f510ad65b33ebea2e98a54988e063a851ae3f3c0ff43b8136206132793b4a3ae32ad5b60358ea4fd3fe03e4746a945183085c6127e3c0b7ae06fc6c0b4bda553f12c9538a0f9f0fb86925a00000393c60b2c3b410cd4532ec09b069c89a819f95ae60af26ec66cb619c502b69188ff2c7ac883445008b909a77b81f97c20e5027487020e1100223817a1ffc7ce380412c0151956c1884e72bf1dba0202a6b25e2f2ee1c876f2f0296bf6a8f790fda128c9ce581e5401caecb39b0605606d6680cb2c05b33e8069569f9150d0ec2f9defae1fd30af0dd01e4ca75f829c81d6a29080cc9667985b76bfbc799a2fb65ce1a43382a9a512bc0c4185d562977ded362468a8be1e9a3fcefaa1f6f33374fecf3ea3e4150b0dc108e5335ad6e317f426e4055dee4b0414ba9e6df0f73f270e3701fbe2031cdf2927723840115c8a08aa8f7072f303bd4bc6554edbb2cedbc12036ce2866200434117ae83a45e59526342d1ead13981147d1b91dea622ef50c8d1f378b005fc06d5ee210b61f92c6267e9cf46efc41b55d37b800f2a619c2c2bc255052399650e2a5a8b1f2818fef77d1ade04b052d45295a6b561d92455efc1981e3260cc568f25a496a164013bf83ab9ba1b17df021668be876f9b61a194cdb92a731269fff309ee834e8563d5ebcde36b8f801bc300c2c1140cbdc12fc28e978889f12dc25299acbd37b8fca11effa843e0b72cf0b0cd474e678844ac4a41aa39b4d6a0d4f43a0bc05c4034134e14ac7444bbd301677a2b10837d4e24cd467058d47aa21cfec762bdb72794a0a2bc0143b90596fb4a0ef9e67afccb35bec6c54f25aaf8d4edf6d2d1e73f3cc18eddc61cfc89ba29bcd1f5aaaaae0952e79f042a58ad68628d515d2a6ed0e9b10e5a42736b3172dad43e4659fcdffe275750c9be96506a066e6d1d839c255446991457248e3597dda864b4f00176959fc0f3a7c6f1bcc672f2b0ccd4a5ccd46ecdb3b9a63a05a85beff49e8f88e98d3890349f92207d338e1337b34472e09a0b098cb43c9c9a034882dd34d893a364f65131a07a2f5da74f592834db75a9b3066030ec30f5f28ce8bf08af571ff4c47e8af12c6196c85b0975f88a3f089143e835b7a1879ec3443460d62b2b60915b88ba0791c6475388f47c398d026d3ab1ee8dce226203e9fb13820fec0fcb9b34f82e16e60a8efc192e0c381a1417306bce978eb8d05e958931c9aa533e8d0116f32e0cbe982b0e6fff77e19ed904fd2ecede624924e8bff0545de88763d4decadb6b07dd6115e9d402b999da3ce19a66317c02215914d15bd016d5cc13e674c27a7d0e2221cbbbccb0092031b42d52749212c297ecf6c4ee983f48ec0756a318aa168b934838d3c3324a0d2498ba3206a7cf8fe2dd13a9a5c29cff5c905caa9469760c80739a75f67f28cd02299e57f46b8623484cb00cbf6859a4298e5117f1cc41a61d1943199dbdeeeef45040870d5d3aa48763433f2370b0cd2a52c4bcf9440c55f3492e2e65f3914b0b380a655cdcd48d49608c64d981a47de3933385535bff5952d9376b4287d3ead35b307663c79d71bb4c007e68fa298c080ae047064b2f636c189278a4bb97784de29675ad1d8042baa85a23f810b601dab69161cf493108bcf7f6a1f67c397db571cdd169caec777dcdda34abfabf6ea70beacb82593c7f680a58f8a09e988f845616d2a7ea2a15bb166d04c1d32ded1ddc3be2562f6c252a7e49de157a2482a4ae92715ea1461a3a0159a990fcb344c37ff81bb0998100252ed801d8b98b067d53d11801145e7a6300e104ebe01405445f37ffe7856b66ce4ca3ef3838e0d6439df1a4e86fe767fd16fdceaa0d20c74fe91f3bcbfe7078fb3cbc7a67e61b6ebc8044cbe1a72fe2f7015aa71bc464cc078b630038a555d985dd73c2ad80e4ca6bd372829564690c857f14f3671d7f4737d7ff5367ec68198a1569f563859f5c60b7443980cc1eca7e4a0419a6ac875bfc598fb890cedf31f430ceb416d344b7eb007ce3eb9c8483faa824c7313b84269850ce88471b05838889064ca57c31ac480678f38f3d94c14718995aed836bbb72986dad40dfe4a65a24f2da03fc25042a6157a95a30d815271df1a9248c74b1cddde5f6e2a8e6ba65edffe82edb4892c5790a2d1d4de854167a7c90fa12714c2f6e57e904a07904ec884081213bc56c66429fcb8f0660d4baf7a451d822a6ced5476bdbdbb61142c82664ef2df70e5c0acb0a680aa44ffa19defe76eeb3fb7cd3cc96d0e74806897e589f475bd55a0e5b49cdfda846f490ca54327d6693e964f2ccd8fc99407307fcdc79d6a51e587b2f0af583e092252c968c138aa9a43de5db8ef2713f7db7441a556b62cd1699b3c3c3e289f9c9c0cf8f063210f343b2dcb5d7dc6f246b4ddb4d1876bf1dc65dfb4c4bebbad6b49efd997ebfd2533ab47387d96e4f43ae3b636283b4414afaf6c990be5dfbec35d35fdc4d18a6530fb3e75ef1ebbe8461a4dfbfb4f697d52c69c3dcc8eb42a6c8403991647cf6299ff6d3c71de5db6efabc97beeea34f74d2177a0cc5560aa9a5b4565219625c99819f1f0dc4fc34bd01249555faa6efe354865da67b718a9e647fffb2bfa7bfda4918b65d3b8cbbb5daed8772ed3bddf4dd8f3e12d54894462f5c6bb3c3eeed33fcd27ec23052ab6297e95dabb3528652ae7b9209d7d488ec4b58dbba90e53c91e88b2dda212d405ba347b38436cbd60c3404a1826290cc913b4a3eee337cde515fe8299fe9324a285f8a9e4895862fb65264a0f0f8087991a0eb22080a1a81b5f7a2503f088a404890b5f7a2503f08364a173af71a138a0a78b225148e3bd1c87daca9924028b3e2ca7cc7a23964b1240e6d3343160d59b8b39ee4b8d039cf74ef332d1b02b283cc7d1b727517322513e9d3208c865d349443287353dcce618ca57470efbec3fbe931e442691f289b18f787931a55ca956a94777fa1bc8b13e58d5f29efde694ff94ccbce16946b32a77be7beee9c0e1947f94bc651304cc71ed65d068ed3e217ca5330eca461da9dfbabe3ba9317fa4ab8a686f49ad125cd0dce4e689433495f6c09098a693d1283a7eba2eb64b4b16449cc85424ae1b6a3bed3657cde51bed2492926ce76295f6c914cae26ae26381abfcc0cfcfc6820b6b5f7a2502c6bea80f47bbbf79a93c52e53d7e1549f8ba6eddd5fdbbbf73b0cd3e14aef7e3acc2bbdf3be9d708aceacab190f8943dba474a4fcf457ca4f18a6d387999ef23a1d05c34a3fe118327dde4b5fca9aeea55ea5772fa5ec7778377d47f7d2a7c384e3f44a385aef761f8de109f974fd1095b1d8be96acb985bc6ca150e157dc0cdfe9a8cfbb8c0fe5a42fe5a510377a7767f8620bf5c5968c2fb648251cb345e6ecc470cc98a99a583af9f991959be2ce4cc167e0e74703ac19ca40cccf9c2d76ce392de5b6a3fcf49a947bdc86537d199f69e9b8489f993bbdfbebd47da615aeae1637660ec498e832f37c8fe862f366a79d38b4f1a7a35e8ee8d29fa7400c65202dba0615dcd3bd7337dd3ee52a1db61da58461a5a7749d0cda60eb64f8e55d0686a5600fc350f009cbe86497b1291ddcb7bf381c67865fa6973009d7d4dcd7885ee39d7bcde8db4bd36efbbad0c813dd98fb0394f568c86b8484db970fa515794e94cf3fe5d38ef2653f7ddb475fe9a42f65ea2ef26228555b285f6c9dbed81a7db165127db1d5c5cc16a984c7496318895df49d8c19dcf0ae061257240e3102c547d9f203b78a2bb3f10f6cc59597178016ae7c877e48d5f3105ba45b207ddaa7407888ad9a1aeddb6bb297de3855b18bf498ede767ccb46444bf9f8f3ab4dbbf2c86e9347e6537615809cb88601b96d191317dbbf6995608e6a923633ae9abd922c2291da56f4ff545b7f6a2dbcf6f2f7dbbd9e7bbd297ea974ebae84be9289df454e35709c716e9a2df7b114e354eed4891020f4ee06447e9262ca3f312bd84613a2f7bd161dab7ef109d034a20e1890b68685a220ccb6e2c7d243cfa745ca2675f4ad3b62f25c23535a1d774afd9aebdc67b0df71afbecb1b5512153356f3fee9345640ecd4d6cd9d8ca7e4cd5fca4204933b33922ba947cb8a23126ad953597559248b0649784e879a29914095aafb821cbdec8228d43959c9a9996906575958078c3ccf017a03daedce0862c1edd129b7559837af2451799c562b1589946a94a5c913ff97451e5345d4e1f0e47e8014fab93dee9935538a20a39ad6e7a13264f70c3049ed6e91fac04318b214a5a76dac027a8a5332f714a0753e9a6cf64d267f2fd4c1e7d268b3e93439fc9de6772f799cc7d266f9fc9f63359fb4cce3e93eb676025c88410d43abd0434c2a0a48582f279b1c13b1285bc8edbec923caf61f19dbea6b1a2a06058095a51a54b5d81e8ec0c51d5f79ec3b3c4a851176eec398305573ec7c7160c4fcc22c9933c9fa387667122cf4320bac4aca5663847573c4fb36f2f8e9117aeec9f79d3459530fee40cdbd0d4e8a4224a33d1a45f77c9bd7bd7494f7bd7c54945b4a3d402cf3ad194807dbdf172afe86691cda2edf6bbdb760f257a2612fde2f08a3ec2212a775ff82cbf98c3d71cb877b7a11ba125fab65f9fbd5a955dc3ae2de85a8c03bf3498612737ac91b3d7a89fcc3294837db5e1fd667827d477950f9c1bee8752da2c6fb3c84c4a8baff59e33cfc69ca2077308e62035d71f2f097349ccfdb03f06c495c9720016b0c1d9ad7883767b613030080c0b813568823c4f6337bc12e8e6d44bbbbdc5b058869686532e6d66a69544fbe254e510cab2932909d3030d59b68087c7933c234b18b178354b2a521b53ce796f63db51074dc3b012b4aad0c5709e74b13b635629cd689665d9a946c4cd88c0a17999aa3ee5d9999167a785fc08d260cd3020ec3c519329f6a6c5d120fd15375ce5db60f78c46c5aee675b16b25907a5601c306701557faddb11a675927c4aca43102945393b29364c9901b213e3f82c0accacab31764d5b07142d59a932787489aec5dcfc62c91d56ca12d1db4b59a2a6a69dcd16c6896987b88c151e4c5460e14ea07c1d5aa8bce15f3c1751dd73dc41479b19143ce6957d765a74a4619623e9a25b4425e2e986649913435437a88e9e1e57ac1bc62ae18ce1671b55a1d86c68ae58ae1e1a3c60b1874151b66a3507395a3596068ac58ae181e3e6abc445a83c62a7a34583456ab154d0b2abda71936d21ce3ca6b344b7871a81c7c71bd7ab061754f36a8fcde19570554a974028971a3125dfaa5db565cd7719dc3c5b259d1a811dba28cb83d344b687bd878c111f323082c87cb7a396c34d8e2613b4fd6060e1b2f3796090f2bba34901cf25324c80f2137384790c07a2889e1e1831557807e04f9010403e2e142175552dd16635d276d78c4f49034f2864804830fa0dcffd16d9ccc71bd9aa55fc0d249906149eece85b4d08dfad260a31a943e70ef1e72f7ded13a49f5fdd436a359c2f853eb411aed57346a744bf40250184836aec6f0a033e28a6cd06bf0d9edd7e125bc73ef66e806d71cf57c34d87fbd77ed5ea4d5d60d5f1be98ec6c566511207896788376cd886384f6933959ecbf203bd3c4d9d81d7f2b8c71c230bf7d07eebece779d693527ad6fbe6799e77ee0b676c0dddf02c8b7cb8bdf33e9abbedd3b66bdb69839a77db2cdbfb1e0e376c716c508b59d34e3ba44376972be64d8cdb03c73498fd93c2ed87f127025523ba1fe2533bc46bef3c731ce5c4167ba459ea61eaadbd17853aaa491399a4c12a711aacb7b3451e69547d9548ecb5a87b14f81588caac1ca27245e57aed8b1905ae583c3eb9c678882d98a9aa625ca9a4c1fa0ccb9d06eb27852ba7ac4d38ddc88356e220f105e20df3d3621c110a793ee4f2f4a1691c71a50c37bcf9d6e89621ded2eebdf7765d4a323379a770c3e8130407bda49138b4bc41d2c8badddd6dfae28692c607874facf206f99b53cd4906f2071d0653b001142d450a72761a5996652846e415dc20d3537a148b434343938216b5414f2fcd810e4c34410c5d48c10bc400a345a3c8947699524a293d856e509397b36cc615395469400e6da091c525648f61b42267437c51051429a8428f0a8030040539fb8a358427677f3122673d446021672f856e64b11e1c81232160082264fa1bba416f2290f4a8c8e189460e67e44c256736e42c873472868224c09cdda2a0276747d9e4ec6f96cca6670814e4ec2356a6032a9a082253c0135114ba9145a146a60f152144cf8c9cdd339249968e4e11c418999e531142103b310959258a205240809cdd460186abc5584d105a90f5d414c139ab91b32ccbb2b39aa53ebac45c4f2917997edacc467616bdb3e50718b2e8c1177e983831452b83226755d080660753f8020c9d24b4b22539ab020956e4ec349a45cb599665d96807379663de71b133bc78b4f79e5033ac56356dc990aa0448f361013da19e68925920b23062064dd505828b18f445391114041a9bae77b64414bc40664dd3b4d3207c11842733d35059eb398112596341ab5efba34bcc5a56c49156c53c8125485a1573682f0a8922f754837278df1141bd44aa3dab59560f20834bca91878b2105f0220603d0c2f572e4e12227c9ed72e4e12249017eae8c1c79b8904245836b73e4e1a209aa2cae28471e2ea4001ee16639f270d185ca095c7ca1251f01345531d397f6420a99e6d873040ec5e962086eec0102498e3d401891b721c71e1300753c59a2c0f86329cd33356dfd992ad9627dfac90a77d0a499a40b00f1939f34283f693a30834c1f76900ea52a903a48faf8045dc004364b08a50f95ed65593f6995fcfc3a2805d75adad59fd962ab882af92b9c3801284b2a04fdcc96d8720214647de28a9ca1226d00c1556356835658a10609e5809a856679ee8ba0661965f90ea743d22df2c80f67827086d70a2f69b4a60f92d84e93243f4e0421cb276450b384bd24cb870d463d92e5659d42969776489697960a4b45db6021b5f0e9427ee15333443eb1ca611fe138eb0364e346e39721cd626f6c911b7b636fec8dbdb145ec8dcdb13b56490ee993e5b9d08dc638ea14341acda6fefc6ca11b5d8f344bcda93b35a7e6d49c9cd9125b3b4a787c6aac8769433726860123ba00e0e7a626cf7469b2836c6caa1a871588c70d9ed9626353257fdb07757d5041d9e736e1b13e1ccd7dae3f3eb21e4e9ef8110426a483105d3c10ad903d309338c2c429ba45a6817e92a6c11a9a2136f2ed4474f1403d9039c693ec236e91a9cf159ee4e863852d72687d7cb6f8c9d6a759a2cf15ac681612901b7d8c009467c8910a3168b20c51a04f9329f6000dede08189533be489c31c62f6c147dc22630fc42d1a89db3c3434b9af10db014dde22d74f1291370dde1829522409ce9128c0901b89f5c9c9d9d949d248942cf16162254f83f230e878bea027490c1189e14e2a633c2af5f80331e4e9cdeed460ac8127db49170d368bc4a913d56205c6624e6c036e0f4d1557b493a8ea8709c832f718bd85c9823c51d060c8bd0a23f7971869b01f672a82368c1134ca7d181905d0184e40d9438382ecc51772a53396819a259c4672374b7711841c71c88a0fc137d8a780912e3418b97b8cdcd12587987f40c5157a525c918fb345c3332da94d19144a1c197b8a6e1cc24dbdb6db6f384efba55ef6da2dd6709099e7e35313ebe85686230eedad8844c99e55d65a7b061157625a637615bb553a5bc64e947a79adec342f917934935996c52aaa2a204a7e5bc7d1d99ce4a2260a81c82f9c91e3b3500e5e68c38d2cdbb8f285b44a9c5c6bf5116333c680c46b73688e0be53033e56e846883f15d77e3dab822bfd0bb9103cd368a3a11271289449b4824125991481365a24a4553d42229baa1b6654207e4071d6e9e77d103c921e6781e628b5ef2f85e1f8c18b7c7c3f80324440b1720413daae2251039595681849decb595fdf0ad842c71b89a715483a89924c6a03b73fc0cdda89a0dba33b4430fc9d95f6237118c394630d59d7b97b7cf005914c5851bc626303dccb709721afc4f13458a18094216326d5ae9875570c3991cbb71634ce9aa473a6822884c7118794ec793a2c65092572231dce77005529ce77972b4aa51524ca49208273b59d2c7fd7a5493dddaa4cf5e633f7ae314c52e92f6793fd39ad9b5bf320dc3742e86d98f0edb9e5d510e8943db68cfaea3bde257f6118689bceedc76ab3d7b66394fdbba5006d469419f3c49e10923908c138acd913ba3fbd347bafd52dda62fd5da97ea6c33b99ab89ac8202020a01e69f1ea2d5a8cdb0f59150810d00fb3abcdbad61ad2b9571d2ac35dfb0764ee7b9cd231bafd0eeeda65745edab9cbd88f2ea3f3e2aee154cb8c6e71aa239008e20f21d16566ae08398434811c01a55ca6d2f6d7e8dbeb372ed53cc4d6a8d5de0f201a6e0cd6de8b42fd2048234392439b5d9f382f456688916232625e4cdadc948e3a9df4a13ce513ddfb42efbe7bee1b75cacb6c9145705c1966ad563a622cc678d0e18a2b73ce3963766b310f40329c6aaef4d36b4c4739c5a9c62e6edbba14ae54ea300ce5a6c34e2f95bafba5fa5d37a58d597d712a7b37c2a90c7bdfb68bbe947d77d1435f4a4777d15316efe82e4aedf01ebaf7d04cce707fb40c10a098a67c3af528dd63cb7b6c99be542d7da98a5d35f735a353981e427e4423576c47644457935db6b86e12ebdee91bef26f49d6cdf7491c6e99c50ece726d6a46f6c6868fa2674d15fba6f668ba88814e130fe28d13e9acf4b17d1377dd32de822fa265e30ff00a4def8a1e254db7881f911a45b9a8633591c25695347afe17eba992dd2364e1ee10d631a2c5bd97723d8ef5354f58c1b0dbbb6bee924ba88d73c681cb412d987fb4b7bf387c3b099968c4d9574d2431a049aaf466822cd32b3226da459ea90fbc596bc29822377244f8e92fbc956119c9c1d253cd927fbaa0d5da4735a49fb344eef74ac6fa66aaebc99ac4b4328994699151de5d34a1fe9a65166b78cc7498c931f4052020102fac1b4b5e8f735da4975db4aa34fd1b7bf441bd660573412e1687d709234362938397207f5d99f3eed29a3934a753b95eeddecb9d7681fdd6ddb4efabc3d75ff6a3761d8e81a9631c9a4bc64b5cba0dc3ebc2b199d17eaf73232ce7df4c9a07e9ff2c1745e28144ee9189d7baabffdde6ff81e86721998fb08cbe8bc4cdf2e83f2d139946f9fe9a3efd6c3d88f0ea37dbbbd8ed1ada69d3e9dfa6ef4d2b7d2a8f4eda4a75edb49df768c4678c7f6d14730edf7b1451a7da5ed2361ea1a5121d2a6484e698676e7822eaa507ab984d2f7982d324e23bd07572cd4c75dc6d73de543f9fd4cb75faa6ea72f551f4a652f7d4b94528a83524a51534529a594d24829a5946625d26b45a174ef122551fa55a9547abfd47587a19ca3b4eb3eee4b357e7ffb4c4bd3629a45667a1e37d248b7a4f8a08f4fa296b58bde94cefd553a87613a5dc2a97afcb2ef308c746b755cf7289f8c8ecc09a7babbe99321754f55d2b9a75ea47327e1780ea72ae621b62eaee941efd18be8b72e34a254f260a193fd05bd7c119a3c67509e4ea69db13c451867f4893e7724d752c293a74f9e9e2c325b66d054cdd9c4499e4524cedca1b8831a6806379c3bdbbc22a88a08d4134e27363c441e160fe6cedc993d789e3ca078eecc9daec1bd84b7c84ee835dc453b9ab5386c9a5463978753edfdb5ddc31b27c21268aa66ccf327679b2d5e1325b365eecc6edaed93adb933adbd77a7795a49fb28992df701dbc3f6699eed8bad0edd706defe6ce74622a993ff38a3c67153dd349c7162d21ea20a01db225c2b09956037dd3e666e27411ee9c4c9a25ec2e0228dfe91b9df4d7e824f0de843f55364e95bc992d323af332a48f2e53fabd7d0da574903efa8bf4d1c4afd22fbe53652f7bfec754d1897f78915e3a0fb1d50316838148b8f4b9a68a9ef48d7e1fc6d8c453c695b8321fa76af4f5177a5c0e1b4702149863e4d0b756a29d3ec66791a9768d6aef6f9f0f2dea6f13e1d40ea2cbe720c2b1c14dbe747fa16f5fe8338481b293af8b741291652ad14a9c14913c3eb14ce9c4892bf41d96390dd27358e2b492cdbbb36b1a4c20bbbbbbbbbbbbbbbbbb5b7677db8bea6f49109bab17128936d971b6a6e6f6a5e67cd7ddc3b0d0bbc33ce9356d7920b0d57bf7852ee2ecf62d13e14f76dc66cb4099306bad5ad4773f0d6e91ab3671e85e8e07d4a9cce0662fd246fa488ce6c5f5b1cae10766c255eede5de6ccda7de2d05ed4bb87e04d7409e52ac5edbcc3f4a13bfbf61cb2778dc3794e6a9f30dfbcac974344962e2282f5b58b7c98d9625bf5ffc47d1357ea43f76e441209e170fbdcae3d9cf7f09d1f6afbdceec3fca9c13aab7d2825f76aad943951552f71c87da806ebbb5a67d7dd13964570779106ebb57ac366a23613b9be999038f4ebb970c32ec2a2d22c757ece39a7176b6863c53ef44e280a1ac11f4184fc90427e48ef2fed7a892ef337c796ee947b188f9029f707704b8cce7d36b6157af7d508c16a856e635ba38732485ba0f7d116987d555f57347c61dc9adac18633d9e2983f32dcdac9195b52d1129ee8a1bf44384e5508a7ec438fadd7a74afb08a76ce83b460f61191d9911aeb161b5ba0b615bdc778444b8665e08dbea1e5b1e76bd7c39aae85b3542dc568c090863121610862203c25845a6bfb305e647e2e6a1b75fa4d69334190aee0bee15fd35bae8f222d14f18069b91f2a5d24f1806bb33a4df4b895ff7270c83cdd819aead10dd4dd3f8c1c34402cd1a1bdc340dd2110eae8ca3340b77da9d5e2a992d293ccdc2a45942a740d1a5e485c8f2d2a01514658a61d1448b08441095f2c596e94727c5ad398c403274e3be93e2663f82d450c4ed3cc369787dd83499c62f9a88c211b01006115f006ad1d3f0499b4f0060dc27cd92d96878862f5632dc50dad8d4e98cd3194e25ce8c2fa5fda92fa5e11f5ea9cf380fb13503c32e4e3db66678f8149ef1451a3ec3e50c34c8af741a3e199fe1433d86487fe9b8a21df5c9f8a44dabe869fb1c9146a8cffd2493d0279334d8207d0acae8933c9fb459c2f45367c212d0b27c04a81722486f634ec99ccc268925d08659ab06c15879f566d4dd4d273fa7cc892bf311cb9d06230f8e1fd927cbf679992371f0e28a892d8748225922f9b6772a49dcbef797e80244e6eef3f9cbd9261a8d46229168f4eddaa7e592c9f4fb1da5d26fe932285f4afef4d8227d2989533a4847f98efb139639fd1e05cbe8bc4ef85e86f4d8eab24fcbda2512890359d3aaf921448ca08efcf8ec072b7d2fae982fb644210dcb1c9b371ddc0a06d7b605940830b8f9b1a146534a299594de076aada596524a297e5981ac06a72b8c44c861244296407f3ed62c210ba6074ae99663cf12262cb511e7ce6740e858a64db46850cb6d93a495f4cee5473b3f3932269bcc16dba26f9be8a265fa1e22b2c03448b7e89698a9a27247f2cc16ee07faa2696a32c5400fc9b49443885e8a6bf397d2d1ede84ec23542bca5935d473bf7fbd40eeef7dc2fae21fdbef4e9a476e8db374ee9307bc2589283344e499be812caf47208a594def9d5a01bf64e2b992da553d3e99ba777327d67fa52db675aa52fb5e11f5ea59bce436c99300cc433ad9055c23a1d82a6aff4d100c38d2daed59deb1b7a243649a64b32bdc1992dd6de4b7f3fd27782d11830cdd243440cf490fb23b5904d1aa4177d92678786505bb48a9e760dcd939f2f32a5b28b4cfbd3b2b599f4c556e672b0b971dbc4151ddc8a5d158cdb8f65ddc5baa8427ff821c8cdf24066196872109bb3937e1fca941ea477c1348823bad8d3eb105950f4acc8928a200522c7a0adfb81c082e0e689551ae4bed1bd4f14fa40d64f4d5cee5c7d78999abfd3fb8bbbf7790fc374267e79bf1816a7273bcf4fcb327423a523f4d15f211ca76a845f9faaec33845753458f23aed07bdfa78abefb5834a54304b79eabe1a6f1a776bdc110c4f0931590e310ba004556a7bc46e965f6507ec391f6a53c8739eece7338dc3ecbc50064798e468a71acb8707978f4f07129b970c3f81379785cba1379e4b51c7ba0c8228777053702653f6861cd29d2826cf1405bf52f1d2324bac8cf8b20f6704d5304371408892b93155d4279561c2382084ed7d67329ae5fccb985161db1d08105686a9db3d67a591bcc76b3a19da15b35cb74e9af441c62bc2114b78883a4efc6e100b27c78036a0000c061cdf2214aeeb8d26f2d6e189b8820e24043ded0ef5f20b6b060a20b6509c30b510b395221776c61916608bfdc8f724e29e7074a1c06106f90af3400904359664ef89899899cac3bf372f6f8653bcc07913ae8e2bbb3ac3d309bc956cd128a38ac5bc5f2f7e44597ea925db6962d146e189b6020ba3421cbb3a6aa87980b441c5e30d1a5de47f2842c1044e6791f335319ca81c6184331c31ffdece7810dc6b366cbed97c4a1c61be2258f96e3c3d8042666d0c5568d7c8c57e9d67b6096090b2cbfb3de5213216ffb96e60909db431ac9939ab860e931a3138a438778c3fcc42c23b05e592fb86174d2835a7befa39356d95a71443de8bad8639ece5833ed2079c64fd3deefb2ac434a2badd92773d5c1cbd9bbfe428d6adaa95671d75f988591ce9a793674233e76ee7e16c3997bebf8b067e7b066cba4b392529a6913d3ec21a594ee44eb7516636a06a81c1f67a593d619638b26a594dad43cdb2c61bc8c93ce39e7ec8e0b5aa0546a418a49062d68215a21597e5a1a032dcc89238ef25352e9a493bdd560c5799b25c6497134d88f361a6c1afd15adda0fae0d2058b38cc2e0aa3cd3ea0ceaa4d32c3dddebd9d922a3aa3e461a7d5e5ec6b85e965846907a1bb789626b53cc0210714111d900446e28c71e17d428e17239f6204186fc9ba172d68344177296654560308a9ba98aa018ee28c71e247e4023dc9b630f1231247cae82044e0ebfaa2790544a2ae795d3e6d974d22a86541837a4d9569b9d5a39a2b2fa7431c618230ebf1c278d94ce397bce49bb637777ce6194360a3244dd21ae7cc7d67ec0dcb4717ee243b905503f8632a8e7082472d8345dd3437213c91da3a1a109a29b8432890872b408430c2ab0410f6243c8a1f4d9410e654c62d1319913ebe8a40540c8a28e4f72d7cc9c808830960862084bc08101931cca224608914389238fe48eabd9e24310648083253b4fb65085560b213721db196b42104f8c410b1e317e5a3d45ee5f9cece54682152a4600c1003f99126122532264c8f43674838239461c0c90b96ba11b5c4619b182fce5ed59e8c6c6534365468352e786a8972b9ab42afeb42a66ea04045a5d11c4a26955cc4ab069d56c41a1622d6092eded9b25a4ff28fd8cdb90e2db9843546e154185ac69a7a59f9c653c4520e9236ed8b19f1690868606081e1a1a3b5bfae7e7a68b4c234f27a42e58790576e1ca10e544e264c9495114f6ca1863dd682127e0450d4ce0042a827002103d4e94411453f8a6894c4f8375a2072567286591e98d0d5880844c8f0adda07726891b90a10c487821451450340f144540913296b08230327d4a8480154f38a2022504f2618cc1586b2d8ceb158b7fb960228d1a8df1f33dc4263e31c618e3a682ac09b3091fa8481423dcb630ae9cc748f923251631461a638cf81469157725c4adb13d7b26d1c959653fac92892b1fb1684a29a594524a69a59452dab19e2554fc4049cc162bd151634716d4fd4e344b89c32c561da02eaaa88c9260439e3f9805716b27133058b58cb3e7258e9f0ae8c5157a0a647f5149bce8327f85ae4f01f0b933f245e9e0cab7066ed7168951d4663c7d7170e5e365164839f6576badb931ea92727833496679f9cc8594e76d6c91a48c73e5a3ca6c25745165e200274f7ff28bbbcad6de3b43301603820f9cb83b477cea1463c874708489c9cf33450bace8eea69466b5577727c623d6c3478cbe6009cd6685c4b559ce9d314615f4102aa707c6cda1bd3f3589631aeca15dbb0f7d1e0dcec693ca39e59453ce4b1cf6889973db68f3e83135ec9a314d9afcc4f468c2c34793263051a820df0cd44595f06e3d37dec881916e8903144f1cb9993f11078e1c4bf29c3c730a2249b6b879d6501a2bf21444bac8da125411f2c431ada5d1e012f6abd962f1276d7b848a1f1b6a4829aba4524ada5236c1d3b35b543ac618e3a3c4df887059f3190d54661744e4f4e2662339311be4a9a494524a29b5d6524b29a594ca15488f70795e6ce0c841a9b53725c79e2558b841ac0875b670349e4e94c3a756d5c7305cfad076f40341672b23cfa3c41b503d327dd5e8274a5c49a963a044707e86700399be5a8066fb6dc3a7b8321fa2783ee4792e8227587e11661553d639e794724e29a7aca11b92ce1ccdd2b3e79c731291854b26aba856c898f4913e32267d60af4019963e71a51e003cb8dbedb577f64bedc09c2af53a890a52b841196a603ae11236b265bfa38629705a4038a086a755821aa6c069592c839aaa6aa7aad630058e9d42078253e5801a9e96c51f0d2f50997d5631a2a422d8cf6c1ad44595b0d264da302b1aa48ff93448e792e812338d5d6be9ec209a0eea201a1e164b071949b89904b61dce9b36d3264ecec4f521d833dbac26b38c7e4183324c812850a6ef9d69edbdf3872aa1c8d4c79b3eddf4e927dd12c21d9b35dd90da759ef499dc073355f4351c71a1d8bcf8dae17ed238b62a9c357348a6dd1de4136b42df1d343b1ac213e7389c4428928963278e3671b28933a5c8f491dad4bad32db932ff40e9251d22aae88bc834894b95c8f41587a84c73ba90e589a94d5ca19f5c1177d263267786120a9c4c3f71664b131155f447e4c4c0064ada2674a3876472c68c8a26964bdac6da7b51a89634422a8de905b4b9276eaf4212e85ca3cf3d71b5980b26ba94327d0f99e6ba7c0d5fdc17fbe1c81eb7e87dc3dd8e6e7b5703127706ad794c00bd10b6c686d5daf01755f442d8d60d2b35e4edadc4a79095b8c6d6c5c5f71e0d43bd6733e4a641b06d6e6ca88fe842730fd17ac81c92da214b452a71673645245132809be8c283835c95c835895cdb87c329b9e18dc3323aafd0b9cb78dfbe830b9df3bc875216bf369cb2dfb8879e7a710f9debcec9fa1a90b832f4e1a896082f727df7b54d6f32c8620964a3c4460229b119722381b61c1b4cd5b7cd270c44a14cf631f10a64693736077da4bb69cfee96565861450e591a62d20994859f5a4317eeaa4392c644972fd3f7882e2497d9b6e3fadb2605e9690d5f5c170ee2dae7a05fb1bacf292ffa4ccb1b8930ec7e34d36a199dd7fde832a48b2e733fa29d6374993f19882afa3801c0832b3ae9323342291da48bbee37ea44374d277884ec2309d1dd20a3be0e2062fa0a16991f09daa968e9c4662cb0be11da593b08c8e8ce8252c33ba5f6c75a21cd1a553de67b28747df2a4774e94c65e69eb85a5c999d3d9c23aed05beef3eea98b9b61f0ab22973ff50b72460e929d9c06737696d41c6a9454a0c9a1a4024dd67a882e33d3675996512a8f9044642a83c840f33386259206692c94e1304621d37758823c710f394d7bd29eb427fd86855bf151e04fd735721a046bf491adfa4e466a73844597104e7419407499b9be91ecc0a20bcd0258c2cd00b073654ed114c52eeedeeb87705fe37c9d33557513e3f60d8e146e289db48d932adae64b7243e9244ea18323b9737de7c4313861840b4b2801c517805a7d443a914e52605c0d770e1ec2adb48b066be3cc961cefa1eff01ec232ddb94e86bb875316df06e736c5d5b004c2d24983f5b80877c2d480c455d5fa6ef694dd54442986c595fa9211892ab5b6760521503124f4840655e8a20a08be0630dc50f2f0742f3ca28b448991690c43a6d18b4c1faf90e9398d4a9ed992b2b4ebcedd5afce2be61d8760ea7ec630bc6bdc3298b6d8b6e53dc8aa5120de322dc50eeb4f66672b51c2fb9d76aaa68c6e4d21c99dc28a5ec2ad2308fb8428f852b314824d2189dc2912194083231b129c1e0081f538e3d4b96a084114422a571efbdac958bd2d31a4b9ac8deb54a744055634224887e712bada75bf670db28addfb66b1bddb62daae86128add46a543bd55e1aa477f9c034cb8b521bcd224fa190020d6f96415759d1a55b624b8cac0b4417fa18558dbae25611df80d8e29aaa992cfbae596bad36f6d859638d353eca1669f871657ef20702ab42f0f9810abaa88223bacc56b7b8af1475b839f440a6b2d5511129ae4452a68d6f5cb93adc4c7f587b5936bae5029dbcaa32ca31267b8e115d648e4d5c54c84c0306c61583b9f75e18d72be67ac1b85e30ac55ccd535ba67ffd8c8227ee479d8c82e71ccd7a3c196f23c7ac4f9f81ecd127177ecd851bb7da77d3d627834d83c9a45daf77b584dc6f0e8d119c681590df6bf9706adb0c28a1cdd3de2974c91c3db3fb8a1cd194d271f35d20580501271ac001d820c60a045567d2ceadd034518446826788191eeeeeeeea63dbb9b3261c2c4a7079f263e384a39f62c319243544f7592592a8c484204303ca9f72ddd979d861abac171b53ed380c9cebd725cf6ec3e7030cdc2d5d75aeb39bc7d309febd560cd7e3badeb05f39cadb8706bbc6aed898248ceea911bdaf823457df7f282ef6db63be77d355a555970fbe14aae7498fdfe61766d2c2348bfaa24c8fd4af30441e719a748fb4bd355c7d16870fb3e55f516b5c44b532f6eb8ca3514325373ca6ccaf53d82748ba8de064eaebd53a35922cf929c5cebcf92277218bb90118b5c3fe9aa59263dcdec8c08223c8944c8f133a24bccf2973474a3b69438e8434dd3341ccda2e16e1c0dea109365966326571b3824061b6c9ea661a359ba098d1e2880624ddc7e54e95ad34515ce4991b8420fd420ed2f32d56692a964ee34487170399ce812caa3df67f473e77ead440060dc89a495a4e44b13091039464ae2cb934a1876b19d2aedb0119e69350e9be7c6fb911d7d8461a393704ccd9e58f2ccb42c0ebdfd3b676862dbc9eef9d9fbd15ff7a36b3a0a86dd9b1ae5a37b6bdff7afd1efe8250c5b8d7034dda27c2f5dfa8bf41289849a2aad278cf4128e5355fae88dbae0fb46dbb8e38a761957343b5517a859681660ec9b21779374644b2b99aaedfd2e64e38624301f7ae82fed63d5d1c073650ed10fc19138589c9e1e239de6cd39499ff6d117e7d612ef207d8465644aa35f1c5bb195ea1da4df7b1930dbefb8279d84654cf8e2b7ec6db9433bb774424316985802354849a49209e5f6d36442e9e429e5744af9c5d7963eba45f9bd2d7df457e9238cf28b47275de6a67c32a593a8a4ac1775414c95a2190120002000c314002020100c870362f18040226b931f14000d83aa4e6e4c1ac9d32888614e216308318400000001181111d2b40920ba942a8cbde81c14c3fdea0a82824b6abf724bd5fff5abba7e36cd081f42841a3e8d589954911c46deff43d4a417d352a5d0bf67278edc51fbabef31ae69679b2f1dd7c53718eb4ee3afb8976666a8e4a974971f14e7bfdf38fbde0ca8c6dc55ec5d42fa48ad8d2ecca73c5f6996e2f723d936103f66e275c9145969712f4612c0b4531b223d07758f54d2fe9d5b254d19c99c68c975561f8a97eb15c2e93bec2a5bbd2d9fac9874f4cbf027ae16f95b9dc32c4f565a0d2dde3ea347a88ea53a6e693af227ab8074de3b2db55046dc750c71d88229e79b6ba4ccb7bf918896d0459cd1e659e21344a9c2c948c1f580b88168fd34359736516b624f824f43d4cbceaa5157c429c9d4203232548654acd9f60cf4d5077d4f4e1249fd985da0b1dca5d7b1915f5fb94fc274ae5616a5b25e6d5170303271e1b05d68bf326a82f8d260bbf471cbaf7a35e9025707ca0043f5fb7ab67a840b8b3263ecc7a7ddcd4ca2c30387d8b46b73f237b8df39c7a7e7683cff2cbcbb2193eee4b46756a84d1c2b465119c01182ea14d3cd3412a64fa67c286e7f82c3ef05221cea799a616fb80a57c5270400df974dea340696b8046bfadbb678cce68e7c8d00faa8552057e02b5fb713560ae861610cf83d2e4e6bf81943810214c7fd0402c44a772326ea421f35d8bb1800341d473566097ecf7143f5b041522aba2f3f8bca1d41f35ad19a653f799877d78ea3a01b13047cb28f6a8a34ecaed45ef33d6bf160c45298e1c6ce52a533d086fe5afa71844882ac39f71a41367c99d302a77f10f59b4bd36df93aa7b2d1552dbc1232add751559116803536225b4b0a121dba218946e5136d0e835a344bf0ba5a7ce6e16d03068bdb3a852a69785b99a7949d32f24b00937601115dbf53e1e5f7a7287eae0f4532a0d76323d926d91bfc7010afc08de1a571209d26f999481337e3352418b104402042636689f359abcf1d93235f48d5872853dc3f7f3ad2bd331c7cdc15c9d26996c706b6d20785c8e81f1f91cf58df09529a6e46d78a0955eed735b51fd8ddbb65e6aae25de354b86647473697135a38ab943b7235353d654ecb64608e63cb11a83e88a9c62b920735e21b7831b5c2f39f289cfded3b3d641d8e781679335c8a9079e768c77461e814e3e0021cc9f25738c7225091cf68dcb2006da4adb010fc4f8c0546d0e76c93b1b3224ecc30c4ff942c4781139cc82ff2293312b3bf47f2aefc7eae52618ec3c09fc1d3df7203bc0c850d1039b68cbddcea7fb8c622d6b9d9c07aab8a1e94e21dbda163626c5317278f0f06ca4ba80220e22ca74839e36d422158da641399e40921df24305e7e82ed435c376bb999e24e1260d285b6a889a0d7228a209187a20001d53b9d38256b37eb7b20a24fc8d251f69ccb4dcf071ea9432bf5f31467bf588e446c442d0a3fcac9456c150de2f062ac465c443a4b26f569e763cc131502652ff50963634a3b7297ed7edca438a27f163d318a37d428c664bf4e2f501236ec7c18c1d4c97aae59be2c689146c1d4d0d90e5552f8216bc9e2b6de2c813ad9c1b7fc487a7c1bc86c4d279e2870207f616f06022e5fd97eca0a0f0c05f5271b51f3d21e15cfb147bb692a72ed7acd5988deeda8b571b821aa63329dc7676b69b753e1e288a4263bd9221bbd1271b345f8a62c23ca5c946d31b0ac65ee7c057af86d8e3ae749202f030baa6ca2cb060cebcdb88593f0733d995bed518ad7b36225a57ae709c23847026329eceed3d0a0b680e29db470a3ac7ce5b80c0d368bc1a3993d949e9a83e1a4319b740b02e58ad6a0820ab7a017adb3d06d00c8ba74dc4ea33e52ebd244af9152de63303c1e2b38e0b66d317bf611b9fb039518c189f8db5f6a5502e2c9210746fba55065f4631a725041fed2511b0da29ec8373c15a5efa4bb7eba86802f5d5a1dc854bae79daf08254c837fc2b36db3ebe78d09d7950dfbe9a5405010ea4de4aa0807a1340e88284e10cb8d90a2f13935479c3f43df595b3440f2889b7f35d87b3356e3b162afe6b4927ec0ebe42392ec9fb999b4fcc2588912d430e4558d174f54bb361c4ff8ef4b163ec0e790f9dff1ed7c29cf8bc9e64f572ff7dd22eca03f609bab34fe18a4762c25593cd178674ea7b502874384956254f085b83fb3e2d7f7e318f08a01cf8b8c49f495ee913b6101418c659b00c640104ede37e2a3a4024733529d721d4003b5e5b786c29a7388450b3a641ac258ad9091b9bb5cff234c89b72c886b043c21cbddb5c6102d24264426ba7fbe73faf96e1dd2e69c50fcb430792c64a0e3ff061b0439e4c8a88794aaf6d299ee70f8fb3a79c779936d6fafa153a11be724991b0069a8fa52ecf46709a6c4435665917ec884383125606c7775c21a035430de4293ac57cbbda062ccfc23f546c2be6049383bd3f83964a411475d24fbd8c9e43a6712183b120f5009fb080eba7d0b0aa3ac2149a8229f3091a94a820c14219ed5f5914b32bf934fb7ac5f128218b94c128b2b691350239d536e907b74ac039f3f2e69dc721927668118607c1b68fc0ab47f85e79026b2ce30f14c29834ec3f14ce5fbe05d0f1e4e60c1d9a1ee42a79c41d659f2f24b0e1f82edfd56bbe680cacf94694a1760c3144333d55bc7f4d9529d5f8489e2350ef480b787f865293c5152fd9ef71c432a358a0829676949b0fbacc62b9b36f623f03ddffb1e3e91531ec8c128d54407a1e2b82880c465afa49d2fbf0fdf099a8b6807bb2f881ee22caa658f6ac1f1616f133d5ca35d2193abb8d4baac97bd1c4cfc3128866f8eafb35ca1ee82f2b005b8575bea9f27ec427885d50214f1fe6277d71b09b07130556a2cabf00ed30c9c20d0dd9fac0dd6696baf139e6c07ca531e1c8c6e1a3084b0e6e0d50ae7275a2e14140c8696734ef8fc243ed3bb27ec253e2366b99804ce1f90b094e00a5826500b1ec278068d9c5bc017c71c307331f2b332ecbea224bdaa14fa6357ab8b300d2dc81d97144029a9c1f87efd592fe367c51b3836c8ebc51fe6b8b85c123e6f92a9fd7f0dd62bcefe2b0f00a51c75250851df3e74ba4e678e5c49d88dacea588556380fce909cff8039b82d811e0585eeffc727834bd8b8dc6601573e8fbbfba51489b97f8a12d8fe4849761f6578df5ffb8cc4a5ec3a3cde9060385227f28411e8b282fb124558a600deed95789138c922928e4de8c03a82eff7fd4d05dc0501e400388efbd2b91920156622d7f214b60721ff5e1fd3f75e121ad75f76fef59fe249d860b505865d44e486c6114580aa86ab911ce832c155ddd186b5ba2279c00a40c89261e14c926b4490364d17d1559fdb6f79d73482d6b400d73fb05aa973a3fa096317964ade506c46db2aa910f91ae70267c83d62a701817aac49d2d8db3bab47cc7b6198bc75a327cfff3695f557dabd673448d45115a151138d2d23dc5a31788916aaf88f3ca1bfcfb1240e0c32ad0eb13dc1f5b6342a3090695c1a87c735bab6f26a0af94b57b5480d4c9b4e91935899a82c662457cf91a7616ff42d541ba3ea0d46724a4729dbe6f5a063af1fcd6708ca048152fdab2efc64e85f9c2f93054524e7a21a743ea9824712d23921a4a1302109b4783609a22ef39407ad727ce30a02ef619ce69d4886502ea8703104510f52d1d961f642d0212b0bc87d6833b8e34fb6f4cdd55aae01a63fa305fe141d7a6b20541e3054642392c0dea3984fb6f7b3a8deb36c1111eba7dfcddbee491bf7dcf1fb590aeab2716cd9993af260eabdf21e3323295a34bf5f9583f6ad63030ccc6e32a2025c429ae047640390374f01822fa23c72ce5a9dfbe386498e706069f4895cc518b49f9069d39b2587875200bdbc830372e4003d57ac6448bab4402224bce39b1d2268de6cfa0e1bef3232dcc66810fa447280227ba8c81694e1b55a060295b66735bec3b69929d90e8d51db54bfaf4008ea06e5f7eec487dcadd53cb657495bff8dbe03d74bad404af68385497799faba6eb2207b6a943a63b3f03c68c80f3275bc1a0bd332ad79416df200bbce6ec7c8551de0ba6939d0f932a171ce482f1de0fb926b94ef5685febe1610cc3edb4aff83805153de8ea2e9f9e5e332a9967feddbc29c5a00e17b75b7d59b2a6d53c249730a749b6540c870f18388f102b085e44bdb44889c2d956a70360395465bd0e34b40ab5c009f2896278258f705fd28c2f2596a79c10de9d6657336b44f658b390e4e4149bb3682dddd2b2fb48dab1750726bbe1a059bc60c0831589ac33f750ad1beca08f5cfcf0a407dd8c09b28fdd3d9ec31fbd5ab1fc54978bcf9e6a857bd794c7bdbb3a4e9298d07ec1c855ad8030b0520d42df0ab770317a3235f78d1281622604401195becbee5143bada08130deea7f2301e3b6172d7a8715e223bcf31645b27e0e558e5e5ebe610ec4a8b73ee667fba15728dd0e2234009a7ff9e6b96579a3d04fc84dbb5a277181fb379315dd0dd7d820afeb4cc69668a8690d493f3a54604f06fcb73e0dafa07befe56198d76bc7e67d47da62698b0f3b1047386e110e2b5d84da07ce4930f25bcd6ec27fb98c2c7e52f5a4e3922fcd05587c3fcf4649449b8fea7cd947df28f9da4f6506fafd229008bdcb101ff1ad4e0493eff08cf720dceb6852e5d40827881130e003c2ad9e54f15794eabae60b5a7d29075d40c7e9c264d7c5c426b00719a1ac3369c1fa29f1c89d192e1304542709ebfd8a15e724912936aa3cbb48ade181c70ae8b4635c3d604a9686e8432bfe909f6365c1ba7ca85fd3c424f9ccd91556dac0a137022b57cbd5615a189f8c4ac403204bbd61ddc4fe568aa1b24eee6676d8e1a50c4e4d3aed42f21d0b0aa73a806cfd3561657979d4c335be15f0579ef847107b0562dc126b454e39443267a441722ac106583e7e06dfbf9704290b659e9e11ec901f31b00dfaf26e5edec1341ee2bb3260a1872ae7b471307bb97c112be0bafc848de2b7e7860d62e517b6f7516b2c5f26493d6f7cc9f2de67c93e44214cb72c04d46277184c4fa5a31bce681153484768618cd68e147553953517ba55c0ea5867a4088e2d480d31ae67c9b6a3c6d6bc9e21a32c85d89a9d4733ec97e5390942daec9e6d357b2609a6c58f20a76b7e0040ca48d0bc2fcea5132e9ece05fbe4669e275c5cdc02de7398513c79190f3ea0d16410e7bec85e7b055beeec354deb090617b64c72f4075e5ce16e590b599ad61cd043b603f9085be0d6af7d586e7359016dfcf26e5e18abfc99b32e4915346d9b76309d25ee98f7da743adce963b25b7eabf7f976cbd9d1af9f2f46a287c8cb79ec0ea37861a60838c00d2ec566bccd4e812f0842d4db134ed77359df06959eceac428239e854052fdd86d14941c71bb9adc208551f39e3329769104e0c8eb24742735b828c2e2247d8d6453ebb881ae45ca8887521833b545886039302b9ac39ebae366307a4d9d9d3f4f40276d073282e3c51e5f510da1cfdeb6ceb0f45bdb38a64320810a8efd61e0527d6290adceecbd775d47ea2b8f51cf6d06b68516b2dc305f11b656f6c3b6d8da64d4c7741a2a6c056cb64713abf8bdccb655be03cf8292f271ec0e8a468391b4dbf66d975a8005c418b8a56bca370a448b59aa1b37294616ca10c59fc130d7187520dc90a6b6864d2b7432d4ac0e3a7c87853c8dacb90c9630d7c2272fa499214ba803da96f7aba412410cca108698f74e0b2513748833427eca73a3e79ca3c54c6451611367efabcdd872406d8d3b28abbd91ab70c0ce1542f4464e957a59993dbeb2518961ee07201170b1049a010c407c01d9643bc42948e676f14b6271a416e05cb140832cd7601a2906a16876605ddbf5228a00b78efaab6662889966d902bfa16467ca55db9c9dcbbf43fcf4ed74d7bad523288f494f1c3dab4e2422c0ac4fbfc7e7d8cfaed6f97981546c5171023e460c447414c1bb1fe1807374da41f78d4240c54885655ce55fd4429484da394f1f7949b0239512fd02c2ba92398bc4d7eefdd94fdcd14532eec727be4e5c7bad11381c7c2544a7d9b67a5826f11b1e4776aae6eb636c2d0bf32ea995bdfb803ffd8a7a565e23587cd66c3986ae179f6b129399f286aad7e4ce26efce8532c01e85850ad58f609eb6370a7dce18b8018c85824f37e545b95df1b7e04638c11546c006ba5c904212833b48c0caac3a21e367370cc100df488d20e221d1c071e06c16c28682b7a877475b6e123dba7c4420809d3747b653928c2fbb55a8614be89a5aec4e20b36b0430939971975ba923d92c54a8fe760f14b5d9e78f6141234b6507fa84b06c87f47c7939de2dfcbe6f910c3d056fa4977b51eee5781c4c536ab4af17fd623b417e3be4c567f18c29353d42534ab4a460b55be0bb5eb85295b16697499d165de1d5f3e402d51563c83a3e25503ecc45343253ef75d71f358bdae4fc65dbd7397c8937530371b34c803d3ce74b464840b595b84ca08671a610281fe8c1b3db9ff89387926d21acaea6967ca3174cadae1dd6585dfb606ab396b2827e0a57195524ff9a1326d427964a27fa8d3dbfbff44e550fac481ee644e1d89552af309c9efe2cf841d2126fee500d5802f22b60411c899e14b0ff836ccb85429ca8a2050d4d623e691184941ee4afa9e29ef8b865110d6861c4e2f305d9a8351979f4ebbbe759ee0cc9bb1637a78285897fd321ecb937ef08cac77666e5caaab72e2355b11416ae100b304799f5d416e59066edc0b833c01a3410c1b9f04959a1b62e320316a9268d7f1774a215ff21b62ef2896b8353335a32903abd275760197aaba2c6d8918d24424eb0f492315abf235c01b1a29a8b713d121f9f32e0d48a952c2882191fd836e085cceac673cad2f847ff40292e3c12f2764d29c4b2931f2c2b489abacd2b32aff72b1b0c58d242fff95c88cc99bd122d147f6d6a270403c0d8569dfe492478fb051fdfd5564286104b78c1c9155c048000d26276925961efdd6b8feae919f1a5de380f474007b4f9149ebe8bb5e229e4fea7ca54e11efda06d5ae5b930406e0d8a94c16bed42ce73e6d217c6bb5fda479247be06b8fabec7b99954952feab89ca34eccc4a3039c527955fd78f12c85f3069a7758287c57c27a9651e4304e07e4acafc3f6b56152fb7f5d0484656d3c378f5ab38346e70735de0461db6ed4d491f9af1699a0cf3dcf6aafbc3b7a77a053433abc351a6af39d59fcba3aea431b19d7571ea22dfb11c71321ace3b8c37fe23c874766ff44a4320a75d54130d80ec7621df0219b8a8d3e116f5e1d0e85a38f0a84c15dd40af3ed4796a5291dcdf7b012b89c528a348a06b5f9db31b4e18d345f36ded3be82a879744cce0847b0e13fba15c013ae892f0ee8c7c6840c1a5ac7318de809d9c2ea43f2e3a5136e8858f82bafc7b14ad066120226191be7ea480658ed5103c85d3224b110359236e647d5163f2e2f9603a6851bd367e395c84b4898196faabf63eebf1e4314493bd14f09eee37c4f42b4b86de4afae186f5b5d75fa5941dcb0ed5afd66c7f4070ab0b4585632d3c0e8de767d1865ea03bf397cd50e4fe0d7e0203a00b656f2003d3f6f591ec2ea1c6ac7542a567e39c366ef52ae892915081e360014adf7a594227c8f4bb03c4371343f4e24ec34bc23ec80cf1e7db1534186dbada216d7b9cce945e87614222eca43e0e33304c05939e44f5754e7ede6ad2f6268fc142619351163e7b34778a5c7807ead0695d07113e7a11c176a94021c3ed9d0cef3e4b274ae293aed5d9d3e9cfc45e35f5dd2d4b4ae761229b05f8c81db99330c207131b319a13d527980cbc82cb0704fc465ea75f02c4cf9bcb9c320a9721e27a4e9c333133737412cf8af2b3692334d3c4a0fa6b0d6eec86f69c1f88582c6d7645c9cbec53a8a47658ec3e1de06c9cf5fa67e4031e6db6895b6ae9315f67339fa290511850ffdf64f1dd5ed66c08cfd53fecfda9b66c7b49d9fa242a835a9da124ac62891871c1fa5be35e0ad9eb2b27ce3cd42e767ed91e4486bba2ce72df8c112bdd0b79b3562ee5f531d0f01a97f64238ea8b28f054fe4f37b6ae859f56d17acea721d3b7eac6f3d7a85f2e8930bf1856b8ac75cde72eadbed59d53f717cb8cb54c56589f60c995801e94391fc4724e35f0551e3b301f17e40584ee415819b3b7fc12d92f3429c3b54079acc852647a023279c2d036a6e29d480618a9df651e150166a137efcc2e309c3f72b8bb590cad436477909f0b8b0d94b9e6c7e1d682d248d67403c55ce1ca82ce8207b626e4ba6d737a16c7612a61e93984dd6c28a46f281fb34122bcb7c248ae39207674ae287a1f79387df61395cb6679443e482493926bf10290ff5666ae014d6b82b6bf139ddd89a9e34460640b3caf0b70485f4d37ab6ab6362429ad0c8658b76b900602ae8ccad4bfa3582bc323ae946d1b75e44d88ca86d0e5f637a6150882ae0f6aae405ea12d6df77ac663e28ab6e63ac9d3b8082c951d2f83df4550f2e645d4892498c52a3dc023f4bbc5500a3dd810a382966ea97d0697cb4fbb338a4f0b078f367ba4938059f5ed83a54f39788bb8404c35309bb49943d730237444bcd1467f268d8c83c6356bd751820ee3c9c576d3e538d5a87d63e0fa4d35ec0ecc6c08976f9c768e593d7229d4f3e4670f7063698fd873ed5712c0bbc2fcb87a08c432d3523798cf0119d51270496dd5e2fe3e56132962dee12d2c2f3c8f41f8fb29fc13975fe24ac35d696fc52bde035f4134584a005936c80a4102fb56d517fbca6b689da0b0cbe0652848599c0737597fbae03741fb0bd756fd40ae849d8c15ebbdb1adcfce40091323cd7f0a2d06f0fcba69a35c0db345b3170d6d03b7cccaec060b39402f8ee244c649b2523db53c25b6b4ce6512e08d2dec88ec22b203a2901ce5f433f6748b6aff81085733a1174ec4a3c55219f0962c00d8dca47fb4953f768e0a7b45f2147cbb8fd0871a2f180d82af43d00b3cb0b88fd63ec21e89e1e2221d69c7bf8a01cac477750127a4d4e99ad4c9bb999ef51e493b8c8e39c305fa0b86284dcf6c1e714c40330316d555eddba7674a83d2bbc8762a22f69d4ae2ff64381a2fe68a6a2e07871d4f27a9df04aff1ee0ba714262ccd301c7c3e4f18ec3e05a0f9d49c0bf6bb287fc7611aaacda61533305e7b488d4e03701f8d26632847e523797198d2ee4c8c8865cf1876e9b2bcfc1cdfbe6271a231922616a2aaf372894f6f30d28b065e3274f89faf3e02a24b0a541f3d5995d509511d0eba57da48e857f40882b2a65683b1a48d59bcd823f34094b754365c2db67dee8857b277a206a21f42de6ed2c55fd11ffd8d665114c780d8b7de7e685dc7f6fb36b05db99afb5fb745d74984524677c6babd41bc08b1228a2288dbc19af134abd271d76de955e0c5f4aee350a12487ed1013310a72ae9bc44af61a650d1a8b3f006044ae90930423b0b45a9d669502b90bdb098f9b4ecc5c8622e7429a0dbcef72b0cf84c865a8e938138af67523985787f0ab9b97da16f9cc888beafe8823e0a02b8b200756d81ee15adeabfc0eab9e6a3180a6f7c7bb1dacc65de75bd082d13b1b5fd055c487c6f1bc423ece879ee7f87348cecb87b6a8513032635bd29017deb751c9e08582117b880f6c355db47ad76de14a141da2bb894063cadd5c70520ba0015b286f61eabed0d88ded949251b40a1a23b151a1877329e86cdbdb1310936f05cb4550e069192a93af62573c4b708ab221d94210b874e5825bd6b2c52e78e8644773420f3759c654ba1e5d258ade0963191b5a1dd2a1ddaca4669666deb77496021ed3a14f0ac93b0c7babdc59789ae8a7280149a8d38d1ff581fa38b94c16ee8654ca1b6507a9a2b8b0bbb06b58d49e109e2d9019766926667e3fb0c2c2a41e783f7e9eff953e86750555e0d24aec890e9951583924fe18987eb161cdb84b13161c8d41caadf7e0bd6f64ee815053ccc00bc967c84584fcdbe49890a747cb647d014d48deeaa024afde198581960957923c48d94b32e93af7f9eecf7c635c68c2742cec1c880f20207dde5f8ad586e55d6f0987ee5b2519fe8cef1379f8dce8b39a42e38889f7e1e4c6aa3066b780d7ef584c3fba1b54cb268ac10ea5db8aaf13f1ee2136dd87325541233fbee10f25b3d1515bfbf8ce2e3c66bb0867ff1ae219f8805caad54b0a3191ed46f7e61d18020c3c9d8f475d077e188218459fd796c46b7598899898b03a0464140a2154bb93cea55f60d6d6742781cb0494b0954483360abf280d7460b52d3fe84d95b60ac9ed9b977cd6b65cc9cfbe7b08dbfe3af58c2e8d8b847271e0bc4542c1989982a0a22f5453e0852eb76d6eb362ffe82e709133431e846acb9184fb4f44918fe97389528384c01f81eefc1a1fb7157b3b864a2007111d5fb554a6d0ff87097af4ccca2db136b7e23f79d6aaad245ff998800442ab84a424ba24ff38599655418db7c07da9376e5a474a9c7d81668f8acdbe6e17c3f49035e01954d57538a98ad3667d2b332916cedefb47e5202d76441e6acdef3c748ad0fcc267698327f32b8331bf3234590be98077ff14537119c4f2dcc7f64c25f2f76bb187dbcba8e5a227b4f41d43a56b6449279adeca752e449362c7d0818fd99a4baa620e7e057426b15b43daf1832c4219959dbc6534b6667d13312c8ab12d38f7f5ea7f8b69498eb8abaf8a716db958eb8706451414f956e505758890ccc4bcdb44e3ae04d1d2a21603c53367a9e6eb009d4171bba4779cba21816fe0ce2c371dbb09f018c59ca15813e0a04119475bab3c1f0d3ca79884a1ba3d8f264367db973ca699a26bba920aeffb8c98d4acd309a8eafc04f52a4f5d4ce1adda7e0dc7d89e759e2ff479463e34a5676f56f796f6ad4e5ee454660dd642cfb5a874d4886a402ce029744bcefe5f81bedb06b1dd5b1f63d6da35bf22636efe884ae42582bf46baf6e0592385a31c853d564e89ab538bc12ed56fceb7dfb3e037b93d70ae7c315edab48a9c561ccd9dc91955660d371eef1548c88cdb8acb9fbe5ddc4cf7e4f461c335e0f8a3e8f2bfa8e3ce1227b17cbb943fec9e811eb3be16b4bb442d3cacf352dc9feb05c0ee0b3f044dca5477f3994cd25088209bf36fdd82424d8d695d0c5a722aee9ecf28c352958b2882a901e93c4c10ca49e7973e17f96fd56b99efa37dc15a2f45ced1cd63b972b18c1f1d7a7fb6dbe4e483b91a6d842d8e141f3bba1277310b41acee62472f184d24aa2e6423a3943c05e408a20231099174373c763e690950b0ea6e31075d414818c4536aca58f98f5ebebe3488b4a1ffd1e023ea9df786666643d2a4b729f0060eb53886182a9f03435cd75f318c2549a79366761dddd71ddc21d5ff01aec0c9a33d4fb649f99759a393764df9c3db4119e8d73b53dac303a8ffa3f92936ca1f4abf8d0629837090a863d85775d3d4e26e99dcf03cca4c9cf32c9d98154e391b6c5ce72f28e912181ee7fda8046181f69caa360506644df207ee55e8a17223bcdb3719518dcbee8796a0a5ca43003a757a841e88563cd4b72eec2c62f2b081a60213e25b810ea0fcea7dad26861dd7ed56c98767fb7b5a5a6aee612b843a9930bfe15bbf25b6c2aecb170361c67faf22a5066ac01b1c4c9f416b13f4a1081604bd2054f9e46363b67866577d0c621046f7188ce6397ac60844e8d8a818ef1fa09e851958bb252381b84afc7f382fbfb9d7c857fcd79fc8acfaa2f78f88ce3899e0cf7146b5011880d1a1946103572b4eb3092ecc77c4d744d03229a4be0599007439da4ef36fc69c5f59b229a46e13c616064829997155caf03bb0fc07dcefba8477adce1d668edabd14a708fa5a496950148901429609dbafafe7d6ee9432376415a9d4ccdeb11446da6a17bfc3015c6c449a0c9a371fbcb7b24c9d75757ffdfca3f0c043d91124c776ec3b5083ff0cf93448ac917acd9ebb2bf44aa4f5d78b138c50cea407ea53887ba4c162dee9ad00aa1e7ae861a8219aedcade0f05b14374f8d12cc7562a4058c2620308902948dfe6099b0d6ab962ca116d6d97668413fe562fd1a6308ac0f25e8034d8366781757775c88417709273b14e5775f4aaae8d7592b848a2eea62cfbfba52884742dff83cb50b1ce52b31c2896077430c9b3e633e9784b6876e32d065276df0451a1d32c0cc6fbd87e75b93049d37e6caa0fc6ca82250742229863dc0c0dd58ea17249bd06bf7856b3e28d0f5959e9dfd0de7552a892821c1e9cebd1b54aae2bd5adcc79d6937997a086f2ac7e8d1491a27ecfa0e6bddf443b797689d04d576469934cd21fc9f80e65b1c8100fbf423a0a1ddf908dd24ff14bf8f05b9fc8506e4b8a90be677da9560c2afc83de491cc4b97f94403abac0f0a194fe64be626019bf8426dca240383b6b8b7f8ee2d330d5efad6939ccd898bd0d0d8e0142389737439d98512164a7c1a6882194e72292abb10cb6590c0ca258fc4eb274ea4e3c342bf379927aa4477b962f150394ea6832831c8fccf69a5d8a0b32128e18a4344f1551cb06d2950222ba23b1c8c89655dddab670f4846308349cb9c0232deec73a8f44e26fe46f03eb45e5818b704bc967528a9e3620ab508089ca2b9588d23a9b664742892c6927e758dc1223fd388b65fae4fb55b416935de3d050b654918b8e9f78d91b49b994a69c179e1a2d80789b4e1a70843111a8d3bb7350fcba57d154838bbc1dea4f9159001b5bdc71f35a629f69448d2650fa0032919423aa1ca27b606a6fb4256346dbd0a60e3384f5bc9e1f177a4205e8be892ca8f6919246029471131f4efa04f69f26a3d23a3ca07a57a993acda25b63457e1254400a61d9e4e5cdfc13199bc835bd22a848d6fe05801ae7b8fa8a10b30bb7d9c8e4cb583e8aa5ae4830c9f14b933354dee709f909c1df1c744e6124d06a7708e5b1dee475f0a2c06719bdddbc88e4e43b50733fb956e4033bddba17c3c2b66047f32f304e54f56db386d4a0d10e52f93b8440a164719928625ef4a683e9666a56b0320717c0f9f14fb5edf9fce986b23cff84a2aee5271ce5f9798bafa4fe6f6c7e7812711692e83363c017b946c0f91d6f8be40eacf2fb827bae3d54d6d4f9387b8e92f3f82a1392b25f22f64174a19e14149a533725048908b6292200567ed9fe2010c1a7ea3347727ee569d472d03e260065991e26af101c251ba23a7d7480643295fb203d5b7a3b3e6e25af36a9c47ad7b9c38498e771dcfcf31574b8390c9191075abab3f89d01bd559effae6fd0c75e488cfc6379b8bb0dd3be1f5374c87758c011ab5b0313a0b34a32375b190eaea9760f336c664952e07beabfc838b9b8a81a5c420bc1d2856a83b67e6ec93c1b6258aaeca26b04145cf218cefd7b613aa83e3b9cdeee9fb3f1f228bbd6ae65f874212705c407752a0b1d740caae552627de4ea66e87360a180e31827b7c9fb0835d9c7ab168706ff865dfc7e1deb6e85d1a544baf53944d11fa0f6cf35c6355e202dbcecc76c52f3d188a43db6cc9dd73a4f8469a824b196f62124d32370f5a85ea45c14abfd037b752383cb7c5ce4725f42251b4ddcef8b6ad07e5449de1e58cb0fe500f6251ca865ad36c08340110ece19144bab4cece6964cfe2cc3bd1d29d1f193260ce92e734034630f2a35dc34103de3a7dd0e8b8c03d6edd8dd277ab4a7785710528ba32afe6a9225ae4c188bc5ebd50634fba8cc59ea7aaf3c7d0d052325daa473d2871e2f3161fccc44876a1c79595f6a46366296ed1b0c1cc49bd5cab8e05f59855139b602dbb0e1825889b2315de3594e6883d720db5558ab61b58efb142ae4c35b26a14972fd034ce7a9c831207f2a17aafe4de3d0bd1dce621babaed2fc8e1f561660b9626a7902a80d0ea0867508c9c0236c2900dd38d595b3d1c5e5d787b3dd92a23edec5716aa1e56d28f1987b215ce02664fa6b212ac3c842ebb7c579b3881c61ebebced8a06acdacb30ccdaaeda6ea8dcf57cbeff0eaa54252f18709be6c46358d9656054aebb848c7cd1b53223f36e94a5262215b004176f6963f05102b736dde71a69ffc688758c0961515525228552c4f630ec468399d151891721bb00a3a1ab5a92fd29ba2f3f465784c904adae0b00190ca8b0ff5cf003e63c258a2dde978f139c25b7f591c064e854521d02336fa3ed5953f9e6b66795c1a3c58affa386f8e752d4d57b8b57a88a4bbd31a297bc82da93217c67f803278cc3b8c6618154445edadf6e512e79dddfd81383ccebeadbe3ecf42e7ad28a18880963254c9e661bff80a7c84f7d7c53aa7fe71afb3c5004ce2e37ce595e88c642599718affcbf64c904780f035931b5c98985693ff9c6403a2b10be6750e6eebc11867ea182f0134ab5bb5c79a5fb7f527750958cb0e08229b4377591d30f91d1a534ff23e87f5c2054c11a57ba12fa3c2b976c00c1346185f5ce14de8ae218266e45ddddf98344364f4f214178f6954bcee6855bd3296b610e0fb69c8bd219aad27987fa31879e4743006e5e0f92d6bd15cf2f4b95fa0d8b7218ab21647b0bb1c0b83fd16ef26d9148d389bb09c5963cf9c9d4f6f539d827fa269cbe81c815c4f0752eb32d86352ba3e3e0427b78244d1e360c5504f830394b40948ccc8686a6578759b6991a4df92984876467262e6636642cd55877420344bc6569715e12071e692a7cb22711d90feb500f83d3df3f1c32f2183ef4fae32de6e8afdcef1facf6bbd531703d0a2a8838a503aabeb506bbf9fb87e2836082e1c9c55b0e5c082c0005b62a7778580858ac9d4d82171888b2c605d24b137281cc4556faf70d786dcbd9ecb11836b15e5fd7ea078399ddbeea4c4394fce1c704fde18a3588ba080cec67ea2bc3312cacb2a500d60b8e54ef07e95b3ef689ededb284e8c9c30b587443c77a1628cb3340ca7796768fafdd32442bc2b182d8c30fa54967c312894179aeb37e6002bf92f76af8adf5c12a0a43a7b419a178ca582d4e7190fa735411f01b8246ddc10d866916bee3dc55b59e58facc2713350bdeec614c937012efd402a99c5ca83b1c801d879c456ec4ae621d58c01d65680e52329ad87700789000a5c802ec7734bd7048b6b03fa009bc98e4a7eb2c34d3bd5b811042424680f5bc6c257cc32726f24995f53cd1ff4c746631f090e60892fa7b9e7d1f0d683121e2fa89b09d8594974064b33d11a9ea517200b2d570355eeb9a39f8cae722f970053879cba5ffb90c551e72adaebbdd15fefdfa0f3cf4e04ef9b67f9fab96bfb5d72d6cbf196fa98f1ee1e878ffa145c9bc4aa3e411954494aff8b80bdd7049fb3c814ec70dbdb414c34db693d6f5787592a0f1ebf7c1b89bfcb3fff936d7d7cc07326a52a57206c33ab8571101a11ce9245a6196e1e5f922edd50a3ad03b8e36fcc21667453c4399b0f4ee39f15550985c911cce8d26cf7ab1b815e7216a8e96b8a812462207f8a51e9a54923138a0ac220bd00bb11ef91e1e79f53ac8a70783e7a6998680e36000d002d53ba97fd51a09774f18d9a4220ce50c39fe527ae1024f2f285848ae29af638a1f5eb056305609f5e15cabd7b90f15442765684a010cfe570db9ba66de5ada9bdcc9456a3957bfb48d7f8051ab2234dcce2b8b01ae557c182a8e9500d4867951925f01016cd52046d8325f45add9128200fcd952cbc13230d6ef0563b0bdaafc71ae3c4f3f6e68d5b873f164fa1b479ae98428bb1e3cc034dc658b8ea4980e0fdf7adf91d61992d50a36597734fece06d591028ac47ccc25d7d988101e6b5cb656c34e362af5094e92c20e0d1f3c36dbe2f8cfa0a84372210ba20357e76f18e47e3e4006f1cfa400d087a61fcd2f101b11befeaafdb2994576a78f75839b6a69174175af485abc0e0a6dd81e12377ef180101d3f6d21c36eb58ba64c26f12a1d97c086ca5846a36eb3462bf35697fffc9bc1b97d0266f85c8118dc6594cf4ca82fb8405433583610c50642728ab66df25b1bb94c5efba828b7df6eeaec9530cc9ea33106e18a71790e0e4e531247379efde8299d39980e50a9176e18ce6aa82ff11b5c7f1694ea16294f737bcff2a0d9b4b26bd39db9d4961a32210e5321c667a95be90acac699a906accaf29ef36762e878789970f13fe6160db446ad1d868fc6857e68ee66d6d085b863ed18846e55e501a8aed72dcf5f08bb7ff70b48e50a144e7a037d214043015c69b0f18f16fdb7f9166dfb55a68480567cadb4fb21fe84559bf57e35285b43e3aeb1e5ac4feee183e701a834a55b0cd4b2f61eb9f37ca8a9e1efdc97921304be01b18e1d8a2d50f2c7978d090590f5c8ad4ace6b78b551e65cea365c8e352c5b4d6ae57c5a32e6eb632426b6e6cfb290198222b29705b4219c632e95124ea3d35f2925e45358733ba4ad75a7490ee8e77f8c623bdd3ba5a7c054e922fb8269e684ae154ecc668738bafe1a0188dd3beda462d254bccbb8187a9cc0af38f7b86ba2750b4a83b7a69da5a8316aec1db8a4f1fae550f37deea4ae9d47ece3f63fcb8abd655a94e4a13c3dcea1c662ff564e18662602ee74df9841be146c1c537454c28c53f4ccdac8c632c4991e8eacc8671782fec92f9b7d2495692f01880d715eeb4be0389c94533b93703818e330155076f9aa9be168007635b1f526d1ae5c92df354b5595c8f6bed3cde41ad916a3ee440ba1b65a93bae9994c4f8e601e079493963ee67061874ed5da40cdc0dad7247f68596979ab7105188584309ab480e699430b5a433b3c3013fbd4a1103ee8b570ddae0e9bc2f4567a94da0d98b2f182c944f3b919caf8c009fb15b91057713ee370272adcd8199de644ded7c908803c28b47eea19f486e21cf39d0576605ad7018612958263f1e672d044026f20eb1998ce3a4eb5704353e56b9b34b852dc948dc9bdbf1e8ea0a92a11c0e69c1ddfe16f39449f730681897cab56c9ac9b7d010005b1a93201adcdeb8a75390c6ff54b5bde37028332de40ec70694ce8fecd780f66c031cbfd4aa7c7d4383e5db1d2451baec921a76bbfceeaaee1530d7bec19525713c63f37a1b18df145dcbed6e981d600a6e1ec26b540e92601e841eb7114379313738f413fbade80b2f501f9bea60d12a43a045780cb4b373aa6d610e206ca386b99ec7fa1ab6681a9320bb86402d6456a8866ef352efd816affa080c327aeb164b11c3a51c467a5c78ee303787b0ac4ff528feec12ca3914f778d57b31d8a7eb2b8763d95fc40662d230d0b38b0e5054c775ff232cca8182b3896e6c8d1a2deb67ef93701b60768ab267073c7c89a070257ab1c771f9859c63414cb4f7b00371efde5d423028016cd0110c06e83069ccc4b26762deb14628080bf62b3700526069166038d95e9b1b1124775efc20bab823a52505ba008f3be66d99009d7730983766a9101f6ec5da1c69405036fe5de161db26d6ea785083ac9114148221fb14858b5f911a561296961b2fb232b0adc013b6802a2df0a766aa0524007f932a585782a54be574ef37a4db1095cce6be681302874a9fb8b1f992e2e0fe8ddbb4e84898cc168153e21d70f2bb2c8b9492782e169a753d9cb525e8d49a2500d34eb0a2e938ed7307563d48b321749501595da7af439ccf5861677e3b90632669029110ba5d59e2bb4b6f832425830e954a04d994421dc8a739c9878282fcdc5a928e20cfb8498b4687f921469938265809e04fd7ad21a0773a96e034b02f066edc0fd0afbd445f88487a72973d333ca5037a2d9f90c5eb0619616dc4c575d80569d9827e26a277d841586f9eef6394a325b5a9da5816b0690c558aa29a1331c55c38ec713216efbfb9322ff24cdc82e4d4034b3d65ea8c84efefbd3e5b116471a9920b77088a9d898fcb49cf8c4db764fbebb39b39d7b7b25b121d7ef7958930de12ab652153b0e893673c23b12b60c96f8a7c28d0ba6b5fccfa50594cfa6890e32988cf4ac0cd60480a57a84e4e25f21ac2eed7c9ed841fad933e2777f7f09a13bcf80988dae86dbe877d4e2e6d57c49d22625db425d9c44611310c5f46514c1a2c0f6b61ff1620410c05dcba6e427b9650d3baa8ab87e031f5a8cab39c1fde0be43302f8404f5571e5af37901145875c1d3fce05d7f777e1ef2f012850594b004888133fac7d0e4dff2b04711364bc49509c2f7e629b3a4f991497d542efa31b4eab2e0625f9d3272247e1c4030f9a19416fb5935622bcace9f200fbd6b7704e655e223135a99f78ff664764b0174a824c4ee3b0192c2768094d7c30fc2ed5f70cc6ab68fa1b493902d21cad675f0934c31e77b79fd45011bd0e6c6fa386f39f942d62ba5adfe773eb958318a52ba4c81e075e1411f7538ae17ad9d89d7f6875940226f6d00f823a9c58ef23480e25949414a5f090ccea2542db643f22c2560e906ce76684a618e231164ebace175f8b31e3bfed98153775b3a2004dc9314832d5ae47daf51ee6a813b073832949408cf1ad7cfcfc7f51cd2bd4bdd969751863183fe118995b7a268f35ce53443b6e12cfa4bc34c084e6d3e9215b6b51e477cf5248998da802478bb3192781efc22bfd2b2f85fb536a2e55e0295c094f92ae205a15bbf77e709b5a6b8157832c2d3e67669fb054c1f37c535fccfa7e409f4cd81fe8fabcf52ea3991ccc37016b661ff5bdd1d362ecb0d547a4351d567e1a839ca9e3c5bf27a9769528d86762e0358b7cb1b5a51115ea31c2afa086c93865c2410096e29f320b21f1c0887f25bcc50b320934fc9daf1d6589cfd66bc267f77d1128003c18b5c9ac345e883ac5ee11c5c41f543df29718dcda23807b1736fcaf2613003844b709a76c35d89173668af77459416981f036830aeef0eaeb1ca09d711bea22350f2a49db81dfe1b5a97886d6a15cb98190dbcbb877a3fdf629c30f16a6dbc2d674285f746fb1c2e4dc81f42b52f064ffb8a32da578d4ce9b76abef5a91947760496d1281804ab6f5bc079e19f02fc1ac63f76402f7a01beda48a08230a9dee6de6b8c8998cece6a4b3d3a37e880dedabe170153c9abc9541140cd00b9be5a0678cfbb72f268f145f265eaa781c34e8572b96327a64fe48d57827d492fa52f48907c0726062fd737640661d835221668cbcfd44c18b8debdf7c4efc016d4def3660e89921e037c27c7cb8c350741ccf2ac9b16c7ec14acc7e4b10d25ebd205d7728224187ea93867da359a8a6ad25d56a6e55c9b56c7b5ee3abe763d6b672725238c8c6037e8435fbcad8120f61617690bb2c454aed5e3c3a96f7d9c09342a3170040ba6e8d29021c70326cb3b8187e14c9aa1465b010a933724962ee485d4a46e1aa9d2d34d485827f3cfe37039050029d6d9625888806cdd36c0e1735c0ef6da4e709518ac074949a3c299b54168abce3198140d1de4d1bd92e48d59daa82abf6efe19c71cbc62f58b4efe1290d3e53381f0fe6915aa39492fad9e830408fabd9f25f0aceff1d41e9f51670e3cde28d1f89dcca8c1205df05b2e6c49ce41a81a9bd1907fb8fd2827f41952e5cb779f1401f3893b6a09bd0aebaab5b136590235e02f74f98fd49390757d8e55df118b7accaebeff5e707b4c2dc06e0cdf03d6444b63ddf3f2b7642e585774130b8433a322f989b485f6947a1e9f961a9dfe9fc3ef02228fa1586503d389ccf054d5c5c75c3bd96e065180487783ece3d78f79fd925ddda2c371ffa5fcce34c1e1462d54090d56ad53fc0090617983fc9778893d016ffdd7d809d440149b3447c6567ab62e4b43a90c784f4d42c777dff2554775e325d1c3100bfaa1bb997aaeae09f5c8ec8418345aed6d18de9855388c3fc992e3dc70ddebbf76cceb7fe39883301da39e36de59900d16d9db725f864746823bb16eb2820f1f6e4f39a3026e90d14a0f773fe6ff0ee3cfc8ba0884e0fed0e24c5a4f97c6d0e5ed7e07606e06aa4d5171cb43105dde426b9f227e69800a77f0797874bc401cf356cbfc83f8ff46cf1a38163d58199403ff40ab0177d994b63771332b0a7adf6674aa05b720dfe350fe87907ce62cfbe711c34b75a3e6eef46582058425ceb36c6e28e091e262054059cac5b54050ce32e8c3174db761ab30fc66972d95cd0d64961633405807e0d007556f5102dc2a49007832771b84cc8253c71bd62b07fea9e4772a7ec7aab58b23316faa9baac9a6f32395c986bd4398077f13cf5d2c0ddf0e6ebc469f46eeaf22f1e21714ecb417406a1b90192da3f1d4495c24cf6ffd5d101283b4649796ce7d5a802891d59ece54cebee724ea25adbb4f35512ba0d03570b1990b77c0578cd3364b7e51def8863d4268d1d6f3ec90d2bab2f557233d0d94e1a364474485c72e07cc9f90dd393aa587200c7680cb7e0011238712be6eddae7d5658a5f87a60432fcf767210978716a2283cb0220e5f9724a85107dbe64019e2763722dc8c6319909ccd96161512e216b137d044536ff5eca2434182c31c0662e620cc56752162d5b3fc894812f3b6eb862b4d7b8da5921f73e5d03dc8599526509cdbda1d6e8d80198018a5a91665a9d9fa7b3e841b769834d97d49932971db5659070201c4116ff76a0ee14eac2ca39ebd5baf84eebbf3cf8c2e3af8bad70d973f1067d12744c4b652ce27c2c1b80112115cdca62fff9e27de482a5ea08c6a63b633c514c73b3b52aaa47b1d79a65ede2f58f5b8cf5caea9070050d4159c71c0b9a4d6b552531ecedfec19e61b69ce0591ff82c6197f73478eb6b8fa7e62743169c92b5dbddd52c185029dfe5b558c7af6c7a919dd947c7492798112fd9772a62303014c62702ef28846a4279d129118662791a8ad1c157b9095dc022165cae6f190824e5841cab8c67a9ccbb432a5f18b8d2d3c89c57251bd22d4b62e4865c1d230c32b4b81b825fb074665699c8507af4c97bf4582ee10a1829b3994710b27e19cc96a1d4a8b8385e9b3b95b13694750eea4820d7917b322e6817dd048ef06d7bb21069cb5e5faae526cc71d9823d58635d49cc3221eb57db3aa779b5d38d94c87261048282fabe0afb000f5c53a3ffdd98b3eee06d37c8536e50fbb6b54f197a94bf8ea7ee7036feb187fbe2dfe1cd57637a54acb74ba3bf009e1bbe308a2041c61584b1644fa562000ee0320f33f50d34ed1900cecb47f91eb82a2ccfce0c8707651e422e913aea2ccee913e8ec318dd4aadfd9f687c8d27e61d30695ca6599c07f7b0a8c03d2f0f4859cb69d082462aac6634c59928942cbed1a5a18e439f5e9a3e8dda433d092d38f4a1e2dac01f36cb7cf9e0ef41b21cd4e9898557d15c1318a884a3090364245b86af30149a19866506f0d5e80d8f946142bd4930761b994c6fee386a891704809442f8b43fa770185123c9dddb3918e6c9ebfae5adfe866f66c0627553982f7cff6112636462ab2aba37c4f6ce5cc77e3570354785c2436e49d1d774ca2b9b8299805896a4a88390ad8d821e0c96f8efe651e4f7da2bab403a1c7fb3c74529390ad51ada3c59fa0b02bd22198a3ea84e291957a0554f22948ae7262f74fc312b1dbfe4363a32c04f7d126561e88d0806e2658c38e83d88ce9ce88bffefba70cad88c090f9ad9c9f5c7f933e80b69862dbf43bfc3f0c726eac154eb71e6a8db358a60670b5a310b6541c22c2d81dd250fea72f899a641570219215d52145c703d39008b6e5d31775ca33c57db4818a6909f69ec5ab55a5a6c680775d281c593cb21efc3dffd4a1b78519a893d259d525ee0a9c8e980c9d339c348dd0315746f14b1d2276e5b34c83c60be9ee5bc173c5a7ef69a5fc66a93482a6cbb45690d0ee970e577ca1998c614abb5f35711e00bcb9f90fc50e84b8e47cabad62526873921bf58b5f8bce088c99cc48a377114c0699796897242ffb2d7e2ff4e334cbc1330d3ae17b92e004a56c937c58a6a44b386bda191846ba8d140d2a09bf46f374987d330d8789607a353f5b84bf979db1fcf591174074e6fa06141bc046725d481b39c60ecd7d1a5cd9246f3485719911c2e8a8cc71ee9b7897ab385dc5836d28aa8de7da5b44a6b16545267a718db680765bde65630bb513be1fd796097da98d13ff347daf38a8e535035682b3c530fca9438f7596c07b04ca338132899c851a09b0f0b9cd22807fd9ad4188c16b68bf28ef36a1b1942d474886d7d87518e2bb7b8fa8cabf6ff95038f39e724b979a600f73526f0bcbc59155c609ab86f5e13a009ef1c201616d9f3dfd08816988631464ea60baa276b8f114a8b25d11aa70871de5928f7c6924e22bfd23f8a9330e5144f429573b65d72c21697a070924a2ba0c96fb84b83bc369809b41dea790ec0ce2205e695cdf5e74f53a268ca6d222b21b134638ff7bc1752482f8c47c3bc972d15be545b56601eaf81b55f0d0d3c1eb7aa26dcaf3831ed471bf02cd247df61dd4a6919688ff15bf80b1f16f4d017a30d7121fccbd875372f28d585797202fbbd9cf499ab0082c1dc4cb12303689fbfa89f73765943e4e9c8615c9ecfbcdcc0554a06fb192ae3bd403e834b1adfb0fbf1e06e4f8e921afe7fe707fd0ec9aa0150249c80dbeadd8c3b00e409f83501c10450830edcb9c578e443ed9a81130731c15e41cc04bb00d0c19e06d28f873915cee7b67fd4bf4f1e56fa0f53a127d00337bef1b0daad2867259ead6dc3926d096c053bbec653f99cfd89e433ef5332b0eab9cf96ec78f7e4b9284330729fc23464064200f9e6a9d1da6b487db3c210763d0b6d3bb3070ea95b5d2604946214aecab35051e2f8bf9262be2bb49125ba70a599fbc0377f029a79f32460dfb0df9ee305211a2b4d190a82130b26a0fdad7270e88a5b455d73dc8bce88e1c9f4f6e02a2d9a558a1631406d2db441b32c513dd7b2c3739be0d2d5ea4b5b4a460d3d90af5f9ec20337c7c2c784cb9fd796b1b4e2ad9615a258bd11ac8e92652188896e7799e57f7f249ec23233294b88c8151b5995739219ef0fa17eea76493ee56ddd7c5cd47ff779c361a83f2d1407c584a4c9b64193f61d14bae8bfdd45d151484fcbd61566cae7b8f1eb51e8c958e398fab2bfa08c2c8f8611bdc7466b23230bc2469be14d2c9aa20de768e4368a6ed3848867296d88a165b009de62a00a4f512c93ff57a70dde11ab1078a74b5115be11e43c39517a5ca141346654353c9094d0d4bf35ceb90f383776c47d57420a0721040966b6c1d62e0e997f7c2d3bab51e0be7549c17982bf84e6950901acbb71a44458ef48191c6a7bc6965f61624b48fcfe41ea4c1d54327cb34f7ac221d063b2e0b25520fb4db3523d39be5f20c85ee5a5d88c7da538110dd9fda0d0a62e2391945220732a02885e7b98d0a58009d00a3a723ffb9e5470aaf7e0ef1eb2012ad42522930da389e2b7b8bf6dd8dd44687c5a40585df8dced13e219fc298c74662d9714039092aa6c3e1642880c6f8d4d5243dd87be90fdcf3fbdd9bf5813124fcd7bf3dbcab63201e88d252c876ac5194b4f6d086296d5935ffcfc5cb99cb54b966906a39cc28695a1851906484b392c27b7d195917dabc9f5b3958969dc0cecb925d8f55639722da479ff0d2c232c87d3981ef17a9ab12cc8a61b04ae12edca7f2a48f363c9d18d6d5819b19ee19003bd2e87455418272156ad11ebe17492f9dfb1d3e128689c15a16844f473e628f61fabd0b88fadbdf124eea6dad220042b32e11d87c6a1d5787dc6761127566bdab8357ed1b4768f90d6611483161d22b4a8389186d6996f643496734388306ce548ec410c36c00198ff25ac2f229b9ed53a29581cc744e2e7d8d92490dd28360c14c93598baac073f86fa123f80c531a66e8affc021df8e749432c82839a3bd28122108a8442bebac3434c8a16512ab55955018555f867b7642b9db597894a75ab0000c81f2d1ba821a1a87a1b3ec952e4d2cb8286934537700c6a8603d545bd127f6cfd3e51b35422d0b2e2f007e9a55c284005ddc70d85b2f8df037d3c75406614e0e6a1c077ed9d8d5632779eb870c6ffea54f21ce636b58c94e3a5c437c7d6976d98b3cfa85bde04e2de1553ab7a214abf4ef299652a954418e423bb0da3b49a2d341cbd563cf535779fd9630e88c4de16c02a17d7c36ffe88c10f29c1558e764ba7925ef6c544493b7cdaa8c64c604378ba3a02986fd92a4acdd341a8c0c7bb3f1f7d9d9b7ee58154979b526944e9f7751faa3274230904d4011379cf228be33625d4cec99b9baef68f972fa9f3211a9576535938589bed476b56a5729cb1d9707d4cf43fd6ed0b4905db78439c4318620aec3353342d9203c745d171e85652ada68c2171b556c98ef3f8b0b54a9d2cc0e83c6e5c83ff7c4a6ff5ba22e18d42cddedce718e217d9a21d08d61bebd244b612990112b9fe9cdec3872d4be6d386a73ebb4db0ba5d9f7e21476f6e9ff929a608f52fc711a40d68bb7fae3b06a76718dc9836f5b2cf5a0eb86b529d33183692b028de22db2127c7201a1522de8f22716d09b6cbf1c49b72fb7bd212cc57280db71fc19b9d210a6aa08410440ef76b5f40f73fdf1b7abb41b532e84a03d65dc1c8fe63badb0f5227ab847eb58772ce7dc80bac4287e1e1f5677e908d91a035e5808e9c907eef7876ccde5de7987da71a8f81ae3402dbce411f4515d24fd23bf26d7005d4794ccb965ba56dd83c7cbdfee0cbda9ff7005a1c6ad912a44754dae1a1da6ac49bf288bb8f89c8e7df2d49d98e0c92a304bed8974b46f3b0846296b6dcdc1910f82adfe392decbeb5711772e9f89cd384dcb39c44a1ee077eac184bcb95133c273b7654fbc32ca2a19d783555687aa08388391ab83687ba635e7c8117a620d2658e203fdd06f835623bdc0a9d1f67ea1899e5ec8ffe6224d1db6b3fab67e1252a5ec256ce229ffd39d41259bad1d69a46ce425a005628427d395c0971d0711cc7443e23f3e34b53066a47ce124d8a70c7fd9bf5d61716309d63bfd3aff346054cd1ea73bf0eefa90a1b8cc407069946ddc5b35f7affb6a2bdaddd9792c056c9aba563027e1dccb3119e25f723815bc87de3cbcc770ccbc8ace611f10e47dbe7bad85a2a77b17249c3cafcdd1ddac81f09bb8f70aaf17cb8ca28a5c3a4ea836ed7153f86773a160bf24457ba03809a6469a292ae03a58981a8b1fd87c42431a23ca944501a3c8be85c339c32aae8344a51882a537362f392112efd0a0f62e1b89ce5abe7b08e50a1a3788f8f5dc9e27d7acefde45394b27f77cd22ffbcb26002665e29161b6a908a2801615842cff2491dc1c32a4319dcf622f9a438eee9a029eb80f8bf9e3507a5b7ea938d7cd82169964b6e7d08118982b1328c5316951d699ce2b967607a94df334ec8db489cde8f03f85c1953dae353bc7580d248af29958f941655da334a4135a24539e0e9b88bd898e944d0005640dfd4fe42691ac8b002d08bb1f63c602bf4fed452c190a7b00d93e16cbbe4d24eef721a64d3c84063a32a86539dde55e826264557fce17c04e130918369ab05f5efa631156d2c60f7469b5ccd7ac47dad4714a4524af2a9586f3908d7b2b8a6803f3d6fb58d5a25a412963ac1417ae9edbe25ae17a05e2debc7b470bbaa96e75c3a5966d9daecef89f8b201277a91b8f1467691344bb74a9292822fb6a2fb56be348509e9fc25c1cdd7a0397fd918aa89d6cdc1d51ce9ea0f12ea90cf71682ee34c3362e86d4438df91269162ec7afe9c55b9446a957c4c54a3ff475145d9a6b6888117db4e5eaef1386c221a36cef00c16a2f045b9975e15386033a40ef364c3a1e6c33b74e531ed4592de4a34a84388d2c3aa5a23ded91e4095e5c759960fcfd6f92aaa77372c47715a99b2c48d256de89b11c09c7b5fe2fa78ff22af4e45b99d2c964e4b11c5a37764aed5b30d611ca8c24441e99f960107b4dd07932c5a866d51a0e0cc1ea511541673c44bb94063254860f2679491490640f8c9fb0eb357dbc7ff6c4c2c0778f245018f4da9eb1ebd01ded5c649568e9c6711d811727595ccd4fe4aa0d30647f6796dca042f24746ee7bbe4b5d8d97893cf04944666375771f159ece94482db13f4715a4cf55369689a23c53e170e0bb06bdfd2ee15f9a89d5fc132ade3713db680b7fc368d4bc6b39f6e1f68e7aabd8882deea7461df0211c5426e8cf441e863fdb02e051f5f2f3a15ba7555ebe67b8fea237905bff2e1c2722a412b7c860c3317b6b882db62da647dcaf31a8838088e0c3c0bf4dc411d807e81401e72bf114dea5c1c6436a1a298f332be146f2fec813f1bb54c5707f06be8707c016c9c5a31013e10581bba3c5858750e91514464413e7e88f71a89c92b29b5ad9859b920d7921c85643e18b4f28a75b7060a7218eca89946b62daab95ec39468e071c3861bd773c5cc8b3b639fea3fa278d65a75b9693f2dc0aae433aa4758816d5be7c52e8f64facaa5e78a359e4e39da8ba28991ecf200a3eef2564310bd5b2cd0bea1978c5a89b1a8f5898c637afb718dded1d2f173809134cf29ee3875e5bfae417608fc2bd3dff223a854b9702f9aedc06302d0681c199142edf24eddd33505133192ea41115d8d5242f14791067755ccd10ec98b1b8c4090d4dc5d1ed2bf804ae8eccba4a2d6b4a1b017080e105656f642bf99a80f71d98b7612a1c337048da3d10769c12373ab3c3866560abe4dddd8a01ebc0f00bf670b36e232e160ed07f6a4794d1f717cc2e2daec2e579baf1eaab862be879a8c33f7b9aeced65941ebf2bfa66d2662585caa87724d639dc1a87662439131371e71259f6307ba194d70fab5f59190a0f057b301220076dcbd02320d558602f7698781f5a609ffc291d339618cdd6f1a69e4fb7eccfae65c7507d1b361d880b2982075b676f64d5f3cc909cf50f833a2ce390eadfaa0d41d7996848ebcfff4333d9cd080eb120ecb0df74f92347bbdddcfae579dc14eb05ec23fe354b5d0f09fe94beb6d672706319ef985e5693703c818356242601effec71d2f204746608c53a1ae490d4a2dcfde8ee032c99d7057d6f635b260e811fd882eb490cbc510f7e6ace0c08a1f8e38406963829bd55db708381155a81a52c2b2b4f67796144d965b2c8d38908aa8f0372c3d91e0559aa37365a669c661380d49c107eb60a4c81440c33ab19a71234d167f8a89dfbed148ec217ed97447ace156b4537e0d460728b5c280385c65d313be39baa27ce3f44a532fcd56577947b2edb9bb24c95b5932778e2a6647b2e9bb4df2267eb0ae8329ef9ff284191aef781769b9575becff7d51c5d028974b4c3981c4f4c7b0fc47614d2a99791868f6331046603f3a2588194fdbbfdd998f8049a565bedaf51df07d6892174d19b4876a3f5b1c4f7c5e414373a2ad9874351fdd092dbf8f6fca5c3c645942809a341e7c16c2ad354c759dd39a81faafd4cefa27aa40a914d3c4a5c0419d12e10e039e1166564e1dfd0d1c11991b2f7075c212b69e1b2b1287a387a133a7945213e6d503ce976a5aaa58ea3cc4b0445b24a2c145bfea82778d1ccd086baae94c9518df7e185e404fdeda70e3b0f2dc53b1928b977cd4d9dfb526cf374b9e4b34e740e4bc975f1ead44b2632e1875773861a996388d9566c8037d19943067691b9d7a429418d5387314e69454c91ae403f99d717c92eb0f78fba6228e61ae2e7d01caed2c9fe645013b56b9926db39f0c4fd6d067c8b3b9c9d0bf72e040195ff6b7d6b8bcb5b78b302017869b8690e2ca1a728c1e17d9d154ae12031f01129fd4e955090f31098d3a1655ba9b87b307a9c2c78af427e7196470eb453410ff9870e70669b60651a221166ba6299bb95571825247aaf92e028c1bb5ebd9915c5215c462c8f4b1dab28a95dde7cf2bd1f5f76a7447dc3fb6521dddb6e7b1bedfd76c53d2eb43eab18b23a4bf06d4503e84054e87e666ae0339f87d1fca555bcfe01459818e053bc99aaafef4d5df2810e8db206e31068459c31538142c9311042036c65e98090f331ed60b0bd4b77f30000ed4636610e83a2a664f2e3592c7144fa89ef3e78fecf4fd4072168168a2c5620a5c60119e48cd8393fded6962b22d5f9091f83626ab1a0d1e47aaf37cc3c647590ca6450844d460c4bd8bcad09294af2f527bf63d9c9457826755ffe4d1494839a39b7aadf80198fd179f99d2260bd213770a450a58b7f4ffabeecb6c0cdf066210570ea29d05b2905aa47413d512980a404195bc1ebcdcb6620b62b120142ba720cdd4c36ebb705bf42098a0afa6b4287782b38f514d22b5829ef4ecd30240311c795e886988048f34acc6009a2dd95a82aafed03326905af4abbd258a09f834a312e5366b35b7eeb502ae405a21b5f8b2e68c3a9e71a28e7c2699908de08a9385ed73ef65351af80faf37f78d8a2473742f6df73d2f508cc790c8c0d23539d37f74bfa2a0254fda72ffa2b3acfe4d2fb99ae727738066ddd0b6eeab8907d00fe444a38ac1e8bf63c67f93a9de4944a54deebc49952878bcccab035b0e4871cbc220a5d7dbdd2aff185a6bd5a118a345a29c6c9111bfd96833283b6839664153dc43646c2deca06755095c82098e30213c985dbc15dd1bb967370078c65577a50302ddea93917f2060be444967424c96f6bf3b2dfcd1002138439e400fe3d66dd0d38969df0f61cfea2fa041ac8aa7cf89b02bb63053c040cd416af653ba2e8b7b9271f0f56f0438a491614917de7208e63e4c926cf514fc97a7884602c348dc20d02d14cd5aac8ec0ce7fba996e2ac4a2dca2a347876f88a3706f6274e21a91ff43596fb4f088360d314b7054c25c127840c00a7338f00ea50f952ca8b5cc6c3619aff0278317a7700498a3053c764c0c85331585d36466ac76e78b7dfa88e42ba633ddd3f1491f84a0c2cc1a6d56f78dbf4bcd48c53b9e6234e32d29acce12f06f793634c236afe1f360c294c78feb8d548dacd6cb5f861ff590b7d8041037cf918c25c5687e0c2b69f30bfeebabd6b76024f11fd04855e36b2f5666de151b8b681cdf2d97088a20388f0aba54c08123bf6982b2ee4a300ffc1bf292f25a5007df8b73a5dc5dea2a3226fb400f91deee720601ab4c63df6dc4ecc54661b9a18c93fcdb0199bc410ee40192b9608f1000b3a986ace01e7823ba0027cc1544ab4a003650d12af1227d66db74c78934d7216f378a3d20711f15723fa7b4bee9a880a1fe5060282738b75afb5661f95cb7cb635f42386dd299b28b9706f6c08b0a10cacf71fe250bd90b246b430972b6cfd8a5727b62119ca3afa9918aa36424121fa1ea0638cd4e4c0200585c1023d5127e93cf585a05dc7d485fbbdef7b68d6911d0ed0f4ab6be285a842ddd051c4958d8d9d89d403560b3e78a2d7cc37d08aac26783f31e607dd375f4c552ac477809712073838d30f4e57b24e729d580b781294d763b25c7d3f55e34fb5ec46922d948d2d364951129ba75bbcdc8592a67fe1355a5a4b42707d14b938467a9d43c5fea7a2ae564c0467281a6e3ce255ab60f0b7eff7f7f24dc7b057f2f02fb5479dd954f90f15063d1b4db248429d627e8f234dc2df15d639592e550b3915c7d8ad4e9e7fdcd4d43480930c450dc942630f3238c2e7b176e912e10d7ba2c590be9da0367e4ce7ca02c63a23dc3e2aec3f6e437fcb0753447260a556f8409e24486b2fa72ded0c624b14b0c53313c40244942de5f76e89cc44f1cd62124f48d18c1a0da12d775e9c40beafc8a36770863c9a58952e03ed141240ba04e0dd4cf88b11507b9bf58bf4746bee50cf3792d1af80db365290f25837da91f429a3d064a195876158a32c6ede883a0e245951777c163106de1822c82f093abda3ce1b5fcf61a77972af3de3f7e2892ef55dd25d25f6ae7df6b01927187925ec17feeca6a64f00c64f1646e4d3b52cf75706bda87454d01115b5f67202741c19b5ea04a3c9ae94a999346ad0e90c74d519c4aa4acc6a4a58affa368c5b66afbde5a6b0f7c4fd1bfb24dbce0df84693c0260f22366cb1b2d2aa24036ac494717f296bd1f4b6ce8e75a0451910c3c3fa2a5e48ebf0e25cebfe2738a50b44f313e9486e6ae1e50cac3f20229fe1934a7005665dabffbc725140d13788984a5c12b0c8095a648699946bc37ad362d2ab79a84cf8d4bc0346227a52b40b2ef6e7f1047bd98022b88e71767e8eb8f61f70c81436bbe0a17abad222d0010c26f5b0f26d4b28a3feb1eb2a34af3ec491d63ff6a7da0aae0fd8ead6977fc02b61961cdecf001a6984e41fe7bc0b9a8ad24dc6f3789b8d3eeddd443de6bd3a12abf34ebc2ee7152cc298369f2720e10371929b3b5c38e9a6d7ea91aa4e14c00115beb87b0aa0464eee762d5eb474e77a11bc9a8fe1462cd018dc18060dc3bc8f0703178b3177ea984321610afab35cdba70e7539a8120460383d11889645987b070c481a35e713a2db71acca2e9d06046a41dc2fff6d4d9c916443b5b83b7d99fda4a3b0998c292787b65e8219da5edc0a63488a1c04c37e1ad4469a2821bb38dc9467f05b70ca3f26e640c9b14675042f5125c54b060c70b3f1264c61d0fd0479034796340290a7230060b9be7969c07dd2fd7c01dd33f8542c67665266e51f59946be712209e1e8bed1847982c4788a20839aeab4cf35ffea55e9eeeae1eed2fe8566c81063f3fc8179dfd25004a41e41e347012c2dfe66809d1d659bc6ed1e4a0977a229ad8ecf356105dc845c1211758837d55fbd9ccbf6f7de4cf0fa6e9423e2ee376b303dd4dca73b490eec3ec7fc1253f4fc898802d593edf976c9b38c5d1f9d37d0f7e6d5cdbac952664913596750ef5805413101401176be1fffa292bce4dfaa2261300a0041254eb1ff38a75645bd4c1ffd0446a281ff6f5b1bfe820ed69d32dd47f8cb6f81843801e0f2ff3b52c103dd60abbff87388133e294c9411fb777bbc9818e26e5b80eedc054c40282b280fca915e313c911aa5a0d8f0348ed7072d0147c898f2317b2490e16c0cdf0f3065c035724a19245de8926f3647db59850f32e8e29fed16aa27df418ae91f5c93911aa0205a29e9c70aea54db6c261a569a8864f1002e89db5bd07ddf76ab8c00c767d1218f40f3db05d211f027cdf7debaa0e731a95df72381ef094df45cb9ca0ad7a81ae600c2df4977fb722de929c7f0922f41298f1cf79d0ae17a3e46a6e16cedff9490356a5d211cf8dde32f949b649f493065afa31f1f34c325c425132d0b9baaa196b8302354527579ef2b759eade6745eacbbc11dc4219cd6f1a01b9f05f82952f8233485c9f57079107a80aeb11def45b06c5b0c46a3c7040677bc09de93c9c3b21be4877e02f90f01e77492f25260e3ee9764df120b830c48376c86d206b5c0b2a4b4311b845fe757b7522f500e92aa636b539c14c01fc2c20fc956ceff9b74fd087c3314dcf81ff8e24c06adf0ffc669634bac29c1cbc0d142588bc3f52de2881b6290ea00750ecb9b2a9e204d45102405fa58af25c02166482632df5cc525c751ac1873aeaf9cbe6893f494c999b4bc2c68837bafa865949bbdc3c445290f6ddfce018de6d9bc19ec01931b1c1bb918f34755fbbd61f4bbb089c60cf9b14b169ff0a077754b47a9c4945a12ba50830f3fae3d6fa0e199f5a77211a942351979eb19cd1b9d97ad5bd340873fc5c19e7a83028fefc6f571883490dfbf1bcc41e89c0b7a404ae77b6b2318c17813d9f0ff29b1b147d49cc7553646feac11a47bd2a20c59eaab4b730085f06c0e6d19fb26922f1ee1f05328d76c7e11f0aa90aad0f7aff603d92d3d5eb7b99ba39ccfad6ee3abc1f400c2d770b0af645bc652dc6666bb4f24b8c93400179e52862beb6a1254c97ce6e9de196adab02b623ddde97d3cc766173290cded705e1a8e81519cc19836b77bda6478edb5ea9fa41ef2ed53561d6a4bef3e62ef84f0ed339b1eb4c9a5677644b12824557b609541f6690976ec64d7456dc1d712ec6fe573990a63537742e586d07b7a6d5d6b7a4f2ce3db2328905014762c809bf622ccbe50ccda3742ad30c9c4c19895b5681e6f338b92895fdc217f2bc408dd02502f9027723ca9b8640abd22b39a4332240a7634d9048838e0c3f5c18acbd36f6f87e8cb9ecc7abbb8089366a7d5eb6c3d01a7842c4af95fd11fb3c48a793e2956b3042e8fe14bba5aebd16b71cce0d5d892645ab16ab19da271bf8b755d4938cdacac96e64da98c20e29cc4acfdf1b4f56851e2d80e1dd277e1fd57c86d05340b602fd52c8059e3e3d7e90b6ca7bb78087c0d1fefd90b1a17a4db4cf817b33eb1d507a83929f9099c169f67c27eb6d83b85f48da368c4ab28db4a76de41bd9967f55e0fba0a4c55aca8222a3b79b53a7cd58a462f6234c2d210c60a7adb9d69370a20993a7fd0e60f22c9a0c84f3c4b8404f8c09a47f412e9456f8babe3c636f2500f28d49dbd3abe8678d4dc2535af179d4d2dce797e0f26881cf05b0ed804922dcabef989484114dad59acd856edb10b41400c10dd2dae21503072d95f2615451580a6f8e3222dfb890d5dbe27e41b46ae2720546ce8947d2e6c8866e7467366def50ac1f37fad5169d263cff7773dbb35f8b563290a2229df50a8df80b2841ed830a81e21ffa07f2435b0652bb8c51a279e60816aed7bbfe5ea35ccc2450eb2b3fb5728bcfdd1e5357ceb26b597bf2b71872231a136e67c36845990d8a959035456032a63d67a5a30145ea503994e4191ec1e2aaa874b6a029bdd3c29cfe5f63c1694a45895bb0f30e2c0a823ea2fd6918ebd058120d24bf33f000cbd49fc7ab310afae7e6b5ab39dd9481695cc0a48eca57e98a0f6edf52dd99b6cb9a59429a51475077307a907d099497bfe9e821dec58885b34dc62a1199ec80765f70df91ece8dd6a03c839ce9b1ecb949cd63268ee59e7a64b0d670344ff31a0d3fe4c73e9e6080cffc5c19cb9e460334199c3a78bc1b2e2e48ba5946506f7a9e8b870e560de892cbb5e41a8ad262c245c9e55a720d452162b96a66686a6866a6ccb2168b8b926bc9e5e2e2ea2f5a0708a00ac46a535b6538fee632c3bda79cbb56eadad59ecfb44be9071757abd56ab55aadafbdfc9801c9e572b95c2e170de887f250ae191a578d2b860ba929caf52ecfb514d3329454f8ec0411bcd4f9f3b3e7e745aa5a8cf7776d32f33edd62209583542c757bc80191595c4e539d5b696e37a05c6568af3f08e7466b50f24d1c0886542ce34080d948e0b404850a25966035a1f90915d0da63da63271858d37e25d39540e6655e93e156503c009fbf04503c003f7b7982811f76363054e881cf3a205eb5af407bdc8f36f39b0e9ff619ae37ae57edf9db88423346d51b29f413126fc00ea73d98b781049d9376a8da62d29e3b92f6dc6d7ca19ee448edf9c6c4855a67be7f659329eb3efe2dcbba8f5ffbd3fbb8c6cd6721b1cec73205fc6ab5f2b195bb130cdb84e924242714a93dc76213047579c1d23bbcf26f2425cf5c9eda9a755f67a9ce318ca53a9defa9786ae79380fe01d37d3ca7ee536d1d4472ee6c0cd18f4e28b95df21d99b70983c6aea886a391e11ac991609ac6096d6ad9fe9d5aa0f72eef4e2dddddbddd0db7683c4c544a9807de2e2a3f90de591f13892641c15ee63faf2148a1f2cecc333fceea6cafbd6927c6f3f3f44ecd0e7d7ede1dcc621cdcee803755724de8d3224f758aa974915cd3a6c970fbe3dc3e5127e7dc1ed128e2e48473ab0326f3098d829af227a1981cd023e8e6dc52716e7150316ebec62fb3859e34de3c2d736e6ad9e6cc6cce6d13319e38b746158339f598266c621b746e8b284e14fac1189b7308f0817e53c6e6dc01686824cdd89c3300adc9e21c8faaf1807ed0b91d4ce6131a0545c5b3a27ad60a9585733d558bfbf313655e724d64ba7ec935a1dd760bdf66b3395700cac5cdc6a43b0b0f1695158fca4f41457902c40a6a1555b138bef8520c2eae2d6973903c65e5886873aee01d9cc4e2e8ef25f848edaf41829eb039b7332305cee9987840bf89c2e65c0b262c9cd3f14d709b7304a89333bd0c87c3ff40f1d7b83dc7b429e0f838c939f61a0aa71ed88d630ebbcdb91cb4051ff4e05df483ceb1508344bf89b339a7faa2699173386a70a0c2e6dc00ba702ea79ab81b4d687c6cbed0af860ad114ed6d4ce8dc0d19977302d887ada453fdcde449eda7e198885b2e88f66085cdb9154e3882b7611ce4362fb5f151eb508ca3f105dd0eded89cf3b024657171692fdd45fb80e68bc780e8077336e70240e96989016ccea98063730e00345a10aacd391c9a1816b0b03997c26f3feb51284a592cae88cce3f03f321d94cdd2a9fe66a22f9a2af58959fbfef321fc98d75638f598a9ceb5f445b562226e716cce97428dc7ada76ab8754a04b3be3d3ba8e5d89ceb564504d89ce3e83c42bfa96373ee6990b02465e1d8c5b192a7963cc553d47e966271d07cf1543f0be80423d06fb6b0615c13da563ad59f718c85e324cec22e14b8078ba3df3914aae909fda04d127636e74e989ee27678886c90406980f563a2130f28d6cdf6b4026cced9548c6b32f3936322a6d21e4fe954bf9c827eb06773ae46c5b826f425c7454c45a66322203467a3ba609a01415a0cef88d5c60bfa4158dd7fba37f48636e0d3de81f06f1847671f7be4c36fde617af89a16398f5c57ad6bafff97d307a8fd156e8e7f370f7ac375e56aeab9f188df577fe7c19bb3a71ed039ef2ab150e89c066dbc58c85b64b2621dbaf70ea0f70e189b9b717c9a9c01e5f7b8bed19b3bc640d3ba09cabfeecec528bb5bba43e85cba4008210c1146e8485d60eb7c44e0af167d1b07be38ba75f7d2254e8de30df30e3861cba97295ef70159beaeee391e1a43d825150e835bdc39b5466a1ca2716aa3470fd3889fd0ae5c5e124e88fb168dee1244ef5735d15eb6c7b9c456d156c75f38e95d1575e4c4a6b5037988c527a49baf0d07d0df04244514a5a355a609dc510c2a8292cbc1861e2c50701e4404f758bbcbc4005197891818cba455cbc1c89777777bd07e70fb5226887eae88bde689df87d80d8d92cff77361d4df1674ee8abb808b63f5175d152151c8a8a54b9010de20e2fb943fc886508b14b0e3b4001862b2a7191c312e10ae6840653d7284912499e4cd102830371050cf08570922cf0935c1159052076eece76842f00156adf50a576078ed73c6ac307e23562988cd371f8bf19a7c296b638951fd6f0135446a11d028f69b9bbbb3b33bbbb4b8feece746803407c77976e7436c18068d105b010e5e66616742bd5b12240bdf26cdbb62c7b53f7e33de53f99fb28636f7a1bf9304fe427eb6cf8f4dadb48e0f4da9f3a1b6c03b5fff43612e663303d8ca9c3ba184e359cba205ac79265475459fea8fd9a8f8f7f5201aa9a71369889b3c13a096c6f83fde989fc642f01eddf068b61c3be86adc3ba186a78223fa66f55e5eefb51650744556568cf67f061ee6ce4f3dbc8ce05acce24b074581d56c27afefdeeee1856dff9cbe6388ab23226d67fb9fbb19c0cfb31b4e72bc38f0fceda407bfbab045b315a051fcc48a23c3e1ac6cfc3a30b4cc6d051162e35c6960ef55bd7567494850918b78a8b1a8150e3df88b2fb9b257e7eac5facd84709b335a2f163e499b2e3a992072e9892648b4d0c7376447ef6b7bb522ca6ae51932c6a7758f74dfac3a2dff415c4000911484a29a5921d322600e580a48909905026a07062d54f7c71021efebf091640a8b6402285d0a2c3115478d1610b0d3a0a2962600458e504592069a2a5dea8a1355ed7a889154e68cbcdcf809d9dfd3d39806de03e22bdf1a9477cc901cd1ff9918ba1573ff297fb4a50bf10c0daaab1f3ee2b410cf4477ef16d1460aab103414c855d13f9eb83aa7a9fe655c542f3d7c10d5f885054b78aba39f0c2a546098f66bc25a32641ac24f9e32f6164057dcc43edb701fa98090f7a4a6d278cb8a27af7b1d26c2c5c8dab803210cebbbbb8b104b19cc548ed25b5d7df37f04e06b86dd89e2dbb535d3c3d3d3972701c8dd861a0f3d99ce6d14259e9a7b99d6ccf3a8abfb8cff6b030b03dbbcdaecd699f9882148a39c675c03b1bf9fe1fdbc82e067ee8e37c54bd93b67648506f43825f4b010a9d2aa03f54a420b60895795136ecaad50a0a0a0ae2968cb0b165a32756b539cb4b9be29f29a8fcdf168157d4abdc3577a94c4434b9b5571ea0a500853a11acb3ed467887e7119aa4f22f2fa9fcaec3f6cce7ef2ebb3337c56f43e5c9df4a73528a4211050505316fcfd3c43fb66e3db55aadf679797fff019bb31f143429ea9b835ac789608f9f6750f959e096f3cfc9cc44ad266a2566a1ee100d910f4d6da373569f1dd42dedbf0ee22a9c72c94e36e5bf4d6c133d3d3972701c0d1adc921dec5848e3b8a5853ad1f6c0f7f7d76a402613b210b77a4a7bfe454de479d0c32dd53f460ff467d51e507b9d05ef2cb7d1f696a2edc1de86af504ba046d002550595c269c81e7b4d4a295fc3ae547e2bb2d002938f715c8922514f691da5a5a6e2de49d5db4b94b0e5cf4cdc280653aaf313db93bdbf133551018a7c9f4062b985663df889ad90fbb88adc42f9e57452a3d5b18daa176d4e134a5390424d2b45e58f2888c8777835dd79c5add841c0f49ac641272a47e4478b5c07606783d9605d0cfd3df810f0518d1c1f55c86dedf721ac6794a0f5c3ca5159541657d2c6e88162bb9caf529042b3ba3d337b7eb66103857228fb5dd230bb4e82e529e69ae81f5a89a0cd518259add36a9d1d6a1d9bfd45d23a41adc384db7699d59e867a42f7d4a36356ebb8bb43d6a232e44056b7d005f2d4f2f2b28fcde1ef1e720be5b81fe887aaad04a891b4cebac01cd81cf6b13dfbb13027a528d47a1116ead7409cc43f27a528d4ffe7f1764db439fc0ee4abf6d8573bfbc395c50de4acd9a720e452ed3a84973a80ba46432c51b5de94e6dc02d63e3a3a6a94114d62a72c22e65ba8a7a8f9654e2527aa44bd71059402142772c5852e872e560a4850fa7209efc415f32fb3b4616510db637afafd72b53dbcf207a982da520719c4e62ce15467dd278be20bb0ee934ac0ee93466cf749225ce82a579bd38f6384d20eb573ebd591efb0ab8196bfa0bec609fde2172d58c787faf4302fabf00ebd82b282816416b55f3e219dec4e5c49a3f8257e9142dbe39b66caa0cb7768bea78ccb559d88a6531a6a1da905c3749fdcc2c8897c2752147b353a510d117465d8546f922bf90e7520fa8fa2be4936d4fe5e6d8f33d2f6ecf397dacc456dee52794b6daea2f6c7952bb9922b05c09536a73fc6b81e520eb2c0d2f289dc1c00218b2bf90b2d9dd48e4ad854b752f003900ae04a87da3648a12446628e5fe2176050a9fd722585a2f40ec792455a678dbe78523b4a24416040513fd94232022308f5934f3e0954fb393252af1118536a7f2cb283fa451e221224107018aed43a6b34840a6abf7b699d156abf73f207949f4f326c4ad0fd4d09caef10908fc3fc99f99898a72fa3c37a0d7b882814a548744e4a55aaae407b5df3343f239fd854bf14b5bf0aa5ba45c21c6a7f0dcd8c944ea4112a0959446dd9fa47a1565f6aff4a0acd2003735e6391f65a0ac52f9bd32fc345256c0bfd948b602c01fa63b818c6b2d06f02a211287e59b9746a2e8af6d2aa58eb6ceda56269b55ad9f0b3c44a11205793fa39abb6890913f57325fda5f6c36521b0d47e9c661254fb864c5d1a59e5d40a75bb1c75bb9e8a6aa2ee6b6e05eb70506bef4af0ce6ca24af52bb5df93f01f76875b9bea4715f1b55210b5df83b647e968624747be33f30dd37d936edd87cae487a25dccd73e6aafffeba30fd68f839edf63a5d78e5ac7ad60cfb1b052db7fe8209f93d25652f220131065a1ae4382a600857eada4a4a198a07bea74a05f077590efa8828e1ec0eea9566d0f7caeada4a41463ec3eba425dba297e5e4e6dce7e9f3a4606da5229a59473e54af98e75b283b07b50d5d6d322c671c58afa8227155279c1120c6050210cb8a85015a5385185922258441942f203ad547845cb912184b8c1d5c5163f108a20c85042b7f20ed3096416355601000b02c1aaf04a0ca290e669fc385ef7d57736528ffec5213254fb97e3f83c1e0742fee6f7719fbee1a9452aea1cf8e112c40227f343082184703e67a6ccb565779ab529ffd9858bea4d5c2bd81d6eb50baad3d4af5932a8fefca4394a7d9e2cd45e2b5b84d94eebce6404ad47726c38438d147600be90b84e57cd1f0ed315e55ff72c4d68821236b129236cc2c464844d684213fc1d09249cedd1da4d0e3388b33dd3835128f4364e1cc6f1f90d1e38aca3539df26a04b039fdb1f3ce9398c4a2d79ee76d4e1838dbb34daca71235e8c8448db1c6e78931c6185315fed60005157ecf18638cd1852a460405810809546cf9899da5c62d6ac46a8c31c61b75594a961c434028e68c188649214241d7a0803c157d67a5e840870844d19212450f11e081d2cb2e2855ab8abad862071981a32f9dda5547e009a04e6d8da927ba50c20a5350d30d01d0820715b878f1c30bbc60b202165e144151188dba454b10a2c2d6124dca0ff55bf85ceccac085123ec16e0e413b104695dcd9f8d8caffc256eeba0f7e0888549f765a5b6653fd348ab8d40c28e8477d053ce79b9ea1474c4e547b0a80d5d4c1d9bc5009e324f562bd39243ec7249ac6eb0ce34f7f08b963b7504b11df414a29e50f5030821720615151855505952baee400042b60384141144140c8a658416b872794ae8851b4608a1e5e43614bc056498e3046080097141900200846528600c011c5ea1a4979c1f4a72ea5f420a548fdbef20c623a5ef50e54edeea851942f5555d7e88820542d46b9c3585aca855a90ca1f3dc98e91e00ffdd86584c414779ec244acb6a2c2671eaaa8f07dc0e7221d42d4f4df9cd23aa687dd42609d3f613f44a9eaee6024f6e13bbcfa767807d3c9ae7321d67e46e21dfdc3e2800f77d48f5d7cc5d57d2aef3b0c728783f763020ee3070af69f10af3aa0a08b650f5f83f1e32e60ba0ff57468534b5424431847da9e609cfba60386f43875db1eb7752bc4abf6262e6e409b5ad60cdad4d68a446b53bbfbbd9bb3b51765377f92be2852fb073e77c1406e6a3f3f7f6b7d6ac23dde0d55054880f3dd78ab03df91000bec7cedfaf2582bf575f8e985e8b54ebf00ab8f28b4b89ec9b471bc9e694d23a830fbc180f6fa3ddf89a153bc43015e60e8f6cec7e6f43fa13efc7df80560e22f41f14585eac1962da4173d4829a554819722546104282e20c2059d2404138694219a8a2a264802bc92859222f1e33671c5bc1206108e6843b8c088c88a14459cc00737cc2956469a8afabd0f2d1e27bc07f55b24a4badf2a1da1a56e171f688110aa68b9c1122d48e2c7b11c61a5ba472104510a8660a2080b58f0e3af4ae1080d848c8c8c5870c38fff0cfe85d6353a62ca1147645a767b365effad3d7f17bcef5437e7f48257f7d30b587517e00bd9e905b9dc524a97129a60fc7d7a6e4e3fbf8908b46b3f991e802350adaed11142d81b1d11449552055a943c95f24265ea1a1dc13a0248059de8eda671bcdef13ac5af52f114151d1d2121c9d87da1df561f7513655c0f821d4542f977773b082184104208216466ee7677a1bb37ef36ec61629cefee32fce973be4318338fd9c4a07c87d9c4645c8a02e5668f2d73df56b185fb37dac389c9fec9e35aa4221ecf3d1dde411f150f8ff612f0791b17949188d062988d8cc0a1327ba89bcd41fd6eaaff5052752f0249f522aaa83b447ebab97768da3069a8e7ac1046201f9610621d01a38a255354f80fb1183101054208e1a90796a37e5b0e8690d0208a2a290a5bd48c410aa0b8e0cb1660e030742503362842020a1f562084112994cc304b99c54e477bce037a315a70a37e5bc56680a562180d84a818851edc2a5b2c11e40589269696fc402427a298004e3a37fac333685529df291d5aad563b1ce105cca473a3d93eb1031dac503f1d15f2582a589c7051e14348045185464418c9c0880c7cf004880a9fc7e7891315be0f1fd6088aab2655da3a271acc099d60590192a7763a1182110864594ae221b1850d50758b8e0045b5d28c8c60c1beb0d59f856ce4b141961bb22fa43d66e6863437ff727333fbe021419613b2b44186c8204286ac8fca0b32a48656dadd20429a0767dbc9757f743e8a509cda36b406d55e3f1fd12bc4c7d6bf0fb70b226408f6dfee90d6599e9352f6b129fe211c7bc41844c8108fa73d5e0a524377b7c733a4756a689d8f37d03142d426e1ec7791cfb055d7263ed728e37d5a4b323284ba2e33011b5a49467e508178980c34b763f4fc966f0f4de5e5d5af9432cbb239b1971c08b03a1f62523a0c1c06ac7c9a0f8ba8d4dddd2625e0869658fd9c60e0971d10afdf7cc871434bac6a9f1c306374b2f380a972c7455a45d0cf592c2424f41d47696f635ed39eb34d487bcb4166e64c2205e140b043b86e0b5c5ea406b3ede90598ff1689b7188eab0d29d4a789ee2e96f6564224ed2d0cea20ab3d56dd77972bb5a40775bdbbefac2f4b4c7e7ddaa769e6b7ab756a7ebf5bbec3937ea8cfe3a11b43083f20f3634e3d32245de602e78145aa1d8043754f351d92b38b9fa6fbe6ce74dfa4b4fb509eb3f6b2fb78ea56f82d1598d36bbf9d7a9c3ade3856c241ededdb9801fd980933691d5eb5b73f817c27884dffd6227d80dd656dcf4cdd7724eec3ee746b53fbbb9f759f2ba9fb2dc4ee386bdfa609f473d6909f1833289d018d988052f92643710f399cbfe1792b6954fb655996bd7c9f6ea19d936ec6dad4b6507b926b26ed2dfca73931804895add6e12a65c74aecda82ee774ba875be5e62d24f5a67c906beb7eaf2903c3ecc25a04832c00717ea4a5ae7e101647436fcfb23e3f77765644e640c3fdadb476a269dda8f5cb7322c5a75f96568cf480333b4b7fbdc7dde02121894df1ec6098912c3a694d23d761edda5ecba854bdaff407dc6d8b967d3e1cbce3d7670c2d8cdb781e653e343b8df8120aafafd00a26a61b98f85baec39eac28f4788ac0e39fad9cc1a5ba8df6e0fef3e8f70dbcb511bbee6de428d1f2b6cd96d1c39288c4125a0220c5457f8f0a8fbd204384cd01d361b6a7cb871c2232a61c224e878f1e85015c173e2068e14aba3bd8552aada833096a0db9768eb1be37f83aaa11bb682083b1d081dee8e7686ee0edd89488e476032bb2123460dcd8c0c8d81396d5a669a37ac563c28254d89c51831d61833bd4f9b30e639e7d6850959c4706664680ccc69d33008fc40a2800c491998c105a4cb15083119a974dca360e3841b9b1a34503364c4a8a139c26225090a5ad26a11212464c4d0901244444d141555393aba828464455252162e97164a4a4e3049e70ca537287884224c663c37363568a066c888514333234363606e58ad8c00011d61b19204052d69b5881012326268688a92942c624f63604e9b86fdf8142dc0c711047e20558007ea42289b586366d6186a52ca8e060d4229a536a1d4349865d9369526f59245ccaba19991a13130a74dc34c33d6d025284609b1a247c1c60937363568a066c888514373c36a650408e8088b9524286849ab458490901143434a101135515454e5e8e80a12921549494a2e083119f9f52f145e365e27bc6e5e36af1a2f1a2fd46bc64bc62bc6abe645f39a79c9bce82be605f33abdb697f632bde62bbea494d2a7943e6334b9c7d8d160828ec5d88ef1438fb086eacf32c687f26394cf9551e093164c663730a74dcb4c533260ca528cd221364d28fcc7e3ff3b00f7df1675ff6d134b2576dca360e3841b9b1a34503364c4a8a19991a13130a7adcad1d11524242b9292b27069a1a464d2dca4b9c91402697a8dddf421e82ae794262d738f99d330db7bcbe0e61daf87612eb3cfa64f475fe218260a26b3f87abd5eafd7ebf57abd5eaf215f6e58ad8c00011d61b19204052d69b59e2879c164c623be6cbc4e78ddbc6c5e355e345ea8d78c978c578c57cd8be635f39279d157cc0be6757a6d2fed458490901143434a101135515464456935a4a3060dd40c19316a686664680ccc69bb61b53202047484c54a1214b4a4d57a22243444833324209df01f8f8dff7a78d838e1c6a6060dd40c19316a686664680ccc11162b4950d092568b0821212386869420226aa2a8c8cad1d109ac19f9de29927d111ca8093c8c307ea0c4c741f50365fe37e99ae69ca1f406857a08461631c905f3bc1c2a158f2e72c5ba2123460dcd8c0c8d81396d1a669a37ac563c0001c5e0998586c9ec690ccc69d332d3941590c115c3d80d3730a74dc34c3332c0c789a50e8a68fca7aaf15f0e9bff74dcfca7c373635383066a868c18353433323406e6b4699c24286849ab458490901143435388888a6cd85484c92cbe5eafd7ebf57abd5eafd7ebf5ca5eaf9764254a4b344c66386b9a7386d21a2854f7bf82e7e550a97a78787e7c8a16e0e308023f902820435206667001d90076b4c264e6c557cd8be635f39279d157cc0be6757a6d2fed95bd4caff992af1a86ac260ab89545ecf41f0ae6bf1a31ffddd0ff6cc8fcf733ff7534ffe1d4fca7428cff3c19ffad30e3bf1ba8ff06a0925c503364c4a8a199e17a78787e7c8a16e0e308023f90282043520666d8b86f034a34045912c1901b562b360204c44798c54a386806674f3099c535cd3943690d14aafbbfa11487344cce68ffd56cffc940c5d7f6d25e3550a8ee7f054f7239542f13cbe70c9a583e67d004a18931c69e1707d65d319618f6d8f27c3c11091aefc8fe9bf03f53fc6f9bffc198fea3d80e5ed39c3394d640a13ac6c138fa2197c734886910c3b48c7971ccdff9b0e7cc76fe3a176fc02e4a68b3757625774787f0e50eddddddddddddddddb0218410368410c2861042c6b1ce45dbd581d07b4743b82b3dba943a107a775dbbbbfb61175386685d232f5a35092b758dbc4842d535f2a2c8b99bf93eeddc56ee36fe1a6817f3dcd1ae06a89d1e06a6b379000dcdcccb3c09683a09c8689d4cb73f9048e537c51334c524787200063f21b0deb4d7031802a5375a68ec4e0dab68b375761f528f54833176b32b4177b300f573a5947008ab1c080dad8290c3135aae349150828014456b28b5f2c5529ce24a500a6a50b4260917248104126134667d2eb62fe1c77d47653bf3bda34e3d078d7f5de030ea6eceeee6f4360cd31b1cd00f35e1eac7dd77329e1f3304414914b7ae11e1f620b07824f49b3b284f39ca860a14109e6ef6807e1e8fd6b9d13ab1759689708344bb0685e3a9183e0a158361ae61355840b1179a4e87e93e140c46f331cc3c8d4cf779357b98ee9b35bca7b29fe9b8209a51ad63de61defdb5a3fa09e98a3dccc7701f911af37dea11f31a9f5c680e7b98a732446a0b8995761d04c6015333339853f7cdb9751fd53a1397753b6fd4ee38f6625454b2551886614934d635222af2bf0423300a2459832d7870a4a3f84f5d28e8f71ec70382b40653f4c0d81596cb1a1cedf02fc591cad403742e3249f7a6e7993ac36e4c4079e3c137aaea0bc0fe7e00c87e46f9180facced5d3a90767ee9ccd1b5e53e1110015e47cacc33887cec59845397fce2e4ad3ab4c3894670df40ab7530fd6d89db9adcced796d08bdb339a2692b6b6b304a6c9adaa3c466a6c9530fafcd4159db9db92f46754ed5ad71a76e9d4f067f3042df1cc6d8157e9dc39999b48dfb6454e7beaefa6b9a6fdb729f7fc3f63e186fdea142a8c44cda688296d10c0000044100f3150000300c0a870342a1509646cab03e14800b739e42745a329ac6b21c4761180419ea8001c010430040000666a4d100d0df795064311ead65739644e40f58d6d7d089910dde5a6450c003bd139a4667ee089c9e430be73a8ea1522c421564652a7fe88484262d4c7de3442e4ef095221ccd1477b1c01742d59e3aa3c6e52e799b1cff4561f37e28ac7fa8340ae0d1068d3c4e120163cf1eea548dfe0bf4beb6593d54af47777ca04379f0fec2dd2023f1904834a15428953881b2ac036fbb044643a4427c471ec9e520e7ad5707e81ea4bb5ba0c62270eea9deb49f2f50599a0e9c6d610d24017d80d3a1af5301defb70de48e57814baa059841ad02f0f1d09b6542fb7009c8d5a04bc01bc8bd6674c5c8a265273a75554ffbfb4fd99a50221497fb1c0cf988a351ed7adecce5f0d696c3c5a6d394f4f1b5f0caab64af889fab749fd56ad7bd06a8b72d83b1130f942ea13c1a34ecbf1812577ca9ce88a0df98353b6bf924231a30d45ba4a4154174554da53a0e06540ddd5bbd80bda6c712eacb2d393e50256644ca044672ac681d5721d57a3d2b36f5405d131f362d02d360f7086530543bdd056e6eddf7109841946237ad1030cc04927252ec439263842eedde300f25ce859e460148ee615029f20850064bd01235ede6813610c8e1b4cabc0b2d70d78539d0182fa1f40e5e7ef2d82ae070ca3b048407507d8f2e7f7f9068c31a46d093d8b956fc0840cd5ae720f788626094a750bd69c9ab65e7b8035c270dd85ce8e1f1576e0f025819324b9ac586f30663ee34bd12d18f376b7cdf40cb4ed162c0d75acb7411c4bb89fd10db699bd050b32b46edfb6b6070ce27a5c241fb61d386cf492097ccc059af8a3d4e4151d119235a768d469e6e1d565b567de6a635441691c91e989be5333902c92ec5e6cd308b17a601a8b6fbcec7c83d96cbb208671c05380771a8d0b0c3fb01ce3f1dc746f70d426ba00501d3f5dec5202669d090c68718fd50413988a3736373ec778e79210a88896951cd1e514514a581fab28c82f151e4da366e94782f3f1cc0644c571be3c65830a381fa0364adf7ce615553fbc65f8f26a72f4d6fb6c8802e65f61435ca39db3f85d08bb1f76ba6c8c3e0712a57d29c52e27e3dfb34edbc418de55a8696282919a75d60a099e5817c4d81b227c19f827ac932a455832c5bf4be1bba015ab1742b8b3290a2e9b9c2a7537624ee69173af4e34ab5c49b99b3acdf78586521b7d628addddc1a36859b8f79b8217b781c8f9a4b6b5827b9a1411aa811218fdc68b5c1c0678ca25b5081c0e3152db45eadd4e8cbd613bbdc991b758dd7a5e765a9b0cd76ba891f5c209e04be9196809ff53ff3f22632d9b47cabef33078b50740ed0f2a020bc8e1eeafe34422364d82264cc67994897afd3465dfe7944a7bc6e2a997e58ef464802fcdee263af90836a2f6456aba9cc6bfcb329d99f17daf7ff2a909b494ce5df9bf31ce92bbc7ccb9e732906ad4ee29a87541ee6d76349eccf5c9d7c0814262d4badc4123a4c6456da52462decc5c576d7df7858901dc2d0dc4e88c8712637cea1d1c82ade48f21dfc7ebb2e7e574b54cc80eba0634f80db44d8dbad324502308500c92e8eef143c24328020d45474dc3a0cc2bd01ea2880f1be68222e47278a9c6fc9382b08621dc09b4729b04c0f0e63d240dad460d77ff87d4595350c42eb728852205a312d2d76990c9f1c8589fe4f5b130e2194834cdcb54bb388c7f977d7a138e3d9ff8d5caeeb69268aa4b9fc0d43bef0c71c3dbcd5f1624ca8388d0b2805287b3f16fd9a66136baef238a7c3280add275d7ef67c499c4f69a30e7bc7f668c2bed1f33779ddbd78c0dc26615d33e7df328ae240e8c8fcf4ce12012810aef8ff1cde2e74124317de6ca06c6e5bbb7a182ad3c47c65067bf7135b1ce697ff70979407b833d413c20ef708f8007b0371c9e8db0f939ecfe9e184a5acd59e91c9d118338d091aa496bc48dd9d280ae2e67272dac33c635f0a1ba7dbaec6ca50c351344113856a3a59b731fa4667cc076c0158307c29479f835bb30b24c4523cb12808b0eedc8e31e260a18082814243048c902d9502516e584269c4fc8e50a923dec5ff2998a842f9ebc68a435962aad1b0ea76b7e793a917275110c2741018cb14be13ce6526387427d424f1e13ba53b4add431c9349f50b3c61f3d8145d6be94aa1b38a8e89213b584e434a1d253546f1617772f5e838c071b9127e40ace55ff197b06f35d83245b85ea3de48088a4bd2c4597f3b16fb3a7c36cec6d56d7b1ae3ad5bdee3ad791f54533837cf5a69a32628daf4f4729ef062f0a7dba733b7c8eb1d214936c081c953662ef163e7f0ef3ac14ee73bd0ef6c2049e1b0bacc8f69514a06b623842675d886316682a7fec877e8fdb7e0ed0eac30edfc049b69a97f05533d1fc3b387705fc2492301889e45595ae072f3c8c7eb33e420846a20a5c82b23668940c50cb45c23d467fb1ea415cba14f47f037a70ee9f8735bc0da0003facb729d6a0d0c8ec68413ee73e37905d0bc6de20c572880da62e3251ffb982d41c8f2e90906532fbc2d2a7d25c6747b0511a9653a66f7ac49be061bed33989e6f61b6c90dc4be5bdc3be29e1aeb4c1a9ef16a63e094e6390511fc83e453f3ea7c9c69d8e1b4b8eb7185e9db238b3ebe78810e2eeb7ad202bcff13112d149f469f42474bea1ab1b469fb67a6fa51577e5b8ba236d484af528b1c59fab24742877332ee36c2b08022aba9cc045685f7e274b3ab3c7df070bf839f6f3aa7f978549c77b6daa1de3baf7c5921f4386ef2ff34066265667e2c45376700089aebde43b96749b8e7fcb4a5ac0119fc22ad2d841b284f0f63f353c88fd675a0f69aedfa8c5f655b6caa866868907981936b27d7f19619686d503d90c97178534ae06ac93a59520ba355be79ccd1cedd67e15497a3c997c443ec9658b23f1b5f79164d52132b29a612c6723d1eb5ac9b8fa9f21e760f1d2c142b937cf45b41e068fac75015587e3e36fb249677e54df3fe83c318075450f819cf4f4b843a55914ca0da4ea296f3f1ff1a510220a5d8ec022b45f7c97acc9a07e477da97c98ae1abe554431dd3ce5725b9888549e545ba8ab119cc04468b444d5f11ddfca9cad6e7c4eb6f3b9105d5d57e996b4191d0e3407e46d068b36518a1d6ea0a3b52f7d87251df3a36fb24a03f7c5db8c5c6302b94aa5206179293f052dc9b3eb7aabc04b4cbb80848953ed738a210d7ccda514ba9cc1476f3ee61d96f4ddad8ad5514f76c840e205457dee2ed0a9479217e90e66bcd8c31079dfd0bd32111d8208e3091f20bf4f6f4332f128b596086ad7af5e09de29efb4e7c07e657f098fbcd34112aabe7b9fe07accc09279f643e3b8541f16f5ba4f32019bcd54c1eb531607796c2d789e33ec72c23216461ffa8ceaf9fa3d866307c0a8f6e8ad6c5a0b1de239f2ed0d923623fbbcb1dd3dc3ab19e1a610974a85f73b73ea315834fa5a42b2f7f88eaca71d4ba180ad2ad6c5c77d213cfc5d4a8cb2c3e633c4f38b0f9052ec8ec2fa0ec23087d612a77d41a8ae25706f71d8146c30a9276ff05138c6b008cc40ba9bf70f9cd2bc6ec67090291a34232045387e6d696d03b96703894d074ec635d0c15fc9cb736850c7ff4f6b98685ccc06e4eff2dd1026323aeaebfaae06e2b6633b998790b4bbadca9fd68700d9338de539cc53839f6a9a8b721d880bd8d9fa196dde968eef8e37297c57dbda24f3c4def97686c7350027db4760ee218dbad664b4c05b1735c77cbc73b876000e0dab04e5c7a3b34a60dddbb1a60214d874e29bba9155e5cd1475faec963ad9889d4abcac18a2bda43ad971332e3cd0f961263b155b63b19f6689acc082ec27d2b72a38bb609078dc85bd576045873172549ad4c600bc0e1706b5bf3426dc6ca74812025330451d81d8dc643a16817af9e13385942e3c6c0de4302501a43bb027fe3d18b6e156e19aebfe6b0bf6e3bcfd181cda0ed654b92a432ba0ffee05f613fc4bea680446ad040f6d0a2ca458b65c050769f94b1fd0ff492b6a0b6fb50ef3bd9063238646f96ae75a893ae15709aad73aacc2b8a7b8fe44d356fdf879ec319ef9985ad49e17cec4538b4e00bfb2c3d8495d1480fd1f1752767a18ee8459454fce06c8b02450d6dcf50dac32e951453b0c98e1fe7e2e3de61566cd3079f4bac8fe404acf07237d0406210e2f7f800dc2534ee689f48eab0fe0af6fc8e60a821759fa76383dad01a00b11f260b7cbad6079e05eabd455f88e608c3615ae191cd60727e7de3b08ee483d60689edf9bb43f765e7e0c47ffd1047d13dac247f1268d2c8e17a765d6ff48fc1fed4b3bb443605a5ee55b42d54464497785436a0f74fa4669dea8b3921012c73af3327dc75f82126a68fa36b0e8b059e0de4009fd6a20d715fda45227b9c1d82e37007702c52cf5cd929535a777269869596a7766301d9a734b98e9a8c8701e3e1711199a014feba06b91cba96b9234a97e3fca33b91d122db83a8d142202854ab2a3232761483f03f31df63de5cc63b58cd8b61252b9507ef8bddb540dc9d8b776b8b3174dfb65a9d626ab4219b01b1018d9146f59ce21a0e7e386986366c79d2552b165518b868187328943d20724055a8b1cded27b78c287b9494110f2e8abfc61794176ce0f999953ec14992ccd6cf66c85b5ffd798751f23458e4c177ae9fb3c30bad18d0ec743b40924b4a1f2b6e89250695e6d01935e3c0dc9ce3f2c61b062e739390cf475be0eccdab1655502b070949a5f499d59b9ac08c19cdf28c1850e5d2d072bd0eb64c442f1810fea0762972bdc05e69f2dca7de0a19b63f62c1f8bad0b49ea1be7c963d85f51195ce9f1bd0aa38a80b89c22216d3e88e7012934b65db8af45671f79eff83301efe623453172d0ba0cd103319619761f18fc4e34707d1358c32015e5b58d2e1774988b84db402d051572d3f9497e57ba965e3e3cad30a43a1f25ca25b43901580108d3e6170ce01806ae1a09ebd08a04b3e1169dbab9c1aff4cc3f15fc8e5e0eb16fc9ca8d0cf9fe54a5da107fa6daa580e007636c7146ebfdc2ae45a23059b58d90f203b48680743b458c6fa916e3a368d1720cdf07855e13bd1f53e9b0853754827d16f96e2d2809ab3cf1bef14fc37c0d36679f5a0ffbeae887b99a3959b64faeddd853285608e2fa0c9bd35febed6fec065e0d983cbd8c187924757664a8a1a52b2b8239682a5f8d825fc5d821967a86390d3f67f8587cc2a46a274bc406449f1052635c7680e4e9e36f918cf459759ebe40f5cdb278094c7a3fa635c163f922471f31902eed7d9e3cde038abad625f7da1ff2405cfb3bfe5813747a1607bc223ff872c27663e9c8113ac4b81c569b8adb78072b471bf497600deaece3631666a400f9d1d4993bd3ba4a98d7598443f529d7b2949584e26a8740af14e66aeb2974dfb2908a269e07a87a3b6d1910f956deffaada1417e463f4834feb0eba9bc4e7a58344bafa5de615bab8fbebd6c604a6c28f82923c423f7b18ddbb6c0d221a0839a40a9884d694588150112893a8a72f7c1b4984fcc1f0b666d4868b0b6f46490710d47defe952a20bbe4d46929a59086fd39989c6ff4e01f33775112dd31f0a1c6b9dcdd8b1e40c4aa7e3578ba4d29742bc796cc7fff3dbea996345ff02b705e253a16c7400479fa29d257c9024a38eeff5a36f386354167a59e86e7d04bd74238bc3fc1c020d8ef2eed09bf0c5ab03ebc5dac26c2670a53b3545997577faa1e952be4843c1d474001a87bb15bb92d4b919809bdcaaf77a2427b0113d4248e67253782de84f09487167295a3aa038e9091ea44ebc09b7b2e963b73bfb07312b55255e0777fb601fbf2ad561399947d7b55b36cadfe8c39eb7f07a606a4c348d4dfb7a7fa07c989f72739c42e096be1e0d3c35525f4799d89fd26273a32b7e1f2a7e01a83b7e4029e125218276b6e5568d97e7d5f78c449bfad2b66c991452890e2ec07bd555f5c99dd50e59212de51211af6cb6827cc6f9b17381056e433a18e4f3c132a4c6fa4708fcf3078143f80a1559a6d8b9d10a0689f4fc61425db27380b00e6cca6a2b6047de0ad42af6b6b7dd3adff083c0360a6214cd9e984c8f0a48f8bab19ea50a2b93d04bd972a3a04c05abcaa20e346732c91b58a5944c1505ca36ebcfae15ad9c5c0ec3f3cb6acc271be14d2927f4afa1282d5fc7673887d6c08a279152ec4292fa2a7e6a99bf32885ef541c4fb45c9d15b181c8511ed5c9d4e44d8b656374146cad1bd6c602b892a816efe15f4234266ea2f44d45a8abd90b06e54939d05055bec508c5b76017b815c893115f164dc0b9432d6417efbd137f214c8b4370813d997f26f2f0e8c44ab8ea402d6defb8d32c1879aad03afdc204fa3b03a8ad00baa06f5196b4e0cf582ba8e2c110856ff46edd4e524583796744266489d9423e3782aa9b25c515402456613775356028df4b348b77d9fab9612c22285022b40352346627a1388015821ea8a747d8ded86809e215402a9017250fd61b106f216d465a4c6e822f930ae7f723dd1c57a98dd91bd0bc3a94b7d01560477c0846323eba415220177449bde08ed83a2c3504e52958936ca2639b5d8e6a71ca5669686015f1c645e520dafad39addb64f3cdbc510127c4e495b55201d0afc324cdd87093bd90b6f83d0e0404a25d82dc0ec66a1bf7b601f80f65bb70edbd5edfeba657cb077f4969d7c5e323f5719a7bfb8662ac24996a8f4ff1a8762f057fc9211fd0cae983efc840ec734e605f5e6509629b18e64620206d58253bb22bb03f480e42ab31a796e0f5c1ef57625322cfd184843e6bcc2a34c1f58df7222a7c3790433b7b6cbfd47646155e420cdf42dfc1a27c366d1293ad92df4a7d01a81cfbfbcc98a3de66d935c8afe64335bf1480a8af8d443af41b0bfe830b940a718fa880fc74a83a85b7952e907385eef739858601124a21e0f9a4c898b21e04eeaab7c59315e9f40da080a04b94bd2403714ee0caf44d35319f9ca87e1a6bbb5f7810a100f957f95485e85713654d9fed3233fca2ae425c10475ce586bfbd55db18562ce13e57a293b11411363781cb950e8e70e38012d6c02e4f06251476c14a2a30e21f8d950860e20028f8bab834f1efae6c6e81c0ec8f4c834273398f06eac1a065717b58410a03c109633270851a1f7636684415fa4cc530efff811b7e2571760fb11ce09e797d0e599012013e376f2e04c6c68d47cf077ad701153f3ea2fa478348e50fcb2e05a1218e862bdf9616d603a8fdfd2adff361b9fef6f8b19edfcf2c1dfee1fc8e86ee47573d83ac16d7c871877bce5751bf1fb09948bf858d43d70f96347b052bf2054008810e84037daf658b41a34c7fc8678abb1b11eb37b2de8f8389e05bcf1fdc816cc736219bae92a531fdcd671b2ea485b6e56463d81ffceeaf34ba5062eb537249b136b9564b2a83373bc8e934bfed3d8c66ad83f0e5d851d6cb23c18832a22f4ef93e2641ddf13c8791f06cb2eb2017ae72138f89fe1c85f2e57f2438348e6999c0ddd0cdf5438622640b6ee30796a4b4d4fb517e425b1aa9de428eb56285414240aa00a418661b5724124e29d1f08af8f6dbc8e834f574e6c085950342ca7209794a794e14fd583f4c807860e87029112a7c58db8915d116499f0af0a1cde3867eba69ca6fec51a16c8bdc9e750c35181d4f7b54624dc6692a9e083ad55f69109ba79b2e5a7c8706b0eef5df4ed13f955833c4d9949e2e0d44894694540fff8cf50e253a26f4c094f4a176a162a8ba2be16d894b1c4dcc572d01e04e8d3fa6c19e007a0886600ed5e5228041dea74138ce05528cba891a70b935ba81b208001a75e29d2736b124a85a6ab50e440b147dcaec352575167876bb07fba9e06f3390bf2e46039aea0188f8653456b810805cd2dfea5fb7bc626194c123e97da879e63bb3be3b830c86661dbc29d29651e32fd9ef156c95128652ab3a47c6b5fad20fe61d18d1220bdbf6ee0dedbcfb5f549e765ee92eaa151908ef72b29baed959920547f96ecf38fded3dcb668c9c703d66f37efaabc092fdb4594b46b673589c67e4df6a2dde5c9525290f9272ccc963edc4cc47133888733c4bcfae643754f248e984f476e63653a7623808c32001a191fc4ede45b04afa77da9e2524d9b7e62f44bdcf8820c421022116874f40f2338146e3137be335016a8271ba53dcbae0485fa7a147dc45253e19c2995fb68b0385cfa8ee0e7b51b0075bd75d651fe7d3ddcd948edd303d9dd149c607b32d0a90174e7fc22f4828b0403040a1d602dc520ec239a1f6ddf523322c9fcbc2ea02585f92dab2683442d7fc519df92e6f0b0a4925c5224814128538ab53962f56acfb068e00628c54edf44a2c23014629036ac7211e07919f61ca1ec7bdd6d8cdc431e8a17b15f65268d5c5b46b84bc09706b708f62613b480bef02adc1b69a6d5bf041afedfb1015a439e3fd36516eae779024b7e607e1162b1ff69c5cfbcc25210fbe20257827019ebd97ca25b9d8c639c913c715a18636bffd41acac736f050da63012a53f6c1bc918b21b064008b7c952b505e20fdf3e6ee9b3487d92442d100326ea15da1162c12039e7aeb3698754ee526c182ac4b327596514a2dd126671513eb609fe787f0c98e03b048881f613a66cb636ef4b84e0d4066f1608439be18e23e60e42ddc1d423ef57f7094587c682ef243319eb1523482389fcaf2eda2e0e9a635ab67b0b161d8c4612ae2a8f7a465881762b3bdb04203e209d4c3a5a1d307d42af73016e1c4a47140d305e5d3d32df9b6780e7f32a5b7b7532ed50614b8316ca9187ff652744db4e18faf3658f9dac1000d88cf283a3fd0e5d6ca229599e6aaf4f8ead73048d5c0803e47a5516e8e80defba2929ced244a30f268398d2e6aca80367861d605b2b6f7881fbc4bb86d0b6c03bb4704782251d3935d398863df66aaf9531ab00649afa44bc478bde633e739abc7aaecee9387af290898538521e1e1160f9429f50769daf79506e85f3d8187c20d89555cbb5c398c502e376a80b5a5d8f853ae59648e99eeba0672e121da01c3f72783de68543eb9f24feb5d9dced675119f758ef2efe309099d5c33536eea5fa0c5960b9ac90694b498f4eea9ce02ea426d14a9b1bcb9bcfe8f743c69175a6d5f83aefd3b8a60d932c24bd6bb1945dc905949138db8b41f2da228efe3e7ddc80a689e77d80398d4b50f1a414ab513a3f9abc133a91623d6d20b8ebfd2d65d872291099a3fb4c807b087aa0c1902bee1e3501cf16b2ca68898a045e3c70974da81b4a0771e81bd8e54cba3ab409e92923b0a21a569701e95f800d2f061c752c3abb3d8debf906bcad8b952344817d84a486d4828201d84bacde7a8aa2948dd5488446ed65d34c3747b3631ad3e9f16735ffc227092f697a856f366b8014bc69557d6a7062139dfcbafc5c7b1c36662ce66ff97d3778d183693ecab98cfbe941602d6e3c60bf6a9388147a45896e29d4925fc7e12c72d32a25b5b256206640e8fbd7224202e584188f52ac593e13fdc7d4a5e81337af345796bbd65894e0b5e86c19d9c8bba0663dc6bf4a4663b167bc14592577c803916b0d4e2467c1a2d12ac1463744b18af81d28cbe8f822dd51c1201c4275d6b79e3acd08b00f475003057088abf5fdaa30571ff0e62e419c8d086994cb5163abb796e86943142a8e622a6efa5ecfab5041db15e26d1ffa69136f7ae3bb9eed26407385b72a02e81f3a2daaa6e094e5208c38a84624c64f80fc81d7d9caad4b0aca4953b0eee10529b4a4272f97ef05b49630273dde627f6121e39afce8e575917826fcc131cf113fbeba77f2a265f3eb38611c8a5418bfdbd916cfaa06f4b95fcc0ca5d1c6e5bc48cc1e5d221e3664f098c008ab98cbc929299bc6dfb89ad30580cb2d69830d9a885934218be71570c08f8aac52d4e32de961368063aa7f72a913d98ffa0e09c9d377ac42652ecfd29241ee6369a7b3f4b167f4d7799ac227378aac77aa1c431796c6cc27a422919556157f6f7e7aa17aef1d26239afbaa735b7f0d4941546e6e0bc27bbd4b848c2b55e3f7b7bd639fbc31ffce601225043bc09205b307f020fbf96e734a7f9fb4919dc3c30f82b8b63a35c88b6e007f6f82ba89c30d72238288599711e000fdfd84d8d05ccce8b02f63a5ef4af63764936f48a9b443fc621cb15e439b6043e98eb58d7d2ba85d0ca354025c35524c05393588c75d4ca621ed4f66197e893b5c4e5b04b9496ea94334d32c446d995da7c8d59335a42c5a0d4ae1cda57b3c3b5b6d2c68cc873e056726b059a22e7435eac8a5889bdd2d456c6bedfe386bf519ac9dcac4740f7724eadde37cf22de3a243b3ed97a4fb834107019bc8b97092036d60095ef0eea350d29cf0be4f1e6dde1691453de32a5469d2e675f91846284cd5ea828a85746cd7346f2f8426a1c19df7cb0815d9e9d35290772034f21c756f88d5006de821cae6851125a5da3dbb48b8725fd3a31a25c3195c2e8ba295521c62e856e66d7b68c3d232e2dfd4170939f514605c2a22f3766d52b5005b92c11cc0bf8acce29789eb33ac008f303651b3ff7c645650391f46d4cb1a87a743c524c5842490101eca0a0b56eb74421541bd9f0ac39683603c50c99efc3704c9bea057ecc451baa944b0822355e3f59108a67da51103337deff97dca62378c1d07f17393775e3800100e73e5ff1c24bfcada77d9fe10226546939a9d4b58d995fe679981f0533832bb11a9dd3eb602c7f65e3d35cb15bed3a6e1d8b11e1aa40177b45ed3199edd77e0a2efd584996c46a8913c6fc8589617fdef789253f9279e76c161cbe4d3a8f439c28cd963cb8163fab40b9a0cd8de13abf7c826040a5241fa1a8fd1324eb40906f1f40f9188944362e983d39ab89b87d622df8b32b19c1dbc4d97c4fb9b4cc287ff50de6ec9487bfc6767b2f1b781c853c9f139e1dcebe1c1d7927514b68eaf2b0abeae8c442c775a20598b91456910c819132d19b30fdce330aae2ef32a90db888ef5e09fd5d747c5e54dbf130af6c29677c120ea94e61812530b76b1e60c6dfa31a3a4230c36a90fbfc8846bf932620e3e28c4e863b7992012014668cca1e7346d593a8ac436de93830077cf915f948917d0c21ec2511d831b32ecb99c77fa3c8bbf8542b01ca252d179aed4c24109a2bba3fce9bce75b80f13a3ddf96af9094e3157edf29411e677afe1127188f442ae59a6a5d2ed26c9cd0ba8d9ae3560790e40cbc829b4950585e97bdad5b3908e9a4e108f6a3274fc7915677b74a16091904c21b546bb01beb7c3fee40c8b41ffd07bca9a1187d1eb35cd2cce60b1cef23d58618b255a12ac6a5f0badb9af5abf40bf5d0f201b91b4b9cafe4b0d66c60f7146a6ee25ef7f750a81d6f935027d00ef70e9f48b75d800b0d305db87b8d9e39a1d152fbd1abe6e6dfe0340c70fd2887af99a04a36f173d5ee7210aaca7db26727a5e5e261dceacd56ffcf3fc2038d07ef4a93c12699dae49de9079616e9b38dadbe6bd1c3bedc967f150ef4f02783de16eb8b74121287049024fef877ebd8de1682faacb7962a8fecacd5370bacee8c2302191b0fdef9a79d4a18cbe5ca5ef3bad66fe55fefd8d9dc35e08e8bb11ddbcb9bfbe9b122a6b76b8dc66d99a064f26faf3096a4de5465ae8dacfdbb4840f9334475b8357d42140802c104bbbfa2fffe827859ed650ccbcf5c7dee37b288b34158a355889067261faf5af94d6d2e2cec9caca7edc1662e68a74fb41d9d5ce39fc9fb38539b89e1ad71a7fe2d658385463eff43d63f106f2171c92c559885af31c0ab7ae85a8e031208e899844278eea1d3da9aa5e4a94e8d9b73549ead2085088f13bdd567e1e2e23923072ab76220881d015e3fb390687449bc8ad829ab6286823d18bb8440dbec107c72d15f281e316e4c0548aad0fd8e9d7e7706a427ae31db9c2732d4cfdd671cb99d5c7ad6555f9f583a60930ec2da44dc5423637f9a3b655c284b8269e9a0fd9e446096468056e7a6d598190398341421726f366ee0261c6d0ad1203198378f1881d9ffe0061319105af7f2f9fe91357cb5033999c0c0f32496a4cf22878368556c146eb1ba76433e3921bfb36dc09e1fa738ef170a634a64ad9c2c93d485a7dd7d38a0895d936e29284af993f425c224c3c22c4916790c311bd120eb970b43307cb9273c98501ead19ca024ddca20ed8a78b797d4b164b3223f8a33790f94f1daaedfcdf523808207e8383cdfc5fa2913507d176d1630a08b2f8a090081c41ad355ea28606ca789cc2aacbe421e13bfffc564ea6e245acb8f7514f6f94da50b60fa95a3d256697aec17c633e1ab278fb4c4ba3acda78ffdd628cc1277f9432d2c681419a8973500ab4ded261d4b42b3edb16f4198e89302822bf11727fe79a5143b43d40dfcb1ef38a6ad491a788c4bda413bde92ef99e44604db7fde66a0ef8a3c4c3a30498e5ec09b2a65abbd1c10e906853cf88a05119aa502b8f8fd03e9647c81e1887ef754ae16fbaf6e387db28643b8b66a4e9e7faf31d087f5e1350257bc5c802a07998bf666a33fc3f60b28d9835dcebf8c5809e622378670814c30b9dd457159fb26b53e1c95e168660809b882ddc1c3d4549288db429866d34c76220154ddd4ab9e5596c13f60e13cf96bf5f831737c841c40aafa8e905cd01d7ab146bfd12aad52c7a685d8515377ebc87b2a9b1c3e6a93e0ac6a54d68a86260ddf635adaf7b09598a7d21c060a04e0c9273540f449264330a1d8d3f2a33cd214b7d4b76489a8eb05246f31457281204fd66872eb4057ed6284e4ad0f8095c26b3f54a17ebbbc427c68b12586e5104ff44053f4ac1f17aa6a8a59a4567d7e30b5394b69ac5dda4b22ba27d1c0251d2e60f627cf05e2c984cf15f84e0a9842896dc232d1347dd9a1fb0cbf58c2c039258059c52731179d1fcb8913b1c1a158858a408172d333c2f894e7877824f0b1507daa2733aaba1cfcbeaf786dfac0cf36624f6146a8173df41f0daaceb7d50e5dc2472c079d5adc8361313e20d8f575c031ede05dddbb7865d982f272dce26837d7fb61c6990e77379061ae6ce5b9fc9f3355bd4ff9a7db73f940fdfe0eb48a1152e89c4928723e9edc14bce90b22779c1b72f36bf24856720cb33f6a809bc9d2ad421fed5227bce558898c88f3f23114a453e7af0b0a760f9483396b571a37a4f255037ea0b2986f9a1f398bc80869d5d7d1de832cc72a10645984cd021c490e97306ef075a842a3791b2ca4816f3d23733ca16e917342c1203d8c846b1ab1be32eafe49924c8e1b81489d892215e90c59026138d530d1577b2f8010057e218bf92446dd05de7d6b9678420e0ffe060cb9412de06edc13e639d5a655b08ac6e3523aadd118ee1b108b5902e9f0cd94f59f9b21b25abadd84112b91a60ef254c0f394da418e64431e6eded88c865a976ae8b6332b92e02c324ce0238aeeeb94f1776b98401f04da5fb7e167fe892753678f7bffbfc1829c2ad70529723034e1bd36b9ca33716114aa3b90bd8c13b4fcdb9fc8a0235715a1463a91d7ef8814842283b1258808f4ad671c0e7611d7ff28f98300b79b63e266b7d0443ff9c99f7ca59936cfb22543b319f4ae0f8612416d7ee803b04c63c275d721f6fad0b60dc7a053fb11c72e33a78424addca64160ec17c71c5f0094cd63d2df90d565873ce629ff1f0bec2412a5253932cb1f931b36f566f757061fee36a8b4c4548793ff9e71cf318743f4a1c9488be3994f7b7e86c6e1281db3e77804bf2c7622474d9d9c4ca648988fed0e6d84bca4aac8479dcd701568470bd61f0c0b2888a1f23a35b559c0b816acfd6c1e53b892f17b9ecdb8650498a2eb11c812c800ff12a02b563beb2ef9efdcaaab337fa9572e92eba5aea0bad4877fce4843427637b7fd88535a30829795afff493e9998b5aaae17cbb6cb83b561a84061beb8619da0ca7a2e5142f10096d9890c65502ac423a1b164c23afa34de779d0ff0ef6d883f44c9628dab15b6d5dd7ce05118633e841f563d65ebd0b2614094a2e1b0de1f9c3f230f167c8dd67e2a20fe7f90ddff7b06f55b7462db05987eac44bfe9432bff0133585b7494c9a4c93b76edaff2e6d22a0e93e1e9e1985c9f8dda50822ae7747a8ab46ba238f438985335129a133fba59fcb64e969414be2ed36efd293956e3c89c5ebf846283b6a2d78cd2a5e3858e18c51cfa4255a0121b4af12ec4a3295fe949668d6907f7b5f0b9f9640b239d6d6367073680ab93f2cb1851ed0aacb307fe3145bdf49df5fbd414e3c225d645aa86c26f5896f1d7aaf13cc0a00cc2dedd5d3453bbba6830d804ad401b8fab7f6510dba3a21d65d5468e1107a7ddd63c06645ce6c7f89e82fb95439384811ad02a1ef00f16bca32421490c87f6d3d140d1ba6234ff9a3b74e66e774aec2807fe18700587a5090a6aa5f0f00e868f459d93831af24186695eb3c33d68cf6df43d2d3d1bd6f6420f1d7ef572d8e5d1d60d9f5071ffaab15d02417b4d6c94ace7a8246fb6bb4c2d45f04ec2f44400dac6fdc84798834340191dc18b137a836fd394798eed4ebfc39be3850e33893de7f928deb834d8cfa9342e2eb1250506f0d555007098d0a21cc8e4e3a636eff171ce4160a842f925ff50c3b1d257d699723b3b0d47225b092139d06c9043b87c0bd370d17064c7fa9b35ecfa6c0acd3aa3ad8170850d3c9a5d2bcf14221d77378e28dce1070af311c3c5a72247d9a43eeae38c0fbede23f500cb8a372df5b2c6a1437725dbb82f94dacfe713b8854c1607ef473d8b437bcc5866cb10a8bf9f7d056e6501d2e89ee8b52546ac2c1153f00b7a765eb435eba65bac805fe592142aeafeb5019d6d0f15a6730ce85837e2a08478c2dbef608b0c7912667f3576817d187ac4a22836bca24addffac70c15cce5604b0d7749d856cf75f7875c50b747ede9a43d3878708283283bf908efb14d4544ac28e96a7ef042a54ed91599496c40bf9fbfeea1a49c7760449c5d4f7008d7224d4b3829aac6b55a3f2295966d31821e20de126a7bc34a17d65f1db753ad4868a847c95a79b6d1b037ca9403f12639c5d94eac2e8087b0d302f914fb068290f77be34a0ead151b5e673273b54c0a986c85101d3616387644d3d535905e9baa83618092c54841479a06043804e081821364af4a5bd7151e77606ae4756e415a6debc80fc31e0f40665cb4521ad0eb25f09c307687630652570d83defe779d94d8bda202dd68b64b7f64c39edea7b911f58b975bfbc2de1c1ccd55bbd5c5d98b3e3a6a44899d4733dd5e73f5091ae98d83cce87a252d710a63b68b6b4d738e78b24b4413463850def2ac43fa0a87b9fae3d84a53df24655d86dd6a367f92eec4f52a2dfe029bf0d39fdf1562f38419a95fa3f1a9cb44d35b944c02b499304de70db1430a535522898b77eb812a54d0a8bdc3eb5c07f12e5c215def461a268a01fc77e2f3d43ae5daaf1092ad043548adba9db475f903985cf2075e1dfe217d44105466f6f107349c0290aa4f7e2f5d3a1dc1c8fde1ca8c79332014b6cc839bc05faa9e5405948cb91955c07e00dadeda8f0dea0a8e6cc6a6d93d20b769821f5aa4db2c0998f274583e6e5a648eee828a996341e1497c63118aab06c06c7195b80aacb62d6be6e8a10fc50ca56b9b94ab03a3c338e3a16be7aaa6b3d385bbea89c81b5b907a0c8c2fed07770ad3b1c8390f4c1215313e6981235160fda3bf0f46421b834856c83c6c42a575cbcd255cf4e9419f56565647bc1406f4aeceaa601732585496c4d0fa05b221b55179c211b90934b756c7c00afe15894635538d2267924c6c244a01bb536e08dcd1b35887db3d746dd7bc14923b54a3497da8637dda356beccdc18696cf58e69ab762792d874aa1e44c5a8dd577974d1c667f1e8a0aa72814bca3f937786b74a13198b9355606f8b11fb174a8b8b2d9c81bbafe90bebec204a3a93050328eaa3285f2f056199597bcc353e14b308bb5c3e22d91edd401124e03727bad236899b8fc26d0b51dcacc2b0da9414aa7683c3af2393cb106ca6223d4bcb37b77706a31bff303252e2c015dd0cd91a28fe85a9c28391e988ce70827c1db78ced988d71963e3e6de4b0588101375f33b79cb8d57c09a1c3637663da52523aab19532c031b883922e4ef836f34b515b3dee861f7907fa04c0cd4e083d68225d86851cb9f671c81672804714a9020acd0ca0be4f9c33eafd9286b844fab9c2a8580b5f79c7ed7992bb34c19eb5ee1aa38ebd620c999b91ffec0902d5fa933c47667c0628272ff3e9d58e623049ae0c3caa0f7223a48bd647a58ce1e95a62ba21b7505121cbd13411982ddf338793177c821ef7e2454dd2180c7e968f24cf53efbf7e962f769ded72c00c7312ea2dbd47ca95ca1eff0aeec8a8f331bd7405b44840cb0ba3b0cfded40a208730fe62e537c99561714de7d4f3c81e0824d7a045db8da8f9c88e1114a9a0c97e1fccb7f0126a95d942350ac132157eed442117c22cd69487a004c08d03eff97569e18adf23d2b54dbc924433b6efe4e20f42311ccdd66943bd5e31db7d4b1052446626486800a178347c5475274751719c7ff66073d2120652cdc9f1affc2a350583ede431cc85ec00dc441a6b529592a8aeea99047d257a05744f281982f8178c9a1364e21c9dd63fdae573e2b47f87b138a4b3597377a507e898492dfa61bb255c40fafc8bd9349896abe0a08b191cf1adb21432660a663656c53bae3b8f7db657878b2bb2d93e62ea399aeb6f5757e775b8e7fb3696da882d24d6f9465f1566edc0d2d6c08d2cd1a5dc56f30f9c47f78fedbd58d09ddac40b279026d513b7f9c1ab721dd1f8a6835218b0c62d8e03c6af48d530d6ba7c74ed3f11b51ac0b9a8f319ced2cd2846b6ed308a1551551dd598fd2f406e8731af3a8da3dc6fbbed744e4b22b3d73d5331bbb7245082ac9df6d72fd59db877d9b8bd442d0f671ef0eb2b872439c1eeb6b92368c52b662536b1e85645705511119f00949d5f2890652d1a71d5457674eb761b9d02657498f9b9f7bd8a2cf1cd6b1d5b45d64c00c7f2be2e546ab6e74d491898addc3e762c4476cddd4e5711f5a4ab6153e333204faa185c56c2a81628ddbf4004ccf4ce649c120d20fa33219d0196dcc0c7bafe2e34eaf327171a72a52c93528559ccc5961e1d502b158a7a701114c47f35cfedff455c23a67f5de66887b46bcda187dcb46f00c16e2225e88fc9fcbe5667a0676bbd527d371bd08eb4abec65801c226e021da0e0526dd1882755901482e8070572a43366ac23da5a14bc96330afb604b5367f64ac9fea96321efb0ddad7c329b3feee015b6a668e4473aa520c94ec95d4e367b50fbcdfcfd81e173052526cb65a34aa748476112c0c25dd60eb7a532d90f541bb948dc26018f4443bc714c6e67c0a18dacd5c6141b2ba8cca6bc820658d6de9523a8a42066fb0b3b56eed8f9ddf2d3735f87fc49ed82542fc2f8c4b1b71b1ae93c62ea07f1cc052c9dfbfa52f2f180943ca05feb7d5dfb04f57d4de9136542f9facb60af908e6c4a047496bf382a1658ec47d3a27be7027ec8555222f9f16dff7029983ba3bf0e99a913ee6f98ae03bf295f86469f935e0c0e89658d116a697009a0ab5a99a7434d55c25a770d4b57a741a55dcd7ae33ee33e819bad15413789fa3f1a6f116bd45c9cfcd8f0de0fe6c71814939633b8cc3bbab36ba416e0fc45a5ede806620506a6e5e222f0eae213c7e3a3a359fa4354a6d9e04dab5ced04bd140a0ed447f9ca458a9899933495f0337ae057093e52ad9aab05c6e4e161a540cc70e4f24bc7140ba401b836897e8651eccabf4fc3a1c0c829668dbbfc3448dd5cb20575753e7b1fb97a098386a10c9fc551021eb23eb681dca3c440b98b9bdc80723afdf5f22bbe5989755e2129648133ac04c1975118af613dfe9883d2982cfae922a19b44a875ca75ed3f5b92f704a2a0c8ac787e9f090f1b0c6f66223985544b68a5696f2afffd15601443e29d2afc6d9ffdb4f32b67e4e8720fb29102cdf4383e58ccbbafb106264e5a2fe47ff0f0e573c2e98b849a9f1b3129686a071e9b57540cbad68dd72e553dca204ee40ed99e0367ca45e42ca058f9c14da69349c183d8fc49d8236a0e81c9daed7082366627e9c95f72f67acacb320ed35c915d1abb8e49340e41bd3c04cc591a115e1664a9b6d8397097d9a6622a7c60e7869194589be5c0c7d6f0854dcb9f535625af328875c6266c3c0ff3df818ade7283bc1aaedee4b29e90666ccfe4e2b0d451f0972105ad695abde4cb846bd99573060701d477496c20f5c8a611cda86e46e6194b8b2974cad6b5fed3af15df6447fedb1922d54f71e325e7f27a8303fa42f60c14c6caab36ec22053599b1091540235472e37e774eaf461a4a64c54b9a482629495326b53b08c825e0d19a725f14d12fa7e2efac47bb2cbdc598af68f24e7f338c5d55950b2d47e09b6037e30247083a29038785da56134b57863725531cd427c13af0298dce83e68f17d13a85c2e28fe7d34a05250583b1a4732ca0090be652819091c161205f7edba145eb9f65351a85ebbce227e9aced84dc34de4cab1af36f52a01f287f1b8d58f35a17306270a19c057809e24ff4b78082df40a77de032f987d7827299f616be6249f4e5a3e190cc6aeb85ef605e27b80001a4ec66357ef621ad06649953c201770cdf8ab794ba2d74500a65aad04b9c8d793237185e79aca34c5b8d3399be558c70e4e907e9e70d798d62cb1888b1b0d6aeea4ab6c90c9a2b045221ec0a1df232b6bec70ebd5accf45927311c96e3a463d03039a27c137caff88b36ad06388da213ec1fc0c75c6c646faa14da7fc748dc01be9e6b62236053d79a50181fffe0700fd7d78815c3b99445ae88140febede19210e7dbb3db72b4c55b63cfd78aaefd598595faa58f9e72d00bd9b102bedc15fd31f33fd3a840c19987a91b1142b1f26df9635230eacc243f27b4fce3b1ef62cbcee2b30f3ac216218376d40db08bc35ad5f4a6f5ac0008a9032ed17e78798de71ba21709c63fddc113f14f320799c29e7ba89871e9225ac953bc3457e45470ede7b9fcd70714abc8dc19a4802bd06ab1acd59d69816778d55e7c9f10ed257d28e9c6e9ae0e3d53485a20ef6c2a46d6355a64ccadb040450dfa5f12c3f535c296e08e1183a685b57ada0ff8960a6a0d895e09c511fc79aacbcd03026b47eeb3ab4eb57beac82492303e129c3e168028c112518bb7ebb9fa4269617cb2ab61484914993a79a3f117434dc928047f2f549bc7fd0a3c4e593c7a554d046a94e549cf505fadc3537301f3be458add522bdcf5fdbc9942dcb34c158cb713e2df0ef0db5c7ef5500bff6bb2822c71a88b2c094d1ffc7e130a53552b4b3fab54e8372be661614df20cb7396a788eb565e30979e3c2b318a51257323b0f99ae03d179247f888ab731227111791f95134c2efd1dd44ab7857440fab93bbe07e6ca3e9b0d5468aa1c478ebc60606f5716b6a1bb53c366fe23fcdd205369178acd5dd230abbb0734270ed92436b5e73d0bfab9e956be48f2c31ad986acd2dbc06dea2763ce85e55ca73fb13e3d12fb5e8267cb7450057e4f4a0d1a7e3436ab139ec637c0cfd8d5bf14cc6e43cc61665973524a86121c07ae6611e43a5adbebe35549b42a4488fa0914243d47d13368da811e38483b67ebe5a4ea2f09a23b70dad0dd3693ba771899dce8da192f9b9536b801d3e0391968da8416b762aac180e30add2c730c85e400c27b419a2af339e481522297e3283566d296aa7691c15a87bd8c3a831ef477e13e9ac3d44e62731757b1838ae3b88ea3a5db9eb9c7b38bf16aa75cbe2ae9faede2366a17f9a208b3da4af81b2af3a7ca7b6a8c874d5f0d59e20a26143e911d4ba99925a47c5cf23c72d788bcb756a75653bd55cb0c13cb93e930b2e4ab236318e85d441b52af6793996abeac4e692961f7d335baf155068a2e99625def90b8434b80357e484381a791ed7ebf981d7dd5c202a15bd2536845660dff3a9a1ca6e9b7a2f904d1920cd7954918812ba863411a026440fbae3dcb3459d8a2c44695d23eb3abb56e2dc1935632787b33989b9876c6c3b03e50021740d3ba464d536e85a486004812ad1544ed10c4751ae13344102c44e50695a569ffe6d4083d5b26c2f46d7c5840c27856f45938bb13bd91e193c4f61c1f5ec2ce8e3a322a3b535a992633c3c82b62e5a3b81eee21d620fab80af635b1f85da35d204b07e5b6455d3db7e352f9f2968edce3a0c34fb0ac425da027b98d509d0862ad351763d7c4c9a4432b2cf741e2385bb26e7acb333b093aed231bc695330b188f140dcb89d752b9f9cf11716a076bf19690e8196f0ac4483e775625500edbb88e78a3befb70cbac0634ae3b998aa0b34db39f0c088f2196550552015a405c20cb171bccd981445b21176c24ebbbc32b2931c15a0f84fdeb8a9650f6af563f60b374da42601a6f7c9e1ac6ecb854712ccd5a9e811cdbe2229d8d517ff7e809e758bf7c03a88884bd33f7f1cff3c4e317d612e57c42af0da0deca619ec7a16f49131aaa68d42c4119565da3af3aab8804cf3e81b9c4cd389bf1c95c229a049b696d2f20b348b68d1c41441158a1a866446164e9fb4367dd861f287bd543eb164f57b1e80985bfbb0a75be18ada1ab4079301502bc7fbc086d2691082f4fd94c90c9429c99a703d724ea4287b70d58759a75ea71106b0e9745fbac0ac1d4b8d6b6c06b885413e3165f0fbea378507ff3c470a9acf63d241777f486386cbc66755d170e8a5a80fdc88d26b018aa35739993e7e8992ccb2ccd4edcba28e6cfb6241efe1689dd2f1aae17a4c1f765e5136a2288db67da39672195e702596a0b70b47299741f4e83390e3f342c046f2545766807f7b5d7ec0d867ba5e086ca36067a3e9c48ae593e101c9b517df65a4d2aaa16d3c996a1fcbe7fceb97a1f5bba29715407900f03f4e072335c34be830a347f75b74431496e01bea0f6767c3e7094be5669b514526fe0e55e808ed29f649542d2b641308cbd9b5f899d3fa8557891cc925bce017ebb6ad0c2fea702cc88dbcee394038c6f0b340db763e5fe15587df7d396da30abb4e3261a3fd41a36b17646fedc3ab94e585e989dab3ab2ccf4601e272bd1340e99ea9ed62eeedc719733b3077aecc88a1a77635003e81c1192d817f52afe089fd96df5f61f2c0f4bcd6490acbb9ee362247b8553860b5192d32b9592343c52f51897b45e3184c2986c2281572bc18a50463a2047c234fe0e7182c4f80600dd2c282209d207027be10db7e706e71f17e9c2ea51fec610fe718a5bca17387fed873dd0ecd822921d280487ffce790df1cf8e538c3d2864ca7532db8208f4bddd4a0fa9fd8b38b87c921d762cbb10b490af8262ecc0eb162b9d4218721a71c19618ec7e0392b9cc9ee104ed99474ae02a21b9af976b7b126696d075bc0c253106ad98fb7112844a6f5c91d2dc74cbb81d0225b2ca6baccdb8a21d08b3c500449aff7cf3bc704827acf1ed25ab076a0756d4f435b96c7b3f78793c311739242dff63a0ea974782d6135d49222f2d66f3a801a9f87eddbbef3d4f8bb9d14697587ef19a190562f768ee29b79563f4a7c3b9f906cf54cd1badba02d2ed7fbd5748988759103732fb186d2d1f2107ee1e8a706dc5e82149d7ed370d425a67c3f61686c652616e5eb6d782798ffff47b0a697a02a767655534c1bc1ecb71534b1798dda5c59adcf3a606a08c470d630c48c04ac34f6fca0baee01628d8b52d35257db44e995f3902ed32a84115168399107f2e4ce29733e5e9885f890cf53bab977906c42053d92b91831f42ac0f9562495c29dabe1162d57b2afc61d53cc893522818ac13cb266406b6659c2b8256401656e6cf45e43faa3b08a05dfd0307e6abd2365db2c7b13cce1b62c296d1b839899fcb4ddd31d17929822870756b93efd48f65ce56c0456ed0d376d7ac275282beacdba789253affafc7c162cb205178b92814a447c0a58563eb212e2c57693c25a42d820dbd5bd9df82c38e2e0cfb37823dbb5eb9924a848c83f5e87d53b324855499615300e8bdfc06beeb90bb2357e729f1f5c9c1ed405708ef046d602192ed4c3314b2e882ca19a175bf3a2cf2fb579ca16a4a008147684d7d10cc978abc1a15df0999d00950b92c34fec9340a25bf95e3b82f74b046c20be1ee9cea051055961e997e5c2e314141177a745c328aba7fafd05149b4430135836eca3cf076cbed9c34c3f7a93b6a8e8231266b24368a8b85c694bf7a30a046b74e25955f3615d404585759d215c38ced97abc0eec14251fd0f3541a6124ebe4b0309aad20459f750910cf1fbaaa49b7ab023a7244815e1ae4f5a299123548226d512bf52995aaf7112bc5470142587bad7258cad0ffbd9e5cd720dddfb467a61cac7cdbf0ba8be4be4d5690cc6bf9c2cc7ba65e8ba0ac2bda65aeb969502aa4a1c78e079b1cab3dcac5fad3618953660b40eb0d3487153decebee55dd0fe8f3da986268e7f980ef85395d02b277eae1a079b0ffe9df8b7a3dc7fa208125193c597c5cffb5f6337d58374fc5fe08c0c11bc6d7d114acff3898afe8a8eb47c8855983dbf2c57e0742f3e909cca9221ca770001bea85283cae48429e1466ca9669a4823e5b2842c89a462f6bad5c9b257e8ba1d1adb45382c5ac695d92683f06fe1838ee8da71a35741d3e208e664abccac8f311e8eaae2702f75199eb32efa0ba3d986638fa160096ff52c6aa2eccb10994de7ca31d1b41e75e765d546f0d29ca1596c66e0a59117b2fffa8965517c79dcc1dca80245f37960f606b468de54d8c53a481070ec5a136a228e38daae7fade7a9901044410a0622afd92bd0f75e4186316e3848d975077b9cd2f9445cdd53f8b12d9e9b84a1253f28e335bb5e6b7ca142fc70ebfb9d1a0160d6b740c7f920cb6156ace9a7ddb1eda66d2526151335eb012482b2559b3dca10980349a6a79919a7f0a27b29ac634589715ec9960c9368a5d1e5ad6a54b502b99f81a72483e6db177ab9e6a388a6350b3c9d4dc1a9568f6ab37f0ca9fdff3cebcd75ed1a9d65ac211bb086247334fbbe72d1f2a1ec892446ff72e425f1bf535a9e352d6237dba279d24f2c4cfcc15f18a38073955ad568e208d5c2fbf44f6991d6bebc2314ef3114de5d33c5825c412fe9b8e1ecfdf5e6895522a11fd82c8520579b6699f756cacb3ad361d18f726f57607606f009de77c45a37eac0a5ef501ad875632f44b65696370613cee04283529bc539ff8ab8dc71609c730c682f997548c6b0f878fa5c3a915d4a414fa995b038582c18ac14cbc38eab69a0cd06cc3829fd1955736bea523608fa040ca5f25a1c3b078bd821a093775a22e45f03aa4e3f578d18e9653a78ee96968390f999a04d4d8dfa929f14585c3c59118acbac5731584424fb8b054105c18503ba616bd91acd517c11902cca023e9b339c2745b1441bf5902189d035786038ef7468d2102e1803ee2d0823380214cdb80aefe2d9542231a63182d1a4d1e8cb9aa95c7dfbec0e45fd99e01260ccc7f6a8797f842dc1b1c4fd00adf9c01213731d793ed1ef15cead98ef8ad8600a4b6c22c0b4a6ed630fc3fa6edab6ff1cdb93da7fdb01482529b0927e00dfd849da00a5f0a3af79ca756dc81bf38b55dd992a5c4b195fbad7879170eb6583ed6e6da9567f48b6c29165e094ac78c9314885ca81dce5e83d20ad5abe1748a85a8d77797f425d0c212ecb2d9c4340080c304ee4ec611bc1b4c746c2028b213cfda833a8ab031bbdc86f1573794918cdba8b9b12446de3ab41f432c06b0473964832934b056df907d4d0886174e93207012ba0510a2a720fbc123df3d7973994a2d37d17bbde65a9052c32ea4a9eeca6376f6892efaccb15f2d2f3147e6323012227e06df99c3290224fffdbefddfc0419b35c43f3ff605622c3f1f637d705c6f85735332539ddf944f08899e9f10db362360f1fd81887d9ff1b19883e160d33e8c43303543637dc2ef471838e675033d6422e75718011fad2d2aa95b88ede49e63515e01b4a67dfdfc10a7bdea9fc44c367b32b5bf1373c93f7408590214f8917fafdac853e0ac06caf29c02ddf46e3e9fe6dd42cbdba99f3443b32c03844d7317c46b3736ff1790c121d27f58d02e031464a79d14cb8fa87a0f36cac25e161ae0c86a579ba9dddc25c9f22a894988eb3f082a4db6364d4124b163c427433707bd65f8749a2fe9e48dbb95ec522ee1532647ef4c3d3914245934d1956b65e62c480e138ea381c008cc36ebe541931164619c0423ad3f1b006b7180a670f8ec42a1c5d23adb34f94e8923b6cbdab6e9bc461c1d4e760aa02c9a58ee57060bdd61e69e418db70caaf5d627c8c28b279b0616573d1156edd31aa896783d6b9d8476c399596f6212b2118d9c4252fe9582c1d7eabfa64b63eda4f02831d2c26877654ba5bff1f7221ac21e1978691d5f0ad133cbae796a41c4cfcee54742b8c32b3b3ebf665920ff9c5064256242f9b3123960dfcd6b8b565b05e6fa4c242869cd191ecea62ea130c4022cc75d73722e90214529268a1116da3c388f7a8444f2e38fdf232d6d937a6f927ff639e46822a590bc08334165ed541fe4df286b04e692b3dab68fccca13f343cf2b7194d964600e40c7e01bd482923517bb537f3dca989c551a8677e92a8dffe401a379e020e8cfc5a50f208cb28d2277a3f0b757346c8c5949910935816cff07c5907aa121737b5ec576ba6eb598ca38df1bf9f9ce58e7bba9a326f1f79c371601885cbf5f869ef7ab61de86aab68cb84ac34d8b00719f0b9d79202e34709ccd22c865f825a4592229932b87c68c24e499a99b9be071737a506e80644bda5c286e0eb35d7619a4a482c4f07da3b2775941a499b0b3f5fbd3a203fa0d071d210b358c4f40f33f26ab5ab24db6090e90ce194403aeaecd0c710dd65795a035a4ffd32be81f52b095c2eab2cc1c50ace483e6767ff07df49f26d32563d8694edff2c0dbe650cc0e0d8a78f71854d8b0dc1758155cfdce898d62e813727465086f016468572999e70c8f7599d3a1354c71f3e8daf43b02ade7568ebf03cb076ca99107f81f04cd8748062fa4fe710ad3539493982f23e810a3cd7cb9e0fafb22b939132219f65f8402a77a4dcdf086e2f299026bd5731088b97434779c3a91f063cc9067807b9929ba5c956722dc00482329780a6bc4934025743e02e0ac2b844c3acf44352203a5947804369db5ca4da286d75d68d6f2422be879c3877257725ca3a84f81a73e5a81878164c130a1b53ab3e07a3aa990fa30144de232cb800f910443088a9cf5087ec0425b0832b30765179ff971f412c48acc27764aa2970c71ea67c2ac841c0264e80213a495c07cd7b548cff5f87453f314772d0b64523a4ac346e9a7638a9778531cbd0ecb60ebf7c801ca4a42628954b6bbfb9fe749bf421efef45f71b970627fd0832f00159414c0fa210346f2d3845a5bc917325360827db8e375bae6c56f410c79ab3a7fd70f1a0fba61c1a621bd058c16c7118420a037f5ba8d7260f0c897b19b656565d633d350859f5648c8160c63f2ff1b56819d5bc90febcf4eda5c0e7a2ef10149d7a5baa45afa7f7d423f54220f4342625a48a5878718f04888ba5c4826dbdcd4555e8075537bc473dff94aefec723d89d81838f646611219e01df5ea5515f5530b358e16783424811bcca5e5316dfb61a8f67d84106c745b0b170bbb7d42d0197e39d1a6d12114ef37488a0715d063e84e98fad7e342e2d5b3b0843cc1f456b10a9a2644297791076801cad64081674c08925bb49b324eabfe08f75c12f46308e710d3219f6941204b87322f0d3164ed50d01c8238460597a83a713ddfa14a8bb5daa0e983775317d21c24c17cc8da373c96620ff27c31f57978213bc7edaf03112cfb1fb3997129eb31d5708c2a6b605a7d4b109540c96803eb938dba7cea21c5edb5ca7255aa480ff3ae4d1d188366b91b984cde973022509fd9d301368dadd383439e5dc7eea2bb0e9cbb660c7bfc0224403fff18a8daaba7fed38c06919eea938ed5adc281643325d1de87365f00aff9d50c68036a020b8313f0a67a971cbb6350e14f0a8c06aef9954c8efb6b4be2fb4a13d688109098852e02fc55bbcc01baf052648cae44a93cca4fca29168dfea1d06b9c9344b8a374f7c1058344a47001c9ffc08ed3d0156652d9f2882615ae76d8d1377224a84dfb298c83f2cfb6c3cef1f849519c9ecf5e7e52e62ce9c66f85a9400c63fbd02b3d37e13f2c89809e12741be003afc88f995ba60b5477650f63df60bd30ad03921bb65b4c84582d912426a153b537c7d824cec8e1dfbb33b2cf30ccb31ffd013232d3d53c691cc262cc171a3ae9da97cf5fe677433728acdc3294ea79dd250cea4d9aaf2bd51a0010b586dce393017e292cc0459a835fc652d7cd66abb0500d95e3f04eb309f3ca90dfec8c1119c691fff1a04a384ddcfceb74e3162940920b76e64c80cf4ce4a5b3cd449c839b19c2e0068dd8c46fc506e9a905a4a16c3d21665a0916014aca5468390f8ebcbb375601afe0544b194eb97283aaa499733fed3944241086a1d82001691b75fc051a011e1c98651318b48b8d109d821cef3663627986bc49ddb55b93d60fd720a8759f315ef947b541981c32c403ad430e5b9cc87be358fe514d4d9b6ed211c77e72c71c136412728fb037faddf07e84a03a91bb28feb3c44a96604d781e3b8d100b51b651e029d19fb6b5261307ebcf27bdadeeee7c8fc6658785cc1323aa2a7f5d12d639ad71e91332f80ceb3111d8e701a815ef294ce7e2d247052c99e9b64413e61c87e1b7e96198f8a6244b94b124e7b2228ef653dc35b48e15a148664a360d21278907392cec5b37ab60d703ccc71f2016f33eaab3fac531ff92d840230d2a038b5e15c3e482c42b2465ee1e0542fa0dda7d5f5241cf8536fe98c4de80910eaa0bffae1397a1967e7b00f2afcddcea6023357028ec884ed5e5dc38da4d704f6259f3058d4bc2614739cc6230c9f1467c35c8bb4b6421680020c365f619a407f45cc5bc0197069e6991aac28e3f1589bb59fc99ca1b5aca2284b08e441eb576f2b708b64d414b2223e46f6fb55225005395bb1c8ef94079a7df2d26437051e149f6544bd10a66585604ef294c9bf9a783b17117ffc3a05ddd7073b629bda6a582270f3ed12e3ca40a9a83cd1d8148aef840e8fde04a4f2942b5900b7c79dd941f72be5dddea884c8421293cf08e6527400099672e1288e1ab957df1ba3090fe9aebeda5b48ef6bac2a39fbca30b5baa3691d4ec7661bcd2bdf5ec4f2d7cc3018e9e7da8f9571aa2501f9d3d5678091ae413e81bc9368f1193fa95ee437cd27447d944094cbd034a04639f5a304cca9a0e7589ed157bf66477b1ff7608fca9fdfbff9b9b3b641b094267b4eaa879bbbe76e5fe3b015a3336b8f3383f78bf2ab4a968fc14f467b68e18009de8475ceb0a13775033867d5ac9e00a1c4d5a6f5ad2f69652ca9452b009ff09ad095eb8a6db7857f58d46d363fa463fc134fa3b11e9f6771a6f2818488a1dd0eba24361a01fecb7e7fa36c95879d371110431afe5b190ebf9bf96d76a7d656fd916b7ba6a3df72a22e7f8661fc8cb2f2f8bcc402dca7903099ea45a6bad319c9925d11b7ab228d140b9602559a94af294a5861dda3ca1823d7102aa82c902ca87294fbaf8e1066773434f1625fa54cd98ca2689c6d0a40b13830c459e89145190512203021688f0c2e48a270f6aa9dadac31319ea931b5ab0b8169a685f3248d3b2da7452da462ebdd1dff1a79452002061e5652d38a4db6f474f0bee86647a59b8e61d679836600c2ef3f08e993bd2f87570154d0a799e770156f23cb6e23a6b711d851f5b85978b599ed8595428782a5c7724fe3c65b26432b71b8070250ed368f7cc73698f10e4ada265209c0cc442f66c09f6e59d1439ce10eded5aed97ec58a3e1c4d2c8c1810e1a3b3d3e1198d4c795cf0a885151974f5e3c88b9590766f6251ffbb0eddd521c6ebf7d7d1222190c84c4ca6019d3ca72c752daa42802f57dfdaa61336d4e2a72370b4f3e661bdddf5a2b8714b8f2fb8bb96c96534a798f6af795efa4cd6d9fbf688773fab794d2369b062510490c2d24619e2419a1460b12aa27ac16992254f8d61a37355902b01801451528a84070c50a2c2c9cd43047c0b086504a29e5374f6e562062840e55508e65864ea1c2e3bc10224bee8e5fab51fc54a5544b96268cf820c92029cb113b6a94c8c2e58ef6a7c097f7dff368fdabf55feb83c8f79ee9eb61cfe3fb9818c4f5dfc3c4204e41c07f3ddb2ae879dffa59f74734f9336bb63694a306866c035fa2c758d36511efc8fb22f0bda7542c7a7deb0556af138b5a2f4523ffd61bc9f73e5777420326282dedc1d7acfb23db8e5927ee402261040d9ad896e779b226c2082f9cec2281af50be8b6afa2443fbce18305f7ed17c5934df138d48903ff4a7c8f2a74a833276bef4dc9334917a35d83673cfeb231b0f31484bb4df75eb7974deb7bee547363adf45084708e97be883e52a11e80a5fff0f03cd9e6be5fb17c97759c522f99e68f47246e5f3f0ef5c9cd978487146c51dadf73a845d96423590d65961e54f4a933ff3bdb09b73d22ecda17817ec8c490342ad9cc639a409dd751fb61ec4c2c73ef20776598706652cdb8c9c8a5c2edfe57bdf2c72bd271a59978b46523462514eeb123d7fb67963ed4efaae679b0ce57bcfc3df25067105b1cd04ccdf0104a9087786ad188bdf54d11c1c047105f9bec500efe543c0e554e412adfce9209ea844993eaf7eeb8beab7bebfd5bd271a79dfbd518bbd8732eb56e87d51cbf3de251abd510ed9f6a1d8514693b2497fd2bcccd77dcff53e88fff7dcef7a1ec2513fc793f0c3856c5071509f6bad5855378765cd1df9ea7aaba3b48a85ead67fa13bb251f9f38be4cff9522c7a19c97f8947362a8e3fe6a5dffd9ce6b4a6893e4eab5fff93e0d0044ad8d1e7d66ffde873d9fffb22ff4ff4c4a27e17787dfa369da2e0512ace18505f3e8fee5d0ce2dfbd14833815f9771f447efdbc56fdee6794b65cf5bb96ab25a97cb16e6fc640954f9fc7fc2a0611833815d59fe2cc16a47b29ce5adcf179dd34f9131e712ff4c1f6f31d6934ff0efdc9c73e9efc99fd4736125adc815482064e6cce69b2d6dc8124dfd6ddb5c4bec964322677cf75e00456260c8ac359ece6f67f356883fb61b1e0e5af6003d806eb6b89c81f0f2ad8facfbb76d878745be0c7be88be053bf96c03c3991465b656947d2c55ee68afec6ab70377c04422f5c1270212fd9275fd170bd9666d38d47d0d3656e7777e26ffa8fb991477207993fce9de99e48fd2d0ed5e03275609e7bb9cefbe8ff8d304dfb1ad7b9a31a03ef822d087e9fc4e0c49e902490a7db0f2f90279cd5eafef81746fd47ddf3ca8bbdd779307517121184fdd3752f7d475dd94dbf591dbfdeb7beadea978902b7dafee9d18a8fb9d1088fce95ea7fb5619eb504eebfe25fa93d3baeec5275676bf430ae588b3ef63247890fcee7548211c71f6893c40fab00f521f7c1eb08f7d4c0c421ff66ceb5e042964bfebbc8060c915c0652f3454dd0e08fbbdcf3748f4896f47be828560d81a796ebf7c2f949d2bcd1725564daa246c7e58ec8e9c5f65ea459b78cdc91d193765b2891d6560083e4ef31acfe52e3f20dd1935d9c48e3469e5abff4db6e1dd4343b50e5e5696071848135684115a6ca39fc19dfb00fea93fd20ec0dba2080c543f47093b7653d61bb621654c6c0b5944807471eb7718b6c1ced46a6e8de25632b70e89b9d5b3b2da4d4b4c52a889d6818586ba69484b3714c4d8ba4ddf3def251ab9bef546df473ba1c8f5df090d609b953ff54d6800dbbc6f00dbe67fa291914cfed43fb2c91f69b24619cbb6d6b36dfee8cdfc8f6cdd14ce5cdc81d438f953bfa15a8326d6f5f903a9ef79d666ddfa1a38b1557efd7e924250d599a450ebeb7b9314aa9f58df25d67770075238df15ca5752656a75538c27ab81be5e3ff4c176cf77eca66eaadfb5833b54af56ef755d68821df7a1b973d87ec3c73edf87c5c23e2c96079c31e08b604871662b3afa22850f30d8ba975fd4bd148bea7bfd23db2d72d96ab0b1ddd7df81d489550d0e25d95116fa60e9f31d87d8fc00a5852cacb842c35ebab9964fbe9295d16e4bffef0322979ce6f5482e8d4777be6d59f3ac1c7b35b28698a24b1a234b7a58c346bc61b2a68d99124c29520596272d1e2693c89552ca1987bbc1063254a4c8a0851a2c2c34998207a90c0e862c5ba496f0ab2b5c58f71cde04eba362ce39e79c73ce2b5cb430320409a41470b0cd3477ce39e79c5fceda862e1560acc8a003939929407089818a2b4c7460340095a58817485071e5031a299d264652b850a384eba1c817567ee5ca085a4c388922aa34fd80431a2f8e988200586438024a0b16397421c50f3e504a29a594524aa1585862045a72534c0461a3515c4a29a594524a7718c87fecb9fe3d6c835d72210127a2f0d084e588ac2dce5c5121a74991264fcc60254a114c707028de804e1c0cab01a3d3c4b160198105154cbcc062c604ac2d9e3c59a214849b27babc98f8110f63de2031c40b356099f29c95282a5ff28d99c27577e22c2bd084952d597c81c4cb5499194baec0f04407295c38391182167bd381e86047cb36baeb36b7c99e76dda54c227b5a7727ca3a83da878f4c27c1cc2cce21b3bae7dfb1879ed37cd049a778c2bc3e68d72d95c99852ea31f3147fd0eb2fbd88adcf5eadf2877af2a7abacb2ca8e79dacbf3798a9465e8e38e3b578eb992622945963ffd1e333f03b5ccaeb1fce3ebdaf80a37bc44ffb8c1652019069779f8877f8a2f83feb43b0ce442dc7d0acbfc1ecdc65a5ecbcbf2644e29a7b5ded7f758a8eb2c9e353c6be49c9d0b3feae59fdf2985dd009e73ce89355a2cdb3fca50b2fd9ed378ced7654aeb0b92a964fa94a7052b2995799776b13801dff33bdf231a39ed8845e1fb88463d1fbe518c17ffc866413863c0051fe3828f312bb2e07bde82ef7919e18cbecf1fd93efc97f145ff329ebe0c19ef231acde81fd964bcd18c8a2714f93c06df00b621c5780c4423230c1e03d1473cb27dcfb3ade747598fc8b618e205e1d10c7370feebd705e1914d0cd916866cfb906d3242b659101ed9186cbd53fda8b7257ad50a9647f430a33f4979d0d4070b9de67f3aaf0e5d5716b2edeb20e779fe6370e771be43fb3ce128de1d91e773489c30271c7d884e8b85f07d7ff0bd8f219e7fe1c8253b8ff339e17874731e0724caa1f43de0707c9e709c97e777c2b1efced3b0861536d49138bf131e5d1d71004d03e76738d6b8389f43e684e0e384ae8785e38c1b7b1b8eaf1b13619f43c2c2fa603895427f721a94d3e4c34022973897785fbdf9e5ca3975a5c089ebc18e8ee39c8e30899daf249bf9a79b6f87732e71c7c578f92ee5423df4e53b94074d318718e29c923f522a9c54f207e7342a6a74772d58bee3c44d9c377138a93495a6d2954e257f80b03f451c8e891b1af2260a12e5e0c08d4dc0e4dcac6f119594586105218c501652104e80bb630dcb1ee9788c283cb4765ecbf5bd4058cce2f4effc8e38e67c8bb30f76bebfc551e777de493af9f3849e6f30e7efe88438a1b58285af304aabdfebafcd3e13c76791efabeeeeeef4ba4f2969f2a7264a29a54bd9b49aacd297a27bd3d04567f70ef61da197ced9ad3a67ab9bd22a25129a90e70c0116e28030420abcbe0cdc7e497fb69c68269a2128173e5f57cb342af2ec98465ff67c815becff4cc1f6bc4c26853eb0e67ac19e6d36146301b874000bb832a7c509dcaf7330609b81a890d9c47b4911dc623f5a69cdf39a44cb3a5969ad34031fecce652d50b840ebc1e6bc01c01bdb61411bc27a6aac99322e18f9e23f1c8687913101a83273c41d2d2dcb0cee8e5ffbe1e3037c9cc3ce7f2a9b0a4ead31235bccdcb6dd563a2a4f539a62f676bbfda84f4d5198a0c0708e48dd8003e788940d51a02cb5129813c58b121f43bdd44a9dd4482f9daa23a0a8d1498dd437201f8f14313bd478a48891d2b7ac36fc3e5c14d8d086871914d8d026046daeac466859e5c614793e63a8f8757850ed3c2a291cd5c74b121c6ef776766f3b291c5dcd832a0d8018294bf89005408c143143b5193e9300307c9962070402c0f0458a199271b47bdbc94f4eca62a488d9e1766f67f7b6eb6a53ef19a45e776f6d37e7f5f0a86b9da68f7db22e8da56d9b27853c2b73a197e7341b7f87b2bbbbbbbb650f0ab6866c9320d10efa9ff77ab9d0ab461bdb59f7b9b13c293d2576ac5df953865eada291101b156fd5c9f45c36bda1e9796fbd9a1372a6dbed96c456eb53857aa2893b565c95fa1feb541d13e5e54d1e99e18e526949ce8f7249e74719e5098a0cd51d254e4aedfc28a7787e9461aebfcc492ba43b4a2cd966821f6516053f4e24a52527aedc7132cd28b21f67138c1f2795eb3f71e39c7222eb8e936a86a9e0c75935fb719eb9fe136b66e59ac0b92345ba1ee329136d9272474aa95cff9142dd1fa9d4f5a754b4eaca1d698e9eb9fea61e79a2883b56a5bae4ba6749284d5f64957c73640a8f73ccb4a24bd40aa557d76d236d731dcc94ebd31d0c133ef5a58601c3d379bcc40d2b9705d21554a67021e608054d535c3db504973b36d51d3bccf5aeea31d7cf4061024a0c848dbe347a13099cd8b9a353b9ee6ee5ba4b5d7f1e9dcaddcb1d3de767ae3f7390bdff3240a2ef8f80673968e4d1555421c2e72ff831079f0b442136a71e3ec4fff7219d942871bbe27ff8168044e1874cc59521e3638044322cf81e90c882d78db17363fc8cf19e991be36d0eeac628c3c28df133902886ec08a5dbf31580443db323eeec618044b30a5e06125500c3092617c6530012c190c97e029048e67329c85d0a9e0724a26082097e07249ac0f29009c1e5791d908867c7b6f33920d18ecef398aaabf33820918e276fce5b9028872685867098c04102078704575c1c0c2e0ece5b2717a704555c9c31572ecec740229c21da0fe530c25a31d61ac0e2b8638d6b9f77b8b600d7dab74c9cb9f66555415cfb9fe4daa73581c4b55f23810dd73e0c24b243ae1b7b10248ac196007361ff02896042e07f2011f8aabae3ce7d8577ecb92fa0fb7abdfd725f4b44b92f3134dcd7bb40a2d77fef72595d97eb3d90c8555b60baadef40a2d6f5c254b99e19cf76168009e256f96092eebcb9dba25b90c30fd5a65ad65e4d8937b6bead696ee5b9b5d65a2bce6b47833bf630a18416258a48820a1f3276f4e6eb460d246a88a105092c544b77fed3a8c0dcf9b5249ceefc184834876aad3a2e14e941892a374954f02d7aa0c0823164a6ee38e34b0e253022052398b418f1e68826ee7c1848346bad74bbdd54a0c2050c324dbc6a2c819bcabaf3655ec4dcf94f4408ee7c5a8d0821eefca122a898829a536d9ca25089c2218b93275c8b2c3cd82f83a924b7be8bca13b0560008d0c5aa4b955bdf135d40a2c9480d527ed022eb4a198f893beef4383144d4ad5f65530529983441040b524800e5a6cb08902845820b9b5b7f7a7fc4a44d15140d48a84052c536b3e8d283102f0baeb0a34cc6369ccb9958106768b398d99c71a700ee9c5235dc2995c59d5c906cc982021d3c18920b5f0a3930890bae214a2fa6262da4a7ef07275e0a2e235ee850a86a458d515289e0bca8354be810ac6ecb902693470a750b0c2f48f6a6c7a30813e52d4d30f01882dba225061e448e6c19f38407529519a8cfc1a2659dd75a31e8d245959f2bb82ea6a46c8ccbb82e8ec840073bbb8ceb220d00907851e40d086a489aea1839cfb16f31096e7b9e4be9628fcbbcad3c07d5583efd503220acec89359a459250e188cd68e785b89b9bcd29f66ddb098500e0c8cde624e5b374cfe8c8858923900b7b0fc7791ba55190204284e0ae7f8e7b61a10907f8e28bac5166dd9ccff910bc98788209fdc585b12d278cbd5b715ae3a0947a1022013cc536e86d98d75f6e5aa8609edc51d4223814b19c7f82ddd35cd88fbe060ad49b9cd0b1c00e625c173dc74057566718c8a75873fb5d7a3206732c35b718cc83c2cd46b080d3ac744bddfe5c4bf9f270d655fe04d368878281fa5b0ffb58ce83d03f9c56c28f9d43a32302c9f27ba38f348d2bb6e1a9e11bb06ffeeee839b631bbfd7e866dc8b7dfef51f08dd8f7fb146c23e763b19b63a517f6d591b173476e57e574ce2879100bf510b7bebaa2e2b97ed3d2c9f11c15da9de1343f83c51dfdeaf663d52072793be8cb9de3a07339e618dbb9b66a346c038b6db4d886ebfb3b8b6d7cdfdf6fd8c6ebfbfde6485af08d9ed234fa463f358dcef9b1dddcfec6826fe4bcce8fdd05dfd0f96ef2254953b8b86cd886f7bf5ad7437965d867ae988172574daca3601a7cf93be92a754e071ba3ba7df908a5db37a96f3495297da3ab9a46f78d1ed3b2a6536e3ff912032931902f5172fb1d89c770fb1d06a6d1af139e00fb1c49a56f3813a6d1ef4e6e3f0e95db6fc39ea2a3601afdb0b0d5308d7e306c2ca75d856dc540cd40fdadb0730cd41f463dd891b360583916a8aec8dd907076ec1bece948d2153bf6111eaa27dc0eadd44b5cb0632bc16e4f550cc19406ca12942476ec250a8ce45098d24c1de5891d9bc93e15914295a62939d8b19b5e4c8ec3228527a6272a4eecd84fb72e5c18e9e2e607282b33d8b1a126c09acaa2089b348d6ba99c1c1555509921d253fd85891d7baa02aa21724cc034558769c18e4da581522e4d19aa9c9baa3141d8b1ab5e4f5db6c0ea92c552e7fa8c153b76ce454595a686a099424a6275f5821ddbeaaabb4db539c3e436e50688a936423456b779b26363d1dc1553b92bca7456bf01811d3b6b3e2d2d21c40dc9c98e7ecb80ea48921f89620a8c4b1451a2b8442186cb992ca52558ece84a4f309eb8c0d0c40506325c9450e1b2e4c51214253bfa12ceed892c24a6284aece84c2da521d24861d5e44e831d5d09b7c50d6bb29ea8f8d304538986a7efc6a40a282b382911d8d1713bb928235f0c29e253fe658a1d7d6a4c054a5262c24889b9024b4a892b29313938958711c28e4e0584a3f2059557f918afaa54b919d8e0925879cecf006147cf71172afa06046ee5576e055261ad5003c814b502ceb1bc0d0f7674ac3617e4864073c5d4c2a73484153c0c8126cab3fc4d0d76f4ac2532aa2e5a246175e982885c973650ba643d7143a2624779a34f4833507961c591a4263bca233838a9b42489ebb66407a5255176944a4762514837798b2262064b0b17555143965882e2831de5d21230b70549d29314a62d48ae6c51ca3145b9c18e92a975cb59110595155c344d81623f6ff965eaf64b2a0f9ae0f6cb2c59180f629c1a9c3c63e5a3c4926fa6740975fb5b090aa766c9f5ef97563c68e6a27c725a3fcf95f2692a197971b5aa8c81fc9bb3f5c29b5b94239ae4a465a85879b57a6d6a509222a5345de1aca865802f4a3cd95ae2914d4a59ef63205bc50ebe1448d0643372bd908ea266cae624ffc8f6b54257c82091d7d55951eb67452d57382b3ab2e590267487d41582ecb68701d9ed6f2155b7fb1a8edd74ab27a56ced2ef4414122da6275797786de6dc9cec12e1fb3003e9642d236a5b777f05506f244727a908b3d7ec82bc48fdc35f0f5f821af8b33297ee0cf01797bf077df78a2f365f28786423a2b21b52648a86c4eb3fea3dbe2cc028d1442e34d53968d7d838f4e1ab29a2b2c4b36dad5f7c49b90fad5c8a9fbfa4642e8cfefaa2864be73d9a0ff6397019070b619b2ed897ddedddd3d0f62a64dc76eda83d28e763de83b9deeeed5ddeb8feeeeb5dbdddd6b37adb46b57698fdad5aed2b047f557ed78ba5e7ab35dca1e3de99c41d26ff4ed1eece8dde9cd3b6ba55cc1c06030180c06832989599c9c17ceeb735e39e0386f4c84812f58ef4cd17df7bb9444b97f9dda703d832074627f77fb9c507ff495df0ce57fde4f7faff6140c21f43c1cf57eca64d68e5eec5d9c9e373de8aff733304862bbefd75b80840bb08dee2995219359ae79907d9ca73fe441f43f1bbe9c063e4e689d063e831f607071c4e934f073b8f5faee2c4fe81b0b4fe80b0bc7d605c597d3acd36efdc4f979a2e7c9efeebb47cb1934a5777b3f767777777beeb3d66e6f47a1e594b8fbf1abc3d677e151ec82633803c17bdcd6bbf75edf0aa86cdf762f1cbd1edefb4f30047f31508ebed19752cfeb72f878c4583b4b782fb95852e537db70792f5d9fc33de48077fd7d4e087ff4edff5e14a42cf4f2a752faccccf45be2d8ba14e7524f1c615cda458a2fb782cb5da4e0e17eee7d47df7bf672ec6875debf14caf90a03ff25be2803ebf7f55ba0582ba0a28e1d2dd11347d98591b3d369948ab30edcbfe6bece6377fed49f320dc77eefe70c9a72fa8d13fab2e7795ef764af2b90c1f673b7cbe512e56d47a1a707fb150463f9c71d7da36f6dbd8be3bc1ce8eb89b30fe4175fb03b56cb6d12174873ce39e79c5ab6d8c2ca953f63c8c7837a9aaefcd7e8197dec3352a52b9fbfbf7850470025b6fb640d17727d5f1084f92ed1fb1ed6a0025ce2d823a3ff8130c348f90a7388bd2bf6333441c8ed76f6d2fa1efc75fd9c52a34b9cb99e8a2f97e89910bbde832fa9c658c936b7a4a5284f56a6c2e4ae661645f22047727ac9a90a8220deefbdbe1fb712ff97962ddadcd77f200852ca693ba29c0af9b6402b85fdc04ff080afeb3d68bf2d90081499698cdffbbf9e411082dcefa7260882df4f94524ee340dfeebdb0ef1667ae7f072a20769981bc10e4944c8281644b34a1e594d32437a5b0f9359461c620774e5152814ad851e2a86efb3355f29c2633b3bff4319fd2e94d3e376faeebe75f5da0cb5adc58b99fbf725caed073c8913c612e51feb844ef1369e174212427ef918abceb8bd06c4eafa7dff77db1b0ef9c733a61c71ebedf2be4eb66ca35c0652d592c5ddc652f97b56425dd9d1e0f8dedfe73c2ca6f70becbe572b93e27ece77a97cbe5daf120970873c55c2e97cbe582c580b03d4ef3d6cb157af7f57a851cf0ae0b043f19c85fac000c247362b1e9e5844e385370fea3af1fb621f6e070c99eb0d8b740d87f1ee8c1843d831dc4fe85a3033bf060de97f862a059072fa7bdc41eb18379633171c86931256ceb7b7a767660b158ec5fb1900023b0b1afd669fd31716708471c3d02046169072fd08a218c7d77c2eef6dc0edc715ae3709a1c62200240d94f1c77ae164f974b70594b9698fbb95e5ec89f1376945d097ecb55410d33f687bc2e79bf594c3c0184f9c1f7531c5dff89de77f70b47ef8b8923901b73b99e9dc6379675edc73e87bb4ce8ae15c7be31317489a06520091359bccc409d43ce5bb6f1bd74fd50d390eff964ff999f681968fe50dfc0611af3a5ce7d39cd479f11e7ce2b5f3db7e70fdba8f7831f06ea8fb90e13669ecccccc93999927f374f38db5878164723e9db3328737586571e48e3b35c6da8ed3e7015f134c9897190887813c7e859681b2eed853c1072c6bc1c2ea71d846d8d7f320b681e463af49e1103dae1d43b519bad25fe08338682fb3d5602199ac7910ecfbb99b89ed86751048170693fd912de719023c3fc11751f032d1c8a973c471e882df6211cf4f70646b71ac5d1004c57e863a73c10745761a4ccce1b19c3f22b23018ec3de070fc11c679753e271cfbe63c2c0473421c9d30f63938e2009a46ecc170ac71639f43dae9b41a56d8873d188e5c55e3824d60e875e883abaeace5b1abbe17569f41b33b12c7bd2b7dc4e290695d66c22acc6526aca42e1017bccc84150cb73bf0b1cf38b9fb56add5937df7934da92696bdebce0f5609c3ef41f6c5b2d0c3f88ffc0e3f9c170e48543f4cad46cee34c1c1c1c9c9fa109325e5447fe13159ec1a23f99d00e25f54bec3895c239c36e9eb7d3dab73c9fc33bb43f799e41229ecfe155f729c0c1365cc8a9e48f95424426f87f22305e06052f3e910a3e94fd054f64f616e4b8209cd1bfe0637c18ce76c470b623ce18207ecf1789013a5970d04982c5883539580069795d03a1b33618207ce5e5ba216e799db367bd4e66ab4e0224104d53c63ccd97d5837c24b7bc4ec7649a2c6786b1afee641b55555521034d1a65ecdc9de241f25fb46f592daf6ba6dab7acac2c20f2a78d1dffceffff7faf8a40fbaffaaaafd2d18940fbaaaffaaa87c23e791015176a1f206118ef2f9e959595e5b4fe2e060f1f5fe0052b6ca6d8e4961cf12d2ee042ae25edeb738d08a49c4fbb70a4728a3d66276957b1c7a44dbb6743f1e4e30bb880c4e6089b26595417545703de352176fb93bc7c7c811a97a358f8d242d3e5a8a42fb788cb5149b7fb3eb42f36ad6efda82bb26e157ff8dd80ec8e406eb7dbfd21bbfd6065a6cdaf5efd49c51ed7ebc6fa7356b1077d797bd4dfd9a9228e2345a46d486773d2c0bcfd7ec3dd6eee21d9d1ab4924a8e4ecd9fde524e9a0ed124bf0dff153af43fee8e4ecd84c5f241353333593753925ede7fcdc71e24d5a86d61c34b5eb81a5843cffe9762967cff939efe998f9d19ef7d211823733f1d43d5826f7a1c5c4403558a6afa1953f4dd5a3c4ec266e1eda9f81b837171b05a621bf134b903fb2c5212530501776ec269ab73463a96fcc97efd1a858fbda6b799d7b1e9691b1ba894f5337e91b93a984eeaef94f0fe508c5a6d15f4397459cb9ea2a69951ba16fd06a7fb214ea3c76b968dcf2baae25d6e48f7ccf7a9d4ca4c91f1d9c1dbba99b9a264e0998cee96a9da41c1e4c24aefc190e154046bb4193d17288d8916941e3ebe6c0b96c2c175a2707cf6b87d2491e340a09b18d66a0a6c91c363de5ec9e3b4ca64fcf0e5fdd4973da1c3a0077a131d0147f6402b8d2935db1e3506d2866c3c7e3bcfde1153bf298dbcccece8cc3c809cb357a54b02c8e3db7ad1447a32b9f8f26f3d16c1ad51ca5c29b2815982e6d1ba54c9f7ed75adbf5dc6ac960f00f7d119cd632cc62e97fb114ecd838cfb356267b991403d13fe3340f8d8dd1c48eb599fc7ae79dc9e725a2d8e2f212516471c7ce758e568a8bb1a455633e076c2376e988c325ca1888fe055d58cf72e541b866c25fd84cf887829d856d34e8f4c2e152974b1c5b864b41d90a3d90a8d5b53a113665297d0ba0b0f5ebe5a823315c7939eac8d2a5f5e7bf6a388d3e1dc01256c7e5c0698dd5420363ce29ddddc5277777779fb25f3bd7ffebe934af041edb609e724e1c9df8441bca5db5acd166481bee58c39d597ef9d8a756ab31820f1c595f86a8aafacd15bed16a9886cc72c75a96cb625fc1a8902de6d0c6835cf4b1e2683ef619fdd603a8358dda320b856dbd477f874cfe8823b05feb86be7f3d0fd7776290ee5dff1283381575ef1267fe3571e6570c54bffa19db594eab5f3f0463a5486b2c97c8a28cdb1d964315bd5a9349211db52a73d4fc26abbecadf1c634ba0973e093e74882085be2394f21f61f9c187bdeb3f4f6cbd80c81fea33f931ab99c46ab562e24bd4d1a2af1cf2879e2063a06efdeb257d5848935d38434ffecc8ac07f3d28b2fc697f7deb5f624b647feec0ed102b4b9039a4d0f7f475009142afa7fc632dacc91ffa3487d653f6a4508d7f78aa255af9c37e3b246ef669320b8ad8d90e24efbbc7b97d5da3cc2fff8de5cbcabef043990cf6fbd7fbcf9d1d2cdfef5fe26cc7f7b306b0ed13dbf6fa198fb6bdbe6d2ff1e58f8c81eac7b6589907dfb7dee8b50309c9f52fa4d67fe26cfec7b8d93462f9e3fad6cf6fb5fe138d5aa251ad32185aacf572d487affe67a590b4d51ffd368d2a59c643cfb2ca0069eb442b9fb0f4bfdb1bcd910d699e68b24203153a8c913514b1446e49c9932f5524c9a4a8335777e7725411365a2e3ffd915d0be36e0ffee9ce74ce661c53dc01986760fe5cccdc62af19a873af9479824c93eedea07706c6175632b851410635b43332e490666882f1c577b2d25aa3ae9248b232bb237380124c0188ba228204575277b4566677c4cb512c48b16f03922dc858d855065b2c5826032d1654a2089b7339aa0a2e071184a91285829cac65b437510c9171396a851ab620411249282811c3840b2637de9021b5d65a398082439aabdb11596b240d4542c026290d45e342eb7294162c53522debbcd25a690650589ccb5157b8d09454f941b2f672d4151c9eb8f2441645a4086965aa0c915a6bad33cc550d449e5a628604c1c290145ddc91712c1525451452e48cd4bce63f4e4fac60c4c2de6470439b0c6ef6438341d513187861e9e5a8dc981f2ad6bb1c950bf3349b0c5551556e789f28235f5c7a390a8ad51dbfd62447dfa33e6ad77a33dcf16b90e18e4338b424943b32d55208eec863ae3c73c44b142fa4a2aa9031442d3de9d45c5145d2940a498419e248ca1b23382c490551440b36f93aa4900796a678818d1a1e8c28d964eeda1ddae5282757dc9ccb514e7277b44f83068c90220d0f52acf04413f904eadbc242adb5562b48da90a0852e7e6823612002ea85c90313921e28a53074435c86247838628635e4b9bc4f9496a66b2f476959bae3d73a2f7ecc60f065c99f389a21c907334850b88105304b6868152d545beb981699289076d6b82e471589e2c2b81c55e48b10b59ad76ad349e9a44961e5e5a8225eee68a558bf1c55a4cb944e8660353de09c308249912f2cc8265145b87407966680c10b93149535b65a8d2a92e58e5fab8146abd59418695fab81a10245052a0c946432c40a0a2570814c0a6b9a904b34704314370481c20878b805e15794c8d8e18b2263b07893c3937f2899e48eb21741d21434ec60868724ff503cd29ccd1a2b68b24082839632b51a184fa3064a22e112e4b0830b6c567022428bff20388cfb5b28773327542a31c0a188353621ae289bd3f48cdc0808bcb06213a2010b36a749a9bf6ad83c30b9a264933571c6a37e97664c931b6c426057b6fa9d68d44e048182954d488e0bb62ad27c83fe23db375f7456548d3c230aacac10c2260414c25637e8d0680055aa18926513e2512bc860abf906fd95f61824b26461a37da553d4154bf787bc19cc78d059d39f4f43ebdede3e1b0528eedeee2e9f711fdb867090f083032a574a29a594b286d736f76e8b3ed3e50d1ffbcc781835154fb6fa4e10e89ebe37c666f48db159f913847d79b98b104977ac5d2995dc18f4c18eb22b7f2e40bb1c756b73a36e672e47ddc2005d8ebae1703f64a11ad45208bac45045893636f90d82d04d10c672d44d863bdfdd8bf8e4532674b87df2d541635df1847e138e6ef747dfe64aafb790759b1d69b1eb1f631b5dc17510013c639c2fbf7f8ade112ebfdab5d2ee64d470afd49bb8e8b47d8bcfbf63a07ec97dfb7b6ebf8e22179281a5c50b2b35866ef317e93c76fdafdee9d806ffcdc969934f5934b998b2d8c273045600545d5c58b20605ef65e50e76b4ddc213160d48724aacc87a7ab09e10b164c8fb2cb1e3d36684c08eb44ea6596a951953ab21c58eb5dbbca209d7550b433898ec3834a9a8b49082d3224b0e0e60b023e3a67094bc48c17991e28497262b2f529ae84840163bf2540c8b6a4892253b8a6cb0235329cd2b8e7c57ec204204b6d891ab3e2c2cb0a4a061040b70ce958b62418a112324f880b2235b794ab92ad250596151c28f2a7664ac1615152b50210390a114a884ccf695412c95b2810000001000a315002020100a87842291480ee4d1b87c14800b759852785295caa34912e428884106194000218400008c31c42034662400841b56c86f71fa51417e37e4a71efd4006fa43a135d3dd9925766406335d98e1f3e42b414e52679ac03f9b8fadb2dee55c91f30e1778182846c5412f0ffa798402a4f097292e3f54c25f9bf50c5a21bfbc262d9f838ccf13512bf4298100996c1df0280bc341994c870ab3476634a005339c3ac35044545f0142aaafcb7d202390cf0e4619ad6342aee4896ff35f326500502460c97e021613232e30b7204cbe25e8d5798ee4187c6478ed99443a591507d17ee3f4044db28af9c22619afd3a4239f17ec93c277f28adcd5b12617c01685ea8c298b4fa3d65432d88bc90533c25157b51de23d6e2949123f0c450d880224a9cbfed72488d92710e8b226ed4321e6827097a4e04bc62e2e0a1d64c3da301fbb474a3afd9bc9cdbfe5fe9b20f199d9f0a907e4e9c9b7781d3914c58569fa7036dea1014008d1ca956e1b0e3c39d5e9635d5bedb8c92cc67d04a3ffd72f4696a91e01014c7d70bb6622a11365cd44828e603513073a02a899d4b7a23e85e5a2bda008ca259ce31112c100d47009363ef61b191bc7c2f60784631d254fd276a92219cdcb8de3125580c78bf433350ff14ba7523a0f1c12370fb631cd1d121e115a9be519f7dd5c922f02fa60dc1de55cb7e4790180facc5afa2332de183dce8bee354b21b45c62d69236ef2117f24b4405fee26f65e12f22c25bca9ece60c8e7d7dee37f2c11ec111aec2cd05792923b459d8e705ea15603cdf780e8559bc0a233c572a11c44f6df3d4f933733e9aca49e091339e428d9152f693fdc3274887c1ed38afac03b8162d70c32bcd8c4cd123276b829764c4c55610929aa9823c6b90c8ce21238edeb5e85faea737c4667331cfe8021ce1b669c6a789678861770b2a3962896746e7d7c7e501f18f50377e6323d27fadba284e56d9c46440ddd99b80e0fb7f5790c79193e6991c8db54f059fb01842936962dba14f3a73b0b4f62b1ec615d9d9c4d6d16f8b58f5f86305e517f4a1e0f6f61257baaffdd9b5ce6c01a6f85ea0dc58fa0ea13706ffa74c43727a1352e52f47229761169b983a3e23a562d7721af8002cbb03f866f0426f1e1801f62f99bb2933ba87670e11faf1e3b5903e40da79c3c87b1f4f819f9e01842de0b853f88576255d8a9ed0af895fd66348f2a07b3a5088497b9551c71de3680ae0322c1aa22931084f63b6492df2705fa031976583f01e568ece8af6c4d79ce5f5c59859e18135340d925f33139bc3a3731fb57177ebc4469508db24e289f37635f6008889323ab619f542c1f4b14be1d67b08fba2b576748c32e764558742cc1c40d583fd07b256ce20f28c3484c020155d8c8e2adb1110e8c9e3a4df7c2b33e58a9a7e91b64ea71afa9de5c7a1477e44288d3969f0dc465e5303d132924e1a10225790e101df415554b9efb7343201ce30b3b7ce9b45930a0b5ea74e296274da48b24628f4c94f004e06a5c089d88ff510c7d32d020ce5749f9dd54b6144708df323539aca15de33859670536ada3ee520a7b26f779c21fd5ae9b08c9298dc99eaebf440c4ff7e789ae30a8d221a2818c72bc07627527953e2e6e5037b9e2df569de38a95a5297d9c4db126b4c17b7054d2185549476bc3a402612353abe2e2d53945376a5cbd34cea40eb305f028f655648a4db4fefda2b4b67bf9d85739d13963ec899175688d8a2d0c33cc15831e10040f02fd3d316878e79a528b78a0c6435da6c25ce340d11c9fbc71456e2794791c94c214d9052118ed20add31cade11088cf3a407de4bbe8102d7d28132a9bec53c4881bd99f47398332c0cd517ecf456fcc1179316f452c7e6c3249c4a0165eba2ba422eae611a5465b3e8a3faf6b35cc5c1a216b649e4bdb8afd3077940ac2635f7eb166dc10edb075d5e1735021fdffd40b51fd28ac28cd742b756b4db53133ec632cb56f5942347191cd7e6302f14fcaa6645482478598055d683c69c1a592c3f6ca1666aeba3d043e40afd031d11c79084d629c932679ad7f8e465826f199954919508baa410c204cca2a60446307875713040b242b6a223352e6452fc5b9374f90ddc0684fc98add717c36c9fa0111575e43c2514e535fab39d6392017714691276deab6621e547308212a62ac848a8d78dae616383eeb0f4326fc1ef4b460d6060eea79b96318c8ea79513259e56c95160e62cec5ac56e93331a81a1639eb4b8a6a687f4c1db40cd5850b11a45db9b946fbfd284085c10500c37e26354723ce43e00d335e227acacae44a6b3539f363cd35fcc772d8726ba3f44ba304f0c2f9722b894eecc9c2da2496672c8770a2ca56c63764ca5738923b8b37d5c42a0251e6b66d6c7d9abe487c1e7231dabedab3215adff85e1fc7f6513aed6eb14bcb70c19aaf24ad82d413e364b531ea42f54cc7858d969f07116be9bb2e0300c32915da84f61a6b1298c5472abd8e95228a1c660a62563268545b54ecd57ae16fc10aeb2c9eb4855e941ec079276aeb134b1c76d03d8f5245b8379f22e82b500939b4bc17639f547948145660c96af5ce8a391dc34fcd46d3fa2b3ea23c6745f799f3c4d42e8952de67a925d8f1189692d24d0f8049f239026bda8b68ce7d62a5c0ccf09ca400f349a7631529ee4e26e7332fc4ca9f623c486b32b20edd172fccb7354c6d032b3b9ecf5a5cb960c5cfc7c12ec65bbbed92983280356d989cbb4ebecd6569020aea7e20352b3bb2fb69aee8a6a51df0d067ce3790361571e73820820437ea77f28791374a8904907a70ad84b30595491009b21887301019d8cde5688095ca3b56317d39894ddf196e1128c5629707ee3949ccd528ef7c4e7a0754c231dbac62e0577390cbd9311f60ec3cb8e96c3d4f60a1b4019771bdde4bd4a7e4b780071829878b16304288c69aebc0d34444f6ad1a07dc9f2a3083f214f54b53511246c4134be6b180219d9fda0be16a1c4d1ca6671df8f1d72fc22fa607704f52c9c810cbce163c7e8bc8843a770b96c958ae5387cb6df7f36b210fbb3459225f06b83e6c7bc8756f9403ba56ccc47c59043d04d3b750a516e60f56928e3860504956004554f6b7a0affb84a0b536980414e880133edd9fbea08010fd5c0918a1ead00e6810c51030e0d6e17c14d3ef862857e0dd2c503b7db37c234497773a5483ec6d7d9fb7beb43b7cf25b7ac86ef370a2c42ce03b60848e6f18930b6f9fe19961a6450ac39d9a55de5cb6943fe226ca981a68d77a989aa17ab483c4dd957bd2ca80f6e5f9f61c0f5a5f5967b02d7f6161d0e58a7a2176bc64f8bbb0ff17797b7a1796d40b092b78d907b9970055e4537e2a6e3713772e07a58a7a326cd600c2add81aba8dc9f1466474ee7e126fe0e021300c97162ca537083d4c88bae6313e61c46bd7ea07e456504707cd1165880b636213703c068645cf205d855307ced1c81043a691c2351e6e571d93375ecebf127be3a3cb2c2094cb8a66d47dfdf26071603385f8661f65d099ddd7c22648f6c5e861de69184ae19ca63ee7a1d68607f56aceb9e369b12e7b1ed37261d9dc1361910e633806d77a8ea7a067d6cc101f39a2503ef46247f505726e242c3d2103140f401986032a030bfdb7ce5a6b1591dbf6381ea31e88dc47b03634fb1be460c0d9d7d91d7965e67c9e70a5c833de5d52cf12f64e94ce265efcac479f97809ff9ba13cdb4d7094635a1a63fa04eba1b1b1d3b71b3f2c2b2716cb92b0aa7922872de90d4be1bcf06ea21cda6653e7f2df5f3aa0b33ebead26ab7618e26cea3040d8b83406460f79390839cf83dcb63d26280333194036c26b5716aa10542667907dbfe5d3f1197b4aa0c4e85596d297f8527defa46280de22946203a49a2b48b363850ff260061072a49e42a7aec7b10d5b4e672716cfee4ad125bfc9741eaeffb3380c34cf02faf2be702901256a5ed80e4f684b87ffd87d962c46bcf7bc11b667d728da01e4f5f17adafc0e7e61baf43e3636b695266a2433559bcf5d7698fc913c59b518aeb4b6c66e8e8c37d30ef931212733329cdbfea178dd2804411f2b6591fffd5144a787f4b3cdb8cfd083257d81bdc4edac8fe5c2844f8228b6cc45da57d72b8fb0f62f30e42bda5dd50fa2e834090f509caf7e35bc5a4d43ba829916776256218d8d43d1094fc580796356c45ef880bdd476bbe9e933150892ef8a81b897fa46de126617d52c23c9d9d0aad823cee0e4f2bbbc0758b2ab0ce0d4d343a89d4779083544521a3b80480319c6f7d9587e08064381416a74e492478bcb0830a90598b90a2e3c115ded0db6fb04a438da79ca254c54bd77558ce2612800a7db2cfe7917076b8df4cfa1cf1faf20a668dde5d128fdca12374ec36fcbb1f77f7f1c9ee2bf12a744b6b53137edcaa9b776ea94f34c1e4cbcdf2bf02092e0a691e2883f16c852b119eb012640e7cb697426b133703df66e6615fc8753cecf57e28ac8ae88a6bf800b5da25343b89b6f868393bf0c79ce2719fb825b9568569be30bc3d5806aaab6a19f2d056857d275d563211d0c11d40d06f65d7a260cfcbafea4a6bcfd422c60ec284abb50a98465537bb4a7c83322aa591b6429b3a6698913153b799a0bf09cbefc7714b60b7ac6d3e20f984bec3620f53ae44153540daa67034fc85b09aff1f29cc015478da5559651122ba8501a67ea0ec02db71dd865bb49b381374fb84a23e44644badb7f17a6c4f606f6befe6b6f89a0510a10677bbb4d7e9e165010e3646b67beb85eb102a628ba121075705e536eaa2d6fe55bf0fbcc4250cf007ea711cfe59e5b6720850484d2890d591e5d12de986e75afceb10620b96f6ac0e27c8ca34fe4ca17b1a0852b392455912f45817f39cf02a968331031c823ed8e022c0d06789c61eda6832e9c192ce590b5ddc885d74fb570828880086f5ba29124dc73c9e1f18f8cb83a1eecc8d51ffbb0bbfbf4e671ea104004c4a97b1aaa346a87e02fe9373371ee006d5a4d4012c166c0e3f40a72c77a96bdde65caa49d3f084aed6f65c532abf408ae08cf3b513491a7e09140d7355c89410f16444cd855626314a3bdd0af929772252f17672207cc8239ddc75418d3aeb0e598d4263cd3eb0c4122b866b042d4fdd6cd32b200b0ab1e8fe08f5ca4dbc302807c2cdd494b91dea769564b1aa9d4d83529d1d786f471db47f9dbf812c3843e433611dcd4b07391db4d6e4db7ecff3cfc0c1ade2f451be34cf7d00d86a482614d1b015dc99437ab043756346f361466ffe977d8c6a4bedf4a37414cf7f8814a98a06a4d11ef139081432eb85e856f033ec62addfa2f91c683f52f6bc17108662b6cf4a899fcbc4e840b858fe3127d9912e4ba84352f88c8d34b4338473dc94c206deef759957c90157d712a0e534a485a8ea755d2acd8ff22b5ec754c21487e5d96551e275b10e20338bb0fc243cdf6511a04d66dbcd9a0c59b1657c9a924c78be832b1242a65c02f041293079ab2c598aeb600211b909fbe694ceb1a91faac3f8037dc370d8af630e4637d8644b517309f9074aba1c8edbbfb857cc617e972f10c672ecac8da1ccbc7c680d4d8e38dc6f83865446736e90b4b6540e1145a7e7d4f201833df2c22317721320a43f922a629b197460b21bac26470e3012cdbc5d384c5bef5209805648a41a4c5126e3c7129dbd1d48718bb316f71012f504fe1687cbffa8358f0667e528e578a0463ab8839ba62bfd36e8216d8d9fdf301723527c20f87cfa612652e6f657fc92d57f98359e733d481e8f1cb9ad14cdf7f725920572c678782d9271c8733aa1629a9c9dea4012e5b0e8117f1bfd81c74324a28d92661c9376196bb0ee04e3883889fd99a18a4131d189d9a0ca03e73d0d6c167d53c7f58db13aca35a323c572473bd03de31c1d69aa4337b8a794cf5ea962fd49c035137dc600bdeb23f5fbb080f30e7bdb3c5de8a24954aa534d0ea27fc20270eede74fc4d270781c39119a859af32ccd19c9ef0875032448bc93808db1c4304694485f53b4dc507b52879a027adf3476269a8434272bd12220505682fbe978702073925255462b3a5cb14d90742cc29592266dcbbf28dd6c3571358bc6428fa28993e333189c60b9821cc89f813ca58b0c6a3da74fcf0387f888f1943a1e3306ee4854577b25500dd28a84ac5fd1026f76933435ece7f38bccb0ef16c744284abf84f3a24b9f03d81a3c09449f6e2e7f684ae73749e08bf4992cfc9d7e4e95f3045ee15746c3aae5ff62b10cd9fb705b9eabaad56ebf5e1d2a4f975dc27965097c833016d59eda716530ad9e29e58e2f2ba171399cc3303e5ae3907584bf5346e6b119f0b0a9fb2f5a617bdefade95f4a36e2861a4ccc047b3a3f655b4ff9f65ed139f7c1308f32a22c6f5b56c0d38054d024fb26ad4b7c6220ffd7912ccfab37b59590f1a3490c53afa5870b8463d7e6251a9448d575e4327039a2416bb65d25cfd6ee136d595e6bf28e96b60a58590a805f166d4d0c8ea09eb431a81be04dcb6a5475dea07f0593afbc7dd138a9ff99aac9778bf440d3e8473fe34a663d0c018faeb09bdd5c1d283ac464cefbebc942b49ee6f4bccf3c2e59248cd7bd3c5c0d93c9b68e34c70403a73e2d839b36ad4b424adcd7eca3a4b34b2e4aa294c847bf977559c58eee92299ace0fabd21d7439c6659d7c74ddc78e9d0710354e45f45f6fe396a247be17fb7067eeb344b000787dca4e003d133fd045d75c97e176320d3264fa91324411d3d6080cb99eb2332a26f204cee4d7ce2d8d399618481dca8d8bb577a183f1ecf971c9b94ee228dce1384ee7c76a9715861055cb09a933ab602b2920d41091eb5c14ff15809430c901aaffcd70ecc051e87fa6d62f9a97e48e4a60347f96d0bce2e8dec8ec3ecafd291b9a2dfdcb1219650d1bcd0b4685a4b6302c3351a5f030a799172a6464d3032889fbe1777d41f42a5959442551cff5242c8247a03750457c416f0662b6041352d1435855348f81566769593b1c8eca46e86ae44ffa4eb90a2af3789d3ab3c893b54778742271342e390c1c686d59a22586fb3ac42439011697dfa5127a83f1412d36e3dcc7697087d07b6fa472b9abfa79535fde6adc820c5f0e65d1cdaec158fcb0fceee5641ba6ea5623b95410ae1367bd55a265c78805db58af2fc062916dff637a9ccd0afce96c349867ae41c7a1686d68f056219aacc81a8c291930f9f4602a9e861aa0af51710e05c594434d56445f23cdee64b8c5d43eafe2e6904e97a8fe59b36c8ad1e7e8574ba29ee1478a180d0bbffe8e5db0eac4b47f9f78d91285fa3414aa7293409848037188c0750cd2a349948b7c32795d0e48b4566f7255249f48d56fb2d1785ec3e4934a3365cb35af90b02ec768685ba199072041c4b3d6fc7f099495ae99da95255adabc5fc6b3ea26ee056a445950003afdc03891bd0964ae84e6f6361b98999515378da4037c2b2dba5d5f91da039afa378fc7a382be0079792ae9fcbb5e2920e4514c155eb025bd08bd142b5bc67fc3772fa6feb355d02c50f969b281e43804804b5b01703c9fc386888d4f3bc808c61412de13a3b70e66518e56ec8de304b2901d789effa31cdc8d12331663a1a9c61f62c0584c99f5ddd782158e3df32f5509d191e06bf97defdf704688d80fb48b68b42b0e009005d8361b758ad1fbcbf655c4b2a965449a359738c91ea7f13a528a83880022c893f51020941815c8f6996c36fdf537c9ae3e11272e9334a57a911f8a563c47089a4f33c6b05bc95c002db0e040089ba31503222b6b54525837bdc6966df58548e790a38941b7b28df87bc6c7c9c66860a15913999769cc125ef58f83203833625062ec5675aafb700b489366786c14d621ca9edb5297f590ca43f71a2c93211cd7e7338dc1dd8ebb1015c5d12424470c46a0dd7a8f2862171319cb482092da1a9866e56b5f20afc1cdfa0247d783368164409d05f95807e1f7fdc143ead9269e916c125dac91d2cc0b267fcb641e4c08a28067b4508b77d106fd4849642696f820982df7344ed9910a2004ccd8704c2cbf10917ad74c242e5808f9e39122299a3ff4dc66f3ef957666564a3225cbec265086bd05351c3ecbff9c41219ab8cf3ef1ac58501442b083c09a8d43b26b8d1aa886ccb046d04e152f471b77432816139a18ff6f20edbde91351ce7c4f4be25a6109812ca62dd66eb558ad28fd6decc03263fc7ce143f60af7575d2048213b5011b78ca3e2c78c64dd6e38291b46b34ae6abc1f188057d17296bbbecd5b08c0395120211f3e9e2d588d34975f8c6cdaa3929ef58fd1f393b085bfb0deb11b433c449961c48195ff4011a493492fba7d4e54e94a3c38d1521280116ba5ff19b627d858898c403b4dcb0d1c80e39fb9acdb1b836d6a7605e93281cb1f64746e6913a8910e27abc301b4ddf69a77038cacf1f36a4ca51706aadebe9e0313585adf901482c0de6b14154724ff40308a4076d024ade8b088441d7fe1d17b15bc8bed23921b6101b05f9436b382ebea409e6be863b340cfb103608a87da97775e8cd056c688353112942790ed56ab9da46322977ba0c426af2046bf4025cfa04ad8809dd04d9321f7087b135cadbaf5126dc5c1182421496f8b50d230f5d9a103f0fa1a2d42b4407f876a92b416be125ac60f0c9c10ead93145134cd1cd8b968768bd42af51da46073c0a19dbb405cc30976c1f3874324bbd425e846303b8c8c01542d6863233a122c1144910a8b2396ad987306a4a2dc8273a40d741b60625150922d0167738bac7e4f6b6ca52ab1cd0ada5e55d5d5752d9019f707206290d49b31c3018af200419258098cd159141c34d1fd803ad74a382a4fd32c57d11a932a0c6d9561fe176513e0cdbb61cee338fb54769c20084255b3ded5093fe0783796b18b85d5d93e650f5e39c53e317bc0e881a75c542a36653e517bc0525f5370c68487183b6e3c4f7e9e9ef258a0d67e09474c70c475e761cbd19bc62cb9714c79e9cc13e1d884e4c226f133e181068456373ae0cdae6b2d141dc72b477b0d864a57f5b5d3f32b6bec7e8d262d8a694a030ea07071418da035f40d4bed45df3c18aaefb09817a51e667e81e97d077849033beb4ecf4be80b504de9baba66bca16141bc89bdeab8bd66af424b266e6fb79d09ce85775615d43a99ddef8ad2f6813a7b0f8603ab8f6e6ff9b3064fef8b1843c8915d4616921e58c93593760469b8dad6017e6b289acf31b8b2ca59570ae8bbb622a1538cf7bcd7e41bf59101d56634d39064217d21d63ec8cce3a1b746ef54fcd124607fb34893792122e5cde06d875e2a7330c20383d2af3b07dc37a3389d1ec6197ad2e0586a46d700cd656a339bdc00792a796e3a07131a01a9f47d872938a326074abd889f81f8fad0f2634c1b8c6eac8af67ff9fc9ad0c1a862748e04fad1c8454e6cee6fe83804a0f16f68a6b4737aed674b5caf706e8ac8ef421e6e7f434e799bdfe8426c359764492900d6a7f1742e3a32a0277731c82430cb7bf2c4fde836c50632ee7cffdec82c03e336a1042d3307239ced5b886bc13a23e0a7bfef22c1721d97214e8c50fceb2b4189a583610eda9ad21a0a60866a165a799184b520baace846a7e79a6ac1dc74cbc53c67f30ebfb622a9586a3714e9592da159a5debe8ef376708fd5dec1b17e83d5ebbdb41814935a69ac4c5194d13927bf7fc501a5f5fbd929251f9ec33800c5632a1236f844a75b49106adad7254e06e48a1b13ae4cc0f09463d6ff283e68cd7a01c52720e0c22c9140447295798afb48c93c242df820ff06d70928fc6959be3a16cac9854b572de7f7006af27ec864b576f3b4222d91ed2aab11e66ae912c1040e0a1cf1cdefec6eaa263bbb85ae81c724145f093520014597200a9710d9127f0b3e6601928453910e17a85f1700038486bd8c132556b894455130a96cab26441c0bc9510a2977e87ff002991331f5aa8d4700632fb2f11ddaef0203ad8c14e2cc2cd7dc0163e03582a78a78074d103cc4790f0325056dc2f044b37a80ea8397ebb9581c5d22123415506f9a081e9424dd3413000460cb144a8f093f619971faf6d98a3bded5c8150e7413166232ed59b2a4f099696c62f47a84c39930d26ba981b41aa55d0469519d6a4acb8269aa5471e45c94652ef611a88460bb31c7bb2596fcf65929e2ca36297de3ff4e6027e4d503135649d8fdd91669d324ed39a1061a3439a53a5a60a326dfeb45eec36f832018b5b444f0bf1524b45cdbcf6133bd96a4dafa1c9efca189f8e1260cb2246d8f02c4842406161ad13ed4a55a87b294eb4789fd175437eed5b028727ffe7439b1c3b4962407c30ae26991404edc855c4a01c5b54010584e81680ce4808f1ba314b00ccfba5f19c02ca4a4e14357bfc41f98a3377238f98de8844d1c3ca022591707774f4352b2a1c5d3e856ac9f5660bcb587e8c3580d6c76fe9c396e06ac5e8040ccaa46037549f8d900d9c29c40f96a1863c86aeb7c375f5d3c484fb534902702304fef264025ab229fe65e509871800b224b20ccea288fe7a38787a3b7442f300101b79ce690583384cadcb2546d468d34318fb3280a853cb6d60bd4f33d2143dfd03c062772ca6c9ba4317a3b61dc658da3e35c91a2fc6e2b1552579c52359a109e27667e7294cffddd04a0937808253b7f84dd977f9a1e00b88a219ea7dc50d329fdf0d9d0ca0c23882d1aad09353bb1ff4a53f3250949c67d1da8d420556b4d399de51a0c1dc77b64dfd5c669e6d84efc158fec2313fe629cacd1733c4a5f8c71a77218f2a56950376c64d7302c4b56c31fd3d44959d6afad7bdd39dbdb4d514855c480f23d106dd50844209b0a546723a4c4c82b2f1d71dc3460338f8d59c139c842d5b22de4709c1317182084200a338990fd59f7ce54cd90991d2128454a0823be45aa93e14446f90d808f06ce74c76f3ed2ed03b0dbf5cb80fce8936eee86dd636dba53a6c66c51b567c1262da575c4e3f6bae55ed01830892898fa9578f4596ef081fc05ad20c6c58fdd0dcf9f316b42eff73de75ae1737f10f5f9aa6898b7c8838197a4cffce9ddb44465c92bb6e77a1f79a172ded3a893f17beec8431da52cf0ecc0f78ae3d6ecd3da29eedf8769669d3e9bc7e6e0e59a160110e4e82af2894800721638c65f60fbe3e80b6d89fc99b52cbfb50292f3c7f513bb221f42234d64197a56260bad1f06df318236cea18bb5700149cb01473e335d7cd6ec47d4ae8c902077106287d67db85809837bb821064c9ae0ff9552405a68929e054cb5af4d845b6d342184b55aa25e7efd55e379ceaa9f8672817762842a1958162306ff629d0bd6d6c7e991d61c00343e2b400244c5f483a163a9a76ef38b8e8750e0b07d4be5716ec971e0eb409db861747134338c8989bd4325f3d7b5c999cf7ff595f841959225f04d5d170522941e4242a972e12f5abc280ccb6738669007b59871ce7597f9596b003b41c233b50f35c950793c3a625898fdada168fd0d18c81db19f51313161545845d8db20b339eb7acc5cfe4119b746d671b1e80c3fcb29a0c86b22088fa77ac035a07eec5bbbd364c0d7c4258c3591cc102844fa863353590206604838b78558ecb075a4cb6738bd2e4c203ef9a6990b8077c627f472d0bee8c2140ef111468d8afa022d9d76117ca87b7f562bd0892f16b574a924ddbee141166977844943c09b3e65004a5bf0c34ccc6c60b70d774f6758b860de80cd08d5dae3d0951c5bda3eb9f3ead78e19b26e2b16cd280641c4a320c051ce540433997b337f5f2110be182a954eee183ef8a93ac4d9f250531e5a921c79e78bbde82d4995da489170a73f9e4db0df0c203655f0e52f41305e35365908131867fabcbe1a54d0198c1d001d11af417a6e0a7e75a7fceb8645563e33fe0ea2aaa4c4d84c7b03c7f05ec37e2bb1e011bca3f17df3076dc4ad2d2d242c42ca857cdaed821a0a95438cb5018b43e305a8d188b890adb3504180c7536a90daa81ec34130865db10c82f9501b67a8f50a0c02fa63b8a8ada4967fcd9d7096f7dce32d854e0e25339b217382d835c555f6d0d840f72d0b1a8f8078f55d7afa537ebccdc6a75981a74f60f7f11b600408a7e417ed66b889f940fdd779c7bd194af212853687378f56a0b70fc89b47aebc32dfaff70e392f74382fd14a06f4e43a74479bd371bbf86a09ecbb77bd6acd63e2673d392f61330dedc85c4531316016c08a1c3ebe72060b7a0df1615da352da4b849123a5f0390e3536b2b020899d99ad0c2ef0d9f963e0d203764cdd806f92e8485ad4537bac7756ed400946897d756a569b00687daf7f519e73b75161f0cddcf79cd4ab672e45a6d8e97637a3b2ed1e520a983782752ec266d22ab3b75479ef5a9ef74f5c2ffefa4523dea50a3ff507dadbdf1bcede271233116c70a9493fb8943b903b84b2b340b63109b327c12914cf9f200eb131b0e0df5f7982482c37bf784881c7d8c294221c8b989cf0dfbd0367f07202207ce08d0590e9507db901a2def4478df167fc7801bebeb7633ccbbe3aea84377b533d6d00c90cf9ffa7e20d8809bcdad167cf47ad4ea2790177825eeea966c020f83dccf087dcc27cd05c1bfe448419ca557c94d7513dd0228139b7ea58561c1ba303064f3b5b90580feab4ac661677cb373f9d52d8044c40e120b5b4459c9f795aac1ed50be1c1d18a72703a42ab317bf108bd2c701a955618b240e2fc14c22221085061c28cbcc83c491780115367c83057491b516cb15c1fefb132bdf617eb942fea8cc97526fc2ba21cd3266d82412876b9a0f83578e7faa3e631c16f8749329eaa03d7cd9718b44d2c8429539cfa8eed9ef2d634692c048645e5f2c641539c83563e8514f758bfd2850d7bfe15024421875917ae698cdc1108304ecd83539766e0b22e820edd45121b336072e67acebcb2e1a9beadb84f06248e4f72b0a00288ba59f67ccca91cd94b56cc6c1e8ea77b385bf2c7ce0aaf026c12fd2d7428a5e482a0fdb2362ec17be85fe971759ffd054919a45cdffa348ab0e5e812030159cc03b19d78b2d57be5c076a2d09f54a91f91a49ba38deefc794483c836ea63ed4252b1312f2617c9948a867d6d7c9f95ab61fd6af15f95d66c265bf9a98fbd75c3e73aa30e0af0f8b1e035b13a9d0bb613e8de0556ae00be92606bc95a90ab20462a7cd6b5b9b5fb252926704257f87f4e3887eccb60620efbe4d17cb84c9424c342f989162ecf423588cf36aad32cc19b0b0eb1b1ace3a87092bd070b0464216d203aab6ddef2b2758374d2f10094b4152c7ba5da4995e89bd4765a7adbaa87d5762846d79d52e6621c10e203ba175e7edf5ede13e761c2cfaa5cc17e5cb64618f69853c091ef9fbbabd8b57c9e2d72a14e23d3ebbb67010d8f472c9805423df1e68dcd526e21891d5b0f235e7a59aab86e75f882fcfd28fe8e2aebdd6d2b19d8b9b19a2807349686eb1606dea25cf97b2e587d203232137924fca1db45dd2be7de55b6a760dbb01e5b2661b3381994eb61a24ccbda60bd322646a7ac4114ab145a1c3473ea36a6628a5fed87a616fc3ea760ca41579e138b62731e66143167f791c88a67820b56292353c9f9f318db9a142be455127972a298055e93315f9e548c6518e1cae6fa1406b6ac8f136f4b0a217d2de576672ea170824b1dcea9dd37431707a0bf6a12d411f163bb30d90b5d972394d7b6c32e87b7b5b7b88f6c88e265c2324565f92fcb17008f6c5f9e99323271165a0aa6a81494490d7b935981dc43c5296da0ad648d16551654ee2b7a98bbe0f8d0a5841d2aaad430aa8cda85a2ddab1be7d2049f241c3e5baf1ec8d341b36f8eb49c1ee56083c6d28f71cd6f4653cbd98507081e795a066e226c0fb4d2c6e1fd7fb7e177bb0d390319b4e0948c720ba8b1ae58c6280247b777a7324eb1d57c9fe7330ae227f9e4a49350e44726936775937367fb46b624b6f69ce3a06bf0e7f9480d29061d72c495e0d7b4c9d66289d944857fc37925368e3c34d03429e1e5273bc27918535a15e7336c140ac8124e9c2e9b561880179812cd08e33ce09ea7ac52197b34c9da3aad9435be0d6aa53357fa1303870de46ddb4358694ca249e4632b25d56f4b458f9b0dcf5c91764bf8a94524c589c3756bc0b0af4f2badd46e6ee0ac4e6156723f6f6746312a50495dfe4d5a1f1fc85412b61363b5b1bf5f6d29e1e2c6946868852701d5bb057df911b71f550481dc191d8901a445c12b698ade5583ae67a0f226d1247d3fa6dce4a78a0cfe98d9b4b0581b47b88f36c34abb8e7737964df85e012e77640ca78265a13f4d3428329c1e53f6e152cf4715f8b129145142156d8dfff644233ff2d175d79f87bd37e1948fae2f574b1d274b0ec13bde05ef8db54f1982cc795a40119ee01099662614e7201635d490e7243caae301d912c0b703d8dbba7a78d299d1fa70293202bf7cd10a7f8bbb3bd231bc8a97b0b0060817e13e747687db9876febade1be570d631772395cb676fab1fce95322ec350d9672423da22ea02b930f3bfa90bda31dfd614047feed67c1e4cf11c4ca91fc2f6b74f5383521e5c1cec9e3d81a8e5d948b5400c87f9a0fac49508eff3e1f95911d5dd13f3f1df388466a3af4b1c397446e11f2630b55b061d9b76f2ff7b71885a8ad424ad8f4da9f5b901f5707b438c2246983018866826cf7eade1b921ec3251f5a6283d37699964998d56472bd1a08fe463da4a91ed118af90bf3a7ff0d49fc554f5aa79e38836f6b19ba777cf37f8241aa725dee6d7fc0f1dbd3e059c6b69b75dc280217426f1f3a251dda3fe726e24a0bd10bda449bd55df63cb9bc0ce77f0a32aef85c78d69978e9de58392a631dd21c0dea658065d1a5c2597763c846ac512e0cd160e9448d1d7cda8a5198cd8a825434d4a0170cc266aaef5a78c7d030ae80f136dc41692473b059d04b1e972943d86086ea019511d615e21272599c051e54e458b5873eb3450437a251dd7fcaa5f6b549f1cd2dd1c82dcb680be32a1af7f38f20067e7a249edaf75ff4699cf5d25b52ccf8c35945cedb620f0b9236b267e804141012da3b543abe6bcde1f1e363621ae94da82f61cda86eeb1b2c0e4523035499201ed5ec1933f73104ebaf827eb8bd1fbaa09f829a7206e8369619a47be9ef37f6563e31271c9cb44bd580747ec37cb50f32629a6233c07239cd8ca0b091cb24cce0bebaba8362701d7570b8d13a960d6ba7316ccb9d12737436617e8cac9921a7e15604cb692da81213980b4742882c2cf0d38cfe221b8deae1d01bb26e4ec56486e02cda2b172a7944cc79857a8744533ecb0e5d8d59a67be46712c48ba5d2fa34fac8fc8b3b12f3bcf5a06844ae2c44462761d752a8de2cb38b0018e868581ebacd22690392810704661b5b3536c8281fcb7e1ca7f216892a1b09abe4260a96cff14138920f514a3e86139bcd09875be55d9b45e8a674980170c2731f53f221f7937c9474a400d35900bc75a41ff4d50beef98cd403e4e7b30a89041d6a107bb376dccbb081368e5d5e64828a2e302ba0b5123c048249c60751ae5c747716ec4bbaecfc1c687ee3c34bae01e5ca8c22e1d791f12a72be28d16c4e6efb85c162741b27f2c9d5c7cf704734892da45aa0cf00062c390c5275fb52c7a2f900c1029afdcedd88cdde606925011a85c42d6f3c70af95c399ca9a7322b5cc23e58543fec4ee52a87532d33106f8a4d0167e9b7db2f16e016a0c86ba70fe6c03a712897083ead0d3b31175c331db5963c30408370c08b11e7f457ea44954a54a6f1c15ed771eb2a0fb1335a42967fd05809a1f6c3c3376a6fc80657a9842eda066c91f9fd9043b889f822f83c11fb28e83fcaf2864e5785d0ddbbf60da73b86539feaada1b551eccde7df9deb238a963666179416234587ffa89eb4e19b2a992ae6d68a07e18a672a958df1c69c1f872a9e2c9b2cea80b223aa626b3b23d81ce4a598d2d7c374cc978fff163eac42c84a6c0cf88ca6c279579ded7d6f621ab72e180f5b0279c7b72dbadf5e04ac30d0764834e83d5f15b1e50a9046a94c6fb653af7ef215a08428809b8a58032e78dad5ab058cafe3124c000144920d85333c62dac934dd83c7060d7dacd811dc81ad13275a020e202f8f9b8632577249dc1a8a2dc25ffb727fdcfdb7f9af9ac6ec8a6f096260f7af709b689a22dd2f187ebb73df5e439d2f9361a14b98c48f9eca0c5259abd3722dbb3bbf98b83ca4f440b6552b1837d7aef7c607aee0a78e18a0ff430591b78f62cd99fa314bf7396733b2e7d946b5ebc34d0d86158bc6175d5cdc99560970f34e1aeff5650cf47e4237cca0b53a981862d0e8931b0ef3d8500496e04dbebbbf15ccae0da299dc0fddc597027e4eaafef71bee2d6d74829fe1dbf3d2d7836115a80cc5aace84838ab19e5714dfb010b8c79b0008c6f8d350d3c8687dcb2e48ec269d8ccfaa9d773e7207e1dd28a3857144a4d52057cfaac8bfd41a34321509e9b52c16358b068917d5832c6654647c4e2d305072df04c35b2178bd08b8c29e91a417128ae5ebb95df1a24a06d1f1b1eab3b5b1ddf1818d613909ce3234242c2aa8cc5e163009bd4403e03732846ceadcc36fa935d62209bad635a69c4e13a71f4bed4f81a9d514aecc664cbfc638763add6f8b99ec84a280cf5e9683e07b20b11f1cb0fede3eb5ea69af42e40ec973ab65fe40f5bf283aeb72556df84c4808cb28231132532d2dca7f78a99c857e7c67ef2b1357b28b994988d90a8640d8ed9083d315e71d5162c7ce8d08d4660ba990109ab991ac399b951ce4c46b3fe7873a39021e7e12b04e653a305ba793a54003c46769dee3197f2ad465a298cbc42f3d7a1f480efe98d0253cd95342c4cd22049cbf0b0c267a104c8b33f961e2af95026fbda6d204736b18b49700f8e87a21d096acb73dc1a7ff29f49032c5c2e7a45594740fd4800a368f7e08758f2450a803a717f5ec012549c04bac9163b8a99ab94cd5941671781c0f8fcfafde5279e14be8e5360e18d26e85740bd89085e1df9bc3189fdd34c6e9538f47d061a2c1fdf2529f4b2494a5f0f6de1e40b3156dff9b59f4299844a507cbb36f7bfda9c35454f47b3690986958f46c80710edd2d116c5e0e4faf7c5a68682439bb925a164969048d9e86f2474aba183af13f6dbee1e839e9c36e0a6aa342c86722b6e171280e1ed4e58531ac8129cc0beaa041af085c63c777a5a66e21c2b58b18379948c3f5e5fb3051517b889348deadb315425536fe5dde70fecbfe37a5e3660efe3c87e8ece9576674bb93db3d0b47f3a771cbd8e7fff13887b7bac15735d7c6d9c6ac15dd22a400f3deccc24949ae9219c9ca1e8fc88fd798cfe5ecc8d259a84fd442be39514c7d0b7fd08ef112fb70df9f231362776ed9a766409b8b809b043c681159bcfb72331de75cdd6bebd4c453fd704bf7dd295499067191511415a4536897bb943a6f5f5397ed9a8ab85ed1cff8782a3e889769e5306737b81249517ee2a105479b54f45be9d580f83d85c8039d9b34ad6312d86c6bc48c30a3dc1c3297d855d49ced13e2f353f9e41f252b6a83e72b6afd2a5e7cf54068ee99f2191935166c7d7135db821b3fb4b5b598bb0413b1a30d8bb731e19f57b969083ee6bf340b5547dc0fc121de1d079ac11336e0cc865d22986d3b95826fa6de54a45e9d09f63f2154a6da26dc785d33b36e6004414424fd95dca6682601fc1d61417713aa6a2e0ad11c03e6cdebe24ef83ac1191f5c77f43135db0e3f7fe515b185873d6f6ceebaf125fef4500e4754569303240d40e6d5c7308cb1d27efe375c5a6d613abbd2b196cb04becd63fd8e1612bdb291848e6089a5258991254c0f4c6f4737f27c3e6fe218f59b7e3fdb5423066cd2eed6fa539b4d0018ae4ad65ef9034d7b1592556e7d5aa8051bbfb112236bc6869385e19ced1e2fc2c4db4625d439cca0a309146071687ec3f7cfc94e3aa282c84f080aaab2e4462f841537eaa96378bb74e2c2c7e24f38a3f720aa5f37b4b34bc5d1b2e38ae1fc869fc8b558a35cb6e509670a24a4fd9d024ff6b2d2ce730e0f0359495fcd98a2199ca42b35613db27865dafbcc75c719767c48708733e65b53d052ea0922b1546b33d76cd9b1b0b11bb1b79aed07a20e8c4a99f0a37132c6b86903f556db9130b354698e3303e50c73aabeaa670a6aa297c1ac7e9c66f708fc5bdbf397474a34274f448d7935d8b040285192b6600cd66042fd41400815fa9e2f838c315c1a02fb820c1dfb36d5fcbedca77aad73ce9420e7e52a757edef19e21909bcbac302c7b1075db767ff16e8c933ab79b7ba4b8938f1ee2e759cbd383aeb18bdc883d21b8d03d0a312cb2c307d754889022acca3169054122d191dec1546eeb6b12b8bc940307c36fdb9105d512b5d631ac344dafd65096e51dab35d1c33fb1c1bdc6f535f8467dc5b54b888c6e58e2894fea19fc684dcc35e22e1126a7520d75d736a8ce3e91655dc1e8f46a99cd05c5411d72b6e1ad6b34e5bcedb860ed62310e96a431fbfcebfa14a444bb134a73251c3c1a33f75c11f66d97c813fadfcf0af55f00efd498ab1bb1ca18dda349098c881046e7989b162888d63841172a5bcf65b42af81887b60e9aee9f27b6eb1a1957e6b1fcc8f807642e568621dbbabae3abaa7d03ec80b915f155d01e6aa08f2f6d3e24bbb4c881a49dbce2de1d45f7f999c8336304838de2d3a57db7f23d8fb29026021268cd3200bd7d8937fdd2052eddeb68d8510bf3562438ea0eb21f5eb5611f4f8e6e0871716e786e552e4b480c5026bcc85b7103d65c7059dd30239ea05a4cc337b658020b061174d5c00e5a19ac7c3bc42c2b9310dc491fd37d50451a67aad59a6f12ea4b65a5077e7ea387589955108d7d838457d82c90bbdf0d2ff79627ff9b604de9d2cb55e10329daae942a4c5eec8c53525c53367b1f3e971b63f4a05e36faa7065ac35eb21ae0dd8dac51b045d4fa68babfece59979cb36c54cef4e4363d86377d0d667bf30263d4d70c9f95d620d1a9b1c584bc3f08a940dbd6db17528dcd1ffe1e68d28b54be1347ce0c91e3abb56dda5ba9b32ed448cfde6368f41910160b19b908f3b5609cd833827c990579298fdbc527868f01aa563851f094847dca8644eeb300d19dd419da702f8bd79b2b77a2d210d5f849bf6f771533a9e9a5593baeae695252629a61fb341478dd8151571873031793548503f53791692d5318d39d93abd6cc5e77f5683bea336673382b647829101cc83b2c49405d10a8a50a5b4ac733cdfd8f55d4a2cc36d2144ac51c95a6878a041fe2c79302e7d69b11112d4214a3cc2d6d54ad40b9b08953842f1ae0352bd98938bb615ce3b9197a8b39e25fc62dfc7ef784695315aceb4724bcf8d3c7a4c0105fdcbd8d8508adeec78aca5d536d34840f2d0d4d33019e7fcb75df9becc703243141d5cf935f9d42f6b36e3f67655845ab79add0f050c0ce40e7e28179673bf54c52950469aafb65303d0e27bae68c18b741ba8f7c1fe8f9633066d49c47b523c6f09d7e441895fe50a71dcfac15e0fcb13f5082d2fc94a5df8965b5e11ddc4d11d36f69cae7e93694e214a0996a0705ea35139b3f6b9177343ab967c6d848a43367be183948083883f4765312b5094103b66e8c8d35cadfe3c776154649a9385a8442c86a391b23bba19ebbbf1d0201471337522399b781b5376b9c3ded4db9a00f271904655ac4ae42474a2cc0b1d0f3528f72c57e5e62f800a2c6c8d19413e51c42f6081bcca51ef3d87f16e16db9597beff6801a76c3adf2793a939d296f302a351902a3a9dffbd185ddc24f9eba8d57dc001be14c69b5749c6f6c6a7eb6163b1e1571c6f107abb3b9a1be3f3fc0f888e69d07bf07c24be2e01913f7df895511c74ed0197681ae1f86559ecb64a09f869c50412a9d8ea3759f356aad509da3617f739cb1b95da1e19b5140e8c0ceca139274d2ad7d41247b275ead5039d27667547520f7755f9e2c5c2c6836e5f4027b0160801bfd3240e73a65d1a97f3bb48e431c241028e49eb846939baec30c42c717f4cae9f89a62d2fc51ad6dae1cccd268f606eca0c96dcdaae574fd31152524bc787c244c69b31543bd1c488b29f7101f8e425eef3c6abd2bc0c55b308b305b30d1e7387462087522117596cf1dfd14a079430e312f137220959a27864c8f7b4a2a1fdecfdf7bce75508ee81679731f32d1f7f3198e3f535bfdfa09170ff6cc0a95719916083a0e8ad9d3e47120c9e98ffc7e9398dff0d74fcb3361b51b4f24d293c722cb7a5ad8954b114192833d3b1953ebc1e8d12fbab74116e56d0e07519ad354ab86056234cef3704aa91c1262b4d3db4fcc12c7a554a10051f52800a0dc718c519effe3fd329057226eee2121d848fe075fc439955a509a5c1ff8506c68d54e6bf338995b6750f9433dbebc89bbc3be0b8c650f2352925dd5b22dfb43834f15aed82f3f70c32a948cfb11b4867088fe0569c2f5ba14c012f23df7076eba3b55e2cc70527d42ab4e57191f8a25ceb41b9d4d01dc64a8efc67fada379ff5d48c33a0e1c3521387fb0952918d8427b695e8862f40c222f20d30a13c7c6cc8a679fc2cb8e469018c977c31168ad502dd28428530d1b630e95ca75d90543b64da9470b0a08ba9b1d45b956360cd7a61e4d9fd5f339de6c508b4831b55a222159f573d55de262c44e3efea22dccd0063826ae5882ffa001f78930142330c4d652c067431fb9a574fd40fc436b4b04d3e9cce778c02569f12dc180e092e8629fc7c38a70867360dc6da0d5e37c9c14733816595ec3e418887e9b9119fe8100374c6823bf5d06d5d1d1469354971be8640850805827dfa6f0475dc530737d8e552701cb47a35498dc2e01285f910aa51583e810d622bd16b450a78089c1dd5731e1cc5bb4fbfb2911b3c8aea094426c16985f27b085e89ed84baf28f85eb3cec2d31c65116978a44a575087b72150ddc6460babe6a332962faeb90c4002887d174dd01bd4194e04488afcfe402d54b05fd70f9d19daddfa35e9427eac7f4ea0c4cd24bbb1fa04e7543df268f32cf221b57b1fe9156c034c4aab5576d7e91820be44060874136b4bcbcc6b4f27681575d3ac2c0f396b3fc9e8913c225bf35b8cbbd3e4825412f53c6b4ce56305961ae0abf8ee5412eae33462bc489b6c28a3821e2f89afcdbc34fac7fd573699e46f2b381daedc84b34f91718ecb7de5268734f547f15d21089c88b5bb1bb1b5df6ff1ba655212acacb9a1d7afc9927abd09fb7493fe49bd010d73e0c6f5597c7babcc094c915c4a1414583e6239f0b75537d975540e73f3e8bc0d1b28c073072a1b0596bb36a69696c514dbe9bed38ca8b624962632461e4a784c64dc0f258d2c383d0fdbca7a08c39224e0dfa6e13d99d70cb5290cec2d725181c86ac2e4dc3fcb2a5e630ac60ac4085d76b162abf6c90a3969274ed348ff8c2493ab00b7251da422eacf82247cb2f0b0f4f31bcd82ce43c6a8160ceab52db4d5aaede3f71f878cd109e6795a01b7ecb23365511cf890ce9a45609fe555ee41680d902141d3cb45b46400cb08e53b688df527b14ec84264d0ca6ed9b3615f55e4f4384d0c6d4489cd75ead20a2db945632752c965d06a402f9ce0b0adae1caf70a77ca665e2b3642cbf93557356ba98b4a46dbea590f6fbcdfe359717f1c465a2b7aefa88894f329c22f04e1e3199189a71955e727edb4638f849d133ea0e747b1f3043400d4a2b8d881794cd24cd299bce6cce65ee63603c56d076a4a427b7b86fc48be1f436ac3cb21d969014c803f0ab5dcf72a72e8293db0c4f0d8b4751349e144df9aaaefdfd875a98cbbf9858aab07326e3e44d91c51813cdb84861117bde4e5dc7a84f9bca8e960801c35d29e3b592543f5bf197982646e510b6d53c697bd3518e462d7788b04d66626952dfc99e652064cf68eb6993eea16ea02c30361a1e69c413590f44cae96e8d6a9e1931a45379a742c246bdb78a11497d6622eb2e0aceb201781a1a22b6c1425319aaaa10a9dad236d99eb1019b884ff186d6759e18948b8fdd46d6cf83f5efbc390a14e97f8781fffb40811dfd57bc684fece0db13aa6a1c214493f19cb0707d9cf72d9602293ca31bbe5b8d3834094a8d0e92feb4aeafad66853ae8c3b6dd410abc5c1a5e94137b325200fcb0545d6121a790afc49a8c8cec757f68f5991b0c00fa1086d54040d1452a819a5c1aef4f3738a6db25505a7b703b11659fd0897b62c36f951a4e0abcf1aa3e10e0c9e0e44fea6c1a95cb7c5a36d9933f9445fd45752fe1f1f680af3e92172c55fc159d0f911f9ed2116a323ffdd64faa074e649017f7cedf28515ce3d46cc6d01611617fae87d82df6df30546c589e9817b9694e4db98c63b0b9ec204eed41186b62e4dc60e69dd9fb5e3db4264293b1111aa1be2bf9bd982f2635445f53c1688e52143233a10d374e01895d072ab317b6e878de15a65f991f93d367e634a8019b4166927c9a24b98ac0725278a7dc9afc616e381782d503be9e49422b45377d34ac9bfc0089b037bdfbb0fc7eddc00dcfcaa597d181e39b7ad17586fa8cdea62d8dd906f56333cc6a831b9d03cc5b90c37339b6227489ea9900dd5e1d5908c28020694d9a3cf024cbee07ac85add847c1aaf2b0b62b303e2d512b7c08fe72e17c603a9b7c581a7b3fda3c522c3e30f080921431e3a4596ffcc941612685df714c506e0b216eac8cc7d9691ade7cde74edc2fe418da8b3fde3224eb882444a4392807d44d161e5d6c51dd3b771dd6a4bb30b91a20eb64766b8540336473dd097dfc3490e8de885f345995c8142fcfc62747bea4983f02bb69e1d567a7c0784e456d326a41e697ca8a8f342c28203df70ea7534e6df76201d93acc2f9951ea250261215ac58348ef6f32e1e7fb5e7e69d9dc8107d9a253b4ce4efbd5c6a8075b23434b293fab8042e9ab62161373a9679132b775394c8c6013489ad462c86a24431ed68abbda932058ddd126a4b088e3b4575752404072d48cef7d4a83e6ba7034ef779bd527ff4906b6a62e15d4869f51bd30047a7b60c9e29522d6b02cbe992283552200559a76552a5cf4d684686376d2b69572d58ef12c2bb560c54a9e17295c8727e5ed5b601eebb2ed7bc9d4f52001b12d5ab85123dd70db77b346b12eba8aa653bdcc49ed987dcd9b6fa9e8073c71a311a06c31442e39f7d6954f05400284403af0c880a71f3a096289f9c97f8756a485baca2066f230e0abc16cc07ca8119caf43d44ed5ccdea9af525b3fa5e9c363121d15e6e898215d6a9dae0764599d66bc88bc037f291c879f31b05e9be4467522ee6d36fb1ce9603202d0f6cbb30532a962f3881e604af270b9d639d622457985216e1657d25bcdcc0df17bd023a5729c4f151162514e1874e3d970be14dd08704172488098370c27a90f74e15b0a5a1612d29feeef192c412454da4040ddcd0722963c254b874dfb158253845fbf7b6015b0833ca2cd024c9200096cc9df8a4a41ca0881011b1392401da68cec795093af2e2e7f83916089733fd4ec303dd9457e17a2e44eca9615f62ccfa3870ce672f2f34a0e92bf60072799442adf708c17c163c0d97b483fce83f12893abad2b3c66c1b1ed0e2484170799a561b5a6c9d6c3fcced52b61e14cab90698d4dc9093b6e09aeb491e278eee4c6f257fe4fbdf77f79a672cd5515c78e49e9dc3e81b0d51db9f4d970bceaa75e531383c16420759d972e8f7a7362410f0a2aeb0014fa0fa76b4546b42c8e15cd4f7c0b5228240e4f0c6ef6007debb09c1b4c8e63e21798d5554c21044bcc2a6e7a2136a6757bd456ea7217bd0e88aa3226d65611c205431747d132602d7f7409cd74c167471f8a7041f60b4d41ee292d551377078a5fb9361961513278cacef52356f1064a44ae356380b8bcb6739254beb5a3a6cc5feae7c4f99dbe81073fa38fd3905bc159d13b4e896bd023bba3f076e276444d87360b3811eeede71b96cee3682a27d35282fd6f44989e73f4138c4a29eaacb47cc28f2c5f2f2933218476c497737ed4501ce3e624598b6ef0efe7de6a37d25a051264094fb366622ad6f1be5ad5c3dd49655f0977c802a55e6e570622135047dd0ac1984ad7c6b0ef865ab2e64d15ba658edf0a89595c2a96470eb134283976accf90a919931e1247f39f15296f667b09700e990ca37ce8a85b9fa32cbecb6238a493a416201f6b24beb3e45371c973744430ae02d85237d510c2a5ba7106eb4106cabf774d04ad3a08c66e84675739dad7b11268a21bf2f7e51baf213f4dbfde2ce71fed0c043d37ab246a288d96ef4131124c9f6aa4eafba1d2355ed51a15711e2b827329ab1415cf7c61cc6d2ff614a2c9d05500b310efc37d8bc05f853d6da21d0eac7e9a91ca31841402d249db15a370a44f75ed3991ca3c7a51a9b84e5d765d316402c11ddb9df587908e08054961f9052c1aa5327002f1e2f8bbc3bb594fff81d8236558cf5548a40c4b1b03721e3275c561aa58b28f1fec346091afc844435828685e71cf35814d50886a0012f3fd0857e3303d15f11b2a0a2b6df2d71a9fac43b0084e11063cb411400f4fb08cf6e9021a498f018f66bab530b20e58f0fcea73f64c6d71906adf320cff8f1556eb4c9bd4570d362d1b7db57e8115a8bfde565a4a2430ff5bee598cd0e01ffea8b3fbeba0db07286a4fa5d61aea62608f6ca5376ca18bca8f3a60585fd47d8cb7ae18b2969adea0748bed7ec7442d0f88f5cae067d899c0b9023a76c6388e22cc050eb0b55801c1634a971b4ddbc2d3ad671a57928a13f321d9e335e56ad02fa724fcae024189cca7587d498df9fb9c978a2eeee7ce73c892459f0452ac026b20f1f08dd4d3daa48b8a508a5ca9aeaa8f67bda8fd57c837877d5924a4caff0f185688c4336beeb1034b700f52ea05eca02e38a3fe60e813522e082ef724cab829511e0fbaca9a8eade836e33d3356e41bf2e1f156984b189f56a464fd8cea525b448225407c1a41c845ff5e3fc49c0fe37ee766f0c0caa2efdeae03bba2c0d1a28b28a1d14abef0274162e109a54fd0279f86e9650d038bd6e5648611f1dbc141d110eea7ae35468598dff70a8a31da0516c45f40b5dd835c419fb1844fc593d343d78a2a68d05794fa08d5630fae41036431b8bb5393e139eba0eba01ea0876ed7f6a93ded9cc30733f55d9e67cedc7e741a20f5ebac0ad89fa43b8cc78aa42c886032cef1c11293cf21d23ea0873c0ef7e5be5179918b97d783cce1c1bb965c4dc30b6244e16bbc3d56deac22f5cd50625a95d3f6e4272609e3c0d85dcf23d66bbc3197e73a096ea919a88b1f0c62eb4eafc28162623cb1e96e523f6f08a1fee78a64f6949d1b4cde3361ea87455776582f7df350a30768c5550bd648d6f4234a66cfcf3696cd337357153e2a1fa3f57a6fad70b369b6766b46d9326ab4ddc3183f9e603c9ac3409017acb59636ad0b1e7a9c65743aceed41ddc86b0f08caf5e4e980dd5f326eabc28e1ce681f62307a2f4b7e2c031765f8931c449e8178688fccfbddebf6456f475de2791e12f8e62c434cdafb0a84cf00b5490a36b4548e59bd7a9a40c4417cad2567e8ab2725dd0bb6e1f31072dbe354b78e4695ab14c41824da053256cf8a8c08e118f0d335bef1d324ab554dc01e4b00962f272e9c092fe1134f4153f7f41f4362306088e5e936642260e35bb9a8a7de6f7833a0ccc26ee9ee69d5c5c48909f1bc01028fe64a0fa55bafe8b02302f0f8c2182c254a9fd584ced26ae066c3c56fa68281489fe8cf0dfcf08cf6a93fa9a83d7186145fb5034485b99724f8a3603b54fca0ba520d87a2151b20f75931dffb2dd128b8d79fa20a8da46ea6b97e8d4c4f2ec12bb07b6ba8d781d3712af3ca4d0b1a9f37c57ccb2bbc4c53fc3324feba1c7745f59ea2c010932994243bd40268841bc44eec3e40bbf5ab275a02b1abe007bd399e0b8ce14b12a28ba0c77864c89e05cb5a78ac626ba25e9bb487c5956c8eb16f42b8423536878352dac51a1c92b25b5f583f3bbd52c50a235958cf45b0497cada21a28ca88424c69bf6a8f5e270e27599802c69fdbc0af25e8f70b6e5d66360f95499904e5516c39e7e3b1486bd57a55ba24e002028c164d3614aecb67d141a5bca59172acfa2819f860aded79af98e2107d4da7fea951f16803630644afff40b27e8911a23916f958d579f7780775556001a64a80c10b3547119c29823c8e6f6221ea62f2cb7e7a16bae65baf063169db7b4451bb545da46b5f77f1ac5df1ff72ef38898164d53b23bebcdcdb71d7d45e3fa3266cca0027f87c8e8eb6bfcd5213c01407ee2810ca8a1ac73a63fc5e345be45174753380949448e786161e1de2863e4c76f32fecbf5443b07c1e31c7c579d4fc94632797586f8f363baa792849914a98484f91255aec102e65ff4a92d5602ffef95c437179a5d2c483b724c403d0a1b84ca08f7fd3ec0a78abdbb7b05e1bf110308fdff06e141927fcbc11f340c27e9bc82fb043e1db113239202307473cf339cb291014307da46022786cfbe62bce9f8877ebfb67134312a92345d24a88082d63e0cdd4b7870203e874255b458fe5f3804f3ed0ec25670af750b50f60fb8acff97d707741ccf770928634dbf63d6dfc3925ff9efe8f32ed70ff71f2dc5907aba4cf35a661875ae222693057ddeb481c668c00656b714d9e0391bc1c8c9983f2c308b2f4e69c1620a6dc6c3cae2e3e1873ea13e31d1fa29cc15806580e534e1a48c319f6f28f00e3c817ccd451c82cc28bb95623f4d8adacb54eb9ad0fe7e0b52385281a4930c28c2a33d1b2b2a141aa219dd6adee1c59b172159e466d0071b092368d658315f9e65c6f6ae63c376adf3309f05ca3411848e14af0347c2e446915951dcb91f2ce9918e09d1cc1201cd1da2e7ebf6c3bca0fd8c40985e2519c892915de2ffee9b50d76e54051d7d21353230092ff24e0dbfbadaacf9f85d56c9873974b4382f424ce4007b93d01a37490155e716103de87055724886d57c68468f440c5ab75317b0c101260d3d3f70b790b4e8a10a3fdd846b40ad1731a868d48bd5dc0ffcaa7c3fd6c0a4ce9e085386670175d056a53161b7b35caf514e93f4e51cd54e6906356c390b8c93dffb07c7b0ca69ffdd83bb7e0d309ed6c788bcd3e3d42584af4e49e103614dba2b78cc23b35b2e8228d81d849c7ff133bab5c8adf8264479f86126e1090b897df48f9b3778171049ff92b44010f1cf03f02e32e196a14fa09717cbc829c7cd619bf03ef832d995f2848d0a265abce8bbc8fa2a2d0e430071e8feb302bccc320a05d15aa8a99e88e414f2212bb81466179e51194d1c5a01d9e274d85782d49cddbc46cacf3a14ca1072be7e9bd1799621c41e050e4f423078e025acca7e2d6603f63ef4d61a3e79518036f0e1b0c4521b6dfb4fedc938308baaa7f8833282e24cc0037ce1b27ccb0a411c3bc735a0907045a6665f80e92ad1a844fd47eb4004ee922f5c7a586c46524242603aec740fe292eaf876d47699158faec3c784d0267142d2b9a4dab63acd4ae53ff7caf051615eaaa09d4293b0233e3498667e97a475e6472d8e9cf44fdb26729a570994d223d1e3a424ed686accf7f895d9459b1d5a95d79fbebf67057248922c6751163a1dcf8471b41c49470c29f1f922a2f4d77eb6d00da4135cb688e9960eb28eac5ac380a376e04b86b623b7b4a2cb1a051f5851ef0489afce94bb94b7a8c3682c00b4882051e8b9aa6aa6bd60eb0fc43410255b19749c08d4091fb20dd8e11bcf3f810ab03e7ccc98341747201268c649bee58110048d3d2aeadf623ef2a1fd0c77c30bd7224a93562dbf6377fad34b0dcc60445373e1ec6b2c591b0a450ea32f78623bba2cdf0ae3b1fb4661d4e68a865e6e4869d9727b371a78e87d7439b3bcf7125c169d47ec8d13da26820b215ca0cafac680bde1e66288d7cd7b5585dcd062272350ef5ab500a3644d368e5aac7379843a174ddfdf46150f926dc60d0fd5eb3b3bd399012c1ba36ad0d9d5d77e88c601cf410e56f83cb43896929e6745a0f8e4a43a30e5501205119577328783ffba93e6ece4b52cdaebea94e26dc7c228703ad2cbcfdedd949bb20ca040b786c441791a45a7c986ca4bca556d2b9a42c273008aa13bcd3309db652f608582348187809e4adfeb4a24d5abd7aa26da0791be3b799c1f3c83586ece4d63a83149309296f959fba783ac627cc009ad18f050c88a30be908eac13d503fbed0f6d981f16de606ff194cd97ee6c43407c894bb5c2ba7ff3102533fe649df333b924c2d5d28bc0149723578881229239fd802a8fdf6a56f1bb53c09a91a913c499649601248acda2845d015c14af42f0c9971346139de39630c20c5a60f651f02f6926b29632b641790516582f9f0a3e42e626228feb4ed6abcdad4a614e3cd57da7fddea64ce6303e4e2e6f26bbe629549943f6d6081ddd6997b9008f0dd4e78b761249f84f5632e4800c4fb05921191a6ca173fd1f17c4af8f2171c00509a48c4f8d960906cb86e3aab7b0e79fef798ef5938c8d822dfec981f2dd3bc31375fdd17635204acfac307a3487d0451171c12ee1a209f103cb2b7c45fe2d358e9c5d2b2dd9d300271d3735bb2514ad3adf67699c2b3ef3f802d0d1a08aebd49361c0f9c5b134d1f214768bc6e1cc588fb0fa3aec1221bbc1bb3757378d82807861fef9c634f13d9e9b9c3891039de80cfae1fb7c30d78212860da6046beb7317bc7e760c7777eaaa046cc4f02ba68314657309c19a50367919a9f3e8ff1b54898cbec5cee96c4aea4aa90266193660efbae272cac2e61f18c5c3202f108aba1620d8c2f020182801ec0f2100a6d1b363a90ae07c5bead6fe25784bcefacfb1bd97b4b29654a32a5145804040483043fc46088142a539e9052c462600b059835f57a71bd18d1a8dee2eba3d6d0f057b6592fdfb24adb238558bf2d8ff555b64232f7d547ad61f92adbac6c7b96678568f9172c991048ac96ab86c52ae960cdd130002a180e31f0dfe20d228a6079ec8b68f9173a2d4ac074b27ccbf7fbf2a88d96502c58e6434bf622d34637e410d866399b65fd0a673839d69095ddc8b1feff2dab94838363e87a1c1c39164ece6549232d9fe2d135faedfa95ccc655ba715d2ba3d168a5aa5aaa2a23addc609a6be5b7ea476f55a51b3682aaef01c7cacacaca252556246f304d2565766394d9b8c134bb7384a3aaaa1cd670dc108bc5fa4d8b2c8bf4625996949794524a79c9eaa5bca4252d69b9110dabaaa48d1ba7972775295ed8cfc52e33d8a5c205529230062e84fc4197f7eeb7efef377f98b53710deedededf0fd2d58227537d6edb0fb750bf166e2ab4143448c023aa19c1f73a113899452d3fea6d550a0c92367f93479ec98fc6e79dcf26c0f931d22c49789155a86425608733770ce71b303c2fda658cb9895df65cbec76cae9fb47d59178c75c6cab8028cbbddb238898a6a94a7a86e43712c800349a517ed49ad1cb6ce3afdee22ac6cc850443c68f5a0343662fe58bc7fe07cbb73c101931dbb8c25eb0582b2596910e2396571942acbcb3947e20a9c0e8c6e829a03256328de44cbfa24026eb480e816de68335ecdaf877db87d90e1cd678d7adfb2ded60cdc9dfc132a2b1d241964fb125df6df22f292fab64bd351ad1b0ac2b46964cd9147fd3118db8325ebfc5b7de8aa5527c7959d688c675c9aa5ac96854d9c51ae3304dccb6fd6aab321c9d384cb32f7fa755551fe3ea5428d4cd0cf86e9b19925e2084f0dab0c931cad2cf4a961a4208e146194ab028f22aa6e10c3de841163d903b4d3451d44415192eb420d4d811420861b8ac86f35ad6b2afe557cb57825143fe84ee1d3e7efb30779e915873dfcc211cbdaedc5d811e1743767e6420a4040c51e2c3f70f5e315648603fa900c217865c1a7b982afe00bc80d980ff227c09af8fa51e3ee1e8537c1919653060f6acbdf752098c0a28ba98efdd0fb01212cc7df5a92bdb80cceb65bc7e4735e05bee610f9faefaf70c2be8a073f3491852c6a7c409cf7c34ccf782fc5021ca3f29ea7192c4542ee693113e148a69f4c97c0f3303017dd2139db0d4d2eb033be3abacebbdeff8de7b3038ab015fbe7ced17b9c03097caba2eab8a80cef71edfc1a8117fbed23eaed8e224307f17d2c77e349c0e3a6174432402b044f38a111d57b3190c2430fc7d78cf26b0ffde87150249673ee8c31762ffbd10485ce603ccd409f1b21b9480adab29a989df14f9f92f2ad8f614d08782a53fb820899423528278ca114454a4f474a11342f8e0eb52ab6624c69d2396c2583e225c1fbb034405888a8bf921fe7a1b403afc36808080522faa1c71717952a031dc3b1f2b78e74365dc0b1f6c53206c8642d127eae40a05fa1915d113344b539518d1641a819f627fcf7746c7369719e1791930eb5ea559c2850648b8ac80861c4c71f1a1a10916eff251a2e779bc873023b06d77b41d229e69b4c741c13a6a138f1c6942e707a55924148a1645222c45c0b6f5f16142476de24f1678f972478029400108a00cb880c6708f4365acc0b41808d335f879453a3a7a042d113bb6e7b5f7105589cd8c065093d4d882699d796665db02d17126f73196743e46f72265541bee1f6804531b87a94d4735997879f898bc58131857035b31777ffee4cb2934c4e0d758c75cd7b80373bd45357e8d8198ebef618ac5ecbbe7c5fa1151889aba00379ddd9d1dc2989b1467bd8d85f7ebf7e083dd3b58b18bb0eeeef6e8839dbf113c9722780dbf9d045a8c7d02191a3b48c2ee4b62b06661caebf7fc79630f0667c5139a05afdff3e73d22c21a28ee04a902617b15c8e0a4bdfbf573944382f9bbfb7bbe648808ca69c60b9b9e417b601a7ffd1b3ee0e4d880d6a80c853c652663587337acdd50044c8b790ce1ab1e84aff2e7f253ec19ac640c6b314eb6d40a42c055c95635974073c7f7b2205e670fbe4cb3de7d8cd556104d8bc1c931a34c4e4142cffb47cac50a6cd3283b7ac44822318dba3410ac7de6c90aa32fafc4101114b70c3720bcea7ae5724f340b53dc498c6bf1c518939c463b38275d181244db97f272a2a3b3fbf3c7cccccc9c392f6d3de0e4871042f8aff478b004999cc1e601c6ccccddbd8ebbd7b97b9db9db77e3c608a360294c3fd74419afdf830f767cbe6193f302d6dd1a83bf479cac32fb1abf63d5de0826bfebeece56659663ec67448c4d42475595a5094c1fbedd1ed696486683b525d2dddd4b647b4a3b647bf4692cec59225de5e4d8d7d26e8411427f858e3fc5d2aa5e96aa4a36aaea5996f5a41cc9fe37aae17fbd0d55eb2d592ac9973f83b5eef15e4544395e5766433f6e31b391a584307d9dfafd02a834aaaa663e58eb1dac45e9d922f05b1deccc331d2d59609b0add70291bdabdddfcbd0203e75cf78e94eeee0f2ecb88c693b0d71f6cd75d42b573ce41e8205ce71e3a084fb09d73fd9e7b19cafd7b7ed1f1ce38e33d84ce41f7dcbdbb25bd406cbe2ef55477f7dff6e7de95600183b96285a5ab990ac1367ac04197c33937849c910ec59a0ed3c4f8d6c1cf6199a3c3555b7e4508e1cbec848270468c314a1d7e8d68b8846e79749f21a584988eb6fab78ebe33f60c2847343cba8dd6d1513df7482fefbd6ee8ba1b967e3ad70dddebe79c73eea4c8589285c9b41ad8defd1cce98d5d10e1c64deb0c97d8a515530ff4d0b01434063b46bf7209c4b30d9f735d7fe5e5dd3b217d6629c2efd8aec4c54877aef596f35c26a4403be3895b9e4dceebe760f3b704d20fad7c8f5298e9594efd9893594945e954634aaca23af389837aad16f392a7ef5562c954e712687bb4bc8f022f5a8068a6922cc50a7c928ae20c3767087df33379bdcbf0c87693ce30c4786d305ecc47f61158f9460694c40ddb8682edac60005d85545bf7e0f3ed81da7a0a0dd06bafbf337c39d80e955554546de17b5e13190610996830b62da0a4642069827fd6bdae904a439130e4370bd03054cf5af61ac397f2a92cc28606e27059c4ea7d3e974caa1323910718299519bbe809a0a1db41196bc7ecf9f7764d40d4980de7bfe9ca4fa5aa3833953ec42843a383848a4229c85333f6e899d9d203b5660c1ee6ed3d06c9a768108c484859a8914371c61db62f8b6f4a66abd9560baccb25bf55e611a46627cf32746830ee183cfc130835876e102b6ad0fff4cf113a39d4e33393a6e8e080d3d39d24414a7d3e9743a9d7252806dec73f26102867f9e3869a593ca9c26f39ed486e7d545c0bc446facbdfbf5731ef1c032e1c764b28ac482e8803b53d5f8da69820f112245889060dbf69c7a86c0a498462f1f2208050535219a1b6a6e1a4ad3b4d7b409401930c371261b54806d0c04c4049e5c0d94d12ce4031b4a2d2d9d6117c768afdff32122283c7e18e9e1f9fa3d7fdefe8a520522fd5cc96cb896ab840035f15725abf480d6d4c4eeb4794096d80373bd6532c63dd107c5348e089122fde4891327575cd1a40993b9699aa6695a0e36b9cfc1010b28a6713e37f8c1ebf79c6558ae3bc4aef184212228afda2f16403cf5b5289e7714495acadc5c5cf80626d8b63f3fcc404344507c5a7b01c9a016211b50bbeb2bcbf16a27227cae3b7b374cf32858ea520936cb5db0b43909a67ff3e0e6e387250622e59584c89c7a55e2ccca162e3b65f08135a1671a850de123826f3be57818ae19bd62a3920e9d3662a808c771fc3d0e0ee78793e1e028451cac392f6055c90de1c877590faca11c6ac6cce30724574604d30a2d85bff7f62d33ef430821fcb74bc4ddfbb9f773efb7ebfebabd776494214bb1845dae08330df7ab7dc7be355a262f06afee41f74f4387f00d114159c93670c7e8842a314b3848151ee22176e278dc107ea2b1141e622a6ec70999ee9f778cc638d818c9a4e289a955d66750356d8c646e3ce42cf0214e986e7bd1b8a8b95002dbbc4811a6790232136ac33e38e889d188f0a1280a17334cd35595654130c5a9d48c869026bed48c979af152d97b103e1acf66a466c05497092ea6d1d601cbdc1f690c6c87e395b9414c8e23d5f6d703936377cb6bf481c9afdad563f2bbaede7d4c75314d1341f99906a51a8cd6905093c78fb9af3a73df470560a647f6c8ea3e715a263f1bb442eba465587b9d2c1729988cf5611f159c6620e9b5d3ebeab6ac6c3b36838197ebe2466dfcc6010e40296a4335401900821e2a93c3110c6fd4c67b0a3e2722448a14e190cc806ddbd333e4f4989999b50984da04055058292ee22286c251e8e400036c632226e22729286e681540b8430522e810bea1202127344e34cd3d01db5648a8899290a48bbb7b555553f66b20e9d40080ad061b38e0520310bbab23c6a7043a84ef46131a7aa2314dccced41dfc41414242397c3a9d4e454551a250494d1e3973377b895d0574081f7c7e5d2e2e2f2f303537dc198a351bae88d1b58fcfe3e0189203a55272a8ecce0a41ddbc6abc82890bd3fd86000dc374bf2da0a43516180d816d27d4699f3323703398579baaa66da3a0408a2387ce0e95713cd4e478a2b041a6734d9ac09c50674e601bbb2017238675cf885b8e20c2c47d69bd04846387b183bebf0efaee3e04feaea43073615363320bd853e1832bccedc47cc4d21f9410d242480b283f509cdbe0101fbb0b86e3cb8be68af5dc624fe7fe456fe791ff65f3b930f02b1f5896aab2bad730ccddbcce9fdd7719863575cead7bfbbc9d332bb373ccbcda0e33332bb36bc15a67d700d8c63666deb25619552765dde3f3d8afbb338deedac58e0d9f7784cfabe817d67bafa5e9570783edebcd5a6520f4ded8cfb5eb6fa7704af9fb98cbac9bb461af99661f3412218470480adae7d160058e073310f99122b60842c2c303173f4158010f5b502942944491dcc1922e9a54d1c5130c3006aca74ed10591c9075d3cd186740125092225446a300322b438a245920dc8dce732401865f7b231b8549300d574360603d4c4598c262f99cca2433cd28177e7f2af940256aa02ebe74b4d2f231c56a24861e90f48489912450b213f54621005c67b0d3a8194a94127e862eacb6a6c357ddae8500485a5038c11f74cddfe0fcb296408172ec32a4c09218410c22020844f4addc27237fb9dafbbba2eb391f79c70c2df69d7775f73777f1d3358da74bc533f3fa90cbfe7ce637c8d84b5c8708d85e53a87cab876ce0531e9288e080b735e776b672eb2d6ceb96f6e02e327aa6ae0fbdf85fff103e27929e60a6cfbf8de1be2ac192f01ebdfddcdfcbb5b477392d7ef515fa639b42efc8659103d3d6329b8a7d52c805c6825444586869c14a181326099b0c412459ae05004b6ad90d0154588526af386869ca4b630379da2331dd217b06d8584ae48a9cd9b4452284263ad3252c2779aec1f137a0b163688bbc2ce921aba055878a719f705f7f71c421dc23f7952852a40010a4c60021290d080b9690dd0344dd3340568cd05d4a4b50d3b9a8f108478788444c1644811137b647f36898e0ecd622a16a652406b58244ba653844da11bd514217c9d18e1f2c408e52e0f0fcff22c4f559d2047336c11e5d3622c2dc6d262acaad262dc568eab7e107655599f62ab6a29a5bcaeb6b690a51baf2606520ca5d186aaaad0ec1b737fc75f2e8b18049ea1a5a8c1080a8542a152286772989017f02e84259e6ced0e0d64fab315a55ef2922069a84b357eec355447d5b875db31f78a7db590114c6af3def3e7fd1bea0028140a8532c0d2686035f7a80ca8cc4a91496db897f939e66d752b9b951cd0c95b6b8f394d406de09394308592b4484280d668cee4da5d8c1849d329801c61f2db080a0a7a95d912065e7a6ce0c6f0a236ec2f4a524db57d8ff1e5a53be3f1638bb83406662f31103f5615b73f8f511b864668fbb2022f6ada1ca4c9e3c7644c4dcbeb8ef0d81d82110f0d63bf0dc86c87fb63eef36a0c765e6776eb56de4329af070584fe5a922c55cdfa8dd1923700f568c4157b0a96b8e0744ab91a949af8af203e3ee48e838367480e873dc43804db3468eaa032ff026cd321140e1dd446a5081eeae66d2378dda81bd40deac6279883a8b48d9b0731ab118004100053160000180c060483e180703c502451f914800f638a3e745234140844418e03298ae228c618420030041963084146a1a14107e32dc3d1252b309dd7fa2bc0260079069572cfdd25f0912778bff438acfd9fc4976d00892659e065144eb16a5cc5703f884abc36ec39236ac9c68d22f6e8dfa9c3a2e0689183a8b9b2e418026f1413a23a74a79d2f4fc72542a96905712f3deb2e4531df19e7c0e10e287f6d526f88756e52b727ec51c9fede104e2c3f650d0d090a14c493773849f2a8d8e49a7722596df977ff473ddd6bf929325352d82d02c772d2f10a1ba7c21bc6f6669c807882078fcaa7754a09af00aca0de2b5c291d08c12261090a0f716772ee185e9a3f5fd7820abd449c3d71a03acfbabca4a9aa8ead156be86d6c98de91fbfda227167382dec6ba64cac7a1273009f2c48c16793ba387921bf88293cc582e19e2e058fbd918441e4d030758968975a6bc2a24ca886e9161b84f4f619fe4cba73e775f2985728c8d7cb80f84baef87cc221d1fdffdc0e8b36261c39ef596fb889522843127c9f02c5bf723e22d9c77103d53a8b3b467cbcd70d441bd9043c416a5b9894e80a28c580a7aad37a643bc85460e67af37ace7611ce4517666e2d1737b287fefbf53b37ddab7920e087a6ada1b2dc7a9222179c8fb465cd2c27124757e6476fb7317b7b070896f2b2a5725683da7ebd52634c1d0d732d25de98b488423cb0a07be5e440b8c3f7173c48ab255a8d4f08920620a0b0262ae3858d66f92bfc215a462f4ab9ec066a74c32c4411c77cbf2f2af5bd97ca2a0b8a4f34ace709d8862ad4f5c15a5f074acb7bc720397ea0e50f9b45f845b7fca5925fe750af5e3c4e039c9be96becf7affca4afd7315cb64d276a998bf5f7aefb6d64f70c4e0497604780aefba6bb9580ed4470635a02ce349bf79a2c87fbf87fb17def7793ec92667a06c322b5a46ccf986e89e52c221293b95c33d2dcf5cc8f9fea810e38a4c257d615f359989c57ed79212a9e20f04fd1e045dcb9619677c2353b809c48787f9c1f4c119a01b0aad3a8006a23623b173de268a308dd1f286643599a366c277fafcc6b304e80fd667c9474b61fd6c82a1d998cf082bb8c6dcf29da4957f3286bcb93f6e6d497669a4a4084e54222410d0d1c543030fd0e326c4c6903bbe3d7731db407a623a41a3d7f5b6943a2bb6fe63d2621e6eafc9cbd408e214fada0c4f7c6b7003b9e22c9ac29d851bda1be502af396da6567eef582742cbf80bdc1756e1974d332d2a90507934be2ddb9916c97c88fae15befd0b1acaa06cf0ae97b65ec5104b13e2b729a5e9c117c6503c78c55cdf8e2fb3bc1ae62becfed8a0aa76989c8e6e84e9bd4a66d308826a604d0a170d1dc5d37e88f9a1937630bed3ffd4ca3899990f005511c1d3999bf833e5ad7136548c3977e96c1f9fa6a22fb7f72e12fbbf9534f8bf7960de7e0b86081a2dc883200ef0046000d7b5424c4bdc443b374118dda2a3008007b99b625e52eda4b3d2180355a9591ca6dae395db38bd5e66ee8ed179ce12c97a46bbb4aff9a361f481487b12943c8e7bb34c577a180145288b34353dc659e999fe6142f6002ea0437a472dcc4d053ade531d56f2303a59c22a6ac0f213e5cc2bdfe40bda80bb0f41c4d73525fb8451d0c2506db1cc6a1c0948b986c243423d52ad65169166cafb029e770de7a25c6100aa75cbd122a5a29d5d2b23f92104ab1bc0d8569282a7dcbffeb66251c490c005c6f1c40ffcf6771f2230b80abf8468a92bcd8da023a0fc26269c3d574e66c9943d7c28614119e4e0a7b71b54a4a2b486bbfa9b8e6c996fb8cec6248db170f020a560765ae9df90214c5b1623cb33588f87c01effd151e26bf3b45650d0e2b18b0b1cd23b04842df3e687fa28de7aaca8436702c7a6e166569adf90da6e9aa1c0fd0c65f0b64812a2d1dc6a63924f3d3e2d13d648d97380ea56509c53526b002511e933543b46cb09463ad4bc5c3e4b121bf622f015742bf76835417224e5ebfc63bceb42200f80579eec1fe471a30905bdcad33ec7e36a33b3436a2617246af64f9543a422f53369a5a6e75eacf6b57307d8410b81e239cb9c130d45d143500ece27e0253baaf3f1095eb97acda7891c5755725c9452e5eaaec3e5fc740e707854b62185f74d26b756a599c8d82bb8126b28f0f4943d59a1c5d415d5597bc2cc0f643055500b84a455a768da16ee34a57696cfed198c89b6c509e0e2635c585229404e76f7147c5b6cc73d03a083a78e2c7ee96fff9aba14e925e458618540e171d3c929a094f3c396483ff9632e1c5ad8bfbb611097a85ea23fdfc13214ef0ee3dd06b67d805306a588029dd59873a9a84882ad100b799bbff483b44a54cf4af18961fe9422b20d9a2bfb20955115995842279365c97d77b145dc2d39b4431b753ab06bd573bad8a76b55d65a779238ae2c748150473a9bf3ce516a3283b03092d42f17d0af4109d54cbb324fe109a8998e40bc4341bb7fb2724ed875bfc04aa78cdf12fad6e0d73bb8370f3cd5dea055906a1472cf2df66e9f801ca39d174db1de9da875f416b7b1951d55a7d0a4681efbd7843f769ca8a55a7535f6dcb1df52df8ad8e82773acf407d7228d28b92851c60e2df939a7e5143b1a3eb0e8048ae7cc8a415bc276c6996d73f790f9f08ff254c4b7403481d0902e40acb349ec5f48407b05d5768d9fbfa0e5cbb88be68dbdc59740bf05ceb10d6785981635a8a871956b99dd85d25bbd2e8f5e5b3c44a83e1edc5b9efbfcb9cf902dac53f413e8a43b0942f521e2d6ab2ba237705f6bbd5d8d797e3f788b6d03d87360c5abaa967e74ab22d93fd226f5ca5b9a19875511abc0d883fdb7f060e520ede22391ba4e86ee181bcc5a1347a63ba29968ffcd16cd8c972382dcec61cc72083962a8ce45efad0611d940195a9b97e0b8a162d6e601507107a40efd12b09f455ff7953a34a57137a9cb7e7d95b8dac2068197c3786d8a8a11ba05213fe306bc99c070670be8a0926ef2a4dcebe3d09ac76ac206317c7c4acf6547dd51313e7f979092027c7951daaf7b24298a116736cfc1a2bcf90a91682571c325595548ae8fda47a62d8eea2670e7dd1a5d91c7abb4b299436ef807641cf19572ecc6fa4662979e1bd145efec0c26e39678c4d6984f45e143130c0071ae448a36758acbcf521560913c2600012135c879bb8dfd75afe6f568c571899c22744fdff38d2fcf8d72b90a691be4dcd0dc71d32953a37d0ae133ec1607304c42cc103e2092858d5301cdc7bdcc2c3d4090109071fa09e20142cd062af13d09f1da11e77a0711b97d0df886c21df7d4f0f23f5faf5021eeaf942bbb853d487048766646b481242ebc5e74d5ed648985208dc9c6a61943e7962a625c2d705fabbf840893302d6346f729c087c7f9d34ff97cb9ae3db3b49a215be9951a728558fcc2564dba3cece45a34799c181b5e2c6f7b03f98984262853fcc2269c93857a68ddd8a01c8a065737585202aa172cbaedd78228cd8f54d2fbcd24774d906e64fd8530170dbff84127f5ffec779c7e2a6a57dac009c357b00c9d63a8cc0c30ea20b6dd2b8916eae0f07db64199a8eecf0cdaa0113a8412866cb1336877f0c3d8a8800487888e03e0b0190ab936dae58cbbf610e0ab9e835963fc570b902d867781327c76d5d876268ff6642042c59415be738c04c48f1d46aeafcb171b9a898ea95bb7d2a142e4816ecfb96e5e67eae12f7965312111560fecd486b761bcb42054664986ac5a6781ce7f85070a611eea49fcda12684238fc73f90dc2d9c46ab11fddd48fd7e86d3d8f119ac2936bf1ceeb6b1424bbba8f0ebb8d1dcd02442fb92aaafd9eb9c028526a29d5fd1d34b5ed9c150cf915b81de6034343ff5e5ec45f1cb8cb82e2a0085772a728112c88b88f98caca38228e0479d8d2c4431a44dd7a4b43f513b037ed8a1167124230bb62434bf47786cad4b5ca2809ffd7af6a6c700b042d3c1d74e0590193f4197f65fb37885d86ff8a710cbc451f3cefb04bb7e13a538db8cf9cea37423d8ac44e016d8cf84c44b9f63f01b170b0a8abd869d824a399c46240043293b24c567b36b5a1df40a3119d4d17b5b2f21c70fbae7aa256159a113e1813f0cd272c8fa1edc833da0ab3910523c2577b1dfaff6cc93c5ac8f5ac33ef680c14786c6fd600f2dc180b9e96f63c896e35e43e13a60b05224f8cf11fed8d85b859e75d0341235f73e60b8d385efe0db1ca614babf3fdd073b7ac3eb03e5e8cf34db305188031b1899e9f30148f444074691256d7fdfcde7e7251e08697175d25b1a2cfc6a69e63eaa08a3bd3e925513b10c766c502eb616e5e2a34545682e7f4b12caf7d6a41c81197553d3185bf0fdaf187ca54fe7cc1af5c6463a41ca786c4d60e76a2cf71294fb0ecdb9da3e2765f679e3157bc48c7b684116d5e76a13a0474db2bae834e93e1408944a3377a0ee3e48e066e1a4b6d164a1f0dad493a8bcda73c8832d256375f7c8bbc3beecc3523ab3ff53c4b59bd5d7aa76a2d617df64d87743d5601cf306f1edcf67a742b2462e652fdecd1fadd5ff5a188847c090cedbe91b07f11be7a0c5ead55724fde695667b2748da1dd4b4ccc16f3ac9d1d12e238451e02a6e568299bbc482a695d7907131f1ff76e69876570e9fbc590547d0a578af61018046788c64f6a36c1c683f55c665e5c597f3f3c04149894547d691cd93a6fd3ead19c602d385a55ae653847f812c071ca623b58cb4aeef98861a58432e499e991c33d61004b102443283018bb8ff7420a1e0a0bfd955e79fdaf57c438b6687a7c382a93a16f78147fb2087a20066d0cc3645e4a6c0a7b6fc3428af50826441e7bccbb84c92567935caca940479dbd78b569d9dae0d693d0c83ae597223bfe23024292c36d1e3bd9d755fb4d7d59db78e75d9ab5c06af0de5582791b223606790bbaa6169cf38fa2caa82a1e46d549938987a9ec3de7df22b693c620f3b885c37d2a604e78dc26796016ef5b81a3274cddff09cdf898d84f9e1e990026ffce732c37cb35fcbf8f8272c3613b9a2b7312caa9b7556e16bd7e8ca811c85d8d59d3e358c1b6f44fa9449687a01a8da8878ccd2b73697f225667ecbbfcd166958d2185041ce9b03b0908070f44e9827440c7aa87653f354fbdd4589ff4ddcb27cffe4a0755aa4b1fd51602dfb3c842dc1c0ab78b748c7afd08d1d5418d874921bd0da8b447839aee22371fa4c2ca4a182ad249704d86db449ed91422fda4311018c7da08645f46d7ed8eb207349a1cedf2e4bed7449c5cf5615c16b887f186aa44e69e0b52cace5e8948b190b19ddab7a7fa22a7778589d3e7e1964ad2fbc142063e0adde6750c238a40d39bd93e3c8087822c869c4cd8e12c6d075ed958fc7e06a0172d99bc4328fcc952c60069e14c70427a91c43d8bd5c650cc874836224f6157563aa8af9ab1e1536d1e6967a2d8409959ff1e03b6ec44f46ae70b6d48d1ce93c1b3d38e1466300a3ec8980e9551ecfc90299124b957dd5062cdf648786c70d8c6596e8b57d287eaa87aaa1116e406df1708a2a03396be27ef79338e1498a8e82c747b099b2a791827cf97bb4b70ee24d612db8f6862a8bdc9553d831114be17ba9a1223d6f919e952174802a755753337a7a7ad63e7a2d9e3a5d723a2d530bb703838b20567f34101fd1c40ba07e60ed212f1bb6b0c89f963bb1133d59dfac88f2eab5c7fc146f51d321f830de09760ed9d8afa6c4e50bc02bc560f6d2f026065cc14107471d3b40b4ca80b76a9697266e6a58b239236ee06ecc25cb4bc5e353bee892da74527310810cc6eb7ac3f1f40c3ed7cba8ddf8e270ed0c53966b98797d06c6e75d7b55014c315617ae4560af12e639aebf60232b7f852ec9d479010b3f328affea318075f5731d1f68f4066060977428b1cc935a8a9681b4b144c394f8719e80db9d01f23a18952387d513181ad082e87264ed2f6ed18a4dc1533d0a35997712718bedb972b873eee40a2a2df8de6739aa9e322bdbc4fb71601e03a5a5c05da08f179dd5c2a1eb3076383d052ba2790b19d992f1001eaa70f61f17adf456b6bccdf0aae90dabb80cb0bf303e5fc1ee1fd129f74198490dd877e7eebf9a2df0e6032432bd0699f0229803debc0da4e58ada9f4979551a51f5a9cac181db016d4dcf17090910000abfb5d7c89f35f803f2af27259ea6f8c3e8c2849e2332697a89d5020f46ef07cd770bf1540fea48ab0fd336905e9c347d3379fe41f248a388bc004a050d072f381513f64f7843802f865ff3fd9805a10e93685d59403122f68d2bd8871d5157867e9b7635842690f6532d70b4cbb22b930548640b9cdccbd0ff1e6928f70d2305472d6a13cd4513f6b46745ff86f54318eef2eb8a023ae06211e0f6923df53f7c36574664c1987ed1df5c5adc085e3e110ce91cb791d1fcfd9903b38d3d6615d3e90e75ef81b27de481e9b454dbc9b4994da8b4ce704616499645e019c21d90d0a3daa60760832019816978fda118ba67f67ea0f930fda00e275eef1c1b17d7973726cecd9b75308f9631508f0ec34f606cb92a5d1ec3dc747e44b22fa88827059ae67f32d7beb422ed85948e4bcfa0b9ae1b7ed355a3792101e67619c9f2cba9c440d7f1c51adf1c21acbd7c46982c09467c4463fad0175f16ad4b1130b9ab20671d412274338313d0cba4caf1202674d2144a9501991d4585c8ac48d00cbc0828bfb15149c7311e1210949f74beb65e81a49403401d6a669621b52ce362a8911f04a60ec415980b78fcfebff641e093465091403aa4206bdfc7767b97cc0c8e6da82e97226ffd6fd40644aefec499a666268996d2ee7f27f50c066d97d2118d118b5f754c83632a791765e500002121ba23234c49e17310ad04be4592f31221ea5faf4ccb80af8c14befea77e4d3e1f1e056111a19ba29bbf688736fcfd4a1f04cecbb170dcf51c542ff6a3df4610e173c2d79d0708f976a75f7e1e6d8476294a62af717c92afe490d6221f5d1cdff520681516f47161480fe89c043257c60ac8635a3c57db15fba1d3cd8d71b5af4b121fa3c30e27fd2562d953e22343c816317e90fcb18784bebf259a8a3705fb249c4a74f0e178190bbe24cd352775911b53f09467fbdb3de80c9424a1f73b15e82895886920ee8147b3d1498a4a123d3191e680cc0aaf7a3295bbc64eda3f32fc3aa08764c2492ec5d59ebf4a002b2f2f1d42a6308a00fda7fef23b5c892317b24b00abd135a58ce5701c5d868642238cf32969f264e28803ac71f5483212c94b66bb0b58a719a181025d17f1cc381b9cd7c3c877d236ab68be2e5ca94cb8a3847b7da3a55af18b0dd460742730c790bc19130f33f6ea11019865b1ec1937ebb98378a26a4085c04b00b21d1a62b6b8e116242c5c303bd0e5971eece4e54230ba9ca99cee962b2e89319da81566eaf50dde148327073f93e9a275a665e938342bfa3843675048d0ef8183ab4bc015775e11722490a02c399a29ba427133a25b618f164b496807784f5d8a5f6ac49349d467320e740c9eb6ea08604874f07b906293e8a71a855298917b7060a845e40401ab40ed74082a31a97080605ae7626e256d4e8ea3bf68987c2f0a7b5c653e6431261a0b3266307673cc330c8fc84441abca6d1a95707d621a2aaaee762b0e01cfc89bbad795fc71b5bfef138ed619cffef71b9d05e2c8d394954c5c21a07be021318035bd3f2dd890eedcdff3d024740c9441a4c228651ee48144a1610886d52f07d52f80046620d17dbf58830fa30d1e086cbdde0e8798379629693a9b107643fd51430f5c20e85f5f16084b0a5f5971dcdabe0696c0fe41e1de64eab245d2a2ccb4811b2b2a5de54b57357a9a80a6bca9a79e1193dd1a9a6098ecf8162ea7b2c26a21484f08d32daa41b3b1a1d9826e575bafb7eb5d74b28dd46ab98f57d2d3712fb55a5bd55b955b518b0fc68ac7b9878e61a3c69c629916a6e6aa084e4fc17d47f48e3f843c7e8010eb6c8a88d184400f1bc4198322c1aaf55863fddb0021d3b3c0cf4fee7b2c685617f49e64884b3014e836c0e02a6c59516affa8446bb119c87ffff6b2aaa2fe3cde9a54204eacc1e38d49f3dfd656dda91b5cafe586f5655de7e4658625b600c1dac54176c7b10e81a2dd75f2a25c9743ec394c8347e10a225c2e0e5c0c26a6015e6aef8bb078fa888329d40b450766d639c041d3fddc1048f99e847b0031f09ff9acd3d97455760c55cee5709ad1a40339f879af2509cd68290c6809d3d0182c4cbbd8f611a40eb62ec1c72557edefdcecdda00e3134b5b8e41fec0a003dd2c3506b4bb2dbcf588398ee49040bab7460f1bf2af4cd0d061dc7126e54988505e64f596be25fc828a0c9f1b62cb4f1c5973f0cf9ed439a4dbe68414db052caeefe64008b2ea722b080ff3d0fad241241999f9468ac3bacc28c2776644250268b7c3a4b3906b4b3350a8d141023797459f6c02ede1b74b98db06bf44e30822aca84daebd80d82c32291cce91ac304a80b4869dfebb7151493169da0c419e2d0edab5813aa2febddd1dcdde643e97ebc2ae881074762fceb1dd4ccf9a57a03ac831397509fb913f02663d6ed03c2e03a41aa387fbbc62ef16d214744bf9d96df55f78392a4d25ccc34c0aaa7ca7c4cf3dd47f508391df3e839db57f7b93838c3dddeb73e8e75cdb8898178c868e325028a86eeb271394e8ca2e9b12fb2f172405d6322ab678a404b091aa52cd18978cd32230fa2e4e6f04ebc34faf5bab8f35e4d11d6ebb2640094da629fae19de32868d75ed825f8864108873194c39ebdf25417c4020d0f721ecc4ef525f3d36e4dd63d62a0b878a91a1662315b440f3b1c0d04140012744991c2f3585dd68bf73bfdc804e394ce17c9801074e8b270c616d0160dfb2e0e6435d3020f45de64f1fd55e1b93dd17ba2e6e4a2592351b44598d732114a764c45c895546af728cc919bc4013ec5dc48094aebc1b477d396c4356aa4cd233dc3dcd322e69295e6e246af8bdbf50b410d86136e0e38c5d2ca1b887f07a99d610e5f6b8c4ce27793c1358ab34ddcf63f2b64b65c6ed06207810576fb591543600107e80e49ca619c14936c4c029fa50a4cc2034bc398da83ab6968452f451c833f45b3558207004dc060e77e437e06a17f2c93d4405316ec26c3066b6c828abeeb80ca85c52c88b8ceeb1012513dd2e298c5625a4edabbd1e8b2b495337085df65eb7f05d12e1d03e1234721541f85546141d233326dafdb85a93ab615864e96c46042f700e6b4e43bc65d454b7798df39359e2681b8b6fcdb223d2723449626f106fb9187e043e9aaea82be91247708ef1111f31a46db2788234ffe29800553e2d3a7e73ec9fdf41e6e95ed35c97a4e28513baadb0ddd952cddfa0b91002c46a7a82d802dc73f66522ad3639f886d7aa52f32bac2f1564b819049fd87d7960cba44dcbaa5453c9144df9d684a708056a596309922cae60b8c123b88fd951b0859816791cc1f7232d01ae4fd5d3ab3bc7e15dab9224dc799c934e89e06d231f5bc475a685d4053a9f3991434416fc83b103490cf4e1d882e64fccb6d4aa3d4d19cee4f91a11002ef25288c2c0085cf053b4e7a81fbd1df14ae177f66382c271f78c0f605635d6b53569ffe09b339fa90e3ba63c08a25eafa97f5c554f65373ad254c359e79ae98404b5570793d20f9a92a323aed1aab908769de5c0dfeb13a808b4a58b832f4c6e2e9d0c890e8aad48e063c1b82e04c83e67e34f67bbe963404148bdd8cf6cc0797ae998c84068f3bb3dfbc592af6268bb39cda9b9bc55513933ef62ea18d2b69b4bb5f9ae515f718a77fed7363393fa1097a126847bbc556df26932a3797968dfd63bc928f7c73575c72fcfcf38e70a93e80f9823096bf7b920c2e2cf69be33486e7c90f78c01a75a0770ff0ea613452340755cd0c83696f19beefa7988f5a0772eca56b3db6b450d829854867462465d88a5b278bdc5b75a2dab07cf2a47b4bfff10bce98576ba64bc72cdc12fbd7cf0e675ff41d002582fdcc5ff80d4bc476130ac8fab9729e746a2c9a23782bb57ee515d01f4036042df31593935ede2305d54eb2bfa4cb64aa752a952f60aca5fc8d4fac98175cd697206c7e0961674fcb1031c28b2c9ae9b2ba8a8e861a009cac598eca6a72c26541f219f2a074cc6b88040008d5234f704bb44fa1a28f8b1a4e0aba961da23eb93ec452e8b1ce6d450c01de456eacbedf8845c0ef1ec507aa684d572a6c6d510fa3569066482f40cbccb6d0046227e45264a74139d3a31106d13764b6562f82fe0673351facb4ffffbdfcfc85a30b99b3f2ceb38a6858a7d5006283ce8d2f9526f7eb942a8c886736df899e7c8c6388c0600b4479ff9751013380d0c640ef9445c2e8a9c8d86e75f4ab0a045b12cdaf1fd0d81171dc4396f2b88149f1d6a714e58857f04b954be2d48cd198cf244150c8431abfa02a551d16db246199ac36327142dfb42e57dd990aead33bf41764b8161274801148d3fd506baed5aa18a3c39ca078d08f8b1823c1e0ebf83af0c5a5697407857d580e32573f717dc8a0325c46a8782e7f10af319c230c422a5c6d4f23bcc18e016b09fe90e1a0ee05072fa07e799abc40b7ee9ebc2ce6d5d835b05c7e17db5c1c813d05ef2d3b9aca1d3388293edc512f0624b95a8ca8a15012abf54b67348462b4efa5ddfb56491804236ead4d9d2ec0c1dfbe85ba3a47ed59b12c675e19308b515132b2601bd62b2a10fdba37550f9451e2bf5c01bcf6e45fb66439bd2239f2773c38739eb5c119d26c9a38d6c7475726dc8f6db7acb1bb48d5cd055a7bbf87b22d3bbaab0937d306a408b289155f748d63683d464341118270e86b631c2cc6448e34abf3904d6b27104f80e8762c398e39db084559ae88c3309a52a999bc40be1c09b8ed0c9bc027fd753935ed3cfe4875ec0a2354aee2fab065e1dc10b9f428621cec5c23e4b9058701c4efcb1d81fa68f5a6cf5b87415cd1fea63d5e8b74bcbbc94593373e9aa8552754554da69cce451d28770459d622b7eaa0e057f5a5b2174ce039c40fde8eedee81339981f5116e4b27de56d4d42d1235897cce28375aa95508b14cccaad574859e1084d0b9d581a637c14aa8b132a66c03d1eadac01a2fae834b083c7f606d911c5fd496059bcab9ff82c12da259eaa2d115f395c2a91f91ed154e22e4aa1a8e05e4527c4a19943cfd1c42accd41525586a01fff8e66fa06c876b2e80be2c22f1aa9c2a57860e18f78704999a83257e5686b197feb97f9ebe47494586c34ff91b587b68ce05700b9b72b94f28f1522f0f08cd2a1be22a83fc826daaa20552baf3c9a9abfa88d7313d5c99767482814d9146cc431edaf7849c12ac1a6f7828737acded992383d8622847980e30d116b00e70eec3043c839259601e7378fab8cd9ccda075ba245fb6a1f0b62cbfa3a69b8c1ae753c677f517c8e52aa5e8f861e54264a2c43e524a8e36d2fbbb8b949adcc2ced9dcfd09e3d82f3fd703acc4077cb8292f5ab61cd830c6d43be9855461ab7e2da48f233d53df31c37a058045c76ea4c7a2046f572f54873bcd2db17e78bd488e4af2002949bd5f3969f7de5f7053dc844a28702de30b8b104411c2f8d6bd527ab3321efe83947ef3318732bcfcc6a74e43800240fb0f028e53de04540b80b7ec1bf15605707140b0c57eafc8efaff8182bffefa5627551462394cd8ff91c3f3e1e87fcedd3cb504cc49a93cded15f9e2eb0f0c27732a2cf846c6cc77da5b3383d3c2a50d9d87b4ed94472ea7b55ba2f2ec0fac5b98fba936aab4bea63e791b40bb270775d8497c078d046d3069d28dbda02aa3e6ae0663e83bd102dceb4464e80d0b8d4bd19f33568075f3d508c1bf060ef9bb8a4caa0d15aa6ea98a120a1b6a6ebf599ab1b74d526edf7c9e00851465f26843bac5df8a0a3f604aa6cca8b088ff5859141a66dc6c506be24347086309fb9f161cc2b035475a3424c0ef3eb0f70bce43c280698aeb960f58fc608e662f4cc046af85219b88f460786c5b78f97aa0fc8f3e34537ebf86a2c0b4f5618d042b5c99c428068c3e79a9c7997d35fbd35c0f93439e48f022875a432755d5cec569a6904097a1a99fc1348413bba428fd672553a40c595da1e721838e0a547b6ab1a1f1a08e8d4265f2f9273258f60379ac15bd15077b8656239310758b422ac3667d1b14f4c91c88f6075e742a98b5ce08d1ce57e2643333ce97e42a76083f8f2cbdc484a152ddf43f9b26368cea0cc58987dbc690527a80ea8cce61cd8db7774a6563a3cc92983ad3564bb0e83be9b52bdbd32aef41193da50cf1e1a4d755997a5ede7288e4d48f43876080bedd047309cf83d2f651054a42719f599343978dd444bc6d0f3ec99f30ea4c0cd2de5e2f642784efe7aa4d595e3d6e3c528d2c3cb1f184132500605e51c9e2642a5db48e116f650905e49ed81373d1e2ed86726604938149e5f33a7293a013e9bd0d43d29f594ca0536166e33987dc61db4e0917329cc93c2833391f398e6a4c0383fe4a1fa1f709d9176bdb92536ab646d751e59dd02db2d7a9e0108409e7170277d5f858dac385bd40bce6e0886a7b4b33bf4924bc1f2423e3cc5698ef272edaa02bca8cb9000e4120fe35b43e7e9c801c0cc5da4a93013dcfb665d05f5a954266e20a9705b8799582f8f8cbf0291e681740caadb6cbcdae5eddd814e49c080bce664cab1d8ba040713f0d3849e898d7e36ac411919fb56018b80d403dfe62d603ad6745769b60d0b6046e9d1b4a9c896ece6ce644c2f6b922273abf60e825b639a8034238450f93f822f4d96630af25b68db1b8be58a8316f2313c5f09c6ac8ececbfff4081a9a938da4e1efe57ab3cfe69115911afa49897f0eff48f450c962168644b1ce7796058c260570ba229b8df9f7e85286cf1d38d5ce78b4a9f82d4b448a0124b1dec0f47e0473a5d8d07d9c13f42734ffcd29c450538c8f6c640d56919fc7f230fdbc387130c9fc8eaf02ed16cb74cf7a4eb3816371ab3192f6d82ce8472e42653d22a5fae0ab296d935e647c5a1c5d9909ec6528cf83cf7a4d00b0f74b0e4355900ed127e64baf6836cb8c18985a0bb48b28e2cddd0c83c05ecf088f45fdc814fd915e9207075dd8303281660411f70733f50a661d57d2dfd5a2a296313ba43c7571ee5c46e464826ccbe1a6d8726b67d0a4e56fc2ee76bf0b85c36dff559c1ecf33d4a32b97f81ac0fb3a1ef5fc8db98723f4cc5d2aff315ce2e0a4010112fb97e9b527f41bd67b2a66d43dd3586207f1d654948151cd44ca321e756c5cba9d44b9e8f724344c4e94814d82314394c28733b4877013d1e6cc237e4d74101c84f49ba9a7d00dcdb668b3c3b507e900887d9115aa991fc797609217b6a3de8f7bcb078659a668ec21166c538fe894eb143b3df42cafec0b8a583182ba2c8ae314afcdff3b215ccc60a21dc18b730d59d0d47ec0245c60a65e5440acc76c0dafc93ae373f8b6f56274e079d995ea79ab561fb4c15a5c307b19a617510b3c20c215e3da04ae61b9036685c3b105026d02ab1278da66627f11990970f347d22e47170894f02c2769f5aa0d5f6db46cf3ee996c68ede1dea816bf49158eb9a3a68ea6f613d4c4edd46a9b295637a4c6b2c8c3fe690d61090ad3aa6ea10321e6b559d6a453f716bf19860ca76fe01e8d8e794643beaf0bf322a0771494a2645a95016531daaf5c7a21b4db01c558749e3e159d952b02652d5f81a59fccaf8ae3f280e727c965e1d3fe67ee427e1fe64f1aab09032d7a09f5aaee06c2d4a34a897c1134d9a220e8e0c9b7fb935ca4401aeb6942732c99b8401ff2aca2398249c590f0cfa0d5639e22ecf1aacee33491a5a4e2d78882659dc06d3f0186b09ff8f4bbec1b6e161d164c9a8e3cafa1ed2278a2c8b283a85988884acd6484b618395181c9ba38abe4f7c57aef001fff311fb36504a8b11e9ae46272f810111dcbb5a62478691b84eb9ecdbb514a69646b4ed801f3e8b78e913b89de9cd902244d9cddf12244db728227795977a1814d065e8752d885edf1b4d65d427ac0550bba5417281eb56c05753720dc997c56a08c44a1332f4b46569e13c0568187d8cd7ac97a158c5720167da760119e015bdf1db7d893e0e4a5e40bf0254db2ec91155413d9cfd912a0242e6d27bb974a01444d99436296944c4dee492cbc1a086f250db52ccd47cf478b501332138f086fd367ba991573affd6bc0382975ff0bd10eeba529c6d9dfc12d987ebc49b7954053fecd8d1725f77d75704fcb258c836faf56b7d1a04702903b5a0a7c4c217feaa9b2dcdda821d7eada0da9766cb4996ebeff3fcb283897ae568417b4bda3e3c4dc729a39ec3e4274184316e842adf06e6a528231d82d3c2ddbf9fefbd06f749602089161601e4093f67c4cfd1e8aca4167104346836752cce382ba0868226f0d9428dfce013ed1da11469c158678cd9c8fbed77cde8287702e8bcb3a5f0b12caeb141701747caf5a6d9e694dbb6c24295241a1aed81a7d7e49e22fc0e4a22756e4548e1a222f3da9d318a387db1ab0d8e54a62bc9d2d48a50e98d7db21c18322e3032dfa2d5df302015bcb687910ac3a23708fb32b3128b03f653dae55d9b7370de05305bf6d5e54ee9488f2e426ce857a5fa9b33c3863b5810a1dd8664aac0b703439ca0d038c18b2c63fa24ce3575b5c4f42430088b140e7fbbf1420712f8bf92043c01bc522774d50a33da91a2f22e476bc05dc5d8a308a9829357e1b15477e171c960dec328cd521dcc3c8330ccdf52910b11223069e672ef024fe761a618befd11814951b2015da51ed54193c22d1d6836a6d706f4a42af4ab275ddb9f6ded2339722c9f3fd029b5304c30b07b0fbc0dd092a386b502f802aafd202f554242caa1059f85c7e41fc1d1b866b4349facaba4255b0f849b225b10a513d496c6ce70ba6cbdf166992fb329cf1f5ceed998770d56174de6f724881cdac946eb34d1c506f8ba86cc69b97d90c8b2a0e4400b54debe16e69c6654d2b9f1d36b34f80272169cbcfeed61c5a22f146da42a9b3b9411cd790b2a8b569c7554557e55e555d10b0d0474d75ce281cd59fa3701545170407dc76070bc03e81f6367175d54da3f9dc3d10534fd8869fea87afb0c7f4c527066fb025c2ebed3f5d8bbae1082a2cb5acb1b7bc659ec2ce0f95ff10d8ba2cb1a3ffe56fcd2e4eac18b5216cad870c9b81f4c29acdf211990dff82d7ad422d52e7f8d8b097358a596dd3f19dad9315fb5c51f1f9ed9a0095a396d7ee36c07d1320aaaa3df05d120362dd8490ce469a55b89b0b1bed5fe317edfa1c0e19106046396ab0934af72be7be909835f651a153c2bf208269ee4270067ee0c6dc0e66a52c7340c6d86da2e5dda9dd30d8f7a34e931b7625a30efb196ea2b8b1a1fac2d3250791c79f3ac92a92684107218119ef83d5374e4d78538b497f7ccc8425ea5cd1b7512f0ee1ba2bd1c5981b9f7741164743808341b91cafa456038417a15c049f2169a44e87cc50fb549a62cdaf0cbe85a4bba4af70adb5d479dca07fc654ad1105120b877c4d66f979e6149a7ceb655888f4b51d4d4f5e6b3a82e00bd423b23d334856ed1997562cca85921dfcc512cd01bffa23b5b88053c7d279720057a0933029fdbc049f8ceef897411a919a624198bea23c89b3211c6c84fd77712c9de5b38cf2cd73423ed15de063ead87a817cb85bbbd8e58ac46a202ce112babb60f648eab22a3e9a71e702d8a5e01192136bc1ef4a619d4d0310ac9c14e8d0e80ff9dcaf887dce2795a48416f4e64ae0188a67cb9d1abc3f1e85157bf3aeb6dbd3f6ae40257f95ec3005d530def59031e6360f6468c30a4cd34514240e08a8cc08961bdbaab75590a5692043c17b8cc3d6780d955c4f1792b781a93f45612b932e221210f17429b00b4b35008145482f218abab9870c30e191d0871727ae944c6ed1571978477328fd986509f1276fa6adad31fc978fc5e96db5b7404db0e7086d5b373be097a1390c8a92e617be7848e0856f5a5e53d4376f12c3967fddfe04ff868cd428131ef2444b573d0e7e7899b2a5a869a2ed19da1b2b70453b883e74394feb55f53ccb510e5e0965e2828492022a8a7f88c6f31c5bc6d72f41dca8744a54c562b22401a5dc368d5ae15980f0da0554a0ec133d8bcc023abb40646344e6d17f486dde77ebb0fe3e49ce0acb6afb6e11cf70d874de6c7c2b484e57efde757ac154c4d53ac149bc8b77f414808b2a8b71a3091a81c3710e2a9d36de5747066a9b54cca337b10ea947944079de1791066e959b3192d15ea7959bc1bb39663dd9cef5dbd1f36f20d6fe7b0949c37b7ae135ef57e70de21042b54266ebe7a743089a783920aa537d88f09208d8b392a4fe77d24793fd3484bf6de780b65b2b3fcdcecaffc48000d252076a2d44fde6c283ac595e0f99f8f389780271ec6852f3f63608b47183942aa757f07421b718522199772b50f07580b7e4e98b848e6d5f6d1d784e7b8b8994d0f2b66bd1ea465724f6ab50fc4157215ed830be3e792b0c908a61ae01875698cef994dcf63b8447532893ef274cc25b7a8230840d1d442bc30f3eccce9485472ab1b3ae7a20a7d40089e7d7287048de485c6ade633549feb12cfec21f13e9b93877f32302dc77ebf0bd6f20325c6590236c9c0eaf156777b282724361d3099eb116948601a6f41f5abf30834ed32338f169e4002b3d026231a48a70bef6d6b72cf3df05dfba0b03e4a357ca678b885f3f542c8fc195b52b4ae1e34df456fc42d28021c8ee01c3cc9ec2b6612d17044c5ad99431c9dd7c65155c85b8e0e8760b300f60e4e41e141743820c73b011acbfeb514662c53121e0e8d5e752eb1e8c639dae88638e4ef0334e50374d508e8b31a74acd35801724e8be6f70cd5f0b3c67854283912626aa3d8f2d861a2813752bb55059354e09025da3184235f983808bc11dd8371668c39d49360a4e81ce740d03cf7a4926726b908468029eb280c08f6ab1f32fde9b9beab2ad2fd2217c8c0946e928e49d8e6f901478ab904994568bd4bcca65a92ea0ac847efaf359dcce8517cca9e42c62506ccd0e8732256e1b0250603d41223da7346b75962b6129b46665962307d9563b9813049b0c4d099ee2b31e19544cd33a34392f9a1db0ccd83c7c7ed9598f484247a2d591f0851bd728d13607891ad099345082e1592e7de18e938315063c05cb5cfda080aa52b2ef8e12134c8015dd4287f456aec4b3a842b858e4e7fe6ac348cb0c17360cc32910022ce688921394672bfa1017c757c0cbb871bd85206f64d3c4530c1c6eee1435f61b0356813951920a5626a4a1ba512eb232940c80ba154db09a4782075e87c5cd399bdf80f8a09358b22c0dc5a65f3af50db313913b5bfc6b70d93104f4ea4707ec5c6413e96a849bb9ce589060b725689d5b4ebba4b5306c0d795eb505495ff90675b2dbbf023381e3271c5631ce9be67ef2d838684d3178625ed2d5be716e72fc965c690602f36295f64baaa5507984d45e3250971d9c0f97211b6e67e44a431bf3e7c09566de3f59fd7289ca0dbe7dcaccc49b6e68d8ea5fe81d2b9f793848c2b35196ee684d15d026e802013ca54a2798ada5b29e4d5fba9398ebcd5ab3db9baad6ac6b702375e8d3dffbf6ed5ec7794ec8b28b88b5a5edf578858ec65deb1b6c4c2ee4bde05c8325f9d3ff17a72db12083265590da06ab3f875b0735971ebe7a0048d4767e330647796454572423045047011cfc903a82dde9bbd4338319785fd38c34e19014a2bd6a920f6480c39aa17de8790334c562cfb61494a90dc14a50a95f01a2a82185e63d9190f0caa75a3daeb79f8025848e472da38c13036ce84eb48a79d0b0d1267a7c5ad0733d0bb050603142edeb4a06044d17427b781b27039abe21b17024b3f273903c4a7401957c90709df19a4451e404221ab48f05ac0914e0556086e15447f7e90309edd83a96a7f9ec3bdd37c87e8970f10043441363f3f769bdc48e299662b229b781c53f7d00a7d39bd8eb771662932fddc90d40a2becfec6c0206fff2a6a8ba62f933111591fefb4367cbab6bcc840d8bdd7e052925a878bc1d1369f88629f75c4729e2c7036f652dea484396fd48124f4187d144429aa57e9106d214e7dc212db4365ac8f4e0b3d24134113d1250b3afd606ad1bf7a141affefcd99530f6640e9dde85108b04474ebd1ab8bd673b51ce328dd4e3cdb314f0c8f962e3106b68589df552e11e7903f87527778c3fef9f4fcef42f7822c7ba86ec3e69ab27316a8f96ee6b9ccca6ac3ab346815144563d07b9be14af51fc21cce51dca2aec0910a5d29a8703331df63198139f9004459a0d90638ef7534c9028ed620e2faca92fcecd3aa57cbaadecbc7a0a9159fefd2ee89f793007820d3418991d907e61baa1fc668ee4f8ddff5c77cf488059b9a9b0f5a277b28842588e75270c1e77fe0d9263e940809a7bf95224078a6804448f2c4ea20521cc7b2171de2603585228bbc91a398df4dd927f690972a8ad928167649d34b116bdb020574c04370ed6da5e7f928bfdf263cf1591a4fb63ec147d880d0555e40c022092959e0e7e9f86014334f7afc874940e1f9d15d61db7c8d45ad7698db4b495d238494df84f7074352761afc8f4dc48da95174cd84f9fc02c0f3da4d53e685159ebab0c2a6e2a7af29fb8cd5b555f091e9d20ac014d8e7a32e34b3a030ff6937ca54596ffa865199e49c4e5257b6a5a843ae49bb2c8a58789c4639fd6e769b086f174d0b1f96a187a41b0f5656b8b80237aa8066169d12eb690944835ef2f9160212ec62378a09d43153c9ef9fef570953dabec02a51439575079e680d23b5a8bfe370020ff32a5909aa828f3b5bdb6fcc8af6d75b9f8ae112eefe561f51602a97225b3580264d105ec6fba8dc052d6f9e2000be6ee3c077f6020aee3d52ab39069c4015d8d9355386e7566dbf237d00b0f9326de98cae0fe1c44c4bcf047ccde6a4baf9a7f8b8d533bab5f2dcbea68193da8289a5d068c162002e93047de2d35e4f07cc52b6a65cbf66f8b536b1aba664e41fa7f67e7823b70894dd5937649a7c0a199526f9928bdf722e8aa444c01c8e31e8882a80d5d7ecedffde4637795161d06d9be4960d40ee103a3ca9aba9ae5e4d9beaa8c18b415d7e790c966213f8dd29d4b54ee55cd580d06b99427ead75fed4d1b1ce42a4ba29a9f41039f542d130eb2a331c41ca853941393e9bd1f7596d963e66506c4e06371334b65b79cfb677d61b6bfce635894c06f9ae1d6b5e9144d0c10622a57a934cbca5f6df39240ddba8073ce6fdc8454b74e43fb229e9f7b5450aa98ba4b85c0b14415cdd4433a0ac0ce309c52d7ad733d845952cc7246bdb406ac822d95096e90d814baa71821cb4e39ec432163f6f305c4fb76c6561cb2923dde27c17e5339145b47fb9bfe450c052d893b99e4f7302364433932e4a5e26b0184387bdb403c6e26e646dcf3ea58269f083ee1911b759e9af8d141c0a6cd24a1473194c228bf562426ae37667ea055cd4fd8b7f6c91b77040e239189a79607acbfa7f1038e3187ac3f9f4eec9bf1ce4f04b67a4aeafff2ed629e6ee031d7679774afa270bbb784175cb0a7644fffacbf40438177bb03ee10ee53346403c1a284430bfdd0de66af05430fe8413a26d6bd791c7bf4ebd1acf637098711efa7eae8a6e73aff547a421587cbf508c9b5271e8a5057d136dff739a1fc05f45ef44d553e2b95ff1eccce39b7c5a8447ae1b78a4a6bdb4a09505a7d8f25280ce2a7cde2f6268062a07c8c3985a0136001bbdb602d5713543e8389debcac84df0f156f61dcf2c3e3e462e49b39d3f16c009f8cecfeab0ee65b2a5ef168a9f21ae25e4061a1c2df63a409ab85a4506a2eee736139612ff79044f62bab0a4750e56dde97248eaf9d745346f5a4d6173849ccc8d5f02c2bd55886162c8a795332c2673c7c857f5d689fd37cba093c0ee588926a1f7f5a071ea3452340d41597682663f39de020bcc3b60b0f2c2a3890a4c893d41f26c7097e679a1bc798c3d4bae849079048866861b2305b8f7e9a4a111607c9ba33fa594b7922cbb620de516aa172e6b0d41c033f890110644963578e61e6eaca32cd2e57caf1825fb93bc6f09705f06dbb88160168ac5fa76296042e17b5cccd9c3720032882ff476ff7000ed4dad02d3186dc25bf0b41b02fc53f5a292fe25a4fac97afe7965a377a9fd09779d3e5eed756bc10b6b61bcdd2d354b43ae5cbdc75a2bdeb3a18b6736ffb671d5d1ba2fd8ed4430fca2edd450aad22c253f88805e2bfb44dc8d4a617f37f49bfeadc8b8a97b69f40ed43003ca5166785b30ce424a07026d0563d3624b9e585045b675d6f88af2f2650b0a863ac375b916ef185a765479f0392e82b1fc2c59c513b167383d6704629dbda1392dda331023d02c5379d0573c7b3edcb730b3f3cff125217befbde59652ca246584074d07580746320713d115f6efcb79c4d8398c0a696811fb8f161b4ac638dc9672bb94c7de85d409ebb17320a5443207fb66451e29d163b74618e76c8d1efb8ff65890635e038243192e13aa3d09276358e289e88a8b89ea10c456bfd76b074de4f9b5d62ded9980c3d13770f4e72e98bdf41c30fbbef454a8e1a763b7d66f10cb8473497966a6492e0b6369e462ff0eea3dadad991765cfdf2457f233f7d6124e32dbe06da5c2c9189688a5114ac3d4c5ac13cf3a33c59dd1692fbdd3da7c9a8a9b96b3fd1de89cc9134f3ba7a5d576fe80ca2b355666f369aa8ca394a336b7a320bdd5e6ebc9c9e916576e9bb3d3c0dc3d719cbc76823033a5a1c8aa524ad90e020eed6e5b314867edeead0739daa7e368bd4a5a6d7733ac6d574a99d17a86a33da59436b576d4a7b596a3b46a1d05b1ecada3603d2339ed4caeb5d64a67d6d7959d2caed5522f5c4a29a574aa524a596badb5562ff294b56a980911194ac9504a260d194ac9504aa64c1a329492212aa30d7112c76a3808f57e64d8478fa60c671680016b43397f3e85644efb0b9e0b2db6c7b4cdb6e0b3b47155051438ceebd19cf34e68c9c26915f734d055630d6431a95c6834b4032171b43f7d0b21731ac914125a80c7a389ebaac469d13d9f4353680aa986c8546b8d6a427c35e31acf4f8b9e4bc920e9124fcf52fca4ac1c4824f1a33c82f59c488c9933e69c530236c4d0303ddd508689d6a444492899dfc4738e538ad1112c871fe7d2e4b2f9386363a6ecf264c9044a82708202d49a94499432b36676b3835c2e170fd31b520288899a49b09fde4b930b2945e68cd9657ece897482a04963ce3969d83c0d973f3c0da741430a134fc367783d34da490d92124dbec6b3d75393b3a7bc9e9c8a42c6a71ce5f5a45028f7bc1e54f6a204f15ee79cd7d3d56c79ce2513cff9e6f5709b4fc9b3d56836afa95ef3aa6945535e4b0204afcde035c75e8ff601d2d0e31b24b47cf655e1ba520550bebcc5625c3aa0043de5e95cf434262a86a1dc7b7dd8c205cd099cd0e204d6bad488bf4d90f99bfa7befbd4fb0ecf82d96780bf298fd090e7ffde27ba99080bbf7de7b594eae08c20cab595e13a323db164e5d132c48b426ae89971c300b6584b6262c38920135925ec91f71ee02043008426a1206d0cbc94f08582043e4c48720169a849c9400a8014c8c9c2001c4c216474e9e48c2020c8c6aceb09ab55a926d5e221991428430b088b20598dcbd443232832422224cf9090a80999c7a89f433f4c50f124ffc40e12c2a9c8ca1095119436528a1b12e7d0447d4aade9609218b3c4e8b2717dbc4e4d3bf2ef26c59b98133d6a2f530296b3e8ba40b94c773eb228bf254b756ecaaff2856cefa9defbdd8859e0be93e50d5a2f5e0c81224436d2d097eccf116477ceb2d5abff19166aef8c287232e578b3a8fc67d5db65b89a399ba09123298ca70e5600229685c689ac660da62362c571966dba25a9dcee974ceef7a67860d25b78f5c0d25198bdc9e5d467ad0d57458ab614f81984d6dbd6066a90e101062b94b5d91658b1d7e39fefbc27de1beb8900497f558fa419f66901018114c245dcff51ed02dcd39bb55c9e35ca22658bf49a1ca505b94d8dbfba99f30cef99b42736922993ba6d2c4d14adf48be5b1cee7a08390dd1272257d1cb08cc8fd4c83775b5d84a734787d163cc2a6aa3986c435a5a4899e8ca4b6e400daf81ef837c4f81c8344fe5ac431910276338c2c818466328b9b8202aa2a34498248941248653bf60cc9827a630929e8ee88f112e308ac0782ae3e933901f080864a466a5d3cf34c9a7e9edb125a963a3c2f17a86a8b5d62aa7d3d945e8104dd3d4dedf5376eaa686758cba6a0f5121fab2e16d1acda514a3445a795b25a34396070934d44343f410ee21cad44df488aeda312c2f1db986a8100da2afef1b1a1a72ad00c263a74cfd31313149a61a86b0d323fa7d97c7d3a1efa3b6bab223629eb9d6229e007044ca5b8b268f19a988230178eb9bd763712de18857099400838594297489985a6bbd7992479694357ec479eb7289b75e7126810adefab7e4ad7bef48a4129c1173344009a4a126ae2d4b6468112788626b0e2290c04494f1d5b3a326967873f563f7b58aaf4844dc60f4d56db64ddd0f752888afd8fab0be1889b1394ba7c77810be7eb92ba3648a122c3708c5944c716448496c042a00b1a4a40b20158496104999ae8c5e896d1a2a6fede4412a8900f6d6dbf5226491062644b691921d0905c5c893525a6b6d254a7d299ca199acd43b0d6b476186f9f5254df333f46b91390d93396d43961842501ef1df681c74fb0cfde3acf9d622736a97e9332db616c9a342af7a8c18305eacf826244c4628500dbb89f369516cba847de0640c4a9c80f102230999ba1565fbd2b7a2dc33295545b65e613926501f75524f1b4ed89c3c351967f2d48425f41c75b6a24c7f86da0364be01648e0d9fa3ab38796daba595565aab05a9137db24ecf3c51273379a430aa449774cc64eb29bf6ec1ceb90c95023d3f9239d4fdb90b6af2a88aa437a1fa388fde8b9f312fd87c7a2798900a1b70bdf819e382f1c7a3fa68a4443cf50e1ca90d638b9982872dc4f450bb9ae480c482a7def1a74e943a5198cca14e614e79a44ed4a99337757aea488410947b86dbde0d7db2734939e8ca488b64e486f5518bd4c71c96671ed1d65affb99ea7516647cd534debd6b954388b64ce9469349350c2f3ba0c0c4d7120ab03459c1651a107bfb528c5a8c529472ddaec5b53ae97bb20933cc11b2d5acf204eaf2c0b9438accf10640e94b7d979b48e0cb717e4c92473cd79e1640c565e62573aa350f2f376ceafe79cf34eebd65a2c28161c754ed6306b9a38a4903a66986d51253b76c5624bb228e72862619c33a543928aa2fbf7e55cf47d2e242a8cf260b7b021dade03ba859faed3d3cde92967b41919519eed888a1f679435c13823a7ad4ace9ae60eae0989630ba9837b42e650a7463267883c72464f7d33a23c0d83e5cf45f1c7cd6886238bba165444e22029112bc5eb2079b420ba72cd1d163671507769413e04c128b5520cd156bdce84a44a4891ba85cd1dd9d3c4419dba164479e813623cb530091be229bd22f494a67ecc96640ef5322dd2cc296b6a91d2316b921486f3e74d14e98970ab2bf2a6183386081afaaa62a4e90a11a22b36c03a06548a10ba62435191e6f055ede029f68b4e9eda524b8d24f258b52c5b142f8f88cb4a749f9883be0d67f3957105ebbf3aaf87528ea31dfe6810875112e9a56be0248f386585ebb42ee348c963ad49c04bf77e988180a41ab13c561a366080687fb8bbbf5fbd33b106ddc7820d3a8c73630fcce4117fddcbe604b9060f073b747043c2d286854a67c991f2583de8a42aab168ff0d966e70be797f5e086d27ce5072c9d7f0902019e6c96f2511e2b88036673251cc019d939620949494a4a5c66d69522a961ad1f783dd5034a716a98002080a7dc45041b2fdd032f64e3d54baf60371ba857edaa7cbb1a68b028c256de13364410800e00bc748e881bf39512411e7108f57573268fb547cf11bf9e50109b624c4fe711ab7d7a3ddc00c258c794478c03e7fc7deea2c89a2fdc83bff4400679c40408ea845445373627050802238fd58397ce8dd065d747c0f325c4f79da1fea42474b5836e5eeee0965db493457d960e0fba9a35a6aa481e717e52a73c6ea3ca54990dcdbd837fcb22675511271c47bbc3584addde7b25186bedbcf25e6b6dadaa5aa59492524a697777eb349d61ed30febeee9e362fa90447a9e5e5ea65d5ba9f86e30cdf614d15f93e8d410c2c92dfe418c460bc6169cf51fc35fb5a29a55d6fd42c7bea17dbea9cd7d3dd466bf52c08e69c93ca7b9db22e9d5d6bad1a94c9e0386fe0b0c2a6b47691a758bbc8bc5dd5a2740dcae4e9b8c5fc72529ab95a25adf5de7b6ba594d6ec95b9f6da5a6bd55cdab95d8b6515d265aaefd3b67a2de6bc32da56b39a6badf55acc755e190f4b9e35f33a6bd5b86dd3327bc95cbfb6d6cc6b9d30d8b56450489cc471ae69ad813797a20b558a9a735a87bb70a4e135d5f7691d963ac2b983a250e36bea8e976da28f4f18176a21e6b84d7bade332ceb3365ece53e2a01fe86eab92af732994ddb814d8e3b3010cb28794a7739b95a2a65d97a207e6a62d4d58a5ce759dd763dd9b3b54ee58e917d549a9a3276c09d6e459dab33a795af0c70a7a2ef584101c1753923c28805053acf84c8eb179db658c278ce0a919a454f514c7a9e32a9e7aa6fe35d1c55377249e22393186d1d3536f297982de6e915f473c15a47ed8a1b3e20955840f1c97208f092642b07561eab0e8a0f9a880897be12d4bdbd22babb8cb8bae2413311f7c52006302063f3419b97af0b1c2c8a8071e7cae10322a010f3e312123293bf87081195d09f2d9f2649368c0c5899c7a896483183a10c94174caf825121120a62042048e75f9d571c660a8a4d7e1f74d27b80a562f05196cd76e3f417e8741ec6fa0abcfcbf4585a2f05d902cf1b94cf49aeadd7c33927bf10c1010636e09f1d7e248fcf3df2f8fe2087782d244a86915d24151f19327f21b9a03c124c2ff50e75ecf61ed12e7287b467465be6b1cb1ef398cb637bc563dfbae49a9caab42937e7061b80372d7c426e1ed0adeb1cf806fe8542a60e2598e2490e5a1414b49e68524b94d74f4bc890ee86d613940fc95460a5f544f331075d217b89969c280f8bae700aa6d43b35eea36db2461f1ead11c67e9fc84c3d63dbaae48cebaa64ef477f295bd5ebf1b9a17e9d83b0c6e80a7b06de50df20ac4c4d7485bd82c92f649fe82d7a7c891edfa1c7d7f5f8be1edfa0c76e63168be4c9fce6b1db29bd73d43b358edd26491ded1f76492d938d8faa1fad91063e5a2a1ebb7d519e2cb446f87fb42fb03ac99c6a46aeb06357961ac6e3ba4457d89f62c9a37db5973cd6a5c7d8ab1592875597a48e49231c3f3259cc58bd7c3351235532b74774855d7a12b86e8de80a3b8fa64979a444449cd68d9eda22d3f0b19f28914d980ac74c54c3f1c35b953c5aa3948fd5db3b98e11b0df0468bd81bbc115bc42e9f01a9b0fac6254fecfd243eb5881bcd63dfb6e4544d78420eab13c83d57ad95c730db84d43c1af2680f92c85928e4490b995f0f85c8319e285af282c9d5ca42ecae9634616e3fbd94134d049916b5883d1552a21631f64b947d70f619e00cf37f481d9b63f7a13ccd4457d8714e61e750a0fc0d94df4cd825b6fdf4f98de6b9d449af3095d22bdc44e66007e50e7a84c48167cf6d4bb6b676addfdced582cd11a69e196ecdf1b68034d9489e9e808069bb1212712a71165fa8d62fc7dce8302a13fcee7b6701cc2f43ee61005b14f30887dab5129bd6ac72115cafe45dbd712722c91bb27f2f4ab9467488d90c917d47ec8d2c49205f038ba99d685deb1dffe02d0b7cf97440193058c7c177d3b0cbd93999424795b6951ef8cd40895d23bd4c9b77737bd14ea1b6997af42fef68e873c9d47d3a3ad67fcc0c918b87079bd5e499cecbd768bead5722057f0c909f2250cf2acc19bad1952f08547af107f5459a194822d4a90b6cc0029055470607008b01553fcde9b44149a8e3a73ac00fa5a3b89134014e32c35e1a46f7e578a7c2c588a319d398382f602054f38bc709072c3d367003f9014b5010542568ee0f2d2ca112fa0f19b19194315463f473724f919ea19d8ae497ec65b6fa30204215d41f2d665116f2d75ba02e5c72e7aebba42c58f3de5ad9d6d9bacf0f58ebdd6553b640fca4f90c67333e90529c8a0797b7611dedfc673d36341f3ee6aafbcdced5a0be19194a6e0f670d8e099107c830078db610f9b947d04e9951dc149cebc1b49061a7d967111d2a29d209216a36fb083f71d72032579eb1f2c6951e2c7b9f401985cbd4738416925e8a5b74292d46225cb4b6f85ab85b20048567cca793427bd9e9437889de835b0bb3661fc04717ce6e998c9de09f4a3b802835f7e823bcf496e6eb2ceaa859f731bd24f5083e7384e7a0270411e7da83e9b1883a3086b7ae9e998495af2d33914cc043f4104fcf474ccc078a8f1bd42167a6d3d2d4b969fa0cd679e0928ef10b7e63ce8cd747933e705718338bc3d7f6bae037163253f41033cc7693ec1d9d1e95d97cf35d6b4c7a1750c66ae81a38f769486a588c14f90009f8155e6d010843eaa03471f5fd8655d9662ca4fb0009f819dccf911a47e56c1f80902e039e9ad90b578a73441f319de4cef97f3a078f47143eb13ec6e28b5b002cac151a565cb4f90f519a881a30534d087eafb06eaa8052f85ccfd2f289194927c8633e585207e82e14b6f85cc259682c84ff07b892d853d75bfa0449a22f617df5c9748515cffe0550f84f31b02998f0a479b4779d722762e93b351362080f01287a30fd5639fe1383c77935ef0d24b013b0daf711e3dbb098af017f452b8a10ccea3db6380e633ac1ecab9eb61efc0f92660e7c0f91c9e586250b68892dfddb44ba4242b2f91b018f113bce990019acb1645781b3fbdae459473d43be1b62009a416bc94621291b493a246218f8465e92798e3bb934851bc7e821fbcf452981b9a414db24703a2b1209ffed2d7c4f7216617cc300df0a6ebba5c3b1478a36929f046736d626d0678c339e79b731c5803e2c61c28df03b78e4e303ec1b994042848f593531e9b60687cf1aa1db2e73304c14249e6f56991bc0d225a1332c7e1a87aec5cf7dc8979b4e4d1b863b11853ac29e6147bdad1f2e29238a80d1b8f026de85c81478c375c446346b1a3182cb684029d9317dc41e6e040cd97ab45eb63b55e8af0e2c48b142f53bc24e96879519239f2b9bbfae9ad1e05ae1ed7a6a6a332f248e56533fcd16203c55c73fe0cd1bc0ba2a7855c665353513657d03c3b613e9a3e4a41f389a4458d869f6e83fc741cf167467dd84c14d8403a5e720fd11f5d6c8ca39e29682ea4458dca167dd864e7d10d94f35cd609c9951cc244b9e833922bed0a2800f05acef3681498f318942a787a097b89d465cb5ff1fa0f5e227591f29cd669e06c0a9bba145104d1b4ce659d06cea7a32b623bb449d64dd488af8e99acb92c0a421fb502ca37405f86d94def84ce35ce51e31606a13f9d73ebf54c07e14a96e102d3fd65389d66538b5665258f3e6cc2116716678fa45fb12c5bd4326e03c7efb7e975285be6e8e94cf753bb94d0b491dea12678eb5369168591517e7d7a5a389f269a89e425c99c4eca8c9b4b8f85e94e7a678e38484b5497d50ba2c29135def0e969e1db67e6bd834275a89a02c709e4538836f1b4931b37a68b3635925e59b7363d28cff7a1c2cc7edf2f160a3d2a6c003fca2d131bb7804af5d35b6cd15a950d68a4c84cd37a32db87b72ee50863f3f0d6fb8824c13a0ec71f8fa7d69a57c3006a3a923a2e3d821e71c32ecd91d4a185fdd2e9925d0852c1ce688777ce39a7ca6f03e970c9b8f2a0181cc37b7de2b468e7b401279a5e596fcda7b7de320686d2f374cb7a27e91eb3e9ad6b7367be756d86b66d87dd3d7dc020dd42d5622e7030c08035d1c2d0a2b52fc0d0a2d5c01c39509895434bbece594785e3073e47f3cf557ee3c60be78652bede8937faa5853e3e8e428b3a54e47b820a3e2dda51e543fccce96d816a5909bda3eda0dcde70b4c05f9f5c4d2da0455b82064e47813e7eb468bd877c9518294f01f58e9d28b7fea3772447bd9e6906956ac187ea512ffc0cc7394e478152f3f1f3c17a1cba4079a63c62593bc1348f8d4fb7311bf035f6ab815ab41bfc04259252166f2d90ccb17e25bf28400515578a145d314274a548100541ca6597edb89c2ca1c6a57fde0898e1d2bb1a0464976e933397dfdc9185aaf98df91167ee58491c7646e8a1386d0d88e92a4bbab2b2f581972cadcfa2deb961c01c737c367f1cef4b691e969559e6371bb54ddbfcef78b9ab6930f40ee79b11a9a3bab5ae0f2ffcca34c47963b264075b953c30d0150c94a731ced7fbfbfa0556c03b08a88786fdea320fe319b628c5021c8a88690103072db67001c5481364c8c08829a264b1c1c9ac4c492c0bc5124058e9258ba6af8edd71b22c82b8588a135e9d91c5ebeb972f6fad95ec3858108de0d3996441c4054b5d50b9610cfa848932342c8ce044d1b26035ab0373c60757181c7c180a134410511813bcc244e941284cd21108588185b102041038620af365070854390ab3050f548ec28c910304a8307d354b61352bca8f1850641a2f917ed26071430f4d727d897403098c18d1e58627696e90f2c31d01ca4285e3acfc7047b63280b250e1b828595c2e233680968881c2868d0fc9880d20d7123150d8b00145cc754ea65259a5d539314c9a74680919c215910255a69794e9ced6789d3dafabf1e6161d949ca0256488a6248a2459e4eebcc65acf7377eebc1a8bf20e47cf6f4620ed27b40ca3e75c8327c8cf35ed1c38c2f039e49c07f57cc2c42c91f97bcdfcbdb79f832a79ec180338bf01e3faf5eb9cedbae92fd3df8ec9ce42f2d0eb5507c943c306d3abebb5c7f8f0372cf3d7c72ac46da1d650c8e4c23485a92564c846054a4b0317326446085ad587cc2f925a4f524ebd86d6061b803588aeae6b0056a126e13a05ebab48f63ce5637dd5787b69bf28cf34fcfb824638662eb0163f4ed88b0bc7d48c70acae26e1067dcd8bf2b417b9ba391c5bcc5fe75ef5f51de34290abd39ee1f811c9f56738ba9099b40488574bc8900d8a0fad27d387cc145cd17a9272ffa8e4b1632160c1b9b763134657d72f1ef353afc92e6174c6955e082687b94e5dbde3f9756aa4773abfde31a963fbeb0d46eab87fc3f1eb2ffee6e6e2efd85ab080f29b145232749e72947b9ed2209c4abdea6eead407369816af7b6037b578bd03fbcced588bb7c77419b09d5abcd733a516efd2bd1e02963aa4c2827389b1c1b9c46c6564e1b632b2282063b744327669e7c4d22b9808902035408310960e175abcb5928ab7d60f80c3ddd039af013318c4aa5a5ce13a0e715ab4dfa7815fd6b44e2a2053df52b9e67a0d28e50afc361c9c3983d8e7401c15c7398fc6a07dae3b2e03f33b9f5e0fc63d1e8a0b551d289f66dbd488641a9e401dd72fa13bb9a32345b9430089a50e96ccb1ae52b1b20b6bae098ec894295b1954a8e08cd475b5495bd21fab4d8b99579f4b9e04a84fd8e80e4a1ded46fc0cfd33f45b2d896cadcd42ae660244965afc2f43418380b250e1a86099b2f9b724872046bea68750648ae937edf480861afc1cbda0a14dca921d9c704438224b2ce162441811652b632b830922b62a990a9d9146738a06e40fa9c3c65b0f2275c8ef495e328738e21979b528640119968c028f164340ca4b4b34bcd1ac07c92fa985eb47ac85ebabedb14e85c821189169add685fe9ac81ab6529cf81a560a2583415ab4aedc2f5d79cb001089c37a0d7fc81ceb1c9c208ffe35ab2e843e43e41fd631ce4ed65a4b438bd67f380d3fc21fd4696302280b150e0cb7440c1436c2d8b8a19ebd06fbdc504f79f69b94d7f80cf73e2a2160e11a8950074dc941720688025d59af01adb5b65abfd5390faad6bab5d67a2a64512c99456dcedfe7f54bf6f10305f6a02b6b9d26e5ead662c0ba0fa9a3bf71fe7c9664787f41ef258ebd55b060fbe284c8921e63230c14ce35440c60c6bc9a7208ba612c81a523de709c4f6593f9d8f5681fcdc3d2116f388e6c1a6dbae9e9e441f274109d403c6da78e01c993e94aeae09eba0fa9a37ef3279341bd83b2b0837c64ce03e48a3a8bae288bf53d2da8d32b8aa3e4116f3fe69f8eb73272ff985fdab6211199f66811ca8f1e48799447d80c182eef4aac0d883c666fcf9e045269d2a449d3aa2d64978b490d6d5a3b22fbc8829247dcdd587032862f498a94c6a83f5e8620f535ef3a07727da401871ba8f116a494524a432099b7822c3d0c643e760e879e0aa31602b93ee2e93ed7418c7f8621902c6442bf7012a79b2a2ddc5c7aee398f6ea9bc0b8b741de6b6ceafd7aef39f1a5a5df893726f0b81648e352628cfc0142acca173da85120cdbb9e7cdeadd9c5e89d0ac67f7a4938657c88bb195d01b88b73072b0bc9e59adb5f3a66a5eb34c63d54bfdc3517553de58a73818cf6ffab8f1d3592de668517a074359ceb00653a6dead754dd7a0647052aee44803f5ea3fd64720c058c38fd22509928451c88fb2a6907f86e38f9febd47f6ca5610e5feb877aed1bfe50ff09c71f6f4306d4fc0ca524806cd51426fee9429e4caf610ea96d812438e2f086d9e29c7399b3b9901a5a5bbb0c41ea03c97cd47c03f4997460f3bb85456a70b58a081992a64a165d80310619695a9bffd4d0dac29fceb9300492b9740b32d9864cd7d150d0cb08111b5a45649ad6ab466b0b73d8fc670b2f38fe781b6e60e4f0b57e36e79c8639e0d60fa62be943648c888c1f2e63ba94f1a4f5f3a47d0ba9cb1b1287d21e8e887ecc4743eea4fd907d74169625260fec4157ed1d8893701044a6432d8a7d5d903af0b7bf30a50ecebb8af1dc4490a7cdb6853917b528851aa1c92315a2453b45429489cc69bf61ca54c85f680474deee0215ea9d9132f9f6a644dfde4e2813a9c33bf0463b42f57dde8114a8574d5f1f6c91c7a654322dea1d3a4a399dfab0348221db4969b13da66971a38ad3b7d6a12cd6b7731ca5aea72727a7a69cf4a8348d1656410b99220800000100c314000028100c08c5429148200fe464df0314000b85903a72549bcb836910a3200a32c0184208008000000800801087aaa60800c804c07847778a62af42045c3eefb1395633e29e034b5cdf2546325fbc9c514f576782761573019d19b546730653acc2cc978365d94273680b7ab0d6ba402a841a45626b2a7826cc0148433ad8c14b87bdb74671d85c2fc8e1cd5df4eff07fc2f3af20fe69ede617637829b6f1ac2acea81d7b731e23d615868225d8d30e4a2fd73a9ee00fdb3bb0379f2c623787d6ff0e60bde4e2429651c41054f1b8e5eb177d93487f1a85c27fffeb02c5ee47001de08d4d4e3da1a43b78d0cd36823dfcfd63ab1559e12971e51e16ad1d9aac40d1b8a1d5c36535fd152cdc1dd743a6d1e713dd022c657f81aa1b1ddbb6ffcae518ad2be036bceaf2921aa2705002f800c7774a43b0ce527d08b6496bed3e58b2761805597beb0c54416617d55f1b56af38d66223fbd0a748bfc1abad4d37eb1cb6c53d2572046012dbc8ad1bb71d254c3a3cb7c2e3dafb5f5a980f5c0bcf895fafada1b01c51473e5f878fa12557bf4d03819c452e6f12e2930139c8d49e428e443fa2428e924dc486bc24fa881878a5eb7e4cac4db4c33e138c559a01664d15c9de976c24c17b5706157cccb889fe8b74a68fdc3dc5d1e105186a265886585229a734336ddc1636a4af7ccaaca6ddc215b168432cb21f5cc0beff97ed10668377117741ea4e0f3bd6094655b7986652629084ecb289add5684280d798e06fe7c76f2dd87b0b80c6d881dede7223cf0df9aabd8beb883623568337f4eabd45978e1e8874268ec27d758f2532d17ba33b412f940e2c3aa0f4ddacc85d4d89299fdb772c99897e86f4d6737bd32d3df748eec647ee3edc273419315e634e5a6c633eb08a285b4d240cd05aab26cd03c1faa733e8e2ddf046250cc643a48e2f4b14500262a0095c55191d8b7e95851ca50daefed250b591c12e1f27cea70f3844a68eaa475d344ce97aef58357f5a422136c8d1fe0da71686bef4fecd995dd063a9faef2bda19214a5b53976c43765fec464fd51921955d1cb6be50b676617246b8478ca199345ed4fe1a90006ac71abdd7e67b3b1780ddfa7087d73d18fc125f72379742ea53aa3087501ed05505f77d03e141d17fdca58a194ae6d0c3040397839e50c2346a68db07d05c2dfa9d7f84c1cf3973f67a0f7a382238e01e438756988ad898382dec85da89415f9d93bad46179f09ec8ef6e037a8ac7145f753d6528ebfe58bb482feed01572e5f6413e4f7eb147d9e7570bc205523acbbe61a3cb0387a5b206768dc06055259084c297f7d579bb1dc111afb5defbbf1d15ee906a5e81033acca506cb4a6e9468ed0563273e2d88d221c33e56bdb1c7160e48a876ce33926373eaf2aa82588be50b23e6366c5704446ae843ca862cdfec095680aab539b47e3f445bdf47b92032a3487f5b76d1e2202c3282bc794d2f216d7b16bf4593d654868899018f277eae3f8faba52e7c31aeaae28fdd02f09d4032d3c3b7b30e9411460b3cab5d78d182d63484b6cb23b69315305a9d9a496b9899c4defdcca32ab49c06efa833c6e110e30e0e8acc1ba2ea4561595cef8a4bb8bc6a5520ef01a4ff440c6bfd79a6d175045316452ce210924922315e8d66b83a70966a8f2962788b7730a48439c0c57b50a3163d96adcaf00e8c58928bf5e783eee9f7b9533683224120d8c6fbce093b5cf4e17bd49cd8ec0dc92480420e5ea6fa84c455798b19cccaf848dce911cba782654134648acd0024ba76416ea60957934c9b2586926a42e08bcc6a3915093a968ed2eaf9561160d5b5a798c41377d03a62ebd9ea92c56e65eaa1e918bb67ad93c4d8c0b7e91209736a37da56e81c540a097037024cb036586efa74a4262ca2d657bba891bea062f9026b07ffcb72aaee241b57c1d563027cdfcda26064625d9adcb55825ca7cd8ab0de4595c62f72e236d5595f588094b439dd8b73177fafc8b8518787ffe52df8a56d030a1819e651fcdea99b504db92a225d1202c6c09f9a13c11231a443ff9b99efa069efd92979f4f702dc291b11f316fdc25965832d7605c31193a2947ff72dc8dd500b70802f66daea38bda1eb816f1d2eb0a28fc2411f77a7d5445cd181120e65663e9ec3d12b9b088f9a365f215c0ced12e609c478f0f8bd0fa58d2f5dfecff35d7e121921b90809b93198aa87ba951c80be1f90036629d5b15c67cebf3d496f120e9df73c60bb3b831906e7825194d91319d9ba2b65720eec3e7e3292168157621f2796903e8cb0cd5ad2b5f85f5a1fdcaa42772ee681b7c3a0209ed17218475fa3928b0401e2bad58841677bc944569126e3f1d148abec182d675042ec67a6f1be04dc4716bf184b398c903621717c280132166226e5f549901c16d963e80b4d295543bcb52fbd3e57f548ddfa59c460ddc5a1efce7908f3af2647087f57258507ea2661a66c937bb28509516795888da0695c34e116d433e305ec12cf9d6912e881c2f8a76b1e2e4ecf4742f3118656312552ce3da13a8f37b4e846503f51520ce9d76b10f2419ee8d88b5e4715bad2ddaf1ad1ae58436353cf49b1a719577186b4bd0c48403564039398b9bd6054b8bf13732349794951afcc2adc2265cc215a8e7c17a2516c9a325940469611fc5ea0aed01c03d8d035ddf7fde924dd6b32b8866256f35fca8886f5a4153c445366f61c0bb452a2ff470b0c4f1ed6dd9bd229998be2910743241261156fbb9feda06160c6201a02782ca826232e4f799922bec0f2794a16588fa930952dc4b3cde1ac6a174df06ff9eaf4e46c7caa56040a429ce1a79f15828970cf01a5c806fd636ead0e9df1953890bde4c8fa4639c05db3e0fc9bc0078c67e52683aba9d5528ec84864fbf4e42879d1ac22c28f0977d2c5cc565455db7c7a7fb5160ce50a67a58ad1e6ef4eae88de23df15a9043b9b5297179c238abd060b40798bca8731db0557b35b51c8304a9d7fb63017622a05e2b0ef967f29d1dda46d5b740d2b9f72b4b4f3ec7829eef860d9217b54045dce60b23d4bc8921e5f33caf44bbbe4c05b55fd24d3c00a7f2fff1bfc02e6c2ccd077a2cd8fffbdf1d54245da179e3a1269fee5b49c77123fea85bba483f55a507b8d50f512c4566c2787a59514b02af6c508fcb0082fed5dd2a26b356ba7ef692c9dd18b6e59500b7524c2cb0722b25d082593da662e301b0d6c39769bfaea7f3ab1244e95c9fce51f95c75b11e383cc210e55194832dc2c4b5bc9a977ff0e205beccea0a7bb4ebd82d1bc3a9fd819da5cd48f959ce2c7298ca44bfa02023e4de912c2ba61389de414c837c8ce7bd587017ae64da958b875084bb77c306b8a0c8520dfddce7e072cc0eed5e62364336c02a841efe26f0bfbc79ca321c8eb8ac8935678c599cc85c898673ca03fa93d052917a94364561bd32d3cefd43de8c5656b538d2e151e9bf018f170ebf7f2a8b5a31a79bf049a3fbc9b00e45dce6d81b8d9b4f45e4053b6c741a8a3e1bb364ec15b7718d488fc3c59449992aaf97a1e24d108166cc200a653607ba74325e1b8f0ec9cb25a87c15b4188aa8fd4673e0d68c56067ed2ea874065176af3ed165484b2fc8b0d2f78c80d7aba42ef9d66e3619d3da9d10bb6562604d5adc509b5dad6be2c0205cc915c8f099118c4d0b0bdec8dfda7d266da81ae89f8a6eb30c3e515b8bd846d985c463fad024185eef147a81aaa23b4b761955b027711d49655df8854ec2ccb62206a45f8763634ff0374b5e02737791f02bb47edfb037296d897de719e91d50b92b27ec7d44debbb2776cef3d762408c1466ccdd991d906cb0586f0947fab9987aba1ed5b7beb899f20401f53a79c084d4abeacd3777a60c137220248aeec16e8db96666627e3c4f1b8a5edaaac1535925a8b17ab9595336c800c368f078dd7c497526858b2f330c953a18d1f199550d5b13b709f66255c42c90748c4d884dd8934a82d7c832674990e2c530523a1d9b70f7dad0da3190be9e8486735baab7af0c6a39cbf1f98f67fecfd9fcd81dc4d9d396b7d5862fb0effae3052bb49f27c3c06e46b2bb92977d4be43a32d513c89ef0644447d1697a3ead73d57eaba307b29d884289744de4937cb65864650997a207f7ac5d11c3553e3ca3005e683cb74e79be7b787c31d6bc2bc6fe1025d57cb72623819ae8eca69232971d645c381841b72de279ee4e7bb7f543841c264b85501bc492424bf1ab2184b7d2f4fe1d48450bac1e91512e1e6e117069f66298d3debb42b8999a261bc135446fa74d27219a5fbe8f45696577c87f6f7096c059546712b1fe7156b03d4428cb242d2b56290caccd8927aa18ab3f7db81bd0a6efc765266b76b4090cb6420f68aab7c5db9a54d8c326602dc6a0c27aa7689e159a148f494829b82bd2ae450757536e95c89b6b4da68e897fa20d4e737aa559493b19ad5675a451d27c785f034d5f3151cba0305840c72ea9cf0c6c3369a9abc998f035696f40605549c942c8c26df2591d8eeaf429fffe619f4f9afb78a10e57cf8e12b4298b459565b6f2ab3f7d5c13cb07ca53ebecf431881471fd49b0a6e0371c7266f4bd1529b546855c5406713d2373ae3a294c82ec1034012c956c95889f36afe71b77ff65bb7c0b05071e5f398d55c13537015f0b2f2f4d3c72bdb337addd708a41eaed5b626d09b49ce479e5be7676bd72ecbaf154a11d448ce1571091657c91cf73dd66c58160b61510851c17d90a9ccd9ffc435888c1f364dba51b11f65404ec8bbcdc945cf7b3d3ca1e758a130681879002bd0332d1149fd31bc9d1f2611754cc59d788b2b4bb40215abd0ca033e579292ea907f6b0f30dcc8540fd0ac55475698fe9fc28286208ab0d0fa1bbf09493889ee0ab19d4f7902815334182359be35cce0ba28918a8dba75202a470349f3cf4a8d00b9b765ec78bbca9679179bc57008ad28b77119b8ae8f0889d582318fa6ac6df0924af10399ca8f6c8047a88b651e3892d22d76e0166603796fd69522a3cb88c59ce36312830d16a88e35a43aae866d58d4c78b4c6f116de4fde286a8d9f2d8eef7d70cf9b28633e3760f046d331d15cb4aaaa766d4ee566fd6e0b1c15b29a870f6c335b3de247e267cd5020fb3c365dab1309af01107d8d4d69ef6824ef12d4b3bc5aac83bef9c636a27113e5bd09531089f5e514c09cbe9d80f8544dcf7bb2fa0f4f65548c2a827812dd36c1f49f2a85ba0337b1e278c26358b274335a3f9e632a2f75accb2bff78f45d8f3c142d34746abde7fb592668e568db0224aa35522c4fe6291670f0e0c7ec1b4e829bda17edc6cca97df589ff45b0aea3e9a6f70bd088d6c0959741423f87f3ec210c7fb93f79e393337d8e59f68ac40b652ef684322aeaea11affcc733676310be2860d8aa0183562eb00b198ec628ecca18edc841e533fa1a31b7a98dae68d094a3396b319997e289ef389d09c251f23296332eb3750fbe3c0bc9ca3b30c10aac1af8a89140feb4560004e758844d71fc26693e5020a5d35da4648213283be0862223c3bacca945e27a0dbb90939ce7d91d5e2ce1e6d2097a28c9a3237d32580e3183187961661ff6771d74aecd538be3c0891d26f0bbd9693dabdb1ce18135f2d7db42833d7a390166c48968397abf9c058ce251b279ee9ffc4c7709ccb077c0d21d4c915ad87c9143300c3c36b277eac6c2929d903beb9d1ca6251aeee830fed76d63844d7b73805b8846ddc7be2d377dd8883064528f0d05219905a70b370113a01227cc8fd4463360be2c62f5093bd1f7881d51fa7b71c69fb1bb1f6ebf00504dd41594d168ee702d81b9c6441d31f97d587ed7abc3b70c382eda4e9d3f64e86010196b8613090d3ea64e7036fb9ab876d5390daada4c74c0dc4fe89e2ec0735d3cedc3754d7b922a6d40410e51a47022c6477f036564555ba430978216ea1681fcc3ea06efa7d32ce98ab73f4823771e2c9f022cc84143aa1bc3913f9b1867c4fd3a27c6eb1d9290cfd457907655d44addcd3ad2726dd351e98a0c2bc831745e3ddff79b9d228ad638ce92273290bdaf47eb54b2d457c6df2226981c5b41cddb291e14d48058de736720fa7151ea79a6bbdb90f3779159bbd577b011680c7a1fe3db4fb5bbcb2ca2b7008ea0d1c22970fd6bae591564070006488eb5cd0bdd12f3a477125293a1fc7515329b1c1be54f53999e874f522b4633c6276e5d27e02bcad0b75c2f4bca4529a44b8ad2231b608b280bb7d9d1771d1baffedc9dcd906edaa43dbf43bac2652cc1c78586e520da1574b3161e043e755680380a511748d8d6c7078d2cc0bb699d6b2ed7e2e4d6a2508536462894188f06f570c60f182a92882b643350c22b85eae96ec9cf1300cf7acb00e1aa2e29a3872975be0ad3f104a126e5d37e640e53bfe7aaae15e74393cbd9984c99b293e9ea4636423e811dcbc585e6a6be97db4f9e187887568ebe04ba8e47375044fe5fac39b7446dbb1b0b196530896fc00c0b012fdbe40d13e5f2172b2dc3af6cfbdd62acd0b13174e13cc126ca5b44c341cc43d9ffda0d0cafd4b8a54ca125724f653e4864d51a797b63037f48c261745311bf8d27c410b60228c13afb6c184965469f25ce033550bbb03ce70d5cf28a2f219f7a30ad2cd72ac3ee52dc9b56a0fb3e5d2bcd8120c1260649621968891b1f1de6a626e14d7bb2f8389faa8a9e97838cc2a67c23bdeb028f6862f1fde349915ac1e69d419e5740f5971f559a4be3a2da91338831fa8dda71568e160f06021dd75dcc772b6802c70aaa5588087ff01d5795baedace066395bf32e13dc7477559200d799c4c717f28a21b82945a1a4c4a077e89a149b94d891d2a377a66d6eeb3aa92dc1ecb576ed1241a96814a46da8ccafbcb081b5736d4e90a666c3368ba6792bef17b6e02e5840dafbbaef7df9df9d84fd950d4f0ab930e0d735d93dbc923f4f19341e447b2ce22fb27ceaeccc67dd6faba4550c78b61925cd7d2fd789e7e41fe6bdc7e451032a442f547a4e64ff4dcea090dfbe5eb2ed04f5427df0d8808ae5bb2e9fa79626f4e2c2a6ec2485e54f251424747308560e11a24343e02eff07ec4961221f799614b8713908596d8a400bf91e5d741db91bf1890c3856a7bba7876e1f6d3501850eaf899c0eb76b50fa51b65876989b8634215a663cc54439d5d27a4999d4fe8270a00a2b30046aec809dddd8368885324c129f3a3ecb8cec18db0f046de988643a34a533b52b5dd140b9df3897c8c0aea401f30ff0892df608b26c5e7eb3b17913026dbfe757a3e7fd88bef645700ce3947c6868e369e8e60eeaf4e746c6e3f409cd6fbb0beb404188aaa6f4c5eae5e8ff89312f6a7d8063c3e008fc34cd74f97e52431d42857dc5f7bf86e214f78389e2464658786b5889d33c91ab278d2add0122ec450ce51ec529e307fb46aa7f96e98f925119ef3bc7d836346338d18bf4859e9d67f907748675be65321a46f3312877a34ffeb3ae7500429de8bc423ea758a55279402155d106a446566f0d684d50b77cc97b4034ecfd11a059e4e91e5cd40a7a78276330ead47a3a32c7886b4e3ab94d2978ca64adde5453ebd46df2f766582a6ae39377da521439479d4e70f03bb70b101a21f8eaa988843762525236d4212d1d31785f3b7f16911f897a0b0d6c4acb63fe03feffdfbe821c0d908bdb83813558b9db6253abec82205adcfe71e401e0897f30f3fd192e534f26736359ab24a959baaee44c446eca96a1729e4b900624533c710c1a95314fb106f15729dd62b74b7345dc242ac46ad0114bff663fc011cc2c16dd093413758ad1246c435c796d434123d5798baff894a6c60626b60750c505840776e32000f44d18748ec3b38dc2a4ef87e1ee4f130e2768b38d45eee75eee9d7faa68889bf22295914c9b43eb80ca335ef4a09e841cb00b2ac34573b342d4722eaf837091d6b4220e1263290f006c2500534ca478a29504aecda8bc7e1090fd63d81a9815af311f338f49bffae5f206385e2bd2ab9d6ccea77fb1b1263ecb428dda194dd756b5fb13ce1ae19532d41669cca28711eb44c86718bfae91a382a1e8ca4e1e502e8686533c04aa4734cc189df5c12594c96eee18d21706858c350a3129a873f7c936755e8ab373a35d90b2ef271673359fba688050318a9644a66737e2b66603097e4005e783656d71eab1b5c79c7dd21453cecaf9e417534333e3c4953dec02c44da472495a0086e29a017e0c5fbdfaee1948d5e4ead29c78c52eaf14fafdf09ec09c7a53a6a8773cae0be90d2b75c3be34dc3c36b6df59dc148c0163f3480746fa027a32c953d04996d1c8beaa2fed6c835c9a56675d1f2a24e731d8cdfc809f20744cc0c56fb7e0139769bd057ae106d0487954bbe51579cc42b303df1f07e42ca58a41714cac5d669e43457c7c387d3fc83975088198b395fd3b4965c42c7b0d20e2d5b231d03ac30961d6b9fa1d12e73b7078799f8412ca3021b4045e3a6b34c976b281e327155f67e5d76489d4243eba0150d3fc9af300926debb7d394b6b121adcbc2a2351465ad06b497059430d46f970d0475cf3c8d605c16094316cdb2c6f7f6a6141f4d5ae01ea3f476502bb9fa2bc04fef37625d89614c480a3732cb6643b984549bf623d33b5ba9fd50a77e8f86edbbc83316810b0ce86f4cd2170aeb9d611ec3d8c718a83c7a20b79dde15dadb7530ded64d4b5dcf38fce242582329f9d6b2d70e7f78add99abfe700b3c0741d6ccd0cb5a8a5e913724fc3a89ba16547d2b3ee0c48e36c1a5dda14e740b8955e03debb344bfccb25c1888524de63c6804b546c419fecb95ea41ae7324275ace92a99695afd356abf5c32d432ae104654a61ceae1cb409c5ec0c6e13a513ea6e67de8101046365b749c25d29e4fb1e193c224dc03ec48d9aad7d9687478395062766f1964e7f75243c98db05269174f145149f816113382d8bf337d028b2ed03b98449e48f1c28f3dd3669d9c17a8a27fcd18d28abdbd3a5057f054a8746ee9fa172a9467a1e7941aefa9f38790b2878f2b85c4ac240b06d9019761ce37d8782db30fc6a6ed3094223a3b96efd290ccd85702a4ecb8dfe069aeee384281f705c1a1710fe9060439b05df3c3f7673f38b24fbb9cccb87ad2e5dc84a75dc77f2bb03a4bb700ffa82a017d09b48e6e1373015469c09357e0894aa22d3a0eb5ecfb078acb222d004e8e1b3f46652b419bedf149815b8cd143402f70220a76046c1c3cdfc8b28ed911a6ce754c718c20fed989007d07389648f2e4f85aad282f0ec26cfd5afdd8478320cdea21884a44e618d514eded2d18e5f51b7c7cbbbeac558751ea844d937d7f1c7664dd10408aa2b23bf87dc57f8f4cb64f04bb2108e6899ef2f7b2e449f3e429669dbd69c5ff705ec8d616781a512a602469f1880c6ac596237d602ce35fdc4d9021269a97fb8074c890fd117dbc587d5ac360906cc07f799d3f17abdd6496cc8a9ebaf5b8c2231097ceef6409e63f2a25cbe306f79b00ea9bab38bfaa9bae97b2a14f6a50fb2156c9331c6dfd2950524d1c0108dcf44aba1002ea6a1ea530eab216c731131b482f52ea75415fe8eabf4f11ada7036dd8081523a17b6010b5ea8ef0795bddeac1e7f42d710371f00d881eecc0d7e8e0900f02927764ca33070ebd12a841d6d3495b380a2fbc346423cc4b11a2fa1021bf567c3a8e3e9ac87f02cc16098bea6ede1074c527dadc29a80ed57ce3eb684659039462ceef603a9ec1dfcbc26832f5e3a27c3b7b8e02b297e8881d9a528a02d01fed9b3c1f8792a5f0570c8e261a5a29713bda9bc0fa4c9b5af332d0bc0988a883f9f1dd4b1e0238b7aa34aaee15f23a90849e7be6af8919888128abd00afa80e839852b88bfa0298f878e0a62a39af6140ea1cda4d37c8baabaf07498239a04deeeb59b8b7f8e43fac96c7e3e11ac7d6fb613d26643189e8c068cba9ac075ffbfd3ffefa4c8f26eb34bc914384b07aba06eb957a21a02402ec0b4ca122d3d00de6c16bd64b04d98ada1ca2c71bf3fda34c0cb603558337619a531f4e786224777da08fe0e54e083ea0f6a48c019fcce2edc4732b16aa203c2dc77eb7ff6b7047b524dd9bdb88b01fab841b4c3cad0ce22e7c8870d31bce93967f09583e635de14fbb8a10fe95aa6101dbc5c4c3b1a7a2722cee3b0e94bdf39ee8dd9608e6e22e92c6e2262907f72f0a5a2b7af8908ea9aa924e41d13cbba7cf1ed1841aec16051a2faed66cf96713f9b05e502dcc5722485ac11dbc3889a08112c5f6b8e72dd57905ecf8891d98ff4fa615b4fca0fe93f56a21e7831d0a3eb363d5ea20b2120942b434bf4c3586b3924d375442c59411d8f91790a16ba8f2bf8322b8631628b3cb201b08dc02eb2f54fc6721cb2beb47ff5f62d01ad019177dcf94bde373d58991cad74b0b37db45e97f03765eb7f06e8641305519801e46dd422aff8c65dd78ce8dc2ae80552e658876613ae24e1e082943fc6ec1f53ebf60bf8acde3e5f9ccba39f2e443c190286bb4b526e5fc38c69be2abd470f15e3505e9f9446dab80712ef019abbfdc16a3d5b818225313471c0b45314fd9706126bc85c3bae0b9f7aecfc80c7ab898fb4456797e9a748e94dd08ceff58f18441e8bd41df47a7022239ffacad6a79325f9421d08f4c81c621fdee3bd9185c78f1768aae42f9d6e74c5d4b33fe454a754ccbd5feae7efb687004e569d79af6cc538eda04341dcdc4c140e22ac17b5270cd37b712b6195f3c0c6c299984e78aaf3be5d8dbd785e18b0b58de4ec46038d545227615983a5ba217ab7a0d400b1624d9fa254b7d8e092f06646c9ea557de72b8dcecda8267603759dc58a7ee097566048cfa4580f790b0e9c2a8de1fc05b48ffaa20600a10388a77d44b4829569202fbe10e6e54dc0d4c39642b6b0f3253ef7dc764c896d897a0ac5ce077d7f2aecb22416ec44e3fcae0cf99f77ecf9e3aa85eb7a50a1bc5a279c40a45cd0732a99fa967fc870cde5cd9865a89362bd5af786349e737b70fe5c54f52bfdf90f829a78072007880b2df682906e611ddbab939104299c0bb78c182642f8909a93e58d3d56002a9242259e0a06a674af7854203f2ff8c55b8f3c911a140630ba6832e7ced3799fa6e2e4f7cb6b2efc26327e028aeb52865b8328bf2745145bd868b7b5fe7bb1c03bda037be8b13faaa9c9da5db6fcf055e7f0fd59ddf66006a1e2b33a815bfc0d1ee7df62545f359ae0a07e001ac56b59a019674b2e0edaf2567a35abb0b40c612f65c21af620c1a8783a09cda88c01c908f688c893cc0e90f70bafdb3a80c7dbfcc574e0a0eefa05637be9d9cfc8856527a829b0207d17ad486c881e0c8074e8e807734523d403e0c4e7dc0a0bb3eb84aaf01df34d2c00069a8d3646224a7f4bce96804ca74d02069812c538bc4772b01b63d1895fb7e7a0771ca22e6efa73d2516251da333310f095608b9b9c9a0f8d8b808268e3d710218289a1293df318ba543521561fb57ccd98eef73e7565c362319d07b57a5c67df699719a4ab70e51235c18d59dcc014df08b1d75653369dc16b7102f4cb534fa828efaef19eec0df9f10389cbb233c06631ff5eb4e25547b21bc5d25e09aefc978e98a5af91dd5a748431d774b0a1d38423d271da3a9e1731f858cfbb6286c152241a667b723024f135d2a9436a035c5633b420f38175f6ecffe8ad869a56c14c78f89712fd35c5357f71bfbdac3104642b5a62f134e7fc8e9f047dd925b4d0e9a709172ca6cd3c344dccf0b5d17f7f882e2b5c81c570bf855cda92d1d97bfe2f103ee264332660fa57807a58f9ab6530c7b6b984bbc3a7daacce621642513e85f37dbcd421e6adf5ae764b9b64c2fdc47187192c68f16f615ca7edf987d117739dc7920de9d2b7f917034b37444f488161133bcb609a6b586cb32d41817cb502f42c96ef625ef6ba7045cb4c8d1c10f8cee86805e63056848534e04b01889d5e7614c1c1d87f9194044906db2bbbcc96d88cbcac930bb02a56de121a107b51eb67c86d66db7b59879ed79faf887449a34573f4bd38525acd4a355852e0e6328aeff41343a7bd285a9515cccf7d27636e3a0f0e755eee1bf5d4c8a716e9212a7d36bd7250d6255d7868f75ceb2510d8a36a14d2abf7abc0bcf2e33585cc2bff3784b890f78fea584dbd84d886ceb93d8a1cf6829147f22daa19d85d5a006e01753f484a99461619a567f1729f286b51e8d3d4205be768d22ca07375905853e5cb8c5a4b3532625f0bb4a1563df02ad233fe0f1e3e88a791ec4e0c07e1d621a9f052c963aa6dde02df6722649340088c26c83ebeb53c49473d0822443767526aa422afce8852fac6080dd90ed81b0290d3f1d8b8e141ac258c26dc469736855636f63e0f251fa4184678bb039ce0aaf5247a5aec47d93d17344b0d701a1168df0926e7551c847111ea00f37ec79d0ab674b5c01a2762198df803b0998561ff956a8114ddeac273b53c808c5774c632e26a1344608e71cd720d901db9334eb6e36e4c27773d9cd0a40de072cf3bd71afef9b1bff0d01737a8dab3ef9dc8cfc89956dd50e5eea663b7e266ff042b22ce2add235ea5628469ad7b50eb988cf020d80fddf977df93f1031f74994cdb4d88c3310197406d0fc1379e3b91421596465040bd500e275b5ef1f77b4bb105afc2c4684e0c6d6abf6227f48e8057c184bf7313933141581e95489902f6f0ea86c643d9ba018febc98694093300f851d8ff52817f69c805c1fe3535bd9ddbbfc400e3c217543950a5c3bc99838e114fd55ea9200b228bac4e9d20d87c15564f00d143d1e00a45954053da15242bfdef7ca2cd69ff60fea8896ff9a4616d6ae84d9408ab8733a87a5e3d642e6bc92e16ef57243a359e83c51d5adf6cf5f51636c9fa8ecac699b2ce78669db8ebe04f43f38bb53adfece5ef666251bd4795bd689716086815ef76c0d8d228343c097efdd4632555de08c69e98e2f91738776ef88d203adf3552b503fd4381385f9f7e7340bc8381da712fb952709199bf570b45069903c77dcb608526e8882724dd1a459affae4d999a1267be00a1f2042ddd899a028be5331860f9b39d001ff509e243d9e636346ad4189c4fd9351d06d4ba128c1c4e91df7f96fd71859cd07c8d378927d0d1040a31184a141722cf8e96d72cadbec660c178944a7106cc3a1b97c824c741f33d137503c1e55575d538db5032717969c7fdcf89d5faa692fe9016f1c7b7477185a06ec6ed03a660b36ec115825628b22f8621fe895c04e36d6f97fb523295d70a2eb1ec2a1c5f8ec2fe9dcad70094b63b890b05bcd173407f092dc8373f25c1420ccde3bc5084c70d5eaf2b2b1995745e4cfec75fa6b882fd09c31e5d36e2a57056efa4d475bc18f007891f83337fd5a9cf262daefb57e56d830562622265b29067fcc42e64867fc71501cfad996841e60f10244b41ee01622fbdbc5615050b9be2b08b4fd7e4f6c06227f720a8d02c410595ffe8cc4aed707f022ecf917284a8d3e871c50cef30328f69c5c526138b14298070215994634fef1cc2213713e90bcda24767b84172513f066652b9074f34a0499a6cd0365769c1a3ff5fa10b3380c970adead64306c51cbfc2d4c44c8c6f2776028b49f11718466c3861e40cfc49a1860067629f36d52fcf1cb222958c8c65269a947067662bf24082adf5d0f3fd9d34938be4e22e1ecb8453df2dfdd0f81c3c52ef28270c1b08feada6f9ec6ddc177b2f24b515021dd74df595bcc35a7aa154bf6e770161d2969a54bee6c3dca42d7cc444372da1542581e840c4c729259e51fb6e753fea441b6c4197cc6d4d57d3b491d68e8e4ad2a8f4d10f99b930a28695aa80c109982d1540289cca45f7267699ac0ccdf594598d370b185a117ac3afa9764d9525b550d12dc6f2143d41137b21f1ed0b31fc430d934ea2a938fdd60ee85c742645f24c6770e3448b59e7344b6a561afc7a76fc1a2efa8da16fd3601b9a6843fc83990156f6746e81836fda5fadef4c52eb54da10792a8e6048592d0b5f7cabe31963ef48b4f0c68b0c4b6a729de7030fc0596904f1b0e25419a225f407768e8253a989fd4306fd71001408f80beb6d3e7bc30755cf9ed10f0a515cb8dac5d4a0939ae50fd5e7fbf71e0fff878bc88e040a46f7c47439f9dc6e9e8430572da2d4efebdbcb59a7df4473e9c6015a84b742c519a8b5b9933a58135dab4d0feea5f0efa413d57737b3dba0de981dfdf23aa51e08db8aa24a7d68ad2caa1283134045f444ce97473271c76572c90aff2b4f10d006149818047cf2a900ef0ef34a6406e1bb9f5e438040a6c27e2ec197c2357c8cec32e231abb48b20407994403688940c4b8d6a0f8470ad695d24aa00c083992857a00d5889d73265894a0307bbc546ba01bf17feb60e2dcf4123a721da5d59f5a47e0d08d949d19d20c5ac7d694b6417916501c89fb0518b0c5ab0be8b3d8f60f15eaadb2a65511273255853cce06e7616db6dc48e79c06e63492d5243690d52ec79a66a0f1ac65d5ea2737b324c0a8a1c72a12768e406d54f8fb5419387a270c6627bab9f3f1cb00774e639333d696ebf6666699a574a283270d17d05d68c8e2eca900eb5e72a7d0e6dbb5a7b23d4c2e99ed7808131b5b04cfa281fc0d38cc5677e55fa3eac13ba2b325f887698d59029138dd3afcec9f950c6578e573973e04e6302a33c7a0a0d10544ea43d5f7eb5b5badbeaf63d4fc2d636e19aecc54ec6a12048226a878b66ff7e35c114c438c0423982df94ada9f80506e3aafa4f90eee5220030d15b746d0ab45b37ed63bda7c0e7241a84a46d79a6c5bbffac788e9119845b83364aabd5e2c1fbb8909f23ffa56001db509ffa3ca57da35c7ccb14d1ad21f89c1735a016407d60518c5b6bbce4e79dadd126ae0f883d80723f9e2e8ccd75990b80529c04ff568059b527b5c82cbb2c14851e7b252464cde9531eddd886ba096c8a904b516ea01f06f80e682234a1071722cffcff7dfafefaa73b2431b31e88271bb7cb93a596e48470ef32d4bfa7486d904fdd7babd89e1756b850ba6318c68f05ef45c6b92ceb47833f2f81950a19ff39c0f1042be5a442ba6fdb738580713b0058618b4685e081a32c8208eed57d50fd0bf271d721eb5a0bec1e753066be82714a0b93aecbb729d0df8ea368741ebec11781c067a6d48821822b370186b2e1e6c20e7b2c4b0e55e7d6e1211a3fd6fa81adfd3a7fd114cee79fd75c7aea2dd2e2aa87632840b2c75d13a4631a87d73c7fed14ebc9f4f604d22c1408ff7d27504fe68e4d0e8860daff1b9cfcecb29a425a63e004b454deceaf5f6e94b49fc775400f1ae021c6b582774c18d280c7cf6732e455798c1f719a3c42e6adf011716a38deb6ca293a57a1d4a51f9412851db92d054f22edb91dfd62e6b52be3a64f92521238d6393565a46cf0fac72f38a3cdc2b60a6ebb3eff89b1b5db544941b46cf64d68ff555c8653cb86ba6753b3e3e87bfcf62aa140be722bc452cec56fc8647d8b01edc7071ba5a04aeb598a2eb5067dc4c375cc81f553a11d586f9bed470db9916d6431708ef4f3f162549b0d8192f36159f20235cad552e822d6f27fb9ca14b1bab94dee7cf81be67aaf913eff0e512b7904afe179466adf109bd74bc0c105251b91557f050b13ed4185b69292a9131b03298da5dfeffdd8be5dda7f65f838d0b6fec298c8c445dfe6421afffc3ab88207974af4deac3c9805243cbe41a85cecba93f8ab6d2ee0a7cf30b091fc739fb7850d190a7289a0c297207f87b159a0561d55400fc6de42bc18db19b0673d45d1fde93d6ad557011883186c0c99f01439e51a213fea6958e784b5d7a89b74a64910c758a38425b9b9e6891c32665313b825909ce7c5cc3498b061edb562b8da00b0d94e3112051023c63848087e41cb9e9d5ef30e6dc1c772c730e92c36a00fb5a76f3184113b3aa680459b4e4ecb6575644e27144f46c93e9c2bb359428dd05fac819963db353defac9ba605f6506ebdadf953895a60f23cf55c2ada02c20c16f4887385383602b23aa16a6b75940d19b404bbd5a2058905cc34b70dadc14519a119bbcabd533f47de6f4756944966a96aac379460aa55231692cbbbba35606d037561a4769f19962d01dd2de5ff4b17dfbc4a04cb20ec7e3ebcb0d8326e0d77e9bfb3ec0b17dec52dc2de60b47c1ec360e0162ddfc82018b50962a7a7efb97cc038347646b2ef453c6af4a9d3b7c9e0df8a5d9018dafeafae65d8b6022d0400ea512bb80f3a3dff9dbddd909c792d289fffd196ffcc3fdc3ed4597e182b1cacc9eeb6de434545066cbdb5fcfdf0b28205170da8032a104199b5451e6a65ea15c6898e681531dd096741dc3546f6fcac4cce11c429574065544d085c2bbc02807993f386b6060ee0b56caafba312fed026f524ceadfc55490d8f41160fb4ed3d702359b320c0ca62944a833e344a8d24079ade725b853b3ac979023504cda981810cfcd6b230540da59783ee0cd4cdd773c16b1aa68f52bccf825fc6bf701c73b73522efe067ae4762dfbaf4b6823c6cd7d522a9b84b597283cfe5ba748c42c1cf9227d8a28d37be1843c54e91e2a7fc5cbce2f689a1b79a4d88898f8afd619ee6883752eee43233b12570db76eb9fb8791bee3ff20c7b5e77fde9c64bd8e8c3146850cf0bb81d74f72ac26303a866811da35388113158a891cc2ed6746a5581aa163ba9c736031e37b54bfb6ff00b424e50102554109567aa16b814cb4ee905800c820287c1512bb241da79cb4f5eb276579a83746181a071f6215b3bcdc90cd057e17fb06e43e19d92ed01df4101d43306b60b4aabaf073e4d10a7844c0d47ccaa06b30fd437488921521de8d354da6d2af9ff5873e4ab190cdc9445e4d629b5bd56d866a7d292256d51804be07f35c336a2a53910aeb12ee3ca68221e00d3909a87a0cf76a55d4140d9c5701eaeb7d05c2475610268207ad0d674f56db3eb62d8e89d800655150174b8a4cf6c747124dc15c9798e5ba0dd62a04e8e67b82f7c950dbf8af6aa9197382704a911aff2e458ab934fd7fda4a5620dc02185e972c93cb2b8a8a8317fdef0a9e7ee03f1babaca7760a941114f43c96ea71bc00f532a6dd63fb497aa8a4421c63eac8ca722f636a27cbbf94ac3a81f000ceb3b97864acd43c2f07fcd2b55c30507b2ef1481f6387c33b5902175de5b8baca2be6f900af631c26ae1c93ad9404691d337188555b4aaf9a70820e53ee9caf1b2f284f62908287bc1abdb7d55357006326a6177890a79642b0a270e94cd629d65e0a1eed0e8d340e901c3d720bdc3a28f01a1876e26db30e0d0323111c7f8b0bfdda65e88a6da16b9af2938582a1a16e9f8eb054a01a50ecf05b2dc5a7619aa6249d865974dfa1f2c69c5524029eb3a05ff382b2573c30077485397975a9e6bfa649a4a8072b7ec42349991b30328d330c6e111aac471c664210ca01ca57db6ceb3b01d9c3196140459b42f70df7bd24d1bac7a753c014e3a407df854e157e11b833c19e613ab7e7da017e16c1ac9bb1df67236ab56c013f48504c3b2dd19931384cc055fddbe83351d074af7da1119538b4332be2ba8406ac94c7cbb2c7e98cb68f08265d17f94244c0dd88c957bc58cecd1313bc1e0f1e3b2361381f1b58920d6618890860db7a321c8724c41d826facc5c56f9705b41d3efda5fbe0f7ca3bdb93099250e495136d2f8211b88202684749f86f3d26820c6c7b0c367a1ca3dbfb31f80986b1c3389b4872593d31a84e6a25b131091974764e01452fb5270637e41bfcc5aaba6632e164e68a2811ec5d17f6b94cd79128a9512b3f2f7dff1f18132dc540b36f1f2ce798daff58ef873e1209e6e2ff9821f26101241e8f47ba58c52875d6ca85dcbd1b72238b540bc0a9c4c41ff33868aa0822c70a8870b08cb5d80914ef160a3378da4c388d1a7335363806bbaf1b0e9cd689327dddfdb327b894b091457748d3dc30e605d63abb2ab27606473b94801791e3d9d0692782546ec582337c490fe09943d3e99e46371c1f788810f32b94acdda5936304a585b3d8ebe64092d1b8f6249989785cf13c6ea70bbd94eba8f281e38bd796c424dbda62fd2f9801690e16b6ccd9b3ff9ae9897b245294ccb71f25effbe4f0256d3f514e532794e792e0fcd5b017a4a02e68b86b0d22a169a1d42b7e20da054ba4f8c4d65fb330f729231de9a258a44895c2f926e4b9b4d16befbb2ff1ae9147cafebbe32523788f504bdc1f95b9f0a5e87423867d9e2e3ee6a44211021d46db7cececa656ddf6338ea0c77719217f192881d7cec3f3a26eef04cf2b1937900bb38f79f45bc95777bbc11636232d8e198a9330be3ec4b4677afd2342b98a22d6285279b49b2f6372f259f84fc38536f3900a4d63351744894b4ae9825da6ef650e1dbde149230482f64abc1250aa1c51c4c304db7323de36c3e6a78de0a5fa496246ecefb657edd048221be03d15baec12e72e34a1727947a66acd961c35368361717ecf9177ea0f15bf4eeb7c60aa1a5baf6e1b4a2445842cc3f8b2078b62df2424e59ac2773075a749661cf085119f8a0d997c8ba1efd3b84aa090b4aeddc42c730500d295c1462e993c7c63b338402843a307e16b2409a6b4bab98505dcd4e53ce1b339ae071f938aacf44853dd9b66c17d5a85e81baead308543ee1e8a1b64badc316b1a0b250d36b5a8c992c45379fda70f3dac5382b687f470b91640b2aebc55cd681f9f24df4b9da3303a61fdb1fbfb1efb93f38193c6d08b48d0946a2a7190202d08b7f65eb7fdd1567da5541540bec7314a250e10a1b2d4b476cccb05764acd82858c857392f532b8eb1de95a82c9df013709342ece1a1371c210791ac5b45b70e0c3f0cd0ba6048e413dc458b593450e32c63edce10ac9ef21ad1b4dc825c1510c4aa3278bea3e8bf7b9893dc9fcdeb4a510b65679f608db85940bad7071b7f83bfcb61d08b211e05b255e8b022ee90812189a69873934cf8e94f469cfc7a7488d51132ab8ae55d90bff0d5f9370ea6dd847c4f2d4d54c8fb6825004dbc32d869a056e9cf3cf3d1e0967579520e3eac734eb2621576dd5ab1ceb9fbaedcb225468fd3df2b473abc7578dcc33786773c1da1ebfe2d2108018ae2f93d957f72016bd2fa30eb64c9b3b9df3046edb284a9e86523040beca619afbfa1fe873c786c0f4d1afbc71928f58fbd1bd3ee2f1ee929a5181c64fc24a351d58b5567b4cfbf0228ec373f0a15834f66191bb7ec622ba406f58ae6ce5637e79d7bc83c632350845dcb8fa14241ea1988f0c41ac20ed17d3ab20e5b721737417532e98891c164717f0ffa66b1f8473f8bb8a6d5d2933cf26b46420f794acdd8e904e22fa4cdb530d8c43be850c85ec143b21c3a6faa21bd41078a8e509e9fa09fc48d5747f37652790bc6f29b60adc9f0faebe7ccc21a69670c58023355c4da98852a2f9ed027db7ca2012bc6021ca6b6fea003423405a3c3330803e7f0debfdd0e7a568ea4e9ae51e930dda296f42ff213df04fd1a5420e567125d4f27dbe5adfc67f4e8544f78a1fc3c78507e5292bb970d1be158484d394607000656bcb5f27a830ff7cc4fdce386ab7ad67147cbc66fd57d56a909c3e850a4adbe8192bd1567310673579364780f5d7da7a17dd2ad247b35580961ee44d288d14f72c84167204f455490aa47a39f955b56be55068365f87bf1307b3fab238801fb59ca41283ad2c16f85ced17dec48590f925ee84396ce2ad70ac400381ca959ff1f4129840a932daab92112788bff75cbcf97abe50844bc4166fbe97b7922617b5dd81a91b9c0a8f46823cb168a75b5eb9aaac37e6260fc40fa3afaefaa5bce19e43d1575e4a0f9d332a29f123aab1a590182d7dc1c5624de05d4508acee1bf9ee36d6a33f3385f347f4a885be7b31e0d4a6bc8249a1641eb43f1c1e6994a2ea3b16eebdec04acde26a27d6aa9d7d0339968e7c8721729d85537e38118519fae8a41c965b23c0b7f5c65a38ef79dbef49cd8f975dc7f87759560ecbcc4026540d90a142e953e571f25927fffde1891b22e3c07c1861cf1f37759230586b499a8f4fe2fa31f5645eedf1841ba6527a492c42d6484b25b7e0c2dc4bea2a70e13459c34274e33def0535ff417a851539ee782ac5db3621073d814cb7acaadf1834c73bb36801f802b4b96acbfa4c5004fe59d2bd32a24576cda0f33b41fdce3ab5eeac4c47c8419b501e22c94532c1899f50743a4adb682ceced311479af705b43f718e08946112c486232f6de6ec16569d1da3ed57174bf973035b52fd4d780b3262a732c1001c35faedf21a1d691354bc3662ee43d9453e5d393070d32b7c62b9f14e0ffb845a6d0e1eb2618d2e1b91d20c36d601620d4c64fff067b9c54891e30824e5f77c1944e8eee66d624d69e5465d667c326a9a8ba9a2a3919ca3bcf9afa0d70300337a9a109c3b77e52e07420b161cb735f8a55dc7c695ac5cb8a2bcea0365560c74f8160b10e879693db13a131f9cb6094a16b4076ba397c4bdcbc011189a6dd73dc7e9483a42ddea5ac11a7b3c0aa9655150eba55a3ba56aba086b51585b9037283c6b65955417cc8a6b514fbce1da452ed2aa0a83fc019c85ce05de6900191589ed772623ad73283a92fbb9b07299c89dca21d452b6537a695a55b286fdfa2e37eb4dda913c7cdc96d8042659533c9707a72ddf0bdd652722739402817887bb96cd8ea77fedf9da12e51aeb58e837b41002d4b7f563ae1f07f355dca70179e36cc41e55f0fde64a823708ae088a69694d4cb5db5f756a26f21b99cd56fa04ed050dde746f06162869244a8605d766074d590e3b5d350f06b68d731be29532bd24979080d78aac31c207fe53614a9e74cb36e5f8b2e5a41bbfbe0783004c77a0a649e1ccdab450f1544688da6ddb016d10a819c2793158997924ecef636c34c36427c29f0648423a59aa0ba9860594c1629f69a5039731c898fda902e17f5ce62e541b56a84c6917cacf72cc67a137ff34927c58ff84b6c8584c68141b6d3fd3a7ab403ecbdd08dc1ca63d9ddb8c2de8e7a9fb6f625140dd123707110a326227ccbc7842bfd5aa9ed027fc155a2ef8da030a6386b3312270f548eefc6c88eb20f5c46663795dd9631dbfe45575d5fa9035af6546b47d679b02036cc01881feaaf032c5b23241b675b41801d8b37be5a8961849b9f56c79e0bda89d700f529926fa3eab15f7351ab8d67a5fae2f6a71630667ced0ad0f9d5a754ef5ec8045c8dc438aefe8c0795557b2db4a93ae0b410281acd793a37b0d66697e15c1ad2f244d537a554bf36d53e160aa8ee9a77eb2053a3ea6ef5d435bcaa77ea8c29e9a373862d17038b3f41b7e342cee7b9e180ce21ee0dbd5838e7e989153353138d77d4903631410ffb91777db7f5b9bebd5d66aec6594aae2ef16fbadf63cd0ed9af01dc57b02ed7f489ab03d5e2eba8caf5d04d511840dce48355be0da1c8c5f65caeb8149a6acb1b59b50a4d8903868729d148e8d4274d7e91cb16b59e82ab10c8c3d714f4f54926c3ac55eb97b72d4ba78b72815187fcebef017dbeeed57b062df85b7df8f28348760806fea24f8c1600b6e5c9ad7e17e32cff29d42cfcaec2457f856ab7f30cd1ae01d321914ab0733a8921566407954b9fba629c0448a6ccf3187fa62750b70c07d89db7702be336f1001b33360acc7a05f2cc374a4f70ad15ff34c52ded921bb5599e5b86ebbb1e41b3117c465946caabcd25666fd1d7edf75c0706e2ab513775f8c8a238c49eb3b0e9c6ae3aba42f60094b7c7cc8ec07834d272dd997f7f44183bab7f6f5178e20479b7216729750a18ebf5677e9a1319e0b0a1c22d9fe55adc259ffaaba8f34f3b131e006b8ffbf47e82d244a6804293a9139147953c8171e7b0be689c35ff633e54ac95e6719ea7ea5cfeaad03f31c63a8dfbe06578c1abc8569ce1f9a00d1ef61d165e0d3e3b347a0ce03d44f4335074d86ad6e6a40f692d9415a01dc76a88ebc9030d41118f33be2ac3146de9345858c0d06ae288f2a7b7ac75eef79d35c84d1a3fd42b5cb7faf16a2a9eab9dcb33059ab867ac513bca348e091af38ee6ed54de90dcac28c86168038b5f599e713240bda2c40402984ace1410fd9c8d4e8fc31570865c574290a98f5768761ed68258807aae184709bfa986db86fa690129e9305a74e884df6c5cd5b45bfdbcca3aa3dea13b9b8e982e1805d21044a969a370889cced618bb32ca38ee18efeab32246355104e21a51ade50290b3206c8cb419c844ea497bff09863dc0adb8f5c3a025dec5e51ea3eec5f3ab824a5955cf590db9a875f9dbb52e735b634d8cfe5f28615474c354f346a0bbc22ab5dae87da94742ea3e7bd28a3aa8f7b0cb9cba91401092aa77b608d3518b2b04a7f1e93e065be6ae931a0e7a6eceb93ea78e57788bfb59d3b2b45ddb286a513fd0a48103ce416d08dd331f771526473435079441ca0a060e40f393b4b3fe77e16b8d6bc1c94b0800756e852bc0ddffa73e8c897dfd3c332ba21c507aed811da3d0be39ff6597a0fe50135a74eddcb7c4f68c863cb1837e4a208af34d561e0d84a7a45fe854b961d0fb1492db9862ac80f5ee5eccac19583283524049dc578b0b8284daec6181e1d4b308b1500584e5ec0504563a1de8fc00ef25a685101547a2aff6ab62886ab5811f4fb00e2005dffd74a21dfbb8debd16188c701514c472d12ca0a93d5e1f575d02cbb6520077f997465f797a2527d4c6dd28f1d1e98419c45d76b78f2af5df5ed4ba5de31127e4f1056f075a72f716d77e2a8d3201b1dacb59d22a61ece1ffd3e3d8e1b87e08fc7890b850fadf1334853acd03b1feb2f824fbc771c050494fee381c7542730b597b5eca41af19d4f9b43c22cee22cb27ed6ae5e0606f772ff849918b7de30b08a57bf21a1b89dcaf66c999507d032610e45d5094d913e842a98144744ccfaca195bcf61b96f9a5b751ca651d0a4af77e835c3e09e0efaf6665efda482573842bfe235b0ea00437d914d93277f0d76c0c97a5fed00b014560b27cbaf94e8e2b343d8869d771c9baaa7963a4f769ec11ae617228b5fedf936d75b7fb9cfda1fd0228a066cddcfde670fef4749f7f3b5ca0133893db5839bb2f69c9cc42d0d9854259c05e179f466ca8f15d9ac52a0401c5d4dbfd1606268457b7af377bdef8524079ed339e267ae1a65478999821828a6fa57108ca3a780a382a55b0558c3136e3bd3fe451731eed7e855d713aab78e9ebec43e6310ea79c7a10ccd187c86d35e9deb96c5adb4826783d21840e3c36048b3f2f96ccef819d125ab0ea24d9807d0535f1c855753cfa7def4a2863a8be3d3d40cac6055abf27b738bdfdba734f729ddb8f894429c05ee3efe25bd1d030a8d5a8e78e0edabb5d667b9d2c5331727cf167c6d5058747388a59844f0157ca85c4730251dfe464324010691bf767e287b21337a01133f1230cfbbac12d43bb40967f3775306b13e6e4fd7097b8694287d46f1a8671fa20cf33d51665f89de15cd6b8d4362574c690ba57187839447c95f69f5120e51e4d95efb0760bfd2c4c8991e1a4a8c0b79d1d614ca444c928a56675ecdaa31aa90931451a4ea464984691d322041bd05ebc8635900f4447809e503a112068034374f2dd364e16a95b85ce610786446efa473d9a0e350be60208cce974a09bb991a6406227c92711c01c1106ceec16c7ef1b218d0285bfb34af34d7b8253083eecc5360a820daa4237e441f071fc464fefd4b27edb0f480ef192ae587fa99a805045574ad5a0dc5149808549b6bb7d73852040dce7cbf0ed5f0721bbdc5824f56bf3875bd756c258d104e1f36f94b1364aa26a2e49ce0a11c1fb9d5e8dcb886abe4f0f9d1f27c0716ed71b1342b3348a599e422f56858ed5a87093683a82fe03022a95e4aad209fcd48eb1059091fa0744853cd044e69ba6c29f460e9f4d8af0a10b7fafb4f5c9c70aaae6f688925618bbc27d09cad29bbd93c37dcf6a3c9ceb309efdd0dd21e25a83645fbbfb2b87a7e93407e31264f89f6af4ed3e4022f4640961cf9d098b56fcb129cf4971dc3b9b8fc70eccca2ef332252979afac760e20af6cf26c6d09b54b42290f17ff4a9eec01dab5aa5d463b5c55573b9c7290ee33b953cc781fe99c81154fbe121e5e66f1750d326827c263ea3a9681e5dc0256d2a52975d400fece0314c8335a32e59980537ba80cf416b433dd6b9df0314bebb17446d0646c629235c5c6393d1b0d157094a1cee84bb1aa393074dbe4f16852fbae8a09db910af7a3bd2adccfeaf013d98587c51c78e96653539b62d4c636398491758c1746ff273716d7e0d78e63aea748bf9a48237a589d9baffe2fb64244480a4014e341c969d8fe6077cff35c998149cea5bd1706c6cfc2de6531ad1aa84ecddc68ae1c37360b95ce355293b8295b9c804f72c761f3b57ef2471fc2e183e90d14afdc70c9ef4e98e0b46643596e995263606c3867f45daee83114e74e50c3149d69fc933c0ed990789aa0b71f54b1c5a01a8afca063e29f4152054595a21e101d395a491ee8c6d9bf1d91eae4913a09d90910ac0513b10b0fc6513e17f96584c7d9cfa6bc86ab1ed366df18e1b4abc30d0efe08f1d3680096bf9b376ff81c43d04dd4cc802601329b3b26b435d2ec04c12fe278c66d20979941b91ab1ac55ace047b55014c0ede4ee29d0d99086c2821556f4a91179d56cd5ec1feaf0054a9fc0ce0af4ec6c917f156a639d91629985b13542d569483751a3cc134e22d614e84dd2049aab3ec31297a79acfdd5b2e77fc3b287baccee5157fcdaa1a2cdd6614f9db583b6abd06e90d50885a23489e7a9857472fefc7a5c475352a3aa08ad5d2d18845d8f0cb2f8859f2d758f0f67af6116888b3c5ade1e2ee78ced3e5f0bfe25940940b99e7bf1e45fb87f843462f3fbb7dfd8eda26c8af81b4b4ac7160a2e7f887edaef3a81912b5cd859c81e11946c72f6ffd0168437131f03a5340e714066e67f66b2ab2f55ed676e84fb27ba9428905ced0a5f7a119ace3a840a9c9af0d903e399ca912505c12cc4f3e6cdaba7e11bbb3135b4b7e36677fd6859830084d8c4b0e74ccd02d03157c021dfd255ff3537e405e3f1d0fe836c2f1cb4deebb392cc02e6d4c05c2dd910d0ffd18e42f674647bf1440f5166266f5d52be6fd9539fc52d487b82459e73b5e0ca251216d76c69e0fb4c4019e9b4a501ccb53158a967955c81eeece059a5b0207fc28eda87de2470627f8af7185a818b9893a24c81f3920a25e196d668589492cc434bde85ba8926c5fcb23c57f3d301937d59731a0a3d2008c28a5324098b2bbd263c0f9a1074fdabe4a85121b72a74d0b1314385c757be1a86b50537998f3efb14999ab9c69afd4fad3badc61eed9499025a69283ac0bb55bdea481d09ad2569bde73c6f6927e76035ee3a7f80c49cde86b0cff1bdd4cd575391b271755e2eb54e6191c58bfec1961029f178aa74497bfdb42aa6e59bc16d58c95073ac95842b62b46fdcbc309d9ee4b424dcaa48bb17c5dc6240e5b6a2ef2c3dba724a79f4ef1490a8797fad94073897f428eb08cc76b86a6bfd465a694e3d7c33fd9efd540e9c72d2ca80743774e246d1c8d47e55be6853f5ae0029283a522f46f93fdac991513ff38b7471c802023738a479ebd9d38e638da04d557dfa8cd4573f8a3b2a13c19e6baf482e8d9bd0885a58cfbe4dc53c107ac456ecd7a087b3bea4276d52a1479441a00505c66377d6fe693682738494b3af989a84b6252287b2d78fccb585b410c90d849cfe568103712239c1704148bbda4b342fe07a46d7b0cec8046a04fa23ff468c009ca41c4565a93b432fe50b46ac56cfc0f4e1840c4894bd3357726ca7c36ac2625d48104b707e126dbac7bd7b49f8958d6af26797c3037fa6c6cc0346872f0c4ee63a8c76faf67cc06f3dc6800e99bd53c3bd80ab522e700c78b0b1e052220427042ab99e8d5920d0ba4a0789afab4ff41b19c0d004ede52fd750e36730b063c54713c06f4eefc3c7a3c396b6910303b55651867664b102a26208e9c012ec964a317dce1fb2c3685af643016533fbd608ac93ef43cd92bd09917b4b99524a018508f708df0842b10b283f09d80a7026ca1f6dd7c69db8c65251523dd29d47d943e3f628b548130a0df5cead0d2e413be2a33efa78247feaf4d5d928234d182baf073e507e2ab859430d7270a60d30747f770d48adeed3292b7b2c958783e3717b685c822653efb304b304b46ecfc9ee45001ea15f7a3cf855de4a51a5b75bca745945c0e3f13649f070910081b74942ebdcd3517785c242d5b059ceb033d592b5e373a64d15b43f643dff7cff1d9fa3faf8fd9ccaebaaeac358551feb78a3c171c1eb5ab8c9056b7596719c4179d75f1d44acf1679051bebfa68b4625dbcfa06d9a8cf125ef96338ceabe6bc7866dba76f917bb5ddd7c0119be7f9bbe84a9fd1d83c12a5f408698c43bcaca2085294354c608856d9763d1b58ed569669943c1e3510f87358ba2acedb185862e1e8623619eb03d98ba6a079d9373b04b3d974a3df34c116997b7b0b33b3fb118f34ef348275baf7cd8fc77cd196936b2f97d2e6a215ca8aed9a51b165073dd125ae01170707890e3f77fb6c7bcf1011097c054780447e22fdf3175d571aa70659bc756783539292635cfd6b8eb8a2fbd6e5df1d928321fc95a167fd398411d2ca9e5c0f11894443c90e190ede01c600237bcdf17ff8d07848448112335ad5a0384f11013edece710a09d1b1e37b5028e54dfd1ce06a3da72ac05b46b57b833f0c6e37df1db78abfdca95c7a0f6d5782ceacfab292f060d77e640abd6f6c531891e69da9176a41db5f66d47234790340fcfded2d89c9ae5ff3411ef91cfdf4d3be397489123acfa987635d4eab837f9cb27106b37d55022c94f2bf2973b41c39648d59be7a979f871b035a1fdcdbd65993bc0cbeadf1851485299b9b977c025008c9ab8b32c97eff29392bf5ddccccc1f9ddbdca4ca42757021703b46f624c68b538cc98a12121528474c1475868c19d4986804b7bbbbbbbbbbbb628290e0b3b31d37f1c8e9b0c8fa1387002f16e06aea6e29a39400309c6c3163e8b5acf164c46dddc8ed07caa072c3531151173750bac1e886a4309cfaece8b19391b9eba279970b2e0422caef33b2affb141b63d4589aa67dfbde55dfe2b66d31fa5744f66ee8d2dd37c61853bdec3451882863163251926127d34f9ca5b9dcd98948501c1276b056aecb65f4d9f136668ccdae038e893613edba4f868cc2704c945f3291f74800d8907af94154bf3d173fa005536c50f9db0f69514aed555f90f6b5e2fbf8cb83e4d78adf0882c8d7be2097bf3c09b87d12d027f555e9d00534e587bf9ab08419d22e873242640bf59fc925987224c3ca4e8c3136e9581500329a54f9a908c61893d0004510485f286d6962058c47c42f0c1369ac18c71655fe54c30555fe46b184991a0863c450e5cbb83748428818638cd10331c610c3a5c6d805e7504950e26a982c66d5c8a096a48005944c8c998c0d1d8ec814dd1023f38215349119a2c80260c82cc124ca0c2616706a22c3e5862f51646650a444068c1b88c898b1c102543c8911030e16f87284130401c058c2e40585872c4ba0b858da07644e4d929a8c692f64311b906c20c306a2346cf0c42436864912d4f2482d6a1e5f933146c9bc05b7756577a4fa737b715d712291cc51b8fd40184e2b706202bba1090c3064326a46d54c8ddf0bdb997aa46b0b488e7aa3c62f4c81f6c9ad9bf632ba103f5617e2e344fab1564434aca9fd610df46de07098a1494abb3f5a23bd758de1f60363c42c812d61d2637ca006514a9c94c8606d300e1cd0fd30d6b8ad4292daea1a6afc3589732fa979ae72724137c62825ff7e2dff50a0a9fd6d6533a4fc5ce88f31da3e8a7493730e628c51070be87ed27d77e516a594a1ac45bad8b030ae0eebf8816effec662835d3b959fb26d77df9752791dc5a382b92b342a3b78203da68a38d36826ccce58713638c5e39d2a7f281c7f766f6707804fed87275687751ca9e1da3267bf8a0b6ab1177706298b56be3152e80baa9fe3c9a67f7a34ea8bbbb7bb84cd9ddd7290046d058c3ae76cbfc3a64386e1fb79f31ca9f3f5b93114afafaf3c7285beef4246ec2f776c13f8747e09f3bf6c5dfa4b5bb7ffb87ee66f160f6ff68bbda8b3c61a41b54ddbbe3ae9432eeee8e4be3393f8489839cda6fcf693cc250ff9652fe87c03b48000c60445c84616262064668a0c90e34389343163b98e2454049f913ef24452bd0e287a41df84003154f8a4fc2d6eeee6167ecd27425873262ae8851014622ea01c64c51c516269808a20897c1961a30c1c1f184aaa0faf7f6e8da3121ae80820c8c95b07240f96ba5c0fe49aa2bb50bb4c0a28595ca7f02901061988490c2534480a2fddaf342c5ff218292b0bf886b5f0b3d2415db2e4998a95df73739b93a21fef869178ba084b6526817be166bc7c7441ab573e4b7eb5d3cdac5eb84a5490dd74989863e4d34b9be38e38aeedeb5cb9d992977c739a7cfe9cfc349a1daefcae572bc82c0bfc06da852deceeaf84f6efa967aeee9b19e3f8efbfdb39eb5e38fb48b3ea1fee1ec7cf8330bdc49c5384e0a0dbbba404fb3a62fbcd4dd528699caaabba50c3195ab91ff020afcdafb884976482843631bd53bc6ed07bc80925153a25423538dae3dafa426b58f5a8c5fa87d2e685c94518b316ada27bf7dc12b7f8575b1e3eaede12cd5df54fe4d6a1abf4ca5bcd5e9dac57146d6b6692b5e1dde38068573c518638c316ad23deeaed4b64ddb18ddbdbbbbb5f8b1bbffa8abd32cd69ccc3fdab5b4bb37d6c6d327b46b3f046ef7a04b4d4ac571aa94a66d8cabc9d9dc944a95da1c57f456673fceb66d4bb172ea4a7777df388c7126d95dc6a86952326fdba64929b79a252dae5edd7ca6acb15dedda326874cf9152935195d32eafc6e75d1fdecdae9bbda5e3563c68341937d50a38c6892915192783855e09796215d7b9bb0fe936329b18db2aa678a4562c16a5fe43687cb058354dc494a879bc86de44d58daaae8a53a53457a9542ae7544e44cf709b23e78ef628b56d5b75504043625c6e95d3a57efce5e3a3c2ea476a05e4bda254bb9a48232202f235eeab7e56af38a75d3f9b8771dac5dbca472af08fa69641798fea5543ed045381add68243bed6146e41d94588004155debc6264a4d2a2a6b9e611082866fdf191ca63236e83c2b33585e91e0f92a4b49556ea587be94d6b2828a82abfc734cf92a9f2f90c17b54bee88d1fed83ba8e03687cb21a3b3ca3258122c06c5240595bafdf6feb1d991aa9b31b5f1a91bd05aa183a13585e6190c62908bf852bb9b53e551cee11f23cb1145c366637b18ccbe787ba8b13c1d667b3aca5f1c420c34aace9fac0fbb29ab27ef612ff6c54fa64ce577352aff93f70ccabfb065bebd87937aefaf7e1a4ff5b1886b7afe2e5bf273dcf5d3190ab5b35379d67ef29eee992b6366820fd00f7ff1bc12e3591028a076f17fd0792cf3177f121495839bbb6b9aa6757c0f716aaf2ae020c7fd148750a2822d757e3fb51857c845705445fac5ef1fab7f44188a05f70a4490534a54881a6e540b322068b850a9c631228b585d1dd4c5e26949cb512c06ab61d80a29507e2d5f30ae82b4a6b0c0f717abc0055b53b45f9065441935dc99bb76a4e7e3af2b43b5178885ba92c52e3b10b4cb818a29a104ae288b448a18d515eb68294a110c3c298a13356428bb9483931a466e77a10122f01ef91de0bf998081e1ac6bc4909e86b1a268f547c2b0a2a51f70a8fe4466ad2f701b2ea31449f9236fc87e78cff4214d3274c5d03fb8e9a57e67474b7d3796d0709b90782472d73aaa8ad66906a2eee9fad5bfaa256e8041c3598dcc663bdb59ca7be6f7e41eeeb7c7c63daad4eaf403790ff7a9f8d5dffcd2bef0c6497ee107b53fb6b7363882c4c89c4dadbdccd45c3a6132543ba85ffd48bc8784f66242ec9e552d41bbf697e3c47b4eb0affe18a692271c90ac0e0fe12f88a328d155f79777797957d3de59ba74e9d2fd71982e6cc27c0cd61ad6aee62149049dc5269a18d2344dd38e905490180263831a59a4903dc1a4c687a218c8a198e44a524a29a5c6b47d318368484a29935c79028b35a6502286882e9c698a303da8be94012545b524a7abcbb483ac0cd72df93442b04147101d0307b34359ff392e1e06604206008b1504c8328500aaa05add25a62e71dd054b4ef0a07ddd25a62975abbbc474e44d96fa0a099ce1e405982b76f8618cff2b5b0f9a51d1126954839438bb03254ee567668900a4d0ad2e130f3708a3868d8728d5290ed30e666aaa2e130f5cd490fe5cc2b7d490f500335fa0aefcb0035292ebf0af7445ffd36c41f77763661cd89318f3182d95dd573e689e7bd54b6edd66bdd77d230e272326ef3d52aa714a793bb1976d979d9aa7bf9bff7d766a9efd7ed058a27fd050da11f6b9a95decd42eae5c436eaa1c26a9dbd51ecafbd347351f3d1b7e72eeb86df536de115f7d8d27a45d71e58366cacdf7cac7ea93acec0d47357925e811627737efcb18a5bbdc26f6d23c2958d38b81daba1f3fb1d2c7499c5457c5ad8c454e9abcb84daae3525d978ace3aeac14ac7a58a624d4c36f1db091bf911151db02751b5417e2e95e4b1cacf58f8dba9297e69721813466a29ed62362ac2b1030d39eae663682855ee1b8fc911af4ffc73fbc2f9313f0d214284610cf5ad80aabe85e6514d6905b9f1fbb552d829ad203537bf5f6b63128eea9e7801f2976cc98ebbc48393ea41e51e1ba39913a3213889872d356c2a957bd7c9b35476a2863c2602aea3024ab43d36080530dcfc0d0d409ce7e728efc1f982664802a200e7614801ced3c020ce1704821fc09b1b5f1008a2802b213734006f40d615fafc9579da5c000605fb2a0e28d4c5507352eecf63a80f7ca0fba9d4f37a5b395ef5735c149ff11a66039d1f721217e73782197dcad971a620c314685a6afbfdf678ac34cf7e0c2526da5c00f6648408d582862b2352a47b3e0097a3b8547e3ed3b05dda010a6e871f87057787a356879f9f78f093cbd5319bce4983f382168c17322881422c45d1f442b4040a0c1a018b2d305088c13c394206c65640abb7d0c0cc0cecd14314483118c845efe834b16558230b224e76ee05ad0da03043060a390d4ead15be209e728319a240a1550d33d03f4619b460e6040aad5e8004fac72f8865e0a48918284473c611e8df6eabb112276494c0f616148adc38f4182bc0785a2e509834711919506902395f42c31846a0d026a31b460a18249093ee04ca4a1328d49d39e268dab08596797483153230888198c129060af90e47a076460a1033b06938228a285003a306982890eb68342d6b1c29f10ed6698b2632e712f58453ff10454313c8f96ed114451228d400323d84891a0c6a98411328b4d383981844275c684004725ad79e27110326723003b9ad6b2f686540d4c00914ea34aa84818019c0cc402e654506284ea0d00e94334dc09c56407b4108c0818c245068aba0828015283102b9ae6b0f0135d8900472abaebd03b4414311c8d174ed711618cc10052e93922e512047bbf6dc8719c8000672365d7b41ae0519658a40a1950e4f14dd74ed79d10f349881dc8daebd20f985071a66a0d03a1501469caebd78650b2d92400e47d79e63a146124720373dd0d13550e982040a31bb1358cc38710472dcb5d76a982992815b85194dc840cebbf63887299e20818cc31145c8c0a03ea3062398b604092b92402166bec107eb7bfa98dc75cf751dcd8faf7e3e7b21d8597561f5f397b43871dead9ea5303333f3b6d1d4d43c57b3d56c5b951f4b69573f8dec35e856b93528576e0d2a2b173bd51a5a975aa1a0aadbef340f8b45a993eb3ff0e19b67fbfa1989f2cf9cb88455ad41b515ffae363ea2195237b903a49582fc14f091ea4377e276f2399dbc67c1e94a1ee522384143f752bbdfbdc8afb5fd5657bf54f28e6d338d9e3ef8f0114be1595dfdacabd5b3e72b2f7e77146bd84523e0da0a838645e2d6004abfbf95be161fb5cb632967d0bf12553525ba8572636957c76a33c5ca1ed7560ae189324ddf708184adc7713268e0fa4bbed7efde1159b1a0618b69a7586d294c5868377d1dfbacd0fdb063304683e3a464667ed9d139c4bfb065a21605f8b55f41fe7e30eccbd73e18aa04d967180cf2f96b2cabe3bffef2e7dcb3820ec94f0890cd0d686a5ffeae239c5e66476a0e7ad05251430e780085b62cd0007fb4a630ac700128b45d010350bef605ad164d4e4a80429b1210a0fcb61510f2a8fc49407f7fad207f7ba92465c497d60a9f267ffbb4179a33dc40e70e166d0591d383f6092e567801063cec2fe5e73c1454fbd9cd5782d5c310ef7922eeda1e5e0b44b6ede5a1fd7aa14fd55ee09e9db5d0fc0d51a29188d742bbfaa714dba30544fbf8da47262a667599a880aa05a8cb4485953a9b87a97e5da62f522a173b5f49b949606b94c0566d86b2b216b5fed82cab06a46b7f093c1026a9fd496a7f3f0da4df05ed350f4800aafcb65d75733c32e198be38a9367599bec0bedd1853af799b53a5b7af79aa8f5e00ea265dc6df3eae50e89f807cd9d5cda95acec6af43a22e4c76256e66228d33c47df3040d5766831ab342458a07b6c7cff668ee9f2d1e83a7d82f8f45fc34ff682e445aca5ad6b226d24da4894c2dd409112832994cf603886a319b1cbac848e7b0cd3245462277195f70cf0356cbf67cfbe2af41ab600b1d587186146210000a0454f61e95dd0b5e4f29a985763191fe3eb023f87f42768c2d4d2e040d295454932225d174515c03494a9467686790ea0f6dc30119cda4ddcc8e934f4c16ee0c8b152ad508926d9a91718f4b4c14ef88f11943c61828471e738fcd8cf88b998d301bd16464239a9c363a90329ba5543355145c1a45508c60b1b2b9fcc53db7d6a28be23aee3eea280dc4aa8b7240c39d4d1ab9e7169ba3a0b151236ca53a9bcd664a9369622370699f1e4858b8a3847009fc710c438cb00c5a40b9913468d8b00ebba836a9bc89bc67f51edf699a703cde11ba8eedf3dee6711e9fe20d2e768cf8f8f8e4482aa28b895ec0eb4eaed1ced9a60eb149678a494a95834f556a23cee1a870dd8d0edd0a071a32d18a0627079a9a9b266ae80d26166a08b5c942e30e269b9b3528efcd0d336ee044a191c331e273860f0e19d0f8fc383aefe5222226f282fafbac8f07396e80918395c5647573a3abe33f844be0cf6647b8f1c54cc756417dce9821c30a13319940820924101111f9fcf08f65e5d0f16388dbe112f8a70008b0d9016b87fba6053d77543bce83c71b5de2dc51ed481c208af8949a130d37c6f270a763b2e3a345dec0a0e12209194af59f1d1f7f2d2e0e1d380e070dcabbbbdddd3631a0fc5a7c5985f24edffdb40f8165357edddd0d44eceeeed8dd317677f4c254f566efd8da1641f9e3abee8dbadb366b65709b13c68e9ddca6cdeac4df1882d4be6c44b0994046d9695b472937d9cd8a6f33048d326a51eedd44d149bbfa7520456ece2dceda1efc5df9bb591d790304d555fbea7d495509ba3d761dcbdf5d6af365949299e76b9d9c40ac470da58ddae51f1849d13185c6a32e4654b99aee612727560b8d1144598c9115437efae019c939c9830f5929c7c7c8454382e2508d2f24d620699ed6c718a353c4e98f1f7fb6d8a02def9b7e1869887ff0852c1d5f485939be90e5c117521c319c2f9c4621f59b161b34ec66ee365f386521ad29f21dde3a79cb521f51f50ffac610aec99afb2c5839ff0eff863e55b00d4766662766e06ea2061f93962dae2899a8419558a65489455665134330f183ecc9a104462e4dfeb38625a6b8e2a5ba6a36c1344e57643a7fce7905cb15236a76708ff8f30aac4625246afcd6ca47a4341011e58b0e9e58d0448728b248d1344dfb20f5a44445893035be8e958f488bf83259719f60cae149e205498c5102a96a4890a9dae758f9d05c55601113638cd183950f197657e454eda3f6338caa3d3d628caa3dcb4a1355fb474287236ea8dae358f9d05c581c01e6468c31e270d951441a463451841155fb1b2b1f1aa54bc49002eae8090dba28e150182104b793ce8ede6c971a76496324c5a0c6b759f9883963681874e5438b677471b50668da965043126a6842d59629842a59aab021832664f005112d20a28baafd5639aadad3346591a166a90a518df157ed84937c4dd3344d8bb17972545966aea6254a142f4a4a645c3465c9c2c251e4da1e4ee5892acc1237a8a416638c93a67eb99c06d490c7135a96ac3040d57ed218aaf62ced9f49d5de25c41455fb9d1554ed7d8670aadaffb0a06a4b31b8220b6e4372a2c6d7b4170c41668d1fd6b8f2e505dec5883238b9f221674e036a5c42ea414c8d1fe94e943151d4700415596600e35c9460c0006003d452e3c777aa695786543896a65099223c99da26b7edc809903094acec6003a8288c80c8949682f8c208880d8a5831a4a52c456c6abc92e84ddda52c2f4882eea8bb9445869913b3073821bb517709872527742883f67b6fef2e37af7cf65ee8caa05dc39f6550aee1772d5710e83a3dafda1534741de99e4e721e924907d89ebef9f83bdbc3e6e9872b5b2f56b387f86b6adae86071f7ae0ebf8dd749de53a45ff1e36b5ff1a39918d3a871e38259286a7cd5174bc5b88076aa317e38239176bd14347424588def48716e7c1f6a16941a9dd438654c20f0c154606b05185ab45d314aeaaa3102404643972b27d2744a8b18a4356541d6ae363eefbbfcb5fc85eb64820f945251513c8a694d4949fc1c0d1b51c16691e324ef6125fca4f247a548c44944f10b3916e78cb18bab18e5aa536ddf7393316a1a5be917d7481cda3d8d6ba7fb58feab5fb0fb9045e4323281e8a7f37ef82b16c41be22f21575443463256412d05015b828192f61a0c90ea9a517749ca957f0a43bd1899c0af02f7778c8cdcc87dce55d1eaf0774882f00f274569cc7bf84914218ae0278e19ad0e3bed60b1b4546a4e6e7ee4542b1fa9e7150af3b7d7e616a3f69af69aa63113738e07e1bef3f9e13e97efa83e745556815540a010fe9091d888a1b831565081db17d16c38aa53b62f88c1d60adbcf9f1f0cf3b7af06d97efe377bfe825b7703e12f2e0b9847c130e03d2278012606b6806cbb0aa27dad0952d54fef705610ede517d49ab2e3affe7eed7b97bf3af60fa02b50fef953d4a10b19454c45f3e733c1b468991d390c894a0f35eca893665d44651cac0b0065a2f88b37f2392a95f21ae6af281c2ccfaefc56b24df4042b4b37015086527e9ab2d509829fae18b183862573445136b6879f6417557ed8f116dbc3515c832adb68abf69325a1629d7cf7f7afe352352b1fbefdc79523f1fc6ececa3554cb5a42495977547f61cf729e15030d218a2e5964e02165e84aaee44a4a3e49d876bee7a4dd51fb926f6482f4915be891595bfd496a557dd8505256fe9c50ac1c28c74aded34ff6253f4a1155be92944e6db43a12ca09a71ad20a15c4a0ea7f3809003464074d367e8c9fe302628107e530ed35951f877754aef1fd9f66a73efc9bac3e2028eddf811ea9d1a4055328b78ceeee21cc7c0758aabbbbcbb8d345530c607bf47b7f5d506e57bb583a50ce3bd512b4e917a309f1f311e10a6522a8b7093f3eed62614157c77b806145b1c58747102933dcb66a620201fafddc0e2fa85d60031318b8c30b0a126223643cc4c02af377bc083aa040b8974254ee93d4766a9e30cda3e359bff9c66a0f7ad6b0380fa85d1bf7add607df4c1e4ebb5aad0f85ae2dae6edf5468533bb598ed7b4cf77094eddb8a53dd24b87deb03ee08168668336a9eb0a554212a109ff1c2c40994ad35bb99460d0ef7d1766d34eaf01a69fbe875d20e19286c7b96c74349dbb3d3f639be90a56a1c8ff385cfbaf185ac8d05866e543620a5e6a1f9ed1b4bf3a8d0db37d3f6d07e93cf79e1576ec8eaa777f3d10b8b2c540739d2757e3fde4fa14fbd01e16d3cd5afbcd67caef341f05a292198d8526b1a69076b7b6e87b75d10310403819a67fe4685053210a680f03b3ca0e6f9eeb7e7be1049e57e7a21923a9fdd0be777c441f85af31764a15fdb775f6b7eacf313c2c445cd99f3fb8f80fcd5d378ad14668802a85d714f65e5f168d79684d4aeed456883ce57a179e647a45ddbeff0b81ccc62e0ae2ab06df377dab5bd099e4fbbb64fe5405fc906757e612bd52d6ca4bafdb46b73b6cf0587511568dd1fb076a054287f1bbfbd233ea5fb58e8178f61a24e89d95e917d4261bfdfbd10ec537561bf91585f682b050f859eb457a576f77bce20ede2ef3d3e94ca3ba5fbcd2bb24fb55d8b71a7985591e45af4199130fd19699e0d970cba83052fa85ffcdb218942fb1b22a45d5f147de2518195449178096a62183f50f3c8a3caaf42f3b0a050fe1fdc2d7911537b3581fd0e6218128e88902726e2e13a21baacccccac04c6520c5566662ade4cad1d12b444f146f9f8ec33d7354a7032071774ab5c41802b03d16a2b85cf058d9bdb24abbf0a5400474b5ebc8ce1a63db369cbd462ed3021a6ba6e897a73fc58bb1878ea2e49115565f4a8e6e100fd10a10dca83846ddad9d99e6caf71e1769ca191080b438eb4a66d5d1847f45059fb8aac530a5d18473cf199554e56c2bf9d26e8d6d8cf0e25bc20edeaa75cc352146264f4d6d7d6b25e987d90419a877f9be1e989334a0051c39e2d085150ee57cfadbc86f217b39ed0d404bb75da50298772198c363bcc7bbaafe53df39eedb74a46cbbff9dd07c30743151852afe27ece397ffe0043270aaa1224154ed91eb67d42f361f8d4b79cea22f53d09c181ea43156c1f02559db27d3020f1d747c45faa2e54dffa2bf53191bf70c044f3abe2dfbdea4356179d8fbf1884293445f35bcdb7622b7e53bad80c8a6ac84620704137075dc6a00106c230bf05443ff54940fa0555f1dfbe06a95d9cdac2cbb1652876aa4d5e08183014ab468a30602856a7c020b4c9c0d46f9fda425b260216da5fd84f7593810ba63ed82603b7afc5338ef217d450c38e70f217ffa6c9dfbe4e1ad3444c5a16726da6767592bfa648bad2a4863d6b275dc4db5a9750ba87953419542a379da91cbdc3e6d6b61766a6957ffb884af3eccf28109cbf12b4d5bfe0a6b630abfab0f40416232c2d20db02c2ad9fa2fa84546095fe24759ff793d424b50544abfdb552bfadedabac1b695af27342bf260155e3ee744ee8cece67478436e886446d6666d5582bc149521db3fc6c36cf458c9663a9e90cb7d8ab7f10edd931455f869cd849141f691d817e0489c2e15504ba87edd1411b336307513c81421cc59725c02afda0062435811f4ea369fd3fedfdf0819ef00389f70075511cd43024ebf403c9eab88deae7871bd5b56b7bdea889a30534649db0f2a1fdaea226a36c7d0ed777252809ed72c7a9db05091e6ac84426f8d4241dabbae05a40fd49407e76fe1610f3b7fe62b0fb19ec8fbef6fe24e0802e9fa04203b58be3d57e394ee809f6e5ef2f85913e3e8a3a8a95c1450551fb439b96c02a02ea360d9d51696da212e539417168a1336e67bc8b1815e18ef0c38ebf711ff990d632b1d2f7d43d4def8186ac1424a9306385cb11182454c3c3141780f4855a32a411036b825a01fe351e9256803390d404e47327c5867f0b5989abef121458aa4735cc4bf56f22265090a9feeda47b968b24180c16c32e4d61a63ae8cf6d6c85612dd7e2af6a8e1d1224341c551aa2ddb3d2d675bef2d1d1acc47450fcb44350f304bdabc29dca4afcd3dda7f23ba11060a15382ae334cb56a354143b6c25492c4a86ea00cc54fedf2ff9ad0a54fabe36fa3023a47c0f5e90857b9339f676ca679564b104c75fe74549a4154fde9a0e6d96fa2213fc57797a2fab399edb1b5bb9f6284220a29b5bf9c6f9783536cb49fb6f16a94bcd4137eac50212cc4232ebfbbe3f7d7fa515598693c01390e5de718e2a3d0709f7866c84c5c8e01161242991583630c50804eeddf1edb7136b553b59fb74788105f7d47646aeaa8f36dea4cd5f9d3c6f358db0b0ee0df55737bf7053c046739fd82a05c4f5804833e3319e23d3f29eb818474169403dcc37dfc211bd81ea98ff353de065627fe72805ff1e38c7b465c356a8c435627c6c8c31237cca0e14ecafa77ad53051888029302abd31598abc57eb1646a8e9ae372fa6643b5046d9edd1e110c6003fbf20f67eaeb6f00db83045c9989b9690af1971764684531c6d2594c7fe9981ce3588f0bac0e06f8e5afa37e21463885e4b0802ec736a6e5c0a996a04cc6b22064a1a87c44d096dde430146e98392965b11c025b7dceee2c88b4d0aed412f42b83f277165886b828d13c6100b8abe9ed129959939b9c526eb29b638c31ae4777d75ec02a6f5d3235b70c547fee8eac52b1b721d3344d8bd107ae6776a8a3ae4df476775f01e1182a4ebf2825c3a293d9bb70817243ebcfb5e3f3365f3379720ddbf0cd17b2ea46433a3f74edf8a4bc3db9bddbb67c21847803a75ffbad1415f46342d070ce36a2e669178f76c9ee62f7a87e555e0bed496de7495bfe9fde96507e2d885b80431950606090d08241620656892f5423464b1358a5bf0532644370a2a88b47f7a89e559a27ff0b1d0214a479b69701a6a8a8a42ef64435756646000000008314000020100a860462b1482c1a1236691e14000c7b9a427c56990a84519203310a21630c01000000000008608066c4120025949beb342a08da99c14bd40aed6e5ae059c18bece3e31c32031202481e301737c55a5b07996a4ef450ab8c2c9344c846b0b4ed9c39f2e3e8455d22c6257bcaade8167c40387c9bf12f38617fdf5e8a2ea2c7bab614bd54564dd3a17670f89de9fe9a112a22d00169d178fb531749f72dd5877d220f48ff91ab95c5b8268c8f25c178a7404d68694635be7478476851009fafed2039e96ab767e9e83ac46814d3d4ca2b497301e6338da96dea5e2729b3d48a6dc9a6d87dbc21cfb8d6f41ed4a8735b32a40039082e76c6040c40bc1a446ba2c5f59ca560a3c5451c72bd6bfb325a370f2c9d559ff0d6f893ab4fd1f12021e520069a0b14516b6e0305a22945032da57ff829965ede15325b2ab9320edd598e99f7b3e45d98eb05bf6f44c1bec7a7860ed09932b335007cc3d6f09de42cb657c656d9edf2369de1cd2eb564b865d9fe0321ce12d8312c8509375c4f2a62f0cdeeadbcbcc761d82f2f5b80e6ca90018b2a9e704d2c94fcd6a170ad44d4ffa4faf775322fa1579b46436aa18636555991c2c3a06467aa2d27554715e24a3d38b2aa1d61f45d7398236e5d57b35a4d3011bd71c6ca5d175f0d215d1be0ea7eb60eabdea5ebdff72ed14081ac7342bb16eb2a84f3643b2707906d419451504e2ab2460bd8267ba6abe7e26782a7d0550e3d1b826450ce28e53d3d325785889bb5f17648aaa8e011338940c4c03c2be6fa9427e5ada20d6ada7118b4f507f07d0990ce6c835a1d9ae8d8ad303a4cc1a7975b6ce2a9b378a1cab18bef6e11bd7db76dbecc43830b5d3737dee3e5f06013567dcda06c34d1438de4423fe6f985ea5ba9bcea516c58a060cd26846d2b9abe4bc1c98b08d69255f68221b9a75c9018be647c0e5de9f24a5ba6844ed731fba7e47ac6fdb12cd543dedbe1c3b10ff92d4219b935d7485d0b8bfc446c94d299a4f87b9af0a838b0978f3481cab9933c6cc36f4cb86d52f3c61d63d307c9b28680d1f364b9b1c8dab150383a7222ac62492a5c686a9835a0d513a08dda2463c144a490ed1dcd06dc77f16e3c9d0b94dcde994b0f13d5069433403a4834ae2c03f76ac35318415d3f208e3748e1ee9ff10a859fab119769aa2a63ba9a7823d9c72f4b1ef903730a7a46d9d2cdbd77ff8367e2e871594a6e75dd028f7b9086bad736b6247c859011fa399f401c43b8a742cd9ae6cdcac91ac54a1c9e7cd241bea75782ae3bc9e53f9b7ca7c8f0ad8edc40a297681b2d32f574b0ddd1eae80e0eedbdd61d69e3390e359ea48a393985c96d8397d570f74d7e6e9ed7872bdd8f299815a6d5721d05c17845597d8bf9e64c8ebc5d33126c9c35b5d7011ee389bb5e19b8808cfe9055e606eed1795297ca82966dc8ba3532dba779e7f91f14c0f25e8460c9a4ee4f238247a07f5d624d25548d8f0d15d6dfaa3bf4afced947f0ddad0d1d9ac8b9254dc21f769e44db7bba0f96354d7b45a7e0d89523a666da797c33b17572127faf0ff3ac99d0088b7e966765ee61590731521e813f07311b2251a5959c858f9601aa44d11f53fa49a8e62fa06a5efff21a07a2647b5bd85e31a465ec3b2be336f94ed4db158173fe8719450e01f99cdb3486540eb93edb6036baa5853fc5f8d312491a6cad9896d0271892f12dfa7b8f0388017beb01c3faaadfd32a4b189b72ab2daed67499490dd426de2a40d1f7ec5d56271ae571b680d48d341c3269ee8a96442280579123815fd02e97bddeabd35bb7257ea4ec1af879df3f74ae0c01c6f56c7076fe3ef7e05bf0d552ccd2c733c7a0cbe9546053c312a870ee00db5f3ee538a969fca0ce58e672824025a679ce5216e6eeacf3f505820175ed3be7a5bcba5460dbf1ebd60ef02d610f3c3f90cba9e7648b4187455f724c5f9c9f7aec75da96555116ecf3f3fdc218649260d406174def4f2e2fd267e81cfc8d7881d1c92dbf9ea3f5f154e81832cc46cf1cb0344b7962e31fbd5471f728928e93e69d692670a3c84259f220c66045f1cf5b75baaa7cb0e7ecd804c5afcdb5bfc7c4f8c8ed88d8eed88eda0a6629be786b8048da6d3a2a29f2aa7d965a1550cac60aa52bbbd72c665f54ca6a5d5b2a96f02790f000dea89a31fdf32a9091c6cff0c4eb1f6fc6781d349876f8e1ac2b23bdd70d43348a09c35e3b22bba9ac6486b4de48388afd0203ed374b0346ef63650a0c0dc33fbcd6e3d1f550526c252c313beee7f8815c809c0a0aa431bf552228eabf8864a558b2c1139bec6d35a49e2b7d9d3289cb40cb3832960e6ae2a903bbface4bb5a99fd5b1a8bb24e897e62cdaa3518a0424df95d2146611e29742899449e0927ee966cd051005f36cf42e0bbaf5ffab32b0e7b5e892a665351b2023232ae6f5e8a59da1ce9b54d67b4c6ff3b1810cae5b9a154514266a98cf040645efb3dfb63aa5270217648623142fec434f1bc7e85c4dff0c03f4375fc9cec82941e533bd73a889b932f829ce7b343e6be751fa1fc5b7e5f5bb99af0842199218465909134e78404490113e247b12328fd15b10e40b4a9e70ba961ca33ab1a8098923d29288f1ad6ae190497d2538c52748d3ca7c63276961871ed89e44d2d781b8272a8b89eb47f24007038b2fdac94fb49cd859854ac757cd192a8c4346a01c6f3f0e55f0bfaa33cc79463166a2e2d1fe7b623b52ef03e2560e813f1ee2f13c9f125a1f49f259abf95b4add92ed9b2ad2ec303c41789bb1cfb3c9cff949653f6f967f462b7175d4919e65f021189b2c40db4cb6012c6db9462b70690208ef7dd6070191f7d000dbb022c630093cd3257f81b2d2c2e9d2bac7194ea88f4228766636ca966cd997ec13b20c7fa672291090e7a370e3b5dbe826dbb9f546e4fbca1d20597c57c7ba254d4a3b5c6f625ec79561012156c77d581878412c89f76401ecf2e684804aaab91525138b7d42255a4069cdb5d2c4e54234282ea76b05d96b6772ce236068cef19c5b0438ac8c37895c8352cc0fda0bc3add46852950dc1164546f8b853f1099b9fa9ddd38cd922481aa35c12ea5e1bdca18b577f1567d391fc64b7d5d04361bfdaa5c6536772f6e5db87efc213fdca1b38f20bf81d2a7918c74a5e0c6d3fdd4dee3c6db28791d667823316ac4e01b4e407aaac34edc907d790cc1353c67accaa7380c9a937fb58ef3630662d90e7b39b38c0101b606183eb5d5da9267fcd6801bc20d79528a571e3cce59b3b2e8da18fdf55806a3cb16fea53e3728b07eebc2979e108f8d2d9b532423d231e506c0546b2a7793284d9a07aaea5d03ecd9446d6a27f8b551dc233b1d869e85f654fe929477169c237d13542ad3810ef416c39e0e4dec26d093682873d6d909dd8cee81c07f72add41da08950eb414712285b63d1fa6a0a99b1d2a80bcf4dcbd8f407f3492cb86b4901141ade321c9b7147c20ec43306cf6d8d72b2fb3c962a2e1b547d18e239f4b9c43d65fb58a181e09a0e43ecef88bf95cc7858a6ddc8c0e8c8a8d3b3fb407e37d2a17cc05832d064a4779f1d3f346b74ff69f0a5e88cc5b7a56d6113aa55ac246f96bae29a6cbd62131b56973b7bae3bf81fa0ccafbde42e1996212a081d6d47b6807ed3a5d9186d4afe6d31535b3a71dd6fc41cb83f1906ef84eb21afbc487d95f16dcbf9301f03dc10cd19ab81ad9be7bd9a68b4a4e5d70d8d7d77789302ac51db2bf43f53ca3647b4bf3064c51ec0f5f5dcd40dfdd28bc2d94d132cc829d13349c9e6a0571e3848b2b0228222491c2ac0d7b31e56c942f5ed68bea2724deac0271b0c2dd8677f707d4b0636408e0eb619c5e89def5ccec3304378e622ca32ea91fbbdeb97849da203b157a8951e1ea4bda398ff50195f042fa29a464c76126be7191fe2365e7bef6f5adc696f30c5ab448edc05019b47533efd2d1a8e9a98fe2d2f4f71da215cb33d6b5527132174d202ce7f46eed2682782c472200bc296330a43d6164f4a4c0e8841e907f3bae7b61a9f00deeda2385a7ee777a3bf1f67c65f55c81ccbec3e582f22f2897432d2dbd6e071d5bd68678bb4562d645c79950edd76dc7ce9a26712c3b788875f6b7daf8dee73f79737f314c2623a24acbdbb193958ffe8c3f834c659dad64901b437b736e823824d066221f9f296c4b07c961e8431238518c86e4a20ad19a8e14bd1b772234e0568b6c690bd31935380b6cc9f50b5ef9eed3af09fb477359a3f226e6087793d5e49ba0343cc5e6b8e41534c1a6015eeda31e3871306aa5075471542bd9b324417e10f09789e663bae8f6464847544fb56fde9f8810ece2aa1af6df348888352d3d2dcfc0c19efe4445b65721baa97730f665fe1e3dfdd9cae5c7281e553868b48c43cc863540a8eec6422cb36620147aa6a07e8df4865b62bf0336f71d99058aa51aa302fb2e3f26aa795233cce1adc34ea64029ba611fb03070bf8f5c3a619cf28f282efe9deed6ef22142bd61d9e15c360e4df2b90b3e25e590b9ba564e2944a4966e7e334c64c71ec10163ed9c9e45ebe08a3940066b96bb75cab2c3020cade315faa88964168ae278fe6ffece6898f92a4e4820a84fc04da65026092b58d522dda43c703d746f61f198300635f47dd5640f3d07c15de9c05c05bf87e463e11867141e6949cc905cd84de3175e64b8918fb9a60297b654e8828a4e5dc978c3208b52a14f34a234086da23ae5427fb012be53d3d5a260e45755659458c9624ad84813ef999b24c3c05a4f1ac88e0580b437f4f001f7df7c344cc226f07dd43cb0e8832b6a330c0d5b89160d968c8795b246065516f958c9faad7f64532ef0c20c72d393ca7a0a63afd936835c53836e3c4949708cca9bd452f24d2df384ad2fba0636872482b2c72ca73a8272d5dca3081bb49022b312925c717be76cd4b20ba54d6fde0ba3a34ec9f520e42634a59f83ece07341c16e490bd7cb0ee488705fce61b00ba28282f14fe49dada049e96d3ea7c5ee08bb984c8896d16a83aee3aeb7255296f21b01bc43a2dd4eb06ba5e208fc5c2b343b691b96aba210c06ce91439f09ad60ecdbb596d885999b6758b9ecdbf8f96e966fc3af177a1efd6fd9197f55050c0a93ae7f5a4a1c38e855b462930a7282f7008aeb9d694f6e4134dc359085fda9ca36c205db9fd3aebe8bbbd5bec90cdb3b005c809d8af3aa4c1024a2afaf5bddab8f94a50ae23b4b5bd00fb9edb99bcf1fb0f3deeb6cf16ee2cb310f4b679d9470be66b7b4fa11a845eb2acde4687d1e0b19128fdc2d8815c222041cf6282cc4f7e2dfeb3a3aa13890fe4c19e223788914aa52082578214a027dcd12ac9558d59bce8bb718ba3592a6df05eabe9e83256bcfe2a00c114f513ffa81d1064684b0c9f8d488f913d518c08a9634f09e6a0c8776bd68e7868808832a1300ed1db4ab2719cdc4c6e4f8e2bc55702d411c351c29d2643738ee67ed12ce5da1d099d57590eabf96ecea7ed375516973bc9234af21d3c0c197d0b45a662133912d18caafcb44a09a5a3cfff99748e07dd590c5fce43d50631be3ab47936ee1c4e43f171f2b865fab372a60d956cc4025d6c93bb8afc259439ed9cf38f7a7c95fa2d7f7e3afb63e43b904d6f2cd12090ba11dd3ab90d08eee8d49b8fee235238d8d3275a8ff44962c4d00eb1cd41d2dc54ab0a41d7d86d60c93152cfbef5c0ca3f803831d0f1c14100c64a9a260ed6fe43e2dd41788795cab5e373e1ade5f354f858ec509d2b6e59736cb4d25f37749703ce7f6664f47ba82c21f2386b55aa9b427218d1195ca79f8072e7f0dee8d09f0cf7b868d14061a2cfaeea8d67791bb916f7e8d2a87b05c112730a4b0a9c064ab8134c6cafebabf20156ca9840a38e7e53975f061c76e8335c021ef521f7554e8eadb7bc726ff20a4c4297221642e3a7c4923b854f6888a9985fe6e3f1ca48296385683c5b9372c609068569459c9d9ece3d452826ef245008c09814804fc097aa40a7e0db1dca621041bd08207ca708c3c03537af66d52f4608fbf49c0a56e5d87c734e39a451c7c975a07d5a65aaf63005548b6b82b9645db2d6e4133b46cec7067eb8bac33f2dcee9e22479ed878b866b68fc75e51439457c42aec219b0401884a6f8db72bcd0a662f4e275c520f663565a10610135f5f4924e9d54a0a03d2ffde49039c684b95741e0f7b35c173c8f915aff228137912fba39a768cd4575b788bff724102b929155b91d74001393cbe84af7d46552fb7a9c52b92d86500fd41e4d4cf7465372c8bad7d15db2043fc9b7ca2df98cbaa90d1df75b504a7b82a3705423a17d22acc5c8de3444edef7c41e47995091bbf47fa343924752e6e3b51e210f1248a5960a79c3590933410efcd04a52001a73ea87c9a461d9fb476859a4d64a28c651056b2c93ce99e7f422751d21738ad857212e2baea08158efd8f47ce8b74460d6c928136aba46bc3674d853601026ad5fdfb2d485780be8da5b0d82c98714b6cde9d72a2a747fe6eb29389c40fa75c127812ad3afd5a15622fb55022f8dd0bf5fdfb79adbf08ae2de79131d476b7216818d133fecb35f3bb4c5eab12efed4e125daf8f0ac0a48e6cec99c4225ae4d913c5730186a081236616611d094d3b7fea045756a779eeade5c2c38cf99e5555de212e3506614343492f7f178072b698d77c124eb990fa12f80143ea745a46ed73e8b667d6a9e072e3d5c99a821918b3615c7932c85874236e8590a7814a3b9d8f6fb2c018c5ed045f9c37885b5beab2e5b8cbb05c4af5965bbd6e21ffd0ee71dde62559804070273904a0699d07634d6f373743836c68a5c0d27fb5c9e87ee55f6408aac5ea7aecf5dd543218acee9ca3183041a7993b42da88179c1a657f3a43bb3ff16241b519e8aeee937e80d4b00f892ea09bdeb76f908cb0d81e3984e42e8dcc0df59bde0448e5e5bba48437c1cca8aa4cd69bfc138a9ae8ff0b3c352ed1a489dd720555deb55f05ad0bc8a62a5cde8a82746aca9da02342d18aafaa63fde1c98e7ac68a3f832adaa9ebc594f70aeafaac5157d6d9584ce11b5b130cc2dd872dbfff93d0977f91bebfab4ed7f92c13c8ea7b2bb6425a6e7a501c819b76864eb305b27e004786d71a2e9b0b85a64b2a2326c55e60a586adefaeb15aa6d9357b542755b45cb27431f53206ce52c11f46ee6f22641001d83e28b801e4bad813e7e83c6bbe1a6ab456384f19723c140319846d7353b6e4ca01ffb7e5aa88ce57bfb4af38781b2d388b03864df8bef400bff1517482428067bd2f285bf43ac8593f54788b404370c757647705a15ba1e5d940cdae74c7623905d6b5d3cb356ac38badeff3e899a34deb7dce17f9cfc173613dcc9b62b1cdb0672d64a954dd90370d22c6d65e89c77c72b462014370f9bd57b49938dd17e6c4464aea2d0e63124ab89c6ffe10468ce7b23c828bc51a3ba7cc28d1457fd50bddec21b738340b0b5e6b9c8ab8416d01f89e36b76a667160d9a0039dfd885d35ad84264c9525aa531ae66dcef1822f703e3e909141e29f26325a0830c7b4972be562788ac5e50af9dfb99749cb544e0b21c3ae1cb1cfb44254cb8b162973bc17279c94215d5f4479d86f94f8910b633d6797d8a16adeeff1b126d6707c99d073ce089ddf3c475fc2a328917169d7c614d649c541a659155f523e44858fac6a804a31bd1252950804e844a430f64e0a203890bab444fd84425209496c5fa43d5f5452064482029c6c6c018f069d30577d018ba006fb44e4a0635422efbca3586892c0e01d0b35bed3abf1d02d9e32d02c907e9e4f9dbed8e5bf60a28c955dbd51dafad029bbfb829da53a655c8c3673ded35b7901c98c56a904b8bc557acbc264f38771909f90b2a20f79d8f24e37e41fa898068686235cae85ba9fc32e243e234fc3995bfa3d27ff8520bb491c0f356193557716b353a0f65e5b46acbf4e4a14b7f341b9dcd6122f9aba0e6aea55d3db4def82787882021ed08665181b35084f519f86b4e720e159d04c9c9915281e135c3f2ac4a1a790a67f591b01779214bf9b7e6f9f2c0b6ed88c1c907eda0e9cf552c6448692ed9829c2dafa593b32e2f7f20f94c41ca6c8098ab43537adcb9637d4d2e3afbd12931e875882aabe67a1c034211b575c5725fbbe98e4ae1aaec5aa44f1a9bcdbae868366c43968525459198fe89d368482d8d6154c0314849580abc34bfed219f171d08173991342b2b9dd3ce1e298e3dd5c87f634ea72ac6ac5253062cc4418b5c03af73adf7319068415775135d19ca4dfaafcc4a1c5c81382f689458b144d2d5c857a4fe9a012ce69b6398cbb8979b4c177f6f4478bb92e09f033f032c1b817d99e0968ef9f95b8a3b600ff4850c4830c8f226cdb4ac9b8192a138a12d32453786e4be7eb4abe343063e6a6a64454aea7632ec613dc872b3858fa4b2d29b1de3b111d8c1f1121540970999c73821623ba927afb9abd090c65daf437e1760de8977cda703e043f7016a128c2b17c2fb5ae246408ab280827dbf5525565507bc02ca56089384d007cb9dc4e649c9a4f4870137dea2611fe7dc54f09e2d0e2e83b67ec873c64dfd44f7bd25bceb52d08662389cedba8f174db93c24b7a6721170eb804d6953da084a6454c61780a8c4cc9a0ec816d26c19f0cfd5edf20086e037fb3905d490114c107aa59a08a3e09642f8f8197b7a5f121a6c61a3f3d1a3aa7e1fb7dc2974485f021eb0064da66515b41b77e0b4503d6c6351629f55f7be94859f4620c6aef4d34e2730255574883f33b4de348e09fd3089be315e1e775506b75455c5acd7517256aceb4a9650d1a6990f713b44512526d3cfdb064df0b0ce8788ddb2a433a265c67a6bf5a5fac5a5ee633008cc4271d044ca40e994420cb1412dff0b2c65b479dd20679a6ea5268e8a0d33b48530ad1b822111b4f312a4322ae2e4b7b00c2b8c4f1094fa7dc55798f01141d6eed89f88089cdb288315db50f1477f64b48c6fa1819c38e03e52e5eb563ad94a729dcdb8916a4794fb78dfa9c28d182a0ea12e5a560bfd34be1c3c413f13c52106e604b814f23f18ad3826a0109fb14c1adce0345831d39700d6f4f9ef22e8ceb46855c3609b0cc5c34a5fc546a97b74c5f3bd84ebede1e798de74ad54d963625cc00fb45e7652481dceacbd7280c30786067bd7b7228a0cd0c3427087aad96ab11684e8b59865373102a628605471a476023c9e79ca1096d1ef01f9fbc1519c55e19286e4909ec13dc20318f434c047a6ed0887a1a973835ff0ac7b757ccadbf24febc63a53cf5add643da960a5ad78b12623637959940ca7185a5b314e5ca1a9e7d777cd5002f682e05d3fc21e1624774086d5451b9420ad8b3d042e38b90079d78565be5185a68c4b113da0abdf7a758c35cc636ee7dddd53e078d77b8cc54701536b979491600bab02832332def1bcd73b34a441b525ae77aae318aa2786532684dc5ee13f7fc52a0cade15b086cb861edbf21f0907b2e73f50fdd3509799bb117522d3a5cc643cff9d5b07ae50f188441bee420d8a2efc3bb013f582b22439a5d0464919944fd8816edd694d827541a5c8eaa3aa7bbadc27391b82d8f442b5487f7100a186818d67031ea3103db375899b1eba23b3c0f6d41fee2eb18cc795a8c2723a26145beb918792112cdc5f5dcb93e11f748f18cc525a3459f474c7744bd5ea5b1a251ca33f199bbc87ccef9c0a3a43e2b82dedd3102ab17f0dec9d281fe0969c5f175eb90dbeaf92e061b667cf5c3819e1e3d5a91eef7b6a4d5466fdf2091eed321ceb78bea8e395573066d8df8b650215771b9d3f2e4fcc354a4fda1bf4d7fe5901903be8bc7696fe436a72e2e8cdc1205e754af016edf187a8f2cd2a2da3b335ddb47bd83949000d9743dc04ad0621d42471455f2322331e744ce7246dcca279a5f9f30bfc72d4e7de8a02d24930240280457b0cf212f465b7ae951c7d7ba1a54bbd28aa579375d3f3161a89c3df9f1740356e578b1b364aba992121962e36f2b96d4178dc1142d1d4cc5174e9b11030f6fef82541da218bdc49b6b115f967ed929fb019f2df5f51ea9c0a59fc6beb7ea99c30d08093d31f49ea9856bdb17eb1a73fb953ce362203eb7d3cde1b8d1a55f81233dff4a54ca3f60bb856793a855571004363099abf71a092bf3ea3fbf7f5b7241379a3b1347c603b0489995f60fd279c973f6b9f9d8cace18f2b7991591bef90fdf5283c21e65ad024fabac0de9a61af0fb69f268e8d69b5b5e0ae4001907a8715eb5c7f5c2d8d95830bc02ac5bbf059791c0fd28d1d2e2f6ceae94de1767058dc86d82e7bad218af6ba59fb1dd951974c274a43de9236d7f5c60a4394104c85b024e99140ec6a5baaede28bfaa256336b4274ab905c6e8bee5e92c55e8dc917c966015c6315f07c1d8c822d5dd036180a5dd2db3f6508ed8ee32a6a0efb0a4d1b8c59f26981f70b811ed26818c7004fe0ccf44e758c8c28ef019448be469422514de3fe4603f0cbb8dcd405029c7a63ae74a886e4ed392f374f742d4cdaab4d326160d47d559b8ec21f725d1cb9827bbec15599ab7191ffd09b514b246b7b50404b68ae576ccf2fb6198ff0ea591a7f15355e43a11af4c61625ea302a805b39d7fd8fcd97ca915b53c989655b2ca666bce5d2fc384096bc3a55d4e0d9d79607e829a708e888f7960bd6ee06c2e4e11948c2d35afc04b88cd50ce4c06ebbd07bf54999018bf9b2fa092d1e9436b4c1f0947123b27e5137858ab55b1a9f8913f3667d1c588d362065cdeb656cb0dfc1b1fea4b7e2eefa2e38ef09126c9a39926a40db466bedf2df53dd0201e00c914bb4cefe797353039141de0269ab7719d9d477b184ff63bb7634274af6fc5169f78fc65d1aba9b8216107bbb67bdaa3bcda654722add7f2b32582e798f8d074c838ae7b8c34017e30c87f5764d82bd020bd72d544a4fb2170061c19ce40d10fe8d11d5892a4db3331f055a757e6b3a42ea973efdf1e1ea6436755027ee967a0499b762cba390a1923da2978706703f28e3fb01456f3d5352c18644f16ec55e38a3907a13f2f63ca41b75383a855119e5d22fd60bdf1809d7e30f4b62beb9cad5d2717d97d700fa14480cbb8d6053ed86b2b618c5b3f680a00518dfc4ad6236c9a4128c8187c71bfab5c0c7568b51b2aac54d62b9fd604dba152c7c2b27606c3f8091367de8d04b703d3c3a2800cbb8b3b1b0cc50561fe09059ab97233a827577a435192fe00e8f1917008723551ac688068fe5c75081e33c62a23786cfed5a5c10b1e9b763cf4b7f384e8a9f382882aaef4263e8d4ca32106eddab2f7c87f7f15ef114577e0f1e0e3d5aea118158415f930e6b0f3135a77016c65f3dc810d64d161a9bef1611b7308fa516d96747743f8bf9a60decb6dd4e9f7ad9df420c25084dad62257f963e49288ee0dd309c854f73cc34d4a53edf54d654c49047b23a59c6f13f65cedb3902a226b61bff68b3b6e64a6f8c5330da8e636e0b819c0e76fd426132dbb12436a4cd280afedaed90e772f3c5c46c76cdb6fe6e7b770a521270dd6eb1510e59713fedbaf874434bd78f297bdcc42cb4c2f4da9fa018003cbcdddfab79b432e77cb8972a130a8b4c689d23017dd1753f7dad7c48de8d85e3933f5c9d718897119891c38d4a3a93e7454abb7808c8e4fc4fa230ef4925d0339e92d1ada7b5cc5a482df4a054fd1603548234a7443bc9a2d7b664967c3b5b84b6d9825e27f0fe8f3626ac41985f54807237a95cb2a9af87b01a7462cf43556b902a33b335cd17153b08780528d56871402c5a3107b1f40f1d62548b38b53346b7434ac301d5218b2a5b1a02078a1dfd4d75a27499398d1584dae3a7627d2e53de5c3b6bbb8222ad23a904863d68982a4646b027e21f6a8d5574398bd6f30a6c913027faee3b880a4f5c038a0eb7d3af133f96273c64c408a4001870a93384474678001a88789795ef91a3f214823ddfe8b833f64f61344e9ef3c8923fa1d2ea35927e1030b0a70921842374fe8cfbe1379c0d23c06024954fd4b4b77c98c8ef93beb96c6b165aa1669d32a0897eb705a9031f9636fc9ed8e98610a24ea878b8349198945285005435485833e707b3a91bf6907506f119aa4da0503c5dc7baee6e855ba98c7cc33abae0e23dae57cb8654e1e90fcbaa1ca35697a25061d225ba2b626699b4802515fdf271f324a3af9dac3fceeab0339bfbbf00934912ab1c05f80cfbfae102cd39c978437c61b17d391788a0b0613b86b51d8f5079a32f8498224025e835077f57ff1f055af2846245427d4cb4fd97e6c526a9d7c417c0f6e051b2e1d3520a45b6865bf53b05dfacaddc4a39ab1ac06657ae2fa80ffd19003e2448de43cef866a44d59b0e211264338bca0bc480308fee2ff9950660a2813029a85836ebc758abecd548fc96c020eefd2ae1ef93435cd692b26b72e3ec803eefaa7b8a908bcfa74e939ba82f50a49a1c4e45ef0b6b610f37911f8d28d01f455cf17588d9db97c47780cefd4d32be7c9a4309d31fa2710d4d0bb3c847e8b43cbae682a2974c01ad8fc5563954ec75094bf3f4e55e28728bc425160f4e9621a3a41486dc4c641c08ba3bf170b715d486e355d5059277007297c7a8dda904aab666a411a92a8a88e7314819cda8089653c38ac789fa7e1dfa0bcae9a9aa3eacf03368ca0f59a47e94af0b09a564602734b3e768e48e6b05eaf7be03586a5fa03c73121085295397903994b3c0238fad942ffa17bca3a774abdddc278de187079b60e00a4c775409e3b61dbf5a59b02df8b77ed1fb2ed8041dbf88f9028e36c78b20037a5d2bf60b1d8f58638e3c743289260a5bfcae7946951cd640be6cb80612796f746cdd76977522c4126101d3f0bb0b1f94bcbc9728cab904397719205ed21ee1ac9409052ed54e3e46cf391009956f1884a89c7f0c2b4dce98a5419cb78067dd7a1822b46777dd07945d68d4d7d34698a1a3f7ca2c4998835212e4ad403fdae85a02c45e226b8318abcff5b791182b64c1c90b118d9c29d91dc147d5818b3681154d32d3b33ff8e56ac2192d4052527cf7e8566b489135c78286ae6ac87a295df5d6561a9eeb3038836b58f8a563643afa1680440925d5398883f10508741be320ac6c58d230f8eeaf63140d6c60ac900d24f5c6b5b60f6d813edb94c991f5676157320f3db6fd33e2a8ab87e92f7f7c7718fb507399a85adafe3942f8267216486910f6787ad1b455993c05fb075a0fb6f048d01a502d8b9da96b3752356a944339ceded31007ea022e0d4796471fe2661040587232758e46d6714f81fa53883bcf0ea5590a9ec9e7c4b94a6ec399aad3b81f60e8a2a07f924efc52701ac53eb8e25718c301e16a373b10cd997f7a2ecce6f270ec04dd4c27a2c5636b6b20889e4e6911108764a8e48c2739d83a4e8e66f95a9121899625efaba6cd348aba1fe5b60ec1d6bd2803cb5eac75efab6472a9bddfb6453bc98dadc6c246a1152484c31352cd7785baa371a8785cf59f64fb0e840acc2dd9de1a64d17903e5cc707fc986271dbdba632caf479829eeaa6708945d4003ced1017b4769ff7038f83f32d21b67a3ee778fa1a5d2ebd33776dd796da8466ee68d07f35ee4107b61f00bed982f94d8069451b9b042e78e5ac365fc4816c2c9923ed63c483c7d639aa16e7ad4fbcb8354f748bc3bb67a5bafe70cb48ac175ddd9f9c6d86d58e0911e9bb5f7e9d598012f3135733a0c8a9106358d429ad64603bbf6c57c5d0a3a391c591dda9168bd1feae8de23640a41f1d7d9cee48798e21d0fb4a8d7cd8d7652d991a246b6d57d338d7f965d7e4683f0ae48eccecf0a475245f9f33bad5ec4c688943553a0f94b496de195b5eea86adad1adc0afc65dda9f70e43d853a184df9eed774a322a97d8d1b82304263067b17a5d50928baa384fae2bc007929c22c05bb74e3c7435330aec68df4a40e9622462881a9fa160a6e9493aaf00fc665c2baf1eca494086ee05b59cc74e4cfd91dd0818fcea0f53dbb50b75965b402dbd1e82664a592af2089980799ac343ec0b70c36754b9f7c82d237008b46994e8be4c6a00b545bd1186e370dad3e398e61abf20f980dd4eedf53003f9d3307b30ea7105590a29d2fe55e02388339be7d0cb8a243fd572bb4197e5fc5f5b912a69f09b6a5294848bdd19a652da4fd5c638d9d7879c04bdd441cca7a25a80da135373b5febeaec2f36b2fa4a33d143679e4eac89f15e2cf2ef34550c590c40426a854a627bd9c5ffa2c85cd2e43423ae40c1764a6b07acfe6be5040cc3477250b9fa725f4382e57e0ee2babe440e36434ab19b0ec2f001cbcf821788c2a02e8b2a23b525a7a7f01cfced901640d87c3a48bac72169d92d2c178fbc4881c1d8e2a9f1f411bbb578950b6c5b34af6013cc16af496938044b983981a97ef8031371ae8f028eef7d649f4a0560e70153ff6ccbb72c807b1f39e473de24b6d27d7b6808376b7553223fdc63ed22eaaa595c26e9f3ecb30fc6c9e1586a14773bdeaf2da5c6003e783ec65dfc25bcf354afb64368e9c538d3708e5dc635e2b62ba7a9f62344331510b7652f60e04fa399fe5723935d44d6cb78be7ebe96b57e7dd384d61e2b3cbfcd739dd13ee46959a537091d60b373acef04a3889286789287b060ed76d189fee6708c5f8f1af9119ca241f6b934f0b9a78d487f72912d943ee74fdf7267f2670e6ecc8bee4c2ace3dc3d6f61e3de2e92d993450d10cd5e29ac7e91c0f47c4e6b142258e075356271a64b122348e072762ada3f11899d5b71c8c0680a6b61aa142de67025b57640e35c577d937953d1791e1d34ec1529e7ec01535b875f87417cf4194e36843f664d5c7f72aa31765900be30d760fdad5245d4696d35a6cc05ea853173e50fac202a4e2d903b3d876b34f6ba09ce6eda810b6e5e0d47a664072d09967c18f71f37917aeb9e47cd1c5d02f5da2c0618aeba171719a03ceac7f94aeac2f10363f09a5cb8a3240703d1b6afe0d3ea0d00a72bee9acff19cbac28be12012a37d55b79fba3705763b75d16aac8209bd164771411b3ec96c0e7bfaaf604074e71336e1b60d8bb53801ae258c2d9659d9780514b9319fb8eef09bae972ceed2bfe91f7ce41c861b3c186be4119306f94d9b0a183ab0e0a0c20dd5e217cdecac65113f8a7c320e177544e6eee8b3ff4eb76ca1196eec3b722bd0a16ebefe705ef777ff4e86ed751c6a7f80562f1419b9e7dd01f7f9c0f7b919407316d824964ee2c14663b3c3b0602565dae13059336dd9afcfd9042c4af74abf25b5ad9e4bd49795ef3e01371a5e84084494823b8f93191d14a1c2b9d059be8169866222ab532b233f7a0aaf1613517cada50f71057d6880e118bdca0d8f6265ba1f8582faf97b1d04d8d85e26b1b22f3a501ab930aca7129a05aa7e8f4ab2d68110d0f9684cdac3af5332c753213deaa98a7f079eefce267cb69f7c67634c74eada140b30b3ed83a0d2f8ccf65e73d3259942d1c3dd5de409608612869a198e21c51598cb5b07d674daceb0df4a58640716e2e139fcfedf43da2de3a64a4ce6e7a8f672f3590a1a2cda3c2c4edcb1a885b09343439a15c62b11b26b44f4ad05935722fb4c98cdae873c9be541c207437ffbe2b7a80227b064572552f878e39f838734fde73ba21d8466f8d62c83321d354a10c3a6dbc81ae53c57bf03dee816b7de38c4144b98983e71d9d05fec4c08c53622a70f80c9b268928b659aa6cfd95fe6fa451d021789a9630a71f7c2b1d20e73624d3e7892591d9cae0654052e5caa40aa689c66b111f6e70ef4d98382a0e3123192eedaeb23de64a2f22e68081e533cb840c42db55de4a3e5646c9e0d7b3fb842f7a640dca6fee3abcbedb40f6ab311494961ee2e823189bf6d8aa91f46c333d9f384d3cd43ffbabfadefdf7a7e9a6f7baebf73a483ce68c775af34e846ae009318e83646edb920a31ecd4ff3bb0eaa9badfe6a1d26c66b86ee69e23c67abe4eef8638a6abac625605dda1229853cfc2c31fcc562179a6078db6a82a38e22c3cab143ecf1866d9fc41d5ce8cfbf1cef0eb67d989dc40836246e5e88331b479959983f4ec91d24061c0c88cb7f03c78a1b76e4f9303a2690f0308542bef979959e06f78a4152eca5f9899424b086320ad320025b893310c4c36aa0be34532d9f025a88b5e11e2b4b986b90c4e0511aa0e2c65d06de49d85bd1d7823cc819504f80b6327913e2b8b5aaa491829ec66d9b75ba5ddf5a6bb6ababc950d1046004fb04ffca53a79d8673d695468bb6cee9eed83d101e0cabce36add74ba5730ed7f04876e862fd07a2998c14a0bfe2c9d6d25bc8bb8873707e97197eaf78390a080744790bd61d5dece8360f156da94a3a61d225f68c92e2b0da4a59817bf7f81ddcd16f3a20d649f82cbd38e18637da7306f8968aca7de2c16072626fed7cfff047607001a7bdafcdb52cda29897aea74c59402564d11e9ea0edae44872ee60a8011867cc53fa38401594a3d38a32e060d84c0329b53e1d9a8190a9358f4863544efc945a1d737cd77f5c6e598605956b7c9211e4d8f880b2d8053511601d6289cef348e1f9727598a21a57f99e830d030a2db74ca7e0d1cfc621fc81a05087dedc0b7a3f4a302b63e5799e50d3d9de36534afd1d5e209d6756923916010f94fa44f079fd8f6e6f27645bb0abecf1eec911e1204f529db990528d435293524904cc5362a533d278ca267085ddd44ffcc2a0a023425dd41764b719cbe5b5c8989b40def1417caf775738ae660db10b4982064272a70aadeb92dc7720ca6dddf8625ab312058ced1922592bacd722d17566d4d8ef5212210a86c8c5910aaa06b43de1ea53aa011450c12e84bc58a1acf17b2a014a92eee2b8505288b880fcf8ddbfe7da413467a0a29635d3a6e4a9141bffafa63cdfb21d124df06b2aa82585ee1f178722d4de0931e88aeb9b0823ef4a4ad0bc04c97d02358404c8075ccb9bb71364352ac4a9223a55e009f548037d98f8c6f0d53715938b3fc607983a7990531605ff47322a511a7b7507eecf4889acbe8f2957dd5a2a53137b4ef3e920a2769834e23931f45f70321dc7a425b6b131a229a8c4d7c1087699c1814d751c7c806eb22fa120e07a3f7a59fdbf51c00494d1d9cc947b14ef3103aacfd51a0268469c76a628182e6b170dd8ce36f52b329f89543d85cf0d0c5b87ea24384f2242bcc35317d53e0a91c15d1c921657c05e1098611d2fda349760ac815b7c61d2da030e67aacd20358f54c255add543f22e5afee28bbeb969a661934590ecc1c3d8cd66633ed6207c7d8605a2c9d72bbf8fd6cb6652f37b51120e634c0a438938dfd68ddc941519e8545fea41fbcb2e65e77158f7bbce6c561a951b4ee6bbab3cdbb36ff2ff8e1048422aad5099f9617438ede1979361032887ff2ce2c2f28aa01f692116994dc05450f572ab435778731277000f48ae60288cb01609ca1ca60dbb48e35c1a85824934c9a7b8eede5408e3714b88fead02c511c162502bc4d0c8f33da4166c16acc1884336e123675460d171c33f20955c894bf05737a68e3081b5882f7441a2625daeef79ec96b244423a4f992628be30db48815a9dae2eecdab262f1f9c54df3603a419ed5fd8dd7e0353181c3035dfda84a06270e3db5c4e2a3ee695b2bf5391bb2008094dc4bf417547e0785c17ea56c1e7abbf5cafb40f9c428f8cb35370ab5092b38bd2b33cc88dbe85f7cfaff06771e4fe664cd89bda8f8a9e4695f4fbaa5b0bf148c6373dc77cebc0d8009c05031722303e7ff6e4f49660202ec20d6d6924d8b716364cc8ff60c837ad1086f484fcf64cf71f747bd0a72ab48b7c8ff61fc403adde3253fe6b6f8a726b20c9ab2b8efe03e6a22dcfe30f004b2cf5219f79fb881931be2dd5a5236b3bb3413e02548b28ad729b6e263c444c41d42d4162cf4e3c83e1c6985e84939a8c4c9f5995613fb353ea5f49acb808fb872f2f5d2382f451541b6b0eaa46057fae52c9d7ad15b2d2034ca8f82444098cd644d68c4d5003622b41cff80eaba989a54f1d9ee1be8515cd41caa27c85bd30875ef752e2e27d934f9f74d2d4eb94637968ef5a420a63676c83c942fe03f1dbd1b2aa608faa1a1093d9fde94e3e835ae1c40e29251a85e4ed40c2f5b7a2fcf62768650af7634c8153203024dd6d63c3d085c0a8ab720b66f022d1b9674dfc98e0ea5fdd854d48270b796c935102cc32ed0a389e79cb3fe3261537e741cfcb5209c5d4725161d2f4cb199737debea6b7497d5883567c00c967fa432b1fd511822a907ab5019e2d0289ce79d25561c95064a3e73bd54470bae829825cf1278dee01931314fb3ab93e573807ed7df3f655050ad51597e4d494aa55c08775be61c6c62ac7336454bad1bc8b9dcef3f6b8a88107f8c87dd0ac3c0d5372513f5887335905743d39cd66f20abde60bc0b96623dfec7c1d6c1962fa06495d9723946b1318609cbb7c87c1e952fe8f61beb352ab1f0b4b6cb14e970f48a730b69ff7be4afd9000572c6a83bf45168cb9948bebece7098e2b02e89b0e00141ca8e9e7517c3804c3f1d2233509100b5fb01138f8e50cde1414cb5d33fdaff786acd6f64854ff0d635bacff49aee8a62db2afdd05736e4ccd26ead9b99086bb046250c0de2a53b12a29e81b8f8241c33f737b506823c7259e9eb37ce7c50783ec24d858cf5503b8b044c962af1be529c79f2e1de78d64a728d02ec4e9360d7d69d5bbdd9a13e2a865e9cb6f9bf2c09a5bf98e7345747df37c335dea394143d2fec9091d3d3d237fdabbb4d21429ac60a0b83fa9db382dda7f0d444ecd5ac1009aec380b5a7b6264b9c4ad50b67575dac4a28e96686004f2371267be1cac849ac5b900cc51370d5d6aa11aab3d73dc102d91f329a5e94a60cca5740a9e02531d35e7231558b28fc6455d11b6b3e88dd9b8a59160dd646746125cdae8f19ccb41a47d33083e93073e67ab23d4a04f5c2f76d4b02340183720f52439083195a2964f8b661b7fd05146c1c6f783896e3b5cbf64a24aca407f3791dcf76103bf47343720029dbe239d897bb2d66d766a8ab1710ce4a1ca111d11e17ab3f29168f315d41338845bbe3ed30f5c75a0406c1d3755155a9860511bb05e8b5410dbd6836f0429687d10ae29b2171d0c2b8be71c06322ddee6cad1b6b6b3593a77a724184499a59a00198b83e1ea944ae574f00753d64191bb00d69db13db487946f03d97a9aee4b3ec6e105a0c771062349ee4d32acb30cadac76d5c68b00b26e956528f2d474a188a03489aa88744fd630f0c889b512bc993046919a97480e96ba2dd6525bfd567f95c5588e14d5c2c540393d7e11c9438e07e00141700610229138e94f92980321ba75df15de13b17d10e4d5947122e217d488590ba802142d1069ba847f060bfd6de1bcf10cce23a22515b86c6ca7f037aae88cecb0e252c5d76c113b07eb5a463ca95028861a0890bc3bd228cb77efd30b68fc8b134492b42b5a16b0b292a3ba2247d457844ba775fcefb05bdd7771fb79f40bb60f48de308a953c093a5b3986c06e0f0ca3785e0060295a59529e69007c7a688d815deb50230adfc90c3ed1df4a9f65e27c05a7272ba58f5a9165cfdf07119fcc228e0f5a32eda758fc690ef6a65b7b887b584bfbf54e7fb559202c7a6fc628c82cba006ea866cf288c7d4990c4ad44b8242bbf80f3d13658f4f1f8397349b61ae4dd616dc24d0fb3fd0df0e267c2409806439d14807a0ac4ebfb84803c3cd0d92b0ba2495fa4f8489ce8057625be66fd40cc99539c98f7849193aab82767fd2b4badcdf70d57dd86b0f670d89c24a97da7eaf510b6a73a95592b287824eb7cf21563f0f8630f767e80c3b04c202ee36580fee19fa80abc78dd28017a134093722db8b5aa122173ed4d1eb0765605bda2e82347487c78e065a20c52862cab7d059dff69c5f01ee140330163df3fbc36f989f9dfb39868fa608cd0dcf17b9e3bbe44b4bb31b790bfdee40d8f62d1c6a9e8674ab6d0bf11c151d4dc4808ced08fc03094f0edf5f73123f877967921b0529148db8f49180049836fed2ff509ab28b58de46010ecd1603680f0a687626ff97a5ffc01f63081d6063f69385b5c8b40c0c68c1efd500410a4292106afc743ae5b054936ccab21c7946f899678d57c8ea324d84228c2ca0b24b7fbf3354bc8d5697571ce0c47744492a2e906aaefa9bd2ac2084e205dd58e25d5427ae63f65c905c2b7c3c6abe82e8380334204dbdb66c32722f645b8d0e054d27abcd5773280234feeb8c7fd907c568dedd247c2bed67d547dd6fc58faa6486f50f4da612bbd9cd68c4d0b24ce2df10a2eecd4183753131eb191859768ecef0c32a5f9e26c8e85b068c9a922bfa9e216304d37c2c49984d16450e0d2584440f91edaa6ae7e6e7abb661f436758f067a9f0f43ca9b82c4edb5583aef9da02a6d0f520c3655db6916cfd8a3d8de73cb6618ae56ffdc2e16de5e4b3c86881d48934b73db6b47f144e2d42659034479b1b398fa2ad90ecf6bdf22c923de9e304070793b4d32ba7f4f05535aeb3a0af4fe3c90442323b331a5416dcbea2edc590ea359fbaa8982e73e28485e86401edc51b72560afee7c4d79f87d7e6e1703d360707fc01f17f8a23bc24e98d19d9f37fe1d19ddc2754123e878ba26547402e9367359d40ef916bc289f36a5b63bdf336a63f4e23f211a3cd0afa6763ce09237066874ccc77ddcdde214e406275aec9eecc7c85ecd666d54ff08a508af33c958cb1086f0b63ad85815eab4617d34a71f486d835602504ab82ba172b559755e6a200fe09e25d55b12d49ab4db4f4f5d134688b22e755a7f75d6316f4edcd88f87dcb8a6bcc9eaddd31d9508289271d64c956743aff3f6666f80b7774d44f5d6b35f10c31f47f6a420450c0ba0f235c80f6b5078dc5ea1abce0cb0144cf60f5634e6c24cdea5f4140b581448b6c70950995fbb5d96499dec3c12dd1260f26c81e98ce7a6dcc40398b9d1efc62c46908768c6ce60b04be82c2510f5411ac9898bcc04f469e842ab79b2abfc0aa369e9d993f37457a2adaedfcd84f52a127b854b038b174e6fabd668ec33cd8540aff6c73381732d54b5bcb6ceb2ef10336390e0747247032da9db6b275f196bf65f80d0d1eb6d06713bbab48bcef4cf78011abaca99c0df1f237ddf34a5e09a75fe1563e09425e2e4291d50bfcf6b85dd25fdd9d0174f9e654440e991296c8c395ac6bdf03d54827a437cdf8e61b7995adb6f1d5cacfe2faeaa368be7d37036b4d40fd91900ad7ea668fc0e614417bb16e62111cf5be8ccfd95a17d877a6c900fae797fa3b6162824adedf38dd59cfa949e797cf8b8bc5369bd3ab017d2cbb95fd6637117c08582a0cbc17ffefb5307a512ac322d9888ba36bf4d8928241cf69e0f866146accac95aa59a7e06115f5db1c732e7198d5ee86186709000915a5c78030966bd95c6e53e079ebff20f65f7bdadae2348880900c1229433ec0c5ce1f22c7302a32cbe860549dbb89890f917ac4141fdcb2900eb0f840f1bc1d8e083e5d9dbd3b38a12338345581e53068a07be6a1bc605617b87986124952ced9c7d199b4eaccb366dd4338b70248db90936bada8a417ae357cf54bd41e70fdce12f05eb40b47bb8bb066a3e7b3bafe7aa82e1c32f14ec3876d9f5e0b54e66fc603929bafeaa0799547265e800859f28a0671ab0c8588b015dfc2a4149d09dc8ed197e74a58fbd597ce28b43d56d8a126e508db508dc28b8da17a6880368dd884c0d1276cb9fdfaa549ba4096d30b58c41ad1338387b3479b791627ffcd9c542a65ba49232d7f94ef54d18c3fc60c29d0187e228387598f15337b64bef3f3133e8bc367a01df3e65efd14c8373c245195380ac2cb2b8760b8278470c4b1bfb75fcd9e523f5a69d9d962b4695b5f659afe6d24dbb26a0b67b857083f7f0f718a0110eb879fff0c79a80e5f72c13b64e8e206a4b36259d14fa31cf01a80617e5deb4ef092503ee1a01ad90e7115ef89ba7a92c55196d0509b6c6c15918d738fc1326da169974f6b0da7b77da8ca462181b27d75157b757490457220a7c7e93d31862e1230c578f8f11538a54148ac72338db3c11c6e494345f7bbd3ba0a486f151bb72402f641e321251c08b82a2bccfbd4d8d93204752583dddc8fb36dea1a80fda6693d38b3489ac43440f55b8337d5b3d8412ea35480cab494dd2cc3a8bfd913492750a35ea78a25bae3221590a8d5a8e054e53c9e926120c63daed779ca354b86dbdd5d09ed5fcabdfce30c9a53329e8517715ffb89d3a89a331d37ce70673158cc04e5b75f43be68d78ddf9e63b8b35435bc3812844648c36b4a836601cbad9bd754cc0f875158a627e48f6f316538fb309532b7fbc1a3051b2bc7c9bad1889397415ebdf556be1c587513ad8b04aa66e5dccfb36bc57e03853f52e73bf1b4aeedafadf7756fe07ce66fcc8a14cd8af03069a0e5e6135045f0f8830b3cd3e2e5f23b91378af1b2a22c5b4340fac8db4456a1b165d76975caf3d4c263abeff92b6ee9dd9cc379e8321258ce4322bee5b7bee0ec05357568db17dbf4df684a239ad0b678c40147e73faf5ddf2440e92142682ba82b54297ff48f1a8839dc62cd524a62e932e39d0eb2cc7db4254f210a70f80640df0aabcea7beedc496baa5f87d2a3be20124f456a1262990e93d1e850e0568ed0354f4128424504d600334278146ffc052846424e6ebb233bdb1c561709b182ec7a67e371eaefc4a09c650d7785c588455d3e5c6f2f0cf70799278a685419eda16fa0adc03883ecb9d71f53d3a353038b29fb48fd6c4c804b09e8be4de9f0749e18fc80065694ec1662cf44de97f2fe4fa9fd0cc32b04279c3b1a3bbb18924c98b524a55a7c5f4053bb65713d07dbcf74182e8fa8a7cb1291209a58deb47997b5fb5f281f6028515af21904215cab456e90c004aceac3e2a1757b8a8963d9a271a80e1856533c3844253679cc8f495dda6addbc98d8d8f0b1903b797e6f8d6736902e535e5d7084de8b98a98a265d60f091a0ff8f0415c654877ded29c9eeb95b9aa26abb6c7a21076ac8db903f358e94ce38b0adbe7f61714fa8c028340bcf92b80126181f30a082d88ac0a07c42fa4435ee0dee6fc6917dc0a0ad0db573c024d1ab868d72e876b0e428624964c952ef79fef55f85355821db4fe1e1136dacd26825abeef7971ec42509da3370ea5ff31971d5590e03de35096ccad299602a55047c8ab3485d41563196c5b4d3880ab605c73eb5eb8165ff5ee5473820e93e46d42cc71f700982a32ecf64ff1a565e58ae1a4649dfab17096718e6ff5df6dc059e9c65c4538072336a6ee1ab29d9138d017da1971335e8a71a433f6d13781bea4c80279d2656c0fae2b011198cfa13b0989e228fe2a8c29061d85155d69b51814235b03427276ee228c79a9ff8a3005e3260a942392a9b62ac89181456c40e9665a906e6ea42ea99d4a632c67f6c7958b10662b01541fa53b2e865f3641515d2a685d408db977496d8a610f8977abfbdd27d941c2add24edb67d0d86a7304cdb9637b75f1a054d228c600ff5466a4df03cf5a2b53c367050e8e7179be9e76e8946b9c3917d146c43a78af0756011af07245aa3d74c84752f9b0d08024c532b077221ee8e695d66e572a66042e65d47a505dca98578c8adb02f7d1c2a94c83f459d58f5ed936a9274805dc4ec5d29788edb8b6818d3108f0b73f733a623b08c15e176e8a40cc179e8371f04a55418fc081e5a9e9d2dec90b69cfb0ede5be668284e337c7a6cbc3209698fa366c5c413e3bc0b199c31409c729abef337ee7073b9b1e529d3ae0418054371e9da6979973be3c56b36e12c85374fe238865cfcb55e5f30ba80ef00d2bfc2c79022c8fcc31cb137c6a878f9969921d604ae98899e9d519c75d95171073f12adf00a2ae17d0940407ba4cc0cee2bc82ef564a87d5b0701df35e417cb9a113ced67f0af049aa5953c3aaa3bd40a5a8d56c8280c0b12d4f0a3ffaafae3503637c889eda78e7faebe83bb2b7c353ac04ad8f36c3d4e587c5ec1e0d63c2484b0aab8b297ccddaa1987d96be1209facf100166d11a7d17203d6950cf1cb3bef2ef2d4039120464f7f828f3f7bb99bf82d170ffc320f5f5bda400fc6a7f300e446033bc5f7ebbd4217d853f379fe1b197a3e1b3e02166f4d54394fd3a014b1ef6f3eeaed1cf8a538051b192df38f161a1afda14768b792c609e0b612c2826995414413f65cd5c6f827e922e8b8d5cdf5016dc62db320a212aac3d02a4c3cbd86b6a4381d27be74b4b718502e89886aa485784f20d6eb07a878886e2ced02a9f34d42016b8114d55eb0ce2efe413d6f0336ad4294fa6683acfcdb79520688fd7a2db9fcb9f3e430a24b3ef359467b858b3ac4aae79ec836c62184c2d666d2b1dc3ccc21db6dfcf566689f87e5872e7938524312c563545c8c7c187124f3dd1d4bbcccb0cb654d32efe785d4afa96f5a6ff9879ad1f93bea579049e46edab020ab9e30a9e8e9be68f76e2bc02c51b24328eb2a6d0a3bcce9e4625b0a9bab52b203b50fceeb063d898cf3184f400447443eda016f1874631edd5311134d726608a4523e091b7b8b7eedb22d9826274cf4700aef3f177196bfc1f92e1042be2439c77b4bbc1dee8e5ca28a69448a9014a1a9fd2d5d14b3103bd7cee397fa9d4e34f097f3491a49d50a5fe66e00e92942740839994ff9a88db1c00b8e28102f61d2e0a37e228424a33e8cb64024d34a8de220cfd2c51ba0091c2a227dc84714a60f0f9e4a0fc04cc852542216c5a756e33c914e18e3a297556ac19ad2d796e633de97daa53ed0d66ebb8153f8cd2fd01b197f9c8f3bf89805c694edf192fd134eb942f37820971df1bf096eddbe609563e7637891ef1ffd8c1f557afbaadc7de1cf4545647ce41e51444591b072431c4ec6cf094c2716c365ed2498a81a44f4f10314b15927dfea8c012dc089549f11800b76680a4dd946003ade35ac309a4c420a7517cb1399d5f8cfd91ce8e8d92dcd680b66e6dde5902a4f86df016007d489413689c72c2d4088f5535aadd953295be45b2edf2282593b14f9e77cdd4af04c2d7473b1d3b02ddf76169695ba6320271a1e0d33a00f50c024622c7f6010ece88e03e0e238af667af9d72582dd973e84bc5477593c5611cde8d79285b41c7096313bdb1431520acdae2c1368a2fa5fc7e2012f1d23d355c2bbd319471b77a22844af1566f668bd54b52b4500bd8887a037a843b747bd46dc97689bb32b00fdc518add482328b30461954db7edd1b158a3a2d97caa11059a1bf397ff81b971bd24e8ad96067edffbfa3093d7e9f34639eb5551f3d6e9bc1743d8d6f98a66c9311a14a86f761171f931200ce45d62c34341ff4f677a24b563309b3441b799c2d66b755ed84c571ac1a1747a8e2d460ae239814c142b6ef7d0f1328e11b620b956b5c5996372b9305d001ff4bbf6197fbe40f62d19396234191ad09cd27dcfa0a1410ece3a7355b4e59a6ec71da467360f0a80b9a47fb212dda2a3b2823822511289a69f3a48f6b913207d55a2735a07333f283287b4177c55d1a309be5287b23b94b4f30597ae7a843797dfc04d9d32f3826138f772116631bcdc1f0f252214c0be07a269d9f92e09071221d12e2f4df8e846d6265f78dcf95a07a6a66b2e054cb1f401ad7abdf81bc410532741465850b06d00a282c81561daac2a7945894d2e9ddb80ec7e84dab9c9444ec9a48aeb0bee07eecfb87b305762aefe45e613a62b2e1b5cd2125b945bf346aedb4f1dcb01ff20a70b9d55d445464330a0365ca78ef64c280c25e0f2dfd1f90e9b3dfb8255db412b2793cf91a914d66ee574d8c5481275f2ffc0bd20907ed232e40cf572f650f3b9b20df3c1da05cc511eb1cafc01f3460520d59540608d83bee506c9d49c5dae91f91cd4bf6af4e7ba7ca1913e0e3d22f3fed6f9fba77096a4bfcd09b47f26f1ff64785634666c682e0a2113af42955a70d68095e9567d8d65a7af37ef9d705ad91428222b88276c01a82e425c044cceb2e4d950d3109d1d1f0497bfa5308782de55e93648329c06aa7c4d0216142a4371d0ca5e10e08afc4eda47d343c37b63dd056e5202e3d397c0d212e66a5f1d50406d8c0eb047a8727d7aa0a6f5c24f27e3be894a262862960318f1e3b8ba8a44433f13932945e0108d3ff43439f5743501b415c76d031acc351ca63299f8987ed37e74d7b5365d9057d7fc72a5f8baaaf0b107b854887b09cf2a1c25a684211017a66506fdcacdf4067505129a0c93cdf33143f0cc6615644217bf6c9e21138174877b948dfba3fbef1ab9e99d5a1fdd4493b98040f186aedfef02e1be02e6ee091b958ce8cb0165eaf629331622a5c9705475faced7e17483e220dfa71cf2ae6b04bcf9ba76ad7f2fd8a07d23a8dac6a99ba8bfc929b403f86c64991af57d8c68d8c820645189038691069634454286100704555d647db9828d0007fe4409c9c35e1cb0fa283519dbd3bed45025b60a8e83c487025fca91d8e3e3d828d9a182ff331ebf1bf5a7244fe1edbbe172ea0af4ffa4445a91d168896e42b9f108de91866970c31b6d4d496e00d6f321c933781981aab303bddd364c6ee14978f6dfb448cb4882843516417bbcb6abb0d800d812d2bbdea9d3863af8d9e6af982d0f4e95a1d53ea25534bf23080f6077ca0a2579a5a810dd8615d6ed065d0981a13d10e0215410049c4f949dfc343736edaad8a42d443d0b0ef5b0a342564da2959dcae5d02014c8de1b16f15c0c4db6129e6bc1c1ae74efc756d3d74aa2eadf1236ba95ac256450dcbd78111bc9e38c0aa0c989f6c0d11169ce76ad9f1603eda75de1a7994705748b07e2fe33f707576c80983a3ab82d9235e63dcee29eace753bd8d2e766cb8966be84d9350bc082cb4f71db2e50c1912acf8766193de087a5cb51ae7897a423429b350e314fb57d0fdf3b7419d72c257df3587c919b6dec98ef14278a6c0eecdaebf830875e591f4aa98d9b15c99aff07071b1a98938496a230159b9bcb091b064d44276ac168268246ec19bd67d56453181b12e453ce92666d5ec0b18008d0f925f8188354108fa508834cfec3f84fb836fd40b3b6ca1d7915beb26c88ea44fc9c7de840d8d65cb149ac6bc1655923cb48c449a36e94c256ab6baad13070991588e4e61aba968630df9cf5783a7a0991a8593f0fc7f9fa01769feebb28e8dcdcc77b10a795b0650b956a94597120825018daaab3023cf6091bf7a9cb98ac96078f008731512a3caacf61e5f764d8a57acf0ee61661527361ead048be13be85ad95733129ab05e4d521a1da81f08107e2bfe8bd92533c9d8a756ecdf5b6b9619430b0fb5e4684d0260048bba35ef5daf623938731956b3eacf37ddcd8e268e3bc5351aa6b85367d7da0c0cf37fef0cac309182b6571fdb08775d887e75489865b6380c599551204a0a21e51e7cec8299a31180f4158b573c5cb6d3a8a3bd61f668c36f7337443d231cc60778b249ed011f3c5ac38142cb3e46fc604d40d33084180a7e4b128d0b5c89e25c439179318c088359b3777d2c4443159057c6c5043471bdde99710b04c10999538670a290935cbabc7a2129441b426c046d33421a35e9168b8d4b1b1c6ced3adaecf038539611e5a92bb4657cb937e775325d2e0b5953b448e7b9cbe586af04d7c1aa4442e8813ee399c5fa3a9f58ba7fdc6865bd27df63692294d041a158343aa41f4686de6c3e92630fd906b4c3b16cceb174e2ea7e58ebd7447d3a3d11dc5cff66820d6a0c7364092aa0be5bf0f8d6923bab94fe830956799e47c1c430c9bb6b6dfc9a649ec90baa78b2b359b288af9334259a3bd8c3437ed7c884fb94a0e5eb7ac86471d57f865326cb15618681198e001ac8822833ea83b2095fea41c8af99be51ad2c97308e88facd973845286a98a592e86f5b93f2a6bbcb72ccbe03c7c21009ed392c86cb1f97b056bad56aede49fe6489ad9dff61d489f59571c88fb64a6d7c2019ae3e980265cb43a44e8da2111a8258140c31e93c5d14503913a705a1c1e81edfa348e06d9c28b4dda02d98185868145cc43d72187082c388ace7e19ee9e2b30f4f18f25c9545830235b46ed5838ed0b2b8193b8058cc0bf5abe9b3733a735e2e61ad6a11e435ea32f77530455aa65c06be6d1c74b9e9d385d4be1e41856f62df0cff507ef5f99717c8c98ee151f72a828472a17e73b66c303054cab0965aca620beb0a43996cd91427081d585376c4f04f58e5f74387aca95daaeb2130dbc08acb4d9b81a4f98eeeed61c050bbc0e59d0a18f9927bc5170e403da59663b295e98a1797e5434df7d8cc39f785b7492fb31a65b4ef8b791f26acc252111837b448fef81b9bf0e6d24b9fd718abc11dc1bbb103a5643d45d504b467364907c7186df40c585352abf72519e8c73abc055a8e44e4d0681cd89dcad907254e84ce83b718a1b643a6b564004b9eb36b7340e834a08ad0caecb1f2576c400be6034647b248d2bd3f8a7c6b037034edf8fbeff6c3bd04f3e952fa3d0bd93673bc332b1abaa31b2ac2792fb742fbfc6d477c02204423ea1dd55eb3a0e9acedbed6f182dc80747f488a4d9cccd280f6355226e6e0473f3805489e77c3ac8b832401011f97b6283996d347e97f98d8d639e1142626365a3f186eca5da5a39712ac1ff366e63f74e8f5c4e13f40915f3e1fee2add369bb880ea668564c2d1748035a13c14f42f65c88732a26859a8fcae53f0d84c593fc8d9754df6639d539cb612b90e9ebcb9b0909caf258aa70fccddcefdfb7e12bbe4d9662936922e31bb927817be35362efaafff252f493f97580725810213b3af048ed0b1089729f1705dc2c1572a9060e1314454b57bad6f3505cbbea99fd98490fb88dbaa56ff2221f6faee10835f6e4584e8152668d1715677ec9c70bac658c2dbae6a9a8f01e6ba59b85df1cc63c0c893e04faf53af156a76bf77c707478cc177b71e5f90f9cb422f1428467b6a7c812ac44e81a38c9cdf192c85cb74c727bb016f9e40385c5114e99bc48d5cc0fa09cd763a9d80b5647ba643b0d85d1e288954c35328f1647bae4a7915db438f2f8e3f6d2aa88718ffcaa62af6cad51d435dff35f2d98c61ed1ee68399e8afdbb60aec14c7baa63ef8c38b03110d8fb44b1c5e9b293cd10f5bb0c7c3973f7ca907fdbd72ff83ff35011d0d1029a63110d1702f9d7e61827adddc5b1068d4a3879c252e494d88c7c0e1a77a7df12934e405e00f4e2145990404c44e284bed917c65a083982e55463d73ee058051ed2b5260c501812236ed8137268cfddf25e5ec40b74cacb8c2684c0df7a19fdd7df1346813994b61fb52800d5cdc878571b448da9320894a8269813e96b19fd3ae5a76a4990fa817caff7b9521724243f889eaac25cc377d6df64c9e501d8d44b30f71aba685f85b1ae9857a23d133cb47196b807d4ebb8766936c28bd58d5acad1ceb31cdb59a6826f25cf866d430c3b34af96d174df7a6dfc252cbb0dcf5fc15946a3b3253380be242e3e40079cd26d11c9e730ed48043047f81bca6523027f88a364ecd66b2c083a3dc3139907225dc66263da3513e49bef5ae844c9e4ba4abd3a2ef82f0c31bbbdff516f9d879bba8a8ce59133464d5043e1b1101a552c9f29661e6cc3031b478bd4e1bf7fac6b79e6c09125ce59988a7d1c1b7c278ac13b79f275fe53c8675a7509e6bd4ab87674206cedde3e9af682da4c76bc8b33292013c4ec41e5cf0e71b5becfb528190b58832fdcdee718987cb6ab679bff3d31809e48628a7269abf99e4ec376cdba18aadb642c990f12d3a017e0d26c617db836d2d0296a36cd4fc189050713c7bd7963750bf1059b73b1614aafae80bd0c6e2552abbfb352a34f2639499d226177b22adfa30429c14dd992354800c4b451a374f55490bd6d02d03663f3f0da91fb52c671634ceafb7b7f3e7499c3699c130f75afedc1ddee42e5940f73ce5463f96b857f0073f31476dcc42929adeeacf672f9325ba53c189fe2ebc162efc1a8478fdcd805796455cedf8ed31e87b932b751b006a33246cb2beb94ebe44a9257a5983fd1a9d9f317d3dd10bda11438323283335bc746ed4bb74f4e3bb98d559d000d490ec843cfbdd7587557d462f09ed0620f52c5c6fff7c54e4d593c3341f68405791c1e9b01ca233f029263ce65ea386b180f1e7a9cc5eee69154479bd017484f03269e4cd7f60226e2801227012f713d6abea192cfa6089383c856b4ccbc3c3e3845a507a5051dd9be7c885138d7a1dee1dd6afda4d84aec3b7fbc31c6232af11234b3a0c95c4e4a4e5cad9b56b8b325d69c257a037f38d293feb77a59c1102504f5040612f6a96cd6bc5ea000a626249aa85d0b0fd854c68217e9db8b6e0b1fe833d13f5fbbd6b0788e64961dca7c6a9a362aaa58d508313cc5fb368dbb57f48d9921633a92256bf49a13e4c00369f7dc4566a66f46732945a49baa75198abc50e170373ce3a381cb3bad4b040285654572e6c8213e2d2b8b4407160d4c17695a5d5d9d653b16a12e39ba490ef49ca9c075f2e01c701f725a5198095803ef4601ec5432026c8ed9d33dc38ba021458c24323bfc2345e006b5b029b0434f28e04280ed1443058f53228aaa314cfd1918c2abe80c4aae3f5582bef8576e4ca7a78912fdf9be2ac9d690852cd504e07e59c8faf35bca2a785417339674292cb5a19659a8a98ce9641c48c35dc831bf61d24b09c78c2b67abfade50ef405c288b036d6e4cac9c1271411af959235f602dc3e4e7e882406110f4e47555b44d04e788956e33439d81f33e5c318e1c156e148c10e003d6008641c26d0593cee1282e5acf7dcc4eccee4ed4b36b48ab3ed1b274046a83a5f102f808aa17062dec3324a1e1e6d415ee1e2257f3f93400cb5a6efc1bcfc517024fef04e0a95b3da94d40aab810f24943854a10dfb89b9cf9d0d10fb68a61085800ba14581100af53c3c326fb103c3c9ff012d91db2d44c31579f29f522175a470c89e8adc62bc1a57113839711395c94188003edf748e477550387d636b3f04debf1d65c1974b39c75202235b2ce9e5c5743148224632df292b88bae45c77ccde542c9ca16864ebfae169e04da5a365b1714c72460d4d4203e9385ac86be04c99b090bd2a071e28570b2f71a360324c0357adbd7fcebc8d65c1693c74956ea85bf6e7d823c0268c84ce6a57c7b48e23205e15bd2a68f43606ef4e8d3fc2a1fce594df950c95717af73c00c845b10e3e2fd1d168bedd362759654d113902de61c25859292b152529cfa200dfdaf6161dd96585be5a6dd9ec6e5decfcbbc5f78b99b18b9481fc8db9327b1a384991109d01a804f38756aba10baa0324c6487e339fe2b0db6acdc1096c1051cb303af0c8278869ad1439702bca63293634ddac1856f0147b082fe6332ecad08829f34a72cbcf969c5a24cb1dca073bde74e0cfd183631dbdb1870c0ffaa61d82f80de0c6c4d616a3117574a82809c06a4883ca63e0314342550efc395355540dd8bb8e49c844818784274bfd86465350a8987586625e41bcff984f6edbb440f1ceb42edf8c759aa184f3ee9fd0f06fa6769f98062978f7438d36e72e55d9ab190ef09b27037e477193157ae7b8cf9e808c3bfab668ec1acaf224cf21a41ae7068a34c2bc22a518129f7794f966369dbaf0984c3b4f88d070ca32d4b988c3a18d7bef087233d6ec03cf20d768d41c5e1eca012e434aa1412d08026a5de46c24cc370f8d387030657ef46d4e6744f342070c77771be046210bda3e075d27a4626f16e6a87afb45c95c524aa536f00ae2782aa0915cf5e3bb038159038a58ba5d1824cb04a237d7a94a842d3bd075a37770c7edf0dd11b20a17fb12c05a8223278d57788109a721b8052b0d3cbed9eac8c48e4ffc83e97bff6a9b184118e2c09169e79e16881127df462fbb4f581099a6031e7d8380b9f5499640dbb1f1a445d02d3e9450f96f9c8a5b43eb4e3205527da9fbc799eb5597bd8c761808add79c2e59d0a2161e02f60bcb96d2013f78d97a519f5be94a7827ddae8f6af915671ae50716b64098a2d9b0e309d7158b269248dafcf3666c47cb08d52854803e6e20ad4173e1d7a683d63b9b487204bce22a2f18a2ab87f0a44fc846a3e4c728c2691caa226263b1fcd6a821e279269d0e38ffd30d852947008a180bd1ab7e431a8fc83585c8d8968ee6fb2d6daa3f695837eb558064ce81d3e4f23362c7a30b8def0e1cad6b527ada0df038ed347461d956e7fcd60a7c98bd36b3021513a403c8148f181c03d8fc3f04cdd2b8f8e4124121c408f9f8cc945ef9169c0c8bf072c6826823242e077f2325d6dd0f5de852dd2af1fcfe123cefd014de7237839ef4ae393752bfc43bc6ef06a2339046561c81a5026660293186a703631fe24a76b13323b1509dcd7be0ae1cf6c6922f8013756e8d71108f02ac42473f21f80d70e710e3953f5f1690e22bbb3b8594837409e3eb7519699ede13d5ca6b0aa33765c198d4c0180b2cfbd743afc36777a346276e9fd27c0fc58e10dbdadf3cd45540204d718cd52f8d87313d44fe9b6e9993e519127981878a32203437695f21a5d421d01a8a5c68de7c23042494758ef5d344ca8c3d063d4cb9a570e736ec3e824f575e5d59093de70b271359c5491b5997d2825a5af0112b89a96897d93dccb15ecfeb0488b83d0f731cda2e920e388728daea48b29a32b1adf517079f3345fb296e47365df70aa9598a735e317e060f8a9eeb5c2bdaa2372c3bf5bb2fec4059703a62a0185f0cfa94cf734cd26ed49c06f7a2149d0e7eaa628f588e5d5afc74815b24c04d3e0d306e0ee0a4954f2290d94e7163d5722252b18561f1f5c1476d6b1d75d000c0cf8de0aef25ba24d976d7a7d128d4e2f9b1b41e6d54d5d5bf9397ceef6c311ae8b3a08fad0dcb013a1424a22a65bf7de311aa487bbdd42e7d12797fb46016fd41de6effc87ea790049b86cd186e31c56c448b8554c416482c96d423bf7e449250ff8f1f35d52e07b3188ea7379df6ec42bf30129fdbe06e08eee3481d8888e43ee481bca4363aa6800f53f4877e2ead080625b3e0bd0c71f16640996830d79e5340e91ca80a94e565d94bf4c8134504c581b878cf64f3e87f979ebf9c6a8858786e028568580422ba525584c8b24ea4711b217034fbbe97b3aa73824ddb74df7baaea1fb54b7cc6659acd7c2df81ee5b88c1e7e6b93c82fb04af84b5d583e3e4cb2ebb9a32191397f13b648140cb165fd0803256269bfe1d4020e0c01cdab84f30c5d2b11d43a57c7c3ef5747814508607ec20ca4645a1a0a3e2445d96ef6dc5f7ec5c7c73a9caf42252370b34617b92974017f9924b66764b4e4ecd2fb5814afb2dd413a8a9b12183ec19fed07b5d3839a2643229051139805de8821b962e301161321b1823398e7c3ad40cf155bbbe254af605d610aa6b9f2635b5c065c443d515fd49594053ac6d54ef8875631679637d65f12cbd9542812b57722090da030df62683e30095887a65d7f4e28219f3ed27bfefdab21a31b1665fe59101154698ba9327ee232453eabc5cf943d7e14e33ef703e4b00f891b0e62579567ea8df6063588001fc8edadb34c76cc8f49fbbdaf152e5f60e11285d13358869156283211fb0dd002962200707290494e6a6d07fd22cf3888e40b44d7f8222df9eef51ad11a94117a706c241cbf573f44f6fd502fc345b68cd12ed202b8b89d1072dcd76855a410c889923400d1f68ab640c0ce55e27d8f1e5dee76480523b45abf216863e158ace23579ac4fdbf069fab8476a3afc580859d170050d2d39086df7be33bda9d9881e7a788de0dcf7bea40905a6bca36bc2d04dc81d692a5e11a7cc3a6a7777731c40651a2b5da7acbcd00680c5d476a74321c9fff4ff3085b7eb36103cb0bff1e9b38d0e398e1f9d67c22288c1257ceb910d0f5942b26aa0e31b08bff4c1aad1cf151a952ad91ac6889b2a79e076ac00df04183578208305475e7453d6abf66c03ede7bd3a9bcbe386ec8b3e56e5b3643d97164a06a4e1e04ea7c6b945071fd73c4e12275384f4fbee39271612add7343edcd19048e3dbc72cf78b90bb2927d989274122064d39691326317055439506bf101c5c763070f87fc5cd8233b1c229fdd02a64bbf2792b86d864eccddd7c21a133846ad737deacbba40342702d3ffc1e8b83dd2c273e0cd7bd2cc7691dd290118a64c3965e1615df8bda12fb1218f3b5a0c9f0f79153f4e4590436f6311a08ee66044bd8b8a534c674385771d0d513fcef14b43098489126497774c0ff59ddd5de28df632ccb1a353b40ffa1709a40a073b303ddb557532422aa850e31949a768b4e7603cde96d075c87e492d291418bed49b96cf69e3c651f33247a5e9400c698bfb665b3d5a4addecee71c0057c84847a65a4b0af2f42660d02ce4a8a8c907484038ac40e43092aa4d050bddcb00afbbd70b253bac8233b753990da2223c17f2f03354f4778c8fd6d8d8b1f43c2b7c02e809c0ad65822e5b1688c1f70f0685d0c31742c2886793bb122ab3cc047594d387930b10adfb88ce10d84703ddb75f393dfaa24bc903643b889611c7b03d9b88faf7060b5c43ff8464a42514c8237f1d8fb11bc6c44ddc952f56954350945a02a4fd537c9caad27f120bc8e3278f91bf2b68b998991b1955f374dfadf87a978aacf0fa253743b735678f36bf52b1ff1783f92589e1394568e5c7ea924048eb632facbb07aff3456fa96ce301c4904173ec82c728007a2e7b9402e3ae81b1fe51e980cd41b5b83e079181389507cff283a610c1d4b0aa06641344933bcadf5f6248acdba454bb054066aa3aac8f40a4701a20ad8594a6fdd14a6db03b961347f407058cce7c18cdb50661b842a081ccd7fdc0de7a0f4428b37fb09ff1da08e912d083ab20f3e53fbaad162aed0f8bbff263bcfd34978d2a78e08efca093ac15d8011337bd7617c001d4b5c45253fd83edf9151f59b9b280dd531b3a560b21d2aa37c77c9d25b83a343b993740bc5e568e49f22546a8599d622874e276aae802166b2583a5b5503cef5ae8f1af4c6e5e1314be8ffdaa512705cf7008f849a45cb4c473585e6f892c06f620954f76a077b3c92cc62ca368f2de1847a8b96d7eee47ec584bd8514a43f8ffab21bc07b41104daeb82995c30ccba9a0e251d2a102168e36481dce2c0d163f09bf18284e080c6488505424ba5c256ad2ca04381c9334548b60574387598b96fd7f1d76954156f4dfc445d8b88ed585ffc2104c7858e5cabde0b8da691496b7bb735b9a594322519d9095c094609453b64fad3484b9e93274b261268594af776caad6e49f5bc77ded7bfec7c4dcb4442510f0da81f5e8061ca0b9313198511605831005330a165cc172699116392e4f900f0e1872ac44801922d26808225082856386fd4336008c0194c9c0126d7c7e027063450810214966e505a413a0d71c39c73ce1e6f68ae7274ace8c24486972a5670c40a9acab0a18c33c4389921e50c1472fd1ede50ad2408a28647ec124446d1d413793e8f8e183c5480058a29a35c7f07930a78c091eb5f20450aa6b0c01baa1b971c1480494111b97eec36f9114956cc39e704bda109801e39cc914222d725505084822272fd1d6fa8ea3821b50206b9be0e6fa85a219ca0c97133986fc7c832c60a79be8e37346f5217343806500031a41003863c1fbf5e4c4a00c316585280c20a098510355b2b7e07bb5b8c10d4e4f99f373473c894084396411846e4fa9f4385eea28ee45adf460d91ebdff0866a17f50421b07202a65cdf863754b7271d308ec895e96b784375b3628a1f2576f80835608a57a2162f9074783145982861b6f862875cbf07e88b2e72fd1f4082e4784375bb225a10ee00a36403cc152610c204b55c1fe60d551d1474395dd472858a92ebbfc010e5fa34b0121266b821072eb40c219f20620515ce1417f8708145174170a143aeffa242b48af7755dafa09ba6e2eb17242e50f8c224d7b7f186aa1135d4787122cf7719d9e450e143835ca3960aab61b8d428c68a14acb0a28b5cffbeac3023d7ff2b9ac8f5412c72c8f57bb01893eb03d5b8e4fa3fb21022d707b294450a725dd2e2895c1f842d74c8f52d90812d98727d225e6878bd6aad482899894133edcaf34a9729baa45053830e1722b0105197b8c872ad5aaae821572c5e86115dbcfbbaae976b56c18214797ef73a620bee36518124cf5f7d3184956b9b9e92c8138bce9309b9d65a6badb5d65a6badb566b92087ae297c9862cb0979fedd52459e4f5f3f905133a3531679ce295158b5d65aeb4e9e45042db1f7058ba2498597164734a2c832bbe1128563b7486293b76d73bdb098820595d78d422a494d97292b6437d784992b8b231b1750a0a9c2a866861566b67072452992a935d14c79e1621135c3840b2d2e453258acc0d262ea66f922ca66191343b785118b89122e8b266f04ab2b689d9621544f9c653a62b718ad66d32ef1586cf2c4633187f7554bb2802a9cc25861015a142d69197364015434a9b0002b8ea4a02149920287a2055cf1248514240be082f4399a4c6dfdb295564aab8e272e8d1c9762b872e472392ec5c0c50a3184912403d113198a7ab85549d365d174b020430e3218b1e446278ea0e59d2519a05892a1cb7dffc273bd9c80b224d39f5132998c05195680a48b4ed470845ad0c2ad39c18223eeab091597660af874594a607155392e359122ff52932b4ca83a4930189d48622ad31c645cbb45cce8da1c979a7851c495c971a9c91832382162e2ae725c7292828e14eecc71c9c9121b392ecd30460fa782eef60ebcd0c20d2fc8eeeeeeee186eb6736d312846cfb12f6b73fe25c494835105827e3ac3c9184252b6a726b8ddb382843d665cebb13e2342dd07ab5f2b16c27a0e17693deb8b70dfbd11211ae74cfeb45e7ebc2dfce2aecc7bcf61ef693ecad8ccdf6dd24209667e437ab2bf63a97463c60d6d035ac8fb8ef5716db749eb240eb7928c612bb7f3be48eb65dec345baafc1600d4c9c47642d066daf3c49c8128757929125966164f9f3732207392ca5c615d785a5918cc999bf1f8258cc602040637ce0032d5c448a90d6771f65ec65f007baaeebbec320f88005eeb7f0c5edf4dd87f1098b16b64464cc7bcff3dec320f80057996e7fe8442d2ca50616f7bd0fbb080bcfc718dc772c25077fc95509799f0ad75f853530a5d3978bc3885b6bb88e8d3c87e0affaf9e1dce0aade5ff5dc6c99a3acfdc8300ee563c70e9dad8ba1edec6b2a064510f42efa916c21f9124a3a5296b827cfa2e8d2684a1c76b307190b5142f16f0ba7466beed3126ace6613ffc148618ca498299b4573891f492319258732cad1e752554297e240f553314b563df7f50bc17f72d063f2064b407dfa1c547f0e749e2a2712e33e2a1c420d679e218ce0b92ec0e248bf10fc371a427d7ffbd14b802a048b00bc7909880e4aa9fac54c9b62507de95f8b3c3f93c8634bc41f9985962b57644d8350d67cec78bd646454d39f1479d8978831f95934451ee9a59f6a2a2b8e43764327f269244a9e1ff3c4b52831b2d6535ee38c58ff1870f800e4a5c28d1ebee1fe7020b2d25aa794524a29a594725229250754f5616cfaad1be2c2ac94db263729e595745220b449bc575c07e2df06af136ef430941b46a60dbaa393d2de6c383d95f3757c839e5a5cfb405cdaeda3106c73f78a9baacb7be50c0264851b9970dd95ef6d822c4109b61792bace1b6a2d0e7abb3bf548bd9dce0c8c9cb41c4d1a35256530dbb918db5dab238f1affc3c01e5f7212931d6a7af48064d2069bff74cddbe994d14a14145d249ddd1435e53eb2a1c254713159bef5861c07894eb20271b3d70f241079e4ab96b81209cba317686e28f9e3a2f93c8aca55ad7c48f58ae72742a41f1a41c24074caa111a41cc2fce6def5c1fc6dde8810cd07f3a22330999f79966c72c1904fb27cf9c47d4287923469e4a054d17db9c47dbc972f84e65d2ff33580e65db2fcf120dfb12bf9fd9c8a741a6e3515717dcdfb27858585b8de0617b179d7d77c456a9ee6af1897bebf74288fa202459372b0dae028635d83a3941bcd93311eb974d93d329c51a8bfd0a964f93964dc501ee121aa57492877a92807a5fb181c675cff469247625c9d12ae7f9d5df9b11eb9f263457271686404528aecb8a37040b961877941b9f2b54c18910c4b40e71fc8c854ca96c1acb8321ca1e6f72f00cdcb775acf644c65853e31e2b8012021414145351d4509239dc8a14b1d4d41426a922f9f38b9d374b8f2639634ea94ce9671b21286caac84f2c88a54b7d42487b28b4c924c5dee510cdab23c8a729fbcf84f902c2e618162924eb24ba52d619dd454a46b4992f2a89564902ea16a209157dca71fe93aa883918ecff151e76b212ec77795acbd5729c7176b2c71e5bbcf741a89ce21c7e74daee40298b4d3f94c68a1300ecea8e9f704b7a1ba269b24b9928d25f746de799d0f811e082e4ebcc0c504a83184e6cbaf6b2e80f939e6ebf86291ceefe0225284e4f81cb888fc4d8a101daff345e4e7f8fb51fee8e022fe3a709431d6b33ec71765ecfe90b1f96ef3fae834609f63ff7e278536bce12b13c1f11dfed5f7aaff1b4f3f4aafc6b79e88fc71190e1cda0f87afc7e1dfc021986de01a3804ca3fe48f7b7197cdcfc1a187c9f36138b434f00b872e26cfbfc1e1cb06879228cfafc1e1bb300d0ec1192c83c39efc6ae110c89540599d5f2beee1121163f3ab402d912796dc4991677e9ddee4e0fc39abf48e945575be28e4440e4eaf1263f39700b1e449c5865d8989eb8e83882b8dde5949465d6ef8b1ff2a03c04028f7a7179e73832f8932461bc1eebb1122d87d47c4ebb82279f52dc580b2524e282eea6c51c6de5e4e1d392bddbe5b750772b95b712b1528bd0d883ba3cbfd2d3d9244942a35262644a13f29729f965522134d2abb90ea48460a555fc6c9eede906398fc2f7706024b0e5545d51bf24ee6606fde90e3d8e28676ca4e5931d6be5ecf7943931e1539d81d11d20d3b5957f41a81aabeaf2bea64f4ad980fcaf673fb8ec83b99fb548fe2b1d9b9937545ee13ab4a36e52086bae12dba45ee538418395415d12197dbff70ba750d1c2e4d8e5bb68c206ba0fd48ca7268a537646be470433f72cae176d4e3847b73e5aafeb574433b7593c4a54734949472d8c972ffd1a447b9df33923f5b918cf51fe5de68b93ff490727b49b99f4611eb4b0e3d2bb228748b84b8e1565494fbb7221bb7a2dc5fa162cc63ac50381c52a1324c7e14f2280e937f24e6fe94e4e1bae34e8a314a96c450b6ed3b6a2a777f03a6d086ed94dc8eecd496d4b84706e972016b2f2d6ba5a824d418e6452fd418d64d2e112b640a3586f513b95063291c44107463c08893a21cc470e1c315412c49117534a3a2d4b049c0095210484b556c01430b162b284e0006892701133932753ed0b8187f36b757c6a8c521fd4ae9eb65ed638b438c514afa93cef8515c8cd14b8eef4a3551b76337998a5a26b65de692f27cf9752698fd5eb38836ec47fa21e0d0beea6df521752d8675509f6e1d1ac9f439efbc0ebed0089df282b813417ff9e188608f18f3ffc99e43c38716f24608f3733831ac83cff53fd74975b044e83179e27f9593ab56d56e5ceb762c4f86c635f3381fbd211a35df3eab5d754e62065ef4688841d5d5e3a36bdd97f9f845dc87f5f17be623cdc7ef27add44c34c2d6f28adf556206eda57bd87cd84b3ff149e0c2cf0cc949f48f42d481e8df474070086c890ae81efea01733d8d13dfc77d830176af310503b281a384d5e5e37b89b6c70e85e831baa9110e0b9c6042e9a9e4599cd198f95813e12f552e6b6820c6179386c31de6db647013d3dcd75d4d7611c7429f7b73874a2ecbfe1b0c5647f2f76646fa8c8e30ffb0eaa280639ecbbbb63b7fc09c31d06aa4a4375e1331c9791eaf999423019ea8e0f2304514970a92d2185822c4d542c2b54289c4890274427011347d4b6265078188e7208eee8f172648261492da1d229636cc5e860fc5983129b5015a24253889b1dad1bcb0acc4686f5aacd93a492c478fc8ebaf225c7ee22c76f2f3108a78d9cdae829c6be32a5ca94a51cbf9562108d8f4528e49824472639badebf9fc87cd84744eda489688841351f9f7af6145bd64443c808139f480ceafe87178f31c6da1621518aece4b129c420c7171283bcf701a44190dc9175a2df9cebcaaf0ee7a3dfece2099c4fe626314a5d3c71c3062c49eaca97dcef44b98bdc8f63e3833dec6dfc8d1b30182ceac6d719e7eb6c03ca0b8d8f7ef3937949889c1a9fd3942a35a4a7a51a9f8dcdc33ee7b9ce1bcab1b1b1b992f375a6f175863129d1f862c6e9dc45ce247751e4a9f968cd376b3e995b0437af2f03dd4e723f95dc35af4fe665dee66f6e6464648e6ebece355f679b36ea2691a7ff8bd945a7ccb532c098a1f9641f74d110329a06d5b35a19341f8bc5adbca119160bcfc88821e27d747a9fcc718ad6931721518adc2fd55bb2773f554b019dbd272191a7df931ff50264a7ded413000d72a62a4f0146b2ca474f4b47d6513f2ff7fb8edcefad93376fa8d6ce5de4e98f3987e8f63bd9e818153b2bad95ee94e0cae4c8d40253d1f572646a61881c2a6801e97e0b32b8a0baafeb7a41c9335a700188abca91a98523605a20daa1dd9a239392339894444d2f48321633a53b3f5c1a53e8d002450e9fcab53932295122ef38a14409dc40491253922472f83d3d4877f98fcb63fd4aaab9b13c57c751a970707ab23eefeb56d64655b3727134b6bb92eb64951b9098251ec113d063c68c19d31eebb65a57a6a37165a51c9d3ca95743bd9a1ad652afa6c6b40102410bf6006d30326e38694c98bcd1572f8f4e2b99ed7dfda42c20dc6c09c620dfe13e1debc8838100727f3880ec1d1b87e6ffc69cb37538295dca28a594aa18a5a4966e73d6bad139638c314629e5cab9961de99cb3fb557fce3a65f458364c5c8cf12565534a698cf1467635b8bc26add1c53bf2dc2e8a09e46084d9703d3796db6fc3ed0cb3e1469b29433a5d730c4ae30ced628c118740e594b1975295ce6c49c97594524aa99452ca158beb28a59452cab1ba19b59aad6e46a9ec94895bcf50cab9bae8451c4ae0ca6ec76f88ce53eaea62e49194524a29a594524a6d7c8cbe2ddc7ef9b4bb439d1c256aa5b5ba1cc442d4248fe058667904efa93c82e777106f5b0e558ed5089ee387f5e72aabb6ca7539e2d75a6bac35c6aeb6fa7632b5d63a536ba5b17155cea69b33da5a9fbb795557df7412a0945219a3d71fa2e30dcdc773ce9b5965f64c719a4637a51ae6e4258fe0596a86f4e5770f05e7e33c75725e37382e53029d9bc5e95577f7a455ca2e5772b9b252dc45b3abb8928a2b7174b0b3a1a1abf1c2d0b470984564ae6cda9a6290dd2d251829c1483926090d5232f13061e84c1266d231b015b88a3b779e71b13dc33a885fe414814ead9bc579e5f8bde2b8ef5b71ddcb7d54acf8ed31c8fecafb4ef515c144ba2d534d42e4f1b7dd4d718f74d11204c82c71cfa43062510f37ad9b5e03a4d00a379216aeb3e96a3c8fe5b2400d04164d6bc6a99b76901ef03a9879cadc1cbf08526e26d6cacdd0b4726a60d5d874d14b6edc9187bacd0d2701073f887315243ae174919b9a9a542a2f9c4b94d3ac553d7d57c96d82e5745064efa6d844ec2cd97d065a4e8dce02c10a883c37861a366cdc90403d3d3df267c571dfcabfb8f118470caa407afac7693247cca0a3ba87bf183b56756b50345f254d164af4a7adc53baa33e825620f7f2f5d8c7237214613f7e1d078dde0ccc701135bc1cba8f20512313098925049929464ca28989220fd1b4d773a414b122c2fe2a27325994cb66405160a52b49294548832836486d73243c76ab574726b47abd5f25a1f8fc8ad9f2d26246372eb6f95dc6afdaf00456e3d8824b7be87052772eb8156c8ad99536e254912a3cf67cff33c4f0531d93be2c4a4c2112ecb9373652e27733f392e8527994ba18bcc7939ca036042a10935b273fb28c656837664ea23533a001aaf6090c3a8a50bcd804ad126f2c9611fc9b49768403f66c994bebd24a420d37f2165fa1f0231327db0042032fd9e12aec8f481984a1893e903214acaf48310a520d3ff000428993299e045a69f01fa44729089c08ba21e32653ae14ba64c011f975799f33af9a9b2cd5cab93df96b9dbc9af669ab9998e867375359d8d8b66a6e686151ac9b00e6e3a37f68091d92ea6a38e7bccb9e1c69f2a748c1d63eceed8dd91c6ee27dca42f99f8f4affc22209bd33d467713b8f84831469d3927954e1d989c2955b6fb72e5538a6d94f5ba125b1d816a256537b74fd2f9d58f66d5eab33182f4adfcb939f5bd5a6abf036bdf1b52596c75b0364c4509d9c4001de3733b5f1402bf22345000e3884c68e7b92e020267e14b91d0055f040400e0e2284a28f616c8b80fd991675ec6bf70479699f91fee9fcccc476f68c6a6c647cf068d4fc7fff0170a3766fcd6b5dd0be686b2c88fcc8e5a375afff26f73ffa68efc6cae09c1fdf79ca9c30d5f5eebe2e0d3d40d250ddf7cbbbe717c7b43aee75c2e3a66cc182c3947a5aea5399af33b1ff70dfe0e88fb776cb8709f06ced30f07e77f382725c5e108343ffd6cd89367ce174aa770484f479de04a263188fecb071d72c5a1acc1667b4614f9c4084bda279b3868cdb8345ff3f6551f4a1a77b3a972a85e7a43aa8ae36b8e987138def960f279fccecf0f0653bdfc0dfc602adc001ebff330893de081779ee7c1876dd8039e079f0783df12fcac8c71bf5357cfad6256e178ca89f023cf77bdcb254254ca12873ff2c421b85ec7ff70ff5cafc3e70f77bc4d11b6ed13c100746772b223e24ad294f6297d42975c7243b90ffdde41c096a964b1581deb6547955a0afcb61b9c100115b42652fe944e93d2734fda6c311996fb6553948a8a4c4952b925131130e4502a5dc9dd3fa52b2146892b5f02259a6c9c4d173d0e74bcebfd73fd8effe1396f9f03df88d0dc79f085ec3c08f3e78932362516b20373ac0121e0f3e0223fb003a2101019eb0fb283ed86a5c03e0af1f0ce87bcf20f5fcd6ff5f60b3fabb0857d21d4bff987b45f97f375dfe3e3b103fbf09837d46117eb69bca1d5bbbca1ee5d1feb7574cf7a6ef5dd2865afcd9a94a2ae92d39383c0fe90f31ba1f3dc70e0f78f7b9a83c3435e19c7db1c3d1cffc3c31c78484fc6f1f83f2917e7693c87f3859db717a5b1610cf4641d1adfde100dd8f7309d8fded01724e7619653b9761e076f3938b4301cde1756816190fcfaf971a0f3f7fd0f7f7da10e6e3d8daff307e5869396fb713c1ed293bf9f1dfe6cfe3e188eef93dfde3f27bbefc3f1850d73dc0135caf4bf8f59ca9c9e6a505153535150b5272737f223a424a5a626a524243f8a9a729917f98ce6349f4d452935393dd5a0a06a4f4e4d4a3d6b5a1bf511525212521fb551d37a166b112a2a6c595117b56c2aaa07282af5e0f1f100b953e6fe87b7de88833c3c7a7c3c3e1e108bb0e5b033067a3287c320f987e3577d18d85e7a43f8fffb42780cebc0cbfe5ef6778c819e4c7108ad973df95b2663fd4684769e87061c108544e0de0151087c1eb8481110708fdf791e1fde1e9fe411b66ce7a3d08e0f63144f186b418840bc835fd25a7eadf7aff53c1f2cc88e8771d8015168078e3226b4f3300f5a68e75b6807f74cc65a166b29b5fec6e79f94eb5fc36dd4a851a3468d1a356a7c9d6dd8f8ba461b19b9769ea3f9c2ceafb7f9866e705e62b007e855bf3dad6559a353df5ce539387cc1f04603d315b698c3385f3842cd37ef72798d95351f63cd4b0cf6b83e04ba71bd7f353fbff0e6dd1baab979aebb1b8c3d8cc76a5c51f2d1915f71b05fe67ede34d4c29ee47ddde746df852b4b9afc89427d5434739fb02ff87c890ba0df82afa75c00fdb12fba00e4e7448d50f392dcdfb289b4b966881b4db511c881bff33e80b2a4d5ecef3407dbc886eb4d6a9fdcf9bc04c42758c88edd6886f2481a6530cb270787b8b20a491483a2fc96e1953e2e254dd27c892bb32ca72021d9194c49c6fa618e3bc98ac4342d4edd4a2d3b9a328a6a1a54cf6a71aae73df6c41028dc3ae8595681d093e50f395718033d5985c320d27654023c3066cc18a7bce1213de0ce178566974b8b415dee974631a8c34cf9f7909e3ca427cfef28077b4ae9facff61026ee2929cfe17243497bb2f3f40bb0b3f30dfefc064123f7a118e66fb37fcfef89a11c543d216950547ac8b2cbeca79def0989bdd41cd4a1a5fcc7c378acbfa71c3bd40ac9fd9e42eb3092d6500ef63794910e9f8018545219e5766f6dec6926e647fc3b3c947712dd34504a95a69cd035ed0195e74feb2428e28c07a294522a71f28422cf773a060633c6488113279c7084a48288a93a43539e566a92ea4b511493932f96cc4878400b3424478c9824c961f60025494c3308a1e40149a03a147a3607bfdc558e4b4a4e782d2d51a2134ea5ec7ee3c5f599776f68e667e48cfc5519575a4e46e6391919ee7ee190b7ffc3657dcb71975e7ae9a5dc8febe387b688f8e3c231c65898091a6c25bbccf159cfd18ef51ce775ed7160bf22c929e57c6eca29e5949999796e662674654a714711417a1c788e52463e6c2e711f9783bd4d23ff79e43e51fce72c923f52d692fcc9bbc830eee332647feb0d61e9bc8aab57e6b8c8bd27399555197766d673fe0aba00c8d2822c71228e3f4e8b5f0c8ab97b80a4bfc45362a6a46c6aca5a6855c6ad1a90b28fa48f309e6864f23a80cd23da7d2865f3894c5a7bedfde849a4c8e3f208c71137743146fe75e6412ae54a2499342391ac375d262af5d1d7aa54ef17be72eb0b6d9e42ba1269db5e25b1f538f0fc23f2f8d323f789b2b5979e3295dda5cb29739f302acd22f70925929c9262e4530e27d13c92fdeb064c4d9eb237921e20b047de97b419fcc0b2a7c9ad1543a14940ac9227f6e13d34df061e73202986a43aef9da424e33a50dc6071c3e8247ffec445a450db42f2e9c4b097833b40f901f184b8501123424032d61d6b4e515f28c150c5dd9d521a630c928daf74f9caf366712b0e87b8f0063fb3e31fab14eee7f0a504be72f74b8544189d6adb10d50417471814700722bb39a7063a87ffeee3ca9f41087bfe3669ca91a949d2528e4c3248e56792c14bae3f9da4bcf1722703e2529ef89b4febcfa7923b32790cd8de4a0fa828e01404073fd672e22b7b7f54124d94a69c90bd8833b2632b59e847dbf445fbff8cecafe39fe3378cdb0f5edfc8c8b4bf2ed78d6e5b75291b241f5e3c76d92af5188a5290fd75bc658c42e215c52aa1d65aeb96335a485070220c192238b2811ba0c6a0a054c2962db63cbde045f17483122c6242d081394185e7cc781d95540ebfc722795b69ad7495040d546011a3c313a229b6b43013c5161c9a98f7755df7c6315a48b1f0c2d30c4e2c4df1658b628a3050ee65e264c3840b2624a0b92e262652324e8e4c4c96e43075d2ba59d58aeb58565e4111003916c522abba325f6c2266895c7e8b1cff00d18bfebb86a5e3a4322a6b9b71cd0a04fe903f351f7ffb2800426dc8c416a0ab4d8c4c36400ad16022644c7d2ec47577bfeebc8e2e57adcb39e9e0ae643890b96d0ab7a7cc8c4a2929a986a456cb1b627dcbe3c0b3ea672c919679e71ff88f17452872fc05b8064282282006a9b647723b9b4569b55adf2d3cbd157655dd6a48b547b2b328dbaccea2047902c2cd7433ded0f6331e079eebd7d86c1988535ea441d105397eacd66a5ddbac962405f84f276d76f65ed4b575c6acb596da95da14990fa88773b95c3536b55be5b420cad9500f4c1b1b9b9bd7ecc95ed4b5eee17f5f2f3abda8e633236a2de946fecc7cf8caf672f78b4232ab4c693fab85284ebfcc95f9eca521f18412391a2d9623b1fc6bb1d6760a0009285cbe2c51b3a20769c50f38d81e2b434c996925994c76245ec9f406a554504a63971fa464fa3f4801b104105053aabc10c409327d20415ec8f43f10022aceef07223b65afed0be48354cd0d510aa4207e90c2f85025d79ff186aa0f5b7c209190e75f6f68ced083095a35381080872ab2f42025d767d9d04981c20309b9bee70dd59a274c5290d0a8c9218a19b9d6e7bca16a8520aa5959e014254b14151130b02c8f284620c5084ac8f3b74b850b5b6badb56576108369075a9e4fef9c53096fce39e7bcb88acfa18f5cc15cebd10db91e89c9f51f9c814987292865e40a14047777a0687941060c0bbe9ed490d84cc9987df2850a355a9c6c9e4c417125d560c181862317682470410897a32892ccb658418896a3308a8e987020e30425990bd6056e8b5703926ddbb62a68b40aa3cbc311551551dc2b872232a5db539ae569cb52dbb2406d59a2b62c531edbc026b668a7bd82a6ea525d2f2b64326673f86205da258a902e53b61cca6032a5a02205d385b26426048b1294274a8438d5a008a144c8144d509a8e087932fa9e90b4525a2d78e1d21c979a5ce1056dc9c9931811b7cb71c9490f3b29383102cc618b135c1a26a42d3a90f0e1dae4b8e4c48a0b922c39215a7232c61126883c168fd825b7c9eb89c72213513c167308c68e80628cb991e3120d496a947d4efeb42f3fa39634c924c6ecab24cdeca2ea644e30fb8b946c5f1eb98c1283aa945f3b7ded25f2d8a75fd71cac12833afbdbb73548a61dd94a22b2f5215b1db27d570e97ba8e6e2869921655ef82726592fb6c336f5f1a49c9e1502a65fb5151dcaa5379181e1585fa03e2ceef1cca999cd99ff6673e6924690edadfbedc9837498b3cf6372eaea3107d5648a28f6a87f6993cb40fd744f4a150449feeedbb0939b81231a8b3fd767ab7eff5972cf1a7919a3a83bed26664db28c8b6bfc816894bb6d6beffd05092c49f0cb808da3e12d97eec22b2d521cfc0c47ef55c4f6e3f573d06d477e170ade522875deba4b559becc7be00f0b222303f3e02dcd5f07ed47a1d7a5c130efa310cd47a10bf360e6657e06c33c0ca5e6a390eb6530ac6228331f856476d4a18a4130dbaff97a64ccbeeb039231fb349f53b5efbd878138686393ccf7818cd9bfd5daaf6f0107ed4365fb8d74a53390b3ee61df43ba32abb556ce3bdac9aea37226674d29fdd6b4b3283db3cf51caf2b2773e27d79a3d25f79cf4eaacd6fca6b56fbf8f74068dd43da2676d876d1711b4bffa7a8b085a29eb1ef66d7b1163f64390eddb318c5a397497451e6b27b6ff61b92b4b439a6f71c8765af943b61c9e13e987a7dd502737533fe9eeee4e8105df9698d88beb3fbb890d377a584a13f7799913dc59420e6bf2fceb8a41dbcfe96097a6fe58002e72ecc902a4e2b1bc96c8516210e33fd83f7d45c7fc6b5051397ed857f48f3ba970c8c3e250878d1cf65383919d8bee6de4d067339f55e069979b504ca7f9e43ee184623a4d2db3cb84c2a7c824242f90b4f09991cf7cd61598451e7f3148536492ac85f19f98524c420b2d4d579cb2cba7ecd24b76da4e336fba22bbf8ac27a7450e25174dd28718f39759f4500162f9255e55b23b11f24be4e95a3fe1d87a67350e7f83fe69619914f2c861010ac0fa2ede4b4b77e3150ed491436fca8e7978412412f672c3396b2d1e94a0d2c4b484d64158ac8779e081f71b863540f5dcdb8779c0c1a4c530676158c51e744f571836b1072a98c450ba0dc31c43517d14da3ebef7592b1de3eb25f8515122bb0612c8c106fa2181787ceaea73082eea7034a600f7f11d3948ac9229b5d4e577c45de51824a3d71e02bc2c710da4d537fd0f885bbf33dd24f716e3d55bfc41883807e3aa704ffe0fe346196b9a8392ca98162eead4af8fe867420bb551c4c07f03e346198b42dcd32e6da3c8e3bf857139b74fffe949e23e52616b44b123893cfed1480a8beb53fe3886dbd88bdb180a7dbd7c4274dd1f470b37eca7a7277f9232e9754a8c31c87c70c782e848efc84d73374d36b1ed3845e48686eb665cffb069e0911b85a2500824611207110a6b8ac8589b715be68ec3042476c05e2491d2332497888346dce80de1143a02f57b9c6d447051e795dc28e3d634e36f3fed4f4e2623e2e07cee05c38d1edee186718a88aa7fc619e777b9b218b42306adf2acfba99247befba28cad6cb2dcf856c61f1b0011f20108ee038bf88bdb0f10199b1f8292a88b7e7090fac89c54c62ed9670e88423f646cc65509d1845b69fd721c9c8d03c98d320b064ce2da1c999e3ce5f056c182e40913201597e6c8f424891a25d01daed3ea3ae5d65deb477fabdfbd75779d34470259b9d19c6a5f39decf6ce395e3edd0696d9ea55c51fadaaebd76a3af1c6f874e6b524bbb4b819b52caa77f03f7e8f89c73ce493ff6f4a89aa136ede89cb3937246c5182395b56ba594527a2da5946e1b1254c61869ddac8a524a6b8c91d6788c2f393b2be5fcea54553a372e0a183060c0cc31130c183060c08099748699632818304761cee0a24ed8b5235b172d99cd365b8ba88848a9e46aca71f2f1a4211670d07f840c38e81fb59a12e03e90dd1054d77943abef9e257b8040ff97f781fe4a5468f2a7993ce6b6afb41114775292060e42791deba35252126773582a57917163ed86317078c891c9e315a6274732b54adc1085c6ebd4c2930d275ca61b54d0a5eaac40083a850d4972e4fa53e74a0d5276e4fa968a5ceb2da2883bbf062436d01ca851134b84bcd06484059329a3291784b880549bd87ccc923282b833487408716768b8e0890f31302e54d9e1b6725c72018a1d2ff7e6b8e4c21535725c9aa184292a318733c69c79811873ea2f7d6e34b640e44972fb093339790f3d8b41396250cc114714b3f944b325f46d8c71c39ecd9ad6b2af8b965c99d0d3f7f1f229a5b321b87e1a36b636b926bb66b24cbe99db3af9b532cbcb5de6b84e06a5d8a8248df745392ad19400000001004315000028100c088402814828cd835db10f14800c7292427c5c369708b324c661144286180288018400038801305354e3007682e79eee7040c7e23542471cab3e8a5010746afc592d0001442be9be613fda755f3bff06fc67ae8bc9ca997de96f1d991bfee11f78851670b5b4b95668efef23aa28d84cff32f9e73389ff847f590a00db04c296af65c491abe3239626006d70d79e1b50b2a8e531e915c542949b08c52a423b61c2fda524c05831051a9755895e1a838004331485d7366eea40ea1053646b76a6b3398ffa7bdf548259fbb75d0a2641fe4b53720ccb00a8e2a12e7265874ac9fe1c9598a0014341e72b10ad74c411e5592742c7d0d6560d6e2bbf532d6e3b93e5cf47dc076b5e091e66f6ebc986754743fd6af4aef6ee3a306529b3e65cb17656e46bdba337a7172f212c7dc14a71782b98036879eeceedf35b87e932858369f19ae72d42f10e70a33e4b73084ebd9d2541774ed8808b5d2434c37bda30b660efdfadf39df20e4b9134759d5195464f7fb9e90642fa50bcfeadd576406b94d4941d98490e3b8a18b89e601e2ed493990b7f6bffedf0b768360bc28de26d84ef4c2c1b9172118272f62bf53f842898cdd30bf1538cacebbc17da9c752b2af19a22e18b48ae093c0f38d82df097090c7ee2c06ebb421126d1c4266c9b584e3c7f902d10fe7e96eaa9665ede7e0556184ef9038e40695d5a9bee647059f5c304ffb41a0a73d6cc0dab4cf8e8360a629cfb0b172ee198e0fd13b5ec739f7986cba0f06980554f3cc2a3e4ce8d29a624ccef6d7e09c0c5ebbbe055339800b6ce564edabd1fe63ccffb6a773d2d6812470681e7aa4a32e34cbc41a0e63582ace32718f8bf1771e47685e7db024b98c5a8beaf54ac4ac370b5782cf42ce8c0c190ae653f34ad15f5188a6168b383498461a09652f82a8a1aa1f530afbd18776ff8850cc83b11315bd5bb196e62e3475875ea6f3047d5822bc946de632dd8cd6fca0e287071e47bd136ad41b10b8496a6ae3876c5d8e381bd6dbb1a94ced56d323cf21353bf045b32e525b0e326964b11344d8aeaf6cd8ef23836171e31b7f7181a9ba0ec6525215513c42d561f3f0827d887c190d0652c7d110d751f2ed6c67f011590d1e39c94531a937ed345eba0b992ce21a48d4ebb9d0de364478d859e0dbda75473f819729f61af4905bd86cabb1f27206f3be97e06d9d1a73187a168b7bd811eadd8953b27b00c5eaee05d885db156a8ef6c4a801525026cbac229c472aef2aaafce5ff7a8e79abd1b6ac827e7e458f481e0b791cdb5c7f5f950f5398db7a4ac5ee6ecd3bf9a2e4992188fc8ba142ef93b5a0a7c7e357dea81219fc92fad06e6605d5d4d80d86ad2b506e218a887b4fa9197525f82253494a9a1008b27403dd97f83a2a960c9c57308bcec5825f0250c6b865ee6cc2648aa84018432a72efcd72da302166301ea4f56e9f2fc80f0006b6507f724242c13f630a8cb1277843665c572e9161bda2a4a65e24c5e3a7ad532b5a171a3e3d87cbe23531d81d99a4173d6b28ff6c42bf6eac3fb72bb9e3f0af4d2a42c1e84a9a0d1d271a7a0a2820c7211ea675bb18b356681e60a28be3fcb0dd198b81c2c7e2e1a9237deb891f2eedf971cade6de3593c824b140b528b206f9efcba4738f2be492128ee5401d147f2401103782899c198323182f9deffd9181ebed7962c5bd5e3f9f34fbf6646fcde6b006d3cb342c8618f122078e02c77d27119ff066b4918c31fd57e2130e7400fee29e9010d144ddb296c9d0665c9e3de67ccd0f513362b26ab5abbdcfc31ce12a2a91e98b3314671fc95f9df63b3690ea1759b18b9beb2b84fac899206d6a80ff66d73863c79ea7f39021fc9c8496fc360d121079a2fedbe07955b2751aa6e1516b55bd982b283d37eb7f959c43c9ce3d7b94f494eec30ab16d60d0efbc2cffa157921afe127cfe10a146fa8484968cfa0eefe665107151aad7b6bff957f9f0ec3779c898698d4fca929b73074ea4faf5c8a6a7171bd3263cf97a169e1908adeac7b3d95cfe1802fa74270a3034694de89423dc548ff858e8f22388b915badc1b04d8457c32b64b9051960a5d4ad684b31c2d8543afa5672e85b633537b399a97485d4468f74985ad28b5b624dbe2c011bc1599cb1b98061bd21eec7b63b66924c3254f187d00232fafeee16f3ca502a79ae6c4fbb6e25592463f7ee7eb145dbaa4d7d32ebe4b4548db2520e3682c79a7ee34470ecbdfb4b6755443935f664bb4907bf10c5275e485cd70559b01953e147c477f91bd24db739898e47a03d6e9afa1cd9355437f3a3d2b083c3a44addb3f8613544a052804c2e75674fdbcf836c4d3755fcedc7f2488e7a9e99fe1cc82de48ad6ac17a0a9a2ad38b03d2c57bceb38484b1678aa278d574415bd3e6d355d12f02dca12677f77775506d0f844e3b3a0f4c3cce3ad081b09c3e394db22955bbe392310cc33bc5e76903c2c46541748969f6655e1986febd04cf474b1ea758dec9030fcecc1dc1a3e4d967545d7a00ef220e1a127b326d585dba2f30da5da00b4181abc7c1e3f67a9a30b085434fcca8d848267ba21a7b1a5b7634663975ce777d0892c1cd96672a6b1a646c7ab01d863c832c9b47fc7478a0b3f07bd39e11d8da08cbb2787c4e2e6f903d2721e5ef59a2811fa7065e19d64e69e1f9a1440147a8ee0650083dc729a4b0d3609e47f7f829fa1a9b59b9b06fb672f996d43780981c3e3e300d247841f76494956d40f9529c791dfac074537bf78efd8e017a8e05b986019ea70b521a24024e68b525b16986d6d2f8379c909608178633e390fea3617618e8ce5b833088c111f6d03156e9f1f4d3059f58598669a8eb0aa278eb07425e9348a17472bbe5c27f5a8051a853dc0ccfd5c3f685f13ccd4c7efd7c0d1bd3f30cfcd2c234fc5f4a29d24b5f0de189f6d67d5a7f0a77c48e1b9ca785d1c02c3ac557cd2538f11ac84f54fdc5c9644c2dd0dcec2dd598292c602d2549c45282c581096f4b4a00d08cdf503b70b33e37b54fbdaf4b987b72da3501da27b5656979f7f0420c11b354d47d34f86cb3aebf5aa992819b0fae6e8298e599d5534b776bb1fb69b56639ddd2f2aefee859ef4c5b631a772e7e416d168dd080816f4fd409a194df7faed7765a51b58218501608f5029f9714501e090a281889343c139186a783736bc48c699aa58307a278acbb86e5b1f66a05c6510922d111a0f480a0693a2251a2d0b98b75edf55113c98962c939d21cd9dd11df61ad460643519705930224aa3114acfee91f2d9232e9c8889f12ac2aee97fe262a7299b7c18236e7650740e0ae8889418646be6239c3650c8de14c6d503763dfffb5a45c0222af22c67151eb9627e2686df42edb06e6ed1ccd30c81ab17f10b428b7dc6eb066b5548744dbc33f10c037b9857b166eee6ed0cd9098c744cbb8398241b38253d37584776b7a5f479db366551e4b586f720b7d00ead598bb921c4548f49d9368273500ad01c914a8456dd005a4346cdb2057ce80cec123fbe5bb3d48520c69b367b8e6d864ab81193fa1555ccc08780686a0370be40cf22acc428360e24f018b180e2a27e9598631272b0bff68a6b02531fb6da3a61530c5aa804575c7573c8709557022f7456c2b33629a26c076c98a98a8452242558c13416ad78b4adb2bd2b972a4d3358abf44f088dbf9111c7ec6fc9212c729bc88767724d99ccae0891bde99eae80c2589ee2b3a033d301d63b22c5e209172ea6b02edfd11af599309227ee974f38d9b77ce70a4bd642ef160ffd76e853d9c1fc7a27858ccacb653af36aa478d9a6f9cbf904d437de6771b70b761da17701c138f5f1a1d2dd0930c9e5c7a4e0356f1bd0e429b2144dcc345511955bb471514771f22a53bb5ca1a178b9229cec2760c5cfe549c60fc54b0ddb8f484553cf035eafe0c508b0837671ee314c11f1dbef267fe402024b97b32b40b954080a3e7323f2eb8e58cb71652bab0c1b246bb912190ee56422e3831b6f3a6698d4051b311192485e565aa53f2eb292848669909823d1852be4142869a19752359346daf96e7e49f85f2e152cadd4259339af1658d6e457bf0fd7cced16c8e686434a2fc5c0f89c62d589da28a96b452660a78d87aa8711faae79d2732592b2bd0b3a560cad54c9a9b086b9046972729f17d53d04f9d32a9fbe5d9d0d908c1782bba71f3f0f9749abda0edb1edce33b191de79aeeeb9ab4f6b7c7dbbf1932d9ccca879c11ede04d96dfcc4994ac73d4d8f5e5701ed1038133feef02f4c01a2c8d3d5b4e767a9b512c33c16dd7dd359f04802fa2e9903f2486ce933a53a9b84348997508e10fa63c50dc57d0accadc6004dd37791cffe308d2ec05ce42c60dc60b4484ece62c7a24c495346cc8ddd71984877f02bf4c338d4ef0a2d4ce2746bfc5a212aea38545c1937008cd03e20b4e81887a20b2d0a6a8a96870bdef54f5e943587d33c9eeca1511962ec300aabdd9167a2c59048797045a4b002a626c624e4da50a2635de6b619b2af32e3e0351635b254d4870607f183290cecd3271a8835433d7d10d129b194928d6a5c06bd2e94e589e4ca8ae13c03d9183a63394e3cb220787b8289aad9c6236d35b496023a27e85ed60a14c75328b3ddb69c7cd0ee4c4def6f1b7289089795a029bafe62f7ce585bd3cd1814cfd090cb0c560f7d370d91184594cd06fbea69afccbf1f1c20c31e15c1783c4b3c144da257695487eb793d0041d88b8f60186bfc9b607f2a2446999673216bd2cc7060ac49b079ca05bbe83f83a41dce6fce9afc7b24af4d55481f59de189fff0535159aad329a44a05626ebdbf1d94d18b2657037dec74412c9c000fa5ade20e3e01f49376b6fda09b8b5de67fdf22bc3e040e1efca4a25ebf80f3d8e03da89ea107709c74a49082e39f7944784c2a29479a83c742be426d4c95779174ed814c5b3a66b151ae1a52ddb3355aa2a22cdbff22ae7695c30090c2ffb9b46e33a0b1bd500d69169831b46cb5b9673d57c01c481fef575d16dd22ff5b558de252620cd732e68421f18fbd2c2e9a3624a981e56fb684f0e296392b0ef94ef77a13cc5b4a2eac92d805d51f3011019024b492496b40ea0a2a8b318473fb9a6221970200b4311c39cdffe6832770f85569c58da0be34673e2388156f074aa42edd75f3e468273306993682db5767d3711a0d14ba75655a4c64b9061bb831b6e5c2d0c58872fdab87baed90ef512c184250ebafeae305c60f020fb802c6a169db66651ab9d677a2c971c9b71531809c1c1d0bb6b59a74c33f30e2b32200b2328513dba98e30a73f4d20c3c18193dc02cb46e52684d3f209ae0d801de4b70167bc98f5ddf0885aa2e28ab142cfbc01c99be142caf3b3bd8e8b5c202df8b1ed4fe24aeaefd6b0420ce74816c33135f48233dc4a011ab8dc4cc13fdf762bbedf24257875e2e2d15818a38cb6617283417a8aa6c6a6c36642298cc7f1a17b65ce4902eb64bf247cf84abe02942ec4cbeed68e0b9cdecb995a32ec72683c4029fe4b2d82e946d458721ffa229c887196ddddcce7e20f42f24fb7f012c40c24526581348d93aa66c09d96d1e908df8643270af9e9fd8fd613e256b8b7ff340eef5bdc6597eeaf034621d3137ce4684aeb817bc76a051dcebf2691a296732082ccb6120336e69948ee703f4c50062ec8392a5adb464c004ec477fb56f457a5ae661068608d04ecbb556b577da687a7e67c73627c4c47aae74210fea689cbca5da2f031b5e06d625c1430ac87df6533504dc9f0c5370b383172d82d93511b9d759c7bf935d5230c213962a52a49bcf748044e59be91433e2ee556ff6685ab8fe3b938be2d57db7285684e1e0eae17fb4a2d6ad272da5b729a7c2517ab1e0f550c977ae955c16cb20bbbbbc5a26a2798f5e302ead4183265370dfbeb229a4ecf2398fcdac89e9634f4c60978681e5712a064bb8e2b07a43ccacf7ee08b4af294d8f986bcb304ff82a3204a0a487b72a8f45bd8aba3f8a2333137420ecc71c2d1692e24dee4c63ffef09b67e1dfbcb222422bebb4e1009cb8e2d1463a353529bcdd97bcebd6da2f11c86089e3f20c4a63341ce245ce4ab3de841b081865ded615e8354b45a77bbd8704905a06d50c1c17bf0bb072fa79fa07617bdb52f5d68ae777e033d8ae8be6829de3a20183ee72485f73b921cd56b5a6a3ba036a1e1c4c89355856c4d4b69133ada1997ee7915b0e6e73677a1b48bbefb015595566054de1ea727e2d6513f74c7d52107fd901691a4019422bf1809938bfcf6ec19ab951431acb030ab3dc52a071498643d027a1d41adfdb31dec49acefd200401d66f30435fd0a757f15e7340f89aeee55f573e065bbffc9b3d275ee4ed7862f610d47edc0997caa88ef533a590fc52f082a472957c27cdc09651811966a5ae676a1250a11f912080ede317449c7c84ddfe6659d5d5933c22814764fce43e42cae02fdf3340f4d0a601843d8cae7219a9acf4314d4a5cb93c2eddba4f9bbc9bbcc7effb373a88f34a5bf444435f11eb76f3934a4f404add9282378e3dc15b06c008f766fed68d1e15bcb27a976bd9fa54ea0201d986bb5317f40c31cade6f4b4c203aa967d2871214ee5765e88c8b4b6401faba2b4b2302c0e529385d90b4430c657b15531b57f4c8e3373b01c225065c016998af985014e5c5e8fe99893b300b96ed219b6b5468e4ef9a1f0b960456e299e138695be55514f1e6ae3bc8ef5f525b9884aed0674da427341e50163e1da6e72a2a2674d8678cd01634473defc80520b38ba14dca438d0617ae9642afba9ad3834305f7fd74cacc9ebafac2f4c74d3bd68ee6e827179f52d9725e6599d2f0d79b5668c53cc76a502e1223c729f20893b2cbc85ed5c02ace565c9c6e2d7161e9c96a6eb1ecdb01c9f549f260cd4884599906444059193ea5b27495be531a64c60a375d1caf782b9c6e87deee7a2be5754434dfe11dd10ad41374608285e23ed834a44e2442f4e804e885b556e7a41a3684600e6bd80bc9a21aba2f51024dd1f6491dde1fa6f198ccd8f5ce8fc56d8af9c6fc798adcb52e96572e90a051e1bf94dd36e4bf40d851920eb87be3ed26ac19d32effd0d51947f1956cbfeb13b0fea0e74630915d1f79bb786363b3637f9f3709f894cc5513391db01cce56e827f34738c26fbb06043b3cfc08152ba04e20bc4bafcb94bcc791e7560f054820f3226c58715957015472b5aeaa6bd0fe0a0be6b328e14e95a1ec28928d1d3dea3d85b07d9f2a685333acfd0f4e4f9800a911d2b83accca03ee80bade8909cafa71bc4a738b05d56839f7681b365070707c19a06900a71851ab10e5454f3ca17c793b41cb38e1a70bd96a2c38d9d84c3172032436bf3e81030d26d35c2b1de54350d7f18f24fc4c70898a8ffebb14d378a256defdfec1fbfba3e5bc54472d344fc1ef7b392fda337fb208aa4acba81da0b9e03f9d71871cc1142e40f09f57a2afe442b077d05dd98921afa5289630a4f699afb81ac776a99dcdbb919728241c8b28701b5eddb2aef8e245b88407bea83b0ea893442c61370021a03e5048ea818f2e90f81ed46c7cd0e25827071b00f4a01223fa40171e1642ac94c184e212486ce299b2988515b187ca3ad1c9f45d85bea3fd4705443952de219720fac7e540a77ca02d466980e21dd140d2927d38979029180f10de1c8fed5d368ccc6f401978475de5e12091c62d0149f64c615150fd58f30407cc2bb3923d8da06da6a8b5f52d1529eb9929b32843ea11c5c8baeec819c8afc5848763657f84f6dfa63fbf050b3fd82485e49c30e29d917c29f5946fff9e6b80897fce88e0688ca7e2b878ca1e44fb4b4905502d0458041ce4cd42f948099266302ece1ed26d686d49881806021ce9e148021c6b21d0293424136d047df2ab35930927f1f41c73f42e9bea61765b54af114dde4b3e26234ebf42bd1108696765729a8927443bb53311537c043102fa466c854a4a4955ae5800c7d479abab134b365e59e9c72e83c8c8ab35a728248464794c4858ad14339ce8d4cc57b142dda3497775dca2c1ba3412840d623bf28f6448b4c2ea5d1f283948cb54924b2d423df8761c4f5782c3b12487582925c3a10153ea7bd91708e9196f57115ee4bb4f19708bd23d391a472b981285e486766209210e2c518ca341884bc22cc84267e004cdfe15cac0d7a1062ced52d1d44e1117ca4230a6fb57bc33300f36e9906b476c724b6c13c72ffaa5528c0b5b45764c747b46715688ca847a7153213de367f2b0c7eba016e299455b69d31e82bade1a677fbc3b702fae29a5689e2c90351cd1ce808b12257a0a81e02d7aa7e65f88953bf44ab5bdb6dff66251830ca3f25bf540dbac8dc7b78cdd7e29378071b040de96448ca954ebfe545842dc40a712887f35eb0cd32fdb6489e6cfe715c0e903e8b212b7ef048abd9d66053fe02d60f5ae16d1233a2cd5519050a92992ce98aefb72c163e35ba664a259f6c17ff66fe19891cfcf0254ce9853a8f6884c068031b553d9eb990ec723dc06ceb9419689a14aef187d74ef71675614fb9d753af8feec9d97c89abab3ef8f450f55e13ac0e480edd2f0fc0e53e256809d011cf6b854a84eed9c432a33f348725593419df15eb381a2e730121a881abf7b29e600d6419a008340565bd7c866e6e10a1ccb6a9049ee9a94d1fa35c5c19720b6e25f54c61821879078a03ceeaedc6362bcd0898557b693f587a70971581fd0518dd6a83d2bf9cf1f05f2b30a5f0ba290f83849587a3b795c2d1e350b25e273b5953d8ae5c8858743b5a09d0a2e9d5f772ec8f1226703dd47ef160622d3fd2b5787f17963393184a243ee6fe0861e8d48cd1f97505d6701512ef41da07bb277bf79bb973027fefc9c8a34ce27aa50a99e207e54bb40970cf63c88c8104068a675d515910d24b777619dac0b09b022b7f8e6caf8ef2c2d7c91f476f89de58c90d4c611143a7f772ca2955eb543ff0d7ef5bec1d40cd2b6d4ef97926ae1b98d709ae9a819389e26445727323fc7664bdb1c8f5bad2ef90f1dc82ce6a4682b7a9effaf169adbcc3db015b357fbfa8f9e773061c9d42eb22cd795192f4da985833de501e775f14cd58af70cd2a2c1e71849602cf265dc8a5c654411f73d1dce6997c1c6a02633edfb26da42c6a7006728ab1068f80141fa8d5f9630084026b2524186b34ccf9e3fea705dde4606a797e1c3435a1fbfe8764e4e5ee211e55cd368926eca0437fe0040fe3bf12861dc21bc736891b23112631099456512cba94706e56f50e175c1153d36ed93c85ce8ba194161efb5f9269bdf86ffa35e626f65655e0e1bbd9becbf1853d6bed5685f3fe1b2a08c3be820f2853b5c9e83c66f28530d135641343e3b06498fde7e68de70837ca49b801d8a91e9c8164e23f964979e99766431b86ab2fb4785dac439876afc34491ae029c7548401ee818d7bdda9e00a795c303b679c9ff49ea7d00f5587c8faa59e6744926d643ad35d84e2f8a67ade9c476dcc8c19d56ad6ed1456ae4f0284815427c49f2277f59fb1faa29c6500f1baeefe9cd0df345e1a66bacd6adfec55cf2a2d2383816b48b6efa53ba673f121e915511ebf3ce246deea71578581a904a5ebe1692a8b71ea8fd9635ac82084625148c9fe6698191dd9556b745f8214b257c41a3962df35058ee57b6dac70ad887e76b6dbb35515cdb62c320d3b8f284cf94ba2e53c8f8a6cb4ce4a49b607125112ae54105d918f8b80670e8ac050a41562c3c73436c8a928bba6694c99dd5d1bad666879203eaadd31cb33292999a40d16d26befc6fdb6bc931622ea8fa0333843a397118a10892309441a83b82f84ae0fca6987d56972dd553229fe2ae1e48a80b6370d030bb131fbdfdc1f63437bca62fd35c4790ed5ddf0f1b3079231a9926c135072c58740cefedf2ad536922e3012d7482fc2ed5ae6a401c11aebcb0dd28a0149095ca1f27a8c13722c782698351ae6f7819686799733555a5b22029891d732a365be636474ae59cd31dad8c2972e42c6ab1a04b0a43a9bdcf1e6f1edc8420f10b7c2b189e148c60f05ca77d8ab184e0ae977fc8ebca2c4ccd8d7d03e8f46a4f7797ade07877c1459732a0898f62f1dc0476fbbe4421c6e7dafd02c2b577a4753979b583072ac9739b79ee952aaa965e31bc34a7ce2441f7605457739c0ecf54e8cb11c9604679b01faa0191869a98bd942d11ab993285655dbe54e2e920ed5038c202d18cb7a9baba3dbe66e2dd025ec5685e2738e3c10001ee0f0f548a71e4b47890280b675a028de6f66a8082cdf4a539cbdcf2959c1a2dc148efd55218fce24d410d401b64e5a43a2bb4bb8876c75828931a1b86da756dc2ecae9c4c4d22396129a76274c92f0df2e88753bef5b4ab8fa9a8ced53e2352c16b9b005ee3b5dc14855520f2f59fe210e0b82351c0517c1d8d16927c681b2c7bd1d42a6962093373ae9294fb7cf176cc3eefca7eb2ada239f4609bbae5bbab837ef2156d40c830203efcd2f0e289aab0a138ff68540dc0712551149b4586f631ba07d6f85004ea84b28c1cda635af7c3b231733798faee680fb909cf1c6f9322da48d17072fe433b64c60b256950312dd51b932087bea40ff0ae20ea468cad25d79fc0ed37b3b9305f7864b111ba58cc4ff7d23fbab72170baa458590fde830231d98caf2be60abacfa9150243bb86c0db1504281c9fb9679f33eb505ed5204a9fb7e5c8b56b3dd3788d328e2a9d15b445f9ff60a133f4c6c319fc1dbf886d6c98a2097041870447cdd7738b7c81424106fa275153a68206ee7f21d92da4153cd83c944f827cb786538db0334224640a096050075a01044200eb135ea712472cf3fbf96a67d9180f6ef247f0f89939e90b159fe1f3786adfdd6aeca0e092c224b8006b17751584ee068b5f5695e2966b684aef5b11aeb8a8fd2044275161210d9acfc8998ee926e7ca16cf38eee05303f39a2bca2f8e081954032bb78bcc3ab956a5b2060648fff8b5177eabbb704f1b5555b5de8b80ab10947cf52c1909c7784b8239e60d25455f35de3fd4ad6f6614d6fad5e596c59cf48c98787491785c7725a7fae719da7e6bcf43499db4b14a3120bb794bd99f31404a139f93b8e9ffe6e401b7e200b3e4c9f6d32310473021aeb814e8916c3ada7a5e82ce39246b052eb9219e1ff4d5a00e37140b96dd9d18fde32874334ae6ce2a22db0e31318acf98f351ab5b0e4637282483576f3906ac7c90a40d2a7fac82d52e208be812eb4e05fbc939496122560a7be667e82763b7d60d6150c7f09e10c14ac9c62cf551c45ce322e62fade950371aef2f37cebcb8e1ff09d302d90e9b85ea974e892d929f383ade43a8d7738129fb5388e388ce8a429783d6602f1c1c6cdb7128cb14afaac30283d87f5745aae4a74e5a72feb93da962033d6f4f49400fbe0ecd5bb40adce3f8fe41abec029d5eedae924d0da2df33912e07e09f40d9f998872bb06deca410f84f025f99f02af712e86d3e5e800f7a80da30889e99d62f5887d0410931c807f70ae12790ff941721df1fc85f92a6917c4653c41dc00c7af993400209ecc4d0e8e3c3d743aa198220e45935903411f05e4a033024f88724ea8178fba0eda71999c2c00f2bf73c888875bf17f96c824604cc1688c602e13361145d9ddf11cd5f1edb9e71f5ee2def31510c1578593b75c86c6585b6ae3f2e89d822507a1f7af736133c8d1ff6c9f89d3a09cd2e1ad8dd53a91b9970430699a8da912a6efc3c19d82fa8091e28d703c37d8d2293a3d6baea2445212e8cb1dde0ca857e13257ea888071729b78a170b6b6a805335edc1fe09946f59fd029ef046003dcdf9b02b2272d82721cbecd40d0a8ac8ae0b4e9f4c71c63076a3faa191aafbf096b33ea760bc74d5d78f0826b7b03657aab5b67d9f2636ccfcfcea5d9fe9b312e155a8d0971a78288f78b6a0bbd1384f40366383733f59657cd3151592a4d7a6132cd704c3f025224170cb3172fd66f49efce417cac2ea4f2882d651d3824f9a0320b493df7b410a38ad2722032a9921d06c655cc0c2b5bc8a6476b27e132c237d22d5880275f6a48444be5584281c0d76afacbd2820a766209cdadbb523838535cc5f7f69be0f34e59ebc9bb6ef9327a24e79b60b0105382b5b7ba673ffe1251c2995cbef1ee3d6efe8251ca5e5deafb2c9df5904a401033dbd0f3fb4d09a24b9d5f28b29ae396b4fd7100b6eb4d50e62d1af29f018930e0f5809082b0abd17f1eb8baaa0211f2bbf6cf1e2a51528b71bcac670d6df28e3618c8419dc63d4d5bda899e4bd4c004f13e7bc6f6b88e287de654a32417a8e273a90c588404653687716d675302d3f58513fb6b293e9a9d0c6c868a99a129e3627bcf25cb531d7a79d5aab4413871c8a9ea543d5bd16dd3b811b6886772a65c3bebc39d4e59378f1d63a2f67d48c6dea1f6cfa455dd8fbc38e939e2406d4e7130095f732b307d98aa190a4a32a92510592198e817211b43bf640b8b795b262a9a6274227d840f7605b33181316faf3b49fab71ea06b846f07f4a95baa2ea6e24a28c514dfa730b88da2c698641005672ee338672213a88f17441710a9858c9c64d6c7755689596291866eaf2d7c09c76433cadc46400fc522429928a004ad3f748042adfd9513c207c0208048295808ce7992216693e00b57b8c4bd24614389fb2c86feeb60dff532bfdedb74da93f268e4dbffbe3c0db41aa8ac39399b7d3b0b60d8879bebff4edf4cbc52e84ff592521a3dfc3b01e98d42e1cb57d1fd6de43e9f77534d6b2922a8ca937a0a6bcfa4c70be0000da13c8dabd6777bcdc073e0e08b95f3a3efec6cef025145dd1fa7493930c8561e121457a653ff46c225f1a56edc767b0e61e236c3529f19beba65e2362ccd2715117aab698829e1af7ca222bc91bc101aa9b0a5012ad6c9d4a35dbb130eeadaab51978c5cd5b04f0a4494fc432b1e485fe3c4b8c0d42060d5018f79b3642e4bb017fa604a00a25016e62f72ad335a8ca08516f516201b7d116571092a6413bfd03e3b1714d83eb6f2aceff06eea44745634c4d44826fbef2f7f372da0ee6bbc34cea1033ff82d7fa2a831f0067ed02f0f1a0d79cb26b4d0521da7259f234b25b8b5532c12659de38f719861703dc9b4cd3083324e0478259e42483a61d6a44e84fc3285bfbeb65c2c8e4960dd0feb02655f7418d81ccc99288abf0486318c8fa04c0d82cdb46adc20600e841e4168baa44f5bdfe615c66395e141f76d45e31599ccd9189afb234564a1a787c5e1395ec8cdf4a3a863cd134f55e49409e6f4446d3c18058fafd1295b2e19d0666b4fa2a7268932d307ef6a9cc86e1ad21da32942b1593c2c3462f9d1ffb36b2d88683ba6d7bc918b34cd5f1d412cddcc2f8228b34e1d96933c6c39bea970472655c53f977348e1412f7d0e124b478cecbd58f4a5063ce2059e9536ea47b0e43172c2a7c76a8e7bfb3d84327cd7ca671c957cb5c8b8d14530791061faa95277e8410eb3bcbc6f38daf0d97399e6467bddaa5c55dbd309a32e6fce3ad2e8940f2360dfc8f77b194d2381e55a9a4823a2ea3238041a4238e8bba966123f548a933c8501b10bc6d8639be1053a756c20e46310cbd5bb056ac50edf1ec389f8ec1c041074590c964a2122ade63a3c7857efae06971a23a26a0b18a4cd9c2e94cb146434a928b73a7aa6f71001ae4f61d0884ab43299117bb1626ba9b503f81a302380eb1c9275de3002749e05f112d29f5d476e25b9f97b905787cb7c70ea14f39909099a739c17f7b532216e5dda5a7156e2d907722b66c54ab23a69e0506039a33a18a3abaf859e19e1609d7b3115a4a4bcb6e690e459150b416a587202d30bb5395677059bfda741626babedf072474110c7b8a843a17a216ac57a9925142b179f89e537d0cd57f3739f9ffd50a6f4c02ac5d8443fddc2aaf53bd9f0fdb018c53db11a97405ea87b44b59c85014fdf0a398caf91c670cdb0805bcdf1e741ec4d36ab77e48531bd6afffbb96e6218f79d358a84e313c7bf4805660c38f76e0aeb106fee5197976f2f9cb0e6dc90e1c35fd09f9c68e5f8e3f45d8115ec6f8fff05f772c8dbb5c0d44f4d90ce03336d579bd68d22101ef72a9805e9c7b152bfaac0ea15c8f3ac21b1420c820d9dd580163fdd743b0ebe9a26bd0d6d2197296225e7b18f3e55d8d26538c5728edc98dd85567797aef9298e00f13c8e9bb80a2d956c944b42de90731553906f8f41be77af8ad36e7cc4a58f165b262e9b30466e1f817175d3a1d9dfc36ff68a0da3121ebb820ef72888c0331d0f06b1326d76979d477ad0438ee97710cc1c86a03a21ec778c4bb7b18fa9e7c854df32ed9658c0734da9507b8e628f4f3aed61101d928b65bdcc84cd9c70ed896190f58bfb1be0d489a4b52dc003987a0b4689642758041032aa394fa7b8e04b3011a01c11d020944b2cdab16776f0de19e3fe91e0df5af3b067f760e97245383f4d217e44a815cac4131dc5fedd83e89302d9d38c2997ed03f868d997f3a0083ba568c8ec8cd91082f34175f74c9c9b37df2627262079eed831048b345613f15c2ba1b20bbe40549874805d1635716205574248f2001195a9bd87dde611de01cccf96932a3f51475930903145ed5a5c481416fce8ae0eeda25e966034254c8624db7a5376772e735476cbb0ae36264e044f4ff53f3870f21a7199cd1764918a0c628b054e915e41fdf34d9aec399197827917b2bba39c0471ee85ad4c56d1aad9f2aabf35eecf0240817f197d0ca5844acee815619c6afc4df5c393e8c306f5f26aad7528f80321577e391950e62d8679be6e43120e186df31a8091719dd077c32d82a2f7a90a2ee14eeacd131aa6dec47637b1ea59f52ec76e63a9cc1200f666325d107dcb41239f72f0ced12982d2b6cea4f16bb6f4fbd6aed01f661c877a80610ee1007f5e1cbc83c9507725a9068bd3f8136bfaef15da0f27966aa17cf9afe610771f678c5f70f79d0bb5bfb4b693b9ffd0928c0297ed92f60f06563a4fe924757622554f32d673493135cf3a9ca668e7ec46e23ab4b01735857d18a6bf06981958d7ce7829fe0c12c34c646732fa1ce96141a65e5b99ec6ac4b8c131eb053b459b91ab9b09cd1a223bd789ae6ed3e1cf17e0b2c76b90d34f1cbcd5a002d8354ef18c01cc9dec8dce67e989119841dc514f427ec2d505453fe1d4a932f46bb1f8e7d9a65cb9fecc307362a5d7eafc3e4605e7b2364673ba95043a78cce173ac38c710b2620e06c5370120c15869299bc88b48eefe98278a83d35c46168fbdea497c34c613b31d4e9cdedd4628c4639bf2526fe1121a1277b03dff90605746b735eaeaef73fe08ef8e7e333bdc8e9c00c245840db48aa3beadf5538b3ae43f098870d05125cea03bd43f6cd4c055eb8547bc74ca32d6378bd0af58a846ac0965870a49fc74a7ca9f4369cfad9c21870220d76c42fd4eab9ac029dd336c0b6afec4f3a48c58551d04c6d15b9bf18fb9c2c2890ada1f046b88fec1391e17c60ff0bb9bedc271d6fac69610f17e8ea37fe74a176c82702f4cbb1c1ed44071421a6e296a1945363ceff1ebdb3943e72452c35ccf7765f378c4103cca698204c23884cb6048f4576c7cfd9de75667441d29740018de41d9e68004915b9a715dcb142480469f44b298bcad5d7f50a110472c98efa412de53ca818e04066d57415b8ca81013cdb59a006c157ea511efbdfcdc58d86cd3b2d4b96b209a53591cc17e3cb00762d5f62255618be056110092945a09310fe890d789b2c106b8b6c8abd6b54066246732f7e5575628795bf7d4e503358f93505bf1c30f136a43740e99322ce49bf9fd6ab4181a0f1fbb2755d8d993d3827107d6d6f8cdfd7691de9d8ee7f96212cc5df2af3bf437a2663719d788c39bb36a1d9a59a7b5d7ed4428581c248863bc71c33f3f8a9064a504f41af47904373c583e04b971b4996907e236289a21265b375a329fd217223815c2182639a4c54c03f038f01f2cf302b8c1df57710c4e41e58a8dbc444ee2cdc05aa4c168ff056099f7d1a9626c762e9992b459eb28c3f7db9ef9d4b63160422801b711f0413e0bb555a08336aaccb2aeedd111bb913ca118319b0061ca2bde5d6bb509adcceeaf11d2ed69fc6874cf7fd4021d8d7c8d7da0c3db868499997379cb22c41b8a7a408e7e5c1c5450835fa9e10d3cfd2fc9740245f260d4a0d2bd85cf0292cc27e7fe4858cddebd82137957308175dc04811a349e7f85f2c1ca58dfb9e04cc843b66042e0b40443f01f505360e79d0c0d0d7493ce4b4e7c2e00192cfd717b0b39e5ac2e940012072d8772106cfa90d9bc180a1c6dbb3ee876a19952000e51dc6450c02caa5ca0a11a12014e4239947749683af4f55352056abb144395d9df4d8bf67d4526925451039bdda0d1c744da5e4575145989108407320efdfe34a1165aa3c4608c0bd1a333bc68ea071363b64f8e228a9734727b43eb87df8410c0c1b05ea3eaddbcd7f53bd179817454c7ad158667143be6f7c025410386475aa646816fe4d30e6510abe49c585a5e9fc61e6262a17ce2b153c192ed05ac637a34db89f52a7180f9e0022be561ff29bd901111b5f22a98fd0ffb698270bdc19718833fa847c615f253be815c2570c5c9f87fb061cc63cc50741a52160eb2f6154dd41a74658a480420a1ddcd214acfa5f54f2dc0f108c4753a11d7a9b8641e7c9fe3744ed3d183a6b888fda6417d1f27c53cac96ec2cbf62c6268b240234e4283f07323b1a7617f726151f2e16c668a78cfc22507b1a164f81e2b0746cdce2d0db3c63f04f82456189b9469d6768dd409c370526c6d3fbcaf1af7d8dc7675b6bd63e05444eea6d5e41628db2035349dc171e3af02be77f81a6822d85c29bd5dea00b6d9e45f2afcd18f0622fd179c0f05209da3435174bb789ccebe4a627f11a95692eda3d858310b7a036d4a0eef435c74a5c9fa344a9cb37983b97343b4d2f50a5a42bb88854055d14828fd99e672694f309aa168d63b7a1e5b8441d5f799ae4fafb4c7945d8cca24d40999fd8981889073c7dc3bbca9b060a0ca2ed5c130920b3b0c03893bb8fbd3b5b9a79d92bd31cff9a8fe53deca91d0f3f5c3f7deb1221d58a894e0ff93eeabe670a146b2f18ac119a02b2c74edce75900eb87b2573e091034eb1c8daf9c7a131de8429320e6d3e9116e789e4a53fc619cea1e670b48305bcfb27d8dc05452e1f851c3978d076bcee70dfb8620318b1fe7c9fe3ac3305d737f7f51a738a500a392e7c8e0f2fd2dd7930e5392daaae1ff131cdc3800f1bbd40978907523751d460f1df51c0e8c8b51d9a0dab61b7f580489eeeac852ff6e8ffc6f468e8f9d780a6a650e9ebc3abe5d797e021a0f7597f2c6346252b7e8861070c1159adf9955993bd1eeb7d0ac155864f4094f9ac658dc85f2f32cd9eea110fb58512ceb393c8ae90aa9e31012d08ece5927312938ad0669c0a1230408bf6bafc395ff6d965c0aa9b0687a66639093cba3c5e8251e5f1fce2502da951447c43d5d1acf9985260f7a092b7a5b3e929375e635bd6a8829a1a1ceb4768a912f6249f6ec062a0ba9c0334b4b0017de97017fd2a7c2de60ae95f3be46460010ee4bce35e1f7ef15fbc0ba6970f8dc202fc1c3937fa6771c8185e65fa062b7e1b7f9a2ba3e7cb829abe5f5c3a527f60fecd62d271dddf1b7ea1c4ff19dc0dce809f26476b9f37b55b8f74012f2352683abb408053415a89e912a48b399f0b7df5a15cfb7436c94ccbe6d7ea3c94e2464e51a7e438a5ddba6f93ad9369884be0034d000a401ecd5b43a68ae644e7761f535b081cc420ec76b50165d94ae85a8e6da6892538117e41ed23cd75272cd98b6ac35925b03a17492c23875231abfc0f16f7b90c3b58f4f92d8b361d31696d704bf459abd1cbbc3aea66baab105f7b49b8bc79e7193aa07e696442f3a327c5d097f0bfb2f6cec225cd137807a8f87cba1b9fedcc816e1a0bdd6e2948f07e2b6fb0b71e3cb8886f37e0bea940119a8208ebb4d06106338d04081291cdd753b1eecdaa2270c16b2093c71d53aeff1494867d0c4bd4606785e24b7ac400301901f4209c54a911466b494b4fb183f347178c0747ff1dc6432d6b1093dbb36e292cae9e35fdae32ca9486c3c0b15441ccc27a90c062c03766fd07d77ae2ba283ea85f829e46c0231099d836da229458d5c700d3aa092d510b2b4ceba1ec11650b985ba1709604ca8804c3d3c18a4811fc433980e7d99df65515c5790f10410b8052080ef208b145ab7ea40214168461ea56d93d45a8426c30cc2699e812ec620d71cddbf31eb3974c35025e7acde5dc0ef5ac054a6e30841c805986cc1a42d1a501c0abb8e6934e1b56a40d70d0499de386a66287de65e84962c86645ecad5379ea2882449035d512649a6f6c88a91085de595f4c3b2ebab803d7323aa00c0a7358a47d83f2bb9094955494fe6853495c4e52133840988ab8a72b948578afcb2b209c126a344d56cfe56b2ae33b5b89f96c7d6aa35b788bd06ef573fb71a06075868e4744af71a4aa0b665a4fe79576219e41f2ddb8c77c498ad2167f11aca8c9376a0a199cffa877ba059bf4f461a1373a93c450293c3300103d08642af2b8c1c1ccfbe3fe4442194786d3ce3ca22015bc59259a5ff83908dcbaf623efd2c0a908191fff58aa355d48b44b1a82dce31b36a3abf6837882daf5047d4f5d546677f894b956242799e3a525b7ec3e825f4e003055567cbcee91b265e040d82581a7c19f7952547b41f157e5752f88bf60c5fe62ba56d0de6f45d130007c4de68ffc66af75ea5e89235a5f4b90a00647e79a3acbc9a0e33691e4cb1f18d0a7810442b49bfba1eb77450bc27fca89d6c3f2e631e3c2d040cca1f5579891fe1752f493555ccfb29e675fb7d5ada7e02c6d026d049a3534b342196b075e135ddc6078dcfdf4422e62400d5830233ab6b24105ab164512913578343620f3713d31a6f014b4850811dbeaf506122feb2041250a77720f52e746a6002ff074c9ee00829ad2ddcfcb8abe4b1d6d9832f9427673e9b8bc63de25ceab83be3aae9778918a1b82d6549683187c900bbe43571c4dc3673c480a3de7c9a8fff20bb320c84f2d20ae2bd98dc9cbd220330e27a78bcb1ba279b35e39ee18806cdfe7d7f6e3af5bb03401362a78d994227fd90ae65a5d63996280d857a8999b02e215da8d623fa12f3444ac98a94831d38b07e90f2cef0ec21ef7edb0e575a855ebb0a5aa777093bcf435601b82bca2b22949166a751e5ff79b046b76d730b000dcc8d75f56e7380e810bee5775d244a05f5b8c9568504f39dedff95b53ce24e68fa1e98af8cb77e5631e6dbff8f3deb62620b04ae1561aec71c8df9ce27bb7b7d64bc0b8de3c2d8525778fcfa4252e51ee6f1f366889365494a0853aac874dcde127c9941911b8606f46a9cd6a85dc027b7bc9cda337d5bfb04fa7bc193d2da402cb1e9c3f3d053b6301360a3806692be13812076fb9bb56f519ce815cca7aad43300992772fbabc34a7c2a30dcb375080de4403dc5a109d0ecc2f8de6f19c8b263398b839708952cd354ce68dcf2c6561110235908d65ef5eb404f77f97fb948b8c82108e41d4337ca53dfbe15f31db3ad487386435507d773e59f90a5e5a1a3651cd45b02773d8f71e4f8064efa3a902e815e1a0fd98446a1491f84d8df6c8e384115fa6d1ffa3265a184d3b98ccb21e885831ecddd4ec90663367c0620ab7810a0dcb19f101baeec58e171e4713603e69ef69c946a041c7cc1ecf088ea2956b55c8a9ff8b619d52cd08da70aed2f47a20fdafcb5d4f8912fdb914b328670ad64da9b0d335190d3399de7d611b9c6256b3bb128914c3ce18b34abefb09db252f2107fcd82efc9bc30190853c512156166a03f52a45922e9892f7c017e58bb3e8a7f1f538840317ab8bd931dd20cf69c64b4ab6af0cc35c56e786ead61cc8ccc8bb3c3c6bca3a0808ac03b8822f4747141704e0a783b94aaf7b16327a9787226d4f90786a832c5f2ef2e6b64111d9015073f4c8d67687a45db95651307ee1df2974ca5ace5481e1f1ea2bd6e4e41cd4036b4097fc8ca3467e030e785833f87654bb53570c28f61d52ec5aa93e37dc9f72ad8a2a30058319f8abeee5846d454267c231c982bd9a760a09dfb7824dd7ffec872384b01efb78653f9d84baa3e492c22a741d1b90fce50e79a877a28b80eb23061ef2c3b623cfcc0cb8a13c819cfe049b0b0d376bd0ddc1b8d2df6f249735a5754b6d16b42607981549d74e0538d751a716192777bd23357bf88fc3141d404bae76ddf7fbea845d6c0ab2472301c1a6d12274f3d662d91f3cb94222f8cc10e161063d204f11cc2152ae5a6116fc294cf47811e202a8296874577f8aa65c7d838c4cd9169052fe26a6382fa9f4ce52e92a659d41b6312bd1b50d2860b1c7dee8375c43ec40900223d9e35bdae127c9b459ed75dbe4f9caba443d2926514b6440a11362a04ee0cf73e407280e6d607f3e5c0646148228296f92815124cfd761bf584c90ee09ba17ae0d0cb94f414a65808a15360559c408fd0cc8bbfce46765eaaed9a4e726de66914211222d901d6fe45ce9b8162f1f60d9a90b31dd0d49972673afb202fed1ef043630776898c49ef67adab577e509a0a1a6f28f7e3d12288332ed022898c6716206176d8c9a61024a9e442adc7adb34ec8046447b1d0af89e0853c9491e871012940af7d30558190e150fba91bafea662a298bc726de678e109046cdf7ca2beaa64534234650af2c3b3f5e39580f8c8288adc95090e0a7bcfb26a7f1d3cc0c815e96e2a1c24bb699ab000e8a08753cbb53d45367116130238a4daea5d766deab0cf1b75376b3e3790f21a524a274bd517334da91689bf73e3cdda52854175b0e0d9058bc99101266105f0a24f29c8ac91420af246a264fb2ba2e0ca20e361414424f9a4529d1b34ddb9ce7af76f6c977df90edf25fe1517815c1e021de8c90025f64a97a0722d9afad4ab8766758d2f4461bfd50c9a91f8ceb27952a466a7f01bf4aebb3ee739ec2fd7e2d0f1eab3336d22b5a8dbdbe802362b14496f6a6c747ac0ae1dd13c1d084eb33d7fc1acfb77623477a5acb3db14f36572abe60141fbd4b56ac608bd45462f44d28fbf432647e2bb105443529eeacc5fec45b665f5009d400c76dd6d24a99a7a4ff2fff533bf9e931085aa92fc5c9ed7875032bb3d523bf8851b154cd57fcedfe464e908c1e984fafeff246af3f8082094a92b8d10302f001af04abaea3892e90d9085a04ce7c686dc64c9bae933ebadf1c556abf84f6d6b9fbeed692fa12c6e906264ffce27a9be730150c76ac24735756313f79fd5693f399bf50c823d5fdd8d7230d389afa31f829183f079f5e68822f1d0e891fc03b0bc384550475718205b5f5b1ca2d4b5c15acb571dd3d99de1e346147958693b1945054ede4800f78c357974c4168ce71a78703d63ec9996fdd4e911cc6cf43a941c14e66f2fbd3c0433497b45546e9e244fe17ba133022791eac05f9ae3d27fbbac14e401ef94321f6e0129fd0b936f4f125978873d5d6931e6fb576a4cba0c7834c7a451d8baaa5eb7149cf7d86c43fd55e3a9d311ea3b297f54193d741dfa7b5d024499735d23e4ed0a810018b7437735935ab0d0162850f01b1be88f6f6124892448377a29ce8e4149cace7186409e9cf99d90090b3d007d3a83ff4b652280772fb9f3f4eb0ac5888c06fe1d7745769d312fdb9ed0186a13b03b13d2e241a1e91ff2371de143e982ca0b4d99b74296e47b9671e3b7d90bf9858be611a5f1d433e0fbb5be5000c03405ca82d8034ad2afe97b6ab6421cb6877e2a8000308f393007a7bf13403087068176e16a16ad61a4b5cd0546a601961a263f922adfda6950131b301632e0ea696c1921c32e654b658285a2c267501907930620641a2a028dac050e0d103627a2a6a154304de401721c3443c231988cecf63b6b853ab5ad4c32b4a1086b4c8b1e013ba52f26c96c8254ac201f85eb73d53a56eda2e4b2caf05ca4c90a9ba417e111b6d17c84082370b7e0a0951594cdc1e211d33c11eefd09653f4028ce32b8697b8837bd56e216e73d71669328718c2499b453ee458745c462c386b8f3f129382e3a4c54217bb2156cdc131286d9b68bcc7af32731a8f03d4bf602a4fbf9e14869f0aac5dbd281de986bcd23c2e00a91599f302ff39f6947aab6a329011f584191a58637c7a44f105d6b39b41947686418dd33a36df60e621c91e2ea9e0558073f7a20385eb359ee016a9c697a17ecb78ca760da0b1330a7de30694c6518311058a8502e3862e507779d14149c4409ae8b10be32d70ba038201102ba80e295da21db101ad32a508796f5e0597a49c03c04f33f9c108268c861275bbb0e12a9d57425832dc42b682d64e3c9af3e157aa4408cc48d4f16a59a7f4860950172077d2b4693275be446499064f113846ef55aa755b06cac23d2179e3a951b528866f2872a4b5b599367e0c9625babf1919b3891836321e74c30a7701b2cc68608ba2ffbd986139883a47e937ab0972719d0813605bde34bd62e2106baafa345c03d6a07b8c8181ec883bdea7d6e5aac78414ae6402e608477a9f3d5d2f190f817d8be1a80f78d2a304e01ec631400b92f577d585967ac41f03511ffc78bb54a33cfc21317011a46c1b076f84cc4f06b42fae1fcfaa409ad5b93e5f6be0f0a60b02ea5024162856e3c263758b91287a95b1ff7d6ef443b6df104560a3fcd454f76fa30a5bbf6aec7afed8950e5e899ceb586b5f7e4f112f5abb49705059d4f2339a709d226c4e8ecf57e300d060b4e753e7571a0a71d7262c974dbd14d16f4995241fa9b9f8abc9305b81022f8747fd0ebfd7d49c6700535909afa36bcdee8cf1f8f5e757f4fe51c4d37b2d23a58abe1027bf70c445d4fe540143f0dac1bdd65477e96679efb045119353e2d8bbb87ebef4ccba05460030dec2f105e4ec8cbdb6237badf6c66c009d4d9c048aad6cf8274b5789c809dc8bf4c5b48141010977d55ed8a5c1c6b1cef254fe8837b1d0617638e48474d70cf38724d6d5e7442e4cb0185415aa02b463002d052bd4ee3f92b7641c1a33e609262611486e3caa3a7671e275e84e803b408c408f922470b1129c58a563941e8d4eb1784a614805c5a56e547b2637062a1cda4e737ccc41f1487874e0309c8c081dabffb52347eef0de26a73abb341f976f82ea82297612051a4f8240150ea5fa0b07c6521572b4cb827e4657c57e9e8e08362e68612514b75efc86b805af9cff34cf78d74f7f808d471506be09d0488297c168917044de93c68c2776e578ca6b1108d79429a8ff8c721a520192bfd560ceca52390b16d06c8b649aca0bd9e9f3cce24d2bf1fc6353aca13f95b1a555740685bf7fd9400615e76b6f64221419a31d5f762466068051630aa8fb144f2f15024c36d239a2efba2522a7d8ab84d563805cb19ddcd91c716b476c0f1538cbd034ad0fa73f29ea898652f712d060cbc2717a50dee8a4f2fbb86a350796e8676ae1da0737a83736090944627c91775baffeeed78a33f8478fa68e3cb496e129e41da07bd6fb2c41f17c7c24f803372c542a0ad7e46a7e0d0c753d95e60e8913ce694113eec95d280ad4b55488db6b71350fc6de4d477aee70885b8a22964602ac0351fd7aaae7c3a0f414239d150aecb50c07754bdb8c7e566470a58f38ae4f6b4eab52ea4ee2b6cdf970e2ee609179338d5ff506d46a52621fd44f5cb1550d507443dd1044e7f22bfade45b97690906d19f6788ce2a4383b64a791dbf395d0b1cf9076b1cda679908ee4a7ddc45a14b725101b386b17e493b0ccfdf647a01f5f115273c2eb9eb9aff8e20c7c2708091262211eddc41434516ff09dbc28c36fc11b19c6e44181070af03b30f8bf4fe41e69b27bc186871d360d27b5775f93b7fe8721b28ac5d814875eade81f8b656c69e00ed7eb1819534d1248ab61d52f84f1e004a72caf5c8df0846beaa4e8e599823d78eb383bdaf47f70ff92ba6b4fe16b352a57b370777ddc87ae43f2b9f7b3a8f5c39a96f3ef7bc82ba51620f89561aeeb936e806e0f3b426a4ef0626813dcc52aaafa76fc47a5c2b943ae8594d04366b1672b3486073d7e298e802078e9464a56fe77548afa6d78f996245e623568888b98e57b1a016f3f15d1cb17a2bef10cc2089fbe5a2203f6c505886104be9b8c6c5e7b3c3b18404d5ee37d254851d495a77ab348f0ff4755e0a38081f1fb738cb5864735903ab0523cfe065236f1e6a206dd86effeb3c58565744e24d26f813aad283c6dca20283303724143ef81dbd25b625c47fb7cb478cbdbf53d6391e4a6d6df4cea26900b91062025a850b70b0feb918ff7d43dcd06e9ac72b0e7bcb43f5db7e8be0200c2df9156ef91f7aae0f97fb8463080cadce9d4507be5cad2ab54adc68bc22e64fb3e3ae2ba58f99e8639fad36371b889e7f899d214da0418dbc3c4470b87552d8508a14b7f7201090f522b13a64b765e2c42ba472b5e0395681c8fa2b272207738c4439377e2832a87bb14b6fbe7d90342e9dc69a3d8051c6fdd602dd5d1f197f862a4023426643e8f0b3361c5ec2fb76cf0e47838fed41ff25114f917fe9d96bcbee1dcb3ec976a9e2e55e8ab01abb7afb39eba50a9506845e5ae168b13b2f55f0d9347482ed87540484d3856f17b81f7bf0f1edbc3eef5b7723b06311d84617dd769000aff6c2305767740a4a44260d4408d5ee0fe6d867ac9080b378cf41c80f1cd3aaf18fe8fcd5b15f203da2df112c103d3c4083f9f184b6ad1f22425b1f188cdccc215f8e0311a8fa202ecef048fa9f97471db630271fa81ced5d8f4788d350c0f1d85832c9e7611a63ba30e31db103e2140cc352ee720e4d6201c01a96173a37e3478a59d6a046d5afa24b3473f368af8bf0dac0ed40cc1f7ce3c1647ae998804bb353ac7f65881feb846f681650c856ae1a2c6cbe71190bbe14c0a948ae24765407b168ed6f25d85c22b7f10051d8aead42d4d915b70599c57662a8d5fba727034a91af6d705d432a5c688a0389ff69164787251c00f9217ef9fa4cf00c2536ceb1c039db86ca2ba39f1b63921b941e1d42c7e98f0e290db381b42d7ada32fb6f4b07127ddcc992e2f178a610d5acc487934e0578d07d1b0a5d9bffb75972851df09b982e8340f18705f0af40143c3db451d0821600952638b05900c965a2ad665938d625123f9ac3673cdedc791d7704c3f318a08aed37e99f7eb27bafc2bc49f93446b28ebf749c27dd644f8fe245a0d21ad06b16b3f185ccabd017d1bfee04c4ca16d92a3c2f8fefe16819161cbdbbbde145c0dfd72491f31d25c6b9fc661a5904589eb84a1c48aff52a360ddb3ce0dcc9cf64740d1ea822a08b2145aae2a0119063906e4ecc3706571d595ebb63ea13034012f21fddadc68769823642b863f9d33bc241523b221ad37dd29893b8c5161422e46ffdbf0e78800eadb9da845c5d5321bf1534213fd2430dee2aaa31910b88427e2c9a42eba7ebc52dab082a09142ae109f9f5bc392a22e4574a880323c0132228988b0dcd98faf8eb8698bb6160aaf3c5322c010bf6e1688bca65d5f791f5236bc930cf7abe3dd00096f3efbcbf8ffab1f34b365ec1dabd2b1f859c7c9f062b49ed5ec2009bf90f50ea4db1f983d5fec538dc358a2ce630a96d0040a8af12bbc73b971fafe33d98a6985f5def9b6c8c9cff80b847f6e144d0ccc5f0073f57bec3432e0bc4d7de3c05290edbdec0777bd31e7a83466405a3dec93b3ca3f9a7ced23c5961574568d370293ffe64c7bf559302ea6bd0fc16c7f59d372fca6ef20e24d26a65efbf96721e4889cb4e610520543158643f43b18598499627fa4acc8632bf9e5317994a99918f8c654d4bec62411b76f68680ade5500215d49acb8331cf63018d51fee0f665d01bc3cec895d05bb6cf8f0603014c2a1eca7afa0a2e282b7fdc63b094f293a1b6ca80026526290d98197b521e2b5175ad6723ec71cece2e6f650dbeac74610154c7c1fce1f0dcc713a6a426afe73692097042f0c1b9b7bc4c4e61bbefe2818a979ceb0404fe60a730e8b8df770815465b5d7ec234ef8aeec66d14ad2f25102170c8efacccff3a93946f7350143368afa0bc65de027db0db117978c073317d4838c9a5b140abfd68d9f1b136937a1edd86604f095f1cb094709e6b6ff66212ff29c0ac106d161bccb4aa66f383263bc66c924793909b3ec7e6fca29f9fcbbb7ebedd1cd0d5102f4146359f1cc362905d0faf32a863bcfc016927c1f6486b31193dd5ca7f3de37561ed4e18f14f39343c148c978b5be431c4349b511fd220e726eee022de4cdddff171d956914f188382e5c83b2317ca21266bc761aae7648b2d7a3243c29d96e3df7bcfcee559dea2f52f31654561fbfccab1390e4c2c6a436a66f9a453f8c2af2e647b577788ee386ae6984d68ebb3227edc559781751ea0320e1626b75aff587289ce04a6a1fe04acd230ac85f9ab58b9994a8732bc22c2e25434e69f88b34f68cd3e09a383c228647930c903f88fa2ca6b8fc7f5ae2cf46b99da9e6efd7c3855406ecd3ab83abdf8edcfda9283ccd95d17b4e500ede81967fcd7798c2dc9759894ed46ae3ac9c1dbf228ad8f15742c2b2efbfdb287e574822c51140eae7983d9958e40ca8f689de5361685c20eff44f0cf0923d8f5b6de6d28c2e0239e15dc55ecd392dc68f1063679df00b8178502d7943780674dcf5dcfcabbcbb2853615c4130b5706aec20b074d2a6058565c0b7a12cd0e6880fcc221f9d6c184ad336ae21d71f187bb0014d600f2f7ae93a764722d60ed8c96e12105a8acc09ca0daa53269ab0da10827c5d8ba322b2943953bad198cbd714bd93c06f0e017104a2c08a56cf5121755040e68aafc11ca5d2e8308d62a4d761ff257a5119a63a485624ca89162a01abf7f0227d8ca07dac6e54fc06b236e672345f07a03828daf27705a77964ba15db181912376b9765ddf9a2a0f62ec03df15083d9931fed94972de4bbaf6ca45018aaeb49502ae6ab41461c6b50e512be7bb36d590068a55045c1753e0c41d92d11e096329a5642b273ea91f7042794928ba2d92398d9365aca43b40c99ce0b2868983fdbb5e3e4091bc516848fb6fa85f6f2700a287d93874069b8600d101529681fa52890e537f331a79ac32b1930a41f18503666875a87be7b3ffb93266f78c182cebde965b2b114b67b21ee8bfdc3486d1cf380d17c52788ddb06dce4c298834dd14a032f791edf2b096f83022fc0d5420b69ebb9d739239f424090556cc8139082419ac90fc030f91dcfef2df8cefae62c01c9631f1a7706681e1139c7d25a2e6541cad2507ceb3c088487e8de6a0d60124f07f32d518c14e9f35c322793b89c74d60b6ac7988061044eff19ab982d0070f5134d2e1d5176be0e189470c26bb94e465eaa37f5564fdd6f2b75fa8449b964adbf95efda31a0786ddbb54e6337fe6a6a0a8cd8b5ad81cdc83b9e42899eda552da5c0fbe43b7bd40430ca4db196904acd59fc9dd6d2d9a1fcd896416c28677ba6b741f49e36cce89abb946c8b658402d3e71673f0deda41bc7fb6b6487bf446e944f04d7cdd1fe35d8c11f1136e2238274e338fdb982477072d596b83f0a4c56bc1a294ab6c403d4e2c93beb83bb2afa51d50f603704486579fca1a56c6823972cf7886c78bd09b99251d1dfe0684bbc1415c309ff09a348ea73477cd24f1fd2a87a78f2cb1003fde4e3b9786600eb4976e066c3f6418e25dc6ee42f9e587de0d233524e872570d07162edfa3770a0049c1c792098b8807e3f23b04569b2e5383648e10fb93b0c8d790e1a0b5bd85a7ac29d15fc5d77ed867f1310cc37fe6e37ca687a518c88dd5535345fb8b4719f20719615a0db182cb74f9576b3b8f1f8a439c04172a293f0c4eb34759cf7cb302e6ad0ad0527e05891b90c3b5122cf06957b37846e12038225f01bf6dfbe94e31b675c36c9b0c65c9db294c0154cad324a36b72413ee68af50c5e8f7076af3628b2230fd72bf44a721bf4cf9cddf65c027d642a18db7dddc0dbf1212ee4327657149c8b0189933b3a83a5da495d9fd6cb0a1c2f4520419ba8a26d79528d715f10335a0d4101d4f6da509a3c2e3cd32c0efef852114644575a7ed150c66813c0b7511e9463182517aa608fbdcab48d152bb9ddcb0494171f5253c86f8ff85cb1c905e4780910708422172cacc0962edd2f9d7b08e56756ef9d0965228d5d7c128c278b456c403c5756467cf3764a5987e0fd0693cf5cc8e75bca9ad130782410cab4d51691bf04f3b4130cb514f4de8b4e1acfb85ef18c3e689b2bbc07cbdd64ccb88fab44ba4cb9d7d7312ae0d4ebf8015fa4f9842c83268f2444bac6cb057717048eda2907d68174555cfc1c5bdd6069ec460da69962490f44e20041f43c23ac04964388e08d1c2db030734c10e6e84b17ad86bda100f678967dc6a9c8523469f1f362c7c1def807f5914d1397b254a21ca9d98dab989fadad6fb853412dfc3f63168e9e253ba61b39b2e3ce0165c5fe2de1730d6028030b144c3b3db810f773ce4f98dcb3ee5084a62ad2d076a12515d790197eee0cb0dadcd55cad4614d7d13468234b78b4d29625faab6768dc806c139011752dd36d096c57a4d9e4086915bb9d019673286ae37b02506283f13f2e272a31dad8dfb9eec4e2b48541ab683441fd5854d498757c5e0b0d2b3e31dd9b58a129671243444a72bb75e92fe7f32779320b042e8e1a908fea768a3c292e09320b415865054d17da8168015f0721de685faae1564ee66900744c59ed23d2a839c6cbf582341f8eb41120661b5363188a5ee9431a8bc29caac697344b6abe260a5d304be621a9a00cf171446d6d1de576bbb97864c7792b05161c7c34f455397eba9c509cac949bf71399bdb9ea97fb38277204bd22964ed90a0c0612060c56f7106af32da9f387ffe107d9d099771e509554c1fde9c36a4b4ecd4358bcebae2e29ed99f953e910b5891b7269cc9067f2c06a842afd18d157f86eac1be24a29c9c436ded017fa41a3a4adf75d5bb1cd61b35b2545070086dfb96b246bee7424b3323f379e3e398a527611ea627fa2423ee559034f58e01c67c1573971e42675fb637bbd0f9490d8d8b0a05275651c73d0f6ef35656b58fcfc8a09ba244e2a820769e1316ef82788093076d1e342e595f2b663217e4d2f3260cc6b1c74d844440bba0e4feb2798c03a0a0d045af4a16f65038739c54ee840df2fb9561d32cc8ff709213191d6631e313b95fff0de876c058e23f9d1eba5ffc141640f445c520680c96e8bf7431062921c5a5eb5f402efa7db7efb7998497e7c0caabbc6bfa51e7311ef277185fec4d8e91e0c8d8438e9c651be58019da1fdcbe97d1a046b9c0114488dd928618f71202672c33234fce51cb15f4a6b2733eedc8590044a72aa83fe8a3f79eb1e1bbbdbeb00bab7b2282e801957a693a4f4f0e5954cf88326ca59fb19c002656c12b5dd22e710cc14e72a947050735a09f168582d782d459c5424944948151263cf65b3ae5271bf377e11f235e82966524b1e9aeed22dc3ae3544ae660aae370792182f07d7767b4cc861193b79adf7c8716ae767b1760f958be5290b6fd1b48b0a12bc79be3a6e2330038bf7268d478f83cfefa3baef16f279a6717e616d586e693846fc49462ee5936632e0aa65b05834ea6fa78e6d4a9384bb905a1fe1b5d4fb6788a109113d9be875cdb7e33350e15d038d44b485c55f637f4cf50b8b6ac9769e400d78ff8f8a3ae6c7eba2a4be0763fc70e6350c6a1a2a64a18fb9c64cdfbd52428255502e0858de1a04ee3297136e9c5e588b50cda1cdac118452315f3282082e86b0c4be17073913718bb8346dd8756a47e59845d9901718d53a7b58ded58f442d2a6f1fb1d35b07fdb0cc2cddc5f0e64eebfa18b0b5e6ce5fe687cf1f030bf7c5146dd74c83b1ab7e104cf47ec0bf01947660ca532a778c02772a1cf4d23827d2b9dd3c79e0b9409d0d91cadfaca5056eca7204ef42ed44eb1b1e9443d81d17aee5f0fcc95723961d6d5f6389e86a1ca0b1ee7a30f463e39954207a5049280d2beffa264e30192325a59caeb9573eae34741cc635bdd2322b11cf113ad00461732dfa3458d5bf81200a66f9f79779c844e20845b8a8b7b67a0e64edafa20858c3567d3fe433a7954287fee03240d983a7cb93b46c5fc80ea253a622866a994c01d4bdfc3e235e551392b064b4a434e40d45950006c38b448ab40ae852f9b52c45604235dec6145f6a66bcd709c71396c903a196258591d6cd9bcb7b7bf70b0240106b47b5949f02490c1450002bbf9e312d0fcda6ce5b27e8a6a82192700054155ccf1a118a0d84c91603ba9075492bc4d380275e43651228582481a1a7ea22c35f20be435a52b664b1bbfaa8bbc86861e077f48c84fc8df7b8eee2f522c5b0e756df61474d3e13a95c6fc96e92aaf5da158307abecace3ca29e5b7066b2db049290491380a641f3bf5a87f12ac448a27990676571ffc4a4d4d1446a7bc8f25f5c5bd609ddd79ac0120dfd0520f4f3382e28dfeab834166f4bb27af544399c117df06450565394917c364a517a7a10c310d18f3bf7ebec3605b704ca42242be2532b00166d6a3534d74dbf16085d493c6852eff9a4b002f6098923ea8707857e1f08087b0e69cab39090569ab59df8e5f1035e4e4d21673831592b446a05eba947988535ba06b3ba45bccd54845d26d36a4a8acf5b6782e12a55d90e55fede0fe5f443b57e811114263c668e91806da972a6f6d3b8e2c66237699a1c5a1ca4396a91b1531d458adee2592247d21fa938709647b1b428d7ec9e783b807ae1f835a8327aefc9f4b359364f9d6fcbcb1c4870c0034c53b7dbd09452e449bd224f1854843f7d54000a7d288613225a6c66381050261fa72a2f9d7912240201ac735ebf77e5210e5befb703ca4a6300d7c0424639ed8d09e06c41ea3a21a87b3950392a2f783faac210726220b492b38f90b45b46e671223379a6e606cbe98bf66488c5223c665151290838b8850524ef971ab8d918c70948a62be09aadea955b105ae642224c6762c520cfc44a86db2908536e7ac4782fd7d2f4c4a151aee54982a1b4485cf6b84f88ccc5ad420dba073aa574da294dcc89232b951f5cbcd89abb9eee2a80c5a3ed2114c21e24537f47c7e52b0555d7db24b300e5acb4375ed269673149d42a5e1e64bd778792ebe96445ed33c946f00b0b1182dfe1aa888605d893c398d48892d0fe6016fa3de9665a7422503d10995676f1b4991112141d832422f23ce245c08198b451c7bf9d64b9989e4b5575dc3f70d5b95746a1b8fb26c7ca9490670908d724c3708996af3b9d664b044f7e4950210a2896d13e54a8c1f7d0e6e72c37b53195b11f30d1a620462c02c65470738455bddff7ffb1cd289338ee9cf3bffd169a7650f9ee481544605c7ce19063a87777f180eb77f908f230738a822bfc675d2a4ec8efe0d24fe7fb14a0ea168af7e41bdc822cd1cb72f73e212f3f3d8cffa62197a2795d2857d0aad8af5771079c68935f2727d1930466652cce0dd947aac90e430b2a3bfcce0326298cd480b9a03f2f721eeb4506f5df67403669ae6c7688576cb9dc4aaa0f7f05a11db6c48af522f74a69b506f740c4ff115f2cea0c5d2a93d8d591175445daab3c24e17e0da5b6dda585683494afb3c7423874a235a89de6ea4dacc0a3c48d6e06d83687faff2fcea016a2822cc25d578c75ac9f9b68ebb531307f8ba97f3645361c921369f25656f9b38e547bae0685105b34d0d0e8cd3879973c2ee6f1d99e63a669534d9fa7baadbc97ff89fa9f161ae23dab1cf8e388d355433c816fd73404dcc6901c10c6cef13b9b5bd7fbd03dfab0c3100e6e3dd88187d78b3922eaa803e0ecbe3e6c374cb85dac51f0233f075337912e82e1f884e4018ae22005352d4d3d159d81bd40a5ae914f07f26926a2af720f373be19c36239f2e9b50cbda9dfc7533208a04eae2cc68431bfa0418a0b976d3a204f71c2820c4ce2db4dfa5f66d2e966114c59d143841bc7e82ce9a2347ba002a6d5777fa5f8588185503bd70362e15a845d103230c240cddcb4d0edada2be960d095aff43f20d68f34271cc69cb4d40260c368066823d03b837e0f44c57a602ca1b70703dfdc05bf56f0ef9acd46bad10b58aaae821f91b114da682e1ad3556f4c28875c2e5a3bee0378c12624825c4cc305d20b162ccf76502e3450747f0795210099606813a90863cea75133814bae7767698a76c016faa3ab21b61c5498633d55466f2d990760c00fe48e7d64417e3a431358598fc88f0b78fa200165b40e5ebd40e35decaee6fc696ba546c929c72813384f43923a4138742a443c24c5dbaaf6184b6badb6dd32450eb05ae99032bc5b1fd2204ee4f1d149f836b084573b97cfbba9bdf45b42f81f25223bd427fa528e3718e6baca056bc083b6f89994c838f910eb9b2ef60e2c3f59f9f05b1c6442df0844cb906aa7aff0291659954c1dd510d9dc99bcb9576e21e297124d771adeab31478adee2e34f27e5352d8a43e8499e2f3b07c51babeb5ae7491b4183a66bd84a1b6b4d124e87456ea612a4da147ad49b5fde21ac34ce49c8ac1e364fccf0bb099a75af914e1f60166008fdef134e3999488461f9bb227825df4ad75ca08ec27ed200cd0419131ba6335bbeb9bd623b4d9ee96dcb4126f427e8a458567b7d36f13487dc140bb2587fca48162adc67cd0769bcc09d309aa6d09b23650adcfe97652e00d1e24f1d8d2105e0523ce3deba68e3eab113c5aa633637cf7b1ee413dc288de57f0588ed27cf8ba0732e5a90bcfef8419f1acf73f6318f7610ab07902dddbd5bf84a4068f5249e6d5f6fb9fce44c661759ea3ccc76c031e814453d05cb71461b84aeb028b6143ea3e0dd4231e614650d452a423b5a601db29685d05370e369f4939aaef07c8c1f73f17a51b5e82296cd0299904a098a7c97025eee55d14b29519e75f12f5dc09f4baf55bb8e58c4e061c80dcee40b8cbdcb662a596e9daea7bb60c8ac2a08053ea37f2584c6a1ef5e2e02133d8abe25848e94e61af5381192c627f54a121934a703ca941684582056b24a07f26597e77e2fbd1946fd6760593e9d316307dd13c1cc5cd5030680fd9fbbe537813459e0f28c39d16d1f823a43ee45b6a9ce15d5c0480fb04a5f9178aeb1adec2563e0008b56d2882464efbdb7945bca94640a5b08c60743081ce4f6734b7e06e248c2862e84f373dbd21591dbec59f971fca3e6421e61422e452bdd18390e23bba217535a0907ee2d6e8112ba4e646253fc3b3ed9a64c791225174c1d9138454e59698ea223ae742e713764a6302ea9b9f1a8874b32b211cb9187e32db9f52475d1e32ff6f1e12e1ab1cb3f1d2c6b05298505a41591c202ca5e7e2133b9118c9175771e7e88a6cb11e7684493021bc62746401af40634583445baccf58f5a6a8818298b28ee91cb754cce90c4cbb821af31847361c368148d5ac8cb183e2c09643e3afed286a28ff69b0d3c1ae25127c05ffb0858edab6133faed63a282cff63dfda33de649d93e123009f2d15e7a524640030e358c407b157c827cb4eff12d763ea48c407b29db47821afa4be0c3dd8ff6d1107dbc411c7f09619deab2b1cdd67991b91c5efbacd142b14bc7a13a2939be3d1c1e7bebf4598eff7bc45f375eba14563e51617fc3a38149f439c206907fe3dba321faf41f21295d59fdfca8e58b928ba568f4c5a42f1e6151f2d45ccd7c03b13457ab29af944fa43c41698d2bbfe5af66e6929116a52ba59371e5473594ba7858572f041bc6a335a6b02c1fc6a75fcaaf76257db61928bffa9939e77cb2f20060836d8761793a9ebb16747c7b355ef4d1e1235e2935afc36379dad778ec53738477a6cc873a2e657b2b106ff968ec816032cf1e83fdcc175aec6bea65bc50478c97144384f1629037642615f6a9172f05ca97f259be7c50e25c2925273f8787c36b216fa1bc76d2445de3f5136fc9674d91bf153971e5ef68b234f453895d20d05f03096e3c0ed1e7c627a58c5db279e5c735983a87bbf27b4a3bd12f0008f5122d574a19a5a419364390cef1252197622e3197984bac8bcc6426a5c8921ca9df94321da386e79814cbe15c5bf7896126a0946294821527d6ba5a514a674b4ab9c8cc94524add23bf5ff756c3a5cce73c654f2ffce60c1bf39f93e38db367bd724e2b95e0eeee320773777777c7648c4ba639991963c6d8c3306c323327ed650c0929e7a44fe56cda22e5e7be45d62cab5ce4dc82512e1bc7338b8f25e900c93927bb2acd2a9d53c75f2f74ab29c0376d01be2264bac7cde71727e63b1a0431df3cbbaa7508114782b5f94a31ba84ad52003fd8d465242cb818c3dacb485850017e81050d70c02209ab821dfac1b7018b28f216635800d104c3304c054b2ea840cb009ec06207af08e78a0757b2ca6abff21855bca06b34acd8c1698d1b97a051458aada2835395266b650bee06537448582cc9ace4dcb0e6aa9e7170552a555293ab4ada82b7204aa56a6eea6d6eea53a92b7eb8a92bb0dcd40d82e801a8e57e0123515f918070516fad407151cf483c705284aa904191aae474485600a1564a6b2e0dc0a5b40a1f5c6a45511534e01e84418333785812f2c1aa38d21281d0c419480c61791c39515153ac649538f2e5eae80834260c1555ee2cc09d54a471670db09ce69c734e71842b6bffc089271ac663871459dcf9727e8d72e7dbd51457eefc0771a494524a1b3421a59472dae008cf0656a42062fe9cd3872f9cd8d003305a70848b31e79c938389020c1c512461f5824d8b5a6b553d11860f9688a2972788d0030a0a96d4134d442726cb830f9d1051504634e1a29c9218628342d4219d407b22c479e1244b41940d0a317aa84318695014e107fa6429b341070c668582a21ac50e3c4c178448a25140c941c2ac86bcc5350a24807061082d5140e1c4851428457185095c2062298a1cb8f0c4c82ba2310304cfb03097ab141145006a14e1c40e4554013a29420758952280e0a40726b4748b992a135dacd0ca8526869e0041145104af6856b1b10d10b12b82cd0013e19db8a2497d31c41bc6a42c66d56312bb20c03e31bfc5206ef99be0faf340c4bf566bc31587ffba212f6d552c0c6dc41bbb1e3f319f51c1b9acdb4b09e2e8a834f6d3b3bfb8b69f2aa5a1bef03b893e33fd98e4d2db3310fcae54e6d3a25beeed1ef81e84f8e27acc47c45b3ee3840d75e8e09d7ea2973e19b1c5dfee1a18c6d3088ecb1db8ae354ad17ee35a6b0ff6b1ead724d8ad0df382284563217e7dea15c9324ae9ccbe30d2c7aa473f529b6ed9e37b5c62536c395060b19f397eb0fcac060cc0a0728512582831f9c497b10138f2ebc962fd5b7d5f911217507ef09dd8b7d773a77f8fdb2db8ee188350843aa5ff832ffdb02f94f383b0aaed60bfb9234e82d54b3df6d9e2a89b882f1a88a3f3d957d4a9e3e637bfe370b4f9126befda59c72cd6e834de19b99b2f1fabe37f968b3407da4d2ccc6524a3265a2db0db6524a3237880646485910e6a58ae09891a24a108260840440d8a3080218bb5d16a52032d60036ab0342f23f5604526c5d32a0c16e2ad9dc9c308e01300b0e38fc9bef10a41ba57b4303100f35e6830e29c80009efbb12b7edff82c9b8bbf79949977ce9106e30bcd205906c18285b1f4910e8e4dc3505046df12843484b0d20eb604218d20323a16383b7078ecc03140eb1aa051a8020b50d244af95ab594dd178de110eb361aad1b204be846c327a1a42b00ca94228c01d19315b380de756ab93d0a3b16d31cae8b1b52933efda36d4dddd1b7377d7399239b20747c75fd8ecee7677f7d8e5deb1bbb7d34c0d5863c0c72efe2ea6408169107b49b1ae07f65e986d15fb9ec6b288bd0cd2458be130c7d2c9132a08bbf87980dc18262d8679956a84cc74b78de3ce89329f64661bba7f5766cccc9699ebe4103ca1583ff5a6645469c1a114aa4255e8052e7e55a494b40dda066dc3c6c7e8d3261b9f37ea853acc1fdfa56b524a29a594d99c2fa59452babb3b7f3dd2203fe52ed863d8d4a2820df2cca47429a5942ea59492c1adde60e3b39452b2942e8fe0a72da69c5272ec32659729bbc8c984c904c90425cc39e53bc628bb4da13ad8f82e6990a997ecc8c86808a329514d4d72367199b2a949ce262e534e2967939499724a2e7236bd787c9fcc2e19e717b99c3c2544e614fb28eb9cdfe3d393b3004e6cbc731ac166e34dfbacca4c6699967dd56483dbf7f4f6d17e9edf13fc8d7a717e766adadcb6d7e6c7e9fde0bb3df5f8c6d82c03468d0d573d9be296ffc74e0c7bbb1f7fe727d303ebcf752bf2965d8fbdf97d27869d9f7db6413965fd2f321fa6d3b25ff96fb3004e3c09bddac70273d3cdb0d9036773c28a0f4337895fcb567e58c18dac0323a594527ea1119c989898ee7e791a2ffcf9617eb5aa55af52a9bed008cecf4f4c4c0ceb5931ac2fe6865dad98c8bc3c8dca0b8de0e0dca059794c66647e62626038bbe2602a0eb7aa41b5adf2d26112192691292dd5b5bf2249e4f5b76ebdf0486b1d0a853a9d78e774ea3adee93a7669cff18ee4b4df78476e9ba6f18ea66519bbb4fad2abecd29ef2cee9b59fbcc33ba9d75ef24ecef61ec63c18df68df9ff23076f59f3cca3c926ffab5774fb24b7be94d76699f3ac9937fca637679cbe7387bfacedf7e117f2939a97a4ec5a99ef30fc9e993d244f5aa6fe6552a99998ff9f92b7fc1fcacdf9e6b2108baf6b9af5e9124f2da47755f68bf7e6ec69389f12c8cb73a72398ee338958ae35e5eec498a8d6deab680edd95b42a53e0eef43c5e3e4ec943f9fb9c8a30c9b7d6cba217d4d03927d614552e443fa49d9a40cbdcff695808a7c2acdaa57de8af960ed7d8006dd47639b5f04491874432925a0529fcab645ee7ab40ffaee7fd2b447c27d32d05cfb1aa7d99e6e6603c8741880dbf27b1c8dc3636e75fbf3553d9fbea755a7af764e11ecb3eeadfdf6c278bbdf5e3e36ddd46f1b6fcdc9ab39e7f9957d65fd4217279f7947fbd3b7abbf507b1b54a47941454d57e39a93af79bd3d12fae10cb79381e6729f7aee1e502f12fa2128a27df745b4efad3f24f44329f39bfc90d0f65cd62bf3fdf621e13e294d645ee6e96f350e78ee7e707c4fcb78d55b13c717840da07a99afab42ca7ccdcfaffeaac121f3f337d5b7b7a341557fdfa00ae6653c1c3532ab97f998d43ccd3391f91baa9f7926aabff1d65f333fbfc6b3dc52fdca5f377ebe8ca7fa1e87f1c2005cd557a47bbea87ff14223ac74431074bbdf842eb32bf585ddd7bf3d5feb6dcfbae1d1784c561f0dda0a8c98811041508183283e3cf3d9adfe0d1e68d8fadb56fd45c3126ea3c947e66f883e43d48749908fcca67dbff4b46f85fafd80faf2bd5b81ded63efec9930dbe62bee3623cebad292718563e8b85af97952ee60019e2cd3ed60af5d23742af0c7eabd2152e98ecfcdeb6ea890436f617951824109b7cba3de6965797df0af5fac73e415d82d82d3ce5d842561ffbfa19e6dc7caed7bbc9427dac3275006d6a6aea72b125d46fc1a873642b5df9d987f5a994a1f7d1be12b4920f957eacf3d15fdb9b9f451d77730ef3fafb01fd45b0fa2d18e9b863a0e2df6c1d4485c7d073e707f57cf71c9fe3f6e750419eece9f69f7430337318743d36ddeed9f568f693b3fcf9b2b080db31b05658c0ed5f0037dd3032bbead1c468cda6c635d87111d6294fb27315e8b8f543a2e3d69f3eec4512996e8d4b1c79e29491a79550ac6ea1fc0ef2e137ec5898efdc39a0629f8fed6bc7c2fc2abd96e26e5e91ecf96a5e91ec63617ddd0b5d855b3f242adc1a65bb3c85a0af0cd87d8a7d2cd82eb6f86c5314b1b4165d6a72f02407368842ce03d2164b785ec7288f36c6529c174497df89cbafbafc85245801d9380496b1ec807451dc391e6ebf6d918b6ddcf95d3ba7b2e06d08ddb0044aee6cbadb77b92f8caf6ddf378c9fbd772c047953d3edf19edee16a1e17a1cfb77a45e8a3629016fb045b3fdbcc96ed2dd333bd6a55c3744cdb4ef54bc746f5a9bba6f9d67a6bae6bd3c63a76bcf1fd95c586a561fec81ff957917f26c67ce10ae60b2dbfea88e872eab35fb862b141842516a13e2e62ed86c74ad1ebc7589c915fc6d58ddc754f0bd51f6859776466e62fbef65d2337333773a41f63a4941e493243e7546c6a571ac4d260fc8c1081e185af71aea051609b99397baf34f8168b62ccb237d2491dbb3b7667f1e5b6fdb66ddb16330f89c77844e784418f02db4410613771fd91ba09de6116e294ed88066583ee99d751fa8829ae431147252d0baef30be79c7372546c3f7fa1ac517af4b420ba31f7eb12b26460e33b958d19bb825d91f259baf7732fb179bb7bccbbbbe3f01a77f71befac777799777787797777777777777777f71fe2eeeeee38eeeed57d453383a326d3b04a3794e54e5d4a05f3728325a3847583663523a324060c18d58b92ee74ea524a2cead4714a68ad7453a26595e6f84e89ccd1a111d939e4155b70f4cc112d34976a15ba3e31724c3774a62e10601fd7c23e397450d9857dcbef3a439d4bdd85bbfb7b8f9618f6ad79365b11b91ec52676e4e3dec2cf7077af2291d8f86347b99b73dd845fb6871bb95c356a3e8c6b5cd6cfcbc894f8f2acfad4c7eae476dc5ba6d6128bbe88568b78426dcfcf21022e53c746452ef126bef64cde8a356b27f1e6f9c75f9e14562391c7cd8845456e9dc8976826262277125b49a9a8b97051f21797c8d35ac49bf85a70616a2791a7978837d1ad75a2d84eb468a9f9c215eb0bbfc64931ed84da3acebea852b3fb81dd1b344966acbea4994fe67333623a1b7c4b1a75094530315b542fee728975a225525cc7c4c4e44e2ceaa4c4a553e294b86c182dd2a2a8480bed732dcec4a5c1a8466f914b6e8cbec68d436057fcb05d0bbba2141cdfd960f943d7d2ccccccfdee9fbb96226c8cfc6125aae9192628fe4a713aa7c38814bbfe1b31c8db3caefcdd3d41b1f4b18fcffda6715bf65aad5ddb3bebd495479388669986f5b66958779fcd8dfbc2eefbe91cbed147c3beb0d2d45b54ca9e1e75d2ba8ab179f577e93289bd4831c62aabd58a777a48b768582b76d9558d0d965ab1974fc32f03649ad6f508b2e21dd495cf383f1f9006654c0f36642c38b458f98eb1a0c4963ddbb24cc5230a214123a5947ed635f6fe713e730dd2efe94a64bdf31174315a44f7a675cbe03124b1ec924f3d1aafa65bf235cfa65bf2376fff56ec929b7b2cff90afd86028aba1c807fdc21a8a2ef5519ffea84f7fe3066b8cb49212b726c1aefab1eafb576badf463cb4ddb5b6bc79ae6f0dad12d6eb5f8c64fa0975ff4f945bf1dddf2c7be70bb61bdf38b4166a928bd1d382028b9e8fdfd2bedea9d8d4de6784dc5309a2465e87d28f6a5d0a04c815d724a4c0576c95700dfc8df648f14748c6cc8615c39a473c2958e01704de45100dfe4e01d156e5060f0a5c32ef9f3637c92d1afaf7d3603a25f310cab7269cae0d421a961c69578d4f9e062d1d6f9d8ae4f28ba3f82f5892311b5eb9e4f1fa134189f28b1db0c341c894e78a76ff7f18412b2dda35efb6c72bc63bbbc46c0adce7e339c86387d7dfab1566befe9749ac15f40521f0ca8cf4883324a2cf758f730af7ad99dbeb3dffd0cfe02bdd57d4d755b87717d7aa95e4e24e097eaebbe1b724e9f0eb77b1450b7bae77ff18274ab63c1ef0a7cd371dc6f282f88f5f8caedfef49da058ec27e8af15b8d53d19b7ebba0f756ef7ec5de7eabea79fd8fef89143eef6e985ffe40569b0fb976e736ee66562663e2b1353ebdfeebbfb5902d3bdeafbf156f7b103d260f72727acfcd87d4c2452fda97b98d3cbab4e1f0bc54259d4dbd3a34ef6843afdca5bdd9f76b0a7eec3d5ed7e86ced1713becbbeed9a6fb8ac4a2d9a726b82d08cb0daa61c615ad8b51b4eb9f7591c84776fd6b17857cd4eb12099273b7813962a3bf69596c162bef64cca33d6fd66b40b7e847af278506290fbb280a7c43a906d84561f016cd00dfd06ae90b7449763d2aa5f4638c315a22fea2f1894486b0f6535f14fa58402fac98b7cf3e2f1fe423559f7ad5c7024ad1c09280ea6154413ed9cbcbfc4b4d7d2f1f7b2b4e919bac1cc795b7e8c378ff42e7d00869907e7b400d6adf4f8354c6041de01d19ec0698c79f3e911778277bfa30748ea66d377adacb08d9909788f00eeae96780794e4f5f039cd33dfd0e700ef7f48b70cef6f441c02f1d6fd19fd1e1ccaefa272ff24dfdce7306eb731e65b0fee66d0cd6d7f1178a932e7df6fada47fbeb117dbff42fdfd0fae9b08bfe098adddc7ba1418abaf523c22e4a9ffd6fb59f7ad587e32dfa2f40f653f56365f5b38f95696fa495b4ac6a35ab0f7a8bfe69079b42e760b9d90734a5c1faf1eb740e4d143af97067add507ea9e7cb0b171b0f10ec7925f5cb71ca65b1e39de9827e31b7faec1d6b2d8adb9ca2ed724d75ff4954817684a833cc5c636dd200873a3f6608b5f37cb7ad8254176a11a1004e3c0cee96f4757ae87777a08107f116100ef58805d3c14e01bf9d2524ecb62b51b72d6f9f00360b1f236734efdf60360b1f5fde7e9bbed39a0d367bb49e9c7a2d91bd9329a51a6239dd3577e922c56de21424e4416fb285bf7eb9efbd67efbee7b0e48fbad3f963b7d23ade44ddb9b8f72ecc43a23421ab1c929de6ae951640fb51091d785c9859c49cb1253176ff563515af2566b51aa4a995151d2512735166ff5917614c588e3a4d745de92d2eb28a827442dd44344714888a94b149ae1486bf196901f18881c252d49e9e9e000e9a95bcc13194e7a9168860501f601bde5ffdef228e4adf68e784b7a44bc253d21de92de4f0d273d203838e9f5e4e0a4a7a383931e8eb72427bdff38e9adbc253d6bc349af7a4b7a2c80959dc14a8f0501f6a9de72d90918f216c8498f05243f18bcd51fe4b371ebb4590103e946302e237df1837cc55c19666e7c18dee98f30ec8a32d4db57cb627d75e58d4fead7803d7d09843dfd3e1616560d45379c6fa495e6d785d2f53fed6081a68438389d1373c4079106fd67f7d3e0ec6910c7877f83cd4bb6f0c6652d872318bff0bbf1f9dbbfb0ef3718afea200aa101668c31c6c8aef81e5bf16f7c8c4a4a428ef4e0064764ecd84c8012853ef56cda61981c815235246717135309619c77c5894f66b0f20810b656cac4c474e4c81115143664a623a4e61df9354b42a6c1e460436682b2bd98c15c8cf47041709d92ee74c56e27948a092818daaa6cb02133f15175e94920423a8bd44b1362528aeb1893224f04d231e945b506962337f222214474705216757a51ca89b261d5ae62d738fdc2f9be756c5ec13d874d32738c07f0c2c2740eb78a1dc6b0c63f295629b4610392152cb0e189248c80c40dabd665a4329eb8a7cb483cc8610d185bd1e83064b208c21658ace08c2cbef0342a30450926306083871900c194c4134af8b0050d2c43284388223ce1c5510f16a5140013765eb69203130428b2f5b2951c90a04866108159c961e86e97ad3461a3d7702d6104932266a0841437306a1fb41449741835a805549a8cb6a8428910d478c2a70a2a866842858c13302142900c94a0820aa32fcc6062061a407409c3084e4c21c4141d7079820344d0c0062e6010c590920fc188043734f1448726286109679cc14df082cb900cb890b16404dfe25a18074c8c8104222ba46c9101ced72c965042b542c4e4486594016385480757bb6c85080b256af31bb513467d74e5092b188a201714c1a8054760b1c610429e250442f0392aa3890a9a60804614a38612436432a4022f24242424b4c589932daf4587036891afc396e82de4d19b9c6c1c5fd873830d13bfc8c3374c126f94427fbb51250ebd0f76e304966f84e996cb1b3e08aff2eb7e649c445edf912de994dd4a3d61e3cf2fda25fcf894131b8db055ece5639827035d210addfeab55e6d56fbb014e56b5c7b4ac22f19034d03863256cf62b599379d4c3784828babbbdddbbbbe79c18f6cec4564694324eb0c619647421c50bc808828c1b968ca0ba8cb434c6cc65a4a5a4251a2c4959ea610c36c640c21867c85c461ae34ad218518cf182315430c6500e4968a265088b09c4c8811855c49881182e10e30731726023e632d20fb460f9c11596062eb8302c38228816fc608805b76b821117264b142c2bc0a2888b824205b7a302e5724610714f3f5c19228b10f7e53252184a9edc4e8d2b29c88282ab9d11c44d8101c44565f1c3f51b5cf1214b0f17ab82e8a68a78b8f40527b8d5881d6e0784934b2f23f960071dae652387db3135b95996a1bb8561828bf10087bbe180c99d575c5992e5869b4971c5862c4aee160329743b175c69cad2c69546b071ab1057d6c8d2846b2f23f5a0c915266459c2dddab8d2258b126e97c695247041421635ee16c6e730c3fa11ae5f4602030c30ae706a051ea51777f2004540480bbe04549f521a238db45aa01f20ef6d9a4cc005e6f1e71d5a7fae1a9c5f6570b0212bfdb4108109580003fcd2a94a3ab5cef97702fdb06b2ae18029ac341f5c71346b28c5408ad534987d2c1ae96fb4522c64d51f33d6844666c4e6a45fa433624f2316e7b4737e2c6c3ef6b1300c9b139bd954025cd5ac583435362be660cc38b2e2332a3a02408d0d8b66b5a259ad6c914dcdaf56384c4c61f31d0489c5528079f855bf21f4931f0e24f586b4101d6cb8ba14c0b1d915c22effd313db92597eac39b1297f4a39a5bf5c213e46619198babbab7588191860977c29dc775ab86f488331604f7f089117302f89bd1660579d31e98b47f128c3c17182e3afa885192bf94f6ad147b153e2508348d90c6c4c3aba72a59d81893c1790e11d0cb08b89020cde0879626bad4cb1c8a4e9c0daa0209ffea98951b3ac9ed0f276340fb0f22bc3aa579dc60acb3ef533d495f5ce2484628c1c86b591235e926e1561977c0ff08d740ebc61977c29db73de06ee68d088c300faeb87d6c73a1fddfd61970c13ab02bf56de927f02efcc977fc32f06230ff6f209b023f2642fbf00bc739379ae1df6d26f1579b0afc53749270800c3b00108615c6a0a51877fe8b44efc8142ff40c17da0905d9f73fa9c523be9d0d9d9c8ee0737d8fdf8f958f7c39b92310c8b18d6da09094a763f5cca1fddfdf0ee47164baf96c56675326d6cc3c1649bc7d91b564d567bad734194514a83505c4249c02ef923881995523fab948619581d1d22f3669d8feee228b294526efab1d7e3587772c262f3d3316fd8d42b6a566b96d55a9bb677cd6ad0715cd771b33d1edbaf6aa66d9f0e8ef6e9e0b827b3a4db9e28875d591b1362dbb8b94d8fe0abc5add9fecdbf524952cfa32e62b14b3e0ddf14619eac12bb5babf5c3191b1c21754bbe94ec352fab300d629c7bf18838c58b4b744b7e0b8cc06a8c825df2e3137ce351a278378c464652fb19fcc57df1894fbe51f7b410e9c355dcd0fa0d1e6558aee2b27bad350bc08d4bd5ca0b53f10bfb6cb8cda673e263fcc2dea625a57c7e9e99e117b317bf5bf2d3b055e9a773429d2b5f2fa49342e7c897cf2e798212df0ba5d58a5d2ef4db2b1f8877fcca1701bf70b8255fca9608e057c8d3b15f401d060dbf70bcc53b4da441f92f6ec97fe1c89ffad4bb55e7006159527ab9e1eaa66eb7425fec5babdd0a7db5f7a858edb3a206811a940f34a48508b04bbe8f1f2a4a1e15cb376a58b20d468679b19734d2ce86108d146157e5f100dfd49ae5f0e82e07a7c1fa59e61969100625360ee1a0842c8f06eb6799bcfd5e03d8557f93c9c1cae01749bdfda83fbd4f57bff690c2699dd6f568b2213788a37b97a74e2bc2afd3179d74ab7efd803882f635e1d6df28277383fd10e7662feeebc7a1531c7a20feb2df0a25843d7ddfea22a95d8f9f25a8afbb38d460fd9313566bc2adfa462c0961939e685eb5ded250d93357351dd8556313bea99cb7e4861208f970acec51570af771675f1c8252638dde12ee71781feea3a269bf753d32da0509ba5aac7555ad91f9b1b28f393ef9e2a2938e43cd796c0486ce89482292be8dfad3671f89bacf501fe8adfaa78cfb2efb581a4bd37ed3b22dd3b2f756cdbefa30b06ecdadb1e885093892b20f549f94b08dc958794cff64171bdb586b51d7b24beee8168f6ea598a7a5e29d137ce686f64acb3b280cfb0dbba134bf08f65549c106a5ddd1a0b49db303a6b31b3b20a306ab538e51c29678c7256c138505ddd3a3b03f7d2c20fb27d59f9e7dec07f954d4a7b0477d2c20d4a73ef5d150c30bea837ca27d98b7dfc37c8ca1de3eca4e89ac18a3576f4d4c27059dce99f1e8d3c6e8a96e276f47b7e6470f64d7041d4ba1c1d9e3adf9123b256193db61d32d8f6fe6a901dd9acf7f9a73887d0cf08e0cf602cce33f5fc810dea13fbb1e18a555767d213abcd3fd7c149887fbf93cac02338073b297101d1b73dee63983d86b1e6510fbccdb18c45e88bf5013fbfeaa576de67c9fdf29609f0ebba6ce9d18b6f2d6c4308c6215a3d869074b3f7c1bfa014d9961a60e8abc314ab13232d94894813b40558a6f60bac6dadbcf2cff7eb4f6406fe1780b356def4aaded1c2d126141bc96659a963d517dd243dc72a795ba67350832ab35cbaa740fc889069f68f5b34dab9fd1da5d295b966173238e1b639aa6a27ca06e7fab2817a7ad88b26939750bbaa7362ccca79efd25e3431f27a394f60edaa53c3ab90da6e63198ba55ced2e7b1e3045a6586629847867a67784786d5b5abae5d8f0dcbbebb8a79dbad35dda230dd52a5f8867efd2cbbe8cbd86035201de407e41dfbf45bcc7302e7dc20e0456774548b3a751c10c60246ad2bede8f73418843a901d0e82dd0ad86b0ff3aa1798afaa52ffb2bd9156da364e92b9f483d0d73c5ee2a688b2aac926d53e97a27dd65e0c49e4bffc9e35bc722fa2b6a8546d58743de910d100000020004315000020100c884302a1503420c8b2de1b14000e7398486654170aa491280762140541c6186000200018628c31c814155501adb64ad121e68267de05dc66414b0192b2c99904d78b62811c63441cefdd086fa45d23c4ea2dfddcce1af0e3e2b9ddea3d2e65bba93f07f1c59389cdc74e88304acf3892d9bd7ed743c5fea13601808dedfdb5ee3a724f371604e9c3c41cdd767795a0debb90827bb90a030e1cc1dfd6227666abe0f2476168b85d92c8105e546b2f625141bc561002aaaf419ad79bce52906ea36db067f465719d7696e225044a49a1944f2cdfe99b65d335ff143e31df13fd157505f6c8d55161dfe7a963941dcf6f1784367056b8ac3af7f5e79d56a67d2f31b99adab1b456bcbb4c903d55af6a1b08d975cc1839f970c4cb19b8540775c70c497c8614df17d3c1c6d92d3c4d6a393e768969844f72dd9378672fe1767d6bacf8ad3187b109d62936792a4714f90f447c46d1e2d714ab200cd582025ecea6c41c44b72991bca4827d5eec13d91c86d14333279be7f2495e5cfe611bd1395a911d89829d4138382b157cd759061644b49a4e62f79a67d563ed009d4439372611ab04b89027da8ae8fc1ff9e26894452a0dbbe52e816fa154a9015b680808e3231f1967445fa0b6cade0e21f5af7a2a63c6f04da34723774ec0661b00eae40fa42b09e00adc0c738033ff5aa9da41b66d1628c3473c368cb861967c284e6ed6536b5c83e56f0afa8ef4f7cddc1a78472bb7cafdc0cd1ea4be39889d426dfec678ad6e36fe4714fb0764e72713109a3682a42799ba8702b186ad25858f05f5a92721cae4265b49c34890713baeea52c19ed0afc09751e5e6ef9a776299d9a453d27c28b6d2355b92920b120a556c08a7573e95b04b50e1768a299818d10d1b0c6c9094a98d6413960a1f7d66434bbaebd38f3365d2a4e280a540453a1f4223920964ead75e64f80f9c31489363b51bec9705cc492215a5821e42ff28aa40794a95ec20868a655ee4de7e1ef6aa196f3308396955d43125fe9c24ee23ccea8dc5fb48a9b02aa67ae73fc284c2609569dd126f259e793a6f130c229054df76fe23a5da284618ad2a2dff2317080f55553d41afaeaa8f31e88530de31d809fa84afaa4060733b7d1bbf0812ed7d9a4f721a0b43615996bf1a0bcc6c2c9be85ea43187100ad1db7a5cd23cf6d3b7237ea15d8920f8b6e0e4e44545daeab9aaa6a6ed7c4376879ae63d2a103dd7c5635ee02e00a1ba53d33c3261721a8cbb66166c7dabed3e7250b2a2a6ef6d24b56610f6ce4092ec34e08ea0665aa101b9c74981c65fd001986baac1f9dbe5571fee8f6c82a53d14e384038a60c7307d62d8b99de4e9fd119b6f92533eb4f10b70844a04b294dccdb7e86271bc4248c5c9bdc46104c2ad0a53bc49089f8ed65274b951adbe23bca97a17d3c55b2a3bfe96cea8ff886e2bdee6f48d59ac02cb337148f8447e94a314117c400081ef43b57446fd8ef046e1073101741cf7fbaa0f1a426cf4d4f6d3ce32478e9123d9a85d65b97488fb2b10ff4fb208463a19fef524dc6d95a7f517e1247a350b3c63ca17cc9225685af1777215bb009538521557088e156155fde15749a2fb1fda51ca913828e380c705dd90b1e1dfc973a3131b1fb72a9273e09dd2385222a2e495d4d3a53572a27eaa1238cd2ff51cac5ffcc2c02dee07d88becb4ad30ba7c4c04939deefa2503d75407a767ec217e38393a0d2ecf49581f5c84d31163c62fd866c581093f60b2fae4815d615ccc74ee1cab7492e25400f5556ac244f267636c9c4c5a21172f42a8d6d8eec256c4133059d5c53e682798c6086212a1c7d55774db521a0de1cbf3f0492386efe7d1829b3be6d50b1989f68af2264c77d78fb6a38d6a9c27006900141c17f7879b60a74e64ee56fdce02cd8d5c25722d2792c9aa1029170c2cc372bced2357f48696905c5b53955c7a659029067ae3531fc7d7743cfc82d23c48c4f99892ea7d184ebd733a70c2ddc33752b2931059f67e41a0075b4a3d17bd2ff0d11279a121c0060dfc30b1a449f612e33d5811f7c63a03f81b633dad03f9c8c43144ba623cf0f60698c9ffc860781e4b6f04f7cdb33ef6831aa06f4f6824798609e339957358a4ed42f6d777928c205fa88959fe9dec7f8532db336203713f4f21d6f6122f9eb016717b46bb9d8860ae03f2d212f52006a300f31944876959f48a5ee16d91baab620d705407c31ceb3a6b42f00c255d2f3e828625d3895b49675f695e8b66fb98b5d05afd4ad7ffb9888231160f42447bf7eff30819e938f6a52fb9c04363bf97f4c5414a13b208bacdc17ead0456093b7fb2369a244f849654e9cfc340c471993d1e3ab3f4915eb311a22d0cece2fa919be8a2597a4427954bb9076a0ae69b0469c6f4ba1c2e318db7fe076d243429592c90a360e2dc889d999d7816b9a63b122e48508aa84dddbc4d8426aeeca2bee81e187d8e2ec0b9b82e9a2c9180968426a6bfdc06cee6d17abac1dde03a9d6af0e0874d72e753e993263b2599500d08d944ab5564051a10a0e536a7a935297c985d2d09317920b5ea574b1c6c8bec7e6d7fdb98a68581de0e38630036c1409bae8df108a9707885b5cf0841d69cd09abf9996b5e475633d494e1796c700dcbd74d64ce9220c66c32b3318a4b1d38094d572a390f65320c24d4888c4645a8f34dc7b6a50fee352779e8d90e9d5cdac20e0515835cb03a819cd61724debd0383ec328abe6512ca6ca1d684b5b019cce4d17ce46e18a5a67051498bf3c42745579427c8cbd19fca8535393bf3c0c30d98f04bb8a1c99dc07da1bd04d135b11285200767ed4373f87768a660c91e51fdb1a3351b43537b613c1fb9897b4daf87bf6fa10f414709dd2eb20176c1b52428f071c987b563edd726f7db9bee413acee6ab92da64de4e204dcc74dee9719701c95c6e4a7dcfd5af771184a209a9e80673226882b136a71e609ded70c7293ad46c8ff9c02da7c12cb3a4bc537060ac28867731a823c30cb62de804a186026e937c80e8d8880ba0be97ba24805564deccf2c3189b4708e543a4c873322aff11800904e9f33992cb4e7d1ab04dd12cd9fdbedcad5d25ba2a1880fd567ea77c9c6fb1f2bd2f8e183f6e3661f61095d5e898811624806b71b7404e5dc74b1721e353576697b85e24374b78019f0682b25c779f32b527ad3ab93c88f129b3e91629438eb288db08a33af8d489679378eec838c52b407733003a8d84b998d7aefb39b1c8ff18a370f083f6387460aa09c7450bc0f9ccffad59784491efb640622727de772c5c53b83bfeee59482dfd0fe0fbd8df278a45fe95bbd3494825c2edcab25571d2ff195b756811c65ebd0ee80dd600de274489d0478a5be74fd39a8710e7f8883209e926300270effe1fec05672780d41612633cb6420debb15c6145af01e1cf8c6086f233dff5925dc97cd2a89d2737e74b0da8c0486db67243089c587de1bee42ec1977d1d457f9b75e6d988f6918108c7903495e191c21938a52ca0c9a9206bc47c4eba798be72f9126bea561470f21dcf76cae091812455de57f9ae30636ed4780e78c80b3a0b51ac20e4ff375b4ccc4461e33a8f9757b5442becc0ae8e6b5b44f0baa99036063b7a01449fc12f37561d1b4abffaf89c7c334fdb47ec4c8583da031303907e0c0ebf2822dd5a76606d278a1290fb30e62c38198f6223336d8d446d4a84f0bbd96ea93fc0d692e291744ef4a5abe72ecf1c701ae35ea15f7ce98282df4617edc6792a00342b698b3cdc68cc68811bb8c9834dbe9e0e80d0cd163845b9deb2f766c63b035027ca22d3f1f1dcfadadb8f41baf53035988f851911e8388cdf2e14ecd1836bc77cb2d42ec19e2fb8b618f8c753a78ba803d726d770eaed0d2abec200ae69c04841c15acf60477b213035f72181ab0df77b1950886d7508753c4fdb715f84de16f81a75e871e4c8eef68aa3afcbca306f854f66fea2058670914c556f22e4a605436e3c7711a6f46d5689f0857025c46f40d7dcb1690d86eb0ed6ef3fbe5e0f2e675455277bfe33b09faea9cdbd144a8ecc79184ae29cf48108699f0b4546079e4ae536a8ba24518ebd7590fff3d5acab0ee3fd99e4785c854bbc5fe7da3f5e51385d2e4cc359dc6c2b5c6007cfbf530e4f6badedda96bac0a8dc5e65acc9060fcd1e884af99d102840b2889fc592780161a9fc48c9ec92d1135bebaeab1e128bb0e2f5a2754031f01f99813b925df2c51916e2e5916f54534ad849b34878346003e9e8ccf0862e789ad4622e0f787d25ddaa0d32b567dccc81cebf28d4d01ceb64e80ff753e06a17c926f695e315c8eeb067b0aac0b1c17e279a274126463c1405028fd83ae65490e83d07acb41b2bba5c77791d0e30cac1fce9593115b6c3ab017f630febcbdeb4ab1c8f6d07a11303b00a2588377ca35d5e3249d05b7df5b3a7f4e9480bca93a37c5f3fedb1c13033cc30977e65451a3fe467460782ea48f33c064d1c205bea5ffa8c8062a29a2e099410c688dfa9a8bc4f5a2351bf3bef231a131067799eb8701bc28dc269b4d1ed39974520347759ec502f5f64da015297d3d44a061e3980b1382529b252f07a8a5250cea15aa665fb39567afdb3d572caf7f4dcaf12c9544ae24cfd6e58c1665ac9e7e1c2ad8d443dbc5bb11c29cccaeee8ac42ead27cad770c705b238b0194f77d1224f4ff98a3f5be76679f43e25fa36fac564ba76f7031e90c2e784569c8108926ff075cb8cf2724ae60015922a80725d5ca5e1643a09baad146efb7067e9775a7280d3018a3669f97a2df154d33d91e2ff7915a4a843e9101efc20aaa1a6aea475f8218eb02e890ad1c1cb35d3128ec83d9fc7bd90fe5a986c6357f2f5c8ca05a42dbfa326ab7abd56140040ee4b61880aeb680b262f26d892d77bb8201cb17fd3ea5f8856001f3fbc730d37871d2ce69bb586c81023668967136e0855989f92d9fe7e00fc4115af2a153ff6fc50dda5ef69cb5ca4449242229cdd65bea42e6b98b1fe19b5fd97e2cca219806b17e256a29dd21abf2c5a85aceca816ff5a0c047b9a61ac37eafb0745fed28bddf11e121364a1cb3bbbc25dbde661380e0e1b560fe8ac69bab1dfe152fd34065008520c9b786783b260a71f5cc0c2e2ea9181258ac2ed1a304f87482e43e6bbe10be1f62f503d9bbd6555b413880ac444e5950c55824043303e24b9a14e88e01f039367aa38829dc22721d41e9087c55132be348e63d79760ed90aea7594db9092374ff8bdffc17dfe3f900f918b50be49a3baeca61c67aa5ee82a44e4a4b03eaa10d417c9352f79377dc556e52fdab60fb7b7f6633243665d9cbc2a5241b08105b2cb54003d221bae9b613dc03494c0766d500442ded0e59de875a6c587ee0a74f2aa900d62133981d7054a017527e1f0f2a06c24b0d09d98d9974d8d5509d26f0d3c5afae09ac6de5252536db18b420ccc31649a76401ba124e338af97d71bed34ac4c443d3a945ce163c4a5626061cdccea9b9bc5257d612abdf0c8eee23157458c013cb859286cad6913651d62ac3d81248287f84b32b7467f315b964d1334658a30c4f14179e5026ec2c30e555529436899953c9042b8a196a30443e8928434c31bb0efa6f29c2f8be7108532c9c2b2b46c512afe309045c5c359a7787ef4fdc5a47e10f4a8b7cb995cef769a4287ea4bcc0cbf885d16451964d9a120287346f660b332662cc7b009228c2e8ea9f60038f4fe8609930924b6b08bd84ec56ef9cfc564d605c0cc00e265e7cfe14cae009cb6834f16d93eccdeacdac6e7badadff84e6c80581f20f8e006e0920d64bed46d7fae47a365d89ad71888580282957c19cc55afc347edefecd2522ea26e9562f84e8a0efec7f898c7f05b82743f8330556be75c0a83a32f0a3606e18b3ff9f21b94ee15ae92f9909564b4a664fa72d8ae413223ee34f5d5b3235e659f265839a27af614ffa38a62d4b7908e526cd4e302122709f68f32f74c44a55ba2c2801b66cb199a2515823dcc7436d4ffd033d5e1fa0cf433b2288df9a147dc9467920a29652fcdb29c7d717dc576943ea90ae9b5a393d6b8586d1fc1477b670ff67fd09b5be4022b7abac859474bd8f8f3d572cabb9051ca547247b43489d339d63655bc6d135e4da61ebabc149f9e78da150e02c0babf5038f0984c13ac07120f20ee09c6ddd03f0793a6e622558c994e7350bb00e8c2b5289e6e87320ffcfb3a3651291984486dca9ec8c2026bfbcb4d17507c28e255828ff01373c36d96c97cedecc0f218558981e52cc73e11745d12c98d7911f1c725cce10339321080c68a01c3e2e169e025ac61e52c0dd4e40ed7c9ebe735ef9c229d8ea7602e75bb6746c2147b735a8f6dab5a5cce634f36a737250853531be9023fd4d6412b2ea58c16d055735928677d97ef181c51d170a405c87bdbd0303e102542a9d9cc2bcf71b7ad1510d99a6e46652c3b8814a911bd16ed4a7d54e68bf20e3139ada9727a4e9a9c38266f73f25e37616b53baa7d3700fa4b8be61852ea87ac2866267e524a5f6ee3bb7850cf10c827bafc0e323f1eb2172333df3fac7db189cf11534610db4d6878f9488975c1f807ca7f89490b55688ee625de50dd8f723baa37d137d2cb7308955aef02a312aab1c241566c843bf93971b4a0c204b0368881ca0f66b5b9315e03b1c21b1b9099cc267dbae4c880810c1b6b327dcf250596c7ee5b245b8f5413cc7ec90a515690fb5ed3d481a4ae6f921227349940c5eab5055d3ee976c2df0f28f139d11f79e009ac4356c279cf2cea91f97cb5259350bf1d01e415490060ea217f56038b5eb9720beac1f0a1580333275cc8321a48f3f56cf51a4cde698c6ee246ea9720e33741ce4896eea7e1064be8e5f1af395463b9f5f6c897e46a50beffc65d9de65f9a2089e92ab7563fe32447a76dfdc327447f73c7768904d29ea15a8fb875b887ea37cd48faedccec84b239330201baba091952963db90c6b647284ff145022c78827f368bcfa805afcbd21dad9bb5038dd18a525d890d66dadc241f6f5f9db575cb72d7aadf9226ab13f21ebb5db2531b4ecd5c5128f533a23b6a0f316bcb43b1c1c9cc7f9942b4ca940591cb9e30593dba7c433808c4f8be55c8ae40d9d65665e5e62aad402a16067347e61199a6ea79cc66469f814d222d5ff2bb7c9b8c57abe74cfc6b28ec2ff16e5f25f651950b8030be98a3471a2526937a7861824678cf2ce12598826f90418662e0bc08f16b98634d494152d84ac034164541dbe4fb9cadbe4fee6b7cc78068379dc47ea9c254c405103b6043ec3fead90926710f3308ff84f1136f67c325046fb54929860fef5f78814810def2b736ecf4905408dfa1edd3b5de549bf3f1a4386ed588914572373e39f97624c928c7d70ce2163b877e832e42e37d95662462f1a151f92456c6b1376575d94a073e27e88926e56164c9e080932201e22870aec4f0c82d17db623ebae21e5737c31a6b9d2e8f7100f88439071749e7fe15ae9019e9ae42a0f1fde6566a86c70c401fa9ff88bc6c56ce0d1291dd59ca6af157694989109ab1135f55a0bf4a48848a84f530fd807566601257b5c69e65a6a604c1629fd592047719a9ad3a62a03329ed2cc44008eded2255f5788fe8b5aaee082198d2b9a1adeabb68c2653b8f97e223ff0504209cfcc68b282f391956f3119447462097f8f0ba3f829baa8a2e753b3998e04b19eb69768d834492eb6ab304eebc55f81fbbcb106dc30f6c73a519118cc5c727402f623799cddace960f82405c571733987fd652151a3e2dc672a6c82c8cf00fb5b90d120ea5bc07bf90052ded24bc664dc2ab48b011b8342029f269b27c25ad618cacd1bedbb6b08d15389380628fcca383855077c9f05240b84a5fe97bb1953970691b0e8d2390119a1fb73ea4e2a7ee2fc69a47de3be80ed10cd7bfdbdb14595a4812c1b73cb5d5a5ba1c22541ff01ddcc21637b4c16039b5ded8fc5898f0fb2a61f8d2f8d4e7997da2c1a87d294411c4841c895063d1b18d2bc9b0dc7622c8eb38e4b6eaf2ef320c2212517cb0355b225730078d29efd51fa19501929e8404d46243906e2e2130820ee00bd1d849608dbca4c3a9e8af6c44e94aa48cf5654545e9ad7dec1694481feb72680fdbdbf95c7b5ada979c17bafb3627669d6b9fd619aabf60454228a10a99049276403b5ef266d8eb8c6d72d4ec508fa46480706267d42db00390a997cb11ce81dbfc0f933c6269fce244e40d07b56c8f0044ec12f8a0b04f841f5bd44e0d78ca9ee0f15fbfe1f46c4afc03960b83c37d2f39cb0d72e32b1f8f4bf484e0860a68f0d8b3c41ddf909ac4d5b3d7c4efc8b43203dcf66091b18d51d5571c1cc96ad2d143b0af68bfab3e5d4ee37684807983a730fc53308f6defe48bb53c323e088b6577647305e24fef966301e90a8069f6baaa0b8f8dfc62ca6e549205fa498f73f5a072a63fb44b9a65c2c9671d4e2449d69e6152e8a55413f93f1d45fbe3807240fa2aeb6f1dd3b012952a8cdea136be7f017723423763dd2e089475f1cc07836e2257cfe8d6d87ecb14bd347803ad37dc04bd4b2373e530869e3987d28e0b90da2b6e8d11e6cd13e2624a0435dc5918eed3963801269036d9865a6fcf642e7879d00c41e3acd8c1eb2aec691f6f09175953d90e4ea56c95e12cf98f2223f98da000b884322cb57388bde3d7d37f2fbdb1546cc2a629a8fb19cba1c6e8096f901986ef2c866c542a7d55a36a6bc989714cebf4e431d9d4e22ff30446d7fbc0309b5031b1c02f9d325d223f547373c4ca349e14e85d4a1b3088d1c495b2f512eb8cd15c2b4ea598736625782d600b2ae7656bd9342e3daa030e11f6b920538960f0433605e6d4c429c58e5b5ed7bfb49d1eeeb626c5adb2a3bd190add64b6f9dc3ae55e2967fb870b36e23b7c15a9ef35868843a7040a0d0fe2c8c274b532400074cd3e7589ca6efe4e1090bb5e41edef21799337a627531afbcdf552abeac42877bbb8dc9d4676f5168d0bfaad782f8cfd708ac2ead7ad138cb686279f1fdf58c2adee4230e4922ff48ab7bc062f5c6918f6a5c6de614613e9845d2f0077fa24f9d1216161a06adcaff4a07ec9fe0bf3a2c5fedf35b591b95d4d828f73c0e7f59cb747b99dd3433e5e7ae32e0ade2052abe4e2f48e1b05c76d2499cfcef01427b00d67698b8c733dc2d75cd9d3409a95111c306b748692c9fc74350d6799be551adaa09f1ad6dcda31f5e460f67e06d768e40982e7c212b64c78ce98542022fd9d591bd1baf2af3a3509b3535fe31ec1534b6aed8d163f98515298ab2701dc47f1f8e09304514bca92271f6bc8602f2344424b6b410bb69a5d25195bb6d069b038e72e7dffe79f1a0291759d5af51869fbba2e0cb6557f1cfae22d5c38aed8f33da45e3d6bcd4fcd23789dd5f9f932c35b64723ef8a8190638c965735fcb400bbad5954a73a8c234c889bb84260ab13d138fc0263bedbbc479fc2d424ead3a735ec5b0801198ddb80f317dfbe84185ef5f797d5d72b4f71128e641cab789ee105cfb0926a4e1a3f9baaf81ef7204ac1ab00b46fbc9842e6bdd997b8a37df849ed4f629028c36dab6c4a0e27780273135f619abf468d8ea3a25814a8018c095d7dfc0a0172efb7c644cbbb14db5f8333d0a7bbd70a52ad448f0384cd77a3f94c31ec258a0ce37115eeaa6b62fed5c8123453809a1c9fc806db38a1541d04aecbd593c6b1e741868764c5a2be5aa9cced303dc9beeb68e7fc96a24806f4d2f280266d6441fc0224b8013194bd13672fc08ace2cb7e9d8980e600b48298cd5a82536396eeb9763cedf26a5ed9196bd43d8256c3b3137ce8115dc16aac3b3da26bc2ab9bda444d3e8dca5723d26fe0cfff259dfce3ed4e52f89d622c00da53bec616d0ebab2a57679a4a8b308dbaa51107220787df9fc594871e687d0bb91b84a09f0f852e130c5298ceeab1d2296b741008acc500d2860ce0915269fd47af8f2426765090f88f0aa002935b708a26a24d69c34cd679875cf46442ae65fb0b6b7e0c85f7784c7bb4f5a60ab94e4a36b8ec67b913e3d663357bc76370accd64011443fa9075a9ca02e057292f0212b3ca46c4623f62b16dae1310cca836227de1f3f0ba40ebb4eb0a4ab6602343222920b151e9c4011810a15a07891df8552bf72e6f46af2dfd45d79c959d1c436dc6b9462fb146636125915027582c380ebf73ff2b8b15c4b21f5b99c20f88ddb81a26f132129d80aa81fc55bf1f29a6f0e4e5c65bf4a1d4a0fa451f7ef43b950a357809a10c1ed1501fb92e447ab44f63d4280ad6a45825d233d36c5c670900d0800ae3e0a84454517d6bc1f0b675e01689ffc156ec75ddfda8eb165d41ec411210803abe492e5762f10b9a44d2105d304dbab22281535e5ff570c6d6c398506803328abbc52fbf5767e510813d60c0bd1d8880f6a88b0fe38c8435d6314374378d2ea937b345e32dec1a0cfb0c64638ff8ec2e832bc99eadfc949ec1a97b43bb51da987e301e73ffda8b2ecfb60ff0470255263069a9afefd7c75e269c8d53f0dd46d6ccbb46069ba65888e6badaac8978a31f6724276962195df087fc6bc8469a4ae1790419e2309b486b830f7fe6c287e366db1c398976a79c60c23b7c94ed66e92f54bb61a1a0ebf6a041d011c6cab0994fd80df84d6118e080f072715ee03402bdc9398f078c494144629728edfbc61e26d6c7beea2568c0fccba7712839197799683e359db3710b91ed5f2cbc9e562fc4e1a2d9bf82e69c065b3b29ac139f03ad191fabdb544755e606c59807b6ce4ad4051decf223d840e4f63b7d0a1a32a79de8f01280c02a9217855d88b7bfdcb0f8416432b3539da890547adf85fc5cd367d9db1189e6c79a1bddb73b701b47c89d971c8749d7ddfc708959b6991af53cf421c9d88a5ff1a8c041cc7e94b926f561a859dd417c8d7ce169ccd495447322793499b97668088d899696344ad964cd94c471f80f97ea5642249afbae784d8003a07ee8f588253271f1eed3bd520722eb4fa2eea55059ea65e34572c60fa52489a4e31c029a0f034a1d449eb87d3284abd77858bf7910ea8d0cc0b7827a976b3908cda0a84d632c7ee4a5397c9856ee019f8f93fc0dbf3f374b2434789796b181d99859d3e78045f0898bbabeac292c9a545a931df70e40d3cc108ae2ea732bde581ec67f91cd860cf94218ed63941b1bbbdc4fd443a5b62dee7ebbf1f00a35d1737488a096ca649d118a1b4d3b9498adf1a3d0a72afaa62821639eb7643eccb5934e8354cde6328608204703abd6c8d5c2d26e460381bfacfdea6ca2abd188b4f1a98a5e8431f228e4e82658cb6f2909c49ae72832e8ebd09c1c2a0a588bf490349614ba270fdb94388ce49c8ccca1e2b500804140f54409267342c105bea3c6c787eead27322e8ca09703b7413cdcf6296ffb438c6ae1a6c4620cefe2dd0b16dd6d017ddce8457134e2f89d58c06608bbe299be9dd91f77c028813b6cd53f6debf885c5d45685a42d035744bf77cb128c4b0fb85749000dd0ca4671b26a9648fd3c8fbfafc1bfab8be9ebaee200227bc4810dd6aacd9fb6609ca331e54b5303b58838f39784477c80e04aeb1a1fd63e7fa6568a434d675854cfc190e922ff5583d0e4cf56d8a50700a5b1f67300b8f8fef2acf72005a950f12153f194cbaaea80d5385e9236b0fc53260aa582e69b4c3f9f5b0e6a47e28e780b556eb6774b1f75697f0c45228a60bd608a3285395b9f5c9770e8a5c11c8afbf79196a854a92a0f3e007ca8bf14964495ec65eebdfe97c592c509f57fab9624a345ee8168e76a430992fc7925a4275aac61493bf12f7ced7cd3a14aa7c8dc29266086ad7aa7ab69713dd9fa418fd0e5a43525fb1f68ee103df4f52a6d29277d51feeeea86fa830366bf0b8c6fa5571d881a7e82d4b7f487a654152f6d6029d504dcd60514232d84fc790a245899364d944207c8cfad8b9481c41508e11432bbb4052abc4912e8c4fa20d487d1964bec0be49224d60508e5afc2ac903e6389431276c6cf4656be7cd5b53049747e5bfa2b47d50cf5b4756c87dc9af30459dab09fa931fc9c6362339ad3c729ae13189ad538c8097f53267fa52eea1cd7e6b8dc4427c36849da699955b4abd18d3b10ec3623922d495487fd38e540871a69719a77935618f66795442194fede38075d3600e6df797f38ff491e753bd31af6c0f3cacea884c0b62d2e308534f9baa53cf4f3e5230a1dc6ab00acf66bdc6a44760ee75fee62a1767184c852534d5f02e11faca1429800d2da226c42b82e5fac89218067bee9365942ca8f9693285aaa98290a7ccd55451b4b16d0fc17a9f6376cacda42282939115732d20dfdb177005c20e7852a4b161d6e606ae2f88288556c41dd19c2656b546a0e1af15f5368f63290e3f7530684c80f0a0d5cca8c20d0c64a6723f8375283355aecd0230444f6f2a3a5b156c0707b2635d948ffe2859410fcb05738007aa0e41cd060ff20c26e70688999e52c7b1dba716ccce36dea3d70b3e32e43a5cf047de3b057cdc1870e24f15294408c659de16cbd937e9fd7e20de08df9f7b9efa7f44dd9472a8b6a1fb9a3ad6e63eb28fa6d7a82af61245a5d9cc21d594eb670bc70a733c7edf70a37de7d861b0943417285ad6b0f7c87899235a892bcf25a37fbc12012fe0d24d2c019271832ab12589d0d0de81914ef9d89d32619862bd7cbc405bd0881e34267976cb18c62d7f6fe6a72609ee8c08d197f801e1b472b1b870ca0515125e4bc0247be43bb959925b41055db1eaa204d6529dcb4368c5d004ccf7a58a350d93a81a91b4f866489865e0e490cef54ffbecafd5e6ed8d3bcacc32a2c7d08c9bc9f98ce0ebc6b93518c87e66046b0290723c5047bc773cd7751259e35ece5c880b04f85d4ce873736e648efe11bc1b823a07a0ff8c6cbf15387b4dfa0fd0cd4568450cf468d17d01dd01b417d705b3ceb5d86f4809e7e944382e532d99288ebe023480bdc17ba8673146075ff3031ec93ee9516c2a5800de32ec7604bd311ff959320b7dd88de83bc20d850d3425eda31286533a02cd628228c8e90348bf749ec40b5d1716c7e6d63e3a0be77621d86bc3b3a3d3ac1620065ef4e25130215a49ea8312c4681a2c548d8d9bd093366d3c923ceb86320b56ef84d29fb1691ea673a311a883e16bab6cbc5d5005e5311fff76d61b68bb4a32bddd3f9fba7c509e04fba8aee29d02ba40ffbd2c4266443877cba9dc18c1a6324baca6a6b54cc41b42d11647d8917a260fdb911d5872dab819f72ae6f3576c00e3169ba0b7a2dd06765bb67df0ada8a1eb412527444d27d3f13f384249ed547b85a70d8e6f00196d3e19b44dce1f5b152463de577952cb1238023a973ea392ed895683a8ff364ca5ac37c06fe436ec91c7e91363dc4b0fa194012ccf166557804f9c12774e1dcb6907d2f262a4b0dbacb21e3d984bb725b3e48298103c97ead4dc958e5925bef578d69df4cc37b97869977947d3f49ba071cacc65159dc11837e50daf12e5969b9a52ea613769a65137ab11bc0d15eccc7d7eb98c71173f5cd548f06eb0e8507ae04d7993b34407d389b0a046f24e192c012306b9ab5b4c9497985185430ba511aada683db869352a37e93948b2a7d75a2bc72588d54f13ec7b886a558258208e4005e7a0317a721a57fd6d2c4ac6bae4f1ecb93fd1dfd72debb8f7da4b986d26aa055d2d9e9a1b15c89c62097166f2cca5b3a133abcdd152d69d1f13ca0b4708c264c5d7a298aa5cce299e281ec9c95755b33a1c3d7a046d78c7eaa14df33458b642a6636cdfc0f925561f68c1cc7e868d2d31b1e5d318e419e28d73ec1a206cd694ce0cd4637956caeb778630bee52691252958def97a88f4c3aad9b29133b3502c73de0622b8cfd9e6539decdb3f356040796bd8c9de8ce0360969877b531636694a48b063b8f350a503e60104c598f2d42d2b6ffefed4f83e4086a7fa7690b095f867784e99130efc98a39f8cd322714603e072f41cc2b49570722375e631a8691cb32fac31c84d7a81b490326246e5f6ee53ff2cd0cc62ccee594191a781e3d5336d7e62414c70fdce8119c04507bc741436c56eed8131be3bf04142815e29f05287bf5d88dd0c1ff041428adf0500f7145225d5811a6a8be261118c32af7549e53f4b10727da65702dc41aef0918cbc8d75e20ce7e46c2fd7e7145e2efba17a3fd5e865964119a2785102c1a4fba30e0cb8ba128ef9c7a3c105fac94e51d1a8c47c90c093a298eec3b4afea3144832ea58b6392476b2121a8ffa50ca8d8bb0670db8b8bf39f59d71c67b6c1908f997201f05d1dd5320ef3fa889c07809a87005eacd497a6beb9e40754d45411cece865cba7ce6cab1d0f5c2bcc6dd14e7cc8d0fbcda8f446893948924e8e4ada01d87f81fe586edeab3fc61c06a3fe889ed179c01d18d5d18d2ca2ebf4de4266f1487235dcbec04c382e67b7b96ef263cbcd0f5b8f5eee165a5e307294f35ca0b6a470ecb238880379efcd3e57479b64b06c7436450b6dd38353709ed7addc9e61dca4a69349350ec90158c7056d52fdcf2165e2cd8ea06c8159dc20fb1aab5358f186f8496864aa2fb7f005b0e68e5ea7d58ad596be96ddb6ffa8d03e968b2c599ff49bff40ffe4a100dab2e0a9af88c8853470c0b0be4eb3e44d03decaf0c2b3e77f3586658099743074546596ddaa0f76fda9c4576f061cfcaba691d20d34551d89838c9f287169bdf7a4bf6bab4d845f295b1759ddf06cb72e0349af25df4e61f83407bf273295f84e712fdcfad67d4700ee6cfba8da7c724e0280c0e8b0bd5e9d94ea5ebe3e17a537ba9c0e50d67bb3fb4e4c16f50ea8b649a343cbe72b77dbd59ec588bf915790250202dd10f3b3f5a50972bfce46145a844fce93e7fc3a39bb8f5f264b404e0ce551ca7db9000ac3d471939e1d78d29affeb6fccac41e45d7d96559b1735cae8cf7c7e32787ac64f1ee0e073c0db24b015ab3b2a040c13fb4192420401a03bd87f85ab8ccbf80c0f825328c1db39c105b8d2c7ddbefa6b69cd34d5e75f828f543c76043dab30ea62387e6e257fb20e6dcc2e0c8590fd3bc6422653a582abae4dd1377c1cd6bf4a2fbf9833b99e91e1780bda61e32a47450ddf17b06d347aff92d1002e27f177b97751520827491b639036dbb483a0260741e109786e48b8c1938126b895de91ee94f073103aac2a3a58a092880cb739c1c63990bbb160442a3ea3c62822c92e1bbcf5ca26b114a958abc8eb6545beb3e93e13811d69f52c6a3098a336a024881eda852369d6f069c73edb6142c63e1d67607007b6d37ba494603064488c2daea01d2b542de8252cc39514caad8a98241538c42d959de569c1d2043992ca71ed59de90b91da4276a68a9517336254b041d202d4f04a74d3667d95d91e101d0cacdd3355fe86abbe8f112a05b31216225b114f63ebc9204d051d2d54657c80e661124a008d546542d2876804db8da86114099b19ad230811972a196bbc76e1bff4075832ac084ce91367c1114663bdd207c10dcd7173d7a546cb41df09517639272d6252f3816b5df0fca75b5c4f20c91f11d27b70b9d6685b97b46dea073b67f373cef11aee84a07dfcfa75c162efce3b33b576f50e490717440fc336493572651ed9e11b947e382ba64f94f246d31b51f82b02feaa8bc90c19b6e19edf9263c972dca1a4cce2958cc9eb47ac4e5b1d3955b8509952b50c7206e1c58b711a3b1b681f110d3e6d5afe66c8b57ec6d4bdb581cb1e2d6a07081b6d004bc7562e5ae4ebd8b034407c6818a6f6dccf49d99c77e7b2e19908c76bcf3ec12b3376af1385165842115ca7d4162644fc0db3354e80124b5c291206763fa8c50a3a19a0ff051e0d46afdc5e8ce608ab25032d760a1582320769b439a3f958fa294c79177a4f57c85992e6f3436e23cb9017e935732528fa7661829afa83372d51a7c63e1e946cf4c9e62260743436ea102941ae82cba04a9ea739b550998cc385506db268a9455c911e0a1099f755b167af95ca0b468a8913e7f6bd90809aa429c7d68c7104577883f1c22743ef09ec7395f5b533a8095be91b7c9ab6884769131081dfbf48ef8bbc4076e824f3ec51b460b1911a8cf3eb1dbf14b696738203d5ce2bba17f45fe1b4f14d02061486500a292c46becc7a688168df040c9ab1ee8fc6d4030c3e6f06e851cc54c881c14a82fea782b1315b22c7e3f0a1da2e5db0942dd642a7c7c9940b4f47485651d9a6fbc6cb86ed4f5f1d8d99ff922828c2ea50270ee3ea4c864cecdda00a894bdebdcce2c3901ed0dc601830aa276ea90198204146accfa749a74ac40d5b82a7b648d8c36d9abfa31146cdb284a60288bfcaababadf938f67b1bb223a1bfa81f0fbcbebd24daf4ba41751fdb6e4265c5a50b07ce69c4f2f3d96c35863d4445e188acf83e5cf76bc841596f86ae16f47e8f8cb1f57579c38b976e7a7386e8e6850cf778585e553a3945e61e54050923ad7eea7639ef52f26ce0d6766887c1f62202a1b850ca3c3b44a77cb04563b30ba57dbaf2e29ff450ebf5d60f83379c5af0e7e4ac01de828db26c4c2f5c11d7591706c756f2b6d1098b852de236e555494090d706fc11a3f4f9a2d115702c72bf211b3cbd8f052044e28f4f2f71c27711916715ad96cf035d1ed588efd2d17b20b02ced01a87272c05cfc99b9fdae190b3b92357cd5e4051d5b2bec459428e4cb0ede41e848a0a31aa809208bb531e032aa797e4824596da07f99972ff6d6c1b72c911655b01530221bb10c2b953e7a1c9bc808e751fafb34db23cb349ca12bb9f2833ab1ab5b1a51c2da99cf600917e6264060e10d2db35099904f932622ed48360ac334c7788c3528e668b152e9262d802cacf413df119443727ec3effea4468662fa6c6994d02159159f0220b73496906732ec3c4ca4c9bac873dc90c479ace8f52b71ccb7e549494666658251ccf8556c7adfcfdb9655ee704514a262c1076cd70929dce25c836573b069cc26986c3c855824339d761e4357d482f5640c956a733fe214b6c0357bc247fd8e9246eb9a256a20d5a10bf7b0deab4b241a92618c793f688a1f36b0c2160ea2b2d41f66678881af4dd91058ef52f662ad0a49d75da4073814c7c601c230ea59d524fb8f911e0a6546af7d6c7288c3da58aeac4e1abfb894811e212a2f9f541864d249c4332429e0c0667e463279474f277789105b5ba03273f0f319c7185205b18b4f073144a1ed081c9162ef18d71c652f74befe7313d130941952cf4312338f095163075c91785af6c8229f2c609c6909e6bbf117892f9794b67514f2c8254fdfa5786d5bf042b9922ce2ede45dfb6bcc6e440e2aeed12788c349eaadbd22cefc184c663b1b99ede3681cc52ae0d81cb51eaa92636f966ccca8c7177fb0a21faf331ec03e29eb4c3edcdd5bc2e7db0f29f2972668872cddeb0ddc7983c7f851cb6636bc52acf62d283689e1e66284e079ee6d3604f18f8125ff92b0dad007677a43ae102dc6c21b4cc31599e26d03c5b9ca1f1596a631cc2d92c13d014259da4ad2e027bdd2cf25b72dd0dab9e41995cef1085c2e8e1846f3154a4943ce33f92ba6d1a07ce238606c1c4d415fa78a046d7e0df504df6fd099d112f6c0c1f19b231d8c628f450668f1e31a7f2c61646b6c63c3fdc39cd49cd21853a22d40c66a6e5378e82de2ab0b477d1bf8d50f0081e4bf415caf82e04b895aa342b9e4c0cdec3ebefca5da545faa162628888333b7e32c0bdde0c1343f62ec2f9a2fc23a456eea48de8cb3a9a2a4085821f2c7f749c27d2144afc426878d64829245fc75edaefaa2dd0ffb94809272fcb776b2568c433069df6a5c64d440f06516539e2630cd50282cfbd9151695da13950349541d22b29fbf936778ca390a27727165319701ea0b8b8418f0b4184b4725cd3eedafeaea12984cca61a44b77dde4195dfc8a2b3a9ab1133bc251204e8e5a24da39f82f2c64ec3ba41a7e5ec7bf828440be64073bfd17448438419f0b6399282f2a2d4f335f9ed7d9f748b379c2faf344abcbd42e3a08c648a4205fdb037ea9dc48708675685c539e9c2b48b9dbd38ddb1d9033fc41311c35b9ade22a86dfb93415cada5646b6c67c3aa017ab5942c744be4a33edc089d7bc163c224b0452f3b7dd495a10eb1d480b7428d6dbdca88a504d368f6125ac15350d6440b60f3a0a10450858ca80f93b44da4c27a10973cd323ef86199932585250a6a3e3ab408c035814190e76cf93da062b5f087c8bfc22fb91edddf66ead5450e2ef9130068d78f485c86be9802553e8e34a70ad2545eca03885633881b85507c65bca2ab747b2992bd03624b6a5933bde7fab0482b16282171c76f6fd013b2c3daa3e7d5174bf8726d2c3f402cae5f240f5be729d8b024a95fb49ae6245f6f02dd49fa511b5de45ea6b4f0d8ff5d402193912b3f96999b4a7fcbed572147e0b02f1d3a78f1cfdd6300f93b9c4b18a64efc1b649cdc0cf873692ab249306c87e27d39c136c828b94163fbed501f392989ddd99c817503616ba9c453b3b8e4326516407572dece4e4c04dbcfe9bd76face522671f1318415ad5cd38fc7c1b1972199ab7e90fdd6bab8d63d3613af408f6acc76b054415c06050726e684ada91064409aa081d145761ec27d891df81e195b0e08e8640b68e126acc23247508b79e7e9cadc730a3f451abd483f01cb6e938758216c0f6b1d6e7b67d8a99acd8c1ef1dab92d2d14c977ecec03b872a74f7632a5e1e318749d923b9f8ddf0c0ecee5b9f0b1774b7b81e91b5fb1d59fcc13c0f16993cdb78a2457e657edd9541476670d447538be79fefe945f0fedfaf24b79b030ebb1a16536bd66b87b23a3f4d763b8291db1cdbe4ba366ccaeeb324d91b78a2f533bed512a63ce35b5b9320b643214a6f2c69c45535c7fd1a3bd0b3495a59e1ae8b2f85bf3a39f3efac8e3503fe01ace8c5fd565ff5cf207620c2a1899148d9ed0816bcdbb2ab8574fd54c84d97cd4442c0ee14e3dffa83e65df4b17607ef01dddcbcac52c88363913dcc2efa57b03a6637ec44a8c1e252214e728d915c0a7184b632a3e4f1a92b520c34488a45b57a27c2e98c991743e0fd8f916ca874364abaee5744c61f4024966ccc69482af7d817c4cdd00369285a7debf869fe646fd0903753b1440995a3bab952f91393f1401d6392fc643e213146ba6cc79940cdbdb35310d4d85382a2c1205e528bb24263e850170540fc1d071c4223b441f1eb8d783220681329d1c25b35480311bfad007d8e9b85425956c67b19e2bb4e62b0f6ec495595b37a96aa053d0b7dc54a2ae2223e1227e8fc3a16229e4f9a904103e2376898c56a6f7a032fb17cce48c298faaf103b433e7edb7f4222a671a47800d25d0fe2f39f7db4aa0119b7166a9cbcf1abe35869c52f801b803a759266018ae3c349ca05756310a64203f48905146a81f8a3d9cd0091a8fa7cf378525e350aef1ee3876289b166c10187b9126d8af1ce95b482fc95d47b29b11bf70efaebc56ae3378d2f9b3160907c76f80ed08d5ae32628efbe6f216cab582a0f693e8bbed4236c06aea5d886c3d4f7195033546dfc22d731aa95b038fdcedc79f827b4567e0de2d7f86c79e4fd0d491165bef6b031f83e2078847b52d21fa6fa6975f37cd0e50f3e25e893f704b6f06cba4e4846580f8e70590cc30660c596bd291935361fa23f00803277514e5f585709a71b88be0d5a2b051cc41a5135402416e307d5081959e85af76050b453227611f6d9ddd028dad8805a3c286405eb92f010a8654a7c03183c1bee9173a43bafc2b0cd34f7018360fbec1d0bd46f215fff365159de3047f461830b66b6527eea31baa317a4b27b24ab6013d434467cef156bd3416aea227b5386c11a18768f51f45ef9fb511cfb92dc127a24336c6045d3788951c6fb2c2ea14a73206b7c69d03a2991f563a9eec1fae7d42f793b61295862a7028bafee1b9d5546ff60d8350e56ba071b7bec964b30f4e0008ec1849bd088e1cb28b17366a110a6b39796408140386ffb8d4603830dd32cb4117f70da94ddce89ff8cc0fe4ae0b3355fb90b1c620fa6d85c3b8416e8cad051825ba064cf27979bed061b0132ada91b9f19259f83e5f419918b2f05695e690b64343c382988ebc69bffae31fa57d1b713111465030aacea73174d4f0873094cbc96817ef66cd4eb81c1eae442ebe8792b388a68062c039d16071cf244c4f191a07aeb5a30019c2c8d6d40facb5538438d0c749e5dbfe78487138767b40b47f499790075b8a5dff57c70bd67f03dc5f73a18871de36aecfd292bcb0a89b6c0e33ddbe35e6a541904c0b8eea67145f9cdb4fa0a79d7a0ce50d812bf579259679360cff1398fce1dc06c4599a00332959a472a319a46141cd07805c010c58555e054c56ea14b6087e11af74c0f6b99c82e26cf88199b95e03e0cfb2cbb193dd27233fff25e5bbfe64f9863c1118beb13239fdbe3500f77e4e02606e9c7d3191b6cfef270462df39dedf9430cd093d0f85ed3d0f22b457186870c036cdf239b5d177d05d79dd4a761af2ea46b3a50409e56da306422f4b0162347a7e79e9468c192c3ef773fe6412629dd9b6b0ebb48335deeaccf31a17bbc45df14f123bbb6deb3dbf854a3fb0037f6c88f7c3482bf8b4da1b802ff1b8322f64cf455f2d0b10f9ed120f1c93dac20d0838de107eb9bd1318a61f2dfd8936104efbac5f6ffb673562329227240b2ad50f65441ee15ec930dc74b087552f254753f9ecebe2d63bd77ce6ba9a6cababa926796bdfd5652976455dc1da7ad0ff873349ef3dd10a1a5e0b92b3a6014df06d80dfa692cc8407d163b5c0c9a8ace50d1bcf164636ba49b92b7d49931405be577114347cf1c4d85c2609126025ce2080783f01737879249de7aeaad6f33e091d54feb4c45d2830f88efa01fb32f0fcf67dc66ba98b2060acf504d0ea5df09eecc5a501ca7ec9901871d044145cceec8277a8728ea34483fc480f6cca223019c9c7261427a4b418e1d0ac40dfa170603193598cea1359b3291ce8085318a7cb67548aa1f1243db75719da8f17135064b984b9e5fa96ff056f638837e169bc103dc360a284c06d3e68e79a91ee5dc14ca97ba4242d3b7e09ce531fef4582ef2383131ff5803a36ba05e260984eab61be17d5607d49ce5f6db11c50e90df29def9b44e9e48227c5a6c8a1dd37b5cd59da7f40644fbfe795e5712064042e259063b3b834aa169f3cecdbc2c2fe16ed9d588942a664f6d17a9869f6532680f3b09fb420a875c225fff1fa2e26754b84fb4cc1ae54e4101457dce3d4b2c9177c78f5caba7b5498756bd6345b18f2d3b9a9a7e56289a368f4893b362ea98799b4a76a89cf3261cc1909ba5470d3e078872f1b7287a36c986d2113a29a50964d5c80bf87f3ca481cd6748a91c31e94e8ee8cfda615ab9249eff787bc615e014a11e75cae53f9f9573e8604bff5b880ef8499703463db5a71b093adf60981bfbfd00fbaa8c6690d0fc27b2aa75d58570bf3c1fd77912aba09dbff32274e73c575865d5bc73f9f1cdce8066f925ccd31049c777be01dad7452349408566f4c2810c13b95805792513d0236b4ba7e142e5c8c7dc26e0a628396c45861b614c5f8d627587adb6574d276e68b0f25bf0e7f10e439e80db2c8370813b4d2b4a03c6a5817767d5b328d1092ce45033748f570d0ca32b19d25fb6ac12c6f8b484a8e2ea509f4a11b7996c9e6d3abfe2f1d89ce3a2705f2a1adfeef2ec141f1f894bd5d5fb9b605bec011c4753b25c4f2d4c5d35cecd409ca6d290eaa52e416cb0d601918098a9a9dfea46cbe6e4ef61e67094b313f04448a5e0706d49ecccaf23d083dc8f4c8eaaffb8451746ce85851894960e7df687bd24babc959bb71084e93a0b575db7897628675b9139ab6a9c31bddd58c014a860a64103053ed333214a4e8f928459e521ba4bc1f1936e7e2c0ad3a4f35f5545288345ed1072a302aaa4bb422d041ae5518deaf10d869d939ae43d6bde03b25085483e13311288a27ab6e211079d9141ef89664ce6289cb2dc18097b444a5268fc230a81bbf4e01aedd925ce2d1e73a809f5c1bc6a33e91b28059632d1c7feac77df453a38b5357cdffa0d234d2a86ed0fc34d019e7ded9d87cd6dbd537b474d448af3cb74aa45ec1c6bd7c59a7078820f11e8bd9e8e8edeacb24372e4dde9451ae6649bf4abd605cd6730703437a2f36916deb13b8e8d183bb4d7c32da9720e9f1c23850663bda3d8c268471de84d892fbc28e8ee22e3369c196fbda6124cfab2c54e15440c4e27e4429bce9c4297287f8ebe939dea95324eeb981a14897bece61e35701393eb3ab659a3159ecb19e6d09dc0d2a630a58bdbbac1e0c536f2b6e604eea740054e425bf34f8895719a7be3a4117580571087f49088652af6450ec20fd209020693e31fe58b18eacda49b3b245dfb0ae0aeb0d1c0f036de53c8096582b813db096f3df7aa38da5803fe591c14166bee99118e227b2c81149322489a56a696ae3e550e23f88224c8db8ca0f048d246c9507ce33f901b859235a225e26bd0f35a74a6a7d85d95745ff6d45ea5aae51ba42f5fafea5d64f7201f905155e743d561cab3f0dc8d03398852b8e24a1e27558266a7a868de044c72d3d1d064775bc9de806a8eb7d2d735d70e37f34c33e4c1a08828f333130106f69c60f5648d4f4419bd6cccd57188417521dbd1ea4f0ae2257032749fe196667ffecbb3497ed10ad35873c44db037ff18123195fe916afebf28a5fb90d91c91c702b4c4f23e4e9fc6badbf1b678fe76ebc11333a6db4340e9da126dc84c2ca815ebeda2f7ecb7654df3d1958a7778ee2678bf64578ccb93cca228d770494f0d59ec89f1b2800756690429060430e77c0b88fd7c2214c559d44474b39fe269ae6fee2ef6a35d70b219cea7f04bcd11706e2cd1f56fa8ba8d399c0cce55b18a3e995882733ff0c63c5cf184f3a38a91a902b433dd3c2d3a0dc4c50e30d5f3f9bfd7f60519ffeea0ac96d4ab84c8b8f329161f4622656f01f7252006f24ca1f23d8ce243c4d070a30183f5e4f1779703a65a48da90553b6e410738c84db270eb129c82a62c26f812d9628a15f45a8427f6f0c14100e398c488633d2ac86151388697db61992fc4948da136040ecbc0eb76aa7aa07908b72cb2bcb719a54414c3755229bed338306bd4b5491fa29ee3a16525db5c1007033db451d5cf0da302052fbb766de86f3569af6e46ff0945858f9dd52f2a72b2966d7c54ca7131aeab8b67266b94d3871c513d52e3b532b00feac4bc93fc6399237d0ee6d69665b3af154bfa646a3a578b9f98f7d858893507adf1221b073f2e4c50b274ab280e1b26a169886e2bcd4893c15548f11c7cd830505306779061add04b1e5734bc67df84980acd74574cc205543f991f118d7284ccb4c4d9f54d1871a00c6268a48f9b59dd4ddd4bf8f861a3cbbcb5748660a8182c6cb3e1d6bdecb54f56c6933817ba12afd74b8e8ff631203fa294aa2091f40e4914355da6c1f0bac085388d17345766808ae5abfa59218950dd369c6e256870bfb5789f5e1d4a97340f9a962e4937aeeb0defd980e20598bdd68425518b018d37067d1c365bbc247730db31695240b2e2883c58a8edf1d9e37108b7c5f576fa2ac269b26883f541a7dffb25146bbae4fe3900301fcadcb8bc0a00b0fa316ff8a04a6c55209fb127da406f9674009c2e20db614114fc5743383c2122c52f27daa70ead2d5e00ed2a50de4b67d8542b08675fb39539adaee2338e8fe19846d9a18e6d9682122c8a0a622a5b835db000d3d8318a87a76d769d872a8ccf01072f5f71058770d1b1dd5cfc5a5af457e62cd01da88297319668c5a8645c102158c5e7dfad2932711789a44979ad7e7337a69b7dcc29c8b1279b4704172adcadf81ae682647fc684351ae5da0ecf6ee9d900cf8d7d29e7ceda7385b98b2c05f069991061bfb00b1c7dc668886954bffb0ec5a198982e4ec77869fc79da7d95b92cd901d8c36f6771a64ed81afc8fd0c0d8d5f79d4f919ecb4506badedaf8fda5e3a9de7f8dea577def88c1e0f1b0e14e08a47ff85c2a928f95663d6e2f5d1e9dcff9f001c1306db2243ab0c34ceb26432c877597602bb2e8bf463d1f9afa8e39de67b3b43f54e80bae59896dfc7569b043319cb53e379215b0c8c8b8a7c67f2e6698d72a343f303e715db24d77ad2acfe83d3fa1a65d1f94d719c8d4516b021ae0a488cbc52b862b54cc3b2a858be86a9437856a978eeecc851c8b0c0845cb597ce6b093dcebe0c05b375a5c823aa03e0449c9772e35bcc145c1bd7e095afdec8396017017895285b31782509314209e3c79ad3a9d80b0f7f54390d54ba0699d23e53fc0eb266fa7d777fa244a7dd9dfd7d9313f8cd3980076e5ab1e62c6aefd049aecb887ddf2323fe90df38b2bf76a36f305ee2b430ceb0b2879c63800ca9dca94a5b0dd6ff768d516224ee7d9fbe04d42ae16f8967182d55155f0125526c8cbb1f651ff2867c8f301621f65dc3334fe96b5ef49f9d913c71420533ac86d769173c2e9401ab74c243696de8db47068b7e0de56b63be10cab5a6f09f7740021fb9be99a60307a90676a65c5578f9963a4cc256829811fb2b45cd13085e33c2390f21f9cce78ace23cf2788527024e1041bdc1a430f6cdc57f36b617c1a45267990addfc14380ddb3fff321b0eb61bae24af2ad33de982b0901cf760ec0bdc59cf2a1859e9f0e4269c3e82c06c354ac7552230d26f955da5df27b1957e9570eb687b724da2ea8a377bd068ab26ca9cad6d50abd1d87be51c5d15186b4e0c898c7db513b0bdaf74df89b18fc88770e32e0263df00adfa8afab90f160e7f96c33e89d3533fbe0dfbf6e1d1e0799638472e75e01a2dadec7ff6d3dabd5fa79f3d5d3d74f1ae2ed72793031290757dbbc7136a782db59ff8c85370b2c08025dfb768a8d487797c1fb3b0568af71ac2cf2ce91822fcbebae6b7b154747d6cc2855262fc201294547a187f5db21858930fc157c57f9721204018aeef0bd79ec5d93271325891eb61f06fb70e6d4618abc0efceae62c20310a9063371e5cac44185c78c67254f531638d9c61ee5fa2a4f02dd8b8327c40b271ecc475f411375ed945f2bce50803d41f84bba76bb3e48fa814d610b83136a0589682a57d852f601a757fc627000fbc4d2e2e9035492d7aef38f07aa0b2ab0e8d444d6ca9f2c5b1f92253de07ffb0a17c7990a05665d1cd97f57c089001d3e627598132e8b9e963589234398950076b0876b316eec96c8148b8a2ff3fabccc76c488cdd72a928f6a53ed3a95296833ab8fec8b77be7f10bb1c14ee36c17ebf3108468039b7fdb468bf2f0c6982bfa45e09d88356659a47e95f2fb4952ad29dbc046ad40ea9aed96027fd6243fa4b0d4e2927b06ef2cb77bbf1ca0d66055146aadf8c967858499ee6c85ec3cddf1155c4970cdad5eaedf0afd1c9e7425871e5a1f0d03aed0f042a96b81bb19772dff69f012dbe5d5ef90d2c5052b493fd9c2d0f032d9ebfbc648d5c887cf90b1fd5c93647b4628a8da8d0bfabfc0cb568faf2926d741670a2bb384b7884da13df42a20b91ba8da822951014bda239c58d4e16a4e7d046c7082e741a1510b98c376c596d71227b03550f4cc767bf3c414bc2219c7e2a1cf049bd21cab406de78576d9be0c7aa08b2c24dbc34c752a6466671dd05bb9d0ee2a35b3d5b5828f3e0ce7b48de3f4021657c997e98db2004a823b5e7f98e5fa7d010d6422a023d7850f982512a0248837c6eff2322fb7b77b6862b712b3d094eb835559d72a14689b014ad943bcd3d978942bd1c5059ead3f763339184e10a9303b6b117e2de1eb3003d7064ebabfe52136cb6c999065ecee0380588c7743c848580967695f6c8e67a51a1286565e2a01fb69b104d74be196c30e4270505ef4491e8c8d0103116093c84414cd8697484bb6357465350c180a4eefc6a8399ef384bb39ab2e52f8ac410f13741d8c52f0d467a5462e4852c208ec49f65635abc2251f3a9d7975774e076aaf7971790b78aa394f1587b377075ef14d6ef3acd7c03a7b805f3151bb09847b4045c0a2921ce4e448fc1e0dc2c7833bc5cd4b76de9187654f65e78de35e0c3db36a464fff70f295b04c0fa2c4b03f660a5e875f1acafe4333cc07706290abc7fbae88ae1fa8cd454ee1150b6833e437fc7cf59c28dcf99d07123860aebf1e36f1d25f5c7f4dc3fc578f615dc25340ec59fc27b0c0a5d2b229a3bc7446e313534d9156ccf28a2dd7366ce439da125c124aa8cce12f62f49b57b190c0efedee7d9f49ec8f91013f863474e2df0cd8782d978db791f84b9027bc804916bea310002f7ec933a7f00d10af21d1176aef1a78249ff41ca4603b67ded5bb46ecb6c3e084b465e733cba989d90a92c839bedf72b51108d7bba586494b95c5d41906474ca12e87a9f88e102f0ec7d9ccb23027534bd71218b224a5fa1f21b21679bbaae9094339aa79ca68e9ce5e4cfe626d7cc98484f35db7ba5bc1409044b5e3f6888dce2e8709efd2283327ea95f037c4cfb2082e526e713fb8ebda8417eeab48e9d8c78fbfe74e0bc08129ddc3895533a2926798009eb2f19c884e2256983882467d876d1f6ac0bbf5027e24ef0dacc9967895c09a803e858799a4ad6e5a458b7cc3f73d68527f36ef3f2659d96c945ab4137241ae4280559092d502d739c30917b3263e7321b53bc2e15fe5fa9065ddc2b81d2bc11822fff8ff012c384fdbef667b7a3e8aa276126810f2237fa9a7b1c96c32b20937963729eb527ea326982b5017234ce330a12ebd1c93d05fc06decd5abc694d046e0a517950fe005ef86dc66238610be6716b67e54d9f427c845c9965a3146553794fc236b2b050d7aecc440a00c3d46c8a5149dbd902256a1b4b8073bf68521e6bc186c392b6864311d21bbbd72684415e21f0428891655e96eeae276a1958c8fa7ee031e2bba2d75ab525ae2d3100a20741823747d892c46835803c1dd38ef419474d9d0573d303239b10fd6c104891cb98554459d2ba41382241d86db4049638b1e9eef17edc3332e9038f51b15123f95134c575d2ac7e8921a48d38f7e5a9ad6d25c0793498c25347c80cc92f6de08be9c5092506dbc9a1c9a1e1646cbf7d21f9bba110741ecf26d9ede1aba247b7fb02fb8e80f773c6af5dd4807de848e57fa0fef3595cdf71010152e67c0b4988a28d431faf8a0ba01dd423e3e3d1030f29643fe17bb72f29a1dd80324a5f92ae414d907655e1d97f688b0140738afa5d1ea610b5a8d7348a508cfaeaf2bb8924e462b6516cf15a5f882b1df813803c4cbc0f1b821ff974364e3e312ee21c2a6d3fd0854d8e2e086979292a2dbf43d3ae08a016cfc384680f66cb64c97e8fef67917d2d3e34228ee539f47ce0595825361a264187504b2920c1182013784d06d418b89dafc1469d6f4fab5d8386e940ef11e46fe685cce87337929155f15006687de0d1b1bfd9f9487c4e77d623bbcb9c96da745ffe477dd362e7f6bbb7f37690c388529cab9ccbc577ef96cd0d32ec9981730cf47b2753fcc26407e27aa5b32a229ca3613c02806859097b75cc96c46655fbfa208aa902232821c2402e780cf933608b98f0f3c52f79b15ec196283f481d006bd43fce075e5618be101a717c810b2e073914c711549fe22c39bcf25f1d0b4c636ef92c2fc97651011b59f99c6a05157c7f7aab0fa1645cba9e7395dff61e13e6552bfbb2ef415f46d5ae71f0f7cfe65f3c5b39b5d34db04c5f166702b2fdce5f04c487ed3028accbf3da510c05cf104bcf5633ab4a44ea32df7814acbd72fefaa3f1da9fff62974d833c0b732ba563634168a75cbabd4b4785c762393ca75efeff7bf5150478509df5f8709fd330df110ca2198b5e4ce92fc5c613e4d41c21e79029de18c01ba130972557153d76b21c04b50d6e1a013158f5c47e8a1c77b0aa5ed18ecedc4a523a5d6b0aee7ea0a75572a0b54ad2cf96489577c86edf4e906b1913dea529cd7e9dda1310019c6dc51b4641a038ffd7924b80b9af6a12dc7f8ab09770612152004efca8a65395dd248c96aa4845452b884ae48ac74d7eca51b40e9bbca282e14a265938706d6b101dd7f5337d4b725c61e1a79e46a29303b78d512adfefeb0b1e7540df32f7e513b51def3dc7ce29176722ebd991ab5acf9d7617403c6985cef3601ee5496c7582ae4ec98593a5b746586fde02aac4a2065be599adbe20edb135e9d8ab13ac00add4fa50eb592d5630bfc4c2d325598bb97ee38956233d1eda5602601bdeeaaa8d2904764322021c3a64a144cdc2e8b76ca3b9825144e9ef8a5b9cb2ad281db08caad09dc51d19670b975128d5832d4a44ecab2dd4e392396eadfd322f9479c637e4fc10b55b2afc66e6de7ea30ddfb56bfed8d3e05e0a4d2751f49310c4419262269bc9a2ebf4f8bba0bb94d452fc60a2ed6bb0e578e4916ebbe0ff95ce265a115d4be44b6bf650b5a4eb94fa77341969b3c0ebcb07bb0c7821bc0709a93b4f2478454b741087a35cd07a13a685675ccc2eabc25d8ad3c7f5af234e7241f623b133afa9489f8453227b0a7a259d43721100714d2857f9615cb846ba363ef789bdee76bd438c40c7fea960a8bd55513033348699b7961cad63f6f42d66cc431e660e48bfc1adf43aa0dccd01e4b1ef20decfb4d5cd47021fdceea9e16328a0de9e33187ba04011bf12683b2ff85e8602730da0eeb96e5e1f2ce64112bcf9258ca1ec60093077f690daf1f0b4e95233a4d87c092d8bb758a856be895afdedce82e5148085f7e1aa71e279b8609cc1f89c8f2dfe7201868e754b57eca92a074b05ec878cd5db88ef975ce4906255b3199ffd16caf0fdd4c9cab70ae3872cd807b8bf4a364babb7acd6a989c89eeec35172fc47e398a85490a272b8feb983e69ba725f66a4d10556db21599d82805f6b186bec99ae06d8c068dcca64cd4d34b339b5e9076ab1a4fbd8d93d0a378cece535fb0b6fa581f66561d14b99b374f18c63063e1e7b7d1bb02ce395772621fda74625592a1509bfb7476f1089809039b7c2a26c2d64c320a5252f91e4414ba9061109f2860c2df52966398af4e4c210b6b269869702a909a19ba3482e066d50f8860673122de6304c1e9f974482078faffbbd3a24d040a2498cde2b5b943e20e430a95b7f307517543042edd7efaf93c99abbf5337fa85bcb7c53361c8bcf45bd1df499767c06e6b73ca5a7d4e06d6ccd0628eb48813e87d6a4cf519c1022b4dc4c5bb33bd6f5ca7d45509dffd2e6e76fcc88325810881ff9d11147fbc6a2a1b97fcfb9b5426003eae371b2992d95c0190ebc3bd6ef09e792f93e061a6ea994b32d401febadd90de2a5a6af4658c78c6298fb31bdcce8148b972d98b7cf0d3731e6d91f840062b4afac78bfb64b56e65b2de22231bbb313dfa49704fbf7cb077598514e25e43409fbc3b4e65db3b0c0ce5b835402e66bb09dd55a86ea57be1e582366d67ea58d365943c4cca29101ce70af911d6cc3cc204eb2fbcd07079a098c6fb99d632db6ed099b602002ae7000c4231c70832c87bf8b916facb0016104069e0297a228e4f24b1ffc9e37e8e604403ce491e4469cd0592bce9a5fcd2c1a960d2a96176952e6e6c7c53c950e2cc3efe17f4f8c0215f7f4c9d81c615e7c112cf37234325a9c6a041abee4f2e1483525ed7ec6f864c25985ac92e2b098f6ae42ee429a451305383627096bcf95018cb22cd9a1140ec78319de29c8b5211874042a1a2b6b2005f714d3c5688ad364ebf96c59216c32d325bd66b4149e8acf9817483c9f7264dee0ee6c17252fab5f1afb4e41f5d513ba4cd18c818c557174f6142668932111ae8886235d28355e8ffca886470becade505fa99472c03cdb64e9c76d20e2fcf77d002c69b653060cc13920b703afa67b36190193abfae2edea2b75ba65a8a94f147cb10c388915d2e620c813591476b40526bb0487d18e28d882d588be8c1b6c01b8c92b4423a80279b31e14408086d27f1795adaaee62273c4a2c09674948107482445230d40c2d51200613e4a7801e2d09158503d1c63e1913580a22056a8523239fd8b5eb7ad4d6688b65dd946c63c5e0343e9e207b744fe28390bebe69ab19bce1671db644f5c4d706294bfcf23b1045c0dae24a1b548f832d704c1171d668e046a29055765f64cd122c83349280207f713ec6d7e3362320df75136a5305a49103ba72b27f5820cc524a0783fcc92540a636b56512542300ffde127ca48755ca33eaeb4a3fcad21b18b0a6c2379d0b877c96e7020070fc623abe006cc2c593f76252b6698f12d8255385b3bfd9974e5664117d49c8de9b6cb9a59429c914a5072608a0075147ff084d17cc60fd434a95744317dfa1ba4da541305656914d0db60632d8fed8b42a74adb921786955adb4caab6ed3afb7386cf5c686d996964d76f5943d656b3626b253d844a20f1a8cb25bf64b6db5fa6d255772255752ca8f88a505d7552ec431eea87bee91b6ef65b41db17e7ba46e7941a26cc8532ecf3661d3d8909f7eeb51b2e1dfe84d0d4697c2cac299764c0505b9504389c5a729a62b2f08cce0cb7f8a89415f6a3042b9fd852745cd08fb5d7563172737642a9f6bacff1642080db28ff94db5213e710038c4184fa85f0485a4bb7aeaf54fda0c6020d611c6ebd3f3b95556bd12b3f14fe85437723e47b1bbbbbbbbd65a8f1c8b952ae254413535e54d0daa9973e7fa57ab34cd397343ae9a33e6c63f8a4b110a0b8ff598d8138cd8eb62c0b08e68e958869283e5862e0575e39c7136cd836f9443e7862e0525e78d508cc5f735cdc3d930778cd1ca78dd3b07c1e7cf8f0760c6cacfe353f3a8f5b6b1215715b1d61381244b57d8ec271b3b35c8c15b8a032d754d70f76fd67529068acfba633da64de9857c879f7a46664f0492d0a9725d2a2e71749a6efca3168a11ca56e5912d2dc50bf0e0cebe6106ee3c2a21def9448e14462b979fbb1d57b08f69d085441063fc1a638c532ed56044622b2c53c9e8a166fcb1f62012740130c813b99c6c1524a61b72951179b9cf6c56242bc29572d42ff71e0d36acd9d31d2bb0f46764ee699023a458f71d21c5584158314887f41212e359ffa21f9f6ebf75de11ffa3d677cff25e42fc488c7b7aa4f5befa55fcd949a0e76dacdcd3a220dd778414eb7ea394fb20acef28c6aac32207e97edba851358af1acefa8f531e867b46d528f04fc59efe560c31a3bef48f7f6e30a859e6fcdcf0bdfc5747ef835fb59c429cdb49f45fcd2af48ac5f18b38c9f64025a5e52cf97a5df7e475adfbd7722b4be9751f7bd84b0de9f553b8ff5ac2772cab1e91971cecac609e89eebd35823042e223e25d80b7024d4d0831bbff5b343aa1cbd19bdd67b566c5867b6630536fbf8f463adcd7dfb4e57146375a4cbfacc7b01b14fff957df721d2cd4ca81fae3e3ef55e402a53ddd667b1fbb1fafac9ee595ef79af44224ae5ab95c3dd0faa61835b0c165282aae5031755b9f3da07b1a56f6da15458f487f186fd6fd583deb930dfa0784fb2cf3340ef403fceb7b25b02dd66bdd1722c9d80de95fc9d0e76637a3f4b3c9f28a6419fd2c760ff0cb755e9178b9af886f5fe85e96d198d4d967c46fbf5c791ce8d73e1328fd5e3fb2ef05a4be7f28bb1fdc60483fd2be6d3912f4110cc443ce1e0dcaafe39c2cb93010f7fb833d5b4ae9359b93cae539e753c96c3ca8536c910a7318b558f0d6a128050a150af9cd11055888d983f8c523ba5ce9e5f613c1e011285879310822bedcfe1f18944188b90d24defa452706923f2c61bd58a575c569ea7664ea9aaa24314c5a2614eedc6e40ebfa73d26d8fdedefe8d0245d29a52b1796cee98bd6d904e4a4106ca420a6d308f301eea98655946a3dc3e6e7960b0ee7e10e0fa34ccdd12b1be8492a684ceb9a3259973ceb92599f354c164712749994e09d5a6821455ca93357162369973cec9056dd2e4882964d07ab0a658a1e7888642082396cc07ffe75da87ea54049ec3084154ed04045455e6102059dc354032b3141f650dcb007d4144e6ec84f5041526fc258a3a429a19452eaa50a2a6a157652a8e20e8d32e79c93ce4922abd0228a0a3ee78a2e0d0b090834349440872aaeb0e2c9ffbc79f39455b14605695dd6c75a3071bca0f64d1a1c59bd0cf5a6cc0dedcb293e9b72ce3748ace077ce39c12ede68916f787012f98d0d4b2cc8374a5254ddd0be1072e4c869aefad81aaed8a4e887941bebf0c840459fc6a0c76e16f16def039d75d7009f1d9dd545fc4a59f4fa31affc79e52743eeba0fa25e451ded2c078a28a5947215853806350f8dbff0bbfc610ed7c7618ccb40714d0458a8c793c043f139c64e36f3478e1cb24ce33ea7fb941d7cffaa6a610ed787c1ed8ed1234ecd28986fb33a76374f9613d162aea05b30a160cc2c2920e924e0d0691ea2d0c18e17477899410c1776a40e52bc10a5704297b0cc610799e509a5945236db9299a539e79c5bb42496ce6001536689cc218a3c38bab80e021531a490c50b7660708a414561270a298c608edededebc00792363e08a715737ba96ed0410f884c8df3c98b71a3dc6e81ee8910ad12a1f4941fbd51fadbe97907aa4fdeab35d100d29cb8b525b836050a305954be3b21a2d92ee2ae9fa1663e762a0d690b02110b96e8df8ae27567e3abc8ce66fd33b92c27cfa47d9d3ef25847efdfa1dc93efb65e1587741e6571d5614841dd6e06dfe6929a557ab11af3134e2d78b8df1ce90b261e50924ebd95aedd71e840e8df8cdbe703aa5596c31db7caef40b91aee6bd7bfdd4cbe873d461f4fba83639cce46fad785d57fb8d3fb0d9d3cf3edba4c746b0f68528d4a75e18847ff5fcbd7ec4d79efbedbbedbd82dbb7f7fc85aeeab18725dc50fe10d7d5e86fabee39efc8eb47f6abd5e7dacb8c662f1becbc7e5f7df4b28f9da669da735e1d12224529dfdcec7fd0cf68a5af7df54ca0affd466b5d7daf9708fedd1bf5733f02c7942cf15fd2cf7decf50be97f202265b4289b6a98628c5c966dcd5572488332f3a10648a2c776231e1b729c127c931cc75b59997dd7de40a615bc3be7ee3e65dcb05e4966862eab276a758f05451837c6b8c48a4be365f3e07169bcab2ba2e1fa0bba22d7054b66b817bc055dd10516fc8caec88219cf65ee8cf7e98a66f8bc8caec8a70032a264e0b83264bc1d7365442941726524a9f2fc262a05237ccca9f3863560d77d6d73bb28244144252d0d453279b258cde76a3f57d3c0b8e26a618401834c0e28305c30c01765044085c31ad7816ef8737de8ba7781e7ba175dae7fd142175baeef7822e9e9d28832521084d405cf972f7440a2a44649621142c4192584b83367694af6f0f1adab8b2b61b8cda52fe9db00ac10b9aa252e7d59b9b8e1d26f592323ae9ca9a109942e26903c68b182e32ba59492c55dea83c10d6ba4d962882a2855415cfaac05144f292d028719435c5ca5d1428d16488ae48f1a353c74c31a2b9872a91aa815dc61228b2f97feaa2ba23f7628163c0061a1e6d2d7ba22ea0202aa8b3ca410183c58e0e1e14a2d5f5ab42b9a63ae78e2ca9f49288f2b942e6582cea5575cb9d44529a55d0d3b6899e109a8289cc81d82684329a551892b5fca6a0410ad035cd9a4c7d2a89366050bc2dc71d59942698618a70bead059c167aaca823a5eac20038cd30c3b4e96629471f2b1d342cf1a19766e7062a798a69a62d82c515a524f3d45b0c022f364c57462a85a76d840e1ca24b1ecc459dac254e9ecdc5995e1ec78a18599daa044a960a856539c645254da0d4fe8d454f58185d9942121472853459960c14798da32a558a14700c324a1acc013471853e577902cb198a5762d599f79394a69e986f63fa62a9d3063c4065466c2c0943063260c336f02800233564099c90206eec1ff018518d3303ec3e6cd0842d0f17721fb50679e38d9accbfab8a82e33c1b441155b5f0077b03d337050c6763ec048605746dc60b2aea81e2c8dcb5067daa839a3050726b0f632141a261c0461e56528343074b0049a18b8a0a90216292555283478aebc1cc504889a467ec040524af9335e4687c085667cd149c6e7b4f919b9c5c9bfa393537b216850dadb5e062cd8beaf7e19fff28ff148b157ff8c7ff97794c5fd339a71e413a570623346082f8208f326d6de14368c4edcd5b2854bf3882f1166bc8c1732e37bcd8f21e435bf2016bccf736c8637c5f09a5ef367fc8bbecf7c19ffa2f463cc789fef288606868d5ba2d30caf4e4f460c3b592d5bc442710aa7f0001cc56418cd16da5aadebbb19a760a16efa81857aca9d78a8e7ed6f5fc86fb80d0b0d198e3ac00da35310cc73f226060aa3d395641836e5423e45471b100d19eb4d55ea80815efdc2a7e14dd2c730907ce9f235505ae2d132a9f565c78ac54ce632475d3e40fd3008e65da6c15a7da21030900bc52c0c237ac3fa421d6d40344f66fb42171f400b0a75c0408f7e215755d9f8452c0cd4ffa6898d71a83450a2ba336b20e221edca0fe10703a434c240f2a50c6a50d24062c3a0fb2fc45588104519a5121a141465f5a15f0d5783f25d4f49e23031c5263c24b770b95a8c04312221c85410128884800d150fc9205808749893161ee2d897332e3251472465494957f8d2cf3e99cd7eea713569529b94b4c4431dbf081ceefcfe0a033592fe72fb6392152b6e686fff2775a1a2a27092ee002e434901c3dd5afc2359eeeeeeee524af9bc926337aebb197d38fd6ea8a8a44b68b0692029212669a064e9ef7822e9f6c73b3db8ef8f639a47f7fd318a7d90109f044025e9c647bafd118a87b4efef383da85a055f6e57cf9eabb56ea1e5cbf52f32d1225abb4da5795471a1d677670083657deb8dcc4b89da87442974f3c85483a51f910f095bff82276c1899989a877c252c12b21ecd636e3884c7c8cee306394f3210fdc85402fd76b99828c779803ffd6a784149f0af3f6bade4b1f2c75c60bb3c86f5bdfc39e641c3fae5d743d362ff06d1e686b1ca8e226c18745b4f36fed956726cebb21cdf1144801e02906c226080201ed22efd1e3c945d4a298d94ee6890067957580b368fb075e9d3a78fa379bc44982fdf68befc5e6e24ffe534c8ebe16058f0fb06a9f7848dcf2d1ef2a74fdfe5424eb15d8bd78fecf267998f23ff1b82af20b1cb135c945f12ebb098041737ecf1ce64c3ef6e23b6b8758d06df07b38e0b6e5cc1b11cfbf0501d29b788237f4c6a9fd78fbeb7430f5caf21bb600561c35aabec8af43512af4767f9137511bffef93050af56b4bfc6cbe95397b3bbb3ec9d66efd9f7aaaf5a332dabd98a66b4468334fb5c0c445d48946905875da8d6af66f139ca961d65ec2867d3bebe84c423d947a4d8ecd7be5f46dad14b483752cc575fbfc8c45ffdfce339992753c97d822596b0239528a5948af912a6c91c33658b3995d426d9145a9d25b78261c527fa7cf4c1ee75d655cd42326c3ef5a6cf902bbbd3b4164d8a1224a14a73ce3971e0acc89aa024c86850a299d214154c4f64c8d8d089939da1d366632e5aa89374450c5f5889f23fadcb5074cad0792242894f09cbb97a5c574baa25852403fbf23753f431bf768add020ff9d7276020f95576fe0555ac43d9f935d34ba766f26abd9db8a71c2669d59572cb93ed0fa55383528bbbbbbb3bcd6a56b39a65533a15f13c72cb4c63a58cc997f225a5d3952fe593749a5912175bcfc8ec2ebf38c34b66f6cf04feea00bcd8500ea08b0debe54827ba942edb3f966ea4bb7b411a74b11c638f066316fbf10586e5675d6139cbc62a39dde8aa11b5029b1b81e460f969ac60f9392e2c3f8d26cbcf6961f95759587ece0acbbfb962b0fff1fb698d952fbf23f225443b32ebcbef6524e94bffa691cf77ed031d16654357c631d72ae9e9c95aaa6ecf9e3cd2aae8c3bf6ea25df050fc3a06a9c36dd9e4591303655318d6df2c75f0ac04d3091bcaa62a4a45a9eacd92a20fff4ec2427d03c3fc5b0505ddb8a18106afd70d69950c1937a455d90978c83f1b32a7e8237e5d259eccb2afc618a43583e5a0fb65d7bf12c2c84005eb3f4370ffa0c12f96a5571692672453ca9c65f2e7c7ccccccb17f91735e13e6471dfe8573ce497d4e77671f43bf3f32f3836c57aeeceb17cea79ff48d29fff44c4aeab37e6c3e98b579d0d9834ab13dc3fa4dfef931d07c39db063bb3e7ccb3f7ccdd9d4ee7a4b75fa553122e9862fde51070052a3fd9a046e99c93524a69651b73596599ad2bfbfa8594d2f7d64aa394e9277f8d95ae7243db1c1c1b3fb4769b2fa5d632217b7bf9f20db3eff5c35efa56725b99e1b2496da756b3596687317fdfd1b9fb0423d90581afc6401c65fdd8ddac3e0b8a3f4ebf6d153ffa8842d661433e30069b7d17f13c79eefc000085ed9e7b18a5dfd4697baf9e3b23f1fec8600525a8103e2019aca084822bb39a7943828e6ef6e4759ed39698c47dafd5b31e2976f45a716fd4fd5196d567c4fd113bac23f24ceb07540e04095a7536a66672c505b9da576ff34222770baa755bfde6199137669ccc3167556b500fb0d6ef2a653a72b7a05b3b22dfbc217eb72d7ba2ae73ca5b2f145d8146e1c6f899c07a9e3c4c3d3f645580e3b2428aa156d13452cb45a105153e3b1df87abe0adc215fec351b27a06a14a87d6b833de4a7860fb6bdea853528e0dae28324c5dee1c6d42b77a2a0246c2a777efce2d2276a10dc3afdab3744bbd37f2efd420adc213f57360f198ba2b4dca960cbe001f0fc2e1a6c28a5dc7e0078ff1978367078ee0ee53ad3f5a5eb9ffca8e5d29f1edc3bd32bc2527e7d27a7ef1efd1e4abf5b2cfa1db73dffdd322e7695b6d25418a8df29d392cfa5c914e91a4ad5604f6c2a3ce4b7eb58b9dc2da0bbf30e03f5bf1abae6f627e08654eaf6bf7ec40fe79ddb318e8cef5a2d8871e89ae843aa79b00b517bc5e9125df2f61933aef4e61b0094b131261719a582944be372940a4e57724f13cac6383ca4cddbf3126183e7e085800febe46f4a80beefe037f080707836dec3f130cffb0e827cde14d1ffcf87fce4c6c67fff46dfff3ced5efe7d363ea39feffb8838ac8170bcc77770bcf737bc0e1ec8037a22df9aa311c777248b3f8ef77e03ef7bd0bbf1409e8defc0fbe7c0037a98d7c17bde919f07df3da39fcf3e0eef083b6c83204a6c7c47b218d978d81fa9ff1db1f1c19ea877b8c1f13fdfbfc38d8dff0fedcbdfc8c6e378a3ff9fcfba977f22e0781b6f64e38d7ebeff1d58d8ef6f3c070ffbf083e6f7e1f81b9ff73bb0b040dfc16ff0e0051f4409d077f03fde911bfff33872bc87c3fb1c38be1b379e633f39fe7ba31bfff3396e7c471e8eef891c47920601781168e0e1d0e003f044fd9a36bca58ed9f8f794304140ccc62f75ec7f7a416cbcf446e0988d774f88c7fe6f784772fc8d07f28eb218e9f80ebe23254c121003faa518ebe0757c2fff7f8e1d2dc518d0bfdc3b92c528c7f7f2cff11cbbf1475efebd44d0f138de28c7df781ddf5116a3ff1cdf5107ff7f04f4369e63389ec8813c8ebd67c36387edc0638b7018d703485787d7081cc3f147fc1413c18d17018ee7588c85910948098eefc637af743061de5cf22607de065e00bc977c8efd7cf18af7925facf27dec30cfe398e6c5022eaf25468cbf315c3786651692317e2e7dffe4c243f6dbc7e743ebf3af2f74f97cf8e0f599532e7480eaae9fe2823a6e28a76e5be0c509b91bd2a513a40c2fdc71c3b914ce23b8c1cc7e1e3050f6d26b58f63b82b2677d2178b3ef6453b67d2bd9247d4269b02753f6cd2522ac9cefcde33d8e6d36bc23b6a28a229262287448a05012cb223f86b2c98697c1bf07802506eaa5a8a464632ff150d75ab2f1ff05a87e8a4f36bc230e63878ba6180a1d12306462598808804ad27d01f1f94929cdc3bf5794ba52f70ca5734d1c7fd31fa9629517e3c8407a75065a9c4e4ae716d65e8e6a52e786d6095ba700f3d849b54113343770b074095818bb41c1e21811c3096ec860b5cb514d7668e2060ea6d8ec7254132a514d5450a1d1ac71185882a8264b3026b65e8e6a920456452d2dddb0c5883d3fa0952c00781d830e9391d3b48ca39ccb67b2b495c7ae39f8f3355c96ebef18db0220ba6ece07ce4fad2e17e8deeede2e996bdb6e29a59452cae972d5eadeeedeedeeee1e4677daddddddddddddddddddddcc1e6b5633dbdd168c891cbf7ee2cb96d3a7f469a5f4e8eeee3eddbdcacccd63dbb81827c7e1abde9e3be7b43d5c10e7838dcfb1e9996105671f9d9eb4eecc2d2959b29bdc26b4d2404f4ae9eeeeb23a33334797524aca75c58172cb28375db2b816e703c7c6f7766666890597c5c697cccc2c8362581968dce5486d4a275a9b34b35c6b955a46679eace59e676a92524a3549bfd612cb0d4a2d8fbb9c4efb0b7c95ae71ee56f6d0430f31e41472dce568529335a3938ed46a46670f313030e2c96378d0608f1e2c7b7a68cca0b2517a936696de217aa259cbcdb362b78a110c8a42b109cf2acb326e3e07a475b32b5b4a6c7ca2bea2803b1bb7e23a6d55a90780411804ae7b0ccee7ba1ff2d20910b9b856d6b2140ef89e8bc613cfa6553775537f37b5f74c4bd497c8870ab7327cba46833e3e5dd17c9f0e8478b7ef91351109e103063491e8e3b2eb2a8081180f118765868c18b56948d597b2baaafd2a41f974b716c72f906881a10446125c1e2a3d75f2741191b461a590c38c242e2851a25e71dac2c275289f0f5d6acc8d5467dec42dc6a51b5f32c9a6282638dc5056915602bf3557b533ae1aff036a727eae5ae7ac9e03f31712b9fcee69316ace9e11d97dabf5acdf9cc7cf5baf46c36495a42b5c4cda925445ca690b978e49639a4774aae1b6969854030f456b5f0cd419e5be90b3cd43d3be6d83fe5af36885ace7063b20ddb79e763f58dffac2ee877faf1fff99c07d7d13581ff7b1731edfcd0bebbdb776f6e372fd5416c45663cb6f296b6b5ab665f57385f2fac88fb7b0fd527619374565839d7d3472552e06a795317e1f2748ea10db25730d1777f4b136f2701e91fb6958fbc795a53733478dc50c245faae988d4d1842de4fa823556b0ab973cf4bacc1139ac351937cdbb526a14b8f5d3a17b2217b2600e192f49b1fe10788abb27c81bdaebc3403e633608d1076d14f2164720982fe70bf22708f65a2f7b0c9ad81882526280c5ceefad2b821297ec09f2b6d2f471811c59f6072d4eb23069c70d665d7da275355ab027c8195d51f3990f78a559502de058210a777b9f79675cfeee4090cf711fe76d2bad66744abf1bc771ab8de32c6bd3a17ba1eeb3b55ad75f30eb16602f064d408860ac50b9fe12c808ee7c39878c97a41b9f8ee04a2963dd04bf61816a4900b458d762fde517065961c0c4fafbac33ae3ee7e0baea75f66f48d06dd939a0748d6ec6bba29ef7aec8468dfb0171ece8211bbcdb13750cbff5930de82be3eb0848323821c1020625b2a071136386ad7ef3647c2b2f94778be1c5cd0b8feecb7ecfc70ddacf6708775b9f0d3595c16deb3ce95b08384f3e6fab2f3cbad525b38e63441a6563fa723e9dd3887b992165432458fce49c1f90ee8847bb62e3e253c5f6f21b48bf34322f064454adc72552c788878a14173684b1248822b0a041bac10b2a52f0c49aa617a03883a51ef1a5890b4b5bd4489971e20e9427636a70a18a34f38212197c50228a175e52e044132c984c0933450a253985a5bb301126490e5cac5461d102945c420e1a178620c10ada784953c606364ddacc3047094c1d32588a50f183152956cc14d1650c92152469b2f06108252b5188711c783042c30209dc2041c39a3261d88b28787e386229890c45c43c0a9326f06899414e5211601070c50a2a2559e84c8142aa480f767218020571b248e14b12121f78c0f24445c78d12333411461b20f0a8417243145216195878d374c4164c501af00186285190c0f3660a05484c48be809344c40b4c5978d12189282827a0a0c9d243122e28beb800c60b4b9078e1e9d5449326a29ea2c890844422ce1b26a0f2cc80e4c9982e3d749b33499871a24c0a787e807204961f5334c8f004551518d0482e0ee7688a9492921b92ec70c5882fb248c1c1ce114f9c48228bb02c3081a50c1e20bcd040440d5257527053039425e4c82db379b0001c12acd0c59c1380791a824a082849ba8c31c2890c42fccc2084933a2e8019834706b1c110456c21e70b0c4b3c915a6201e29c3b1828488cfd183dba7497d2047fcbf9b894f2db06e0c532736c61e39fed7fdbd1e08423e55a96a282caf5e8cd0a8faef605c99ecfb4af861b1b1a91d7ff33af2bb259966599b31cab7d4fd8ec897ca57d6683953fe78330f61101d6317b0cdd98414d82c3e6134008cb178479ab77b780ecb320acfab357f6b5c197f6f433ff97f63103655fa5fac2cc5282534018b22ccb3249b92b9f5969a35936e79c349392f9d9a564965688bc0cfb6ab55a497777e9e2ea5dc732b3e5724193f53959f21429a59430068a43eee04f8d70ce39259d35f32624b394cccc524a29a5945232b313ac2ab6c15d00fed81579db62a27f5a16fb63fdceb2efe88f8ef891621c97fa6d2cfb5ef30ba284484c7298fc1c6e2cc7b4cf92e52542f6ab37ca7e95511965af7d47447e81a7ec439ee25864a21aeadb58babeb1bde56ba706e57b66ac7f3f295929656520fe4660398a853b37690496a35a48ba1a0f55285151b4cc1fb0a85158f82259e79c73ce6f86a72c2988422a4e9dd804e2ce29ef9c73ce282876aeac320aedb3d5afe25ca0bbbb2b9a9991797d6a9a94ac56967d8b2f708d4c6f0f071b5dabff5c1f1176f55eb4aa736a9aa6d1baad3c49572bc642c677df3801514c4e709e3ace5add7cf7b1ddd9fc699817ff6a6dfe8b962459b65e45f5301c193b4607a37750501838e4f72ef97e411359d34829074d3a77528e41ae17eefc1a3be6fc1a656c92940bc5e8c7675895b65d9f552297d218437214644727e8e4b084ba5952479ea15382254f986e08e2887af2e4facf1326782403eaeac78316cab6678ea8041ea25f2debf983feae42ba27ee7cae85826a341577c670fd2368d3f0100b2399f0e1fa00b4a13b9fd9cb9d5db24b3f01309003a8940278c0f5e79e3ba9f3263b20ad1f0a6c2baa553a23730deb276aae824be0a1eab12bd2ec4ac4bc91835d71e1c1f6e02786e2a9356ce2dc59410855f47294933437fca02dcc45d5103e709102869730f2045b7871030c29b0810c1236fc8025c825c50a2edc2c718309a27832473acd3373a20cf15b94133237e427285e739dd9506597a39c28f1840b0c6a2995a9e4b201e76e6c86c0c00e65bab860064e17282658f26632b1444e0d486aa05343d2172820020b1f9e9023656e804a933356a058b303179985c9d28a09262d38993f9f6813a529b436dbea7294132e576cccee85ae382465fff42bd2baac8fb54feca65d8e7202844cc1c7449b945ad5b41ab34863f4e862ea8dee4aaec7a406659426868ac2f247270957be7b174cb1fc52724bf68e6eb60c54b0fc910121307f80d4b1973cc39841068a3f35a4e5cf66d44bb03def60201b622cf3d768707a4b2943ee5a8e6777fc9658304ba794ba9c9e0e4456b2c75ab2f27988cf8df23959e98c69985d0ce4534379e7bd7bd8f8e7b3cc595096654fc199516a5d73d6ca94259d328b9369fc9a3d675ea0eda8044e974435e9d0d4000000404000e314000020100a07042281301c1ed2e55dee14000d7992466e5c9ccc635190c32808216388210410000801060898a9a1210e00686d76025bc4ea29c66fddb6a2625ef32650a17d51929d62e56f757534b5b9e7b69a46ae0504606dd7041902b0ed88c9166c7edae2f917205454e88abc5ac38df461653ceac3153de72f6d48708df4bdc8f5494b6e6f0e589b8d05e1a32f599c0eb4703af805f5e7ba19063c3213553b890b1e3413f8104878e29d927f86f3302866c12b308e9971a5f0e562c1f5372fe18aaf8e2e40d0dbf772add0bb868d736f6fa555237dfae0d3b0d09a62de1bf91eb8496d23c5ea087cc370f1123ee8c4dd3f680660bf4b8ff0ee53012625341a6c7d3a262ef00e23d8b42b83ae8125ec0359d869361d26e8998536ce15c471e4ef808d8917825f6f8af2787bd0dea78a4cc528c7fa1ccb668f297dba1a2587c1d45e900e0e1a3020ba5062eee10cb5761b9ebed3d6a6d0672b8cf10a10ad077cb63340cf968c0f51a2c04565806f62d25b8e8db508a6099e4a810b9d053c74d86a66cec8875fddde71ff2ec76a5123d2803790b430886eec3d83b57e1dd3db0386ad98029a74eef4ad1fd92abbf0e77a4fb6aa8ac22bc1126c21b4315717640458ab3492e4d0679644ed8d371a52efccc24715b1d5a908ab0ba4bd7c94ae6032833ea11ccf7efa2c004ba4fe30ee2a1138e6c6868bb86721ae3d088a38a0e8c49e63b24a275a614fa83877dba3a46d7bc2abe565ac528244b10bee5e192a90248b978777b897ca8af3bc37313b80edf8696418bf91832ddad7aa5055822c6eacdf814f3f9f9f701fc49ee5b94b70642832cb9b883f994f2a0050fd767cb1d6a7618dd35b7f49a74726bc5303fdda29631a435b091c33f32810c0cdf439f1d1c71e520e206f798ef2713ae4502fd133463345999c6d44b54c9753a4b943d2c1c56e8cd6e248e01d70bb887a2bb224da2517a133f668b32dddd59f4438ffe030bc036072a3eba9ab97fe5a621d31ff371be3ff8f3cda7b6446c797ae9ff1a3c0dd77c0202b91eb36f6048e8b42033d1e9ec14d211a7d430a44e1c9956731f50156ea6d7b70d1140bd850de7c9e4e5409e4519fcfad5aa90cf606e1bd68e2c8f49a2db2e94a3cd7ed0a1e52790bca95181efaab69a04bc593b91ce64548bd3343ebb108e882c57cf4be98d06fcdce6aba45422417e8d4c8a8cd9fd09dc95ac0e273bbf35c6a92830b3daf1e96bd7448a6a9691c5377bf6d35ab30331450c9926b19468c9792ef4b554f52349020eb109f6ea8c8540457cf80334706f511ab5419a891c01b27f28bac0e8878f851d49142885f64ffbe325bffc7080865f3c8d1ae5391de18526bce8b43d3422a994182d73656537c21ed39cf4a578c435568bd8dcd4d1d0d4b8aa0c8cab19efc2c352b5157125c28f771c5eb7b36f7ac89eb8ae6fd25f3b9b8feb4ce8c9861a7ca6a4a6121de7eb91ffc4aceb45df6e58e185c55316ff4898059a018caed30ba1dff6e5e5d2bc7489a09d323a2cee41a363990e621e5ae9236bf1e9d04c747c1cf713ba3a6c59d23ccee2e929b1fcefdb7445e8f12382c344a3bc2bb8fd904dc6bf3d66c6924d4c746884e0300298393c069081b51d59ed0efa673ae13ac788742757d91846fffe312d22b0b064a9269be70c4f592c0c6c0de52d8ced45ef73c43a5beb5b2ea119b026b36a1effb37f5a12692677ca2c5541421b230b03791102770890cdc7c70a662f1e82aaadd35591c2b36d7013a371e49e4de7d03423f2dae50a3b90e9179cef4ccac2c355af7b1e8bda115ff7f73a32137f52a4f59eb8c6910e1a1193ef2084d82c9d356319c934e1fdc96888d0be44f164cb03cc48e12cf2cb060b0a25a9e54b91e06353967ab779355891c3dc4d5630fd838d8540f72d9e2e0a637784e68821893b91e26c441476473e3c5608e067d75ecc04043e73b800a7dec75044d97c73f0e933ce1b83430b4a851104e11e1a6b2069e4a1c1a262103ca332bca7b0e252e9c66c4dfdf2ae04954229363bea1f88c183b70ecc34a2b2302d3ba0bb9a111c06ec5a2dca278802217fb0855a3ec8070b1176240771efdec29d0b4880d4371392a170cdf200faa9804599f96c90c7dc90260072255633517ff0fb5095a771c7c1dc51dc867956f830a24485d39f37050231e5fcb01e0449c1a2c49b1e4c41481a94a700c177649de02bde910e27fa12161399dfc84bcac462f9142f5b20ac5b29a50cfa442a04ba756843f75e50becbe095baebe4b075a1e2dedb473f07352a268f32e322013f4ac1caec9e9796843fbf014747798d3180c661dd459b583abc0c72ffa2403f583034288187c83ee4262581cfb45a1e6eff1c18333dc76a1b123518cfc9219879c90dc6413baf3b142cd965ecc06d11508dbb941cdb956c18dc67141a85ae572d5e9e7e78289497fb28733aa74b38115f72453178729f537c645adc677481290f448b568a5e7734bc7e30ed2fdb19473908e08ef89f0e4f7324e7f1c9dbb86166edac1bf0225e2ce3801a1ff61ed21c025793a38f267ec88b38328e908ff60cdcb51542c8d424ef440efdbd7eab13d3dd9c8ee930882250051dc5fdeeb70b2ac9eb2ef950bd21cbd697f544aa562c5bdc5e1dba3a89c20da006ab3577c6090257b5ab837564a2eb0faf6063c6d3c8dc238054fabd9e346ee26029deb5232d786cbcb997eb9f237288aab003a534f990912bad043603937329654630adb6c17aa52727e42b9e776e716128f077f3333990953b6d51d344adff581943dceb93a3455d0b577294bc41bec01d431489a681f505c165eef26738811023a4f2a564dd7112fdff8d491e168d15e8f3501af80b5d1e19542ba325cbacb755d2b8af9717eca17680e11b6e5bbbc04bd11a8ab82dda34442adbcf5f4268d1d0adccebda03c2d016e87eb329efce80ba8b5c80addcc018a646550acf33944ce4a8c0a3a99038ccccaa050f73980bb598976e5920d47792da8ba2a7cd5e94a0c0a583de184e8f67ebe25b5d8c4c9c1daa61cc4dd4a2529296f5dcf99226e21b98ab904fe9253dc4fe610150284f374145d57f6d9334f104b7a92c2783915f6d230b1f5a9a1bed31536ed668cea09d414b6979e37ad5864ea0e712b725889622f2d616fe61611e03ea358e44e46af0981ccf3b25a462220d52f354dedcecd5d3f03a2169f892820944e343bb2c9455ae24665a05401e39b6f7867f7a178a544c27a1c51888c447f7710021309b1bfd87fcac3a9ce0babc7d84fde08063f1a8fec58de1666fe375287476adf88e8d03a83309f524de2674618c0ed83842607ac8ddd1bdc5326ee942db97e00be6c4d95042884d9363ab6d3128274de8e879d587779b72e62bb96127844eaa1f1eabfd65292cdf32cd2ab58d76b040ed1b39d738a4f154fdd0ff34a2dc6a1cc05a078814b075386993addf53cb2284628838aead645c15605ada73443d9b002004d930980570187ca6d7f22dcdac215541e626cb6888e985c4e0487d8e2286a221a3dba8987177bbb57b52163203ce118a1ba0a546844fa000b36aace6391cc654bbc9427b45ab9a484d59b48a9a16ceb9f3a1f81479393486371a2e6703064575dce91a832a90fdd323954a60820eca2fa9b24497b94651915cdc36438809cec30630136b7514f7169a381f181c4f93c2dd854806273e69b96b74da636c1af04d7539f3d9206487aba6d63eb9d8ae9f70d8499cba1750f5d4e032e3b8593eb412a36e382715c64f066c874f6970150148bf9182abd3ad5ae846b1ed687ccda5cd9b96800ab09087052ed6927c1c444a5fbf287ace9521cf27cd561892629188c58425f26258a2ef57351e5cb1955a56500e640e0e2c6df48a5dcd24d071482bef17d1899fd9f5630b3c036568aec8d0f6169bab3b695ee320130083f9f82d6ef2201de83da92fff4a66424813cdb2031f3da18ae33a7dae28d83102a9a3f3ee06c46f1d44f83b7824e2201297183c742391ef22a5b2de7472c4a7d4c6f47b809f68723112eea94036ea46fa763d8dcde494fc82f2e0606f80e583749591a2012c1d5a04f4b26d545a89ef91098c5cc8c3ca2dc80be25d28405725a46c9d1aca405028b1073ef6d2f94ce8a7368451c9490afecad731e617e682989c002f0d1d848f63340ee072ceb5767fb370596f4c182cf6a836d63757370a8320ee74cddd8ad0cc46927e8f9e64fa6c1c4587df416f96d9798aae74089c3ec5f0d5075c18c3763dd516627cfbc9b2e8df870996cc7a99d3e5631b9c90b64589ed12f1cae303f8d52ca09da7c26865a5a6f66fff17bd45245a315f5df46e7aea209535e5e30643bbf9388dd477456aa2a00ad4951520f4d115801f0be5e62670a9ae6fbece34b9f6cf672ee60ee0ead44152a26a3fae43e9f4588daa9010d97c7e7b0bf3c0551b55029443d6c9c00135f0d7830a266522780fa1cb1b79ae7ea0e8b14f0b3592de045824b857558e5cff771bfc9ed31ffa6b159fdee0964a6b7a534b6b0b159793b1d0c9a9c9c24bce3e66eb8cfc4bce036ecec3fa88b40706299a6698f4bcda375b51ad53854a0fa537ef266b37e9f2a718818cb075aa6d7703b5cbd5458bb027c58d54ef05f187c1799177ba2f7ad38c5de65bb4ab96a161f6e46dba55fa8d2908fdecb5a622ec76821400a23fd771735cee9dbae0df327f24846f426b84857d027f7b4ce3b6d9994f29466127cf1f4eb852f1509791d416975cc5576f68b2eb0813cc6cf025f77fca11b365f6688aee19c7e856055fa5202cd01dd05447bd22088f42d0d06e808588bd35394526e023e74e1eaea32b282610c7bf4fa4fde324a374b4cde461b7709ce3560abd8592aee09afd0d3fb4efb25bd9ed5a9e2a1fbf9fdba4bef0af6d5f74db0c0dad119684f9471bb57e5bc0df284472af5bffdb98623d08c45fb29d7b905ec28e353409cd8fbfc3c5e4fede3a8f2af098c073ffe6156539dd7c65fcf1e12c3410a7b69a5a24bb20af60b6c707b49b1d4bbb89c50eaa91c077ebf3c5b5db1ddfeb78d4e993807f1a34f62e6d9a8f2e9a5307703304f2787700504a8f61b467cafe2e9c80e10e9e351242c40da681931aa1736100cb7196ee7802ed58efcdb7dd41c76a54f01c2d94b38880324dd9686a0e087aa8efc647adfb53b21cbe24ea0a625ee140d4762277ba9f4affdadb938622f6a288f3206ed7b1c76ca34e9a1bd000a2dd53a424b96558974dd1eb75991e051dcccbfa83e81a22bc40a3bbd472c7f623d0d60b2bac20c48e9aa52720def41c406505c0cd064784d4ad74339ecb926ae4736ac33d5bb5c0b0bf833f0eb4feed4409b5793b879ac4f9da3938101215fc84739c4a78e81adb26dcd612959e08f19fa68928b55d36a715156f08e084733ce8c686fef5281e8d9f97fd146aa81a5a45aee9a72c426c479f4820db4dfb9a3122447dd75a4c7f69679991247902e80793237fb42853bad956f94c7ae5f2edb4638fd7b510c6b962026e3567eb26543b35f9789aaa5002ae5f311cb1a5798d42ce1f1e9d7a4d77da7705bcced0fb37af384891a470d2a63b2886cafd7d0930148e77e1200c207264232e931cc4935afe50e61bc5f8d38c09d1d85ef1994803de4517fd0601e40dfbf91030bc49e8fb818036d03ea29bd0135d20b90ca8d39077286dc7f90851a79b598cd5d5dc711f95496864ba1418173c3f0eb74a8cc7e130896e7b99b03f705f4ff8b4fdeb26c31f59806cdcc4f09d3084044554d1b052c0691e9ba0c6c1b4bfef820ce8d3f4e81411cf9d89e47575a8247302fde9bd864c628585df5918f07b270d202a938c58d1c4f70591d6887ed0c4b07916c66c71019e86dfd08831d08593892a41db5499d17345dde2f39aacf439213882ae02dbef297dc7fbb94ccda52a80ad4d5ddccf2dc73997545b6c39b1f29a105e5e10cccc06d5ff79954632ac034cc395db74b5271e0f1184209474cb4155c88a8d409082df84124cb5ce45af10656ef70b8cd49a67c5ce923ad14983ac3ab530a710638c66434738e03299f25be424011d4e2dfa94a71b63270f9bc0db460325e402953f1517ab84eb0ab628451eb0a474d4bd8d755298d670e14699611b4ae1454a0485728536c6ee46aae4bdc678e7c2f59e23fed3a6c86f87874045d571135e438fc35bbe4a12504ac96ed4309d64ddc2d021b80eca9b2c676bf9de37164d61ed2ff9ba205fb23d285d59ad5d0632b8e0c45942c17430e20559ffa7d8d69a9a5a46ab4506d0ac0147fb1e53431788e8dbbb44e9df60394c6e47c624a28ed0c1da21f97549357b16d4c998457170f0e82628b4f1733696143322f196f7d3f978825f3a0b14478e1c94de9cd0d961e82478223a705421a3a5d47ed49a0e8a57025e6c765bbd8d2cb193acd09492e6042cde10aaa1b6024e226123908891b69ac0f24205934c1bcac72d011103744640462dac81c4ab7404de9170d629d1edd2a95801a962c4d7934f10c127abcd8c21c91217ecf666a99f4f8b9bc421d4a8832618a7a1b503557f67add8e4edb6f8155d174ae0848c8800d4e411d847ab6b084f4b49daffeb5f9c7af769a887245ea40b55bb2e3be2c643771686fc5f6ca27f4a5af6b7a091f7890d36c0ac8761e8bc047864dd09cc59abf21c638f29ee78088d75de7979b759d3800363d57fd7a01a939974a912d71d2834f8c9ca82b942d0fca5d849fa3e9cf23c12611a355fa22ff3397a44a9e86b1ca6d5281d6a67f4a7bd706a7939c17cfa753910e99b730ea713ae976c5701ecabcf3323d01c0a6fc1e85e60fc82fcd4d9b4779eb4464e9f33914289d3dc5521d1f331025d7f3d1ab5f88c2ee2ec72b274583d01fa3dadbf73d4fcbd3beb3e38fa7b962748761c07d104a4a62ac9fb2442fb691d83ada38153f65164de908146378ac870ed904ab8aa0f6f6608293dea14f68a10288766f70accaaa3d66ad4ec19ff266b4427a240bca7d17b27e74f9bbd272930f9ac40aa9ddf0ad088fe16fc3f41196c10d4563c53991a27453b4aea9d1eeb37d52fe06ac2328e6382daf5c60610638b0032159d5caec79adf47e861cb439d60c8231cf0ea05edc48382dc62a836c49c24b34a285cc5783613501101f630d5b545d679918955cec7aa40cafccf208044306029cbf03bbeb2c9b7dfe2aa3102a9dde11859c45a5daea4f2c6cfc506b9e63319cb90c4c90c58de303071196b7d363f9bc82e6c4c5882bc661b472fce6c836c3cb6952192a3fe74aa3f0e516aa24655c579e052b9b45dc74da903df117e411ce32b2c94cfcfb11249990ba9e4312fd84f8e114ba6c184ac91ea88b692d09c01401802ced0c6b2718d83c95ca4f38fd4f00c08f8b8e30f019c2799df2b953995fd32a314d6ae03395ddbc732c60c7d66f428297e5d802dec21a26d4e9881016d34ab9b28ed0012e3302c10a9902f2d70d0e6b4ff4ff8beaace306931d093f5cad0cd0a1657a5331484e71fda0bf5d5287b0424f25187a7b0514ecdf6699ec5f85147475e4993eadf79ffb8016c9e1ef16362041c063dd373590d14caf8fb191410e62c23bc4b6cb04f3105d4c70858b2a41cfa0128b3c5899822cb79d047b1687834dd053a242d1687d2854f8e9273f8ced39e82d10d9c0d10399ff4d7aaa75d8f08a38c108a80731d31b792108ac21e09396d5dcf1f9606bff035113687fcbc695ba3d19110d3e9fe9307600126e0e0000e166ce053633df90058e0337d882c84ce184cfc8b9b1c6385556258e1de4031c653609f20949136ec5d639b1da4d100bb5c48d4f42b17be11bd3559aa1146c16cd2e160d42354eb84c150c1839708c646b064e06e04d085e3261e23c9fd105ac363000bc60441416a299970701609e3b541b6e5045b107b3619c4f496a725b6112e0cd5c4d6ea1fd55f9585981df823acf7939d974c98097d9608abefff5d29fe1c88519b20c652780b632683313624f1f36ef5575cdf6dab54cf6afa1b1301d5c0f8c0bc2196142b04c115748494c20d2c702d84c80d51c4560d0aa5f7b1bb10deba8833a06540f547582209eb3643c356a914661d811667cf2aef048b29ddb45a09a8eb2e6e12968b9bcf40d8bae67d89df437d41a0d063d912dafd639fb7c393fbb0bbcaf1720c09821c9316f36a36d0f0a663bb546dd98e51dd4ed65cbf240064737662d252742b994fcef1bac11060e9b1c290b8570915c623ffd5506cbf02b1e8671323ee0d7b0764d906442a240c7e6bbc147dde0c391e2147dc9cc9a916006d545daa04109458fd3e39803a85ef6ecb8b98982f7d3cd52cb34b0aafea94c562ce26e04b53006a7ad2f0c1937eb7d065eef0fcad34ff6ae80dce7c0e72cf471d69565fe5eebe158a1e95d8d6160abc4782328e5e9ca8438aaf43843dd70690b44f02e5bc894cecdcf8c4dcaffd72dc6287285aba75e03adccd9ccfea2782c302f64be619a09af731410f72614b85407f9bc9f1ac5728f8a38c8fa30bc1f33bc05476c1d4355e235f9b198760f0174120c32e400ea5a9bae1044194e79f42bc9dc6282d2e8e503f4afff1d222d027d2e80386c9521a4737d9f19d5857926cd8f45f99b84e3d5ac10883664f674bfd5d3bed864f302e2e503f3b038ae0974c2707c42fef15a8ac56aaef21b2cbcfa5658b2aef3ba27c27df53e9742cefbc741f42f3c644318201bae7540184792af7e95e87f6b27a94631543330ffb8cc2a062a366a7ade77c166490ae2e0206073933d7ec2a9dbf4e15be9e5a5a9ccab5d2c6cccfdc984f89970ed49577a9669247fde50920b31aaea972d25a7849b893cf16a0ef41b7c8977a2aa18e7588bb5242327db9008d12f5a2a593e14616c0a634fe6731741c04ace10c3754acdc06faa11d3a61381fcfdca365e41eaa37f516550589e91fb1fa34a7f591fc42270d1f85d3e69f297d5a76795436ecf6e9a8968ec686b575e703e3ea8fe10164f7bf587dc96c6f50a4a71efc3c9e070b0e98ef6a8c512edc3071ebe8d16fd89781ae45e010cf24eee2958dc61fa5c30699af775235f552f849676978bfbee5c752242596185527380f1cd40e18c5bef5e599aa335ee04d6c77ddaec1fdc1217c5c127374b3b69c139fa19b83c7998edb182dda062e757e5be1a2b6cb6d36b44968cf1dca232a3658fd299216342724067762365cff57e14bc4d6470e5cc75ec7a09fbaa49be4dd76f3f9626dd92b3755a2d11eae9089a8733630ab481a9e3d461943bbf1bcb5dc750292c1b5ec0f6113bfc9baa8d258a6c03bd29536f62691fa4a200ca6e24767e9dd9b1ad5aca15a251c57f7d615d0ebc2a8943d256095a28835d16ad3d6e60f26411beadd4ce208558abdd529f4c7ef29ac36f8e0726e5050ab329d6b041ee84db56cd83b9b95191aeaeb2a3592edf681721d5c45f001295e8a89e9446e78ddbe23f5f15bfbfdf1df1bf63dbeeb431c906279563457126b2a7481c6b7ba0fb4f8d8e30ebeb88e82f2b350d47f1bd243cb4bbe416b786ef6d193922cd16c5548f5b3b2d5acb3245019201f383d3047974cbc8f3160fa7e107f0cd0177ea67a79ffffd0e25e42138a03e0924702976104cca260c79f78c17a53800b2ca0c6603f2841649a411ceb6adabd228ea97622a2c3ad195cf9e8c029d218e3f3731bd741c018807db42b59a44885b40772b0488cfb0f3712803bec5f1afee58f6237aa9904c4abfdd09d24c22889d924754512b6e435b4ec22753ac1fbcbc8692001bbf386268436ba12afc57710584f4420e4dbc04a2716c5adf49a14a1dbe201107b737c88a07b91b0ceabdcdd53676d5104c3203a71eb93ba402e26a8764bb4bf069d957c795c62751614a91dd2003fc8f5b0af86174049e2867867c0ac114f1e40ade705abcdfc8e4e1458719d1e199c2f184527313cb0da23ec01afa53a2006f37ccaa3d50acfd8e8416aa705d49e058b2b82da0d7bcb3e0420c824d8136306e6b0301e501f00987c21c1b9173ab946e931638c9d56a794cc0b3351da7a604ef8d4b9bc3e8c2a1b004cbd60f58f72b0163e43b16a9b00e6eef7b3c3fd2b886ace554ee41ed11e31fb3816d1ab1a07165855057baaf5aa13f38d629f88b5d0901eca002e4d80357814a437357e0468924dae70ac5293bc734790a18c7910041f99a8efd39f673198574bed10744bfbc0ea5471f8c1efffb4556db3fb618d91d041eeb057f1d436456205315eb75469365dcfe1fdc22009c15d8515306c4fa61db008c9ffd70aebe56504c6c38c8414f00ff9a0084ae8f928824609b259beda0bad48d33b408adc1a10a51175919fbcbbe057ce4041c7be2c0bcaf9d772ae05b48523a962cb06070210d94a0b2822f3fb030e21a08226702a2870a9cc637f8c2b26660cb699dec04f55ae4b8814a27825f01dd3e8134d082f07371799664842184afea0442f00927eb038ad4018e760c4f0b746e19d21ef70a8400d8fc89230e151207de4e0c7d5b32fee2f775c3063238dc205b52c208a860d296bbcc2d67d03eab7e588b25f271c87e37a6bea86e05026acee08d694655c62f6c043209a868c632602271383ea3e6952b08c0ce28ec010f2d62ea4de050004451e460cd4c4121cc09caf28ecffb41d74ff0bd3ee48ea25d71748504112df0bc35a1d6e9d9382c069e63c60d88289e837638596f57fa22da3e7ce906fb85202efef24356e0ad964f6b0be5b2452678af4e6fea45d28b4f8e138c77d1f4321ede96b3bd0f21223a1520d7b74b1ac60c4d1cdde05d054e47f8e01d2e44d0e8ce86ae1bcc030af4d554eafca9cb325fb2af676aa3d125db7b4c5571daf742bc7f80be3b8ca1474324332a4db35cbf6ccc7bfef3a1a7c51a38bd02f494230ff4baf7a6693f9aedb5bd1ea6f80d9ce8a17b35f51b079bff1e76291297ac81bd95aa39a708940fd4ec86f2afbb97b4dcb4baecb008dfeabf9ca52f1b4dda758fe388ac68f6589f2b6dad34554db2b74860ec5526bed67b95dbdf3e8ff87a953c324a24efa7870cf3d73025020270207bf7c4be9bc8a09e3130473def0563ea4353304c5d4c3383458a0100fddca114f74e378fa06145f5faec7c772e9fb9c1e78cef7cfff22e844b02ae7457b07691fa49cd106e03b1b42956ffb91929e3d0f00f2e2dd6ed5db63974e18ae3cd459d9268d4e20bc9edfa95124920783bb1d06c5d35c8191e6d455ff0a3a90d309c7c25c9af449d5ba745fa10609621a9817393973bf4b4ea75958faa39eb0715c35a2a3de6d6976a82a841ef45afdc1f24b06a6003e0b3a7da9e5c1b87f054aa09efbad578037dbdfdd7d2133abe925be59f9e7fd683be7362ca46e37923f00bc8768110ad85c387c76ecebd8875e97ec47421e6093631d09ba734fecceb8af3614fe20eb778d06cf6bf4ec1bc1f02dd391ce80fa96cd7877c0f501bdccfe8487ebc87788acc1445bd3204323f871f3fe0a28f130fdce254200b3f1a36d71a1b6147143f4a430517688f82f7f4e40fbe7bd29f864964b3017ffed1bf2f69766afc7aa372dea1c021aacb624dc06b12227e0cf1600802573ffa4e113e27fc16569bce7616da9da64681e4acf896d45eab051c55cb3de88c2cb8e9d6282e5dee3fd25c88caf75eaaed1ffe75a7682d4b3558aad1a1520d7dea7ec1c1b879e099c0e5a185628abdc28035ff34867a855011efba09fe7ec1e080aa2ffd5d9f194a16552e7c57a9605076d50b79975130283755b4f25df301368536e93dd48669b2aa2e80a990952cf42a7ac1366c946a5bdd02671a495eda21a8dd1bd54c67042028a4ffdd5ec2e3a2d943d0cc8cf2cda0d608090898c939a828457a5ca01774cb12a1963c265f8777caa62b39042c8176de6d63ca2903c0b6982f45375a44f85fd942077aaa7564584259b29ace640b53609d67e0306e757699d01b30ddc2cbb6323427db79cd07c9dd2fa0ec56820e00d1012a2953ebaa1e751ddd44e378c2ce97cc6281162e64a8be58d6c59ff8bb73e862a6be3165103c5c1a845825b93f1921ab432ab021d3e095307f8b836ae77ad62e8d090134a48f2b3edb08f891c2c8ac3ce18492006d6e84cb725f1164b5827fd6caace9b0150f1462d25939f0e075439df17b71f53bb5036e0ff77547a3ac59f3da65ebfb673b8662e88a2722511fbcca8e191896f5804562b1b7e383952858b71ade6407b42ff952f9d7cc09411e84b1f2ab9a3c634cfd258366146ef8a883208307b6091aa6de65c7f5607b780093bb429429694e34ee6fb8bf64b1ab45274a1bd1b5a11615b20a4b3c175cdec3a376f4c04bdd7213b9ebfd14192fa8cdcbd7a3ea0d67c706d650558684fbc0284ad0d5566a75b2b315ade236f36c82709b561e59ffa63f6808137d157fa0ae4754ad479947fd47510a4972bbc5523b73c47b8f505d24eb84e77856cade1702aef08b9d115b99f1246d271f1a2abcfc6c0af38b51a7779c500d74353352c98e825e3c710b8c5ec26940e8ed90de734da7abe28fd5b132874c107198c9decee9626a57083a30d822218f9b781c3395761b903be931b19132748c4372b1ccdc487d56629fd775baa481a7aed10005d5b1f63a023854295e80dbeee46a99df02216fb6a22142622d363292953843f5c5eaca138205bd5a4654c37bac8865ba2584b4b79ecbcf456b6132120425d71650c2324532b982a988e5361c95e114dbf0a8897a312296fbdbd10762e466e0c8f45623356ee903c45420ba12d390ab8780f76c14b08c64839bdf1f9caea41756170bc82022834ffff51cb1fda1e00942a8dce1ecabfb7bfb4785795e5c05f4f1d1a9a144e404683d85653b758200b984a151aa9339324d48b5c3dd1588bab30e335e9cc0bddc6f27f226ec17aa8e9b92cf9ce5999b46ed016f40124f8ae8aef2b85f93ba8cccefb7b1e7742967abc134e5774fff2477b997b570d74571037b490ccdd4d1110ff28401731b2ffb680d28f80a0606ad8aee68c7d3c60ae79a015d5a1aae5df92c74f45211a1fc8070411e78df07ed0e9c27f4e98f9833df9d906d7f2f150391639cef8a3994f0e968096395d9f00e1a2f4f358bbe7ed22d657ea8fd9da9c5edf21440480b606840ded9be0b0d5a4db98dd9d650381c2f1147fffda3d51e13c124e4dae72d5b59c9f4456148e026157f4516f95dc1261488878fabfe755a65c644f11b78018bbbc429fe1a79d861051fe5aa38fd7fec7e653e1728cf6ce847b8643c770853f19741fca90de277315a9a23ea1843d6c44aba2b46d67de4fa5ec1b037fe37d59586305b1835ec0414f4f1c867778154831f8311806b2b0d29923e96a77d19284fbf5518976084b19f54ffde40508fc8a4ea41f27d6817defe93125e9d3035ad687f8f460b5b9dd0616885a4eb540f02792f93b2cf547646315633c8427b9b80401b807da220a671fe0aa22da59bac88d90a7e8a513c9f809e80c84338faeb66b52ea111be9daea01168c49af7fd67691055f1ab15c384d0104715430f32ba7ce080f5bfa05509a95d0328738b1dfd9dc6771515c19a3f4db04f9a24911081e722198753a510133a3c05ce5e30ba983ea7585905940c8202938bc0c6835127306b394f6fcf1e5836b1d9d6eba112badd6f08db63c7a706932098e312f4f200695bad3751011e8b5dfe0053fae8ff2d5c354f62ddae0bfe2158813dfbd4029c2f5fceb959824e2c7ab18f513e7f293a023c162119761e2cc1520075b2f7be552faaa70c1bd6318f3bd6cb6fa64224671ed6acdcb2e996661373b44f065350d0a2e56fea12bc6cfe326ccc1b64513f117fa47e0ca8c583eb25bac76dd47801cafa753f195dd99408ea4dc582564b228fc505ad6cadb22bd22fb5ad412163039aaafebbf07b00c8798034aa81159635892c737653b082b9d2a723dcd4b5260f0d43c41df65bef89c507b2fc3764eaae98e0bf6000cb36f503c8a584d84b7d8c42eff3d05d8331a3126846b390f198d249139448f86f07e50dba15ca7ea3a86622f4408d191cf0dc4f854fd1b52c50c5128c41ee071e37f714eddd1450a64ff8fc6988dbc3b848c4a8684d02b12d87e0d0d3185344e053c89f974a4694c47d54d76cc352d74f86502ff871974f3bbfc27cfc3aa303161ee9a84880953b275a7a759cb7ec9155ede82be28c3f182dd9cdbdf315969d52b6eeefa3a27336c255951e4ebf8556a04b4322eb431a3afc0ccd49508551c24d43a7e1f3895aa0d8e9d72c15da0f8103dfa1f9fb1bedf88d1bc1d7d3f34bcda9f41e26d44b864cc0c2895525f362cd7257d11aae8680d33f56cc98adc548b47bba82430f7d01bd0a50ad9bc9d586eee2ef3620f95f3f37d7e8cac4ccdc17f7e91486dd5018ad3f2257dc475594eb5e8dce5c5408195d052b4d92fae966fafd851e458143ba19bc4e5d9cd3ccd3fc89894b62d96e0de96b8000b760de82d7733c8bfa25f4f5ae0373cf9f8eb2fea7903be204931727066744d0f933e2b24a5e48c262367379d49000b65c968560233a851a4208bf2f89a2d7e11a37478f588c26a1072a446c2d7f46e6700aed9cb9a358505b60d88963dced832ef99dd989eb43cb4a5a69b00b49e02e00a618c63349140041a62acdea08c18c214f9dce10243d69850e5eacefeeca4e4f87396b6aac652b748d72064faa23c275d668c018a685b44e12637cc7f90c2ad8f3236c70ade26fc4310d4435d7514bc708e1e54757325d7d8aca953c0b6a7b2eac1e91152480bd3d3943dd95f636483302483fa22c4e691279907af25fcc833e1688731da7a85a8bcf2c7090a236b755cbb7085f446214e6b36c02d1b9a228769ec69b85f69f8a525e90a7f9b94aeb73d7834b2cf0f84fcc801078361d228138ac043d59e54c240c269d16584950f60a08ff46ccbb1b128cce20305d8a334d2a4a65b3e6d6e646a90c0d4c4abd7f5516f0c0f5fbd7caf1435063799d972904554fff0f5c7aa9ea8127fae8be4b3c112bd8a34ffb2547310d00466f706e10f1cc4b050260d320d1bda3f8124149a0a96243261a6cf78196aab0f7b08b536c16eb65ae7c7f97ada7c938aedd41a0d52b06224b348f505606e53179a1dd9bc540fecff4032f7d7102ec0fe345bcd7229611654587ed28f09bbb5dc032aa1526347af6eae3d9aeac9835213132ea3e3e71b86100aab66b817c7cf7b921560b354514aab3ebed138d8180fc004557a97be9015ac00ed49c3437f9019daef2e95cdc99133519b845ddaec5a02cc0d6e994ad5b33d50d393f0fe505808140852e8a6af9ebfd1db2d8f074ad29180305f87083f9e3fb77ededa663ae9531ce44074bea3e3bdf23d635a0102a5df6415caf4e1d780e3f2f2c0c1ff3494a1102458aa9c58979b554c3a71471f4d0d80c408468633d02c7f645716aa5d1959f1aaa51fbd377584f0daa34f2c6415bae4c2ed42a748b78f5ebc08a905a8a6ea5d1d7d791f236dd456cf52c8d30bd136435afa94df4689094ba1663622d6f737c1ac5202b315143175a7b26ba1dcdbb276246d10659255848206d10a3d87664fb557821a1515b368efe5b2263500d8895464677d7864e4d51b0d3a26821d214f94fb027aad69b1b35159600de727f7846f7a2ff3993d9386b56fa38a8cda76d909d45686216d8535e544bb8d3e6e9597d12ea42db5481b8b925e322359f1717dd83a2d6cebe47a94ad52e3ad9d322c69487e8b444020eb8f3a96e1daddef17b5ae478711141b3120c994371518ac72f9dd302e1395c5ee75b641eb05ec81b84e74d0829c202616050fe320d6f31d53b0fc9969912cc9c205a17fbb0c502e1245f91bfa91d0b0a1b0b042a2049240c6f0356f88fdf56efd83f4b32155d66943f92a8f5d1383a159f3ee1482232f1da96e83c77deec7e7947fa771f9596029c118dde3f9f76b3fe12ff52c5adca917cd59a9b95577e3e132750644803baa609ed0cc25122a20c14531d5cae26ff8c9c0b2a0772401563d5218725924e8b27a081814dc80bbc88a414d2546cd768fc937e857224e3b7d00fb22d82c788bd8998b8f79877c225ee130bcd6fd231fe95bdb48c883332a4284ae83483b2f83299de1780da1abcf41c8884d21667371b046eb4e98109b23eaf2715fbc2d206ff444fa664609340c28131627d622484d4f2824d9eb0534f63706043f1f03f66969121c524c269ee5c3f1970a86f61cec1e69d561c6add3182a252d333924c06ed692e22988e9adc9253e19f8ccc2ac2416c1f0fc947435083d20bb44f3572388405a1335403245a03249bf73b5419100278f563b92674a81462ee0251e942f60671bcf733733064733606070cd9b69ce6f35b785c480c341841251c33e2379a75c6e4fda1d91964c53fb1a1b68bddccee1f266e2c5394385d9c4baf082a281d304863410c3ef16cdcd45f20a6479df3f1bac6024e42a10316e1e58f14fdf56c274fb99fc20f0c1129d9a8670534cb271cadfc0fef4a7ee4eb1fe46e50ff4730050d689249336a46572156d09922a23b5fc30d2f1469d9dd9d33080455665b330d9a9f71bc7a7eb21bb191aa3f3eefd0537754dcde2d0ec4a33fdc75cbefedac2190d502db251bdcdc9b73dcbf9e76a54fe4cbfb707691c92bb2681fd5ef0c22899335eff3d41b0c0463f25dbd0faadfc31258de222b7d70570bc73bf828aee036cb2ac783ad29076daeabfe694ae8812aaa522db506ce3da6debb5b78ddcf357cb72ac488b47b517edb7acc80f8cce616d4c4c25b125e6a21ee741d2ade25fe7837e475a32669b30400c560c3f0cc412a78052f5f82d1d1eb1bdf5eda234ab55f26c7ec586f93c95154039e4eb248f0d8e3b72e1cf5ba208d01a4305c929d8b6b420a9a64542a39442cbb2990856e9e8b61669bf196eb528f9cb8ceb1a7d17021dd34eb15b9541e290609c44e436b99faa512b6cf606f6225e6ffab94db941818f03106f200d426ded1512a1d6b8d814337e8c636ed341e813e5507de6fb8e7508c9e2f8290de405734b3af784298853d9b07cbf098f32f153c70db2a14464c4854aadc45641c744f322d0a4d02bf608d7469fc88e0585acd1b3691f2887cc02840f8217016e9cc87bc205c113a689cbc641b8ce797c32153de0ec7ad75a0e89f251fbd415fcebf7e88a39166ed834e141c34dc861e06c3447484f8f51fbe67cf46a80a6e73a4301504aa7872ab32139d9536a9c1f8e0c584121b2e200f30ea6739813f008904035976f26bbad8af4e090258cdc7aefdf712b207ad71de30c07d127bdc8dd9cf713ef009834628a1c57d510209122f0e2f41199e01a6543e58d6416e8c49f768ffe8223ec834de81bfab71db97c01922025bcb7e1cd3fea60e752d25b4879cbbbe5a6dfa00e32adbc043da5c62a6c1e810f7072d7b42877a81e354b6b4bf13e65ecc804e507b41eacfce632feab55774216b9bdd40445eaf151c457a924e5449daa02640dcff2699c13bf9a96901c0c84c82e1224976475619999179a253f823a06ca6b8fe11f9445d4c995a7e82bede49db3422cbe2bf9c7e81154f07767b193a41908f079432d409f8ccfc964a49d9fda188de07244aa9627807332a6b99bce9e95f700553fbabb053a09f11ab3c1d073c6ae4f4b2da197e6a1754d0381bd9a5d4d4fe45b3d555d4012089aeb442fcf05b978c65f8aeddc6d82a879cc2cd24a7d5aa45742f635be976874fcfb62aa4ede08b7a27b75a3b1768bca69e8fcb1683fb083e069a48e369840199025b737408d365c45645e40f6bf8a3f39d09c758743ba5dc4916431758d7b468d451686402d9a81a6a602c18911732069ab8b2f22d0988bf0ba9a33f728cf3112abff76aea06e2ec4b744bed12d1e50b099583c89aa5941c38c4203a54ff10903cec4f18aa84884402f82cdb473283f91bc5e60f7bd319db11ffc74c4bd3e31b6149be4d12309420e967814f4fb3f5daab019a58ecf89244bbdc20b131c2468e7dfa2a33f7436eff65a8ca751bd3dad736ff04ad0e9fd8ca6dcce3d46c9ac185d7cf8cfb397c4888f043f2cc48260a190bf1e5a6d75ecf5b3f5a31e5b67a34fde1297a8ea7a3a9644d79b98ba856e2d9a9f3e9f62a1f144b022f995242a243cea676e0c1e20553c7af32bc847d0a4376811d688a8effd19f15deef6473dad972884460fab3a1454f4bc82305e80261fb92752baf4971dd19b49de6082e32602183902ad2975d63885a2a40010114a8c999fcc934724e7ec68117872c18c60e60d9e3a15911537a47b5520ea6a1915d0f2b73583ec805bbff55a38422c85d2c1f70951cb793b76412a8390f1478c98e24ee1861bbe9a48907143972a0a85f48e6ce0bb729f4647429869ff33003f649638e700988613419a95e5f48f4e553e8b811e79001c0bbc73c2d291beef22c48a3ba271c604d55a5874fc1b98ebc42f029c04f3d6b6a7223bd2a50bcbb470c6c7541f50c69de850413ba77a7fcd6cf539371277d38b4ef6374aac44e46658e3aca566394ff7e2733e6b6656cb025b71bd694d236a0516e7b3dc12dcde00696df3eca092fbbe3123b9747c93eb179d925bf536ae37c009f6e021281fb2303a168d2115686ee366004201ed70e2405af6d00ee2b0c1065118684297e24119130ae6df79671a71e5e101264114c407e157f3a7e30bfe2a89b4ae619ca3e10de20c843316111df289d508400d1d550fcaf2501da33bb43adce25c7154c4a843c582c29b0dbb9a863c35a94651acb822edd59b1f8bd09ed5778880374cf56999150e204ad46f7f4ee903d6f548dbe00177afbaacb8255664bfcf4c9d12148bbe14fd65070bfde24b1f1434facc9fae1593faccf316e5d3f75693fe51620a249db1f6e191ef0eef924980e94fecbc6a32f9f66f3cf510d66d8b0e531876799eda5d40eca97e9858e2637d258805de1e43b69f0ada8d46a29eccf0da07e517624705d085cc35da3a92d6d489d04af97e2d748793a19fead36dc40124763b8469520b36e9c6d8fd9186d24e6acc52bb791c25e8be971989ba84408437b0a2f0577fd27ecfdb38af4e67f9144e27e6f9aff7f9e282f0b5188014976934a655a1e2307b2edc3d81e74a8fcfe617279e6bb0a3e8526856270efbf2a781b0dd96e3a1d471bc4519c60109e5d216250dbf9905e7537eaf9acf8f71afa86535e08e511179f3ba0024e2305921d855d9295dd3df55ea384f678a55da3779a00d31d262434f2f4b682a33d0c319472dbf56e9abebfedcffb06233e689f9d0f014f0e885bb767a2b8536fb5d85a92b924e01c2cfca852212bd0bddb1ae3528b3c4175a02c590e484bbc8467e500cf6051a0bcbb315f0902cca3f4942a33e2a026a522234fbe3ca064d539d617da5ff1f1a59f15ea18a386159ea1d4668c93d58f45a4185b57ac191adea4ccd4f4378516276c84ecc59395461e0b740e6fb4325384a04a50f48fcd15dd1f69e0244851dba66db4120e14a3b3089281b13433f57abe60137e0c9fc6c52043f9bd76b90086aeb4dbe7b850b750b6bb53ba8e1e71a052b7f08da5b816b91d9122a9abbf2da561d5f3e6209f26915f60c1190af367478b2fe33735775bace397878f302c3742ace2dcee927901cb09d1123cb7372f419ba7970809ea25c6979607d37d402e64485843c47cbf5c952185f33be943d0701a9d7a0a1b3a665bf9570d3d4c7d372f5bc9443714dec33ecc847a1da94dd54bfb9ca4be2c1f195122d0cc042de68739e9fcfe2d045770800829006da2e1041b09f8fb11f577f10297a6bf55584f48dc777ce34a415d1d01f0e264499a0880634f5b1aee639208017038c4765b6a0da523c217d4a6b3bc59fb99818dc5d73f576583fe6fd6300af76371115c07fe007808732198eab5e66190cfcebb22340e64d1bcb555bb2ad0305a2e6f97535d1b7bc2149895044d2332714a09289a698849876ef651b181bd3a42b7b20c7a4c72fb65a8b933458822c85b141234d1f591489a4a7ab9e3c0b4312257ac29eec498ced58894ed8b4e11fabfd5339dfd9e952de5e7de42234654438f3a2830ae8a3029db266f50420b654a228e1c4bbf38fe863bc6d98402760eda3f949441387086947b15b05aecd7a24c33bf2d24e57e5db58bf89c1820ba54cd73919c28fa28246ce977178f818903f31c585e8490f09c483fcb962dd9c4c7203472f7664b1e6892e3b12b7ee06cc9bb18e0b53ad2ece96bbb9eab356cb3dfcf8ac35aed3b0dad5ade14a34c80c53218ccffb769bae236dd8d1199c7ec7511a493615f3e6b4d68f9aea112b5f97e437714dc555f03b084070c744a9b8316e3f2c1d5615de61a7f219559cc7fe17d58206ed80ea9b57340be35960c2c50b25d6ac1c35f579fb2b506c91a8acf130b7db23626b4c2a3ef959e7d691d5c9eba7de5ba65bf4f0cac19165365ea57e921e6ac30fb1fa381667f2d526a38667cdc94155b1f994fdc6662b2a4f3e11b1a4a4e256e819e3afa069824df70a003e161ea426e868b7f49fff155ef02531b13d39907bc000dbc72806374a8ee4e6838baeb39f4aeab34645e838cbef0189c5f97c6f68305b59f2dc7f4e126141ee36b9eee6da581df7a7f840b92b9c171c6e11747f8f2ec31799debd884c75856865c5ef7f75cfd9eb31ec7af0df66a1857eaea47f80973c025c4e456a0845bb004e87370bd76dc530b8de7dd85c862bccd7e730aa94ee87f5c825e93faf2b1d9a4e1dda35079a6b6a763748735cfa19057d94b664c46438d3c5899520d9c6d3e17734840724376ec7809e4270fe686803ea53b143789f9f21fe6aada26aaa3e136c23167a7027a04a9daafc6e7b62a1b07d2bc08d12781e5f83f6cda491f5aa31c23dba13f6273a97b6f780955dcc43d8dc6d23279c337d3562bd250d61b168969b1ecd1f2f90add949a96345c541bcdf1ed967a3b381dcbdfbc336d8dd35f14aed206300f5eb50e65be2a77c1c38b791e6e0839452ba0805231b5df19166df0b99e2e5f8ed5857429e433864ddb549f1725e9110ab1e0737a62ab476d153bf3174d3d2505f1567d2b5b0ea20dfa332893dec5881655ebe236b199de5ab4f412f92d192b0e352a4c211b0f23c2a17a8300c947fb2404fd65db5fd68173b43c99077bfe45bed7c7736b3943017a314369fe560322ec9210c1ee710fb3f8bc597b6819a64858b49f503174eb37065ee96a684ab58dcb7ce4562c80d2c20c78ec256431fa245c30981911ffeb78d659312172e73d17f4397c0a83167f1ee526cd5d12d2d48ffde9d94803956597a386eac8d66c4389797f7f93c53f5d9f4c7c71dc2693aba15d5b95d0ad4816e4304f1c6439b59cadca720fa365ca897bd73a4e133653d93332a110ca1d68b58dc98d26561b362c11dbcec605de270e6889f95b4efa159b8b1edf8982c2661eb11c77a2d1a0380a85fe761d053c8a894028374fe60094cd18a869d87f9903b81824596ef0f920871bb9e1a60c7ee860beb741a1d36b189dadc644bd0a0f95cde7c45e51023580f81e4245cbfc28b23968da0937696ba007e8cf7e404173e6882d027af461ce72092301dc90e71123dc5c70d24299de0cf7b26d3b38a36c6adf0a165f0653bea90f8ceb27e02a43cd541ca43371cf6308bc1b9d8bcc0b2f7889aabeaf138a610a6833a8245fdffdd656418d0327f5c322a3425b6a5c2e1fe1a8189e90e3f02ebf9a77f348b03a6235eef5443e8942df8d3f63ea33afc37d2784167613abea4fb1465e8aaab51f71c51d35cd726a5d5117784c314695aebccf967bd185747154ce9557b457969f490deca1a805117365c47f75a3e4cab87bd1b00064ce7c2d975f9768a545bf77691152c4aa4bb6bfee0eddd98e790d7ba913738e99726cf2f09b8495e3531cded6d96d5119f5da971579f4dda5102c27e563d174d0f12a098139f7c469a135f4b790f350c4036fb67e6224cdcccfb2e9ff1a41f939cc68dc4c0b24070c92a7b8c42a2ab216107c94e621a2c74d3059e3409403ca2c737acbb0d29fefc69c9ff99e06a12ad9888928e4a2a353855d4e171f0b3816973812edbc6f7cceb6ec2a63628dcb2409d5fc385c40e9e01dd1ba6b10935222e416665957baa754260ded401f2c0e11b69cd174799a2c5090358413fdc14ddc0bac60219001fa97322f5b1bbad5a7e1caa8220fec2b222bcf1aefd143e52ed8b353152322891fd153c8d8b6bc8d7e45e49dc89f0df7de23110fcd8b99b28f4edd1497b5d679c769f0fa8bd3b8cb92a035d6788d1dc8ff7f72454812fa6219192a4818b7420e2361e0caf4493aa2bc1b751847d818a08e3f59b29940cbba99a4e6fce60252c70bed88724dee30da6ebd9c4795076d17fc94e8f61a62d300867ef4a76cfe433c1087e66a4a1bbb81d1ad209b579c442db285c11a1e89c66604012f0f6e7e142b5942a5b40909b38805d1b3c762327012a89444cfd78000201753625c86f108f5d180218827ccc1e6bdd402fa96e80a32bfb2b720b85ddb168384d38b36bdb31044fc9119dda0482bf2a575878d8989e3f4a1dc93b693c754cc5b27e065bdca5deb7c254f83b21fcbe680f0e817bd9a800c7c150603f59acbf7aa5c76a3c7f0053cc7889868dc4aa5d54fa216067671fefc11fa04511ab513d65f2db00605248b974250806022f163d1e3bf385a80ed5d2c60a8f2814662a1300adf85ca39dfbba1771ab2b3e534fee7e51129b90cf92fce697219cb7e20a06371a09d1f64e21f54c2537a3c5a0f3ed5398429db3d1591f8c7a66a4a2502bf4d0702a24db41e9df2deeda38fc0101d5eb3654f75c474afab54e8296b60087598967c1bb8a0db4056163b4403ce6dfb68e721f6db933e606d9effba251e7d056513117031906aa79307857c029ccd87c719b32a80afe7c8e2ecf5e2f60a729f28e230cd75a377a641bacbc2221f397c9fb62b9632806e7e33db7a5e5e3ef7d96ba2b3980dca335ff4a50cf36b51c8c90ab8208ae8359d18a71e7bb190ea7fb40594252ffc1c2b1d6b86840725eeaef5da16ec0cc337888ec628e9bf8f1f5c5b381bf2a267c11bf6c634f1be8115b8d6be52ca1618e5906942fbb6a682e4171d849af3c973d414a26ea3d87c23999bb80619bed6c4d5a2906b80fbe8a41af1127b2034ca403e109b7453c73ab467fc7e472f2f6aed293c96f3f1b32fd713b298e060781d8b564541be9c5c79d1475835006174b448490bd65fd0fb1ae60d0ae4633d5cd17a4d0d44d5492394478b9bc9c9650cf4698cf795f64fd40f035d1b72d30762f245c1c8be85f76981758001a1b160a56a812d5df257a297b7e12d084440a8f111bdfbd081a1e320a2e3f0a9f099d032304f77f58d13991b8c6b8ff822977b20c66910606412b7c5d5e8978b2d5b75f7295524db7679b4331c5827f0f81692cb710e20c7f3e20e754b34118defe53fbea9c3a0a65134387bf3cc9810a6ac7b3b3b15f1c8e88e1d068cbcba076324d732c9f6ecfdedf19214f41f85a78039f63da2447e1a9e35a0d8cbd2819cb6ac6b46ad11fe87aeb2641e9814386dde74ee5667180c26371843b569c1103c13b119397a269318f1502761a5ae0c3fc89299a914d3faae9fc95c38e1a5b7ce9083a4fa5bffb963b2770b900e90d587096a9d33981b179291c9af15036b1f9d385c6c85980cf9580c7b898534d88c16ecf5ea245ca0587959c2fc791376a38abb575a8b9b05cc155ce4572d474d35e323942ed289403210f45396309878a57b54065877f3aaa59758924373b47ee929b0830ab75c6fd1fd855e3a4321afbce4b84d15e2f71f01ec0c1610b53b7b3460988a6d9534da8c385188d57c88973db4d5eb8e2007b00f53ca35ecf0ad19cf7bdad3b2aa68cd03fe58737a6cfd2c8b370a70e9d4b433953ad1040def61a9caf1367f02698c694b840df28e078a0a4f581886121f860fbef28995036edc9e4b9cb8ea3f83811f9c8ec5a28252cb80d7e3cced9c82389e410c36002ecf25842cc523a58f7d046c2092c63006d8205537e3aeda22ecf2f4c479226cadd47aadd4985ebcc11e1129845d53fa3023800b358ce38257275ab3c8095ad61f6aefd20ce441560804247c8a7d73591cb1412b22952a29bd18485a0058b9742f6feccfb86de074f1d19f498a16b65ed6cba8210787ca0b3a9201cf8de5541bd599a62d519a33c32c0f7fbf329578322c6ecbde3b92d1be8241e2146d691d29e970832189746f968ac3bd79ce43a5c5558b3a42a743f3127a329c90389fcfc22d60777210f3ab6be73ef8cdfc5ee4b8a90f026b4d2b462446ab5dcf9aa05e37f1e24b47aecb77b63541228f78115283a615d40c8ac4a9d51dcfb567945cb12bd9ce47d1a0e05781d9d351ce863065d12f7096ba11d5133a0f119f930d5f15ed14fbc152a24f5e9b10ec10a1165e3b747c7ff5de5c21603d24676c3f9ffed4048ebd002e5f5427f5e40912f5799587348ca28512a2e4ab012dc66d7ab9221a55a8aa12c977ede77e96f7c0a01aa21c0c9c937e13d8e4466adccc35b4525ff01ac17669a98e31cecf9df6bc825b68b86a27c922104f28c0cbf1a6fd312577ba7f27aa1e1ed80621cfae46ec385706a295bde402cdcb8c0c6247b6f81b1d4cf222908218cf8fa275b4ce29dce03e956052bd1b3a4ae1061095015079d2fbb5c8cbc3be96041e4b60a5a019c8d71a2402301e86d0f5c67190bbd013ea90d94dc4e297211be06015864ba75001be8cfbe78bdc2f73a12279798fd56f9ee918e2f3e65b8cf84208160cd3c113446f2a6894c5136dc033353e9a87b38fd160392cf724a5b591ce8818824415d6bc50b40643d2f61541ecdff9e62e46085683614d9849c3aa6b02ead05f8b7c001b409d1da3ca7954f1acb8281bf3f53f62e7e2ea324247941a97063a2588578ede81f46ce3f59e7e44ce42ee6dad846c8216d000ba5c0a711828bad9fd60ed399fd703c8e9f2f1cf7380807fa462187080b71658cd17c653b152c22cc1bda870e98d68996194894d0867fc7f57a9abcd496cd36b19fe0dd1c54300a55d5451d5b67ea969735de4fd6fc3f06ad07a307ece0074b0d80bcbfafb30a8656a9965ab4c2d8f911430b11472ee5ef1b3a2c053e48db3e82f1d4139aff10d56d5a8d46de6f3e960a806d7202375a272ee99114935c15bbf286a875a28eff62b3f3bec83b765d03e5c8d01eaf1853a380226b842dd18c453ee0720c9a70b1b53a681200b232bea0d2d4db5a1ffe3c65ab15200c8d6bd963422bca72f4541397575b8d61c291b2e4ea95c3784e75c663ecbc073720efd1e8556112c3a9ab2871c408d1df32ed653cd58411034800b897666ab7dc16846defaa9026eae1811785025a33f9d3b33b6b0181335194a82b99a6bc4dfbe0fa3bc6788df9cf8e9c7d1f8098d14c5f365ce355561bb4e0820aa3cd2d786cc44688d226a715456ddbf4e483a555b1683af231b32662d7a1061ad6ef1bd4a8e9024522d9534eb88ae5863afc33461eb6f0696a74762e4b8fb742536ea0a1e01d1ebdf94165c2547d8342d67bd8ca87191f7f071ea412153557fbf0580a723b420121e101b24cedfe97b6d897b9be8de8779c482538a8f4cb047b5fcca13aff715880e1211e7119d2457d3484440eaf0830b6f845363f5e63fe4abd50b301bd0bf746eec90ea03a36ae540b6cc5c125732c9b7daa7878a72ffb84113aa157cae408c9e9928600db33d91108e465e146488c95636715048b86a9155a5dc4328a990c6cadd4d01bd30427de3b4deaf3ae5f659689b5240209b61523210d0576b4362804a554ba98c9f31b39bdc19e56e8cf1611006df99f7edc9ca8e94c54c40e235710a42bc200249d377c3b183f596c60fc03ddbdae6bb4af1480eae36c59c0d11b3d6e5a8e5ae7506ccbb7f884eb6b1e72d84f9695e96d5b481a5d9a4991ea5976db9a926527121e96ad9afd23210350286a5b32cecb68870ff1b9d56a2acf6d8a71c610234ffae297d84125b182a4809f7c6ca9b00821d46ec0a2d6a59a1a805b9aaf57b72544f40d54cfaf36ab7d55d295bef11044015d675abcb831a4046d00d29e109405c01527528f0f5bc821b0722875e8664976d7deaee2ba2b58d6b70f8320895593a5673944e3fbd7e4040b5811df184e069928ff23e309d149381e5b25440902429a8ce5ea394eec643549fc0512a8477ba48df6d564af9ada1fea8155507ce1e45d30be966853cc2b0806c5a76ec92d7e710ba1c0b9f855a59cc094699788e2a747a0971d2ed9a321fc63a22ff5dd6c29bbe21901344665628c55001f998806dab99d9efa692bdd340de9e19c814f4577ae7745bf441344d92320a4dcf8494061470d25e1e26612336677018e4e439c870022214ab11d9e71a9b1491ac1d0b376f0e30218c7e48d497af6c62cf4e7722e56030c11d26b1ca51434e4fe8003cba95407ce1c6438a6b2c99cd33bfde34b2682b3d4c2f8e60dd11e6881b8414182ec2bc9bbd06e579158f0ffcafef3610ecf0d59d4ada78161b2d5fc99548d3a236436b2e38d47f21fe34dd9d240ac85a9ec54d1c4890ced77124d27ae3555cf47cd5dfb1f0a4b83984cfe51d53a56f2a6210620ac6f9c9923950f70bc4c5738d2e4e9767a37a91714d969ad66b7bcf52c9012f15dc602ca95798c59159cba69cd29b6a4232cabeafc8c87be24dd1f02842204cc1332b8afa3f9e09e6cd160704e1ca7d1332b890a2151a94387b675564c52bf662bd6119952be8150c8c4a245ccb22e6db18a05fa121b2b577a94c71b08d7d021a5e64d54438cefc5d2ac4867c10868d68d6d0a4cb48a43084a06f19fdcde7472a84d23abe8bcdd37980eeba6b9ec6a356949159187d4b2b9d8a874312b3b9983514787b102add531e6a640e8b8fd240484b80f3d1ccc95b649b6c2750f4a6397d33c59367685e5cd2530310aa9bbec18ecab748f7545c5ed8c746b34b0102780954347e5650c6c1bfc23e66d7d9f438c32d4903e8326b927d26088b1317c2e3c1aa6db9f1673bab5fd7fbc9d45425b9e413f24b886105edbd1d23ae511b45384614a5ee7710e7b784205456d861425a6382390a0f73fe10770c4f3cf23f8206b410e9639784e632d79fe439db98e46193784ff8ba338d434ced3382bbd790a85ea122dbb1dfc9d474a566b455f2be3fd6d2f9bc6d3d6432eb437accbb4db6dd83271f07828e0857b9fdc64eb88cdf2faccb7dc00be0a17353eb1b0bb6c0d7695e6037abdfca41b88004fd3ad06f7f07ee9c8a1b602c7d306d4e9cb8f75fbbc24c9da340f406ef9b1647524a88c356a14e78072535bcabca2006086b852c903032639bc6a285982625f0b36d3440798101c8d4c531be35db8e9311c43509c378999a52c6415d666c69ca58f6ad912ccf5d91a80a9dbd3c92988222965ae047f8982a412fa9f5aab07407419dd5d0d4fe961da0b013b9fd9f08eea991e4fca2edcca85d4f43bbdf6c3fdbd478cdc2e7f9f9f177569d536f94b7f36dcae2b9a7e59164259bb533f59cc22f97604e83cc41da5c4ec16e15cc6940177352f08fb5345e4da6ac2e2f695bbe694587aed814e117d3737154e07f59bbae3080afc2ff6beac6988862d3700b57b5bce295d8d46816cda8808c4855c1da25c99a205d0a9cc344b69165b1fa50c2d32803cffb399f0b0e94806dedc32864bf1b10178c014815f553a6be84a2953e57de36fbdd9989fec704ec9c0895c4472f90b501d35e95092af866019481205e60e4cddfb82880e5448b9afe51e9d2dd4099be398fb7fda61ddae8610542c683fb0d3750e97e37c055e629b47e4d4e1f268a5700f133d81aec8acd014280bada48a6371d198b3a5920a1b5b2b88ebdb9d4e0f1b972045c21d34e1b542a1923c0a2102d3321ecafee4494e0960c4d798602ae3af5ed1c681fb8e85d4a26fb8119acf774214e61517eabc537fbec3ddf5613264e5ea3f8413ba8d24f8482b94c0109c662e50f42da86da41a2ac34e38bb7991bd6b0038180802e4f9b709feab5b9c444d09473d458e5ac30a6f9bb0cc4b00c24d32f79910ab8e9b36826b73102aa83a331f7d1c0ca8433602ebf935c217dfd29aca8f8e9ef1e3e9f5acf9bc7fa003cb1fe4548b54ace0a4b619b2c3f207c7428ab1803ca56613098d859c885a93dbb01d7a222607e27e71a15a34a7d1d1efe0655b818f044ddd4ec4c91ab49ab18d58a7970021f0c92e99c3a95705a797a99a8b536baa6f6d31d2a74c4c3db08217a00bf8026555a6f869285d79d8a7f8db9a67db608dc994a3d21e07bb7b333c2f423a5b31471a39506bc823b6310441d9f6448c9c7c8aae1bc391fff94419da3e5a6b4c5edd87df9c3fa25b8d4a7da86c44c2406099bbe6e80789c942450d325a5c147f1961ca74b7e688be7ff322616cf4706a9149c9fa114dc80d704df620c8a9b4ce483085fdf63d3154c533b645729c3783c6f4a95537a230c492068e61091298b3cc70e90431e26b516ac8ae634910e1c17f9ee734165f88f366a0552890a246f7abefbbe6438d93b8896be71db18e964ae0f49e9adfbc1e1b987f212148ee5e2ea746909d8f857a17580237b41957ddb2c0036b0db4741cebe30e42b5983f4ffb452ffa9797b00d55fd33f52b3100a274c909c30c60c0907a2ae864b145ca41031eed91fe779141baf7d871b6bf351d82c415f00c7c848f937b98eef20768782670cc962bc4f51bea2c23e3539c6377464acd12eeb6ebbc623713a3b4a22cdaf4c312dc9b3c90d4c1603d54f738e6f889b445d8001709ab1db64c24f057a4350e9de7772d559638ce3456a3852f8bd5f94d6cef422c1f2c40c6892cddff12edbd983d4475fa59bae4dc9b9b1ab9d6b6c3fed4046559700c194b81bd7ddfbb16e7ca2b620a2541c1a7e946a6b6c86036862804e4596db4e4fa6619def02764163512278680fbf40bd6f44485f771cac100d379773d58d2a4d2f21f2eafbd306f26ad52ef2a8abc0ba39fbb9b985f4d9415ce937181466a0a24070956e562bb9d00549fff52aeaa4f4c6cab434d017588775cd0737b551d7b7979ee32c12361a7512a60083907a5e6b97f061a2920a210a89b35750fcbe355fd58d7211e986633101849b9ebf53912cab3d50a61069fd3f58aa08d9f9c5ed69e3ba31563316d25d7ee6c62b7b629cd0fcac976e9bb0dcef4f69eda5992f81ecc1d90af0b5189bb99072377c2239560b8a9df3ac9f9b36f86fd2dc9e7c7b2f2ba82b671d29f11f15777b30544fadb41b1620f2787975a02f0e64d208d2bd3f9e54a00dcff19ba6fd77963227c654c7e358042a7ab72c24c1db6842ab300091f3cd7b37611b94d732b2bca6703fa096d23540b5b1a80f80cf3a2af3b8530e85e388093464a98c3e30fccd48a5fec468d683dea74504f645446f0ce029575e879d855acc68cba782af074fd4ea41169c71cc488d861d31ff20918374900346222eb083da3251ba1ae7cac6d26ca1fdb3d0ad3595f4acb0679855f6535f8d0d954e898eaed73f65b62367857daa729c5d315f223cc7502bc90fd1ef70f73df55cdc04875b5f8e387f2c1efa73f090e4320f31d8fb62833c5a052b2405ae5886314b98d6330309c621862634a6879816a73d67302b6a1a57f10c2f3e627b5f1790dc3f8b9f4025aeb03fdc30744dfdc8c979985b3a6096abd32cde191087125e03b9878f431e0be8e353a1804bbeebaa6723c9832fd53ae5ae10cecf0356a1c8d0ab70e720a280e69bbdfae01b3c7cea347ae04cafbac9e9b3cd707c8c099db3526c06803063b427eb46e4116df13d33c5e5b7df524dd1cc5bba20036f02bae5968a1d00223444030a4c06795c308c4a4e07ebde33e77b6cb6e745b30d681b967e9329debee0e531201ed67bba102646712e7e606ab87e4441a758046cea5487196e5741601cfe3abe995ee48e7f0323a400aabed1d0da27f5fa5fe013c84b437bef2de5de524a99648107790758079c16a78b2a85b0f3c8e5cf38ca2f7f06da5b083b77dbb87da305b88dcbc8a8ac0a5917f7e5df3e201db41657dbebffbe22a128d15db509dd44af71dfe2083df76a5b6de30664e7f56bedb5cd3dfe4dd4e2aa4389a8d7dfbdd6bf3312d14f645656b9fdff1117f7ab1cb0a4b24e54517295d359c1c5aa11f7b54bfdc27dad155685dcc57d0ed8c9ca70dffda4a437e6ef5125395ddf5330ff97c7215403221ceead444037f7f641b8f17762a9f735da9bce155815a2cfbd0c532028a80c51c392132f5078808b65220e15158cac0eb54aeb1b870c93710a0e51462459776b3dccca00be6bb50fde71eeaa268a289568ca2043e9044c7630aa72c3ab7a400a6a0b0bb3872311966a40410a3166ec127084c8709ac4c0e9616b72ffde264b708a6e1370cf599631683a24db15b20fa626094e042e2099b1d6daad5a6badb5731583765d7d5a6badfbd0a696035f83b6f427cec974f1fbad368b4319e816ad2fede27764cb0798810e43ab83e723c4ee2da7d4bed16f92506a69a594d25aeba6744b736da873debb8f6cee63d4086142d209a6086302154bdb96ac80e3c25695d4eee33d6729a4a0046f06b950a7547fc8fa4cb3b9c0dee5d68bb6e2eec6070469427f34a7fc6e361d757e90afd91e382be34a0a5338144ba8084848418924610ab25d6a319ef7939294d2df58bb8f783badd537a5d4d234de0c0ab95615f10cb5487e145a47657150b9f8a8ac2898fc69d3a10a0524f404184e609182e504242758151698105984329e0866a0c061db4184378345c90e3fc867cfd995dad67bceaed076f9addbe4cd2077771daba78d49c5785f46e57cb9daa8ffc97914ea518f42bda638b647a13e9c17d8478d2c3aa0bbf2c7fb9cf113cb0af6b8f77239f851a811bf833be8bfcec9f970dac8c911eb8362f7d543594a5a1232c7b536c77dfe8d02ad9cb9f74422211cc0078feda3e7a1bca172d2bcd74151169871460fbcf780fbcd1bcb567671bb973a3cfe39cf23aa763e14e83df765ab14c29c88f23c07776c5fb6bccf62d9da1985aae1799e8ee7fdac42a9f7428a73c0ef537af4b1cccfe5926c096d9abbce3fbbe7ef44cf9efd3f686d6fed2ebb73b873efbed6aecb9ca69e145df7572cf5f61e7ff77df5ca233beba0ddb443ecee9ede21768805a1fe160b945f1ed9de885fecaed3e9c65201a58f7d7f47cc93e59f12bd8ca78dd6f6cfdb7bd4c7b2b5bbf7442c96be87f6edf0f85d52dcf7eaec1bdebf5fe78bed63a980dd3d1ecb17bb0c3bdf41b737e269c3bfe5e9d04a74d93a7bceb064b1f9f1db2be2cf6287e9d74477ffc1d0eebec478d30f81288e0f8676ee511ed95dfe8ea81b4bdf2b6f042102dc1fd9734f8ae37ee7fd043b2f7fedf2571022b0d2c037f7aded818fdfc6dbe650acc59b41d7ea11989a78282929d19413b4ee50a95695c4de9d1aee96f2b2713470b809e71d1472973aa1f376da789f0a4a8de15ad55dea91ecd35a6bad8558d1a5cea1b5d69a47cd51a291e8d1aed11aad512154d786425dd34f18292d618b794c25944e3da1b98d1b794c24362567d2e1b123839214159817dcafafb5d6dcf64c2237ea0a88d8b4b6422f09b1bf94a209d5e05dc0021e67d3771e4f56a1eef36fba89498f2e94e8efc1e71e7feec6559d5f470ede1357f37356b31b577304d2fd4804cc1c8178d77bfcf9c16f9c235e79231ea70e71c5adca32a2f46ef14e4812d3d528436b0d7df1f07ff93fd1878e203527a774070999a6e1121065f99626d910c9a3d5c444294d559175a94ee5a42555268fd28406793c86643e688e011d7f4c0bea73a38e334c581d4ce872d264474d827ca1d168349a75ef8046a3d160f8da2062f48deb94db6a8e011d7fbc0478db797344db82bfd0b8cd5ad5082dcaf2a12f5e130a4aa275afa3e6ea893e74705059ee44e3acdd39a20c1502a29ee45bba7c411ae2369ad39ce65b10ee3dc80c528530a69c2b15d3e5c442640d12547a6cca7854a19a0f893e5e95cea882873a1fb0d3940db496506daacb4d95b99baacac6a9d1e5d090fa55001d0cd19a001c03cbd1cd2fde412229cbbfa85abaf9cd2938960e0d6dcc6d37af5cf0b056edd85a6bb5b539ebcf596da57a1363551fa86a65532b1b7777fbc6f278a0aa950ab55aadbb6d7776956c7a7baaa93d675962b14dc986440f499cc95e9c6c4f9b52957befbd4b565051955c41432ec10d97b3e72ccb122c66294e94ba74c512ccea50abb4ae5966594698a5304496a5166dbd211363062b495274dd73666506194cac1c69d174cf99953056b47c332b55665644d8df9eb32a5434616940228592cb4847820c461449153ac45c46e69922a6334ad018d1118aaa78b9280e3218417379db5745fa324a62e48a4384acc8e5e1af8a5e53f2a6e69a66a0bc70e49aa1612a7279de57c5a12e5d602eeffb2a1123b336c399970bcc2a83fbfef78f6c22463e22a4bc5c608c7c415489b9c05491087d45b1c2e4226284f322cb4d60ae490990f355b1e55290400a25971114938ba404407d55f41f8c2863e49a52e0f08393cbd349d1265f684c2e3752692da2d29f9545691554da747111f13756b481e332429b2081ebfaa82ce79264cb152e231e1043dbd048e4c2c0260531b88818018f704d4a00235b9926ae23faf572557105a4522a684f4cae3af2c0d92908168ea8d72cad793ffc0961387fb8ede2546a47ab5cf5f1f418d90955433c61cf900ff5f1ab52a97878787a7a7a7c7cc8300c7f7e7e58b0f0e6d461dcddddbda7c72728fcf9f1f171afeed5abbb7f2b5fcdfaf3e3eeeed5ddddab7b75afeeeeeeeeee5eddbdba7b75777777afeed5bdba577777afeed5bdba57f7ea5edd3f16fad361dc5bad5640aa162e5c00bd78f17a6f8aa238864463059ffbd00d5518a4220ad23060c4881123045285402a55a8dd9d060d1a34aead9486cf1f202090a885aa45a802c2d385d3161f510b17ef22c8d2f0c58b202272877beeeeeea5915dc7175f75afeed5ff5f14c571ac00dce1a001e80763c6a0313cc6ada0023003bb71108c1ad467850123460c13741d65c890419224eb63b166cca001127d90bf0d90c823d23a8cc78821838824594d44feac1b435313a9767cee2e43860c70877b24a88106f60542f0489205ee98f139050dc07aaf5e4a396c67ad7a33543342151d67801af82e8f78343e1d46d7a861e3d361b407000058f059e0b5565b1ed1416b7c356cd898b5d6fadd7bef68810ab444d7b1fc2cb0a0d50a400004f0b93b6500166f660e7fafa7503771b890677f8229b29543984f341fde5459f47ba2f0d74b6cc0e7a0748608c1ba298bfe8092f43bf0b40e4a27b5a3fa3014150f17180c96278b3a9329e44dee4293afd0e42838acc98f9a7a1e1084e6c3000e9aa8177ede6b308339f3260e03e283a99bacdc8180f766479f362e3562594c1e3e9f0f48843f04ba3836ae088802a9a031ac49112c870687866cfe0949f029d96afd7879ebfce416b4d431720702721890e7e2d36174e9b04d5f7c1fc5e10e03896aeac54e8a851a6db1c65edb1eea032a17a4d8710383f765091e304ca0dc69fbb73d136cefadf758079dc55830fbc2011833177c8863a685daaeb5ed3997466b6f81661b102bc10f3398d1326506850d67542d8011807d6dad6a5b6badfdbcf4b0b7b75b0b5af63685fdba98f12245972b33b60d2a427f1097372c8badb5d642e00a9b1fc8a0a04413319765b32d0d4cb605b7b5d65a1e2ab6657078c2d21d2c1026b3ade58264db143e0a13ac5b4f5b1888bb1ca7a5cc931f2d4c2b2d67aa84492cb4b489f93cfdac80e449cf9657b80294253c1a2835f9ac80258a2a4bad6705189cec24f18c8192d2f2a4224267470450b42a5401565973b6c2d3955814a31e578e909e662724151183cdb68c800ac7719c15d9374978928d9a8c683a0a941e28247179d223cbd1079cf4b0525466498f2ab42094f44821294d093db6182561420f2cb4190b5158e9b102d3cc032454e9a1a5287c41abc228ed527f8c1b748bb6ae93302a5d62c0f4cf9e332a60643ce99e3d6754ccb06054d4e87f2209a8c3708a538f2935fbafa9b899f23df941a414a51e5298286b4609fa1b018906f79cc59eecf2ff63574cc83ad4aab047512c07bde365c60934169344e33232aed0dc120d11f477264691d64acc2881b652b068d076cf59ec0d0d2ab11390446164333054d4b0e2031812642976d0b7948ef5edb5e38a7a083cfa57a4b4eead1ce9d7bab2550b92962d974b7ed15a90c8bea5f6eface307dfdefeaa7c071bceecb2b563fe1ded9ca356fd4e52b2cb9615cb1a45cf2abc2c6b183da8bf71c97fb9e0af2217d8afa8fbaa66289a7ab922dbbe46cbf1533ceaa840dba8af0dda69fb07e5f83a6b503a526ecf1e7c28da75fc1e47d125de5bbc567d774cb75b3b3e25e78341344d8ee20ff4898b34321ab52719ada98b15b4bb01dccb297b3bd60d1b59a35aa62a55d9b441ff0160740794a4a2149a8cae5f6270532af49e6002aecb7e1551dbc50aba3ed2a6235cd7e746d7b1ac62362d67354252b3983a2255a34b0be26a9a5fface1914a4edbbd47b622d82d1f769e6fe0eb97b08e5fe7e1db978701f73f17e13571b16b9747fc5cde954d11757746cdda0acfa198abea3f883067b80e01e95687f6a995021216be7914db4da7e22816117fefc9c08c32e0e7f16930cb9b8bbda461fdccf0d44bb70b9fd2a836e6fa305501b8b738a46b4bf6f969d214b0b33a8acb2cd3daed3fb184a5c6d5a4727f53535a6bec8ac20155d525965794f521cde7bdeac792c3fe86077bf4d173dd8368f8ea56ff781c49f925ee78d1d45a1b4fe3a16015344f54f3425b7c75f71fd0d2afd6a77c6983e904a47226030ad78abb58ea8ed270af5aa71857fe7552b20faf10864e755757b4fa5bf52fa459efa984c4ab45e478b7efd22740c2264d2666d7374e5fddc65def26800bab3d7a2c4609bfbec5b6d0aea8d1ae0711b29121f4850afc1efc421746fafc5557efdd3851257795cfda88ffa0df4d3478df48ba86812c87d8e48912829345ddcf66da8df60e77f679c95f58d1ba08ae8df54e26ad33c4f44f528a1e9caafebeac77dd42fc03ecfeba0358f3c63113dae7ed457fd4a35aeb6a7f4f5d3d7aa5f6d23eaf5afc447893b44c00051bd1e8910019380e94252040c109ed73e5f84877bfde1ab463faa2cee9fe7555ffa9112e1e845af373ea31efd6f8c3b5f448f2efbd28d94e019551c87f281a24828c93d4adc3eab258a46a2421388990a6b36f7f3a94c61082ecf8ccaecb27ed9dc6fff65af29c198ae3421e18c36f72ba751927beead134fec922e39b14b8a4604b235bba46b36d7c42e699bcd716d7e3a5192e3e85115a2322aa301ac292481356ad4a041b3b4942307498ae2cf8f8e4ecebb3651b2be57b10826b7a8b188116ac4930bcc7d9e31b210f5f55fe7f5a833fea847b2b2eadb2094bcf2c54214bc2ad3022bd22b94b4f6099daf104ab5a314d54a631b17c58a2e6952514c6f493c288eaac63e07f382fcf6194071786f3ffb0a98187c6fed27828077fe3eca1431d91215eab9625fe9cbb67eb4ad97d93607fc666390b66d6d4b8bbc310a25adee9cf407d3c6486563280ed302fb58a462a60dfb3489929ead688f8d3ea6d1db584e37442440c59a2362bf0405169c5ca8c713d079fb7a5ec0bd5d0179d44864b5414fd24b8f458cd027925ea8df19573bf8f572a5c6490980f5d7d4eb4f8d9f7ad4a7c62761db79c5b6938a6d8b448f4d1bd60a5dd2188d6d238dd531a6472221bc5e2e8d1a894840e9ebebe5d2634fd2ebf5727914ec803783c017a818b9ea1f71856f74fd20eb33463086852663a490418c511263f6449ec9e10972891c9a72600aeb9a5aaf67d4bd2426c98e729abb2426c9ae6e1a773767eec720d22fbe24263bbf2426c9eeba777edd2f8949aeb31f750ca85be0bac6fdf246d0b3f6c67dfe1af7c6102b070d1af7ea7b6fd4a8d1ba31c4dafe9406ae5af537b4bb6edbb75ddfdbdc1bf723508c8d7b8da449920b8a111463cb01b7a018338ef03c4a291d29c514079d94529fb5d66ae9acb5d64a29a553b4d6d25a6bad94529aad78d85acedacd5a6b6dadd6564c71688a81b51587b556bfd65a7b496b2dbec1db2e75ce568a73cf58abae14d56590a8d2c7b456d4fb9cf306aa06fd26a5d6526b29ad1445524aa9a6d4da9be7eb18dce1b8c3718763afbd1b9c6b3738ee70e824b79fdbfb06b67de72ab7d56b647b1b277beddde05cbbc1b1d7ed7577270be78b3783ca5a349d668a2f77b76ddb360e5f8e26f99005ee84dcc959f4e79eef50388efa255d12b2d0a95dbad3c59f76af7b3e750caee52cb89f1d03ee7a1b77fa28491fc438dbcf74733a42a716751aa24eee64371da12c74faeea7efbb7dcffc29fbbee918383939dd29a84a958b76d80ce99625f56b991b1a74dcd480044de726072414103ee47ce3061876fe9c6f78da59cc6c671c9eec8c030876fe560e5e86f4dc81e382366739ae862336670396cdd9a0c4e666376ca9810a8ebc51f30cf7c3be4928ec7b3f6c61df59d2153430792fc4c84c21d3b219b57e20cdae3448997de0f5b29d3d53c133d46070c0aa66681234c311a784b540481c60010509e90ad212dbb2b132c8b6d5019b5073c0a187292430d44ba749939cbfaf634cc0129a5031acf03169ca89e18b134f098cc106269d2a6452f4cd4ac830981743104b7009455d0c4d4eb825b21c83131dd8f2f8c304d92c8627273f9c182529e18712a6274b7e3429aa5204f362c20f136a33193eb0e4078c89862f84474d1586155186d8fa67519678c2e950ab42a638af598d8928706a90d2c40629566238d1a83d67525c8891468a0d240b5278505531d3ba33cfb7879a73ce7b875c107cd3af7f64eba074ee0952588ba3dbddb62be309adb3e70c863755681f353386d03c3ccc4881fe6ea061a4c13036c070820a309040bed1e0130d3ae584084767bc1cf900e648461e25bda0b4cb6f591454c7b9f33e300744695027b5a38259dae313feb05801b570e1e2c58b6305412f60c49041b266d0a86103002ffcf9eb59a4d37c727ad51cf6e44a5e3accab05ad00086000376edc20000ea19616d779a13e2f7f1d06bd5287404f7a2890e8e6b80e41206d732caaea8fe2708da4718a32a13f4a5677b7d65a6badb5d65a6bb3b515d7ca430577ef56ebc5b5561e2ab4bfb5d6da5ab3b5d65a8bc9b7d65e5aa9b5d65a7b456badb5d65a6b3d6bedb7b364684fafcb5e9775a8ebfa4368fffb41d0aef5a2eabdf782f7de7befe675f7d68c3bee5e226cadde7be79c2048543f08c869bdb7de5a6bad76c35d9e5d9e9823efbd97db26b74d8a290e71daa8b372b7dedfeebd3af587ca9c73cea04aaf7fd0aedb7b76ab7bb31f04590fdf6badddbe8fdb3008125dfbd9fba6cd41699dd40eea9b14476a47c5d393da51599e7b6f8f4f55f1f4f46c9e9d3d3fb5afcfadd5faf60189eefb7cb527fc6ab5f75a8b83c577efd517e8de163e55bc66399cbb0704614363f2e14ad5d6cefb7418c0c109b0b64934101f600eaa2a1929c551ca41699d8fc90829d0493d06a6902b391425264a2328cd5a5532528aa394da5165150f16cab55aad2664b2e867803a95f089762578c6d9743472a7692384c27b7cd8b051a9787eec1552850c096148085a488e1a9bb0874d0f143d60abd5f3c3a27ab9f3bacc429c17bc601ab88ac87fd808a9d56a405688650394f2daaa039ebbbb7b0577d8bb836ece49575d5544f6c694bd70baaefcd448b5f6de6de3a6e83ababbb5eeeeeeeebe7118679c73477d568cf3e79e73eebacef3be1a443be89c9808ccc9b19bee54965277cff33ecfc999f6fb9ca26a543b83b46a72dbe501411d46a374ddf56da523ce41a1505a6b9d21a21c3b7cdf0853a9d40e49546307dd2110dd2a1e90082889ae63a9bdd65a6badb5d65a6badb5d65a6bad7574f7fc5d1e8968c085abd6eed5bdbabb57f7eaaea3a3934aa5767676542a1511518f4f1895554e1e4a1d746010310407b90811471fde7f4e854dfa449f6a94299bc640a21ac3a2c1112f81c928c42177538de85315aa5b6a14c7a0569916d047a2f5cba68fc2941acb5386e90b3124697dfea49422a184e6508ae4068922d66ceab02f94525a4413344529a54eec1d4a2b0e4478e0043e74608511580e6cfaaaaeebba9f212165776fbbaeeb2a120f605ea0bd5cf9f1e440a2bce9edc3ed081cf6f6b38aed618448a20ad9cb8531fe376208a618bbac6184525396a61a0864455071c66e7f99d43cc984465f950f46cc0050c43d7387b8730aa625fbbebd976969df2150d8778811ec4b44d2bedf3af364df1fd27106cdbecf030d07f645d3b46f1a12f64dd3b46f115e5eba0a5da6cb2484d3ae2f5ebabc340dc6defd68632c44d3bb80440a2e4079c900b36d70e802b6865440a4400041800922ceb68fdbdcb80104961940f0b06d8e7d69afd76bcacd21892b650389364bc6daa06d6bb0586d34e55463ade5b66ff9d93f6ccb83990e98e101cbb64ff200c5b6df1a62b2edeb506261dbe721dbf67d70a01467db0fd203d3b6df8107b69df960c5b61fc20f4c2f2db0a23073b634c3b63320901cddb9b570a40993315a509064042c2cd828a4783c4ba66bdb33d8be3b3051e605ca832e75ebc60e4580a006aa103ec6586bad7fcc9a272d2c86e2e9c598374d80546113b226949a6072118e8953b452d3444d143557d46c69f1b22725b050856e62405816bbf23302a3950ce94aa8e689858ca9c86789a61f198d4acf12b05076022a3c163bb1e4041a27d4f8844e4e5025e1d44326cbbdf75e36489435c59459fac1060d0f48c491648e8a52aa906644eb423ba2acb9552119d9131dda0e19214cd03fa0e89222a326ca1346984ccd494e125d34193831d06210348180a6759862429b241d567852c44b87189ca461d241c624044cf88726b8a933a20967c491114959ebd044d1e606ebf006ca9dd8e2354a3fd630717a87244eac2a8c628a1f51584159739758ef1025ca089c76e80213c10e47454d453bc8a614a114bef036806025a9e0c3942f58b4dd7336850c8c351aef399b3233010833539a76a99f86294db08af4ef399b52c5192a55c2300446e0cffc16e4a19c4b3ff3d72f73e89f96b2c0df11756c624bac6298432bea5c50ce337bea7c39d3f89773884e447d8ef8bd8b3954ec6ce888e564431ff42270a744d47ba2fe1cd17b1d31e7679b0d72306d80ef2dd9d068a858ce1e76a9cb09c410bbd47ba6d99a875c3b3aa2426a9ee7c9947833a8cc479b7b8ffb12dc9c36f7a56a03c1dd9a6cee37cf0a4a72db6b73af83b2d8a5dd774baa4275ac5b52cd3f9f6e4dd306f7155ca14b2e4efe42716cb5fbdc7369e615b93493c5855c9c79c1b46eaa0836c7856073f768ab2579e8ddc7cf81fd5c57f46f5265713f5dd8c73d72e12d147719b11125b91f63dac77293825ff7a80ae1e7c27ba4b967818db82ed8c7d78fce09bd7db9955b93d1e67eab71b5cd4dee5dd034978f9ce2ce6883ef91b5770a4121d0f2a042add6745971ba3c178f0e9a73ce39e755b5bf01f9f4ed58040c10f2e9577b7f0392fc4b7e9109ae7ed8afbf01f9f48afef68fb8aab8ca9ff30884becd39e7d7599c5b67189651127c2ae2a2ca0261c4788d9594d0df971c6c73471c8c039f2ba238ba073f3f75af0d76b00d96dd1a8a83e7c1cf4e1487eac1cf6d288e9d073fd7288ed4839fc1cf4854686baa2c30a8829e9f3497d96036aa4239ccb1581592f1e0734719e87111e61e7c0c23638ce573628ee12d542897a12cf08fd0a08131967329682c679959a682b1cc91e3c68d5d724fe44ff0c7aae7ef1f71ad7a80d4bf2311303d4f64044289904fff882bc7385865818fc7b1c45a1c4b1dfe58862fc6f2398e5659e0932ec6926c31963fae46163fe1586a9fb1958f36f89e7f3c222e8395f01266c26828093ef9d365c5551e2fe043c6a7385815c2f8e9712dc44e1ad3b00c912345fcaa2cf0afc81d7134b0e48eb8a3a31cd59554361a734c2cf118d3250e017e55a1eda8b2c0bf9ffa9e3127bf6e4ca1679b0cb41da58e8e36f8d3458ad3c523e63594045f2566274a82bf23e636b94649f075c43c0525c1e76855686be26038d35e18a68b3608eb621b7cd8069f3baa421989b2c0cf6a9ad0fc52584667311b3cda60ce98c71fd4254397d1dc13f75431e69e9e36f89efd5a059817cc0d3e90fcd2d9bfecdeeb44164c3ae7c11e3963d206df836ec494fcde52f27b1dd43352ceb10450d71d73acb2c0ef9cd0b3ccb1a6a6a6a60d7eab0a3960b2c007a3d8e0e7b8a0e94ec494ec5e8b9692dda3c41c31487e308319043b2316748506fc493e08165901195b3f8b606e95df77fe551ea7cbe6d873a2714adbe8a82c1087f7f33d2cd6dfc0f74c4524603cbfe713ecc6361c5416f818fc2c967a83ba07fd3d8ce679b40dbe3375e07b97fc20cd20cd20cd60fef2c17798831a0cc1f73e1104c1ff3eaf29e1cda0922bbaad2ae4faf7a2269f2d5ed0dd779e534ed90f77d1a78f7a1df451e07ba223117d54163864dbdfe8304a823f4170aa9ad0df7bcfbd40eeb5c1df68756eb40dfe26ab186fb20dbe0b9a2e72a3ca025d1ca1ed57071feb2ab43df8215985eea3c632c49b2be262568d7eeb256cfa7acd765a43700cfc0625c329f404260620e00d42d8c107f3821bf49dd20c5c844d472edbdb0ea60dbf42039feca8c48c9a6962e9d08c00004010002316002020100a87c442911c87f24c14dd0314800d65823e665c401f88435192c34008628c21c4004280310418620c62caaa0ce82a27d81a1ac6c4d01061b5d9edcef91f453135597b57896b298b5c3fed1607cb8201197f8f5f0b436057a8d99f0bf9df4b0982c9d8a91fb0b66e869a4f43fa1ddd75ec69db3caf7c264881990dc6a65fcde8b6eb2fd58728b77d373a70d78b6ab8db30c1258e45eaa8e9bf1378e20dd0f39bce6dc57d9ee720f1fd0bf4ec72b18683abeb89f8ecbbde6857bfd033626cd43430c9911a89c73558d1ade1cc59ecade19f68984a28de704b00c86e1033c924462667a000997ad9e6656fff6e3aa9e4c3221c21951df47598f989bfb3a747f91fdfadc6fb8a6910d74f90fdc7cb2040a317413834047e19b9d105e45e54c75b7a7c8de76a4a3cb8b2b970206acbcca66c79930d6e481c8615bfce2dec4810865abf6c19f6a0c1c2a98c2f23c3bacbca2dcf7f44cf630c6ec94b5026011c8f8f599dfcac8e5c73b2d5dd7c505ab97fb19c9ee6409494fa22491750fde0fd9467f535c948f53a394e0a3fb5a51439a474a43ffe0da2f0fb6225beeae4d2bf425289b3d7d24d1411cd89049d38e1905cb5c1f58c5a7282a2aebb4b01a7f3cfcf1e91334257e1c78ba5ef2af4f6591ce6e3da3c315dd24bef1d6226f74ba6169fd7c5f7107c2e78bd0bcdf326af270dc269fa5692034440a399b139884ea4386b2e6f1818c921af102f9a4d4776d6e1dacb78121feb114e2da06156ece0b1294d364a6f9a945644790f06d10b189394897602912627164d109876a7d104b94a02d963b04c98adfe403829338635d40289c7bf7ec49b1885578e65310819420c4340b0223899f025f244a068b79d492266c9fcff60100068c9cf3b66322c9a0e59f6c641e7810e1f95b821c769ddc56ee1acf5984945cb94453feeafde9aceda418d5068c6ddd8431a9b2431330886538fb0b5f29e2cfb904739c92981f50ba5a8a09900e1cbf7ce45b53a3412d2199c79f3d73279ce7bf8c76555c28536b3115e28e1cf42d426884fdfa5657aa83f4506f8f2c20a335085c96c98e8c841e481cdc5f8e789bce4f5fed95e47bb40a8f0d98c2bb2805ee504f04ea7f02d6119965c5fda975c2f4f89a25a6b61b4201ea3aaa0a9a9f0ebb8dcf3e12b9c10b0b18ed82b3cbd7e99bd98d85ffcc22551f2a8ddb51e579d1c2839cee2b99994c7ff68af798c5550d55ebe53f2fd9c7cd7fafe95ef8b372883c2fd75f738a45ae9d2835979ec2e8472f86e2e9dc91b3027eb487aaf74b275c9bb45fd7fb93b914fc20c0163e2556e6820d3e87f737c6b4167f9950cd460687eaeefb159345275223f0d26ebc0348d4feccdb3c955e74251d4956a1198835f2549e842e1725db120c2984255cb18ae10a0ab8cacc0ecdedfc96e82e6954497942952d6861f9b4d9927fbbd06b62bae756af2d71b39b2891f5676abde8076e883d075b9e6df8bcb3856d6e558bfae510b77bbe76aba13ebbe1c8ea3c63af59f36c84d316917d238164dedec159226a108c8ba947c9c62a0c03014eb9c8d509ffa5bc05e41607c6c1dd535b1a1aefd55f6db9f96d7a3b4266d2eb39f69bc491c6705170635417c76f54703283816e6819c50faf07ce909da0355d728e3cee04d1a8a8918516c52d4e508ab22291c139e499378066fa2098da96ecea3b20d632bee696da3f856c7114bc00c7c795d6c50aea4a5f77c8e8668e51dd5269c2ef02b1c48dd6419f6cbbce964b8875173e27282c87f2e351353154d91a39bf7f803d6d3d51d912d98309669d2010b52e5839308a2069b02be7d3627223b48889c6cb50d6cfe56f7bba647e6faeee75aa747dae0c3b80d82a1b20897a22569a1aee7de6dec850fe285470d4650fd7ed5bff265cf56e3e0b7aa584af25fbca77bc576d8f8e320a50a595325a07b4a7cc9fe66bbf2e1dfc3683c6a8f769f2645cf49f1961276f04f5a9bc0cb9380747d1075107a28e7a5b6d071242cab1cb1e0ea9e3f70a6e4bbeba8fce40b75d97b52356f987ab24fa187d438aa92fc23803271ffe4d658dd7f9d4b4fdc4ccb8e39ef21b13ac9f116ea22bf08059a1029821fbea8ce91694f634fe3e20670d28b1805ac5bad5c52de942e18e92dd1950384b594cf9ee9db5fda8399a017dc3204bd8ca37c8f9393edebcd9fd887d35a00d06b25c7481116d85c65c45232f6495ad569fb53fd2efa112bea7534b0cb2d399b4caee03b8d601c6618d213f97632f991829a2d23a0e988a01184ada7b7d6a20ab748f24b75b7acc32c90d74eae950073858dab1ae2a47d95f83d02dd2e42ba19c89c073e8394857ff25a567ae5be5d255ebee0367125100a3d1895a5c4794c0679555eaf33c0f7fc85480836be9d8a913631f4c1fcd008e5d0d9df2ebc2d77959d0417b9eb31165b5bea1430194084028e88c6f2955dc9b9bf641c3a6a718d1c0ae442a5378d747440564a055d57a5e6b40dbd02471728212291f7bcaf2a6ccce8b9c4c47b03330427cdf29f9263bdc8cf8021c8cce00b28bdbf0af40bd2d25e6b0476e5eb662ee0ef846570cfd469589934a62d5cb8eb8ead95dd475c37147410608873b81452f58247c2bf07c80f241789faf967072df51961e9fc4b31927fa71ce272e5c8be516e761f8754f05d74b9bc8074ff31ac4eaf56e8985bdbd9640b994fef6feba9e81defd161185527321d3ea19f3cf19275ea7135a42da5955200b2a990c4d2e6625f8cb513ba220f1d67fd3fe90b28eda00bdba9fa161133374b3d69d9663794b9d60e423bb4f92222e3c011f1367d71055c4f9e49f007cd48ef0c6b59517c8d34f9cc2895bae29adca51953b4d75035c7e7b437a5619d2e369b310823f197b2411afae924ac4111d40eeab670594b05482e41417a181c89128e3595a6d09c0db69a6f9d5693a3fa789ae4c1265e64768fb4ba30d5f41b7e896c5857a57722bb24c2dc61bd42f36f78b5d972703c07644f72c2b0486cdbf9042177938bd4194fc2564851ce6342ad4d864df71fcc4d3c6755480c9a1ae1eff08b461d3404ab9a28d109975dd48eb47b23a73dc73228d20e0d6981777c23b0be501a1084d4f5cca8b54773e8c85fd612003940f499e88862c0e7e0b4bcf235a960fc5e925701b59e568474ba462a9ba301524dfe5bfea76bf83959878a77611885cb2265578cefea64cc9a2456ff9436bb136ffc38633df479f5325a5bd0587342436c0a3ac35da5f019d2f536810e36076dc04219cfb3ba8fd1fb3987d7c4782e7440bdb1b8eff106a67e52948bba04147c8cf4191cee1aec8d2df88cefc3ff8cbf5b353e4566453b35be6d199fbe7d073f90b35a7833bebb682133595a7c04631904f560f184f8b8240c30dd5a0e449e2db9cb5127c5a7ac0f1a70f57da79237af81cd81088e977d097e61138949481a1137b4de92edf28df8bb787c9102ce7befce19e382e5b761ad2d03d3b71b0887c05029fc4f7497ee6aef1cfb1ec7172feb4fa09eff82041ef92c32ec5de519defc68c2736e9c3edeee16bd2f794eebb42bf65dea99b110037f29017aafdcd7d91d036569bf32e00d8b7da8925ad6fe4eb49316fed23372c42983cae806b82a4f7cc58597a8f01fa84db9aefd1e48f975b892764ad8b1c3a7a9fcc9a27aece21e5883c40bbe52821d8c02b40981d6b65fefd7922ab78966632ebbfa852d3d724b27093a6b47824b0a6b0d88d2fadff1f5901abc0959139faf3fe39d1873be45e5fcb7ed134990172dedcad8e998158b2bbb5327e5a404d95953e1f51ecac2561767e18e45e2d0aae56a8c471d7aa7140f8086112591d9cce551e38f0ebd2fcee6ca0cf604986e4d1e98527302a132d103a27077a31d525c88986c67cf4a82ebf0bec430cc8f1ffe8f92a88c69672cc364ea2b5ae017ba9d74188f0c75acf22dea894fc5a1cdbcfd916491f296fa19c755201e98a3a190cc5ea987afaf44006da82152eac9ca23e503729f814944a78a604891d0a8d0729dcfafb6d58e98603c8bc8eed584fe24c37ad1b7c09658f982fb7cc585f4f8e7e637b3be7c20dc89702920252588efbbbb8bc3537f892a44958ce2719a15f1a5ee67e5b42068d8efbc16bebe6dc98000aeeb5cc297310fb464df60bbd49aa5265cc59c4134a009487c1c97aa94f53c318170df2f91110acbc96d6a289bf75376ea0ec600c3e52c5cab8c0743e878335034ec53ea79c790e617ed5ca5c6066885f1ec090f3255dee96b256f02e8f7d36fb9c1a476cdbfc23e3d68d2a31ab99fd207e6c60e5a74b6cadfcee3072e9a551c4118f3e28faf0be5bdebae8015ca5a829732ad95e599a9348ed881a992c356381a5b5f0df5aab843e029e0f24ef7e5b5861a0291b8d5a124feabd0d0c8591c19ccf10578d813cd4809e55b18f625f879221052f514ea5629ae02e7e80ff21f9e0f680cfd0aff1d1b821be69b73d9a34fc08cf43260813c0ff7a646541136e4394c5c584ee40feb07ec8f1def6592c9b624f73e47dd7542ead4b43b22b47c40ec1b8243ce508114229a823c2d9a8c8679a0c7133a05be73b83765153decb64c12d25895ede4ff315904d593edac9a0bfe41d5d958ca9c999c775e619e2af9d6acb34d2c7f521e11ebef4dcae296c28e304d14410664e6cc9ae8edd0bc5eb6d2c68114745428a0c29a69a5f0546b2d3d2caff559c304e074f7e5c6727ec59edd35216adbd139e32bb199d5837ed303550a2b5cb739ba2f977aeb92a304c20481c9b834e5a2bfc25bb7d9e547f640821ba1a82c5526abb8014898b0b5d051d9cb5f28a445e19b3a2dd950c01204f1c7669ae2131694420c44ed42cbfae04978fa5c57548acc773861715ab9c7f047d72420fa8adde633d1156d5497a5329dd6da7c2b340897cc030917c67c974e145fe28b04d0fa082f6270fcd7443a1dc948c9c297f66b40da218fa736c8e30d469189d5fc6675bfc5f9b5aeb027c3dfbe5df94c11f74226ba670c628ede39fc2e76321503e2a61a02915b098f6ed2584d053360e58a7a6a9c24cf8b7704ed642d943cbc97d07a992a1f5ecaebc20401e7a0203729716ba6b31ce7ebe75c618f998644fd9a890b581586d596240c7626a19eaf11c116e401875c37f5c7d880b10f92237f941e269710c77c07a5acc91de1e4728c5c4d89f07a235634e62f219f18efb3616102006a12c9f03cfadba5514d5a52ee8ef3e3bbde7c2c413a2146ce87e934659d4504f71b763f423ae176e6af2a606a6170f834f82346d632448cda60f28f874541cce70ef4136548a07498c113394e64a250902b398edb23d3a2aa588830149328f483ac398405be16f72fa02b0c214c2d2564c4f7338899174d7277ab2e2e81cb2aabb7624ba23bcdccb7adf73836718044eb753229d835c634dd9e04f1ed9c1623006d9359f2159fcffe3e4f5acbfe2e0f4a815a080ad0735a752a6b2d7b378c6ebbb439450d7d475eaba29ee3a90314475edc8248f814f88aa0701cfda12108a323942e5ffc5446ab97457776a384d1e7073b85d9e8717a91b9ddc1298f4221817d378a7f2f0ab05d3dfafdcacf15462448839c82a2020495d44204bc49a8fbff7f19000d8393c670b92f5e28f051a6fd336f00651d094e25b23325c330a803c32d013db104b79129954d725bc94b807d5cac913c61b84696ce5574615d9d5f6b5ee87ef037c3ae921a7477cfffb77e9e76986055f79479bf16e1c572bc58ae94842233149a8845a279ba6c67a8e6fd357c1cc47baa6257fbf55bea7c025873bdc1ab3374a3ec4df449837d3e841ce9a90055f78f5c05e9f5217e1437c09c05135d35e3da575ea1c2ff3f90c11cd0816a01ef9790c159b19dad415457589181ae8b42bc4c9235981e0d61ef29e3ddd9747826c5f97aecb0063e132857176e11030b302c1d3ffde49776f3082c3d30f3ebb62ffa322861e5a88b238c78d965621ce69d1dbfb1a5462893067f99452f2cebb646401be4550fca1e268fac4efdc31b1f9876d55bb6f6dbfdeefa8428cd0bf3a71e3ec12ca2d341395aff4e314650fe31dae67184ed130bf2417a7a1cd577cd71b3c796e75a0fd6ce479565d2f24cc1ce97aa276b2b9381644c5bbba4168c7f81147e60d5f0874ad8409424d7db856373147d036c6b2eba5c3a042d146cfacb30ec4663701fd2a437f2f8d155f3b8cac7947f0b226d3e25891cc7a5f2f7815e9ee68245c23ab9ad76683f27532dfef21efeb63b4288e29d8365011a4335e2385aebb80d558e598400ab5be7e43cd49d1be351aff492523477b50c6fcfedc8092456d50e2aa89245a932feef1c48415f849ec4259165bcb2e7e2b3ad2782b9998ad1a8f4eb0e1f2a959a2634d643e5ab4b0bf48136b810d205173c7799a3ce7b4319821926feb0c756d78f8486ffc99c46ee5756e4fdbbf15c894814c98a1d5346e95875166b22bfc010c74936f74dee4a177b3fe1402f60540d13c36700a8b44f7fab7e4106601b1f9e70a03c7b262ad83a9f9516a01e7c7f9b4ea9b7871970f8c35e35f08387770dc88e32bd6ec2ba924b91af8830fd36a33d0837be85db1113766167f79bbe241969656f848299f88218757d2509141648a25db98bab04bfe79e5543ffaa492f823fdf4adff6369b37bb93f6a607d73e534dea327536dbd957edbf298da5d7d9afedd156f54f96fe52eea7e407536053cf7129c1e982b8eab2538206b3e0ee40f81582cd970efd5644fad9cf72a46be60c6f537f10a6f688ff4a152227c333d81bda8244522ed828692b3362e1e1489da72b95e14c402478922307856c981ce5cca51772a8712f661d441d5126f9d7cc9b5d821395c7cccc1774633c06a7874c1227bbe284b8a0695582c685817749f7219364335e07068e9264512fb0ea7156be482e076aab1ff85b059a16a99732343d0c3f01b56b381724dbde96093f716025f098329b9dd7449f02d00a1cbaf7582f49e89a2c6347477834cc210947bc3bf93adf364c0e52b95a1c2fad7d8d46345a87b2d83e0b0c2ac7e6817bf6befb6f4552c4356f3ce9bab94e91c835c372da1abdb896fd86e1587567f86506b25ff1cc8137402724630d1a6e6795f0b0b5fd4c94b9a31a81a40913a5d5251b0a82f77c0dbf6f4375eed7e34d7499c9d65fc75fcdd5f839678c6918b015882cb57c4c74d030fd5e06c60661c23b3cb3e30aaee8bb3c66948f776a705da9db7dc6c89728a0716a7058c7ae127dee1c63d95b75324ab28f68ade867531222cbfbbbc7426fa6b245e29c0ce08c05cac466e8d6067f216604e72bc4d0319c93c69282a8325ee3973d5a56ff521dc0a62e33379ed36108e069c38247f2b42b3a1edae6fc9bb45f2648a3557e4280ff6b9209ab9c60821233e81312b5f372fe9de589d205a773c8043d33c16f8ef38462ed9c434624c213aacda13eaf674c20d75d712b4e992fbb2b3188ec9776a319147de8dae1b8140c1f7dad7d16869105dad409a0dd0ca53244bcccb90a397302fd208d6fc4decf8a9c147a7fa33800f5de467238164ea008916557b9a8f8a19c00bddf4aea3cb139f5f0743a83641b764917b8fa07d047d83609ebcbcc9ccdd4f3a4677b7e7acc37467afab81def59a09b8960694d199d0b62debb85613acab89ee5859b41e6f6eb891fce67b290289c0b33419841a70978da9e711a023a9da6941d5122660c84844ebb2b087c8eef95eb0e0ce5a8a1b9494f16e57d05c1e3a0fe13730f9c9fd6e026d1e4e5b68870ad0f832f5b8d42a691bc36db8f459f36c84bcbade7d19d89fecda504c6e64ca2ab9c710456130918899ea439ffdfd4728e2593fc80354cac56a517c6d0010ac764dd6818380ced161d6a89fee0ed9afe545dd749b6006ca89220c69f8f39daf7bde019eac750a129e1612de1c5b8b6da9e438898d667645789e12cfffb0dc5a1759c98fc5b02b387f833aa559310f062db5adc550a04de4ad05e7ebe46c7544d822dafdfb8782273c8a95c0a707babe9438f76fe5ab46335979328f0bf3cae9b4757b7f3ae364efcfd06a83122f0e81bd71a0cc22ed62e9614124125af12cefaef02cd343550b8860c38fb8ad0cd2d5f7029c1180c00007cec855a43d54abddb4ca207660846fcbf12cc9834c97b2318e2706fbb8e75c9efd4a124f2608bce7b5172e7dee2b8dd24d9cf1717a505051eb659b7ac3b0ec6990e77e3411ec89a86d7c106b21373d4f5ec6aa034e24f29f9b40a1575932636256fe1c2007a5a95db515d54f2351db807713e3ab3e81f29a723348b6e0a1cbd158d36a676f3c5da9f85ba3094f7bd70dbc2f824f4640bf6f4b210923f64c061ff99447daa9fab2d10cc8eac73d8cb2a398670bda1eee8234f747aa1650d2657da29ff8ddbb3ab44ca2bf9097bcfa1138391a68482f49b86cf19f345f81916a819ca3b504ecf853840ae4d56599edd4cfe61095c9bd03b69c75921c6f4009d04da38357c226212467928af5885c8aae60b3ee956e7f96046165fcc7196b2667d276980c4f8c51c143685fac6243829f00e7c90e63a59fd993261a1ded0b202e58c4c076be600272a364d229c1c9139b3426271fa5d889eb6522142d589a0420323c1e53559b27e161d9fe67d28ca80855b3debc04c6c906072584ff6c051f6c4e997c74838fb9d157e725d55ef61ab258aac8a18d1042a5cce74833b4e1151d3ecb39d9aa6db8a0dea13bda5854d8196b41c2b61cb0ab2c1d673d280615e84e2311557d97e542a09b359beabbfc494d45b810e82761c3c049487cfc648b7a9bc27c42e68fe89f2e23f3e28e4958e3083356b54fc04d1e5d70fbf034e0db313192aa688bf60defaa81a0889793e68daad33179b924af7ede6fb6f97d6a51b018e3af64dd9aff4879816b728e800f3acd2f2ef99feb4fa6f9860be97b95e9b9efed7ca5d0d3594ff73ae50f64bff6036a92be0c1097e08c24d1525bb1f1047e60946366840bc0c559c0c47716a2c79dfd095fbb35f54a57d0f2822777bc5a6e6783981ce89a55999814a31b83a05ef1f54f99910a78fdb2202088a369fecf5209531f46acb16648c03232b24b9e0245266e3d9d08268a59c394f58acc75b5a47fb5088b5f8b3d5a9602b4d6040a999a29864a860a4cdf84b6c3d26499c6ed6e2f71a74d1a7370a1057a65d25fa7bd34120cce69e954d4f7ed3b510d6c3f6e99fd4d24509bc9ab57929b742609acf035507574b0f619a431dba47ab9b9a61cb8b09f79c694c392ea1bc3271042e2c835aaf211dc2798c58ebbe15fc4b66be58859b631949c78cb0c22b42aba140d28a50dc5c55a8fa56705028dc1c044576a9546c45ecae16666dd4a4c8d28937d66d89d54a5fe386d07c18e924ddf712dd59f4563434b14bc406def999c7e9358ffdba647b9420f4e3191ca553327665ee7f68b0f6525774899f1fc840a49f5de96a5775990a1f9c7caca4e6c55e4af053c36b95a597f5c6a262c44ba364a551f21dc6d275ec05cf7a7b95d24ee44a02627283ed6e3e28202bb5c70d86bc83a7d8f32e2d15be65d53926eb1ac2031b0ff2c860f0ed23dce8ef2dbdcc3d588fade57de8514b067c257800fd300fd1e51f3ad58691b4d0bcbb549600970cdddda5acbbecdcc277e8f01bdf693869a4234b9a6cd216812172d705c8b69a73dc97c31ba0635f8d0811ba7b118b9aa855dc0584a02d2e5941ec1b749750255b3a9bd418ac5117c0c2f4e3b52f4092dc915ecf99af53d2aaddb94ba2d41b3bacf405d47bf7886fd3b5b0c5cc0582700453fb0a906e214cd35247840a12f344939f78e7ca5adc366d90088c01223f66be5110460cbe00f1e84a9128436c299f045500ecfee680d69fe14b27cd3418b273001b43cb65b4daac709bd4b4aea000e3ca8aaf95e8f60fae327734d716aa8d80930aec558bcc424bd2030cbfbd3707de433ba43c6122c014d63e052c08d0118f14980b576591afbc204ba19b80dc5d047fa7f7a3ff92e8df5da75460d85d2282bc0840f9b9f78f043fffcd317206558920c8f5295d520d88779f75436c0171eab692af86fa0ddc7744fe435522cca42fbfd081fea78fd7f6b984222219da97851bcec481d7845ce2f55f1c33bf6ed7517d949e698221c74a21d3728b3748a1f7ef0a3bb1cc917e656b37b2f55bf3061983a21d25889e4ec34a8784f6af12ae3d909eec0d58f730c2ce008d5d5f72eb9427eebd4423c0c8b61a725bc0f9a121b5b3a73c551f71bc72fa0cc90d2a21713052277e2eba3b5c253a8eda809aa671964a7426afdcad8e76005b37d918ab051f27dd13faf625cc0686923c385d7fc1408aec4fb0edd4a9db18c3d1b1023c818597fe145a0be2fc2d1e77be16de7bdd51e1ec88d2b0d859f3a9a1b85d51cbeb90c3961dfdba70952ba444ffaec9c233604976c382678fdadae4a0bae5b971fd85f5c6c6f38a1d56db282ebe3ef698c7e44b47212b48159a00c102e4165aca1e4ba0f781068753f56835cf8eeb97c578a92d0c020c2df5d9d29c532814b7e66c02d5d5ed9201c07843c4d8df519ccebcf0fa05b091b7b60a1884bebc103bae7a71251632517c153f982862cd855b904f70108efc6951b7cac2855c2bdf3a0658552a3f4336f473ca5bd12088fa3becbc8f05c1189d01012faafe0471c262f1af7382ed398134c41ea6a16958a786677efeddc8b77240862b30d07af6ade53dad1adb8d01fac102ae29db97e1d6357926311ee8a712f5b7f23d4353790a988357e13dcc119c5d9eae3d6696cf81ae24748401fa270dc3d6c5280c4403eec3d35125e8a8d3154f84a04d6751e00788a62af714ce12244a61ae16e8779fb231a014c71005fb58df9fb37cd0a916f49fae00ba77641461967b2b493babbf9a9d544d66dea97c3015f2cf04cc93c8dc239557d8503a3824878c788da8f7196781894b402fd8e2d89d30dee8a13eea62bc8bc843193e519dd70627b56a10a09372ca7a609d589c31e755900b0e11033a624200deacd23d58d5a261dcd289b5fec7714cc15907b986fffc9f48ffdc041b6707fe507a3b91d9cd15d74176e6963e2caaf777e0555c0f22c0747b275eadcc03560548278bb0d6ffdf9ed91624299f8f070246267dfc85eb161c05f6cbd9445c2fe0e17e2bf3425050ef35ba3cc0d74a55fe8d73b52feac3fe2106c18b0a8bbf02b207170cc541b61b829e8b958897ec7caab7dc79a17a886b5c2d0b27f69c29f458c6c64e24fc87ed1eeddee1b0ad699ae2fb6f79c664fbd7fa7bc9fe62077c1b5a20289650fb9fb9427790cb786720234980d00a435a097d1b7709e40f0e33b6180f2c56fd3eee3c7ab131772f57afc39e9d735edef9b9ee0d1ab9519fff1e7f6b24f685f8ed0ac01f199585590588babfcf077a6dd39c7b37ed85da08c9a4d42ec6112877cd25e793a398b763075bd44cd6aeb8a6d040612a5c7197da9221a1fad69d837c84b8d08fffecd18799f13383375fad267803dccdc461f53c2f255b9bfa28227f182224abdb06c128548006d3ccb0442d88ee898f13fa88fff7edbced3f26c8ccd568afdb6e481c9af5b21e4e11f79abe4da39c4ccb8961891f5492e5fbf0fb81f8df23f9fb3709714e4fe55b218cd035e6cd0371a5344e4ceba07f3404e20f6a0dfcfd232202345a7bc185cda2331b4e6f38f18db2b1ee94514234d9a4a412911a383a3149400793b7d64e4d511ceec5899d5b7d483185f47ffab1f1ccc61f70165c0f68e842e60eef4014fcb0bcaf6870bb9258804b4a2179b8f372818e90ea2cc5dd208031583ae89fe3e012c818ccc361b26e7a6a67643d47d7a06788f04383c665a7d71e424d22f206a7ddbe0e06169dd0e9c900ac2edb6fd67da36ace681ad3dedaa02b6f7306816e22891715ef946ca29b12a6a2f6a43b1684697740a0ca56261073dc15941cf8cefcd7760ed44d676eb9f6590d52a7a6ee80814fc43593a492d39cc9e6e80e9f85fba34751ab426703d33dc85ec262c751ca69326e6d4104192fd9280a1982f32af1bc0f43201a773374788be3f39d12ec031e11b7008733521a20f15f38e87b0be1ce2394f568bac2b3aef576a8b641267ac81f41506af7bdadb5a75ff18dd1c801b84128f733284e4ab2b7db5601eeb191b98ce5a23107cd0c7a01500f194df7c2627fc52a2b73fc2d955d66e14e6560e88447587054b8ba6dcea50fd3ecfd184388f7c6a960569cd2a4da0be53568e41a72a6e7645e1e6c435ff92804e925857e9f06622170510743db2afde1a329dd0953f27f5344afcde33181b6f010adc49c8710961b92dfa7cd2f55f2b0781808638e209c4c5cf5aaf94166fb1824bdb3c956444aa32379b79743b438972941aa811a527378863190c31bad24304fa9e4059e8afc284b980587683a8f14628b0711709aba9cc5af570870e22e45d3e84beddbcfc382d864324d9eb0409558bd437ca2219ade60228ba6b4b4ad13bc4de71568e492f8ee78f4b8ed1e2228bfb20e17920d8924446efa5e1a4f5c343ea31e9c0db8870fad37e4a1dc86f1618aac9a91a103d4f17bf8baf70a1ee61672b3faca04535b49093a155e38361cf78521dac54b70575ea75915667da5acf9e7ed2a453b015125aad9a8679137e696c4c947beaef0e6e84fab616ef04d8b0549e835aac8ebd9c76dd01dcf1024f2fe320a171f318be32efbda1c7582048bff3ec7196799b5b76d8f97df001e7cf9ab8bd33bd45bcfb73e6ec840bff3abb3979e4d2012ee291e3bca107030f2ad2043f0601ab1ff1be9c7654bca225b14730f6792d315542c73aead0c78bbe4433758363a00dbb3418007195bc1707b0bbb00861909a70cf1d5f3e51ae57bd428e793b071c1bb1240a7e3999ca9e9f57eec805de1c6d99dccf0a345e8c1e26780f32be0f849a3390619a89222be8219617044219a5e20f6c99c2024ace24db1f5b1c10a597fe6583de0d2657ad9024e0c14a2757f0ccda2329a077313a60aa4707da658e6abaed10fe94b078c625cac3c0e75b1be553179c424866f6e8a86e924f6c365abb22f4bd80d0b52d774ad757c231fe5e461f46312f310a37534873ba75162f6febf89aeb0ee1304bcd2f63284d9ed04417df1f9b5785b39b317c565096a7318abb0da0e264e77f2a28b41093569cea1a7b6001e557a9f540a3f26b094a110d0b94687924fd1dda3ab59a3c0138beb014a2ce2a39e3a88d69d98ac567cf394e31205bbfd3a7b406a928adcc6a604f19a30c1b06dc464d514f68029c2c7296a10ad178e9caa7e3202f2b58e3488a643e67b505017df4d11b81647b126653963c701e645cc112bf9015789b4ad71e6eb450e30a957ca0bc1cc935682503a6d51c79a933fc8c11ba37a1ee0941236093081394ad414e3bac384846cb7a72cf9b92e4e102b6fcaa288de03ca052c1aba40282f219f41b43b95d643d2cf0c2c5cfdb704b3208408f3fa3b50332e06d16ef4444bda02edb258ccf0cb8577824c6537389b8c4d0db14aec3299b84ba2165ff4bc4a6b98efe2ea6a38bc84b31f6b3194816b6bf93fa543a2711504cfc41b1c165641e77a718f310967289107bc231444d3c921540aee9b7ac8e3e9aca9b72ec18c266812a69834141bb9c70b4c99d911961e7ad05e7f28085839026f4f780f808fa168b9e236638584209aaecdb0eda555debbf4253825fb0bc3cc63502dc854cbf4341b9fcde7f2faf67070ea4b4b017ff10a8f45052e50d416b58a5303fc6c74556936682952093f31f119ae24bc07e7ddf3a6c2d591a85d96a0fee331cf5ab0ee6e9a30abbd45814d9790cca5fda88f586fa09568205a7ce078d04c46356dc96865a1a15c3cc279794593521988d615582e0c3940a9dc9f37927f0fcdd1aaae0960d36df722dfcfb02ee7ae0485231ae9af86801ce4a7e35f3d3c6b668218668eea9419d2581f1e52a3bab9aa42680e4b8ce2f06a98d7cd92b42ab46410e58d2443064c60e81200b38016aac53148acfc9ba6a9272c8fec5d3e852d0af0eacd018582a007be737edf271901d1ae642f903e319af0ac8eaab754e55de24a56b338ef2fea6a20af7e808ada3c78eff9ab3193f11a4a138078c66531b0053b691b7c939357bf3bd7373e9bb316934e6f00d13a0e488ff57a4a3f68da70e302ac956ab45028908d716a3b98bde9e9444f4a13241afdc42a457cfaf7e4f00be92e45b5864d6785102881d525fcef67c7d7348eb8a0da8e415f6f22cfa9e398cff8780d12af751a08ea5ffb8dfe8e747f2d6b883347f8fc3c7a29b2d9b66a59f3d3a8cb3649456bc206d5e301c822b7be430de4ae6a7ff214d937cef4718477e252fe6bab45aac5d1872ba20a0aa53f171158852e3e1959f2ad3bc7ba5b63cb3622a8f996dc66b5b71cac32dff3c363e1c8ec309d586e5e6e8144894325ea63836d56882ba1553a1c13e153ada13b49d0adc49accecaa10370d5bb43357c8bfb8045d6c4d105b339f76746d297d07ad8f177dad9f23596a6ec75b74f6fd692bc95a29fb3139bb200c550b553a8e371f7cd1b06b33f5e308729eee3f417dd08d3129c9bfab52d32af84e57d117aac2d839422a813d7f68471cd87d322d69c729aeab06aa3f56512d8e11d093bb84ac765670b94740e85e656168a3a6717d3aa9d2d818bcab86abc2f3869f0c563b12b048054d8369f16a08ead9d75ca920c769cc6a7315bbb4094ec817622497ff1865c86224f04f6646f899a13e8da9717133ee6c6bf830c3ab4fa90f328f55d8faf5f445620a9ec07261c74b05d34e11b4ba631f5cc14d5b1d5def056d978ac35a7500be44962c22d25fb878d333f6c13c3c9c5c811c220b83fe976fb2deaa8d980535cbdb565aadcc907ace4f6121fd6075cacbe7ced5bcac5c7cdf1b6f1230ad67771ff749bb994dd91c1f19eec37d33355ecf6ca51b49ca0baa9906de2c4f2dab6704f74d9e1965f9aac5ac4659a5792b1cf6ca60e818e8d23de1a43e95e88c41a995c90ecc0ec22a676548ca6ee6230a153217bda9dabc71c1ccdf6aa967b62d7a8753cb0dc78c835b3df663ce7d700be6d90b55f4037eee03ea2a1a910f2e511c1da3342727af92d5fd7ad83b86fb6d5119d2eb6c9358ac765528273b18d831c370f8c3e626966a13679469b41d4ae427b930049649565a17186a368ea679e37c0fd74c5f55eac3641ca1f2a0c125c37cdd22f12dd3639b7674cca3d0edcef440ee648690a69e96c9018d3c41f19ee6e6ef68dd1fa71c7fa7fd6a99cd04e8e66172e282bcf9245f4a6a8c0d5840b26c57bb6e243b95e9395c79be7bbb75c47571bb83f8fec0de3e0b37c33db2188838ab67a4d388893d58b061aba9f0721ec167f248cb02873e77d59752682733eb3436343aff1a76ae51487accb15b2c6220a157b3538bf8048c8b1834e9ed50a6415ef771dfeca20241815efb838331e59f63fbd2efc3dcf618755b0e8b6d4d0d6441c5dbfd969bc3adf3e1b8bfbf5fd4e212fefb093474ae278e7e9e46eee3231394815e0e962ddc696417a9503d8399310c459f1b1eda2485717544a50418b3174954d5b3c0c30284bd8ec86055be15949821fa22fdf7bf1db0f24ab324ff74b4c9e92b623f8f7ccf1759a534ed63a88a8595530909456e571d3bd3d7c6505b28203ed41b341a5ac67fc4fd82c184ad88bf903cc33adc8d0339008c1f2b8121d908195d2569e4a81f601a0d123f1b215905a717c9d23cb568716b0b7761acb2603fc95d4eb45543e14b4c295836cb48ec97b15f2411d5e1e285dd057e3636def3bc225ad757c23362aeceae3f7e819172297f8073457bf8c4996e52790c0ae5e361b6d563b74efb22c153de1cf75cc075856c86c40ce71bdc9a3211d19654fb457f06c64bd34d5e0cf30ce0b224a22328d74505ef2590199fa8e894d3b285f81384428932b30a644411ee61626db9ca111369c172c6fb3cfd09372b6c884a8e8cdf6c3c8b6c80800a9c42cfd4600bc0817079b61f72312d5cb32ab465a5dd8df1e6f13e8455eea31992ea86041188507e92cc6e7ac90383a25c88741d19fee86760804340439e724b4736e90879cc26e3117b1b96bc78a91e9ee562a0c48012c6f38f0950f8f2797ae61acd7cadbfbda4dd651b4fcf2ba87fab9b751353ad772027847e67d033c4aaaccc05daf4f8a736776bd890c8e123ef3905d055d072426127a717829ea04fd0b62204f25e12d48c57b13a8e7463309c83cc3c6d620413e95dae059ce24692a98291891b102fe314f1352e0fd91499aa48f2e8b444edcdaf736277db2d09f80c98adae794d534de325101097ba05d739bd7cd2899583b67977487a7a6f3e0f13e25682760dfba50295dd0fc82ad3095a76292056a66a8c6cfc802019a73819e0407164aa60b27603e3653a45c93407c691a982c9da0d40902a3d0f683523fa6cffe235e56b3f2f92d4c7f77860437d64e30a09f490a5d7c70f39db4b23c8c5a7f37ad5935b9913e2e755e8dc18477a03ffdb931bea03535e136922b1e514bb0bc97bcc948a79c063e274a61cdd7cf4dc9bc1431de207f08d7cf75c938148886c47e9e988c100232ada72d6928c3530ffd85eb6eb0aeadf4a9d7be23039a88d5eb8aa6707c88b0f5a9067ce15cc62905875cf42859b1ba74c2ddb36dad5c8622c229ddd1e4174f239031037e9a7a4e0a3947d359292c092387804039cad6321936c9a8cdc17d993634817d95c1a5e4bea3bb728e60da7da5ad46f1f1f5986542c84eb55328971250cbd649408bfe9896314693c84d021bc7da0666b257ca0b1cfa811478e5948dfd1703746c30cf117f848b40f01682bbf7f3fc7b5c5c16919a57aeea9e124a45418e148ed721ba7c582ca6d14d560a9c7c71ef32f8e4f9c9b7bc9fb99d9174c45bb238a879c75bbc79fc76704ceba7258af664177211ebfd66e11c8dfd2953b87e01181a17658ed3576d572ae5b42b7df366755637108d4b431dafba62aacddb46efa2bf82d4c48559c9347305823cd5b8324c56c923b13bca18b6e1ba1ad6347ae221035b9a83a5f27b9bc1ba2c65b8aca4d36d644d22381c4baaa8d0c462439652df63e96449b96923e70c15e43254aa89baa549e784beaab4e9061671b648d24efe7b3b5ea49d21ef40eb5e5059612b820e0b89730db20ac5825057eefb25b86cd2792eb3275ccce960f8b10b0bec0020dcc93610bcba5a0acb5e135c67f3978570fdd739a0fa6343b5a43a14d26e4ebbae0ad93553dc7a6695cade9f034fa3b15ebc838b587f78217897327d8124da372bad0143fc0f063d3a02f4548343df13c5240ad4f0e0a6ccf2875f2b7a18a4c90c333aade36b3596af339b287e90f6ec749941718019b0b6dc4ae7ac0b968741d6783ecafec30b8a87e042a6ea9f53630606a1a3c4d6a9bee5aaf1d4a979f41f68c40efb236b0fcb172ba10fde5289ddff7ed50b763c35e5075ecf6449414a15bd606d616024ddbab3454f940a64b958acd85684b40cda3b6c5245b38422d7a3a54a08083271120e65fcacf4b782415c2d21ff034011a120db5783589acc177dc67694864bed5713e4516db58264657a600d2bea9fecce391912439e7b2fc3c3161094e8639b46214edfac6aa178b49550007611456b0a17bee73c1b55cdb1ef155fb7b5d9ed605964c543d286719ca4438f2071480450bc0c1cd44bad920313ecd3f70f6f8d78f2a1c5b7bf2c4b23bffcc05828a5489ea0a4b66e8190eec9120e7381d824cefdf9792495bf7fe1a6ebeef23c6af070b56b5e54cde44b7e507115a8e4f8d1d4197fd2ab7b8795bf701831f1bf0df21b3beddd2f470765a529a23aa8c4a755cb3d898d39031065f6e2bdc9c60894f12d1b79e4f13d23e79502775abfd15d54a42c59c48306ef79fec4ba76b191f4f43733fca00514d6267367a057c42e5578bffff741ffcd0c79201cbc0ce3045baa9f4241fd14809d5f32bc178bc709b6c7949394efce4b93448408059bd4204ed5cd48608d7287e692e5e8488765302fa504ccd65269f541c01086142f4a715b6a1f2fb1bbac3366d91a6900b3750e681f015256201e1173a2e6dce48bb83c114cef2bfff8c564b3836dbb0b59148274c24a041edbbfc4ce336e9eb51391ab30afa1e62d47dc11db58aca8e872fa126d13eba3729681e5cbc4b10e5faf32a7e84b8362d155a7a16f27af5750e5d223e4a004870409f02d4255973875c533f4a78b0795e813334319a7a3a4f36880a86d46f99b76b1814803f3279343814d8f980e5513038ea0f0461d9ad718afa5dfe8554e34711a00621ea67414797fc1898a3979297a8bf04e502e119fcc73353deacb4c6e910ef8ae2826767fdac30b9f4fb26fe93cc1512a0c1c0f055335f1d1026ab425e80edbe137c42759755ccde3a703fa7f8fcffe0eb0f9219bb9d2d571f9219b23711231eb48d6421e3e6cd57e0b1fb99473c415df767618026d58a73cf1f90054c681c31988ba9d4a013e1a1cf8eb9015a32c62d117db16f3e3c7424b52d8f75bd54b99d9ffe024772c10125658e814bd67e8db2075b7f663cbeca7b8b96573eec67ecc241c2c5c7f3c1e837c2685b5e733cb21201712ed0d9be67702c5f56371444fb792f10d8df6cf3b28b55b950f98123055f97c039883102a48760b25efe6c70c94ec7a020b252fe3ef5e5ceed9877973f1d7d17ae1e301dcd44822c72128ebcf149f57163901081c0e5a47a23556d88ebf51fec757f38b87fe6f68cc34bd29bb36fdc77c19e01feb061c8437bba8ff1c1c9cb8fe77d6b0bf9dd933064f2804c4635e6c7f36afa1aad138523e0e8c15d5bdeabcd68ece9fe4c06a4c98df6918dfd657e030838eca2e080443832b0703d44e454e09d0acf0d0df27e43e3f9df0a003f05d7b49102d63849de00803d2527b285d1f4f9f87d2000647663846af838b513824ab2576ec9d8e7100f1745eb3ed4fb4796142245105818d008429ce040c2376fada931a490f225c7297c58f0076a00f88a2f8c422eab67fa75cbfc4ca40be363d91df1f92d49b7bef20114aab86d3fb4c91852e833d73e64c489e44fd2bbe810f1d534e20bd7cf9fb276d0a896f5f3ae506a84d5f2831ed0c9de277079d310440f9cc564430e97a541df9188fe49b9abd8a203561e77022b6dd1ff96ed6aa883aee6f3ee00e132fdbc8c4c4ed23b5e7a1f052a803b2f052106d87bcd8940e7e687cbef6e19a72005b30762d84e37c17fe121a8846dd9325ee8df5e60bd5f30c0d3225306ff6227f06d84218debe3ee16fee70f24a9d0097a4913ac309f619832ded87858522f90ae60a84e01f592455ca38ae13ffcedcf3f14b3868fed29e7c405264d143e6704477c6824aeb2cee72d7cb8a22f56c9922b4552777833fe331cd917635534929f51768dd055ba8ab053eaff83c8265dcae994cf52b99478b471e97ebafad6f13ee52f677925e28c1742cd36baf0e61d85658e080cf5c26b86363a5002a4d535da0407a29ac5b5f8b4d730632f76e0c8bdaaa4873602bf275606813f53a82552aa44eac4bfd1a17d4639dfd3b8189643f464803781640a224b54bb3f0cecc8acca6c7196be52cd47bba012201af5a42e40d19e6f014617aa3f6306d8673ffcf12ecfc3146676103c33d41775b420e199f9a5ccd9e69f56532ad56d2f09da9dea36f59206d65e1277618b9f978836342374875ff278439766893276c298271612655093bf8df19fa8f5bea42e94337b39792813be84f84f6d91a7c22f52a86eb9605871f3d1a975ea41f42895826568bc51c4233e059792c3e9daabd2439c0e343543bef3afec0dada24458e63e369c6e42d14784b4d3d0f1a801d9c1f4d8d9d0f1ef112d1e96f5a188f9be74c55f00b8cc31466bbc07405d72e5f0907bf6f05a29c7c60e0f497f7ab2342f9d14dd82bfd0a7ac63767b27d7cada2f29aa7c7afd3e4a0745bc14863c4e973e39679bb1d016d8fabe7fcc47665810663c078ba646cd126b2824b5f2448f194d765f22bf6116073980a984ee51966eca29ddebdeb045b9d09198af0f4b427180960e4429870cbff7054e9ac0118d663e7d352393243698f55b613d8e00965438fafb4e9f2756e80caf90243a48d2b064ad4d9c443c77e05024cf84a68bbbf69ef0241d7f04fa566cb719ec6c99005a9ee89032eaa23d93f105b0c1f4ef96d83d85998efe6918bec970e158f387015843fcbe7d9fea1e748ed5ca08c06868f6649363d86100074b6865f508af811c94247fd87462fca1b5c35763e21410c705c4b1e07cb081d632943e7c1e3d4f2edc22d698e85ac112592740e03c469a7fa8875be4d92dfdd008549ff60408f9866ec4c6e9128ba031c18285686c5ef6fe31f3ae5dba8b746b7d0ad7b2f7c629fa0312155759620ef37f4bc6c349c6b46087f91fa31a4fe51583f1138c7908a344b937d0c900f058aac9b2dcc12c4e90c093b5630f462fd1a8ba4cd5251a9d91f5982a24bb3f9e8b42a6f1d01401625535d7afd9938fd84ad2701a86c81ad463c49b869012f2c9655a7a22d8a786a2c1748dd295fc289ca0ab2fc0f80e16956048c51de8d5e1720a6b4c2ed0a824b55c7f1bdae0cf278d490f0abdb350c848b88992a9e456793c853bec5d0b3623d693f65e9d12d0af4fc2519e90b5e978d3c9ef0a28649c16f6609169ddcc044bc877e50e9102a04c0570080bc99533890e0c2fee72fc7bd9f1d2ab90d079dec7fb12b43a4a0579c7cd26be8cc968659dec653c3132d026391bd139d9f9efb71baeda2200c77539cacafd6dbc9d3ff142a6940b87212ed03900e936498010f77580b62b52e6e22a2b74dbdf09535fc7e1b2c33d454b61f7fbbe9254b46b58924427e0e36464f0985de1b153adb9ecaf06b007a8dc6c5ab86e7d76c266a428b10ca65faaa1127bfbbb8566f29a697fc3178a3a9938d202ae7480f78dc79fd8eea2d261a56c17d609baf4fd5195443db72a9902abd4f488354b660eb722ceee9cd58ad881485ede5314cf48955a82d44392d8b511dcd5d7a6726b4bcae7825808c704e8a880c892f607543711c43a360c747b486899a22a731940e582c34c6746e476873e5d6ff2463a1ad7a4d266b7bc4a63843701e25d4ba789ec91073fc655f2e08ecbb91f32aeb746a37e1eefa5347ec160912223fcedeee3175728a220d08b2c314cf54569da8cf3f39032f5eb6b275c849b066f0e0ca120c41da2907fdaef2ef627e0d12834cd2756422aff55bd4bb116f5f0dca276489db87550b1e38ed98bdafecd72869bfab0eaf7d114eef5e337bd7c10b9f2c69d6d764bc7906e421edb6ed9c9ff06f58e34bc004901784489a71e00ec1d90b72819d3b55f47922a8ccd7d4c9d4f7f36fa2a196cae1998c99a9d123743b57110d0e3fb0c744bf894287a627230b00a5cce0c0db8add0b690d1862101b07e219f1e4ea5eaf4ce0cc89f65f88f078c974926819cd36804291b8cf62b69056c365ddf717f0864b46f3c7ed7e614af9f295c9fc7fe0eb3e2101412cd38fd9ebf95a7750c7ad60683a26f876e69e33a03ee7887e4e2e6e54dd5d048244aba73607e410c7ec899cd2b7b50d51f75672e90c143c63f575e644b1d6913df6930e21529778b35ec01743d95c099c76f14ed0ee6ba80b127a25697f5fc885cf6f260d299884730fdbe5e7f4b51c270b9699b5dc5f4f8eedbc5fc85d33cd343d455c3daf0300a6dced162ca522b157b3d90c82354a9778ba9445d89ded62704278e52beef9f95e7ddd25aba3afc7e47f3ee878de91ee837acab605771f4f5b72c1afa40b76bb8635310d56a2bb07a99820bf686a34e5c8ccc230c8b4a9b91e46a096dd85e16ecc40e97e19f0959846e621a4b72e5dd38104012da037c10d8c2993a94960c2d60b3bc8d8a09de0ea1e7d2b40b26c2f1446e7bfe41aaaa1878e1da14fee86d9fe51b35c719e1de5215d78868229575f5fa3b849c6fea86a2a04f9acfe106ef670e0459d044fb510424b82d3fa3ed586a052cba5356949103e945cb4da21c0ccb16b6542c2c382ef5a2935a1e9fda1451ad5907f10486f4060afca2aa4aa0aff201a49b403454d7cb4d29802884986baa3ab96280878763b51e197ca4c659d704327dd8a966c10e969ce02af81411f5968e58490de59e730ef83dab89a3a123fec9cfc8af360e27a961345dbcba8f78e322cee41079ea15628b1c382e9f765e17aade169694353d0956d87b0387bf9494f6c3881d58ca9b9558f0bbb18082d4539b747675368823213d496807fcce060c3efaa8b5b5f6abd204ff9edff6c6a1c0584908cf483165a98865e785fd0a96556c27d708bda67b057929d6d4d6913e9d64e90838b92a86addd526a9fc0135d4c162f216c22c0779bf7b0e0086e014258185b8c2ca2ddbc29af6c7bb2f3f39fc5e77680c6e0604467256477245307b8367ea480bec0c23b86a35c937717f30d3dee87a46283e75f5f3f78c74071ea2764f53f66b893f123412be41b12f8efcbfab0f48dbbbdfbba5f899d0a27ba656ab3d4f16532372806f5197b9ee60dc89b69711f88997c8c745e08fc4d9eccef93a7cc31576ea9cd0670e2e017ac84a8b2aff23b3b240e54e27046cd469a25cd2128838e8bb54a218ab7a09e0aa409576d8de1d68c30228151b86ae33a215674cd059defcde2abd498c71fb1a9a2a1319776391cbe97163fa15232e47d7c6508756b25eb81563c12b17a3477eed186bba073571c1e766e54bc79ec58a23b7a7f8d611ff75b081659b59a4486ff4518f571a8123c6545d17b200b75618a7948a703b46f375837b33f3c810b53758ccb5a76b2859709bc23da25cac032c4153fe1f3e1643018b41e733f285adfd6c392ae62d444f98100b6d22b8c3240d21ace2d1f71870eaad62cc139208548d7739920055a703de7bb49818faac984b8874c9dad3e61bd2414f95ea476e43a4f2236a65511b430345e4eefca155816af2bc16229f41cef905677a5e786d64a790bd1b2ed61698b4f92a040fcd5a896a2de9349739d5b6ad2595e028ab0ea570b21ca1c754ce95a6dab084b4a70bf18f2c1953d698770708b1c42def9becdcca4c06f3357205460bfcd901a1a0979c4dab3bebed5bdf4bfb2b7dbc968adfa08879f3cbfc96a285521c4d8fc553eb31dd84afdf382b4f5a86dcb40db0adf644affa4387b996601d657647be5d384801ef5d841da65c3fc065cec94b333d5ad8a03b89db3ec1c4c21232bfa899cc079ff59df4fd78d7216386b88e462fa40c8ba7b4a30e61f0f0fc8cc4b73cca3fa4eda9a84de6444ec58e6e338ec1747fb15ec40afe8643d325e3fd6073017f0855ab0d994ff728fe242112aff087133da467a437f29c96d0f5a9c7d5f3e158a7c9384d10f4900f015dfb1096500586da61f10183b49188971c27e24aa620a564c2128c828653981fc5e9f034e52d9cfe6c6e222d50c0cde28d2206d19f723a42f88524e7b98b9832c45bc4194ee070df3d9081639f5b91a6811e9cd52025d2d259720de8c4529f311111d911e2c0a6bf4d5294e2841b819e5d11b043d4b0844f5d6d4708901197b5804f4756917588b02d97a18616f43e1f88658cfd8a863a01ec49bc3c8c833290876039da4b4f7d48d68bfa6140a6e4c22f6503d5b15a03bde980d07cf2cf6a4c6cff391abbd24d494e7201feac8c3c4668fcf9565a18622682be0884ad162589b06f95c288ed290569cc527e5425e31d2a5bec58b53cd7ef507840a4162e3be4184a3f496f75049f55a3491cf8b1a1ca017699fa910125cf93eeba2bddde17604152f9d368bd8cc334130f94bbc70f88b8ef9178332b48f8a4c38f60c21778c08efaafc4850e0b819e68b84106e6b1fb2c99cecf181dad6aadc3db7ee7a84efe5d0c9d42dfcd1696a2b7d0eab3ebb258768ed961395576d5e84c958ca2112623875b7d998185b5844901b6a1609297410753f91e9a94361cb1617ed19b9f8e272710a1ed0981102a04540872ce7765c9db290be76af87d0efb2b0aa928e85a54be50626b6181b9ba7526a7d45e4ae8aa828d91dd917ad32c319e5a0850abff1c4c8f2d690a0fa46d98c299e91131279ad55fefd96a6031b728589c8df4c3e097568eddf76c40a6cad256d3447942e4b0218958d2363b3c38ce5b6020df4ccfa1ce2fae3892b343aa84251d1ac480f4d218dc481b3d23c49036408269c428eb5c399b8696bc079662b0c143bc562840b41829860738d3ecaa3092aa46b772cbebcf497101da277be91c042174423a6a7538368083044c27453a7469c5b2ca7c35001de8b6ad60d2abc0b3015a1a2aea46b71af2f0509e61201830f9aad4919e75c0e9aebefbf58ebb7605e942c6b210cf9129b434b999f4074a66249975cab3c23e9dfb30db820d7e629bd8d14adfb0c32bb144d2d2fa066ba613c699c469b90ace8e6d38040815ea69e575b1bc4de766023fe3b2b842a1a7f77532539d992f880725cf03767168c21ce1f4698e0cf5888c255a297731c9756795c5cdea96f97e6f28a741e310dd0e841eeef5debeabc71081ae5cc119b4484b5ac841bf61cb3da27b553f1db4968ef64379c46149ae56159243910f9c30b14c9cd1e7196a4d5bf18950333e8c7622a82d9293bb4e615aed431b40d7cb30132fcc8b4fbc68b361599fdadd228fd701ca3f44a8538487150b9a543190867d0c3b91bceedfb0156f4b020500a02125aa8a7766662a41a32fcdd3bb21a58d94fb69470f37f7e43585b0bb5450c0c9da0b88e40df21e3a12e1b11f17e223ef98829854290c5eb9fe265f860c3ec79ebf78a6921dd743daebbaf151922babb4d43fb80164937be03651dad2692c996fb81bd8ef6cc7b697d9dc47b3282a07dccc95014ebd3749bc4fbdeef468de8a5e9653a27464b4fdade2cf8daa1acf0df840266549205902376a65f295018b5aab2637a5362076aff10cedfd0d788b503bd09413b83b476e06510b422d825c62c3870dfa33aa514e4e791f3b64e293f3451900e98c5807c9f844cd57607d89eebbe71a70b85197b206ee6c68f243809488281bd00ee03758334d8d1736c74e7de7c46c1227f22c4273ab16a1f4423211550f8e96b7265af7359478ac550937c86cd2ef4f3cc2cd6618451f9c5e8c01a879223118ff7e499711cdd1f0731b70fdd7b5739cd9897498f716738b038863a50b25f5c6150c27c5536615ebf828578a29d23adcf516f836e14772f93ee6bc694f69a845f91b3bd81333c97c907c8c885822518e06fd035ac09e05a7a3706d62aad3466c923578a5746c79041029f45f99f04d4fbd003bb6805de9a8576002867b3d4d4a6af0197734baadd2ea3e903aa492167acc9259f227f84116e45d529279031316f794cd529db374cb3e59917da750ead0f3b8e9b68de0436595ec1a214ff3d37dafcf1789c65998c049722c5a4d95db7065f97b69a7e5dd57ad1e6ab7f8af21d4d5f5dc042aee323187c7e4f1d82ce11472fbdb1e2f978027dbd16940dbdff59a1eca22147fc79cf57a163a8e877d8723423e27e6c6eb13fd7f68a5ecd9902e38d0b9e6866cb01c3bf9ab89bf3160ad2dff4e9bace58f929a8676e509a2bdaf6ff9fc27a8b78971447af602f70e669ac636e62d715b541017ae194964ae5c2dd7157a8572f2417c9b89af50bb9370e87986bdf96b52e2ad0469dd3db86bea32df576404da3f3c1f5a085f7be512b24e0045e11393be74f98d2660cd5c94589360f152af1cd8a46e4320d1e885204200994515879d601eaaac3fe8c518bbad5b893b481fa43261a3cf89e14b9e3b8a928055b8b53ceb6b5e86bbb20c76ae3d9cdd002078952ecd4b89acfc3465320d6c75a7cf815dde49c3cab279fe8624f8253f98cee7519998059f6d514bac27adba9fa7b99ea998a6ee41b3631b906c532d7be0fac9f2ed29c31c99e9f30edd132f8df2650e7aaa162d21e2c1e4e2e9ea81c6c085394f7b9d706adbf5d3f99a0147e9ac7e4070624bc6add5b9f21eea422ecc48ae550b27ff94322d174b4e4ece165ba3c02b75775a746ebd046b870be555d1b636acf23b9ba4a5d7fcc7c297fb6c17356de085ba43c28208d1aef0af1488cbf65337715646a7f23381f54024f8bb9bfe387c422362ce92af0e2e2de5ac1a84f75137cd7c66ea4052981ce51a173afa843b8e7dae610622f50c51756e567645d189fdf3a5fe0fafe1d2c56b11d531d62679f10c61df024814dfd95524789ccb24469ce1280760a9bd26fc0754a477b498fee0a81c53ff1cb26ecb5f33ddc867d0fd12a41c787a5c5919e0f25cbf221d67be6a12a4b05adf4dd869befa1ea453723a4dde1e3796f2aeb3418d33208b9e8923bbd6a925ae94504a98c48b7d6127a84cc4141a5161136bc54d0dc5069af5416d383a8d12783016990d706720707f85e8b4aa178a726328e9d5b003468caf3b8cce020dae4c7196cea937cd1170f3a4049db8906257e6808ea8e0cf16051474aa6d5db0df643121c991cbfa7c0291f5591b572f3e8fe36df0a72cb4b6d9e69db7f32a1b5fa21f30a99e7742c2ccb4a1c850e19cea7e11c394c178d5055d15c0b490cd15980b97082a1026438dfdf962f14b49edc5654f3ab2a972dbbd0125e324691b711cc37b9de1445ae644f02e4a618a70248b3232afbb332b58508b7068734f523b6cf7a49211e2be59f1cf3bb2ca8b77f891d76fd3fdd820ff9193a7a5bf067e39d55814119920b19a3a8fdf5434fcd901b8565257e60e404ee02617f3d3565bfcd90f0f16a3538d20c002a7ca78580f645b75514ea4bb5076de34a1a8e05657a89830f40db422bf921d38aa287489a3250d922ec8e620ab0414d48cafa09e28f54a24ba82ba30d37174ef53ab0249f3759cf608797cc14c1ff88be8ab0580b8639eb8fccbe8d4eb298cdd5c6bb4d6473024af389685110280d6b3a39d3395132b120482b223f1040bef91ba9a51f9fa287a49ba5a13fd591f6bd88d3743d68a7b9300a984e0790aba471a66ffec552c23ca2fa118d6a5b987910706cbdd69a486b7bef9da44c291e06fc063b06313186d8f7c3c418d42cc5c499c730f39eb731a9598a7df8b11e91490d8d09bc813071e63dec391613675ecff33c94f0610fbbd94c883d6ce6f5d82eb4338fc77e3e3fdf6d5e4e9d836a937312a15ce0d7796b42a75befe89cec797cd338f43c7e3d3c1ff2bc4b64eaacef1199d42cf57c90c8d43dbdc5d455624f55cf5550f3fc27edecdbce46a82f7fe622942acefc9bc6d7ff0c0afd7e2ace5c54caf23df84da38b09161969e8c811f5f36a967c9e3ea55f7f897ecd51a7599b4f4599a37f934e3427f4d3c9c70728eba7be8f8f9028b77ca65396a8a5c71f91871db25ece66f5f4fc4d6e053d8f413e7f9b4eb29757b336e98ea74e198c424bf5652293ecd52c057d159964a20cba62da216ae95148ac0a9fc7f06fb24d28cebc6f1a6515cff3c823ce3c51290b4379393983d223fd1ee9f37cd0579149cd6c845a79ceaf6d96ea07894c136ad6dae480ac07e5e5666dbe8fe5599b50558c21776380bd3883127bd8c744263533a5fa24f0584528de5698383ef85085c33139c9317c2cf561e26c89be12cfc77efe9134421ee7135c94d8628b34c418b05411479f8a33a5faf4617f9b57777a5ea278ac17b35ed4accdf7aceefc9b0775a78735e57856334bd2e64472f3363c6b722a7cc970b3a6356b73fe043cdea69607d4e452480bca44dff305439995eefa7759647fdf161b0c7b4e203ae1ad714ddabf67ffd30fdcb3b945ee98bbb9bbe7c82f94f6b21074a3adf0e94d2ef2e9799ed771e9ba07ddbdfb29eeaed5f99c1eceb1621bd778e6bc5c18267415f8caddf89c2012286bfce0d75b1d7716ecba06251465a2972ebf2392329162cfe3b96e0c24047ad51b912bff25b6d0f7babeeff55d650c14bfd7ab07cc049e87895866507a847d8f30d1a98e3cefe46316d86b01c76623dfc76c8f12b6e4238fa885069e11f672f45eeffa91d9c8f779ef1788e27e1df0142d6575326d3623231aedffc5535cd77b52dcaf0396a222287a9ec8ffe01b0aca2370ceb682f2211441d99c70b6d4bd7f27fe84e2d177bd7bcfe36ce9fbee3fb1136750bed777e26ca96302bffb9ec7506acddad6ace1a6d3cc9a35cb238fa0e5f1850b5fce3557de239226614888898a614191b34979141027e02c9146e825411b13fe98f902ce0e428408792e4fc1c9f2469d2ac8d00a3206196974e9fe66f7c7f31596a5e5237b240a595a0d4ac1acae7a66387966e5b1362734ab2bf975df045dcaf3bcc5988d62939a6058c3d8e7dff74d30ac611873a344886be8c8a51131024274a402b18542624262f2f2e87a8c6c7f934878a40271a344488c687e3c5fa50dbdfcbe3b12abc2fa0eecb67d31900868439a529425b3d6555859ddf7816f5b625465456330269ecb23ea9ec2604cbaeffbe6ac9d6d09cdf57d0814ab955e61dda615c6e4fbe073a254ad8dbec21ac6c21a86b1ef0b6b18c65c9fb4739c9ddd765fbbbb7bceecce53745dd775b3737767263289a6cfe95ee7eeecd2dd6b6766767707634176a89c6efb6e1d13cda75fdfc9cc36afbbdfbbe77ddff7cd2c14fa73b9ac9cd057adeefef96466ee84be3dcf33a27d9e47f43d337722d3917ddeed664fe8e3c3fc7d9e6ce3fbf9ece705d5cfab554be8d03c2be4791e1da5363e77f7aeebdc3bae1a92a9999e85c215d84c347b78320fbdea6ee544ea935851bcd9f9fc1f9d4fcff33c22666666cff33c8fdff3c0392de3d073cff33c9b91e7defff03acf3dac28d2ea63a2d9c373cf9bdecf6fafba572d71b7799eb7867d8ee7795ef53c4fc9ddf0bcce465d22129d7be7eedea921474ee75e97eb72eee5d49023a7f39c1a72e47877c7ebbcceddddbdf3ee4cfa72a7b6ad7aec2a3933cac509b5867b97f32ea7061b723af7ba5c97732fa7061b723acfa9c1861cefeef8634de66e29bdf33ed0f5a2151663201893ee8576b88e3aa52831295a563c55575655c5b2b4d0482c8915e4f323036b4ce94a46c124546df3925268e41ab906284868c7acc7078ba1c059ce63636267cd28a50965a5d34be2244e268331f15e36c4c34b7229f06f422e3f90ee8582e72098211e5d7dd14a5f3c916a1f4d79db73b7aa575de5790ece32b14e43d2e4c7608d85c5a4f301c164566214b22614d4cccd35726b4b6e493bb8de9a503cb69f62ed641b06eba86e2385a6d7f41a1e3c66104827336a424da85966464da8324ab3165454b7fcc5a5eda6bae53797f65475cb5e5cda54d52d63e0d2aea2bae52e2eedaaea96ad2e6d2baa5be6e2d2bea2bae52d2e6d2caa5bd6e2d2cea2bae52c2e6d2daa5bc6e2d2dea2bae52b2e6d2eacb8b4adaa5baebab4bba86eb98a4b1b03d52d535dda5e54b73c7569bfa96ed9cda5fd4575cb545cda6054b77c814b3b8cea96a7b8b4afaa5b96bab4c5a86e33b8b4c7a86e31b8b4c9a86e2fb8b4e154b74a2eed32aadb2497b619d56dd2a57d46756bc1a58d55ddda2eed0c54b74897361ad5edd1a5ad81ea16c9a59d46757be4d2de40755bc1a5cd81ea96824b5b8dead6c8a59d55ddc62eed35aa5bd8a5cd46c7e90ed4366ad835f937effa34d28fa113dbd857fe38f2a3714d5862608241022f5c89800b10b0f28016aa5071000b2ba830e5968294063060010a4800020e8082010a70020106608200a204000a00964a20610425114200e183271e74e08483264c36d060bc4b32c0e0022549922cb0211d2139520105468cbabbbbf316a28798b28d36ba8d968ab9bfd79d06f6647ef77d84950662281379defcbee979a02cc2b48e91684bb848ca2ff2dde9b74adfb93fb826ff06263523c9bc2542cf13c3252c9b4598d6305b9df0f6dd298e725ab594cdb2a54b9fddec9cbae1b9d7759f473f57d7d53bdfc117ece592b4a63d5c84f2e7cd8a48dd6354bee47c5a292c84d5a758aca7613c62b33b9f863c3ddf13fa589f9e1ff1c7072802a09f20a2202021219a50d08e1db51d42329904b21d43434386643c7810e131349b15cd78f4e831418f990f1f457cf480000223087cfcf861e40704ff14fc0f6b2bb02f8a47441b410448221089888e88220002040908519020b620408408b14048101a2d8926a4564b52a349208112096a43865c304402cfc31e17ea2194ef44ba6ebe5b90c890a20c8a883c4d2075e70445628a4c51640228a30b1815f912ca77235d47c59d6f04340a75a0c0df29303255010554472aa802c991aa232456201d5d6143c2c2025b164916689124690b2549b8b8408915061774910106185892811777c99bf17ea1c108c6061a84c16483ab264cc4e0a0c9184e3820a30327703ce8a08c271e98f1c1933340f8002b041032204208682889a0811194d22061840d94400207964a5003004b595000b04600a0b0112500710410458e0902e8c0004c6883000370e304026815e084370c5000385030c09c03a0400701078823010890430109f0c00214b0c58005ccd10006d491d2003a529062e796c207a6dcea50610adf5941051c0b2bc8e0001666a0e2001aaa50c9d242951a1ed082162b0fd802012b4e2e40c08608b870c395087079e14a1709bce0050609e0c004430e31306189e1cb9d4f9bb0e8806bda41061c981964e0818619c264a1414c0d59a0b4d4d0c3162d639cb690b1c1a9cc0d36f8c0e506335db8fce0a54b140e5e80c80187209e7210e2cbd3103a7c39b3830e4480d9010d0f6072617828424c1823a0c41cd103549a313da82133660265c820e1439935667c48e207331488faa1024044b1090208258408c2024308b1c49921da107186093444349143e34411b9278c28028a238c88224de50976e823604de6953db8b0af144770cb59344111232314547004c91192cd82a4244a2ec020832577d46003264d3870d281074f3e00210411944620a18425004009401401983000029c500003a07000042440010b604003a4a4709ba2c20a2c38804a95161e6005022e44e0ca0b12808129062c4d8d6b197a86a6a1b3740dada5b7b453dbd0373497eed25e1a87cea19ffa4bebd03b3498e6a1c3b49886ea1e7a4c93e932ed439be91f3aaa81e8205a881ea2cf34118da6735d441bd147749a58db400748e5f3db7e5df03ba5ff7bf01381aed755a8dc29ddbbc0fefe16865ba1fc39e9eb6fe1ed34d7fbee4b6cc1656f54ae07de1c67b3d96c369b4d4a29a59457fe7dd3a960a482d17c158c5430f2e20ca83be4f294196c5ccae35364bfdedfacb838658a3f089dc856faca96b31bec9fe24dca7ee3ae053333b333b3333333773a5871d74d766766eebae7ae73f7ced9d9b99b13092396396d8eff9c57d8b123478e159e2a030e381f04255127250fa919d58f35c5c645b174834428aba494a449299e73cea9a933da14c599a2443690352997b8a8c2284a92f644d82f9334d9a0d1cb967064514654196ba8c018e2f6b84cf585d4a53c332597f7f425fffb66dfd366cd639a3f452699d734be8d041ea9c87493f9df6cddc86307fe1229edecfb28b3e67dd3e8b2b34f6c81041e95442627de38e37f5d89b3251c55c3429b20fff54bf3ebf357f92f91a9aac55907dff77f4fc599d2eb674bae7fbdabc78631f95cbf444a291ceed266ee1903c5f073d92bb3e671d67cba220c2eb25c0f4a1e71185b9491483d4e0e430b2fcd9def69bdfc4a61dfd4afc6c5a57e98c8c4d474a52a8a94ad2b27597c05fb259e87f53ccf7be21253d087df34de786be61f7b1ea59db9381b413eeca1f407f7d6746e3fe801d9858aa83454fcd0c595623b95e924ae1cc2ef6f2bb6e02e1839a693cc4ef2b054124d029f3c37d9fd6e61d79bb027733a7d2394ba231df71ae7b3ace5f4cefbc029394059baba653d20112e92df31618532d1cda9cbf61dea896aa8eb346957234e727d4f100c4399ec1f8b6fe7248df62f9365792e97cb45eb64e2ca2a9db0b0b0b01a8b7e7d02c13094b9aa8bbe948934f4f2a97c1e6b2f264549ffc65b208c8a4d66ee36f5584fd3794a79c8befc59a59bef6f9c45c5f924aba4d4d02f99435686269c35d9535de22cf62c4536e2a409e34002512130e8a78707f6469c247b17dd41bbbf4929100c4399ec9f8a4de49589b3cdcb29ec2c4993bf43149508c127e992469c249b8986aae20a893723d7f5af9732834420d187b3c450bcc9c498787b176751795d4fad1117b9c4998b612eb1c9bc3723f7c65946f6d64d5cf9df7472dd1b67dd8c48ad6bc4d7ec90426d384141444e6611071c4f23953858a08ed448679d39b8913a9809e046da51255181a7917a5a4f1880a30b2a46262c3ed6185922c122d4441d4ae046ea22838e0dcc31d217951719a81a29f522aa0aa79156aa3590801a29ac8a306f9c461a0be3ccd59b3284c88d3484b2c269a43d555a5983971bda8cd4a74a2b6f0033d566a43f55daf6a2c50a6aa440555a2639068e54165b231316a6630c183a46352278995822c1c27696787a625433457653278bab910a0505350b0d6141868badb1a9b1b49c5274de2a7ffe94190265a2e99dc8d414b03343d588e5cb1a418944ca0b8437f082b3366b73fe1499769041c3184ea38bb44e62ea08ac91ce9601ea8ca7715a1e5b76179f349ff2146fde688db4e1ecf0343a9189026a6434b49863cdc85ea8c1658db4ab9de53034f084d548bdda5926b6620b17592396971458d3ca664b1dcf160b6c8d1dd8d9231ba6781afdfb0ec8a00b041f9c48af07c16f4e127611f9a078035d6e6f4d13c6840a757710743d88f4025dfcb2409dc4254e9d51c575f166b3048d3450c1e1264c8479b1dcef13dfd288e6cb3a21e850d0f1191599e01675443ca1cb433e095c354a8309e1711ba4c72d901fb7444111ec1087ecec7dfcf80181f511410f2033213c6a6e8786b89515b9dd51c4ad9091ea3688e5d1e74a96cc9a5ba0fb592512897c7aa4bafd71eb73ddf6b80def74cb93d434a9b36c01172555b754092f91ed16bc605a2592e6d6ddce25d52de0a20d24136fe2512d0e40f75707b5031813ff396b375b22bcd96c4be4b741a844d6a407b127e107defd810f08402108892053e2e1a30704ffc3462012050122a4469380c890a22213185160a40224478e6c481624495282c10519dc25e3061a30e1a089130f3a7802c207212889304209242c410140000410c504020ce0040314a08b1214aaad8b94038049c182ae262500cc6d0164a634a08c0af24b8187156a2b619ab48069720aacc9df166d6156a86efb884b59a86edb884b1d50dd76119752a96e3b776995eab6d15cda4275db445cfa80eab6cf5c6aa5baed212e854075db425cea4275db415c1a81eab681b8f44a75db5197be50ddf60f974aa0ba6d3397c250ddb60f973255b75de6d218aadb26732996eab6c75cda54dd760f9736aeba6da84b5b86eab6c55cda3354b71de6d2a6a1ba6d1e2eed2cd56d83b9b46ba86e7b874b5b4b75db3a5cda5baadbfe72693b55b7fd7469db50dd760e97f60dd56de3706973a96edbcba5dda5baed2e97b697eab6b95cda3854b77dc3a59d4375db365cda4fd56d3b5dda5faadbde7269eb50ddb6964b7b87eab66bb8b4c154b79de5d2e6a1ba6d1a2eed30d56dcf70698ba96e5b864b1baaba6ddca5dd4375cb772eed31d52dd7716993a96ef903977699ea96ed5cda3e54b74cc7a56da6bae53a97f60fd52dcf71694755b7bc75690351ddb2072eed20aa5b96e3d216a2bae5382eed21aa5ba673699fa96e79cea54d4475cb705cda68aa5b7ee3d2ce55b7ac75691751ddb21b97b611d52db771691f51dd72072eed34d52dcbb9b4d554b71ce7d29e4075cb6c5cda4854b7bcc6a5bda6bae5ac4b3b89ea96d5b8b42950dd32072eed0a54b7bc814b9b4d75cb695cda4a54b7ac814bdb02d52da371692f51dd72062eed36d52d635dda4c54b77cc6a5dd4475cb665cda4e54b75cc6a5fd4475cb702e6d28aa5b26e3d28ea2bae5312e6d29aa5b16e3d296aa6ef9ead29ea2bae5302eed0b54b70cc6a51d51964a18810a19618a4c0385301207ccc1085c94c025acac586a49c90acb8c0fe8a787ddfaf77137fbfddc7d7252c7493ccfef7152ecf93f4e82d5245a39097c7e588c87cebff99ddd9dde9ddf9dae3befbcd13b937c7ebe9c9c14fefc0ae3a4eee7f78909bfefc702f9d820a11db2211eb31e3e20f8c145f6878be48716c8060971917c98ddc145f2ab95d9211e5c24df6567b6870f2e82808b7e70513847e8a325046f70ef7c6e3e746df5d33365403f3d56640f40a1191158055cffd9e38621c82008be3b7844d63e6ce3e9b072b281067707ef6024215c936f346b42180909c968225da6f20682408420d8b91e04c5255cc27ea3b9c112ab1c6067c8807e7abeef6fb0ef6f3c975fc9f79c844e9077f0e922f2db0235e9a16fc810280c7f5ad18822b04cb907082491be9f3d74a875228f1c4ff4ef867bcfa7dff7cdceeb3a6f4eb71fd0f7c942300462d9114a248df6830c30743a5dd7d3764f2783a083a1ac7560083e4f36e19ca1ec3c91e6855606e8b5e7303dd91e967a267c61044366c92c999925b364e6efe9f1f1f9f9010262212166e6f7616666c92c9965b62b0b7213e4e60aefce095f96b649abc08856011f3d61da74226936224fe4d195bec2e16433f7a4311294ae4d6f9c6d2d1c15a564b9137410fcc0cfddfd7ae715d40ae378c3f7e5ba9e573d8f525707e5d2939dfce4d7e5acec48a4be0265cf798393389136f4ec2b27b0cec2ae14abb4b4aa566b85a083134b6ba2f181adf9665e4d2bf06fd3ca65acca772b086a69b95c1de875567ee12227a943c3d045dac0a38cc2418eb9f3a5d60eac500d23358e6bf3d34469219bbaa184e2ee88229c5ef0d1f7f367183c31506792c1488d9bb54e72a58b4c99a881099b6df9ba6ed7755d57ab7ce224d95ab62f5b29054b279836ff4a27e9ac2e327f5291c4c69dbce5e562d157f24a369cc6126d17e1f7b7bee24ec6401cce09c411e1fe65681cae03c12f8cf5f490c964412ebfd86f8f6060da1ce2a32b9236e9874a12063e4ac2b4f936499b4fc5bebaca5aea22f33bf1939fccc9237927903b5f5ec934241288c5664a4581bbb2ea4e09e7ce29dddcf99fd7754f9ba58c755d8fd2446aad247cd44eb491b469158b6959595ddd97e5d13949def93fd2ba6e8f9ab07b29a5c4698562ccb0e6b66d20b8288876828fca740f4c9bdf4e5dc79d6dc4937438a14c82651a4e5ac349f226739cd473e7576935919eb8487e895ae2f670e7d49a35d87d59394eaab0b55aab2b623271678e8be6b3010317c544ea37e600e74f2d31c29bcce5e60cf3ebb84eebce032ef4c8c3febeaf6777cb29a79c5e85334ff7e979288fe6f7ed5726692e0508ca99c691c031c8e5a90e7820ce9bb0839801e68d335371d87ce1431d5f48b065482e6808301b000347ca0d38fe812b26cd89ef14bc9812238e1c3b5e9cb1f3459d26a82b78b89ac30cac2b92703a6850307903079c115b561c78c2ebd47e38a18a34e83c3103163f40f91cb20f813a1abaaeebba2ba400e38501e99b31a8cc5c5dd7652a33767290800b194d703953070b0d0c31856f8121c7240386902cd8042142881c5f1c088c1a0f12c82185a03d5138b2b429734689ab37ee8120564c2068524b1a566feea06163ab8c91d11b2a36535084e0652a3650c83ca1cb546cd0b02923ed0ce94172d18137f241f9619b3754312e6f5c5bc891f2b6bc19837e3d2e538959e2d2cb5462d208d1c1853b45a46933831923d6c861f485c75185c5962f6e8ce12285865167bbec4ebb42cb04be2d5f842133a9c4784165460dd7130d5da6d222cc7d5da6d202897b7ba32e0ca3967d6c847e792a8e5518278b2f0edbcae16a035dd6b0628d71d28983c5957d5371a6be08c3e9727dd3f5bee9d20f7ceec1e3846f078931263227c021ecdb5dec56e13bb7fb5825e2c19e4c97074d2d806fe5bb9ee73df1264d37d9c47bffe941d3ad12abdd7f6114c19dc738893b30dbdd93bedb2332a4e63fad4c3e99dfc4e7ac83babcce05ba3b0cfcacf5afeb5ca0f884ef5cff2a3ca5bb4feb60cba9efbf8f69a44d63bf27ce963cef9bc6177fb5209bf0065e998f2542975113e1105c1c2da5a858d162bab245e3d0927ac24a85b3a54fc9c927ce48e0d1d634468d4c3d766ed6feb6992e428544514d59dd7e6e28cf93603a2bd75abdb50667e6de9a4e63cdb937994576612c293d89bcac89227a0a2594952c2325a3726fa454d59a36b989c4e3d8df663a89ac52a68bf4f3982ed233694796813374fb659544aa61f5d41c3797cdcdb1e6c471a9141d32edb3c7749996c2658143ceed3702967ca8040c1d28a95388460000002011400083160000200c0a070422911c04e248177d0714000f65783a64523215082391381848510c84530c432110c22086102619320ca2191b5000fe119b2f79ec20e98583a718e11691fa39be5f84663d1e7d5568c5c2e90c46c61fc91f537bfc407132d6e8571cb37ea078d81a85a8fe08d532b182b5998d69013d9c71789771c5122b9c17527f21b526f5b4e6b465ac4ddf395a537c3198b9296b86b57126b6c05b3df56f9fd74c84817f9900714e928259802658f5a4ff6f9541ad2b84f154e13037523a695e9f2754a5deec90a13f335a901a3be75bd0f54a724f810c69536d4d04212658b9171312f131e35024d4432e96ee8ad01704a843c4b94703ab55e0588fa1e8a38756627c2b9605bb7c5a310961b7cc6c6e78494891b7c06226f1df093469c07ccec0b16e634ce30f42f44c659e1ddcde70e4e3946f8b495d6ca37752f85236b222e838da1db0239b2390acde82ba8f431446d84acb558c7d2889d50fa64209075818a604537fd45d2ebd4fb748845fe54049309847ada679d79a6003ddfe933419ef93dae1aa41667381343252e3fb92ce76e1491e6b7bacc8b7393f999f6259c70412ffe03d4f3515225a586a51d6feedcf6c36189bee722c5839181998b5a1102b6e411e603f7f0e7af2cdada2827c62e90268448bf44887012665473272323d353ad65e207044bff3b76984da6a5584a625ec990dad613f477212189e01be8c37d06c80662b6d2ae4e82c990bd296c850da2f6ebd3ee6b3229660410843389ce78222091d201fcbce0ae0a4e77d2e330c15cbf9babef1e4c62485c2818e097c08ae0b90b40c5a02d552c262f557b55b37ad1f6289b77dcc81ec3d8c4dabfe0870de6b9757b0a7cf1ad492d8001ea9ce71494afe960758a8ec0a4d180334b21db9e049b1be4dac63e01bac7ad1c96e2154ca89d4d63d0775dad7ea88266ddd9921ff03235860c870cf35dc2553f666e9f5a1a359c544021f178fb55e4fb2af9a357fb5049bfd32d771df33cf334f0fed50f2e765404037058271620e6312754c01d375a736cb9e2d193ea3fb8e1776a82a5f2f9bc248828593022cb73661060810d2b1ed123fe001af3340a59303ba729ce3c9ca6ae97c74964e9d87fbcdeedaf805999ba5a38d6a3507d9a0d81f747e7a67b1cb4309e6bd7709f4b39176940d24ef7bb9c43cc8c240fcab1be31190c652caecbd84b3ef5f8c071c7f4338422e878481383f05dc987019d0232ac0856628df9a39c756dfa50b696367b4da19a9c14636c2d64af06ef86828383b3810c10601af19e6e9e9a52e632edd6502a897339fe73e4ff7f794598e517d9a8980d3bbf67959b26054563f2c0a90fd2e6caba6eeac97811db36c4df002c306c30c50868753b90adf445a62d87c71b5df90056e6506f3fa22b9642237c7920c04f2ad66a1dd70e87efc0e440b2b37748938e61686ebcf81f62ec716c6a449420785d09109fcab8211d1932523a47fb19207ff60e606f1f0ab329a71b13a23da45c5472f9a860dc1320e71daa154cae3e1ed0c26f0cb99c080e91e746fa3f99c4781d35c713a4b8270a16b712e3424e1e4ac969caf2951c96bac26d3189f5db3db9b95d41d4b0d9c465954e5a5a369035256e239b3153a57238a030dd19885b3501f60d45fa02b4cf21e2c4079808dbb9515fd47f4894987c633d1ad70c06862f42decc13071533f2c63a1c685854cfd38493f6bc9e5349b871eedb7e815f5e1520a3f31f6edce2280bc18f5922a8a170b152a5624664c55f1bd2a8c718590d154b03d03371f2f34837e1a926e954fb68cdeddc55ad52aea3d67062c13655f44d7978a1483347967030a30163b86a010ec9a4da939b3b4cf66d5ccd293de65caf3be3f1e2343a37fe7fdf05125815cac8b55ce2a431633010d481c5df059d2be3de0718ec276d215b6321612428f87c7c4617cf6b56222519974dfbee97734e52b1a3890401833e45f2f9fe06046cf8b81ddf2be892545fb65119d7887e95a257dc4ea2640d781c59ffa767c1e10be6fe4da8f33798c84612ffcfe3548d501d4bf576bbdefdbf43fe1f77dd4c718c8f755b895c6365ed1dbad8381a20db3ce8325f4787dae2bd8298b08eeeb50c4b25ad4e44e2d97f771f81680b6ff3c468f26eb627d0fba81bf34dff129a0facfccf1b71d8bf35acc5a75775c4a98692999208fb8f27e6ac4337016a171b90f9466e4f8e9ff72e6f9e3907eb2f29c5b817f8c01a5e11bfd238ffda9aaf282e86f70e87f6e79fb036d285b5403785e019e3e9f019ece2a6c0e59f8bb2016025be3ee1e29940560bead25b3b381d3919cdd91103a32f03a6a3546e8bcd9115f960cbfe89ac14a099df3f3e7f5f3a005f4808a3d26d2c1cdb5f0fe3a6122687a55a1293b2d72b80ba563dd0b9bbd481d32572cff070b09c5adb6e4aa242db23c49688e19cf0c895b090a37c160276410ed5723ad5afa835b9f3d1eb8a3df8691871fe4795b9968dbbb4decd698d8726e398ddb84634e415354c3b88d2d3459d23bf03de538685e1807535aaf341b2fb70919a90adfd36d82e1a88a089da0996d824e814511a080054bb6b2d4ac78215e90654433bbb1b1fe4ee9c2064ade943968e0d4cb630296560a2c4fca83eab3560828230277eac1d61224ac55eee676f298149e6772b53266bf0d6b47359bd004df6a2c534fccafec66bb18a65988d6ac84f566e2a40121162c11310c0cdfdd645f69d3c7701947a82116e12a8a94ffb363bf0e9b30fef63c3acdd4615e567fc8c6939fd8e3950f0fcb341080306317c8d97d6a716da352a5de4ff3794eaac4a0343a42fd5db26c349bcd80bd0124260d57952ddad545980fea828cc28f0265267960d8f89ae3bcde8151e1812e8c5510a2c4fd21aecd6e6dd6234482f83a5c1c0cd7ba0f6d7a506b8a4ce3a609b4628c9e8a2383943995df99fef146ce42a104db1061e4677dcda006328ee7bc2771d29ece5f8971c56300fdf1d3242868c845974b551ff2151a8cb280f0cc2075b8dc2d8a5806c370d115a20a9c546f3e9d998b7c90fe3306f558c6454d207ae51b0a514e579f05553c259a83217f068c3997434c6a307195a4ec17f5dcf04f137ce10297df7114f4bfbea3baf0b8f25042b84670a94d90bce6dafec1318c481d7d25e4b155181af2aab2b8ffb1f4551544d5fa5e16c8141d30cb5fb5a9dd00fab5f9bb37bc658009b62bdc7b0bfaf34b4a4e8fe239ead0968fab92a73050ab64d521b0e0a4656ec944bb35138a1a3e33f369f70980feb26c69a124c407824ebc035965d114ef6258d81175d3ba17756db5d69e5a0b427f039c7640f8541b8e12123d012837a8ad7e6955bc2bc56bc5826a4538bdbeb66114e104cdb134b3e0e15d7f58978b85f5c5d556515b54b7f52c67b338e3ae502e93c3257c0babe0b1ddd8e2bf86fe995403c4d8de3a876b92a8821afe47f812138cf3252ec9be34b3e0a6e07f48f6d1b881316c97b81dab215dacac75b80e40552c586dd1f2ef997adccf62932415d645de4ae27ee25beddad8a34e707de5948c2d559fd4b974a0644da4b64b54da45f6288aac52d9704c38cd364e5a46a25252cba012a1c8c4fae605165e241df31d11be60f7f0d71e3c981f2298dde1ae60495fb5b2165a7580d8f3b32f35a587e4034709081a38ce88be45f34b31dd85232b2b3059b10b22f9c2c50468882d0ae025615cc473d4a14a61076380000701b35e8285fa602dea80850502bef6ae6535645d258bae7d3e269941d8126a813bd24bac76d018d041a6b7258413a49f6b8a6c3611da3c7547a575a2825942539373cf188d96a5e0852e9db4acf01b52c2c29ced8f9f71bd360416c4fdfa9d4b3c152d0fb220e2b12f1bc376a197a4093fd558d2adc3e839fd6050f637276a83d7758df9a3b784cf2492ed6b2eea56d769ecf287db1c2e9a0f453c65c746985501fd6ce3498275c2bd57d2aa3ed12517af954593c6169b794c812c29515866aa7465b4569bdf04a80da439d6e9f6ba7a21edaf63a7130b0474ad4a822d850f8d1553549038f049aec7ad9245b456e8abf8c9eed2c716794a016348594e0fd48f3bbfcfe84bf82f65e1503cecb947d8cc1654278ffa7237b67d4aa97853d04bc9a736629e693d794f6e43711d02220c05c2dcd5fafa8640181540f2f49453ce9419b8dee6cfa512466834ee9e7bb937d3102a504ea62803561aa2084f4ce4ce52010c1a6ccca3b54f5015773f530ea1537f5060370584242c1d845e41a20c709e905e87bde3b6935e3b32c46a951ae533ec7a01a1e2873ab8550d5801471ed35795dced4aaa8c1c312a83251d4a84f276d6a94b9aae182e0b7ea72b184eddd286488663154d12e1697b806e8b8a90ea150c392164183e32afc05733ed8292420c87173ce394774ecd0b032405bdb7abe30529106f868e3a146836661cf7615241912fd2b9f5bfa5c1852cb3aaee8b87023f1555d2832d6db526a460982282923be9d598603f25cf6acb03a71b0d9387f1d655a223b3c47261a677d1808fff37cfb8ff39e7734d73ac7a073a90060cf4447920822c10f4e11f196de8277df9d4e20e761508747cf8d63e62461a431135d9fc4f2770f54674b64a7b003c8bab13812d367173c5d96d8395aab241f10efa96109f9d3cf674dc3474556c461dac57f00cdce6678bc99d842ceea7fb5dc89a35e9a0570ff5d9c91d7b64213f6dc672c1656100a7b3223516add60839287c3e6946b40535ae799aaf0df1a5d88dd611a8e4f5684458be52b6e15917d98e8cd1eccc47ba0cba9f1972d348dfb07c5fe0646e88685b68868bfbf3dc36b261c560ff497e5ddc71ba757a1d060dd0d3e9d8b3c9bbfbf354c460cb8c33d1571b47f97c1de8a959e53f3410a8836d7de7b6d3ddc6aee4d57c1df53fac4ba00de3bbd88ebdd9909f36636db9ec74b7f90abb9935e7296dc51834185b663a0cedf69ad03d0540a951c639bff9f224b25f876a4cb6a7964acab129a6853a2b54eb72897925b5844f589a897fafa40238d3708ab591f467fa2b952bc0e56f31b07efa71851e80e2b47ba2ad49a63265faf344b014756878b8f5fc3c91c0a09954687a2d7896c4eef4bd18951a43253d6cd1dc3ddc8a0c764afa8b65a00087fe415b70926a3a995b113ade89b8a59aaa42fbab28302538926a648caaafff58e306434092d04c233c7ebe0e789a0f1d6d9eae46dd8a6325f383d36e48ef4a6131a4fa204ccefaab8b87d7bb1c91ea08ae86229f364a39b68a58729e0ff2d352393412a547b192f4a14cb6668ae8f380e12d19325182a15add83bab2099e09f3e2dd5275f26c8bda0baa5a28b81721463728ef7cf2c90101d540b12c276948413f0474905d5e7206a8215c8d8e559865a082bbadf0d2bb8500fcba31f8d5139548bff03045de92c42236892de493e813caccb82980b031dfde3ae48ef6bc49c5c6332468f4b2105689c55132e645c9c88ea186d809a54b5742d11c32ae7fc6a0d3038ed887ab7be650817ca585c100af89aa50e25fdd3b7765e7cdde70ff32ed3c32330504e7cf6f983f369018d24fe3f7013cef17c845e5d8dacfa9ba812a72b5d9a0fbe3d9a5d47bcbf67b43d2065587e33508108bd4c53a00046b63ba709e6ecaa32afe5b6c87e03aec259f716a45406bfe56adc8f5e329f1a4b80701c4e9f6ac2a881d6dc126891634a0b77ddc18f214b347d6c692916b5dfbc8414e7c64399628e65162618b1e88169e013c8815d6ba21f9790854513cbe568f00ae14ec9bfa2442ceca8c6ad5c8824ace7f569bdcf5f2abe63745ab65149bc3fbe6c0b5785c116ef208ef017d02f5aff1636cecaa05e09e5bb522e0dbe0d1ed924dcbd121262bc9ad2a6a8748165ba15c542cbea0d5fe81d826c4d536879d1f5fe8584108012736c235bdf7927a704d5bb62d9d947a81058701ae58433890a51cf8e217fc312e5b169ded94efe6b6481aae83cd820cba68c406d27f90f8891cf5c13594748a56c67e26d5fde3d11f37910e98890daa9d4306f84d6c0e983429a0a2f5819c677b2e65b0c472a7778341b25f7f42443d38e0736f00be78c809c5e7c8ef3de915ad25268be88e178ae67bb5da915005260b9c24daaa28b84b517ca3a55d0cba7e0b3c546451c25141474f0aecfc3d10c0b37087c20f050ef77ea44268241aefcbbed82ed3d95871d01df3ac4ac58122415b720ca3115305c31c708ee8df707fa8422a20e721eb102814b232586ed9b606b307e9beb259e7f1aee62038e737fe2b99697c188aa42a7620bbe2fb6c04501a51b233698b5e5f515f191774ec0e9c028ccf5562ee5c4a4fa0f64fa85c186e9867beadd179e8124f63f7b85212459b236622de6454180ac475b59109057721a0826b8d51adaf8866fc028aff5616480418e4ed45694462b7eb5e963666ca921b3e5418354a3bd6ea488282b83b5826c9bdd8e2848176d789afd4f7e62b9fd17e2375203e3fd26366b70fca43bacac9b7d0560c57d5fb5fa7314162be7fb4ec70386974d8e037d22f7fa70e2a7df0b885d1e9a9233508e3012c0d230dae0d2baceaa3be857f4576969d40e57daf6240f54d9f37bdbab66bed2300e6574b711c8b703c6856961c039da44a855cddd5c8e8a7bffc5deddf1c7523400ecca6bfb749aa214f524530011cc91e4422f1e8584c75967d9ef5b8f416ef16af03942ada14299c3d4d70a59bcb40b460a92ad0b239513e7d48f4ebd974a4e5479d1eeb4178d41ee417c1d011242c4e17eacd8249955a4aa9f7540bc5b8e430fa8d8974bd850cd48171e1f5024996bebff4f338f0a024d13262ab3fe56807bfa9d4d5979a5f0b42ab3b04d4f19c474e80cf1fb2e8d9e612723babb57e66d6a326a0db6a6e18b48351d6fa394a71ca174aab190c94ae5d589f04c8f2d9a12b18f53150f2899e89ccb27ff91ddce3345afded292707144e392b581e4849ed43bfc051babf6463d175bc994434edaf9c6789fb9850028b8d74f431a033c8e6763268b4027f1c9108f6f218414ae1e1b1b762841fb748891b8adb421803f11c2bdb12c6a0d42e5d5de84396f6cf6d407092c3bf5c406b1bc1cecd31dd990da0f075eca171d3f0ada99d16145efc1e7da565301de1ed10a915d77e2ac5efe45430a240046b0145f58b75ccb4d98960109ad9263cfbdb598f08694c1011ac47e0fe1290b203faef9bab3fe1f9f82f6461ae1304f4bdaf6e6f949130f250fa0123b910dee0e62d0d3e0dc91a2cbae33d0efcf4c053d16d1233bc2091764ca7b85f29e8e14d2747d8564829f99310d28009c8836e39e3af0ce757ce1bf85c7238f822f29137dbcc37d3f0342207516ba79c607eb919e97a013257772e171eb84da9212a364dd1bf54d7d4e106c0c0be94eed395d5aa682a1f8b8b3421c6b59bfbf11b8bc8536d05bd80da44fd002548db3d605608e0bd664241cf19744d676dc5125effb00810ef332c47d0cb9484fd46715e40f9eb6817686a3084a3a40070ef204b109a11b326294815931e4c86c9ffaf6d0a314788d92c93b3bfca1a1e1733270a194cd274a336678e8833e0bc51b77e792cf4c0e9dc5cf643f2f5d03a93b9dc4025ef80f9443763b77f8ca09c5c5d2380f3f05d29c8c0df2d5a6e49ca33613865edc8527eaed125f0af8cd92d5c0556385ee6ee189796afb64d8190dc57d4f4b8b5f93dfe4d7c07231967daf15e21827c85cac6a977058356ecce0d1cd92f60b0700bac30df9b560702215826dd6d86f07270bdccfd475a912f6e0ec02a5935dfefb1138efd8a745008b223b1886fc8015d5718d48b860db05955b1074d92bafe5823d51344650853d86f4ff773c46aab645fae90714743de5361d050e21d11f9c4a5509ef60eabb162a204d1fb1243bfabd86b7ea3a1da566e56ae370b83acbed768aed29b3b18a45d1a87e23fcf0c550dbd663f581f34b0fd8c8c6e296c0ead83186c62840143cfb76bf7d3f2a70b7cf35b61311760ca9bd6311befacd0281ea17d8a0a6aedccf4d9255c4f529685987d37918fb2e5504d174c4ab5ed72a75d7e4a2d60677214b58f653f960525addcd7a4eb9603793e4305f5cbf7bf91e8b57e498a70834cfde331112142f6e9e51101fae817b11ae2584a8f8aa06213fb39b55027257e7c801c87fee64ba66701d1cef47e7840ece7d085265252be04958ecefe3dce6e3c29c0147d778536f985b43f85f163575d5e2b783386b5e0aea373ad7d5e83c5202e9562ef0a4de9107aa50f164eacba26e784292ea16198d270a14ed60506af0566f77b3cbb48a4f75689ec8861823376d0f7873af70016988a536dec84c6533fa55ef49e277c01b5982332113856c41fb8cba8b2817ea879a3d2f34aafcf73255dcac858771cd324fe60458d3e60f29dccdac5c9a0753a2f7a60238f6bd422853cb438690c929191a91a85529bf1992bf750c630517988ef7400ab85d6d35119be8259496369008aaa0a8af84db509b1c38abdc6d813bfba97c3f62237dc27d85030c719cce17a9fe97eea3e0a3824c159450c19dab45b7a648ebafc68328c6ac55149e703da4d0bad530f4ba28ce731296b5182553000642a9bac31709a7e39f3580e94fecafee734d05d04e2982173771a08f99ddd85506839e176ab380135759333bd4fdbb9ecccfcb4a15d5aca3ded4ee24cc6d3362e39ff3a6d688796644ebb9238a3e0b48dcbceb04d1bda2d2366d3964d99b4f4e4ed42e0554a73bb584008c9ec0a8610080903287e10718edbe0c30e7e045be3af47a372784dfba8f18ac8f5ab2491b254386530108451d5902513953d47bdc9bbed179cc8e0f0b93fef2a6ff01183a8691e15096efba1fd99295c12ef159145b4420cad3f90943d4b5aa76a425496a63f05041667c8d9a59e89d50b24ccd53d85d4c6fbc03fe4575584f00aef5b9ca2362312ef0a3858e5a32d9d23bc5971596cdf3864e1c5fcd43d2fdff24eb2b3b63fe861cc75f2240de001c5ca56e1321ca4e467f3c2197100ab2592dcdb213e71cb2a0d177842ff98c70ad309d398573abf47994866c8007b79d2fbc1dd24da88b7d24ce411dc6a516bf169c8103d6429ebe638fd4cd24905bae87a8c3d743dc87d753ec43e5d1e635f1d1f739faec7b04ff783b85fc763eca1e321f7aafb31f6e9f210fb75fb98fb743d847dba1fe2de3a1e631f1d8fb9a7ae8fb14797c7d8afeb63eea1e321ecd1f918f7eab21f232b13018c0ff4d0eb1bd5488fd614f64951d4babec83d3f222295dd63765f9518d14ec01105716d40ace3659687e27f6b6aea84a772aac0727bec47100faecf492ea65d926ed5d5d34ed6a6db3c7d1035a16591f08f09cedf0abad4fcaf5086b546781f4dbd3278d4fcde8f531596b3b992efc5a7113a7369a14bdb26a8f1080b1e13a7e803776389eb96edb7a8d40177bc3be0f28bf2a64bb690ae190fba85b10021f482bfbb1c7d1e955c6cd6ee17caeb2e8b397c6e45c76cf8528db8f74cbde4d551cb164c9c1eba3d480f7058fc597998bdcc36f5b5f1f04a179d2a70f1edae5dcf3dd3bea8c0868f66d3e7be57de0ac46f77715ef7cd07d4c5a3c988b398862534689d06408389a270b6369a66a5378745b9f2aa86fcb9c6db3b3eed580dc6552f875da3ed11740af332fffbe3fe7098a4f219cb45c93561d1fea806100b8dcc67ba65413e3b905b9899ffa7e3eb495d46ce782831787da586aa8c3b5cc5bf58b77ddff7d039818060df2198e9858cd961199b31124b978dcf4f11589bf4a3888301a14e342ca4bbd5e6b89611b5c353753fc7ae0c413d416477c31805bc64138fb9247c8502644c0224b99c80fdea37828e1253b892658b9591fe010fed1f42e8a28b7ac14301b45b7d7e246cf046cd92c6084827360b411402a22547a78ea63a80962b2fc60c69c3391d7c0b2429b534e48206b9f2581d805544da231ba2a4d9f702a23627c70210f08e0af8dc9214ae9063c5afa96249cd14f94fcbe6325049f256da42e24c9220fb7be600219ee5c06c7ee57f59463b657e9bd50cb1cfdab457ce9eb3cc12995a887d5980bc65c1ac51f99810909ce80ae88e04da59d6476b8a644bab221aa502734575203b12c05e791f6d33322dac0ac1140b981b5581ec8800fbe5f5e00dc869655544575a682e7405304702ed2deb83352045ab7513ad7201b9d04ba01d0b69b7bc8ed600292dd795e80a85e48056817e50a8bdf23a9a2624b6b86a82291422075a07da4321f6cacac19a23b3e5ba88a6a490dc2835d0e3802d6459650171444955fa3a7d1615d18eac89c0f4447185740ade65d4e17a8c4c2ff015ca101846e8a6819660be959e65af6038f59e7f1b01ac2ce956fdd8ea06471127a6a88b80edcdda7bbef0027f71ebb0925024c4dbaad36cba3b58df4f1dcb06cb06c2b15063d00a0e8403cb450380767aebe024c6ab4735241ed1e28059b191ef07077961cc4c64c9f79389b8103023b0e6fbcb212b08981735f3fd71909503990bac5c7e58c49623662246fe7f7964a50033b131f71f0e9942cca8a895eb0f87bc34664860c8f5cb455c123122b273fde5212b88988bdaf97e701095838c05a67cfe78648b11732256fe9f3cb2420823b135f71f839452c8acd8caf78781b830642432e4f5cf445e0a1811b373fde79095440c8b9af97e18888aa3cc45562ebf49b42937212762e4ff9747560a30131b73ffe19029c48c8a5ab9fe70c84b638604865cbf5cc4251123223bd75f1eb28288f9132767dd46b245882d72780e6efd77aad265eedb4b84e8dda55723b69d2280c62ef2fab0e90942f5fa2557039b7e24a8c62d71f158ec2d0274dcd2ab8f854e2040eb2ebc7258e83501b5de920b876d6f13a2e12cb89e58761221747ec9cbc34e4f11aae9975c2c58f42b813ab384abc566bf08d131165e5c2c7a81107a73e9d560a1af09a8774bae1c167b35b1349fe414cff74d81cc5ed8c9b94cd2f00a3a4efecc826e73f4b7dcd74007c30dc37521bb78fc429fa85e2f633221b82062d220578d080a74e9963b505627145cd330a94ac03ae50f81a5bb055c3cc7f0b5c399ad7d31c3518ea81363acd0ed1e6b43403afc3caae17c382a1f4ea7127f8c7c2a6de48e7f54e9e96341feebf8a60ea3573054867aa4f0838be382b17bbaa49258a856e19b386677426b161529f0c858dd04997d417a6d471caf11dc094e0e72d8f18dc4f5e67a3449ecfbf89cc04939b7397ae1565534992f018fa675bacaa3d3d2102954e6eeee8a6f6b21f9a4738aee0c796ce9bf98727e73ffc772758621db8d34bc8db42bbda0567ace869a44dc142b048c7ee9a8bb6834880fc80796165d6822176e2acc124224701275b491160458d4917ba6ff1137ef0cab2a2bc6d013b84e7e1e29c3163c961da8921edd5784d84a0f9efa0432db155379f1266fbadb50cb7e0e083f878aed2f9b14cc17c782585087ad4d33457711e1729f6084f1887d42fd4644d6a7ec756a363654d63c8ecd94676ce0e4bd1067169fa6fab643d27d224cdef44a908f2715c3ebbe35adc470a532f021433382ccf21f2968b384f46825ef81aaa80c2d3339b5efbf6b44ccb07a596eb14670e09adfe230569e9b5a8d16b1b8357d966c8ca6dfdf7baaab977e4397553c99d4c5650c0c6f1ba113e1723ec0853073d33113d9e97cae7e9b6e11d093bf854f3f6391702bb13ed261bd4bb968eb0a9b736cfc69b186dd11f2d60d349226e25112ec0d278f5bdcebfedc39492420eb69c221d67ae5c5bc2ec8de209df94549534b745f9531d56441e1926e3709cf2e87dd61356f6a6027539e119df563d19f63a28ba7af9e9ec79e3ecda97d00bb694adaa66a9f55ec31705632f84ca7c7d07dc6075e7754df867dafb4e8b095372e3c6db295cab859bda9df10201f79f6120107b8adc98be26969ffeb21611dfd7e7794a884ba5c9fc7fa3c75734a6104cfb910fe75c748e1fb1e59569a5267d9e96f054bb3cbab20b38882825bde1df1f4f62d6f61710444c18716487014c517a058f70fefe79e0be48034f5524de6989b7d50b7b65457bde92ff58026f60c18a9cd8c3a11390f64eaca5db8f721d5e137403cd7adb664bf5833941d4e73023ec694802400d29c5603c065d92ba5176ec2eceeca28c5c4265f84007eac27c14eb80a1d4acf4bca5e6cb240b16510cec5a468e64b4f7b7cbca46b7656195eb83658d7b25f7f179e6122c3fcf88ded36270086c9e67c47caf7c66c96e84799d51f25747716da79bb600eef7539d6c8fc9bcf4ea7c8d40c7a06b40d20390144f8f8f54b65f1306b07341e2b935b88592092318267b14509eebb573b5bb439a0cff63a25feae606a0096c4761b67a0cc28dc5d77043b944e5aa279045c217a39c17f93e0b6eb2481ca89c2e5c2b9794690fe9a8e821ed412200eb7684de10e8fc44de51706fdfd8a75b326ba1b32c53e083709700719baf45b5f30a653bfb002002cf8b21310db1773bc429442bee053a8a2dcf438768002021315fe01d43c7b50f26d4ada50a2e5d066ba12e27712f076df2f15685955586f194189b8c005c5a22e0eaf5a114677c1052c12b9c9d752666d3b99418d5ed2972214a32eb4991308974b6e11085db7873389a8598a6bb5d72bd447fee13dceeaa09a8a96a51326abb4d9e9c608217e9e9b8e2c6915c2445f4df18f6b43452faa6b3d77596e34caa3cf92ae04fbfc0227c91e9c087611612e5541b93943b1d3cac13819d66aa9b0130b8a7c55b2bced8f63134cb6076c2443a918f5dbd52f16b1958410250f56b4a0639b2e7ff484ebbf8f41d76058c20f76d70a1469d3165c51da10067bcf77c485ced737cb06d4f94d8eac408d2235c2ad2cdd3f4526fb7cf44d6a0090ed7bfca0319af4b2144ba13806a764a4e1fe907bb8193a4b4e1cfb13e0c5b85013683ddc08bac6748345c19d04a1d515e11d55e39d4b6f0b79e6077f73823bd2a506e076b73a60728409d622d6b775db016fbfb6fbe56ea58e550a825323d8b552dce3e70657add147d15b1fd00159a2967242fefa37d8022d17a0112e373a3201fc1dd32cca4edd3dd9dd8eb20037d12545596dc1818bbf09b676abcc273ca9b8a08d7761a8515add6582f9c7249f8a3145a461ec83d8ea4aec5adbb7035d8e66e8e619e20b379d7ae071d4838234b7362f981f389dc18afb8299f4ccd6b4c5f308149f2fcd89e888f64d6bd44adad62f3c7a5eac3ace4a7521248976edcb06a44083741105aff0f7c2a6fa71f3dea9287372a81f17e29991e3bfde777668a19ec16648c4fb21c21853aca3c045f3ede91acf102aaabcfff8c6a8ad62c1e612f9e741c102f8ebf1c02ad66fc5af60902877f279094e312954adc70c1ef7d12b855c2333d14c68732ab73a8c93e00c94565b1e755ccf4113c88e8d47c432f6006c326d8359a3093b6ebeb9512414233e72f400a1934174c2d82b1cbb4df7cc3315e84a12b50de71b60605ba6ff780859cd2fe063eb05afcb6e383537566752976787a58db6be0eb88d1884912e51950740c1bdc948cdc01bc7da5a585398132b9dbccc96ecad097c85656481f4042b28b8cbb2013d07064bc1199c3f74968034c15a14781f8086f0df43389122ab3ece51ae1b83874d59c99802d05b34d4fd06e9ad58cbf487a4e78d23d6ed0b4427e1ff0f47c2247cb90eca4688ccc5629b10dc830dc255a72dd65c0f40a508b24deb0e5008183dc1061ca6d06408b30208cc00e0bb5978aa5ef7adb862434e8f2d5fe71aa0a57063f4333408ec128244d7c05d253c78de38249c12bec676f1ed2aa71e350178565646338fbe1e155fe52679d282a68e05be44483e463701174012fcce3af8e56e49b4697ffa7df43feb06ee718a7d6e1615169634f3b109599fd70fe49dc9d55215585d637297b086ca92faebf359e77dea47703bbecb70a607f44332795f64e5c267f33db1e44420ec4703d661692d9b6e88d0e4f4ab55d9c0631885b1f44f36a2be002ffc01c89524ce544486aa82e15ac9884a7ef6ce2e5640d3bd334eac12905426b2b771418a99d924c55859504643e828a40d69de91d013a2d62c61fd5a958ee518aba792ffe79813305fab11fe67f9cd2250efaeddd98264b3064ab3db1873b13a0d124fb6a171b504f9b624cac3c6082ade878d6632a4f07950cd4ba95366c1bd42bf96ffd2d1b56b5483c140c0187a37edb84df314d538f61d0b4433a04b174b8390f5b641b25aa159e81835f8832ab19d3e679fc4914938ab897158bc2e2b309112ce6f69a542c36bf861df3c06ebdc296a5189fc2dc2eb5748110a7e749a4622e8018020d22aeac517a42912d56471d784d669a7c4386cf52d072fe5113700806d77b59cb2089786d9439212740acc868e6f7a02fe30c8f6ebe06bf52fa5328503819945d970a0f970e4355dbd9464a31f90a689e7cb3c7c3435ab31656d667077cc1060f53a6aae3a6d2a767cd050bd367b101b0041ad2fe2bb23f76944051e6df5a0c49a329167b4728f7ac7ab2f0386e825b3381c4ac68048d0410f03c65b004cd2b8395cc66472fd3513a05913f8e3ad944c8d45fc5ab1d12fc6b94c6ee8234cb56ea083fd6e7142793418a253a032874bc0a2efbd0753e6345a4b53d95403c4d40a9ac3c034d539e661ed2ff06688789e6ac1b8e76a4a3af3b940cde3c5fcc5f41526002e6975a8123c3647b0e1475a5390bbfa7c6899056bccb9ea0695e38366003dc56a62c658b1b4d990cd95554f7b33c472aed4437dc505eda3487b4e57dc61f3d7e9dfac66715275fbb41a68f965a002f3c221442f3864d53cf10a1bf5318344b1decb3999b39e1c06a8738a439429f5d83f966573582f050754ca212366ddb19e5784ab41ce278c187a92bb2a24b2bef50dfd582f2624fa81ec1206bc7fa2cebe5f5b1b66b1a3c424dc7228d77ed211341f90d6a7932a5c5f0b1330eec3d2f1ebdc1d78e9f3f69c31f0b268bc545794a4a76b19df37ef42d987c74a19752915a78aa64fc5c016a683ae0ca14d1a4e6582ba079f862e9df0d5635283e04e7c839350b4c1c60978050ae78f13b08fdf37e155b36fc2cf5c7e5d72eaecf2c7573e56ada22f4fa491fcf05ec4f820ceb571cb817193dbc07ca75871439caf688fc846a10be9cc03620a1525b233e2282f2598dc616370626d4b537b7e9d43de831acecff895768e953f037fe2432ac3b1bfdfd55d2d476ee491ec11d6b6c7b8e9e5276b0cc7df4758636ec743d2abeeb797a8b46682a7a4c9a1d8e9393c1185e84ed1686f9e385ce4c0ed0ff7cb518917906229432f61dd48563cf3e3b94cad4487eb08b1758e3660e914b6f351479e57199c33b1bfe4b56248e89240455c245eaae2554a54de941bb958890d4af51c8986951950fcd12254d4ba78b5ee41c400327ec2eb37701c591987d91333942a980f327a70055954f220f4b826cc79c099550309816de8e3c814557d16a1a73f0f8e8139a2135f69f187f25cd5bdc971a912ed28d0af229bd207792310f7bc3a9732f70c187d0cc55ce5545ca726d53fc00a9f3aec619bfba0ec4b4852776d589cd315b43e07c13311258379308c11b975a438e38cc50a814edb18ecec3f8f33119ec7c0bcce20074ea60cc3750b05683cea2f486ee98eb26d17515525c3ed37cbb10a12411dd1566cf6eb8f3681a0e351b388dd8303ac60f8931fb3ca71f561e695113b976156e7e9dc11311c9c118734e5b92341199d10957bf496ad371c10fa947cb48fe2d6436828a5581dca2c7668a4afe42c3c1bb23fcb258ae3ec47ebb44ef07a73a6f4f24cb9289648162ea6b51bd85329006461e921428c310cde6125d914906222753416601d453c686078cc6a5237b78736811659927c43a2b245e5374f0a0788eb5d63541644471a390e4e62b27812b3e2b84f2f05b049b358b0546aa1c30d68854227a2343ee3293d8deaa9019047d9f5d50b172545d6e8000cbc29a6d1cde162c1f4b91429e394314a4a3f54fd87744ee82304bcdd1d55e0310c830602c74fc92e094a8b27fdea2b286b8b09ddd78a89012d66c01c6a84545727629ebd5cd96c950c8548ed4fa73981c7039064185e4f28f72fb60e115b93835df6938d8040194dc447dd971a76f5d1a85ccbfb05b2d60a38a8fb47f22d95821d1b53d82c79402b872db5984a55352a181ab3a5e38e45e8091fb015db034184637304b5eac0b1e9c0386be3cad265ca6c67d25209915c6bb0dddc0893cdddad9027c4178527ba705b8abe9a95a4138a84a7487e95651db272e7b8dc344966e58530362580f8061088174a6ebafb67646227f9a89ad0f82ab479fd8afcf063536e726451460561b635465adc3835a05cf612ea1e97e9f91fbf8be34d6b264de119ea7d1dafa4b3a76062c1643e22ec7ea17bb374a24a893d0018b73c1a0d42aa9721b42912532ec8fca885b59014935543f0149716dc72f86014e2bb74badceaa228d5537bf72b538a27e52bed4351a8a06ea638797869c25b065924344010b811eec5cd9f60455e81943189e473542b34d9cb2ffb05c5097dc2cca887be639ed42a2f8634f496d784d2020eca218bf249298b9a82f3e69355ca54a44b0ad3f4cae5eab74e4900ff7c8261c38be5d2d3e7a44b79512daf3acd599e7779522c0087711be71959aaa4ef741648cf059c60800bdb9b96001bca50c8d385a9b02cd46485c3958f5bc850d3491d61927b524424114be8660ee055559ee32c4a6e488c37272409b062c52059cfec17f6356ecdc22eed08112cd85772c0e469c1683b673d84eff8c8a82db8c3faec2e5c6b62726044c6a8deb90dae917dde4d0b93311a13ee12bd904f179c601b49e826b0070dde35039fbfce2d4976b4018a876641e1f348c50ced53db4216c4895dbc05ffbbb54cc74222b11514a533676ed04b2b50315eb5fb75c139c84e5301a0aa9f21666ced5b425a0008fbfe21a3b60957fb1779e379e8d00ee2a85a8dbc45422ab71def9b92a992bc638932ee17a85f0f425f3ffdebeb9fd607501f3d35233f9e40f1c6fea8c56491babf5a2a0f8c08d2f54a98eaeff37dd8fc574a9a60212bb74ee8c4650a9e8b7e14d2040becdddf171b23c5ba7189aebb14ea0d7bdc5496e316b2c74a9dbc235d0706bd92a2e0ea0bdf48c5e33ea053a9e283e4c67f94722cc757b392ae2123124f03c88a52a8dab26b38cbac4347e1f70a5ff08e892f397159e57cbdd15d86cdb918dae75630d32c6724bf9c2af8563dae3b8325dd138534984194867173e7d9e8b906ac9e97667cefea387ae4a997dda43f1afb857c220128fdd3f12bbaaeb7fe0d1bad7ceb4b532f1456ccd5d7af3b8d66a74706cfebe986c183f3ee77fc7e3730f487a51cd8770d4ce115ef8ab2156baa07912d3ea1822d6ba0c5be7777bbff161ec792de29f2940e10ca0ca162708978450a12f97c1c5f5a631841d3e05991e4dedb4eec91a8aa641ced9f0d01ca8c4f991b117541c19fb4e8c141d9c30f37c0378f03f10a26adb849eeb21a16e2c6576954490c41ab7a6363461e7396a5439d208e1b099c1f9850383a402f010eb8a8fa566df863af97f8301e8359466eafb6df57c36a0e3ff90f9b214b6d4ff2879eda8058e7b6b3ed7c88c636c7e588b0a72948fe10a84f23923d37f1c172e21f138f3d23e34391fe85bf90bd22742bcb86a7d20767d161acc8db632593d707745b7e38be07e745ddcd3ebcc1563b587f208930f5bfa0e641ca8939052eb71e6fd74be343818e68a311b680a058c6bc4af003fe3a21d5082b94a5b6bf4edcd6eef6de7ca35cdbc4bd7f88da7bd1efcac9c223e6ebaf08d17d883cff270c85c95b6060c6e66cc99f99e2f92ae58f5a9e798a8e6f3eeafb0c6a06f9f86c7c009d53c6585e1e8e9de8edcd4efd4128a7f046077e353afe90489ff612808368f4e0200f6334b8f38309d2a4df7541515a9d65e727e8acbfb85b02942e15c2578bfbccf676820298cb6901b4b7e03b8322fc15ab49a73f19d7361d84ae9a9947637aea0dd511ad61c1040d2297b8b0a9ea71c7803adecd828267e2ea43451b878d47578444b3faf416521de3205af9788d1bf64bc580e0b9c16c165608bc544e9b16e6a13225b4d8ea58f2425772497ccb8f8a8871de6d405fd746fc0b15af0dd4bf8042b8b07bc8c5e1a656456bd1646676fe0c560a6e067c5413a9459a88919765fd2dd0f58b50a48e958381130d881243bb127fceabea4fa5a112aa0fc5ed6bbfd638da931efb6a48e439191f010489cd32b0b31e6b4553b5a41471a85a90ed1355afa5b41c2dbd525bf1ff945dbfc3b205467a2b602fb65ebf4c09d254b2eb2ea694e2f3b5b239550db193ea969d62659e1a015f258e648189217544a0af6422b02f0e5db2a1af4aaef5c0287f9cca3ae03ddb46d3f402c52f36a4ad3675affcfa6bfff69b7689f181aca4acbb683e19facc9fee7ceb50e32536e9db29495a41c93e668ad33101dbf7cca098506f49db7c2e4f928212cef2ac534c7bf489166ba002ba738997489e770c76dc963667b8e5524e33039abb17a6bb6980a6948faa82fd97bf5cb25cd1004f3525ccefd5cb046f2396e3557c24e83e7e1c664042d3762bbfc3fb39778568fc67363696be9c8a01b580b35cb6be6b46ab4ac055f09de5ceaad881403b310c8d3d7104356d3cb109e1e141c1f75de703225d15801fa85784251b785996ca76f9884c5d5602ecb3568499fa1ce9b54dc7ec49f8482d41755fc20d89175a1b06d362265af4b2c3d07ea82c5657c6da0b660b37a66ebe6536dcd66297cc53f8d3fa6bff62fdadcbb21be3c3e63654eaa9a0e2b8977b80bdbcfe23cdf57ab952d2cb61635e5e445ebe31c9efbd1b6ae534ce7ed0e5043b13d9b6d1a09e8e81080624ba036aa12e8657205816a703f9819b4295991c26757055b82fe96e34a8125773129a97aae8feecfc88f8292ae25cdefbe214013b2845fc8cebfcfdeea32ed63c334d3907afbb01306e789d8d7d99b24cbe15ff2bbfeddd6d6fb9a54c29c922086e0865082017ca6ae58c133178d272c44b5dedfbda311848f6b6b9e8125d97df43cf35227288f41bf8da1645475da04ffddbd8e28c81c7d54298955b468e2e548531dcee55c49121da6ac86cf9a967385538ddf13744fac97bfe86742f3f9def71ba0325b889def3a61722f15e14f6d7bd28947e9aad71b26eab3e1d3827f4f20bc2395f28fa49863cd024647660284412ea54daea36385220eaba3d65a06f9bb5a512eb8e0c2473d49caee99205c9648e92393eb329cf992e3f7533500ef3d93fc99d2e3a1dd4665ab7ac0134c61d1b48ca88707bf410c5ff52a97dac3505e99c6dcbc2ca58266d247f75570d1b48025d1cdf8080569427e59658e0b4180d9b3c67cf6666e7d9546b588b81c3375ad82fa6f1d7f193180c44a5c59101004c654db7f5b7b8591c1e1ef73baa9b099259fd97959033a328ac0fec77646f2628b9dc023e71109c059e71e743105272cf6d5828ec868db01899359b713f577ef2d04f959f9c59fc04abafc819ff8ebb7f60c7ee59875325671c0b3b36ecfaab9fbfe3286baab46f335a0c3ef9cb8eb25a8d9e4dae61ad86ac51000e173e15a6f1279bb66d75ab9ad69a0264cd08a4e8353e388a59de023ef97b9be13280f5ccdc09ee197425e48cff085c49cfef1cff10d8391d14f65ce7409390d073cf21098544fce47f822acb09f17777fbc08259cc624d5517c0bf87a0ea1a1ffd4f9a7d66c82c1f66b399aac539552db6142ca9e73d3df349a93837666656a3a767f5faeb703269d1574eae4f958e97f5c6d2413d9be9c857b779dce3038f0f9d5729c4853aaf65daa9e3083c422b5ba7aaaa565df3829c4e2693d99a52266b56e17d1e15b57893999987300a08d6b13f1208f6c9019fbc99d5203fe1f2fdf82e87cc52c352a7325953256d4ed77fae1ce5393e5bf7f975706135cdfd4315d871aaa6ca513d5389c4b0e3545def3b551368aaa66aaaa6aa67ce6da31bb349811030856f0b4b58774ee6fa6fe0f51bd747d77face17a3f7b8ab9953842278f23ece839d5bac123f449b0e9d0e8f4ecc63147b58f4f0c34f5079a5af4dda110d193422426ed4dfd2ea24762d24c1d9a82c43ce987c0c03c2914220a212ea19531df27c91975ab5b2d92625e5efbef9afadaf3e81aefb570f469d15bda57d1ad1730941ee2f8c94d3a2f6fd2417b17d29b86700ee9d954811717d2cbfc8d1f42fad18f42215ec2286a68caa17ef8fda4d01444e66fc8fc0dd390d29bbef4a6973931a149fb984f920313923ee685903e26441285f7484cdb9be8c39042d37c93f6a497425e3ee6634224a62d3c22c4e5edeb2073621ee687c43c4c28441442460ff342d810498fd7109851a803f3129a667844079963d2483fc4e54921920f63432c29dc2149ce2881483ff6c851bf7f7c1c2f735ec2284c397491ae2b187a1238fa17b0e56340978701edf367d291791d648ecc0fd1f1a5979f497b2dd4b9f144748451f49b1e3469323a748451d452e961625e8852e803cccbbc8ea7610c1da9949f489fe36352314ffa0ac07c8ed0a443223de7bcbc3ce7bc3c4ecb8032c7049ab4cff149726e80262d34e9c8bce97384486ebc298c22f4329f244706bc11de78ebf242dc08b9c7becc9b9e86314ca1f493cce871b8fce82b601f47f89cd3d2f29cd3f2387d03e49c12c8399a4f8b3e5068ab35764a04b64fa7564f7c7c644099d34f80a200d150ca74b956ab2d86543d9897ce051c81cfa305b4626f5d3d11d82993cbf694b15351ba46037275cd4be698f875790a50bf1cc6b039e9cb6f6e73e3d015767e3868e12849a861b87f90a7d9da7713460b6b9bfbed473b3f7485cd71a917b344429d16a77dd5f4cf470d400a3712a5e327be3182056d237cc32d1d1fdc299bc0ac27ecc3eaf7a4117e59f97cc8a7d35ba485f245ca481716d3cc9f22c847c822a4b803d37012f2347f9271e78f3d1ee0ce25eeec959c9993888ef7ccea03e4e0ceaa317d7af9f3a1b3d385d2756ee897433f9d6c61097bea17bdefee169658ed76ef3d0c98384211d6c8d9e9de7b9c762f7485e52b67a3be97ec83c7117647e97f5c61fb8e7f04071df7fb0d748267cbf9befff3c01e72fee6be7121ffe6fcdb3770bbf2f3b0449234fd149469112cc2f38aebea4427794c665b62666ecf9ba51668a9856935d6261565a0a1659a69bb49e9943c7b761bc8540bafe4824ffe2bbed144e7825132f0c99fae98891517ec6ae6e94dea69301918f5834f387ef2e7e71f16c73217923022d2a6df9f0212c55b704e7cbfc3944bc4515f58e78f8460bf173dbb80ac0bc552fae431d72808b67e5faddf8878bbd435dbc4a1022bafbf1372060792c69f03d9899c59b1941d587eb1ab45b0087694414138ea4706f19c6c92a1243a57ddfec0b130af3b719557349860a7044836ed47190433b9baf910c1ecc82eaf0bbb4edc6a8f90a6813a57ce0401ca1901c10601393b87a378b99968326bd7684f1f8a7f09641037461c91156ce415ec46a97ad4b585dd4b0bb6c4795de53927c79e0ca2f785447d4690489035da1741daf0bfdc201cf585bcf213dfd090a3ac3e1b561ca579c5e148a3c8c031af021c05ff6eca633013a4e8cf51f846879de0c5758e12057649200e9233fe0b0082c32e094479355d937aa6eb32744d498afe23e82251a23cf977d334a78b5f1cb4c353a4b08cb3308ccbb8feb39999081fcfeb28a5f46f13e19b10c81f8c92fce272fd475748189d1f84a36cc84d644b18fa7f3fd7bfaf8bdfe56217bbd8c5ae3943eda3075a3061c5cc54f479feb651f6fd4bd226f4fe37a4cdf6fe38a44d871a8938d8e1f99c96fa2210880d087be97b20101d18430f3f75600c9a46e4b26844e48d9bdaf61cd76d60e8a927fe10c029009e486f0b08d3b6c5dace748a42e16f5b0edb739b6df916b0879f5a207ecd012116fc8af9916bc2d5d1adbe725fad56950833f4959cf1245656fae48f659b59ec0e55729d2e99939d15faaa45f00b3b83e6939e554c2833ca9472a9346ba5ea993b9fa584e5ba159d1d3b76ece0e131c82f06bfcbb5a5a48445c2bebcab40def0c034cea335e5206f8b47e70bd8c20bec894b811debeb4ea9b0ae196b8a47977074165c7f1b8ead44e32ee4c95f148ec2d191f08523b331ba8baf3c9fc1556a25c562b158ab98d02530c0207a278fc11a8bd5181bb24530df43dc2b4080c2ef7243d85a46d78c9a6ac7f5ea4a0339e33f5facd335db0e5d511fcaa230a02ea067d02b940daaa24be40c738b4271d8dce9e9a427939e4b945cd8b65381da448df18d16ce17d3f8aa36c1286f62f60c8b15292f17eb89931d9e949c093a03c69b985de79bc9869c50b88217d765333bd2d89c6958b4202d0b4c8bf1c8ae6b53d0903447980212aebf26a5fb32b42854a05c9fd2555c2368506c21699e7869ab96e6735de3b9ae29b9fed40945ec481b2b8d6a257d2a83ca681579d343481a9736fc4e1c713d4b2fc1727dd450705f9e3f1fa2679791fd3c1a8b89c0c6f202f6cc0ae87ac26a273c2939e33d5de32a12f4895f66c1b2934509ffe84abc65ee0d83bdfa497743e928a95e754ff3901e86926667812618e562f009039589baf2267cc531ee420c867115f6829df00a8fa031a9318e8ba9982891418679475f854db0a34f1e7fcf022ba36bb66f959f8266cfa56ec50b7b498b211556b6d84b666490e1f341bf82a14bc123950b6d95ebc2b179683e1f737acc614e65fa18de041a8e9cc5570e7398c31cf672a2b19ea8a2ac9c256de83777c48b515e78043bb24f87a3c68489b4610e7d481cb7558d4ea64e288b52262d1ee0009f8fc91d61433ffa6ade2e015e48861df9d53b3cf3b7679f57cd91fa24e02c65748ac935808d4dad5cb7558e7d6e6cb0e1f3d1cf60e83678849f04dba55ac8307eb53845cef85356829ca1f1974a47da009133fea1efbbafe1aef13e0583c1d8a04f5a6451271e48574c542d32fbb89c30100d704f550d527ca3853a4c235f2e2951666c763161976f6695e9c574c2f5b7a3d077b2fbae3f1fdef77cbde62be8e52beaba14e8d21e47558daa6e89f278c979781a7e871c666147a157ddc27aa5c4dd2d5fc7fda4c6449e0ffbb0cf086c292dbaa2c819568b4e562219aba6344aa74ffc332c32cd8a56c67509a3726da0311a1b698c871dd887ad982f15d3b0158caa66f049759965be1f5f35c363aa9894ec302fac1599a73ca3adaf1665420f43c684529ea6640f71abb9ea15afa68cb957afd7063a00368103e0ed517d98c6a7aebc89ce039eccf181d1993741653e7c43af5032a81a3b88d1181176ac31719e214ffe57aecbe810b329a3311aa3311ae338cef27a7158633e6ef8814303aa1a5418d000a64ce88aaae6773978dc11b6bf7b80c7da75bec2af51aa0deb5fd018743fa9938f862b592e8d491b1ac81b5781f32b083fe0178d4925211b96569127ff3358577c281badeb7fe3a331baa24ca84aced02554e52df77196af441d27ac5ee97c10f87c841e02524640488e1ca51b5336653077984781bbc9f5ff42dcf6a4d23a6b5795c7311a9bb18eb1b792334e5a6445e91a1f2c77a4b11834a0eb515cca48633cd2980d3406d3112224021190c01026d2a9dee12cda0426c051e056c023810e9b389ccae7c3615b583956f61013581ad53c74e77a96399b5168e3fa0d6341d2f8944dd99465e954ef4cd994f5aa9d28d19e684e3426da121aa3313a2de09140a7d2a73e8d5ec6f5d74020b64b4326c8197f0f43d290402718748247cc810981d880b0b75f03df8f8fa76400039f8fee31f0ddb0018ffd4a8df5180e8b8df575bdc586e52b55aa8c5f2e98273dcfd7ab6b48307436d3d9dc5635caa0e4b6aad10d6c60871d38e03d619a1a30aa82c1277fd235382f83f5761ff018e4eb32391306182f47f912a220850f460002860e63c18e1bcff5a70cf3ce2fef48205865ac52ae7fcdb8ed5c4f8244d529d2865bd5e77edafbe58f866167bd4348328fd33e4c41609ef442609e04c3313f6d57c381a53b20cf40e9a798977921312f43fa98f015f348603e26e4d776dd95c819ff900d4b5fe6c0901e66474ad79048a153d0838298af098b79524cccf277463ce92812cc8f3a607e748143be9937240260ba2ff423bfbecfebfb422b612a1cc4b3dee1d8e78542a1d00dc58032679e2167a0c034565a9cf5c412d4a27720df18b0731a7422e649608967cb89d1d1d35f82b5e854a695299b58ee94855d8c5336d615533932cb9036f35b546034467ec2f577987b8cbbce578b77d553e627ff6db3b6549a29e01ac92214182599f674cd9436678c1a112b285122ac15661ad7a76cca2efdd219b6ca3629d3989cf1a755349e23ddad29e9896d9bb53446631cce57d7cc0f78fd810f7c3ebaff0008b63bd61583b5ae384bd7b077936a4cfab6d2a8988ff99898d077b2602983ca94981f3b8acc8f0de5c7aea2144a5f72653cc6619ef4386dfa422a2c87ac228135e6ef1cbf58d5ea9ab1c6ba46dac4aa8c8eded47aae2fb9ae299133313b8a77a43123357684e51fc7cf47f70cf62df2dd50c4ab31695345a2b88b578d711581c7af66d2356c480e43b6206da412d71b09cdab73951d6458fa9d5281b45102f341ec63054b96eb3b17e554a6a07ca5a451358a85c25ec51226d743273cb4f892332e73c6ba3a5dfe422a2c83da75f7771813541aa5b1ac8cdaeafa87bae60b8d05b70b79a3a940d2bca4cdca511e863cf97b95cdcfb0ee57aeaf1c267ac1fac52f7ef14b5ba2f5809a1239c32f7eb1e6d335a394dd2a5efe74898c56719d3eb94e9d30b91e8edb168ed6964ae1f8e27c98077bf849bcb1f58cbaaf28a3933ca476e58b573edf78299924470ea1bf0d39f1c8b2a7743e4ee3a07dd3d9abee884d5dfed20a6c897e0ed96db914a56346b340234643166854817aa92ca44003caf4b59ef4e7d26f4f874f87d0735fa2ff8e12620b815be8eb735ad564619c115bc28b007a72cf7a8d6ec198c67f0d46f50b5a72bb7e5ec0285e83595bc88d615a14e1ba37aed4a28858aa0a3da0d73369d3fd836eadd1335e836f7a0bd93ef056b7357dfe7068118ce7dc601c8644f54f0bd6b29dc8db31c5f2fd5ad2a1fcc896b562abd5f37d3bb2f48c3e9d1d20477d2f7afe0ffcbee58788de8abee5452fc4e8bf9739dd8e2c9665bfc586a6feef47a3377528e4fb914988e85d5ef42eef1f1250f67d53421381f502dbf2f6e76b2d282dba11972ca4729065f4489b225ad883f66ec4ebb612d2a6732cb7815aa444b032685821a2d05e88ef5de67743cb14e47b9717f2bd4b28fa51c8834c14dbe2359d2eda5e04f6cc82b3949cf1300d5b05599386b4b902ab5544af5a37c802a87bbaa7c56ab15a2c16abd25ab5aa51510bfd91f7e295a4b063b77ab688b69e9033cc05df74e7a0597d65d69f8f1fb88dbba3063c76ec96dc9e55351cdefc6ef88122081895180cc6400ea4c308931968c643b8fe332698cf476b74bb188fc1d1ed969cf1d98807505c219bceeb7ab7ba55c33d55cd62188bc1e2a1e26ddbea562577b3b85b0d04ee1e69b3d5c7e91fee14ec15109ab48089eb9d7cb990009231c38e3d6b713ff6ec093264d81b6652ecd82d192b36f41df5b6ba79ceef1f04fa869e65ac60719ac17ab7cab354dd52a92d354b5134ecd8d3f3b41edbefd0c28e3debe16184e597a965a5164b5857de915d2ef04fcb823bb0520b1400ddb17be413aa80a15185185c91c62c0cfd58ed8edde380f9a67e4a04cb2e25ac2bfc1ce0e0c777013812a8d25ab5aad11d3cbc17c460144bc127e7b055cd7a0106183e1f4e7c36f5fdbd9bda0146f5ef868f8bf09c3de72401bd4070923b9fc386d2270ffdd8be5ad8add08f1d9f79ce2a8a9e288aa11fcbac198fc18f492ff9e9663e853fa1fd58d3ed5b9aa2ea9e16bbc759b552cd6d9b556301847e66251dd81e703c27ed66f0669861003bccf218740bc851a29fc29ff0264e5d57a4fdd0431f6286c4aa67c794596805c85d9a6f6bd1bb87fc6c8b53c219084083c720b37eece4ae8659dda2fe85507ce11276ec1fc80e82acf1810c2e1612d53d7e6af10dab68705d1ae1fa11d28b9022b73c099574150d96b8feceac59f7a8ba877e17a56f6e5bd54416acc106cf0862b00bd6b317fc4967712cdee34e3c359b990bc04a7413492345b7a07b4a4cd8b17b1a4b8fae449485f60fcf6aba35a78d0d37ddd35724d713fab17ec7eeb99f0fde69d19f49cfd89028265d17bd121689eb3e32024b9b204775cb51cd6275688a95e1c28eacba11b116728a79b6b023a7e61dbbe7867eec773f1aae4c19836312d5e5b05946a4ecb64f11be100a1b2eb1b31f79f2174217b36ecdba35eb59cf7a26fa3c26020b3ac943898f3bc2f695339bc82bde829f90db4adab470b7d9b6711777fb99e26e1c645bdc9e4118d16b71631e39b33dc8861d79b56d630d77abd94a77fbcd13a174cdc8473090a3a436f211d2a6eff63ad28657726607470591286e224fdb6fa328fe7669a8847b98270b368a104a88f9de6f510899efbd10f4bfefb1bdd8b1778fd4d46db0de6e52272d6e4fc1f9dd90e476340c7b2ba8a5aefcb8c7e96a5ff44346159ca9db51cf3f5310d1b7bc10d1b788420f85881ec9e845db57b053d7c74fdb083485defbd07f4f2916ebdf813ca5c50dc84f9beb075310fadf5760bef740d8a62267b66ddbde51dfaac4f2d3c6ab16472e0bc4767ff8812ffdf938ed59c9630b8b74ac4ba2abf527581fecbb759ad77d921c1387eefe48bad1e6749bc71d6119a4b7d3bc0ddcee9cb7e3a8e6b579e336916cb1d6e77a61a0b0f46768da3c3485deb4bdbff726eefd33857ed382ccef7e0385ccef42d3f6dd8f9e0385f87ba12948f7a3376da1902eecbcff5ef4a370fb91fd9d963762cbe9de9a3621ecbf84435c46ef12f6f0537d99d37d3ff0252267ea87be203307c74ff5394e081cc562fb6b0f03dcfadf016e7d2f01b77e07ce204ff5bb8fd19033f537905e6945ced4dfea16f2acc7ad0f7e613bc525994db36cb381b828a52efaaa556693728a5344e80c01c2375ae801a619c2a81e7a60141bc127244b64194b649650a74d168d603c70fe188e3c2ecc8f31301004b8b29329996acd41b59eb5ced704a3ebac569133dd61bd22692a19ddcdfa457d7d95bd9a70f7578b3ea5c50a83e88458adfca38913153ed58c1a0b408d5533d86755cd60546da2731f39d344cef8aa09f729a4374ffdd1573d66a852a4c8eaac56a932bea957244d25430d2e083003d50463be6898af09868a7d26188c622b9a99d90ae62a92b9260a3ca078e01b2ddc767adbe19bfe5965dbb92f03f0d03656f8f020a170fdbb1b7e3118b4c6180c465533eacb06ea041a03e3c5d5a736a9aceb7c53a7a837a0c20b16cdae691e3ab52f477ea1c346702af472f118a42f35764375428dd526eaca058f5f3870119caa818a01f38bf9622bd8278619fa01105080d73140cbc1ab2b087835562adda82bd3fc15f00df73b3ca61ce8f37328ef568382aecc342cece8ab1b8fe3475f1d69aab3b3c353430e0ce9635ee6774a6fc4d672e5e72af015308defdcb1797aa7ee58122467585602f92567f097e12401466e8294338c3c46e9d3a173605ee60b327360428ee1c0696cbea84aa632d5ef4c5e0eda646575683ab0cf310e9236f20acb292642cef8f3b4c3fce46d04c16032495f3eda89e89dee22a88d2b4197675c85c388d9b1c48ef5a519616b65b1c871b26d8992a674a76738b1a88650ad27a7d888909f18c128ae019f425a830a86a4f1e7d3c8fb42cc4f5aa34104fde08cb0e394c9ac2cc112648481b094167de4d55abf90370a7d558a0bdef49955cc2753c9ec993c2e782eb80045cef877313b5b09247a882a9a54859a8a6735a4c08ebe72e2acda64861cca7c144a94ebaf49b9fe74caf59f54ae3f3fe153fc78f3256dba90289ec267be7cec8ab26c8b673eee4dc63aab41be92994f9f41981f6f9002cd8af9005680966fced9f2cd1781324703e54d329bdc80697ef46a0d3b726c7a9f17e220bb90354d24123128354dd3b417cfda29f31fc9b1561704cbdd915bab21f00d18d5293fadf8462c2dc17a5184920fec0e3855468b31366c6ae58a327250e81322fa165260b9cf851f3b3207f14de8fd17206d385104248ab330031802b286539eead405e2a798eb0bb30089b95cb1de36b07b408514bd7bb87fd08276c92460b1ead70e4f6a95e2d20784526d631ad157fac28c574cd3346db52af57da1a93c69979306ea29edd350ba74c36b1c8dc3d3869033fefefd4835c159f886959044a4ba2e08b6cec08e9d1a6bf87e882834f177434c1ceae488582b53bfe84dfcdef726ee5ec8f748a2e0f09174ff0185dcead5f51a1a8003878ef21c85833cf9ab80ac56da6ab5ea55af7ab581dceda4b6da9ac80a3aefade94f10887ae5bb12dfaa26411a825b928d598cae6bee2b5be48ceceeb691524ae914083ca4db946529a73085124be1eeeed259d0f168517a73523a5dca3927d542295896c297668b7336a1352de81514d4461b1bc7416183823627f51707390703776f7ab3062efca6014cf3af1467111dd53b6c481aef56725bbc020705695b6d1804050505f115244a6706a5256c12b7bb881a9d3a8ac2fae63d68b70abe9fa2605f73e3e88b9d080a92335e586e5f6d35c00112ae48238b531a965e99451a4069b45cceec20290976f4eef5cbec2c6ab123971718528c4ce9060e530e1d4e9d3a75da820b0f863b78bc000018c453000410c38c0c343d661800010840001a0a50538029e058a0aee1ff12cbd2a88ae5a17d3eaae631ee75faf4a6f7c3b4ed98eba5522db164395e776e9bd329f55992a5f652e9fea88a9d2df29c1e02b960f657540186cb3ba70004ab8424765cb67405c8c25cb1c11ded43a1c42500645141b1f5ca2cae2821801558d29559ccb2fcc006f998cd9e30843bafcc62b6e2e1413453b18d84f4765c39df0339e5a720fc14b33cc5101205c44fd2467bd92c2e1cbb899c2133909f805c40409bc370470682e18ed2757b8a3b843c39c75c3e72c683662d863c48693ea8da7cbe99a10d4c23830d4b48a147e6d3cd559b3229ccf09946c65260d40fed7b34ad292067e6ff8fc7a1b3bfc5b0a39c89dc125b2fdc125b2d51e41b8e314d174186c859bca6c05e8e6225e4c9bfc9f5af1a9dda8e97956bedda2c999bf358c6329ea99c30162bcdd34c38884e4da353abb5d6aac1180ce697f6fe42b6d0c21f39e392335d7bda5639308084f00d6f2199e594e6275866999fa858af07d6c42f734cdc3304ebfe1c964a1206b20556dc1948eee11bae82892ae48c5bd2466b438a5790a2f3c05d84ecc1c24d8265adbac00e445af487f5c81997c9aa4c26639946a7cffb5081b35ccc1823fdedfa04d1f0c0eda79e3592e3f617a1e198e3f673620c321cfb678edb438e5b84fb51738c392e573b346cff37c71c957eec815b84fe46bb508f8ee98eb6695be5667353db26a5734ef6baca6bfc748e5230bff3b6e91d0d694d84bc9fa0474790e4b24ff6c071bb73765c881d8aba6d200e9cba53dbd8351f5b05c769396e2b21ca7130fd83eb01e616e1e877dd2247e9e4be4e2e1c616e11e66ab8bd12db3f964a258fe3429a86c4a6bd168e20c96d0e9caef1164a19c3fc19a2da6fcf6d1bf5aa51285da8fbdf363727d7bdfde0c3f5bed2ed470edf9b55b0fdf6e78fde9bda08641bb7e547ee47a1f6ddc0859e0da9e8bb341cf9ebe7c3d6c295e511f9beef3322de5ac11ec4aebdee46b1866a35f1f620de16517d7912fdf72ddf712e2010f5ba70b3d62de4e2f238ce85b4d9e32a47f2c7df1ada14d87e17d3a86954f3b4f9385dc11be4cb4b696302222fcc0f09b8f56b38822417e6c7fa9b3743a12d7c6f9cb743a18f646248a4972191b649c281e36ffca8fd86e36571bcf613c717d1b690391ce1d6a27ded06285bb45f02678bf66540af45fb31a06dd13e098469d13e0c7843c660ff05c42167ecbb8039a468df04be3cd93b0a4b72c6fe078ee2b5e13712b945be1789be2e044405e5d4b45a1fc7ab374bfd31999365287466cdb7ca3c9d8990962307ad959b93a3b4bb2bad5b87638e4b7bc871399ee198e3ceafcf6d2d8eea9e8e39c2ea8190b675b4f98bd4ef2ced7e4ab939394a9fd66deba17427b7fde55c58027d4f9b97f26079b845ea87359cda15d80bb9c06e4f9dfbfe0a8a5db3859b87251c7fed37edfdb5d7e86b4184ec7f2ccb6d93deee4e6bad557bd7b4f75a04a2c2890bc356e3d1a8d0f7e9c51e4fe4008a99b1c68a7ed7021a36d40ae3fa8f1d3709a280ca7db9520b1410e176ecd51848d44b1bd2a54eff8926977ee743c8438bf4c31e5827240ac74fb46b76685e42d67440320c24cad61f590797527f7b433c7440d6701252a4cf3cb4fbcd93903552d2e71ea72cc5fab47eedf1a894751e929b03aaecd60f8542a15028140a8542a1901129bb45ea6f1f0ad5e7ed4bb11a7a9ee7799ef71d0e1ad0b045425fc48894dd51f4f53d6feb72e87efb31f4a328d4ea9cd5d6fa3538aabf3e10593726ecb6ddf0fd180a4729bba1914d41ec8f7e88fd91e887b4bc281c43ef3d8e87a311ed3d2058bedbcb9003badf3c50fb7e6cf543a1ef2ae87de759db6243ef51d0dfe285a31129bb5d38562e046ecff9d85aae7492c7e851b8270301511a82bdeaaeba542cb93917171c857211046b11fa11f56496140c73314dbfc0056b6182143016b77beaca0b1825d987909d0617c172cf546a5404172957a5b56a55a3dcd4298bc1cac10c542e18524f868911330315b74bd9e1e8c92ad30c54936a52a47a443c29508af5973255b03258ac8c1a56260d2bb386959fcb75f1662335048f8c10ecd8ad1d50b07c7e72c0aa3c907881b504005708828b284be041446bc62d2fe9a0c2cadbf24f856d9d524604c1375aad35a238a8540dd65639eb140d0800000008a3140000200c088703e2e18040a20782a83b14800b7c8c407462381608932887619451c6186310218400010118819991ea00be231d829503017ee523f858000fe934e6bd14408e7c2f48bddc293628b7668cfbd82a34b80ff58d0de48ee977fe6222a58955f1a59754c38eebc5b7c60c4662a7a14ad62fdf93d6308ca6c658645293f2e1525337a4beddceaa9adb35a60284b6dc3cd149aea25d23877de2a0634845399c9e97f975dc4b0745e765e4350209449ec12970fff12ca61edf472a8dec65c563683a5663114f67f615ea3374324360f9b59f8b8f8e925bc3c78bb47c48c70514cb1321624a297052b30ab69e8bf9920771daf3e3d61741ddc2d6721ec3530dd113343ddcdc17c4418420bbc44d145f2d1019cdca430c21da2ec409670f01d9bc8f5e1862ff62a1efcde85604ccf36a60e41eefb1c51bd54c743670b1d093a14bab127131ae1be6bfe2fbe49c2faeb40d62dc18686b29292347407b046573618310d36dbf6379cc9a889a3e08101d814272c68e62035168fbea7086647677a35f461cc0120f0aaa2516b38a08a94216a1af50e83a53f990341392ac1eeb63824c8f11cb85e3a110dd3b03e72638ff878994f9a18c49c83dbd9980321eb172f4b02603f81b42c38679fd8607d6870fe780c11b9d4f18fc5b767d9abc51d2150cb39f0e3c8242a02517254c14a400a37b0614c03a2fb04499aff770a17c9e0235fc2a6df0f0525d6e2803c56774c8da1946e0a8bfe47899b04b441d37e92841480725543b64421e69531b6530a15ff2762652e0cb33294d41af9495978a48187c25bdc248fb47135de44f3435d4c227b7496bfb3b49f8b91b530f7f94fa60080fa9f42be88c1634fda7345939f0c5e0d08937a205b297654355e34881e49438c8c72b0fed9ab6c76bbb98742a8f925367419bd2a3db73d43cf59b8e80988191b498d19aa73082fbe54b07f2f9e0e32917a77b9631facc2bb9a24b39108ec8df7f477e994587acb973b682fa487aecc635b9b891811f41140f41867221a81c41df8aa24bb1c86e2ba8055bfdcc9293845829ffba1d3fecc63b2256d91b01e1abb6f6483807e418ec476c25f1b2571906044e2e19d8f54dba583a52724682627cb320d59133c676da2aa6b3a56eb81983768bdcb47e34a4f07f9fc972a2cf1906dd924583587851246d84704f0e746604fb1ad19ea2f9db8a4bf2dd6689d9b0ad1320be5b037fe0e4bbc09d7a659b83cdf2c05c770c60ffe2f8509e77cdef3e6564aad574f657199f75f8f4d06bb80f0f7d322f778591aff05e6fa7cae2503f6b22a3a0e979cd644c86443a9e245a7946d7f13731c82a53c04771922436f8a7fb7e591329f7d456afe2dc3d73b92412e8c614fbde13a0a3ad0a82a6b2733b21132876a7effe14027633bb54b183c822787f1eff91a9628b3c1faf5ab44a14c909145a0ed886ea90d8e2d7e89577a32bd6c2b3dcd543749d839a898fc15cd9b29f1c933a0a48f5b354977e01ff0d5d021e76991e152b354721490af95754fba1039494ce715b34f82e2bf47ca3fafbf11f202f481d7dd457547616e1d373975cdc5a018ba09234bc86dc91d1da138f0752cdc0e5bf83d1397266496b774657556d41b2362e86ead366b7783911866bb7c7f441133891c063be15ca86d977bf421fdb963426a2d7bc8179ce6a77eaa924c3ccef1ca2dce60a6d93915a0ed72ef1efddd737d6e1ff3527a644940c341db1af6fa9ab93676ec97a9f2038332ab555336888815155d9dba9a1b8e03f3dc4e68fa81003aea8afb2fdb9608e48f8454736078d0debbc0be16a9e7101c7e6559e1e2f6b9793794426c564a0ac2612d2842ab453974678475b2391e0b8d9aaa26e1cd46f96de73e3a94bc92ae5d0f17d7e18528db4d444f2837f33d93a3f75e2e7796ed4b8031ee25d3c8f84bdc11a8298c821630fec94e76e46821cb1f975c3351414a4f9c48379ae87d2088ebe8b9116f9bc358cf770e9d8b61a4248a16d2909acad65c2b08a6c0dc60d53d09c59e838c4f0f1c716aab44e1dfd114f2294f033cb843edc6006664473d262991c1be3f261817066fab854d64570e23cdb817da47be1f9dac4394f8aa231d200dfacfd960e0f5cdcf96ae9efb0b07f703ca50b2d3b8b3d3cc0cdf194e84c676f3eca501150378d63684fcf4613bb32837a106310c9ef63e227e5c33872ca6ac421cb9e7c7d864352192a90a146e0314e5bd9f84ef527678a609848592d523c7d2c612ed23509920303599cd4a0ed794b0a5064d734c39e33bcebf80baa1be98636d1787e1b678d532253523036f182587722876f240745b41c385634f575ba32efc70fc162a1ef248ae23f403f5aa0a443145a76b9bdeaea3f92279f9c57af7c0d39d169f7a40b7159b1f0fb7108cee3fde500e851f6e57f394bcf7520c3da842c7755b8fa9b7d9b31eb787d7a2f5867d0b87433608c164c8ae5edf1c00de6db67c984ee2c6ff6c1a894f2230f96bc71ff709924c2d2ad0fd001e2c9ccdc735697168cb56063b188b193380ff0411ec4b4d77385895e7253b2371e3e4bb17667bdc9f226647bf9d0a59b1ea296860d9fe57fd70f696998460a8220749f2b08d28599825099efc44266468207b8df5eb5591dd4a00ec167e27ae536ae8e337931542554fac246b6f5120e0fc359051c998840edc109fdd81e56ce4ebbf9eacdfab1947f453bc034b3d2c27dc0928bd417a07cf5c79e225639a2f792a028de6885f75fccf8f71f70cc0b46c5f1d539a30d5e812f34afd09510aaff8fa4dddc0822549cab8e74126dea9a89aaeefb4f0307a083617586a0e2c508ddcd706162e22daf2ab62b13ce3d99d1fcd678e058821f5ac770d208c5078fefed67aabb894323331d3fbeb9b07ecba64aeea4e47918f6afd2256903897277bb1dc319c9cbc8e2ff6b700a2f99a7a9e10e4664525d6a821b690f3b41afca6d4b504aef3129a3e020a499760829b0086651e0fc8262e80ad8d7e8bd501481a2599666bc9a91f7e84f5184216793e54f10c1ac7e7214092632e603a6fa6c75c77fca21451731fcea82a5eacf26a0e2a06731e8f74cbdb7a77ba73a716ccdbd220cee1d266657f9aa96d9d1b9ab6b9a63ca7e56faacd8a19900d2edaec3fd32bab0fdc2280a4613f46d66ee7fe8523be24189fc9a1569d4d3db08e9d673e4eec35e862586773f2126ed00cfa240926df00a4e6471533fce91396e116474c3c0106f85291e72f4dc2f28163a115bac0c8f82dac4d72b96ef6141bc6c84a0271cc8ff009c38bb6affb12ae4df714c8d16bd880ff41bdb235e4f4f3679ed375198ca3c417d03bca9d68a9dcc8721a004479092519428c9942a80cf2d1fea4bf1359a0abcce9bc687105667e614333327cdc79f46d82a75f2b106c65700bc7c2fe13e1b3db829f677692fe3bb93ec59d188c96b38ab8cb27be0098ae03ba17e35966e73ff587cb7872717d8423254ed537054f98497aec21238c0916e15568d0a4bb2d348f4bebff37cd0254a121ba9dc099f059d1e26e829567f7ba94892b2e6bec56ed354a7c435f4dda696e12322fe789508ff8d59abae962ddfdc1f6d07a0a792b6c2498e492f0fc2a9c0e071981da593743964466e5aa8519997f355ff202c5b53f53c0df38af0b94cbf1f5f1c0a490c4e1e82d56c69188a612045c218c8ffbaa50dec70d26672b44ffe742e19a7f6fdd2663c58256965e953d367e1b1feec4362c335c1f6f5be3f2e2678df5accab67144e5f1d8c7316aa5637ef939915997b923018a2dafbf67e4c00f1ee5b0052d57454b90c204eea5b349de4f8658b1529760d362f51cbbac81f275aa24258a1f6119395a41b059d16923bb865a86be5d9b31bab77e9b0319eed1e88ee80cad6f9eccec4e0450aab8881e3b932d2cec9bab0c695fb0ddba3a238df61978543e1bd7e8555dbc85163f63d42f58e2762dcf7d05fc1e48984130eb09c6df7aa6f8547901942b079802dd79efc0763aa40d4a1290a9061effaa39b6b0db3d38ba62b4ed2e24677e4b5995d588bdaa417a52dba137a069e954c0bf9d893e5887a87478da10a3e5b2d238343c8975932ef3149c139ce0e52d704ed288279dd4108868e0c5fd4d408541643466f25bc4307535ed3bd21224e36b2f0a9c44563e81765c487668acf99733ca5eeafbd6ec2f03f4a4ab0da67e70c0404928832843367f7dd8dfa3e798cc300f067e646e4cb039be3f0b9ee84e2253a2689b4b233434dc537e5285d1385b58062363ae6ec809231eff1f258cf4839f6488b753c6b796ba50da9ee60ea084c936764dca09493d382871997036073667513e39f7cf84daa19e440b18cec99959a85441435bfd08fdf62e51b895b0c17d91247681ca79ede288f1590a41a93f991ad39882360c5ae27cdba6aaf167260429c5793cd5ccd9b1adef5016e64d981bbc6e58f84290e594de303dd5c45f7fe94c4ec6308e1ebc34decc3c5bc0fb7047d05e465b311ead1066bef6f532394d299dbb8d7171522e1b5cb450bfc9b4f0b8cf4afe402cf115c2c3d56f9f7dcb4428341ef1760e68a058af0e807bf42a18adef36a6c8dd3f63957416e08f4fba4f40351eaf5a009c1f3d50d7f9ed3e95fbae627b30ee21495855a4e512850cd9360bdd803bdf8449ddb9ec73db4f2c283f2ca602cb662438f6a1e227c3c135bcda7d698db6360d4bce879e81e189dcbdbd124c04d95ca03d5dc274f0f5011c68a5791f7a6c817d95d2bef506274f3b788c44a69464f2cb0000f3d7139b9ade9ef87659cf299f977d66c2ff1009b7178054ca3fa7db27ab62178c73cb38639ad5ac4b4f9c90c09897a5626750823fa3cc05a2537fd78f294db47a1f3a4a3f0df45231440ddcda4061a57807b421ec3c018ed87cd8fe48770cffb8825350f0ad8861e7a64cfcad27a32ea9370d26e0182db8f2db2c6582717a315adb60f48c00d2a966becb3e52383ca89c8012698d30286eae20cc8eea74f4011bb78e021da309366e49d047d1418eb580ae9b41bf260f1017d2f3f22b04061a0890d9da9683f7cf16a23336ad5010d61563857c890b1e267f11647ab93899b5f62151757564adcbc5202809efb6230cd1d19947b9fe6c38af442929805a357f3c9a3603cfbb996721101a13c5b2617512b416e7dec41999db32e8088502681933ed74ca29c75fe1f1352b4b94ff99731e1e4e79938ecda428228f29f160430a8aafdd90692c71b0bb976d944d1653f3c5dc8e567154bd8b756253e0e7580c0e6fed26b12003a7a8a13ac5ad76f45f67413fa26671ed93c0385c6df61b39bd6faaa109a0829e07143604f4801e5720458188e52959a5d7d6baca94f853d4c7065bb0bc0a789b60892ef01a8d6a6651be0a5803cbb28b5f3c3891b107ee99424b8d6069313d2ebbbc35e62c5805f0c60992c5e090ba1c32fd17e4d684ba47b851809d60113180c84adafaa24b7ec3bf08299051f3980117945db3ce2fd775d91574b1e4b3cb607a488eadcb387fc028e347cedd67e5163b6a34ab912383e3726ee01b85dd017e3ff17a06040f0357e474958ff9aa76a7c45c289b5ff245589b4f06287be8aaec2ca4b1c9c8f18a68c1954cb195a915f8b4cd8edde3fec5a9ec30fbb5496450144f8e2479b68e1e1ddc156405f45e28d0581eb1779d25c272096a1e856c190bc399c0e0c0d4d1bab1388b8fcc69e1997b1397ee0080afac909e7c045b2b181e6c6591e4ad92ca8fb6ca2e1602ba3d5881090048e7cf52791a5acf6ef22e49d18cca55ed2041ce1844555aeedc6334ab92b724d2f8962b5c264750a3c11f111af0fa4befeca44aa2c441726bdd51de875962ee9d673d212577e0494b893dc9f1f2076ea2b3af4555c2499f13d27d358884d030984c59edb302313af9b802246fc50a9664702315ed2c0bed99864461bae88b3e40d254a131a20a7e6ec4cb8081b6282a7be3a6e64c3ad1a0134be3d5dbed2c6932403b629b448fb656fc6562b68bd51bb069e162e09cd03dab434501b48d40481cdfcb9daabd62da73ce2a8a3bf20fc615f166713a7fc8be1ff082195134ae95c96247a4ba918f19fac2c26c4d5523ac3691aa5a5c132a133ad894579c03b3da7628cb296ffbc0f1c02b26d2fc7944e67915601c2d5184b79ab07f6119d595eea475d0968bb2046fa9fd0275a0c9cea1a1ca459abd0e309f723f2d4fcda9fcbf35c0287cd846c27b58bfb2df41090a7cdda70a508c473e33e733ede6fe8faaf627228bffe8085ed2f40a6936244ab85aef85c5bca5d02e0240350d4b564f65d6f27d3b9097fdd8260179d0eb1fbf33905a9dd9b693abe278436450cd299399907fe14032b97261eeffdbb20785798ad16774c7499abc7565588beeab1d9b62c6090d89c31a437633e82b1c861d126619c0d2537069f7f30edc2a2b3f89c9c0ef202035d7fda3d44ed9e65cb9b50b349766205ea63e24fc764c26ee49702dc836b427413b6eab33a95f77e0eba02375da3544ec7ec50997d46808267f42baa67ab44ed7d28aed67c781f8849c86f18abe4ef4c52612525d225ad2aa9335e7a7b6da33585bf1d6f4b592e853ea40d67ac1eb14af71ddc3b482026049b7146318c5b95051158bdcbe53a46329280273f235588625a9e726ff33c793ee859d082bd3151694f0f89028945fed400869570905306138e668650bff26a7f1b02a89654f0db86780258bce146720f3a14a80fc0cc503cfd3265ed272291cba4651cbdc0e4c3d54233b67a994ec94071e17b10fc9ad959c6863de13a3509103ece71197154f810b28aac71b810b982c5f69555cb0561bb3ea3b50ab5ddde324b7c4c19d2fb1972fa2a3b74e4a10354df1de5d49e35a79a8291ebf9b2fee7f6c67fa79058ee19ce729c060131d2af4fd861b510db865088016e153b3050a407f3d8754ac56616cd460a130f98c50babc8e9d575658cbed9d1d221d8b00651cc208058afa0b8310e85cc80a7ddb6eefaffc2e24f7a9bb7d886f1ab89ea8cd108b770f5b4ae139cacdff4a5753a2cb2a986ecd8d1a5672ae03184621fe4e08e210fdd2d3253ed57408c8d9f8a1727bf22ff4810deba87d44c8ebb40a3d7b1bb00640567909b401aeef254306c34412933e4a83fa0f614c494617c85cbd19061f9e8f1cc778453a8f749ef7c45c2ed2a362a8a9333c781fde05dcffc47a7280b957c8e75dec96b78878f9f2eafd40486538f0dc8a9e39944c785d1369c52172e5804b865177c45ed0e44dccb5bf2f1a5a6ab004e73e5fea79926bd0cea3a79e3a93618e933c9c954fa3a37395753930b1fafda91895b12c721f475c1efdab64105c874c130a5755b2daa8d82f4e39a095d9fa488902783718ef9ad1bb24e65ad1c63d9567cce61e87f6b5065a3e2929eb8d70e20aafa251934ecdde984a68e2f1955c91ea19b4e72a7d97c4eb0f85d5cf6bedbd8b12760395cc5b1f55fd90b9dc34f130ea2afad88ab917a9e66a61aaeffbb2946c79c6b23b161440637c1e1b19d7a79d489f52d347dad34711c3dcbc2a220054907b8b7999ef97cd1fc1b298a2a2864974c11208252cfa11882f9bb40d678d9d05956cf293512ee9c19b81fd6469576f48e8d1ef735fa2adda5059227711937d7cd7fd1b4f82fbc2b83b198759043ed751758359cd6da4fe4951974b66fa6156f5200164607de9b8bfb0fcbb561625f65e683200c49793afb5cd2f7ae0a9d5b3e1aea03873e66b1d68336534a935e6574c593d589b44017bd5c1442d51c1377a1cf7a020e84c220038eb037822945d2066bf8bf94383348cafd4ec43f15f965eba005dfa38c5f94d79dd0173418361c2652c4ca4014c6d0cda75a790a6c3b6c77e75d0c172c28e944d97def7b9bf023278373652ac0cd40851f94470874ec892d80e4205840504dd402b76863e2303983f840333d48bfc470146927868b43caa0f9fbe56af036f96627567684cf90fb8bc94437a27313e898aaf71de55d93720a664894be15e04e6b0692eaf21fa7ed279f8ecee91c9fd8f467f3010c54e58b8c3ee3cf83588622120379851b4cd506724796c55f7c137466dd480ac5c23d954a913415362dc572455ae454a2fa6092439b9c6a5632d84ad71aa96dc8574cd10d4143870757fda0eaa43bb958222f8d4f7e664e680ee7214a31420da84cb1fb486fae544dce469705c01619d9c14777395b892979c8276f4e656f3ff1923bf5f7da8e5dc75a76dd63ed1027b3b2512661992528744afbcb8384dc81d9c1addc6d13ca3b4ac70f33e04fb5c09c61d7fd5514c3109453fba7f7376747d22356e95a6c70e4c2d30e43d4f8e52825e83f163451592793fb1c708164ec42cd069b40ef372faab79aeb5d08e02aba14c8f89772c5c113ad6d4c635d718db58157d815a4a540b357f6686b29886a4aea35b1704d1423e1ea514af9eff46b03f7e40c146befa2e4a082743a62e493c56a59388c72c3b78637e7bbff6969afd01bf16b2fd8b8e0759dd5281b2d690b5445a2600ba3bb5515997af3c8d70a40faa3c1b6087b63274a8b953c88c22225637f931d6317e1d340abfe51dce3c8dd119ce2ef6c68a957288fa3e424ecdeaf16857b9189727cf047ea352b7e922e6735381b1a3e739024cfd994f4354e97e3df4227f08e61f1badbf04485f10658a42871c8f3df3f360f35c3990876a23d97f40af8fcad37b82ed38419ad85d44b05b3792b69bbf0a538118210d56c9d4333fa67820e086f4e56ad466e606c84f4cc9462702153efcefb9738e769f499ead904a0a266ec8e39eae699386d3d1a7e69c01345e61371e855938d850fdc6fedb294ae41bac012c82413f3f2f3488bda9195b8929b8c64d9e603e042d3824816f551d775c02bd3e042014622d81ea1455d3d8680b9020ef09b143345d436b4465d050acaa6bdd20dce09b342c0965c976d7e9c25811478f4935897280789c09e052abb292513246b7292ef97b1517837dbf7584d7826355e0c4c82ef4bd88cbedad2485170a98539c7c72774d0b2540bc64188c89bd4c05e44be5e66bfae8ca6d1844ac0aa6cfafcdb2c7ea527bb247fb12b663922516994b220407241a8c15df45c6b444b39f42ed0caa334e947ecea01e0e01b4999fc8495211cd2f03f5342e3dfdb1f314381cdca1b12f74ff7cb04bc79ed7b1a33347745c388eb07c760114d84ca408d1c2c6eeeb920ddd7f4b89be9c9c319e6a46769846ad9ba88646391849671d1224732c42b43c5fb2675a15156caf76d5d332685408b87f92245e70db3c999e57f8ee08e4cb6b836568e291631cb5a634c018363113cd8b3cfb6bdb6e542bc74b74e574cbcda6fea681b1b81da88f32a6412873c804b49388c3e136da31179c529214288ebf626180cbb29ada0c0e92697a3809ee35d5750a928a5a01ea0ac8e4f3cb615b002bca4909acedda0f333409b6a8ed4e3245f99bef2a4fc43a73bef2fb7e0e3d0e7347db10beec567b7f6d1a0d95bd9ca1bec8a6c67c091fb30685e80d6f9df290de3f41b8bbb3a4fec9566cea4745b2db16939930ce3bd515f85d5b054d88475c0bda4162217b89aeb02acd805495fae0b5827ca969cdcf76bd89480d51970bf68df2eea4a53d0febb1f76761c0d640586cc66e62c8f85144b571096a6c834dd44bbf2ecbc343ceee97e0570b77cbd8194313241a8d6f814684919f26ca4f9db682b84a6d591f1627ef86871d79e8a3d83d84f62c0c77325bb241a1b45e6580df458b80d45efcf3ea95b322aa279a250de6927100e51c66d354e259170e4afd76033d9977cdc7e34b2846d1e69aa3ba28c12c3b476e67b7b868fa73353a5cb2b228ed5ef9e33f85869e1186e487e5fe96b16a9b5a272eeee82fbc525cd913baa6052c51345e9e75a87570bb2189594c749b3f56d395d3c95392e2f4efd9452f95615b5ba1cb1786d18f655fb0ce96ea9525d003f817b37329f8c345bf4eddef37d1cf1bd719028622cfc2b325821db19f7ba70ca00ae07fb9e6ff0df889c884134f86796a680473bc76ad9a9b8b79ee7ce6b782d652615554de6fed04bc06d4c5f3bce8afa8171c83943ccd4938b296410aa9cd437b9fe052d0d9d2c078716da054ad7b430e9c20ed62cc48f3b0b12857949afb94e68e2d7779447ce82038a4fa8b21276704162160b5ae71a9a9836070e28496a2df502a8c404f5a13b6880185794ac46de8f1e1450c069cef43213e6d7d067b47545ed0a2bfaff20f11d798c13cb7c0ea1a29814602aca5d6a7d1116f97fb6bb8928afe32fc443b7e03448dcf4874939d2fb9dc54ec2be43f11509cc9bfc49e80cf4552cee392d321a1e4529a54b904ac7a88097663d64d949d90f9df9482f866cd027d9394816b029639427f392b781a70971c310ae20e3591e59c76dec0c768c1d3184d78891321861333118b865d15b14f08487316573211b30fe70bd8cf11715acd5278cf10a3bea32221d5269fefbda5878ebf5e45ddf2f580b1e0a1f19e3bceae34a3ca7159d3fbb125fee3b28405df5d8e0ae8d9c9cb25ea15711ee33faed09b2d18771b047053ee9db5f54c8451d475a20e5a17b14d88d1c8301264099a13358d0639bf96f600c542a812424348cb027f0497f9fe7b13e5732f99a389429bc1f11394787aa361f293dab8b93b52a80a819256b34183c7a99e35c1017c6de0eeb1da011d07809e703394b3ec37148ca8fea80e8692c19ee376cb7c6ed665c16753b526e43c32ef1f1bd40ace941cb2aaa1e66452427fea2ba8a75fab091612876a1e38d134474b6dc3ad6b9fd9b3f94809387112a9dd0ef897e794285fb66e4ac3eb393087579e873d8b645ca62407f0ab9d8095e7d9cbf75008d63c3906ddd2f69b194ad87279b308c445cc5a508f8a2c0227180d483053a945664be07935c3d10bb00bf16f8b34444fb3566a0bf04d0df57bae4bdaedb39497190f3556c51d19fd7353f41971cd0b7ecf145af628b364c841c6d99ded3d8eeaf3d12bedc1288585c112809dad72c3a1141ef622ca7cdcec4ad074305f961212c497d5e3da196ea4fa4b87e00ba79012d0914e225461a172ebf1c89cb49baccb27c31383893d463c2ebecbc5d4e4a2c3b54d0e38ac21b90c5e8a7dfd820d074d70f2dbc5bb1c3cf01c3d8153fcfe8e7768d801dc8904f5f2a77d0b8c91dea52209aec560413126372ae7adc81f0b6ae0d884be4298a59524ff80cadf0ed8b13804f9ee9a5376cc241d093b5d04bb43d01633c1c95082b975a7bd22f27878793d7bb7ab12a665cfc727bb4b30bceff3740683a3d17ab1a07db3e5aafece1ade5cbd1c6506e4cadcbf36a82c44c8714899b699bf1a88637b66f2c9a529b2c3875bf03b4d20e451bb4411dabaca8a03dc5cf031028da62da85bc971de37975f9f4d787cf24de9f236f7fa543f59c33b8da97a6c43227be219de69717263d78605fd8a262ef3bd56c1264c03834d1dff8cfd00fa49b9a18e46bb76633bb89227e609978cd67678572dcfab9405093fc8f92fa9fe8ea643ea110080bd1bb65c107c6d1516b5d8ec820abfb7948d8a91bccedd2219c6f6cb123ae369adf1cdaf44512d425acb64e3229acd232da5259af0681efec2a944ffefeb1cf80774e41f96e488f2f4e3d9e5252364cbefc358e8a3603e4eab29bb9362d37c3a0d0da615934e2d83c51fcd725a16454ca4389ea59efefb34277ab9ec678acd1c8bb2bd39db618a5816098fa94d3fd1207fd74b547ba1db55ae113048074599f588083968a4b4b85c0c84248c2454c3560c5910b455b2a3c58afd21f71767247e475082ab953b7e45f5524e9582465da05eebfbd6997e4f992b2f4f5a4db881c06c0a4d7d67bc1f89ea57781af84669ae280a0b005cb07592305274b13c07cd274cf89909791e90d7ba5677cbc703b3ac5b6a2bbc0458d9430b0f4e52040706b70b28e0a3d51edd8c334f4659e53ce3ea64c5818b52cf7a23b852634ae66282fed75974027c7a2fde03d72f33a0405017752da647fcc6586db16759d20ccd138705eb10887dc2b1bb9f8dde688f2e05b4cb06bf1f171dbcbd80c00711a3b064f5078878e0587d35072b00350cc416c71bffc577ba38cfe9e967af0126ee13f70d83e033a285c5d09334bca6c27ba71d4e8e328cd8a3824c3210daa9f7d9a57878468ee0bca12ef01190798b1a3ffc4228f214d449333111590d5e9085957caec71d2b76522de183abce98e283929bb0c3c32741a628e284cf4c42337dd292702d28e9a063b65193aa756b9b1eda37e107a249721fa66e0f566e236b042477cc24498bb8e115ecfca982a118f28991640a8502217a6d9af9a44964c5451f78eacb86ed401f0986db768170609ef253a239de19d036e78063c364888868eb4f10c2f1111fc102250502d7c51fe605aa6002b85e909bc48b02bcf7759d2e4d8c4208ae03b9c850bfd5234c496ca985aaeb6efd7a03c2916e8861f80379887ace64a8e5f198dbcc28c6d44d9c4ce7e529bc0ca89ee8dd376702f2b4a8b5c505884c3894d7888b7a620bd351be945398c0a85df348a5c5e1e617938bd8799b804ffcfcfb56b9466af445b4c140b745c255f69a6c79349a4c32a1cfd44aca486ff290fa160413f9fd7e16088e5304ff2428d209c36ddd3b0f651d636f423e24e3320371b8b03a12f390054ccade002aae2422e84fda2f007b3e7f929ceb911de67daabeded28e3995939401ab1ccfed6cb211e1fb8e9c187518c63524fef89127360f20a76c851bd7c98df9980922f9671015a120dd825ce4a4e670314d0d5923d733f64cbb2ae0954e47e2e5c9cdcb05f3f7a0d2b91111e5c1d641c654b3f12f543fbef36f58b32b80270d27e48eeb3d44c9987368972cd5938890eee583a4528daa243cb9d2ade01318176a1ec83400551d746c05257377d79dafe0e0d5e1dd9e0445983c947223a40ab65dcef9f373b469a651da47e74725e73d077ec8e6965439cee220736b156d504384abb47256734552ea69b7e3c8c6aefdfd39f684beedfd5addfe6a32c8563234d1c9b611a3e59682de7efc3f9cc9523a56e9d1beae5aacf5140103989c2fc295ad7a234caef29f09fb5e6c8108683702b27debefec2078b7bd8f30d63b3dbbe5a09ecc83aeafad2f8a7bca3eb97811049ac0b7062fe9bcfa42ee160a889461a9d8480ec0e816c7beda7fd032f960be7adeab3b9c8caec1322938b0c99fb87ac18e95726d3589a2bd914ee15614c8e2878ced75a91ccc2b267971f6206033d6c40842659c1870f6f464d0c4966ca4eafb8389cc5c979f83f208c3a8d5ea6a5ecf2902a3fdbdbe7d55b5e16bcd1e2d120c58a4eed2ff44c5a23af32a21eafe5274ae675bd202b1781b7b977bc458f31341cfd3a5fcdcfe4d16fcb1bbc71d36bb536bc0b41860ddba15cd57ac95a4ee5a80c2ac4628206b616561048263bf22c5ff297ba0589ea6596df46e90b8098d1606bbc0d8cb5c1374f38568380e479d9d93ec01835b218b3ed3b6017d4f146672bb16272407914d98677cbb03d85075738d915435a98ee133ada2bb344c77f03229762d1a06247fe9559683c493fbb80a63901fd4f30c9ced26ef9665f39a0cb7827a2303ac124d2cab1ad8efc8815588641a00990e4063113cc3ce6e48e056335808fff5bb61c61091b26e2409cc591941127f89df7548fd6e0a8fdbe87e72265b03bff47b848ce41d279cb386505bcb86e1512a82682e08f039accaaa3f1d821f8b8ea833e666c2bedb0bb1f41ce8b1f41b25704321c5dc7982e73cbee39979d9c8b07a7c1ad72f2455de5e6079621ff4397c95624d4c4e44f563a56fb85ce99d6693187bfcdbc7ef206e312f597f6ff9b4fd2ff8f234c3c873d66afc7ed2093f3fa336ca02f5d93830634cf50cec09e5dd3433f6de986aeab6a67dc425b6d72fd445846b305e31347b76ec7c9e7d26b0228e50bc1cadbb018cc32c211f40973cdf5a195e6c31f891c26d75cca0749b4b01b4d02309819702979098e58cca06f06ab16a7ed5b09ad5a57cb942e6bcc010428c38bce727881b00cfeeee52de91335e62b9b31c3c7e1ecf528d05502ba54c4e92d902da8bf41db79a83351565998c25dbedd2dd05c068fb2d08325c480095ba9da00320370fe0cdbbd9672169adeb1c5c013deb0c6f7824e796a75f927ba961c7da8c82de7a2a60552eb63cc2e7273ae33caa3e8c15def0aa967dab2c1235d47204ef60e8ac14ec39c36dc66272a09d368693d64e1a7d8548b501b9d64436f578cc0a91d7fa1ebcc0711a8a396a00f098dfc105b8fc4450ffc77fc1bcbfba6881b0130f04f78fa6199ca3e2ade1e464b2d54c3d9d36f3eb5f6c814e08db0ccc8a76b8fe0aeb85e22cc28c6b76c48ebc9e4c936c8cbfde80d50067932a70488eda1cc5418fdf0c1161aaa3389b47e3568e02efc6a8476a7a1c95edaf4cebc1693e46c57d739fe01def2e988f36c9a2d86089a8205a8f66a017ecd373c5fcf5c9a7677e5bb2564ef4187fd61ef230d5bedb92d1f3376296d6a1f738f8037c5425e256e2d54a054a277593a3698de56f2b439364c9a8ff736e74e993a741df893fd7fe589cb2097de9d4892cd0fcc27df14b77d7d6e2a4e32781d442a1e5c3ff5a487038e8bfe5c3aa06a8b44c53c4e3fea58e58c9d6d59e43f9c455e633b397bf3547d611b6e0fe3263b90fd73e503155d8135139d6ea9bceb09dfcbee6b68d518ef4833102308f05bdd9bf5e872d11da13a5cea7090c6215cac0eab1a07306d73731c952939fe242c1b2898b4ddc289d37ffe24a95ae4157389dc9d49c8b8f6244b2b55150a2b45e149a04005988b148c8820015d5117284e9fb3b9a927bfdbf90611f5345564eac866f1a22c833b2af3582bdbd6aaab9d683f4e45240ff7aa142329b53ecbe889501b987441c07c799f30585fe236fa0e10660b4c4bb9bc5daef4d047f0d4b9e69de3d5198d97343f303090520fd3832df14cf52cb583e3a6ae78a3e313981ecd011150c5f52546ab710b1d5064abd2d6e1a250b1e325e8dfd948e9b4635af7f821fb0f95e8ae8cdfdedee00b85f71430b41905ce800b2fd544a4f19afa98c9ba5019845343640308146e5ba8ddbe8db12775e516ccfaeef95d2ca8eb6b4b7c1fac90cb34a6f0fe87649fc06acc9d45139c4b2d0f4447a9589fca8468efd3465d36e01ae6c6a556df1417ce4da61366f3b4359e082c43f7832b46c64f52da8cd249d3c30163eb948199532daba5c35013f8ff5c0f246adba9cf79a03f0ef80038feb71211ee59bab93d725d11efff58b77a708bca225c6b40eb87bd468051d9028d183025c67c6b9b8e7c1907b8ed204326cc6ec953a385a8b13b532a145d74d78808b17360117c68bef7be55f3d8f9f1910d46be16b86c3f8d810235cee45e0945e111541407b8f3898c8ba21d73806765582bb4928b726323381f05acceeb205f0f65fe50fc70b016cdee488d459c4f6215b24f8e0f5e5dd2c0bab0a1b2adadddafbf6e10a9ff2ecb0478da5d73ee4691874bbf53af8dad139cfb2a97cc9f3b5be8edaf5602ae0f6b76fb911353420ad352d76693d5b827e4604f7d6ccc57556f35a9dd8a62b4676f062a3f9e7f764ca50b4bf98e15c5fed57208ec61e6aa52410365a32a65e31a3e93dede1b81ac0b921612618fcaf5137f0316924cb2ba91afbc3f088b3412f0ee0ea5c0f05c771a6c00f637c2ff4dae3abb303c073a4860960403e720c204e7f1fad39709caa27d337b3d8f572b435cdfee518661a1fab8e64c7aac1586b56135d92d04ee9f4d63bd516c0b8373841cfbc5d25c7e808eab1e4561e0a7a93ef7c731dabbdf6405a5f8b1bdd92a736942ca38c1eb349ee051910a27e10fa7b91ca5c36b8823aabef1560e81e719523f575bd954e8e7339e6bf5a9e8898e34a5044c255519bda83282a5114250c62d9ee4ddffa2f527888c3ac010c8901da01f4464842662a07192298eb8ceac5f06aacd9ec8c095d83b6fc4750ef763061710191f7f4c928bf0213922e17cfd6536e7b33fff955b81169749f1a24fb6bf3cfdf9b97156092585e526e78c3784ac03e78a6972122ec1f555c61ad2cff36ba604390421589e045a8d8b84c40507adf6880059aa38f437afa672929e0c3aab215180009ec6aac1beee8a69193f681eba3c8191780fbf1e20b8c2f1f62a7a115b98eb1e41499b8844d362555183c6e3eb8d203c692595ddf8712006d00f7be4fdc6b42c90b9d761a1a391f2804a517fbd31befa9c2c66238b965d8aaa705457bf331492926aa7fa412072f2a55d43b667c3572a716943480992cfbab67cba785ec4599643d24691e11e90ef8a5fe71226254ed1578e01342370d100a0b908d55a319bd39f03b5e4f8ef37e08f08132b861af9d7cfa2472173224eb4b1c38bccc3248538457c8053a530a8ae51adb598f00526cbfccc4c04a98af0306c82a11749d0aabbd1b6b792271985770eca1f4997a4ab0c6d8d5338515f50a40d8c8459d845a8b275a4ef9042e753d4f5db404563f4b071639572520742d9187634997d189dbfa0de42ef91b8763fbbb5b317d6ae28543b9d112125506f9e16be829ca297f0c1b3df5090a63763051609219350dfcf5fa94678abcd5be8ca8339671a17cd1c03ff374ae99f51bd1603a2fd9322b3e8e4cec10fa41f4cc756bc46f3a1ec5d0f301ed8ec67339f317579e66fb081581c28588ae07f415013c91c18fba3b17708d838dd92f789e061d1356d24ac02c0dd0d8d3ba44f89d5c8068e967ceb94889e3e9e87064450dbffd478324cf4270e13f38285755964f537853fc403042b298e68b8368f58259cd3b0ce9bb468b55bbcde7aa59f90b3b4629364c128dd11106a82541c474d47441defa9a7f9938c439b8a2997211d64c9642a607c27432e8c6ad1ddf089a30ec1f4509bc0c26b922cb9fd16c68cfa4cb6db18fe540352eaead0413a5469fbe9a65cda02c7ee85ee788a1fce28727b3a4e2eb0d320e1b568b040b8c4bb85aec536a096d12d0c2ad7adb391190e3a986aad53d1cc8640f6f42e1375e6a17673b6af547e90ec9d18f0c7e447f8109d111f3446b3c72ffbd4e54d4e95daf9d7b1e57a0a8dd50c5395789b2d50e58a0598d51e682997d6239a6235c1e3c1d0415cd0226df024a5d5a4c27010ab4488784b62d83eb2d66ff073183d34b028a101ff8c223eac5731199bdbb907c02cb845d3abd65008a26b44b5ac8195b12931140e5271483179949ecdbc3599abef333c0305bd4f7005d9bf6366428191c05f3399ce9027f82b5f5812e71a14ed8a77c4d0cd7a8b9084e4b95c997e3b4041a64a454ff9410ceba45f069ec5ac8f434dc2172e147c05c3c255a094cd68609be864c7187b95aa7c5ea772e295581975fdc8624c10bbb847b41986a1a18f6385930a229bda2ed9acf82e043662dbfb357c6bdc288f4f2f1dd9f77ca03ff6f0466b223fdd86e13ab702b56faa9a1d2124363f76b2e59dd73d62910cf27ca79b55bcd0c179e0a1eaaee09b3109ccfa525ba8aeb28c5c329b5aa16eb2b00aed95bf1f3d53ea74f7af1c0e152137d7690c7e59a8a7442e6505a20e556a8c6d0361215711ab69d2ecf02ca479a40cf4479b4d47c8e082d13e4a78a1fb250f3a00e2a7b03470a6e790f266847048d73c388ebbe0576a69a21867b50a56d3240b84eeb1050ce30f51df03facf240fc2737b75f20f8c1f0bfc00d985ab818a44c9c3b43c214af8684d774b88823520d9986bdc0030b48be3261e90744ad06447b62123fca6aea5c073ab635d7de7d15104668d5471c72af988a05676de3d14e2ff8b2dc947626ce4987912034bb469ec6d324f24d2e0ee99d784db08fe7cecf5b1a85aa0558531a752b115c0f2902828ced5ae075662a5715beb97eccaeccc5e2204c011cfcd0ff0a73b3f79512a78d65da5f2c448e41ef030c2b793a33b53233d8ca4caaed376b14f1439904c84c6a534eae9459c69948654fbe1094bcd09848c0bacbcc65289c9ad8b5acb7253c9e63d59047c13374ef98a28495f3dda9c917b0174726125df9f9242e258eb46ea183b467b38bd29773a06ae66edc46e5d3ebb7c30b5c5ba2d6980648505cb5769e37af64ecffc9c9fba4c3e1236ca722b7740ceb09f8f65017f45029694eb6ee9366a50715d36e507b1462eee151948956beb9ab25824611e58059e24bb288efb57bc0ae052fc7184c732962b75c3f599088e0b2145dee4b46cd260e50296eb3c4334b7da4757d30da6ee8a3dc04250ca2ec6158c4b39c02e002a30545b021a93569152362e60a4fce5e2b0adc835fb1fbc91e79c5d32e3bc4491944abefef218349fefe0615bb64ec6086ed398ab56074b98787878650a5d84ad8688c1a4815f5be54c546513d381a6e744de59086391621abc8062f4523f4a3ffe4e9355cbc8ba6455ca6044d64691c215b44bef2ef281d8a2074969fc411ce749b3f8de30346694764432f467d152053262610252f89f67f47204e3152dffa2b00d39b122377c1906cc63ad40f38e4bc1dc49a3606060517cb5453d0434545ea8e02870b859d2797991933fa02d40022fb9f4ec487b812a6b2c551656b6162d6ab1759e212cf2d42872a43e0e3ec372d24f37d0f7a0e6daf41a95c80bbff0e0bebe6488af7971bdee534414e3251f37d3ac799860e7f5fd4b5a093b152940a8b4bfd6a2994eeefb0eba935904531738fa657f40969208c7f7f2994a5ea02f7a8ec9d0dbc9c4cc21d7bdef28a753fb66440fd19f869495b0254b3fb5a76310280719d06aa424d6c656db36ca44013692b63648166333396415c843628f94abe19067717ddea9b9b8f4ec0f5173844da8ddb29f5b633229295c741d50b64d846a6919f798061e383158e3dcb2dc83e171a1a0db89c16000c92fb1e903789fbd75ee15ffdf1e465c35fff4364afbfaf0d82d4a5c1b64c31369c2a72b2f484b7b5c773234f066760f09a3b486519ec1804b14f5ff005528f40c6c140be2b27d7a87ad1cefee41d59701c540c5fe38cb3f408a28ae60fb1f489c352821e5f3eec3b983c7e84f070902cf36a5f0329dea20ce823e7d52cdd341faf293e4d3fca3b6b4f59a01d18d4fb3f0c8ecfbf3645782d48937c02f98d68dc1790220554f573186f890bc1748a4597574a351d09bbe7cbbb13eea38a36032b7520b900d31dfa05b628ef1c880c30eede5673e8ef87be4c7e1eb3e55b866e0a79dadd40aabd51e921593fb3eabad598bcd788bf04b2c9ab31c7e75d4e10c224a11e9cd4ba65f08e3cc21b01dc220f0f6f5ed04294f40b74bd946c45b5dcd841042204f87a0c9717aed7ab3d2a7e0cebbb3119690915dc9aca0012a7ef3130b7536e2e6ee58f192d5b0ac29fc0b596748b9540714395b971c789a720136ea2fa8be33155595b6d6d6772a90b580180f8ce6841253249b30222e85683a470d91e0ab8f4b70bc75d09e98134fd31aea8ec283a8f09de0a7ee68f366764003e7f76ae6ad0b9d7cd06599c9ef408b1dc97c52c82833c5317b09a78b3d65a375e56e4778f4b56e9b52f5b88efcba089e1a16d9a76bde1d1a2b4cbd5482a26a1aad7a7c7943776cf268f8881cfb7c09d80e120685fea9d962354b9b78278d0108992b8e7119a2e11882358e84b6a0ca60e7f372ea5a4288b6d51fb7484338e9e5e9c055cb4eb64f71eb6fa4a97ac46df0ef065bfd2fbc7191135d0314496d5d6f6d0d4ea9d15c51499160462b4659f6f67cb5066df22d47c1472e245c4bd146381a3aa6a735dc80d6eb96ee6631cd488ef3e049664085221c92cc74329a41edc4081a040d95204220ac4b61e7d42aa30e2a0e28860eaada6da05f6e0679e86e822c548782637334fabbe432b30e7dc3e7d8b0a66d3f8fb007fb6a188bf7533a02dd17b61429582066fa45ea122d13a8f2f60397ee7b184d750926b9fc28f8c4a6d27e452aa3b6a52f239f446ba9eb5507c1f3ab679afd9efd684f1845eed7a9da8cfdbba0bb96f3a2cac22efcad9e5dbf43cd628cc9c14b5ca529d572770bdd9507b3de01fc7ce78bd3b4d4fb9ca8ef177eb6d58ddd0812329467668e28bf966fb571cf5643c7faea3059afbc912ced37bbf97b964c2784b009f2928709a1825dcaf8ec062162887248ccd996342aec5b84102d5a6808ab3063cc6f35572a62e1d60575b5670401b140f2f797353a151969c240abcb8a20228c53ecca4065eb49f49c471e4955b3b000f70e0a4be99a45fd90938b70e66deacf1b407386deaf54dc40afcda5a6fc1ef220c125caa2b68bf22ebd66b1a2b164d048e9b91f043cd90b1eed4d2ef84b7a24dd553fc67acccf4008e53f6b294901957b12f5931b6ac2b113317a808dd0198c10ac51bce247a1bc793b8d6e96222823580bcf9d5e2f028cf6116b88784fa1477a281d1478ca9f35bcc636c3f7c5a281d85d147496f99f93eb35db15e8cc61b4d7efc7d2b79128fb569443dc1a64dc61fff640d6f6c766e0d12215657bfc6f62c4750b4505f78ac4e10a64a32fe686b7615b69c5c0ae92c0a22700f846c218276444d40cbb17ee03299ae07c477e25768117b896aad7714be3377a34a3d45c14dcb5d9f716d6a977369d902cbfce970841ffc4f5af51501f3884f3e731b1a35867322e79ccf47e886d3b51a4a280833144b7a5474a04d89b0964cd6901b48ca40eb973c2bf394752dc574425f982846bd873182f02d91024493f710e6c14c9b46c4a1c08594c76df400da0f2e01a8857bfb5a898582d00ae02214b6e6ef2c3454378e5c21489225b05db4ca9352e449d2f2eee4e5bdda8cc85296e8ac50286eae97a543630f61db0d222c49d757cc95473c494f063da26680a3b9513ee5830a644aad338cc6feaa84f8582bed672ae3304b0598e44f3afd3aeafce0814fcebc18e38b4355e87072329138114b99cbc3556467c8619dc2c96843a39cf5423fe3c5f823b705ca74b8e12b46a32ad5bb82ab18456b044bd7f221badef67c1bfa5b8b8486a96f32ee7604e8136b2d3b1463569c224fd5081938db3842338759a1e62d8975b51cb035efb896ecd57b988c9f98db4b8094598883b5774d6e1dee0ee66a529f4138a8b523d5f4f2ba775c60f15d6eae4df50db6d3903c251f32f6e456bfd982dc5d33e1a89a497b79b9dd5ab2b5bd0f546a63e5fd02c775124834fc825ca88a773ed052fb968884c1f2d86d71db0831c29fb86fc8f1f562babc4a55819fdc726bb5025497ff15f070fa2081c07cc1041d733fc712687c483b40499f62515e8b23b4cd6136430e1f8114dcf68937942d3cd88069bf34d093fd49edb71ae438b505b0db13f7fa85fc3c3f90a70a73c1eca04b893d396d5ce548a608b56aac3f461f3e1883c6b6145afade6c7d47486aff547ffa1e309464cd51cc8abe60b32dee670bf29f10c5d6c2f7b35403ccd563006149c698f491129f04805ab7acbd67fab2c9f2bbfe74ad21faa31db21bd92974f4ce58600d52c957c734a6dcfc42d1043c1063cac03d47808d022b21bcae03ecc905fab4299a5ea87db60ca89c75f3d0d2d6df536c884a9d4de24943b1010180183ff4cdecfdd7ff9241fd9a3f61ec02c3eec0649c7ef1927276019a35cc76d40bbae92d2ad134498f5c8291206df0f47f7da3b3add1c53e52f71b8b45a48358c0baf7b80207945d21a5a14b029f29c6c70cd2d0a7bdd7701ae00ec42ed4f9ca8876dbd0f5a4a714e3329225f7429d8bef3b3c99d5804b695a2a18d2241c265321802d0ca5994d49fd05fbc192997f3035d7f355d2f03040518916a39b8fff20832233f1f635301b10479b9428b107196bd671ad3c79b062bfbda63a66242763aea0c90d8f195331cf40e55788661425ecec129d767ab7dc62db50a9c6a84f3822163ddd55f8aaa6f9070b4fe71b9257f7fe4279840e06a57414ff1fd6f14506cb6ae73d3e82b04d2cf3a883b16f3c95d1a56046b6b242048c0990072ed398f2284a98ede53d156c3e4e8476ff4f68ec85cde5dca377c65af6f9b4c88d5821179841d8db743c4295d300d020c28541c4b5389179e5c20b3989843763fcc75f9d857f5e82e163c75dfe661eb532b4e5578532764f12ea9714994ca643d5d1029edc9baaf7000df0d511dac74852796846c1140f720ba2c0b81615470467bc1fbf81af2b421fa74f0a7b3097fa2f612a1c85999e5e965986774b936cebb08a1615d8f89af748212b572a633b99807a0de10d7eed93b476685f9203cf8e17dee0cb5b3badecc917645e8fe4fa6bfb31a59689760ed39378155441df9f182902d7939b78b6dc8a6cd193829dd2599a328ae56e931a69485f2420d909b73d9da4d84d927462421efa566b53b5228a0107d2fced97c73bee3e59fa9b712dd61f4a8197531e9224f95e5559bad5b270a3965b34fdf71ee4cb7b007dddc6e84e02a8610ddba6924b7a9ea6912e07d494c8523827b1894ba6bdb50e17f14c40cf92aa43dbd6a21fd9b01eb48157b87166b72d1c59ca7d6eb591d4bdb5a9bf96433a7e90c7bd05156dbb39fe5f73f3cea106fa4e084904765faae98450b1e28076b2fe166cc501fc13e348389cc00953170fb69d8d17ba463bd930a6b2fac7a720f214b0c0800f6303326f030a2d08d7d76b8d8702a916a057510362d2b2dd0484e9762b938f4b76836b77c4f7883706d9d1414ecff5c94e9cb8cf875985b3e26fa47e7ca633fcd83da2d6d97df527ce3ebc575aa9aef2890aa645f1ee506b4524da4930761a56a4536f3090473a97474df0f1ad17b5b879d385ba251ac5f9a5c64b7d929518719896832d579e0052db554ab831fff57550b9c2cbb03ab063933e05b1c0293f43044f9dbb623825ac23e12b1fc4e674f0098f1acfab472b1594b522515a416917106e990b8ce03c3c34091291c4e8a813ae3685f0ef1a98500f94308a1d756dae7cb2a5f6e658950c04e4c85a4be542a479914dc310bf9975cf4f322f5b52379eb11037ed306ebfa3655f630ce5fcc01e9f9c98166db4f5fe5e623db08419def7fa089d080841320d70aefef0bf2a4509736d4381f81925b79554291f956d85ec7962c30373e2240361958d5fedfd12f05eff5a5d06742e4678de72102b8c0549b00b2d21fe392bad65083ecc6012a327240bbb1ea6a11677274c30acb7e1e5af74a3b836b195388c735e1b1f97a0a6962a55d1d9f5c813896a7c0b1d1625af4bad9f9cd41c8c43d778dd373bf0f9e755d83c2a86a444a63c997ad0447c40f54632fd0670905deb60dba56a574d4649c51f7a314de94b19759f6c873753408ae49f71aa7b714329dc36bc202a6e976d140ff3529492b5c87246bd7fdf145c56e8408bde28f1f9d5e9feda39d0c5b952a073dff8627b7ea805f9d57b96121966ae616751879a27bc0cd9e7671d894aeb0148c3f6c6b94d53e09bc35b44753a9e4d775fb5eec89dd14b66cce6f65938355d55c0c5a3ed5d2d6ba02bf1defc8d29f330871c5200e9ff49c4ab5f0ff17159cc96ecfbca8520672cf8c917a4b5f3c632e862609f695b774ef219ff7bda407a08f15b0f75b81247dba1dc1dec5cb247d8dfebc17f17520556ada65aa06d490ad59fb8e6c49a7d9f7736fa4af0afe222686b141fa0158aea41ad1e8706f3fc1692bc32e2157c2a4e200350c06651d176e9f0fa304d0cc72a4931a550b6587f31c7c54ed61ce0e010efa047a58c50b2a57fe8b70e481b387e37da9976ac9850bf24c901ffce85072cf5865dac2a908f8b882047795b8bb78919c2607f8dc3c5a718887f70d08520dec343fb623efcb9f448c1550f07c1ba075b910c8f916b65450faaec150dc6c2d3cfa2b56776082c831e885cdcb5b69235d4497e65fab67bf4ff8839ed58004fdae901f09aa887e147bdfbf5d65c65b1999084ba0f27058139ea1e60c0d37cd68948194b7c0e8be9f585c64bc6c5100ce9b7a840c1b06714eb6f7f4e2cacf54326a33a49d6480b3742d5e3554f47ff171e187c51eb6aa0976ed496cdd87ed71aa3598c8c8691360e181bb2247e3dc863f5cad877b7a5eb97135b53dd7176d7dd3006d41744b58b861f889eb4468750ee73278ef2b21ca1d5950073c1e926da58250b7aa7d6478c4c784f0bed51d158f0ebe6fe26330b56c7d07eb6809de72265587175e8cb17e3ce22a05ba4500e543f3fce8e5685017928857d4793454fc5166c922f2cc327b8faa5825437b6d4addab460aee7116537f3cc418d47155f41b21132578edc403ea7f62d4e58567f007ff4c61ab1cb90d0cfaa09a8277a97e45a8b6e78b162a717221af6fb13d515431aaa4a5f1ae85296165a6ac3363d61740b3ab36e1edc34e8e73a022d724bdcd3a980ffab6e1de5e3e8e1ffa931f263d5af061e1e56a83d503791bfaf1c2711d1ee8280d94c371afa5baff85b174653d6c875dc2a9e609f4484b3cb16bf99bf630eea650ee31b31e9bac271579a3eca32efc7bf5eb044ea4589c080ec20ee8aadb75cb7bceb7cc6890a3647ab4a28236f08a7d7fd1d82f40b82a5c231143f6261e17307c6095423d57bbca23bcbc7c95d3a72d2c35b76f67af91ebff4e8dba568c277e13978078944ba48ed8b8e1758a17236f7c5dc809936a37e564f8c8d9d732019dc49802a5ac3cc039dc8c795acf1523141dce6eb6e9aa19fb4f72852806595ad795a88ecb557e5e7d3ec0b85d692981b1d577d4c1668b822efd5c3fadd2f4e82fa47e701e65100a11d776c292f27656edecd981e89245be302bfa8e5e81f18bdd1cba40a5ec19656b02e502fc18be709cc1699f1afc16ca9353829eadd98066242e4f88382bb879148e9da0dbe338fb7d083b6fbe66f3c816a3b20ff266889bec9a43932dc638867f85e655f1384c923de503f4c1af2afc68a0a1fbc2297da0d7ea3587929a9d2838c8e09a535aa4b76d6a054e267e3d9132bbcc60447606306dd0ced5c386721e0403edb3d8dc2b64d09f9a239d512b04e0d6b3814196502292a118021364eced07821573f4cf3da7d6ba94ac75a1087c6e7a78dcfe2f8b87741cf156de9d449a58e552df95b6cef5716428b37354f98f62023887376ab4824a55d27b8852181028d24bb517f0fcf60695aa465575b7d16adc11954f650496f52d24cdc67593ac11ca4291676f48fd23a342dd4b685da3332ab5f4991be8a6c6e89c9df6ca31467d49139b90cce8c4d4c06ea3b35ce854f143f1c03bb7596f7462b25f9c8262d49ad7dec663ab49a4ae0fb0991d0a35b6dccb1f2e7dd5be6ab4a33f302331fc2b2798cdde9b71596d2319478362bf842471b7ff025bed5e28ee6c509cf363e15bc4a846241b62a212c39e895aa38478592d1516ee7b5b95c1be73ec1f5a1285a9a09879cfb76eada1f960f23f59eafc89555a69a21babf1e5978eec61b5c6a626d0c22865e14d30c6b72ccfb049de909c0e5893bad84ba0b090495ebb2e3f0ab28a5ca4d2ca62b40030a16c9dffa9136916adea9c9d1ceae7038e2ad8d831a2734b87f0d7b2f6f4358785e9723027b308fb39385b8e4763e6e67fc9b5c8ab22928039684407cc98a5e7d37803e6f4c1acfed4bb1c37e5cc1963d3bf4c743c06633c68fc7dec89c9e05e61073b5a5d4e9398ddc8c7e0bddd5f35923c931b3d44372de55da5b9506a1ce657a24645f93b58010077e1688aa9cd85404b7c4c61971ca5ed1277364535c2de422f01e082a74240677c8e6289f66e51350561b86de8f2aa2441913567d00c7aefb18b124e53ebf057edda8d7f188b653e06ca313c8ad9d723b8fb32f1a27bedf5cbfb6e1186c367a54d0fd303d7bf0f4898164f49652ac71e0b38f0052b202d93218592a76a96372ce426b1b60e2959267c59fb08dacb26e24d24627e63b7bcf632f6d3d75a08efa32707fb9a2bced37b22e81c2d9d97434ce3a6a7b0ba434fcfd6017949e25e117db16365681a49d5cc4fd7363f520380e6a37518eed6759fa062b811c833dda6528674c0ab7c2be5159a62e8c2abede6270149c965c03a01456f581803ae4773547491a52c8b295504bf3492ffe5bb220a43e1cb6001fbc75948f49ce875b3e055c1fa2d496914d7f49e98e7a1678b2351aba528ff14a2252a6c753bffb0a044f76e813b84b0538fa1060144887e98976c9eebc5f89bd03a3b4e04b68088230889623d728f9096b1b29f97066974153456f15f5fe6aa040ad4df7ea73fe6d737acb29cd20e0b87b158d053836670a23b31c6fb27fc13df39f906dc7a7289fba17c1e3b5d0de847b97058c6799d409d78a93f441be76eb8d0719639cbe3bedabba07ed830c55704aa1054e6c08b7b26be44bf6345fd8bac2094d383bb1863e625c538050df18cdc375352f06e6665313d6789299d5692393fd1d468ffa8889a866fe5a6f9aab8288b89f68889896e146a7427c5037697a0eda944a3f4cdd0d98c308fa41d397ca52fbc7d653a02f5b2ea7f08e00831546719dd025a379c9fb87fa5f1eada8d64c1c3c8e43ff861a331588160a9cbf19027d095e9d4d98f8aa358f23ddd8b598aa89ae982d8431b853aaeb7ef0d859cc9d62c7e864e4f771efb18144d34e1df75abc740e2f84820a387f1042f21e3985d25cf354388f0b6632d949cb9caac901631b9318d1e92e7f5caa3842bb485ad398d724f584b910b4b985c743128953147c49ed9329acf4cecbc7ce2673590d9263be40a25f640c19d3df2d27d04cf07aa2b37eeb8a6cc08ca470b6385b85ba5be7a19702608ea502acddcd01486ffbfdfcd5de33045c3656954c9ba1be85f4f2060fc9faf57042bebbb7633e167f2eb34e47e293714a51a3341ad46252a6f6cd9ef476d3de991030cee72ddffcbc953b68367e287373fcdd7cbf2ac1b5a22d8847abe6ff29a7577d09f8bb8c0becb6fbb2220726fdf21c3deafb67ff65fe7ce1f4e3867c8a6a818eadc00965592df155e94bac654d4c3acc5cc779abc3dfac21c553dab8f72515a061bfde0dc546817b04189831efcb38affc2f17725eb6c234247ef9672a1e89c83791321e0852e196ae8094afa8fa1f5152949fa0f8bf1f9f465ca3a994d33f7b8c825872c8dd086d3b38f8d1f7874ed0166668a903049ff5ae76fd66343d5d093fbeea8e8036d7688ea6cac5bef200826943caed7f0811df30412c11fbca212782af7d19c8e913e45b6b7b46888e1997ba42e8592dc03d6e03f03078b42aedffd213bfcb1f592e4dc31725c33e68b7bf48057fea93548e7bc4f65debc9936efbbdd883e40e025bf7441c6e357b4669b008202f61ded44344c7d0c8b5e9c9068beb6537a4c2b2c3a7b49ecaed3414fc8d9a9609b38f623b0d14f1533e27264ffacff7aeea69e0339abde8a9b82e4564bcb9d90a9251a35217dce79afea1f3f4a311340915afa33a7d4a5bcf6d2feb0c2437cfe7d4a5f0d938dc078240d38a3d9b7fed5124ccac30d29f4b09ee7c0ce7ca317a3e1cc91623755f112d9fb510669083a3f20341fa0ea2ba4857485f7ebc7069755aa734ca69c14360906d9164d84c5fa62029db6416cf029f0e7862ad9d025cfb6d1f3f11bdda828310ce5ac863dadcb615b3b931a364b0d34d101d9134bababbbb8273b3e7f2b9d411f2702aa5b6208df89b9317e2d9541c42e2ee339f4988a49fd10310c3b3562775608f19145b66cb5c34046054878edecde7831cf2d8ba59f37467a525620f6c728434484dbbb32cca6cca8fafcda45078c2d3f2b3f27bba120fecf4dc575610d4229a0acc7f9498cca52a28105ceb7817f358f7db4582d9aaccddd98f5638ec5b1e45cbc7a86ba177178e9ac0d22d536df35e5d983e29fd24e654a1e196ccb60ea7c46efc720b94b39d0f86a0fe526a68b8cfb085d8c81f5f8dca0aa9efadc06858e3460e8e18cfd92f6dd29037017899a9b7cbe2a88f623383910d92d5c36579180ebdc884f95ca153a81e0e27eebfdb6315bf4bd2e9fe78202ef21cd02d1de1283fa8b8f07b76dd4012f09cf85d141978e4c8b0ca5c1ed57f5097f1e580b71a9ebd63bc907d847a4f233f60ec5aebfb7d059840d6d6ce2ecda2975ae2459ad4765098b69d0040c23db036a54ec11ff7cd04a06d50dfa6fe90dba93b3a37e47d1dc3feaa34047122dc329d769ecd06878ae8c1e912f0b9773e1fd2138ae19c70d3dcae23265eb67005e042d415aac143326cdb63c3e510632e5ad72a23fc46b8806810b02c1ae90c6054cd09b4521e61b7a606ce456d8ecea77d1064835b88ae6eeba25c81c943a21b365d98dcf830b070d590f74d5c20098ad43b984220bba0196bdfb4bc9beeaa1088ba4d307ce6675b8f1f3b96d9b940b82cab9f7f592673bcd4b6b4291ad1269c710d9b8b0abc45ec7ac7a85a2560a8f3494432a8bcec00a73fd92046ec9f35b22df7c065b2843bd1a0c3284c47b7c1d47437a70136e0f2040bfcb761f6f148eca114cc925b37fac8ec002a44d804f0c470fa8498ade2733158ab1b913c037c87cfb4b92292f790281b4609bdadee8b8c8fc23ebc390870bc157e6cf8e17303148b2a60591679d76f096378cba85a15f07bf357a085b825fae9737804adf64e537426242861c1eafc08e3eac8380ea3898d9d0a553f53374270a4be45a7fdc088d3f8923ff4f1779cdd4c2ee98926b855985b13d1fbcbfa42eacbfefcbdefcc464e88f56aefafd74314526e408b3b23a434c741706dbd60926e0db4f99b7d8b4b8b5e2e2e2757c9bef8567be60500096dd4ddd53b0be674f347f5d224f569a8cfeccaf9e80dc5125931b737873682cfc43a62704b91964ed8096c527255283c3fd0a6b8f6bcb963380a91e2b085e8dacb81e6577e54288f8be899b6b1f0c2a21672ff176dd1574d6bdc98ecc1848b122994e7a6edfc8d29d62d160c796fd3e5fe91772476be0d676724f5a27007862e0764ef792c8e8598a79ff4157d57e80756e57e29a5cd64ddc68ebd43255e6f8c3cdf60f548f9f98c719bd7d8cace6fb37df13903322e538eb19a8ce3e30a37e7341e7e8c511a27ef302814aff8ff40aa7f1e8d96f10cb4991815fa9ab3bcf7cfda0c03adb5e878ecd99f3f40d31812b34e8034a5bdb6dddee350734a9f6ca88648f63aea30722ba410a8cac1f7a58ba319b1c3b04887e23a85c2ac01397ccab62f1102c705ee10bb878d86b66198d5550233ff8f2ed7f3e22d48bd800cbb600a97fd1bc4adc162050330f7b1942b6f0b499d8e423d03e8faba442407dd8ee0872cdcbfc15e2733dbdbcfcf033160c6c9f6384915ddbc2284d5aa34f025ec1658b221e9efb94240b3ab170e633ad0eae39ee9ea6cc1898fc0ddc662319c322ef2d0cd714b8131420ea6c8cbb1c94a49cc877d88913a9a084567aec4fdc0138b601a8a7db0d8190964b867da451ad845da51ff99fa41493aa927000cd5132b0ab93bad88a3e68cb42430c0e3d5ec93bd91f43b260f983936f65cd1a9b4fb1a87ec8ba3b1ca85166cb9e1a4ed2a5d15690d94e798e1ed24f433dcae76d72e5537f6563046e38ce709ee8b2f600e89583feb492df6f42aa17bc3cebb99cf97832c72602813a5067296e434506ae063fd5fb0b731de2e367a8d130615c9ae86f884fc60e89ec8c8191740f861cbe56dcdf5c21256106951f651ecf6f1f34b57ff1171b763c1ad4ce936068752926c5a4845f048c35a9ed64adce76fb7a34ecb35d5a12c05987cb24718576a6f9b78268f6090c4e1a85c954b504bef34f75227f67b8b06801201887692945936b93851fae203fd54d1e11f7c24334c5c50e5f79cbc3d0dfe7a8be447031e4d00526ee6903409191faaf0e4cabe006547bb64060fb846a21a120d926fe4744941e8034d318e46087b91129173cd765d36917878797c0d15837fbda0fe2206d8ec71b265ddbe4bce9bbbe9387475ec2f2ef1302f060c48db264394fa80ef270c2c09da2d995d8a00a4a500051ecfd06d6ee59f16cf4c4308562021ba1091b63506e4d2496cb0f8274e49b96b4d120e815bd508efe975fc988638e3717a514a238e8b95ec5d6ccf0abeaaac310015763c49e49f7b1886b81f46e1534fbc28ca796b233e820b4fc0e6724bd8614055de6ec312dcd1760164ba2690ff3b61785755c603f7e8b15ccc2d7a5585124895786f69755437f6aa29c3eac0666c1699376c0b467a1e1ebf175c07b3ba2a2cd3f78910b9bb6a564d84c1e2782c73a5422c980b68d1e6dc016f0ef1156bcb41782453e4ca77b2ad3bc2bcc1596fd3ed4b0d6f16243c14186b4ca764df572edf2c3115afb12f730666ca0071549d07a2ca3ad2c20a1fbcccfdd1a6641edb2d294d819b292a5e40404a11af20b65ef638eefe150b9bf5bcc73317a6d60e410417724113a629a94f231d15aff1caf940f023b08456a3b4e3f780d08d93bdc58144dd3a3afc3df3f96adaa68d6a4a0005c1ccdf1163fceed06346eb3e622bf2d62388561442d825d597af6ae31726c156a8121ac9dd171d390e74603481852042909bf596f7187b76cf6666da36c899ba466965e802722751f6997754bc2f372a032a09985ad2428bbaaf6d81ee99c9bef3019c6582974926721629d4321dd289fa89383fca693a5d4bdc60555225f11566392ee259c6546bef9ac1e26b8241ad9846efcda391e160b99160b5157407f2e396e97bd163c9baf30a45c887cd6a4e1735a675374dcfd364c1ad5eb078d511a9bf493f7d54089d36915d74a6bbeda99c742e9f034b5e5340b4f5e791b6c987368b5ca6c019733258df6f9f3159d2a298a5c1f76e51f152558c1e5da90deaa4de403f20ee8f1ca479ad61394fc810817d97f9cf52eff06c2e037e3cf147f783ca5a20399350d48e50ed349958706f4bebe6b9db4175efa1016997b88b4b7f7628c6a42c62377d4ebe88c84f9401474afd9b490b760096ecd5d7fe9038a3eaebe08913395cad829669cfe05120df11339d3ffa978aed2e7ef211f05e2d26e420b6ca07f01322c7052a441b5aa24f1223c9a8def535b541e61b3f160ad3494afcd2f092de90a929eb9bab47a078009c9a53da0a2bd6527e73d000cc5431020f15bdd85ad5f5d2129ced3eb02d40804faa7fe885720d8dda80369d5edb5b5d45d553aaab4b34857914e13829bf64654c9261966fd63dbb691494363e6017c3f98d1b15e5edeece10d8040b4c6778c09a43c6e7e1fa7aaf3628aceed347072e5ff219fbbdb6eb9a59432a514520879087c087eea9ce0a6a445c1f6adb646981096deed8f665b8584a6f16f42d5c27611b2c216bab86d04f903a6f17fa24a61a908314125b1e812854419e1524b1eeca0422f5819fe42dc702e41d29420694f6cc892c6b54ad2685325699226699246a9a449daf4991e52b22e30d8d662adb6baaaab6d1549b5aeeacae3f16c9e0d06f33cdf30b7f1ab43678b1677f7f73c1b0cb66d2fe29a2f01a102583a37ae7a3e9dd753708dff9848645c86f4b819bddcb8eaf974de83de4396fd96224c11c66d2d13c6a50dd63a302947f877115edf3dabd94aa705defbafc0fb929c9070c9f361618844d28dccdec85a4bc39ab061c3fca3542ad7612b58d36030180834ea713344448406079124e9d30e188c460d56b163e4c8dd1d6bbfdad5adae358abcdbad180a75b1eb6a5723778cddeeeddddeddd55ae337e21a04704d02c09273e7849db9e1c80bda3a49b86646847b708c3f68731eaef3628c317a3778191ff24e172758c162dc7edfaaac51322b4c74efdf5418879947f7856cc5ed77cf732d2e75ad2e4d66e0df4bbe3086cbc46d3c149740385c86351249b09d4885091eb3f6cecfc8097b03267e2d73900ad3f803a15e71e71a7f990c060624ea71e3a00f0789d0502409d79c741c1cc956359e2063c7c8919bdb4837ec155dd1155d5b6f5bdc6273fb8e91232a2837d880ea27351d39778cd0aeebbaaeebba91c7e3f178b66ddb647c61258dab854bdbfb47572475ef1f5f481cece872c51797e8f58f57b024e152bf0cf3d35cb3b9a060d9c4262cae7b32f8ddbe245ce31f4534ae112e11e13ddce0bad7180cd40ea9ee1fe216a554a107a771b1ca42e5613c798d0ab1f5459683f20b232b028169fca7a822ba8ca476767676768cec189131992756d7bf8b1c63c766ee1be6888a27af79cd6bb4298d3436b7cf9b799363e40608f8602f1836e98d83a20c6d05d7f8b37f77715d8beb322e39b3d74d298d1ea5f1f361c2865e9bdfb2ce433d2c8049fe8ab02b768aeb2fb6c277380be622ca261e06cc654e664544b93e856bfc5532559b1ec60e5f1897d81f423737b55a4be9cd4d942b1959a462c7c8919b6541aaa591d28d6e3146598c41e246dbb62348dc464712f7bfa1530da54f5bfd12e0b569c252080a2023043861fb23c035fe096892049ac63f2626f46cdbb66dfd59c28a98b01bd3606ef464700ae4806b74b0b0bdd1205ce347e2916d6b7074c453b64ac739d8bde5e06f44b02d16f58a25c44a2592a12896bd8f8a0de5f57fc5183932f39c71048140111465deea6696f10517da8936ce628db19014f6891a0fae472aaec71d5c7fa70293dce5059760de7594f418a62937de755037a61b0f836ab2f12124ec0daedb8087507c018b715d72b1509c146b4770c913ad78ace2510947177089bebf6bd13b3c0b2ecd8f43c47c1881b81e6770ddb770ddade05db8ac068daf26f385f5da982fb432be6abfb0ba565416492d93c9643299cc23eaaed55c2a95ab93459290982c268bc9e88b40a2be5d8ba423227655aaa642f5e3944512334f8f3fd0c1ee641de8eab8fe11159b38a97a17772e1e995cef40d1e7274e0ff42010687aa0771b50bc71e32b358dff87f6c37ae383e223c2f5f7a468015ce3ae831376def0e6d33961411fde88e6ed3e9f8e54804471072deb9aace6aa2e14eabaeafdb82e8b3c445a27eae1b2b26871e102e3bafb8c2d01d11ea66c4597c4e80ed7fdc3d053d09c5630cdcca24e2eba2f9c604c19d7b84c76c5f0316ebb7a4b1458b82d8bb55a3b57aa4872ad56b550c85a97aa5659fc0212c51d8822155ce36f43f4825445528c4552e401d3f8fbcb5452ace213abf88395cb7cc7bd10f90b2e85eb1f12312b20f4285c7f1b9ec5f597ae488a3d341598c61f89d00d6c105f1012030b4078e0b0b0f2146b2e03c92c16f1e43b91343defef5dfc0ac699a0f7772bb8e4bdbf37a171b870a973a7fde876f7bc9bb255e34857f3983299ecce952c9298a5cb5ee9f23dd0099826922ec034fdcc85aac4f60c9a50b5e84700d3e8d802f37e25ca0f3d97305f38616044de4724eb47048e6630c10e07db656e13c618ae749ec700f7f1c3217ec4c1f3ce3870384cc1c1f3f1dbc46e33677ab673eff992f873df4cbf2989e7e337d3df8d0ea6d932a1feacc5c19f7b8f28b6e787c4f73cfdbcfe84e8bc203ee7118db866bec80b2b67cd01ba27830eb74e2bec0cf1bbc7803ff73fba796edbf462bf0621edcdccd03df743bae7be2453b64f12bffbb66f48f749fcbbfeda0bae9913fc4449b0f2c38f1f2b9d5e417e118b11c6cccc393723b0935fd8f62f5ac618ed17b20893524ae78c94ce58a3284e4ae383925239279d944e49279d91718a413f4797e3e278afdeff289fd2b05e19075d7a3f64cbfa23ca3869d41fffe3231cf4c3f81f3f5aa07f04618eebf30bdf47a5df0d78e76de639a2dfccfce49df7521d599df6ec4bb7377d79530e02731888d1859629877e620b2b35d394fe20b46ea279bcac38d8d14fc77d3892405ef711b5c79670a471fc8340f3f8b07c68cbba4262e66a24a46be63595758544fdbad63ce23b124dea4d6e5bddaa9452d64e344d94f2bb691e1df7e99eb0db13f673fd76338a10c491f02f1d9d73e33e54e4ad22b8a6ce561e0e085f5a1f99f191c6e9562f41eb86c25a1de9a6c56677773a2708052466abd5aa6e72dbea5665e5a4a7fb745252295b5cd33070358e5361239ae8ee09dbdd2f36ec58fdd04f9d0bf11c36b0f2b7207caba856d8a6545ee148ec0bf300c24a2965f7f1401f2ae26e75a249bd45d3cc20d038fd650b246efcb8ea2f34004dac5fd07b272f3fb8c2085b708107ea09d1017da148131f86289c564ed8b0b31076166ef4d30d0f3c73c2e9e674bae9d837ce830eeab43ac9205c43e794f5930506b0894dde924dfc1fef47e5368e9bdcdc56ab6debb62ae20b12b1e9745af9670389dce48992f4922378809972682780f083cbe4bd114d0a1203a05bb17d4d206f2b92dc89477151e1aaa8135127073f2cb6d5dddd5d02a6f130da861162300e8ee6e18fa3c7781fdedf112c4edb5fc138f1f4a526340ec7a5fa56d5ee9faead8a8d53a479f8cf4e32ff22dea73fedf1ccd37d8ee3681c1dccc37b1cf7f3f7e339734d7b4baea91f19e3c74353b85d889c71ce3a6b8c31566baddbb6515a2bf56a6dea0109bdc0355d802fd4c5ce90f7fe5c158d22c9fb66601a7fcf62b409a92159d56ab55a0d0912d0ac999bc66ddbe846bbd25ab7ba511a29cb91ec1163947d7a60437f5db7c1e23630b7992f63acb53d5c953b2cf921e435aeb919f5b8d9116385abc562514ae98e118ee3385624b1228d1cc755aed46bdc7024bf27d890778cd8e047dc66bee7312e51fdb07212259627386d8a5415ab2b602f07a5d36f918dcb5e047c87209114a35061d5eae08dcc11b13833839b629e394cc0e502b00da54c30e81fe2b83ce399bf654cb04d2bc1b4e536cd9af14516ad3355eb9c73ce39e79cb3d2a9044bb94b348dcf280cfafb84c2ce26521e45b3454534b138a8258c1dfdd01a2362138d11b949cbf5a781f58471e6a6985f718ca84d51c4b7e3e68d11b1a9ebbacf526b4c5f4f65c399e2b2d5ba6d33156933254f0e42e192d499a82e369c29192a6eb7613bd89952c2b2f763a6666a862849dc828242cd9443ecc1cb04c31022e86d94810fa8dbac3a6d0dd1ad6198a908fa60449115599755ad0dd56835b06e54b061b35256dcc609931a9592af48aaf1345054505166111316036b2ad11525b27c60c39992f9195fe39b9941e667fc4c7f43647ec6cc901a1ffa1a1f7adaece74b13c988362eac77e04c7f8dee97097d8d2f898d0f7d1200fc8cd7c144433403fa175e07538c6806d4d9108db62b581a2ff322206c979a9981c6c73c0664bec6338e1a49313ff318a8f1386afcccd7781c684c1c1f80c7c0cc0be0c3c1c6cf7c003e1ca60cb1f1338f03001e47674394d3e30a36e66bfcccf63a98a82bd208d1a1b0176d459a0304615de69dc6e733f38ee3dd0380884d315f030038be991a2aba7d3313a8bfbd0e37462aa22219dfa21bfccaf8220d9a074c970bf3531416b9309f13ad28aa60bef8648698bff14362fe068d2fe6852f89ccc739d113cd10b1292462538c884d34ba4fc83fb1a794dbd0bcc45a9c39e82e51110cae710114d7b7309b8856e825b8cf0d67ab8b619022184cd3c91b9b0a60cc142db2680e7adcf9c2ced4e748e31aff2e78c76bf1e435afc51d445664c52cd189e8839899a2551c74eaa22d185e60e387d479e0128dc6cac2c1808511f31d644b58a99adb8491b5a2d1bca4669309e54b644d9f59668a6be60ff38a0c08312ab028b10897933ba33839588b446898a9ae61a68a843cf3d08ce7be980f41b7f14d554ccb66b4ac4616685441e6eb2d66cc142d66be68b3fbccd46471892fbde16c4d29b11559363eb40c7470816052a4451a6f9cc8126388248e60d03fb23c92a612ce510c26aebfcf17d7b87baa65911669916524922218318c9d1d76967891365373ced93595a231900ed663f0325fccddd2255b9265ebd6db16b7d8deea768264b15eac178b158233078d98e233b789dff216d7fa28e30b1bca4e4996cb6da4152e6ddf754ad45056556ca122595226a7b82eafc89914b11536942c09939fef1e1472ff96b0de9a7ddff711c99688e5921549202859208b6e3af849bb9ab41acb8a8291d4bd7f1a003bb0d3e2d545f4642092011aa41467c9c0dac2b66ab74593ec275b9dcf7c06f35d0085b2e059afc2071cc00de34b1ee086f1d53188e8729b3006115bad2a31462b3706c125d6d7856507a713969382d6ba6ddbb67d88648049f1ca4bf67ac5dc30be5876854932ca8bf5928105856be2a64567b9fe339e39a145b9e2379e20595c23a3482b5255a0a638edc82bb8c6d5b1497f506cc8b4193edf3d0628066ca481c27e8e965198c65f52b92e81705d5eb9ee4956a758ed4a75aa53f3c5f9278339e7fc7c3e1f4ae9a4f3f3f97c4694d239e7ece1a4fe7c54bbd13c1ff77d2fc4396984b1c562b0d7bffe35e7dce6165f91f4953a6ccb84f29a5aaecf2ad79fce6df3bef9482a116167fdaad5da5048b6c00b03171b53602b08cbde6784f55fd5d5b6dabef0b7554c4d1516b799b05904977670295eff199bb348ea3e04044ba5ce28dcdc895ed8ed89839b152cf7d2003eb0fe7c398a2e5c6edb4c96db843175533eb5d84272d30da7943bc60d638b4b7de327aa41c77d463f380f88df6d9b1f3d2022bf51e55dc04d311505dbf160d914bf7faedc26fecbd5625d8f22d912c9c086b235a16cb096dbc46f7be2a02792810db727dbebfa6f5ad8fe30a656a9a852a55253958ae08d2c95c72a5315695cc776cb411f4f1c74c6e2e9e964aa3a6f015c5322029ddee7864bf1e30f31753363aa4a3855b2255b32bb4d617bf27d7860a76a968a3207a74a0a9646beb6205d30933298491acca4103369c42c0bd7bfb1c0a54d62c134fe52b8fef10bab85eb63cc18bcfcfb0726c9163321f55442515a45b43ad58598daa61853750256019b80d4af2879c0530906e36a3261c4759eaf364210aea726cc55515485a52fd9ba79d5dc6e5dffbe3245ac8a9815b1d754bda66aaaa6aad61d23071dec566db1cd0710a5b3810c0b103b302b56acc41839b2a7eb4fcf6284c42e0a892410308dff16d73fecb8e6b8c8c58e8989d93eb5562bf7484ecdea2aad9344d55250fe95db6a48786b8ee3aac771713aa83f346a95c2073a34a4c43123140242103233ac9db36eb14306084a2b5be48832bc1ff17bda281f3737a36ddb1a4581202f24923acfbbbf17b93d90cc2259d56a35e7768c7adcf8e01a121b4ab89b993bb2562a5b69531a69ec6ddbb656b54a35a9bb8d0eb791e2a43afb32bdcc39e7a451b66d71dba20cafb25d6bed6d1b218049120c491bdd91478475976b676727ba7622084a54241941a15ed2a6132e3d719b3058d2e4f6a11f782e993b4c9611ff18bf21dc9784fbeee3fe685c03055b7feb264dd6228df770e9c541ff3837340f5f010b1a87abbf936af73e2f22918424d2344e4ef390bdbc20c9f97c5efdf9ee4f0dc9e713424237d16914922f36d08a741922631ef5c8018670442dbad3f5e8d445b1d026940965512b5445a15c7f19455068c1429fd0551435a1729b761b896a169394db44978c0f36152342511445538d436920002736a42852acd5da900f360036a0a9bafa01846f7f32e5a05314d7f8e7bdbfd1c38b75d74db90dcb1bb24c004278661e4fcd43f3ec7864d7ff46b760352600950d23ab968a39d8b558cc16c164c5c513a594527631bbdd2dd7bbbb259df3935d8ff1c186a35b639ea73dd03be84b9283a9c240a02f097d2501fde73b8f6846f4cdf4c3b4e787783c0ff32561df9e7b1d4c3a7414f5c2151b6759708d47591436ac308a1a82dbc022699afc6bcc6d7a78b15dbb2145f96f35b7691a4579fd627ab0ed9a32a47bef4bd2d5ee874d53c21ed7e455589483ce84a6fa0b47280d8091c635f091c68d60c31fc51b273676fdab169817fb1702312708174ba5891bd22154081136e458ade2fa6701a3281445511445515477b2f1454a29fd7a924b112ee1b8fe466c9032923a402bc235fe9f13a4eccd49acc52d77cdbc702b553ce65dfcc51b07c1d2206ce855dc4626817a13ccfc2458c42e65e35227650ae34423e020ecd410d7047d4dd0b7e33c34f85e60e9875e60528ed320e0728ebf935a44a790d0a42291c697e8aecfbd39952291e44b348d37e1f3854e8450fd4287c29bb88d045fa2b71697a870a9fa1242446887a2bd096fb9b7ea87a2703b91edf5b75a9ed06f7b68ba66eb7113631e16e8c8c7a787a5dc9467e592dba8597fbe7ce504c4650f24b3fe39ce0c84d58a15e3220506e5b5a56585ae56d5bd7635eec9c27237fcee6bad5f3f97bc15144d226244af1a87da20d48496218108a126ec12fe7586374d7bd5ae48530433c7107694ebcf79b7f2e0fa3f5769d9f951fa021bb26c031cd000976ef00126f5e949f3f0f76853f801130c869a4707e1c67558f33084ebb18fad0ed6533fa127da4f6606b80083880cb65aad56abd5a8c7ca52e166a64c9bfb65737222a94a493b0f1555aef9175a96d69430c963c34858d029cf97e3a371b88f86e6e1fffcf2726898afd7e49253abf461060cfad3d0384780602b705919610818d7662c2b66f3dde3e68185a5b55a6ddbba3098f993827262f22122ab44b8f35c91b922658e48294084694d7a1b69dc00d37dc05988d0b972743dcb105ea24966334f3794b957ec8d1bb24cd6cfe7f3f9eae0e73381e8f92244081b322dbc61de22fd34f847e446caf20d43a0474185d828fecc32c4ebe9d787610a1b72145f50b9a22996fbeebbb9f9978076846bfc6d11d6238a5ef480f483b040c041e7bbcd7c60e10d13029e28fc206ed3d94c520cb20797e4fb1549001def36954b5bc771de8fcd0697526069304e7f29a856864b9e77cf15eb5d7bdd779fcfc7fbe1f916c944e172f6084bbb1f1a8d46a31df1bc1d0e8e1cece1e08d833e722010c4c16e193e88a1dc85a5091396955635143a1a7532274f9cf8cb10979c8e4236c0208dfafb236bbdf4a768042d9271ce398af3e5acdf10a7e98cb07ec31010d6e6c086f6ba7c8fef38fe7cc51084d840dfbdb0262463e380c1216c689c3e1de1022f5a57282143316d368aa25028140ab56d3b463d3a06338399ebe82736d4d0a76ec234fe12a3f333ccbbfba5773d3cb0a31b3f9e15e1d9ac8894348cb98a2d66d7218bb1d825bea2966d4606ef3ed7f87ff7c726f3b76ddbb61a5aafadc6c5a5261ac7f3fd963eccc370c586b1359f348ee7065c62e2243fbd22c953339aacc99d4892f1fe13758a246fd249999c359ba44bb6e42aca28cc87f2142a854aa15cb3fae578626722107ffe8caf03150eb5d08a40709a4e49711b260e63058b5a82e212f3c7631173f6d5da50282e81b9f10718d10cb771116f5785f58f556e186bddb6214245d87645527479a660ebfdcfe1a4c8c4458c1397300c5c8a33f8dcf0bdcb7db515bae185b0d8b88469fc7fa84e228a6bfc0a1bc6566c6d1f7a1f4ed38f058b6bf12d22601c04308f7e5983112e4d0528a0fbc25a3d5f6cc556addba8c7cd5cd950d2ac6256ad184abf9eb4165655a9ba9b9b1bbad55a6b7d52f7616dff4251b061e84a5291a8c4f5ef2118076c33df85e052ff4da4691ccf97a479f88d77439e7d5f6e92d4f79bfa85355f6e68641e709266c25f51dc2074872269014ce37fa35255954aa552a938ae0a19e260ff27369bcd763ce54fbc7054173f7913dae46a4339399184a4f6e068462c51a06c9f56713991e43f1a816008d6b39e71e9b3a104dbb7ebba6e3ebd61ab26f390e17ddfcfcb077d731e902888839e811d52a20474615b3509543525e9314cec31f48f4056a6e9ff421b2af7db047a04bd6d40e87b6c60d8ddb8efeeba0f924e61b1b5630efabb604abf0ecc2b895d389950e0f6e57432a14226985702533b9d4e261d1d2502a05513cc2b71c02c6482791d253cc6eb64820168827918dc98604c30cfbd6c900f0b9d05d3cc9eb0ef1a62d6a63b3d836189ddbe9909f408e6f7ac023341d0c2ed1cffe00e6f5a8f59382a41dfee5974d9f006c4041bf6cc51b3d96c369b51ea33cf65b67065d65167a08f500eeaddb6b1043b9271123b5713e7873bc21cd7a9cff67ecc2f6caddb3665a432da3ccd79d2d15698344b67d55552934fa78e0c82b147a3ffda5f47c4dddddd3d09e80bd8ca94c388c90f5ca629fd21c36230fcc0facf990830974b9fb95cea3a7dca9175ea0607a9128be408f5e00006232ceda0b0f23671d053ad1c2acb7c3a9d4ea73ef58952a7755268ec34b7711dfec5dfbdb8bbec642097fc9238ee03eb573b8f16d6efec9e5e3708d391449fa6c37d7d5a2455ee0b400dac90d98d1e5e2ccfaea36ed846c0208495c16934da9c3b68758c8e99274f99acc7b03559173f317a778a42987843d75b75606a24b5153bd7df690d6cd8ad4869b2586beb38a9487f09781e611fa0f72f82821b9c04fa680d0025712793ebfcc53b2b1e366458ea156b1efe138b834da4b0adfa9ed8119020ec1d9c808df81371555c9062080c3ad83c8a500029322292a06a8566383ed40aa5a4a452527630d26ad156abd5ad6e756bdb463d5a33a4b85caf1d2f586656645ec0b65497ab7dfb500f48e7e1b863c3762921c68a3cd142609c2456669f54deccfbf629926efa497ca2a2a208dd4a4214ed84b80385b5723da9f2ba5cd5e572b95c9c886fbb869cbc2e4e2754cb0a1396ceaa4aeac9e90482b20623c95a2e798de35dfffac562c1e5367d1b50b047e889f66f4fe9f6fdfddbf71ba91ff24e7df0648db06d65db28a5b6e3a55e05180a77fb3ed5ea606f2fbd1f5b1525c1bd0d6ea4af9123f2044b7d4ed70dc0a300bc8502c0c6a9410938416aa05d6c100c02c1a07f0d8d83a479f8105d584a382276da69d970c4c869e774629d4ea7d3e964b1405f1cc7c5da49b264151df9454a296badb5e6c076dfc1a049978508dafdcc5924e6863cabaf5a3b092781b5849429490e323b28d3e773e8b8b032791f6b50dc660726758d4b325c9a21272c7f58721d06256c18dbd5318fc13f0dfaa6312fd61cac5dc023c39b6133d948e30f33051b46d749a2b8a71c05b2dd30db33dff3f15c1679a28f8863acd185edbeb0b7442ef10a26512298c67fca941ef4e086edb272c376f911b1567bc2054ec80df4d6e73e569cc6fbebedaf5d4caccb89d30d252ad6ac58ff6e2bc27d72885061b91bf2dca813d7db65a579b812d207066740ad4820e42a0a39c42bd624ab5dd48adfd4502b4822a96b55d45c35b9721bae166bb1166b1e0fc735421c1c4284be6c00c1ccb4b1d056df4412dfb0870a0b739d524aa9bb870a6b61c842686665b15a739cac5c00b3b826a6c541eeebd4ed20ae7f6c988f9c9b90671fe010b007901841e33414be02140ac54385a5fc04834180328486122b024bc6daa4d06655615d094d11aa227473c38ab1582cd6b6c154890921726242c310a6e9f08cbd906ebd05724c08970470fd89c0000a283d19a6582a73847dfba9967e433ad50d79f6b2b6880d2152b013f0dcfe402e794c888cc562b198903977787c60e6a992826a321b89b551eff40cc1728c49705f9f37fa7f798aafe2adc899354a7b38cde69b1662778cb17687c57a37ac4dba35c6186387c5c65e71e9bbee8574b0729b9614a61ff6eae670e5d50e3329a3bf6f6d6795811d503db0d36bf0067430dbbd6ff5e7ffe5571539ab55575fb93c7592a97f90684929e58e9119dcc43dc3803e1eee330203f27ca4b0ec604ebfbe70f46166f678a011e89b3ba31d5be5975365fde837bf1d233b3827288b52564bd6a54aac61cda55d946bb53614fad128ca6ab5f61f0447a366f58b4b7c7d764faf1fc6a5fe1fa6b84634a10b76b3a351a86d121c3721063d58c294433c220c93e7bf54ca32d7d8c993274f8ff60cfdbef4abe7a39f1107dddfeebcad4bd8203b18b1b65a6b592c101073ceba4dea24c65837fff13f291d758a76c157b0204290e80ca100eff0175917589066120d1a39187683cfc042b854e3bae806e192288bdb42827cda415110075d6efd520bcc11fc4990ab7ad2c38b95715e95759356791031ec7c820dc1eba22dd81094f2fb85041171b121087acb240c0683c182ccb9c3888e8fd8100e7f99c383cc9e93ba8e3adbe9597be9d8a45cf274308d3febe8f1ea973c5dff26820d2e729a5ffdc9d51722ac97cb59a8e8b2e58637314358ce4205961bde64a982c9cd424595cb598ef0e2e2f6eb543f1e1ffad55bc4729dbda4384bac4a9f9acb7c65893de186bdf369653942ec86ed456e348c56a415aef7f0aa3199ae80df5aad0d85fe417074634508eb1e1020ec20783ae108c5d3cb4ece628425d8bc765eafd7ebf5dab6978582e338e68e7c24e9288119638c2e9c4c32af2383fb9890679f1f8c2304300ed83cdc13b14946c4261939ce711cc7fd370e17f27e8608f41f519bac884d9c2889cfc788941c9111c910292972fa3cd853dc225cffb000d73f2236f5dd3e326e9a216a1367454a723899bcef603ec7258ce805bb9e08072433433eefbdf7f97038d271a2d6e260f7f2e19182c5e16a975feed362a5b07cbd9f7201192118a3c56f00e8fd6788580121cef5070bc0a53700f3601b4a039530e8df34d063b8eeee340ee42ddce71f49344ce38f802b15641a0eee73d41d5686877624e420d708f1b8c5c2e3f1b0876745aac7e3f1783ea311087ebe1a699cd890675abc97306157b9eede00506fb9a410278512383f9847949b273a52ba05e5ca78fb31f67432b90c5ba4eb72201084020e1ab1e1c80d48224d7f441ee794d45ab972ed20b88542455253a9b5d6eac9c0846b9c874a7bd23a6e93059372228d8eeb6134185c0069d9c0862db3f12f12699630e908e7c3c3033bcaf3f5ab50a47893277de7dcaa7c00036193152690538324f4175360d05fcdc3fb0a4c03c5f5530db8d0b296a3aab85a2d6ba18e5454ff509d340a2593c96432d9a8879021a998d5687453c4061f3db8cb4b0b6d853a236c7f884bdea871280fd73fe4031b8eee0cb9949147233a1a8d46a3d1b66db5be55881c63c766668e0fc3e50771649a077fbfa00a1760a61c1820c41096609acf1f120117f1174435780cccd26336123bc03cfcdd3fac2132820d6faed74abf9b1b29c1ea25c618afb07d638c3146902cd65a6bad52f610620e7d90213970dcf80c001036e4fd33925e003f2f2b43d7fd8642a15088527a84945246dbfaadeb6eb08a31c65879d63ff817e19201a4c93ecc69f3d8a8a24b72e474bad1830ad606f0869f3f810e97fb7e90c83ad8fd9d534f06f91fbbddf2f35d07deeed37d1495a0c7f8bce78a9d119f8b9df48000e10bfaf682f0057d33de8f803ee8bdaf0718a0a75f1f06e663be1a693ceed99bc17ecc10fb319ff7cd44a0043dc609e483bee71aef0b6d002fa7c3fd70a28f8c7ad9ebb82f6c6c62fd7360439ef12039e06bf57ca1e8f6733f2bc27156c435d25899108ee71a219e837630a71466446415a6d66a63ce39a70e9348245598da9da304db452f3a78716fcf8b2ad61491b8754422e97dbc17aee99085ebefa8482272d2c18103a777a8748db805356534020000000033150000200c0a860322e17848260a728d0f14800c70884082583e1a48b32088611ca4903106210300060404400433ac14053a62ebe1d49c3cda53fe7eae98e3e643a94226bf60417b85a91e3e638bffe5089ea7e763537b32c3cd1b038c3ed906023efb9e63f336e064a8a5567cffe1dbc0c094a9331d30f3b69c1fa8766c68dcdec5dbf79c271ee0c74b7ffa91677432bea0e7760a7cc1827b9935fc8bf91b5072e98c86924426c89c405bcd9ec76dbe2abf8b798d697cbdc896c50bd10f35b60d3eeee2e3a9e6833d8c3fdc9099ab586de87e245e3dd53f5a77d96d350a46ee4a20ec638faca8d75b58722f4c9d5bf121a79728d4dbff51812d25abf793941e0c2a3e16ace0527081eb1399986398e835311a9d843c292e10d193638c719ef7cfd7f6db872fb15176b6af55a7c41259774b743e7789008dcd33fdb5aeee4c0821d8133f78bf84d086cce763fd2975d25249e98900256f18c87ec3ec65ba3796c66396f3d40cb3e4c9235fbd9c386e20e511a6494adf8484fec0ac303cc9fd5ce9de955f3b8c3d890d84fc9f49851918390692050b930aa0579e9ad95d4f67abac99a2156e8e93dccbeff8726d19dbe1f738d5e6ed7c81f89e00e560d99582d3511fd4eae2c29a878ccd80c50717ab9af4827868365075a7a67f4f981646cae036e7d550443e5b091d11a8dbb68935740a15ed5daab9b4b9c564bedf47b8c41e2b26eb9f4fab1ebcac8d42f858421a2789cb6abeafa8603cd6dc3ae1df90ee6c4ac2ee834dd2688f12d65da7c2fb57c0b5720d571f17e5b89b64333557a8403c519ee0153a81296d23cb115ec9cc8faeced45f44fabdbb023c37503c1bda0f244dd058a0871e98bbd4e82b4b63b49ad2739856ea36ca901473eb0b125b68361cadce9e8fb4c36654b25830f9d53498c9060d377291e29da82124d2f2f2b081a8f4863e004b0616448ec2caf20752c8ec48e23aab98bd5aa73d6e853ece07c8aa7c44562ff00118bb279768075bf267ac182846526963eace6824eb4f159bc13b95b78e24f09182615f1f6c8da9cedecf86c906d856644d1778143198c830b1d57beb955188d86a275864d4f5fa80a4c72e30164540087cc42ff9727c32262ea5fc500adfbdc04c1a29e92a9f226a78adad762c357c9c06f4635c001d00ce22e486d8041ce8b8063ddf6937531de8bfaac2e6631107123727a9dd72003ae25618b18b7d7895e442553a98a6bd0b626aaf43ebaf5713efdde821caccb1fc37d15039cdd5081133f990cdbd4572ba8199fae0fd85cb60836e247223e06cd98b1a2fa39f743322774f5ba60978abce004c9f467bb2bcd1b1d1f16bc0c0b1bfe4380594a60e8c292610bca3f87e4b333474b25b02f8c15541743895ff0d42318117dd915595f6cdc4236582b62383e04de142221d853474510a33e68c682aba033e40b58ebc76bbc6290c30773b02cf76fcd1178b3a2ee7f8e8f0fc7390ab6e209eaf8cda14e00a8d0df50fb9901c367b8d55d2671cb402de4f72c2e7e2287ffdb07044884a22902a1b17269804e76d75db1de3187ae9943411682352fa2fa673fb0b818702808f2048681c5785b1afda62f312e6b7b34085bd8e3327cd5a3516ea969af67a2efb5fa660a86b841d56073fecdc848b0ab4820cf1ba6a0518546fddc3aa69f5022edec05965ae0c3dc47e8a6e85fc73ac1b4e09da48ed3d53a56bc3688a032d9ee5c8a52c55e44998a3c187ff1d5957efcbe0dd644cfb9adfe76930432b7015d19ac16f64b416e9a0b4d35431c27fa157c4d6546b94469717208a9ef5e444089ea933e9cc3c016aa0d2d87cbb90bd340d5d291e694d283f019bfc4f428959d736f5b64ec3e26bd24ca3c5f47394d134b17a824e181534f7c4c7016c5975e862d77f7c17203f98155dc4bc3dde2706189ca8a0f30cdb6ff0b7ac52c187aacf776aca195613a013c29d387817a57895e7df921f24384c56e9bc980f61534848c2bf683912224128644ca8599eb24a5afe099b80a72846470d30e9667628615161a4e182ca3ecc012a5f463a792f424412cb371d5289c610963613d2c89bf147ab590b596ad168d09f959ef77dcd554a927e8557c038723f857f9a6fdf08cc80a557b738a86981da9b15998db868ef528d12ae19edf7e79192f43b0030a00dd35ae6422ac970c657fc6f05d16c62d285b56ae153ba5ebf61882126f6416070e6d6caafc044f1410cafcbec35560083280eb41ae14101b312a1f906d0803adeca8d56e3b55c8b7d59082f6f0967ca9c3013ee94d68236909f3592ba01c801f3066d9e8f53db3ee9917d641c7afabdcef143a35918ff1e0b3ee042966632845372cb10a3d3d7165f0781b38fc300e1a6855ff14a54d9e0b85da30c8e917554f58acb08ce62b3b4d0f0cc431fe514351b0dcfa6ee4a38048fa8e95ccc4ac6a6bf7187a708329cbbfb45a20400f766dc5fbe3f8bd6c01c11363d1d5403eb63a2e564ca500c7c5ab9e57b63b842a4ca541f88c27623994bd1223e82a53e4ffd467bf353255ec48c98d1304d65a0bc0bdafa55e8ddf9fa060cf497c2745543ad5855612761105814e8c8dfe28965282f1121502ac17f9e23fd242b5509870abb0cfc3cea02d28700aae84c53c47ee4bf9f48611b8c30af99f36881d16a91b2626e9fa5b2fe34126128a51b518ff14868931744b9cefdf7d89295be661474a494f2305e25a17568dc11611278160b9ea49ac7aa0c4f9d1ae59aebd032ee838a6fe11299727b97cb6f0f3891bbd69cb48535d2f68b8e28d7a7e27b8cf0b8d8e20ffa142478c06d8a0bc22ac0d7ba8b3afc491980c809b0eceec3fd899fc402108979470025ebe8a8a222f98194fb3e9316e16ef9effd147df3da0eba96289ae5ca9e362b9fedc74695958a965df4dad6b72fff8d05d4a4be04e5dcb923077c745526bae38d9d6774e4e7e63afd4922fb947b0572807c84f889a32f90fb771a682a9bdf5155a31e0986d469b0e69181d19624d0ae384985681c49ffcb2e141ea8440f30b0d206d9815d7ce97d04067f1001a7f9aa4cea198e2bc91f6310bbd35cb4658d06a1e560182dce36c20e3ce539d4aae733fbc4a2edb6a1cbf13e18d3c52332047fd2f87bb177025aaecd21a3d78b3f859ad953338f7d710c9944a348921805137c51cee1ebf3b7abd2bd2eff48bc6c2f9f3bdd0b32de2ab613313936e325cbe3749ffa7a6377a01532bf8303a282701f102eb21647f7cd7fe6703cf7f5a0f1fbb0a0ecce432b02d948eb1bb99f850eca8db8a04a2a42df4ae6d8640c26f51a6926fbbd67a5c6f408c0d2229665949e909f22b189f71e1b69207e6e162b7c085dc9b1f84c419878845a611d6c7e1500a61d132064056bb5329615241cd71aa62ee67df9700cf1ef5725fad2508f59f41c94cb7ab6362247b7d9b9914c94602e602c81e1e69b2746dfbcfa05eecd43af1277f83745bf2a2bcc8105fbea6e82656bc44a25427fafd91311c34049fbd38599b17536390b654c662572d6180f070a5e1a5eefbaee7b186f2a51d0f94eb86423f7049c09867a6ba0e8e831c963a0895b087d0a72382505fd857e22f100695c6b2db6d500b563e0d93810de15bc602be8ef3e3e953f6f5071a3cca42145469abf001a1d644e6470733ab6f9153268eb85fcedc950150ede8e01fb480db574c85a9a2f49e5df326c5bedc8ad6e28ef29d8fcf5f0188892809f680888a9dc93c51f3f66d98713b45e19ce33b05c4f11d7bb1fb015bdaf0629ffc8633b1a0fa3aa3800fa003a7f72e4a60588ec8743207b6f21cf9f96b0fe20cb33d3e5361989f0cfa05ee8ac497760cb21e4e253876ea6c690fc812946908b79e8a354dd2665f3415cfa23420fee8b62d1743447aab966c99df93050992cda4ba6125f713a111191a8220b177f575b71426f3c25aea18ea52b6e24be13f90de47ccec53fc53d328697ffa8601d6f804022c3cb68e97f047cc83abf819d3218d36ec4d9951ade708a04561c7eba07edc995ed404c7c1b7f174246a2509c5aaf325d3ab9e467377de354d7d1802a79804da159f2fbe2af88adc376df0046f15a7c6bd109475608da1dabd3043aaa2d9fd190f62e131fabe00eb1487c39431aff841da124546423ce668fe09ee5500fd5abaacf527d06e5dcc128e77c0f530ccaed2e9c9d4b0461c8cf5af3bbefff458dc6aa2eefefc4f69e522bff8463d0343815021966c9f5aee60e025d943f37baeca3e0421500081742a80c14d9ee95d07f88d88e62e88640803219da46a860f1d8aa905b12356aa842f2d8e4bd536e625d3488720018d8663186c8cad165e9f3e44f6b6ca5da5329850b9332056d0ecc5c1cc7eb691c14d2aa9344cae648988be775ed2a5a3767493f9ceebfb7e54268d5c133addbd8c83e93504f758d452df4d7387168061150bf8d1c2e562078c4ceb31eb453672d4e18c45a5c7d50b3d76f27351715f300aa0507b2293d88489abbce4edea2a30b1295927f983f4e9fce7b547a49b1f2dfa38a0bb207e1a3a12d89eac800d15deb523fcc44cedc16afbc04e160e26662b2da8f05d238d6b81fb8abbc5c40e5c7ab175242d54e20fa0f839d54b32ec3ba87ef63e407746998e6536fa169dfd074af8a30a42996c6bc6f155698dfcc3ae14b76c556f0c2e790ac3ed65101d3f746a62e45d12556afc0841c038a7f9226ac88bff4c9e13eaa2ced3719a7a6cdf72771d3d1420cf79af1945e6299988d9630305f06c1643bc4a42d090e97a460602988dd09d55a0f3338ddb4f6898290dc1bb69701dd35b1457e1a0170b56380b06e82a7e86aa2ad2dae523308089e49c7b499da12a5826b44556984b4ce04b44412848a638881b2275c58d00ccce7200eeb5771b9895f0ed4d841fda00b28ca8cfc8f9a3249ecdfc74398fcabc720b8d458b3557ed9fc641f213abf5a7856f49394d2cb8b1a3196f55fba01a9691fe8d5c96701cb0d89f20245c76fc4ca49b6467f2a233142aad5ea75b0b4599ee4a86bf48f546db9f59396f73a836c176ea6544b4a640d6c5cb5135c29dc7ca942ef01f1e9c93e1520c961a1333f1625e04057ffdd71386530417f1a5d7a7b6c91ca2c870f23eb8cce3d44f66492c44a8f353f836afba51fc3d64c372c2e8b473a721254bcf0bfceff3867966954f999a814c4a5d0b0b154b6a3aa5f83770e14de14c8e9c390d8410ff361d5f0838dfe306959254f62e48adc6112ae26527bb0062c12ba5efc99a6fa8fd341bca9730ca51a730e8086ddc4bfda344f91bda9685e9867f16964af46d2da38c4fa40aa814aa99d13749bf4ee6d155d113be71763d80333ccbe9ad85090b228552db7b5bc3851cca82e80705b740df45251a2b3321d7c5ddb0fc8e064c802ad6907067bfb6d3ea8bef3726c87bdbef9f5cca7f30d644a1f5919df5ca9f58d02dc53007b2c001b05008efd46bff4948e11197283f84526e333ea5949f592a79c9a487ee2deaf5aa02d976a25db0710717faf8b196f6df9939ea8f409defac02ec7e97fad3500286567a1fb4178d785b85619f23ef685ce551434af46516459e00fb1c5a1b88c1ed57300fec042a57f70f91aed1093ecc1f4af0d1e34f494adc503a0a40739769cc95808f2f1e88520ae5b3f5940995ba243a12576a917d1beee263657207e7ad94cd2b06b3068c011c60fb1ff9dfd2c07386d0aa3c7f461d382f9ebb705aa3db04f03b651ff004c518e63f7c65baeb517b0845d83523f57feb52cd31a0dc803ebcc521b34e3c58081033b77650efa62e9187d2291b314604b21df488e64f4990c8990a6f36a9367bc838d6719d2ac2d1a2d7d667b56511b37271a94e6ca3caac8e3a1d013a16037807ebcdfe18bfb9e0f6521cb87e4fe087283a3875e9e60b550a0ddaf9eb2b672bc3d3a708b915eb573e4eb36cd2b7774b21f8a3504794586673342f0e8b13d13e46855f2aae9493fb020bdb826bdef0cb42a5aba683fbe60c48146a1a77da9187a73b833550c8bb91060d970ac3d2ec5757b6353eac66c81f4a8e488fe6eb69e88086ca3633b5b3655a07c8dd2ce7cbbb26589b4b8f21d43d723f80f0c7cd8535217eae639934e30caa982bfab4a6c3b38fdf4e163110414168ae9a527a6605160885dc7199f8cc3d9afdf7a369f26742ea7df8486badc6bd765a2fccd717800d46c36962271b06b00dbcfe576984c58b5e7deebc5b0d24608a87799298632ef373118d225bed47cd647e298643587c0a430b01ca837c52e0788df4030c6b52b278bf8b5847f8498a9496aac1b54815cfb50c33e5f0581ddda77c7d95c38ef4d9ff0268aecdd02d7b164bdd7267b66e91f0cb5533ba3cd65840ae0303838d390668185f0f8b6235ce490134eaa7e76ee062c82999e666f30ad251cde26fa4aa5746ea2acbbc55ea1dd785d357099c6b0ee1686d9d4942aa96081d29bab3545acbbd2430500971ed86b1f0b2482530ed2ebf14f7bd4ad3ff94eca7831fb70062ce6731ed80454ba3bdf7475c4b919f13c491ccdf5669416d170e0c5793df76518e18c3772e91b5ef1c6095fd9d4bfe0d973b3666042df85b6e02f1973eceead3aa3502498508f7c082d764a1926122900a2c20f4366592563d1b55f972488987214d63853a6036339fc387248f9da2ef7214e7991c23a16b04431468d795e3c7b2da99257f678d231d8b89cc96a26a68c4b8d82a451fd8ad2c79546402bfa365c58f8bc31137fafdd03be7cef9c594a4b7f1ab17fbe233a665c1f071e5a3073c1cfea76b9b1252409bfb16ace86d006880d08f0a035f26e689f27a77e7430d41b77785b56d31c307d2c0b4b1a33d8ad0dcb042a2330947513140aae52d98ca629b717ece28ebc04fcd85c549ec66f63ed8dd952f1aeffc971f5469330250336e774f057afb15b224c07716bc2b2b33d1c490a67ac82f84147c54181cccd0fa76f5d3cac128c8dd781158ffb581ad87c8e0abda6771283d6995cc2e27a78bc28f9dcb6ab478a95f01047a55321a3ba8b0eda1db2d9e8eeef611a0e0501a83cd3b17567dd1888a1f0aa7b72c48fcdde05c13e5c909eed65b6fdaf0ffe305445d988476f4d518566aa03dde53ea7c45cd1d63cc700457b20e7999b1b04eec640c4e03c0250477b4c24b34513fbd7a58eae9ca9daa3edc905d52069886e01351a9d344404b52337532253dc27b3685bd71cc26f8626e9b1b1f8f0d51fcc83cabc8592099d3241cb7af063e03d71f688938becf2efc9160494a2ac78d5074e928382d6412719339a6e7b07234699d5147c1b8a985495bed6f49926844b2300a0c9f7aab8445de6796253f54205e446f2cd0113a8c3ebb7d3e57361f03cd71a21c2319efa7da6c0f3c417ae916fbacef331ca8196f8d104e594b55be7f9a9a11cea92feb65579f2faeb5d2d2895044ca2979969e3b65d790a0d0765aeeaf254e1370465677b6a2366602d1328664f23693de008ff39441030f1150733fadfeb6c38cff28519b02a145a4c6364d05a756b1e78670fbddedc947e13b6ae175fefd15a10564252275495f6b34c996bcfbb743644cbf89a69b252966f9f4047f5f21011a90f85bab824dd2974a5125f8bc6ad6b4a80090105e6f8342649fcd4da0d86ecd4984e72783f6d1e657e3da8b9cb43194c59fc429709e4e5023e8bb5c50e864d27a11a6d3742b5acadc4f23ae21456b9020ee694b7de5663a7a86670f34d5217f72564eae30bc40f510c2c60b081230adc5c2aacafd1e66f9568b5703f6446c504756ee69733fad92e9aa689823d0a23b97d46b99612182c95bafc9819ff1067cb41922db2dbd3ad5bfe33b48767a688aa33f393abf4933263989a7a564345d7c83d0548487b0e09649f112201c3ed470109dec0296820e1505b76575d44d4befff5dfd01c1fc49b010593470761108bde3a441c7c4fdaafb190b564170de9ab9df051e7bb0adcd9908e391dbad9f0ebf0477675ecdf311799e69b02ee8318bbd2a025c3c0edaac4a3592b79f62a4837314c11201a84f31e9e6d958e57866040a5f2bb06982cce859b06162e7a040a5b33f2be5623200756880a328c9a19ae924fb7c896f5496a60340abc3125eec47590fb92dd03ca44299e8716bea1d047d06acc421483b6e41fc46a4a745e158aca19797068758b833d51a5179d53d42138f6381735cbbb0c2b54426754a62afaa6fa98d0cb4625e0b03ca0a3d371cd2e6dd1ad6da6d2bdb297f5f13f597d5b7077ad97108d0e98ef8ad3321cb4370e8bf374dc67e3e00030171ee1601d2f95086414f8c70209dc1716a50219e35992df51d221d1fdc9a18a45e8b29ac81e5ca93d30d428216c45440b8cd926add5519b0054bb86a61b307c22323015401c2eb728da840ef047d4c2abf2996a60944aae607e0e052139c6d12df55f4e28505a16c0e7ac134af49afc298e4f7493270bfa502282bf2bd621c1aa9383e0a92d5858523b198cc46a14067c1e00d8de0b32ed1c531761786b9e130d373eb9fccc6eeb2eda5d8f75785b906bc1c05906e0097234487c6f9c1bce308eafa1615256edc6b6b87a2e70d81c86576634bba14ef1b2daf37a283a5f4cb18ae78e59b20362d53afe4710bcf560be1b0bd378dc7d95accdcd00d0c880634eeca564b4796895b914c7c506238ae3c395a68137a2195e000451995588909204a746379ac80f54b7755f9c430f7dac534b39ad28c28b99e8015638307e72450b8cb6d56c68b791582e86757a7cb90d014c783178b14b8bfe53fb1abf2a9111591961bd0131261c82275d1628a06024cd36e44e0e4fd5204894b6e17086a8c8ba36d0aeab3737556bc35642082d351adfa2270bc1faf0ba09d661ab6cf443caca1997093d787a97413cae68482221254ca6ad099580274db1646fc195b4b105cfed9fba2d3ccabff992b2e05a6aebb5d6ebc7ae40ddde56c86763007485ae72adbc1375be6925b889e3e02259bf24b1aa12b76f9ad65306490fd405938953c69b71f50d702e82e4c451a537200e3cc349f342daf3a4c3c7ccb0f15e616383124ba5de8ac5d60764c040ff56c415220aea6222300c018f7ca2ea3dc57d508c677b779fdfb326d77d64b7cc573d08f2d8d48e79cecd62f080f04b96580534a807432fe4e4f0433472e7030652a9a146b463f83bc36b46c41ffb48b1325c11a3538a845639d98c970e1445658a013a8d9a72ba36aedb034b621b89d757e460e32d771eb95782d6fc4a726c9accb93774b04d23430bc9e39b9c2181878fe1ceb632adbbf2df4fb39fc55a73fe78cf0a50a40791b2f94d84e756b7c29e6f7f8f0f4c44a87775423b21980066231bd3cb306d03a86ebfc70a38aeecec325b7f9e8c4ac1494ee261f2a283ad8a2970450d2cd5254dbefa371d98ba1b27bfa0284bfb1e4d069735b724225ad6d93912466c1a74f07cd9843d4c411fc05540c4777283635a8ba7322b10910fba436c84809e0c1cb773f53860351cace24da7f26f1aa05ae4c0a1a1cfca141c8d28c05a16ac2cb83404860d2702cccb54f37aa462a600e70546cd3e426f7ced4f257c8a47f406b1a0696ba3af516dd6e02e3d55df110ee1ad2539d3d21f7fdda85ead65d32d69791e0ab6b3be2b2a3f158a38b2d8a25ca8ceae350e29a573a3e1c28310a92bfd4cccaa5ca4602788c2936196e7b694c31001029e7e9131abc4e57be60c8d60d90dcb73f084ff7735d9b6b40e357f528f934cc700c8d873c4018b91b15293e280528bc1abbc95f361e5aa23a6df9f4c3ff3a5c6be857892e384b0889b5c844f5f0c63a698c04bff8571655ba39c0d6d98bb355d39b4eb75e43715665d93f7d840aac0539d367298834087915c53105f9282082bf20629b93072355d70ab1772d0069349560a1901db72e359a81848280cbeaaa41a6255212fc3c9d2c035f185fb6c3f0a95daac8bbc6b7baeb449199ca6e671f1116ff046fc4d2fe77d654cba12b157f2f217f4c9d24de8e021b95fc4bfea89bda26b27e2dff3956947cd72fe88c34e647c24f80a64f157eb3537a44f4560089e81ce39f4814c7c921a1fa0c5e85666555bdb78b272bde309a023bb70ec66ca3005144d6de55edea7ec7394d1b2eed9087eff331eb5bc5f92efde73c5b1449dfb5f8d822094a12624a6de71d2efba4699d16baf0571eeab785a885669f7a64c1b5cd34d43a2e27d12e5a726b993525c633796837224003c7b3602469c8e509304f21905f743b88340ed228bb1ec4ad54bbeb07e8216d08e31b295c69dc52ef205e8ebf038bed90be5f70c60bcda7782207f3db2650d2154d7d774ee622bc31ae36232a1563d1067b4eb0e46aac2fb174a39c0b066de4ad7ff88b6625f7d7db6823b3c919769223f5971942dd7d3c9ccfa912f38f6096ada99838387799883324dbd265f55e9f8b5d34c3cdc699464517ac47a02a9939a82aa997f3a37696f4fbd9555e4189f787f9f71d3d290864aac4a1cb046a33875f5b32e9096f6969c4948b49ef670d0c8add2dd394568bd98985bc772598d18c3eed9327befa440387134671d89c27a04348f8210988b37304870ae817268ea4b03bdab17d757423f12a239eab66631fbc9b8ab917fb0d2dc0b89ea17114f80e01006014f1608233a4fb1f11c9d1b76e4ec072518d98848fb2373b034060fe6b635dd7af23d1188d3873b11a5eefbc847ab0c8308b41eee2d3bcc5fb3cfffef4a800eeb7a48b65d6991fb630aa53788d49e3abe8faa94d78b2c4f9bbb2769d9501e8c5303be66cb20046b5d82acbe1496501670b2e05439b70e57ac25fb3f018d4d37cb45bf7c42f62aa5e1e221c6b9b28407e6e79f9cf6d1b358d8461b9ffa992b0d40fbb18c5de6aab2aa76e3808fac00e780342c661ad75270afe59adf06d299d15461ef5e081cd4e32aff98ba8f7e200124bb361f1b0116e66e6595819e6eafba9af907db765da91d61dda9a00156948626da81e037589f371aab04fdbe959b4f1fe0d267b311dc59689b8ced5e175a1906f194b21712770f3b31c4ef87a826ae9515df000e88dac119c99004266fc18303621595fb33b04b0b8f6306103924ff5d26540b087aa20df6d04adbb9c265cfb6d23e1de3c43204f48533b6d6a5e7df1827e8f8152c75f7bad60c0c4d3deb6b3de5392798ba377cf6824017190849b04e2e14d774ad42480797d80817db91e36a5f26841809fc418d6695c867d0464db2d4390f4260b4f1bbdde97802cae777bb192f8b093b3ebba2a865458d262b1a1ee4d826bed829bfb52369e4a13307990bffc22269b41963c938b646935bf28b90ab1cb32fb8f5ea03229c8ec4610733b4b09eb5997aecad42b68e119bff37536d9c6fc0e240892d488c8f4e26757f640d94d07fd7a4b49c7042f7aef009a8495af1b9aabaa32b69bfb611c4bb6f53958ee3812cf95c091b0f55220cd3c6d8539e3682de051b36c348243bb6f5636ccb51d3bb4ffdf525e36ca342d7c45c5e112ac40b02af57da520e2ff87c06fd575aa8bd9a2128b9902fe131d9c39a9c7fbf496b50aed58cea85b516ecd60996ff961d073b42bb01ea4b14b6b8608ed3901a7f29302bd825fb74f5054bd1660ecc654e76eaffa4cd88fa28caf6256ec32ceb2c60bc50c1544c0fad34d0a51effc2ed614630e777d126164013d729701fffa0cebd3f6ae55644dc6a0798e2a068fae33840a03f6468b5b22b60db75c6faf8c97e0f069fcbf8fdb60c9cc5879357f0188b68376da4d6a0cdeca36e729a63e0ce5f59de37e3017deb575791c57e9620fb2de96a7d8f21be0a7301939ec61819b8ae52abc17fac40ad022fb06304f71a40d20d7784408a4cfb10636de17817ff6fd862d7ac0bb7c2b640b5751078c4885f4fc28b803ce31b20267443e1251f6f248e0607299a9d912e73a18dda523911c36f840c7fd7064a245478505d30994d090c2291627f44552ce0bbc8a37008de2a018a38a4c55ec085301b5dd89d51ef0981523d72f9697d8158d3d030a48ca9aae08ac397545e443675925f75cf51cda139d3cf07e73397fb8dc520884214c292b43d343258314c471f4068682a463877f12c8581681ae56116198752e6735176004d5934c99e2f84f9072ee8d9d153a340fe80906c95d55a8c9cecc6d502255823e95d971ded6d2d6a1cea2f0496c37e609195b0d0e500bc15228be89f59c4d5599a4c5bc19e816d4f6067aff643c5edafa203832bb50cb18980704e560ad70f57546496b34f2d535c779a2a51596e8c7855c05c0e7cd9a8b916782abc2430131ffc773837f1e26bb650ebb846377daa39324f298956c08ff5cf0fbdf2a3307ce0213b375696e2be34ded5835a6a2d73825f79c92e74add5ab94393066d3821526a7c720e83f80a44abf231a3222f0dc7a4538d2f9141f7d5ca2be517827943108279dbb22385046c5ee71ddf7ea41e16b077016028f0a18f000f821cd83ed20afb1638be0296efb9865c816e6292d4416e165d8f4974c87aa9a1f9a49620494694ffb742a5ebe486c5777243355ccc4d942ee5aa512d80452427b384eb5f8dbfb2a53814d19ac1bb310b8e47e243e615d68d9bb18f33b6c59e6ea4cc0bc61a2a24672f23d9b1430d8953f7be0dba7805714cd2984c13f8a7f55932ecfb1ba008738515170f305854a408f2f25b2780a915575309959024221d8d7d3501a3538b604d2472a73282228eb7ab5f1f23ace15cd85aa28581173b847650731cad92b488f04a9cf1bddb329051e1c411aaca34467f5d1c6105c5cfc059d78f33ea865c8f3787b531c0603bb1c6374ef72adc8106690f86de4806481b7a199ffff55106eadf8e6a2349d3025e9c4d6bacad5b6c569f519f2e85771d601c0014c28c570c17659b9ea605010700789703e086fa669d91ca726bab52998ec221c9bc9bdb144304adff22d3738dd7328b4c19f76c16be3f76833fab29afaa5b0c0b15135d7c3a80421a150c48fa4f65792d9ecd9e14e7cbf07530f0122f58a1159e1bebabdc375861850d77449bbeb5ecdcad20f2b6b6619bb9e57520d187f8eee371a0f01c784d1491746686ce3415520922ff10b49cc4aeabb185a9f41c449129b99c51658acd5ee7418027c7feb40ba420ebbfa2e759895257576d9adae4119ca5949d7ca113a18a1e7aaa4a0f5574b0128fda51ac7f91453424e4550ec0b7f3643fc4f7ac1900803085384deb4e016f21bccc1007cebd66d52ecd217adfcc785d857f162877d41016f197b4e2f916689bc7d4b205625c4d348165d917dbb981816180bc59afeca4184c842973693b6568f36e477a801a8b4e6d6c81710167cffd5929afb761c2101acbf754f7a08583f33dde4978af1af3510046f0db6756684ebe3e1401d81c00e20f3185a863e216efba675453b202d28c62e3491d6eb63c7d37aa10035c85a7517795a330b154c7cb2f6083c136e9fbb25c36e6fb192d8fd5d55c7d99f40dcc6518a419cbe4e4bc143aa301705733a263402ace984fb039ceae69907c022f7219788edf9c91d30281ea6026149c39e2c208d6337e76289c62d7febb1dd5101338d03c489b6adc2a94708a556c6b3558e49ac548eb29cb428c0b584cdbb554f3473f478d86a071e67cb2857ac6331c1a99c89537623d4ade22a5b8bb3f58c066cdd5a78245b54d1b46c5508e9aa5ca480d6c0197c30ef4ec078d86c7c72b9b43f53201f6bcd62b5ab8d7b67b01078e005fb58ddf52c091c11fc63a04db4d4f5ba97598ba81cf668ca917e6bf1b021779ac9acc81b2ff42fb06b2cefd491adb7d616bac7d314212a8966e5c79f95bedf02304968fbb040c76d423533b96402fe7407535ae831ae5cc893342eaa58a7052658c43ccdf677c5b9a0a729ce66716d1e0e7138fd5713c02d3d3aaeaaf2430543c12610caceb5e667c3eefb43e9c4dde55220ecaf3df2d3e55fc660fdfd6d8f4a399394c1fad386e3a30945c406ddb95cce3faa541aa011b4fceddf4f4fa5eb8491ad0598cb7f35dab27f8f5d0179ee5c3ff96d970c92dbd67826be8b92a1012207fd80ccf0df4f7663abe11e21717fb20a130a4b7cc3f2a4ad0909836f3255c844d43e9eec2e91a5683962f89c0b8c5c7818e376b0dad205bdcce83f18f207c2e7fc4ea3f106ce02ae803ceefd8611c8b708b335f8c2fd637200cb678123037135e0f237e6f9f18b04e30947bc0f0ca60895407b3e65dbb7720ab08f8fa46332dfd0e26a4dece89b67229f4a44ce1228e404b179442133e9a6cf90af982dc87c8495ed2f55e69c3641533bb5c644f8dcb161f8373a87f8d90984853151252daf616c94e0576a0df0d7d8c8ac2c6df90eb70af87ba0ce497d35a1b0b9941beaa5f6f17d3866a3bf3eba92347c56e7195483a6b66dc4d504da7b010674f4197340b44bb4cdd871c20502fd1caea9b820fcd47c828afa01021027e538b49239b9a990ef86730c82a4a9372e956c04a92b2ad355c1b423d487c3cbc112a990e72f755b721057880a4690a8d1f92099757ba174d7d887f1d9db36756d051ededd04c9b5b5020f15eeda6e02d5bea38696fc277177cca7dafadfb0184ed207ad0f840e0d27bfa626e48ade790a78f6f0cd5a127e283a93ada77af832706bc1a66d87f73f7f70b26859093f05ca7baa3432cafd5a42981ace66c4bcec1404856bef94b15c0af397bada8e30fed61f41681050cf98c9bafc5f6b94d8e9adc8551a31608eb43ff9754dc6d389eb6c7eec194c6234735aaeb5ed3740c5712e0a2c4d550bbbd3820d8353c12a80089856d01d1f559bb8c3d85d141e571c7351aa84001f802004f06e06e0ee4f9418253194a80cae1f68f3ea80186a35893eac6c32be4dfe0731b4322014d8b666a58081ba67c209afea223cf0750746672e268094cb4b1464c4b47a6b3c00476b645250105717a8ed7b42809e35ba86b264e7fac5d704493bde4bcbea7f7852649929b2eb3f1633f1f9fbf8d7a208dbc9b3ab3dfb9b2449c00afcd03ba03660811f9b6bda0d92003dcef87b32e1856e5d1cabe9568beb8ca647d0d32f9e2b040f0c30ae8ba71c02914658d6b47e31d620e7e85d4c6e32acaa125dce72acaf3c3af1770bf1ec9a2c88cf20ab6be60c9a44b0a092432f54e4d31abd701d5515cad6f30f9a62f20a5b983f39fbc3aa7997b9e60fae514f7ca3a18312f396ea699b7362b25e2132c34c1657adbeed960afbb0aac991742f204aaccd7f80432ad058ac4062b68d5d16e8ff8953dc32adc059dc572baa1b7495a0cab58ea3eb14e0103ad282a56b91ed5d43d6e0f18f175c98e02eda980b864ce32368c60381a9912a4494a376d874de059734e878e1fe264c61451161c4d303c98339c530d2a5329245ee2f081964883cfe76601fecbd0e9cb22033e74cadb3dc5857a37c64b5e01c1d6f809f810563b38d000c7557613475297eafb8e2587192fb9cb048869ac64a48737d4971363c80a054806df784cc5ef30e083e9ac6eb8c1701b08495a832340270a4b604a9f2deedeadbf50a16f12aa03dfb2d8009028f3e9baf68732fc24a95b65feb3fd0c25e7b52b021a50c85c02830f7dd0729bfcb440fa7a471cd5ed864487bd068a0a766b5e15305934d2ab6caa449ebee6c51869a064dcdd418306763257289ca32e0a7be9a93e866f388cbd85fadb7d68f55e346b6f7fd5837b3a71ac6e245b39c1f1bf48b0937dcc0045d32a2c8f9ce0f4a29deccffb65d35af6994a24b743137efda26c028afc0482ca9034ef0005a8ba5ec7c450f486b84c65deab89d00acbcaffd3191026b01017b56bad2969c35f79fab7d7e6e555015ea9872557a551cf7636fa579cbaa67e1867991c93db45ec52ed559819a3701d6daf9c104dff2647451dcbdd51ce1f837a86dce8a7e6708abce62b85fd8d8130c8834c7446c571139b3df6a375b97a55556f7199c74787303703de9ceafc1880179a61a075f80761836b98ae5502c1dc9d860a244328d1a33229c3fd7b61fea92b8bac334cd36b971071f38aa87670dd178417e4ef29bab34e4a22685f57e1cb12c899605bef84494cb058b1458fd683b86517dfc6e142c603e48c6cdfe3813a65b8c8fec9fe86a0a80a20deeca116178e5ae71cbf4226aadb7098aa9a87265437d62583528de7ffb1d26c887e855de6befacfa6ad6ba55b26dd1dd27c238589144a2080d8a1dfb3979aa1c8e0e1691ad3911ba66b1ff705856e0d451f8720d2b05964fc6a11509ca76b3060107cadcb45fa3785c418348321caf7470dd5e52f756d89ef4642e241e06cc8626af2d0db5da0d76ba074c59a0f8496f45a1d55b61dc9abe52e250a34f6ec79cf353998e712c62e9e7782c24b68676813113269263bd5a36801f88c39dde6f7dfef6ea4c3972c4958fb0fe63b268dff39f7b3df2481ea4acf1bb5db76fba5c4981b7eab77db2c23de2b11e2ead7db601cd1b6f5e86b5e6c37418763555bc8458eff7e802b18335c72ce12fff68ee581e3e230af6a56bc0c9dc99921ebcbd1674bee1b902237f9ab70edb6b655e82cd7cba88b8e2849e2f15dd206721ad142984f65ef13f3f143e5d95c25e37c4672f3efd4393c0f26b5280dc330e74a704fd6421505f65a923a4b21c40668102f7b15dcd0f82938fad13c4747c591a3ee84725634186877219dbdebbbcb83b98118a4640fe93305b571e7e912112e63e1aab96867e438c612ff5b32b828b09dddc147db76cffe1dc8b6542d388948222c1956118a203456e37ba6178300fbf711de38c758531b004a227f1fdab53a826adf3e6329dbc6cc72f3ae57e566c502fce9aaaf7d8bd42dcbbcded253e6478429bf846db7c0185e590d391a1da5eadfcadfec21aabbbe9c8e97706c6eced6f8b1960f41e6086baa9c2f4385195b124df5825dab2e57afce2ce59628ef6df2f5c644de08071db5a1ffb3d061c3ba083c4c874bd9d040c87dce6d7039550afd7ba98da24f7fd96a07f44040639452d1e8407272a137c28233a650706cd554d37c5f44466dde0777c801ca7af1b3e29d38e2b32321cdf8f26d269a8c1dac3f5e95f0393d2fd0c1911bfcce84da0112d556df7b06748c14c66b1b2910840991a75184b669b0a83bbd65a4267d8cd65400e4adc7ec4c706a478ed4d5a7923e8bf68941a6f397b914aa2137daaf543c73f8415329a5277aa90f6a632d1bb41ba4a9da1bf741b9e640dea902c906f510af80843fb59f17dc8dee8f27c4b255c0338ae75918311eab7100518f536fc3f1927bb56e3b186a9526748c61f7cd768fa1ad1e4376c6dd8ae91391a8a7671ec8b446b47d8d92a27ae5aca8d20368870a731aa411a2ae493fd133ddfb2a2ddcee5a8b6ac3665a6fee0c64124f07339b24d4ae9ddcf0f46ec28c636ac7fa209fa4c8cfac5c4c0171df05e4d8c69b8516816b0df2b05862471f65a7612da344c479270a78cbbb54d6f496a2333024b818b0d21910658e6aad69ab2c816a8d9af76b2576b1447f613039a5757d131b2bfc37351642e3ae6a2b8b262d4160225f937190bc7bd8b774edd8eb2d12108597a1e5cb05213fdb0cf0e1458f16c74c71105ad80a14ed1680dad776e6213c2f7a65cfdca20cb7517802c7d30d25ced7df83a0dbce5212f427989db4edad7622bd731d9d74b62a318858e0586547395d772f5f7d0ab376bd6839b9c15be9c111181d0d5c3cd460a0d1273bbb818d07bb0ed2a9dba9cb59b0e839bc040bed2809aca1b069a83dfe6da1b1b800bce62747dd66a3d270991335d0a80f1fc555a6c52b458369db029071183296ed98990f888ae7240bf8ba8609111b01f64dd4c7f9df805b81a58abb8ef49be421820093b54efa715f42dafa6e71a205c7252caf80b72d2ae51c0c017d40218932150304b2bd548e4bb009aa5a968d182f9d4702f4141208695d230ae6f4c8f7cc28698d3b0672acf0df2c36e57500429b460cc89eec2846e14ccbb2c2d569121dbd8fac0f8f291c02e152e0f51889b02e88b8469ef16e37354f3f63fd038696aeb8ae5574334845ee23ab2ec48cd066aabfee7ba85ce307242abf794d2128608510bc60a4774da2c9074ca44623460ed6da1c958aa10a88deba50b1df4d783618d2a30186845edb4d44a24cf3aee719f0db34cba41ad30430db935c2f30d276f11e35b47b6315e82534d07f8b1696cfba72581e5fa3cba2e7befa8c8350d68315c7f758d87da0ca0bae484b910babc2f0153255da145dee6546d8431fe487998facc1e8a18b4b2017c0f8a993b08a4f853d645cfdd7f12294f6e65057717656971d2bdaf8846350e6a7b4db5565bb91a6f13eb8421f1bcdaba61f5da99fef77b9b8b6ab5dfdb0a1fcbd2624146e6c54efe99002a7d1ccef3a6a049aa79b3ac7a082c9a9f6702df0e8f1fd1c3e0907d13ca2d5c9943be4b5465b0a5922f69f1514bea4cee8368afdb09264d20a92b4b0d54a917b8554efaf87062f2e7c1441b8a6a44eea9fb61fa5d28b2dd1fd20e88281f1ff52d248786693efa499ebd7464b3738aced1f499222af7caebdc4e21c5893461621afffc7edb52f2f928747875a10593dc114836349b219122553d8acb0eccbb105349265538ff196a8cfe7293dfa81ba58ca7b7979ee3300d90c5393ac4a82c6276c57f930346deb7721b993b1bae09e47cd6272867e5b236920ac3efb9b9d6b41e29ec033a586071e5e53cb11e1a8293e1b49673624225597c49a63755c2f53f95e7c5d0857afb31ad163362c4f8535e2b2df837d64c0b6adaf017851baa167877f63733b6715993e3105d6b1a8f6202388fd5d6ce021ed6bd30f7c2145977e786dbbe968e032ee5929e65bb5f1f17ecbba8a587c29d69fe430780d30434db3f205e13022675a75dbb582c0ec8bbf00b37e157934cd16864af98080ce4789b24c40dde2d45692e51726f42e5f5c406deaf27b33c2e268dc7ae03f8d466f5a6043b814fe0a23cc85b7f6e69b6750e3b9352b34e78471f628ddee680be61a2cb8b371d324451181e54b00468eb1edd149feb6bd8b65621b6908b9d90dd1d76da5d212bb6afd6094a2398b2c54f41650082aa65511cbc89f8dd6b8ba81a187e288d75f120fc6581583d6502bb0b22bfa99cd1ee45bf31a3b9039587081cf28a91496152901d9fc965c954cc71dba5c4dafee67f7c0b2b62a1d7616999b27bcea58bcbbd55d7956ba41793854e4d2c59b51d6a9114142edcbe9a6e95cd0a1733b4c2ef80dfbe56c5e92d516edc68646f89ef271cd3667e0c3278c8f968e8020bbe7f51b65fe82cba0ba3104c48716d03d3975ff1046e6644b528c89f10624c1f99b4cc2936f268cb7defa05d49f15d5a82946c98a634d9004a315d50364ad980915a758b35ce3e91962e96199ede91d2cddd212f2f3b1efa465e0ed0626a5fea7652b4fd2aaec1183db649ee9504d5cc415a0f92aeac9a4afc1288341d8cf74a711984beb31a82fb605cbe77599fac0c08ab17929c6367ac54c9c547385518ce3a20ac03f700d97b38167f15e8011d92526a9d6391a57e297222d6a00a9be584d59ee91449bea91c52a80087942c66ef1ac5bbe7f38a3060b444552cc58a6b92fe4dc679542f84061ab27264ede6aafbfcbfc1d506da2b43970db032dafc7056419c003e7614b154482077ca1ddc54a0a446a8116a70f0c6c241ce2b43567bd8416fd29471544ae581740ce95a52983ec0963aa073755b73f0e5c12d796b05c6a7542d9e4e4d092fb8ee0dc89a8f5465a43e0a92c927bb32d16bd6055d2e2abe860eb4bbd652ef090dfbc52ec9fb3e86a069ea63fe3390ffba165177cb3768e7bc5e45d0b86b0aac27d6660426af993da77b80794d1532c1e20d8c52e89fdc370f8f1b087ab689df216285ad668f1a39985fdb8dae8e226e54ad723ac96f8fc4a731e0c4116c93fb97c2857ee985a8f60bfadbd82bf2b1d97387c0cfaba64beac91e72d525beff87935115581ab9afd1812f1e61b46d99ab2e9016caa2b6b11a6959d1212e80aadc5c1210bfca8c00d8ed767012d4cf8101a2b4654e10cac7342fee7fd869e8fc83ab7ae0caf55189511f266375b4aa66227e9f3b1f0fd37cf9ec2cd64d27e4c4eab78490d0cbd03ff96237872959b6b64e2893c80512470794b1855e63b1077d40bf835c93b11675d24ad6396a5327321c19543d11bec71cbb802aabfc55b9d09c7d83fca03ee3af414fda88a4eeaccbb3f4717e9aa7d07a0d19f38bfb3ef445c3f6a5d0c1d9d89a2caf7812fc36d1b1e10bc5a3a5435af6e2087c34e770c1d73fabc8f0fa908aa4e487a8d2f11887952008622ab8b8d9db8d981829c54e1a5de3eb3766d0f5955ab74aa2486067cc75f11b74d400329c23c07210bb1bbc39204f83c445de9875f9dcf94e0415271b5925eb32813035122555500f78f0c4a108d7e0a1b119c278920e1f3ba344c1fe250e56a577710377b4d08dbd7dc3491f4c3414bb9fc995fcbd34d7b408f36f3b10d8af513d3a85a62428f949586babd70c15b0630afe1db956d05ec8fd7ee4d4250df08ddf254e1c27ca192accafad9bf4c9301b62b7fab614d0667351334c92a6bf8d2db855b9d2031d371059e1569b4e3df408b4e5613642a6ed26e65c7aec3c2a86cb2533e29e80c08a3d03d0e653c927032393602a02797119d62c678870a7a13b6420bda1889804c73e930f65179fe8023f3fb1661e64d9e5479e247f7c123e9666c19e89b634c322d4d1b3fcc45c61c4b4574822967601096a83d870ac9ef98e82cec79c008de2e0b9c7afc95e86b226dfa81932ba175268f0d7e390951e40af0b057beeeb756f7983496ddf18228bef4b3a8549f943e88946ba49381da02aa1bd67d0d737194cf1dfbb93e36b5ed54887dbf908a353b65092afdda2d4021b575f5ea1d4f9cdd6e968a96ac3dd6a442343d90b1a28ba19d955bcfe338f40f59e53812f5be502e2a082e501c3a356200b47e7f0575bea4bdae77a9e385ea9c2b71817fb942971cd659b2c3b0f80b0342c7c3bba06adc8d75caa5d44209a8bfc55000053d43868041d71065ca61939017c00d16f9398704c052eba7e4869d72369334f29fc432587c5608ddb47bf5e6c08c3ce58bb1257b12973a89b3640110accf4f26a7fd0ae57ac5db02b7709a27f811117bb33669f1139e2043ac9d1286f288cae9696a75522e8f4177ff0b7456f6170892c7865a92ad49af6a85d11ad4bf6cee3d3e80c7388eb3666ebf4997812fe14e795f7515979f5f1ba3de1592c79288408862985bfe5c33a360dc963397a809ebdb21c20d9b681dec0590f8d98ed8d959ef9fc95de6adf19ba01d9049128f3f9f0e087f4c68d201c36b8aea07a3c7577aab068f79f64cd709551beb2165a277d2a2dbf89e653e512f0f4d905c6a5ec25242e220cf838f5382262481b6b298e32438221effd31cf0a49688e574e65e8c081fbf6a680fef754cd9135a16a4f932d23b9524dcb4831b9fc01e52c454a451759a82e776faea3a6f81cfb8373859bef442ed3dea358bafea3501b6b72a0b2886ab83a210b5378bd9563a7908d4d33622dc3e026d79e58f16a22bb7d46adc2a982cede2fcf5ce5ce7c5ab567c4b60d646a762345473296141bf0833e797eefe35713af32669b1cf9617f75c785300e2a092645bb62f23887c13089796a40954bae6426e1898b3c683bf623cc6ccc138445c5c52d1166cec802b0b6b51d52163e6d0c55989cda9f8d68270f9924a52e197cd38c0bba515d2a69a2002162dafbb5258a61581e9cda65163040cf55f7aff0117d6c2587ff5b3fa9660d53cd50b8880ee0ed3153ae91c591e6cc5d16243c1e06d45e827b377e988ad5d617af7f3f2686acf249ec0e5790615d0079dec7f9339106d3fc44463cac394108fc86c83fba3b85fa4c78aadf5b4d9ac8eb78d0c7291239e65a784091e77eaae8ba450a0a17daebee902daf1d1e460c992f8e5624583c8c651a130c14c188c585cdde36c00442dc29f50e5d3e0e88f6729336366920956986592ffc25e42d4fdd76fa9ef2b8475ec56471226869ee8be0e6bb0624db6c7808647cc9bd88740bfa19d9cd43c061ba4139afb3e22d814d546f9e82d338119bb9875a19294c034e881ab51a072e2888fe8f7a911a0f5dcd830bf4d6edec76b0ae66524519092b7ebda85eb38b432247f3874bfc0023f6c0876dd1476d29ea17c71804a813e80544fd0a3755dc2e37e8d951c8cf95d65192fc80bd21441814bfc249bc4d8802e361d36b86184da2697f3856c3dc4f0b86f1fcb8a33ba285d0a3acef6a23b7074aed1ed15a967d94339086ecdf896a7318f7fb32b864631b18d5761e2fdb6c79a1fb5c043d70de28d5f0e69dd533afac3b3d2d9cfbcdebb309b46388d649d0ed32d30d3dfc918eda088fa39f84c5694b88b6655ece8980b654ec3d47c0a0ec7b7797c0614011f7cf57f9c46cd8d2e90d9125404f92b9dbb36bd3d9f6a3abe9edd2a62e0e595a2fee7a2f67a344090120056b3023ae6a6991d7d20ffb0f38d673498f4cc5cf022c7565a716845f054fbf1573a6a733f54c324376112fc507c329feb837c31f671bd5ef970c28519b66b9fe09e83a5110bc14c031cdfbd4e39ff38a552342f369220e9cdd623f4143b3a4caa2b2a87c499f3fee9744d5d2cc85171825d988dc5bac0b26d8e7f43c25378d8827053bd88b5f06efa3854b530963439d108d92d87088a853e0a63b9743b495fa2d4ef797dd9f2cc3e6c1641d40f112b2d020c13da59676a122afbf6b6d50355ba901ed3384d743202178c557deca94dd20aa16223155b93e7a59eaa0416b6dc4ac8cacb3d39d1c28bb6b73638628266a42753149e3c30ab7f33b01daecd2bbf566f8aaf5a35892c3c9de83f20b90284115ad4f46b9913d9985f1e76757abd9e9c288cf1e45364a6d2623c0a04d031ed2017393f9f34923d5e79a5714ad7cb7404e3fa0a90ac881ee08f39967e6a5da7866d6b2075c293eebe7f4fcfc80c22599d12bc15f574955d7f49e75234d2dfc0536849a19d6f419eafd336789679cb96fb57ac9a6698539694b197593403daae72c162baad2a1988df8343b146402ae6e2a5f30e544cf5511fcf075ae834741acf68cd298ab3a07e7d8b53f040c677542af1221e261de84cd62d6c0956db0d92ef7830ccdc7928124ed20488644300a99f7f0cbf41513a84178b710afd5b47102ca794a3b267a01623308ae01d60f124d88c8af3695bbe8637250fe46f53ebf359ab6e501aaea643d3d597babc437b243ad1bed7c096d2b6e2f0308708c0dead206f7d6092974f0c23d10966b4dc501a03dfe0e842dd0d2418d35dfa0b7237ec43698110e8329a6b2777711535eb53dc2677383c901724df564a01939bdd9456b93aca7c0eb6117341533a9335a10e2d587c11d9916d7c4890c1537a439fbee851b0824b1f523bbff93b580126677f6ce02a9a5034fc9341b8fdc790f9ca2a24820ecffdd7f6036e4cb06bc0301a0f264d46e84621649ad65c7f24917f6dfead5d9385362a829d54c14829e16d559547f288d5a59081f4c9c6f511af5e94804f10d1fa4690ace573b82f10726297953db7c7d38523658c9d4546548acea36cc8d11238a88d8501462131b9d0010cbe488048b0f83d203107bad3d777c587608b6173c8c1d58dc88dcd2069ecd981f87cd9498196c40940ddb85601c467ddda3cd0ff014c7f779cdea0f039f28d37821ce4db6669195cf5f28741341cfdd84718b4d573dc633c42030ff1e7b8d11837ef70b52fa62d2b3c6a8df64e0cbddd91e5ca87173c32a531eb6a6215f9ac61d48bfda398750ff039ce903561f62ee75e1410b8c884f889bfd4ead3621b1dca902637cf3b4d54800eb58155c8869b7a5e1ac823c3ac40a191c894253979d0a7140ecde4f6c412ae707fdf858ca6c6de95db80205d1946460242d8887b9219c6eb1d67d94589dae015e2cf4f4709b5c0317b9513044a389110d22f70840aca7c8c6fbc81846e2a486b27304e1504033e9805bc41194f1b04a56cddaac691f9d612a6c95ab3d2b7dc4f25df31f5aa9349fae70ce2226f121556872a1c1c6333ce81a47de02dc5adf68e2973399991810d14dbdc2fbc6c022921383df5c03ad371fbd00b9bbb7e1ca6c63bb9205aef492813f654c2fe128c1e98c3c2843400218efcd34adce90f64614e752556d77eca1c9232ed2d7cadb928e330c1ed0f88ed9834ad62de3bd00eadc32e5b79aae44e1331b15ac08356eedf0e8e6132e820b28990b837246cb03f54e073feeda6a8ec4f9ecdbdae07921c6f0b06edb399f77895b33c843d434a86d538e0e8e37700c88110fd54dc2fc82c686866cd1c29ed427050751b2966acb056818da3e79b84c4e37b1779da3a101f812af231f792285c789f087941431225bfaa2512af3f4121791b987a83c5c46d2cea321ae16813056e4ffb6c9c6c346d79f5e9b9a7fa70368af5a7ce4f6b3feafa57d1e61684634e07221012e4a03787c7f8f3da6d3107bcf03eeaa47fa8d6b09b2e7549644bad5974b374dcc1da11221f81c22f9343cd58e43f229a3b188163606d4a0fe1826f6eb66d1b6d8a3ecb8e3c434d419af53681c842521ef7f29a3660489543b66d80a1370600e2ad5316f7d3278d376ed39d377a076e01b9e95d65da55c2cadd3a2b6a3ff7d40a3cb14545d08550b967e48da7a903fa632b90b5667ce5048ecf42957ab53b8b712f0dd68004351e2c5d89bbfc2261bbf76795d1388b034514ad4a335a4dfbc17b9f736232a470d5d6b9957d9f7abafc020523c74bdafa9b82f2d48554d56db16a04d0b26938d2d3d13e34cc3a1dcd9a0f138f7471973e050bbe26f6813ae1fed03ef91daea1cab3490a37d432f1af4dfa4304d1131ff4f30cacf53153142f348f7b51a2fc83e58279f0e8603393321d86b97ffa7cf21c8a1d198bb9a8ee3b51c65e4a4e0ab69b62f084410f38d9039920556751fe3391bf6560e2acb3151700311331b02fa54e44bfd4ba103afe621cf8fd1fa83d8c30184a15f6ad48fa9c29059867fbd5caf143a40282e1d819bf3c371bf31f62df1d80d9fbbc5cfb4a18db255c51b6d55a912bb0465a175c291ab13f838789ba549214caca259192ea38bffe43c84a87e4cd5d6498ae94ee3a0f99656d3a825b7172e9bb28150ccedecb77aca715c441b45772fe2ce4e7635ee4c8258274f3eaa94c482d097232a66b7611c8efbd80e5fa315218ab857ff5ae03ff33976c4ff92f1c4cebe43e993f7a5a9b8b2a132e1fda7dec957b3b9e98d36911829909a284ed6ef1240e02171b323e922212c78cf84ccd2453b092e928c9a04c22f7825300f5345c3dce8e5967856b084b851921a6257a68d933b101b9cd135fb12a159c190dd0a212960e72c8a0fe64bbe958025dd845d296a509a760702692575aad8f0bcdcd9eeb7c83c5557b134d999cf01ad47c7c1cd1ac7406af0a44d5fbdb9eda198aef9c41e77061506445e4813239e41dee6cadccaa221fc66f6ad87cb421821fbc9172d04793c0c2e044ec1be5d81e006e2016ce7339022b7d52b37b36e1e648a02f4400902e9d8ca229565d9d3ac04c1dd6a8d4368f302b789915aef24b76d1859947b9a551be34ef9b4386ecd5cba2712d3f4a9afb50bd2f2575a8d98e75b065c8e29328d89a1fc74f2aa6174ddb527eeed80e5fe83475d9a78fa525901e582a05e88f8ffb558438e32d68a341de6e95e6944d10ff97abf89cc5c4d4abb6a55898b09a9c46810b54548556ba7296aa31a80e5c4e2cec5dc58b0c8edfef3e41541b79bb844ca925901533c7c0ab303f9ca371879cb68ed6fb9b900476dfc175be1803f14848ebd88d6af69bfe28be8d0da3896e07d8b8d89f40f348b5ebf010698e92d44d4c2976962d9b576fe6f9bff1ba382a4a3da6a85c7f087df812800e3939c26b4c44de3a2bb4d0d5e5e6c5c09d76524318c2116037afd338985499b26049e7eab7853d6a773c3ebea01f73d010159351c9a303e11c1d5bb9970f18c4ff54d4b690a7107664d73770cf628b8606d13ebd6e4fe80549fd336e5c6808033a8e60d693f5dd3a339fb38a39654188a539d984cbee76662ed9d5d28a5f1a9a28120487f888d825f7c3541851a6220a2eb06c7380cb6733978ea5e41f0ce2abed93029a376c66fa7eeefa056d6f2fa00f025be678e5b4a25ecbbbef826495f01101205991592a7fb231f5e0726c00a4b618206d9a14843f4c30473b4e091d92f41f05ecc1269559fe17fa2bd7b881791513551312b058be92b229f7454c153776162f92579b10ca2b35ef37dc95e504b6d92a235462325f312a95953174a3c4bf8a9599ed4f44f49dafb0fbc49a3b2330b5db7588a792ec00d68677addcebf77680831cc74302c40f2b0268144b5f1970ab4839cf8d466ab3ab00547a45e1eb272519ce0a613866863b69f3e5d9f7b79eb9313dc04a09462c354c2845fad52e5406cc412b77a32c071caac2c8032840a4dac3fd01a6b4133c16e438ecf28812432b51ba02a939892bc84c087b54be99705e77a344021f2b70964c2dd445120323c85b4a2a16334c682fcb0869ac0503cced5b51fbc5abba6b528ab0bf139685f9b09f313c0e9db38cb3955d49eb5d72885d694d8ddd9e42531c6f11297e1f15ee92be370a967092a09eac04759681d0eae33e5043cdf7d2d4129f9c9fbb227270c4ad69db3b3e119002a3726783f273bc1abbcd469278ada4c7ff8acbc5a839aa52a33a8223b3a03bc80eda648a6584a220e504d435a9c014f2ec135faf78cbc7f488b532a868baa0f4536bd4323e7e27c1951e72c65d2b8563c311b676f7c2f7c6eeefa176085a6f08b62e03a23c131739e87bb9dac05c0448093a22dcb491b8389fe4c6c962ff8843ad0e76baa23a6ad6aaf29048e3174d3f71b5e1109852cce9b92a8c0b34a065d5b761f9baa13b335e95e8927483c95c7ae2baf97f59dd50d397c6714e0a5f1e3cfe4544057ede6ee94bd4e21b483ea50917d1aee469701008d0baade1b55e707eab23e929cce9f730ec14831c9be24dc7e071839714ea095568881caa5dadd42a370032a289fc0094731ba4946d479788ecfaaee55b4cd3466a5b4b2446b04a30d5a5503e8523d958afbd1ed1fc70f487a579d11d0816073daeaeddcf85cdb54b54f177e1bf3ddec7075ca8ef32f9735cf82658a145586bcda4a06ae26d61b84a59a0baec5ac897dc5663c81b3b54976e73c92d07f26fc98c78c733eabafb9559e843598ad76cbea68336c70786cdd7e7e0d680e7d9e73caaabc8b4f8d35dc60a19dff7373017d59b4106c569a2839c7b7b274308711c58e658dfc9ca9756cdff2c1716010df4eca49e2d1c2c7fa17ea110a979d7d046788ff86f57ee164fa2491787dd64a5e13a745eb60638c20017e013e04f3572fcd247b0d71abbea295dc36134b4c238b963a08b00e1a690a8374bb12be81ab11026a27aaeb533d117223a35d4ae0467a7dbbd0d5830d6917290a5870e6dc46f9d9398b5173b4dfecaef3bf7ec096ac4253961925d592ed75d9579b574a129bd1025492c08aa835ae96cbb461271215ad48cf5b208816e7cd0c847473feecf1d419cb6bba5d77fa4225a20288f5274846ae94f5987bdced38d756bd8c396fb245a9e2b4c1326cce16a120d8c30d3b086a2e8e6245a358ffdee715c32baff63fa01a32659414a0e24f2db0e2afaec89365f7c45e22b662f31240e7969723e008876479cada45ae650743cb4411d80eb74ade9202275304e624eb0fb12fa93f1586f671633907e4b713d4c05b5f2b1e70454916e45d71ac2cd380764527240489901ee3a00cea6088faa2c5c9a799ba93adc666e41ca3ab4839a53321a33195854748128a1596b4982f223b41f48d514b6a484eb9bfa92d5af769bed16671f625442b5b0f289d39e8449cbd8bf01226429c5f4407af4a07e75618135067374d6e9a2b37e064ed786e6c7dc20cd45bb73b95b794250689e8a557168277439e4bba35c842c1ede78a1f7944d74f4ff9ae151b6051612891b53a3d9f224672f8744d6d09f81d5308fb7ff59654588084f44158214eca054eedab94d6930b15f1452c727c5868f14e02c9d9945ac048439c393af89ab61a422ba67e4d8c07326c463a2c5712ed5caf34420296a3749ebc694fbff47f69753195e2489d98261da76dab7831f5e660aa1e21961104954140165593b5b0b6ec9525a337ac3d0722d0252cb0b1c9b958a0edff3471ef0d509ecb6ad68d5e602a17b00ea99fc3057236d18676cbb8bc5b6988a8d213d1d31634f94a952510fb1ee1b66ea3117cf65463fa42b44b96d303829acfb26b791b30780b87c1c7cbca3afcb97f18adf3176b79f003933d671d5cd48d57f049d8759b5475a5805373d53b003bd004790bb1a6403ac35a6fde7e9aa250ac2dcbb0f9872181ca2f6f1c11070a53a37cb88e2c4ceef08b0dcead10ed26f90dc3594b7d2b1089c4caad77fb6c505c9a141dabac72e3ca17234e5567c08f86138fd871a104dcc34b0bbae329470851487f16acba4b6da1f4f19e239a6d68dd7c7819706a2aa67e77a778db72d911a77801c443b404e6e7a1e91664bbfce3ea8562cc8ffce7700244308983df19e007043dd66d86cebf257cba8227f0993ae355c4c6a1e0dbd5552350495524e5bf594fa653073ca5a38b4ad6e20d5e1f4a4b44d54b95582d74da101c06625259ffc2020662b1738dde95a6c67301f8d887c32793101f74408a92a83c199492de55223dfca478067406586faf309c2ac2a15c450fb60265b27fb83df96ced792663d61f8eebfbf827e939bd0031499083c8e5274b13e6ace4991cf85dbd35621fe46219477bc996b6dd7163c85cd84c8f05ae9c7604e19a8b2cda641afe1a20db79a5b2d2c937c9f31686694670805cbd7f4251f99568fc1b9afbc6ca0fa401221a5e3bab4365240d75b84f64be0195a44da6d2e0ffb1cfa8416ddd994e15307f86225c777d8257ae54bde5e6c5ff8b89ef5e225f4b6c7f4c3bb50851cda62154d0081d34b91344ac33d72b0faeb2605e8bcb69a9135cafade1a1c540ac9e10193e95de6d58591a202918c7044bbf0bc59186736553289cb3397189df8a1b09dcab9378de24dd0434d46b2261e76f2173112026dde1483186ef7268294e8b91e649b36e83f3c92570b334e662233c272b9284a6c9a0da34bb670ab02f519863a3b50f874eb95648b740397870bcdce75b0d66e7d9ab763b04ef06cf5434c1b02c54c51e83a41d7cb7a9d1d3c516cff1fd371737211128e46a2a865fe1ed3aac76d689d9cf22711e879b8d76f1b889f61aa1df3b03dae0c35d49ce3027182ed32ff993444c8f63e4f4a8065c91ab8b0944e24e74cd1f1a244a05068b5a43b1497df6ebcc3a59956cd10b903fa5620b949dae2a184dd589154ac609f1aa9ef3fd0c3e687556e6c943a1975df244d8786240c59a2e490d50aa785bb0eb33545ad00aef11fc1db7b1a674874b5f89dda6e307a6eaac1cbe471c6dc279745dba6e8a982ad94f5e26ee00542ac96cded164bbc9b3539e4343606f84d19a41f76a3ad681a343eb32f7a67978a496b15f4914625ae662a173d536ee6f8a7ba9347d1131d7b96a3caa44533f5038ea67e9993a874bae31966bfbc794a1f35b9efc249a6076e639bee04c1ae6234f66cc53342b9c5983ec68e165797054adb636ae71c6ab6973b63997012747b504ee426a46230710528245f271b0b4b120e46e731cb9f978abf4be721391d0915c46a302ddf7b61c23fe34e0b7988840c8b06b22ae586443a2623be6a79cc08940bf004ef733a5b7a559fc8128986c71170c5e461334b812d87dc9ef3c46bc0bd667a144bc0dd7c26522db94ff204d942a10408f3107b8d3478f51018d0d7afad1116121361260f785beb5185b3a4a91fccd66e982ca89b438865f0d60913eeeb9df77b36e3e79738ea1517c888b6dd26d8fdecd1153dc527187c9a98d984552102752e695bc2c5ae18fe5ce73dc0f050e0d2db7c69fcc8c30b878821a3d9df2a4214869075ad75c1f6e5e0ba980e07f91e53ce10a5e8c18fba754a722f28a369765561456974c0dca108bb1f420f833acf526301b018564d33e1aede0335b000757192324e7df4f80e4c11e5b8d806e2040425b926c26f9be6b1e0ef5dfc70bd993339bd39033abca0237dbe6b2073d7327f314883b052f3831052c2626ed92561d50b1be74c5c423ea7df697540c6ed4e6f2737870ad91d6c80554a17ad37ab9727dbdb821662bfd23a2f8c3e8687e299c8a7125a75db559d9b4518a2b1003186fcd9838303260186ab2b0e44b35de5c8354a68ea69d174b7c33c10f3879709c5fee8c1dc22f6b0c07527011b0cbf6c4352b391f162e8c7424be55e855b27d3ff613db56e85525af830dd0233d1312a63e52be6f8946afc365f9e6903b23bee323535c3321f4124a404496318f6a1f94230d8452456d53ed43f322d04928d02ac565e5d5481699de8b7efa0ff5d9718a89e64d14f05716e3819b67ff4bc5548cd8d8ea9a94c313cd733c803f15c1bd8ae962af3a4d5c491f590f2b3b0ba26c925e48b1b9bcf8e7ca281091b0f5d9ad2f45b655a2e5ae9739edb51658c7a7cc788fda2c833646811f9fcb2792b89de42156a3c34c07a808879bf0abbd9b08343b8e08adee2a3795d5f24196e719dee7b220dca49eac557a741b2bf9512b46e665ae7061f0c9f4421f8ef5d9ffd215ccbd837fa15ac05195b6ee4699740a5d835912aa1d8ca58246b465e9462ba9de0f9fd555288f8e61863697560a366fb0d694f1a03c78ec79130a2bed12f4deb851f40d4eb27fd5c69b885484e8243138a5279c4547084ca1d9f1c6b56ca0d4e4e3c0b616485bd8fe6e5b8c237f8676ece24102ae885238456e04d8cf72437ec20acc47b707dd0d1c8338ddccce6a67f1e135eacb57e4515deac7cb2705b1352a16bbc9de10c2a059f1ac7a2156ae841cb41f6f0ed1af73cd166daba0c5d5fa8382a8fc877b746478b2564aae4ce2a998b097bc527b44b00ee4bbfa0a14c7b807adb4d31f070594c494fe1404537c644290abf3174fae852800e6502c6c04a0d00cdecb84e291c225fa40ddffd53f28bbf2bde3015acc0a03b4e6636ade4f4ed88909ec9f51b2c1028dc74fe40664a989f69b774ed56388b6cf2d7a3f5b657dc816b08766bc3c21724284400931a7a1d8fce0fb947cb0935985338ae90ba30e407b2bd5f5e85751f6be259a01b3fe64bfac0f2f96e92898496c51af5580638e45890a8b17957278a474adaf90835c9bbca65c39d6a5677425eebcbbf7d32c5eecc1c495440f1abaf550ab0347431c9b24ddf0f50ce24a2af3d964039cf782a7bb6ab822b85d6587576596251728953d92f6843d14c207528386942df47109f9d0a405a392e427f1f34b93d83490048db38bb81766e35cdccf2fb903cef71e4c9228559dd1aac0de7fd7658409169c468d382eeb512f87d7bd8e50015f74f207b1044e89644b87db3e0097a7ce2436cf90de5f043157bc942683d0f66c535623ff162b3ce598af3943f27d1114f9e67a466c5a08bea0d97c2cda582aee94e3f8a7f7c4802b7df4fa798b0da5b8378b03cb1927acd376467d1e78b860096683a713e7d3cd8416fda734ece30b6dd376644f1ce3f4dc0f77eba11b913ffcd52335d61891c368ce76e63454937007b63a96ac57e330687ddd2ae8a9fcaa6014849e70e9c0f981447e28d08eebac4c3b5838af4a321a535911f6c539b109705c06b051edcf5c4203a3c628891e741ec7285163099f07f471b47588adf898aba0b7189b80f525841d9f3e1093824b4372fbe738316a43342829b7b1e7374978610bba6d961b3ba4a74d5a504284693bc9d1ebb69854370d0b3a8771bc71dc02077982b8bb03338f600a3d2ebd98a57bf3d46ee7225c618f7ca9071c6a958740202610ea0282252df5d2c62a6651640c83fa8e37691165b359a786ec6858480678b62b8ab6f3431fe743cb023d9b1b8223acd86b169a164e2a62e3626b780cefe4a4088432df4a244be431ae422f1d586faa7bd221433e88866ce7aa7b42a71cb263301a120691d4b5ffcdfb6b8e3209762b3edff6286823aebc5451670792aaff5247b313378042c427aa37816efa2a06a0424f27254ac85ce13a04cc641403b8da44ae5ffabd82e7dcc195fc8ca23cb085482957119db777e14c787d28f82d83b243518e0dbe28ffa5eef745405e37b4973bb43c563124854b494ef58af000c2c182fd76f58a8743bd360478432d4b0ee97ce1c581aa660ff111e65f390cdee20420d8fa4d8d692076167e6b2ef772695fde4c3fd420b94ec04761bb129598ee707397da0518c054ce178a646ce0e9db289fc301f2b432acf1e1cc9ef5eb56448f598c72586fbf008c08e07fb047bb70de34e85f5dfa9706bdace478a0be5a82420becef94a4f1d94263de507bd21dac40648c4a681dbf628f8707d2d2c6dd401904ee01680ebe823304928cb0239193076fcb5efca1cdeb12fb1e109e8491e5207322adf4a044037f4a02818fe52abd813bb931c2509640afb1aa2cbbf218b08f98472092cf7fe0842444e10df19061e34523bfb51038ef29cf29ed18fac3f290d2f211e228a219e0ac384d3ee01cb628b6e210a684ec13b15ee16ff5c9e7c51e1d83657f30d74951381a2cf0f8e4bc458290ba4b340d7e659294937c05ab75c5ae3aa4a38f4a4a0fe0dd2497877a83702c83724a24de7152d459239ef472afe5ea9ef091b66012237412cca772d7b391d89aa8512c21ae54d3dd91f2bcb46691dd9d955ab171df60d861860137a5a5dfdd6fb4780b2ded6017d0b663bd29a4010cab8fd38eec114a37d01aff11406605486fb8ebd155f275015e2e3d893ad5220178f97addcfe06dad81826225888604530d0a16571cceb45635fb0c69f60ede6ff091074722a3d749cc99eaf08c522433b2a75c12532437de9053666968d66dbbfa4d54683e1635404e12b975997243d9012bf523ae51be174232da9065ed45c4886165434e87a136943bd4ccfb9c5e62aa2bb41ec270296fb4a2eb76f1091f79a2af8729b675caea7774babeef5a5b1dcbd7b88c31d83847edadc280a4e02942a1419c4f8511fff75e4ea13d3d40737ed88ba62cde622fab19725a98e018b5d51281eae636a7b547ea4941c26a8b7b5916fb648e274040c6ae13c35fdddd2f49874ddacda922a0ea56eaccaf1a2170c18847246c8feece050b4a1511da13d151f30e331880db300d0d58cef821c90d02df1e817e68b9bad148242f4e016169eedcf00ed03054e24275de95f013ff591ceafbc27a9beadda8c0f1adc4106a00e37b9f8c1d2334f097b9ef6602cfcb10f8c1347717a563c92f2ccc44654df86a0a25d84d364da7ec0d2008f677fdce7e454211244da0baedf597cd5a641a82bab1e959d71eee131a0b05682917d427db195b42fe63b504cd1fa98b8011218f17891852788d1763abc05c7980a2d88f1102a42db1d38804c9d72d7bf4f909c1ff1dd4cb3d2aa543927385e0c89624aaa6c6399ac10f597c568b01612dcc3a981d10989c4042141611b5fa3c7fa67b70b2e8e8016c41abe720ab3eef1ce57c2723de3a48133ab87f047f894f2cec8b5e4c5ad1d0dca54a8315f4e82f1f2d0d7c0c5f60f5c51ebd70bc013ff448b596848de3c2bbe96a42941b86466d643978dfc82667b19df25b491ac04907d66cb73498242f88c54871da6d1c592eb8b9d6d7b3f83276cc4df65ea541ce82d7b84cf2bd3e6a62a8631e3c9c48488183639399ff3e7a980ca4a0fa9f41b6ab69923725f33603a0d8bde144a4a5cd10b8ac21344be6d7045e58b680fa5fe684f3199361f5dec7ac2a331327f4fca6615ec591818d8a8af43a8c92436c72d8a884f11b9e414298e191192cfb6641ba2cf6f1a5f494e5207e14137e16e6d1f78a2c38e6d1cb44e36a214e0587323816448068b04e22538c1f68e712c42311904426712dc5c50e89908ca93707a60126b6e79cc09ece4992cab2288e226656ee3c83530971ea6196aab06bf41e4ba08ab1423559e22b009c4238335a44f64e6f68281d98762c296c8f5c0813904243140db2a3db11d191432cefd034dfd335ece71dde2303a9446d9339ac36e59e4a31af53722e19a8ea36dc0c34dee2912a276f1658ad823e42aab286e1273a11d804aaaa3c4be2dc36e8e5fc2378c64d1edd9f26d245bb1c0f7d9e3413eabdd20e0b171275eb208c94307a5320a9604b32af250a38ed1c1c7d103ab0051d1c0e781888440b77d1af5d99c32ae8ff3418677b3dc7ce24327f9a934f8c02843181090123229bf2c463f75bfba55dbad63689cba2df98a203017c8a03da1e8a6e5920da15a903d9abd27f458ea7769d7c31cc9d5666cf13012eb38c2e4c5d4817d6c9680af1660be02e4427ed4b600ce01cd600702076e890f040c95d081e7d3bf0c3b271a7f28ca15b4b4222a8027941dc6def2df79652a69402d206c606e40623d80c9ff1a2a4c1f519df2f6962824dedc334da87b3ff89c940c791b8153e3e05916bc120a89953cbb7a09e6680a26779fb32c0202da8a7960641508752097452ac766fa01f10380871b9efa47077e53d6fcd65480a19af7d3cf849c67becf96466762dd44df0c5869e665c0e5c79ef3ab9a28cf244ca64109d4376219bc005394a981692149200512e71fd59240d98013d246cf4b22ed64a47739f59f8f33ea3588b5d064856404f829dadc9f290460c262b7c6245d7d171978e8e8e8eebb8cee7222a78d286bbf58a80cd521bb2da9881804bfb47a24e6e57e089629ea8999aadc93387cc23e66a2a993ab3c8f567c9cc2559ab944f230a2b6a2f47d0abdbf990f1392f36a26f7911588410a719dff2334432b04922e1924abed8885a5ef4a3a9e049c97ae6e34d230adbaf434a6004bdba3db4a6bd4c617d786eeb5e0629e78712b0f839c958dd9ea823660be965c6b7bc10a7fa5dc04f31bc88f46223fad18fc09cd18b5e889337832451a4171b979ff12ea0444998ef67bccb8f482f3632fce8650047e0cbf72ee0cb07eee0a7183e0650aa402dbeaf3526634cac48592c2f56fba25d79d65c8245a14260ec86fe7850b7012d131b70bf1f31d109a0d440766a29419354e28f188f9458f16f0bed6f037de8d5953ab012a808c5668809569ca86e990c2543c9503294f6a22b64133551ddd665c4609c73b240f30769fae91af3266886283373ce39c70bf8a987840192127203c60f128689cd2143eee0c8144c4345e7a84cb3bd8c0178200b143b234d4088f18425cc9546bba7e3eaf63d5bf50dc8f340b16dd53d9de366b58a75cf0dc8f34031ae9331f2c701f246de0887e7755cdd34dab205700956b67a70fdb9d3a4681c5ce7182acc47f86ebbb7043e5066451ec23210827f304d739288fbd2f4da4cb60a010f9c59955158bf75369bcd4a386a6c6a7b3ee0669e3c7bc6672882952fbeb681de337366d4629dacaaaaca65bcc2ea936aa44875d59eca9acdcc524ad7bb58ba4206b7608204ec94f36385253b7dffa3a508d7a90ba7e0aa629a15d3f068095724565c7f1a46ac58955c7f8fe723c2ca97cbc31e3f1a94b0defb007def49972b7485ae303564479d02099575fd4d9508c35415c7f8fb083d28641a661a174161e57bccdc1dc182eefc5e76083de879f0fefbd16b94a88d22286c836255d2a2bf4d3925d80357afd65ac11e82a017879b99b99b6a929341d0db83f6cdddddac854421c0fecaea09b2bb3b6ba129d65b591eb6a42bc4a7461df1fe03736a4769d2885362eb0adc99a982179a68e11a4f7ceac0235e71a4492e61187f9974a9aaaabe5c66565555555555555555516aa393cd5062eceeeeeeeed6f89b58e107a8536c14628930c1211858d0dbcf612134b9623ce5fc5881bdd7e81c7c7f4e3188abf56f93f492c3143bf6583640840cecce2311f1ea71fa00baa28b2802907f38a658973bc198115eb8fee5e072ebd7e47ae86dec5b30476af1841e273be37802fdcafb57635f23d9db24d6a20848ee46e26b493d4a18d44702033b03c2bb01283361cc4c99b97182157bdcb6d0460947e720293839729c1c512c2547ac82a8e48e4c713299176b99ccca703c742c2cd3f8f4004e8f3aa43be372de6b37a42947ee69a0426a82650f6d3cacf2c2c94cb0672f9cf721ef43a00fd07f209f38506c1f4e0621c994370ab115e4389415e528479709e226ef1df40a446f973f0938b39d1ed6670f3be5212705ecc2b287b54acb98a60ad370d30939cae44892a3cf26784029bc4e89b1e29b92a6940c076d6a9694dfbd1ca24dede27ed324101c348c1ca261bc03454985acc17599c4f5e7da88ebae2a71a049204cb3bd8c42d6402661c428a1f0d6624c09639b9aa6699a8d871330409bb86bdc03304cb3b111b817390b1a5101e3c8710d23a76a46cf8a56b5f95a47032b8eae7f7f5add3628e69cf5980e12616549ca4d1bd5c0e66c369b513aa56bdba9b5568ed5e7176ddc7e1a2014d45bc11078c8347c2587ce128707d3c80bfad58e0ef49fa67d0fea3ad07fdd7b3c80fe034b2e60f0f1bdb7f2a1d7b4eaf2848f2336a15f791f1ece4bf7f7d2200f3cac84409046c312b603bd9f76d48034baf1e7750ffa1c30c7035d442f6cdb01345260020e6ffa060bbc600710217ba53886022c2b712dc9706432994c86c3712b51586245a68a8c47c8d76b072baca7dfe1322f9d16e395e08b7cbef385f8a10b560c7b8734596b297592cbf526055d602d8bc5816dd1c76d5bc48ae10da5290786f187b2e347cef9e3bd58dddddddddd1a7b5d8963dc9320b650ec77c55207c57a572c953a87a39690da154ba551693492a65269541a953a28d6eb4ab26989c545370fdac443c2748eaac48b0a2f287891058d2456641f70fc72a84f3f1de697c3b6b5ab3492168a5de176668b039bf5846b48eba458012feb3ad0c7f7476c3e90c197e9e3653e9f3a1a9858ffce8ea4cdc20633f3a862c16ed788d1bf09863bd6a5f88ffb78e83eac924fc9673e33b39dfea261fcfb5be7755ee7e7013257ecd8f6bd063e17ae30852f2440ab44f791b9a2ab1e7045dfe19058d9627948c308cb2c27e22a1f1f1f1f1f1ff7711f4a6d4891125aef689675234ddc7332c90bdd3a311cfaded8a0092638874ddff0da840a7ce246e65070a64acfbc9182cc27e8262716ab321addc4b46dfb949873f2e439e79c737a366a74873d86f4a9128bb2a3720c6c62e53fc7bc5eafd78e275c2e53b581654c8561fc64fa77b519b6b05f415ec781c03ae76c1b7c474cc704c3f7d0e50b567ee7c0ea70b95c2e970e4aeb1394520aaba1d2b44d6a018ef1f7c14d1501579ab6efda4198ca6d928d4d0678e81bfe1b49233961abc84710d989059886effb0e1ce3308e03d65ca06fd8d0bef1b0524d2bdda0d6ca951bd6b18e553b44126fc4f51ddf683d0449221c7e2ccb1c33478d7eece29a63099f1d2178bda28462b59b03e9a43ca5e372d2446b088b476584fbf9f9690ee4714c41946a744a3baf683d10685d292edab4c30b870215ae37ca9398be8567e1499c0aeb38a85e83ebaf5d61454fc16029580af6ad48b1e00fc8c30e8aedeed63fb27fec0f140bbaa2ad3d52a0fee43df7d607568914de37cc0391c026e714c7b1384f718caf9048adb8221e4ed95cf4aa6157a86c20e7ac54ba87e826ae607d0304a5e01c70e863150ce33f04ac75450f58a9fe7151e9d98fa760ab15acbbbb57a9558a4ba552a91df8467f123ce5291c351c637323831593b55ab162517660465e4f5a3c158562d91512984a07ba4e225defe2fa92eb9d27a58c59e9ee4ce34b347163b8ac8512ad4b3f20fa5623ddd717a7914bf295af6444f9bada532b5c7f2986bb46a3781595693a762cbc07bee21831d8e453ace46bc54dae285f2bf95ac9d7eab5922ff95ac917378bccd554ada68a065484693628388764b24295a039c6ef064a9ed56baa54413c0832e64260182f5e1edb9cc0e4ab5153c5327649596513922402c748270ee3bc85273df5b56af1fe0b710faa3e05c738e854348c7fa0e85c5c7f0f149b0572c370a0e85b340c2849c334283a0fdab7a002e63275c534dd73a01b218b24207f5fc95713962fa6e1a0742191c079b5d65a3f0f9c71988ee11bfd376a709027c31352c662b5645294c47660515cee84a65229562ae52c16d3905a002f4bbf45f9e4fab33c6c79d87ec475e2933e85c19a34119dd5800684218924830c2c2e1343abc2ca9f48b0288be52c96a649901315a534caf86476840f95580c0683c118c635d2d4fd5fea1fd9339773f68c695cab2995c23006a3c104eb75a100ae07be50d0f75ee841341861c546813c1fdc7befa37bd088abb31aefb9f701faee6342b0f48680e831ee4b9022bce7be08d0772fc4f51c071cc3c2728e7020112f35dd5f804f1df8e5d0af69df0d25d0c097991a5e72a0e2cbe6328515676ca0c252c043972aacc8aff9e9606f835270df2869b2a222b1a8158c4a4d0b6d947c73fb82f6b17b44b1941cb10aa2da49496696b254b2121c8114c0915c9f0234763e722b608f122c7dad6218e92cf447a91ab97188d82ad625aed1d5ae518ee338fae823a5212d1c368e23eb937294b526b19386173616a341c48aeca2410496b5d6da0edc073dfa4ec3a0513b49ac5fb1562c7058859dcee1afd1b7f01d18a094b8cb437f6f314d7771794a8c0a1c240a2bfe0522040926c6518bc562b1586cdb3e2ade88840ee69c7394b2f267a9d65a6b85511a6cdbc61bd76aed68c4b6246bb576349261e871746adbb66ddbf64d60bd84c3a07e7ad997b5f6e572d2398cd008b8b3c72f9670fd6b04b1628ffd55607b50cc61db8090638cb1c9a003db9f433f10728c31a64b5932bd2c2c10d26bb05d1c9345dff0b79e85f5cb333995ed970b4c59970b4c76066b2da53c76e0ece0d14ce03009ad262499210a2bfdc331eebed2beab2e97cbe572b96a0d39c6c56264345d78508494ac9e73cab64cf30dd1a6b053a206334918c169bf7de55eba7b0725a4d324edfad73f728cff04c230cc800d06d75f93c1047bfc306261457e694e58314442b550c21da3fc5ae7f49e98218e391acdf9df7279083db0e28ae17c71e4ee4d50a9ab80cbaf99948a85bd21f4c51c51ea9a0b344dd364386de29ee7f34fa93d3f0b81c9cbe7f423e485cf890bfb86bf0c0654775a9a0d8648c2ad9f6ea00d35e4b421e461406986366861e89a0c67db4036d05ad358638fc5ae7c2c76f43e3e432c7b48a5f4402bf60b5970a5863e10e775dad4b8bad53ac339f7858e8eead553c4e52d77e22bdf9931f7c9e33293089b5a8700d7df554ce357c63b274df64587d1a0c28aae13facb2eaacb50a699b22575244a0a6153cb078d04c380c050b6c9681e92249d837bef76fffcb5a3d572994f6b47abf59ab5c637411554bf1aad1608342b28b45102b9cb16bc6913d464f496d3371c023e30d23afdf07666a7ae93d137ea26b51a7434806fbce66888c6d1c98139559b54677c05418d3a3339335f135e33a3191d978e8e8e8e8e4e8dcc93f2ffebd7afd555841498b0a95500609af6de821558e50425d07859b15dcde3aa6258da4bbade76e530720414a0508da7feee9fbd916c38a6c64350b4a06c5718566cd503c18aed5251c0c3510f8ef177cd5c3eee72b98b028d637bed29f8737e3e33357da8cc586c4ad122d31fa4053327d1a3eaa9e5f9991f80aa27d18bdc1589fa7b5e791d4e830afb329faf769a3e3486478931c72933442d2d2f12b55811c9cef8185e943f35325e87c7a0b049fec8dbf2f2478c1faf63a377f99834b98c4a2ea492e85b5e9425ede98f07913f31d0ad21c00d3d2af46a446143ef695cb062c76231f9c3f2f67fa68c22653226757cfa0649362161a18fa7482c6154521cc6bf3562ad5432406fa9b10473c728188bc5c299b2ce82c5c62ed7eba78a131f1e1915d893e972b19cbd8df64cd65a0150afb02f3bf8a9e7edd7130b4a62e9e778ffe4e9974b1cf5cbc562695f353027477bef2b98537fe33ee783e11f0eccd940ff32419b8fbe8fe7fec5c67b1f9587dcbe971db4dfde4603e76bcf270ada54f0067c9960cd69eeb4abd52c579425ede258bb38d62e8eb58b631c6b17c75c1c3332d359348c7f17302e9a6ce18a6d00ca288cd194d18d6bb2d060cea94d6dd643f6e8a17953aea1693dfc9b2013baa78734795f396f7605fb89c350c07b7c13ecd8f53138c6ff658c9bf3a20f35c6e8e798264c2ae8f9cb53430d38703ccf66cfdc0d4f9a617e369bcdb6ad34da18a8b657cfd66a6d206f03792330ece5d3886f9de252ba09d7bf91f413b197741324987a4818d8900b6040032c532a75c9927a48538d8419d239980e81d5004b804bd3488ee6aa860a27c45aadd3adadd699d010b899678ca5bcb8675afbb57e53fa4cf3b9e8c0b287b7bf1b660d13895aad9da174d5755dd76a750e6460f7ce816a6534912c79691ed8ad195d8189bb89d40b266559135a2a9f222b554b65237beac669df01fe0007543d5a35fba902a98fc044e0d0f6105d9715d184b1a5ea3a15f75dabd59ab1999921f96db53c2fb451c251637313c4689b9b3948376b5ead6fc009c3d22bfe126c0f4bb84c831081dc9e2bcfea8bec73e3a107403058917d4413338f1e48600296916f5d70fdf3e1d90d0d7a3ac56efe36ee15146be07a6afd826cdbb681402216bb12fa40228fa5b3dc4a0d7d54d3346ddbb66d037d9625b4c20256cbad84be0ee46d9bd771356439c161ccb0d878c4939e9f202d6b2923d1ed92d87abb24768eba2476e4a387fe4305c463c58685ba4ad7c934130e5552878763b8ea412a18561c09c089ed7fd1a18ddcee1b49c10a366c94a6aebed8bc67bab9f4a582363f9a1246aca3c1081721d896151b562a51d8f852c31246f6c460ad82e5400a969955b0a947bb8ab83d706db3cd25af381a81110557e708a5a9abe09a15a18511d63ec2465bedd980c49088f580cd601b0c06b37198a60283e1a8117501d3509a864ab57886b08e5829d1d15028ed09b11a56a56106564a6dc60cd804d130c47ad81c48c52e20d4a4848dda3698f7844c715c5754adda0c2ce781225c6f2de8a80247e17ec181a4d62899ca63e9152b2af460b3fe8b1ce143e4ec2ae75447b35c34daf6038b6528160ac51282041344505c2c168bc5623729ce2d9526a289182580c39134351305d0a1936f7eaac05f7d05418d3a23e76c1a680bd8cb07f0e33e5ec563b399599624ab540a435913aad4101c1e33b8420aa8d38f298428a7232f3a740fae33abd5dad1c86b50bba669db65e7256150723d8c22bae8557cbabb3f3d42830e9bf564fb555f355eff9476b7bf49e7eb8ad7742758b00e7b85a26f996911bd7cc5341bd3d02b7a57182d87c2f22e5e1c9fba24f67b1dee45bcfe1436318df73c3b5e8d5c77306c42dff22ca496b724d13749868ccf01fd8c0fbd88b4f220d207e67c2ffa1cd0b71cb111bd8cf7d102ce781f22d0876d7087d0b7fc4de85bc09c2336325ef4392c3f03bc59791198635f460b89e54524fb73b290f83449dd8b3caea8bd0fa3db4998d10d1de83f1c159702865771186fc2add611fd0ae23e6da45ff7670ac3f8bbcecfebe7355ffef297bf5e2f4a619dbb38d15ad358e3e6315f5b2101d1b70438b4461a898063fc7de0186718ff2a5be0c1857dd19e6f09da6df01d2e235fbb3c62d7885d231d34287169db569dd8b68d37d6d1d9b0e083c372c79126eebad23182c5e101019a3044804d38339d23878930298e157c28ce26936dda56ab27f3dcc7758a389594942e9b996bb52ad5ab54b2566bb5eb54ba4a9afce42ad548baaa8e8690a1320abfbeccff022e13c40557ecd4a80512d515bb894de4281ca5e7f3930d1031ab0047b9f3c9ede7516b10ef4917061e848007490bd5e3aeb7bbcc640b516eb77f9352cd063318e18b91e3d8fac6711cc78dd2b46975d3ea07e27134053b63c269391648b6eeebcc04b94edb368eab756686eb487cbbd5753536375cf0388ee338862c47ecfc9186a156ad473dd28bd78139317401060f4e393fbe24a723f47ff40f923875dfbdf47b1ffa482ffd20b05bd50097ca94202b021359ebf3beeb041d232684cc9692d075d09282f3465b5f0c2bc060b7baeb5958b1a90ed977a3608f1d845220429060626cb55aad568be588bd92e79c94523ac1be9225537a75b80f0971ccd1684ed115ec775166bb5361a43f2158ff2aa511b3d9b8da09a2d2318262aac39fd020866d596c8918aa7e384cb1f28a55d37a36c36d7626ae5b212e45d899f67db9569487de1c116c8361d858adf2ab42c7cc35a3b3d96c369b699a3785d6fec31a0fd8a8e1ea2971bfe64282b1251afe5326d8fffbb55afbbf6d4e34959479aa092925ca9c5276777f1886f839e5fc98a1c4ec14fa1f9c173af6a52c0c36f528933e4c334720a314639453aeff94b210cf9c734e1114566c253bdf0e7d0abd46aa170c825ed10442dceebdf73ee47954649f293b1db8a78f4347677f3754903ddc260f13366945c85432954c25234919c704e118bf230f1ddcb1752453c9502e336532994ccaa48c521baa101566e6f97acd58c591a639271039ba48008462a76d28dc0588331ab94cd7754d0242ad10002c7c0d52781d417bdaae57932d3a47659acd077c75bb374fdfa82feff55ce7813ccff32a28b4d1d203d555c0652db450e57a2b9452daff9765e877f7d73c48e2ce27acbf46a2b4524d2b694a24a574d229c7719423cb15e5a85d51b21cb1e28f9e102bf68b254b96a2ab821b720c33b934338a5b02bfe24b77d0093b8ee3467f0325d0af51272f0d3e3703db301211ecf793d2eec8d6db4637ca1a1de0824eeeb4bb9bfea6c3f348d20abbc35d94d00ed4d11be86803450f78a0f73c1f40d77b1d1e690711ebbdc82e1bc01d38a858fade83409482a8b7c47a339ebd1e0741c3191b58fa6209071c331dd75f6d14b62389f5fa50e36a4fa5699e4a330c050c0683c16a361b25263caec1cd5c7b7e9cf8548945a9d5ebea0652a954dae488a86cf7ebadf5946cd2d4e329b1f28acf63b52bfef5ba4f88edeaa655971efbc2bd1097a3a105db2abe53f5e2513529b26a7d31b0948617581f47fa7d74efbd8f9707da8040ae46ecd82caa28d2b19b580f7f504f471949f97e19f1d281deca75001af66262d943ee786c93b4ef6feb1cfcc8a5dfa3722c2d4a6538ac97794a29a5da87615d00b022c452f0653e0b71bbd520cf65b48edb36baa2a0495ff30f87bebda2a4ed7a3f60130e2887f7aec97432dcff0c21081d8676fbab8f53a7a0a26f386b27d55af56a4541d106e0a35960c20bb60409cfc85a511e826046ca04a081384cacd8abd65e003f92c1b2700e467abb4e8ef19c458939e7a4829d1e7ee1c4b6cdd9715b2761484ab05c00f017b06d9536cbb65adb9b42a552ad60fec4c8ab88ab87a544a51a8d46230e65a92459a552188e46fc725273c3329834f95879d0b6f9b0ef3912b7c2bef73cac3c2805969910197299896aa70897993c3185eb29573292a61a2961ae699ad63f05abc4271a66e02a8e718d44a43ead907a95d349f002aa75fa315b4a749195ab52aaed4557a9422f690a7244abbff2dbb63dfd2ca001b9f31bec41fbf99ed7a058128d443fdfba4cd7812f3bd8173d0f2bcf028a56de87e86d079244dc8f6ef7deacd665e66b2ba4976949deaf3c0fa067017dd807fd0ae8c3c7ca7bcf27fe74a0afad9034ed7b04a3db81228fcb813e8c6ec3be9df4388ccb4d6e727b52c3c3fb95d0adedf3335bad34a67195f3381157f5ca55bd72550d35e0c06163a55ab9cb086aa55aa95e2a954ae594da50712e0315b8d979641e7329fef21ead824fac8fcbd09722f4a28c22633b5ab7c33457700ea984964e4bc75b5d41d933f668d1df0183892540d6006d8709f4df7b9236f5f03a4792040ad71fe481a27331db62e65968ee45928a846936ff828b2d3c0b6fe9788b894ec78a2988709cb7bcd571a18d128e9f6f8ac7de0aa594524a295da976f480d087422f0ac0d42d8771d7102e9f28a5945a92e85948f657487d0a919eb8fe4e5c06f837164d84437f569f4224543d8942274bb2608e039c70c13aadbc10276f85c4e30709b342e2615f447a11813f802ffd166c70073f853e04f62861502e43b94c119c63fa4801430ade5728740ea6f95e7419cf2079ac7c917d388ee338707379a8846392f40d241cd3e2987602dff07f3510f8868bdd9a7139c61b089b7634bfa783d7921a74d139ba7796b72f826edf986003692101a2eba8648eb87456b2110100000001131500001808060583e190583c2e9c27d51e14000c61804084643c1a09a4410cc350c820638c610600620000000c0c91a8005bc4b75540a5a7c72b1deac50145f8dc4928dd364c074088a269ee6dfe486a1253dfae4a91eb83683c7be2abe30ad51f973c7c268c732e8ff712c14d2b6159b51439e66fbb7210af987b982a00826bcbf7e28eb27efa429df022e047c4271939921063dd42feddf0f2719645a76fc4a962a073a7af70baa8efb54d55a27d93719c641f8c33029f90d5e58d35719c62d7755351f582d2e269d3d628a6a751ed31a34f8240329c070e9648c5802a045db863d9cf40445b0c278df7d0fdf26edd62eb90caf0a8a0a1d69d84882dfb62ffad8faa54d3307de15dd3a4cade71fc11a4c9fe03c21e02fac399b2997cc8a7581f30db8a441934184df0eba87bfd4c734b0a3500e2cabe6d7e2587073cea4c712c021a900e439abe8a36df876321bd5e1dd189f53fdb99d07b13dd5be66c12e3107a6e1fb13d2e9aaaa80a707b64848797408927c17d4278565c732853861a9b5652083c50acb269abb8d77f5325231858e3bc3604e0cc020993c41205628091b626e07eb718fa43c37cb4e2d484f935d82269a29d0c2698a6213032ec325c24380ceeee5160a8f8307a02a297966fc2f63df573a80452eec54da143bfcdeeaee51ec6f06c1c04c9b27c675ce3ce96b1300961209fab8aae6c6968d96a2ef24ed067633fb5349caf52c366b82f5361003c7d5dc4f209b46aec54ceae2bc361ea71d77f6779559c9b30548c205520b84d0c6c00b953d273ccface5a88ff49f5320dc4cf82f7e966b2d97d82ea59e90de3eb2284f69e12fd4aa8b8f3b63a290a242f139a2bb71e7fdaa2dde223820750fbce770f88ce7ffa4963bbdccd1923811cb1a3310872841489428b57cd37e8bca7095840ec583dc08bcfe2a9259390a06bf59c1972f6db1ef75c123888d24db519f82c9223ec9311750ca9039825b68a01cd4888bd54a844d70eb0c32e7366dc4dc32fa794fe4ffcb78259a2e16d2c1397a38ba90c9bf56a2f4060c24048bfb2304d1612f8b233594f77fc494f62a29c14623721828a1e4b5264090272e3efb2145450631bba11192cc5da390cab66c8041590d50a9bb19390378271977000321aa13ec11b9c6b5ef849f56990610c2891094cc57ed207da0b58ff2630cd3030a2343b72fbaf7863d5c02ef0fcce269c574fb398d5f37922212a71037e680942eaeabafaca5f8d22e00672ccf690b3efb177ede1c8e0241ea5f6cce84181921802f9e88ac7db43133771e6ffe87c5078b9880fab08ef7f586d6c305f763c9753bdc537bc200d22a1b3552a06463d1c1ecbc7b1418fef7470020eca9b44e47dd34f56179f4d3979cfc99ae87c3d50a586b4e45b9f38162737f84268c7f031aeef6deac37571ab87ffec269b6622c4b8ebe1bd9bcca9ecfc587f01d74ae4205fa715b832ff7a18ccd52caa79ffe0bf8ba31fa94da79c386099d32273c4b492d095d0dfad08c5dda8ec265996925e12f3dc56165fde30179e24c75ebb34ad4d869ea5e7d04d122e6278686dd34d01192f098e5f11178f25da24ebc534943b0c54da911116a0225c41a7bc2b1ddc80969369c512d042a0983f92fc084a8ba29eeb386d4fb08afa396330a5009b14ab80775aea993744276dcfe0824b6c90a3c105ed2385304f95525c3c7316f5e508b3e4d48265a737cc2b2427e2d809572cf6190871215ddb9b526e68599f27ca8c7e9acf43f30f9180aee798c64a33295cd17b24a643b62ea6ae59d78f71369b67de7a77c0fe88eeba008439a19e32ef7b88b2095c2187ed8a5bba01de81222a89bf6167a78184d1e358988615b41bf88485c56ab78871c2f25a76443eb9efef0bcbaf66041011f07b8b71797d5d82c0d326c4edb90a633f4dae21cfe522bce3b749ae2a19ea31fe08ea8cce9ca9d4c17157ee00b22931e61f68d277b4ef9a623f4cd7ec43c2958fa53a43b50fd4a3fc47bfe7a05a39e5be281b6f58110248ac2606f7c1e7848cb2b5a0e3ed9875d048c9e8e69af173ea71ff6910b7d4dcf9fadbf1661c5db3115cb9cb1aed85079706255abdc40c69d0d2810a2a1ab821f4dc5d2a141f3c4a74ffb7dc1fa3337a8e53db2777235f1bd902d7165f482f30ef08313bc4d8e827f8abf1df92fb57482ad4f85ccb67ff6254990853988f8e34c1ff7c7c2cc1a8553ceb6f714dced694df8c12b789fc51c671a3d56e358cc4ed05c3c08be80b4322d7fbb964c450acd41f24390420649b61876ca9e84b4dae0b44ec88662bcbd6627f1f9cb43fb4406e7a4231e6686635002426c5c5cbdc10157e2e0d03896c2b2a85d91c7ea3a2388270c6de417c82f0296b6ee7ad0a33c5175fe4bcf5c0488d85c42924c9f66caaa4b7cb5c3c486ce6c1e4f736f27c42c10810ea7e52b86ca09826317c1f4e791ffa7f970d753bde5b1d8890e2110aae3e960ae47bb62b05c7dce68304d4478e444d0b0021a16dc5c09e560d108b55f9a83fe4ca4263c25bfd1ba7a8cac88314099149e8b8a3302525ad21e6e171c4857023c0d22425e956613687fb84caf0e1085f04a5dcebd05aa14149bc9000f40ae7d0a979eac196b3ea77cc1e900472d196d9dc10322a43a4b63824b1a73cfcef9544ad41a9f541500cdd601ccdcb55239616fe2cf20805af10064c9a2c103eab785fb5afcaa0288287402fb8881a11ce68d4acd689fc170f0dd4df4605deb214bf82b78db0c5dab8a95077a0c43e074b61820de2d5b9a3d319331124705fb27d4dcb192e0403cc241667f512595d84bd36f1fffa60f0455f9ac022e3402552c38da214794ab8f1f925952569403a2769c7dcc4290de6d76a71702b2d5b8c26662793abadf79337f21efd7293cb1d57f27c43e1643278f7014493a6eadebec9eadca9c8c8337e69fa88b81601253b10fad222b24f717d93155181b51cca11ba3a945fb686b5e81fd95feb5aae1d18036c9d58c781f997df3afd81d4c32b8fd02498cbaaf172d0ee81b76bd4d4e8d6f2edfdabf747de81b5f0a0ee42841b7f50dd2e6ca5f682e69211372f3bd5ed3101ad0c3415cf89f4fa0b43f7049528721df18c30e6bee31e41b7dbd12ab574ed3128f7bf85a1e0603cc1611690fa946e93cd34c0464626e8f1875a506b2264317cb3a1d54fbadbd5b0b2117bb166dacf872c6f910da40765a58474ec680cd3a095990db68c06cce2be4490b31c7c2e5c63a1a61a95f9a026ffed3669fd2ff1cd5b14cd2c97b97c05d12208576bbb3aaab596342dd5653ba3892f73943305d99ed76676ab49765defd56c6444a20d36059d73a3467917a7462baaf8c7f5864bcc84a436553d58061691eb013920b0ff338f4af469f67cd8fc77dc77b15a3cc7abf038185113104ba82059b8b47076ee02b7c3a8a02af80e0917107c765d0a9a602cb280d28b8a1f1e37e678b1d6d1dffd1347b2433322eb500ac5c669a3f595cefd6e21c7fb2cec274ae8e60854636543912d940011857b03cb3c9e40071656d41a7ffa2b2c27ba5e45e73e406acb1722fd0611a45d56117ab5793c0b41d7b8e0cf58a768ba160db3ae9670ad342aac065a4e48b12a8648c3b2b0c52498e3aeff2091cb30f630f9f360f533113f92ddc8d77f4c7184a3950b9960661865dc046af7fd502e451a2c1ef5d02ee69f95a2810789004dd525e386936d41afd48aefc038aad78adb8fd03b3b28580a296c543deb10f79af3a1cb06670df1cd083434d9e1baaa7105aa1b1d3c737064b21ba962fbdea89fbb4af8d4d1ca8045700b6411cda7b616d1b7a5269eea20001de27a1d306c87eeef553e34df3cd071d078ea81ef12f9b34ada1905a9bef9e589e60554abb265bb1315d20c238760207929536f6e2ba2769cba4c786d75f9f3475e1b14ea60bb0ff131d7599c205b8dbfd92acb7325ccc4b069d0563f30ab50c54812c66f0a7b0eb7555c3105677d10c2d6fa795b06c9a2b6e73c23be86dbb3ba01fa702162eaeb905cba71a18041a138899b7efff68b82245aa071c00a3f2af2cf5a4f01fafb2a3b6ae0202394cf177e37d1c739a6998d2d3ec9e60706782ff3ff22d4de1f2133e0b22c6c04eb6085217a07002426f234f7207ca3bb5afcb0cfd66c61c8770aa7ccc0608c53c43e753cb300cbe0d3a1c2319b96fb9c510afea4040e9285c320bab877d9d0af1580c3498e6a16fb58566dbe542cd0fa7d945d8194518520194d2f26278ba18b76b31a4a74088c9c3c35060c85d9007a4707b6ee0bed053c580d2efcf5911213e2300b0b510058f2d040c9ddc2ce13f8e26063b40f862245ad3920a320fa5012ae625435b878fe65e367265e5e35bee89b3f612ba3fa1465a463c141c99e6da0250c96a9c2ff6ea8871bcdb4ae49448a0f033d9ed696bf24981cf9efe568edfda34912c0e3ffa3df8fffda3b699910c4d5c183d98d05bd8129e1f3d1e0fe7fc0d5c813375dcd1b51e0f4f8ef2ce5234a35051cc4c0c93ee922e7645c121ac28e2c7728129633860f383211aeb1c8311f3b9633dc0e506ec87ab243dc6acc4f05ed9f2da9423f54791deb92ee12e698f3857b4b0f7afb5e33307655cee87cf1c5757db9c25cce8c40aa0a45331790844606dc4d40a696465c4bf79c7d10dcab3f7b8425cf62826480cf5e108686b6f85d1f626e12a8107026d1d48d56b2407b22b34d4807efe83e83782bc1468dd24ca44f1aaa2856140929b19a24885d06161de4802df2f01c2f932fe726068cbf3dad0baf78b193bd59c0923acc2590b53ae4f915fc0a22d3657d085139dd3d52c45feb687c9a03392cf530bf92095889bc942763396398baf7a754b0e2539a16db4cc63adcf983900d2f5cf077192ea85ce7c3f94b202f32648055b102a69b500501ba4cf025ba411714cfe7fda756d812543b31f7920efd20968bc0911faa7c23a24ad8ee628eeee220337093e246d31362a87d746bdadba15ea2625f769d93753775056fa3cbdd5bc36b1d4198e8984b67a31d8727720d3f1351916ec303c58efe0a0e0bf235296b3c8fda15d69f8270263d26de3ce55556cb61a3c2829c4241695c6874c0e6700334c567df1a25fd379579364e3d563ce9375ea227f13f027e6332152580f80a6dcdce6bc34e17331ad311e9d76f43a1dd64e1bf8402b6066e5f9bc2ff61cc4e27e1469e01f9c3cba70112ee9a4fbaf73a47445e9913963d3c6846c16d3307af07dfb56d9d416324d3e933bc4644b67a61d4a1e4e9dc3d66a002207672bc3b66b8cdcb0e09af3d3b97ca754019f934b61541cbe73d7c109f3f9535cb97a5f75d5c086fc2b4ea0cbcea4bd2284b609d4a5718201e069000ddcec080ca97f8f7fee1dbd005cc8527250d8a4bc1179ac1d74aac425093c162aff0f22605059b7994783f3232f4dd96c5bb1fb3fd522d2da87bc2a28d94e5ae2ddb2a6c6004f2a51dd7c0c3e79640de48147ef7a0f70c3272916d86e3939c8d83e88328bd11127d56b487089a977093b4e2b676cad27f44dc3f4d28139a4d6d81f5a91342974a1ae1b8634396d64c634708e28e48d39df050ec23d228cdb2d45ba753eae3f81b3282838d9c1ebea26a5d07deaf56f2fdef6d014b4d7621d7c2797c1532378df8d1815ef922536e52b1305979a9a64bd3544a486f45c39afe07a8d4201407dc16d2407cc31b247babe960952c56986ee75c5c1ba142facd46615372e9e88a360f0165fbe01e8ea572300c86cbf3e8fd1499d999163cd8f620e21433a520b80fd80e1557214b7a644f09f897091a362adaba63c5071896bebd27e1c9268f425239540699e6807a2c23136c082aafc4c29502628f4bfdf396e852a62522538c0780d812d3fcff8991528c5ecc10800607df981879205463d8e9e2bcd776c3db3ea0bbfae2b431f324fa3d1636b51e554f219273ece699f8f8088bd9757390e4dcf481fd97414486aecc9ced42c6db6c2d68303366e8d51194e7c443f5a8d62783c4b7b21db259b9b52b777db591cca8e2bab424ef7abea063244517d78ac424b1466e8cabacdae2d665388a6f811e69abd342e1e9c55516ffb7cc5924670db1c26c9932a7316a88a4b515fd8e2ffbc378d2465811f892c80d743e5c6206b1c0c5b6cc23f0bc020d695d64582781fec69c308f352e89d3fc132cf19f0951766d7875d656172dfded480a15532c3c5c741836c39f99ab258fc55774a35042a3dc04b5c1d89fda1f0da289f1296ed453795248dd9542bb27281048bcd6aa8f192f41bb39c9ed3fcfcb65a3c28b4f09ea79940efb07de19203573248c986c7746c95a1574a9eec60078c0f6dbe5a4b7611390671d38d5f3181facbe02a09e7a3f2bf1c276aa60be6d1fba3124c272ca8c67701db7168dfe3f3f5f7faca407b43cfdca7d442b390eb595404e51726c8733da3fe70154f7a671bfa23d03c286b8aba37efb31977c4d3a4d6405463e2b504a896cfd76ba0f754f3e801bfe5a18633422fd50ba93c24302eda7f820584210c54ee037d37db88b24cc733511d1711e5530b02101f3fe70cc297d1f2977103e3cc33f7f50c7b830bfd2e485d5645ef17fa1e98d5dd430a7c3fcab0f546238c6c6003c2c48af161ceab17973e96f58e256782d8deee55172021c039455d71af78f6f4a9b93e73d8e665b94e993710bb710854c20aebdd021e6964d1909a875df30fc3755fa9b61e29443157998c09c1fa8546cf1202d8c01520d3e9a63a1c538de76203d87ad2f673fe4d0ad34d90d04fe7d4ccb14759e339d1e886cbe9c59c05f73c478dc5d6d05d822fd26e9b137b42a97756507811d77b89daebf9b18e7c6bcf0e0f08e1c4152bbe7860af7515db364fc86436f315ec87215318ad64aa9751d0ed9286bb5a9f1ba25b42cfdc8d62a58655d82f200e27269f6a43c8eed9a7483e24ab1c62b45ca509e9cb0b0f84819cd8c7556ed2b8ec83faaac3949d56c3a2a197c9f53970f2c6c37af0272c8839b2017316b4d38ace2151e9f3f47893dee1c6d61c31d24449c1bbe9a5d667f0f70ac9159b9171bf2e221524b470c24ea40bc16341e0fc172b6d3172488ff911e9b5875a0b2626029b8419d76aca1031f60e323054b5eb0e0c350642731f88030ad1faf64a6a4076f1f40e9ced772881da70437f16168fa5c3fe4c36cb3245a04e56b0d195c6b8760308052cf8931f62fe34ff28210bb876abad9210731bcb1e40f6e126a82ef504461f14788de7870662695b53b2b0f75b58e7c20f6ffdc1fe66e78fcbf6390f5c76d10b89b7fff00d43cc1e0ca9167c82522cb7a5e1c3b0b052235d5e96fc6c87f58310fd0873bd2e551b8a5e198cb2a27600f5057b94a474fda69a6dd8f10837b6ba578f561e94f135aa25d0b508a58bf7afd30cfdbdfa5544192f6c7adc0055db1bbc95499e227fd40b1ce63c443fa438ea2b72d7ba454c6f6120de5f48a79049f4a4426c5253981a4e0d382a4a965d3d0fb769ef5a5d6d3d6bdd28577a3499ae518c3f3dcfafb4ae3b2011dfda41d042bdc10b643f1b231d11659e6d8cc8ac395bcd948932e81da32772c416ad932f0db8c4ee1aec87cb988c880a31167c56777e7851648a451d7690089a01ae253995c2511792f4adf9ed67b40e60bc179c7b8f2bd51afc0c80f7631628cd6a126fa6c5d1c66d568db8d32f7f6e39da066f5c62b9c49389d1c9ce6a70c6a41293dafeb7d726cf24bf0d9240b0231d628690802d50238fb5d01a1958150c601a9821fcce1c9aa2593fc3f5ec53ec007c1cf1c7bf2fee13bf71a34e04a4ded34dee000cdcc9f7b51166e4a9a640c97ebd2d4b287525fc226a5e1959851825639227f7948b6e1cf3cdbea9398373f86c8cae16429426c462331cbbef3a338a57375479af28b6078e1aea62909b4dc30fc2bbc50843dec6f7f28942283bce03c0ea1e7992ee34982e713d0c6b11ffd5e5ca23296f27e97ad7714c8fa414dd3bae9c205fab2d077e6349da1e40f620ac725f145565b231df862fc9405c58b2d507a2b699935287b860ec7da0150b3bb93092925c232729ccc1251a0737b6a272b82a21a65cdee1f15974490e8eb826a6a9b57f0940895824659a0fd44fe73be6101b7ca86aa235dd3984a65b72cdb6d801499f1f4e14fbeb22827f4eaaa5327e2af6d472faf5ebd9c9d672d221c251d89fe619951cded6cec9788680e5ea9b617f5e667670adc80ebc230f4bb9b1aa1e26a73fa787c3699b244dffc88053cae09c51622d0954d96ca725a7f1f196d7e7c5af4da93afb134c6336dc4d471fe287701d580986d5e2f9e7ab6413380dba9aaa6b469e4f86bc82affec94353ef1344244873851ef8ff96aa537a6cda05c3f9dc0faadd387c0dfe1c07fc775a0f32dde5c4bdbec0c6e454a172de29f596ca138aa27f77549b24a193707289be392fb54524394f158abfccbf0920ae379963689ef1a3819932ece9c585de74d697bf0b5757cce238c27f5aa7c015500fc27b751339eac4655158f8029a87ee2dd524fb7cf48371d437d7507a664e835ea064c0183bf90ed0d504f11952aeb149d4405f6bb08594b491c4b891adc52251c800a6631b2f458bd3e2fffb06ad5b7ab5739deeaf65f9ca59d8fe8a1d5ce5c62a378036d148ccea23351231951cbb37e52f153997274782f33a1bff0120698706e508348484cd6ade31e378ee644040cf4b1e94a062cd992696a782938ac494395e597356e521cdee2aefe7b2915e2168411d38dd3ac38c72f4aec8651b5faa50c5d0ea5417b75fc5a042a38c5b3306c7ca817f50fadf12602d0cc1b904e747fe73ee15ca4f31d0ab20911ee0fbd66c972844050aadb1ed586fb23b6beeaeb80353f31dbe00c9f654189568ab15beb542b692d4c60d17a8c01b1a4df9440e41abcd118d3c27b627b78192eb976effc09f3f911fd4d55c4e0296fdd250d4dde80ded3991d61704414efc5de9a403852b6c4c42c6f015c8e75a6fdf09bc45070e70d189d42eda5b20aa431a3dd036e894e60523c20091c0696aa367fde0fe6e8011381c49b84a0a050c3c200f997b00248f1ee8dbb7c26d7c0a0928128f3aa103d40d548bb5bddd4b873a1a799e942d4e4d4fe9080804574149f75ec757915984aa798b75522da221fff0e37269123ced35d12b78ca6782db74e630458df7f4942fa7b910ebacb38ab4f45b572cb43322d9305d409c737b6929d0cd6fc84469134c3f432dc1f420e092a2b1e613a948f502a5165f781d7cb447d97c82a9ef0a94e57d827e051ead65655265a167b8eb4ba198fa2372913af11f12b88db454dee48893c4be33b40347ad01a57e2b2345dd483e1871a34e6c57d36af4b4dd15651891bfb71450902334c1fcf33e97c749e07cde87cd8adfe233f02b5ca7cf9746660fce1b5c0f09653979fec34b3bdbb86e56df7663cceb82c65b75bc31233df99d32ab520f4ac9154a587ea42f7fadc824479061fc095476e384c73722962c88c394968af26b1189abf3724bc4bfa296f4245741f68ecb71ec5325171fcaf44cb1fff56ba063234ad465741a2a11e6e5ea1818898bdb7d37517f6f16da61623641a4e26bd1d5588ab6a9c6129d5ca3a4259b943d295427dfa0b9baf551f6a42618c906118511675b49378daedf12abd77de1975a091e6a8ce98a170a3205b749710175d879859b457ec988bd89f2100eed0a9c02e95a0ce7bea68d5a18dc223b08d0fcbdc4eea3438e9cb310f72464992be3f5fd45ec6b3cbcc3a282cb0455f91d6e350169ea9045aae6e0b8c5dc190511845507f105caecd0092e6430c512dcdad69d622a6af8373511dc027a65af7e8f3d3f52f6ed97416f8717938837aad82dc000b797f739bdbfefc1e0c740a49b2e53df5339af86aba1bf116d34094ee24db37a0cb16eb06f6edbd451c7a5122d1688702a56c18352d5ea017838d9e8df9f33d8e51e9c80e7cd84253991de7f31b5990e44c210199b5066ec89b61d12af912333a253f76615b7eb899b29c8df7b591740f7c9162b20038a6342796fe99a66ec397519e602f352dad809548bb3a70403101c069c3cce76b40a202af446ef9145de16e0d33c646e9e36258f9a4ffdde85c3155603e59c6a07296a3b93e0003e552f96a6734f6e42742cf55cc1d425ca2de539ec2c4c9c7e123a77e40791534e0697e3d4482716dbb5963a051ee7e6e381d443ef62e132092387350e3cff79c22e028cd4dbd70b150cceaa2cf7449caa20c679576d15135eb22bed8a36f893c6c1848d96b89fada8850c29640b639fe6368975bfcf863c5d27b59422b181ab0ac18e04c57a1bdb2a98a9653d16f6e651853137c7a1b546088cf06530e1f0e17c0fa75bcb8f2f4ab6c29fc3dd81e5dea0a4492573e6c0a29f8c090ee7cb10eee41c28c7f79dfac954d95b8e7ba9302d7fa57f2698b0a6c496c77b483767e423778200c2fbb5615ea53b751e20daaf9964bd37bc787f61374cc20dd36482840401de24097d9610e2cebc2273eb99c866134a25ad804b24152d751a8596914c2c01c5dd76bd388121d2171205614445f8166625ff79ed11b425bb4f50de399f3fbc3011c78da96439c0a85db5ead0dbc568fad5763c127c3be6b2a8c2f0ca463acb8969fe8480f289acac064b8ce5c024abe6abe476391f258a051486d35ca14ed4988a94ad0285dfb60772d62a2611a811fd30002cc86a296752d1dbb7bb4c6a88cdd9a5c90e99d02d6cbfc97c64dbcba749028e38fa7fc33030f7f6e21ae7e680600cf4b2a765fcc4b740ab6229776e1561eaf196adb281bedc3f8d10efd1df336468af4f04438d94987b88993cbdf7f143143dc6636a7efdcc838e65213a1dd11adaaa4fe2810ea161a1d2cc154c3c5c28d8898a7b8b70344edcb82ba762bc9c8b35cde31d785c59a2122860b117b3078339018d3c176b6f07c8a5b2b41fbda86c63a48f74cafc3d0d99819debd46a724e17656abfb21d20355cd0572a5f85612ec6f2b58486c5ba80811a90b9c69ccc90cd754844a53e49a3f507ccd51bd2d8952de78ca474df3543913422312024012c670e880450cb97a18033a464d5356d57d2f51f2bfcda01e5368d8db7c89916c773d4341f75c51a447d420089b5e6d23bd5eff3f439e7471418c0d8b68deedaded2d4ba2dda0d0c995856196d7766ac0a1473f5cfd9ef411ae7ddffe3fb2e76633c5d17d9eef36a420a236e6ab7159dc573b5156b91d4ac6687978703fe02861ab9b808f7aac5c5bbdb137e334e4a046f370c4ff51088ace0bac5c232a250bdd29ac26e8d7db9fa65f93a12203996726986cd1c0a1458aa42b16e43ac1321a4a31a3964e5e0486aace2bdf936f2bda43402b080b083eb1200b0894a64820c37b312138815e59abd319d121456276f22973290f406c61c489fa920d75dc084cfd5c1aaf7660f238f0329f8132369431b5e48eb67c612a3e336cec069afd52994048045ac69c7065c410d6278171e99e096a04e2bd23213532b970656c5a06b75906b73b52f51c277d5d63e70c7a2e8ecaf58c89ddaeaad68e40855ddf76e04d3f1edbc88e4093d84b3cd0fe67e88b983fe444bbcd88ccef64c2930e1b7aaa5be2cc90122816d0cbf48f0fae630ff71d621fda69d62042e006ad96a3bbd0588a19e65d1c8712ac8b587909209bbf5f009fbcd2e0935628a4e999635aa1deac96b4abf7272905cca58102d8aa9a8f80943d735ac52faf6b816aa35e868306b9d1fa2b56249fd644a23f49c73f10cd6f089bffaa859ebd3a1694b8b21cb5b0ae9f9adc17c23db222d00e04afeb2b980db309fa90700bea810a590d23f809b03eff7d30962bda5fc942057595f79e2879df7eba0df55ca77453e84a7115b1dc9427e9c6dcf20c99dec66e6cc34962dfb81a60498a40b84aa7b3cc04b973cd63b323ed1a16121d826c572db5bf4e7cd8a8dc5748741eda69685280d8c3c416cc87a983181a29a292b201e32f40ac3aa6d035c82543e2955ba01d0f1cd355b3035a18b72bcaa9a3ef6544de4a3729542ab11aa9590ff1d2f1895159cd3d46c008c608b0c940abc6f6492ae342c7e8c19072e896e93516d749b646de09cc355385753472a71310e4e5627d4bcd9455855c3ce353cc0efeefc5f318af3f731d0d26f7dfa9f308f0fa942c003c2fa7217dde59b746926d44e33031d1ecff9574d0b9e7f2e13537b0d167e903fd5dbe050e1da40618e7382a709666bff057b39b5373a96c1d6aff5a10546234cf6eb08289cbe8606cf75eb41eeca09184bbbbe27c334ee08f0401505812346546cb03b08fb6affb5378624228be5211f1904987d4c6376b8191e58634fe8294ae97ac9091dbc67018bbe695492668c02dfc01819d98a25e3b26be8c11891250211e19d80764f69c64d241c49a7f5b5700efb0e0218dbeae2ab0d41c6959ec1f7eddafb5c19221e0e57944a892b6580b8de6eda32c49fae0e944e8d7adf8bd841afa058aa9c69b1a44988ca0aa1f2e2a4804334c581c96274917e99f3098ec0cc8ec29ab4709aac9ea68a7e9a5cace3f1725010ff310979cb3cbfd06e3de92119c9f3831fae082e3879909c50c02c71dfb2e83233e1b6f43c0cadb38f62cee202963846ceaf67fb202cb1e9674b19abb2ee12a57e757bd8707f150350b4a52988de0ff06ecbd83561cac68454511ee69cad9b752aaa851cd63787db35e6ea54122436a4c3564c2058640dafdb67a0202c522dc68d26233e1f0702e8150cf955c7fc1557cc1e90fe3bc52a5750d85ca24f9f83a0576994a75a300aec370eb29877b107c8e656df2e7b990534b0c1292b3c4f5037f08d9a7e265fabb8c0296534b59b1b364f8e6203173d67ac9805d1011661750192f3907cd633db59a5673125d4f7c323590187c9b0e0f043a307e83fb9b22722334912263c16177a9bb3dcdedfed2bcdf53c5d4c32c8522d75f76d74208fac5f80259d1300bb2730c86b93a029db926ea0f0ce83f45347923ba25ff2727ae66971c9c2941ad5dc4025428db6afc1e7b72d7befcc6db16976584a2c66ae1ad676f6245334bf4712074155800dc048cd340c0dfdce0585128d1d53e0fae1d68229c6d70542413b1f4e5a4b9815be942c537ebf901d98158dc970a00ef63e77de541172f7da53678bda02c0aecf2c121a40bb9a91bde2e84442177e397264971689c0240adbdeb4e83aa18eb0b9cf6b3986c8a39865e6949231710c6e720cd05a5d7dcba28e2ed5f934069ee0b70bbdde8c7fce7d2ea26825bd40b5b3a04d3b62c8708898637e613a22e4e351377c95e0851ea25d85b9276850852e80b2c69ace512cd2463493a0eb80b91f2532cbfd3f910c1b2357a468dba2e7dac6414ce295f36b5bce993ea757d450f78950c1040ae921e1838a881fa99ddb27b8de1647364cb323cc77febac0c4d9d400313027c21d205b02820e85a0942e9e4047d65b9597dc26e14b95e3ea1586740e113e218772e28c21cb8042bb68fad17caa1536f8c7f54d816a02ece6e7b0bd09d1c487f32e55d7c200e0d036b473f45ace576a1de4007b639f3fe2d7e8d09e63e6c04a0bb682cb80ca0918b71ff16ae4e94bd4b2aa600975ba444477c55798d321a177506d5861c9617458a73d153b67857c541f84aafe698cced7e09c6600205f03ee14646461c63527d70843c690844eb7178b591122f849cf1a5fec5bc0f07275d5d102a440e5912f2f75b8ac5c11e65328970e9fd410e55cbf4ee0850daa696581b943869be35e431a472951d7c145b5d6df832c61e5f1fb95428764f2f1f1b13582570f19f7f16b1c275a33a04b370ff5e202e7755483fa0db6bc41b142a431a7ce87cd09d41154022c7e36581e3cdcfb81cc9c20aa2f6a6e362c766f4aead5719f2c3fa95ef972bd1fe7a500acce6c360455cc1535692a8d34f030aabd67b52ce855d67e3457b2089e8d12f3468c05403f414f580b41a0e302c1b8894a695b939564858c5c7b7b7425e1f8eab981acd58be6d2094cc98668ceba21168e6b6bc94ff870de594f041490ac3cc09e5fc80c818c95563361e5fc42656ec689cf182e15e4ce3fd8dee70c89afde7c072f5dce544652145e04c2c9f1e31c02299390224b68c4b44c3a1bb5342cb491900d0983b70594c132176b9dcaa69271799715a01ccfd72b9e058d9c991578fa81aa50f541a55db386a327fa6af400548ce9c2fbf495b9ba520b5df6b4fcb5369862a0d4f43464379c3fad7a6802f75782c5bcc9863c5354536f0a4c090951511415cb22d7d20e8152576efd513260582c2a1d8754d61515fd25b0d4e9ed72380b9e2683e62a3a92c5efe570b8995fcc9d620c46f2c1578f3c7bb5ca4805b3fb7984e87ebd06062b5b5a68fa3ab60a0dae27c1b5851e337d0f9e01a1f2d2eee0d74e092ab282bd7ebfe2bdbf0d419a8e61c615fb8a819ec759d4c0d114f17306584c9ef46b16f7761a1f0ad8973714897e3ff8632595af32aa4839d6d7901e103a5f1ea6a5153602d2d416128e927fd978eec93a40d438e5bf6bfd535dd8a13673f5b880a9f5ca5734526a4118b2b53fe96f8779ee452b36d3b98b08cddc18e2ff0fa1366003ea20ad894b4bfc2d6d3c73f7d4ecf32850085c4b6a0d97e17ec7e2fbe12269bb3de3d8c8d0d135de8e9f97a5ba007f91399e1b30457beb9e1ed01d2c9dd9903ca645f022d28b1c29c4c80bf65b404c16531f3ef30cf5297c8cc81e1907bbf29c463fbd4b997fce4a5c21aea9c78e732ec67c3f551734bcd1388ddf7a5a7bb1c10878359805e605fb4fa3bb28aac12ebf267927fa7e776656d325f74ba29da5c2bf3e97413234efabcdb51d309c362bae4cb685612ba04ee50e7e9fac94df9445490117765325d9d9cc2536f9576f79e0b607495e3e5a75285a0adee504b1a78e65edca0862f2852d1fafa4888d098df513802120c26800c93d2d729a0722ac8b1d43bbdc9c61b8b0d61551c6b3ba6d9c76844f1da1399d459cad53f660a5b08f37bd7b5c60f60a85597d7f9bca4a7928ffe83e1faed5b40776eb974403f42477c91c664ab12a5b8625cd0605719f1cc82b9d18a2f100b279901ab9f66eca211f23d9c51155bb0d67bf434fb9a5e52bdb13c148a4b4f5068124bbb1fe18d3efb69612cc1b021d7e7859da69f8af25084406f289bee11e19bce8fcda2beaa94e02b552dc890cbd8068f04d6d759d916c1048bdef77a5fc3f3c1df687a8592b0246f0b476c820d7a01546fe5a2f0fe1d450eb551a492b65eba8840174351856bc4b4b1af8062263f8685cb81ecfcf3adc661ca2f34dc6089d3d173092c634266b9df1a8650e8912545984129363f4db3caebdc130a758209dc38777ad5e759d4039ac2e68a3149b1fedf082deb1b436284a6c557a6ea1b0a3ac7a10206e4d477efeccbfc3004ae888dc3eaff9ee62b8248c68e66b66701c0da40151b2c41fb02b3a19dd0c40b30199bb13db63e566fd6af3e3812f469ed23341e3c0972cc6e1ac0868ce0043cee79f89922b14d4b9d40a7b4c5ce184f5850d4a91de0e6b0497598062604245d60b27a37a27eb37faf6e801a04e89b12b080f74925705cc9107f29085bae3a8c5d368ac4e1a76bffd6169eb1f314100acfc4923303f72a1425e9643ffba82c3da63b90ed822eb405711f21ae1915f8816051ea837bd335bc10c6a3aee24e0fa4c1323cbfee3062e1b00516f60e50ea2758f725fef3a27ad16fbf95d7958cdc20b5e81a44aec8935f4d17116ab8f59a24c74a55746075848b991017651d3d1a6b9e6185e591c0bc206856988f2f522d132cf7428c2b45b678aaa38ca5d2e99a80303b95381b8d51450ee7d40c4f18cf6428a07aebaf22533ff86f8c43c192df3e659320c71826e4060e945c449d008113db777a856e9c89050adc8a96765a74fb13ff8853936d1d60d98d764d2d07c0dab6f795719c8283d048424295effe0711e12214489a970351b55644541beffaefc7da8c1a86fdde7deb735ca5fe3123ee03034be9bccd4ff3df460a9e8f31be0dfb49116a5c3dde10d9c955479994d3fbbdf7051aa94da6ab5c7391dcfd2903cdb350e40a2bde5710bf582eeef32128e3dbedd2b20204bccfdf0b57754169b31d72825dcfe0493f17206e6b06103c3091bcf693e0868183eb34f80f00763e06f04e2e60abf92f5f8103e288ab431266ec594e0d80c68dba450c6ecbdf7a0a84d4dcbb9bc5b21866364e0cbb6426993cfe5dc03a5e61a6d62805d8c2d8cc44f04b4e07fe2b97f0f608bf43d47b1678e0ba9f5095e5340d492a795a0d9de9c398a79a9caf105763ad6f0c56d492989fa088729fc1d62b2a31070f3a3e7c7926761884194ef95076370ec4ed8bb2abe0fffd4d1e8fdc7bbff8a912e552d42e44e91bc82fdf8878098f5eaf1bdd0d8e3f4684e13a778d0c5fc33ed7f048ca2f9a82e9460767949f6c0ee7fef146590ecc664b28e28c21e309a0b004974d44447fd1ff477ed9403e08f27864e4d354aafe3bd8152a89610276fb71342a507220493e94da9266d2e0ee186e0a492695111dd17793594b90709daecf592826fa480300293785fab87ad630c430163dd2871eeb1ca218d6d84d52d58e8ddd4a6d510444b90db32b562d4a5e76a84c281630cd80a3c41468757100ddde4d28bbcf1282038984171203c268c697741218ba0bcabdcd5f82fd377a1eac1e09f9524ff0d72a8d43e5265767cb79ee7b25946abde2ce090bd38f907ab823cbb3ae3255e9c1bd27c8f4173cfcd6ce1136e908828d1199cd6553dcbaa45cdbbddfae2ae13270a5e15b11dcfd28c9f040b76e1d189879138b807a116b00abbd1850f56db94a9b5cdfa1c294ac24811992e510c463a31d8dba010494d8b77700b186677e94fae402bb9f69ce396320c1412f65641314b73280580872468d9671b0c546ddb8b585ff76210e0e028a98132a098d1127b4bfd7b3488a129bf3111144fe1ad63e085f9e63c6b6022b35a557a34cbe8f8d82c58d6f015ad29d46d4eb607fcb4bc102e3cc726d153d3c0657d4171dfc39f3ad31962629769fb432aa010e43a5c30b4e71c0d8ba124355c5b2a111280037569a63eac424322c432758f7a007f0962720ecf5915ccfe1c01eaeaa75969ec2fe588e808da49ce8135f7d4c51d58ccf37a9b8538576afe6218bc020731ffbbbfd76160d546f570d58bb2a9131090dd2c31967d66a471c1ff64f939e38dc7b4df8150bf4be18b58721ad96a177f577c3b5eae7a398e30dc8297b476aca74ca8234b076a5afdf339381200b8279e92e98bdb4cf4c0aa48af634ed738210497e6d000eb99cbf1ae704071a6c431c9abc8997c23749070d53fe4fd53838acb06aaaa79aa46af9c564305718235a758add978bc65d315bc0d01e5a2511b4bb03c3715973a4666b9cea3cd9b783e86ac224443eb948c2baec8c99d18300410cc1713b92202ca86f88a2f877128a81abd3b08057c7c25ea57f2a1f8d9bb974ac05c7ab0aa100df66600ffbd3330ce099be1dcc11f846ba69ec08b0c96b6649ad5fd96e11634260ddfccbffc49665abc458943444d0b339c4c445e16e114a3b6ac5c498107ffca4cf541b30c684729c16adaa0dc2460b8aea248bf7e19843503b9fec3bf7f1c6284944f6fc63016e6890f07fe630427eb0cb471f191e238a43f194941738a0d19930e8da9a553d3c681673a832e013911cf85407f0c8dea55c91b47c01179f94cafa55f19480ca240c7b5b1f372b6e405bae9495d32e36b698dc2ecc60353d114f89ccf724414c2d28fe8cc36308a5e6d4116da7f59aed50d38ba103fad4bbdd6f183ebef044732439fe0963305ea5485022c199eb4241ebc6f89dfce44bcae8f927d67ee80467dd20e4ce5955bd51f62dddcb617d399c43ce46621f6e5dd0a94c8c1ad655b532a43bd8cfbfb783eeba32618e662ab00c0b80952688a45423558401a1bf07c8f67ccba199db609c3c1d31db6879fb919e477ed6e940020665062a84ccada77f96af76587e404ef4bf562ffeafc45a14dc8b18a4129dbdde07655c940c27cdbfac313d04e80b032574a434c66bf19f9017e715220ee28f478f748c72190e61638fd5e763b5fef74318115a0d85c9264f6708e002a50fb158144a4cfc23658848771d8fcebc522942405345dfe00f9b6e8c19f387b13470d6ee6907606300300008382d4686ef3de62517c93837f2f60fa9347d211f5b166fe4d7e531c05c57210b88b29e56c1405cd6c9b153e3f1bb5c5703329037b2b254be23223e68746dab50bc1f25ea87020f2d95c818f21b63f386f41c0fb0dd8254155b81dee18cad39426ca90e4a535cb9e76a27a089b2c3ae6cf104e69ab3a99e937f4ae18940efae603b8817676e4d600c46d4e427dccbfc714dd5589f65b645eb9ff71c70f9c0374304ab21d76e386d628081d98f0b3ba608ede102340af0e9b9ef8226a8a72730635b0921a25b3a0578a813cd9db37dfae130e929edbd48b44d6453c29f2bea4c53dc358e8ec9a1fab04808ac103f8cb8fd59aa9992f8c241449b5efd1852f7bf1c9eaa003074815f9bba43fbef249973c30fcd29e5d0de69a4f6b6ec6cc12950ebc85d7ace1d0ce29d7095a3ca01db64cf02123814f82fc5bcd5dec1eb06582886a34249bfca144872ce269aaf50ea4b7e71ece906d7ebafa2a2bf81646f4b66a203e1c8b44f7dc48f2b90f5e7f728b3ae5fb89b6b4bfe90c0d3fac21ce81545abfc3f0d7bbe37e209b71310c9f53bbf4236ca4acc586a8a70341397f12344cc97bcd25f6c3ceee685eb9a35c0fad93837ca52af8ea92d03221df9eed6ef563b23941f87a4fcdca2dbba87a8c2b9cbd05a106cbbca20e16f93d3c26415e84d970bc54ffe830c866ab4549a12c23ff84645817219f91fdc101048256ece05958c0ef86eb38eca2b55a28e397904a0f8e0b9f94d39d49c689367610fed215e50d17a051fc26f14b07743aba98a218a1434f2ef82cbc168d72bd658550cb8c2acd8f3fb02ef70a9367ab085d91a51e8e5c4f2424ab56f0f25382d4c11903956da35da763f89e4da7bf91a9cdfc7a54ba3cda529b7c04ad2bd23410288075ef88b13a9cc07e5c8ff4cdd79d476666c049753515aea4c54fbd2bbf28020b6935e6b3b93a106d310cf407818ef8aa6b31df4d6d190dc20094924513cf488e1610899310ae60cf9946737833a074822250cde9288cbacaba54b3e124c00da42ec163955197ddb58e9375c0907220c93d4113ee729e8d98f6a34ef8c6713609972666b4f84e10c7f575742e1f6f8d641d99524446eba86e9b3e9ccc180ff0ec3cb40c8f2d8ef8e520e180c289d3c4ef298d6f86e257cfecadd1e4638fd72f611339af20bcc7b98b2d71b55a6c2c468412487946907f476278c10500512a6bd8386e0ef261d9b9d3f4010487bc45823b1f36ea63f71a06bb1a772f14a09d9b8c40d250b6b8ebe87d0c7292b8890da226011b96addfd8a75a04621a851d368a0a1879e22436408d11e66e984338db2662e48130ed18ab9670d824c80b189bbb74fe25b90258a03ac816cd00a59566ed2e7149ddd341bed866deb076fa4d0ddc32c0952815a633eada34deda23925d3949eefd2c296d2b2b801d502933445214d79399fc2df95b7d6a311add180500d0eb98a965cdad919edcfb2b456c832560370607c0ea605330a6a25c63a700760279cc1f650df6bc8b7f85776dc6ecdce55b4bc62eeca940663ec638412300d0fea94ca33064cb8f4e022a5fc8ff9b1a71f15227ebfd770a511be21f2aed815b9d27814f88f35cd51a197ee29273714c2ac5ad8f75a898bb44cd8571aab8f69cb2af4db6676db7b2f530e362b1c3bd0fb67caa48cf5df5607e56f78083e7137aa97bd7da13e3824bd3c5e292c81d26f7d1078a29935e05f7503659aa56271c5262391d6c8e2a08fff86c22f6bab3f3c1efebbfe42cebd00433f555e517ed822fdbd34de4240b79b5c2dff9aac290c3857463180f21591ac197e218244125fde54dfe404420bae25f8d781e28c9ea74a8c2f746d428ceb70243577eb03f8321ff9abd41e8e6e7a8ba6b4a3bdee9168d2ccfa499303d53df271a15245f58e0ba28b94377ad046138e287479d9ff58721587043af7c89b03210cc1e8249f3dc5f45cec07861b08d7f8ae5bc094b0a52c2dff86cb1bcce9644477114ee9b257e4446ec63de1b488b155a8bd9955425f6be3699c5d2be7ee10a4ad426d2a693d7e0ffc0e38d2623cab55f28f05c5557c1da59896b38c07f89f141a4336c304cee5c289e9a05581b0ef813fa1cab7026978b74413d261b75f6114aa1d5124e8cea8cc4dacf091128675bad07d6f2b6b55ba182d57cb5eaecec5a39c6479873e0e37587fccb6d07bb0544437e2aa8f2e89a548e87d3329626438df0f8f8becba8e0c41f45a1e4fb5a5444eadab86d8156b6319654cd1feebb1289a09664828afeb4adf9a5839d01c85a238ee5be9a24569d7146f5c5a9678908c3c4941c72b6666e42a3e6f4db434198645b9686ecafe52a100316169ef4fed55e036ccdc099afcf298d73bea505df608abc60225504566aae2b83d2a221de7657579748efccb0a7c70ba4a6d0837cab4d58e46f9a48a07756b53a1a5ea0f7635516b907bb64881c5731e8fff60097fe4335b275e56df9d80d97a7739606a23efe4fa7fb2dcde63ea992acb3491851c1f94bcb67a42ef5f7844d293388b315223cf205409cb9680103af2b223dcded5b2fadbd0d339c75bf63b19042aa77869f1ce4d54932f4e9b7a864a1ad8fe889077cc0c885ab105d6b4ea87eadc70793f21aea5e5418915951d3e7f59783e9a48007b1dd3ef033c6140a6b41ec4c64f715c6343507d05b88110e024350867ce3f6ed080e90e6d7f9a480556a5a70c5d17ae4ffef9011f769c763cb0941087965d3ce4b4cecfa81dc37f6359e4dd370bbebdb1cc895dd53a3b36fec10320f4b16bb39f2204210ab0ed1d5fc01cd489023914de7aca27c8891dd700cf07bd28c9278511f3fc2b9da833aef11cfc46f4b3cfa147b2977b99930c98f359eab14c71b750b8efeef8265c868c0d15d5175e1f77dd22ec47efb08c44930ce860d12497ea7b9ee5b6354be91e6d0cd85ac4110cf0d84e60855c5cf5c3a60bf42c1034209c8508d516a49e360014f62be407adb5165fe7e6d215f9a21ca0f1957a92d3bf40bc6687b37e8b90347d3e854fc3d06146acc42bc6e512bbcc7b90fa3fb627d5e5a70a3048dad8ac721f5f3e11112cbdcee5c1afd6d1a31d0a81c91ac1d6a50eeefb966737e3ba4728275dd0a300573c37a22470f859ff5db3a31ba6fdf2e5522b143ed2283e5e8d29ae429c6b6b81b843118e67509bdb87739b1527c63491df3cd2916c69c5f7e09c7a7e6cca493eea08c77144706ab7e5f3e1aa8525d3cbcf2ee1111ceef895f16818b06901c588cb4f71680c411c0a6e7a811ee6e71b0f5096304e6b2469cb131c4ab174fbfdb82d0d9755612428bdb0f78f85fb7dc4565580a61ab107fd4cc0b9ac381a859a8dac21d182c00efc11d842834289f2c3f487b761447d55fc6732427612a66dcfc6af7321812de5277a3c681f7200016c0574a107f1c133dd68d1ce966ca0a5d99fe1ea1a77e61b0aecccfa1273a46036a1563639926abd89d323eac27de61ce714c9b840849937535767dbc79925dece58c574bf4244707e91aa2fb9f26c01bad21602bdc064682a30e3f5e5a48c978d2c0faccef90cb904fd1e7ff7b13b67adcd221e6fa6ba7aa63d05190598dcc8e0a1b60cf58ba335aeda1d983b6ebedf045d4e77d11c2270ebb158c2655ef7fce3cc45f39c0a83f3d0e4104d0c88b71cc45dc235aabfcc9cfa71c115a9161fa08a8f946c2d205c023740cf063e0041d98a91684f080c5fdb00cf4defd83cf92a813f93ea01e1e91e33d8fc60769c8274b254dca39fbc8c2f2aaea89d0225eecad34d1886bde1d9066959e853474a71ac6fbb44714026c3b5af802c9b977aeb83338d5479c9d7ec01c86bd357d04c80e4eeed0564ede4b2444a99d9121b8a823934c270906b48ba2d1b35e0057ec2608338c70db90b3c77e5548a002d88fca8b68d07136a647de9d2b94a0dd8f2323c60ff2185237ba07efd8d49b4bd809bcd0b22a864ab8a279cef7cbdb22e0ecacd8cddeb6fc8fe24d94f2788d91eae144874a53abca9e1c019351654663f5ecf40e1b256d13a3012fff19af4c119a5d0a3d256c87e05dff323f3d89af38b8b6975abc69d3a5cfd70a78bda5488d353a97bb20805262c3884eec00c098c9472a9c17661c273e05d409c1d7dd569a25d0f8e85859751609e6805a8079169517dbb8cf82671034c8ccdaf73f8107d191a524d23bf0853acc1ebc400305425371bddbc230e35675770b0c5c27e0d59e294db4032b0fac21c83bb971c4a57242b40c14d088f9b0b3f6fd422a951da18b66a38752273df0419a74579e1d18a82bb8d21628ae1dfc1744deef83163062281099fba4840177acb4f68e4bc97c249f4a98c1e778eb63309528785e6ab1bbf9f48f1f155b0fa44a76e764e2b678c4e3282e9d386ee6d5e844981fc0d068ce827e092c66d7016f28260cae6966b6f343a2721489a3113fc9cb5a7b18044a2b3c883c4a076c01cdc6853036d4eec04ad9737bd327a350df1fbda99a86da8c321057903c15f54c82eb040daf29d310666013fc8c473b3242f8643d1fea179e1a3539d4bbbeec7579da5f2d0b4a0057639d682b1cfb7fca089c02b9ff52a68242802b71292926a7c2206572a1903acb4f705704de9f6be532ceac4a4b30014691d0a94c12096d7d4659a86061fb91228c073ea4b0ac2364351573000dd3ae49964dbbd915341d3c548430916af268b05883b15a0cc851bb7e755a0325a0b12fbe7b2c725484e50ee3087ff80c2ab5730b91b5e5e2bea2d2823a40a68745e75d0810cc1a5e9bb5b35c1c13af83ed9a1dd43984659e4acb80bac93f55b2efd6721635a0b3713048288f2a61c5082af585a61c621046c37133df6877d84e79f9d1fac2490de14c798440701c6d41a00ea2996e1288467cf08a7825e88ae82358fe05293ae71f91ab4ec498c3a157c01910a31cc017e27af57f926f1b4a11273342924f7a0d08a48b707560adb1ce9ae81fe0cbca97900b771bb5619a88b133157fb585ebcf264f26e0246a29126c18cb7692456abdfb48c1a102aa54ebfd5ba3ad107faac1bbe07c8ac8c4fd217e29bf142ae7d386cb0ab4e560a9db9bbc25effbcbfb8f35df51ab5e76fb81790176a6b84331800015895b09e8d8caaf5a2fcdfc518a2bf8a31ab50ca30fd6a28ab11f96ffd8a1be471c34613e29ec673cbe802e17373373d5b069f4d8a8531066ce2687f0c32e0056c1c5fdebdf3d163b32da327c74d45f97d1cb31e73596d0f18a3a0622914df5d2716788b3831cdc3bc4459bb9943150ef090bfdd1742ac89b472388cf746ce7c6e6115e31964d4d22849c1e50edc4c42fe91cc4d7f2ef3f0f66898183514e2b07b72b73ef9a63313466878209f0b51901ed12442924d60512bd10562b500965b18e8487c73564df6a393e1ee64099c13b880d3004e31cd1d3bf0043a9d2bc52f6875d7bb22efb00e01bd10256817ee8c7d02935cdfb4925868c2e556ff0a0252c6f80b41e3ae221a845b2e0d07188a6b1f22f533a02f2f13bf2fb06b317577764c40f057808b3138e21f7b430cb79f800f16f899372296268643d6c8aa884beef196ac118b2355181c687341856628a9fbda1222b327c99420912bf62b838599195d1763c1f7be40aec21581be1a12b2d7e267811a8dc4e0cc3242973a15308c24732306a05da8320926255fec276050ae29782f8eb2aa510f1d55d30682edef929e6ccb5d0342f4411187407021b5b11b61a4922ef93e94ad1abda2fe548c4a81b79c3ff4dca1db0984345ee5a064d070d09c51b7a89a423e97e198c75d99d0363112df24cc35c051bd577e3e6f1c4dd0b8d66a5ced9ab0648c8dc6ee700d445c3c3e5f0368168ffcb0a13e21202bfd678a94891c7be25fbc7f8b9645fe645c8e2de4060a0e496f2e1627673064fcef19ce19e313c1fb3384740fe77dd6155804e46fac78b905d906ea28b7771ddfe2cfaba204eb48d65c1c33e2c93830940ae44e5abf50e0c50e67c7b6500dc981f7831cc7d421a9cad4119787c1bee501df5b3e419006a21ab5cfaec44c6b28bf082ac31865784d732ad407974a6b9cb3fc788702400bb233c8250dd636e4cfa559e73dfdff531f7d151aba75b13c93d007702759d4a0d5c7053d3c1f84a46dfeab5f1782e766fcf738eaeb2bce8edee1a08431aa0a2c5743ed412a0d68208398d503d0104080c86bc3cb4901448a1224f9d4a57e49cc5b06a2357ca97f13fc3067a72dbb559af7f347983854a28597e0640619c0faefe4e306aca40c6c836b92aa3935154a7d2387994c9eb371374eaa0f64c651ca76eed4d2683286ec2a6c648e9e4b1f69aa91fac2c083824a1381c583f369b8069c1df44e799f0ea0af8fafadcc2c0804db33a7fcb5e324d55f4271a63d70ba7ad9ddbc59e440316f1ea85ec74e62d1d5a9200d965145f1480d93791ab88bdc71cffb508d587e6636dbd7c447f5416f087196abf41e65823eb988cb9341a9c84549ce4856d4d35545fb1936c8003f2a637751f408daef9dca9bc63121197e57c1133520e770a5c19795d9019c9668a1546f7a318b4ce74a0c49e0f6363ea597e60c77db1c54e20c758c58ca1e30c27a22e4304be0207c5d18d246253521d122c008222248bdae69217f5bed647a6b6df7b43ed3740c8f2cb7d8239e0b0f2fd82253956b92ef29f123b034040c8c841b013f1cd23f3bb0b2e595d5862b560d52fc24342b656393e7fd5ec1220d0b97ace46011d61c31b2cbdc87626858552362dead4bb43feef46d346d8133c72f9f09786356bef5354ae1d1720be9e0e382cccb9215f514d52783d63749d5bcaf56d9287cd287230339627380ea20a0850b5cdc3c4cb413dbfb493f34c827670404e281f5d34b80e1cc129f2e0c99c6f3bcd475a38c10e261d22460f6c68e59c50c328ebc3e069d1bc1860c69d3bc3f7682eae08874ce7f8a7022e130158eb8488c5af85590e35a53dd5fdf54d716e3e7713a331151b25386de025b0eba5362f4b8297d66ad5e6f152e9808c7e921145ec445cc0712379bc324936161c6f5cc4cd0c353c12683aa6a424a502f3d78c42cad6a5f4b7db57d0c364e32b3e8d8cad4d01c6472a33f002257cfd7585af45a41084f932c2dfe8acb5b8822b79861729ee055ba89b96fb025d7a6de8bd9e6297dd89fd206e4fcc209c29ea187d8e496333d2789a8a6dc5e20ad045363805188fd2d43aaaf9aa99d066429916e81e1227977e065a6ae7f3b85ca5069a59dba92d96ab00fd17203a110f5b9892251fa731b8a8494c2818734315e364e8366f66f69170672ed93283a445207fc617244e11ce338ebd22d5d7bbc5b4138e2b9075055f91b5a28741a71c22d857e07865504197f63c5c58d9b4556aa6b742fd0109fef09250c4c4cbd037a2cf9a96dd8f731ac821c863a5130bf5e0f86f498f5d22554bf1552200f753ec72111150b412f80e2f55aacb764a9234525c2410545c445efff46ce2f2fba6d58c3fec874e694563b8350820d4af9fbbe6ba6d14c983b3b6716b8f26cf73457fc576ed54206e8eba3ba6b25b9cecf347d9a06edd2a3dae8ce97035fc246050206568ec815dede61d503cff6e9b7753604cc70bcf80404082eaed037f629d3fbbec371e7a5a077ca0d9b50c9d38ba11bb4ae6f28032af931d3e183e4421c39c91acf01a99d6236755b914b8655d29fb86c0e543569ffa995eefe52d2342bd951147cb1a4bcb4cd4d69d9d080aa7bd07b57e15a0e0ffd8f96dddb49381bd0ed88defc964af041207569e67777a449d6934dbbd359bbc45a4b8d218a8e9e6729fcb26169f74204145ab9e2e67f9ac7569aa9e70471d0c367925e9932c564828a2ea5c91f7aab2415285edd2d224671d34e54961a5b1200a25754824a3fb596b699cf108b538040bf2f00d43e8e158f36ce6a77079cf78978d94735332300a5c663e441ca07c7415f7582d52cc565fe08186c0939a0a53e596b62a424cc454683919280a218f8e21d8fca2e0a0b609bb035413167006be16a1cb9eae2f76893444efe8fa26e0bde54e54cf682a6f91a0d2faf7c4f31d9567972467452d2524982c6706b242d35989285902e7503c14fabe5aa442fef51a200eb495490e6a397dbfc4d594ab7fae388774a3a775a3da383e650521fb5936b40b0beb12c1b87bc088328785b80981f3510da3114697643df3d280fb6608b84ef69a7a5904d489334ef150a74ceae6ee6d704ee2e9baa7f4281fcb16f4bf315774f49ebca369678186912d871e9cd21f8dcf28eed11284eec6be42140e551dd3ec59e9344758468dde9e9ad00caef1f03e4827aec27e46586a51de2d26fc2e17c470ea67463130669f0c6d34224acc32458e2747edb44ed167f33fc2984ae8d0e75019673cce3130e6d4f549055ae941a0c6c464519221f73c728bf376bf0ee4e680da75e910f0f618b0a3c599f425f67acf2c31f6fdb95b38206ff8eab309ef755f3c2c31965131f290fa771851352125e7a795c08159c6dce4894d046aa1b6a2af7c19d8e8431c5ebf29997fcc22b7841d2039e5ea8c01195103574b2800ea16fd15388780eb1bfb2bb052062be6adf61884f043f871f9179681b40f2edfb9ae04d4f09fc1df56daf2a5c651f6c1655666822177b1f6c27b3a2bc5d5bb46476eac65e2be24e6095e162c138b6d46112c54c97c18cddd5069b836aadd18c1112eafc6ba8a044e07fec0b57f67cdcdd9cfa478890ddf61ac7b04c5abb47bb8525cdac4776185a100e74d6e95b92c1e8872adb07a329c9a7442f5ce4895dd1cfede15d6898a54b063d8d87274fe88b3149274b56f75f5f6bf5a6ca9999611bfb2e114742f96c52600b11a4a4491b09f046f5dd3a3e546f39d177f561aa35d884e49af9c0e2edf900849213edba22e252d5767ce0404f8bcc4f6c8d974214efd982463a94e769bc95ca00bdc88a5f62f5257e62f2f5f9f5d2faf8af85073b11a176cadae0c413e57cfdd23803405502679c8ce929c3fa88cdc3e0db896d470480b1cb1b4b47be284049fb79e04ed2d7396e927fd383716a55d6a9a1443fad25ae320a87ae51c06ff44a683636ef354d841ce451b79e8b5e896684c22ac6e54bbe6f73a01c8613cc80d52437bace8c0ff8b7576550ad2043223bf59fa7a000983d56c3a9eaa428b73e498832606a1cb8f3f7fc3825548527004fa224cd934c06a8d82ab3809418f8adaa2892395419acd0338386b6d1f99754625ce03630924dc74575cb37ea3b4151951fa9b6c1d3ae4bf689489fed48e86078b3827d37421ae1d146c4e9569bb0274419b72661c39b43376b9814ca79f2fa7b28ed9e04a3d498529413af3b51373c16d0ce9450d6b74137e12043045b530713d5106fabc45b8767b30c11ec4c51aa224aa6baaebd9950afe32c82ea597765fc020b567f681c7247275e748ef440dcc1c3463105d35b2ff94c83e1a73d2ce7d1fa0246bd20bbe58a97835d2830278bb9406767c80d8db5d849a0fa73d95e06636009b583e7a712e7627a64cfe5866efe2176890413af4637fa870f6340b501b0393a8c5e4a71c4e71f22a594f6eff9c74f26393e865cde5313fcb432bc99f000f1c1bc69b32aaa99c3fe0f1113f83ecd5a65915bc41db9799eb6b1b03d0fad3c6be77919ab232e9b6947d02ca6e3350f57b46f4d0068c0e319de44b7823b5cafc24385408f55a95057109fe411d2d77421af97c490198168677da6e59d8c469ec699bf93040b851a50d8f57923a2b36b25c5024b84f7225fd6a9c4c76f389dca07c64fc1987d8ced0694e106d58ebe39a001e471f1552b831b5f050da9dc19fb44171265e8f0e1696166579104b09bbd4ae41efde8ce8cf49f4f0a987f9120962c6123d25008387c6383b252b2d89435dc002017675f5efc51b486213c6608dc104da9ba66fb31cc044229fe9ac5b3db9efd0de219e511893b408d3798bb57215137641565d5ffd5095841b7a3ba6c4502fcea53910159284c2ff910612f2fdd2802d1441b242a0fab9d74a3fce6137ba649677933de48ab67d9ac2ccd62a31844db8db2016fe6753f2708e37237d8a9beb85c71e2d981327c33823a8d4207df0b0121a4c38badcc72f8f650ad8a3f1c964d61410a553c7fd5350e43063a438adfff5fc929b5e6e1d0215fb11cda94fd962a69645bfb0397f2dfd9c1a2267a56cf6c14234184b7365e5b880c9ee6fbd5f5e300502344afef7ff8add0203ca4c7e021f0901f5b79f2c99745496a16dffaf962d4bfe02aa3b4fbde7af311ada018384652168fea2579738ba3daec9a2a614fc1bc6bf633c5e4b5e9c4c29437885f927690cc55887aff87bfb58dfbe18f087a0d0789d4a454be9c37e733f6a39b1a14ddf0acc8c2d4c1db96b3a42511a01b3ab80dc2c50265493cbeab9534c8fd878902b7846b72cfb4434a63b67bc3ceba0ff42bb8c552572a560c04e9c8b76cbda952e23031faa0c39db5b158833d958451a9a6e320729f0b40c69dac259afac3ecfc852d05dc22c71425d543951a6f592337fa9c4d51b0306ecda6d23c7a6086fb5d96564dc0f717cac82b7679fda27bedb4d79c118f66cf64873e374023a55e29389186a0e1b8f64e5baa7f0894646129fe468ea9e0b39ca15be2343988e5a85344edd1fab58be7d8add79eb726a5d0544c836029d6374dbc85f93636cdd0fe783485e42d708c13344d208a9eb57a0a2bf368f9f1978efb3fd57501e5818debc2d3281d176a029379b0ef90b29adeeda8249654530ab16f489d43a6f8b04fca74d48d42d44752e2206c64e5185b3f8ef31667ab347b0f4908c8e963fe49d5cb246904ec6bdd574d016e0034701acc1c75c86c1c0435b9306473cf8a413794fb46840fa559adf12dcd7c3e09f86b7b17c912afeb705c395eddf457d125bc23d745674d3b7be5ba4ce3b9464b93095ca11bdc6a65e3e3470f2c834caf76ebb588c3fc654af43cfde26a0e0ff79626de3cd93f5f8f3ba5fc462c6a9928914ab0f2ba7ccd50d14d4e567e2e9b4e1e50f60c8151d11f962ec175609bab069b70f93b158618d416064138b4eae630bade7ca82bbbff0f54180b654ccde3528a950eb43bcd0cd30d5bc488fd5123ddb8b5dec0f395d73e67ea618a501f0a3d9a31d04332b92e9af355373c1913dd5a98013cf8f2637beff06462c9c5ccbec42e3237bdd7347374d9b3b16940be20ff8ca6119368087edb207bdcd6b00cd245d9447f9e92e19548e9fcf72ea2a8a6e1ecfce44af6864a9758b5eccae389e33e742f235ca10007ac79942f4cfd1e200320af1c6c6938516268d3823bb20f53b8a0282c6ef2d9a5dc3f0c9926685c3ec93d6a488a6de61b2b57271f7b05a997c20a87b080fdd8c8980c4e600c37845a4db55de43b023e646074816fb68e431660bdb72db70c661cee0888a9db65fe98b8a1d574b7950b5e9e51b24146007a2afb9a1a7c8352a10edd1fed170c93753848c0f70e708c21b552fbd9255ce646b2ea69e1fed9110a015633d987ca8fb6b99fec50402a5dc40d8a349a4800272867b9ae501fe9a94fcec2e7e86a209210d9bc2aab338720f9e28612ba1ecddd5cefb91c7dd5902f24937365028a879e11c17a227b2b38cbc6d2f79794ad52eab5d1a98ef13aaa3e22eab5db0c753e1ed856349e3a07ce6389722be9fb40077ecae38bc3b619617e61450d276646e5879c70c1d9ee90206960e470fe5da9fe925e8adb5672f3a7526665807f031d960c749d6cbc73a0ee040252e87ffaf36703e1bd29e1d540a824631f2418e7ffc8f7dd35c9327d0fe1373d08755ba1f290771fb65ea28bfa1d1ce24551743ea2f5f9e4f2aae934d870afae0dd1273bef0c2d6e85de10d9b59b601d7e3d0b1933de33ae204da73b335c366dc9b5188eeb059f7a5c6eb2228d41903c0859b0ead3d901868274fa4068e3c813d00033741ea73c915dc548822d584e368c0afb9c8fff7d4650ae2c2051dc60ee3eacb4e37afa2d870529cadb159d725835f05dcd2e56e16c2a14855a8ab285d8cc94af527e35605952cb29ed045ce53ce96d3012d2b87cb5dc9c5940688b2ca999417e43f13d350ccf33f13a8c24599a177032b13fb0a9828bef842959b645bb4be9843ce7bacfd24360f686807e329afd84902dcc15c93da620e0216276e80ad7e32891c526510d878fb53da51ef66f25e457e971c56158744e306d30ded17ba8cc9703f94bc2c663ca5456c99988df30cba8a9cee778d8051fa62589b34b0a5bfed80a7ac5e9f8b8067c0676581436a7046a87e95aa70532b8a9e64899a4a1d79d4598b914dad5df0276cbf5a0c0e6bf643dd2fb9ef53ea8e356417cbe71d05b1b81a51ae3ddb78995c1490f492ead7046b19c5fa0ce2c89175d9010c59bf2695a926bcf96ae79d347fbbb8319e9c2adb28752a6b9c1fcadad91d8d95b286b34326808aa6903dbd806fe6e7f1c1e899571b0bb4e22296cfd7412783b127b1c84d762e11ce0ff1d7a75752e198641454cba8d94192cda93586a6988c8ef74d97f6d083ff746376eaeb14ec5a1d6dda6ef4f261a08acc46032a2bf9b970e7aa8f6a6c4d7857d1fd2290477ee669400ed6f746a3e5572b849367d029632653ab6106db9fd2e639531b9e1098218a4f53473e2bb3d165b365b04f09cfcff0ca8b42ad7dbc66e93900b90020d984e76b5260a7658d0826120c8087850a135f7fe5fd8a30b2ae7b8c3d9fbcff4558b267097dc8eba576a2270ce990030638b75b7d462729830b5b57fa46638c6c00d6dcab8381c768a80131ec9b059b39beb66a77973a406c9339b06c4f376df92b49ef6904e741bbd04417a93b388909ab029f1c5db8a19525995b328138b99748ea71456e7a413241b33241d030ac8dd5d5c4014163707b1cd0e9bc5f1b25d7fbb7c3b7ccc80b57226f47da9cd3aa090ce9aed9b97c99f4b96b177130434f5f044b6c3c5a68bca0835bcf0b8df831fbb12c7e6b41f4dc96f61b6636cd2d67b4a5f804ff482464b9b244922654a29bf05cb05c2056384cf83cb4f3f94103246cb4b5d903f84b0582e44878a344992244992d5e52095e2a2bc4ea7d3898ad39f2845013eddcb896047740d85c169fcd3f79c565229a8405788ce7890f1adf0991b0aa3846bf6fa243ae6f35be130df666a2714bf82f37a2b5e892b89a6b1bfe4a7782856f0a753105bc4901fac7be61d03d608a5ba1d90130cc33014eade7befbda11008a6bcde542873280cc3300cebb1d1aaa27d62fed80fe5f90976a821b06359bcf7db7e58adddba17bb376443947da7c5d5c2d9b4f628ccab04d134bbd429ddb44dbb30f6bc1cba3f346ddab4bb5bebeead9b366ddaddadddebcffa4157b0caa7e3b615acf2e938ae082c8a6cbeb24c4921c6b53056f988e83be8de2c5f9cdb40a390f320366299ea609867bfee7eddb59b356a35ab59cd6a5ab679b125d42efd9ae6477d352d13d9bc5cd1ddb4e9fdffd13b2a84ebbaaed369752ff5bc9c413014baaeebba7c34cdd7ce78c576180dc57ea2e9780ff1835df5a0c0758566d14f7ea233a09675cd3753500582601af4e4613860f04070030e5f5ae6b305b5744c0aafe0eb743a9dacfd0f183a6763a6f9add6b71690795afe7c26d3667295b7bee5262c302569134d9224fdd60a0fa6be050c3de33770d3fb81ca0b5306372440cbf400402652355e04920973349fb80933796e3299a6699ae6bd366e4c9527582854ea89142567cf2a8a8a8b8a429521efba2e123c9dd05ff6b9f8d59b0f9a977edbe72821b0d85c847f9120830d5b506e830a19a03df4aa4fa1de7c93d237e99b660875a25028140a55832bad8ef58c45e061be011df33561cbd0ee017bae39d3ec206ce489f59145318e23a9ec1142ea9c929488d47154a94a6e855dacb550f073b57a900b374de747b221af72d2b1dddc60bfe1bad15123d040086b0e1e3a22d034ae23819e711e1ae673d7f9a85a6185518676e27ed241142fc2ebb9ab1fe4018badd27185d7573f198ce3388ee3385e570d1528a59229c584086aa754723a7215e148bc896badf59215c287b8f9f1136ec4061a7a16fb8b8ed263033e680002dbaf827e72afdb0e158197c475a7c6832ac0a107ff93826b16c130003d2a5da552a9542a595b23f4b182529369753b462514ef755d2c2ca15333b78897e4f2b067b25c696a0b1fa92762a75eb2104ff53cb8036aea181e5c3f2f91c254c634454d2693a99e40d44a92e5ebb85bb783cb5ade9ecea853601d554ca1d28131de386d0a3c23145b955facc0627ec9c2c6831d8e500a8ba1971a60f1caf0b2048b311c80d6af069a3d50433b096faeb986fb7da09d84f55bc15361d1eb3e22f067838733c24c099c4307f4491e29a295003f797af0d64b02f2b390b72b14c81b9ddc2852a6e83c89e61ee7799224499224795d35700f1126241126b2bbb3ceb22ccbb24c0a07ba6bc2e17b9b841d8e8cd20f94153e9ee79ea184a29b5ab2acd600411908d10297f375bde0fe8944958a2442d14d36b250c846e0ea50c500080eb836290cdb68e0b1ca5bfe1da41b6e735296d910bd6063ae80715374a4d48ea4b247888e3dd924b66489849fa91d59cdda56f762ec7939836028f494d2cb0bd15014fdf0421e0b13d833cbcb711f98a8cc510e904d896ad4ef420e908da734e2c9329090115594463c58f644e3687f9891bd241adb22ec108b72c933f2f7a8568764c707ed4841375c21e602cae1d9b1b292735cf2ca97b3925b720e4b9651bd3acda1147ace096b761790e6dcf6e9724e0ecf0e973ce3cb99915d3e1a38f728c725d3c026954fcce1f4c901d57da20dc78386f64da0472ea01ecd9872b30cd4061ad6cf0606b8645f0be8078519594dd33444f40c2dcb233a0b7a648950a435605d526849672e130df3f9f244d1636b60c78eb9453aa67e57eb6299acd0eebc35d99114beafd81213987e383f6c18586cf3e6871d473a8ee368473b86f5a654bec5c465cce639cebe8481c57e02e4ad00e28811977dd019f046017bc6403cb8669b5da39a96fd014a53f46819759a8d701f298fb68f86e71e61370f9af77bd22cc0f75e1f3548911162f5125493f07307de65027f7ed087b756268c18b65ae5ce977b2f4bafe15c86e3388ee39274ccbb736e39950e24e2bcc6008d002e947580451f3fa1180a49ce43d770dce751b4cce71b8ea4c1ef3e97a26368a87d5e96b4c440f9ef5d89810b748d972e43c3e5cf16f09a2d7fa6a169b416ab9de01a2e53eab8da0758995d1e1db76929cfeeffae95e3f8abf1d3e273d9e6bb5ce3931d3bfed4f87c3a6ed340b563ee83dc83e30a40d45a315b310b002af08cfdec9b409b66c8b699e301d05371aeab063686b5f6b2172e71e05e620232c4b2e7a74df3ee8e5d1d0e232b39fe23f41bc21a7a156ab889562d485ce9f02af40097452c671983720b9feeea64c0fc13b90e33500ee1e59f58f3f6ddf061be40349c7b98cf5912d8e66be19b45d1a783a11fccb24face177307c7819592982455178896c3c0c58233472411882e3382ee7cf4745c64ade01c8387ac99d5d724bd6998157be729917508f3619a097fc0179cba7e3e35df8cc92678058be1ef28887cbbef21a11b4cc679719f9472f2b36bfad805eb20c900a885f5436caadf4c8c572d957befa924701957000298c4acee7c301c85683114f96731875c7ccf8d1c67d7806b843140f057a84734b969157f2966f68596d2e777b6e9101bab7e5e215efa2c16081d24858ad3c2e832151ce9d8d037444e066078fea590574298c73ff29dd2772b350bc95c9dff01f7a8696a4a6e9c65155852af543dc756ef62cd2abddb19a200ca267e8fbd03768e5618817f40dc2a6f1ec45150d856ed131d46c9a6adaf19a9ac4445b49764c74a6a292784d6de2325d9324e19fa087a84dde888ac52f51533fd22fefada82615559b5cd7a97bc61d46afd174681ace344410f4d4343f6ed1338fe4838ca7919ec60424000441a01452f8f154d69e9aaaa8d1666c4ad3383d62ecf3c7d17fc450972601a24b744c915a83f0082a3c8d271dc6204174580149891105a110405c25741883a800054200319ec6244e2f40d2848d0ba66a661cb78e11bd70aaa083b516afb0788deb0842a967169ec01f09bc0061d82a40c2046038c2e03ab88ceb8c54e772d2785d1876eb652d86a9986853ead4bbbbbb3f4702ee8cfbc3cf0a8f7cd499d6ca82ec89946cb56a619c1287cef4c595d65aff71b390c2a2af2ed18d274ca9b121464bd42d9e26e76a85d918f4f0d0d3c3c3e8299c9e6ef5e0ac56ab9567c3f3562b4ddbc14385094f7cd05a6b075e7c20c1627e5a6b39841449962b2749cc23aa26282524e9799ee7d119aab3e309c93478244a0025144956b4472b27d414249df1d6553eab758d17293c57d4ac2a3ad3de2354499e4ca5195a928eab91349246d2485a95ab7255ae4ad34fd2248f409e48484574828c438a189d8c41c62145ac56abd5494de7399a3facc6d509c6d8349d3a72498ebc7704d8cb28f8ca66a8cea7d0353448cbd0d01d466ff452892c91259205631ae487d2905211a5911c499324c91a42a10c8436e083070d3d42a150284492a927caeb254ae45484f49ef968d7f38f86f9ac7d62163e97826635ab594dd334da1d734ba552e95dc1cab2fbddeef77341462095600123e89a175c4648d35c3d3c55ae3b991052a74898f530cc285f28c153e04678145fc251a46b4631cd9b8186f92c468f934422e517722691346d070f179c4a77777777f7aede1ca5949abc887690144c24f426ed0799fbf943b52ccbb22caf4b8b5ad6b2a2ce28b5274935ab939aaa4c52a9542a954aa572ee546aa351c4094a52a32fe2c4893412c1100b5f60b1969563a10a7cb5b004ae496a595b50026399c6f5ff6421dcd6a2ad90b5907a98cf57fb52a48ff42454288a42b196f553dfe2add042287eaafc944d7d2af5292f371312944ca3ebab9ed37c7d34bad68f8528b85d86e690f3c341f4aba74e0b267c7d622d3b500e38a168f30d34cc58ca06a7b0c32e7767013c8ee3986b78d5fa7d8afb5ec9c37cd674c01555cb0759080277e832b496652dcbb2ac652d65144192e5ca4912f388aa094a094692368796e18ed13e5c75d0198c1f80379017587452c454872ac791cb95640c2d43438004e1cffca030ef425ce603ed76bb9f9bf513310c046088816878ae76c7e7a6af78d4ef668a1b5409b502149193c934dda04c138599a6699aa679af8d1b53860e7ad5cda3c70668e8a6d775b917b829752170a61fcd1887563ce86ab55aad78d47aba529038d01ff61867173dd0a104477eb0d65a104c65daf54e5afeb30603b30985301b1d5c8a7a201f6d1c8805284001c228078866c5c84d9e032a48ce491afe7d3e2f290c0cf7f9690c8c41e1ccf12948be388d72806c4c588dfa5d28401023ee5df80ce47918c268bb3ee0365d7cb5b68f9447da47c39a2c86ad5adb9349a8ce94a73bdb7d9bdfb66ddbb6bcdda6792c14efed981c83f79d7b3d3f315a5a71ba7165813fbb3cc1a2ef34cd969f07fc7da287bfe5a7b67d94480fa639e557f48d2cfa86155e6891c2c165ada67d626bd9d5b0ec6adfaf5ce673876a012d4183bfa24318117eeba4a413cfb2e8e797bfdabe5c6ddfafb45c7e6bcbb9d5e556abdad0228b89f9b39853df2a0974bed9b42ca3f289f77e3e11e3eee33e31b75a1c07d6e898908d9b8ed9c1e3069cfaf9171f4c96144b88c565064b904abd7492f4141c71c4e98b68bef32e84e5092c9fd862884159348bfd84e513fb0cc72c7379cb2fb0e4cf083080ca27e3dcd2322d9fd8397896ccf26d94a394858585858525b3b8cc7099f1c98d9f252443c60aa8c683a18eb121dac183860771721e7427722ae2a907959832be33a2d66aabbd31cd9badffa7c09e6f9ae69fc98366c7d4d4fc0c5a86e6b57833090ad5738305169dc6cdd987606b6cd3b80adc0c30962fe87234b016b4125e1f17e13379f0632c0ec2cfbf45cba85208038b7ebeea513553b106d488f5390163d96607c9086db6f92b963021e2a6693eabd7da0e862bd3c00f7e06b984b7090f839d5074d62545665d4e9e60dfd9d1988096b33c03368388c27c773842e7953dcfbccec3bc83713ae974d2e9a4d349a7934e279d7daef499fdc8e4fb890f8f74b9bb63185823e482c4464130c46303363616134ada41d0dddd6ff0d07bf0114f8e0ffdeb4f131004c15a59250a957a2245c9d9b38aa2e2426b8cbff4b07bf66299a6699af61d0e2c7f56bbcc65b15bee99cacd8cf33a508f34500db31e7da77d8ecaa080b32f14ca26ca35bfd84d09bb37a29f348d4816ba41a15028146adb68183b1b60ac29d87996251d3a9aa63b555452809df4b913bf441be91bb507e572a6046dd206ba59f4f30ec1dd24539c43601d55879f5ace3e1a375aa641811e693466c83ce20c00b0e234d241013aa91110da8339caf2075a4223d9870348619ef6608eb6ac65d1f3c126fac66e6c422ba1c15f3dc10b25241fc10ea7ea4b4c4cd5a952a2a4c7799ed8799ee779de7b73aed06064b15aa723534c52be54e4c717c2627dba4fd77517cbaac0a2b35827d64a7edc7d3eb9eb3e9f87f38ae7173d29fd0cf9e17c92c56ab1582d91889e8e3a4f2ae564b59a8613f2319a46628d45cf9375b2ce2d735974161774c5e4c1a74d5c8687929e22ab7f1485d1314d2a53ca4404f53bf74fceea4c2c16cbc462b1582cd655a38edbaf149165fe2b58e5d371db0a56f9749c870516a909cb70b295560aadeeee6b93650765d9b5f9db319fafc96432994c9a76a2678f55e7b8accd19e833c34d7df4394ef843d163a1891e9f50f44e9c448e1a7ae17b87492c7a5ae81e9d494015de0cbc2d3cd149abf644777787e82a1402c19cdbfd5d6afb1158f4540a0473f6ae86e323cd5d9645279908d08d1e0b23984b8c48b14003dc581c0109967b7a1eb4f91b7d39b67d46ba8fcb9a566d1c908d38fbbcc44ce025f0767531d40bac7181dfc5d00fd21c4225a1b761437ed5cf895d951281205523b50245adb56bfb05fab073dc0812518c2320da1045463c9e37202ea8afcb36713a91669323aa22292eca9e7b3a7912272fc36ac7dd8ed3a8f0b8ae0b39bf3714047a825fd0a61c271dfcf9860ff866b7375eb361b96e9f13d1beee98eb52b400bb0ccb1758ee220446084ae8c6bca75396ed18adb5d65a8b596b2db6c1808a2a701676a1e88516eb70703178775805d8731294babf2021438c9aa6691a160aa5f22766cffb7080bb58a5a0ca543e1df7659fdb5d16168c3d2f6710bcda6adb6a846c885eb0710087459bedde2d9ceeacd2dddd38c0526ec011515102a8e85bd7759f8822c5eade7bfb2f765d1fe63baff3c511a81110059cb45062b4b9d0e2240fd2329f7750e94174538ecbbc7773c4eedbb13bf7ee6022d1cd329ed389349b1c5115497151f69c4e2b1b9b3055390cc36c9aa63ba92801b2d1d3eb7ef423d2ab4c0394427802cbf7c222a27f813c9d5ab4568e0a6b4dd3344dd334e94c2804821e78c19013312851a31c200ad0190269c4e3194808053838473c5a1645d67e8f553162cff1bd9c7212e6eeee5cb6a00fb492d0039eb9bc7508702f412215f61224ca5026ec25488ce193e4c665ff9c84611223b9ce45b6c8d8ba2772022c3ee9650042dba3cb2d1fa8613cbb69e24fc5cd2fecbedc315e2a7925f16710be8b5baed7855d6e5a1b1bebeeeeee9fb5d7755dd7957178c92e19072fcfc88fe401a4977170c92f198719d9cba4fd8e86e7195eb24b9ec1cb335e5cf2f5edf0f20cda61cfe4cd57758c0a26f6e5931be80554432b096dcf0f7cb8c16867809e04b9cc7814280c7bc44bf2c12dba00038a49924f3ec992f1019062b3eeee16ffaabb6d60791865fd70d68f1387fe1d8e6a2ba537887af402ce0f51005a851911dd116a02d0ea23882602167fa2a1070fd66a87b53f4c2c168bc5fa11b2f1e1a1564abfd5067cd46bafa555cb5b7655bb691976dd5aedadf6baf7da22863ce6ee8e5dcc8235b831fcffde7b5d49ca2ccbb2b274f7a157d7d5ba4a4f728328fa0676c3d6ec701922f80f2e0dbab0fb6291893c51c64cc6fa86a57d03cb9e9b66a84a7778749e0daf2357b0539e1efef59fcb4d5c64714583249779d702085307a1cfb235e2943c4cd96a95996e19966559869ab68347c912c4da16ceea455d6932f03ea30116166d1b823ddb8cd3575f7d5191c5b0ac0a1004310631884bd6087a4813d137b24c81f01f7c6479211c08acc9215d7f307a66253fdda125238b0f454616df0832726733686615460d819587fc900a9272313e2b327056c99fccb29245af594dcea8edf0b18a505dcb5a11f075752c2cdb2643fb8cc8f872808cbabc8525217564393b060c7c84f0b38a11c2cf1f28210dd2cfa23b40216475cc93a592e82caf94eabe3f39f102d327e7833db0582c168b755d66ad6fadbd027fbc2458ec966a478808870b85df753c5b48bbce092c86b4f0e66f5c81b3f01ec1622806ec490cfebd3871d3c239b55aad560b27f582eeae5d5b2ab5f4a2f4ba905094124a92a5cd46391b6b6da8c03603010720ec254a9284b69b408f74ae6cb3d8ad1728e0ed72047001fe2e5aa1a396ece0e43d95dba8a0ff1f23def702fc9f93f02181c56ed510a769bc898ea9a23405141d128ca309383bea055224a2244992f43728176bca759ef8576e93fa5c8434112962e3505e08bd2e6e09ced12a702a7c65d246d3b4ce33d6c960f34b16d8412fa105d56e0277dd6bad09ca186a6e7930ec0e077e50088c57f0bc9bb13cc3fdb04c02338e89c88ba05bad6e75abd53af2048f154eeb3ccfd3da8eb91e93acadd553700267d75ed9fd687099bf9db59496e06cb55aad160e86854129a553f0f6ff5448ad6507b81a241403096fadfd2ec77605c6e1f770db82f3dcaec09e4bdcc39bf252447e9ee779f6b8ae2c0aa6b5d65a6badb5ed3838f7629c7316e1d0999c5bb9955bb9955bb975b3ad954195123aa71c209c12e48887cb40424490608d78b454a9d4342dd664537466465e59242bdb4412d65e3bd6644ba59bada6597b5df6ba2856b14aaf6bdbecc57da41691eb889ea12814132a1e1b2459b52c8acedc286090c242c5c406255bea9824d7674976b4452c59b2da05f9fdecc97e3ee29456ddaa944e415114652d2a8bac9106515e439f582788405116c5836dc1684b2b9bea9826e7ca96e8ca96ac2dd9922dd9922dd91286995e13ce28986952cfaf188f42dd7b31db11d1fd4cf4f32c014785f0ff47f03c8ce1192108660e1411423734b4dc619685e84cf9284eb4ccbce9cb0b9ffda8042e3b5ce9900000000001c31500001808088442a17048342295b7b23b14000d537442825e3e1b4a849124c9d114a30c328621028801408000c150892c2ddcd8ab999bd4543d9452a28be475855e6441efdf869a9c7eed04c8b24feead4d784bb5e98b0eb34645472f2c0aa6a5cb1e45c9525fbb94b0cb7f78143c9c394150da8d0fb07dd83214b014cbdbc732b77ac29132d9d931009fcd1a086ecfd58179be1a213dc83a3c45775a5bed482df17138484987626691f016730dcdd1047c6aaf9a6b54421a328c5e79562cbae5dd98150d7d18fea05a2d2282bd9da99e3b53515262afbb684310daadc2e33b5bc08160d9c1027a6e280a71d70e120f70c43c48f97d58de2761878cdd226f85980c9a9e93cd6f7ac287d8fa75c023f002e713c5b76ffd48b5ffd85062a200daaaf1e0711042b3a8126bc2c5640cf13778af074eaa29f81fd4e241a49165a50e9f539d751e5c906d684d935cf592db0d35020ed627c712ad834431c04ac64a13f7a72aebb6809974d250527c7c41f7f427597b5cab489f7c2631f34e88cf3ae4f42baf87af76ece5576c5bf0b49233e4484a6fcbc4d814942b2a9c99f313cd00d97a5482901a68aba5dcd569425b9da6c8512bda8a04f531448d64143806f621a0b7ea653ebb21fd1296bf63551cb58412662de6bc64232558eab703222b9166339d28217a538498d8c55f451fd8d1af76ed151e5d86b2ff932ffe56c4c59b4e0610aa12840804debdf5af4577aec948eb8fa239b7f47fc6f92f4f9f5753b41d7a0a1d653f6c4625c01e609eb15af69727ed58b9a14b2cf2b0ef42e4cfc1312b79cc6a86359867f18b96bee4cca441a7ba31888c4c3ca6784cd22a999a54e6700f6238f43715d7c03c7c9345e2eda3e80047325f9ecb2c8e083dc48427a206966e39142e75cc1cc5a3263331e9ca51639ae5996b82cc04e58f19ea44f3245cccf0ed4bf25b31ba3103ad980316cb264fe8bcc08934ceac860f64eefba4d5969aa5c18c1d7948e8424d9199def6ab0d9a1152cc7d11fc038e70364293e7f79e80d20d7d2f823710d1549f325459250e8157debd11b931e82c9662e4fd850176a1b6be3b6c7d3bdd29ac108fd47ef07dbf639aa9fb076792061d28b6dd82aa88d417f2c7bfd63498e3a1a9818731ccbb2b967def42f8bdac8fdd6c19703c113ab6ce9e2e2236205166a3a77f4f01d4239b635650920adc89154b0d635a23c631fe10436de96ce08af2ca3065bfa1789a704e79d6c8cde9cd1a1923d65fa01e373bf629356c1448bb36329877ad885d023c5b4dcac57a10659764699d3e2f7c2c3b128553b6913ac4f039d903de3a342227c0212916db8893c904313ff3b5a92336f6b21242afb37af5c72bfe908db124646186a3fb597694b6930f12dff4a139fbeebd09a57638f42aa3d5c33258f8565a29fa0915866561aa8e2da28c3761ad2884407cef8e13bbf490ecd17d5fb1be6bb9366b8fcf79418540bb930dae190e431c90dc9d81a97ff9f5328331a95eef4b243f6efc1e13c8af4d453ac47c1cad32f35a98afbdb4f5303d54434b73a45991f9ea0be154d7fe48a9634fc5f5104fba3e8209aa723ade8f9ca96ca9c2bb4515f1128312160e910cb9249ee7b93b105d91834d27c25afd9ec80ca7d056b381a4309e6d30d258f92cf6059eefefa98d76324424840a08b839113193246ca59d296b6819e6765a3480dd053fa0bfa54a876a85bb1bbe512c7c105c9499d51b7c7bffb46680060d06dfc18a9dd4b735a6366b41a07c84124ea9415ba8dbfc6fd31553f857b0423e67d215f3b52a113bf8348f4c407199978f5ceb1a625dfbbd65e570d5485b6eaf5a58597b2103a2b9092ac094289327835c09a18acdb282964aad6e6e65be42ab39e767c9349b9fc89f96a4599b857c6ccb6f70d3b8f260017f8b9bc50ffaaaf705bec76082e2d52cf42ce46832193772ed946c0d51f87d935ec9517a2a1dd450760926bd0cf24167426b79cab2e456a44583a3a89de85bada3cb5f48f567504b26db24e66c26e00e01529e90cb19c29375688e776d1f7397e4739dbe1ca563ad4db46de9be29db682dc5068e5d8b1f1c944094d5fbfc52d08e9886a38b2983efb848a4ba6dd9e4ab1a3c6b5bbb713cc4bb07a21f13f378c9546447b40b9d5c3764295d85368a96637e3cab8bdab6fbe0f8bec7eda54bc205bc2d75953d535cf8a21d3b812a47eb8c76f51bfea362c45f6c87640d79d2bdbe392530fd0d22233d802a058b8040a129f1b7f229457e0ed2aee6a385afdf7cb0fe457e1d320bb0a90068c6b9f39cddd8cb0c7740ce68d61e094cd3358ed99fd5293a661d13250147381caf90ac58f5bb87ef6cbb67aab2e7156537593eab8f1db81a2d33f5d87d3d1046075ac7858f336c9e4f54b03065585d57298ea3d6a206b5db8de10eaf2ea6159684182c1bd447c407aa4024149f67dc69612c511fe0413d63cafbe347419a88e88922a901567780a0174a07ed577dd760886414ad95eab06d2f1b01adc55728ba22793c45c155e69bd6a40fdec8068107cb9e9e97518ffaec2545a5c3074c384f3bf7e14c80a691c69f272ff146ad861432a21a8cda43a420e1f26bd1357e29e1bd50630cda39d6c6d58ae035c4ad6120430a39c3d2d08b882463e094073fab43fb2905a94cf4a50e6eca31c4b0493857b59174a84afff3f07af2e9371857c2c53c523a93d180eb59cfc3e1b56f35a5ae0e377840be332dcf11f26041cd29ca979a20e84c27b2ed612a9d4bc3f00f5c4fdd6e8fd801bfa3c0ebf2f7e28c4f406a5f472558a880c2568dc266fe86f51c9b9e22e62df55e1b0ed003f5ab3533641e6f0d7842bd1b0776a6cdd7d63b21185e4f96ef84ccff8611ee89aca01daff09e941101c89de345514507acf9635494e88e928b08ee93bd14951b00416b84d0eb61b10c1d2f73087724c45d8cdb374b85bb35c2dc3fecd85a230860b1093419bfd64dbf293284571adfb27bf951c70ac945e9a5f6b54e7917e025f16b1204595ea3fedae44f196ee001af608028511be53e9f613e9c844dda4741ef94333cdbef91f0f16ba810baa132ae82c70597166c28dcfe3f0d9f3bc01f78f62ea20a0d6358d00e15e32783a88277903fd06f672cfbd3acecf5d7f29bab88ea85b3498e12827d984e489f360583fbdaaecfa6a6714aa7d21c8f23334f281ba678af6733400fe9d11e5e25d0d1ad1497716a3587a749076624f81178015bf47cf0106e6818b828ba65a2a287ab12398b605e17f4e97576db31c709d88e8a5585b6a1bb512e628e73731fa8d754677cf6ccd9e9950874337f880ad364826df075a46ee444ec74cea06b678132e273041644d8966a8f6368390a121b8528966239f2ec58e798a9fa2b0a8b5e04d9488fce37bce285e27b803237a5c45d382ffafbb3a0be967a2b126917e219cb090a06de48a5adfd84f623b681742e860197dabc1f004109972f51651c495bb32c0c4a9b93eb99a331cfe8cfdcd4df2eb753ba9c34715886ea2f07814b2a7e633e0bf1fd49e6585e0e07e68dfd1051ac0d928acc8a21dfc91331f8472792227d6d9102c800f8f1fa35dacf9cf3a57122b5ccfb86489c049f2c6d27908adbdc1d3aaa36f1d4157d311ff0003f7c2baa78e4f4864161eef5ca101c97eee9caebae37532cbc6b034f320ee1780e3c626cab2df674bc978ddeb801cf5232133ff18bd52044ccd9ff0912177a1cb610924c1ba15a2d31a35b428223c89d55a2227e2dbcc2afe5c4f835d8e658dd92403a8730d87b9f851d70f8a6668efb1aa091b3ae5b9cb75b99173432730ce5e2e6ed66c972bd3085200b8bcfc257aa77cc964f483df88c311b89b2d19606fa6dc3b7953ecfdca90afcb81924870dfba42edeabda6b7898530583e060c6f27164cb67b094a2e3f60838c84afd8e95a5af628822719b919b1f0eca7a68046ae16bc17940aba218da2401ef4ae03d0ccd018e2808d15a6b5a0718b24e6d4014e303f3826ced880eff2f206ebfbaabfaf8588c3c2e623f2f51bf1cea41e7e4c7a13974921a5f42b346b01759346773c6e704e313999fc3e198dabd2bfc8935e5307991f1253156dc2a2c9c516f3b0d8e8871bac88740b3f477356c2160cdfa29863121b5d6dcb917415dd70bf50691224a16809f09b104b0ffaa0e0652c28484274c198be2d0e36a948f873f36c03133caf074bab48916619934d17319b20367ace9ced6e2505ceee5aacaba9241ac2fa4d5a228ce9a7192fa2b7b2de3581f858b11ee84746dc640b9d31403ccbc34194c1d59d2360cd937da21bf0e4267503cdb2e8872c9554f209116b6c60c7d0b15f0e2c2f854088734cf646a874709f241d82ca9c0e6c1ec9d90be5d7cfba4761d64520719a94ead7fed8f17e085c0dd17115862d334067be0b9b8e4242daf953f16b8c9cf1108a940b2788d7138ce0e281b20a4353b231744df56a1cb8c97af32eece387a3340096528af1d160b6bc4d2a1354adc74e520ccf3cac1841c0662c122b12ae26ace04351ca9657dffd66dbc59bc0434e5e4ea63b1a0a49fc7bc3034b8c780fe46a50c7f8c90dd1794df8c3233b61750f470c31005a899268ac887146ad8c11e16df532592c70cab5459eb768ac26a80808ad4d0e3711c2acfdb21a80982b5ee83354b2846450aa51cf16c798327094c5ffa98a28266571992ee3929c06432fe51fa14ef291452e77c8c32e18de83a195d1cedcb151281454849890fb730ab7642ea139bcd3c9a329a11fc002aef36f923ecb0c664112d8a557dfda6eb513d98fb5b6e2254d1364a788197055c422ca8fc1fc917d038c170dd88d7a7bd2d9a1909e1dff0695f854635fc37cf9f03a066a6f8674b48ec80253b1b33eee4cdbc32e8cf5c7663b06942f0103495ef4181f79c00b83240b5d7fb635d31a19db5ad1533d78ad5eb7f5031e779ffbfd13b46aa49088b8004b6d7ae3a15b8644931ba55e535998fc83cc252b1d1ae62eb713e6f94c492a2d9fd6103a2b9d77e78fa7588ca6c6b4c91b26a3ec974954fcdbd4d362ec644b2bd68a7bd4ac95343f73d898f26ee3c1c358fa5bc574b24ba47094304be79cb4da72e345b8f33678a04129f6652e3971ff22047e77706ed21b38e44113a1f8a347582dbc64bc4dcdab25067118a38c438ed952a323a9f83a3c375d5c5d75544f0432c7d416fa6d0919403ca69756272566d0fe9052e013dbc2b5287add033ac768518eeeadcb7faf3ec4b9abeb7538e2b836f5530a2fe436a1314ba486c41bbebebc2fe6ac95e0f6d7e9ad1065c1793100f255eafbc923186230c96ba9f2afe0f8555935a54a542bc54c457755002bd6c969ae0006003a0f2b73782b98681d45941b3c5d1f512126f253e1a1a03f249e0a9b59d8c169ba8ce5caf66cb16c74fec5e74c46097c3baf86302acaf298b6434926e9d337b30e5247364e981e2250fc4d6f2a4e5af3a59ebc51bd6382e86f35831ad7ca7ba4b6a95c1cfe746325ab5569f2561fe53156622ebf91d08a3c377fb6425bd6b710613937c0436c4ec796cc1c23c032cb9fd4d8ceaf6122abc18169e34e098331872e81a7caddb273a6af965b56efa1bed756313a8f8ee8647f24909599c4a2196542a0f96685b840e6daf1848b4b1fd9393409d76229d402340a325f6b1ce00297cc4680b79fbdad7c8bbd341cae7c95b92c1024ec2eb6f4976f669099f0a4177828589554a1e6dc8d9f47b9164aff79661a1e11fdd1e310d2933b224bb08ff5a86250b04c7f6653a0ab6ac9b1e93b9fea3ed7b3ac9c76112fc240ca3b78319fc140efdf24770d3f4306d6a33b1a49bba76db61c478be52fa4cf948fc51a6a587b9a27288a5ce44d857c3f047db005c13235bb79019d1fc894db43914083c9c90a2cbe950491fc22a5fee2805ca40da169e83fc1e6b82f5e459abb1dd35e1d6600c3b4d00f973c77325b9405b270ff2f55ebf047aacd26df7c2139698ec5bebb31c9037c183f299d70e35e8340072f4256dfa56d93a14b9c851f40e9b94b89790533a4a808beae3c3b83d08d105656073183c34f863b8f48768bebe057e55cb5be67a04f0e2d7ea5a99725de18ccb1477a74fc87ccba2915a16a593d49cc52687254b5ada3b15f61f9cb721bf1cf4bad678c2cc2b343893f9f0cdfb6566b0a14623f601185b809f957855b7a6f7a6a6544aa9456d3e85da043b47521f0b60e7505b0c335a73b5fe20d9301c668a083dcc442d65dedcd77386ef481be2c844d4ae8cda706627c9598a6854c3c77a6e04647e320dd7e887a1b8466f188c363fc31f9fc94170fe55b210d6b80741c1fc746c954dd99d836399066470e0fd938428a7e1780e1f4be40426d1f529d7626584d6934be930bb74393fe6801716ea34919cd327c5849e2da71ee143b1b134cfbf12ba1cb60ad5a49a9d80e72c4de474537bb21d27e0612532e872e90de18a426b08ead20b64bb0086325754a02e74b95cb9a45dba963f10b630d5a15a6e4c09581100aa30b33093c4098fa8214362fb676e06ea4cff1310b4d694b05b760519e5570fb1e522ff088cd2aa1cb3731797f6df1503a4bc0a7857f9086747f6de797278dc203b5f463c6b8133217dde9d1fa50e6cb98ae2b6902e727512fa332c0735b6dcefb3e6144c3756e2c69835c79b29fc05a15140271d86d973cbbd5005eaee72d9724048ca0b05bf6839488fa1e5002d715a943d2820a65f1a58e2352b55fb99f9b5f4e492a12e4c3b0ba06648350b0f240e0ac455abecb750644ec9064f7ce55073192d2ece691d217c3507705d6c7c61524719e9ca3222f1b8475912e4ee159ceb3b798abe032330945a539458135c6ab713e67c7184f0260702683d2a8e4c0b13ec223d61b9bd01823603a25724e2d174eb60681b8bde11de521de241c8dff470f782bf55abf5128ee0d3902cb002c74c5cae10e76271d6d81fd0475ecf48e551628ac9485143393589e85878c1374c6f24e2247285330b599a56670381222b54e57d3c1f38eb00e8dbe5e7046f808477978bf0ada4a7bb7be62b1c9aae02bb7934731c3be8c964805f3287b46a2ee216e66d914b916b130d063616a6eb1fa44d004bdabbe70860859c9675f89fd5978a6a7d601a8a0c6c1fae93419903aa3839e24ed43ecd44a80df420364e7ebe333a295f95328ae6f9a3fcd908345ad595069bd72be9058b281f33e45bf519c5f1714fd39b82e473a66ae4098982c6b7cbbfb7b30d949e94624affc426e449b924d16cb8827a640bc76e18b170fa8cb6548eba488d5fcd4de90ebf8437427ecb2906e65b061d303a29e66dc3fb44902e69ab1836a86207e35841309926d3dcf3a3e6b5908c1f1521f4fb9c34bcdc203d796a8b0be8a385b92addd0a36b247beeb210c0eb8ebc2f48770fd299055864fcc5fed0f37d0b0c8992b6c29d2c3af021779503ed1aa89ed2c3095c43e2860e50491439c463b290a7138035f51df94be4576dfed226544362c6aba12f8b37837952208bb260300e83525abbcba683344af6f7c164c19a472ebe85f68c54264fa30114a9f12395e88ef9966dd6f0ab5d1d3bfea1e8f8e36196769a885f841a27980dd7f47c0ccadf58434f82ca748728cb91a525f466290c34993462d9a71e7a47f0e5bc3b2303be19588d96e1b89b8ce24a114da4334ece8466647034c093855ac8539f141cfd72e89a0de523cd72f2cc65bc1ce9997dcb7291c4424ac7c4e6c2e1a5982d36c6279fc3cf437dd149b5971db863aa414fcd673c84b159828a1e9fc66412c90a70036c181425d6697b84515b11df40700f9d857f1026f711c71ee06e94e86c476ce1cc6cd1b81cb8c69ff05a416933e06d6d3cd18761cee8060dac5cbdde9d9f067b4d622f39e8d3f4c221f343f8c1ecaf2c6d5b9287ce4dd85e43a99148aadbcba7ad20fcbca2908c27a0742b821aa2fa84ba5df63795b17ab7a7598b87fb9b728bac19576f6d9a940b44b964924d82e765fa33ad33924e0db10393f5c19985a36f3c0faa42717072f9e2d9a34e048004167c9dd0ed4f679cc12158d91466e08be8b694994c934acd82e7d452a9cc2b4408b084341a74443863cb6713d2d378627414eeac776fbb3cd793d76ab71abfefdfca86eda324276ce779afd6bb02eb8a6a8f232c0452273f73bad5729877460ebdf9a663ccc4f27e7f559375d252d37a0519b1fff036b77863480d83f0a2b529506d045422c94951b18b221a269be1d92846dc607d0da70589fcbe88f9785c6e60c62f37a940f889d7875266054b07a596cab30915957ea5f201e44ac5595279d2cc7e1565572481f395b24b2bb880e722e3d738a166a4f213a06ba7dd128e1580a5893eab2614b3fbf6d6db963071358cd20bb2547aadf04537d228fc6d1a72baaf4ec38c905c04a770f0b236433df01584455731321862ca75d60ee4b5fd7f5ac9ed4380ac7d113bfda8f65de0d5918109bcf810a3b401340879b4f4e9571b2f5c1c9fb1a71fec99ee75747f6b6efe9a9460c001ecdd2a5ea819ac85a6e5dd8886937de53d55d81ee982bb5d682b3c0e921d8ddc94d7122499950407d112d03d8c9310e6cb3f58e7d31a90e47a6d7a729fbc425871ab26da749446f36b70c8838088a34c3dbf02748996daf5b45adcf85e57a88049c85b9e71f2147ca5dc3a8d2e6a1f84b68bea956fa149ecbd97b822d50913f41f6902d4c58823d9e3ffd03a911d04ea493690bd0d4065028fddd9c3088edd487b5887f8588cbf8b70d88a160cb90aa62536b6334c5dc5001b3e4485dfd4b8de407b1537299bcf6c36492d7b03762e3122d59d2fa0a610e8d4067a0bcb816417b52b34ef41807b65fbe7545ad51f11a82b4a395f498bd87b90915b19d7e1ecba217de937d3f68f85b0e23ca03438a623811b25505499286c3d6bce49b3bb4fc701d5f45d9cbe229b17ec6b901214eea6c5bc2b28e8695a111e9c8e25360b9dd536b29193ee9683ec8101c3d02276b85d7bedc25022a0ee8a5feebe209e568e868b66ab75febe44352b82e8b6155517c89c4442f265a25c38466a0fcba093ca29c4779b4a8886083a39ce39c6d6c836476ca3d497a43c7fc9621e5eee377f5b295b9aa40c0592f460388ba129eb18683a861b447f8085f42df80a4e58f068ed7df32af9078671d953e839581a456cca4d034aa923db9bd42b0723c46198a237c0d18cd6a6481e08edab6fa438b4743cb2474486e4b8035ad6fcc4530c03068da1c04d62c472b823f2f11d7a5e6007c84b2a93c9699e6696c29ae5d0a4937b17d0f4344053f481183087c2bab8fdc5b87fa62873cd53811d911e598ef194408376707b8946030aaf60689f790ee014b8de67dee1ddccfd149e469a9644a4a996b2f8a7a8d8ac4437209bc8df37ecf054c9fdcf2eb5c7cf87d2e477a9a540286e6d821e898f2ff22d5bf63bdf879db340f9dab9ddcf984a0d53f1bdae520ce76cbd19d5efcdf1ed36dfc1259aa6c202494bc4e02f60db1e8cac542162ad04efc1b77b4db45a73604514e3519e65224ac4ca4fcc4156680bc0a7804ba455c8f673070a4c3fe00b02d5ebf47598a635a18099c18da48e3c5fea6421b6711e9d211b437ac2ad60ada11d3968a7e729f6850e60a06c13cf2db1d17cbb4ffe5905a41f877bf454a24909238447cb2ff6f60497aec953c884b35fa82c4496896c765fecfce8a2e94f8d4fa7160d356a1538a0d87e80538f9f0185c38342c8e0b5a7553432ef4185b06dbde434887f6b2f9dee6d0e8bc59a458d226ed979d86a342b2b331133b72d4e89dcc152da2a6952ca3f67ca0b678d34b77dfca91654d5b45e3177b9f2888c8760ad99e99bf2a21a05dec8a82e574b1cb8aec136f8d9c58e1d921cbed385fd5ca04cd98b20440415916e30f2d38c211a2d4bb96e82191f7085c495f65fc599f30628ed0426078bb8fc2a81eb485213d6b9b8e3ef5ebe8d3dd2d2575a11597e50e26f7d32a4c50a9fc762a95f55d07b189f4039af12e4f3afb5d1bf642568007b70d96e9e93433b306dbf826f8340fe257444366729197fca98c1422e64943d46fa4f36db1e819c3096b59e9b24ac4e0e4940943f6599fabeefebdc6d14eb9165edb05f330ac5b6c0e27d17fa953c96a2fe53908b8d88c4cf76a18a8fac1641b9172768f611a7d3c904c7d0dd0a0f7f732dcf78983d1f7debefca4b0695f1c3955a3ea6bd61561a01286eaa89249ce4329ddcdb6bc11835d5b09b4072d14d2b1f75addb4a908b39fc3ba179c6c23d89688d4a374e8fc4a44bd846400cd0e57fe0ac52c3da024e82b94c3cdb084685e028256418cd8d57430617c9c14884bf877dd871c74ae9498bcbfb9173d38722f84602a18575e2f4185f5bdf44c44ae305019471ea44061d52c08564f9259d3d73d328a636ebe6132ccb7e8d6b6841c1aa00c93f177c4a270b52e3579bacb8c37d4badaaabfaa7758afdc594a2d09cdc02f66d89f03c780e3a001b8847aaa2831d97c6cffdefaff8eecde42659dce336870af544a12a0b1d58af12e5886ea048a275e4a25cc01c98d8b66f2fa2b5c774aa18528259788466897d323c54ad109d97f05763c8e2e1c2ccb7bf7fc0ce200cf596c6b8e28ba55666e0eab2e3fdfb6d5ad00dbd1ea6773fc56cea76d806a02ee0d8cdb632039137e56347d69e18b46715a5f4809f6200ebeeb8dff6f6838acfeb260657f58ecc0e0c5e6a6884bba4126c68485ba31f37343ac04e89e863c893f129f3e92a14acb844976f6175c01472b24c56d270116c7a72306c070b99b18822656006d40253b1a7428fc5b990b717c568bd05cc7b11f1c13b9c2907ce224bd32ec89aa9c891999dd2526d9addf0734c78465186b42accea6778b5f06dbc507b27886c515172a13e991a915e7c7f95d181b30d20a864f7b7c3d8614c6dda8e1475be097025355c8a49d671d9b46723200eb0a9658b803676ffd2d575841afd2336ebfc1f53ef211d4043bcb34fe88c06aa0c446706b8b3b2283671eb71f9568e39e1215ff4372088d534564fd3322fd25554710dc387b47f44bc4fab8e1b6c707e9980d9ff268ea72f0e2afb2f0bb70090cd1b0c94b3d61031d64e7d23110b6a846b26cec0b5560eba1d02c03e54320f19aecf6aac4aac952585b8601800acb40271a67fe84c7c317b3214d8ab36dc2728a8135a780278e48de7475467bb39e9b7343ec8f8d08ae9532e8800400c6089778d57e0a12a7cacd0a7dc5b5f880e7854073ff2109118c9dc04445ac2f0e7ff78b4a559008c6ee1b841a88c8d906b00ee5acf97466ec723495241af1ecaa9fe5b87a7427bb9356bf05137f80345092d3fc339b623a826e6d2f28213c83dd1b95032fe22bc7f2687b2c88380ac3ec67ef491b7dc6013b1ed877371b72028ab0a506ee4d36250438335c1f7f3cef52435f7f7ec7495431a1644f5863e4d1a4250d60aa2b5536cfaf7de04c51abd4de05ab98d7869d64df4242174ccda7d22ac7322379e4d19305697244e5124495030241406573cd425cc4ee9bd4d57c033f277ffaae35fa1096f3d96e4eaf36dd302bbcb80f64ea5c4a7978dcb8de06b1824c9cef5a7c72d741f6609c0c572b2a65953f24f71cd42bfc7d29a019767b7b5885e1485bc31a8ae30148020032cf944c79cfd3b100d76ff7c99ad6641b161f3432ca1d70c5db91064f52f86f977e71b5b5f489167e5ec635081fe9584180a5dd21855e35b95e53ad4f8d6b372ab4a9a39a6abfa482a2d19256d5f63b837c4f3188bad3785f4514d00a699850f37fcf53708d2a0c06915286d87adf1f5bf8d355063c7d605860e73e537f15f4806dc6bc08d1093b77a6025beb459624ed64e539ed31e95e8ca5ca440c5c5193455a43012b7885cad713249cb47ce2846c0283a0c490e6880c57beaeecdd5103e99851790f4011afa5f325e235877a5a0bfa96d38f535810f688019782d41a36f358e4265466a0468721eecd40f8193352c4f6aa20432475984255634430b6663022534997844970e1ef01bc69f4f63f2814369d2f745cd6dbb6089657f6eec3d081850cd36827b73b8fc911951ad2b761d81dad57462aedb1102365ad864f5b7559b924e0c086b05d425cf2bf8d36d99cb56add289899f8a7153e27c6a266128fbb88abbb8ee24c4f8f455645177cb1c9fafb5a549f95cfe69970c3f9771e732adce75ec10b16acb8b716e0f22481889b92b846525942a72ba408a11c751b59aa81c7504123e09a4162c554dfc0911ba01c62e4434b542c78fbc35f576520ec9d2115e4e4d5a7369eb72326276a29a5856c96ce2a86635025b70f0ae0d84f92d5a8442bd4d8c797b71d9f2df80ec33f2f8f36823d38a39bf37b77ad434e09704aab409291dfda2a8c01775820ab32aa38020b453c68d7bf2b9dca0401d51182d56bae2b78bfb411ec771ef526ed0323de1777690c47c9a20566f74d9e86f03b33a4613e80c45f1e0c341fa127e0e68d36d3a2846c5b4e944a2da195f784658acea1e9134be42dbea2206f4555bf142020aa876930b272764283095da7fe21ff07c27489516d9dd280c12ab7184c3c55df743977c669580e6703b0f98bf269730b4036ffd27bb32df59e6c8f996f539c046f880a99f85b2602917da7059fe718875de643ffce75a9400f7363574b9cbd3223d7d718cb7d2ec3f3e9fd3582dc4180ea186ede1831c07fe295a4c5288239fbae18ed1d11deab78d40c1217e6bb663fb742dbfd620eb36950a025045a1296f1c8cc58d69f6e99aa882101be12048017edc87860a4a9e53dfab05ab783ae6ea58b237d364f0e1e9c5871484c94ec394d6cbf4da1d54ddf72c08ddfb9f4f0ff393c5ad830534777bb1d4f4cde55f3659a8837f459e5d729ae3184105352b3e69d8fe44131ce64e0cec3808685f7ac4fcd40684646025b97caefd5464f295e8f4e682a74202773e3b15dc9c2cd7eee918a94fc7c454ec9b789291769d0d32ead9070df8284ecf81f0cf91b56f2e79871bb100839c550e1cae97a346dd6c62afdf4edd7e810bd910e48f717c159f6765a5bd2f3d601cf63427009544f0fa6abed4c316d45f10e50afdd501d33afa9e105a6e4a01e5d6c9dcdf274e015b7f9f008a259974f18b9e4d54aab50c16ad33831590e00c96f9d6af6e3c016f14a652b44a327787dbe7d3ac986e84da957cb4aff994b940a922e81ba2eac0b8f4be962e3ba8d554dff58e8b897ec40d0c8f4a680cb11a228bf3105e32617b096bf67b319b64cac1d4c2f74445371329a21c6d403f65529de61419a800c9b0247fcd3cd8f96dbbdcafd730657e651519359cc2bb5b08d98f819158ca983629e2239cc82d13e46631162cfbe46262b1c7dada16f2c36d9b0c520ba9059e92a496226f9736c94c39d266c98530f51b2ae9cc0f3a633011e2d730a6a22563b272306dc398d5e52ac580310883bb690595d3957542b4ed3a711dd780585f5982df60fa42d3aece90db5fadd375f12e988d65ee80b90083614da7298a8bc39ff1d9d918a65f16ddfe890ee74db420f8964ac240c120e59d942b0eff6f9271b56b23cba943efd4d439fc41da041294b8f263f3ecfc14298cce3b6b22fc6365067143721287443c2b33ecc42ac551b30a3f5f00a75410a94698a6f9a87829e8de2d9c6b2564e118ada4c223d8f44b0cbf3f934942d02a4d4d0511a99c530618cf69b5c9359f826644addd9270d0675e06fc5d1ec8bc2e480762caf181a47ab0dc13a95462d5cfb93c3150bce59c9231b7ed5034e647ef9d4b21b960d0d6a898bc528551ce0fc3a027ffbc48bb6de3167f2a0a4e9b34498774b8f03c032f46591f1e5b8e8900d1bc41b404b1b756afce3c3136dfda82d4cc1a5cd7b319a2b314c33790d4b9ac47c22083743da1428b803910891909bc200536fc5ce2b45df325e0facb0864356634716975bbaa784408a06219900948d87d54e7208ae128e29792e4ea8fd560097330308a7e8ef08225629fd84ca6f0a92e96c2178e5184aba21dcf7669ea49a2722e93145ea34e90816ca4306fbc759641a882854e50d0a4173a06cb1d40bbb4af82e278933a5030d220bbb944100b3824a05507ad6dae71861c8d38cd42d68c62c7ec37be50611861950bf32661865fba32df5c787b951a86f8d775ca81cf594534503a4d36b76e495251a8855801b0d734579b0f8bf4725a16508503bd16f46afbbfd8a31fad25ee017ce43b32651569f5b26d3bd9e4db1354910b8cf51e40040709681115b9a05d8c582ada63aa48b8ad53f186f716852eddd94ca08a40aaceefb10a4515d9930277a5f10a19cadad1eb3e9f5930cbcb2879ce29c67a6e08fffc77968e454646ceac21dfa2704590027552259af8edfbf5c4bba74853c5abea7f7b512872d2c338a0bed9bd85de5a6b982f51d10903e204afdd090ac23e4391db273bfa630750ce748308c5428873b92858a481e771b90ace55c5de1c6ecd36def60e73e43491c9f4c787889a619d90a82298e3b0969a88a03ac0cedc5344acbfae1d3d1b2313084a2da8f87ba1d6f82dc2de50c66cb43c0291c5c8cc0e7ec135534e3e26ed040915622d22c39f7dcc0a26f137b6daf65acb96aa95ff2b50a6c7ffe7f2ab0954ed6e923f1809f872857f44e54c264ed7732d28376d9d4a1a055007fa10fb923b28d259eafc47a89e9756ed4357708067e0f6de7c969dd6200818def3bf5c421dab786992512a3939f467494737d48d0c0d9c79a78b7af3c2252418475ee41573d6fc47c9a1801ec50ca36862b1fe984b313fc083b0d0f9be92476e760416115bbd4bd87da197e1cd83874cda87c0797136c5e0a578e55d7e71d8aab97ffaf2286a834db791270e217c0d9fd5f706db36e8f24d481e821f17ef33dd99797e3674fa560b28a1f38af89d1312020f17f146a36c1d23dad75c23992c613a9124d3e5545cfbd3051b91e55db54b1b40bd43a3f8fd9493dd1e3d5f6e48ed5a29bf429a614671f9bdf410479363bf5e4a7ffeb86d3e013628fcf8baba271c94e1bf6e1f16da3d772b39d75304b746201a950052c08f9e7d04828e86847d186aa1335601ad0204e836bd51adb0345e22508484b6cd3cdc9c0950916e318c33b0e74e7f0de57678c8e712b1c49ba01e7238a15686ecc70963a38636c5631ec7df0b59b85d593f2817ecd8a432bf6cc6caf9a9729d7ec93af35ea781b68ae94b550133c35b7da0c8e0f8f4b0b2ea4eea7dba7d39568f2acbf999916ef77f3d9de4ed73c38969bd3675a10c04c9c8902210671f88711973785f216f81b6be9043ca3144faa6767232edb5f6bfa7963ca3d1c9f3d5d81b28ae4d5cef5e22275433f5b4ad917a7f211bd0cb08b0ca7663a2153329423139edd4443597871300b00019ab45225fcc210cff1104660cf48339124969758af975e8a5e7cda4d37e4603270c6985d5facb53334a215b520a61802926db529552da0df2c0a1d2463628b36857e1e1d94813e212d98c984013b66246fff8bb094a59fe4bab6ae159bdc900ad4268d2cc7465d74fa49cbe73cf8261180d1c5b71f0ed938ae74eeba71cdc19a4a5dc0028ec16553fba0bedba34897f73a25aac2a8b7c79fe2a7e287395812d434c3bdbab6b0a81441d6495ff76dfbb55319af7c93cb4b6e9257b095d2c008844dacb0825f6022fde172b29b9cafb5e2b7e622c789d22eb73eb78461043a173e2b933e28fa50ab224578da1dbd6fba8d219dcb0fc7d15a2c1acefdc32a07dae30f0177b15265ec84bf153da268cc814640642cf9224341849e9cdae095a95328ca43743242924f9c1c61e924c9b38e117ad34c2d78e90c5fef223119947ffae2460a98ffda68add68482ef2a462689a320894dc1ca21505b993ede407aa849cda1d073298c966dcbfd935170382c4d38eb7a72f533645919c6a71a48628a91a831da57ec9c9b76225a00a47dd99ff7b93046e2f6240cddbba497e997091b103c0e144aa447641422b238adde70758c94311ed098c55ea92b407569cb365a8563e40474d7fa959dab10cf06a34392bab28821b43e00614fed85356c3a1aaa55bcf61260f3bb43e93d97394845a885e55a0be27988c2c48ae0737fd175fb38598c3463c62018ea43c5272098b73249ef7c4befe1448e86384c893accd48514f76da6e391d2a91e7cf669eeed8971b22cf808d0e20213299cff0f6f5860071ef14a431ca3c91dcbd96acf168933efaceee2b411c3a05215e3f23725e70a63ca02945f908726579ca97a85d468d75945e883d9106a53dd93265564431b5747abe0e48d0cf96782189dd99e06ae73e4fd26982f595c6ecb4dbf3507304c1a5511a345be9049f10fe5390c22dd52d2aacef0235a87d1c5cd7a8da2825b12d2e9bc54b91b6dd1857f9c7963d8b58f6c18e05c073a3224ca1d117885691f9458d3f292e54dc6fbe542aa982b9746f1788e8e0a318e7b0e24c458a7d2389646df1fa468e8a4f0c275e5949e5535237c57a8ba7699316ceb1d407f199ac0893679398916ef827ee280f3b6e8c027bc8029ae130d35de8f4ea640802cbb44baf51c8c9ad262720902dd02424ff5abf0ae738b83fc0df9eb46d4e17ea60b3b887a273aab899a30a0827ca0f79e0b8bc65829edfe9119cc504a7c2a691d102e74efc41000a039c7ffd13d6267262f82b4c85cd8986f274dee642e56e0009e97e887ca37137e0213be7c5c6d16f771ecd40f15f69e95ecfa5234cfee5a426aae4cacba826f1fd7bff17633cd826251768fb6b06f0dd44f50c599497a60015dee1f1ba9c3c16d5e3b7cdbed23ad6d7bac65004cf4f9613977563f6aca206135003781de7617aa53590432a14a841c10fa17d3b7009dd31b0150058d569b0d735430ca428c4a00e98d7fcda98d9afad85c07966b9d0374b0c38453bbc5c36c331a3337a0bbf23730783a31f860fd166eeec27db2eb80834f5b84625e345207427fc3d84633141c661b4489054fe0969d61408c6162f6f513c8160516b1aea89ed26516407d5605e6ecf9d77044c1da1e63ba72d49aa19b49f1e4844c70e0e212ff9467ea48b4a5d0760521d03af1081b83fb61ff05c664034c70e4a230ef8236b00ae89c80e62a7051c911d088c2abd88ec8033522a68fb87db47dbb1d03ea16cf1c1c9273d99ee14c80ed27da91d1d341b0c78d00e1e36723b956a09d4ee7f2eaf586707aa9b7e44ba615d7763ddc85fb3dea804fda11ffe45b0703aae2efd40b27e5fd01f7096f3dd9575d2325397c3e0e09bfc5308ac41b21ed448b10a32823f2614ec147dd003f97857224a761bfd7c87fc01271dbefd6b8b63831746132883b6c432b878881d462f8c64b907fe23e234ae8611747aac8dbed0dad83849d648f99cd6b6ea21ce5483bfa8c6a4504021b72de308264694001579647cb42e6a66a07277a56b8ee3a4c50e6cd4a65961d14a67a7b84fe0c7313e5fa2c4c43d738380d8ae5e23938b361a3eeb4d770fe657e9a198e0c4a623c17e337d63e9961024365682ca5634ae910e8e0d2d495300ef162c670b78368f3027a87398e2d36f36948c8313008f4e062b85f7bdc560c9a66e1c91ca9de004f7b1cb38ac571f9a81c587204b47bda16e60b7d644c7fee79f66f2eab53edcd93b3bcfeae6db6dbbb63c21704fb048c8723129d2c40539d862e6f37db337df49d1412695eaf3ed515111b2a4aa0fec3dd429d127b449eb60a25a9100f1466ddf30d9c76611a89e3672b56fffc605839890635541cfa5362da2ea532a34eba9b10c9629fa14ef1bc0be4dada92d00906f811ae3028297e1fd156f84fe4f0303bc77217be53897629b1142cd96c9601f57e6725472c0b3716c2110e1b20824cdce5ec02640fe2bedafaad274dad4bf6aa8d662b45ac47cacd2d74ba53d8bfcf92957966010e825a1826df349107d95feec3cff04a335d96639134e7dba1606dde82c2ae0dd9b81d1651c37ff369601587a6c53fd5cc8c38adadfa2ba43c934c129e653658372f6ddb0df3e33544caf26268642839cec97119ea1bf82efad609ffc42b8dd46d474136a7986eae95973f7acc1acb8195a70d0b3596d931c24857839abe40c452e390bcf33e975a4e4255d487f5f173e56bc05db85fb98a7c8069d4bae97a950ad51fa6cdd788a20e4d2afa788191c2303977955ba1939e5c8594670690a369f956d10cd266b1409f0ef1b30592a01fa2a6b777e25149d24de86446102c11584e61e26f9d37b09f735eeaf68607831bbb16075211d896c5d27cdba5f99482edd822f44042ae570fa249e19f50563e056e41cb4f019935c04ce1f478ad260569b721afe497856c61af92923c2edf16028cf65477fce56724b13ffc3d30991e89b29f101831d715e00ef4a304bcfa1a5277a94de16cddf5eae3c5fd9ff562312777e04f4ed4ea5243179fe5f44fa3e64f26df2d1df59d4f73bf14b82f2cde5bb9c5109faf9eef0893137224a2066a6f9aea3358f664f383d1b8b8cf3856f80334d3958283f2d7bd74902b28f1c888b74948448ca1be95efef9a2ad0a18c57fc9759c6fb907c41c89d2672f12fc26b90b376562288e5549e95aa1ca447a245d842f19fcd5197fc771bef00cdb6c4a56854b006f6448cddc84be01382325e85930ceb74c852ffe206421747bc45ec856dee7e67c952a78c255b9e6a0f2bf3248e97935a65cabbeda869884bc533a94c7dfad72406aa24c37c80c23419fee28364b0837c7d2156c3a50b051e2d1d49cef66b9d7152509b126bbc7d7801b27c515311175deaa8384a0ec2e3e715aaaac39ed95d7de7ad128ff1273bee0d7cb3fcee70f3819dbdaa1339b41e995efaeeb5a6a65e7015e6ba1becf7bdff67973b606b5c936b6245d26d0af47fc746b53a9b4fb3b30ee36fe48970febe382db3d734ece3f7d43d0c59299686871cec6cf7e09401317d21087284f384c9d9ce30092db0c5fff2bed078e0343c35b3011fd138de43dd35567822f7a422476a558a381c231bed794f72028b43eafe664ae5394d4db411e5203c5da6a32658a444b4fc0ef4f3c6f661e73f1cb09048b1557c8fe6fd5885c8b7122b8ab494329495597b1e504df7e399e50ab21de2ff8e0f6b6c60d792816273432c029b431e8b17a111a7a3c1e759903671cb8e1ab0d924d294be502495e23b5efc941ca720e1e0bbf2f56352ef8262f0a62f6ed87d4629e4fe09893b676221de295afca56bc3247eef64e262c9377cceafec2ca688dd873c4be3db2abdc246a28c7cba2de78675d7361d76c840a651eecc0a2db9ab4e2244c53ed942c3e2a3ac6910a710b39cddca4ea2e5c52b2bc660c2568a3c7583060b5a38bf4c7f3ea190ba8dbfd33db917b13c1bb45217b54019f53085be7e1de821d500a90c64173c43782a49adf5f5181c80dbb7f41c1f9c9e13468140635d8cad9f0486bd0a10b1ff21bbdae377caa8d3e764b8b2439b38708578e672382de17b17a20b7a7fbae90e62080a143073a48098fe0e26727a84741ecfc5bb473b59c71fdb84f3caf7b6d1ccd13c68f599f8fe40efe26871f641fd3c2ebda29736131809387d833420d65c2360541096242876668f6344351d469ddd307640f6d2f58d48586c90cfc69340d7758d3ce9a0d9f2775e0835a6781b6806871a5ab7151f2190168919d21b95ef3ae8bdfb28251d50d6650cb079bc6e248c427e34a7366e71cf048584a90a9f7d185180076d3325f4f2c30c1be23a48385e12c2b3af8196e47a183e085eb1f02237317a52f875576d062d2f0e9b0603350312819bb6bf690ceb789319728b7c045ceac783437796f38b75bda27b9a3d581a34c3c5d6f41b982b017a991b3147c019656e02af822f1b05843ba721b4ab531ac04c383c3385c0a76160c84901a579db7bf4604a20da31ba8ef88886a123fdcf95048fc3a184234c59a17da25ede799bfaf9fa3b7e6687dcd87048d5e92ff39a229655c0d55aac3bcc07fa3684e2a357854d0e4bb529d7af08128fab5def92e858b01e2dfe27552b3cfd03c25cff80c77490782cb0d657141537ce0da170c5315a42671bad21b472d2c6ecebf0deca0596b2af5111695b398143c4c717b1bebb7ecdffdb65c62a81d5af918cf6f0d58cbd655da5c82effdb54960ff63364658aace409f922f2671e66358953347a58481570d3c0edd9a9e0a52174873424555dc29ddf8b9a5594e1751004d016ca8570be745feb7ec6b0a47cb2cc79e63af02d06865af1ffe14576f562e536f7437cb70010b23ffe718649584d0507b4f63714be9047529e808aafb75de0441980c5035a980530c3e38886fe823c110e0b10f54a8cccbae674f1126abe51666a91d97814a6970a32d39ad5f76d83a3319d1865bd14fa2fae908edff19564b2f6d5632d9de0e7c0cd10471a0196629a5d61e99b09c4664f07a8320faba24164460e1fb4e263a4d2c8cc356f51a08189f95e2589db59ce086e884e30f5dd3e3902ef04d632fc530cc4479f8f166a3b8d6634c17c6684b2b1ee3a6c5b7eea07082fa4c2199d5279de8567161f48145f7de3af54e94f47ebf4c066ad4a57abcac1730896e23cad79aa8b5f8b57e4aca258fe438d6495649024f0e308b376744b5c662a6d1ee95c2bfdfff2197d486988608a2aba9efd1733342e704ec0c42c80ca78f4b6a4572106ead8947bcc8c01735d465d4c4b77ed1974cff9f3e28a04137b0144b52a7efbcd81192855b8c5f3b9ad29f195de871c4926d4a1aea5c176ebb4abc54bcbaf0563bdcc26bba15a58f9adf699d4e5d53c500d6f55aaff687dc70b78e0e3f762e240385cdd1521f13bf5c2d2ebcce37baf8b5118e905d15180be43f3d831c7c91ba7674ead6eaef5a18be999e09bd51f94156683f40afe48e87b1d1a77b02247268374ceef84bd1cbb8313c75d3819655e66fc6f8d522b94a518cf068e4c9f76d72ecd5910c8354f7d71bb194007723d16be13bf81c37961c4f67cfe9b853ca7d350806a6f1c758c47573fac9da52d46d6675e361f698d66588260358a4d918f59edc01488f30e8ac6ed7b5af8951aaa062771fd4dea41deca000c2eade0a404df79f19a2427905b1cde9fc72b0ada361dd9147a168e2fa1aa46304affa74a4e6cb3a68d3b9db3d248189a350a6735677869ca4783bd1ac7663545ce792b1dca963bca87db8578b52405837412154a84929027719f8f84c62a2933f452f0ea85131a970369b24df26211f74cbcf37f263ac32a22e128d80981151c435e4a24bda86b105873e3d8e660a8cc23e1698e8a146a5005f2054e9db911faa763fcbbc0205c86ddd23e47e91a76895bdf3f63192ce712ecf12c6d714cba02ad6b7c3810066ceac7da83c1ca30b34386e61e18797161fc774163e909b6252152b557bf6bc036f319dd7537f88b4358a241754aade0d866bf689c6749151dd9ebef34216107da9ae61a0ddeca5d3eba7ae18e4c9653d14865a645973e5546bb530b430039486bf4b420373355953677a5f0500acc856c816a874d831f3ed858e9c1e7d3e2a00f06b475f363a053e6b846945b6b53e8f0de71eb37a10d49252acf174a529d4298db602619e057e041044331ad64caadc7b737898a93849bcd1ddd96e9f81bc6d207dca8e6f1f187a0437101c8d9175d6ad37057a3fe6a7346500126370d06b8c1ec92aa651ea3b7cd17ec37912f542132d448c3024f53e8d6d47ea321e98914b0a7c394be700125276e155e7256f3c532c0fd5d1a10ac14401137a776be45ab08127d4b805f9c83506636f4bd64732f9875ff0bf5a69e0fd42935c062cbbb816107e9ec3ec6bd5688e4a9b414ce28fb04b31f0be7b5fc48b80d194ed333673a6b9a938a8260dbda2567726e1664fdf581fa5c24a0ac561909f5f2f03d204133b2a38548f56e1d750368dbd4dc1cf3dbe246384de6122e22e2e022202fc7b060ab5494bdd6a67f48383b21941f18d376fb4099998f7139dd6aa271573919b47466c16d0cad8580d00cbfbe55fcbe13a0efef82ecf1bec2592070cd366d9f721db3d9b48315ec9205aa262e35f2868c4e33b21a0f1dd016ca436160d6690116086294f17ac4c9e3f402dc04c940d509be40d54e10893d8e96a77eccd6ffe7a6b268d4a1a22a34d1d939fd67f525cb21b8d4c60a09a22470112fedfcaf8deed7e963597e885c8b099843db0a8bcb386dda43c9f9961da5e68b1582d985830e6bb5fc285a07c92c27c4a4c537983cf5d6e10c1dec23d657b5d612ef17650efa05711ef6c6f124bf152e856edd78b2a0f1f0a43c88f71e1f15f40102d9bde3a49a798fc85eb214e8a0f72cbd3b256d3b5180a832cd500359f97edc354cdb90466611ac6418ecc6e40c4f40df5eb56f5052e000346f7505e3fce4d5d18ee27e30114a96b17d02b1dac569fb06f4cb33b85f8352438b93d9d4116ac376a3d92d93ea66c69c5440945f74a43ee38cbc3a5c2dc35f2632d6ec78ef6479916d47f768b0fdf3715e402be01bb07b85e62bb1d8b466868a89e6a83a8f1bfaa8a2775453530e4122c9f16df2ef24f77b7a986ec2975ddda3678406e06d0ba7c525d6610e773c83d623979fe9cd23b00d67f9ce87f1cf2dabe327792bd225d9b3ee225fe9de7427f6c3651910958ffd13cde55eb05d90af8af11211745872a595e0ad787b6837055ff53b5881ffba8833a332ae57b13721815a47540549e03a9d38b6b89aa6a0071439a390c3414399cc49d0a27e33259696df06ad09fecf329718eae36f58219823d8f816a0764efde7542999aed9ece46319b876ec8219c4bc8a79750b6e87d02623e293a63b038c50656abf90098de7d1667f0569a36a5cb19f7f2baa481ea8f53570bb0d744bf5ec6716f07c0a00ee32d835a428ef30e3071c9b278fc19c4089c1fd1abda374c939dab7ef38083315b8ad9676fc40765a22c727640fb24635b86da0e5b9ee8faeaa1811c5152154195d1808354ad9da2a5ef80348786b0c482c7aa44d81f39781ac2e9e87616504ab229033de9bbd63ae5ae105b9f282ac1e82a3251e03b2955191efed4060db9455710c618f92f4f57f512e5eb8aed02d74d71bcb11c0ec2e9c4e299a8ca72c600954829c32ea7f67f7bd1e1e1f9a9844d9614cd8f5b78da4bc455638707a299050ccdab95ad4d2a91bd9f81288f09225871a358af4ae09ddee54edb7801bd51ee01350924555abd312590b106755314e86b4c6e4daf14fef4d4cc60e7f21827bb45d5612456206a2bc5059010ab52ba3ab568fe9a8e1ff303c4ad6a6ab38de4887af7107b9cd2408d83e154295cf57da5500f0beea88d89854d180389837902504e11ecb1f53270de9d5811ac4224c074463702917194b2c822b8149a4293c1f02c82a688b49a1eb55594927b0d051c06893720d4749e07a417403918c18c2becad3010a9badc01605b18c1e5e37b22c4f508af01ab719701eabc0a7489c5bdd0ad4e9e55a01bdfd295ad85128f288092325df61963020247e7816491842902553f9e4b05668a367639fc7cef98cc6afd0164891bdea9c9c25d386c084dfd13651c7ea16022db893f873e5072e599b55d627f513aa95bd8b1bea372c32893199219199753993a484ef18e8af7930c893b91c5206f254dbd28514a14ff14064365eb1449bfd23165e47cc87593a6a84134292963c87a0bbc8f3e0c12a3ea649bf499b87ec60845c44adea44009bdb273fc21d236ab7e01e0ddc267def96c6474096a8afe1a49f55a2da00a0f30a3fee65047db0d4da64ab1153a01b7c9f7943c0b8e30df564b149b7685a617388d5e2fe1564ab3733c899ab642e91da185d424122d84bf410f2e9e79064e70497869ef50fb9b9d8f463114c403296c93cdcef97415ce4c0a0c5a635d388547f47043c623f3abff46f9c14ab9b5f1c3357198c456d2019a69f448e52f532d8ed2dc474a96522ca1656f0a770f6626abb5384c437ee3857b212b3b88aad59e6b565d8d47b6b79543ad7e0716d3cab2907f512b9069d5c7de3785e068ce7b312d1cdab4765348d658fa6f539a5d8a7306517c8d37113868461bdad4b1f32a7919ad25eec686b1f8f84cea836856a94a07456a51ab56fc96a91be026c15dc5d9c07521a50582f99244e8750f9b6d575e83373dfc4e2605ab5812c920b9a643133ebb282cea0334f71e5437d98da1eddc0f50f36ee7cc78966c6aed662be312c8b1ea311f8ecbd349051c66e0229b54b499a77402f6a3a3f4149e629eb977c58ef0c0ff87f14c8a904ba219f297141efddad5617102021c61a5053ebae4d9c9e40123ca730744ca68a357114b0661da17092f2491aae12c8b9c8aa72d3327723554fa5f75c7a5be608e7a5674ff534e9e82607355abfed7b234eda1a6d6ab9ad3c35814553acb070dc85d0ceb806dcb0f69f71ceb8ce3878d708cc6b848e8319275b90b83a1a72790c117f7ccca65503c5ca178121a89eb47cbf575a7b7388b6143e8d551cf57c085590ad40f9ef56f84945457140875b882166d4a9d22e92a5779fc880595cdb47265f5ed1241bed76eeda69e00eb5d61b272bf792ca393bf73cf5b3a51646851fd5af9f3937981de9abfc75a7aee959e9e2eaebabb5dcd0316e0a7b13ec0a24e67e94cac4375acfccc01cc7496e80738c5d8f5fdc40381d05b8bda1e770541f9630ba1907cce783756c9803efd43f977c8903f59cddec74cd972bea9de860aa5c18db6e4b47e591b265d91ec7d476950bb3fdfd64c8022746c7366a060b451c0b502c9a9e3df5d74035bccfc9d16058f2ef1ec2f5c15c3f966850826cd7aef05814aa51a7918285702938a996dd0c643647b384d4f9bbfb85f70209d4bc1cb9a7b4100c31d220203b88d597acc1d6060e7b986c1b111f9d22a211640b5b8c0e4bb40482e09242ad3c80d6435d2445decffc44d03d8053028147f33ceab88dcab9b78592cf697893df46af231629a8c8b6436090e95ef88b66167b75c7b6fe7ee52edfe56fe2c97e78aaf11c57d9dde7d9c52627399313d27427df2885488164dbbd231b56761df171e81648c14f158de379333e83f9b7e1501e67ae5bdc9330f4d46fe320cb49d2b055d2a18ebbc4dcb008b9e41f1a72d54027cba7687e4419b658e4049b5726eb240e060cf599b58bc06966d5941c8ba904ff337ae8fbfe85fa838065b99e0a049a0b38d3bc5086095ee0d5a298d364eeddb1cd93fc380ae6764d2e8503201393b6284ad90ab557367dae61bd08b604d2bcf81979fd4a643b68764d4c387022799fc7be21083eedc7134f643312578f9fb2d2e6fb1598eca608ff3cf9a49ffd41ed1e525077a9b4f889692d4310515a4cf2c3b2379fd533fb12cd74ed248703ebd74bd544da6b8bfb83f97f39236b916760f5df44423479df36bc6c53c5d4f03c054ac0a85d9076444e85026ef7c4888c483b2c46c010350563f5381bc58b5af76f2698b0f52702a805f1a3cb9a7c5e423186f93532c686d54d9f39c0122c357dc5b01cf2b88b2eb8bc7e18576a30d46308c46075267bf043ad3aa4606f8ecdb15f860c69d4133c03e42c1775ee5ce4c748640e859a20502e97df08009733f5f1e93a8a8a959bacf20cd4b46ce47768cfb268248e00873d8e7e0ec1380948c087419910717c2a385fbe93b5e206d8106729c2b7e6e74896a472c953f9a2f80c82f2f285d1725c272ea735aa949244fc9f7005d2a43d23faac8e788dbe840436f21df1a5c0e11e16159ffeeeb6ecd8850d21ea0187c41cf27aec5783b2c379a04cb65a6c209337ecfcc5433d6df6ee600156b2043b2536eaf00aceb28ccd3a6b86ac0f73e8c9f407950221f1f77768339590766c967c617fc651e0ba92e2dd006d0818e1cd3df05a29d5cb5a5e5da07c69a3617f4bd035874718f4e7b437e20ed2904258405d2d3b1addd8f0c3e92507970281cff5b1618db7ad3a6405a868fa739c2969ea9864d8f1081d203b6628d50f6ceb2c26422fd5920974cb92dc8153227eb4e482bf12802fc626fba616b9cb30cf3f087ebec37f8033ab4bce2cd1e0c5e5886cbdf6fbff038784f9179ac2800608f0175a8ff5745361ba60d3835273e0d1eee2d1437e2f7f8fff57be2baf823d78bf19be36b1a485b84031c8075097e19a71e8ae80882dd92b507b67152011f2d3c737bda76db5d1d316f01efaf9e0ed0fa11738f5e8808e08adeca6ef235aad17838e0bd7cbf4b15c53ec793286025f6fff9fe46ec95637e1e095e5dbb8f8a115ca14baa15e373c5816d386c00acc88dd1a3e472e4692b1960d082ad5e8a062a953ae6d220529e4ca67f17df9bc69cc2903db8e41ee9d7ae41d63d56bc0dfe076790456f18bb160b7fce44df852d73d6067791eae69d6e9b06c0d8739433d988f0fc769f31e2c298b9ad0db0dfc0aff0a22f6b3c008d09c906b081c7131a886c5941c2d9e79c7db5303ec6e504c7b7e3cf24d28bfec4aa12247af47f3d5171d5d9dac0fec0387de709201f235138832da48a3432ed961ea72a140420a48a820c8676a9449b62e2cad85f63f64e779ae341898cb722d6b5242a1cbc49b43c43022c895d864a772881ac87423db7a94fff7df2d763957156e1b0dc2431f6545b9603ae4ab2838e0a5d7e42780ed6121e4548ff02959e8ac6581eb102060fc1bf5738c7321d4e5477cc73ca51388e1603e00fd6580b4064561c004a4796ce7c0f0d561e416a1a48ba26a5542456b4f46a38490c4cc5dc4f826fef82c8eeab2aee892b78dc7de1363c86b019d5d4817f067eb7a8722575ded07e5397b727081a1c00e90388dc9ba09c21fd16b23ff72a1fdaddb012e174579ea4290d4709511fe0572de5df6f1beba6054d3e8dddd03342e817ca82384b9c487db5ca88a4e1b73fdf500dcde5d47a419419212a025ed51b6e64cb18310994d6c36a95b7b8e1cdcfa4a27d917ac8f10247e40a03f56497894bc86cb3345863c7ff0dacc3f602c0275e46e4c58d126a59e171544622cdf587776645ce89df8007a086d9f1b786b7c50040e4bceab847c2d9fac05bea40a6848f6ffebee3fd053e30aa6803d0e9136b22639d1bca08785ae8fda927a81959ea3b13c84b7081afe73e1215c97bfee0458580d59453c853ccdab3447149d260a9f93f960aca9e5949f7a2ca6f7aeec4c8769a2f4738d79c632bcc2ea737ef4482cffd457f0658d4ca8874a7140ff6e76d9dc05616ca4ce82bd05b7402c3e18103f7e21b1db179a121a823043777d7ac698a8fda28be778df475629d4e64572b4ca5061656040ba3ba79cc3299c14aabe35baa2b457232c12d150a4e9e0d3b7a4fefea3e3815fe300a090e6c6be04e8fa511f7d9a65112d3a48e35fb97a03077500ca39f10dc249dfaa7ce67edd05501135aaf30cd2935eb06de6f204149e60d41099dc619a236b7d20634209c318138f3216a69a1c9770e4c3bc0999b33f23c2be1ac6cc98e1f48b4da194ca9106f257c8449dcbbdc525b7023261ba145fc4e616b1f0094559b7942176c7824f3969be8c09eba3fe4310ff76dd5ef39b018990db75217b3ed7577815e1b96ec5c271f1bb8d6ff53d859ea0c980335217e24324d4ac1fd5581b6483f199990c6dff014f0a6c7b46ffd0fdc1d035ab33d595930dad53607e5c8dc585e72aa45b8863fd60f0e78af06e43efed81b58131b6b27d4b4fbc55899a3031d56ec65d89c91a1060a27c1604690d718705a8e83f9457e7f9e6c1d0803e0fe7bf2e049fe9f8fd2464b9b249152ca94646205860574053e45755a29469c4cbd47d662f9567a29a59ebd3806eb9ab8f7d251278aab64431b924e4c8200b14aac6889946559966559965f7ef9e5975f3ecd975f7ef9e5975fbe17923165d08636b440acf811c9164869436bc3907c7d98ef67aa4aaee4743a9d4e65792a4fe5a93c95211b96655896362c4b1af35f9665f9e597effd7f59e2adafa0c39e96b021b640ca29eee28fe1644bb2a17b5d156c68c37074981a86a10d6d68c397ab34030c2349154e766fce7064dba8497cfed7a84d4eca904206bb94fdc514a394d20bf3b277af4ab9d9a3fb18dbae05c7fcf0eb303fdcd590922dfe8b81b2cf2ac8bf0b5444d3ba4e52fb730139ae6c67cf9bab0a5384385d40eb77398b4c8395bc3ce62ddc2bf71572104eee63c67ac4324692363732aca6871e3ae9c4c483003129119d084729c7711cb771383f080cfcd1673961ba143e7fd6e74a702cda86ff681adc4731c5077a0ffc89b6d1393c98a61a8073f5351318cd976841b37eb40d9f4247ac29dfe3f09e3db9f3e03931beb66aa7b40d1ad2144c01838ebc851df3cefd4ee5b40d6fa552ad9c0b7b8bd56a1b1ca6651bf66d4a350d7fae33a59c06d399985eb7ba065ef87c4c42071eb23c4aa93dd8ac1437e23ab57d380ab930f470348249c3075238bda0280f3dec74b84dcbb0e63167f4f0416524b9234a29a594524a299d18e7fcf9fc87a80ae35528346766df20d8a30898a04b8f7895af4d05640a2a910f2158562511f742784c2d51e76f673b140d8ad22d681628ea0485eae137fcf862b22ccb2e1a97c025a2a64b494b391d3a0c8d6a5d0b0fdadfd741cb287d8c7eefc0ec077af8eb073ebdd8fe3862c6d829a0e60a35572857425128cce650da36f8b9cea6c53eadb97ef02b07ca3f1c4551144551144551144551d4bda19110c2d0462770220bd4a3380c3a800043eebf66cb081530c678bb19085317f1da22d9f5393add17395618c3b21a343964e4755dd73499ae1d95da1db9acd4e693bfbd817df695ce15ad533401840483115a26922a884832917600294b20330048e43967dc073d938934994c26934966fb81c1efee340a72fab6ab967620d6e1f9f953dad54c8d408de3691545892a0999836a728de3d829cf7d918c580a96923d675b1ea198353899550c804963219988ef68d2c4a2f965d883e1513c67005a64fa9d8f233e4670a6625471f538f2e0466c15c2e73bda862b691ade4614610a57620425aa2528811c57230e57e24a708cd738625ca372857d52366181a1cab1a9944629f574321319ec548c4aff1472a5c9b142ad344dd33c1d4f87b1bdae9661d85e375585154d581bb76d1cdeb86de3f0c66d1b87b7546adb36ef25b4790c1bd0cb5ace89dc14881e33f84fc76b0ba2584a3d218212726a624a8245b1568cf1171983583948a886babc94fcda637d755d0dc5ba0ed234b80e420a1264073a08d28320a828c49448ae3872ce5967d5461a83c5b410af61ced42fb848824344c6a1f8ca552a8ce17f956a6555f831ecf157ac93a12efecd64013e84593d0a93cf875059b6e322759d7380ab542a95b52fab8d8806843585aa6e0200838cdd8729ba6e7713864c43eee59c73d79b5a2f16dcebe14904217e3deddbddb79b826677d3a7d76317c83eb6f4de7befe6850c7eea67738cfd3f14b04891bd2f640a81ac83c5f5ae8b8b32e79cd66698bd40c8321b17727d50a60273e5f3b7995d4c61b6ecf0813cec1235c3de5a6b6d6685ec9915727337441081ee8af495d1227fdb9b9a1f993e58e307ac0f9c2b1afcf3214992bcf7254496aebeae797154a4722453cdf615b95ab739b8901ebc5f6b9cf93db6eceb532f5d7fbb52069adb83738b1c201ea20e39508b38ec8b21438e5c6ee370d4dc9426ea9b3d67164eaaf03172a2078d691199fbcbe15199938cfa9426fea02e8c658a1013dc6863e17ae374fc9024cbb4d30fee614964ee734b877bfe3fb8e7bf83a344d091e70fc2f3f9c10683e32ce0f9dcd22a2a313a1ff957de07d7e5ce07d739c863f437b017f58ac6ac742ddac7985ad72201ee57de022a1fa30bc2a3d279ae9ba20e688a3ca029ea96c3b4683fc4a7817f611886611a913a52977aea247d726fac25ed63b8df3aedb5ecbb3522d5544b602d655ced50cb6853ac9a6a69eb8a700fd6d2a804aa715243b4711c4712d7d295611a892d79d552b6d512564b9ab6615ab66d9bb665dbb665188661d80f2e9381190c06ff055367b2a9c5bc625ad1812ec568b46dde8bbbd490c18126071ca81986d4596bd79e329ece26e3b99bcdba2b32fdb985429a0efed3fbb7d66b1c845a6bad1d155e593aacdd42097b10dc6a6152eb6412932426afebbaae2773c64a53355902164d7247c3224f56aba80da60daec89ce93cd7f512260d876927d3f3f7c6a2499324258720ddbb45b65ae410414c1b4d360de736ad5bd98f16d96a915ce7d2c1c241692b8c1285c17a7466bcd036b6566bed5cabd4225b64a9d52a795a2d4f89eb783cad96c78343c7699082b5d68a22c639ff6b1fcd8de7a8a190e7e5bef7da720b916ba9c1307cc21fc6e00cb75dc015007cce8217e459679d2dfdf88710cdcf067bfc36d8e3164c9ba29aadc51275c1c0206a218af7be84481d23cc39efbc5bb4966d91e99555f22c45d0319e0c2928c20b9fff006cd04ff17ded9b86c5b6cec7111f5b37b51d884ba6a0b4b71c8ef97518688b4ceb4f6e87ecc33bf4e31bb82ba239e77d5eeeb82ead7c42a7d3e9e42cc34a0c337c5e064b0c510534452b20950c6a510cdd8f23d8b3fc87a5e333c8685106b98805f42023842812e3697c72376a10f9597e05d4b2a2f247f011ed8710cd009aa24f86719b8e180ed01a484706c845193445db16e33d5088ca5017acab9d9597e163f818cf926198eb1ccc029a2dd218191eb33c986374169dc7d1438dcd8d0f397ed001844aee782787ec743a9dd99d4ea7d3c99f19c3097905bf4a1325a81edab74c974c6d24c98925460c31805838cee3a1c901877b3dd4d8b877d33fe800c2bdc65ea094524a29a594d29e349c93e5edd7183f6bf8a60058e0a35a648a669195b7b1a295ce8710a2958f010ab1688a3416509f20758396c89770210c51a4f9114c9c208a348a8223986089b47a04a9092d91664f80d30529d2ee14486a8012691793205c4125d230145054220d2b021454a148cb84ec201469dac602ea2751842b91b66d2ca036c2881ba4441ab7b180ba0826544889b44eee84c0f3d5074d26478c1889b4012cc94f54bee24c1bfea4690871184c9c3397e7b36a25cb790b3b4f2912d3e355b5c49c73deaeb6e68c2fe68c279f4a075622a52ac4fd49dba8389346eec0ca8433e173fb2bee7d6c2892413b6ab9401a4259d1987ca3380c062fcb8e62bc2562ce99edc5061a3a7634b16ca29e8ec807da519de29eab3a1d8b4bf2d90e94d9280d69d8aa3046476d10af87e1741ad2b08661188634b4f6e596d955be18eff5ec23ce0ec018a6d9bdb53692318f5266cccffe721ce013350b8f34d4c54e549da84a1b06398274a152428ebb5add253587aa0b25b0c6d5683bc085942f56f77a09d5f03c29c158ad30cef93fc7531ca190e7652e2efe8f13f185141c0018e48f98c15ead1ca673accd716f6e3169f48a0aab1386e9c8844194e3c175e05f57dd884bbe21efb460ce894321defa8155b9bac77814dfb0cd5952664c06006cafac7316f690738693e9e2bff2e273af24968f72fe7c7c544953e54bc8988b9e9488d9b91467b9776d6dc14d0dc3bf303d475123a68bbf14244aa22a70161593b88f95847125a9703e82290916c5b6d65a6e7bae7beae2e07f18ee66c2b611ad1e6dc3a604d75eab65afaa3db40e773219c6183f6e1b3a7a4c7c84c76ed6a6a6ea7a11258aa2288aa11a353659d45ae76ceca2fa824c5f730fab9cd18b0cce32026ee4ebef20d41b5399c1df8ee3388ee3bda1910bafebbae6e4ba0f253f9e4fae992e2e8009865c43155ef3e347f6d8ffd87ad42175116f6ac81a92478fa68bff689251c85f230ab98507017cfdb4fb40ff7f5dcb0eff5d8fb300041924d1852d82a08552047687795c2928a145175f37a7838c530627596373e303d7d1e2aa21a37531e7acb356ecf3c7d35a7b85464411a191a99a53b5aa610d925347505a1508ebaa54dddd1da2636fdb5328582ba20926910f219a481729d191d9796780e763541ea4513e5a2ad1122d8d4a9a66b79e344e04db69d756b56dabda76752ad59c69fab967ebfcf643883815ee8710691d100da94b8b4dfecee78e862d990a25659fb3ced3d11235512a326847d9db133dd9d169644fa3d3a87b85655bd6cd3963b37fb9fb58afb88d82f4823aaaac7553d5c861ee4a4557aa95ea5af14156a9ee6d194f5cd7cde5836a03d7755d97733756e4f999f63df86fa0ff5cef85643006d1e4e01e8e1e40a522c66661f39bd5d92ecf59af8b5bde139ff4095bdec4a1987ecac950fc496a625fd2232715e0d3beb62dd01b8dba4755f491f8d51a02716f840251263455494dc345da8d843811235ae7237519cd1944883ef2d168f4987a4569d6a2c874a6711e001dd3316811fc64568b22bb1645ee0ffc7c9f6cfbcc19ef743a38e2e7b3849a2a52f173823cb96e063d3a4b02eb01d7389c0e2bc8af850cce164e0cdac59190728044132d22c960e8a3223645418aa2288a62ad9d1f4c1ba0c8f33b5a9669dfb1d66e3ee6143e602214f9b85e080f293a72fb7998f6825a76b83f5da5bad7407ee0c7ba2258868b4075f48218d4757e1d9b0ef795abe97c77b8ac6e71f30495fdd168341addfb121a6952e8a9892909a694528a67eba3df4adc05479c2871a2c497cf3452d20ca5826b65328daa9c24a4911494131393c9f484c94d3e254ac95af156cbd432511c984c485a2d8bb24b9a4c319551587346e5e20a5df8dc3e685354455d4ca696a9656ab54c36c8df72fdfcaed4d5c42f158d59b99e78a916148fe9b28a19e3a4542a954a792e219fd4674fd4c55b4e52d99469cea04adc85e2c0549a4a53d932b5544b98584c399b4ca4c96432994cd70a0c20b40199265babaad86d0cc3b02539866f080df15f7f6d13a9a1e44dc6f2f939038446413748327d709236ee79dfc74fe96dadc66168cc0f3537b4cb51ae7a24ee28cbb2eceeeed6b29a39a736b5d63410f7f5c7075bde344dd3b4a7716d730fc7d542d74a69571965c6d7b5d61e31abfdda3d8d5f208caf8b3a6a9cac3975ec1089f0002214d262e9506121d77b6f8dc145fe0ffcd0f4efc075bf7ecdbd5173df723740e045188d829e73ceee401503584b336248dc7b2fbeeebdf7d2eb567bafb5b8de8de534978c4c4a46ec88030799d2c8ac7d77c39165a426c284c9e19ef5c04c5d92df3bfa7e942cfe74b807f381e9f9f370ef0324989e5b92ed20006814a0a8708232423a31b15fe9052253831ee10371f83a1a2c0f6f71407f1ef26bd9c1437efd1ef2ebaebf1607f4d7b2c34f2490beeeecf7a41225914824128944ba3744ea187177afdd3d8634a626c9c964c4f675f12b810411a2dd6ab28cb465abbf6f4eeedf98e47104e2030022b93ef83a34c78300be3a031872f6f87176cf823a3e903e109faf3e8d8fa036b9974b2cd94734943503b5d4faf9d86cdf22feb40de4b5d85246c6fbe914db2abb2bc5ad467bb04b353fb00efbabcbba161bfc6204feebd1bd5a06711797d265d865d865d865d865f8a2a2827d9354f6aac3322ccbb22c4b6b5fca184ca04e27146b0a9395125594d493d309632ce5ca329f33442efc099281c4b4f8449c928373c8669c7d76b91b7d5a32636a6af83e594c27a793a8042b419d5a6082214af04dd2de93b34e18630cabb141dd40ca1778d049e183dab60ddbb0ee31fe1504e6fe911a615b66b047d5da17952f68d6da69a7c51d1b5bd3384204492484d664145d3f2f900f7e4377b70f5b6be0e694c1900acc21b7f75e5c20bd7365ec7018eb27db699526398d27c8f56ac2ca8a528a1208655fc103d58884095648018491342a41c2c1109c71f45e42b4c385765d2fb9862d642b10d7556bbd735a4f167d5517c624d684d32a8a1255123207d5e48ea3aa474a2bbd51a96846158db9c944594959b8062bf2a42e2fc83a8fba786800a50cf6489be8aa010e64e0a655f6e80a3d5aad3b8ee3385e17e785cbc0b6dee762528fc6e76f593e9a61b08690b135c3bec4f45cc084a906a37164afeb3611482881d3a3ab464db9412142134f89d290bbd3a0b134e744067b341aed20f7e0e19edf7882f60ba533d4d3c57638c820a6251d6cdbb6b57a6cdbb66d1dcffbcf4a6470b63e70b2b2fd56aba58282eadce4a03a76c86053065b01711c4d0e387aa8b1b9f121c70f9ece6a73ec8566ad9d765ed8cf8bf1cda670041654221f42d0a0c24a1465ef43081a56c84288487b2166167250240aa2ad6fb801c4e1f3cfe14f83835540a72b420efd10419d3316490f69daadea97698c942fb81ba150180a3b0c851348c04d08019309d38a9950088970088e1234eebd84b073a75a6f46a50b46f7de1fe4fc00ca144d9053349102711f3781918c93e8fa16283214597c6837eeeea57a363e3428a24b850fc0f8c0a9f201111d50aedbc950971c2e0d122a3e70b27a3055d5da9266e4de712ccb1d9f4ff9293f234ea271eebdf7ca9052d3552c91b14506272902df8164361473890c2394423bc4aa6d35ddddf1a9187bf083e149624badb5f45a0ce49f18a3753cdcc5ffab3b505fcdb99da3c0052600030d683b546050426b5c8d754790107c11da71af2b546305061a8b00df9c12c5dd2f56b7680c0de70c28c6d7a45a0330258bd9635bafebba2e989c6f5c1de8e3bb78f8879f86ee91420a84843b0a7d5c3a4ce91e48451cdbcf4e87fbad0b627b6e7bae1b421404f79d0c743e3f7d95ce67117e5af2803a201ca2f6da6f8f3d2b88edb5b7400bee2ca0fdd65151744ca7d0162d411a8496301948e4e11fedeffa9a6538e6873b1ede3474984ab32830bfe811cd4a352bc5eae80f2125d13d275e16de161e0ed6c562b1582c16c65e8e62db5a6a69cb58eb323273a63ffc7692572783657e166152978f0a191742768cfd6554f6de1a342a5582d184c5643585848275a94bd376bd2297ba346738b144c74fcba7a2fd8794431f7dca074d29b207f1cdaec8a539d3ff83bbb80fee6203039386ff8c06e60c77ee060ff7ae07bb74030cec9b9c0e334aa67d373bae86325513bb8175ad2a92c12ea14aa55229c707f740aa9b39c33327d5cea6811f43a1b2ac860c0e3452604c92d9c74c4336b96d5c6419b68d66ce601f27e348c6e29250f03fee36f206c69d4291ddc6fd9cdd3daebfd129cd094992645ed180a13a94a8c5c0d62776d0597d630c04cfd71d0e3232d78533a4858316929648e2e93c264a85802924896173860c0ea4e6311821b5bb975d204e51d92d2832385b94bed8da897da23f421f6d3320cd39ebac5a5649b4da11574b29a55893b06fd29ce990c6ef0fac94c126913e0f88b130580eb30a50c06c8a0893c6942839547cce240559cbb08d524924128944b2f6a563857befbc531445ea7934a48d628be258924183eca1b73c9bf8b1ff74596eac2124113853b7033ff5ebae676022b78c26641f0d046708a70cb7484b5ad2520cdd7075a077832dcb39430cb2add124947a61e2a16c5032039939ef17e2de776df45b88d77879b1c1061a3eb16c510740684d42b69f13294d454585eb171d667ef4c39cc8ff953cc0f96cb0fff8b3b7d1402dfdddca402dddd574495dfc4b20ccdba1bbea5e0b71af4527f93ff1092a7ca08fa6286f5996652903671cc7d32a8a1255123207d5641ce99c18ff77163295a9e99691e971cefcf56559d6cf910cf6785d374a669fcaa8840e7a64c968040008405000831500001800068442a16040302094cdc37a14000d577a3e805e401c48644916c46008a2903186190288018000019299425500c62fcfd202af25a3cede476926b63f2a54c0fc9206d818fc040159cf0a4f6aefc8f07baa91e60ed749d43d2b2075d1742bcd3bb80fa7945c6b03d62f00a582e2d7e383ff660afdfb19659cc6015385c8a50af437bcd7837524f7e494b6b04eac50e64b6d2bfa44c81c8133c6b6b6e73ced05c1a6c24ed55d67c4e793a6a61b9a093193f59769678b8a9688a82c81b1de893b294755e589a146de29e630e9526c64172833fec060d2124d7fa25cf39990a45895a744f0967917d7c338afacc4d92b9f99e2f1427963fb6d07bbcace34e5bdacc0d7fa2d28aa0865e6ecdeb93f5f49cd395c52ea42f19c6a8e88b23f8427b54f294d5953230599815eff254d31dc54b89aec8180f0f077c75465fd946cec4cf1462a94c23935156412b78c13bc3b0c5dceab9ada90ac8ab60db5aefa194ea07c7797fe8537843952fd2c2fcfa626275d37e31da9e4ccf61327d44dabe18340429de298001def786b431a7ce3484191777928e9314ced8158c3ea8e836b83a3ac51886c4e8d470523b04b12c0e8dc2eeecde12a9cc60f7b6ebff74d1b80f0527e7c2677df060728f3455f7a41ec225d68c8fd8579603fa9d283b0b1c37a7e5568a19c1b3586e2830b848d02e99c4dddaca53fc844f1a25075c611859fc4e19206d7511e6e3c024201e88d02ca61076ff5210056b04017c2c22a71ee401e494bd4f7f985fefd0eaa0e459f1b64a5c723c562ea632c58793adabfded5ef5542a57da5716045e64bec5801589356b54dd105eae52abd44ac0d454f65a70cfcdc7553f8844295d833232334965459bcb2bb59dc359b221cc1e6541b33ba8bdd2994ec0e1b34f1aef368c5baf83d472599f6467bc44adc24ae290a6d0b8a8ac76bf3d42a0fc9f668b18bc958f481124b044e07f00618073b1925e58c15c162a9b830bd28f0e61c41b2629abac63472c652f087b0676e3b2d85f830ffd62ac5e22d87846f368b9a357c2249ce95e10fb9080af3a98176a217ff4d3d00a6e4bbc6cb81877c78e0356ccfb560260c3eac23fa2d9664fd88a11e08fdedd7cb6ec92ccd8b522a1d41008cc3c232bb64545679de519890597285d53b2671ffdbcb0dea41897c74d49caf1341b38640217cdb816821984cd168191389176c94cfeb9e17e068949687149e541f57a3f69ca67466f159c142a3af050b1549b8978345f8029501125fd07cc395a18741557f54496a191439f69def98e4ebd420bd64219404b58f3c91af362336cffa5c9eb0e42640bec22829b772c9eede40fd4903d2869df7778f7ecedd5774f4b100e746776a2fdde95ca10a3feb74a75ed01bccbd4516c332926999679fa8951155178a92b9c692a0ff1e3bb012689f538ffd11e70539114f90a13b0da116f6e3c87c8860ea453c91c4518a98921d5c6162d4972b74900ae45fcd8753cc61c5370c9d9e11ded5ad755fe3d0732817759f78221afb637ec7c57e3993e30fd67becbf4ae2db726811d0ddede8ec02734a435f2b192e4c8217c0ec681c956644890b25453d4b6bb720b1302676c43213334d24ad2b627409b327d1bec1ee3f06fcf0cf2811089ca9eb4326473277dc27d2523161c5617e954bca0df6c2a9654d1c3c4ac6719087917a382dc680231256c87b822c5a9e22c8c20d0035e542af3649632d6a533983ce8842a5e13b6482689a6641792c004890c1f19842737664dcdc43b4dc79fe6452dad542b3c48a7ddbf4fd26ec1b3b4be258c89f982de888620b28d7aefb09c469da0dda58e9282b58092d48f7e06fb59753701b436c87777b513159b43ce6aa785751a5b7a13fda5703f485c159a418ef0c1cb98b569e23eb32ad5bd151c5a4f75409c3e966de62d24c66c405825a5caf780d4868827bbb2efe6ef83e3543fada09ee8acd91b92a9b0a426799ed9a2b40bab7a9abc3c5db38cf3fe312c2424237a6ed5cd106d07b71e64f297a4626c30f4afbe78e2325258c0087b639dc9683ef76edc4dde00a1a83b73fcea0e18651830b82d60940785535eb302f5506faa736f3df332b86579a9a462e8880934aefc18270b8aa317628cbd245e60eadf848b366b064eceb7d199d023bf3f16100a7b4247af47512ca48b29a84359047501d7014628363ab2d0c0942d7f851b8cfc5375d3a63dad817a3b8aea4733952284530a9b16fab7827aa40cb3a437d74f5925b64dc5c9ea6ebba9a5c47bc79474b1cbaac42e3d8e96c8eac84225f706d16e113eed66a1b265f84ee753c6521a063f4b09ca749bb03cf7caa3a76c7f69e8b1f1fcfb99dcedbb67f5d3ad68d2390b39759ea25be59ba325308a46b3023a49c08630957c34f3ae33cb446b0b5c9b5e9526978bf55862dcc9afef56b072f2ead393efa23f8971793aca9c6213419acf51fad0cb2b6c17993ac0a27d94827d9e38e3a8725b527595ca7014cc4496c734fe964eddba4eec1b5bc25595712997d68405793ecb08b2cbaaf88c396dc02201d1baad3daa4ceaf59775714fa3cbd06802c95878d0f3431633bf964fe213e9cf56e1cbea79aff2313b56781c3b8dbf7e13aaa0849bfba71b82643208a9f129918a03d868e86987b589aa90fcfec0b7c3c4e4bb58691cb1b64593e2d5d923d4ad3175c5cc0f5173beb928e59f8bb6840abb9f8eafc30ed483b8fdd9d245afff0c2ae7cefa90b6d35de87e1e1ee29d862cd3885499143fb27d970e85722c5c52cbb3ebf78ad6d0e58a9cc5f238cb86d06ddc69ee718b9286c15cb7108c70aba2b3e70a0a23568e62a0943c745060c106e314443933b9a5e18adcfb1ad909891b11eb136071dc8c5507267982866d613a0ad7388ab6c2bc329d5ed826cfc012a76f19c9a3988218e8294067e7c5dbed236ceeb30619a490a95906d44546fb7fd59222729df91a7a510eebbdd71ac0972b124012f970c39b4159582f8f0850e028d6fb234f1244a00d7d4bc171e21ce81ca925d2e2afbd23d4a56eb6a5023d7193fb1546da43da033abc5fb0f2f2bc3f54290fa6d20c8bf6047381407fad8c58c7a92af6d5b0514ae1e97e74670e750a1d0d976830681fcc6e292f87305515ba69d0a87b0f01dd8436d5a65b0d7dddd1c1a4353ac00f33e38446398d9f5744f1deaeeb4e2e164462740eb0d351137a8b3e5d63c9362a538e21928fed3076071d05c96d070ceac4705076eed0a1f266c64a22826c8cd4769cee50b3a53303fc4a5bbd4e210c0d2645487d0fcbd224d63d8a5eb6be5ea590c1893c18a1f5a3c2c68d287343f743bd6d904ea00c0804893ecf34395237e5372b59889ba0c9d1fa2e302d13c2e94a021aa63df3faa3e80bf1eba8f27120e25845ae8358c62998a84a232c5b43d11a4f275bf5aea2ec8c1bab9842ea2614ec2ee030530a52ba9101eab409698f939c7e1206d1aa3199288e860906bf12451fb6a486c9a6c6bbf9bd75fc25246bb6136bbcf4719ebd4212226e7b97ec531c84c31f66904e631171ed4d55c2d0380c682eb5f7b8a837f3b0ccc8dca8ee36381b7f5cc9267ddc4051203afcde2315f9d4f4612538025cee70a924a19ca6620035decde70fbf2ccede068b1ddb27b9edb09e66d03ed5aece4938ac49a8edee848260b224bfd6e7962d97ee60f805d230c8d1dd279911c49c01ba0f7a87b04075784ed76de05730e121c45a9043a483b9a235152f0964b3a2878527cb45ebce69eac7dbb2ad2021d8d3e9178189e9f73a0b671b28a2384421a7aaad69ed778b30e8e077e63a7173e756f25240fbe40ad1e69156ef56843d75efa0742a11efea63ae4d076295fbe6fa481a0417f63b7b1e5eed1b5628f40e3c78686b2a31587c1be4a591bfa28626150f583d0bde714e8ab8c753552a4f6ab55f77010a542cba9d000980deb78a603befa003f74ba29b677d53a72da7eff92310c0428693d5ee7fbe99b32f5d4b6fe01c78a5563afef4336d82e1e2214e2db9389226a958270abe5b7264cf60315f5925058e87bef5637502b2313265f7a7d268c1bb5ada9008fd6c01da67c69980ee4761022d52020a4e2eea3a905fc41783f12068291f85803b834847ab62adc49313ed6f52c87ae37eb8f5204fdb9699cbcd6887dba04511866690ac16199ab8cc8c7b0827dd10608367e664cd177b3758d2d1d3a2bb8e7ae8e62175da57d71b007a244438b459b49ebd2aa139882d373b52acb6852b1bf4fe480a6e351d3a6847ecc255e889ab30701322702ac1f31cbfaa16fdf60526665a88cd1ba9414600720906d97969b2ec9cc8dbea16bb005cdd17ffaa4b2eca181e279fabd82e3734013b7c862760ce382162675d95269bc90612924f1fab6e883ac02697a89a58d4e89f567a4373ebd3bd0bbd279d5d20bde63ff24dc10cb2fe235f7a5c3735f09c3529571ca80b6c9dd9b078fa726bf31555992765c38c5c6480a32da180196656575e9bc1044b9758bce811411fcf7d2f79b2632ba4bb46e4f9c1a949e19f07684770459bc3546a743fc4daed6874e254238b7ec78120514ad4e40858343c960ac18ba84ba168b793da5daa2748615689ed5e1f8d6b3a669b61bb2c0755e472892b60e97f16e0e08557544c43e12ebeff854cdf4a867343e8c32c570b8549879b5f3e323d5efce881b44d5298a23f8d1ad7e026c2df0c58fdbad2a1d1f57286163fd8faf0414fbd9f38be581b686316684b50c2a5fa6a54e2b41462f21a7d503f3aac04aac67c142060662c32aa1273d0dc0f8fb4499947ea15c2d629b125694007e3aabaaad95c1c475def01edc2243c1529e16f86c6bee1cbbcb233b38414f8f800c2ed772f81ea9a89a67405e8db86dce67ac1e266818a31a1dae67ae4f0053697d01f3190302d1de03d40a878cd722ffaaa693647f36581f2b4427c7070a17948e194be98e3eee1b62663f5c03944cd88462c01711e99d943610a93f2351147d0e02f2633a9074efd3295c7f0fb9b3c6c1994c911950e2c2e8170e98f0c85656dcbf80bcda19af36be7951e1d634b7c154a5861e3df779586ad4ac54c34c42e00323b66e1c3f6ce7ae6688a4b7aea13d433bbc017a036ec43ceb13e264e04141a93ddb719f9b3c41cce03eccdadfb9e8ba4ab95dfbd3712054ee8c846cab5f936aab94c202c0a2949f90c9bbb9937ddca230fbdfb10a35fd5b6082487f18eadaa173926a8324017ccf81574eaad92f8ddc70d4585b75e34e82c643d70dae5a300e26ab120bebb5a83c12917324849b9b8f1c5bdf154af4ac99435cb445eb02128417149b91c116081bda59aaf18140ef2bc55207ae028835fa038ca06210bbfb11ac43c50a7657c687a24508b74ed3a693f1a174261006d245692616a5fa3618ef21138a308bc0b946efdd1d8fda64d15e9256b637ad0ef58f0bd7e7a0982f7ef138555a04b57c695924fbc242f4c9acb15afa8416d5284f21424517312199d119dde35c73bd8625e7a54d7d266eeedbadf38b837c9fee88fda35690429069b43fea9282e84d47117a465160878ac87ac67df5bbfa8345a3f86fee172bfff0c0a2184e9914a09b02dc3d5e5f9aca70c70c4db170e68cfa030975008de8a28e2e9b124792d0606fcce338d5e11dc1121afcb516011f4167a9f56430654665c78ce470fa902e53ae49807a477eb9e6fb4e03beea5c03eaa5423744e0ac17d1abc548ce8989854726e764ac4b1c109c490064d77c6ab5d3f9302370a9143a13824f53e13288f9d5b1da91cef3031d3074afd2dbb3cf4a6d7e9c13bae16a0e108297d07e69e248354e6d527f76d9a617475cb14085ac564085c80276dc57d4b6a41031a3feab429dc45e61b3d19abc7582622174e7f81d6337aa97ca672a64d5562679c80d9dccfcb8abb6397cd7f8527c7fbeb5af40ae8e64b4a278b23dfcd0bbb6030ae95940ed9d30f21281fb21abed9ee79ef14c78dceda006d7b6100514d9da46ca952809d4d42f8ae722c9dcbec8f9365eaf22820ac3b907dd7ccff00b164bb74fc3fb8e26ce70e49d8fbd5289cbc4e3198515d2e80ec75eb1f728a6cee7a557ac117f0cd8f189c55134ffe12cd95da9976b9dc750c1b48cbb805f6e6a0e8e928d4cb407eb2da462aa9deb1c0ea86a0a5fbdf9528b5af05241d8519c745ebf80d00271df256a2969e7c6a0be9c8ee8b8bba52ae4f9b12b2c1f7e1f6c6cce1241beec981d945d15d3557a61078c11cf3f229e81f670ab09f7aad3bbc055fb5d2d3d16912d8ab7013d1db96cfcea00e5d10f5c5201aea9dd38d4823f563839eb0e60c862e46d7f228f75acde2270c01685e8994854fe83ee3fbfe42350f4bec63245bdddc83de2640a841670c1e67965d181850347ce1118bc8e69f630e7036f374b6f382e7a6f5d065098bc505d3bf725c4128a4ef7e2a06facb1878d8d2db970ba5d807040a4a4e7d5ebfaf993092dc170719ebfe63cea2f025a64dba471b110192343732696ddef6f38e5fa7e8eac7115758fb971745998cf4e0736073a719a537b3e16d89e999ee0f0f244052adff6fdf2cb6a84b37b71ced93c9915bcaa99f23b01f674cea1600da4f57ed62664d4f120e8da4a1843612b108688b5938086ef15b7e460a15bbc0978e60c7a087725c212cdbb0a062b5d93e5d12792641c4f3bbcc128c8e6ed5ccc0851778ba5861aa36a35bc8fbc4caf891a2fbba244a7d76994c7e7fd1f2cf449613c71b72f16ba0029fb0191e93b45261e64a74832ae3c1ffd1982e5ecb7157024bafd75c1e5812ddc30591b09fd2e72e95fcf45b060c5a2ce914234cb65d1acf974629f98f274ed0903602a73a6391f4f6bf702b8cbd9a18a4ac92b075260403941e4fd136500848f702375294b3b47ef6e78e0499b33e4deb9cb658714d2219cbb93f422d78fa87f17553eba3224871548bf275b646056b6514009fb8a6032fc0e9409750f177df7f303987bdd75cccbab3789b56c794ec7f85e6fb232c4de00a4cd6e03d8ea308b25dc3dee58fa5762708185c82bf88278b96d8e305c9453c5b830103dcee14fd87d1f76da413cb7a8cd30f2e878fa02eae90bac34967f7120a7379d0e0f528db0382a694bfeec64f772e546346ff0170137718ec7d2a33bb4acf155a03f18eac426612784d1ef466f4a258081482589ad54204e5997f3e92c2c1b08190ac78fd80aff472a37ba9731cf50f6c9f56afe1109ad1af84d10bf5ce63ff9c609860912d3b6c06ee193d855de9ef3b41049b757f5ab942cdc0df858e16139267780795e100e04503cf320a1fe06e3a451719a441e40cb9d0e78dad4095f62094a27b7b437232895bc338f5ba0b4938f4cdc406942722940c851e5cfdfc4b93691bd718fd4eaddb71d050fb1c37cce18dc3617782509c7175bd3625fd4b0d9e89606eb7fb2b00a0ed54839804260142eb72a4477fa1c331734e13798affb63729d209edbf4a50c4ca900a98bb4a0f358291ab785632966ba14b57b92ad73e04fcb49e963d07fa266dd8a050cb1d6a95eaa7c18a8c225502e5cf8d4bcccf453f5998aa177e5a5a53130ebfce7c7d7e76ee4c6668412a52a044d6fc987dcc133df96880185254081c738184688e7dfa69fade9376b03d5929143d3e480eb9d3d79c42865384e3f24eca2294d5a8dd6947442cd3ee8c2400cf18127261846273c3362956c65b11a2ad147e658006d611c68fd564d557e73e988ed819ad84c9b5454455adb63458d31bb7d121ecbde3e5e98476d89485bf05aea48ac27112c4c6e1dc68435c69467617ef027f76b7f65ea0cbb34b5c44ca0d8096aeae18832b541f515cb59bb64f6a08f7d8b69314824368fd2f081db48c7ef890c3b7799afa9d2c18201ab1b2202f2e42dc8f672e13f0c9a2be755776c3772181490b98ebe09baaca3d6f4b3a64e93c74803dd2084e34369709fb7f5949c739727af81c1707ff4cdb8dadfc8c00814ae121090c48a9af2bc882e73eb046c1e26b042c46dc3143ad20a78110a0704374546e3884b8640a5b06b57ae60e9600b574d4838281571522703b89a54526930c68a26c3c23f53946535c1d93cb7dc2e21bc772fdb4ab067aa14da4b2719be6416b92c707a27622c55e204b34085a743f77148d6a88972d6c7c3d7b4ba2600a1f59f2b959927ced9917331e3b8e5cf5fb76bf1b2cb3108bee4ff0d0a7d01a882d87e30140c61af2d3212c971f7ce678084e8eef547e0bc65ca54431a0534cff6a4826982847f2b6a8662669b9e48064c6a7e7ae0cc52ae6655518ba5765a7d2c57f4a7c3850dbdb2e5b8f657820f72dc215e34dd75d49c42454afa42b118ba87e195d562e56c346d57d4c2da3f5b094fd492da6b96b41737a2e632500f8d0f67f53414132b3bbf1f17b861de0dbad2b0f1299f402e538ac08b200b901d054222e08f4828b6e9062094cb11f3578de02b4a15db227bd9a111416762043725c2d7a533a518608a0a00ed3608a1cef5b5b0cada9dd7f4474236a6561362710bf3350565a1d7dcef49c17775e161c3a720fdadeb4f45696ab530aad3fd4e190227d0cab95f0ef8553b98f6875b514696f119c0a18c7083336aa63912d980b351313ecc3f66c27a4e61462ee23a0a7303be455d81a6b5140fafcc1a71acb192b30a80b3dbb29d737b67a2238e9c7d8fe553028ebc9726f6391c81a88f33a83dc9e372dae6cfd756ac34336d1cec50485f887843086f90d11b187d6fe9e68062a59a86e14f646e01156911e652cf54cce7ec15d98bcb68b0402c29ef3f8b2ae24669698192896d29aea2beaed1fff9c63aab186ebd93b4557db92ba813bdc8edddcbec07112583da2333f62e6bb075bc12c88f7c87366230b7ee84a3351ffe7952f8c03a0affd90a56745a28dd8f3cabd53a33b7d0e5029fc6af0a8f8168b1fd4364b1c5e2ab7fc0a94a2b4bcc7904e3bb3f58d139c2dc8c5e92a334222ec27a10a1cee06c18a2bdefbde33645c916d10ecef3e38994ec8a8494c2aabe934a70cda2b8f31e1649082252aebc457394d42cbfd0983ff21625ae9a7004aea66aa6c3b6b70817d1bffa9e810ea5599461276c1afc51694fb9357da37fd556fd96de313731a7e92ae2446715380694f9f8f3a652b8f5cda16db33442f575c54c7f42ef0da60bbc456c8a5323a8c410d10b8a134a49954a0c1a6d7cbebe91463756f0a526aa7b369438a8a6f9c8110a68433f6c35097176ffa01fb59786f85ef4b2694518afa5813668c5222faffad826b97ec92c8b0739989096e970b366a2b2fefde632f3e199cf492833be9a54411d9d78c536022ce502ac1deeabbd86a49682275b51ba0e797c18fe92708457230ce35cac66d4701dd7ffcdeb316fe4522f27a3e099653808749ad7d87c7518fa02fee83c10e1067057961f5a4725127e11a210ff99d0f9065629632f4fc0e82ca94107886a2f40a37731c45cf22c160962f3d59129142d642263f280a9571fd1127679786cfc51b9ff4b9f25f0d3da7b2294c84f3024376cc0932069de82f3141823447403e555fa6e04d6511272cf0d2228c98a2749be9ccd2c0292d060e4f2012278271f012e2c32b652360bf6e4701fe2b84adce4a3078dcafc8cb8474215346293afe42439bf02cc3d0da7d92979283d6c8f1370c68f286441858c61fb266776eac6f8dcf2c36755a16f7801bdbc9bac2d4492e6ebf76efe88df38f0d3b87e8c4a3999236902fe525b2db33e36e666635380697508fb3aea391b8978c6aa6585a3b3201beed8a582167e97a90ac9dbc75a6513902f9cc9c80216ab400eaf1e7433c306129641faec246b2826fb711a2b0f5a0d7da4bf0a249e5c8ebdec98ba026563e0b81ee30ba846e42d01be1374026089fa1e58a7f3900c1d04e11d15307900b4c784a3fda73cc3bd8ea1eb81bb8b232613c6fe6344627336b5431e11041e9b5dcfca3955c858101333642a04fb71b99172035d8070b5315a918c639043305f214c083388abd9900203e62c52c9c8c3382c276492b971746b4427865414beca5f1e2610ed5a27e9aa4b453083c5652ef161cc04f1d49c3b6ff5f40444af49ebc71e15de033589e68c5b982e1beab107b85793864a084eb38182c94d0ca268cb4d59c54d93be162f6fa4025baf51eca72dade4d00684ccb84fdee4d5d6ee3e7c85cdf13bc1ec699dfa0018a6e3bced9868757a34e860bb38475586ccb3177fda5b6fdc859b5600580b531706db503f2bf55a7416289fed1c5e877baa0f37871be7c09c0d0ef7b7ceb1301920bd49902f834f0ffabaa2611f08230fb4d2e7fe6e075c0a1765e753b899b6bb6a5c5a779754369f7e4ed93ba8bb5bd4cf7194b9580d8ddb60ce85da4e0bfb63faccb5d4778750f2eedb84bf80c3616f1f8542bea8fdbc22eb978e4a030819d8d705c0c4aaf12ba3f1abe4ea7316bb8a3f94d0e694fce11cf6c935530474e099db288d3fe924b72e99f64aefc04314e637a57454e930b43060d314e992b58148d3fea48cf94663b07f71ab7f1255924d261158c472bf0dcc1630a642afbd931ab7d19d340c9344aa8f4daa86be9bb031a1ba52f730573028c50dd95fe8e7447d39832e3cc2d5aa740196573a802b4b9b70313b173f0d503bad24f0fbd689e06a401fb7771ef79d80d88dc4bd36bd243a4dc198646261176094746ad89fa779a74dbfd518c23d95335cc6b5299e0d6467788452cd09106a3bf62e537faca88892f228851266b1c6343248853388634e9a733005a145c6d46ce45f1921a120864c79a9e5c4ff0a26603e681341c3bb25bbc16fcce7fd242a904b81271a8b07c4c75da1fc39925d9baaaa8575286035fe5d24571ae28c0af6abb6c96171e6bfd9c4ab18527bb2d018c6efac2764855d70044cc800b9ede4b6b7ec38ef28738a21933bcaa25d105dedc687e8b8189650eec4f5d1db1dd32ff0ecaaabcd16caf445bf47d88261c2f869394c525e10a758e30999ea6eae752dc4917c6d1deec71265482f4626aff6845f47be3527caceb76c1153b235776be5e6d2a4a4ae63c21000d750c548477260fe0891eb1b9e9f1bff3bd9de9ad952f1b4fc3cb47d12d2dd4da2e585fa095755e56e2c9b6aa40573bfbbc6f1a0a34871e358454c5c9faa2908d4b50bb10f403f1e381b35c9ebb39d00aaf58374b4917498563dd488e341b7c0aea43de8f160de4cb5c2a2883998f6668a48e9e21d96d04f8f07311f34efa84c37040e92591d9feb84c23f32a7d7ec04f35872b51a8438a9753ce82171e73e0000f2d7beb6f236f2ccac0f548b1b06b4a254df3508dbf3e37aad35604118eb601c0f4a2296ed132268a9b805be5a5851cb76970ee3385bcffde98f07bd7a968552abe772ed5dcbd45946dcc5f6cd302ed6b8570a31f7d356e323f12f77e24141f7fe9c76d60dbaac3683013e17af1318282172ea45f93eda84fb338432f4cc5d488ccfaac24c6eb676c8be18f9a2ddad0c7c02bd7f118d7d49c669fad6fae566fca49f9d9adb279d4b1e6333ff0610e1e253d632e83437d359ecec21d3de502f2fe217923327761663d93a7c80ca6c2ce5993b71088ad7652650605e8d982beb012c9159c317839d61d64a9be669f10e85d98d582c2c36af66b5bdcadf66ea4de334682d559be1555348dfe01e8ca584793797476d32eba446061bc900a050bbd3676c3974934f974a10d3dafe8630fec658cb812e00bf7b81586474b0e25f3309bb501781506e676da8086f2924888cbb7ffbbbb9df2333ae069acbc92f069e56e916ba901b6e9d82067a262d6700f085dc5a802dbe0e99697723cccc2e77526c26f15b34efd455aa20374acc9b6114c6a64e36809af56de7f30e72e3c1b27f92f1ffa46ab97bd003f0ea39ee3e75263c072aa9b567572814d68a0671fe698c82ec6003f6dee5943232214b76d49e39151f3069e13c8a378baf3b4d1d08e0fcd63e09b8c333703669bb65250270f60642dcc63b0eb78bec38955cac4db336834fdda5a419e9352c371372cec4f59928b60ee5fa87080c7458df60f82162acd52c531970f0e3a8394d1fd6ad860397afc96c67c400ca3f73f869f892af4b1a046ee5ab321e1f65b650a76e2ab4ef6ad459b23600150b76b800d6d88d3b36ef11dbb710024165580b33fbc125f073b4e6462a4d2b1a58f4ac0315a3b5709f7624e63a3dd1ba468739dc167c3887b3a9ddbc456e12853aaf91cfc9e2a2259cacf0c5794b6ed9f3962840f5418c6b772cf0724b6d6e3c3da2ac764d404d5e308313cd56ee1f455c5f79738b885cd5da77ad4a3e8e2b33995aef7c456d0a7f5792f19c840e0fd9b67810e2f52d7f503870003c3cf4d7508d50e2b6c55e5b93849ec62b24bc929aa822e139c9b57a6df12b4818a15622cf075f32e9eb4952e6bb9510c109e4608ff6931025cd02bd96d534175587fc4d3e281ec845f68dc2aa060e94465c30bac79c2f8bab7cd2b096e3b2ba66c8f9b18ea34813e2e172bd55d47ef937194cef571262da86eb9ab5b32d1a317e196499a093181a8ffea317296b928df806c3f6b39d64ad6fb8fc8a78b16b1da3275f280f95b13021ea4740c91bc8dad97ad2fbb7b6b2787745aa5a46106c7edae4f062cf1254e8435b535f477dc9f150e5488c20d88f0473199146c83405bc8b5507814d8811dd049c4430bdffba1dc7727fd5a25d5cc547b4b36d52a4b4cb2354dce4100ccad5b59eeb53617671a9c6bedb62c6ebd901743dd9061a73286ca38b830deffa0166562a5b61fccbba29db1056722ee328bc16b77912e85b3d807c41b0d4956d295c6698e070645823240216abce102d212b3ce13a24ba87c5ddd6ccbcf6238743943edcecfd462828d7c2ebd3be4af91e738c60686a334beae5266079160d4aaea3cf278132342a6909cec0090eac12b2eb41bace7b602b7f4e23a7eef8ced7ce0996b3b9891bb51ad6f799985d6850c0225452228c159eec8056261ef2ad00d85bd12ae2b9c5e2642bb2606f092c714ee3c9cf04ae1e4af59d0e0213975ff9c2de2281884701a08c2806e3e876851db50256794bbe278afce23a36d95b7474a3dc80a0f7b6f64ba8d3e82f15fe2ed04432f147e9180561ee837ffec21a1a13bf5e304d39bfbcc6b0c4aaa71b315a4a0f7cd437b090cc280ec7d860fc7e6ee35315ebb906138ec05457f98b36530933e1d391c424943dec3701995ac57221b8a2fcc90063d8a8d12402561abfe833083b300c6bef70d9d53d1b79eef8ad7a91b95e7f1d810071fc6dc24d44f18e345bd4882f30d4d186a09aa3cc945293c33cdd60448b93432aaf85651dade7d436ba4f0e77a8bd038eddbabe862f7818be49d9816b3316eb08e47aaa6ad756fcda54138663c37475ef6184ac38125863b4066babe67e3759b7523fd551df3073cdaa10e6aac83c075303947d746e0bbe7c0d73ff5844b7d15a45e834601395651ff71c1fc2da244af3131809795685e3d11297b38f7b5ccb9b5663cc2aeea5664959d8d65bf9daddb1b2ddcdd87b2895b241a80e22b326d3563c80e56a769087346bbb94a9fd96c989f158fbe28377ea9f6b55684b4fbabbd525399ee01a086e08ff5483f33ecd062dc32b54220315d99098901ca28b3403e0f89a07a893d67278a02bd865366f915f1dda5a7b2c71227c5314a1bc59e8dfb2aafcf87e07433d25c54e9b95a526067e95fc40b6a6fe4bdfeb78f4606c5ac4ce2d1becc716dc2c987fb919d7cdc25d395f2b73d54c4cb6fae1351b928086be2694bbb87e244bfbfe15362386db0016d7ab02864a3ca2aacce0e98015cbefeb3a773e7ffeacc296df5c579b3d0f07052a1b9f48aee5f12ebda73f79b95031bd48f223c4b638f8d4eee87fb1ff9cfb295f2977cf8f269f9f93109573ff27ce7e99b8bb31d79d9ed602181829515d135df8744601e8179f043a86fd6cc057b06fef9af34ba30168e46e553c5b3e0fcdf9921db8a1a419f6c7babd0edad46408b9b270e863a32cf896b3a1ac7f25992fdde4579bf7e95ee1cb161aeaff8af849c32ef86009005829537f0a521ca54ca16886c3fba55476b23068d7a24bff95c6660dacea458e1006fcc4197d6b8bbd20b77f6b14bdfa37c5c495a6c5b0374f8bab7622b56039fdab072030eda1ce03d6a1330e0d33e0046d4a70667c273a5c61af4f0b193b896c5fb99a68b26f0a7b442001ab48c9e5fc04464eeb386d3947db3e14a2c38c7a25186e75330c5a816c210b3c65b43a6001f358bc0c6d8406832c32ad5a3002fc53b65f49ba405240e31b314f5aa1fa4dc9ee366fa7e0c4b4c5680672a4b709da37d2c9448ac1b8a317428590dc47429ae841e25d718682497dadc2044cdc57540a0c67e6c0f96033ad7ed71b48bc524a129c0a4e0735a397cf0e51e5765f56279c2a72d0dd1d59247f8abc64ca5d0519a691b710c8d5516f27b1cc6930e99243c1971c65d27414a9c34579269eb13b7ebc4e999ee7cdc39dcd3ba037af9eda42ddbcd9d882d2e816f85b6e7bea388e452b75b7841dd859f25219d08f479dde74cec802e86c81624cc7229137d0979b3ee3342fb7f949b9ae9fe467489b7b756bed08764e7221da1abec729c513bd42693470f36f84c8205b9e793515d70b8618bd0398b6ec7599ecd8c4995e481770d848d81b6a1d1384646521155f28833fba10cef0e603ab91a2ee3e0e0116b3a7685f474d41122cc947b33199d6262ad28b06047715a68ef2c94b329b38ce8d9e428e894c0197cc66e05ef783eaece0221a2437bf60084749f82e3fccf9e779e129c49f8ec1624134fae6852f8bebb967691ab790385e6c3aa4a35b88b9fb1c6d167b26a43e6d10c3ead4a79a5eaf8fb68903bfc5165311637b894a16a9c34eb438170f69ca781d6885d6a09bbf8bf0dab88a38a34f75d0f427d33b3d2a796156bc3929b30efbc1c1e06e226cebf61795a52104f5f95d470534bd0bc28bd19457a79a6649c7625820554d2ab695f80ca0c708f0cfd0e7d59515b81bfd9b0758fbd2b9d7bc1b59e764466416c7ac1cfa87320eafb891d73ac579d984a3a6d41d82e671ffff9221b7c57edcf2187bb0b2db16e0e445722a1dca40c78154dbcf7536cb2527d87f4d3b56604b63640b6b3978bd10af4ce70d0608c4c66c8d6260c8c802e26c4df85291528d6a0c5097424aa31a680df5ec9a41070144f342d30c5d8a4148283cfe5fd74a4a5be354baba5e725078d7d563fb4939b36681cba48a2ff5ba201ae93347547f696b5820b66e436c0c2731d895458878eda0d6d6e440d60ae1dd3a5d34e824b9cfa96dcd69e8329237df73cc6d8be2becee34b5d7edc53c9503a44138031a9be24f935b8e3defe95a8abb6540499a91385d55545dee69321fc1272d08e8ff0b8fe0a17e653f0254aa1f7fa368c6d4d5de4f29dfa8ff4eb976df7b8f554a571b34aa371be258ac0bd21078f044ab5154c9aa10ed56668627ce492905a42feedbcf234443820730740fb1ca9993869643364dbf4b4c6ff4d92bee72e44fcbeccb7f731f85d23195fe8015f0b5816b8290fb37505751b8868c5365152005b97705289c65e7315b5b69f76193c4f6dbb69fec77be5f305bf426917c0249982589d14a620c0685b22191c4a6365d17b8f92b7d3ca72c35f42ce9af50f627b0029ade625b103523a367a696014f20ded1862fe0e33ccb19092868b48ebb4ad43969951db46390c6940d8c8e80d74367915a7780044ac04df259b3db4c7dbeba6e6f56dc61edc215b69b283639aa5981de21d14b8a62599a0e59538442442b2235b090a1c5989d4687fcc93b9eb0c22ce8207fb6a0389ba0870738319a125d6326332756fe54bfd33f9973c0c35e91490469c317536ad3d848c878711dc1e52952ee89b32f939596fadcbd27145407313520dbb32e13c5229e35f9b857cd3d4604a8bbe63cd4cfe9838774a5d3a72063812d1b66e9fbf1591ea449964d4fd451e92f5daa1a5fd6e6e48a21a4814247ee3ed4e763511169e06f9c197fe00ab271b234f27f993c0c8011d1401b3c3db4754ce7a3ec6be141f91efc5a2de8ddbba7804f99987ac9cc8da33607dc479de58405843f2ae6ebc8533efd97282c32de3de3e835afa38d634d1aa87d76a9ebb0801e421959959cc34efa54b3cca73927fcf653b56de764a20564f4feea9a7a7c2603fb351a5934b33c0a278a49cf94a7822a52b35c4d70899e6d1a0e7b384553f4416387808b62bf3766ce0eedf077fa364347b031024044b52034989abc811bf4126830bd443ef833e5d92f47b1ba082a6232e46cd3ed6dde994c71ab76827e18550cee24921d0f157683a711cd58ef8afd3b27bca1f97afdec1eeb2ee009d895ea9dcbbc99c3657c7328ddb9258c1a347edf58200739425ab30d9c1ae1ff84bf291e616f5b16198da5f74ef9c1aaf881ab447b549e7ab1486cbefe1ca097e79ef0b868d7a501c64c178a7756f7547286aa0dedad17b2bf1c4d8608588dea2b765f6074d57e54dc95030e1a8601650ac749c9395b254de1a79833ee153339322bf006b01e7a43a4b15441857e808593d775c7a85587b56937194434a089469f741a236258cb04a3d5dfea4079e9fcb2711a1dfe68c3bc22d150fb499aaf431056d65079029454728b086999864e2e7fd1f968f72d37ce77601a727dd182715de56e0b79fbc661e66c289b0ad74037a5e536958fc95eaf2c36ea6504917550966164b5ebe2e668ce265135ac4b5c949f9fccaddee62f3d9e5905a91abf536bcc029b51dbd38f8e01cdd3c414975086a2124e23ccbb19818680b2d896a750999a9b34b826e45b95977c74981cd0fa9a64bda3fcd5ca96042f914493b8c8172f4e6558594275025dbc90509b84c5580809d22cbc017b9f923bd1c71b845a928bd91ebe31a50af6e3a9f38d24086e0f3d00688c88cf8b4ad315b6202ee7cb25674e5823ee5f269c7c281bcb207ac90438a56ec79a5cdbce21861819b798c98d326da3c06644a4870ed890150fa72fc24067cc40d02d856ce689b315b63d1c92a1619fbd9022bee6a94e88c25209fe68446a40b229005f70a4e45b509bc78fcabf4b0995ccd9e46d9058ee6e0acf26349a9dc36324903bfc6001880736541f402c5d40350d0e331b822d84ecf43295e28bc7ccebd75d1542a6b137bee79bf053816251ce6ce89883b1918e6f879f1a51324801c53b3edfeb759a41730f928ce53d70eb3a01608931723075b9030503eb38297d38f3b20a444952e7c4054a0971ba4cf911ccc8f3abfa0dff23fd170e0cf0ddab25f5ce308178be9678a6f7aaee4b1ff45bbffd4e1fd85d9fe92cbebb576bf28851cff3924e5ebff27c1fd184c37d64fe767f71f492abaaf7fc96ac23cb479ad16c5bdb4019f4c84fdf17fd2ef546278f4c27f937a7c1bf053772a53be0e7886572a930ba9a9cc55be9190a61a23b51486c4e6c901dc17a6eb4e7626fa93a7051e22163f8d34ff8cb2f151fe5879fc8ec3f09fffd50510b52cdad8cad8f5d317614a36048f3391ce800c49bd09bb91e90f47091f96fd59c1a5a7dbf47ef884e14bcc756f59815555016fa8cc0dc3fdf9de32af5f64ee14ff57aee0a26cefd51abfea360c8984add837d82b450da5cb2cea355f7fe47b63fb196b8bfec78be2756ce9eec2cae5802662eca7f98303388efdb864fd1640f6273d59cf1f32e1d81fcdfeb30e66b9cd800f5a33eb1f75849a41fe574d2c5f0d4e0cfbbbd35186c238f2a7f064367c10594038be2e51cc942037a7c5f2b1a6866239920277ba1a65e7a60c4a23912883c430c41a09d8e62aa53795cea2cfb61624c836fb879a9574ac9ef68bb7fd00b5f3ca7f94766e9c9a7e225502fab91b99e0b8ed631c1ceb84ddb012b8e93836fbf77fbd56001b1f8b18b61a43872df64e6a94bb90c13e32541878b96ffcad4a8ea41924ef319c0a65e6e068b7317be246a2d1744682bed142da46dfcf04fc1656829a733f092bec40124f0027eea838a5409fb4e1fc1b8e1fc245973fa6c53fb9a9661b0c0f9e1ff7536a92488d09ffa3c1b5a1f0a4cc7fe88ab0960d04b17b4d8f79bde38f0b2b24f1ddf30a3fd75b853651b4e73da1c840a1f0fee536fbabb213292ae7b1ed40dad312024338167b60528609f15457ae7b6941e8ab01302c72f6db288ce77f7161eb35dcf89bc84d70d36ee6cdd0ebf1c6ed077bf5caba58ea497aff1d9b3bc4cc9802a7b4ccf2f125482d6e0d1c6c4cd9b43fcae01f82c0562a8fc3be3fc2e534e939fcf2aeaf4aff5b0cb7aaea70a5c1480de76e00c2b35b3a205851ff1265490d126f0df0614250de7dd3bee49b0b39e673f2e4a62cbd0dea0e1f0421cc345f63afc80dbc51d1901135342f71aa1d2d5862118c869d050726d2ee09fef3e1384078a916accdbdc7fac1bc9e4042fa26fb9fd80551a75c67bc8b28c6db6072d070f54dbbfcf7bc826fd172fb42ca356cb4c4a9761107e0214e95cc482d756a342d66b4b6618d976349b25b263d2ec79b53c7d439cb1bbf8a68d0bb0ad484246f111a908c2be6adf43360fc50e093c311deeaec42e0c5818aafccc6c815d3244d88999daa4257510c34b43eeebb2a608b674e2b3bc4c38ccf2659fd923c0ebda89cb2d767b7eb9dc727a4899a605b7f5722bb8882b280ae252dfe0a925dd002ea5db098bdc349b2d9a57d21210eb44c4f9961c7f89d136fb095f9e0cf685788fed0e2202cf27780eeac7aedecbd2568d63627683ace9f5b6c69d8391a6a2b61d18c967a9c050ebc93950dbff161e59cd6f843de895d14f302161659c0018c49e36cb7bd39bb81a8eeabbc06540c5933db06a557a904a3f9c56d9d0786e4118e1b4b53eca846ae0212f930d863b8738af580067a3ee5b6c6ef1a64c1c27215324817ae8ae9edd62d0af33f031de707c06431e6e2de6f00edd47958444e5e7e7de3da0349f4670a6aa1f38ed2300ac7aa606d111080b2695042cc0f434870bd3bf710cb5255fb557f71ac6957f04684ad49ac63ae19987e04fd0981f4930dfafa236c39610edfc18040e8fbd7afe72dfe70a04597c197fb41d998c8449dfef34e650562586b6d09a1cf9610559311b157de674834e0b051f0faa74b196d14074e5d78b5ac6fb2286efa45a9a1d1189ed4573fec3323dd514af5d0f5de4e4affc147d2af527a69fce7b5376a90a4d86678ca62e56f5cb250fe3796fe02ae90c4865677b9874c40ace6bc288daf987dea8c841964cc37dabac6b5885e52234433dd6b00525f3dfea2beebc068bff9f6fba915231bc603b87d7a227dece951b89e2c2057babef5c57559efa59b2f7677e183d53e718e978ca1df9137daf4359a884937b40596392364a48fa217255d2180817fea2d3eee3352533e66b8c561aac40a0fb33c0044280c7059dc11cd783222a481a0cc3cd85d985b13ac204b790a357a845fd1502e9ff07ad4c1b51885e1a2a374748c00af687576219f57b49e8521f7e46055b6d1ca53111570d217560686c50c58615b78c04c1e217d69cf5d5966d6a6fc20a0b5ad2d5c541750704a9a072628af962b36de467b563026438d740341e4d359759609402c1935ad60eaf6f99d06b02f8781af16b0cd381e06f58745309c882490170d5f546aa9221348173440a0a8f755f335ddfb2680a1b6975d95fee57ae0990c4714abcf53f2bf79bd03e8a8e8b09489dc474a958b10e62c9992caa0a5e96853d7f2c490eff718273224720119c453ae3cf13bb8b151dae622a1406ec6d2b00913c26423f6d3ad5f16ea1f1940363aaae7c989742b8530d7ebfc07c6cbee8dfb1d7911c91f1b18e570dec5182dde7846af6ffd200231b46629dc0b791cc674c0568c9e4ea6b8f86fe1ec81ef7ce5dfd505b78d36764fbd5d17cc2e357f95e38e885c71d3c954f17dd6209ca7d83975b9fbeed37dfc13966708922ff9e13ceed8baf7881c543790cb87f5d2f80643703c35c84351cec52905dffe9d645f08bede8eac7b3377119700dcbecfe8850b0d07b1a710a90190b66965916bace8ed1d34855886ae2e0b0ec88e0f50f61730a87d8a062fcd810156c26a3611373454756110286255659fe2515023ac282c454b70f80fe0629c2a5353b1802d35e33e619e4e4c06ff81420ba10bb08d31ba1ba20e252ee90ae239cbeed027d4af5f06cb7482ccf30bc254b19ebcacad8b580c44206ed0ececa6682e8f7ad65e77a43c663103b3dc23cfdcda54e48de000473fe9d6cecec9bf20dcd05e7b32b3ac8b71d522029809e537f61b796e185dc8d63e700f03c07e997dda89672a6e4f6eaf9058f0fb884ad6a6d3b8bef13f6fb76da64c2c90233b4455769bb06b6f3b7a91dfd753c986ff6157f6a7e653d96fd135e32b39aadab067499268eb8650fec8dc0261412928500763aaab80224dd009827262106a2c900a6273845a42bd3d07c5cf9f21138a82eb3b7b2be3b7c264ce752edaa35d423eb374da021dc2b617f189aaec1a368202bf297fbb6011ff5c0157e65cda8750b7e3adf2536720b8cb94419bca4a4ac02de87fc9cc95c1f64df8f02ea3a05e8fc19f85d63cfda3c9257231064f9979553bf202ed6582d1dcf75d8ab168ecaa3860cbc08f1f188a72fc24754cbd51fb5fac4f5bda78107b25d034480fa370074ce14c2f98e31a6340372b6a85b2dd7d1c10b65872871a8c89bb5b6d6291f1ee8c83118fcf4c6876d2cf246139e3c829f00c64132065c272dd26f5ac72cece4568586c93564d70375fc7d510ddb0e60f86a1f0adf11ff636dc17f622a87faa8226278d1085022421aaee77122a234588227dc00dbbe8c6066c38cee9bda5a9e93c8593b917ca05a114fc24e03e5775ec6095f9036aadd1e386bb00e4272617657d12658342e3426bed1b2278c556ded8d22ac052084371a8558bc01933caa1188ad95843ae00892f1479aca1f8e0c1f94654fe6a8e966fb04ad38c19ae7398c83f0b65d8d28d656215ab5ec576a3b6082e9125ca956472edcd6a12502db97b0bba51b225cbfc2e490961f3f3fed7880fa08b59aa3175cb616083a360c89279efbf5166a5c151d8d7993aa16adc259eaaef8c919bb1881ac1a2c0896bcaa74d5b72bb06706a6ecabaf198a65b58b72209d2378b9bea7a78b779289c2ec7254d75c3544a945e6d79cb32f42bb8a4f0b00434151163a3a4d593cbbc6426d369380a067151dc55c3c44113fe7b1e8da9612806087161ec3c75d206ea41feefa987d925e7de71b401fa6bbade669e311a6bbcef828393d468030486ad9d006611fa4aabdbb8abe9ba4aaeb9847216303cda8076fbd851cb33055574b202204a0c3c2c487b8dcd06b37c294674a88fc99b45c6469ab17d8585d2d106407b08eda019babdd79f360aab765d93f407d2f46c367854779e40c571bb6e430fe9930201960b2bfc186d20616637db5bc8be8be444fb7c952140884398ca66b1e00319a88c4f6b36451b2c899e79d3f4a8c5d5eafd4156c28b2ea970200342957ad083f86c10fb690b3b74464bbdfee0b301fbe685ddd420971c29401349a7a5e61a7472f3e2e6d676ef9ebbe86ee1cb50fde19a33ea892cc59b4222cad73022a4a917b128a7bd6b60622979bb85b2556ad77a382574afbcf15e4265c35577811ee9148a5989c3870370ef610b364f21914145a5122ddeb6f87950dd87e3c3ab69dc869e68481ae43b4fd0b36868e06a391770e823e6ba442b8b8453d80d2c7088bd6aa039971a91da348582c4e51fa7eaaa558a09d72d0cb232b291826038f626a44e4d0463751c787c021f6f0ad2dae04ca9cd1aa4d2466026c8622499ef7346d7fccbe7490f2d9df3442051dac959c7d784330b081fd3736317d2cb69ccfaa5f43e554e348a07c7a095eb4101278aae78b6991289f71e3a2357e21c2fc2e0e793a206011848c528fb79c64f53f219d9a6c5b81a1650908ffaa9dc7e9d7ac07c647aa10b15a8fd9c31be18ce4aca22f00499341521cf195c24a79cd18a2afb1a332650ab7802d8d1fe5baf5f42471019d50091b5ff0592954c789db462c19ac469a16ee40393b308b58e6f35104913ac56f5bf340e7a4d0dbae945c56cb7e0e1d9566febe127029447e204513d76f5fdc4a470be7917d9e972f0e2afd2430052b0a9f4a2dd4669f739f8edf1917bcea9c4ed0079fc420fc25f83879e54cfe90781411004cc33676ef018a598ccd600acbe051a3e1093ea39ee4d1fef0718bf9540164a5be16a1d48fb887b09f902676f91636cc4d9ee83a4ce38032c88dfa74628a7b1251732d4ccee2cd4ccb1ccdd36111bcee342406ea787c79ce397508e5fb2503af2b040a824356724e30a502179bb8c6a336c3371d4fc658935d4181f5c1a793c8f89eb10cf53ba5385894efc739781f1cc15a12f910dd0cb5fe0430a9ad4ad9e4569ac71aef71079484e34082b2df19e760f2a821145f0f7048d6f4146aa939984803a547d77255fac21e1c2ac6665f153400335f81a60630f396225f3e2c7465cfd14e6760b20e9d6edaf1bc6705f4cc8cf575e2c511dcfb48c8297d7e946c67bdbdffa73a0207c97c0ab0e262cb5a7c0b733a4bd8f326439cd44a81de72989a25a3d10853f3fbfdd1650b725039e2c57eac1650f9b10cc4473dc3a04ae8f4c4dfbb087ea1c1308b2e0fd02625aa1b514395d709b3aecae19c0c31c5d4c0283126e959efa474bac198aec8a1a6c53052b04857520e8bdbce80c7f34beab607e660bca8bb97ff423e8d1dc7537b1ffb2fe2a115d15c5bdd556cb94fe4475856c94c9e296ec990a240a0d331fd0536d0722cebe172aac5afaf47f25e366b9718e26bf79987f9202a9c829c20745c35d6c2e852c984ab202a6d556dcbc04de62c74ea6ab2212241bafdce3345136362a721c1c5c2019b6718b0a7e736e5acc00333d014d7ad6a56d040afde47d361f19c93947b37c97e24d47655da3327a9f594e46c9f43c58991718232130a546c03cc0c81ac3b12e2361222ec87ae6b40b951a126ee409e849cf57c5d66555399ebe38e0b95e8b2020b79363c5fbdf73e0ddf2d0f86165ed56acad1c83ffb6a1192ce5941e28d3270113c5100fa50a1ce683430c4301531c6e7b1de339a102f4b0e6b6db4f23b16b3fae68f4426b151783312d294e22f330dcedba593582e6af622e1fa298f97de5d2f9be1593b7786e33ecdc2a6ea96b036d7e882fd37e39681fef2b7c0ca90f464bff5e2a1933be6d8547859e972b94957afd0d0695ed206fe0b75fab9f04cc21ec242fb26c8c09024949feba9f5daf4012e25b6deeb4f25ef105f223b0c7d71fbfca6d43f5663f25c5ccf86c15670b0434c33bd77cf31563bbc19106981a6557d2e29088d67c468b07edd397aa99b1a6424687092c6ae79045722b4215280d7ac5f07cb1180fa0c91404b1ffd0c0dfcf0b41d1dfbdb1dc6e2575ad61f401f1f71827f541f9cac859a20b6e699450c1813eea20903b01bb8c29c003e2a4fee05091ca637f9e1fad0ed0227512a2a36bd7e63aa3f66b388bb52611d2e246c9cb591142f0373b06edf2c9fd78b7978f6783a1a7f090bf18bc5fad13bccfdc237bbf7cf2512431cc7c5430f313f0fd7d4884598b9cb2f8632099dfa4836a4a99296f0b8a347754e78c3e08ca9216a0423366b4e3067e76da5298f5aca1827f1d4bad6f03fa2db26c1c2349f32c0ba210bbcf835823c8c420880a0c8ab1b4824158d21b1cccb65b6167bd85c21dda6e3e50bdbccd0888b10144041e8674e3036d6a178b31d18ec327ee9a943954383479c93fc21a8fb543065b21654a118c13cc0f2c34c7889a633f4904931d5ae0d2405b973605be9cd9fadd91536d990f5974d7970239065addd0db7ac20f966b58c0cdc9f814d94e6ad31580f27424b7189e1d3b7976e03d52238ff767396e1c4f92966747657b04611fd9d63fe2e0811485ce948c095fa66ae95417786cb8a647c6074dde996e4f96d5200fa995f495de60bbd321bfd2ae379cda28fc82506666f15e7672fcf57298bd7c4b90f62d4ff8c2c8ba5ff6d285714264c6a6821324f29d794f9c40a74abb0bad2045c327a35cac25c12a6f60f64e035be2b0b92fa2ba60f9e53e183229f99d0b399168174bee85f673526349e43440e568724b72eb475515c35feecc644627f181723da4c27aea1a9d9eac2474b71bd45675dc744506bfab468cf8a0dca0b2b01c517c37ccef474b744b3baf5cc8658444a1c87c849f550b08a2612abaacbfee646a1bfb6a41759b1a0b44c0de52b38311ac897d613c277d5efcd100404515274294d4e93d9c1da388c9def1ba4b5fb66c18451f11eff5a5e4b25f62257a84f12aca9a08de493aadde18408532f7646c6bc75071a17773b2963e8863a8ad04f10f1cbb87670858c9773ef6195aadc4062e7f996f310e0f8a4d4aec1a157e4fba89a2646b5ecd292a75c1106a549dd58f3835089d5b00e074a822f49b00a10753b5bfff2b12d574d4e31daa9faf8a9c348e65b610a61d3f22769962ab6d4a853f17c532a59a29e36f93ad5c9db2ecae4ecb64e59a838b57e599d04719246a41ccf12cd3995bc7766229488946189c972b15ce6cc5924798dd989f05db20731dd2d2cd1f20a94c1063bb75b9e4832d7e84d6a1a4de7573e5673f217bab3320e470ad88db461ef4a5d85e6f00d782b8db003be0c420cccb532a782b8608a898f451c96fb7b92f3129d7a458547f8a928e373db2134f70a36f491a42164b162ccf162496645d1d8d9c4158ff59da588825aede81c172a59ac534ddf4a873793b168c1aecc059d8b5dabe17a2b865b1d32d0dd6ff646195ce9e5f07f684ff2394ce5f6e7f449b19574bf66df92abb02ace7520c1a4444789ee12abb63d8116024c62c7abb87e640e79ef8f18d96462fb8dd631a940e300c2ca9ade500ba0aae6152183d96bb6403537878613454272ed7867e19cf02678358373aecf4b66571b0f83f850b5a7c652e674ec8269e56348e02be54ccb12db18cd69b23767e7269993eeb8d8c5279645ca11ccccd3d2fd1da18d5afcb718d96750f6bc590184d1854c8ed27ae2798207ab7c2a3ae29907e1eff4235f7ebe8040cb990a7051891757bbc9593d08699c76d50ea016a7aaad68870448f34eefd45660204fa03d463e1c6409860edebcc3a27cb4b508ae45cf2d2acfec0d91f51c4fbee0c1bfa261c1c47d152ea3a7aff3a8f065a3e6b192892faf85a03efc5b5d6336941fdf590607ef8acec096c22f39fba11a00f6672fb08ddd888788025d740039f11758817a72c055ec1889daaadd3e73baf9df1625a0891e750604b302069619fc0036ed6c53ee25bacf4ca31e1fadd68c77ae542f70dab8826b00ed10106a541f99b5f54a5fbabebd0d991ffaa927fa78d560add28fe08fbd66145349c2e2591ee4aeb1dc256fd4ab7352f44310240b89fd3bfdb9d289fc9d437379c36058bf256ac3a63483b4e20bef9252c8484766c03d68ae5acc436ba65662a80b8ccb08de04fdb449cae5ce49c4319e60f5ce8fb4972ab858ad2e602af963f92054348b38249379e1ffabb2df5b9c220cb702f92a7062177efe5bcab725c5e53943dfba69f310f71d429d497d96ce300ff1bb5f551f305fb3492fca17de04273f2bac0194baeb838bb000d2556aebe7d40c31255ac6536af19b34a8a3a040718dc58f167d3d16551c87a017f87647a27562ef4830a5c0430b673508dd90c55260248731939ebc4bdd7abbfe6e5e081fe0009e79371a580ddf7e0717cb251225f42b267a87b675ddad92aaa3753463bfd230b8ca0421a22e61f57bc33b0d0fa1a2524643ea262cf51251f016a331f29ddb3d8749606f57fb6584b77cf6b90f742b7c7b4bd0c0161db3b173bc0ba93a8f554e5f09caef6ac7e5139112a92528ea04e0be8099047c3e640f55db57bd8969c0b7b5a99feefff4d60fb8fad07eb142c21c519129a669825994fcd0acec5602c9d0bd3b59470f8b43d8c0b730eb4b5dfdef619b2c01729bf2d596bc9125db0774e08a742d1a6cbc53f436bc485b1ba429198ba7cb428f9bb216e6efd17b20676218157afef3f81ed0734e471f72d90250c17a8dd75614ed910756f64d01f59be57060ab23c484d8f196327c48346d1929ad0cea5c8ef4e918a2584c625ad33ceff46f8952f4d7607562ffeeeee633de6d7a2fa421ef27a980da427cff4d45f5962c1a70d6b89b8ad58852aab47af230092d01cd60c98321b4aeddf0027da7273549306515d914fbc4ce5cd25c43ccb5ed24379381801557f19e1d7d2e14ee0e256d89ab0a422915727d16cde6dd128d9d03d11b51d5c69d7135534ca6aeed8e5b05881fadbeadafa458af811c91757f29e1cff2a0c5e12ee76c5f5ad9e80c4d7798fea8bee7b8a3dca2e66e4ae3980c203378dcd52172416d9855a0d0038099755298a1ae0f0b86acc3392c5eaf17c71bde76a3e32f3963e470ae263c00438661afe371977a22442d9b837b99251b354b08784bfbc3e21cfc5134a2c68a94bfc1b8027819581db89aa3763b775864d27c978fdc8746b1ea82581d063026a2ca293561a8b1c0decf1076a18aa4fcbe73837f959b7ca8216874b03103fe1a43ec22340878ea697d5d770bb87ecabf7d56bace48c11aff6a9b08309cb341b7539e3c707fde42f72c2166a044824360e24638c31065ad1b82692c0521a8871ed51ac032dcce313f6510e0a4160ce97a3deaa8e68eb900d87b7c2726ef86962376a7e36186fb9175607a6aacd97b7c406c367e85bf8f9a6355da3e167b538bec7bd541f191b1d2a0176dc2c4832138bd293b54c93dededf8410af2f72e0e37c4e033f3f8980617a9e2f4341798a5ab15a798d1660d4028dee75adee4d0af74649470a228571e9d94b594981a70a84916add37da6026c110e9bfda93072314f35a18f956a49698bb5fd8d12cb481e51ae5dbef7b5e53222c570be1f9955b4fc03d825b4061e195b5ffb07b9eeb3a750f40e848b817fdf24694282d0e17fd0fb860811cbc7565c19fb4592545b8fd971aaaa25aa548479b6a5babdaab57ac836201179f129a1333f085388eb3986fefd370dc56b88265e303ed6c0c0c53fdf443d9502d28835768ec33cb110f7d9dd9ad3f01c39d6585f8cf0b5d2aafd279c421edad415dc82ca599c8ac29caf5d6435e8cc7f9a52ed4cf5ec25372c543812469789f28fd8d8d96072da1262fc9b892ca149acd93dc58f18fdcfdf45728238554a64821c356a1ec56461efa9926f4a5e509b7715c341f4eb15237c60eb4c7e2b41340a0450b4d1c0173e7dec2bf4c0a7248206192cc37916e860be965b151587766cdd785fa005d0b20b2357bf7717ef8e240c619e8aa01c627b16de979b3d56cb8191983cdddac1893911d6b4dddee1591f6a01126d65a492b11b074415e2c07590dc3f015c1182b92323d2ef456a1abe24fdcbc12cc4f17b7734be358be389844cb19537b0527a42cc7a685c4bef8e4fa2991f0bebb45d38ba4f3f44cb51b3cead60c764fb02760c51dee1e32a3bb671f67585322e23e86173630c0413383e7335d77df0a96d0ad6a6161aa14a4d4815a3d7f6cb74c4337da781761fa92a77e06934966e735eee1f9340410151d5de5f0247908ac95e60d67ecd78b9e0794afda2c47ccf319ba1389eddd974eb73c23cc763b884de374cefb9060dfa5ffd031ecee0b1119600ce1d2d069986bb1f099c0b036b1962c54a5ab693fe44675bd83850079da9fab5f3a073af9cbc2154779793ab22e7225a272f7dc599386f61ea1a2c148142d37150ace6786b2ce67caf2fb90ff46b99a616fe544088d99d43549b818211980f5e374d0aadc7b38ba7162aea7e785548cbda4ae85fffdbf7afbbba4384c634bdf9f97d2f1f5bd540689501364980a0e47b9e9985154f91ff9407dc2437901b9ae648fa30021fc638ec2af60846bf3123027f2bd07e508da449c3a90701a4536e0781313defe21cb11482933251afbc50b9164c01b75f9327e7ba850d458f7c5c27632f825c9c4585885ba9f735b8ba78c2b56740d6abd9460069c5d02aa71814c5e4010424e079c48509fbd9d87254ec77dc26891177202d14e2a3955c91b46bf28b6354fcc4f99f5ea70983f81cd73b51d11e662407fa4dede007b5248697cfc3e000c01690dfe98597fe37b1b3714289b67ac7dc86f7b77db724b99929429b704f104a80477766dec707148a66463078f7b677776efbd3317a14c9d973017b219ac07180960383c2d2ae2869033e9d3592ae188e082f2c4154c18d8d431ddba278a3c9564aa2ae6d9e83bf2672af98ee9c7dab1c88b8a6e2fbae8e26c36b3b163cb8cc717fa640bed2189fab0042d82c160303aa444049986408623f758866c48b52dd2e7ad20b1e5615e248aa2488b6614082ba5052a5a24d222f145175d74d1451769a92864a2289b55516294a468ca501351f48fc5542a95ab602caed2297acc251689f090cc61be63c23277e46c478e335a98f9656eb1b9154571c8771e3063642fcb2de2d0c762624c8c4df1b38781cdd0bfc83136c4846886bea585c5863445c533994c9c899fcca0cc4cac391363be3391c4c498288aa29128c3c58484a702134a2975efee9eb5d65a6b952d1a5488ff7f57ffcc1f49a3aa2bc4fa081b0c8e87367c17a539b42d2f586bdbb61582a769e34c6c7928d03b27744ff70a3f9a2a5dc75f69cbdaa75572b6eb68784ab8ee5adf29ae11030c8c90439c40ee20ab136c249bcc1df9d32f2cb37665eb8f9b95c8de9de04116a315118ac9623c82c631a8729c4c867f51fc9ab5be93a5a4f87f7f1434e0ac22ee50074ef0f6f696e0cd04df91220a5246ad76fb82c2ad0341e4d7a13e35a48c897bba19a48f7529a791283d294ad143a186784513bc6bed6b04293d51fcc21c9b0a10701cc7b54c06050f1ebe834307997ee5e13bd68e76a45f47aa82ab64085f69c4b2d609a5b4d24a7311f1f0f0601ef9f1d42aab9492561a004a29a53cdd0326b96bd624ed56332cb72ddf0e494e8e0b735a9f0b3826332546b20ad1134e26eb3b6b36c8646fefd4913b3566f774cd09b87ae2ca2cfdaae47e7e5923fa407804d594a83c0de2314390134ab2224231598c870cea710cca525a60c4590b8ad6de60b797b5d676b693373b4d34786a08811ea0943efd1e57e62a3105d2fd218b92d2ffff27219aa064d908c857a8f29ee5d7e56971dc45e9b04baa586d569bcd66b3d96c369a1a96bc49cfa3fc87abc96469562b666a561af8f1bcf6214d9d37ddf6530d431d3434185ce9a8c1290f955aa1c306a3b00dcb632b3e8cc23658e8c3365f0aa750df89357540d64b524cc3bd3c13cde42c57ebdb8fde99424cb57dffcb9891dbade5184b7932cb32c7fc566b6db5542adab79a41a81431f43385101942fce1808f98ec47ce600508db8f47a94f4a653fa5f724b11050f020f4336f3a1f23ab905c1784c8f94ad22709e9c3e15aa4c6aa8ff69161a45cda409eb8d29ab5dd51a5846f79625aa4a2294dd38fb5f7ce94e40d759bb7a1a10a0299705da247f754fc61be4bcb98d84bc798a69fd7bcbd8cb1623725a48c892b8845ca903f73e63b3f676f4428d631a69096f144f20ce336d80de6dc57a5acfa7f251ffa2e3ea890ffdbed769bb79c6ab3b1c1c3906e45c62f5fab1175cd03305d92268d0680182e872b8f2d1fc4b1a4945f721314aad631a495ae8d58c669abd97258695d6925b55cb45aad56abd57a28c116a19f0c988e1065fe020c068b113d293214246b2294e46130a2144e5c180e3da59340c81e2409ba2f1216ac1949605316a1425c6bffe9ffd75abf4787bd300c433725d9bbeab24dee2a56fd731c676bc51c0854bbd51213ee31752c36b1660fdf913f9d65e1e8b85c978d2f9c0dcae1ce22011934f0be1328e528e59842a5505c9696c6a5a571694ca152282e4b634d0a75023f8f86e6d6d0d0ccb05e1ac8174b9c425dd673014f2770e9f34ee0d2d2d228e5f875266b39eec32fb3349868e84629471a6818979696c6a551ca51ca71695c1a53a8148acbd2d29842a5505c96c6142a85e2b234a650291497a531854aa1b82c8d5746fb2914ed5228da9530b3657574e9c7142a855a1a1dd3cc9f203dddfde481cd719eed4cde079e64ee0c4d0dea53a9e04085940d7e1256b9111752a9542a582c52ccdb54e23a2bab54e645dc572b10a8c32b160b9b3b02949cb9f3e2967164b1484d2c361b0b162c6e5aa06646d3e8c511a9765373a52865b1c614681fc78202064ee81e19cc005be31cede346264c1daf7b4c4832d913a427376496127b0002cdac58b0bebf333317df197c71cd6a0e8140175c32651f8805fc64bc930dfc6c349b5846486ee44648b22732d993f9a4a8a8850b9a24e24c9e96a32f5e80408fa977b893f867d8b13a09674448e845786791196bd10204aad8c50743465e94aa03b9924996ed6028896c68064dd88b172fc2ee9e53b646463264b676db368b5c33cc0dc607e01750e9839c6fcb38e6c4f87c25b1cc70da6208c08454abc9c4298aa128abd0aa003819df0c10acab6b309e369231dec4681f37cae91e0efb282b5a2289aa11677d947d93f1998cb229b359abd59a0102673b292e91424c868666cc980142081505a51e4ab2888c8100020821d0a0b63361894b450821844063004ff3311c36680cc05fc9279938138b339c414e3880afbb71efeaecec804016ef002db15baea44fdfd626423718ebe880401dd6010f309be7abe1c9dd417e98e3cc3b753adcb58f8fb64da69b4df6954c9c297d1ec98bff0f7b0ef8f89db4f371a0e2effc8162ffda7bed94d96030189030317c12872d2e32cc185923e50e294676982c5b0ac9a59a8d1c6292a584c1602afc4813c84c79132365bf66912ca51c132583f8a70e8f0e08b3ece15e62c29a3a17e46e8988dc81e92a625ef85a9029b661bc84f1c0b54982041b488211207b2d7bdb9eb20ccaae8a8d62d692373f4431029bfcf8bd10f48061c10d6eb8410c90c01811213f9e3539263ffea993b22c14a16acdd60c144262160a304240226c322aacf049999d5a8879451481320c31898e636d20d2d13c2676ca6ce2346de2f494dac4e9fcba2a13b0400465488d881f375800a302e48614252b416644b622455b84dc18628c727362c80d22642a03c06e298068cd56b843162a4a64b1f242873b933b4a96248cc8d2c48b24ae4cd08b1bdc0bc307395c9a1b865c9413183029306e23123e8c4938398c4e44318a995d530b3831dc0fc90c1fb8dd8a830b19ee69042c48fac105946b734749a221d55e80b99f3b4a12521049361843dc2f7794a42c2ec4242fb0580e6aeac8562a779425885a4bb557acc8d1e10f733f2b28ae3ff247822b747c0a8e2dfe562958560a8f6b4dc04b695ae155c5dc63ffa16354faf3f3d8621d8f2d4b87e31af3a79f748c8a6b4caeca99d18aca95b439dd87f45e36b7784c3127574aa10a787dc34287fb0aadb53fdc0659426e63990f4ecab55163864bdc76f7fb7d79369e9826a960f1be6e60b8af36aae1ee3e6407dbf4e1f50d0b22aec4ed3f6e83aca1dbb8c674406a8c104c62c07de34d164bc96d2ca7c441e99dd6ce1e8e54a526b9a2f55818cf6a59ed58c2ccb087013e4bdca3d216441b524a2965ad392d5aa7848b2eca66559424299a32d464766777766777164700c8159f26122560820387d8d0223ffeec0e47bcd793c58911a70991217f8b204fe495ad159f6007140c65b9c188177e2af5916badb5d65aeb10b533042830a29fbfa3180c29c84e72e068f2eb60554858d48f2d41669828aaf9e1e57a31a2f92166888c6d26489153134a170621b00922991b867cb4930e413c116c126412bf22221d0e2f364f052d58946ac871da8816645758883511c3d145e9080814457028156d40c85864051c369821c1d104ed28880788e0606244e11544a31caad150b8d9b25feb2c094c98e4aa88b1706d6cb872051e82d89e1283a231bb700205930683c18e44d1a202203da50363a6307eb860ee285aa49440cb0b5c50b95cee285a6090e40a102d4ad0aee78ea265091b19b4b828cd224c6389e90ac79de30548dcb152e852d0414787254ee1c76311e6be46c8d415e4ad706cf10a67e998d8aed0f1987ad8b9cab231438a9d5563b278b82f55b70a5f61a68e5f2d17a5dcd13da92c318feee964171e93b58345140fa878078bc96dec49a1e93d4154eb110cf4e8b0ffb77c3c010d645abf8ae90a02f36b4fffc71d6b8489232d0f0eadf4516b25eaf6705f570292a7016d93ba8bdce188dc12db2c16af6fa4cf8ce57eefb4923f3468cc90051357e220b7cbafcf7d5af54f25e276585610d79faf62ae9a2a06575cfd5692c94a12cdb062d34a369961ed1d8e533b9b7cb2c4e7cf4be6563672a1262e4dee23159a6489714c706b721fa9600308ee4cee2315845ad0eecd7da4028c862bd325044209181db81f0c421e6e97fbc8470b4224d73b7231c68710889323372a37dc0f071832a8618ae8c3f5721ff958c104b7e63ef211a44596cbe53ef201a3b9a37499511bb8a74295c245a3e570d16834174d872b878b46a3f9d72e1a2d878b46a3b9683abe3f4551dfd5c7db88aa48d009aeb9e3a2d1683453cb90728a0aca08e1c4613b2146feb8bd132de68477174d872b872b29c9f522486fe58438e10c1180c879e9fd2d9ba391ed235bc68cc9d45d8a31ae5f07a8073febb2b56b1b756e34da08a12bfc2b8e42152b57925cef2e9a0e570e575be92b9df4d2c33027c4e9300c3f10fc4cb7939886735c2fbdd58a31001f12bfa40b395c345a0e178d4673d174b872b868341a0d575351f46decc499aa2f9c2a3c2fec7a2fbd7be68781a9232fd05d5e49ac42a680219f913c94deddfdffff9d524a29a50fb8930419ffd34b7777772ce54bf9ff4effab1e67b2de75d6c479edeeeff56b777fa7a07cc09ffeffc6656c58ab52d15a2b5d514a4998f7b3dc8dbbbb4b4ce9af7000e4631266f84e030306954c316d3c95f3a8fceaee6e4b6baabf97af1cbeb057ba12bfbefca29452fa2ffbcaa189dc8ca59452eaf8e5b793c194524b29a59452ea81df077a4aaf97e9a8dbeeb7335fb9ee6a739a46536b28eabdc8eefbfa724f2f956259777ac78425ab9b6567284daba69452fa1ed8e0e1779f3add3dfd294f6d7116f5594ae9d3cad98e524aebed0429d64c1d8952eae269e5a03ecbd9f99da9ffd27f7de03bd269becc75758cc63cad0f3cc9dc194a29d8dd327786a60675bf199aff477dcdff7f8f4acd7c3b5d4decdda2391da331a594524a2b4d0d8a529a9ad286e2d777b516d72a3357ffdf4bf1fd972b255dbd5ab955df2b85faff5e2bebaee28a1072ba7b6581edb66ece4963ce3973e6f4e9d3e7df64efeeeeeec972ef6a55f2bc39a7e339fd954657ed7dce9f52cee67467bde69c73ce39a75fd895787e375783faffff9cf596ad7f73fa4c25c5e0e7513c29ab398eda315f393a865677ec4febe931f8d9dbe3563a660cc5e17dead0ee6581ec983eeddaf5ed9873bc9646e7bd3e98d8c3691f1242fbf4c6f3f0347926fbada6d723db9b2debbb9084d0860b870e04341696bda9b9412c83db08374f4b75c3803bab9532bae72439cb8194b21e012d63e21bd646a57d35dbf8367aeac84a95bca3bf338060a6cad10c3c7490bf07c9165b9e34501d2141fa37412808216a803283211b55e194ffffc3c712875454ca01c8145b8aa4859b2e2f9e40e7883f715552c5c95083235fc28f5a95e106a2039951f10521b38420e28b1c3822dbbe8d1995109bdd2391680890a932c00c63d783aa735a29ad1fc470054a7e61a32b5492ae8c30aa5c8162063e821a1331519d1626ae787de3b5c91b00305c4e04dcea9e1499c33cddd399c31cc7610018b935b738dc608bbb2cf0e3e66648cb6a750cae95396bf220777409c575b524667f28e7ab1a8ec3e15fcd9793c3129ce1309e3a26dce18a2f873d0f3768b2d85911086fa898a24409b2ad768616638badef38b6f8face636bb15d7140826bb1c53756646c599cfde1b0b5788696e369cd90c3ab2188ec0e5ead153f9ee1062270936db7188fb0a0851147a0c0d8e182b98f8ef03074bfdc4747720899dccf7d7464098e0bae29f7d1111a5ccce0080b2e5e7069c2e080e0aab0d4702dd20b1a2ee782704603c2881035301244d1f5dc47465210c28c14d9c8600829bd69fa899a4f2557396a6f3a4085bee3d66788ec58eeffff2d27f32f4ff3ffffffffddddbfcf1de3ffffffdf29a51eedee6e53edb8eeb6ffffefee9cbbc9762050e37777f76aa25e77ffd7dd0efa9ffeffdf72efee52e6ffbfbbbbfddd5be604de53072e78b2bbbbbbbbbbbbdb57afeeb2ad34cf39a7bbcf5ebd26276d87e7ffeac5a2cf9a33ecd56b26b7cb4e8b27bbddddfd3bb7dddddd4d83a3ddeeee4ebbdf7ac773ff5957a652777759a9bbbbf707fa40ebfca4ac5fdf7274e94afcd57274cc3fada6af6368fda1e72a1dc3d9aecafaf5e95793f7b4f6f7e0385a9fe3f073958e01673f06fe7e4f9ececffda9d71e88b912d3ff80f5ffb49a68db5775d8a1b5d60567bbdad3e451295d5c29c1126696ac3779ef792b5f1779deac6eb735752bef0329066fc870ac578ffc2c1a13ca4df8815352d938373ff89dba6350fa2c21acb5f7fe0dc2ca580b42ae500d342ee46082792f147102222345e6c8080c34b2966bf04f1d1f138d23188e589c2c13832c834f2090cc494b11f80211ca046c054e6ae311413c61e445952317828e8488744cf95b1c872d132d20c95c01321d8283231686402ff2a53c62416c2d31edac90447eccc18d122b1ce5aaca15ab00816a132b5635638523f9710a10e8392b5768e48a6d40a07a332b12829d5c71aad66a73ad15dffa43ae18d72752a6a88043814095e21a2247e4a7921fd38040bf82232235cc1459f98b45e6a60643029938a8d4ec5de88e1136400013170000180c0886c3218118071339cebb0f14000f4c783e5c5c2ea48bc3a1481003290a63200662180620c410630c610632a8ea08003bb571b1b5d28e6ab92538f456f188d0a93bc2db4e411b91dfb7b4856b84d6e480282c8c87b213b860ecba34d473f9b735bbfbba91cb070e27583551eb3da938d2af070a9dea78e31d45ae15bdbc4b472bb0e231cad3c4b1669cb35b5de25156c939cf179ee56638e10ccfecb6a85f71ca3d35163060397332c2b945e7be8b899d412132c255c1f44537de41fec2f8aa318cf66b4d8990abdc9bc6ec35b76dde7ac80fdc36a53e9d587abf2f40e01e3970815631636c60ab0a1156e1d4f90f3bb46b4f06b1e5cd616eefa50cb268764122a6fa3d35d117befc737f84ea8b99db2c2b047e2f1df8604004e03229edcb7c70a7f661750314339dbff564fcc0e9c9a593a0df6f6882278635bc7ad7f242e6bc7fc8b650b4fe4b03d8bbb5a6a20cca8cfb705ff2d320d58f8250d4e15356a630b331527f21d78f0b09784f078618b31f88b506c5d3532bf9d045cc023420f54490e05374bdc7cf38d30032a38b8a40b693497c3e92b0198836f00edbe47f10128bc16a040f0755a466aa3360164d6d76a313e9451b5ccb67e7a33897cb09c30f710a3d73ad16548444cb207e87903444ee736dbef78077cd2f9adc6e9d6d3ed530e1a06a5634c4d4df1e24d7ea482951f4d033d2e2eddd15e64dead564dc9fccfab1fb39bc2e9c18b095510f64e817100070a66a3277ae0f11241a121fcb9dae7e9388d17891a97f1a180b4c38bd1126224e5b5f1888cf60c1c7c3f6c8dc5c77bfd82b912b1b53194cbb4a049af1ed9f20d4ed3bb4663028c538739f5839151ced87d66d92105c67058f6ac9c9f2c29ff0bdaeac1800e4455c7948ca6556a6abf7ff36e1a195a095bbf4daa9614e0733785d569c6900a7456c9b1520e50b3485d0470d4c4ffc09715281b1227c1a850f98634100e43b1cb279f1ef91e2a0a7458833734d31443a01cf2af6409bd49ccda11d47271ae8d5ea0e2b1df7e0d5b9e358ec19838921dcbd474a9b967b9494b4fd6918deead420956a79bbc567fa0f0f1736d0a0c00ab61bdd61fb4dae7c1c1dd7fc32c05bbc67e5e4884425336fc30b1fd3be75c93ed4f1222ea65960f253ca2846d2c0f4646142dee13f2dd0a4587877bb31b64186d40d1983b70503c40c61f9fc363133d3bacfb6e5a6d6eb386ba0c6d09ed88f1daf1222abdd5a1fc352bcd6c50ff4dc9dd38c2c09b5f1fe02413f3673684382a1dd44ecf827e7fcc956669913b75a0ae217bc764f8efb0a243667170f49b01417109bedef089cab7c89c627a9f7a7cd11493dd61ad6b4764e46300cc5ac8eb08f3b448148dd44a212f5437073bf71efff21b519d91fcd1729f4fc7684d2824d965102782691e37a21667a4a99c44e0950d4c7229be3e1faf9d1031a03029070b454a7e32543bef3069c915bac9b164dfd320658d64a7573dd8875b1376feb28e972e1266fee6bc5c75bfb808279c340a64e7c16e01d88954b8a5e38e6411fe04adcb71e5b9b20d20001d2c3b3489378b067930d9d171d2166ce7b6059692dbc6b14fdcd90ab2ada6cbdb63c0177a94aa6ddeb62a3139d49bfbca036d955ca5d145d29616274e6074620e8250891af575cbf83f6842ecb19b97a1e8c4e684d010dde9685cccdc21916e103fb0570e37e309a892693f9af27a698227b21cf152c854629ca0b34cc9a11a5b82278cab805ef2a2fb4aef2a3de8f203ab31848683d4360d87393e574bdef9e349ee0a42f7c1e13f0e620d005ee4364af3269467a237b46bda1e525da1d3df2495a4cea457a4b00acf0597da411502a56c7e9d3cc79a3f1d3096b2ee52a95aee1640880977a17450e8546d5ea6f0808b0c8f95c8815b50ab034236e2203c299503373d5acb9164df38d7cf932b14842ef4986b174a9b2e4b87f125d2a8ffd96d5517aecdc05c5158177925257489e9d90bc2cd2d0a534717bd0b2e8f6d4444f5464642f883294c9468ce820cab0c1d66012be6bdc7e47b871e7ee9bd53748e9f69a75a1895a1ead7aaf550686e75ceb529576945f464141c587a4cb3204891a18e16122232c485208b71c93edd5ec52daa140c96dd4f0b30dc7845f615e34a5a515e7cdbca068b8b1767bd0a3711205ba86ee9d3466d51622022cf2c34f7df617bf6e2ff800f9fe85e708b8417cc4e8943b75ab91e2f0117867ceb8c38142b34a2ceb90167101b8de441a66b77fd6e96f6f826d3006654bd33989cc79ed8582d574a764fff390e2e84a2aaecc89409f8dd638dbc1c3b482b9ef0f0c0505acaa620c53473345c9b22df075a3130245c7c962263f3b1911645aacfab545a1874317034359eccc515a07d2021d340647c1d7cb00555ec111cf988773c93042087c75c7cc67b354c5d2b3325c5cc260cf05280f81744499cb1bbf2556b89e3866e94f2fdcd9a112cffc07d51feccca81b3640ba1224109303432c4625229f5e8d87c620118894ce78261509d979c5a7b1285229d5c3cef4e688978c40558fd2b6350d0c15359c7e58d328a8326c6eca1802fce969b51ac6fea73488359b3c192ff8a0ad9862a79f2cc9463e5957bc0008058878fb6f122f4dbf4b649f0e61cfa6520f9df547ed9cece0d4cdfc2ca233347ec2337e9d0bb503c31b3c221c7080c828dd2b5439409105a0ebf1bfce1bc31032d85921c6443319c7b75c39cbd2729c9d510ddd074fcf1c422cd6adf9c80a7c3f4b091a699d1e5bec8e3bfab5201c630048b68043c31f3b66df91beae21889687aed006a7d745e82c50943cdbdeac4f3ebbb56b81c14922723d8f1e163ab24604b350a78632168fc3e30a1ff41d0073f52684cb9f089405181267286c475c016b33f306eda46ac0481d4fb529ed3c348860a38e373818b30e32f97b8bb124a366264276c5c5dd345b9b1fbede1ee200dcac8e02e2fee8dc9537f0c9e715fd6f6554e15ff007cbcee52bc1cc1e088283179a5adc121dc5e240b593518c8defdc1a41a12ab8ca33f19833ad77a39d0d80c2326f993ad123838a40757650782cd4660d175a2ec5195c84a008d94d24a6c4280f6eaee278807d2de44401291366e1ffb067dce109852b637b10695bd1226258241652a93be4959341cb5131f29e57c7ff4410f8551044860963b1130428e467c38a12858bd45a9504565d01f6ba0c901a2b90acd357d4e0094e1bc5e08ee580141088ea9d41bfd321ee81d8b055e759ea417e1ef71aa9f3eaa2a9d730b028a003cb997062e0cce01c6842ca64c94693d1a10ef3d0ced97afc87991acf5cff9fc785f2a13632caa1976a054221de0d73272465f2ca3eaf7f2be160f23a6a61b8a24b5b6b2283ea1ea3bf40f20c75d8de09753543dc80730bb09e6df2515280c52e3a4d0861da1ccf871f04f18a15cd8b00ed03fa5da1a527bf617a5d1e071bcb33e5f66559a8dd31801770a34e0acd35cc1882befbfb885f41bac8ce8e1bb42b18c16b5d28ac308b12d90a1c20f01c405c8e8f5c38a8e3b09bb614d92bfa3cd07b5f55ee47073e6601423ae0d1cf349a2eb1a6c3bd4043e8b156d764571a3b1eba08519a686bc0ba4ec4af54ade220078b463b1cfa3f5051854869fce246e841b834ec0683c589fb5bec071f3642bb2188dae89042bb2cae7c6f76cd42d1054316ae824c36750f883dd77d3341c56f9837ffa0c86500de8a297ab5621bc4162ac042093f2349de0dbabe6e93cc8a00978113b173012111db42b857dd4c08cadcf8a9f89443f87e9a8e5f46a5a5ea26e805729e8b8fd2053a2b84de1f9794b99dbc9c076c166d61b321b68ac2ebae1343e839d7436e9b27873c26988b3b56eed102b978223ede406d382c081d5458164edaab6c6da1407a417eb380dee5721459c8e641d68a30319bd2ab71c5487b42b07d183e763c98001f6d913e2f1a2aa269bbf0a462c8c10ac7825ded2b39c74d3e2f344cb2922bc3f61506bff128bbbab4e6a70257b0a5b5cfe7ca82c838bcdd291a078233da2638400e2b1a1a1e592ca09c584dcda74a0fd30ed18dc5121838921208750de5ebb6442a13e42127b3881d997329bc0223bcdfa30a29430c59b430f5731578275dc7a8b4e50e2e77d0838cf293122a9a680197c2e03cb49b35d70cccfc043cb5876e4176adb22f4905d8bdc2cef495263851d456c36deeb236c50b70cf770b28380ec20c2f6b9f9e4108759127261bf535613e42e2a3767e140bbbfc703f919427277591623c6141a4f50583eefd812f158edd7841af0d01316d1bc3e8631a501883b7328caf40244e9f005bf83c6a7512a4f51ee94de344602d96f05140fd769a3c351ed80d9bd7030dce6b9c0e913988a4f0b2db1dcc8feed0dd28253b91e57d19330738ee82f9cc5890617a47ad0bd3d229c701749d200d2eb8063867707fb3466ae9a72e7bec7df4e2baf69bf5f6ea0de4072a8c3280e008afe7617cf388fded2bc9411892cf9532963c4d4f0a888f743a98f44ecb7fffba61941e9989f0002fc604a06a4c00562c8462ae47d433040f0d1c90891ce0491294c4c5c985bca61919ba3127adfa366db86b6ef2433397c862ede49572217db7d3b21596af8996ab62446dd79eee7239097707ae3610d7deb6435c9bff8e6d565eb652904d46badbc1f11e393ad78ecd64033bb1594921301dc629dc529fa815df6601c1eb1fcd4439dd9a409a59c8bfba4990940a014137357311a94a5e73ee3ff18abe813048a9e0d483926f9bd68e3d9e5490b35140b0bb5e31ceba77ff8396bca321f38b385b902f493c3a5b5bf7e9afcbce86fa1e7cc3e297270f6a68952e9fabd6377b0e946901ad59895ef6c34dc4aa56a899aedf3fdc0d14d616fb08a5ecc89d2b333764c38a07faa87460d064f557512717902b37029eba00d92a44844a117b8374d5153c65346b3e22ebdfd1c20bcdf030ae244c1770ddeea3a86995bebcc219107f897339b5269ec8e7cd46c53fc1b0521b36f3d7c94db3e2675aa9ff644b4d8ec76b75d84f9f4ac72cde4a7c4e034b2107a7081866484a7da76656423629593a7ca81c1e9481d0d41967e3cfe00b44451936e111ce5ac1c2c64bffe56c05688cbd1a1be7a025db666b0d717ceac7e7f6dd15347ba1ea26f8d3221e68764904a5d1ac3792eec48b8d652a0e3fa21a4eb727717638d83d3043b3e7f4f32560439dd21b809d36ec81af37ed0b55d1b89a757f790d1a397846d84d5b46696a3bd6a6d12bd68ff7851486b0db7e628759e7e297239afcd881d99ed3234e4a13de9a74441b28c7691985d35ef3d06ea8b388adcfb0ceec20831ada9eb4e3f7e7e6dd1af5c29b9897ae57dba4e4714a3a5d391bb49c7672602934d403e6b483f49099910ee98983ab6eb252ba4267ebe742cd6cbde53de7b04cce4cfbb227c4dfe72f2d82c7d65eae4a7e3ec8cbe9afd0f97d1a2f56a1130635012ae1222b38d216204882ca83aa80131625dff0abc2feef24b82846646a9ea9ab2af577da7a720dec7a8afc11a70798c9fcd702503e217f133a1557a6aeaad46f3f2ddab59050e300515f8e4c9bb06a4644579f066dff9407293003862807bcb2caa5d618349f7bd81299fdfde77c1043f7777e7fd5a0ced70cadf7cbce5588020a266fc1fd8f6c8ecd0f50d3d056eced0e95c5e6c00d8b4dd0448adacaf0e69b5c067607177c61748bb37c4ad6f02eb6560146352417555edb29d5d1616a799ee5c9fd5c092ef97f9bc92646a9ccae6bd7324de5812d5d5ab96a29b07aaf86a66ff0b7e4dee6fc8d26a1e92e2e317991507a1bd22201fd552177d1e7addfcac2d686330095d4be520e15e94cd0903b5b9b1bbfa03edd82dc1871c9d12e8ca1d6a4e6fd337b3a64db7fe896f2bd2a525899bbd62b6770816c21cf0389502280194cd9f91c39083da68306f8567e6f70adfe37e4097225d8a57b8edafce75be8cc88dfb0c4118ddc5df05c32d406edc8777dd0e459e4995d5d1a5020e38fcc2c3047f134023aed8328e7293dd1658bee72a3771b40f9036cf06c8c644b5e5334dea15f3e991bcf92ed441ed4081e5a1c1fb9396dc9da999c6caa5bf237ad0468633090dadcd9b6eb53a635d3d92e8f572c9cedf4076e34601040c3797fd1b619c115b48003698612dd274322a0ba5e6385c499cdcd04d7d96aa41bcc5d958f03677d18e9e0c858c7925b4147a189c9909f24b62f1855db48278587d726238e474971208fd2f23102564415f1621c31b5ae7020799b151f659769a7e71f01baaed61567404a65dd1c2ea7adc9550aeffff50292fc1161f0b104cb161ea6a704aad0f460b244c109e476304daa3c2b3898c466e574bb6b08a5308e64c2e194f3d050d85b81ab4219fb074758b2bfb847047d8dd29fa7ae882752bc52570e8990732144fc371222dc249cc9acbcd1613cdba85355700d5243166ae8c798de09341d44041bfc8e87e8087af7ba75aa7d51eafb78e09bd1740d58138b81f0242c2cb2db38fa7da17951fa94523bdada8a641129395ab908721a50721ef5482c106fae070630c3e84126f2e7026e2cac916608e2c9cde19bd584076d4e58ebaf2fa0e416a30058348322c48336798a719bb8ef477d4d5a8106d437baca5adcfb22ef9486b7ed95a21fd11f5de0d52d56ef777b0f86faff9bd024772878758fe0ecd4b4906ba855af06d2cf03f74d715a82999b355edca070660d5f4ffc81d5cab272f1f3f803edb52dc222d207bef40e0751e28314f23996faf2f444c7ae53d6193bb016f9082fe5f724f903710fef65f740aebd792c13a7a45a1fa03e6da6f334c5258f63d4cc66950ff56003518be09529c1f3f94197486ea0ff39e67c4cac99904fc4d0baab43c29e2546c9c013d50cae6f2c49f67a528cf309083eb0c44671c5fe34541f08090dfbbc6544a650d1c8db8d30eca03e0eae5f27e2cacd570311733551af20eb8701fb885d50e9838f154f1daae4482deaa00119cfd741fa0953135681e38c633b0c35c4b419830863958d5e895f8f14616d5c7565d4fb6404727bc2606092825728b4865a8bbc548f55954552e1aa0a4f03930a48f64f8bbf242355c8c5f7a288b81d4ca01020f1799afa9cbd4f76d8d52651e10956d0aa1720528924bcad9e97446a1dcc2ecccd12493477e6994fcd031647126bcba93b6d09f80ef115d1b8f5652778a10ac4639454dfabc77057ac09f3d69b3e2998a63d0b5baa4fd16cb1d8cfb8f9f8fc496ed431aa7a5748605d6f74eb7c71aa5eac94662cd0a61da51a3eb3240a173821c467965f81154a185713cc1d94427a9284467750a918345fd158c7e1b30910b84dc5b65d2f8053252b006cc75785abd70b4263d62a80233ab8922ae393216a9d8a5674c12ba44111b3ccddf2a7b02e22f6ca7e5ac943ad686f0a4cd76c5a826fe61fc45a676c129b8383021bc4396917a802602b4271f407ab4978046c92c1ff39692fc6bee0e29c46987074c60de4820260195e75091838e8dec05f427593b0dac948a1d76c265f0489d4f68ceea602d18ce71506212d51f864e60c1ce9f77eda39433b7bbf1a0b2f280cd9d524392b4ff0222fe8a62acdd6b36c2c2a64c07b946ad2567cc2ced2d0d9e1e3d6a56184c10712988631762b92f137d3fced2037807fe6d21ec72827813d073f828a6c3b5bc573dd7a60d265e64dfb7476ccc961827600a891e58027bb7b8f044dc299d4bb0435902ef03b31fe65845581c192be887c17cbaf7e9ca897c250127141ba4c5f76c0a3c422999b39b7b52a923d0870078d55058891277ca6b3504582dafc146418a77c22c0dd1cf99741164506a70169f1855135760b3494832766a53947b33bb40d7f656b8caa6138c4e628ed40169c036c4e3e2386ee31e0ae55852b5a769c78a6e2a9f7dbb6141765b01f0502955608a78a7098ae16e75b19590c20a47359c6a144545628fc390d1c8596b8eef54ebf11ec2ed84b0fcf34e784a65d72f7a592c16cc4b693e6e14f0868417130192a5cf2323aae6c51d7cfe710678f8c87cd61d4e2be5c98af5311809b45be29f67eb5e30d6c9c6a02c5b5b6f09395b5510454600f6b673a21b2a9c50f177cafc02a20c63e23d06e898f8fab8fe0a693c8d3eeb800d390efedeed045a871110b5cdd158c8f0a0b82adc973fbf98096d0ac01dd8183ac7572251fbdf2f2c71a8306ecccba9b430027e74ad1f5eb755152d5bfbfd4ca1aed3e11d7b10e117f51da6662015f828b08a8c16f1168356d79a7a3d2611fe5bc0f0876129fa873f06c5dd54c6776ce2060883d41e41ca63c08141eae13a884e2059ff6a73ad63e01c127a0da3a4587e5b54ba8ba97465b216b997b6dc1bba316196c69d9352b10bdd0f94684bb6e95670ccaf6d5002d915ec185c8a6def1dd1194dbb7cf828ab5f0ac445b23a6a9e87e407069a6a5a8b06eb13084c5ba02448556f6680497be2e7fcac45743a4c492b69bf053a5aea7e28ca77b1cbb7a63c40ff29d5230f1470f22c315df38e99a6556ae4e8b6e8c39c366a8a96fb8bb7a8a264ba12d7e3d0bd689b4d5c3e2267adf52fed6ac145b50640ea6ce06b3e63245a4fe4cdba64454b0e182a5e640d50f39cfbe1e064a5e0d24fb759b7741350c8eedf6a4f64bfa7007bcec918f2b203c8dd2c7f4285f9a37b3e49a604ae703eaff2c1c59de380583e815a07585faa489448890bca0092ca8ac6fd9f9b93a9ce647b864f8213447ae2d045cef5568e5fe7c0229e33a56bff2d42bb75d1fd3acd9cf60ea464fa8aeacbcac788d094219caa9bdaa04904b148401fe2f010c0b5698a438ce50c7861b0096fca4aa3f871702dfae180c8249f25abacb8655612d1449efb10dc98bc1665c2fe33ee7f92500c2ddc59418083a4343a996525c044ee8edc0be298bd13f28be8561fa401dd0ad150530afd1108238889e9f5fa64f25a71bdb08e8d108e82888683d8ff8baf82b4f1709109e927612d95719e402c2ba0b1b9fcd1fc5bd9fd9cdb18600a626bbce6dcd9653a84b08eec2191420524f6678dcba412d422656b898eeedea9d2f2748d57c88b655cc3c31ed054a4dace0a71d7b4fd51bae7752b81311d65d42b7ede5e3c477845f7a06bffbca4b41e4305ae1e4c428ab7341e03af9a0b6ce8ee438b39ce09edf48025528c338b8c807b737080003a0884764b972dbac5fd8a1d196b480f6d5f1d9356a206a25ef7dd5504b46973418090c0265c30072b3ee3344a6f18d3d19cab8df175db8b3890d88d12cf097ea8e2f1543c1a0c06946f556b3c23781df1af9ac3fe2dbf781e09c614a10697c1096444f7e0da43015471de4d2bad04eae4493ca8a56f6cb8a4cb78c10fa4b138451702ef10394d19d2319a05412bb045efbe89a76e72b7dd9a4c0de56367ed8b2fd1605c9e5a4b7b5291a8cc992d2e2810246c8f747b0de7f7992395cf66dc180ea9e23cc14f5a55945c28e822cf1b58e4c7240b78b22e4e0973cd5726ce60fe5d20eba9fb791a036c29aeb8dfb13e22ea42418b06fbd28566c9746891fa8b388e8791c48c000b868181b06d06376ab1824ab776fc08c60a26c537d49d1f65d2d4f6830af29ef5b1672d0ef57c33f3fe19588efa84b41b71ebcdc6c474af7db3907f989235fe93297d39a50c528769dca711ea614a4f773951a3d3f6e1040ee7df39d6e922ccde0e0a8ca9aa25e29e03e8601a4956f4eb518588b727f21bae53405946812e8c89a32e8abbf7452a6697e78132994a47d69f6901f05dca76e278121f5dc4ab2f9228fa2865b47e93564094e39546ba2dcfa9920b84e2c40ce523f8f5dcfd91b25729b4fdae63ea89c7b241e1c596999fcfc03575cbd39fb0695d55d8e5152a58a1597eb72981311e4b9dfa368b8ca33c78977b54d9d3fb7525162866409962bd7ea34e201131f8f6c21e9a518e98fa4668f1dbe156cca4771886439bb43d4567ddfb34cb9dc318f3731c24f82df80305d1be941461806a6670859c3fbd984d197d53f253b32045021f40091dda44c0175408d80b235f82b40d708bf5420b6e1612c43bbc614c394a8056e818b82a0fb0645e94fc71411c6067aba2cb208c3b420e1cad140f112112a978947b8202353855158fca35bb8a2c201fa1ad233f64e8891cf0cdcf6a73b12da81546f188eab38bc602850d241a538514f839d01ca624d5a7b05496136a5a4a80fbf105a3303ded484e83f2e380605eb4162826e800fba03e24ee8082a33e003748787b10d426d980a89f21af41144c7d85cdb6af8d15a60a5819d31658226128875c0aed86a401109c64a37565b310a4306ba89614ad0d895cc682a52dbc0663ee9845e66e46c5ee09542140406e70a9c80b40bc92905961153b742fd27cc0358f9d22044411da85bc8dc264052b8d13e3be0bb9fff5be8dac7006c4165f30f349692ce70d6723161e0a716c2bdd6c29cfeac2468d3c8e81bec420dbc1f82849155b98a6500cb1db90c2a13fb8237578424368653da4bcde9f95bbae556858a874b739ae2ad6631a0955ca9247614b59e083784a6d777e5f555f9b1a0490f623a95dc651ff8f81ad093c29d208a832dd5def508bc040bb41d2775fc1bb80365efcc7c14911094c1865c6b9ebb71b58c02552149d21db10dc93f09043d9186996c0af98f3ad3a06713c45bc3abab2590e318aee9e0db39a807566fab1a0e17d8a0062d935d08d59882462120599e6eb945e714a131af453fa70f40584ae71d703197676c62b09afc5bec59786a72db2657265298f89d7f3d5d029461480913ed70caf81d9f31cae1495513ba0b39451a8591dfa0ceadb989d6036971f24c2f75d36ace8b8f298413dc459b9a7c74ec1f372833b21cf0f88500d4d61d930d0de98472abf6859082f9beab109e7b9aa24f80f67e2d273ff3660ac2ac265a037409cf83bb723840be1dd83a47b5946a12a07a3128448876cd0d456554e4a2c6d211009aceb63e86c0137ce21cbccc84c23d59131265004aade2336d2fedc26a1c2a9cc5aa272fe0cf5f33d27a067eba590e0b719e646292547faab164bd0c24d5a85bbeeae1d4ad72155f40cd2c72248bb0a10bcc284379c751a96a5acb54b027bdadb032b2ca0a54aca352b6b0921913fa0e7a0618c5602ebd6ccae333feb8fdc343f2d42f3aba3f411d69d26c3c5004bf99b3bf535877cb58f101d43d93daa296a94bbc8820a61569671d65e61979eac9f2a86a2ddaeb501fab41dd543c742da0e4527f04000a1c48feb4d10bccc079c4b36344416ea0948dd4bd7f0fdbd682094910ff468616913c01b2b07850e81eb873128683e889d785589c3c0dafbe61b2661f1415db244c18cfac9e30e86774b2b19e0508f3d687b4ca86613442315c42f1244817a7eb07aebadb654b3433e886534dd3839a6da9a58dda0e68b37dce520c9ee66b8fcb8ce6361c8bdf81d950821ff46cc7b40f97ea5d537e578859378357420f722a3420676ad204a8d619948677d93085936cf314a8f5bf93fe690c4740af777268f6ec35f0427b45f3cfa6da2374ca57a03ccf6df6b1103388cddb623884acdc4de051515d958878757e1125044ef03c9ae121ed95b353b00efcad93c6dde70bf12a077e7e227f01e2b5ebc00d0e0bb59617434cd40503490e9ac80f32265cfd297efbe64712357d5027a021f05a6c62081a890012bdad8a8b123b1a38ff706c0caaa50290c5bab95294c4c5c1731831ce932ee11a59f9ef712a080343d08c527263286cbc0975a4965e23d37c4b519cc4a05873e0376443fbfdb0e5d645fab842e6dfd6950389cd7cafb99818b709ba10eab352f3946bdfbf381cfa0e644df698a535ebbb98124d42d12d2b86dc702eb21902186cc0ff88688cbe46f1be0263ca1762029b85414ded2ff7010dfad105920f77cab0d7f4cda2c2630fda6d469ec3b904dd1ac5de0e623cf6a8cb46c709016d0dac2e5bebf3c0e458bd6203578dd9ee112a57b4915149866d828525105ba01ceb9f21da4d9c828f28eb8e0f5f877eb362c0bda0c6d117727344c2367b10ee3951aac6dd98d2759328f0ca6a99e7ac7d20f361fca569d8982d4044a028dce1d28bcf18d548cbccd50900298914726dd6b47200331cb7cc8b519061206db8b39a0ff8b8e6a261f9134f23cdd44adfa5e30b2c7fff9da6e6c6bcf86996a6535df054f3b58029f557e624d0d08bb1a8ddc1f195a23f4ec51c69cd7ff81d4257ee3d52abd59dbf4e6233611d14d001dd26cd1b077ee3c5f24f278b34e8fe201b830da21be340fcfa2d17f39924e569830e31f6d2239f7a09e3b9ee2990148656787baab78b42418742346653ee38dae9da3e2d363052ff6d704c7f667852f25862069e583fadbb1690df6e4be619764b1ae7202e9b583683ddec99b2fa1395e3271676b68df627c67d35d2774a0cbd91f65a021fa06aa6aa0059e3d9407e79ec8fa2941acaa6cf0ff4af99226a0dec8ebf9f3cdd36b099eebc434b9b235a519367d2d220fc2b96e065d4a348fea9e6cd2523c3a56c2721aaf8f51bc270345bae13c2da86b3838b8e2c7d708473d1f2729958c95aa65143a597c9d2e4b74eb82a6ed184147de7dc465c2e2a8a84ff5a0003d1e1fdf17d1458932a442ad009a2719a2bb145b3baf82c39858b5ec5ce8674599fff3828a48a3ceab0df7e9d86ff04d6fa7cb6e6bc031c2aca0b7310d090d812372faad95c09b55f28162f13835933c3b46a998600e87588ede951b513b77a1f43af5ffb1eef6801f4b93aa00e362310a02ba26d2dced2754a19156d7d7c246cbc939a6aca78ace58983c035390836758af77463f8f9fc349cfcc27964549dac7778a658361d84137ffa7d46c4518caa2dfbe738f7629abf505936121feb49babd1ac44ce3275a63fcba3c4c6b0d3fa99a5baa69dea226ee5b6d4fc5a92af6a5e52e97b135e54dc89b7cbb690a93b5dfe92df775bcceec12e88bd322e63e7200d213a0a9f3c4e92d950d63051b65ed3f42132fba7ccb62d39d5800f28158c2eed121b3a17fa7d1326584537390aeaae48f5d97cf95d3f584fa274acfbbae2a5ecfaf0ba6748afcfbedefc419b62b55e2efb49dbaa3770d25ff99d2aa31c67ddc7d5ecd8f38f42369502c3874e2d7beddc255163b98374571302598979ad8b2ff1524934dcd8f226717a37680e0765d434671d730f546be0b3f91a17a78e6f5c7c707297950a768ab7014d39b0986a31bb9feb2d403567f2fe252cfb926c1d6a20eb6f6e74ff8c11c7a06dc871c0fbae4c48bf59aab81557ea515661f236a4e8dbb07b38f824ffd068b786666002b7fb9b477d457a063a059f9a0e0110363897dac74d98adc34b3fd1d8621b28f992117bf00979eb09749c50e2774db1efe390935726f1a32da030e7f717da7b12b3c375490a6741e9edb81045d994a0a677bb87afaa66c4446bf55d68a1a11fa55249e671dcf97cd664ae851cef4552332eb1c76c1af25d796f495ea10cd49ca740a95dcc4e6e3b49e899053d542bc8ffb8441d335228fa17ed3b5251a8fc3590ce1fbdbe02135e831b30becb6438614d5fda70e74e730777331f43efc9a63348eb8fd7662b0d97386d002d8e06cac2354ce136bb82b5619ccc9f85df17f630c783912085bd1e259c69657249d72835db4005ce694427d4f0d5be59491c8ec4cf0112acdbb5b8b60da1ebafdb377af7097684703747f45f63db5f62e0c5403b497349d457a7dfc1f1d72dca54b40d38fc69f86c24f35f13891fee5b466f6821b4a0472dee3333a8c5d5ca5d1f0c18ade7cdb6932d9c75eb56ba342911760349dbcaf84a98dff196a9ddafc2b8ad6d784da52b1461198fcb2e318f71580bd217b628e8a119fba34b2219504d3b0ae0d39a41da448a85f2474f35462498f85321aee754e204d2e05bbc1e0d4fb167c85dd27583ac18f7ef81268bd220bf2b682b4e41fab524c55a78048aafc06934f9c83d641afa8c4e42ce80445762e5b3db843cc681229fe5b7330b50797105598237cac3a4235c397d81e0b6d22f4cb7c9c9cb341c1e7b494b9cd48a263b27f1cf1945ef0b44f4fc8c95ebe4fa0e6dbcf663b1693d8cb636dc7d2c4ad5ebef3c35b861db3d257c945e2ae536ac32de15ecd5369b8f1a254ebc5f18df7026cd7913706734a377ccf79617bf81b85f0e36776db11cb956dba76101a033d42e33945ad70fb7f1c6be0b9af58444c7f1943c54b3d485ea095feec24b208885e10dc260a5050c889fb21b68280aa67910063523b8ef200ffb1397296039e22b5c9b46cf1f48a88ee9c9200de0e1b98b8733922c72f7dc615f62d6456927a7517836f64c98143990bba0905980acd21df42b0658eef1e6b7c8602fb0ae65f72397c0b0b47f8474aa43fb564808c64df422d25e81d93bea0e04b646d644ff8161af25227172ae54ae674649a25f30a5ff42a92536c7c678a1df5e1ecc35700fda37599f7ac4afe7c2b81062b6a375f4e224d3764e1cb93812caf44508c5b4a2eb3eff02245f3803acc018e2ea94da9d17afe89b067f96b5195943c3392b8816f3b08e9e1c0b7ae7669409062000491bd344303f805133b314417c91969a181a339cda45a721404d16ded7dec0ae7e9bd9cd9ea9810c70a2ca6267fd602c27e4119779102d1e3cf98c9188111f8f60b4a85c14be980a14f31873567713f6821f3c5d52c5c9779472fbc0cf6374a3a5bc44d91f8ca6c85e8d6d7afb5f9bf7d65302460a01790d50f644db96bb809cac62b42952e0d7917983166df5396d739d6e871045e369403c518413586d8b27c3d7f350b24a83100c70dc3859455671e3679a5c9c092e15cba6012882170131a7022fcc1b739cd1051c018e1975274a46664e82583d57798dcd4183ee2fa6080dcc7de6f987979db612c6ff4cf1d89b76100b7abafa91a42d19151886ce07ffc557a49f9dc49229a889725da0bd10d60dd5246977b581e74772a33774b06ab9dc8e941efe114ed7581c616b66f0ba3a4dc05ddf48aa372d0cbb53c1c44596bcb14637f5e1121a48b14b569e6b86bca70bd01fb4a3a7277eeec368450d309fbe98ca9ff87b2c237d25d5a4149de38a5be1bb17e7fe666a068ff156d7b5b0cad2fef355fc14fce04e2496317f82db4727209120c0f3a2fd8be93e0775cc3c6a6761b8b75e37f99d4be23496e5708b92267735723443da89f0f96ce450360b9d31d1dfddf48055efd76941f07f9413e86d71c5d1df2e1324cd89dfe84028ff0c4be4a36d4b16eeb364f7efc2af51be8d1d7eb080bbe41c20a01be827d098f00f0bd1c52add0b82dee12b137753a71baaf8c30fd14ab81f33402690723e747e894b0f4a3c1d3b3050ef9f669c3d1da11215de238dcdc12381d8c65b2eddca014bea6d73dedbf0c232ec5e8ef5e4e786cb44a6d4631a99322d9ffe0c3495c196a35df44d56506448f1f1739d5dca53b8fccb3819e7f17f7673368c8b8048ecd4e3a6daa1072d75ed32d8bebc5c49199163c3394d926e24feb7106fdcfcea91599fefdef18d524e90337a92d9ad1586545c1dae1c3df6e040dce7f55a9f488e0011a36fe9460f8528fbb3e177127de898afd0beb6927c13fbb4dfaa78617b8ea234574b7b905bdb0f974feacf00be10cc7e0cc0f803c0e1e94031d30b37793cf72027675505ce053c3d537ce5b74c9b55aa9a2fade9faefd027c2c60d293d13ef0d7728680a70fc8f419041c9470b558396ecd2ceb572221bb3a7598339d2abcf1001bb59edba032a8446954d5f42db8fb5485f68c626c60feeb555da8df251182fd708ea05b834582f7de84012ceb40dd15bddf0ad3f86fe4de17585fdc5d952832b995316fc396fbaea639da06c606029311fd9251797a4ae870d137d329d2ee8f33bc59fa6c755f87be7e3382409c479c7889bbbb6760040889297f1a065469ebd96313e988aea3372293bd89b626fdd375a25b1fceecae324cdcd6a708b53ec4574252eda425044b255c78d5e21a3a1c29daeb68605c8be2fa4dee0a026d1f80ee24e01bda2eded0d8e1c07c0a3ee8e80784dc22b846d7be67f81b618a4ce7e186beb9f5fa6c8daefe87c2fdf58691319f0850ba2df97e62411f732b3e714a85c46dda776c869ac65caf1a74d493f55c7b1d8ee15f0be33e784fe7174b34ed3beeafd92026445b84eb7979050c0b2d16733cab7a2e6202459d61f9053746283a111294e593fa454c66749a7254c150bf507565f0ebea91417d6d20187ec42f5fb7ccd77290f46294acbfc67564d0242569c3090c90deb09200ec87d86ae83b83830960f96b5248e930bf753a9d5c19f59a135b686b42df5bcbe7ee9a9ef299b086c72ac82829e8ebbf5620d777324a5f94cb1cd57afcdb4fa9e3c3c4a1432aa1a3d826d92a3671cec4262104b8d070bb7e7a515061383133f906604a072141fc965bf98de1ff341069658013977dff786461b3b13621f27d4b181adde0072d163b1572d8ea08d1529612230a3b9d39a8c5d83ffafc6e30576b7796377da74a31a985dae10b0c7b4dc9954b3f7ec3313325c60ac4920dc5d19d93e5798189f7575938d7a4f2635e635e7220fbedf9f0356e053f4bb4866636ddab29d13b2eb767d71fa6115cc85b492ef5d7452ec4399c670f00f9b453dfac690d1ec00080b69305949de57acc2a1326e66a0078191fa68ea255b76a1357456f5a125187b6831afda8209ca31716a06d782e4ad4f3988e0dd2202ac6e31c3916edb999be0a17fd97a38e4e61c7348bdae9dc7102de2b788bbcaa36ba7221d3bf5a2d1afef867a39a1f64d59218628dcf9f8048c942aea766a318d86c902d31226baf33cfeef09248a0a4998054edb0e98c3a98b87d8f3abeac04db5751e6d1cb6ec93d4d36d65553700c782fbe25bf44f9dbfea94ac227ce586521aba6ade6c4a9c9be4b4f467ecd151f997c7c6255b09abb95098f0fc4f56233168f9a833ecfe0c7faa7aabfbd7573ca46f6c23d2d11dc0c51c61b56557cfbc10501d05e19c860f34f810ed214e774dc3c2be45e1a44f2c7e9538a622f821861c4c92fdf5281a7d0377a7e139d3cb9584b921176745999a5aa9a9e465fddec800fbc3e54c417d6402f525e9e157cfcdb6e864f83c8e0e96dbe54efb2ee0434485c96aa3d5fa1420cf0ad78ecd2943af11bf5f72269e5a64c879bd9c4f005dd5465c47dc4d13c66428042f9dfcfff61865a926a5032501cc4853f90b9a23ac75a23a943e7d85c51330a2585b43077489602a60a3fa82bd0edf6f46811adb5ae829102279f4b526ea99bf0796b48bc5902521da50b95aa89dd91b9288d9e00dfd2b804f6cdc9b08394273464b033bb8b2013b6cb7e47f8807f129bde855d2b7242c147e66dba936f735c1123a79efc6c44c03a0470310fc0497e2e17f857cc340802095bb10fb857ff1057868c6239c054f1d058889b50cee6fdcca11aae53a6f454c2d15ab7a16039702a2e43539b60ca68e562c13d4ecb565d95fae5c29e3e669565dfaea37b7c5b4b84de44580c17af18278e7a6d9b564c406857746e4d42560c0a1102d2eb069cdc01d2ef5330a2bed9bff252b9e887d284d57c89b52b6633969511723e48d627ecdf26279c93d6cf263bbe7370aad25d10db31d38beecc824152bfe11d6a713da9df7eab0f1b4c452e8d0e8a320efaa6d49947fd82dc34b389601d0f8aca9db686f6c8ac376f4445b705330038ab7efe3b3a9714812b6b242b2d6a22eb23bfe6e3a613f33d4ce3f3be2ee00e02662aad313031e3f790969fa4276d7329de32355f77a9ca9b50061391b232cf0782c946aa684b99c7170e367e1a72a3284ac33751d8c95f28a4081fee4c86a0d97a094c73947a6e435ccc81e3ec3d0e3498ea5ae586069f8193e63aeed1282f523b0bd5f0c93c7fe16aeb05f70100d4789a3fb2b036ed7f7f602abb478e5d63bd3381b612b61ef7fc1bfb3589f88bdf40dcbcc6bc95aafba8ed05296e40080ba6b79d4664630a21b62934cebac5fc73a197b0eab7b3976cc8d5674d457b5109bd70c71089cdbed50f0490628bd5c47bb45effb36341cb17bb19b7ba3986e3966b0207411479311b02189672e33c4148e5bb06593f37659d196eaf9b2142ca8dd5bba539dc77131e50dc29426f873db21e1f0e15c8e35fa2eefea8f6dc979c0e09cd9d7272c8ae678b90411c97e9d6b004e12a37adf668e349f52189c83554c356ba386542ab7ab81651ca45e6afb8c3453819b252625e595c5e317f2aeca3133081de114e370c3322baa1fed60877745f37d21382fa7a0f95641d60570eaf0f9930cd14ae03840c1caa08d3a07b4cf0a9a0d34770daa393c75bd769a76580d35d2e68fddc7404d4b65c3dda54a7b7e3854c1fa3fb9aa0b7eb14f9979e8aa4d0f7c1a16b1d6cff434f2c52382ba795289d40f41c3c2f31b977e86fae8310e71fb58672871e92bad782850aa25dbed375eebc901dd0bd9e9273c087d2de24a51434bdcc92629f2ae19aa69d0bce0fbdae98c03ee5e261950b4afd1c7215b119783a68bad0fd1dee23391f8dcb35394899a352832d2754275e99746677801ee5c2cf3329fe89f3123193ce52efd31056cb003d2743615bf3e7515b3013cef3f388569f47bbd5a0b6862bd3530ec34707f4fa14360337feae21899e100355deb735777fad88089ba3b921627a53d82ab89795e033ed0ef0e879598a3163d4229eb86145e8dba7db6b9e4e05069928cebc7cc49d2b02f4b2793d0a6da10c5945366e2aede6a1639678e3ed7f2a4ec1da5b8c26a78124960b70b461f82419ab75ec0703ac0286f14549c844e937920c94347d20e4582e45057d2b3e5baa3a51c9a8fc05407ebc2c5d420ff8823ee160f294323bd0005052d25ac938e05f4ec0eed7d3b4c9e087c834c02433fbb084f2531c414bac8920b45ea75f52a61698ce6e747bd2e018a7d3c1cd1b42f96f3dece717295958d84b46d8d75a4952ea4a176ee9a87696e369ecc53d7465e4d3c759aed2957f5303b0db25675c0d4b889ccfc4f9150b5d96eb16b49f61794aa2a97dfb397fd3b6662766a209ac593ed08b00d88eb0131bc97179612d911ef00fa49e646b6152a13ef69f3fdb0d424df27496b44cbc3dedc3ccc7e25c96d8aa710bdf2bc45e85182e202c5b0d23dd65820dd6a65506376152d389a5e282c8b898527b317256bdaa3be98da98527c510afd369920d9b616058b2377b9e361af7dd85bca3d35331dc9d373fce60bff7e704678fa1e3737c79383e8c19f8760554dbf43148110a6068817e9c8ac81272e3fd2acf50bc1e0320018bdecf258d48852e9ecad58cc3faee4befc393bbfa76b18d66c07621dadcc78002ef3a9180594bc2267664547444fbbceaf8a019d7c22080086c4748b5a020d90209b3acc2686b1fe14c1b02518bf891fec37823f95658a15cd83ccc8a023c71c5e8fb9021d9a3cce4aa48622bf9793f56e2b3f7f9a6dd006e803ff2d534bddefcc3b24b60f90a508330be8390941d8cac27a1ff39bdddb2dca07253eb8ff74d3db78837cc02e3d3faa41e01012153f2b15df2423cb48332bd4c7625ed2436a61d91f1a0389906dd6a74a40193f4d051fa7a61472c5e709eef74f16f307e42fae827197d22412ad6fb9c9a40d09a9c7d49495b5ce2836ae9f82ff8dd452bd35b004e658a2818f1c32e2bbc345f80ebc54afd32aeb33708444fd8fc6c9b43f312991b83a8c4c7d1189bbc83fa131559bbcb69c8415099c3b2a14737596183e465433ee60189fc42c772a1175990792aaf00c7cad7ce604002ecadc5f73c650910570eaa49a7d602c6ab799a87a256888310fa8ae2ece0fbe026a68c2ac6fb40bdeec8a921ed3dcd3d9af03528a866778a11da18b06f8f85a2596f2170f572a6f939559cadd9e7ed33c225c55e4ba9aef80072a550f0d85931ac021f68703657f73e8925f81cd779fdf4184bb4cccdf667bf03cf4eeea24c54f08f4d158faa2574ddbb9edee98cea195ab7460685eec506f6df97d14d5e5989c054ef428c2b462661433cc2c7be41a546b09c3636792310c300da66e4776a4848b3a3ea6ffa0ac9896aa4d0f77bcfc15eff57bbe5ea37a0981d1d07131ef6c333740e199a41d0e12ff0c49f0f7117b31c49ac91ee9d33b161bba92904026b665d63083c07905ee95d9ffb49b1df8e4f7ee3b92280ed13a3084bd324e3dc161787a73ad8b36eea42d90f332cd30c8d10c34e65790d59095946f013a3f630b81ada24c075bc00b0837c9c54bb275ccf57a2ec947789d0b1c6595bb7f79399f1e9dc68d804a486c19be164a450b431381d82331abccbcd0cafa1934206e502f76478904c492be848b6559c3e8b2fe65f947c2edd8f245bd6115afd0974873134f6730b0cc74f359f06047671ffc500f65432ead4bea9f1fa6ebd86604267d1e5b5766e3d77d3c526e3cc2ec9c21425b53390a0398d49fb9a3775f4b65039932a8a6d3673ac46ebe94706b4c004eadf4bc158309a55493ab94997836fa9ad91e698ae1372c7228d282db805765cd2d761d81debd680de4b9ec23d23381fc5fe2f6017c49a453a361590a574c1ef05349642d9d65a687eb19753a73634726d6c6b9f10b21bd984ecbdb70c2d0cfd0bff0b3f6032f4832df2bc5c22ca9f792d6543552d8bb86a6d1131a33c3f7f1dc5196c49f4381bac5505fb655f2f419221d6ca13758d257b20ab394964a711b8081c64bf7894e7446113c6a978ec29d3b19ba2299ae26d748d797a244489f2a42d5a94679c731e80c7bd899e2623571368557cd441d7a0af8936284ed163294cbb32201e8e38a45cafdd20e92c8955927b49ee222d6f56eafdcc7643b416d073f373d3c40dd0cd913893dafe9ba01b2b1a47e92d858f5047a12877ad75b38158c2c0b65c2b55f5a9db49557d2604fb5a0f6bf58c72dfe3c9df25942b568fd1a071c05c8fd5a0716cafd86b3d35d2ab520684a650d028aa0b88c419fb7ada13674eafa74bc41994d7d37aca449c49793dfd8933a8d7d3261a87ca1571a519712d1b34f683c69ee819eef518111a23922b17afc75a72f5f22c0b8c091de2d124b25167d80e723da6b31d4da07924ceacbcebb118cf27933ca317752d4b94eba7075a264d9dc6c1f2fa0b49ae3256fd9514672ea1ebe7899fd3a07b814dc1374790eb6554d4ca95e8df0594ebb54efdef01f38b85dcc3fcf73017f2a7606eaafb7d0e2b75531d4e0d913acc6f52877977188c63a4c33730bf18e71ee6387fcac2dcd4bdb7e56f4bd5a5c373f9c815cceb67d2ced52357a9771db6a96ba54a3eae5aee928a4172d5826512963128855baaea4d295526a91c72404a8734e381e9ba2c48aaea3defaf35283b3aca3cefef3a6b934a317e7e7e689665493229294b4a4a2a259592ea4fa9a816957e4a3fa59f52515151517dc62357f54866414212a6eb8e783cefef2c4f2614399b954f9d23d95913922bad2555f55b140d9d904d91ab1572ddae1081c6a3b53423882bd792ab3aa426916bab12b1db85b91615e5fa7ad4bd5e13925bc0745dd7e10f3a212857a05c3b5bbf6942b9a6d8934c922b6d8bc6d1597ac1d28de56a465da36e3d5255b52cb4aa5ec80b3a50f793ebe6936b85c2e681a7a1d322570d0bcec684371e93a954043724106f411b904c6a1cdbd1651a7a26d1383a5756b5495a4faeda0c727df58255c97832dbc18c27d7dbbb3511bdfad3dd80a2578f72b723770b8a5e7dcadd9044af1e7537a1e8d5abdc2d49d7a8cf82e44a631255f543b2242c916e893311942b52bd8bab194955fdcbd58e90e4aafe48557d96594b91727de947ae341ea9aaef4ea59f5c9f25c915e72355f54036a8133a51fdcae596e091ab0d8b56d5d77a14fc65f5167fd603b98e20d713fe626496bb1d758daa823fee27d7734cc499ce75abe79a88ab529254d5734b74492c92a423b9bee3366ec95614bd7a0d73ade8d59f56ae2f25d52ecbb623d376546f326d474f6c59fc9492eaa611895ed57aa257bf618d277af52f4ab09fc693ebb72ce20ca713673eee4ce2ccc9ebb7a3380373fd8614678eb6d8a2d892e2ccb77121d7d7ed9f6694eb2bd251d5c9b51ee57a4d6e2cb7ef8d24b9aaaf3ff0465ed15b1d912d25e55aa170dfd29d6ccb1fd3552e54d235e051f0291726814217228141f0db91cc326bbbeef73c996d3bcaf5a5a25a2acaf5a62b03af1387dcb8c18a7d7048a6b883121fe80452de8e51146c7300948323792b8294b7225b446f834582608ebe59165b5f6c31814724c9f0ddf83b62a7a79094b767f77bfe6011a0e86dd656275a52b5bdef44125d230bf6834ef01849e2d99ce0c9db9dd0525a0e84f2f6147cdcc95f4e90a64dc1246f5f53a19337888b00e56d4950be4e04925c4123494418c915f6ed441cc915fd56d2a610ca17fe9a8a9dbc61970823a9da0e8d24c915114852b57d7b864eece40d7f3d4552de9cd0c91bc5441c49d57623db59833b20cadbbf9ec2c8897fd9edbf0e3601245792b51d36112457a9d3210b07b250be1d1a218a33a56f4694e4ed42793bcab552b5fd74b72b5bfcd9bc39d1ca1beea2b71d1a6999f0971519e5ed2da191a3bcbd6336ec033a81a49de8641a59a963293c91a604c237c2f0235ee396586e0a0bf3b557ed55833c1fe409ca54889ef6348e93d3d31750251a473d7d71faac7519c995fca4cf6ed02b2d336233204091b32430394b22ce9c4e9ff1c41994d36744e24ccae9b39e6c8938833a55397dc644e358393db5226657d82cba2df2b6c3247a43323d6dc995d6d276d094073d633a3dfd41af5a4e4f85e4cac5e929915cbd9c9ec2a0bba90d323d9624579c516986e5dd3791ec0ed5f18aded1cf34f4449a498da33b3d768421c5192c08f3912bec545382cf5fd0a5c08ea0b3cb039916b58830a1be0ef31ed8535848ead853c72e04e6d79b958a4f3d8775dd54bcb98e3d1ec39ec238a963c781f9f51cd6062455dbafa0bc3d75b7c36cd7eabace0ec643afa41b34be974c5199a6be09c10ef3ed02362c8f5a7336da6ba2f6c286e5baf066458457645b1692730086533cb06387390cbeb9708ac7f5eba9a7369b882fb072202758019295fa0f9d5b7295d5d20e4cd7791e1090d692ab52c9f3feaeab317c7c7c7cb22c3b3a7a719b554292484825a4125245aa48b59515c995d6aa3ed5a7b68a327d6947ae342359020a92463b3b3b9ef7779db519cb679291dc82e46a23922aca41d12afa188513b82932e5ac68b2ed6c44db5644e4026f45529564bbe58ce4aa32d18670465a129c1167945ddcd45fae4b0b22b532bd6674fa2d081555f4305d672dfea0138c720065fa43a3db82327da32e391bed84324464a5b62c1a47e76c2bda582ed7ddada86b703c5245b72a5a451fe401fda7f3c994ebc95453421aae458745a65b1632e5764a2520d8526dcd23997240dc4fa6af8913a971d04f2d7a55ea6b080624579155da7822fe361764faea851717b24a3b59666d0793e33f98fc957632fde9724c448f1ee5723fd1a34fb95c1397038a1e3dea7247a247af72b9a0e8d1af5c0e49d7a02f01c9d5b64554d16f4cb22136898ec8b9254a4672b51d19657a17772b922afa97bb191dc9959665d61e65faea2357db4ed6551f94ea93e94b48f42d5bd419dd5b2921657a969b237af4db8e5c955a454fe92dfe32fad3b6f375cf317277b3ae4157e80b10673ad31f20ae2a9254d1d3d3e4e8aea0d47b6735ee8aca1d61ce287af4970afb71464831227596957a95654f6c3ed4e5f45b12d1a33cd1a3af78db891efd8b12ecb693e9b738637a8933dc29bd8d33df29fe3828ba38f37156c8f4b4fedb8a32bd762457f49a915cb55c6a49996a4699debe3790e48a266a3e72055b156fe415bdd511d98a94a912ee5bba93f5c974e542251712a1842012940b83e0912cb3b6eba4cc3e9a5c5b729565d6766d65fad295b9f9891e7c0a17e1728d883a436edc913756a44adfb8ff88adaf93bec8f3f2bd04630b26f9a0501e42b22e5c845461f9cb4af7061f58a49964753a9e7afaab8958ffeef3ae234a72fd1556d46f22d57fad848672d77e51aa1d20a7eeb4aa9ea7a7d24a3fe34fd7a89f9c022a4ed56f18d65bba76bfeb80c9254e074cae18967047a1954f7450628e1f151376cb3d04b404871f9be51e025242f412af99bd64d90b98652fb21798bd642fb2972c7b41b199bd64d98b245a96d1f6608430d2a04993096343d4b3ac258c11c6a15894936453f41941932200810a2ec880154190239011723c2ace7017908e8ece0e58f15d5c75cb1700e42f86c68daf93c896596cd14cb27c7f7088849192892920921bf9834994c025566401990c49a20a199790b1892311494cf2452553c85f5ce26345969734e0c97288065ac8f2afc110b2bc375483a3dc4336384296af714196078207135ac8f20c18628249962f020a59de88a11b504107073ad8c10119788a14a1c7a788157e868a8021cba11f21211ea2560e9214191d21dd20cbc3241da441e7063b3b60034f1347e85122cb1e3282f4e30411509010114f963dc483a32c1f731667be4d28cbc34d28b7d014497913da8c907dc82dce40a20cb7242cb825c9cdd9c4d2155d37e4a36404435f68f101419e9fddec253ea8424a9e73ce39b119f97a2791628a198c60ce20075d0a2613544d6610021990010c43621c010c2be8cce0838ef95aa8eb1c1d637a65a52ac295f686456e4455bc8c19ac4cd7d10091e55b75bd74bb7c1591e5bdabaf0d64e95d38124b298cb6157d4cb4020c482527d80069044d8fb410c1bdc71ccfa41fcc16d0132da0f1424b0648f9eba22cdf56c877f2323159ee2119b4e8921944accd3da4441bb2b027b987944022a2b50f52061209d1d09841a38b4ee491349a9877727970b62f8aca646ed0d050e5c8724ead7236d8c466092bcd8e863703001e958d9a7925bb2eecba2edaa815b9494faa68eebc24362f99f3a4f39a5794534e39a5fc94726612ce89ea1848e3ec0bc503adbbe17c9c53e2ec0abfb0e7f40ff62cf19ac8c3cd715baa240f7c81b9b806b9d3ed9cd23f8c2c78c9d95ca39b86e250a877af22ea5a09439e9724f6933c197ef630e529a89f6e6f0afe687c3437b9beca7a6f78cbfd6ec03f68e496dfdbfbdde4fb12f7433ce18f473edddecf3ee5f56a29d7e68616f615e67a697fc8a0ee77bacb4b9c8dc51d3d18bcfde28e5e0bee6ba50d5fd421d75d43dd0f66ad391d50e59eeef77cd2ecfda1b37dca9b4b4067abf2cbdaffd0d9e21b5a5815fcd5bb1c723aea5b6eba2aaf9129d74f56b939b906c1320a63d086cb6b644dcca2075d60df6398eb05e622c22c05c71f8b4ff01dccaced3aea792cff6458fed1d44896bbf2973b391b95a7ee4bf4506f7977bf9cdcb1e49614fce5e494dbfb5d057f30ab1cd67acf9391913c9247aae0abca57aea9ace0c0aafcebb2ca6ba2e491f0baaed35df0e9d8e926fc7557777b597e6a39ca9b1302f5165c6fe7eea2ce72bf6794157bb8726d4565e5d637a75281e8ac828360d93e7241c03469d26447ccf51980b9e21fa8af5c9827b74326a75ce5167f5008eafc24f920f51c411e2323a12322a496fdd745349efd2793abfd47a3723fd34f6f6e07e4c9292af88b4056b9bd9f4cb6aff77b9675d7fa1aa972835c59e5cdd9a86020327f30a3700564720afe78d87b7abd28879c0dca4fb723109d2d0e7205d181264d9ab4b209ff90c9278f3b72b5b5aa1ec6e4faac56fc6df8078ddec997e7933c196f71d5aca933800891e8d1ba71487b6534cb7ace47c80361e7c060c4e090d135e2bf1b06e0411bf1dff38c4cf4228acbcd43015d63e60d442f7f32594787862a80479228a3a5101262e180d8409c8155682f62a199340a3212ba060c401b2cbc7681c007a00d5647d7f824ccf276864d58e8c3920f9093e1a14fcb962c0c90abb74adecba48c5d7d5d04d334e4e7f5a169c09ad2101861bc380c6491bbade8259d64cbba3b2667de0b68b210ca0c4050a28b88a8c602b2bc5ac5e50b0359decba65c49d69793eb9039148221b9331c02c18ea9e5fdc6e98ed3f22a6a8e38c8954ddaf12100ca2fb987424064fefa25244bce46bab80c41cadf3cda8288792ec10940c81f3542434b6e2e2e93682a89392ac87e14c800f2a674c43cbf247a51069616d1d60c269e3f716662d1aaf8a905518e474747aaa0e17137d6c5c54546a665c4564427c3eeeaa0582d5707c5822b6fce86e535918888517ec94d44e40845355607ca53de33af93cde97dc234283b02731345afe584cab18b7abd29775ead6be5a812cab5e6b0cb9b8118c4f591a2c750ee970198a27807cae5738d440902e445c6404ea6c03cccb31586951c3434f9c80704a6264d9a346175d7b0ad7427d09d4dcc9fe8c5cec7757aac00353f727af2751ff39d2f98fb28a78460e5698b0201d1a0d40e7a79fa2eebb2dc51a0959be5a02a0e3081d8586ee7959b659cda51b37ccd12a78290f312a778dcb4a8d103666b12b576928841d18b4838ed2d1807e3f0d8812c97df3c858720e103cb05a7e40e64a5a4cb7ba4eee272212d4fe11e2e7ff90b16d2dd05a724e6f17297964b2002f448f42e6db95c5a44e5b4fd2146b9a7907b68c808326d512671c6f4f82c1b621f4fab50c9f1b4886a91291639d22ce47829ff6c892691bce48f59447d81fd2890c4418c381267604bc21389bff664689e9d1e3b6dc9a352504e6f59a76d207996154c5530968251f00957fc7979074dde2890c9740a34b31c2dae004dc6f00784e9a6933b91442f4a1ce40a424893264d88323689b6162acbbfe4eddf34ca4e232f4faa745c789e6e9f1fc53f68b2f60dcfa049247a1113faf901029a447312116597372bf566c9899d3ebbe6d2b9d6d27579777a7bb43c75ec0ed1f2141662e4c6e5dd85a4de22790c4961173c892065f9d91afa409119904a1b9d5402b59800922bdaa29802490a84bd33a53ea4c4a91d9ffd494c7fa80f6542ca679c8da42d7903135b391e43527739057249e13b7bee1c123df9895dacc17e3979ee64996d55bce46c36708420fc60c8a5933ab21f6d115d7174b493450f529cf926912de2cc377f72fce6901c5b4b620f2ed48a38f3fdf801eaf191ab26b2f3f95da7adc6e1e3c215b82e9168651a85c6d157e44f6e91618b1a5d17863c9047082ef77161d85272e991a872b9cd921253a02a02daa2401f355a82f6f4c096cb5b6e4abe59ddb1a3388321c955cbe3b124aa4377e4cac5e3290fedd1828d3ab9b38b7be94855fccb9d4952159fba13e9ce23a98a7771646dee2110a4217ff3c8e5c2ee9690000422f3088910bacbeea624a62dba641ec5792404ce884b0c3a310afbc122b8127df041073b20db927be80936e427bcf044d14f4b99a3dcba054e2a3430d04498c92e92ba89acf8c88a184708aba50a3b02464c0a24403188e1f3a29c72ce396717a44ca459a994d1186126e79c534a1b39e9906c49447461d817ae2fcc78c10848ac0470de0c6d2e51d367ad22a3226dbbce5a795df68ca7dcd825239c48d7135910b66801d0132420b2001e9f2764d08382a427828082999d4048aa3a7b228a152c00053a3a80b0a311bb30ec8261041b2377145d10438c28788105387cc1b2e48ec20b4e14c10b51743d48b62e22cecc13cef4dba355d76d77215f4ac0130e6e1a84105a6b6d2dc1ec1f4d861d764cabb2cfd4d8ededd10768b3961ac34abf6e91d28fcd2eb863348d3078cdeb9a9e91adb9878e78e10a360bf9fbbc47433b524516062928967be8881443ae9f9c88a717b439693721bd539e2c90a3ac79e2c800cdd81ba50239c6d32cdf37058165898174938c6118e711391ecec72c6e51d7755d5729f7d01126e4cf7ee640eb188de330d5fe936b13b91ee59a45f42a508c9f628b5c638c3046182352aeef187f72358254df874690ea8d8f317e12212455f1f144141101e39d58bb76ec12512455da8910922b228e2e2b9e42286b4652a5d5973eb803a1acfdeb298ab276680429b3b68d2065edb0891fb98a2c4d3bdc01529ce922ed2db5778cc61323cd82968a5a9cf893ff41e24b9992513e428886ec59ae509f90c77e599e2f8a6ce7963458e9237bb06338a2feb514ada20ca5100a6aa01f9f9d3c44d1bd018a1ef4bab33cbe6b39cae52b7749a9682a97e5a8fb37be69c2450a1d54d20a5286c75f4be1c828432914ada0e46c22c65e2335ecf60d2df02704e8f04280ae8bb3b988446fb6cb8d30d83eca31c60bd2a3e84d9a459eb185099bfde0cc277a970a3b1f77ae9e222bdfff681010504f0aeb89de757474a4f21ea9428332dd30d91367a60cf2bcf4996ad0e9c1ce90680434d48321ac83f29c8941974b0cf58027cf5324712615e7e74ff23cecb1fd8ede8ec9d9348ef826287a2a1772f488521a74a9fc3aba7cf2853f382b6f79c461b9cba3cbcf4fea86e5a9b33ce5027de42a75d31df5eea86bdd6de95a30cf9d4279c68e3a73fe221267523e3f8be40a7578048d60512b7fb2a7855a9ff168ca783f0a75a1905431498a1e44f28e1e3475324fe2ef47ec9c1cdf728593c3ea4caa7eba46aa8fbd71aa69d668d30873f744c513eea993933ae79c73ce39e7fc49e5b84f8e3b0fb99a5a51a61742a4492f26d19bd717a06a7e9221cfa9863c4faedd0c64393e1edd0b8bae31df42996af205b2a767c87eb2470ae105b5679d73ca25c823a0b460e1b116b6445bc1c2a2b460338de62963906da52c3be1cf5a3c4dcfb2ad44b169858db95e42d1ab6e1983691b0796a79c22cfd951fc93373092a704c2fcaccf354543d14e9ae998e9167f59663793e9d7e46caa50d64a375561adb5e22f55aa37dd1f4c2653ad37d592092b99ea4b2b7d714a3b764dbb66d26ebda95ec3b2c725639e0c8f7a8f05746f298058de52106917666ddb36ce86fe6272ed3a6a1c58f40a8b42abe667e93acaf39ad46e56deb9a0def21edd5db09095776fc1428cd40b5139eacdeade2a1d16d2bdfbca85a0aef2d4102a47fd46e5a8577cb3f2ee3571720a6816cb4d5514eadd6f50ef5edf756fc1382bef8ea302391b1e2c3db98347b10465f815a10caf72a5206299577951854df140bdf3d7513092e208a7490194e18b6047c1285f4771267efecae2428a3312338a1e93c696604558ab8fe2104552055f449cf95a8a534ebba6dd5a51b8130c046257748d79b1d6c5965cac08b3e2c87e368af81167e8e7a4415235b9cfedf3725e6e1167b417f5b4788876bea983b5bac6445dc594325d339d6ebad8d7a9d33b0b01caa6db37a7c3def49a78ba3f98ac35996e4d266b712a6551ac111a8a351d25eaa06b74b6a65b5392d5546852ca8961589bd2de9673ce0bc34e4bd74412e38c104278b718e3c4d98cd7fdb2aef15d2288914a794929e3c4536230cee092f18a18931ec4b0c65f863b5ed7bc660e8573083b646a88fb983300ff86cf9be1af8d72f6989b828f390ebfe1a68688390e4f41dc23e638a47adc7018bfe1301e00690a6cf6d835b128893d429484a0241c654882dc43486802094b202189dc4354d8dc43541cc9dcfd66e64e4caf91abd4498c13ec611cef2a5c93c9958753a51838558a8153252144259309008f817164ee6537ca30e336fe9d9c7236275807b762e3a6e065bc5933ae77d57bc4b80c2c44c663c8c0292e47c815d0ac4b55ec72581e4ec19b19f70eefd5780c8ce3bdc671627c460a4b46885291540ade06be078dabaec238399a164fbc9be262dc1477ee9e7715c6b171ef3800788cd410337ad4788af3aeba9018329ec3eaa1bacc65b0100f5fe0467519188706d748553c1052156be29b65037f4d44031b816f1600b0e44ede31b2779b756760bf4e7a76f29314134e0d112386eade853c06eee10951e14b95097bb8a5ea6ba19cce1a0e17c6ad81fb74b38cba7d1d9e0809477213292122520224747294fbc18c029fe12c86848a868ec024cbe41e12fac94976b60d951ae21e873be01e079c72003e8c18f77e13e31ec63172729c141703a7e05537321ee331304e8ac3a92166dc7b8f19f7b0102337315ee342bccfc03d6a7cc6676021315e03374b069671151662e466c655172203f7505dc6656021dc55b8a5ea44939c0da732e3a64c4fa5a49868524c29de4a0af7d44a8a938153284f7137329e4aa1c898b1825354b65b141977064ea53cb57273192bf886c655dc4da5527a7087d67ec6950144e66a514ad69eb0acfc2b87f98ba7522e9ef297a79e4ab9dc9452397794afd0a4c0bcc09f8c8b14cff47217930aa7b8d368c12915942fc8cc283fa9bc26e664538a3bb9a9d4cab9a71cfee4326e6a050f41e3aaa738dc8306567dc6653c95827bccb88ccfc0324e375d2032a79c64dc96aad355b7a5ca05d2dda4499336649413cae995b34139eafe885ebc4518c1c28cf21f7e1cc2c37b82bf994d87af917106e5b18838739a97717892c15dd51c96891ec763c82f55dc077540c8e16f66eed7e1551d3e3b39bceac2132c13bdb73d577ac3ebba258883cc2026c7711cc75d1cfe70327778af77b7ccf418387572d5735826ece1d4094e0de13dc66fbcc7f8c963601c2327f826c65518e75275f21827b89fc3aa91f0367781d0264d9a30d921b3c680dbac184064def007b38681c8128e000eb759306eb35ea260bf1f5d84b3ec628fb9cdbae136ab46cedb196313b78ecd81892fa630e79cf2f04a29a5d426ca35ed1aca0f9a0c3921b0b6fde446cee6243e27cbfb3d4720c39fee372f67da6e6f4e8637c8b55ddeef46deb8fb6d599b3ae8350c51d77e733e85c59cf3f31e0d19334fe25544eb984d1e420861a4810d3112610bc96b0def4723c39b60609b551f5f83c87c437e850afb6d25d4077f75ce68640cbf9382fd3cf983468e9797c9e2cc87cab285a609e0ef11ec0735001c28cfc3b8d6dc8e79183fb9dfc9eb8366a4e4ec7508c04bb502086c9251ac853fd6da1b376e683a04e03afc6ace2687d7c4d5ccea5773363ba04ea893a6556b6db53f9c6c3c8a1eacb54a1e0cbb2857351b7c92c706361ee5ebd5daa4dcde949b6e54123d28f385630bc79947f8d3456022ef50842345189277f80e45207245cee11fcca72da0489273780e876289c66dd8c9c9c93cb9e289cc41871c0e374e07cc395cb3a75faf185669d0e9302c2c29296f8e8525e584c36e6861535236cbd9d4aae3f4eeb5c26a391da7eff0eb62ab9f6ee7541030ef70987738ccfd72f20edf2108017c00ef68accc8e9765f6b7efe674c01ff071bc26621be5ea75b8343434a75c3dca8534a7f9e9d809e6155fd7f59caf9ee535b2068919f52d25057f39574acad53511b25cabbf2e7b5d36d6b68c8a5a45464618923b77468216b2c88d043128b1b9912006241ba783dec5bbeb020a26906c81040a22499664fa08c5904c1fa13852842d327d11aec8f4ddcda27797d45de28c193366cc10d23366cc98819343e35c0aa72cea299f05a203e6ec33628d15eb5cb81cc31f8d8cc538eb23516b0591282a6971f6cab37036b57e72db9bd38e72533a520ee328efcc72181e5eca711e5e13b59ae73c95c2373cbc06e3f4701e8ee3c3733c87d5dc3d0f37957a7eb372dc54aaa62663213d9c87d7f4e003e4ee9b7b711eee759d2e9657cc741a4f0162937d06d681e3dd4df7b3f93fdd171708ccdd258763ec4edffd59fddf1d1e77f45e70b76121e8bbcb7b81c03c00fc65c771940b738c9d0a6773e10f275f3511757a146743af410e08cc28f745cab19b8284c728d357242208657a8c08addc435464d1e51ea2624946a293e9afe48911830346a9e6c3cdf11e2e0f7f716146b930972ecc382ef67b61fc8147cf72dc87332007f6e10c38bd876bcd5d3cecadf7874cb6f774c19a68aff77ae5250f0e31317e612ef0310c8ea84b4e08fa8a534160c791a88b44b65a8b5142501f6e0e0bbb9d5fdcce289defeddcc3cd61e1fbe5644a2477eeac71a86b184cb9306f712665666b32bd394a4da68bcba2579f72ba66ef1724460e01f51988d9877dc7fdd0ddbe539e723acca89bb2efe13039ce728be4c029fb1ce7e1ddfd307cc2457ac0298b83d8e1399c073cc4880e01780e3cc4c8ea489c656426b56387e7f01d9ec335952d09b98790f864c8518cba30290b36559f9383d8e102b800b00fd4777867980a2287d757ec03f51cde7905f3950e872ff8fb75b82e8e934f98b529b76731d9145b537016bddabde2d3399b0be6a77b17f7833987a37678773f9cbc3abd1f4ed621871b24e61c0e7383c4bc03e4ecb31baf34cfe24c3dcd2f997ce3f6ded0c2d2fcb3f906cd339a67375ee284a04d9a3469922bcea277e3cde9a0c137be9ad921e8c6953a5c49240057e670e51237b01ffcc99a83c9723dfd84bf99ed3526ecb79ddeb9721c67632d863f1ccc9b433dce543c3b16da75428d03e61bb096aea32c375a8efd861baedf7043938b93afb7d88b0178fa0933a5e4a4d4c81b302cbbc7c0313860f853adc51fcc57095bfffff3bedb5e3140aa2aabd61b99a80651db80af7129b4c4ca4f39fe753cfcb331efb8f2e1313e6058d403e601b754c9e0962ad5ed8cba9d61b4beae0ffbd34fa7afac5cc3b69595affe74235ff57e1727db5f29c71abb3f6e4ec10180364eaf91315f077361c6a48fcab1a75c4d6e30299ccd75dd0f02d95ee56ad7c500cc11c8f55f677a733627db25194bb92b1a2a25db265945454545e55f17d9fb5d08e4facb9aae1f379b7000a00decf67e3a646c0543a213ee681a07ecf0977df52cf8eb360065fc05fe3299bbc0aadfc62127848d9fbc466600dc0f66188745366ee3326c5c067fd606fe7ed0c83030004ee3aad74800dcce32ee97a9eed7e51af79347c8f0312ef793f8078d3c4fe3defd6e320da824d2b81dbdfe2582fd32ad844f5698c8f525ab72ad7f78390bcb3597bb84fde2d191f77cdd775c1d2673c1b227c76500c49e823fef84bfabe0afc38a60dc895e123d70080b49f9e61ec2c292ecb3454fa6711817dac9306e3fb3761921a9878c4044fa40266c66b6ec277af51867637116bd8a82ae016fed0a13ec2751903f591467ba43974395c34b14340ed36947bc9049103077f466ae4fb95ffde99033611df51626058939eaa864e070cfb396d6fbf066e5780fefc1c37dc0427c380fef010b3172e3c379b8901e9ee3cd8ab906b7a813756024459d24265107e64f0dd1c373bc470f38a6870f98077c1ef9e3917fc8641818dcd13b86f119340e23c62d0ccfbbaad398110307d59bb39171a1920b7188f1c3e06f66986b37f9f47a0de35a6eba761e9ec3aa16a786c8711e7e93e33c601c23f638f526c779380ff039700ecbe2efd69eee07f329e5d5d6cd743f1ab962d36ba4e9dea7dccf7b7342c0b88df9e9de70c983533b6246ddc56b24ccc5e1302e048231f7cbf2fd2f0e312e447299d4e8a35e666badf5516f2d13ad96256a1129c100ccf65048e8542f54ffc12024156340668b81402660abc2d60b201344ad97291c11b95ca2be1e5efa1ce5b0208c98b75cc1bce52ae532390ebb28d79f52ea4d3030f04793eb636230c69e973fc92393e30e19e1486e79e9da4ffe82c48cba8b766df3af18ee9cae35077d603e1df6f02ff3e19fcdf65f97ed3f2fdb5fae78b87dcd6da972711f6e0fb7e50f997cfaaf9d2ef409900bf330fd29817a0c63c8266c0893b040c35aa3721ffa68783519da099aca18ae0bbbae09afa0417a45a88529b33c23af2967f462cf75daae9479e9631e3bdc6e68c1a68248f1318fca9d3b7f13a776a0327654c660cf77ce1d67e4f624547ca09480b00d4dc02e7a51ecca4209dc381ad6de9c19c811a3f89a1cc8f130cec8432886ace5a5564d9c6530c3160d34b863769baeac35ccdd3967420dccc8b25d2a6345d60c4347168c11b200e7ea406bb96261d89583102c56ce3c0246b80520682c0d6e57abe6c90296456fb3b0db2c0d76bc6eb396d0b03967841ed0e68457072694110e41c37266a075a9bd920660074e3ae7829009373c10d4829d12a020091f104187e107f69a584e11248c0cdda00966e831ba90a022092d4ef043a63bc3fe6e83fc793468aa60227f2dd44986aaa8414f14296862086a1882c492b28972cf1b720f1535c9a6dc434548f688d6cf826d2e421e493e45d306441b0eb48ef920cfbcaeebca2ad69bede6dcf29cef0b4462b7c8764d6e14723371a66aa5f78138409c89327af2317a387048f476a8dd400b11fbc9e46db2c06e7126420ce9135ac764119569e24a9b8176552a22ee207f4d09c87798d422077104b4b09f0c16f693c9376b15bde806f2754920aa906692269d60e155b4b0f0f29b110b85748dbe0a182c3c8c314231681d53935b080c36fe9a71c618048b54d0b41c67e39c0c71ebe8d93d67f7ecd9b3bbc140318c6254ca8961584b2963f2bca83c867b46b694f328e8c7a6402b652f3dbbc41fbc32eca39f5d83bd59edf89af304cbf097fda2b77ebb27d72ef7c9c38c524c7f7439fba4d8857f1421ef8f2ec74b25d06fdae18d2da4e672230c9b768880f8fe341c44faa83888ec472d621ffdc1893fd87de1ce4060ee5f37b5e37afdf5fad227f6d19fcfae5d3aebb33b2b9e1b86951e5f7a8d2c5d98afc38abfaa5dfaed66af77be5ed330bd2d61fc75af8f2032777fe384886fec235ed334fc553c4bf7a15dc336cf4db5cd73977ec22032633e801c41af8f2012bb3ee23b5f1708fce4769c726ae21df1fdb8a3cb5fd32a4a7d9230768650681d330fa1fc25040b1f2f2bb0e53819e9a8699a8f2247c8b08cfcba20b88edd4706642ec281eb9d254e0eeb7ae471e1ebf04e213a595e0f8194980611ade116b90f9108a069dd0ddf57b0dd4162ec9eda15ec372584337a57b0d9a78410c2ac218470369411e9ef8285ffbaecf22f71e66edddd36ebeedeb2190498bd0f2184597e9ba8f932e7dc8106000fc06bb0514353386ae5aeb1d316bd993b0a3028c95df41aff784772fccb148260458e570840a0d1488185ef4d87488370caee6e8bd45300e5eeee9e32c6c4c4995496b7eeee9630b08d7d3492fc804681902a78db51d84290b2a09021514da7e4869c0228ab846b5860371067e2e171e20c4df4e05519e64f468706f302da8364d9342746310cf380c019bdc982655858f089d82b04bb5261ad15ab87174837418a335be0479ef9b94d21996cc43c4bf70d7f7f07b18d9d8795e422205b44f4e6514e60e790a833758032c59148e4c178628c31c6083725b496f029f2018fbd11208d0862fb4e6cc27e91076a10a7b2c76738882d0e49d1cad8e3cc1cce66346275a2146c7cfd23ce00cc1f95d8b586339e60df6e77967f453a1669640552d0c91057405a0106223a39620dbb189cf9ab3ee283cc8c1dc38c88de3c8d94c2d94a944c1d29298c3a3b3c30893c672a08cc47846fae07cb3b8c8343852776585d77712012419041100bc7e5382fc7717178533b4acfb961b99188bb474e47841c8f96d36b5a844f5d23f4dbdd3175c4eb031ede5410a5c797bea3744af107e3212704c70096a7ae414e01cd6a79730e60790ae31c216f4094c44abd7b0660ce5e23e32d3dbba597ba8b0341c03385100bc7053ea2a7904410cb883cce0b3ee286198258296ca474177c04968424969193b7b038c1ce97ae4c5db973c89d3bd12b510cd57d6641e6cc6cef446f422b8c66963ac212267e8aec767b7a9a379623715647ab112caba8e8cd883f1a18362961bf78abc3be747abf0c578046f6615fbabd56baf1f4f06218a5cfb0434b33faef26c30ad0c8df4df69e9f3a53c7b4448c31e66843b1c89361fbed1661f9d54165b97de441a1c1da4f163c87b07c0e89de1cd29de52831c218233c1dde53778dc0b35c24ce32f2d2b19c8bdc351695b7ca63562a7dbb5f4ede340cfb0f9db1e76478bfe7084c085f57de5c94a5d39b93e30d72e5151c0068e3462ebd466eb7a3b7650ee51c8eaf9147dd6fe6146c44f47ed0c8273c77a6ce144a7189fad947457cde99608b4e9e7df6dcc4990b70df80c8d8558185cfb2d935127e592ec272ecfea0c93106b119fb01224d9cc93e9fa3c6932b16dcd1cb6663a16b58a16dcc6b175ad135e6e98557942e3cca227a33ab6d141fe329463f9a4c6113585932eca92c8bf391888b900e3e4b92f2bc4f134079c6a03ce77c4667a6ac429e3a7205ad88aaf979052c8ade8482fda68ecffc9ccd3242cb9167b24e0dd041823e72c5806cde8839fff2010bff02027b09a5441b628071f61704cb7d7923ebb79d114e4a31ce112cfa2b23555146aa6472c7c3932a1739b0f0510a1ce4f8971cb0e2e5fce7498f860b27d44352d0c9b1832c2329f0e4af5b9c8e148a90bfb6425eb38b332f71669281c6cb08905808641e73ef319fa20530b862152e256cc8c109ae147477cbeeee8edd0db1abb1eb6aec9a92045a1c4084534a29a794524a6d421bb023b431e5001ada90d25a137607703d8bd08694529a8080958680ad602963ec18e337f3978ad0463fe6993b69638ccc119ff2d76519638e97dd0def17f3c92c718cf29ab223c45840712e8c5ed725e31111bb9a482831d8a4dbc69cb31fe1bcb00bfb81c5ae26f3921884524278c2a08497d863298310c3221614e3a6e85637567adfa3efcee240b32ba6db867ca984bf23f2d5860cc32efafe840328c5b671c1d8309e408c4e194f5e34db221c006cfa2a4b598c56c26a29533cc4c2d7c489d9b8a49098e7b5d2f6755b561bc7d4b87b6a95fc76a53c59205fb133407326af439a4ba73b2c964b1848d79047e44b7275bb22044e402206186c29f7901236d0ec25bbae086722f4e0fb82d73c84104208af39bd6c622f110ee03ac5b2972c6799e72c4d6cd2db7962d9cb8bab94c15206e11bb3557cce2cc9946491bf4e52a4a40a29e418e42b265f57138e902f254ae4ebc615745032f97bc9c1841e26846109567ceacc107476094755c8df96278aa80479526188880b4b90420c2588c00a4ab8c2da2fcbb009f644470be2764a5089f800cfb682522b49ab415675404b4f604e10ca86884c70cdcc4e91822ea080080f04996147676768880d44e814a5d44026cac490813de58e420c6c802164b5dc518ce104558ce1055f18c30c66f4d82c771463c88137033ba99079ee28c850c5fcc001e2cc67a3a60040a0ae6ba27e20c08413af28818040a402851f9ca025a60d489aa8ec0401c8058c249028a11205105c40052888a09288a0029311b2eeeef6011172f7690ac0a33f1b675266314fb0f0fd3883e7212b7a727fbe5c97854b50824496f88978868e872965747663d735a38c538832c2d2a905c20b4613b412b029ffcd39273c651f6d62fd9a98d1eb98bcaed933a31836e7ecee19e48ad3057d2161e1e375cd6be294c472c4a2db66d9b265777777675052011e368700982057a4b4616268c30b754ddc3171e144cd84104a3803f003fb6519a8a58456286163bc1a9b33b330c88e3dc3b28cca2ca318c33d6397638c1d73288c4dc4d87dec7336f6c67e659916331a23ce6a60d1314a6cbbc61170ce6641291cc9f663c71c081f20e535e735fb03ddfd5386724af99a78c1fce564f9a923c2a3c81d68b2bb5bcaeeeeee8e51cad9b108730c5d843093ff72329c17964062d89c58b7a9d492177627463367dfae715d18c5e6293ebc2e2f6bd8d735fb7a4d2c7537be2044b141264f39a544a26577c309610732ece779fd82311a6918bb21ceb07c733912c227c42cce5ca62ccec0381ba722ee5cca16e83296d1f79db3c212cce095e1a71c81d4eaadf5b262b54a292fe7155a8c51da07d5a880200806913065f6f929bb67e4bff92cc64b39e79c73ceb89aac289b905168cef63c19191a1aa12c7dc83e64d3eeabbb9f050dbeb24bce970cd3c83db3d2bf790c675225e72f0c772b20a96ca8a46d15753d8d3a4533224000008314002030140c8804a3d1682ccc13416e3e14000e839a4878569cc9b32c88614a19630c30400400404000406466da00c0c436764055e2e393cb7f7b1169696b44dc3d53a14306f060f1015863a2d2a8e5843b9ffb1ffa8beda4fb023d1822bb81e7dbad20d60725548b59eca7d708e201e3b7810fef2df099633a29698dbd2f1ced3defba012367169b549e4d97c89dfa747367248fa832b38c5672abb70631dd62fb7c83312f247bb3001a6313212385d857242d53ca162676cccbb2253d5c5e75268c23f738cc0ed896aff9c3a49f7b4211fea5dd17ebcc4092a25af46e284d62ad80824a16614b6585ce602bf3971b0c8d51f74d0ae44034f3aea9ecf41d62ad7d8108397e3277bb31d0a1a1c8771c6c2e2776520bd4328b64ca5ef0d7aa8ffc40782d4e095f26371739baa153e4cfb722c8e14744f4cdca76cbb26eb4068f060f03935dc6e692010fe6aeb8bd090278383d7c603c47eaefa506271e63afa1838ed54ae28f32a6b9064e12c10b0b4488bc7adead5c92720472bc2420b75c37d19f2ffee648bed2445cf523c8b70c463f768135a76089b243a87348f645980f14e377a9cea930fc076a81638f0d0d90086e1d54713a8721f80ec81b7ef4c39bda043b160e3243110d5424069a3480936c7a4d954cd512e0642e15543b42337a6cbdbd8b8d281116e2510fd134f9c9f57848ecf632a45b81b40d5be89fc85854397205ee5d25fad1ae49424ceda8258c431a9d380e047c84615b261da9fd3d61af153243bf0aaeacf3c6b18225e8c690dcbe4416ffce18c230257e1d8cb20e0c3e1fe18fcd421986c2399fa401b3eca1ef13d0293c667ddf4869e7858bd67ef8bded878b01c03731c2ff9be456fff7694ddff5f69e73e8a185cc91e73b8fe6a044ca8a7ca55efd7ea8f8961e72e36431c4611d3fada9655befdc3426b1c299cb0487195a16511d77fe3b83ac56a9502e95d8a00ccdb971c23c1dd65823ac470d3418a6fba0523b28c099062a9e98893b143d5a8e1e9b0f2478495e81b84ac1722cf92302aa9269b36de546705a4bd228249def296ff7fad39edfa4317049cde183030ff9e1a30389330ed683ca5bf18a1f1c5d1c51b32529375d164e4ff80208ecc4ac8f2b3bb7c4420fe3904d931f937293c371d5466e0aa0e34b00d6acfb4e0c58f7f476a28e13f9884e0cdee4b5eb55c5564bcfcb3edad5367a6a5797caf0956978b41005a2bba3275d275c5d210e7f919a800383ae15989db5bb8c2f32433a440bc8ecf88a61e53b75b7ed8a01c2e4df9c4062b1c2b6fc48f701d70a8a87e34aa2014adbc13e7245a93bb0c2a6f41108779272ec56c733252b48e88ecfc0c57e7b989db3a5ccef273b165d5394961537143bd85c1bef0447647df647a9ddb3eccdf51f021bd69a5c4814db14a2df1b3986e44abc4dd8c2e9efc10bad6e0edc875bbff776c47ef0070354dec4310362cd23194b7037e939e3c6dcfda97aec58aeadeccc3afdc97f380e9a284383367c04da44e6f14a2f6f6046bf80c9598f35331f1a0e5113ba8603c2f863028743239e6fb643fff67e8948545b549626777658a5a50cc544b4e1639e4888859c598a9279f210a81dcaa528ceb010399ad36a9dcb4dbe86e2dfb9b1ba3c9169a3ca4ff1c4d288165573338e200b8e6140be97975c0a4a01d11b881ba1b0688513c657481b947a58c256a855e657015c8437fe54329753b0f14ca5dc2466031d3003d348ac380c969c5bb6fb579cc23582874b87202068b76b906831396ed9352a33632cb81dccef939a2960823a5a8714fcf13ef3f4521445ad7fa4d496d3dc4efb1c7837a2f9ce1089147a7b683113975e80d235164f2c02992f69340182eee9aea434f4c5b3f53f804bc98b2c1e60c6d0c98471c064e711a9cfcb5111fe990de1f10265f67d28cd21a74a4a6384d49e53e43b191a7e781f1d301129f523d964b37957d8e146ac0e673fcd126e69718fd540016b8314dee79fbe432bc35daa3f3078388aad066ec691bd0709840ed1d930e67738a2b355b4cfda2a494a9cfd26c6463e17aafcb5815b5b48ac8445e24d5f1cfda0b9ef3777c0f24a2f58cab0e4e63fade0cb9d2cb593caa285d84c4125bd80a1c6aa230d2c06e34a2c78fd74dac4733a1287122223f3ce7351cdb88d925b0edf02711ad3fa38f264d2f039299df95d04dcc0b17dcd11f1149f079538689e70eee6257ba8ba85532a9afb879e765bf9c64522a7f5aec4cbd9d8bfb1689f41dea405ad318c868aa5391754a78f8d35b47db53e8ecf87fec02ea293550fef24dc6e0cfc11e93b502d626c3afb2e1600409277155fd28a69bfbf18f2a09f67cdf609f61f8a3c890673951edb4f23d78920cdb9e08b157b1c168f45ad2f8cd73931a232c2b62d5c4cc108516dce83a4bb3260006e8848136064b63b69c5e8fcb9e3afbd61d9fc4623bd2487b5ab41c337246d643af851320faeae845109fdc4600f216fe429290eddaf6aeb6a8648754188c8b3ecd379e96ded7132c80288aec874cd785721725ca353b4903ba297d656f76292b5fa5f4f9be561c4de639a6aa0054f89af41cb7b338048ac33e7d8ba371515f93c4badaaa7d9222ab3d6dd2906da792a2769d17164a3ee090c88c545a3694bceecf4ab1e484acc4d232c63fcee98c29cb492f40352662cc0147d07b2580a19392e91a0dbe45ca52a0252f0d1fe4c976749a875e91a9824a4ad32df1acb490a2958b2b571c5061bb57b32665108f383c4a7d463fe69469322de535b6436ec0d31348b52283e2ac868bebdecc72e517c662ea1420e2ea0be47f8df233a9dac41e771df965e986cd3557f8ccfc73defadacb0c835c53fdd2f0fd4b802e585a67f29d292d08f49912853080132ccac600496561df98a1fdb943f1a53bbf403c907e392a3125bcba002b1581853775d46ad8a84d89b9d0c527f885ff7a1bed4a09bbae225512e8eaf4bac07d6eaf3db807dfa8edbe84036738d189bbb8e0b83da99150ec914475556ce094223ddfa764366ea61d81ccf668c9a81564ad7c843654ef76b2ed5bd8407d61621d86023f2d32b1d5b38784d4179f1db676e9f6f96f97882a70cd33e84d51da044ce43897a29e8010191dd76e3020d94cd3bd912696a4b2cbf873a099050f5050e1ed2443331784cd4da9d0dd116b5681b20de4ce509416829d191ad8f178d8b6a168a1a68f7a0a9d509b59d6cd9d732b57451ba7d59caa22d1c723552f4246b63fd06a5267695481dfad90f4a1646f68917cb45692155f4558c59cb9e6f1612902f88593d046c10915ce832aa5a74a0cf22c141fd5a75c91e608260d02db438b5ae0c2d453b39ad74716da369e58239773c59fabd7cd27ba89d90a52ffa0c3b4624ae55a9d99aa145307dc6f5675e79e3558f1f8cb09086e9e61f976c3f7f9de8331061585d02b49e0961dc181c4d8e048c0529375f4f8395255be020b88ef7e248a757fbddd76ee806ed79c8533fb2f1f8b00f0b42b8e62e22726a128239fbe2fe73ffad1d0478714e044a519fdaf4e4706b801dc16332d2184bc824e9041b6a6734ce7585cf52fdeaaa9498aa3fa449102819f0005b45b9f6667f4fdc400bdeb3c8d4f96a8144484ac9b8192bc7c2446cc3f370c42559c58fd2304f901cbc2cccd89bd5c4db98ccf407e05db64ab4c2176dc67ff2738b667f9706aaf55578e3980512f3c94e1c83fdfc096ca7b00ada08c4b9a7f2981dd8c3f3f323baa278672bc14f3d8d8db265e2b4c21815a78f7d19a500fc55762e262930f4b3128e2a7327b23dfb944c190fd25da2a7a6c2f28809f1e7390bb4a77614e8d9dd4d11a9c4412a6b06f6829beb8d73898743c877264b60ec764d5fda11561eb330f0fa6fc9e29f3c61444838a18f3b7b598987bf87f83cf8a0c707d900fdb815b7b5302e2e0a0eeb5c011cfe26a6475f8608b5ece79af3ea01222d31b9b3853f20b4569878eb9b01778bb180136b8917c6130551a2ff655fc189b32c56875b63e8277465b8259b2dc8a724bfdcb9645eecdb04da3c7ff11aabfe6a5f52026c4cf1978ef96c9f31a56c230882b9a2feeca8b39a187e28e1294e2f79aae36e05dd3865255b32214d4ec03e3f435724c100194febe1452bdf276310313add860c512a98e9de30fcd174571a482cbf73c78e5c08b4f9c49db2bd84bae61967dc7b7e7e807d9ebcf9af37e5c1af22459ebc14c8b2108d54b7fc92f0e415b25d293d9c98c921088d0726366e4762d5c16b62a72d3e058764ec60809aa67c49937576038955367aac259476368557b563c9b418ccb7055523bb40ce828e8ce4ae77d8b8e9acaf4cca4f081361b3b084f4f935099791bfd4b1751eec0aa0d1a604816ccb54923ba5798186c0690090ad100ccab762315eb4d0ee2fa776a9d09933c891169770441c83ec869ed7745f9ddb91d665069cce12176d12dbcca8b1048224a5119d499cd0082e0b24f74a6d6cc4640ef820b6ab6836cd6d374099f74421d39336dd554ac6c59a6328d5c4ebfddf7e5a1aa4db59b44912b25d2b1637e8a185ac0fbbb455a2cc51c3d06059d4d3bc9b72b419c3527e85cfd0ea43c1963f023d279efb094fa4a962a75a8812a24bf50fd36234b848566b364e135b53b81e8ac88f11e3898cccc3ce692e2bad8a36dde9b7d61a6aee17a0ad9ee49284a283424f4d9130023ed4858813e0347fb41d54f96f7986dfbf09875b67ab3ed2a2c0b4e485391f37334b7280ca971cc8638e572d60e46b5fe4ba501c40d5b777741f316c6fd5c56704405ea6bc294616ff95d3ebfbed3fdcb256d56946b6c04e058d732bdcbbf14c4b2b5465581d70a26202dc5d50b49f321dbb18bdfa69a0c0de8227873ee9b67647f8a3a7bf381295f219414cf7021d7b633b7f95b04f43857ce6998beab7ed66e02ce435f378ca9afc2d9c97267c9289dc9ab53f92b4fe7b2b506ce9f49b073d8bde4c82496ea32c16c043674bd820f5acb061b56b0edb407cfc93423bd161eebcc7be83b4127e1fd4609651aef10343abe834f1f164a3c949275cbd854ab9e294e231946bf9d5bf170a3ee72466d9a6562d7656d0525d3209ec8d839fa8834a3af685010b97a062ff3ae0c1a80a111424f8828620829497fef4953551fe52ee21f884a3fd53dee6fa17644471f1c59f7f7e01f228baeba95416b67d24cc975394a85685ec21e576c7446fbe1a4a701dea87f7381a10a929fe6a751e80f5d7a679b56dcd61db6553bd133de15a585c1cfc32babc38e429df36b9cb1b3e93c10911a0ac7665edbb2de33cfa3b101312a0c8b11e14b129197ff0eb9f121a617c44a805c17b44943f5cbcf4c288a5aa32ee2a5080ff8b943be8d6e0a68ab835a78f68c279e43712e350c34c502ccadbb24419da5c3bf22bad29696e0f3854772d50f7968b9ca069a2728d3905fa00329552a684e4e9e5f82d55121c1604db0ee006f1e0de516ab1d70bc0ea68472480b58334b11f2ad7a5ccb5a4032ba260cb212eaef0be6ddf4c95e11c346772740989e9faadc8f8c11876f861e512b894d47a39921ab4c6460c6e8bebc09354fc804cf9b59bdad3cb94ce9085005eac5cf49533945096844f8a1113b7b4af69ee9e75f7d0099d6e75bbdd39c240ebbd0213f73a4df4cbee7052df8dc2cc845f49324955886d000e51c89c8bd9a78c043fd1e48941fa253a1d8fdf06e00d28860ba268e24d049d96a813972283575024338d4c151e95c2a4510653434596a7d1d305581dfb7be6c5115b9a93152c70f647244f735a9298faddff763b70bbfa4d5901bea34bef9f4219f634fa35a93533000336dd0d99116e4d3a23880084464052a2ec469d321ece80db6bab330ab185b32a876637306004978b49bb01081d3a58ec5c3b2f9b3b5db17a1a4d2e9c9236631b8624cd2862fa1c324217ef737e0e46cd79c08f67b5f39db24580b8716bfb096bc790fe1b02ac658c8a3c33cf064d2ccb50905963826c6850e3a4117db24ee9b2bcd77171dab1bb4aaf63ca9847a0e70cdfdf3baeebb801c617eb6e7dee919eeebb194b238fc8fb64705eaf05ca0e8fc4f56510939759682aa7d5277b968c89e172ec3aeb851a16f05bc67c4a6715cd10a8db04dc5deb3457c757a65c8a3c667c5e3ba3c81ceb5036cc17bae2a9324c055af9e7234664c3a306f5450f3162a074bc5048b6fd7663501951c48015fc300a7a97dd7ceb05a8045ad549414d086536d6ba4fb877585d07e99f7106a36e009c422fb77d27df07a1e93137268b1bc8a23be72272f76ffb3a32bac5248c7ff8fc7ad3d146e27950700e3c2c59aa1f5c4a0d083d68187521a83e36b473cd5005f19b35e6edc23ccf897a4c3fb93f859dfcd1f0f26cc45a40859488c8c4a11ed9aad3d6cac4c0c156f8332cd24c01b1bde76017e945da6bd1e13db405e8aa020319a69b4826017a36a4afa4a00d51b3705cd4f3022e88353c8a5d0dd1fff6dfe234ac3abf1e9156411e34def8a982a74b9911909931e5338cedc5342f31e5e251b9739911c2954798b5140edcdbc72c2db3d9802807395a921921326292c6bbef0c4572a97bb5d7cdd5049a2294788648820a1fc9bf143429fb3379fd88c2ca5be114100b2711ae057f598cf75df8ef1c38865325177b55648a1d15b7a5252d04c2edfb49079b9a3b4046b1e1f87ce526545a018a071aeee55747c87919e11237180b5ec6e02e41a2e5f77de1d43155514b9942ebbbcb2ed59a8105e89fafcd38858f8ae336a7afce90c14e8eaa874e40b64c03afca87c1cdedb4261c9ee486e88f09527e4c24feb0924caaa40831326cb457a85856eb55d282780f5b939baf9264cedbcc435326cbbc3b661ee33848592d6acd4d983c34e06a7b5bbdb4af61f1caf746cca12648d9da68786846bbd6e645615496d98242df15a44904dbc2e4721600338967605d57fa23233cd0d4af629f0ec9e6ab90fd4a7412817a33467402b8be0161a62288bb95c135cd23402a11918fd250f3d6d0904f1569af6a54cbe389600542e2fe0b3707eba68556a9c522e8fdfbb31b6c46212d765977a0286045fafb3918d38c38267b0e3a972d811264401cdf3586844982dd59af7e0058031992c77450b0250bd7e5764320797d48a0f3f36030e45c1a21e8be1cf44814d080a7b15e267236f4740a927d028e4ed0c97bfecee88272bd827341c703b1d4c857cca9fbd3d10a96b3e6f9fb1421e1eeec91d1cb30cca8506c120dc7a6c616e370138a02f2ded6940db5c1e92941c416c2f2af7d50ae5381fa970c8a0a44edc0fabaafba29f38c902a34f9f523050d944688ac0dc001df3ae96a435fa621cd81a601965f2985b798ad3243c70765eb68f7f67e1584400b5d9e2729315e714ac75b0a0b32c194a75a3e9c0f0ee1acbdaedd40214324409db721cbff86ca6887b90b800e7860b9ada5e3cc499096a954134f4e765f6a58b33abc4dfa1e0221b46fdcd08adfe3953393f16a8a2a117cc52bf1d0f428209ba78b10481c55803e95cd3ec79099771ed3507e7b5f6551fad4b49f0f7a32c71a052187ee35c347df512ebc40bb7ae94a2389137dc510b694233b731af2a00bd1507e07c441d9c2f219b02e923ed5e5b7a1bd0b544e226474274ec8f35a85130468320076bc9ce0c26dd033b154356d9bb33d46c556988a5ec27b97de18f4915751cd18aba8dee4a73eb172742819871c40f9495d1121f4becdffbdb6617d1abc3152254489d6236309efce375519506ab2fb31c2448b9b32d91efeaf93b80251915292cfbd573dd5ad75726989e843cf9e3934eab3bbd7adc7087be75d787647cd70a5b69ca3337b327dfc7ad5569e23663ca5ee844b7480054b31fe72b96387aca843c0b59334d599441dd664685b56bb9f4db46997cd731d2da3b0089ff13c11b09f98e135666da5306a248611337788444c59ac0aa5c614035ff50f8272cf339acba389309006f8c477867590bf080bddf6bfa523027ab143c68498b11728afac88f684ccd8e04806c91d9312fe8411b6a4937d8102715a0bc838a49f1d0e0b9e27c047a1cbda06fefe380392fa1fbfedc21a0651d93b20c81a1111a7ed0c44cbcb8247cb703e575646a5241a663350e5ee0191bb9167a60cb71321cd7211ea634b735db159bb5b420b343d4073a333091046c703e45a5e43fb48b98fbfba7761d74205c68f733cc25bd16db1a4d633a9cc89d0cbcf3058f4b58e231da5fe8de53672689e03de0ea79afc6538a62d36d729c33c58ae66712493a44c9799d4016ca6e249e2dded55ee119f8d43ad1be5920f725668c8f96465db3a1c9ae67468b8fa006363d6d2fee85ed9eef9a113896a08d35bcf3783d121589757eee278b94e71e4e94e41bac9c0e7bb844740e3abcb211ad2060947948086da401d702af6dbdbbb18bee1375ac4b2f088909309677193aa468fc3125f3d6170a1afd7fb1e551afbbfde8f61d0b71d00ac1fe932c08d62382c1f97af39417f7cea6981b125f2f3b5f7369060f67adf4dfabd5ef3da7d1eaffc64e3178c0df4958c132b673b7e24dcff797937f72567034de94f3318ba0f66c241df9003c2f09f9f71b71f7974fcfb6e2459dafe8f2135090fc1cc1468b449dfc2e5b67d926c0af2be9a478da0005d53f3fe8fa8356d2470d01185b0e45067293aa82fae2ce8445117af79631f84375d7b4f9f4e7855a9457d3181c3d0a6c13dab0a1a41c8aac32bf5c41864cb436c97e7c739d749b124941dd404c9af42f60f4da322fa05dc5aac283f9fa612567b80a664763787b9f1761fa06ba811147deac1207d0c6ef943c33d29e9a031bc144b10b32e30cef6b7d5c7ee4a1762825590e05f6b3afd1212f95458bbc1a545cd66ba2adc3b9a55a2aa3557770c610c6d051239da83d277001dbbf035b77c7ce6da840f04d43030a3f065eb5f74a0f70b78f2bdc7bc9785c5b1369c9115d790cbbd8cad3d8a23d1efc6db23b8faeeec31c15489bee096ec7381f8729e6886be1837658b80a135c4ee7ffa6d06c6fa304994bf8ba9c4e7edc8f50836ce1e1c3d386a1c6edfb9ee95e5b1bb52dafa58f7be9ce5b0e882195125a53c4e33e795eefb3352da1c20872bec804997f96659f111ad109435b95773eb88bea1c124c4516ee559e7c581d9b1143ec5ee10f32c519e3227882d82c4e74250d429c23b044616e0a70cdfb63b616562f6231c37bb53a4e4206c21ff4d9d1c431a65963fdd765f068daaa97d009778b37dbe92fb13bb0e3ebab4821769b56b7347a32d1f0631672ecdadb753719d6a17949fade60ec0f1d942bf35eb5fdef580bbda9218bdde66519d8fadae2c4f36ff389b5e8bc79e67846aa4faaad24854e95a55e654cc4c4da3720feec8dca98950ba6598e9ccacaa09a404d52945f55248e082d366abd210594afaf5101cbda6b4ea4abf6e3efbece722946f79c2de62772118514a3e5dbff9af6a22b5ddd7b152eec6709d74341278a99637a94e297d4cfb40c8e954064fc3810ab0ea6e5a2b8d77f6fafbadcbf05385f82396fd07a0441b857f752c696a469414dc1f63fc4589df6a018d5cb6a54bdb3c59219ffb3570172b02fdd65bddba677814fb1dd29ae243a7f060bcb25b7725286bb3745eb7dd95be95bd7c47c1aac839153f2e480b0b1ff021e8f4a4cf3b34e5978093a48691fc83150f2e875c0cecbb37f6381de797e0ef00dc8b883d25f9dc24a2a034e05e45682bdd7fb3c865a501f63e52af077a3ba2501270af23b49502945e25c95c44e2a72753a338155f4aa2825b35ff71efd112e499212e3b7a81d00693c768c07a13e736bff9b4e35dc5accc57a344bdbb776326d4fc8e870aba582bb7f1ce91ec7993d40247a9e4491a9ef95a5853dd4551a860f1c5b5611e2d6adbc2aa5c6d152e5a546a87f6a3e5a5e0221b658e30cfdcd11b9d873690b833c345182c771b0a14f357ec53a0d611649b93af066f6ee3a460943cdb21f3724219771f1977b5f3846750f6424112a885cb10b10f48ae93756d470ee3ed54ed81ce8cae6c3877db6641b0643689973367125ef406f696eb58c67dac6f1c1767d4eaa82d0f4d35236c998b3c0bbec8301ef4871eac90dd66739b2c73936dd6eec169479eff6d8632302955ef3e01e20193c49115aa31773c625a1eab0d69c01fdbddcf56b6f6a5a799d34d86438af30624bee30d6aa1498514d436a73927182302d2d4fa426634492cdff758548690817c85aedda916b44073f881d0e1ce6aab2e7aeda4cbf24cc324c61569186dad962ec6de7bd095ff7b0c8d52f1b64c9686f70c73a2e3c6e0e29c283811b93e76f4579878bf19a1a65cac5cbf1dfbd53ce1621dcac6b4bf1b5143a90661d14ea2287de3c97d1066a4d405e76c20e8d9ce6a622ac9ee42d232c60d2ea1f672a24414458366e6e12abcdd3918c01d7e70aaf05ae487994a5b84fb97becabc2e6a564d1c0638516c384cb27f148d497f208142b358366adeaaaf229e360b28779999395849d5fafcd6d89d96b05267ada1ed91f9bfac8985d396d38f464407d46017dcb4867d7c5d1ec491d235a0d1c65233840b9f8bdbd237097aae9ada2618c883db74dffb7b2f6d0adb19c76492332b6e8c80d8f3aa6c582595464cc0ec84dbab790bfb1de86cb54bbe24e0ad25b19ea57cac39504a65daa8791c17b2d3479dd373ea8e9f0b7ae0388da35b29953c9d46cf08c263d24e66a91195dc748a67ab3ddc1d1b565acd2a55109b199c553d040501ec9c3c101470bcf49e45f9b40088dd22a0a40c2895f8e28d58871779e145562689e9ba8893931faad1ef3b13fbe728d11f0dbdab3d898ca387ad000b817166914a4dc89800c81c1b526ffc2901db2bfc615a645ea6d5d49ef7728900978c9ea1a02ff31545255ee28100e4ec7053a2631e946ea050df4559fdda37ba1b8ede8ceb446ee7e24ef41a7d0c7caf81b65b67262f7842ac705fe36f803b11032c3f4eabd191de8bbddecc64624c49bb0c098b73095799d5c69b0394f3f884c4be4bad81d892de9e56b8562be2ef4d20ef440d028a9b13a11cd8fa6fb9661524895dd322739c762bc7c13ab66d7912566709ab1f522c406dc6e3b54549b86a7b29fea9bc4604bde5d1b4330817d0d7647be40bfd5030cc6601df002854190fd186ec7ceb90976503dc76238653577055a4ad1034198b32d30302fc9bb6560af0705596ca06b4b6107c5003c225bccd708edb6cfb9e84defdef90357990be679485cb9f112cbbad0c9513d166aa8184818749807b2665e0a08234a81b8d6ae0558c251e684d3c0897d532f09a40b1eedf699639a1558f6c75e3761350465114369edecdb9e04f2304527a68183230327280e61d2ca7b0619efa07e98b133fa0b860a735a04619e2002e9e9a71732bc51bde299beb151d4d5b692711a827bbddd1e43592bfe844ef243065056e7f40ab7760504c6adb9d899c2a601eb2e44dbd635c1b051a8ffbfb1dd8e9029c9d15a4fea5b74c905cd615ad9f8643a9af046d59dd90121092022c0aabacf2a7dbcc82250f451e008646d51f0c655a6c86796016fd266ff7b2ce86f13fe6154a4665c7333fb049d6f7f9198ebfc7936e74340d74c3e745e6149eb31988b046f38e522b2422a6bd154ae0fe6e8beb7aa4c816bd11436a59956eb134426ec501ea9e631aca105dc86c151c0aefac881e0009149a496091be97713360f935337768040b217d9c407828882dd48091779884a834ca2999c031ff13681a1884d3a90819ede08e4b07c8fc5747144c598106e02d7064fe5207613864ab49276eb27a370b197348bd556d9881174626102866a2f862d6d6c10004f2ca0887d7f17f17343074c401d78ac8fd3f47ab55cb19f757a18b1e33b324dc8894f0307fae4575108d37cd7a08aa9c157df624a116a225a1aebb0fa04b38009a9cecdb28d20df2629a4346f2b48fca22bb65573bd28652d02e4709b663d7923586ff586f131a1c22f29f5303db8e5828011dab99bfeab744e816123123b66bc96e9e64e758f805bb202dfda12395c902566c5f506626f0e2cb038a9c7676817dc72e1bf196942893112c3468e306cfe1c0ed432497c921c6b7db911b823b7ccf7becaf0d1dfa618accec50ac6fcbf48b2e01bd3d781a7a8d21bdbe54c612ed8c8e433ca4b7f8a5328d1dd9098ccc63801d171a0f131db61dc46878a0923693c19f599dbb418fc85c7c73b7e04e3ccf5786dce0e06e084541d9e1db27adddf10bade7a791511e0481bcbf048da90aa09bc7bd019eeb51748644e9e6a923a363e1bf5c6b537775d5e722058fe6402a725e61fcc89a6ff6e379864cafcac0c03a844438d9aab5a97792ef193d7d5023d706b9a5b96c865dd4873b350e3322886d3a512bab9f27c9fa91b3b870e26647d52626ba26b332c17a22be9d4add760e3375d028fd10e7a6df2c416119c56830b8e0fc91c6235d39a162a047cb692e56eddf63d7138b0e26aa3fc693d7562be780c432dd54fdd8e18a57146bda6c30623b846f82737d685b2f42141c03edea0f4924e55a45bbbb07031c9778047e62463849dc3a2d25f5666a2b502a1d3593ff07f04bed875ce1105562492876d59193e79ca4135c387d005cc5e758faf234c8128f740c35ddb4903e5cbd69150b9e5e145019b7448c24b12aa0b60c1c0461fd0db1ba51eb4a1b81aced55864b569fe754dc6418de827663bffe0b38aab8cb0b75ffba296d84bb6ee7a90d16ff3ec936fe547877db655672288790f5b9290ed4f9285947453ee85522da2e033a6f5ccd52c828803d458c84647e09ca9e66c564d46b10c85666900bc5149bc5c6468341b3f737987aa0b7dddd2804e9f5d1f0318d910a14dfc9202141cfd79a95aed2fbe82631584c8a44af2584d8ffb2a56c90fc9f1d97abd66fb02bbe384d339fb7baa271dd04cd23f7ffb3faaa7bc34843f0f4353f21d3748eb401ce0e795831e0db60024f47884def0f5cc6054fd99906ef4c46fb872ddf55d3043f4e6c23b3ad847f8232f7555efc0fe5232d7dc7c5b3dac37be54f9781e030b2d2cc1f41ca1674b116127a7ddc50febe8eb00f1d14298791c65381eba2fb49474523e2ec5307a31bc65171ce3f098087a030a79a70cb844e88d1213cfa58e54904d86d2e0222075148152a7d1a2efa27041789aa4f8eabfcb5a43f2f905a6b01200967a61d15f0ab11cc558d388fd40ed4206662ee52f41930ecf87a0680223f8ee0b1f197e21e1666d19f6f4d5b871c07a92ffb8b8e90e30c16213d6a8d2a4367b5d207fbcebec1842e5dd7ce59a9a3f330ace7e29428cb660a06b3436b169ec66b92bff32f7368608a25a3ed7f16ec4ae410111a226c0194e7aa3a98e7fcd3ac238f6f34f5fa5711c37b6081281de0ab688ee667448fdad44ee54da9cf6a0f6f94cd0ed114f019b5bf2f9e6ed21622f3699a4437136159275c437736875adc7654931be3bb41d2e753407be0984e923fca1b4b086a5912c24b104e922c7ab58fbc374a12d75be1bd2a48285b00365bc3692314369eb1d71b85c42f09d747ee0054c0d9eece12f11596e86443013058c81843033b96f2031e8c2c39611d1f6fb4d384109fed2869b4f5313da0b9800b41ec60907fc9894170486fcdeb5835960ee31fe63a0d21e3f8b1539e938f775b1fed43ca09b0d6f9056384ce40d992008ab0bc44696cdcb99d24ab7bc031b40ef7ad59c7bc93c47bbdfcee8f5643c0504dfa659d794fe8f8643a0bc2f3ac9bb99091af3b0ae2784e23489f71d22f42a09e46b4de865befa2a346de0bc882642231c1f76cbf6094c23002be31429d12222de7450de501cd8b2af9202750593bd38b6c35e2cd25fe67819bd523ca87e5c24b7e517b746a23016f645ec988393a89f1122b62fe4862bceb1dea888c825bd5960d4029831af2b9aa1075c647291690fba5f82dfaf332ed2540c9bf3471b8744ff79882d22b5ae7d318d70192b5a12c4460d46cf1af641a69975b7ff07d41ef2a65ff1e51f36512af357c16c414055e5b3eef7b5268ad64650e4fd8c19f4f2fa3a842d1f2cdf0a49005f5d0350141f9490a5e2061dd03eda013346af81468b1855f1b9718020d0d158973a41b38b270e3ced6b50c4ff42bb78b4a3361b121ccfef2472d4c32592a2a3f4155dc202a8e784b661a1a28d224714853636900f10c37a31deb824732a3d56aa2daf861ed337d7cc89e54b648a78666279d903d475f7d0520561a4a2e1c25ca1a11ee59543d90ee4b80aefbd9d776187edf7bc35b3df1efd2f0c8410d5bca2536c75bbf08f976efdbebdef84cbcdc40916b9d38f1a13f32ce1f679fddd7c24f5f926c1a2c0a5ff8f7a657eb0bd21f1a10a0adf82e62abd75d057b0960a08773cdd04168a40ea6b9c8cb1bce3f101b1fd1fffa4dfcb610905f8279b9d2ab87f2db75757ec60c93937bba28a78d31753e536f04f9e84d6c7411adf693c26ded1a4451d08afb978bbbb2bc12591f520a5d2bbf1aefba606285c479ebe5acecba1f008bc9b4afb3503739bf1e6bec461d55dfda827fb3195dab61702d48c505b88925e2cea0fd0bc86eb7bafd2f480562bfed37fbc92b2e2e3904b07100f8da40494087a968425c85bc743e011b137129fedd7752a66f1dcbbd975e6fb14a047ebad528168f98d9491fa140717bc20dbeeda0c0aa4ee2d0498b2e017e21fcc06c5a8dd63dd56c7d93ac939832189bb20e7816c0e0b3d93e7dd3cd51c79cc27397565fc31cdb4ee34ef9365c2812c341c70e4d312e0076d0154dcee2682a1ae2aa9db1881acd181954a2e1743661cb7a3f524a855c6f05e4629578c357612aad2b23bf8b8e9151b14a008d4cdf7d5574d330706ef158b04f941249c139d7b2aea6e5bfa16d649055a7706e8b883c0d64a3331d08a65e56dac6fd367ea257f84ae464b3fcef0555aacfddd6f6fe9dc5a2c28f2b0382069deaf7cae4c14dde2a4af2540967ab8fed78d2d377d70084c2266d135b61410e1463f212360c290d1c7c4792f721b046033d6ed7acf193405b973818af0ec7e026c80d0a28b9fdc09ea73bfb0d0fecdc347d193d107513115d520c0739fd34ae7525dd795d0a4d6a8bd189042303c3944dc2c3bb913d40a918722ad5f08777bfa45e90125afe3160a9cbc067918fff1847147324879610249f643f88086295a6d7ef1b1ba0c6999f4fe41f2ff9b88d54445b94f58b066d56d405b452b3b38c8f51fea981f4a21da56426c1cbd71cba4dcf362e432481e4bcd317b5bd0a67db67b0a559dcc252b459ea1847dced7c8c8b153e3abe034f70e2b29f708783ac9c8dc073240f49a7609662801c9f89c48d55772ba037be420b47a473329e21e033021ae4da13729695a7bb563f6c277ced9bc63e305ea3bc2b1f9c0e7227099230376bbad2091e51edec9e4dbf0d2854f8e75aaadf13a5adb51a95bc19cf53f1d13716084390d1e765f3d4b296079700bfd7647086e9e01d4e35223a303b9a10fc061962bb29667f31261e7d6d00ab8b044ca292b91ddedfbe6877d260096660e23c6b8c41e883763b636c71d787c019b3eee8665c401178af18a6403dcba1c8b6fdbeec3a0fee6b2878bc03f30a82ea62d2cd60c46524e445205c68cce4a49f145e2aea07be1e7e38e41ccf7e091cddc2ab0f708b9d210c8bfc2af7b4d9b7ab67205b53667c8e7f9274e2eaa3c5e4cff1b6d637b802f189ab81b1528f912e0f398a6e9b6dcfbf2cf968822d95a69b1bb98f6bec02f8b2d4dd4e5ea78dd4648d4e9835c2c5d6ea6e57a633033fef44eb478a056a485c7523348583bfc549673c39768c9e0c51649f6792be29a213655f54ee2df44610776c42223ebf6eccc4fa6eb7f122fab17d82b85941859e556ec947e5af5ed293382485a5dd2e8fe0e34edb3e6a0804e92f23a4bf9892102abd65310a2d65b1fb7147d5a29155deae468fb8e470296c6a9c55f04b2dc6d42ebf8d65e6ceafbb42cdb526f7b1af844b7185234b687b6fdb64a530c5d63b60b062db1a9b2445b6d424f563f784e0ec58c70ccfc6c6ce04c70d2d7e48767ce7ac6989679918446171e50791dcaa69a6125c580e5dcc4ca1bce6eda84dc3539c95f658c37075b16f6843b3f5d306cbeacd5ee5c14906f58afd327cde5c43d5cbf7530ac0d510b206ff85da670d5ccd1b4c40365287bd48fd8ea07d33241f7da4b68fb0b4a4c55f481f24118143c7a52c5ffb219cfdab489303321cd0dda1232eea3ad777538a4a7ceefe8b01e7e10681d51bbb283ced841ed0d0df633d40fb174de1397c9b193c0e638828e82e2c9a20f59329ca4287d61feddd39fc3e476e4d5800cf9fe1f2ec61ee4313409af24d344c38c57c74b3ca3c0d0dab0ae4e8b91c5266605797626ebad7ab3238d4a7411d554c2c233d6c96f0cd15fff9c548cb6d38a7633359294a4c2d203297e0fe8dbb2392385632e915ae5d230d9eaa57ae5ab014b4519fa4aaa953795f9385ff9459b05b7f88f98cce6821e3532f27edf388d9d98fceb5b92c3276c4cd6567375a29dabb52a75da242b7a314809d267959b263df1e6f373b9c77d8861c95569c9e9797aa02b7883e0027637e1051995b20aa1e33db0bfa6a0c679088da269cde95258d64874164f02fe2a716bc6805bb2b5ae2e85f77508f28d09e5a493fc7c4b4ff811d0b205399221532c678a4fa0b95df74ac3c736a31654306a9dcb4e9465a15adc26c57442449a8c83319efdecf6d74592421c0860b68aaf60728912ece1ab8daa507e32caa501484fed83c9a5689c2de2ec0af973caf23d907b403a64b6b1f3c21c2e12fb07afb69b89aa6b81718dd20eb03e086e3cd27a9b64ae9b033b693fc4e1592387b1aaf070269e2f716cc694246582a7a7056c2ff46985a397770620a0b33c81ca8c908f889cefde81ab7830d7162800443649c13961076ba66bf3ceb306c45cd5848f8b6bcb6495f7013fd3e84baad8350d369f53c105739851238a9f0c3bf7e03cde451595445093100e63c376aff60a216a5d51c43ec80bf1fce647237a7f22bf19c83199388be072c3982af0259066fe01afaff41a285f6d990bf365f5546d455ef7ee8aa0c71fc95033068d0fb35e36278a00701525151b92729f20716ebca2a2a91aa73b8b3d7cf1095874df4b74ecbbc19523818c603877fec492228aaabcb49cb0c5f8256267b76647002d288a0d23591f620067e987c241bcd08ce276a073e21b3acf61331edbec6bd129c2e7ea3df45be5808109dde59d1baa6456912390779193ef5177f0ff4e977552ad603758a1373dd6b5094a626b55de0f336a058e390abd183a49cca8a965afc770cd8279be614b1ab9d1e3cf458490030411c2e1f2f04edaf1ff1a33d3e08961988b2ce49c2b0636a076cec93e6b2c90520cdedac14c4a87443ac5cdb189a42223ee196d21483486df2a64820c35ebd7b680b1b7f6a3d5dbb5756a7ae7d79a82ff82ff440b4ca9afcc1ed44dab63bffc93a3ec0d87697d2290374b472749fc19a42ea1db1dabfc41cd085fe6d52a1a557e8ba08e9d01c7db50515c2a32e3c5891a7ddae9223edb98edaedaa01915330d2b84e2e27a2b90ab1098c149a81b60b522e094923e158d9fee0f9b099a281d6ab3e9445ef4f4fd062624736cc9e620d256743c62c0d387c348c2fb60453d74f1a8a6164cc68b81077cb0d29d4d2846ae974afff20a6eb3df9e464f66c47b4bea18301c2eac14adcd9bc56eb6a12c7b6bf45c0acffab7a7b1fe70d69f6ae5093143d7dd900ca021ae1c97700ffd1b16617b151e8fcb7ed40af480580c9cb4a4c92c69b383cb88edf652feebd84bcf52b30df8dc58f485d0e74a8b2fc908bb69f2271a9412c9077331f537765b857be7d6ff48e0601b03755eaf7e5d2434077215131d55aa3a361ced31dd9f7d055e6cd589a5a48a2d43b0121f94b6a8bcb8353526b8c16ad83f7501a2df9bb237f25fd05bd8449b295775fbb8b6abe34f0ea66038f385c2451ea912db50134c77dd904a41ab0c54c1173434742c690ed384ad073ddc05144870c08ce31fdfb5b86ac5fee2f9e49a276613da33d682a862fa272236ec96c36015446e356d7adf5fdc197d757b7d7258eac95d90481973785b6471ac802f726e17dc824a5860dfd60f98f253aa6bc8376c71bbf88e14fcff5f1b0f473a03229308410fec36fd004946a8b6808219e95c75fc36e9dea6cc242d4d2f076e3c17bb0e7602a8012afe9450778acec6ecbe39d865b8e73ae85f9f3c448e0d7d5ca2bba4fadaed67cba29b985e22a2442daad690380f751dadc931ac9285a6054fd05e01a8ed801b0db90749644673383fb808a8adbdda51000bb08c522fc5768e8566b4280b0408c36d9a6d12ef4bc0a31095babf53c64793a3b794eac375708c3769ca46786bcc2d90d8b9e2f8dfe18453cd24f1a495fab30be5130fa2e4a5b1d22256f43176c4a58028ff12eaeec070eb35abc8cbb5beea7a0d3e390864b13ccf18d74debecb0f12e4db6b1b2608da8bcf3c4a6c85eef8db244b691d5732cf20f0d8aab1f5b8aedbf4777b4f4801a491f40ac7f5735ad60c42689138cb2d648ca60fd67548933c869d5f640aa29fc533c928911e99d375fd8a7ebe28309376500c48970cd28c020c7f3a338fe889e1766b276c6d175e090263edb794ac371a1a22b9adda656a7d3e3712cf20dd1413999b0ac37f0ffd3227c40d136a78c74592ad3d91e89becd842ab08a3d0b16d0ea230c5994ac0bc829477fc6e8891778006c50533b83a68542d30c36aa914408f9190ee72b73379b1ee6cdb9d8db26cb50233137ab3cbaacd0736f5ac3995804fe4949bc0dad0e1108f96f39c07e1b63081908ebbf9576235bfddb489460bfb94217d09d176947e8d50dbf4dca0fff975254a3b275b297b6930b2d04d9115e6865208cf1097a0cd1c41df0f8ab22434398a976fe8872c37d4171202fdd969cd66959ff487001c41dc62dbabef24b40a14e733f824bd4bc401cf0961aee662ce2c08e7e71dde9da5e7fcb0464ecd7c7b652403d712bb9200dc6c4b9dc80308b91101febeeb9d0ee049af0670d0205307a09d386e1016ff3e0247e563addfe562bf8f1b589a605cc713459c887111c1a6bded18b0a33eb3db58dec051796312261028fd66af69e9dfb822439c9a0274162d61a1a4c72cf0b2208404114838055e53ff0fdb86856d3c6e4a28365056c9d28b19ae8fcdbad74e631bfe053aad406170f9692583f09452b3f298dc7cf2158b4429cedcf38619e620dd60723a056213103904f39505fe4b956ac0e7e4b4ce3735d2db55e2400d27d5201fd2bc24cf45d2bd8878a0838383450e6d2826565c180a2cfc76cce131ca61a05bbb11fbdcb8bbd67e40d668ad90fd0a11ac827a5d07cfa0555d2b59b8f2f2b4f90e833ff119da56c87108581c2d9b5a7a03c70c39e1005f18f72845be647e7cb63c6bdd8d58bced3ed7df99f355492a32ebe824ffe90b33e8bf8eea189c64038f7870ef9bd1aa9fee79a43dfb806d11e55e24610dd3e0a635a2d039fc55e449d1b62f22c1662f619891f0ab0ea2ff76653c17985fd1b25881be516a66449f1832ee84562b63bf104f8d327b1e56bab6d6e14ee347a64b5bdcb23cf1695d7c85ea181e875c570ab971cdf2c80a15fb49758caf03da703063f19abdb6ce6e4356fd1c131efc9b2cf1d3dedf968608391ced7b96ee6d071c6a8e38ae76cafc922c3dd99b7eb5c25da32c8892435d119e8533fc3bf65ebd02015b7013b82996ec98d18793a6c43625f0ff004762ab4df9c04e83b4768ba6df27a8b407f5149907819b5344f0e4a76c433ef88880293866ff353ff4f4b5f35789694ea52008808e815026fa225f0cd3636f65846fed129935f410b495045dedc4e2a706cdc292203e0e4188a27690bea5f86f22b7b5a857cee935cafb9dd50ab51b32c6ab96a104271a12afacbd795976718602098b257abaed5d77d1eef63650f2db3e2f815b83a359f31a74486571335467c4640d5257d731515bb0ded783e589fb8559e3d7f17f455115860d679930fd5984411c39c2df51e320acfb8c2b8249f0f80501558bc9d97e4edc268de6f98b60905f169ead6517061521999f68d1e38c251ebca319e222f8e48f084eacc6ce695f847925a83d2db6b75ac221d6e092e7338a703e66f5ec3a3a4930e1ec40a7805b167b1256af10622fcf44b11dd191f4f145ec0ac5fec7c99845beed924138ae664b56fe112ca3ff0a2212a043651ad8b579f32710de1f11856dfc12c6be0f817fc81a0e9e22ebc75806103faf3bc26ab1854291ba3ac0649a8aadb04452e18456af98617a3fd3538f315e060fd875bef2fd0f58f8c2bc0d134e14f71637cf880c955edad8e8b611258b9d0895cf2304e46380b28b1c598043e82dd57e44ad06bc617f78ad02b205c9a0d292e0ccb903a834989365267713e54690647ea678a0e30f8545f43420a700b965204ef96330855644eee547351a9153ddae1175c5c3a49615261002a89a3c7d28061d6870254bbfcf8c4d3f232c3b458bbdf452b4ee34f22b3848515cbb8621ae96977019660a2a566dd107b2e69d8f88b8b63a3a3db4c384833cb5dec81772ed77ad64ba5d2f574c2cd0f344976ed58deba32a0351fd15b151547da3ee939f239e01c7243e77da80e84b77ec27a9ade9e837aece030d51662288da86781348da2cf9e4d91fb1cf94d77fa9d82927fd506624718b8cb4168d64f1be9d785abca0fae1fe967e8bece4a59e416fe8fcb06e2a6bf560d9025878d4ec80b8a1a000f3460ca67d4d0f389276b674c70b4222c1c22c5bfbde3e51faf73e1d4e1457d17d911bd02e2e9d96571a739353a1a39d22247711eb86f688155a2726035b1db07adfebdec94af9755e8a48273d18c61d81e6f03afe27cf96adcada8b8e2422475af05eacefd52e17a3dda8c655a41d9b8300f1e6f94d59a743ec7ba5245af0517981f8d9e6f8baf8b86a0c82fbff35b84fc993861b390bdae49f3c4c73768f4106be0437f20d00e5b95a3cf611821aafbafa8c73e48c18a7012c4d20f6cbe87e71f5c6fab6e44556e79f75b643915068073f7a1d94604f7226ad60633ed37bd4ab16e4415a7b80d7903b448f9031760807a180d55a7f1854579b5272b11957aaf1612d02750c1b0ed4a59f6a0260da01bdc294e39416814845ccda753801cba9e02968325471d550cc2b2ef8c1fa0fc8700126355ab49b98e12c2b160ad383e59a00692599dc3a35e8b9b4467ad4f78591a0b8fae470077f55f1cc14fa24a682c61762b12d9f60de3c89110c2408f3a83c4a2a9c87d0f3f111a71d5a3c35fcd49da62b08414d6d34a28878cb2b1a625dae9d88dbb12b6385b1ce48e25584b561963cc230d98d1c2b6e31505279a1f0bf4dd9f44a5b50108abce5b4903110cb5afcd61c89c91f2ac4be423f7de443819a9b38cef2b501ad2a42123a943856ca3e66f327b4030242bd9639c4901443a99f54c2c08d768bd4bf755ebeca478b2aeae56c936ac081d81c97b3081a4a48d49978870f31f7f774a63f0102348cd06621a8465bc756d11f7edafaee277d33e7f2c35ac543f025e1db35138430a298f796cb30bf208294ef18e18ed8286233e477bd1e21438a8088e5b88fc664bdfefad96158f5f8db305babc207f361aa787711edcd0040de5d2b60d38652981e2f1346fcb1af0412113ce3b1340489010c7f373f1b4218e146858c017ebd9305f3524de2fcaeccdaa814c7dc88b7202fcdf6aa417745bc1ed88fe3e2bbbffdfeae08fc3196641dbc86d76238e96d608cc0444359d7de34765505d899cc0d538bf10ebcdeb8d945abe5a1ce710b676394190b80a76b7f6aa56ba74f2a7c86a6157ce8890a5dfbe0a62ae28ae7347f0bd44d3b67461b7d85a6739215ee65c9f304f1d64d34baed53a40b135f1080c599e30fd504f462631e69f743e766f7a8514ded78feabb6ca781789b2eb83ebe056286d212175c9e08c0a5f1991b14825c501f3e7b8b59e242a11768ce43902711722b383c7e61e150d4eb1dc3d7b99dd14b1565b2f98f16e59e3f6d3506d1ae3c1e4f6b310da49462744bf307a6b1d361ab45835316aa96ba83fe8e2e5d5e2d0b836b396ea62de9b5f83e1f76cc1fb7adc9c30983ee0fed148a50489cbd1435bb0d69d2d8ee98754690fdec4ad02506ebc30f255bb96c281e1dad74ed2a0ac3a477f597d961b2c7bd0627a42fc11d00d04e5b59c950ca94d00b64bc0945fb3dc5e58241b4c3c13b846b61424c6c86c751c55749fbc48a2545b6375a0b09f5ad8927359de381f0c4b1294a50d8cc73ba487915775640afe2c3cc45a7f060cb1ae44b2931ac78a2e5539b3ba79184d775a5fae5df3d433e32c312df49171bb3c7ff1206a515562446b7a5a0b399d22247ef327a5487927fccb78aee4d40fa26f7c0757bccb233d8e2af61d588da5a51f5a00f32ca59af193844e11787f86d1fc968412c1af469bc8cccbfd775580dd646523d2fb63ba43e608908e372216bac21f61bf3a359da80ccc9fb3689d5fb058f793d2bbde05e6f1339677b48a606868818db2ed04fea2c00fc730f03586e116eba4642c404df12f6a09879ecd5d620c7af27ddd8e5b72a3790c48239ab4155e147d0c26085b1068686cf17b89e8f62156670e25f1d1b0234a2c8c2dd532606c3f2b8940066deadf66f56a922b8c9cf60da212194af4316cd4536a0de98198bfc9388cc1b7c5cbdeda337e1ba003451175a7b390a11d9e4040894ad34eadf3117b6b3861d7ce0de1be0a2404a40f7de0cad44410e0f892868a392ec0090cfa482ffe87de1dba5402550fa24fb045ed2efbc9461e05b52ae48b35f65185464cf6b462e063f4f2c6063dffeb4848b50b52a0a14105f89f0aad3dac2c35466ca7af93e8723ffdf0271dfc73506cdfdd8320a28137221b7fcd608d32ac486c2909c1ac125b05ae3f11281019adde1c1329cb3df59849f502c88f1bf3b9dbe218cad2299ee2b5cdb14715068dd52809b001e98740673798fcbb4569e6b557a9f6daf4cd90b4b55f6e151cc2fd4b9646e97136b108fde318263696076ee49074466211ef7c49c13ae075e9d83845ead3cc005304d81fb57cdfe489f2594235f94478622c8bf8383ff6074a8daa64024811500fc192f8c1a925a5db63022e2821d52dc8c53e41c5134433693b7f67e0429f6d0a93422a684846bfe3e797c3dab31cbd0e6dbe76f6788711d335c3bdc070c8ad0cb4ab20c22102d82989e9baed187e4e21df4b23ad043d91383869311668ebba68721586a097fa504823f94abf8bee2c60787b4e58ed29486b2290f005ea5b32ab0df41b648a4b8a214457f8d006b68db587d26a474d55acfe7bc8bb9a795e2c101aa468001dccf1271148b7818fd7728108d3e94ab8d42ce7ea4e38e1e8429e985c08a5b5e2112f514f35ba2f6efcbc52b672a1a6a1d8bb31a3353bbd89267469059d30207dde74a498105c20be18992bc0304053b1c45aecb1e82d652fd88a57e6d875f16ef84f3e627ec84d1179fa9cf46e212ecb9728c4938b398fa82b1517b33f1940bb31e13ee69adc6d9b1ac1c4de8c40caeb788364dc5bdef3805cb6e2ceef695627b21b443c45613a8bb2f2ed5a0f2efbfc2aa0d428179a308318c8c7e5daabbcc30e7849bfcc11c8db56ea1583f0de3d322cba806c9249c5542b2c9877ede25c23475ab5727d3543e7cb7a2037b9356ac95ce4e7ba518385a5bb1a752e9abc41d73b3f36286c2f0599d3e9f23895ad4c5c4b442811bb430c470e634d0751a5c5eb0b4eba30f482f35b67acb748c7a7df179c043f661a2a1531e30fa6a7ce50f9c2162097b0b3d168abbec56b36d2889ab4a8af033097c9aa15182f3f3baf6508bb669e14dc0fc26055f4699522b318cb6fed27025105446dd3c9e62d3d796934d0fca083ef3e54123992cd5be88cb6aa4acb41ef6c20e394e220d368258ebcac7cde12b2a3231441db814d82110e9a078cd9c3afa8e7cb06bbecff84ddaba5e21d0a20f66938dbe31edf9d6f0b28b8bb04167eca3b2ffe703a230e8ddbd4bcbe576903cb061a907e2d01feb3220be336bf9640965e64a2d71ab29ed35b0bbd0db739a0c3a686f44b2d3c4538836f9bd32c4921b8867036bf9e0df731b46458c0b24100ab9daba55026c39d8ea78eab8951f7248d77b119191f91efa5d7b698de7e209089d1b452f538762a8b6936a7da646460e43d49d78424a3848790bb9c2b9bf641b1dfcdd9ab695301333bec634e610165af8860cff21e80f301cc0053cb26351fb4160aca6e99fc9c1717d635396a8052fa97029219c6570642c519ca55d40883f1a49b1b94e42d44da967bd7983474dae43a0d9c106ab6957f7bd7e98f919cd87e29f46b4394ae40724246f783dde38bf5b286fdaeca31981fc047567e7f6e353833e548ba1ee51ecb2cc6f575737568f203bbd412c8642d36a14c10347b3843afe9079b67adf8bfbd888633c531ce524aa8c423e4c106a287a733c2dc49892493e29a394ca38c289136c34fc730d6400d66e56f5befb615a3cf83910b1a35f704e7aa114a843f0bcd23fad81276504b8f31298046e11e91332dca33890239c82d924439f258e00e3f4e621b6ea4a645fa1c451a0383913cf7b9f070861a92b4b935b6a513803dd7e65ccb44ae88611f7af66ec68ce1afc575f9241afb39943550afd4e8cb67ca4e6c5cecb86de427b290579a207cc7d8252b27062dffab384ba338d4cd6fc2573f02d4a0421a51ed218b19f2f0fe3d81eb819f81b4c6b735a31edbc59ebe5eba4b081cd151ea943052f72e036b76b07a70455ea383f291b6904f1f9eab6c793b645c73635108abf378a9571fa6a505de763ec58f9195c4264ba895c09c3be0bee4644b65b029d48f3c48c16859f04251fd2bc24d5d83fede56fb05d8ea311abfb620e56b13fba59fef5f22c98189d3a94cbed22b0bf73396d9ed71a986d07bb75e56f6eb4955ac218b1243c205ddc5b6085da66fec5fbc41953b27b7b151e7317c3e6f9fa9006f0f2f89cb53092486577244abfa99065e705a39e4ae5e2f9182626c9c67fb0c3c17870e6a3a9514a789e90dfdf977865d22ce65218a910214de41a7894766e9d41b80aed92573c0c12e1da0fd1592a78bcf60f9f4fb0c5fa1cdfbc792f78f9714ce6cdd692c76e05684158b827c2fa26f03a9dcace322b61a76180d8e91063a8807731bf46b5a4f05db19373b88d85e5ca06893277c57bf2c1d781aadafe2256b599f2103d19a9ebc7c75c42aba47e25e31883f138f7e5b214a3bd32ac84c41ea1c60dd43cf2aa7402729e7f6cd68bbd47e26426a839c8aac6faef6552267d0d7894bf5e95618a6d7bdffe5c5ede31515e1b4ed325f5f1550f8886138ca405ef357bdc79bc0115462404d615762d78913c8f9292bc007c9f9abde4ff78a6cb6f0d2640ad4407602afd3825f50af05fa65db34e8c8ebe1460f776ec01e512e4b64f62b81b3367c1572b7489416368859071baafbce97d325940304d6f58029294e354f9e03f4d03a42e4cd0e30b88c61d9399fa25f076f9bf896f4733a986d1d9a63d17e26705f1a681a36bf5b9452383fa018bc48d01a3f9d8344204c989ad191c1b6ab65993d57ffca0e9e931f34883821825e8378a74e824e48ac94607602f5f2659d35d41682c275c861afa189b898101bec6e1470a82a16ace3c95a03e878d8b76c6dad9578668fea2d0952778403f082a683c5df519c0a2580eb9dde228e0284cf84c2936d3777395d49a13dc50d38bacacdf6cf2003d309196c92c17983a179777451abc874331d342e02c115ea9147a565968cd52749918105793517e9ca35af49a18626843b6f1a63fb087e3b4133f55cf2ff79ad6b113f40e04420aa1cdc447c6b3700b6041acbc2a17fa81712445a07f070db66f4edb4408a8e499fa7cd821908bab367cdbe5f235e3023c588d872f3f415ffe0c461d2faec735a339303c63e74f57088cb89775dee600eaf9a53e6c9608dc9a8467d8bb5b4eebd69c2ed440927c45337184dbc906963f627a13f13185898faf4443dd558ba30fdcdc4a6144333206c945d7db4d5d1460596c31b2c00e435d6326e95e239394fd8e9bf411cdadcf6f9899195b0898ae37cc25720ad74042788885fd66da309c6b13a0d73bc0678f0afc6b0a71fd7414e880da125f5158d23fb53567db4a42141a121e7b2326c50d3ab3ba34bcd2032ebd41237c056575d26c85c426a8ffb575054ea3173f1c6833fe8582a10a269bbaca3d082ca1a52e5553c871f0f82522deb356c416e88538dad8e1b4b912c176413573507db238d1a725533db557c2285d9304e2b7d19ff4c4d24fdb48cbd9b6f19d9194ce5c3424431c0fdd583ab25305ae6cf6b25811edf0f01f7224df67873b293930dade3e3672055224dd0f9ed455cfdaf041268c8f49a7beb51de0aff27365b215a241d71f41174257eeaafa0a68beab5a9ddab171a9db0721ac17aaa90242b6943c394feda96730f8f85312d12c27a7a88e8049486f162b3ce5b02111db0fe360ca416d27796f25d9d2e78ed1e5ebbc0a20b4083120865fe96daf9125256bcc530a291541fe87c72eab57e15c9643d2a61e4db5cebaca8f52f9a85a0c78a40ecafd1526a927f18039af82075cf1bc0bba98f7a00402c1b803f4c574f6d40db76cee91506d0da21402298b2bef6187aee3b85f7e153386580b82914a4b3c69cecdbf1a108c70174d933c9db0f54bdf8f546b514fafbf0512de6d20d780a002bda80a5726055bf8a28603219d862fd92910297e4ed0227a3437e42ae3f78e8e6e22345dcd87ea404d0e29f6818bc82dd1da4109e030465210fca758bf462a474af16fab51128c5cdd66808b9e038eed7cc7ef9956a00bb082b1c4685bd314aa0ba2492cbd7d10bea7099d18d1d6a377bbf1ea4db13656f600611c12a84cbbed41d571aeb9934036ed40536fcf813125b2ebd953e25bbd2a45cb7a24ee5f6622e8ab37465619bd178175df9b7c95ac64367378c3cd0140069f1a771f53c1aee04e1f055038c566b91b95c7ca22655c860f06904f68b7e18261c129e2dc1066f3675361e66e7d2ab46b72a4c2a377378da04199aff21e80ab70b35db6770582ba46656d2137222a01a683821b72e589c8aa7f49217a74c628dd2017a48bdf83b2f3462487b2606f2f47b1eadd7abaa23616f5adebf77a39d3d323d14938ee88ceaeae23e1b4c9b3e3ce1f472632f5b5f39d76c946a67980a135458b3ccb1c9509dad37b522981d514b41b4c131642b50067fcc118d524317d9e9bb442ce396a86e5b428b6a40b0e6e9e857065be7c103a77ddd2532889c1354383e1f9c160afe5590bf756109964587b7c9f3d92666eb9783551511848096b8d8fd30f17c9642c58cee5c0d6de55c522906e7c45d0939454bb44fbb47348db92fb76d51b2e1d7b32abbbea0d179d49ee3913f8165dd8456d2ba61a5000ca6596b3b9f3723523e0a7d82bfd26912cdcd8870dafa7bdf5b480290a8a735d157012a965415686d529745418d21602241402b778291d72f7fc183896d77ee248d3fb8b61648c9c9a8e19492e106ae93b0cfc4306970a53bdf9059f151b3001eea9ef41e1eb894adaaf54a13bc734fa0e6f6112685be9f080717db0122d99faa1fe97e05fc1c57e6cc110280d9ab0dbd8ca86243f9fbcd6dc56ad9e77625902dcbfd7b5a172cda669cae253cbb95c3c97c18ec3584f70c4491f6d34a2c55bca6d25d7c99801f94e7661c0a03320031466d9e9adba4e1981b7261dd844d109016712db57388c1d35d1b999928634f84d5ca611cac5e53d2e2039b2d2c38a0d3ca438a9502964a21ed1ea5ed8c044b3e022a4aa02858f29d365559f77471675ef0755b25c5bf04c2f9f3b1f39d920e1b498a3f92020833c2755565baf15e96b2b266a118d40b39653c63033d31ba1db30d33b5f2e9237b2156a46bbb2c74619f06619a53ea93bd56ff1fe58669fe7511c7a990ecc1a5ed2bc9e6d45f87e5196d40abc44772e37ff7f101988e8a9ebb9ad25949d694f124c02153cc6970290d864c9fc72aeff8e522298c4cd489ce25e2789c1921c1422806bb805c617c8981f9af5fe0e4e9b95e6e60fa69beb19c5fca08a0bea187eed957d3893e77b59a91fc97d7290f6c23b4e17520f9430e3a898c7259cf76caa23d06ccf16d36e0c7900dcf3f1c0c6916d425e20f7bba9b45a1d09dd87e58bb85a621ba8d74406a767ab59f97334fb7bcfe113e966f57ea87b90f6ee7bf4ab3660f2436c764f87e32bd6a4a5f978fc6e9ca350260a4c1d4d1dcbcc7c059ff8de670c2a5faf08f5cea1d9ad4f931ad45fceaab8ca38719bf8ae152e302a7e03c453bb3338d02c226c1776fbe8e696def33cc9a8c9000c6b680a5d20055aeb5f01948480f2206be62f668247ddf0c7424ca4916f324aeea057e551b0629edc70cbb75059d714fa3f5d707c4602e27bae0361062da7c6e503e62ff3c30b0d6ea39534d6c23d2f89dc49d6488392305f499865b2e861f20775641888c38bac092a1338f422c4e159c1e1473ff24bd214cd639a5fbf5823bbf3537745755101814a4cde63c0db6511d4390fce353cfa12700aeef529f39da47d3d78b77d7ffc08871e5061d166580596586a33022568e0a95cf7736f611b39b315357615d4a3f6f86211409cb0cc64ffb35760123cffa28be2fd724596afd020fd0606d159ef9d5720fefe5273afe00cc8b7ed5865a5d488980d67ef2092d5bf331f2fe24071b68d51254e57c1881dbef8b4bcddfb4553081196965fffc1b05b39562a191b41e6ad1551f9afb575c21efbe8616e516d9092005a6084a09459996d80416e9ceed1877dec58fb910045aa73407dda5e58104366d3712aa903d766c352b84e78676f30cbfd99547d47ca08c2c8728e5f2ae3df8a8312186375af03aa99dba2af73c21f87ad675dd8798a7c997a65a3ae958d01b217cc4b98bb08a3abbcb77237092036d9bc43092367d4d0aaa905cc39e28e11a72a5045123d211ca54d98e669240dff4883fa2de45daf38c752977fa811c1b55609c8309b3550a367079c1f6fc8ed12bbe2dc4c9553470a59d54d323420762f84022764669e2bc16e67d0bfbf723c270ade8cbae829256ae252aae31387d096eeb18989067ff40ecf6d9be0342960df95780ef81a84951b907fcdbd61bb4d88492c590411462e5e1639a5fa251c03212ab94e4778ebcd48ad5df206b2fb4fc4033833b9034bd6ead0673578d551691020256fac43fb95aa6ae497134ca08e7d3518057f3261387cbddbc1bbffe402c25564a7f2af46154124ac783318364a6f22f293e3a82528bf3c284babc3d064f142758ea9d291de32f62ce0008ade2de0a150a61c86b011430a144588b7ddee8ac29f9fc767a89615593c7cef9a3e02db71efb96210ffc816057bdfab5fd9edfed807ffb3cd84a99ed564ecd5acbf159a7662b9e83f401c9489b65f88d28c97b3be308c3119a80eb23877cf281eb11a6be5e765cefa15ac4bcc8dacf47d8e483877609393d19f95c9a51932adea06a1ae4cb3c0ca4867dcd8dc487acf0cc0296852ec641da9d7c90adc81ef02981c82e33da89718949f34877cbe0787bd80b8a88a08b0d6c8e100a911a79aabc524e257890db7cb927f4305ab2683ec9d378019880afa570ae15f6a52e8e170567c9a91092fbc294a7bf71f5278c5f99b46b78713fcf3b9810f421acdb2b014059e9cc5627e96660bb8ceb2dd172f412e4f6c614bbded915c71ce29d7d6eea3da7cd166caf4ce413a5f66002edf6b1d496bde3823c26117940b9bf5f4949d6ec4df19310a57bc43a6ca03b020a28f1bebdeb251d164edd02169a57247af1122ca37431883469aa901e50aee0b69745fa977c45a9477c1f45acba96be8bf42a68e4a48ac6ea275dc87d5647e2e4bb616ba6f4586aa3a8fb3deaa7b8f082cc90e104a1611654166541f14826d253082135ead870a659606b596b8adf85934b0b079d1e7a348209359f8d168b15d0ef918bb3dafe822f91a9131008bf9bc199f3a422af1388d5291f23219001a3f3d5c5e109fc8d4ad62a06c63db3ec3d1f1c59bd61ca52b8e4f8c4840570a2e9e26665b3d8541ce32b34a5b12d9c425857fa1bec75d78721537a4ef452ddc253b79c087a28c5bf338978dc89be8f76cd64a252ef0e7d46944b1d89efa1889ca5c81d67d660f50610fbe71ad304d29f313fdfd898071a4e0562cc3b3a65c72a4341a59bfd92fe192d481032e53482eff4d94565562c8f75638a1ba60594a8156699ae300377c18afbfb4f302ea6b3963704a0410b565f7c971d249255883432743e5001523f663eed6660591eaf966200fc61e1b87911d4c7522d50ec5ec51c15578cf50f497b113e1da18444d50dc7e723f0363c352ed6ca3406f959e0f064827e72f9c80f923804a3555826927e11609a0eb522669404843073b098ae0603a2041c38a2f4214289217cef2c63ae05beaa00c4b77f7620750c01f1003331775706336f448ad32d03974ea0ed155c22e2c0b50284ac48930d58be043c9628b8faf8661d782ea9437cf7dc50820019faa5d8c33e845ac65b064d007cdf0aef93ccf25126d3827b7c60497cbfaccd4530e981976044008879c184324b2281f396bea0b15df5aef0a80ab4a74398e3110515c98a8de0caba4a43eb33607c9a1bd0776134292c039741b8e986de6cb8a66aac1ae7bb9472fd97d61c6d8b84070b1ff7096d4fdc52e66d17ea857ec064a1b77821eaba5c7cf25dfe8dd9188c7a70f327a4386953898c50515c5e07b0c8d6e20e0c40e749b414ca0dc4d78f302122a0d60d41c0d7fca4c5f851b06e55b183f78e629b3fd93c1677a707fec77251ee896bc52d6e13432c95e54ac7eedab5a6d4f4bf009343e6a283a80128dc03b60d32c47e69ffbe15c3cf1539a46079ebd954b9b07ca34564f3051b25957b3fd9c146a43835ec85a59bdbece417978db00e2d86ecd767eb9db491a3f74740fa159f871790ca84094bfe32f747885cd831b693d2e63e75e963254a313fd01e86d5d29436397bf7d69a7825a8084a5d17d7fa6f130b642e0e61f86b346e15fcbdc868f4409d046959b16662eb78ad806784ba3bd0f467acc909bef00a7acd467eadebe651ced6543c8d98538731381fd0a23cf0ee97170830e471309294933439d001954d21c72ce443210703b0805e073ab3bba107a02a78b87e77fc3c2384222163c35a4c46582b2448b3c808c48c862f1a2b4ccec42968704a2a951d320da2c846537b0fa1b5d854da779109d7dff4dc555ae775322a750112438bc976a3628945153369e17c1264478f227c0020a773c6437c7999e6764911aa5d8527567ff4cbdf0143e23842f5ad3c06050cd6c4de29450a9f2e0c9d248992872fcc9ed18dff47fc32527d0b6fe889e000d54e71ce5cee2188dbf37fc98f2e3d34873d09982e4011564bac12adb29f0e5790b47d2812800808aeb89821365525dcbf8bab4ff7737a89f634a93bc80acda41e358e611a024f02399520cdcd11d4486c0908e134c2f3c7c18f93a5bda9d6c5e54118598f93131d9bf4631b0cc424b11f043741ed6204a8c51808115b9e4b4dee2d50a31608f1414c3fc4de83a5c7f1687cea2eba3b92782603bfa73a54bc78fdcc752537c60b0f5f1fbfb4e05140588636e9918dad3f48d75ce931a02f39d4fe0b39a7d0eaa3e224ef825cb7d94d25380e1fb87fc36c760f3f6e6b62e2d52d4d1682f8e6684f49df629902262fd646ba2a011e70d14f3ac26fd3767c10709ee101153500b44e422a69cf21b5381c6f4b15f724317092d9fd7af23e6e97258ee7220020fde3f01c465d26bd888663966848540372608437fcb966a8d309a7d75858d131e72f173b6b85f1a27c80a88328a1577b68d201f1a77bd0dc113b061d544978f06dfb4fe840c2d8981148d625c85dd069744b55b65908bc32a4211568bbb20af3525d106d0ac97a0ec3f7f27349087e89dc5ed7d33f3bbfb5711904b085b6cb3e3346e0c901a641874a6de568564285939a19e317c7b4aef66b744d7ab0163911b1e911d7bd2f5ccefd9fb12a298240ef6a837f32714e3709c6e08ca9216c981c9b1a582984dcf0059310c09c55cda92ef77a329a8e86fcf0f744767a6c4616726bab748c5a6fc32f1a05e45a1d84bcd20c2f0275849aa97ad4d3f0451d0ce73a04a45016f760d36643afb9eba6d021ecb6e53b912cbef11d1f590bac099b3e98e8fecf332bc009c7ce15185796d3d2f8acdcbff84a3b11cc89eec9296df8fed6a231a8f41651a673f14540c1190baf6382354de66ea56ab686d7361281573ff327442f7a085429844b800364a662ebf996a6a3b21769b9cfcbaf41806d7cadc138b57b44a2abdc60a731d9c54a6c8323fcf279c2fa13c5c8e8b97896cf3c54be90ed130ec753757511ae6917b772dafe94f521461d63cedba8242e1105f28a261632a30db863d7e29e7835f3b71168cc8405ae199b538d9e3a5d89d16fcb1a87dada4f29fbb52b717efa0930a636924b530aa4958d20941a66ef0564d55ac8f667807bee96e083e2d6bdeff0d73807fb2e08a6df6154fdc05ebf09dd2603a9a387f5d6f0d146676f5274409f10e064f8d88f54a6cfffe2e69b994d5d69757d57c921983d16a7d0806cbef21ebad0b1f139466ad8b0c0cb8a54e53c3080c616b026149cf39c3acc29e759d0fd6b54dd428ccd182614944a9b71adcdfbd3e8a95944f79b1337050feed6ce72dcd4fc6b36bac51a05083d62406d0d4e83041632a9e84d3d0ac4ff393059f7224bf0ad5a4cfcffba0ae9660ae3f350354522aa3394da4ce1383faa6723b8063c93262a593a590d76d00f38c149e69925b9ce9ab15c3b5e88f94a3980050ba82eea1d90ff8530787e72564125ad5d6c9a2347d4d1b1bf6fb712fb96bcf2c20116c762b69449118546829be48b8d980e1e04712d4c90136832f2fc03bb9ace910446bde292d37e0ba2e228b80ab527406c460004db2e108a23374d737147b1cbc22532b8cac84b1bfa9a55ad8cf29fb11e04b18f9834763279aacc000b88650c893678a5d1f4ffd844f7ae79e04a33a1db7b89e7a4cded60883703d4c430ba44c7990f59b5e0e1537afb6caadc2d649b18b19c4b236314192e3ab8c5c62f007341ce35a86d809756859fa14afa6184296f65a4c262b14b9bfef9795860986eee85d52d25ce6ad99cd48be425b91d3b6a18236b5a6b327532a26d2c2d8387a338169fb882cf145b7e8434bcb7d829f22f5e954caa9e86a1d7bf1240feced94f80e00eef078feb402f008ff05ccb20eb1a74bd61089d92b519064ff5c2176cf5be544a2bfa705ba62bafda1c7b1551cb4460bf0cc233e2570b2068cad2dbcf92b112521ee8eec2d9703da3b49fc33fab217ffbe97573271b867b7e6649fcfeab4d359ac0e8b3d37b2eb1bdf4fc38e5a8e7734bc6e282b34fd6d292528a9d92740468988f9a2be8e47a838d538a0cb08b914e8dacb50c9e996e91cd9e63aa1a8df0533a60289ac23c577340e6bb490676a1f1c23fc8ca6f918049f05425f7d0d240f1562f8e05ab891bff85ce6fe67b7f265e1903d2e44d3c9a030cf1b61d62ac6cb6ea29283319b65cd3d1ed99ee9b00fa0bcef7f83076494857a1aabb73d8c8dab2bc97452e2a10a1d158fec1bd1babf8a836dbc0924759b67a1168ddea7ad06b73e05390e8c92b79703e6def143f28365b62e3c1e0416281da2b8f3c9b3adcc2398b0a7a4a75d9617a8fa5a27a5e9123056b1adf2fee36342c98329970c01b0e9271b4012653d4355ee0552f0809c288fc352b8b71bb2b2adfb224fe3c3fee3f6c9c236781fa8f452c1a3da1aa50fd8072aec2af1fc479ead2505224c0e7cdb00cf9712e282d964a6202fb6732c77bc425620b1a36a5f60bdcab24b99a5a453c5882783a1fd8aeaab85865842dd03ee87b3b4b7713b105ea4618ac3563234e2c2d2a249f7bc63e75ab93663b324ebca3775ce4bd3c11e40754d45fa24be8fecf73240d0208a446324c7f5e86c2a00a735a9c8e21dacfa4acc9673f33bdab6cd97cae66492989a4cd1f0a2431e58098515a1068a1bb703539672337e9b7b25ec0747737630c6e2b259967efd4b77d44d2a92621a25865b3900657cab83bd399557bbe6711c0fb0acadeb930f7a42feefb9e21efded7a2329fb1a00fb0288e463824882efc21cbcf93f671aa687e990f23191ce25f9a4d0d5f7eee8292bcd4fc031a1b9cbb0774cd2b8a792bb8d81fb55485873b875183e5f91ebfe9f1de1d8da605fa615e3b372b6d5dda3e70451c252c39d4605fed1b0f3bcd993005a2c398c6d0efb2ffb421cc4424563dfb530c7aef1292627ee24226c6bdfd1ace879a94f379bce15e2bc50071bda35c0beed005b269478aa892aa8f605c5556668fa685f7212ba0523881d261005b245b6301cbb132a45b636bc5c8d50876738073908aef2e92dbe881853e0b9785630679571d900bce84b8fb6459efe8c6d6227a2f5be9b8a0e274e6a57b143f3e52a8a3a1b36916575a00c0a35e238e3ff4bedd51fc1a80c7fb01e11d04d4898ca07fa9d641695cbfc0b7bb94595b05fe9fec182ab259ed771f4027a3a892bef38142c92eff302e5b4a3ac9c1a95d339ccc253766ebb29b6595d32d1190e23cebc5338cf91b4f4dd9c92bcc677d392690519712713cac72f3df2161f4e22e4c153ec8dee26b4918bf30b507460a93cc3d6cad0c40eadf6808ba37e80d93e31904f568edde2130984a123d19d58b59f02915942a54364e8540560fa6d9aa95025d3adcad447d05a09b665f02d8c2e319ed1f66f8b2e2b11392574e8d83405cff9a54b74fd2da9ed6e0497a530078d8d7832c59e37c813f6f4da8da2cabe89ed55647de9f912af9cad9100f4f1321cb856191e57051196aa9b54c0061c834be101a4d700ec16528155c16388913860ded7c62500b6098be0589ae1ceae8de53123dae708cbd9c8a88e96472e009737a1ec6464fcc28fe1983d8abc58f5fdd9353065bdc79ff750beee71b2cd45fdd3251b223a95403726ce4635c5fd20764ebee9e2b7e725afa457513bddae2d8468a907ebe1cddf204c831422bcb07c36d046cc0512e1861adfe67af02477513763bc8bb4de4f2ae3e04a201c9af37c4b8bed3e8200297795510a0f166b4bf301b8020615120a8ea242ee1da6ab60bc6373cb32da51fb3ce051013a7e20c519cf821b7d1c36ca1a0f9854d52f799b0454b5da006bf3b83c0853995887f6443a7e545b4c29495ee0ba8bb50f403ca75b35f61bfc0948c9e033afba4edbeb576322d49035e4cb60d113b40370cc87a35328e1f26f8dc18cd70cfbbe0abf6a81f1b0ec1760e683c2bdfcfa11bd40c2eca814f4b4195819a985203b41fbc98377f1a11fe498e21485ef200af052d2d8b7c40c87c8784e0799bbbdeab5de79328f8803f8d942a24e83e190ea1a0912fc950220ec17daf0d5928d2e8e2de07f2e076f778b1ff6a955f2d1602aa5cc1dfd30c3b70d0a7f74d2826c6792a6e211535bf946a4f50c684ffa188600acf10f00886014d881610f3c6b3df2c8a5a265dd2c45a7ff7482790ad62542af58fb8b339b82fdc171b28269acb4108e886842f7121ae47cc9fc472f4536506478e182201dc3e246496513c05920a3cdf6a8c3b47d794420671e592b93cc258b73e9a5d6901607523af77118d2b7e2294e2d6ab989cc80754359177bff28ea4341483e3b57779c4b80f5a041ff07ce5fcd9bb9a2ae1b624f6aa544e11c5c55f7dd8aaf1321fd9aa92e7640038545b24f0240d7257de4f2f4b45a08cb26eac54fbe52e2a0267256c1be98f6e7a6eaa3eaa5794d21e19b3c6341bab8871dcbaa5ad1b32916f152f7d5baea0c0a9feb50745a268ddd70a3c1f73e4bd7ff1362d55147c2a3926f92a4815db9c9eb1610fd357702d3e74cfa28889a6f3f38caa7962f5670a3fecaf725d0048036cc1b83b6f84aaaf44d5e9cf6ca300d393b03c517c4c08aef9924e26e7d02e9386fcef14c4ae018c91982f1944a7e5127acb6681bf4401d975fc666314642efbb7793b047d45b3dace5cbae92cf46b1e97428653ec23c06f208f1a4ef13cf8aab69e3bbd257e4e27dba671f5e128a4b47c1e876d192209c353f721ce48a55374b98a697036105457ce1e8ff732d5d74b10d1e68235647a86ad785262ecbeac5342b20025229dc4e2735552f37da064f36a573e0913993afbb98938dd4c7473cf71b0384a630c3d4ec85d486b69525095815f4797375f97a045fdefb535c683e5ad08cfe3c47a0b75792b35b6ad427f71e0106e2229a3d83aa9f22ae155bd75f84151f5e24a0c2352dab48a3d577cf0bdeda6d7cb8712e48a1d3e25b677ea3742f15953e39ab5621f869ea1896f6536c472d79b2369e70f98ceff2065cec129d1b389417b2deb536bfb04dcd088d04189d9765182e11e4354930af9a80361d397c6cf92f6d7427f2bff27f8ba94accfaa5e84384e45954d02ddec3a6504ef07a1c45f4268660d2645b258d2734d885acde6e204a8412f7d7fe25a0c3ae57bcaa747e6c8aa9225a71fecde5da8cb98ae17192df28ae7a6db30ba2bb1fa92bc051a1995e8d1c3d98c474af51a6ef010fc9ec1956400d1b6908a5c5dcd5c408d9b3e20807497ca37247f17e1cfdcb7d8e44556b32a898eb0d2782bfbb90d68e41147ba2170536329f531930daaecd543012cc48752177aaec84110dad6809c969a0ab6968e323e658b64538ff43c5af121e4f66399092acf200a25d932ff59a43dcd2da420c5ee72d4f52ec56e1904afdd7a87efe2431635a7ebd7d81ec5cb836eff8d4b22634ebf11d1438f91bee07fb1c30031d8da24c10b1f6b66d48c638d48e67af9887f7e59985698c2c1bfe48ef0281115a153d84c6e21bcff24231662374f7202c00ac6bff57307432222bcde5d86c73597deb83cdfd73bf2f3961308d2f4eb625b46d16b88ab83264a2861f3291a4929636885286d79bf9f45395462169ff6bd53e898f54509b38cf24570c6dd89601a5028cbe528a85639bf8ff0a53995cdbf18bb8fb9d9c9abf96699cdb4e7b067f82b8e34fbb7f095c98ffca8afd10c29b1b217a6d91e8417c1f5fba6725bbe3895db33948763429a69526454262779520cf05ffd78a07fa8a8997174a8d7b2fc34d6b39c6cafed1a6032546b2f1de727b0385932851a133acbf694ec0fc4ec9565958f480b698c81431924924c8075e56a52d00327c80ec2e2193cabcc2810b7bbf0b46e5bf39b1ddf653195a5aefa3ee4081a218e582349bec13c5b46ad910a7d036f0d2c608d9ac8feab9b1ce781818404bfba61f81394d4367bc8bc61157d70ec7d878e63982905418ab0692e4c5f7b3ab76fd588231ed030e1003665fce7c9c2440c421d28381098730341048d10df3dabf079eaa304eace1e3fe012408a8f29a844371f2546350d8787b435ccd2d014aca1c78bc6cbad243829972abe2acdcea7b9a88b50a90f0c9204ca3a3f9b54305e15251d2f4e10ee00105140cae798342b731c20e6cc34bb3318e4204cdc37d1219c52bc53e32e6bf45c1d6ef7bbe8e72ef9c5aedf5f175380a7e0a7b76ee2daf2633ffb1fb5d61a2184904df6de7b932de50e8b09f50879093a76447829c34abad4df71a1b6bdfbee7c3ce553cb49a485c4d6ac23a5dd58e7d27295dfa695d2c26b52db287b55a0c24b4ba11dd5d3c70a1cc147820fcf43e0a5edcdf3f8a95d5611f2d05a92c2c753e9b1d4e381b492f4159e02b9b582f156cddd0ade4a83cd3945c05f9af5a7813e699f4b2d3ed3a8c33c092df3e47aa6b33aad33061fe11a186637994da6042b5ad57ec587a7dae4c7f311ae41a789aad475d94d66b2b48d89dcf86815c12f55871612d5e5f391119c3a0fe91647e95ca93918ef7602eebeb614c00ba73b8fa8575a3ec233e28e9eb1dae384946766867c0821bcbbce92df380d65b80bb84e8b340839c2853cf2e31ae03a2dd2904492a4ddaa77fd94ff68521ef41ed2a4bc89963d1569b187b4ec65d8652d8d8692acb29b0cfcd46828c9aa8ddb0c15a9cd4458c729d650e41ee03a2dd600c59d9556dae2df774cd354a7572c4a7e529cbeea58ad187dadf49556a230abd66318f68a5ea66f6573bc5bbfe396aa53f7fcd6b7ec2c036f33e8cc269c52b1d38b321057a71bcbb4c7caf4ad761bc1cccdac4cdfead22c3be9938267503b49bb89b2963ca8b53a6b8297e9da5bdb6dd2b6acab8defecc79cc8f7d24a3974dc7877658e77a5fc7437b66f46c7d5c48dd79f0faeb1a9e05171b7fee38a7dd837c5ddfa327d9fcdf16e05dee1906dd3759795e9cb402e7cce909c1ed7c791325c2347a8b8f559d7d5707349d769a1869a4bc475b6aa26b95f5a084228d3577e93e9cb3a7c63bfbec9f46415518a76abe227c8ca742389ff1c141fd2b2a712edc4ac4cdfea17fc68eea97832bb595606deca6e3355738d845a276d899f145c637ad73bd98dff7ae2a0be67a18c7d302f2d3ce941b6bdbcbc88ee494d569a0e8fa8eec6777b52f00c2992e12c2cc2dde9687821bd6071fb431851069a961868a8b91de35e2e5b4f0a9e01df96b48e67c4ce69125b13908267f061ded4076ea82dc136431863a4674ae5bb80e82b5031c59cbe7022909997dff42d93d12a2290c9ee00dc0cd856c6ab38d3fa2e0878ed0f190c048ce816f8800b8580b77e93b9eb2e743193e670e640518afde1e6707f78404c11ea6762a445448e4a55ee0cf9012f2f0a50c0010e308001c48861125304f8aa995c3145a0947efa1543692da0ee021953044ae9d4640ca515614a51b9d6cfdc3a2b40a51501b7e4e670b2a29fa1b9d2222287b3b9d4258199cbf67240f44dec6572e9756bfdd3b6573f5dde1a44ad94b224105d19d17d175e5a674d60fa344331c4c1b932af00557eb25d4d32c249dba619c82a98a6699ace9adc80fbc31f62f8128a6086c69a8991f605326603ee0f31fc1c55f0f1509d132287fb0333f363d4f813ac9ad42298a1b9cc7f98b9932581bc3cf19df5f6304d30e7348114dce95369c9b315cfa14e878756470d2d24c12666fa4c8c8c26b1bb5bd6701241f79ecbf4cce384702217c4cb229b9ac47d4575ce654ed932f574df6ff52ad05da7e6e9f5e445b7b0186eaebb035527a33a2250b00ffec13554d00f3815fa1938157a47b121bb56e85486acef3c2fa106d9a5e71f4e08be94b570e9c656b8945a0c844b7dcc39abae4d75cb38c508e1d48c641068157a4a9b5ec80f1f4da2d7c09079c005419dceb52e4ccfd1b44960bfee62fa7152f641698f26d14fed0c593cd38be890517aec331f41cb8e20fc657514015a0d5cc7ac8ecbea704f65ce79aa59f3d59a16a5d6034d7a344ec79a34dd12a71dd1cb534a27ba750ffa48e50fe7b4b07bdcd114fb08d5c9d8200edf11f56a1e47e34c6a524da2f4f5d9da55280d1910da40b67fcb6cd3d849e99be6d1679405f254e8619a690cfd43424fb18bf89029b9747b492e3d1cf2bc30d22dd4693dbd4e4d4e29f54c5804de5c3ae9bbb9f4959a4f85fe69d953a1879ae8a9d09b6893ce3a7f69f31f8fe65e367f63de8b59866c13510ca3306ad679012fe478417331152cb954dd668a1c7ba5300c135d27041704355f313bb56da6c8a5aa3bab88eb54a5aeb345c465ad4d245ae9167a4ba3a7d194de3d7af7281de2d2b7109c10f323ea1555a94af5e9b3e7f24e2f7a2ef0f47f2ef1f44e06b5258863d5dc210f4a2ec24316830484281a31b5a007d388424c13f2a185ea64e4dc290bb91eefd655ef66f77d7242c0fb4ec47bef04702e0e36e03e06dc17c47dcfe43e00dc67d94d64a4b216c6bdf36f9959f408e77c56e0684632f89c17979f2bc3e5e7905cbf1e5f9048111ed2e35ed60ed1c2903dcb86121d7041bc1787bcf7624e6deb89ba392e989979729fd84ddccccc3c396747fdda3dd78e63cd9d5cbb5caf2ebbb3a364a2995a109a967d4b4269c85ae7e160d527cc364e9c7a629ce959b4dbeca94367c85c8c8b111dc62b08974018df45f4e36b8c81a3edb586302699a6254d4c44da857d94c18b57d32d930a215674a4792e289f40d773c13e659f4ea6e863127dbe9160684ca0d893394db2ccc2e920ede3d1324ccbd8c7ebee49e9e3b6acb6a69ad8497ac7b6ce4e770f19dff10238d954dd69c954d9b7d397b6a72aa38c30d65c33b3331dc08b9664d32184104238279c304208219c7114a58c324231c421977a9d46a0915330890b823adc91a7e4cea55eaca9e91ecf051a792ad4df42ec64390423b46f48b432a091e7f26c0bb13513031d136d87ce90bd9f5f0cf509c873a999a7ac8881c5a50e874cf7e1034845f5782a14f55aeb057074700c768e611733a9ed15b9d4331f59e50ae31ecf856b322ac9dd5e1697fa16e3526f0875f7843c15eaa3e995ad4ebde7ac5665555cc08943aa1d911db2fe4549447ee4f832ed443b32329ae071340e9c5ef7112adfc9d82e0136c943f63ee7cbdee31ed5c99833cb442492dd92b8eeeebe1cf05b13d79500c68ddf5c892fa59452da1cf1d0be491619b2bbdd5996adb339e0379e9fe0f9f33a37d9e688b68a79685d57ce4d2a6a0c1940844e15709de8ba3bcb9672d46f73babb140bde75ccc4bc4f1a0ade567dfe5944c8cbbff7ce6326e6591ef1d2da1cf5f3b4d2dcad135a27de794c3c9dc77be76179ccc41db0a110133f644c34f15c98a0a1a1a18979114e27b92625e14cfa906dcee630ce750faa33f143b6b91dd73aba855d66bfbfc88405f8466bd287ccc5af34c93d3a19d3734316930bd1104e854f9dc91eb2cdd9189102c3f770ced7a811b1125ff39f4d7d1238566eb4a51c2578b78a79772716c8eb9889919f1a0a24b449b0c0051c439d92f23c6662a4e5414f599bc31d6aeeba24aa90a7d23a213fe57c2a6e9e873c0fcbc3b91dd30cec6906dcb35b9a5b022679c8a66f434c5f70823a0f4a5178eaee53c678ea4eccf39053ca3f1727ea79cc57ea939eb24e886278501e4578a74e9d1664a8b9eea4151ad75d67053920e16e435cf739499fece49e6bd2fbbddbb19c991a3a4146172f8b5b6badd5a2281af593e607e3ffcb06e09515c02d0c158089dd4401b01f0b00eb9e8ac766189db46bd7ae15acb0768b6aadd5568b1ebe52971b393639363936d5fa5551d70d9753b1b0bc1f2c34301ae5480d75cb54bd4a2dbb5375486aad20842548d1ee18a7b690d8fa130d2bfebaec46e34ea7d1f2cacaca8a5cc12c16cac2ac4c7737cbb402a5bc68c83f4b5978c67c979597646141b2524f69b362e9ee66e996eea9592696ee696261a1c162b195841f4e55732be6ee943912102b9f78c6f4e97d0a22247ab5a294ba8bd2aa02d1ead4c5c235e8998256f56a2e33d50d1942082194333a569e15a5ab57b8465f3e2a5e0d0bd790f45bf56a6efc3b535b5919aa31335fd75ba33159d0b2406dc3a151978f26a14f771faef07b7cb565e1191264a93de1834ea8ddd8e2d6361ad5b23c9c5b2baaaa2aeab22c2bcfd2eaa726312d7b1af5e0a4b3e724bd748ba7b2ac58c896524ae9a5692c5ca3521c1a0e8d998f43a301594633099128cb58b06fd395daadbee1d0ee75eb553b66a75544f5d2559d890987c6c2352a16295fcda558409c5285ac8644f594361dd36af66aa5babb4bcf61dd1485d123161234ce4718db96a8bfc768ab943951e6449913658d3ddd58cf3955c6c6be31927a49cd292fd337cef909e7fc46cd39678cf36c23f5b6a32cc6c738e3678c51d6cc2a227bb4a5f83e96d58f8da00fe89ad70a8e6a8abf2a05e37d9abb549c2ea5c5f864acaf4eb5eb53939f346a8a320b75cbe4b9982d8651ec5b758f700de9b196aae766ca96e267e5395f238561169bf198465f2fe1bcccaaf5ebba6c7579dd3a52bd562ad218638c54468871c80f92758a846e57d2d2ba3e4a2995e9a795b049e97ba04b3ab1293d10fb7cda0f175981507ea319258910c9a244dd234261212917a136acc88d9752f2b425b8527d2845dbce0a60f26c41d28eb0538f171149e9067aa4de4da9ea7c85c9b7950bb2156832993604a2eac330cdddf71e469908fc68d63d1a251bc61378720506af8c5eb172e14bb5ad5c4a29ccf215977abc62e5621486612b37567cc5153ca3c8159f06d9fc6024cea2cfce9a7557df9cf3692549cdf9de8cefdd94e731921bef1a47d5f3ea9ed11161dc79cc3d5807c6c94868b41b23b91047157b8a71be15aaa3cc8bc798df8fe71e71ce0dd25b9667de5aa93a1ef4899e950944d98f87d2fe71eb93ef69dddcc33ad43ad3e8439ac9611e47abedf989ca62769b17cbdb52dffae4272c8eeace3ead4c7b3a97d0273ff1e8e1ff04d79887559502c5a58fd6ca86a3baf3950a14179e9fe02778063cbd9b2c222823b9b1bbbb9b9f704ea5415ed47e40b8755ce6f279153f6ba55af5eaacc167d7ab95ce39674b47cd4967654a62d562947e7b8fdf7bd396e8e51f333b66c7dc4a4ab33bc64e7237c8dd3af190b6e74d6f9a3d379eb302d2f617fde8a7cdae6e9e33b38aa87f9885446631bb351fe33a59cc6e1311f101b9954af9de7befc9091ff7ab43ed7d4e69f110be5bfabaaff4ade273d17359992bcf4e2cbb2d5755256de97d1e5a7093794d6de93d9ecf5c8138cdd9afaab375c49c56e6e153aa533844d36d954de66d0dc5859fd4a51193dda69b792a1211752e8557c62be77465b453934ec2c046ea145267855aa5966559f3b22c4ad9baae43eb0cfa654153c8e51a78e3704056dddabebbbbbb9b7ebea76a62a25b9e8c333afa6eeaf3247237cf77dc86efd04dd38ae6e79942e014d28e7294f51ebfc7eff17bfc1ebfc7efb1c5a798628a233333b3075a5df3de0512cd996522d14f72d3c6d6ac7704d7e8b365091172e555ea7ba07cd7bc3f51762be75c1ecdf6b242bac86e22510d8c5ff6358f40d5428206008f26867d34a314bb89ee0d18769ba2074fec76131bb2b05f1ef72fcac8ad1446dadd3d3d0cfb2c0ff3207f7336cfe5a4b81c0bb3a869514a29a59452b6045fa78d4c4c9c764d9b9999395e1a2b3373f3cacacb8b356796894494d23b1a798675782db9754d4ecd9b2e5a727be7e565658544fa45229b1a7ac578ad57913a7422086fe6cc3291e82791acc9a2afec4be419f1f24eca29e4f24b615b040f4f1a89f3c96c2871a35b26750fd5e6a9d4e2c3acf79e2c83f51e175c43deb2ae495e58cfca64175ce3559627f3489c0b99f93db6cf96ac1765195e5bf2467221bd90d68bf4d1923837de4d69a5945276216f2417f23d89433fa7e71df3c82e7806851e2a2f4b4a0fac26cdad966559f559d985e42274bdd2ea5bfbc81b97853b6597142e0b7c4ce329a595d6bb801b0e87830387c3d970f0bb56b4acec42be3e8ae14ed3651738ac57d5d25092c9bccbd6e329ae5bdc8d8a419b2e08cb6e382afb8feca25ad985bca1288973a9eca24172e8864b2abcb9151b028563a577ef9d53cf7f5a7d6b111412bd6b464723e9c3bc2e59eec15ffd0b5eb7b80696754f697d5ef0e6c250bbb08ff7deb39856dfeb7e481a7a51c2bee3c2aef53d245ce39d21172d6d0936f6076fe235e1cd8defeeee8df6d5dfe8db625c7e2a2d9e73378cf3b9b1270fbf7ddc9cd9f4ae5b7fdf3e13b0e0af3f6d93168376649d1fbf02f35c9800d96d9e844cde7b87dc5262c72c24aafbdefc09d9130bb26d652008e5256937b62c24aa176f621f5bd0c776651141e1925b2fcbd2dcbdaecbc2de2bccf368d4533494c3d044cf34ebafdee83757bac5b554a77ee34dd2c248278293c4aacbebba2e6c9bbfbe4d7b7d5155d7b7fa78c77cbd32b3a8522b032fd49933cbaeeb882a0f7f695beb5c6923136f92dbf578fa94cbcac06bbdfad63a5993ae8b50b46915515fb2ecc6d242a2bab555ae4fd1c774fd621f978cef2fb846a56c7fc133e2a9bb14de5c9652cafe62023500d45063bdfb55dd4d0821105d920de19ca0fcfc24c3b76d094aadebb3db34a3e3c94f0f08a6df711ff5b7756f93a16ca94f29d8ef51946b50b24fa98d3c039ee7a7f7fedee127aef13e515611d79fa557e419f3c15fd78476bb7cafb1e3a59abbad3ded96364f69f5308fe12b16ea96e90ece73ad9555bdb37cbc0c12d2469e5155761423d7909ff8f23719bed4e9ab0c4f94ddf84579fa39e37defef4d37421b79c6342bb676706e75e51b0890cb80ebb4c8c9b93936b7eadc8a91ba05bbdb2a4dc675cfe5130f92daa354422a0ff3a44cb756aadf21733c87b4b2a5ea155ea67abdce5bdf7a2bd6b9f0cc966fe5d2062d8d3301a4096ed7a195ff3c5db9efce3585bdedb6322d66335b6a8b9d5af6b273a26c891e7bc4a2b3327dafd3697339aeaad44ed465c1486f455885444d621d76d25c0ecc8370e6e2ea08960d25982eb163b343a90d256c2c6103f3a866d95882f9d948b6a1848d259ecddbb96c6309e78212ddce8d9bcb8930220d455bc2c689c634547d137a31e13c2efa862fb4f1369488dbb38194cd8d87d82314dd0b8675228b6233fb44300eba732e29f7c08f34f75234e754606822cd3914cd0a7dece6965c07e5e3355919081d7fbad1561978a91c78e797e01ad596a8c74b59c953b6c46c97e01953daedd95cf825780684f444e3188e71391386a6a16827f555279a8b715611f2c6a47ab495c4c99cf37f11908f274368517b79efbd373fedb3598287801ab486eaaa63c69899e185e4b2484c2df2138ca45b26f64bc3ecf604126624d331f313dd62c53be62835772d4dfebd62f05570c9a4e4ed7412bed142cb489a04374612cf4822c0976f51f17d6e59b6da9c4b76cbdae2c7ca2c1a4a32ca6e38aa63f4932ca2343f59eaeebab3d8af63f05ab6749d9a0065f9090b09689f885cd578a8c51810c2d09d8f318220744bf1f4f58134cfabb35be8734b93af22c300e89e3f90e6aa568920ed5611743275a94d6d0bddea52543c11dc7f8c849f58b13abbc29c3a538b43f8c89c9375260fe123131fe1217c44c431d6737933097c8475f888cc3c5acde465f62eb3bfc73a7c8412429f01c9fd755a980188b49b8e2b0fb5f8c8333339de9544c8770e6691982c22a2140fe63d8b88eb9876317f72374277e11f64cd4d47d4fbaa3c41111101e1291bd23806f4f164d6eb890fb2d23cbcef482e8cebb458c3cdad5db17504bc59b74c0babd0ab96c6a734ca5611f49174a389365b251e4513b54afc8926d2d88a003d4c8bb4ec28dae7279ae7261ae8ceb2d131a1c3f46348737385c6cb1059bf5d43cdc23c07e3676e9cecadae2d783ee29bc23361159655c73ec7b0eb309d61d9371010184d4e5044175f8472628279387461d7f99e0c7b523c299e1493a7e5116c460708f41110100602496c4405c8032b9383b42dbb333a3010f611100c34b2c204948140d64d4037b138039d848e594f9126d831cc9a3c115e9e5899be26761bc10cc86eecf9c763b70cb33e0a320a320a320a22bb90145a369bd1615d24fa645d348964f33bae1545a2ec5366d15b728a2815616589b42dbb333a325196890ef344d63d5ae9ba1361229147748fe5c13cbf2eca3c22eb338540d84766a27fa6d004fa605384817272d2cacb8591797e896c36a14c2793c9149a40d3679a98dd5ea8cb5f5ae419565529bbc5d339e374c2940b90f0e1f20caed3420d3ad4e62a7b7daa7f3e9f4ff5cfa7faa7b4e3560e7e3e55755dd54c223f339b62ad5ebdcfe792f968db435ac9fa609f4ff5d0e7a1ea0a1da631cbfac7e43af60df4ab7a8acc747d3e17945f36f4ead7517e7255199cbe5d329a5899be21bb8d6086b37b32bb615745f908cfa86c35817aa9dde0a71df5e564a54d77b7a7adf3118fa7c23cd83dec394c57fca9be65af144eef1a7af560a7af0f81348b89d48f87eb2dcb6e15d3f3113ec247f888a7631fde7bef82d2c4e4105af09649e419cfc48462138a4d2836a1d8e430cf84faa595aa4965627299fca2aeea323953186326c7ce33a35ec913c72106f1873d9c31c6159f5cafb71bf35d87401fbe2c1b6334615a85b6b9fdde7befa395765cceb24358c157d91c0539f53ecfaa2aabaaec302fa3fec9ac2ca3fec9fea1b8faf0abe715f594f8f9565596c2aa8fdd463093d9cdb215351f84675074daedd94cc2a036ec6eabab99032dcbb2ce3956b52cceb12ccbb2a6c9aaa6eb569d769379599c1b13c9344cb3a65b293ae3115166b504c37b5d4d3a51e9289da6191dce4d229452eaa615ce393ae77fcad2fe7f68c54e3c16c824bb2cfaf19c602093cca2a28732d18927f33cfbc75e009fd089282546004e3240010820630056d3ec05100000c418a5c010a19c9884401f4f865d96a2368ab2b72506ebbdf8de93efddb27bce49ca7e9772644589f608c89b2494721bbda845f958db6e4cad572debfddad7dbc29e61575b95a5941565184b25d5c9d8e4902b1fe37c71aa26a7e88c804c37cd382335aa99736e9bb555dddab4d3dd2625575e29491a4717e9faf74edb6e2949a6c72f489051107931c403849275a0942d25948f75ec1631ebd5f58fe6c91c13b12a2b33ddf8884c131fe1237c848fbc19aca118aa93e15c7c6e92da96f5ad264739237411c6cb4ff67db20bba6137ecd449e6a4ddac539b8fed198a1180ca6e2f77fb6d945025cf360fe65c7913bbb1911bb21b34c23817e7cad0419f76e301dcde39171937b9732e0438ca9d7329c061dc3917037c74e75c4807c09d7351b900b4ab68cea99034e7540ca039a752008d009a0c6d009a732a34d66af6767b11800d80dd56006063d88d34b229763b8c13bb65d26e32f186401fdbf3a2a124c36ca52c2b13af7ca5a56a29ea4dd196f897d9271b79c6cbd1baea4839c2a9463574e74eafee46b4cd0df5a699eaa9d4e254e465afaa5274aa8f8054232b4a9647a6e88c80700d792aee163aac3c9e438b3d20bb356466666666b615f4bc5b551c5951bd149d1190917cd53b8f82c05797e90ac05752db463537ba2ce6cc3291e834f2a50504692a7a0fad5292409af7f2b2b24222fd2251c35190471fa722293a232055b5c518d55c8ad1ed2585741439650c1b2ab194524a91ddfef8c44a28e508e7d6f7deabec28c808c8950c318ddea35d5bd36c538a4e671195d018d12d0e833459641195641acf4b69a594a320ef103eca51104ea9ee24f940e33215792ad3776e74725e8cace0dce91b691a05c1d145ba089592e4522c95f0744c8329f25c5e5e565648a4ffbd225de4cac72925c98d1f05e11ad528488a0e458d6a2e9552ca51109e11290c16c01ce86f301a2826d7096b17a5924a2aa9a4dba36207a554bee9bb3e293e94e2c6243f7b554aacda9275aa4abb4d333ae4e994a7534e17a573cae953b7eb74bea24c594a299595a5205ba51d51af9f522291234fa594a1cffaeba297b624dfb4d69005514aa90455b55237b75a1425a594525217a6657f9ae89386726872aacd7fb4ea308fe935a5e0cb96e4add75f405c29b8c6f478b2e702efb1f3b624e9abb4e4ab86843feb67c5d54528da89963d15eb269a7b139b8f508a7aebd53ba7fa641151d15bab9452fe52534e33577f70d059d41fb7625d31106fad9ab5a664a1d40a83a2a09cd45b2214eba2fa030546b5aab3a5faaea1ae3e205cfbe4dbfbf17edc780ba556ebd937bde94daf288e5267c16a5d1e26fbfcb81b8c1f0c639232ba07868d012346c5f5c634b2392976cb60882c4bd3b585ee81a45d920042870ff7e528e124348ce4e86c6ec7ed0c41e3c25c8065a016641e22783e63f880a8f02613a010e80f211316989c7caa38f9c4a3bc8080bc6051018145cb20d231ace8101513a572cf855ff88557483b4c8456906d2f1df8cb4530be906d6e679b2d5b63314563d144da8aa6e2c19051c6944f18b2cded4c266c5e919d9d9d1d6793813354d72d20e09a5b724dca0c10226d0c847b308d119c1b9b259d835f6c08c1a02c6c707ffa30a9f74932a4c87381e767c43c827a99b21092f38a02dc8c68ffe04351649b33f2e8f38820c988b647b819efcfbe7c608d2fa09b1797ac5578850ad214fce31a3152396803ca03a47f16869bf1a89d3da2c27d771f9d6c7337324dd818c1373733f4630a79136fe0cd0d0d7838a10d372419cc822ce00c8921babce3bdd7ec5ebcb6995b430b49b08999e11ddcac04fe64c1c60b1b0e831736d188112336e69c73259b73ce25d9e68c349c18e6b9c8bf1bf15ce2d4a47849e106373827cab83b50b58103b5d908597643348d72908958d0e366a11930b89febb4888318ee969dfd40732bd6f39660222db9a19384910a2fe0bcd8438f1d3b9c010b254062e00fee0a10ee26a2e26ea41fdcedc5077773373db89b335273210fe690e40863f0811a7e78010c7007406c01270a353c74e2202466c60a19c4600d72c8c210a2c08798feca737140164cb8828a9c1a306108317d98e7e2b2480213867421061af0200e31fd249e4b0370e690072c6001061e57c4f46f3c9713242982199450410728d82066016668c10ac0608517841c89699a1ab0a1f298020b7d40430de620071b0401ea8006150f78140951c105e6023f5c4970411772a0c8208410562c8021b0e10311f480033a08d98153bc81055317d04007287ee7c590374494ebb61892852147ac232c794ec417fc0b23a0ebb470c313575eb7c50ad0206326c0096e3e8639b8a49e89a20f3588c3139840042531dd5ce4478c3ba02e60430b824c3d842e4c95138e0061f0890f52204245c1092c48a18ae9c4c4872d64b006eaf270dfdb62055fd09b2d64a0c5c5aedb420660b8db23139c1093c7f3aafb1e1a7e6be59efddea3a6a99d13629a64047ed93a17c47b6bbf273778af708a3312018f609a3bc5799eb2ceca591096005238e9a4df22c7c8fc4967e4182b8cd2cd2b34855ae184fc4acfb175ee21a122a5749a788a16ceaef4add14fd3ca95f255ad1ee35df50d58d6bd95a90bd051d43c45e79dfb085ce8357c6fea8f60e6be379d9a93faa43ea94f869714457d4ed4e690f023988980a31cab505db760539fa51342462744844e08f89c10af9d101d3fd996e2dfe361bad497af5193324608a7e93d7e87dd7756e92398b96da72907b67b4208fbd49b7a85da564d337bf6ebdbbbccfede2fbd57e122c699e57869638c18dba955e62d6db38aa8eeb42e68bdad0850b768ada89de27c73be4f32c6279af46467154c4f359e29cd2eb5f2360601949055eeef2eda8f217bef34de9ce83451ca73b6caf44b736fabcf2c3b3569a23e69f212c6e8966a3759750b94d24ef2512b8114627ea377b3275875eca2ce3cb4e009363195b7d0831076626a7b010b3cecc4d477e30220f888a953d59a13030e919c980a6f728cc4d4a8e343484c95556bfc041688a013536790214284c4545ab5c641a6f0e326a65278d0c3196a626abd830c68626af5052700e126a65a794092b3862038e811533124b44127a6665a0cb1424e4cf564810b3631f553b5e6bcb8998226a6829c165f6c01276687b3f9820b45980d40d440c36f48034d4d4c3da95ae32d7440849d988a52b5e69a015b75793603f85554b5e6eca0879d9a980a0346ca1e84a0849a981dcc6f8b2f906c2949dc9ce634cda9e15ffca4c1c7865372dbd81c86c1e54c935d420c48e6bc1b433127cb4f0069244f0a32f01132b4ce9c93aad7ab463dad3e0b4cd32f7b91ea75bd08774bd7e3294b630ed9e888ebf157a5286de6520d87e8f69f83da4ce5ce030d29e504a6cf1731cddca76def0ddfe3cc7d7de13403998d70b4e69c73e64a6d939fe4ccb52c3ca30ff366203318a4e4ee9b514d93e4a8a649324e31c2a74c2949ea08488ace2808cf98a0803e608f2689609a373759f328c84dd60f2699baba4b5578aa82b7e0f3d139345e562aad52578081c015229c627c36a1132f25949406a9c944f808e49c0b2f2584d7263dd122eb4ffc28a1d40236dc82d4c403d224f98034493eca4ea2fae9956c244f8aa86dccfda2ad502756b0818140b7307c40ac90f5b739ab2cc2806ff0049b98429a24af9c429ac472c736032b1c3bdd542ada6d42f17c5e2bebceb998fcc2ee5c16f26837dee46c5e5e565648a45f14e5e434e6076f6c9a34591a4a3297a3c3c2c833a669c7754d3ad922eb6f2ee746073c70f5c443ce0322815427c34db74e2554f284099d9311c94af5b07aa5dea2d349ba05b6924afe5d1a914664196495a253ed280864db5e344e1b699cf6c2088744312e83d4469450a7510d16190e918c6f1612c5b80cd228c8888a0b3b67cb7a07270bdd8b6e89d148b2ce190599e01458c029eefb2848fdd64fe1ad73ee1b05494972273bc99caa868a846d86847aca66b148b68944a225504de8609ac82df154f6f7eecea8e9188b2615b1e1226cd32d946d4e90c09f70daf8848cde15814bc838068cd38a1bb94811510c1263d1840e915bc233a285d0081769e281de3645951136f2399f8b3cebe122f7c5ccf29b78d9465a42e592385d5a6861861e1701b7623b3144acd396641bcab22454d5215b4a9b4dc88cb879cf72f78aac240fef46ba150908ea43740b5579c0e9e0a828bee421314417c5385320090dd850e2c69350272718418124346043891b57647c8da040121ab0a1c40dd2151941011b3148403c7743e2233ce4c8345d73e2234ca4d6a16e40dc6a52cc7242f0090f323e5b19275d46b790ba25f2890fea542dc39f40218b775d9dd4647cdf7b7ffc77f73ec4fbdf65bc9055ada4ec66f654445374d2da39391bda11093f587eb0fc60f9c192fd6081d60f4a29b590702b8ee507d532c7f2c3ad38961f593896898bba0d7199c66516c9c233e02bbe86442864a2e2d5f4147ca75d99f6fdb83c979fd8677dcfe9a39068cec3d0685c886252cd53ebf3232d0586283bc9e2c287404d1dfc74ab2dd17a59c712cf52ab65b12cb52508a939f9d637ba0846ca484bd1606432226522f8dca3bb65dcd8dda218f7bdf7bae70f961f2c3f587eb0fc6031695018429f8f941f2ba5b41b12d30eac91b2a1e828a69dd0a5168a62da9976604d4781c1f816822de4ce77145d73e79d9bd36e32b0c77bc6ce06c87e3c8f6e240cb66082348f0fbd6452d2504c3b4d82dbb46355d5b403806f5900be4d013cda8dfa27c6e33633d03d02b0ae55b2bf56e918768e2c8c771a31bb85328c3ee512dd3997d14f465a8ae61c0ccd39156a1d907b75cdbd6e62b7ac85dc4b64e35d6d431fd1bdac876828c940f29ecb5eff7c3cf71105d7b82edb51cc40bb73a3e019f1991619098470aebcf08c2078b9a568b32a605437a25ba205e214689715d22f12c5ea155622238ad0b8c7c76e248f15b1c52eadded2a8b3cdebaaaad8f5155fe306c4b672e38b6dd3366dd336f3d166a20e35e93d74abb6b91baa19b96feedc28764e1a060ce0a4653370c22e5dce74cc22e1727c480d2ae16e748b3ccc8e69999439ef6e9a7ea35ba08f2b35c7d3b4d24b4cb6d25f58ad3393929d243ad38d0eae11a8a42d98643dc9d4880000800001011315000030100c07c482e18838d2242da40f1480108ca24c6e5219e86192e49032c6184200c00000008000a0499800d2c3730312345a41b15206bdacb84e2b1f029f9d8e502d16e8f9e56813233a05d7e6f9c8d82ba6c3e017cb304a39141e336b391e3ddd9753d16ac99b135d53d35460e5c9d4d87b49be59f3cf10ed73be8c02e28ca0009158e6ca59f306c35e552500df3a93e1c977f46aa097b1a5348ae8a5e15e4a59949d7195efba0dd2cb502fe35239603ae269b8975ed2594ab2ef2d008884cd6708c21b7349537e83bd1c95ea4ef8508c587121c40d06bda614777b35ece57c29eb573b2c5e39897c65f0be5698e40cf11f4ba729757a4f707b57fe60cc5f42ac8155b70a1bcd4f752e784364612e9dbd2ee8d85b85346723e0c502221ecd1a6545dcc4740916040c75e432cc1fa2a228b470daca363049e2b5eda6fe879a78d3d6a3fbc1f772b2e3fb96440008a3a1e05798919dfc836ec0dbf9099c5f7304ccd142332e09710fbe421ee299b6b2a787e60fe0b62745cd66ded1553cf41f83ead56b0085dc86fccbe95a63f976d03e45f5507290051ea7a0d17d6f510b7137c179850bcf9744392e16f609f4f113e7141b2825be0f6e829c0e67d2815a1fb71f4c3ee57ef1363fec21af9ee9d7a7b6d93a30e144dd6a10435c921a13e479a70816f50b96d948fbfdd0cad29629c63fbb808a064ddbdf5f285252857c0103cf4bee02e3e05b67950c87a2b964cfebb9679fe16ae67807ac185d35eb265023b73ee520530cd95a3631b4bd85714d8561ca9b12607418649635443037306056badf3fc80a1d5e3c3c0078ba0e3d15fd830d039a2fcbbf9e7d065dc1c5f025fbb4045110b3b2e64faf077a995daa30a2f52860410493caf1dcf1a4b24c0f2fd148402567e9d823f9f392cb5db961b7ba388d1db9ca20289d2acb5e88b4ca0a881d2991ae4af7049156f359edf613591c5a28c7bec4d3b288a4d61a952cf63fda3c8104abe4893c2ceb4fc686df7cb84eca5422aee0c8fa1c05d50f2fceab26e40f8b6377951a2cb9e685760ffde5b4acf2fe30e3b506f89273b1dfdb8b2cfc03ce8bca3411943aba21d2045e22d41b7e5aa0722241431ca49a19f0a838fe78f4e4f0bf36fcacb4c416a46d7dfde6f65da9eeda75dceb65207ac21f8f8bfa61fe3b77e0a7493eb84190b88db1c52d340a04e0e25ac283a97f2c1b7da38d628323c35b96800c3baebe1380615ee94a0fc25f33e4b11332a53a01cb821c571da192c18775f599c7ac55dc161352378efe0869e2e10cffa43352cd755a583d4dd4188519a1f0495b647eed4bc186d94c371dfa188a1eb1a270d48c9c74efb4d43c21abfd81c3e981af146730ad2f8cb3bed16782e0f6608bc291bf6e8a58f35953fff4a149032b1aabd3688b405e0e5c3fa4bf3eb96b82f11fdef11189bc4e1331c4feef2b1ef1f467307b6fe095ae9b2755e5699579148032accc1eace3054df08fb56ec6b645ca7969382e45667f8d4372954896f0597ea72e32fe50145e8e18510798543664576976520555ccf44354df9154622a337427fa80c5e87a45350a18341ab2c459d9ddc171639e7d8f68c5d270a4a383210ea2eba3920708022f04a45507e3ce61341110aa9f30bb3707b0574b77bd27ac4c45ebd2c8b61a3a38b619e9b341faf3aaf399c3a9f212d899f9b7e2f534d9add4469b2d4397f9fcc2df99f8d799f5fac7bf69492a66143b80fb9616bd314cba11f48afed0edba3232e68e8d2654e0df4516a7563708b9ebbd10d82c95c8c6fb9498461f3d2788b6c8ad74f0991dece840e90c8ca46a2b7ff8d68d1d677ba7b2a66d72254b86cb1a32085e0569978c770c00683ea85c39c16c55ee0a3515d9205d1235f2e3ba75020a4da072d90618259b4d7a1e890e422efb3347f49c5e8e8239361b2477cd28fdb5f587d166b5266edd8212a32799f223b098268f14b9b7200cb1a85daa48ac8a3a71b9a75bcdd01f440feb3e3782f5ce66033418ff16bac6d312e97451f4747c996334c916b367156090431c4b8c2b2fb195f87f1e6cdc62e9c61122cecea36d525a24918ca0b7343756ba003ed99fafb9fe3cbefef7e92577c8e7a8c85d4733942deaee85588154a31fab1ab585f3c21115af0370eccc6c6fc6e146ebfb2b1f9a5e4737741cf39488af3744a43f4195bd5f81751919f4f66e505253d3e04db13c7ff0d63c163f94ba0517bbb3e38763a508fb4cadb14016e28a617072b3ae29b6a9bccb0f60f24370d93b3a0b6ee37ccbc89e3cafe9d451b4c7e7605930f74fbadc11f612e7d37f5a0db49dce6412738859d31f05f8209661263e7e54025df2030ecee406a058066334f82125c5802d9205d7354af750c9c109bfa4dc64e19751268b37bf527bc1906bfe9bc3d06bf3d8b14bf785554b3e46d68062de684c403f4adb71fe0bd69a27507af91a600a1ec38dccfae80295a8f3bfe4f707e300e7885c37ece307b775ce7f38d6a42e998411f4422ab2b2bd8c39ea86368432b41ef1e6c3b0aa2a6442b76a8afea7ff39ec1c3cb3f81e24c3ee05b46c55850828a1a8e269139d5fcf2d0ee87eadb8cbcaa3e9c67218c33bd048a66c2a3cb318ad97befab4cc05f10ee07f709c54c9f8782aafe31c65ebc5e590c4437cedec9a31d59afcec40f02ccd9f9e29125d747ed2cb669d3d5ba2414e19583b4854aaed797e51e6a543ab7fc63cb80897c30c4961ff42ca5df99d769fb69106937cf32018654bb3d24a3a8e2671fa130fa8d3ea44ee706f2f85d1319f93a5f075510c5d11793b9c88596802c48532cccc51abc5db62f942d97a18f20428fb7dd7fc9b44f85f3e026beae560246c11571579f0cb7da5dece975c8bd20fba98bd6cea45ab3e31a6abf5fd59a4f3fbae4108c0720d99b3e6b399e42d3ac95e2e52eff661b8905a17c150ba6e1f359edc0b2b2ef6e98ce0b7808329e33797f4562ba2cb85f3424afa7a1be43903d96a54700df5880870ccd76b970c9b06ae137ad74e4156fd40b53aae42cf87e9b7b1365d510b5a0b735d151e08ec28ab9c30378235f119eb2437b09e932d4e016ab76908d933da0aec442c204e436a8d48ff3521fa9a2bf3021ca717e0b019ec7fad46b3207bffa64cee6d83064633ebd431ab94adf8b14717dc2086faaf831bb0e6f25c05c811e53d1087f85c97bf250422572a9efe9fae2a2af9b709db20eec9f498f344422a77bb7f2fd0595e966f64609892e1880d675f0a0ba59006d55eb64600bdae4e503ae2a45ca9a196806256def0213130050c4d15e5a8823b9b5bc4509cef0f39c2e74cdfb5a953038f3bff437e456f8119ac619d45af52abfd48fbfc27497c1c2d73feb9c7edba0173ab8e2b47cd9665cda557b0dfacebbc4ea7032f2bf57f69694c91d64d787dfd0991956ee2be1a645e5f0b4b30148b3b01bc406b8551784a18c2efb3dc86af943fa3b40f78e046a6cecf5e0355a4ebfb90fa7b14244316d28f43c3b3ba143123281a61f822a484f79769dcbd1bd6c76e1ac8f4b5d8e2d439528d707f395c1ecc1a7715a6b431a0f6fd89b8aa0285801ac875a99989f3e2e7630c6914e8b082b4233a3636039949dc5c6cb574d3c5847f4590c821897d207a116f7e4f5c14846aa8ff8f186e47bd9a49c3d44c43515eb3f34ddbfa99bf462ed95f17a3496673bd525367fa8b70bf479ebe4b8847c4af3c799001a4304c0eb826607120e46d61bc52f1275161e2cc84fe91f48bb50ee3dee807373d20fa3a6c5a5a4b755fcf8f031d34c5af19905c962426b0f99072ac3fcaffd9b390335e242adac4fe9f60981daa5f3921fb0c8761169d05782e3343b4ab290f4289ea918a6f512f9b020e804f3a64b13b4c64db384924f591d52b773d28ac046a4b81df9d24d3ca3a23cb7499db77b4e339174cbfc628b6b46ece7eda2b026adaae926031f03d30dd64b475fa820c29cebf8f4c75bb0d94a73b0c96f8c40d15247f7491a7be9162c0aade681b0f2675951e43f1e496cca6e8ce236e1eeb535a58fc1075a399e692e36eea60eed8880765871c2b78697a233f7aa17a975df36dcb7c5bab1e905893c32a41dad72b8f09152d65e20bab56584448164c2478015161904c0191d7cced300c98b438b6f5c857c2207e066a3f5fc04fa07aff49c18e77e59248211845faaccea9263f58edfefaa4b511f98d2ac3e502315d39d0d6d67f72ccbca371c88e5f429ca63ec39548e20325d98951df6eae992fa9adc8037347c20c2aecf5dfd0f06a1f653a3fa82e318e7760eef521a67816050212cc15852623b4964d531e1318c4d4411d2468aedea76da63ed7f4a1f7dcd03028917654f9edc46e39a778ebae52991488043cfff9b716e5ae7b0a0218a41fc1d4329bc5f32af06dd2ce15ec3f1c3e6f541139986693891042874d6ad25bf9525e41d2273b1006e84514238141b1074775a74b98abdc71490c0c96f0ce7e8a938a355502dc7c29f206ee7461b7c847a87e4b5b77d77d88d1e0828f312522104725f0f311b662f0c176668966fd7c82d3ac63cb53f82902e16d3a3195e8fe41337e651e58b285ba66ad358dc4ed77a68e2ce34a06b09875abb4be97c3ef01b0e5536b8648f90d6d0081614c4328a988772313eb8ff5612170f15480b0fd826f0723aa141902173dde1128721aa7cf33453a5cfbc6ed941cd8f0b5c5d7410d76ee17a8d5112bd03bef2bb716f59bf27374d171192df29fdc65ae70874c5df4adeb7fa87f625f569c996b84e04faee6c017b8e0d927b7bf127c728aa2a6060755cb5d0e930e4350dc0fc21408da19b7130075f2a5419b86dcebdd1b1382d89b4a943bd46f0640e8d17ab7d0168b277a3d3aaa453fc3aa47c7e31a92de240718a50670e3fb01824386c019a4a04684f448b01de0901da054a557027f3851d17924a53b73edc52ad55493505e017320f83e6e57eb514b2a72cdf470d0e8ee173875194acd0ed91d65770e2172c6312a4cd7d15b20a3eab600bd928077d55aaf086caca49a0dd68a6d5b2d564aa0c38dbdb28815c3402e313f96ac8f40316b28e9d428aad7c7e44807800269b2435f3da5ec446610ae5eb39051838018fb9a7e8ec0a74bb6381171fd99de482c8e8bb5e3edb65d8f157c01e2f2965cd70c164611664a6d01d43afd9b4a1b5469c90645ea739e132281ceb3308508b039ed6e2db4f9e7e97bbd36599010c313f36c18a1bd3518489fe8ae1e28b16e980a8f786260ea37dc95c889fb59f58230482a16d738cd50b009efe79ab0b5fc2212502000e8bad67b3fcdd3926a04ada27444ae69dbc4debeff9758405f25504dc685f2a69b28b6e0050b62e6667c0c114a18acd5e0d830f3493f35ea05fccb27b7cd8332be79d87868df6fe6d6ac5f255f3a69107ccb28143ec2f2213025fb087a74412df4024e4c2fe0901f01148d6d84d96a9d4b03fd8654db33bdb15d77236ebd135b254aa86f529fc3372977d447afaf24377da1134b8a58d8a6a52c69b20f7c2c2182fba4414587e05a7cd21cd26518a018a9f7ea2a78d789abdad06c7b9bcb2a845863b23bf39b82d4ffaf616022fc3ed961f46d62ff0a575f32a915ed9f7705c22b5227ced444d5d1758c7c52f870cb6e65dee78d959aa5aa40d87bb33abf9cbdf601aad9a8336b7e922e0c3dfad325ce257842ccba8f26dab31312ea462abb91594b73595ca7bca129d9958710a7ac3f3fd121a9cb53f69c913c4876fb3f2fe92f17addbe494252198f2fdf09767feb85353ebccb79f67ad39910fc6fbe93e0c7cb73ee5500a3c81db5f208430f95512820f053ac814f2cab2cf7aae262a89db970da58d807d0d5cf5a287cabc1d4b7f3769383c4cddb6a314b69ecc2be9f35d7d305a4bd94b887dfdd121abaa897616f7e5a537461ef11b979c2823349b10db0361c86c0d0ac92ea4489b2ccc7c1522dd42463179151fbeb184ad8263283acebecc87c8f58833c1ede47913be93583a57755fd16edc611a4075f16d029b7af9e4af2550022b33236145d0ecd5aaddd409a5097626aad88a2133bf1f3ef6272979fda28c3ceb9a14919e78d9028b79e444bf07b3f615309a7d4cac2406ca7daacb996461a54f06f959d153cc60c25e46d3d09170276baf4b39d37d9a18f092b8c1ab2f99cfe9dae28c4534d36eb1b953354bc992ef2c4b501ad0512c372a0b89d3382340043118f43e502913c247a3cf02982b2ad82d1876d31edcd3fa09f9905b283119a088f0ac4ba4a5b5fe53bfefc108693d132be8234385feebff6ed6bda88ef6d1a4a4710fb9a6798240d3e33f1b385c20a221ecd9edae3e407547d2181987e22587dc4e1f6a8a4d7cef68dcaf04b0b697fd8a1068bbe667c56d10e9141ee5974215e6a08c1083546377ba5f7228f6b5121e54b7862f9af677897cd92ba8de74677a39f26b6cc83a8479e39f644258a1f808fb8ba7aad54dc90eeaaf9cc9213cd13a6c09f443e83c19bc43e82c197443e83c191c331d30524d2bf03efb7937f4afea8216701096a422c5f4cd105e286bf982bfaf0a454f1755f80653392c82371c7ba7c163418f4616ca4fd7c8d2b5e0b8d5cfab4e83d27389b3651acb6766d21b20530166f91033bf316e71779152568b555c6c4a4d0a6aa75c9cd7dcc8bde3a8c09949d0b95c2849b6c13ed9a560c7f62e04456389952d5576bd5e11ed6609a8bd8f3ebf647e19f1fce29182be444f0dec69fcfa249a98338194d9f2d5f14aa70d6df21b9d74d01a3b1e11c71368282949ac53352cd82d2a43dd679e8694edd19b5b1b77940b9c9d0bb321b8ecc01a3768b365d814848465def84783350d6ec3a1073279a687e3a01a63e6933206489a3bfb9ec7326d28dbe684ada197e82d452a984ae58ca7eae62510f0508735743adafe18d4077144cfd6957f9176ff49d8023fcdfc2b28b14cede2c456cb06787f55ec16d74c3876cac91c13bab853545e13781d1fa4130e689905739b0becc649365f083e5cbe9bd255bc3ed4b005b17ff569d6ba7dada8bd8cc6ba005737e80f5df14c4a282d7757648a3b07e40af85c732cf4b3ab5b4a2ee2d5c46c74e06bddb50dfcf5be7de0133dc7e565b21d68e8e23560f59ceed05acec5735a5950adc4ecdfabb0b2b1d1f468aed0427ce672d3e0a5c6ae31c1329ca09e4e4e6b0c50ca2135a1f30e2d5083b96c20d74d77755e20cdb04b62fdc48ae20cd2bf7307ed21f7bdf0e9602ded2cc6899dcf376b526c40e159f60ad2109f435256876f564766bb38ac6dc0446e6fc5cf83e2ff6918286461c95aa67e7dabf5ba979ddfd96338af4e739c29a2be051a7b05991e25049c9c7d155318e9c421fcec1fac42d5f971767e8f6cc39fcbe5b00620136ef80f94511af7751eb228d2e00202856f4d2913df71c2abf2b02cbabaebbe4108a4da91f1ac506d9728768ff47c0a59a735c1afab2b85b4d6ffeb4d96eb6de8349c4848a83ee7a0eeda83c12f9a0e51a6d5955295552e0688c3e5655502162c54bcb685f156777a7f095f5b0ed9a5aba2e4966ec49b6ed9329c7dd1bd33adefc20ebb6d174d9ed86126aca5162e8861ea7f21b43a53cd64c12935286c1a6d3a0edf584b278e91165c2af27c4d5cfe13247cef088fa19ebb9b50ed04c225fddc2bafa4012e7a1e30530cd95ca51d52f6513d54ee1838a5069839b2625d84043f5649f3a6af7d8def5442e3f22a6f41c1d776ce8f259e206ae69d5327c537abd30eb971eb7479f3e754f42b0b31c2710e382856b6af1c9987e3caa24a75a08d8af8799ad2882e0931abde72cd90bf8f0d4eddb138e6ca4c0de55121e1da4eae8bb616ae1ad94dc3a00de1989e995d837d5d67b839b6a0bc13384460bd7c06b0a3eaa00030fa81d8d27b4d2b617068aa1291a557f7750b6e626459caec50c25ba8b9c96f5beecc4a30bd570029621e0bb26f52245b513efc320b1ede1ac634f2ae668b9a175cde54ebbe1209ddffe70ff52d1f9cb14290b645f21799ade1c7a018eebf2e330562a6726949a215d2a532bbf5b918b52ab648cce52337f403998e82438cd2040e92dc85f363d977e8dcf2c958b263bee7dd0fe6b3cd9728c423e79388aa498be4665a7256f2bb8323143ff55532a6e9073900daea1255a6ec75a12a3a072d8bb05b311dfccb10859eda8be5868e18340c3c3846dc6cce73215a131b95602d3c3a1a7fcdd0d9129a8a396657908bff311b4d940e36e603b18eae4733e61430aff35ce431e5000456d0da32073dbd3a5c5b9a402b9a515d99291b1843edc6cc49c9e8e6cd232b8242361dd65002f0941254297863bd19a8cf32e2035559444a3dd0838f8c6585f31eb0f67fd5cc9c0f7bdc0f264bda06ffa52e00c5345309fbc5065e26b3fe190a74f106886807dca3963c0d84da793e31d630c16cdbf49b53495f996dd68e5e5d11f2027b73ae3dc8cdd33b191786b9dd4b84e4471eb4fcae60b347a0ad9ff1ba45e3e02ba8301a445724e772d51e7d5459d2538650ae414561f0a6d1f8e5eaf03ef2bad5ab0d8566e45287f81fcd9fc78c065a27a8a05000224e2dea7515e42cd053402c2fa2ed74762cc48e02b2568f24ac15ab297f4a0f5fbb2580507678b903f571e2315b450626fe4f28795c2143739e7c61a54cdc714a9a82d5f4187ff3154612a522ca7bbcd2649d92723d1141d2ab115ea2f4492ac6210e05100d510c05a18507f983ffa91c8bbd63e898d70e87d5f7f97f90c44a345339f319d56df9611eb4e7e915c893a88af4295817fe6db3c8d33f4c327dacc2c08b8ec1b9cbd3c8ed079cf6388aa815d269dc144770194a4ac0f91a2eeb59ae1caad7033151ce527bc4ac6a28834283ed82dd8fcbcc656121bbcb6e1b5aeb92188ca9adb400ca769b23485c65545ed95a55bb81561d316c1eb9f6cc5315e935defe7ae12dc33fa1495d6c01cd52bfd77c78fc681b67613be429ce6d53116922bc3d0234328b792563a74b2a614fb2421f43f3c5ffa356963199ef295415cbdf9a2811d60e93c105ed23dd070420e4b6e667409a3184532b622d27ac28498080e418835a30b5479b8fb0de499b1fc80e3f42e4b54d0bf3fd1851ec1fe92ead42d50c6ab54e781de310568ed41399df2d478643c5aa11249371596d77a86b31124ac6c944dd0a9145cd2b67ec0cfdf8e3b69a7bfdcc66512ddb7e657df1683888331d44e65ab17fddafaf5cfbb3e0362cce8bc66dfbcab67eba0536e88d5a0abdb266d2347b029cfc273afb498cf8954bd3f210acbedaf9a21ebdf72103b52a7b7102504d52dd0d20cf7944082bca52a74ff4b55907c48927b79e1e5f38d70b9de48bf0181ec28ac1b95139a3cd13dc57faaacd3c18eb45598612ae8e36153699b1b528ac8c5665b3e397c84137afbf5b479dc22d6ae84770f1b67c6c3262596178cb709cc26667f1d2caac1608772b6930cd23c3e800e084f019ddbb0d8de12532b1bc08406077a32bc451f22406e59fb055cf7dc0a16861d9dc680db19d93ac1ac0d1c74272821a1a6a53cad80c6c8e223f45b2f1e09ea79bc24b2ba4b56c8a7ea8213e341884445b15c6bfcf7e8166684a94f80b0ebdb59dae8faa5222aa6812877da3c7f4790375901c5ed0d1b152f42422e581ee58ca20c0ffddcb60c7b3795d5c989e6c647983237cddfca4fbd6f5c94fe79ac2633d15c057018713766cc4e3dc64048fe79894a30b3c0221f92d95477994c32ab724a9e1c66fa68e73433a2a89e1d08c712d56da68ff1512b5e5a36dd0c2414dbb39750751c7ab3b59902dfdde7664544d0fdb8d02d002d4917b4c39a0ff0301b600f2e0d501ec6111a7be9a7ec61bfbd5af0b8a9e9e28ac8c3d508d174a21b74f80f24708a9ce52aa104caa2e4fa38827c09908589d607e2928d8716b9675f6ff2efa00eeb1ce04719a4e7d834dd37edc8709f84aecdfe374250a82d3b488273a633f390a38a0c51f1e7d5d515b04d8095d866b8ef06e96691cd690a6825ae92dcfdda05cd5354d49f2a01c6ea20e0f28dd50b4c118fb1cf2957e00608d7ea0839ba8c27fef304fb29f36ca40695c98defae48e8ae7467566a2233fab24ad5df834bae363d6bce1b4639b3abe7e984a9a687dcc63a7987ac4d0506a516f41b2cd84bbe7c66d942f650e936242f191d79ba72cab6c7e970524c73d63ee116fad4d8b1f8658c3c48871bbbaec31d1e4059be678d3f4c8b40ef9d0e3e17776068636b7a39dbec899ced2a8773a28e91210b236b4f2be6c5368a730c016eb8cf649561b694aa72bf5f58ae1c63c798027938e3dab525819e7f44c609e1c5fe0a32f6069ce9982c973e5224f921f25388760d4ca0d8ce0386266351ca0e3c4222db7e72ea55638ad22c74a57d53ce6ba3a5f5e1a46005932785d0851ea1a6f1e537d145eaa609d0ca03530981532fd0a04a6b4309b85281156c6e700e536682fb7daa26e40533394de5fb501527aa918671ce1e3a1227b975064bf41ee59a31bcdbc37082012d470e5ebf79d9586f6a2274acf5bf8b24ce15e78395740a66f87024500d82048beeb7e720f354cbcf8941be0cc2c609d510e0ad3157120ca590ebaa437218e8dc088c4a431911caf014226ed4ee2ae1836329a9c0905cf736a96e72532009a9e612dea2f3a501a9c2064d6e1ed02464a7382bea98ad208a8193a2360aa30fadb820fc416fcff7f4782dca64ce1ea177968d1c20d0276ee573dd98f8e2528b313efb26a0c05f7df49b4229ce9dca97766166abbc567ca1ff8c73a75c54dcc3f231ca8eb99049fedb3a21a9d00ca4dc0fac401105520cf6c3364a5e223e46f7343e8e27f8f8016607185a54d8e957af3a3732c018026fb0522dbd01913cef0f0c9cd6e0a53fb63e2417420d97210f664d205e212860b38e7c8d58a8bda31226d781812307822ed8546bdb7870333d9c2383a7d624b704f3ebac798e364dd5cfed960d770fd34088628fef15362ed32bfe6dbf53d62052300c9ab35b1d73fd6aa19888d212f6aa1012eef5624e31c69c387c6be3154fe218c5903586961eeaa296f170b295e23317a1503d4adfcf20348f2317c32469765476976644e05e2a9b63be363aeda872c73205c90b7a8d312dd8fa2439ad0c4c04c77239dfbbb5fa6f311327c6d241dbbd428bc707c72e41f77ad2907c2351fd02f47029f6eba31e737d182f8f46fee73aa2f7db0e6d130745d3dcc2576e6c8bd9c37eff853a285e36d41ec7dc7f7964311f80cebcb5266db0a48a9bf2e7632aecdc86e6a04124e890d03647c9b029fb3cf4a5fcffd2e4fd379a34bcc5e2cc52f392ea8a7620acd8f4c548ed52e5d9bbf4ab4794117c60c38a30022fe932861f62a3cc432d0cea07f8c5e2524a84d37a232e870a81cc3522f29df0b94de6c83547a34f7182069419fcc6e8c5b0718dba8d8e1eecba729f4b29b58d198854d8db401cfea8905fb30883efab5863cd17430d61c3807cf4e03cd75f5cc39705436e1139a0509401349ef7e2a3560a585d8e0fde8733fc16dd98163e834cf2b4e34f151dad2f09e25fcff4439469ae678e730f2c6402762e4d184804b07c88d7df017e47957423064aef54ce6a30d2577917cca79c116997f237c82e34c1c91f152cb25e6cd8dc38fed27922ef985ac604065c5ae5bd2dbbb0513ae2b6c80f001d71ab882d24e294f55abd4b4c4fd31c8960b97cf4226d1531baa897cc01584aabbb7a85c5ec55703922db3fdd7f5dae9480501d8b66ddf6c81bb8e81be69387c1c85d61221685b82db91822d8e8532b2c77bb4bad02760bca09a9ea341e8699b22c8d1986bf1813ee90c1029613c5bc3462e42a47102a7ebe14f4100c7815f4aa70290e683a88d9f4c1040ce706487ffdfa86210373d926256ccb3cc506d44b9bfaf77d0a81ec20f87c744691588ee7ef1c42f3bd44c755d4661369b6c45f7c2cf8812bca8e69aa0d7d3f817efe793e07bf904109af190f093bfffeaba66b6f0f3b51867ed1af86f649b7eec5fd7861e1acb9660b422d91bdad796afeb441837f9d7182c8b670c4b546031591220706ca905003592156d8afa63f8c1dc1fac676ba257bd55bd3212643bb95a1162f9143c9c436be3701ab6d8bc496e0fbb9dc94828c5885e8d045aeb3884cd08a1ef822224387d02a8960d33eb872668ff53e340d99ba243ebf8db5726dfa52f3b9521d1451daeaa2cef858c1a27b5c092c2f22ef74091d58ec1ffa1968b316d5b597cf909e5a5c9b1d9eab2aaae491ad87cbeabbabe42eeaef7a48a3762abe8f13d63867405ae6ff25d6f6ef0dd729be01391a8b52f16cdacd4a5d24e9f798a5db48eae76435583b5d7e44bee70b68eaa78631fb0cdf66e21e510721bf7918d8b85c944c4746b6b4688cda0da913201f1302df90623809ec4d2e64fd50d71124f4a04e420da47b44a02ed2e1d723c0f9c7a960426cbe15662b15219f88ef463b5359f0394580d1ad666442bcc4c69d3fad2939eae3bd6ec245157118d53c6c845caac80d328412355e1c33bb8f66e4b7dfb549773cb76a260f36505222af4d58cb58b3ac47295096a29d644526b538c819a148cf6464c5d3d54e2209e5b4b688f62a8fb3a0b3922e1d3b945a5eedb3369d76d731f536107d0402f9d5f60d11ae067bd0a6df04ed9f8f8d216cd290a7b23f43e2d6524f4d17e94f7bedf803c2497fa202ddfd76b2271e9ef3b791ba4a014330465f6d9be68b70560a3c4a5c7a19d45f8c2782d61a5cdfd738eb4fa202ef37737d0c1ec1ac51afbe203940c978aba6dec1832918074ac7289bec5573ab937697f42af6cf25065fc532d983f61034dcdc45b5543cf1345f472090e212ba49f2e82ffc54f442d2629f02eef88b44c48bf18702dd69fbc61621fea404615f888584f6f007afda443d564245029d55c8ec31f9409fad5a27c2c54e8666bf571209f3f532becabe16b7ca0fb34f48ef9c36ab7d5ca3a1ef4eb5abdbf41da1351d1b39b7d3f16807995a501eda3bf6b110a5bec5739615df0078663d3e53a73efec50a1519f015b315be005f9ae2474be305a4bb1d2eef89c83c4280d5b7aa87eff4c36459684c37a1bd4a66659a5da343dd9cd9dda9507b917c3b11ed9c4411d299675e9b27134570a92b9b68ebac8c3fa0f8f166dc68193da21f3780464fd6267b634982ca6da1513fe03021b4c6eb1237235489af974a600430d3706a45c3b0e34199961d209e5442c85ac62802b516d9734571ac34c5d6afc40db8a7b88f156c329647d4d0ea01ad1d4b166cdbc04fb8de59e43e4f25577c05b6fbd28cefcaef16dab5645fe08ac572f7068ad2359fc9c3bb51886b640782344e7921bad8f9922001cf0a751fe15f7caa441948b870e2ce9fd6d6c803ff7892bffabb0c0f48854287916cbb0ae71890aceb0d73cf0b1a03af6ce96363e9ec46959a53869cb954d2fc5d068a90359cfd7fdc86c278b1d574657e0ca4685661fa81d58744301358e60c3a228626c4ac8894569892d2a8f1c71cde57a94b2a8a88cb9a2c06822539e1aad35d062096f816e3384b92b5f5433af902a2bfd7f97b02c7733c8f8f8af1e5f8d773326fc20d775ce34e9047c4915a725607157aff08f514d1184ce6928abb6b0d39fb6b569d1391fb68c83ecab34a7ebb76438db59d2ff6e567a48af888f60c27b70c4067b20f75bc7a07b5cbd1655a768de92ee5b2f5476bdd815a9736158c7c757bf4618d1d18d2efc32fda7f3d083354ccef84b7a4d1c457e9c5e9ff01fc0c2e7cab690277c5cc5e72d4ba03bae321472a89d2c54ae3cda7195a51b6168718c23ee689d4cef1d765eae266bf5426386a6af8486e9ed0b79bd66577de2b69bcf3a6b7ba12fd672773a6b6986d7fed0724e3862ce34c2ff2239903a2498ebe2e652b172a2e8a6314608ab7d5ea9137fa3223efbe7c04a4f23f0cd36a6f0529952c25951c458a83ba0b9de9f4b178258286bca029108e013362bf2312ecaa5b12a5a5c3b6a1f1f774d3dd4e54072f2f65d0e5945ff792e05173e191624b230da7a20aaaa514f8e0df5c213766980d38ed5102568916a877417dd5041c950fc5c88ee898473a10a0a342f74fa4e832326caa2a8f16a200dabd671098a36a9091d5f269ea23c23cd26702cfeab76dd86335730682f54d146dfde1c2ae92cd4863e4301edc51afa9e8d7190e5fbca1580053e714542b7248f632153d4954d3aa1c597c923b4252aa841bc45d124dc4c8d54bb48e89f3d48c649d31a1a988146fa72f4cbd7253f081336d624b14fc2b62b9cde8696bf36d480e1cf62a30424946c06aa51d93d0953aaaf543db15766020e4888014e6aed9eb87084cf80596cd3aeaaa910baf60ff55cb086c1a1a5ee91961627cc45889c3990fa842c6c08c76dcb2b9115bf0dd4c34f420ef6d927f971df43817d7dfaa2a75679756ab9a990e22f7054d1915b6ace483c12ae09b4e09964cb7724e92c928c988ceb063b1c6da6ca599e3aa1c064a6f00d6c3f39d6837e5b787400ed8fae4067552115681e3ed505d4c7fec01b0cc4aac186c962d010ba1a5c972e7c57430a228a4487497f6759152bcac87570e4644657bb559898d272fb04c9cd314c8700ad6c4917290cb58d34ba989896b5164291dba324f5af2339d7d9b825f4748446e6680f0db4e3a7d83f3175ee6a077c94801272c3452e7c679b2bc9e28a87560ed884f349675409aec70d5c53fec3f980bba6dc8e681d8d05b8c42ccae51a204e7b7cd8cf38542abe43fcd733cbc172a0473edf11aebf7be106b8c0f9e5bf52f14ff80f83a164a677c2cfb016ac30ae28eb4fc8a7a5b2401a50ac882b223f300e0b22f5fed4d99b887805e581814265d7e730bf1e57f41d3865bf842b4ae80632e004b1b8a21bde42c776efe64be3ac68d5aa043283e1390b21aa759d57d89e817e7454f95ed76bebbad9426114963eb162c92344b3041bf729bdbdcacac07e714d16b775d8839cb432bd9541e6873ee7236e47ee27bf6dd0c43cd01b40703b0b03bb243323aa34df40d213a0c3fa8caf5fbb6d3d04bec6e5ed2f1b2754e5be961b5e98c658fdea053561f7358a5367fc9f73f93161d0d49807b20100dca7da6ddde64a9e0f15e92b523f687a8745b8f11ece51d37dbd11a094008ef235c8326c7bf09e262759ce755f7936878ed6b82fa57367a53e243977509a0602b59b14786bd0f2ee2b1c909cd7bbf1f597e9fd55a8d5b6d87f07b8602006c55dc2cb3c3c03c1ada255117dadf5064f43401ece3c8c2eb69684aaf34914793ee5dbe7bf3a5875349daa6264b55f9e3f88ccfe567b68d3220942e3e3d54327fbc9329de1bde43c4f4fda2f5045f51ae7528ea79fc7be871c32a01da368e929bf31b7756ca174b7203fffbdc1cc0fa5eb7ff71f066530c5b8154af1960e66db0514d0cc0734bc5e7ff0d94ce19b97f4441d793e9eb496f480c4b29d7d2e4efb862c5ef4d877e6795e77cc39e6314a42e160ef51d3f16e90f75a9648c8a4e075135e78eb9de38db2fbff5cd203ffd4ae2304fd3777117fd4154d618579dfd2955f5c88beae3f5c26b892f6bdfd811c974c7210b4dc83260a5d4fea4566b5d6371b1fc2fea5a15c801a16fdf275e17014ba65113c77d79f7ca0ec170507da136006220a24dbc23949bd644c8a1b7d26fd40c507a0a328d8454b55596046ed3305bfc22f81ed4bc3cd88fe8b0c00265f7d2d83979bba4f33be987f2af6e6d2201521a06d3e805e1c7147237b309d9eb7ac0e9e93f0b833146d6798f3c8f2c5ec742b43a2413de78b9051f8aa22b1608f247d979663670a3e2ca54d187ece2734413d79c79e2e74bdaa9dfedfe2b9304bf46d5d3830a99edb9f9ef76a086cb06816d66abbe79bf711dff5bdb72d2fd4dd7dc2de0fe88b06dcbfb614d035904d50cc3ad799f7ddde0a75c7e081c8165c29b608d816840a5ff49e66aeb68a6f482b429358a8e00ead8364a78a441074e3183a9da92466674210cf93093c7ae62a175530abe7337c0cc21b152b199ff471071dbc1fe31d150089ff7338206fd9954711fcdfaeffdb37242fa757fbb192430e1559069b8f39dcab3945eb75b5cafc46c88fdb865366efe5fa516581bdf7301817f3f55e68c47ffa60a4ab336aa57e114bfb83d2e6fbddc5a1d9dc9e67f16a2fa66886cfb6a1b5ebb5ae8abd8a1e6824b08b3a98f9e9f8f2c2c20c8264f0a24334cf3380909082e4e9df7df4f387f4137cad1aefa28da7f78bcfe3964005225450bc522464aa646cb295ec863c1b06645cf590de5b344612639d96b73c30250e6ad691eb214b07b5859f253456c9fe757bc9f38b6d63a7d5341774dffc1d004359e88987ece05989ee9ec2b33dbd732a7ad35a267fecec16de07d321e16241ded0ba67d34b99a11bcafc577cb49ecc3d241f57eff58ee8059e57afeecfd766232a2c18cb06cf22d5323ad3708c77bc7fcabaeaf94a2ab3ef4f58d7626dd513931f69379479b74f081543ffbed38db4a61bca6c9c1cab30e5e6c07833f8067b3c92e216c23318d7cf677662da646b1ec91e9459d7367c03a59ca7f5abf317cdb67840fbb78ae39ea6920d49aa93449568a431686c9c8028ce29489ef977c7b90fd78fc58f09b7403f4c0430075998a41b7aa7d18dd335c3ca0a47d74f109bb9524aab6a57cc019ccc563bdbdc5d00ca097cda449f2e92f0459271939484aee83fd1d51f6a7519fc03945818aaf835a1ab726faed994b6b223a8341ef1d09085513872a2055b86d2eb9d20e986cc9a2724309838967271942bc78c68c6fcdb55953fd9da2e6c97d3791d3c1942967fd82424b6f3fd4a8db88c4f948f385fcc69934d1a195091a518b0c7e8fb35c60ced3fcc545edc9929c1a8ca165f59cfffd509ca85ee81d6aa3ecfda056b69b9defd0a19795dfd83c0498831763c29c8316912a55e45495cfd1d2118a2b6efc117b744ee1f29711a1c754660e570429c1829a5f8869344634a9d6b05b6c9644b61aa8e94985ea5ef59ef5a67b54f60a4c450b850a1b22391df37626ba5c8e2c0f2e9ffa14055513b47266fcbc83b94153b366864662646f9068d32575fdd73bf699ae90a7b20723672180492b2ea3525861ca0e6014cf98a8c9040a470aece911cf3192737d042ec851c9665b7f8bcd830040162c594b354234f1a1604ddf8c3105712f4703310f4bf5efed90af739c85e4bb8f55328f4cef5f9fc3bd2c11b6a751bdc96705af171f228c473e2bed244103ebc02682c633b3188389cf8b8896f0c847b2e3e460dc3aa4ca80f52812517fd9375231178dc73c78a44ba8873b038d030154c9faaecebd50f1da10f1eb7f69729554025e88dfe9c1cbd137ca9e1ea4aba164863ae4b34dce136292abb78c072ad94a57dc5beed0dd8d6401f7b527722b7175316165f47b75c0b072f16ccd768e90c8c45278e3a91000192abcffb4d21e8189852f8a3f949282b618cb34ad5253d7342799692c3daa5ad070bc006508fe1af4b74ae7bdeb856141f93e514cabc3c834c5ca1b8755e0b7d5ce1c6ad6b07df4c2a0e8982fe91c5c2a67643c3c3dd2549a573bc14c6b22f87631dc01d4d3a049bb68de86f5a04339d5a7c9b8ccd68c7013629a2f850cecfeecdda5f4ea36449785c0c2a5bf95b437e873a5ee7fe6eba6436eca1ae50c1b03f47d49325d2dc40efe8592cddb7b3e1b45e3026aed7aaf5e48da524b0ecc9ae23bdaf29edb767f5a26a331bbff04ddb492f8a64e2a83836d9ce136910aa436c85b94161d95e43e1658323f45ee2229960d7e7dbcdb3c42ca206850f93c62a62295a7c638900b107c37de0811c8840042eb0a412ebf9ddcd37589a3303a31c373e45d79aea6dc1f39aa54dea88066f3fc6dd96fdbfb359b780441d6cc55042f562ca2bfae51fc854271595351e4b8f33ff5676e362a6125ae6948933698b9b1a720353fa6efb4b1b62b3edf9da12240a9a401b47de63744d0c05f070c83f0246c98b92ba58facce6bdc873f1e23817988da1232887cb26115f65fdf03f32a551faae98d1beb2bcff64390b57d82b671887820715b043cf87728889ea8040e0cbc17f8e1f03d6c2eac5d8c1de7ef8d98e645d383be6c835f03bb300d58f102d629c47a85f8777999ce2c34da9fbeefd3659993b93fca6d8013323d4b37ef0934e7a53bed3251e78b36af1fc4d48fe3b09f6747c1cb5d60336e998aa2fc593a0a9041593b79d419f394d8eaeb4cc6981c2c7d80660134120b7091decfa710bc3ba99cb92b8f0a090e39aedfbdfd6ddef42ac24c5027b50a1c4a8c2720a8e51ef90518832ed4de913b6375c3306b80a7c065e1844f8faecb5ea5f55ff1e08977741f0e9e2fde7be3c0108e5afa93a3c70ad72163d4ea97027c3ff2aea2f120837f41f616b48473b79df8cdd0eb8892adc2f1b17f37980e326e1ddd21085693bde25b9a3ddedc0c0551af368b0b88f98893468708738500c1b15f5ace015796b38cba948e7f533bf719c3ae414f95c39a3841e5430f93524c3e38f2996fbe93ade8dee9fefe89b8e1e2576d135137f33e5d669ee9a56b9c61e05e97e61da3db18ae7bdf239fa96c8b4e114b97f02f430d02f87ff370f51fb3890ce9ffc9a58242d990a93e9584d2e98e2d9a473ea70b91fe43818552c6c2cc5c1a2b02ea354b27cf2f990ab36b0da8b51076e7c3b1639c7e81852c255b705b96df329941dc49ab8fd475f2549125ed27d70f306edd9a4eff8b79109506d9ba3709bea94fd03fa4b7924dfe3399aa09bc7342eae1109cbb5bbe4002ce11e2b38fad73f2abc6c86a7c521a1ee8cea929789d39ef76624e300ccbf422325fe99b69efb38c3fd420881c02eb3e4d43d3ccd6814721fbfdcd914a02f2de106320e7ada640e9668a54b5e32480f1a9da13411272aafcf202d6f3c96578c111cd4acc894319b27b94ae244d70ac21740f12432251071ffde208ba19e4986e01066f70ea7f67d8c8cc78c34f0378884362379fd124ce5d979152fc22b2cf4aa630d5bec3020204574dc975f7a1df3fbd243b0e934c6e535231434304ac4a291f5a2f4e725b0648b40ba8d9843cbd87c4f471980dcd35b18bf4e09c9e21852be7de289303183e7575f38444f058ecc4cc6a491cbb3bc0c2b875da9c8c05d66ddb09e4c2d8b5cf6efb1dd3dfef0768ebed33a9c4bc832b2239d0f10062cda84041b8882da16a23d1237cd4f70288638cd85330e97aaa90800b84f05eda706f012cc442f3dc3beaf8088b1f3145ab54c3f9a9558b23bd2f1ea4883bb64e8ee5f38cd25f566ff570988b7fedcf25c69701b1f48c7d96c2518754996f0fee2ea6f9971363fcf3cb7378add722801b5fb731e0a197e04203720dff274eb5ae75a8b91be936d5c2e0d1d78acc6c16dca9174097a85ec3108a03f1034f1aa45e0022651d678d0cd18a04a84d43d068ee2566f452325662d274c2e67900a85ecc0476f1f052f9fb2913a393f3f2c0a6118939084c9b9936134881acf77de67f3cc4b41210d4c984aaa7c0b5384c8ed97d9e61be5ad2df0e889c072f84ce64a5b875e32a7ee0403d843dd2f56613555f9919d3078cbad61f059770655b5644dfe3c05bbaab56b21c2a20bd15dc85fa56e6517bd343097f6bef4bf8e710adeeef020051eeeba7c10983d8e82d474774b052cc0484504a1a831a1a9888c3f7128251b95363f4b4c07d5a7e89ee555e1bf2b1c4e9c9b030715e849369e8c3956d9c654779ac06f07c1ec3879fc76a401a7f8aead108521dea63a252fc0a453387c2b2e8a5d9c267c538ace8fb66dfa9b21c6dcfce0f9cad69f2f56c2e15c35ada0937078fa86d689bf132f599c3ca34e4256875ce3745cc8bc7434581f9e66683e999369f5c75930980e367d99dc0340184c6771f4d0e5a3f9c7fa2d382716d2298c3e6029208c50b3e73b32dd4a0af8dc71ae9e85fa03c8ec81c56cf369eeb60faf7f9c04f8a053511dc943332018372729fd368840b925e73777a7f177018ae09ca095ac19eb84cc52153fb4a61384cc11059f62a2728a827c5ce390b280cce72ced14df6eb3bf2edd9892b42a384dab92a9e47f84ada74a2588d73a0e0d645885a81a08881bc6da7b89c48a16f042e18b07c12069ac97be7bd49740a5e61e918843d7ccdb41b41073670b798a61e7ec5126b60bf443b768dec77cea7f0ee89f921f4e20807f58037d0a9bacda27c10c385889bac0a0838a6ee245f69be64b956a368963d4ee034bae244579a5023b7f34a1c9eb63416204ea38a52d240a0d89d6e9ecfe38661209181a88109daf73e3e0d1107921c9f235e0c34f12db0134e090bb0b7c3f579e3e05caf931f31d3ce2b905ecb39131737f3facf5b26f4fe7e6b8c04c84a014864cac690f9189fcfd25db6f85b133717b483263b4d36e59f34b3a3d9103922c5cea5349db183e881c3ba97d0e5091b27a9eea32c62ff8bde0d526c633fd08dfe2a581d9f764ea896c77acdf4e0790d942b9aa8993ddb8e923e2d6f5c4109361aa00579472ddada7bb82390c0b8eb651ff5b1bd2604fdfe5acb6c4767ed8e057c01f34ae6772e8e8b23fab3cfc661474624fe477f4960cfeb884fa353983725b2b2b4e94af1a2c939f8f24ab8ee7ff36adcaf33b46eaeb29427c57c49ee3431a027b635433ee893538a2054db5641063858146a0f7f1ef039f70c0ecff47e31ffc53da337a302aec793ec52a2c510cbd57f2521645fb87a4fde6ec2af2958d13d462747a9a5a86fa9fa0b99fef00f829820fb3a38f2a23c4e38a468be9683ea87429b9abadc38c4427cbeff6b5ce6db791fb633f888a2ce1501360d121a3529f1e5e54070a837c9ca44efdf4667d6a7f2f235001345ca372c6eae26857ebe89973aff7b17edaa29d05af280ef3bc3bcd378f90d5cf7ada6955dc5ea75070af06b4b788d39f9b05668e3413db5eb15073d03f8afbf43871f6e0214f9b525d374b9f72c3bdeb6266c88c5c92779b2a27397014158985ccaa84069a064347df1c1846fac4d170318f5be1e5eb56cc61985bf5243c3885e1cc1449ab69f31c9c261a61dd8a2f0559c7bcf9fad0cb5ec185b3c944aeba2a9dd3b8cd83d838d3ac641599a72c89034ed58d113348ab64adb0d26554cfdd40986c7bf7e1f8ba2db85d7d5d3cdc49ac9e2f989ed13136a3f01a7ae460786347a082a347a9454141c718000c458a4c5febaa45a8ee4bc9eac82f31602a47c8c13d94be9e9cd3e741c71a6e2a90687082ff2ab3fbb5cfb09d95ed954fc2b02dbfe41ea90b06cac6a69dc218ce6c6193eabe4af365c0271ebde7746d47474c385541a698baff630a7e91322a6e425241a5686d6f0a21333fe0a7e87e1dcae29fac9545859b7e91c23ad042df0ffec48b03290a4f78f080e59e3011f13c1325dcb1af514eb19cf4159741b4befe620486377f5f8b0994d19991199a327825d5ffa319f852522c45e21683e32bae8e1304b46964562f0944755d64b45b8e8ada1ae86993fc95d9aa179bef28e74c30e2983628a85a36f4796315f514894bc0fa33971c25bc3fdef25ce4c605d72fce41993991b62f35b74d2c6c9d91c3463a35b3f384a57b6468f05c63e06b711cb34ce621d7e989ed212026c83a9d71d8fe6a211d7205f428ec50ae048e60651cb8b776b75a160b2ec371b34fca664348b5151b1b52a8fcb352f4dd2c264aa13c0f973c62a28d39e031ca875c1f0413cb27369a495baf622b519082946dd111f3256a5a4b316768a9d8da1b9e6606e279436b4db6781f676e2e51f0035944b83b796c4f4150cbd94664161873cfe8a8be4d6443f0aa7e048a08ce2f736e23df47550245c63c91bf0c590b3832c9acab6dae5ee7bf3f210175e4c9461f75c4ab93d425fc63387697c7d70ee5d9a38abbe986a676bed2fe4d2750fec5607418ebd0cd82e36d93108b9a8e55c6ae0fe34dc6bda482e31799fc2b095e332279b7dae068c8af9b01069cc3d1bcd928c1e3c815fe98e4077aefb810e66aa40c471f68375e60f8a496ba81edb5692b8575d7abc8c0b5a955dbe6ed69c7781343a891a59671f48287f466ec02e3bfdf9527290d00b46bdd9f84dbf3b703e55b97b9f72bde3b1dae0f419fa40c0468fb727e090600ed503a0162b711f23daab2d8027c75801e12355686594cb943441b088dce43c582f53177a4f119e97c02e9a3e6155204199c80832fef00b046fafc8b1bf4f56088ee6077f6e73981ad5a235adc22d79ca70b5bb6c3918956e8d27aa39cfa1876680302a58584627516896c36a2666db867f12074276f985ba1267a696603ab7aa25b2920b204c621ae740d77a143b0a95a6d264aacc87014a0610c1e92a695daedc44192b7c893b4c16aacbbb9e12c9c3f3e722d617f70f4a5af6df88003aa8f037645b70c9ad9802e788223153dbc3b115ef5ebc6eb1f2536bc8c9fc190fd565150ef4b2f46b79c4ff7aa256d8943c626631a738fa1eb6e7327a281b7a4bbb53fc340ffd8ab47298b52547a04aaee5d1106c81a9e9fb1b8ffc16d8a40d0bc83650e42485daeccdf108f363a983322a55c7a5133483af8609d56860e1ef5c78fb75e05636a90a3641366dfa081d836a88e440416cef8875504a3c5147ce98cbe05528a1e8e229b611e3bb1a28341e47a196931ae1fbdbba9a92ceca542caf4db7ad4a7dc99a58ab8aa0d24bc1429162bccdeb9cb30530b9e5cd3ad4d1618cb29fe3ac0726c71ffd4b19cbf61e1c8e3d0867340e6ba57340502207589c7dc237a0409bf1b1fc6520b372e4e6965bd5def18342a8e8c907d2abffe08e5860ba01de9a2edfed52cf2432975a3eff07bf3679b34e872138993a4e5310a2b1fe5bd48bdcc481a8bb4143389bd73a7e7816290b33b9b609301ea29380fe44ce498a671954ee342e55ddaddf8d2cb15c4a3ed82ba5c5165854dac4f7fc82f82c37cd77022438a517fd01628f515e4c872ae25d8af1d07310ae848c36651d834fe4d1fb8f68fdcd2acc2bb2fdc9bead38713e9668b520900f531cdfd272ccbbb1b72e6e677aa66bd538c4f82eb3a1d5d349febd312dc577458ff8d397c3d4883e21c8f6a30cc0918db0f457900cd49523b067973d599de240ec2cc72c0b7ecf83c1ec4e1daeae01773b839fbfa7a38d521d75e18d08799eb645917a6aa95e548e0d19e0fc516b1f962da368b9dc33585f4c6243334d721d31ec8e8066cc924152fb52297aa3099da70d27aea79f482559c47d273d07673e0be0178d90800bd57e979095688ce5380d063713cbdbcc91afd044aa169e49483034fa0b83effb162071b9575b18da5aa792310210cacd5f06b5431d8392813aaeff41b4b52c0d1b8a0fea18b84e0ef542aee8dcb2272744eb6d44849e0a1724120f39c9ce3f5e92ce72f78cd9ded2eb0368dd331e215a619e55ba41ba651779ad324bd55b3eec020df8b3c5bb073854edc765d2cb7b8626506cdec7ceaa61338df51e137d69575a91e9d3f9a3a1bec997cea751080ca195b19483ebe8ed1ecfc2c7af3ac0b33693f105a12def795cfe64f0adaf2c2a8cc04194d6c88351fe077f0d10c8af5cbe57d912203632bdbce89cba418dfe6a2876b287bc5fa8dbb21ea4bc167b19c92973eca612694972d2f9321ab8b50269c3ebef7b3310204942ab828a2087744b445c042eb69383b451454232fca11d333426ee9d234af604529e7899048f6a2490ed8a78faae832d20ce3f3b31cd1c08eb816c8654822aa6a7e53bae05db4ce2894667a07189ece585c684b0d66ea7f3ee24a565012eb132af3686c9cd777fc9deae96c5347e292e239509e9a0943ee7e2a677d0bede172fa9ecc2f940e8038c7e04583c1887021f6a8910b9bd40bc557f6c729dc6df6b170075f0e2df1a62225ee0ea55fcad3aefe7c68a56f73408fc718f0d5a28aa3ddb8252e50fbd6986788d8015e365dedb90db8873317fc472f6530aac84d869974a0cc69be77959a3529836ab2cd5ad13be0b7b99949ccdbc320cc3b07df37f784c5e57f4a1fe5f38e3758cde0743e07c7e419d97a26c9f2d0503eb98aba575e723ab0c22f97c7320c950f9f2ca8af1208814f391d1beb29463fa68e3e5fbfd035b745477ec012c51cc8269d638689392a2ba7e658bc84c5bc8d0a0841843e79795bd323fbf68259b0fdda0c5c952736c6f6ac133f5ae4d5472c9e3893ff81473ba53904506a34640e3f6b5cf32719923eb8200607aacbb821d97fc6bc2abe0956773d1f6c6713ae40cc0a806360e838d5bd9d515ad6424a6ff3fd04618fe36bbe06ff8ea461fa7589abaff81ee8d4507eb1abbaeb771043d2989acebad62c6be310eba069e57f0269b5dae895e26498b4b14bae39fab4625f31739611f7b6024a43316c223fb158db6b13b87b21166b7d5e6f7606c61a433d7d64a173c696a39ee79f5b3b777e20ee67e9f01fd73e2ce587bf0f6d52c77e4ec22b69481134d07dbd952217d3e835dd220d595cfa7c24768dd74c8a79e63391d055e1aa9e8ef04d0291fc594d8cbaa596df39fceb4bed1ea6a474f4550e15e377f9c21a6eb31a97c682377ecaa5882fee86de1b854dca73b0d9233923796d3364fc3c743ff62d94201cf0f30c5cd2370a7454d73489db1c13c76c5c0ba8d18984cc1515e005d13aa83f23a670fb62650a576f4c9c298357085dd8d321660a689aaef300024440ed6206be6c034dd1b63f2bfd5e192e592d1793308e2d64096bc57736e9b4433424fcea1120f7b0f90cc1c30d507e6215a81913301e22eb1cea93af7fee4621a287a00c2588208ec024d5196ad82f34a8317457292fda110b7fa235a7525e758365f56738fd180d5e1b48a784203e0554f30521a834e837f2bbf44a7db6b275be2e2d185d9dc7ed7cde7478ea523ec1b987edacea7de36a9728a8642729adaa35c093f22ea7aee4427e7afaefdbc27487d06fb836ca7c7944ee6d0041cfbfc9634ab6e2ade2c13a823cf00d603569a3d0c8d8b251688860e9e225e300ee3b5699d0fc69afd6e6a95292304ac4152311359429853824dc5703824eb29309d5781aba626ed73337dbe189629248a99f28482f4157be885429c89292ba5ab7b718c098b47bbd57928c8c0a1261602ea3e866d3113354b00127b2d4d123dba66bbc728f7902440a3d89e9c4a0ce39b2aadf6df50dca410d5ef42e3c19fba01650c2b1c3be5c5ada3aacaa78f06c4f06d6b4bb887818dba24ef35e48df618b74d70cd5f32216b07d68eb88c621a148fca0df027b9234fd6a8dfdd5c31ad44d622353730af211761c518058c379ee52fe5e48250e885ae5d70175d768f9b13e40e4067bd75632c2ac3dded4587d9575b98bca7506061b4d270827ac7fd859cb75b3ace21890ba35a4d0a69e4738beb255d10192f64bc13ca2042393fba8398b28c59ef80245036699da8fb95228e29f682fa549c4637b8f2276250e923faa944e10f947d7a4a821be89943fb9f1911fdc38b430a3f3077a4bbf5a8617e34f2299886a9441466cb2af85765f404c837861ddacaca14c32e31e5e5bdcaefa9186fd774bba26863fb2b8becf7253ab7ba019541520c81b154fc9fadbda940f61cf42169cb27ce500d9f0edb6f3dd8217e0af71b662e4426d2b3b0d7f37e935a94d17b8a4186ed544d6879ed22edabf946bc3a5f4535807f7670c007d52bd4f466468d947138ed1a847187adeec84108a1ece2b01e184840a2a24b07fe60cb0de83dc76a4cf99e3f4fa5b4a34b340fe9312bf305363bb8d97ed4973302585c773d97e4374d0e0d5fbf208d716a77d44d31497f5e80893fc2d044d48f91821264968f2d9f6f19a2a3be8421f4acaf6459b455e5d76610e443ca66f9fcc659b4304133d5414a8bb875452984ebbc5e703328864bea78478a5ed3897a296b0037d90d151db3f79c19c64f14adfe8539a5463e2548236085075002cfdc6a543719dcab52f9c71a67015aa0a0a295e5838965a109e79b2975158e2567c0cfd11e51fa9d3ae9aadf2e1f9bfc76a0d4968ae2ca1a2f90fcea9689578b8db443086fc17ac09e411eb0c25aca4ea9c58fbe81ae7c62763d02026f1c53048fae4135e7c500b5ebee3e599e29a735abcf5e8ae43be4878be066f9c37f96c727c65f7d525ddd273555e0abdaad4a84dde0f3c27ef099b872421b1dd1dc53879270dadb699adf06ad37c533aba059963a73dba4ac9517ee9e526a47282ce14e9de4fb2195fcdbafd5cd7be8fc008c198ea2f2c02536cbfa0c7d9a80280ac6fdf6743a28956477bdd874a1ab2ddc4a6b850ae75d5eb9a10a5a891562f62eb6d888b38cfe879a45efce135cf1397d004fc36a1a1809255c85c55140a610900de2a9898dc490df6e01c189d5dcf7338e2851455dbab25d94fe88def9aabef3334ee4a1abef1bd011b49c733da7bf1cb1111038889c82039cacea16ce858002eeed57ab682055cb78a514846c29a8387c863e49e31bc7c0aa4e1b83e52e1e40de5e3eb52fa43d61756022e82b26091c2c5171746ff28b227f119080392384a55764e35f50f1518384cdde3844d90da4b99eb4541679e6d063eebd9b7d75885fa6d73442a94be876ff3c8c45e67a70e9947993a756a445589a46a3e06bad3434a89519f71d3ea63c8694c583420b4d498e899637102b137e0e30f6c2ee779584a9655a31bbfb3838ed5f0184bcc8e1d74282245bf4eeca888ed708ecd35316a74bb29979996000f90a6e2e68d6bc75d689453be5b9d13ee6d87bc647f52fe98c51c1950dbbc015e70be8b4f80bcc82da7d5bd0dd9d29f4726a793bd6735c9e56c7f3b233e490325c5ee23a66441abc2bf3968f4e712f115a154576090055a53250d52f0d16536cedeb34e389112a25b1a0cdd0cde0eb101a37dec22546157704f411a7ed00175555aead7f0da932532adf7e8512a3dd3f5bd911eec74a1a2acf8fc699c43cbe7c54490b88d3ec4f4cfd08759d58c27b380d05f28b6d215afdc7f79a745dbe3553145e300bb017dd2f0a1d8bca99b2a939229c68cae867de88d8a17dc0d26ffd6440fb183516b384fcfdbacf27e2d0e6fd05822c886f872764ede37ac1005405f918b590dcc916089fbdbfe3266f7d0e8c453fb74308a736ca14e46e04e1645ada74309ea90cdf9e45fcc2b6b80fc9e420dc8cb90ab4f4f83655345fee478cd8b020f500be19eaa8ee3e741fadc57a06f81caa08204d5f98356eb94bfe786eec711683f89a047d04a6ea3217566f3f25efc9ffc39362e0dd5e37260139f13dceb699153cb33a16bb246ae99c2cc2ff6223b309ce204384bb946a06ff82c4ecdcf5b5ab0e8cef268af51d8d4b9a1a46565785c5095071e48fafa3875e2c297a01b04b6b8e2bb56a7bde31d60462e1e4e28e4dd360b762b694e17c96ea150339b417c6dc70166bca55cc2639a71105366537b56d93a81d1594215b46fd0d13c12b099813f5a176849660b686f5464995050581da69763eae0871cee2dd0576b62808af46b46120d2d572d049c2cc2cb90cad13860adc57c5160bf3c1265ec28f1741a5631ade49c354cb05fb8bcc2bda74e625c93a4e5a35d52f48de12b4475bd4da1e824207d520942d1a684f4ed9e6ae958faac307fcc16013c9075bdc351d7daab10d728efef4dd1675b94c4d0aab920afcf7b86461da940a8856be0485a6dc2ea3d33e246d8003948d2f0a5d439b7a8727988d3471fe89616c69bdd9ed831eb19ed9a95be1d2d77d6970ced3557cc0ccb5a0def14171f84d2e50cbd4d6c0f36782089041fbe7af430408d1579ee94a443aed3cc782a96dd1e8e6c6460b261026f4d41bba4f93a14a18af6ffe9830f2cfc341330626685c17b10c592e7e4df47027db918bfbfebbbfacccce51177392acc77ff964e1698a88166408d18600a6220f3b47a97665e2d451a2e6a65eefb6826fe9fe88d7585177357aa4cc9e27d7791bd1763f26ca6db1fb0d1b837a3304ab5965ecb647f706d71f6272977bcb7ceb8e2354db9ed5bdde25f2ae531848fd707f8f0ba7adf7f099c7a454dee7f856ea1c31b06d606ff97d6e2454cd366cd78bf04c6f22c8b29e440f9017c2d9181253d76ad9017c0c7c9f233f404c97081e913b8310e937c9eafa57d53b85d07447a81866fb17855c8ab2b8781466df59733d46a4cdb4472d77cc675bd2f074d9dc80d7b06319f14f4462c48cb6ae27dc625ea8f2e8ec99e9fa95c14a7b0619f82a1da97092b2b1527b77aaa7bf222358e4012f0cf65def2a460ce3213e77513c405587614418f68e66e824874803311df3f999ab7f404f03c9230cbb6773e8c8c4cac9ca0c603ad1c453633f902161170f784ba0f3313e311d89620cfd1fe5c73967012b81de8d1860266bc739139895e911f38ad04664f7ced96d4394630d4d28d23e40a6acfad442ef3044ea219504bf1380b1202b044e85027ba2f150b832165e90958ca59940ccd552aa7a092cc85eedf8077c908482494faa30d0da37ba05d8f12d69a0e0d1da25a451dd4720f0d25ac863a3d486be908a1d5a17e35a8aa0c89a0903af060da1efa8ec99c4065d726a22766620feebe9e9e74d7fb2ff917a279ab3e45ba2f5906f8d55d518fc00370f112a370edbf3f8713fae71fb2b2621786c335d14a49f338e75b99d6e514616244ec244a66d9e25dc3794f4b5bb52f92e42a309af3aab04ee66987a8391e746df02c286aa1005ffc7c1371e1cafc120eff56494869567b6502166b0293b089f3066603b042679cf92fffae2ac317fe16bf4b87841b23a1767558bc9ccc70c8d050ec6ddded14b9683fd28a783198366175befd99302c25a4e886b5f2de63122df975a704e1bfbf5b97696f6905cdd1b2b2035814663c59039b23ad60a8788b6816a7891e68bf12f2e299deb4b401d151425bbb06b4005d4f8c6d187399adf053163131fbd744851aa9deb41bf1b199cb9db9051d13350d9f75daf35e52bcd8a79fe505fafc960f09e54f554b3c2c55f68eeee845e9c28506c543c7fdbd95211e55300f1aa41c2f45dfbf337b2167c36313e879aa8ba21ea14e38da60284fb37e68eb58fd5b57a2ac205976344fb91e0bcf15d446bb32d85f4f70dbb6df52428289a5172134c28f8cadebe84984a4a6c82017f34a780611531e3e1a491fea603c54cac59924069ef6cfb800d201266c45eccd40e93adca972449c8a6672337b2c254f6e1e1adb857845b4d3322c6376406da41e0c174a002a13a5f0738123ea8218a1ca670d87f1658d0ac263764c48e23c3065d564619e05a68632052688c552a6f15e77cfc9f0652cd214f9c0044463460fd4b562031d842da2e40f58584614e896d0cedec8a69f4021aab53b942b259b39e8c42ee3782165f4dc43acb1332fc580fa39df89b55ea0f931238a557c5b6d88ec93291ef0eb721ac7d7bb33c74443644606699822e74b32c1de3fdf2530cf44cf1d12cdf76460be0cf8877a50c075648b7303eb9d911e959511b026ef40f213b1cf5f56fb3bdc44b66a49e4d627ce35e7e52704db1a19fac9350fdaf64c6acfde2c9b0633e8ae8b6be2ff36b27ac43cac1cb2515220e5a87738972f325824fd22e6b7f8885c69e3042b7191d61b004364d89ed3ea65a1ae340944628a3e6f90c4aa194bb33730354d7711b16ac8cd7fc94c1b6589b151e3e0d00d25346654c9103b9de31166288828e105e0ee7b56dd7121542ec7c0345e9014309f9e5f86dcf932b7892805d856171551ab8840559f3e7f0d24c63d900163cce1f65a5873bdcc6546902a1957a68bc1407b7e52e90cf120ca87e05d41831339834858ef80db09fac0f7ac672761bc048554d8f1f9d4a03020d27b1af27e3b2b2f981af778ed674edc8165745239177d9613f72d6e5d65875ad5085121fa920b6769a6824485b8a73352f602ebb4ba36c452beb37c52f0f6a8d9fef3778f531d8ca712100bd19d36bf71ba14dcf2c63060af9c3d73e58dd67da15029d97a6cbbc746a84e8600913b1320f4c86dcbf278dde947f9052c4a865af96426c0ab3629ea190de96cddf59552e86e151a200c5ce043535fe10adaee7a537a9cb3ebfeefcd0befee356719664f43f6fce44de17865fa44b430739caea90d49129b841e6b9840faaf7bf319c6b0ae631c28764c719196f3454a2619ff5db129616ccd6ea684491e83e2ecaf26d1a0c00203de21af9cef4509b79961b0c3836889ffaaa7eba7fe8588d691276461612240477e115add24f036438bbfa9d2aea987000cde69bb6a72fb2dadf4aa66ebe3892cd88c34840f1e11f6d92a55d90306dc44d5f2fa5810f5f76c848d2d07ad793b66137a1a9026e338aa0c29d87e1efa095773fc58f4324c506bcad8c3401bffab14a606f67dc5bcebe14af28a5d94c2cc2a112a4ff156a2a901394fed2889d0b1f2474792900dc0ce619cb9fc681d01065484a8e21402476a1cbb48ba9e7ff611d969f839b9259aad21b3cf08f6522a2a72fe6e246a9c41bb2c6a20f28341712ac448b9ff69a352bf0331921932c4be73e90d063f543644a96f76724abb6fc1c6ccb2a141a03b506fcb8ac4cf929ed51c084ef662903b9704100540b55a488356dc7f082e1f0975556658e008b1b15f165c7619778e5c9d8b582468fbedf8b4473b4b4f175aa128ac47f7ed8b81530b81123820f09c5524d56b03ac7826fa3df532f89b110c4fd818382b1dc74dfb5421dc351e03231a3ae21aa500a68583a769dc4c29e6254e13616fbb41c9315f02c13eaece9710cef714357ae4ae8ea14a27adb6332847ac19a906a97fe12f91d367911a4c470d6c7ab1e4778b3d915a93454a7e4e59bedee0e5e5a14dfff5a3cb6208798fe22ae54a128867ac81f9a4f452808111459881a02789f266b838cc1fe7be3ce8569d2d8385fa19449c9884342249dc2d6985199a74dfeded4ba3ad0724308d5bded47e74ddf8cb78c1d4ef631c1fb143cc168d1df724e697bb7da3b8bd3afd3dceaa3cb29d1c2aeaaea024ae9b9f529628010f8afc4130f8e3287688b8d9f2a823af8a18cdb4819bff816ed0fa95048cb5c6ea13fe848b468883c41a09f17e37cd1d018437d1df080f507c0c9822cec49734568e78bdd684bef6b8df10cde6d803aa8e45593e8bec39a1e80127a239975046f1b19c6c6702a0e8070eae103cfbd5515faca38f097102d22b09525c7a8aff0054f77eebf5fce9ba00a226cff44384d256d2cbf646760b0a421ecc25aa96d1647649435ce747df8fe1933272f44139f2d2f37cc721e013015885add011d0208d0795c3607bba3fe1127e3a603539c272d62708d52d2f37d880e3726b0ac55b7406c1aca5038d450eaffcb31c88b71e91104bfe90190e03c50dc485131f160bcf75811115f3f57a3c12c12fbcad9a25685004f81b3e4e422a6101282afc7c3028b86197dd7dea1a61b5cf1575e243dca003a50930cbc517e7d8538606233c32ba37f7d8928aeeb5d7320c237255054409c105c2f75c3c60a36d7cb7a1632b3353cfdab53d43d73773e9c36c5efc847e0a819f2a340fc9e49907797d535e31c8a651ba480af015c98600346e04a2a97aaddcc2aa47af70d8f828b86f11e1cd4712dc4c46edaada701d62dd9924d7bccdef6d304be2a85656e12e175b76a6f353d8f4c6b0a941f2351f619c5e5740e5d9aa1dccf99a2b68c5d50f3ba8d167840e3faaf3b1dc4d0ff979d8e2b090ec916845229d18725b06f597b2f2359fd06593c9057f1e8a8cc475c0e4a3fe745919b721ccca99b888e444423c3a1494559e9345a2a901ed7096db378b5127447a31c518acb7fccb20ea53b613575a3b078b9b5eaf24b045f4ab110d996eeda9ec15d1b7bf89f4d548347849549ef480ef0ce0bf7ccab20411531220a20d3d77d876e105e02cebf2f129e906106912fa1be7f020c99e30d35fcba4a8b5ad342a228531d257ee47b67bc45398437b6826913abd58048399c490e033b07a917075800359e6f8949060e180a35b7d82574152a1e9e7998391f6ddb5b157003f17ad50c40d580ae3fd5a320b30da6a9d5213ecae2fe8215f7d0a26acb85f0c772d5603438a9d0edc7cf420c67bdac978d75ae132a08e518c77315800db2fa4c52871450a2f44eb94d70eb3eba47c7a28e910077db28a2ce404a29a9130c61bdd24cfa06ab6df2d688d7bf5a5a8859098313a4f057165e8dfb73fb9abe802cc8690a9141a6e5dffb2f508720ba006915f471ec2acdce5aaac040438f094b0ac947786e4360e5135cd229fe811520863d45128bccb41413d17d32f97689a1189964a009e0caedd78e264a2341b0481e5ac9414665501bc386e657c79b371a736205b770a67ef25174176893a0c9f2709e14363252a92ec2e10918a8bdda5dd8db433e0241a3cf1062f064b534a8c2668dd5b7dd4b2c1878a9f9c510b55d2c990f819d43e438d8e03c75ad39ddab498ca308bada6d51537826ca755870444b49bda83c51b4edf6283850d3465a46b7a5918f296c0b5040934dd76874f376a83ac3bb74860ba07ac7a00b267edf2638dbb85cf293a281fe73d058af830aef9d87c0170fa790c7bec95eee2595f64471993171aba06dc0180a89a2e29ddb6674d1054186e82d20d4f8164e164b078ed49772b78080cd5b955ae49f7bcdae20e45320a4be59874e7f591a37b46303563e99274777271316c9006d44737d20811ad68ff462d7cab94318d33e936fa4cb07d1a5c3d490c72e7e411e18f0647400decf95bc540b27702313aaa60c21c28c608930ebf40d629b7608a4fdd962fe78017f0fb0bd367ff2b258af13bc4d1e0b7d625c90d0213cfeaa9331ca72a1bc70c910deb150c0f5574d10324b6c5b09156eca40582bec81027ca271e4b21d1af2a4585b337042a3ca8b8cc7da9d808051db1ee8aa1c938894e54e2d9e2563d4a59d467e2a8a357293884617148c947c58a7464ccc57470228e7deef8e70146530069aa49fa23975f9055aa6b735a27ce4bdb0783afa24a4adfa493e1543ce6a567aff2226ff3b6541860ca091171a33b7ca9f4df459e8903ff845a9fa4d87243810d57d7ffada6bb8f24d247cbb36db877df7bda28ead8721b3ddfe147185140a8e9980631625bcfd8e55893d103a32e0adbc6877e28a3aac50bc0e86c65378cd44e0d240f4b78566d92f83eb0992e984cbc4f286aaacca4d6dfae22dfbc0f543425fbc903478dbab3343fdff9a94336f154c06127f43db2c87d3b0aa52e3b20639132b56bf01d871ef98df05966afe3cb60c99d0b4c785c53480e5c6d5bd3ed563525cab0e6ef924a158f2eff7a27323fe8def7763762a41d9b92c8f5eece23cb596efb5683a44bd490cabeed49e87ffac64e245a3e310e50547fa21a2922e67ea2dcbb9df44828a9af319771338945b16816bc0cc70a2d8ebc166d4ede75ed2028af56841e943da9e4d0d5dbc7520bae4c0de828773fb53cfa4ac80d920e1ef9db2216fbe7dfd15777833e24c35e9f8a8d13bf9e11e8427e5d2c791662e4c930a6f22f54e68538cb310bf2f85de14b76f7d2072a4af3fb17c5f4d2184ab904ffaf6353615e06ecf0c4c83efa8fbf4bcc6e40328abaf1403c7885a5dab800f2083f4e7a0d415bcb8101e82050a2f3329c82a3ea96e98888330d5e9dad3926189c03e8cea2f5283ef6a17c8dd2e40c8b328a12981b867596143b095abb6203141e85c623d48a67ca8107ddb7b3278d4aa0d9e2618f7d9edcfebc594d5c8045e8e49668895f1b1f54feb519a6d3d2cccc4e8238bdc354c92db4078995de9c5ce02d49f608516c7c4b4537a2986db55369f12b72b4a5114139829fcbf10f08b777b494fbe7b5fc6c8c11f4758cec291b9905d25ed3863514fc80470f0f82f129bfe88c28fcec4333686526f389f26cea3a525fe887cea7cd5d4a70d44d076711067639138539097b5af5161f67cde416e75ce085285474e485d1d59e2d003bd3c778a78b023c7b1387097dfdbcfd6625f16e435748c521ea2a4f452de0dcc18bde63533bb837fd213265c38f953ff8fdee911abbbfdaef2df8f5de6bd9f76f56fd72f1f2398b9aecac4e32e2f79541ffdef3105fd5b296705b925af93376a68972bd882ee81e3f4ffbf60c1aeca6743664f0ee5133fb38472a6c39f6317835e28ace65e7474e99465bc0c235819ab61ae6445fffd0159f2bf11da187ed9c9cc87fe10b67319fa6bfdf630602860985ad4784a18c9a36ff3f52a61ae28b21c9feb6a81e05085eee3d8c6cb9cf893636e5d1fc44664b55f1005f35b4eb9ec3ab93c4a6c01926c85899d17a437b7c7bd745fa2d36775c80597d43845884747f52720bbd4d9ab2129b120fb5d222908af7c17c68453bef0544646bc9b52492b6d7851837ddb1982d42c6a8a0a735b8a6ad9ee4e346bec0143619dfc1194860ad9861a8d5a477558952781b84481085e6be1765160240e26137f9fc3ab7fa7f745cbd8e9f97561953a9c02f9fc2d6a3c264e2096479106d956a69088c54d11655246cd93e12d4b810c33d930d9524926309874200a913b1c2755119f3461b39fdafd86d726887a8b0218c84cd78b716a55e10ff5764990d0870a58df4a8ac99ce847e9d9b5a61d1eace785f2280f460481a9501aff644c28660c685ed057d65f7465504273d4eaa5b3864439f23b1cad7489e5dff0cd567244eb0e49c23141047e6336dd1046bef4ff17e949168a21df1453a62a2101b179e2fa1acbd5474548fab837d4704d8328ac181b4b37f4e491b9d6c6821578fab7807c8d78642eb09535ddf778dbf7e3c1a8174a7fd3c86f90547da5004f371dc794a1f6cbae88effca0a6dc590d4c3f4b31c245e40e4d401aeb20609b41528bb8af929ba5bc4003972594b65571548e264a9af1f6eeb5c8722654d6d81b4a61aca5af2c42d5db597b8126a1276dc860342662f8228c1a0cd2f13c0709c6b8ec68e46f894ef28c775ee049d4daa253d8c78c2c2f77adaa869f68bf5c2b83826c181ae59b73954f131f4c3651d7ed0bb09d51f5893565574741a1c9e64394510d9c233b5128a06a76382daca3e18b778f8be8b2febfe94ed782f5baf445c41acb8a73a65ea98d8820823beac0a50afd1252a8641fcb8800c2e0694e185069929b9f8366450920591506b440b95ff6d931b818f6c8539c125a3032e493343c194fd5a506e7de9717fe8b43888d657d63bdfcee3bd19c3d021238c803899fa60d24e33b5c8263c434c44336a63a5b11713e9d748be5db1de927c7603757e127e912abd8fd42d3f9b6ddcb4ad2327462d1964cca8bbaa28ebc296a5de7746bdf516e82bf3065dc0c165457d3845258a6d819350495b2d3e3c303961eb8ff2460d9794845e5494f731c49940134a64fd5dd0a92958eb91ca13814d0c4ed78ec0004a581231438b66aa117291228abe2f585ccbf634e63516db435671f803ac3157005de9908284cf932fe373d18a8f0e0277312d5c806b4ab5f665153399fa7e1278f3d5a8eea451ed8c5d7d85fd4da9d70af5762b763caf15a1c0e7dae15779c285ceda55081391b4e35e224862593d219ec2d1835022a713958b709ccebbef3bd89e9d6111c6fe3a2e5c8d04e9485227396fe34fbacc317cb738451562f292a12ae42b106f7cccc1369990b95e5a84dad998e1c953177a40076759502d224128c2b9ea43c984ed1dfc09b6693dc2b4f1b8e399cb3dc1ceac3dbfad642a883023cea85913966618ae1648a35d4f4b292833e63449e42401fde88ccfb66100d462c9cee7ff211da2c09d26e453b499919936ee3493627ad430b2d9667016a6e94a65cc4c46a7d90235cf4a585bd552abcc841925b3b8b30b46e3cb7649c738f38228efd0747e7ebac029992b97c52654b26fd0cccae9e896c936025c1ccb9c4005ad5213ad1afcb13e97a60681bc50c0519bcf70a1c1dd77590a2b86788459a47f16a97909f78dc73770ae5de15a8364bcbc3e35b84a64e5e26d857daf71dbdfb5a55985579027e7a793593e393a33e22aa6959641305eb1227f43937b592886c52abd4eb294dfd22475b98e3a5b431ac678a2bb10f9967a02625250431087e5d13e4333f6c00add16799dd0f2943a1a68b032e7804d4ba4cdca1c2df58d927e122987c62091c633e33f5b133dda892b25ce8bfbfbb7520e77b4b6205a5cb0e79342f928f65420329477b4eb85c1d876368726573bd84d7d16c08351f793f935eb6fcc615d6d4472e7f6285f9d1727c6ef889301f3839c179a7da1159cf1ad7d3d4d576215e8152ec6fba631113dc69c03dcb95e9f8a06ea97f596dc9ce39658803a35eeda7880deda2eca4d81560dc0b80f943842fa54e81d78b2eb8285ee472368c3908e6c29a822421d6e434c8dac773a62e8237d5a99d2dff0fb1a805a08bc23bf9c737d53199a02e7f9ab6728433720fdefef05a79659f9798e8436fc634998a6bf77a0921268dec01a29e0ad821600066705cda322b0942b6a5fde571a61e0678d80ebd0908230757f6142129ac318509f59d04041249c505055e7d62698c7006d610f7572f6cb1a025124cff694152481f4a062a7435eb76759c4c8c60b0906bb8230e1addc5c45fa645e1b0543e1711ffbbec4468b207ac1ab1a20b54ff0e79a9412044469ed2fb5e1179d92e24be3dca770398f794f6f5ede03d79a7108771b1d886fa7a997b41cfddf52237610ebe1c029a8f89bb211554a488387298a3c21fb0d40c96909558920b2d0339e9ba249019ae390ffeb99b7cdab6d33a392de967400f7f024888ecbf1115b3033c948c52663a79e840bcb3cfbb1d20d631fd4ff50db0f3a745bf8a98c6edf330c72eae6f61b93ac97b93d6f55359ddaf76d3eda3906ae0e6ce2465a0818cb55ef155389602bc8e4757075e18b7269a264b321c00ef58f274f599a77127041754133c5891c58ca66c9e671e7f465a4bf7c027dd1f34182d9fb153816570f6f40e602cb2250039e5999424c738698471cde073df46a8173569c6db0076323ee137b5164db77c1a7acc560d77c9177401520fafa9413bac9e23154b274c6023399fcb301cf901099bfe0dda78f8fc8db75765d3a857e4bfc5cb79295cb13a7a8c654250f9d6cdbc7a326bc416eca002cbceb30f4529d9dc36d7ca90788174e5ff26af9a45764c0a9655f8d8dc8458c4b2af0a92d75f33a511554ad465cb3ecdfbd45eb2a5e91a2b74f51cd9983bf92bc2d32b3d4e034bab0479304e559e908305fa7add04b986404372cb39033d9a164695ddcabc884f72108b6d30239d4f8434e53527a7aa8a0c6536b86029bffb9228473df97462d41de9d5838d02f364d2240f2ff5046d4c611860c8bffecc35ba2550a2c447d14185bc09ae28bd59f2cd1969ae4d8ae010feab22a71f5dc4d3cdc5d424ef1393799ed5ba1f23e33cc93cece99bea6fb93f950b377abee6c80505f0cfd23acbaf5117fd54e5634e5bfa0a1f1208eee8dcd096706ac748005d7c64ae940dc7d72fcb849f142ca3fe936e72c84d4d521aea97dcbba3a74a099a8a8cffe0f45b52b09391413883eb161da7c38efe86989974d059c9f842a7b144f0e168349fc571ec2553d549f0de50ff478fe5ea0a24a0f106700e8f56ce22daeed89574087f7fa6b634673eb54980022c68aed03209c423a4cb53a81baec9e4b44f91bdae3006fcf61c9d661e4d9b342cb5a614a10a2d8fefd473c3ad56da07179083be44aca65bd15d999173aa933c1daa50258991f1cab2efa04cbaaa273c5584b6113f6016b8bdf79b8ce3e62fd28d540fcee28bdc640725b7ca1448d8fcb7571e30e91f5a6ba0b23fdded28858400f370178722f5fb70538ccf202e6beb26d81293227c3b651650ba9d8f2f3d61acade1fea2463b38971bae63298a6cb2ef4e84d9c8f21b2680d495cf2dae338cfcebcb3150ca238e2fa34173d5c6409bacf91eae00dc8c4a906e06f86a41ba571961e79634d0ddf4887f9f96d291e3da9aeac649266762213a347c88db941d3519249f661915bc24e82fcb0faabb0bb5ac30f83ca7f1e826e9828e7cc1bf480e710d66cdaf124e1bf8a214556ee67518131e4e7bd54527d50c3e1535cfc449e8f5a45768845093c60e9b9628c233c92bfc01747eb181a64d165776d0b22ee13418fbb1a172986b3a20a24dbe1165a637ad3e990f32a50ca8818493c062e1eb4c40a562ace5e5657cbe31424ec248815330ea675cc0a291ccdca40fcb5831a5b7fe5f2af6a52bad90ec2113d19569fdcc3ab1f045427462610610cc211b83e8cc62a3274dda7c07faecfe5a94a41686afca89513f265207470495e741b9aeae82690ea82c3c57d854766f0b825037652e87902eb676a0cff77cce229bbf0f5f9fed23aad95b55c13677ded6cf1be087137880e06bd0cf71b8ed39ea289a2329a98f48bc4c44d4c54061da9f21dbcb5776ae3bb7a4b7c0baae9ec94ece7ec8a7a8b80a19afea7da117b641964c94b9667717175754ac80d26002f3736e1482db3fc840a5da348cffe13308c22a43f4a11dcb9d0800af0b7c20d4640ec0b6cc2342af6490cf3017a6e518a198f1f2290b11ec50db7512961467c6fa9ec01e841a5b84fd1ae0abfa5e6992eff181899e142a1662e529f7a4b96c2aa121b97540aba5ef9cdd14346ad3c166c7bd18946a2fd8a2e6feea7e5238a2821dc0059b6078120b2f8cc6dfb65de819224593a4dd40cfa811a210f8073836ae4e1da694e72e342718e7dd355256323c11ba386bacc59ca502e401e3257f9c2e060ae047dcc4ab0325a6088708a6433a7fc00e2f0beb063b7578b381001a8a711a98a05dd2b591ecb05b464a073482db1fa11b49d2c68cf71825668a3ad87e2521673128cb306a815548d5e1ecbe66c0f74e426c55e1ecb0e12b48ad5e5b1a44b4280f10aea59d290e27059595e92088cd523fa2db62275624fff44773993fcd116cbde923274c58b0ab45e32c1546c034a158e6a5b30145ea503da7996d0e7ad34be5c6d5bb8cb595a4b27a6ce13003b19ce0bd01fe258834645ce122ae22511c5161d628bdcf0f801e33eb7bbde4ff37afda35e0ad72ffe8f269670e1bee17d834a7ffce22d3bc8de64cbbda54c29a5cd0683068306346421ef166a21168a21c68f6b70842ab696953f9439628c514a295329a47260be0ec373c498715877c55239b8fa67a91cecbf37dbdc5870dc8cb9c17f3e5755d908abfdb6b586bca687e8526ef69c150041507e45a66d69c2f9f29fdc04bed56ab558469eb59b80f06b3748e56b3fd3600ac71c2a9a48434b46488db33538f252fbe79459651a354e3885a8475388adc110f08f5262526ea80f3b15b4dfde533b50bf7d9cdae19f6ac76c55ffd9aafefd981fff07aa92406307744a6e668750ca47fde6eebb6973325bb1ea550c72a8e1004328575709872f0870021cb2f0828315e011c5ea2ae1d0831516ae6931238cc023acfc35e933c3a44fac4a9f137b29a7c4a4f499c9a86931238cc02b50c6cb74333191b24c37f33134326464cfbf71291c8ce2994c866518f3fa67c2180e4de09fd96233d8a20eafd96e2fc60c931ebb3119dbdd1d6bd9b1bbbdbb1ba63bef86114497c8ed5162324ab92b252633e93353aec6a1c2f82ea5e4dc93acb56024c7c86566d8cc30cf231789612d774a96f24377f795bbcdf5203d62188661f10a0cc364c4aec062160cc33029b12f24e65fb29c207ac430b98aa9ebc36b5ac76b5631468c433df699e3ac0ef73634d98732aa8e4dfe7ecaeb788b07c4f3693048bbc03630b02768ed058bfc30293b66e6f828c6e9be7df8f00192c2e19f0f12d02984da3239ce2eb2931455c8b0218a183728e151d49095e090430d798897542e7ac9e0f0030350a81728dde0440d99b5a2640316a6a2c5450e95f9aad790c3912d9d53dd38eb423ba523575653829c4471648601c81083f8d188961aa7a89186276a0cdaa1a3e1ec2a73d5abc94436654a1c894804612938e245415eb34a35b4acdcf080238c948ce0e086071021a186b0ea55cd902a90f88113c8c30fb6a042b3ba493ff052032b66c8c28a1c9470c08245d27ca6a25543a6a255b51857a8ff8186f3851c5c23d001ae1c1274d21a657cda3851a671c08fdfa790a08cfa55fd313f7bd487ab1b9c4a617ef6d9e4388e7bcfe9e7487051ae598eece79c9aa6693eab99fdcc661735962dd89666052e622b9b73c3c9d628616b74cd987b394aedd63e7b02c2809a00cdd7be0423ba757e3f4d7ae6633f7f9a842a28a002592cf505ffe2a5fac854f91a90f50522a06e7ed38b64585307f303bbc5c2d23ef38f88e6a9fce5664212babdf64a4680e021b2ad2781f918ca53f9d7333f95fcf9cbc23e55f62c60d9639f4a3ef6cb6221fbf9f3fbc1c01030f0e243abc5ca3e955c96f6bdd7f8d70196401419b1921c3c3d0a68c8454c1e1134ec22958bee927f0c8df3d3386110f971510a09aac373baaee1efd81917a7d59d3776c7766e5eed0760c188d3eddedcedd47d77fb6777f7c605c70884b8bbb179cec8cc3122f18845cfc89c25c6e8ede8e87883ab78459432fa17af702eb6a5a9dba0968d3916a4ec97de8e8ed7fc40c71b5cc5ff188966dcd238f1080c74a87119290284225b106088224e45b21079d2220376a40b1aaca06c60ca860021635ad18e6e51036ab5c56be3820a27451732c0be90a2b58ae08ea4b80a2353617e4f5061607e12a930325ca9300f238503a62b402af51bced4144d4d7d4c7d11414da5beb86a4aa9e5434dfdae004449b16d3475b3a9dbc7eda79724eaf67448ddbedbbc58aadb838db3495f0c31c56e4e0c153930e20b220b787531c4c58bae4ccd9ea8d9cf8c0b126c7183c5f12e1543610b2432cb22801a54fff954c593106d26af0ac39c161796640386618cdd4036d920a59472092c5a920829a1c54b1750aca0b224d960050d590d65862c0d01ac494fad17528d3f6fc4f5828b11473380410e929c205a22882d3ecf2cae1ae4e83a0d72185feaac68e8344eccbe307e3a4cb5205269542f6848690c61894ee188af56ab55e428c9155c38f1e2cac11169440a58b0558c5695115de934ceb2fc38c3801ad2744982290b23598a6accc2881a93862003e84ee6933927457129129465d1b8e0500336b9f81024776c119471118286a804e362c411d762cb0e2dba48cae504397486147772692207def1d4e4353bb9b84068c7109c3a224e4a337c914a32801758d440802050545da519ba0ce0896e759566d0a244b5ba4a3334e598814969062230f0efcf565d6f6babaea7daefbad5c398e83ed7908584d846ccaf8c671e388732f3f22fe77c42f40b79a96e128c2fe4275c2ca5eaf093da8c04887d3c8277016d905543a061ece91ccec2d33898098e3cb1836e6d79c2b21fa76c19f6d987e9c4784171c207c21996a19ef63bbd2bd49199b7f21ac6da6582262ac60082d228652c21b1262645998c22d98c61034f4e46f2d46234d1260a45050d5d028409456d44201ddd8004885ed24c9c4a07b93fb953739566299aaf30169723b99117c98d932104c4d0122a538e9870a9182ba80161f63c0df2c8fefb917d3c7aaef037ab071acefa537ff007e9d8c91a769e4a61fe8e6facc5afa381b6a22520e2487b47b96b8ff25ef02f3a2f1470a0d03930dfbfc5878109b30f39f662e488607edbfcd9f3ce87931f11a5edccac82f13ffcb587e1af3dc752f3b28f44f40b7a16cc47157fdd4cd7f46b2ba09d1419541745426713db3eaf4725102f3e225ea1eb6d6d6173767936085a4c4822b3f79af92808ec6f0efe52330cc3b22fdcefc06f7ce9c110eb8ed8bf2c22d4af07a3baa7ea9fb5bf393d100790e62b4b284ab08c208a96111c69ea8fd9dfb93144897bf8de434b69410d57df030dbb97122d980d70659a8ef10f7908480fb53044c92eaf0024862f08d78486eb14841fc837d72de58361c762a93868832cd4eaba2a46432ca12449c9fd8341dad81bfe06ec092ff8172f3bba55e5d780bde1da5e07ba4a4f05fff2e5cb97da5f03168c2ab8cb4f676f646dd6a921585986d2b0d3b0659f61588c40acd1b1280463be61e9004850595789071c146009edba4a3cdca0c30250578987293c18d5f07b8ba416282dedb3e4ea4a1e1ed7d5de4041c36deae17352da750f32ce32b8cfcfcf21013a122833aaf23659a0c7d688cf41a8dd3132a7a47b60d82cb4649bf99918638c3ed1fda36ec4b634616caaf2b7ce3e9084adc19f01fa518bd2b2d758d3b44c7bceb0d8621b581c32c5e2f45377b1a07c192f624af582f2851a477bf911091071282e699cf9dd854ccbf862c47c5e44bf229814d71f7b0b68b8efad103d1d373c14741a94361290e0cc1cf584f9974dcd999cc9005b835f3ec83632b7c1179996b49961a856a2cdec51dacc50dacc309436330cb54ccb0ff3a9df1ec59307745d4f4f4f4f4f4f4f99cc27e38bf18594c27ca1cdc77ddb3741d000314954326d8d661b315a557e7cb23596ee8dcb9f93d2ae7bf94bd953f9924a83724a95ccbb9bcdcfbc179c911a1c82ae8dec896d645f346a2eb6067ff1c9de7c91696fe44b25745a098c14c6bd029aa0303fe3e5c72316274eb1a07c39e38b465acc17dad0afd5cd2902a500e8ba19f12bcc179960d080c6f7efcf8be44fda715e94050909090909098983268a6addfc4c3ec641937e554013348c46ad2a3f1a21b9965e4c325b63a590b115e4394258f2a390e74496fca8240e7d2c8a4d362858edc7dffd5412908f3d0b8e7d404d7a16c85f7eaafd88b25f96fc653dfb11ed9837a605195e0bfa91f7145846c0c2753ab51ba10723f480668b369a9d95820842d0d09f78dcc96b4ad8010dbb656f1844a914e60ed8c5485cb4af255751d1f217c3021abad04e7b933b6571269dd76aa99b406f2667a55ec8012b21d085804cda096d8dd49c9476dd3fd8af55bffa1583d7c407e235d1998aa6a061bfac6c4e347a6d0dfe4e0ba5c2688a1a7f33f21c9a45a3ae24ac3cc799bc26fe9c9476dd3f08ae564c948a100410cfe9d7cbc90926323de938adb2803abfea406aa30353e3529eb3d8a1a01da0159b6c036324f6263e17f109ce4a7ddc44ddc87368c450a1fe5da917a35517fbe411f5efcf3e9992c0b252bf291eee1909fb908b7060518a28d3aa5cb492de6bf7b51d0150824d6e1ba7fa63f4f7e132fe7cb84ac21094fbc17ddcc17d5c887dfc2020e5730a07c7d40a42eac6defc622a05d48ed3e9612e770648ccc004b57db8da334002864bf5a76385d7c2b362df2f5f074f8fd4549ae6c3473e906725bc447e10b94d44aa0c2212878e704b57ba90daccbc165a537056290456aafc1b8db33ea8f25dd8c1098b949ec2812282c68a83bd0c8e2ca502f6330d764464fe3958056eb509c12a69c815133190f27584a046d91648849478108251015d25212521185816ba94e2b81e68ff2a0931a9d8af6f18c6428da3d2fe3114e771d8dca67c2e224a3db37e98c5bd4c0ac7fc28edbec33e1b50b56a50feb2745637b06f6713e1075a7fa0d8739dcf2d240d2a69500a3528352bf46bc8462b603a66a6d0551212aad86fbcc92859888d3c466a50be00aa501551ea97c53dcae3e9d1625640e3b1044d793dbc463ee7f98842c36daa12ca12757e412410277655f9233f062168b84ffb1d7d7808fb9529283666a6d99b1eb21f7d4e4add6d36a45bb338dcddbf0f40b32bd95a93d2676262626abd8c3c92e80ae1fcffff9f3c74f0c0e44f791a696664907037604c1d9fe051860e51c60892cd33e3d91926a349a49991e1646a68e641c9b0e2bb86a780523495d93c681f3a7662f849229c4f413334b403ddb523c4483bce42317ef3056f194e345c97cf8ecbe572b9a490cf8da741973b0d6e41c3e9e2a01df6666f76d9fc4960760d13dbb922ac18f4536d1f4f8ca7458f025ac40202e35371d1def4c3c0dc97d0033ae39391c105c5d88a3e1e0eaa28d58e50fbed53ede0df9e5f48155277baeba672506fcbc2c9c582f196a5793cd4200a6c834bb027f433d1fcb888461ba59a4341d3aa54f854fecc04658287ca5f68233343433a8310ff1ef9df4f93fd7afce3f7f32384a948912d32d4a7715a1c0edebdc0daa06117b64610ae2964ebc2deecd3b08d0bec09fbfb855bc57ef1eb582816317fa78f7eaacf1f3e5ba54a75ff1117ebae52e5a8ca67cfbe3b85a3b32cee0fecb97e347aae8ee8578e4a49daf863588f9f3f9583ab0f4c642a476318b61206dd1b8975330deeeeeeee763b4de7d90467409b06411bcf46468ee338ce9b7783a1eae158f2632592df0f0858d46b38994a01e3662a076bac699a37350fb5715b2a7aa738b5198cf0054fe3540ecea3810395ca81550e47f584ea0ed55d9f7dbdbe70a91c5db9bd49e5f0caed8d4771c0487130606052746fd893c1d1e58895c66c31a8989898182d26262666c6c46431588c8c311ed3311c7302bfd8c50cf38446514336e2a1d496cd07206088edb3623cab8028a80ba7a7713819266e18b8f2689c6f4229527f884151a4d07c94204f9e2821c5b69cb9a0092b4840b97b70c5513061b27df49db3bbce93a06d65a3fb1d25879928534cf041fb2c15666ef6d2ddfbab06757a1ba73b7e389d7a7737b66f9ba0057dbd48e04e9d3a75ead4977a0f9c301a5a52d42038a345431eda6716d72747c4a7c9556471c2a589cb15c58997902926f880fa2c158ef254c3a74bd35788c0fbeb4a297d77e3faee7e3dfb294a09ba27ed966977386b0e58e065e7c60c1d68b82e1d3a74789d0e28ee2e82b8bbbbbb5f5ca675ce3f7ae156d9dd940b3c4629dbc4141d9d0f596989a61a3e85120528dddddd1d73772cce06fdf7c6bb2c8d954d83fbdd206f3bb3733132d6e43b8e8a7083b92ba0fc73ce39a70735f161cb8a4667db2fbf95ced3689075a46c273c8912585c477614bfbb45a8abe4a40b126c9c34dc3e3831738f06f97d70f5f1e17e0f1f3e5cd0d91b7eae09ede183db6edab4695d274772024ce5e70f003f9290cd86ab2c450a17b91e9888a0438cbcb8ffc18ccf04e59e8d836dd7e5f4fef7864a7bd8a250d73d46667e0c8b8286ab1d5f89c2737431f624185058517612d0fdd5f2036b4728818222e2729520690853e4c95a8aa281aeb866239818824a08945c69024915282aa65eb3cfde8c42775775f7bb068ba06b43164e51fc553d9fe09947d2753c14c5a1788009e7c46c2b3498f1a8c094ca39ee72a171f6092447955f5d7a340eadccad2a4f20d95243bea18519ba204de1691c6e41e5dff53929f51b0d12cd5fc5b430c405f2573ea2418dd3534087a2108edc5d623724e6ee2ee5638ca57030f67247e28e64ce22a8ff7f838d6152a83f867dc488e66373ce1857f06a7a31755d47b4f1f4c47f148ac79c3c3c5e3d0db644794124cfc67ddb1666af6def1e797ad9164476332fc398962512ebe1c2d7e8f9e8ae9bf3eb71a3c5850665d6c37d9cab2138d3a0fc3ace5eca28d4ff553bfcb3cf9a48fbe8691a57d983cb85246bec065b92b610996982020a8dd31f3d09e5e6666460cd38a9410d6d666823115aad5641336040436e794f31826469fe6ab55aedcdaaae68cc0f5ce980ab07e90c82814fc4cb781a3df6b71f44dd403f05ada599a56e0a0dbb89441123468e8a1c0d79e9ffd93ec8d6e0efe7c0720ed0a2096d203e9e0f8fc70edbe8a0da3bd9cf148e2835ef9be1ec695a8a086ac842362d90866090bd41816d70604fe86f4d0b7a544316a2c12d579cdc9091274f505f3c61c26408131f5e0d2ecc0be8d72a3289155ee136c5cdc1beae03f766569bc69993c77baffb81c61ccf1e11f68bbd7fcd7a57b520e857285f549957a50d7612342b92e4430d509200a19ad0cb01326308fd979ca67d7326314b46d33c63b5d7467be34e360b1115435328b122d4519efc3d07e6b51ffddb6bbfed0dba7dabd0bf793d1af427a18924a954654b65a52a5dbad544b0df9e7a305ebb521e6aa2e69ca86ddb8c3a339a935277327ad0c948a7ce18427b66764e7376dd3f08ae56298f480af56d3427a55df7afad3414aa7bd25ce9289bb398b795ea0d45f57602c6f64e1fc6c873603c877eeb35309e4e08528db37d1e6e40088c944783609bf6dded2814aa67f6bd4429c95dbaa687e74c963b35e8de46d597696ffadd488605fdfcdb8c21df0f9056cb63f3c72bbc6373de6b74f0146b65bef6bc537fec07c16ac45e9b3888b26d93d8172e5663cc767fdf3518d03ae3ea5cd1073dcca3c10e2af47ba401bf0f08743ffcb8bfa908ec77e353edf874b8c2d54cbc22290906f47f65a48c0da5734257980ebf12164b4cc2201ec64fa741ef874c413ea603f641bc4af9d9476fbf5583197ff9f2e54b10af1c0601f9e964afe3ada47f343a67fe8d95ce8e4605c90674ca84fcdd395b4d7ff71678b6c68ceacf23dc205477ff95e76c1f2b995f94b287c7c7f341a1fd27a8d28b51a5b74b489654e96d7553bb37b2ab52feacf263ca5ddce3c6dcf3afb6c68ccaafb33524144e705f0cdbb75e83fa580814a2a4fee8f7d98c6be6bf59669dab95484a438ab817b2f5fc0889939b3b78da41465da52737d84149294992ec411ba4f7424370b9719669869716297a9c148df3ae55f73abc867f7d76097c2574a7174e15fc4b5d50872ff9cb2964ca2ed5ddddf1812f35fc69d5fdb80abae8d75daa1493d1578809932f5f5adedddddd9ff6a8466d956e8f8d5a4dfbeeb92f1eca300c23e6a373c2501a44560d45436b9e0685a0daf7a980a23e5c0939aa3fb42851da2729d79e5909b51feb2fd4deb5cdb25578f36db291826a2d8b14b8fa2f14285d52529ea4a43cd12289249ee8d2c4d2d258c9501aee2bba0e349c759bb33f206ba571c23929dd710ffc81f21361cfafcad1771f6c20e857a41454f9cde840c30eabc293199c407a6bcca8b1f6f311b5e75c7d50fa7df40616e7c7829da385ac038a66a95dcd7c41ff8e42b9621f0cf10b7d364b8dcf9a4fa4b1eb86b74383d0b75016640213c8c8ec1eb19732945232b38c531e716c7759d37380809a00654f81eca380fcaa3032fae7de70f4ecb47f2b30b30068a0fb11e8df0f05546a0876529c862a55765f3c7834b83c1adce769707757fe569b06b7f31aafd9d75e40bfbf705d2b2846825acccc2c9955df2da9115d59798286da5f1bb1f7d0f323a4e74748ef7e1c04ef0a614dec8b086145212cd5673df1e5f7133beff66e2a5c5b2af69bc4e54a9dbfbf836b2583515ad92589b667ec53fdf601a558d83ef5a90f889b5fa722807d60830cce5feda7170aa9a1f6a86deaeaeeee0b3f2108e67ae033f13fb29fff237b08ec6826557e33a9f2db3ef3683a677b17ba66f6d042c37dcd6c57270e7a784a32425d2514e4b0bcdb02ca7533b804e599066bd720af409edf1f00196cc006a7084f34039bd3c2fad81a5cf931b038d10651962455dea6ba5d2a7f0536c747f658d095e734c19e2bf77eac2d53d897419ca05ca5122ace2dd719f639b22f7bc9adfcdc23f27cba867b40df091535d4910197989d6de1bdd2c44e287f051a87824ddc6ecb8f908406a8fda1571b209f90f168b0f7b5e2b1e113da3d1a0dead8b08d04ec09fd08c09e5731a0a83a27a5fc3d8f5461bfac9edec67e56ece3b9c1b59253583e494a8202a5698b1042114350ba326504fdbbbbaca40b13264aaa50a7d6feb033298defef1f90d7ac8a05195f7ecbda9517d2ee419d9d16dd8dceb4caa2a32d550175a13852523718fe905570a4a4822e5e18cc0b6630fe19ce9959b675f78f20723d7cecb7e6b01f52ca7010ab7f22848c0f50647c80a2a4944489a5244bfe5c8f80c2748416a10a0ae84f038b653f9ffd1f3e8ba5fafbecfba781c552fb53450985727f74f78695800285092682824aa8abb484117fc1822d4f9858efa44b19dd37c77f7fd0cdd05ed6c90cea1ae999f891e5ade195df8ddce539f15f04959f1da9b22fb514d82f5569d506d45daa61a8ee60e4853f7b8b880921a0404dfa81e2cf0f9bc90b1afafcecbde51ee439254041819af4641f1f0828eb57f64c2a16a26abf9f263dd8c7e79ef8fdfc3451f1c49fc0b2a2b601c58f5bcf3df60179150bfef1e30784bdefa77e59d293f1531fd0fc38bf75a075e2b2f1901050931eedb1073202d23e36f21a1ed201ea63232ed8a7da4f4581f9d8f764af7d3f4df6b517c2eaaf67fea878522f84c57d13150f50931ed473a5c04f10b3b09f8159d803a154fbc1d0335ffb7e7a780dff0496e5e3e535ec427db84da9cfc96bb2af5b5e93cd07dabe16c21ea5f1872d5419fb80fce7a7daeccb1e7b1632ec81e4671f0b1890d7ecd74b610fa528e0ef4abc865b3ee42da45ef29ceea55e1687def21a969605fd1a7aab72f7521f35c84b6dd420ff26a49b0a4d71bd76f77f47c78d0697bf30e2a8fa57c6f622e8462d647e75ec34b87fa5f2f7f2224992224746a4808179b98290b06dc9d6d86609d6060a3bbf79a8a75e5f714531c462c2a47154dc6fddfe432ec291eacd9be12f64241aff16c2c9698855a4c4941a32d2875cc4b193cd0940d3efa618bf947ecca7da3ee637a0dba8440ff6243154340300000aa314000020100c064422b150389e29d2247b14000a73864c805234944643591224318c428818420c308400600c101a1ada2600cfb45151019851a3f4ca9642e3cbc9e9e230c410bddca5922151eb6290133dc8040710390dfa9d5f2ee64468b80d7bbbf9b50c6a5f8db9db7bc1bcdb5b99a8ab1417d21efb8839dd7eef530afce31d0b0d48aba7fd31c98afc201dd4ad87cf68586ae87da0b73c0051f036bce1c9f10bb9d9ca75d0a548cfc2f04b21e143bb30cbabc158702965b9984b789d054b0fe099a6802f25f61e62abbcaea765bbdc3e6cbcc26b135de1d173f2bf57e8fc0fd30dfe28c902297d465cd2c6071cc1711a58b54da2af8dcd35eff98c8983a40eba74339db4b835795e46ed6de5bf7680a20161c46f356cd46ec6ace0e307005ee094ef35939c959e05e7502ed346a00d948eb664b84ea0fd76602d2c4df7c0c0e85432409a2e4ff07d3efac79862deae4ae6633d0f8980bedf9e1d6d4f3f185b9bec6e75b0a20ac2292149b94301ff592d1152560019f0896466a9bc403f9a3a599624420d5414ebcbb4642be8297680a4394efa251dbb909bfb856a465519e302c1d58c73c26f1cd072508acb8e4f6c0de5976b654bc022c0bf059154ec50115f6a43a7fe5d5c351cc885a1c8a568492fc6688f56678ff62d926b0cd78a9db13e0437b320f81ae1533d5d9f233600ba074ebb18800db48727f031e718641a090c8c3051827e9e649f29567b897d1d03dbb5b1bda355624c9becb19a793a07af76844b36b903f3eed7d13bc65189837eb28f5a6ff06a5ae00af533206d9a0b10e636db6ea0df0585da3706480490c8821838ff5b65cac4af862f75ab69fb17df951268f7ace8eca935978cab224883c18b96d60c48f77c51cbd60b0d162becee62040fe7e8f281813cd569aee986fa4a2abdcd66322ea8891ed04b10f5ace5e7bdad198ddafd2634dac9fcbd12c25e01de377a1280ad60b6d78717668c492518025b6363f5666e28537bc547ce8f0b61814684452c48ad1b77e60a60f81578d9710730185fcd868486b2977a5b38bab058db322207ff6c23a064ce0472356f9fb72e5d0769ce2d49b3690636f2adb9e9937e03fbeef0207049a13003c28153d0762e5eb25e380af784f8966daf63e9870d52d2787c02fac858b4c47d6105379ae144801d963e187d1cddfca6730e8bdc4a816e28752552286f7d182c57429cfdc376ef3d673a9e8e43080a275dba9512541f6c2c8bdb9bc08e030479e366609660917d9dc516f60c1694faba7be3b49eed38f89322139d6bbf14204b46ed3c8c0477000be3d3e3a8529c1983019e5b87b7362baee849a4e7bc9e5798c2979f3909288113db26d78ad91258c30585b7fd390efa1a9ae0ca5af938e648e1a06021a961ea51a796e5c29a0eda16a72c8b298aad7af5badfb3dba5fd94f5da8ee00ea751e8675a6180e552e03d9f01cd087876a7bcaa3266fc40f652ff305dc5cb87b22b7f6f32c7d0a69ed5d56baf6924935c72733ec42e7a5342072a5e0c7977645060151b4ebf484ef608807c17baae0cce493f20bd65f20f3d00cd5857f5584a05863760897937d0b04f8ea5d85cf263fe26341e0d0896fcd69d58dbb6ff00514d4cb0ad7cdeaa005bde134efe0862e7ae15dfab73e63d81905206e6363ba1a3475e15bc721bc90e92568481d88b641266488435d6d3dde216eed4fdf199359e04c5ae0dbdf5c3a50de72f2f452cba382b003b584abaab53659757b51f6ccebe345a9ee57ac148f806c0f7a4b386e32a765651a45d14b7405708a8d8c7114db2b1e13cca7e9ed21d465256c6c1a6d0143bbdb3600287fac95b9d13b4cc504ab3e4e5d97d5fd028ae8a92557592f2012afe7990db1b9ee1bca96ede4d3c4bbdec8c979ce08569112729833c93d108d91b7e0212ff9f2e484f03d297edf234fbb4f1833a5628b9a1add0ce938bf5b50b1399b8341c17ef00520c6a10bcdb878826bac303a286e00be168961dcc83115f5d1eff3521c85569ea590cb4da5eff5c2ccff80546487f15e8d6dedf2a81c60aa8e103466dc75bf52c691f731dbbeb9044ffe12da857b36d23007136e7b24f1d3db28db579c0470ca200692ac2a3bbb79abf16aa53d97c3d4b2dcff097bf68563d9dd21be546e641da3c60689db8417fa5fc6aead8d4484fea1c55ab3a2b24b851752fcd4e74ce7473d036ef03a3df503c2947de0983e82648e7c9685b37f8d246ec786157689df0362f91b6887737efb7f4acea4707cae198add6e9e176aae46c956fd416bdf44e8d4ef0c1415bd8e305e8c3b52603186061dac74f078d86f0cefad81b6524dc9e89cde71bcf73ad0db24c805349958a17922452adefce8cdddaa0eb1f553b006e6d8825a41760241c8f45e6805cf5aebb45000187fc99f2c91879aedafd7a8eaeab9db19ae57a65fb9ace508b8253a279d96fdd3dfdcc9ddfa85e0a7fb57112417d9e741625f337ce2a6569362e8f87e159952ccf799783c830d5199443e66c30e0fc95acb1b431a30ae451a56c94e98039899d9f2ea24598cc7b3f6d363a5b6248425be3c213165574a24b8c867a3dc4b43643410a826568a0e1891131b22f42006136368ad666e5d429a28e46473f157e98374559d29223b4a5314c4072e1a8b0080c1c1073a484f09504e8f30264ab5c5c1edc05d8e2f9fbcd4b86be5b6774dbf3bf3c10c51705cde0ccfe45f19c056288801e5768da31fb4d112fb4a0a827164d33526bad5c4a38d966731f36d792f3636c50267a092c5f52150b665896f1941f2d2a6aea15033805ea2465fafdfa94d2450d4acfb45e6c91bc48c2807b50da164cca7d848e8211656ab39d15febff3678c214ae11201b4beb9ae8b5f075b4e17a0b08f1ecef2dc53624743e20eaf33da156f216532f64a4dfda7e2f98be5241c96426e9a4dfc500dce2f202e276d59ed82002528555407d9a8b216d7db0e4c79621cc76579e6cc7ac2e8ddbdf2c1aaf8f263f0002b0410a50cdda8eaee3fe413c1d5ff6c8b55666e6de9947c21b952d650f112abb16af715c9b3525a59415724ac4fd808fdef64de05d91f43a10a36da60d2e847e3694f82686c174a00c84de0207f9ba02d20b1e73dd60fd645372245041c0be35f37a46cc77b83240f882950a6912e1be9de86b355c99d87d880cbeea3426c06ec937927c65f4edf5ed52dc224a3d280714ecb67490569c92db668bb78c7ce42e41e16f28089611362e5ae459d1f105d11947dee239fcc91350de0e42946e0f202308a80445749decebaa4a7c13f634acf318b3b0a74327ef2f7890a40162969c29ed09152804966498ada0e7cc6c2d3c6345647c6f8ec41093c7e30b0022ae3111e80efeae238fd5eb426c3c2cf35f81fcf370fb93155754fe8763d7d32880ce330beefeacc904feb219491e1845e96a950e2523a9e6f5229aa90132d071b8b1e9cba1e29d56fdaca15c0b940731a31176ffcf6f9c3ace50877b5f3e2eead764a2b447442af776799df1092c83f7bf9b561783321e18ff9c82fa043b7067a06a1a7cbeb3a534bf9424a8cd89ebfd375ddab26c0a932800dd0000d53d4ccafc649d8ebcce4a54fad78fbed1f4616ca5b16100def77b8107b14ab05fcfac65f23d81abd3e9a2ce180e3c10bf60352d6b1b460857c52c3210baee9715b94b39ecd3c97e95f5e3dd2d7babb1e6c4eec7fed7c6fa55d5c8b5d615314ef9e36c6c34427e5a7e8fafbc99343030489ba20af84c7c3fae2cff247a234afa1f9592ded8203578b1f80eff5c3a07be6d3c0a94131fb4c10571c8379d0d422eb743a993848abd36accd3ed0629e262279b3950f20061cf3d99d8afb7c0918cf1a045cbb793b1608205c52ab32cb6a89361319f3bbd6b1fb330595552ae19954b7fca5433bc9e413ef398e41ac00e08c220f8b58eea709e4b90f8829b4287a2c770dedff1278a6ec97d1900e3c3e84e35db91841e11c3693f44e867f51c2188968a1a79d24aa8e41ea4ec0b9e6c9080922e1b75d8d3f00dd21cb468cc23204180659cfe07812fa7b171800367e00fae60590120ffba117dbaff94556f5f7dbdf5fd126e3cb1fef00c8893c4f60138f6863fc4278f089e247add297f86dcf4aa5fb0156196639901e183074eda75efe1c40b5a87f289f2e6a21c6213a8083369a1f835b74d6c8741eff024e7f1852c0ed554881327236c7b04e18d495a8e99cb22289207518d8d36e1b0f98522f4b376f0b30cdb1bb326f77ebd949bf354d052a1e52dabb3d0846d2f12ec85e44b32afdc50b8ef43e4661ee4f8189009517b98033f8fba3e58155fa77605fef0a0a04d5ae9224236d0c5228c695a48a67eb09d0c5a998bd281da0115c50d438f19331b42e51210506418e64064d11d10700c5bf87aed1e270e2b65052e98ddb6caa101c20b1a9f57c880435a6cf0a3ffea5c83694f2bdf6caf32e183a2b344ed92d9a54fbbc2d605a78edbc3bfa02b57da010fd9725b69f3863d0a28fa9f17ae66acfe5240d072e48bef1426f375c6d6875d0ce3c044eec0daf61dcab54aa04cd44f97fba40d4d88f30ccd08f26d24db57050c640687a9a199ccb1c2fe058325a4b9b43b3860c6abb49a6f85d4f6751f25fde8eef2241c31844c4bc36a2fa1f3e5867929f37684c1d14abf247ff53b1edc8507c0ff76464b68db3c12c3b5889fcfec2a47d0a35185a9615f8c8be63c95a7aff06f8f3318e5a2141746d8d41363251c386f09f4daa7a391f2e4c1a82583f4cbc73e9d5de5271786e73b2e277bb63d4a27633c902c7810eca6c2db134463b2ef38b21749c708e2d949157d97a645b792db50f23e0969064bba67dcb2ceb7d5aae4dbd60aa3097d979d361341146a7d343b42e51dbbebaa4eadbc21ce0f2815097cb0508c9e3ed6d8fb692d466bfc1edf1b2758e11f00acb397041d9f910aa401c476738145d8e2da15821c87acc3d95589fd293beca5bb9fcd1eea4cbec0db7de335e5fb5249b89a1688d9c40dd884bda315a1e55bfcc5bfb9370a17476765596d25dda2f889b8bc66868d209fc8638e415f0cada750ea232c6a793aa64e38c0d49a7412fe9948f31f867a0e98c026ba719eecd328b73c1f954f0b63085971cc1823be1ef9148c41c38777fa21b1ed374064aac6778a8a9e3dab4755928af5025237ff98b89661366bd702e2f70be7d67a4608828d86154271f9349b02751e0030a4711abdb4799d80bc9b34527baa6a58de8a1bff5e7fcbb71c505560c6e70bcf5c58642045794b7e85fccf924caf469f2e83dbd4dce90588ead3606c12afd46c1b6587ac8aecef66136370eaac6fbf9142d344448c9fa7f29a44320d8f3655847c06feef6d65a2a26bdc25cb2081eb7f98bb3f9feab89c6bee2c457c315e5d4b0c80c5e94b94ce295a31990d6a18b9b4fa550c82f1490a437ce3c90f3ddaca51e892411f7a4eb3648cd332b2f81a68b7a60a87879997d748887d5dd3a6a6578a98e8bc3eeb78b9e40544a4dfbbe2903ec7bc879f51b00e33e5b59defa5adc3aa39e668aadfc08b42daa5209dab4b9dba5d98c6a3f12e00a3c5b455d1506e110050a15f135e06790fb49cc5259a9eb616e600c4d27a279d6ec272f4b73b5a4f8165c2350523a1e0aa69ffff57e54147216c7ee780a0072513c430a29a1ec5ca7cf4ebb8e4595162380a0f77861316828ca47374cd61e3393b36e40f7c9e5111ac710ccf0bd6b0815061820adee301768752f0a759cb219343416fabc042357fd143dda81774a865dba41904349eda712121c5ded7ef5fcd131952c3d016775cb5b61593625aba23d041568d31cc7d4132f8fd644770a4a5fc9deb28d073f225a8ee7534701ba3d240c7a6605cbfd8155b27fc6799a4e1239e29cf110db55ce5e20b38c33b3adb49987dac8a5b877f7d067cbf721d7073879347270a528733971e87ef318d3386de7a77a87a212648f2fd6778de2988508bd9f4f2d99ef9a3f461229aa1d3aeb46581ec0f2ffe2ceb08878dd9c599a5b25fd13a57e698c332135f3707b5b9d64a0da08eae159bc9d605b683f6802bcfef7fab771cad584cae63fdbf9521db0e066432d8e7f17685e5f6fe92abe10fdde2151d0d2321c2a8148d10ff4138f63fb9b1c862c5f2afa6221472d14db06f9770028c9007c426e18658c24f6f4cbd06fbd1a5182c920c6cc4a2a2631fa4ddabd21e786ccb3215e87f24dc2c3159c693afca6416cb3168fb200029b728a23549635c8a8943b69ea89a113f57a448ac548c1dfbc747c8b75d888451a660a47e1c9278fbc5dbab6e843918d788c66ec634af895925d9522e9a060463197d5d80613841827bcc22d714c8643ddd12991da40d766314511d420743b59187815e1b06e63caacb6f673a944b781ae9d95cce0a684611d70e86428c421fa6134e00efab882b4e6e468b2018f6d59be986a198225ad01d20ae21a42d6006f5cd7ae06f67edd443201c274e99e0099bbc47c022ba01c6d8969872a5183c58397558e008ada6ee4ea3e4b80d9285eb390111fbd2a773d5e487d08a0748b5848240edae13de2db1dd3f0a7d01d8a962217a92003db2046e706ad27962f9f8fbc141aef1caaad7de40778b8b757bca3c4d340befdd50aed43b7ac2e1613bb337982884b47f75ba9ea8fdcb1968f939b2ab90f8ddedbbe17d3f345dca2921695425eba9d61b48702f17395e32d24263299ea9a35de8c49fd6796ce5342f1a2d26300d90dc789e3679f4109e7d0fa9963be5cb048786ff5559c5596e040290f1eb6ad1f4df3f93a01dbcf4855514919c2cfb174cc4f03a1aa3dc2a1dede485b16ac3a9749b9420761dd2d70ceaf04f92daff4be6f03834cc0eff37b38177a171804000d5573e2b4ce5998fb296070879f9f4571e61c2ed78598559c5b217c3c7866ce52e5998655c679f43ecfbcd055ac48a81af2ba7d151d295eeb933caa58c9f508c9f86ed675ba20b5cc9e7352208f939a103ca2f164e658dd01fcca7524e5e963ec9ae2517be1f69ba3ccbe8df1f65e565197ff9552f1428b0ee1445f01177f28af03ca6f3434c120f04154990a0f91f1090e9a323f0bae498b91dca3f14eeca78f210d96303343ef4235037418a9f7fdd72cccfe850a6e4c47ca23e4eeb9b6cee1f83647781cce9277b4f41dc6b7793e2b27c4c8c186688da8d4ee1f81f44245a10577440406ed496edfb96ca23c636f75489334a81ea58c438270d60dc8b61b5fa2f0dc756fe9593e623bfcb8ccd5516c906f9672c51cd31cc452606ac662ac1fe2d65c18798f40895d1016b98bad17745099d8645c16016153c11673d1cc2d05d083b08061e66a5a25ca6c07bf782200aa82f36d6e282f4d6bb036938e819df0247060f27a08e80b0fb048c7aea39ef25874af96b7cca606731d2315ef4ffae78280016124b092bf0e00c6d94c98ffc9a9ee11b02bfa403c2a9896375a501464eefe342d519541cb33d807b0ac8036d5f6ccf85f09cd8f79ade7570b7bce9641edd35654c38c3cec3b67022c40152d6d00e8d340025f95bd64b0132ab2925c7929cc692b5fec458a2fdc3817b24876958cfcac8cd4d3afcf79de99652ae9719763e3c4ca6947711581a25062f8bab47dfc0b20c171db507f67df9a22f28ac5ef24552535891fe7fbea4ef15242b2c4ef31df3494dcd720c088bd2cca466198a44f03c2f4dcd9a782b3434cb044e5aaa0f7512ef3dd32d9537e098b55dd33c18de4f24f73f7af927bba3c7915e1fc93a38a5b9f9e993c1c9a17b257b86bf3077a547bdc40fc9474384b0b50d912523add5ecb54ab1fc1e48b7154a381f5203b700616d3a34796103d0c49eaa2864ec9e4b7203bb0a3a100bba733d18e1155619b09619ce756db14e6384d23c2b4764a401285b2cf1bfb32888a1077d0de9f047a9cad26f6fdda22c65b947c521f247367586ba46ce3a5c1ca6091c677e56a5e0b9af919179a6cf9366722dc779ad4458d5adf40f6f0671a3f78d54a2631a6abaa489f9603a82f4b31bf70738f1e64bace59ceae66640f46b6e3f25dc051177594cddb84063883d6c6e3ab2aa3e2f5bddb894a16b25c95970a630e6f2fe42b5c1f11901273876310693ca6e19ed77602a3842f1b2069c01bcb13b16ed80d0342bf3de0b05662dcd10da1f5092a38c5894639a0db6a0e62058b00c53abd56811f7cecf3927fbaac1804853a478ee0d1b52121110e52ab8f2ba79771c864de8a3526f5430ba4ee8decdb81d60bdea0181b56d91a6ffbbb4bf0442f1926b3e8e1e15e71005e8f1a20e27e35ea76cc162497604dfd9d0ed3b542ba8ead33dcd7b830e3ce1b12fd1f0353f1f45eea6d62a423502b9b364ffa6b92dc05bfd3ab1e0deaa79a2e42ecffc9d57647f1f3e01ff4a3a7499f8d863ec7a0593a0ea4d94efdfc5fbc38c996df201f174f0b32ff6e8af22446eb9c2002d4a7b30ac8c6c5d77052bba7ca078af639a39f8a470870e57bacdf51018363cff4233633e5490930d98ae9471750e915d0a456e7f60a3f6c646933333c5eec11aa345457c8517bbd13acadd4bc2773c03ec7652e80425c68e8ab97a357ee35312ed0474bdb5a1539cb22d8e10a87c70eefd8b9f26994e8b3a14fadf39450459e4ad2136e58c5bdd44a861db1d8892c10e1523774f8321dca50264f6fb5fc4578418546975e68332dc42d884e5965be52bc45272020dde40fa65a0f67add422dd70d25d3ed54a14c5618b7ad30beaeaf6d54b9cc6631ba176a3e7ae4c6d4f70a8e92f2b40493da63b7c37a98861d90f29a462a30f5e2c4a7ab9699b0d104b1aa8279e87831e51ee3bfafc1d3358cb9ea7e3694269954ec3e8705d42703d664722059c47c1bf837894ed15e247e23dd745e3d8397cb728204482bdb5c614de4692adef7110af2958dde7144e0d5fa696fed12354895d931765794e2e4c16eb89b5b2265444bd152914f62cea7c9330dcb37f9a0e32aa0a9ff651562924fee39bc24eef1e5189f7bf3efb3fa6ff27c0009890f2db33c298a22c0cf419e576a058afefc0a8fb3f9078cf344c58f8b063a683f833d2d2fbb057f0982b88a87d2b3155cc32223d1893dda8a207f46777e33bae1f58b6973325fb613fe57e8001bff08f219cdb20a9314de5bbea6225b1e781aaf796cef47c3e826c414ca71f0f85da08b7fdce7fcd7613823103f6a17f4a38a87e144163fd8aa5bfb6230ee684210bd70f3867ee4154505b74f00c62aba397d81e42bb492dbbe697de556dd1d33df7de63ad51e02ef888121854a850f93d9a222629591e19ffe309bdb1b9b9fd3eb25dc1df0e8491c19cd6ff40f2b2da332896c368eaaf581c83e8bffbe8416c86c6a6c34de30966865e927895d4b46b931f826cc11b94828737435dafa4f9558dd97b6fb0dbdeb7b71397c106de0b04edc022cf3dafde623e9f5481a6b7502697268fd49fbaa4fdce873991b553d36616614ad0a0ed37805522e4af6a952c397cb54294f4480fd3a06655c15d88f1f8c2612bc696f874a128f4ff25468d726ece0a80468e08a2bb16f9ec681365690b7461db8ffb4445adddfe973b19bad28fef28d812069e3c9a07c36698ac665c9a4f77c57fb7670f9279a8b6fb030739b53fadd4622d9323fe0254f06bb74c23982b0e2525db8b8d0a370cbb062570a2aa64c15210ba0bf5a19052dcb86ec6cd2936e927a013a45fb6914a3318c59041ef1d5ac6aa08b39c0d5bfb5cbc28ac0e7da844b0ccf03db957900f1952c60ccb413f53d801c15bec5d7678fc9f496bbf36cd20cd8068dba111307d93428b1ce410b3e2cdfdf5493d43a0b3a6881b0ee411a0b16e3a4e65350cdbbec972903ef25d18c8470b3b594044766593d887b211aebff1cfe9e2f022a56ec4fd6573a04938c9cc554569f3e8e8d391a8defc44bb3098a63dcc9595f43d73da9e0f41554fc9d7ddc23f1ca6bdfc60cd411ba7d8bc2c4ad05beb66e6eac530df71d59287b7ab247e52d74d27a61ce98a4aed40ade9eaafdc282a8e967e24043f299b5ef2705b5ca5c1b34707c75976d2b0a18b785b3388f8b6ca340a7d3d8a5f7ca1e40e7a370d48066eb8a0a36ec4fd854aabe6bdc8f7431bf22cf31fdc79192a4003ef0bf40c2c3f4bea46609293d7b0e3eb8065a5d3cef62c4dcf053f12c1a857e65bd9458605bbaf99a93b9d987accd003841a32cb60351735979967cfade3c2a8a69aea7e9a0788b2e779272ce4e359d2e5359086f79aa56417b722f375b3477af4d2fa2df811a15ca84e2a2bb0445a452e70df980a8746d658a442415ade9985ee34159d30fbe95817cd58b9b27c87a077b011e5e8b68602533556d20e4d9d057a46a697d79882911c99949fb1d659f521dc3263bcf4453dab3ff94c7d8b12bd81d17e22a144ddfa6b250d6efd11fc906b511fb5541083bff7c71929575ecd4d830a8eebf03a136ece40351c6b89c2ad14dd6413031c7ec8c5407604f4c3821b032b280914ee9be88a57ca953ba77e3c83884e912425ad2d8a15a73b8307a72e696634bfc6b74c8da6498bc9153e8aa15383694d045f325b0765c1ab48ee88558366e18d46010a3bfd5d7595f9944e2e0f39b2e02a2190069f3396bc04d66ea8ee1aa719047a175048150cc4ed6b8745d147f1b6dd266e896c5ea1e93b357087837864f3e88fdc98d4ece64b14a19736483e664f945cb0e38eaf0cb50af122620833064ff7fb307b6f06a494eb6056b4ab3ab84dd8698672deb25c390b0171d19ee6623e4f47b0a9e64b8e36fb3b1abc2210fdba6f3c94138f66720a85be5e025eeacfcb80d75eaa9918bbebd914f998b9c3ef581b465445cec8541b96ec4d9299156730a0bca221fb38421ad31a90bdee4b4a215fe8171f531900830a7209a245b3a6b8d02854410b06289263718fc6d08a492ee5b6a506985ed8138b7e00000c6f4b7ded35e6fcea4f614b8916161a588355a063061c242c0a5cac779cf927a3f88ded44efd55f142eb8be316649e3b7fdb029a23d638abb0f68358877423c6caed110e57a76a051c287f51b87000acad0344270991de63991ab3ade4738ecdf6bbf48eac914086397b7032f61f483d83f535ec19a4e4a5eb3bf6dcd1efc4ac50de291e9a8d2d3f99f6b812b855ef641ad9ac86a9ac6336929de869c9ca0b6e2bbccd1204dbcdbc8dfa470f4742d783a92513f4a0cfd22731ef3b0afd14fbf05b5329be949ee4e3fd5d76c59b5804127cc1ef2c7ddeba106b33858f2f62305dc5b75951c395f3bf0f1cd4d54251db70a39fc6b631065f70d83454f6b5517ea49055b491cb0ca561b16d0ad241057f02007426e8148d9d4669cc47f38e1948816e8457152d3b59a74b315772f5a11ae842ecd978b150c20f336f6226bf0367513ac98201ba2465b9e4745740f3e46a920a75cc052b69d0d41a42c46695c76210d748f57e889dd77c0706ccb7d629a946a12c7c21d560b273618333411114ac4e933bae43c063e86672463710b8b57b155f96b2a8fe0b7614e0f6cc8bc9d76a268328a94b5afdfd134d42c35e6e35c506cf7fecd518e311063c929cee93f701844925b80503fd58513058e7308ba981b98fe5e23ded26918d81137923b5dda76430e5eb6e7cbf53161a160770f28601873fc0c73dd7b843142ca56ea388d8673a8ce160f4908c60ef0b49d2489f225c84f3c208c0a8e719e201801c19205156fbcf6926a8d60cf69d1bd28f65f80893a0535b4023c25d713ce9be17e4c839505320c18c2835237fdb6cc5e124c766e659a17637c6dad1bac49ad6919c8b7a1318c687513f876386da7e77a99c001be4499642d9922ebd3a302cbaee4cf7f5a9bd322e5c5ac58ab0fd250d4ce4987bb213ab5c3e75f07fe0d540b6ebaa8b0f92bc6d84770def2ce2c1c905e4e545118fd15383b8c7ace311869db2e6d3ea488509ac157328547c8cd2e50c88fccc0340c00948899be0306ee576999e31c0607ce173cea4853fb765f444a0d753e47f63d7ea6be648c1bfcc60308abe7d862c68a1a90a87bec1b8b15ebe16128d346b6dfb03a1a7ba90477a3d81a3bf434a4790a5137e1c7ed64304d75e026467be2423121e955a87dfe82fedce59aa263baa83a1da5f83c2dcb51292e5fad1cf24a93472c58160964f139e3242db3f75ffe4eda4825c1bde6ebd7aa2b503bbaad18072ef1aee2932cb7c7d854b2f4499ecac1a921e40c8ffb8e6fff2450d2e3430eb9018f8c69f2dc707513cf522d5734d2acb91f150a9ea542e5daed3449809f8e76723154e3d0c76ee4a656d07df3bb452bfd872a270db53e5439706fb6ff2dd88c435bb113ca57f2ffe70d9fdcfdc41340ad89803bbaec959dfe9a32d8138f176793b6040cd4c54aea0a34525f18efc6523faa6b3ec7e4b39eb491441f6ca53e69613d3418002d92f9ece907b50f1a8e3fe1c77368d05887293770bc692b02640d9e6f36d47c00217f4d199fadf75066f2fac4bd3a836de016d9b669cbb31c41d05a5c8a00b9d69dd8d1c94ac1092b891b7aaaf62c2e7d22766841a3bebdf390fa669791f24559dedff72c32fcc9c97bb9b8b13445a2d4f76c6971daa55c2284c41851629b88fb113f138a6a5ff521b34a845eed5b2904e8f5ee2a2fffa07007ad637fa7519ca5a315b536c909aced28a720d6f2ad62ea4c7b48399d27a32a20bce288e8ea3228ce97967043e7e1e06295159ab85d7d805e01e86f312834bbdffa02a5ff2601a7b2459ea9f030eff49df6d3962580917863f2c89e32c66d68b940c424f894113aeebb6d3e857c71540ae5c9a0dd96c491c43295946ad467902f196103d54a33523529cbabb8b82c04a875546749a7a975320d0c201ee704dcb30e91890573fe6348a0d0f37f79badebef3cd1306fc4dbb7db1744aa045eceb66fa66bb2e171efacb4e1f0758fc77b6aadc819b1a7d94d389600384b3eb4946870b5f9680c28e6734d8111ab4dcab7e56511d375aec6001fa7dcea48c3236dbbb80836ab9b6b5b75f4ec4eebe3d2e8d64c5ead9e6c0e6d98c89f911de90ff118bb6e2039f0dbb55bc820fbf9b436071ae80ae64edf5d8cb85fdb7b7a0e21f02d24df941da8c5010b5a27db34681e704391d3b4d771d7ccb471a303a258955cf4cca0146459c903d21854a91373d06101419ee3b52976496710448db0ed5f4b557f670ed2cf5a763b790e18ee88a8e2aa86e03a07d564a394ca4d8c98bcb2f2c48b9903d9344c4432a9dca485a15277155bd38a0aa3dd34f3b5ec24580b8cea5c45b2469ab8ba6cfefe3e7e639082aec95ca0ecd616ab9774b83a099c0632b4fdc2a23210486dd1b6889c4825e3285e0672f88f93a3481ed4e0545b85fc4ca31a2c584a2b4b0fc27dafdc337ef604c5dccebf69acfd0763c6d1b26c665c0616dee4dee5d2d7b80cd332cf84586382824072d7c3a9a71cf02f3406b7f11cc2004822701108c4636aebf80d2f07e55b38331b4e496fc05eb00ae9370d33720da213a1dde48dc65c94eab29da2268a1eee83c5d6801fbf24152fec6033c4fc55bd14b56223d5c48da0e78559c6f6bb31bd2fed400bfd6da3311e96839a62a3527a9e1f616124751451bf8e77561693f0da1f0bd30dc005aadeec77d4070778f469ea4ba277d38c871a2b8312f4100bdc2dd7831bff46ad639741fb1eeb69bd2130c3a20afc0ea4867f04c80d112909834799749b25333a238a434fe1b9a947110d260bcbd01e80b69dfd408101180aa984e0a5f99f2f6e240e3e8584ab53766170d13100b7c95960d0cd37ba3c85cf48055414c57ff8f94df64caa0d624d36eac6d059f11ef76cff51755531ae9dc235fe0b3a55e3a852e73481159dfb1471ef6ea06f97cdbf859045ff4dd7a89c2014b4d4925b96179739f0724fcb445bc3296afdb13024f9eb54be0712671d94acbddd88a4d27850b90c1ac76a4c255a2aa611ca2d16c58a383f73f86c62b61ef2be35864f9bcaf1a08b9b89a7605b93e6edd011875e7ca6587ef6cd2305199a5c4030c6341e559ff7d4cc87a87085e69dc70c230ca46327e718189dcdee089f7f72581b61e314c3b376c3072d8e75de464420a86f7d64d8245916e3d71fa7b1021f1bb1815baf4f4ceb1eb87a8dc136f17a8130228f559f4637a94295e9d951f1e3ac48c1a9cdd9c26bd407b6ca7efb78206285b7b76eaf6518d2decee7d6895b3d28e6e2873a896b0bdb5b18aa822e68aa9d91f1f5a70c489d5e3b7dd6815adb6482a3e8861cfc9610c1569f967607e0b4685861e6cab162d4ccc78072c72fc86e6325a66168442461eb5dabe01979f07bfaf80db00b0109274506f0476588f7c150d3c6a377c2aff2e08a7b316efc89ddde2b8bbdf26ed0876f478fe224aaf9ff74e094834deb59bcd3bea1e64c7bffd439d85ba11a930c5d53843701821a772254eca30a85cd43052341e72d19c7ce1a6fb91ca23fd3001ab526c1b9c0ed518857d18bbc1e2caa0b0b3a35e6a7853ef283f2ae9a2572d1ceca57b3169313aab34cd6a8d920c2625458cac4d57df04e48bfc16f8b974e178909535e26b965ae2ca251c881a995c6fb6206cee30a49dfb6e91ef822780527430c5ade57c0572e37f43d6a86d6a62211061c1b27cfd824320963cea0bfdc0e6123ef780afe27f4c0b5a3472cae09a9d1bfd5855ca6c6b0e214f2a2dd0104b027b518356adfb12dbfda7772f26ce6f9c98f0e9d8ef19fa4ff34677a3d233c41c0fa424bfa7e34261c39156d7b7328100db7fd1f3242db62172e3371b2735ca7d4e67d2f3b98e04a0fd74b9fc3f555e3ccbd3d024bb482758fd9acf44c8d88bf73105031e162bdb9ef7c2f9c08ae7f771c32bd945a0550e17c00f8c66835e9d7970f9850eafd31107efc955143254bcc9a67ba36013176080185504228c705d0a4e6c7c8f2c1792c79163206efa5deab2ab97b2986c4d8248e0b1368ef585981b11045edb837d014d0110c64d9eb5d79cde7192ad70d86c1460063f80abbd69c944ec1ac1758e2bd239bc49429300054f4bb0368d0cb30213b36d14c4386892fe4d10f19d30e075074f12a507714a853bbadf4f633ee90dfe85fd7939185afc14ce7621cc87e178ac6b9622e61fc09d29554bb5186f23447d8a35e9d36f64b2a0390381677a4dabc577b6dfcd942772eec29d51e1ff351e4cb792a234694da0a8fa4804d0892f4ef24cfadcb93c767484cb52b912a7d4eb8417e19406fa99414cdb410836a53841fe26ef0fc68933af2a73170c7fed35108f01157df71dd1156ccaf6f7540613e8af39196dda838705d60f1ed1a1bd8ad7178cdfe6e9ddbafc2e61650aaec18fcec6193ef860ba31bc6af99761f5dcde3972e45ac3efb17952a1d7be1792b06dc11928f60b54d856538f41ddb6d57e24f53c19983b8518c97e321720cbacda5702f6cb194e83d0a5a3d2342d262767bb78535b96134196546d064c927bb91b50e7e708f01948ba39158744333d441b68cbcb14416788596ce5a2307638d3b4ee925298dfac9d96475727a8e26a7b950ca53242e88bef7dc34be4461c703953cc3c2fece071e8d8bd5a74ede4b271735394725c28f45a3671dfc136fbbd19b37e834663a272b8421fbfc15fbcef3d18a2cc8bb4e9d9e8cad0f9cc75450148377a698bb949a43fa5067270e6c3f55a5f79da08b0c4a9d0fe4445f82b37497837ead2dece261244d4535e765c6f9ba336fdecbd48c75966d0ebd207289dd62fbd4f0e531fa24bfc4791d8ed5174367a2e16a43840c6aad41e3b93e09691f9923283d1115a2b451efe8a527e8f89b1401dde5161eb0a87f676894ba5145f7bc8b2a2a08e69b940d3b9ddafa9659ff272874f1562a2619bd04f93fde11c9648779c592dea291be88ae8da90c6cba989d911d0ab05250118e56e56395f634c2e4d45bd78d936dbd70100252e56966554caa6e51021d605bd76edffd8f61fcd03606f2318f9a408d03a2605117e2591a326f317dff9872b30104391a0a4ad3f54cfa769f4974168cba0f82ba17041a8135633708a68b665e357e0ad0cd7b1caf45e84ab0f1736336d08a7808109210fc5ad9814c1796bd82db351a3e704b5f123fe9f8f9d30d5685d16f01ea9ad21ca79c214d0bccc97cd9556e2e8d94295bee1131524afcd01346b482f17e3be3be5a9d02c7d4dd9b9fbfd6edd86f65da6905c3f45425b8582ae89049297d46363b7f0a544f80f14905fb306bc7fee6ac69ae40e1b24d9811f9661cec821e7df5ef5110f88d312ac5339a50a1c4e35251d94e0befb8967fa2b906744b832845ece13c1f8e630e1c281789b60ef09e9c5fcdef4e543f042d3c058b5a066772b240b5d1506d45adb3e8293d7b247ef76b1372fcb1ae35a16e24884204d62882f78d7bff054301027a5c6bf0cede50987f0b8e7b1fd76fcf72a8055e76bc52bc814310eb2c9f999e1aedb3766178cb05e81ca38dec64576cc80228e00f1422688ae1e7f56c4f6718692f1d7309f99cb2215b997012bed94b2f08d6a9f4b47e77452d50f50557b6e63d4f524439d35f7a054332857595f77b1ef290b42d4c31c1d2d039c69592b8c671bf57275e5bd72fdb56129954bd5fffedbd64402d9cf0e23060656f70b0d0a31e8ffaed2aa6c2a1482662c8f46b11d682a1835811d0095f85b0f3d30bac7b6784c122056d0058ef4406852339d8141a44eb7a01e4b50240b0b16812758d7e2ebf621afd8508bf8f427f4deb467574e1e9faea1cfb38ad45f19932eea0d97cfe312ba4295a37efdd62152ac47677754d0fd71ca9f355274246f366a1e88cbca7970d74e65d9b7ab5c1aa2ee4e950486a4f71ef0f992546bd326236bd1ef11a692f364788936bbd5a039b8e11df8b082a80737d102d88a92b79a8a151c155a0ca04d2baecdc0102699bb10e6b6d978714ad7d07e092e3a5768598071c76e04e187e285c6fc08caf8563864e91b4cfc884e532966049efb50323ebc8984d8a1170f37ff312af579f545ead37cc8eef25a52796100ada781da94c7b4d5a0a8bfdc1d6f7d0abebf4af09c70a1f9e22c4953a05fcfde3b16e7394e2a0ceca462ced744272ebf47d16d6063e87f730a673312fe1f10412eeb2a74f33410c6302bb4c1b86c2ff362fec2ba113538167cef340bf120a35ca65f74bb98ccc92f6e60efaaadd78fd5ac41432769266e7d7591269e4da2aec889a4a51cfb69ed5f66dab4d38ce39d8b380f7d09d17aaf2d36f0b264225e733581848528bd415474cf066a03503ce3e72bdacd10bb94a5ca304b455c1f0317c3fdb9ef75d7857cc4797b1b37cb05a32d05b4a75bf579ff595025390933fba4df0c9c4e6f3e9aaa423a7233167d7d7037cdab0f6a872c4cca02e35669bb252485dc0fc066d5d13b038ea9a4eab91cc5cd1db21615d00ff7f742a6b8a0df1712a3a74eeb38da4dea4a9104f95f3fe8291e355ac9f4355d4426fa57ac340ec7860043d027ce62a152d54d94e5ed143590344f664f1c0dbdc7b9ad532c277d76fabd94ff85b7f5feecfc3ff9fbcc84c294b073903ed3d098c2cdff1b0f2ee17f474af20bbca5084ddfe2301f0b7152df30e6ed3d7f2bd1c49bb320050c40f7c4edf9666fa385b458bf0a6a55b34d45f03fef9f72c2dfba39890428084c51a675c2507dbd760ac82d69119bda9fb7a35d321ee3bf12c589ec01ed324e05f8fb22f0271660972e363313a355f469b380e495de7dd510fc099ad40ebb169dc1a2112be88a0693cf86dbf47e9b81d2444dd845ebcd236e35e9833c08ea364c4c96456e44108264cba85fed342afc882eec2a273e9b83c65c97a8bba1f51ee2f4aef6f07a96c79933ca373ad08592b8f3f8fae7dc427b363bd8974e287cfc41c8aa62f666bb993d92da350fc1a0284524364fec375f98b9a757959ec0934a34bcedac2181d2ce390f79635b6fe0d3a2f65e471f65e1a99a702a5572b3ed3bb71cccea078c5aa020ca489d505e833c853dd155989f9dbd5619f621c579412d7641ebee51c2c548886a8a9ad18a9b4d4eb102e780effe4f8c28ee094a08a60fe4b35be16eb9c17dddfa30c24e98124429bdc06a4ed71919ffecdec856cba0ab2130739d9ae3267be008f4189a38a1d5370496e39c27d8ba01e975b55e9cc7518654c1867193d074a24b9416df456504780acafaf9a033ead10aa18bbc673d4a562199d32c08f0932fec0c81b941d8dbca840abe1babc68698ca2456953b45abbf823aa18a85a35e66eb7307c9cd4f29c29c4ca55f7f9588a885b963415adb22d0f033fb980398171b15afd2c52a8aab933c2805e32837eef01dba94012a18901e9ab0bdcca10eec7da348bd220fdafd8f35ff27f4a41deb57d04cf8b7fc12b4492f401b96724808ac44f263c40bdd650e945862a2ee90f556bfdfc540c4d43e49581460b48ec146ee9676156feda2a23390a03ec5afc990409e48bde24d00e1167e04f8a2c204211599d378c41cf0dcab8364ea75885520ab190cff3b324c7deaf16b367c17862338a1a4955f87da0a1b5da4ec1cb545d060458dd60fce281012f13e719dbc4dac4ca7f052881123ad6d4136d22e2a4225d85bb8333e9d1d124eb6b15870f57f63664961bb2ec86cc7967940f8c92d04191f2f9ea3ec4f963038bb4c36245e089815874517aa53cb20c050d6cbe57380a65d74b3ed83778602f3e53190c7b21819cb1f2168aaf869a5c57a997d9d7cb2b4df98b2cdba73d1d7742bcecb5beb234b00b6a79415ac596378e61a5953c801c87f467669b355b31bd347aadd7f4f1984ddf9d1de95bab69f2393dc4526db9d8feb911a9dfa805b462cdd825a8eb15864bed323e516fa2581880094463fca5e5f43467cbf4102874c0f228bcd3009dd05c77860c34767e7b038b35034bd705967ea781efa816e9aa97c7e93b4bc933ae9d418ceae0cf2cf42e9ca82b4a8b87045d11dd2c244d9eac00ad4897b9b83cbffaf46f91ee00092e0e6cc7cb533ff384374e05609e9192176f7665f938007fefa9750ecc4ec186d6fbb355a974dda7d9d97d1e9d7ae666b5020951f2d56f9885b9cdffb2f1b59d39743aca9ca6cf1988369e5aa6a0f065b1b7b9a1a81e4f0b77c88a87d709f94af9f15ee23b1c25a28cfc944f4b3dbab1f336883d86380bceef246126f0228e6ea8aa1be83992b91d59e2188a3c0ff1ea6a68b0b12480bd52720e6f2e681cae1ad9b98fcd03eb5d2eeac5888d50a71aee9f0387e09b1467ac24b69ff0364f3ee7e531d0f846cf37a4e36d98e1a7b608feaadce873cb6ef24852cb88043934c3bcf28c6488edee100de2c38af1f8b795f207a57fc5be0f8f81bb0326b8c3f738bed181126f103f20dc8c0f4a7a796fe89858e1da184e7676a2a38f639eddfb9b628e8cca1eb313b10946cd78b4c102aeef53e503119cd142093924846a51e32adf82e51019d4b27598c28b9fad71e743d7d2356dc984a544a7e32a79f4664a7a367933fcc25e818369f672734d7083dc68c3ce667afc8f179fc3876d49948142ce28a279a32a48241ee59ef957c14ddb92d327130f14c315945690c21021db1b571411cd8ae6ae61b042c046020301e89080f673ee0eaf721152d4386ec1de5f08ff8893f0b6bf3a665050703231bbbb9e516f057b56040e1e68e139d38c7c00321b648ee97ea8cd62c2263cef0f4da533140f399631fe3b8b081252414d8a7af9ee09989d1845f4c719c0f031a18f96ec086c1b0383b143e7336a910ca8183b112cca18f2f0e35da8d8382f89ef22a47070cf204c7a981b4288f697d4be99e7e6658652ea6ce4d0f91b2827e0ef18fa6531fb66ed5991218f0fae7fc27967abef1868c4aba354227d4cd408b1fa01921f6650983ea133ebe354c19e694450ae93f9b2ca7297bb4e736d8fc3b50868eaef9bd86ee2ef58ccee62917978b3d4d00dda33a215dfba9328c029b31f5f4e8df522ad3cc211e7e0a988c803ffbc812ea5f0173a2a4f907742e408498191b5a6c767cb377870df4755281d5a3ff99ecac9841baf88b09b45ed6ce5c976e4ef7c7f56c162b9098620b922a20c464d1cbe20d34e637f8624deb2269184e9bcd345b88c40dfa5918a5e4798206cb82739e0b1c4e9cb4106039e3b1017129288eb15a09aa50cc0375c12e31fe941cb6918121a2a1fa188ee45caa997066e256c02c333b3edaaac90b8199ce11c06dc62f18a3a51042957146f2cdeb179c8171f73f41a2694d6e65a79c41a2333e3686486137b1906099395164c3d845c06a30d2d3ea12eff1a6da440f7dd34ff895753bccf3130122cfd22873a4daeeb90732cb568ef8771f321d1c6086049765efe875f10bb7839d43b7dc974a4e3ea6b063d171a9128e4e983633dd20f04e2cd64584654b45e938d7ea7f08e527e2bf0f0a5b462db45d89adb7fc61c553ace880d2ca49fd869164402117ac28dc87c5868bab71fc203b84f8210798e72de57ea7de9585ee7f910f4e9ad053bde203cf43640496f3c73443fe4a8915d2196053ac175500e940f8e8dd487e84b5bd9e107b8ab4ad30ef8d69632cdf039e433032622b0c835fefafa45d4f0c41b2233e474107d20b50485da81339fbd20911d59cc7d9dd4bc2e0b47f3b1b3ad42374c0ad08d40626c076a1ab66491377d3627a17b984ac04d13aeab1bd4e43040060903d391c4fef60a458438d7c2fe63ae70b38fb8840c2329f2fa43aa03e562df0e1629abe281f63cc7c2d8a5ac7b075a3c2f162aeb086d9e54b0c5ef6134a5b4db468dc77fc1103a92570c44e648ed4131cc630c4f03e4b2f9b7511420cb4c00a0689964a070dfde5cf9a82357cd17fd6f574e4a26db6d8d8a78142fd45b5575a21447925d7a6256415202a0ccec04bac5a174a843b202522cbaf25dd125310a691bf6c04f83ae6c306b34e697b8337761eb6eff4347c62e9d035e1aa30be9b033ba311bf85cb1494fb70c6f23fe780f72db9c6c8b4f8c8093c516fb6ccb982db2eef75239e5579868dd55643c2ebe773d84b4c3b53feecd86b8ae70ed562cd9e7b4339fda497f2dd35cc8c30014654864a1f5c7568bb31f1c20ed38c947d63a1b34706d01dca23543b96eecd5038aec014d893afdb0179a73e5d7ce0287213013b71d6a2b2d3d3329482c9903bfb397f17d6b511895c0311840c6a055b445ea38c1154184fc64d8596a1db590d56b5b888fb9ddff83c083977a57560821c66414ec6de11f15a98a0b3d853df49d1c8a56026226e7e1e9bb3c3b716dfb864773c06cbd39c5009d5abaf5509955651b9f246d50f64f422887de398f92aa79f5428d4be9006bca81856210b7781ae9bc5ac9cf2de3a22b4197e57a2fe3619569283b606b446cf0422d314058f148917e9dad43265a326e590a0451a8a35dd9443269d858a0666f34150da644c64d8e4745211d013009fe4f04dfecfd7447e695f12454ef5b7c9f3350ff8ed0761ad73cb4871e2d22e6e749ceedd5321ce6012cb316ebaa05c9dbf9d5234a75315ed5df280c1ca916e121cdc236871361097716fa3b35a660fd5729bedbd139ce34db8ab29c177fc656fcff5af6d21c7c4995cbe4e99c75a567332bd842c339971b63faca21959943f7d2ce2e374286bb8d06a5582151850c71fcb441b50bbd256f34a23fa2283909e6f34559605293d39015f52c82df23ed39f8c37e6c15c94554f3a5e7d4c593e806aea1ebbcdd266584dd9148645c521261dd70a108b051d936a9207bb082cad684bcf30d9f36355c13169c3c38a279e2e257dce65d7f8c60ea11daedbbec5ef894adf4044260f15a2a9e4a725805d866121f171f384e666ac0169d0e0d5afa616e58f48c693263559aabfe6ab7dcd9632b481c40770b702346b6148b74b6324cad32b362398b7dafbdbc8633364186bdc20d0881ad338f73b1644a663a7f3c290a1f93f7e0adfeaa1ffb3efcc3c20006b3a4b31cd1f5c3c01db6d2ad1f418e6e78a400cc1e7b2cca06f8a391b1dd1d9bd35de692a06888d1dfab601c37808991d79a6cc5e87bf936bfc090865ec7afd4dd335e745523f8803abb9623d54a3a666d00d9b0353910b30f8afcb836ff7c5ec5247425984460b62f64caa234755d85970bed17782fc7c089a19a544a880b1eebc14c363ee50a9114844eb83cbc19fa662c39d58ef1bd82f1ac7ec5b29feaa1ccd3fcf197647048c8c9791f38d39c58d454cd7b694fe84eb67c3357336af946ea23b48f154c5512c21b227e63bcfeaaa04ae87e6027a380a5b706a22cf853a51cee12fe72e7ca7ec50de09676fd36926998d16d33f539dc1fb59e43a824411497b2ceecd1aef0615a597ccf57709ed27e7ae885e49a9ae992c9b5aa05165db4b1315bb997b4906519bddf1c8976ecfa6af7c9b7895272c2c0972203eb4d96247568634aaa85fa93e304e375341dad0e19d94eba0e1774f4095f3fd82a187049ed8a52ab1ad9422dbf3d9b6a70ff0dc527b19dce49b7ec49a0b0ccdee5d529a4062de19ee1bbd980e6adf3122d599344ee2371b4d7f15435a527b09d4ea3a1f8ac5d5b2df9a01390486a5df421a7405f806e4de1de8f46cc9e137a1b0b7c83ac53e4205ce2e7206cc2bdf39b2164c3734dadcbf2b669fc5b15bdcf4ddba31871963fe457857ddbb8ff884d7b0399ef0c604df356decc4cd68717bc29fb78fc66a11b367c1efdbca11f4bae259a1da674f5be6580650ed7b5455aa49f3ff325f9b370acd31bd359165fdbeaa13f31a64340ae631efcad27eda700bb479aada4654d2a88d7884547ed4fdb5849e2e5974fd7a768d6a740140efb3b52115675ed730c5f8110a6ddb6b884370eff78261cf9ff8ea048b47e2765d21126777f38f549c67b10e06cd870b4475aa9394c50913435a7bbde803d26413ba908cec5550b3f0e1d341e7bc88c15eb715925e66b3840b8d2dfa65045699c60b7c4265fcfe59e7868e6958174d90177620f6e7429744d95f4310320332dde683bc5576d9917c72a4441db07827895c9a80f83b5c9bcdddf1529cad3d7b6d385da6ced6ca54c41058c45fd624ca45acea8d4e8d397a9a743ec6b38943659431c2204d7d180684136a4a3d61b446400d9de162ad632f6cd01b48ee56e0e3b8043fc1a37f38115268ec2e3712c7a263e27853b6bcf28006b4b2f05d8e945242de1f9ac69dc96b5ecb6a9b6cce2cb9e61f4647f9065079f8eeb26b1842f4f26ff31aec8c9d601e2f3d42346b2e39ea550ac438a2292fc4b852f1ae43c67b25e30f101f8baf9b6004e0604e3dd88f067c0fc3f2f39d960f1f20ed173674409b6597102a9f6e14363f963d7ba694e21e574b397d501c89d7cdc1f17017300a9605e662759916c8c513243f786bdc6c5204578db07ca09f7a6e7ddb5418a5838a4d07a3f3b98d9a127cb75865edced017030ccf23633533455c63ca68b6dc2b046797c117199f4e8bcb78bf24c970e1d95f25bb29436478e14b1d6f353603f5a1c09302dc040ec7051fd5d2b67b6cd6a2ce3404f7c8cbb61b1a4a4fb27722638ac0de2f52f20839cd40b3de6c194cfda7bd0927c9e03beaf13d8912f0ebd7323831102f814646ae0df83f72b5f92ca8b0a7d021975bba41e1a6d6aed817e976c5c7f1409fff3f569fd91be85c415e1233be46844f648ade9e60a445b7c6d9819d9272ba4547a88299e87fc0ca23f61050bfabc553cab042091b71d813459328534bdea20cf55de19499be78a1febd20ca5220d90ba80724205c2c79ed0648b31e4cb357f3083fa2d40baba705261d07e24f0bd49c01862379b867cab68c5e68189aca39acaa01de8a864375e2b4d643b1dd73f92a55d94232e36b7525cd921f989896137314a742c402658590ca1f0a0e8233745b5e90ce628aa07c5a839a1ea6cf5dcc0191129022a06384e9852b5d289094baad6e35af4bc7a79237e26b34095e11491c9dae86468baaeb2026433efa38bb1f884db536bfb337e3fb9c42bbf53653fd3fabb9a3a1192f0ddfb08c1dc13283062428c3a3acd7921d1bfe0b6f3680bbd727feea3a06b1d00fbfd81df8670de150805510ccee254adaddb9eeec820375158e95d349fcdb57d77e37e4bf8f220cc7789e8532f33a24195e72af28cb4dfcdc7f96ae906317410faa957a77335271f67a11f06c64853ae19f0eeffd284e11a3f514a12ba681028b22825913816cdf8eb59b488ef06bcb118444a79a668ec6d396171eb9ef03adc121b9cd3436c92c4334e4d811fa59e2882eee1264f48c86a20db05fe693b61a38a0f410cef60bee734978f2bf38972c021f04e14b240ac9fde60399aa406a1245f39b5ebdf993fb3bb137f2c3941327e7b91b54d9ed6b1c59c95eb0d8dbb473b1442af4afa8689b7c958dc4579ecb2cf36253635fac682f28801f37312373147c8e7779b52d9f63bbb70ba58372cbe43411137285a3e783f150ed9b68ad4ecca9123cdb5c1e59551637ef484616d76ddc85151c5a912a0bbfe7e7c98e98b0293f86c5b4709fd1db455d6afd9aaf89c8b1fee09ccfea4daa25df9816a3a06ce1f80a24bfd1eb9f039112771467575622a342819afa2460a1b0d6f80d6ec42ca711d76cefea5fedef7727f85ccc09486a58b542f52e823ac2f34922bcb3135cae97bf69cda06853935ad638d55046894c0dad57c8f44807ea144a074e2fea4a0b8c8cc2dd3ebaf9f4fb863c1de4cb0dd7203adfab18cda6e65c0da14d9b9d0e1b0e6b7e483c19aae9ebdc9caced6937cebd1e2e512479c062a11acf774e7bb8a379535eb9505f4b5e16e2ada1ce92a2e8927067448e958180bc76652dcd4975ed48bb37d425d0811d8f41b25fa6ea00c45dddd20db14b7cf3c0d10b38a6ffcfa429bde32419a6bb69fbcb70fe5f800ed529241314479b428dd0549fafdf3a55ddce521ec03939955a18cb8cda2f3550b4f3d9b53d8e8a9c22150335973beb48fd8d26a367290cabee0094be097d3410a44fb28c9a21b164bad8ebfc8045bd09eec244d3ca8e49cf60f0d8a9e8d05811d07b2cffd7533e5e0e8f2c4631802dd9cdcee2736a142c7198337cb6e919eaf5b8337efc1449a1266d3369992436fb8ab8225c76594dd5e5ebdb0b5962bd9ef4f9e3718ecc1cdeb9f53b494b5497188efd2aadd2dd8fb192bc5bf14b1de22dc4341ad54312fb0eed0f902fce205d55055fefa45cdff123d7e2bade04c8853db7f4195c8c10804eb691d704b96dc6cb93259a23ae23e1c5f1a76ae9fa9de69996040c94646b9c9579f414b935d84d6bc0f70ff037780dbf987c23faa0535b898b4642d5145ae1b472ed5b9f57ed04ad3bdbceb2665a8a3fe6bd527d34913685aa5640361f52e00364de62be7c500b1adcaa92310b880306f5c376d7ae82f1b050342f28a2b54e28e491142604a43c98ab360ff070e6f457e51a37de4537e097b0e3ea2ac501a63f8cf97f3d6db198f673c10e29d21c688c2898b4488903c6f5ac3c4737b1d5004d137a86bb15c0fcd090ba5e42219d026f8d8f2848d31d0f90dac423cb75ba7ecd3700d6065c7b81d237c277d8062459df83d1eab50ba865b15c117983f8e9c40212d6de1e76b189b0ee108691d763d20229c0051247c30dddbdb3a14e4d69e09f914384fd5550b55de90a502db8f81df3d0bf33e8148e0a265373f93b1af792de3b69363f11912d170b07665bc849aba574dafeaed60a471255597ea544f6d5172aa43d3333d9f1bb68f9d8eedad3b741a2bd21f0ce6ab06e6c947b91a090b85c764231a898e7c315a9e1768b9eca83827b92aae43a771b7d1d66c8e844d36540d67adb50e19a89c9184ce7c7ecf955e7caf1f454d75044957d54d498daeeb0dcaa3ab0a5e6f103f6027b0c8e21e30b7279f06c19cc071b98746b8d2c0e149461372b430b0d3286262156154ed6916f0c8a4820dc8562399e971d4a12e89690dab4c8d28b30be170f9c8a534cd91d4277492fa45996083b9333e9078508c3ccb690c3fd423d7fe2bf594c8489e44fc929831bc199a219406885ed5f00b362aa15cc7604a17a38e85dc3dfd092c68038516f208fc452fb4fa88c9176d2af8557dd242c7682faa8da1e730168afdfcce77760de5780615726b025590d92da3051193d013699a5203571153e4be4ef16717c6367b61fba8014a301210526258d14ed6b73d60a365c5338bc60486c92a44ce103f55260c9896a069dcbfeb0e3834e41e494cc4870481e025d732e05faf0936884af1e69632bc16adc7ab70a7f955f7f82adccdf3e84e4249cb3559a1ea8dab4f732a3ead954279720de66a152dd7e6ac718381f84c28b42e64625a040b6de681fa543084d732d64a774653e33588cc9c8e92788725bc928bf4a90d122c4fda2046def513f6aed96d24c363b31deeaa2cd0bbc2167315301aea8a320778ac59a6a9a866a0638f6b2c8192d2d19a5d957bf8cf51036553ac474634416e14bd66324d9e6db380c7b057ba5813acc65b48ce8abcbff74ad0e233938cfcab854af10e0873a8710a5924ba4bcd07b5f1b90a7f8608608215062251e48cdd1f3eddcbb7280ee1bbb873418a12688109dd342dca4a07739e5999988e8883d13f5fc8d3b1dd2203092a449ba500cb1e25a554558d81b0393fc46d260452ee645fdd18eb3c7b0c68dbb87034c38feb61d85b762e9f87362208afec3748291114f64d3fe7a06b7608a0668c95249a704ce32d88c3886c76b025e4e3b11aafc2c40fabe81eeaa142cbfc79506bf292848d26c5e3b8c034e9af8980fe60761cba49c8d53c8c5f3d691a35178c40929a58c0d32889ad674f36d3234154e44f33907290c90e67076c4e8ca7e7dd1250c9e2e1eb010840bd276c6ee25e3da6eae7d8e1910a4ac7a42841ac1545b3d5c144488e3277f99fc201f129bd3aae73bd3c85c32a327f013e3b6ce40ade0819eb831147800b5745047a04b2cb07ea5e461e95d7dae88c499aed0488a1e563fab5da784f92fb4751da51349df5320a9b459125efa30e5ebe0efa55e7152583ab3dfee44685da5a8cc04cb91cb817cf3ee65e22762dbb152cf4dac949c56d4857616d74877b790f203f576995f85e31eb8c94910113758fb0bf671002fff176e06c4a3e2a58bcadb903932b727ab31e3482a386a774d6af6b778b552e07c6d9660d3b416e1c9134f9441b6e1b43a30ccf40a611b868808bb9c5447720081ed9a43df1485b5182e9071175e05cd65352936e2eb53ed2267e83b7e964d6eabeeec399c6432f30104b1e09bfee48ca514cb383164318ef063c8b881b35921c0135a310ca4f22284b9d0aeae16b1c9ee16094eb5e5e827442296f7c533e65235b1e511b31dda7646a4fd2cc1d1b67aa716c32bd896979f4d1fac53426d34d45a756ca25c1b518fda847a511fc3c9ecb3a9b42e2eadacc210eb8599e19f4f7b71e05d21e576a73ab0fdb4b829ea4a3853ccbcaa897b277cb0bca209963dd77517e430be48aaf020228cbc70d6c42e926bc850bd313ba0f424619ec67d990cb5442a7a206aabad19580e64ae12a219e0ca366872c991e41b408d628ef858c208d6796989061538f736ea05dd600ef095cb8007e170176a6e3edb78b7df27f077cfaf778e219d27712610e2151542d873817cf377b25d266fd765de3b05891f83808c194bad871ff5d45514c4fa0db8cc5d87d5c6402e4c2c163e2709475783a8056e3182549847f899c41c6ff5f3d442a536488b12f76e84bfcd1ec95405c08a73c7183189d17a1f125915473b3a9998480d7256d0298de110854bdc8a498258f9a03fba0c802ca2ba1e8e3501c871c4a4a950314006d923ab12a39c5178bc049f02283dd1035382e900d8adc81581cfbe1b4dfb75853c010a30c3257eae070c0979180961061806d0918cf31643ec2d77a29679caaef3ea9854ea33fd34426e8abde615240e4cc01a3e8d4800837f290f31def003304643b8642447915c5a66adc13263b727f2a8b82eb516d0a4f4d16cc305648d9cdaba7e3a5a1d3e9a96e5bad58275cd3bf81d1f91f8c35bc1813e8a94531dfb3aef671bc5624212697c1d25ead2da6e586968fcbf5b9d7fb3337a9cee9400a5732ab2e353e42438e4a44e818b7c48891aad87199c76e57b38213e1739a0d46ee5987504810d96eb5a42a589e717cf832e2a675aa776471aa3e4d6625c12affdb37eeb0711babcceef5a72e38f4848e6b650e7eb20dbe6197bd18eb2def3e16d0ceeb0bdca7cefe194f6884e907172b2f1eac18ac62970b06f62d56a2e169cf025c13ca1c27dbbd109cb2f0862b9394a981ea112c46d054a8ac48587dd59b92c145da9d122203ee412871598332254ce4300794d219981f13db341aef8c36167da1fb862583088ed70d89e03c38e4e65fb44ebd12046ba4e5df539b5c887801f22c47e611b477d61c55b2ddd94557ec9fc865137c2b0fa1959d6e2ad0cfe4213932858a6cc4941509f2f20193f7a940768f59e400f4231657471762e4217a1253a47bb7ba45e0859c082553995305ea5937d36047e1040e5be54c21c578315b553fb39729932c57ec87abb44a538dd2e9a14229a96f0c866eba8a520e0c84ed6cfa1e57e8f81a8db4a70b4bdaa495dfa1c976e8b5c92f2e59ed30d0bcf266809abc7857e795951c06d80deec776d4067d1553fbb6a4c696e46ef9b54b54bde4e9bf90bb68a952c89d99cc7cfcb00b050be0c9a476993c2c49e4ae5a54505ac4c3104e6fb995cdaa591658c4ce02b5921d121247224a14591598328279848a05daefc1a5290e0741df15efeca455b86bd5e615f233006b94086960c088b3c70bd6f972c89f3bc2e79fb93368a3e7579bb074880d0b51cba2a4658cf83c6a6fdd6d3fd54974297738b2d58be18c9e120174661d656a45fc2ef974def5c93a7b2db5e24481fb46771bdb1b8c1e1d858b84bbd984ac13cab19dd8846c14c643a83721093c112dacbaf87a74ab723909423cbff584dc07fbad16f981dd60d81178bd76d23566bbf766e77518cdd350a0199ebb6bc32dfc695e379843f0dd301d3d046e35a51fd7ed9ba4fe1e9b9bb5689899ebff137120af01a2d1dc68e08bf4e179c9418883ab82112a553cd506e339a1b48782c8891071796755dc713eea0c20a3855a69a2cd55aeb93ef5d442b139f8e69dd1f2dc78a4ceee2b0c9cf0df3ae1e9bb61ec21ffaa567fcb8d50dc174e343eb0ca3718312bf16888edd414a3ddb1d644087ed395d10767031ed792e0cd591a55028828a257d4abc7656ca2fe3ea0dc6ac609176ceb92fd629c28ee0179723ab8d174c821cda48a91a6c317e178626e53f16fb20b2742b8dd73800df074756eadda83af3009abcc19eff36b80488a527ae8bf5719a12c3b0bd4e5a98b5dfc04ccc31c23df32888ec1fc197d26f2e484c5519114dee59529425b481906e0cf3239858a986c0d9e93cbf28773a4e1f133444ca12b9b934ba6515239551f4dca89652285c2993d0f4360547b30f20e64548a4ebf2ca44dc0b481faeb59ea5a6111907b4ed2d1fe84148189c8b1a8d84a4cee667e06b1e2d2ff677de30e70923ef554f175e9011a373be6e99d8a8299c13db923e3f2f234d8477eacd8ac47a9c56b97478431c0f7993065177a1a1522cfb1c88c1e47b9a0d8b4eea8b9a75d35432957c5fc4f9a5b15d6b68d0af483c50d3e97809f782f9bdfc025c575ad7759546656dd39357d050de280852c6a4d3f57e037866f9838ae595f637d4bb5fed90a38fee1c4fc12baf6db24e83f6b11756a203904db334b6df046451ff267de3bf2138b61bdc5269b538a7e9895e59411a84bf147ae9e98cfd0cd221f1c40e9fa7383372a6f3ca5f88d43eba34290879dc7a9f69cfe3cc2459b819b61c627243bf58aa5c6a7267ac78cf042f46853d9b06c963057574fc01a8db60a3dd886596705cd280d543274e3b8f8e96623c1ac5c866ee533667f6497c312ad9ea725ab6f53478579fd871235a96181e1193637de84e80dbfc105e7572cee58b0188d7f1f6ef51f24e8dbcacc0232833af1324339273ae3eec10f8b0cde8cdeffc02c572ec49010786319d463ef2513f320146103ba3b82745c308eba9a0ea58266fa7dc08955740b5c605360b57c5f66d484a679b773ec9080c49501be08c87ebe00cd3cc310ec0ae14caa4340134e3a5eef9b4c21c36e17e94fcf1adce9e476981795bd72a191e668c416d2f6eeb6e59652a62465070740072e07dc0bd93a300572e1e675c7820a9d5bdeadd42b485ba43cdd82ee47b89784eea7d7fdf723755048f7938988119095cc0947cf74aae15538971cd6420eeb7b2794d3ef9813d40cf8a98b8a5cd9d6a71df5baec791c28a43eed7674617361f251c3c6b2a4c54e9a2e29b0ea59df48848afc5bb693c3dac9c9a9072ff221284f5072e64bba27a8b09d5af4204a1d095585448c88a8fed5bd9cadbe71f8ea42229a9733ecb416815aac34208f1f0e7b5195eaf10761fd2afc593deb71c8fa9fd56af5abb0c8ccaf1e874054cf0a8918adc21f11bf2a0c419553b0e3708ad11ef49d195d56a864e2985505c6de6254ff2510cb8d64b26e92a6d3ac67b319d5e32d79227dface5c25c03b64c6913bf325173c36d2152cc1489f1476c8dc60e44e3f09f43da99346bb81bda5b33ff6960fec2d43e099bd1f7b4be88386df8e077c3b663e410acc7ad58f8dd43497a8dd36afbf11bc67f2d175a9cf7e273bcff3b13d15f2fd043b157a4beef7b8e46e2cb9bf91c03413ad7f0a5d216f2473cea80b55d4471794b885d39e9528da09a8ad74522389c136a42e22f0d8484923914ab57aeba386af1f8d444b809b16cea816bbdffe8065142bc98f51921f230f4722d60799628c688236fb2912b29e1516f99945b4223faa67fdcccf204da4a22b27122ba4edc17cfcab1f659415385ce118831d366b4d4b93c98fbcc621817d24d67e8eb65a92a4cff6d652912d14965a1acea4a31a064f30004699fd14c8811df772468374cf853f463ff73b4ecefc7e2a1cef0d2507ba30806176286228b8be9cd95a6e6ad16e16c98643e05196152061f6adb5d6edbfdc9398f80226db2ddb9f5062748fcd91fdb9c5beb4d6328151629dca23fb938afd293f13e48e0d9f6a9db485912dd26802bfe8b3dec88c035fd47bfa9ef7ac9008ebbdf0e7fbd41399f93ef53faa13f2ffb4de7b16f8d33debbb1939e03a0626a2f91fef67be051a01e99ef53cbc87267cf92b9933a3d77d2034effdf673d6fe3fadeffe8608f09ee681784ff3fe3f461e02e9bef53fded3fc8fd116026985f77f68ae7d1a5ab017befc573ff3ddccea675e4030eba9ea59e05773813a2ad44c1fc7182c6746f4f3fee52110eebdd4e3f881c493c91cd61cf8833a288158f6a7e028ba9864b2679e91ad2b145d4b29e58b0a9e7276695a7eb100a7e020a5e38c1aea9e9f4535e35c629ab59e13706ebeca27305db8508a23539dce01ff7ff53466cc7819353443381c314e38ba38d77fe128aac21a8e327f854953fa1196118eef5f138e622b1c63b5a69921d6100e41f07e27794f083812e64f461613bb7c275d1b7a0e0040d9b4ec34da2686aebb4d7bb282c060b87bf794ee02edee6edadd9476373872b9a7cf321f8539d9d32d09dc6b4b8f05842be12f5dadeeb59dbadcab7d4aeb04ed579a6f485d2d23309db2c55a3f2d787e8e9672a72f0d3fe70d2cc051eb53aef56f0872ad4a3674c9f5ab7cc09b7190476f8a1a92100293e96fb8a3a8354a84926ca5b243aade106292e9d35aab8e1836685143982a5c706156eb0fb9d62f5799ab0cb629d36f117ed46a24268628a2706109125866b516e54ac549ae55860f4383951c198431e7c40cb6e97aa93db208da9466004388f67042b24c5a70a62a044393a002212a65c9dcbe6cf9c2a54eab4403112f6c36c460c06022d7748928c05e9659b278829100e36499258ba83c62f10ba727b4e5cab20516368f3fa5dca19fa3ed14789c436456b42642f6bceabb4f2993974c2514322d273c00859fe0cca950f6e7a3e33ef5db4b0fb73c9ceaa1bba2543887b660224666aa4fed806b1e9f0b7fa8c2c0e31ca20d557082a13a4818ad4d010593964c8fc8f4471911f4e7049a4095fe03ba47cab7639280c9c2e9697ecef5ad222929bb97dd6f985ce9095276e9495b949e38653adbc79c20384cf2a0603b09da4cbe6829207bf27c7f14b05fcfc969d1715accd101d71f71b2879fd3a23f8665ff6d0478944e3eba7e8c752e830a5dfea20c4a64f7d9b783c69716dbdb3e2549292500b2a5d6565ba96f1b121dc8a38b07198490ed1e065078c4221358059afb45e802dcefcfe19fe2c8602f13ebedaf17bba1a45f0fbad1c740fba04438ff948729d057e1e62977eaf75824fbb70d652cf4b721d1fd5a6b7783a0088f3733d92a53a6d054c184a60a2636d890840d33ccf032a74ed1841f1434814e2174ce06cc16a5218ec5feb5041e63fff37380346cfe30014ea5607c4808f12121440baa0d50903aca1160a2d86dabb556bbd99e5270dd68583efd1b06aee1ab7fe60e63e10d392738f64474cace19043583dc45e089007beae7e7108147093f80cc94edbad40c21070f1083ad0dc4116d282be18710f865503850ae6c99504a41c19f9b5639a7a4f425f09dcb90c8becde958770cbb2b05430d47fc426db7dd22d28be12f29ed6ed99422e1eea8975d38b4c56e2a84a951151b266fc3e4cbbe9b531a7ad98244f75e4b3d69690949949f63ca29a7cc3165eb88f6e30d30bfeed4dda5b4a981c17db395baa49c51c3454e96b5c67a6d6d12d34b73299de15adea52cdcd195a7faa8089f8a8ae0addcdd6ba5b5d64a3b9c929293f24ab939b53555069d73f21081ce10bd6d72a6f794524a29a5f2f502e1480d1b192175e10eb9dcefdee1f8fa1173987c569eb456bbdd71ec57e5afcb0e4af9d4e5e3ebe6da79524a29274d4a19a502a594524aa9d7b5524a29a5d4fb542b3cf37d18a86cac5c18a75836683c0ef42eee928b044770aca19452997f83b97524a594524aed6d5fdde01aacd68c67696ad4dc8a5975061c5399db6a64cc70d9bc6478121c0130c3456dbaecdfdd6c90fd3b9c9ef18d5073879edc49659f8181cb868deb06a76ce4fe8e86c7b97eec1feb3bed9ae9775d336db029a5e1b5b9a95123fb77e1cdcdab064e8b8667737f0700ef29695455cd8df355708ac320a536b36bbe8865595d4d4777772ef07c1fa59452ca9aeab5e54949b3497a299da192b2384aa9ace102bb7baae54eb5dbbdabcdd6ceabb4aabcefb374deacc32465e7eea56ef348c9b1ee8c47371a8f4a4a6dabd64cf95eb3943b94565aa90c8f4aea140a6af4dae5dde6a975ba3ba6b9bb53bfb91100986aab9d9133f65f5f0f6c29ad332d1a302d9a992ff6267405405691810c6a6bdd2a4e8d960694564ab3b0f645ed97d60be31b3b43ab9db9c27ea9f7d28e56cb62b1baae7bd985b24c9932b6be36d059bd6e7476543796565ba68cad3602b8e1d9dc8875ee9303ae034a847670894c892385060e0f2c0d1c0f78aaad0183f3e2e9a1d5bfbe2c0daf96563b64da4a3ff09922c5cde3a4fb767476a7d56e38346eb27c7a656d819c84d940a062cefaf007c3968c97326a70c277587f4d173c7ffc57e80a4000bcdb354d19c8c820834f47fd1a35d4abb5bfd91a2d0dea8b34d4bab442e3d3e12ab0228c7e58b45e18c33cd8a7637b1b1b78392d977f5eac4c242c66789b6c6021a12934a9e0a821f349cd04d54766470644f4be173f1daa176ddcc0deeabf1e7d801956d779988ad7f94af50d994fbcce0415c8c88070a9af0d7456af1b9d1dd58d8ea7a323bb4957d66e98306f34ce104e269b4835cfa46945c804a1c9ad5e304436b9e4709c5b643602b8e1d9dc8875310e3a0eb80e3af0a4f4b9dd003c297d068063436a1107081a383cb034703ce0a9b6c609e7c5e3d1ec49ad25266ff5822ac5faea9161b9483497c93e1df46545b2cb200cd9b639adb488a58f9037fa93903bf5034f4a1f1f4f4a9f248c5d5eeb8dcfeb66a63978824e0eb23f75f798f489c91d9f14088cf1c54e0bb3c316960d3c44d1f4d0d492e202950f543a1fb2b84b5d23aa218fddd460f21cfb293799281fb8c8aca7cd63e38a79f5d28725eeeb28f900743be83a294de48e0c2e410f2f5c2994789062edd74f8725c00e284852da61092d1b614daf88be3a8871b5e8a04408997e875d2efa2e7a058728a40ce145174e66f4c5e6a9405cb152e562af66091e4516f20eb976453a28c1c9f4ef3645173599fe96938229327d6a37a5211e32a55fb9e4e420e50465327ddafda083031513e4e042a6ef395ea26891691352e0f18a3a50d8cc8cc04d578c1607585b68a0c4e0021e9a8122f4c28a5e160f4c544d2c612a4e3e033411b5520af29c545328e94690f44141810ba9273c2859b8c07550bab80618c1500a4a18306c475c1420254c51624862002786a2387161a916258a0b062041541422036ca145a1c2845e2e3480134b51987819c53aa6650b316cb8803924b028304cc1ad2cb33cf9a08114d826cb2c4f48884cf08c2cb33c49914197272d32e0e2290ae604af90d4a8823f2736ca60d6908d21f08a080d76c038a9c613be4c3570c03335510b5e6599258c164c61c0a8410553174416b08d013606e69e9c00df285b4809e307518c510d1bb097651631546a1c65119374a41613b3c5d6a493392924302bcbaf5f18065f292fe82370bf510a5aa048ee14787bfbeaf7d519039d4722790b715a746f5197c5c4609fc205c4eda2058a5db885f108c214b1618c714be90616dcf09447fcb48ad32d98eb965d0577376f5c33601fa9c733a6e011a7bebf9b9e6cd93e02cdfea927f8738e965212a9beb08884a285312420a168610c5af6fb94e3387ffad465a8c54240e58e87f52be55e8242dc3bd9bd48909ffa1da9972ab8a42f4122cf41dfee0d456858e7f841ca0f600b12794f411cdde37d7f2ce6b21c6070151ef2289b7498b8269d2b0afcae16a58bc0c3257379cffd9bb70db4a0dc3102cb5047eec8afdd5feacd5d0098b08ef4997912f1a72111fa5eb885dfe22482df3f4ad5a9c019e5b04afb81c4b1c3eae3f7503aacfaff784f7f23a13ff5fd2922aaf0e77bff2dcc6971ae5ef58f55ac577d10d6ab56ff839f151209b2fa51fd785618fb9574588ed81830f4ab53a039a32c14a1e14f051c169ee0b0eafd98d7f6347c6d21fd1ffc1ed2573da534a4d1b0c8cf1c72587dffafbe9c85627b30df7b2b68ad8f630c7d7f2449e87b9fc4ffa3ef3d10ffef5b416daaf0b5d5d716fec0db130caec0090536cd61f53f1003e5fa9c18dc42f5c330b8fe6c9a4935e9db42da6227bfaefb0de43e9532d21f391b7881c7f76a10264196c9aa26ecca4d690946269d4128773f16c99386eb8144e40b8018439b11790551153902055ea2663fdeab422233cb0e3800cd8a7020050c41b38e6a0ffae5ac88e442862a42332fec0f9433d79125a425ba24c70b4573b2d2626f791d61e9deed515e774322ef91bcc76ba4f99130f34c016d246107da88c2374273e98ffb7602c3a979c6761abdc8a19a67742427538b1a3b6a49f38ccd25b7182ec678ffd5c3bff63bd27b91910374a11f8563913cbfa1644079826d942f28c3d85244cb0e1a41e28129cae40df49745cd33bfc7b778911fcdcde57228177605d1bc47ce80f2903bbd8ed80277c2118b39798f87c31ffc1f0e5ff3677e5e53f541583346e16bb258af7a396b16e84e0ee5b0ef3d509643d603112322ac377afdf03e0172a6fa9aa74335e0091f58019e54cdab7e944d56d0680eebd5ff1801c1ef7dff83c339e4b0a079e4b0315ff89a0b50bdf740beaf098918f9f41088eabdf766dec88c45f35f8df747e6ccfb1f9a703239ac5f334ce187745802e4cc0487f51fa9be6936318da999504d5fcdffb0bcc7615007d15ef3ab79ef83d4d484aff935ac67853fdf7be16b862f04785ff341bcaf097f8c80cc7ccdffbcfc695eb23e48cd4cf872ef83cc3ccd4cf8f29ff19ee669bcd6d3d474d00254bea505ba934371636067f2a5169de64beee454d4475d94db432ceed44950ee1464f5a3fad53b943b2d60155220a73477f2aa38aacd3ea7e9984b290f94339af43ad2d1dc21e9eebed7e5925e28a5f5012dbabbb7839fc3eea639e48efc98ecc529bb3bec965da48d7877347436c7032692e6a18f3f2665b247ce205ad417292515593e0a0e90208490a70e52ac40210cd5202ba58aec3fa5587fea604576b17e92303ec78657c8b54c762bddddaa6e7882dca92fbde46a240716f8a3c97348183dfac0989412e63547f697b0fa29b0eb8fecdd7fc20b914db7392f40261ab4e840c68b306ea8412cb14d7aa82f7ece5b31b48527bc58b550c33cc1b48277d1c2e54038c3ad6d58704ba48262b663b3d25a690d16f097a59210458c21c4942d4244b13428e91c2658760068be27440bb93eed5ae9bf8e00aa1845193d512962d43f2f9411982deef9a51941e132bb7928eddcfd7a97a500168a64f76e760a159986f386e3ddc2117b8bbea82c042656b64d22348f540a6229cf20bce45492d29424733ad7c4a9e3155869bd7a3dfabfb3877bf94f2499c8ad9e5ee656cfd4864cee06b2c78625348c3e0d65c3c601f08c2040691ecf80a4995640fa804e7d053a54c20f1e4bc6abc210155aa443c20a04812f7d2f2e2a32a514082fd1a73fc42954ade17e377b7bf9e9f0d1dbc66ba7bd96c6451e71be933623df9f136886938684472fdfd4bd9f0af1a4f590835d54ffd6d0fefd8e227de41d7bc98ffdc40b7b68389f72bd3fadf4fd09a57f7669f55d5cb66071519953eea7eed3200273299003abc0f38fa84dbe3f6fdfa121fcf23bf768173cb7344f33d5ecbd6f5fcb3ce21291efbd6f8ff09422af0b04d8887b276dfbbbddedd29f9f2c5f51facc21b973ffbe0264cf8442c29898b46891573ef8401441d0c686c5baf77edf7b7f9cb4fcd7730d6dddb5928142fa90c3a40fcd15cc60aeb5d65a9fbb628bc0435ee7dd6ae55ef0e762c04745cde3ea82bd86a5fe0643f64c2aa496fa4c4c5ab4c82b1f7c208a20686353b93c674deed4af453e44471f9252e4ceec41faf4d76642dea85f8b724861ca9d8aa536538bf53beca5166b95d4932e0bba1916a64c64cf0974932dcd5b30ff205b876cbb06dc2b8041a579f05bd6db6f26b2c785ba903ddee430e943b34dd9af35acfe3e0b9c605ab48f417b05db22b037794f7dfb2e54e59d496a81d2619b17712f7031e0f9d43c76c4f387a25412b875381572c73a90f441923e7e8473c081b43890088236360e2404044e026f368923913bf6534dc224789c4dfdf3de24202bdb5a3b9de40e7d9aef4f02a3e030fbf762ecae1702ead30f529f86488064b3995772672d18c038bfc8f6bbc8f683d0a709914c27c9817d25773613a2e0d65a2640b98588b2fdb63fc14481ddc99b869ab2c5d247faf474923bf6b3ece91a562061f69998b468b922af7cf0812882a08d0d8b85b36dfb6058fb36083c7ad9be0365fbd6eb99a00cae0041d320041843d90801f6c068f0846590400c836fa8d0f0430f5e7e1012bbf80106b10abc7a0116633420c236592a159d40dc01d764a954c484175434d001b3b2542ae2c1865091932745405ac4d8a0c953161867a944d4e5044426d8801605080606930649b863a286126c97304289e8034c4440d8d082b72c958878f0614eff6eab57473a6520696459abc9f293e5947f6c88dcc98084f573b1828c269cbdae7379333a9547af87adfd6ca5ab16f67ce5ad4a2ddeb6b0042379ba2a75f9f43a4ecea7a33e6bce1c8b43591c48a704e1d69aa0a6bc413d29592d55cd274ef73c2a6578d2a3299b1befbe365b43c16d9b7f73f391306987636978423a413940381d9d8e3f59432303261e0600e87e8f36a98c192e9b9b215907fe8694fef0b1e6914a4a185dc9743f980274c515474041c152a58244169dd36ab7f196b0b91893f993c78e7443cafbde5bc311d370e6de5bfc28b7f42479073ef83043972fb490c169e61ea81285cdeeee2d031e316e9c7657f3c8dcb31ebabd79db7bd2f3878c6a356d4e9f41d3c1f1a64079f8903d43648f321b903d3c96e691251a1275df605765e50196d0b0f9dc93710ee540bda039e49ec354c90c85f69caf21dcca5269862a799c41b98416271641997beefdab7fb7c999021d227de66637ba6ddb6fdbc681258077ca56cda0a01fd913812119909e0464cf1087bd181ce571b3d611ab40f9b5825c0a747a9dfb5bc2b7433e51f70d689ed8090d6891c70c2b37067fc63a416207d528d149efe92b5f94116a7532bbbabb7177775f8735a6d9eb6eefeeeef65abcdeddded6058da4694de48efc9512980cd49353d214ea204a29add45b4a995c1aa294e21a9e2f7f8eb329cba7b44b8bf273b4982b78caaf1183e5d734a5d49b524a699d492df250f711e0299b5221f49fe60ddb2f3b245264e645ba6750e041b55aedffbf56abd5fe3f6989e9ffbf56abfdff77d004a189f4b964ba073b3d413575116e72393d54510bcdf0ffff1f4c172e5b269625115ca97708171dfd44ba40ddd3b55aade8a8a8a88b7e740d045dab0975a935d56ab55aad56abd56a35ef367501438518649eaaa8d56ab55aad56ab79d01fe9ec745aed76b9546767f267e77d95e33e15bd57b5f26da3ab5837f32cd9e4a2d39680986a28788f283a2c0578662061f55960fd16f22ad21b8a0cc8f6eb513beccda709824b9ffbf26717f9134b6c224d2bcdc3bdfc7ea6a79b9ac9752fc62ed72ff912153ca548ce225a4fa12cb97f9c4eb2fcc9448230b1cc1bfd92e674c57c856aaadb50ba85baa4165241518193502fa95a28053912d63d0b94123697963a550bf92a945d03f2fd2976affa7a7c4e85aacf87ab64119e3fd4505aeceea1bb86b6e22aea1bb078ae610bf50bf5ea1b81fa1337fc277b0f9617f39e6d06b56a0cb0102247510b5ce00596284f3851c4154e30e960adb5b6061f5cf08219c22ca104083ac9cb096e1212a37ac55092263b34f1a4cb17274e6c5189b842cabdc208870eb2b8e00b185b64698111f5083741e1c15eb1c24cd312375ce1845382c3115a94184af08014461d02045c962748ee155090d45a6b2d81184b0606ebe58a1a85972c668a349822040690c00287114c11a6c9163329a7796e9e33c9262182cad01412601ebae01b9e10a203415d82a3329f3d8284e65176c90e45a61ae571d226d05c32859c8825398fd34af6a67214953d004d8628ba72e5ca0c2cccfc633c4e9072e4055f4039e1441033ff19348b3cc9aa8e58248bad73e0459dd8d6850a76d8c24b91951cb4f8a18bebc08154134a6e70128515485e70e1160890baf9f385807bbbe97dbf79de77537f5f08d86c98fa1b522e05e688b5d31008f75dd87e432e1c63a1c3a6ffcf7dfbc3fd06e4be0d8170bf85f8b3c02c937f8ce8ffd84ffd6cdfbd7cff5e08b09f7a20f653a10864fb2e7c8771e06bfe1b995d30f5f7132067ae90080e2be46cfbf153b65333fbdbd726a6a47207676fc0097247be6784e7c75a942f71748fe01987dc915fbfc0217da8b4b96a0007e562381a70824c862079b6cfb593390c662061dcadc9a59e2c3dc81c09997ad0352a1a19302053ee3db04648a63f45ee7b0399faf07cbfbf03784e4d231407acc608ee4d11ab570f87f4a13e3bd596872cb616ac68e10bfd23a4ca6502b422b43801921034e952a72c81258621ea0b2e822f5c1430d45a6b350192db4215a0285a40e22f7c0ece52e9a8288f1fbb5e0800093cbefccf1175ac064af866a97434248e81b72c958e6a68131c31b9329924b11c1df132c6568778ca5d964a4378916189b5d6da301f688d59d244eb8b314b527004e83b526bad958b0e1c145c8078a116610598302ab8428c099284f1c3064518474a66b8ae219686b052a543681e2ee575aa6f05f290b0fb52ca5b94650f7658d3af3209bb72c9089228e3910273ae091cf852bc2fbf1ed4f3f6f8debb59db63ae902b5117b61ad65a74a28eccfed441c47aa7bf43e6eda6783856ddbece3cae3ccf48f6c231a65219c92a8cf11bc9d88570bf92592564f51cf72bafae563be4abbe7d16a8d330fb1514b9d48b62f35831d63cdc5bee6e7fef9c77daaeed169dfd252592f288979440ca7666bd68cac9f62f879f7e3a2c70819953bf43666ebbb5077f5c0a9c92b2147024a9a3af7a5538aa8c641a8e3ade774672178eb1affbcf48fe7aacc6ef57a191bc0a475927a4be4a0a517d55a55eb5a382ae56ef90d95efbd95a6b3710079939ef9d3766a1aab413cf5a67752ab3ba2783a58023b516a50bc7f2c764217c9db34e20e89c73fad359ebac4dab774e3178fc1ca7230e32b5d6d818f27ee02bc834ba3fdf2bad94d254caa6524f53a930265bede05d1f62caa72395024900755cf04542cd1ee851fb77bbee479c5a4cfdbdf47e4e419939b0befcb617f5177d9a690c31269c20bc796013d162313451327b05f929229d88528299117d2f7c059929303935cde67b2ee6a6eb240a542a0bdccaf2bbf995febf101aa02fa8b46df844b5643403000010042315000028100c87442271402498e8c22a7b14000b778e3e76582c1a089324c7511404418c318618400820001162184254441c0020516c50695ce0c5a843790fb716cc6ea340f88e8727bec67ff44a591a7d4a03d016515bb9882701a944cf604088a9ac816dfba7aebf6559b1951f11acd87833f99578132e8b05894b0396bc3c14fda0d81c251ae4becb11b87d6df5b3b22abcb0321eda997ccf0553aecfa2271e5203fbbb049c307d2ddd77ffdd1754039b8b1d2194851b34e639d94b16d7181c34417a142328fc0043766f574ed165c011b932275e5aa5044c2139dae091585a8e578a2d1336986dcef41d8a343a2909289e66c6fb733806b46ba64a4123c90e2d87cd49fc6e03bb9a0322f89e04b7144b6a4664b0e235b373cc9dbcc5d5e9d5fdb4c8ed074022da12347a37f75f2637d809ff5aaa49c1207e4dc0ff36febd6f733da9323a466254a02390fdc1ab8de241832d2227f81dc8894000d566a6afda11e08d31ac4a34960b097c22b653a056ec1c9659af2a2c92d5f952168d6d5986da6a861728ed695f7c6c98bcca5dc585b20d0518e6d7556507c6ccb4a8aa2567ab12d6b5f718f3e062835b4ed068940d137ff25650e8471bd865f927fa139ddf988d7bf7af073e58f322439d70bf6c7bf81e675361d6008a9efe03c9fedd952df8c548de5ae9927cff1c0ede0e57e01cc7b5c0299081af12e7d99a239d46af049179cd0789f2245cb38d45bf2015786f07f8e1a444d1c025635f92dbaa09a50a1de1fdeaf02ea3ad1850150a7045f6e050e9a2af0c47432503e80f843ffd477f8bce44b7a47a9ec4daced89ff4d5e0b1d45d5fc0c678f68eacf4b061d677700538d4a1cf10b87e09d69bf802f9217aecc4edf23d0ace0ceaaac108501a41eb2d5ddc178b53990499e3bd7b804cf315ddc373618138c396eb1b57f2049a2483ff4f90f0322dd830fd256ff00c58a483c6fca47dc0dd3bae6e76f484a42bfe1f7a8d396f725d1c9a65527795a846f3d77abd5516e4b1c1648c87d555170d21316494f28c21cc8b32eba08697c5b3e01b72b4be9a80c61682a1861b4266e0a026a09947b0c78eed58bd7b674232d34f3ee7f09b099ec69e57efc7cd1c6a0907d1d0b3299df18240ac9ed79905926e52d70c2b08e8326c3dfcc503b8f21d7e589eb1260be009d1fce33223a67972f8dac80c55953eb04bfdd6b53088b008f77a3471df79b12c164b189474cf3efd82833d3db6ed2baccfd79e45b364011f2ad740de9a8407745e2a8d7de6218527b48a82ad9e5979aadf0ad542059db4e4eb07f06d3b5a0f0311f719d8e85d7f1080d21523f8b68c310950d4300d1814d8d7b32127e42d4b5553eac0874ad205acbfd234958e4a54458423f57cba213eea0326806bfd7a5852a4fbbad310b100e89ac16834a27c0019f7406e91b94b91acd056b1e086f0326a5e1fb51a64bbeaa56bafaed72b597588ecc825c9f9f1d18b473276ebdea571e43284a417ffad8d58c788d207cdded3c885732d27e8ba41f2c5a5d03f1765913064f5e9dbd73d28da823dbdf12dc756b1106e6793d369c239adfb7dd33f786536e4189798ec9f9ca3cc61a638e4a5ca04083d6548ef7bb3e1bab03d208abb117d6662c2e16ed826661bd35e3aae3426342d13bb8e7c569d7d719931ec6d173374fb16bcecf9b05870168fe1508c6eda0ae5867260ed3cbeaa26c4815302c657c4915a5af1328cdcde240804e073031cba83425810c6b8013a379c53b30b17e3527a427b35937213b184d4132e9948f5438601f71afc250e2c874f691eafb9a1a88252063870888bc49ccd9e60c85ba3651afc0b875d9e0f69f5910d01be54d96a5bc92d9dbe7c20450de810987c49b1ec4a39c71e988f3981762514d65a19446c0ed02a648abf4b43e7910c4ee677dba24382c020ccfe9f8c90c81c12fb4c9a675a66fe31cda40caf4f4698734d3d8d1041ab89e910b5c700634d049f665aeb12d6fe1c771995dd38518b11a01f0fa0a3ebd4437b8733e2628aff07aca3fa00d3b69c9fbcd178bae094903a04265bbc15b8b67701065c920096c17a21aa166da90b79884decf2d5da173f54201b7919997b71bb9ab38089fdfebe4cf5c88ad97802c2ca559d966c49b1caa2065e3bd18cc909659a7da867a78ab4566445521db5e200f5698186f40151831a1404ba072a1cdf1ba4319e93458fa4db89c9679a2b4bbd895ba381cf36372b3dc9bb3500e6dd863033007b6874ab93f0e2d795e650d0d768e9eb827b654bc44a5cd6b404ad7d646d3ca8917eb37868c8174fff3eb4372d147d9d1ff188e24667fa9ee0968a7366c10e2c4cbef1adc26085982e28947bbb1c907cbf8363e5a3c4a5daddcf20f8e96edd4c5060ce4b4d5102f4dbe2cfdc6b523f559a4b7b4df7592db24bb0fe1038d4813adcaa8b42055120979520b5ca66ce2dfb229f4be442cd08941a1591ad89622a79db854716fe4a94c5faf8e129cf94de03152c15338c9c68a0790bde558927e4fb6f28b26fd68c55a61a1a8906bd6302deedc17b2b996b196186e54a13cd3d7c2c407a9e284ec8dc8add9b6fd1049892eaa0451f25be43898ef8d567697653862cc6bc1628fa5672906520457d2bc90a4df8c8ba0d041aad13904562a33b797b1bf06b63a33bb5d9602cab57fa57b4c9c729ec3691d09be1f09415dad8cf364190135a7898d270a88c1c200671b8e1c767971b892a02dca774301a69c277232c08c7739d1a0ea04b05e44f67a84c08d1900afa40c3b704c9b44bbef45b57eec3fe374b339c86451ce7300f0d58c0c92cfc22850e9c33d3806099e4dea1b02589b7d8a2713799084703ca7f4e5ac476d70fdc7a6979f264d810747871fca9237808392b5bb4ca721f4c99a7a82070c65e7794c4f35bc4bd1aa66fe42e76a6de91544f5e6a63d7d77c9e190d0f5c00c91a5bfe856dd3770c5e8b44cc5a6b145eb348b578e9e2c74c6b9abf4e4c67c473c8e91b57f595d54aa527c8cf69020f5c195dc4f3cb351a8ff6c5aa761bc46aa706c7a7d915363791a760476223edf30a6ce021b82aa8a7c3aa20603d009d37640a3d91e0e26b0c0eac4d5165ac679aa2884cec34761f38dbdd1dd2ec38dc3a31b4fb88812546426777cd3f8d24dd4840b301bd1f0f7945cdc8efbe6fbaf30fcb3f6033def57b0520255f1126e4605fb55d2544a1d3df0f77c64eef6a49dc46d89373c6989547b44d06925186815edd9cfb0197be2352f67b36f602d480af3c620ed5a206320a36b1529ce2d9b5038061bf04e5de27b0eda846f6623c4a221fd76652cd83572c9f2391b4600e92d9e3729e0c005af50d79013f851d7c98144ec7d9733a329861b046ae76e4d228129d8d46fcfeeea1580586971509264008285f2bf81c6dff8e9b3efba76dfd97bfa8fa2400e1b00a77a75e2278d42852a52d3037e8897d437e1afad513ba13b24821b0f604640821f003706f93e6f1936c0802995ec54d54ed25b18e5a9e4877c72b7e3f9b2e61f8041f3cdbdb019f7f89a0e1af3fbbf76140ce2bd03b89e16b36e0d52b1704d020a129bb7339b01ac4398c614162c0be8b55792b8cba87f93bb602ef2ae0f499b9191132c82edaf336c43a81e142947d037e217cbd06c9256fb496e75d4cb0a48cedd2cd3ce05691a418996e99ef4734b59626ff43a8677616ff544f830985dc9ff8e4668a5ccda95efe497fce8b890172f6ed0d254119ae31f69affe67fe8251055c84bcc7c1cefdfc6a007dc9049518ae28c4e559c733f96acb5e5ce305cff268f0e234be81045a5e9ec4530f2ebd060c813ccc892bf88d395782fcbbea4ecf2f9e95f91d090f47b2c9d1ca48058de4fdcf60ce8ade0916b2cfe757f6feb9d2bbd9f2f66990ff89d46eb16f31827665765909e4c5d2dc21d2706cc273b9e69fca00db21273ed7c06effd3a3de8ebe8506f5c5626f40927f7b3061172ea62c176c449fcbd5c9f41e30c4f8818017b9ef54531cf731415c7e875f283e601d9d3234f4fec46018ab325e6e9fb0dd8bd58f741a7f7832f8bdb81eff1b8e30bb2d955ac7c7643ea6d1a99779963d6a4d811629cc1975b20f9af9eac32c67eb9b67ee3e18d48ba7c6d8cf7ffdfeee7fba579caf0ccaa756587dd14e6aa8e49e8d79062aefa82e723228a2aac0da806056f60346e1502c376fbc55e1c7223245e9af5b09312f819981dacfbc1105996e88f27b6d2a260f2b6ab12d9b3386f80d78b0951874f8cf63133dc63bb2ebbcd76fc2e8805d8592c5685ca52edbd920df6a4b53718ed2d791f9005e00d1ef679edcd3ebf1719f808bc9c700ec06ace9efbc31d32fe3d033560c3a88cef583e0e2ca13b574c13fb9b297b9d74effd5a4640313abc947bcb48235e330b49da7720cf9fcccd159616af63ab41f5bd0478a8a602d9bc9345c76a1f2c768d0070bc2df3b9762e401c97e8bb2f1d1c6d19058897f729c16fc1cd33a788d110cef8c3e94e29b425e7496b49b33c89321d1774aed8e672925c966d7903d63a11b64323fd284267a03a1d5fa3925e58e785e51b5fd4f6fdad90eb72e7815b31d30f266b8c2f81da0d6983ab8c9e22618eadae426de7031deda7ca17804ef99e46544911d6a769f6ac7b23e441bc96319da1c8323726704782029267afd29635a373c2f97ff12136392efd0528bd44f635503d2b6a7aa3dcb7de011973d9547c7339209d46e40af776701ef86499c7628ba36146d1e12433b552fe630ac42bbe30669a6e871ef3d47e93023ee883ff5300c865707eec9ee9a041f818cd4188046798c741828b83c06ba351ac3a22b6c5d6e905d38ae6e37c40f0eab05871c69e0fcd2a190eb4311cc53219b458caef1b33cf7a71a8e80fba422cc9bf201ee431f7a0b6e77f411ca25b0cb22cdec8c9042bf84d26ef4de0256ec37278ac8722e894c203f5feb8ca2797a02f9d3845afbc130b3d9ec75f427cdd27f7d0449b07dd2a95f73c79ce9038fb03d2af94f4745114b63693ed184d4443adc1c26e7d5f5dc574b4ad1ef626146834229e5c9ed63185b7ee020c950d1ca715589b0a95cb61a2637b8bda10120a0335f616e821e4c2882b6a2bd8fa25bfa88924d14d7e2292e54eb4f7233ace3da17fa6f32ecdf3bf3a4794438f3b9f832e73723d0d393d5596eb48501bfcd3280e11c7b70a73b08927a43c4f28ae722771f07780a46e16bab5c3c937e23d8b55397514cb3a5cce89459f55b72b4088b91caab967ce8fb007747b3f38a3377c599d1fbfda1d4a7ae65002face8fdaca2b083553e87de3f5896e02805156e0d71ee5944fcc450d3938016013a8d0d709d97fa9948ef9207cf2e620eb3f2bedb7529e3901a75719cc88517f5102c4614d6253d2bd9d7f3ef1a96687742503a0360a71dc85094c76be8cda0590c17f0f5856b6c8e9c04d9816f8fa53b6c8394a6c2933e093dfc453042707d7f22f6aa79bc4e0d6fe2cdb17eef233ebdeb8570bbf30a952a69d90df002c1a0480005cb8a6a12713e7de42142ff5848b3fb7ca268e3e2a3b68876b6393064d894b7326f428824b0042c324be77ff4145704f90978a4a6277695ee7ac18805e2ed13220491286fd1039bf11f3b1610a83beb79f175b014af4ffe380c305f36abd6dad40e8e3339f4190bad78145bff7d0ab424c4e3724da2d3e20512c82eeb3075a5f26fed40f12974b1644aafa62b1e97558fc072255533367b0746defebbaf63cd091f31e5b1f0a80e5c22c4c34e1ad68eb0da9b6e09e9df2e4b0e440f8f10812b210395bc14e20474f06d54d54ad78ff27565432b1ac53dd1d64e3d7e9ab7f50875a3c330e759e76a60d351619070e8a1437715316f07157f0333351c899deb20820ea568cb675b71c669325247b479216461feabc23f2797848c42651ab3f4976d9de701e71d5df03180524ae6cb38a5c900f8924638d9271e1d4aee733217a2220062ef1ac5a6bbc00a09b7bcef4cb41146fd734fe0021f35c9287fadb76cde9ecbf00a4f8639982f1e20e837f12e970b55d1111850b10aa575306cc6c88937e41ec789a0f81da85df593a2c7ee0d7185a37efc0d726ef8a8daaa73a9f1b88eda2da7a2624a503349499692a1686c99990affad100801a63f31e2500cb32ed3edd8d85746625b5d94d4225971d6647cdbe4bd87d8bee7d788c2e16218e7a432810dcd67bc6a09266c149f3522282231aa885afc0dc742dbf2a0a645f40e10621d709016b6dc03aa38218ff47f8449dac78300e3abdc899cc161122be8f82e7d86395571c8db99ddfd8a92c2efcd3c0e9c084420cc8b2aa7d4846800d08002259b3ac371590cf50b80d50abae4dadc004c6fc387daf60d0f73e51a4ebbdebd725381e06c13d8afaad461d4bdac6dfb347e95fed32dd84707d07f32ca6244b938a2df10461f6158af9b71d3a2c966fd4e73dea272950b851a9846c02d36ca02d0b4c621e92e8d7300e05c198977b57e34aa1f504a2cca1f7609b776c55828c90c35a604d1f1af9e1e7815fd1dc88852026cd817d3d94a7aae6155ff30f6903703fc50f9ff9eb9f32fe84aae19c40179be7c0e0d927fcceee2312394cab7500696461cd20543469c71922e4c15f3a2840d5407946cb9c13cca6667b9bba34ef5563a2fd26b26adcee9eaad40c506deb068c87c28a25ce4ac7193fb1981c7e03c56ccd12bf8caafe96371002092766ba901f13c105ddae20b71281e304e62c78ef5ff360869eb60b84e979baf76ded16721e89990fef9757c959cf270fdc9007a92d0fe5860f744b21dede2b55f9f9facbed30acb4f27b4623add5a6a1bd44b4074fd531a3e8219129ecc41d81e185dc3b0467fb2b3193cbf141a8f30baccbd51f155ca065344ce171ace7d4a80585a11a6c073a9e3282a338de6fd909d7a9acd2c9685806d11f6b7a7ec843690d08de4758ca4c016eba7dc9475158dd36f00990189bd5b07a54f0256b2b59f1f41a361447127df7aa29af93fbbb1ca757b2b221c4ff0b4c464656ba560250a491a1fddf9344815e6eb19820539aeb0d3aa57ad05424a6be231b5624c67f5a3d28fc18442dd08512d909714dc8874a1c284a204882677679e184128b933d9e96c3a196bc83fc902ed313405ad257de8eaf0c1ef734314afa34d22b70319cdc419cebc698275ad5a7e0f7d0e3ea6634d65714e8183f24c12e8eed247cc5246843acba50287daac8dca394037d93e7e675890364fade86dcf4d2f603b7ed025608e698181ade8fe74a183a460f203a03301d1b6d687b5cd9af18f1266299f29bbebd3d393958679948eff17f5d158dd1457c15827d625e69fb0d0994f255bec67998c9ed902f3154110514b6f2984cf68f5cb7832b6808238148056ba51412fb86afb38d65140d111bf3e1a0d6b5febb1cf09dee7ff98f06b893b65b05a6871d696b1708848668c8621a0858a0aea2316c3a20e1e9346114f45f2c7ab3ba6529b167b7dcbb1f478316de045103127f2830ecd763d92e2372dc5be9d1daba5e0b5dbec18763e8256adcd88c32cf1623a6f54e8bcb99bad1a52ad75c164fdde84c114f6257022b386c038193941422130a3cfdfeb95c416c53b25ebe54777de417e7a79865af4a207329b3f84f366d0a115a3919490b917ef14941df2c6f27cd04f0aa8106e628e92c76b386abf9a35580c325f239808d13633d2359c95f935634f16862110ff3dbb64eda2e78e8b3408b41df9a142ab8e066798910c42a2e2c86f778a7c4c39fe0536876d398fb901f780516ee6f9ef91c001a22391747873aa6dd654e70d66690494da4be7e5a218b911f7c3464ffff6ab5c1ab906105991e9bedc147b01a25e2358f3e3eaa9f7e2cc1d9d06870495f6b00a236a19e17dc205baece553bc2f94b87286bdb113a4d907e11fb802b7fe0dc3748c6ec3ad468e4b3ed691b59714e2f8d78834f7b986706e245d9c5cb9c345d7bb1f6a1137232585459feff0a33b540bbe0f55bb801b70a63ee3423c03db16171fb19821ea326dfe2b6b058de401699069f75d3126a97a81111305bd5b336deb48c95a6c1d60130176523c1a6c1cc658912e047c54912e0da4c0514ba39f9e0cc0d06cb97dd6c367b37d6e4b20fd160b7ab8bf4bdd8fd4ac9920bc6742307d7814a0d64e44f9b0270d492111e8cfc43606b7f509b96ef2e9b1afea5e5b380a4cdbac54b35c5d77e0a53649601884b4e9dcc7265c8e498f944bd43268364c9c57306939b6bc52edec8054d740555c151c6b366a55014353356c4151efe858e441b2afb29f095e4aeeca0a9ac9c91e50b5616c75e515fb0bf67b528f3938124b02d10729b82cb0f88bec6836f3e3548a278eaaf5e6adce52bb018bc2404dcdbe27e4ffbebb16e9d44cbeeddea399ee4662674a28a2ed795576d3dcabc62044de2618792823058d130f0f1509dba8dc43108731970f7dbd05fa5ff17b93b6ce01e5141f63c36b34d11cc29939564804b9cf98fa249f296e16bbef9e1a791500b98c12d3713c4ac9a4cc2366519616a1fa155cd1146e33d8d35c111406c1e900cdfeda1e2bda1602fdb3479910d1254cce02482bdac45fe2188feecdf23492ab460f7765237debf4fa8f7440004671866716f439c3939aee079ae57790828fa349bb9d263a6fc53de1aa65f7c8343b6522fc59a19dea1efc54fd92e99485c3eaa8bc392d5adbe0d2c742cc64d08d007bdd0274dfbd7e4e16846dd8fbc017e984929526eda1fe97c23e71d8381d45648df37bf40dc991d9685a022fcb2430f73a8d1b755e18dc7a335c21f25552c127023ff180623d74fb8dd498c7c0c53986b17384ca4e0b1a67b96217030aa7a24d6c39dc41cb2103491bc06e0cbf638e0967e4d8961d5c5371bf354eb9a6a7529db08ae3ecd5bf60a94215cac55d92a107263f92e2aa3d2f82b120e70737471560f7b132f5d6195b82ceb476ec9a9f86afcdde12742402a5c9b227e582e934e83f8e04cc31d5072c195b491a00ec454738a82b297e36e1b334539516d6f86356708a0ad91bd17a3e2861af30ca145f18c7c98b646ad40de9478993f9918c256e14240ec424ff2200c2c2e9ee77c057e3cedbc9139e2b92032df6e4764cfc00cb65aae672077e2949452beb6341536ef657f79690eaac86aa564a4ee53279d7d0d1bed754bcbc7a245f24a7af98b0d3441b6a1916bf360ebf583cb3968289a564e3bee2055ff209388f81058b852e52243c1c49d7bb9553d1a7b649ce25e6c4a9c335932c39808f37026ea32410be16d7f918bf9d65221268f8996c39bfde4374b02342db7ac237feef2f9ce33994f69f633361f40cd70a9e6550e6c6c6336706df0950c26c55525a9f44bab34ea4a8afd1217c9e9d685badc00a47e5b009e6b15417f3c4591815b1640ce00f2774ba163c94449cef009505adc4ba48b53c434ba1431f1f818916286484b6ccdeb4f76c9259676a760d0f2e9c4695fecdbcd208b0e41e48ed125ea7a28d9be7c7e9565f5e8c921f2bce7c1fd8b03702d2831c039864015159fe7ea8654fde8f5fbce48815bf8955762b616da05f0e0887f2bb9f543611ccf635d94c3a751da38183f71316d079c35b84d7a9e628466c05a04bdc88d3979fdc3fd67c08e578541e6bade0e3161a7a8670a9d294c18f38dff319506a02f9bc8ad80b10d6cce1fb2e68f083b03d83cc02ed8640bd439808fa0e902e9b1bdb51c9db84a41534b246d4e5c3ca981264787129b1164a8d6456cd57d4187f155311b785b7f2de477dd1147a6c114e445221a6ce380101048e1501d44f5898136505f6841ce181730c0e68bda980f9804f9de6be6d8c04235a470124c35c3275b6b78017d61b3f6b8d436161f09091c9ae7db61feec1a27c02bbef8b141dfb41c8e1e268841a225e1fdc6237f6fc61461c18aa4161d06920ffe6744c87bb25d75a6308343d378e13eb44ee71f45f81b16a12a7f16725efc8cb585028e814547180d708f0a2c98d102e0f5a93cd99ac08949ecfc28accfe672a8b9dc0b44fcbad8fd1d461c76a4785d57f3b04ad5f994c4f8a3f60829484df216ec4399961eedee8111bb59b041e9232cb2e5993bd508f2dd3f02caedecf428c0189316ba87f4fa5819c0a9da6a8396dcf9874c9bc4455af3a5263274cea2697faf1f10784c703a3a8079ccc0f26eba12497080e1e73aeb9a8f995a63cfe5a93ead4e1d7ed9921c0c21b78af7ed9d9d516ac8d00726dc9d5c7922829ff2654d64ac3df22dc3e9f27f78b38f3f6a80d63e1ddfeeb59c42768c893168edc5435f85e6a135ca7173c5eb892df138fe7099c7c8829fe128ef49ab75ebc582d3fec530e87b688670d2b3f51a59b437289555cd744da04f10a7100f8080d8d13915d5029a6771b63da6340bc2c2b6cf1e7d87b1df146480e60fd6c53be531c02d7f31a4c50820e5319c61b4931c81b57f89064d8a1f5351c85386f431286892426ea9e95c099215f68c23817d38315e580e39936975036569855f2024f0f339652b4c51e85c75740ae3d9fcb882437dcec022489ba24b4e6993678fd4c85cb6493e69b4ac192fd4f77afdd3e09c51749d9b93a75d90c5ece0c6bd9eaa3a37a5612c74ba301f3eff94add6417bd2204c220f90dfd1c927d606604f87ca2dea73556214ddea13d917414d96099315a11c3d302b2b70961aa432ee50dc165bbba49cf4481907e71258e3e5a0734d19c2f008206abe48c8ba99067c19fadaae9a0899c96abd2ba3ab69162c91a215d5da5881e614f751c69a74c5d8ec0541a44321a03bf1bd64ee391915cd5038d361e37ab2792ac553080363067d2aca54e2722fc544d63423332d8bf308cc08e3d08c33f425ce8e346d51f7e7d94c3cc1d1b7fa20bc3b0b457824f69f89454ab1e1384c73e0e64fd7801919cef697dd5c24df6385aa71062d7e3a04952949e71af3804bd0b3d400cd8cf99a4d4460f3a05c4942d15477e4143f86439ee2be46286be9d90229b1d7881036e0aa80270a7e47164d5b8fea73ffa9d7657d06ce2b364414c833e08604ae54943f49c1c4f7c89eb0bad3a643f4a7bbbd4814726d02b7bc29f6731ed9445a2060d0938178914b0beae8f7e099838356247ea574a3f9f003858424155aa5ec5f318ac715bd218531d2626e1eae94eb99eeb1660916db862ed52f8fbcb132f1c00e3c1376729e78b6e1d22f022392c56cb67974b5fb413a1cb33ca5dad651c35be0ac24951fbaf1c6a8b0e93eb9714428b36af1a3c5f6995523ff86ca18ada0e1b6cff976b0cbbdae3162c284d0474c76219d3142c86f1a0f83351ba91fc487d863d747aaf3b0da2d72cec230043712e2913822629c2d91c62a5338c173fd7228d4769866bf789bc1e85307bc81fa78b8c26c24921b415ef83f2ab4875353c289c387a9e613003ceca0ce96edc0430c8f7db88a81a27067ae909a2b2cd2c8b292459df237653802698c8097552afb80ca8ce6c782812248c4bba259aed93063395176eba28cc17072f5e74c42e4ea9081ee104b169026353b9b976470f4c72e6fddd8f1e26616b5677e805d3d2ac71d51f895544efb86f0793ff0c8b699ea4626fbb2336039e2fc34ca11fbf1aea1ff22461fd60bf7d86bcd6f8f9ae30059de7583b8205e240f05a6f43b61d1a23545c3d531801e60f5c0794345072ea829a7f5084003caa1404b90c9ed84876b9c9a7499308a3e0241a4715f779a33cd5f302756b22df7357fb1a26bab7bf02d2aeea1db427a784fe1a9d878806a85b1fa13fb44416ebdd04746a4245ac7b06423ad9b21e193d5bb0ebd73db5fe8db522014398110173f5bb90341e0d9d38ac4f0990d553be5215d6da88cc15fb5145562bd38466a40dc320aa757493a26e4bd8cc12c5200f4c601ec99795c9210c916bb8941452bfca5eab46cfa7f1f174f4044db3d25c38b3349c5675e004c4d29afccbaf0a363e9610678b55dff82947071db794d94d186aedb80f6ae695c2194f3c7cae24d635670899d996977f0b4e3d4ef54190f2e9eabecb611d8e7836551dbc35009415cbba97e50b5a864094a55b5f20bb49b14d7e15f64cdb496bd219f8267b205aa024299fc8a5df6a7f3f7e94a12bb25854e64b6e281aa943854dc3c31a41a6450c3e1207acf8af9259f792fa99350769d58b6df6dbd1522e7596b6677b66a3f76be6f030e06acecf5f68d8a40c61d20a2bb01c22d8006f467573cc7b600f9bf859aadcf1efbaf2efe1590c3541f316428d05647046be359a52d6328fecd3f17c93f8f6847b2314e552497b4d47dfa1d5c91966a806d1636ae7df95be10bb5dc3d0c5b3aa1157770cd26017947593dd2910fbee83996c68d2ae693c959c7a732c8f3d5e9298123b66665c4cc6e24cf14022179dca80aa8292124653762758480566c448f9079840b2df1d0b4a2b50f644264db1291326804cd95baa7bd0cfa24e649777d366b895cafd16a72b0208812025fa3752c8e5bc6818f5c8ebda7fdd367d7bcf3558e3c7e05de9430f1e5672e579c507279ade88a4b255c3e593f410732bdacd11f0d6ee8464b71c0023ef79ea2a7a406dd5e1be32e97c074e302f73b5d99661b0c36efeb5c721d8297b8b55f92ffe75640d633ccd3a44a2c29f639a6361e462db691cf6b32444d6fbb6c88dc21d63b8db2cda2f012b7b46bbe2b253f4a63e8d0f5c2f637fad48f901167ef8aed89c56b4e7bd5f515c605b809ff0f41fa93b1eb003b85f02ae24deb775aff054ae1a8ef034aded829c7a1f7ccb718d58ff522da9f9393dbd1f130bb1dd5cdb6ee61ac3822826f9f61a678d6f207f3edf9538b5b9014848674b5071b07307b715b5b6f93c871ff56b835457f63366df690e1f806609675b9b6db0af3571b0f04420ca7bff53cb0fb062a0c609fc715ebcad55fcd9497e4e898693c690d7153509f252df84b7e9df9533c8d022876f93c39e017aaf07be45c268241c50b613a830b550dd77e7c9180ee43bdaa5916cd223c1566e96044b96e8053556bcdbe2a5c3b903eea33055eb817d7c36aae87d795f168247f29f19c195617b2e40cd6179ec4bbc577f86cd74d46fc1dea01a4f31f0cf07a7851a87fe99d472bf65330d376ea7cd3a0fe1071380d19a87dfe20e16e7399a10ef84f1560b71b9edc08d4e2d22a6f78a89e383b1edec4140b84191317eeb39055ebe31782fec44a591466b45583cec81642b3a89a6c32881be04c743bd5483b7d9757e0cbc73c3eea1d1155f35bef6af62a5a15b48e58589cf7502dc6432c0da755dbb264a596e9ff0ea95e14e0b0562b6367b27442107acd36a6b2ada732efbb8346349a3cc2b148f8e5894cddd2dd0fa54087e800e8bcb067228008870660f41e9614fc98335cbaf334c659e3fc72b442c32f9f9e23840b860f11fa5bdc2bc99beeecb108f33be8fc359cfb807a56b5b661fa08b1cfd2ad182b3eb3d0f32cf68dc0ba23bff5e92fbcb72b133c1497be6a76886014ef1fdd09dcdd1d58094a7ca599d2c0fd99e351d9695d3dc6462e97fce7b823665d7c94140b893c51efc054de99e2fbd28fdd14b80515a91c98254199a9724b94c61a4d07c3cb19c8ebabe8e3d1fe79488b4652dfe9c0a491f67094c6b0602ac27d3e8e59a6bae0f71147c1d6da1bdbf6cb046d5f7d33d07d225e9cde1d3c43c479ec8428192e2563bee28ff769ab87badaea5a9cb2478527cca698fae8bbf59ac49fb63b13e712d1bc2591f2ab0abdaa9d3e11d44521deb07cd5705d453de9339dc37128e7ed3a52a8e8f8ae270ff56b3e9aa27b596aad85546b9f393cd7e05cf82d89f46848855f0d46a375b08c35fa8b37023f32bb39202bc853543d2b26be7e00dc077438d16d4adb5ca5d8cc13ebc0edabedeaab6fb9229a9cb80e6a4ca9af6df21142f93cd4773821d122a916530cc504f05a0e789dc6b5c4d45c0c3c587944c4178e3685b2c496e4f523d2499a8ae5741501b48646f4bc853c7d6ea48356c717311763a3b16e1e8119f78e78835681cfcc4a1d88308e58659ced19cdcaf467c6383ed4f522cd1292b6d920e4a3e8890df8d0a0beb3729cb54ff02580747172ed7e1a816d35123e32e336c26cb58e608705a78c6346d54f105dd93b8849f1a13da4733d8a46a65b54d2af3399994de7fdd3a6f05446c6a8ddc0e9908c15dc15cacfe11061ac56d0501bd066dd121a212dbc5dc880b641977c325fdfc57cc020f8ca1555e45de688208b5d3533425e706c52bed8dc4a47d5dd7c4bdab9348f25d3cc78f00d0dd9dcc4eba2c731b3f2a7f822858930eb8f8a5b24c8ae664f3b736fa65a9d7cb44e9d7f287492555acfd877544a9b8e83dfd1c2c53a7eb1f48fdb7adf90333a803b247492f5212cb69552b7c4e185fe58c45a12eeb2975309a4cebbad6fdcbe1b25d57aefe23ac25cf7464591e8abb495b4a31bb4ba73d06e290a70055cbda55a452ad41dd15add8b1ce9f8b1158bbf5a5d183bd5280393ccd0e3a42d1779d5113d01cb1539dad3aa061b9e30093babc42a2127fe57d97063f7798610b747f02c495b55f1f4a03a38c706466cdaf789eebf82cca093071bea8499e16fa2c9b57c964415419fa240267b33a50031f358b83de58360e209e0bfb43fc69979a47a799f8a5ba91165129a9f4f59e2d30d61fd6787640ae1cead847455f7d5bc35eb7f29ca7441ad3964ea300e5ef3ebba2bac00ad87d1886c8cc178bf4a13493b29332a54fd01f60405551afc63ec6f9fdff2b4a75c95d80d8d12210192192c6ba568b2d93069b8a8956c10b867d5e442e179426a2b88b213bba8cc037687aa499e0aece384522a29388718309f40d5f82942e2a0f337296f3328f75551eb46ae97225411cba07b7e4892d3331b9a570f154ee6cd99293d4e7d8525187915f47af3c6b395a6270a009b0b258fe00cb0dad6f668d1b71a335a551ec3530321c3ab8a04085574af56c120880866cb76a6587993ef5350a9642e810d2f4f32cd927575943b6834ea1785424080958a12a918d49d2a6be5c1a84d987a5fb3aaf54d8c51aa439b29aeb7d901c9d0107f42bfd1833a87713aaa22570a4f5f96103419c0771f667c33e5c4d444c38406c5562f7b2c145ef83ed86bf8913f5f070f3e0d047c5fd9106249524af2ce9f384926023a33fdf978a4c6df9a03533b524166c5c83334bf89bf4937ff87a76981b750070583c7919014c5835284a422a9cb317cc2fd629746718f3726df44b1c9198542d805a578327e6f1fd548ec43063922866ab83303c1ff9275319f10729824c1fc63a4a9d9b0c89040c238079202a7d0ffb2b69250761152e280c439a2248a70ea2cf3a5a03139a7509e1e3f7f59b602039686a616b4f5f9205e13084a0715fc243b0b8f36239c78bcb3f5fa7867569fc3fdf250e8246dfd16d9601105480788348641dbda7250e6dd392e8bace96ae0ec21c21ed4497eea5da02408dc13a9481db8e0f166cf55f9f01bdea053be32c85d1fa2fe860ee6aa0b9eebb58d8812cff4b624c8a3c1dd43d41e7dae38e94ff6df8298a7fc060102b0fc378415461f26c79721b8faa27f2bffc059f603501228034c3430c865ca3def301c5462462777aaaecb279418d7a7b7d688cc9574cc3ca50094ce3e4393100c51ba8d026eb1888cc988c6d6cf36d056b4e181798d92b630d43b25cf3f02195aa0d4883828a997c9616601e8cceecbff89af12881a496fce5ca5c218c76cd7511add428eba094f587f0fd928198e559c1583e3cff159b1f98af8d2851ee7191ed3bcbabf45348451c26d16910c2932fed577670a07b4308c53ea14ccda7d415d5498c8ace6a6460bad15ac5dcf1967edd2f14610e57630a089742f8a9b13e549ede8ec60dcc72d5360c1dcb25d3ebca23e3467b8830cb08ebfdc9d125fa27c2e588d08f20d2a06632d94224626f56c524e9368bac3a26b52c9a6855d47d72bf04ec0044f52131a431468949f7251b3589fc7549a89b931e9df73ca42375802ee658763eeb567e0698187ebe5f7c15843b7ef6c2f9b375341ef43cf77afd621ba74be86555d996fe834d8c11c1c753d0789ba37669a0996e68b59e35ddf31edf2b04c571cf2433c5727a0b32b1d159f77c60e29e556bb81bdfa3b6b3346f88a0d55a532364d5fecba74da018e715d91c9128acae6558a70269fc85e1d904b20b0aef87fb5b1566f1123053c9dbe3db0d4933a239d53170007ee0119558b90cf63d961f5c15c6992ef22c95b022134317106c7d0c56a1036ac8e167b40d2a44a4c62cdadd5d3c77b74a513b7401f98c682b8b020215be21cd5111ef0ae699946c75f7bd5577ae152761ad8ba03fc9f2f097e265a379b954e2fca6575a2e60391e2e84fd160447724e743ba2571b85126a1c76fe514a1c3de9a781b2da681954eb25662ac8715f2e5069ee3752e78185ef019f9cdd6da7c7dc8a6723ec04ff8f543f441d2661d9a808dd914b8cf7530e1d6de2d74c6a9de31cc5036250388ee29851f0e85112066f01a04fe1ee405592049484b0793105390ab6a5dc7168210d89269914eca8fbfea500c077f28a69ce1a366a38fad2093d0cb276f721ffe4c59c1ba6126fa8ee071af515b3de22bc96b8dcf6a4ade847a01d7e4135292718a2d2e0add0384d7a79d3e56cfd25428d885e55ffae1f5556d0ee2f5587346c220e8003bf1a7916dc3ca915a4c8e71b2bc18b5edb5e934e8625bd7c7c29189d842c36baca0a7c06b4c1ba8f1a3579306f4caf559f016df8a5c5ae96a902a7c040f91e3c5fb9848df1702bf09e365c663061b02fd199f3d162d24164533affc4c8f6f8bd8bdf3b6c901e2178e43a27301a1c5902a1eaf1fb4f85e50cb5a2c85b4c02d20a4a5bb47b118072765f140e32963e40f5557b9222666ce8641b8bba8e5c592efdf3d38c801c5aaeba71183ee74c05d48de3da28b21bf531eac910008b6a7c3baba9e60560f036c32c16e6bfd7deb9d16896f5369d2579dea22aa2186a3869be52b3f636f34ce97c0de6d9601f8bc195c59288260b8228852f382aee59d7b668a1b763ddc3134f99113ba7afaeeefaf674025afb1db183dce84c915782a9228ca94834e4cc1f265b5fedb66504aa60d1cd82d548a830e8dea02aca52ec47fed7cd5d296e693c9f7ecfc67a80cb721aa7419db0ecdc2c08d6ec1401428972bdccc593043a5aac9bf824b70ca074d7f63aacf9ba6e72c9a248f78753b509971f0aaf4e79f386e7c1811e8699d46217d87a31d101becc98f8f8c757e510dcf7826f05b43f969dc401b814051845c36cff8c00fd1331ef31efb95bfa324c0cca391282cf75258585e3346cd91b21fe5da95ab2caf814a6784396b7469f4acbcb3603d5af5bcfb15c84f3bf4c6754d404feae786d2f176fe7783effc714a0f7910cba6f00bf6bc4ca2da359ed3734c5f28aa257b84e3a0776281ce2d7902a65c96457c311c89041abf41fc61ecf7647f0712bed908daa0021b0700b1b0c68bd720bef443c118f0baf120fe7c5fa213b5d23c017cff4692b9dd95df857295628c29cdad5c08cba7ce5ba53514834d74a4b660f9f1fbccca1e83e10e89ec0578c0959668fb436e51189053dc331d42a1607c679e4090c6017192103872cfb02f300d47343d7ea10117fea905532dd6b61630d189c501325e06b64d53602cad5aa31e1fbc82c2c8682fa16dc798b0c73216b402a849b564a524e27e620e0855102f3b91be8434700030511c12e84a21d75dd77f95f2f05e1de40da2578d4875106cc53a0864cd573bdac96f65b36950b00ab1cb04bb2e6edeb1f6c4ba90ee0cf09bc347dd955379566a24c742f3ce626edf6fb38ef185be73ece603ca1d3466676a39d692c74f04436c495d269b866c09281724f914bfcf6d0d5be93159e8abc18a72206403c0b50e5f271d7d66a047584f1039157516e222941ec506620025c6981f4a6585424d87751a7eb762dce3a2ea1207608e229a3c72abdfeb845a2660b28ad6393c294f5bdd3ba0527022580a06847c71c7d98f16454a88799e90a43006d69ddcbafdc6a5eca74524bff1002a2ca910682fcf5d991e3173ef3fe1fdbbdde64fe6b2a70ceacfccfde4aae81e146ae88426669d7a754ef29fb52315b25c48e2d84d65d9bc3109bf3c852af1a87189891c9e3260989ce8920dec1fcc835c959d088fa1b5483dec5a5fb397cd273d77942f7389a656c00fec3284ca0fddedb3ad337e8476c8b0eddcef591187079ba00a7a14e1405ffce9f50580e6aae652b7f12c1fc4d514c239bf2a95559663cc0fc85be6c58b023bac754404064b778205e52d894c70d6b31377ad734e5081d885060aca23e4dccd3403c9d535b8557279fed82c690aa12818c3edbb64742ad8f092b6046be6148ccaf2e0363c7d85330eee4afa7237802a6820221863e51e3fd6f9f5c3989db6ebebad5017e3490e1b5a9cba19b40dc91b49f9e914a4ee728e6a3213f33e1c94be3eaa61781603e32ba41ad8a4c5afd5cb9a9f3579e4eccb60e4f8a7739b1432a81a63fc3b5258f0e791d65c7cc97c94bbece688256fe0bfcca66596bc717d75338d89925da126fc4b43e28b0c02d6999af0c197f7ad3d15c4738aba0092ae1952f60891813147fc45c8589f74aecbba9a0827c72bf14fc06253b1971f276c75eb4c1536cc634eb220177d8035cca3cab29519b34cb2d55100f7498bc1f79da8533100e7cb7e9a87b35b5099955e5b47300d6a5fbc880aa020b7096a66bfd34ec19ac5467fdb43552b501ff4a4de5548a9385aa40b3edbcddd0c6fe3702c6137d40cc2dc28974692ea53048e60ba53e777e7ef3f4ab010c57b996431925994d498b6d43c15960162296a1ccd0a4499506644b98dec60f25e7e3ddf029ff067f1b499d4537ee4627537e3b3adc02a3f46cb0588341f116ab5d428cfcd860aa4c1e0d8ab668e0e01471d9597e7e0c7674e242dff7bf02a161d79303d0267d6ffe279dae4e8d0a9be9d9e6c057c8678b79b8afa40e9ea24387e2a217f6c2c4a1e94933567a43242d3a9856c3eb5a175d445abc9a8d09a7b5c586b22d8d0f535a88b6c64b46b4c2f7206ae474fe843962056d41c58a70fa86dbc83c875a6ec6f9cd9a879084ed19a3999aff795a996c9c084b75b33e88f5f374fa92e1e03193c6e45a02ffac065d5ae1bd7d62232c4dca862914b2aeb724f14f498f11c528cdb68b16bc46482c5e5b3d13634d3ec8e67c3e552b4664fbc3cfc123082dab5e560c1aff0aebcb9f4b6ec8dcf5b1b231baa59c32b5578ccc54b109e43e7160ea7207805dd334c63cf758e04362c69d093c1b68c6ac2de3bd707cfcd8da8066765ea5bdea941139c0db092dfffea54808cef8a232694177ab17d7f5c97289389347b83cb33dc72ef157b298766295c4111f8ff25f8f1b38ba00af6074eaee86b17e192db2932c3165eddaa1ee46d355232ee844456497ba8e103a5f6e611e7fe72f900798b31747a88eb6800d09bbf09d34bad628dc7a1b00e1f81ad874619b5f1a8852aa0aef05fbb5b5990e672ab6332d7a0fa638c14a0a0ee024549459dad99a125d15af537c977802974e4944d1000633b8fb48cff0945154ee69047c5c1050e0aac8a581f89c3447aa458d114fb42239cfbd132abdffb87b08e1e15f3354e4944f12f4e85037394897ca3d0a512183ffca8cbf1092e98d3abcff28f118a3a72a0c4f6ee83db674a0647e513684846c39db1c6d4f061eb3b6a147be006d31a3eae30bd86082595a0f136d6993e0cc071c81c9b9112528383107648ce3aaddb6e7be075ba1f4012c601a514368008dd85b2ea506d8719b805112227ff171b2a6c4aeb290d42437939b2442592cdfe67a48ea5227388026a0b21283a8f870281d2fe97973738efb8c499aaf3ded77fb2819a82efade8eba95330e93e2405fb3b11cb012bbbe3a2197220eb55d9de7d63af674dbc1064eb63ea6facc21cb214a1fdf9d8e96f05c7946005ff67294a825168fc654335ba99bb381fcc4216b76da9ecf19989e7097df11dd7e6a4915717d7f8b2d924c35506e02318ee603c74061c9fcc813ca58c1af09d21c4ba754f020bc4609b728edcf2d7c2e4a3248dd5405719c9e98df80bbb63c40c8e8bb99c233688a6dda36e873189e228c9574d37099dbe2dbb8f318b86af32aae615e67e46d290b256fd26a398d4fca71691f8ca390b9c7af221f079b2e02e1072bea6b05be4bcf656704ff2a980bef27498ebccfe51e289a86fb44a365419be647ebd08c180e2d256aadab69c3080a499f9030dd9b96f0779117d964c0c8d0936c88f132cfeee15365592e46daa6f1f5c884264af51f876c169010a69dae6fbc3382f048de9bf61338b73f4ab5f02d2ca32f84f91c45a7ef25a34a9d4daf98d36a26a1592cfc366999369f75d1fda2262695fff49a071a14afa7bbbe72385e9be60a9b21df9e09f435e88c09b615185c564375cb2d1433dda29d0a9cd5eca59376dbe4ee6b4f467e2296a18aa4f81ac02213296990b283a1d36c579e8dfb8f846e4330d139c598fd94cfa777f3081f2bfca1cda029e63da1b946f213f9da70cc6e29afe479444673a73ba945a1802fd87f7f299165abd1499c79da2684f26c901677ea1e453cf7a39e774b128905eee31cc996dbcc17c040c9359a9f1bac8dd063436c783b225e722f0312bf93730a1f5cfe16de425ed030b6065e85f8fdd6d7734ec1981d52205fffe8daf6d5224b8137bc05981ea3109d4fca1be0c810e99307cfb8a4e131f1ab357b785ae0425e8868d71724b79f5a71cfe61b8cfdd9327de5a1431b1bc77fdc804339982b96e026d4ce8ad1f53cc98001121e2f5225b4fb84a22469deab4fd2780d8e76b3703d467a465634aa08f79f01e34000bcf3f011b009c28b42c75a45111ca824441c147a89a7cdde1b242cac3e6443f8c2dbe8a20151f4962d6f62364420d65fff06d5f034d2da4b2746818b286b09ea7bed83fc13b256318e407c81384a7063382ca1f8bf6bc4e48517fe08c6297b06d0cbd6019414690a29fd30cd9c4342854e4b67f347c861beedf4b02b3d424cc0ac0a4b78410952cf11b4f2166f90f053cbf38fd104af7161ee364fefd5211423bbab5449cc5e7f035f2a2b1bbb313e110d0ee4d1b6b58d44c1c3de90c0fd0beb54ad1dc064b6b787c8ecce420137a3ed3417039de9765813397e3cd8bdb4d0ff0a2b7d9a6ca0031f1950fd96f9012f35f4973175c656443217d9fc8dabcbb458d9f112ec68a7987a40ca812b17fc5bd67f6ca70d89df2fd88d6cbe9f44f2f924f355860541ae6f936a94a6fc65b636c0516b10c5029573b44d9a529926d1ee84abaf68daacb8d4af046de4cb1747fb7a8b9d23052118b534a81f5bf6078bac85b7666cdd3e3f5294c198a43cf0e9edc3279d288bbafca0caa7ba44c50d432ffa00869c0d3e1a859c73bef7c9142888e02a6188e5aef0f762b117949480a72aef49fd642f2a7a2bcccd4a20a0080b249aa1608392fdcb81c6249d32d21e83c2d04c65f5542243e052756976bf7243471efcbace1a614f4ea7ed4de64a7ccf7a5900212a51db03b34be2f41b563ddd989fd079a732d01569dbf802727ab65aec2fcac2df7a1b7cfd05749930a77644d4d55c136fe5aa47a2dee5d9004a373c0136c9e2385c666fd415dc1a4e4d3f0061d313db364f6294f60f2f0ee974bb3662bf87ed336634c4531f8b3d051a9fce19790eb924b833ebe8c045ad312ee7825df5b41d45516708d2f9ed2831f2ca1739cdccffa17a318e66b94011c975c1721d8b63593596450b23d0e9b6cfcbb9ea1c1285abb561b1cfbb96417c8b4f922f6ab7f2b0ebd44b9c3ea59178d252b384387f44bbe22e05b00ca713dc5e67367f98fb8cea641c7e803d7493737022d1ac1da6b41f48839342eabb19687401211ce6f77d45871e878f9d2c18b1b837d4611640dba82e3fcb827ea1bbc432541546c2bda81a01879bb2fcff748711895c0ce8d47338c150faec01c85fd8f7b8707f37707474b9fc378479f3f5b93e593102acce44de8a84fb2c62c350716904f1ebe596ff6874ce5cc800a16f6af99dde74494af689128fa7b4b0ea2d2b986504cdc4f64ee8a74462ac3b51824b59751aa3191bbb0e70af3a16a865604df4356e8b3157aa03b55f192b45f72d95fc552c96e033a827c6f5937da44d6b16c78c5d43824603f495ce7d309ac420e9061dab768eafa78c0f7bc4cf3e57ef391bfdbf7e74aaeabaa204e8152e0061a6c2a71be0e8aefa0fc6aade9e994bb5439b7ba8de53456d61a84d1d59c867e50ea969df1cebd011d801e9a3139ed3bc2d0524f99c2234461e22545a408d539650b381defaa4f9a35106aaf89c038acce3623a0e9d0eeb3d3471246e03720a60b6c748f3368de922502319eb716bd260d74cd03a364c97f76bcb97c70018dfe9e26f6982703eb3e3ce831deb0a2dacc92b5d8576e358da850fb49e084a202fdf5d97f57dd4d145e48068c85a0fa46ab1836e560827aa32802c61544700fb0e0a97206ee0dfbd650645e99c0a686fc7b5f2d9fd95f693ff61dd34ff208cd4f3c26eed4a6267dbf574c3f60e4aa680e741271bdc522d21eaa5ac46fdce781b6c9a380766e3ec3372c75f2c57014a42735d20ebdc007bc41dcbcc3a808ddbdd24f99af727c1e42b24d9d9505b3973164e6068ffdd8d172f8b1204a558a34d38a743e5cb88797a53bc4de440feec468754caf0e2c341679e6840dcb677103d87f343db996a5996a20e60af6355c6392af3b5c90ab0fb180c9a20960d0e07efe1df80c242382306ef787514f4c186bdb9a514f76cb587dd556e793f36878c9c6e725db870ec5e7c5d06244cc3e4e6d1c64bde84779756cddd44f66391dd7077618d51985ca45cb8010681b75d7112f4e423a217bbc403c3e4432225afb5356ab59f57109fb3ebdcc919dba08b8e073123a789de7669b91a50dbdbc5b866b23c783198f4ba3b555c71f2ec457c5974f358a687724f111532c3d9ecf3c947ba5c1476720ff0fcea5ee031a0f282ef3e319450b873705f0a12d108442166e4494f4d82f2d89c3288c7b7ac5177037682bf9d0e0f404071f1f2dd1848fc8df11f88a1f5be4b4fc098161b2233d17c92acb861e5e78b83d2add5c58dfe591193fcf52413ef7bc7fc3102feb4c87d8f0ccdf64177626125f357470ee2766f9f806e8ca4bd79e2311136ccc045bad10ab2d691de1cc6399433e4b1f77b2eb47f3f39d32a98f2c96a93b0303c96fd8a2d8ac0a37825f07da91a8d6a7a270f0d43717402620713050032c60267abfd294e53a0a9150eb65445455e32e6ed4dbbae7858be4d8841e28ae254101843e3f887e369e7668ac383c38924ea08a1eea05051ac7d754e87324c60e805bc12766e826a6c7082d65a9e4be20c7ebb4511f6a642345129e55156d5d57442846aa5c83387d82cbcd64664cb737a171cb1b6ce97b174e37166aaded1ef2358695be1c94ccdb91495f6692e1114ea21bc920e6380fd997057788767c30cfbef53b81a9f2289eca0a4804b048f7150b844cc93e49c3911e6257f9a3ac2ffbd669a9064ff28556c04215c11ee35d110c7b555b0ddddf12078f2ec44d6d95e3d0686098a6848f3c6a78b55845443ba6b16ddfeef16f8c6015328ed8d5c29ec088ecca5dea9d0cd9b68fa4200671e553c2955620e651ed6fb4efef1846a70878639ca22622116f5fb8b7617b348dd54b60fe3901027444abc18337eec0936a3a880b01a74cd97ee2be11d8972df3af7b2f2d384b22fc6ff64264f698a3d0ead5aa4e63d913c93f13bedb0ba8a5f7af021e9db01fc76c5e4fe944ab6b7964baeebdb6dd3c056e20b70615296a5061126ee1c6f4947ce69e08c4b99c4aeaed95e91b99aa0336c2ead865cd1e507aba16d2f88fc695a47ddf946fe0c19f511602d98ffe48b51ffd11f3353dcd139bedd76969ce674cc201d681c2ebb7fc5ca21e4bc53864de4ef0f96c8c1513cd01e26982aea0bec34c8791f0677bad5ef617643321e164506ee4c16cea4a2070bf64b42002c34866ab19360ddc3557576f9add31d6feff031b49eb6e490922dca732e7a368e6b131910092f574b5573760ec0cf788a16a40dcb9bcb91e2e1bf455e3e310563d0fc8997bf67b76f03b109e46e862bc27da116018ca32887896bda74a05ea5c111daee99100b11665670fa740cddd8278ca3f9eaa110385b60f11db22479dc7c6cc85421681278dd2200b23cbfa8fe618d275137688d335111b2823082cb66c37f76104be175c918f9fdcd86cfc34cd8065524ddc8e741195f7a9785b8b791d996dde34e1ca109887ee18def52ecfb4df4b759483442f4d0112a357e35321dd1f325e5e30eb3c97d8230d1e84c5ac2295f54f12608c78391ae7a69f18c647cafbc20832684d3c06f12e8443291b8072c4c99e1b8cfc1720bd5e19beaff324ae5f874cd0af84ce694d00dd84bf894122bf6a53199d2ea8057db37e14e9a057be7a7dc82a36a8b50bb9243e9c61b9843cb8867c50ef273781088640da142595f80997db8b485275eae9ca772ded5f9fd685ff701d2ed3314de89634274ffb921639ae2b3b2e6dffd395ba0d77c0722060bd2d94ca323b937e2b8056ac6dcf2afa5ff00be75fb102e9e7c947066001e80c7a519c3503a727a3df72ca339f8db338a7a6e2b974d68b5e4a2f75cd283571ed90916b2c619b09e3c9f4102e74fc9b479171f05676c35ee8b2dcac462c8c223fa371d7b3c1fdbe4b04d20ec20075bf13ce68a7d1fe73526ea8cd829f32d79e0b1eea0ec7b7afa64fc135f9c8cc002d2e2a90351102231ec9a2962fb96d8d8f4891e2246ff9130197047cad5adca757c74437d21bc9c805a837a4b62636b5149e15c02e26ebf6ac42383ca48cb489a5c08820deb1bf9a309e0111b3d42ea3cdf09b581346ea12c69a80cf47e7b427fe0d5c641ed1e23538b315cd23d267931875064a8f537965db436f47269a60d6b6e1464596d9179e3c82ef4851c9738c34062613ded7d4cb8298c60c9f7a96f4b109bd75dbb9e2620a1a9e8954c3c68034ff67fedb3adcea225a5d96a5e03dbc81934f53406b997f454af585772711a7b3cc9d3535f66c80d2c12cc3e3d1813975321fde4493058b784769d137e9ce2a4a66e511f2c327e93334e40cc3ea1fc6f03c2aea330ceb0dc3e340073948f5d991cbdb0d2715dde35a11949f013bbf3d4d90749db3b2849907eb584edf1cbd951787be304de7989053b9e384ef79278f749267ab4c62c946a260eb9e66fa223992b44e564ec5c45edc0355c3c0370a9524e4429ed68c90cc30d123bf40994bd14c25a754510940cf2ee9c14630de2b52efb7a3b8c7781f2ec77c2193a8a0941e37eca682b2fd337c74a4bb12a0f1477758d9759f3f07424488568e1ae9c706c6b08ba1b8b868a9146b9667f85e41b9b7664fb433d04d67ff32bcc966df0d2044ffe35ef26bc05ce25917c1b492822e9d4de0064696d9df6bd8b280e1dfb7610715349ed3ff83da7639edb2896ddb0ac3be364a6369e6449a0ec7fa322a11b6d66a83c205378391d746bf7e12bbb1c7aa988fe8c08be9a0d793ef08a8f3ba56f32589d7a2303d665c4568616a7e477a2975a1fecc8ae785e61ef148056da1dcbaae4f2bdb96d37bbe87894292dde0eec39c426a342190168a17698ca7ad319cee9ee5f26a0c174174588ba1b61959206ed371367f505759d6481572db2376a285d09ad2f92378797e10881072dc8463f4a2e00d85778c46642a0895f65b071b1803995420fbc871ad2a698552a936d99e7ee844b306c450cb021fd073532159225a4ce29179c47c506b9429cb28dca138eed324291084cbabc2c4b3c724d1977d463dcfdd439435132eff08401140358b5e7abcf00decccd5df4012ac2530a2990c6455664eb910c589bd951c91a8c30c7eeddd785993d835d1000f87fa1f046c26fe7a034b14f9beb1b1e89d68662092e629c149e423a359f7659696f27457496e3e2289c1d84321e371dcad42e5ea4cef279ab217b78dd2df8eaa6586f08b01a9ac77b200206e079f6ce107464f3568d586e9375f2d383a182132f99ac15ed8c2d15b75cc4e6c8c3148e6e3699c12251a83eea859cbb988ae03ddadcbce8b49d04372a319a446eefa6ee2da51c6342519363918aa982b18bc5700a4a5f8a5c1a77f38bdfc67fdb089df5380de99ad73fd168234569e22957c80e214b52f698cc912a0fa817c4ac45cf732251e4ac8180c015ddf2728f271a0bb63b40ede29c23487e5dd5d7f3b75836693ac86ce070aa6def697a6b13062f5e6d6a0822a5143a14b28e60ad60ac30236ef71e96dcde405d68b3071941b79c0f70cae1905a78e7ef9c73c4c167743621afff5112adb2488ab866e35af5acbd6bb9e0fd2f8174feb0e3e3dca055ba9429705d6c668d8ed95c88561c50b84e8fe5c69720d7eed3a83237963f461e8627a0b1ff92d492842afb7a14eb436686ce71e43df5b7fbe7c4948ceefd6dda4509c836d46d619f25690c2b8cc1f2a69a20456607c8da030ae6f9508d85508c22eebec9e79505504022674a2a9ffa6bb1e54724326aa91d41883c53648b077be2e9e5864a4d5b5bf29bbb283db8a055020c877aa42b08a957206b8b547f34b5b692aeed043340a3332b229d6824575088f84851072afbb648f8a0ed3ab031f3134107ca614880eab57acb8ff718a2d23b6b9b0354f76a1479fe00fc676a4659bbb04e03bebc62c60ad9f56b6d4b5e4c959e430947d35a1f75638c200c71644cb4485941e2756dc00f9df4ea80f8ab3f86af5a7eba9481116bca9b7d24b74cebae16955fd8ec94c1d3a2d957684188824674aada8da4f87a5d10683a8860a50e1e9834c6ff5544dae0ab7be4280a713ace8cf28b405f8ec4269767a74649e12ef07b5a19050d218c530d6c29ea1b18d2133cdab7f0fa43661851b6da737116e2dbc401d208b2453be61488c657218efc64e520466f1c21892710ebad3dd9b8983ae22c92ab7004ecfbd06933ad5d31d3094b0131825b10499aac357cf4908b374efd10c81b5fc939b6c5a3107a2ad3b5f4084ba4cdc24d13e43c57e2ede44e60a3bab70e71f709c4c0a8dabd805adc12c73a9b01e9ba9f4aebbfb478677b6f6460bb135297dce487435f47bea5e4532e3182be4d2ddd3f2f06440a93005e4abd6009eaf521242c6fdcd1f2eebb3d5dc853f591835907b414ad9b16287f084e8fb174dc7a696d70fcf195d4a511c921dc79e00659595b6550edff40083121a4c8ab8ab7875be3813e1c34e63877092b656882cc976f995a34cf117cc30e30844707e4164eb91bdff4c166e262b7dc128b2e87620fe26d3fca32a9bad41a95d1df3ce2881bf832416b5d6692a128c3dfa0a1b31136f669b4dee3ecd1a7363749172cffe0787cb6870a47e700f2259d43515406836181ad5776a8ff3cf5915910f586ad919cf875cfc3d35a3f814e21c9f4d921956a29a1ecbcb9033b54a817eb0725fb3931db55a027a8fadd840dd7e0e0c691a67e4c1adb61efacca5ce07ea28bfcfe5112d0e6c896e5687b236ae1f0c949f0c89ed968f07fe49297b2680fe27428de7411d3932f50a040d2dd4bd89d172d49ff3fbf993857eb30d0e19763662ee4f6add3b37e273ce65ca70b2693b88e508b53031ac7e5290d114abdd77432c2f8a7636ea85a2061337365374d623ce9812dea8d0fafc9a660aed715a386562846ed94a55f3868e0a151c186032f68fe65d038118f1b94e1f8fc3ce80f73af3ea65834d8556f459e9929086e65b19a44cd97081fd46071e82c841a3dcf2d76c1b23160f54ab5cd0912bfa3a23af989069580d7429db033ccbd7d5a3f4fb66f258b8dfabf16d52cbf992472b16cb8a193456e3c322d5f736984f7daa2fd5b0e63d3c191796954326321b8e94766e9644a5f79b3ca394bc1801a8abbf45241e6a3b0c36dc59030af7e385591bff1a54e6b622f888d034051a56c7c3d650ae8431f405021ab8949c8e750bfcfed5b3f1bf58e174e9e43f295198121a2fafe3eef746207baa44d77e3eac97d47a901e7cdb769e9144942a282cb7682351a744af2b8ed865d21ac51a3c331b63ea5ea00b35610ca36a66e9cfaa87dcb546d6e31684bf197221d16141a81455d64e628a8677efd0479cc0c47ba845def81d7e79d86864652099ac6b02ea28c40d10aa366aec23a5326b71b5e1fd66d78b813324008538efe2fff289e51c461a7cb218a7c80f1ba0869f89334564c112251219d7d849c944d57614ae5bb442268e24c9bc069058122b911e703b2c24c3e24dd65bc8bb350eaa8aadfeada52920d779f13e11b2687d73ab80044965e09268789a6c090969da3b0cada66c38bbf1995368660b35a83d7ae06a7b843ce2bece287ff51a02abdc6b378b9d65199ef0a72d20219495fad061224d241165a544a7d7df22322786e74f59199b682bb014a98c170b1a39e52e9ee645184637613838b0bcee7f792d0e03dbf492390f1790c107a2174864ccc009e34995b029074e0da5cb58f52282adbb03e9028c0abd48f6d5b7c9f01f98617ada1e93d0d45a79a2718f3630cb1e3c51846d0612e8c08560b304fcf4031ae96694572056b24a07159d088bb37ab4d38d0835da51aa8c37e30f5a145d4c1f95f6dc3df41cdc7f89ef4d0f2b5a280998d1ba9eb8412d2862063671b8617458bcf0b2f0f1ec2812f7f2761e7dfffffffd4ff6265bee2d654a29052f0909092809ce0c050a43f9ccf8d1c44ddcf4352971539307e1f0516742ac3d6a5dbd42724b7dd9fc3ca8fbeffba17383d983d887bd51c70d75eab85023759a5d17e2f0e1e0416ce54aa7ea1eb041aa30871d390e0a6a3f053d67ffcb2891992425e1cde6264dd3b4ef4971d4cb90a3529b73e0d8337e4ca0107165e241dd771772538bad43863772541f018ddcc568d62eb1ee9ba29a9aa2a2fa888df6a14bd045f40aa494b2e3af518110203cedf9c9987f64f899305e3d5445e1ad90c4a9cb4c9849163281ea611f1c877084a96cd42d1ac2b6d57449ed1f4965186973936ef5373b912d6cd01622691af600e23853bc71998a780884890e15deb8501cf3e9130927c0a30f5b1c72802465044a9850b932f11828bc02ff0e063434a82b266aefa0f6e43250672797b75f9ed05f000b51a52376c09efe3eea3e2266ef9fb94e0cade383c7beb277cfe33a23d5afff0e9d11eb80e13430e71b9c4c46dc83ad48791ba31fbd1a43e37c37bffbe272409de73136a845b01d436aa2d322d890b29d76f2203e8a2151a49c6e9edff9ca2a223b5fa444f3f0ecfc0fcfb42cdbf99dffd1debefa1d2794f27213c2d884decb6f0731d3afbff7dc04695e32135be11edc85d9c94a759b1f3d1e48ddc426ec02d6bc0b1b74816df917e9d80eac2316557301998f79ffde52cb473794783c3ff2110f0fe223a46ec23d3ac63aba466a529d06dc15c720d6b907af8e7ef758cb239a221ec4aabdfd349ff0003ecf67df4478be48892cdc164130ccbec33103b59f871ef96ad7d91d3ee1e19fc7860f79c2a29eff4109d80b75e67b106aa05977f5fbde2a88664f5bf43e87178a586887e71b7cc2f319f8c4033c3c3c3cdfe11310f0b0f33c08ad6a084f58d4423b3fb3aa31e11111c3802fe048a3e24153537c446b67db59366512de8a65124edf18b1947e32cc69f2be7e75de56bf9cfd59fb6ff8851e73ee5e1385c73e4516575cb982a972303844fce910c77149781c87f38437e79cf3bd07daf371e1ae8be33ec5fdc671e17844acdc770dc4f5d4719d3490882b8d3571dded5f778f66d6b1dddd9e725f9d205efbbd7638f650a9171cd7dd3dbbbbbbbb7fdcdd6def6e6fdeedac05dc7577777757ce4ca3a9996974cb34ba750bd26dbec875edb7c1d1aca7af48a5964d2a25d5b2d9867b1bee6db8b7118458d6d3e7ba66f681d9febb1b11397f58477f90ae1b4d6faba3b5acb8f087cae933ebfef17136862e071151e1bbe1e9f413e9d40bb4ce6696cd9612bc5977c90a2c5450c192153c5802133db0024a18fe194b4e9bee54ae7a93744529cfa49487aee88af2d015a53c5c4ab2945366736bee91fdbae473727e952bf0ef6b85ea95ab0ca0ca5f06d23ced3dee91bd7c0d7cb1454dd334eda56a487b5074e2699ffd1845f65ef5e4d4fe47b714c3355442fa7f34ab8064ffeb929f8121eb604de1f5e414eb90bf2579628b5286485e5213545ac1e447e218c21be9d10976000589082780810410d60b2a595260858923a27cb1031e1891a9209531690172d5a985b15a9af2059309aad49e9e7c8a55952750a8f2fbc91be9aaab02085594f09c78fc19674870ce3a42c2ca61d3ce5e526609ca30c489976551781e645dfefead0d2a00b7fc671886ef0faa3f0c3cfe1838d411c22f9f439db9350b695a74cf62e08d2c030c91610daf9f25a5f8f9fea19c3635ad8a25740828a92a8c84d08670da88c888d0429b9060c6b42a6210845400115332294700e19222796b992081028a60f2a2601a9c24555125081a58f0e4ada50ff3c34fffb444e07575ab40614104d4173e8881020ea0b4a0700694127c48c38725218c98c263eed147abf6be1d3e2cd981ded7a2949ffd942c9d78f2b746e1c9ae6357bbe31eeb7d2357395d7ede1a9ef410c90c8e81100294cabf2f50d58f2ac74ea344a5cb4a6e97fe3a92fde55355551f1d5413eb9c90a830421dc594142a3fd4714775679f3ae006f81085038fdd3d03def2e7a4e598f66662726a402ca5c494b4af557b47b5796ef1cbbbcccbbbee2f594a29a53fbbcbe94bf8944b48ba03954ae876ce5156ded1d7dd7d6ed7ed673ea1a2f639e62d89f1111bcd1a780cfeb64422146a9af1f33da97ec8321f7461fc90c4053318021b48b0f1c4162ef025234820fc208d0dc30a2c54502e62204608360c2a94e820a2a80111413461c51629e613543cc992bc20aac9921c3861841c2009c9d45da2e2045418415997ec52aeaffb0fbbfbe24ccdc9a5e694bed3478b3b7d939967ef99d30f86393316acf4f9b548a9d47c6a99bb53f7fc05e27f546366667a84d37eaa25993d70f70b7082f0f8a1b668f16056ee53cdd9c185bd9d7c7f22d520c1db976afcd775b787fc662224b23e89e92c8085e6b3d00c8f3aa1f93a4160aa3f4cf5704c529f7c6086d45beeee04b1723034cbb22ca39452174cf1831a34955fa3495514c10a57f9b31774107e90aafc13092a5e9040c2a0326f7142e5a5f27753073989caef7d4a4cb85229156b4e9516540e42f570fc9a9fd54032955fb008c112a1a5e2a3eed2882bdd3d0c222485a12eb8a89d85d3c3f1abac22bcad445cc765ea2534163ff6c5aafc3204d9170aebe2e7df262a767fc0b053f97128eaa0258201c870aa2cb20462142af38e0dc76d5a29a1b0af7d16703b65f33c4de129970bed7aa8bd7358973fcbe51ea52c5072994b1135449f2975dc2ce28e9fce615ffcfe1c730846731e86d7bf54c695daaa20371e185eff4ef94d4260d2fd532e9722597cab8ff53378fde1f91cfe04d90d77f8ec87496818d2a2ff0c59a101dc86212d36ebb4dca1c8779ae8209ea45dca12abeeee51aaf7cb65225c8a649101240da4e3d3891cd471b354c7411d774bf528ef70e4a3f61f4a0668a0558281d2376178232321212121f506161529215f7e92da034c93d0f65c4759ddacdab1fa4383194461243662a7f6b3d20a414a93d52285c6af528e924003a1f954cb32b044a7a713b51f072ee30ac99be1c849d38974007054e0c9dfd1416c44b776ecf0f161b1562b24a4f1e767859eb61a74eb679b81f7c3492218346852e43b25fa026fa49e5786a7d33a5078d5078f1778238fca38108166566c70813ea7d2c7f3993eddd215e73b28579cd3bb5d91f967bef2ce8afcddb6f85795c70fc2aabf6ae367086e7f59e342559875f9b30e73577f1fa52ed5ff874850007649698b231dc5a4388247f5e75e985e411d3b987ded7bdf60e99ccbcc54db750791cc5dba31f306aeb7e6fc49837431b8658d3f9472f85e1adeaf385948f0ba21b4940fb2094b5a1c5551a3cb4c428225253021e1042dda50c284c2092d2fe20c1b6981b0c61426a2a8c0093880811a501c21460649218f5220b865ddb4c06b4e22c47df645a9df3e891038c45beea4d39fd2e94f7d12a1a26d2bcab62442dbe72d29bc19c6accbe927e4de7b19702a9e0c9d0af94984f8a5b7fc930811f156f69fb7fc95b0818b94b0617e1616cd90c32cd4b1613eff0c89784b4847fefc2a3cf0c75b2ea512549f413f713390992a85711b55d5811ca8937d12213ff256160e31ea286fd14f228484236909a698d00a29c14f7f8557c286feedc322ef3bea578876a863c3f6fd423a94071dbd6a8067ba4c7e5405a5a19199c42d7370e3c3dfeff6d53bdfbcaf9d97cf6a480fd3d81e9cc43ae4b7e49dffd13796ec8093665b37b887f6f2c66fa0b7e28d1b52c78d1bcf205df1c68760b7e28d1b3f321255bebcf1db3be8db167e6f7f23e4a31691963012d3be364a299ddbb67d2f1fe6c79eeafe3d4339ccaf6b1f78836907eccaf2c6cbf0e5f7dc06eebce83fc6e0f871ab39defd7380384016c89f55bef1db86cfe0b8559877d5d08d23620dbf55423c90c5194c2085cf71770394350c290cd34ef4f1ce55439de893fd8f097ec4e1286dce29a17ce328979a1af33b6683c6872aa79cd27307bddef847b51d1e77779e2b4c13e4df9161864fa337f0805ba90c2e77c04feb181cc51df0d33cb06b71d5373031adfe7b8e763facba626228bf116e9449fc49c2d37e14abfc61256cd00c0bc5c43d6e60daca5032dcc03d90b00ef9854c4c5ac73c2a21abffe8af7e5534d0ff543330c8460cf5c37a16615ee4466b78e34679686407362ada818d36b063207701398a8bfc22483c1b703ceb7f3497c4f484373253124331ed0b2ccaf13afcac67a1acbb01f67c08fe6f20f845907096653e335343f33b7f464ce13371fc80be1fbfc7ce8fac9aa5006edbf2df0177758caeeaffb5aa8b200aa0d370870682edd0ff5441e8f3f71f1ba578c0aeeef473be2807eb7960d1ff582c168bc562e560e5c0f139fc7b5efe4bf0250f0c4ec334790dfcf9687a402f07b8dee209e98a86b09d704c5257211bb528657e5d270cc7233caa7f4ec838e11716edd0a227026f6428661e3e70f0f8f1791f8e276fe48e7117495ff33c1c373734a43720f736e098a3662111d780e357679e061cbfce844c0c9594c450ab4ff4f91199f5e327036f6c26947a1eeba9b34050a6004419b2775d53aa5c2c90b048da97d6473eb4230dcf816c6c5f3eb89192edc147ac43be6cb922d3d03de297c2470e76c7806ce5a4265a9453fee769cf494ef695bdfc1e9c6f633ceba16a39384cbf37fb1f9e038e3d95be0d387e5b48c4ffa37b3ea9276435a7a38537a5140f62b54f95b3d2df2143d1fd354d63f99016ddb5a11d2dca9faeb9bb873f5be53bd287513d3a3b365fd26cd3380c9a492dd39848c76ab93e4052feffac06daf9d5cbf769209d97dbd39ac7f9554991b23ae44f1b2d6782f46dc0ec73b80de4ca03eee2e01c58ad9353d37eaa86e6c3681c27b32c530dd1988c66dc38e0c8aa33f593079cbfaa4dc3c18356dd62a9fd9864f5dca986769e53694f44fe88e3959f06e7573c5b0f76621d47583943783684445a944f1f07dc791e50e7370e6c8b9df8e504ae727670f1564fe5ea6f562b1cd8487e28b32ccbb44cd3f833d6f8358d5552ee93012a0fa932540efd351b671bed1d84edaa866cfe47f37378e4a7fa3bc80fa312e2cf6f03f2fb88dfc762411d83693f323bf1b8789a114e72a285fc1ba83dceaf6a933a60afc0de01fba88d5a94eb922fd286ff9ee7a438e1f4c0e3183bedcb5f7e0fd8f32cb474c594aa42dda5154851b9addb01717e05faeb8039dc64555936e7bbf365b2cf62f3733ef54c56cee7fce855999c9f116bced36cdbca79991cd8cd4b1b4e4543f60cdafcaa82646ff33f5a03190a47d1ba09a640afad0292fab9deca09c7ef8bc0d7e12f4a22d4f33cf43c0b656fd3036adf420c0ad13e87172a7fcf73cf3bf8c4033d3d3d3dcf604f58d442e0ab6af8079f9be0290d84f3f2198a9ced2585e12a6f8f6d75f07b9f037e20b860e76d215d1787e31766fc37515a942f6d42761272528bf23d203cd6d802aa1afe8bacec9798bf87bfba813776ac911a481ef991d956edc11ab0e61d1c8754c93d0d38f654999f01c7afca844e440c038e34f5a563dc3b0c3f5a943fa481fc191c7f5b4d49234be5a74758425653d230a332cb9a00af0784e28d0dc55b3f2dca90478b92478bf25755030bf5bc149ddea1c435cf6c0cdef632b353daa66c383938211b754e185bc22276c596ca2a205d659ea1b8ff088e58aafeebb4c9cc99f7fe6ba06d669b993321cf99f7dfb6779006fc544333cfcd8032328fa45dc667405957b56d1f64fef632a091e9acc463a175550ca9f79e9b41e6b7e75a45c3cccb84d1a5cafccc732fdd107dc9fe12a4f9560991f9992a622ca937ea56f1a24b65150d313133e10e560dd53cf7a3e5fcf6b481669e478b3099df3126a93ed4f0c69f6a0426138e49aacc1bf991097db4e8dcecb6f7a09c9cf047ba8536e07c4ef8ab67214ed6e0502465e35024bbe4c436b0e655aa20335ff332a08f167f1a0816c3ccd73ce772e7737e2392b3b393f3ae1ada5435c83af3379f6364e66b5e82453aaf63130a89f91c5ea8db1725115a3d0fab3066cbf92de765c0271ec8c9c9c9f94d27272cc2791d24b26e7f13fe34d04c081b3949a68aa126dcf1f38337728c97c87cccc77ccdc7bc3fd79c7fdf8facbaad827e7ce647afdbd374d00f6ff96fbf3d6f0f1c5887ff168e5e8bdfe69f8dcd8dccff6822fe3fef8f03ccfbab54bfc950c65f158587f3dc736c5f32ef3ddf0703859301b932946d731970ebb675db16ea6c32bf2dfe688ec96ca11099f0090f398ff31b119c2f52629bf99c9c997094f96d7b9c4d666395ccacab4200ceefb66270de655ee61b88261cb91ad95edac8af79ce8dd8fcf7d90e329ffd28f3313fee506dfe03479bcf6a6ade553170ddb6c604a9091db0dec27919d026ac0147991f699d792eeb6a40f93423d79b99979151e2c1e3e74746e6c7af9bcc8f5fdddcabf9ec6dfe47df803a78bd9101475a657edbfe876b5566bf81317f038e3bd49ab789a9b951d1c01ced6ac0ad34b44e70e498ffe13bd418702b0ee88123ad5be8fd0f9fdac3c4469a3a3214aa316d3101de7a8f92670403f2bf6eb8aa21bbad9543926d1a94825b16b7a5a0042f539c52cffb3ef932e54ab7e422b74899e5eb0ca5d3944e9eb73a54cd19c6d055a98e1fe52d8b7d71d9987642e08d1b535454038d2f53b234d0f86969a07163aa3d76571a287b62f251c4a819aa8de8981326df82249bd857ffec0bcac95bfd947adef7fd8fe2d6a5c5dea2dadbd868a1eef3e8a0d4c684d5d1bf5bd4133ce9d44057f665d4405b7770f124d4cf23f7a02dca5bfd0fe54bf2984c244a3deffbfe9d6a3fb7eafe76344de8b180fc37c5bf0522bc19760eb620024f470759b587c1544d8ba241be16eac0d57584f0ccc0a548b8ca306aebe2a481b693500dd42e7582c8ea2fab87230f955f9e6c946852a71195e792cad3a8b2ec52f925970ef22950b349f5291ea4925d5cc988698937d59e46ccde5289246d4a9bbcd5b4c9839e9a9a68136daa4dab872a25dad48637f2539353652b4e3c51a88164f7d41bdf206da5b3ca999fa90a4a5b825b964ef6fe498424a89365ae436937dfb351aaf245529d3fb27cd449b5ec4a9d2f756ae8976f43bf0c8b9490df9f8536f06f61913f7fd315ae5ff66c9dce699cfefaa66dbaa6bb1ba669beb3de3ad55c77adea97e6df51dba7368bbf797e67e6fb69ea841dc30947efe36791b986959861f8a90bc78f0bc74f85a398312b51de58d2ea8423ddcf09470f271cbffd2f1cff261cc51fa7ba5f53d4e2fe0ca8e44f1ee4635b1b138e9e172ac184235585e3b79f0a9fb670e45179763c6969387e9b71d4e7ad56d5aed439e182b4a7bf7150aa53bfaa2156b7b4dfc06596377abd55b603c1f9737dc415c5d868a4557214b7bb9ee4634be14029a17400bc99a4c2913a73a92c95a5b2549651aa75777777a7f8658adbb66da3f9946af324ea4d5a8c62448b1eb6931671a078631f31a9de40485bab9e38e19653e0ad15509c547f19c340befa56d92d10e13943e1b6527fee2e4005b8adce5e554f5587ee45d60eaac2b4aa838caa7b8191b158e5cd6181e7deb5db17112b551db2ca441cabb0172ad3e0c52a7f17846395b35851c6165354e134c594de6f75ccf046f1f3544e58562d761005a255baf2c21bc5da3cc43a859452765048291bc8a594325c4e825b393993383314fe2c41b946b8ed9b167838bb5c6a531651a0c8236994e549090a6fe24ca2b858699212a597f491962c549438899b7489aaeb5477fcf0f30e6d28a9d4556a719f1966c7cfba606ab4f0ba37088e1a2dbcfda902c2e14ae9d239ced65b9bfcb232548bfb315ab89549b2499728272b4d4871245fb2450b1315279dd4b12e579ca634c1463012d491cd42061c33bc6d91856386275b5cf6177e0e351970db6266f9d2259581d7ff5503b9bae4cf26540cbefa28a954964aa354aa4fbc9c107851bc7eaef25bdc178cab873eab7e1fe22a5fd65042cda50b78231542142a8b5027242317eafabd1ed491a6f60f03e1858bc39d91b4e2806cdd17d755128208f56b71eb2a095184fac1248cdba83bd7c5ebe290430242d65d1a820f940d982d57aa4a8bd1db252d4cf503de2e699952f7bdee929626aa6a5ffb75a3b1e2cfebeeee761590cca0d2dedd2e8174b8b29a823e5a641e73abd7f3b36d7bf6766f21799bb144b62a866c6edbf6ccdffdd9d63c5ae4267125b81567055e566f3450fbd75715e466e4d4151ce3e12d5ecac3a5cf9e3b3f6564c183673b319596c9679095131b4827487728d6fd4f12fa78a27d87eff3f2c9d127a43d4df7ff4085b4e7ae8f635d387a14d6c7d18f90f6fce00e407b6625d57d1819fde009bdb000e077615be0ffe11352c2c31f2db6774b2e8389b68294272ddf500401aaf2b3b80ca4ca3e95df3d26f73625b8e94ae4b2ac331f16173ed78c460b3d18161b48cc5695b3a7bf0dd45ef7aa81565e85611fd5a3f9eaaeb81f9f25d6293baa1ae232eeb79c4c35a48519a53506ae0dd31da65823c193a31f5cc8074d48891c64edd8910f42b481bcbbb329a56caedcdd9bfdc0c1ecacb265f6655916ee77077252ba37e7a3d56ce53ebbfdb33a5d35b47582dad66db3f74bcf54190fa6bed4ee70eb56372584558b528aedc3ca2ab7fb6ae7491b48ae9cd23d7bd726336bd9d7dd8d18522ed7ace1f1eec32491695cf71b854152a5675a8aeb568479f985d9a1c2bcbc84635195a9aeadeac027319c16e92fa970d795fa1457e9ceaa2031af814f62429a6eb1af74b986cfcc27959abb160f975da35a66c8eb9a5f7d672699596acb9ae653669a9639e179443eb9be2b3aa774399dd022ad780c6e2baa72c8b60ef0949ad4b215674a07b0579c9916b2104443f2533b32cd69449f9fde8d61607efb1770577c81d9647d79f9ed25942d6ee1762ad949e66457aebb751aa4924abd279ec6592853d02e8986fce7d3ba99f4a9fbacee4e93bdee2fdbff684d0b97abd3cee2f187da04b9669dacfb72594e4a29cd6896895e5716b981e68622abefa3ccda4b3976ab59e972911b5a24227be0655cb783ebc6d5cb8f3430d91371a7f09f488b383fc8324ddb3939aacd15a57ce75f51be0682caef330bbb06da90dae0fab90685c00ced48c36306c2758802631ca1e145e5f746fab4051043489a4ab324e5fbf134ead9ccb299c2104f4d68e1d2930b6a963dcdb3b2503c42cc5b9b83079e5677e989c9138fd65d7a427a3afaa8189c94babf340426754324fdb520ad90a1c4b63e4aeb05174d57f895eacb65471db70b0b7564231e3275e4265a38195577271b388d01060bc05063290c2a6218218695165c811a03690c294b6364c182022c3ec0f224d9f8f934279c0f599665d90a7c985d202105d2873356f0124402b6d8c1144b68d962898dd9832ed030a2ebe20c1904f1a403277cc185ca954bc8c1199feb8b20787c51459defb3088d104fa4c106133c28411421c9832526b2a480f036005ec0a0cef9d40ba73abd061a797ecbc593359c40a50d17b0e08a286e708457c45bfced130b31d3acbaf081a6caa75e0aaafcaf8b23bcb6041e1f7fd44a0e9614a1c9833cf6ac5071842c82948c045e84a869e58a10bd058a9c568850e7e4a57286940851bab54d50283c2a5f166fa55b5b47b16a44b8d2add57858e16202056c69e298f8f85341044870e2815753b70a12889043081e4ddd2a4868238527783275ab4015b1c5f3ea56814a4205243c9bba55a09c48c251168e36d0ee4eb99276f777f713ac245169beb06e953482f018143985952080becaf9ca853ab27cc61d3ebc1b9b65b700403a2f6123b46052fd084b5a3061c907598c60698b2b517680c552d3117c588ad20265898b2e4b3f687a028436765f9303759f7a0e7e021bae00c2084f0841064ba060022107180801064f28d9c11231777767ef27ea589cb999b9a04e46537706132a68a209f729f5f424babb3bbf3b3716777777ee31a4bbbbbb73636131199e497777777632ba63edeeeece3f72952419eeeeeecefd947999bbbbbbb337167777e777e726a3c570f666777777eedaa18e907ef9cd8e458ed14c6e021f00d14c58b25041d1b4250b2c47685a38220b348ed020d1a8e623e6880a0c8ef8b842fd8a91f6d405c9080b946c09131b40c1b263c6c90766ecb822fbcacfcf2b9c94317184169c2406d44684932ba0b04829a744c2fc058371449447c42f852e66e0c591ef8a966dcb931b68c142fd4877a4f3b94182cb123e57322fbb728575e8a93924e12da8c00041451954904c0ef2138cb043243d91f4184c2f4da62481090b1289448ef15333832e68f073659d382e74c0649db0f0a05e83c838b1a2081e481c898fb16259414412ab7d67e993f3baf63cc2922112e62b417e0ae9e124dc9ff3eeb7ba5b802b929e01d7aa96cf2a24bebe2a863252eaee5ab7ad92322cafc1254a149410c75d837c4512672409ed165cb4408a10e74a9a100613a11e609a8a580444f08249a8879ea4c98458194a423d30b310dc72309d8163513582a4ab94a10e5d8d64e1760db8658dac04a9f6732fa04fb73e160aab56fd4b0f1cabe3a543d104de56191aa13c84b755d0b852fd99634cb1260fe21897e618972eefda208e3df9a0b168f4732c8d6d6d9555352cd30f269efc6d823128a02835d2902a20502b14e1414dfe4c6e4a8e9a5889518ca981baef29b6f34fe6a7b1ca1c638a516951c95127c18561e49852ed576a208d4a03f9c7a05a037d939c83f3e52526a67350a3a99c0de7600ac7c14ec74155652d1ccbd262c782b0313354313cf1782f61b8dd8aea493918199b1c1e8e3d79100cdc77dd4f57a7027df625bf5f876b7b541874e8f87170fbf96129030c04e851a7eca01fdeea1ce4aafb9299191a9a2536526c6c6e6e8e7096c0c1c9c901627583d5cece8e6a28e5cf4d9e4c535982fcf3532ee5d6e01f0487b891831b2c160b05265010455185292a743e5a3ca8475d1d66703e9d8f6a48ea00e2ef9896da9307937442878e9f9f12b0a003163a1654433eb1206c10c7a0bcf5145bc22e618376ebb65a50d2826a8895ac88f0465612eb3a72ec8963593a8895c4941cc5b6d46625310f3aaafd635d4d04c1108e5d1d57c92b22a80b520d718c4b8b354835432a1c532f3ff32d16c531a8a6e71897ee59462584fbee635441e677e1287f863a466648b7452da8a35ab64ab1a8065a185aecef5440becad0a6053eba3ae3820bfbdaea23e48dab34e4070c36c4a0fa09a87d71c7a55e542f324625c47fbe2743ca4a90b8f6d105c7ffb5b8a4c509f3b79e9c224afdbae0bc1b5329ed53dfa9972afa23f7dbcf7129493f05d2163b74da1d1a91df8554be67dbcb37229fa6a47cfa0deaa0d5961ae552dd214db19462b917eea6b6712a9898999aa6a01ab685648716611f0e4131fce392bf2135ec36e5e8403118a98eeb86e873373743eccbbb2fc78a1ac87f6a6fa4a808c9b64313c996524a0f2606a42faa0e4c894016c80631605b41439c01433c08b62d1cc6f0a0dc45a08c064e4c7184ca7fa31ae2a5eeb9a8a3e361705ab75a655a7c299e9d8eeb7878523ed85628a26763aafece5907ad76b4c52692d57f2553d9fdbf00814603c1182e641686345fccafbb71e3c68b239dd4e958ac1c2b48da71e0d869121bec720c29c263ff1cacc5c3e15540629cbc71a1c623ab9a3d677cb4c41f899452ce0e0520bc8eb1f68cc4466ccc190d278aaaa199f14b0ec7917f59cb40e92ffd0325bb8208542891dee7d1789e2b498c2580a18329a260d22cc836c0860b90886289255af083904b41a35377498b1234b8c815aca0026ffcea2fec0bf6420cef3f807dc10600c3fbbbb02f980b42efef635f301f3fdebfc7be603d06f0feaf7dc15e3ede5f00fb8209e005f4f283f6050b5a97ff8ef707da170c685dfe2cbc7f00f6050bc0bafc7fde7fc7be603bd6e5bfc20bd682cffbb3b02f180b29bc3f00f6050380389ff5f375ec0ba6635dfee1fbafb02fd80aebf2fff777ed0be65a973fcffbfbec0be6b32effd5fbabb02f980aebf2cf79ff14f6054b615dfedffbb7f6056bedbab49779ed51d8170c8575f97bef9f635fb01cebf27f797f1cfb82e158977ff7feac7dc158ebfa1bfb82dd58973f7dff705fb0705dfed9fb83fb8281ebf2eff7ff7dc19e58e02bf016086770fc0c7af9821101e97dc18864f8ecf705230af2f4f705232ac0a77e7bc188847cf7dcfb67fb821119e05f7e5f30a219de7b98178c88e6b77dc1880ef0365ff3fea97dc18868f8ef6f5e30a2219ff338efdfed0b4654c3af5e07468480e7f99df77fd9178c2801ffdff3fe30fb8211d9f0e183efefed0b4694e3597fe3fd63f6052352c08b8fc2fbcbec0b46b4804fe15bef3fb32f181103dee755787f9a7dc18888fc0aef7aff9a7dc1881af03fafe3fd6df6052372c0b3f00078ff9b7dc1881ef03bbe0518118f07fa00bc3fcebe604437fceb05f0fe39fb821141e07d7c8ff7d7d9178c28023f8077e1fd57fb821149e07ffc0befbfb32f18519117fafafe3cfb82114de0617802bc7fcfbe604414f8187ee861bf2e7ea20a843f2800f6ac8b7f0220cfbaf88b803bebe29700180150675dfc100073d6c57f0388b32efe078037ebe2770068b32efe068035ebe22702d2ac8b9f01e0ccbaf81700caac8b5f0160ccbaf86d0061d6c59f00f0655dfc080055ebe2af011c0272ebe2a7014cad8bff00e0b62efe19406d5dfc0600b375f10b010bf041405f17bf0c60af8b1f08180319957fe885ca0f0396ca4f8001547ea1312a7f75a1f2ffb852f95f18801895df851e95df471895bfc7abf2bfc0a8fc021040e5e701eeba824220a7ca1f0070d7b5036c21a8f2b3e045e5070050e5ffe9a2f2eb00775d2b80bb2e17b8ebf2015500f953f841e56fb150f9452e2a3f0ae0ae2b07b8ebc2118e9eeb06b8eb0ac15d1708be0f2a7f0fc8cfa345e5df59e9e46051f971c0ef8aca7f9342e5b7b1a2f2d7b4765d33618cb7c4ff52a5f2ab72ecba381c1b48a9a8fcda8dca9f4dc1df2b0558d1c8525b485ba95c79090d2a756bff04fae5e98c2c6a4edda53396a8bce1d45709e9a91c7e8ff3e4350b96089113cb51f2a4d440dafbacfe924a3765e164e5e94af54d4b6d0f37264a3def7b9ffe3e4aa78731f4fcaf4b675ca9feefc4240a977538aa746a71eba26d51393b24f8be2d6a6e514d9452598ea234715c9840d13d1941c9625f502f46e0d07fa1b28b67f4ed6f514c4c4c4c4c01a3daa884cdf66441ec10d1080000004100c3140020200c0a084422a16028186ac2b07d14000b869e4876589889c32088519431061942080284106008010081a99a1a00f9eefc6488e4606f630c03b257007b312e2ae872714fe80b28e7b11eb8ee7de9fb51e6850079288c27a492b5b8cc4e0ca9bb0b089bf9989a40cb37aedd12c2b831f3c51c25944241c49080b87349c3b3196f2756f4dcb3d198e2ca92cdcb0142ec2e6d9a85122e32f3b3add43d7208e66a346218d43b459036438066a326c6bac0197ec1a6520c2e8177586b9871c38a360c139b33ce1da8864f61120b5b3b2150ee3873c08193f2949d54853ac1095ba41d9b08498ec8f5c1727ac9b53ed393922d0d19372d94e093d1a003614dee8426b5a2ee7a151f27bbd18d3abe4f18e3775fdab7e6c3dc5ab68d610427098dfca8290f6bf87df1fcb8c19f3f5470c50ab5092b3b91e26fa60887da360a52be693d1415a9a9b4578876e59f20d6152da1bb68e17b847b02ea233e1e05fc33147ad4899561697016d0ae5421736d6450c4788038dbb671df1df7d0d45864706ea12ba9637766a89a6a626c072680ae5847f7d2207541c40690e54b32c3cc0b56d824ac5852bb0c6dd0736b673ff1903c942b182afb33ac6d1d7227518eaec7d4f2b6ab2111680e17dbaf76c521d58dc44ed52898414bc34162d8b886d5c17be1d1913a10845f76c69d02de0d37eaddc0b966968e51ece602eb2b6e5dbff1c89d1626cbc02416219ac459e80dff86eeb4ea4f6f02732c4450bb31ad8aa620a833d6f18fc5c2978f15c36c5d7352b7a285ca6c824933d01bdbc28bafe835b5eaee12535a7d930c75ec6812c69bad96de61e4e0e1b876a6dc03d3df6eca4951ba959ebc6004dbdd9e79529d42d151f3ea4da13185da3ededf38b31a153b2c55a8484415f44dea024194b51e77933876df34bd09b06f0266e5f9cd888db8d8d18c699f7cef8b5f12a243ab83e1628118d96012cbac3b7782f5f40b66e9ce58a3252305acb9042a757bac77fcb6d75c5614eebb15e9259538483d592525bd64ca04f641689c43cfe2d1bf972a108a94a555e3fce690c34696ead7ea767bcd61f08ca4236318919832d3828f02b1e4949dfab3a0afaff772198230a1883356505b8c45802ffe1a54d5ec1362a726f07c98c54585572940f8c54fec247823cea4e16379e0e25f19ac40c7eb86c431d1572332914274e5def29ca91e70cbfd20513ca6683ad34c2ab64ada6bc3620a8784f67158cb47fc9a3f3aecc7bfdb47d4d2c5644e5225f7222dedf70bae09f1c2fc1432f051c83cc8c3dfad0f45f5387988768efa311aa88f10c9fc5a2ef726851c20bc7d2e4405d39d057c3428967017d04d98284bb7992f1d85ca071555b65112092d52c7015e4ea5d53aa95f27498b5e20bc9be0d88f82633bbe908aab84ec82d91f496ca4ab16bcd68b6f144e1c46158a7a2d738b6dd9b98489e7b655454444b1f9b044b46977e7d1ad5f477e067e20dd9a6c3d0e364303e33c65a092e9b6f6ebec860a55f0f9de0ce7ad3c54d0f9513bbc754574b23e9f157b04780733720161060a77eef886c4380d5a3f33540ecc2dec2015f41035ce00ceef0460bacb043f6b0f7c27b84403513f0761dab0b0231a8b42dc3889983274a0b525c4c89a73b6982dc600d5be0f354aee607f9493b4f784887eae02474533726db297d12ea77290102158c9c490613faf2a861e75309ed024bbe9b9c708a79da204ef695ff0df6a74b70c0871ffd65672ac0b7eb68920e9c031a3f461bb613aba7b1a2fa8b41d178002e5b3ccedca14dd865f281f9078b7f50a41029688ea389303ba059932d58a0663e663b275a8e13b1df3f734a14bb94ba7c33799d75beaca92a1d490897e38b2cfd64b4c0b788475362577926fe0921033a0949b9a22bd037894db162657dae80072abbe851d93bfc3e781b07cbc2a50271165bfc27848db09f3f223c43227d07a412d214578360a672b0f9b31c2d5ff4f74c88baa2ab6f141d1521525f4e08e9bbfa201c89bbcdaf1495af381bf3069f145271671a8191b17422b12292a9a961087402df67bf614660199c9896d6f5775e7c567213d4c8ae730cc42221055ff0f402c902a7e6c875859e566414a9f56b5a1745a81122f0dc94265205b401c0ad2b8ecbe818f4854ef8ab42a0b53f3cdb4a84f755b75126cad72242d20e5c6337928e939902c24af9872da09704a499f320f751f0b2fbd832427214c3cc2118de8bc28425fe1f2d1690077c6a9a7f59ca0a2bf3172667840627f53dfea699579ff59f7928f3cb946c22cea9afbb0032008baa6b79ff8a00c3667ccbd3fa1c8282c180202ae064f858e3156109c5c79c3778102f78806289ae434d4485e0b5f18a4f82bfb40c60191411f01d0631d3dc190ece090fae56035c442ead1f6edd0803ee7b953be3077b134dd02c65c163b7cfabe11b0569d68b37dcfe6a7e7699d667d91793561df202c12728d18db0161e2bcf0e4dbdc8ad74542cd6c3f0811f2b1a6f2a40ff9c8073ef5299ff98cf1b1b468aa088745c1f8ce38da69915dd4134eefef453338ede853eef7f2941f48609b454c5cfbef3eaf2ea21db2db879d8f3c39f2b4c2cde497c9b1e84f28535a747aad42ebb249aa31c0cf213141734a510a31e002355e1d03902fae08c22ec0859df5a90c8073145ce05762c85fc8c7cde1aa96b76eb26fd81eb773abd2523acb6dfce923ec8f9bd96a727931cdb5b724e6266825226795d48c2d19d543270c2ce777f91524644dc32433de2f6153f60fcde82e95c01c379056fd61f34ca02cffa4a67381aa0f09d244b816e08168f404fc930f2c1f9783abf0d22a948bdeb9b8376189f24e6331993837c1b232eb140589a9429fa5e4928e627a964c2c97ef85d6d8a3dba18156d06b42cf0345ee8e120062bbe6888c552adce5bd291efc0618875532201727df082ca2faa11dd46262c9a2070ee5de89189b5fa9a6d55bdad20d79369f620b1da05ccb366b3a37aa27dedf55a4b4c6b25305015b29b0323438fa08b07666eff1ce21d398e0e83e00cd09196ccd0ee46de74a5f1cb01059ac03a3c9a4a082b65a3c8988b9eda1108ec9403b9c5de265d8efcf13a387866484fac43407a92385077f9b643d9a386149b17b7a477750ba42c7c2a9a77b3e4b79e61569902ed7d4173cdfe2b54a31dd6b819805667954e5a4751965ac08bf9e713af8aa85f2d6f8ac1b78bb0b1ba07cd5bf08527f1a4d294733337e618f0bb259f96bd7f2ee4a32a4e4295146287d4664c82e39a09588b403a7178b9aa8b91e14ba3511707660c2c703e306c29d199f0d9d953e5ff625c53fc03eda24dc81d0b30d537178b1908e52b171b9009f3652a26dafc8a5cad5ef8a7f1be640c8037b92294ce3a51dee1eba66188313b5ce45d8c357697b607e24e153a4d91788afda8fcbc09c134498c4e589928bec3770c5a6ecdfdb99bc73cdf0a911f4ecb0e75e5338b93557f2f16b1987ac160e414943238aafd7bfd936b1792adb65449ad6ac6bac2bd8fd289a0211dd0b4ad78ce3258a55d24560973b48cf55e5051b32432411b9d0d4b0e67e4ed9f9937aff6861c7d2d0352edd2cdd760ccabce0e70198442eca8df812948d8481bc978264c5aa9f26f5c01f5aac7d9cc563c09d4e1b70bce7c3b1dd66e1c26163b609890aa0f800ee322bc22f85875be551597deb6eb7094ec0a4eeb1945136be237bd1e009d463c8ee43488352c73cdd96773a7dac0f8bea55cba42d0794b64a73d2e980816cfbaed781c6aa06b51f659dae1c81ed85d36e153b57da9885a525ee9120489b23c276bea0e895b697f72c31ed88575475858a9e42afa2afa1d7d053e8d442676106e7c8ce4888eeb66149ed97d5def5d62b09c077714ab6dfacad816f1a1af166b6b2014583d5bd93f5339524035472d1d75871a9e68b6a83a67598284b1a2d176ccd7d9f5456268e12e4a6adc6789792ca3b01f29e3dc857202a600d6d55de5c07273a86788e8ee1edf582cb9fbde07caa546e9005ac7f1c9138edd6cde523194bb7fe258c6fd7f4a6ea0d409ddf62d6a53ff0b4c264b89dd520ebb6dcb16594959b863d38ae118b060b7cf4c1e62949e437cfc78e46c6ef89393f728459fa8ae08e4acbe62651f2c31c513b49db553df2185d9f12e23d6127341507a0a3dfe6e74889c072681955c5cbd2e4b0ea8e0430ba57e159d157ed6a156f3ea0d2edbbb2ad3181b4dcdbaddc3870acb39f0cd9c57024c4870ce01f883ca9b2315b980860476fd4dd4cdc5284bec02e60e95ac48fbd56ba5e71c16b9364bab8ff688f16d61625e28b77db4f7f17612f7b76e3798f95c03e7fdb8a4cad466a36e2f0a3f52cd2251e51432f6a69521846afa678f18f0b8e9b6923b4bb1188d79885401e82fa8090cd7d53d2a6144687172a51e6572f10cd5d1b0a13c8b9e2be11aed72f650471983eba2f1535dc0b3d3841947673bebc42caf1a57723072c3f0a6fb4e8ca8ead49941efa7f452b5128c1d8a4bd58be2174a2349c0b0d4811bc5c51b1898b932ec9d5e91f43e6be20130cdd2a1a675084e75c10e0ab6b8c2c1f6320bed5bc0ca2ae320472303e345f7d0639511a2df7f264cbe7d245bcbc99f16ba304817cb57daeaf0d8508a4e8f431f7b351465902aa68fa5c8c919d158e6a1410bcc3cc8ff50773182a89cf92e5dad4bd7ef09b1b875d11bd1dcff16f2c3b5907dc0918b1a86e341d3cbd05ee0ada485d2cae7e3a288b879e77c1db3cd04210bb2c88f55864b6304f44879f79473446bfcbdb62ecf68020e12d51b7e2398502e8f3f006454534f913778b33cfb280ff6af11da4a0190a74ff92c27e8a93da8379e5c5c9a0b9c617aa27d7590946cef564bffffd3df3daf8689e29810456a6243810e2f2421cf3c708223711eb42710fbdad89b38cf2ebdc78dc3d188c833fe8b4626611c9c373a4b3c08bbc0b51e2bc13a76047a8f26bee0332f72d3889a34d1969809c242d351106c2b97b1b23cc816693fbff5e52b49ddbe2abc5793a96f369318ac083a04b383fa7148e83e71a0fe972a368d910649f01642683b900a7d4abb50013c72ac97b46d3611471f6a87d3ed7b7204c68729557d883469f62a96e911e89752c798a0f2853ea596b982b4ae8dd6d92cf90ad3cfc796d05965b52a91f2e65a1167079043ea34ee9ddd40a19d7e7dfdef008e5a3b0745bbe6439af15b14b07872aed34d68bad17eb2c3f4fe27223222870c5f48a7f0066cbf9136387d53d7f6ce32b8afe50e4fad9a09c812ab3f0770e1e01fd465b212956ad9325abdcf3278a81633b8fdef3841db66daa5d6eb4a26a418697355f3cc448bbe0bb88c6d37b2c31f0aeae963eb307395c065d7321c459dc167b20b9882f47e5c56b8b84f6a8659c993b13b5a5c0bf7cf641ebdfe3a0fafbbc07bc76636ea1c0579078bb17eec81cf79402e009d68279e7506d4d13dc0556c98dfb0ccf3c855cf16ac55626658f47055e77125a488234a347f862ac785f936de0c2b22459cf21cb8b26d8da80ab62aebbaeffa78a291f15562c46bcf813fd75ec18f7a7db6044e4f70b4eb3707de20844f64408e98b385adb9a131800987ecda89bcecaa57ce810c9e6aad1575fc8296bab0e82e36b5ce272f8a36288a8b36603e951befa3c4578390ba6dedc086f83a2fd7181c8c3b3f17384b050b49a0ee78f606c83280616e6a0e7d01e8ed0d48312662f6a6933e2ff3bf89b2d7fb194ee0f1c374ab69334e65ef795d9d483ae21a57115084892259cfaa7a995e7ce72d8869667ca217b9399047bee81d797f5f5b4d657372620d8ea2f08460e0c1f57c4c78c182b517e6ac1fca82f6f5e736feae807622129a7f861df57a09c9b5bcd35a866bf7ba81f2080f8db1456d397d0ece0d0cd29825a5c344ca7ae9436abd65e2f0c290580ffde2ac9a7339faef3a930fa94703e1df9b91f4708eb086f1e44fdef492ea567e4f360596f88547a110caf8be7614b347c9f11a1b503794bb29ba52cf13e4094075ab0609e0922a7a35540baf0fc774517278eb801e7616336123d375bc427dca20a6673344a030dad468aae6260323a6ba75065b192d897c8049da355bae2c6e72a98c6d6a637c26009113c63a9390fd61cec4f583d2141a9654b2f176a6c257885d3a3d0044db333eb8f8a5f60f680fdce7e72e702ac750ce9b221047c73de797910218ca882e3a21c5b78fdbf7254d489a291d089a001e171d6e1fbc2897b33f193e3463ccbfef423bbc8cc2156d37e6fc46577a1132a1bc2147c55d8b320fb3c9093a92af11a55370945e2168345c6173c748868676e4f359c901e68214bb43c883d94cd7e793cabf9469c024c5ce585c0085f6734adc0eac894f07303377bf4a1fb731db83346691a8e100eaae3ff91a7862f5805ec70a94f268f6c4b4b942e933ecbf6406988f7acb525320cbb83f08dd27df781403de7095d192e9833f77f49ef347b4e57d79862a600c790a5ee528edb0dcd28be1b930e9aa98a96e3527adf0afbcaf029dd8d9909650ffff17a6ed426861c53d0901ca0f56fd995fc04008a7eb5a290c5e235a4de9f5cc5a7767743eaebc0b36cec8ccb52c71ef68625c43253dfd6cd2a12170cca252f22b3ea541cb23bddf0db0065a3fbf3527d97e009a4d3f4693b3e5afeca161e0b8dca863691ac7364ff9bdbef4820134278bdb1287da0da96987f3028e000bd93145b071a3582fb980d2d11b57b5a7f063523dc6dc418188916a990f023b01ec20d1ab4d1a31d2be8c791276c027eebaebfc6e8c83154e4e34618e63e71bf851bd8091b8e20f6aacbf9031069ea5ba6e9fb3574ab74a3c760d72340d2004040516f60e1c4fe96b7b61dc0a665013c2259e69954130e24a600814d502fccf0656d7d697d818afd17ab08558deeb858a242f58851649c3644b12144d55cd3cbec6a5cf4a6098aefcdbcd0bb6df5252a9a5e5faf3e9695d7cebf410327438924e63f3670631bdd0e432434e0959037c2f8a085447e563b3bc141bfc14b3d95f66cc0cf1ac0f1d7658426e743742504d482462496d63d8c567a5ca3473b1ae488ac243d002540ca44d1c8817fd53b41231731fbd8f1c36eca96727aa78b6c256dc5182638b420e7787dbc626d2b03493a9d27ff280718d0f376bbf4ac6e576101885938487dd9a3be01d7b92e1559aa9e2c2d53f02f91cbfa691a0009618a2297326c819b2c350636d6938ef01404a53c90b4a13d05a548a402c87d8b4d4aeed19f0405989cadf35f52538fb08f31e29f30a1f2e17a226628b4c57a0fcece4bb0caca823db2ea24306f25a54cb41831502f5d94c510dfb8c1313ba0063b85282bc10edbb50401ddc3b3a3b78799412f20d3af55ff7e300c06d37a8469944c8c79f4187411f24dae4ea3736ce0ce41049476a877264d28e853add467063c518011a45e89baa3d09ebb847059b9a0c68148071253fd7a0844b3bc830f27b3a937e3ac187cd8e8f3d9b3fc559d6105d540dacfd4c69bdce8180a8ef7211e3c2731cf0a6ea0d29f3df8ce2089551465a32a1fec8e9724caf9f6b5960f9729b56d51c26c0e3e64321589bd428e8c7338b346e4ac22bb360fcb9b89cca0be226ba870e6c32146304991433a06a222bba8e49882772dfe9541997fcfaff30468faadef75533e3845f7cdc7970e458855567e68bbd289b5349ab79cd19567fc0baa93894b2d18c4188e445c66d1d323de802cb1a11a251acd0d9498e2dd446fc4d0c3032b15ff8c1301fa474c37f7a646c9fb12883b3450af86a2132bc6de7f53fb7c01f90797ee618ec40642e1386c2a33ae710d2ed616c613b77393be485e45a6bd065a55ba4b0bd7febacafa65477fafc0855ce0cb18124666cae0b747b40243f07f6c4b6b26e82563d7528a60fb4158d90d82cb7f13fa94e5c4c7a7f3fbf7ee5ca1bb43f4aefae641446f00a160fe3a54671802e122e632010f52208b5529271b60780c328eaa1e11dcba426f9abef358139aadad7193bcbaf2216e9a8679489d75c6ccb5766e7355a5144c388eeb661d3a1b138623075a7ee66800709e73bbcb430d2ba48830cd610c225f002e4bba40621c78b67dbce47bb925eb7d9258d548bb593c4a2cb1d0d1034e53d4e79f0979b90b795defe507eaf8a6c6f9347a97a86c1fa5035042ac1abacba4341db92d34b8b80cd509ad49feb9c9bd6e4bab7db0086288d704be611d42ec8b1db752cb0b84fcb012f0080f68cbd8b8fd6c008c7aaab58195b592725b2bdf0faa4fec9a63ba0bd26d171deac847696d21ebfab550aca17c8c3b5f64982c980c5cb230a0ffa18bd6e545e63dbe763b1f8a748a9676e61565b1e0870538a29d9a8bfa545879e246441969c4b8935e6614b1bdbf9b2d45e388413cfaea829489c4b3c8b83462c5c2e2b6d4d35aa9e8bc5baa7436fa5e8a9e88637e4022c06f06bc5106e48834a5f8b07400c5ebbec14d4817d86add15679174dc482a01463ad6387765bc3b23d5dac8b73578e6f88714638316d26c6c540c8eee972d4a22acd04483ed7d9ece3c62fb5b26596004fda624c29ea860bb85ebc04677dd4b37b3e9befb1c8bad691081b349b2695c2d6ccf483824651b259604bebfebaef1b5710226c97dcf9e0803fcec037709fd757545eef2ebc770698043b3bb7fb08b120876bb88e4cd6d678dafb77e31f606a0dec0b4d71635500deb1650f55eca1baac3961b0a85cb556d80466369c2952ab1d9af45676986df38c404ab0304d4950f0e7469758ea3f4ecae494704e07f55ee1c0e5f74ed884d1a6020efddf5ada633448315490f69801958faffd25bc17e1d8e00702d1909e01b6e929a79626b5e0f25d62b3b4a41ecaace592cd78b07d17c818d903e64d50ff820dcf75528e8773b7f77c27c2feb6fd4937be255664ed8e79cdc1e4d50de3ad3a456356b6b550d66c17bf9a01a5cc836b08dd355efd7914c136a56b960754f56021268a710b282c072a260f5cc2c506b3d223465acaf051036f1962ca6ecd3b769c40233ea1388c8e27e6fbff44390389a7af60c7c71f252f993f11aab8b1e74818753eff1c0966c55a6fcf94ac237d3469258e753b08f6de14361d6ce50e58d27634ed840f71496cea0793b80a128414c4fbbfdde27ffa2897c94d63edf2fff9d097c4cb3bddeb5ef7bccff66824fd3b6d7fbe6ff34d9f83038ed2b5c8e81d8156c1687d78e18db2f055087f08043287082b4b061b5bd211d224a1d4eedd93f867b88c25fff88dccf87bdc74cc690d08fa162d9b86ab650cd8a6ae295561a750aa2a13070de131b7a55913ec3dff0f4188bf5748954bea5c7e4eacf81bc17a6a184ac632bea9420901a2d108d1a380d6cbfae33d0342e108d1928512e9f7469a0695c20180df06dcf58d73d1080327a3e3291d77ed418a963466fabe6b6d758370e63f6fe3dec28ad829ebac279bd8c6d838de95e91f98e020ecee5719a5246ba97b8f7572dae58fd0b6993affcdd6a43e983f20c54533237d132dc12ee4f8c8838502e70080346b24d697d433ef0b55d11624f2e4cb651eab2c1fc2896ef6ec0010ba55a8e820383439e3c5f42c6a43dd4362c87a3cf1e50bd6bcb3e9194e6f164c1642129d684a497584ad71ba09d1dd0635a7260ee724078c2b558bc5be82d2b4fbd86175717a993dd06fd12db08085ed7467c0bcd872cdadab301ff6e27b9101f2a6ff4d8ba8704312e2095f504569e87822476261e61e809778629cdb5dc55219a2946ea6374db0376b3f2904cc5dd2b501d07ab80087841334518dfc75f985c4a834ba4dd6e863e4345e3ed98341e5fa42f858b3294dcb80bad954b8aa8c0fe13c8c8f704239f5d4df293ea6c28240240bc637b9070d9bc2d7d02e32cd697d1d8521d3d2e34ce46e7003c6ab88e7a6af2a4021ee0c984c62ed3b927ffbfb26e8c8d6971193d2f1c5c01adc17e631cb359c73015735a3eae6f2c519198ad9f42f9a1217a964898cf6f7ce9c45534497db944d5e99358580c2d5b566e2cec050a3c48bbb12c1a8ca3e9235e13d1444a80031b9d362b287a3143e6c86eeff4eebf207160a949dfbf273bb6a794cc701077326da9c3e2fbc208d63348daea79e08a8a040b10d38cc7da4ac492b6e893325bc6b0d534dcc3a4a6188c6eb580b53f505fdd798893acffd0b1deab8767c53cda342641ebf82986382ab531eeb875fac03c1e8f4418d170eeaa7df3247727f04c7c3dfb3598a3c33e50ded0bfeeda9c0b0f87a0bf7d06ef03c474ba2048c0a2353b7c85b00ea7937bfc48d442c7b85f3e65037fcf2415c7d38d447983fde0323c115b45020b2800c2f1b14df26f9f93c250f0fee7c367c9fa13984509933d653f685a040d3ebbd8f0f91e6ab89c003b579254c758270fb2e7984c0d1c0239059a8e74dfadef16ef2864d250b6d7e33c902b11491d77ca0ae35b33e0a07762ad06037a1244103429a3c19c023c42c5c0412101076d6988d04ae24d6129fc7301cb7021fda98d6733f9d6d968d4ef1c53554ed63959b5c0dbbc4f4430a9a1a288bfd6a98319824ea338414b42bebb84a2a42dedba3793234f828336d83c8c991d386084a60598a4ef671d412e5343367a5daadbf539272c0c67f323e6aab78d833b378144ccce6264d4ba0f5f65343588ad626f51f6360087e93cc8e82986456f20173ab4fe75cfe6ee70c9178ed24c21561a7415c89cecbf6e3d16a51063a45aef0cf534dc7ce32ac8de356cc528a5aa0fb6ee16542842adb549669c141a50ab2d4008777827f1cb8a4c106e3e4a37eac07689cc40192e910d71d5d96b911abb75033b6b91a8ad824ce7c6331af35805076d3fb0aeb20da0432deaa61779b3fa0d5eada633e1ab410bc4e8ac05a809e4e4b90f3005ac1fc6e0d34e9154ebb6bfa300022b28ed9be72e3105bc3640b19595810076f3aab0314817979526feb6eebf7868aca4ad49b1649a85f228a1a7804f22551fe9da5510095888600f7d6abc7303420398f1682242ce20f2b7dfbffd5eb6c215bafa7ba553f71866b0eb4e47d257a1810166960450917f769c6582443126c0241185f7372e1cdb77ee061c6c2ac43a0344b65f6387e83084712137e20fc2a233360ddb18b0cfd10acbcd5ad41080985d9b5cab7965c2ab27a3f0529d9dd6d07c1d6bf0d0be707b5d69c683b6eeb6e6a9e8515e42a6efb75a62fab2361f235fde9080fc7d07895a7d65edf77d9f1fe52ce12c9de1a0570ad7716088da90b9fe7a8527999325dc9bfaa9078197efa95af252bd39ef924e96d1ca8e0fe7cff1d12270d396d09d1b160e9560181973cc70561c65eb6cf8ffe3ad236ee7061c4f02cc90089e10906300fe4c173cca890436a8ae6899cb0eeaa6c99a0fab25716e367ea814df9f2ab2ca5abadf652d70cc591416564165e9860da457d370fbe2f69d219ba14f6d74e2c4cb42892c0853e1dbb028cb49694f4f633eebfc65a14fd4374d5a34733e288e3ab314e922e33104f9bce6d137edf119de8ad5fe4bfcc22b3dbe2d78f515b9f098a768d2c032bb5041026579e46e422dd6640d27d802f2b92bc125b9df5b2bb9fa36a640290f1d4caf2fac484cc06236dd14edc48ae1c028f7a0513cb8c233a0b153021607eaca4c94290d816884008f728fa85c6a321b595b8b4fee7701465e0a887b34ce401ba93d93866cd9ba41c12c99b1ad32d7919df403ee2cd9e4d40491a4c55eefaf5108299b0ccba5c24e16eff6cd88c545eb8166c8374c73588d8104aef40806354900dbbf26c25ab866665f424995a0fd5cc1b83ea7ed4b9c24426875f90e66e8763a71a01e15c60261a70648579dc3bc55228bc8c31903350a34c1fb31e389824bdde9e6fd45e876931f54e988aadb434e8553a6fec51d1d14df848f5b39525bbffeb45ff408293a6fe090607702248804fb3b8400f2ea2ca89af7211fcae41bf2676c57a254770fc9db0cfdb4551fd382cc2628738f898aca16996fbcc76380bfa17438fc9bbe21123c81d5e0fef80c11398cf91c94905d6a8a2a1ded801294f22383fff60e2556c549845dd1848b96539a212f87941a02d6e15c16f6ebf063f91ba840c4372152220b171057ca18ac6e01805915087965ec6c621196193a9a1e285012e516de7da1591166c516f4d79e720d1fb2578fcf25941304843cec79647009dfafdcd3be4d29b1b2ea47c4b8181d8a641f01297f052c66c56f9dc7a0ae8a8547dd9efbf6ad1c9f356986143b7ed6504582b7010423805e114f7142bf1351a616184518425104a67591dc9a5b04de73d42d29021b34230f542f38442d10a1fd4b8b9c080290bb801f7a4689d90cd6d851aebd95457c39e807a13caf6ded826a85a815ddbec8100fc9228e3ee8588126510eaf84bad0015083c719c3956636e2d921b4f2df56f48b029de0b48ac5b236228fe083e031fe0f925b77bd26c6a6053d0077be8c6658728823a3c2b925ad82a628f658fa5f44b3ef8b478b8d51c85152c9e2587ac7f9a7be4b101a953900cbe45b246b0e34cb082187843a752793cb002710253fb2f7aa45e8da27edcb3ddb313f7b094c9f54055d2b61f501351a5ab2aadb0b61cc80ef314ff4ab783a0ae16ec626265eadf8c5d71f89a7b2a936209dd8a3e5779903480b159f6ab0451580983fd2e747f12d1c00943483d09296da2690cd5bc545f152fbcc022861901a4da55b5c74bf25bb09feea946bb3bf94c7b7a96c5e554963638e40e088649163bd41b05988490131af951b80ac287f9aec86ca7a8faaaf0e88ee6ae090972ab5d8b3134aed521416ff4fd5d64871494439f1009b53c67e7f9181dbd68d047b05e64c0b5a89446eb7471a00abc6c6db9c0cd4f8c10b8d1fe93c5da0d2f0c9a59ce4f5bacb0a6840fa2a669ce1425d6b7f3577b6fdbdf667a98581e89de94c9cac8139eda2ef1d769421fc202d869e6edfbe74a0629158034f404437731a2144f4e03293122851c5941a96f9da3a57e59aaada82cf5ae4e13266b0db0b2704ce674a03cc99b0829bd35d88577db9ffa83b2d461955140624d8cee3723c44ec857eafce821e9d5585f6c414d0f90103cd1aac26e5b0b08c69d0068e5b59df96efb50fe27f9ea3aaf8ef4366f8c884d573b9e7dc819239dafc88a3911cd3294966490a404cfea21b803953fa5398b60796bbd86a070dbf95b2b32b75d43485c8c0b8e7cc67cc4d6ba6cc4ff5989701e00d5968a4856bcac825b8b51e9533d53f583990c3ebdbc70f0404a5c05fc604ab73b245eefc092023d94a49998255c4c165313985e2651f1d8cd9789b77cd9ec772ff2e047c46aa9819c5ab461cb052f1e04f5e8764b08dc882b70d2c90241527f53de8520f1b451df0a5de2b1ac3fc70d58d9de121a887ed0be9f4181363c6ed3d346a863cc3faec6b19e476d29e134b7485430c7bbec1949173d0dc1500c2c8df96c029a073ffbc2e9654b9785beb439d203f0b96abd44a63c551a581acc088cf3d18d8e010e72c18a0a8a238a865b74fd47940afeb524338775c8ad9f0bc41c15d5fc473d6331dc94462db9443c20eada804fa2dc046ab03c1d8a7472e72574f924b621099fcd72407464f46100c1aaf40ef39ae23eb92ce237d2ec071ae441c2c7f43e8cf9dc4cdfa1b16336c57a53d474cffb718e75941df08670953fbeb05dddf38fcc6432f2dff5ac80f736077746014b75f77f1188a117bc5c42a69d59b33f76761e6bd636ac5467b723eaddae97917767a9b4764afd5ae239f9d65e9a5ce982e6357564dc40fae15b92764b568aca946ec6685957f99140b098d067c8149877d329fed61511c444c684c67664ff362fa85b76b99fa97cd155e3e94e0ebcf8b22805e28cbdd8b3c8da2d47106e2cda75a6fae759f71e6b6c33dbae16a6f3c947123311dfa6717883eb2e38f24867825917cc6aab32c8a65881e22c628a0d51d2f6aa66897b70e4afa400389ad59c31c530f7153e5a69a6fe296117d8d9d60775020f4234d1be5f5fb11ad42fd944ada0be201e1c1f9d32d42c4c9c496f5d3e10afc540f02913590433038fff6f909f74ef61af49719e4e273a0cfed51127490d9c8831cf2e251ae621c9b3cc53e8192f969047d187e9225b439058ef584b3ec937d948644022d099179b862fcacdbf88cb4c385baec8d5470ae8a43e8b2d04f843cb829c759f153ff2f2962ce4b48d0ca6f8c3187436daaac1f8e763b3acb93c5d36b28ed82571a1dc23e18d28a75fbbd163c5ae51e9ab74736fede2b11c86ea792d4b8efef8d806ed0723ba9b9471a527a34ab574044a22ae4e5e23d36a213f221ae8f0f7c3993b5c3999c2b7b859e0386e4065195d9a1816ad0905950254be89e7b597604c60b385e1d40832e8b90d5782d6bd600de17050476f1492aaad58c33d5c34686b9fc297aef57d8d7a29affcd6801e8539980ab61dfc8cd6474e18ac0123c6794459210dc763d8d939564974fdc4e1e48de9e6a72059717bfdf76939bc52c2ce104d519e4be91691e8377f29ef45c7d4ffe8edd2d9673cb86eb25619bbc9e40df2d2dd678427283b4e69b4c43ac9c8905b8ba26113031ff381999cd07eef14b27e39cdb7c614bb39a7e1e2a7e831f4af16da5dd432424bf35aad0a28da8de7039738c735a39c71f0d3a02a754b3501563311d701d5224692fb40fd43466b81d53748a6f55ae58cd1165ae2016523d31a5a7242e8ed5a11f162ae0ebd0f94940eee4a909620d4154121c507b01d38a9093cbfc3d1324407c5eb5927f4b349ace513f906fa2af708d88f9dff4ce2e78e4fbd4a9d7ec3e2510e63eaa1227e220f6b9ecf112188bd95d213b5e19e6d9fdf6be208c62a989a31dce9c06cfe66e2db644597fca6763d424ee4896f7d69772295ce5413d14525c0b9929e7f8c39b3c742f8d2bc5796080dce4187661fbe67215c35a0a610fb03da8374b0ee051102b9a3b72ec66798750f3c7bd6c874da0eeb750085235baf4a645800c479a2347ac2db015a27e236485496027de882f1a8b8a32150bcc60280231d6522e1d20651d591f0d4cc1c0ce23bb0194a414f4186120d5bc6de6d8140c1e2a8b297624fe124c18056b02c346988291be19e089615a37e2a1b21b1ca01e6c1694345f49196f3dde3785cfd9876f8bd8b5566bf0dc185cd26a5b1faaa41d8e248cf76aa3ab737bd5d1d83182d7456b747cbead6c4494aaaf93848c4d7a80695d5ef46ce766a58fc5ab40b44f5ed5d46d83a280a243f1a93bd0938acb7c0a5b2d1e26770c65bfd2e2a149c30e4fed0ae649fc5fbe306a77fb243e2b01a399639eaa23922e23ea13990daa8922522c6c3e94ac880d8707606971fccdd7aff6bc30fec947b141df86c838c9c2392b68d30ac6d8d3bb8e0a86b319d0d2502ba6832efcc8a5600cd1586d31bcba10ee66080482abd4fb46ea4d3678279c018a7399db99f81233c39e5f0723cd8897ccf467c89260c326b9c41e77136af26a3b972cc61874bf8c4fa6cf02c11df424022a0f00776400c49c1b0ff8c630361b9e3067239549e250dad5be14361bc20242bc0c7886190eddaf7e3e3a9d63ce6734457035d02ca859beb04cd0841361c4b39a754a9d33376287d90647e625951b1eac1c5e64b520509b9dac34e5fd657322142fa119fe1e7eaa9dac1a44e9daf93cfee98e6398c1f4366f82c4899f97c3d4f1b8b627244bcf13c62399a1387a8bdb82c350258edcb5509b7db00ef35082bc9c1cebfcba4d5aa3ef6c79442b4ba809915b16bd346708c16a02a95b220dea6abd819ef265d0e83cef9ecfca8e5b6a6467c9b167d18524e2bbd37ae05fb700aeb5a7f74f5d9bda2f1d686b3a54a254926db7b17427f85de99e442425fd723a0d5f8359d604533f0bbf99a0762767fd89941ddd922539469150aa4753110acd9bd52c35672de04d291c1dd17a0c4d01086ac0afd947be46c289eb8480c0f9ce12f16b9ee07b50d135fb404270aab9db50cb3a73bbbc1df7f4b29db6b432dcb6e3c1b1d261f091e5eb83ad5fa6703565567b9143a2d0fcb79a1835801ca29764144a769da946381d69d642d555e26c064ece57ecd1d6c11f049a8215fe35545e453868bfb9158f693c03f737840bda1e72d66fbbababfc4a3b0280634c47279a531fed08030e50186f01b0d37590781ac41eedf8c50ed160954606e26334f8bb34f8afb3e953390b213b55e6c436f557667f7e87d0e53f09d0a51a2efe167df0b362f9ecf0b4587864b594739c805c61428054fc40127b4a89661979808036c7a8794548fcf7ba76c9d6dea1990fcc159177ce34ce0fa9ec2b6598cfbb2c8325a2c49321714be67ace643539e70efa3998992c2f235c4de5c6e6be17dc873345f5ab9f75d6891de7dea3e3c7d89dd2d13d6d03ddf6ee1fd6763cb8a4014623ca256af7e7f9dcaecaa487e82cf163191d5d54322ac0673119351ca4380ea6d909dcdc5e6953246b0bcdae7a38a2ccb5ecba7521fc310656e6359d155cd6d976689036dad860a40a57f2be2c6e1928871182acb859667c3edc337d3a1a25ffb5f5f0a732c4a2f0adeded62abe871816273726c26d89226a3fe4c612ba633c329a9f491ca92faacfbe3d5a5d399a7e0dbc03af97d0408725fe74458e2a2db05d05c96b03e33d064ee358ba03e3df399962640acb927cfdeffe993716d7e125269755ab1bf500ae9624dd1c881bd8b74bf0b0c49959460af1adc7da2206d70dd8dfdac74415e3fd1011d9dae8b89ff0e04ff1344b303aad860aa2d248b78749548d467b78509e4f92c3e61a2480a36486c41fc9c3491cf10418d6fc9e82c97ea2e9819bd63c79b0614c82a4c1ff79746ceb4b65fea2baa6f7686a7b9389dd0ec2647332a50de73ac11c459aea451b949579cef6fe9af50ff37c36fc8f61ab484d6907c34fb994e3f918e54264e03064761fa1e317abd33d2b74684a310fa876e97630a6667f9e23ea211e4b1c6a494d5a74aee9a7bba607d657d74f96b735e3b0f5e21f920bbffe78866833c079e538ed7f96624d4bf98b94a9c9b549321ebf313e745555bb4d53b4e7cb48fee313b72e64b6acd240d2277fa2e695b538e76ce6cfbcddd61ffec232977ccbabe80df1c229ce2189856afee5741cd37dc6b6df01599f1ae6fb789f14e9ea2b3871271f96141b69cf7aa061dcc0d2b46eb76eb46e71980f9f0d53484a1fe71f77dbc9a09476abec4e3ca089196f10850298cb63502ae7878718503bb50bac7ce8586a7ce6e712e63f225a52cb6e6012da9a3aa43b4c30ad819c30d789abcb9177a902bc9a40c9c079ce4db71c2dc429f3196468a3a7f13570ac5b7e7d63cb1992d602c7822ce274d23c17de3656245b06c6e4733f265ed5f361a9431d276f45f8dfa79953be544377398733a8b3e8f2904452a6b159cc54082a3907786326d1eb864b0bc618e5ba79ea8df84e37a42ca9aaad10ee11249b1a759817247da7351d3c9c7b640560c619f23b30ff08e68b9b18ae2d94053f5e9309478f00a222c973ec9279ab53c7bdc16eecb89f53e3aa9cb38444403ef52d35c69be5a828417190d2fc315e30299b6991193736754659db25f16763ed5e8c37b0c0ceff456c5570a2a578741565b825d17486694306f2b6848069c7f3c19ffae0ae87664bb0bc9c22c23257741b33c5b8186fa8a03125861777e034ddb2e8b2424f73cc9725b118dcfb0065ab64f47061b7d7f45323b8d55aaf60e193cce5c104249b33513480b0f65bb87803a4131f839917c738295de37cfa3b1548e584f4198c055ecf23b2c837321d9c861d1ec439d0a0b9b85d8a268251248f1c329dccbc1c2f0272abc01f145e784d1ac05c1461688a5e2ca8a14ae494fbd187f847c62446fa52a20c97be9415c4beaa69a7b8d17f145fceb25db941fa401173bf618cfa47366b00249b193202eab9f1e80339d2159859333634f1c1a667265a6653b75356c8cb6f4400a6b884e60ec1dafb7aaa79f09320b3053e8d3559b0d68e7cc4d785febb8c0cf02eb979aac9e07f12566fcc2a9cc8423c1bb77f95c4af60bb1fe945222d1207a64a44c07bf8561c7122faf80dc0c12d1dcd9d99beb7b2cf80dce237424df2f13e63f1671430d224003877c21cc7b4e25e5501c36fa1d11056b5b866a958f3b9e20b43f03d5047c11547958962f6e96447e5d1308d720fa774ff67314f8cf7c973667e5c673fa08f6960d3f243d74f777ad2036253e2d492ade5322851df4407180a00189368b1b858417c7207a728befefdae6bed13226e41506232d28e1bfceeefa4949b4dadb720cc85424259ad5e858d8f267c43c23c660fdfe4566745b6addb15be8c03240ccf353dcf0fabf7b7947200e9a52dd02fec5a454a152f00b10e24aa89810ea6707ee057d41c883a5893dc933a704e1134cfdb2768f3131234582a2a0975357c0e1a5c058a6114e0ae5b06ae7c1267ec5da3da86ff8ac861ccc7791ae7cd5fba1fea287c2137456a51030fd60f62db750358c84bb708195afb69116ed09ae13f963bccda18a2dbc52403ee6fd48f4f3403619a30305de273ee783dcf033d69b5d2daa58fb56832d3f28ac9d869357489602e0e52dc5804bc5dc684abd43efd682e71759858a0020c05c8b5188538817cba910d721867af7dde666e9a1b6209a54fb5d8c3f4656a96c96ae3830ef2a2c41b8da17e75e60a8745f52bc5934eeff30685508be9ac185c7d9c4979de4b33cfb266993eb579f21eaa0018725473368a422f0272d8f090c677c8099493c16915ab8f45e7f88370afa20bc714c21b6d028e51b34b3607ebbb00005c5d4157a7b1f4908a9487937e2de75b33805eeb6817b144d4c3b9626f313cacff3f7971a439a41df490b29e3ecb84461b3b8fed352aef0880cb1b16cafd3d618d5e63fa4f09e7cab2f5399cdabadaee92e29e144bb0b62416b958cc0ac4dfa72dfcac18c97cbb75069d0f38149f81a7be1a98a713ab792b4eaacdbc7d6ecfd19d6f722386246f1e08d84b97fe53724865afd5408f2248b721480a9663fa88a55418b77df3e1cf26d4f9cc126fbc937992084745797e5a44d326d393dae0e221bb09a19c4aa23cf8a30a8d30b54e4fdf678cb82df834773af527fdeaa13df84fa2308fd1196985623f755911e1e0853ea2d9abd7d469d82a8cb63836592715f5d8520755b9832430a508fb476aca2d9da8c1bb0c30fa3981b39e7834a456e13de0f19d37c91a241125f35a34077f4b328638098294d7303092435529df441595886c160ff2f558b5e0c342b514f32736a6079c11eaec23e986f50ec9ac4d70f91a71c5587758089e17ca857412b5832f899641d6aedf3209cba44b3082a7ed574b3a5dfbf5369ba002554fdcdffc5b1f1507e686fe3d06d6271f780781a386f472400fdd5abbe9ba9747e9322c562b18333406326806f2dd31636407a19246d2f219e56435c7a72bd52d4ce3aef8002f82e000e5ba7694a17744ace5c107913036adfe8237d697354714d5519b848ef6e39b13a9886f333fc674d44ca5f130d57c2798dd83a30c5f3336d995cfeb58a4af7570a735fea9d66e846693ea2c75a102b63a0ae7b953c51cadb05fcabdf84d4520b692a74b840d10b099f327fd283559775c30cdb82fa93f2762f95ab04584ed72f7d829971a3cb955de0a06046020828b9d32d6de7130ba5068031eb9f055ecb91fee8c58eed8e9f9cb88356450c620ae8eda543c53666c267d839389c32b7caf1e9dff661cd3b842b6e8b64af819de752954e643369b669861a0d29bc5d8242a9caa64d9ffe1bea94687fb2169b6d3a82219d95652f8e78f95ac2b41a57b2c9a5512e7322b526640345b9200ee8f54ea0a91278ab849e943ebb390e30b95f1c6ab3f0c3a9eb7df16b3d6d570e1f99c7e10ec65fb2edf08a97cfe6acfdf911ce92127d0eb97d7050100cbc2a853b91281f56da2a38b8ea14f4f9fc3439ede7d2c1dfec4cc9e5320c4ef15aa351b02bdca1831248d2042bff9656cd2dcd4e619fd11a66586a88d9fc3447f437094efaba76577e1eab4e17c4a56860c9f989925b8d6e052f294a2c8e4a0d4bb535b82aaebd60e5f55279a1d314ad4b18d9b10387bc3694eba406d60f2085258c387575991f709dfba278ec8db674d2e9ea12fc2bd0c30d742977bfbee9920c24e452f4d45169ad7996779d2b2a956a3caa932b94452f3894a59da1c15c3bcf77ec6353ca6e3381eb121492cabe9156b8586d8b5582b33bc53b03c8c6821626f0185873fbc58bbdfb31072a9e4f410fe1dd8d542f735eadd3981eaf29d6ac3dd1323010c1b94e258d27c215c088933f81352bac5decdbafeb3abb243100f18c609e140d776682e93b57efc5db13c1df64417fe89c6b7704b08fb51eeaaf66830c823e02938845456ec538a1c0ebf8f91e6fb31596c57c49140c6370d7c153563b69f935d05ac705b60ffac7108d45f33ba436f4beb6f436563d80341fcfc3cac5e266cf8d564b9a1d1c897947115b09968a23d4e30b707d0f195c3060d6b71d4b367bfb7656258a767185f9f7f69d5bd825384d7132cb5b87fbfe28bffae397fe5c4ef46051e00dd07a9b72cd06f573dacc0b426b42981049a287dd7bd0b1d41988cc027699e6ab550e8fcb445759a09890773e4fc8541236f1bdeedcc7932b8944e19235aa3c65956fbcc78ec85c6b0fafc92d61fab74eb0b2d2ddce333f4289e91791a55070cb537a64b55f53cd558af5dcce24ae30b68b73e6429842836f4b35f9eec1f0df48beeb6fdf5ff79508e830683c8007c230a51dcbce0ce2c22291080850ae5a9a1349436b0ee3ecfea689bd1b3c16993fafe59a850a2c034f02a9a71f66873529b74ca318c8834986125f6795bff498e8a91e726fff3ef0aa3ef3b5fd028d23ccbf3e07a125f8537812b694e828edfd95878b0606db73339a4979422b5d64f42a01a68e1b8a16641203f01443b33fbac2a062a9f0b65270cb3e106c84f502b823cbaec2dc26ed0a8b698afb39c685ecea60e51c35794af2092ef085c1df74ccc3cfe188c4466b7a37e0f1d310e1b438e10b2c7eac248594d3a8adf9c1a68796375ce682d452d32f7091dbdad105a5581c8364013d542e0b3a0e528987c3c8d1d8fdb4693f216eb4ffd5db84e4be09f79b95f1d6bf87b529fbeb3d1327ee4560823ff7fa6c4b1202ba1e4e4794ec472e7bac40a610c451814a77b2d950146816617410804133272a09902bdc75aca7378202e100fe28eedcf19d1d13477206a85dec5cb72b95bea22189ce04608a39f02a6b45dfd3e68b8532076d7e7f6a90de6fbb242999b890612b057398dc0271a9d0be9c0ef2c88129d0fdfaecaa383f197bd731ba37ea5738ced3abe820f9e804b1b588368dbed309e1a62c0a5a3c71af07cc2039b7f183fe40351d6d82a0785e085ad31593ef885a53358b990c4cc2c650462a05a3e963a63ff2b5decd164022e07046205611a485f3704ad821078410afa13af1205963767094a23233c585473f1002b79e8c1f22d12f241c48078826c36a492b0438a81c414541668aabc1c3066eb36da4a77bf7234336dddc5402a03bf0fe3503de1faacbbedc358ce719212712d5dae67c294508a4cf7092648e738be41a9c4a48eda2e4f386c187c851255390630029e21a92caad7d54c9cf048138355a38408d1397e6eb4e35e259c95e271b815d682e7f2afe741052d1b70c0876809277e067c62211a0f3a2921b2312e4bcf4f7aa44d0b57ec3e1c8fdad372e13a94aa9366747c52554bf7427f72c586b8191145480a7cbafa2c47862783a41a0fb2ccb1f0036d0e9faf5e5631a35668beab75c36fd5d6e827f05a6a1e5c714f3c493870194d35993218a55790d5af757520d484c97bd9640bd7422bb1d42cb2bbace5c6f5fda69840d95b4d4e097e657fcded22a9d297fd92507d8c0ec7cd9371b704133acd1db8bc6c3e1ab79f15e985a12349ed035bd02ea14fb539fe51233565156a9bd6df98626cedfbc934b9257554472cb75e6b35c986a20b89d41e4039d0f2615345015fd335187337d48d43a2b32094087f2466356aa54b0099104f0a95f92591cae96be5761dacc8c28909ea7b0296ee4dd651d9557177c6fbac2b549f9904303f652d8e808b85051b3078e68c524dcb64f4b61805f697948f24076667af59af26696e0e4cb59581497f9828fe61a48e547c60f782a4fc67e84fc9ac9784d42beb0881345060d544fe5fc3cb0d2846e732b3240caffcc60ea17429cfc9277e6515170afc4b9ac4b4f9c93f81b8e556af197bd5bb16186b690bc9f1cba7d2a683ea5deab15d10ba4d09c66fbea91a03c2242a4e1a0733c2e029695a8fd10b18ae74ef255c72a1a9ee9fd161d3677f3bc4a2ce9afb6233e0b9f9aa8b98325776be92498eebf9b2e4e443bca3af48c4c57ec280a68872e52edc9e76caffbc2d55b7033161617880cebb31241eeac80211643066b0cc04e1724e48dbb60d545db168a55e778f902971ec6de93db1eb56600532ac31f28fe4133ee902d2a88c2947907d036080d4da0d35ec811ea84148a98645f7ff9716672dae14c0c0561d895b5c17f13ca97eb0120987c4c9dc08d0e10bc86194f71e7ff8b5df8b82253d08de99f58bde72b765908d4ba5cf493dc15783cc46e435e4bd14a02de3bb1425861674c903a3e884e8d703294f68ed54e7e3ea95038b2cde679e91d7b25c33007a41acaf75ea2f0dd5f75ff8393fdfde6bbe300d66a95618ee036f975ac47e8c9f4313544e0f3d2a4287b82b1473368304d21d1914f5a92e2a94a3382fb9d29008950c7f70a5106f36c910d7d2acda31404a656d92f8186f27fea35910a43e22fb2377254bef888ad473091d7b51150ac99efba4b1b6c0c7889c86c27c9cead524481344710a20e5beb252cc380479677690bd9d633141a8dcb3b9285304f43d390b62e37018900e3b512871303f60e8d9cd1fb6c054b902417934e01f3660b0cc8886ceeba4d13cd7b6bc7951553ae520068b110bc1cce3711aeb01750c8c1f31de6ac7280320135f0fb5197c40f603a46648e7325d45e3df7e21cfda9d63226d3d5cc2d810556150fcd89d32747e1913d0237eafa57ab7646a15e1d0f7e928f19cdabaf10c089226bf661f41da524553c29379b763949f8585561392d604de93a0194701a7ad4824779aab316b452c3d61245832b28f67b7116d684d5ee495a49b4676028abb3cd18a0d1a44946aa0764ab0d592f2967c8090f4c7b3ea737598e7771a59d6fb5527eede2bbccab8231304b8eece0870966648aae87450c12a457ca021e76a7c0419551e9a6da4b6cb07fa53cebfab1d381b577207849e829fc85b26aef139d3f02ff6a55276b53ff6826125340c15fddf869757b7585e72b217e43af610c81729876da5020a81615e65d4b392e02228d2d498c301d7be3350919efc6283a51ec580136abd7373844676835c2acb575b4de25ea6cbc245bb31f394a93e39cb53c4cb9312431f58c4b12cf24b3e399844f02935c1b5cf0747ef74256084628427e5dacd6a29898b1d746b20bdb7e6cbd32a00d5f02139d3b679195c22f74a63dae4774b4def34752af12978848b33aa92f47632375db472f05732adfbb2ae4520e6246081dc2c79275daee00d32d8bb1176d7ddff20700b2ce913e8df552e4c8610ccaf03bcc734c04bc3888264fddad1985a9cf389d56df72e3335e08b265773dbb273742697d6ca04f3c1e7411aebe8d56b428ef06e3324026ef17147e11dc17f68635cde19596806996c501f4b60b3f6fcdf898db9086ccbcc8e37d87040534d3ce3276956c96184a04f2026327c26ac9b083ad4fa37749affce8c434f65de20c83ef62926750628674c9c2d8d32fc49e3bb0461f0486580582636ca890fa84f202e097926584d4e496a7f3da8f5a34dfa565df0236b5a8aaa117f773d2dc3899d9382e9bf5cf07c429431fc1392ff3460ea28353cf8b53ae210911b72b0f70ae623fcfda213b4d5ec7de833dd1b9c8af60948a422b7a9c1ced2a3151c3a4ecf657f91ecf41bf5a825611c50f691d1ad49444dc6a8496de21b2568062f6dc6f7dc9662eb9d3302d316282b22483012d595be867f40f69feb582b32339b2b97590a509722cee918e409e8daa367d5851d12e23264d3f6d073d81c25bca2c2f94993046b571b3a636ca93cee0db21a27a90836831d103b22f772a06ff5b61e4e074f00c0cfb8fb15f108585960cff591eb750f91365622f60a6788b460b253117e2a5055228272f06c436e470d56ba961789dbd2ace13c127e3ab9e600728ed4acc51c0e04ac2dce67312a7cc8903b5c0eb3da16b12a7f1dc40d53f6242491a940f0990cf035cf4113c12edeaa9533426627fa8207a78af9751f09e4c9c6fd8bb039bff2382a027b890b7ca3dae0ed1755c202088e88e1ce8384a66e6a9a4b46261e0de6f02b91e7e38a373aad6ec7dc9b08cd31b0bca184963eccfdc0f5f321c9a6760b933a73cc00cd59c16ef1b35506899fe830f02f00a2894948c36f934d6a7ce046558d0fc992182c9ef8322cf96d38da0e2cb9561306a7b8c48ac0f55a54d48aca0ef32a8dfbde94b278398bc645e2379dd76ac6981b2a4dcbb994c313d167d6468a27a8aec462031529d543d7be62aac314d036c2dc252b905344a8e278ee4ee181f3097f7edc64d289b148e67b3e3593ba59eb2adb7ed728d464a9cd06de9a410c2d2fc9bf31dd4897ebc9ead42ae4a8d201cbb50d207a7429acfa30347616bca4f497715a2523bbe2f1cc1ec0631885ee1352fd4e5885a511f85674a423598de4df2edc6b9e956f1ed77426b83825d444e461ec128b58d25176648b07f82e6269aade0b346da6781fde324a19c029e543fdc5382f8557046ec60be8e9fce64b36d20f9180f1ffb07bd2878f522628e6fe996ebd7cae35a713813d0b2a6e49b4b0d2fada5f9a6645342ac876b2aca5ea064fd13d9652b007a365370ff13f0bbbe7dd010fd7b294d091ef0de48e30193faf6d0c55e54d3b9adc1310ca826e64261755e4d5034e56a1f0d1aa13fe38c5af61105430fb29a09188e6bd23ab2b66819a5c20155f7d1fa011de8d6240e76808a8888ceac6e5bd67035e9711a26a117d67c5c14762f3f94c1e0ded36adadf680b3a5b28325331c9ffde477699d0f03dfb813176b8c4e7627959d1d877e474a738993f46137ba1a297b2534e75e4f908256ceb87792aff2a1f11ffe99ec0eff0e2aa0e4262715de4b79833d3eaf5226abc5846bd6cf061bfd9583b216cf248abaf1fd6070913a3f707ebb26ab86cb0b130871b2f616e8f5141378eaee594765b8cf649f80af3fab7be17aeeb753aa64ef8ec49cd32fc20dd5d1dd96c27d3cdbb581712da6fce8f987986f0deb441f3ee751339d439cfefff96560b11842f861d5354ef28079106adf7eb8088560e4a6b22074a849a588d5c75042203972e32b2da77bac9585fb23d7db17d42a57bc60b7d08054d953e64554de39a04b9c24d7945ef22d7cad9ddb5856b03aa400feed5aabf09d1bd60d06f089516383c311f7f78ac4c6acdb68c419e039a6150bec2e09046c2e4995f5bce65dd36209c3f4e2cc8054292f9f009e5f2288da554c560d8d024430d22d8b762a16d92cc457243442adf25d219a0fc89b78a293fdefeccc77ebd918629a947aceb601de8258fd35ac83b5fe228a0f328dad21b3e75efaade1081ec3b5a89e615f2489d72261356f43faeb0c0e02484623fae1d293cf55210752c0ddc40265ec0bf90613c142577b0c4e5faac573d715287bd2c21454401ceecb45722f1c144530374ea460de1e6fe0c3c3f2b3c9af1ecae2f2cbc3f5ecc10060a080a26bbeb23e1aed0ddea806df98cfb8fca05e9257e1139cbe7210e427d6af98b5ff0494bd4edffa01ce8d848efc1e34498b8a9e38e8c0d05e87fdecd368ef87d9845d2b0b34706cbd67c2331d22548aa410e88f2a7596d4e2df9d59d165b2bf4fa973386d8a6a7d15144cccc38a436554e0399a74a854e375d2faae9c987d99cb436e7da3ba0bba64821e390098571d8c2fa65af64681e570432bcee2003ca1c0b2d0115e87648948fd30b6ec6edc1c05bceb6d4154fca13da8b4df9208be4c1f33933efb2556f88715ff7facd00044defd8cb25a65c1abbeeb4656146fab35f054ce56ec50f1ba0c512c52aa02e5688c5069e12fd97d802895d459cdfa4c06c71552f75c1fef78f1b2e5911ec819a004ce2ad3c5548b49d52e09e9dba0907351bc4ba62911eff30bd56a95358cd3fa08f3e9201e85069855657eaf5834d5d52544248e08d387d6f75c58d4c02e2c021044fdacbe06eb5750c4fa1d18631af2d6ed49d79098774d4f6ce2a6e44d3d27709e05331b8de89c57a76cc1c0336b35a70cc4ccf58047d204dc7a456bfba0041eaa8e35992ece03f2982f97fd0c5d640d1c550c4f8b8ca071dab771364b32407a967741b9ad744972005361f29fad3d36a36e7b7f476bb8b6a6db48e02beba9fd4b2415b71216ce091ec825515da0251456ca44f4a2cfbc00edd6507a2f44890ddfa91d96c234117f87014388b171ada3bbec39e8109a96cb0a1bbd6c9e2bb6223b64762e0be0961b7e1805e82993ff9dd181f5d13e5ecf52d67a763cea9859603f27b97bc54ce62abdd9d1e18a107147be07c8b53ef2488c208ba23abd504c51ee8796b24ccda856bcdc24cd2a82524bf032d74b502d14588dd3b4ff3b7e1bba334393ed6984406714eb0a67192dbee2ba5783f6b2c20ccc7f4d0f6651059c548f99e65287da537d0a7ef056d5be81031ccc2229290053dd106232a517d40013143ed9c40a191dd8dcacdbcd7a85c0ff0342ec32f8e2f65a8ad45ae53bbe4500cb9ff783cee8bef7060d1c202354082fc8ef0e8fda0b09f29144210ab08386d30d85872c603442a19fcea13d58b25af1abc43e1440c2c1671eaee9c9b8fdfcfe78e896a0d3a08917826db2d815eaf9751caee6230466f25fe00ec3f7aa2ea033698f68b830e702c170068b51c3b80270ed68831eafe352a5bc4d5f0608808bf2300d4517f620976234e846852c629bf5c1f0e836280f836d336e0e674de2d8e953ca58ec744fa4ce0f6166a6519d8881c37d3ad4babda1f1f06dc81746a0ba281d85ff7815c1d35d417fc7a2997f8120d1df0ef67dd5ad22ad6668aec5ad5a2d240254a74faf79d9f90ddec61d0f426bb1828868af387edf13a3e87e0409092e6564f80370591b80a078a2479aa6024a7223417141dfe94ac6b0ba87632773762db9676db70b390f0a2df2f63501c39195c3ac463f1aaeb0e3c05111217505fe3d4077b33904d06fdf348aabef16d160fe25bec9db8b6501ae4cd50fc9d038904b932fba76e68eac89a9e675226da5bf35e16426a86ba2398cdb5df39730c6697d5b0062cc12f256bd78e83b295be22181ec09280621f256113204f8e34c49acb713f850b5649eeb55d9580aca660db1ce28743ca196f7b26afc5b7b6b6a67ed140e4713c37b3197010166d05a53d27f5ad16b86491753d3e2f391a48359c400a1fb9a7403735323392e539aaca2e5cebb545a5a32017433245e5de1248a1d6955e09edd3114a9a459482f356280aa2bb98fae329aa57f85ab969fc55c11798291d3cf59b994dc555557b5795c9b32ca1b9442d99c19210aeec001daa39d94893d6a1e29a831f34a613c8da73600255999931d4c7390e64ad457f9b602e980400572e39cd1b88e5c9f052fd930db7b13e5016718384b370cf7edd755ea75a73c736436a19d03215fca359f663c7af0f16818115b9033cecb7072ea4560f21de6d44a0ec1abd6d9f3af81404a78d6bb666b5912105d921fc65febcc9f272db2d30002b616d477eb18e714866b6e41890aee1f3b4061dfe4b3d23905a67a98a882833acf6261ae426d0b4344795e1748248bc3e89ad48af708e13caba08cdee3d3b94dbdface923b309005973861868e7c9295c34521e0906ad854e2a1419a4e5b10278da3345fe65ee728c81a61dd348e2e3604680285d2402085b52009a98e1d4e61f75e17eef53aaf407bff9bed5b07b891a7030d7eec01a3c3289d749a6adec6f78b930b702fbd3a8bb0c081cb2e11cefa88c0c0c3e7bf036aaa59cfe626990dfd3e85ecff37a0786e25060b91933a266451ca90d03c73b174c7f00641e2999a7034344cee08fa7d44043dc7db3676a00a5dd586c4e6fddaaef302db3694e511aaf3302ab443525587ead793bf474c7d3b6e4a3192351e83275319fa6cd042f962cf82aadfded240c0aa2dd9da2a41d60c8ae75441575a7d1e638a797aa512c20ec2acd380c3e77d321660ef8eacfa32a4514a28fb601e8f87f357eb7d82cd167c45c4969a3af2a0b6055d046d866c5d9ad8920eb90e64d7a2d4734713c0915f12444d9782e768aadebabe43373fc139fbd605c4dcc64ed934c2e1130b2ee25bba24fcdb1d1f5559a55543bec4c60fa68ca079ddc53e9dd7e4f447052dcefc61ac20f8c0da7de6dffd62328740522d9e5e7e8cfb119f86009c0a0c72dec3a757ca64c64495d9d16a1550a3d74bfac7aab8221bfb6163d45b0a53009fba81f259d09fcff4a9ee3f30b4303afd87a23789bf1a63ecaae08578d09190ba5f68de5efe8c920a922c40373c139ad601506b2f4bc60e2129b78826b17ac5d954de1ac14f899a603bcfa2ca18742b3d84a66e23da145b4a3c42ff5c773a62226c8d6a8caeb50a208908222b2f601ef3c37599a48fd505bab6b90436c5cb2189641dc542603de80c1bee938e68a0c28b48e6857c5daa05ce25c4c84b589b3137b280c1df8adb5f4417a497947f05217dc888f684f6873793d12caf19dbed38cc967e408b900d239694a982ec62b7abccaefeed39feeab13aff90550e70e6e5b97c2f664e8b09d49df95b3729ac67ae5086f03261c99ce9893e7b7c1972822bb50a06395a4319405ec18a632b53ba958dc67ede821bb0ab7d7b36afc78a9722eb3cf92ce6619701f55333e845b22969d4a896b8fc739ff61ab78b66242f3041400e8045eabfbc66ef5c4082b2c0990b31073c1332480aad092714d30d082c5ca6a74b9128c413c873726cd010ca3ad4dccd442fb8551afc2fae2ddb82a5622677245814dd6c37342af69c895da809f94c4db3ebe4232ee2198ff218b17b8fb9cfb36747aafddcc32a02d91ce5baac5fec92107d7f2a891baa738867a0987a92f8c2407c8f54d74a05e5310a2da05643be465f374e90f8324638d86e7aad04f8e78e58532eb0b1baa849183849482f33c2660a9de2f5e50b245b417ba2ea706a8ce83c55543fecfaa8816301d98f187670088c9d9566cacc2941a490b470867085ee2161786d1177d07f673e2c0a8651d868a4ba2b976e0f2f8dd06f2281c2291b6a92844ac498f929687965d36d896adb294ce9c643d0435c05979d56d159ada4a441f6101b69b8bdbc114102f63bac7ab7ae99d62f3237d4fc4fefa46e8e44b0f64c847cde1ccd209f135b8c391c4a441bd2c2813888f62b903e46e6d6ea1f835818cbef94196323d420f5a89ba871b54982115415735db870d14297cf51d844e6c059e8fe85d6edeb533217611da6b19487b3aa91d41a006447654a4eac0a0f286982eaa3f11d1ef4c4309ab532bdd7202a4fe76543a2c481d450055fceb279621e445a4324b4a30cb73bdc817d6e160dcd84ce914226b992d106e291267e4824eee77c0a9d465bcc8caddcacc50b1b2642c8448e5a8a0f826ee8300e2e0890ba7c84500c22a0380a81c759852983bc24683ab87be4814ef38e2ed55a3e00400c3b505dd96238d178af20e18b40a622bf6974d7a3a95c01157dff989f8d2e2c9e75312064cbb8963ef7f34ecc400730d482c3e4a9aea1ba95ac8dfd0a264c43694b03542e04e9034f85736c5a14a6741115453c244c33d87d9829f102fd9f2db3546e166b3fdb1ac0d4e7f49e0e7cdc3e97e9b0a9d2b968ebc04edb20bfba1da7b56e33d8a51345864119e91be84653069dd51201cf312abf895271e2a37311dfb3a9d16df93f669992c6dc4b208a62254e84a5b65377632bf89ff7083db0ff0c332e9064eaa61b39512645774d0e229333b48aa845b4f4eb8f93b8e69b360db8639100db2b50ea1d4b0c76b8b33c4b620cb528162665dbb716581c7759d6c9d2cd8d631b594a474a9e21bc65c048c9f6716cd4b9aaae4472329f49a7b9bf8c5b7dabb606cf6e47930f0df472f604a2a62e03118d07ddc31746b9aed06d79387de62e286343f00bb5fd56bb02373d25c62eb8b9cd082689b50bcd40aae1b9782406b789695b073c271d4ee38b94de5c7d178b8ffeff91d46eea483089603f37804bf83962e656543c2324689a71d63423f88fc2dca29e319045b90c6050f19b74da2b9845723bbf2924208d8b65874a13aab6805db757a398497e9a31ce5f011d3076e49c6a271ae7dd147418b061602753ca51e6a2f7f4e366c2ff4b992719d174bf3c21dc1b286800a6724b51b1097d1912d3ca829da55ea9850e2fc19aab11bd571fb743fed9285790b98402741110da9e3899321f0647c243d5cc2c7605286a4475108446b0d46c2aa985409b083117aaec0fad22d080401735358cd4554decc2d09f7640b8ca7a719db6b129f72d298b6381d24f5ec83e4e9e861300fb2d6c00f34fd4d78b619383106c3aa5093274bebdfc230b8255bf44c49c6952e1414594f66af4361cf24e1734a3e36553297e296496af7d8db57c5af27243b959d51f20c7eb4654a0ea2593852adb900b5c90c17d03aa8bd8e6b9954ed47653882157d694853eea42c8cf59535e1a81f7c0567c45441d5a9b4fdd7bcc2ffee32106a88bf1921d66970313ee34652c7b2db0c41fdeae14d3c0b93832d497bb572bc004cbccbbbe704d10f2c7018fbc2aa4b7ff151e6eebd78d87c010c88c11c2e799e9273ecebcd3b2b4d7eb3be350bc15a8ee8d648cc49115a99c775ad63349caca8e261316b716a94b99dcc9b00a72dcb9cd8448e470cf5d90e9c5a6883390eaa4bdd84baa353b949145c98e6ec8b979ae700ebb7c70b0a2f9a86cd3d94bc6188fe4719afcd7394afae850b8b8a57c1ed6ae883a520084b317667408b69296bf68a92c51e0b00910f11c1a066361ab7cc40d01d83cdcfbad8efa6400c7b2b7df9cb84b6049808a74d860d18fa8f347274977067aac57790114c6ab32bc74e8039b06ef158cc5ab529e8b57b9147575f9885846d0b365ebd0e54b3d60171f0bfe7ffc2445f5c610b8d00cbce00e62b43e14dc55d2a32d63f795746c0d9c84a09d19b0720b6522e490447cb42b4619c3fc12d125e0484836180364b16ef6dea3403c74d9f92d93028102134dbfecc0f17c46a4d6317c5cfa69ff395aa84bef9d4ca8e4a81fe5b9e387e8663083d098f69e666294620842b804e0b10d7e5f0fdb2c30e7b19c5d7ce9b0bd54f5f5c4ef25fd8e83d22dd6201cf5008a0957ef1610e8868eff4b34382ffd76a5ad167718a0970451fba13a108728a21e040e8ddb9159e2a4d66990f6de00e9f4a49da729bc994f32fd21ad4379c602b13edc5ece29cd88c161fb53e7b5bf221fbfd1544b408cdfb9b69738c0f444c38af947f04eef7224ae440e85ce81132ed20fd6a08b7e7c7a22d5d731145bc1af3cba061f15d7ae04e76aa435bbf6d5a20cd051450cb00d054926f86e832992c800f82a449879454bf11d111e40907e146d6827956a0d0b635c054e844b43b8b8155af0a47b7c1b92f9c177f588afa6a15e7f2c318bea4268bbc11af70604ec9be9f6165d827ad68d513cfdeb919beebfa6f2b92d8692d4abd2c7e1c109e6c2513504cbbb6cadb2dd4f45504b77f8fa26581d6a7f5d18250f8289d705dc1d8bc2d46c8c8b56a3b6ec5ba60bfe37b8cfdab5e744706f4fadc4814ebcca050dce2a784d8fcc6c994d4854f977394212aaa46f915d9825b331f921ec956e6e5a679d0b0e4a4a8e3b80ba385d93016fcb835f415f4ae21fa8685c8df21ccd8c4ad0d20dc4cebad442240f415d6839c597d10f8117d059e0533017288bd86258f90a03710924b1558970333af678dce4bf782cddc68e6c849612a8cf84d7ee199579676266a383fa729b69c1bb3fcc99e42506f55293193accfea1ca602199655b13b20cea5849144a48da0d2bb7bee180f0043134c7496603a65a4863ee49c0d77b724096229b309eb6101274202e2accda05f0c0c3b034b533330c6ce3b3149bec663e2ff0ae78e2264739ed3f79bd4c06cfc0f54cd25f655cc5b857b92be8cc846f037fc398a656843de93789529a672d354ef5053ca722c71a628119cdbc89539adbc011256f4ef4013751bf94622331b18755b188e32079e0059158dbf2fca29fa918ef5096a93e27ea1f9e7b9586fb0db0e4ce103fa71927c5a30523a4a1b557ba611d5181049f87b5d3578efeb15ed62555c45c95e739dc48d5d89b1a19346eee9649720ce20be71ddf7cecec471e3790bac553678cbb848f0a2321d57063ac65bad11fd0fea13d44d12222989482291bd690760084e07b0073203ca3f72503621eac4f840c0f5af379d7e867b4ecfa8437fe6d613874d1a6b4a8fcc7d55fa56383125ac63d7755dd5d0dbaf32f63663d6630880bbbaf247949a2e65159675adb56fb19bdc7f89497bcdbe0848763d92f65fe7eeb57addae6bd332eb177fd8d192f0b2d30b76e207f8e362e211f4b1711fe311d1043bc62b95abdcd33c8d5ee2499df89c0d2c184a1d4b7b9e3427b566fea491a86661463b9256423ac16234e0e803a683e455310fd8ea0f9fa828ca96ef590587e2cfeaa3ac2af0a5a2c71b04499ae9ef859d197961cbaf681bb1b634e92f866dfa6d03024aa53e6f67db7fbfb36ddb7e1b4a42b76cda4c992933fdd7ed4c675baddb5002bcaa3c0b226902acb7d62f86bdb5d6643299be6867d96fdbdffc75fb9a724f19f8b587d956b4b79c846e0dfb2cdffc25d998cdd56399fe35bf7e90846e5d61e79c55aa6acecf3dbab027dc39d20eb955b9cf97347b8e30aebbb95ba77c2eb54a1527f6c42e480487e4c79f28a0860c410d77ce96b342183d9797c72eacdcf4da2aab8fd10cb3f7fbea150e81e14f5da137037d78402f7e0761849ed72a6bf4aa2a75304429c03ef62d60df02b744ca92eb97d4c72e0da50e108c5eaa8a1504160a61bb7d485eeae89fba62c04203e0eda6674c8c7388e34044fa56ab351d0825c4e8b3aa755befd65b5e1c41ce96357abfb3054bfd59f875cb4737d473c884518694f51656cbb2f4071f47f4e1d5e73edcc1e8b6657dc51b96bb68647d9dd6b2acafea4c22ddc15495e538ea43fdc1b774a53f77aaa30a28a594d2ea734faf023a84d0e974a702858218ba6c8f12dfa330c07a9475fa8c925616ad547644e05aa5cec49565d518e3ccb9d785510cc32686490cc3302c6298631886c11a42955583449905b99910d72a8d6adad4344d46d7340de20bc3988d0eaf0b5bb7d6420cb3f6664259a669dbb64d6bc7971046cf9ac3abf9b6c19979c41056964bd5d0b48728c8cf77fc32b05d6f189b4c27df3dddcc8c27b42324ee1cc8d309e108899b833cf06b788c31c618b5bbfc19a7749f4b61be54b992f4c481982fb0d003949a77ce2b39135ed9baeb6c6c6c6c6ca2436d03521fbb00906a3acddc1e39a7ac8935355e032fed83e1749a99e1381a9a1a35f0c6aa689de130e73457484d07d40175b1734a4353a3464d4dd779363637372854aa67e762685574deb821a35f35375046ef6cf00dbe12dab0216dd88836bcda6442379910148ee3407983729c6843dec09104ae40638ef9835be24ef5ecd41c570e1682bd317f292171dbab23f3349d6d07e6c163c29b96f15c1e8b5d3c3cd5e27158a37d45fb5e871107077338703e5b08b5ce9183f32204f403c210421041870ecfe33a211d6ec70e1e3bdbcece8eb6b3b3b3b363632313ba2184c3a3f615ddb89109898f234772dfc91ca1fc5c177de7e0703964f41c392aed33a7391c73721cd29cf5a633a11c5cce0de17a16e48a0075441dae83e66428cc9d5474284f803d4f4767c70e1e3b3f42266421beaa954a553435654c792a958226ed2bd2e1e9e87411da910989bb0708f2b83c78ec509e29a3f3c0aa7d4b3c2f13e256277ed43a3b78ecf08c20a343c8b343661d3dc6e831422b7ff59bd0dda7363f222866f3d8b163270bc22313a23c3221d72828f8bb0e631129401581e107459e11a4ec4164fbc748d4790a0001c884049009c11eefa9da5734c2087284e8b0070a85d4beb1a3fe260081262c8c6e44c6996c77aa69a414d248e98c926a0de91473ce49941373a0110906d260a7f657043f3772a31d03f6f73d938804c03be0f8dfc78813638c313a86151cb2ed1923b7a5ac01f5e855abf5b562dc75305715dd441ec444d8092184dc7dea5cdcaa28a5d4a97b2c62d239e5c3af43f2ab6ac99773da9b1e2f289d93cea9ef3c83fec420c5bf0ef94f2d44df7277cb5d4f49abdc3306ab307f5214a8be728931a4958ba4f690fc1bebc72b095b0f777c5a7754e2db7591d4aef4d07c9737d88ffe0d135809dd951ea25aeaa1f83ec3d2972caa940942861bb24c1720e1e1080e2e3e40022f5d0081cb126362161d30e388279acc3082262418244a62e0a5035998a84103369c71045a31a241135f9e30592209337e904598264884e942c90c2906121b4c0cd185164590994195320a10850c252652c2fcd0c5bc922305195e6c20081f34d1032ae6480f61c0600b285ad0031371cb912d3e948004484835f8820c19908104298d1ea028230c2c494954073598628a256a80d2039931908821a3054300010aa32f35d030530922c8a032c4132c64446024c34042811684e940131fc22c9d012684230f0021cb18198ac082e6e0c8ac02c9092ef0618b3159a0e10130a81847ca78312687107421811552fcc0831eca50d9e28b333e3066098914400105151a7ca1e18b1c03893efa000f9618d3449829611cb1028e61860e70b86203165ba8902408c3064e0091a481249070010e8a8408c1126472c032441857c0e8c10f860801164258018510f308119668a207625809410c6e9853e091a9a50b078a30438a1f68918418f40bdc12850cb4b84203661c81c313934a1a5794a0063878f24412497c8104822993c609c68ce0cb1667502fb48031c313204ce0050751ba4092c2165d6cd081862852ac50a5235420819409242d30b1850a25c48069a265a6801a01c41622e822461844d01b1c992f80687c6982e54910aabc400772043f88c00654b4c00a0e5ba2e002890c63c268228827be58c2872d907c51c20834c290c18a3368a05e60900ca2f040822bccc0e1055a403252762042074fdca0c50cb309241698e2041fd042891b82b0c2060f38e38805629630a3075e680d669214e3e3012caa4841175e84d1046a11812aaca46183204ee00507ba1c296201106980b105193a5cc981d200491454ac30da010f1ae08013740631d00004305ca0000733a2805e661648341bcc20450b30a688810697b802314a3ce921cc0d42d02d47240f31474c3027619eecf81188f8e323c6075a01e365c7773137a72c29fa7895e84379b0e3fb951c36ddc1beb82cd378043525c09f2bf952f46ef08071fc3ff635f237778d370229c6ecbbebe6ed7e6642366f6176f3a7fca5f6e96f7296bf157676cadd679928b5b38f9910f69f27d5f8a4d3d1c912f87327950cb8c6bf67416abec607c90aa3973d8ef74eff383e6234faab1be37e467f7e0c247f6ee898e1682875362db5335dbddf6c31a569ab54e9d93769ff883ab08a8d118b91d43f5ec7bf7127db7abfb9827dc79bea3f586503f9d20fff127d28fd1ca3edc41dc7cc97c2c09f4cf912944792ab9faa554b4a86061a68f0bfe63412e44646bea05de92fc85a7de7db9e31188707f8835a64f6e3ba6ac5b8ebfe33b8af8d999023f1439628367d0c6a195221f14396a44d6b75749d4201eae931e14dcb2e10166310474675fb58f153138cdb6b6fb529b0efeddefb4375fb6aefb5d785b3bd32cc6a1b5c98fe946127d3fd7ab7ec3bc3aa1635acfd479fa86947f7de7befbc990d2e2c135a68616484e52f67635fa3cf497f7557bc4df0f49346e3199a65cf68d9333371e6bf222af42b38a33fece929fb36e5df387f38f6fd4dcb593ee2a9093108adb5461b5fa6ea4e297dabb2acaa48ce105007faa81fdfc6c6042f6c19718e990026ed6cdbf3e3c76967d3b2aba4b28ab01859b08108b344f73697aac9356832cc33d955057d4929a592beeb9ee8590f923a99f0965d8b51ea4facfcf564dde34fa24f7d8933b5397fecd9137f8252330aa5d78362a27814291d8a01dad388cbf9fe010155203cf9032552f4a9a46c229bc826724acf8532f341d18b3ef2639229e22cd0d81f8dae2a897434f6855536d4017fd5dbc7feabd087c7587f75d39aceb47fb00aa55a89dcf46da5844aecf38954cece0edeb4eca6a20d49db6e94e6bb1b98053f2adf649bef724d9e791aeeb5c76ff115b072fac32793ddeae55996657fbb1aefbad310d6e81aba66dbe38f3f61968d31bc7dbf313621faf8e3a4fdf2a7fef7fb7b47bad8b24a4846f5dd49f4e2e32ff873244792187c17945c3fd951cc4cbc5d2cccb9aeeba29452eb9a3e57a67f5dd7755d9665ed58182db263557fe5b96bbe95b66103feeac4138a65f1a665106e1d1d1cdda6bf0eebecf8f6b2177677f6ad738b9d1ccfc39b96edd8186d88db9b18d9d37f75c67f382fc1f4f4b993def8b7b75b166d599f3fddb608ecb2bfa2287b28fb216b6b96bdbdaeebd22eec56edf433f1badfeb2f7bfb417263d7ea0f76b96e3fbcdf4bf06d6b6233b7aca75996595d62972e996bf9da7a5d6da30b2875826203287655a16f3f888691b5828454a7acb08e38dbb9ed9d9bde5d7f443b1bff7d7b73c5827d63ec462f76545bfbd6d3fc153931e9aa318d556ce6b4693965d921eb33df9b56e464574af5e6dc78fde7f4f70259a0ce71883afe34436aed3b86d94c04bb14814a50c9a1d276a0ee081503fcb90f108d1d7d9cd817e4cf49fe489de851fa0d903f3fe857ab8ad3afc3315be2aad6f710faf5c3080c922a92dadf0ad8dbafd25fead2f067ef5bce74fe746cacba2e4d94b3aba34d8ba436d54377a691fd4d5dde4e4eb5d65afb35887dec83e2951fab3999f6d95c4e4311100c981e860c98be060762d24b1c88e9fd6bc41bccde94ddca4bdb2d7fddf79f8e1dbf7e50bc377b98ddec7abfd7fb755defba48ce1eb2bad3385a89dc4355d71b3d9ca26558014365432b631ced6e432b6178b095c43d4497ecfa18e68e25895b49c5f2f5156696c6dfc470ccc61ec3bece8de9fa7e6928a7d4fa44a20fce1ce74ddbb7b6be045e637c35a6ab43d7f9f0671ff5634d4718c40509680fa5cecf20cb940122e5f31fe265caec21a222ae89fca1c0102475744aeac82aedf7dc00266d7a85942c530648a6c542f7a3eba856bde97b0f8aa345c5b3735b6b71b6cdc9d976d3b2ebcd222cfa0e8a78d82da59f43297efa3350fe70dc9f3ecb50d9cf3c0472d24b5099462960db6666def4309cf457f73dfdf60d98d1a687d9111666fea497c02a75502b68da3b10fca8161c087e07f2612d053564c8fed5307a1d04a2d5e8ddaff9734ef4b0d4a9aaf937ce237eecf40001459d1f55918df10808298c5fa94fe833dfbfca775da55f5c8ae042c49e1f87442e457079b267915b5d8e31613552814dbfd60af557814df557813d047fd2eb3fa81dd312de2118e052041727732b997beaa8efe5520a5ba38f125a2bfdfdf6b1d0a36f037d6af4a27dfa1df4713d7d6bb3fc18f6f13be8436eecafdc411e0c67571fa1577dfc0cea602c75aabf740779aaf9357fdcd657578da315ee64dbcddbf53fe2d88ef9176dd75f7d0a03ecfb8ab8225225e697ce96f988efcf8fe60f196387434d9dcda638e83bfc0972273b48fed4a50fe370ff00d4891f97e24ffd751b092edd80392a9f1f595d58e8357f3dbbdebb2fb72fcebe39fbeeecfbd04b80dee36a9b4d99c6efd94353c61868918d3108ea55911d2058d29e32fbaa7ddc5cfb8b3dcdfe16524a7736f6f52bac6aec87b2afb6b705c06f59c11e33219f655b5dcf8454ffed6c0f8abe594b29a5d4dddd35f6d907492dfba699fe7636fdc135fee0bfd4c24cc80ef601d285ca9a73caf8517a9c310a71c2aededddd7166c09f57e9cf4a0404747a82458716649f69ee37cbde5ffe9c2a9abfd4dea6eb6ffbfb35b9060dd6327ab5ab65d909706b7a9e4cb46e5a6541e4cf24d5964f935465ca94a95ba45160d93ef6f7d29b7d633e5950fd97fa56d8b362d8c2ae6a55f267554d19b140daf32bfbf153a832feb9422985a8953f98a4e5e200891fb69450e290b976717dd79f7cfa36cbd7d72cf74df6dadb2b13d22eedf2781dd92ef616dbaedd58f6f5c8d5b2b77f3d561fbb6c9642d1ce78e2ae1f5f72ee9c007ec88203cd2ba41894b6f555cf18eceed42184103e9c4257fe9ec89db8932ddd0996f9d39a724a6e4ba93f4b3ef6d8755ddcf5f2edcc84a4f4cafa2a3e5137777443767029824b94524cd70594caf17672e4ad79a6653ae5ae6fe5c5393b2998049403276e3bef964d6ffaed31beb2148ab6696b307ad7d34a7bc8331fee9f3fafbd30fa571577974f63956d0352749154b1d839abdcaeb1733bcb72db78b1b86fb2bfd7da6bb32cfbbe70df6cafefb7d033a1cb923f6fb8bb7c1a7386e05029e38b0a0afd59299d4bd38bd8bcdc9f385d010da3870202fee6290575fc973adc45cb6e129654df924f89e694ef50cca5c3132cf214e914f1e33b3440922d9fce4dbf4ef74ca88a2f33d1dd3152ea35fac49f918a20ec0ef4240584b7c3e3e04c20993157aa16dc450a47f33635ffe10db91f1f38ffbd0f8eff727c6efce7f9d8f0f15f6a93f01fd01f798f12508ff2d13dd41f4dbe79541ec0771967d764283e5fc3fee4018c1166cb975c0daacb10cbb6e1aedb6578c5e608cc4a972115b562dc75ff51ee141809fcd5db6558a52607e049c841de4716f2353200fe270b3d4e4efd08f9c6f7641c2f808cfa2e432936978f709fa4ee1e6f6fe4111e47ee795416001553173813a2b12e244319244779030000a1548690077a3d2cea863702576f97e10b6480058d11f8abf8d264eebb0ca3e09ee66d6cb8e7381bee761926e54cc1dfecb20178e872016cba7499009c179adbe51fdb5e81bfd9e5126ac5b8ebbedf9e974a755efc6420df651fbb264339c0d3fc7c8d03bc09f9e7ed4f36e10f90a3bc0700d2d5f0b0cb24d828e3f4f183628c31c6184f32292949fb1a244934c4f8f8a2143d2f38d9bb401e2d5227cb95ec4e64a70279b4d871e6e3cc7f914b0277f9a72607d4edb20f764597078071d1e59eec5f5023f3fc4fde8f93771e95773c8eacf35d16c0b6c947509fa46e1e6f3d5e2e43086b32843b43b8c3c3eab143271f8190c7480c465be7adbc34b9a6e66dd4bc083535b3cb01202133f1307100f149f857c2dfec3200382df876394526ea24e16feadbe51e9c14396e974718d3651e202575e37cd0c3e8e1b88142039b508f7a9ae7dea9a03ebebd910971a8f7501c0ad3649bdce51d9312feaa08b7cb3c1e6254f6c9f931972eefb071464dd6f135b2f73f39e8bbacb3ad4d969b8b3128bba703d3641b32b5cbdeded2c58bd29718268e893a34206335c1dfdc71bbac236b419745e87208d6043cb7cb395b086a72085f23e7fc4f2ec0e3e41c7f23e74765111e47d60f9409f05dceb1b90cc5000678287f88483fc238e9e8e82893f025641f06c84c7ebe8607f2a3a5c02772f44bd857a91c513942bdcd8b40f3dc5b2f13b2d129146d08211a9506f60821e7e402641873e89c6d641f99840ca30e01725cea94701712f28ff7914b781bf9df06d5657d9552b7cbb9ea80bf0a80dbe5d7b2a58b8f4c4206ca2ec665b07bc40f923f36cca872254bd4898f83237b97e879c9aed4651c1b33a2cb37503de8b95db6b19fcb507c7c7e7072cf002e0e6a7b54ced91598f3b95d46d964a75293bd4af42a19e06f76f96673198acfd7644fca5e253b8d8fa692e2fec6e6f43701f93354e213aff245208e4c2327b3e35f2f351379141fcb1f66d4a926f2285be2e003d296a7a29ea8821166694ce1a5ac17766528d80f134e889ec49e524a31aa6d76c01fc678d04257b042ca0b51ca082c44ba1c5ea864a4e3f0822c2ebc07c2d09903936de32f5982e1143938b1bfbba39414fa70337607b168f8ef5f1915c4fef5715f9f659402f0df4761ba05540bdbbfb5305b80c6850cae040474420a2c044d89492bafcca4262fa015b000da3ffe05f9037f85e853450cfa1416b629d1c7faf8275c81572ad00f79cd8b5ed575bd705d4a1ba752de77152e2d0002712a4878bbf4bd9db0c5f8180f49f71ee194c14058941fa720825dd522123dba2fe56cc72943e528c658f557d5917429ef9d4424104fa6e094c147181416bacf0b6b8555b6a29765458b69d9a11069d179a5624aa6a495722c4e2eb6edb2b0edb3b7d9b6592793c5f055ad4bd334ad5a18ce9f3fc9b4ecca6a666536bb19ceb62ccbb2ccd2d6c3fa4b6dfe24ea443762c7d732a49457bbced2d5aa1615d2487fbe67fec6dbf64b6dfb0169f64d883f2e7cd5277bfb37b20d1c8ee66d7297617d5b6766ac9a1a341cde5c309994f0df87d643ed743a65990e7aabeb8e968e8e96e2d1921b19c9997884d5ac565c6b05dab5d628d68575524a5f51ae2cde188f8ee2516675448a4947b5bad157603086611886bab1e930a3a5a4384d3e20a57af294d5ac2e549fbfddc88d6a8862845305555512f6769c28874a49a717c9d94352d7eb2e830d4af287685403e6a287f990b2f43fbc292a7fc6915ffefcd31e3b7a07218ebf6f3f3b8e6cca1e7573e2a287bab9c16167ef4a5fc6ec48b323102612c986a92a33cd859d6938af945856e997bc6a8555b6cd43f2e7e7568c16b6ac9a1f7af531a62f3d87c86da94091c4c0202c9fbe3771a320729bc0ba419c49b5a6c41f0752ab13d1a3bec5b7446f8a11d047dc4044ef6807ccd5ffb0172f4b4a5e941cc90b4e5735aa732324952b55934ac2a95143662643af8c3f85d4a0017620f3514ea267e56ac99c713a207a2c78730758e6d0e03ed4d0e2f5442fce1d60a951190b90e7caf6db4490092bd4f0823fa8e4829c3ef4ff5fce6a42f4cac0df4bcf8464cc9965e01863ec2612d6d34bb8fe8b615bab3fab8a12bc8f5c9f046fe83dbef70b277afed8855595aed14b5082f711ff24d84bf0aff9bd878c1173c1f2adafe40c5275c35bd66412d630849694f2f3f0756537d72a3a05014e10f0e747f15f70ab7a3742637f750355f94a4a5dba402db54a596b4de29bbe53aa5128e8214b13d144d1f34ff544cfdf03d890f9c5b0e5c3f0dd0f03177861cbff70f67cf9f2cadff5f5ad4f6eebb3b6f51f8e6519b176d543d6e70f495165af28f0bd37a386dcecefc38dfd25effd7bfd7529662de6ee1935c4fffa4b0f5dae3fd410b92f6d7da67fd56b7d51b9ed3f5fc2e8535541719b3b16e176d17477f23bc659ab1860df9dd481690499b3aa26c8137fa681e15bb9304cd74b88dcf5bfd63a2bc9559dc6950a70bc4c948e3ccf842b4c9ec0401bfb947b54b06f1b23d0864040de8ed6dfff07bb482995e613f80baf9eb63f99b2c72eec8d0a78e5aa441e992d5fd62c5a556cd2c2209b69ff75d8572630aa7ed1ae4bb0ab2a1c24ed6f2b2f1d117affabfaea11df56ad2c9d84eeaad2a82173573f77a551d517ed4a9aa8c636f08728eac80f6242068e91df754bd004a5c90ae2334e8d023ff6d2eaa21731fb1447c88d5e9cd782f46de5740aa13f67177db069adfe7ca3866c77ecad22387b08c782546afa714e3b5fc87ca1ca55747ab155f96bfcd7c33b495984bac936b9abc10eb05d7f5516d837edb1e7d7d05f0f0e68467fdda95613ceaec5a84bd9337f3ec6b5fde7799ef6f171601830b6d01e422bb6a6e1145ad52c4dd31e4aed09007f807e401ffe9aa601c91f1f03ea646000fde748408e84bbee3d0f2905b4c3401e1b4db0fc48136d0c7270c54a1949fb53fa7120622cd5d002fff5d6d78f5fef95b1871b7ec4aca1f9f3ba3c436168be1cf26c5af353520790528efef75fb73b1b9a69b4faad0aecbbb24c58cbaec5ae8a54c99f52dbb6ec2d8b94dbb66d407b4b6d58fef89498923f15f0b2457eb0e2aed3df7b29280668cb16ec5db4681460ff20e9ef4a385e680cf9c79f6f59b7aa1c8582fc0f921615e3c4b6d9d08a182d56651109817b88481691f38a9842d42e54a99e05384c7221b53dd79e9f702935addf58ef4f5d000442c48958fda0777d1b46ee6e79162fc1dd5d471b46d4a9ae286d1245208417e12cf01e368cac1feac3a969fa23c2b099da1ff6433a1b811afb548a2815c4faaaf5148498fe52d0339dec9fb2b7699e55d70ad999a960952953a68b3a7115020ba10f9a65fb4b27b6bf10b7f24892fa4aac4f808c53d704500d210fa5b9fa2b53c15f3e66c4dad588b52f25c3cb7677f9f43d53c13315dc359e720bf74be812eb2d4705630f3314b09dc1c03d0462ca309c9e72798994164e6ffad44e459f4ca36af4a2ebfa033b03957ddf042e34157d3ea20b81cc3c0ca6e79e9e7ee6e90ba9fe7a9aaf8710d2ba29b5374c600ceb88ecac8361060629dbc3c0bd897bd3b3c069d36f9a85199d653bef299bae7d135054a72e30f884a01c3a60cc5941c41bebadeabcbc04140ab24c19e9310cd6706e8771155777568fd07b58d1670583af84e0b437e2eed6ebbaae58aff40266072f607630da514341b1b0952923b7f5371b71abadb9bd4e6d0c2c25be5bbf7080ba14e7e1bf1dbeac2afb002a239611e99e87c059b763d1726256915f6613e93edfca34c3cc1a328dc8c801998618dba4628819620b104cb144c548ee6049054488913421bc4288a5a32962f4306f589268c0a008b7d86cbde3b0fde368e5555565966559395525bbeaa165d5b7189df5a77f16a4c26e5996cc558e87392bc3ac5b68699f910f87cb84dc5dad2bf3b72324a4da15f2f8756596f541715a9635bfaa2a2d334f4fc86642e49e9047e6af0add29f3778570853c75b31bb66ddb766ddbb6d56db3b66aa373935bdc7ceba12589515828a79c724a09a3124cacc0ee417624ac2884b0a3049a907ad47a208f9d99901f417c4ba9a1f65279d4244621843da7860a42bfb9e1a441023838314e4a679269c54208218410429f16662d66ede4ec09fd5a95fb5b1e71a2ac2aab56abaa2a286755559555ab5511e14c5ab9af555b4eab5955ab56abf20a7ea5616559b55e17d600cfbb989d93e60f0bd19cd13c2177ed866ddbd44429eba729ee13ce39a7432ef037b53cb21ea594525ed100cf23d275d67e73cf39bdf2ceb2e6acaa5a41f841b128494cbab88b5d183666f81ca574469c1c58777777d71046f85083b156021b67430821c4d9fe9002a6d40fd8e3eda43e1d5c08db2bae77c7d66cb14ce796dbdbe9e142a8a263621633995278f372204ffd4c77367794dbca9b6977c7dfdeacbdf6f7b34c639a96f9be7342c8536145afc82b433420a89431b32102a4e2a8f44e164435656648002000000043140000200c08868442c168381c1266557714800b7e8a4280661fc99328885118a48c31c6184200000000006064a6888300b99eb317ce9b79d38f19cad04833b1c8ff42131b0f32e2999964f16c2d046d4001a38a3cc705b6129d10be10723156099b1938d7682629f39bb180e7a4e2d2c460f8679ab9eb21c7e75aa9be2a3f29444e4abc12c4357b7388ca019d6f0536016a4a6b6a2df0c952d5a04727c83bca2fd4a46791d9583fc0bef459748f5aad60cbd08f630ecd1bf3e78deb71fdb9b88a488d7e56a2c31190e808912d857b89b2e5e7d88d13b607671d0055a047d0112ab65471546478f726fce304b44115771db376c2d161300de24a17c947635f8a08372eea69f7d63874235ba36f11928c8102e2d83a0b319f9605352a11ff330506663565ea2b0683e34b8480c03cc22410f2d752dffdc6a6e75f2e6f0acaa709710f641e7ac302d9b58d64e7809009f169525f499a0da11d9b2ebb1ed9804287635a3f1c7fd481be2d9f546e5f1eeed2beb031d6df1e17c534c4397884520f796cefa41de72489ac8634ded1b5de383636fb96fdb0e943039b398741ec865e9bab63873014c78a6c603fdca883ac74969aa6a9d9aa8ecc840eb18fc64f4cf1ca82469952bc2518cb80b12c5570c69b39e85843c18d4e585596eed365c90fe498579799d9993620c0be9121b5367b38a0d0c3cb5acd341f42a2ac1ce50681c68b60529cea113c53de7244aa31abf5f90f9768e2fee773a0046135783a1854059838adfb0205d481ef69461b784496b4b469c571a4a847451729075da4a38519b77ef07792f65f062481d79fcd0e6703ef3f37c09551b96d0539db6e832cb1a875d2e681078783515811b70ad77d8d1c89cf7c22937c662279d179915282dfd73d2a3b7cf2202943bbb2cb2d4b13f2c5c28ea5780ec033a514246f39863c5c889e1318a98d2dafadfde32a76da48dc84f0bcc13cb4493feb4847a22f2efba30b6cc1a396d9d191460f314011d98a13f76eeff5602776c6f393fe2e448e64b5a75daf31342dc435ad5cf831aedcc2d644f924464787187b896bf375023fc83fde3f1ac645db2f41ffbf09793fb3cfa13b42e9ab0613846b824c7fe87e11b8dc976e4e808d1f310cb5cc9e4bd099dc2dd6c06940dc3c1135fe64fd42cc3d414af1711f4c34796904ed35fbdcfa61bfc93bfea10bfce4db3894c6441230c26656b60c249f3234bae73084f2544aa2db933c51a9e337513258dfa1885f5b5bed36ffbbc3a1a7e7016dcbddc085219ad490efb794af754e34e9ad505975b89d5fac7fdd887ab1b4f8c858c755db5901678ca35840a0b4119e2298d90d5393773172fbb46fd5a961e0d688d0cec930db2eca6a999b6e071aff0beb04c9ee8c0c95f3c12e13583607e022c73030038396d0e61a889e38bd82727b4a2abf82ac1534fa47d426f855a50ec8e4e2f12a1be4f82ce814035c0d9b00139bb218e4da797e182b160f42a3f7fb8c20078fba474de297aace375ceb374e32a84ac996179336d731afd90a967f98c835f1e143de1950dd86014171af2ede66eca02362cc1ea2846a090c39b9403360a76b24283074e9a305f78815c42882e18fe63cd3603647af0bcc5c1e49ce8af8cf5e9cc29fb524a9fb6e77d5ad42601d58caeee6a8a032ce33af7fc2af9e3ff3aad83fbeec2a253ae6dd00c5a444f39b125891f9f81be0596e0ca29a5e9e95435912a4390fc43289bf5c651cc768ebcac84453f2e569ce56dca979aa0c914f794878f3a62793b09037c8f0658015a81bdbd83e4578e37efbf969f1f71b770750dbb8f8b92c23d1226c15ce474955c8a92c7717c31513180820ecd1ec0d0ab7877d414dea391fa0aa6571ddb028d2725bdbc5958877aa48b31983b30f7d51d3057e84db622004103688962f0acebc063ea55b806c152345e113a8fd096f542a52b1960f9c9f573a08aa1d73c2656778a9c46df9ef4924c5241a6f9575a5929fa0c09dce942cc689dd8712d33a3dcecd4a6294e70a0b718872d5d055c893ace9e74008cc0ed83dfb78020b32bec9ef40f9312bc424b0cd0772891755d5a5f789cfa31d18a39210624270c0857d85b2d561c35eced9e04186a19499dca5c2f7cee1b4ce2c1aa34269bb43f8872143c18010635beec9acb090f30051de30754d3f0e70bda9b70e090dbf10d02a1c7f87089890d07b41056c2e479760010c25e9d9e62725b9cbd0dce946b56b63a577e44c0c64f7206fdc814cea47594d266ab37e84946bf68585542299070162c63e4509677e0c98a657df302f302e024c32be17f36483ff47a3c53f933550398108b945cc74b75abaa3cbf72c5a99a5a29c1d57a49246667e07d462b05820099f7d848fa7df70dfb4ef5a8f4a5a3dfdeeb72041b424d620f94a2146e79f187527959348c3c09d41d7521253eb3ccdf8b1a7fd8197393eb51e5d3ac04c6f35a66a584b3a33edb06bb365e1d271761049e28a04105ba24588ee70c2501b55507d45dd9361ed1cd26738746092115dbc9e52783e22590543ef18ab2b15ba1038bb0da8329f99b7f505a6ca8c99f0774f1ed00ad4a120a05295abd4408bab8c22a04956d845c58de8fcb3e42d46657d10a96026dee86c581157c78ce6fe1af30ccc2c4209d449ca8ed025b0b2a8828a945d3b900da61609c544e00af4a0bc2fd827862831347c7983c0b6acb92d0a1a6badf0ed93b7fb35a9a54c916bac9767a5bb71088229472fef590d51dcf300154363d3e6f33a833838d8c0c5e05866571441ba315915654d5a51374998c18d2ded47066e692b6ec8aa9c6226f6b9effe8709b402ef81f9251552bc7f7d59498d17df6c8642fa5422ef28569e8909afc4484b1ee3c83149ffc643cf156f24e1662a998da5dbd83b63a15ff86e8dae5e0785f67c39c0daf205cbcbdab1ee3ad37ae23eb915083bcb36e4ab4add7f87fbd8b71946f600a6d4c15037a06bbf43c00b6813888eaf5af34d9372e6578394ba6a64ea6bef3f9acabd656a7ca6f238775e2895965412c414f7b75fc8790ab7a9f3ed5a2c8945523b866653396dcec6bced98392eedef3e3cd05bdec2b655577d1c431e6061be6cf25fa43591aed0a4db74db8fce72deeb19d48a7d12eaf49db8f5f983d23369a8fa23a3231fd638dc21f2d2fe3ec882b6a660085d3bf9810e5573212b061a03d0dd510882336cf1c57474cb50d0b29e912cf67edf09817affeba1cf1349cd885e5bebea78c52b8fabdb94173621213cd04802a45466a12d5c880f21365115fa914a46d68ecbe500aa537b818d82ba52567b1693ff9af1e5e152645a3131b42bbe5bb9eddba2dbcb67c62df50caa35210c1946c439ba983a79b2a947d5481b646ac4e25ffbdf552f94a9a67d78d9012e7c2489e89d861a81362ce9832a7315f1c17cc2154816d4c606d58538f178d0f528d3f70c47c9654b586895304d42c6d77a800d2e0734b0b9d184b89328d207056a4aa52828b35f72b626fe6e626331b166665148dcea3e85f6721e9206d083212db6773841ca4fb34a442cae70e4927d3053187a3761842005ece952c91b224f74399dc3895b2e7cc32079b63a73d256b987a4001df2606a269115481a06b8286947efe218a7940cf896e017c2c8eca100f805dbe6afb6962205fbc03ed798c61f8a42f0d423bf2145f8c70f32b3bae9e46735e195a4327cfd05712b6221138d9cf86a76bae042b4a5960e389bede0d80d5341f6c129fc9318b13308a012c77fc6ad0f0d95853e0a77b262c970e43631ba11465658b176e0e6450d70a08754bbd8b4c17396aa1f0d25ec1fa5d591a37d72fd9eca151a8d4b8801c78ac45912942d8c9e5a4b46f030f055fde15f343d7d1e6fe103e639254b342ecba69e5048ab0d605b88f7d32df24ef3869de4cb0268a0e4fc9b544e651846934d8703235f9d604e4cdfd15e6b4552e15f44265cf4229449ca9430523fd125da087ebbacfce07959863acb9a9f946c4f5b91106167cb1f4aed74661a4d46c48d5da407058b6b02e64568fe1cc45433f51a7a74615a83ca4ff58e4f070796cb4ce4c553b88a59205c1517d1c72e607e47e09a4d009ad335a5bb846d843e41ea5c4f0cb9f1c36de3dad3c67b9fc335a37e9a711222997e0d20c6e13b19f04a08718b8bb8c77da05e25088d777661c588beebaa10ebbf6e49807b008215a6aee5031f8613a160f99f6310140c9b29794b6b8ce91b8d4cff4ade7a970193267470b55853777f6fdd953d74ecff7d8a001f26bca7761041c83f35ce36697f865811ac6b5aca776b035307a75432d9a3b72a17fafe854d44aba7fd5243203faea55ad071e492b27403a554d725bcf3aaa2905dcb9d1bef3cf64685d80fce310de97b92be5bc2c3d55a51d4973fd60e5e9c3600f6cae970c2f58830511fd5ceb40ba50a0e270286e6067a65d21e7638e193afe5b3a99b59f42a9546245ae2dc99773b291b7d85fc537bdc3ccb8c9ce9825ad4f8a18c2eccf386f476d9702e623ba28083251ef712fa083ed3598b4c90182d71612d14465182f8b0fd49048a27a5e091747e47682cd8e6197fe0366e25378ff345d531e34ea3b761aa39b545cab93d7d040c2a1e254e1d9e3a68906140dc237eca1aeba020c6ab7ead9295ba8509164571b5ffb10c1fb6e3aa333140f870f647125b07c5d17a18e801c8aa3d6044ebe3718f2c41daed1568968ca540a3d78733678246244a2b841949669e0d2751322adda0e6a0ab14785bbccd4151e89dc61111695fbd254b1d5a33b146c9742f811e2e30ae5e63b370ac12a2f51aea517b17c23adfd0b20c0bf6911b6dfc23b3709d2f3a59c59f1b6dbf40920d05432013844032d413c07543425033e30ee4551583b62f611913e2e1de56c4cf1f6ed9f5aef7bfe7bbdfff3d3b25de134d3e1bf7e79df8433831ca605975b997e295c38692004043d0c0fda08e9269abd52aad7ae03256eb66053fee125cfabb04131bc56aa00ebe350d23629807ab4e9e93e5afcb42fcb72f5c0c545e2935ef17d650ddca3b1719cc9118fcfd62dfac7664cdfb0dfcf43403b69de86320102014c4fb0391134d6463c8045df4c8f118411a308a2fb4fcb97190a35d6a83c199195ee22dfa342ba317817db3e4c6595828065c4c700e9e3d37fcfc888d7ad85a30108e776347a70386a9281160f82c1faaecabd40d6c02a81b2345c122dc7644cadbc3cf8a23114216312da2e0a3a3274f0d942a73e553cef868a0dab8a4cb1795d54ad5aeea151b603f65af270c2ce2dfba6725056b667f2e2745661415e6a5a26b69037f9527bf9e5da708e688c5be8a6cece7e94ae555095ea2dad49c0f30628ff1c6edc840b1c5a4b3f857b8a573e35de19717efc83796a945825b099d376eac2cb9dcf76a4c687396ad4fe18d260db60a976ab88ef6080b33941433723fb3d94971571a61804115efae603ae575b619e005e72f04722f589b30c64777dcf4d79763fe9c06500464a26aedefd2075f0c007e64fd8b3200c634b369cd22a3dc916e7f2ee4791474a33106c0c8fca1b4d70fa81e42ff989e520362f7b0ea08bc9a0ac1f420e0ed93f06b2a99efaee2893e45452fdb27d1c8df09ae6daf40936a894bd4880d933b9648ddc14084b5a4b3c3845c1b9381b831329ef1445e1b13bf46df20888263197b88666f2c0443e08aa31ef3289e3764efa6ffd91ccfb9abb5e4ceac0a71ff0637368a71e6d315c7260f435dead76b200ee3db34d02c05a8c01a231d4833b4800748787a364938383374a19c602ebffe0e4c8d497e682708440d0c114aa444f93c83e03b2a3ddb3d387319482fc62a35bb93242726aea90882c53aebf01fe8191d93280eb6ca556aa07b20e088b78a880c24bafede68da0b3f9da7e5e91b32f19b32d839d2036f1373512b90e7d20384f1300c8d23835382d70ab7a1b49b048f96c62fce14e5caba34df71d8591580454c4931acc9e1019b6f512848ba03fac040fa15eb0fb372369828e71b5e5763dc6921b32904060ae63f7c2032d783465e2c1ce4d013c10d5c4ca996033c374527560dc0981bf81a2688abda9421d62e30c371a99871544cd46fbb822c2c444b5f10a8096e92f74d12bc56c28eec9fa581c4e97062e0de451bd807edfd68f5b234f880a90b46841b08fc2cc4924204e8eeb1015df476961d99cc607b1cb1271f2c55f1c3d22208306cf99f3bbbdf8c29cf04e3c57cb047346c9273645e07e726be99d0f7206bca2a0ba926f227573628e295b2dbb725d939932429412b5d9868c73579eb1f24e3da71b8a47baa6cff968723b0c330ec95019cf7c1268c658d2975af238653650ca84f80dc5c86969ce1fce802c5f84e63158ce087ac4053b31d11374fccdbcfc6b0a4693293693908683faf26ee7f000ba79551fdcb208adb2be718f16ea802b1845462cadc7be00c8905a4cbdf8b07f2a8b900e0b9807660e760b25fbaf890b772cca97150b3b518616ccb0d02361d86341226fc53a51787348aa1d75771a7de437cca05c38f75608b73c71f1112d81e90b457a3b61544d7a928310091871ba15ce31815084dd0e590bd1d192b1a29bc187623768576eac95178635e9ebe757c92518a368fe143d9bb6920278dc3360568a352359977ccd59112072c3a91857464c4952a885f30ad5cae12a4100173cb1600b552a63dc0883f5422e26cd4ac6bd044d1e63b1ca9c6d1471c60bc40040771e74fb11a4b23f2d4988069c9bd276e7773b437f4f69fdeefae14a5a9924e28a063d97b42fd843e81ae8c34557a988ec6a210719cf8ffdb5a9f9e918d6694e64fccdb915fcf98b62af8b40b79c6cbc2bdaaa6e4fb1c8a957c0efc09c61ba19093b07a1516a96e858fba65a05e7cf16a70f117cb6487704a865c6bf6edd606cb5eb6344be7f72a2ccfe9fae24ec93b1b3e5810146af75e0653b343b2a8897fd398041c9eb9f2f537a96e27446ce9efe98e1bd72439299cfc46d5da6bd520aecb4bbae01212b0c7672a600ea6295f4db58a5b9c363d3ff7349a0317f5986a70e9bc52f0edfadc2a0f61c0596d3c50413f50f56c4e4c51849ef403496a1f22c474d0812a73b586921840095c093351bfd29d4bdaaa840c94052670729b7b40064a28609ce26f3362c4b603d6360be79ec1b67847b69510db70378f9230d845b106f5550753ec23b87c8591766494d8fb0552a5e4a437bc62cea06ad937d72aed82a790a9d03a589ced49829fa30dab9a35e96e6ad6fa1e9f8f9f021fc7ccb921ea6ee43700092038f24a1e337dba370d90b869ff2ae9d307f92d1fb7be35ba88efb60f0bc7b32986fae188ae5c990c747092ac447850645950ff40acf7b93f4210d0090f33b9e946821a1d5c3fa50f20d2e443d8933ba42b6be7d9b8a6d3858467c05e47570f207ffe1546bb581f7fb3e790d838adca144639bbed26a8e629ba878ced081b9aca610b25a2f001626ebc0eef8823620ef356405cb6ef2dda04dcbb9968dfde6a8d82d50ae1b6d153c28bd3d4580ee532e802c3ff26f42013bad61da5c54ab3ba5a6d42ca5e482813c52473c6913d35f3e376ec87015947394d749c4b9c952ec50a00d13668b62656a7cf703b2a5ed0e628dc196e80e35b317e8d1b07edf7cc1f17f6403319c6115b3a28e01c6757dd4509495c6d60b61d83fe3e921ba351caf987b34bbdc5eddadc457f8dc06de9f2a19f8971bb7517f2459166947dcc8d516e5897956f5bb0b02d13686d46bc44459cd770a20d7e7c120ef074212e6a1b6d41d62af94aeb428f4f4659960c88ecabe4ce91cc9744b8caa596e6808e89c9264577dedc235dfc2e66b8ded6c14aac8e166e0a37ecfd4b9b83b12a52256a4322b04359070c7b87ec62bbf0153ae810ff32777f3e6fc552af48fe6da06b8ab2ec8370322dcd5bfced48497122a8462e0d97c7d401267cc8fa43df07e3c35bd7d7db9afe734bae24029ee0f27a69cc7dd5b41dd0904c73dd33898f49ce93ce0985b73cc1db77e66f112ac2db6c3cca9b49dbeb188fbf2c66ce01665839d984c87793e28c1199f2ce136b82f3af6e4de777e1e3600ee06ef1b56497220ecd0edf2ee279fc5bf24ec9dc835c4d55adbac878ad7485e442dc44776bb797ad79ff97aa0b763a72cd57f766af9ff69ebff227ef453661417b9d33c53f39f7265c8db667a99ce3c5bfb46e3df1fc0bf70b88cf2fdb59950ecd9eb1b7d0043be8a7d956d175e6116aed345f9ed2ca638f9d27bf2558aa7d8a7b840ad65f87d4d5c0481553dc9f81ba16ed72fbd7d053a7b5897a6f46d1f94e27a675be71ebd5635f1962c2a6027a97c44fc68488a1c14dc378bd1cc3d1b43757f4ae40b2c9bb5853f0a0efacb75b13c8aa0400672655c46f05012db3faeebfd2e088db1b738af8d0bf1fffeb628fc38a694b0fcb91cc544b7c32a4f60e024dc5540bbfb81024ef48f48392ad571e17da4eb69201c47cf5c37b87df77f9865eabdcb2d44c70049b4faf25d220b5eb80ec617041750e855f4e6687628bcfbb9e2d741d68b61ec99b0101206dbaac3821073092c0e1e00ddc3457048335214471d16b07da708ccd965efd74ae15c5135d172c4d36f9b1be0c47b9698abfe904614cf3a451c92d1ae2b0d5a405c8a86f2ffc54b82689b2b0bc7f06df474055da040295a87235304419a86fc42f93c77d8fa26f1913df8d8885a5ccda66f2a322958d80fea2a472f6a333c8ab66761b5e7d776527224c0185361e0ff97d92a8c89641190a0d6e2bf100e1d733154fefc33f8e0ceb84d06c8d5388c831a512508c03593a29fc360c5332222cb47d3f76395a54c96585b46014d66816d6302c31f03888f08e6fef60e99c347083d686c53e66014b2d7f3aa6a40faa7066cb0941e9fb0fc1e45105d6661cff1df877487dd0405b62e07650c704de170a2c4350780848148270b78d4ee5dbaccfb8a3239655fc7b1f97c5e4d79314af7156063ed6abd8eb567db3d7bc64e0efecb94ee6bd6a8da21c8824d6a8199b6c3d3043b27e8322ccf665126cf77e56be0f99b3e618cc69211fe20144a515b5f5aa9f992b7ffb08b36fd95e7aa3948024d7d424042529062b4fa592370b355b3a743591f9916b06aab3c03760093de1c6d4aa12e17cbded4f4ad9969d1eac0f06d09fb04e4c3868323b3d3b247a144d795a936f5a9fd5ef8e0cdce71d4b68891fe4ae046f192f3a241aaaf4ac6a8c16624679701a439ccd1c58a24fb31045d3f84914bc5aed122851dc2ec9874e6d3c0f0ce4a1a4bc05a9b532161dffb13fe440406735406507259db663d2d1fbed42a564a445eacf62a7cbebe791c0723e718177c0eba5d032e1e7a681ef1293f6f9f9f9fb9e74bbbc24eb88a70576d1a05913d5606564a3214d94198050946bb09dfb8657a872c2274d67b2b9446c74a6e9932d4f91a1ae45108d83d450ee37037e0413f360a6660bfb34fbc11f74b4cb2c08007cb5f64975889cdd83df3af1c046ca98eeee5a6d61757272902b513c50c2660b4522956a22c7ca859b1f2f19563ebcca604d34ca5a279fe6fb14338d2cebaa445b95c36a16132f750a5b38c7119fa504d877dc61f228076e56deb965cd3c1e0a119211cd4046c72f6467375d0697a549ca60617b29d60f5f5492e6d24b1dfc7659ac44e24f69f49758507994ee1cd5c3203c2ef441026c3e04a4e54c4bd04c8b18c8f294d41142f20a1372e27e07c48897ec7b87c69ad28608c46eab274141075611c9d38140c4c60f4567a4a086028eb46c3780ae886344e649a076edea083c1ea3f66a7a9bdb8e21aaeba4947bd3cbc2dde0764b28a744d622873cda3f88b65a454288633935a1addb590eda51d04548d0cfe976f2a8bab00c66f16775034b13ed9e9aa509a3ab4524c5fb58b305782cd345a4eaebd31076fb6fdace4916229def1c7cb8029da8eb939d63f4fad2000152b351edf71249f0b2d17eaa263f137a89ad82c5a94a9a388441bbf20f4f2a40694d54b500853cfb16d1982e2c94ba1e7b2e1c0365cf71c696b4d6c848b5e3240fb7a071f52a9ed934c56d2479fe0abd077eacd60bd4dbf16d451b6842411c9fdd3374184ea5215e4ad16c20da9cff2a005f3036617eeb33b50718bbccb46648ba5e2f6a16914d6f9ad2867f612b81d50cb9e5d63acc04891900a0e311ca91a9ea5d606838622777f96688a41a97c7fd095d36030ea05c5840a13ac48c9a6d45dcea7a1af0349f8981ac4b6e17c616e70d19f8a5e73454c56f498c8f2a29552c826c0066228a5008ad43bcf2395da2980ab23dcd8b49732ffbc7773004e1466c8b189040c3e7f24bcdf8ab8452df50699f1457bd4988ce941aa1e09d2cc8d30c99d8cf6ac6554f2a53f00842131021c14ba6ad314ddd5c912f12d7173401e16f04c0580875b3e2f45851d03f7296ca22ac7a4aefe6e919b5ae5c49f7c0d394b66b17f73ef2ca732b269e4c17324fad68ce4e6d4cf5339f92c9ec33a61afc76a573073b9c661845a32b661e746474c17fa574f194da3a76733f944acb529b7d0255ca7ae62d01b9b7405cf08e6d87f48277b60830e4fc70f893a247a080211ebe3f12e8e3465f4970f8a25adfd4531368b847d81a59200693ab4bf6bba2157ea81f2fa4a4aa8b672282035ff49e7511867a59b150da0cdd51e9ff4f52d4efbfd24b85086ef4994d6f0a495c1493bf36e8bc3a1a818f0cfdee61b623c248884acbfdf6c4500c074121a98e1682b0f7a1b366a72e5ba45101bbe0175e32e8b134bc5ed847435a7d06ce14023d7dcd0bbf2cb716b88404a286476b8419e888ee6beb0df853519b89ca7ff6c0366936caf064b1c5f1b2f8c09457d92ae1a8146142001bc60dfc05023f9127aba0f7a2b8dfe5e64ee53f29c31e3893a12ada442423e190911514cd040f2493563837a0aba541012651739564d2cc57b86a75661aac4a202eda38991f8e126cf74099a4222ed67d22b3e8a06baaac3b294d91379deb6fb5ec77c179b72631cd62460902ac8f34eaff4c40c199c0f6e75e68638724bded50f8aecd1183f4ff6d92455a4009857d2b41778faea65b53a570489eecf07d94b68013c3b296a59ccc39d12e420e02668a4873157e32de3645bfcd77663916e80bb8c92e8751f4a03a071f14efa11c01e42332838c8bd8d3c3aa7f10fa705f2ed537867f7101c6f82257a0b7552da28b4473ce54408706adc18fbbf7049ca5b4b511f4d1bd43438c1d6abb963db66cc378caad73431b1e2de595eda73bcaf5e2ff872fa6313c2bf673621b85ea366e315ca38520d318445b05b7c4585f40dd4beda6ff7847d50b6cc0b575d391bf8f083c291d288568bbccc0da89a001d5038900a4ca35f3978b4dfd4e064cc0945b2532c3e799ce52e36b46ee4cbd6f36b1a1a6da315d56a3b8948e87b6340333117ba62e80db55389517e5eff846210f3b77159c687b3f68bceb817181695fd23fe7470d719566c0c62e5cd69141c97c4f538d56420bda604900f48525179c7d4cd514206515d5722697c14c80674af95f37f6acffa2317ac376533e787db1009c49bb48f6acee3ac2370b2780630aa56862cec984b12d12abff05637521266f7da6ff97781e1c869f7298e320c31c30b296c064aeb5356b6d7d5ffec855bf7eea41e4bf1936b5de845e2975dd16a0dbfd44d16e8354ec76341f35ed6d7f795e4739b9eb7c8a8864d390286deedb7ebf8f97fa790713599f9890a2034044a77dbed0aa84b375589e644650632983e7a4e5d035c56ff29f2ce79181ef32bb67c888c23d85c9c360328cf5126c309aaa97f041f04bdd4c3be3efce04eba510afd55f7e9c2293300cbcf32d1d082dfbb7953b114517b3d2d16fb6f3a4b31295a96fef3606c1468a376fe36ec12cd93771112feb1a81588e040faced0ce8f08f3b3931211b40556d5e4b279831a0c0db6d3f612cb82e252c0487c6a115aa42983a06b41c2e8d05c44b5943163e80b2cfe5e70fbba3aa8633cdf268bc435cc422be60673819c46d75897712e1bf83ae2fe7c282eabc82e3a3588ff984eb3ee7afd448e789758a6f6776c4a27eb335c529d164741b4cd6a2a4c669fb64b7be6a7b83684be167ce376914fbeccbcc322577810241b0aae424b64580d86e1632be5d05d3a6b7ddecf61649b06b3ca4e44b0126fd6d1b87882acc47b88a7f95748efa7480c80c0a2f11a03d19d5e34b9d4aa29e482164df30021f1e272f7972b44aadaf7b9297af02f96a4eaf303a743829c675c4b71d777f6f0f4632914e31edef7a3226fc32af38816dd7e971f5c4a32170517f4941d766176a1014161807efc3a8a23e5e42a1d6eb8b1714835eed4de404eeca1349e3f077e09da66f80edfa9d8b82b15acc897ca200ec1d68948387bfbdc01e44b4e13a768dbb7e3102b7aaf6369f5f280a2d45e14bc35638d994c44c7c71b0006ab9097a1db7ce419150debfe9798ba93364c2dcfb0f909f71855f232360e6be38bc597790cda887e00278f5792fb53cc61773040782e728d0be49021ff68ba4b69f574b9d7d4c6184564d08850dc2788a90d14755538412278b9fc8b340a11ca64ab3edb14d95701c206ae1ee36d5d621e744d4394a99eb0002bc498d67e81faed6dc91c69f8f5089a68c8b01551edd80acf0437a7e2b6936387179b38e1fcc1d4ea94515abe0d3fded7b171ba5a72fea1fe1a1201947383f525cd41639ef787c5bcd22bff0982949d6ea7c152a990b94ba60c4fa0ea054102d1ebcae1d0da2af52b94424e229e2a69ccef43a11ce71ce7f8b19e05eeb1e03438afff407d122c93d430d8d15a5dc3c8d269fe104ffa09d0c7d6a1640ffd7aadcba9e5897d966f91efd580ee2a3757ea53955a5509b3d65bae22210e0c0bf9520f11295a53a4f573197218f5cb167b5cbc71b8ea2a3c3e3103dea21c81f6c0237150b7b75db481321fa39c116886ef957f7b328ae5f74bbddca7b6f5344fd09b1db1c69f820d503f49e6a7cb8810ea6ad87ff290511670a4738b6a0203b9eecdb116d4d80646f747c8d97ba311c0764581fdf98ac28c76210d4593d81b3d23be4a37ddbddee67e5470bacff2761551f00ef5d47aca184cbcecf6296137e0e37df8b38bb9187a6345336572a1cc34096771ae6a8698a68e5d18ae3d965cb178f347f606939b260e2acede0644bbee10189ef89abe5b0d7fca0d4020ee4dc8338d09b4d6e17ed2aa611683e6f5420f1da711cc5f5c9c86423e28253eda9832dd8db6233bd6e4e7c468f6cd7c1c03f8d53889e816f44629373553a1cdab5fdc48851c0d0b5c778784fa7e5c85a3e7e72d91815f6ae51423bb985ba596b1dd28ea32d9a9d344300bf54e75bd76bcc1d3762b57bd9ea6b90afc445764abd8fa4f3adeac6a892ccc91b7ae8b56ef599b0df8bb4e384470be7ca10f866194b55b90d12451406af1d447b14c8762f36b13e5ed937500a9463462e16ee2be628df16d1ff7855a8274010fc8e577c65a7e8cd6f2b66a4a2c848b5b6ef99ca7967ad81ebbb46ac16fffb7b235b008b1f89e20e396955e050c60086021833c3fce2d4e36b95a4d8faa574d76c6fd147a1e04702d3da269938fbc92185de559ee53dd0db0fb96eac950f01b39454317cf454fbc75ebd1d7190f5bdb394bf997386fe177aabb6f60b4d9a5b99d464f8c8b10fadd7fd029483e1562dd22da7874694d392103f92dd7ddbd4d89e804e6133495d48b0a240c97b9978362fcdb18ceeb8f39c64d4bfe4c58e6f7c7b69d73ca406102966e160743cd8be15649d6408e4aef42162eac6fffc0de44425cf5debe7de5534a3154e50e3a6a789d965242217c8b464480a4ac41050e96404913441f9b13611192773423fafdbaf6885252aa3b019bf35f325d3a5ee30295fa524e52ceb100b5df4bdc087e6ee439b2b2186581e0c7049abbd701e89fe00bda5c7c2795d30026b24a464b5e654bdb507e9c9c482a6654e759c998c8206b71f0a9627f59b0344f532792b55cb50f4f796c2b345b33940be328a27e1bccd1298d3ea569848652542a314acd926cc951e4229d1ddc4b2f728c58248f6258b153986ab68417fb008fd6d3a58e3a8d47249d5b055cac118afd30adba1a9e04107d04b5534c5cb77565ae04958ad52a709af8af0cff5ee8b715ce97c08f821627b17537d6008684f31de2a6c09c05655ed87d315a36394669f10dfb07d09f1008fd87721af0aaec924d82c8d246d20ef3f158aea12687d5c5f4d6cc1e4ecfab57b42e610cf870a9433781468f94fe50171b35da3fa1cf93e5a3ab3e8faebddfb1d566becaa841e0980581981fa32c9f8e6db4dd169d66052cb9d4925373727953a7fb8f19b14d0bbe703c93588e3ff35054398b27302b087d03fae6d92bcd29596840ce942a8edab1fcf4be58f3e3db8b52d91b243a8620390ae971908242bf7fbd5360faeb19c70baca3e96f3a8497acc08cf6df4992888edc6c67244a065169c118df4dcc6be1d44068c39e26693fb64f8df61f56c6c079e551f4ba93fe60842be5f61e015a21e43884f6caee37f962d3540c8d0bba43112d012f6d567f98d9fccdf602d123a249270ce2f59eae36b2a1f6bfcfbca9d9f7f44b13f70612366125fd13472b1a63708cd7477e3eea693d7e4eab06f3b971e2e519b2220189c406680668f9e4be10558ff819b58bc65fdc7587de86440f1623c3bc5baee3efed42207283b67a2e2f0f4f705de34ffdcff4fc5903d9aa358c3af4ded11743fbe071db7bd98bb9ce40f9d18300be2af0bc2341437e4b240728bfc42d66fea9e9ca77024c2bb671dd142047885293192ff78f48fa582763f3428cab6d6fe9770e90833b1480c914f88d0d77ed73aa3cabd85e101a9d2e8f109ef134893b4c840c15c06298c97d590831b28be4cf20710f89d3bbb012ac1459616d50b2b4c221b7f363283ddaecc9690e66a419c52fd5e84c228681300844ba7925655bf98d010006feb986361864ac1cbedc63a4139ad3a031f8181a0981e6a182dc7ab6cff79496b8aa4aaecb2b209f2ade21197413dbe7c7fb9a5eff83807657af64416300935704677b44408a6415d523fd027dcdee8a19bf969c0cebb8a776348c7bb627022a84d873bda054c5a23c1ecf3224ab2719fbb9740e6fda9626cf229d4ee323809d66c30e1fc9a3bb683c0494ef7f95fb2b060258a7218056d589be600bd8fe28a151581751915c782674e6ffb623294023b30bfe1d5f9ed3ea15c0d94fe2527148a4b1fc5458522480363b82d0aa9334a239eaebffa5eee83c0175c09fadf69180ebbda91cfb6d3c67effd3a7db74d6f53fac42699e8ed4024b1e85e3ec528ce53c772d9e483b249c64757696a3a389ab7562028a671ec60ec8329305ec4a2456d9eed294f0146aece6a59b79713c3db73ea49bcf0722ff33eb034147ffc5a175387e5032a4451d9947ed8652fc01f44e7f967de58c7c94f525712e92286d57397a9b19f63419089c836b68ae9e0004c2eda55dd4c82c4859d3ece3e6ade01a78b614ac3fa4ce12fefb52a0f552221b57a08184f021f71456ce7cb3371d6d94cb69e04f2ade483c7091360807caeff8e204a482e4181d2011410c78ebbeff126703844f5ad32e4aeca8b4eb4e3f305b8106bda8bc873773b81e7941168834604a42571596ea245852fab2bb293819247f52353bd9ab2450510f710eb540cfee3b50952eac8f0ccd3ded4089d4e2603691e8658832932946beb7c4a02d4db161cb8752858a2ef236c78f807a5cebc38b05435b6b3c5616304469a9c9fd245612bdd066308232e09a94485708497472ef2498e0e875d6707a68e35ab7f4a22633cd4b8f69f1128ffdca5c0624375e6e8003c0191e48d08f0cb659b0d08f7ab0950aa1e20c897c561a85630a3e2b18f8d37bb1f2a756ff8c927198d2d24584099fbbdaabfaac20274306087bc629269af20666dd7f3524c1bdfb8a1e49541d1df30b53459ce64e6cff92050837fd4fd93cab47c80e3b431b941267eb4cec6720534528d3628a6360f53144610fc4aed0fe1f931a7ecc9a39c2cb7ca603651d14c1c2aca9bf0c61069486d708ae2882f931f39ce619a5e70cc87e6368cf38ed97131c1145c8492ddef5676bd56a0cf659716638c7e7e8df1f1ea5047a45f8e5bb1b6045085504f13f518d410c7892e1a36909c9034fbaf63db1e0896337418c11c686d71d142bf4b445ceea2daaa91fc95fa69610f6a1950f02ac5db4582b9e472fc78f5eb896880b4a74e762342b0145927d6b5f9a6e43c6683226958b02e0fbbec4f0f3c96353da961fcfd1c39f9c368c2b0cc4797cca794037f9bc06da7db7bac34d203a9c06788649db16d1bff2077ecc6220bca84de9b2c183159f0ee2b5280ba525bfff3df986157965284d1890b3287ddbc664736a5ca7eb25e2afee50a940fb7e3a882586f6677914fa48ed865154753c7c524d63b785d374d4f53bac436b9c1760f55723be6d3f9d46604cdf32bf76160427b338587d8c0f46e06d367530f2ab4bf0b2e69b446f177bc75da7185d47f5dcf6ddee7350275e65fe0d8470bc5623a3ad6a0d0902bc21b06dd8e2a3abf905cd410de32925bc716a3d86267c4c04115a21a0d20dbaa7d3e96df04d8c7598f0e8fa15d2b742657e1b6fcd4fe14d2fc39f9aa6219ded46e4d3fd876e02e4aa7e41f148cb7181b6168123b44401b1188c7ebe7271d44ecf7c42c8b6eb529970e9557c202b4a553411367ddc656f85a06cb8214a4b20f7e934128af6dba4fcb8c23dd2a5bb15385262aee30602d13390e5f7af58d0bdb01366b3675cdde503164b9a75975b1d96addf1bf02bf2be712c259b7f3c498e77cf011663fe9e65bc98eed0a458490d2e621fe1cd87008a190b09c2f50e68e5167de078331734608aa5a3efe994d0b832370f99bf3e3a67a894707f98a2faec0dd8cd48a24d98f059a23774f793bd33bb9f79972a0059bb340a6489185a0db6c74e76dfc0e08e0621e56143b0e0f1a6c2298c1d76a3114510528f2e08d901b68d07772067170a05718fde10d2476743bc06d8e34a317ab2f4bfa927a1507e1ea18ac89de1e33d2c3f6da054d2c3ffe0291dda8851f1019d864128ced7bada9239d9f0afcd9b753e2c442eca4bd223bdcc490b42dee88b6f63edf4c94a4505bf8da54c080402746b217832c831e3d587e0256be3a82a3a9c2906f22298e1e395d5cb4131c5ba91657135918464e1e18386f3cac20591d5f9b8d1f0e89904fd163f3fcb631a1cd77bc082e6599a3f2a4f4c8e26954c7600a3c504fc05f7d167290b1774faaf86c05394b553805b7c4d70865c09230a43aafecfb1272fd4284419521b5319b03a598629a005e26c8fae3a9eeea510c3d761b0f53cfe1b4061d42431f48c474b70475b4abff4b99820163596fb38a4d8af197ba6832b606c1c939608cf1db118e3cea5670e4d1e0fb2471cdae58a626f4038a684686c68ae64a9f2bd74759f0f6f91ae0add030864421f0afc8def56f58f3131ebb02e99bd18327ceb0c3e7ca22c08296a5318ba31a0868a3e80bd4ef9ef8f5a63f2951efc71bce656b1f891b766f0adf619698aff31d5d11f02ce4e93022296c7abba33da3e83aecd24d791d24642ce0f88fb6e8c8286ffb887d22452b4fc262d5a6e915c72cd94c424cc98771194ba74587d8984f42dd0f5d2cc1a7e4b32d7ab0893de63ba3a20e54b266352b800dc2de80fbdf33598a68cb54ab9f38722c78ae0b425ea6ff031fd51e7fd0b27dab233ebe41e1b892f606ceabe49ba1cbc6edc9d2fb98f7b57e78297d3d6e086af4466bedacb4ca09776a12ec2b6949265a49bd4b6ddce37d9bb20aaf44b7e1284f9c4f5276ff8aacb3453fc2d2ba51c9a8e0c50e4fdc278ea5d839140e527de30726e8089e3c0c5c9f7d4b3887ecc5618b53da00b078a5e2bcb0c50884356eeb5594a1c595c8f74fcce6632446fab8dbd306c6d2aa59f99a01a35126919bcae6cd7a825a2cda0b3c2e542f41155747998f13e54c38f75d9e281205c4796e2ed31a08154c99653bafaf9d7502c69dbd0d3c3a740f3ddcd3b3e5f2786ce0c42f10d8e951c2d6e255c02157a94093cf4c4619b6651c44f3046cd0a2bd31ce0bd40f8cb2151dab3e13ff7acf1d2c3b82198abfbb861422461d3066cafbfe77cc489a52d3981f62a5c766ff6c2e29f3349c48c1c0ce748e2255e85d70c4161cad2c8acd3528889158ca05248b5e30d933badb774cc585c0a187a4b5424c9b15a322e0c5a6320ddd445eab8881fc7e34a7d751bf20a0a4f8fa6c05b55ac0e7eba2cfea298f5b3939383c24ead1a2c83bb5346958a5fce57c0e299b4fa4fa5601c06b96cba86185dd0bc115e215c7c7c9fc1d78d242f5cba099283619e7cf8aff8ee5f82ddcbbca6d33b869528fa4e3e598ca5d14b6251ae4ee0dfdab080d825abd6d08b91c28cc34cad010a3e6867c87c0cd14d00f2546d75e027765f88ac9e3dbde2858920ec6c6575ea103006f19955c3ac3cb5a8d41d33b8475bf17f05853d1b8c09a1b25784a66307fe7289388ab84bc438a46262f1aece39e3ca57bb1b2abf550d063334a1546812ea9d6c8a205e0e45b6ed2f6e115819a419c9578b1f2d1afe888c4b79b666937ec56d2b3144bd26adf100e41172bdaa1eedb2df12ad8006a66659574c006c344702e24b54fe09a3fd7d2a8a87ee3c55161467c80e26554455d2a0f0e4a4910c4bdecca2485707658734a6fd64483de1a901caa2984be1d108d66a9b5d04f6c2326b6cf8511ffd7a049959e3383604068a3e375f950850e356f5d2573e5f77a4ba2c04d04ea1b20eda36b88ea044f3706abbd37840b5f3787ef14094179129b54c283393fac80db3fd317a2b8f553f6459a7419d8b158589f04c5c70260255a1425be6a2de5dacd88961fd5ce8f49f278627b4831eeb3a6d735688b7a34124f935e03c60714db3ae3fcfdad8c8dc6ba4ac3cb206fbb7c4cd9310bfc0786f1b5e815b5360c49ee7e8d26f933823f05675343d7b9d53e90602b2aac9034a94c55c2e3210a6a9b6344e6d2ee78dc77e31fe38ce639f862654488cf0bb548eb32fc4be9631583c6440edc3327843cd80c96d27cca7cce4094cb60e3a701f845b02e9c91fd189dd9ea29c9e1e832d15084b59fb9bb9b213a02b70cd81c4328748b9a8893cd0bb1f845845ee25b9c1854f0166512cad266bf874687e298b8f42785c2ea1014d25e3b6565a9116941be22b8cf919327970bd956cdcd3dfd226c80e573079116ca1b223adca1264ab3962ddb96d0693942c0f33dedb2c9a662b9e3d0577c2f5b220892cf439be7c91b9fc89c270bd4f781bda9c1b97a8d5e650afa681abd0bbbd8c4c49641c0912a1a558a51243ff8a13fd7a1e7d0090ecca7eca19726b082de1cbf65a747b14a77889e1e998d769fa068f6341dfc433f85a41a086898f67007a9579ba3c29b5a899892221078faae6fcea923cf067a7d4484c1e2a412fd7ca898cb1eac519c3e00cbd8721b4ebb99dee40857a36c4db9ca470f830d13b5893788356ad0da70be61b0cee21197c6e74555fb5dfda90320a4284ab13daf28b1517552f50773421aaea469b2233b54bb2e7a78f526cfcc56c4d6040394b9ff8fdd1640d3b2bdfa797e66c22d2b29a155a2c25037ca0961dde875ee5f547602c66c609e8f6e53138cf986d80c78d0a5e8117ac743617797dd99970920daf81ab550f6e0a4f1caa0fbe1ac3f52733169a8c3a4bb8d684958a8c5029be4e821443a8f4145a04993ac4e20948642a2f35b6bd54d477f704a8ff3c7417b142b2b2e8591a6c0cec5c2de996ccb2c042d81e137e5b8c945e4cc536e678b11cd5c32be80e86d2f1ef04c897dca657ab54e4e4defdc425742c0dfef9383cb34c07b4ee36c5bdbeea8b84b0d25f58a3593b61d9a80eac846d89e50d9518ceec09ebfe11ac102547149125790c512d72ad759a36de382e7843f62758ea980317358daccb191184678632b5b5f1bac28e31ba1ac35605bdecbd9940ef1c7f252e73736057c43dc3cf543f284a4252f5fec770561443c5da8fe24e2481dca7fd0d38770cdc8299ea63a3066033626d8ad96b5393541a5b873aab6b5d53f8e911d02469ffa36e8c19df658716d0bf2500f3513811b7613eeb49488d02bb490858e2e017b17c4156be46083bb2b4e77d57cd1a716d0c249cc14ee368cc830c5698020ba66d958a59d0c1ace285f2072852158d12a856e9f31df085ba114c9a721e81447a0e2b93744e94117352b29283f8b541a1bd92f38221b339b3cefe0855819315b104d4cce7cc5881ba176991c26f5b3fb845416b30e7a3b2bdc139da18904c0161c42ee339cddaed9e88933bbf4c101925da4cad0ff333ee312e0decd57242d5f2e6655aa4b6afbe36b2ac9914e7bec2dbe0f8cc107113b5576f3c8a4ff7431110e59b26352e81c378118fbd9f70c1da569b62c64c8c1fe47da2deb45d10192a234d5089fc44406ad98596d915e87864573f2ff3f8484888c8fc5bda034b998a2b71450891b70c2257aff1a81f5ccc61aed5099702ec7b60f12985228763492c18322534ab3afb11105febb0a3f394a5b1e19540ec401127a76b16707273e39e80888231e373ec35a71c711626beb21d2bb07e413926454ad3d4cd8dc9381fff122a73fdb05d54b0b02e4aacf22321e2ff19a7e6bb9015f6293708a953d7c16b245a55e17c946cf08476fe4a0899f6dab3c14f7a6b284cea90375b9f3b7724dc781498297a398cbe274eebb29077002e863af2cba0c82e31562ac05ad5dc2c86331251ed000a1c830984e3c777df04c6dc178ad935e7683a4ad43294d01f6edb9e1797fa15c9191f88f6185992dec6ba7ff961be0c6400014790b17d50850406bcb019b0d0eb628f40a3b4366d68d7fb4d0cc4aa706bfc3c8f4030915b645af3957f1b469189de2cecb86f5236e225db0696937b7d3d56216548358530a994fa7fdf2276ecbebdcc42493b4fcd2fa5c71d9d6f166aa0b225b37e8031f67da4fc222164e2ba52deba42aba3566fe4a6105214376d70d1751f284ca131d213cd879792fd7ff1f2e9b1aa9f896b8924ef7a8e45dcd8fd97b9e92032850cbc3ff76d330dc40b45d46bb3543bf69f051c598e303b568a0e1e53a2c5542e8ca32f4336011e36a9805ea5fd446744a4e5ea5f55bb7a773899e43ea13aaedc9b53bc1a1073c3fa01dd01da8830228726146c8de594f386a4c920009602807bce027bbab820c95335269406bf82e703fac04638089b38fb446ea734d1d178fde7f1c4e940d2b52a8e68145445cc38fd55968e9fa3d8feb9b676d080750fa7b698947971088f12aad99ddcf41f1a5c93ab9ac679edfe8850c6ea425ac39c72ce2b5b814bb3adfb2f347c3e54fe9adccb4411f083338eddfde64ba2012d45a5058ff1255163dd71e4fa4ae4da46faaba14297ea9efd2e6baa41a1edb9508e3b67e3bc6e8975c0a29a2fb28fba630cad14773154d2b1abba50cbf7ea2108d8e0a8ed1593cb19c506dcb42ee260114e6bdf8821146933cf7f5e3272b13dafda40c0169a98218d85eb79672bb1b2e1cd0853262e944ebd01d21602514a05e342248d4b08e22eb43f8546b4dc430cf8a8450393ecb1a31b9d0dd8a2a61da570f94897f5beba7a462c9f5367805e043f235782e7e537c70eb4531f3d3c3f1a39be4fdaed6ead26b1ed761e539c2230655b14eebc7541b234a14370994e7d6c78af4d41ba467807ce2218c4d82fc1087a332d35d99b1176ef3696835e2e40cb4c6b7ebe60edb414cc331e8be095a3ded7e6bf81c5d0dcedda76d04c2aa7a1a0688db4fa6aa98f3c45d61210d974c0046fcb5d11904094c6700b73d1c48cc20eef16ca8dbbc0181e88d532d18ce1a2f74b5296c1c58ce32491535290fe453d41413d9449d0cc5fcaa79b88d8423569cccfbafa3f502d44b056b689911612580699422ccc95022fd7ab5919c285284bfecbff3a04e50b7614d971b542b4c4dff36a5de5de251090899c8a99b8b2ae10121dc2224a5ae9e0e84338545802c060681e0ce5893465ee1d2f38a883f85865d4b43c2a240dde6857ed476803c60dbf578d8dd1fd2f32172a9d02b3f94c95ae5f2cdf2b065452615b8d343d91a915516b930730a58147428dfa48064e4a99fe91d550bdd03812a92333e4b3a78649d1f69b484fcca9a5371c6367682e864194a4ca01d90eb79ceb9a35e34e9a58adbc528268d2e1580fc197a89b6b03086588fd187cc073898f437bbc669cb128e7b7cdfff8544cf517f2b833cbe1233a9aedc78a7b9f446d3530d1decceb6e3c1e8f5d6419cf025d289d75badd0f3e18987558f50b526cafbaf35a6d913aea65b16db180a0cde4c0ba6b463730267efd591483f515730fdd8c224950870f1049d3a0e0f1a4c4d2efc3f125466c1b318c394d0f134d919c289fb21801438fb85ee4eac5a561d20cddb2c176733a4b502370d1ea802202948f44349da347a7280f466c96c5242756b710a85d2e88f0251ef70031e3e85342a0a960badb1a693dedda6da76e02d2c0993235a9367a14e38902903663f6b3bc7033108de9c170f68418c40a3f3b9c90254f7f4372858715776d3905f7e2cc4db9dc83f2c9242f4d73e5cca68273ea8a5e9048cfb50091944f7987974b0d8d9a89fa6c8e81456c4f5e1240e889f36a26f526b753ff549a7b483c486407d719eee4855cf8e3d6da9b7c4ad92e2cac7612ef1ff41f5264711a8a797d6d424921319638aa1652a77a4c109a607182f9a0b22b2baff5bde75c20b990697cd60068bedf5463181ca2f820fba50599e12eaa09f97cea7f7768f1526c4dd39c5158167182c1fe9b82373865a29ae35e1d76956ba68ee21428da18121c268ddc9d38a2364d695a13250d139ac47f5619fb8afcf04fda0ad034cd5971e1d06038e18f431e4c07c3953f792d40e26c10cdfa523be712bbb263cf089c353df708a60d1e13ca5ad964ddc07e979b15b208471676a0bccce2238b2d5e6710a175d31eec4688a80c66056ff03fcd10fc64244551556fc2059a7c5058d4124fd39a2cb493a13309f1e49d3200f6e0436e0fca00fab147443b7fde8158bff75a24eb7c0acfb4cd047acbab1735b61a0eb6d61fd81f9e606a0cbf94a624f30649adacf1c742fafc048c25c8e1c08543b105bd7f576b6d38834673d34abd69007f76c311c4e902bf80464f914ae5acea4a4fdf20c1a67a41e6153e3c87ce422ad85c3595663c108bbac3323f791d275387bcfab21666c357078436d9a9f9ed2208320f0cb6322c32125ea1673579a3eaa76a8f0e726f9bf2c0bea7abf25aeef491fe22e674ecbce9a7831453771a1614cb67187d419a1ecbf8ffb5c108b085d29867d7e9846d4eb6653d4d99dffe46352533ea85e793fd39386e312ce8fb5cfda468903db37e52c3f3281d889acd0f2df8afcd19bc29b4d8aec5f156941432d679804700c38d9c07941fd17d5b43139f192effe95247eba61647b77caac4fa21584ef124b0bc24dbcac1adb18bbbe264dabaf339650dfd09f5c0f647915c15be98b165a8007e345274b0ae5a72883141e56722762750415c47d6f36ce0b0da24ceb7274ca6e1be5ce260322b2a698ba737bf7050fbebabf344a09f1cbdce722e7f566af567dccb030fcd51c9f667ce744f900b22ff84fbd3e87dac73e0035517eb2668a14726758cbd9710c944b9d51502442be1db150591dbe1b078e3d7dc020030b32feeb27c281600c928b3a36fb04ed2d077d4dab56e97e9c2707a14553b158d1692640ee8345c3fb2fea77d25e4a10ff8c2cf10df133e888d6e9f23f14d0aaa2bb7731cd578bc5bf541232736022dd9d0c7530b13059465556cdd0e5a4fcbf2bb92021e21ce9f4da86dc3a93f3eaca39dec791e11a7e6ca4f37594f36b1619221e90b7df84ddc509b920a610b61095788677c95f09b4867f988e7e3b10405f19ec6f21aa120c26cba98f05faa090a50d14288d5c6542fedc914db46f093dd0df6c61bdcfc5dea94ec72e647af7951e8e884701024296417074d71daf1f269bb7f6f077f76a3bd82f272c6849719cf20ef8b0bf9bbf56f7ce92ca2c17f0a217082387668bcfb0cf272312155d8a28a45d3fc9edd1e733fdfe1168a96657cce44b4f891be357f75dc98f826b4d4590925efaac589ded1edde8a25c4021928b612cbfd7c1bf46a7ac8f8813818112eee90017cd8a2d46436b6ff39e169a8869f69773f653ecc2032f57f0725745c9b986ee452a7f064284628f32599d3a88c429bd34edd4a489b5334f1f4dcfea84badfdf5311ed0cfb48d2bf4a9535b3c4a5b648e8085896783845bcd0f53bb3eee625082e383883a4d0ea9b3912c088eebde91def124e4cc76f808787156a62fd041cccd82c79c2d1ba2401661d22787125542e19655782b635b1a6f99cf2503d7f5fda5ea6e2a73b6bd5d291fd5e916418aa11fa78c2c68cb6036805224e562df0ac5ea32d3972450f5bddde9f68b7087328f846fb8b6f81b2ffc41fa3e1ff6e3e52a2ba4fc56eb8904b832a4dd51a31799e4aed56acde29f080e65f6388dafc04e0929ef018234fe9183c41b0d41694070ee547e18e77758a574b98518e1b5fc037d11a307ffc2af06c03dd84d68cafd7129c1dc855b0d829fc163d15bfa82fb13a929e3223e52b0a8b46e29e0a57618064d62d5bb301258ff190132728490fbb4aa5a45e741eead75352ad2bce7dcce531e20b9e5cd2c3390f1d2517139fff5d6b422e383ce9457571c8a1732fc160e02aa97816230adae105acac57345bb50959dc3f1635194c467068c4c05d4aee649594727181f8d8492e73862f5cd477f5e1823532ea548c301e85d372a72c968620546494e902e25d7922ba2029b7177cb8296b58e8dfcd16662e0d878c5b33c769df460822a6dc6e9a63612c2d439aee9bcb48bd332f99d1df7314a01f25e35cde0d85e89150e2039b0ad69fda9642c5095cc09f7d8ed98bd01caaac4bdbdb18e4c8deb0ad1b632198053546057cacdeee19b503e455d424c111852c65b1cd87dc726b9c5e6218ce5340d446150fd3e2317479120f978ee9bf60420033c040dc3d667e92885c6ddf33763c4dd15164c7803158194ce2194e88e94be3548225ddc1bf29625cc0eaf498d97ea822cf1dd7e31e67ba4d51a94dc327d85c4cfbf17340b8d99b73248d3c3cdc82442b338271a0fa01897b4489cc5a2defc5b8bcc00842c9cb252a60a03588e1294016b3da55fe866df3509cc94dccf0a410ce99e40afa883971bef29944c619217975196ac4a78166343d899bc0e76f87842695320178999c0ebd8a241f39fefafc583f2255973ce4c27776c36dd1bd7d78d1105686950561e9dca5d172ef96908c6ad7ec6f563fe314eead35309ccc54f983a0b06aa39f68ff588a982c3e1b23b03d4a7732a8324e2ee50ff43f98b3158373e9b470794c9b9bc1704340c05ed490fd03050e0a1f91a2d4831d4db04ae16db5b0117f6e5d81a463f52230ff8bf52e6797281089b4015a381fcdf019783c291b55965fee711f1437089a28d338722e070e8e3e71909b1196ace829e45fad7365dd8270f4d4e6a327725a1635b33b8c4a6734da08322cd7397cc6749342883c7ff136f0932286e41a5120fb12c2d1d18b23500c608bda0029e41bc0d6a038452a12d34641c28c6d42193ba12bebea464eb1fc5a00ba6df921f415ada821f2c65d87a254dda1f4a0fb3c9448e3e8fc4fa2e8f39ad8d20a91f7bb65f7c4ef62417feb761913fd68860b20978da8e4133a3ceb95e2388ad1a5bd10388a795d9ae8ed3cb979763f42b912a411cbaf22842607bd27417fb7a200e9f61133d1b4402183267f99bc0369f42580d9e90819d762e5d5b5ad0182cc1acd4f243f5fa1de20bcb921d5845667c453ad3649be0316e54d17adc98bea1bd4fc46a303c2ad18d1a81f721512be64fbadd6ae367c52d6c91f663cbc41ff79aef786ca552ba820e01280617a83059db9430f2cf0e9550dbc890f0af1309cead5e0f209b0a329b5f315a0c8df0408780d40f1c876dd6d7d973ad67818a0734da102ac34aea624e3bd6a54815e895cfe6f32a9f460d07b186c59cbe7b0ffa7b91aad31ba977733b4f2fc3bdb758fcb9c31eda20f69c346368bf1713100dad1932d0cbea5cd8ec141e68b5dd5e8c5d1205eb830dc4a78fabc0b80ab1698ce5fbc7e96abbb2a629b74db6488ff4559050c79b54da564e5305e21b10f799cf54b5e347b432ac60021ba63e53d0ada9e384dc2cac71afc203e2f180773c5a510030765143a7319a2bcd86513238227c10ab23ad11fa8f5628e27a555d548309017f61a685ba17ea7f5add012607a1e9ba3ce8f4999a6f12526628f7ae8ce0e709867e7c550d026ba9728a9d71a31cc110cc202d9dd4a61f091cb7852730fcb24b491e079198a4913b26c2b0f32deba055cbaba8b184b8f4f5a90900e8942e464ae1e00b52ac29f3e40b7704d9d074ed8dc368e08d46954a53ce64e0f3d99daeecf4af88acbfccb9a7441958494d18cd0e3f3f2bb704815b1c595937852a7a26c058665297d195f41b1cf3fa17a3e7f66213741450116e043025b6a56c1fb837c641b00cfc259b17f47b367be1fb6949582a0a0f8631f2bb1fa63f3b6f089b3fae8acf289451ec8bc2f7c832b5c29e415efe905d3aa3527b5761b4473b480e00e244d990100e3e88632491ee5d7bbdfcd69adc481a15098e42cf36694f5efe0882b005c5a41903f0f7f905296c1d3787e00282f164c2aaab966a788770263f095adfb748fc416d63043e45fe3aebf159352364de4c8e6275039b1d6a2e124280a03f5e6d26529d855f7aec10f8ce40afa49054606028dc465c9af853b3145dd61d875a0958a521a18a55f6d286e60d160b83f2cdc27b4235e650714ad9c7d00e0c347e8071a1927b880596e745354b28e82ffacfffc3a59a006e236586cbc74f1fe1d3cbece560e7ed8fd1fbf98f0313d3c1c0d9d34115e38d07e831a8f87f01b695cfa39ec8c3ca4908de6abad71b9e3b9c9c24e05358589708d413934ab1bac654ed9e05c96f3ee89b8070c022d6d61a6f7c55592e8c7565323c1bb0fc3e30781b485090d0757decc9ac2b3d09a1894aa213b74d18b588cf2f935528e8053a94f940fed7a03042a109eb702513fd9b57d467c1a27f27d9586d09c337e34fff214cd8cdc6449dc220d581ae25e2990d8e407a66026338b64a1842d57c292d466e305ecbb87580584136b28b3b8b95f573e723498e2a4b4313e0fc3754fc9b31bdbe7e1237f30aba8b6c72b66f3cd4bcbe2e01ed602c88bf20da00410fee28c37def869f5d06563cb232bd8d245f06db57deed11d1f59849f6b2ec8cb075e4b9c76c48b8ed47a1818884d306f2734aa8389c5cd28fa0dde97c13cec3c18cfad30cccb11ed9e213c79b95124ff1297b308cde9ab4ca097a0fad72e480b95d06784e572752ec1d31e0297326dad5725a8c0de32ccb4ba5d6f2af6f0e0aad26e0df52a25eb01b054c8e62e68081bb606633821f73d84c2d02df1dbbe98a22d453113448873f34e875d329e502a743acdd0e6f3f492f5874dab25d3c9a13b9237ffcaafbed1e071443c3b9eaee983586663964538018b2e12053a147584fb5fdbf5e51bea540c912f75cc332acb2884c1a3cd192b5026abe19e01724d8c9429b6450c14f4dbe59556c33d4e324345c14af86cf84c17bc26d1971752687f1f535065cdca38b7ca3a9b9ff6986b4f4335f0d1bdcaf6bba96482bb56384b727f5ea82b01ab96f86ce556012c03f06e4fac0e551a417dd43e9488869d55929df97404c29da98ac4c1d4952b0b25294a3ad89faad5438026399620ce36103497885a595b3bd4a0d526a04480dddcb233f07464ccd0b1e362a01365516deecde5ee9e28b329e2179504515b1930b6107a437a3d65a866f206708c879d88682c74b673f2f1dd99d24908c68604ffd81056a82a2b315b8abdc828a735b3e5a6e3dfe66e1917a628017a0f2856fe3002574b3c5d46d5f5b48a971a9063f1ae592d4f456f7411544c103d578b3d82be3a40c90fabead4c326b2e359ef86d07bcb7fa31ffa43b6bc96dc750aa05c4e7b3b4a4448457d806f9c8df1a5b0f53833425ae384e48bf565ebb45d119414456350dc8bf033dcc31663b2e0fdf523d8406bafa2010b2002b82823f15af74a3ad7c4c3e87fa4e03b63ba54465d5af2ee74988cbb32ee29365ff2083e21d85da37efc84024e6b51b541f8103a3f4eab92172f527d12360c74691acd3e77f1106849b51ff7f19a932900b531201875b719acc448b6b792741020ab055262581e78213a028d13c3d901123aa2c8ab098271374096ce0cd3ce108ee55f5e145c1f2a60157851359b7e5308ef576f08e3e9227233e868220ccfdf6ede96b6344f3541a141d1044791d8f08c30773a50df496cd3c4ef93e014870475091b88e36373e984e89994c5641430ba57bd7553582bc42ab21b88e179354b733c2e6ccac454b283ef2f350e02b0cd887fcb8bd9e8cdc70ccbe8024598fc03ead9a6019e866a41590211ea6a1efedbb79433e6020d83d6f690624e7ed401d6e90384a1e85d993cea9f83c199e16e462be0f383150186cb789dbe08f7999985d028b0b328708fb49bac9309f99266a1977b450abf80e5e59990f0690ebe02916cb5b2817982e40bb403afa0cba3a58f81ec7827f61484fdcf27d1169e597f2252c2188ddb984c8f4f0e8514f1fd90aff02e878401efe45b92dfb6ad4a317454776067fba0ed215a01e9238790280f33bc782272746de898dcdd85436a30814318ea9410618f35cc4877c7cad245548eee207d8225919594a024b8afba397e6d689bf4c15e87501e923e810d234799b415bc869a13fe4b1fa4deb99234ac7f561894783a90e447654df6faf1d98a9877f61a148aa1b2ba37d2624babcc86cd63497393681e70c67bcc959407ccc0600dc1f20f8770953a4f87a539206941ca30f24cb7e9d6062f8ebac08d840688818dc5ca9f98054a25af61310817e05765198eeb76e7c86487bca056f582fd1a39671f70657f1f59ba5622000e5443f6a3c442eb4315b6787451321f0e5efca5d19bdf9d0897b2e71d4c2a7095c2a13bd7591e1f55e7614d9deda69170ed7d1111f2c6216d41e8ed050bee96c7cfdac1318086c2915b955bec2821441269a2ed0cb5b114a82a416a5e61e2a3aac6a16af8a4854c242cde70c27ac553f4e939288e5e85c7a1ff319e5ad040750a69ecd917cc87418fea409359f757f46a28160fa2b457e99fdfdfa8ea7070adfac3eb5edd733c55e929ac8e364470da2ce336b8db6f55f3276d9da172c8a1f397bf59640307c2679060b7a183a6c1f4f1a5ecd19bc74c82cb7883ac9baad7a4c11cae903186766c33632bd780681f34bc6991c0233310b9e8ef70f6a946bd1186ad330b1668bebdd19cf5861fb06f4078f5fdc8df7d4893f208ed8be7a0050a5fd52f76337581ad61b9a967105e5cea8336efb52cdb3127e0d3152d4d1df52a94cf12a23076da06c5f63c9e725a5c23fdbced24fc4c8af680a56196dac59d8f2095124c0ee825b54ffc3348b5587e075a6fd6aad72b19a40aee821deb17be888fb4c0b3eedc467dc76481e01a506422bc1f2d977a98160923d3a75300934132e2184b80ba5d93b93fa5fac96db9eb4e43057bed1654203c2f3f2baf095fd335e970772e52e69418d363974d82829674ba0a3c064fa407695fcf82f1e691b6297df41d5312b8e67adbf4b2404e9b66e747cd3aa25abb9243111ee1f82fe560dbb4bbce29d9689f5f6da7e59f96dc046a7f7d85633b7210e684a767bfadc5ccd200f5649d0985229296971d5acad8fffdc2c1afe5b8627a8437e16f754dcda395d1f49ab7189988e3bd9fd26618659a50ac43b1bae60fc304e0c02ae1868cf7834bdaee9a5c7fd0cc6096eea06d17e85ec997dab35352eb77af98236451ca72018a5aab42ae11b02fe99ed0bab03222c82022e1bc0b2b0267f7572e3f8c4f6038e82642b023feafb046d7fd3c116be8fd7da587881c0ffc5ee8796613ddf4f82df91398587a629961ffd65da28963ed2eb25fe33962bb4b86b97849f434d415a21b473e6f0c9257ece9bd99ccc9cbc2c04da63a093a1cd6840617407ee5ad5a84dd4296d09ed2c52ac376e4de4303a4e3bc4aed34aa1aab836386f7a6658c3494b9f2dced9d9a96186b89c889d20a985266bb8aeed13de92a03e903cf33d54597003e49fb427172f988ce4aefff334f84f213eb76a2ca398b8c1135fa2d70f74f5e52a695f9d974c3dd69f8c473f5094dad7fc92e092cb212374182639e71cf99ad2fd4c9b97dacfe3e367ac6c5438de6169c7cd4dec5551bfb1e06be18cfa45b146b08bd3f47a6f105e54496f5301cc358aebb03d6ea5f7919a293cc41fa6c4305a75379eeee9080c2b402cfdeded5802e1c7d0e3dae484279350e1fadb6206e12b8c2be0041666ccfa3a927bc0267371cc849cacefc73b5ad89295708113ec15cf83efc0e76ee14d252ce68b2072b25b8814c2533c9b387e344dbaa3aed98fa6e886061ba3afaa42c566ea84e61d7ee0a9e1c3dc5eb74a5d818ac0356675715d48b9492ea2f7f18be5317227d7e0bd8163eb2cd7cf018379712ba3e23d47afb61c270c7c4cd25321da3669df270526dcff1a489fe1db9f313f8abf0e3dec5e7ebf5b01693499b011fa95eee93e3b471506597d4f4ff37e0d2bc9f1dcfe9b259baacc350f4d6ed4bf7b891f6d5500441bb3aa3a9fe7c8ba49faece7be1489c463664797ce8e279a4464162c22706888345828fe810774e4f6b38885810a1146e84e845bdac08a3eb5f35fb9916c73b0b1ef4e3e4f4948d3272169977558048fd795f11c6519b8b63e2d05d354d24fed29f7be3fe302cda5e24c7a52ea7d27b4f7988375d48b66d4dd0fa5da3efc27b562ac95d895ce51ffa7715566af1cd69f7336572ba4a7c5280a15f3373635aae65f5201b6b676c86dc124d85f164dc759da2d5fa4efbbbc5dd1c4f91413854a15876e3f748d2715f98068581421b25f8ae29ad78c69c4416895bdca8c46e03aca97be88e2ae480449c2640df963708162b07b6200297c07c29f051de67d817d3988db1a879ac6167ddadc78b4e61b831095e4e309a7082784d6afff3ea16ae3f35cfd7ff1288067ebfcb284b4f07c61e6feb52750c79a069a27398f4759838e6117290eb37fdce596cce00814bdf6fbdcce3af8466de3fb9da66b9d2cf9e09bc2983c0071f28ce3c67513f8ce561ac395ae40a1d7740c6545c667f94ab2301b361dd6925445b4c558d02a2b7503d6b185a5026001f7ea6fcde53a88e6f9be701b5280b5fa57a98abbc6578cbbbbfe9183ffa71d3de546698ff0a3dd8c85e19de7ec210443d5e84c0f774fb699c25ee8c20210c51c28f001037289f6d27f312f5f45fea8006b110f44c100fed60034a299e4b9b35f216d905418041a35b26140e196fd72a06c77f61cfce704164598b54f27b9959723aba4b8b2205a086141877b223f1cdbaf25f172565ff9b7cf077cad9163b10a1b68809ffa3fde074fe796546bdd39fd8138c5c8ebb503d69595cd968995113450f87beac4c6fe2b66251029a43adc74ccb5d5fe21993315e3434ed13f2d8e81ed7dd60e76fe52f347feb704f036470c720c28b965fc656808c2393c0b6a1b517977d54d17dadf34befb4fa25ea6c7cc0a12a4cc00aae7a174b443e483ea686f414049286f627a3aca5589e935fee235c3518f8f0c7c20d2319e8667dabdac051178d4a4cbfe77ae67cd2ab45aee7dd200ea90920efd30717f8bc367b01ffaa0a6bd3c0d8b8bf8550dd81ecd80ab3d1f4a248d2a527386223317471fca78cf6b619ce7e36f052882ec1dcd68b212727de8dbf39bc9c3ed0226a910bbc6ffe5097b28a229220a6382841d653707fbe69e03f6bbf280dc353d887b88a093c403629fbebf695022c720887bfa65772233ba09f782c8942a90cb1dfbd635c59390297111e4e646334a669c5e573f2d1ea120eceadc5da012b584acd9237927a702ea2f97d172d4f8fd0f900e6a1874800bed1ddfd771d644e99ee54c58c5e32b500b8787e6fde59eca28b8c114d14f834c74a1f8546fc3d3bc745ab9b423b369353d0b73a2b9c9c1ba3839b3b91a557fa7173fd8ee029698c59f0de3bfdd4fe0f70714aad4d743b6657f1ef7fe7f1e794d2b33d068a89a04d8051c858f90b14f647c5b1214e25ee8b68ebc1ca8edb6e386dd18ce9b45fae10e1111317114aebf4051acc57b53fb8812c68a8d00deb63f130f05896aaa38bbcf6f563aae9004a3eacf21874f74a4120aee8380ad2e459c28a3ada04ca520391d832e2d83e5de943b110679f3a096407dd3de0ddf802b0d98d3c1d82ddfbff4e7f3dfaf23a888e3b917ec717028395ce7372a8f05382201ba57d1fdc37126be791e2f22feb0777d4663a940561a2c457d575fa57cd5596c99abb3825d187dcc7e42cce4e7f2f644b6636cefb479f7f9012b7f0622ce5fc033c7fe846f076808a11918afb3e4355d223abb820c741f03375303b94613a16f926beb660ff832f46db3677982ac8967af136b65a8f5dad46a506485e3cf24ba69791961704161eed3413c376975940a190d2152e5b7f3cc7660bd0667adb8063a90d72da36ea8017647547bf07ae857f5b36d55e23656493ce74b4059a409a71b054a8bed97acc3966334cac1f98706240204f14b433e3d261a7b1d08a6d73309bc9fae4a3451991395ba6e565816f8ce4e752c7072d440a41fe121295398de1760865b178e10515e3cc3608139eb711df2fc9543a2287791342a4ee66ce97d4967d1b200f549fdb3a43479195541896cb4bf5aee709e8116c93d40eb0ff3b53519bdd9818bdecf69e7ce0013a4115715ea341b746a4c0260d3faedca6b0b89a3a55c7080054764d4e7147c5193e900b7400735e040c24e0f2023266b9f33d4d9d4a037b742a07ed223ff4640d512125c2484caf71e5439097ec2555377a3780e788f266d9058c33919017843aec43710e98f204fceafa42f6c1d528df1e2ce2084958060c6f078a4db164b9a4eb7c3e2ca2bb35ffdf49b0df7740f21f0c80e5ec59367c2d524524d09f32529eb4d3af8dc0f08e65b2277c3f2fabe23f3d347d8e3598a110d1c66860b2147e1e851a07270c4b052460c32cf4a3381f13e79b39ffe91962da126af94c9bff80196f0fef0a525eb4ec9f2b63b76bc2ebd3df1e4a3467fc88c12cdb62a24ff01b80eed8303564a02463c0989b52f669f70c37045823d470b8602fad1a40681e7b23effc6241df80b61be2369bf1580f260a414b5c87343b26e8c16fd510187904bf8fad3f5b1bc2ce905416e0aee0f64988f6269250882cf9dc832ef83b4cd21e035f7ed89c11d351c9a2971f5517c515e88eefa5aa76d65b90c5752550bf9bdfd0e3ba634129c3c5af487f11fcd480d82db932caf0fe43defa070a5a4a099ee2ee033054002cf5a4cfa7c97ee8eb9afddfc0dcbefe3a8f56a9ca3027669060524e79a6ba13c3eedc87cfb051346b8146fda85329ff6765a983fe848ddeae3048aa8911f322580e5a7dc36fec224d47f15a324e43fd4a091c691c28f79828c20461c14040ad48a9059305816a503f2460d49daa6f7c11696a91b2ace1fdec5ca0e4acd0c517debf1827a4a560c79d233ee01188823eb83f6e31fbf2120e805054e012fb5b9acd453c1ec3e0a9eb42c2fd3a71e37b3d6882464efbdb7dc524a99520a8b082f0924096ce3f2fc31b04de9b7ff0e70f90bc03f7adcd45c8699a91835a697edb1593aed47abe99f085a208c03472317d2f3921b3f15395a4d0f84505115284d8458b2fabc6b6a22c4922ba4be4b57eada51f1539ef76f32d5486986d5ee5372499fe3ee84d25a4da67fcf4947619af8a4e824eb2d7f01763cc4ee6b14dcf859eb8b17b232277cd1aca2254a04ab387b6444a21b185525ba0437c227c2aa91c58151d3464ebb1fb7286732a83097871899263e864512f5e3160fdd4d24953f3b69647b0bb0ca83a898c528dcf848fa870f3407c6f1c18403e3c88f5f01b6a11fff48ffd070ae8f6f3da83a0263e81f9aa5ed237299fdb88b25da48025e82bdf8f448fc4863afe0466ec59f338051a7d5712fc608551bd3f931c79d71fef5eccd39d342340c596760589504a2f8474ffb4892823dda477c2c7ef4e6523c299aeaab3577592d05553d3c444444695c807dc48f11034c13ffe32624619a780315d5488f7bf157c840cd3ec5d33f7ada477c9389bbe809e34699e44d5a4e44a41425da673dd9fb50b9381ecdcacfdeb9202de9921d4c7630e91d4baabfe60e37e8313a6c814629c76fe6523691e88d3e3f66dac7052ea55fdac1a4b27b2ef6f3b9f4b5b8757d7dc7924a5feba6ef14c228e3c31bede773c568dd7a2f97872e9699a664bf24977eff88c1ab24fbd5113733837b300693bbf7fd70778037da1c6d54fe90cc5c223272965984069fe5535aab49723ef25316f56b2b577e3de54a2b526c2f91d85bfaeda5cbcbef2aa5ce887bf2b7aea7bb0148922c27b9674e015c6c270de49efc1b3ba8d98fbe81b6e81e67d93dadc76db8bff131ff5118d5cb5fcc46476d2fb76deb71373bbfeb46b40d712eb9d0d8f60ff9b1c42a0a51f1a9cdbb17fbfd42ec42edc267c906e055fff0cbb4bb18e5d1f81ee338d3f4e7a1d806e2a061fe75d94fba8feb2fdb3ea4c52105a5683cc331fe57af9cec45469960a49561e662988d8e9a636818e39c6aa8cf3d03483ffafed6ba5652259d6a7079ee8bc0fce874aac1e5b9e781f9d1f3fdb970dbece95443e9b9e789f9d1dbc02b2641949efb20627ed45c90530da577f922257bdabe74d28ac4d8d3668b909494eabf3c4ffd17eb43457b1f97afaf591e5289b91d262ed6870a8fcb97de06ae2fcf3e80eb5dbef4fe3563b8d8afdf6342e8676fe4eb217e3d3414961196e632073d7a7818bd26f486a2853806b7213dbfce0c6e537a664e7d8c35f22fcf8d9cd47594daf5144a6bcdf8923aee8ede2c7bbea2b615292addec575df8650d5cc76dfae9477b3ad5b03df745467f03b3ff6e341eb7f93ed3b4ec6f80c0cfe93182047e0faeba2114037c1d0dcb201c753f2bee7f5640dc8395079eed39bbd9c02b1db8d91c1f007c5e610e2d1595dda3d67fba64cca5131a1fa065646ea497c43ecbaeebea2e4a4d1ea40db1c3184e5b2adbf90c2fef27949ea394de5c8a7d89d48d3a4dcbb22cf34dcbbe3b2dc3fec22c4b8f269899f9e740154a6b894f4eea4e3e7a9ed1e85fac0fcc8fdee7e549ffb3d26a77f25277727f97fa31d607e6ebfbbc7ce98847edec8e1849a24e08388126a1ee94591e785e1ee679b081573cd8007f87ec6de095cbc3581f9f9f950e1456cd66f918cb2b98b7a71d983f65d6065ec1d8d4cac57ed7e7c08924759dbe5f3ebdd4f1aa76bc2219711b6ae3cf41c85e45738a452a85d16b4af786c20a2b806c5a86d16b426f247c8c1bcbc8fc78771037373032333d6a6a328c5e53baf715db651b8625faa70ca334cba80ccc81af33037c1ef833f0bd0ca3b445c506ee132efccdaf0c5719d79ff20cd72c62422b92a71d86fea477f9d38ecb9f6ce855e94f455625fb3fab51fd6a3fd2e8e75e2fb3a4d27f7e8c310c7b86b0ca0863f04415517c981bcb9847be6919f642ae2d5bb6b461c31f39055c9ba9d248230e3448c37d1b3532a1c243df1d601bac2bf935eea8fd66bebfac0bed3c11f981757ba0173e7f462e7cc242725e14cb8000c9b0ebc7bdc1000244757343afd93df03c39af99197a4d197fdc3b8ad643d492c633d767d9c32c756fe0f82ccb328f8bd13c26bdc6dce8bb23d92fb5731996f83f6ff4fdf57af8a774a07f8fc949cdcda8f99db94392bab687a8eb2b0ba1761bb98ca2d55c9e7fa9a82a7bff1e32c398dd8b3fc81fe87dc557493f320e7c5a03d9356b3e1b172e818728855dfd7ab8d1e7c69883da3f82bf74bbaedf5ca575596cfb23a8d35205f81a1ae754524ba7bb41a51fd4b1419dc1a57db40f7f26ea217aa21d9d19dca6a18ef6e1ef704bce0d4ced238afafdf7a0dec0f3c1ee3bbb8da8c76d3ec67283c9543dc6de41c9ad37c4a836fe31b84d9b4c2c1d45d4af9a58cbb2c630da5996cdcf4e32649f39d76599f6cc651af69d61df18f69de6f33dc1e73bbf7f7e6f9bd6652719605b1ffc9da35b859164c4fe90c48e59ea4efba56e64c991be1b6354fc517c2ec6974c13396b993ac22b47fa909b3b8e2285a8332ea4d18778e10ffe23bd8928d643ac228a262e57a106d1355dae420da1abcd8d8aad52b799736a327e47db9f034f50a532cb94aa3019a6653d1cd146a4116544ac1e5f613a355373a660ed69222d510888a2101951374ac9d0b40c4208bfd4d58e348241240bf58854fffa7e97eeeb9503b9209ac6715cf771cc6d9ab669ad75d3ab75f7f5fb302f0e3ecc8bc3f51a65ce24e3dee6d20b8eefdb2503b45f920b3fd2adc298e6d53db7e95cbfd60da7c5410608c60584fa994c8f6ddd551847f56bf412aa9af6d95fdda7b19643ea6acfdca5659ed1ab65dd977d0ea98b437c987539a4e86557517825a8661442088f5c11218cdf18c42086c53859e8daaea750c5fdf526a822fd75913a18ca6d5e8d4bd858857d9c5807e328f818db4a837a59ca2b2c5169d462a4f3e1351f9e88f80cfca2664f214c72a1fde80ce0d7f06f866118b6759122b9f39b5252f4895009245ae7cba9e5f2155f984194032713b58c101a11bdce06f5bbb9fe566aaabbbb7b8ada2374990bf2566ebc1ed7ad855951112c9229698b8c7c84ddd9a0fa5ba9fe1f17b5cdf7503c0f591f640955a1d5242474bf2b0b64614ea06ab2202a42b9110b9a72b5645e7c980b6efcefcab2a36981a80883fa4d4dcb8d3f599355194ed68dbf234ac52edfc089d01d627141f108dd3ca8a87ff9d2c0f7cfcb57d608e2c6e7314585cf438a28acd13dd078e693555a1746eea018d48f85fa892a04b1b4206c412bb55fc0838aca97af44410dac9abcd48dac0285a75c58c19310b0d0a47a603e6149994058e124f017ac2c454d60c284279a587914ae7f8de222faa9824514222d2d289cb8c2440635c0019117b3ce14c040e3410aaa002209fa53646967a235c512ba5e6e4d31c6c52eb7a638632ee1964401cae2074638e2889567e00b368a5a555650831c1011456bb68c52469413aabcdc12a2410b51849c702688105b422a9861e880460f5cc084222ba2285d6e0941112a020241ce979fb4d5e553f9358b269c70a5fc198f16492a09130d88e81a0c31c485156674b9e58328acc009723425891478a1c5d009b24c2088b450e2074b10f14bb2c052ad27435c78b9f5a404330373085f620330890c942e7c53ca035888f0f324063e40620c278e085263b25670834b0bf9de9c5860190209126230c61a5b8c20011663e0e00acb77b0022e8eb82e27818b9607d0b8dae5561223b8dfa7a4179ab713ef543a33ec2469263ced7ce3369f0a08dc72bf4dc53852de7a250a625c1cb77a97ddee76e8eb4c033f36a1ea406666da1d8bb40bcb5ef38dcab95ddedc15aa5cb4bf6ee6d2c138ea9ad736394d9bdc77525cdea53341d4f59adc3431aa76dd8cb445bddc26fbeb395bddbbae87a155e15ff1da6a37ca344d7bcda52bfda8ab245bca3ea3ddd7d787783f1dae666d0ff1cacf58e8d22b73c9a28114d757dcc69d57d362db42125d8fb7ba0d13d10008d7b3d0d80b26975b5d10e072ab0b2b3709f44117f785cb2d1f44b9def6d98cc63b2e8344bd5e931886595ae5c605e1614acba8f32737445eec35312a256dc184228e0b82618df110ef11243dbc9ef934db360c8b379bd0b351f6b48cfad55c49ad8431044693fbe3728b4807d7c3b2e36661e53211164358586935717d5c6ef580755b5f64b9dcd222a8a585932bc513222b54b4b0b4a6bc6cda18350ca1fbc2e556184eee4d182c30b4c46ced3736a216308a89a2a0445554a22b626eefb8deed002304444caec96db8d583347a6006902cba3352ccffa2890b80cbad2f987891c665e172cb0b2d2ebb4d0a5b771fc52e04eec428655a7344cd6e6efa0271ef86c76fb0ed65b820482ec65650d3e5bacb7a7c3b172b3d89e3b867ae318c7bf7b8cff127759fcc2565309051b1efdf3ccfbd1e34602f93511257847d003e54faa998d83d17b82018666a1c9c69078b5aff8b97f437489f034725faa4cced4d7f4f8f7b2cc3dd998c2646cd300c4686de7284edb84d66638744cd7ec4da9e3ef6c17041280b580f0cc7eef180e168c85e875b3d381a6e68b820a4ef3b391ab2b7372f7041483fbb9fcb6e43b296723464efbd4cfb7976db60ed788897c3be2dcf65a6c1be2fb533f0509da7dcc51e062c286b815ddc90ecdb520cc37e623c37dc10eceed434e99e59f36c2b526e3c2da37e3757cec76c534a7fa326f784f808a1769895916f4fa0d27bfd8672f98bcb2d2eaab804b8182764be96795e4d0dd6a3e6eb713d0ec37e731b18c69118751bd2f76f15a62717a473903ad2b7979e392f35fd18a71f439b922aa97e8d89e962ca9db9dce24204976296ed377aca29a0ca50a737eb4edcf315261405d1ec4906ece963f6c4bd3d8d9eef0e90f41a471fa3db69f4a3cf3a24a30eb39799863ed67d29ecfb3199d49d76c6bd1bdde584c56245b1cababe64308acda069d42dd63031f9268ce2abc3c51346f1fd523a5c848069f83601aed61aa715f5b071d7a02f5fab8fd9cfbb18d0165d5c9acbad212d9752a1e532110d9e98d6b032a58563ebcea75926c72c05d2217194fcee8eb8279f0764c02b81189151b8f275a070bfed7e3c04733f1ef28e6ef176908cfe00714fbee7d5d4f4a87949d57817e769cdcdefb6c0b443e2cdecc634da0f89b7ed28fbfeaf9b1bb245f7346d76231b8d44b20f8485961c0dfdec5e4c74d3cc8ccc0ccccf984c2d34c60d696cbbb98921062031b8e4c47013b99b1e82ddccc29045fd642ec66dabca588692b80db786547025bf9becf4e830ce842ac85e3ee16adaa6a3c3c3f333f04851fb4bcfdc901dcbe39eff484ec7bdc805e9b1359765432c3704dee6283b8e4c4c83bd10ee694cf7c120e9993d86eddcfee6b9d9f76b5927432404e532d11466dc7a99688a1f5cc62244e6621b0c523037a4da9feb0f611827db8128f9dd3b5095044ec93458866118cfed1bae7342faa74c872475373bd37ddb4bf7f5f880dcbc97bac9d1d951f18ab254cc758ee55e8d769ecb9739a2cadcdeb62d320efb371df9b8a75ddb75514a63865858a8c609d13ef3ba947bf27574a0b6b4a9aede6064324ee9fd4d2c73b7d7816930f5291629a5305968a42f94d2178a75497cba6b56a963a6d9dc2ba27e48646ea5dab6e1c802d4dcce713a9c2c09e2b849c905e976db4cdc93929503ca57596e531b4afb111d90b88d3b71c20549c283ea0f01b7d97a6e30e2dbb68dbe63b8197e6ee7b8cde8e5c7e03693dbb01386b59452a68a6c6580e8ca211fb7e116104eae7c20a8dcaf9bf8fac9958fb5c7dbf2c1930bc3e5561845578b1c91fad4a9b3cbb847b226a6c9b2ecb70e89877141bacb76ae7fc605f1a75dbc3cd63b24a9cb29ba318e0fc72a258c922fdb7e321c1723ff8b49e29efcce89ea2f547207cc40cb1642b707781b0890107045a5b2986480b52a314e015a6700310d544df93e3ff0184e3061ade2fbfc302d15f83e3fec006a22c54a5a2afe3f9c90e0240aab68a9f4ff00bba7bbbbbb9bbb25d3305b33d0284263e85ebe8246153097afa041e52a216a30714a64326e702ff6304dfc1e8269e23ccd06729b18372b56b86a004f3aca57a144acb287b8f163931bb5dc6fbb3d04e3cc797ba02ad54050c580f9f282ed610c685517d1a8f8b451f1dbfbacd5a1c3e57e0de4d240d9fd98a853b8299864ab6a1c151f4866858b8a1df5e9b86dc3eed9fbd11437451d5a82151d2c97c8029b3aba74a3971df71adc5c84b809aabc99fe3143fb881fdd541fa6bc51c9ce10493656fbf9e5712fba1ced304d7c6e8afaf2a4d7a24296444a4d6a924f44462f474f6464a52881f997973d2fef43455a986abfd14bfb1d692ec8e8e50b67bf929c82c0ca932f6af6f1055a7c0d08e3909e5f875f98e8729c139e7bf0fb53504260c6174330e2076aace07f12f5a331a298c62f147d9900d195f642dcaca85ccfa50845bdfea3345c598681a88078afeb3dcb32bfae8797e501cbb21f825c9d0f7e31c62c8c9b7b1d8ca3b668cd6235fb88b3df79c8fc877b79f7d34b86a88078331ffc660c337b2f8e645cc805d9a1b708f99f7b7549ee9675356754aa79ffdcee7a80dde44b72e35f5434fefe1d30a8dcddcfedab76221dff41a02a56b447f85b33619c7e93cc9bd6e034cc30fda076c15ad498f821c0aa9fe123ee39048c98a0f6efc06afc68e0be76b5fbe8b23a308d3c03890b8d647152effc9ca2a09f73e67c966b8822212834212673ce391909ac1b4ca9c22ac210583810ac882d253cb04407a49452b2e042621221a594920821823099fc1586c604a65d6e2da16289152d084153de4c1ce932839bab73d30e4302872e5fc07ae372134b5b7ad069b55a2d1aa79cf26504f5e316141d776fb54c5e8eec3c881a39a1b5d3cbe436476f416186f0d84eafe8b4af1ea259d0dec1851f04a1402743bd03cd6196805e58e1f5c743bd1399a81f0f0545c9d181aaeb31acbbb9baa11eeaa1dee954efd01d541fdf990981cc936ec26468c883a8388b70b0019310a310810842601d6c00160d018628832068af92f04fd22ff767ff588beb115a71dd9db280b852807e35ad8ca3dcb4c5c4ee43d0428b085fe8dbbf75ed4c4878c1002ffc0a9a8fb84b957697339c5e466e1eb033840bffc3b94270e749c5d2156239c2de11a8a233720eaa2a1ed94280050b96200e64a1290e409287c1e9bb3c2149bbcb091c1a01bd2f6aa47fb4b310ee13d158a224d1f2b11653d5125f9878423e8d523fd6f24510be0c909a9afbb1961b8ce4403a618cd02e1731b61e333230148608cce5089cc9d832f68b0e3e2ebaf0a111f814c6ce2463cbd84d90b15fa8162ddc6be92088824f9b35a596244ed4efdf29a5f0af7f9d2450e55df65dac549ac4b3e2a5303d519bd0a2458b162d579a802549171742f8b109beac8d10cd43a935fd27a07f6054a856415a523fd306eaaa53b7673810b921f4d239a590decd795d7f79b2ebe109cd34a99fc908f5335d77db6148d4bf9f77eb90c4620713a66a6194f34a82d218e5bc28966914ab584629866159a66d1bc78d46a49ac154227db58d1bf568b4916238eeb71189546ba9e4e2f2f2020313c3059111a2a670828a0387c9044d2693a9965c60626e4478c36fdce81ba5928bcbcb0b0c4ccc8d1b26538e6b4aea5293ea1b3f986e4c381c070c4c4ccc8d1b3870984c3972e8d0b163c78d1c384c394c39749cfa9443c74966470a9d5d6106002cb4e0a15ce0d1a3c7d643cbb01e3d7a6c3aea0623733ac9ec484185ffaeb3768515666600c0020b2db4e07928940b2ef020f1e0c163c483070f1e3c78c8ec4861c78e1454f8ef3a0bbdad9d2315989dd9f9bfeb989979054e86a8020b2d78330480112c1851ddb6d082e7a1502ef0e841e303a3970f1f53fa88d07db40f1f3e5868c1f35028175c7081078f1eb2478f165af03c940b3c7ad0a0e49cd2615c30419927d5ad0b1c91be3d54b0525fa83406a18931c2b8bd20534475eb23150001c0d030d0d0d0f464a1a57604d0c44e53ea2f4e48b576f0f3217c1d05b043085d4c103d14fbb8ac89c7b46cda0183eaf282faf1d0ced0d0d05016cda1472882ee7403bc4cd47ccf347acdb75150cc8d989f4153b02a49146c23ffc6cbcf1903f3da283ee98b8c1e3e09be125237e1dec9dfad40d4dcf120889a2e7467cfefc4a8cdf1475f04fe8b55427af823ab840a0fc9d257c2308524ea07d52fbd074155a985588539d1f532ae1a9182b05041198c93cd3bdfb340d5280860f0c0abf028a8b7c0b55c9aa983e3a70e5425390255386c433171769b1ed3311c754d0451f349dd7cd9589ce5ded472a73371d4f4a02677ce6c0605050505ddb933fa2015044d79a9228a4934499fe32f5d5771b1429a329af34b9dd441fd3cc8a3b8cd97bae236e1361f5fa9122528ca6a742364481097c6c3863e83e80d44314ae97f24034c21dcdda1b7684d027ff83b3d146015cc04d826fe02d440b1d70fb91a78355f8b3fa80b80377e0ff6a633b88ce8232957bcaccfcfea212a7632399dcc41a5514606be3ff48f1f87a40fe66b0f5f4a7dcd1d0c5631f7847f869771e79c139226e9739c3422b19063c1e17090a036fc18db4ec76243afea9b60ec72523b5055faf93f02aa71554f9973d6e0ab6a5b0b44cdce594f1c08aabcca8c325f9b92cd06a276e55560155877ea8477be5b619c22a88a6fe241c5825671a7c6927d24a53404898afcacba055156b8379f347fd4cd975bd4cfabdc6e5d2b6ef3e1b8f3e7d756dcf9f3bd8a3fb7c324cec7c13393bad684c65544612da6ba4d216610f793b935f08a096ac70b2adff95feac6eb239f986f7c24442e50a2b8aa003db46125b2da38c2fdda89e6644b116df3c16c3d4666701b6825063789fbc9780aaa4ca6f89aa9b6a03244c123d0623fffea4e6e79307bf213cff51462d863cf2b2a4fa8ee04edce1188e215c686ecc4a81db12098213333d727b5bb9b075a4e0c53d2bf28d54107a8468d393ab80d5f2918041c85d99c2c4e90a81bbdd839295e161887e03e10830b4c8c1f18218740c500aa720835a057d890c17db97c850d297774f90a1b4a18418b9bec4c321d17449d7115fd7adcf8f165fe8377dac89e64f624c3bb83cf9d94ba0d845235339e4926422168d7f5542c503108e96bbfbd4eab590db400d6d095fa555335fdbbfdbc5a83c6f0bb9b7b98377b0cc3be31cf7e0713f54b5d8ca35c71ef9cf5f961c57d8e675d0f06395035ceea78a0726bda689d00d1d9d318994da5eb73520b600da5628c99fde0cbd3c8fa6ccdd1906d4fe9e8475d6454fc4c067a3f9f9b3d5b0fc69a5e4a0b0002e6e3a108bbcc651513ae818529a7f90f4fcb8fefe3a7d89dda9e6a80ef5f24beb44aac1225fed002271afa29e9a1dd81a8f927cdbb93664f0c900f5fd3ac0f0bfe0ff195a4a43d69f37f569b3d6996071ef9dbfc9fd5cf8acad0cb27fd977a5e916c0e0e2719fae989483ffdb6bc92160d2a27227de9f7a5f644447bfad4e2405fb35709159ef8a45772d2de2a89f674fd4923d9d365315b44c6f7374285f6c045d7536b011f08e143fbc3e8e147ab0344cd6738e1977d0ff05e492ef673aa21f28c3ebeb4cd8a3edba44ada096cdb411035e7d7a021834ef4e5a9066a79e49f78e25fb6ab448e75bf168aa27e9095e33a6a8450b975f97e70680a54f52a0668c40e75997250bfd8795abc15523cb1c216bb16b819cc6cb16301005bec00a095a07e94852d7633a4245ad862b74266a57e3176b6f542282b958b6e527f1b680713ea4763a782f6248a4381414d1cd58437c1638bdd8e2b3b29177d618b9d8c1604510d3482fa511f5bec4edd35907b4fdc6b92da62a7e3ceae9d74fdc45103a0976d2088f214aeb4295cc805811c6f95d413d032cd4308614321681042c834d43d18a78410324fe9ef90f414fecd5950598a00ca08a10cf54233cac0fae95fe1cd1ea39cd7d636878e085c18c39ecc21810bbf3df93206870bbf2b506fec16a31c07cc3108eb978fd3598860ea7672351216da404848a1ad0d40b91f674983d7b8bddd0b060927eef5f17a0ac4bd9c105da6a93a228b4d76a3e988a10bb3d48f7d84509531704be2800563a8e1a40652ca78c14a422c57c600ca957fd9082f74d03ae204373ebd62239ab1e2c65cf9b4827165cb881604910f85145bdbcc98429314dcf8728bfad53bd345577e943b50a144171f8001129240c51cc2054f5c90c78d50dc184d26d642064a6c37625f98da848171c42cc2093542965611683c2122cb101ae04887208a39d064060d40d1c0086628424803538c98818829401b116f6ad305e594725e39c38b39e7dc1202477114da84a3e496275cba90e58c222967248104356bc2fd6a3555982b50d0407d31a668c1779b1391f9e3c49dcf949a482e9f753a5c10051fcb16edf7e180848beecbe700affc972e076c2bba97d5b1a4c2fbc5e81c14a8327dcc8dff2a0e1badd501deecbf1b1c94dea875c441b9f061daa81f5774e18f9ec3c238d9c3e7cc701b9787cf157164b88d6ba5aebe465f7244b08ff2230dd84986f8d8c7c7e1e5a37cb12719220ef24b2f5febad748a5648e9e5cba7f44b5db51f0feea8e3a12f673fbad9af9ab6a2cb5a6f10420cc67252380a72562e8c10890a3f76f386b6606e6ae5b2501ac08c4a9d97e7cb37995e4e8c820f63fc669c1e1a4aa9fe1fe58860d76d756bea72300d7cd9e948d598dca66e9c05f5fbd44d0e8cd2f6d0d9f5cf201bb186eec052fd53eeeeee5f5cf525821604868a6a6aa3eae0bfa30815a3d79411fa476b819fab83ef284265efafd4abc0a1bf01f601ff45d32e3f64557740882ea264546a5ffe6b630cbb54aa948c6114fc43328e0a17c20ba1055282cbead800023f16b2018a0f3fe38244185a2be300c13832e3867037da8c4a85973929d9cbeb7229d910fa57f7f98df2e3d55bb48b034c03bfb92119957a5d08c4ae99465a23eec5ef537c5e65ef2ca86e84280650b2930cf179e2cf3dfd4cc3256a0ee318b98171e2c36f00ff507259f500154ca6528961b43e4c036b9ca480b88d3b61038cbae3dfe3957d270b1620687606b59f4f1e1f5a254a564aa215b2e255bf9351bb655ba8e3a2b2ad1d79a87340c6500ef291c02d5baeb4523cb2582b0821dcb265e55bb6a8608d1ca4c86732ae14fe359aebe118e0459ac96032c84083e4af792785452507ffa692833fbb3d11e9ebfdec3fdd16883aa51b5f23c9e719165c22e5b4532d7d7ada81d60451713eb5a79db9a24fbf57d4e12ba1d683a808df2f7b0351d1e1f717f16739e40eab10c10c6a53aa5258f2990196158b48cb8af6546415fbfbfbe10f90f084751b0a1ad30cd1a8db44d88d80160eb2dc1c97afb0c1ba4360528426975b4320e372d1e5161945b74504a13b4b77be469acf3ebda2fed12dad19a36234da229ee7d8bb5dd2abeca36522c40156d803f52a7b13f6d967ba41035a1d889ad9d78c7105d9a11f29fc48bf5f89126a7b6ac66022fafe45a875ab258916fad7f350eb4385c7bfffb2ed478022d03750901decb52c5a9f5e65dc8e0645a3cfd9688b5809a201f58abe6619a278a0f56122c40156f4817a757db44b7a757d0d1995fef5f1a137478fa31ddabbd5a27fd9d30eed55967daf32eb43c5065e3151424549665b6360581a0b44c167ff22bd62615da06e50e7e5161144440c0115911842689d9999bd2111124a184550462a0b8d67be54b4ff10de80c6348a20d283a296106d04418408c804d41aa24882c0d426178470c6d0d51982e8679ec0c48d975b432cb9dfa7588fb385c6333836f84acbae8ecbb0e6d0f12e36bbbd020f6a578710be91d9f5ecb8c745468ada4a670f59da9caf75bde209ffe32e43adf3558a21b7a062d6870a8ff6d9fb68f2a177d2c8e415e43c2baa7dbfd8d14752b677ae48af36ebf3c34ab3b36b2b3d05a2e0639d110aa9b568d4af592c0a37eb2c6d0438b6ff622ebffc41ff9b5c4b9bbd69cde2308dffe7c275fe991856997ef20fb9b10d86b33d7f896db4cc740056298027c09effd55120870ab00f7f78861ad79f70dbfb180ad75f07721bd75b88eb708b3567d4ecd93d6e48f6dab5c50ede03f00eaeeb34ca3921432e4ec80e8d456e988d8614512087f6e17f791a5a9e70fd75988debef5bb2e06b00ce0895c2fdeca400d115f7b2930254571ceb466b290bea47a9e6970d349ee12e8fa3e27677e20a4854f8adc58f85e4e7b88c3d76c73d2e6af721349ef99ca734b3ec295da59b7094f7138ef215a8807d785b7fbe5c109bebdbe569e116f5f3a12952aef721efc26da277e143dd41f01f541e5a87fee14a149181eebef19477468eb837c56de077fcfe243a40d5cc525d4d5a0a520a1da6aab8d7c409fecf8516f62a767de7f714f7fa79056d80ab695700aa2d5d8ad1ec2b01b48a95c8821b072251e5f54fa42083fab5909523f0fd752000a7e00d3318eef90d91bed31eb9611a2f2280d70057974d419415586ab75a0868470e2a7f771087304608234c615829478e48c1a85f4f99d21e1dbe13582ea0ae7b8eeb5fe93a659c78994bdcd59c91011e3a08104ce3ef4c7383f3fd9b4c371815f5bbbb3bfe8fec2b0dcd538c43baf33dc639ed29cae799ab537c25d7f34b5b43ff7c1e2554a2e5e1bf5e49ffbc94cc1d58d47e36991c06acb5e646a0ea9a454a8c1cb9809309b08a7610377e43a14d8b2660e4889306e2099742f1208a7d84d11c88da99445ad48f8b8cb8cd478f40c06dba280235bfe61bc7385ff64def97fd67d967178bf1bd0dc6a4c5987dcc3629a1d45ed3ec69085fcdd69c51a9f63f37a319ed7a80f7b25d8415282cb8b204145ba4b18a9e8a25d0e0ddfa87774b54e9efb90725848e43fc1901bca07aeec1a4b5a834ae9574410c15d1000000004314000020100a86c421b1602c1e95cbc2de0114000a8ba0447e529849a32008529842c610600c2080803100032033325b05e52e2b6ac05478bf273b1cc876a547901d81ba146452eef7bc0a948c6ed2dea62f4c9784d52eed030910924114fa59e0de1db03f7be46813a14b0483308478a33da0abdd88f57483bd0929767d894aa9d6a74997a1d1efdf20adb8322ff4b9f2db32957ea26f2727881446ec4a0366981f409e52a16cba27c834a97f45c2e8d2266ddfe35961756aafff73427e714d0be498bc34156c2712b34a8c19376272f61ccbb04846d1069abe353cffe41950d37bd97b971f906e73df893077e4af3278a08a15d69543ee8a40d25557e39c1447ae6f27d6fa8623ded381c5db8e2645ed5362123465a0da19a66d735cd0901b33250d21d3a40c601e869e3a9b7e400274800cf624a71aa248b5af2ddda3104e8262d332a7d4534f1d695510b5435dfecd623eec499ca60a19a64b673854e7e8b8351a8c1ecb49f3ec57b9cfcf6218b3142dc652067ee3018ee3f99e4bef8a6bd3149fd698085d9b26191563177429c4204ba735ae485996d66c534f02d8bea5a5f99ebf4df263bed1a7ea3db6ddcb779e5035886f68f9ad01e7e6a411591c11a285e9d386e9b95fe3849b3b131296689b60c7047fd6af67300e35afd4afdc8b1af0cc57d479aaa5bcc3b0495a6ebb1a4dee9fbba915a5e6b9a7b7a83e7ca942115f6a4c7636452c6cce9b2d3121d0873852869c1acea97138132297234ad9f71a7036ed93dce4a3d06876107bdddea8a2497b7a103032e60360b535ed44e2f434a3f286c8db3040a7724415e7855431fd9d2550691537de0b6b9ac00cf6060fe1dd1914837aff900d09e14cbf9b34ce17ad80f44f4cc1c7bfecd97eb4ccf1e734863287b6749e0a597b3217d375eea163f35075453223b0ad0463ef194305c5e3ae9d5d9bfb431a0b34fc277c567413af7ad0516f20f7526b81b41c14f0285746e9244f91818bdf417f1fd299aaa46dce45ec861de8b6ecc22c1f9bb604676a3429910131b10bb7fb15b3b27f806bcdd4dc9918ab7e5c3bcc653d35625686ca8a00dae6407f0e3abf8444079703b0dc69b0aebec2138ed0e46f218f23ed8fe9337ccc1d3174d3f22d25120b373ebfb55aaa7927d9e457a984be67937bd4400b158c16a7582a3285187737220644d1674744542e2491a3f12e3e2c208b0c3e6a31cdfdb8db67d6d421b0db3c3561a3deaa9b3300382a6375739025324c9f99e14007fa8255f8a775a6cd7b7ba1ed1430fd3e025ffae9914960d95dc21732ccbe6af91a7d969d7422103823d92b876a54989366723d29155b1b652a4d0713868417e99da2230a84b62b9ffa95df646bf920c6f5a5a209df264518f62e82fd04b877ec21c699d05a8fedd529a529a45777e211905de71d67dad532308ec29c450f9704cfef7205cc1d5b01fabdb8235f93c6b229c1b0ecd0316a9a37c2b468962deb93a00266bd779b973fb622e9455b014f10e126fde906222f62ed50787f916944f8b3c326ebb16f97c21ef00c3a881b683c196137cd1dd2181a9b61d00cade4d67256ab6c36811520dc8e8f598eac6b96bfa24807d0a526715456ad9dd5392e45d278fa52db44729ca53d27f8c746fdd628e7680b1ac639c0b4479ea1832dddffe9310f7cafcde26b1ce83ac0aba9fefde4f20bdd4a697e5ad76b4658292129e9aa3cf3a3c5e64808cd35d65d1ff1831043dfbf4e30da8f62e853e320e2ebaf6b4074bdadb306c5de1c6450baf472f59d1f579acf9cb60c940737e52f9529d9d54980e634f15b3732c56dc00204014a399d294e26a748e514f939650c25c9e4833f71bd3294c0602053a530d90abefd540f85f5e707211f476e865f51315c05ff6706376ac6b493cfbca1cd613bb8704c1c043003fc100f0b0c98d6ebde89a300ac9ff3cb4d9e179da4e307e3564725bfa5470c94d164049b5828ff385689b94f3f892c0f8a4de70594968634ea62f95b603392e10f934458131cc2829fb71b91d5ab7cc5ba8fdb090bcd6462d6d6b7112c209022d00c703b61ca3f70769d25dd7ec93c91aa1a3aa08b1fc2618dcf4bfcc7614dd6968549b6da939444e589d17260dca0d9defb984bd573771752bbdcbdeb245fa129d25aabf4e755a6d2ffe4a6070ead45120df5eb2ecee3b1116a37fd61d0bde355392efaed4f917cc8029f80aca4a44bab6f04824e7de31648a6a7f1bf33665d0fc8547a8d0cd6d5ce61e82974da717122f615cef89cda9cd04100a3f4fa2fc88e870b2d10d7a4ae535e15adaf48b6941a754789547fa6a793c16fcf49acc54866c396dd637c756f06d458caaf97b08192b89a957564ace6fff982c7a17a2e31c0e4393eaa2db87b007be308595948fcc286d4990731ef073d9cb7a3785dc08ee79f883b352a2f98d000978449cf4f78fd59966e09315f00af3fc7f9b408bbfd74fc03ae091c8e1ac83859f9efa1d1f78f1e10db9accba489de6a94231165a627439d5e81455eb98cfd404f86fe605e103c650133029087cbd2aef653dce4d82f41264787f5bc94d02006231e10dfb31802e9d52fb3d96277c0add8219480b9ec5f6b7a21d674f51b46d88d0354a802d758d6d430bdc4d9050da6b49b7f4b30ac0b552c13985651d3339aa0c309281050b4ac8cfea394236cc0da609e7e904b2159195e69959fe560b265d7682d212ed3065409136028c1381a0a6296e0b1c7c812f55d46b07b0fea6a3c346c3c451c03b24b260f8fef63aa97325c0246fd5ef1209533ef63d32cfcb5606d063cbed33632929786041d91eb23c1b6ce4f8538f013c0cd045ac3a21e793ff965ec171bd0a0537e42b9642b34c221238dac2a9bb72073ef45cf5f82a757abd14608ea9922b491ad78ba3bb9fc7405c6077e5c40193d65d9d5ff7e750ee19d2e361655c4e5dcdc4e5f09357e400891556e5ecb61c11823acb329a7ae7e864e5c6c8c23d75f74b9b5ab47844fd31d2aa0307b2dbee95a1d4c53cd2df7de0159c3283ed866c9232018ee686577dd0adadac3724742074a9a5f37a41fab171400a989cfd0c878270414f62927f78d20875e2028ce5acd0a66db899a666ba5aa361de96b9da3f26c5baa4cecb276a4e5b79978f005b3c25b44f3cb2edaca9b53fb08fc9e1084eab2d037ceca42956fec090a18a154383bed00d6a7d03ccde11317107e09c2351e99af5eea103af304365ec0f94b65a30f7d228c5bf72a36afd4c24c1efc99419f3a0c0fe5db33dd2358f60f78a87c52402324e1dac9722d94b5dc62f7543d5a09e03dc74cb3ddabe0228c297a720f54a740a663202a68d0e04d1367602598a391c92ff047c8d34f4446f95dc3bb60e0e5b86a49ce615ff5df72bb576a04f11d13b706aac6f23f4c81e540d31e5c0cbed861ed42e1b2a948a2111fe46a8535d1f168f9de235ca0c1807d8cc2dd870b0705520a0fc511a2fe5d442972e5d88a1ff98e7965ac31ca22d3ad1ab59b99b12703389017d6c1ccfe2df1abfa3da2c94e9e106b9bd9b64d112e9a703b389c8b3c9170ffd7ba022d5255ba58b040b37b066a79a934e97b8c14e99cc02c64d5de8051d715edc14114f4821039c4a7103750fab7d994471a32b9934018ed0c73b1637cbdbfa5ac8276e26179f5a8826018534ef1b93aaf04b00ea485c9d93accbeb24b6854fa6703077bfafdc655c67cc7565c94a39ce94922c5d64e5ba933bbdc1727df3818fda7d1cca32445f04abcf1be64a10f3d70a47026c02201fa35cec8c5692b88ea2ebcb178a51568d406e51b3a418c592a246fad460028ac0519be451405a592bdd065125d37818f459c1679adefb20963759cda88b6f52afdc628c9f1bdda3471d5b02344d492da4a48a83befa10ef1b8944782701b3c16360662ebbdaa6564130847ac848941251c5b41301c42a43d12e0c6f3defb328aaf8287fc051846758c866c5420f50325ce3a81b8c038780f6f81f11b83250b1ed750f0d2732f332abab1f854f780c34ec4af3db42b048ad3377828785e680d43e4da611d579e97de4b8b2c7c7f8e4caefea89e321b5fe868c617e68dc3ad178bacdac9c04a71a9f4f6e0005d1755752d04bb84a239b5ff2cd50865596899ca1cf3e5fe2238ad0d87e60f9316e150c8d873b08f7a08cc86576b2c79d5acc09b8adb4009c6528a54041815ad12a0042d1a7f5e3dda474554576152339e9ad1faf866494b9dadb37ced17c7c4d0ed7c8ada71d3202246697637015ce7c71d3bd6f1325abf119eaf2fef79e2c15c9fa7325a1a638fbd5028136684fc7e76092aa31c3f3550255fc22c9f1c89817236b743bfb573262098a462b22a4596de23e096f79956d744e9026aecdd18fe05af37ca753c6136534ab3de739f19054798923a4bedc1984f5e92e18a090ade04e7c8e8d41171e32d38e41514000bf47d3b57a52a07405eda8a9ae0bb5f2f329467fb89c10164176e2df92ac84c578c384f9bed282e0b07e3e5a46505e2d0f8e6ef0afda3d054e7e86908b1be913253285f1115b4e9931aa0f7c11823936d2cac2d05857e0b4bc0f558f2523a93f02dca963fb21a3eac080c54cda404946b72f727cc8fc6990d624b86f2d92ebc50d73eff95e6f3d6b9774a943e9c42f027669b6198a80fac4bcbb12b6df2fa333fa408dcbaef4016c3860b18274903728e23079b04d026968e5a3a07282190b052f0fcfc130e0d433a8eb485c318bf20e6d88ab6205521991bbe3adb22fe3c67eb88676c5fad8b9b3eb4a4fd405bdeade12df9fe364edc2114449d2620257ae4c46d534e2a41e5d09707e814995c550c7038d47c7249db13c408c308f782bf85f55d97f897e37a61e813f28e73824720d7517e9a17cd5320f8edb31ed5980ce787d3bc4c490b1067caf6c20448176731f37f323cc8a1457c04f7f26043a3f9d1b8d814e3c62b7c454b36d158da504addd16639b109602714fa48cbd1059970e4b7a2d4cd4e07ea37dfdf7373e1a18fc6a9d5b31497adb38cdf641a8837ae31fa397d79a303731f4ead154cc48aca45f0bffcd6d64684dd7dbaf5c7b87b6a04aaf049400d48c1a297bdf2621252653aa9e6638a24d39c06fbcf75ec2f87d72b15614142d422db44cfeeb8a15c73890a5b597664220c451cb0524579543bff334149951337c918623c4bd5a4b4f2327843ce48cfd78e24b716f18e2dea98361256ef990714f5db2f767dc5343ffb88ebe8f7861d07c9bbcdb843b66bc696174c421a27bb6e98b1b114fb515bec98854ac57d9040de57e57130816135731b67295d3923796e16aad075132da0947464dd314b4b2646d4e2ff48089b4fea90f0461d6b4cbfd8100e64523a02773ce0a3fab64e6955b2dc76ed4eb52e7231cb328c714bbe4ffc9caec215593343167eca95d80de326b113b0f1352add7b31d634103b7100469be7264a03e31a9e18a44eb0432510b6ebc53bad80323f531a8108c7accc67da2c573fce609ee4bd78cce03f17c6e24a31f37f2594b5d1ba0d4b573b1a4341635da765363f2840c748850c44c5abe535e0cafafa284cdd83facf9ea77ab4312dcfbe7079430c314c669efc11fa9730d3cafdb5a700bf5c1dc58443b76d2f1ef700f9935a57321f382e44e6b92c3b700ccfa43e6b9c4caafe48b19672062a126e26f3092168983a888cceb61799fdcf46b207075d13fdce31ac66a0905b6bc7fe3626d19a51b461b3a6887be4c55ef37f25a431daaedbdba49a0b14e9bc0ce4314c2dde4a2f7e8db506d6655a851ee2f2c350a78a5046b25dbfc326643802370ece24a4e445d7deb158c71a9bcbb02e6e180c00fb9eb3859c676d9bfb5e6415e93505454adaed082eb2810f364a619b37a69daad3431e0cc185d5fd28294b4e01ceda81d819f527f032771e97324cc8c94387aa6b67617ad4cfacf611eca215866d6355b14a6013cb2dea0c203d685bb19a6ee84517863e21c9c4ffbe2eada1ddad0430cffc8b49a6f118ebc3cf73bf846dca495129d3be32f45be73d2b126c500fd6e4fac65e5f9c7ae21d922970e43fdb2c32d507c99e60b2d67a5efcc1125302bf2dfc6217123bfe65005d9055ca9659c0fe162ac480015502b28d1fa26fa605e6a1c409096c70e0027cd9d214c8d5c82b8ebcb06cc80c05ad7e8e18cef5974e9f07032f889f8656e7a8187146f73911058b549f4d673f378e07be6dcf456db0f6168fd51fb2916d6081703634833e860f601737315c6ba830b14a3212b7e30d3b7d9b7dd7ab340037b2a9a60715e175acd103c6067dce1cb6e65127357410a9f6bc4dab2980c3bb30a911623d193d1be1162243e4447ad47455db0e3eb7e036f1ee08bc825ff2f43deed1791a4c9484525e3f4feeb73cf85a3cab289544c588f452e74beb10ab51bde1d8cdf162cbe246fd0a4958dd75713cccb3ccf6027f71144df0263c46fd652f25a15c81fec0739798a593dcdddd56db573a0aebc1556b45da8f826de5bcbf40b3760f6c8ae86c870c034fd72dc27333da0c62e04b858c1b0bc2e04896a7edb1bc9716f065f681e97724d7b653f8952b3ef9dab0fcdc95709064aa66041c19153ddb10183132ef5700c1068ec7669da674b56f3ee2111fb33e8d02ee44f8fa49353011cb0ecb865e52d0116480b9673d063f9ca93d33cc429259b33dc6ceac1d09e8bd113e80957871b327b9da37c9f1a6eb120b46ff416836669400c587eb62c3762c35744c22494c6c97c0447ca67b6e60c7ce7aa7444046e40849b290bc8b37208c395251a26e843977eb2fdf64a9cff1aad991be60d5a17d1f74248813c77863f37420065dc4886ff338bd69cdb46346e2d5107b014f6ab990e7169760cb9b9aa42a19f6428ec8fc640a8978b1564427dfae96f63602dd2646a14efb2b1bc191d364bb0c8ef68f1301de8e2edd98260a2a98034a2606dc383fb36e208524e19d799c18a913ab05ddce6e89915e29a26df4be867b87379d22af9b0b60d54a7799782ca38a1f390f5da577087def7a70b9b501729ca66a08e7770fa695aa0e0789415d7e6dc901ca6b75d769a52a86bb42d8de711fb3b83c4931f07638bccbc113c2b5de1126a45d0ef455427a22d79c5c22876cec885b88e80e40f6dbe3cb852057e0610bee421d5b02ff0f956a1a027ce331d581190187118a7988e594c16429df372c76704bd81951bcbc5e109fc56336b71d4caf960808092b06d3bf7301b4839954998e16f5924996e79cb5290c81b96a34d6e067f894cadafcbd2e2ca672cf96352e6064ab72cd3c7be6aaf35a9896b76ce81ec1afa927a30c2562bf7982aae4fde3ee893ae1b137d9c01cf19621788ceda0073592ea93b9dbcd4eb9f8697bd6716c6b3a9cb9efe994f1924f8306c34680f71042b0804f4dc0f600721efdea709803520bb08bea44d79e0aae25534d2e58ab3e564ed356b5a7c4bae03fb3e8683e0e507b37f7e9f1008ff0384017ea1ff45f2d46b725c10ac1b457e8c0dbc245d3b630eeaa26e7177c92c0d3b02c7a364e3ff33bb0db938f51de7e8e5933ad8ccb64613246d12899651599b2d35dda96e99a179bb4b15acb1a3508d1f7b556408691c5f169123abd9c66237443d96f9d4d148bf5c199cf283b5db45b0e7183e884c43865c7f3567d2f7e937198ef87a6266697e3daee88a68e2d374eb7b4636acc77b9e7a8f3cdb7a42fb43b1d531b2011d8aab383c5ed3def1059c01245a681efb10486e94570191376bdc20d663188519153dd063dfab66bbd551373c0672a3d9487d029634cabc5876413b4c7785c51e8ae70dc11f050e3b1a64d02a683094124bf966628b8e068390a815f7f79358729a59ce44697a9c11d2a43a5cf1572287d3835614391608cbefedccfcc730b17c489ce03c5d9954f2f7a24a11236a346feb1d3f3250204fa0eed4e8e8f45bc8e8ba9b10aaf9cb8c086137a75589328d3657bcd94055b85de9971b430508f1f824e7b8fd1e969caa6acc22aa23fb2e904044239d1779a0bccc0f6525acb21e760b624ffe5a645388865b7c5db599d6096723f1e0a11a8109a734ee1f21e84058bce70a939426bee69a1026111c802593595c890746b195c18cd42acf43986405f82389520df7eb85605b81c3d5b4516d771f22a6a89824fb651eba0a60b624c77ccd50a1249b6ad692183f25917c96e447cb60122b5b911c774692130347d8deddf74d9281a088b929047ffd07fddc94f6f59c8a249a7d0137f038bc0c7ddb825a6351636ab377a54217c148ea145fc2545f779b4181b8403fa22a92389685c9a8542682534c26bf4a1847962bfddd31fabba703c94619ad48e4c982d5e71643cef7e1259a7109d9e5a7d281a0ebc4347120a4244192985f3a5ca245e0f8ae86700ed946f00a02599e9bdd1d5dd3be72c471b0f3074a15461616ad8f8048508354b727220b2959104d84b80185b159302858c7a139d2b17d0a32c9594f4207ade5b51ca0e30eb67400cc8e85b50a5f28feef9b5be983bb277e40c09fa40520957205cbd7f3e993383b518d2eac89c107aa03299086ab82c94002590122d423526b08d0e6fdae4b4b2c7ed2b83da3428fcd8984030eb5bfa2a093a9203b6954ea25cc151d73c182564d38022c89a43f236e86995f82b923dc5ebdb0402826c969d42fca782b95fe01ed3379a50239446922317e67563af0033351bf28416815746cebc787ba436d66ba23a3d0308a2346ee384b94bb48ba865127928b9d220b48c97efdca93fae75e1433df6f9802bcd50becb760302e69177285054448c61ee5f76b9d570f9066220534a11d86deeb35f7ee18b9d82a82e06721c1dff1fe039cf0faad120edb8aeddaae051777bf11aa57be06d74615d0772cd1783234d8fdadb3743057f6422e6ad6125ed2292834d149219f63e05f7cf258f0c8f5e5148409116ab83d7f731cf02a553f81887b3903eb4fab00aeebeca8cd29366ecb3c91580ea15adb83aa02e02e07184df61ef5f15985e1ac6db9d976616ac85673fb471c2eab814a2d23c4f83549c1ce7c0d8dfbe67789fa2b0b09ddbb01ec673e709059306244e9db32a7cd0f4f9f2cd4658c68a1fdd8e6010e1d026bce66ab451045e18f861c0735c3a60d6f2bb220111e90223f5125166e66282b72db65c4f4f581eaaa7f2b79fa23e0a33bfb27f561346031fb0812a2a1c6155af99e406a80c33baaff903441c69d96f0a3904c56c7e35ea3901fe440d13c6716121fa25d7e49448a88157eba6f650d50e02ad89d0e8dc02ab765488e049a1e701afb1061de9b4390aa12428020f21bd9a0a23f1b73bcd8de32a4d076c4ed6d0edc60c52fbc1cb0c4cc7850f1d10755faed489edc0b925868d0981540afbe5ba5487bc28d6ded347b0d0a0137d4732e5cf996161685018e3c7d2b900330f2667e9a82bbd074b4ce64bb85e6f2f3e28785e6b0e2b99a4785c3bf403eca880bc787965b5079e63f5025e4c4b3cb3ca4d7dd53b4c30c0322b1370ce06fcf188aa477bdda064c09cd63ccca775558d45c9d127542f3b63134b80f82a53b75091b826dd0d446feaf9e97eafbdff7b7eabd33366b5310103c652141d439f577892f5d0eb154eecc3ade4eb8ad9c26eb314d8ea205e12d17077b752e21292bd69a9e3a5ba9b2a786956328eac0d35432dcab5b009d03d35f7a34fe39c42fa9e49d5763a734e8b0d3f9539d1bd0c3288bd6c26c4d6a7c202286ccf43145d921d206c7206234557603067703d32c5257bef912083ccfbaac09bb3846417adb8dd823a047e19bb001633d6591ff6565caea25db2121e10ab7a7b25ce6b28b05b821eb41b5d1ad819fe036fa53e8817df628b4b7681810fa8dd9f2eeb6197ee37f12dabe44791c2063ce607c874e79683fd441243d54144fd2a40475b772154dbb2be2330df1fff6db04ffb82c39c8bce50ccead992c56c6f09dc36197221b7fef7ea2bea65b7206237d32430578188a4ca203f7b1ba94b012d16bcb1485d754912f53749c8d760aecc70fd7f8a35b346a6efb530aca2d1a7bfafc3404170650429b07f09f72140ae078798680edd8071c8995d0cb9796a691907975b6ba8168d7e9973c287397dfa401e8ad47bade0eeab76fa47a102e707c147d960aa870f99f494c4143e571ccbdf32f79cf6134187b3170f5618ce44d5be290d31d9a19738319a00e66d451987c1acb5bf2445fb02b5771193aecdcb086e1281e6c15c8c13c7f8b9bddf936d8ba73c41335b3807f493a4e581e8b7f8b066c6405e0562d0f9737014be10faf2b3f36dd0fac95ff0022083e00425cb96bef9b6477dca946fe1466c367662aac4e83c2708b9ebc283e4de28d92316d62d335ea005fd531c96dc34d10e36af8e8b3d8b6a7912a30377c8ef8d0b7fa914b95ade0855ef8457a313ad35bac31ad48059abda2ffb4895598d1ebc2bf805627f193a4b2bc61f2280fb75aa655b0e427a14a0e79abd5ff2344c24478885eedfffd461216c5627c268089e8b5a16a0e5a41027543e0fa31480c212ec46cda9dd4797edad82c9fbf385825214d9331362824742a45970dfe90b3a21d9ea124ec478f86011689493cfec428f9e78477c42340c207ccfd9088861f9897815185850117b8fa94e9ca4b8684746816948d47485dd40708bba1c218e0da74801c05b190e0412c20eded8216d4871d6a4b869e65a1ae2462f1ff983a71b757c8c42a8dd375130c6a32121d22bda395dceadca9d2bbbf0677efb43fa0ded84bb0f40aba920317f0436b35f96c0d76ac9be17bb87886d4c745cfd416c4e1b10aa370ee30174a760ea796a2d265af60eae5f6a6245bf28a8c3be279d4a1781e7e1893158f133ebf1d0d7460817f465765a0e545c8d61b0d2547cc023e62ad2ee741798cdaa218bd753b6595c6076c8d1f0da89589ec7b07f3671d098a0479b70040fbf3437cbbb81b60433434f2795328a0e15f58aa42a61654e29332cfe10d6f25efc1b5d40c0520b94344d9e463a501b9393e6cec1fef15a55807dc181bd0b75faf9d0324c9c216ff41813fd617de51c93583ec0123982bf6c1749db84152357d2ea545087d48b89a033f4f54a8372d4ab56ce3b1ac450d81f69053ef372d379a1c7a0540ac7a24114711e08f85ead3870bd92d92db746df7cd7ba24bc6ac19eb8e1a08caffdeca19502f4c90337077113f211db1055bc0dde8fbd8d5be02aed29fc06962e91aac9dd3d8858374f40dc06db90f9a9e588e6f6fd90e20b9063e57183cb544e726ec5a94a02839308859f2cc3a609639647c80e81b0ce86376bb8b1085080f705295010f1d3e3912634f6b603d158acba7abf7a9f815e919988e16aa9020e927da69bd4adeea9afc705d5735e81e5adbf47643e88f9629a46b3971299bedbf2e05d1c0ec7d4e7c3219b256500df450d9042c2b2db7fbcaca9fbf282514d673b03514b03c706504d281c1fb9af02419aae94e1dae558341596867ad56a2e0d3a98169499b61e9c095be1ca01b09be153adc014935b2ad29b40ccb45f52c2aa089d9288f5409038acb2094078045267b96bc6596e498ff730a34f6a4fa6e2c3828de795c649a2866029e009382fec8a0d92e90da0cda6c2b84992ff75dc7be6b8e5f39be7c1ad5c4fd4ac5704bde5a2a0ed77edb3cc47779770ff44ac4f7be53e739bd0799a420f46217d1e6a9fcf876111c796708b2a508b37088aaa3975ca087a05ae7c259fcec9d83c3153d1f8f5eea9f17a8d6412950c45c04e91c8738793e8e21832d7a2a2b52755452dd17b53b1520d6868a7119f94b56ec3bf9ec27754e1f8ec30ee56eca4bdc4009a60f05d3618ebec78d266e4da293bbec28b05999fa9cbc8abf8c2a95634d1996c1745d8654e0580d41d48765eef94679790861f22f428872c64180283038dc0c741573e28686907140df7d5e299e6c5b8a2ef05ac1ffd1950ec858bf4780ccca2bdf06b42c20c51df415976a08fdb1dedb0fd7d021e3e1c4b2dcc5d5b5d77a24e671260a4237b29981e0a5c43558a1b56e636f97ff5169966a2ef2282a1ee075b6e00431a936e130e9a820a8d9ae34191240e7102eb753c8263f83afcfe1624b4065d3cdebf60c476c1b852155f6f8fcc3b2bafc64d9382d1cbd11d6e59c36f1272adc52ce743aa3c3d9c96b99be2f071e2704b32291bd74a80c2ca9ea9a26e25475cd658f864678efffe1ff456d9276370dc58315333e6096c1f3ffc30a1c4159a64c8591a4b997c24c3a32ce7c1bdfe84eaec3a07d0b7b6fbb8e374864cb49bfc3ade10d9babc5fafe88279ba5ddb805325d88d7bda0e4f229cf694e2a2187f894df0fe1465bfa997d8e10c66a35ccac58dedf0d3a0010b24e6a2c12867e83edcad13cf7663ce119bb8d48c0c977e3319fce73a6d3fb6e9b10a7fd9893e3d5d236c9cb03b0b0ed3680404075212903fbb664189ab5b9d3c5489349dd64a81dba502dffb2f9ffe2e171b36f98eabe52934f7d58d4a893da075e9cfb61d7f80d53668f764bbdbb2a4b0822df2df6beb583810601d36bc0c180e3683e3cbee52cb4ded0e4587c4e86d9237709c9e2c93c93e133136c4e3188bf25b8665368e489bd87c0e407faf38ed0f8f526d25d073aee60fd01071defb843bc933eaa99d4c1a5bff5a650ec5143ecd050a360f9e75cddb7f711fbba8ffbd2df4e509f0d2614d0ef85602262ee633725de6d102101ef6d8080c0fcbea500ea1fee0f981b53ffd6f77b735b2a5522b212d201f8a7fbd79081ce748a4f95779db0aef77eedc3afe6fba12fe1a6414c2f0ee5133726827bd1dd2b788f759cddf5b215878a2cb3aad2d420a6574352d733d389c02d19788e4bb9958bde5cb86dfcad33c24d195d56728b15614ea4a4553e290606bc8f0103ff3eef8b7dabbf1cdeb0db1273f37de761261bd966393628017534dccc4f3204588bfbc37760dc4a263944fc2b35a787acc4f6c6e912c458a08ce238ca2e104b6f8d7e7d43dc03bf26e3ace8ea7170e18a55256cd694ab3f32a3d838351ec47add1443284f26f0f13656efdeb73ebb211cb726b28c5f1c6519193212b8dbc8042873525d136da06596a3f4cfed931766ee502e6a1968b5ad5fae3e23f41aa4eb59ddbdaf57b75606eac5d2d8cc14adc5be57b75738d49248e2e611e8e5d88034b2aff775df9f7d36f132dc2cd385089ac4e61c331d30d995144211ff08eb37b85f8aba618c6d3102971f07c0dda6646de24e3f92fa323a029611f0b4f9a5ccd6e14c1429e4e10948eeae99f1b40e460a17e29cad00a11d88f1a4fe7055e0131a7b2091cebd4d2cf1e0c08264fe128cfa2f95a2910b37d4ac2c86ea313e45e02bd49226c3ca7c0e8e3573b788deb087f94337b62c134b12a3291de3d35848974f950ec4d77822c8d0fbacfb3f814104fae5c3c46bdf0907617a07deb27bafb0cf0e82f2a103c0daa6897e9e017d988b103842dd3c258b4b5e7a99f77ce3da4985507132212e56558983ea53e23c52ddb4fb149daf24cdee054df3e3f3863e40450f2ff1d892306f934d98e029fa929fbc799010883e7f5968d932bae83613f008043a3abee220b67315e0bb8c6a89941de6d4b90e1664d88cbbf4c9a78eabe9ee1809fcd8b87cd61e8caa50b663e6b308921fe509f29bde6deeabf577cff906883ec57eddca4d29d5bc4ac010ac5ce2a14ece0fb834e5e18b7b26ac6bd475119cf9f0421625071817b146c5a0e751b952fac022192f78050370ea198513055291067c37b37dc3b51042815482f41dd007a5b7c80fff57c941e1d7b1f01f0b196257e536541f3a29a0723e718a19a188aabe6a20d630a466cac365d1f95be9484035d100b48b29e141afa16df78d8e3f0f1c9e534489be19d2ee6389811e86207e601fe5afa1824d730b7665c0ba55958d68f7af6a7eab9ed802cbc32163a2418fef7276d2eed8210729d0829d63b1e5a5222f55dd2b392a8da58b34754f3606cc2b723a5a3ece8f6389baf21c95d91d4743e1666643ccc15969fd5141714b30a9a73d647f7dd4b4b6ea2380b9b2a33cfe202e2ce94f615782dfe4f4a61c0868be12d6debaf37318f32d3c59812542576e32ce58b4aab773f2d24ea282acd03fed7e727bb407b2da611f8c1f59ab4ac8cb515a1cd96a39871e5bf0cd7237665f2b6e7e3a805839e3bbf2aac6808e8b0434eea516d7255366e13754cd3cb6439b6dd03a5d1f62aec269ac1cf670d4bfcbc297d4418e823e8d63f2a256a142a31c327f3e6ee40368f465e07255484f8068a1a46f5401c78a17b8a4c0c39402de3cbd71cb294f91c5a3d578fbed0207a363d35a8bf7fd5ebee5f7f3f5737adcd4c7d390e04a24b87137b36a74dd91ec62d197570a677cb4bf4b626056b51ac28632302761c27f6e8f4814ba7f88ff15fabd97bb8a4f9872cacc1e970f12b118f235aabd43fa8d5a5fcd509abb03d46c26b300ad24488c9291aa43a086e0180947840f0f5775e8c9b4e5af33e4897857875e22c35de80b0d249c0faa929f73ab4541fffa518549d436de8caf339b1f3a6456b35eb199cb6a05c82fa9e96b09a68f930e635e204ee9038e280094a783348feba6158fd8b41e3f8c66ea32e071e066c0b9d048caeccf5be097367484985fc5a843d5b0f001cec072df05745d47957a68a1121b0a7f94ff3d732f865b7789400641ff9a545f2cff2a750601a287c880f6b7276af38c4c7582acf5f41461df3fd5d2295e864e97a1533bd725654f94291737eeab7629ba4750f9118e8b131fd0682a253097fd78fe4267b9662cd7acbeebc82318abb7886688a835b18fc85b4ec91ab0207a4afa13f6ad192c09808fa5ceda217b17b0c8d5e7e20ccc7088ded78678f66300a2a982ea80f0534290694b16ca53e9b8e389480e7c2e2d733dc9e8a2378b7a3157a0bae8233885003ad7ebbd501fc96aa7cfdc08fe688c06dd261dac840704b43542b9e5389422846d5d27d1950c6a40e800f03b07900fd9c6564cd312480bc880d428ba8fe4812475a41c3f301b4af8f19ac43d371687a11cda8fdc87d4868f43c9991e94a9b53c51fd67b82d559c24451933eb22e9dede2e0946d9c61cebf2bfe09d94eee6f6e410c595d2e770fab529b2a2f9607b9b2280e320d7d6b5ca1cd6dcf9850aa300c5714f4dfcb6320332552bb45667b9aaad4118faad946cb3bfa1c3daee34b99756c7eb681d116e418fc697e0eda2d872b2962701caba7942467c097f3bba2068860181e3f935d04292c19816a4d46bb1dbeccede72b1b00474b1de160731dbb3029763efaf1dc8c5c6a2ea43e15118896686563705eb7c364d9d800912f1447dbe0a5ffd5fd92234eeb1e312132d7f1c7cbc80065252137e7f842598d870d800fe2c540285449058c09ed2f1b7f5171e8ad80d18000caf9840ed78eef4e07914efc0b3829fac53f21ce40d17bbd8c2ffc9ace978909d5dc244d55fe03bdc9e1844696b4f4d801ae426b0ce8a86ad3e16ad7fb324039be10bd8162d933b09ae8d9520be283a500dc9353fe8898b70bde56d449fc091389ae4cbe96d2c5ca8aa791f838d40dcce19b56ef6c92d88d79b0c9e7ae87de26d47cb540487b80ec513c7793d904038891068f0cf1698819376fa8b352ce56adbec09293568bdb4e099a58c716ae617b69c78c731a7038166a43fca05c2eaef19caa4c425053f957a47826175372a125220fdd58ab6f0d02b12da8b56926e04ec4a8edfe9fed0321aadbdd501a94019c2bb96fdea3ba2645d1197841b2add2fc5d5c837b3334714ebfdd60ea9b6d021fe7fd468cf58edd8c6ed1a90222c9874f3de69a26b74d322bbc8942abdd1fa622934526a0746b69b2483032bc7a789fb33bc94d826ae63769222b50a0a67be7f804ed819eb556fb35bd260cb2857a50cef32164bc6c414a78a1504bf3bd08059109cf57e9611f55934e1179387266f1ec47477506e326f7dab99261d568f659379fbee0cf38ae81c0cf988e250466cd033a70de8dcfbefa0c58c2ef707448cf1aef0b7be9415b08e1df101fcbe2b49d1ba5e1c22db2983ed9aae7a6502af143a479cad1c3221750fe03ebc24057c111f856b36dcc3b01f127bc283c69a54ea4b423767d06d7a535f4c51d648eaaf655343005c03e4713733f39715467c129436c627b526306527dc8d965421ee4921310441ebcf07dcee60d92a87e0f3c11f1d436681e176f8e81a56f78b20e07608a114653800c8241194c14217bedb270d0a85e61eb993a29576cf77cdaa2c79a4c9b5026e1172d0a30a9c57bd3236a770190846d0be519b60d058300a18481c07cc0d342691179e2ca010aead2ea8dabbb103a2a14b3fc8aea0f9944f75cc2bc48cecbcd0d254f89fd473f2f19ba1c40cd0e2d26a1b6d98fc92e3d585aab68d2b38ff988f7a16c9064f9bc968137ed1c73452c009a228b8ac0d224352ca052617734cd24c610cb1492250854adcf81469a4701f8d0d02efc3e944dff76e86efeedb3d8c8e9124b42fe6c81a946841abc3490341ba4ddebc39dde83466e3918398f393d66f256664bd89cb8b57eb423e425035bcbb8486a8715445c80fcd6c8dbd335465fd4530abb41f6105525ab9d83cfe30324fece74f59123eee3fef4f25fb25a9189124b55256fabe9365ef435e16b48bb78e1b75fa6a0ee1e4c383514446b4d72e95c2e36555a3cad7001210e4fe7a67c36cd58cde12a657b1402ead77dcc32db34c1dd34a3628c9b518e5c2695c437d9d3e60792960a7ed673411feaa3cd07209dde7e6c0156283eef6dda6bb9aad4c57dd05c1792c6b607825ab0b47e4a9e0c267748328477920056abde478941f12b0a8a38fe9bb5f695b84b82e80f24a74f13e96b1a8e0b255022170b2eafe70d3ce53f65c9afc4fb8348b90598a9617b66591634b525657e3e24b08710c06c5969bfc6ce0d24ade764b4c2d857ef0db3e50b5196f1c5f1a827ceb5c7cfbce58ab28d37ab1dc9ef3a0c8ecf252b806c684fd4f75b18279bf812e51e4350b4069c9e2ae22dd8f181cad06258a8c4543d88f35bcb540c95e13347d560a0ab67a5a1c135ccc23c490c7fa3c3e934020ab6b3652fb1c6b5c819c514b582ad5a66c4194ee2a5f4edde7d824fb48e8371ca629667b84219cc30b0fff6759aac367349d83ef960ab6865acb24a6a7cc7eca1e8452012f01dd20d2b467ad84f033c7881c262fbdf4e60c071c112a9962102c8002d3aee0bf2378ca56f129da8bbc9c049330010936b92dc63e9f5a21b998630eb5882e2b8bd01517c0daaea8af9aa2b2e54dfb53ad7852c95c3b32a3a46cfb7cae9fd5340afb0171f63ae53744da451796bc30f3aba41f2bb99bcca0099fad28ab7a813f54677c639b4bc962b5a9b77b24e0852a29e4a0b720a926bae75202b9f2a44d811e7c823b77d8219fab97015e10d8e549aec085aa7927df32b558c02fc4db9c5c7b81f5f3d9a29663c3cdaac81f0f1c167405ef2e8e2abba60a463ddf80d1337c55d133038429ab6217a81908f5b9b9b23517b964a37fe090ef1efec62ea9bf2a14fc7e18ef3165dedd63d3f1bb2c64b925b6bbf51fadd04de5836e74ceb5cd57e2cc8a741b6d6e24145e159d1a852235366816b938492f085d98bedfa8a955d58c7cd73d9ae548a7e5ce6d0c8b7291548280e3263408800231299049872da1e42116a7bfb7afe14097301e2d0926116728eef9db938d209d888921b8aaa12993f6570e728081aeb50bda2c6ffb5fe9e9d98e33bc7df9d952a43e140d6a3b06e76220b769733d9b81bc6c2c7b74e81c33fa1b31e0dede5df22b7c189e1848f704dc7051eb83334187b2112a43bc0628f802356719b0186d1caf84238b2af6cf719e61a27f640b3ecb06d33611f929f6145050c43ef8718cb099ee9ce4ce008bdfaeac68b59ebf36024393e5cf679ed5271f8d53539eb40c316ede5009962324cb1fd7a8beefc1ed507d97174cd7e7cc5a839f133dc90d0fb171a8200c3fb463651dd033dab012b34415ed13a3afcfc25ddaa462d1420c6a86410be35776359938bf3ba99cd195eaaef4709754cd6000c7f0e0ab62f49a92117f2ba9c603ea143f24ddf45069b263db0fdfa2f09697ecdcb2da448f9d53e0c029e068e539b8c2dfaf76d382f9ea8d95304f57c5f0171224e85262052032ea7c2f1a0083bc4822480eb55e2e09b504a5a8bff361e3378c38fe8c20151cc774a8af9558cfcde866f858e6459fd3608b1e13db70ad36cce5298769b27a8bbece2c99dc99f91089a506e36e42d6baa71d396735f3cb2abb0b3d6250b83c0528c2b412ba00be3c95ce0c83e50ef944f05dcb454bbf7c60ab8ecc2ae452d0beb89693385bda25e7f87e19a0cade78ebc2ccc1416076092a42edc1b52ad8f1bfeea5ffc8f91a245cfba7bc607184bdfee0033361d118fe7371b061395de9c712bcc649b4a95dbec8a29ed3339f83d5418484730916d243f7a3237476e588dc8a5f6c72e2d4aa88b29159d5fef08291ef07444d588221a63af7992fac83ca35409e09c5bc23a72ad641221eaaa098c90cf6383dd0219c6fa8a6efc24cead13fe4999642619c61f7858b484264fa17ae8da2ad05e86f1a50b537f1fc47ed14579342f1db00b1b85d2cb9cb5b53590a0878cd7423866961ad5b776c6828fb819d78f5a2ff62792a53e8ce6d2ecc94a3a1cfa635b8bf9a5a3fb9e61888299662ae648047151c46e303264c517b5447c3a095479edcb64c65b76cc7c5d98b6d191797d9e9605b5b4606f379761fc669ecb196aa76c9165a8e80e2c021b36605729d9243c2b6f0419fb9ee1a905cf82adee5bf298d297156f1e6d943dc70a3fc40c0e07c59e9a9866f6b3119c7549b05bfc0c1bd3e652f4751b8d56cdd52060976b4db19fa827e895efb7698ecabbd038c7456e29de81e1e061f65075f4c8244b8d4c004943b266402deab8b2921e55c63c5fd751782cd0201c78070b68a5ae6a2b21d9fcfe3ec6ed896f4f3386c4365fefde1b6de95fd9c185238b206ef6e10ea6dbeb4e1f9ec4686d6c5270dbcdef08ed73bd33053e2f84df5ce6bbe5d4678c07df817f9e0f9a9baf629483209dfda20577bc5192c09c89238e8c98437e22a4b46692f003028a86f8fbd06f251d6ffc0e9207daa01de51c1563843888744a73d8b61a505fc576d26265e4c5b5d232b2308676189173f20d26127276e98d40b9a3ede0050b2d7c381b7c157555bb16cb36a9498630a6aea33564bb99bee57596f0a92de5f3da3f8801801161375ac220c96acd41537540fc6397098681da7fc45d7b1cb940959f229320d70ac517ead4c59077e46e9abc8acb386008f2474e8adfc9162cce5f7375c3e001e84f69498b4c7b67b64e62996e033e9ab04d7b439a04e8ede2510431b08729ece639c07ec3c9e5d7dc4ec82668e08e1b7f5d6489bd065e8fba125f505301951b0a985c7f66cff44cb49c7b62346c0301fad1a61c6db6f5b09ef16ecabb60a83a24398412f43843ca833f1f363f847155d5daa59c17728f8146b315d12ca8c238eed165b0fb01141b9bd65f0e6730a32b3b6aef82aeb4be9f14b993e7041b327fe9f6142ac7fd3654c271005873a6a41d4a404bfbe05b77c3d94abb1003e7a9daa5f6e52bbdba3eadc26aaa4fa43be755a6634d5cbbc6bcc87668b4245d8076af5defee1593d2e71e0ae0b269b18ce6051a6758669b4eb5a6276d280d7f870245c3b185c92166d43d6f026050714332d109fac8ffd3ea3ca511740b6d1d0e4d47ca048761a79946aaf5177c8335c3a622133500d192f412973a8160e464548a00e3261c8ab390fb2474ea18dd716bd9b062261e7e8f6e99249642e9aa618289a3fd9f6d1b925067b0580ef27587ef3fb9f23db0bd91b3c166d1be5e962b9320261cdb07a8f9df29fc698078a46b9c200ad9cfa94077537d8b1aba332f3c361bd0f8fed83762cec4838dfb2d861f48f1dc41636994641975336f0a5e667964951c40aa070a4812bf1fdbb01d8d0b73b37c3886837a06522a61d8f396e4509eeb0d238bfacf05ee7afec6b713a22be268a8944d7f0f0a44e3480e922bc0dadf40caa44542c5a2759e4331a975ab10f9094f1b56bf23cbf8d15386ff9d0c5c47c6e0cae4ffe5046c4da0ed542ebd3f8869fc872b1b71a866add2f050d7eab7d293eca5a86b072db2db7283091c218eb969220fd5a3f3d18253a23c0852fba7520593d00fbb7b03a783bbfacfea0b50c41d4549a5c3582f0299f0d5bfd0f8724f06fee71eaacc90724271b09b482a3ac4ab3f14b5c4b356a0b87bb83c55bb812130ac77fff131eed246bbb5575c355c241787232eb2c85c09079d01d20f1dc3f04f58df90becabe2ae8e4dcbb450b3e207d64be969a4fd136a2f8a051240f38a6f65210a14e3ce9eb575fb0774b838a8341fe90959a29a80a892f5e86a819bcc41120162f2248a13da26ef90785f240bd398039063e7d748fd124885cd07ba3536280ba47baff810a84a828e146b5219bfd08dd867ac61d67a3a85ae181f3be78e15d4eb74737bf7f79c168cd836de2c8bfb2e21d4fcb049bfcc594607c425de3ac4bb1459e07dc1e28f1e02e547007825fa1328034e7c2e18d03be8b713b4f21a24cd132e14ed7cce02537fcfe7696eb7b95b8ae96c0b6a8e003c9948517fb617be3ea21f5678336078c62c68b460a88541e235890a90b7282ebeac89c44b700d3f1513da84e1c9b8fa5819f5e6dc916acca2723db81fb1557659c8ccbe2b9e35b997347c4b5dd168d0ef4113a60c1e1216114d7d60585ad20f651c76abaf908d6da96c20017f26f6702ae133d2cc70bc65c43bf1e65f697d2e916007cc0c68de275055c71b0b4a35dfd21fb7d64633a04db23f49d20bc194fa4f72c4a5e08be701d0f325a0b1340e3ace5c979606571cfe042cffa80ca1eb922d362d1b968f8750f1b95d200c45e9ddf396b9f5563d7b47a1f1fe7db27d31bb6f415b9de9f32950ff0b443e35bb8d974d1fbe2029657c4334db586e8c440021b1119a8c87a3430e6e8d05978a21ffbb56347c176834fb1eec0da0a6807743e814e575f3a1173d6e11409af62e7ed7d160ea76499b3849dd551ba9ebab0a1e4f7e89da279e443c112569c9ef0848e9b88ff4b72ffef58c9eebbf0073fa80e4f38892c6fddf1e9ed68a44ec631e06fbc12cc8106d7ce3c89172f5fc37e65608550fbee699a7e788cb95e0fae5c8a36ac4f47a6207fd557fdaf3cf465ddf79da157652a67562f66683668990200c75138a4a210e439aa8a249e804423807e69890ac91c33febcbb36a947dd85a2106f22361e9d93bdf7a259a9c8f9b4e0818e432ebf4ff1290b31e282237ccf5cb17f42b8cc57b9e2d73db46c8d8175dcac384283f3d841c802af8ab43051b9c9ffac2148e8da667ca6a2b10ba1ff67ee4062bfc5fa66ae5d7a542dc197bd3bac25055683d719bbec07689aaa7099c3c00558745e7c1cfa938e05ceba3d5ee9cf2e0b7048750045011b9133d8d63ab2fcc076c41fa26bce3caabfcb5949af058951d9ac53c22c838d68a4221c28b3180d38f1c8b46f3cd4d9f3eac2b941dc718d40916f8311380703518ed721c1f042d7d18268f6e2239166001ad003339fbea7c3fe9c9a203a50e3433c8fe633879918975a8cc8a5774411bd959f69d573b791a2360bdb8c1ec23f7d5e0427b7e6780199c81530dc8d21c12f14a024f03e220a49b4204c88012c48397306736c16394d090c096fc0d75f5f1347e128077abdc0002016900d5fd6ff70e360b6ffcc1f2bf3c03b6d5e1496cc58e8ad0d6e162847c1eb282b5909620187e6800a636038096b4d4cd9f097b8d2903e531bf674e9fb159e34b1b1050521639f9d50f083fa5128c1ea11cff78f022f2b9c6729a1732e93258cab6f88502f6d7a222ca9ff41cb81703726a7f250193ce455ca70f3f918388562e19ae35e374d6aebd4763d226caa9bb28915577eafecde1ba5f28b47caeff0fa4ac42608066a6ecf646ea4231001d0386743d6462769f81f71967deba1117282c90958d42bc335215a451f3fda094e5c6a927a10c10312188944cf17a4706c05ed4e344c37d3f3e0b50e6f3abf3138f3b5e2401c84e10de31f09754988793241dbc580e656837604d156e3d5abd96dbd549e9d31c04b4ec33de7418904545294910ed2f60c70d3aaf74325f38e853b9a4beb41ee442ba49525fee31c2d4e4f2acd9913a7d584a13e6d793818b80202ed4571a626e7bc3e4e9fdcd3f44521049f63bad551b6c733f37545abb4dc07668a2f51e99f35d4338de4638588c64d0a17619c96f6920884441bf7d563c744681108fa96223862ae7bdc4dbf46eb1c4adaa31a40ec489ebed66921e3d51de1a31dc6d9aab594dcf94eedee1ec71ca4196edc8a7674ec33129bd56a616339bb502113e16c4e888b5ea4aba1c5520051dba1b438f691deacf598c3c9151c28df5961fb637feea239dd576582a441787b5f364c864fca9c4aa1679fef747ae3f488fc032921a30be9ec244585a2564eb7065fc6150d1b621b4026013e5abf5d057b53190f08bf10723373487a637355cecb13dfb6e29313157f248c62d3dc2308daf3f254f06f3f70238e671e7d9440d80e6a8da5ab67db447d51c519121fa412fa23d011afff5f8c5c0be0fab2b2da751d0d16adaa32cad97e7b753a7ba0111ce340114b38145c66af14b10f34d600a5782e15790f72b7abe4b214d040f843e9472679fdc9df40679c7da2177a6b9cbd087dac25433e9e7e549df68eda3221fea3f30d081914b7c9a204986c846db6ee2941661e60874f8b752afa147b6440c53965c1aa75d3dd457c9afd7dc687829e85c384efce6a6b0988789320372eddad3e957c20b7c03ea4e777219d0b636442f2477f060dd6385946d2df75bb47cf4f7afaeecf1e57b74ed179e9aa025a29e435fe1e7f7b7c3e8aad1e88c6d608bbb49bf0c07457f9820d8c9efde01ef6e8e72a8441f6f4b98e02c9a527ba270bb0f922788b11bfdf93fae5645dac0a6352bf39ef36729244a0245acf3ebedaa4089bef9a624ab56d18f03e602f773d8e6280ba3635f55db76e6a00fe2bf95e564bd954f1403dd3be0cd9ec81f185298d9ca704de1ea97f7694a973b1f5899bd67f12efe210fdc0a670cd4689cd43ed8e56383b01b84074205f22f47b8933f5954018ac8d98e61c6573372978f177938c1e446a80284e0c28d23103adfaff4b27d24329d1d685ad791b957dbfb0fdce9d208adeb927d9874aef367c0004c13ae0a86fc3fa18497c5fd8ef2d92efc176feb21ddddb02510e9888070ae8a517ddbc7742bee4b3a1a03a03caffdea17fb3c886de783adbd0750e388d207babb1e7de82bd80ce0bed9e949fae977dabbfe6614a4e881d0a51289672a5ead9a75dd32238660e11b59c81e48df6a45407582d593f8b3f34f79fe26aa040c5ce6849287c31384326c94edbb2744472929b3412b4b084b996b987afcb18c24b8ae4e0550b5c0eda1110111a011ea270c18852e70fbc38b29722e43fb0db79bd7736b1f0fc519e3ce6da2acb6f297bf610f9a7bdc60bc318ca762af290043af03e799962b1018f497b132476b87515dbebb7eb14cb1c28e587c58cbce3d33d66e9c90ed6b768795c83542308246582d4333971824364a40ec7c3990e73bc0b9f5d4ae59145dec0809fadc3e0160475703aeca8a9088530bb7c95d4aff4b6ed6c9d421240b7b815248c823f03397c85e2a41c29a94eb3eae0232027c3d82c9265dc480afa612fd78159eff068e81da3acd5a3aecc341431e9d86a610f2bb8e056707cb3b9f044aea4178e78bc7f0d062b51a77b41e6932f133d11075b4861ec78b9c2ed8994e2921597a07a488468ce35fa15ee4a5115a5197f62a66329a7aed8962d0e4fca1e4e3e7f92286fb70cc29f96fe414f43975e9f1379323f2cdabd545c5e6b6391afd1f2b30ea39c5affb62ee9f89ef15277f7dfa2ad35921f0b3d2b7b87df6922f8dc7c4524ed7bf955a554bd5ab27b7e761078daed1dc56bff2b50643c44ee6a54dfb705469fcc90078438e330365e89996268fa47d9792841c7b09f9df2ec14074af02cb26b708c85943cd94364167e06231e4ba6c31e46213787ccb67e6fe47cc5e3b6bdae95d6741660898ca966bfdc8b434b673385ee702dc321c93e04563cf5cf29e95e190411f7f1519b0a3ce6339759d0753b56028f702dd83be8c7db0893ab19dd503967060607b7425838271edb2cd2dc74dc530760483186771ffd0c9bec7bb086c4f420bed030b5424c1f781f3f9582156f54cd54a8eea0371bf2a39fbb647a9be623314c706390d2f1cf636b883c543c08ac7ba16aa77ad9a4667b93cbd15769a0cea2c7e92d600cce287f8b73c11497ae1f04b54a482e9a7573cf022c468a2a8b5b02a714cff14ac4c7bbf127c0fcfd47f23f367226806faaf7682b73b178e13429154edb5e544c88fe63f4305ff4baf5e7b95d0e53c4885053be32053ae00db3f4f052d4a30b05486a1dba7c1ae2aab3bf215078dc9a632442f09d8b08e9b7f5106eb72db85f72d7c493624505abd09c32e6cd49689cd0273510ff736057bb1d80471be62c7fc1507c11ffe57ee1147eb8d3e2cc2dfb8a3b6006d9fdf8bf67270535d3ea01fbc055c7c3640ffdce3a9cf878b0b057517d7fa73ce7f752a731676979663b6321f93e6c32cb615ef3c3b2892e16a83b1dc250f8902d28ff9163647a1b2531d843be84368ddd9ae942aa1e1a912462172cca85e91c173703a90ac2d0a65266a1828639a47313bc239eabe79412b44e96743a004299cc418c7a733d5c1d5940475d0898c96274632f5811cdcefdf03ec5b1b852b89a350eee2b0eba0ba749e872bcee48b8991781ed4cc5cdc4c5c7942ff8429f3cb818307b3e9e359cd3dadcc8dbcb3e0de0dc05b0bb814d065ceda06f2d07149510131107202279bdc7e3bd2677d51211f5865da3c003ba4948e5160db9404d879ef3d19f6b420c1613f10a2dd068d0424b18b7dcc36535aebf215aba4ec8246fb4c833dce17c83a06d4a1d00960bb1c51af5d1c57e5fe237a8d665197b2e42c956f8a1b262871ca2e791d1a93918ac3450284f2b143197a36e303a9bf624a6d445384457a4cd8bcbecc86e83af04a31bc05723f1e6cc7728360f2905e2586fe02952dc6489e33859abb869df56b6d6a46ed518a7bc9d221e58ee918444c0168e74ba8ea96415cd1390a56837a2b41a6a975af0c830bfca70a4f25c644a5c83016fc3b945f40b67c40d4328fba9aa4e6b26acfb27bc3c9ebcd8efa820d133cfb260101ea5b83ae634200bec90366eab172bb63cf01838b26d2004aee5829c1fcc1269863bf9ac7836d1f74f9e9ec631a9513d2012752c610043966be300618ed5aaae82b05e2ab33c1e0297fec247169bce5b62ffac3614721f4818abd7943eb6e6064fe2eddaf09c2f31572e26ae93bb008b7ae3518346548de1db3676bacd119f3c2d2c9d35e3794bc0d95d788b785627f69534b48c82b073d39ec9621821f0221cfb998d3fd1329e0586fa1909553da42c83af706365a35db5cffaad4e830bb5c71f77ce9920a4c7029a406f6bc022fd1a3faf7784e6987b8927d4e5e382254cb38320fc2b8f2a7491b29a5f33eca1e84fc073936b6b53356a808934396c738ba00034b8786e6a8868512d614efe9b33c8682ae3e56efc4832a1b4cdc178b0f865e3eba0e0721ce80209ee56f076ea7b622c39981a0243e5cd7e378d8832637f13891a76ab7fe9d8a5774e5bae530dc96bb60eb81ca340177e51a562a3657b6d51286eaf050dffa9845889ec3a9bed78fae9c0dca40e52b31e04cd752aa97c6779f0990afc40e679fa3d10ede239c3203bf17464b0366172fcc7647c72ac7097f79a822194c0e52fec351e9664096ab892be59ef697786a5d9e1d91c768037d13b4a289767bd53f241c499a13f13baed4b7a09f44ae5b8f2d4c74f1e259f9fcfb987e33772e1f0424e9d0470486df547e10617e8a08f1fdc641a83eb4aedbff2a2f9f4aadf9f6d1503719b862958c42b4f1218674b47929b136aaf84cff9de88cd10954e2ef9481715f941bc38a8db262635971e359a3b1acdd6856d148d63696151bcdaa1bc39a8d67e546b0cac659531b07b152091549c06cac0213b78e07aef383d4f5e0753e409d1e5cd783d6f9c07572d0ba1fbc0e7cc05be707af7bf8ecd479e30e1231514c69cada36dbfd902c8f19a6143d8bea66a014fd10a01debdd63e222e0f2b33930c137ba7be763440d00c7b83d756c89cc02a6060450c696804245d50ea43794cf10b03db03f21ea3c7a1b122e464cadf9ff1400fdbcdf1c9e4f7f043b4da142a898c56af9454af79d857782f7636e58b4c8b5d41536c325002a5be2f4f864bfccf4ca293b152b5c1a72cc1344e64996d167ff1cd4d774134e2176a01ea4f9f678f3e5ccbd83e3343782d9f1c87dd193828aca06ad874e712f65d9629f929ca9d8fbb16ef6b4d4cf2b12e84e91ba20281cc94adfe7db2bbd21509e6e482e004853fbe03f5a0e422173e95133eaca3d979fa0336f0477d478651a9a9011f48aa8b053e1a00a1cb6cade011b03225cee9cc0227675e31d3e16e0fa102c2568f577d0a1613cc4643c24d0417a7e0b52f1d038a361f0722c10271326eebcde6236144bd1f5518c3dea46eedc3869e90942de91ca9bc2ed7ff2428c6e79380a3b1abfeae5dc9a358887f90cb10188e164e4148d8b97594a01b85f6815e2a483f19577335e4d91bf6f784e9e0ed0d97dad7bcd7f0ea53afb6be6717f8e79b6e86161245a8be2811a339dfcac83db3d8e4720baff9e0d5936b79c75713ce2c68b5ef389cfe8fd9d683dc112fbfeb2fa5415c74e265e4e9fee8663093eb4b48bd4f681800a6d6191d17351c52af7ffc8d2164001a9c4b20d9be4781ccfcb0a8ef2a54725d3b9e00568bc4a818cd38dd5005abf2ddc3ea2b79378aa0f48424ad9e0e7a09c8064d2a20c111afa95d31ba7113aaccc9067e6c8549d640dd307d79c53727e6e1ba9b854a3204901ec76db04eee47e17540c092c4eb500ad60e228b8a0c6d5c8c0ee9b59526b2128e1c33253624181c06b51f0a09fad74efdba1c2121eea0239cc40e9130028bedc45ec61d975ec288fd0e7d2eb5af9274a0d6f91227d9b1ccab9d0154845b639b524ee5b5a91b2eaedfba29c82af83a616b9d31e6d8efc9dfa4d2513e8ded4da8b17248e2e08687ba6189f3a96ad847a26de7f3659b89c77fb972868e1fb547e080edb9c42edbbec1f422f7d46eab45d1ab5713b516ad95a22875906ee8c2857806c4ee33823fefcd92df0cb8658ebd950de6b58be2a1cf4fe9aa827f06622307924c1bb4b1b3b59e7b8d5ddfec7322387820884cfd860174813567db4bd4027a91a49a7801504782f37806426f0e19f74a4f59c2004627c1df0ee635b2fa6b20f3a82dd761ebad1272e58cb1d82f7965c320a0306b3d767ece37077d5cbba293078d32f357f0eb0b78ed9cdcd921b5346ed56fbc43e18918e8c772875ac17d3b30658a5c90c0621b22d2e4ae82b8648c146bcf836673f5eea9ba5896051416d84268f7c3a5f941d615f3107d763a26d914c1b6f04e59fd8b3e88967ad9f94fd6f455cb8b571ef473f5ac9a1e44b760e4f1ff15ad2ade6ee0104a53a98af813119d4aac13624fd6a466195bf6505f6941f25d7730bcc2e08bd0c15343665c2fb2fbcefedeb848994b6b0a927819873583a27c61a18cf353aadd06d2426825469a1b4c5fa3851e66d82aaa6783efd75b88738e2ca7156e209f8d75355572d5a65dcc617122f112493ed94f263033ca4e908ae38046c3d8f2bcce0ff1ae2c1b26db62f4db4e2dfe08cfde841dfd2ded1a96b6c15ba2ddd00ec5f49d6ae60496bb8b36806469ed07a2391d2d692bf3bafe08aaa1b2a5053eddd499dcdc9b323ce93997db5123135a8bde95285883f3e3d0c5ed3856a52fb79ef0cdd06914d52fc9c1c00bfc16a9e9248b2ee7f85ef7a1c1775a02010043139f828239b722178474a97876c431302dd573bc85f357eedbe6d71a89c2d07ff155c62688582d18cd591e351b3faa04b17b142f16ade4a93bc6c1f01c2b542baa8d92b59e11a88d34d0b6f47a7665e9174f7bce57e62ef90f7c939f2a87266f01b400788defaffd5aa46631ef391bcfa234ed8179a9a4b8a74d19bfbfb87a122abee14fc047e65700e2a3c0c3c3185edcbe20c65318278bc1b90824ab1de9a32e1f805afb2c00c0a156c5fed2dc98601534e145903518c896071d02e65a9f7e9453e39d1914a78d11dc2ff8390272e0225a32f75dfc239f938ca5ca29e568e99ab18a301071436ed07f672e29ea893fc1aa78368964a99b69a4820fa1ecd2b5e983c4b1685dce83d01b301c3a146ba672a763dcaacc96bc7be4ee0d54dbb35bb0c4a23f5be655dbc8e61ab15f9cce82e750673df208d78ac87fe3a149802951309f5e7b247ac1eae45d263e8905bba61a51ab771d43c2d23ffc00bb0a4fe9a8b39083b31dae12da68c408b61a00328687fa847cd8afb8eacd465fbf054acb2c674c2c244ad50b4af5afc9c1f666eb836545297a7060bfa7da8bb58300e76e4d31e5224361e4b5e45ea3b3ea172586f46a95901462ee15ba77bbcaa867aebb39b656523f6ddeaa9b6a9dcff8c12433380fa617996c1774ee499986c7e6b141e37f1d095f2e102cbdc4b06b655d5e03de7990250d3f478879ed20eaf74d9285be921c1f3472befd87120eb9a968e5c6c3bb772ec329a369376795351a89aa0d294f16eebf4bb50418448a7156dc385a5d5f5f8abdbea74f13ab2681ad49d4a7bb4d4d406c0f2fc3f0993b498cefd1991b1f75c0858f96c2e49d0124aa0e2ddbfd5b0c5995db19e23d02a222db6978cd045304c266c623fb5e4311df671f70dbacc03fa16ba58007701f6374fce0ca1d8772065779e287442a30ee2bad4e088c503d85d2a4e71d2d666d170dfeaa851a38188c399344ff04fc168f902e9bb9237e159d3b48b5c5f609f147826967f05526b550f06d5c2cdb200126a350ea6b54c84bb185876399602e85fe9a3124f04dedc454e2e8ffdac819505c6d02ac0949ef4f739ddc36733a6f7520846d4dd4aa8e618977a2343b2253d0f7e617bddc79f12b0404b6f6ff839bfcb00498ba49afc0b3f7c4001da0e7556418c0bb4d5cb3771c88573a6a4c8457abfeccec09215eea152c4831c1a024f20897660fad3ea0d2f678e9dd897f73fe8ce398babf8a87a00650a148de1e07dd59ca1433c15ce95cc18e65dd55a26f0776e51225ab8584abf80752f44ee6e67c046a10e37360e196fede0fe1038b5d2b6d1243d8a5a6a5b8168deeb0204172cd553ff26861d5187dd0b1848a192f7efc1e73453ba348e7f788f76449696fba56f674bf9d59818f58251325a4c5f10aa2812544c2fc806a14b370b9c446abf86adc2c98dd83ece888a78a8ec7407895cdd33d8cea1b9cae92e7f9f4e8fec81232064bb7ad8c37f6258d6dfd0a760ea4f4a695d734fb841b760789b1ad3c8e7934e5b36cca5bdd53dfacd927a3abad36fd7122722d0c614e4d282b711281a1ea444275157265c6b7714f6276876c5d34c8ff3ee5243f800eef3a1132cd46067b39caffaea48409536fdf4b09acc148472185d6ce77904be2a7f25e24f500527b43beb0f0efa1a4bfacc3c92bfc63a8b3f9c06759b2fca2297b65984dfd4ea47c62db8a08395a99193686375e2f4ab81257828e1069e95f4fa12261ae6dfe55896bf1c4321eb401a6945c98e234b312fddbc527e367ec353fa9facefaa4af46d8d4eb4376dcb97dbb74f90efdac7656938555eb49b47ee78eb81456501ca11d6df07c29f19acf71c75d9868d3f6a346642aee4d1701963eefec5ca446370d11d7eccdc098220779e359418cde4527a0115c1b0b9d3d22800da87bca60184d04a21ca40015b01a21813115420377200e3284805600befbbf0506e61c9408ecac7873d75c36d222463fed21a0935694e159c31293b6adac5e28375b6e8d8f8ddfdd317467251da698b8429c019584e7e1e3c7b62b590d1ef9396658ea5ce3080697b2d0352da18366454938e8e7b6967489d75e70628bf7e02cc134956693a4e575156549ba2d2607108bb34fe78186fe4723941d11132a799e7b6a6e3042dbd6070a80ee9d048e7ddaf19ac4bccc37984909e4801772b7cd1491def5bbbe905e4424bd8e6bcc19a2d7e7cedb793d75efdf173b0b17ab4944fd7cd7143fb786b67a3d1cf298177c0cb21e82c95fd0ed039786c5240eb9f2b504e5262eac612f9872fd781eab41004a72efe87ea9dd45fdc17085569afdae8fa4fdd1cecb5e8685b544831171aa89cdfe478ecf517f6e464fde1987cb019fa3ef4e83f6a9909f7f879723085aea16a374a2f0ffac47c5980711b12f8676fea2e752ae33252f626df3419e7e358a62054335d447329bc43860836af0f8b7154d2c5dc0ad05d2af46092d0959208cdb6d5bd24554fb94bc2a9dc4960d70bdd673d6e03d6d1d4964735fec078158822b7cfe44e99ff5e8249854b11a047c18971a49cce6b061069b02de621dd49060e9d8ef2749dfbc1a3d10ae775dc7d9ace45612950fff995d29b26de8c6bf1196d4bb52e9126201d4878155131f45d15c2d58207387d6fa28ab1772a5aed866f4bb46bc1f16894589191d423379b27b37a5ab4f389a49a72f728264c92eefd8c04afa85e8d1ceca4eebd7a1664b48ba68ae99cf01ad4b861cc533b83bf6d4d679bf2e113030fb2be0079832fcd115a8324bd1a7fc6a9b45723520371f3595b21c95c067040465a69e7387bf96265d710d6cdb4e089e35add86284b09d46b6f3a5d4f7c7aca86ec6913dac00b17b00975ad8b34c07a2deba83bd630542f280b22d768e9fcf7c2a4a3a8b86da35da3e3836a0b71281fb687de0e6abb54440b77b599e58f4eb163433985b5543dc9dd784d428a667a5787a3f0a11c931011e137c607dc3fa9f04ad95f6279ba63957cd576add37bb27cc52c0c127e25f7ad67ea4db2bd43fa52ac3582e159d726c0d432201ddee8e36eee44a53ffac797a95d683a9b42488f2c1896a773af7aed21c287b678045a99f16f724b3dcbbed1ea075726b40069a31a8bccf178e12cdb11dc3aa47f494ec00015095e3b0c586b79c46f0ad7ce1dc362f518d8ec4c628270417c565b6d3f996acb7fbfad6f81739681280f7d3f2e1ab93fad83c74e070dc4e099c6db654e9a0f7811ffbac96e853990481c833092b788cc624b0ecc0557a529b771573f664216a4462a5af7e7fe9f904ea42112e75f4fbab958a2439ae0b624141d1eb5a4e5cee7a38934fc4b9483c8a1adbe2bb51933a21adc42b1b0245cb42825fb5c6000574f7d0f86d41695d05aec3f6308fbd3b3de47f17e23446f2914cb83c462437a373c8522be3422a96532bb254e3fb80dc74cfa5f5af7075db85e8868f85b72e5a9c114e8bcc5af3d9680992312a0cb90fef5e29c0faa58fc72c973c56e8e24169171399cee3359587e7ca71a225d0932131587174f4419ad995d433e24abb8b156eeb38de9515448ca6a820aa9beca337b53cd666b6503e5b01b08925dae3d91ed9f878895ec6097885c2e795acb11913d93a80ad2ac4e2fa2a3ca209197db4887a4909664feddd79f97454ed22c4a02d9ce0181c4d466acb828b794773819e80f5837f6fb957202ab6410559a32a97643e40b927981b7578af8f4799f19178353ed0a3b41067743e46ce76ce3befb6bee86c7d80f32595c76e7ea33d4ec87d8b19c6fa9b1a2d3f69858db9490e2400ccb71d7fa66cff7c1e669711abb1bdfde74c14fac9d04a126caf9bb5ab5a84110d5ae16007d46b15fcdb46c0046a1cd50048dbed5effe502b5858fce71fb8d55686e6e59ae21f0477f687e6f33a4df7e6805a1e4176049bc7f297544ea8aa4915b5a6730e8b2c3b2d200ff0b2054baa6d26cb46cf87ddae15aaab8cadd5c1e2d835adc7c0fcd81e42a6a72698d10d889395e4d6deba38a0bc77ff9442122033e0e247bfdb225b825f2e1891dc57b958e57a83b61083da7d1829d235e5983cdb253ae86b32bc4998c9b00a664e471163d80cd352c8066528ff34c1943a4a07322bc8176b3a9946988d3f494fe5769b9d97ebb447a4b5f60ca80073ddb6e324b8fc73c959131f1b5823bfbb85c27c4ec7a25e4f846b7e09afc532386c77ef36a5ec108e9a999c0c768acb0b653be3451f989dc2b83e0dff812bb86fcdd4050be19bf70b5ea9553e10b134f642f1b6ef2fc1ef0aae8dd16782ccec29588fb37b4fcc3c58b6ca06a948dcd88198ded0ad67cb45410e06554f9856e51880f1911bf3abe0c4c27663c7f8480754587990f23e01ff25920c8996177d652a2b8900a8c28d61f7ec89caadbd843d1ba7792b878a7dd119ecf37845ec9267988c0f3947af2ff6e118355ec3aa62bf6878c5bb41015b3491e361c151fe2bd4ba6162b93872f4cbacc439507509a418f1fd05a9aa2e77d5fecab00554874638061349471f2a0b9bf4058dadf5fdc8aa236e62641a90c2e5ee315c377f9f52d83811a97721bda5ea6a326d397cdbd17be3a81e4371ab8e59dcadbbc9ac94da1c50e4b605d5431338ce34ed9aefe851cf15f8e029c702dc1ba355f5deb0068dba91eb2c3054b42ded2ac2ba15bdb3baa42259ee1cc50844f020022deb1c72c3e7233dae06c19eb4dc0623c3b9fc9b05e6c4cb1f4cdaa33bfd033fd46904275c5bb873064f6cee7a422bc8569699f39adb506341c6f0fee328f35d9a966e9320e4ecdecb3045ad2ae246714b103951e55031a610bd89be241bd8921cec4307d29da4e6e03e89087a28d7af8a104ec1c0795db59ed657c459ad73a005ef9759977a213b95ccf20959895148d969c988825a75e0da30e990573797d1a16adbc6372ba1ea149dd10078a1cf817db91b32639e9b7e4e0d3d8080818c1dc44ac37572aa5308ebbbb9f804c85136290c71bbb7c17123bd4e8c8922294f4a664ab15241ca9dbc1fb0e06850c7d4208f48d1b3b3baef8bc2a6cab91ee60a7a8e9424968532d1af40e6c872afcc7ab75fc08e27784f13dd4f729502566a138a723137cf026ccf275cbd4c05bd7f5ec14e24ea7fc0679e5acaa1d7b01a783325b9b1c5cdc12e30045a1c4a8b5328376b252d93c700544bf93c224d4a29661e074e05b80c997ccc3f869b27287a575ff300205abf720dda05442179b15cc62f4c1e7099b664b095743ac5d4e0953298ff7526950c1a3c728750fb93a77abf94e66defd6d214ae0bdb7a0fd7085bfb850bee5407f3c9dc1a973cd8a0607edc5e042939c59c49009bd84e2e5886427c7b26864e1e51e53b7a2be111be3855370a067c22f7a65af10c5c7307db899944755676ea0fc52a3511c8c302bf6c2342369bef5b265b11fec35b850233673cd70a0491b91a777a3ef3d634db61532158695964da4958c9b01b66e08b0dd44eb26c744535939c9865445c80b2f6421efe5d75299afd016c8bc60aa1783bfc0a356ac17477b2a85da4622d3cbbd496feb5089a7ec7b81db171f97277dc24ba4fd3fc464d603c1f49ec032d08e742accd7542671d5914c163af8208d1aabc614d264623ee8ca4c44359921a77f9ba92928d607815b8c6461f983db426d1d0616b5b333ae1dcd392977ecf181d5258d5550d4b6b39bfdf14596b19bb7b6d77283fcae385eb5f1e2363cd66987f2774e6a3daa64230328d1a8ad6a8accc6095110dc05d4a52b4cf7d8d6aa7e849c51ba043e0a823f57295f66caf769555c3e24df49850f388912941b94648012d5be687e576d3f5bb586c56654f748b48b26195e3f52f18088fb0beb82affb87d7aa0df53fa49ca086edaa16b4614b543d3d6652bfec05f654725d0b9124c03d278efbee4e6ecb80875e511a58ecdfd7128ce61744963c6e8f07ac783cea320dadf749066dbcc391fac9a9bdb651ff26838c9747b7cd4876ee717610ee7865ffb4f6cc853bbb5f6db5fc7f0b89d4af15500f2db06518de6939a10af925da60cd3b235f128bd1aab5e070d1b7643cc7e2b885be673d3c39ef359c6bac70c3104a3b5f03f3121537c78fd7a8aa6a4cb3fa77af704301b0c87f2fa5224f87711772e62e2e9747e52b7edbed1d8849e03facdde91592090ba9216032cb8aa12f76621d24bcbe42f40247e91562ede15ca60e0e73cf36e34eeec7d0b972db486382914e00c06b540a96a4a1a15754213f8e9aeafbf78ae17fca807ffbad584ce691ddd116db74608a126b4b2bb7b07280925093009878f64c89faf29e986c70870a36f874d293c75f83515bdc9e1e9000e7f0570780e85c363130e2fc4e4f0480270f82543874761c9e189821c8270d8a1cf811c5e861f87aff201d209494a9492f0806489c861cf7d1c7af097971e58f038fcfce8613db487df083f720880f31c3ea6913f5fbe3efc76aee3d0bbcee12597095485c69c1c5ecbd20978890471e4a60568a5c75a784e6e5280bec94d50b4d2634f7e23372140b372d301b4d2632a7c959b0aa0b9dc64a4951e23facd4d457a2be0262ce040501e04010f3ac08914e0468c7e24173d96b50d4977412731403bb9f4177b082edc85ddc48501e80fc8052231fd0de0db85105cb811222e0429e0420b88b5a0939c72dc8fc845d82100fdedc75010daa1a9e0a024a7adbf1d990206a03f146e08f73bbef7d69f0917da424f7242417fae8b1082fe727e11427892d30efded0bc004fd091d85c75c3872895c2341416e420b408f692a5c0b26c472dc480b381ed482b621c9e94f720aa23f13de447fb10fe024402181059d9400edd4d2dffd072c18394c53b92c10799213131602a03f120ec402d01116989c080b1f0480c883b0a0bf20076ae1421040c2139de484a33fd881682af73bf2c308d049582204c87fe0273f9ee404a43f58d6c2d07f682a9789fe7edc25b0030101e92fe84234150e48c8939c86f4d7fa124d05ffd0df8d7fa0a9e01f1f007d48c8979c04168280e80ff600682af7c87d0284c89320fa0b3a094f8290f0242798fe700ee4c9cd8d3c810179921311fd053913fd91f0007cc80186aca0930ca09db0feeec7e33e3495bbc29127399db082fe863c4853e1562012e463051efae37e64051e4a8e9cc80afa23f2a027ff29c010157492938dfe4ef85ed74712fd057d08921f1f9a0a7ee9cfc77b682af8f524a720fd21c99ef4d0542ea7bf1e370992070505fd04fde027e9a1bfd579f4e0f1242724fa0b7a929f23f990158c9c707d5c252a10d19f910f5181c89027399da03f9b2b51c187fe587fa9e0e3f524a723fa23f213f437e44a5e82510929e8240268274d7ff73a9acadd4d295c9e148ca460444b21058d3ba2a96429e8efc88da8f022994ab69b8a4a20d29fbe0e4de5f2e828c11629c2a3a960ad3f9eebd054b036a2bf9aa97075ae692ad7d61bd1543823468a1829f224271d3afabbb73a749ee454f567e4b5c8ed4bd054b2dd9402bd3c97a3c21dd11f7d099a0a77a4842739a9f4a73a47c4a33fee5a53c1db8624271efd24a7a9bf239ffa2be1f442b3c90004800293000c2d394149122426e4111ecb247c67a00b65213f9247780999841bc9402f92859c481ee14332095af2205ff0411948c3ff64215a02215ff041f2085a2ac917bc0879e72164efb00c0290ec13634efe23effc83ecdd47ee79650f64ccc97be49df3c8de79320074ce5566cc894e72ba863bb47422efe826a2eb648f4877398ebcdd9553cfc9d97172f7d46fe48c95b7e39cfa2a6757e50ecba6a5969466cab4dc225fd066bb021ab648b36bce48a0c4022936cd330b0d5034d833db1d408006061269861b2b0390fa059b1095741312923f14683bec0bbab7d6c3251f30952f68067fd08c9671eade9b5f73f6744d3f037fd04c14970b6321f9032bb53686242fc8577dde00a65f50845d1152cceaba6378010afc052dc52520848290148989813c3546f8bdb0a504a955fe1092af0d52f943deec801ffb87760ff5845b9b457bcacc9eca1e94e5a0fa2053b21d81b9a98439828881bfd4a68f6bb6fc96af6ce71e10d1ec18e26e9ac0b65dca6ddb9036ddb659bfd42715e035e814358928d6180a5f767d9124b33f986606f8bb512aa828a5690618459f484a67a500d296b3d62921851b9452eb5c00471d0586cfb2419ecf428966d494d97996edc44bc9a4e4de235fe9710f7a1ceeec42d2831ee7b81e3d1ef34e8f2cdbe9d123fb0ef723eea8c175e7316731c6b8f3e57ce736eeec1c764a72ce390046edcf4a972d25e731ea2931c61863ddf9dc3bfaab42b9d89d9093f3c5ed8212235f59b7f32c6f3b99ae54c897dca94b70c8ce65a6eb17f9c2227f2a14cf79e83800bcebe438941cbeea3bba3e411f8946be7ee0b2736c789e9d9d9d9d9d9d9dd73452e0fdd5331b03816f7cdffa3bfeb9f60ede3bf9dfdd3bfad3baaa4103f757d15435f2e78cfc992ad04e908a334e789eebb8771c5dec7b2ef00936fa3b9975a83594af9defe84a867cc119a94a3fc121fa3baf3c348ffe5c00d03af487f74a477f7773ab1c38f42757aae3ad755dca75eb82bf8894f445fe7c53693fc91ff8e40509b67c85923fdff2144afee0b83c15237f762e4fa326508fcb53a50924cbdcdcb8f987f78d9b9bdfc854e97a87fe5cad7f5309c7639764e7b0a34ad04747850257285d01b0843f59e6c975f91e19caec3b5996c1b98efe2ece3dfd619ce728f345de8bb1cbf5bf5e30d817b423ec7068fde5dcba32dc391fc5c91fbd915a608837b2120c9407d7a78f93576518625797b8fdd5a5a56f9a994a53c96acda30bfea81255923f4b6ab63c851b045ac86c18bb376960798bc42375aec02f36ba2ecd6a96d5d71398db10ca026178d8c00221d8df6f012b78883ca899b7c0123d7c07353c3019ac70c8384f63e424a464245314d04d0c7e8b2d180c16020c0683c160416021c0603098bc71ef4ddd7befbd5ca4f04e209b9818f8a548908bb1fcf9512395d3470a31126675030c03fd91baf7a6eebdf75eeea6eebdf7b2388ec25b44ead01d1e3e71fe7407f2f011a2037de25d96c3ab584b1583e7689c18771004304c1783d3255f31062790168313281583f7d4c01d776f90345832cd73624ccdb4ffba344e182c082c04181c026130d85dd96b56579140aceaa81d63d448afb592dea803d35257e75e1daf6b5ecccad5f8aaf47256522ee3a0bea01fb0204499e783abf38ce7d2ecf5c5f3ea51af65b97368b7f66a2909fba0c72bfbd537bbbeb4529adddfab8704cdd4eba5c15ef5be226dddea7a65daccd125a95aa5b5d2d77dbd347d656e9787069db2fe7eb384d43f1d7c09947d2645ed6618a31986d938a29860a8992a023c85fd9285a065032d6e9cc0dfcd1004db4b93e1533ed205914bc041261ba1b9c88aa399881accd13c643765d764d37da6f458762d27ddf058a6951ebbba6a164733097bc7061996c803f4d1b265cb1c76803ef2ad2a7006caf121ff08c1e1c6bc82ec1c8ee61176d0c01fcd22d01c420e8e66187ef2389a4168858066203b8dcf179b067f540747f30f56991d1ccd1f442e5aced1ece30648981182b504d30d24da28022bd910a1b45a228a85c40d2a2598f0129d1250ab2d565880e34246b505aa025b5354b7050515d09ad0705bc4e021c504d4b685064b978bb6a58d1e889a404a82071fca1041c46529892474205ac24c1250e84094849a24ae50524a02044a444c984922cc0f444d5c72d0783f2c1de3964b2703d2019e293c2df98a3aa49981926ad75a5b2e18b561176994f100e9062f2c69d6e841039cddd0a305b8ab800722c02c0cf808036b4df4e0a00a2270b621183442e0031a5bd88059649090907e40a38c1eec5cdab08b36c6ecd71768a4410234d668c9c10e5128a17936c4062184104208218410420821841042324284f1a201df15f830836dc07c8044163d5650851558db100a8926a640c28331f0dd100a09227ac4e06c43282480f0629352ca29a58c5766526ee84329fda69c73d25aa994724a29ad94126fb9240a4a4965a694ca2ba794320535958b0a0c2184104269ed9c41a6a5f605a370a296ffece79cf39bb79452fa694b27a573ce7fd99cb37ed63ae7dc2a47d3e06c2aa7def47b0d01ebfee69c733e421ff8cd39a79caf5965a5130758d21aebbdf495c4a5524e6ae9cd6c5d73e9a51ad2bbe6d24b335bcfd09bd9bac6d6ec525bb333d5da9aada1d7d635975e7ae60cbd6b2ebd34b3f50cbd99ad6b6e5cc9ae94935abc532f6c5a6d16630d5b461abbd45a6d4699905e88823489b42012e88b16a01fb01e286f8f84e5095165d2c2c46491b0543da318242c99ec329753a9a65f28e87649ee6f778284d9ddeea2180245a6881441905ad3bd1913d2182631910b8d484abc540ba1cfbc4d9a8b71b4914b47648184d2768408761602117e80f6c7ed6a64d7236e30220c1641881140ec29718c1016f4579669b892704a08e12319f8c988a495b5d6c2664f7cace2880d5ce18123a8382266add2b6d65a6b6d50d06b7f2da62c76e02f36e5590113103d6bf0878384d9456c20db6a8004f3bb94527a420c3050031a41d040882e4a88c97410812f6c43c51a5bdbf2fc8837ec2f4811f8655b11ec0f8eb19ab59dfd6177aca5d6c62d2dd8918a6db394d9568b6d7fb1eb2f180cb67d90d01222226eb0ed9f403962db4779c1896d2f0306ae40d262859219db42282268c0a4650c9722b874b9b22d84ca0017f6622f62b6b50fb2d65a6b7b64e1c0bd9d076012a679f1210363260566071ba6cbf562881a0629c352460e148bc572831fe6089e6a96a40bc826504f7488206213305db238819b8c008d7429fd8075288049d8186a18e1011835baa8a106163d30c0aa0dc1a8d1850f23f0b6211835c078f980ef8660d4308303141217c8d9100c1b5b94b0fd6bc5bb6950a2440d0b54755cc76198109109a2cc182595627e3b929b3eca5e77b6b4cdf96d9bd56e75d65a6bfdab6e72665de04fa7428922034483f47a990934ed0f50505faa8e1ff0847042a8a5c45077f698c2b4f6846b179a98ce16a4b2473e35c67ce268ac8184a54b9d99cd7296d15c245f2ca4745c8a4af0009882aa81e16397c472306315a0062ffa090cb56f179217f38040ad0c7fec06c47d7e3e19c357e5bed12f587be27927962e775838252ff504f89387eae2cb9697f67572437461c134a286e002239a2070902a03268d96c833bb21b9ab974a5d0e490b16c102a1845862df6cb92447d8f2d5a60ea14fe5eeb32cd24aebebddb093ef5e9044c70f986e4be40fd556432d44f312263045a26b75419f3a035c796409f3f5d556faeb512d1443227bea5363e827b00b0ef194c46d2b19ae0e6838d82e1b0eb5d65a71b85d34e104c5e102693629a5843e74d2092f6574c176e50f84b0a0bf30b51267905286b1d18927d0937618a38f59529b669a3e92f747f38b503bc618e352f67b6917a3d0de69aa539a462c942e118d80116ac9eca187b14d6608a554c9cec84a4ce84911282fd4280c0485444949f7d99388b6db0c6348287dead2141b78098cc92529ef0b6212c6e6375967ada78f59c4e244c6a6ebb387c296b4826c7d1ae36a4d2a8416586ed8794eece78de636bfcd6f9e91fc7699394f49dc5edc72c3edc566f6e2535e6ccb5ebcd985a49682cd9eec3113ddec37cb32884642b1411e2fc2a889ea679452a289346c90e766cf3aedc5ec475ed41e91ea29c098eadd8faa38191d6557fdc8a9283beabaa77495edddab7057dd4a0cc660ec1ec6b6d82591303c8049dc148624f644a90bc978b3176b517ee23951a9b25ffb5de9a3ecde8bda48fbf58c52d9bbd76d4bcdab5c5a50c6068e422505c1a691524a29ad144a144a29a5dacca44501a5748b0d0689b4a1b673d8c01149860914af1ad883da73729f7ad17d4a577182af72e474a43a3db2bf87faa83ef5953e9a3b5be0d5aba84e6f57ba4ad59e13d5e9bd4d17a9b4b73dd1d7a752afaa2b94fad41ebc7609460536f8f0840e19f822765f349f7dce5bed44b347fb4b0d342640230c24ccd02296a9741558ff647595a96151474e45a9cf1fd17b2d2a0663993e4a69cf89fdbd077591fd7d51fdfc07d33ebb14622c3b9c324e95071f4351f6d4bbc77db3077fe4b4fd28f5ec474e9d2ea2bf3fca9efacc1ed41ea73df84d17ddd31f51ed412d03d2ecd17428616ff336ed39493dbb51ea993efa117dea463706b5a6ec4252bbd7525a5cb249d4ad84edda29942812804bb5ca5a291492b5d6babdd65a5fb3a7ea084d69eb6b985a19207f3eefcbf142945a6bbd078740b873c021f465e8195bd40287c82a1026f100a39658041c120f213c03a11816ece18bc0a86311d0876660533172cc025f458cb1650c228b0cb6bc05e08b7e0661c6968f715f1d97a00fb5400db67c84102ecdb02712d22c6385495a3a2595b27e61938254920542e3820637a8ae6c61c5997a450d2ee98cae0733d6e4d0829a43adb5561dd81c542c11a8ba484d81820c18d134031bd460072796a05e10517be0806a731b4205f1441a323d5833704825692b488091c314293893431850c82eaeb85c9c01020b986146cd813e08a61fa99b1d90b698316306f26c6e674ebeb07cb9cac03c32e6e00d4d087da2a69312d9aefc81504a2965bdf9fbb561841d84a2505c00cdea05a00b019248427290fcf9a07c8970f856ecf0ae7d781da1c3bf051e067c012f177e2800f6c307d97148857cc18770f822120eff04e8f02c08393c94270c48c0a144913f5f130b4f1d3621e0d961910f3924f2a0c320ff6152618b017e806f874d2b3c75f83515e0d9e157a484c34732f2e723a2e5927cc11b397cfc42fe7c418a1c3611bd0bb596912f49867cd517409f7a9d31f017a3caecfa28a64c7d952f1813c9c817c021364731d58ab59672d9edb35b2dc3feee69d7823e57c7a5ae49a6a5dcdb674357dbfbec6aef664deceff1afc6f28573fce14f71ed7b3b2538f8114b37021438fb1797602a95c588c56adae52e6ec121705b6db16427e4ec387f566f5b8ae3ba221ccfa1ab38d9c3fd45d9df1457a577ca989a65bc21fb794d4a8fe178130e3d0395426892147243adec35529ccd53cde56c9e69e4eb862ad51b7942654185c6d96dfbc23d51a8ce091597d516ccc4d26283e50610ada51b1e709472925c9ccd1509c71a39d4786974bcec50a1a3cb0e343f93cd6832005086e70b1e647a8cf120ea25a6078b0f9ca05c7e9cf1e16c9e6640d802630a418b085846e06ca64004e16c9e68e4ebe69b10cee6f9245f37f7d10dd93cfb06c4470f10204080bc80dcb46ed8701b10203e3e5aea0201726f06044896d90a8290114408418810988f8f8f90880195a6bfc9ad9c314b6391af8a5bae1db18c9005c3583d527d646aed2cbb37cb62a09bde52aca97c6dd9cd0f80adadfa223457f4aac8d55b6a7596c5b8713b460d73b4e02f46c52f7ef922ab8f59764fafbecf3ebba1d68e5f84dd907d2a73406b47fd4dd9574f69ed98e9f845fe7cb41b8a5fa895765a0a63a6fa64d0da3bfad1fd4531bbe6c8f214d8bef1cb06a486fa4a757d9623d1ce6e65c36e5edd85eae805880f187f3a9773bd76762ecb2df9aacf9104cefe71bb56224bb2f80241807b491623803e707f5b48de7b513856ca6eefe918357beaeb925e8a51f255731481618cd5323beacfb5a18f1af88b513baa4645b5a40757789c00771b42618145191f5c808a170a7085c203344d6460c134051630b0580a8389ef2d9376564a2b9d954a2979a49411afe6953f334a29a994f272369093569b5ff313c279b3219c509b32c29b26ab66361a279a29291a0821a438986dc468a6d2362a3d234633319aa9b48d4acfa06a6cb322118295ceee55ede479e6b287e59bbb2648d7b8eebd37621a35b95811850ffb834f104a0c0a9ca298d00006d03cc1c36cf292e68a2536231ac8ac5146cd0d67ac985cae5043857ca0a905300853060c282618b7032ce056adbde280167b75ba5a750087bdbaaa4bb282647457acb1bb735d920e7eb9820a1d5a3069a82b7088612079e1e588e8851559d02ff78c0d60993744696ddc0d285dc08b316b54c181304e40c5ec54625b0e6cb16d6a5b6badad628b12363de5a1034f154d6c7b2d151cb0d65a6be9611535dcab04864958e67401cac53e09319d18a14622c91eb8bf8ba948c1929323a27898c1131531d062861717164a2a8049188f1164a0870bae1b82d98193193b90c247961d6851811d98000736c2e031c271411457ccac59139b37aab7d393a79e8c41d86403c71c5faf18c5e002671b424df18507434031850a5c504a0821a432d628bb9871ce99d93a29a5349b76d639699c73427875d8a69c137eca0c3999e79748e78c311ecacb28c116e99c93b26084c1e61ad8a54c770a241ec86092228d1cc6589a820725319ea600a20230589ac2081ec6809ac2e90632a26a193268c0e4400d17186f08260d1df6771cd2c08018692cb1031ce016b918bb18a59c52c6edf5028128683e79f5f888446e76fd5dd3734a2112ba604bf92412612182208fef16391a6f2bc7c2b3a8c50d053b5c601965193c378c0c782106195e908f686478216628398a9297d4a7f6b844b36060cfc33875f001dfc727e910e2834f332249b821d004726965308d724e55d376d919c9d8762dc7a739b9cc1c9880f69cb71b45b24408004a48d769bba9018ea773de9c118b04c9d79c954a092f9c3d70aad9828a405953546ed5d4da2a9b3ec218dbb2d67cec7a043cb6ba2b48fe84005fa99ccaf52134c15be60440583369a780ba014e6d0825451a3e8690e28c1460ec6e432829a2aeb665f4a9d4a65d0c8120dc5259769ba57eb56b7a48ee2f6e6fbb67543f9f42d5d333aaa74fa16a7b6f964d2b76862967187b6b96e1cc663c5b4afc7876566bfd3785a7d6ecf6fe8b99f6ae1e82f2f54de1d936d6db5cab151ba70e5bab556374b58838216a2bf5dda44024983e9e9eba4610a3f6b25bed65ba08ce9e59290f4f4f4f7018f18293524a338dab9e93ca182319b576a952d62e55c71438be4a69350c744f3d05b6e52c82ca1b1b3480e1c3e0cb426b0d1c2fa7f0ec18e3149e3dcf13a714f16903f7e9be60eb42afb843b463522960e9b63293598aabf976d92929678c12069b3adff13cf2e7b6648fdc1c2e93590d60f9ec35f37cc600d77f98eeda35a148481084792a9b64b2ce6d730432ae7602d011bee6ab6c4277f6fbabb1968f99eeb867e4991ab73c805fd76d60082505d37e9a3fd58b33a4e8210db659e3c504c6673e4872c1fefec396367cd0805b5b6489c2cb52144cecef4544e103de36848a62a9e5a262a622a5f44a09e79c73ce39e79494d26cd23969add44e29e5945266f2be6094c6ef7b5dae9b35b0f8e037ac5f26ad36bba9596bb5f225ef8f311827ca7fb5555fabdef5663745b53937eebe6ed00666760c7233b17bea267bfaddd45aebeb9ba7347f5987ba5258bbd93247a98b7694d65af5a5db96c6255f50c5adba24f05072f3aa99bfc9755be0c8d9dca8ad16a594bb9956df0843d549c9cd0be16d3229dc340a21b4787230c0f1355575f637e7cce8cdaebdf207de4a7180a5a473ceade3b0d8b009f8e5527265e008e19cab2cd35524105b21b1c6524b2b0dd4d635965a5a69a0b6ae5943abad960c5bd7d0325450c69a32ce9401633c53c6cc4119136a4d171c37395a5bb5569bdda036752e417e7435501d2c9ab05bb2cbe2a214a4c496ff766cf81d48246e8920c821104875a82ec378263b60e7248ba57494d9338613714b4c8a4848a94deb5210c5002865b48debd2f2a203c5a170bb255b077b71af1712a15be7352d15bb244d97e7aa150eee82b9588129dcb0eebee068c375e1c2850b17c8850b9c40383155c76d5a54266f78c117632c9960192424a41b9aa0816b820ce73406eb092b5450a4e0c6179dc4b277ec8fdbdd6119bb3bedba64815b74832203504f8800a7f9b89db556e044164e423441868b04504d54e00699a14698113c098162424c1c038a1928818118bc98c0024b21934503496819620359d81c3c80a4451f3a5cd26c7afabb04984d8f97b0c1a63c6c8051118c0ecfa64d6b36e5e2b4e95d9fafcd8383314a9c804c53139b5eea005320586bd3245ab029941248b82e165603aee86a0527581981034427040e2f749cb1c9a085458e0dba2daea441a3b855dc242db57ad84a80a30dac04ce96ae0d17746d58e1eac1b526cbb6869a6e0d3170769093a56bc30cbb83141b2758ad41828b46b5668bba0136d4746bba986bd4c099a00d1b562658b3858574c5f502098acc0b1acae03594e8aee83ac688d41536c4d8aea051c65e69b9726c08e5010f4499800607f07737f528de7e105949795c3b50603829fe04d20e537fb2e93fd134edddbfdea5bdab6f2c51460aae6042aae2a4e9abedb38bb9632e45027be8efdd6c96dd669aa76a29b019b5411e3c81644c27661ef9caced26480716c18e80e627d09910bbf68d3de3dbd8c3ff29c5c6de419d9cf6b8fba48fb165347927b11eb9cb6124be922ed5d113ed71dffeaa26df39cb05e5fc47ad5de2dd2eeddec292df0b32cfbcd32d6b58d9b167792ce98bd5eead888754d7b29dd2435fe91bdda357d548fb5778db6b3b41f79f746dab973fa086bef6a8f88c5fa1136d2479c3ed2ced2de2d621deb2a4ef7ea22acd266cabbe7aef69cb058f89c3eda8eb7eddebd11fe9177b551f7edddb7145665a20c65f664d7f20bac55f62ecb328c6941b3278b58e42b68674b4c536a4d1be4d956abcbaf3a1b1b9bd56ae5c47b77239823078e635d25e7385ec57b8e5b8949d8c91f79efa0777ca39cab728e737ed4ddf3debdc87b77a98b72367c2bab17b5be7afceac6b1ae62f31bafd2bd752b31a9d25558c7afb2baca4a0c77c72fea8e2f8f6daed255726ef32adebb5b896115cc717c231c57e9a39ce3c8398e1f79cf7118935d124f7b91ebaa17e9b46ef491cd6f9ca5bbb7eea55e74f3d58bd4c70c7a4ca27dbcbf70cb6faae7dc087f471fe91c3f47e7f847aeab72b2c77de730e6658fd39e13d7736ea4f31d9de7e8fc4875d7a63d4d3b715d7503b8b4a7dd4aec06d0d19e86bde39cbbeed2559c8a5cf77415a873e81ced11b9726238349e3d32277b9a2b7b5afcedbf6718cb89da8b5bd7dd888573d63b9cb37e74e369ddcdbb1be1b08ef38e759c1fad7e93d24e6ebeba016eb497ba95d50d80a359bfd15560dd9c1e91cdbda2ee30c624de7eebb2973a8cd9642f05e4053d4ef51f8c2a923d267adb2cc53ebf90a1e46c639927ba70810e9e32ed31a1b7b75a4ad534c6b2a2c9951a2222e882572f3902cf675789dcf544f2e7a2205ff0dbf69b67fe22929ddbf6cd6e5151db3fd8e6dab66ddb52598bb0bd7d9e955d13c8357bb61c047decef9c73cb36752d9522ca02a77e9ba4e007a1b8772b9b1cb77803db1cdf86e3baaee362ace3ce3a8c6d94734d208eebba23d6b9c318c775b7d968c7499b89595fddc8c6e62b7de4347fb4ba11be8d66d91cff88f5d5eaac17adcefad445f8361792b24b01c66e646f7e35e75cadceba951b87bf916d329c3dacbca93a4e952f9e3d54c70af0fcf743bfbbc10e4ac2a8e9a106259a6a30c40e492d08030653982fa830636fb03a2d68c286304a6394400aab0318558c1b5610b5c54c0d586ae982dbc2cb952bb6060f2805f50086085090051a25184115b11578e0863ddf72cdc3a0b2508305582e13e6e2ea722faf27a8a079844e912ff839e18c9762ec72fd305850107d3a3229a5b4a5c33329a57386286f1adbd4b822f88b4bccc7bcbe809171e67e3a1066308319dc6aadb5d65a2d7de40e63174e7f3344cd3066b395da0f8aa182c6d7b64f368535b395521d635652334869b3c43441676064bcbef818971809effa8179a0b4caa0517391b09274c5a5a02818547c8a5d2297c804d1ecbab4e76193d8658628a6e8c40c63b643910f0a92d0ce9066cf25f055efc248544c970be309e565cf29e1ebc99e8f51863d9fb62836388120661d679bbc719f6bab609324ffa26cd5b56c73b86995feb46df3450922e7375025e0b259cff1b2332a809be7bce626dc59df62d62eb3bd0e13e09bef759800cfdb6b9afeac703902d31b8ad175d80920eaefe611a05b4a8edbfbcb62e521eddc6db04d86d0475bb1ec6a65594aa46c36e3d8617baa57aced3677dc37eedd6137b45dcbdf0c43da59f66a18ae24d25c71336c284aa4ac0eb7262db4812f6c7889a4bfbb39dd1d4b24782eaf20d02449ebddeaac434fc926a5dedac36f96bd4dc45ad4f4070314a41d03dd10b0f138373404ac705e8e81f5ce1b92c27dbbd552585f5d062dcb2d7f43aac3a8bf21d576a1b9cdb0a36c2599d35f448236f90171d35f44da753b772119f12a07cd9774c1107713d5211c426f1f5065775f6596a6b79a9eb03a2b4b4969ebc5572fde3ea5691886b6738f7a6875d6ed3f2b48dbea08d06df5d097b6d552bac32ee7782a47a4cdda3547a42de5e639cfa199b09e73d90ddd3ca789eada6dd6f2eab06372f39c9c9c7d31d055f68658bf39eb25c002c505991d332e764d6e76d4526c0452dde0ec7532de2a8abfc16e88fbea36774d589f8da68756d71a2b472fb3dee5cff5c9b0216641c882c75c406d94bbc9ac1bfd459b3f6ce1d6c9707299953f2b4838ee23d2bc4b37c9defde6f84ede6e471638daa7e2eebac76fac5fad752931d0ddfa77e3f0f6563f40eeb8d29fdcab6f5cfea06c2e8a2a43f1b27795cd0b5bbbbd97a1fc91e190b5ba71564b47206e9ae48b9bdbdc856412ee5df6ecf190ea9e3debabcf72df70b657e5d50ae7eeaa5577782e7fac7faa73d7b2c7d24338f7549e6a75f89caf3cd5f16d6eb377e3ddedb79c7c739cdc3ac769f9e639593bce8da6a9f4d0cd3d8ebbea387b9c1e627d75789ce377d95bbd3bf7cd266fc7695d95bfb8556f1d276fbfb1d19f95bd1d6e57e5ef861eb2b9b7fa567a8875acfad665acca71af6e337e97ed6d32ebb01b6261fd5959e91828d7e5edb06312c28e31c429735b1d01b9777400efba430b1c0fe7032c7c96e385663774b3e373e40e33738bdcfe5a0f889b76f837e32ea5303563fc1253293d3f9d9de97ba189e92facdcec4ed8ae6961da94ac54a7dd085d707c2a03c2f12c098eacc304584a03b27fc39de9b8458b7c55fa6d76f84d38eeb5c7b93d0ece715c38872e1d97628775131c1db1e4fc669c692109378e2cc9903d395a96e1e814570bfa581d976aa10ea894ec98a972182a44230000000000d315002018100c09450271300e26b2aae90e14000e7796406c5099cc634990c4300a82206300208600630820840064942aa20301946a70aa9e4126bc9c963159da9b7e6a1782514880c1f32f774369bf5052c700b13e82cb399fa52af277b9f0d0be18b0876bd0d1747de2522fd6b50b1a412694da9fa8f606ba690ea60c289d1859eb9757a711b4702dc182bd32a8bf5bd29f58d408607197163c02efa19be661da82597e4de90c15f52dd204b4dccbb155ba44abc55220889d5250d440e75fd6a27da565ef7305e2b31502c2c19f7d8e10781857526cbfaac8d603e3898e6577258a12a014bf51ec1e95ca69561c00a6ddc5a608be431d301860c8ed0e9787ed2406c2825bd58d10a623630f58985a1801676ad2893ee0aa3da594a1d5b4b63cb02e826708cd6cc5497ba94dd1aca2a9dd5dc3dd3ebba452329d7322174473a262d4a6f510c7818802106581ca7f830da512e18d059778e5591a15aaba37ea057c195b8ab37314dfd9e7993adb4ab593cd51fa3c57d402a5b839d5d9a2492cc4404247883acc4420601484f3c3e32509d7f4d2c15b8db31be32fde779d62291ad12f7c08e24d5c84601ac8688684c6819f30fa0b6392a7619a5db3e8078d97710c0c3f517aaa1510eb405bf551329e288bc2ff9056fa88ca11fb7dab724506fdd8fe764cae53dbd5447e6898da82e3324e87191b76b3fc09ad65d42f271fab29f0b484c439502f24c003fc647a0bcd03911591df42bd02406a38b8924d50c05a00f190840ea00711de30518268952e84556ec3413eb6469482923289a966e568a7dc7aeeefdf159baad3f4b8d5e50bde370db8674310eb77e59379d9a708bb8de482ce0fe90e0eb41e9f366bb18a15b55730320d591aada7180f4708bae3668f6e0678307549de09f455d8c0b6cf12b9aef5a36b5c8e461425e7cbc1261836083b22a328e9ccd70fe11aa5da44b57014ebf535d503372bfe085f122aa26f168e462e048cae005da3d801e6ecdc0fb443e894dad8b204ee41b52f491bf611459364f61598c5ed6f56646f12232159dd37a3f51e093754c77ff125fb62f8a9a21c86073ed636a8306528554bca2a25db1278d603e83e8a7aec0f352028a6792710d15705a2807129821033548d75ec52cbbf0c7ccb466dd056e40db05d28c9066c9710da27c56ab21b26747771cb5fd6ec1d4afd5824a310322a27c516079ad0322ba7f33d30d6d017b8c47e0c635085a7904eef619e2333c063995a1f648d4af8bfed8937aadd61cfc061820cffc6434bb763c34a9469f74464d9b11014c0d82b88f6c3275a18af3fa2b053eccbc81f34f059cf05e5fa10286f6b4eb64fc53d442c55ac87a72a648b153e835fb66d132b61ad1adae757066402db534052afde3751c88106a10cadcb854350d5eb2d0a31d335feffcc303e46f324740124ff816f40f4aa2d945578ddda388c22a4bde6fe2942a09285cfac04877883264e0de93c8f505097a28be9c0ff28e7fc4ac6c8de4b23292e46474a3888090fa88f0a431e962ca8fc4829dff3d90755f0dba504acb0ff256e4643d7c8a090b63591f5d81e4bfd6b04b0041be29c93317113bbcfbb55815384933a3291b2f08297ed41297e88123c8732329ddbfc545915e56a7550fe97e658ced39b28d4099f935b00b0f5e443b20b0a2cc3f68fb1b4e3f42936b35be4836c0877ab9486a0f5081f347c59d72f0940ec17b1deb8107098a7392149972c8dacab1655b33e7f99a6adb094374b432c0d9298a45e82d9906443b380d9063ac1eb9a80363b5814adcbca7232f47d5cc19b98fc726f8ae8f807244683456a59b43994f645e2bd28e0f4ad7926517f646f4998c70d332c7a56300922c1a5e3797e0729fb9d1f8aff46695713a835c773c1451123e07a2a04b04f943c88c9ee0b687c1d6aeef18e71e4a3338505bf8109201099ad46bbf7c2837dda08a6ab02508bf0b3d5a1ae930a8ef2c208a23340ed3ad630e46967a73cb17c45e148f5c1c9a9cf4065f9ec912ac3f9ee6d11830f61eef026e2365957a02f820415dafe6ae30738c188b31204a109a36d95b03b2215fab322183f36f5934ba754137d1b0ef3c4afa1758f90fde37819d434b84c74011bf0d6f5883406a5742946e507d3c0e5bf204f09860bd9d2f2ce7326f982be1383b0d8438443745c9eaf514c7ac95db1d521151327b6c8d50063ba8a5882fc7372186824a45a886b788000e2db420d801a31c8fa1d76bdf45f84ed82478a347920e89bf93e1e4fd84ae03e90aa43c32a7c4da37295fdb19269b69b50c0fd179dfbb1f403f33e297b3bb1df44318c470ba591bb154592d76eef54e5b91e54aca063f1505188fefac26c045bd6d53a82b32c5d48e6995950a0a5161dc71ae261c9772400637b1f6bd1d3a8c2c58516d98523e787c62e072c7f421e206552147ba2a8286f120fc768f8504ddd869b109e1929ce61a2c7315011398dc6b07cf5f9f80647806d0d56fc90fcbd6967940dee1e67a841c703847d26dfc27babc58244805092230346d4b6c81c9086625d49577f793a64fda86db5fabc144d5e47de5513b60eeccdee0159c5786939a7224193acaebdcdb915b3ee50f2c71720b3158c7fd940343018f99931b24577086451b9856323104308868701b0ea87945cba099cb6e789a1fe861cb7a2336cef279581d2e13c1b889714b908074e2028857e04be0ba074e77f7edfc0d4022476e3c2034199af049d206ea9da807521d3741495685d4d77ce4dea8ce1854cfa64592bbce8da72af921d075f4fae73a15beb57742f29773af7c84e298ce4496ff7ad524ad47b96d3d46057b4db5d3de2bdcfae76123ad6456fe4feff92161ec38e7d4374d572448732fdc518acc302e148ce9b98951f47d092036369e59f3264b60ee3423cdba8bd1c846425ba95cbeea80b7e3e296b0badd524bb8dc4152fc7fa8e9f95434e31c54367177a2960930e2259e490e9b3b03298787912c0303d3b43585bd119e2686ab6899081d3b6bfcf5bd4d23a10d760879f7d574848956e50e411c831b2e5e698828b9b6171471dbbcc1f9f03ab5c84d9d5f7760b13797d82e5755b71f7017e7d60118f914b1926e8ef11c59286b602ac20fadb597cd132fc4c40c4594d3b7acc7e809402db5066463e7e5d48c3d8bc678e8519807a0a8bce6ad127a1e24d452d4ca096b373390265b2954b8ccc8d4b974fc361668f8b47580d2ea00c879dc83e700c917592d8b91167bb59981a29817f69b9ee6a806ff97ca8c821180d09468c0106221275b05042773577f7eb8fef0f3fbf7d489b68645de51e027a1a9b1c9ccf2616f613e654ecc0082bc844467a52a42f2c00c1ca99479ca6fc297dc4a235140cf204ed843676cac14cc7e770000d6fdc65404f03142fc37d715e945950341ad1c755fbbbff3702e311cb9450f23aef20dcedde5e52e6f02e4bd2fbe7fb0c6882ae633e0626e0f7838fee265aba10dae4817d1eb425c0d77b1a57846c2cb6c15bc93833a7cc250ee94debe58db9a8c0a6d83a014bac2555eef64801b548eb46f7ba60d2993688d86fa79cabecaa0721be4eb4f521df625cf7fc864c188afe2baafcd7e99e3e17730515795485e99253cb35f7b10075c878086ea5b58931bc33e6260535fcf1cea904697353ee7a66969854768685b721c4c67e97f950891cf6631f34016c8004ffa45ff2561350747fec1604d219e3b26e26de4a9882b55494af9706cad4a88f73e1f9574cfcb85db1e183137ccafa14d378701222c9be12f621639bc9480cdea0707a13208a91ac6b208c9d1f20981a3163c3da617b87db3a526af0625acd3331a73e0960c6110ac8fa81d80baa82b6b5480ca1353523f79f5f314ea0a8334e2814371860230a1ed33d3a98efb10b4a7211e67aa1c5b9908ba1ca54c57c877c42d66992ca5e601bb3ad8c34c9d8305b6554552cf3de2f6f15a1ade069c88f83355cf4a839a04d83c11e395888b6275f71518ff1a0679768233cf483ddb2bb3d3e326e6166defd884c6c16b208ed2eeb989469529213ce01b111826905af023fb7fd6146df1277b309dc28e102203045ce2080d9cd81677fe30b1731997effb4fe988f0b34f96c8920c117e18485cd32eaaa74c1c1f84dbeea2cf5096d66ab9cfde0e65cb24c82554e9165476e9d4b935570cb0adf7fe7c15951bc03c26e12295d8da204220424782647902575b6560e172535669f034f5664e8d9389e9d93f58bfeb290c4b5d39084f924bdb2456a75b7dd6a568be9531f0a69d372011842c20567a6f1ac496c63ae4f9b48bb1d285a412afc67284a350d5bfe18eb049f43268b998e6980155e8f231c49a5bd4e8d4e8aea4b2ea83d252c156b08c973bffc8b4498bc9ab33bd5b0fdd317e160b031bc5753b6950367b082867da2d11a5d34c5355dd642993c54b5feacc3b1448cc72f0ad4df1159ea12335115e7bafb71bb0d76af44c9bedc2070fcbbfb36fcb88ab86684929e1053925736ad2e88011c731b293e0fa73c202fab60adec36dae4cbf0252d6c46ea76cbf8d2f59122ba5c01057595955ae2d9acc87ab821c423460fb5be1034fe9aeec9561adc80d95387d82b0e924c6ac4af58f31a20541537ea762b2fab57422b684ca95204188d823d2f7c9c900e0ea445863343847e43520938bab887d81538e8eb936bc4aae9b8a9be5f18cec1ad2cf1a767b2030d5f1d85bdcfce8bb6d21806c9e7f0b9f77b86be6f633d91ae2ece2e826ac4c4e31b875e25e476b2ea89894b6805d349bb99d41c1108cc84af010bea728dc5768a9dc8a3d8f977299e0f76b3ee3bb3d4bd727c569fbd855e410b93475dab51d61e42a40753011e3190d58b53e4f93b6d64fc384e2ecd106f16fc0afcdfdd70849d953a037b60e9eafaed1768446fb468287d11f4bb42601f8e3e41841ac58826c6189acda3fd0a70c9d7792f368791418c8e39e4e26eefadf0ed092784ceef4799be02370b075032c3179aa66558db0a2397d5c8a87b50ac49661d149e32ebeda5b0edc419afa4c63ba19e6f71eb3a1c2d2b41d5a4e357eca4c3974bf00fd300ff66722210ebaa53fc0bbb239ebc3b778e603c36337ef5deba04ca0c9d388a917907ef7539abd1cc32f9cc4f364449ba01e3b7c11dec29590ead6254c078bc35cc368b88a9074775575c4b084718b99af999e57599e1c1f619a740200aa6caadff73508544da8729054cda08f6c93ec723ef55e7a737fc8d35c10d6d8ba7930755c01014d1a930faf638b151c41a4d93a982645264c9c6d1a5dbe08b4d937bb900e7acb4ed2560731fe6bda0f946cadcbf4708fc2e0a2b097b0f259ccbe97460416baf5dae28bce08d04050bfa6cf886cd577e3604a42af3c3507587519e0ba5dc98eae3c1e5e95925a5de75320d65ddf7163de8d203d00d12cb197c4e1093638564893641620fb3bffdefc88119cb59b7a4790a5cfaa07e76f1d71716da62bd8c250e082ea9470df3d462259532917fc00b5fa8d9f9eefc917de2483cdf2fb5f6a34c0fcc3bd0030b7fb0661486fb452f6c508376b55ea3c0512aabe3cd9e37676dad4f1119aa4cb12574eaddb5ab31a4f0caa7bb268493cbdaa9e4ccca2e1082565e55b6a3d7cb33282a6a14e64b95a7c4272c5a6e43b1213e360e6ed236d447082d97eafa04a58645371a6990a78a2fda40f12ad972e1e5d6aa9f0cd3498b93096ac25c89594dd6f262e006f185e3a0e811ce53433dc0cdfb887b3856044e7f3e105b0f046f564fb1c9cd8820c381dc287e714e0cf0e14289fa7db38944ac40bb9a15b285aff73643e5a83edceb5cbeda46b44503638bd7fdced406e75a00f1012f6742c4395dbb78d0b62073c117525475de6c41a9979a659ff6062790c6cc20101e43f59026320631cc26855fdd56bcfb43692f9e85e9390b9e10d720b74c523304a049f6d5a25595cbf820c52a41c5b683af0081a627181712e3597b6d002a123d6a054bfa532eaddaa1f9c2fa43297d1db09da529ba77277c7fec848d095121bcc1fe0ea2e4d71b7804deeb1194f65b3a3defbdeb6ba0e8d7be27454f985105883d9f19c009c25ed2f558c8552055d4fc4e7beb8673e5feb4354df06238859f31b5cd5ca2c1985e9d7dfd6ea5e4cc78b32783196a30052813c453d402707c9dd481d22d66a6c454cd65959d106b3961c1fc04838219b66f3a9c0d745895f849a2d68e8bdd2768ab90099289e4ff875b174dd9d0aab3844220aed91f4ce8c8165a5cdf54cdd40ff0b4b683708b18ea4264ec849a159cf2500a62320c49be9d0908afc423d8ccf40f6f16927c7c9e029ca1e27ca8963c20a8f15eb27cdc542102bde92dc18bcab89fd3cd6689f698741cb659681d73beef9a44d50cabcd6ebdb92a88f164b6352d93b05d236848089ee5f1a07598f33279907e2c1b9fc55bb73df30945321dad79a141916b23296d07b9d2170dddccf9fa146534333a9985739dd620fb3bb3c88c2822f7bf4133e9fcb6cb960605fcc06cde5e5b94186d045bdccd248845f7298358e49832b4a3ce2c0238747aca0e883ae39af6f4963ab029ef3d78814e6dff8220f5ba41be765470fecd3209ba44955d00eccd9fb924ac175967c264c6a445f8fc5ef76651d571fc7e4ff731f0a564b408835144d87b883d7aa3f24750404c06d78c9f065b57a7033331883a26fa1d3de69aa65f453f6f732b623b23a21d6cf1fd9a35ed77b144ccc2cb0ae39a7b744f8eb4856303818e5c82d2018bf22a3cf078e359753930dea267f48e4e9d3db1d5002706c724e494e462ad471f7eccc734b7dd02a384c4f402b662ff33c35be93c06889897d57657e7977dfce32a0f6d02ce3adc77b34af9f22263ab404f88d1d9573e6a1f1b1607927e81fd79a4d1e8ddbf3a2c8e1a92f8141a22c1a22554bc88ba5be93af5a54ab608d1fd8cf2ef04404b5f8aff50c82779c3e0e68150535ce4e04bd97aa28541bee0e214e14a204708356bc113cda25fee0b1e50b9c00645de6cf6ae8c113225a3274a6303b34aa7408b8e5c5e35c5cc80f10f24cdaa6c87f18b4774405a24e301b712408287d9e48a72daa726d863790fb46dc3543c8b7cb7a3a1394b70afe84867141a6594d2d36040908abb65d6eb539929de81e27217e49450411fba87c8a175df0beb3f81c64559f45532bae17626adfa837056872bc8f51628726817274d0c17b6703aa0c559397133aed1a7b12bb1d6029112b79cf5a26529069098aef22f0331c814159d4bae0cc91db603c5bb0e3481b479f951c67a06f075937d19a4e2d4cfc7fc2e604f59c383a88f1775fbae93a4c22c529a4581a0ceec498a782cd6bc6991f4c08133f7b14a866a7b13861ca448d738ab9a8e60a2015dbc2db192097bb1d87df887f85cfc2d462cb6e1c54ca4090374bb12e91d331c83da119cc96c8dabdd859546c3c69be55a6dd44924ddc6941e219f2880595749d9d70f9ead23a6838d09a5c48cfb800aefa1d9978105d344d13eab180a0e578018e103145a6445950fa86728cdc7c59ad19a7030f34b25f27f7fa0651f8b667db49140dc1596c6fb969a6fd2b99d1ca04e3b380172b77aa7262d428127937d0d8dee9f40ca82450a11a197bf7c4b5db0b1bac56f7f28d629cde4694893d7ae88b29a01bd2a2944a8afd05fbc0adcd3c32e58047433ac6449c48f360bd922d28f180b8c88629b92a95bb77501769136a98b3f2a12eeee8e53804cd74351a6b2264c2444a240b25c9330580d820ef0cbd726f6e1749f6faf279e37d1663976312a4b485f1e18abe117d2ecdf162a1d116b18975bc27b6e806df24cbc0618574485beab41b8dabcfa9e0530fe93d20147ea837038e13072a496be7c23ef60a074574888d979b6a690a4cdd5b0f67921f20177ad9b782d86ac95c2b4acd297fd34d84e81be44750f0503ae406e6d909e9d0f46a9009484942124af377600b4a3037f68948f14240406a38f501aa1257f4936d3a0736288938b80a2110c6a2ad1ffe29445959a62ccfa95f719235e4cd94c83a863f75098cfee97cf6c7d5b0dad3759b35db5ed8128cd0322919e2fab0b7d08ecc302708b99df12232d1c824344fe3670a0c21534f89e0bdd2f7740dc2ec794de2728657cf141e0456cbe33026be29b8e544f6a4e655bc6506cf9e9540d80a3b92bfc78729e5523c8f06d4a942d72fb1bf7cd3451a01c0904e7980ee45956d23cfae32144941d336cd397e37d509e6124216742ec8548210b9a6c9cece18671639c6d704f63bb6565f7690f7678721a690af9fc2987d0548595fe30dcecabf6192d83c78f8189260caee6bb691d9e5e1c388a1a4089baf06861450ecbcfb02efdf7cbf0a8ceca30eb394388959f2f2375cf46470a9dd6671e306563d697e89c7b427bfa7897273292062565a4b0a3f85fe76a015cf2ed6e4e33417aec98284e41dd54f517c69324cd5892b9cf478a8440b876e37d2b6bdf2d6833e91cc56071aff6dd82038f0a88bc643f7a64bc074518f8e200719813b7a096f4e435f632ac612c6657a4ebe74d23aadc5b5134dd160a474a1a0034352b1daedddd909917d152ceb277cfa11a74973f15c2803abc6d71fe45cf1a879adc8824246f99a8c3e00db7b29c5e1b3058b7878fa9db805fe1da0f2aab3e50f687b5aac7023f69c779d41110cdfb54beaad3db9b7e139be161eb7b381debd203c96d3b71dd5820e229067e67bc44cee6f5416fe2043f70a46a9ce7a88a4a41b037f501374cb9968144aa8606ab69096c65d3b6a6d6d9f9bc845e2d09011a0961b64011f35a737b46b4679b68599013b56e54b5e277eaa384b819ffdebce902a64b8d002c094124e91e1f17d4b2bc8c20d23510b6c9817e45ba782a94284683fd4dc18e474dbb636981adead3251354106dfa25c1ce77da0c0130ad39f1977d592629e1125846d6a56336a105d8fb58d1ceb641ae013772cd3ecaf0b48acbc4bd3ff9b70565709f5989960397a587c4812e8cee5d01e27ece34f5b16bc86c37312ef8cd7307aef4e6b6b66ba1fed527dfd5ae2d9c77f3ae61c6378422f422e9ccc85541904d391dc4fb7a0153baf24700fb94965adf575e1596c694a3d3a381c6d2db755f630c490017e9a4ddbc505b0bae695a6d42dd2a492782cb59c5d5bf5922af9b5f3202120c53b3eea8c9f2021b0dcc3c943e1ddd0e843a7d9a8ee0ced8dc87a668f6deebfa164db991e1b032fb462585933b6751032b651960d0f2d026eaf07565ffa40cb8b25ed15fe84544923eb111e29d2efaffdbabfee193a5dcbcc5c30def70bd7f585d37cadf6f2a8897b0a5ace33f8302b64b284f547a59ea976592065a23280b35f2780231becc3198b035913e200e6e78f38a3ef9ae646877463bb09c326537937639be1320652dbbc72cf2b645055171bc9601cbdcbaa70eadbf64c54fde5f78c42f17d03351f529b2f3cc55b1bb9466889013c6074210694ba8c3944ddfa8722929e668ab163625f7cf03d3a61078fa779879d71879d9f35c1829c26e55e83adf9cb255ff4bb0ff7fa237899ddbeb2b71fdf93fe59de5f6418793464477730cf1faeff90781cd1ae17df7ed625dc71e3b4256547fd8586a819b0f26dc7f49adbe7f09fd7260259d5845b50c43071a2b2346410b054df91ca0b1ef371adba99a9d556be71a95ad49980d26185a8c6ae7808a999a34bb9375b423557648d6ec92d1a68e8aeb3152257fd3573477ef62471562ccc4474707c056d612e2f1863fffd44a0685990876b1a7c1a6c68fe27a1127102413b432700ea794187c6f042718ff6f6c0d5a532514fb5c83aeae80856d64f0d5c169e4119e56cff1e2f878855bf37df1a5fc22cc0c6c98a933efb111054a7538bc1c32976eaff63040876a34e60eb67e442f4c126442abdbc764ca1673e9a8a45a766e22c30224a2c41bc9a86b10d6d741837914eadb275ada129bf185b0b9c46eb35afaa91f133d5428d1fc0d251ea7e8e1e9db869301e22078810d9a459a8974cb822724f588298a4b0b25c69fd6888c80b94df1086714c726af358b756f1fc6360b469571aea4556fd16e8a2760a2507d92a05d2eb7f6e35e9ff1ded52175d7b163de694a05307c59ab70071fc791c81f0b8ff979e164e3d0e1bd3fae6d44677e4024273174119f23bc87abf1189d5a34ce780d754415e295301f711fdb51c80dac45cbb17f40095220a97a113c50aea2e979e044aa17161b2ae9289f408cea251fc0a36f84174c70dac9d7d4aadc4c3ed722e7c2ab3362e7e02751cc9f7145af7821fa032d5b3aba9e7fc3fa4e358501eda77ef6138ae9ff0e0d1cf1bfa184a350a85b5fe1e75293357819acdd3afd62137e1ff59077f0301d32eb45ce0f98d46a4d46727b091a64f9a798093b97b92eab9c249b00ad3674e842c58fa9329f1378297a8a75f1f41961ba3d9ebaa2ac65354d92af723edb1140eeb2b557117f04bff12fee3807f9545998edb4cd24ae37ffddda6a15eb750be48630bf0147a7c0f44ed909d96bddf04658fe009600344284124d14327f0724732cd16d26d9c4271653faf0646318bd4c9198fd8a7014341a0a8bb02aca93268aac4b99da82c7e5587e2cd51c45214cb7229cf9b43ebd20c72bfb14f73978d1a42c23e4a19434d1052cfadd44c3b45122ae519d2023d8bc19f61fed2f1cae851b94ee122aed9c09884b283524967ef9e0f80fda203827de0562e198c2e1f0936a1d0dc14c53e4a64e2de08e5cd384e32caf568e4954579c32a2fc83b6aad4adaa702b0e8d64ff2ba18973ee6d49a35f3c805393b71a1c8a3429d2ffdb623b166801409c82f00374482053462dd309db084a50b73f5ee42de462c1d4faa12d3fac9ea167dcc4b7803e2315bcca7a75154f5fe1802291b5eeb94647c580b297d485e8cf17d47b74dc55632fe149cc098dafa4a8c577c0d713db689610726ef5bb95f3e4ab1efff63516dd143f51ae6766f91c826df31f9a80e75b5ee97bea1e04d80677ea5ab55e08fe736d5854746185d1adc0ad50f3867058a3439d6d38618d85018251831d37007818056263c73993f21dad787ed20e203e3673dff800071902fdcc2089529557c86a5b842e7ef5c7f84022a1149122a8e17a059c551802f07cc64bfb93b69b116c247f26b3e6bc5107af75af33383b87313d3ebe6753b6d0682f12a4ad0b79b4e23a8760a8bf7d03fb4cc6e65c954fd578471313115ce7e2d22898892ea363828f9f1a5980509bc78ce5d32e048a0d86533c881edb05847e2e922fb12283e738f69c17d79229f848e923062247ab94b8d17ed6012d258695917cbcdd64a1913aace0b10a61c6c947798f42ec81d84f2860390d48ae9bcc269998f2c79e66ec5bea917a849283c3c705851b90043dc9c784b8e5762c99b03c4696258c194991e64ea43ae20dc996f1f86f9db8e670e56054d126a679c15eb911fc048700299b6ef609c9719e946381850d8129079a058ceb6252aa2322b2dc797415e196663f7cd40c5edb42583769dd8a547a4a6cf0a1d7f169796506bb6cc9eb1e6ab4badeb91d4335e6a9f680cdc4060b9e2fbdb87fe950ccb19928ca1742f99aacbb63c2c0a64d7698f654745d34155ccfa44c18c3a841390b6d38c97c0c2cd0510582ecc6bc3ad17b26ca34a2fb215b1a6e112ed79508be134e09ecf97a84ea5fd9eea2c610e4aa977da1cd9011207630af99bc0ebfb13da475e51a8b92bd80ca80017c1adb0b53d66d009cf97e8598139cc072c423441a75501f6f45ad4c1afdc6b7c18ede322a5adecca571632a7ca23e259d0b3aa7f207305f23a17e45d4a20deb6e684c9dde60ed83b6e121ce158c704ac6ee642644befb05667a4b5fc4c01dd37e4d476adfb6ebaa138e93648f17f90c963a989aae13e793d29e1488050d44ad528692c5ef8444193c4687d984ea17efedf49cd452264ff6916c5c33a5306323adc1f4859fc30df9c4a09dff6eae7240a7ed4d6885fcb63ee56e975eb90915dbe883501308aa36b0eaefed2da7d5f5e7f10e37c544df6f643a7611c9947346b59d71cc60c46a1d5d6c41d450dbb410cbf79f17e202c43788d89b275d838352962450fad12b2348404315c347c994688b95544b9f85be0cb3ebea3d961b99681a5b9bd13c329aa23d0a6d2ca80551323394e1fc76ef27787d0eb4b89606161cdb68e5f538a41c7528bd9b525624db2efa1dd18be7f14e7e858cab45b2306e726789d1776b30347fe90d023495b923b74d674739702c9b9901cbcb9a4e7d3fbde930d6ef6c1775597df630baaf859171dc51d1d93fad28e8a452ff234801a9ed7d2cc2b1684453bb99527263b9a8c11d654ebaa5c4d098099e49332a29dfb6113293932dc50d822bd19af295c1fa8265262dfa0a5973e750e33ecd037f8c76538ad373ad9f63b9e40e4dff4b85e67f79660e27ed1e45d10d710ff4b92e0e0a887bc72c915e5fdbe6a5f2648ac95f163a8edd061bad1be3234b27308c611b91043d99acb0f64ef6f6ad04d56bbc4e779248775e1bc128fe0118e33d3d6a44c0efa759e7d857a77b3436f6f03d455a29286c20e6d9162cab9a5a0d24d1c31fc7db8964be9f4fb6d7435a0f2d165ba0f50010af81e0527a6170e738ac0d3f3c1ffc77b2cd750627a768cd1200c86597fc8d7671a41f1fe151ea7437534874612c25cbe5a29bf7192c5670b807de735659a91990d10938e0cb80e9a3ec6d5f53401f3670b9f3c2cc31ed0e6cc1311a0eea6989f92c9e8ce89ae97073a0e84194827076ed2e7e2e540485e5c5cd3c68ba16aab9b28346116a97cf101c815257cb0012250658c798a256a8e8144d3eb0da89423721cb0a25132217968207a076f8e0d40dc0607358a35963e6e74ca502187012cd8405efe5070064d9f403f79cf56f66a0c0225a7c7a1fa97d0ef6563bb6a42564e43539fc60006a717829a05547a3b5af02c43cfbd40f196ccd5c9e4f037042c46439e805dbeffaa7ad0b5d3018f2f3dc72ec8521e1c5fb974b90503d5c76b1a80404695f710028f69e1f87d34deda7b7b28058a70ad53a5f74eb429c44a52f046500f5fc9eba780256c6961d14835dfaab413b21fde04a2800dfe89096bbc878648bb6880680d9047479687fceb201b1d57c0fba429aa967c96bef8e6f0ad7136670656078e3ee3fbb4d65cb0af1d4f2a0a1b85fe8e0862267f748390d8d45eb1d16e0fa3631fbe0933de0894d3c9697e5ada267cb8c050bf80906ee0cf5fafe4517cb801a1951b17088ec2512afa0f4be5ace3ae61c343c627f78e354bb97cd0629d1a6e924571008a63440fad7d5f69cb6378a2f3f456d2ec4f617f5e8ba8504dd783b650c988e5b49585048602970a1b5642a00089405d71ca4f7b40f617bbf36185784fe04ec4d9b5a83ec0fc96e1c76868e8ffae0e7532d6b7402e5864dc77eadbe9275e403bbea8ada205f2550c50880e9b0342851c5c53007b7d8520847fa489c60b3541aa4c35a7856af6496392486cb14bf83a271a3b693f742bf102c2635b03c601cd23efa33646f5fd9b46fa5e44204727ccbb221934309c111ff8758719693b6ba2e89d12a3ab6dbc7b243f4bf6c8cbbb5d257d00e40f3e1fa92a9b8e9e46493256de71d1c9163922ab0b2a7d0a9f5cc9a8ae335d000918099ca029229a66de2aa651815c8428bfa1b94782f799057549f606fb8ad3d58cf1a37065da603094eea4653bc593ba402fbd7b20a3366c246981378dd291aa7b44442e78bfc2084ffdf38ee82b8d612c7f020babeac14ce5e3ba1fb707dc997652417564188382db5c25be645a6d41b16e817488c1a5120fd866885048788d075226599c81c1df626c59c5add1e5fc1962baf239237f6430c0fcbf97ca33111ab99144d798e18e664d5f8e852eb48d85396620b0f19cbfd12e0d867aec51d53cb01a81382e184fe12b8e90f7ba568dde8721f7abfc30ff322cf097b48b8eb1ebfd004fa5aa9600184ba35118a8cad1620626be68c7bb9592970a2785759d6e86b80a0d39ba9ae6823d4c810a286bc913df00452d023c6e0eb2f04453fd514b413683505c662c005bfdd9be192be185add7af4524807145492000d2ba8426b0bfd8f5de09834e0ea1f36cc59e79388f0c9b695fc273858ae101d3bfd2464bb53854f9a82418dc01f374dbdfbc5655b938c3da8dd6a0a0ef05a0bdf496a7d24a919181709c5ea58313e4f5aa1c1cdfcc38d146b4d784bc92e95c47a132b7023b613795646873fdbce42803e05d0cd01a9456a3144b18020e76e185d232c6edac45aa67b135c050526101ef051fbfca73b21c0a02cc5a9516dd0e04ea10ba00e3fa2e674c251450122b9f63a24cd1f262344f9350647719dd8fa4c24eccbaef7c4768a043cae22a5bf7a659198f14ad7cb7533818cac48bb6fab60a25723aae79572e14d3ce7bb56f96c38ccd7074a86c5743ee6ce79036e66c7dcf7e119d5cb789e85bb10dcab84b35900e4d40d66c1c34c4f2f821968b3923fd314bdeb55be17aca04245380daf4d6624866311133b40e4993951ab5630f9940e8ae0502f8d62678ed5278773c3faf6fdf7721aa02af6bd7a7f8cf2f71adf29087aa8b6b77715765826b9f7cfa73934d1c16c50e8f9101de132a073a7b3fdb97ba216314bef4a53bb8dd17bb72050d5e4a7951a176a57d6a9b4eb16bf4aa854210b7496ab3a461c29ca6bed739e81f59e071b53447c40be7ac99bb6d166de1f99e073f392a193bdaa7eba1ef3f7f5bbe1c9e92e063a8a816e8ea3f3ff2e70ae67c4dedde7c332d6b01fa00b3e8c7b168791d82b8be9a15d6118cee92786ac1743914515100db77d168a298b3034ea20e4a4d5d024a470048598a83695829585f56d995eaf536ee0dd0089fb9c28184563dc83f1436837b83d7cc3729c3e9e10f26fab415d501835712bc23eda7a28c49a63a5d07d202aa2f6fdb1da32dc38bd24110eb63c8102e11d5ed1280b7ec3f741b7da61e7eddd1b044a973750132aee7b96c2d839e0d3d41d506bc4e2cac0d001170cfab6e2aa593b166b19b9140a4f472f02a51d0b38e31165294dc7a274ea029eec584048b2332d551be042a60c59cc91149f59b46162de580a2dd2e2e7d225f60a215f7b96b89968390c1496c075d1015fd598842b8612607d07212a77294fcb0a3ff122eff95e06a5a208bfd4a6a91622d34518b43f5c869335d82009033422fa0383c9a47c21a2f00a80dd96aa3545e1928fc0e47935e2305da3e61e48ca4d281fbb197c0b91c43cc1696ad70d51197847dad972cf9238f10ae36552ea649d176f99f987554654a6b24eeb74515a2e909a2718594f4a3b47b385aadc00ea8c6ea49e2c2f67233f66e14014592f92cb2851836052b393df3df7690ff668a34144b2fe60917fd8ac04d177c3669d12cff0be796aec9e94ccac0f0e024803def499f42c8edcca491055984141164ad60c442a9e344b62ed9d559095745ba141602fad45d1b0562d185e6bb8421ea34106620010d574da18ccfae55d55f6a596afff2b474b8ad57793be1d888fe579e4eb6047f7356e68ee30c442403bba346bcb94d66620b8d24ab0382e818fc5de51b503c6f8485959aef311a511dccb861f546a9f074dc0ed2028b02a44805efd094051c4a1e01d221b7bbac524a60cac58667888073bf6c4803e037cacbd4736bb1759db6fe3ba8d48298e8adee76750e04a2a4cc2c3bb2292f3122468a9b4774e4e91d088fb524ebfd01d29e094cde2009fc7ad0ea755dc1048f53dedb49bcb3d8a128f61c8f6ee56d8ec2355bc5af5e0672a9a0f41349dc3a8abea87c0d9deb8693180736adcdd35e07e75c98941db24d6fcf50bfa9a76055313fd48a8b6f5e2ff3312b9c492c18ce7a02f6cae990e7705512441fd1ce32c511127be3b5c6041b76d3bdb196affeb68a85dd6ae813cfc7ffdd8488ca98227914a2bfd21edd4e80e091bc8c3258540f3a9055edc0321476f940732778d9108ed69971c0c79a603cc46a5530199d638aacf3edd41c8d3714e6d4489f31aaabf52fa0a0d64743e87ee2e5d8f9b1b56365dda61731c39f528f477c2d232d62c25ea7bca7eff94333e9cc22a9c127b9d5e46fda01fe421fe41d7fa742c5a847bd351e9681830a2cfc31ef7dfda2d8583bfa70189164754ed51e284893471c2c27277a98016d5a330930fa3247ceddd90474f273a09f331eb38d09bd3152b1c44f5fba780c90be1682f4315836691597fb9f43e8b3b35240b80bee139034eaf42672b3b19014954e1f04ce9552d915dff0a9492a37f6f5337d4df34fe112d808bdf25ebc349c6f39ecc1cb74af5d09c193d11b3c798f07254258fa60231e6994b609c0a8688d707ed82bbc1f2c86ccfe380785bab3864d9de6eecb703f2119491ac75ece3b445323b0eb24c81b6890d5212664a8eebf31639d2250ba96994685805762541f83e458e8f92d603c08ca8c17ed410f4807b06a68ca6243052dba3d3e6a6e4d7ac1160f9e83a6c4a9653e718fd8a0d5d0200db4f5695ed3ce9fe5d74f104b509e917a9f537191e36ed33b7b42100bf2eea794d8289967b95e7e37d79be102ad82ae718160f1afa88c76ae2b6a617f7e93bc7db88e16793d2ae0b400860a8fc6eda1952c21931799da4d7475828ac4e29daef9c261d69c4e04aecb4340959db8705445ec0069dec6130f1d18a097102dab338325abc82800d14818e9924d351f44f95773d04f1646dbffc889a88bcd28467d6eff739443b8823d9a4cfee693a96fdc15da5a0c17566ff6dd7400ef44e48088d52ffca79ce68e2e23d73458f65bf9467956c74bd504377b207b4e605bbda5c8e6750d7b5d7dff79339e0895e314a3b7bf063d8aed54dbac394b6efdaf477768559e5241082529a7a1208e2fd5ba55d85e992ce90d1c5e44a2b47a97ee4887b4ca778d408fe721d0f9354bd53cf17c969f398a76e721a2f92e0b68e4fb10891dcf060102b484532149295345eefbd4759c0db7d8cf3dc88c6fc34e8193e93abba1622f89840f2d21f72ba4ca447e0b6f73222a32e56b5193ae4f2d2771120d0228072d7218ce77ee8197fc01397ed084346eac458ede2413e411fbc95b91e78da772091de8592d8b4356d43e81d1aa8651e463c219a120e154abab04ae4568b463f104b57296a9bfb39100d49c28cf1c719806fe0d79ee3d5e32619b9fb41c510fe83ec729f218884e7bd181eea68f415fa688c4bb37316878ac5c4c0d666a5d8135a3d6c827a00e2b9433325ceb6d7c0696d8feb1c950fb150ed54a0dda88a563a84df2b5e37ea0b678cab460c84411f1538d0dd65a3ec5de4cf0c3ca8b487da1445c6e1a02b97982a719828c486b34c59ec0c16f140e44813e2c38e1da7f54dcc7c878281ab1492167021ffe40a9b5c56715f051bf525becc89bd97e543a5120f916aed56d69019d8c1d703d1a19ae38f1791c4da632f9d0632d000ebae327488bf46391d0abc3c1e98131e95823c083dae1a30860761062cd2d3cb9c60463cbcb67953f019077f52419e385f410119636220194d6d1c48cdbb04d933a1e591ee8f3df70d33a92841a57383610298606a421e121aec7e35f1222585328614fadf7674414e635b36459bac721c5660a70d9293f4fd64d9ab6265e7a4de49c205353c8f20327dfc910d531231da5a562a7e28ae1192545238bfad2e616d6892532dbc09034984b5905956b2f2794bbeedfc3678764ec104f8507feffc79df90c00d614914a394954f45575f67fe4d6a2f823bd476d5ce8d252b55175f698d725bada9424d08b47413abf077ec37c03dcb1da1303b8d97e9f1a1c227a51101f6d75921294074fd37370a0d65541fb9fa4977de1d542b98d94742d08211b97a483ff63e4976a36f51ddd197a4e865ec7c3bece84d88fb3659873e5a2cb75a18288204276614bf7b516fefc2300761dbf29007e1dd84db6a51d5d02dd5ffe26010f2ccf569eef88e331d4d6e9b1b6d3be695215ab6540b5335691721decb8bc1301c0e4eb547d008dfb916c49debf5e3d5c77a5b97cf756459c5bcde33af75a1c9f5b432ee3a79e77d36bc140971d256e6a211d5fa17644a06cbd00e7ab791aeb804920d8410b5b79b4e90cb72a6b29aca3cef5576e268f812d75bc973f604e2b6b4e5327194ce84fb7b284d560589a67898df7d06f950f1300de90fd34dc1d247abc374610ae63c971482350d986ce88f111571a4165661b91800b72be620aad9fefc82310034053811a08f0f0aed56a4d8f9206f11514d1f0ac16db842515c04fc15be54b047ff9dcad7be144dc765ff566908f0044293ae69f991733cbe38652dc66fcc9413af71f4924e36add19a907756c38b3b5b801a561953994a3befbc082d13941a26ccd8e97b8fbe9d9edc7f1e827b0ae786657d97e9fd106d8f91e6897ce945c81932437b85ad516698b6b1447bd8a5298cb87cbe8616e69017a2314f53ba2af6c6ecc00fcf9a0da605a1dd220f483d14593ff533bb2e92cee4746b4a833330c9cd3bc648982e176d41d0e965ec1318ea6809e1c64c63417f87983714d25950d26e0e56fdbb8550baade8448fa485cb2d804b12016be1d9af962fc6f715af9cd141fc0b58f3912f599ee1f8de42053688b0274cfdc207c413296a3a768088e1f4e0bf6e45a89133932e978dbf5e3a42f71781a845be8b484f40114be285a63da57b6e9815bfb9062dd707f8e1716f7b527550b807bf6285b2707e9df9de14134906eaa0e7409bf4043f20671926abfc3d6b417d748df1cc1be5c688fcc91349a224196e9421aaeea1bc67845c58843e1a92e37fc903ef76e52f1b59018f1e4b1178ca28170a622bad08664350191cf3f90500244dd754327407f8ed1b3629dc3e8c7a2ef525899a8ce551972cb290ce4b0006b9d45e44375039b0c194d1ae29381500e3e8c51d3fa971bf48daf18c8e8b9d0763b8f141961e80aa2f1ca4ea20fafba97909c669fa4fa9db27cece60092e3ac5c7349985d925baf86db82c86295973d183cea0611eb57b4f9170d6a4a8f066230dbbe15ef97d14f22308496b62b16423af6c4e3d32d97539c01735a25548d0ddb49119a6989196bc81f372e2ede51187d0f1d1c7789f744de4a87de6d0112206268057e7fb982681acdeb6511bf2da70ec3a391f1542228c3a23b9025c158b83e00f03da1bb9e3ec6c2fe0989c519a9e9e21b4f0d8e07d2367d1d5aedafb38e87be4f808da21ff13417f8be908181af67ab3b51c8c121e8174e13af04fe85b93d4651f541f6db745b3718883726a25cf9a0d9283d1e1e8d598d121859e8b509da3e64aa798ad086e29028806c2de4438d3775a060943d0af375bab9284e3c3a131d981c8cc3785faeae24622e5f87b06fc83119f3f728400fc7d790e70572dfe909d11013218ec3e5ba52884bed9803797f11496292f73d795ee0206ed23225b5af1a9e8c722cb081cbc97a68a450d455e382fd2755aff05da5bab498c90abba44b3f03fe9257a4cbbee1291429d986f105d3d8aa736bdc624444740e3c744576994fbdbc7a1bf949ba99eb113cb3d90ecada91d6b758fbf07fe79c56ced06962a3d51181d8e47ff0a66bea5d4fa7f3d4d9a7681b99b497e57af3c437def6360a44f3ab9c8b7d3ba0f21d73eb093336846324a120e47f6537f532a8e7a328f53a9a8be269318f31ceaa25304a0c4db6e0ceda2d2dbc504cd174ba6f864510e628557f13643afd593c2e382339ce3c6fa24c00221250cf1a9e441a35af53ea6de4d014e37a7cf5484ac45156453a4c5565e74369115fd505e2ff7fe00a296712612d3e9714a6a38dc4bef3c308f0a62f5095588d2957f362fe5f440d820d94040e628174b28450f781771ab234b03a282a0b93b325fb5ab4456a32e071ed83b539efc35413e5734fb527a73e4e094407fb8e1dfe2cb5d7e6e1e4335291f5fce22afd1661185211a5fb0f6497f331f7ae3b061313dc274763de4ed1e7b9feee9adc1d319fb2874ac162d99cb4ca56852017cdff765021a388689ce8ac35b771b5acedc6f12e8d377a3fb10c126e1277a503af594d6976c6d010158ab9d8023ad011742b7e035bf9c29d113912b514ac01eef3d90a27845a4094ccf63c8cce7c3abd5aea13aa45a18bdc4aeb6fb72e489b2285cb736f556e712a6e1088f977bd27c953385a4f1db7cb1a9dfe14774f3b4dde20bae242d388c90b4ea63fd515330ada29a4b0968873d14cd98d0cc88d20a67f0444dd8e9486b0a957cad909885705deb791089b40e5e8dc4f21d830e9aac746c093d2f1923db53ee725f22da3ed90e0121ccd0a42e169586e0fca06c3e4e9fe7dea2c232496c625f6011b3b6bf8e21d2bb61aaf28f7fa3afc03b7e15e5bc40f858d723d1367e65871d8b25b8b917d8009084e69032c0a618e7b5b218928b01027b4cc8786dd2f4d6a846cd8e22f585d985e0b8300710396beeaad4cf252a27576f1459bbc3e4eb7c5f48cc44c1d5c1adc721c538c18a432293a310e9eaea5a1d0736bef4bb64c6dec56fcc2a0d75ea141722fd2f8f9ef1e15b70ba4d678fdb167356613e4e264181fef776b60a605a39629ba7c8675c456fcb211203b34ea72deba70129d399bc749c0411c7722cffaf43359c113454c07c5dbfe846660e374bdf84f022919ac66f9b73c3defb6db5849235548dbd857cea9e3005a31a13ec81c7a08e14398ee23f54beff73d8a8eaa0d7a658ea1142b219033070b373bfab879f5e5ee9a9dbdee9060d68f5e04742a65365c23685db4d5ada6785ccf70c96c185eaa177e407236e27bb4d29b011a76be2f323601103524a6678d4f88df32e5855b3fbe59f0b24ec285e85576fc6538a7e9cc75534e70816766729c05198fddb6e9d6c248d4cbb5492f10f8df502029027823f808dbe6b42df07953648d789633607a5f1d9eb5e97c54abb1c8f74f1fc14279eedf9fa96a2090083751e866af3c9ee394fbc464bb5a5922e65722ab1523502c0e88077f7700861148da0410fabbaf00ada3c88eec1de7667cf5be321f77838ba587be1414e9ff540ee4b1829d38d06930369fe3b3cb206157aacdbc26e446bc21a14d49034627bb476c9b620091431ea590dda99280725277e81532655e7f1e910470eb26b33373e3ea08d0c76b4424ecb0d79d78734df5934b548574b5944310697556b7bac23071ad838482192c44f7cf829c1a449d4d4661ca3a6862350ba1078e63619c5198378cc83fc1df7168f3d4b775a923c1287dccb4b8dd6280c5294ba3e59dc04adb14f461c1e5b7de9ea9aecd4550b85b681b66c6b637d8a607142f2d69ed1c25d6ae0c5525b48a63a13ad597303b451be63ba66eedc75ccccd40765fd9fa853a81e63e83292c2de8c6d6f415d8e6bb7d6c4bc070eb9a456a2cb5a6f59c532aab890a2f9dba868a1125817231eaff7e59268376eb51a2dc920d182d176897d5847b49c217f0322e22c0aa6ce63d67110bd3944a982125e1216505da7bd61cf036a548197034a7cc58612ea387a6a9dc9fbe14908908f40c06775da13e384ac2ad72e404435b387fe52237082e8e708d87f29def0e6f44f89e9eb85f3560d5661139281699a8f970408881320c1402bff78c3569c9484e13398030a464d8d764f6f70d137c341d5fd111397a9cc0deb4fff2a253a6148fe01ee9676ff1ad3db52c073537e72b869c4d247ee53461987b433d50c176b1b26bf032b8384944290d5fd8f6c7e9c1e90b91b1c15bda0eaec5dd6aa08b9ec32852c60ad47c4a7c846d6954e88da3586bcc26f1271e90888266f54b54e3bfe31c02a88a50aecdca378cd81c02189c8d46436ebb4cc9695347d33536bacb80d6666b0cef3f3a3a16a4b631d197ba1212aa92164d61aaf3bfafa3bf7e691dc2363f121db81e95c49c8f84ba8e26f54ebb543bc7fcc78bfd3e83d550b30b7e55f90b1864aea21c4babebeb3a09013c37a02b193d15697104f8bc56fe431f655eff81170712f0f269812600215c68cc1ea446548186c3c86c43504cbec79a8a53aece6c9d683f5de3c5fd588ae52096e3d9c1efab578fa0aa902257546d73420bf6b84954d7b8d9997e9f204279dca44742bdbe9e2ec542e69ea41927bea276dc091dc6409ce3dbcc514944961ca08e5be73918f6431acbedbbe3ee1443345a5142eac0cd457fa51671150092f3f9137f83866abe5e8a28079b36e8cf042c122d723f56d7808ecf26b1e2c3ac53a12c3247b13a13d5c0790baaa883e8e696b799b23ba58ead308890676b35c466106442e3201418618589c805b7fc2fc2a6b023d8471dcd6c083c3627c90964ef5313771e414cf48cf4afe6b6508a99202bc95eae717769e52ed24452c267b6a789329e351aade4756027b14949a248aedc23aebc17afea089924e913c6830cceff3a670460ba0fd0d1cc6b96e0cb33ea64ceb5b3ade67303547b7330c0d9ceeb6b2f71e6d60cdb2081982483567da1a1a7f0a413b4c6e720035550eaf902f34594addac78826b09b7196952eb72d924782296790d6734e0e3c29b414224564ecf307fbc8466b70ff638238977e6b2b1bd0b60dd0f1949adbaedce51d778e3a92296f96cbc047007c457de78093d0c6072d4e61430cc89167fc073d2dec81923acec6e5a896ece6307b7c4a62c542b4995f3e16a81c6fcc70b58c596c3e1879fed05c1e3639ae61504c48ecaea18e2f255ff09ce0568f1ad9ed298022b27980fa9da7b5d2a84e4be56dbc6f3813bb443892bfb698da902a9d7b3f85a86b9e6f34172e6be038704ad825e8636cf341e9a5b1c34d53b7636be62e2cc3168fdcf99c6d84544fc280918134a5a6b35fc3b817da4dd28711ccba44864cc4f74445278c841be91d4f8f8a6594a5104199e97575938dfe086bfa4616b742409f06f7df7da00ca54b986fbd20bed4bc881050d7b5c62a9633a778d15ff959b2832aa5c5565a55c5d456193ff6befde5f7be58d0054210fd9579ca64ab835cb1784ef6028df3a65c236c56ab5e960e1d8b1c7f2503f5cc5ac4b25e58ad55b452917a9f62d98e94d1fd58e2eb2a52f9269c14826cfd2e983657f83c1536b25cffe5e5c753aaf6a6ceea7c94302949ed7fdaee5feacc2f5d4a5659dd673d9fb45b948e8c36a65b1aa1f2b90c94a973938bc185a87d8563f9a9b3beceef98d3b4013b31f4fd9c92c6cb61bc65617aaecd4f5ad3dbd49bf93520a77cd506e41c28226aa6dc16f4c25bacd16bc889161b9ec39446b1e88e9d65b1dec8d575c568511d748d1b2e3e977b3fc91f2fc1190f0473601628284d2814dd681197b9a5244f379ef286722203997978cf5f830391ad1acaca6b910b28a3a894f807d02b30bdbc8a2bfa649b1a0b10956c055311facdd3d5ade6339834c085543ca553d7008103f43b58cc96a4211f35f0de33033d0e73d8c490e83334d4e7c8b0a8dd8bf686f4cd26e79d191a6675ed85f72c52b4a86ebda19cd6ed01692fa9263ae453462e331961b2a862ddb0b79a3341cc727fa27d4202a0bc98cbbe698848b58a0ea195ebcbcd2b58df7e628b69615ccde5aaa14f8eb9869769614c64e55355e7a64fccd48cedfa2076831a0c0420d736ad59ed31a65e55e15255729e2eacf88ec62f0cb69476ecec16a814f705452c5ad6902f72f8e3559a838fafb4acf35d5edb502b9e5f2c5c1517b616a1eb76c0f9d6240b76bdc5ebb0ea3b40145eae000fa9d42412fd3788d9e7817185b930a6eef4895c61c37fe7cc2e3f8c71e5575cd44662e08381f314e3251e35a24a680b97af984818d3eb8871967ff84c0409c89a1c70511bc3455379204aa6f2764eef76652335ad9a6d1632d782d3b955081dc72f9e2e0a8bdc2a42c95954d9f7935d0598f104aac431b09d73b58cac47e2a36e4e207fab3773390cd83e50fd37c0bcfb587ac99b6f30375e980a840260b203b79a7afa81823cf02287609d4fcfa146e6ac642b4158149f7a4cca0557a69d3ff5f733edf3e06849a0845742e0e11432d494b6bd65f3689b2a67d531ab7c5e82fb70ad2340e30f2c49a264496d0759224323f63a1895a43fa3c44c83073d943648fd414b3f74f10e46f770adb19f5470b7da00abf5ced32b191e3decac62d7a97463ca5244b40f915955df7fb20f662431676038156331062ee94e1347c5ca0f181a65049218917bc12405e33cd2fe3ee1f468e0726b0c409af029bf214d61dc0c0cdc26955765397c77a12336224854e61959e735b0be7b501e4271f0f19b2d6b570a8cfed4093062293ec7c2c18e22c7cadfb4e2fd62f5179b32e60a376cab08319b6a348108d2a9deabbfc07b9526d75a32ad05085d9c3a9b9f47e6e0410948793a11c96bfad2820c6dbf540a60620bc0efc338dc9522be3e8574c422f7b0f4bc128fcd30dc95fda6a4a73a5c2887620e5192a54339703d79b369117d603c9e1a83d924215c7aa3eb12bed2a3bab202ec14067168507376f9fa2dc4cd435fe57559a934d0d50e8574f5b811ea881ab579de04b4d46a55539326847741eb13dc4b400b5cdc55367a0dbd46060ace696a280f638c7956480fda1a8bd91dc95607f2bc00c2247b9a5eaf478eca8d1d8bbb312f9de172b5b6f120ff914c48eb7de349a497f4461a02f9b5e5eb43457bd2240ba523ca04570121b817d007aeeba11c17e756ddf3364bff5da359806c705dbdf8cee8b7e48dd417426de632d27fd140a53aeaa62293c5dccc2d8b6f4a9ac9b0734216294d209a58a788fa18c75ebfdee7a92268f108b12f4c1b83c9d5be8fa4e346877075b41d51a7c6c5e9c548b83740995a87fca255ee89ace12a6493c9486eb7dccc9b02d67f53b846bb05afc56b1a064d86080258d6175492516995a19a634c37707721209125d6362cdd600adfea90bad40b0eb3ec7f8d9eae2488f5a86915a29d3447e7597478530a2f086ceb2d4f582cd676467804fe98e280c381dcb2932889578ce6a8990d57710ad256a6c8398ee412649c14fc7fdd1a0f3a848d482d2e3b770e00d5eed8f26a12f044b8e57578687d4c93bfb88df125fc104de081b3a5b801e26e2edfb4da4e3257fd7d10515a2ef164ff78c627ba5380f704c3bc0c093d9b32cf024487e491f4d2beb110b8aeb8ce70db4006c240088f4d1a60e414a84d851501418b21eb542bee74d24fd3a6b7524b19491ca8724ecee9ba9b96b3f26d3e4776ffb93148c45d493a789d83de6bebee24461de41e017039d1980b67eca5c8489f14a4a75d2221bdb6d479b6bc9814e5b312d2aa485700190a83e0df213fafff6e158e50cb05c570f29026ad700ab9508df1c1c10abd505952ca0e6c0c6bfe4ae3b9ad7d5110f11325650a41cc050c28e90ce815c0ef7fb4b1e327a5cc2fe24afdf46e4db41517350372b77381c69b8d00a98b4ccc451814feefa516035f0b2a91bde09db77ca8057d4266b2951775955dd0541cb34d74a4db39d35de5f9291f4655c29a7babac2dacdad8cd37033b9cae554f05da38a321b205ec0a0805b85edf40966b9acf362ebb585c58bf467e4d5025f30dc1d52b997acaf80358711f41c4ee3da5c7df582e4d87029229471c5d06ac69cb5f138558ad1a91b2c7e5d5308856642827eb4799233ac8a58048dd2ee3a254a5a13250328a35ad75c505aa6ed342d53d0ae11b2e91b32f97aab90b0318a6be6742987eae3fda663b44812b8c924db6c539a63e74c1573b22b4b52dbb1d3a6e64c2a07248e0af5c7f446470615049e9f6cfadb2989eca66c4bd6877cdc4286f4d0eee92ec82a8185af5203ee8368730dbb0b9257afeca0a34aeed888672ca74b2900ff8204b8cad939d07df4c215732b11d4fddbf0fcecd2f41bc3028674bad903e4b0e2e362dbfbcbe471771de895363937a63cb5086678e3594e7c09cc29ae4bfaf2137e086d5d4c28a20aff0e24777f5c5aa0587a5a3e82fc009a9a873a1f3a06fba77772716228f1c7c5abd2a1cf38de455ae8bc2c5b08e9fd2e469c4ed41bc27de3caa3f82ce00d8f2d76089ba66d4f00d13d0c5c57287aa6b9fa1f758f8b76caa09b662964fbd719488ef500197caff04c751384a5a83e4efc7693bc727862c04231f9d1ae15b38e70cc710ce6d4521bf6e4e7b89c0b369aeb0c8c6afe240b22d814b5738936b6495f33b5f5cd4327e264a1acc9f7d2080038694af34dfeb4a69146e8b872917c6b5ad755e4338b3a191fd6ab53f465e571d18f5e8cc22f4633afb92d01a8408247e5449f3a41eb66c959bbb9ef416a0a790503f534801a95e41dcf8284b2ed1d91f2a4bf174d264a33ac7d82b188006c8510b0e394fe60b84f352c8f67029bdd15829bb65413f0a650ce13170c7a52d61ce26cb36679ddcecbfd34ca9a934bc5a801ffaa8b62f78a2add22ce17015ad01b06cfbd1c5c1c00ee4b7129ed0a142394784535ebb24f2028d9632d3743c63429e5aa5ec2f0bf5939632a569cc31faec9c0f962678c64615f7eede107911a7feec98884f94160e2527541c728e620f708b647d54d96e3c4e55e85807783df46bf7b5a9640f71c8ca1b7f35d369817625ec824efb6f64aaeb98a11f3d5197ceaf3f2129720536761d3ef4e0aa6e9cd6201b5393e55ea6570849cef792461ea65303edd41e202d279555107e2a8a81e135be8acef324c421d19335d1f63f7b612623b46a182a1d38e9f8e0475800e010f586ced1dc8dbb49751023ba70c27e4a7ac8c6cfc045b65fe54d7842b9eec6768d024670413c49eda57d780e3a745bf8fed7490fe974737a943cdbb9a1c7e309fc31148f1bd0aab135e5adf6e856af3383e1d7113491751638caf37f2ea9de3917e680c77fd255c9d938a0080bc9560bd889ffdecaaaffe432e4bc8adf53b0f99015756ce4c5e435094c5742e751526a59c212e9042967502a53cac0e541ac50fef424e7b19bdfde79c4d13d53e467892c91edf86c69446a5940ce242a0bdb726ca17945544e3979aa8591a090c27534cffe9a5aae4bf50b39ce66468bdfc7601e2ef9d47ea32f63e3a91c22fa4960332565401c977c98674d675c281ad91842aade990dd7325360aba6a77b60ccdf76beea285c15a22233906ce039b524d730af213b3c5139355e8572ec045d2392c05f0a1c82440b3df97b515d912de448646f8bc63a710dd7c05481eae14e120a25ce9490f7000d134fa9cf3e5904c7203137e5c6b7999d073950166a931c06454014f521c3c5280395cdf99786d0877f51f800b92774853c0f30059c8423771ea84e06bfba1610257ca8c989aa7ecd5e73def82b538d2986bacb6b0f458c067934cd29ad551ea422742c3e12924a7fbcf1165beaaa039cb5fbf55eb05c819ce95f3f4ec4b11d2c3655b856b53e106607f9e9c8cdefdcc88cece54b01c5f81b46146cd146b7826c7d9ad93fb9c186cb46bb7e258e5e83a905348171b0b0824e7aa121c53384844e542dbca334b7310436c0fe4d1365a2301e78f79c900881e124fe20130319dbce3d4c68b73290677495eedba5caa53f8ce246a400412f5f1f4be8b6f13f6e575c1571d5ff6c274a8064591eec224ca81146ad772d3603b10db186d22c6839597d89e6436fb28c4d8677010e7458a65651f928567d4dad0d28e1c4728f056dcdf30cea688d929f439986247e173c685394b07bf4b8d26e916874af3e0a6e650a39e2a3fe4a9816016d5aef84e3cd99ce6c1e93d7d56238f53ad78dc0f4c386a5c21d45b243902607a9cf730e4183518bd2d968ac6ca82b2fb67dfb2f46bde8478e5c10a5cd7167221e392f8478ffac9787408f35c64a3009e904186b1179b94d571a5e01d790dc684eec09828914f9c05facc660b003d1b98b9301d722f6198d040377f0b3e8000081676ccc0add35383e5b2adf3d3f8a461109e678e97697666712eac072090eb94511f3f97e905186cd32eaae0e6f196db5c1123437884480b2ccdbb95694bdc0feaf1f2391f020c9457b02d1939c83b386759c47316d2cdbe8c765f2e25df05995f8a3eae348d3c5609642417015d966aa991974ea66ff3176d08d00849d88808a21028a8b6ea396daa24d99330cd1d3f390db7ca46c0b810be0420ac4957a0e14a453225320501ca580345cb71b3b567b3e487cd764cf441fec6246e89b129004182b374ea4df53bdd15f4227dc88ae00c8216c184d75ea8aa4a5d604b0c4403ef80f42c7152bff9ba9a22d30e34487bf0033c21a29d6407154ad64e87e13cd4e2c0c6d0ee0d59421f678252840cc19723ab54803f1adb66eb9aa56465c079887d09101e62b23c4e513242dba9994dbeb241ca16fa8888617b6cac2258d507a4bbd8e428dbb8ec71d341c186257cc8c0a657392a239c9e2c15771d14e83fb8818757704d07e07887e106b37587cdf9dc269a007a31d5fab398ccd4a09549ab9718156a892aa82a26327347da40a065ea5838f4b208fcaa462f29f718d981253f769071ded21064702f49697ecee70965466d56f6c4e961b2317f379f657ef561a6d86b05535726e79cb89a6582ba228f29402913164dea1580003ea5c17e0bcf44a26d21dc189f1b5996351ebe9a9ba9eb08675d167208e1f7b8f3c655b05a67468ea6c7d8612ba0b7a7cdbcecf735568394b404c0266f480d1800e7935342272d346405ed2077d4b16529302cf50be2c4150a112bf770068914ed6ee2cb73b4ce68c80e087a31dcf01f53857ee3f04bd3d6385bb3fe4eb47aa682a9633f299ab45504fa30bba25a20181ea50365fee81889443650d29450a0329046b6fd2852c97fd42a8d6288a74733fb5daa8a5734901a1e8b0e40602a430d721efe6d486786ce9b85da93d80e8c0c12dcc87f72017ed466d1482344eebdb72444ee2da54c3205c10a8a091d0a1308cba1b64dc68eb0d714614bf5ab5e0e1d6dc5835a870e09fab0e97dfbee9de3d9e45bc132489ce3d1114e7ff1e30f3cbf6f228fe7348ed55471ba8b0716decca75e101c250c8f1d613e9bf0c40b7a03968518443cc142842221b0603efca00584161fb21bb020818926c876c08204ce5dd4576ee07e61b1015bdcaf971280d00de1d323134b99ea51a7582855d0475d3ae9bb4164e7c60e2ca3b1ffe43ff91f6a471dbd02d4e69c6d4347af002f4ee3a04038effb86fee6e104b1aa94ac815299e4df8c73f6c0b1c8e284216459c214ed20adcaa6ac3d928512848a7670430dbe60507c5005113ae860adb5292f70b6b8b1e8a2c5931f9e40825112580f561c618c0c0c1d72d87a10460cb8f8c1890d37f01026c9078717d20b0b24271886611804a6c082071f2051f485123e8412b0c801ab825a11461068b0c10b0f7c40a58b2864c000dbc1042fa31f54f811f358688ac30eb04ad3a04783fb25c50e1842083d5ea14165ce89655224ad70bfa4d062d24aa552c954a158cf81af570babfc8395d3b66d474cc22affe2b61991b84b8da37fdb5880f13b8f368ebe24b0fd7a15d5c0fd7a49c14f8750bd94e2c704710047588afb2545ab251936e84816fc19c5129cec203d4631466683fb15450fa2c892443d5284a31d18010544c85206abb528acb8200b31b4a0610c1aa81811a910e560ce395357c06c81c208cbb75108a9748840c83be79499084dc909657b944c53652050d1875e06fbe1959e11986300810ae718700f7c7989a832c5aec1fbf4522b36abf2e71bd65885e3c5994e5e3a70b53419a3d795a42cda0ca0d71357b61cbd9ef882014df11b29425ec54aabd5e1e4e81568e9538161bb10c202a0e8af4087e025136ec0b1044b107e94525a81a7115172f4e6681cf355c0b3051c6dfa663ee7cd1c3f5234d5e444707cf8cd99b95e20220752be1062092928c0747882f900290618866158bca1e70d3ef002888d0745d002872776488253ce2909a0c3133e5a580cf7cb892710c087971361d897135f946007a9c54910aa1356387105899a52428c51ba7faa7eea2e31eca252cf11720501fdc4169824a4a32d46af22a221215710d08fb7c024211d6d317a15110d09b982807e602b499118601812a407cf0ecef72a4be30d6d9f2224c51525080ad0931f9fd90a03e64b9217a42e475cb66831caf2ba526485a8cad0142129ae284150809ec81fe9235b6130982f495e90ba1ce1c8c5bf2d5a8cb2bcae442b18be4cac2273641cf20f36a055a9e7c0c7b056a95494cec7eed481339940972b68d950f6370ea861d80fb3a8ab2ab61fd288f147e3801826bf3bdfc02adbdd0dbd3b5651a9e774e350bbe9dd6982edf78657409fc6d1bf42c2c6306cfc9294f26569963efb5aea52e931997d2c956ec81c52874ddff44b69a5c280b319638c47266e2fc678638c5147e3d0c2c647c2aeaad8f89d819f4ffdc78170673103b2f1e96331fbf9547ed693bd7c2a367751cc30ece6ecbd2ccbaebb32170aea9bbe34c677ea39100ad1385429f78edd1da571c0d9ded0c334170cdfc6dbf48dce417333f5af1b626424f12fb61552cae8d0217c6c4305dc10e78831c6d88ee35f3f0d42ad34424f7637f41a6608a3c396d808c4f16626d82f74e853d8c611e3f412c4be144208a97cf83146195d89fa594a8fa671cc19259533f60d7a851b8d634e2d621fb10845504d1822125eb1ddf276479af50eeef9a7083d6b658c56222cd5186354417728638cd325ab0eee170f5e30f5dec6bdf8589884e49f8dbb5f292a65b15008ba7ed502d4bef49ce784fb2ecbb86f4c9534617141b8cb7de699ae474d5ce9b94c268bf52007f227d8ff86e14ffcc77390fc5d2e21fef96b5e10ff6acd1238ceb3d38b306dfd80f1561218a4a4a38c3e7dfaddf46b2398443d4aa9c948c42068235cbab9c91026d246f46256544158f82ecd4d3ba8f33bde28638c32668865308319cc604e49cc331235ffba6191c7de4e187032e45f3b0cfe3915eb5ce94df533cee6f937717f1edba1e4246e9e38afbc99480a639e39a9fd6da3b54b395b7677cbee38e38c33ce39e7752fa2e085ad31d41b05a41217f221d991e5ae78033f85969d0f1f7ef11c5984096a7002777f1912496929ff2004821204e4aef81e04545d5268201272f9073f432ad65d10097ae9241c5da631e2cca49765524a8a69190644624d85b9749ffdc5a750662944c77cc8d938e263790458becbc611fd739904991cdc638cd15d42f7ee6e82c148c559d3e590869e02f78b065ce0eaf0ab77a418ca17f2414b4a8fbdbc13a09fbd054a8ffd122a42b02ffd92ece92fb9b01597d09ba16083758c29814ce800c40f2bfb2aa9bfcf9f71f698b3747bbc9218af0a089b2110108540f1e6aa58603304a28fbdd35f82fd5c42450866fa55fc25f4aee21582bde9b1a75fba4bec53ec210a4eb858a5a75fa262601727de380b99a784092c3a4a428845456585092c42c207894505aa9480503c080bbd4277bd826b627705e75dc1f916c03e7b0bcca793e54a661006146fbc95c4bfde02817a2b02b56861f8ace935cb814c4bb877b7673de7a440d8ecda60118a63d21b8391a3fe61de0e367f37b20da689db8e4c857d8ac2ed471388336c8ada36d555f686e794aae9bbe3f4ad058861961dd91eb44ac76d9af7e8b0e96df60d91fdd1f7cc945d29318d086dc77322c690a27cdf7c4079537e63a39277020725580d840314d6b6040e9660d5504103ab9e208955e32809563f4ed4a971031a585dc50962c0eae7893a359a3062d598020c568d2935b0fa87449d1a47f8b0da280545ac0a861358fd45a2ce0c0e5ab07a0c560f01074fb03a0b568b210410c2b0fae11735b01a0e5181c2ea87425147c715376cc16aec0a56aad503ab1f16451d069c008a17ac6e2e4760351080c06ae824071eb4b01640469820b0ba817004563f4c8a3adb1090b8c2ea8760a2ce0c2bac2d095b7c60f5fb4fd4b1f185d50cd0a18b1eb0bab9d081d5efaea8d34f6869620b963ff1a18b18b01a5e8185cb15ac7e278a3afdc4d018276075562507ac7e7f459d28041a7c7e585dc760758d82d5375a2005ab511083d5ef495127e78814b0fa1d4cd4a1b185d5363becf06383430c58fd3128ea94acb04e5a94f8c26acd05464060592b395ec1dd6ad5d062e598c5b672d412434551abd58223b072ec82f918b5688062c4cad1cb5cc2a8d5e202042bc72f9468a8d54a4207568e61729c50d46ab5845859fa401a865a3e43706165f964fad06ab55e50042b4b2825318c5a2d22a22cc1a8d5ba8207569652a018b2610b2c7260653905cb51ab25e50656965588306ab55e70042b4b2b520747add6105958595e916206ac2cb35001d46a4d798295a5162cad566b4a1656965c8060d46a4d9982956517295cadd6942fac2cbd4cd751ab65840f2bcb2f9507ad56ab0824585986d14ad06ab582d0032b4f9f1db45aad2d64b0f27c5284abd532e28795271428865a2d2a62b0f28c82671449ad16951e58794ac15d049ce794c8c59b121105064ab8683084137e68a1e086ee07a024474a10a3862dc2d0a2450e48c440e1435f09e20b134e98f8011440604f4a0003ac8a124cb181e27e31618299c54998cc09866118564312a4644e5c590d4a603e3870e2653a6902f37182c5886c87ac86221f319ca8c009941a56e083854fd0124e843003ccc70a4c8ae924cb747202d30ab61ab0d87c98e0848b520d5e309f273024d42838273d30dd60d2a20626947cbe2825ed207d8c40392182fa184d2758780d48d2490d309f32680d5a322735d48004adc2e704196a092c2b4a90c29ad86ea0e2414703550fba329ea072123334c1dd2ae6e177f7f5bbdb737aedebed89790ecb486c68da69d358d0fe64e266688257a8efb1df3dc779317772777eccf7982e1288ebd7fa266f65bfbe49061f9a0fee63bed63aa36e35dee4c1168ed15a78f3f29199af41e36df733347e06024d5adb68fccccc8c47bf763450dbf7a3be7e4dea6b6f3dba4a205dcc57cd43691705adcd3694d679784b79a798467d95b79bdc6bfdf579220daf717b9ef5aa97a1501268bcd2ea6bf5b91b33540e50a8dafde9f439a6c66bd7decb4570e5acbdb948cd6ff6e65ae365319f9d22865dd73dcab35ce5b6cf9eabd63eea3b7b5731319d8dd96e8fc6797b24367077572894e5663c279379ab1e8d536f3fe6372f23b181eb5d71cfd53ae3ae667cf6b5a3a152a95434ee6a46ad3277d575d90ad5dce937191999e7644ed9766580486c60ae7bae7b8ecb2bd423b1814fbfea90d8c0a9bbdab2df32eeb355f7dc67dff6ae50b747e355ea7b621ef5a998477d4ccca3503116d5bfdd1e8d4fd77e67bf7b6bbfb3dde59ef372132b57546ee308198d1da9ccb22c9b32fbeac9995189bdc96b20d208c43e661018f612a9c2cd07166594df110806047b1963bcd9b1863bf8c1f027180c1f8b3ee6bb40b38b4120d35b09e978e3f21e8138462410c326d4593ff328eef0b67137f372924c71a642666e8d6c7bcdeb18176e5ea279a8d7bc7e2de6ceb83083aa713bde7030cb31cca83f83665e04647ec6631e90eeb7afd0ab368a4a6fa3c8bfd69b9b208998fee6ad7cdc9e52c43d343e2b7de5beeb7adbb29ff1ead665decc0cdd4cf79907b9ef381fdd73d9e28c898ffa5a7df8dbc60477db37ae5f2fc775377fb769db6b703b3dfc158faec77ee32311c2c6a1799ce7318ec5dc9c3df7ad3dfc2c4391163e126b66840946b1e038f38c408ccab8ef47fd8c77243e5cf9b83ddc779fdd1eedb7245d9793601822f5aebb5948d73d4fecbc0c5bd87ef5320cb82d77336cd9af50832dcbd16c07ea65f5f2aba2f5ecb79b5da8f1db6f31a713fccdd3eefc2ce2d376a7e6f59c2e1288b35fa14ede0af5d99fbe5e197c74cf3deafad85efb2cbb4822ceec0fce6e0ffc7ed8c21a677b647eab3f63860b333366cc98e1d1cf4af565bed2e7b22cbb19e2da5e861477ab9ff92cfb1a5e6f40b6ea75f576bf09e12ae4bcecfb67b84a5da8b1c9788d6be94b5fc38b1b10fadc975ca871b99bc84526b2373d8f73dcc3af35e67b8e445ce37af5b2ee6b78b546eddf8a6054cc968f64db8eed396f957d97f1787685d4d8be765ec6715cee3ea37efbeaad3ad40af5fd32df7d7dce5b718ffa98cfbcd5e9ed675f65bcd4b6552ff5325e4dd5cc6ddb738ff2561beafb538f7a6b7ffb1ae3699ff2627ee5cda43c2d06e5c9004f57c8ea57ddd7f899cfdd1552e36736eb75bff2666a78339f79dd5b2feb4d488deee6263337dbb4ed884724e2ec22716c4309ac09eb0fdf48f64fb1d62a4ae501bff4b5e4d54e48295b21f6d27853aa9f6d6ff2b8bb7d7ccdeb29bde94f57156f4cda735ef6dc9b5cb0b7238d3726edf6946af7b5a4752e9cdebee6753727c10f79e2f6d27ee769dfdd476d092e7d73df516e2e9c6ec79b8ebb99892dddedf4ad952a8f92e7037ec97a27ea82fd936f3c4a3f6b12acbdbda5ef2fd19c04e7fe23910eb14bb0212a0ff826887de6955694529e689ade95b4a7268ee39ef332cd4df0f63909debc9ef9be65bf651a7dd373f44d4f4ddac5b6db33bf6eb908cef47b66dbb671a026d99163c0d8e72218bb997e73df83bde9b96bfa8e01675fbf1d7bb941a0095e75a6ef4ccf5d53fbb7dd9b99e04bc4e9a58e3d5d7afb21967510f7b6a374794cf8990ff8f3963c8857a65ff9b8d8c49e2d51b7a75f79401ff4e13b2e79f9c31a863f4d25ada4693717f91efaf135edb5dbfed5eea10fff74fa7af2b20a9fe8d7a89dbc06d2bdfd9277bab9082e55d5e312f7f2e310acbdfdb8eda8df7d8c77ba9989bd72f3e19876d7f431a51ae371f5e2ca83be0ff854f3aac903a27dcd9007fd69fa7ab3e91e91a5928f1e4cf001302feeeedeb50df6ad7910620d279401e2f852cacffec88633d99836bf62a68961d83542b33b03f63360d894917a0c43a876dd104a2803c4fef1b13fb2612cd29f5e9c7f8d6417890772130c679833cc4772c2f3ce00ffc826af1107c3c33ef6d682ec913e9f05bf0d53c8472046e23fbd8629e0e8829a24a043f718a59c13c3324a4bfe512ad15dde4ca394d11d4aad524a391ffacc4d5a18c2f70f2787841f3ddec0813b50d4b699c1ca145c7ab9054a165cfad209c3557c8927865b32188768290e15c5a1a11987e250f63568b08da314fffca97fdefed92d72cb9d9fe116eca85780d86112eec691a11658bef4f6a48704627a73fba77dd98d4798946a8306b604553318d2b6adca703fd5fc53f506e5b7bb175092a8e76834bd83fb5337b04fffc3c154985723088bfdbc99f6883a4d55f87495746f6f4b2e36f76b02bd820c1070fc20bd02f522bea3e0c5f2f817ff42b1f187449d8e415e9166c8e67e0d7905c1b4927743a757a08ec10d48f6f2d634c1e62638cb6872608564d96d9b1e4651d5793bb17ae9e75f5954c1a67f6c2ad53719cd943e9f4aa58770276e3b7947339db49d9d5ec171e95f88c173e2973e01ad43e34de96d13a552a9542addf0aff4d0abf793a7496347ed665aa2115e161e9bea4308e3f6a3093679d2bf4ea202de6067bc7400d4ada3d34d5c83de0f65613f3d21de6261df5e66821b89c4f3573ea00b4f29c4b2b039e52502c3bc8e37f1a64b8c3addf166d88addb171b401ac804f98e8934e13db13bf33ee221d8630c49006dcb73d24b2850ef767f92b1f262caf912b300ece5d06ceb1210bacabc0426323b00cdc6d44766f2c50ed8533cd6b074838ab6854dac9f60a2aff3aa8de48c10e58c01becc45db443165f149c35ec7d053eb1f0e59d510bc0c58d1e0a40040bbfb5da36392bc231c26d488e4038027928f0f78490d2c60f16c3cfaa9fa803595a8df30a8e39d8c679fecca9ddf9439b5a193f3e1d22a9a492528993c373b2a7b452fa254c353b7fe68fcd8ab227f03d258000d0c5ca79058531ec7c41f8f13dc720a04f02277e4ae5396129009ed8f70fca1f2fc31ffb36b039fec420cff1279e13dfdf15853c67e58de1ff60f8ffe339fefd8a51081a57d81efdaf69349988afa5accd9b70b08fbb0847881a7c7181e784dde9d18f43a343759fc63fcf47bc4a1370de91b2fd5f08ebb9ecbcaf9ac2e66f35bf1ff03b6e780eec07ecdf63becb2fdaf9f58d63c9e2c252088c8b0984e5f4a1819d3f77b600f0c4629fa7f7640a24dc2f2c493970bfb02881bd28ea74f424cff9e239d8536aad4af59f7dc99330c6f82c60eed1344deef14449e341b192c6e873ba43b1be333d27fbb983f30c4df24ee6e520b2133774d8c05624e227bd817d9c77200b6cb6d8230f2a9511a4607f9a96299bf96d039bba0afe190faa3f4d0a435e09c8a6704bf1830d70c0f862849f1d0ca8c7173b3a3a0b3684fd69686cbadbc6a87245a11f8233b0425f7441cc2dc1660c0828eac496c4801a03c2802612306582c568c0fed15f13a971fc7847d88c8393031ba24540571c1afa420bf64f55e9621176e872211f7215fcbd1b6c76210b7d0862e853a80c611f5a41c83f77a1298dc387a20f6c8e4371c8e3908d2245684a169b5bb168b6aa0f95f9363c0bfbb611b28408c75fc5c07a44fc359fc8c23b44deffc1c691cd2aa3e789371907e70b2bd83f878a46472aeae0dcffc1661cac81f7c17616341ae8151a0f11e239f05a213635a968a9c0b8b0bf121ff9363c8b3e0621ccd8c4300cc3ee4ce22a804912755a9c183653087443f5b1e16790abe02fbde97215fceb0d6c76232ec95aac1575e43bd21637b253c8735e5f68f11c0f8a32c5b93061734cc2fed5b760376a1cfe52e6f862e1169cdd08c98de4c55aa61cd86c53d95114b66dca46f3a1225f95020c6cee97e5c2661ba5b0d94ee1fd1042082184dd72f6ecd9b33b1e51c28521e7c3a3961747d83f55e408f491574629a38c52ce38b18a6118065d49112fc93dd247b08beb7ceba936212da24256db7047b0340cce30c8837e9e508d0642894285f5f716ce306805bfbf0a2f84b03fcf8e0bfbebf0e207b3dc85c0b9afc8ff6c3a1fd82d7ed34714c926a97a00e65b7ed33c55fcf80d069c80f19c24f8e0238e6790b874c2400d40b5f2c40a352712c28d31d0b2e2397d8597271870f2c573561870f225eaa0be7bd95ff2eb8eac8b78a20e8e6f363552d8fc7f6505406c38be6f3e6a188c79c4255fb00e143086cd4712c2fd42e27271bf9088f0c72094e765e9674b64e1e72560b07fe45183b1db8d63debc2409e725449888bfe21005fc3a0d540034c2a25aa9c23305431d104ac110429d4fde07f0d44461db891b965094420bb500c718a54c410a9e7640eba85a075509f46bb0bc9988bf70bcb4461296c61b252c8da276d47126e8c0e5d55852f033c452d2647c91c31616930d08b6a0f809623181b003b56d4cc0840c8e581546814511ab8d78a084114b053c60e16209c065049722569d5ddc40882156c54cb04412ab662ae04110ab9b8002c688d542b0c16788d55f04e1082256ad5af4025084a41b92584a04d022a208442c260bf029c20fabb1f0c20623d6f7050a767f1bcfa1c1fed45ff3af2d27e5552202564361042d7e586de3d1871a1920060f492c252a5811c6118b09aab5c5ae8448251e049630462c29fd8bb025bc42861118575094167c253d7a83a10130a4b67130411404ce1667095f46081d32a1b60d2d515a8247248636efe0ece8f01f9e03b1a79023855ec133d3f7a75cb31ff4e4767568b7fdc6e10374f88d27a015d0381e4feb04ec65af5083fbb71da58f1efd92e7a3e1675007bc3ba723acebf09b1d1a50b025d4b6f96edadd6472ffee99be898ffb73e933897dfaac61449f4253e9a6fc939a855f189ba9dfc08fa988fdaa1a47b4c266faddf0bfb468e2a07ae907ce340e7fb8b1007deb4ef9d7051715f42256b547d7a9ef697ed3ad00c8650c9c1d6741f8b0858ba3219c298e9716a17295ab5ce54fe3153aec57173dc0ac975b64caed11f6039d8a4aa148e44352e89314abe1353affef31f9ec43ef51a9f9f69c9a87b0a606974a5a95534c2e29bd42728a7f9e9442fec1cb43c2d2974a39353fdf8f681d2ba7749caa79a5506e304afc9b9fc4bf195f06c1d02b34d6817d53f3b34b566a3ea7bce6b367993e64e3fd08147eba3fbd816d024f14bae0599ace05db76d8a8f16abeb71d36fed53cf4e4973c16e04b1ef03188829721aeb936fed53c8fd7ac6116b96e1c352fb51a221f721b5658d3ffc0ade636163ff38691c34ed6c6dbf879e239415127c99c70e298b88753382821b5ee39350fe1eae1b683e6874005603fdf850c748ee9e767ea7db618d5f0516ff2627ff0318f0686ff1d84355e8e2f74d212c6e3317daac18d855a9a621a1212f2a12a442f37f22d6ec5b3b8162f9a9e93b10fe2dfec998209492121cc83b8cbc67536de8643b75274c58750f81a358ff6d8781920aef9209e93e10fcfa1475a2ad745f1cf6a5306c9201b33b0335ef5331f5f0a094921cfc15ec7fbf9b268d69f2fab78ce7605cfcf320b9e443d1febac149242728a1c62c1b124a2c9a7f1d987e8cb8b04d2fbc33f1b354f1040838d5d3cc7c6cf8f4851e73f3718e9aa79d37ccf931b0268b05906f504f16ffe11887ff4c8284151c75d320837f42af3a14729b1e9e62518fb98e4dffcf7e217bf99d9e7f6dcf322cfadf837df5d13859b29ae71570d2f435ce3b18d4786b11bc348b1f0572ff333de87b46cf19c159793ed52cfe334980fcda743f2ae543ff3b9c1dc0008d702f73e047f760b1c1288e5cdda97bed478c653d1f088f89fe1c9789e0c30c6cb5ae643783eca93211b1267d723eafc9ddca659f8df377de502c8416d1b79b2fe4de95311b083cd90c8debe90c8b30f3de939ab7879c4b76f79649915a845eb582f30169180319965df798d136fb0a7168b5f33af6b11cdf4321eec9bf8292fc683561a75ca2ea47a94274f39822296619909c7bfcce413630cb55608f405679969ca2eaf2c528be422bb74af2b451c07adbd48208fcc73b02dba544aab86f2a015ffb0c77995ca62367c605ffbcc7f8eecc582fc27eac43cf60ec573a2788efcec841d798e7dd4634018f3208c611fe36547d4b60c83b3f7417a82b39f5070f676fbd104a3ecad5de7446cf0ca08c4a74722717c231d8ea887017591407cba19da7bc3bf18fcc39e03fe61db860027367b286ce5348c728d655f3b9e7c3c27d3bab6297af705ffb0a71a3d71dfafa32f3843220989b85849ca6d74c57330ac888655cc84758fa5fc9b10a848d4517552c47060c0bef4190da1b044cbaaba5ff58924f19cf8f57dd4afb5d65ac3e0fa4d1fd6d7d115dc5e70bfb2d8822bdce6170c3f460d7a595ec1b008e248875863187d2638bb32a9be464fa9571657f0fc1dcf51f1788e7ffd1e5555359aaa5458efe15fedbe81945ef93d6afdb6a9b707b6b0252e300327607051c51554848912813862055bbad8628b11c48041104198c11170f02148881daaf051c1ccc28814203184215cc9d283186300318f3065052a3f4f4822cc183208c2420339ace04b0f2508eaa10a96021370f92268e04cb1e822bfd287d269d432460361c4a8ff0c4db089b5d05af0bcab161cf3386e8241cf72e79c73ce3c21c5f36146fbaee4cb246cc00afb3766d89c73ce09348e7e991c6c8c3d5b7ad1b7146d79d11d5e5b68c1bd4516dc2d01937c93d79b04a8cc62fc58a2b494655966929b04fc8bab52896634e062bcaa69b6b5afdad7faa79b93606fab45f0f6f6330ff59d1773aa7faa9f699bb6755e6e125304f7ccd3e87e86c6cf68cf3df184c6411a35be964a5f4fbf51ede6ed2bd7695fbdda6d0fe16b5f3da871a7aafd8909e65ef37c701f03bfbb99d6705caddc37d6b6ef3cce9ba1098ef99819dcc77c6f5a0ff68dbf4298bfc678272fd73fc9a03a542a95abed6eae1f83656ca5b1b57e198665d8845de68427e88261199c451815a51a9e0076d12ab53f60aa2382e1cdf32b0438ede38cf7d4335ed75d8d4b693210aa64361a3366d7fd065f06de4c24055393d31ece7ad2b4874422707af8dc77deeaf4f5bb6fd7b4d5e9725fbf3e470477b0a630f7f0747bb6ef52b8bb4452d86e3eb6db2a1a57ce9832a914d4b40e7615c22e057f42a85d6ec26e42082184282f13c1288a632c8e89f91a73baa70ff51c17f3a76fcad9989f93a6f0bc99480ae56522a98efb69396ea6344efbe9715caac3f3bbc97199480a6b29f88021449a0834006207a926020d80a08208462919341428385ec16928500e10a1409161a0a86cae58e9bfa3809e9ed669321675b279a1fbc8c5a44cf6a22e774f73ce79bbdae1a8a2a15aea53aa97b1aa4f69dfbda6c99c7ed6ee352436b47a35cd7efdd879b9ebba17509db742fde93b4a354d5ba16efdd39fbe3ef7272fbff0029e9afdaebb446cb8db23a379446cba4bc4065b1999542ad5a53aad6adfa53aadaafe55afa2e9ba17e87ff5476783e21ef539aa5da5ba99ce90b9a993bdf99ffb19af370834e11ede6c83b99b89d8745df7bd751de7e5176c1e431a0c6930f7f6db7354d86bff50a9d4ccc39f79eba1663e26e6a2ec678feaba1770ccc7c8e7bc8c7acdcb2f74977be8698ff24e27cfb166d3a3bd041a732fd8f080386b3df51df7d6a3b14d2662038548f53b48e0e265b383042ea4a0368bcddff78551a1e70fdba1cc44d052e7b7077fe51f83707c611eaf3202e1f6a3f8ddb862db8e9ed3ddfdca7ee9d0fd219c4d6210c4ad1939b09926486e2d10a778800a683ca1d7dc10ac651b9a54d6f0b77f98b3f67f088637737f02623f88d6533ffec7496562a6bdfd55894ff1e5576dc710863fc385e1cb7cad70d6c80186cd45f0f63602333ef5aab740e055f5d4b75f3dece373430061ee51588668e1f9337cc4fcecde7e05f290831ce42007b99f0fb98ff172fd917aed2377737cb1f36bb511e87ebef6a7b7945abbeaae8fd3d56e1e82e773f5b31e10a7b2fd989be38be5b8db937aedf6a09e7b1eafdef4b2bd443e9c7a5444cd78f8dccd7d258d7b2462998733785cc6cb4cf0b4d23f0e73af453c2390ebdbe7aef4efe625986adbb6c3f44022de2260aff48ffb7ab59be11f893be66b0fad7fde839dd087faa64fc2404c587beb0dc1311fb71da6b6c11c4d4e61eedb5f7a2af5b6dbe9db3d4f4c7d90edff69a0642490d46dffe03545c0f4d56463eccb08944c9ec5e202171ce1fa36428ff3f210cc7d47934c8cea3feaccd030c9a86e7eccd1b83350375b999b8ab9f99f279e3c2410c73ce765d467eff6ebd7efcd879579ce47cabe7c6e0886da43eff4315ef78e4f5e465d5a737dedf49c37c487c675cf133b2f33c1f56a57fe87b7979b29fee4264343b0840800220b128edf48208e3e64acb0f99f86d34082111536f70bfe8e0e1e8feeaf84a3ce6df1bbd83d957aebedae92be4d58a62bb59b8fc4a01804c4bb441e2250e4e19b51a21882168c833b8a214809426afb90d5316f9f55a800ee4d5b3ee2700b6c6bc184bbede7c642f7f4e186e439d86f2fb71d5b103679361fe79c318495dc4ff9dbb7a4bf3ddc1e6e011500f1b67ddc766c4d7694baae7b16fcbbcdabcf794db0c435cb3cacfa54a27e663fc76664b1b4c78f209e331fbe0e16f59aeb344b7a2688e49f8ccf04671e12962859706309a20b5e006e2c41bc009f1e7af9886f9edc76188165e0550bf1edb310dfcaac04fb5b8ffbdebc0976ef8863eec22d600a5a15ac3d4fdcfc88ac127fde8629cc9743fe491f700b9882fcb943b5b63d932acdbe09ae52cb3e635a9db89a30186b0eff6edc10e0868773d338fc336f003005ff8a6d3c20748f374e4f320cfb52c55a0bf1b3cf10630fe3bde11f8df76b1c3c7c45b388aa695aadb53ed5a6cc5efb1a3dcd93d9c758327daeaf51fab546198e64b8745be832307d235d06ceb2c46e8271082c3070cdd77cb09f0f1f43081bba3c1b9e05855e76c8c6095df1ca30f11109b1d80c5d2f03c42e1d58f80cf879d67cfff7b781d3e61bba54345909be242fd8ff864dd9e4584db12f9b2d24427e5e6343a737a85654060e4e5216ec9f2388902e3aea9bde2524e47209b9a440178ecbe58a49415651d8243c858868ad120c12f2cf8d8ca04ba607d005849424c465250616167e4c7a127bbc7afc78c0d10fd5878af419b5ee1e416c606310608f30ba60ff1f3f640f56f3cfa6781e8025684a1ff578ede8c83255a881cd36017de34f593b8dc38da4c80115acc900230e6ca62d486db05118f44d5f4d1e863ffefdfc98624d0aac114fbc811f05fb13ec57608741a28e6d1fb48ee91d628161d185ad7ec1332c77c9a8a5fa968c116cc1a4cc08368ca01bd9435711ccb3e159548a24b1f1163902049b3ba9086c9e8f3aa55799262cd18bdd8e37f9db51a4099a8b0a55183e54a3a13bfe2561338d5b6cf7079f8c2a5d38fb910c024202fdd01f22ea47447e9421911ff99117ecef5ddc0b9574c88f2891fcf123223ff2a3272da5ab8bfa72c7e3c934efb8c7d71be1fc38f39ab08ab2ec2ae97893a3500c6ac28a40334dd878e54b62c1ae2c8a3744576cde21a23f06e5080494654747961865099b77e27b32409c77faeb5ec2365204f2276ca6389a3472c7be4082a14a10215d62908b0ceb4f630516c628d115831ac74c1376826142e3f0998414dd592e5bf18a85374b2224dc464141f3c8bf56bffc7bfcc05a3d5430ae42eb09d6679eb0f0711cccab83b0bfc4c971251e6d61f3e73f5bd5874abf0dcf2abde69f7b3c5e0f256ca6168a221838c00323f6b1deff82fc117cb0bf0dcf03b4dc20826a7ea38ab7fba879e2d314451f30700073ec042d4ac148c2fe5acb070a1582dda14f6c862df8b303ed1476b6eabbe969bd41a2e9be406fbb7fd65f94f0cc9dbb53bbb0721a1164084c0f2ed8ff07929730ad658b43a65716a424239b61bcc2e608b47df7a7ec43d14571910a84bd077424ec434343eefae2d50822253dc6c1acb77e1cc883a0f52050bc29e2b697ab8bcd146b911221e43f2fa129561c0a7c1b9e657a77a1a12c363798f8039bfd487beeb3bfb68fe1155f18c78289ade8f0667be1960c07c38144b6e1e6258937fe9a67823ff1860b9b29a65a12f58c09306058e9a29ce8f57062b7c4de1277b06048699982fec91dd8577545f6937039d0904b4a15a3e8452b6c7697bba45004fac1591205cd90416404042a8c3081135b8001f4431345c020c51006fb13f9b2a323862265d84cb514582f5d5aa6a0f4b420db02a42d43bcbcb0ff0b54a3e932c43f57c11f853036d3a8c5669ad48497216fad228c9708ec9b35b10d88ee8e2a15fc197498e7f3b90afe363c8b4a868fe3dfd71e4e7ffefae2dcc0f95c857e1eafd16b6bbd0461ff94a4f24694c1c1e6ef7e9fc5e68ff6f7e9b8b9af7cd8480b0fca947ec1fe5a9047f129290bbff2d830f66a1cfe364a80b9a20e46146fdca3640f92ed4921a2a82387a4101196539e0c2baf0c722914848786a43c9248f4775446b082fd69bea688a894ad7585452d11d10c0000003314000020140c094422c168241a536565fb14000c93a248745418894990c32084900100100208000400000088c8dccc00bcbdf5763e6c71bb01c0cee3087158de58af47fafc668d52d7008b50c11dcace6ef541765362c9b20775709e16b79d1e2c8ca5fd113d585aec72495f95a9b562d016689e6e6d922c58f60627a7e5170064bc88a92bc017595dde71fa0805c6b5fb5338e888b1ce8242a363b4dae59a0cfe25656837fa81860b6dd383116b0544ae81af24ce2a5bafdc4a8308f5741483cc46319365a504c6be0e5bc65d12d3daf2fef471af7f1d91f59de9ef45639e56ab34d38417e9494cb508995c9744f9449a6a3909af86e09a94982a0690e6e8c28aecbde3bebd5e8a9f54cf5e83dd01c61ec3403c0ffb3dd6b1f750f5ee416ece557ddd8b94243f0ce583c9acad7be96f3249ed9cde7e4707c222cec723d0bb89738586c6904206add0bd82ba833c64027d57388382da5eaa8340c150adb0b3b07b53699df40684ea7d299eabbe986c4f7d87a0c0d05cfd008b8dfd5d85f6025c6f6ceec522e21b48633100332bdbe1040cc59c693db8a589a7dde0c756affd0bc1a6fe293560497f05b6f2870427302c2298b9c9dff58a90c1563f85691b21146af1fb6b6c2f7efd8b5ffc321fdbef62171035ff6cc51c694ac1af619b8a96c0c9d84000fe920376504d4acee79413ba024f8d9cb34fbcf2da288c33154ff8e28d18176376dd4c91a0c3e4969c85ceb9476e2574164263d7395f3a9fa3a7686eb5a79cada1dfdb82fffc84143d670e47677262eaff857ff6532badcede4f8f3ebc0f8aff899d6f4b46d11c8a819d0b96ed2a7898ad8b98a6f6965b06fe988f04efaa17a1520d020ef7cd69d983ee35621a4ae0d0f997742e366d3ec9c6179193ee51c54aff24ad0479472076105a47773dce41488748fb927472b8eeb82ceb988796236ab76dd152ce500cf95dd060a41556d6e03ff9323729cca2f563c671790b4c4335e868ba18d520c042d23214509e5718dd5e9a6a6b566be52c1852624b4e9a721df7cf094106ed7919aff9f1f071f06bc886188a6388081bdeec4f752ebeb2879cff93dc511e1fc92ad38a3511a483f971918cde9ce732846bc7cfb927631b868a5571f61539dbb0e13f71470ae4ccf9b0769b06edf3311b561bd0f337c9982c39a77680f6fd7483b10eaf55a387369f519d6ea5769b0fa9a5b6b0216d737e565404e89a7a8625a9343e25f753b87941f2bf64f38148b0bc512cafd94440e136ff4c6901e8a8ee9e1fa149503e02fb40d9b1476046497a64fea740bf5c9bffb48fa9756d1e32328ccf4af0be8cae66c24fe16e3ec865fc28df801d155298633e82815a526e2f779ffa687a0ee25a9f2f15c9ac3c0e7a65266f3467a6292e638f1ed0745895ba109b088a6074749acffad589c30ac95b854add954e3dd31e384c874ce359f09ea159a19feac963e71c660c392a1873510eea586f5504ccbb0b53fa2c2ab43d3cfd85a7aaeeab5785cf7715c6534b1c23af9b221e53910aa53b475a07810bb32476b1e6001b5817cb5c9b17c719a6736118962623323c10e32a486a24fb9cffd444c3397fca3a52cd70f0bea66e38cd39b1fbb6e22acfabcc9ba169e7e45f97be0e9711d0337f89ef6340cd72ab1e21e29e81a0feeda5064feb16dfb9bd6b128c405ac79fa01c6955df03f79cfea36f52f7079ee5b4d1ec9d50f149f1c0f6dd86658f832d60e088d234e3860a66423b3b9be9cb60d3d3a2451371c8bcbd32bde333731b619d906a8d341f009362dab908ec4980607b93d3a94001305c7d0bd5822e959bbcd2868fb30ff3b0dc88b74dc559a8f328fec37a1f39bbd06da88a34b31fb030b1e5cffe26e22af74b5d1e45a689c9811d4c58aec15de935c61d40463f52c8bf9b1330fd0eac0cdcb096a69c0766b745a810a546f79e91cb3ec8fcdebc8985e2f7bd05cfd34ffa1b62dd312df30fdaebddefe3d495d032dfc4496398498b61ae80bc23e89e1fcb58aee1052ad20d5aa1cbd0993abb70fb21fbb735605ade496bc58b80d1963aa41803105e44a49e782b48602fb8a26697874799b3c4a71bcd2988d8187643241c341a6166f07156f5b41303f713d488e69abe29b2d413bf940df1e7f56c14a7959dacb56df8c9b85dfd5e4b14cee8cf8a565d3f9fa11d3bb9457a3a782f12227d3ecec7b87b706c9135bad7415c47a48bfc682326992b439349301585ac1023033cbaf14586a7dffa9d1d17bdb975cd7517aa8c37410cde23c208f28783a0972997a22f00fea8051609f5ad58e1d8b47ad8ddfe1c64ecd3a6a119e75e2a1004b24f15a613991bf90411eb3461a34ee642dd4a4b9ed255e4ba05c64ae9c8368c2ab8c2c39bc6c44bca8aade4cce4235c2391de27b35db5d113c27251a382e4336fe64804d8ad5b13f161233d29a034d2257cea0ea8664e0d4b7662530a2d4a582a7de90239e8486faf5dd8453ef9911ba36eaad8631a5131319769c4b0473e57bb92252b6490c1cac908bb23adc8a9e830e91abf3a24576d7ebe5d8213d8ff942ac1c5c074a675942782b2ddaa1cd08605b3952af210c2069706d119893499d79bcdf9018dc59d72754d0e398e86f3015909e70d13423543ef2782ac923817c9059df621323f33ea39e7e5b3c4c1f614dceb990ca6ba30722ad6a7e183dff41f72ada08cb49426c0531f8c37a211b13d1b5ce424212267ab333a4fd7c9819b40bcda9d15516624df0a46ad4ce98e0e5eb5c8dc636fd0d2d58ca86c3ed73cae0d307d72b8d703922c848148d41040c6e4e7a1f48192f34f5ae45f79215c30df061682a07c3fe089f2b3b6754cf4635c5d0cc6c70501098278e8de5d29c8f0c6381827bdca12bbf22d84217eff3f7404ba81ae8afdcb818c64d9bcdcadd521281e284adbf2cfd9904696189b8e11a370e9c2fcdec7b08287d4a26ebb8f0b349121f2b9165ab0602a2a0ddd4bea3ba918c5381042bbba75d0cab849d66b1851677f2d80ec9ddb9d3ea4980451ca5baf77e003fb8a0177d2a4505e439f5cafd53ff5dd2073e1150bbf84366d79bde689588fee9ac52ce67a67446aadf09bc379cb106d1ad60b902956fbcdf3537e9309dab6be41b135e86034955f0a108bc753fdb584fbe2dc2fda70029cada1a8dea300b986ab35f26a03ca6853829c84cc864ca023c3d26752c1a2db0ad89a52ad9e3932a3f0a7bcfb5faf5c7b9a7772b708dbc86b2f6a66e626975a836d85f676f12e4c2227ed68311f29d812191224f0517526739bbd30cbef7de5570093dded20bfa76daef648e194abccde1b8f0eeb6ca48371f62f25929023190d0981f7dfa946767405ac0cd840bef0dff841cbfec4fbee1913b65405b751e6c103957595c9b2024437de8685632aefd073825c6127d4bfabebb3fd24dd3337302a9e1d7e48a7968fefa6b015bea15f37216868eccb46c37faa5b790bcad875b6c98d3dbd9fea891a711a50f77f2399c0d73ec528490bc1894b0eef3582d5f9f12c76f80e10ace64b4fb189b09d1f3f8b397d0ba0dcdad2f84c257ebe2313d3b7e9b9d2e98cb80ab06d8a60738f2ac258a52747b5fd0050167f5efb745223e512fa089e6d2736205c34939f8592a478e94feeddae146de36c96159713b85f8fe6eb75611f3a376884071362c27414d16bce21c536c7b2d54e2cb5d5c94401091f497d088d88202bf9409590b3f67545145e7816932e7e7afe262efa42c874d4c136b94fd46ec2d565ca8acea621ed3843e4d59bc831c4d1d554254fcd417ea6d0224f7ae40fc02d4fa587f37bfc605eccd990f8d1f80e95a4f83a15f4d466f92c87b1372befcf7ff9bc278038f2aa00aef575bd28045660ed57fa98d93bada5a14562397179cbbb048769f7bf888d8a8b7fc6266551d8c396b35b82a09c8514edb8854076b8b7c82f29a46b59812da87560ea2b934f9cb062b0390be579df51869a11aed68f06cf40fefb0d1e9db875a9edf413178213c8e717a8967780e5fad8be9c27926ad3438e85bf8d98780fbdb35937a0b571d0b755f010c483c97a16fa67730cff49e55b38bd615560da418c0c73fde0cd06657b1b78e2b86ac45efe18ac892bfe01d0b2829039f75b80f66c172ed913235c221dd7b2ce73546a9adbad89667b466603168f529acb08d2b6b12bef714af6437797072a58277e402ea2c9eb7a64c4ae5912053ecde75f5497957eb98f90a0fde91251e4dcc12f089d4366610ceb8178baa6ed38ee129f83955949ff37ea7dae43456ae2d875a9b26a37ee2f69a4c97c3fd42873c5ab026d9dac78af3337a77ea0b9a094913b0ebb9c840379795e061cd88b5ad0229745c40b9cd824967801d4b75f53b1c6574b57fddee2d9209c9646391f643c939b48fc4c2ed03753fae15da873183239cc943d60dd7e61e1446f4b66b653b7cff29f481b0398b82c434e919aab04f2fd21f67c097f71fc3651b5cf772545614f3860ef8251b76da66e435a68c1a913b511d39a9c2f141d4c9f44dade3c4f0cfd2c4bba2443248d45bf242836440dace00c92c106c5597cede299364e5248cda8a02c2179dfe03365ece914bb80090b7e3367a80c1b9452014cf7dc5a1171cc0af17df33f618557aeb01ef14a7d529cf190da28fddd1270d7f1bc4773cfe665b1f1d9020c4a35e32c55df1d934b98c8879ac9d51943632a67b0b84c77791f813833f906deabee4894c1793655dda5fdb72ff2778aa0828ffad5c6b2d03eab0210fc18f385bd319d0b8a4df5d6af1907a2030e497c9e104110740d0423410c5c126983222aee559ca093b5063894843abf799b46d943b2018431edf113a6e6b9d47c30aab91e2760ef7be455040c9596e9637a9d061dc1bef7ac06a4bf5c0c05155a5bc5532863557ba890bdd350f3b81406a042de67f8b320cfd50d4a1f6e1c478ec2ddfe7e48f3e1f222d4ca2313b09b39b4cc57256056d8e760bee7cab961b9f4ca7fcb20148cd7dc0e39a87e6169ccfab6b10537702ecd7e60c503edaf671d5796319db236c3b9432b92b621053912bf51fae699800d6045c8a5c0569c5664c6959a02cd172dc55e5e0a7c29fcdb450d10658528b28e9089972695584dee4c89f1602616bc765c57aae40662de3f6a216e8218678c9c6f90718cbfb40625b2f377c262e862a71af1afb431405ffee10f17d790b14c686fde64bca4455eb755a360353a81c1c3f3700b7c40bcea213a0a2aadad071fe16a37ad5e8aa28295b4aeaac4891f26fb2d1f2266da1f73181df6ad8cd76c2d89512e12cd2a498dfb0539f8146072f58376138006177472f901a3a5e885b5da22c7798e0f82b50fc3ecf53486e84f3a499d3444211220ddf027e64418f8b1d603227ab351e564a486f436cfc6e5f4ca98c73670dacea3042df1a217343a6d97d8693a6b115b9a7810af3b4432b8b167804a005135895d1bbace946ba8bbc199f6b0dc2c333ecd2e054db3b4bccb5f0a380a776c05ca1dfd8bcf55984a9785581c2bfd0ea79666325038ad436b5a627dac2281defd6a352ea7bf1848033b59653e9986eeb4b173e72edaf015fe94d1406bed8af9b3185a865c34005460c67463b42ba4143181da0d584219a0fb6d0c2b44d3a469deb7e3e45793663c837ab29514723eed0c88d734ed1f3960a04536a3b023b5021db3f3b05acc76b3194242a157414c37920258153cc9aa3cb1a9209224ae19f7d222015d37313c76662a8ded9bbfcdd564ded0c693d2b7f637ec601481ab1bd8a30158782528c3ee190db110d5244089ea11d5b34e3fb196a644ec97acf61598ff7e57dbae3ad27c79be08f85e0b4add6e2d845863c3a4e5e361ce03f3f60b32d08d97d19efb6f12aa4b6608aa2bde3353591ea0e855429ce829d50d9fe7dd02c5d4544472976b9db3b89073241d899236e1c7f021f2a31f5b1bb927d6097b502c62ddbe9f438f32302f572774ecdcf25a5e62bb7f72762781c8e28f32d056642de8fe2168be17e77b96c26bb44da5088487794177ec8fd1e3fb03ae797705db034ddaa6c421e090be3f82f805f5a721520e54922cfc4584c2da675761f0b26c800dce8528ef89ab60d8e6c962edc2e8ad3b455051ac350795f55b49e896243569540e38620f58ae9318c82513425a4e5a0b00c880c8df865bdcdabec578c65d31a7766013a319adcd5e08a7c393744c04124ec55506e093e8a10bc81a1022c500660c2b6e11372a852aaa75eec54f694ca101136df74a591fd3076d63eb7068186711e3fa62100f8cd583044dff7717b4382a46fa60ec84ebd6de0e5ab58759a5dae8e6dd82be47991b72e33195b76a49ce5549be04329ce0a5edb4f4a441f481a1fb6630befcddb44646bc7ceeb49e0c1051c04fbf9d1afa108e789b22ab61284549c6d3b43fc8abdb64d5ebdfd64b1e2239513ffbe136deea5e41cb046ce5bcceb09d949d60cb224562673e000e60f28f86ff0f64a4eddcce9b1a63e58a0d2dcb24f89df13202f5baadf62964d6fa3a6bd092b9b516efc9c2361d403355beb7a7e7606420f968f991fb279cf536cdab4310324fe01685f5cddf0882e10a6b5d463668a61ab986bf8d5cd9876a11f6f290dea7f2980ce94485ab250dabe997c02a473e399000cfabe4ae5eff783f6d281e4f57e909983c250cd3d1722b0d6c915d326b4ad57744ab6fa07adb81b578de5fb8d3cbaefaa4525a8e84a9900f2ee7a4ac2f2fea36137964de3ef2a524a30aea830f4680ac29618ec860567873e82c30bb54cda869e676758b27a360bfc99bba472b505dacae2a0c2b882413f8d737fc882b57f46e09950adec77578cbfc8c4a9facc280644d60b5d49be8ffe70479d610f50422f813aff62050c56b53f3dd860dc6ee6d37aca382b673f09aacb9a195ff80ccf6fabf93b6ea79212a0dc37c4c7db5cf95978d429977b152adf80ca9956d07cf80fd6fd3a69762ae4fd39d382e540e837d5d019ce8299178db971b7dbadf2b5a39fa342607eee133fd1ed9dd0414efe3c47acadee50ba6205f69ef82524c4cbe793cd73bfc6628b010a9344d626f85076b62e41afa88b7811101f6bc02d89171c79145ed83bc87328ae8886ef772a31fa4270d487c97f6290dce582056b4e3da0b60e666a9f9251ae843591c79cf7f4d519e76baf6b7171a90326ded473e6465785bb40eaec41834773adfc90ce8ae00d76ab1da3f16433b84799c26d1b9090e65ec19bc7dfae3d403b1a8f3592b02196102399580887b4117cf8a324fcc894e4fa4adbb83737a66e5157d0e130254fc2c0d673bc48b34fe46af4cfcd35f8ea6f8981a7afc32f3c592dd2a37cb879a89a1b5a54d112b29fa26e5ebc95d10bf443a691e3c1d91040fb3e5477f8a8559acd64c14d970bb17bf02b04184d426a068df17a5e31605307bdd85f1542510732b20ff8dcf39427563d9bd147c5b2e0acb2e8535a6ffeb11680671d62126e5baf9784993ad0f50d23f98aa56755585898e16d2623230f584acee1b1ce551b3e32e007a2124c4afca4970c48f646725aa770f7976c07d0a817a2d7d778fc4f5261374483b5cb6eb46ce340b68cbed4ad9ad300a86d5ae59da4132340a3514911f7deb56f4fcbfc88c940eae3893ddac04d0990c616f49d262b7bee75c2269121d9ff9bed49af63703d6238d4263f59163e2fdac8cf598e628f80b320517ed7ef93036e27b3526daa65530063bb707cb7eb5a018f28151b090f053804486272ac002764ca34e2a907dd091755b4823136a525b5c78e90f95b171cf8890524394912bcf068447ad868f0d4444d1474e4e3bcae03968dbd503c16797d0542aafcbdfc5201e26465560c2bdcebd33d0eff34536670327786342855c6c64e7d224eb00091a5fe8f9631c4f575754657f51eb2a4ef2ee780a0c2ffb13400af9484d3b86642649d00740a26fd0c0ad8d9c26f56f9b83bafa44f4fb7e0f6d93bd4b7d034f95636ca01566e60fd584c4fd6676afb592e8cc86f6c016c4b6d2c31ebb4d99a524dd063a7791de64f796854799a825ebeb537c10c91b49023c77594625d2d36d3e06365977e2863f29f8f1e6254879550f54a9b90a64650716bebadc514d06e3384380c9003b5290e42989d4f28ccb4bd733a0e5dd88e08edaf1aabb02aa09ad3ac24835b8043bc7c580bb9d061971e4fb334402ac542b17b54a641c47696988f3ebcf9c33980bb39c73d005b3b78619454ff35e1ca763443958441a1153820de26035086c0c012e787e797f40351738703850a22f1cc198a37ecb3a07e2b310ae52b57db03ee03fb27602d6530dcc1955b696b2ff0c27646af7e21fdcc14589c2f3caadfc98b273c9574b1931991549cff4dc5b657a51419e2b19135b07caba82a574c092313188015574fe605c3e05a275603f3ad7a0d50955e3e29df7ad8d91a28372297fa11a1f45249fd7e173d467f7fed9aa699621c1509875e2096c5ba815507c32d33a99ed598f5a14830fb4d4ab9fc1392cf97043e6ee82adc5a8eaa91af973b7dd19381a2fa81957b4f921144d9ddd6bd61cc4f18f432e932fff65d4088f85827cb1c5e2d34ba0267f52593aba9deb026229772971d928ca843aa407f027137dff4b13eba0ebedd147e23ba8a02c03eb65a3dada1bb84205450a7a9aa25d92ef5854d54e783fc2fc445dbd04c1fcb8bc73c85579c12bb271be3861fe027c39fe5b1cf080a4d79cc7c5eac2e896aca823fd8eb09e6b0704c1a086b54d9f463540e0191e000b74442d695a1799a4e3b955ebabca6b3034eca7b6c2b3bec138022a5b25c8011c6f5646c81c3fa844b9891597dc6539987ee13ba184238f0f2857348218eb79455fbdd88e500d5ddd53a9ba62c881504abe4cae3303daf32d9113bdd196a02077b24c919c2015beaab82960ec3b37578aa4de809a62bf3e8f7c63ba898bf0c9f3cc26914bfa0479830785cd17578ddf70306b4422bdc151047fa90386e8089386adb24b6b8d15572c7fbf01026c5af2de7eb0c2b5adb36c25b9c74dcdbb061b131e38e0bef894f90c3a36634e02851212fbbbf8ec840a06893e00d5fecd986cb7f6fd24c5c473fe7f5f66fe582c52e2b7f23935f19bd81dd0961016839db5f21991a58d70f893517121404f8a5622d4c0a074c7bf6f150d3b373626a8c88f27d5308e6d48b7594147a1f16f88f3c8f173a5c8e4390190d6cbeadac1e8cacf980becd5e9ba39d3568cd8381cecfab96dfa91b10b7b58bc89da82659afc7990b1e3e97f1e035e92449c97c5f6e7acbde920b652f89f785207e0b56b92c8db69d80064817bf5ba94a32227b959755ed526e7e05e98581bf23b9e3a8368b03508ca034e49005c802cd4490aeffa99db98a33bb15fbe8f836ef5752b14eb7078a1c43225c3d2fff9882b6895c4a3bab235804e6cde26d6bc41bcc6bf0cd72d33aabf709645b3829428d9a39512b1e76570af0822f8d8f878203b8346174a61638943c54c4a2ead1b40e4320211ab1d5bf0a0868346d582dac8986ca3b9011243c7f85a851a72cefc6d019c5a82893f5b84222c512f5763c73473ff3577a9274791eb64f101b1953e0c22ee1e30b918d815690c3836e779d011d1bce269ba653c4985b5b3e2ef479d2418b081064647d19b84aae48b889e39c943316fbfcf47ae349b2417dcc27eefc2be900cdf9719d693be3695664a53771fef8654bdcea05cc41004c378fa12a2cddf50ac98f43265e3e29328cd8a2222dedd8cb2d0a96563ba029b66468c7b8a56c1f02579af90519a0d2ba936922a254094ca8edd7ca4a7ea9f0807a074ad8dd9aa03098d469fb5df074d6da54fb92968572c024cb923006c87a22fd81fa59c499a3d0e773b4e68bebcc2455756380fe895e78b014f0545d3af69778e40ae15ddd4532fd3460808262ac9d30ea1994c422785cca3e629e9867ecc1855d80d799056ec73326366399e8656c693811c8ce653f1c07756c8ef1097d3b9496f1196e0a00eb7924743b2b8152a4953d11e42ad9abfd39499f4be64a833dd70daddab9d88e0ea1d46c50d83da768497574e74563eca8bf5777be0ac635f584cd65b9e9cdd064778df888e18d70f92a1574a904ba7209a61ec8e9ea2db350b2b764bbb78114e8c4c40d7828399a097ceda9615b2177c239981cc333a1eb5e75b14eee591eb486f87e41559658fbc9f3a83840a72f79dc19c5f30544c80212c36cd7c633c6064d527e3144907f87083da6c76fef47c49d85837bf7872fb5a3fb68ffce50649e11ff3632f2130de24fcd2029101c40a517b8de2ad59ac9dc7b5e85b514e8b47b281ee310278a5a4085633c5b16905ddea50fdbc8009913260e8065655320de44257f6bb86c6424acbbac0fe8839895d4fafaac62234d33125b83082864c40749d805fe81e70d4e9f6b67c1574e82d0ae40a8a829adfbe0100cf122ab347a56239a0052a5d1c1734141c1667fe9ec8817d2f6d7c62db5e3b1726eb44de3624e9d25907093d8080c56b1dc81858b6a36d8fe49cbba0291713b03cf99ee70030e00f3468eb2a1231d51f18abea950ded7b3a59c28714476b38e7f8507281315cced5d2130ed5ce20b5bd82004b3836f98b6e3172d971ffc5ea0b3276e6ca3bf135c9dcf49da0fa7336b68ba399708c3bb89836b104fb69aab8a9e3c431f21453c013b37a21d25ed2f3aade04b71e1133b36184bafe1d4c119642d280d81677c5867ac4d418fe2358ba8296ee4a8f0f2cfc9e9226823c2a5b36b1558fa19723d5f3723dd3cb2a57ac4cf185e93a7d91bfc4d4833c9b0cef64222965c9577134b3f5399c2e4bf5a6d3b74143bb20a04530371fa8612cc16e2c26a279553eb061504b860e838d85807a4fe685fb0b8ebf112014bee891d7786eed29ae0d6ce127cda968be7faab74a86761d727fbbbfe483630b54e1771a59fcfe7076762c00f27475f9d9bccd1456e65f64c9f7952f3b8cf5a61025961ab9c2f28f9322e95daf5de131c27f026755bee9f136230595c0e9302a85d1115ac6a8e785b4db2cc06b5e55282b81c720ccd3a5c6a4aa2a7c3661de3428b94782e36cf5e2dfee4b58777f38496da8ffa05483c22cc126d4c74af752cb35d22981a3147767685e515267b275320877ab5d69c66c8240274233c04771b59d4a9faf952ffd8b48a894e59087d8609285a738dd3faa752c2ac53a195dccddc5e6d4c9089507e2a2bcce59310c2b7ca15aad2e35b2f302936a0207ccf1da58143e4705601ee50c6919e430d6b039d4d79805614ee1db5ac9f2e5e0efc603a3e9b17fe90b60f11a781038b1eba91949c04bd49e4ac62d60a8fd374d287c649abe7780482399d05147a29113b7fe23bd5d3f9723fcd6adf25d5771480b111e7cdc95e4f538d7d6b66a95fd994256aa795bdd59e7f3d59990a36392c19baa6b0f77280f13c45eb0d73dcf1da213e1883256b7c71a00e8480605c0241f5e8065ce15200203d55bd87f360ad5b640e84aa34120614a0a5e456fa942aadab667848337d5cc9a117f10897d8cff7164300889796776de4267586dc7f647be27308af6aaa74147b31955c9ed5d6d528db089511ee643ee422b8c68ba8daa06c463f175a3639439fea3f1eb5a9422d6da4430b93bc9ccfd78c1868216c745cc6a865a148185d86a13040cf8f4029e99fe8511e69d52d974ced5d8737f24b5410529c3de2ef4221231098eb8fd4f42dc92ab85b027a300803647f805bd1838a707658a76f8955a7300dcd55539970dac5466345b9b697397dfe73dd8a9c33b6ddc8d7c5a0e74c6964fd7bfca9ecf1da92efeae0fc0c31f741c1ee1771a9ffd78f0c66d009e1e0e7260029fad0b242cb3417ddb607f20b204b8b8396f25249e18c6839e4e9a76d36fecbc7f813ade0ac4b68074a615c0c2d47d98bf12b1824e6187dd0a27bef1b0ba43e257624aed64c7ebb66f6539246cde441526fbcea52afa6485049cdb7901db46aabb57e6862af9bc93c51a4e104384a9e8f41c5b49dd40afbdda07daf023f8fd1dcc294bbbb709b8a42dfb07e46a6696e7f6cfcbff343aa54e471e2ebbac8a2144f74fc6b1e3cc56a71f2f34c48193d6c9fdb8d6b1f21c27ca74adfb502a81c0916ac79861090cf068b4a77cc07dafd7b65d18973d3b414b658d644a66ceeb914b1502de40dbc9e8d4fec7f214c11a8bc0c940fb79fb2b3bec90e0bd644d0b0510df7e7c26e759e8e867d657bdddc42a5b3b713154b64b94e0037a05f18e46afc587760a6c5bdd26b50d74253749ca59afbaf37c90bb74a37bf2ea46b37a9b0418f7bdead514d04fef50eb1159b99dc9e881869e071350f4fc21ab915a0da775ae5f61d4c7e2c5b3bd94cd5d39678b511b3c546af275cbf2691c692c7d48fc04ea632842c16549e632d3f907199d84f9caaad40bca46b809f3482870ffbea262fdac543f19d0fb176238dd863018a7a4ba2bb6b025eda03a8561e7b3f54ef0520ab683e32ed13c4e085d00845bc5580747a032c29db7e65d5e65dee425cbfa1a170810b5d1b0bd6b21ec90bb9f60c6b2e1653dc07df8b1205419eb7d174dc09d8b0a76111db1d50996385d3605e85885d6464ce1a0136eab729452b5adebae5addf0e1aa1a8bd61388ebf57a69f3a1d59fd4a2305cef3d814beb1dfc705656c003a0fba8b0ccb9e49e5bc2ce10dd2c0289b9509bc925e431229628ac0f26e2a95f59aaa7e188a2ab64b8a4114a8212c8caa7032220c07f5ea13a4e4870f92f2f35083443bacc68ee1c94b5f9f0ce784b5b2b6673be54a913ef889be364e1e2313464cab6738f7a797714894c3f48a1374212714a6ba2cef2251871ddae1abc1196146178b10a6eeb7fcd5a0186cd9475960133ec2e0e235c485ccf1672f6b4d0c9869979a50bd02574c77e36da2eb2c85cce22f3a4cd6eb00d1191c93b3c1c0af0be5965158e153c873ff8086b5a05a2d06a3a59502a2b98b7f7f12025e72fe85cc511f0396fef2053f9875a4752e429c7a11097edb0c56e0b0cf8eeca88ca796bb13311fc6cdf3a198697e5aabe7192e1f666cc86c08cfdaec702a506d8c46e03ee9f3513c9a8002f3786cb5b4fa83234bf2671c32b65745007c99c02a8827da804deaabc33ff29c39a25aaf6e7ce24510e7c0522cf222ecf793d018e19b4aa639e930b9df07879c1b9891306daf1eacd6b0eb9164df44d6e2e033de5e70173815c310626679f2b20bba7f03d0f1830856d970acc922a04f4147456881ca0116703379429663df745db1ec3c4bf370390bb00a9d8df5317f1f0dbf458629d0ae7ebd3c1632f39fde2ac412eda147d296acbf6f681de73b78c47ccd037090f51511208eb6d026e499af8196f6467ab17c0ede3c3f50b8e01f1d626d49e354c5e4d8ff9ee839061b92079a77b9d6f0372be558dc652c0f9e6a788591f64aa93abb59cbc014459c423250e06835f90e624b73f5f5d1a1e84c37852edb3c26c1f4abd664981f2d5852c9e377039bf01de7918687c8d901eb3a1ce1984db8f968303ee066b6a912c51c046fa7832df2b660ab588bba9737dd33d6c15a9bb5da75246c86520a8a0490148e078ce25f25b674cc05fdd1514ba073408252f414eb4d3c791845aec4424770b5a4513cac9d416a67be8e075b3dc5925ce50d663e6108c4eba29144adc2b08eba0becd5a3d2f5e36683e7aac4f453150863669c5072472bd230db33d06bcbe0e6a8eb602257c4f10cfe03e3f712c238125c587ab72bcab0cba1f21a2ece27431d3eb12c760eeddca04f41bc741179c3f211619fccd0ae50379275b4cbfc4ba6570262bdd8ba3face03a6e65d2fc6bd9c1fc4856a279de8a0c5e2ea58304b7af1b95a5ba6c2098a0debe67315acf99def2bd16adf079cc9532fe3f50be04123445ad2cf72e855eb36ee3ee3af5c958ea6d4436f397bf106727bb8dacaa0a0a044cb31f456a3eaa7e2c96800a3c59b6c3255ee8326545d956c9119c2a0a697168e06acf621abc8f4b81b49d40f25a1df010bbb1c423a37e0425cad5b2012ca5d4e3a0a33809535c4a730221ab583f478be52edc5f740adda94558c02a05cb0b56c88690e729bc549b94abcd6c2c8625ad95f31bcb22ecbcc88543bec00ab75aa7dcb1060bbb0577317bd8cdfd0a967d2654543e5eaa9f33aae3e901b2e4f1a97f7d374918f86ed083bd42c5a4b996d2739b7b038bdb46bcacaaf79bec7578ab39d64ad25f73f65773e859e2256f225d23fd74ee4ba350e12455b79330890dca9b7f42e8415bcf418991255b2df27434257b5f79831b7f2c19d930405c1f06b5e75a503e0e68398b1b0023e21f1923b34a75a1abc787cab4f2f71cef7d63915d4f5603d508159e12379ec711291f68b16399504dd2b1467a97397febe16770c0f2be84e1070bd7fe1e8e1e5ed91ad8d01ff3d9fda9380a4549d1ff03ee8e7199adaa900de48ba7e9716dbc5037dfe3dfcdf8287e58d41ba0b0974b342d201c991870db7aa1025987f13d1684998c2349877e37d0870fe0afc7187a0cfd23a91313c4e80ca78d029708da936e8b639735f06ff3dfa3dd4c0b88b836295347dbfe18db44f88f48083edcb1bf8d9b6e631c8c024199ec4a742276a570b12d7f6d9817a641605708c51d556f44f2410425b799f17c9d0e5598c89c75c86465e1a7c2fa137e45e7eead8a23058afcbe34ae1faa1797524ea3d5ba8cff0d807ddd6186280c5d452a60ba9286dafaac4d5462a6ce0accb9c7f2d9a2dff6f8e655d24213e46593ae40cbf59ba930203b0494d8590ceb027611ec73cb4af2da26f59d3158d51b921011e0513376b951babe06657128980117b3236b8a07e1b6339518c4bcf802621f2935e2541ea1510604ed5568a79db93029ed547322b7f491d814cc97b3dd98e957eb595f210c2a6291a01afd7f7322ca4547445fdbf605986ebdf5c1737dac4e0a070ba15efcddb901c24df73ee1cabaec7fdabbea8dbb7a21962ba70d042eb7eaec4d3d6a4b2c4ee8f17a12b7ae6057ae1d8b7f5bc74eec9501c25193300e1281f59df0099b98d44595c0d89e54c7ca284127fd416fabe89988564e4b669135d83f9ffc5c8b83049e2f0e8bbc813640e5953558df94f3dc627e1da26a1230f6caa37681bf48c951748b61e686c7fe2737a89680c6356ea4ab9fe392bcebd71d6439764467b3cb33758bdb38ed7e5dcd9d5ec9826a81febe0f5160696ee01bd105f2d693e5b3607a3e60b370fc8213065ff51080ab42c1bde746d3be6cf51ff385a9cc6b6a5ebc25eb4f82265ad6debfa04af85cc0433e6bd81dc9b841c618844604312de794c2c35a40b00d44f295f7fefc42d6245c853c9700dfccabaa12921e732e3bdc3316ae6d2288b5229ab0994ae45208d8106fc81be67429b16e51b58c2526a201e87b0c3b592b449df3c577d71f8a04539b6db1bfadd365e8e1ea1f857b310a7269f8ffca013ce0040e0a94402d721d2dcb7144842230d8ae5ba6096a0ef0e385442a06cc3a4fc189cb29ba7c73f01bcbe4578f2220fa8e9d8eaf2b3b30e44bd345fec1f4465cd4174d36df8286691f7de610c68036a1da277f4f4b6f0dbb9af096e28143741fab33a66dd42986d0325d7db58f8f3f1aec37532b5fe98f4f6712b05132edbd7a267ed2b61497fc5326fba562c135c5e3a7cf6e69b5c79d986fdeb5b5cc4dbd2f2feb682ffaf7b3db72c769a910c38a84479df4e6f44c7d965313fdfec9d38de5ab865afc6bc2f54d61391c793408189158dfa73e1326506638c269a3432c8a6a5e419617a7703406c8ecfd28090fa106b6fcd4aa0b694e2fbd71660f3c158f1ed1a71dc6308ce5e20e554460b5b6aa556be16f32fe045605dad9f9284a2bed96c0b38a5d4e3dbfe65669253d5ab09fe7c3f11499a8fa8946ecd4a614920d903daf0240cc858b286627860772bca0fe65cebf983a77b7e30b505f9f441a956f3a6dcdcff57d8185c2a02b625ac154d9a0c6bff61ae24d1e922c4356b5ee11a261034ab120f1c0d57b0e0ff17637de91264cbe05dd5bfba08aa52305499d3d066024c992e30ead93c16a2518dfa4021fb0642e0840e5b278d7b83130246640a831e05218332971a8f3a54f534eb50d421b7d53b055a6bd8278b46dfe7e5815cdeb6246eed85dd5fd7f696b988dd8e956345175dd20d87a952fa740653813bf5d6b61fe75d4f22cf02fe8b6f174f40e3fc6f5bffb2c390a69b8d8d4c2b57eceb9ff0cd485ef96ba1806728d714ecfaea3ae799b20a9005f3097fc71a5a35e225b8ac8732a79ccf701472f36e3f4e4eea47516d88cc64c1392908e19247c44fb364024b8d423a8f38c2fe59c47dfa238253a0e6d79301d9d776320b921936acfba6ca962596d9dd78b0988a802556666f5433b3e2f6eac21af4ca7913798aca6e28fc208aa14527d3073a77d12624deb7b20815ab75e1813c3519c1c5c37520a20b6aca1725d068671cb5ad166759d49905853a92e6c2490f2178feb5e8e7a215abdd4c4bffc239804416b105be934b40e1541934858416ea4cdddfa126f4038608496fe05cc14880c9686f4cd2e5c720282f44893c8b1ce3d65a78dc57b40bdaaaa452873b9cfdf65f872bc031a2c0c70191469a362e29b856639a90f17f131393ffeaed7f7d5e60bc82c8f7b1aee3fb798511eabb05e370cace1a987e43fda50243f5f5e7af26244a28e05f3d530fe25feb942c82d3d846b453a21f02f8003ea688a09eb4cda85f5db4a84b92a203adb2b91fcc1e838beeda0a61bd634bb4d7f3e07d81e70ae5ec0bc7e19f46d14da93fc3aac6270c213b42de1dfcc2214826946a151df09f67e61465d77b315040663a254059eaa2b2f7442217898c44e916ff6930ae8632d576d72680681391edc4aa1070d28a6984ffdb07fae5fe720a84e655682fccca14b3b95fe45e3534d2dfc9a8c33af0f85e9efbeea970d7ea6cd67615b3cbd1ae6fa268b3e944a1b257542308ecb256693482c425a72058f851080a90de600d140c17db9529c5f1ebf25624bcc4fdf21632702463ad80557089eb12bb31f885231372294c6b89be502b9fb67970b0e1342eec0f2e79a978e3ece3da45e3710375d4574a7d37b7ea180012f55c028287d3f196292b8f15cc730148c01b945c159079253081b0469f3f173969d10373e8fa3e1551be375b6c7130257dd0f0c64454faae33038fc283da2728131ddf19092ed4cca205bf64b58efde89a5f2588c80813b57c5eb62935436ab33317d14326d6d1a8ea568fdc2b6ed08ac46ac01a7c6947b5bf12d025d82b0d09a51d51b3706b8029b2db32204e8c14c5419f7277aa22177da15b9548ef6a1a1021c546c0a7ac5a55624793ba578bd21dc25a704f71dfbb7e00f0bd3a4d66f2052810cc522e9e6e2fd1fba9b82b60aee239697fb49c3c5d70552d7f5d19bea8bfabc0fc2b0ddfc9af698f200d9eb9c36c24743ffa456f09b23e9e5ed53aad1d7fd0580d173ec03e6ffc819e846c1b48e11ab6f5affda2359252d08c1d6e85e3b1ad4d54b987a3972609006169090f3a2ecd76e36025e1a63e586f9e7e3ac7056dee318a69c14d43dc3e8d10b3979b0a1ea2d5d295e1158f018553ae51bfdc0dce2773b4b5b40dccc626ecfa4315d5ceff2a04fc9bc5cd17155260dbbfb6aa4d8464d7290cc72a8b6dcbedff33af42ced74343d79ea8b0ccceb708278046f390b0e4de0174542c8b348f49e974dcc7fd668a3b60584681de095c4bb2459a0b4137e64a3c14254d453902b02a538db34632bfe1b01ce2aa901669e666a5a729a3418750525523195c1a880397813006ad911821c16b8a8036775d650b3b4171eeab7e2bb31cab766b05f649c881a2ed1e7c75c14f31b14128c4612a88809e212672cebd6e33722be749fc0802a2327d7cc5754ca6c4ae7da09a891c1f0fe58f9b454bd4e0a51af5e2ecef75f772b497423c4c649c12251070f9ca16cfa0738aa38968f569de57e21d23ca41e19f2575b819719b570ba228fd43bdef991de75303c8ba5293209598e9ff6b0f526ef6fa43060f12746dc9a07c8b2a5280506d2d499218a00488ce0e065ad8863dfed034bec077bfafc3161ae11163f774c9ec139cb2d5645518a76590f1bce60bbdac2d1cc4fda17e58ddb79968b07490841ff343cedf3ac1a30457350b99fdbc68e947c76eb6f11f1a6e0a5b5ae848e1bffe2dd4089d334657116ca14ffe2eb60d124ad10c009a9bb07005b3bf789ff688a22326909cefa80538a43198460683a9e4471b1a53bbd8866e349b387703335543677d9a4a54b02491062ca06643ad4a2f7f24b43dff7cf10b6143ad67cb1b94057efa53d0176317d56de1f6e281d8f69de842ac03dabc5548a7c980f8c8a21f6b3078814fee14aefba6045c5be1901993d17487fc98217755fbb946c6c411329b65a23f1beaa30c486814c43435bb9c359369b3f51b18f1a93f5dda68888037f42830fad7cd6950c6a76caa3eb0aa1553ec0154a844d5500273429f0f5099cf157084e086401591b370bbb965798bbedcf959deb54a07e91ed9a85943f7859369dedd6d94f3d6544386eebbd81189de3d5d7e804ecb2743fc9e2b1f55d390ecbf8923ef407b8891b187cad86a4859bbad945a5bd79d6a2c3dc5ea744dfea26af63501160e57cdb707b757c2b6f7aacad8fa76e36279837d4b118865a31125bf98e4772b2bbb74a397d1cd0ff53edfd81498facdab178efc1abe1d446f8d81074f6ed8e0e4011c36de9f15ea607049fe08e1a3d5e9edc7474ad2407c1d239ff4b0bc3678446e9122070ce4adeac8f87b5959189db3099b04c8eae02728c87591119273e30f7a02d5666093552ff32f35887b66daf533ab82c2988d4c1c6a5beea8a25a66d08982e842b471793713aa60b48e69c0a2a0e8e3d844e287e928afad1e0182897b98cd191355c60525231682082aec10d9edbf30243d076d89a3a9b10b9852f603b6d3bf65bddfc6dc7a9759f11715cca376b9d9d5e6fcc3b32da6b6e8e06ee65eca0102b82ff39f059c6891caab193a428729acd9d4bfc23b7c215c7358ca0f72994cad2a3636f244bd555399c8fc738cd689127fc465ae80fb0c14a23cffe05e490e4b2a671236d64b7f8bff6dc556664665174097238b61ba621725dfd58a6fa1c5b75df1d7ae3a10174c558603399f5c3ae3c0f5bf15a307d5c0cda1fee964db22b8f89768254c97efc8142d324820a856b1a00ab6aa4cea9bf90a75c5fd2f4e896c198a65b3b502722d5f5f333c066deeaf55da4814831026585a4bc27922c3f4b2b3c3ebad9fee018d5c454a7f491b09093edc15283e58d3f1f864030d00374f745f38ddfe4c28dca9943c9f5b9c3c368f8cfefd411a186a12862e4cc28f79b594c44b30f5b3dc42bfda773ee74147176acf1a2b1589a03243cda07a9508a02c9fa7d1b3b9b79c96b5c9f2d3f5ca7f88896a7f1f652eb43708d58a65b5b9db09456f17639dda832f0996f46c15450a8c404dad293f53a5b51b5201d72fd818bb8cd000d4da8c724eedbe4c84fa14f136676a679252d945d2c7e3f3349f7a94f9beea5afb334d341079e415b691620f70f04e0dd33a974800f6a81b68fec66b060fb7b80f2a6be16d0c484d78c2c88a129cb2caa3d7324d82d42a70328bf8262d72271a79dbdca2c1827292b4bf4c19cac9dd90b150d1179c4299c13a65226bf94e2a25cb5e0a44f2739f49c932539721a425806efac7923b20e85931eb52ebe109ff57261b7e026e0ed79eb54fba47504a94089da386ee0975c206cd5335497ded0352ee7b17aabefd6c059277c127397212d0a2619d6a3ac15409bf529a1bd7708ac51218b7ba7a4385c83bef7c2e2c74c0d823e220c930507d243a2b9a5c2bb944ce17ea5a0bac841d96af901769472de5ceb2dcea0230b7cdb19d76207f06815eaf8a6e9cb98ff004b23bd05db6ccebb87bcce47052c18104589be143e2523e7c3231874ab8ebe862ef4aa39405b87d512e4a113588aa315e72a3ca3ff692951a38e7532738c4f7de9f903bcf4b49ec12115caa2bbc765a706b32d9c3a7fda899315ac053a1b9f0882519ae659be9325bcbdcb0a5e8f781b88442d7cf0515fd7350887d2243e28e51352adac496b8d3f80a11d5de5f029b6c847d5929c985d20ba849ec6b29ff50af08b7f8936806281d2142383a45c1193efeb21f025b3581d5c3e2c2a025cb32e862812469bbe785cc8f4c9f5f33e0bf19827380f193fa5b4149fd2cd0c3da989d7130f4090fdb16813f460681c915ddec339f0ba91b75e50556fec47a8f3ff98d20132a371b23747458c783c76b3cbb991d5b0d65124b21711f927a5ad4ec04092a709b53c5057dc5d2996b50ec75a134e08ea11372f79ff577607c973cc4f8ae6a659d700b81d517a2e4c8ae86ae58e270cfd8730007945a36e2a961a9ad62e910a2f49302f72e37b5da2b96509ed24e34108def9699c60d764a048f4b15bd8c6dc5929832bbc255281f0d1b565b3437d8d5f79c38daa64e202f1e2486b6a225d84b6fc8cd10c6a1cfda57615c56b56493c513528e1db1f698336880c37dc6f28f003734293ec044363692ae938aacb95063b6e836df1a150ae99e4b620500ffc6f02d3c2c5ab76eca4e4f5f49be4b62b9d8429f7b119dbbe8f5eaf32378c6c974d08dfc816a3adebea48ef26c87f6a5a31ef4baa4ad2c5711bd842fc4f35b5b6bef928bc034772e29d7d12fd3b3b61cc39288ce02fa2aac3f5384d7795d09e161981bdd706ac5975169f5564fcde8730c8e9e333aac2f6b34c82a265896aa1f0cffa2c3922661c287dc70d38775a6102b0362580cf630bead344a7151c89a78b38b32e14e69e7e9225651d4d5e029c1e3ca3dcf644d57ba33a68bfeaa1a0774624230c57310576a5763e20461afe83d0aa7633b40abe05035e1227f91f23e20b2d2fb35738ee96a9b25cc1f2b4d5fe9e0b721c03c2658362cced80d794c73fe4987831793fe54fd9e8ceec69cc7f42d1ce401f9ff3ffc46a6b4a3def35dde3169f11afe2f511bd129bd3b98cb50a044d759978ccf24a5b2c36735f4a8d1ee949d3a7d2b2e6fa6e04c773cd9a9095188c777ba2faf365212be1a127917c8890e51b967bffb5d795ea2530742b83ebcaef03f0583b5a4b25dacaa5b0ec576bc8c8d130cb625a00fbdb9280863228a6175d163175dc7bfdad5c8e0f0d3bbda73b69623b29362c63783b428f3d7922a5199dde691332522cc5aef067f2f08f5a50081e761c4f2913036f95efd24994adbcbcfcc7b67f2e954c773e824d3d95f8aca04cf5e52bc671387778f56ceb445f671dd88f421e7c434b4977ab40ee5f5608f9b7ea08b23f2d870dc6266c3605194e09a9d6b9501d488008e1395ded04f2c8d0d744e23bfa87052621fedd6fbaaaca5ae1b386dcd2261468c7f76b2c289ea6e72743e3fb43f8019dbdf0b4504878d7b69528f44718ae4cc75d8038a4c2327acdeb3c6a6c59245c59c82f2120b46149093f23a21709d6c33e8c9df6fd25c110e7e4e248f7e96b1f914a6a53c431b0aee78db8ee06874dd6367473594005f6165249a87e8d5393cc1e1ea229a97170b82a5690513ded5b63712d0ce5d205d0024198f17a228d8f22bcec62a047a6fcbda1963d5d21832ed074457f62a30bd8f21aa1aad4284515117ff9607538eb51d51cc531d0fcf88ca9900bd28a07ef11551a114a2e691d76822e62a6c6274c8a57a33513f4f08d815fa6d6c4e69cb8c0fab17a8f8ddfe12c19a92aa13223213437ab2c1a8d60f4445a4017f2939598dd3a66ec29b695e89d79a5856594134095eca45f19823022a4c667b5791d28359c317c523090091f4f931db7c78421370ea8a7db22efe62cf1d571faad191899379ede566f02ca7d0c66215a9af4e8d8eaee682e4107545233283eb5fce62ce0149a554abaaf182ce649a4127df77911efb59e15b5ae5a98f481bb0ef1c99a95952def60ed376b73996903c459aaccae7f91011a30d6f7012136000dab754e0069367732339d2facc095341c57c88862ad0834fe7ac044dadd64adac246ff99569a120d11ade2f87666c8f801278075d0789038a7f70966c77d3e2eca2ddfb93524ba5b7b7fcfe082e6f3d6886b3b42137225509d7239e61520e98c0231562b6c1a48bfaa595b97eae369d9fb867b9d2b518b5ad89d95010aa1603afb5131a64f2c77524ef052659fb2d28398a995494ced22d049a22e76ea742391895acdd52ad57392c0b5295ea0f3188329614e748262de426f468b782a8be5fb653de30e49a986083863d93a004bce83179b28551d2d637f9ac90c28eeb637138b582491f55ebae4caf8fb76fc4614bfa141f62bb84b3e2ebef9aa8cd8b3479500e4b8c90ee94c49e8568d48f70a97f810b3f92a302e475bd530d5287bed51551d208922e93ae7dff3826546786d01ed0187a239ec9b8ad563622c4d849a7e5f74b7e1f2d1d13eecc1f5eab5ec6931d34ae1cf0649b4cf3086e85c48e6af65d3a2c97ce4b90c21c1048dbd9f76d47f3d77303a7d4d4228ed7329207655185cbd133a401dd27d6dd8936dae049bc6fb4a43a661700d94cd8ff9fd2a72a5a1037d5388662543abbf6855c2bcce3ff48a120208fa26491d12899d0c65882f715ee6e93f28bed2679a5aa7559b3ff172a82a96b1282f642d40fe5ed612e91680c93aad62000baf15bde39069cea3150173aef8acdd9c97784c6adcf93b5dfff574021eea110f85e88b446ac170a5293dd211ab2b12ed2aaec3114f309c2b4c13e883d91327ff45e8421ea22af943ddb9c214dc95a94a7624dc40faa378d1ec3c755e25c30329de3398ebe0327ee892ceff16b5adee16afc3806f8b6bd25739f23907521c06e90320bee22d3f754551f31555653323a9220070efa5242b183fb3365a7d8fdfbf8a95a3138cc71673b2432bb2b54ff07c12656c1bc10066a6a0120fd437e91ded602f30e13510dc192d657d4e6244189caeba892edda033d490732f8fd562c1f85dbe9310cd7df7b12cec8c21cdd4c9995de536055cabe1913b4d1d991e154b977a19512cdd7427b68c9d53bb59be4fca63a8b4d4fe9ea2714ca07cebbe733bf3550b1a9f62105ed08e7e1b329fb0a9c4cb7ff26a9470a29cbfb283543aef326aae261555569bc0efac348f9677f67df59455ac5755720b9d611d1b571db4b1086d59b8ca443fd1eda4197616bc8657e180ac710c3d8f21ba56cbecdbdfc4336755fc215f552fd1693e37cced71a8f242d3328456d0ad64f2ced4c33dcb08fe1dfb71fac591a99c151c8fc9017b175a21a04aea05ce380dcf14aab1ecf42e4487a57329b73f176c9b87ec17108fecd0480ede99343ca5caad7826f11d8903cf63b10a1b5c2b917ce04001eee1a7fe8aea78e1c32d0cee443b16e6d785111ea8813960469687e1eddc4d8b979be86295df22e6918045cf3e533e9f93a100ac582e3bab559e8bec866405f9d55369ed8768448232a148336d1253f6d3debffa81da1069eb1de59a8d955f8e00ea8168ea8658c16fd9ed04892e7af2f523cefff25de209e56e9c3b4c448e7e32afb5718778d306a9bcf61e6677aadfd604f15d433a567671d63f43019e94204a550f4b356273e5133f0aa468623bd8944236aab1c5e19c449aeee9c620bd057218a9c6cab1e1bf80d0e8a6ebe48a0eb35204d2e951bb9c752c71acc3259d5567c8f82babdeff387965e59caf39c86c53ef1f400480067edb79b70f388417f09ee989d306f35f8297908629f13459d2094d4c45f252fe6eb9212cb8d77960191d71191c03fdd7aad5d5aa7afa696e89c99d120179732aa8a3e6ac933abfae0606fa0e078f761c1f09f6b0cc64f0f8e167545bb22232e773fd268bf9695837f8ff3fc90e0741b4fa71a50cff68d9e460e851089ca1cf66640038782fd8c80738ec6dc1855f74d0cccabb4d84e5f2af83a98a44f2075759b5c23745d0e33b730cebb73ab9ed8c8c6e0bdeb4996982b13694d66d6bd341074e5b7e56a1466e559807ba23c1a2e2373b6540f3facc01aba690805205a8f11bfc74d4b2701688baabf495d5b87938f0baa45fbac3736f024880386b85009edba18285e07a8b46d7355d246a1bf846934a464a611e3a230512fff853999127e3ef347f88b0c563627502fc3de9b11a4a80e01e14fa6f1673c824eefc0b7d3f1aa1a8549365a63a95ce8bf166b8c8ad282a1d9a5b06f0132059517569a5a8a2f7b534f822c7315536b0a88a930a3137892a6a06c86d2522e013a4eac9832fa08ac24e462417e40a005208eb86b691d58f4c8bc82b1b75fc6d07b48f56854db1fe061ea5617dc1cdf3417179056abf928eef12563a60360d563c9daf969cb9737a525cd4d974592f16594a4375480c2eb41978b41e03fb48a88c0f2daecb9376ef900d4d47a7dbf7d1e3f7e917f889a3b2b4710e595d386b1e1a0680de56021a9487e3f9c613075d839febf48c5c3744ec44fe6552b11a6fd6fc485290145bd831a9497570557013b7067486df6ac1862cdca39fef15d500b7e28c2595923735bcf4e3ab95509bd45e52a4bc3b9ffc38d25d3e27854a3ca8e4e26a4866eed9c6e3604fba0db65a6c91029707e0067163fb52f72abf86deda90c7de18237fd17660ba92fc427806ed598d4a3359729750abf70645f8d3cdc5634d60eb338bc2281bd3710fbccab1247cd3eb7963c9c46820da15cf66e1fd5cefd9ff7f562f4a72aaa15abda7be8dfda8435662052e36ce1d1f4fef6c1111650267f682402ef3f9b97737c5af86db8c0c246ac0fc99c43995eb8cb5b4353079463af2a90b865c2041a111916ce0830b195f0a00874912f3b771f39631836bd29057b5ad4dc71ab517c396cad82e9d31d86a691e82afabab71340dfdab0b2677ae437ce823723902a3015b00edd6639b1d36f669c67934554ed04e7beacdb6372fcccea641ca77779ebebdd308ef4c764ab61f71b27caf467fdcadb2d36bee49f103c4df75571b4257c2cea459c15a2bd52f2c2bd7df629ae20c9ab853f25890be9dae831087d2161291cccc48d6bfd4143d19bad2922a20ac0316f29b277dac55017002327b050ba836131257ab0db76aad90d2a17183901b325c6cab9861d6b593aa447dd3ca07ad9e4658af4c51d782308b1f83e892b04c749ecfae959d68f12150cf309f90e8247eaa7234b68025fa40728422af00ce7d27ae78c646ddb0083eff09f72450b0cafac998427f618cd1715ff8c18ce3205cc47652c2c993568a40345f5eb72779b0004fd86eb2c1764525afe3d44d1e5ef3add97eb3ace00e9347afae4cd5f9511c33f807362e6f855098d3aa38bd3d26128dbd8df44bd80fd95ebc643f328a62e1a2f2421642a0c95a7eac4e85ebc41002fa5cfd2f7b40de7aac2c749dc7b65400625748c463add68c337ea116fe396e13ee1ab6ca65108bc8427dc1ee73983942f75538651245b4b6ec931413bb4c13a6bd514943fd65dbd27171183f6aa2127c8449369d30b6f87c8142f7be3fbcf1ead38ddf0c6157b0df3f852c1172663c8de5823db9b351429a6b176687428203e0315bfde28dec6596b15ed03be9c8b32fa4616dc8424a332aa680f9b53a1502df5ddb13bee822c83ae4fcc2f332e1d3a57fd18a5892b6d5b8639bb5f1813560d19240c3b4810faa1d8bcabeae02916d83b83474920a261d51bb09a8b871359647d173a61cecc81336c525edb5dc713e8d37046da3dc58687a93811c13bc4c0ebaedc5868ae89f85b59da4b02d751af3e7986556a75119e41753d10ba14a1116a127c62bb841a668642b763eaf92c03386436e1be91e3d10e6f83106a446e96a338e2ea698738bf6b8b34349e56c0dbd9d4994f4633ba2962f0cafa978140ad480dd24c8a521004c373f3797faeb90e1e9ca4b3e4b5c470a39ceff250c59248714e0d0d6e8381986fce4c510c3008725e6c1e6b04fa9dfcf572e5f1a120721472dc512c74d2ae082a4942fcb8dca9d6b3ee1764a0f3c4c212a8e30260f3d098575b04fae84a67f97e5fe89ea784a0279fe26f784848a2e4212b492bd6e24bd4cd5c14d0cc2a3616202fadbc1622e94bdd5d7c85f9b88c9a0e8ad273f0591c67ed80e626bb1e9135e137a0a49ecf77ff6cd310973c6500ff9bad5b5787b15e49179987fa88f077fd12e32e9c04cc217b587fbec7ec833d02a131e705868ae52eaa64d83aab95dd497c681b0d2c7e502d371e776cb27038fa7b094286daafbadb96429dea74f4d8e8f8e3df30e72dc6120509f172383d77d235c20373565d566be43eb966f8121fe23cfa3824e1ced9bb1091631be68f79f7a0670446479b462817c02eb3c32172d057557dc3172b12dd868099eebafcea2e6e738d213108a4faa0a7c30d39f5e9552e42477fc0704b9314b14cfdfdbfd6854d00c2d4b08cf33c52caea7c264433c68c41c6e80c51911c70e93f9c6d338f50d19965bbbb6e6a37167c0d70f2669d5d2feb413f7245f90ab387378d8bee0dc4a55fcb5760f235d0687b51b9e2a66cdb9a2b76520dadc3b58b101da6092bdefd940ede63791edcdd93bc71e82ba79c579e631e0760cba65f32417d977cd339660b4ad3698cba07bf385b0235fe192739f05167bc26f23b33cb891db00ab20dda7aae49d8eafb080829705033f324db6eeed74e327b98c736e8d91ee9c98ada6c686ac3e406771b287264a3d0e53095b835c723a4dade439d8e51682676ff8860b94499e1407dc1d523e8640b7751b2a0fd376cc50730cdb65551bd5f64dec28f7179538337346787c68e0ee50a240d563758714b06ec483e2486885d943d65ed65292434b69ad965271caf1f97d62380345602075bc3a41419cfebd0d88a681c4342b9b7753a3634fc19d561ec449598621227c032591e40ca818edcc5dfb3aa86450c4416c3c92a20f48bfd1421a9b208f56ca8cf8e58278c6077a3f10ba37413eb63783aaa089b217e755692f1b0d1e6d7bc4455153b60d3e8bb420968181183967b4b942fe0db86b1de6a7c1ac63dc9c4227a5ce1001950b6bd1c65cc8ad1e67e3d664a06bdbcf820e3b473cb09567a9d6f93bd24be0880165ecfbe206fcc5fa3e2affa5b682337a7c16c377e9c65500a7119f621220c68d46d55e57975a25b5ed45184d7c6e1cd95432db77667cf31cc57f6f2600494c1fe51be80184222db904ed2861a59e36fb1ace1ae02c068250ef9f4e51c2c235fb2f94b3b00f7f1802f962578723dcede3ef035980bccb6d38133c7936b2e3eb49ce18c9f2c0965ea153260e59136ab627c154b84c9403d45cc58e882e1cddc92add480a26ce86b8372848c3ac8aaa2e1c320220b52a8eb08eae20ae68aac994e2a88824a5eb791afe1447d443447c15852a16dac0f102f6ec6cc8832924983f4070cd22551e7ebee86646707b7e3b1d7fb08b816a7f77a68d9e28ff43d3264efe0497057676198446de33f15baecce729ccf3cbc77415a5d64af8d3a0dfac6d0c042ec1c811d7ec4e973049dc6a0eb9e0b04c91ad08fc7e0f4c08df76e33986c8da0c7a97eabb891fd996ff3f02e104b5dbdf9b7831c0326b8c48956b200d8b95ea7fb30d3f345a0447c39a3a5db56a664420fbd6e4896346f4888653f7d72e813cf8174b385feb65679c62f0f95d0fd3f88c4601165cc71f47d43f3b801a4ead5876e08903b2f523ac97733e25e5f5e74b757406165397a0045ee3b3eccf8513ad704a08c732c921aa775c7e36c469d20e97947026abd1778493790367dae22b9d70e04cc6ccb76b5faca3e5d6844e0bc50b7c2c910c53cc872cc5c010874379875a83cf0abfc92e2c4a57b644fa0ab02ee43c7e8cc328c8ef1cdc04bba8a29c0ead76d08963d5d089c0dc31aa4968d634f6dc803b7f7ca42fe10656ecbfef6f7f98faf7024a6d5f5049d3d16a97e23f366243806c58119d2b20188d0a9a209512ecde8fb4d6a1cc3b3c12ea4980a19eb3ccb7b53ad52209856cb5ccf01b73e420ec7e213f24e47bd9198f4509f9548ed496250749f49dad347e4043f6dce0b9735826d4a554fc043be7dd4d722b61aa64c7ecb3fca0eb59da5ebec9664b5c3dc4755d2647e4d41aaa9aebe9753804e38b5028096f964fed9a5c86c68c642a3b04a10df45d79cdc1a4641170e84b07d7067b07efd713c11c3c08bf401cb185ac46b4d4f50d69c5426f180f5ea342d7cc38e214d112c21a5b2affcaab8970480308cd3289c468b83a4f5b8ed822e2c2bdadb072aa5e8e3c1f399ca90d1de3f4cd9a00d474ac42e05295c43e51cf4320db89f78a8a59caef7f688d9a20f42d20065d9a19d3db3c9555da4680ae3562a7e2b612b7d7df47d5e447ba40e166d365a9a4b61cadbf99f16c3b75793b43751df1fd5547fe2fe44ee3824e85a54f30d0fc0f0fbaabd656a87a197625e1724737c18017109960a68f042148cf554615361fb46909140dd9ba5e0165279211b909a8d0eae444f9a099751d0cd60cdfe4b604c26a05850d247587165af630eb2688a9bb0e86a23aa7370408c4fe739bae2f1a63ac329eb81d076a8ea3f57c124d6355b44f35a2875cf390cf4f62a8bd1ddc14b825e4bb18a4fa236a949cd991efbd6651885f9b0caccf96c8e504be8cd53c722ab890870816744bd8ba37b25b59503a92ea64c13b296168c9d25de7104601d726e6559e9eb466d1777e1f79945b63eabf6ea511694d22d915b4313b9fdcb34ad9c2d9d5d450fcccbf82d1431bd991f9dfc05cd95d4fd877473e8b5614859ece1d79584ee30ded1e5676c76802679b69fd6cf9c9413feeeec8fff29c5b3dda9972977bd7c096cfc83ea76475790f04a3de4657ae229b7c225facc13ff83a6599aac01e25a8697d487febba0df9bff4339440462996045cff0245aa5c7d69b0dd04af41d0e238f92be64bfb4624970907077d2029330b864ea99315d9811858d58ab58a404bbf5485161a1612bc47e6a0676e451fbd209cfd024146d456f39139b50cb5f7df726be05da8b039bb246307542316fdea80fd80ceea02d0657ce2028021626f9abe05757bc020cd009a7a764ae70e82a917ad45cd24a193cb048c8d14be9445b63b7dd5f2fc084f309d1bd9b19d093e7b09b5cc927894594ab1a683e0d2ff510e7c414e30036c95d308c5a04f88036606ade109e013e48019612b30097e425c282368b5db048b031f201798095b0193f00be24099410b7002fe0638206686e358edf16bec1afd8d4ee3b7f16df4373a8dddc66ee3dfd869f48dbe46af1adb0dfa763665178128689b75b0dde187220af485a14dd085f09939e78cc17a9c3c7e66ce21635a4e93d39722d798395b0f13e3e7c89d330d967372fa9cb863c6d1324f18be47ee9861b60c13e677e2983246cb71d2fc19b8c6ccd162e6e4287d2e73206724d8f2a17f52f89a38c6ccd1629e18fee1e0486e091b4853770f65f1f97be0ce8c9375aa895121d573ef05cd9e76df3799ba14a82dfc5053b97d38009eacdad67af1c8c53e1d92883a5ffad380b420cbf229f56115f427e85bc3d2c87f7d1f293b507bbccd5667587ee23b807c1d60933f0325954c18f2ef11e725acc54a522370da8206f7435630a88d778e2b60aab653263f866d7211eb82031e018b8231098cdc8962bf5d82a539c7ddaf7494f2972a24d1c04eb3012101a0f3043e2959d42764987ee2f5d1e11afa4ed1dece78d48118c83b8991810f822692371e713fe66b6b2912e3e04a4fadeb90544590d3e7d18581f773e1e56f61641acc0573de12d6af6a0d78a59f4037767d25e33606496cdad5a83d215993fc27270dd6d22ba7927499afe0f3aa77af935be6fa7680965757c7cbc57c42cee0ae6c7833cc01d6b72eaaae5180aa3e852b02c973a85c017094b6d0f05bea797a0247fefa4cc50a5dce370471b0d9f6d465b6d002af78089e6aaba1dd5c56279b79c0357350f8a2eee06d2084140c4b1d805eb670f6d3bc43f1e54d8b391898aae102146e2d10e5e1cea833c895fff409fab2b184d9802e5cee2483544d2aa06975b483180a0f4fcb2928ee5ab0c1c775182f8ba15a882bed8c18f54483fac31f9bfd65d67c7d3c56f49194a88e742f9a92bb641c829a6608558975106ddbb6dda87a00ac68a70d12e5e061d2ee0674031d836b29db95eaf35676312dae63eab2f5d48c9ab1840e06969e395d80ab766927e3475ee314249585a80749d980c51c2e0f1d74d63b9e6835859f47ede59bb2c80814173d427d94d84741945cbb4f7d1a2a3bfd01fa4b512d40e1e36dd8c0c05554378ca76f78865f39458902a6f3071bb5fdbe7cd5f86e2511f5b77b2efe85eff72f5f65ac020091516dd31002f052b539222fa2f84127c6ad675d7b505b1621173952d648942805f969d7e4a3f8bd9cdf338b5f42f5f39a80e50345f3dc5504707b78ba7328379374be3c4344101f7b24e78ca78f6002977546776606c0e46db31b84757f95e10959184830e28ce5ab43250401e3b022f7340dda7db83bbd4d4e81456f6f82d1240da1cb35d077cc185d141b1c7ceb204581407cc6a52c813b618a04c7f806caa8df3d09b04a1d1ba57154867fe95318446e12aafb1b0d77cdc0236af234f7724a65454ab80c144f3775c92cc15006975936124b1c7cd40cd6c4ee3013195452dfa1341cb50228cfae44bd87812e15a0e514f5ca09eaef397dbcb84a35220d686f59b634be8810cc306c03170223c95054b99b8f33c1b14b20233cb3df430a868cb390c1ec34d3644fbd6bdfaa555697d7cb5cb24726dee2f5ada3c4da530ee15a693ed31aea1bf3a5e937adcfb1d68ee301ef9e76773c0674bad3d45a00adafc360214523581fecdf8464a40481cb22d541abd2e40b8ef0c6b2c64f2bd60ac3953522a148321ec7ff786a5cbdda9b39a791e09dce1fdcefbc08bd787c5aaaa79ca9602cd6e0f78b959163bef69198ac99e8cd7f9a8db0ac46df5df67960829408e2fd72ab370d786214cd223374d57cddaa898df01f7dcab64767004b9bc907a697cf090f10447c46b41506910d18ba78b4920dc3924dceff96b412bf23688150348a0d51cffd1d413b1aca1f142394e3afcb46b02a8682fdc88b15ac753d5b6f2163a9e5b1c492861478723700fab7aef7f9c3c7859627491a888feef4943067df6c42040a9ba8c93acdbde4bd03921b6278d39fa69e13dad6ecba0b7464c6b70ec34b93451b05f4c4600098e8b600b4681109a2c7340ebed12e62b301a936349fb7dbf7b0e4e7cd1ba284a71797a4dec830c731b37cb65adfc25e8fe8b27221b9debbc7084b026a1d9f53b0cdfa5fbc066d5d2d97311b27c8ae715085a4cd42080cee17c742163be37a9c00495978f9124df0075a2d8d77b3b575c96568a64a454c20c733bdaa3108d771c809e8d4a577d566f6e071f2e68ce086978f01af5eb88f80c990bd1154f98c607d6944bf6ca5984a8f4298491569d55942a62734836f07b17303723ceb98880cd87f3e0a82094cab9e73f7973ce217cff33510839860ea318856af9d58f16ad101c2a22d722e456f77c5b5dd27c5e22230a384496832e095297502587130b11645065f821443d336a74f310b4add2b779c18afe515f513abb14f1aef373d4f20532f612bce97183c56da0fddb77ece6313092d8fa1fc45bed5fdef6f0dd3b68b82024aaf760625dcff38a9a49c8c74d0cf8e1e574ec4e614a12a01905863b599398275b16f12e3d997c5049550196a893bb4a2ec5f5b6eff2bdea83b986d1e7a94145cc256979b5f956bcd9767498fa281c368248c8ec512a762d76aaef4ce388c4527d7c7b7fef2f5ca4de219be1f66189ec17409a0818efec1e3b1dd77c15a05bc63d0031041439487a46bd48e9d05a352734f3ba923d262d0af8a69ba2b4c1cc58055932742516311aba55c8b8196837e8622e1fabcf89002496e234019ed7deb3440a910209f8917e2ed152aaa86ed67618de87795fbfa3cc32e15aef20c1c46cd599cff0b030dabd2daef1df37ad186a75e2ce2addce2ae876876c3c0b823b30515f434243ce88baa3b9b881a669ab4a75630a774227a03a866ecd04f178be1122d2e99b5e00d5e0d343620347ad070e341209ea49d52914ce47111c809b802aacb43b88bfbb9a568211df44629f642ab2401623ba348d3ca60ed8e619005e1b9290b08e04b2ac7213aa7440a30e903847d6eecd39f7580eda3ee9dce0ec220c05fb2c51dd34725acc8ce054226c6426e942f7f53f24efc8b1a4605fb4e6b86cb4d6adcaa8bf53a68512b1814773b77557d8f6f32caa4370198910a00f99e9382967342e214d981edbdd03d83b91513fdf576152a2e7e70500c63f1ee95962dfded85fa3047526fd03398c5abcf3a3e1f02dcee7dd60d1d2d0884dd6631de69037414374dc5667e1180790e46449e10b637610e78e4600cfcfd6c7e1207300b0e1dd01c7a1923269269c866f8b6f075f3d5ac56904516f9ee9c5937a2002e66a0ff7c9c4c18bc9950c9a489974581e781d2779628435592d6f4b6b998259e910b67df459514a19f9445601155232449e94d723a6f9b92ab9d6727430c99c6282bc79944a43f2aa476efabe79332e9baff489d26676a4ef05f07964008361b60cfb10114efd31a910886d93d72ec3605762f722b55f26eb894e91db206d060ee4ea8e9238b974c465cea1c14a6fb06543629ee1165a34ce39bb1ebb617e96d80219105c3d35e3ddd246e2357a1c893e4f660cea2398632d567c28883d2e87d99baba04af7bf04542c24b2058ce767cc05232b48ab9d61eece5868b05533401dc2ec8bd7682de66ab711e89848bad04f56732803b2c3fd76080f299e7224e05b0a9fad8fc250c608b29be452ed8d8b6306556acf9f389a1270836a04413609a1d1ab6e1dd5d9d235057235390fad6faf8e21fa6fa9fabaff3fe86ae8e16ede74fbbaa32f73ad664d9c495ac98eea0e279ae266b9be1be102fd47c6f91570a91938ee7d3b926e76de3f3d7ac2ace80d4cb2d12a735769c2113368f28b44ffce9649faedaa6b98b7469a2fd3b5664608d66d6154ce85e4330bbcf3abf9d513c40d3d9da9801482019abb7fcef4e749ca616b0978a982d3e9c866bc8dd77f93f510422bbf4a5d47502761436d0d6e951822e396c3ea2f8e02b8c2b14d00bbae9f9cfb97c9d4492166458be0e3ca2e31a663b048bafc4cab50015039f67ffc3ebab48fd1ad68fc7106b4f36dd6f69e6b75264fc11c5b0c791de3ce77f9ba2b7239feaf4a87f7afc0bcced94842ff29d82192cf7daf49dcb93c2028b56ca886177fd293591807e18384558ddb900316b04ec3d623754250071fb8b6af2a4d87d18fedfdffb807cd408cc80f380030762e57a05d375b465a73f5fc798c68f54b4e809a4cc519618f7af1a468e126ba73d1a07906addcbfac2296f0d03de079189249d13c7377b9968a38ee2e0a2f6413a0c8df57327b2ba920f7595cafdb8fa79b58ec9a0ab4f5604d95ad1f697ff02e33c5f76e615fe588546fe0645ba6d1af5b6ca803184912f778b0db503c8e9b44ab8f98089b7294ef0ac01a60a4f1187a34fe4262909f561fde3d3ed1e6a5283c15b9964b198444d106e50998092d80a547d492e12f41a0c023fdc80ee111c946dc629081a3cda10a3b1e85c424ad107a4f6dcb99cd9811e6b225721990f41add00ea8b35b85387e09132f7e05e3a0ac49c9362a2a56b63d10f7c511b4c20250e9cde6d876114608e5d05eef5fef43caa203487dc81f6b805f97c80a0569e57b159e399600614a1ecb84c013930e49a7f4a9294f5c0b51e682cdb43676ce46f299b344ff2a8ec0b8dea68df35a3b10d5a5498a561a9f73f8679c7ad9fbf213321144d11ce971fdb9001581a8274a7003bb88259a7079903779b33dfc77dd5e2c4e247448261498158c4a6e2cb858a29cffdf2449b5f66584447d9a0381680f490accacac57bcf6c0ea0815357ef47bf7427a59d0de069675a8c7772d4ca9b6f6489b1efec5849c33ab7fc125cb50656e7ebecf740ba9ce542581c42a27bb853517d283e0160e445096a44577905f4b75056f4f86ee24e71e7a26fcd67309053f7a27cfd25d5e25cb7e152b2593e52db08390b96e867ab683cb6bdd64de1fe0f3d226486d5260e21c98a3c1fdecc460aa4589c9c7e2fe5c773c307d0a126c0ac11a4eb59e36214f7d9cf588b5da94fb963e3ac5e1690bfb9e794fa207e2a37bb1c92da3e0c611e02c73c9800c8abb6e3e96e9914e449d4ef6a1f531cb9c04b8bb0254dc6b99b33726e85d243dbe6afe96bddbb7364db8f90ddc2c55c56b7a8787cec8f99c2fb3016637cce6cd02d4c8edc45dab90e92470757516ab626ba967a81fd753c543e85630b70d7af61653abef4c880877201e7686d60c24773bc15c9a8fd5b4a2539bf3570ed1794069d31a044eaf39408a99bed0502aa6de4356809527399936eb60edc68b1be4921f5bbbd45c5ed17540886310de573e9024bcd290724386bf95602391dbf5c2a3b3c232d9d38a67422c80dd2a46f04b2baacd4b89c52986a90a5bb3a5e41740331f57e28f4acf81b60769c1e4302319c427dd1856f10704a8457ea09a50d7dad41ea1b7cbfbd428f3c7c2baffe07804800bcf8aa33785bcc42c8c910bb16217f466c237eeb8c9650d606240dd641329c2c4a641807a5cf4af5c070b261e4e3b3db17af687e2f946db01cef665837df1cb5ae982815b0e072ccc5eb6ea4820a553460be7344177a439c41d4eff30d84d8c69b360cf1dcc3c571b4b37b685ad6af7e7f30a7cdd4bedbac884bbd3b2d6eb4dc4d68779abbd387eba064596bba0c1765171b336d0e6c69a4be90223d0bb6f287b517b76b7e1ab65690b6e9cb6c2a71722ee3be095a78b3d4187e205c97e13b2f9212549ad7ff06a72ff5d0c7755feefe737097489ec6006b38a3414c8a8993f9f10bf5a5a0e9904edfc24584a06992e6de2c6738370cce0ee463df57aa4b77a59aff5ac9ed54fe137e5c6bd996ee8e6546481d5baf16fce1b74e3bdb6f7ee656f0aba21de6b1c017d88b85367f14f656a5b0bffda996dd180b98fd1d16854290a47847a04939bc1f9f6081828da7351104faf120eb2338274de14f873d7a7a969857b31957f1e323c82184b50b6abafc0b446889e2712db8579f42dbc752eec51e7a1bec5de6a4b7a27f678dceb3128d7b4a897e49e75b84352cf4e3d9374aba3be893def7c9f20fddde25ec7a05edd92f6b385b3eee562c585c2df2cdf2f41fed0d2abe7c2e2fab2e297e15907cbc50aa3f4e3d27d89c21f593eae1bf0acf657c5ab7a710dc14a736771e5cdaf90c060a5ce37dc5c518c66dec5952957538a577ba599cfd2cc4bb43206654af16aacd442b19a798b2bb9a8a614efed95373ea3999768259a942dc5bbb1727b60cd0e45375756db89b7f24a54fcbff2e8e8028d9f433f905edb8b0363fd5fffb90dddfe47466656dcf036f171a95ec15a377913e0e325eb8a5517b4dfeb54330b3c9e039aa3aa855b3052aa9a74ea14ae59b46ba470d6de23dbb6e9045638eb56919daa4b16edde55cadd2669b5a2c429a0621a8c1a841fc2100400529608e28a8c5c5e1ea20781e27fafd7eef55e12c6bc4fe8b1caae37b3b6d612a2d636217b6fb977e508e4071a08d3d5c93063a6f70428304134ade323c3ab90c4aa726154ac90fd26ee31c4ca02051e68c07c56a746a353a650a1d2f05a5792fd581347f922b613726c3134193e653493647590c28e0c9f2309f6abadec35b9d845c49f179b712d57aab7c34b21c37755ccc8d0c464f8ed03351226c3a334f003a10e0a199e7b7951c67824938ee24ffc9155bef803039213421d9ce10f6764bf0afbf1726e86af49d8afaa60049d3c8112447342863745f9a123c3bbd6b0e172d4ab65b75981ab11486402f6e8d33bd12cdbddc0cad938f1c795e1332e70b82a71e28fd59125fe1870f3848f8ee943391ec77aa932f48c92c68606f6822a00e00a226fb644596baae596ba6a3031352bc4d4acd082878367430cac5981082d980183e3f52b9b34934930482f47469c0d334a22986143110e36dc60030c830866942237ff2cacd79ea4b36389b2560abb59aa5a666a1f1d4c6c73de4c2980f6726d75777777b79431a3b3bb71f79d73c6ee8ec51e514a306c7c3f4208514db8bdadd37bbc72856d430c55518ecfa109413e44987ca802473ccaf111c9a7a5813a56ee5e72779883dcbdeb7e1022773f80e1451047b0c1858f4f8cdb5879dba8e46dbb0d236f3ea4206f3e4d70d00417b090b5c336d0c828ed912965824fa64cb041a66d84e00a0ebc9a1fb478811442190abde4243cc9f2564a964b4862038d2ce5490a1940b104a38d04d5b26125890c18174c36583093e784a922724ae8000e9450865d2204f2473a401cea92e59328813ab03f293e225ff211952f0db046fe88c421007cc93560f2d540470d24a5b5ad96ecb70e2c0cac819747f2104e2c7b0b14d8efc740a687e99b4803b38940e4696d8f1e666eb6daa3ad4ce7040206eac82f85dcba17038170b1c7bc95f1e6c09a590f03759460a58c929bff4e799e7042ab65ed5773e2ec0e869929868135b1f4ec26f172b64d579c2c5cd77ca90d3b7f8900b9d820cc566126550b2c0e7d13719ba065467b1ece0c4f3ce95c99613fd86bb5b6d592b03c3529365894bf24b0cab550a7b521ad8a7603ad8a5625571f6d48aba255f1628311484720df92675865fed9dbfb0f8349b0b6d24674a8854025a5437428d3a436a24374482bc106fb86cea6d995f436195ac928d7734a49a59452ca7e46339a6536349b369d4d2af31349da858c8c12d205ec66dfd992ba9c4497a3f6baeb4f292b97cd3eec322a3749adc8ee6e39a7fcbc72ba9e645795bcd3f2ba1c7541a6eb8926a59cb3bd98156d104688124883d50f5e6dfd633769ab924e94ecd47c581626d8278cd504ec8985524694230e803a992764e8c29ae8cd63aa0242424242a236dc0013e20398902a3f5202a56f909090909022ac616541ac0c3c071c5720f08aa214a1a13caf0cb1c168a9a252b56aba9d97e23830fd8a515830b231a2318ab245571e08a2427a91e50d07884068769a8e3d22cd545f1401348f4304d234d3b2c841ab2a14d4920cbc52d5aa66da81fda051a402eacca01ca240b1816ed92dbb65b76c198dae1a72c2e0c0e0ab6f61366258179113904498bf885d432ebd7bde943833d6dd46466b7777d3ae5bb7d464679dcdaa6514d367df289d31993541d5ea49fcd25efc8095313c0476ce395bad5ae984dddcc056fbe6dd314653de24d7a5bc4d4e526ec21a9ae54de3b2ca69336f25aecabc99b8d2c699386eeb60e84f53a96ea7086be067d10661a6076b20aedac4301bcdfe699aa6497a654728259bb3c8890decc0892644232072e287a8893432972191134300b52a154750031809694058102106900d8608030968074700418a15238080046486116140e9170c324018525a5fb4b42108815404243c1a08f10321bc60d2da322412628bb90358cb22592003322db0801416f0895181dd32240a228d9717b6cb902888a3208c0e932bb89665190f8ca094d23190fa05c748aa3efd823ffd82325158ae06444dc0c0082b55e4cb5b020bae341124234413506cab89272e2840c05a9adc618433a0384a810704bbca70e88c19c83439a307af32ce08e3c76e190e9d518433d238425249ea17f4f9017232001b3ca12f1f9200449524606087164970923d20609905329a65b406233489126589295714418a200769e4e00a1499cd3ab0840c5c800552e4600b1870f144e3828b2856605944404de45386444045e4cf7e8600a21184d08f1292f82162090330f1092288688827800c1044510928853434308411c4f8c072190ea521254a1a53764c61b30c87d2c8c11a6914e5c0b41ae3bd980426d7971a6918e1215c202221a55f5068a84a919101c038a23979280d339a20c1450f9080381a52034a9e191201c144fe9e63d6c46b85268d6a9a0788d08630541a011199107caa0a8aa05682b42b8698568a321a1021855045be604dd2c18f5012183f0308824f52108418c0109eb4981cb1321c4ae3072220903a0ed4564ece6896d11d32b02c19121921450b234600c508275e51ac29432223909899c2d60c898c18228527b6942191113e438a48aa7d038980e0402bc321352c906986444c9068b083ec8b21542a08c328096900d10408d90b8260d37082524a29f5a10645449c0f42d0807641240411911055e45586444240c9c0061be76bd958637422e67863582d43a21f1f3c614b39466984221aa29f2d887e84b2cd90e82708099d26c020065460618231b6b002762101318020c6154a88220650aae813642ea0598d28d410418449b135c321353a60a5e529cc73d639e9a42d3bc3f0cacf4b7b764398bb7be291a452ca86f204310a6a6db568b4c9df12aa840f980fa9048da22961e3db4a53ebc369b610c6186b8510c28cd65a82b07ea74bf7eb2157ac69a734f6a8a7771a654a9f4d082b841aaeb7f488187494db89510c3a3a12231e5589c10e9c32d4ca964a14231e9dec91ccc596b5167f47e8e930ebd262eded6157e4845b495aadc3c0e9dd5bdd3bddfe74b256d5a24552743b9db6abeec7ad4edf4eaadbedf6ba52e75028570a0f3992ed37fc1dc991db56aa956d657bc2495a79d5b276b3276b397bb2f69e77ef301175d90531ad5e254e8bea36a53aac3dfad445fd645d4e27a96ab5be242d7b249fbe42a14ef4747a94cac4fae9aeceba4356ad2399a6ae32e1242d4a8f504a69abd59242c3913c7f4f4669e960043a20410ebe324116ade4562283f84bc2e5c943f6194f5e6997b48361e2af475e6d6e938bf2ae3e6f5744c5624d6b97b4ecbcfac7d2e4ed3798b5c7169c211e8b1aebdca7655d6a3bcad5e567afaaf638777ade263a224a86441e18ca5ff421f2405046d939ef1019a1c6d98b8a1135c48b11e3af872c53af29db4b7872fd112526539b5a08cc8d75e0c94b622e9d967209efe4d4c07ef028d325ddf23e7d9276a1e5f44794f0b0b9b0d8b05fcd137a347449cb9123fb459fe8d2e28297b476a54a9c167c8408fb4558e4431bdb108158ad830748d954aaa1d6fa392b2cbd5edbd0b0255c85fd603089e3daaee5489caae10a68e7b473d8b51d265fa68a2319b0c6749369337da7ab15ebe1b767fae5c242261cd668d8e9d4116ce9dbe72b761160bedeb56123f334ccbb4c97788a02c0189379790ee7c897e93058637a16b604435ac24876d305c2f4cd74984dcf3293e9f14a1384d2643242c34ddba0098394601f83ba54a511cf8305b5621739d6b05f0705b9b8b05f0cb22252d12e98b029c3a69214b2aec0d22089bf28f412d4d2500029cd32186986a70eb209547b2624871cd8c6a15a39270b8311c2881f10a3917cc9d2e3c53da55f42edc94fb902cba47fe44b66b87ddab30200f892bf425a6e47ca1848338c0e2437f87a81a82fc5580f733d84b53edeac529afd19968f6cd8cf47218993235ff252ca8db2b0618d5a48ee0c657767a8a83d9823cb1af68b463627e7cb89460be897bcec97b4f25808b6d7b0972b38326219adc07eb625676715daa8264cc861613f2e77e9f51c9c0a308a9beab36f56577d536587dc94df917d6b3abdda7b755557a6306c0f6733645f222f12ed11ef40b3499b37562b1c93a53a0baa57f2f3f26ed9252b6a3ad4bcc34b86404fd62e85c0ace1782a6fbab2934175d3a5c49bfdbc29752d85e54d513b2755df698be52546a2ba09236149bc446615ebf5dd11c95818ce4b16d4ea73b58a744523356997a7ab29f134e16fd219e349c618a3f693fcbc277b59f1d7d3b7f6a68baacd5a5d1586556ada94f3b0a39485e55feaf5a6b7ee57b36bda670c167f3d64fb1bf47e4a3255f19055acc3c669b1f0973afdeaadab24abf08704667b61961d0c31b3e01d2646328964d7aeb14e57afa7f67e3c6493bce9daab6eaa3e753b6b37c99bba260cafd99fbe25d2528c7a4569d9e3915ccfdd8f875c67d681874c7db2e9d9e9ac37ddd2dd81568c645ec21bbd7a02deb8c2de6897da391b141081c01ca3ea281c2916d1135572e9b36bd7ab7a765195e5d46ae138b1a42a4b6618c95ccd194a574d24594eaedf2267fa2667e3c0588643eb829c2e0fbb19703cdedc48bd85e5b55e53ffc63c0ed527fe9278a8cf241eea15856754c9d093f56c62d7eaf328d5b3ba425d7595eaa2eab74e6567611b346c868ba48ec2486ebc9378291c6f1029ddc02d38585866df49eb1b3559e6e3a90b72fa9ca8393f3360513f1d09d667f774d6955d90d33728a5f4b2d56a7559575d5befa26ae16869c1d1ab54b7565917ee7476d865a5a350cf6cc8cdc27acbadc471b93cb1d81676d9e17ea96f6f8c2423523a0a6543669d85bf245e5f55cd70afaeaa73bd5f0a73a89f90d4a3bad6faac7ad945827ae7c6daed4e579851f7f452a705d990b598a9e6d26737a41a3520a7744bae971cf339ee4b75f984e4093d221460828a10a0e4d96f400e8e6ef96122216501e3d1e4c05e2cab8f40caf4ce6d6b1df6e7e4e453a491461a69a43021763ba158261c2d2d2d1c374c2cac154a754ae570ddd01423aba5595690f59de6b8b3dc387545b673f61c8d34d24823a5f6f4598e7e3b6d1cc77d5ea69c7d8a3e753f2179bbbd9f907cb2dc763f7a14b641c352ccb19c4b92f3519c2427af72b8961ba7e75a386e64f6593764ce701a2267e04ef614638ca79310976ef58d863de72454876c9556d1234eddbea45870b4b80ea5cb057fb505e3b8815930cbf330ea2f3939a7d3e92424c7c60606a3a1f13c1898974c5329fcb5562f2f17b645a55eae4afd2585ba769b3a9d8464d555f84b92934f9fa7f77e51084d5d6b734e54488ea739a79c534ea6393d14010911410e0c511221c892e505dd7769d71064e8eeb6e96e9a39a5940d9bedcdf92d838778d2b477f9c93cbbbba59415ce43082184b0b1b0f15edf05c3cc9d638edddd52ca150864a073ce49e3e1acad496e42fc13b249a79c3383c910692279341952da40e69473ce09e19c734ee9cd39672c452a71b43e73ce6932ad86a8539bb46693d60c0b4d8cacd6ac0d3a6bd6c69c342b514a33adce4cab6d64339b5ae50e4b39d59229a514cf9969b50d9ad579941dd92c7bbc966571ceda4696c11e95873933b9711c47b336b6d66446a306bd5a67d775259ab55192198d1995117758946a7d9c8f5aa4996cc3e462b29964367358b0df4c6c3dda68464a6fd9c41efdee121482d58ce4c4d2cc5a3768d846ce49b312a534d36a96d1c09bec309842f7ade3b49c1cac65da2cc5eea62cd8a314a7a6695d106d562a618fd852cbb21964ce88c879ad0b5232c98de3b8ada78cad96a6698f734ec945d3a4997c74c969c51efdd3a90b92fd3425ee6460e3b78823ac291d962e21ac29cd5caa9ac45940df007bf47510d0b2a1c8b4997c5c6c6cd94b86bff0a646afff132b371b6736246a398116db0c7e3a289c0c32ece0940c5b9e6d6db0f863b9521619ce3bb3ec5ad66d935ca9656545412c323c4b140875e0155d90599a32058532540520c3774d562e4c6929b2df64e9b490210bbc01c4494b0cfb554d27c7aaed08800c9fc30cfbd5b354605d707060c07593e1cb0a554c3fc86153e558c0bef999f4c7cca6f853653287f98e0bbb212fece1af872cf3ff5b22730a7fcc85d94b017f3d5906c73ce6dec3dc2f49fe51b81f0ff99f401267c624fe4e6f18dfcb7b7859f2c539173604fb4da01374c49e03c809bdaec7ae4b629a14a357fad52f2161a7933b86cdfe4da01a76dec265574300ee32e2c2f333e6891828238a50cc85f7c476e4cb85b9850b63aedb933f28d4ba9f975db838e251cb40c3e2a359dd8fa5fab4271348ca513cfab8fcc5a389c50368fe68527f60ca03a894dffa7ad2480b6d5c402816007c172e8cb57061acb5b0df4b3e19a2d003d703a45f58e2b90f757ae4fcfd55582925cc95dc3832347d03899c90c102bcf92a111368e43e0b5e041998729321511257f2869af09008102726c199675c02c4c185fd9a8ceda6331d8d3c3d2aed75dffee263589c21911256e4cdf428c4952ec5e50a4bcf4deed18af6e6e9277b6dd0b0d9b9974ea58e08fd40760de17014eac9aaabcd60061f108aad2e8cd13c69c8d917d608eaf4c82854de2e045ff35ba954aa37a7944da1de57ec65b5e74a61183aa7acf4913f4aacf0a489932c63f3a97b031068dc1b7ca4dc1b7cac189aae2954faa6bbe89b2c0bdbbdd59948891f64d8d1c0e43a3460fbaa734176dee83edf4e248eca9a7092770343c982f1178b2e5ff47da13cb5d0679b08590e91e74e9f2ebbec27eec6a1f66646e9bbcacc0a2091124ff2cc3f4646454748502091123e793e5e699c199b2c325857a816167633c8dc425c5724fa904df1ca7663924b8cafbf7081f5fa2f096c521c457fd13730cf2efa665af1a34a1256a41c8a55da9b309b88058c859cf626dea13312fbeef371a86fece72395be41f53773f7ee1bdc9a333d93c25dee54a8bd69cf5dfb93b53826a16ebcd2836c833a3023319dfbec21cfdce1ac339dbe9ba1c3a58b8496cec52f2ea1444edd3324f45c9472b3675be4b8c89db647a510d93e01a3bc3d0bb97604b0c66422c385c0a13cdfa44a864419a0424786444d8272ed23230b54f90132f1404a224a1bcdd9423e453f5434d013560bf58d8b0623edcd33c9f3f0eb22f2bc227f2ff98b4938cc17cd204b398b57fac5a47f7a3b71d95dd4549a79bead50c9f33d7718f9f8f8f8008990b022e94a9e9ffcc9f3891a59e44f2a416f2a6531a96fbe5894e75d70664279beae31b90d76a54b848abec97a32ddaced8264575dd9d1771d91e893296e9f7825265161a54fdf449f3c332b4d46dfc8f94eea9b2c0679beaf40ae0b925123f8ea8250a23272868d1660cac1a8e298c48124141fa9c2da2e86ce3ac4256425b0665e754b5ff0f0d0de844935f47970a804c10ba1580f2a2d84388a4d91447b16a970451f1f2aa21441e30fa7ba5b1a484231d8d1eff4eac258a4a26f287629311b294f3aa393f6e6a3933cfb531e8711c2f089b5d1eb0914d4c9269d51e66b03e3d1c06ce8945067c6676159edc5576d6641676c1b9d53ceee081f4307598cd0dded75c37477b794db0fdb766d64f2e7f23e578d5b6c9b9492b694b3bb2184d0e64e09d31ebc9452ce9b29e74d7777378b1933c6d8ddd9c60454d671db35a5a8a9b4954cdbc695b614544d2ab1a026163060177a3108c8b22cbb1a8dc0d4b030418ffef480d2cd4c3b64f938e98dd09b593725b69c34d36ac984ca3a6ecbc1d40d39caa6a8a9b4954cdbc699784add90996ab5d412947153c1958eb449b5504a0f4367162fa66cd8700ef98a93cd9291c5524bd544e965e9565abb212518fb5431a52c5a76ba32bdeede012b7f704a6b0651b4a1812a489841132bb17eeddb1f7253a2223090e5a7a4d99338ad28899092270df294b80cfbb5e2df3712a7c2e25b0bdb928d484404144ae1b4b48b4c7f8fa8064e64d99c19064174812c88886022cf4b0ba02053fa6eb528a5525079decce844885ad9204c865289f8eef21b0a36b0dde3eb86c21636c3b300e35ea49cf234f9949d7e3a9d8ef4cd119a4ef58f85b3faaabbb0bdee50da0c7f47724881adffea4dff2250ed2b9111488c3f59266529e31131b4296fb0a87d72761c72b0a1872f720f4639fb7a28ca59b7d3ed451fedddd50195d39d7d3be12fbba6e57acd27d767328ad4eb5d40e3a4b425fd2acd9c83c499b1aaa484848d4eee18361e0730723f8d1f2107e1d45bd256354dabb5d67af85a6bad75d62adf46d0d7d80a81e54037c9357badf3d5f45a7acd6a56b39a55ad6ab5e5663a91984b47b0116eb29eca6aba34d55a6bbdec82546bbaa9be845bedd563262c4ccdc0d0b22c05b046fb27a59c00f6d00e1b6a34b0466b6523ed69dfe917d92f067511500766ed3d05d499d71ead0415c52fa25133c9da95aca19135326ab536d33ad6ab430c30974e24e61aa98841b046bbe90856e62f0611a9efbeae7e4fee21f55ddf93af4bf3b2a6bd6a1046c3483a886eb141984f46d3112c15ed49813a11eab4f27c4ba9711e28fe54b15f0775901179be89140ee4393b0479ce22604d0e1bd8f8af6310ac99e768e267add606750e1227fecc39e747137fe6eb5733fc593a23eea22b869d8f41918ac681735a213527991af66bb5e47560fb72f6105bced6b8cb69ca696fe65285cdcc46515f7e1ac9043ca49f33c2181ff19c734a29613361d30eafd596f059649b854b1a47083d7d292332893fdae96dc0a16ff2b6c939bb66edb65cd23b3932c86e4913994c91357cdf896ac27e1d254e429327a641c80de050922349bcf6280d0f4f90c6dfe4c951f69671a5ef745f54910cc7d64aaf9f37498b7cf759fa2cdadd8dbf1b3fd21569ccca6296eab3342e229f1599d73eb34f9361f5794de2c8c5db1fcf0ca8f75177a1f010ab952c863cb95fe5b56f99bc0adba061255ed2a60c23493deb159e384bb57cd62d9bd5b1e3e4ba23ab2fb9ee2c4efcc943daada4edbb7fa4af927e92f6e83b3db26b181b84b19fcc9f39334a5bbe4f46c8cb1b833cfdb431c3fe30899bd5b2b5c4818e4a803929aab0cedbb9ca93ce3927b637d0c70b0ff207f3923f1a14963841a66f7d8c4c899cb8608923583c60bf7a030d00654a89342094e9bb05b1408a52831f2b404145d91414f06056cbe5d01c019b1865589eb9c670c6457c10d30ea14f64d84e296c1499cfcf1623955fcd0da5644d395bc258fb2666b03dd93489ad5619bbb9da77e609a314aadf44ba34e37433510961686c5e28945f6cf4924a29a59cb2b1946163d19523c8404198ad8b36f1944a7db04e75ab92f1b46948d8fa79170cac2aa652e7696197a7700bf3a45218d2ae95da5c6e4fac757b622e41628ed7aab575f26a0efc822d8cb52e8c9d7e52dd0f16a9345cbd7e49a011068a98f205ebdc65bdf6cde95bbc2c1dfe7aa06caad4bd9cfa25cedf72ed0ac75d375c5c386e6017015a7f790d2ecf812bd07297bfe00a4c61a4e52eaf80ebade85dbe4e77b956be4e6fb956be526fddd6679363927853a943a39cc22cf5f67eb08b7c8268a4bed35beaaa3ebb20aa14fe6656d9afaeebf4d5a395eb3adde52f6f5dd7e9ade7b8ebba4e775dc75baeeb8461e051866df9098f462f9f393e1fab481c1d9fad4f97d5a7eab3e5fafc093a72bcdc54aae59eee725bf7e3c9dde95d27bb225a0ad7f6ba4fd995ce42dde20f1a65fb6d83028bf2e9f5d6bbbc27d6faea56e2b854980626aeb7bc75971b69a9cee3c2aeee3d312117ccd3825ddd7c4f2c5691afd9c29607106c8ebffc84f3205ff3e799c288cb5d7739cf144674bc75285f2d98670a232d3f01f3b4dc8579a630e27a0be639825d304f118f10f99ad7818f84fec12328a70bb6f2650f632d2acde578e9c9b09c041659d4d5eae95b6cb53cde8a0c893cf0247f3143220f00e59e58cbbcebb6bcf59e584fcce5f6c4b6e65cb727d6727b622e37c65a2db885e3c9f4ee96be23bfea1a1291588dc3044c78518426a18c13e209e50d7cae0fd7cac9adcbe43241800f251f175a38fc76f9eac462219cbc850b633344171197eb30bb9eb9767cde35249b3962279c4a29a594524a4c23941c3972e4b8cc711d394eaf0e281ddf6989c216f69b4a344eec32c05c73d8ed38cd611784c7bb570c8cca9c1e7917c9eb3043188cb91f4da6df646ee797fb45a21f2b59c7656afa08befab5f6511fa150a878b4d91c7641fe83dbd1d65a9965be513222520c71f3ae87e311ac690e8af63428da13584393b2271914f40ad499aad3a45bbb6dcf41c57eda2aa35264bd7bf8f3be09e4e32426665d0c32cb7ca35b0e1bd8d3e351f7f2ed41b01f4d0282afbecd5b2d2929a4d0accbdc06aa3505e1514ac66ea35c03f50d8c3906b79356a2bd0934a5f44d6645df08411deffd7905d489691c9f4050a7f51f38fe1d078e7f15c7eb61c4a142f2d527cfe5e3f92ddc85ebf2811f50c3ce33761969c1c7835c88cb078e22860be02f3cbf2796b1eb0139a74236f807feeab1900bc3bdab09c19a7eccd5ae006279e1c2f018ee0b177235ef76197605e8580c587bf2c2d5a0c070352903fbd52a4faa6d5581817d92dbc763de9f494420548dd803ea7c22017500a09d51b54308849e71d8d5b07397911682e0d9c27b62d06524ff85bf8079048067c62ecf054304c0231b30108a452658098af5c4a6d88f266dde8579fb71e58f1f3e6e43711bca1be7eb5a46284ffda4f336b95f16eee30279ccd5b97763a645126702c9d71911084da26a4420da13ed45b4e88cdcdf00c0c2ed27b0a60fe43614edf575f2044aea6b4f2290ee40ecd11a4d6a0ec09a06c085f9476fa081daeb9e026b1abbbce7bb30f3a556f40afd88a9942dec179de0098499b03782e0a4f0afa270085bf986cbbf13f28df714a813f3be975957e15f0bb30efb887cf559ef22fac6c7bdb7cdfb50e7c76ffed9dc7d857f35775d8f7f9d4f873918763504e02e1862f69ecfb34d198a65ecc289793e0f2c0c1c14c5f25d784fcce64ed81eec5bbe3cf18735520014eba19e119be2df7e7d7c8b3f5a602294f3a54b10e113f37161daa38771fda211c80d5e017f35f7c03117079c041c913834fda277a187c1f4f42908d15f8c8f54c05fabf56da3dc0b7f31bfa4e7c93cc7635744c765300c33af8bc47bcc3bb286e63cbcdb93775c9867ee47e3c275d510801674f02314e371e13db06640149b42e29c980b329f919efcc743963bbc1999dfefb4f43c997fe6c2bce3fe97b95f4fbebfdf2287a5bc0f791e314efa464a1e972a7c02f9b8845d7eda5ce2efc72790c4d389777aefe27bcf3b4cde07cde58ecbd3ae08bd5764e63318c21a19f997779a5f7632d47ce6927aafe3d79534a7f7bcd72576edb8bccce975edf8cce90e3c44e6339f91c1de77a4773f99a3e0d57ca729cdf13d8ca4e6331809ecde775ae62e91d9bbbc5f0d7ec1fe7747e4de7bec86c03054012f91d9c7e93dfcd97c3ff01299799cfae0c1437a7f7d7b5def255f3352e6de5f3ce469cca5f45e9799c11f95afe9c48977893f27f2530999c71c097aa737862686493e8f6fb18361067f3d7966077ee11d352c9acfdc14bcef348ffbd5fc7afdf5172eec155ecf778a9ac3ae480d7efd15f37b1849cc656264be2369ee4e7ba7a7f9eb13eaecf8ebf599d35cd8deccebf51e3dfef1f83ff31d77c29a99d7ef7d7b5d24b07b19995318fe7ac8b0abf099fbd93c739afb29c934df71d83731f7b1037f4bf28ebf5e7321ac79e18f27bf7ce04f490c0ff975d8373f6e73087562eec3067f3ce41daff98f0bdbabc19f92fcf2813f9ebc037f3c4ef39ac75c086b6af0b724bff0c7e333df9134f757a1e630c37ae01d62e621e36124f432f732f432ff8efbc55c739afbf19067bccf5c9ad7dc1d34f7df71fb93b907bb22afcfdcbb1f0fd9fbeb4258e37de6e55d99ef38cc01a13ba8846d979435eb149140000000000315000020100a064462a140249808bbae7614000e7a9c466a509c0bc44112c328ca20640c308410020800c6001821a21901b02068fa44c7b603af965f4e0776b3e92439e6ee0df3f9043d0ba2916888a315e6157695466e2f4a16c7177c6a1cc7c508d3950d20e02481be892c2660f0d4ef419fdcc044cbfececdc2511a38f95a2efe2825a2e2ed5e42266650b9a123184c883a5985dc84eabce2305f065d9498a27bbbecd7322f50b349a2c92f2e591998dae67ebbb9fb5a98498346369818f17daa06af4ea3b9e2e3ee3d1b2f9240adec3664146f8bd6d5695ba63485d14ca530f552cb4c6fd15505d5be01c3bc03cfcb9d94e9788e729dc460dc8fa174560f28a237f4f44b96f716055e376589a1544f00059fc8abca1c349aba8ee5ec02610197c6f217aa1735a350b00aaec080e719a366ddabdaa7ae547d03a1419b5dbbe2c0c01dbe09070e1549ecf5f3cb5f819cfee76ae6d199e2757e38286671c5e0f6022810b3f32a86e6c3fc6a4e144ec95162ec4bb6bf2f85ef10518b454c17488d565d4221ff5f65c52cacfc016970e7b3140ee3e25f4865c7613e5397cdeb5c4db0ada37f9bc309934cb518f52ed3ff0146e557ae35776bd7e3deb7018a63c34eb7b4c1ac7d263d249e85fd60c762948d33bbcb8ce8946607312a51685b29f83b344c73b300da18ef91478956b4fd6377205d8c0a72dfca52cdc7fe47a5e80415542a9c802c4f94d679a9716dc78ae3f102c1653b2adea0e183061f3752ed685a1d88e2bc25dcdd565f0f2a1b81964c5adaeb4266dbe492c2269614ed8c80b0fae7642654bd006fefae302f749c2c96a2d873b9cc1786fc2e482c6326fd0c2e36e18618dda6b4aca0597a408a3f78815d2e9a00590a481de70484cb6f8d6ba4d0023d6b0c22a14d5876ee92f73c7b703c28d6ba62c4a9beeee1ec4a3e06d529de1c31e19c342dd879ccda2ff965a389b5c5ca79d834cb5fff56df25967095f1ff54a70d53b8983f5dd55de7cadaa146e8916d286eb4833f1d1a123bf24e39e5db4936262573fc28fad5369a46bc26dec02a0621ee02b1e13f7fa5896ecf3609c4c8ca5fa93695324f7e2d138916cf3f7d57d070ff0a99d71ada9e80ac8b9cb366cc399dbf0b8ceab1f55c7befb385a686eec46363e992967f1ea8416963410bf4f09ca5601112f1a0fa208e779d3f869a4493b048be70835bd1f6d6480cdbf0f75a27dfd58dc3f32ada23a9bd1c5b1ef15fa442cf400bedf203dd468bcb41c911a33837d941f71a418a2a96d3daa7e235fd29e71ed169f77381e86b1b2a5cd05fdc0896966360e1c621ea85e291ed2810410e98916b25f054327e2d7a5ed50d5fe8f5f3b5a93e52d6a6ae004b79c272918756ecef181daeb60355d748f975a7bad227bcb252f700e5cfaf9ecdb6d44b17144c155e2ef25f6eefd6fa166ab5fbec9900b17b4a9ac4313f0ef70c1dbe26a7469db161806c76f41e4f67550a101151da22ceac33fdfacc80fa9db3da808ad6479a05af102851e1e1b840c572ebee8f06eae4a6fc0eb4ac65f7c93144d98fec669b216d1b140b1f974ab36eac82fd0e1247b03bf2492b7237e6b7412b64f566d79c3ea4ddcd0e41013ee3a51d10af92f9a2771245d1d39a4c4a1b8858dbd38402f77427f1fdaeec5575d6dd6c5fca8f9a092f760863dd6e2b4af47e5f8e6050396de8972cd01d24d2bf9aaf82f4b1f9b9df5f217a21f4a22244b7f4f0745a3d5250e36ba70c5a592832e39960c15793debebce9dbd577826d60755aad603239865e8e4cf58851cc09ba5c8e6a2581ae77dfc27739f94d0cefd8e5c96bc4e10c3b253c4881138c0f4baf9f441e5c16a46130a90125618ecbb923494a6c27ad25e0d7c683706c3b9fb186370fb4c813e580ab3da85243d3b67d57b34da8f1b473b214806209dd225ecb78840b1c15cc7bffc78cdaaa108af8ed90e48e9fd1c7e6184bd1d328bc8153ca03bdbf3c2de2c50b60b5d06ec36080295bdf9c1c01fba71ffc84c06f67102f109780e0aa37f88b7c4e105db150b328c4fb0daaad6fad8368cc0b38a96dec46ec059bafcf0c56faf5d683a41226754a5ea094c6e05dd889b59bbd0a39aa8f870af1af6c19575a83f98d013097e00f1dd8c1745a6588e4b5c8fe8cda2b5d1e092a7923c62308bb7b14e8c8de31908216d89caf7a5524a030100143944549212aef0b9a0dfbbd7503f78003bde88f82d2ab8b55c555989d7efbf6b3ffbeeb98e8cb23644c0b5b9bcd2baddf06f1798d07b484cc6a6ea7f38099f162393b6867c91dc844cf7ab68e200dda96fa22b835aee20a135a349fc91522e4939b0a2248fff9b982f7720d0c4a1ba5a004eafa3c4f48776cb6027ad64715d879259c02660d2cc768cc4157d78e33a7c460b270cc8e5f372459b094dd6f9e36dd99101d1a3ad0370806813ea517d860dbd89995a2ef3aabafcaebcb0b56e293ced9d4aeddeec9d84856c52779acc58e39ee0d223debf7141e440ea059713e20bccb8e292bc0f0b51252ec61ca8663581f1280f81c245ec1cc7977418b4216fc57e90596fd21f8ea9c76d72921bf110a570a7d5aeeca8678de1425c72a6fefc3c7d526a557aec32801de6effab5d326c49950e16a56915182b2bb77ae37a2995ba27d7a5e7dc4cebf07804ca590e512e6f0497cbc8bddf992f8351c5013befde93cf3ce1ec75530b77946a453ba6882e26f13fd280c6d2c2a9b941e8ece17a4150f12d0a7861796e4461283eb77ceccfff0c3e4119dedc9785e19bb5b232c3f750967d825c20509f3090bd2b8d5592d298de1bcaba35a86998f366f484e85c9f790aa64e1161b65e314c551255ee1cc975f91cce014173bf4f6393e67b681cfee8571c3c82fbe7287665489203ede7879c856ac0ff738e169c68c47ae597811fc1a97fdc31c8ecbe470604d8c944003875809ad1f07001d117d4bf41857a92e9257b229b5f0e3318f1ec45b2ac903f3a4572d2136d37d2e0ba93bed7949f719b95d5fc55e45163092bd1fb729e78a9157299862fccb460d12b24c9edd78512a725aaafaeac63dec0cb282bd0e7fac500d7a9d9357058482800c6f5de20d0034bcb860b7e178165ecd39d90e1765bb39e3f82694a29ea30589469ee19a8a3af6d06a1edcdb574b8238b7d1b60a1ee3b0b02a37db4bd5f068d36cb2bc070cba265c377010af6c8669addee3d1d16a836e666a2a1e439307d036be7ed505033db9dbb60918a99360b296529bf9fff472ff87dd20283145cb3452e47d540833cb244291429029c3e71855ee3cbce409e16553e1baed26a875957b7270910a90ec1e021015e6b8836af1e01bcf6db986d23800d2c0b5edd2f9467b2e5fee8fbfc4393f15b763e8d2d9b0d1c5d9d9223cbdf06ca6dfaba08dfae1b9bda5d35eef451dd920383a6190570c17d03d64a1362ade067b8e975165d3825983264cca8a17eaba7dddc8a3b0b63bf1cd0cfd4e28f2b275a0ca6650d1f999e4bd822715c4148cbec4da862377a0679ecc77bb38b5ca3082c4446d5af019ad54a74526c3de0b0d1d637f3ea900bd0f1e776e66b57f97a80a3eff39d57c45393bd6a2ced250544e518cf086299b6b5f296d4130c9d9e80b9ec55a27cbc8680cf4eb0b55512489908ad637368b41bcf4ca700ee22c84696a5b73ab419913e9b3b64d6ec5603fa793f7fb4d24cbc5ada6f707b029f8e87f8f574cd1590b0880296d27446cbd45a38f532e4abb51621b40aefe864adc4ca0be1ec84581f2b0aaaa036e86ca1876bba5a30556e50981cc483375721f0149ca674291607e98ef668094e200ac068fa3bad6e6d046d1ea357ff37f743fbd16408c3e68d238162ad19d0b4273742a2c842520815cc3954bb9b2e0eee5ec8ff8a2eb86463dff758d25182754e764cbac00124b950667135b4e9a5f1b66eccf693dc8e3cc8ba6709a8ff8c5668af6bd15ad455dfb8b1cdcd295c4ca9937db9b1215cc07faf0660a4d0e9ab56fc481a6482e17492508167ca0041b812cd62b4eb015061120f2f775a1fca969af74d37da81a5bae7b8a318a6260329924495bc617cab2942392823ef4bfecb5de493a6162049225133400d3c10c815a0824a8b937de7fe7e836878d9a13bc5cce7a15dd2228e04148aae1b38382cfd7fe21a922fbf24e79515d01fedc6d677c81caf82c76976e8a55cdc40054dec6a240019cb5180393b207e357076f05cde25057d3fffb7181a273dc280cbc4260b7cc1356b76e249b759197ef93f1e3638f7488af8b46adf11bcc19887e8fddf322673de1c7bec2eedf12f31b9ad0a3003836452c572b3202562c8e82cde5071ce422396c10ebb4a6168742ac850a430f8affab0182c4130484d95e7d199a629c8c8cd73062c2661c4d143bd218c32e2cfe99f07923d6adced813c0afb41d836ef6d6e6acc09d375a63a1607f0340feaee97def24ed69acacfecde6dedae6eaed93b15677143ae87da89da0792582d9437a7c23be25ffdcaab5985ae65e26fdc262683c3c0fb892519ac27549a32d5271919e4676313224ac2755b6132e280956ccf6c6d6a6ebf2072cde802a3b5b0008372c4b6edd349408e74106f5b222d6136f5d6add4cab9350698107c1e026f8ffba6c4e75f070fa84cdc2ddb1bfcefe156932bddcda48abb79370f3b92079d35c9c4fa50d24a344705b227ff5a50d2e83c26088526e5f36745e30d1200a407a0147b889d7f05cf5825fcfb53c4c60e3388327a471fe42f91dc4441349dbe205e76e9c993feffc43864c591212b019558eac49692fb46949e6242d63bc8cd57b5e14d0170c27c569abd1cf025bf65a62b86f5a6340b4f03e265fe14ea937e51715f4314ab830239220c504c15615a643da015781eee3cea4a943c7f57b05a409245b93a1985ba856c0b50ec4779fb3e55425d4fa3defd0c5bddac73977a50543c5913b672ee039fc54e3dea3524caf3b7373dd9a4aabb657a603293b800b6e87d7b38dfabb00eeff5f6b28af5907621c89e5d82b6401403aef75a7de21a7538cd39dbb3c69b81cd452e8bb1e510025d04e7358296edad4e258137a2146c36a58731a9a9bfffaa915b4cf3cd06329012428317524330812978d47acfdc3359eb2400a5898668ce2807413fc3ee2570359c260d4c487debdd38bf9f07174f7206f49e23dbf6fd5a8490f8bb90ebb97af23ba7373ca85e8fe1ef1c3ea5b5df1b44952b850b67d9f68b83fbfc760b40357cd3eec28f634b25ab5c358176170cbb1c00bf74d4bd3f7f3e1ddee2417c77bcb0267d55db500e398c3cf6a05407ef0d03c9b04243cae235c4d319a76cb26e007aa40bf2122abe7e70c1a1d3c308a2c523a7fb4d1a351664e9106cc2674255f7df56e8256ce2dfb03e8079dd127b5c9c36502a9562f163dc76840d594785978433128b9b3eb835c2992650d156763670139fcc2768e9c2cc7056c7cdc09d8928f7b26ce7904024cd3a34c558df59368c563f7c917235182931c448d523f278dda44f0a3712a8a5ddb87809d2d72478bf5d1b466ae39f83c648959e191173d04e6f42705d6a281a6bfaba156598f58034359d6746261e1439e3865130eb07452cf218b39cc5334857ceb5bbd33c2e35e7090149de3bfa9c5340b9b646e817ed0c5bcd0f36a3cf1650b2490ffe9ac2ff05cacd54208e80ad08e9dec17358093dc7eed1192173e62b593286525b865a646b09266633336c06bcc7ca48504094d935cbb6290e1436ef075fa26db2902265f3d46b92937d2c7e1474cc8a4b5771279cc7a595153baf967ea9a8b50c84b2eb41e30e1dc1d4d99da4a5a8ad4c1caaebcd144ccb6b3846a04079b56e73e02e8b15d7d334d8f40a4aceade42e3333d32de4a2310b14f1a3b91cc6007b42f46b25a11f4839db46db88075cf5c01dae1af370580c601577ba06bdca77e43b2059a15269ab2c48f70d7d137361cdfaa5fa53c8e6292006722b63822fa0ca3b6be7dadf4b1d3600c70a77fbd24867202940b735c04813dbee21bb1b94d696eff9d7b9bf0168938580eabd7b2ad8911de8949511bd92071db1d0dd4c80f95b09e78f637f06d112a11ec9fae82a42150d667b55a4c03d8d61cca31ec1899993ae5a66bde19ed685e210fbdadfabde97ff5e01991a81661a227ba94641bf9da21095867aa1579a468f3fc187dc2c7023a7074548ac090f68cd1926d27aaad2ba709d2ecaf13d015787526ca3e474016e9aae3c5950c2d581805a1202cf5ff54e16a601d422065c4baba8ded193d825f3e9ed84230d7643e5b94505a732336ca7756ad47549f7cd06185dbf17312c0a285cc36d290c8e545dd9f63d9a7ee87b7f3d761b781cdb58abae61ddc99c5202d804353ba22c53cfb79066e2b4efa603fa5edce43bb5f4beafd3f0469f996ba161a78c893b2c28f5da804f84c0fd615941028714f3133c6692220fef1436ce0fce5d98a6aa38c16cc18a850f2680326aee68ba3f8d9d53b6cbd69167b98b7bd35e992d88e27813980f32218a3a66a0ecbcbc959db9661bb004c98596bfb91a61358d87a099b3aed0c4551a5cb42041149f101aa2e471818f793feef44f1174d7555b69179b7571af375ab28ac8f8f1a33e31c41335a8a18d8cfee8373c7845d5cdd3975e7350c6224ccebd87c3e70a81ddcc184c8ad6114159be29ce518285bdeb5808fde507751b31807335727d4fe4cd95cdfc384f6a4d002ec60278dd209405be92d4259a7e38229f3d50980c40e8e9141d44e2592c092d11d63f0360fc83232b6c074b7fafa265ea498d337ae6ff9349807e688668b5399d690292931c86fa51e33ca3202a9a77d68a9cb326945c967376a97c3d3938ddeb0952a492eb3b16652d2d473af01a4b0027858bcf837a424f989081634bbf7a11b2aac11f86eb03d31526ffac2b82dce9352e8b5ba1f26aa6d477b641a59ae62d7af011feb251191a627382a4c3e2ae2d8c05c1279439ec1793e4081db183ff46569500a9157f3970f642a37dc79bef4ad142533d43a0a24d75ebf7fca1d8ea65b1438dac006a74e6d7a8b238c64e000e846e63f7bd08eeb1f1d3e4ed812c710205c106fbf6a7e2d29d03ee7cbfbbbf11415fa5430fcddc99a95bb947153dfb373db0c0723c299d7cb3673fc7db6e4ce7a3d0076f7044b6c39667bf435dc5cc346d94bcbb05960045cb5ecd32d12a29dc4f984ab8c199ab97e51f77203b22bec7795071792321d3603af129574be29994e96186e7df2f5c57c9cf9d4cafa34e47054261e2bcc6069f094e5c1e1b1e2cde17e780ba90d113b305d8d9bb5913f7f3e489c0ba1d9868f3b816b835bf242ae9be4818099c2c8e6dc6b17e31235949ca12b9242b1a05323da125b2816710255280f0cfa6b768463bc4017b82305bb6815095324ccf5750c2d829552d0c38527c00874bbb6d6a6a4ddc8a65392d3bfb1eb6418be3c7ef0b464bb3d42bd982cefba7cbef003e4cb4c0e1ee01cdda7a0298854012a826b4c9b61cd1d4d0351a0415a8d810ac275c3e365563d8004ee64455b13e044e1c8951f8b3d8f37b0fd057da759ecfbac0a724e6abe63d01c662df91d42b8d1ca2bebc0b9b601fbc0194f49a7ad2fe5fe1a9136b0abc104bf2a2056b15ac89bfa2ad91e0ea53b116dadc4b4467d3b6bfbeca01e9172618f2654c615f4c61523f7e0a7ef9ac5b0430cec126d63c7af3ef0e0173d0c333437380175f68f096ede2f42b81672d3fe4156ee26a6c8dd4f241eae5b2f2a2f7cd8e544cde8813cf45fb96031970ce7632bd0820f2bc77deb00477a490eb47e2701d83456b2a08c4cf3158c455c8e217301ad11101fca9c80a1ae0da80ce68928b70d70a6caf878bdad5736d36a9793d0c8f605101aa4818caeb61806dc6ccae7bfef71affe90c02583e6873a1e1ea05319952a6561f8631750ba03c55f83698dce94ae5a8c7cadc299fac95d54e738e22b0e88ce40a330619c44a720c1ba2a844a07d6655dbe62e5f40fac450ec50d9f22404c3b624275373288065db41ee54921d27e4be57e1c0d8bb01471d7149a452a8d0d5a731ad11eaa1c37652203c88d2bb84f393f3f59898e48518f0ef98d9d6e260fcc29323efacd3cd5c17d137939c20b20040f1328dd98e33579f201d04339ff4017c95d243df3aef5e566f25224311cc6cb073d92f584e4ed94d0265b53a1e0208a6a4c996a3b8fea42134c42a6bbe74b4545c31142fc9ff13a08b0c0535486eb5928b4681b92482015c14ee9ff7d10791b55cb8725a024ae8c38d0f449205d38a198195d0fe63bd72f6f408b626126c93dc5353752e1a3c5c591e9efcd33efc937b4f128f48f9228457932695c3cd7f27004fab7508d0b2d3d539684e268f4f5906eb96337fb7aed9a89eb2c1537f7471053b9bc4a0bfcbe488d6f75fea79d30dd1793b00480d8e8c3da3e32acb1269a1c0f7930e89043dff22ce348f45a3d783b239c8e22967a544f998fc0b06275fdd92a0ea1a06e1f21bcdb68dd1f2f782f8db3df79af89214f2aaf20ac040f0f7cd3389357ed953df2767015479284956ff6900536feb77524a738ff418aea4c04cab2bd0d7ee14d356ebbc9ec07f2a7dd0a7e1ab7a65bc64d1bb2b0863835b08420669ea0407c0e3d37ce99d460e0030475e0ba4a470a07903d82981787ed991c565652e4c4ed6c156852f305a749e46c241e848fcde89608f68b823d08013dfb81342acf7ddba874dcc144c10d8257d24a3bcc8d2a936d7978bf89f6efc4db972e6321b9060a8a59c671348d3d9a2fb6b1a6a7da31c5a58bf38e01245e297108d0db23e060e19538eaeafdaaf8cee62f1acde682e041baad57d55b2f4f10773af3bc098c0772e45f4e15f8f4aa3a7714cc5c6b3ab22fb5bbaa1b16c11549421803cbd486009e5bc2734f3059c39479b6e7a9cee54d42fa56b67af70513dc55fd823c2b29111ac96247c4b531478a82d7102555114ad92185ac0b1cced4b084bcb20c5cb8f27bf85738d7d99ec2da8f5596cb5f5c5d250148adbc8634a17e68fef426b25f9797a85c2c6d9e175e96258af56656bff9e820110769060218a383f7f02a9cb56a5200b0b11893dac17a4d84dd2de42143ed0807201cc328d97c1645d4a0c6ea9ab7b8f7e9c09d3f13b00ea9c812a860af5fa1641b171e032aff3c8755b956a38a8f074a79a3d3571a1dcf10450f997c4c3a46c4485dfc52ab59b1216b5428e5322824b24012066cf2c857f044744156537ee79ed3fc9a2f94a8b18b1e160bab61e43df306368d30594f25867e4b220ea4640e11a64fad3fc3f94b537fd1dfc1ce268d1ebc5be5762a03de9a10058c137f913ace1121e1d11de1c1139693aff947e1fe9d734244b0459dfa2efc19e1fdae752d6c53c2fbb945cee06b882210c5db15c132d2f1f619bab4a734b56319aaaa1ff10bc0821d6634c969b0b29fa06432f2422d1dfd82edb74e482df624d0721a88b846d28d9296402eab19d0ad6f0a9ddedba7221dad00c84c33f1d16715f9842b007c8dd7231cf75310cabd2ce5d3dfda69daa7ffa79ca9f23c1a64eb1b66c070b6f1c7b9a909d4cb830c9582c6169dc100b188f1fbae69450c80a319533d4f2efa2e1803250bfbc6ffa61216af8dda157763ea727ef9b7a5173e8a884f39ed250ffb64153316130021725882b3752a37169df64b16c88ffa1b9cf1b784302eff90f006f02beb80e0bbd760fca8a2cd6a235d85d9dae54e384484f1220ae12851b8514e69d9e6bf1a475a8adca139b8fd7443aeec2dfab20ff7c1aee889e572817ded39fdc9229e66cddd4fa8cff1c7bdc5e58c43f37f9b6b7ccc79831451c53b3e7a17a810a42d1b8322da0bd8df1aa3e12ef8f305b1dbf8fc5a4c54c7b360b3de04785e97f1e9202b361949b3c8aefb3f1da0ed894a45e254bf20d0fe716ad321ef782fd02bdbebf3f25b27b1a66fcaae62683f6c8cb951222c03274dfa0736ed5a7e117b321e3fe16a034c40517aa4386d4e1898d8b725f298a99cc4eac79074bc99c1ec31a638c52eeb8ac6f48ccfb245831c612e81b733585c6c0cca2be6507d9f05e52e7087c64e004a065c845a7d724e786a5f7528abd56808b91b04f644d54aa09e1e5970ccbc44a36616af429b902b2c0e3e461a6cd492c69f0367446c9da40c3b09370b00f9c72048bae54d2b5300c5753c87389a2b65f81d636b5f9836751b7e34be400bc29df146bf35e8f78be6ef5219699d05886a315acd0c828cc63de998ca30c6fcdb285d05322c03e7d7f7a2a33ac2d7f53d2f063ab9a360511e721d47aec7e6ecd3ca3ffe28ed76bc7ddb89e0a64d3be9073b22654564acd9f9505e75c2728533a9671a127113cc9256b1557a26b7173d614d5144f6f66157bc2ec287672b10b1d3122f1f028a79bf6d4e74c7d426d2dca4e086de05401b0a9c8ad7f15202132b1761682ec6ab4a0264ad49e5c74146d40d4b92880173d9576243f578c641d63289eeeb22eb9e12eea32552d3ec026e0e7d89035155b8d5e47d7b607eb2fb2bfca7fea2c81ec52f1182cb1421d057a9079a420ea274a93c7b6fafdd1d7dcc886e4c8c3479515faf485a07082d5780992f524a3a674ff01523ac6010c453978eeb06417df9c72f486cfe88e826ff24ba2738c39d218b395ee884b772e14aef9bd615bee5cc1e9e8cfa7b4b627367733e755c44ed69e2321107fb97c68b39bbd40e54bced37b16655271c9a6d575e433156ab80f9e2be2d1d1f55ad959d87dd4bbb1c33140088f927382363dfa681710517d850a1171589da3b5df4b691404f733070420c7e9bce730cedbec68f032d352d868a815ed7ac43ee5b26e0189f62aa67989423d8c0bb8240398fef46469b9d016e3642f6a976ebacc63a2b6de541638d8ee5ee16777f78a7b62ceee5e71277752faf85a78c82f9b38a0274678964e9f056eb843e9bd5b06c50c8236c71f19ee0dccb07b2cd311affb8b83b2e4bd9a8ca6385a1494726986aaa51c2959e791f1720263a2e3e1fa9b26be238ebcd524fef1443a5874a3bf36369b7dca90f3969d70b869a6bd68374844880e5fb849704073d16c0877b35539f94e6cde74b351828d87eb4ad8dcda85cb84bdf3d8623fc6f8ec2a623c2df5025c666ef5c637f70b8ea471bad36d909d54676cfe99e56e500bd3c984463082ce1c58dc80aa7e577ec9ca3f9c73a0582f13ab5a86cef34fadd02fe0935667515a2a17bd6146f0fe878faf163c0172ff17f46eecc87192d23e961fb4b711da473a2166ffb21d49cb647a42bcb09d98cad42f628f4bddd12d09fc8731322b9f45c7700ca2897761e81f98d03823e4344bae104143574ca3ecef34f0d048f48ccf5889e0898df0527f9032b0a4d62c094f23fe38c78c9d0b9ff6a31c6a73cbb06f09e16d81b28e34703d869cd234532a4f90a91968560edf633de1ef7edfdc02157632debf0c71108713b30e143dac30d5ba72a55027b9721b7b04818d46dbacd8c6c9b2c53e3ab73f1fb2232118f5e5e35353bcf59647de5e41347aa9da68356a6df0ea20a50f236bbafc3eeb0421e2832e6de699a4287569517aab185b5dfa1502a5ddf6324e3428c90e97cc5c69f8ada10252c49671b277a116af7666f6c649570e6933f58211d8d7f50b91fbcdc36b0b84c89f42a7e47d68d612920c454053d4b48bea5f94481fe43476c61add25f8ea9ac634726360dba16e49f002fa64b70db2232dff0e020c940c441e659b9eaca5478fbe3345b2afc961b1418f01f84c61c24fff478abc556c8cf9182e7421ed7d5ba3e353770f60fe5d15c4839f671ed2c816194d64a588a24871a59e6a168a314b9d714db9310d6f99573a27c7a17be654ac317ecc9fd6ef11cf828a71c7b4e7f62d3398a759ea00f9dd82d94928da72cb5fab68da11c2d21e64d23f96d1834e0d0ce8f97aea3f24cd735903bbeae0cbc36d66e3ea96e7f028d066361c306aa914119015c316ab6617c0f932d881a518cc22c708cdb77157b96dea6e8935dd7243c271045fbee044ff88336d1059092067ea51342678ba9ad0c285de1165632fda90d50e1b7209c69d3c275404d3ba2177f6eb63e3fd5daa5fee0a7bc8ee9a1a54ba4d7eb2358a2048f8019c16bcd7578bbfa48488b59b8b87a188a0ced4358bcec8d86599d973899c02c7e16357dcee2d54f7aeeedef295685a3d2aa2bdd8dbb4eb3293755c3f02b6f76166dfa9d94157874a8c48243785c590303d3334a8841abb29f287841b2fa7cbc9ba280de3883246a91ddedd47c67b9d013424a055c0c24f28cfe6cdcaeccd56c5c744b812cc1ca50a6c27086ca610830def270da47468cbfb4c4d1f4260f208d36324787cdbf27a666009f760f4d36de1d9466dfec6052f80508e3fd17981a349b40e06ef05400250df235f6a9134a257937875bc11bd55f52dea1ccd927c51489ed4e8948ca5b86a8315e913a95b2492e725a029d69a13cb646b044006c93233672c9a586a803109b6d0d208cbf8def49d2897b03ed20428c4e2b803fa0cfa37d435d31aecb57737e76e4bb76679e4c7435d2302f77eac817650f735f069730d6cf2cc64c052ece401cd22eb4e24f2f122a6f0c16f56b95802d516a68a017cfe9d50d00fe396885d529006ccb32864d1a8b3041c149d3514a1800609f430caa7e3775e45f629b8a8b882a0a627b9a791ddd3376b499bc5edd20c2f73b3963876665434767afa375e1535699096bd9b63b31e43a846fccc5a99446c9259ae26cd2ab6d046f1b40f136ad4c9085c20323959c775c575ff12a1a7f3ea338f991e65822458955ac38f9bba68e34235e02db4c68dc73a70ccf4cff33636b02f34ac21025084bf42048332e1641a4c6b4b11caf4187808d510174c997261fdd06a5a8a0443051edcfa0428f94c32db3e7f89fb140aaaec66e2d2af45d4f7ef224d488cc488d17a16615d986c76682840f6b08bc44e66c60e8b0c75e4710ac75354783c554631a8a3e0607e090157e3a476069d920ce3440351e175b960f5d542362d11cc6f10be071d1b4ea4de1bc554402dd9c9af70e7265479ddee7aa94c8b97b7c424e666ce5235b519c478fb450819519c2f1f1054351eaf23edb19092dc5b96b674fe8ea22622481730f3d955183b5b66d663753c19d3182b72cb8344e63ddf0d4da719d3cbe513d6f13ca766982648ac48b669072157753abd61ff11ccaca0922c0fb28790db39378143cde2983d44479fb9c2fe50e87e83f11078ddd623f0694bd13708784bbfbfc46b46c17e0b5fe87c210bf3034dc3d1a9439ce9004019c138be7d6f8ec5bc010b4aa7cf01ecadcde2477964810d87c52c609a25bcc1001c0af10525f9654138dbdaf25b24a58a22219bcb2640f26cc4a507aa8024ab04de3a07f34032307e1b8d11224080724be7bf4cf855ae2a95e5a6701ad534149e920bff64a31043b415cbfa7470091a8105091065a4af8c7dcbe489b2de24858aaa9245d8b287309ef17f86b1e570cdcfb01c7ec82a2f61767d1c94e47c5f0bb2c7510063b63298a1e8457e5ac23ba3726d85015ae83ecbd60b1415e9c94dc46db990325e022cca6de063ea9215a053389d7b49b35276a49325269d0f94e96862821ca4c655977251fcd7fdf6ba895842c782c2c89addc2f89a5026bdbb9f48e23d942273621210fc2d5046a07c156120e6b04926d83b593e53469c61cb736e6259f95fc6576fc78722a05a1382c7be2df1fbc8bbf7f7237d0c775af540046d53ec7247c0de200c70150f7e7fd5127390442da085bf0367af3ee2f89142a561ea05d4775c4463d821d92ddcf3bdbe60cc776d97339e25dee77bbed116f4da22da6a26e30bdfaf36318b528263d71a2d07f1db31e18421c5d6a1f02c6ae4c4ee723489cee042e21ceae0c3b1da6acf9b2d3298ddb0335d2f75bd9593006f22131c6fcd30f57411c00f4fb95a6665edf27979c6e911387be4322767c88844b431f1120b5fcc595fcff81e5c92a3d1f8a1a64df3e82722c2530a8f817d481d231590ffacd4420607df6ca37fad9196846ea5b51d62f8a9637c40ad852b5eabc1aaabe12a23988161c0ff30bb25851089d8641f11efd7e8e7146030800a750badb7e4ae18910b9c796c1039d656c806fb38b0a3477da4940519c800c473b1dabf5a9957cf236f97eee1fdc002700779783379cb577c7cb3b030cb9e56dc00fb7e7c265317d7e96533ba78c01b4544c1e7171b9758e2a465950559ac6e902574571b553e8c0033933e7750bab6e785ad66290e0699cecd744f86cc1e3a28a2a3f6e6abb557cec7613cc49c33ff13a82b127e2865005563a44dce0a2a114d523f24f03f4b372ca2c40f5203f5858a9d3bc6e97b31600ec94898bd3bbc39d76dc8ee513aa7588ee3b152790822963a341285685c9c8cd095020c74ff3d1378ff9ec6e52d80947f723b438012d01590c2d897dfd5cb778ee793dc5ca7d7e6e44da4c0d9eb2f5a5d6f376081cff9ba45175677dab13255a6c0498c3320b53353ad4c9a4e18972ab2831e9ad7b4608657b1add140a8aafa3d4c923c04558e1e30e362219faa771a5592e01c68b0aa4149cd86873a8805a38f69008f1747e6ca3b6589d5ca524ae4ab40971520ad293c96e01ef592188f2ab977f674cd1917d30e911411ef7aeddd3732fe53c5bc49b72d90cbc09f3fd116e11438e061500c0abf121c5926f3b7f209d86f4dea6553c5692fa0b903074b9829b1ba0d1261ce7a7bd9fbcfe69521f1208d3d313130b21bb95cd5965dbbf549d39079973a237cc393218359cac66a14eb71c414d47587a4d8ee6930bdad911a3a2e72beba01293ae3781f4fa442d440edaf36a75999aa8373e565d65488e42e2f536031caf9e90a05aff63a621e4ae4e7c41aa42561db13cc50b353bc08d8b1625d33e46e6325b3da2058a3b98eee5e0566518a793842f2665cac31f32e523728a45c62cd19012c11b8877d84a0a6cdb0e74dafb450ae330a2317d174bfccfeae03fd09aa4e264f31df64d28dda21578ca331bf8738682faea06b7d45fc36bad03f6d4f0dbfd44d0697933e5577d1ba6e4537a7e6b7a89979c155b01bd8dce5867e287c4c988ea7d748e11df60c9c40e5bca48794f546268fa8ed0b068b197badd674c75db7d335694736869e557a6591859039cf1bcdb35a2f3239c29c3818457d8308a623260e56370861c5d1f91b7cffb07648ed4b3b5d51c3115e4771725986c33512f16735a5d6da85a040cacb2e84147c8af0e3c800fc722864991ad49259432d75e0d729a9587a681ef98d242b4db20bd95df1f3c079d4c98c05958f809cb3fe9d9e487c68edece4c489b6f2aa1887976fade781c44236e1d71de5cf59a64bc8f483384264f9ca1697dc5454c027811d29ae28618af915b62b2ac280d8ae5705d879103afbf99e0a86bf24f0ce7798d61d7efe5ec7a26cc95d0264e6f24300c491c89cbb52b144d873072fa34270c1aa25d3d7b4e2c4520587d9ed06126198fbe09fc6d32ff3f786ad29a4094c015270f10fb78cd4c4646cf851f903162e8608b69dad535a02040e933b1676c64d675c64f21e70428cbe12c06be37838bca025caf060850b217c46efc0f86c9a8478af759f71cd7ec9d5b51060d755c63e8e1761fc1feeff4ef3c93108d3666900ca89856d0c8170045850dda988b1122a50e50e5dd94cf39a98d28dc133457a230414d86c91824a4423498512fa1e9dbfb1dd1467eafcee2078e5189c4c2c131d3c7046eb1777f574ca7660a84a895625edcd856bf7adc61de7ee5806d28a399d54310f33a8acfd6ffcaa719fe13949f2b2af71f2df36043920a3abe327558193ae4022f196e26dd89ba84ca78d7b29e2487bed123cf01f32e9b392731cd0af8bc7735e481f670ee52c263362a687f60ec00dcf561020bc3fccb073de519e08e5c297242d6bf7f9ee3fa434ed0269f7ebab64e0a69c72e693e48204ff1f3e55ad59cacaa88f7cbd226fe11796217ea9eeceb06205cce67c2d12bb05ca7c6eb97d46c743d4a6bb44e4b891c5c32144610dff67612ead7ba95aa5e9f25d5b32b3c55197abb85bae026bf2a252ac28b79eecfe6096b08acf263def8fae9e39902b6412e5bcb3d5026d78d6c28a9823fea6f3b4c30f3b862c6c9ff50686b06c23d6f72f17a17d3735a0a602976856b086f53ca34353ae9e4bb0cc4e4a153e318ff55ca27c7219f7ac44db6f2906d88f3fdc382f4711c6f40dcf25a4e4dac4d6f6b1c809546c234352bd805bd5cb222e031824e07913aadccfb5737630d89c8c78d0ff87abdca010fa12ef0bfb431563d8f582bcb6c3a809af0d6f8a492e84056787288dc7f5f1c3e50b0ed1847cb7ae2ac003027e04d88047720076c46f32e11259137838f68bfc98c55637617a6f6b4aa570c0f182852f01be59242f525a1eb54f922e38695a99fef75a40c7f1140b6eb9665d30c3dff2a948882ac700c83669d8c2cfea21fda7fc2582829ebe923a810a8627b7cc7962eacb7048f2051344e2e656e357d4992d095e60ea0a52e7b4039e535bece44a7b42830b45468752db46c0ffc7250a71b8c26581bc3c89f9f4ede2b8843847a8e64e504352f117f026a5561340765a7901a71221d02bb3fd4c64a65c6200c408ae71e751b924bb92d5482375264e8ef531ae536cffc09c4d2fb3bdd1a2d727e8aa5c428ff47f69eeeb22b65820d1980ea8acf7b479e20747cb207c2a3b64ee49982346200461b8280b5bd1b0f9510214348582ad93b821f6dd69451c41356c323e9b75ea2b891071e1a858beb05e6e7658101b743013801297888ff787758c0adf91a5c3b4767ae292d5673c75a418c07ad272457b4f4740e103e9778bd15bd741511526fe62e28f6e0965b50a829bda3d69a6d4bdf318987a8fa66c76cd228d2e540dd5c4741a095d5b02a417fb22d7ca4292f544243fea08c42567772c28485b514a16c2077a4b9c81df7a8c57497e01cc5291fe29e05f2a640a03262920b5f2870432dde660c4d99f10a2f94b640c972c8c5cf27d4053062884958f496638de088513398203964fe12b51a08ead32600d9e02e4f50b177073b25152e53c876add683812f6ba6cc44c87807939f048baa221d200735c02acdd02c58579a01e5e3bef97306c4b79bc58eab3cb12b513791822ef92b84211318c1566f553f41fd7108b495d87a9eac0817ab9a34f3fd7f1bf94f14ee0a834d0c01340af85086367af9b47d4f0b0c8a784edf9dd8de2e697a7363d4d925402b43ad4ab08a619f6317e666d504fe06a2622f3fe449cb6cff387e49f59c641d7aae56cd5025246d896958951e4ff1ac80e83f35623d37ed0c517612be47d96f1005b21e4b680a6d6dd17492587add0c634f0e67488e10c5edb30abf2eb7015daa3fbccb94d8d81973174ff86760dd78b47bfc3a957977c3a7b39ce558e2353183a50bf9eb72802f3de1c06a7504608a4cdc8f8bedc3291ec6152cbb71603d0155684e29a02d218925086c97ea100e5c280458e9193dd949230f5d4d081fac95e9d0d62a7916e8e271dca11e41485e6aead38da1b65766d3d312fdceeb28c6fcda1142a1f7025697f436fe71919bc47b9e6e91723b6096b4833510431143cabbbc579500ac15ac664ab0d6cf3b43ca2a8d4c086d50b9ac8ba65b5caa0300aa23851c8498471038afa8eed0f78acc1dcae0ff851e530ae22cd32580d46c1d26a3e09deb6ec72aef402e643f63fe0e6054d0c8f413c9ba1a209ce7cac41a742244785f58f30f40edc3180cd98d8136d3e816aa4209d2d46d9edaf44736f1bee359fc8ad2f47913fa4c76dbe689f5d301211b528c0e8b364a790039c228828df3eb460a982b5093f1f47e5f4090a0dd1150440a2f26b63562c8da0f0dfaf186c8b7f7868189726584eb56f0171e1d4556d209b84360e97fd7240caf04885e2746215c7a09b157a0cc8cee307bd4db08ddb649dc1e8f27e3b81bf5d42865ff07fe31f1f4230c11f4d1bd065f1f9f2de940594cebae04412500906897d400312b36f1145f372e23175646693bfede6891f9816c10c2cc122219fa35877b28bf9b92db5f4d77f1639cd12fe69dab78f3b7c52b83b6131daddbf204978b220001590833aa0744408b3b1092c12aec207b35fb49b5dd9eab616093d709a4280510b3bd5b3eb02d8b5d5d75507a33d981cee236ded3df00a67e0bb2b56512b5e0e8b848a81e34547675a0bc88bde54bacd38062981339358f5d3b43e02a5e6bc004813b2c5baedf4449b2f7ccaa56c40889d1656f214b62d79feb3638b936315857dc31aeb6f794a93765dbe470179e4f1e17f0f452540dff1538b0409bdfa08197c51a92418c91c86c097f50b3b4981e293893b82a8fcd4c1e3a693c057f480f40ebbb8e0e5bbe72e07cf8d70d1966672560c79b9362312ac623b859f7c7e6fc3c680808d344c82946e1390c3f94d234451a936954db84095cad206d7a781a89c8fd6b49dba45fc32e09d5705717a8d1970f22cf3b772f4e87bcf483af6f25200a0b6e6bb250ccff9fc4c03aba70351910fd5b42dd293e999c0d3d514a698b27fbd689a62411a68d10255931f0bf940fbb62746ff45ee238a300d02fb11e2b85266122237119c0c4b37f18021f015eb2d6fbd6e58cd3de1ad5d8156c4692fd01132389a3df001b6200eee64b43dadb077fc8b0f8ce3d714cc26a764f8b7a3d8c30ea04bef18ab8f6189687f6e589ecab22b9f0349a61ff695555571a8de1eb2cc07ee3729e393f83c4171030d7321b65e80ea3cf71a1c7740b20400ba51c1ad3b3f122a4f83cbb1021d1846ca5c059477d89feb81d55dc74d389b4b7abd76d56cae32a9506c1198a8fe2a168ce27069d16a0105b19b226404ca39010dfa5da48e4b2ee27545c288d96ed643118a6b0916e459889d14a2105bd6ee75c65dc7c80b072f989e913ee159625eb230b2b30bdcf153092654f06693475ff7bb51d5906b3f9ecb74cc67f46047f98baa8b44a7542ddf776988762a6d66abfbc8dbeff6c3314eaeb9ac9d12971d92a689d58e8fdb0f93b395008433ffc876fa4ab638b874e8d404867da41955b068b59f990f265bfce2466899584d147bb16b96aa568e3ff2efec3ca73a8d27db14142d66577e4a0cf79dd634281bdbc9199e303b374ec928beae8d162c9c81ee96ddd9e5ac6ab35f88c46fb0753e7658b17253ad62039c6f843b0dad17eccc6b7bdbe6e7b8e5ed9a717b9c8c673e9370d21903f4921b85e982afe49396e5bc35686eb499d99cedb4becbfab9442baa14d200f6e8e7977a6db458b86eb143a55772a9a13856371a73b8fc846622ddd61d4cb4fb18d4546665692c91697b9719f22e9ee7d08119f76dd3267b8b9337e836e31894a07bc41ff9a84739a00f8cf91b51504ce4d6e538a214566a8ff2dfd355433fb7f1732c358a4409d01441dca68362e22a257ba5d723f7cd04669c09b47633c8b25ba49d3bb5f0b8f233ebb875999759aa1908368a33d068415394fa2e3481119c00506dd26e8b4e1c6d3177e4c3e75e14193bf933887d798a02219676bd71429bc1d10e176765f71cadd8e0fa549daab12193139a263aba7f24beb642d7b1002c13a759c30840978c4ba097c62331d61b281db82f2eeb29b92c0992cab28bfa9be1d52ce87ec06ef848f6943b6d2b7f872d19753adfbbbe95037d2d4d057872be6993884a612d24eac7fd854ea7e87548e05b595085942e0281cde4194c39d88d7f681dab247acf6d412885346f4707ccf2c5dcdf7bedf5dd002c03001d3bb957538db3ea7fbc793ba2cd37482bbbc3466aaf1344226fa3799515777dc0225e1fbb54e22d921ec33654102ac1e8a2db9b7bb4f745c317ea8bb296889d75e19692f86b76ee90bb266d89a2c09fb02520a1c581378226878b1474f50660f78c3eeaf3c260a4b3f30c2c23f38a1ae8a8ac7ca9222b417a796a021582890c817ef97ab63fbe5466255a0c3f1b684f8fccc7e6eb3c47a435fb8d1cc817421557bd5331a2862134aa7dabdf5e72c4f595aaba36d8447ba617722ec28f9484738ce3f3ff8c3543c9df0120c37f9d303181550530b6d36cd7ba0b719dc0eb47ad0c4c9f2cbfc1aef7b96e80d215f4426b34f6b2ffe33bc2a1ec878d5cedbca4060e06db58c129fb3798ca7ee07dee4914667374f93813afff3f772b6249bd649b781d2941f049832e3771fda8c5fe1d0c891c991778f63a7747cb88b3e05b8b884828885440f2cb7ab021d330bc4d4d0d96ba6e470bf7318e6e6157e1b3addeb2eab7e6b2299743fad1cfcf03fdf026213ce48fac7f91693d229e64f103702f3b1b62a033a274b08c765d8c3791dd4db8f04022f28f57ef4cd9bc71616db93d1d07d7207d3cfa3c83a873491095ba3acbe5da1a05d3cc29f254508ceb6476a4a1bdceeff6ae9f9f31450afa97f31667e1004329f9a9666ede9e806caa4ca1efb2351f7a1af6b378ba37507111c56c2e4d76b5aedcf944b9b6bb6fb793aeb088513836d475a20fc2ef361a1280e1079d5747027a5efc5d623941d4fd8d3579cb17dc42f13e538f5645608cf85ee5895c195325e247e5824e31ec5f83fd6d5b868036bcbf4f2c24be4100cde8178eaa66a034a357c9a1920bc1d70b238ed62e33cf0e37f42a49b8b2f9752433711d94a3e6ecf9dcea9b7c8d4ddb7a7c0058ae04bc61bfbf7a05bc7acfc1823d559be4dd06428cae8cd392eacb5896f2a6a4100b3012a8ae9f4f400cd1c88a02c68eb916021891d882afe5e623dc27fe1abd55d1c30f901e40019d65bc9325c10ada4dfddf4b59dc19731f2a0aae1385ac302c8f5f65aa73bf7a4582881b3d27b467d367ca2530b40864f471b57d13809a7dbeee4461033bc99819a926a084b735c70369f9e61dd347c97a931fa5167c3d51700d7bd79ad25681cf152ab9668dc982309076927a1f85e08e6f551a3f89c10add3602bb2f5e19a12321a0f204f7bb8d8ac5a3be4eabaf01cee1f687a29b7fe33e84436926005e4dc311ab8a44e581cef9c88638d5fb11274d8b1ef1e6e9064f409ac0b992d31e99d324031e09a1cd0f82855921d2d2881f0f5a4207748d10a403a04d465c008856251c91b0f3af112b6afba0332f621c6d25934a273234057948019dad33947af6d962e226857644bba37e0c03151cd3aa38293aa3ace9e2a9bdd16469e5634a4fd987f30612ae45d6a067edf7e51e56f10016a4752208096722e8fe6dcac78f34a975250869359c87ca8eca7d230b01e659769afe11d4fcbe4c964269734cd7da3598e3fa6f42303b19c450f983d1b973ecf774aff043864df2a2d2b31d160b93824443e4907bd8ca5c3f07c7912101510bf13b734a58d86887ea95b5b9baa27c38c487151b07d3381db4c56fe65b49abc9cc76db119fca588c8ece62a6aa28b00273867918bffb8796bcf3f0884287d8d7830e4c3662ac45c1823f34c4e5bfe6a98184e378fa9153b1665be9e9f0fbc610e34754aaf5c86a9f03798e2487d0c61a53413e0dc849a0080dce9ec665a04b63152adf34737d9d3e6d4c582d593dfe9c2d3ace9634555f339d3ee17ae4e295224c6f1643bd99d04eb33ac7416d041e9a496491c241ea668a806534e925a33f199a5cbf83392f4deffa37239ca8e07c7f171358415640ea4683fb80cfde05c6f30f3e8f9d2f15b8c3a0e0ca3b73f95bda3705858bcb4947686fc51c2fae87bee5726642c8b1a8154990961250b6cee87f9eb1a32b3657cb5ceff8048d9bfdd0a502ca5271c0c56e3b237a782496ba620809eb15efa34e0afe524a1a0b56db7c58d066974df6c1dd24a23b4a0a48bff6a10592abc339644316129611fe9c49c0f751c44f88195fff0353d69731c0a444e4aa0f7ee350e1fa18f12e575978e25beee04c2b042a0ea206ef51fbf0cc707e40a904a83e2a19c8904d3d8bea774934f138f720bd88871ced7fe8a1811a68d8df742a608849aab7fd90b95eb8a6970698430a3c720e05a2fd3b174cb6345ccbff282d7da3356d364ea437f859c959b9d10639221c6133c94dd357d45080d9645b1e804ac5fc24e751e624dd233eafd9a4bd46e45faac314e108ec77b940bca00be9caad4671107c91883dc79078ed9d242214bd5696484d8786fa7bb4b303263b8953a8686a2c1f07f9e306d844a4afe6c245fac954f8d667a09b1026cd8d67af985fbc8f969efa19f0b666fca97553a335a06e12df3b583f21b8583a9a79e8372e1a40c805c70fa402573beb132faae42ca492d80fc1257952c24ff524025716a9dd5461c89af73c9bbcec7ecdb7b990a85489c9d9960c7bccd8e745b880cc709e7687027cbf02702dab7260000df6df86ca3314693964a7147de667560bb3db2656ccb2cd1b7493220a9488305617330ccad252db6eb094b581c2a69c06cd773c3c611a33ce5bfbda061378a657d1f6c31a2bd1f6092ee403156ffbb3334cd49a67e47a5ac9b8b751f2592ebe512c3da705d99840d246dae7bce7d8154c2f35f9483d0e0939d1b4ced99b6a47afbd99a0d3c4a6036e7af1de05b4f4d2d1b32cb7c45d198aad21d3fc484e2c0307b89b8628dee366ba2e96b0c2a1f580a38255c17192792aba80badf7e978e30e42b346e8cc93474df35e2d0319199c0200e2f0c168ad9803aa83408ffd9c458802d226956c41913c17490fc5003e00e67925de798b0cda8d5eed7431f7535d0f1d0f052c5841f13396998c49a48a37dd00a4dd03a4d29561f9b311f436fa1e5740ec6ba106f1631d0e652278cac462e6afe78082a2d18d0109b926633c410153e54eb9beb1a1ad23cf6f1e577ac8f1b3546bea0c6d0d01cdb98cde4e356ca1cc2d0d0f897978c6d1a91fd319ed55d914169d32c343406945bf228ba2ffe2c0c7ca5f888b19f9742435946d01731c45944dc6d2c665625f0d6587ad2de39716aa244fccb73369412bcad4ccec3237ad9d0aa63009eee0c9b5a0f334eb31d73e415be6455d70917d28b6dd632fa973a1a9c63df2d45ecf2c4a921ffbd9b4235e441a568c8abe79927889bcf7e7ee417b2f23d3c98eaf770ea9cae755ab006cfada004af056026f401e18ab5b5937f0034641c92b417f59c627ccf2539f2bf5c4064767ab39ed6c01dcdf8594085856064481b430f8b2cc1c3d167ec61f22cbba8890d5f17baf63bcbdc505776797c635953dfb126624cc7a9ddffd784963de0f4f3b300ae5728085a8e8f2c2ef9618598eeb1b8daf5b31e6eb67eaa46f1521e250257b1e3188758b78a21f7f1144943b7e4aee978bfb1dc28a95ea03b6321a0a0e6b5f53cd1d10527d4176cf93525085e97427478cb5ee6abb5d6238462df3a2041b9aec01d99f20219a8f00b011409ca22a7909f239b1da1580b6edf8571af32ef8874c0a3780302b83c41f7ed57e30c5828bcf4bd04b8d9b293a31cbdf05516803134eecbc0df37d697382baaf8c66f9cb06621c23da3ccd14edd41428af45302d0a0ca353fd16f30f8f318c0fce43a6bdfee2c330f8ff0a80b91f3273e37b6484baf67a1a3945a394b944f3d165931ea5a4858c560800af2eedd82e9c3812977377c6c6339bad9351e3877cb5864743b44befb2a1a98708016247f8174b1413d82efe76f887630f660ac3039f4101458cd25b5c6a05e7bf31024c78d36f3ca53b5c91f672a964956960c775e80228c81e73171e5ca34c2cfb15d98f78b270a93b82191a0ac5482d882884c4eab21ee7f5a3f3158fcf6df191a93ce10bab165b5cd33eef7b3675ff2b2262ac5f2fc9dfaa9f5846e0a1c8eb23ffda8e459860426f1ee2c49a5f4d14b62d80d48d6400e6a42fef3c77862148924258fdb7fa4b0be1dffcf25cc8ba475c7d3a78f051c20350c4be802608ca1d4fe8afa390bf45e03bb94846786e42e03ccc7b7f842cca21545df417eba1e7469273ae38c4e2117f81f772d1acb24c4acf0c707a600654b21d96f9ee1cd82577d650c46b2fc243fc8a92e9c4377f25c8256b981afc17281a2d977578fd6e7e5ad2d5567b2464996508dea7544578541efe250c95fddbf958a373b80c57eb1d04fcb13b14cfb3b5b8bdf76e83713d1a4c320406af41fff1ee23e906deaa6c0041f1d9adf5fb1bb56237f607866b7345c6557bc119774f7dddd676aa0b1efa4a5ba8f10d7b6741c67311991d239e2318db93651af27e587d3f5604f5a0675d24b71e931b531c7245fe27d248d5d5ae8a9a64d62b341b039f90ad395a99f7f014a3daf22f6b0d24c1a38686a055dcf94b2d8572cf6d382444cd31ea8af32c1bb011a98b8b69df8cd243511310e481bfdffdf59dc9b4013ce32b72a29497f527c0be9b2393813e7b55cc0f5a1d3f9037950fbc53af8b0c185c411f5f880e0deaa51287a719fa18b82bfec6be3c1af461ac16f981cfe7ca86c60f8110e3ea286fc0da5dcf294058f73e5e3620cf552eb074a766e2b054493d12341c299afac1e4037213af44a8eb785b2b4bcf3e081c49b9685d3e7e0c0b7d9f4bb50861e4e97a83216cd1ead54f12f894667136d05abcaf09dbab318ff0904bc0368e3805ac82d7df57b57a76ad048f8a5c11c11c23048cb810c0515f63d8e3b960a64fc4d09f8a491301f0eab4cfcc505a86df794c892c095af8148d84b48d4635bf7405dd5ae9f35fba1de27fc18209f8b924e0d9be53e172ae94aca3bb26dc07f650c12614c479b1163dda2d08625c1881272be2a36c6e63b6412a67d85d9452e5a126c1f46d699b1b1753ea684cdc30a4777a4e520ff0610cfc484ff24f4d8c29219f33f660737182befa6e623963890ff53ca1e3689e9a0509311ac7ad383a17f95a6343234573f5c62970c9c3fb948fea27f995fffc4e162b55896ed0526e9fe392c077da2082c68fef00e433168ae3ae8fb6109908f0ac9b6fcd409d415e45375f0e6213e16533d626911fb7b5360c10e328c04423a2ab6c106f94ae59e35bea3903e235a29f4f10cd642ba33b09bc7a88e4903ab6157cac5b255e161c43f252ea6d26453128a18f4cfdf3b68e8e7cd61abba085f7c9d99c20f4b9116992f3eb36331f599f6845c4a8825e3d8002c33164799075bc1f9f3373ff49771179ab89fc7aa56cc60af0d474390761114c38d71870fed3fb9462bd63c736966c78b45263d7d26e17981df2e1baaba9dda2b24d2bd5591fcf2892f9984d128084fca1475467d2d1da996848948c52f8e7de949b9e03cc46612ed03d1dae1c704d17998771ce7af4ef8fd1188882497323dae7136732c6332217bea2bf2481ea3b4e8a7cc0596333c5a5a274e3f9e29096d3ab72b3fa231d8415b2d65cf362ea20bf43e3141e62f7c3312b119d79d365a93cde83494ef914afe180b7178864592e3fb6dfbeec3fac5b8bf680ab6edf411335a55e939597d0692c0bee89402384896516361ed06470a3e325e9d9a4279a1cfaf0652067a2d413f0471c4220c5cf8e8291fe8369f6ec633c9f7d9477b07aa6a7a10bd1da89565df506f25718716080a2a123678bd00e1557fce34d72793f8d1885c05f00ced1cea309cc8f0c69e8003675230a0ff32a552f8270cf5dc81afbb35ecb5274655790030677e8dc96474a0ba38e6661b573a873f4b0d7d4f75b9cca52cf29005222cc8211e194710899691afbc33017e9d5948c1cad5592f100ef1c603d7aab6c80e486f191795922c5cd3ba61dfe254adb74e76fff95c03f6d2e423a9c62298941ba8c1a2a42a1411005f574783983b6fb1ec8758318f35588fa83e8dcb112ad1558838ee0693c06bcbb1a7ee5461f882c292bc6f415ef068a64a889c1802f195fb3db1526494436142a5d3d894e606c4c33917721928001a5ec743fd55714cd366a71774b0df8211fde296c86eb9d174b1347cd51ed7fa73309e72edd4a93e3502cf31b1bd00a71e6a0af65dc8e4030e7d4a6ee469a0c67a9bcb2e24873e972ddbea021d65ee11a67cd296a4e365a0fd13e1ded90f219181ad20ea7d3ee2b6e773fee709439beefd52efff063776bf24e57f1cb9e92edb54a8bf904b46f70efacf844c4415ecc7413bf5711a41c1e12f9044b6a882f900facd184f0abae86dfd84de4169f4c508162492d16d858ffee2df7f7c4bc6ab0b45874c700c4636e706a283db902128d610e55e1dd3c41c694bdc988cdbb592f6445495c64cff52e3e37235c3de85204f6775c82a0810f39b0968993e4b7b70480754cccf08ce92770d60062cd4fd68c0dda4472beaf89fb2cbae928892146d4643b8fa367f503ccbc0301a83c190aca98f5c878fac41bd25c9149e0cb4c2b6247dcf99f859d307ed7bd2af9f3988b1079b751967558c42ef424eeeee43e5439a00d16aa98f9a727c699f033a7cb2618be02738c043186eea545597050865c060f5b0a154e494974daaaf0daa8ac4c2cfe1dda1074565c41412902e8546d9e752d4c352a0b4b5d1f4ac3292b687153ad9fcc93b2c3035532c26a96b2a7e69c5f982dbfdc2655cb3cd1f889a2a08bbf883980fc41b454b33572fdbd6d9ef24cb080a03f8bb92a150f9a53c12f4b393051e0516cf233cb9583abe0d26385acbd5bc1e9c67755112a739b9534f4c00c9a55c6aef0621824767528b11e3c066c9adb185efe676fb347c2a860ebe8e58af54d300a9b304d17b38fa486ba36bb7b61cdd655091451bf55d2143e1ec897cb62a5d4a3456361824e428517f5af97a77f69e580dfe6b82842fb89d0c49bd3c5f36922ea7c890c55de4faf2e7c02282c159d5734bdc4c45a84f292c721f2f36461b9d5ef6c107663cf30426d4d1f3160d3f86e101dc7f1642b7b092e4c2ad91c7b8408935282197bf974c975d201e95b79634e5c33864eb958273f781cbc5c6a11fccbd9a306e048c6d425338965f9728ef992a969720747325af16b48884f3a87aaf8d438ac9bd906e97b82954ace7e2ba378db35179f949bb09854e7705fa1fda630ce122e880b457c68670172050ea4ca02f2be8743135c77d9e740bd26194ea78885476dd6ee42125b30eea87102aeb10c3841073436a7d1402c69206f4f0802f9baa8bd67f39d0c27de9db3a5089818a9d2fddb2c348a2232dbf7daab43bf5cfe4d2f73f32a2ed5c5fd29e4a36a25c8eb6d07421c6de2a7f485b98f9baa861050cd7ccbb541368f73668e93208312138a528bb2336d5671a429747517c712e452e0cac4c18d9503b5718c8d97d6eb3a5b8e5923ba106039bf4dde3ebf2a01879ee3b668d9e11ca3851ae06bbcc74802c2393c0c7996907342b4ef20606cb52a39cf591b3fe72d67a34fb3f681de528ed701f8a40e0359db72be58f12c337f8bc7135ad3f3f15385a6148d2255d7a1e5af3c5387d494c6b97772bc9a7b974c46576719db46d5e8ba12e6c15fb3e4294428170d9e4866331eca07f2872ae69ecccc33456ac6855200a7173fb0aa4fa828066652ac1827ea4810e6d5daa94aa30e134cb9c841e6d85d961c2d86c38019dac91487936f77e5658c3234b54addab6502b8870e91df3b38a00b3e41417f55f49b9a956ebc265b5722dd684eeadb3ba279d0222b78964d9e685846ea4a80ef7d10f575c8ce21b51a29e81aed1a935341ad84cf4ea799281e035fb2f6e81ea599950509d224b8ff372d1997cef86eae563bb0a3d887a737045930675553f9200e48c9dac89d21c1a154bbbf54cb9703f29899032d6516e9dcaaca79c75d2dc761816fbb6875ab6c22fb79b9917451dd6bf9ebf0ca8ed822489056a3befa110cd8f5c95225395cbf0157bead8683939f48131e5e76ee3528a20d8323ae619288b8d77dea8b9964b1c1b92d2c49c459614ad50190616339b8281391a5b6bee82f2f8d098c9bbbcd9947caf711366e345c126f6a2ed9852eea45175ce0c0c07d089e70b7d8ccd3fba09f3363311ed55904aeeddef3f00dc3352bd18e24c433ec658dbd02f3a013893885adf7b96b4e1d899bcc91e0806790dd4978d8c59db5d075f40e5744f576255bd7816d8217cfb58c35e4a16f4b1018d528fb3468b5f4163969c78f45a0d2d5a32a281fe9c492870eea5cbc3d9d5f151380e46a383add0c5cf1ea679fd026ee68f42dbbc53d973cdcd530cb381aaa910daee7822e423564a6a757f0160745033b92854ed4133523be8d1683d369b6d980fc590606b3ec3a3b201e6fa64b4b1e02d3d2097291e0f54b4a059519a7b1a4347a3cb4e288f8125d357dae726638b95d4136e5222ed1cfd3fd5c62329f48a0fea5a1553a2b14cba21f55dc969ad86f1d28246d2945c719acf5c590412df332ee4e4fa1dbf6075c46b058f1cd5d873c67333c12c91662062fe879d6aa0e4fe330ef1ed06c92e7a9ed6f74d8d704b79fd37f9343e76962267ce9b014d51604557c3cf429fb43690f4891801fbb738a2901c86a64fbbb08825c7986052786786dd5d414c32861f2ae4ec2e0d0438c6a146764632a3dd25e4e3791f9b3d1e35ae7f4ad6cd17f0e004475781549a9c971954390f3ef0b7d310ded069e6896381ee5dd8183f9d69adce7f222380d918fd70435e5088df77150ac73d820c7d353b6a9f9a8d2a53793063242643a17385555e0eb9163b147fc9be7e208fbed7406b155c4336afc7dcc7a671a89b7daebb6a8574a95e3af8d73b7ddd688df10d0c284a0afe6a64299ffca6beda704ccbd12b24b6176eb888cecf2219c7b0c04b495b334cbb7fb2d82648229856f71419bc62f494d51b6dc9937caa690394fe598f3182d11fbb527e12b1a8017aa004f64ad4dd2f9721d88a2493abf35f94b65d025107d0d1c7a466a08e90ceac13eabee18337a8869507fd1146680ae5c66162aaf64134957c7592c485eec09a15fb31a56a6ce48c54daaf542b2878383b7687b2e63a7f411eb805d1700bfc56d072c9a651448073615590c70eaacdfe158067512cc79e6e3261eb13d55663b82b65b18282c8508dadc1b36776b196890e47cda38bd521ef61fbcaf201f432218df35bbdca3be2280dca04fbc575b5fec3bfb4e45505e6f4e03dc5a2295f2cfdd929619c3a80a92d3ca9e799cdcc6ddc2bba412c43c568aefe0909857824b6176f8728627b898e99b1683b124c65c82bed4cf8e5f177a87ad263861a0ace1efa56d171ec5c5bab5a7d70b4b1e609e70733ff1a72eaf1516202ebbb2517c1ca2a5c44f9f7e72282127882a977cf64faec89a235592698c73bf6dd6afda36cc6f5a9f31220518e956b3d3ced3f35157854c4925006d22bb394e17ac656609acf1f69b0e8c6ca12d63b580cf1336f733e34ce992a713ecfa3184e506c80be221e500c383112008750e509a883d70e0875aefb2029db9689a9048ab9bd9a1cf5955682669f85e164ece85ea1b3296e62edd4f22654fb26bb4758339b66f242faf49621dbb644b5a6399ca344099d9fa48e17aa0bbabc6f9f2c6dd1147dbfc777f53df961eed49ddf126583585efe9023743f14232b1347a183dd76f3a45c5b0afd89896bb12d5b702ebd31061572c10f894cde200b654171027b610c193ebd95e82565b5538bc00f69769f409b40709f969a6530d984e5ea22e0f689912b09cd6f796954e15e4e91fe09a862fb2d35fc38a34dbaa978511e74d0a26011b121c07ff2dc1172a23ddbb6303233f2ab7cd7749f54ff2723673d173225d4daaa092af0b4158830397061b2b27565e1ea7fe2dd456994f32f5690afee26390ebdf8dd67776d0b53b4634bf81e2fe734580efaf1b8e96d5dfcfc51862e12273270167a2c6a8ddf808f4f8459eeb8c0dabf70f39e347a38a943e073375aa435a8b6e69bfbee85320a82952e32d3839f56d31d647bde2c52422dbbcafd0d416c3a80956b1a946dee704085f4266b0a1ba43d043a1a1f9a5a305c15351d9981a263f758ddd46dd4f5f7dad545b35ba6d52a844a7eed36a439734fa8ac5fbd02c2071e99211c00030b60ca0084d1bdee9c330ad85c00f4001e6d6bf884261f632ffa08f661bf35db96969a889051237bcbbd777309f508dd0914239d623b3a15fde41487a563d4bbbd77637b775defcdfd60c3180cb3d664fa51287aaf77d7bb1b932fd5c5342a7805bf53d74d267b8750f94ad783e8dbe5556f5dfeba3eba4eba56377bbb6b92e3e17615c3ac35997e14eabadbbbed9aecee8750c117c7a651c138f6e7d5d464abf295df2136bc826d64fb9b7cddb03b664366d1adf683e817ee21fa867b8c7ebdbb3e62f2752ac2a347f96b4423a625d2e57997bd2eefca7e5d5e67afeb9862ec2a81bdbe1bdac8df65c3dc1f3f44895e5f595c9c9edecaf920f27e9663ef2af8266fdfede554e075f1a86511fd3159723ea8264ab668717c1aec42e52d4ec3f2abb46039b6d1295a5bb438cdca555ae0d8a92156b00a8ee91475717f69bcbe2a6f7ae9d977baa961ee53b2c6a12a571c62217682435b42e17bbded1db9fbb7b82c5fb9f82ad7050f5c02eacaf18750b98b3777cab1727c1a95bb68f17b9a16bf2dee7f4fc3f216bf1f82a5850b7c67963ec48902c22e30feca695c541bfe62081bf00b5c6f8bdfe4ebe2aa1cdf95c764adebb29925d75d931c0faa99f9af7c0896ab5c72a71cffca698e574ec382af72161617c72c180fe102636c3b557f9323e7ddff342dce72f1f1102d70c4bfc9337366aea8dcc7c8d0468ec72ca52da9948f3e28d38629603fd891ac795506512a67c823396229bbf664a98a41a9a44962cbc21695d4c28ecc17f9932f4b620a841ab139383ed6c7e463c24c39312726873227f644ccc7fed4253144512cc2d86861478290c8217246fd15521b65117246edc6b2882145f4758f75aa5fdc920c6c1879ec44a15e8c883076a6e2651147f0a1961563376e1cfab8f9aa17e56a5cafdd5267bef4adeb32311cf3b22ade262a66091c8da1666248bba34b84b13145fb6e188e9457cf7c8142cea0aff5f2c93ee8a504ebbf7e4171f9cc970b27d72b93e0e8d4fcb513031b4a9e70e664b90509836921ca5c4a228c4a7ece0de4190653155450c8533221cfcf9c1a8f1d003aa6145812c8224c66b060c8510b39c6cb0d48184c6a000231d65c3df345093e3972b561244f8ee461e1c99949266a5e20b64c0c3420a6e6656a0a24301561e506e6bc0c19b173c74513368463a7a475b16da2304f368526b5276b9e4dbdac96b4256d29a93c6273e1505a7d35166874935c6f0f36543860202f8bc82132070859e3001ea6c6b14d727cfc04b2a625130bb547faf03411190a9b086661d60ed9b2e843968caa92216710892a18a88e3d2e35b0d1464b53c5228df1891d72bc8b0c1adabc0559289040598749fbd8a64c122986c35196d8ba381eb24ce32dae064663a634d6fbe2a519d85035b34d548c14065bc91c311f1a04ad5eabd7b5ee9e3cf29440a84b72d7b48f8d3eefe8e3a3ca99528b3e78a6009d74e80f5d45ff4c54979460c3b884dcc74153f3c3290575c91591bb7265b3edb4ab8b4c14bdbcb1f796bb2524d8f0cafdf71160e3b2648dfa0dcf5ba169337368d533a07553c919d9eb89e4199fc81816976057765d5886165556bd33131533511876591aa31ad81e206a7834cc77611bf3a5071c9fbdb65374eef0d2325ce24be8b3ba648c7b56ef4dce64c81821cdbae488645765a2228d5844cd5bf333da8ae1409bf8bbd7b78b75915c37ec93899cf1913e1fd2d19641d2bff9e678c832094f2cd4209d7b64428166e9922e0ac644d1e310e110f349c0cca523368c3d618c0d1c34350ee01181eddee9b7399a30a589053943e659ba71a6b267573681bc11e95abce88626f0d68899043e720ba8f524d2eb4938147dfb453a463a7612965c0244b71fdd2f8b6eb2f5b66fdc76356d6241a289f96419a536537b943342df5ef1862d19437be56af4e9294743db4626194389ac6a9a96b56b59bd1addb6ed35e837bc4d99b357ad46f6d0bdc9968c512d2a35496f1039ecd3c890af44af9b8659c7cea4f36e44f6b0d570e863b368bc62be2293f9729d0b5dd355f944d5dce1512ed1c0d277cc0e13e67b3daa5e1527cc37fb56c819327296e87eefac6bd866e4bbe876e02b26422126f23562f6f0c9be07760f63b7b7b8b83d729c8f2d6f585a2163843eaff8a25e28f039e964cddb7028a42787dbad87b887369abb6fe7ba6fddf6cad5e87e3d05e85444ce88b9eacc977a9f1cba538a89a2efeef49928ea44a697c3514262c319c47ddbeef6942be6cbfcf614a0f912f3f6530ae40c99b7538ec66671b766d775af31dfede4add3e6c6d5d8cedd9bec63eee40c9f522063580fdd30e58a6c552443e89e2c57b161ac6a6dde2bc6236f8fa21bfec81b0ebd87a4b6d96fdebf7ffde1d0a6e6efdeb4d8fbbc57aec6e71d7be7c8193163de2947c3ebc9db75514dd4f579fbd25b6fec920dacf53a7f5d47c06579de64e27d2ef1de24f0bc7b21fbd0354dfbbeef35e6bfdf84cf1ef7cea6660e6fa11a44262e59f34298bc4acea00fa9f6cc85661a96216364af9d3bcb4e391a19932caf092b31f19c3d7b942167c89cb964a793f220afbd86ccb2d06916bac936335f58868c41e911edba76dd38b1df647ac3f3c8b15e77562c63a63b4ed98436fb864172bce24d8eb74920634c26b9df37979439a031a714b0886152461f9b2c420e298249e3585da04523489c884a62f013951811224f6c3373054cd9c694b3bc917202bf028d2db438b5162da24c8b162c2c518685656525caacaca8a844191595d329ca9c4e2929512625050525caa0a0984c51c6643a3989322727a55294299548a42843228d4623108c32202812451991e8fb3e6ba38cb59e17653cafeba24cd7715c94e1b8d8b26db165ca685a9645992c0b85a24c28145b30ecbaa2cc75595694b1ac5aa34cad94764799ee39a3cc9cb145467e5ede486909c104e8a70a287c789e48b2736489247472884821b26c92c515414b9848f12325ce1563d12bd30341a91461bce28a44b04215991e063d55ec9e2aadd78609c33353071a4329c5e982166d3062c3f853310cc37eb09f8e91e590962c8324905c226be426251356e630027b2387c8481f1313132fb52ceb9a40b5704c471965bcf53246a5ca22d7371614f3d496b1ce6add1c88b0b5946d620e43582343b055080707e41a56a1cf4c17d4bf19b1218dd9b558b3cc14c7999219111acd71c26c745249295529820d9f290a063da57f31bd2813e7fc8dd6f5a17b93fbdad48c61193246fdbc8dcd46422ddaaa181e949cd5a2966559169667a439d0faf29a942b298388af51e6ccce224f6b725f0338808359b81034777a16d19158c43c859846e44924cf2b7c4cd49c3c988e649922c655cb25661892a3e4287e01e3666640cec9040b47004a120435ec4451038b0832d87440822c18c18a220b491822054cb010638cb2bb37dad87c99b13b7637a594524a29a5ddb17b069452ee1b1762f63eceabf21095efbd76ef09bd61e9cd25a0fb09c342144d0e4bf701239f4e3b03418ce9ef3d39dd996949890a9ec8f5f4c5fbe4f4c43be9520b96d2574a573979a9f4caed78711baed9303f14141b504c9fe92b5d7b71633295dc0fd4647aca0d6f72cab5de5ea484348b5cc0604fdebdc9f4dad47cc232640cd3eb0d5db2e9338d82e3448d6ebadf090e61e412e9c689d2bed1b649ee878d3b0fa1dc9de3be594b0303857af1827a1e087ae1e87b41a905bd172f6e9c29cf761a0d8c9b0dd7d36b2fb237fcf0c5855c476fefa581a59f3032f7d0bd4b031bfa80912b07235399b78d865b16343747727cc46e2002d01460dc4004202964f93b6d3802f7892c49c581a3ac54ca48230b8a3e8d52cef328776f87ca4f1aa651c19ee6554e3bc7d1c44e698f9daa9a17d6ea795ead9ee9a4e4795da779ef3c4eeb3c1c59f4d1e93c3a55ff712a1de90453b92715fbbaee386155a72a27e26ab5a2d368441ad9fa5315590e3c5591e5b8d355c141a3824fafb59eab1d47e34315444db3548612c98aa84a011d38ca4e4e4a99157db5d6d1886447b556558761d69a4c3f0ab579efea3f6b2b0571d0cdf36a15e1eddfbfaf6a15f7b0b5b31da992ba3a1a8d46231044a12794aabe4ff483eaeea24f54323d364c4a8aa9d4892cc5a12a77e0c88a4ea3118ac99e946c6647d64a1c22eb6d9b8a7b941908c2714284aef34cf5dc8e2ef33653ceb28c4412812392c7ed40b9a97298060577f7c07b60e76d998769baaeeb700ff01e281a91482452d7913acfb3342654d7ed309d8b3b4c184b795e77af13bd870dcda277dde8ddb59ee79148249287c3eff624d2f72e7e64459fe9a298ce75dbbbebdd5e2e9ecb36d3fd4183824d9c0da5d96a48f32b83165f70a2ce3ee3911b87d9bdec72cf3e10147d190e4dd144b1567bab918636b0db87ad4ba4087e66a6b86061b32cfb68a3413c5b20e5a1e5ded9ad9769efd7d6b2eeee5dcbb2cc46f4ce3dd3a8956937d432dc63c4d1daa36c949548e006825a9659dc0dea43c85d7b86411f344cbdacaf69a73c3051df1eb8043c466c27e7dd4d33ddb3a311062d16711d531c87439baa51aa510f8737dcbd1ab0dcdb02325b9f5a9665f778e4d6de97bbb5a91aa59a46b9db542eb4a959c371cba1909ce1117978641b7a808b92c06176a2b089e298f0de81af4cd64e940ef41e078410e00d6d3278d1fbd8d75bee3a3777cab2efa60b66a2ce70e33851deb6813eb64d2385b4921dcd9ef49d466f9525adb7eed4022d32053cef74020fe2eddfd6ed7d8fe0d69d4de313f813f8ed21963f7c02b18d981dcc0e3ec3e8f7ed8636fd6dddf67e05718fd2777d6cb97bdfd062afd495e8bb114923913a52e77d218fd49fd733f4ee3b9d2fa47fdf356aff99b87bdb4874b2ef814dddb9ab8936d2a5fdafffbdf187c1cdebbc5028cb361105414a3abde1e85696652f1da4b7fb4e0f7e7be9f6f840cabdbba29344371c611f5bceb230fbbe7df47636d93b78698f0ed35b1f9ba5f7e8bd631f9b8fad1d00848d9cc1143370828f2d5b40ce608a194c71b0061a8eac659444414ae9290ebb6f9556db3d76c39470889146f560b5f5ab187b2aaf44aa28f444d64fbb77dabee3f495ef60b94a47e9874f1b5ec1342cf766da0bb7c74feb28d52a3e59ed9eecbf6b9dc9844f169f729cbe729a130d8eace1d0541fee2152a9448f128552cff3a84769ad59adb56ef68638b2cd40100c5119040f5ef35431df907f1cf81e7d2822d2e9f41da313dea182a3cf36adb7793872d7fd2405e524238128267b52b299c892ac5da17693f1047e74d2cabfbb912ecde923f0e65059b122ec43a5cdaddb56aeca4f9765bb3954f0cabd4bc382e35576e52aa759c12a98e684bdae533e545127c7f88855953c8f69a284e0bb2190fc715c08a4e32cb5f6d64e6d3be9edb449a7a5531c8a9a36f77a8ee34eda28e57ef8fe59ee5e73f6961702c99cd55eefa9c3373e0d73d7b8ca719d394cb9136cd35c49b471b7d72c672d77bb71da6739daf566efbacce4c9effb6e770bea903d72a14dc665dfd7894c9dac36fc914db7131c3de68ee3344dcbce5dd2b37b83c4ddeb6e3df07e3814619bf6ba73b7bb373ecc7157f36c9ac33e7e009196069ab76d17512cdbcff4774578b30dfe7b87b189a216f7681bd2bc61edde64f086417253d228c3dad4186b1c961e47f4deadb2ebd63fd87ff8068edea3f5bebd36959c0fdb1b9fb4f749c3372cb5d76ebbcff2287e05efe11ee06ddf90721ee79d50d268e4f5d6dbd6ed7d1bf5d6bd511c1beb9e7dc320b96d08247b07ad873b7a20f9fb4c77789b280f7cf70f6313e5dd7bf779d47adf4bd2edfb3cba7db4f33c9499eebede28d5b6cdebee9dd2eddaefdeb0946a1dd5a6b6d13e958f2d1f6fc1d0df39cd0b92696d8be93bea7d97dcbd5b9a17cf8536f3f346948eecebedb52e37ca9d62fa10f743fda9fb0e949fbe43e529b7f76e48b377eaf0a7f23d05d7efd486dde3f70bb4debd4b6b101fbc537cea4ecfbdd60e9f72a0fc741a144ca375df431a3dd605c9b5b35564b75b7ac38aed462da5d45afa7978d451da7d1ea51ca59bd5a4773311a5d7d2c7a3ece88f6ed6729f9974fbe928df7142d9918225f8a8a23693bb386b6d7c7d77b96bf6ca0e7ff5745b7fba33fd598cbbaddeb54fedc41dfcc87e23909bd67ea6439b69efccc2bba1c69dba7fb728f7f41a84e68463a3fcbbddcd91627f7a6bd7c65d8dcbc20c64edf494d39c3c1a149c929e6ac319b50182591a24bf344fa8d0dcd5b0de369bfd13b5bd5eee9e69db7ec8b09da82d7236d43b098d28c64a1ae91193747bf60377efd96bd753876f70d8c391defb7743eef16ddf1eaeb7eaf71f3ed4537cb2a7f7bedd933df72db3f806777a79db876da8e5e11ea25bdca3f4ed3393bb9496b651a4a40a8259b6d18df39e7d0343d9768fc361dd6a287ba8de1f39d4358cc966fa0478b8628ab9eef7ae7613b5a139f49885425b88f36e2fc7891eefdd74b14e79f8a10dcd75bbc6dd1ee063ce32108719c8e049f76e77431eb9b321eea6100e79944e4a38fc91edbf874c3814dd8b273547ebb32c3bf8ed8a0ede1ba21fd9e3eed9789b6d34ecb7cbfdb8c16d59e761198e1385c30cd02c7328c3a1cf4c798ae3478e8f41fcb0a4f949f1d988c9d7f7e9d58ae5f0d73729babeebf2baaeeb3c7c5dc44d527a123d985db71dd2932befdde34a7ea661bccbdb6818ee339d92ef6e4da77870d8466c628a4159ca08b4e19015b6626ca27866628c266dd22ba48dd094c5b044aed6afc6a185758e8480340c860384a988cde9db68982cd8503ee1c08ee1207285fd66c523ca707986308f4ecd03e9d4bc69e60cb1c252175fb0615cd2d6112bc051a2042272e844d949a58ff53a64be504bbb8f9462be08315fb29989f326a6200b64976098b526535c92b5deb488fd0822c2480f84423f3a4513c0c5a01f5186cbf4fa44a04e94b1995e2a1165ae402f8970090399864c1fca3164fa5076215329852ca74842a65c963ac814479fa892a9f5b09deb126fd7bc3f36943a2af43b3586c416fafb043b846297f499424829268afee8a09a3cc844a9a53a211d580b63132575e4a9951b1882944c21043948304308da27b70fd044054d54c7051b4aa06eaeaf4929e62402a353856c222ccd3e5186abc2a2204e27664e9461c9fdd93d6530719d3913d55fc9819539081334dcb507d98e8368b72692b9c444293175e6763bae260f620b1aacfcb952d83851f28a896a30581994fb73620924a1882d7d202020a03ec6e44661dbe78a4109a494b25b36edee96e10e748724ace1880db15c84d6311912369446480dc417cb23c8f4024654c7c49656c2368e4e755eb9810d634c0d13638d189638606b20a22e53080af632c1cae7d0215afc0e98e4013be325cfcf2000888619000160dca0ba005a1ee3383cf5500eb18e43df7ed8f8882d3387088a066c3873a65c71df68e623ca649f8fc9e1f34088ac8cb758394bca63343d4610dfc5637f8fb1616450a772b827ed3a3cae64dc93864f3964dce53a72b80e39dc25870bb1e1321e5777795c692ff029870e77b90e1deee2722101b80e38764ac33a5caec375c0425077390521b3f65006659f993208c705c6f8c696f95f20266a3e07193776aa050e51319d9a2c388c91658c1cca1d142a0587bf099770682d883fdce1cc0396c31b96475c1061b55010190efb584e177ca8b0c72bb30a071c7050a954383cae04704f9dbaa7c6a71ca994000420800bb1e1a947ec12464b4b0b0cd461a00ee34202f096c7d50df7249fc3e32ac63d497cca11e332aee386e7808500e0375c0616c2040d007ec385d8f0183711a0c88c1d260a35800be3dae8d4fc0d57755b6e8c8bc3cd38dcb852ddb88a71e3ea861b572d37ae60dcb8ba6ecc03a83153b7621bc6c400f4c53200ae0dd7c5ec7a9eb042cf135638810f0c0cc330152a8cb1d1c34f8c12248001a358ec819d748a9e3040a6101335491e2801b69a31cadcc0fb94353632c6943f0d037e8a5aca020cf96a7b289bc8f31207d200db470fe5111964c5555d49ae069781c4969969c17af77bbd46c05bd7c8106c057ebb427440b5fa3ec4aec003a1848a9f6c3373c5272c6f39c87ca9a71cdbed4fa1930e4ff4791e0d730a611ddbedb7db875227f450e684b045c2d67b8b74f7547fb3e2eea9e209d07057756abec34150d8e3805cc38a09eb71b53d443d5b6df71e9aac0d441932e4c981f842447cc9260b268e9c6123a708220c378fc8b3a661e28a1447b2c995a27bade65db9d3a91bc496f9eb4a2457267165114964a2e63c42c3b29133e4084a105b72e8ca5c8361d95a12a2c2528cc3c6445d2558f9309b1a0c8a80c4bbf6ffdc4a7c7a1082247890a80286945236866158cba6dd2d553ba07aa28ffccf44c5989999989923e4e4d0443125f24420030e7549b17a58d775cd5f3c72c7eb560f375986cfd20b368c3e34354772297354a2842a72d330810d314a4513ba20a2020c83a80917801f20428250980daaa0e9a805d49452411c74c5c1c1c189a886b1d13895cab91a89c086126789d9b4e79c162a478e20c6e80b4b1c598279491c216cf8bf982d67cf77b71665a62414d8a9c4199ee059459410d8b9842a46a0b3924300421474565189108ec0596975a3777ae0034728b2babaf0269f6e5839cc40c630264a7e07497a70566d25923f471ffa5b3fc1d573f55cb5d65a6bad97cfce5cd26dcd25b5a7f6486176b7a58a735e2eb55eb556975a65b89c40c7d2c9eda3840db71c755a4746ebb44ef5913b5902c9242a974deec82676e4ce04ca5d8449c4c2049a4016106850ae3ad3886d64a241330434880659249e39ada83ca8a1cd933ba886a7791ed03ccd9306ec48c67ea8851db17ab6755380381ab3bbad6e7aeb5eef3bdb6a2b05a84522c8735a279d2b03d9c2575b97b6e0a433516dd5478aa38c98b47a685c92650e8fb8e4013c2a0f2be877af29b6cc6b328a177cc0a7872592f8acb4ee001337f05969549ec00849f05969b5e7a70547569ae5042c663064a55d5bcf112772561ab60149c1b3d2b0adde134d0d129940e4089e55454d948cd911988004243b44622044d5348a61d8abc51e0a65c65e6f45f7fb41cb189edb7ca83ececc8a6d990b43f7369bd1639ac5a4155c8dcad19076648a17ddce46928c5d912790b442c6980f61184603bbee0eac0402ca92abd41a4111180cc13ccc091802e8ad6953656b94a996dd588f6d974e1486d5502814ba7693af4b2e4e1476b2b01d65d942a0efeef3406fccd7e79b9614b8ac895dd69453f6b02c7cbaf1d97362cbb22c8c7aa19a2fd3b22ccb0aaf8ba27f81cd2dcfda018cca4a9bd2534a69a5b23695b9be6fadb5ce1ad82badb4d25a2b0cb6a2d9a78956276dd95d67d3ee39273eddb8c2be2c8cfe4d9b36a530daaa1f9bca6682165f8419a5216d02413fe9a7920833833a456fd109058c760eae4b42b752a0dc9f411d6d6d7a051b5e38b92b9389eaba24b6583d9dea3fd7ead3934319106c587d8e4c597daa143f5950cb8699ab89335f84c064bd62a6ba02a9c0ca26dd6448ee954ca9414d727f0275aa59a2b0a16c02947b7234b02c9bcc972c70e68bfcb5a467db208727d8303619126402992ff4dd6f988a598093a392152891c3d81395a8e08a091357f3a1b5d1b9c646c300d17303096c189bf467a6cc01d23038ab6e15489181f851d3ef1f551d42a8923a3c35353535353535353a74e8d0515353535353a343870e1d3a746056a5d9d1a91361d69af00e150cc334b15335db6aad12c73376fb7d1688873db1a7fba14caa4e81a3930eb2a87ce5a39b43e58461262ae4a715dee4ec212b6485db0f1277214da35aadb5fe264b59bf6119f2862ed946c3ad6eb20cccd95e10dd90268baec53967ca34cddb9626839fe91f7b4513a5eb94bf5e1a581819c60c7dc0f0ace63616154a555cb0bc45fccc2469dcb56ea41c05ff403ab7e3bb218cfc7ddff77ddc51fe5d7b7139eb9e4e180573971edf1e46b724fadda2f42b2cd7acedbb28f8a44bce87ef28f8548f727bd23dd57b2761584a4a4acaa9621a1bfe22936e714893519e82439a9c72edc5a9742747a3f4931394d3afd3c9c9bbaee34eb89393128e13757f724d076fc82393aec9cd0613a613755fdcb89ae97b4f9484618c4e718fd12deeb18272c38a7dc0f856e88a0b96d7d3b75061c1a1cc2a57b1d7ac8ab5a00f16ab60dc8df644a09d74eb0257fd66e572c36962fc86cbdf80c3553808ea383c48001ee3372b19f7242fe337ab1c6e8e1bae3acd0d575d5ea5ba0c1c0400573d880dbfe13738a860c4b8a1c58a2013c523771afb25777cb082dc30bfcafb53672a315fb61e1934711ae6f44e61d189325c6619819d79f495539ffebd61b0563daade84ca4c54131657465ae982275dd19da09b8e8e4e5ca5c4d549461d2327df3c651fddf4947bcab08ed14d271d2b4ff9ca532eef094bee58197c7a2ec98e53ee12ecfc699fb9a46156c209c40903e28a470d5e7918814ee4e36af430fed8187de561ecc98d250f228c5e4294913bb12507730711e6e43d97903170d00521c288c087272b57f248cec8c953ae3c5d693252fae476581c9c21fd8e28ebbf574c599749b4f46918d23b943ff2d3a7979033641216b7b85e4c5967b12e77a24c8bd8622d911b473c7ba6ce9da73b713ad54fb9132574d3a87f72a576a513dd133b05fad0c82e87b49cd149e4feaa74e38a74e36a74e30abc713564a56558eec49639244485c5b0c4012277e2e6dd78d343a2c51761c5e9e6e9d69945bc775c983af3257a98c5a2a9105b0a39c345c628437c41437ce9236223117b07b17d109f105f342a2219e48cedfd1e4394e1de2d3a0bd974c58b1332cb3b0849e40c1a19830511a6a65344fa501841471379896cc402b1a53fd7106180c89cb9634413816147c4549f67e6d24397f4eb82b7aee8d45ef2ecec441996fce5fe7c22a50925a223cad01314f9294293d026624b9feea89cbb31b69cde82e5ca23220c7d424c519f4e5135649ac54579764dff9158fcfc5891a3613ab9d65ba649148c541a5deb596777727a826e42a49b944824d98488dc44a0373a6dc22b92441699454091682659913c512672356acfd979a73b0d439774aa0a3983662163ace02574872ed9c9134f21f7357be71222aaffdd898488f291319634cc8c22a65a362137cd625291db9b40b1a53f9f0841c1ca1e9eef4b1a66a3594cda853c0aa91ae47da88fcc51395d79651228a66b335140666a1ee7e7e4fa285d1ca4db0316728604628cd9303444be9025164d64938804223662cb3c7861c8ef456c51cdd4fc8c99491a8bd8d2e76e8f21b6f4b7db6490319e1051fdec3615b1a910532e72464bf1e284dcef20c4543f0ba62b4c468022a67710db07b19188a8be75fb8888eaeb6822d3c8199d8405d146ce6822b7409f202ff1851cce1d34c4598688eab7b8f3e72ec9730d3175c41772bf81763ad56f2372bf89c8fd9d9db91345c5595983145738d13384123d4284a1064e044d9c23e008e28c789f997693b10a9ad812438018bb042b63da0c7194fbbac194410b42ad71b307c7449d64287778b7a7e9fe5dfef3b0e9662531695d550c8aa14924cf1c2c3bc2863268c917ecc4456822163f57f830a9a24891bec4b00c8aab89716856c8162a08c68499420499a4496654c3c8d5270e0cbc3083b4287a767777239948827690c8254c41f2874221c2483004fd04fdfc502c836415f45dfb72466155da54042919423e620a9f385f26206f83e509d8d82c7d105bfa920731c684913b9bcd0f28acf56a447bc368414131413287f1c746187d76421782854e8f248b7c801c95d820284b4e869e5d8876ec4242cf4e7368c7ae23f4ec9403bb761dd835eda1ec210d9be46dc8277cc427b2fc4c1256eed022cb5f3631280605d13c658eb56a1017689d97143a4a7c8ee8d66182c57efdc77cc18c881a4684636a9a0830b84294a1292da74882fc81d40167a36144efc7663981053fba10d149affff9a9a9694084912d883f3a37b042981042bae8f5a7799ad1c1d3902e7ae44e13e718611007618206bce83a48d8060e0262535c31a1cdcc17d90f0390e56d8833a40a628c3e6647a4cbbf8832f4f2a4fec804da9823d8981d6acc6067cd34733eb4c8da8fb78ef4a4adb15d5e9e6e5ce217b1e593f235629638a4f9b352fbd4dedd6f363314ea50167746091595914e8e99b19161a973d56031ac424dd4d502454d1d6f762fc8c15982176ee0830023da6d4c549f6a60c3c86363c665c26857990608f20307a771e41ef33e54f9d2b02f582c54354723b47d08eeb153563643eb4395352c6b59166ca4481c59e28989ead9d34f744ff360938707db393251488e44196ed607f1aa77337faad66bc534df411ce47b909b5588cad67462727227b6f4e3ec79e4889c3187e496526b91687411e95e0983251c4464799774633ad5a3eb7913d38c46d6da8b623ef06a7434127d640a32ba27bf5b9ff634e02df80f07b1074728d14d28d3e88ab0195baae4ce13d4fd79c57ce18932a1dcda73b6b68d63aa8285827c820aa89f9bb9e9c3710f4598c9d3a966429e3b1665b673aaccf950a5f05899b54f80f61139033322cf6f9916ac7422893cbf5db3b6d8309e05bfef8237766f573e617907af63748b858c22f81dbc8eef2016c2040de9e085c44e7d3ac07fffb01012963dab4943ca9f3cb17c42c3fa137d9f083cf7378c45babd90d2c15ba4dbd3906e71e920b60d630fdea29cc803ef8db090efa35b17f54b570eb10dde3b0de85d748969be8fb010fb9177d169bc8b70ec14f8efa11c228b8097c6298777f034de413c318dfd085f744ff3403ad51360405c7d380810f866f5b8b28f40a25be3ddcad8192957ce876885acb5d44f9e7a2ae4fe09a70f0ff3dcb7abcd356459a3bb7c8dce872a6fdbb34b6dd2d0b013b7cb5f903b3b4d828096fcf8c81596f1e4892d54d81096488e4c54b73c92fb579ddb4ce9014e963b71623151f44d66d0bc42c76727f3c1ce8e8f4f94b93f55e820622193cc9c91800a9c30740089945276cba6ddddd285ba58392af14952440335c6ce52e4f0b109108ddd48f639e79c73ce89a53c92e7e90d6dae4c7ffdba82f0ba2f735e71e412ec2573a80ce51902a28930f597e71ab2c441c835e41313d8d02501d488f6992977e6cbd430dd7246392a392f68864fb28fd91158ab4a9e248c01a358b52eed9a9846bb8957d42bb6a0601cb1e13699ac0d7d62d544c584aaec32511d635355a1abc246faaa186ddb3ab3b6b7296742b034dfd0acd6758955666aab1709565e073c2d833e7264a29640f54347e20c2901349b0911466aa18166048a2d68b0611fc97d7aa49768249d2476f7916f8fab1cddb7ebe8be5d88761d9c90ed9926638c38fc68ecec9eb28796cb30131ba23cd61128a04082fe749289a2bd83a491ec20a1d612b1859a7e620b7d88084b7f58320dbd4c2189f4ed2c456ca1a71963622d1c62cd23c40ca080a20a3aa0a2044248a1831c244890f8f4b060618dd0914148924c89058e13990faccca14492f9c0ce1c4a2448649034431259be590645999bfb210f6820f73128260a934155d8cbe201425225164d8276ac8462a2ba39196465108c9e257a7a7a7a321250613be7f280165f348f5944eacc588247a684953953c26648d896417c8999a8b89a1151fdda84c7090b6c189b0c6131c1ac41c87a6b96481aeb03e9543f74b1192d83886aab890ad5c407045fa062b0d288c61796437070e490a6e8a8740d97b4450b158d0000000083140000180c0a874342b170409ce7e2201f14000d8ba64a7e4c18c9a32087510819440c21860000000019001860b48d00628607bdae1afb40ffb997e327352707c5c08a31b0c6489061496de9c085263ce1ee420974353c2c846dddaa2970138d3b18366d0fbc58a44e6f2755c2a99ec67717316a3968f5c2933e9d4a07a9509afff7b4d8c0072a1b06556ee68ca99b16357f77514715f4bfcec3981594865e7c74ef684e03124c027f2215aada7ad1c71c1d48158e77b03cd4fc2c5f47fca94f3a3b503a6f068dc3a14d770f61384a75e19e8350a5c96202734b3c1e23cd2563cc4858b4615c368ac40e99074db6073aff19db91e526668a4a7072c223bd6307be64872dc40879a83d1ef899ce4bfb6b618c07a9be7434516b19268ca6a26f768c7541a9fac9b1395b16992975874c8297f399d93b2bc8546075c5dbb88b3094877b48c6ce5ebea85d175ac86f32b60086657cb4dcd95ba2c0381608813623b9345baa6bd30eb626d18c83cbb42ec939fb0fc90d535306a2a58205c5c511a1a5091ca9ab75e852704458f038fae6d9bf35f610fee3ae4ea846008f79f9c09d96d92200c10fc09486385854f58036b3fb8e650b2e1ed5cad70fd2614b82dbc82c225195a5c037425d75298ec532e34b3dac180fb07c17d37d9907ced04282a239fb43ab9e2727a1fd3026154f51b048ac9ef95c035f76782d4c597402d3a9d849ae540749b0f83a42a8d6d90c57e6d61464600f2a1f95574651e19dcef569e2995595c1efee95463d23adb4e1dc57d1a2de6e3917041a4677841e0d66b28ba49bc5175b51c8543c4b9f85fe166ed878dea8ab12655e8557bcb1afac9ea702558d69e64262721add1b9c861405efbf33ff7f7cd13d4cfeb2c795ac646914ad58a8ccfb8d15c581c7a7d40f073cfec2318cba85327ba13f730c068da5b202a9aaedfd3268591242487227c417d30b10c7591f0ac52c27b329bf3c217d124c148ae91b67433e3bc51c1ed8744d2289ab68817f53249a9e0e9ac40a71fd9266f51df2eee8a13697d6c8a226179a17e5640b9319b345329d930945838988a6d2b1c567ec232cec72f6ac0c7b9341589517e5ae624c635207b53de01dee30683cff6824f9fd8cb44cc91d345bed990a0c8dc0dd2545aece2162ac058f5990a5906c64d92d397ac8303eb0409117a34282b1c09f12035caa5027c31cfcc875681038f0e955ea9954fa93db312722d52529df8292fbf3ffac215e7c5615f635306ccabe11c97c65045162946e63c9057a3898b4e1b6782efa7c93a4ec95be695438ab73b740398d623f8d223a59820819336974526c021c28877a57bd7797f39353d1fff4350d8e1cae2164946f6acf09304d23b83869f0d0486503b7be90e812aecb82af5ca668363cd0a569900275cfce2ab009d49336b63c6a101e24932b70b82e239e16bfb2795cf7261e54dc7b53e49cced8d0e171a252640fcef3768e04be57755ddf9627e0143dcf5d536e1254271380209a0b18a602e8cf3f9c4d1efe12272acd42c954ed60825dca15a191e53d4a3a7385ba96ff26766a173d0794cc660df0fe26156fc029fab5ccb1262bedae6bde670e071cc74896c7f38fac9d5682547ace9172d764fed0504e834d9629ca27d0201d5c920e42c882c6485eba7d9224c6940e44220959dabb4a007a6eafea42f4b7b83cbf27bfc8705919388248198133531ffadcdcbcd56f9abc1822647196122029a7be49bc44c70f27d12df41546118e97af53939bf11e1cebe8340b1782d65e3b5875633abf152357bd10787073e6740d6ec3974ff5b4e4dc5fbab4a08ad7b963ee6b44f8f9e2fab6e2a120ac1ee66b8079d50d8bbeecea329d7f8f4cb4abcc816d77d9a3a407f0c786f636303394212d41cb90aab2a242cbebac4f6fb8b68f862c64f8995485fb4d4c566c09c6c928cbf6f4f4f775522e4f0671122003f418dfadeb5849d0ce0d775d16b852b500ceea511325f59d408aac02c3eaa75fdcd034ee001821539ffc9d73c79918ed6f981176616bd19494521bdc5834977e4466b16a8493e390eb3dfc251ff5ba78d2f1fed70467b114f50dfeb7969c8561238d03f4af733406d58a33b385cdba9062060e76790c65d4e4a652e92536c0249c3bbdf6cf9bcfbeb8a2317257cf0f0bf72bf651548189d29d586198623adbdb8329db4b4102da44fc92c9101fa8d40734f338817a3873031052d18e4e31cc6ed614822e4ee1fd7330e85259e1f6bbc3a49d534e4ab8b95882641385884e24e4cbf0f57a1551935c6c313910e4d24d957e54cd17522035c54238519f022732cf265b1fdbba166561e8f75912cc26d2389e762fc476b5eba6aaefb2cc6d87fb7742ab16807a8024882462a0b81f5f003e853a8af3760165530ca366954ac30601bb138321b6091c7ab1771b82736818a41113157ac72e446cdc877a21884d9e049dcf0a1c8ec227d75518a4baf78bfe9c60c8a51da32312b2bd00247b101c3a0c83b8677373f1b5cb4c527dce9df333139d690971ba4d263c570d21d7e14e22f70ad95a12d3710215d0c4ee0bcd3f475c2aa0550934d93feafd5052ab294d3885f34e0a33c1aff9f8442fcde8b8288da1144f2dc59cbd148f0ec47199e49684cc99fbf0f9d34cb47e86d0b42124188a2a9608fc1412ea4dfdcaad5a170a62428036542499f762a7368ea7b4ef9929636000e27ab13cde83b4c686b35f60d700d9b92b8c81684a068730b64dc00007587f7651d9316341b6707be0d78ca87c031bc46f60ff0a6a3feb8fb1b15d0a8b3024a0c20cfa818ffd458f5f386dd84e71df6b8561f86bfa9162384394a0fa5191dad7fa402ceb7eaee813d7e708a166012100651a13c761f4fa95355414d5b00e99116730ed2ec688d398b195c8691212905c3604e4b3fc171aa9098f86281bb1f2fcc8b79bbeb33a876a292bd1615ffe92f6875e6bd4e92e30350bdb859a6a6c30ed5ba14932b74540e29e3393735ca1805634b22e24f749396948c614e402f7221eb8341db72904d642b8e90f9d81b4c19e7dc8c6b9044f37755eaf65836cb796b7d5b2f7a57e79e95bbf458694503e43010ecb7e15194e2e5e8abc69d410e9bdeb47da050e3cfebed7bb28d6a0eda9dab852211e3f1c8aa61d4a6b87ecc2c0cc3d96200e086cb06a057355030262593a3aa3aaf06dcc05c091e4115da068d87211e456e48e8670f61537cffda54792aaa078bdb8a58f804948875811c155f0c5c6bf021162b14b78f017ac709d942374e81675084201990bc04711fa700eae9bb4139622beb02511e0368077aeed34718113eebaf255c0cd9e84897a0216f8f0d80439c42ce08b641eb4609550c1a8dc839c583a72b46c259c364135cbf95519ba2e6c07bb1126cdebd02c29e35c413bc09f3d2ab7192bd49dd8322d8c41e4246273c63db828c8c4a871590115d0c47233742654423fc0565dd3f745f59ad2800020eba82f74049a69fbd2ae31f4719b8744594a360c7053e621819262fee971119ecaee6417c5a2e40c984f8ba0511ce55283841a48ffdadde6034f69a0481da5356d607128adf1239e86c8060ae2ff24a230a3cf6869e0c7a1dacf47a5d7b523542d17349c9f5512b4ed8d9273d23684e6c953651c35a4aec9ad9d232fb29ec33f26213e242950131f49b21870cf5d13e943f498db245856e3e20631ef9e438027d07f35b8f216871591b2498410ff4cc5c2a49fd6cf44928ecd20a970d1716e4e060066dbe4465c5475512cac110eaa9ccaee36bcce727f7571b18b702ffcbf630e2d839008d363dbbb8c04113077513cfb21d616b9e8a57975615e217b90194a05f6e20959125dcc43562ac889ef8f15d87f61d35acbce8484a008f1b3addbec08bafcf7c5252c390ea0102a4d8ad9286f1c33b23890c77628a1f1ede59e88e41df705e8c7800435e2a33026f0c481eb8fe318112365407d4361c47c2fbc6afc9ff2a0eab5312528a3a090eb894341c9e9a56c496620f617703bd1dcd92d05b2eafc65caabc42ce362a121fd3c8fbe59c3263abf8dd979c0a25ae772e1bfb55a153566093f4dede7ecceeb347b07d90ffa5c990a1226ef0398666faea318efe3bed2be149e85db476684276718c4247e04f7970af61adecefbf5d971c639af891e5e079eb98e8e3eb27a8d1f540e37fb815a27205661236db8425d2c749e1a7fb1edad2cd9a9ce07ef05633e0be4c8018196f2f738e1f1db99e8a3f21655858e9309016c50007f9f38593bb32b81286d4ee4771a64bbc1b59e259b467a845383acd5d51a285d68e63e56ad0c58c1819459aca1af798c53cf05bf7bbea429fb01759ecf44521394c9f69b1c1edab754c8f752eb80942976c1f2ab15ba9f956d347d036d9fe6b1674dadfdf48c127bb2807d227612c209d8c370eb07c5297abfb93d671c7969004f132f4c01a378483b45fbe74bc9dede740bbc3a04be43c19e2f6baa03f662661e28360803f26592a234abb6c8852f19dbb10db288c75f89ced5494a66da9eb9e44d4bfde85244d64702f1182a9b867d22b0cd8690b37ecc1ea0532650ffcc3a03556e49cb0ebd03d5851b10fa199244231dff624088c4a9f6d8f622b14646a672e4c6fba9ea28bb27e6734922b49398f15d37ec9b6ef7f94916abb4df63e5c802d0ef67289a5d157aab46832c5f281bebba26b1228b94baf9bf8f8240c3088c86c6f089da104c7106f78a3d13aa6918e5811cc74508c82ee0b472d6e55421d64ac22350535c4465ee37cb3d1d062ad3bd5e960ccfd89905b62684f9f063c239536c8f370964f866e7715ad2dde7f03729dfb203a4849d3320dfe406bada3363f2fe0e9de3d9a1928d06932471682d598db0abd0e28bff96ffa41b966c8b162e335c1ffafe241bedbdcaceee3f695c9bfe9381f35cac15b0c38a578a0da2760fbf93f4eabd2d1ed969caa04709f57dc274de1b35120e30cc68825660a31e2a5134f749b2c25fa4273d21a51c7a8aec8987a9780c1a4c69440a37745bf360220bd646fd06a710c3f9be977b793ed9a10a89372cfcbca3175001505b4f2dfdb4dd6c00fdfc623299202b95e1492935e4eef5241c75919cbe1d62ef7d49dcd22beda0da22c26db1a98c3250207254f40e2ca469788abe28e60541c6219357e7cb6058521b352756f68d28c883f70310fd94e01178ca2925848cc952f7b55fe3f5b2e00592b4a3e900032d46d1bbd40262c05a78ba3619e26aed24bc4b681ff97e0515e909e26fbb8270a4c9d97829df39d22f1036834d31a7b06a9a398f6018cca5a04df981a30ebc332ee016527a101bf9f537ac8ef50cb5bdf83d409fb1a96d72608da24b9993687560c33879029266a53b397bd6621856e510280ae19da8dffb1da775a51857979aee9c1b2812f7287873e8fe6b8fb3f95dbb15d233a1f3ed0a818ff671c3a395ffd9f954674800c3af45f2d94b8443dcc1f3104766083b71082aeef22f40e8abab5208af1f79ebcc19fb841b0f3c0ebe5ca636a3edf111e79404ff15cdb62c9cfc5a3d39d76d5f8297a13fc11d8da2e40ddacdb6f4ec0b52bcf098d0b544405b1b243131c23c0d48e9fc7e7c5febaa2a2875a8e5c421045d6dadf5b904944468f5a0fa93a71b5c41dad3fe19fda92723bb4dbd86e4f7f3429e66fb326470daa4d047c3eb23c1abdeb3bf91a16b67e0fe3ec023f76fc99c329c2ba05d75812b313250beb0c05a049c04dd74f9222db0bc29c14a2fddf76a60012114f913498285289cf7f96149c1e66bf8ec66201a80ba7b479d478ac6fe18aeaeb96a692bf36547c78ba32e97f59eb52fdcdc7f1cbfcd9677cb896fac05b718cdd3492336468dac9da2705b63d08ebb5cc8a9e52983358621c5c3ab6701d80e7480fb10966b6a3624d1a1bf94f7862477df7abf62af5965f984398216d8f4930e3c6160c0b9c9bbb771c5306be56a5a10b07973207dbb42afdca03c045241077a37151104085067f980f54cc70544b16187d45231a718018fc8131182f3c429908e426e7b8ead2439d1d4761b4d516dc89a71b6cee25ca89b3083944b19268bb4b51a2c3d49c020d9b1d1592297553a38cca8644e2231983fc526bd7f9a15cb36888f5f08a6a8e21df4c959b1f6b6784d8dbf7479821c47c27917dd804b79cfc96986ec1df61b8bd04d7637ab6b39469a773acce47ac4d34f072c54466c978692a1e3c85a7975f90fbba33cc38b257638f1069cca3b47ca15406fba6af7e8ed127de7f3074331f232fdb18f12a86e29e7f86ed692c710c6ff74e07ee390268572264eb0df42e504801827821136c2c20b0cd670e9e92b5fb730ecf6c6abe183253916875f1c37c40f035befa1050afda8adb3fc28458aa113810f2922229e229f08c299d4a8a8ce0ce1866d1a0dcfe7bb27f21acf24c43033d295ea8fa097b94c83f3816eb19045e6b1b39ea906d251993d53d55480db661563b1ec6b0cb36d03e55a016cdb89292da34e08e179e2eb28818d95ef5b881cd035c3dc559934b9d8bafe4bde69b865cbd9be2ad4ca7fb234274d95a2b7325618d106e54d8b4ee736132f0e0854727c15be2794b37c0414a54d89e60cda5c7189f213a28025646c555f2640759771a6526538db1c2a41c3f95e6e231ec59e6e0c64a3f9d590bd703af29e7546f8c8a4d680e8bc4a756e0557bd719aa0a885a4583d9b1653f6e01fc9e65b225c12333a3709809e431f8327cca9efb862253c64ca2683773eb78eaacfc1c07bbf07cb0b987f0bd82f2ae3ed7ccd3493b4151d7748fb640b8998478008edc8ea9c746ffa54703f235349a9f040e6866b1989113d064d0bafea15a88625ea7d09c923a8940b74944c73bbbdc9ca18a3ce4ca53102436e24a8bdbcbf7e2b9ebe3a9db201bacfee7a7fc532af8e73a1d7f5917f63b52bf9c36cb5da2c5f4c8d90218b390ad98579f7f661c2afc818015eefd707c9386629e8f4147ceef50e4a4a9de77a574980947bd891146c0f88fb6636c1025c3491ad48395fc1104b4f819124354c26347a5b03230cf0c0eaf54269bfbfbc7cf3ec23480a6a9909c1727db3c0392993c332c9efef8565624840dfcdfef80681c5d803009cd2abc50e0d33a5c120ef1b93ae73756945ecc203967f25598a90c845c2049f5fb8da3bb952187946c820faaa7bdd84f24bcf7d6b3cb630d2302aaf334651025875b762363f3f0d896c0fa43bca885c1725411b32619c47927d7cbecd6e16e4bc216bb0c6e8fd85fcbd9786abfa4fad9f0803ec11dada9a971a319c17e5f4266ac96251ee9a30beb28ce8d6cdfbf7c25596a48ed12112499580306b3425989eb52ac3439b96916467af337b6d6831de854af6e81884c80e187b9975ae6ad9cb9779fb310ffe17c9b2efdca7a835cc3532883e36c444b9909910108a65e033abbb7b050431f5ce27049108b97f4a3d8b5efe5e2fdaaf340429b8e4c548468f1bb312b12832c1b8dae39db1a438b51bb4ef492594664aee7e694c278b8a7b77de51fabd9fd938265dd22289ad49c5d340939f9e950a7e0d14473c7448eb6bc8ad2131223cdce37689592259ed4e6dd0e32e8a6344f88928ff66a1f5136c9c4b980693be0de0af7fa5cb0919523d55078a17eb8efe8f6b5b1965d35f5ef237eb8e46e2336cd437c57c892393259f241705b7bee0dca7b7a80cd073ae8aaea5d8a6dc89dde5109be94a2ed11b35b4d8cda6f8d61c055492e92517aaaec534ed527c0fefb482c8a2288de0330d6ea5094ad2838aede9c554859fa0c22623d18fd120166e49ecf970e63a1357ef70a7c830a8ca64cba4009d94639b29e6c95e3925f522bcac6b91b4d5ca75ed383f4dfb5063cb3bef9ca551454c044d29f618100a7ec1a78ab22582ee6472829230fbfde79cd54fbe284d893b5e5e2742b60f43b91528f91a1da137d64a4a9585be30b30e8f77eb1be799d8097a8990c6ec04c9d6f7f1f99e8678cc4f21a26f519aa3c3a16091fb9470f882a95957ef828c85c6415925cdb80b4b08b706e25dfacac1748d2cf3d77f96db445be5aacfd4ca5e755606e2601f042c45787794452f91d7a91bb4f4c5e42861d542b75ec6a10017436a247dcaaffaf751cc0389bbe19a40991c3c33ae166f0617d0ee1a013c49d5c4159f1f068f1456acbf894413f8330b027bc5845d93c84a5666e2943cd39b0f4dca5e87e38cbc53aa5e904d6ef70bb21f0b6b2b3244161b990d808a66826d04eb60f50da8d071d7f47e4a5c3fd2d9f7b4e1e0d8c0bf8a96dd7bf4b0d6c10ec1fd0aa18f524d97939b2749e4be3edd4a603afbd8be4312467102d1c5ef0f2107d514bfe681f5d5b64702bf0d4aa00643d1e13b30512ab8e61400949639ce12063e6dd402666e3ec437a24aae3e864835d4fc6fb38542625f60d5ab26bda6d1806e4b70f85b872819906c3a43f5a23c084962958adfdc1eb9a84ac70d4ad690306ac0c33ca56a76d1bad5bae88feec72256ce13578550e1a6b895ad964073ea0c2d571aced296e48c92e1fb358af3f810656a0da77c47a681eabe1c36069b4c7b0299b0faef33ff828eb1761f7f75e2a9cfe921d882ee73d262050d383335e924e0588ec6265f1b380b7b3957ef0c9ba0af2a51729a0d16484c9263d7c2a8e960707416ace6a1f31450c8263c3531beb2ceade9f05737180cc2723685368ae1302cd054111ad9e82bfb0ac0054c1946e81c720c8f83345d749d29562debdc520d595dff094aecaa3b71307017e3665c50f5044747a4116feeb6af777ac391d60735d04739d6b6ffe42fe970f01aedf0d308d965cd4bcae1083d6b1fe7428e8fe6e6003702571e299da69044f30f6547cc7565be556c517e9680ad87fbd339b48bdd93afa66873ca2f0e3355801d64d7659a050b7704fe37acbb43a237bafa8f573facad95cd93aeb74e850ef383f866db75ea8672014e23d2a1f217e984d2e8c1ccf85dfcd9837ad3143d3931867fd56d91f118d5888c04267a79cc408890d8739bafd490bc3021d5738497ebb59952fb28d18627692b51125a36858dd7f121ba7b8e2533fc70e9959c0188057b24c853c5597cbc484fcc741d16d30d35a69abf55471ec44cef6a7c28e04134105488fa66a503d20253310828d89aedd00137d1c24a6b311603104696dfce1ab0d69729d676acc8613b8c2e718e601debcec66092ee4727784e94355b6701d42de6e0605d90a983c6635a74dd9027bc5f57b969863efc1f6328fbbb1c8f2f1237382bdf13c01b1a68729fddb928b73a23e579b9d60d9f6f6a13348218f7bd20819e9a9293ff7c2489e4a69f98f77b12eea61bb57df466960334794f41589fc96261f28e7cb3c807808f18adf09c4fd3e6dde7500590712400b0d3cdf905a24b1c5bcf9a63134aeb1827d71debd7cc6dab84187c7e3c379847de41a796567144b00893d94b39df68efb733746b0f8028fa83640f158ed8b40bda609cb7f53436cd55f9499a6daf7662cdcad645e721b0813e3f4bdcb57fbeee427ae10ad2e9ac1e852588cc261603e8b984f5e4e94f3e97f9d8b4f6bb44d2b0ff7e9f93468a8612a24f0da13b94874f1a0096eb23763961a99116ce80cd3cd1ffcefc8ca8b33100cdca2f4b0091a4c05800a4a60914debae28d5707451154a325447d4cdfd6eebb9488225ec69cdc962f93b7e1f952ef5c6ae8f8326df8e362d6c1ea2efffd7579681734c23b637824883943b588a757eef5de1181081c07495c7c3ac01092c0baf4d648a54adf7ae347ff9c6359c213b49a81a795f64a14400b5a8fbdca5333b80122195d96709f8c9e3042c2063c8587e3ce9fcd1c3ad0527144b2d99c95cb1966b8c2648c9c11c1a30ad7c5bc46dd168aca9d4527466b2862991613f10fc96b8b225b8bf5a0c941e0a7a47ac3b19fa0f02b888ae93c6afb0729b52c1a3bb038a8daa25b73695d8ca4a960331a65267c0c13ccfa0f234134ae8696bc7846e37c744859718411a7658fecebf6c371d1ba114dc6d7e19e92e2bc772bbcf076fefa78ed5bf916c7431490e0c49801d2eeacf3d8e9f6bbbd2673c7836c4db574eaa25fa0626b82085ebeb8721e98dada40716b49bc47c77ceb46a40c6ed8e575e75c3f96c3fc186e328050d687e57fea9586bdefb1f29b32fba775fd5f7b2bff3f5f109d243b5f45516ea67de051c45719003d14b7940c4f5ae016faeaa00e7d9cf900309370f48f76973a6d41817cee190c4377c18e37639673936e865ca289b498a64f1457e8ad064151e87ddbea0268786fb15624ff1b5027fcedb9a1c7919856653a2fcd76f5286965c6b5c284691e94edbc98fd19cb81d92b1f11f9202464084cd3a645814e0d0568396e02ca72e64709c0fe891678b2a280d78ec435b5e65cf80faf27b67b31ac960098bc9f1539f5ddd76829adc8c84f529230d97a601ff8831dadf23a927826b169b4fa450678ae6876a81b05168381c21ead4a65a8475b657e12fed4eb040ce8e4358621e61cbfd79762478f948c4c98d90867d244b10dd3dd917c844f111f683a39922a2ed0c5665b1f40c17c21c4f5e34d4ec1202892fd68cdc390336fc7635f067121eeff5894d696bb22615b9ba04b08aaae208e70056b5cfb3c7f037176951f0b42719a1871ae12635ec3faa2740cdf1022a6c7597d0d09f243a19b2320c4e4d94d6e1bde4af0e310ec4796b178af415b80ba4780f045db65a3b8484f65556d88be28624466a675d6ed55844afc335a9b6a35c14d5218257b5bdb2bb28f481e5055307a4792da78ebfa5e5bb1b8aa77c55aeb27c515ce57739dd690ed3b77348150467808aeb64dc2252d8a8cd83222b599a173363ae9f40c1a48ec84e93c0009f2acad20fbf9295d03cc597e28ccb27022f861b8e0d895b28d597ead89ad120a269cfcf71567f984c1e29071bd6e4a46f00bf793a16770f899f2c5080287495df92945972b825897c96237b2d42beffe64621e9068ea3d83d6b75930d57d40ea0fc88dd22e5d0571ad8c181ebc05d0ffd8c7aaea29eadd323c57b405ca2c5045ff53aa3b12870b42ad5af5c69376ed4c128213ae66dbd9ec3d4444fad85a7075513bd444e0f8a7250dc0a3741f2b4af687ca3251e9acea3a5687e4748f00a06e4e490e088e40ce20b732a3e7ebeafec9a6711db1fcec9333660171aab313aabd25696dbdc6443e820a5f1ae43a3a9f411b321e0ce47ed072aed688ba36cc6052fc231427974a93e83d91d25ae8d9322abfbec95a875df97fcf56bc679ac7f062584d3f244d6e28bfaaecdd78c8674e88b55c2c97730906c3ed27d1cb97a1566b9f0f00ebdaec9019df914719de311e1a98010012b04db445539417487928dda744d5a72daa1986921f625d0282e94fb8e671d8bcb76380a8d475df23d3d60e7bc83e2361d860be83427592e15864c454eafee81397d5a601ec49a5fb5e79ba18722bdfd556f7978c0afc7bd15d0b01b2c0bea72af2383dafdb9f2729b2d976b71f28e8ba86b8c6e0bac7febf26b6e2550aa2810f2efb39781981ce990dcae355caef5f80f62824cf324f901c8011b404c708997a1ae19864e3f11e050959ec6b15aca471b17fd62a46ea7625f4e1b01cbe02b3a511a185960a715306f3b6658e3fb2fec74a1da54ae2a8132977bcf40759617e3094457ad059f36a2446e1f4745ca015cec3ad6f1af4c7c899d258af398b07c484b9040e249e0ec07d0b7cc1a4a405824a1e440cec3814da7af763aa0c42854161e3979b5d74de5a4449886ff8c36e22859072351901993ca87422fe7ffe4193164b185346dd82b89a2f7ccf60237c3bc52241cf475fa2c073fd639869f07a89406f75bf97f1d6a2726f1f6e065e382f700c4b13977b02e26542bf9608668310175ed9f69ef3004ab41a3994319a9fceb397b3eded87c3942df923a102e913e92d9ade92d6e39c7a95b3de92eb6192d5d3e2a9e81df30a9b807a51a4ee4e89afef2438bd146341b5b0c21b863b821b0a6ef4c36d104ea08f9d294e93059400bc9e9fa5387ec31f9f1b81a21e331fa0b96054e9c554f867dc40223af5cba662018930334467196fa3ff599e0dab1522c9beacf43387b0cc8b34f033627e81439d3f8fa5ee3729c3f57ff6e937d0952150ef75c728b3e6d67bd9843482cf48f9aa1d004c24977b56aa024f7518ec7d283e83ba8f7e511f9c0f50baf1368d791f805f52e6b8822f98d9bf91bee8832ec104233cc007700b6cd8d8852a9efcc9ff5bc8fef8d4041207a7795af176472fb0da6aa9a85f8502b70b41c2fb70ea286a1424e1c84537da386753c3c2a96666c521defd8be2082768e90f1109e677c4c836819cc88d2ae85cf5431a0872ecba05c421d0c3ab473c0e2c0f7c63a9fc55839a70b0b93e8e51c25432ef1032f8eac30e43e3aeb7df872e8a87feb378637f7104746ba3b77e1a6b3304ce5aa88ba42d8532bf064a911589b8eb8436b988a4bcf4174a2066e714d105a6cc8ece993df3a6d91551d858603c853e88fe1ad59cb64540e0ff0cf53da6258c87e5862b8d8edd13fc1e4713fd16f2aba3bbd356af62574d3c0139fc55559a6066d908551a34bbc576e5cb688cbff6acd00cf49cbe1adb5c4397335d40b0071e247c903eacc0e4a68ca3afb62e4c4a650d775fd5e4a4dab5ee27883ddaab2a1c0b5dd7d1998270e37ffed63f220eb1c6d9915a78c5cd2b3954c3bf0bfae030874e8fa17b5bb59c141046e4c724c2e20daef4c82e1306484923990bf7185a4bb8c2ec5595accc2e99605b865ba8dd15e0891b57da8264a71ab3fc267e1a32d2054d1817993d6b596656c65eb635a8cd0c2bd255b381dd6ca1d73753f20b59178cd1bda4d6a612fa15fa6f62419f5ca5947a5f666d32d790e8d60cd064373654ad147a47f401cc37d800311dbbd2ae7dcfddd1f2f8d95de384d6efdefc55e3dfff87a111195be01a1b59166d8016ac40695e8b50e30e34f1ab02338901ae5f101f011bea97f90386dffbef87a6de33b64b6a6db42cdb134d1505cea1aba117bff7927b6d32c4d5720c8182f699c1d3cd073a68e6ceeac122c96d1eff180ece1303b0ed8c254501da78a1c2f1abebf8e9124fe81bfb08c84469c200b715eef0d7ffc7871ef4ead83eaeb961d9602769c104f6d37284f4a7a5a9cf13596bb34fde37151e8df0f21970a3be913f52fcf9fffed0943e4fc59275d2291a87cbf3e71738b359d5b7230754b68a4ce85709ebcdc98f4e6f52e66a02f01daaf1d512a416a375982d138696206c89f31568c8fad663078d804bd53f657de6c7287effff7de84aa063fbb8e646cb808da45d12384e6b84d0adef87febcf70c9891195d08d4e6bf58d1a21ae983a01ff8a0c4b1a59970aa892722f4976139dd51304fd15b291e05178d4a63ac3655845f23bcce9e1ed9eec8026ec74da1fc3cc12fdf59df342160e1829fe90286226758fdf284e4d6e286b1201e2161c1a7534bfee7885062c1960d390de077d0ac3d5895d8b43af33c4287b34e5fa7841ba73722972d566db4d9fd28276ae28c3bef3236fcb4c6c7466057fcbb73ab9b2ff250df546101e259bef6b91b9bf6fe354b45660c5b05c36bf9e78c7a9c7269cd1ffc7648b285d729240b1c183bfe36c9dd348def7b44bceaff1f1b43ba1e94878722fd1744e24097a2bd67a760ecb8f31b1d506085028f63562b716581ccce300ed36f9b87d78498bce9cc63ad17c58cc6da32bda4a63132ea3cec0de5534c770c4facdccfb73f97245a2700929129aa3e774656b18ce47f44f5ab4a89ca086265493e1f45abdce1cb727cbdfe9dcf90da5484788a7b330c3d97d01c7bb8d4e051b6a87159079599b4f04b15e7f08ca4bc7cb2128d018c2ef5ddf4517ae5f35f05bfee049eff138a9bb8a717997c62ceb77d91cb9b1f517c0a5aee084af9f7e22b586e210597d33a5d392217f97e95cbf30337dba09ff5555631668a87ece94740ed49f475b82647209a400fba51baa9e12efa475ad5a456359be11f2f9e7180a61dc9943d5ca236b5f9b4cbf7fae8be445cb1d189c310ae8275e6b24696d1ba5a9b2c12ea22bb10d80438bc79af361c4066492b3d4ee12ae3b1d03dad55f78803a395e30573ccaa678c54cc6aa92cceb5d90d5a90d4063f7794f3d111c8a413178bbe4bdec5937e668268f2b3dc8fd5065313795b6a25167e3aec9de7c9bd1d1b10b817bc2c34c375fc71f479d3051bf6b562e013f9b0f99c2f67a2278e4cb75a7d2be0c2075fc8b5f33857a54ff423922f4acd89f8ce79e3ba4fdceed7719ab91fc361b647b79dd671175dac08b2e50f0968619bac5659c591e35c8f6d3e9a935b8ab8d5d82e0f57bdf9fef91b9fd6c0ed88a20571c619c9e5eaeb955db03e94eb2b1a435876d38f8f767c4d0366ffe8963e8154ab042fc94f4001a5397150b12e169d154930ca42980c41945b31025369fc40b839c275517cf4268e84104cbb83a5e85478d22181537209e56d0d37fe4af4d162ae371aaf6f63991f8233d77dd2a9d2d66c9391cf7f2af6f972b82237780303ff0acd31be0bd5eb1f37711868d430f000d4a338330d917ddbc8f059abdb0581005e13fb645f1af29088ddb3457d2259898c1fc54c83b94bf2faeec7ccb34f40df3511c88549703c0b361a7b0c3261dadd19f960e093f01abc235983152a9e14ca00e06d9cd19220170d90b8fad7e87b68a6b64611dce2ecf05d54ad871dd4684cb57330e5c0a8b0350e1610fbc593125a338fe97949ab427fb3ae97f33d08e12e4d3b74302481691c06fdd3ef1ea631b3cd7ad9ea1dc2689081e4f609959359d9c5c4ce268ec23f967cb2ef53af6f9d04029f05f7fda2d4a22646cc26cdafe5c0892012fa0a6753407fd671c1a59d73890f6481300c04121e5ab354448bb34d9870c1dc3d69c75abe124010becf4b456b444d0921ce64463ab12e933c971a930ccfa1678c86b9f7ed2eec857214f463b43989008a5c165b0c24cdfeb7e1860990b7135fb8319a7ccfd9a1104ff3ad6b5d1aa3ad51a734781d1e33dfa5cdd0bdbee9b26999a3f776fac94cd39ae19914e0aaa0744eb422e9b98d84ad5fc7b47da148c88926c916331a1d51c616352cd4111ba3f3d77949347b844432210af0d53efeb1ffa78d30b3820a08ec9913c171207c56a86a4e3ff364a4e8774fcb1c30c85fcab65f4ee540469e1e85b4f03a167940cb6099cbcd566e6197dd6f7110bf087ba164479c5fc203fd6c4661836b4dec8808a7597d8ccd43dc086d3c0fc87885546b21b1c58df4b1409267f82b4c196d93924b4bc5885fcd17d97b2b751791fe64fa21c1044c9b905737769917efcaf510a2b257a2ed5d197f536a009bebfd82c5a3c754fa5f365daf2a075875d92c9df63dad3f2c8dd977803a46c7363eb45dffeeedd59bd2fab8dea7a949aec59f3fcdb58610d1ea35957e249b3ffcf39491e4cf50b9fcb75815cfa3a6d7b6eebfc61e8b226b054f1347bff07d3c681fda6709ebb1f54b5a2eaa087c7a48ae7bacdd8a6012b8e866ad8e25b5d271d11a369462582a686938b79162ca33b8581242c40f8e623d274b6b030808daddf556787808c28148457a3f4195d5bdd4c60d8ab9d2add4fb49c1d5ee8630813ddd8d4ccac2bcb5d479db2f8d0756acac1e4b84db705d3fc54d0f78c16d343ea84d8e4f6dbfe64bd6ef6982dc2814e19dea1e8b442784e86bccf37118343ae302e95a26005381d43b4846e5873e1c815c18f456270f1c5ea4fb9601d01bffe4718745561397f3e132f14df213d42bae99ed8b13c4bea4fdbe026ab82c345d19429aaae2c00f78dafa8a9799486c4faf4f487c91b1f4f0de324b9f580989d0631f4f74cf6d63f9dd727a94a94db30029a06770f11be353ff5e71d3635d47592fdfb1bed6d1cb940eebca73c01c0ae17d7b9b803c7f9ed8b1e333aa6deb53a72b804843a31d10fb74375d378843ede6186da44ee83be5e8de78db31e58a1180f0f7957d4e21e9c7e5ef046f6e2bb291205ca674ea22915aa36ce9faf73a856b63e010be1d8e9f5924f6668d2f144c3f3f35614dc808101c0793877b3f53a7f8b96c29d2c7281f45450c27a3b6f62ff30bc51d3667f1aaccd18179dabeff0746117c47247a905a4b564c4bd5d1e5cfd2b9667fe657498026ac77dfc22df02398351d82f4982f7741a6fc9fc790bd3116b210c020d7941f7404a4733e950453dd04a8e9bbe48f3c2eb3ed7def4be2f172fc69eb551431ab0e9b530c4eefa7b5d5e42f7f26c7973f3eee7dd5c65becfe7387d9b2ee3f6fa907d19c2df3ec01401debfecb1d266eec8283ab30401d17d462a667df3e920ffe9af30e2259ed8967a65921988c96850e4786da3f083449086d50c02b9593441bb72ba7c1b66455dfeecaa1c79f021e3ec84c1b580d928a2e3901fcc05c2f43eca28fbc12c3c8a33a0ab94ea3e53f52e270c024494b9010c2fc945c5e11b3641c7112420be99459fdc2cc9fdb7336bd5e13882d6c2954819a04a05861710c4eef55992a978acbef0e040a0408c836dbdbe4061a366c36fc6068e8bfd9c7ec889af00e72a81109b0482ccf257b660ee7b5d20f85fa8abc3333ad29e1a70f8d2221f6989fed0a4951e3d663374e388ce0dff6fe171507e53cf8a8fd3c85db761ed251fb599d7d7515a657a0e2400f20497fe7f51fbd8ff9b1b8c67c145738ca58c57b2cf38506a243370a3c71fa9d5637a337855a8b78cf610613c94c419447526ffe84d9226c102d2f29daba57a780f1831d10264baac003bf984b3f47f18e37c37cd455781d7b3a6de687af9dcf18ba8086f1228f9c49dd73818793ff97715ba3dc67693fde399a8c6f0ba498ba420cf2420135214cfdd89011f710fdd80af0f288f4061b62dd24175967fc9045203f01e7ef298092ee97d64b8db07fbc335acde38ea0e201f83185a3efa0d87aece6dc907365070ee5dc5e0e7c5034e9fea5a804ef7eece687d6f088e7adee2275b2b888bcf0d510b3ca98d550fcfb7811306d8c9bacdbe6109c66355965bcf819a678996009a8e8a10b41176a1e368bbef54ff0a128b2e8c2d4a52b31243704101d71beac519ac3f10aead0e9ec6a5e4550fad8c54151f35e5e4b5bba53fcb20abc57c413a2607db266d6b5be8511dd1ee54bf7c83313acfa2c606305f0c701fafd50b58188ffbe11c71f50875c5e9201f520aa7f0b6a9dada402fbf6dc3548b15317fed91cbea69c8fa3a1f426e206c5bbb3f7506002824bbc2b834f28bbbc6689fedb14d6e14ecafa532d10b8f328a1cecb28402affe93b5664f5302a76bd085d124d656b6ccab8b85d1ba21c287d68a1284777de096be4ca8f89e98e4ed65404f5293f966d179300708f97c7c2ab961d5e1202afb389445add2318ae66b554b8d91332a782e4e49e6a473fb79e8466b34e16d8f3e4864a31e53b98840a65cb50c01c8030c7952966618d8a530cfbeb29764056f2c168efc00774512338169e582347617ba91bebdf60ac71b8371066e1b3f5573d3c5df5641f25b57fcd4b572f8a5a9c9b5fcc57ba58778653173d5fdf71aa87e8a8e7e8a4baa0002246c176efbc41d425ba12db094b8e64c706865931268c3330fad6effc93ae9e662d2b99ce5b51c1fa265005f25f15346528ba902fd30a0e3066b24608d677a850a14373b904e1c38412ac7fe332266b245e166301a26db75baff4a675aacfea15b9c64cb9add13b2ceb5b588cb3e7732a8546d53321002a384013319da24ae808d02b761033a6ee1fc57d430c54b4a0f8689c344e9ee9e5a383a832c54ae5e65dab3a16fc56f200970aef5687587c055e35a41d4af49dca9425950c7b03fa04eb8a665c34c1802e257ef3df5b002ee01a709584ce6e7c230ddade4691b50622da9d0f65cb4bcaba29b902a2da60eae7a467a9e71ce51632795416d279872e267b26bbe98396b9ea6760459fe23eb1af443f024f4b47fa5a0b4ec2766ba9e5ac8701ab033b536335917aa21024947d324c04057eb812753f8b77fcb0b0d490cbdd5c487ff0c1b916fb1620e4d6353873bc7775b755626f827352dd3dfcca7057d05e3db356f8519708239e63ed51035742498451878dc444eccd3ab5775b6450cda8742de5f380548f5e769fdab294ab3b4356fd91fbf0a8bc10c2e7ad3e2e55c92156d046af0571d9da3b28d460074e35478c0df8d7902e12012f8c61454b8399232538206deefedf20b0ef564a4eb504bc379ff4fbf80e38d5fd03f37a8cfc3b853f7546c879aaa6d21c99c9962555dddca49cb2715cc86f28fc868ee023dfc724bc89f075d7af1d81ee88bf4fada419e2e6c016fcc5073369c186f4427417e150e2879c86112c13f61bcb9a97368832af2a9e06f182ad3556bd93b434f03e8bc507dd231d7d56de8a0de4474318f9b7a613ddb4357ee4b37be0333d0f2c8d747ec185dfe7f69b9084d5b6b53910b01a74caedcddf79bffd3f035cb1d900a38739d7f45b29e9f7418fa07353f7a3ff07025d3fbdba2621b6c7fa0569e0a74292fa6d792af2b25f804176651387b73b181ef324aae94168a8f4f81d318f06f9ffd617de22821bd28d1b9017c6e7bdf0ececfbf18fdfd7bcffaf2bbadb522714c3d67e7aa12bd20299c20507abc7e2867ca6b39d0c253ab94ad8ccc02688fc7a887f802fe5f5a8c284a3be640362fa8af2c55a9ab38e923c4246603c39521a751d04930b043ad947b5022617dd64ffd8982c3dc153abce2937d5c544994cd69a2d54302e69e97a99e9f42ee2003dee33c67545c6b4e57b6284fff0344c7dea9bad1f014b335d7d39e2a240b6183e17dc9a0db7c17ba5cdfce15eb1f0733d059251e390353211db06071e909907af6ffec9e52e02eba2def6e40e340cdd5cf4dbdb1aa9c3c257cb22930936376b16ef0507963a54570e8f02c7e0525e83430889f33ecfb024ac44cca9937a9bb746a8ccf279e5d7f748d8558a774aa7e3f2803f86b0715d49345fb36724d06de20f8b0606a18451c662bd35601dce441b86ef1907022ae11a7c87341f62d063e5dfd6be5dd1225101f5412d340003248945c9fb9df7fa265e84679e600a1abb15c0dfbf45c050331f43675d8ba296ff00530754fe991a46137c216298df5706588bfa80fb779ba20a3f1f94c14f5832e6ee1afe190a1708c773796878f9cfd4b5a6f6ef531a31ce784f5d531c9ae1cee7c6b611d17f328e69953199c48cf902f5a206865b5ebff55090c66710084e8f0372f80c51419a77d12b388c00cc840428809a6b83050a2a660e3e5367badc8572db4ee333beeedb0139d72e103c9d9a03ed7bbdca9d9a06f9a17d4eb714cee7ed62df778772e8a81896454a1cc8f58c34e3fb74a60b19691bbfa238691c624279862ce434b8ac9e786734e2fcdd8acfc085860cf992eef37b5def6085accdf6f284a824984cfa1600ea710114949735afee0951e9fde04f09761c5a8a6e9064ab8f69bbeebecc5ceb070af858e9837c0747e4511542e21cdfc99556051ac94fa44ec01cb2e14b5641e5e33a229dc858246dce5339f74ec46d44a5e1dc8b45cb5b51e075c6b40d395126c5883bb87a4c62cb13c2b7d9a25a47afd0de4122c6d15cb4578539fa51a43f1cf6721c770f8fa4e41db134df9fd1af59a4b74cadc5c3420aadf2e02024780ddd1e320a1af632b002aac3635e75000feaf41b5758cf62eba81783bb7a3dae55af842964ef46d1747390d3f1c80e774e42f53fc760e7da7f097fc231942e06a91deaa8b8d18316e7d610a1aa27a73a58cd04381e30222c033c3205769b4be4de8545371a9fc4b2626b4b165fa08a7d0262ef024a1724e06077f1b54c6c4ee5dd9dc6f3e6a22b17df717326f689cd014fbf1e14800f14e1d4b04225288265d303ad3a167951599b43ae2d0b0a111cbebefd654926a4774eec0d3b97288167f6bb27e0b96a0f82faa8b2e13d0d4b8301b16aada9478cb8539d3da5797ecd60206f6295dac05a809b266ee39965116d346fc8aa4e2ea0964d18336412a14d1fefc763590d972acfd70e39900602942324b7ffb2142a1abc461dd50717125d06b953373f88bb64c6f72c4d5ba14a740d68733a11c9a22f386979ceeed2653c5d5b13b61b8fb3c67560a0a187b443e4fe5aadf7dd9dac47390c4437569894e779df2b5a7f57d6a4ba449ffd8ac9438e11fd3c88bd327378b4dc410fd3c2358455f1a9bec3241dae84b3ad5b6e847c370b47fe2d82feb88387887595ed99bbc1ce21dea7b189e98eee403315b48fdc1dbbeae94f72c9cce4d8a6bcf420c57bcc13265fdc9ab4954f52aedab343de063298f3ae6d2cdfb5ddba3ea6e27dfbc07cd807f2f2b817e6cea1f9be30dc53ed779730973c903925c69df96ee75ad45c9e01bebddd27b72995eaf50f70be3c6eaa85dfaa490ec1aed418531475c64231c2eb5fbb9b070da55a27191703502c632eff3fb6e8fdea0fbd0866568105b609b9806587af021b0e1f2f827354369587d78b5e7a7db95e2146887f4d4ce1022c670f269a2abead3b17199fbaf0ceed703e06d6e56891d6ab642782e7708917fc252d0aa663a8c3e63d8b5deb91f1b548e56c0aa7192e82826544aba0bc698e10cb4e9d1c0eaf9bbdea40ceef248bf896f25b40c7fa8b762d7572d197f4ee686e018bbd064f29f2dbffa32ed90726d183e05cfd02a84767336a1fd7c23663815093f2ead7620cb68f0760f0c2d736c9fa79322dd66f5068770fd7eaff4da3a96c887ab32f2d00454efd616e464a4f3518491fc0c24d2dbe0acca9aed5fdec2becd6910dde915dd61956685374929a46573a2dc0beeb81cf90f799a1adcd1324f547d5650978a904bc17270f8dfe584f6d3e8c67a959eb2ab7b952bab14511e45922a05547d128d1ec686145c53c3ddeea28a36bf219e18a95800f6231f833e113c0c832e21bd480e6a906eae3cfa060f8831e3856be25caacc7f8e24f9d68a18cc001d85722fa9463ac83eb58945d778c474bc1713df6a0b7a00877ca660ce3842b31f2540288ecd9f263d6d4c5d7739a968edb4aa81f7b945079434c09050ccfb2052492239d0e509590c731dc1ed1596780c94cbce128954f3ed4b5f2f16a280d1c5a0ed3ef01dd9ca46cd0d9930076c4a36f95d15e4734f7473e9918935b63e9529d79562f4fd9876127d95bb6c3400fa97d2b756856e6ca37a645c25da54aa1d785afad3c78f5763e4af2e1b038c02e3e62dfa549d230206f6db499367703bd96645701b1b82f2dfbb5fedad839168767f3ccde49841f8d901be51c2369ffc9b86a7b57b58ea47aae7996c80ac16beb51ec75a8fbe390a5f1d566c03947540a3ab66f7c0578ab3c5cb3d1b599cdc572d6e425665c1122b3cb52f82073c679d6f8ba987c3c56c3aa3d9258e10eb988f595fadaf7a8fb714486cc032fb53824922ec3d72f13613dec139e19c86297f5b1b80066a3e7c6b3d875ce6bdd0a5e4f59512ae4fb5a830971e8ac84609f2ebb0a9ac0c1dfd7d7e475497d42c11a7040ecb5b9e4f9087bcdf643fadce4ed61e6c17aca59ea785bd7f4f6f9dc3eb675bda3269cbffc212d62b0920a4a947815ba31b22a7068032609208024fd1420b86fe928dcfc91cdd7b306f70a6c75397da793aa2eb240ac0499aaba8ed640acf6ca08ba70110fda1c68839b2796014ca340555d0ea0bff51a555d06b2453cecfd1c3fe040457be16ebc0d967fcd158437846a4e39a6d094294b018a2e3ac7d59e105ce86035f05c938f245ee1d730298cdbd17c55a4213fa39ea93efaaf8e0212750ebd525b97b5eb24dbaee862cc7de73d9020c85bc78cacd74e6aaf500ccbb657a9aeeee9819824ef74f6558fb850570dba49f38a33510ed58d40eb5ed181a347e8815abf4e7caf5707b9257554d49f0f950c76b047664e448687285da13d0eb8850b5fde767208872ab4b66770be68a5594929f497c64a65d2658f007b70c17ad8e28ca3949400a8b369b57b595bd78a5a56e12c58fe22ab7b8a15bfbc2cb33c4fc03beb67cf206bd86809d09b7d98e669c3eb26ba485f8d5f794d0046fc360aa4bfab02a50c129b7b390b56414eea376526659e01b9f1b923965c25b1404c98bec2b654576aa70b30214b4c6070acb71ff7e8dbf145b0a912a829940c6ce1cb2e8da003060d50118ba6de5946039661342c445000c91f4bdaa7a343befae0498e685e8c4be658eea3c939322d3177452eb0c1c2b39a13045d07f336216b33435c79aba815469009191840ffb406c6fb583434b3643d5d921c4fc5408de8e16430590a4e6681532cc1690ca642ddfc79104b053e859b4645a9f9a1fe4b27c7f07532d8588b512e0124326405eecd1c5377b22619c6e24f29877f6f36a0fcac1a7a3f8eca614067a55ef421497d32506443d5b1052f479eb164e908172796206286dd576049671a6ace25607663c262a0a2c06cc0246b4a1853f26e53f00c5ee4e3cbf2940560545c9ffdfb9c60f2daa22d0b2ac7977676e6ce159411b00147a51a42c7f7b4c9e12cb0109921cac212b9ccabb2e7b10e17f570e5f2b2d95a9e511a246461eea60dd847b70645d1eb8536d01bc0816dfbe3317f74785ced5409624fbd81c53c49520c4f0ac13555498f97c38c1eeea51ebd20fb2c126875911478662a22565d50071a8b85e560781b0b50a49783212c8c65af3650693129bafbab400a3790e71d65a16d62a00cb3e64cc0b36f22d665a666f73e28f8accc080cb490b026795f3ac4a49995f6f364a2c1c8e4d2ab97c1d2faf12f4ab92d8e6b6e35e2512f70c67ea236a05c55a67ce2e34ba708f324a21ef50609567d910cf2d0079b6418a758138f755a1e848d198b9c1a763f244436aa7305272fc5a708df17fd982ea707dc08045b9a2cbbf2a8a9f3adad3bae3046e42ac0654b468366e2e88943cf33a363f29e51c17639f5cfecd9916623415abfc58928428978cfcdffac16600477796885a2e3eb75ede17cbd1ee4d2923df5be3e94dea46e47261ff987827c74050368e6acd15aeaf17b8301f60905ecbf50274d27dcfd40cec0fd189d9ec6fbc1c61d593ca8701bc32de304eea365189ad683195fb42b7c62562c4391b33eb960df11aacd957d11955671a741af1fcd1834ba832506110a51453387c7d68f049905550120684db6093fc2e376ab92726e944272f92c54ed9f108de09ee8b8fc8762bb926c04e459b476d88fb4b9304216b5c2f738def7589368849250026f14ba30e2073c2b83d8f75f702318a42ee8aff18fc6306c6f73e39f63671bdba214045c8c508936db83d8ef349b7b12aff222a08295618f1222b40a2f30b3626fb3930ec2c8445f902d689761d7b71bf445006e3c723c1a40a505198e6285807898478a5d12763015ba061bea3341ab38faf0fabc9b8b1232c2ac4c5a37da047491907e910ff3d82c66e3320283ade7b87a0fe48aca078952b4af222fa48e8a59577c7a9d49e0a814ea0b82d1db792e2a218c9a7dd92f69d556eac2a221d430443c53dfb9e766a07821edd4bb66400be005511986dc2ded50aa9fa4f5a4c466ed5ab0ae34a944f40a6c06d4862b848df4b893deabd4920a8f9a0dd99345a665923bf33790b35ce07b64b2e8f3f6d2aba5f7e75345fec4630164643cdc5f41fdbfeef62384947023cc67375febb2cc75e5c83c21424a5fe24a15b511f98f2981306628ee197baf77d7830c298e7df2744913a80056b9d040aff9af96896fdaa437b7bb82f64e0ce61aa06c4b6634c180a6b0da2d68caf2c1e633ee9cf704655f48721eaf962494d7cae0e7fe1cb00eb4d298c873bc6d761568e661fee4fa06638fc86d5cf25a607ed1d53a40109cfad398325e70afdc48ce9bbb6f093890f3967cc2f6d9b111b4689651dc209879d00e4e68538da47a9f067d0a11f1a0ea353cb93a4d182e363d673b0d3a59ef13ec08ab6c4898f8496a8a09f49e9cb728284b1844df4629c47d09c90991d20069dedf75f889fba4704c0963e27c7f9a4f6a7d06162dc6ac10cbc07f4fd0e0200f34cf26bc24d782d0a026e0e30e8445e9390da239ff4f00486f162c45b86da762ba40fa36896bd03b190a50a06b04d0a05ecc379014f4937e6c98429938084b45ef3bd2efe03d19879148692429ee40dc9bb4e476511231e930bf054e5474032daef52d61adde609f9a84973890783277862188dbb69a8613969f551f6228e2714fdc8df000ec92a988d1882d865c8ccf1ec69b034d2cef704ec510890e845d099d999060f0e96b5a10e88d556060745c338533eb7d0b838116e555818cf009735b56cbaa26ee2cf2b4a897de93cbdb0d1453af75f5abd20e2619e15369dee064b555ea1113a1e28c6e516ef465823c781c9b073600539376c9db1a829b1b0e4db98fea241a1d81857194f9f0e6e52a1b9b09c49f46c8b08623865ac8042a58eb62ad996032f86fa38c95a5951aaa39674f83f82cdae16468429e2c3282c63ddac98c38666b076d704dcb4bb72bbdaed64356a8c56000b5453fc80ad84021dea26fbc9483b0bbb0f993408918de67402877a453224672508dd36972c00a78adb2004df56ecef3139d80ea9c2c4099563738dc152b7eede3413f938d53a7c22acd1db359070d4795498c8cb2330a5ae21e56ec9d6d66dc590ff49c0441879826cdf5d3f5d89c8e8710f178e135bc1ac8cb24de7fd23d350c2eda84b01a6214a746efcf0dae462686219c3805ff66494ec4d69cfeb7e608d87619f9f3d5ccbf019e5d228c6a1d9a15d902b60dd4a6d72ebd7a0c0eb6710e5ab013f0613faa090afa930898c25cc09528b44030d2af60de0c8834a51370f849cfded966dbaa61bf04d1b1d4fe485f1122b3e6252513bddbbc5a3b5264c759a49941f7d7e214586ca401cc9d4d4dd1bfaaf32350fc6486bb0d22d0e8851afa22dcd0dba8f3d0aa60190169b0980216f34252187a5bad08b7b19e2a2f8e59233f69cdbe925a2e8bbfa635071575e30fffd6d2ad0dfc864ee86d9bb14513cc8202aaad7f82120d525b00626a906b7d852c41a6df1c17bf1073e109232e30f3db957d7e7566b7de694d0c673754298e8765a9d97976f44cb0773e0566740a1e0489d405219a1ba19fc5cc9f8abef8b7423862d2779b8e9abb19b109dec7b5079a253609afbbaba7879a03da8612221f0c8a2c4c3d204cc7ab3a8f1965374388a9f7a94c1f2cf022b3fb5cff8c0a35d7ff0a51d2f902a295714621d819aa7fa60c1ed787dea6ccb3961541a26769039be9e9ee6b7c0f1469ed8030f9d64f63094c4ef0502b20f1fa9c513e65e66829b1e7936b18e9eb9f7c180c80b025c96d49c22feae016a16650a5b099f2c0ce4db4bf5b86c97467cd37345403060825cd333909978d93e57a066ad6899acd09c5c517565202812342cda186889f80391cd41a0799d657388749462d9ce25138161b505fee4938ba17770d04c3b689eecd51159dc0c02d0962a71e14e0426f04eef5ff5b22d4d1cb5546b1a58b26c0e2c2b5bfdbe0992c51cbd7767600fbad66e6c5dd182681fe9c613d68f31fc00bb00b39c681e411b2396d125b852c2e0adc29c04541354499feedd46329229b3ffd412ebc37646075fae66368eba2e237ba646c61e11b8e14e1fcf9abb1495bb614d1d45390ae4414ccc2c991e6b0a1c6fc20eb4e2c3dce8db1998ceeb607321183e4ddab3ec49bd2e94de4965f43aa0de3b6eb7632f8ec3d8ccfeaa33a502f7a015d0b79681ca9caaba5ad6d182d27101bf14b5b0786cca9a3ba025ef9e5ff4df7598d0462ed92cd46a0bab22793148cca27b85b1ccb470b05261f0c3a39c4406de6fef7f6386116ff5b0b5b56b752fc8ef18194ec4d799fcd0c0c6f61ba22ebb374a01783dee533df1095cb6e91d775bf1442c8ad7ac447768f39eafd2058ab8e91e2604e0bf82e4405b386e92bff7bbc11f1dcfeccb2ba3470ee5fb8b9ef2f550dec104b991c325075686ae4284589c69a07ff948f9d9e4ca544022a7f6279ccb2ed352ad4bae9e6e65e9bf34e6de1941209e93bd3507acfdab285d961d473b7b16d9d76fa0a5adb04373962582c83098551d512a6baddfd23ea4181e401647b799c38dbb9604072d617edb9601cd7bfb6dc912f96f8f19de6a01e4f61806d986737a7983be290e3b417a45dbabc81dd355f38084812ee0e2fb66159fa67aa63efbbf157044ea181062b9a85d29d26de6301ede0d1c096a9ac16141850e834bc6905c6ce17e12c0096a28514463fc576be070ac037da5e30454d24369620b5dbd3b4ed6829d3060bd02b5a9fbe10b18e49eb926d1f2f6cbc714cf49c2a0046c6da30713d62f44dd7d71e53bd98a6a86e1f446c325bcbe1290d598476aa1a3426b62a7e248559bfa573d03268f9d2a8fda8138efdf7b7276ec25a51ed1567265414af8631dca963e38631e93df9e6a5be373a672760ac6cc6d5f30662dd7de7dc7a75784fc02bb9fb157bc3b76d9027bdd5e195905163149d795bbe36538c00287f67406191a462c6db3b3d62740d181fb6a7697ddce33c50bbe574df0c624085791db6a8fc3a482f651fb613f562eae59d23f3dab0dd2cca8ceae4cdfead67e6bd9b05a2c82b832e1bfd75c1cad825e7d2550b274276740889b18e5819529a98047cf3e73cced1232151afda058c6cfe5bdbcd6a230c3ec0a63b332769a30124806b1ab73b7945091b308a3c8c7c452a6ee2526d2d1d043518282cd1dece5b1a778d978ae3a1eab2b96352fb124704ff7a6e20f3da9a1a6123f4e984d33e9517949eb17da46825c5a83e4215446dae239a015512a7851632a3a202f0ea8f8b186f4356c6bfbdd05a87e0e67e78f713334998990ddedf2000d18093088372202e96f22ce482ac68c11d239c5076952be9548dfe642824905b24ceb58d49995ae0214b655630b44473c0db8b6f4f15160d936cb6179b43ddf380acaef00875f0de106c747a3c88d419fba00135e3dd579a71346f4ce33cc2d7a9dc1f1c88d2d585d74a98a63945003fc6f9ff471db7ca582098327f2217c099843bf4ad2f82e12542fd4b2900289a3aadf1e81c745db4f50aea27a196057d68c0e7d450f4ff72a460a512ca60a06bfbb4ebfd1d844c5a9c1c5180177d86d40efd6ffdb47310cd96230babd22ba62e5fcb879e39193a1abfafbab906f6b0c14fe2ed9f2f15e76bd84d2e078da2b61cb13023e941725d6ca63446eb370ef99098d3ebc9c6b2e5605a3aa24434ba202be22bfc9c10ab01f110e0c7dec7ee0673a087703cda24e68b33df6592c62b3aa55013a49306423b269b8db14ea2ed051c839ac3c5b57bd01d717cc04aadee10dd53e30040728e723185b9bc9c9b813fca7b9d8f0e06b665d1add0390f369e31f0034e6b7f93746407abddd81e54e05c8167e6ee2c21a5751680d6d64ce0925647a50ae00c9434c80bc34224273bd40722b3680fe26ed73a990f20bdfadd20612340a0f8dec82262bdd910175c53c457f4534a14351a4134c213965c8c5fbe9e776fc1abb9c9072af9616d2e934928cc6b4d46ce86f6bfeeeaf059e3c6c67b726f3ec960a74dea364ef2e607e276bc10512b6add49da43e47de84f6a1686cae93442a740109a251e70463d79bff20f6a8eb31238b7ad0fcb0f425a1bcbe6a83e1afd11173e365ab8b9191c1b70eb3fb979fe8a4f0c959993c44e68789a89f52b2bd3cf4d5a19bf85575a9c97f5f66adcdd24416185ee936c340738f68e3b4cf3b06ffa69a74a4d62b966b18387311fb786d6361b53a2d4ecb7e759011afa0b6ee86103548f48017d5d3028df992e21bd264b033edbb0a1f2f70f6323eca4a85fc15e5fac1d3d047fc5e762490255ebd8c0683976a6f2e6451325d6e08bbb8340a3cb871b26693c6796e268d926e5d54fa1a8ed277989e1fd86b892e152a106b2d6417e4566540040ab1ed099818015dcee788d7ffa55610d63a31507e66397adddcc35d9f9a82ba9ba4b5b1e0731d63fb1126b521fa7d758c329226f029ef6df9ea5ef787b25ffd3843cebec7b7111714e06501f4740d5ca2e61a24ea7f7b42d9ea8b1b38a8e001d706326a6015e3dd6f77604c0a19c532661406c4905a5cd5d616c483c7d1363dcbc505463f97652e08ba17326a1e807e3055e7b2d1ec775d54f668799a9523cdf8d2590f9bfeea4de6c2cf8c3a17334da1891d57a9c42d8185e425343f41a0ba3b98ab9a4d003e5d0a4f0109c5f04c6dc48a289cd85078539ef429297428f5061c65325528335a7b20c867f0ff38dc455b583bf1f4d83815e2a96eb34c966af1677e36d9bd6dc5ffa9314df9a1c9e5963ff85f431f4734ce22d3a8be55e9ac810016573ac31dee82810be687d8c0d45d439ff32c7eee9c34a55f6f20d116701b08a3d48aa2b99feb410a90abcfb328ee655b6f6f2de5335e95069c10613c8bdd392d08e77f629cf6d068b19e4146e5d443b48153285d633f9d1f1484c2dc75e62c4923b74f392afe401f215d81d285dc9aa5b8bfb6f43eb2633613a4539e1e5dccab933bd410f11c14853c60d2f73537a9821d230d7ea0c52f75ec64ce11d51cd4f19a5509c462d18f153d5a56a8f9d867827d80eceb02e7d439177a5d3071049762228f7d6d6af60f1ed7b4829a2d053e1e683cb2ba12ab57227f894ead70204618ebe08d246c52d11cb70f15e698302ef7b87ce31c5aad0a9090aad6589160caaceb93e8299f27e5715459967f98e577a20052c2bc96f5e5a4df606b05dbe961d90a68a71a55c31d8903d489d7078f8a9c683ed70d4352430478fc095c57ef58fb322560a1501ad71b809046a763b8c70a43cb983ff2a7a1415c6b07e991cbbc1a7445e0798aa6859735428ef049363016f4d988105cd61e3c9908c4676badc587b6bd1a26105057fdbd1e699b9a9b1f2b12c526c2ee279ca7dacaa990049e7ad63f0c53a9914b010164382c2f9687fef7b7af337077beb9d640a1c9d9cade1a972b1ab129efcb9fc912a52078d03f98cf330c7afb64cadfab2b93477788b52f3184fdb6bb6f712f86982c7c1eb2803a4362f3cd218e2a7ee80c7633ddb64b087092351ae700b38b5465a057e2e8efdddfd47a2e367af903a313d9a8df4dfe203169e7c0df1f881c4ad6ffe63d2a1610ab2b147b47bb9af58d3deac4771d64a9eb6e89e0b2cf20e683958653a18e49dcd69c16b36cd39034700d28eae67b15f05356c3f3d701ba69e71bbd9ce66395f1915f92039c0135790dcd492719bde78cfaecde9c6a93803e4116c52c67bae250e605796c7b9cb141cdbb8c85aea434e8388f9a84357182de00297eb62e88917fe1b4b5bab71fd839918ef48d6022a5cecbb706b92b56e1c9fbe49b9405a88a2584340c594bdc492908ba41e7479c479df6d3f6b75ff88a4b86ea6976868d66378587596ce93faa25c66a837795c27511c4cb3f6fde6091614f4cd567bab274c4993448de70f80044f008c76ce7174d4f85625fb4dea6f5b45a28eee96122f2d3fcc4ab02663687036bc3d9608e9ccbdb0773dade8cf4e911884d9f1572870cb913caa319ab42c45804a468414f3d40113d1bb04950bbe93bf4ad809274c76ca502b3c0e5db2172b3e172e239b5d163c60f578dd934100b62fbf009bc3d78bb31b4673d7213a46da687271ecac2d04d388653094dad14c81c147f4a49ee060f38b5173b4128b3474eb3a81bfb1f5846c48801ac068f0e3225a38184c1cba33f91a0a09c61160974ed849622312589a5c7da85de78be4768e1363ed1fe5218a26ba0905f6dd7fc7b111f050e9408adf80bd00f8deafae2bac60f19ea04b664fdfd9a24b9ffeadb0ce78db4a043b8f05a8f178adb10ba2232e30bd5d7cd80999be2cdb89ae1b18450a5a40a13373c2f232e3774c9166dca8eeff625504c8d4d5458d2da0db043a5cb190829cf5d090b3a5262a181075bd071610f1a3febc70b38e6858714508a25c712b8ca430709e191e5605c79c82abe3f5dcd156b99dfd7c53c0da886e979373e141cf8977ef2fd280df6c977a83838d08558e78546c3d0a93c57338aeac138c06c26172fc4fe4b38497eec5f834caa65fe4d5145b3048fec4f812c0f66b37ea11d201dd0029783b900fb21c0327cdd166a1e1c307c84364d012ee39fe04059112dde1ddf05454a7871238e45e69645dd649bdc921f060a2de7b68a8d7015350333450a3e5cd57187e5228cc9b7de5ca5d428bd61c9fe64abcf1b43bc9e335a76f3885a0ad906476904b1dac781fa9db05a3d1391be2daaad449cabdc483a79350e909847990eca042c538c4c83b74ea1063a47112b6af25ecc3add7cc6dfa563adbbadfc63e619d9b063a8098a819cdc45194f70bfbdf7901696fc0daf5e407784c158506d370564b1a814a4a7182e2509ea139beac8e44beaccfbde739b6cb3ecc6019af8ac6d115095cb56f039bc2202868f644755abc9c21e60cf48b503e853d535f417c9171757d716ebfc30520cb4648437f9ca3963b04c7da77e5fbf4f7b63ebe14bd113afee73dad00557547482ba7a5e0c46efd5af86e44ddc5332bb1403684b537f1ad9d7da06d00ce803b1934548378d1d06cec777b0befe9257e14d327260ff3201fafdc7444f5b15c546e9d55363a75302ef6b184358c678c7f6afea592d017abff6a67ae1df8083e75667a423cf2033b83678f2051d98edd63f56d3c5953351871486e5bca5df552b7f888f53e2d087ef1457f5813ee8529386587a762adf2c802f0767cda402916ed9586f5f8bceee5f433adfe7ac1119f656aa0ccc4cb046a18348c61471f7986b2e84af3353c60215037d5b1386b6cd95c5ab37d215353bdd321fda85c6637626f86294838f21b3bcd6bab439a592784fb83247345c51894b8fc83f39b5db053107132ee40090799cdc41900f577d627062243fb0f6ac2fdd021a828b48402c46ecf090dd94978fe11bde4d40d0f00405340280df5cde43f1ed2a025657a744e71c22d25e04bc201ddd20beb8eaf32b30f690d6fa9a1a02cc626704e8b32138ddbda631c9e9620d70a3a3e83280dfb1487bcb16c3ce81a810e1611dbe675f6555d123ae87217a27095f18b47b8cb58dd44e551eb4b54623c4282a69b88d54fc6e04e0a13d51dc9461a938f931f7fde18031bef785aa16e3278e13cf3dcb712a447432570cd78707ac98661033c6fb22fd44e6adce7fe30a1e308020506c5c73d25e0fe75814dd831155cd7dde542a6a4f1287dbf9e1ea79ea3dea1e78fc8d4f20c79ba0747d187d5a132024535fe86334aa388f762eca0e4ce498d978576f0307419e74c45998ae3e73ddefb84de251dd37bcfebb579b95ce8664d7056260f5bca0e3768d01b607c11321a2cf2187c2ee4f318c7ccf4dcde900b3c659727747d2ec528f3c9c13e76208f4020778d0a3c0c51e16dbe653346ec71d8cee71630ed69b4097b9f78cbabd0bb05581b8956dc853cb9a3707edc49227656060c34fe3f60b994f6e487e1b1047f105487e8898b30f3ea1cc2bc8e78922f2ae82126b7d7af5596316c4180f1e0a1bedcda9be7e82dec06e8db6f148030b832239c6b5c629afa53225ef50b4f02cd8bc965270fc814b67f08b76dccac8d8f2650da286d6845e603a608a455455ce66a02919faa01612c9f7535ba7fef713309fad078f2e157740116d02f17f14be0f4677fc5931c5d9de49b251811fa03ddcc4c5165136e41bae56a1d3ffe83bfc800a263dc6082fe2ecfe15ef5353567a824dc3703a6afa96dd0fa395758603a064dcfc72d8c4149eded1841efe314a01e9914faec47084272cade2bcefb0ef7b4d30ea927be12b1551d2849b4664d955a381db40757e530d7b4a3f6f86d33073ba4333bdb907fa2a3a63568b217bcd641b8fe6ced657055b31fb9f037f706094d1df567f60f39850a936d0ca4c81aa4532e987c72d30f9e59ba57406f7dcd1b84017affe2f9af56ceeb8fd45a3d9bbc98545a65bbe48712be9fe960c63a25ab6a6999b54bbac29e8f1f6315dc302541a060a3715bb2f91a949cb510e48ea8bcf4cc1c21768bd3d2a12d40d01489c910140c869bb9897594601820ed3f3feccd57b7b3c503c0d930f1182b1b1a38aefe13ac206cf910035ee7e4df6755cd09454b5b642d3a21a09040d6a0c42cb6721cd91a8c894b148c232ac224a41f697940ecb4aa220f73b001efddeacbb642d4f56053e67b2e4c560e7377047809fb85ac622c794c212b85f435ad6c4884e132f22c474768b73fa5ca69d763c78ae74192ab88f7c8f67f8df8ae381ced2769d67e23f6ed00d86473c003609de6d07a3d884a32f9ccf8684652eabb3dc08e0b99f7e7f5b31b83d2aadc707f536eb9e97f682e46509262337d1e5e17c627f8ded0148b15a6764708b00601e4615de3027212e4321abfa73c622d85317c88e5d9584614f4cf23680892c1c506c11a0908e7e6b793ca49191ac00038a7775e6ef0cd5f3e3c7314ebb16ea6698dba5e947cc808b48b300d7462956f6a3aa1a49db126d2a612b3022592ca24638010569d2e69d43843621e8ab09f4cbbb66b8734fe2d8df62807f20e17b21502f9052a101e44c87e5263a40021faf533bfbe313974b7661dc2598055c55c0c83a660f9b1417aeafd372fed6a202344e15e4c8e87fb0cca3869165ef0f36ad7170f57c09cfe824b671be17166e360dd040317fa65b6bbba50083b3d782d8eb48692d646700f431793e29d38ae8df0238a4f5625113e79b98e672130b4b8a0196464627e1edfda6146ec714f906083b54bfdecccd65223ccbd07fd86870cb4656acb541007021a7bea23027c6be2f6e0d4f91d98766f042fef26998b50f52a2674205d22396a59eaf2b8c71649a8e056189c0deebcc5f405355333ae3419941c74c0561774b0b14007ba9079ff9f40c4df4d788a094bac77f5fadb24aab7074d76622e47f28370887aea4c6d1dc87d523b365252d9efb86fc16fa321900616046ad41515e26c6f49d3dd3f560ccf85aefc5c48a04320019bb596fb3c901c766bfc86757f4725176382b301b2d360d9d120c16180ec60b07436b371757f4e824d53132653417600e15775de6f1c8e6f36dd5f1e21f71d0f50dd116a059edb803cb789d03c7b431c0b003ee483bcaaeb971b072382b703b2d761d9d331c1d67a3577078b18789a7b940f17a643429b7192d3c649b89951286f1275db9475d55cd21a6796a68ecb0fb5776e0e1eaf9f2a76d808360ef6b3c51f76bf1d768c7e56df7d677018c76ff10aaec414d58fd882dda02c230ea249ae1e0d1b886cf1cd93d5318c4d7607b577164c8b95a819e4487e99da2763e6a6d55f4159f66f186092d80f93a8dd4e9d8022f075cc01b56d424518bf05fed964fd8423cae7dc25478968f5ae8448bf6134b1d89731a69c3ba730d505987a8fae1ee1759272f6c5fd112b33a4ebca4e122ff67475f659e562a66c84ef9a6de37436a4acd1aeb5725f8c6831416cabcf8c4ca3aaf2814fb5a7a10e6f94a77789d01d2b71fc9396b3b6d38003be261f7290be1ca2c1018f432c0951a0f1f2a9e1208558e4563227be3c332da12d26bdac7ce02edc29c34da7344600448b04c3ba059dddacf0be424eab0ef2cab9e4800eddd247571cb18ff6d07d1d4a2c01363d6a8d19872d748e29cf9f26b3800310769dda6ac68319c5263ab0711c9a0fbba4d271334baa9739e4ce4450e38b4517d572498585b54613a9b5ff94ddc35c1ce11d13f3eb60332962022318de3141c39d000b03d84039d9c00ba60b676d997d99223eaeb3fda3f2ff36f3e0df25c58e4976536b8b3771073a59215f4aba9c2e3e1713bf6a659c9a06ae6220794de25fde6555341e00aadbc806e442260faac721d9bacdd710e887402d99dde28284ac60dc583f0d36f1fb2aa43b8dba23581ddcda33e26dc5ae09ad356da98940681fe519bcec25aef6132e54087f437ab9d941695d9c92adf9d8d904e15246bf2d9cca2146a29b7738f71bbbaf0f9a44aa8564d0e717afefe421fae29a841dd9b1b7f6baf1c1e688d7ed23f47c0c8ce5189acdef3422ce7c58b01177494da04dd31ce7ad3ccb9f78ee1e125d2db4414f5d15c7f09ef036ef9f2e94a0f80c402f0264d44fd9226a56f229ca6c576e2ff71cb86441f1c689b06b8ceb0dd7caf85b82e3a9efbafacf34591dd73d689fec463a1f11f2a85349c7294bc99a6585c1032db728cc08691d99dc5888f081601243ef02484e938af4ed38bbd6bf0d349e425800a2e424b602088d67c8a9970b158dadbbd2779026fe1e877e036eeef79ed82f258ad63cfe5006174ec9da4fe9d22c0883be069b9d20eb127308d31a363b7844aa5c3c3e405bfa6ac2dc5ffa74be0c58ed62f1650208b0cde362cb9708578904628c80315bb0d521f79dcbafd177527cc690c702d9a16a193d0b655f8c3ab92bb22843650dc74167205404713f6ea64fa2d0b70e8a0098af23d6ede4dbe26fd38a02189654ed8c523fb3d12bcd3665345a071c6159594a84044726ca62012d9547fd9c5be115c85b1b8984ce0713415773c0a51b8e135aa4dfa95568959eddaa3266dacbd5fd106935e285a894a9d7d0a9e5ac28d63aa3a4ab7470b29b6e1cadb0a3111127f96ded162420edb795c13991dcbd3971ada597a0a4f0e895dfe518776e1e9b61939fddb0123ba5d75501ba1983b5711894ad4a4ed17edcf5fa9a442930c2630f23d80068ef6645b7724e9c3b0f1318a0ae8bc3a7c02672281edc9b16d630fd4b1d84de422a783b7ffcd758d0b04c684b4854a09b2f0a39470f2555c858ad088f6d57cd03a9e676896510c15cbb44f2aed2ac5138c3522b54cba4dd5a53bd5a02f34d40d61f254723b05557b0e291faae9aa6fc056b6473601f8a882a261567979df3b08421dffb10ca6296e84fea9f1c5bd3ec6d011bbb6f4924a73f39c3ad1cdb011847ae3e9b9842aa654f4edcdff8a63e17280597e2b2aa5ffc7a0d8ba7b45c68dff41ecc6fd99c4a75f56b616d9109c7b86978b11ac9d8c1a4e4ae280b9712024aabab8b22f5f56304ea6ab78e6a3b029d5c0a2e61234463fe89506b44a4af04fa3874c86f6ebd0ca96fabb25ad8fa0311906e00fdc65c0785d9e9a3bd053c9952e5a34ffcc5d298a35cec3cb9562123a026e8f1e4e0c3839cb042afd09d35ecfb25c61f332521359aa7f6c9240f9cf03e82d731f9f5f487c6352aac0314d3021adc555f5d30522a9b0ef1e28426524ca8ab663d160dba674e3ce9ae2b07ef3299aa251505685066cfd96aac86c2e88aebf742986220d1c260a817bd5a040af1300a49a1878ac3a36ee8c6b98ea1cfd6ec45363dc2c3881006697befbdf79629a514f205ec05b305bcc1aa6f321d9658d2df45ec244bd23699d3b4134d6c46b5ed88924d4868a64454341372f992679b6c33da8e6c421b0c969c6626d995bf139e127a45f589e44e9cc9694e482152cb9d546772a6ea3f7da6cc6936b02bff15c3f3292bb72935e47b20a7acfa2f973306ed4f6304a095181512d28c9413261f274c4cfe32605e5c5a586241b11382fc6867fca9e850f00d7f15d19fd819234b125144900aaa3f92ea7f1a8d6a5852044be2b02bff203e1dc6ae8010fa41142f96e462497f1fee7994a652ff2c9a5ab650a5a9e536096911c36215344755152ad86847a5763763a2ad7e63d0d94d6b9f20744b4edcbd930e688082c61c6b7287059e1d9511d485c9c3d3625848335f3e561f17cc916e8ed067128428c8d177baf9afb95bdce11102ed6f74706c1e4f3c7ade77ba59c0d25800696767fc4b7e17d072a1b8ff8e1dde9722eed0817a17e6a7fc875ae1e32aef8cb747d5c3538b5bed10d4b7f1503c3d288ebb33de7fe20b5c5346158d0951a211954f51e93fb15869a7917d5040594dfae7657ff81e3e2bfc920c59dd0a47f73c4a6b18ee0afcf04330f47ed206cbc349b2bc479c26fd6f9af45edaf116bad5dedddd9ca404a9cafcd73e511f9c73776fef2697d58ac024454535a31e5a8c2c94b6265ca9d53c218618328218b80bde82c80b3943414008f1d9284dd6090e2d6029bd89113257152aa8cf76581097299c4d66a276c9486c1f765de735c88a60f275953b13a9b00d3336628193daeda4b6f3a6b03577cae4c632c54293debec6a063f7919055a8a0fdfb251b19ed380dbf7896aacc7369ce1d168e2a3f4fb7542a03d134bc03ad856e6dd211231fbe823f5ad2911ad42621a63a7b5ddcca335fb10de81ed5ad658afff58d41474fa401dd2f3d5a0c18f5ccdcf4fd295dd0929b18caacc9229f752b759310e8696fbeff6e44108340552b8c0c60dfe6994273543e4785a69df1dfc6f042cef33cefdbd7f721d8034e15f0555a54a15248814853535393d89432a2787ae2a94997265331bf339f128e5fa94923b1318d59bd99a8de43781f48d44bda08e56d14f6964975eeb899c29c4fb8ac03dda4a127d5b9981e4dce9f58932a1650fe79b58ca4d2f78b240ecdefdffa8790eacfea968f9e4769ec4b560a9e47e79093ba4944c84ea89b34645439e6349e47692af54fd24a52c7d6f4cf3f000fa9738917069dd58529764cf56b117b8ee4bc8c2edfd3f2348a501dd7e1f710ec979f2c3465748a1a9f54c00a1d17957548175dbe4c81638f60046d7997f7173ba6ba89fdfcf21268f99751022ebfbeea98eacbdbb467e4e56d34cdcb4f16d2ab493a6be46efc948b4aa37e98e73c75ddc19973ce39e79c5d4bd7c9a5630a99f3873961384ea3e226a7a2f2a8ef49993eb5e5e72e94da32ba5646fe11631c63ffc6719a14d88523c37666e280f3e7fc9df96541b7295a4165a6d71c0d7474c7409949e8349acd8ececef8cb0826b0d32c74eb7bcfa18367a88e584644cd41d5391acdc66667fcc313db6c0daa04c28279192fe3431aa2ea05044710550fa8f1345ef532f2a8f134543cfe47f8df9d5919550b68797e7e525fe8ba81ae726056fe960752b728288bbd33a597e2400a1be0cad230ff29fa80c9e543661a7f5ffe357e5ffd32aa1ef05fe379fcd7a8f13d22fcd7f8ff1f7b667c8dadf1e2f3f81fc71ed5ff8b63cf911cd5ffd813f335c67df1c85166c840cb55a25e8aa6c02e28aa964b5bf958f8de4029632c4fd1b2352c5ff2104e78e28c0354dd405751dfa807bba0a7ca309f82498daa1552fff22aa4c66d52060ccc7bfff240eaf730e2e732f234b9b38526b75a001c3bd37f125d402cc460e0892181681d4ebaffd00474541ae2be4610f71523ee4bf579b5db978e163398a63fe1cab8b8870c031428309cbeffb3719ae7fe24daf8f046da7b11d2041b279430734300374455f8ac07f21280a80a6f7c8e00fec6877fe3c6b36ea0f040588fc2e7b01e85f1c71199ff71c2b364c61cd6a3f0288c3fc86701b9210001bc0922eb4f106f3c29a2f0366666666cbc09e30ff26dfc8f137ee68198f0404af89cd597f0fe2500e04d187f98f000f81f25fcea81bcc22e4124c09b200ee04f10713c2916e00b20aa38a44d0735e941429b6c9b55ffada85ba51fcdba45f4bd1f756bcaa6d256444b9739cc5d4602ba7de92815168a0afb44856dd2fb243b653dc6e6e019ec0d7f0fdfbfa3fcb04b363be34db6a66b939d014717ab9e8630b963d89533d9b08e01f1a7a880d5324505cc886398f7d33086a1612a1eafc2f97124e75ba972def3bcb1c7cb540dc0717df58d2e5f398f8b391e4beea1fa566bb8bdcb33a5eca9dcc8403be33f846e23ce78d3e4d65b1647449894c44ec0036cc9107c9061a526fe61d09295941aa502d7fd2a003c9abc05ac96bc05ac763e36af79881562613db4ccc51b610d1bb94993fecb83eacff15619a5c29c356c14f79d08564f754c0bb5ec09a526bd95fa951c1c42a78f4749c29559d7d1a36b7be609db85fe685ee84f6ca157fe9c98819df1f7440cec0d1783765defa679c2eff9fef43de02f9367e4ebf7449d1ebdf2f0f4dff784a7ff9e47f8fde9797c7f3a7d189efa1576037a5c5512b45c261e4ea77b50605b0758d27fd4b133fe9b8b72bd35e91f9c1c96252519aaaeba494900a102cb8e81205210c3d49834e121d03b2684fbc42488ea34c94c63e11a93a6a1cf29292a2a2caf32aafa577e5f3d2e9f32aabae579a47ccbcaab7c4fcba7742ce3ae8cb4855d46d95c79069ffdb949b7fc9be55bbee507cb188295f108f840640da482a3a3de1f48ff27d3e4c3ac44600625163908d5e2096419d3f1855ef17b3bbe62cec72783944f797e1ce620670939978ce4a4b20e8bc5c2c961d96091a806a3bc4e932c43b8bea27aa139ae7d7aa9c7acf2ebec740ba6f2c340e303d68c80a23207618450e5e7e1b6f6a1f2f3ac80b29af4b1355bb97befbd6edc1718841a89fdabfb7e1b4dd37127f6e895b7d3640fd761b58d6e7d651f652eb8011353122d60814cc935a51a55e2d7f62f5f4197669064ca508ffc31095ab23c2f06ceccecceeeeeccccececec3f4d9ef8c1120c8911c90d03bcd9d9bddb1b8b203e3423f18a27962cf919829f109205121a9ccc40d4440a2366109a2205480cad00c80a211560b14b8ea2348164288a228de44807283baa7852e4480bb890a17a90c44fcd071b92340144d4440e4ba408c202206a53d8c086fe6a30248aa1708a2234253f0005315bf2e467c8cf2c92439325419014418208dd6700ad0ea9adf833ede73add15ad6eec93d965504f3407da64044db5251d74380ac2c2892896242931028a2555bc000914330025d54cb08276e2892f7a7bb777bbb7677bf7328e6ea5e65094a41cfcd0474562dbdbbbbddbbdddddbbbbdbbb35318a6c3e0b850c507a77b4df6cbfa3cd26de6c37dddac6f279e38dc51bb6d971c3db363b37f34df3f6f6762729a9010fa1fc2bc3738c7065ca19e379a9909190883890ff78fe333dd2c53db72ef315f820f8a7ef091fd503fe49f56d2dda6256dd8af1c7798977b12b7f1e5487e24cdc97ce44c55951b36e0d6d6b386762695a881d52ab25252d13063000498ae28c192b2bfec38d2de33e3128d7909a74ee3bd4f729323561df09c249b757211cc1ef1b4bf0bdd1d5a40df8a50b649b19edefe19272cf7395bf837ade7bef5ef7fffda2bb54f7dee55a66eeef7b15b67e63f879cfdf89464eefd234a7e79ebacb5bd79237c30a8ca458b2e1081b64d58b8a8a8a3847c7b89bba514ea44612edbe8845ff6920224affd91a21b666bb695f9b118d8b84d0b29b98748be35105ee69547d5fe2201d107eb79db8686b9886add815c91dbb2b716d0c42b92fb9569980231379230d328fe6b1c744b4d3149a327e9616626139da1a2eb6e5c5b638116cebabc1c8b652dedf89587f626b54dedfa1e01cf4fdbd880dc261dd8773e4185547a2820a040f41aa7f87d151fa07953573691167fc27a69e13617e135f9e672d24d4b3ad49a9eecf21a12b5f6e34baf2e55caa48ec846f30f9ca7ffaf00d26a698524c89c608ad168b3565b2297317b896dbb4c9362396d191c21431e651a2ea19899db0f44c8414a1991251d14c28e64b9ecd52336488f0cec4d433522966f2e11bfe2c4c3ea713cf58624eb3c9b4d81c9b11cb268bc5e60c605e5e3ad641ddf4662e2d2cbf4b61be9a49aaff36c36e22d84d86b186a5d964be82718ecd886ff8a302375f10a8d731d833099529e6e8ecd6a07e64e2d6a854ffee616bb83fd6e4b83bd3429a746ff6a91ca549e6906833b1c307f2f2e7ffbe0fc71fdd7f3fc0f776290a98042cf749d79f9f213fddf7ec13efc1b11b7f80db0528dbe709a862f04fcf237cd4d8e385370c68d7bebc574ee7ec8c3f4369928beaf705dd4616e99afdd5e0ce35aeb112d7d6a64316d25ce3a52e7f546ee4d919ff79946a22a209e7740bf8fe5493ffdc6ca265ea26f5a38afbd2b5286e544057766d0d379ebe24ba8d3adea6d3f188a3d3e9743a4de634e93839364dbab7747af8f42ec8118de3d435a2a0dd77834968d7349bad112b5d9f5f14fc83782f7f478d3a76e6fb8276638a8e59501f62b6851cc303cafd4682ef32ddeacacd61836ff89f3e9c4d22c0726d8db73cd82e7c7d5d8532154b20d57b70c7b9abea6f8373acf646006ad7502453314dcae040b9ed8659d8db5077aed2d5bc28540a503420f1135b33674a093e12c40e218bdfebe8967faab7a6605229a430bbcbc36c9ee3f2309ff3f2a95f9787791e2e0f33aa1ae665bc7c6a54f5ef4b064c932c1fb67c384fa78d85d296959516951696efb422d2d08517683d3d7f8f2728941304ec8c3f8b07d81b2fe28dab6b580f964e53fc0441c50e3f4d3c7952cbf4459d7329e5f5c8d3e4e4f131392e683fe9abb94caf81e5d91a5ae7b7b035f3a7d75d6f403dbf471e2ca83fe934fb9aaedaa3aa7ef5b301e5c61f996c3184a157feac49a2a29e3957f967673ac811184d5b88f301d1b32e6a1943018202e463c856bed334bfb8891d08317bbd66b32e6a23f07fb6860b4f10083f3c8da2e72b70f4888a7ad69dc0cee76ce65238773559d433577fd9b36de49f9db9b1a9cd3c31361c20f8295ed0a288ac269bec221a6d224d21f307e6d3640a3893b6046d9395dea4f96a095a4ed884398d7bde37c392aa3fe581bc548d5219fba76ca31a34977a3661d2644d9cb48954fd5fcc1535bbe8710a9593c6cfbf094d19bf50b7d8f368bb029f859a94cda029e38d43a5c0db269b42e537e9a22f35e94bdd2a276c0a75ab74da1c72a739ad5b4bb37103fda269a949f70eb717e107da2f739a09e3718eb359396130d50ae1cf9f4d544e581b9934186dc2605ddd7fc07ca1c6d904035afa12adbad3684bb425da52f5e762b4745a1735e9bf421335d9335a1bd168b4179a2bb139788cbde17f7a7f37da1a5e62094eb21d25cace84e015d33b5117b05998000a190cdaed395bc35da38c9c9e51356c666666661e4b162ac1064110fcd0719af410f652056511fc0ec3adb9707442b25b5d99b9bb9b6178a1f3b156734078cae9953f07e27c238b6cb21b6348b7626b38eecb0e22e18ca3d3834611fabdf7fdd327f4f91ec461f38bc27c4f0ce2bdbc07c520decb27c8bf7c82d01b042867eafcdd0294ab98ebe58de2aee602c6948ff499d0d92dc5e41ca8baefeebe49cb5630123ca33d763847dbe0c2f6dd871cc74d1f581141dcdcedc4af9ab3dddd45207377779fcd9b37770af72278639962eab123b2d0a47f221d83065a0c4124690a307c50aa0205330cc1233aeb3e0f5fdd75980ff90816b63531b1ad99df0b09e14043aa7b91128e6d0dd7fd37c28c72dd976a1b550ff01e7c1ede83e1e63d0f301c55db6824e5f7857af067b5cd27e6b0263b51b571ddf6df7be1d8037aafdac69c6f546d1f760f82dda8da461cd49f3ce63029e8fcd261dd7ffd4593ee305fc5be316837313549035af64c24a2a5c3981cd6ad6d8288a0ea5dfd55a40d87910e837143ecca1f89fe61a2a9899734a9c32322adaf4e25082ac776c67f08e5d1c685e49afdc1a07bb7fbe60d04b76d034fa80de4f0b481fc1bd8830a4f1f828f7a1ee0a3c653f827149fb6499511dd9ef3d8fbf80b41af1b59a4cb0b9237293e1cdf0a9acc24740393d0fe44564fd9944da139d4db9753c6c44253c6513e1aa8ff2e69d26bfe7dcfa32379edfb5ab7befdbe5df9f721287aad919a5ce2481f7f1c92d71ca95bb56e95dbac2ee99697be4b886aea085aa66ec881fa6fdd7ea8032c00aaccac270d9b51032d1bd6b186354109429747fe1f304292c5308ed6675d9e688114a5f881996b35a41d983908a9492d46c3a0297ada2f29cecfdd5d13d0fd263a8201121c363b6e6ca80eb45c9a0dcb85c4ee76b1a9269ba363aac95e124ed0316c32202de1e8baaeeb7e7b9bf6bc6e75dec6799bd76ddce62c1da5efaf29d5f28774ccccbfd3e4bfd840cb55e57fb18167078784176c8d8778f8197a9be69a060e9479773218364d34238872111045b3a259110c4533a2d98ca8685624039a01cd8488848a8c8478a1fc30d18edab9e4ff264926654eef6f83ecd6d25411ba35cd02e367cf903fb5710cf364a2fbada496293545f4f62be02a2a189ffcd80e0aca485d32d2f6cc48ed791cdb5533d2116db78924c4b4a36ef5126666e6187b50d8083f0943b48f5033d0466ada8f44747fc4f184d6401032d2cea800a41af1cfd95e3efe08c1cbe5aba01951cf84d0b2675dd4b396b5d00d347eb2240041f702e1ab66f469aa49268e8a08b978d3e4eeee6e775721039224e1323bb7cf8deb3ce5846e935bf3e6ed9f8668ca555325fc50c20f5984ac9202ea620141f73dcf474f8905eaa72eebbd238e4e4d87524a757056cca9f5eb34b7e1e8f8ceea0f4b70bbbb2c8fba76b89c481d22bf9c39915f8baaf9208a154504a55c43d4c7225e607676677766676676afb150e369a106456c8a2390581f77a03d7a23120b78109e7f1e8f3c9367a7c9de259eff7f1e5fda6181679b3b7ec33bcdd3c26c6b1ff3a9d41441d496b400fa82871f9808a5bea281f2c840b3f907887fd88128a514e88789fc004d77ff719afe2939c63ecc3eb13f516f7e6143bb45a98bb5a36f5650a9dcbbf8a09df84d6e2e624c933d95e09fbf7371b04df7418fdece30b703fdcbf4b82bc474ebd4a44d7b0cc29529d746b7b6da58afd5bf2fd336627ab5a586a0278f8996d45bd24fb57ca1ceb9a47f7b759630d4fe90674bddfee6474656b7df19cd48a86eaebad18c92d4cd6773fb6fefd2adf9dbafcc06841b76288285926b89134860f1ecb25ed014093950fe0ee830ab22072fe020ddddcccdcceb62c540ad20e2c4122d6214394110314c6f6b31e0d06464440f458ab868a2429644142a9210516406126858f213daa0ee7b1c6b1e19c6318eb14f73cd6db1e6d9692a22abc74393a7d960fe556c51cb57826e1ee7799dd771dbb6b9bb4fe6cda793208313473b14b5348184431bbe4e23d34bee73415dac19ebc00721eeeeccce1b4f66f776253da91d014612417c3cd07dcf4ba57e64f0191181388c37784ec42b8a9408519188a00454f480c81731081a628914454a569820091a9e7c5794a0464407198d99d99d7dbabb7378c40a21b57caf1ee1dbf438cfebbc8edbb66d7edfdc260f68b948373898210521995008a1b933cfc9734e9f93594da8b8412d7fb21b75ed535a9951f76f7cd53a9aec6e1860e4d3344ecbe9d636418488466d76f473bd719a6daf69a71a8711ef594f4bc5a880f21351e0088d2882848e5e30272087a39a0d6666f73ccf73f6e9ee9b3405915aa650e09344b4248908a8d5b56c752d5b2dce61b444cc0aa35aa66642c4c2f6be267a33bdfbbbd605922ddc87de6cc9bd8f364d36c77936dd363b96f3fe9bee5f990e4258eb13ed0889d6b414122d6c9e328aa0f375b68679bb14de4057b98b421c3d7030b033fe2a4ae8f6b3396cf20183073eb035cba1c0564bcaf3a8bcd3dffa79bf4223639b9a7cc8f89e948c6779fe1e984f7d8f8c7f7916984f7d0ecc987a1e735ed8c5260533aef7b7fc0b035bd39e4baa49199514d1d5e46260c579a2a48d20d06d3261b174f7245c19274c4b5b33756bbde7b8aedbbca02ed70a3a4d2ccc1d1a45481a8dc6756291d3eccbc325a7f17cc543cb223cedd74bbdb494fa9c7bdd9d4be19e6b7323f3cfcec0bc1bb613ed0889464b21d174b6c6bddfddb1fb1c705c5fa57ce5cb5b4a1982da8ddce6b51729c272eef771db6cd431f55b3be543433ea55c4019b56c847bda743f25e81c3d1fb706749f3584ced5a4ab28a1deced20f1ffefe3b3c5b73aafe18d89aad66becbd4251c4d93d35b4d3f2a45a69629d269fee43852622d656a7f7f9abca8142e3745b9746846020000d3140000180c06058321915840281c2d7a7c14000d77984c724c98c9c37112e3484a29638821860000000000002a008188010df3fbc11416fc68e1939ba6d428385c039439c70c73e9987a22a438b8b27cbd3f0ec0bd2f1f22d8870c8ec036211322c125d046ffcdb2a9682ecfd7848c359941eb62eef31ea41a0e9b0f22ecc16e6be837be6b49fb4794791d7be959424e05a7ff7975cca5eda5be88d77d379a1029b74aa3b986c2e6ab6052ce5e6d389b260c3852d523674af5e37e735140f685ce331425c46ef50458db95b84efe0fd76345e183d6e036bb9725c2e9d85b0a114569fb336e135508a236340e3d4f9a84c68fbdd4043d39cba62339f6697f30b53d7440adc6af45b42a9ef1c7c420053c20e4b87035ae92f842a1980ca84a01b4146da423fa5704e52259f53c57affb6d941f5ca24f58482a051301dc8b753579501af73808271fa9c0c101de25bb1341a86e21ba0df56907a5f7602ee0ca6f61158c2e1171b889bbbf67d5decbf929e574fd363e6f34f6f033bbe9d0ce6ae59df543f4b5ad0f5cd38eacd8882e9400b8ca96134338d0060ed4598b28d11be4b90407759965c2031640a104adb086765c590900c830007eaff2eb4dc9aeebf581a4dffdbfdee4d2df10eac3c7f2dfd6ff44e8faa60729746b02a1964910da4b533abb72d90939ff7afb5f288cbc1bb2c8744846e69967ea7750d08d87093ea4586b234ac576fb484b9a2b67f5000dc66bcd8d11629fbb0868d2861964711ed1c7088495f57397de8d4a309629a1d60c37c9111f46aaa07df2f60a789a99d601eb52b2b87eaf928d464531cc7e616f056b63c4425681df837349e18ffe0665ae9fb253da2fd7f7ebedaa7053793adc4433f30d94d30558bcc756befd89445c16753f59b64fa10fed62d23c4cbcca728eb1fe505c37a1dc8238ce0b6547026e71310fe90e73d0c730e421eabe4ca58f290e85bfc7207c840ef50cf28491560f6d9d8b4b19799f6d4d1cbf5296b7227dec1b4af47bf42b52f3b52852821db91d977c65dbb011e82fbb65e80d1b6b753668d8185d961abc42463504faf64a8ccd40d40da7a643e0f953cf77738fae3b8450dc3f88744e504222eaa0c036d13661f55d427d44c2b46cbcc2279e9fe2d9e92494a6bbe651ccd1d5af1906c83f25a542fbbecf013867219334dfd11a35417be63b4c2bf85e68ec4c21eacfdefc7902456f12be23e185e177779c3f78d9a0803cfbd8dcace043d3c748d10c6cd1fd9f34b0f55003326deb343fe8dae37e632b0803713f5b0473dcfe09f2d421053924c2eb654f1ef0897079a1909bec637fee543fec201de7c8caa3ee2046af66d309015204a001c4ea0f83865337b95722c98f149e227d09154853184e3657e2f15662ebeb3477047081690e22c62b91c12207ffbd4acc6a948974686815598c5a787fb28dac43ee0d361bbb0d3e945ac7c283e46ad40634900dc1350ff6f9fac9c285b9595ad7f4c281831772578369256fdbae12c7c01f0a86554d72faead2dc2d584589a82fd6b3739ae140685a093398e39ab6da2d3aef3bdac73a219bce352b3ed8a71a64741e8d500f011dd18ea3b2a211737d0d6b9ceeec07f7f58ab2a8bfd17484cf7484d9f9edc399df64d1d8329e6ee816adedde4596aa0ed2ce129ee8ddda18fd757f64023078535d1c109bf4eace3aba2b11e824656104e1bbbc0ad1c7206f3255041dcac6317b8cb230a81ad5ad58c196afa96b160506dc04acec05d3350029c34483d9b8990c9d32aa309adc08f7b491adcf3e47a45ad764f6024d1ec84dc78dfcf17038cd800167c74bd155f586ee9a626db3ec90c8a89cc5695601388dc52059cdb936c6c1281642042e301eb352ddb5d6a7af88ca9bd6fe37567816108c9eab58f80525ddec093759e7e9ed9d5f14954a5b9ff4cb93ecea763485445cd3ea8ca0ae5853ba7ef3fef741627d1fde17d3ebdcbf5f48bf031a2b2f27bd360a4b536e4efe4ddae0ee84e3b314c11f98ae123c429195f4fdede52e9e6a7f7c0d867ae158eeda51c704bb242a5548f92743812c2124edcae58c59b47cb3a6eb33ae1aa8f309153c7bfdc3c07f5f9129f16ecca4e9616d0ae86298611aaf0939b63e3e16cc5811e971dd290654c5506ea473c3c5a1a21434f1e1dd52a42951a9923228637f30edce85fb6e42209967c60848484b5e8db2a00c53ab31863cece850edf8ca16109583c72b0dab416c0f9bda585e091ca3829c40c0b9432203828fc4b3c3db08ff4093bc61e8b02a22a75de6e7e51fe8a4ebc88b6217ca056c90ba61ac70fa9372bfad57a9b937d3aa3fd8088df73cc272f4bd6b96fc5aaa1dbe4a61720dc781225d90b673814afa0ce53140d51ef58cdf15e7533dac44f8d8388fa25a89b94f235a44935f5e75ab342908252fe5c5c0ce5700a8009cb8061de7a99d0d6a56df6dc767e725ef29978d1e62294b940a6fe088b1b9f29f3097127957996fdc654a89a6e4bfbcd04593f2ff565b9e794030d3d9d95ed54703e7a0e4762f4a05f10174f4889fe5126645622b1e311eeef268d62ed3555e1c38529359b29cde014619720c59c0d2e8f7a17493a140ac916335708ba24eaaddf103ad728f2e7a10b77b5c9a737b76026b17acfe11e6f6779852dbf661d56762db623cbf2b8050ffa974d53dad7aaf9566b919b7774c0efb0212a41dfa65e84c27bd2b5ca9ae31ad02a34c7a00db57b5b2d81f113350a10b5fbe747f8a51f74e02ab8b3a6238e47a8b877c9c63a66219e6b0e440e35ef9379467501042493ab185098993d052fb57bc502c01e9f2110c4618c72c4678947ffaf2022cc0d86cc64634f68bbefd1b0fe8a74ebadeb1013db6b19a730a24f0409a85c80e5759d0f8d225288e0430afb41c6f48c8d9bcc1d6acee1673388346128589a3eb01e6be135d13eb4c042ad5b1fee5fc7b8407ad6a8ac11778a785105e0da8ccf8b86155908791f15f11c93e4dfc2c9617e4a0b246a3cdaf7d7c8e31800d588090eaaabc54490705670e4f726ad1caacb41120cf8062cbf3e00433aa2855a9cd4c72aaaaf9d85c6f015d7001194346ce6a9411a74fc147068890c2715d6fcd06e5d36cdb157f6e206062edce0499fc2468dad53d73bce1016f06a5d534cb4b9b227ef86445db74de10271c7a88a014c344e6911240ee78b4c1621e1b1f7a87964034a6f5bb9a7b5e8d3a7931a1f53a748209e099b771964db42dfa653315290639f3503b06347def8d35d579c9e0a6fbb0978615f728d1eeae36e6da27f945cc05c8b29d2b0752b68c04880232cef1f964e66818f6d1a2a044030d23928af467772af42de8023e2f503a37bc2972e2ebbe8cbabb05f130a2f4db22b6f4452ba201ea3c16d9737a898de8413056b292d1e93e18b393624f1b6af4982db65dc955ed442d92fb2d4c44295480a50f4a6763c960f656ab6d82737c5b4764317905846e2967fd1fa144fe84e828ef904740b2d3509a15568fce0733469a0af51749040467c9d8abdf3ec86678da489a86c51ccdf20d9bba9c9a10a21493acefb89f4124a01ae9b5c36f2a21321b192b18c7910a134bbcb8a326d3a6ce1a65a4385aaf1b0b5c1a8130b925c92fdf42c98eacce4d8930dfd0459bac17a15c2275d1f0e214bb9edd2b674fb403bc41598286f41ec868fb086ebf84341617d1c820cd5301caa7eaa34906278e4d4f4011aff783948690480b6e30b670a58955b24a82f8e8a940a2cb59dd548066e16a04ba71cb36f78394bfcd1453f49415dc2855ecfdb4f42d28849ed44c9f51e23a53a25ee003a6d933cd0781ef91d7573db0f63ccf87cf0ca26be8495eb1e06d104eea5a87fab87cd974c1e626a745ef4685de7261b4496e5a49462d3b67568ca821d8f2f65c9577f6fd594ef10a414bcfaab2d96d556a4f8fffd00cf0b3fe662de8b9d178203e0c6d861d95f4c70e8913a75b38f60ab00e3abb0aaf7b5de3629a8d448297982a21e20a019fac1db5e10a33ba3c00cc301a40af6cc506deaccb982b981ffb64f97a769b91b4de11acc8ffd8694e505899da04900f87f65d37986004c1222a81ef06be634aef422a700bf783c7b3500b2398d3ebd6024a70cd67e3ed21f1c2c27668981d293d9b0ad3c9b59fa1a2b38b32c5e18ce0e5b9ea9491807c5caf9b7405f5e6a292ab53334be0f03f8d750f7f82848de80519b34628e62fbdd9585c772fc459802705df60fe0831dc399ac70db73c6a3e542e320c9fcbb80dc4a3e29a010ba6b5bf5f4cebccc06ac54f1c33407e39a45507e2cfb3bd5dc2e451e36d7e1b5950cc3640840e89bc45c0174c59fe31a22486cf2b62dec2fe1a9ac65ee04c82ae9c3946d6ddac1a8053ec01a19a5cfab8afca6f3e0a7f9bfdc03c568a5f12d166e503659993e619d3b145f11c7254b8497961848cc62c47714d43cf7f7b84e643dd1c2c363b73383393b10324bad722fc410b60c6d38b44f009ac425500636acf2d180b34acb4c72b3b0a31f2be2eada2c785284742f21b81938ca1a52f7e3dd27412434d316947f9c2457ba69b9c5ac75cbb81ced1dbd0fa701a19c76d3942fa5e381f2c0c4f274cec4458c4987c3d8903458e47314903657956fa29af88a22cc49cc08ac33a9678c5f318d4074689d12b56c90daa79cb65e40025946822e6103e18c8e9b8910e7a59fd788c31fc30d51a9734eab0f25e9090e6a26eecbe8da317d7d8a8b99a60e2dbf698ef4bb406d391976a69f7478027dd3d5db0ca93dfcddee1b8892a4ec8eb8ec66fcfee2593efa570ee13fa7d26b7d7b6145899bb7c0ee822460ab2aeda0b219cbe3d678f6e3de043e3d76d1fd1d233497dabeaf31cc47d48591677c3033e7b0fde8047fcfd1d12415b3dee8d6f06199ec55c04904dee665234f66f59ebf7ba840220fe5cbab3637f4ffa9e8a9c59da675b25001136619807f7a5acd1319ff2ce814bcbb01c3928bbc425c40caa166eafb78278b116879c98756018b8bc8a94922f4a8eeddcc4c5c9c3de741deb995dcd50da11232bf78e242916cba97ea1fcb7ef479d55209e6c48913ad83c8f110446f7a1166c44bf180f1bbaaa104158ebbad2ea2a4236f44e658f9821884ff8f90cfee783d4a161c9a0d12386d3e27fd98a27448748ec9612ba99a66258300558863ae49d1ee342b9be15b06d4a4de1246d8adebb57ab312861647ed5ba9034fd49c4971346cddb1b47501dca38fd799ac04c0cf62a765cf339815d62915197eba18fc5959dba18e731419e55eb081613cb29db10f4769ed8c796a4fa5a2f6df7708aa7ad6b831efadc91b9013ad3c4c976ac2c3894096a117aba338b63d1c61ab4e049606b054989c751eb89c4bbff00a5aff4339b6c29e421e330014253f3358bc34926ada2b54bd483e12a9800e9297e91bff59abe75cf90fa2c53779cb012fd061b25e5ce26370ea529c42a5ee0bb88961a61f4d3dfeefee4c03480c6870bcc18e42830b5bf7028eaca7d9f6c3d50378d47c0f7b354ddd9a260e9ea118e411b644082e9a92994e94e665bca2d0670db440672c70b0027a8ac42ac6437f400cd99dccb6c74089f382f314dc486a2f5e3af36915360642600f87070e3bba4c06c6840b2179d1a1003ee9842fecaabe753511835db8df110998418100b370cb6921e37949d9771b3111c2dc74a86fe276d68619a752f332201ecbf315138f377eef90c7761d78c6e268ac2b2b9a9299ca316065f6a1b75defab79ff4aaa3d1722a406b0fae6686b6ba8ec1a621f58644ac702182bf5d9b274b863c83c7c608df188a437eb6823b2049135837851a590c1c728e5b745788eb0f4b5808975b31701f4941b40ec6e8133b80ee02a69e14e7bfc1149fdfd10a62a70fed82cce1e707e32ac6a11f59ba9ded4e25796024b1c729b0e9eb1a9e5f28f7e29ea2a7f2c37474df742470a44ddf75e180414b8a10290cfde0d97237c67395e942d233be269c6410554e2dcc5ab313af66b608c2aeb861556b383ae4629ce308778595f389b974b24f5dfb9cb8ac9019650f80ed15f94c67dd048a858f6ebbfd8551b393bbfd275495eaf6c4229bdc9bc885bad1a13312a8c6e73cfb003b18d924c02af490f4e6c86d3b8cc83b8f493a2f89a22380a5d9dea0e47a68d950b697ea7bb8f1a69878b6d870f14b99c446cc67265c2c78152dd674e4ad9ecb4c76b62b77c0d0f58655eb1b1a0144150a7926bad4a49c39b6ef8af0218c05affa96b0e31ef895f9ea0a684c072c18cdb7c3a6d833230eb9a91712fee75167242de473aa6e5b35e6782982ab068a5984aa944c7e9c85bb1ed8cbb05e5d140e0736fe3b4b774fb72428d714a7dadcc9b5779e9b7ac2c22d0e35471af7d2a3ec91bc49b5b58ed3c85e415407df96cf8fb67634b8b35901f299d421f8d7da1041f09580b2babb23fb6e8c93b16c1ce6eae2c60720211843db7a5f857c7b1ac0a4a00515a138934bbf88774314ade4b2beb1ed3f14dbbe19792a026dadd2c3451881689e228cd57800950e378647520f372b9d0b866d953312e1b8e9248cc12951b4cfe3e32940a3911efe1773dc191f220a154fef99848803cb8784cb8d8c882c8bc7c15244251553d13167e196446b554053df7da43d06042e5ccb7a30b12a6e9eaa6a988c2f6748dadea5c6ee8313657fe6ec84d5567cce4b24ccd760fe080f4f465aad8147d8fe1a317803c6d2aaa841f9e8e85983d9dd9ef13ea14b104248559a6d4a790ec178f57fbbeb14601983daeaef49c06b9658d34599b596b548d7d161482392eec030769aea81d1efc9b2a77fe0ce54394775e9b9fd4aafbd44db4cc79e8787c862e9e8b3c794e9200521abc41c4952d3ce651949685340ffa65219bfdb6d9801c26f58c9eb53353e5d4d085720e2d445b17d90551572648b6941544171101571e88216b74b077384a465568801c5e6ff331bdb352f2e97a7539c446949560dd1516b092caa3603a946218a40b8d9fe36957196de6869564abf3f47aa19e833ef78e90ab46e1590a113ee67c415576651f3e91e76481e1919c35b12d2a56755b8e5a44127162cc2abddb09e09052529cc6cdeb3e58cc03cd0e14fab33d0afa33a93e170c6a0466fa51114a1f5f3768c1f23e4815cec692d8662a0bcf136a623067a60f1c9093d6ef8cec51d4b3f65217b9a9b6f8a5ae6598004943279803464c95fcad3a3a000fc1f9fbf002f3d26873b4406d6224c261085d7fc4c1ba9088031e5b0e5f1f483d7a1a4ea80da9083a8d0bd1e397ac591886f855b2de7c375f0703b5398e0b0a3440b19f3cbfae6e09f492f4328067205035224b6a4fbc44233d78f18cb43a13970949f5834c4f40b1495b8e548efa78bfaf29955bdf5746e76aca1ac9f4b15e4184ecff0a35fede4c1649c6993bb28136b88929f0dc17260419ded7ec09d8d1e19b4037e48f57d2de6ca730eef147de7ee11336b3594356a2ab2657f2b1e720e69663d3fe36882c5ce9daa39a988880b0d1ad0fb73f660e15f6ee0e6cabbf6624669964f2d1207bbca2bedc8997697472588cfb9bf14f993adec19cd118909c29f31416200e89b843e74d79d0df5c610ac3c5f6ee48889c7e1fb01490cb1a8256ff117f837ed28fc761e81049eea7c45f6d130b29c52a065645100c4b000bd424249490a1d37b6443dd88601288d22f39b894501d4c1c8b64495c2df3d39d7efb21540f646cd9be060fc7c389e023752955d64bf53cf5da8b7fdf5552194bde3925aec7d23d1b46b814bd0ab50e82da3e777a1d6b278b71ae994a0ab2e140e14b0659979a56c2d45149dc04231f9b1dfd1942fc36a95864d4fd4da580e187c3b36e400fa8a70f193fdf727ffad0d95248bc1e2ce03ec11d5d8d3e1e32f331c047cbcf737da00bc35460330e473ef7166fbfc75dbcf387be533b00e9615d86a430fba7663af55690463d2f715a8715333b1ef6aa2e0170c4d2e3e912df3221e861128efbc10eea160079bd593ec5bf4af22eb648c8b45bee1b2da6796d50512177a8bf1025f1a4da4a144a5d8341bc40d4d8837327f36fed53eb1eb9e690396539f699cf9ac483cecec692175d619371b974e3ae5de58e2dc082219fcf1f033664986f15bfdf4941b48686c549143012e42f6123f4d8be8c19c510f3b506a1b68c1eb5437dba20277c11d94bd94a671b2f0f071c5b43c3c1dc2005007022e4c1b5c45564005f5bbf67b8a09eb06ef25b36b38945730f1d903fa8445295c71b9a706212cd02cc04c9a7ba73025dfcab49faa1776fd64cc2419501071d20d37c7b001b5b0a4b21955be26d966eb4a731ad37a0b9c8cddc0cc27def34a06a00cc8edc2d982c4eed931cc894cbb3f4cff3933b7151b71dd7f132a195ab05f9e420f3f6ee5a6bd2a189231887b9bd3b46f3a7b8864df31537c50ca7500f13692fd427ddb1453dbf872dc35c36a7f88bb69668d8672b9b48c3bef9e99e9a5e1e4d2101ae32bd079e144039325070725f0609aef3c552d03e5fa17041e3825db33711064a59eb06c2f9b7e36834bc715a4dde151641832f11efee1219db1419c1000fd6f221b4ba9f31cbad579905c6ea7dbf142c9da8950065a69819f99dc4862146bd2ce2d48c2a83d5ce0b1ba64d7baac7fc7a87254fd1ee0f762e0aa608338393eec2424f6fc169a6a6969a6a682f07a08006a03c797c0ec2392813cf6e75fda81bf6c3c93e844b61747a1c5d72b3bcc9982ef8af861e5299fca2ad589ec227ca8b1030b63a8e83e4bfdecc7abcd1864be4b3b45267731dcbe4e8d9752413689f2e1c50f009f438fb9ceb0e39296c816c124d44caa09bd2161db0f8911a094c1b487471575d552dbe1272e996a0660d2daa49fb03dff9b1a1252ecaa70971217b4533a0e7e59d8f07871c69f8ffc3399cd04af434440a8c30ffade43a287eb3117bc9116626f4c1005652d28f5468ffbf4f4a3f17290e27b06c1a0f4a37760122d4c283be28dcb9febd4b063c1a5343f134177e152e99bfda3fa150d25c15b6a18c69b8a46c953026731f4721853fceac236415266e898b105db0a3121b4d682d90f44770eee7901096b539863155e3e28b8fe235e5d474b4b0529f0611f060379a3e4f37d8e0eb397c932a01ed678d9018242fe6eb4b5d36be482b957c0d11e7ac0cd842261417289430d12c5d081227d207facc291cf02d22a75087ec0d9cbce192600c32fc62c5ed331a8fc5e463dba0d4a0711a1545b4a50acb31a43f95991d9b746d162c099f7fd8f1221224a8c416fb84a589056def4002369655ae7708909b982b4e7487b6d6089d9d0a9f7bf113468af5364ff749831a6c407cf782f2f99927150d93ca91b8587ef3223f2693fcba2074ac93fcdcadb7e89ac564c4d16a83975526e813790aef8851ac4b6adff1e1c41f84ba7bdfafa026da3e291802810610d78c34329825710724dd9a5367515d5813ed59b5c8959392ed4c462537da136ee3695e2495a737e5ebb0508d569ef820045d693719f549346aa410975f73a1b2f062bdb21da90df7b420a55a0b5948b63133ca6bc586d5c4117d8f1877f3f13832b0ed040127ab0873a05d20fc06e857fd5d43400a8e6623e5253e0a8ef4d85087bde4fd177122448e6abcf6803832ea731aa1c95fdc574f60328eee7304754f625f27681a37264897c84df8cdcdff3eccaeb95051646187ef0b716d64b5043189928a3710ff9e2763b91841ce42b10489414382b319a65ceb0a66571213ca6d2f546df4fe7d8668188389035e5b435d5d719a83d8c44079dfabd196f61fc60c26fddf345c7214cb6cdc80050a7a1212e9cf3032be97285421af45af006c4a28bf674e487d13653d141eaec1b729fc085faf2d6c6f3dd8769078bbe3aa86fb8b4258f6a344a2cec17070dfc698413a5db57ab9ed6ae9850e6b8e460b6d6c08853d287950af928d296fe4bdc7ef793f811a326f74a23eee7166fdb83971e174f140f2d243c9f98a33cb8edbc18faf98fe2637196898be92a3c94aed3de3b5a30d233ff5b4a22600a5b91174c649e5e956cf66becf2f9b741132ee7f8391ca5ef43d3f45f96f64a00b50b9e087589e5c15c2f42d57e0a95e1ccfb449a032b6f75a196323eaddcc37f24cb75a1060dffbec358a00b0fcecea97d04d09a50a578f14c92c71ae60ea816c4b8daa0e43a61c9ecb19c2b557b2637445416e91b1ac0d7c6bedf4cf8298e1d57dbbeb25a25e3321c2af5803a4d1a14e3921e4dd681ce948d9ac27b82a43f6f23119b0de4ba31108cf02ec93c53038a7163f2539588f0262eaee92e66ed030cf299fa03dd74a3f0571c66b5e76f95ba204de9d3b955354f53bc7509dad7f8e7169de94c534c0cbc3bca43e853482439b71d5c67a01bd0d036d65d76e5fe1854d0e923985cc151abed7e4ae614cb4f20aa988924f0b0bd7ec597629640001583632bd388419db9e27947ba6e93427fc3082de2dd24a2546cf3b7024f9eed1b42df7a56b588c888e88816a1dcd43df2b0793856693bcf97abd227ed703653ece68694f8d2c07495ec490c722d02331b960d59c9c0fc7ae2b55c4da7ed976918924dbef442795c2cc922f031c995bdc79173a291c0e13663f964288efb98d839289a615f8ca700101a22c7ba52934bec4f54fac46b11f955469bc14bed644e40184d12a353945c624a4c04f2f8ec36ba18b1024f4b01698c1e5f644983216ce2e3af9a85e2206a5c683c013c1e9c06ef95eff5ae4180e3c9c923eaeb40e7b57180702cefb6d368de9b81f2f6cea761bc7a4f55fa60eacb19229e5026e68e0892a7dff4183db1ba46ce0bcc19e0fb994accd11e87915984c1a508e2c3487fcf240e721b0cf35b1439a51757118b1c1481c0014e1c9d78d7303623d9ee86291ff9589d9a82c7e2d28b14955a7e526b096a8b96df6bd71e7dfdbe6a0968bd9d283bc7b347c39854eff2d18407e77e6d4365230ae1fe5e3e35ed5f49ee1e0b8465004dc2d0a3a559615b511ecb0c0d88487908ad1b29f9969cbe2935093c030dfebaca2454ee37322224fde714d23e4262e08f87f0e063a0bc9fb98fc9e2aae10812e2202da38311851983a9d9c901408159b6ddd435b87e4d29698496557762d9e16bef2b71260ea3d507a08db74187c1e8270dcda32bd8a543a47b1705df594dc0d1cff4e0e3146a0fc3b0778d912e4c8e34b56ce63e880558e2fe6263a4f5a80e963e5b0ddabd64c5bb73a6cdfac1bc260447d243aeada03a64d8dd5f094a2636bf62421f8c25d20b4b26d913508c9591420143211f55929e5b539882047029dd47d7fecd7809fc434b1c3fb8a4a940e9115d2081023f38bc76faf1dcfa48b46b1e20e6915c18ebf4328ca5f54858fc2f4816df280569903e81e6f68db31796c12d24223487aa2a178297a14e4ea390fe483bb8660f24e962dcf05687402390042d30a2c35818fa9de9316b3c79627400688daccb7d23d96e6fff42d7f63dc560675861cc35f5260f6236c922dad351e69ccc0756f8ad67b6e323a13faed0408200a0fbc0dc3780b22e7677cd3b232237aac64f862f33f64084ae221348546aa298c789a0d17c1caf968d3a262a08ee9bdd7653747dcdedddc3dfbd79be29c625f919f786811dd8ea280a060b0631b69941e50b0085f1dedd87ba954b097f5cf7304b2cf63160351c78fa4ad3396b4e58de021136df7c042e51440bf92556677cd630be115e14070e272fa0ae6a6f9fec24bca37cda4a40ddc37c9c204abfa6af193787b6718eaff64c0ebe362171dceaaddc6cdb3b88d92613cecb7c832ab7ce503aee2a0a64ded8c213b91ef35556b84f542792f9b5321002197392f180ee9a78cd21cd98333c305eb1f53372745721af6deb742a1aa622e7dc1964e377dbb9d319ce33e94d11a5d211964bfccaa969003e1d533537ea2e52d46f98bf8917f57247a4fea49c91f6711361f04bcbb320dcc97ccc81c84eccf956111de122cf40fed5e855841a1b4546af842fcf0a4d1645c3fb9505e887ccaa61389e4825dbb23ba77335633c895b41279e46279e02b18e96f566753825a7fe98c2c62fb9527edde51e43ae85263a1ba44096ee8135f2030c7efaf90720e8a7cf44a58953d3d6903d7390dd5dabd20ee242877b680de21fb206a75bf68834759d4a87d31dc2719b513ef806da4519b6271411bfe9afc5b8f7b1b6a0cb0ff02895ef908066ef2609a9730be939a87ce91cbb273e7c442f724593e24b06a9ed26d1ab4a009e67cb218ca5798f35c26ca5b71a2b065c7f52070d5f5f6ce7389f3654b49e888bc081df70334bf20e2c7b2276c07dfc2dd78e0fe6f260f2205c60f882f14180ff33400f5b9badac39d18f13ae12cb7654028ba0d88209d560d02dece6ca161f31249b7282b149b84d973de88b45044d236c23861fd6bc3884989f8a667dd7226a65b7419a1be57b9f50d40c73006ea32745945deaa4a7cd2cbbcddfe101ca46c72129a295db4c91f21895fd63dd4bb9cdaabecd500475633b252144fc64b93ccd6815bbfa47e73833511ac69fd38559a9a08fe6bf67e6197f2fffe35d51b0dca43059e8e54d206b7ff5fed050e71462428f233764f9cd883067ff7477984140b0acdcabc8ed97b6c001a6bb506571f5989e750145cd7182f8217b1e786469987e171b8dfb6007d39873a0c2088b278a1957d20d06073cf723b09c01008c52f118d5886749f0625dd612e5681f8ffe209a0124e6d0df07a9aca39029feb3de5840e3e61754e319a7628628ecc7312b19ab64cba12aa665c25348be574315432a751907eee057ccb1655b97718795713db1a76ae05bbeccb77395c9318e8fb539bc6790098a7190775ebfeec78ad4fca06954db4a18275fda419023f85bfd0a2fa705d335333ade35d661170fc0898a8f0759d3b2bd097a2080119db3d99493a626e3d0c5e23e797225824442b5935c28e2223020639764006eef088eb8f57a9d129859025f2e1d5cf16ecb8a6bc6141866192d813765ec3c215f2cad6c0de3cf190d94da301551af1847db2e0796aad32049ac830c25a10fe15fabe4fc74af39f9285fc19d6be8a76e55b190609581c8d070d0f708b8230bd269ea6c1f2d7e9681009511c0c9d4394a90b6548963b35399633bbb9b054a6b36c44f28cbb1860b1fcf65998b991c981b528b27147e7000001ef66a784360cf301a1bea9274bd7ca70baa9ecd245bf84fc825de6e579c629aee8f7ec0e867b30f742cdc2e45a9edc8c890b5075a8bb4e7c2df6c7e4a4159c989d941d59451bb67695a00dc60474134656936efaeaff697739af4a57f9272a9a6aeb2507b4b536be4643ec2189681bde910fe99dca4479c6681937a77e4f1432261a064efdde8819d950b5b00a4bcb1a4eb5b1a696ea43aca006ae917eb0ff5922430eb1f772f6b97ca30a578b4ec97585ba188025fa12ada180e563e3387a2e2d875e6db425555f31b79dccb1475a24fed96d52316aaf6a5cd932f85cb107c7f9824835859b396ce9302b104951c9c736befb0704ce8f5947894f4bd204065d4faa47f212dedf21149a602d261954d7cc52a896e81d7b578b381f9c0bd7694ccc7bbe1806537cd04354c002680976308d4f637af2d4d988beee962611a905c8319e3d360445f3741e7977eda96c0678465a7e08f367be9924f75bfbdafcf078b89221532a0bf145e6b5b9454498d4b35c2f993a346b39bfe603c57969a17083a49d724b09b6e2b92adf050c6e40aa8f90d38b5bfe7a3385764c05096cec3adb24e6e29fed5199b2966911bb70dfd6801ba3288ad9728f00ebc8aa431ee24fcde9f0457780e88a57a7872734befd65c282c5385c13cf1b7e20dea9f92ea843e015f2553c27a8697e0de286f011d7de2e5f185bba452553ee65bf820a04309bd75dcfd79eae5366060f09a9836a0723c4e9beaafd9f59ef4c2adcafaf8d0446cb7f84699a7efff1d32411d807e771e2e36b7156613f36246b20cc94ab67013d613b6b38cc7799445b435f1f8c0ad3e8d2a051575de7f44e10f00f320e664aeff8d9fc5f4ef2eac3b64a6db536b18d5e135d0dcd30746fd57fbbfd3c5c26fd1062e76e9594cb2dcc424cc8145025b79eff727ddfc86b24e3bce3161eb5a3471a1a60a12333c7c49496ea072ebee09659f78f7ad3dcc29bb7dabd0b1a4233f623519f32b6604c9feb7391181b2f6d304024dab983b833dccafd795e83f1817a87d00618d12008456ff77f440214ba82fc88143e5ff403c07a82a3fb39ddc2544d7d45ed2ffc267034b3a60291be53480a266152a8a30c16789d4f90217a19f7e5b4fcbbed992bc6b064bf494888e01b2b91d04d362478b4f6a653f4b7bba916655cbbebc8bac47745b9a16e34e4c709b8b05ecc39eadb585c847a02bcc593d964e63c423e414ead3113cada04b6fd6db9fec86f951f3a7b51193ffd63c8f81dc27106535c05c26c0efa52303114f43ac43a16f757a8a9e67f4d85f1f5cddbc190dc4f58f6f6333e0fc847ae275addf512e75c638f9bbef4461a0be4eece9566d315e9aca6265c8ae288fdfcf474c3e4fe3023cf1d4fb04e1222aabb1920cc118b7f489da1a1994649d644482b00764d04064ba7d23f054957b666d8012eae22e413485bc2a8d3ee75fe0a8bb80582f50ddd3f5d6751d44c63f76344f6d220aa133df4de18d86b5df2fe00c6a2fdd0233b1849b5a644dd3fd3b3d137f1a4597402c6faa9fac072a9f9c55106cfa8f4a36d3be334585445be77da1e8c072b20d8da1d464ec5053cd0b562f183cc8d8f1a1363aac40acef5dac70985f2842507870bf9dda91dced1a5c2c9a74eb0be67ae46e4ef49945208933da97b2a177ba5525859524f4c394295cf25fde937e1b4c02441d128a4345dfc4eab765004af4a97e88343dfd3e93521815972bacb25ca5b7226feaa98986cb2a1d4fa6634f04232adfc92cebb9f66b7bd5373c4a37c323f3301fd88708c52a01201ea30e6194ff2109128b318dae1af48bf420f4afa8e5b18236acd8717fbb265a7a72bfe1c631906c4b794e707b5ba2f94d243709a599da6e28724bf5a080b193315f9bb3f268f3f0a2980aadeceb715d091d83b5f048556b8c65adebea09ce19c6a526c00f44f18cd92a7ddb288bde3c681a513c099031b9af5da8f94388e36d31097f1dd3c128b3a88d941841c5afeb9cd295752766d3e85353ca02e3fada6f6c785d63f715bbc51cf480b4861a3feb90280b8b1276aab953290e8c30654f6fd3d11a6dcd1891dc51e6a51e5859a49c7e1fff56aa0fe80d9eb4d97cfca6490ba583f388702a3a41b23787d1b4d6aa81546e6eff9cb013a3fcbcb115e3fe7e068d8b21c420dd364283e4f1a36bc68e9139476546dfda5982cf3aa138181aaeb04f7d2b2c49276e90ed4d438214bd8d471df05f61c706ceae72ce548023919a98f7fa3169d0d467f982154a0f714c1f7eb547a58e98b57f43c4c5f33f6ca381024499232988d1b58b565ab2435e6cb9c35f25b1209669968f2f587d4a73aad5eb9144dcce87c224f6e78cc906b9525cb50775048c6a36263452d3af46260b8f72a15036431a1e6830fec2f8393935609f21f119798baa429fc4a201fd789dbc597fdc1691e8114849d2451cf3772b9178fe2712b59c9745ea4b8cb103924d3b7db400e3d9ca1bfb912daa478008c21c2345ef9aface9f66000388bfadf97ac2bdd523d7da6f48f88e828bd30a5bf9e5751a096e5de821a194d30a48c430c34da648eaa5bcd34ea197d40854be0e927de87d5e3a93a8519f5b56e7a978271d207eca08f90e0197bff84adb1db5c48c1c899540f2066795462ab2d86a01870eebea1f67cf280b03ba5ba14b0ef7cc42ee7083aa1f373386abc9160c50262d10f93556daca5a180bd1213564d6bac95973d0c9519c0d8559b38a2b334645a554f2d563069e7b72e3d3bb89a89ddda15b31c30c07c373e39b5e674863b72ee4e1fdb42265febc6c16a9df5f2f8819fbf7666f967007effc78408f46fe50f619187349984d3f59c7ed3b46f27bce1d9711ce83e43a6f657d05a47f3f5f4ca39da20a6a947ebeae524270ef6fa44fa952dce8f527eb6bc8818319e73b05a063723d673108e2820b30285d8f83eb689e2eccf6ebcf2ddd63e313bcfb3e9497f814993a251420e79efef3004cd65f44cd16ee16c4777dd44abd9eb8301342b3d53ff223973471d2ec5d2720810eb5e1da51ff9d583e1deac8d97aa84da47e315f31942480d10b8e3f7aecbd0b160a11084df7b12259a0d99ddb6e606f1439551202b33c5bec2016815ed70de6b3009901855c7a3e90c515a8662bdf4827a25ee665ca178c151ab0ef90f464e42b9cfc00fe404e2629087cdb0b57210d77238fbba36f7d53bf5d302580c34805f77a32ffaaa06df248fcd36edf08567e5c99286b0ada10138bb8db9ec1e7917e5303eac1d3b7165aac99d97095718f43d1e1e21f961c8604360639657878cb8d2a988cc6fe14b4838471e93d9b5f4000d6bd16096b047656f0a3c4a6fca24b4ecfc37a1e300fa3b13b437eb3db4112ae99913a82eb91ef2dcebc719aaefc0e0301a1d18c76b31ac25d20d8837c504df01834f6a7a03d248e4bef6deb4e409a82fdbf2cc780073f4c748503d9a881cec4005eac9951d46c53b792d3c6c732168496899510370765aaa9ab611210a35bd7bfe0938d4a685ed62e1ac8a09346973a91a5a8c939eb7243388edb0db00673a351b1733beaef82776b4b567812666fe0114ee0617e2340b0d4f0d4358080c7a09a961f25cfdcc539ef61621ad90036cc9e15c009aeb6b252452f3e5d3f5d3094ef5c23e793a22fe130e3898f7632c0278ad9cf005ac99510a95d48a03afdc33be9ff03cc32c40af01b2832ea1f51d181c14b186f5d155afefd093e31f94be008be92bc005260751aa4d83009b20357749053ec65d5aaf98f595c1f185807ea0ef0ae10572e683284cf7f4dbd88b4def0249eb4f946a578153bd4866ff0ffd085d7d6768c37c65732b9355e67356034f17a9079541f243c462b031951cae285944be967cb6148781a62b03fa70a7d4dbc7a1a755c2b4d1e90b0d47d97c14c3272af5edfa3a6fa87f3c0a80879883c2a4cdc96c406b3e461c7281a6c26a1ea040428e70d2e74caa11eebbac4639f8decc0edcb82ce7b68c590a9b4dda57744bde3d7cdc9e3c274ec5b73db9642d10e3f47cccb4614017154daaa39991191e2f27352180cb10faf3cceab4dcfb10e5234038d9f61c4e06106ec690af1cec6921170f0c623e4e04337a7be0ef7a0ee1230da2734211c5da6a171237cee84686c0ce14fc6553531cacd0e40bf13ab6a12841f6833d39481fde961702522b84588f44be52b874fe68e5fbb461d716d7a9b49a923f02208ab0f55920afcede5c53f3ee39e083c9e01b997d9a6e0f04021e8678dc8f6a3648f8783e7734c6cdc9b8d7dd10a4e79435b3fe402733bdbd137db27667a7266538da8747be52d6c9909721f9b9a13bbeae1c7b014423a14f18913403b0b1bbd2ec02f1485641a9286ebafd37018471432318e0871b29d78683042d07c6bac0a083c06014dee087606b92c2e9219759d7bf85673c7a398e72682dcf681f29cb410db429081be670285e91eb4ac28c70d5a9504eb9a502f6382b9f6f77c250808b97d1f5071b1015d55002acf2f9f3a09681050c58f9b70deefb7addfc0d6b02057f60426e21f6236cedf3d9f54eba4a743f5e29d1139ea8ab6681c02bc728848c03e290bbca49dd959b82ad7defebb7c9d789696dc215215cd533684f108c8d24b2016cf0f39e0636645258475393cad28f784371b83c5c3f7772a4a9f7dab0d241694cafde3a086230b668b27391220a6af86bd93c4264d9a1b676c320626ab14187f51caf9a374a9961ad5064400834365d01f6f43405128d12353200506d6d3c3bcbc184c7defe8346c67f45a8fb83b1b679cec59a613f762c992583b577be8082fca01b3b230b4546301e02e3b389326ca9eac9abcec968451540b68171a2ec2b6751f8218e2063df99b88ef45c76447dc1c6a6bdc886ee0fb7322578d10acb78ed89f2a9a06890b80ad54e0b48a6dc8ac41c04c35019f06a57bda24a7866e461ccf5055832ca556880bd87a418065eabbd111f1f6006dce20aa55efcda4387ba4ab633d31679eed833ea693f82c6c6fb7a9b826fea1b3d3d1655b442cc4fc08eed39567688d5a10178a766d115b21a1f2e59dd32e0f7c6a0d7c4f06de1659aa683b09962e31a863dc582b2946a1de2fbf5c5aa4d01df379e4a7b78ada380cdd52ada2fd737e043546c941d1407e156953a217cf7f7711aee380bf4a8c6bdd176efec9010a614074131beec2b52718696f623212a6c01d18ed0e23c6b8bbd3fd63e531999baf804e971e1de1e4dbb439446c59f9da1b097a2a95ee30bd0b2c7bde9a7d03c46d8e6ab01d7a59e5267704fe8d4668b5c82fac7192d91a60d547b123dc8a8255ab43672412f15b0f1db95f0479fc2297017f4336fcbd9faf54bc5495a22de5b0d359c6c33e872ce0fac226d0a1fd042d0c6448282955b92cbefe9844d7cca12b4a19167461109b20552387d25d3a7ade56ebb79441d50497b923651be1551c5eac20100e88492a54bbcf211861b0e41204c5336671b5123d2317e45489b45abc79fe55e0b5b9972d21f0199994d979e8111caf33ae07d13a15853f88aee26a0460522a3f9615954e35aa7a3d9fd064951b960a9a15914e2965286d56639043f5d4dd29a364dd73414d0643a9550dbdc940e351ede8c5bb304db3158c28dec2445cb016896227985d7588fb225afad8707771f0d78967d2eb09f6efe21c8c648cc8867914ce68e5590d396c74bfdfd20fe8aa53cffa8b14b3091d466139a97c82b6caefc0ff078818a18f8ec208c20c84fb214b2fae3a80d2ca189b181d6f2a9850b5d79920069086930a0298df90a71ab052d52fb2507164f3c1b7518c71fbaf4ed5e12d4dfdbee5b0199f4b7d0cf1e237352891439c9aab3852ddd268f52ce00464f03b6443aa40e4fef5838f2f5e90f3dc05d52d8a9e2d7d279615e5eb321260fd19105023763005ca06728d2bbb5620abdd05a5cf0cc8c0c24d8ff86d5ad0223b3b243bb50ca3fab25bcdc39760c04d8f2a0bf814f19c68119b5c8f0fdaa0bf7aa75274cfc30b1672c1810a7c42a0f9ecb7c8db58786ee6a34b91f792ae2f7a19085890aa653f182c844819aeb445b387341bfeb21129731307fafed2e5177b3664611e9b805e242207a2090498f8d3b41f7b4f03083e21dcb35713fc0759fa9ff6f67319c2f6377a7a95895e2fa699737cc390bf02d15be435e2ef4eb4678aec76e69264b880090eb241c04488c94ee9beaaa43e3f6c40d6a8d637a048477170818d598394c88540d07c155a3b1baed30fd878edf0828862e5dfc9147aebb178beaedc8e3919e2b33e5493cfb79f8c39267c8a8ed757a6bbdf93bd0d2ba6e643d076fe1fbb5746bdbe755cb2a665cdfad65fc150f7058cb38e7190095fa859f9d51abddc858cd2c86330b585d53f80d717af937454e00f735ecadfc0ceff89c2a0d9e196ce794d25e8d72a55f18bdde7fc8ce1943b08721bf63789929805d48a3ab52276c320006f6ac1e0db86b8703ba61986ee8950a2ed1be38a44defb2db55393de7aa1486a1e02cfacec5d1465a63007fffc92c5af01fab2c09451eee72fdea689806842475a2a518a982df3aa8fe691cab893add33330d7e8a17e1f3223a4b0c414303b7415d90fcad51693565eb4b3fe26812a25befeef5f5b73fbdfe182f12bd73c77061949c82517d30cc1e22a49dc6198c4211af38838ea5b622e5515568b297eb1eeb2639e09582b1a3704b2d636335d85e916e08a53b1f8f175155a098cc6330e6307c7466f8e790833be31ad2bda23d935b7fa5f68d74062a8f1a18c9fb7b0f80392ad1f78cb5b40f61a7220fb79caf4b80eee42b0f6c17df375e0b4fc9a7100af0c9c976cd33a37189c9ae39d853e4e9603537be8c69a6eeff1522f4238208c0beb20ff3d474a47196b0433ff0bbe60ec47e1157bda2b753de0dd8e0784761f146a80949dd0bbf526b87879ad9320635b3bd9df280018b3dc1c65f7c848dde78b2eb20e7981a9ae7ff7a91f2a8f5ba347680408afa6b659749bb26c9c48f72665d097445d2e7e4dff201c0ff010f48fdc207837713eea25af7f158f73eb610e53462024c2c9fbe75ccea90d13bc7f1e448c4b36cfdea86c11641f7e1922d00f2f9e1d4864c91d40607c97ac8e2d8031ebc7f5ba288f5e1337dfcfeaf0d3d5a12019c0fe4c76371811ca54069801aba595d32d7538ae73707f3612655ff26c88d4cb4d8831d5afc0833563d4ac2039d2ea97363434801717a8769013c11ac0ed6ae54b034d4eb059503c60106a7e21fc60c36b599fc90ef24603d84dafd0c4176e38c3ce392f7fc0060960cda47953c2990e57ae671ee8378368294ee13b3d59439897668c106385d9decae81d59278fad64d85ca62f08228ceecdb38bdb70f3f1197f809e9a9a30a6234447e6526f07bb7d535471cc348e7012623b308df1ac4816a19b529cc81bbba2c438872b0da2afca4d3496a304c56ad403a6070ffbad1d02dca4c3b05d90f54f1d82f20c09a1e05c7bb094bbe282e06ff6fb91bd5801229eaba3c1d71676d787b026f485785e9c17993d7ee3a27d53736e86c814a754cd144ffe2283c1359bdbb09422b45c6d29f592cb46da5285927d6546b4554da13cc65ee1fa22740161bdfedea00c7466b2643f0c3005eaa968856b49ad193fdd639bfa7d1e3674c91c92b9aac2bb01bb26b0a6225e6da9ee4f4ba6dd4d75d593ce761abf0998d6bd6cd2d9d54eaced69b7423d572862cb204970875912faaf2350f53e0a0d36e159c77a6f91603a4902b2519dd0d93b259571604dbf3d55765335dc752fc94b23599b08b050c2ae0b5448f0f97dba61425e89fe7d4f692c3e57d0838fca8777e9b514e9af1ea5b4e804608a616539000d469c7d6b33ae34f0f729c360f204f912944639c82ad312c4f31eedbfee0e00b7834297eed56e23388fe37b328d8ae927b4a8efe216611d8d97d8b835619f899c9581c2e427ec6915b2d2a4ebcfeeb0bfe9f9801f4c0275d10144548d8bd19c13e712d93349e3141c881a715d76259441ce4a2195ef70f99707f86991fe35b0a36561d9b11ca3fc497f68c613581ac83b8cdcc3dd9d1bc6eeffe13dddf0f6b95c135c8fbae10b46ba6fd8b91b427cbf79dce92b382b5142dc4469d28a65ec97e83913bea81ead74abf065ad254eb6d3bebeef60a89f5a25cf5d00bbd09ad583fdd17e7a6f186f8179df9d91919469b5c82e9045c8dcc59aca244253ace5f99963aa04ec1b91588041aa28566a89a220a3da54eeaeb5f889d87c1d75796bd00f5437b901c80da7cceefa3254802e8e2d62050984cf78fad896c8178049d84be4b4e1d6a1960795bec6df3e9be32b2c7ae6d80041627b398600d9eb6b7d3a25f718d40b5f18f5384d27782b4de90bc418b5c232b1614e4520b9fa6b47f5700806530b95a170921e1a53c4fdcba30f9aad59ac8b28710208d5860a529e1a1ae1208dac52ccc0dacdba22230d71b63b32c857fb1ebfe939d5fad572ae0828836a8d9fbfee7b880cd3a4278e5a3463b394849e731c6ddc8387ab43e443f6beac6971730dc49f700632d089cdc7e00deb0593caf0ec76d05df7b192f73f23184d42b4f7ecff851adc419d75c077eeac863014de8a58f9fb325ca546421fbad6c62aeebb8959f51e7a1ac9d29b9910ada085c85d5f1837e1b57ae24b4b93f5df9997cc73e04aa36d6a07e0d550ae270cdb3be37da01e4af63c3931343b912cb7debbabadb7b522d5541c335ef0a5d0c712d1e5d38de53f40df5585322467df7845f810c68c76dda9da9fb5a6b7d92fd5b00e9019dd88f5579c552ff95aa491df6dd2fc09cad9ee085f17ec652a4a385ee91732c0557620bde0b8b1a71d7e6103a8e90330d7f82aa9872d9202a630e76ae9c1737d1b658b124a124bb19e866afe9ccc25ee99b0e00a8214fc37ddd4600b27354bb58b332c9d6d78e690052955f4abb8cd7492714c59e1f1a848db44fc601834a812196c18189c36c1fa60299c464e1b246264be61436c4dbe08be8f782c1d1383b32b84e6fdcb922d348c2f6273c4c8ee0524989d1646388fabdbdc56fda910ed365a5cc8694d6bb314956ae4b39dbf999381cd7623de15b9becbede367f99c1db1d373769d30db38909b162e4339603e18e8fbb08d1b416463454ba419d9045a350749ce859abc5cfa94364b688a6939f88b325fb96301319cdaf405354b9cd62f8dd2c2fb9b90189a60bf9ab11f3108cdb9bf0fb5d7537f67aeead166028662ca07aac57fb86704e0d83217d64bd7fad12acf24dfecd197e25ac6bf9cbf0cc65c9bf148ec0cf7fbfd71dd6ab89358a7510a4f014879568fbc7aa6c6391c19f026879578348a59c7d35786fccac19f0bcc5a7d790080459e0b63d50606a551bce726cbcae2c01949aa3d35d47a5aa461d5301fd0b5069c03439c8304e5e76258283ffaa5b01f32505105f740db868ff561dbb0a45b2e2b72362250481d05083b10862da728fb926bb0b8ddc35de8cf87d4169562febd1a524e9b533ad75b41e59e1c8a7c1cba1c9fdcd16eada51cd5d86769b9073a9c442e7acff625a7dd10ade5c05fd17f1079c86334459d97ef4ba29007a5b4a64d804ce84b38bdcd8e02c61d1f793d50c60c7578cdcef582ae4dcca74625ab6eba6a2828937c2f9e6ea9a56ada503ae4d900b0d4ff84f7b01c51972891285f8676c659227ae3861b0efac63ca07668524c3264ca2d3ae502a0a342f60856bdc840d4a0d81f4018378b36c0cf7805f6b52405f25a29b75417622f25eff1ea2337f286acbae0bfe368d0c4580b941eb97328ad4623d743de60613f6cc48f248905479e7467abc63b1d0d232c3cbdd3cc0b05a7d93b49cb47e1bc20b05e4be2c807df9dcc11ce60e8f85c6d8b2cad1e9fd424b5e65078c778c41de4972bb1136dd8acd57fcc7f5f62d57585eb39d43d807705ddf6e0b25bbbf0ad35e417c713aaa90d1e75a3e9e411b583137f019b9453bf00d948ec7e9b88bf6e193e0dd3f8286bec8f32ef8ac2c8588858bab1c984295b7a44a0f43be0a3bdd44cca25f2c6e344304047e94ca94bef7d4567f71e1cf7660284223276374c01ac173b543217e095f77ed05cacff9b88752b540c300f6ab1612f8c8009e470f8e2b7f3853244fe9342640b5705949f58279d57d5a41e2b73ed2942981cbd13e6aaedcb0cb56dfdac427700bfabe36698f757af72b8ad9ba5a25eed143c1bfddaf0f0a1ad613d3857212e6a152a0bd13934a7f9fd567a5230516468f2deb78fac82a90909ce81073f2ec935291c3f09b3279609283f490fc758819e09d92de5f3ebce545a35d564e576f67f45158da1d4646f8701bca2063db25cb02a087817da464a2a48102e3877e02aeea535fd4da1651a1dd565618dbda9c8de4c4fd77c41818a9050688b2578ccfd1b4e23c17de61ac40ed2d61167afcbae548a807fb67c010eb26b9fbb4ce3791f228e70e06b48e8bb44086dc5f4072352bdcd1456582f206bb3c078d09787ab047ef547eb688f07e30c7dc30870156bbe07a2cb6f716758274632606f0089d4ecf3d73658cb392a0528a80f19779e538d600323ecab97a0e33e9a25d1817902306e131759c315b6857453287c0c46930b4e80f0a7fe4ee082359b21a222a1bc1d4566762e9dec8f5dae24eec8ce4ae9b51d492a20e4f923aeec1b74660d4b5afa370eeadd288e63943c6c25806f60a77137546ffba9091b0ccaf2093b6ea353abbb5cd03c56ca06b08a4743232cebe01817d8f75aba377972b0c7386814b35c940b5b1243e75dbe67d8f434ec204223092f4630adc1ebce4f276c2604c0b9785106138d63df3fe69681d790d1a054d1c18d4ecc2583cf7fcb087d5311404e5a8a51613f932174ff93e1bce5580b80abc9a025281091d46340a90c63a0ae926344f531487a6dcd78a6df4dd7fb8b419a6c9d2ca3031aa8c28b5d30ccba9a0ec5922617dcc1ebb501475a21a6bc70491b98de1303dedf668a235f05dc25451a34654439ef38be72d3c3d1e6cea9a2f3a7aa3040a48afb45665745254483e64dd3bf0f21a021ef8af767beef6fd2dade7b6fb9b794324919670867084508159ce241391f8a15cce82573d99e4721215b0e69f32116492f872d7b82b1e7130e23ec1d8a6c5761cfa71cc4d0c721fdf555917376396f38e47c05a91b08220404bba6b0ebcd3b8edbf88d0d39a72b67a753994e4d3913131393eaad16325f2b538dd5e9baf47495313131c9549ccd76739bdf820809827f88030df156557df97d9021df97f3f6e921425c3349fa97ced70f5299aa4e22e71f80ccd720383f3ecda87134b665dbbfdb6c1accd7f82017e69036af43dedc5811799099f7b75a8c968b5aa53a3950c5189732ba36d197ffcca9413a362f833ada88be74727490ea2269527551261a5d8e32bac4b54a91677454ce5fd6589783973dc3ecf944a508ce133991d3245a8489ced3292f60a638207b782bcc2115e044b3ba847e7e5aadff71ccb97e2ee70d290781726f8abb6a1512fa11125acaf4b9a319cf01e3e75a2232d7653ae21163cc43a61b7f0d32027feaefbcc7737e9f66e1b0ff8d7bead2e777d8b1483211bfc683c4af9f45b2c75d78f44e63f14642f0e78526267470ba56561a0511b267ed7ee62df339dd6920d3b6e72cf2965bcd04ffc45fe441d8b1952157ad80f1ea3ce6398e35dc98c70935720005f53eb814972e5251f2a42f4f33c98125792b47d3189d51fd64cf9f547bcbee40d2d122cd9921cb38214f5697e9aa22fe39ef794e0aeef0f01dcd2ae2f99de7016f686e6e6e685c2c0ae1b5f3532ce2f91d6dc4d22c9adf791e2328453ccf7a239da74639cfa3270d196408c9d344a6bb88e76368232834cf13e38f5e34ba88f53bbfa3ffe8455af03b3f027ded3c110bf45797b3a6ce4011eb6368a30de60b5797ebdc6258f05acfea22f5cfbfe6d14444a014fdf3b09ec86b9e8fa17d54d78e26b2f33c12d8d19345b2a69e3734f3e666ded0cc247b3e157948223188fc5bc062c578124a510ccda32d2089b0b4479f8764d17c8c9faf1d9245a3591aec3ceb79f07c0ccdd2af2745f67cfa379466521a1a4a3b3a6f665ee88d66d2190d79c81d92488c67e9fcce4b20e7799ec863901a5717cdb348223abfa369e88d2e799e88cec77831e7795e7422a6b5e753932e36bdd5a0185fb67462b030d68941d2a51f37dd79fa19c88393a44b3436714c2239cffa1d92f544747ee7a994148ed40d95da8941ea3c0f99f3536469a0f33bcf43e777348f9c67fda4d134a665c89c8369ce5325a7b11c927271973f5905cce2d0db796fb5bc2585e3230da494c65458d08fd14af3294c1b3594066b01be699ea563ee3987687e262fd190b5c8f4a15f436dd414260dfa744697350434d3a7359cc77ffab331975c75d618095c69a8a555afb5d65a6bad4cc250e7ea129fc5037cf1411edf8f19c4efb94ed0ddedc67a02196f501a9235dc55b1cba02293d60aa91a214170e74136220265e6dd841944542f3083beba6acab905de4e65a02eac204e2f5e359ab56386e6c397919973cefcf44b9a65e67c99cf794e994c43ffc7ff0f22c4c8c78cd498a5b254760e09aa770c31761e78cf3d11d5ab5eb45b728e4934876ab63f03e690b7c73999f0a5cd73439e7bbe549ab5237c9f2a529470c4fc11e44021777b4fc1a713739cc8fd383a753a6e6f15f520f248407ec99c95166a6cde62e17f244b0310d4b3ba42fdbdf72c1edf7bffe9210ca82ec7f97ecfe25ef5472f4fb32e0f56f7e37bd5177dafc29d2e12fffb4f1b893a08a82f77b923a77dc31d44e5ac95dcd37d1fecc8f287d57d24abd33c42cdc21f3eab7bf18f5e1e8b8766618d9fd5e9a2f0456d04a57ba30ed447af8f7ef0b3af2e77f4e81de09da20ed6dcf66a16fd72d6e8d482fecfa169abd17e708cb9734f054a5d8f53e720f27c23a55b7da54c1f865cce5a8f1e07b0f9b2c70e4bb78dced555675ceefb9cc39f9a6c2197bf417f7fa10c727e19199907694d4d4d4dcd1fed1a7286863cdaf4ee194d004a23d778aba7ccf8f14f31973d1867f0bd05eaef896a06d2336d8fbbfcfd26230579bed338e7654ed81caa2d6ff5a81c9e400298600c24597cd5ff84b5a6227790020075eba706f9f8eabcfdfcfcd4e3fc580522c6d179297871dc79a794cd49085b4af0e18a06e8a65fedfb73d0cd2f69be317dfc9843d5fd68535dd20c4c1acd8714752a66d55bf365a871a0f11de87bef6b70e06e0f480edc8d835f4562ee31891f879c791539f3204896cf7a500764913a640e39a74b66becdd3901eccb77970d2d8d0d0d0dc7d43f33536244d8d98f34a8be2ab4abcc3f99ffe911cc99ed68c1a42b46cab8c29b91cc739c739de382e019c3f7b621ea3b573a649ce9ded4fbb4c707eaffb3c4f5928b6dd976ad26cc81cf222ef3feff1732c0dc0ff5e02a09680a785b86f85dce99f96c692e7972d1c5626cb0586a6249854d75a16955cfefc30115fd4f993a2b69ad0aa7c9a55b40d0ed9355c7df82a92a5c1eac3950e593c444a021977ad1f8c1bd4e58e0d64dc5de7e9ec2debdb3748c351fcbeea12c8b824cf5de6397acbdb75531aa30375da03efe9db0781fcd8ded34969800f9640c6fd7df71fdda08f93d2a8ef7df7def4967f4c545fe341aaef6ab8cbe61a845ff6c7834106152239bd14402e5d6c5ba06a332c539c644f9230c59468d294182f37292e5136da6ccf396d92284e2f66d25470d3443c6923793e408a0c14e61bc9656b0aa1e5219583214186ccdb10212121a1214184dc820819626b90213ecac965bb73e9600a5187c900ce9eee098b11d410eae004f9870f4c6f4a18e7dc026343cd98261f64c4f063e70e358c7bbed39eb40c310224ea8fa48f15b9be9d35deca795e1921c8f4e73741f10ceafe665fe5eaa2df91a3eb31d32c3cc9db3be77c3badb5f6ae6ec77d3727be42ba7d6fc79153dc2f338932c05bb5ce5ae7acf34e3be79c13055b6bcdd95b638db7eaf8f3738d07e15725725d9b75d639c588961662a081d6724d87f1e8f3bb3927ee640c81a95bd1b39221b79eb4e2c7176cc894882cc3c8c4fe018de145a64bb116688c2679e09772d9836589d326632b4c1a2cd86c537559186c069b59d8ccc26616366b22d3a99bb764907053ed404377bd12b99cb01ed56dfb615b9876a5f6a9f745f7056e194d7269c19e2419e791abd5a095e6a654535c4d9ce26ae11457fba6b8da9411d989da2e51b9be6572a0259b85b1a5551f32dd5cbc55bb484d9fca79315dd56a6e0a964beb54dbdcd4ae5c6d3b3950978a85ab511bb689d2a8514c1b1d501af5837e385057a5e610f7775251a718b7e428efc2843113a5b3aa5845f705fedc4cc06d39240e26666afad8bc55bf036ae37a3169d4af5bdc755740c5ae17cce865d7e7b22b4db25daaab3ebdddacd3cd3addacd3cd3add5ce64cd68969774c9cf8476fad2e52a95e0cc53ff2de282c0a8f9ea8c2c96892cb7a67170bd4add52099b7b65ddf3a554bab41aa9fd9da76a95b0dfabeb6987e698ce524298c731ec77f0ec95bb5e438981279979cffd1e66033651e3919b5b134673427cb7a756ddcb57132991179cee6d75dd65b9d554e565df53137e33e094e46c1e92d3b9bb1e8ab9e65ad18beea45ad7a2222c521cbfb30b47615eaf9b29a45f59c3ef44b9f85abaff9c51aab41add68f63ceadd6ff38ca623333dae803afef3fbae5ce2e964b232f945f6bcba49591d689b4b4a66d6dbd55579daa4cada58f8d49bb3e0ce39cc7f16b9d7948642a65f3960c156c5cadf39514f58605164eb3d96c369b952e639a61ecb719bdcde8cd6b22d74a4295aa37f0e7f392e99e52f5bb881b8cb01a3366cc181ef665b5d1075eb53e91db29de72a95d7acd69d5e518e73c8e4bbee42191ad936df296bf0c156c939d551a387b26b561bf14aa957e65a25f659b665b6bd3a60f54ad2e511a54575d0a3975ea692157cfa272d6d65a39ae723adb8c33ce18678ec6808ceb679cbdf5b80616ca00d32c0d6f418328eae1ada794524a73b05f605c6606549c39cb71d776ff5383ba9f5151c85f98c277b2eb2182a00782df78f366f8148681873bfa8f45b6df3d7d20de83e39ceb541ffe286c485211a50e7e08f93c1c9233dcf579b88b6cfd430e7c70e01fd698607ad8aebbbb5f5b0f9bb5e500ce1e5b6f489caf64f717e4549d4af5e148a9be8eeb4221e28d215ef27df1c3f70fe242fcc12eab9e95f3ea698c6e0cb627f1f126c47f08927f5ea998e03ce87ddd6c776fb5c01dfcea5578ab5ea5cb957ebd77e28dcee66b74996b3eab9e136fa85636dd156f745e87f3385ff3e5940b7a5651ceeb60b104ccec959375f6911cc9eff90d828f7d71c89f19def2bf194c1f7f2297e55b3b20eef27a83780b4847d627aa371e791dd7dae5c774f95b5bf30c360fda58a82120f84bb9fb297aff1b74a321b025d9932948494aa0f55fcadc7758b2f7436ebee36ede717e14ed88f3f6e7fc71fb569348320e79430e7120f0e983460e3491863890ccd33772a0fcf4296cf5d4ea00bfe671805fa3774de99f679ee68866c65b2b585efd78ab355d572586de83159c02998c86d050197d42a750249a4495c0ea59b0bdcf14e73c8e38eff842c89e0d91db904614667443da10551b4cf66c8e9c1a3284088aef518daf9cc0492bb520644a0477bb373724ce0be15485c1781b57c8be3343e2663dd7e49d913aab1625a34dac69a9537aef0d0a83cb65655df5bfbdb07bcaaa7f854f5f2b4bf3d5caeb05b32390ea8bfeca3aad766ddf03b5c5813820de739ef5dccee37c0f9d1f9fc82f4b039ccf791ee3b334119d1f3f47138152a4f3a376223b8fa367d5e58ebd7a276985cda119384f593c5fe218ef896f693528869ea00ec9b279d61fbd7648968d6669b0f3395fb4f3396f63a38b749ea58d705ee78dc6dff9a3978d4c4d6263e3394c55a66b4f4a28bd0c8dcd8ddd25072a2f9102aed24d427fd6d97c5961369a01f4d26a9095c9b0954d1ffa443e3ece4f7107d2f8384f6b8ca624b0d826270762fdd4a035879f0c84223082e390d7e62f17072aadcc81eca67fa11c683e1d9164d3bf3507cae9726f5436fd3b75c5781012387b3ac2894d9f43f2200b6637ca81e653d4d4a67f6d35887bd1a7b1b19c549303952ec6c9814abb64e640e58dd1689c9403b176143d7120aeb669cd61534e0b962807e28ad8f471988ca903eaf6bf4b3568d4383fab0b07e779d02f548680e16818cee3e80fb09e48901fb50f0dc57ece8f5f4e2928464a694eb39ae7894c7ffc9c37c27919231aab2efaf9f3e3905df2ac2e1afd95ca1c9279fa76c981689efe172b6f2c77311b2edea22fc5066fac065d8cc7f1ffc67c767963565f30a60fd78593f2168da64f38313b2b12c86772c76d2ca7ef359ce791f3e3f3c0799636ba800c45886961d444a014e16896e76896eb1d398ff3b24f72c891ac49d5451f87f4c0c8b6c9ca2a8c3e7943f6a79756832617382449a18b2d7ca952993e14ca8322df9a7e21e72faf8d78634647fed5e3c8bffaa36d146e9ab7214bba6d7e454e9fa2dbd4c8bc2569c8b267cb4cd7add9ba4f9f1317d0da20186dd583dd8ab55a55195285d5a4ca549ba6d42ab5484db2b2dea9de82adda5161d5b54556ddfc7d7b4356a4ebb72679ab223d0e79539d36fd90bc01c91268979706e4d1c7974683705d6115e6f75eec15565dd32515c67947362e002720931927f1cf880f6230dea6cc271bc46cfbf84bbaa452894856eae9862a9b520c367dea54836848e0cd061836c5917a9a12934da1af12718c2fd972308740a03bffe775b853ad38ac9736fe6d2d7e2297799aa791c9e48aecd103f45820f87d0ffa3528cda6bfc34707f48178902a03fa440da00f7e5ec5f11b8421db3e91ab2cd8f38efe434bb69a49d2bf66de897c86ac71ab500d2b43de0c6a58fde32d2a02ede77afda9d57fc22f403c5b38f006c1687b886badb5d65a23300218a72a3e052cc24c728f1d38640ce8272b70a9a21e357aece851eb9173ce3d7614d57614f5b876470f216fd11d0ed48383a639a3d4a9d42541869111b8121a62d304304c774ab3830e5ee825a4160cc8c129a54e67b0369b0d2ac80bb974a6ed4c381f26a4b0810a909cb0c194131352174b4b92a49e9cb04198272b389cb04117b35188bbe7930964bbcc2f83821fff01402dcfecf954430e4872def3a9862700ec90ed9e4f3530d16252c30930b89271f67caa610a0cb8c8377b3ed5f005064932b7e753939809983e882376d9da4118f1f3d4e4896df77c0a22cb2effc72211c2fe8d6e416f357e684de6533f326a432693c96432994c269b426cfb348a6ec102de2aeab8dc17725c48e61a2e871c076ad61742d1eebd31bac51cba975cedcff3eea55b68b227f7094b8a9555d9cf8fec3ec634aaa3356a1b41d4dfafb26bf316a63525b9fcd9f6e7caa2b2f7fe6595714f3c8846d9264ed31ad6b449466bd40ba5516b5fac179096a907f8c1efc41cf8efddab2c32f7fed88696cb9ffd8910d0811ffc4f977401f8131d007e1962f2febda4ebf89e7b1c9fd6e57d0ed08d3f141fff10f086d8032f28d6f01f070aeb1377d9fd5383ec4bacb24f844c65a0906f8da38597546dcfd21a9555998ccb94d04daac3b4265bf27e6e8cb36c2cab7d8c7f7e644b41e66d6aea66696dde682d28e7f5bd87dfc3182fc59498c61d5a1bdf5bf6b7a53177d55fc232d90f77d5bfe1f903ab3f4e5e1559a65ee89615d4cae742d16899e63d7eec698ae57b0c7edf7bdf73ef69968efd7d9a45e49f8f92ec3dfe3cefaff7f83d7ddfbb98c5d95a17f97e496b58bc55033ffa6546a35e6acd05067df095a0bbdc4235c87b8abb77a1246a63ca31c65dcedc385eed49d3870a2565406dcca7424d55897d1afad21cca40d51cc7dd0feac05df62decde3fda97e671a636a6aeb6193d6748dc7f1f57bf7a5939be520412fc05e5fe07f7ce9996dabea44f92cc913c4f644ee7b195efbd77ca5bd6ae7e7a6f6b49dd9747ae7e5457fd624e306bbdc7d883e9a0df7d9df2d6b4b86e6135551bfeb2da7c78ec8703d9c718df171f3f381590c59fa28efb62102bd47c78cb86399bb6e9be242bc7fdeeaf76240c2359de6dbc7b64c873a685a9fe7b5f6dd8615ee891d5b38fbfa55c4e29cf61db83d5a095d6512191579fdfbfac369b03ddb78e64ab5230aca914b539aca43fdcc81ca298f3481c7acb72da6132b070da5e68f394a907f7bfefc41cf7f183dfd1ac412b30c4a2f844fe7da28ec9e56e100c6d9e72a981a3fdbd0881fbf8a9a8e33ed6e2026e0e6ecbbca72120fef7a07843f51e693748c57b498f043db2a45946836086035446fc4b8a1f79ffc7c6ef91e5b83d2aaab4e7912009641499e417c9efc03359b7b8cb3ede3e6a107e85d5e642f6245d4596b97924b8b915595f1ea9da5ecbc95671b54d1ffb9f44c67fdf6e9b44436de88420b5648bcd1f35e847a806e159fe1ab3dfb5843cfbfd2f91ab6ddb47807d960eeef1c330fe74b5551bd374d9c738e7d1be85652772e96050cecb3e2659dfe3cb912cee3bf10698b3bdc061298b124eb25011c5cbcea72a4fb6ad71e40f3773577d1aae65dbe8f597dc559f09cb72d3e8447ad9f57b68c81ea334be8d1d40c5ecea91de86c1e61006495ec4811c68d33fda251583e9944c88c3ede202e866e5c07fff6a0a06a4625e3dd645f9655e461be53f7aad7e54d74c4bd39ee20d16d644afd5e7bf1ac80cf3020a609b95c36efc7663cdd241a996dabe02fb2939932b301e2b8703e87b9a95833bdaa0b8017dfdd08dddc8f4194012d89edebada6394467d0b033d00571ce6489e54679ee4451ce6b0fb778b035d2ae5405cbcd6a25d5a752ad77f3a356d2dda6ad1a9d61cb25ce89649a5f6dc6293e026f0290d0acbcd974c706ebeefb857fdd7618efbef31c73ddd9c7deebf27a737ad0762fc16bff79da8c33ed656b3ec7bbf7aef891c7b7777f0c3813e6ff5f777d4a05c5d36e5b4edfb7bda68511a57e61be040f9ef9713887dff670ef9ecfb42431910cda1f0a7456dcca8d1337cd34c99e11bffcccbc8e819a51fa6192b34cd4aa1cdc29739343708923e4a72f8f8c507c3f0c110fc50b374ec30fc4007a859e0afb417053e7e9508be07fe07824f37087a9e66853f37793189c36421fde3adabbbc8dc7f24ad79ab7b16dd5ed73dc61df54269d06f927151497775e8fa56037463fae007a0d8179f7b20d6f34992bd0faa4f926c27deb9e3c8f34466d9b7f9a317cedb7c11cedb682328f68d6c6eb4d1cd8fac1cb583a37df7557220d5dff1ef5359cddf2f2993cd25aa2e990761dae821c481c4eeef1b7d39b9d818ecfb3fe690b53113640e797f7f86f418a5e1e37ed09c5b5cc5c53dd277d7819c7843fc2b36e0886e8fa43ba434fa3e6a1c94f4874d5f6a5fbf403cb12f9d4d1fea4630893fe78f5e2c5c84f339361ae7b12e1a6ff4f8472f1cfd63247fd8903e4a32cf13f95fadeabadd83fa4afefbde5ba129b92626eae0bec43177900f381fdeb24fa5541e328194feec1e634d044a91c703130175d6ef1d49ceeab2d406f63ecffbc00982a27e0f5561a87aac33febe7b173ff171f7de73f841a29f5951f75e0b3f58ce19f73d66e9a2efb1ce2208740c8bfbf74816a73350e4fde769efe70b7b9df6e93345d007d0079606dd7bcff2c7f8e3f19e03651d8a1070cdcae16fdf6e513adaac1d451cf4b01ab841a78f156ff8f4b15fc5397dacb621fd942c9313cda93ab5e122581da6ab5aa71ea65c72b53974041735c5944b677272a6ebe4f489495cc935683aae39bcb7de516881426f81d3a77ed9ed59bfbc78bbb73440434f674b52f04aee3607e806627549b707f641a863c64c4ac3f334b65ef747bbd3737bdace21ea85d38ec95032379dd2b0356ec67536fc219716466f30ff0a951f67cd894bf7062edddb126c093687a610484df5a32a54857258ccdf22b92d525d04a424d8f4a9e1835f86f75683426d911cd4585bd86d5b98087259a796a6e6d017d446ddc22d714b4a21207912ae23389dd93974c31e11e492dee88dda006361b0593dfc341310cc5d7299bc75932c29792b367d02fe2ab29b2ddffe73d2f007a56a10385dfe17cc8266fbe3f8dd89053092bbf7bfa26692f42f03fcfd02a0904b260678efbda73a039656010510720e4a5597006c752a80ab0a352b87dde1db1dea92c89ef2d6fd21d2c5b8ebfee7449dbc75df0024ade2aefbb8bbe0ad063d2875bf8252fbeeaa2b92b7eed7f70fc37a54c369094b407f70b5e78d1a5bcc9e9e16a5e0ea468d2db2f59608d62099caa4b6cba0613a08e298f3fd0390867bcea8135c89392ed8ab8a63ce39e7cc60cf05503de7a474ce39ab1017b470410b75bb2e8d4400674f896f168c07e125eef2b762ac1824a4224536bd4a5769d3db2eeb33b164cf2b7b3ed9a43678410bb509492e31d4f6c7500e549b1c33d5251f6e870ab515e32d5f926d937bc923b67a1e554a4df8be7e5fbfafa0934cbf76b5ab5df7714fff821af205dfbbaeebba0f042b58a93bfeeebdeefbeec16f00bb7bf0ebc00ffcbefff48c06e85abf0026f4743c20d9b2c17724501b7c47e26583efcf3de824d39f18e76c671096c9d9b30024e4fb17d490edf7e0b76a1087f3c61bfc48b2bcdec23fb14c8fb7bee79eee398740a0377fd101fee0ddf4afce7101cde10f3e48410a6a0ed00dea06685b0013aaeee1ee131c0c91448230823852041058182145f45b1533850eb31d5e68f2058a2ec9085937ed0e6f29ed700205296870d2430f598410a22b2106ef488f274640a2c5114d767842c2b64f63bac0965841142f4628cd89050c2b4788e08624685c3a101851041fb1e285d203591246872637dca0e48a0ea966457ea56cff61c8fe5697970b058630010f5d58a01dd1a41ba126e5dd0bc05050124b0285a8147422d46c58cbec06a428aec49a88a10ba196053c62830d2928a288305844d0a183d576a0a1476a0f15a69890984a32c6c68da94de198e4a815ac0842052b5d48490aa31685b3b956259a8070fbe2851794623a7814b518564990a00b1451b4c46ee0c0f0e891da0bb7c9044249102980e1e94b1556c4d490d85a922a27a93e4e8044851121d030821b56501be132a9553bc50aaec4906a21c4f0851630d65afb46f28eb1ba8ce45aa9d0ca83521213145844b0a289175abe702e7b1b8a5411831329a418a946b4508195b4446103432e47ccc930048e0d159640291155c44a186e4acbecbb4942a0a549971f6eb089208c161cbe7b24532d33d0e4249e444b0ba1529416116c8ae06265e566518497ceb3057ab86551c11547c2e8418a1364594060c20aa618020bd74511537858977e6b0a9e84cd051b0951bc38c20ba78222b6747ea4c90b51b531379cb083162338304520e1719574627b62427241072e382e94ace005081f76f832858a6fe96e96215172052d0460a2102326054530e52bf32506206e43d41053b205e74b59e856d17a1223c4174d285061845b1125a86ef55202272a2588218c089c1663b02b390b3cf4c0c50909214449ca62e64802bef80085890995159c705fa400b9903282929426b65ca14316599210a960295964b952801e9414e1f4830a9880e15464a1225ac942823c44eb10173c48d990c20f27c8e2258b920c9309446df1c4c6b43404150e6a0c9e4018148288d5848872e3ae80020372d34a1434ae64950081ec3df244834c0aa72b2a4ca9370c111ea1800e26e410a50a132454b1026aca4a890424a258c941845b9410705da0986aee110a85e403b27e641a51220cd314314f5e64a9022ac67147fede7baf9523271494e0e10729da123a380aa0eebd7763d764cdf4f1b7b73c87ae8bf92c3f846003942861603b7054101106c621635cab942423584871e10a264b8480438117d78fd401200166892c4e8cc0040e5c88d862ccd3c7c6841a5875a18b2b218922060726aaf8a2045db829a0f08e14e96207134819610a14332998e8c00c332c524ed084165d4411757d3a81029715580821ea85239c1411b4d06a21e20a771d8718931980b08202a530382888a8c2390d242c9488e24452d416dc13449400ac4cb630c97828e10712e08965b6f260d10483ad8a2a49ccc09d40866dddda975f212289c68a063688a0092648c022b6026e0b11307ca9fb8c0bf188148d5b6a654631d101d97be413620431b0c005942c60a858640939772b26e894acb5d6e71347381183a2e9052a489c280178a402575491c2298523312e4d2c11876841c458487001092e464040e3981822d6b9d2bdf37ebe3fdeff5b45b02f09dfbdf7823268c073885e24c0ccb09e5c266b7d66a1457d7757cade840acfe7e47499afb676de5c75b02a7cc102172938114105596c4fe0287984791de167937a6489ceda24e53273bf4302b660e2842b5d84601ab344ad6b22e2e7a8290c122eac70c10a09342580105725c83e5f2da8d5cc5aa11a84adb5b26de8c49624820f25f060c5cb72496cfbd4c3b65fc381eeb6ba0432330bb9c4e37629e0cc1864e1436b86002c11802f0090426b842727d70bd99312002926188491710c00a892710b4764156a7882b9a2a4208a36050094c8aa3d9f68494cd17ab8e04bb6d9f38956c3055eb2b7e7138d89b62484ecaab0f29f3dd9b7df696387d2a0efd5bebfbfcf397d7be9e307d874cef93bd4460694067d9ae9fc0ce8d379c3891c4fdd8e727eabb575fe4cbc13a3d571f7bd960e28dee8eecd1cd97117efe018db6d0f95ecf70904573dac7a08c1cfc3d539bfe00ff7feb8add53bceddbde3bcd65abdd66a2bb5ee6ea79d6228aa9c521a1c9deeee95d269c15a6badd6ef9c40f54bbc7184506a963cdddd67786b3e56d27577da8eebf0c59ef5be0a86d37edf773f50bc4f2c8ded8fc505d8aa050c7113f3fbf9de5d3a5b5dc6a1f50f745eede52e77b9ce76b8ce983fc36e5a71f8f8b10801bb3d2ff41e04c3190ee4aeba4419e41aa48090da30402b4a559d70b57abb7aa562ae4106982e7f4fa62b69585d6b9986331ca8c39d9d1ef550208e16dfde67163fa420b3fdc71f9670cec7ec72e5cb9e60268df98c12c1fd73eab4e79411255d3869f20313a957d41662d3a84d8308c116df3a90d0161f146f882a104cdae18be2e73065faa26a47fc4fbcb152893a0376871ac89cdaa01e91f87e9635fbbb32667fdf13104aec0f0b94fd3d166f7ce3f6be136f7806c0f80a958da5367e4ebc8127d00092c00cf083086a96d96c5b5cf36dafa8b7432b0a12c25851e40606edd2d0848f4000c60e2e3c4d75310b02091fb6503a6bcab3f594a504b1fbc41bdd986d1f639ff2230863892b6ea0e104501c314495afc3d16996945cfeb7ae053b3ac4b6fd6c9f7c90b22dce6073dce7a80d04051412dca0786284941767c4e6fec781b8afc1bd02e6e68e0ccddd75dba78722db5a2156d7ca0c3be9e8e704166d267c2a98855114f1ba9802b338018371fac29410ab12c6065cdc02a103a8942df6553d0b11d90800000023150000201008054462a160349846caae7614000c7f9e487c54968ac324c861148490310410420801000000034668688a036d8f710c200871e6e8a3c2fd99197feb07902cce524cafefd92f2272bcd65401e29fd44a851cbb690287d35105a34ced90c269de87d3e0be909d91bb5fa76f679e3117fbba73089e70420eb9c6e748c104ff861eb8ca2b4f51f3746c329ef600837f76a8b554fa6763110b7f4471eb75b4a8843c73b2372d6fdd865942867896a2d7345abc89abcce20d52a29b6128bcd81db8c86442338ec234cbcb83b6365398fedf854fbe56c2e36f1919cbee65ed49651a84b2d2021950d80245c84933fb059f494e33b854c2b17733857119ffdc11fbc0a82805171c16ef79e67f9e508bcab21dab9bd39fdd34e89117ceffcda36a55d60a51af47ead2570ff91d6970548457d049f538d49fe984f55a7448217630bbba23ee863dafa605f916929aa8c9556831477fd2d67fdf87a78a645e24d71f83fecb1ac1c4fba479b8a441b2ac63d25686ba86877030a0368225201cc865e5de05230a1a5068326f85ae1db86da83a1b84dd5ac66478224ce7c46b31354232fc493dd4a20283fbc2611d6b827ceac5992aaec1f014a6f3c4fac1a279c44aa76864b811808fe53ba7669060cb5a591fdaa5d1593f25a4afaa50e077acc90d1386c54e71e81d4c865a2e3e2766196e6a177c2e6d2aaf5f70402d7349561510db41f2151cf10e2a10b00f33e25c212184dd76d121bf4b8884e12590f16df0aefe8a6304f006d9693b9f6a73f5e80ae38a013717e144eefb77d3705dc29d984dc27a6cec0ee48796056bfceaedec36043ca5fb045d17d472061b98c4feb21138944c98a089c2cbababfa11c4827a16999e6a3b28b1b1342e0f87680392c503abef768825b451e818765757622b81ab5e217f49e4da8db92182f14121fd66bca5ac8726f3ef89b8b271097bb930e24d9eef729921c195378342661f36e92d849eecd130e7c7bc327653beaeae6f6845db752730f65a72a78fc49768ce0112f6225a100578128f489e79d1885b9f7bb00c1b0dd565cc633936e97440197f426c243ffb76c482e9826d733c0e3646f34fc6c67222cd93bfca368aac23def846c5728ec3b129774bec3e2e4d0b649c49368dda9644eb1b9b48bab135c1de477c18ee7221620335ab8a53350ca6867735764d98a5c238056f043fb133de1817074fa4834fa1297161212ad602aa122bd622b67531cce05f4db869de9249d103eff160a216b67be296351a73c72c2a1db5fd7291fd89b5a4b3db780b28a22dd354c8dd48c5db792cd964413b400e6d521bedb08fa8aaa006b988ed2eeb0ce3d13c48f88270cdf57794d4a4a5b0d35288332432c4e6de9dd37e9323f52637ea01d43b4b4eb213b224211288d915d5192c82341d15eb23c94591665a3b6007d41f901e3af918fd4be2e548e833b5c30ab5d456b8b90d58a1b11e01e170cbf870024d1e774975734acc1670c4f50496653161b7488c5c799244005f420b4e0585e7466a0e51aa980f59e468eb3f42eb36006b50aa93726482f5569b95312cd5489087d340e3b00d339839f8f88fdad053e7ea13d8c9031b388dc5256f34874541a37b04672d2944331b4eb190e0f665ffe24aba897b774ab1170dd37eb49c12b83ebee61887d905bca2fee42a7b5dd869d9725bc5f8cb96d3af5258c4e73e9759806f80bfeb254a4e61cac8551876fba88335ab9cac5c2ff0bf3d0910cb132763f313fe432b2d06c76b958cc9398e9a4694c911cb32c6168567e2874c415115d534da8e3d836bf9cfd92f7521fbe09297a3dea4465612352a76cbabc51d4e78925eda8a98a86291416b539ad401209a7864744a5a3013003c65e2ff738b6528d7b8c404ff0637ab0bb45f3b8a9e4cc9f0593981256370007c416025a8ca3f3a329754d015ea4ddc946922488b0ad28efa94a8f453a8f6657241b86640952350a862d412d01c35369833306f0889643f44a46a468659483a5d84ef7f2aa297eb72a8aa46c5f7b63215e395ed2948e3d05cd5e3f2a5ef6bc28208d2186b29fe9fd4dc77494f40476dfa33314401025e02da5111fe31baf7155acc08219680ddb1a54409f1b2510cc6ee9d202e3322841bf706824106fd43652eb3dcb47c204d3ec8b905d7b1b2f2031b13439eb7f887acf40d21a70fb149fa59c93cdf295a897643186806b32be00f7cf31ce2bc369c9d4f35356bb187f51c289e4b6474b5e2062c6114f8d95e69919c89b352f09514d282bc828404c2ca8e753cc7b705dc7b2471c693b62e9420a6b981402c1aba62eaf8b115d1f53d468bfafb6d517d1d9d6a2501e571d509a3eff881e11de5b7463775a0002d7093b55b24ca71acdc3be4b8f474a6fa64bd24b98a5aaf99027aec3e0ef6c4b7833c81ab30b7edea79ecc071c2ea37038870a228971b9311b9ea2ee7910f26f5bcf29655e4b85e89031b5f81381fb279dcaf80242a0bee71db5506125d5285062f242725ef1e2c30c78abdd8e7ee8b2d374a91e0243d59c7acd54f46d2be44c0b7b27cdf1ac684158ab40c58e6df6a2a3ead8469e3163be4178ecfbb53a6a43b7a4c895246d9109f0e0e58dbb7e596217ab5804742e86c292728964f7de04dacde186db7f09d2522751f7e021596fb0d4f8da8e5df75449f170178618fb34b60a24c43586d6217183e275d9325f509aa248e94c9cdfd6c1c2bd2cd3ed692ff0da7f923146fdb2b39ed7a835aa9d174a672adec0d8936cd7222935e0cf19db4465f0c99efc30418fe5b15f8eaee5f9021154af5e8e330ee0c2983fb1c0d811c7a2bf6f744ba0d54b9108a13630dac02bab1af867a87a5d53402da985c12b9dc7c25b8eb96024a864b3988bd2f87b917d19737f8f2a2ef98a492d7085fcb34a90e9e09657061d901522b85e4c391ed0c8f083c7774841bf96ef6b9fbd4aa248e42c3b4cc206d5d55040124989029dfa364c8432141044e462f9f74329e06031bc263f792c4d88203bb0841ddbe2c6815fe09d01c2ddc6de133771c4a536bb97abc1efe29c7b970dbeb30e7e6d6a9d2a88b8419afa7c9d33be8092e32fbe897cb11a2308360a465724d24b35cb3e7cc167193fd0383b9144214092eb5964e8ab220d5c4385d6a15620befc840c3f1b6035aba29d32d11e33a14a7dff61bb931429c3931f29a994b444999a4e25a0114ae58f5e406f947acf1e494796846c1c816dda64d6729874de08856120e43a2db6120af832ead6bc970323e358fa5358b5cd50e8f194f9e7af149fa7be620ed466a74607c66621292f0adfe88aa038abad0086538bc9038ac66c41fd1fa31be8901988b219a5bbe18615a36fd6940097221161d7a7175c87f99b85e48026aa096c8454404da8972d8bacc0ca388c5d64e5ebfc9d16b448c68be0e1d2ba9ba2e3080a23f41623edfe3af551659af70b9524380efdce306a16f46e3c409195f195e23ed018fada946f30f6e871461c0b732d16f356898f072022ead6b864ec4b721944d809c6ad3728e469e0f375b4ebe0d1ac2e1f8b82f02de4f023fb37ca2466ebb0a0c5c7da2603bb893e566dfd6dfdc1d79dc6d2f4e07e197703dc5501e476e68ce57bf2a323faf2f08a6f86933d638ade6fd8b8b7e6e78b81f78169ed0ce8a3578a6676e518006674c169481a8b30be9c541519ae1972961e2bd8b729810dcafab18b580cccde253f6bf5386fc86c44e95071b47f632f19fda19f2e805858b8973bb426539dabc414195394169b693549c9c2074ac903f0245e0da0ccbe553bf07821208bd1f823940bb08e175d42a68ab2e4faa8588291332694c31cd210ee6e3a96ddd2c4d5ab7384009e6ad5a6c93ac577593037626fa88e7cf92672d52d324c5d6c96088c37a6f5fb5bf00b9543cbcced90e4f84904da2de0d46f60ed4415fcd54b8fdcd0435a8d16d5d4010808fafaf9cccbb657014bdfd651e5adc7c5cd75d514c1ef36529c6844ae9b31ccf9a7c4b6efb3670e11f2b53307f0923963de024dd8fb8180deead008069c27b10d299a84d9ed1be6d545e560681688a0bfa8a030a1995f838ad76724519024bb1c4a5f9af344f037cc6f48859c9c2f44d5925e65f0d48aadede6812dd5f3b77e6f90d6a38c5fd0cf474da55c7b768a6b0673454eb32731f17f4f8140ca2846a640c6d2855876d4fcf9b4df27197b167e251837b7e175aeb37d945980bcbcfbb3ab07b350ee533fa234d0c4bb22ba140fd9458b1fb63029c5ea1f7a37416250e553d6b7eac8f53d4c79154134c91088c5c19ea7276df25e519326bcbcff9c10e93a2a7ae5d11b3d7a2bfed0ab03cbd130a10391f87b1e8562519ef4f5a46be5e5f2ec1af93ecd8ec786104230bf4db371927ab3b0323c7cc853adbdd7bdc025f08887230ad5144f855e8afa121cf0a919b6ed61a1ca4d66ee6ad03a9374ae0b46a0d7932b700191a815394efcebea5e5ab7b783845ed7a72fd88c9d95d35b9fecb0080d36a5f92c8102a6182c0380aa1d61f3d956818174caf9f764c8d22b1e5039bf27844b9d4ae4d8a808e08a984a384dd3c99ab9a21b4bb9ca29dbd2fb3aefaf0ed3e4117221cb8d9a42c72da1640e6a6667c0195d92b54d14b802c961e4f31e7b3138245b85f9872d36624cc29f254d617616ffb5dc348850184f162305622343c7b1061ca69db8baaba312471aa376df5d78327dc650704987df93577c0fe832e32dd2a6cd5b60208433173a7d41bfc98947a727c0c726d2d70278b9899ca3be51271d4a9eea132d2b7be91693bf8d17347ca2a34ce481958b9c06589e7329b3192160634a420ae91a14d62b01c01622994da47ac829b4159762d5a45c456d9f5984fe08070a2136323d20831b3f76c492176975158148989f1064b25d2e6c660a7f8d6fc1c8d2cf8986483a2963671cf3b628752e265acbd42669eecbb6217dc5acc344920aeae4ebcaf63252835efca55c6054d883699aab499087d42c14f46c96f0f94825e0519e47aa38d7816db244346fd4cff28e9ddf5fcdd81cb525e7457deb5f4de4af15bf410d113111ac6b87b493ea43948c2422d4417143680a5f56073cec1b177d12aef3381a84079785f35c8b02da768ef2673233a18baf719a00f6b6049e897a377604f01b510a3d668df8e7ee6952464396a451223ca0429eaf7efa3d25e267ca04e83dcf350563cac94046e59a8e3679fab9f16b02813ae0d4e98d5f65743389611621f6051afdec4f697f6fae3367c7fdce313fa19100c6d596c33937d06ad2a875b2a60d3bcc90433ee06e44162ac5ac41c02f6f96e2ca1e12958893104793125b10c0d891577ff0c18c6c4a21573dc159cb788c67d8862f73604cb5d50e75421a14a4147d62f6e2f0f92bf7051aa4721a58e208e31c49037979472d1557514b7483534a21550498bcc95d88d3f813d5127d82d786263e77939904e550d76f72e8b74aab3607942d9eed3e7317b1e98e5ab5cc178e1f1d01757f49bc3c20cb1ebc546d66004f303a981333ab505889b7320a029cc63fc3a337750a876c65bac26e82976778493a943deec4bc73c1ebec2c284e57a6ac0cddfa6c3c5ea55ac1c7dfcce9b7a5780e535be8331b323dd7286d8ee090ba4c6c9cde7d2996a38a0698cfe9813f3cab6610d4f974c964e41d85233a6c8b8c05b673fe67eb04fc31f19fb36f503d51f3679933c137fa0d0290590c18b32620155b1023c388808b9de48df91d1dd87780b6c70426da691d7834550a2016158e3256354f58a082b0afa78671a0423808744059d32990555fa91b6b6cb80be5d9a7b599c764450ac90a4608ab145f80308809adf3dbec8c2d6146286d9c7dcbaf1b07da8e11384f603eba4859326d156ff6ece92da8c6e406bd3456bfb315eeb386146708298679951248c8caa31dc0370559407c32e899673b52922a7dce31d52d7d35f0291768461d326722c9074c9b64618b4f26f1c57f17bc0a5607e0294850ce26dad78e7820a866f2973e5852f79bf3cc5d1449a39beba67408b5a42c50c91d054e8ac9df40b01e2d20e212a97d4259f0a88808cd0defa879ef11c3e4ec28e9acdf3667fdb86658a08cd11da9e81610f86ec02125e81c733a6b3491dbc0eae111b6050e594da4dc28310bc0875902ec4e1b692ad73e984bbe83a601910115b6cb3a4ac1dcd080d3951b941d55914e9293b33aa02730b2c232694da072e341105a699575cf523ab14044927cc8c714c0e0c26da6352e627c76670cb35b876cdb04c24ab7584d07cf6041830cd0578f380df3d9e0e0586d2fa8a1c029703d6429317e6cf0dd08ebba8cf243c9646fb3b6969c97a233816686b72088732ab2044209b1550a309dce7722289f3bf8aef8a248de09c7fbacc20e7fb775ec535b86bc58565426e5df5ecefd09d29564aa41a5f707f28be0d1531fa17c41e87dbe76a74b2ce9627eb190a5a8a62f6bc2c3aedb778378f18b5593477a45942b6da568eb2615c47f417cd0a954b3bcd884f680e96754dd20e0b0577dbdf039682875c0c6ebe801c5821a776197d26f9f5337b4a9c217155afe6708ac80d55f9c155915b68ab8730983025824f3d712fb8d787780d6b4f7219c21b77e2e5e32579e63026066d29e68d3baf8e919e54e1800edbcd8e104662f69437f217d768c58e7d09e35a09c23748251392646e1b56183cedf2b3a44ed73d74c160e6f473f2f002757b0d4a79527641cf89fac7b4202e9c9b4ef1f1e0cfc59b3ce9d87cb3fddb6c8be1ee52c5b2166d063fa64b8913abe39e4e27288cece83d183dd9fc8a1c6150dc0045f2eef76a206b60c154580e39f4c5ae5bed93c282a60795905a2aab3c9fad55e4960a30db96ab82d4d91c3f93a36168d8b5c703c2abfc37c69a455de463d2c31596cdbd768bab16d060810665c9df288da236319d763910b5cbc549f4a88e04ba39041d55be33d1490bec296e9bd74f3d27f90f8c8a7e595693367a8ab7cd19bafa03a9e0b1b1072df9014ff2d91874516160e8035439da747e4e3a1de180509e3660d077dba87dfc185a2a0d8d4b01d84ffc279123ee7fd619cd553baeb6348341f391c6431515367cbfba8a4f9abfe1d345a283880dd26cc29bfb4e76e2fa68c3eca4518f1d4594a1e6d2edd7c8245ca90735dfac80dbacf99f58d063a23f884913f47516a157106a122cc9de5d63460ecf0f6565380e9610da6117378590ac4ae726d276a69beaf2fde81b8b0c2326492769a6fa57af0a62ab5105bdbe3a765b8dfdde93f5f89c762a933463b412497cb0c4659c673050e12bbea44ddc5e50432b690c37801dba76c973f096484723726ea2c1d3e1730f2ce7a06631a4b0f7083a5261888fc691d3843f29f631781f6d4e475490e8111fa08a3c28963a52ced91484441c16a5a5c1e1abd0a7254b621fc7c7c847e975947de3318d52667add960d7c1a659e347eddcf6d36a3c27edc22a853d734311198959d471b20d6ac00148c48828c90ea6cd7142b83a520e522fb7f72c89722581cc1de63af0b058ff6dcb86ee09f0237fffd486cdbd1e4ee818f93c1f8c99b44632beb11f998a9d16e79930cba43d691729bee0379dbaa8058d4dbcf45eb7500e267dd3e88bc696bbfb1e2feb6c60a022a1d7826ed24ea77de0038578e82809238578e88b5a93720dca22235a9134919e0ea949a0f3db03283b7b0bd468f07b143f56f1e0073ea7fdba5d85088ba210db497ab80fd7c68dbe409c4c1853467d0acbb5cc21a442d49cfd634d1118a251884becd7c27f63b002b979b1939cd9ca0937248ca431020190e2fe7b3d10090ec1a88b3117c7a1b0ea28bb3a391fd641c980ca5d0fe5c4c90ad02a7915a9758d28d42538e3fccbe117adc740e3acad8caeba6a4a6d3414a39850c749b4d2703bc6b244b5d830c4ac647e4b6ad74d7a785acc28ae0d5d85e1e90d589e64755ba6b9c6ede53ab4a89b84b358cb82a794bc2f8faed871e0a7685e059ecf1618de400afa4526b43ed5de41d767c6b827cca2792d3112f5b5481cf86527919ff69b064935feede3e023882b2361c2548e8a9c66ba34e0975bbdf2a5285366762239fc725335b4cbc2412d1018e336c0a484834e7598d1eb147cd7c46257178b34012aa80f1a9c306c6d63d4ab613357f47090fd42844e596a824340b3680489457bf366085cae375733d30b61e4c64f4ec6d833d1050e120badd0259df9f1154de9865955b733738c8996aaa1cbe091966e68cbf13be8d9ca71ce17606a9738bffebd651c38ba6b23410425d1bb2b8eb91db957a96409c33729636345d9281c1b320651f8427e4a8a97282cdc9d57a2d5ad34e1e579622e9e48504aced3c1bb9c804e5d513cceebf69c67b6e6efb63c9ab0bde412b46a6e884a53f98cc2d817215302c7f5e804bbb9d7434f551f287c419635faec5497de2bb7fd319265ea31cf8bcac5f4b9fc52d5c4c60dbac2cc244045cad802b64d0310881ae4b811c7d70e70a167a31275e12185fb901f18dd254b79863a352ed85dc4a1dada9c7143b189710bf845c32f5685f872cc34921775abfb74f17a32f85fa7ad99a78e0d9f502dc5b479043d531d84060c454ee33697f3fb87785225091e6460b48997a89bde974e202e57caf7a6e4d84b555de206508655deaecb02956bc52a50b9dbd102b73a2818ce8d5b24fe28e77c10969539505d5c5a9571139882a2d2b9697496914335120c26fc917c3ccbdb1d4fa08a19f9755be20e44409fd7fa6a699198aae6314c2be68c8d17bfcd156043347c335804541a288e0ed573177f9fe336755b2394de9bd9a6046ad8fffc825de8b6507774001fa222dc46ca404ee0f9ee05b032bd900183d587158c4aa81c80c5be47099911e36dd6e1ff21e6cb36c258895fd616744426461f9c441e03bc8920a1df2034eff5de2a8f6b554c61a437f4cdb552a04d6f62b2672f23dfec82c71e57c5c2ac5d60812d353b92df92b41877bc289423a6485b3cfdad755210248ab7324ecffae6f0a30feb9b26d0a4f1749dbe10464de05c20fe8e58861585010b50c6ef69d84438265a80005aa960ea610d425c433f4fe86dafa281860e27b5921346eef35596eed0a3372324e98a3620e684096b49b32cfddddd03423b14ec50847c12480153070b4923fedebe2e9e74e29af29fab189b643ddd67b3af9915286c13e69bd5c8da852425db90ef02af3eff628d3513b8b90203de9c28b7e4a2f191b4bfafc65efa0db26fe78dc60ed5a95403f28f231fb05006b75ceac5f1cdde6d47c73058480bb8dd6ea5d0527854994cb35d99842b634249b01e9360aad971fb54123f267a2cb78c90bc9394f516543d0fe79d5bcb447dee6d13ceee72470dff088ec66e67981b40b50bd90f499452480d9be1220c53d80eae6d5b1c362f4bb8e3efcfecedb5fd621671e7c32dadc987abc3a51eeeff0b86c8097eedc06210d4177a959c6aabaf0ade1332c7abc33e267d1f9cdf0af150d87347b4804e15fee3bde26bb4760f58f5efd6733b47b7e21b781886eda52f2d20914c871bc9aed6842218a47ecaad0932cdc6f4a71a44166db01f873b5db97ec511448ae13d7a18bea9c14ba4a004400d9b9f4bcd59110ed1b8ce6c0ecd195dab4ec9fa98da84adc0c37dcd0ed9cf2f82625d90449a78d34d09bc7346f579e981ba7d21c29aa748f7815a5aa8c7ff6a16fd90ea4169237690982119dffdc63bc501fa4095650e8f6590e6c34fdded79eabf797beaa983ad183515369235cae4312ca01e3920db2a510c17c298fee689afb94f7f54fd79f05a8a46f8ff3364549f1a21af4224c9bad5faee050c4fbcccb16b4c832243f7a493e8a71c5c26e910bfe08dd322572a85ea26b5efa2d630fcabc8b54bbe817a72fc6981e0ba285f55524ba2113cb19a009b68905cfbcd6757fb75f569f14f297cb6ead4c7402c301262282a644081a97b35e0305febc42eb70e132c9c7e2c6e9fe1db2865f796e8c4fab8062c16a3e8ac69a2749359f68c465a1a42dd3de2de2af20e473c0d85f6df729b69cda455f6784f4eeda12e1f8008b436405e5877b2d2b2cc2f71e1822049cd117660f86032783d892b040bc52111dee68b8fd05950adb710b8dacf4b0f3a732622a22d18eb841f0df15a70365c4242142dd0670cf257db043cd878cfb6898d3a49bc27fe859db052d47840f8e4f406bd7ecc599a93533928910dbcd45f871475cf839fc643b470fc8b1c89805e226b100ac2fa986710e1720266d7148d2a55bdc0e70f8a2f10c19320f05a9ece04d13631599749bd954b9813ab11960385a9e55e04090c57b4a4a0e4318aed26a947aeee94e360a72ed4eecee06e7b04b24722d5c4808e647bc6e8e1dbeb3d5c5b7a4d842c4b7c2e0ec7ebe99137fb0c238238fd34aee8b21c45a280055da9204c670a5df10901c8fb228b38ec71427dabc1e54c8b2601ca0d9e4fb6198b08385011c97e4f74d6f9811b17237cf0d2db135a188c99ad81736fbcfffccfefe6771f2a60e98ee426292b92543abb636b1a2a8244106f2ad7721fcd19ca66171c6ed70322bf162a705ce8eb7a95a2d0494881ce5907cc328df0a155cfc453c0057f405c10240ed393e898e8e507e2e443f663fb4508a9679e70984d6dc598c4f1d603b1e5e8605d70a05849059e6dd6caeedcd5dbb051008a81fac4513f74891331e5bb94150bd0dc55a87e6d5f33f7740e051604109564b8b49c4b9c518b726aa45f460c9aa33efc2335ac8573edeb3ddceab85a4a604684ea15248d4ac273359b83f45032c7ebfc7d0d568557d261ac2ff01a42f0f3467583837a6011a62634323b59b107957daf7f68fe3fe947fa2ce34e591dd3491890c1ee75aebb9ba3d90c1674814bf8a5af169e4bf4e00f1e732a78c9b0cbe05bd01b8a704d76777bee7c267219ecc7b5affe90b02bc7e09dac7f4d1975c9a8e7f6e66351112cd80bf16e44ba13011d2a0f5d011badb03789366574cc8f0e60d29ad93cc40f58360eaaf619adb6df42c4d4e0e6639b429647c9f0c4672a5ddcf7c7204dbb1b0d568e08ca0dee4b5acf3ad746a0a13f6f1600d64216c34e69a920616891de81e515d884b80f49f9f44aca97859e79b42d3535a1f2007e83cb70d247be91eb3aab89ca9b357b0ee84f90c9fe3717480185bfb7da6c27256c1fef6607b6346257df31eb705155d27bd341647421e26667dc98482ecaa88ec4c50c84451a0e4952df50c7c540a09bb490ed13b0fd6686bc1e92833c1806c67007f1fba87f10c49a20604eb996cf479a8543bb003111baf87b8c4316620f360d492dc26f11c4101cc274653dc155cb045f9f9bb90f17b9bdc54aa84ede78f9837e4816ba654c213634e5c55b8e5a0538baa4a51584f98b4b14186a70563ae4965a812a6d290fe2d6b02b933d012e6e06d53a259777a86a5c8d40c496c8b6851db7a7be7505d74193d4e86cfde7b5b4b147634eacbd96f0ecf8373e702119357b6dbe10e43a51887a52d75007cf1ac203c162f802f77465d6ff9257339231577b7e4388390b6a2bcd42aa2a7b27d1462df3dffd00f92cb372ee8592cde9fc4db6ab044308a4709dd38d85bf319b090fb53011ddea6dd4d703322f8014f7dfd7c9e42de6a98490dc36919be84b09625ff99593a81607a21ff99d80ac5de1086828d5e10a45d500f5de27d91c041e03dd348c7adeb04aa11efa968eebae1012c79cea5ea6de996c4f8b2038dd579f4afc71873c4fd9dc4302854a26e5557b5e2cbc0047f9249f269fdfae83388af2e5909f79b1b2df13b2f104f998009dd206140519f8267f284c80fb535634f289f81a81d49e7c2713afdbbc5ecee769c2e26919a5142cee8669879c467d6fee0089960d84af82489f0878742b61a9ecf9de3201609bf453f482a6171d10d7516f0e4c93aa75d629435b71a352f47e9f08e2d6c29838e9e23ffcae87cd3b70886581210558242de111134bb7584a2ab3f9d168f18af167059eb2a2af2a461f300c7518b33c56159d0d978a51a1a096389fda3df65dfaf59488c862d2be5762e1a34bbd652ef43a7355b50fa7e47f5b306c6c6816d98fe1b0f0c6f2e12f981850268419ed92ae5ef29308c7377ebb7d1c9ccc2cac28edd3cebed585c3fdffaad14f085b43e78ec5b1274e910dd72e11720a054e4c117d8c4954d120703aab5f42ec2c8fa5ae455e170f61d2e59e288d21721822ba9a6d0684f015e36a60cdf80ec50d28b6dde4d572dcc6263cdf015e40d50374952f8f8adcb41e8760b9de7213825123db2978483f9d22c1106894c9e850c4759a8dd4d592cf8e3d2b964b1ae1a2caf9571701cc2eac65e1130bea9d9dabec0c4498f4300c721491b3aaf95f3d54cd2a2342750c8beb5c35c289d96e61ecdf84d482237a4c233eb4c9bdc5005b92afd41fe9d2bbf6fafbf3b38cce7250f592fbc52c90adfedd5fc4a2ad5d734c3858a0a06d553a0de4f8586b8655321dd980652a9bfb8f1586fa61d3332d3948e1b5738ec7a2a73542aeb36efd8ce806025c2a29fc21db88138ab40109526f1514ff1ab0d35d9f323265f4bb09d59769acaa6288da21b3f188e0c62f4c7307ab466a6daa1518cf375a9b037de6743700142c23d9f1f11a01a78ed735104ce10a45f78ded08cee6d4e0cfe7d5d8af32e258b43c82de6ec22cb43aaf3ec33ba4b19752d4f8e5c04245623e415e17b55149b63f5db00889d8a451e158ba3e867290d6816f33ea5f10d54bbfdce0a6cef939db5c694646f85b0752aa6428a2d05d2ed8395e3d3d476692809a7b044f0d4fc417fa741c5f837c609934722c0bdf518599a7509ca75b4ab4539c6cafe484a9e9610bcaee2d9c17fb872aa0c23d26b1fce5c043a0d174a5c90c33e3e50fcbc34438f15fe7c3b95b637922d2cd4448680ba54a3407d3827765301f7a00b8b15280a3a4fe5327a6579c31b81c737b192c0d0eed19486bbdf532d4adc63d10e6e79683d98583213522c0c08c1528b88f51dc9529eaa5fc2e1959a28385a5d6a48d25e891d8b033f378695c0eb15523f02267cdd728b3fd08bb42fa3d8f07df9cb5aefceca2cdd1a106ec25185b75891e00761a78160dddbe03e88d7597fe0613a6e3486c341343f789d68f7f2e372aa7688e9bae12136183f6f7a92bdeb67e881bcc38582ae26fac4f8a9329fd751e93331d5fec6c7ac725098341daaf34b37480f8e9cec1f59c9095d9fd1354461578aaccf2843249973c4c9ddc9bafb33ce3e0c4d098bd079ea8861a7f15a019aebe7c62b0d4e242679592eafc949b84f04d7e16b89b3da40c83d607c218d2c374e74023a7f44bea6810e14126926710e5929b84f8cac7fd6b59fe2c936915d7f633cab9d400fe0cb3d2a1b57a5b34e332395453172e9e05708d5e5b5bcc39c1321df7f071236380fde011dd26270500aa519b2731c398a384696c81d06a3022a6293b5f7587d5ef3112ecd748bb950237f4937be3b3eb206aff085485e5dba38446925ef4ba835a196b0cc0126f1aabd4b4c07823a12ce845452d334582f6bfcb6a1ba5ba612b9175990eef9606d6f47ae4661e97868a89b4c845f1bf950d4f98fbb51b1ce3305a5f893dffb315c4cf95900a0c2567a19913708405c0042e8dff254dfa600fd76c4a242dbdfd08cc06eee27fc3a2f10048996755b18b67ff419c1fdc04abe3c3341c06922e83da7997058857f7e79cc50b16018f309056fb7118caf53fa5061bffd8bfb35e9dc2ee93e73f235c1ed48742350619c3e99fe321c4f96731288234f801a076e7ad65a7944519f70dd3d4314b5a5c41c1113889da04f23db4c87915e0124e066ff9ece35104f7217cd2ae131abd9d335ee583b9abd66b0b56719e0bfc242c98ce6cb8ec78bbea43f6a8667b20ec84037fc787dfadc9c0d986f8d9c4d4339d0ed4bf86fe7e543297569c08a71e928705bee168d2412a016dad6c2b5345a51df60f9288080b07b844ffd9cd41998a9c7c7ee05d5e44d0807880acab4b39cfaa44157fac4a0e6ed29938a01fc586b3cb3a32de53117aaf55b650441a8b4bc2eb179fcc87e1f50105c177cacd33b9751a96315a4869ea779257f4cc9a4865b53ae60249db0242ac9d83e408940675e22347357426d14a74b68d1a18c788e30f35c0706a322bda97f2069b571e51f28e3c3c59588d3dd24a8de4c11af3a939e9a0e60248eb9b323fd064f08e88cec5647d2b14dbf99473c914cf2952982388d60b3dec2124b78d2119210d326fae7e2e4988ee40ea7f138925e3e3ae884849fa290f00b62965ae31db3c2bf13d05daf792a7f51302da1e2719613bee873331678720367c6ef152b7b34243a406848074f16ec6a3eb40a7588a6c8e1f4de399b899eb807402fcda1fff38b1580eed0f5fc0b47da9d526e8a4031939f4801d10a3ae242a67e66c24116065849233e116d74c9d8033354908aae13a73cf137c6ec0b40fe464836345c17628dd7f586bdb45a4d6c2c86e67286234e548e46628595d2e87ea12ede97cf2c652e54d99da85b20c150309211c38717f31e3517a0ed4f294d9055bd3459e755e2bd1bbc1cc462fe1198a287a27165ba3c6c10a59050f91b319454f80e72f39cde84183979a6102333f9c403b698f9d3fc01c2449bc8a46d3a469210553b227898ae1036e5ce814b4cc73c8a0d6b412a79d769b31f7a1fc869570f75c4212e5dbb6be6b4dee99c5b4b656376f925d079fcb70605cb936096d0beb93639e115ee4810b271ca59cd55edd4737e761eaa7ed90a1118621542011abbd9e36ff4a9964eee76ce62b6b17f7b1dff4eec31a7bbcb40c497ec175a81dce25e7008932facbd713030ce1f75030ddf17db37a56af37c0ff28ca8c1503f526384ee4a2e04fb1081efa6fa34d1586de26c6d6f43a99b08e0737523f9d47a0bbe20070cb23cf04eff3294f8c88df955c18ea2cc91b263a9fc7026c568a586b4c1ae78e6482ee154d1d10c95170db63f19dae95cf08871d959dc56f2837cfa66a9c8b1d61f012480822d831165960c4ff9b9681c566472bc60f5fa520199de14d2bdedb624cca0a9c8d24ea21c26832b9353a48729affc205c5fe039b9b1ef52f305038bd97bd4b13cd7b3a247be40148927f1ed4c194408965c0927b7a2966d17a4a178459b16c01ebfc4fd4ae043cf7754bba6a131c080c9a2151799a1870da1b79da09a2583f4b3147255ee66c6e23ff9454b20ea6726b97f6614c139a376a49cb8fee32ed2441c0b4154da16ad885eeb4e2631b5da6c40d44ad475baeac7f2ace1e7901f2c465957f8fd929f2df134c6ec3b0b7e953e5d0ca45a1a2a43480033b8811c3bee2f2b29f62ba5f36f4c293299c9bedf24fde4b11b4ea9e83001976ddf4b760a07f4097bebe0a562f666018eecc31bf67823dce1c02d7ac28096283a0d6342a8f4a237a3e154bc52d98c381082ebe1fc17aa6169ebe3af05d0aa35b2cfb4efbc6a88c006471847dd72bc4c3057498b1c79f5da2db6386586ea2451bcd500983385aa7233440fb126f352068b06e1b50b477579e7e81743a56f2398a65494fbfe15409f4bdb829b69e255c26102675c3ddb8c3d1cf0805ce6f86251602252c366eb4d3397767b4a0a2279118b4a1e9b34336dd679666ae3ad9f98ce959b3ee4180904ee9a40c511b808025b18f71c55d43457b8340358e3e8aeaf2a28093633020d404780d89b28eb12bb999098cd4091f2cc2dca0bbc39e5bb07be190c7bb2889264dba7bd013601dca77ed76debcb6039a9be329c2257d709fa004095c8666ba67aafc0e63f8e0f0efea3718ce2a1c7255f20f8e4bbb7459f480e4b1c9e7e910f99625da242c64180a8a3c153519882b1b836ba19244e1170eb94c5b3145507bbef996412b10a206c648b716e1fe9c085d9f7e660f2e21b1d1fb58ecdf8721cc9bb318067f9468a91efaeed88462584c340ccfbd9608ff46fe2ce7f6b39030bb6ab2f1ed2dd3fb61794d48131a17e09b664753e3f60bfe441597d0125773e75ebcde6f526b98c0e0ec3d59a406d51da3d1222ddb3940f674c98bece6fc8d38531f23f876e598b65307b66eb3fc1d23f21eaf406b6467dedc07dfd10ff36d0849b4262dbb95a172c39251b2c00ba2d109e0e603eb0addaa1a21dcbec670d05917ebb2ac95195d35d0008fffbbc0047fdf8cec334e9df575d3ce6cda5ead503da56e489d90255e0e872d8b0db0635adec800fbcef58138c39c353144012007337fc63f38dfe4e82723ed0b09287ea2ba6bedbe05ddd4257429aa3304fd764c3b47282fe00bdcadf3f37b730193c343da06c80b9015536be084d3e341ce2b225e037c9a0bea2ddb76ef97d57cec9d7f2601802571fcf5b4143aa35183cab80c6b7ce62fa25c557e61009e72ce9fa5898c6578572fbbeb6591cdf5de9bbbd48ce89f1d0389d5ae22d465b6759d17258453577aeaafb9c6750bc155a29f144b6cb68d014c39ec1e4099d201726271e95ef2e811eb047bb971c1688fa4e8d6eae1350c50dd60c3e296c1a2287e4aa14e3de4c5de840284a9638806f3d0206871856b14f8bd48747bf1ebb7cd5283b09c94208b37a71c4cd38144230744ff9d15054e5484f795c05c7d25774bcc89fee821d095070db7592ddf18a84aded3b59169696daf60c997a2a8ad2847cf72bbdbd0232f73882f8ccabe44aa102d2fc61efda093fd21e7b57180796032d3c35e0d684fa1b1e0bf83fe84eab8d3f865ca56b54128c8328f77cc936bcb65cef8ae279fa62085d34877578e0c5837b374dd106fbfa35f955c40bb5a5f87ac93d3c1ede77b8ed85368e9c14ada827a898b2b900ccb1c7876884e3d3f5186c5f31ddb8d2e9393e277034bff8a0f91dccdb00047d80aa1feacccf1e1749e5940c5c99f85eb4ac1c0256abbfb4264d07a77bb1f7f3d7ec6a6883b937fdee1f027cbaeb4220d814e06b66373768c580f988796132ff93b7e9f2f318133d286e78773f058d0a8d5b46fab4ce94720adc581fde2927fe0ce443577c50a7aedf8fe75e21d98eeed93567833bf8c878b5b088131128a7456b593c21c7f0b8c80dd0012cd65dbbcf6d3f132e097081f1cb04c9385e1444cdd836e2f019702fc856a3ed1ad0188af8637f830320670f0b331342fead9091f1edc33c5128bd259d9a8ba89b95afd984e6411f7a2ff19440657edf17c8fd071a119901de9faccd44b07bc9197b5e8d72bf587a583cbcfc096054f0f78f0f43686ff9e7e95457fded4ae03359cf0db5a98844e7a5b563096f92f0c96c463209a51cde59c5ad4e3180628b7a2912e50362801433629dea3d3a9e41438301fecdb9150bed31b03751d5861f604f33697378c396867988f925953a2994b93cad955f42f8f07048a81f58d47f3ed931cdccd80ae8ad54d063ea0a0c5d3d19edfc72d777276cb652d90b307f2d1021edf9212542e54b1dd7c3d2efd90f81831d8801b68850b3471536298481492ed68d2940a140f8a2b4796cf91d151eef277e2eb0302c64212eba6fa5c0b38a1570f309a2f79e4372d09dff4ca193339f9686c635a7cac4929764d1fbd1a1d09b29416b72a7d3bd0e82567842730562cdc7e6ea759d79eed85b0ef9591761af20ca076700e276985e25fb12ec662f0db86c6e801504857202d6b50facb149a6f445449ce14f62480ae455eafac6bd218873b10daaf77fedf387a71751418bd7a9df80501829ebf41b4edbac835a7a602b1cb334798d0bc7f30eff00acd3502fbf153a91bd66e37a372ed3e58ba073d2a06dd1fb539cffa98ba07cfeee901e1ec28e121069922a5152793c5cf9fc180804a0e726a8a308167989cc9654b3805762c194a693acf66c3c0e34e7dbad11850f1bb6fe1d408364c4cb10f4f3760b686adabb460bf486844bd2e67eb69a10eeed5599f1bfe41e11a59110451a1e914d6dd4e0832ccada086a36bf620eecec50993f63be23304cf04a0cbe725a35afd39abc971d049b65e0c4fb18f285d382256260eccfe056b839b8fdd70b2237f7b060b32932aecfa2600452425b2009bcff687be3814dfee877b012bafcd10b851a9e44fc1fb135408e9b1f9f3a910bd0c1dd88c9496b45fad4dfdd97bc51e86265d863cbc328b8ed8c18e511264f894d4c668d178b5d86811e6bb726677296265f5e494fa9a15fe058628c06f87eaf3bf88e0c2036eac63e0c91ef34f50b0595bbf4072f7224f83664695867ae1bb678015bab4035e2e109b949ecc6d66a341839dc7a25d8fc84bd3c739b90a1be9e3ff519571d624952e46bafff3ef676debc25ecaf377055694cacbfe9acfa7703c946da7a0383ea2eea88cef330c855f7dd8a107b31e45619a4a9c5b15cdadfdd01a5891c289b70372b6837a44517e56066bfaa205928739260e66cd3fa8e6925b1b1fbce097d3e0aa33eab0b7c5ea4c07e422449081da0cf1771b30515bc63cdbe82635e3025713ffc0f56d6d1900748c3244887d7c68d9b94da5cea781599621673046ff43f8ecbb14e53ea300921b16e5b03deec222193f52b3b325dd9ad631c6e933cb147d83cbb355ced395242260f66e87059cb03568922dd0c4d4b20ddd90f3594e823af705800bc4b4ca4b46ed4544e9f3ec5dd01fcf74e28426042237b53c373fb7ab2ba08d7784b2ee8dd6548c3a4f1234ae6b8e3d630b52e61ab4f8b031c349f124caea74ad91ba16e1ce341bda6abde7b1ddddcbad2762666762cca329da72292876967ee170366736cda2a40e9e3c2b3cb523d29a7c4bdab1b8e130d5b493ec3d7dea6a920dbbeb0e51a5c55a6605b7125c8d0ce45db87ed89582803e338efff9c748e30ba310384fcfe1af1f7268c5fc5f7d3cc280d6c903cfeed9d66110a02af41a2661128815f02498b1d48b0d8a9eea79a54ddea152b552b4e566889b5003f6069e273e49ae9437ae5d59ec71ec0cb865a135693202bc8c646a88256a0cf712a8b1d3bd57345b148cd4e53171af122dcda5d0af1529621a0edb20baad2accbde7b45ed1f02433195802c0af09bd866e6fa8fdf9e2ad2690dac35c5dda13f24f37e2b91a3b890bb53814b40e32a963771978f5264235dc7f63fa82be5645ec084dfd211f57c5693ddd32ef309ce0526e2d226a56f498962c4b7bbc054e8170d8f8855dcff4ac4245d64811a2e580dd662575bb33a2d72973736a41bf55830ad71148335787eec433525e068093e8cfb2bafc5b9691c3ce2397dec4b1402682a8103b420be13475ad71ba9f8a9aed8d94f7f7f5061dcf987eb2d3c592253378fc14d0a3aa0d10ca2cbc59fc1345ede6b5e8b9aec74194974712a33a46c853cdb52d61c6343c784ac5e6f77be5263de5ee219e9bc670346edf8677756635c386ab22913393c9fc2ee7916b130897a55e2b9d322a1185212556c16b92e45ca748e0eb0b0422e27503822524a7c9d23db635a49ddca2b07864f2ab00c0cc0c97e212e2d35db65102fb97fbfbf135a6cd391078f7fb81c85cf2b7f7c9e36b0d39161ce7f4b5ec9a63392126d2f3b1085a10500c2cc860c8b24ab5c14b996467aa3c1a45cb8f48e3d9935ec51df6973fbbeae3ff0bd2bd1f2d42386d183c2ae4d966796a6773e4da07b73b34881c627ecac67a7bf3ed229ed927582558d1c75fbe91588ab00b2c160de0a456df45981f73dc98e95be68899d8d13c7acf6017f02802fcb034e168560d1404c5ff9eec40adcd935180b9c33ffce7eadbf6e571d741dfd3c0e92403a109ff6ec9b7f70535c6df2c1e60537fb3fafe6c99ca6c8b0acd6c2b6756b410630d3b060b3a58d0937a9b042b6daace8bc39a0f87d122c38209d96553ca6c745d74c6ea889b7dfc20508033b81c9aa083a0c93874038905878fbd84463427651a32acda86d16cd58f92e2bdd0d97a46866ebd28cf428accd2463049144cff229dbf3368a6867c3c2b90efd2dd5199fa10f547bbbe1e50e0951c1bfd086d5947976d04c2fe4977fe644319cfea2112622176327f4e129714d5dbe33cf8bcb95237a7e510e1668fb7f5c51023ae3db2b8ba49ddf9f0eca1efec402cda3c2d054fd682187693578281022468b3a6a604adfcb66b6a718344fd0d5a1ae03173449ab78a1cc829980e0f6dc5d91f8a07d1ea32c31b1ad785205e776638523f788c314347c11957bbbd80b374ef397e5d3a31a105daf920493b9bb3beb701ac738bf906b4fd03ccdbe4b09f923e5551f7241f43381cb5a27602af570624c14db3774dbf9747fd800249c192e67e9e63aac9cac9e48057fdf155a577fc35bf9134317099b64b85a8a5744ba9b440872ef8e6d7abf35acbd04861c2d59b53496632119b0007a89312cfb97cff6d4f57b2fe35793677261abe12b459edeb996f74c30650a0792119ef9700adf973c00b3537aaafe3c9b448707c100b30a24d3f012d414f36cc291b6af2ff94cdb0f76cb6ea6b129f466b702ccad0fd6d8b99835ed8e6cb3b481426c2bb11e57e117fe590091c4229924c34ef030c1144dacab32ef4fa807afda534eafe958be8892b6e843f0deeba39d2f711851a4aa693feea33fc437744459e105712a2160bc5eb054c35c35b424fe6c8760c4bc8f925ff452b7913b4e416749ee48386d36e3e6e9af29d9feafe3ccf9cf42194b2d24774e9da182a59e14268bc9eb81cc2dc0cfc232d5362afd0da1862d12b0264a62c0a9ccce61648967759e0359ca7f0ddc99e25682de13876c551f23cf4336f5d88716008f9526af3f14a99e1514872ca14569d290c9d9f36f655040820b1255fa0e97b3f80e17417959aac383f8ff197638a47624a058f704e1439c13fdab94e900836fa3911a13328a2347507b0cb4ff63c686a122e23b8ba0ee8cc21966aa22db1b92d58c913aa876ca8a9dd60853a4dcfb760ccc9e50e710b4339eb01bdd9344f2a30cefa95317b017965bf447100b703a07451085657c8df483679788121d675c2820ae1070248b46284036925edb7a06132ce2a943e111b585123be604d8ecc0b1777af93a540bc5f9e1e7cdc5799c65d7423fc518a862fdc698c5be32bac20fbb1d0f2dc41d011d034319fe02c2154e064cbd204fc0860cfed60aae55a8ba9230780f31f8a9631c9acf94ee20730242f7b695fca20b002086a4e04a300166947cb6d7812f754e5a83d5c76faff07a0072b7500c7b125e2e2ef15f6bf2498f4b7812d2e83149ea24a50185e09ffe5078f9da944a60a391365c0ffa96e6055388fdcda03eba851ac03e5d736651ade4aa54529d0612cbadc5c4351d28a5b7dcdd890d0dcaeb5ad2d8b048e1cc8a4466c149b577aaed0a357792bb1c26419fc750c681e2b5024b1392ddbbc84b1c501fa5c622ca68a4ecbd62740e5f96c65387846bee10b14382e177af167c12bbd6756a15e64826bcd3df531406022af9e179ac496343dbedd3cb7532cd23c6f3f429a5ed1d0400f42eafddcd34a6776372416486b5009cba5ff45a70c3104aa7eb6a54d66ac272bb83c786e4e3364e1e786f57083013d89721fa7e3a192e1b8633c40cdde621615498ff3c5f1793b704a27bc6f36580e6a2a5c1799ecf3b2fb1c6a516ac4a12c45c7ef87f19bfc750cfffea3b64c34e9d7fa75c8b7219b70d92c6cab6c13311850d18eefceae8df35d00642f3a7f79a8de06605c93fc23a9ac45a419e4308a89382675c9e79aa7833d9f52f895b26b0309938a7af24520e061c984d3276a136b5d094532d36d452537325942e7e7ff4c6ceaafa25ffebdcfe1b5b0cd7e1fa084ab3d18713cc8295edb83c64abe890725728f5459c01f9d46595013c41fa752db2c6aa6653bdcd0552384ccf118ab95d9d232c06df5ef249132161791026c1500853755faf0fa6caaf099d90526e7859863de08dcca5a5139b1ee568ae9d9e7f0093971d883122b9900b382b612094d05ab9746b332212b5aa40d02003f66694b1a77f52af97420a43f2df22bb29a4111a4912bac8fc8bbc4251de925f96561df87e38875e6b9fe69c1053c7c3019993f1ee0ee11504cc0f19810b0a43ab4fc2b76e71bd1fac498cec8a12509e856d6011e9881304cb23cbae4f84b5aab03de24141a7fea7da0389e61e37497a878addc42b5c8dfff061727bf557c580472b3351ace57e162b1d731f1125c5ffef82e93bf7bf6b7b4de5491f39cf1de74aae13d6e1f1fe30235a49080046aef913925db858efa3012f9244bac90106393713e26cb871b5f27171c1dea719074323faa6a5d900409e28500b0f057a3b576d045196118a634446f23802d29acb8dba93922543e846f905a3e5b1470d30c9d95b808c913a6ae0178d0cf0d8481e2cc478250ca4b44e333e3d6c0dfd7bb8cdbf49be5833198708041afa67e2bab4ab2779fe9c34c4b5f5f3be759a5eb9d9ba8d50671bb81231317858daf82ded9663245d1dfcb4164ce8d738f02ac146cc7d008c1fdb7f54c0f4803a0dbe5f8c3e605f2c547b89fe3339864ed1b2da0bd991ced317c40376c5f2a354e4627881315db417eb20a85c08d1bd5c4c6955273084307a62b9e42af0f55262e54c5981a6e110df34e8e1fa0b9904628adecf83e4fbb2866f491c85f8106aa1030aa96146296543ec21e02945003fba134e46f6e25f393dd0a6612aa102cc58965e9550e48c389c1c04a326470cebedb427e442f8b848df82a8c5d55eed0d1446c6e5d6e0ccdfa2d219019ad3d400fde350d690cf04d083844adefe5069c7066b0ea24854ef3f5853328d7bb2ea735b370eccec5f0f777a467c98f040c423fc4c3df237ee8b1e9cee6c859c860c0297667809ffc0b73af9815b01384834fcb56d3e782ce11ac9124345a3d34fc22ff7e0c3149d66f7bc8feb9743bb54405ea88902011cfd4ed07ee19c6b8e6de6dcb08e2e86427e5e675dff052652af14d7af936f032bbbe508c54fd4dfbd16921e7b1c1471e96f3a521b911e32a5888b5a4083be97a3ab843a1dacc19e60f161fd84c10fe94c9952829da6979a83ff25faea14df6f6d5c876f3103a8a93deb51a3834c9da185355a8b189585f7c71ca082295ba29e326e60b9d08ec5386deca533a03b903541380cdf616135e348c6b09a5cd4a074d7c2e3d794e44a0c4ab8537f3f53b12ad2c2a996c5ec0a1b6d9a25d920a782bbf2120262a71f816a66ffdfedf5e5dbb868d37517607e880d697720a0119077b19df4e9e8709a177bd25508b00c6c79d4b1c1ca7f607aa7b18e6f93e40c8ac9d8705bd9deec9626cd4aa06491416430f1e204177b62d1bd18e9fefa0da4d6e032f785851dcfdd3e2a2058b1811aaa976c30a067d9accbe4504abb7c2a39492889fe518dc529cfb481320dc05a785cdac3fd225936716b149620ce252d079b0f14a947bd7efa17868935b6483516f7653fe108e5c1da91db5a58a3aaf920ecfacdaec9cb997d80860e966fbc4c2cdc52f41fd3dcf8bfd70fc4a28f99128b1b5e0d412c91fd59a9deea1d9b5170e92e1ba12064847d72f9ed9a58874ce906252c6679851b735cdd5f3f17e35e0ec6117e83b9718e85bcfb608428bf03df02e2950a07e80844403f21b8b0e6ce349c5ca2c260aaa780949050d2d9fb90f890e0d33b4a8263b0f3ff72d06ad34072f3567c85a6e301d5b374d9a78a94e5a3523735e3f9c2db0734aa7c7c5787879687b485f1fce35966c742b896aee8f0361d98ff221b58f0746a0bb4ff1443b1f21eb26e1503187f52903a842a278d3cccffe3328876cada9acb5f109f1679265c39ff58a2f5f63162117f63556389d00b7ddb9cfe0346c62344ac0c9f130294d0178425920cf8c5623e09bd387d8f1d1bc4a7df18f8f38f20680ce2734505fccf2d7e13a0fa68ae5f7f949346bce6896c992cff605197740913534fc38ab50d679c92bdd04e6682ffb8581c3de476b13bed9e72cc7807e6ae6a0f0fca5cd9a03086c1a5971991a7e0a074c128727a4fb8e63240df8d4e87ba2077b50ca0c1de5ca6b4a09fd2d16022aad1342b0dfcdf706d13d433db3f592bb467f911fdd09969d1fcb4fbe4ac156a4227dfba8999642bbb7239cc6bc02d4e1cc5ffa47642171b9807bc2e62027befda4cf5d0b994e36b110580f40ae0b8505bac989738219bc3deefb4fb85037482687f8f196ceed78ef556907987d952b042016046c57e36e47e9d5e4c71d5fb7bd2327809e4663d75a402c9b6d89857d2d07375273e3142dbfbd1c4c658fc64e2436ad5d8a714d9a626b8c6a26c3ca5cd8b7bc75162e5ea63a1644e4365ed6b16107b8462e7186586b0b2c43f526f0656c544b70f5e59e809462b1e00853cae8bb57e39c55abe0e2435c53bf68c0dd39e3bcf6da3250f4ad1d47f1e9813b22b109c9f2ca6b06b362f8a0c11774cd90321561ea5633ae94828698a5b87a338a0a1ee58aa9e3759a68ebce835ab5bffab1cb564a704c220e8f451d02ca511d3a61071e59d7a638c6eba218fd186a9abe7144e21dc862d71cc14fbe19898710ec7321917cb26df10994c2d4980eae0c9c512cfaa4068fed52d0c57c9dc3fe75b51f333210839a37ff90e8508bbe215d7fd50cc4c90daa00e10194fb660fb38b9a85be80afd5232edba10ab7c98dd8e5ca4eaa6fb7828b529711682b92e5c980bc877a83b9d7f0b848181110e370bee7ad434463f90c954705032d56c0b86305b83f61642daec190941c8b687f46d70c0c0cb4d627c0af930844189d3df40c1ac2c5e0f19e598bea0d6405cbb41e35fd850f26bec7aae8873c883ba0fe69c268839681796ad11c090e891f6cedb944fb4118092d8b4397dce87fb4755b1ac74217d12597496c41370c4653df24c3df308c2c9605efcaa3e8fb55250eef97ba85bac046d6aa82d249fdd348bae7b1f6e658b8072a2aae3dc22920e419e656810cf8f80398bd4dbabc4df89b4b46328524ca43edc03677156dc05a215d2c504db8b28e876a1797ae0b57de85f634151a8bde6002948ae1f540fe4d5f53186fd979992d4bd95c2fffdaa0d02596e17b439ab392ae0f50e6bec0af1ca9668890801d5dd1c9fa69c06ef9fb04b9b0260e8279bf93f1fb0de27d60b0c0d07329f986aa78e6660a8d0785903e7bd71fa74fa1ad87a952a22ca0c215d035817ef038624b5445a326d84ca96a7486058add15801596906b1d78381b9092f504db0008628d32fbca6471359d445a248c0019ff3b8f6157b7b45c14c76ef673a927ac5e425c0a2cb71cabda5b2f323aa037543ffe1c84c1833ac61c016bbcda50b219bca8b2d210914e2e6aaf5483c943fd4e7c68228b26bf751cb466667a66529cc4c19812a8306a25776f5ceb24a601baca327cbaf470a61955474f3d9ae0aa8fedef83fd62d1d1e843a835c3b993a00f09faeadf015cb28d01b01fe5ca49864b667ba0cf0ceea8439d39cb2a9ce71f7b3a5a9b2774141bfd68baacae775d48ba40cfdeb1d1018f77ec090a44ca5f4672f8a1b6110421edbf5014dac6469e980d3e207f43e25353c1a7d4d010974e145a7cb8a5e05d7d79c7f4eeaacd410dab582adff9b9ef8ffadda3682651dbd8ee69d60464070908bec020c622cc00cee15aed981698ca7d413b83b78fc1a2e6c85bc11787374342b243093d26301476c94e671d7008c7f076dfc7d5ea5d765cc4e82b0fc85ffc8c6637909142f4f3fc943170002ee6e4ffb8a3807970aad74a2aa0faa7a3bdfe4c925757497aee9bd3c1ed174e3aa9ef62d6c8df963d0272545e3a19aca824d7ebb9e22d1818f67e98d5dc3e1b3c45fbbaf8a5e053db3fa77af51d975a22ab70321bfc40af2e3828542f4236adb4755552c9437ce84d39332588d33eae8fe5119eccca99d74962a3c9f09ee67eea15f24e58fdeb5536db348f335c67ddcf19dfe848cb1bf5f2be87f5641032816376f9dd3a983822463898a9ac4fa1c88294747715251561bed2933c08be3c006f05a7b3fce71c179e9fc1bb1de53392ba96d0080179c058c441b9e370714526fe5a01dd1ceb43fdbd75efa6324d799955a4cf9441f50c25e456c6436f95fb183851782552a4e3ba51d2597225b8f2b84dbd01ab4bebb18a49712ea84e86297c4be4e394a3d88646bc8da23d09373a2534fdec05540253bb9435ce0f9c1ee444de9557f880d3bc81ae16dee47085dec4022057b0d3cba15569f542939f58609cdacadf9d5055a9b123be4fdde7985a8a4eee0d37032769527587fa1e09cc7122c8323d1c7ca23a98dd63e970031d8545288d6224c9cc034e60731a9212a89fc6704e67f4ff3638366168abe7c5300fa1dc6b95f56e079cc9b97595750031d8d346244a5619031e924c5eaa8ab82200032737621f4e5684fa19c40de1ac18cc6d416ca42dc59e52b07e1a69f3dca3ce63ba55a4c1a3cc761148f83808c62b840b9252ec3fbb2060b9e7c75f6d94891722056d24771bdc8806d3c5124153b2e07ccbb16b02987c0e0ad70a29b48ddba6a3201ed15f4c055ce2b9e3feb5a7ff082215587c9d891c9c02c2eae1c5efd8625c6535ec57be13465f5d4b5acff1945f0a7a11b0e41546b1e8f3a3ae85fff4ec1c89c2dd44398d04e7689464ce2c631929b4652c9e7645684a0857d6c2cbbe2363496c88a5d05050fc76d488ba7f953959cccb5ce8e062c203df001a542cd5e3ec4fe97eb15619a9149022c9ad71b9de36a3a5d257a0d3ccca684666a703ded560df69a53ba546dbade63093831c306d04bf0ab91ef00f96e22edc0e9745ee374693ea38e7c9a66f766d395be678036af3520d65e817f05c7a9e4b5ee15decfdd2c40e6a75789c112a2cd02da7e7aaa27b57b50626a8f989630b47550d39efbdf17d08da5e066039e2be80c51a0221853d0d840fea4e5d0a818de468b455d1a99b98b69ba89c118ff0bdcb250fbfaef760e3eb3098b92512e0cbf2cd32052281727ce17a1d55824fe953029190dab087eb179c5981c1e785a2400bcacfb5fd63025983d3841b2123e21b529bcae8f9162068db42e1280053dae8de583b3b713f2f78738df67188b7a01f40f0918fa1e51ddf85757ce251e903bb996d87ae4c845a420e6333e0a3c292521190d1318d4840446166d1241f4a837b47179ea554e144991857037225cbcb44e782a9c08d58f166da0e7dddd3b05ff2f7cb5ca53e445a3e6c06a1e3e03c7912619a8418606b4b5a6d8df274ce2aad313d5ac4305024352e9219046a0ec8ff68f6d7ce075490b9bd41fc9940f8a73fadf6577a4742f08d0524f72500d944e950ec1a7b45d2bdd46df4bedae251dbfd53c97a4d3c746de80214c46675522ca77fcbb13500597ad629d5625f9c2ea4c38d1334225b00ec20b9127dc4a15d65f129f4f95668768b51b02341df16c8430c163878ea13ed24b683b993732cc35b987c78e2bd25f1fc6ee4e152bd489baebe997a247076d380c092acecb79c91213a29ab370776cd2c3b3871204cd156e2ce3ff091fd31b65cfb824f14f400f6d9bea7894b8e2744e60b68cc0c05084c024e1cf64867a132f7429c9411fd0c9e225f4828bd9f523bdf3f5a8c21b09dbb8f4770bde3a488928ebdf0ff3d2c300bf6f4eccc94c5b1e4551ef25d144cdf78fbab3fbf37f01160ecaf91b153e6a9322575c082c4eefcfcb41f606eacbc87e9ad7022a0916ca782ba8a2bf4f408d135a1605c85ba1e211a78747ffbd1ba7c36589bb7cd801166efcf55a8e2e9e24bc6cbcfbf1ede191a777fb4a6a67a39bb92498b8d06b287882c859d668a7757716572334aa19f4624fbd7dfb3cd42a34f6f12da60c850a21a2e9998bf4256b961200e57621dff71ff89860ab8f1388b889e6836fd22f120d9f42e08f4999454c0523038e056404d989a3dbee88cb59a57efd7c5bd3827bc85a0bc8c02953ed0d7dcaa108c115a48ebe00c19ac6a0b7daac1b1468d6d8252e93f061bc381523fbea0f248d4de1945c704fd01510d54aceebfe791ff1a44852613dbeddecf2e7fdff1e981cfad3c615f65ec413546c787497f2dadcb2022a6a2aa463bdef07ce1f303b06c8413357261f8d662a71d344737504ba69b8be1ea8252e6838ca8936dcd7041dfd6a646d1cc0b12436f3c89a7feaf8f57fcd34f860017798b1cfb22395d2f2836dd02988ac37178f82e5c543a98b8982e59a0729168c866526ce53173da0b9300e6c17ce85a74032f052a82142b290911a0edb93bade04baa6e061678107143823fbfdbf3c06c0dd0de671ab6567cc3ee3a657e7ecf6d10acfe4ecb89f9698307623643468e0ecb86d9d9b18db9ebd3272d674b43d42784367f2454b3759ca6bfdb30a9001d8b0ec48ed1378d40a33524c3a81de9f331833c8a3e9cee970f651dbfce88b26ed8f6a4240fa2907529e11a44d4b35483310d25e3c006f2d7d51432acc216d43a4de48db275262f925ac1669a7e6e9317fac48f5a8c60620767062e78bc4d93c3fbd5dd47a332512d2fda7447f76a7badc77f862f290ee4b89804280f591a6badc290d3e128c46b681e69d71c3ed2a3b6cc4c530e0479309d6287b692e08529de31d830126afb4a4652a8aeb3303d3547d00b93b661edc1b226d6d79a7761fd1e43264a090a0d8e9cca3454ff74b8257714e299ed3c6f5b6648b9c26bc78ff09121b9a485282e4c5c2b240129ac0819c8532b0b9cca4472c180836f7285d09386581375779cb546d4c8ac6043b3255ab1035c1d6e07647f453130bbe2aaf04e5b7bf49d0cd6474c25a75c67901a23870ae77c32e61af3ffea207f8749100649c3cc054c36691d4127bfc7ad18e1f018ed75f0609001dd83e41d6e2ac66964c248eccb2802c378c9252146c8674ac23b81fc13b0089add0152a1141a6472370df94ae57e54ab1c3a26c55f24a1dd0ccd360e794f0a63987a08d50ffb84a76d3689ac1211ea5d8917dedd6abf81476dd0ac1c7017aee6abc9ee1a41e6f5edd0b89b9d99983857dc32914abc1e20c29c2c2882c21923c947c980e43393500716ba97ec56ce33fd4d01240988636baaf5aca649f87fe606606fecb888b6a7c62dc99688774d648982ce1cbaaa3282467050963b31d95c29f55d427d4166b142f767a695ee5be116bfc9455f405c12a1c3e7dbb14564f94c73dc0a7af0aba2b89c172b2fe5f9c059c0a51fa792481ba1fb6f97faed51fa1e1857eafa818290875f34d876e2a17e5adcc7bebc7b01b941b9934c2e4942e9064d836f976444ebff2dd6c52f44fd9132f4a4a0ab659f052f5b80e6696d217568dea33b7a90302d011384961f7809b0238ed7e26a28492676e4368ee5548a3f5be6b7c3f4342ade03db48e82bb247dc95b52638fc38802881d0c2691f2801d0be3a66700ca06e22070d3987ff4d44db8f952832f0a74e43f77b7874bb68fa67435fffb3a5d98b034ae7766999e8c9563366a70d86110deff01dc7cfae0391be8ebad2fe2d816a06daa82ad16587e43c91dcad74b672541420d12c50f82f737de005ad7235436f404e87ae5b3ac732a92a34bfa3c20f267fd4c7ab07947232044ed716b52a0eee6bbd8388d7e4d15c904a4ac17bc8a7c57032a9e8e8a5c30ceba4a078e335cf0db5a938e3233d9e16a503d741755da53cb2b848c86c25199e1245000f93043ed0b8ca80200a4d1cd64118869789d5e90f2b393c38faf9492f91051c29563e737b8d7c5c704dd199696ecbdf796524a299394015b080f081b083dddfbbb0e92e54cac171d082fc24500c43d3192e533573280171d8acfb0e33fc3cc2ba48f17e79700bc38bdd079718ab17b78f8e26c5abd38b570f0e2740200fef30889f313080d369831481685caef5a062fcea0a9e47b518a91645422baa2362f4ad98e17e5171e456408b2077fcd8b52a900dbc50e7436cd8b9f6366bba86dff1cc9aa4168760028e60680a22e0104c59bad251901c5cc0050d40b004700c51b8d480450cc0a00455dc4a5116dfa2180e2cdce50208062fe0014b50730a84d3f01a078b3f3880050cc4440516f10146f68363a0028660380a22e402b1b6dfa4240f106cf7c4031f780a226c06bb6e9077961261628661e50d41d30316dfa406694857f80621e00286a01f88cfe8ecf70cb0728e60080a2d631c2ad4dbf87d175ba30baa20f0050cc1c80a25e81a2ca69d30fc7bc012866ad0128ea1c5054c1367d1c50bcb92ebaa26f3300c51b15088a2afd81a2a6ff44f90614b3e6018a7a0798b46d40f16657998cb26c0d28dea87480a24ae70045adaa59e300e9cd162d28de6c51dbe055ba1615499d99a40227a67136b3e19bd31a5112b09766d4d42475269426457369ad198cc2742a8f5dc49c9868100d923ad9680924d86ba63edf5c37cb3564a1ecca2d2525a9c314862ce6e4464652c777b7e1177e022761240c0b1386c32d5c05136121ec72fa7c6f4e6540c96257264c8a3109690ae5ebba3f5ca32b044605a9336363665032398620757cdfcf375791ac601a93449c2d933a54db5c446c2fbccc10238707fbf9ae9f6f4d4657f445215949636df91d8d4d8fa40eb67c20b2c73f67b1ba7b752315f46e7bce0eda5fb6a9267b2d49007b326dad71b66912d4a63ff67c516362e747142005d8b283098a40f6fc1f76fec654c294404ca94bd3d6db9f7ef69c2a5a7a983d147776a84a2582ccf6f7917252ea5cabe420875486568656665b9e200fb1b0b267ce13a6e7d3393fb9bb3d6d0e91aef9405461026da74892ec49bd6c31f7b03939e80955ab5beab9d30f7e53eae1299cc4113ba16def4bf92738cb47bf50d65aa5fa705321fa725aedc559dbb8ce4bc9e899ba6bd2e66cd0dcc081812a878e1a9b1d3c6efec3ff9bcba0cbc02b92b327160f2d0e4e475f3467ad73cc3407e3ae38a741e7f3714c74e57405e6a17d24586ca645cec5b99c27c8593e6f61b287522ec63ddd7baf067f2f0777f5f7de7bc3bf37c8fdbff7de7bf3f7b22ef8f7de7bbfbf97e7dafcbdf7de9abfb783cbe3efbdf7eef87b815cd5df7befc5e0effd7175fcbdf7de1c7fef002ecddf7befb5f1f70ae0e2f87befbd37fede9d5be3efbdf76e7faf8ffbf3cabd7706e0de7befd5b95368e6affe7befbd3deebdf75eeeeffd1000f7de8b736feaefbdf73e8de7c1cd206783202c9e0e80fc188000767c0440a7070034c0d10087831b2b1dc20d34c8c1c9c0c6df70cfdd703540efdbb8edb79b8d9bb1e96ab66db3d96ab64df3e0766cdb66b3d56c9b8c6ac360db3ea523874c25c340e6d1641be3138d8d0e870e372e8d8d17a7c376d452b9361db455c964797399ccc7a7f18e3e6ee08ca6b5f6cbe59a1dd49f6f0ee394cdeedd14f7b6e7eddf896526f9d898eca1201c398bd26a85d62b58b687f3de572ad2a702ad5eac55b6f7b548f684ef7d6d39ab6697e76d6086f4a9473c2f5234b677f35e7dbd9e5e605e514ecec3a30ae953953a78915ad1247b3e2467d5714c1ac791e98af37c11d2a73e0179911a01933d35ef3d1d725695c968d191f754c87974bcf7148af4b1403f5ea452b6b703ca59d6356b79ef609cc7e6bd772fa48f3d7ad1c1d81e064bceb2af57d3cbcbeb69741e1c4e481fab248017fd0925d993e3e52c3b8eb011c953bdf7ae83f4b14f3b2f3a0fdbb3f19e9579900f156d2fbff7538cf4b940d3879d63c2c89e1b31675d974be68272cdb6477385f4b94701f0debbafd7547a2dbd9ab647dffb7984f4b94ad387d5797126b1bd1aeffd2472d61dc76934bebc991e2fce29dbbb4fb23000785186b1bdcf80f4c140f3bddf80f36cefbd0ab207bb64ae984882b3b0cbfb22d2071f4d1ff6a5922702d9de6b04e7d1defb10644f7def43e749bd062f6eb0bd773c7ae08b747b9f237db0128eec71161ee9cabee7fd943ef869fab0ef79af81e78197b4ab8bb2b8eaca711d4d1fce0acf1165715cd5ae97d2f4e15e1d286dbbc964a36c94d5a7e9c30101a94fdb7e9551d6368e553656d9586540d3677bfa01e4a2acedf572bd5c2fd7d1f4d9940670f4a2accdf57abd94a6cf762400a56d35996c948db2a7e9b301edd8a76ddfca284b1b472b1bad6cb432a0e9a33df900725196f67ab95eae97eb68fa684a01387a5196e67abd5e4ad3473bd2511a292bcb64a36c948ddbfe7d9a3e1a508ffbb4ed5f1965e571bcb2f1cac62b039a3ef90900402ecacaaf97ebe57ab98ea64f56e2e0e84559d9e57abdb2ebb5ed63a5e9938fa6064a948573d65886c76d1fe7e39e9ea64f069a3e641508c8b7889ff653d9531995ad40fb3494d98083715634c801713200410ec659d9f6736e523c38ee75f3dc6bdbc7f9bc97714037321e321907b4ed6750d3e9189f6a461de3f8b4ed833b389beda554b3bd746c2fa56dffc360c3b1b9b6230c36178ecde56daeed68dbff1c9a4ab6016120c321f3641bd0b67f6323e7f1c9c6e88d797cdaf679dcc034da4ba96ba954faa5b4edefb8976aae23cda5b934d7d1b66f53c3ce649906d4b564b22cd380b6fd1a239dc7a73c8ee3530d9ed68b872fa5acc197d2b6cf693a2a9d4cbeb3ebc8b52d78b4eda738a11c3e95641968fa6c2328aa545a63d9ce40db2ae98f7b926de3f4e1be481ff66d4bc5c5b4903a761bb77d95f7816f1930a78aae9b6cbb93f3881369c79c47ecfcf3ef590478fb53698e7329ecaa8baeecbb5c1f3817fbb8274ee6a22c0e8cafec87d996737151dbbab898d4b13ba78a1637d9b6bfc99c07cc06c5c168ce5a4b0ec90d50b995742e666dd6aae7643e2ac3e2dc323f559a0c78019c1054903deeeeeebec3f3c0531f0a1e62145cd91c6c990505aecde974f373ceba757fcf885377b73216dbf5f3a79d6ea79b1e50fb1944e1c62b32c116e6b6e005a48cba165fb88eb65402e8aa3fc5acd0b10279c57568e31ab4f15331d2474a1fdbe3a761644ff7acc9346663c7130a8cea098bb429f519ff6cd2a09851e0e1d2fbe0c88f15ea7419e5219612fcd1cd8fee681af7daa6713bb6d7669ba79b1fcd5aab7e0cf7dfdc600acae7e119a9ef3b3c112a131393b7349d4c934903b9209d134dcc06e3c73b4c745535d734cdb70f3450771ee6fc6108c4cbc60fa429c706cd01e92efe1adffcaa5d8105bd2b2075f05340fac05545592af80abf93e8318fd9e0094e210cbc22149c4473c83d83f7834c1fb9e3e1bcdbf4a9efb258de728c93e1f08a5cb07a78856cb3baedfa2777cd5da76d910034dcf950f594898eae83c81eb977360d695efc1b3830f091db878ebec2ab1b9a46e5ab95d672784634217494d19ebe9812e9c8b4f1774f71784552600b73cb8062eabbd75ee519d1de035b981ef3108b1e63fad1ea400decb4ea7d0b75e74722b3f3dfe764073e7d8e7e3a609a82f0f45e58464270b8f3a1ca5b9476ef19a1413ce4412226991327041584ae38cd30464a2e0b8235d1592daae17970f347e319c9e01c0245ff4964348de88bf7690b48c82b72535c900e7ff8af52693de60ca1ef4feeb989bf7b7102b16b7846f2fbcf18394f77f49af144c8294febf09b4393a8a3e338e6ef5e14f2c022fe4da26f0ee550c18f82d4c1eeae4e88b350a02bac04b524674d58ca1fc0182d4e214b97a40e7efad151882f31fc9f23c7cc0c550167d7b450b73f1299edf9b387ce231ae1c04ef4d745b6f7231c38893238877c0a4d21222e7bceb30905ffa43c6a26a78c02a5ed15f1dec116e607f953ef9e7f4e7d2ccced81297846dd0764fbc40eb60bf18cbc4f9c401c89418c266c638c9fc39db79c07c85d1a7822e4f7bf224773d65a2ce4be14e877812d878aa64b7404e188068f70e0053cc4cfc2dc1b98c28f17f0a3e03cdb63cca47ddcf6e579c5c6184856d24baa9f4c94557de5df650d631ce2e778ecd3af7e8d4f888c64eefc3b94e54fbec20f66e3a8240fb33106daf88336fe1f4fb4e87f57a8fee4f8bd22146c61ee0c6ab0fb68cd15c6390b37701247ac42bbfefd797fbed350d09eef5836fd1c22cd45a9e09df781ddf9a5f529377c737cd022654d222faa26bd9484ecc9811e217dbce88acf5c70e127e8338a44dff31630ffda22f3535e91f917949bc23aafc8b49bca7bc11e1e52b08e30412959142657f591b4a45de98823750059813ee2ffbb3ae82fe93fc41dcc41e8191235729ea35a93f66d39cc75643ed2f695fbfaee84ece9be7e37df03c5e9b2bd790b98bf7945e63b9483f1996c86690c6afa741a8bf5a0459fedd8b3524f531f7df21585499a7a5f39d59aaae87f28f34b1ff19887f55998dbbd7828ba93c3809c4774a75dab4bf6c8fa4960c6a830bbfe0f18b4e84eeee43c1e73e9d291a8bb23d125aa44b160f19c4daf7858fdb9499f3cac150884e915c8c34a6352a7be10fa087d16a62355094495c89eedb9dfb6bf9fc881e274d9e6b0e8ef3207e361fd9c1e747d975587c252a9087675905e79ba3f63333663dda657a897c7b111298cc2fcc859da8d5ce25abffead4857a44ea558bc68fad023a48ffa957af926d592a64a49489dfaa2e7b0eb733f5c5aa43198f388f4efd72fb2c78f50ff90c8587fa7a1a23ddf7d3ea5e01cba9da8556eae503757bd4e596d8a7a719e1e3416be4ad39f4f9f648fa493562ed5f99e9e8775856a77f6d01271a0ed3b149c3c48ee0fd164687c04c9058f50f07ead427f86eab306ab874c3879438b9656cb3af3429cc77e0c170ce11b2104e7a9b1e007b66efb41926d2b0d61841c9f5c51f76ad4ae4f6badb2d65a6b7561df2c575cd9a21ce5df0fc179ee8fa0a239abaefc6ab0fe21839e2d0fa5644a638271cf33342e4a29e5e74c8f3e2769473f107ce30fa2895cad0e4a991227845215826c0d04d96208fb88fc4e0c617bd769da431b5fe34f903df9ad9e2f93f26eec39ffbe8d1a9fd42627f3a53e51a5f70557989f0a415436dc092dd0b267befdd944f638cf7dfb53c8ce2b2205c50f6c79c54e20e731c1431baa54f2e7bb6690ec9992fbb7765224e9c3bea863b59c55f3d321849e20d878686d9289f4f22d9d394fed0312a5f3d39f4f73e6e867bf15ac7d77495fcd7befbdf7de9ad4791ff8bef7de7b6ff579e9bda9fb61ce5a7b185e9bc569698ba1d427482badb4f2f39772b6b4f33895f1192dd55a6babadb5d61c1974adb5d65aadad44b22db35811dbf6a5d7df62be9fdcd509cb96599cae6cf7a6b8ed59634f1d53f64c4daeecdb6cc73c45db97dbbf8ad98413ee0ff10bde90e9743138b9e5146999c50a2b5b66b1e26873dea9bce8a6a524d1a7a391d9bc907c2041640f1a6364687fe44abcb1e9933da93742d40ccac3a54d43038af5b52bd0ae11db63173127262c5b6b50a4443bca630a4316734aa540d199f6eb8924249895dd75a0e8aeed5406942cf6b461528c4948dbc6e1d89819942c67242b98c6a42b9bb625dad93b5b4be36850dba62f725bbe4f9e3ee8ac51482964382b1572456b8c8c8a85d64d4f0149f6bd56a98e7bd22dbd9a4445b28238e0c5da43035eac4294f0623d8204aff1fed40cc942c1c88bf50506bc589b2ce0c51ac3086ea30ac99a2e115ea42950c08b748b222f521684e0ba08c99a30105ea422f8e0456a020f5ea45912f02285512892359d10f0229d81c88b94ca7e91f630c4535e4896b70ef0a28b618017dd8c02bc485f1022e38464f9cbe745d7d2f3a2574180173d05415ef42725193209c7599208be3a68d0f225b883657b9c33d775dc10cfa2de19c43f846e1e601ad504a7f6947e62b735aa499f6e339d765ad5d1e049aba6b1d91857156caad56854a1a6654da3d99a06567b6f9d9fa837e6b4ac69793e0627c6396bda764268a9bdd652fa73ce3927a53f2db5d75a6bafb5d7da69a7b5d65ad05f033bfb48926ed68871d2d68a55e8c40e23a163bad79b820e4ed061462bd44de3e10d2d7a6aa973bf86ff2a95be5ef40929489d8b8287f77384d0a2def763f7fe910d50d6101530207d24307ddcbf7f5fcf21b13dafd8f367128b9b4ff82183963d74df9f462fd0dfee6f1c0625dd91308d9fa6e0f7526b41fc9c6704532a653b9b42e5e94634d28b1bdb8dcfc627e5782754eeb49c2ad5dc4013385054a924b881db089a90672c386b8cb2868c4e20fd46e0e40f710a66106824db390f96c9f9358953923d9e13b22725f3a91428cefaf2b8fa929d95ef7747cd987bb31b38db665af5219ded189d51564542ca39d248cee37bfe48c791ce2315e6e1b45291ea8bce26fe5918a82807b3e7ff8041d797f36cf4079a1fe22c1af071e813f5329b3c9c4b37b468fa1cfd56a8938629da602df270cea4ce3c729f85b96945aa583aea3cb282a92c903d5eeabdedbd979f98023d707b2a6e4f917eeefb5cf7fdc61487c1b2ed3737fe56f02d526a86ec997b3e3d63b26a8caee6cb6098d5c99e55863d5bf8452057f34db0e767d9136a4f2e629dedf99c5791a4cefc9c299a22c5f6fcf91628569f8a64aeb9fde63c582e6da24e1e4eca654fba14c659f3e9cc79f03f8eca662676f3e992cfa7239d383634b469be3365cf033ac58abf89e59b41334916c6de2966e447328ac8cac67fb7ed3e12996d04ff055b98437cfb8ed0df6610c8c29c51b2a788b3985a8ae9a455fe10a775d3bf20dd860ce930eec08b69e8af02de2dd01e73ccaef4936e7ff6f0d07bbafdfc193567d307c8f4d1e56c247828e5061bbf9633b246eee654fd9c01458d6303c59f3efa3529545f8e5c6dafa596bd3d069f137b7b1c05d8dbdfe8607b379201c59c02b78de6dbecdbf87e6c3ccd097b1349d81b68e3e5a64988d8b06103c4913a1b48f372d31f9a772109119ab79686e6b7ef7aa0b5294d0992880d319c2158c1eefb8979cb9ff4ab88b3e55b210090b187b0fce9729e2b7fb65c30611b412a09d9b549ea95c8a46472a8294b6b194957a9d4cb4c29864bcf820553f5c019811f28394ad1b4ebcb3ae794b5f370669a5b3fda39f6c22581edfa72f7d8f291d81739b03d10891cb37fb0f162fdfca1f31cf13ef450a652a9542a053ee8396947322b985f4ad943aca037f39904afbef74368fdf29e013d1089f6168362b69e0732bf713232325aa60192c8e55effc63da765b4d6325afb8ef33eb1f380842593c88dbff1338f648ed95ab6f748e698a5ed3da62fa33f2d23da2da3c1f7d0f33c24f6c5d4b740f70f1dc0f172d71a8fe3fb99791792903003fe2469251909b9806078a145e4c7850f8c59420561948186983144665e49122233a0129abf910291d8f7b81f66de7e343640570e22336093999f81ed0cce7c3333df05e8ca312801a9e3afe399408b322684fd99b742ea27765063066ca222a264e66b804d32112599aee42b513203bab055d1e1081b6658220a23221f904e2f314a9a9eaa4843082292aee42ba9f14a667e03945503d47425df41155dc997719926ae8a2a3bdd25655df79a4384ae4fc2f431926082ac596bf7604a1d9f2931498d8273ed39b779f9dccd6bda8df69b767301674d28bac23f299eb3e6757c8e573d063c387edea0791b357ece6821431210c20824384f9680b3fee7a7e03c746aee19d1344d48065050380370c240700a7da0e6f1353cbea6be6351b4555b66c1026873b5dbf1dd3cfdb4ffeffbfafdf0f6ef979ffbee771ff7ded77dead3fa6bd4781a9ac7f1dd507d187c8e4ff53abe1cef9f8eaff9fc6d3eb96f7e884f580db6d9f1e1afde0778dffc64a2fff8e70ed0a6061473ed6abe9b9f3b87082d4ea809f5a263d9f8455762e3175d48083fb7e39b9b47cddb7cf58cf0f89aa73cbee639ef7688c0e36b7e88efa8f95a987bc7cfa8300e14f443fcc6774dfd0bc8953f940cc5d9b2e1f1edf8b2873b3ebb6d3e0fb4afc1afe39b613cc49fe3f39687f8559f0379881f83cf5d9f077988ffc6e79fdbf89cc88b3ed79fcb7cfec2ef7d0ef32b8e743fc792e44a1e8efff9927b53f61975721eed77fdaeee5aed27b5cd1e11623577affed683ec5aa767a4babbdb26772ffee4b328143c6d99a5cb95dde5685b6b6f68d1d803c1e737a3b0bfcf3c0cc63b34e7bc71536e5cbf9a9c358c71ee7276d0339d87136a688a21595a9ab131c638632fc8e59e833b7586fb09e53cf9797c267bccf846b0a0c433aa89640322d84c318de5f01d408420a58a27050900e931e29e160eb0852440f2e0dfbc6eeff0fb9187dc73dcbddeb4b7f7b6efb64f4db1b2e4bd8cf6a7d3fbd4adb53a6ded3ded53e3ce4fff62b132b494df6508b33508abe831d163a2c75a942ed5d73b897414a2ae1e3836236da21e1803c18c93e331ee7307e278453658d5caa55efcce73ca4459381ed3590d6d882a1ca03f81220d0a6f809906f428edab0ef41507e617f3c679de7beab74d6fdb96da5e664b6daa6ddb3ec8b66ddbb66ddb96e319d9ba6ddb64b6ef3aaf03c189e319d9c0143cc45ffe21ce851dfdb66f6e0dcc86e99c4c9db75c4dbed7a14a148baf9cbe90ee47a9481ff8c8431c16491dfcda4789fcc96555360633141415a39449f6d0ad514da323dd9af653fad071fac0afbd967a24323bf5be395013b2a54880edfd8e8cf7da93f1bcd17b21cec3bde7b3bd2780f43e88ece9def33ccf7bcff3641ef48c78329ee7698fcd3c967951bfcc8d5764060cad56eb094d4113aebce5091213bef6de7b2f045c509a26fbb95694ac3c011440464b3c26d65a6b9178f295041365021261804c482208ef689a1a3f4b96905ba8c0b0d87105587111490912ca4c7ea680090a98c8518a1f7cedbdf7de8b63df7befd52266b6efbd59b4e82c29a8416ef1824836114608cacb154ecc96f0e0f262975c5faacca62934686826f304370dc99e5cb9f7de19165f49a5a526ed01332f610d1dc0884571c117504d88dc1df67dfd65df961833049530c43873d7f5a2a8cac507345cd5015c185da24a45174552b80294305151f282285230a94c38456152b493f1c516638ccf10a22b99f3cf0d32dae414443023308808dab6018c600c125f9c44b0022074706d3238e9321be221bee00756748318980f485ca1a105dc1623c238d9a10902a8254304c929c080c26310e957d1146ce402261915d43003bef6de7baf962186f6bdf706cd24b0d8333a202bc5ce40492042a889097624c3740415453a218b714eb0b04e8860ad566b0a26da320a588bc849511dfaf071feef903bd18c4411bb4e6890c517638c3186a2096a639cc31014add2345adf5087cea801430e5880d1058a9f31c65011c20816dc07ec300baaa207275c15474f3efc4802821611484cbc10b1c5cf84c2618c1fba128ee0a18624ae1841144ae6932e320419618c711090af649091a021adea7e5250c49210c8926a80a24395a11b4034f9f071fea160cce42e859f31c61896e42b393239d905c062a110205c3c18e1258b2b58680f5d8af0ba94c0a5559a46eba5314362965c308c31660213b431eea1cb82461951b41f262e6ac03180382f78428b2d404c61e50b26921859c0822c323198145d2c90c08c20ca920ce30e328da32516482d58985c18635ce5c3c7f9b7415aba5cb10d6d0da0220811784d00e921a78d68cc9596eb0b580e38074b74022a122a87555e2b4843a9e80730309528f785289632e00a59138ca5d8bb64ee501c7c1e91341fe7c3c7f9af42450bed89b5d63e31f2957c6598465225fd183ee009d34e102fb90aa127202a85dd42d3b8269a2685144d4dbeb811205e55a986a46ee9873a8009d7bdf732f1d68e945454996d8cb355420b0d8c1d722ca850c2894b8a6108638c9d6839c00917187ff2f4848a1374618124c61833f9f0713ea84b0a0aa32219c688fa82a50c5c44654c92a6699af653e42b7964e50a16a5127e9670184230f1e104569c0cc1840c430a1745d65abb84155fc92b198b56522dbd91259ac29d2ad689fe9a74efbd5a64f0b2ef3dc2044a206901c58c9f228c83982c9a946c0892df5a6bc388f94acaa08a84316bb98a4009e12064c87eb44ad3688d6fd0966d015ddc34848238e8d1c245d40ba2a2c002862e36fc28a1699aa66521e42b4964f482212519c9624ceac249ebe2cb931fe20e5cc64c7162cc099e98627471e24a54f5408c28ad20664c5c60a96734a518d074efbdef23048d9a664ad012941d6ed0678c3176f94a0a11d9225b18fd1845c8b0a488a84b1337bc649210d22a4da3f505bf20995165a8082943d8862dc61a59b640810e5f77982e8b2f166ab55a31608c31aeb131c618bbb6dc2283d0c6d8b593c060a225017df83817e3ba25cb0d395ca37ad444ca3d62ce643269c8702706891d8b2fc618638c316e32c2cac6d80a4d0ba4705ca0c829622fbc6028081fc4bc8892b840c92243030e5aa4f03065054a44f105d7d02445152f0a0d5cb23c63a4186717bcc08698983fc6788c2669204dbc61fb846b63998d31187a581b20c62593f029484c1c146f26c5698b202fb27851a362c0d7de7bef55c0102d4dd3d2c3089a3811c2e8c7059806241f2a52102e269911cc7849914c235dc96cbb9c819dcc2636bc144394b404132a4ba4ac00cb11627cc1642597a525268c317e828bf6908d1248100514315aa0d81002a3218c5a60b92c1947284a10dba161009391d5b20570400206298a20818bc98aa19264c91353d3981fbe98155130b6502e387cf838ff4d59c420858849c489549c1882660b4b2d529c5ab70a30568c2a3b425b6a88a2822f4870c49319943c29f1a24a9221588b0a0e60ca408a61280bce0b4d0372460f49d0e0c44a165e10c153367e5db497e8a00417b2041e8ceebd178acc86dc321446b68778b652f45333a8035e18b5ec4d4fb49c8a1f2daec75c98e84ae6265aa569b4963286a6699a56e42b7964e50a16a5a5052cd19414dbf7feb8039a8062b2538788f94a0e21cb4340e921662a225a4f842bf42efb4ef184114245f98bcc842f505a0b0b70d0863e7c9c7f210fdf7bef76b7eb5234dd1054a150b282a12a6ff1c518638ca950a2b531c63570d1fd6895a6d11a1301652230553af8c08f1645428c71c4c513938c222b5eb884a317141c143f0049e1d2c36c630c055f7b535130ba925bbb4b2d8021480a1aed892c964cd932cbe2880722a2789112810a2334ae931ff287f7091a829aa2e81283c18910d5034baf2d34887105891db00c398c51daa116162be2e1c3c7f9bf181812a30727c0b852051522b7a6b12f0e44eec5185b0d4fa182102c696202511469e143154fb4cc10021aa6e4f0650fedc841e34806335e512ef8c20823820033b524b19480a21ec000d3da320b8356d1524516a9a0d1e28b855aad56138c31c63936c618e32d474c1be36cc7259a949d291cb64f348d8a29464f90f8ac598c313ec3e52b299489f403ce3052bd6be609eed2e2a29950303ea93c542e4442331d232ccab862044c284144858b0e2e4e8a1aa880e2046bb55a4fae6482b2e2c485c9101eea92a4699a06f395444a1a999c620f1843e62bb9c52049617297178a6820818c66354dd3b4fb9ab7354dd3b42366be922d971091d16d00122f8ac3fdd162888240f518638c957c2597624dbe9256ab2110415aa569b4c68106dc84039525fa6b0c3a1830851394fd7b31de72451a1bdf8dc128c4c4ec96518a14a37ad444886dcb134948f91d7c826af1c594a9d56a51c118638c319eb22d9bc288da188b2562d8b62d4cd0821ae38b26a204e1189ad208639bc64499819e41b110143f685439da4952e4840f3f28e170839527a6174c70d102a858abd58a6225153324410521021fa298587c31c61863df18638cb36c1ac18f56691a4d0375103e4041b1a40b204d9f08f239676cadcdf9adb5d6da28a85c42d4ace51222ca259061a47d9a267fce5a105cd62a1a560933d8943a672e64bc76cef96d9ca7dbf91f27eb336625a0d17a2c72b6399909d56ab5a4dc2bb534d1b4ef6c37b9768d2c6440fd7815a3195e4489bd60c51437c81dc408e386fb2f7b780e60c6921b6078418b491573563486071b3235b2e4a22b99996427185ba9513c11e48442b9f77eb7afdf7b6b2d4307269e68594195344a40e46229fb76a1b20315306b5dc539bfdbb8fe9481914559ed7b1cbff2c5108d0e3f186f50687124b3c86634bc38bf7d7db981bf2ffe8ca1f8e2858d9f461546c9c66fe3aca0c5a864cc184a7b9aa1a90100000000c3160000380c0a064442599ea6499cf5f614800e6596526c50369d86b224485214c510a3000000000000208800829073b4a3001789589a955a9b67b75d52f2f90cb9b4157bcdd2c16ce3e00a990a31292226c739bbbd9835c20adcc6afa81012339a8037382fdd96c7f08b065c974661e27fbe4b6b2600cee951ed6d1c1c96205aba8556c83643ff8511e10c604d7a368661f77c56de01b4617dc87331da1eb9a09829d58134a28a237f74e4ca32aae8855cf3505a7cdce072f6b98838a5d6a375f2bcd63d2c995f60756cdabdf3d30f62ae806482f2e47fd2d0745d633cb442a43114e0ec4bcc81e0860d4f474b978ff8c71f480d3364e88b3bd2e3bab5e32faf69da18b2cec5c44bcde9d49c5e95099b68a5a701e4e6baa1272bbd14fd1e7692dd4b3bc99b8fba5a9c3c8ef5ec7a01abc045be1013800ec5369816f763a72e882d5cfc32bef807ff526d70d39601b99b926465357582a9a4acccd80c1d7c0992d1a68bfeae692239d12300edae46f6ab7ca0bb8d1ee94cc9925dc131abc92cd9dec37e20944b2766e17a123882e69f99e7e34aec1c67275835854084012c2b051d2f3360ef867c98be957e8668364aea0a8a4054494ac64b56fa0f73e6a633ffda7d13a6344c142b028d257aab535d8cb8d4842da4457587a87159ef481344ed96963c519c67e13addafd15397b633a8a08a34b8f0ee2193806ba69aa9f7974550bfd34c867623b14251abaf7165845690ee8d4aafecc1a9c3d4cd2b5efef7c82a8e9adb37d6935613b7d97c1ec4c8d7f0a486aca200fb12e3a03bdf243d0cdf6b57e274dfbed096f8dcbef9150e58292f0d7dbc28fd5854239db3400c10d241b4e16e47881bf28b7815201a36e38c706f89ee09557e7df2edca497860a1da903fe008c478d59a620c59a6ba01f05cdb7e18c0433643bd3feed737260631c9e07d006b5bf63808adbc7b45b7ef99aa0e5083a693e85183120e430db11e3e40907ea4618c467097b8074336ebf41d69d1874b8927312a54df0b10ad126090f2f62175a70ecad31d6ac2c30535e3e16a8ddc8948978012eaa875417b41318f703dd749607b67573e026a9e06665b282c4fcf8e299fcdc5c45993af3108d9f7046db141719d84240fcfdd6f09f3a1a9efe7603bb0744354e9c20019d4e9f5e7bbd596b9fc885e9e5e36f9fc8a9ab77be6c6a20d05a692df0b1253154b6dd6d245696b2afc72accb89cbd07d6ff1f4797a35be9f69ef5debc61b848a4c37341738aa729631219d35f0b839edb3b0d2e07523ea99bcfbd208cffd0ea2b9bbc9596451aa0dfebd42dc888495b4cf1db9a9ad6a0e38963ac28bc74e88861bd68a7ff8fdefaa4de013b5af3d5f2a02ba7e43255b2022065fa05515dbfce106f54db4f28bb7552e39881a4cd65865cf8fb0c5a1b38338633248e159842ba32e12268214f8a898b87fe3865ee5ce60ae80503f560080caf3abfc94ba346cc56d73435b2a66abcc7a44d1a1cead361b9838097d26b0ae0d8c8740f7ee85655c34356d631faccad21663422484ed4a4f5fde07582a2273f61178228dcaaca68055963f8e756ca37296d4b64cd06f6f05d172a7ed8de872a3081d11de94aa91798c980f1db1d6cada53246d0cfb194a6f62a0241bc0ed903f193288c7ca587c734b5be99d57619649cbe0f4c3333ece7b34b58c9b4e27f03f5b974ab95e9c89218d10b1ec4939bd4884953396d851d368ddfb7f13f340bf227dfd1741bb8fe5af4f3ff34e5bc49884ab6eabc5254af84399184029c3e5f463426b2c7c695e5542f3876a1597d79e0ffdff15a0a423cfd07b6eaa2756eb4bff2e7696b6fcd01092b9f5ecfa9e43cfed87ea1b0cacaf0b63910f719b5b5424e60ef026281698ffadf90c95bd336d7509676ee0250887152e543fdf4d0ce9b2835c2d8cd087e5b6e6f6c4aec81e1da1d356a58ede46d3f906226a0e856db6ae22c73f686e3f4bef09d476619aa291edf112a6db9eee92f22dd96cbfd36e35c315f161f0b46d324b0f879bcf98adfd8eafdeedccf22c164249ad8ac917983e181f9f3915ee441bfb6ad680c9b45468589bec364fc56c0eba51a864602bb0cc7161784c4224ae9089b7914eac4ac63d78e58190f1d462b80638e0bda7f4b5021af785c2d7805526776e6f60c979ce3d52ab4432dd815e35e4a0c4f94a25c16741d6911b6896fa3168042e74e8193560fbee3803a5716ef90d40c94fa423e994f9704d4563e9c484af9c5410bd89c4d94e3b1fb3f4e0493d562533e9b8a33f7032a42b646c56afb7b85d5e98267d4435a07e53b1f84a77321bca95925b94017437c005b39a486aa3415ab3194553288c478e017678604c213751d05da19359bc4f7ace368bbda50b60392e5b505e63b35aa39bd0d7fb811f2ee6a0c232cabdff2f178969e5c7fb3bcc63d0a5266cc2af47b925b1fb997088f34e21bfdd7ad8fbe7c0d518a52d68a54158b8e57d8d853c9d7b41062a71f60a10af8796db6cf4f0a7be6c8f36d4c199cdead3c01f747b2f2b830947dd087a8940e63b820f4ce65f5fc8b2e2d0bee4bdf30ab550cee9425a937136018405d5ff245d7ad8c077f4018cddfffab03b49e78750b570a49f50eafc38f9a12d526bcc88b46059a4c234ad28fdc13291b2e6cc106f6b4f5aaab4a50201a6842fb2bb6ded57dd6ee06b5567a8dcec04917816a71c1be608e44bc836ebe3958ce016c3f9857777952765a778f18874585ff4b64e6cd430e319939db0887f30c11ff5528158b08e1792ea4cca196046a792fddd925973b6012e7c3441355007fdabc21890579fc706e18737901ff960b62c22d05b65ccaba1d3d969ead8a4af0cb4bf12d6815562c66d6f3e13f2c54fb4b6ed0eb74c3e5a099abf3f3833b79d176563c9530bb040f2cc04db2c351e6f998d0e69b4396c79639d498a5b2dd351e306d8dc62280b9547fb6615e3cd399f5be9f2f5b9f7dacaa9699a7068a0361a48466a4c4cb17476da2f5f47ea44a089e4849c94e3d0d3597d7afce5193179f4ebbbb45296fb3e1e059b747c7340866b8e310673e1d5fb4ba63b2911115dbfdc595574b2fe99dd5e26680acdceb8e799a95c836ab0d95ad4b2b908c03427d96985045c49a4cab93d0f609765b4210d4aac2933e935c8d6fdefbff8a8f753c03b457ea60173cf748bafc448e1b4ebc7fd64d839dd7e7d5143dd05c1b32ee9e004b6c6aa99d00dab977667a1bbfb1ea856257b7c49ebf00824f1aaaabdf5c31433ff30f503f9635521c3bb9f506bac5431512b0520d378e0aaf4d90fd8022f72d806b13fb283189e59191a3e6c091c75543cd6cc87466b42842c45c1c1e8d0e71e93536a3fdb4d1b8b998dba6df603ce243cf117eeb6817d587d830e262b6721cd541856c8a0347fd4edc940436c308b33634fb4e99ac15d2d5f6931fd3c1c26026f0389838850355c373e04e41188ef2f9f49cbb6951ebca41ed52f8cccb902750158c937424795facc0867281b3268eea29f3f3addad0ebbc8840512141a9eeead73452003ab6759f4d68225221076ec90ec1668c69650907f7fd8ee3af136ebf9e2b6681b7dc42191c1b33dc6e669eaff2a3ed0897a3808ae34541d35f771594ed5b8ee6fb618a21cd6c3ea4006d6783b9f9a58fe76802bc45df94f7f5b16767e193a3a9af43cd6f0968d75e8a554bc605cb00399ff6365158bfead1b489b280ebcd5ed20869684e1de3128354aea070461f9c7c2c9634ce70001e97bce1f355eaf9948ac0330701751cbe76efb18859098b1b79ed39d974a016a58dde34f0e0c39ddf12b8ecbf8d7b40d7697e1a5424b94d7f062f46e184889e7d4be6b0fcf064bceb37c45909a679d3826a0cb8936ac8b26a0e45bfb0a4d22689f3ba237fdc72e41bfa32b6346ee399f6e9bc1350b6a9ab827f80fd3cb6f2004a13f6e02d567ced5c13344306bbdc43686c24447436d390603ebdac40a0a44c8f375e1f30a5d02be287f97b02fddc2aaf64b879c398ed31eac6f900ba8ab2528816cc480fb4799602d3fdd8caad6684898ecd63211cbee13733b51ce5fa9c21b97bcbf4ab59b7497cc855e666d904deab169136a81c1b34a455218a8a74388eaa106eb05050d23cd599c0b1b91e4e39464527a120f8fe91a612f693794c90321131a6bd42e4f0c08955e7f4d9a2db9112b83faa8317cd39fac2cb94910b282f74cb23dce4143925841abad3ca5f1dfc098e2165c3b5abd46d07339e8b1737cd4f7ab84d3250058e04982db947a6231d2ba7698b342020aac7fae284ddbffd43d9e816a87a3de6dcd0aff6ce0db0546392b5fa8b0e9542b6e5fd3c944ef5eb794f6aaa5a3589b13d059de1d2c71b6ac6cf0c0c5d72449d795f81883f62dd32c97bd8ee704a7d4367a014b589bed95bab6110b91a8cf39d23635d994c7c8780437ffbd4de259f678358f3e0e2738adeb753e3396dafd4ae5a3b16f2f30894027dff081cbe640300bf93240ec925b0bf86a6efac326311762adb8167f316cc358293a39821bfe952bbd81b4b6c2dbb51619cd0d6106261c7323a41ca0dd5667c1d9c7f535caab2da27f06787de384f4ecfba96b6fdf15f4ab0e08eac7fa3f6cc713b323c7361bac2e96280bec301cea58518c880e4db1cb61e9ab1488abf04ca7c0920c8aa3bd7984ce1587e6660f2b0dfa42bb0c5fb48eb78037d22bf0aece3dbd3f15c8e073605e04e427f4de09dea961ea93b2a98966c2607436d0a554619bc09bd9de262669ced06e1a2b0cc061ee782dd2e0f851c74df6467246127020c73703a1ea980fef963cbb9048867fd464892f49cec9a1f5ff783117af870030c73c4c37f5d99daae862c3cfa322734c74e760c8ee0c65b4ea7fa5e74934166754317c63ea0746c6d434b585eebf15dc4697ed53f178dbdc65a672853f4a6ac12e7745b94e53429f3bfae444d989a450304f4a46ebc9c491fe7c812dd6c941342b3196dc54fdcc9385e2939da9fd842a90cdd0fbd9348eece2503f9592ab468a8f864f6d41ef735e0380ac00e84e11d9ffc7ccbb16b35a83b6cf030a042873c97dea4a22394a1ea657ccff9d20b2515e133c75741df6091256d2c206b8510ac14ede483a066513e26213128eb84d4a4ca4d9442134063bdfeb22e64e774ed8f77b9be1d70e9bf37454037eeb53ad91c6bc1df58342348ce0ded737d76df5a3976edf231b2df75e38dd26e397f2634bd9acec8cad7dfdb84fe4ba971430111f13619fcc9344e55fcd7619dcfb4b427a1da24e10cab86da1785560eb19d8fee95ea9766f3e71c0b87c4b20ecc1f8e572973cb560d5d3920b951670218f17694b82b30506aa73890abcc8b7300384bf6b258932d1f9205c1494e44d981b7922db02a1742397cd3d8f66379bca28a990a2f9e387fb25e16daebe96fed5a79e729851c9eec0ddb79f4b673b6a9f4ad2239a17ffb39897185b4c266d39778f7bfd4d4ca694d5c2c3b4fc2dad44465aea4297525e633446c6a57e5f781983ee1021776ae690926950c4d6095ab7620b78ebc894d4f8a1339f109a79956ea64caa05acfddfd52c5b2af1488ecdcb0b9982297219c97846e9c14f7c6b2d561059da05febffc313935ab09b6e3d3e6fa69bfd06cb36ca4add2df7e66cd8d5bcb0a6f1801d39b5eae9b1bb218558193518f763a38c698a702df231f8ead8bc9d6ac55cb495488b18386c6ae39b58433ab9114de31ff5d1f4b7a727aa9979e19c75de1cd68f2e01e3e4f3f3fcc104e5361fb654c0c2c0aae4c202f308f370da17e580750c075e1c2cbcaf86101a7eeb99380aca3a15c0465b9375b20b912bf46c4ce9dc691f34cb057d28f458be7b3b25956b8b0669d234d2497ed1122ec3cb84aca600a4111ddd0d13073647b901ffa5becb4a8e354435a900668c127176ca3d40301cfbef411a268e7218f7cf23e50af1e0e4e3a2b2c323d3a4312265cae7a124d426a09dfc6099eb50a25099703c7da69b261dea18cbb6455e2a6cb76d283ddaab5328afa244588b52ad98df7ae3200e1558b00b1cc95fe3fd5ea8b94ac8b1f98ddc17581c780394bd45db415e705914c22efecce660f107bdb2ddbe618168781ddd0142890026c3ce6f55278c0c5911d3e6f9a371e5332f3c5d8c707217a9529d742266b51f8cf069f3471b78feb66cd8cd56ad1e78a873e44ebacae533e0ebbdd7e118d785a67286133ca6023264953a19ad6a215bb961b7b1350058e43a35e6b22619893a16e45c8e0575ecc3cdc1aff031900e37c9b8fca8b1265fe15bddfdeaca365ace8cf540189f30de1ac62cf4688007a52a0ca3199eb76b6180ec718053d6cf00b116c20b9203de1ad8cec04d0b8dffd63b28aa32f945bf6703b0140272cf50939a40f93d342d2cf143c3c1d38d573d0599cf963fafafbe1672d7587ab6cd9b7ee641f038dd12cb9b93f94cf7541290d1ddb0258159ec7105697facfea65202212d3f4c6549731712c6186ddd32888562aae5a3aabc151fdb64ca73b84b9180b1a753e9da5ae2eec31adc1440d814ac76b75d0510f7251a633234d0801f812dfb00b73bf1608c6471e0a127edb5fd6cc403d0c30cccb13f2a7918a6428428e11f40c90b3bd7121d717a5032e5791c714fa966e258c99c8c529489ff5606aa729cbd7f865836680a8edcd0ff887766e65ebaf81f215009b466ca1295afd3626468d7bddff5af49e8840da54e7901691308b91e6619afbac60f621595c6c7ac3ec7ddb00343c1e4824653b9c6aab38b30c052236b0a39910517957c391bc801fbbd8505b9294fc0c9682c53537112b056537dbf7bf2e6d752d349a679f0a021585863183dc88d5f02faed84cfeeda1fcbbe2899e2cb38098b5d815d408bb03d3b8b5a6052841f0eac59b9914e783ad42ca8577caed35c3eb485be09b41c43d08fe53acc92b763a1fed1f8a453de2556860c3b521adf769252364068ed6b730789fa7a0a66fdc475a61e1a93c0b8046e4b6bd11aa54726faa50ad02f00936eea76063a81c2a22e2350dea6d6d595fa30916d7f1e80ab7fe82c21336cbc289e3c5e08a6c3f2e0d7426ab58e2b6b953040a60bd8376710b639f5408dcbcdd1ecdb3501a3564ff3beee3a5d7ba0a6758bca2dc81231e0233217306e6283071a93875903b49955ecead296480b233d18fd9e90dc1c06dc621c1e1e37c8f6d7bea717f5a48cebeec09a7cf974ad3eebdf259f9731ffcfc732855afe4217b9190d7473185ec11bcb2e56a42cf4d57932b7a5fbe4fe0900e4e1c0afd7ddd3886e4564d801d9bc023ac63181ee9f913a0c6e013c317474340e8fce98d6e10837a197e636f782fed760c1d21bcbc473f8a18e12243a15aa9bc69124080c97095f74544a121e55fcf38c4de25e97d7f9ac047f3927910d770dcf00d0df614e5d3aa0537dc4b695525805eb702025228c0cadd5f7bec55e0aa4b6b64d4ab14c6e41ed9771f07b6fa135764ab2585988bcee7ff00f6d4451ff7f4ea4578244f5a848d496cd4ee1dc9d14d4b6109be34551b7d781d92058c43b8f700ce53611df569fcc1f615479b6b31dac4a62899897dbd3d066095a32aefaea928bbd9ba5e367406dd8359b9ed0c1a8c15ace934c229b0e8f0281671b316184261549211124506650e851c03036b1ea8c858c428de10ae205115a1cee0a593d4e37ef14eef1582caf7a8a96c52291772ba28a51b5df6ed897527aa45ee514a6641fd3afeb29a390247d9ff9a3b79599ec9b38301b50ca505f8e5bc9db5a69397e36a663db6524bfe555e9ee726bffce6352a8877ee90a17ed44da4f4028892ddb5b9ac9073635a79c7a04a62ba8d3a196654e07ee37fecd2d94a4bcc10c81923b420d8bb30aa0cab28f384d2e4433214219d123c72474c1e8cf8203d9ae303a22a5d0c7c8a29cccd9e357e8711dce9d01628d59a5f1019a24c518c02865ae754996d5877c66ee5b9b8128314485706ce39a8a59f62198088ff674c9db5304feadefddc6a53ff5753855e1743124bae4215dc1df5786896e80330cd8205ba3921a1f7800b16049dd546ba937b7b1b06e0d92f4e496c2e5cfe16eb2de57032cdaa0c92840d04e98c5c58a05821d4d29b6370000bc76c7bc9a303d4aa336b6798b0dd11480d6916f8c164502c38a598bec0a4560a6b7ed4dd5fe37343951320dced3e2af9c44665ab358d9dd4466d9abc90e06b32119e3ef818a82536be931c2c1bee502b4ec8d0293c78c5e78707d370b2495f52c60149a5de65c05897e1db3aa52fae888a47bea337539644803957c5ecc7969473270fe7ca7022678b75e6447e7753b296166e8c0c0288aa5dcbae9718ef1003f5b850bc0e09af26ace3b98d6c94e5e6c3b31724f87091d8f9ff2d1892afdcf6f28f44b0a4fb1cb96e8f9671c840c20a00eabf249c1b2e4719f03d3ff9216616e617eee4eeea27dad466666b181fbd87a26a0b9acfb943b803a80ca30125506721cabfe288e8defa16688228a8e977273afbeb419d400ce206614c5052b257976b522809d31aad04771a44ef1235c3eb4022135ba7393fb944ee74f767f899b392605a93a7b2c1d6ed32ab85699afa22194e6c840b680acd09170a72357f7716cd9ea73dfe565a6146de9384834c24d60a176df7ff0e7b1fd2a2d7272a7bbb47936f108cf377e0c157eb8ae1a27278d613ae701dca901e1a567bbf152845744d0c5df4e370013e3dff3c91aa8d91088d25a3adff19d341e0423d33117bacd8ac2d3b7c5ec8576c8e6008227ca1bc9c645bfb190970d69b54b11993041dfefeaaba69ef6f988e71f4512502626b07201d364c84d56c90fc3f619a9b24b00f2d19eef175fb6c7c9db3703019e000c10c5ea0be93c3699fc5b94a283ba89e13f2b5a8afb2a3af5853d5070ffe843a1e9caabd8f2e3509da9316711ce0d398dcc19c3ad271ad5cc3475b5c11107f739ef604550391d957d662fe4a50e34726a73fbe47120a1e247b545e8def59e39976046985750e79abf9b4c5477cd508286e1011c309539850ba6c9520bc109b265b6302a0b170a0bc35f4f59ac0244cd82370abafd888d4ec14f56e502d53ef24c03f30793c010daaada864dc5aa204bd4a30b7b243ff1152533a945bc4836f89cf813e10fef31c07b5b7d0dacd43ccbea61880c8bc35c5f0b4f5e59630a8f1aeb822febd9320285119d72f15ffbaeb11a4324331e2ce0a9347af1e9101209f680e00fabaa0b4645a4e04ceab5e373e54acaff45d57717d929c69cc0c14fa55abbbe0312b563e829e464aaf3d6635ca1cd3b2d2de4a39e79efe3f55b847fd947c36a13447afd162213f1edc32e61caed08ba90a20b5e6da074b74a2621a0eb29e6541db16ab48534b772cad16738898bdfd3e842b890a6369c79b399f730a6507b4087d1c5eaffa76078fa59623b9aa813db321c290032deab6718561743f2eeb1853ed0315344a00ab4a423c8cecd8d146b1535ed25c0a9ac2d15cc31142e7ed4cb4151a6dee7a89c36cdc3dda901a771b5d23654f2fb767d2819d72d20c37795e493fbef4f9add9c6aaaf9103a6c179633a0c10d7b176b265ad039a53e0e4cc37057a2aec74f6061e2b3ec963f18aa9b0c74327c9c80a1afbf5cc133c2e890ade8ec574fe1001752d77b49e83ae1982e88f0516961b742e0d5044a4aa62db5eee43b47da06be9839efbb397d0bdd8a174e4ddd364e5a42d54b15dab354199c7058f06817686f30a907385fbb0ae55d72e44564ae994bf3d9782d1483d2180633772d4bc2d742488b5eaaa7fba0027c3460ad5b487be964988395d633cc33a7f3f94eeefb22db89413618f0502b875abe51a563b8950aefb3f57c4bd270ef316d762b7d7ab329e93a19a5dfd506ec63d200ae5a63d6e02d985611481c64ba2613238deddfe28f1eb0b2956934395b06c5a61c1bdfeb857736a3fefab01756d581a4cd30b136f763f6cdf2569456f4e04439f20fe90c1485031132a9552fcad82a3899445923a5edee0307867e96ec2e7541d3b5897b0de141b01703a312e0c2a543d9e50df7434fde4857117dd54a436845d57a7ead81f89e54ed113903277db3bdc2a233deda93ad255f118748b2ff4c9b8e190aeec224a0b0ec9c8e3aca01beca1f3435584fcb9dc421c808410f17601c3c4622cc40e481dcbedd3a18f464794159791bfd98168fcf5e7c9a2fe2a88cd955f853d0a690e4b53095c1b83fe1f72d918431b218f9f94f900bc624bf65f6e2a5180f95bc259802aabbf352c83a2a2cf586b3d4cd2a5282cbc2c96aa5f402954d7db7224c611a442429c39d33b0b6c4f57f077afb388b46d666d8a9290cddf9df890856b65c71ef967e03d316ba7301edeb98276a745b202e45e1eecc6ca12e68804626431a4de55328eae00dc317887a8d28a234beb74a7cd19650fd06da3ae4d006c0e850f09eec6d2d5549e46f0180a21e6ce0c8d997e327787723caf2e3e346b23a9d309f0f16e370ff47e43580429d0888ba247ea44a034f1a7a9588982170130c0c03edc250f928f669734fb7c1e90bebab943662ee26c2344357632e7aacd0cfa5d47c512edbdfb70cdd173aac1e70427c81f74970889e79110b91d80c3d6440f49bae8b98afa20a27ca8fefa46baa837ff42a94f952f15342798e8ff3f9fc773899dc29a00336144d71bf7854ba10142ee4060303af38e77a3e5d3b0832ee792228e2c707b03edb572382678c336979426834a5d1b55298f91296e9cdec3e6cefecd6c0f43d9308b54ab9c5719fc0d9062eca721844d4f6eb6fea7e959d720a06d48100c4620a3274615178b1b4a4d7b53d713f0139b51a02cb46c1488b587051cbcd70cad1674047382e9234828ff8b460d5e15e194498abe6e51143f6516eb11859e6c8e8e8dbee3c976ec28e408d81c9a5d390a75e5a5e2b06fbfee77d4d380ece5015ff9e0d4b9154d3160f627fe2bd847fd1298ec42eefb6d029e91cd7f2060f08270a56b6c04bcac807ea68e3537b2c4d0f1d4ae41d0370089554c840b586e59cadc9594dfe893119d0acaf1bfc1f65b83392ccdf8f1b847644c04b4d6ad7fd5d4ce17989dac28253b6e2f3232883f2d8ccc30581a68a5c21c1b10bb1c93a8936a55fbea790fb8aa7a1b8a5f31c1e39e040d2c5b457b67c3bf7e4870f4677f4a19c58fdd3a6c82da9929b4ad5484d34d8219db0c9a4cbf5a606232c40a137fb50e0c2c85488b1430c5b39f72674c12276e356a270cdfbec9fa705fa874653a611ed8e245c7ccf69f9d1454faa1c3dd1d6a94397de4c79d2f0902f3a5262a16274edffcc87b631b3948cd4cc2947542cae2104d86ac5f896115c782a00ee4a89177da304ea6ee50ba0aad21c80c5c34f787ca23e1003fba884d2625b3a4bee767a0f10b2747ca10dc81777d797312079718ec227476d6550449152b3daa840d252f6f5b10e82f25bc9c17ed61e1bce605eb46ce8758abe9f0dd3911b66b212f1390d0506d2c11857bab27525b5272a5a4b95a0357ae5f0d2c4a8350b2e8042d740519fbc09de395c010ee6b4e22755e694ad5df71b09b033b765aa3b22d527eecec48cfb47e39097770a09141e36e45d12736344e32ad630361134448d3b126e990337d485f2279f0fca461c0fad128f530fc15f8b923ce006407785fdfe2a06d5f4194e016d6f3c57cd5ac661ec10a9a6a8f7ed9f69272d136376b182fdf60abc24f497116f4d572a1b636f9eb7e026ef184484e35a07eab254173a83fbe147312c6106a646d9ece9d448e440045e1def6f90b74466721029b409240a849e98ae9646342e3ad809641e74ff6c92eab02023255043d1f0e0e67168acb766272c1506bc14e2164b4ca7101ca09d33b6c3361ce6c2592526bd50990ee93235853096f573040e6daa11fe543f02558e453fee17af8c669890778e7b2c887b3ca9af3f419347050bdd07088063703ba12c100239ed2e9d56c1ee7d00a832a0b5713e5c0f2f45fd227757aafdd15938fde3bc5b417880b7e49c89eeb35a46a68d94468f7249077eb55d1b9a39db78d86de774aed3e3e7b574e61363daffb5bbfcc7dbb1f049b3aa9e1949dd566bf9d31c9ebfecf8b1c2fd5a66253f62d554c874cddf04a4e879f30f0428b6b9990b36d68f1314dbf3d1070c1ea71745330e60fbeb19ce7f9583de72b7cbb76d79b00991072505162a4c4eb6d70444ca030b88416de653057f9f1c6e1df800e13ef3d456c6ce9eaf825faaad51d7031f2a06f51d23f475e89a6621ed9c2d6a9c38998ac10d1fe344e90a106e41843d1176883a62d35dd119102fc7428eb9baccfe4a786dc0364c5920501a97e176d7316969f876f6739c4cbfab26e1251cf021cb1dc48bfaebc7c2a1661e27221c05315d1f41220f8c2835dd4bcc4b78567bea10c8ba4090021bc650db5232ac295337e7d40d43896e5d733f5fd8d95117c707a8d5c662681f4f3bb9bfc143d06a2d248108fef9506536d4cd3980881ca12035c8604f2f32161027ce85c4f7507f0873e2e7ae53bef9467828504d4e3675b22909502c79bc87394d48cb7bb209d60dfa89234ebd8eb3772631f80f01c85790db234ef58808313f398ae307c6f045a3f1ebab74bc9d09339e7770b569e744937bc2763cc4d6636d3bc682573ef2b996d96fd699dc7cda1c7c6ee590c6366666636cca774c5156e6813420e5816906a7a41701fa0dca1419b42d543b2695d649ff95f67ba61807f370200635939b47dd3c23f4ce54b1e5c2e155e9496418e240f31c7b2462efbb7579e897432e4a1664e357374a246216ab4426f97827616306dff2c7153692b3a8f433e40dd47ac317836e98ab48158b0daf241273c5b1998eff9c2728ba6575a8de0d4e5506ed77fa35c0cb61e4f899c3888eb247b615ef28ad104820a97474f8d5b2c430c2560f68b9e27b8411fe0fb85496d3c5b508c7828d3c3a536412607372ee4ff630671c31047b250640e72ed825d012aacabc7950998774a54156f78dafe42781fb9a8ba666fb8c4ae8a4f6de0e43508321dbb0e0a243669528246c8cfe34f41c9e1f11295c46ed444e1825b8a076b0ef8376b6d321ae0bae95d83ee51f95336304fe008fd93f82b58b36cbd5f822dff0c78d63cf1eb2d40e9d4aa7f365aa688f6072bb28f2643fa25816012b3d328a1f24e638e4c016f43e31856744d88586d60ab2c6072f897fdc64f9f3bb1850bf400f4a378dc6bc46833dc661b1e586a0a869c826b54d5dfb1d7c2c9f2a21bdc44792ae0dbfd8ac8095f69b919d4f58060a2e88f988ab14871c0cd63f0efd42403d717cdac51f26443a69e1933386744ec296d79562831dddbcfa541b5726ce8eb1b64cf3ad9b35cce735e90ee36865fd75c97e4abe6aec350fd9e043835ab4b026857d392d8b09cb41d5a4cc9a35346f52abd1d989a9bb0d62829e6cc4ae43ad77095de51a73fdb46afae55203c86bc182cf1e1f22981e93851ee4e88e1b9a7ca2ca8d1faf6a9576bf6547b6e63d40f188b1c52c48ac51d7c215ba3fc77c5070790c41a409a2541979493c9320b851a44767df89a11912e661a85acc95f226283ae522b6ae63592fb868b6b74a622d80e80e96d4d9663eb78ed2a40b2d824a4acd9540db9a81c3831b0a06aaef679994d5f2e10c4d71264b7d41e3d9c67035789034f91c479d5410df09ac25deab0238987124a67770e26ce4a54e42352334fb10c9ccf3a7d0b00c34f3a2e75fcf1e00e52678fc3210c6228fb8e510a0318075a6c0082fa8b8ce72ed2a54fd8904734f199c77ec18d217a34d1a225c2e8c682a60599f0aa0af2649974306992da04e251559a4e7382f1fd3d8a4ac6370e46c20abaf6099d2d84016e9f2e7ad6b5cff332fbf7abeed81798cfefdf44f2cc700e6f62ccd5a2006922ff8461dd0d892630f3709aae03f3b36daa95e53b4b196ceb21ad33f81ef200c2e57b83664e44279bc9f4fdfeb5e6fc92f408298ebfc144551ef4304019ba7b583111f9c30ec66082de8bc081b90ac28d273857f285e61aa311ee13b676c4961a28d3f102b41be004d188ebbd6ff37feafa90bec272ceb2e1ec33a0099770c744416d9e5341c1ccfc1a588b6a8ed54fa65e78466ec6995df8db07a0d1e44a801adf1a85fb14b19d30befa0cd0bda14357ce81536c29445604f3775831e43504717de0ae52972832c8820d74611b1a2bf0ea6483dcad463f2d0adb56d6a45b6c4281e32c715e9709edbfa7089629dc2dcf70b4949632ef32dd77f1245a60c9c48b161fad4c855ede1c7ac0604324da651c362a776bde28fabcdbd30c5108855420f776d40b10c9d5dae25c1ba3889f8cdcd54dac626df8064bdf56e765519d82faf2bd9124f804a58b2fb0fec80bda3bc0ec798848dae2621a1339a1bc2802a628f5259cf00ac225f6ab086b6786278532562970502f380d49b3954da170581d96b3bd1f79d34afe3886eb49d9ff2287f1b5f94691089d46fa9587799e8718ee1789515ea918f42f8b11bbed6a10451ebda1d12be87cc40b3582971c9596df7827b1a32fd45f3d479967ddc34d3370f8e12b0c43f5e6d49a87b2596c20a6f47a4612bee31ac43da2f96ec073ac1da3bc56b731fd774d3f822008ed3969df670b48d7223108c3451d193ff1dfc9de0cb000642a8f188b5550118ea17c95b21f68507e705d6d125a078929fbccef65f63d16e3516e1cbc4e80aa894dd00c2afd388199ab9de1d1e7352d889c8b6d3a637453d0ed844ee3a01ba52fa16a147783217a2029cbe4680d14fa0b5ed0d8058073c18ae9851a603d9fd02b8912ccee92526e75f0c42d89309de25c7a3dcf6c3801de6550e908c58537a9e41864310661c61e94672375efcee09389dcdc5f66717c86c744ee8d50940f54c85f2fc9a8ee2824d22b362c507ca4e9b7dbaeb0c80864c58a0d3358310b769153eb580d47913127db0287d75c1e84dda7f1e8adb3c457e010231e23be631a2b806724ca20ed9581920fd857b32ecaeb0a1006774e1816b983bbb29cd0351c6d1373aa10e8923ae9fc94ed108a52fabc4b0c5f1544c59c2e0d4447e50e5db7d8d07024d204d597e0e7ff9a4fc7a9e5b64c171939c21005819fd344ed80529addbcd97083213b8783a0d35328a51954c085bd1fb7416e21e4ec2218a2423634bdcd969cf5a593beb36ce4cb7b0354c6c42d0e4a870332fb0a4b6da29f5fc4bb5ff2a6c355ba6825addfeb60bc0c287e97e6c951cb19be1371a419e5bac0b9bfc3f28690afc62aefd0e9f18b04dfad528291efee35fb8b4c66fb9bf954b9118f2ce1e1276385855ae0a2267e9f8e665b2c6cee23a5d7b4b9a7d82409c1b1cd2fb1af8f0ca03442e0761c8f4e3ea25e86a46174ee1ebbe84613534f50e4e1ad2b01645d9122082cf84d589198c717c228fff447c95be7b5e558a85e52a2e94c55eea20ead7fb64e565d32b50b7c110bf4b8505bc2f1e07d76b363472db05391359fb902cb97dc265c6cd52727704e6134297bcd346f58d4218dda55f9da213796d347867a569c5105a40ec6f7ccf046aee230a361082874e115264bc25a270cfde7a198052a3c24a6e8e4f40cdfa66e9cc0abc9410fe492b4cf60af66437d5897896d8e5592e86cfc651ba058f8c87d90d4cbf6270f1e46aeeb1c4a3e77847b169b27c339a87b91d66507156f3e87a6331ad5e4e22bc89e3b6416739dff4813680a8d0fd9bf9ff9b52191dd985754f271d4cd9f21becb512408d8531304d380c80da54ab02291326d27c6defc6aa9ed1cbdc51c05cce30238fe112e2f3cc326877d6aeb472b3bea01b9909edf4fc2c16a54d9bde746590652bc643185dc07f3f15347bb2acda8dfe1cae8b9c914d4a3c8e767840c7ffbf0381df9c55ede8ab9298aa18428d5feb46410f388b846ab63da3c946309471f5b0a982686a5988502d6e8f1dc74c08e7ff1bd320bb46e1780776109c2208eb1417dffb95a487597740497726180bbda7dbb831979ea8a51b44d70800022a8b4223fc942327e7c5754739388a7f76d4c397805dd72887c8738756ad7ee74939376a8d49d7cacf858bf0a08a5aa64ca0caf348114b309066c67a6868255465d31554efb2bc42748dd9e69484573a032d9a0c880a90e3e12ad2dcef8ad970d8b04e3eb8bd48e347a658a892c73dca1c9748488ad029173fb7d58b753ba7dc33005f4e8d3e5f3e9e442da81be733bebc9733b95d55ece240f71e891c79a64bb23a8647f0778a845858ff85ea61aa4a926cd5bbbb9d924e76017843c0e35186f0aeb8ae12098c4caf06b2a5331d83006b47d39f6bb84f5add049e7b46c744ead670ffd58a2ccf22a618671783c387bb810662f1957d4e079e2de68f1c421a6ee779019008a2e39814b144a6578d579e121819799201ae4679499b01213c686037a6c28c35d5a1b686f1a623c89191a19358ec71bef62f6a04c4fb52b9e1399aeca00d993a5677e3d460200cc2372ef0dfb911543e44165453f1f23f6b787dd77772c9734a80443aee6a300da711e271c9d70960df3ba5f5817321da3d434d6c1313365f709340946ebd6b458a061b6eb73eb91459b00ffdc300bcbca89a16068d22d3e910624543c395fa9b28416518f4acb060ecf8cda51427c5f15b8a63f1c0262cee1e8a7ed95d7264b5f43170ada9ef062fa6a3f3bbe428ac9b42bab5bb8c6fdff66310611422fb262f6c5e1c9bea9a2b3a45c6b1dd8ebaab36ccca1a99d75f82fe1d1bce7c1c283ccc3db84842ccaa34245d895d7f67fb379cfd6613226ee557a368cd37974f8eed68e8be29321ec737d5b524c1e73a25f64f1589679b2ad1318d6dd2d825d27e5abf1dfb89b8587bc288d5015315f5a54e28041e73749d522a92db017d75e1475979b91da947d821b21723f3db9295671fae647e0309611ebea3620bb31d7f0aebd4655df43ac39515daa521dc14f926a59a5177e338b6f2a38a4459581af11b73f58b9cb2e7b05797343ed813bab7d56b4576d41f3bde6e27f6b9ebcbad079f650b66dda0518f4b2c86db180d81e8b5eaac61da76509846def8b79d8b64b12a4e735c16af521cec3ad27b15cf44e82c46012ead88bf4485c5b60fa94110f14b54f12c21c3ad3e7114782a589e79025562d2ef3a5d33cdaf40ced8c094d900824663e18ae84263c07840536c220a17d5c43a6e85fd2cc1462bf1bc690f48c273bb01b4505a8da50464523e594c34734edd07e31e8f93163955c7ca056a1f223d174272c59725bf2a6ca33084abbb765355a19f9839c3d6d87f7ff2acc55a79552b52211d1acc5017429875516336307e3fa8fbbe2cee7a817f2c1aae411eeef09ac0a4ef27b4ffa4cf3a58ddf9037fe984af5d78ada657a779e3eb3ee20aa910e1ab38681b03e1ec76e7613af7ef9ba47350265158fb494687f2cc55bb6a2d67e0b53a334bf585243e13af5c2452c5e0d2eb6aea38cd91c4275ac567f8ac729d47c61335cdb6bbc4317e9679436e5bba709654e49d96763063ffc58282a4fe1883e4a7046913bd508344e2e4a2bf6f6b62f6ab44c099d8948988d3c214179389f9bf2d43a42c5ac20ebba0d56865a292f1ff3c93d178df2ad6b311021af1c2d871aeaf2ea40b1768c0ff3cab3da038b08a45e1019ff77699096470c044a48d727bcb7182193401574558fe58b61b56c7ba3585a3f4400c4720206ab3b0570e881637a0ecb05885458c5cfd7929e13ca5a14951b18982e92945e1579a6ae36884547c38e0ce353195ec03e374e07f65dbde85b2e8f174a0603a579e2132496c52466d1125b30e68afe2d96b546135cfedb8adf98e8e882f2d5245939ca446df3985394ef080dc324ab9eb061d30873734f572e922d3abb2faa370f4fa27d59b1362fe09a9494d1c1f015120e0219a610c8a582b160178c142eda3d49e2f3e44eda428ffdb546b7af3b13aae2e80822c3752c041a3932aa0bf2e94492939a6cf95961328a6450beae94aa1bf0b5effb754c98cc1cf492edca4859c5f4b26fea5cabc9692c8dab6308091691426542c378737db809e28da8cc34a699b75427a76e2f2e6493c1958a35fb75665b9efa57297b107468c987c12031b34fd6b47084b59f553e9833bd42c957faa1125fd1981e644fb2e738d30dad10c473949dcbfe1d3855c3f0a32a2268b990c7443ce0c3817ffe1d0df8c74428ef154f0f904af5799954bcf60d85137fccf076e31124610f80cfc1131cf42225945f469c878573509c0702920cbd0e1ee56ea69a7d490580e0aee677ef78eb05020282e97c7b2548856e825c67f0c35fb6185c23da3777acc48636401519f28c93c93b875e8c09ca28d092315189624c1deebe190746df0fe88082551b308c6303a3417ed682c9f1716b94adc72b80787f0697a4062e6f891b0701a7a1ea5e5bd66c9baeac354de33ac65e0d995e98d91fc98d79c350e29659dd93c7c1ffaa83c0dd5e70312da0137d228abc1f074fdc606caea84071a0d1cb0869d705137135746e68c05260471ee501b31bdb490e6cb7d6a170a87274cf30003d1262642c2372fde55b048486d2dcacaecf8cb280fcb530a3074bfba8b8e823ba41600428cf91605641879aa3d1622b5be95b98751166a27011f6debb0803dda2305dc5c3120986da95839ed014d2fbbbc92466991ab207ffea54f17231cb2fac40d9682c2e1df2f48417e11feb2679f20465b474202cba751a60afe8651da7f84d118e50b4d38f275aecdf2ef7fd89022e3968074dbd00d14e23ac3c533a82c1daeec2ad0a729c0ab65c5c0375c0234fa9d0cfe4b4a89c8e3e20cc522b18c73c56d62638b19cbc7f3a0b5d03b108fd1b912e0c5458b14f3cb6bc49cf6158a035a77cd38af323618f5c08311d77298ea4324f08266a6c561c0a0188136c4f47b16ea3603648432f45764eec0b090b0ba6fe03bdb057e1474e6cb369fa78a2ee484a13aa2bcb88452d7977b4fdca0d28829ed07f09d3177fba494240d11bf0433cbaded6ace02fd2d0e7303f0634a00ed92a6377e48b14a0483d560bb94ab55a4adaf5a34e2338483cb14440e9f8c9b172d52d59f31aee3f7b73f1f714048fe511e496c8550e06a700ade3b983a0b426d5cf4f6e29746893c8717946c88f5249005a271637ead003df1b2cd41f41b08dae54c00d2d4d8f42a3b100aed136485b260844d9927126265f7a1f05d301499df38d4a003d27a22e4ab0f382b559f66a87624ded8aa84e7e623730f26da39f20075f6231852f8f1004b1cc032395bfc362aa9c067209ff305ef02d2c6644579a9664781a903c2a45621d8ed70839ab84f3e9a952293fa5e82915dabf83f07de1ce2652e0b5506cfb20f1d0648ea33180f477c69e451a7b58856312409bea0f6e2274e65dae8420d8bdc060adceb745251edc9fa068ae23006276eb161b7098a5432fc9d81e81b0ca498b24820e5b53704462a4a655c0146540ca16009db6b9c5af9b52d23b05752d548596c60834878fe92e070fbb5daac7ada19a6564d4fcde160b013f20ab5786d29ccb5d05ec4d1a10368c2ca362b935d5b2aeb19ca10120f34403efbf78b6f425070ecfe16ddbb993dcb5519adc999da7da9a7e3dde8c2cb9dea2fe843377c538dac473e6c06f812ec5314376ce44fd69b872ce849ae27273c392a5965a03674da7580a229b73263d4b7f48bbff652cbb78b6e69cf9e0ccc71363af5a23a084c6effd28e59ed35dd98ecca00e2c3f866a7a692665d3f89899d95e8ec7657a7955f4498dc06e62a4687655cd99c119b468e804b0307ac311e44aa3b007e680098e1f1f24d4c8dd9fafb3676a45212d7986a5e2cd3310c09ec7daaccac3590c8a3677ce9c10e20801d47294dc0d74da03b6fc0bf6bc22e6cf576d1ce6d25c9cff9c2369dcbc99732c72dfbeb7fe361fbfe4b2c4a11f8fc51ff74f4e4cb51c428c0133020a005bc4e94807c27f921a0c8e82b8154fb2df29c56aa6ced8abfa6439b8d02d55d3fb5b4f381eba5f3f536573c7060849301f96ab71dd6cb24f59ee5103ecb2c426803595d3ede6979f9161a9d7c2aee61463857b7f79da891926eda4b73b87d794c9c6be9f449ff19141cf65b29d4e93b39c77cd4358dbc4cf6b4d517cc52b61382f91ab660195aa7a87a4a28926b3237ac437c7b9502c91e17bcde2a847e4d19b0a63f603da66dbd57ac7c11c74ae130db409f89e336cc4d86bfcb63bb3f062b255c54cd7d6356656dbb3c77997c32b36b126343eebcf0ebe2762ddcbfa9ed03166a55144d630970207dbacdc3d072172b9feac02046e8ee44ad24d3caccd7fe2491cf998c16061c78819e45952a040e1d1a17e56e8661cdd292a7b6ca1c27a475d1e0850512388f29063d46ac41f2347ad28ca1f4129104685b2703c07dbe18553a8225655971bf1fa2185b414fd12ab568ea40af9831639ac47532a31c62e9f2c967eeb4290c44de9ceb868fac83a15e4b8e5125163c4bcf6fb7ef57554d65dac7da1c15a03a4a981c476086c201919aadb3931f35609cafd067ca20fbf98879713a0c87fcb299d710060c274cd3da7f0a4f16611cfd6873c429e504474b25c65cee202c2779de260b7c42d202609125f4a9b6ffff35b97f2c3c21fc08ad1523dbdf64bbf65a937ae8729371c039b59a97c0ece0c438c2efecfcfaa941f96e0802a464ab1f4fa931d03ce0c07040e0c03f33da93132116f6ecc5dfb9fdf9a94272ce8802a9414324f47e6c14617fea724e5cad009c310fa03073f5e46c3d218a9c7cd5f5c94adf2d12f26023c70f70131e2c2be3e2a3b8f73a62123ff1f9871f9e7be742747d5f8825d5c29868c6a5745ba28a55d7aba30fe2caab02f4ab7638107e4cd629aca00a87abe0b5b81a86ee00b7f5d577a38b2581b1ef34ab46c47e3c62a4fef2cc02977549bb691b55619ea0d80c6ec46814a159aae992d400ad34ca3f5d719ce9b97f61a0186be9ac02700494bf4e0acb11ab2cb4d833b76060475e49600c3a30df85d221107f8c3ab0e485e02f41907a1be6cc627e90974060c54c7928dc3b5b07c040823864549c52d100acd95263c0f78405389802256be0f66d36d0a78a608d909b4236a5f59ff1382fbd2b7fd5c5bb6d1586dd9399ec46b415f6330b2bd9b83c2ee55dc045759223f394a1974dcca9bf72ce36ab44f7f0671f894c47b836c4819b6fc47916d9d20de9a727d8342682d80d22e2ec2ef56afef612ccd31eaff59cb6c451c5c2985c50e836b65d1e22e05513bcf83be4f9d9cb44fa54859d38007174068f305513f2e00d43cab5abfb108c16e39c41f6cd19d2ac91970867433db8e17be369ee72671a052ae556f8466aae55ae132d5b939b7c2848a6c884fc50cba5ff01a7e942355f825d8340bf000b202c2be17de0cf0bebac8b3f6dedd69c0640a7a8118a823210440739526302cb9c47706f50dce50934dcf4e5cff93c5e8b9043c6d9d9c18ee15e8431b4f12753f2557882fa29fb4e80d72d8db86877e4e17bb135f62fbb3fcf9539b0307ba32d1fdb14d561a464dde926e626261c9c63608303157b68143136213dbb413fb2d347e396f2f048c2be045b059c60dbb255171dce29e764bf8e961d12c4efd4d83365eeee69ca83635d7bdf185ca86af01fe972df4ee0ebf7fe2c84a98cd2d52994131ffbd34b39d8e5beb60312312743946e10dbfbd44c0aec1412fe265fd0a65743c52366c4989bdde2dba576777ce939bd132a6128cba211076a45c070b5f918942b7d5e0b9cfec2695eb6f8ef4cb36f98ffc4faef6a661a64787d9a9f2299d0f4003d375a29d527259a539292337adf7e389415ba9f73135e8416c589ce0477dd9bc5bfa92d3808fb5bcef1284ee3082c53efd4b3bfd2feeb99817637b8857f14a80dab7840ed30ab4584c087887806940d1e3d8e86201e9e97112cc9c7c110ccfda590bd825742733f437b9888f068a70fc23efbdb950930ab81f9992c341c47b73638827e0ee4cece8e3a1ad1a63414e0e854143102ea69dfd75ca1741496ef2bc9408541e1ad6f6e58b93217a3ca8541dbf4585e282b4b277e7154b9b805c43b33bee0468a650914f324feffca65063107d770b5f3e87fc931550b2918af665cc91d180b2f51910b969b2ac50562c16996c9fa0a160c22a19ad734759747afe6cce98b9ce9807b07601fd8fb05c51f4c55f46f2fc31bff122e712e51f4d5e25cf58b52d07e6a9e51cf1c1c28803d4a330bbc883a08d3840fbedbad55a1f0e4a89c25f42cdc8cf3a4a862eda5c0cdea2ba741e00c31f218c94231c64d475ff24038b85a09dbab0692342cb30685a5618a3eb5fa322e613b896aa3b90c9a57ac0ed2adcf4ac64a71132223222e5904a8cb391f1e1f297da3287cc276a054a4fa0cb2e24a1166f2be2ead1d03a55b84e49dd283c387f91c0528166dacc5231263173c48c6ddadc01f0e8aaa79dd00a2ac9c05ba432c2057ca72b9d51fda11038fbaf546d558ef17465bd32fa4dc1484e63006ae48b113c60c6a5f0a440af934349506a7b57e6c002493214713ef762b9b0c4dbe15f63115aaef3af893abb55591063996f41fce58167b74a303b0d5df7780d12d558ddd0ea7696b8bcb509b78e1bd8fa9302a5ba3943fa28d9ed46234e8c621174829e22c835e3fd34515e5e55d2b65a4075235543d58028c4c74a64f28491cb38ee45617fee85984c42776829f537c0be85b234974f6a3e8e7d613c0015b817eabf2ba2fdb93262bcd16f443c0ede0078550b362e849f31c4d7a3fe6420b6182177634c1dd57f3aa998a01651bfe33b0b81fc7a18c48515870c1a1ae88b4158b41b5244f89dd9b5159237a8123be905a36e8b40e5a1705ce35fb74df9f8919e1132b2fcef2458f05171082a0327818affad2fa20d0bce082b0305728a008c03a5990a1ba9b6bcea0792889c1d62c2890f4042330bc71a81c19be930b9faae1356287928f2351867c811ec40c82fcfe44ab8e5f94901edab3889cdeea6c9aeecfd5506af9e849d2f79cc765952fd6cbe79d9588ae3bb33b1ad6df653e0e9086d6a3dcb84fb6f2dc9a8637996684518f43e2672995b6424c0936a95b3e3dde2669353de7a2d140923018a452efc8e2f4c1fb4b6bfbea0150c701788c07600cc1442f73586dda14054a97bc2fa5b881d9014d8aa4971fa487da281e617020c0b68d4e2c48bb381965b63c5e0a695d5c58d0671cc496e430c787104e6fba2fe6f29d34f3b1fde236579f52ff44ce57f7149d8bdb7ed3f7870428ada5055c921dbb3b466adb907ec6a1b6a990aab0d7ac04e6e6ce6df49eabdaf7ca43b7b1913ca94e8dd1b9401205aa90c4b36822b391329320c9539478c1e071d77e8d011071c3becd8a10e1c39e668393038d7adef08be63c71d39748443c71d3be8808372e0d6a1fc3bccf22a388987f307072ede4a5f8e4df8e2cf5ee527baad4a855f9b59b98f7a82d4cf1756ae2955f16756096d056c75e1f012c1cb93e7d6e271044171d4e2a96d09c04545d23084a3152c267480532930ccaeac78766da09b0aa9159733ed4c092e7bc4e6c2054a1d9f2aca981eee39afa134596875af74774bb8d4ce720b594e5a73d6ac07eeca4a08adcd9a61915b49bb949519663cf0165b85b26ad61963ccd6d451d69d31cb8cbbb1325a2bb366bae456d22e656586190fbcc556a1ac9a75c618b33575947567cc32e36eac8cd6caac992eb95525a83c1a7048b33a1bcc50f975c53ea2710484f4fac26b5b69d5943d2a3f105ecc467fb9e913c3669e39254a7be09664257a951e954720ca3c00de277ae9b976411645e567df59ff5acd045eda356aacd8969bedb64b6c9a756dc5b4ee968bb56cd1ac552bd6fa719451d959f12542f0dae8d7a73b4ae59b87b22701e66a075f90242c2ec653ae791a9d224235fa72ca738273ee7261339b12c2afd6d63092bc9806aee44e00356d86b3772f6569b2e05219310f0872115a38928b10d4dae0e17ef053494dc4c5886c94901ec8213a50ba90a0b39afb1aca722edb1c801b9daa513fce47ab708f90c2aed1380f51b362a19c408cef9e9874b45376036ec1a2f11288c07ebde8948b57a4d2de8c5c019e18db290e1cd28e50123eb0fc2364b962b8745071da645ccc002b69a070c48e080d49601f1cefeff5fac933fcef1073cbb7b33b9a77ed0a83d1eaa8b6316622d4bde0cb1f03153adb0b1f4cf6cfc55d3e9c019112e55b38ac7e79ad21a95f930bcb651c0bbb24528c474aeb49eb00b2ec22fa981d676968a07b14c55cb54728d31e6d130142b838df9ffe0773354ea5816e40c96f5bddb6d0eacab6daa465ec3e911d378ef8d33a746ba2439badbf72dc0dfd020e980ef23496df2a0345b899921f73fe8cd7604be6751d89ece9a2f97bc57929c985633c7c3203f1b5fa81a056f8b952d0cfa80fb73c93a50e626335b61c70c40fc813ac5c1b1b8e68552e05e2e974b36c32add18447b2d2e177b1d14f05f3ed31042e8eb8dd186027b4ac3e2e4edf0d7bb2b2b84f45a5b51d9d1b0f0511bdecb33a88fca40ccf9bd601d2772319555e43858ae9794786a41c85f1ace26db60e4a73da45eca6d70ec50188855603818ee005100c3cef92308b8db5b70f11eb1e0f0ca0dc758992a783c8119affe8b87871040649c5902be2e2e4c860ff47b3f0e1e63bd38d9c40720139bcde1ded593f2bff48333edd8895abc437d7825be87b1976c0d15a813d4f22055293035db3e895038c1c31e143361404714eb59e27aa7813d3d2085f61e18c174c2260689f317ab3d912e5f0dda6e9bbd8985d3401a4059e4e5ba0f496c36008773ef3e4d3e2d298947882372b6b5cba7ee2a2709ee1b1a3672fb4a5a1bb124d4240fc64afbaaca3f0365acc1f8b03ffb723ddf4431bf3e7430b97092c2f7dc31876c101f114c3abc90f4d2f6252ce98149ecc5ed8acd140defa71dd636ca1455be13cb5e9cababc4def79878e231e33ff1d7fa33cdf18befab12f3191839a49dc568e4f3883ebf7b90d9b0b1d63a8b95c5b5b20afb6fa3b0cec9e160773cad9030412343bfb00b5ef33206a03e5d03b661e7ed88613431b2040eed0aefc126997e7336ee124fcdc86cd858e31d45caeadd590575bfd5d0b764fcb8839e56c0c02099a9dcdc16e83f6c40b35061946a6290149643b4578388b4ecfcba63701696bd947ea762ee6861bde1a765ad8f5272463887f756cb8b3e67dd97a5715df29a0e0c5d339a223f3d9d26af0b3b6bdfc5cf5be095eba5a84dd27928140ca026cfdb1065040138038fcc890b0cfb159dff8d76385cef2e880b7091d06da2810da3b9d68e30f4be18b8deb236b5d16918c458422d83c68bd878e9410a2fe3a4a76ca4709e7c62febdd8608748864c5e6a8c87f49e25193061881e7701527d99d4083633f1749b4a6d86baa84ce8bb79a48577b58b8f866b283c566230a3840b26a7150ace4181b8320e06c07bfc1b181cc8439aa33ab0cb9a890238d60a96bab9e0a1d157a152a09f17e4ea331a96ed1e031046450025809b16a26ec3c780a4eb9485aee49549e7a9d2bc505cd06de08b3dd0ee7d9a474bbead9791b69b68b64d0a23dde3b17d9a16bacf56ba96d857b561310c53ac43dae331958f2b2476ba0e0428ce6f3417c19b8258a512175b58fe9c75f79909bbd4ecbf0033479461f63dd19fe943dc395a0d6e5ca99c2b98f5ce3ce4f9f3a60da3ac0696bc84f04634c159501c5515f4eb2b7ec8b0e3091a739a0b8259673959de6cced29d11c561575e34588dba84c7a76665f90decdbc1f9d8d841705ff4a688e0d231ad4ecd6a1e8466744b1b4240a1c2f1fc4cb86e19a0ee2943d448a0a297ea7b27a9e96da16edb608246a4d5d3c1fc8a7a5349aa46163423eb0e15fa8554454d60846a3cf767a340fcd2f0b35b456c44282894150461685fea1cef0ea0f316fdadb6373b7e882e835b7025a236544b84e753d6a8c1abf1a60940dbf970be16f8cf7b5a67139d8adec08bf3dc1d4afdcce654b21a3ddc8fc4a9741cf9c29322507b176991554ef53a71ba7c6068f1acad74da87a6a47856174631ed7e5a20bb122055ba38eaf43fd69856bbe98d2fba8cb8cf45e649a08c3f767101cc014c46aceb07561c4c04a67a4017995aba4c3790325af2b52cddb4fd990aed8464981ccbe9c4483ffc9f121c579047ae82c9282ab67f83394ca0b2b013c5d010af4dc355770ce61750acc7240ae50e5c6eed0e64552951712ddec19e28f9907ee30612477136af386f61118d7dfb570de7c4c681f1a17cbcbe442e5640ae7627934cce10ecae6a60ee50db66f3adb8ae077af16e826774a726fff1d310353034603bbd91abffc38f9013491ad3faddd27ebb42e0a815ab25104e6c8c69b1841e3436badaf84d45b4a84c494f20daea8dca3b05a62389b43522d3ea7e8c72edbeceeee454bf596369d4f280845d13751c939e77c216eb5eb2cdcbae49c732e06d55b3a241a8fc8f7504cfeeb160114632a525e623a322358a5206f51e99218d7dbbfd8b1ddb54dab893df7fe5ddbb45a4b654282836539f671ec9680792cbb1a42d13ccf6ac2076868beef8e919022393c2e4f366b46f1e60d14026e7c26b97d77f7a757bda5c121d108e2e9f84074adebb60994b4ee09aead75dbaf623173aa4ac22c2feb68df5f4d0a526fa9101152fe208959b2b4c64d388e3c5e3d40215b8df242415b3846476f06a7d06050573713ac8cdad0ca2006f3ffbf4b918434501865e6bddb3e0b93c403bb9510dd21c32b47a7a9882964f4a5e4a4fa78d7366def7ffd9b2ddf61fcd4bebfceffff1fa0cd9cdb2521c396bd01f5c13313beaa6214a198f8ca5131817ccfeeee6e347c71eebcb603b1a123693161a59c734671aab7f409fd0f28d1dc1bfc0f29c3e4c7bc20cbf250396041e15ddbb4bd8bf07b767777a42c249c7b4fe7eebe33a1213a24472867e3c50b628e0b0a510dbe6727e672391c77777777f7202e45a2f1c4b907714076e7f9406f79efd96b3877770722aab7d4a839001db5f6271c9c3bace52ac6d58f7a25963688745f6cd88d301d399ad1e5ee633f50398ee4cca880445afa1191437d91c15ddbb4bd995eb6e9459a59bbd62517311185a42a2c9f2a1f8f9755c60739b2e433f4734b5c9beb2ae97a9a58aca666347d718506a31f2490e5a91f8447166fd9658097845112381fedd004dd4d582da26b83dd12d7e6ba0e5cd15a6b2049f5962a974de7f30408d42b70cf2f7067edc0427304deb54ddbfb573282918f530b41bc1c434b30a6b2dcfcfeffffcc1d1edc7fd631710861884168bdce05efdaa6edbdc232a237e3e07b76b5c5dddddddd3d884b959ac4e02f75bab2345592daad2e40b28ebc5ddbb49deeffbc5c89b95c4e664d9734904b66d48b5fce90231f4515d8e8124b2fbb62964400843a939503ab36b304cd8a22b49195861d58402c79027ee1220691e97892e1f3b58b8699392504b24d5b62ef481ccdf07bee6469ebdaa22accc2ad8fdd1a6e11b78c5bc72de456d24ddc8133eeee24c434ce4ee3c01a27d6383425232e30d6955b3ac4c6c7552583a9b5d33484daf9704319c673e7fdb825aecd759943ddcb4fadb5f6edd45b0ac734451e10bea09c73ce334cf5964a5165759dcc889dd0ec5ae5114f1844e697ecc17a1159660c3073d0d3c00a9c94183c3830ef4e366ab1b5c23291c31eafdfb3bbbb3ff5732765815dd05299624083d05411244c4d5490cce869986293dfffffff6f2cc5b890915b41d972c29b8246997fe55aa429168b3cc5a0a25091a88854646a560928eb55aa9135dbd1b0f1003272411f71244e73ec4312791ec1c4258466bc51878ff7eceeee4c285e9c7bf03dbbbbbbd18c13e7ce33519fef7a4bdb15b7c4b5b9ee9531c32c1c81abec98e80549015271e532637be0947307823005757757712084ef7d72090af311c3062a2689e297fdff3fb9ded20c9c7573ce43e0213bdffb493e7c21592e930b38764ca86b6b63e085e6d18326b2851d764b5c9beb12d9620f98bec1f596b659a78265310192e20aa9eae7ad1a3e65bc8899c96514a2ac788ebef0aa6924516e4c315b286dc9744f392ec664f411926819ffabbef5c4c9b80e69261ed30a0cd06ddf3f5725d55baa5c660e554dd7b9e00c9390fd40babbf3dc6cb5536f291ceb132b1aad81447cc35313fefff7b9998318d0dcfef37c81102dc6863a44468071d9d440aeb04a61ec040f32c566e728c4696cd8d5530ca291330386870ab6e20945e5e55f07f724393c1e8ec35ff332a5ab088bd34d915a109d56bf3361c38eac6b6bc788ebbfecdf2d716d25399bb43d77f7a41a1a9c3b2cab81758ddc32153b7ef944249d6822a1313d323306486fa6f4815f1519083da22dd39d791585553c4392a8de5263e6903cd60280ea322cd3911bea04800a0bf2518304588f1d3771115186020d4ec0e44a70d960690153ae40116080686c5ac134d9fc40be2c19309bd1110ad205f463e1844b8b1b0818f05a19723c423da990fd789249326aad354b53bda5ce27d4034b34e701f8aeed0666d2bdf5e9a6640a366af6fbffefad37c6a06cd8cd1ab6407ae4e55cf9cdb8d0ca11c118cd84181ab934c1a69c463f6c58cb2b513d97ee3f7548f4dd5f49885c386106754b9cedf45a1ea9c771a8b523f366b311ddc731eb04a3f9822175aa01cf02459c197ebb756678d7366d6fd7efd95b9d5b92ea2d55b61d5a96bb16c248840550aa0b860dd205fe2970fe42585860435081e637b10e25730f28c011fc6cd7a312523e7777f7b8c697c117ad91c5c1f596b62f3976b76b9bb61d8ed16affff4f51aab774d9747e90f2f4921b37d3188b6ac75a66c2ac20502df4cceccc3499c68c18728c6e896b735d1d31b3fcffbf2a12a45e51ad183b6ac4dc58a135c3bc514fa6920afc98a4bcb4442075df33d5e399a6ac66b4d65af7bcde13f49ea1f710bdc7e83d47ef417a4f32c984738f73ebeefe434abda554595d1d7e88ed3a54c15fc10a6cddf663fdcb56eb0bf4c05ddbb4bd31ac00b0ccf994002061c7468526a08f538c98b8c5c56d75feff0fa2516f699127731014e4debc0ea9e248584700f4065207e44a41e8e05ddbb4bde1155c7777e5abded2e0507fa0246a1d858348486444c72b16d1b2820aa8a753cc8a698d94a8962bbbb6697bc7231d4c0c77aa663a1440129dd24e13834ec693526f29555617089e18887717ab42761d08817db4708a95814b24b0495334e9da7819feb9b2a45c87a477fb1e4e617f20dd3cd98e3094268aac64ec70295dd860b1462c60a5bb3b8f8010402eaa601848979ca1e342e28576f4d3f56c555fee9085d46dfea924274bc6ffff1ed3d38e16ce3de21bbbdefbff07aecc2fa432e92b6c967da0146559d98b09d22763cf2f6719e6d3028d4c0c7725aa9bb66b9b5693edfd2b091203014530203a3e2b462ec213575f31173cd08d207aabb0686c5878666830069deeeeb323142b091316954284104aa68965450015cdf32034eeee4094989669c8e9be6bdf1febc50a6ea74eb3ac36dca25f1a172e71906a5e1362553f532b869e47096a6473584efcff2fd930e2fe338fe5a9649885d906a7d74bc3221ca524c1403183a973f4a29725fbffb96877241ca872ea0f1ff9c99261bd8481129f2d0f832e25a70b9c17e371a5015de6b480493acbd199cbe55ed4232e352221750c03ba25aecd75571ebc382ebe50311383d34c8704616c0af0a0e9d803babb2fed1071eebed4a38cdbf7d7f9ff7fb9ded256657bff40adb536a2516f6991274808841191bb3bef3dbbbb7b8ee77a4b5bf6cbab60a092fb3c1c5945474bae1b37b61e05ebdc1ded162c2a63d3966dd7a089caa45c6f01adbb7b902cf596767d0822b683e30c7e84e4eb5d5b12a77b5cdfa3891584172873f88efc766dd3f6ce3f150709a2a88c651485081d9c98c705ef02f72fc40264bde00b3c7ce01da11a3122d6236b66246548887ae755ed150b295750e7b925aecd7575bcff7f9f32c33107e36628e377241bf13462e7844c0ac2c5b8c2ed8861924437a42899c2ffff41f70e46a83becb2cb215459c3eeb8c6cc96505fce395329d55bba6c3a9fd00da8a239126954acec11ab2073a28627a6451126299e8f4ccc8acc8e64d218f62ca2a43453d48f180e50345b9ed25b2533072be5762e38c32939e79c6984d45b4a84c494a2cada40d38562b66b9bb6b78f75e69fce9945a2e085988ddde9ce5b89ab556ec5d7dbb54ddb7b85317410b582cc9325684515c49a31a26cd95d31c3da646d7a01bbb65d5e6979b1c97b4871746df091c8c26e896b737d85b58e987a4b77704c53f413238fbb5e79afd65ab740546fa9f1884c9eb4a074770756d07ece39db20a9b794294595d505828dd8575044f8ffaf9273ce5f39f596bec121d178f4f08564c36e9e96d4647c75199e81f550656042dac84e988830861eeba64caf67be20d9a35733f6a3455cabd81234bbb6697b8f2d6742d7bfde0ebe829bc54ce8738189e853b3719622f524d51d0a115b2a502d18797e85d602ebad0fa452503174f0f8111183215b412648cc1fb690d1e77f001b2e7d252f7cceaba98feeeebe96242aa6ded21ddc012cceee6e8d3578f0d414ba2619a880089d1c3b7ec2c246a4d0a79b212c95f79edddd1deb897307aaaf6b6389818ac26f23e9aea991109adc340d64793430674eef6c802e6bccffbf12972a091571ff4a9c056a9bb36fd7366d6f1a361e324052d21328b97b81ba7e5804edc080250cd103244aa80495a36a840a6631035385d09d64ace4ae6351d82e8851c70fa8488015b7c4b5b9ee53f0c52d716d6ed4f94e09470c39edfc8f58002f5b6eba444cc6bbb6697b67e07b76777727a025cebdc689c84aa70ed912aa3ac4a54ba1e7cf4ce5bde02a0b6c05a99899a13fc2c8604442a849e83e29022c5feecacbab97e37d6c6624651b881cddf3d1901808804a1e42b2b0cc6617785cbf614909b7bef7ecad9635b5866bd820789221634a450c142c1b303f6a52b462ffff19dec761e2d803b61d8ca2be357d6a0d7b5390f2a8342d9e46c5009d00331a041c41c3401cca12b9a6031480070d8890bc7888d02c1286c6c180480c0086024100180c0602018140202c8a817096ca62b805e646dbf3bd2e64f64fe4f004eb23ff0e699e3247d24b954b5088a40ecaa685d8258c2a3eb4ecfe165df5168aaf83b3641af522ff11f7da40c0a2cca02c46d2efcdcc6a1175a1daa9d70e2a750166303407f0b645b3fc83a82b1e4899184fb508d9357cefb368f6496662150a6f7e785018b411225e0e15f5890e2d8d344d1149d7f438da024305fadde9c440f6f31737f3568755bb2fe44eb98682b9ed86ba66f98ab4db8a862750ed262b4417862278d552ef5a1ee7874ac9ff53c3143d4951a8845e4981874b338fdb6644167f70da9070c9695afbc2886b3fb9fd8c545bb6e2da3c96ac914c53d3127a343d6fafc05c2dd69c4ce918a410957a367bd3c9241d7c1218cba1ccb4637b47115544336634360960ff0568b26be93a44a1a2d9f59b24d4d66695674084fdbd875a244b6e018a06c2095e2dd4ab8dc170c6fe37ba6bdbdbc1e8b152458909ab6d0d8a7ecbca1261aef5c8f04b1c3068a2b35b45a74db7b089e3dd78d2b48c6259262ccc5d1dad46da316394643eedd95a6410cd1e4fcd54fca74e1f6da5b8bb3250970d8d5cf4a4a195d4643556a2188b7bfd5559311193eef6871797c02bc2de8bc8834292b0731e924411129b9ca537a6ceae2e270dec2c56a22b96e463f562dd90156539a401cfda7ebb33e130c2b32ef4653f2413e0940f5b53b2c237b31677e5fd1a5ba367da9a8f52f3ba211cd0a86acd14aa4534574928ada454c5c91726a7a4cca5c9e68a194a25bdd2e1ff64f5ce58c21b4eaffe332f7dd0630929ead5abf96f88b98f4712bb4d25861070fa396f6c3f228aa769d1bef21e9e5f3ddee813d442ae61f6e8103da1331e1fa9e78d1736369bc1bb1251fb2176cbdf28b32e55ade7e471266c81a4dd06282c44d1aff58c1627dfb68302d90a51f44d311893b313a8307cd6fb1475788acc3457c7b4fd399f0880683d5c650b11cd9d249aff68132935cc4ed966f320996adad48ae4546194780947df9a4e180080b6741118468028346c2867ff5c8cb30a14e029c09e75d282ea573c268928293a1f81872649162790292cfc5167a458b89fc6f0fd6686b2a058e8390076939b60b212624324df61daca31cfee89ab31cb4570f2e01b2296c28f8a721385c653b853610477395dfa84c8bd4a47210579b988cb39a3a2c755a18cc1bd73f346275a053857a265e82b441021e8be4a6224d4d03359ca241f3d7c14887a47646ca2c411139ff02fd8d3318ad02125e321b6237dee5811d423c8a9a8510786e2f6c731a10a01da3d9a5796898a7ca49222908098242196da2adf276c85713d91c840af479be33dd44a5eb0e00c6ea0f66ace750ee1db88d1ed6f050d177a8d4d6c6088a44ce2b2a490949a94b4b2a527de01514f2f3a3301a46b8f2609de770961ca6bc649fd8aa25cdac6883f60893904bfeddb91fe918bdfef655f3c8e517069c457ef5e66a650f89c7757ddf4bd252eb8bdd1c0613bdade3b21caf239f0030c93d7aa99a04305b4a17589908f2b2fac28b7981b3ae9e762258d3f881e1aec599b58884cf5cc63bdb190a6736247b98d5cb9fcd2610614e180a3298dde507fbebd8deb27dc403ec12cd41cf3fd70695f9192f71dfbf654235c6e90353490315fd6c832bde9802d0853ba985356e9db8af0566a27858c95138b2db3035d7c7a3a75dba3b647d4c995ce53d2c5b4d91dc8ada94cac2660ae2d76e22ba90317ed99b94c64fd6e32287beb800bfca12f1d2a2218ad7d1db5ca5d3b15a28269a39f7cad6b19a4b66f7e239af7f89607a8a796e0afd971daf7767d82898e207218b3bc828092d7b227d770933e0ce683636435391513bab68dad1389cd709cfd3c40e78d02454f3c51050c97d335cb8a9278364abbbcaba81e076ad8261218b934eccc8c43dff240fab4aabaa8d5c6efdba7c461f7be65bb39cdccadab8549a5b52571d0cb81fd05257f52bc54670ca95a3f890155712e5ed711a3a30a6ddb7ff721dc7d60c33917ccde9916c22a0b4b04d0af2cf4d33c4e6e10b9e3dce44cc35e553ab56df458700e79afb0220d95858da2acac25d9fec14943b4a5a1fff0bfb26a4dd03fd1d0b18b36fe593955a56e6c0b044fb29e7ae1b62415fe0edb3738f03ddadf16285f195300a2f2ee76deb793c71105df2539ba939f5340d31ede2c94a4d025862142884f01f8b598b61adddd5408ab29283531086f384921340b5dd1eb61b37b68394340723ded2dae6516f96f14f7a417ab1907ef8f3d8d002807e59119c18490349de164614cf2a4d018c482df8a54fadff007d1eb27bd59e53fb0f0af55089995afded158869ab33264e0d9ec7bf7a2abfb5b2d0686615895ea0b03f339048da25177b97eb715f87d1b76e542097067eb609b821735cd01863e577b6046e6f6ab46eeb5e81bd192f6d9586dbe510289c53f0e68887b0360b42f22b057fc969c9d4526900a413ef5d1d03f08bfb46d36078b080c9db4de95ca92f557d53820708327b4cadc8b2892f67e0abd2f158e8c14f38877a6bc6b9c4f3276af85a56859bc1a55a67fd2bec14f8b438d341d5082006a530ba240c0df3f9c5422ddddca3933d61c22138dde9184ceb36a9cbf6fb9b5fef5e3ad28aea8c716ac000560b91113db931cc3057892624067762b3a6a9b0941a5a9c4c7ef8dc2f56ac24df912b4161ccd44b5ed0e896a88f89219c826ce8f87bde318ea29d098ca286bce340d0a3ea2b03a4dc786543814d0d7e91bbd61729a3651746d1fa26c0c61c4f34605d342b5c3ac9f66ac3c78bad3d11a88b7ddb2cd19c17b8dce701d600e729e31978ed283e17bd13975471a3cc41380f61b06e6398572c969e0a97c8f0e0e1ffc1e75de3e8ad321dc86edf9aa0ae222941c5ad99472e7b497fce170223b142e1d9d710f84883ec8f038cf50403ac8bfa5be213df902c8c309038fd2b8b108378a2f35bfbd9111e646f33569271d6b03fb15ccd2e646434a6bd7d12267fb3cff5d27d2e98334a71d985bcbabddebd1423a96cd10e7887b26343f81de4cabc0c072afa62e8c7cb05c6aeed6bd1769bf89179a93f1a6cc33976e5b49095f86a79dc57f7cd358d583071d03968b122cb8d09a05fc47adec2af778011f7c542ec21fc2ac4299c316b9b112b064e15331e6cac305901a167eefdd1defcaf6816b685ba73f8b3cbecee1f82bdd2b89654323073de92f0279361ddc40b851462f2bef68bf7c6c788e0b05565c089e13c681f12679ab7ef9ba9ff896d673ef47529ac26095eec0fe4123407ab6faa661484aebaa842b7201eb6dfb528c06d760a637fbae62f1d3bc14d90c54b814df5806b6921652d24ab8e6143eada637b0cd72fa85511925f5d25b5019bc983960a35c72aef5d1235a162eea205db3afa039ad0d2e604bf6e5f0aa5b5d8b602d54635bd41d408a472636a64855314e8282d9b4bbf573350423b6d42693728633f2df5fdea4fc962764d5f80192fe48a89bfd148273cb239add4821e82f9643abd542ec7632e379c8cea7bebd05e38fb9e072b7da4c52bf436734d31fba1ec750e77db919b597124bdfa5214b2c8ecdf37ee72fc8b79da76f87306ec947a989a549238b1e6af86b9577b39aa4d7f14d3acf0d74b922082611b0eba19c2f31d3580b2505c2c15283ef06b9132264df40f716c2c541561bac5bf141f24d724920545cceede2df3f42b39d5aa37946d4b5ed98f05e1fb0e30bee5a504e93f4498f0e111d342b496ad2f4e1f4ff849c982949182dbad8a5fa4c9801369e0f9fc5680685ac05030ad4c4241faf9eb0a0e17b0b25f92004ccf3f3f5ac6bc5d61ab9e36c80a5b3397a18d538253934ee2c38bc13353534c1106bfafb9dd6d6cb96c74f050444df427c6c0ae8fd0b2952172f5f981c4cd6d23cbd4789c704e0d6f09bc12e080065c7516174ac9d4a35b5035767f959234c16fa345d631204cd16fc04323b557bc95c8ccc5953e3abab2b23c449f7f2660da91a9d322a49ce4fb65751590f6623eef1398865d64680c9156e7910a949b7f4048c7010441292de7129ce5036b030541da3d701c3fce1c711ef2e44b0e8046bca4094dea630ed38264db66adaafdbbaa81a47cc44616ce544e1419ac089e82c1be936dd76769eece46f515713c1f02649320b810a4062230def881095b3f4937fe3b0e8c6dbe3ca7818253db8ed10c6d2eedaeeb1e1ab8f3be1ff8f77209a1a6e5c21860d572e8f0300173fc6c2a2347e456ba045efc66b1abf12b2d76760aba0ec363e19c1a04e44733a3ea7bc8ad605d8e9725d0d49fc0c25d494e0344c50b616f398b342edb8a1894229c75ecd67eb20a2d7687cae4c330d13435ee91c21c74aa81054cfd8170ac3306e3fb097327717c6a0cbdc5de3d921fd7ef7a56c2e839d01a4b883fe558446cf79df45115ae624eb16c1960813dd326f3cbf8594aa969d497a53f2030e1964eae09908068ffb047e7ffa76a9d4564c424e87b4d7e412f2dad17699342f204022683428ec9295c3ca27e0ddb0c2f6e11701e9cd20969fd2f6c41a10902704f0ac8769bd1a50d1832c23d4aa888bf3c5d6bcda86761f92709fcb0c60c26f20e1ab9a0955c7a446dc5a28e9e1e371b81126b7c9588b349d0642927c3c733ce462d4b41fa86eddb5f655e5ae883ce7799b4156466c33b15f4e0f4cdd142eae599fabe121b1cd09c5fbf759726074cb015a22dc7dd33f7e1dd1ee44b9b9999cf6b7afe8b95e1331cbc12822a14a6b76411b37f65ffbff06f45ad9a19d4f7b3dc8c2ab945aed68f4d6a80bdddb98040e8fabf9ea2e33939ed1f67f68f7711d1ba6b4090d8e1ad131584f88aa1b87b0d1794ae4e9eeffae272335d065c65885e150a41a0983350131a91f648dd8ed9750e39c62b72e532def6ea17e0bffd88aaa70451cede72200c471d3a5002c5e0f6fa24180d1471824492dbdde688bfa41fb4029e138da56dfeb755a3dc949d459cbbc5a31c0aaebfade577cb36c49acf1af887f6679dee08e0e36bfa2d64938bd418cebe8c1067b011ea30f7dbd88d884d65e340493895c03c835bc0ba48e1c402839721a7e20a414691b12ef4ca1ba04fa31f71ea6a357967a29919bc7e7fe5edb61ba0eb613ccec12d7779d4cf61a6380d3dec324e84529b72deeb6bf2ae9b31323744956ab86e8d7bc0a41493c245c0ca0ad88cfea98bf6a97582645ff908de9a238ceb9c1415b68eadeb2f8ab10624f72c6559612ba7fc1323dfeb15bea4a857a0f56ec1353ced24deb1172eefe871e56575a0bc12c3d50b90bca175f125b9a3da0bf442cd46a68e06a86a547ffe5c17cfa11b977d968e4a02fbd41d07ff9e4d54d866965af5736f497b826c02f6d9d3e1e9cbd13fab1286ad5c4965502d99cf32622d97a200ac8f446cd0a477a938c0c13c9703a4dd57eecbcbc742fa1944608a93744ce7051996ee05a4239cfda769080fa6744df4ff541f1d08e784b07876778caeb692bb9e788673d9b17bf49fc5dac918d70377f1c6c587b8ba233cc12320a303b55b31d2b34a115e96a5eafc5da000152a60c3294c4817b3077df6cf4f3b60db39983e05f13e7c66e084105d287db7ba5328224be105f81cf482361c6d34632366b58484e8d25ec257da93ff08871708b6dd9ea121730964a1b8ca3e88d1063efb192853e840942130a0669a653da7d2df75522648aa574bd3a29257594a111c53b7421b60b088916063356ccf5d84fd6432eebe295c3552803800da55593b9f378fc8ffe76e2371bd072d3193c91b24adf9ec7b751630309762c578eec13a747e353128e64daa4a423933ef1e90a451973330c923cdfee5d7712d0ac9426173e69ed3e21c4b2cbc82d5fda3292b2d9a5799380030ab75cc9f823bcabdfd466e66a4f8ef611c1c7583d93470ed5c1d64217769222423cf278a34bffe5e1c513f0fcd84146f85880f415c520cab65967690184994116c5216fc10793b5c5c415896083570e317e7d7ddeb3719e333f36ca82805985976c9e4b50f355f9f194840ac79abd232f7d7d32f9a6efbedbc36b661bb782ecabbfd9ce877b745b38e77ce3e681d601763490bacf0a99d8c177aee097ed7eec233aea98077c7ac2b979e385d474805fab5aa88bcd8a964ecd4957841b07361c04408e03909bdb6cf698070725ba56209131b194cb54404694c68f6c36a071ecc559dc54e5b87c7c05264aa022c965107a3ae4cb2f0c72bd56eb113534f7590d4dcfb928264ebfa28f548becd638e42fdd4cd72429abde6d3bf20c95220a5c6a481da5ea48ed54da96b1e8577c332d98bc5a6b6d73267ca80bce40cd021d4447ae98300aa46b93c2d31a2d2246d8c45411993f7cf9215b7624817d8779a3c2db8328d10db2204bbc787500fdbc5ebc9bcd71e28038a7da15324eedd453fb6a54e921c908e3694405019695788ecca1f3b46b20bf381b859732ddd868edd1a7237ade96a05a5cf12e093438f1ef7f3b33b5cc7436835a6db765cbb14643b682b0ff9dcd4e4ef8dbf1af4097881b93880b6456dad962ab116944774b732b3f1c45a0520ab8514a0e5700cf36510c31584b061f1836b699c521ffdce1e99c0e89b5dcfea76c532f34f0f92d811440128a9bdfe03e2f4d24683d7fe8f183977ea8f2d944bf544358e0918041d55223dc07b43685381e118c6a3ba1a4fa713d1c2a4a3a97839c994f20c3013ebdddb59b65bb0011f6051f586c24eaf547bac67197cbfb47e939d5e9b7dcde3c5d4ea20b1f7c060741c76b732e4bda7ff60d96ed4821e2b971b86bba909b6939b71f8608eb662e62914c0d4b538c147a086beee2d3dafe979001300568cf821027573870356c330c6e036badeb53df3d357b97f1c7f9d7d370119e77913b12bcd79dd62c0785216292327e3a6880af7b6d7807929cd3342eba9283427020456e980d1d643f21d73522a0cb0ad4482931688409d813baeeb6ee34f654c2cf8cbe81eab8dfbae134ec60deb1b3ead5e5eecfc8cf8c804c49a988d5bffa77054d3b6fbe58bd941948de3e200e1fc316a70edbb85925d469c5e2897465d7c3e5c010646c36e09f1593e3f51735adc6bee6f9fa557169481a8695dbadd30c6f2dee99e6513c54376ede961d573c1f3cb16db9bb8185c3b908e91bb54be0225aa6d51efe24857de5891a7709bc9eee680c756461af9753bbfb475daf342e6ebcc6fb748536c58fd5b7dae3d2d8abdae2af7f8a116bdad5cb00d4a8215a349bd917ff1d450cae6d3b975f40630270d820fb983a30d602c66dad4f0c1cad0ec3e58a3315bfe59bba5018e46b956bbba8c77281aa25360bc431f499e549e8f928ba59527ac5cdcf39c07d3042e86e39d6a6ae8ef0f70a08d1fde3f7ec1bf6f66b9dad0cf8ae90428b8444168a90c0580156220318e5bf2e8b5e70e2bc6ece9cb04fa0bbfae26f607b804aa5736168509658c609c86a0cce70ae5daa08b14feab81e48c9bb3c59c1b0f62ddf690d91db364ea3286cf219e29f0f005579897d1bd9a8a1c97a41b800825c44be6c736fe3bd7047d0a2bf3124d921b7254bfb9d5993a440c0954d4c6ec53301b96e1735344e1c425a189ef90c7293dca0312772189f5c57ca07e950dd954a766d5779a191922e19966174075facb6308c5726174b9510075770f7915f3b06f658ce2a5c7b60d86ebd88574fc55cc1c02b4ac3b4320748eeea6132dca3dc6f5ac179fe71d6bec523be767cd55a9f31f6289966041c98313d957871d67fd236e24cb549ee258f7fe3171f1b09ce4b8f4d696fea15fcd087c75248bc919c76e974643797a90056c9b6398a340bacebab26a790fac76ceec942170938318fd9cc51d85817315db8580f5e34d1a00c1af6d05a5cf14e76f8e84477425ec910a3dac1e6c098c1f147c127e95b3f4bad2e01dec8c35931dce2c7bee53598b6bde7973071a0a6955d9cb9972566a80cec26f0e8ed81e7951a4efbb575ad6610a864e58422045f40d1f252564ec548b5546695e831bfa1fde8b6c5f919eeb6c8ac9e52753d0a6c678609984a35f7b19cad9d965d343a2b71f34dc84040834cc3d4e2fcce21788c995a6662684ed2a4be0e04f9acac09651a0754727b7dbd694d2a53536b13af58c89bc8f73397012d16edaf370a3bdcf4a5328e898a1626611c9f10af07b53c1deda04033e1b81101daadf8ff6df92ee534e5c8c3bbaa20b44253fabea55b28d22d2391346911816d0d8cc248a2140fbfcd8f2d0fe273d0e462acfff6244408d2c5b54e63afe35b07bf388cf2d7daa7fdc18785549b578d659486d0fb1a18d1809526af40485a1a19d2139346285d54edaf80860de4d4ccaf17529a48a69c4e4122edc0994dcfb1a90fc88ee511332d5337d4508933a67a95d0dd4a23250ba18c0c1235783de853b9011daf25daaafb7eb80b34e1f5a26cc4d3512b453fa11825a5bebc5a20d0910fe038ad381bcfdd14243566610fb1b5a3352cfa6b5be72c1704e45c9c956ee598be33456ae20458f125abc95b84b80240f89842a3138b5ccc9822bf3179799844872390fb597743cd1aa9d2b44d4759fd48e2d162ed7fb41c0bc62122f892364cd6b72927fc6061d4cf26958e1ce168a1919b6d894173dc8d85412268b0d379eba4528ce085e30955786f12d57962fe451c2d523d51cb9baca878322bb7e297b4bd87e43b7f6801cc12245a7bb8d7cfd9ac8ccc44795035eed38ad0ed080afb4c3c5485a52ee4f5e620ba11240addda1868e97c3bdcd8a83282330f2597b97030d425ff6092396afb002591b82fd07892c47c68bf3f62e4081783b7a4d67486693a735b34f2eee3b1746714ed4125aceacc82d19c9b343f54535b23619509c2cd0420c1cb025183376bac82afa30eea09ab0c9ed51a644b4ae5e5a863a63f7654adb046b295240b0e88616e53cf40aa602bb033d908a5444e5a8b78adc19930591c01946db369fc3f6850c874119197ed6d45624fb2a56890498be906d1ce405ab12ff4ac82ce1e23002c7a366128a2f758c59bb3d6bd306e7f4e8efbbd3d244b555c0a5192564390d69a9711fafb7b379ee72889f1f74cf58806bdfde655bc93d6b07831bf75cb4c71033198f2e8058f188a297a486a32835104968cf14f3d755d9bdecec0f94035c8679618a245f22d81a0461ad0e024054dbe3016f1fcf2a879ac891fd5106608ffbdcf3d11f45ccee091dda2cb13f2aa44f20f1ab83914b7b0aa2ba97b88d928a4b8cc0290f25af77e643422aae968415e4bce91a313487745c8d3c0505cef3be10c1f036a336c9eecdb3d4a6cc3d0e51cc8d0cd274f0e7242fe5f3139b383ff3f9515d9c97c000128f1522679070c725ee9fc00736a3c7441915bbe9c3c6ea7bc38e42b1d2d6f17717519c1b81961ffe69773e700549e494b90ff1e010340a9d813f2eb6df8887acda12fb90000e0e505db744670a3208f4c0e7e920f7ce35834657f4ab8c0184cab5bf9f0232a88ac0051e5798174988806eeea46858b5ed68cae539ea970199c98b3207671c4c270452761cfdc2c7119139629d19215776be4531642195195a6abfe50b50abe36bd26ac4e9b72536890598f4ccea5b5bb026c21ba3eef3f14875f10b37d783f7120bfae425dce01f2323a04515a6c9ee957f91dea4811d11b7f188ff5a272d89bc507bc2eee213e03917cd2c8c8fc27cf6ac5fc6c89dd8b629ece1f6a754feb15d595bf71bd6efc3947040427ae02fdada3d6579900ad5d668c16804ea9309ec56a54bd70502cd7a8a787833923604a3565d7b5fe2ff4e17e53cae04ec8f7a783b4fff7744826a42f38d87fc31c88239e828ea71d96cec115c0fd62a611daf325c5b310ee38c6d306358bfd6c1c6943b37aa6463083c843acf5a56c29e8ad8a2b92972d33d2ad507858986e49f9d4b578d2d02bfd5bdf320bb32fca19adcfdf3a1ceb49f3b1066b5af0165e8d449fe64a3e206270fbea7495ee99a4abc3eed2d591e4067a1a5a47655eadf13388bdf7305abf9385585130b1136a61b853b6e18118905c18629862ef04ce1f9139aeec31c2b763aacea6371067789a8f58c305fef29f4f6f8cdc789e5d7755120169e858363d8ea82ed17da2381a03ba9a4d94a8ec3bad452f278bfffb40a2879205aef144deaaebe4bcf2ca13d15911cc682f0a0cbe43c023f742dad5ea478ab83d98aeb6ec983b57c0011f22db7a8cb99f2782e7d055ec5ee8288f62d240627c95521277bfe3d2e121ceb296baed42a2fbc8819e343492a39730a110977e7d1d8054f6fad40ed6dac8c31eddda1205b087243335b323b969591237811282f7e83e6639672f9ad9fc553c37d93d946de542851b058b3020664141533e76557d75ca8fc5b0ef810ec596079e81d1460f3091bbda7a7c1efa3aed6003185ca144617f87e3a7ede9f743e6e977786f6fe53b4fea9a3619261d783f946a13c3fb9114856aa06f18b8c3be73ed7e2866370ab06f7979a58c16f7af20ed76d2d6f91b0af738cc5264d6f2c6a786aafc1c01d598bb8fa84f6a7e454d0348b8bcd41f3ae1f7b028337faf664209272fc7ec0611a8172dcc269acd9f607e26acfa24ff16d8e772dfda4283a63a73a372ef55226ad58d19144a526ecbc8419646485e042106076b482d87c9648963d2368ade1b834caa97552b417043cb900136df1c242e3f7eb56d79ebaf56bdeb6e235090f971905b7f2e182548929e025697e2edb295f3bc57b63a16f9efa707c277dccbe9ba94087596c46f533dbbd05ce3a6d605f37f1d3358776ffa50d7228d4a5237d2ed02d77a834fcfa7fc44c1102aad4e244a6cf26724aeb15dda5981cf8177623f249b8b763ba4edad621d119c4b017ada842f58708e25a503f6a3b75858eda21f429f7aa0d80f9444ec0bf5ab0cac03d0c682caa3257748598db2963fb767ee8393d7c5f631bbd4b858b236b4ce4599c576b9d8801cc9dff2af3ee5d67be05e4a5137c263b04d64f6278490f802c202f70230305157835e2d6174702ea87ecc213b267e7070ca091e77e6726d5951d6937555e6f6ff3bb8c59c9f73ce990712a59949a5071e4b9b07a42e674ae95cb0544f6722bdc59c1e8e471d4986c04cced04526e48825672ccb4430a78fffff4d2ebe953f97b541d6065a98a7aaaaaaaaed8ffaaaae1106d0e6eb05aa295befc88f891210a6745865536c5cac54244229692549abb2964c2477944a301972b960cdb411c2216c8a563645f5c99868533389a46555d814b635eae3ff3f972e83e0eaad3f9a6be0040fae9a96872786232aa3a3af1ac618bfb428cd7c717a8084bad60e4d702bca7ab2ae555b582d0d8d20451897f91ad2605eee91f9d329019cf1d8da8367b0bbbb3dd1bec88a61e179c191fa6c01ecf88211c312e300ab0cc74156db5b51d693751dfbd1fdf021d5c2939c2140d61b74621372a222a25122e1c2ebe3c6a6a6f6bbbbbb3ba010629cfea829943526f115326b5dd30865d5b2a2ac27ebfab076002a143a70eb122f898b369b6dc5995c318b5043c9169d5bd489546fac0a9bc2b2972003ab13461ea28c32ae13c8140d63580153dddd352bca7ab2aeed29b5b505c4465c3f13292ea09e8fe3865b7777d48aa42e2b63570dae271ab8da08a9c4058e4b77b7cb6229182d3696dc9278dceec32e5ab66c5ddbffbb2af84ed830fd9284c0d29a5e52841d2fe5ad47940f175dd45392f4c678ac7954ddeef64c8110774337f6ffb18e28cd44263d60298b584b9c333fb843b537e79d4bce39b6d6387d0504f3ce30012e3039f4aa0ce994450301de8ab29eac2b0f2de608012128bbaaa1a94645950c4de4d443b49112e0032a9d4bb76b8ac061bb9935c3929715653d5957fc12d6c7ffff9e77e57fa744f0007d88ab592308930ddf970d163234323864f87f14c39c7d5a5c162e276fd99aa41bcc38b6c0a2cd669b494da72041e0180faebbdb4ddf0c052676400b30785cb07462109da8ce14c12dafeeee0f3d1dc61b5a93b6ff33862494663e19ad368861eb1f72b1f2e7bc588c802220b960ed9011290a6a1933b8009821769ab090d8de7f96ccb53bac509ab9d5e56187d82c67360d4d76dddd6d18aa4c69a6fabf10d562fbff4c4294660e3308a662ce6fc953b2f7df4ca621b6a47a6b8d6cb8f16289dd97354f0e8ec063d0e45804b3a2ac27ebca7b9b73ce594594661e91496587aca5ad8396eea58fffff3860702bbf0e1b2d8b8cd40c59fa4b793897125d379272c920998f1b215217f15694f5645d77217cb6a6b6e4a5bbbbc99029cd549f32fd60e175bfacf8733ea91411e3f0ff8150261ac4feff0dc00986d588592e07dd750c09142f70090d21239a351853b06c77f7900fa5993f6088a160568f6aae45edbe6aa4436966cff7fb6004bc0ac618639c5bbabbd95a46a1f63be3ee6e2b94666ee10e325d286ce35815368565310c4ecdb1350cb41a5be3bbb229dffd4a5ab56555d81496c5ed77523d9f6d5d328258b2c26b7ef9ffe70280e9f6ec51eeb8c4136d58fdffcbb91473d3b7b19aff1fcb86d24c5d4f032cdf8f76769e865c923bd2610fb2fb6079fbf8ff0f2534b4f2b7b8d6ddad944c69a69abee4803cb37703c09b84ec171b390caf16c22ba89996a797946606cf30ee8131c637cabe59368ead71748dd36b1c5fe3fc1a0728ecf7ee09fc727158f7d41dc7b52a97aed89d7b61cf20dd7ad2cbba36f310b6a42d8ec8e7f032e7bf55f3ff2be9509ad9f3fd3c5002fe3f6f841a5b6245239c747c332335f5cffdff3d6c28cdd4f57cbf103d805db37a8b31c61877dbf296addd880132ba8e242ec618d718a234b378442695226a2c557c9382e2c17b51067892b62bca7ab2aebc921b43160c94d01325b613b2947104caf7bb3141614609163bac5e7e3ace760ad8fbea1b3b4598c7aab0292c5b03005e77378ec40b39dc4995245b7c97430a9cab1897c5fdeeee6ea916dd4a37c61d6731631fbe106be707a29e9a33bb73e6f1ed8992b1f4f1ff9f461289e4089b9e8eb6d7474b12a511bba99c730e16519a794426952182cb1aa7deeec52da705590b8104494f46ab7bf06efd3d4f91c32c901b784f8b218e9a8c68a05c0cd13a8f3b7eb83a154c62329634f4248f43ad087684305ce398f0f471146488cd022bc8f811923b2292c3e3a846850efbff6f3184d24ce1f0438be2ff322b9ab44769113c76e1fcffc16a5cdc92654a33d5d95bb25c4f36e89eeabc227470569fab730855da6cb69a99596c912872dd66deff77e8c043aaeb5f1e6753532f898a2bc8c66206f9b1e85249810848d68b209e196228fa884ecc540437f0d2d5e9e888712372bb3efeff5f97338e8cad29221a6c36ba860d163d97a5006ecc38ba526c94f08731cf3d41f410b6d8a4694811efc61e308dfc5a862472445a384ee494d0f755c345219b825d5cd50659dc11917839a71042af8e28cd4482b84afe38ae942b3760e5baf45dced852300f0613cd6870bb181508993671ec97ec7e79250930206408955717d111e70c4eb930f2faffaa224a338f1eaa9039dc345ce668055f1ebeb12a6c0acb0a91f4b4d7dad16f7a8325af407a5891347700cdaaa189d1825a6e93156528cdc4b52f4e4f0816406d8117071a8c31c638aa09001a7528aba5458f251c48bd4444aa5154342d3ad478a4fd7f18d44be97be9eec6f86d0b3f2576ba96c8ae545c8105d970c09df8cd7a5cb09afbff9584509a5994f464eca0648577aaaaaaaa8a337bdf5a95daff1f86446966324cb9862d51c46cac984e45ac57b3402522449e488f9501820792373d2db723c1125a472d406ee648ea5815368565956adb4ef0eed4d0b8015b21c319b60454b26abfbbbb7b92509af96414d16385c2b6dd3e3b2990a2ce8ab29eac6b1490b21cd54562e66c81a7d301f3824caba8c914d21b3143c0d6d4955113d412a8e753c4cb811398b2b46010f278f05801416163fe08fe3fcf645194c143bedc691221bbe15b427edd30b6d2afd8c7477d39c77004df6e47621cb16952ca51559574238a2498d24cf56f4dafa4f9012a996b915d581536856571cbaab029ecdb4c5d6663d1c7ffff1cb61c22872e6723e4520c1d442f1518bc69acdc7835e49d36ec9e2716d030a88fffff01ef8fcecc06d23d4368853c3e4470158cbb0257e002a6c88b5b8fedc724e34176cd7a39d7c7ffffa82572e5ffdbffff3bf5509ae9fb013308a7a0caff12e0e4c379f14ceec9399b90455ecbaab029acfae69cb38f10a59943b5b81e51902c083e49959a8bae2ca6254b6e68d316f5e043974b956a50ef58153685655b75c74077edbcf08562c44c52d4901f27b058b28bc2b12a6c0acbe217dd993986b86555d814961debd66505807a594f742bca7ab29a6bdb8404d4ddad43820bb025b29b2043390da51563657adf740b57c3d5fad121ca41e4f6c562654a33d5decdff5fc48d62ce3fbefdbf4c12a5994a0f64966f115343195239bcd8755dbabbdd54f9b7c45bb0375bc21155228eea85ffffff15302436214a3387c52332790f6cca9fe4869f7ae07e1cd0bcf58aff8c4ca78fffffc5b0b295ffdd4035030fd44c6f85f24d81f5758e7530b662d724fd09d6201fe3a2635a35e6fc254df00e05feae0257316163238af8308a0103ceab86fa529aa97eb744db59dbc7ff7f61cea80957336e37398ad408eaa7e12e9733054d3e2762f070a167c609d70425263c7182f39bf5b8bcb5d4f8432cd5b5efb74f27889baf1baf28ebc9bace786b075e60415c42e38e19352a88cbb3bb73a8cb8b0b7c90a674c4909161a49b8080b93881bf9b09d9ef37c36abf33eeeefe4262115fc995ac8acc07b79393d14cfd55d653dec0dadb060e54b5fdff1fb72c0df81e9596924f3227118d12274a4b48079f8527231f1c28ca48109934f460d0c1579ffbff91921ae9a9918c8d64d5485b8dd4d548628d54968998ec54555555754bdda563099408c20092540449662868464cc786d5ddff3f0a08a5994220a28a5694af92165e50f514b946388854a72ea6c80cfa04b595ba6ef75b53fb9d7177b71630a599eadbeeee96b7cfee99931cc298d8e8b101b4aa6deb1a65a4ca5bce2df43ae59c734e12a599ca10394b9baee7fbff16530a08885b1c331bb639e79c5f21946616059020a8946ddee6c40079131a68040d0371248cd31863001400070c6a94b4787808311687c5c1a0480c0484026130180c040040a140182c0a82d068aeaad17800a9362d9b755087dcf8d252e1468564365bc5a23186ea284aae53a43daa469d13895e5178dc5b2b30f6db8c5b8ee015ec4a8f461befdb5090b46549080152a5f5c6c1719314fdc262e01b2f7cb846499bac1e5a5c8b5520cd8497002e3e611b62c8d6356057eee490bdaea48a573a2d93e28830caef4f278d631235b561e51fce0ed32674e53f5140a552cb7c0d081c6388e0e53a5effafcadb1e9bb7b2e6da0865e6d8d3fa48fdd8523785e51f71ea1a27128cee1c5d7b313a04ba1da2b55f9be41aedc5ca165f290751cf0590cd0d4fda27e79761ae35b87b26687011cf31c28ad9d213cb6344da3e3f74b2b3537c3c235bd069f7683ed20d21d4b088ed678cb5fa2a8b5490f1b7bf61818323e03ed030f223019ad1775bb4d2eb1c7b0de8168040063925f1d163b92332dc60a11e8fa243ab93c61fa4c3ddd46864a2d2308e8fc72fba05207a5cff261d3ce57fd7f153a8add024690786bf3c69bd9e960280f148bfdd347b2c4e8917a4c69317fbdbe47b65fe1897ca90aaf0343c7295c6066ae25db25a775eb1efc34f746a1c8bcbd876ca1a2cda61554b1aa040360a9311f60f9eb859eb6db7cc115a2a1a8a6941cbebcaf6f168f51d1ad31f04589a44a940b87979d0107e60ec29246a8ccc420d8fcb4e3ea5c9ad027bd880c3948e6b7159b4a8a37f4cd55e4911a95427b5d31f4d2e256e27bced380dd8d246cc8829b585e8f429a07f12dd81c90e4aef2cc53877cd856996c1478ea4231f03dbb9cde6d8de7297ec59b50b45e032049ebd53d24e41279dbfbc974d67b9938564044581bd86a8ef7d6cdc08a29870510420372809173a9fafcedcec4568d34eab4f895b5024e18176a716b453bba8d17c4aa98641e0ade052f9272f7b879a028af2df79c3a056ba73b0dcc0ab1dbfbb3ecf2a45e574a9214208b332241ef642a9b3a700c8f059c4882a07b51c53c0c4c948530ddf491f68f199e39d885c90506d27e3097393314eaf300cd5dc2bdd5d2f3e57a48999a80f83cbf9c362786b75587cc2c110b6df4957d4e698db840b57f3990871489c67d1bb9709db69cb1d53706044b9c4beb1b1adb6a704c44f90b01ba8672f30afb5ce0a199edd47bcb8e111e93344e453b4d2244f262a824b6193cf528a7639103ac8b8f2a9bf48c2343523120f50b2cd953e8adc09dcbbc14975bd51999a584ada2ca8feb61edad30207495f2dceb01a900e462e6039dc910c907c662c6d15f58cd2e8df6e596b650c42f533dd0d81d1c710855ed25db4128e89420b34545bcd5aa3e8d1c07cf9370547599d25c9fc3535068d70d49bf3178596ab1957e640d3c1c9ec30ab08cf814acf3bb05ca64ff945703c097977487372036f25de21d993cf004ee36e30c75fe07c8e237a9a01dbb35788df885ddc18fa956f48bfe02d9850367135038276ad7ac5cfdd5aeaa97facc69751b681d13c3fcf0f0452f8018b861cc8eb837bdb995d8d04681627cc7ad38588b3024e671d9ad1941ac2b52ff4a94ff9ec9119c77e26f4f16b2da84947bae7c04807e6d7896a4eb000169b8967d635e87bccbb421fdf28d726084833494e7f3dd1f6aa5243fb3ac63ed839d08a4bcdc162b3595e9d5ebf4459d602799f8bfbd5586690b47ed354eab043c80b429f3b4ca1c0e42edb66179c066fac66bde8536217815d0c77fee1a07f688191d7f3b54a0584cb0b21db23e415fe7ad635084a5e405de7c966d880a6746f69be38a57145cbc3f38f131b5b21e5bdf0498a9e2bb907d5aa73183bbd5e33089b4311958b675a65fc285b577710993c27a5231644a4e96ae90a487ee4b5f25548fd8b6d66f546e5ebf31b5b22d90630e8e9505e9479a09a0b589aeb9267946fb14fe12e4b5bf927e8662b339ca794bcebe7924a432b6b215078fc811dd261095648c9f765445061abd52791a68bd1ae7496b0272b2185476bc1989a6dba82cd1e6595ba632c3c2539e2cc4490fddc1983701b68a2790f7ff8a7b6f3504497669a34a6b5df26ac7103b1be2d273be43d06cfe8503da2511ac3b9093abf2e5a23c034eb8629158e83605bf704cea275cba29d285a1868e5d6848115adca25851956892766d702a8f8df05f238a80c2a402072804596d9116cd5da5f2fe3f834001ac6bb2609735f829a9729c75aace0d836b8065dbf5c8df4e036076bdbc0eec5596eb839aa9f283ab945c837d4e89aec18423f40e74d8cf50c31ff70db8457a528c77a3e540551713c65e776e3272224a12720a9b9e434ddd663f81bf7a6ee289d5fc5c00297698a96aba2e380fea4cb7fcbfc438db3b037c9112df4f081eff2c5bf86c1c8ca5e279d4970ce10ac5b53260a9c2fefd849d37021d84db480268086fe18e2e5b029c72a3852341224da2635397b4231b8fbd5efb58bf18e9920bae9463a4980e06a20cf274249c6005bbcfe7ce8dd18c24732cdb4ac1bf83d11b657c55bf551d7b85cc81352d7a7c53e89bb842e069f8b2c124a6af601f9b69bcef5e6740a0ce981c6c7714b376ac1ed6ca411b2609a0d4efddf50162a9a18c4213803c0ed22e26c04dce7b9032a2229a2c083b04df26a7f4c657e471792b80fb7e08dcdd5298ea718e1805344b41e197f95e6edea21c20a06b24c5f28922d3d784a3db550c565354db5df30631d0935751bcf76da87e0e9a011f121f4da51412404c9d533ea8f1b47c2a766ba97b8751079d3abc46d9e1afb396038767c27d5737cd76cfd19027c2517ace2a75d1f2d7da2387c4a568d9c5904311c9fef21808ab53b5830728ccff0c1a15829d66e8dc39a93d514b76b89e25e9d555cb043cb9d3a0e67a77c8ef637ae9e59c1be788c702cc609d62950454a5d9c578b09ceda25e6bf646822edea84c8e59816c400a69df0178f8bda50a373bfb87b442a100764cf27fa86762ddb6f1c5aa225543fb481039a155063cef8bb3ff2e592becc16f12b8f93d10ca7bd85a644beaf8ce26b7b20a3ca57a6780188e2f69f9d7eab8449aa11e447e81c35a2a2c7771cdb03c1d4f793c81db60088efa7525a972757d5d2eb1776feba731f8830be46903f5f1411464cfc60e10d96c79d2503bd6befe426afa55f50d91c298ecb41f2cc0f06dd7fb4a868b05a8a9ef77b074e8fba84274b104ce035f24e70b17b13a5dddc4b3ae4afb24ae106e84657b1ff6c49fa936154f1752f996f6ea52be089cb271a71c6f783c5997e91a0552fac57f96413a7df75af1f4f8a66abe2e4135ccbc3d8d8d6fca963f9f533e50307e01cc56d5bdf4feacc7a481144a30d3c7c55c6ab2a5c9ac38d13b0cb04ecc556f816d52db834b498546e7824764d532fc61e67023e4d178d2707147b0092326e06550ccb01985b2a541513973be2ddf77c2c98099f8990cf5b973b5286c9544566989fb38bc5247f2d5a1236d6beb0cb0f494e67b19d4dac0e73700df4781b55eaba76e47bf70066d446295b25417f50d0814e4b3530c7024fddb0944d737fced13c0d7468f2a1cae92ba079f3a212f1c0875d95c9f86399f8bc530c36c8a3da191668369fd1b569cb210f73b09820cf233bbe24d37d370caad446009b2e668b8ebf147e6fee3cac7025d0a2ead12c4e9e4d048658313f79ad5a630264c542096ffffcafc920a94d3ded10ef4a19a576f6e80444ec71712de5894aaf9d467b4a712365766e2347beb00c4b98017a2fd4955a3ab7285639dd392b77110c9c1c1b2d12b20a6db882c00473798ffe2d7b3ab67acdf9581ac9484a5000817f0b57a48b9ece8473f75d62ed4be91653391c90280e4be7b125992693464a0a244222174232851f3db684db69d822b1b8c75f36af99cd6babc646e9796a2a2e1551f9862c5df57abbcc8b6873a900b24ce7abea3c9078941bc4807f073d624a9514c8b2146fb8ed92221633ea1b5444cd927035792ae8e2c22a206cac941f7da4616aa0e32cb533bcfc704b421a5a676a2e93b0505c95b550d4c544e00267dbb6f72c08576e2350e1f469f3e98483749111862081de8772d4b56e7a49e96a234abe8eeea3e116b5c24746bdaef6b7d3493275a86ddaff53e254618fb4a19fb07b06e30ae735850e508df75c9f19de0ef29821309f3215956c124129bf0e4098b807229ce8086074b272accd3281655a57a501733a1da0399981da2ec635447792f9e548724a19f09c3fdbb2b78e747385b38d78a995c203d80254b4994420fdd4e01a32d812921ecc8b9c7d183ddad7d23d1f49477977a961f6ed52ee658fc49db2bead3b00d4445d8cb97b6af667936aac950647d04f7dec399d5bb2afa1d386b5c29a41583892242fac073acee0c7740470225faea9d56a87c6215b60c2c7ca9734afe6bf501ef2b16a25b8c64276475862d9a795aa5e5e334442f9a253d1aa5a930b88a83970585559b7ee75ca8a2fe119d85d7b8249c9ca40a079b1b873568e1b5551ca752b629be3e06ff7eb0faa4b490e11f169d15fc97c4dfa13f10fa031ae24e8080137e5b3690ed6ab8a7ecf87c7577fc2d8ad055c8214e103cc8920ec43fadb5e332b4e05261adaa0a2413b37b1ff24c2b81ef63b9bdad754cd1737d28904bb5670779d624f5bc1068cd0801e6001b8f0306d27a5957f15f60d1f3b443ec1b2d9802710178009518133e01a4fa23257e2302ff1ddff9750f6fc024a837c6af43b46a17aecf7bcc0a5d980bf7e91c36b631cab928e7652a3e5395289179e82399b02c978398c4e4796c2efa5a6cf4fd704ede56334970e33d447d6146bdbcdfeef36d319c0d40d3cadfe267a4bc688acbc30c9fc52689aaf22b132504da6328d3d259c692c8614c6f114c2136c651b2849ed57681cbce5d7ec868975d13f3b22e2bcb18c369a150158f43c588f848518a1c3503f525491444e881a8696f16b519af71ccdd9780c76e973e452fa293057a143030a0b4e6bd895823cf05a6dc82cb945740fa2e2e85dd61fcea1f0fa746c15674086a9839964f7382b9a0ffe83fa03816808fe035a21d6a7ca3675d7b1bb7c16eadd24b159d2d42ac8cfdc552389d1980bfda9ecf64ea08426a51851b7d24f05511f813af01fb2d15ec806d423e72ed88249a22b5c3428e0e51b9d1d6b3b180430513c2b90157403a533edc59300a243854217afd93138c291a9d91c861a66597f1c0a75708ce831bfe516e8a23f9eead92a0086d518d7da90b5e096652efffb956a443fa022a07dfd5aca652255b4bbfa4d6e7bb38778a0ecf9441718c9fd6e352c12b38112bb9575bfd5bafbfcc2bee0b8d36cbdaf4d8a279fb2f2c4688d7900602553a9d939afb7bded4c3c37a5d2fb7bd02ea964bd9a69a5c263234759936adf5f51f07cd2cdaeb75dd5ff76b4902adfc34dfa2ba87d1eb5bf0bb7d3aa9ea8308dc0156b5ee68c85c67cb39352aa13391e126a2308ca6a4d75e5110b0e9ae17cb0c50de55f41dc61f0cd5ca6e79715841d7bc10692cd9881858bc51c94e9229bf1992361e2e2bf02015ba3a9b1cb2f04f2ec93a44956c4af931d28f597f0c6ca20278ea4d5888d53971a147aea2cfa9aba84a0e82fed663cc3f65ee777e3e125eddc757ae872a6b7dc2560d07201882badc1fd2affe4b7775ef80d45ff2feb7cb12a9121ddd54807c1587bea92c8ec3bb40d154828071b6e417fbd5511957577aacf68db03863d4776bac2cafffeb84b7b9182131aabddc536355432bcf0f696a9aa126619489e1c2f837e56090a568740e2d036e1ad58ddc669f3eaeb25d781b0ca4a2309e5ffb034be64faccff6797b8a4fc845ba4f183033f1ab983220670ffc2231999ae83ffadb834c048dbb4d7fa5ccc3bd941084927cabb0c7ae5e10f7fdcd441232723b42a53c91c716cfd60bd1371d858f58b717353649911cd818916db570b2c071169ebb7653c9c590d438783eaab595ea2702f0ce7b618fb342fd2e44324ab75a30a4e6d5d41dd8d926328e96f99d871e4a368dbdd0b747bb8532b48be47f39a153c69b843532a8e93d7af5d643312d8e5b5a0c4a25f42219acb58f2a70185476e6e08384c5c6f49b77819579789c11ad80cc72568b6c9e9ca2dce874dabd6bba4de1b94886a49fec7cc5493534bee914bb968949d4e9831174104147cb09ecb22bc02aa12f5f38d0b5f26f0053046901c43f244c279943ba89b11e1f1c79b935d56d06f6bef29bb9aceaebb31884f5044cf3a013a91e663af156b02a26eb6f83c8cfabb0ba9646523a610d6fc98009973a394195fe11da460de87f866fe5573402d801d4f263ff9965a7280e28bbac1fb3a91532b116666f1014580a361bcd554368c3ca907f8255487d14c049ee329f36d1c965f0fb871c65390ce44bb2842a5678922df7aadef656b933226b0e5f49f1a87934e662483344f5b90192273be59a78d50521104002e0bb4d70665d566e48f7448cc15fea67bc89905dd64d560c9edc3772d7fd0fe79f04e1e16e153b4c198788d4958a48c5bf6907e77553abddebc4d936111042d296b4a8cc2094514829e441437056cd6627d8e24d3402595c750e0d6bdaccf61142b9eb40d792fc1263427c75c4804ff7a085386eb563553020d5a2d91f2b938c451d0e1e46a5e477f6fdf6230c4eafbddeb1e129692601de45a6c59a2902133e5f6a230745579e1ee33275b7949954beb877d6213b8f9e936bb6029697b62096eca3b90903a67767f7171721fad61db7398a629fc90e9e847224950390feb0d01424edb4a4e35b638e838aa595cb525d25225e5b021ec60f55019655ea536c72983be55d13bd4e96587c7ed40f3b4f505f0768d27f3233e3826a779b6da96ea128905ab30ae2dd9a9274f0d2b6d99e1d25bba5b08dce4414451e17b3fa4c6859ba8c4afc647dbedc69b6fc8b76d45f0224eba4fd5c40dba0b542bf719df330757f504b21a1aad7f1d401e2a5f83d2c6d2def10155855e5956be4409a483234b7c34f32756ffc36f090c50992fe760a384fe8b8b759e16c0e65b8ce2469aad7cc75aa3126b186eb7be7f494dac0d612e66872435f5ae50ab6971f21e6e8e22a4029135a61b7984c9f5f7c4e18a0410c3badf31e95fac7cfa3d154312c3a4682813c11462febbe9dab5402b434a4f05e49f052ff85a0d4f45bb7192106f45466f5b20c3c70847587331ac70b6d0f4bb56230fa6e66c50038cfb4624fac635162f6fb87cc1cec3726c00722b670a1efea68938df478446285c70505ffceb73aa508ecd914b96c4918556a269d38f495a8251b2b0be5adb91bfec46f849b060967363139e2d45f127b22b0f8439cea55ae51766bb3b92cfca3f4180bc8a1ef7f548bd86c4cc348b8544cb3db6ee5cffaf1929356d9288cb80407e847082711ea1d233184ae64a78a6a46ae59a4510ca760ab19d7d4e0ca3059ec005c0e864e5589b654150b015d2d314b519eafcc0b80c768df4e54836a95ea3ba878b035b0a05ac8826c5306e3659bd8126f5a261dae5b3851472b3cb8e118dab83603a556232584de2ab5d5cfac6d967120e469e0a249c2a84e7c2aa22391db067a3a275dc2a092b81d30f0fdcbc34faaf08bda5adca3346ec6454a80d366e899c2f7cadb8f4033ef6801f4ce7505fb9f5e9297932d4b73ff34b69074d344473edea2d1c4925f5c2b53c8436839466313c99b431c4a191275219dcca464842fd4a8352f4bc28ca6816e5564428a218d1396eb5f4fb77fd978bb38af36fb6e95a205e34cd60cfdc31d8d3139427e3707d7c278e09dc9b8a779e465269b1a008776d0858f259f86cb188578370b958c4bb8b76a169daeffc32533e6dd1c2de30d379e0bfb66a448b0f3060859cf1e061ee028af35bf19db4166d67846564c600ee1b32ed8ce2f2804d5646aad46f6f8777af521c4dd6a0bb77abcdb88c74ed3e9aa987517d220108b1a922ebe9bdd0598967c5d22003d678dfb92172a40a02c641e9346e75e2312cd96e7ac7691062d74ed5b2011007f14b9d6efae4e95b3ac039efac873f07191ce29a3a77aac664ae8246c66268b102f78c60330563d7a56c026214a8af6f2dab6207bcfa4f7edba740107d51fc2eb52afa264c2ddfe1827d1a1d7748cb4445b2f45b4bf953239bf7e60864399f74f5d24c1561c1176b8b564bfe086fe44fe158da0ec3a66ebcda0345bf73b5dc84a4698bc1c3b5bf5181f49cf3c818972d1d5b58802a4b7314b83be9d99ec89bb4015c386d96e87e99b3ced7eee25b9a7a22986e0843d77aef9caca912b5d0c88f55061380ea1529cdf84a222eff2e5864596c8cd9124646384f0cff8ce257aa2eee2dea3c3845c968e7b1c794963ab7e0908141a0d262886a99bb5fb9cc2ecd67c5574ffb4e7cef3e257c8aacc503f343ca9f02380b6e2726d982d1591818417dfa0ecca45fde0ed31a94b84e858415e52e22f5b9766a6daa8ec656da88c25c5c79700a6b9a107647fbfc45bd95232a2bdf60db1fb6bdf309bdb0ccccad1f11efb5dc1711d7cc3edc0caf0ca1909cc8283cf2a0042aec2a386cf7460c95d20944ab34213b702d777f31df7d266c3b12e63de1627802154a13eca949db37729d8d5ea9638b3e2706bcfdc5f20f2f822940ccc657083040464a9f93aba30924406880913b6f50cc7c28ca331d8d1d02f0e1def8a485517c0419fc11c39e23914fad2699e57923472d7145fd0f06afc16b379d8bb8a69cd0273254d470cf70030ce3fa02f1baa7213f2c34953515ac2e0ec98348f36fca03d91e8697cfd74614cfccd74c7e4798cb3d28bfc961136e2ac71ea2aebacb81bbb0829a0c07f672be14b67c03d8d9f31f0e2f079bc71de86b8b96a0d04e057fda6d35a7bdb49db820fd89817a7a9a28a5521eaa2d59858b96cb65a380b589ad04836f715661cd76a7eb5fa6c9f6d0a2d59634a960fc11538d24812af0cd6afc0d4b093f1801b6d001c24ce443e21590c524d303000805b45bdbb0e03447da1360ac21ad213a6027bc06c3e10587b4e26967b33564b96b530a58e6a5a8051ca179fbb5547e2e57a0d43287cd018565b6b75ee05ae07b8016948de2c4ca919a920991e025149b093a8c2fdc9ff78c7cc9406c25da988d7193b2bbe45cd379fed3a6ca7c322b05e4bbe017f1f2fdd3b2dff9d641e99a3d566d6134550196467e4326a20f8d1b090b8009c3633d80e776b2179d52854d461e1a8929d1e5096dc8743cdd1cfe7ca31ae14ad046ca127e58a3097e81c1dc164bf8e17d4829cd8de665a551a0b1de6c4d5e094ea71ee568863068de987841de2327e4b70d7b87d846fc1e05d1939bca72c93146344659f71332aa2166e1ff3417077cb28429b10d1c22da1a2ffe7c673ce6ce8fa1e66f22b1e28fbe63a915a507b095a8aab9bd16244cd369d39451dbb5db4145a583f48c2d2a7365424c4e77ad4999163905a029b4ae74f9a8720f310a7d4012a44aec6af7f92c71f53f5e9a9d92bb8329157068338468af607b2e87c5e3a455459dc62ecd7a00fc3a18cfc04b5e46c352bd863720b26a9369ca080274574e2766041555b8cc21515907e700c78a89e0f619a757e999b034773ba04951e3f7b70ddb668d91c8c0698815e16abb03d03ca9813aabf069f6ea2a1583eb4ef5288d43257e704ae86f80404db7822f605e894c5997382d6adf78ad1d86fec29589e76e887142ca7caf53df09fd1390b84365b0ba89bebaa1aca0fc5510538954961b137296bcfa152db73535223a74a4ebbf9d25eb30c726e450229ec2bf5a7d06026856c6281c0a62e534a486ea8f80fd2ccf5356a8e50f54fd2677db304d5220fbfcd29548d41b91a9971e84594ee5bef6577bec7127915eb0d24ac0c82b0d207e565d5b130d4bf2f1b50230b381934c79ef5cea9f8ec62c7344b17c91b3fbbf0f85753f70ebdd44d7629623304549ba870cb87bac1b9343d114855e38c4d9ada3d145541990aeb857eab77e2f70e400ff1a8bb12a429b3647749339e20a044d26bd685974d10be43e38266dc96e9c543670040e9443eda9b180d4a8a7d95f7d05ead92a4ac3d23723de7658e57e3d5693a945dac2d5962852c65fe9eb597ea9b06b3395f5d1132bc756c03a1b5da16d1e87705053bd9ce85a9d73412ee9aab365298c5cc7b0dc456282424934ed9746e084c088c62ce9467c5b0b06338b6dd9215b763372b481fafbabba955234d57fb24d9e93bd9e06afca8855a6b057f998b3e58f3d5902dd95cca4b81591a7bd3c8ce56b2915d27dcd51679cf75d43925efb841a040bf357f0d0cc86abcafe2456937483f7d5b58ddd0dac538fa3fe2342ae65669f760a288f9f523bbfd1e24dbc76b196aa82164a287a63a2970b881454a346aaa0dec14f963cc01bf266e801828d44584e73d3b94320fcaa79691c947539aca8a24bf6d9c9c693876e5f9cf7cc807051bb4d4c21b53bd5da722bf7f58a36b964d03786e4f165e04faf96b68a74e0f09a628f297cd9c348275c32d1dfe014aedbd76714faef4d670835d13d71bc14a868a5249d5c94c8f9a698afdca73b3a0b8808c4ed165f9a5552a9774963112457136bc5be16f2c70510dacccbe8a8dae3b917518f6af4a772d573e18b947a8af60a601823eb85ca0d83d429d7ef7cd186b6f555203a9729c067e422178ac293c12158962c6c03842f021ac12960562090d1930e50943f6b202ede345de177ed4ed3b36f485cc931ca5eb7bb0efe1be5bfa857a41381c089f7c31ab085328e55aaff6455cd76bdf74cec0ad2a706c47222ff2aaa7de08e034f88fdb6c9265b4a29a59452060f064f05e8056fe9929f4ff23cb951a8526506cdb77ea32d50a8e2350d30dfd61da62ebc4e6eb2a432f6d8715dc1376b49e5eba796c7d66bcfadaed88e55d08d22ac42d349a0c736271980c342bbea6cad5334dfac3fef37eb37fdeb60db21ea53fca9befb767e4c807dfa1011ccc62113037aede8fa0875db05e091c8f49f3a6680e9c8fa10127c18e2c310128c48301f307d82286c1c8eeed8b4b3761405c2911d7958a2322a53b59487274e4960c4a06f735d8ad7a4d2086b243e3b0ce86bff94e8db98f08baaa331e85f578200d37ba309f37712d276eb49d5f1f873c276eb2e36055d40a03abee3766fbe0c76bbddf62bc6a027d0d7fee3f9f5f99e6f9f3fde68c2f6ebb5ea288c18291ee79194c18cbf30e6d66ef7495777dd5eaf1e23e98e3f4a736b0783578192086b6b558cf17d0d773a9fafa3752c924915063d12104186e973249201a62318d62740ffdb19323d86b5adb5adddee2b1d79b0c9a0153fcd39270913ac3e4511767392f0ed38518281528abf5efdc784f9b583bd734ad5ed18e38e247debdfce6ece4942f50e6e9500d46f55013c6ab09b9384e954ed2f65e1ef18c3c4d7516b521b7c2c4c9f63d57427a4d2d26eb77bd2a25d8f419fc4fea3a4a9ff589faf02d3579faf441f8fa41d2140abbd38574defaedbbba1edbb51706e551d7a77ea2675e10d476ce709c9b945e2276047d269cfdcc20ea356af7834c13a75183a9f9f801d61d09fec30ee6e97f4934713b05787f143fdb6dbed7e97f809e01146dded766fc71feab8dd6ef7e16eb7fb3a9223d8ed76afa34ffed011c9d365afab4f479d12fd3b924c33fec77a0c134f3da9baf730ee6eb75b7bdd8f1d91ee6e8e2aa04e794cb79a3e7d822e9474da6eb77b3a6a65ea79a14a7053880b7bc2f0525532bc33be513c69664aa03ca7f9d9c0565a6b6dc38dbe23377e7829a648c1a1e6879427301a3b8ae8e0338315192bb12d3562778ee7799e1785a40a4d1db4a2744520ea4bc8961874e4889385871a58a21c3132b342738351394dd92f1b5821f6c2b2318354a13903121c7632c49adb9c1ee004e3032d8d0b61d6c4a0e5c0522bd346cc6c5d34ba72be72846c25192a6709c207f69ef09495f6d219e79c73c6b203c9e76c85735416d09c78a05f727659da861cb6160491fa9a210c980db46e121b1ee440eed54a6b1cd3ca87d5102bac2dc46bba57ceb060396d3569af1abb26298a95f31496016d870796730e2ffbb53515f62009db4c59e51ea2c072ce5987a5b185c5c985fc14e2c29e708642e4d01e58df7703dfd05c36ec9448648e95225934cc98c1e2a4c79490106c8cf1d8820108a719dcbc91e2c3cd072288e3a508950f1fd28cdca0851b865c2a62419a3cc881dca93cc881bccae9172cb2b04748539c1c3abc6e13d61286858700a283e389d5cd063872ce19890ecfcb996a4e7d4d894d8d4d954d9d4da5e170ca0ab03a6269c8c25b6bad776455a1b9c585e5c50eb01c84f7de7bef1d984ae692cab173e50712ec39799003b9df9cd6749855c656396e6c9db5d65aeb2f3055689299e10c7ca179d72d87f94e799ee7a1d1aa42334ad797d818129ab29c6f50ac0de5fb0a2cecd13da692d868c0f2c2ce9a929c73b6a17b6435e51db308980f6b15e2c29e30d4509e3170669f51f3a4425cd813864fb58a191f39e79c438be79c330e1daad0d48ae2bdc0d1e520543583f2eaaaf185c52c3b612a32990a4f210085302d64de10b1b1a6254d142968b059ce535e235a588295c52a889919179048299b52662fa4d0bc6c28d953daa3d20526c4ca856bb5ad9688588098f51045446dcdeac992303435ed2b2aecb1c156b69293af88e11757c6c23651c07ec0e94aeb41447b29e1f182f274b5d65aefc0aad004436676811d5a0ec218638cb1a8d010c5a13144091324374c512f4faed8b8e0799e6725aa0a4d1d3e866029c9ca19b0b2b51583b132ca0e0d8d0894a3acaf7cc656b87a54b3a979430e0f7220c7e15b67adb5d63e7455a1f9855ff820860b71614f185a5d1e39e73cc40acd9d33946faf365f61cf5c43c32c076160a69cb9b141a8a992b3508554716ab86b1ae7d9b5eb8aead2f1f365ea862e555e806839d124376429bb6d74641bb6daf03ccff3920ca942134b49d6169705926091e2f9408b0c8e140e4da684d1209f3d0ce41d75f5ef08f28497f0eff2b8bf8bb3847ff4e6e23cba5f0924eb493dbe9d82483dbe470bbe5081e98d88befd13fd8ea4ad033109e7b89194f1382fe15714a3fc48bc263cca8f4437218e4b23584186fbac215c17dde623d82b562775fc7a093149c9fe2091257cc745d1667331898fff884a1d8ce4895157bfc7af9b709b97487ee867a965f7624fdfc7c7250fdcdd472499dec74571e90317dd821d9081c293f8b8530a177d042b8a4f7ee45e41f71e9164fa1eff7c4423a2bbb8820c0f5c1c771eb8e8e2e89404b799dec5a524db93e0760a1fbf8e134d8ce4f5126f93d1236adf587cf2230fc62437e711939464bc55a2494936075d474c02475dd55510c484369f7f9de66c5e08a4aede4692c9366d22dee24cb28ee9cbc84cb8aad3e6b3bba7064d1ffa219c4fde7e7efed56fd4d5cf9f4f1029467decd99132fe3ce72ae2cff3a8ab3bfee9781ec9251df1c98f76becc32bd9e5d0b923ff1b3fcd2f2dffc6c37193b62f6273f5a41860374c61de83ba3d3ac21507ff2a31d9f479febaaf6efbbe988f3288b641593643d2e25a5907dfb1e5d909d8e3094ac2efca414b26bd7a30bb2ef11468cf0a92e7c3a82a54bffadade9eacf1a34f7d6596bd1a2e0a6aec1f036ab8a15412b705668c807ea4655450e22fa6e5a67d7d1c93fd6ab1d9a12e2b04854837c070d4109f5a894e2b5ef3197dd8eb80d4d09adcd37ef8f7e2ac8a3cd75f5ef6d86b7f91fcfed5655648cb348a9125dfbc93e9f29b7b5bbd8a31e993e8be4d267d72299fdf6e59b8bebd86d48b7ce4db36f6dc2fc3cdee8cf1984abcfb086b5d65a6b69a5420ce5a0eabed65df53beae8db71522f59ca1e849f6596acf7448d5d5749ecd5f55d7a6facd61393741583fe2dd9e6f644281156f0a044b0ad306b089e7b62f5db6f9deff883649dcaa84cbbd6b16fd4d5ab49fa4e3576edd397e8d2d31208aea75e7dfbd6d37c5bd259cf2fd0e9acdb3c46f53ccae0698ceab6915cca696f73bc455db535ebea6b9cd3da27282655a78e41a48ce9d674c4baaac017d709d66cc23c81aedda7a01e93aae7d18278d7a01f3cce5183f926d0f4eb79c612b02f62d4c7a3b52246ca4bd8b5679164fa3c7f89f41c8fba6ab361512242e8ddee0f21f6bb2f98cb9ec5189e5f18d975da2df644a5f95a544af176d4d5af172ca12255c7e30573d9b32761d7d5cfe3985d571ffb1ec9ea7be9eb579c382425ac0ebea83d385bebf883547dfcc17e6fd97707209ce59b45cf27b8f1c6f72798a95ff10af200f8595ea97aadb7f92dbd90bcf5ebdfce0ffd10da2add493afbd775f5c9eda0ef7182255ca4ebb691b479f5adab6ff36f484a9874c78b048a4950d8595b5b5bc339bb4dc4f7e623ec55242fb9f4365b92aeeaea83180841ffaa67723bde23597fbb57a3fc0824ef7f1ee547d701e0b8346b08365f9a3584ea37af3e82ad22769b98dd139564bc9d3f95c4a9777cd30066429bd5818b5e502e2d67470146418e0b6fd43eb2d38d58bfe3dd7fefbd3731d6628c2fa698e28bf1a423a65c18638c31c634bfd9b1d65a6b6dc5855f3dc6feea2754b7e35093903ecf4f33326d8ad0a8b1153c61da396647d2da91720de120fc7e9657a27e3a53a5b71cbe09e4d488d953aeb20a98a75c4fd1beb4a2e4c96dc5aca472f6d689e8eeff05e5fae9942ad13525a0a74e9d9e5ed15987b2c2595ad1a4a6547ebaf59b751c96b78ec2ab8a59768179eb406fb73c9559a4442d28012ae5afd73bd67a6bf5112a9d32595c914e9945601d95f653b7930575ccc23781b8688b4a43389d1a992c54a04e4f8dcca2e92c54a05366d1747a5576ed9e725d3de59a2c5498b3684af93b2a5d9172cda239d4249ccfc34565b2983f47af348474050a55b24c21aa02852a5c66d07cb2443f4b32459e0ca7e0286e4d787f966488fc0b7808cdfd2213e47159645ad0f2659f40b4524b2bad14db4aebc5bbde6a2badd4d24a2bfd284833ddd4a3975e5ae9a596567aa9a5955a5a69a5d8565a2fdef5aee096c5695500e66c0ac0dc5cc19caf259c787bcbb9bd53800230972911270073db04604e8f71a19edb24a802fded2498b3360198f35a94a08e718d4004604ed7b1253c2200735e084ebc70020463521f70d3e38131617a3ae0c5c381314e466c80c4cf72cc11129ea701132898f8598eb132918131347830b07381bdd366a72ab7b0402de1a54405c05c1633a5870245ec1056e5cd981a54da1ccd29b4ab5e7c5759c504ae18279f04c05cae62b04a44404c90db096272801e04c09cb63c7378906e6b6e1e3601cc691f311f17602ed7305478aa4ac881396db5741e400298cb340c92f0280c91efc3f4b079de083a57228491d2a9724003be2b068039af0518c209ab588039afc502324f199e2a05b0b02bb6f7c5236d2ac09ca76d08604e7bde170f84045c30595870002330a72f98ad112c30402d401198cb3e477c88866e7845000d064810cf95e709008c140fa90390d33784131e01c008e6740827bc126d0827243da7c9f4785ed61f8039ed0198cb148c44f103e6b415a2cab6b967f34e8039af0af1e46301e8f916e96b1325c09cae42e0f020096a16b807869e9e1c2484d8f1d8353b79841d03c32f60b71d9d9fe5172a3a399d45ca93c57305f2fc2cbf64f1dc2e7804ac9a5305b6d9b6ed67f98588cdfba263c4fe3263a785ae56cd56a9c837a3e52b15f866b47c47941961c190a0ae6739fc4892033956a094399a5368d35ba5955a5a6da5957e14a4996eead14b2fadf4524b2bbdb4e8467b80436442ec57af303ac28e9b2cf06ba2a220be7a5151d50f2222fdb3f4d2f57a0f81391d6a855a679de990171bb6b641606e5b2f572ee8bda900c0dca640800ca06616f20bdad620fbd9f8de3aad12a2ebd7165e6ab8c06f9de2aa21604ed32d04e6b4cf522c2dbff53a03055dd41643d54bf3be14b616f583efbbddbecf368bf22cca0e7af0ad9847da5180df9a2fff5c151de4b107abe63ec102b7efd58fbdb5ceb3487b9e45791669c7b328cfa2ec9e0952bf76b7534509d0451e730394313f67d0fde1d303e6b405261b1cb85c59d2a6c6d8d9d19c42db00604eff6774805b058180b9ed731655d0057d17d6d6eab9972fb5950073b69c776f60b75b09f8a9d7122d7c6ac8155ef580b94d02cce9902be41971afc29cb61f0f5fd501c19caea012f08ade28a8048401cccda2ecf7bb4734053df75e679db5cf599447bbb5beaab3287b9d457a1665a7b328fb9c457ab4d9abb61f90efa330dcc03c6e16dd3cfecf52ccc757c73f4bb12a1a0474819f7a16e70b600e8435e1ed672906f533df9eba531f6bbe17d605eda12092756c2faeb859441d378ba6634ad388cd781223e769ff2cc3da8461664d62c29cc48499691213d6252c882c61c2a4a6be84b130f5258c888d2f6147a4bedc301e5e07a91b0bb69f651891c79561476a7801ea92c5a52bec8bd70416c407e6a50bd7ee82a509acece2e6757e965dca3ce946f889cb962d624358b0167bbc25eda98aa5a23df8597249c24506babf608489ddb834e9b02a3a5c747e965c5830c285446e7b5c452969e28252a48b930b45ea7a9003b933c1ee41976aa77af47ba9950b632ebca1f1264d7339a27180727707b23f4b2e3c9e74231c85b9e8a0c1858a06d63f4b2e505a3603669415fba8dc5256966a4358f0254be811e40895314ca444d16052394790148c703831c61ed2788c4bb0a7c74d47f88e55762ec1d6bcf7b30423b36686accc6d8354e0b2f9da7bed9b28cbc6a986383acb9e565c7aefed962599bf77a82b9ca11df3cecfb22cdff6b32cbb7096212a5f52004291b5660c884a0a463e346839928291d84d6bbf9e97b35d6d133ded83745d7b76d6a09bf6fc529c8fe3dcc7e636dbbdf4ba09b789267c489be3fc2ee96a7d1f138e73138e53e1a3a5ebe95f2d5baec7b7b82392f7775c4724975ec74391a42a489453412848823ce15e0e020c10609ff6db7bb494bdcd7b2ee882e9716e13afcf6bc9d0b7dbdc8adfc5b93b6e0cd96755412fd5d9b75e7dee7105ed19037a9c56c80a51eab75e2919ec2d3390d82d631f080a7806ec50ef06fa8e488fbe0956a082db77443aadd075901e55318b7e459be79b45ed37f18a3de3d4592c5b763e2470bf7c73c6b85c732ac0569a53bbd3715352ae613d39c5f0b55bc7dbbf4058fdbb4048ddfaa66ef6d55c830098945d19fb414a199b1c46787864d4a2e6b8409416465504d60f31305c3655a9e6e3ef258a23809fa5da1335264ee4c08173f3195182e7629c091d520f4ee40e7676b5811fa4f8f974ac93f0504c5b7b6830a76a3787aac6d69c1d95cba91116e1b4d3ce9aae579a9a1a358d0cca2b2dcceb428b2a02088e0808341f3888aecc1e7eccc131258e952c376c1c695d706940e0f8a8d1dc1ac104fc00132210e44035688407a469a5657d0e923c693425f4668892064395b42a1fa8421ca449ed90e694b5c624ce12375c663a1041c31054c2d42096c3161c80e8c1ca13ae1bf8a0c4890d9868e98065c30d34fc10e222368478089220497c34b1dad2c1c3d4912828729ca4c8d975a53d6944e4240edacf6aadb571cede5a3b9f9c5b2b6e1dbd1de28c11051152a15cf587195fa3ccf8ea1fbe5dbf275010c484d6ad6b3047b30e9b3c59648c9da4d41bafabc0f4d7317d4ae10c29e3383db9b3d0b6beda6aadcef4aa6489b2d8fc6d60cedb1ef69c8e6412f6ebf9d314c479dcb388fabcde2c7a018b152b40568cea0aac2535d850264c0d607b6ac43d6dbd600398dd0b6bcedce00104b5e3e7aa488d16d09c9419b3c58685001421bcdcdc20062e3548dd70e81143181c9c68b88216150000881b6ac7d5e981c90e438c64e192b59f7a44e07186eccc17133332ec1aba0612235fa4685185d94e24ce3491e18c5a94263a84d84d3ce8931f3eb2667003856ac8a6915d20a64717acb118ca7cd93376806ca95135264a8d72ef78ec10e04401b91a0325e4c99a138979099b51840d5f566a1e600992034c4790afb1393c3e48116707355b7478128395b41461b45a984104d85a0c7276a000f2c4860f1d48ba1479128707cfd65690355a3b9098ed008747070d086288ac356072680305ad040a2e4dc2d8a80147899c227cde802468c264cc4a0e6590b81971f6a19064ca922142e03809d2c684561a1498304b98e4c06687b766876d6681c34406316084f8f2658699587821082a24438ad090a5e644106b80f08222c30d333748c3234cd3c30b674d5ac072068d0e9117998eb4af1b67dea8e932a22b4195cbd75a271065b26d88737c31c645583dbe3d38b28a7111548f2fc618bbc9222a52c4eebe41b3d68d9bc73f4b37664f863e2767a218c29c513833f33c22ced0c0d24d0d1c4432e833448c8800c10043d566ce72d04424c428c4b90715a0c9bd2900110539d33ad3218a5e7b56c313d190eb40672c9cdd78d28d6adc13fe555ab3ac01332b239a5aa39bf4106bc8f29a34529b524aa9d2b54e73e02caa3e8baa4fb1cdee6910ce928dd9d7323f59cc59e267c926cbe38a588326c566061b29363b9ed8f48022b2c126091b18b2d7b7066dc6962ad9ecd03f4b363d2e9ee19b75d61e280be41ac1b71cbe9f659b27f98631c6e2cc5d2bb6016b436593f859b621a36de46c9d1fa4fdd6ebdb919cc1dfa68fa8ca66d1c5b4379e9f659a1920ee6799a64a47fc59a639b293a6eb71651a227c843f4b35509ee7d93e355b267e966ab04010d4d9517306809fa51a2c5fcf8d8afeb334737adc8d8a297e966633f4dc48902041a2c7a73413c34d16277e96663e3cce6ff3fbdebe1d4915be7a6abe59777e6e21b3f138141f02ba6e10451901114519a1f1a25243d3c44a0d8d112b35ff59a2817af0675916a70c08b5322a6a6550d4ca70a05686835a190d6a65576a287e966536ca769ad2595429d2dded763b8cabcd741a00dfb7f43f7cbb31ea67a46b2bb53adaf588ddd65a9f1819ed8c88661035116146040d22a7b5126564b839738ce600cda13866bec8ccc0b9beefbdf7cd073fcb3354fefe2ccffcc0e5242fc1676a88ba6ece5c455937e31918a2dc9c6921cacd191e4e6e7269268d99312b381c1aae88331ceeececccec86eb41650ee4d8882dc847960324ab01e776ef55632b23d3579aeebd60ca64b995f172a30c9781722b73a5ac24f3fafcb3249bf2a41b8140d839555427edb573ce592b8cfabafab5c5bdf5ad935557bf7eb55043656988208208a3216ae08166082238d00c71e3bf9fe5103b3321ca6895917143e68ccc1632568e944132c496d9c5f7de2f7fc9b4fede92cc87bf40d59691d1e17702949ba8c4eb9a2a6a1022c50810000000c3170000200c0608244194244110835cf8011400095a924e543228a10444a130140c8581280a621808821004621004a3108603599e4379006f08f625a52f948835d9a91cf4e87e805b4d6f4df601d52d4ec33bcf3d2d3cb2ef4fa9b91f13e453a7286cfd7e8fdac537da566343a7491b1cc373e4b609d88932d64caf92684f9c1034e1d0a249b244afb01cd0faaf5e890543d89466a24a107bf452882f2f7655c21e63184f5a1211b5b7854fde71975188f7dfe82e7626de93b73ab7205a3e88db850b9ed2700fe052e8227d43892d0f9e0a890df5a8292dfcdd9c319a368becad0b221fe3d33c420a543c767b20ddfe5c764e092255a42feed35312f4bf3144d892cb770951a74d32648585e0da50e8ab43760ca2e7ce409b4da4706f4b8b06731862c939f45b5a5aa71b3e8867c6f5ac641db00cd6ce4e1b222e12dfebf302f45ee3e2d9ac0cc6ad6d7d4ab823b4b06a5fdef9ad22c8adee66d72d36d8ad786c782661414b245898c148db51440d011450f010ee8398131faff942ec5208f7e5ef2d1ccc21a262a20cb37a500841a3031097ad9f31483a2882124a7d18101198160045036a53e505710e7a3168e594c5dd7f64d1398afcdb0fdc23f5ba786357702997c9c5ddcd6d5dcffdded81ddeca7bbaf9bbbd6df7bfdf1bbb83b7724f377f376eeb7ef77b63eef0566e626a3f0e16c337fbaf1ffa39f18cee60f6c5246bbd2b22f01f85bfecebee5079eafebd571fff9aad298321a14848d670ec65890022d8cf7d4f62be8c0b3618661945fbb2a405c71d2c938fe25d95ba286e9d4f2331374906c68f5be2daa789e8528d054e053941107c14d5c678e05c3ead5a18ec07b3ec040230766313faa8069b20c3fa51d5e9eb27931cdba7796860e2f58cbbe8ecf560470f7310e3cdeb508a3d87de9a87aeca0e102834ab3e11b9296e0a3a9b298010e095f65f4ba53c2c2cf8600b69d2458dc159b07a96aba364a935308e1d0268d2e6f9bf189eb43cb4d61926a1c0073c3823cb6b7d376abd199cb3532de9876240c4c7401d9f8193c9b93ddd927f3807637c0cb07eb8e2639ddfd99c5bd22df986b29663626a20a30ec9ce7826e278953010f7f2300f5556f619473fcf97638470f9d64532c1b2e142876991e660667e5105037cfacab5670b835c418e009bd1fea3aadbc60b0118995b0b90fcd06d804e1eaa2005c4baa5e0ec0a7ea697e289f063f44252ae61e92d5069a44c3f3c0f2c192f8fa8edb0c10b20be72a73b5ef8c765af2c9941fc0a8db7d2d321d8214fe2c548ba653ec3cf7c25dba3ce1155eb54748f790bec9e87d2d11f41e83414c1a35d1a17c99e47ac3bac0a1b3757a0258a3aad9b1f0551b16ddf8498b0eb2715daee53d45e4772fb3dd9fee4368de2ea00fd45bf955bd2b4dca24bbd71975b113df686187fad43635358bf0fe976cb36f3cf86a3de28fc03cc35f141da38c9e6450a1f4081578f3f04301d68b7d5a2800960bb15471668714417bdb2039d7b2d2789f11ef588a2381ec7f122d7eebb1c3157087cf1e884ef48f14b7a032a4e607fb04706330273e0dabb033e68fa6725ebf0962426a4d04df6f55eab0db04f1c406dfefe849aba0c61ea22ffb307c79d75940ece2f8cea004b9826b80a72533d1a795a3244c3fcfe326120233d650a87a3b4900d8ee18e7b59a8f7ad35e513acafcdf96cc1a5283cea440c46a326f85bc4d489b05d0de1db35b54b76e4b31bc76b159c3f7ff51e1660e9e326cfea4e88e5e376b2152df47b7386967e694e57f1775a680f416f8bb63f83ae6219b423a1f5739e91e0f645fb4b84a2fba1039459fea0e70a618044bd52f66be2a7c6b2e2f8b9adf6b2a3fe8a3f2f3a7ed93d59eb3fa9eb137a246f69f18415f05ff76397fecb955367a248a54519099e6bbd81a0b21fa5270b0b85ff6e7d993e6f75b7f67aabd8f9ed41485f237278ade530d445bff18c75b85ff7fc2f9de27be68f78e665fae71601be0dccdb7ee91117f18fc46ee142690239f18177ad42eb53847210a930bff3a0d56d72547cd9fc03a30dbfb49dabf82fc7bed842ea0f92e9d0f90d41b320c2e329d4690cf682e8ae47866d63c0c00bcb07237ebe1a216ed20cd2e9570d3efa86ff968e2f962fa923e2b15671157ab70c8fdbaf07bb229e33c2d132922d035f529dfe292dfb8956c26c632bbc66fcb21e435e7409d3d27da04ed2aa043ab300a267fedcf7a96ee874bdf6ab1125ef22a33c8a36cdfaee5bc0305c94604e52f2a4da592a6a59c1f4ca2c583f0c87459c07c344b4a48de1dcfd77d0f08c464f42ae4e62002637cf83a0eccf104d644ec545c9582047d19b027738d00f2ed5510b93c27be604146f3d95e207f8dca8eed6cee43786779a57831fd86de207a99c683182ee11bb6e1acff97e521abdf913497f063ca2e616dddb6a2bfac013dc1a982ee576bf4565af01f4cefe7412c19ad9dd912fe737913110040c4de2d85ab80db42ccab34779d582a09e8522a70104c8d88d12751c020224d481dc7651f0bec65e745638835c539c06a90165d6def00526e1a641277fe3d17aad37c3229f345c2deee09107fd449ff066d17201a95e194cfcbed970359ecbd949f8a39a83fcebfc10b75d046603bbf8b83f6dade6e002116f63d82f71f9c3784abc1c3f2a252faeff43631d7818bfc486e057c117587713b4b5b11c6989e10ae4f565520ef49d5811721a5e2dc8033c90f31299b78b83721a3a39dff6dd94a441374635b66b3ca41fa23fc105447415c0e561e45bc2755ea714402b7e2e1bb4dd55a6866781b6dcf21d705221bc764621fdc95844111d660191010ae80eb3170c3a47aefae9de817d421010892f9af0af0086fe30884891fce2f9f14f4f8cea0edc00bc96bf4a3e32704131b6aba03055e1135c25687890a19cca4b8b63f20e7842048daa4534863b80e3d39a73c03d12f49e8f265262a256f36651730e8c4872ff782a1200097d92203e24e1156ff35937922443dfac89d4b6ec80164959c4373830417aaf5db1ad5df4b2561695b89b5dae157857c1f0b69635107800919e869959546152853edc074579282773d03043e8f9ae5d1e5f4163700e5441bb6fe4c9de9a663dd70d03e7b0915db4a6fa5c582c71db6b0a0948c4ab3b4bdf01d249a5cb5b38dec3da6f88c9e2186f95c3ad0425ffece3e04e380fcc166865285e67d20f4c6e4c4addf7c05fc1061f13841a2982ea494a8354d27827790d5589d111516ae393bd6087192577c8d57dc9969cb9825debdfdb12fe70b5134ea01b85b7f14ac0bdacc11adfefd5baa661de989898fc06e6592c00e29700e0b32ed9bde950fc166dda832ad7ba4331239a281e25a61337fd5feb61900e036470920aca9e2d67c2aad8cf4727f4e613dd3f02c7f802c19318df719d543fe6d3517ac181a6a347b4ebb2f9507367149ff3895d96740aedadbdbd2f44066c564a8036f731457f571b4c0fc706b476c29b772ab2aba1e8f62f9d43a716fc497b602f17cb4708555721f80ca5a46a001f17dde0fdb01c7b771e1f02d3cb73b833f601f8d5d108c43fc9479017dcf36f242a7c4ff8639b13585c9cff49e3d24fab2e47253d7f39cc9a57ad2546d5c53fc6f9516a2b14f1c31f832dee4196f1edc98d6402c949e3a1e7cee8bde2880bce3a582f209ff609c7f1e285ba2c60c6886b82d9b8558c6120451aeb7bc254adc7afa8ea87ac48a52f2b949173130f1eb32d208a10671872194d3cfd8d79fec66015f1ac3f384426c6396eb9eef3144e459c539b68e9fe79d8f0295c504e78d58a5e0377d513000b0d28dc92b67c765a906168544ce77bcdc3b5ccf06758c27c1641103c5b86f0f5483424d44cf614894a71e02801eb2fa757bbd5c0a661d40047c8752ad9087c3aa2e42ea5990d41a63efe3f40e15f44b2258e3adca0c7beb74b35873a70395eebb7b138ceef7e8c02f9e26536f1c4ee7234bf51034ce338f65c04fdc1c57f08f0a72a884b225e09833430f5d2565566edbf0f28f8d7a16097e31711f9859e7c9815c63f17e6f5e4c8b3ba1ffa74e64f602ce27fd408967b8d11264c8b0f283fbcaf3a4751981e45d2ede514808974f1599df6516e71ad2c8a0ede32a0350d1abdd6d238d537594124f384f6dc7bad8172ea026a376b5d4ce47b144f6b861249c80af9949c9807850ac769d6c7d57bb00960185c899fb79acd3dcd896756d5842fbd2937c437d3413d517f06d79df13d9d0a82a310577f76115cb7ce8f183a0488c29acfbb2270c5dbb873d63e651fc08a0a2ea50b9300430fa204f4d527c93bc13a78f8b181254304aec9919c15a1b27cf16e80fb954c301857b01e7f438effe7bd7d9f1d9b77dc9d3fe6ff58269d59e5df21f3bcf57f2cdbc4cc43bba61feaec9b6e5e360ecd3baec94f689ca7ff694bae5b1042127e91897e3842438bfb517b16cb48c9194b7d7785cd6354bc336d879323a2355751bf1d66eff21d7393752a040f2103d5635c1a8ae0c790ac8de30878b64ef22a5bbdc5fe652e9668de6c7744b258d21e2e7e9bcd199effd4858c2956fb182175e66c579809dc0b0abeac765d1b637818b6f4f32510c672220788b7a25b4e2bdd2e430f69d893881d4ca51d274e36514a3550436939f2a9c0bd90ec3c53fc63664a4c3038714a5ef0667cbf5b318b15ca28eca3c3c5cf206da3273f211cbf2e9f8dbf5bfc651ef5021145594a72cda05965ba0d5800692a8b220f104db55839af75c0ceaf16f4d499025f029762a8276271c3413a21ced80f784aed850c286ed166c69744c0bec04b0088202dd3f3cbe6a7ddab11bd9e4919eb08510d48d32638a3c97d01eccf7aff0d902d7935616a35392fd9ff1ff6c3d0b05bc362c63d466f0f087015abe01b838d07bb5ccda1a6c653a6d3d912098b8414c63a9bc263260a889259724a895f4623008181bee3bb060e97fb40e9a1c45f92876d28281e855352949800f2f41410638338aed668bff597401d05a4b7132d88b4494adea600b8e511fa8103bc62143fffc39ba50b69a32f1811fd4f05c3fb3f45e312e8aa374494acbdd5ebed202d3e5ad841e35568e081091c1ecca26a4fe73c74c644301117a3faf662b3dd8dd49055d05b1691958eef1a7cb3e95455d8712745743d274070b67654a0f242ba1e3c5b7d94333aeb1886256290e8b0383326fac8cf4d4f6855316046979c9c705c4f6d8f85dcae983952f8a5a37466220985a8db6c9c79c81308fb1603f8afa5f631d01bb6b1ed0910498fe369f44cfad48fa7338dbecfff4aacf5b4af05bb3ab2bd779c46c139175c39b00d03844580f37bbd0c3974170a42ff3a77781003b754ffcd2c21e82f7461f0522396e0d3592ed83ab75a7ddabd88ed66f08e5b48c6124963897a829c8792771e13b8ae9eb53f7d2d3b05188705678a5f4a432cb55a7674f5cf23d34994b377aea71cb9c5b570cdb2c60fd22103c79908875410eda70aa732f0b9f997feb18761c70c9362f0d8ba5294b580f534d3925f35b0e7d3d9a1250d7f05830894ef2e45d06db64355b1fe17b0a388429ad61e864c93b72a1ee94bb5c48c02941a578ef17a81c0c965375f333dce1d7af1b58a1899265dfd8d866268ed80ddb500f320475d38a06459d4879be976e8fda7f657846a8e822293dedb4e89a93c7a965e48e21b58df6d4f019480120112204db01082eadea8d3587279845f21e9fca1ff6ce0425adda94afe106f2b3089ef95e088eff329b3d69b1e68ae5c78836af78e2af38f7bf3cebbffc7ec138bb4cba69fb5fc3354438a394d721b5b2bb9218d98409ca79adeb7a8ad271abdc95e036a34de06410d5187e480ef7719a17902fdcc970e27903b58d10171efdb31067d98f72605021ca9ba6fe8316d59588e410edc4e21a5fd58687113823b94f49b6f8e67d06391640dde45ece52890794aeef96927d9c6f826c584b880d8b25c0702b888558a8896dbad6135035c70c2826bdce0f8aaee34beba7df23a229628c9487c9b814fbb34384bc14e0f9d1a8b25519c376017ea0522201a9002ec2f0ba8b1d98903c7ce8387106e0485fc0bf9a25dc0c3028ee35e65d53e7a0348892393e599761292b3b4589d231998ce01d7d70813711dd4e8543bdd9c631aa84d51462dd4c85dece81a6baf4791c0f0c74fd7673827b6e8068f4313b96f4006b7061fbd7e7bba679b3c6cf1a3984d9d318b2faeadc4db92dbda621a0f8fec3b8887fb4332ab5dc64aac7d0e9521cd3a3370c6867b3cc5846a21a1649a76734af2ba22a2852ce15b0ab53b6022d3f6f5c2c5d52f6d20299302e737ab6aa4f788b785c19fdf6284ab9bdec3d55ca22c9039e83a809b2cae9ca098013dbf489fc3b7e8a3fd68ad92a2a47ba26e478d49f9c55c0427a9e2bc970e8c80465b30ae794be41c338289ab6102a4d108059e03500fc88a65b5307f4efff528134fc3bf3ed0b3f6dda14faa9a5218e194d6d2beeef229795c8534313b9be8a29e56f3d0363cef8814a31a13b873615f56cc4c4ca0cca8013ea56a1632e52de396aa9dab565b15538221aeabdd3094e22f9554fb6d84e3e2d39fbefd30adc835b8d0b9b7b96ac0ae194b4e2612a466f1078bcc59e50bf17330882382284735daf404e55a5e710cb2f92e8e21269efacbfc9171798ed1a39ad5bc23a59613ede5be283750d64b3eb62cc2b49e8201a62a2a3f255668d4f75585913144c832becdccf64a1dba7494d94716e6f695e73185f0529457e7c967493cfa6fb365c50df115c33aef0eee52c07ffe50384a208a9662347cce083e28c48bd4ba36a9f185f4ef91a3bd85fbf97b6e1c96b10f9f59ce03468035899bd61f9753503a4d74105935ea8c2cbd206ddb97d5a1fc1f4d4558041f427a0c8219aef47a1e05109dceb79e3eccf4090d4520f599ed31346247c5aee8e09fcfdf8b4d84cbce98bf32def5a5c2c455c672ce30c1f31f2d3fdc21e5be3bd85555052d991f125e4509826ab9ff190ed51c7b959d636456f20111a569fa29075d44c58df552227a5bc2815abbca36f535b5ef380d14ac95826688542774e5b7bdd7f8675e6a0e4e9ba85368164a4f508023811c835df08c85fdf908c4f5f5c099ea6225c7a1380914a33c65c5287e392993afc3eab4a91baf82c4947ce631d09648998f534f2d2780d4f4e8b2464cdabdc7a5ad5995735a44b080ab6b82ddab0f4f36466665929a9ff34565ff06159becd0f696d7ac246c750e36d7a6f7854b9df58ca295dad97a2066af1ca4c706b341b8cd5d90c5c5dd3f85042707b66e47405c19ec9a37a7e9fce015207a761c932f11cc6aad441341f29dd6b6833d490d4c6437cd36947fabf40cf55ca5888498d774033adab5d92e766807896948cb1dd4029d3278370a24e8e28c8884117195254238d531f6ddf04d634f8239745707938c581eb95658582266563928be82482a1f22c86c4994a14c3c12a01d70d827703e090b0caabe13f34cee6e1bf6ce3747ed3835589e7b1a0e4d0207e3bf03baee1a4f88b1cd9b9f229ba0f47be70e85258a6759d36da31e27c1f017480295217f2a5f6af7b026246696c99a7f8bf49122ceaed3c258514ee00fba085f6a2bee10d69f2574c27be352b5389b1f7e1232c055d8fc5ecba40e36785dba784f59696f39dfaeb18fe6e738f2896b3849a3122cf36292fb9ffa108ee0a140b0a5ed177c06939de4ef1db3b6708707d16b094ef7ec3059a454d8d443da37d1ab0fee2bf4c0cd2c31cc6a667840df26d690dc65c78e14d956605ba29beef803b87e2bd550e101b0706cacf77832b56674d5bc429d5b9fea85a919e12f74d64ab2851f54821b9f84789b187dafcd813a4d73f9bb7a869ae048d031eacb48bee13485b910d88268bf068d753ed9b9bf62d2d2bbe9229186e82b3a24a3aa10cdf4045484ba4fd0b0c96119535c5835fbe17d821fa3e76e016d5a67755f9cf9d98010b50c95ebda505f2754c3ee2d7b38bc398dfa81e9d4b0dbb3a38c0cf2659db4ac786a216fabb604639ca7833e7d39011c742a12c7dee8cc14a3faf8b43d2a29a69c6bc3e1eeba9ac3469e4dc672b360dab155c84db6df08ef16b263f2ef264bc56092583431252e1bcd0c7e4b8b689535d6cfebadd0eb04998ab505d93e3c2713d4f74ba7fa12f429b674c3f3fd2b11369df79667840c09d0c53259edb1b9e99ed7c9da418b9ce3346b03b80d8d5ed68d36745c12339eb03bfa0f9f47581dcc67393091525429b08842a16d53c57211e606cfc9d2c64923932bb2560885f8ed8149b09ffc2837ac7f654ea49ea2a03fc457fa3c98e78cb4113763aa6cf85a6559f4842269fa96b19026a33e0a93f7fc4f42405eaae74f2f9b3de7582af42db3ad747c5ee6a72b28e36ad50bdb5ce98d2f9043b6e42de9954e15c8f4f1686ee2652273b9ec7527b800e4b2279dc242d4c653b7b8934d6bad3cb56fedfe5ff560f2328160d4ea4f30704026f3974125939392d595cc7624476ae9f66ee1997cf0d3e395e67d5baa43af7dbf596bb3380341314081a3d82a0726006ccc1f4c5ab85f70585d7eb402f2a13535776807fde8cec59141882cb347633c198cd31e6a60664c4e749f18443fcfcdbedd0e52c4d1276440c47d8ab9dfba245bce3681f83f552db95d4e9e95883d007032b2638ac1cc972e6a796f4852c5f3b69b7eaa6ebb26ca62b54bb181533fd6ee5ad331cdb711227589ad63b43254284f79fe478acdef813cd26b0f86b3b093325f2412e1bf68589c401080d833fb97113e741d0e427dda0af9721742e2ee89badd512d9effdb9cf8bbccc1db77103974f07ac4512ff07e39473de93d8593be55cdf78c88fc91575aca8e387dd5c46f152ea3fd14ca040e65e1d7e48e74716ec3a4df6ea7604b5dba0eabf15017cf221eb846a7e9545953906e3d868f366017f08f76d53dbdc550363c6ac6f57e19cb34d19acaa6e8adf510967a1ecfcfc00bfa2667a78103d3be1fccef7d0a61a66ebe4294c7563633f99eb50c38c4dee1f1fb9558e67478e072efbc257a454a716cb1fb7bb1b9905c2c55a9ce7e0f0110fd853793a1c8082feed4e65462e3b9e49eec511e0da82171b0baec5ec8073600c5abec0d3e1d482a71f73c42cfafa8f1dd0d63fa3ed483dc1b8a87698eee95e6b9323dbdb1f2bbde05f48d0d9c7e4e596e30da07422f458b3cdb8827b41d9c9d3089bd1cf1e9dcfa429c6dd5c2f8fe5f20d151761a73267e07ae3df15aa6ec951b0f65d69db94139fb9c76ac6ca00db0893dc880b8fe81e8e1f900787f90596aa97dd72acdcb00ef67438046e38096398c53349fb95a6919efac8d03d14c41b183205d5568cf86f7e36ad2ae17ebdd3abe6ddb00ab775047d3cc8807c21cff016025ff9f145ea3b2352e975cddd8bae00f08a0c123ec9cf2f31ac75f45d30ffcf9b5d65d7e7e338ff5f400e80b71ba45eda3f60be08d71c484c8268b26ebeef92ffb3baeccbbdbe0355bd5fab1220f29c08eaa40c96a8aeb53fcdf4ac84585dc372e95eb1e6060ab8c6730b4ec04d05690966679167e79862304a8fb717f4d05432a9ab6082f7d35e8646cdb3eb69a31bba7868870b7871ed44826d4fe400f1c5c43537f80173a49a1088f8abdbf171f16e41368d3dfbb8c7b1cce0a85232440e33dbae5f61fd7ddd17ebaafef2e5e06617880c0054e000b2030fbb5ba4a02073aca13eab276c1e874c376e2a2b76dc7d4923d6905b65245297165d41a55e95c30e623c0b2d8815b5c9986bcd801706f6b9261fa9b0d26c1ccdb8e283639600a9d283cc8e4bbc4b9564ef7df332bc4f9905797a26e05e5cd7af38fd655260c32ff83a26ef1746433d23dc61259ec835af1322767f06703f2e6548d858321e4ac4939f8c2c77bba97c91677b3607b231d0c774fcaeb9394dfdf96c9a899341421c90f4e14758c8a9a048d125aff20669fed7b0d474bd44caf74b52a336a671c493db06c7c92a8c1f379fbe2a8382bfac44d244fec0e84cc0d61a7c30798093b78c538158017f884aa3347092aa17ea04a0e3151fc40fa48543b01e00b85d8a71f6aefba7a299a49827fbe147f9ade3a0f052cec21a0b5de196599f4fda1f050e6633fbf267be3c63d997e12c00f9c84e322d22c6b94a226a31dff4823267a748cc81eb9ed25589685e524f629c5c69ea8a3fde99e60f68bc92739b5c6e76c6dc1adb3bbd7ecab4897e753241a8098f1c2b97dba8bdb0adca96f9ea5e608509c4d44dfcb60eb73f40254aa577c1bcec7eb270f9d07b4e5dde04b9cfb41df49120dac2efeddf431725c44bcabc887a98bc5d5bfb7e873e442bb1dee089a00b955970bcec608d2851637fa5d0c11c88b5727c11fd2becd04cbd493c666b77eef24949390b5df72ce0fadc30eccd3027f67a2363fecbd4285becabbba57688e361fc1822ab6d1046d67ec5c44ee785a1eeac309296fcfe39b33c275e7e502372bea67d7e4b8899a573198e0194ff835b19bdfc91ed42687dff87fcce6997338d735b1c019194e72f9efc8aa4a2141f45371790d2a6a4c9b5b9dfddd693f556cf28ebf758ea235268d712fd9a9f1ee0e7efbc51f7ea49e15ff3cc3a138c00ddd0559e6c9d93f649937516b4d630667a2fdc345c1bb1c1d218e5bb96f924271bfc2c359e6089f136c68330eec2d46c3afdea2ef18aaf9c35f5b12281635f88c8a6ba24b62dba60682027f826b3117365b3ac7b37da680a707a9f955f98fa417fdb1589e36a471c4b7c7f66f9cf73a619885618466a4db06782bacf095a564ee030959ea4a0c4e18f861e0a61c657c2afa8ee3a97b98e67bcca8bff16d76769cfddc16d37d163f74691e65e79b790ba4d3624cd1f3afa601b50cd72a52067077d31a24145638b9269ffae4ff9564d2388d3f63f917a95d3407850b3cc464737060a4bc6bd79937ce2498fc21a0f56963bcc95b34e578621b9246604a6eab683367bde78ecb4edb56aff1e3f11b71cc3d5bc75279babf9bf726c96ee469eb290793d075066e2142fe4d03c09f9e4da606077404f91b0382baebe1513a7c9e78fbdc9fe4d53bdd50ba2e3d8e2ad7039e61d23928247b29ed788c7fbbbf2748197c4dd173c5576c6443d630ba44f702429924ff2751666fe2d34e266eb3234fe3e9714c66aa2bfc16cd54bf02b827a6f16e41a67bb87c8a87ffe9233faaf8c79274eb1ba74fe85fbeb8c2619975e415e60df508bfe7e8c8f4cc26e34f0367aa1070505f414fd9897f828378f7993f380e0f7b2d672dd2fc5df4cf200607f63d5a4b0caf05a001a9932a53b00a8370802fa03830a95e4ce2578f304746103c5d9b605f0d6bc60793165b214dc65e507c31dee160af1f207ca541d98cc67776368152f50ca6fb84ab326283d00f220a93e7c931e615feff41bdc56f73a73be4ee12531407f25887fbe693b0a4b6aab279ee25858e139838c1ab0404cd9407597d45c833aefaa2771760946c16f1e3879a9b4f29dd8c95d8e713f11a3a49406bbd7d6d828e0bfc98bd62cf1d1bf8e36c4725fe19941dc9937e89e32f0cdba3b8946c7da54063260ed9df879c827c92a529796c412ab0c889999c50729d28f042e0112978e7811e2a5c42fffec9c68668d577d89fb78229fdd5d97238cb33259b33c700f8ca52a25209ec3e10c742618e219105eb9bb6735cf6f1fddbab1bfd0c8b05c6e9484ce5599a819779a6b81c386730a87e39ff12023df3427bb02522f80456bb8e2c7bb6d948b8810d8a80acba3a34ccfeff4e31bd1c2671b0e555abbe6b498deb0a0d7d07ea096fef8ce84e4986eb8b7004141642c85faae28193616c2fe7b76b9de48db86600b8981c5f6ef747df083382c2f47aef577cb4346547b7f43ac0f41a7372332f2f3234289daea64c95c769769895c7340c38596cc9f5e31bdb5526d9bb24917b5ce6d354c4186632a9d6299e0c8618b5a2fe36eec5e37f2801fd6d84e7ca462385a1ac58f486fc1c9d1832ebbbadc41a3fd0f82503eccf798388548c348210cf69f4dabcb6f3e70c332acdf7a5b54a022e98a81fadca535bc7440f4a6e2aa361d68e3cfc733f679cfb2120f07f63cdadf67f5bf748a71d19d664e555f76a3c7e4614c97555f232439625ab73c0d8feab4f534fa91354acc90dccfa886b3e71e3454a0ad183c0a486c219122dad0c7488d17c88f88f53d65ceab9834eaf8166a982539b69b8bb82bf0c3b30e9a2e61ca91202752277e207319a35691a4f347bf1d6fc56190a3e43cb59e447c41e2aaa2054d3fffcff323a42789e3f6218a4c4162bd1e574121b69294acb375c5da3b70b8b010bd8a629ae64054ed83d63802813bc8af9782146efca9565d7d32ef466cce972964afc1b6e1190708292ed48ba12085844f6b3a9d5a31b0b2641271378b5e5d216568db0e19c735932d15ddf7ec63e91a59de4e3797dcc628ddbbffa20af29563e64e78313e4f2656c6f57ac414730df464ff451e603e5bd2d2b8bc6ba62eb3b2e1e710f20faf2a0c232f4f4c2053dafdb14bd6e4fac6b212e81d23f510a4755e8dab7e8f543303a1494be1ed505b366f28eb0a93c476f02605e0ca082c928a91057112c11d02a602c7a37623cbd4d599da03434f1884fba8701b9323c642f0dcf983e39b5f682e014eca3ab06d1734aeb415a714dca15188dd88b473b5b06c985cc481f4e96ffc4d54f1774d75ffa9a7d58fa7bc53d5bc7fc4e465a9d8e105900ad074a46ad711521a3d4d5bf278acea9a887ef789d962b17d91dc082783d0314f67e4952dc5e0cd507af0d98cc92ad91f6e2d1b14b395c70f22494713ae3cb38ed9e6028e859bb379fddde512295d8220cd7d16968dc3d7db1e81f6575a7a63988839cca6ad3857f1198b2054c4a775f756d2c3fd60b922ab2b34fd3065cb487ca5b0f19a0ec198a45fb2b4b9efaf8dc82a260a741c148f22acb2aa7b6ef0909eed5f7292bdba20195ed81014e2582511b88c7c30b7a0334216555d712149fa73c7cf034a6363c3fe1e18687ccc22fa64160ea2165933a4a7a09033a2a4f71945ac18e70aa68be25493f37e19d9e2adbda64a9b3db6314c47461ef63c69d024d5d1c9bf5ed32a350ef6787b722bd483e131a1eee097f49508e45fc519b2c0780bc95f0caab77379de21097aeab9e2cfc5720f47fca642370e17de94b0830632b1ad3a28c98c76776a019d3708d0a6cf276777330385716cc014e04894fdf4c0b52cee7d30bda21a1dd22d4756847447b57975ad120205f30ccc1900a5a0b9b439e46428b168c2c05350ee1c4061e7c4de9284aaca9b0e2ad5f19fa3314c26ee93a79b560a91645ed7a6774a2af802865dcd6eb6ed4621f96ede841c6434803764c3bcd27cbf92ff7ea5b76d77a40246c274cd83f37d7f3ed70481a387e1fd6deb790004dea14dcb062f3f5975943ea801e12887f364a76a8a0ece689b2c1d589ad60fbf6b1e3105bcf3b111cbfa39f6b01af73e4cc51f2162631e79202452c805423938ff5c1685e26ba5e0882f6d9e1eff0c465a5f571f3a181a20dcaacb49078dfdb9bc9aaf63d25d46c9cc683c352e40d1e19a929bccdc4a6b5125d2a7929477d57756cbdc1e9a3e72655b5029aede61c395a29f566ee33bbe66b0217b04c2597e4cd0218b436abbbe6d024238d08e4cecda778d6e3e50a5754689d3bb4aa8668f1bfa0a101dcec9edbeb730c6dbc1e83264ab96b6ee75f7ca748c876f72e3bb75f053c879497bde09092dd653237a79515b4d856563482a13e969ccc9d37a8627d7227fa78a7f8a17939bf92574aede9e7658a80dae2439228f95b2e3e2d26e29583da6b80d3cdd3d23f025729a2322dcea557d531583ba9112dc0e061bda12a9dc07b420d6f7d9ed44e0f2eb8eef8fe34a96180e4ff7a3d51a365a2cdf8add5a7569f0e01a2398c1413d3bce43134b0a43db1dc09133ca6b7b4813b7bbe553aee80265f375a0e8b0219bc710255e9c52199b9de6547aa4da874aea2d5ed1552e3cd13102ec625861fd9c0d25c9b7908440c582613b4a061d8b19611b0b25eb7f1623a2584ee9f5a64fb9165047756f7edfdefb841b41844546f918e694e2c46b278f30801e370da56434239909ede42d6f5b61276759949c9f9ba347ac972de480de83d49d55b2c10749f41da528f1750508b263a956a5d3d3b35417881bd9044a97dbca8c956d3719e38f34a9767082a2eb3a299381ee90cb65485f76c76848bf3f47ba156fb9f9f2f0c869f163b54f84583fc84e74c1df5f0e5c708afaf324d24dc20ab3f8d0516f30df92f869c4a4100e318abba7540d5ca3bac2424cca56c053f0a5837810712f9462fa8ae765a5139f4005d0b460f318aabff72a651fd6b4700946804265eac12461b7703386c04fc3e4904925df87d8d7e35e9bebd192d8fc1c04854d81f9b94d4c3d2196e4498f85067fb1f97fe7030262991fbefd912909e6041cb7def18c7e8ae85aece40418ccee42cb1f1843327d60f1ff419316584a2a67771480e89334cbd1a6b33fafe419f10d55dc34c597003f2ba75043be6a725f83aed41ca91c8d709fe780145f4201a76a54bda0cbd22030350e89a3116da16a6a6ea7b763eee767206d057dd10216c39bc1a51dba4d007b23dbf2cd6325c8ad3aa82d6cae6a5287806447dc143273f500998b85400cc27b350c27c262e0e38f0a50033e5e4a9cc95587d6098fb1d2bcc104df3f1f5e8989c8dae1a48540532045e742987591e2bad48df05036d30f35ef3aee01c10d983dbeaa944ce5f3406a141ed59d5fcb1561397129c48323a965dcabd89127ee444b01ecc83aef5a2466dd2fdc4b59a5ca3abe50477f42bd881bb9abef562a7761f9eab54c04f6af33eeef4bf240d158b0488c4c19376ddad59c4c01e8d59bb022824539e7a00c67a8ebc97a3aa9f582c3ddfede0fb47db07f297a6340f0a8e9651bf416fd3ccabfc12f69ffa81ab0cb04817cb566ea7c6f2e98109d278cc6552492bbc8b6aa67b885e9f38694f970f7504cc35091f20a97ec324b8e912aa3af3d03701c0137826aac74c548dd55a05c75323a0f950efd6d29717d2ee7414725c5da0660791248fee222cd0b0c02dfb61597e0ee9e51b809586e01c1390c60564554f1b9d0f1ac86a74fa59834d0ccbf386a0cc6cf644fd97b4e795990e53a5cca75f7a86dff1ef33b8deb8be9290a7da49805921d3ad50db89d1a7b3ad1c7ae03e2ce9642f69993c144cd8e5965c9ada5c5360098dcb008d84bfb33e10002b73616e34cfc076ec826d8cc02879b42f6fc94e501723797dbb816627ad5c48f7e352794feda0cf77fc1c65e4dd6e4010efa061c09d56e7ed6e8e114676b50a14abbad14cf2a664127b9199d42c543e5ef4692b9ff5821da9f9b6891bc74880d07a0953d504ea3b5d27fcf6d062d1ecd58f495a60458a4061ee440dfb8713b34cb5065eb18392851c69c52420a5b0615ec80c0ad1e54b7297504273aae6a4c434b2b83ebca82f80870862959315e11292a5a8414c99d6b1bdbc7ae66ac0468a39ac90342357159307b1580b2883b6ff98b4e0caf6f8816f94e94a0390493629de113ee80cc56670866aba0ff70c43057b0921eaaa3a313cd090d8862b60ac688c16f2731051c1844eaa168335b6e884fbe093f1ddc0c707b714d750aa4909a91523a8cd9504a2e3a20c0bbffb753ba4212cbd3c3e0e0ebdb6a6be818a149e114c5fbaa9831ed830d80196a00c4be9ab8d4e2f3c75266288936cfd0b27fe1adfcf500f4d2cd736165e80b765598297a57f28ae75e54480c96d3b4a39206f9f900f993909c28697b553601a150d33fd921d36425483618e00d07b953ca7a5eec7b9e0cbf187b6be811a159d0d30bf94e3e03f209d3066dabc90a71c92ab37bf6c8b1726ebf32adaa58b8a9ec1ae9d3b0d5df3a504f2df55f72c2c4c7d24405b55f81430a339b74e40e4ba5adb599ac60b1934a785ddd2b7a8140896571ad23c2038464c8f087d4d3c81902a14b282695f3027ca72add8388c5d21b8257243d666eee44ddb1b36afb76c05361bbba9087ec58f3a9f5a993fe467d2b8fd674a66cf05264c09a52ed99342145c9615b3b6e2eb85a6e2ef96fbff37f0286fe2189325627e8d9338f4805d089edf52626d7be864964406fbd39ac2ba5e3125592a7a338d4a5808e9b3961de474701222ed554ad18bea31b4286b9d2f2f2a19e34f0f71ce6136cfa5849f05032660014660e830cd012dc1445ffe19ff4c158de1216496446ded1793c296578baebe6c4d2e07faf117cb0a52250589914f2da356311186b53bb6bc2a36f2e43cdbec8114489f9e1e128f0eaeed7b59a531fd45e91f423b7a26e3960ce64cdff14e48898f806366b1ffb6a00468e6095034103464d86b589ff2bca59d92467f245fdfc69b034df08ef5f56445abd9f50cbdc19e80f0e55c6dcfc6d19ac5bb598e5183ec69d7767f5666599eb295c1ac3bdf2e735d86f094955746592928aad4b2ef59cf815107248a3cb00d62141aaae8f9c3a84322e25f1802030ad5114f6b4c7f20ae4d70e1ca57b2725e2ea060e5a49fca82bad1a209e6508bbfe56607cd1ea35ceba58d02c45c036ab4ae8e5710400b398d01e149323e741ce94282775c1cbe664cd0cf78bc3d3e3038041707c220e96dc40f08e2196ce00e3a9c734bb965461c169cfc91795d7063c9ad56944c24484b530c129166c027bda4edbdf7de7b4b29a59401be0976083808ac9353cd9e9740ed11037f37087ed23e38a1f688859f341d4faa3d62e2ef1663e2276d76729eb1573e727cdea8f6b0f7d159a07a778e3c69201557b7b97570cbac0320ffb02ecb3f9975200bde9083fcf31d1dd6330e1c30182c76e3062c0683e1c071a29a33396e1c7ae7ac4c26dd9df31da24a61e10db7cf5d2e3dabcc759ac970bb7c5cb7d9e7f52a16b9ceba158b62076db893797ec5a29d83532c729da8c6ec9c7356ba52d9f890509c89e26c0bc5ccfa4c0c75e48c0306c3913fc5a29c43aa830bc3ccba8ed0d20c7e9073ce3860301c19fc3399e40330b3fe41fef9160efe65d669fe60b00ff67ddf7798e5e0cbac73907f7e7078457b9bb778f0d3e79c33ec200c070c8623e74c2679107a99750fb2d65cb805688bec9e58c4fd070cc5a22d003828166dffb1c71d2091615907aa1067d63bc0f63f99836fc9b1bacdb1caace7c85ae726a918ece02a1c6b0f1c27ed66c57cceb3a393734a4feb653528ca681f168002a954e32880cdadb3f278904e4ac18f7aad95ea8626996c1bda27cf99e78c757bbef29c2e9d3d653cf94746ea107aed613faeb85015ce5d69f5e34f0d975e6350e0ac330093a513b0b4e795bccc4f9a5417327016c648bffa9cb3e452250a971780a85d239aea140e58a26d57e2eec46927416c192597a62d658ce145470f863e51b1b0ac3d4b2c2eb858cec04e9b0cdc0e9d6a082f54e28b022c431688aa3d4b1cc850b5b09a325e61853eb488c81197d031b353f278d153e61003570e3678814eb0a8180d3e586cf070830b8e4d887360f297847e224727c50f7705076b3ad8ba0000f8464606101a1ee810e703271d51661b0c415c0d3bbe1c84b804a0fb22002ecc00c6f0d89e00c14c8f2a7c64f183734300170c6d6200c16414603bc30059d608c1298814224e866d032dcb01b61f10e079293242842d09214c8c5066081509e0b020c28502362f16805fc0806d8c226518d9c106475c20817bd28028231a1cb0617980c743095d906c4440e00813c444800909909980190aa0a9c01516d0e2026d30f045065ea0013cc606c8e0001a1de021ce094e1e8842c2705443920d870ff48002e60202292525588204d298149e4832130215a8508a60e362052f580869ee61043d8841821ed4e8818cae5645ca55f2a20564e28416319b3e5c4ac3e446dcb3d402a7052d3770e173d2c483a1e9b3c1c9d3f224e5c40828259e744f4091224a29650553c0a062c60b3eac11e504860f86186e90c1d352456a06303424514315ae89ce4a1457bc2a6cd8e2062fb0747b963f8081c30f64e4f0031a3a401967a9c5cd9ee50f37e8b2dc273b6c5378d8828593a187eb458b962a5f36a53ed8227eb8466ca94870b967800869a64fc6596e11b3c970d72d68367d1844b9e58a2e21cd5bd4d02e34080a04e542b7d01fa80f540bed81f24077a059a80e34078a03c5426fa036d02bd40aad81d24067a055a80c34060a038da22f502a740a9542a35028f40985a24ee81375a24db4097581b64099969828d5483002165610810a21484a0169891210a0f0812447a4074ee8000736a0810c60e00216a800052620810898000124253cc001460d20e18891220c588002882460c80842442842c001689b284808062800902102fcf0d103041e03104000847604cd747ce001908c7ae1e1059c75f686da4a3fc51e33d35d457a2a7a818728b168993f188b0ba071e88efa5b430a640b412eecb20a75c80debcc8bf16e3224aa219e43ddab8cdcce11118990606fc799c443ee3eda6e0f762156a1ee6e88bdd4aa30d64becee463bd61280d4623dafee7acc5927903a91e81eaaefe69c75d634f0efbd07f1ad9a5cdad75e9188266dfd148be6d0b5b4d2004e2a83fd5662a9b2377c70ac3db66bbf7dd364bd1e527f74496ae935d3e6c7062a6e6c90e2829b2b6b5ca141b7512d464bc616639581b5026705576220b45d79c10ee32fe3eaf1c5f89663b86779858b2cae5841668a1d5dae33ee4721acd4cba8ac041ab88ce5952c54289c999a2b51b212ae4071569e5869e25aa8426f996ddb368ee3b819a2700933581967e08267909a82c50c59f00c3e34c1a2099e5d51420d6f3a2c35c0296ba001951916698466694a19d29431b86056451a5f182a2aba1b64f8029732bc6046c5ce9e659518ca2a4d68b629a680e14dd40da228226c6226862e3786219a988941871007331886336270010733314811c312294881d099281bb80045d40c9e405146a9894243e687bc67f608c65946899965d4112fa0a1d2c5940b9a28f1829b5bbef0c54c89d59e651497282d4d4b44e5f0b4448e3dcb176cb0c73dcb17c0a863c488f91123342ba2a4c2460b9b4549c58a8dca1927a0d4134ebe259c7861ba22382fdb0f984a96a74bc586272a559ea85079a2d2c2940e0d318a31a24c61c113a9295c40494db1028a14b76739e54c9d22834ba3a9062e70cad2a40627dc7456baf8c270b20418181833c41a6160d0c50c42d4104319058fb809d700d54df9d24319a58c32ca9319988e8627700aa6e012b426103540e9f095529e3c990921658d2dee594ea161fb01bc1dbdb7c746ea78fa88fb778bf591fd7634bf4ca4de9b9b7ea9a3f489d9974a67523321b66a032f332f5e84bc04f113a5890da0b0186f9cbc3650d438e902a58aa72e50cc38e952425162df3d4b284868f9d12234fb014a540950ac64114b285252663ef4f024ea095409e504ca05a129a2c062b6eb02ca666bc289a7c56dc249699b70c255819b70e2546736a431830e0a0264bc18a18316694c896206275f6ca5131840cd6ed061fcf62c9d14b1c96719597b964e76d8e438db611cf72c9dc0d093c6c66620b15a40fdbcaec92c90fdb474d746210b641f8b19e14ec2a43560d2b8739ac802d96fd6dace02cd1891796d2fa5df178469f12908f668fede7655f6c5fd7473e380f6a5145cf66bcfd2ccd49c7a0b1313a918fd3c08f3415f8c731fdd237a3c841eeb9008fdd535b479fbbcdeb269265228cc4b5da1d8fcdcb121b7f4a1ae22117a1bda9c635b6246dbe79ea209760d18555aa06d5a69b440f3320b546f81de8c732bb92cd06c02aa8fc58c6c2761d236dd8049dbbee96981e683da4c9a1bdce68ad0951fa1d1f5cbf0b57736c30c66348c76cff22906fff9f62c9f58507d289fb4944e47ccaadcfb11cc0ed33483a17c9262877b964f4ccc62289fc438cd6098c9308bfa89aa325b69207419bef85219b93d4ba72f9bfc65253871f9716ab2f19e65d31a9bbcd0396e0665e4b8321568a229a149cd78abbce9a87432b8b965d3174db328a3d093262eb4000b16512e3811830b61a4b890830d38f62c5b88b3557b962dc8c00550dc38712a5b58d33473ead9b36ce189eded59b6204605b3a69f0a52f108f7edf786650b58c0883922617ba76bdd9ca25d1dadeb1ed3bad7365a775288a375277f4cd188a3795bb6dbbc853b087ebab644fbdee51aed4e8d7cba0947eb6a4e4b340e09f77a63f2aa71c47326db66d26efc4ae90de396b137af88771dee1b99549f54ad97c9ba79e0ec8d966ec62ca60a764ece324a28c7594c0fd2a9a9da23e73c94a3534aee47b0d336932fede9efdc3724d6078d7a3da6c91e982647f1ae1ead8647df05e05b4d0ec1c7b714883dd59eb524ec1bf73a1ed9f37c251609373ee5a2c6bf34d9b3d2a2ee71e9284bef82e915be4a14427fe469f122c523eef44bdbbb7af48ada8b21ceb2c1106fa1b5cff67787d9de7e3c6108c1675d27733959759ab7d71efb23aac7bb73a86bbd63fd870d7f58efc7eed9e48fa8f0944671e948a1ed79da1bb577f26e6f0c0a8414dab85672bae101c27888814f4feb022e3dbe97c9a4290b747b7ac6118ef72d098f1d9e4cc2638f5f9d14da5c7832a9b2b26bcee83b5148f8a35193747b07cf8945c21dbe262d816ebd3cb6f79516f51d8302093599c2d2f60e7e78db1d0b1b1f9cb4623c04dc9cbe63d01d9b5e72d17341c20b12511b5c81734271c76f1229081e6311c4182b31d97e1f625dfb8e7f2f88cfc4fbbde045503f714a4cbe5fcdf43d7ca89b9eb8dfc7eca1ba8334226bb311fc2f7bb9c9aac39f342e370942c3bf796efa096b16ee2d65f2d11a46100e73da16f62ca90ebfa1b3377416c71c42c281e3c61c42ba71c3c61c42b261c3670e21f9f8c4e610522c069b434830d86b0e21bd5eae39abef9943483d3d3c73088967ce26d2ce8ece1c42d299b339849493d39a4348ad166b0e21b190c639ab5fcd21a4d54a358790542a710e218962388790c2212410fce610d2f7797308c9f3ba39abdfcecd21248edbe610d2b6e1398484e76c22dd399b48d6d6398454e7acbeced91c42a273ceeab7d85b3d972c135a2f1990f6501adbb40c23a53c224b50963065182c5041584a1388d977cff2082c6ae89a103b6dca54bb9174ed490b9f73257c235a6dfb146b58d76ad3027137028fb7df1b8ddffe4d7d17f5953252bde36e24fe1ee37f376a1d1ffcb273f0463adf3e6a1ae57d3c4b8fbab675bac6b9f97eefc11b71bf1775cd5e4aa5299a9a94d178d55bbac6b4bdf51d4dbfe0e7e85a4bd754ba268affde690bd4a48cba83e7748dbbea2c4db35820fb51ff7ea56baa4fd76a9f91dab4405246dd435d0375add3a3127b0bc46481acb54016ce54944bc8cd97f11d1a8e66d9e074a3cb0da74734347c995a4bb4764d5c3f46a130b2ae9b6166ad79c86d7aa2d56eba6bb69bc8d24cf72b3709427b95814dc50e956fc38656a25fda75b777b0c87b2b9374b7be4cae003ba973925a391232b7f7bbc57b393956ef02e77e8864f4c76ea4e32f5d938addc675bc6643d79ad0383429fc987efdfb0d55dfe1519de7b121aa87573d3c19d3aebb32a94335c904c9e8f51b37fa5f52484636fefaf5d44a2f5d7bdd86a66a5e474b82ec7c09dd54c78680e739781eddfaf89dbc84b28680d7d1b24dc5a2159c39be79e99abd05b24216c88e87d16c82a6af9b60e33cda8c9306804f1accd69e4e833df65786caf1d89b6c5091083d08c66207ab88a4c9462cf69812130e0efb8d14f05eda266042f2d2359fbf0ecbb5251aec20169134bdcea483c360afacc4c4e730cde4f31bbfa19b9e72fcf598911c87fa39ec93168bdd464c3fe5386c72f0bbab26b98b26e8e72cd1a658046faa49d53d4d2f8e5a05bb49d93eaa43b62dfe836cc402d97b909158207ba06c010b642fcb2758207b00acb0c90e72931507fa29c7f57f72932034a51c9a05ad270dcc643da23ac96eee5527596ac58a15fa653fe5c75eb7b0db5328ebe3c6ed6fafcae48d5cbbf1a77c92aad9b406fb537eedf5a77c1baf0cf5bacf6119ea751bbf91a15e875dcc47cf50af4f0bf43c693e36b2a7c9240bd32a6c9bfe95a5ae5e1264537dd47d4e6daac6fa88b9b946d0a8b0ed7ab049109acf6d1c6bf0b0dff807e5a39570fce1b43e9e9a04a1e1b88f37e79c739ae08c09ae6cf086b73e9458a0d1fed833688791a46194582ce46e4f937649907dfd84e3361e821aa9eefa271c879df44e52355b3fe1f8eb9f263d9d6477d52a6cbbd35b7068166e5c93dde8d22a6ce44403b393898d87607da48fb6c07eb7bc8ef1abbe2ad3066f9e531b9659989985d7412a1a79e94e24616a4fb1c8c84b57a65d7596782a2c0a905ee602b3eb7dd4d8f53f3a8ed8f561ddc048bab6cb045e76bd8fcc882abb5ec79d4dc0596957e3f515895dbf6202d273f182a67a7963a5a0ae141a2c55c6568513d309f1c6abf20d21a682437c117e613262d75fcca22ab3fa0267acc222224ccb4d0e1167e858d929228c104f11687a8a28c38505cccb8912c08ad8f58f95e04c47c4aebf4f09c2a835f4e9421b3f6a0f72b442c75a6537ca6c602465bb04f366d7eba8a51729bbe6791e26b8d689ea14774e547b6253f43951c5b1e553b1e8a7a27ef7916635f9b32b185234c1449b4cfbf5a0a89174fdbcdee6f8eb53467afb23fbf0a7f6f8d1640e9d446853fd7ad594295f8b5a8d4d93aaacca6ed8c011e28073239f60238c18cce7f3d1590255460fcb6ebcfdfa2b1bf5f0b83a179a9e3c02cf199d9c1d6e47ebe49ccc1a5b5b4b68e4a1125778a563d31f51067ee10d35fd7c3aceb3455e38e293f786db8eb3cb41555e8461776b10942028425a64e0ac207532762fb08b19651735e6ec83336fc410420f82b5522b82dd5593b2a2dae96b6fc539a76d0a620d4ae9bd58b62ad65a8bf1ec02a68b178a8f29bddd5c24e8e244e57400679d73d24fd102f1660d1067765dc2da0fcc998228c38c2f3679d7ec3341a02008306a04a125082c4148d9358816bc33d000c128206e00820b07486a054a0a65a24db5246befbdae15949cc0711cd759dbdd7bb9241aa8b2eedafbce765df729165924dd08f75a6befbd5cc6c70aa8b28a801f171299cc474703fbfa24f1193b34ac74a3eb32a11f96c88e60c422b11a489254abb4494a558d098261245dbbf688e1df8df127ad8393e3f6bb37009cf4d931ee7773dc274d567bc4badfdd019de4b163deeff6bc4f9a071fa1f6887dbffbfb3e691c042a9fd5b1988f5c958d7cf46bf57195c4354e98365a693289cbba88f6bc975e3af2d49c12a441900983cc1764d69464d4b852261921c818b15d5546a689edaa32b1c85a7b2f1932656243ec26992ccdaff1043ece4beea990797baa91867c46363ed212da66e38b30db10d96a4fd28d978cd9b0bda4c3be22cc36334c2e3d41c6cbd681e76da6db6791abc95a4c84f94d54afb8648526175a60fad1bcd54b6a9bb47bda1e829c18a3904481c1519b4e61242b6c1c45a144d1a8bb46a98413a5182e3c6e18bbde314a3258ca27a22e1c7a4b409b435caaf0eb36a702d44981602057870c757e18bd93a110581098f1de2704a8d36a08f5aeeff8e0b66d0fa8174f42bda897b6973fef987ba7667b79d6183cc1b4b977f4c87e567104a23d750a74177520f8caf6ae6c9f936fde41b3b9c788dc73bf9af41e3b5704e3e9ba91bcf79c80b3b95b27dcecf017f69b7b6ef34a2ce285178570dfb99777deca4721197e7c4e0cf6b95d1deb5d8ec1fefa3c7830c788848f3d7c4cc7605a84a5adf31891d7592cfbed3ef73cefafac7357d6794f2655f7ce938f5a9fdc773419de7b8efef169e523eff9cb3fc2107bfa7b9753a0db1efcee0782ffe07c48df93b60dbaf9bc10b87692eb66701ba3c3fb262ea03b772b825095ddcfa9ee0a38c1dfed6e9be5acb55673f4cecae3c7be97ead4dfe625d4356773da30f15d749b4353cabbb5dafbf42ac5f976d786f0dde25ff6203123f93d29f528f5b890e46e3f02026a8feef63aacb5a30b88917e7a5d9559fb201fc6b9ef9c33341c131a384ab0eec2a9156a0f4ad636f7a9497b255c469a5465f5e1deb24e95e12b49f2a1da5565415323793457034ea2f10b51982f15e3cd23d41e56d7eef3642b1079199520203c3d39d11059fb638739d4138dcb5569d78dae5fb68530bef435c90f5a65ceea83881875aaac5231922ed797129cf48571962880736febb69dfb100410cd213a87e8a6dc11d7e31289454b689ceb537be053fced604eeede7ab77db1f16344ba6fd779eb5b26bb93db5597ecbed3d444191b0c37b1a8f52e8ff7597d3b9d7346ce59158b6c45c0779a5c0a7f445184a5cdba4fed71bbdf1355f137c75acff9d4f94efe740e5e47c75a8f11112fd2a0e60c00d9dd9e2727b3deca222c6dd6988f56faaa7c24ea4d35a867956ddbc34dc7865c2626bbbb734328d3eee8c6224c279898c02e2fa17190964c384c4c3ba9b5bfdba4707f4a7687b424c84edab687e43d692b6281dd1dc792209b8a0958d2c16972bbdd0e7ae208447b4e1de417525184edf76ee19cd15f1e73ce48d52e1a3267f44ce8924eed986e410f0c2d78ee734f8fbcf7265dfc39758380b43a317e155e0cedb55693dcf157994719e3eade571f3fc5d57857f7aeaeaa0b37f86111b8d77349b8d6a515b55c942870a3ea3ad5491d7cef05bf5d95799431aa1e923faaab4ab27baaf4e78c3eecf2b418d3d1b600ac32da755dd7755cd775f73ecf3b97bd9fcd6df73c6fdbb67b9bee964cf0cb28fce08cc24fd0970dcacf4f10f7feb85c5d5c231365302106136b9a687ae1327166cb6a8f0e0926c4f8a8341b54c558c97ba918708a609c251352f62ca7b26c01ec598e19635b4003dd2d0aaa629c5546492a72b4e6a02de3fded829a18f161468c6a8f4b373ebd32268c7d5f05382b7ee7bd83733749adb5f66a926a926e22916f7376ee3bc7b845b4cb0a1861eb98b36a64e69187919c6fc8a9a6ebc6f122b993562a543fef18368b9a33f6f72c91107382375b68cff20447ec316bf6077b964ba0d92092da63e277dd3924f748a2ba3c7af7529d1e46fa2934d74c09d41e36ebd833828cab7707df913e36fe23d341f270ed1177e1a827d7a9ee6a65ad95816098b9306fdbc3fbb56dde0eebc422efa0e775a0d779777361b661ae9b2a1106d9a031e3fdc765cfda5a67fd06054f2541bd7d77c73a3cdd778e431d34c448ca36d639c639cf39fe8f90475fb14887874727e11dde8a444810274e1c347bdcc93af9ee9cf0adf0ac1c7ecc2178fb2ed424f80d1cab6cb4e1528bda34c0ef344f3a3339dd6cfab3a9fdbe8368d22c99710eea2f1f71ef4e43f2d3a4d0feeaf45d587d8c3e58b2fa31cfbcaa3a1eea805f69f23b47bac60dea2fcf4d392d748500ed838c18c77b9ff60ad9b9432b1689a08eeb6777dbb98fe3b88dd3dd9220e3eebafcd34d9ab78d9261789ac9ef902e0972ebbd7462d5f689d0ed4de669f267671f8ed3a40edde8ddbe713f8fef62c662d1d6d11fe4ea0ebf1fd60785a25142412818e7c711bad1e5e2b237c5bbe81673901023495baff5ad30d41d9875aa6cc35e4ee2dadb7de80873733ae9120912274e9ced537bdc5d6b5556c7aeca683767219971aca2111c0d1262acf73e394c8ff8d1d212850c3f5c497eb424e19b8483b4586a6d9b5b83b4586a2bbddba7a236b80255d9674feba3155ec79539906afbed765b47df6da758f2d177269b66d2ee0da476bb1dbb23d0de6f47e03f91ac7583a326936ad781dbbdf86a122fd536ba6d14cb26f1bb7c64cf64df7c91d07724adb249bc7145c8baadcef656e049efdba6491a3541bd6992d3db266ef6dda59bce57c786d0df22f4f74828140572bf697b49d6712764fb15b7eeb2f4d86d611e3539d708e93eb1c803c13cf7ddc07cafddc11db72ee67095bd1359ba21494aaa6412ed3e7d74c9ac43a15020780d39d71cdd6fa7dfde515c414a528dbbe3cf7b2b4f0b744cce351b7f03bb7fc7502810aac9ef9eb7e1cd6df7debbb94d53287407be4ce892aa8e2640b3c34d4f8fb388b1b241d8b34cc24d125fb66ccf3209a89dc4d4067778fa338758dc75f66c82b067013a0ea60ff0cbf5f54862309705c1d0f33c2fcc47dfec3c2f0c5fa75f62f751f8af087da7c9babf8727aa96ce2cacb53f569aee8fb4567bfa2824bd935693176f4b35262b6cbc052c30e24c9318b65c41850aa2a8ae6c19d326aa0d531a4be8a06123031b19b059019b2deebd3b76c0dacc0e1a54e082cb1316fc0046091a3a2a90ec1a356bdc4cb1a9114cdc7174d924467aed9a3454054e382976c884146c9a9cac0973860b35d600b1c11cf6bc55765411da820b7c31be343466b0823245b0f1024a67868d102d3653556ce0650d2d35f0618935d6e852c0e6068e0d0f52ba30b808830ba61d3e44c1268d931d336cb1c69a1748b005182da0d831650b279aec88d2dab3dcc2cc1650ec90b203ca0e2818632c05638ca1f0ec596a41841634dc90051a38642186942cd66481e60325c66ffa510b5b4ab1e73f5862ac65137bfe0331e5127b7e0e81dc86b76ddbb66ddbb66d4bb3831b1a7cd12587285eca8889aa200c4e64740146142d5aa03853c375bcb8811733dabc81c1541764ca266219258506db0a153da8c1e50657b0a07b028b3328166840b14135c801c6932e6caca81203369d1358783143100d9ac61a039d2d60d8d1640cf72cd350b1c3290d133b9ed29c200d1120ad61677de0d1de90ce3bbbe9cd29ce1f0f4ca0fa273218b6d0aef5549f80c6483fc27d9d3540cc780e11374e614f7b2bb52a72697f01ebd7b4b36799064b53cf9e651a2718c400c60e4e51bcf083161460d1c5c3020cd4fd7f8ebb26ac7c3c3ce1eeb550601175b1a8d2744518ab2b6c3083156058b105973328b5e18a356964a046898506563c61c598d91560e4809ced79f2677b9003888ceea827b3d783f4117f58a07a12ef793a8118a13bea4998b38a4fde15c8790653b7da24655a9190c3364281502cdd28f3e06eb25b6d72b4fa225183825bbac5943161a140e814dd515fdba841ad78a18939ab27690ebb0a608d917e09bd00d61845a05e689b393465b95aeb69166b7b90581f9f40f6554d0f8ea9c3fee620538725daf653db32794d6698f16346694596b20a2382c8b0e2cb0b3228abb0a18a2a33801304a78a261ce26af18558bdb43359497a7f846f871c71f7fee17f07bf4ce3eced1fdd2038456b6b772ea9ba6ad326d58da92882e0e7ed4f18aa549fafaa9cc71849a14d647d048931f250a9c2f0f334488cff541f736aae3e7e3eac218c9ff3a178f04eaad811f1e08d446da4a2713658c322234da29126279553a8418dc6ab1d0cc02ff6f6895590666fc74b58c80d0fce1ac2ea24f74d2cb27d92f6e2b7af32d4694b35b216c24ad76aab33991584fa5941507de299c32e5bf1f75ec81d4caa966e5cee5ec5a2ee134f1d23b22f96520565ec1558ef9e3cca18aba55badf5081fbfca2024c4a6eb84c04e889b29352bbaea446e953b0be4ddb31b05d7de7412d70ec573a73a488b91fcc1719947191dd771b6be8a4482bcb12a166b6419b13ebe356aaa5b44c273b1d811d77b1ee3deddf66425969411eb3a571a5f93325a5d47d75a5fbd9673d57b726c7b4f6cd3b123e3ed1fc07a8f566a29f5dc7ed2d46cf19c5256ea79ec48cec79bd0eac95259c9953b0be4fde6696f6cb2a302873dbb14c447815ca8d2b5255a4d8aae9668abe9810ef62cd164d968ca6c3176c47ebc093d67bdb33e4656cc84d65937caf99863d43a4bd728ad4524dc44234daaaf94d493747347ec5937a1e7741cb7d7723e6a23d6afaeb5ced25624121ad9f9dc463cd7e9b119dfb36e643f3e288b31545a29f55ce52adaa4c0abb292ea3b42c4779abc9be3b873bb23855c737a625e42396f87ef9680d968751dae735920bc6d22110fae08e20a9ee7c87890314e1a8f96aadfd14a5246aaafae94f3d675b492eaab9332234d4e393ad6b5748c7352e9d8f758a78d8c629f3ec2fa65fdc61e30de7ebc655d95c757d15b8dbac611d9952ae9824b5e650d31c5384193b7674945952e69446931861a4c6a58d03101a6c9146fa6704393b2076b56aa9f349b4948816eac5370c0a435717772128456cf9da8727702c598fdf6bd1a71ffea3d13ea3d5da3b4dad43d8c1df13414a5c58e78ba064569debb1b7d6288892cd07d988dcc32e211a11ad4867b585f754d2a76a49ebb513da76b52f635ababeef4b440429b6bca75db0dd27a410c8149030f52bc55cede493b9513fd6afd1ac0e518be9320340feb23f49e56a29fb42526daa7d8804933526feb9644c5720488c65a29a6aa684489c93df7cdbaf0d138cd74bfbd8e31fa983edaa69598e0dfd18ed955335d3d2d90c5126693e3cd6ca9519aa7c4a4bbf70028136a5094d6bd09a57577a13b372f53a7a5ee39cd8426556b914d934b8e92e0578d023ebd127c7a229b56825f7f330a98da59af6a098d8302be1a85fa7a2676c9d6aa86d01938e0f77df105f7867b33a788b271e61451db942cdb942c47490cd034b5efed99333a4abe8d82019aa6b635f3e628c9ed42aa33b366571a7669146e17524c5b8893c14c83022e87a926db9ea51456ca28b49465c204855146b1840b416bca3371ce9c31069c3f9df5517f4fba40fa39c1b97acd3529a31004b591ea56d7a858f538f220e3fed34d55d7a48c787acef3daceeb29fdbd11fdbda7f7b491eb56d7ec5dda8805e24e754dc802713cba46a46bb740dc7534f855a86b52de6be1bf7bda687515f7d5e9a90e754d8905aaba46413d2d10ada7f5352923faaa6b4c54d72cf0da78d6d428c4faa44d375ceb9346395ab8cac9352923f02d5da334f0abb3b252f8ef635692320a35cd6295c2f368b3d29a7eb1403cc818b5524d8a8449c3df3e699f56c237d2544f53c0250553fb0fa0344c9b3fdc6ac9560e88a02dd86cb185150f82f7de7befbda0787aef1559e238de0b8aa2a8fa15c14f111441711445d678a20a8ee3388ee338b258add63bebe3f5d6c156265da35095b56eebf82f5d65e32852718e5a3cce2c16164f1031c6d8a51297c2f054960f2af1b67e652bacc9d541bccae291ead4b5319e26c05658a54ae2529565b8ecfb9db28c15d74ed8c3b3dae8576519a8bd7a584271c6bedf61a9be8a81ab71c43ac6d23150c7b491cf61f778cca408ae34fec6fa8db10eae56ac1b815ad4e409fb7e458ae756392a955e81fa28fcea44555cfd86e1d5a46ba7876747b55aa956ab202edebc11bf3a61b36e3beb03f6f1e098b15ea9ce3acea1e8da57f599335bfaa56577257189b48442cd06bf7a3de7ad1c4d8e2f55a71abf164bcce40946ad7c04a65de3a793b8c4d545708a4531f1ac079cf4a8542cb7953d4d32a56844000080048315002028140c88c40181482c4dc32c647e14800c8b904c6244154a22418ee32088814806a1184388318600630840082169ca46001418201158a2d577ecb846043cf14c7c438620918a066b84dc99b810adb5b4fd522608dab7562a9c3434482543684e2ae184f7720053e0fa25bbb099c630e86ea9e253d24e22cf2a5520fb06205a3b3072bea38bdff101383d761ce609962d3c15cb319fbe29163cfd34a72b841244ec1567e48281e58e78dedd6962e25c31f5d174f9984e107ef44b6d9597dfa579430bb81e119fd5e5ca611fb0048bdd20c01136cd6520cd1d5d2f7680965d00fc0dfb2ea7be4641e0fde1537a128c00e5a801cf37a823355a3366cfdfa1373c326ca6440f74b9ecdb7a7fa4cff0adf64c808f8d8e820dea28595f6a786a5019bdef8f85b0dd0b91d8ad6f2c15b117104d2e9800f1e4f54331d4ee7fb973f5dc16bb608988c721082dcb6c09157725a4d5ada288a1613cfad6864e2b49e5e6d61884974da39bb45d670f293140fde30e14e353c9c228dc34b30adb9e86469172b7108f28f01d92d6ad27832cf6c4bd1e7f026f380318cfa1f1ea6857111a25e84f96a5eb799640d1836f89bda1c3b99c4b4a188072fd52e4225eb2eebb54dba0bb4e5771a738717151e011e3e7822503212650da0f504e75b55b6d0672bf9b9e38684aa5d5780923fc4ffda11e942e141755b0d4c3db57c73aff42cbf65bddf4d9ea6226e14a93d5401862af123730a1d581ab5ab4540e6fa0a46e2ba4cf3e1ae5568aef82369a1505e04df7939b6f3a3a9ddcf5bba07370867aabb44bf228e799f37739456c8c5472c455441d03e73192f4679c9155d22dbad5bdc11690208bc3a31a55c2672d0bda8157957f8dbb8e178c961c6e803e4b1fa72f35a52243dfba9e6142873785c9643631bd9acd931d0598658d2f025e1ae4e36e6c6bf4afe3e3c0b4654e3361da24632834b6c04764095429bbad47d91ab1e92b163266d15577fb439948d46f0e6b55dce9a6c32dcde79c6521aed209409fe2bcdefc98b4cbdbc5b23c4cdadc6d1c3b8faa7ba3e387c6b1bf7f1193d675f9479417aca3d35c84145139815c5943950309f8b5767069cf5a6c828a2cfe29f5dec48261ed07d416bbb18931b4a70c3b98aa20924623461b92225dc71330e809ec9e420a6cc45abab3d36c9055b31d17ce2e59a1ec4f3fef3fdcddfbe8e784dc7a03318a3bc16f6965cebcabadc0f45734ba6c0a7828239a31d0d5e29b2c83ca48cbdf03a5c1c0dcc573cb47f073baec4a8579203466d78788264cdb333e353061ef40b0976bcf502fc2a95a18cd877b695c574000df50501315e9905e2a0e0d0eea9d868795231005a79b337d7e4c7bfc50de8315579b2ef2cf93c9feea000cf22632f0a3bdd2f7e7db25ac11a9f2b9520e7561eb6794217968bd43d754cae53266c9ba7a84cbe3e823e2d4ef9df0afa1d6b7fc9dc719a6933bc89a3ea6075dd69131473f3a15c54f8a59726ac4cd86ab6833b4c77f4a57ddd59ccacd97f8e81f52dae0a6feb685684bd4f798dd99eb0b492d364b22304da8f2fd6a6f342a3ee8cf2ce8959f6078cc9615537dae85888671579d11596cd552a32ee40982e86110cdbbd534333e5b4b0d4dd821d83fdb99434342e0d6a4220a5d991cd6ed3f1bd32355552cf0455d48161ec5eca4394e0d91c76b41c0f5e86cc5cc808471854f70a0e83bbc80176579824aaa1c05a121d56bbc80abfd80d42829f246270492f334d6590c0213fc29e1630bb7ae9d2169dca6a4e62624281c0c7cf88ab8d0c95b4e12ddcf76474c59a4cd54918fd262a38a67a4ff30278d39a4cc396b0773f2845a70d8dc884a7a4e94ab1f1699e044458b466b8f7926b6762e5cd44647bc7977d9600407386f4764cdd8d3fc5a6141246a6c1916c75713d117a8088d5c0911d5589e13a9f905d9691728878f3716430500992e97c55ecf3aaf48426eadf3816bd7015f66d469fc836fa6dbc8bc9763f9b34929ee790370359082575303bedae2ef7100ea15307c9124af2b487f4c36988dce6dc4c1110ff6cae36a44dfab582f39bae77ed0bffda5875a43d70b39df6e266c190b6664732cd8a59dd5cf99af8d22c50550ded7e01e467aec7174e46f0eb511706ec5bbc1dda545b963ae2be154d4867522a3ff6c4bde7ef707051ab4777b96d2e3796cc529c578f57007023492e112defce4105e17836e5dd9faf2392c9e152b364d24f29196bb3eadb04e26c8b120eb458e02d5570e745c8989a860cf2bae2d7c527fcd78fffa2a0ae4a0cbcc3e5dd689b4b15e4395bc90a990fe72c06c49d8d57a74a838c16f50a422c023201350c8e2eac4c65ee0b6ea579a611859be6da66b3a8ebe99366857c2cac83d6303b83a1beb989b578e79c505a0896315688a114a406c372fd4a36d40ad32c24d29f665b90db211a8b7311484049be543d6035c27f10754b1bfe2ef3dcc57923dab2819ce3c2de0002208ccce3a89c81f3ee8d88645bc2c3bfaefddb1c286269d6aaba2966de2f305a97b90c64bb67ac0e0933cc4cdd861d27f8c54df2e4d775d8d5ab7e48b825ac5aed491819c0a74168230109b7220a1ce231a59dc2c77149997806544932a2f59d4eeaeb15bbb1d4f064949f863e4ee640babec0d969ed3c9f51a1721015759d104e9b6f83bdd3f2f4eb557ccb8e9ed8afa8e211a48defde0c42e55bc44ad102611e5104370eca4a2b28bd1216debc0a0bb659fe6a8d4b40f2813c9aea27cf7c006386f049395bd50c77a0a83fcafbfc79600953a9626dd3ddf0e30ae36e7827ce8aa93aec9a146e04ce289177cb2acd8d38aa80d2e7da2f6e6bc688c46ff53b5494260bc9ac10ae3b9ad4bbdc0c2ee156e737dbead3861741e58f12b03402c1bda38ac4b9ae58b312b468cf7e03e7a3d0769724fd7a24c2b6c37026158e80ff0fcb65a31cb1031f471dce4f2719bd4cb56773b013326b18cc40e1b982a374ecf29673c75364da7445a747e6e2bdf2428c8e7137bc5ee923c60526eaac1d7d89e77c983a9469a43299272fa4662f8786957720f988e1e49071002f79c4536547ab25f50cd969ff643cb502b64b0a7e70668926d1b86d0193416498134febbeb97d571d89e79fcaf044908a129f82e9b0e8911ec39bb08061abe4e224f8487ff0394b22f2f46de9619bcb45042bdcd3396b0be0d337dfd1433444775e6c17be52892cd9d65428db381c5879bbcd9699cc0f16190c1ecdf217d3761136ca413878bb2a874f2c5a5ede8c4c1940aaa66946eac4789163c02dfa94e3245aad9b124897c8a9098e444e66e1aa93990f1437d62b2d95c62fbaca8615e7c0ee5ac76887f2eb9865b3dd9b837adf70c69bc516efa27db985e04bfc04944d5f1d81d6b270ad73cd80b2a72c4f96c243ad7c57014fced287e3055560118a88f8c4f07c266528686030f458f58dad06d2e6e5040b411213eaccd3a820bf7d82fb109b8af28b7b86361445f0488940b6f7ca48fb0fe93c716b6c3eb253888830e42681674514552f80242fb5d08dd0752374e9eae6ac68387d7296c62ad4340909c900f0c25bc84b6fa74884b502787256d75a6a74d780ea423fe456ae079f0da730ab43b0970d0e55514f9acadb6596ce46841effc8a0494421cd83d84da3d50952ddb4e9f3dd2cd4f7d13def6a6960b3948dfad36577137c589cf2240a95f60928676ac26b6f03c1a0771839728595c2bcd42e0c4b0d205f38d120e534a69e4dc4df04eab0a3160a6b81b598f620b8313bd4c660ca0480f4a5313b02c324a169b45a5bbf506fbd36b269bccfca182c9edd56139f1bb574c15569ffee7097b54de6cd526308db0392cdb9dcbe6586a013d4d54b0d800c1143cda812d4014206724322e23089c2cbb3dae362557b0d5fbe0db21a4b8cd069a6bb435313c514b6cf1bc984758b9054a5b741be6a67da1e3f13d3b2657633b216297ee20db866a7ca34ad6c9a011286b180d5e89d450854d21e6e26f06008ed6b486bd635e50f6a71a04467871806996965195405b05586ca45337cc5245210597b0770d6c9fb087f0841c896650604d4a5be54db55b682d726e0045e34888c6cb3f44ed21b78e5b7583dfabcf1ebc46138f6b59a30f816ce2ec9288f7d129591f422e1264018696828b2e5498d2738403ed8f280b071c74dafeb0ab27a5bc22b70c1214301cd0abe059225c572581aebe581c7b71ecf2cc7481f0acbdcd3e8ca0f02c829f2a9312c5bbf4573783cdee6df8aa512db5e418c11b9840b975859a9ac3bb962c74f92abac648af97556b435159b6f83a4cd98651fcf45633b749aaefe26ad15ba565d3b922c927538524f2e0686e6be7e3879f6a88436a4b798bdebaf461c335563335d7ec4d4ef810b524d4c883cddd8c1656fe480be8f63bb03569ddddfaf0c2880dc1d17530d9a832a5bef9ff729aa5cbe59c75372846a79ba3102d47cba719215c25652ee3ac264fc1b496ae668d85cb7083d4ddd77c79280f4f5cbc19c5c1dd39b1f92d3acb8d83d865927e873aea288941e04f51946ccce313c62eee9cfc26130ee18bfcc5a02f4ea19eb3c0f2f4f740366abb1fc199d0a603c90fef13c3b3ede3d4422b714dbc150ca278f50a2f7132e0b6c3d68dabcb3af5e2bd93ddd8c013ccb24e3e36cb483ab1f5fad65ae8a24a135aeb4516cc29349de02b2334fc42e92ad9e106674ce3d4d0287d80293b2897382b954f8f040e67279e4c1f116b6b5b777b51e2a0bc80438dd2406c2da00cb04dbaa2c3e9ea9cb7315022fe5bb03025fc85e4b853e5fd49f7ae0f83f83addf427630dafd03c5ab391734f255510e166c3adec2325989a51810d715e031979de12e289ec574c04536aa56cac1a1bbab7de94b918738cf103b427c5751e95da33b2a760d41148c411674cf6a0542954a44307ebd4bdc65a653af4f80165e60df92850dec7a84588959b929f99fc9432ea48df5a593fce625d3e2ef433243ece4230cc3d015d578b41f183e8d07f1e999aa09e8beaf6827df7cd003e430b11a8a89b2097b84662b77b1a24840537f1906c183a34f7e997689597db11e924fbe94c22e443fafcdf2050040fad4d9d8ff043643a136fadb908f8edede1bd265b7cfbd621eb9676d934917726cbad248a53a33caa5bcbede8cc2901136bcd575940e08b076b0ce328b516f9d496a21ec416c93249275e8db92d267eddf462c26b36caff620ec9ca86b48fdb624a6fa65c2b86b59345be98ef171ddc0927b6c6e009c42d0a32b06721b798e82b8ae916435f93fd95bcee28a8b64cbf8602d11f48bb5bf78f10e331701629ec2117bf278912667d8c628fbde47f0ded3068d6a5c4c575e7b581e920ab8341bd7c0d6c424059a490321185c70336757bdfe1df9c4ff85f4e2d9cc6dcd82c54375667da9f20bac3464386d65da88e5a709231f7115be4eec5059ae911adb158b5084448aa5fe3426e4a2e492af50ab050a146e7b89ded71c1d5938fe3fef170b84c08ddf2648905bd214df6dd94398980d2c6ef7ca0e8f35a3b4d2c78dd6b95fa31a7d9fabb35f215dfadcdef395905b1389aa782d163e49626fcbb8b8187ad981b3800d331f2a384198b98d4c6e095db5e76c31e0949667014c98efdc882d184551cc1af8f608d2c18aab8063491f0aa27206ddc796aa0891218c583e67588164e6ab23f3c18160c143ff67638d85bdfca873ea2b7a95262648cdb8a3b2cf64c0db22fb8c848d455c6f575d2e9af63795b44b10eb773e293757b14c4ed552517f9baacfe780932df19c18251201ef67d5a56666e4fe7e06c3d622fcf2c6331fbbe5790353ad622191c3145436e43e8b33b8c45c09db93f80b77b23ec31f25f6148c466f41e4d270b04d4c0beba7f6a7788f79c9e7f1b4ebcb06634dbdb20ca283ca8a8596b28053e56bdd4723943562e542c2c88d73689e50b096d78c74f04390e3eb10f54097a91ec29928a9207902b73b037408594195ae0b9658f3cac217002e5ab71e5b448a32175e4cd164e355f11ccd26441964dbc3db7eccfb27bbb65679789a10ec109edbc60a2e8793c842d7b0c0a2219ec550c8d9d04381a431a41e1d4e5c7f9f9f192be4b2f188a223bf0f9f109396eaf57f0c55a5e063b34114cc9df22d8ae2ddb49e0c360079a10bcbe59624c96bb6202a50bf6428c4363d516ca8b33e750614ca58e1bb72717060220fa2d2cf3a2d4e72de9173a9eab61f8f464a1e56edc1e1d0280a2f7594edd1abe3c6ee2c8ed41938d4feb4d50574d49b1168e2bff7d74d76e790016faba103c30900278d58d5ec2327bd980dca03c7211585f13d78f06df3a7f54beac95a8fed34d49db5a080ef5d4242236ef2208d0fa9e731b29c10e500972d776aa751db54ed50a79e232b253bc4652656eb74386e08c5931e016adbb3d46d735d1172eeec6ce23b15a4de1c318861f5621cefd514f798118b52766f7245f93bf269f7a6eb501837f38134c470a4fc591c54d0fcaad7984f767351bafcf222cc1916d1dec2bfa0f188b2e62586309ee71268d6e4a2c8307f44485419bd5e3e36a4737425d4ffa2078ec0366bc60bfa4e0d5179b05de949651af3ff17f593a07442d2f407353f0dea9154f620deb7927b5854cacb40566bf67c592d5896012f2b8395ee4cee2f65a723ed0c776a90c2c24ca457be678511ec67cd7a19410112c472848eac9d020e5bf6c0d88b2606806722dafcb323325703efb9b44ce6045132c3a5cf93b355d1d4762dd8cfb590df85e192b8d675d8e54b99446e915ab480321634036277003cbe49fe2606f5366955787a23ab085a59306b1f3d3aff61b5ea80a2095ba25936e8b6822253b0155ae797262ebbbf49253568035cf625925c07087d8494088f983d8a3b5ce31749d8e42ea0a01b4892c48a6224a17904ca21798c4ccb9f12b3cc39b9d8f756701c0d66aba6c60043a012b81f6bfe850ed993211a5df900b6a8f473d762b778680b7a049ff91ab97d520cf4924d5b01d6bb42c4466d4d2c63725f83ade648f3bea9aab74f96b58812ba31876f8d054f32d07ceb63c4127955087ed1b53b5f6b4d753d274d1790d3795b994a860002f97f369ce6042a8c434111caf0ec191b8b171e505abe32883419ca136ed99d77e3429cab897d5b2a46f77dddcf15e81e4c292fb8130e094a1b13158b4d315fac1cb56784f8bfa8cc4bac21793adfeaf43e3b72f3b458d8d81066fd9d636d490f4b733ef160cadc8d890592727e5fa6a393977b0c57696511bcb776ebd7f976f322db442186ce6fdded6097697a686535d09cd98c0f6a95f6885d761fc7f1f105d377f85584261f750591f1f94249139367836d1387dd25ec235c1d700ecfe4fadf44e911faf1df6bc5e12eae2961050a609f99fa4c3f66239bb980a2a492b5a64eae164324b5d7761b49aea69c8adb37d832e7032d0a041d402201e718e29fbfe3eb633185995e1e848da250b7686500c132733b40c41605077dece7c5fcebf10c1b83976f9dc912cba624a08b64cecf20df211a3490bed2f093edde5fc6d7e4957d9c74f09dc88626a6136bc3cc55f607909a1e9c5af38d4d9ef16ed6d92e2600ee55d130872db418fcbbe23a8d37bd4bac0e688f2d669858b4f5c258a5af97b26e5440b13f7ce517a9eedb4d6ad897403979644596a1d306f02a277e1c9c76d89e9353c1d7045ab90ab6197a0647a433a6c21218795b57bd0d2427962d057c1b013c2b03772ed100242fe40818444c4398293ffd87422fa2c9f83548d9169e9e7b578569a05c89b245e22f59f4c55758e942fee57c42db33d874993d496263fdd146162674343e5852e65136b23b98a46ab0c7a907e3c8d175e76fadf384bc60993bdd784fe919f585f6d105c39b299b80f39cf4a4b44decc421371c1db23d7cc5ba70043f73b4501a47e6e43e9cdd6853c182e69d57b657a370a80ce30ec4d81dba877bf005bade09d84f4e4ce24c3d47d180b4a1551b4eadab3ca8c0bcd111501c5ef9ffceb915adf368f364c0f4abc48e0e32325eda3e139b904a2b01293a88c8bf8da8b50b2bfe4da64433c66b50a7ba47d56882b81ebb733b8a4aa5d16ae8de623485ada78d5c87b2e001c783b8b84cb4b0c2b8964e985f04e0218c4e8da8fd5576ad25735fe70371e065101982f061e9197b75a5fb87bcf7871479166c1eb420f96985aec42e9f56c01acb10d7c091a188caaa7758449fef068c420ab9fa7299618899fc4d3a0dcd00b8d7537f400839e8e28408d55f9a99fe6d0050a5de6dcba337544c108e2ae71e883e3aee780b812f88edddf472073c377c5452969952a537e5de6372da1c2689c18fe038a472ab5e742ad61c7d4606b30d98d7ed0843d7ad4690f489d901fc333a893a3e8fa676dfb51494a94665d6e2f3f213b8c04f516704a216dd47c6cf2162085dacf9c06376fe60f9e688d35e4c97c8d20386185cc48f556f1cd395c7f3a492cc2e136959a4f05cb0be84708ac56e1aba75129f1456d874b74f1788db8c8d1da72183a5b24519d0264eb7a8ea863d96b2603c76e592c25a71cc0d22b7d0c142d047a867e43d4fe21b39216dd2a2d4ca4c8cba9f5a6bffb1799bcfbe3e6e5e41dab005d285e6a744d39e0425250b4528244b80eda2ddc4d2686dcdf4120d9027b14bb70ea2e03c32a68d71cb586feb421fb806ff089b90a9eb5a809b96ad9aa8ece3166c7f415c2dad714fa14c6f344155b567ebfeba835b82bbb06578a17959ec13189e44807354fd6eac811dc674ac748c28b3b6b20eec3a97d42d948f8456814512f440eb16344d79c5dacc02f9e5859afe90e051e8cfaca03218c6695845968181991f5869fd9d57fe7ecd53f5cb2c7141dd5ce9d9646954306d07efea29fce502535de8953d5ed681b1872241eb7589ca5c79f0d42ceb08794c56b7ddb869f75fcc8a28245ce3198d4875b4739b02ad28384c65b8c2740e43914d591d6eb064ce622f95feb007592082472c02a5b44ac2da580f5a4ca73485803f4e814ce40b65b71586231de1829694535980c02ba85566b9e54bfc34a81277f0bbef96e4df60546382f8a1dad379549136133da853646684e36bd9993cb6692668313700ebdedd683a65babf356e9436bcc67e513221dcafe34a092d113bb55223644eca266129da8990c3140bae2af9f6a452e28b04298d988139225646683112c64be939c2f7e5335eb328e265744103723aa3f30c0c4e1e1c929a6c96290177cd4e06d2b44a671c0d8b45357cd1134d57d81acd39bd0ebf62f3765038bf621448d10b0bdbd858b1073cab64a6c1bf7c39ed10ec1ad36c946bb09c71a66fae7a9fb5a013aa2f45e4c18d3a3e76d9ca9c381eac3325ec67f2eaadff6c01ce2fc0ee08744a34d447fd0a52467a8382d2ec745f8bc75cc57ccf53196000aa4f6da565cd8faa726d0f6dee6264bbe46400bdfd2f6ec23f1972ecdafe860d76f397bd135d3a74349767d5d7155dba54c409c15e6e3c71212225a1b0f2039acbaf8883a7d6bc0e09903a2b2bf518ea391e897e4426c454dc570946fa7440bb3ccb62e9225eed0ba25b35508f137627d46114fe3afb1091d85f7e5c1a2d50ea3d4993aeaeee101d7289e74b8b40716756b27ae4c9aa5e3519d059e303878a9cf7054222af30afdbe743ad99f53058152c24586b06133f836c6e16c0cadcb9ef41fe50ad84f7829f216e08a0aa25a3828c992d92da3c72a1eac348f9a136935711fc83913dbef91e4121f4037749868819045d4c97e9c8df1b348e8ad724dc0f160f7e9ac50b7546b45fe6d72cd91b88b1180b80e7e87b261c17cb4c3fd172dbf078f71cf1cd60114342dae02810f2c390f9cb920451438b811c93b68fb0d8dc2878833930d27172ab53ed1f6176e1bb20513727b5ae557f3adc57edf1b7800238aa988699de524570809d18397ba7ce4b30944a91490846d550d0ae8a762cdce2df65f7915191bd2f275245026867150a18a51223585513afafcf13481921fb3f61dddc35b4e5454d6d11762f2306e4c7e0838f05542207a1254bba376137115c24bb7a4399ee50e2f7dcc668fb888ae82f37e2d594ed567387abdaf284d7ae0e4e1c56fd75d8f5b24eadd3a94f107b02f02d37c226f679348cf3d3c0d7a42491d21d3f98171e75e0c208a47ab4b4a32050c3317757a4cca29fd0d8c7c672e94eb02c85696f8d985d79c8b21f624c31febab3153aabe275b1729248435e6438b9f7a8e2e424d5754d69b44fc73afa6b1d23a7f19617c1b5a7130a902a267740a5f7e5da973969ca48afa4516e34ce2e1ad9b4f52a6ef197030bf27663a34d673fa6ced37fc82a044af5f214149c2dcf99b10834866512b16db42ea2f93ed44376f88b0844583791ca04057545e66cbad13d2384b5ee738c47546b64c9c765ac6cbb626e52401da95f315a172a284a8e4eca5aaab700d9fbe90101144068ca09940837a1a5ab9f3f494801776bf5a9750230c98be899d98b6d8587f7e5e2915a161287170c9689b7da15d99a01a9a3eee6422deaefdc265cd65504531114fd30d800225efd3311195d0975b448cbf4928931ff70f1fc1fbfffb33f8937e2c85c0a241868329d434182959e123b420dc5004580d120fcd3c8eda29a1e98ce03e780881d06124c559fe0ba64058d82ebb831732dca707e54ee41bedf0d2f02823877d015d998818a3a63cc845f6afc6c82023ed95d8b90925c6f7a18edd906da0a5cab62c85ae9c04c209225948b3c4bcc5c53ebbd872fb3bf281e5b00721f9dd2344f0c0b5a344f0916d02e4ed0ce40a38e6af79f156083b35e1116223d5eadc98ccc9b3816b88257e6187a0372709f26f9f2256fe744b7a49885e41b434e0264b441aa1a98f0e4d915d89856d1af9179298a7654851da3b642d9cc4131c9a52efba9e1683e772a793e4d2edea95b20eeb2883d212fa42b4795be85e1580f71ee2eff8458e3ff2a1b49fcc1a0b4da76e04ea6ca9b90507104d5bcbcf62318b2877c62c24cc11eda5c1dc9c1179412a4054b4f2028193a67096d2095256e9589791fbe1edd128ecb805beb82a37639c6b0c243b351f93b0fb0a892061eeaace2fc0256c84ef0032e3a8951d27d1550897b07a21f5e2ed31e671fa198c76d5bf12ce1ff6f109c79cc48f8015af107f77f2b862a9a3a12e48e4a6b4bcb74968be534688958bf6cbf2f694aba2c0e50068d2dbc9978acaf21e33c2534a48ed044735b1a57fb75a2abc68a317f0beb8e799e3aecccc5198f7b39cfc1f330c66583ffa7c3f66122d40c11cdbe53818a97856daa051e6e2b03d221f9fec210822bc8705fe26e20c0433a41ea1082d8dcd79e0adb1b623c78257aeeb6c79abdfa428b436ed940c64f0e40ce61e20dcf14895f1c0cdd80a7ee22a09d3fa91316219b1ac3725c7559b18873be6c730d65e7e2ac1c2ea6c228920531adcd85e66daa411384fc76f780ed06837b331607c8d0d1e18d6363e6e5075eeba2212e985b6af168ecd439794c183d38cfa6b4142e04a877886f80a578642a1a55ee1573d5ffd61d682ab2cc57dbfeb234915dcd13bd5ed6a2d50a8022a2eabfb118135086a627c27016b1ca17475ec31fab5c73188e67bd9c1b540984bd4a70e46a62917a4500f0b5d058c995c74d5ff2676f897d837e0cfc11fa8a408061feac5a8724dce74cc41c91eab62a48e2be25195d66a7f841ab135a56a5bc9e64ce161c3c156cd9bf707055bde6cb889c28afab349a93832303c08b6f82b7a7572d80a0c0efb6edf9123a3c5a9513b53b13eebc8bfa5da07b2a5a72f7391849e472820bae446d0808d5bbe4613d4d8b1c98adc4e6206d90840ee460170a9cd28ebf41cc126b54227adda5dd26adba8fd874dde316852a6f27d7441a2a3221fe46ebd5f780f21d77abe0916a086e4968c964efa937f2f2486e014c3094dfaf186054babc7864af938623ed3c0be3602dd20e37b11bf5ec588c1e1e82707d9bdc922baab90d8ba78c96e0e45556a40425d639e3e4550ef3c0698e541046c47b85917b521ebc18c8398516c0da2ad35f0ffda6fb9abaf0b8b6f5ff2636e5b5086537a6f512a2137c5b209bb94f7b092102067e52426d02d87622c3a86d81fdcaa9b571fae25c5f50aa7f2111859b9bb6b200f838fe36486b935eaf20611f27381665a97afa43920ce3013b62f0160dbb52e64dc9bd31675567b4c8b97419fbcc1bbd80e342847de462911897583dc57898ddf4fe3f9d2fe6fb75187b1021ac35c2c10e4dcb3a5cc2b15911e1812b921d79b3e9d1f39d5833c9de929d0d06ac6c9b71bb0caea6ba90cec9f5b3977d83fcd0412289c800703161cf40b7c89740f7fe1f03e226d97a30474f851d402a88cbad3e16de4079f22e0baef28003cd11a8a82b8f489fd9df0c9c713fbc348202e48f3e24bfd4e8094701159a97f35d378c4f926f535a11c251c7372b0bc6b527fded5c4f3de11d6a120bc44f0525858d99ee036aa3062ef140ab6355000098c37c14e167713f485c28ed1be2e35df98fccc84805f15b74c262beb35c9c148656086a540e2b027f491b6e08ba409b070ca0386940c1cace44f2b4e4a81e4d38b404506adc094402b6beb329c24e578844e0b0913398bfa3741f64e371abf3bc30b737e6cdf1da88eae298f859b5d5937d55cd680bda55811f9f24731b4997168de5e896c1dc83032712044ce90294a54a5d703d40ba73523021214e29292fe4217c53ef313c286c0861ad0b424b76606c183cbfb0af1cfe945b0d117e58ad30bde837887171dd11801ec082574f8331e29ca3bd752582cf4e6559644af3808580304f0c2fd5343b69c9807705fc98db600d52c9cee43af5de176495ac7ee9abb7886f31a1ec5ee60321c4096bd7fa513d82d4de573ad6d91fb1dc8a57ca4d24509d20997618879a585f0a06ee639ca66ffa8a78e5fabed09cfc2042f5ab71e2960df79936d1346a4a1631ce44993bcc6fb53bd2efd5770853b081dfa6944879d822abc8984a6bf1d8b4b190663a001dfbb4dbd2379ec5c21a0b1309713c0a63ce8e0db91079c46525d0f27434e3be2dd52b9dbac8d9dbc5a0dd613d5a3fbf8bd279679fb019ba519fc7704766bc44d997eb98efce58bf02a2481eb83ddd23259b14a9a92bcfa0af23e4739264df77238e2c25a0a26c3fb564b2a6aafdbb514917559c7376c07c62c5aa3da013b9a001b7f19b0db3e6d8fc0727cfcd26be6929f721f204e40dbddfa2ffa45797b08f870f6a3f0ca2738507d98be8a80c2ffe0b8a07cfd80dabbb48b9b9ff9b7eb8ef113973307e8e2c5a50f261097d54c0e435f6418148c733281cc31bfc9ea53484ead400d4462927cc812c5bf2aa664bee315ee6b409900cc7e71baa640079768b7aeed6f41f22efed0792493dba054f7e43088ee9332c1887af7d4e2f5edb9511294f70c4a1f4ff82e3b5fdd0bb3666d92fb93b65590c7b068cfa378dfe5e653f8be329e0f86d60775bbe4fb665c6a20be4d0c995f0e1fa060d1328802fb0846baaaa88fc736bc6d73986c94f9234ec521ddf2fd9fc32aa851b38adcb575cca8b61bede1e81bc531ef2f194972bd712e8a3b807bda5175799b2f9693261316525a824f99b7b0bb95483213ad8c153ed0da04c353399c26f23cf90aaa2c0da4db07c1272c4db047acb8e39636a9dc0d61e5d2bb3b4ce8321146e823a6692c3609fc0b23ad060024c7c8301d99a05f7c418fd2202aeb1724ad969fa5ac66761b1a4e0dc0bdca8d5133f2a5275c4f6fa6362f428a18149a8e551fd49a90263665d82c88363c201e451f79906f5047ecdd1409f959c288f19a5170cd15a8265ae0e43a8c27f52811da31f5a4292ae01acb9e990e333976597e3324061ae3c258069a8bc1696b102014b27dc607222621c7c2c37d3a0185c555903dfcd69c12cf7768ca6643a59887417ff0cd9bf90fe8d3cdf14ae074e56cec4eabc51e723c51a35008124d0a4234a4240003701b3ea2d9639847f2b468a3324650f5a537071f70f05c3ade0bb071beb12c6cb7ca73b24bf4d5e6f4910a8225e62eafe155730cede643cd362247d42b5a200e23f7b6fe46492f911b01f910c05456c0b210c0e697e4a4ad432094599e1268a7088cc5ba9a6ef48b039e418ae512d86e96e86e7e50608d3bda774984e2faa22e2703fb2d1991c2a48c4da7b80dc28d722d83c3369a57ebd8a96c5d88deb65e40812872d43cdbc2707d0f70c67063f06c878010e11b7bb58f863cf545b81818dcb14d6171edb7e4a2f48ebc81d9d1113d95ac78be7aa964b53b0630e680307c42bc2c1fefe16b8e0e748685d9719368dd1b7ae53774e14bc11cb22e6152ceff88ec343e56462344d79785e60bad0a081ff058caac14814e8596b977c7f924260b502170e58d0a3c387b50861e4c615735f69da69301e48159cc60fb238ee2b378e51c2537781bc32d492a0f57aed8e12f296e20da5a001e9094b0e312dec8d0bcba0a60a0efe6044ad3a94553266559d86c1a3a8e4b451fc065aa24995ae40c5ee381ed82a580d983d865d83b9997bdd02c570f0a9879d1158ec059c0010569831e78eb3c482a77f2c1c8fcf227f0d7128a0ac460fa1c634e4e1d84cea337ae915392ca23d252d320cb611511d0c9febd06795c54e744f2c00227959a67029f340f8e8bc51837478f2c4aa5503a03774e03926419530cc3c372fb8249a7a2e5370b22cef2110712c2ba151dd6279a91a8386cbb435bd9392f22c508304b79fc0ee10b5497b4a28594bb697f01c4caedb62fc3f3be271b1c092dab0a4b8a3d9ef890bae7284a0a562b115c5ae8d86bb508878059ab170ac245ac291c1406a79c1edc7d233d20298fba9837868a42848540f6ebd8a06b04a1e91b07f4900a619d8012c4d1145edad88f1c07d49214dc5b75b516aff81fb237d2849890b5b3a711bc7c661e9635d44cb550ea8b883289ae737bcdc871e76dce438d2c415080d4eb8841c9ea3d72ba0d5a493f81574e256bffa7ae6848ac0e75aa0e0248ff4a28af4b49396290600a29a8dc88f4f47ebe2cfe06a2e2049a02f4e45dc4bb1801dc005347cb83ee469e32bd301f2144978a5a9018a0b6494e7cb6bc4918437cc82fdbde1139989cf7856ffa11e400901b009f45d150d020f5e74c15a2fd1a4f830e650225c31604b74afe17d662a568173590ba710edf45edd4769a5f42a709901a0d9132789c77a19069ae3f5e19aa37ab47cb5ee3a3047a6c36059a88e5012621737dd004639218ebb134c45bcf09da211e6dd592972bf3f44894be9ce4607014e4536dfb343898762436cc712947905cd7e8e725151bad273cc7e134bb53544855bd903e19b4e200cfbe4527594d5733937b02e171e311b2e2cdb5bc0989af370095f9cd03221dd0bbbc9933ac862f377dda023ebbe4d1e940875814da93074d84437018cad2abc1a484a26a187d5c126c2fdfe460b46c89976b02857e086aea2e61fba5eed0e4c4c0f1e843524aad3723f5d55180701bb94b87229e9753fee6d56b03e8966697e69b605b941574ecf0a733941d86861c16cf935a2a514f88ef40a870088789e811a43462a81e5c85208021086f6ca5fda9837051ecf0a337cb68ea3c136762b654279c8c54e590041f968e371b802038e36d8c5df3570141671b5cfeadbaffd7fb269b428db56d31174064b119508b971e7917ce6fb5497195a05ce5ac8975bdc266732b280638fe83968a65c7e88e62e97e0a9a17fa728824ca35084c55d88da80d7872caa71d091774707b2a8d3e93a2292db0151f5734402f0008db4a08b7c2d8be43859c006a62f0e7fb6537f6ac8fc43d09b88f122d7c5432d06460b30c2001e340fbb4a0f323c02b8b412a447181a116844e1e24e11887971cfba6638ed6d1bb13062e85f8623e6f6936415eea6fd264d9a8e3551712ef325d48560567a96c6ffe962d00f5a059d7bf33da7fe2279eeebde8433b00ad5f810d7fb625e6b8ff205ecc732434406f51a5b86a62a33b3c35a974df0d0e128f47e5d49c0d2b856718bc9a30451d148c5f51e77425fd6a1c37b0d797cd6cc029a8ddedc020518f7c2c102a34a8b416dd85aa7bfeeea400ec3eb47405c0d611547925780f6a67cf6ec26464b70d8927a20d1078cdc01909d1be1d52a77707355b61ade20b1177861d8ccd01c435d3910b85906c78e6879c02288d5921ee560d7906f18223ac797160110207082dbf5ba5faa0203332d3eac7534ef46162a25742a440b13a882838b091e2ef1eaa0ec1c6286eb15f18c57b346dcb058d93a085ca04d7b24460d148395efeb08993bb5a67c800752d4f2067a00798f67453a921e5672101ac6f0b3dcc15e6cbd97f3dbfefbe76e5e2768d2272ce7782ed67188bb5158f769771b323e4fe838e566a508e3f8bdc84d1475b449da6266a665c098e195ed38c7d54408c954e59b78ae1c0f2528d779380f6a502cac503f89c02f5d1f46a8c56867e5526b0e604dbc1a2195c980d61831ff9831b8647e20130c5f3c900259e15d1da5695fc89aa2a0b1f3302c3adb87f068f868465af72d2c264a28dea23538cbfdacae66038d525c9e6f5b4b1dcae87ae93635308431adb0f887b8494c650b89f5b33d25ce5f5604dcd87cfc4448ec077ce6a152d0c954489c8be4909575b0052fd516c5305cdd9e01d9294878a4e01ed1cc185dcd43f318c79cbce8d09f47e57e1da7fb1741035de0dd0e2f4fe8dcc184da5b4977dcb84f94bf6e3c5c1ad2850e413d075c1f59b376cb0bf9c3ba1aad0fb4870945c70a4f376f67e24b04a640d0c4504fe5f13a292a32e54df1e396046768c641766cd5bff9ba508fec52e54ae1b05a46090004a69995b01d1a754d3c2e4a5b686f413b5a92e2b62130f37f1dff12d29e9861366a1d379a08bcafe3b7665457c56508e642bce2c202256dcf200cd7488f3348fddfd88529b0ab754fa9dc12a5b545355fbfbc65dd206635f5f44009a1a6a4bfb99b0d5990f33dd67207c2f14b220012cebec72616ae02f60420ca6a6b861e9613d4413977f618590e4f5849a82a09657ecb51477076b1826a6861c21755b9e25111417c13aff1d109f3611102f514487b92700d19115c9d0a24593dea658c53a061cdebcc2f93445565d83a1eb2d8fb8e5a300a98af15b30618237416ea8f41902f6f9b496d31e8d6c24cd8f5e9e305e0141dba80c835802e8fd4a86ce0517b2b90ead1103ddf455a75e664aa3c13046eb53eae20d1f0ec2171e531245e571c25da123dc77e1b06063bc2d890c870fb227839556505a418331498820de7f84d0a7eadfc689f63095fcd0a9b96d80e20a96e04d79f109f59daa0dc831b4f31667a84feca68d2c9bcd0ec2df902f0232e05dfd8f80668f0d8863105171586d83e878d3ae9e87562e2e0be0df4a7540bb1f62d22b28975dcb143384c9ffb7051a7377fa848816a1a6165216f299ac9f0eb3c13379a9a65a37c29a08f7b5ca60e703388040ab2e0f9f9edecac72f4450fa95024c13532a6b7679e1444e62fff8a9f4382d198aa1c0e4e7d0913fd01dcaaeacec8411fe98e9772d2690ea211e8733b68e596295fe0bd30851e067f3d5df139d10e04f95182923dbc7a04d6c17de6a5f6c18103cb4ba19d0a5262f85faba3e9aca6cb5fd9b4d3ffb29ab2b45afad0a6d0124b57629d0c0b1e2b71e859fe745e9254dc898a282beed28c409adc68705812561e10ff93b62177f3a71620b2aebec38c32ef21cc0a76591b5d1a22674d002a87203ea46e237119404ed6ce8ae1b39b961bb33f6eea42d498ace063f1a1f2bc32a123d865c1ba36d0234e472b9a5f7bc7d37d40cd10b08742de7d0923ddad89ea3a503b838009acc9561881044293aae5cbbd4a19190f23c064e97eed720669da61f7c0752ac2a2cc7886e6ab17453299409a888059330ee742b6322734b89791f550bb40bb6cd9998ad51508bf2f47e660dc2b3f922d45e58b98c0528ede4515d416b711c7f4a12d30532aab07aed285075441aca0b029060f93b363fdd34e704ff392c5d88ebbdd9c93f34a06842252d65b63b1ec5ae10c3b6d76c35bd25596fe83515d2423c979cb01c199d2c657ffa60acd52b6dbbb5b5cd6a560155c79efee534d6b158c0a41811e292878cb2f01e257b200730a05c943054e2ae1726ab063ea6780f3838782f41d8cae302566cfe669a47ee88af71320e551e06332a6bd73038e08f24f55b0a01425b1a2ef917f3217846ef50b2dfc6ebfbd9f105c66568c5e5c7551da47065e5d659cd92415b739257a668c958701491ec3bb4266a9061dfbe8be448852f25d525debec0316cba2cb325d1fe09dadcbed33122953fed9b0fed4bf6319f810f45f6b1f58a5ab2dee38b6e777161af5b8ea60008d75154d7d7354ecbbb6b7d4de39877797def1affd8b7ed4d9f0fbf43e43c7bfb116b76ac33932fc27d4107fbc987e42fb8597d8c4bf8cb26af6418f2102cb245a9792760bbdecab359e43905034e646a4affada4f844e4536d3c411e9623ab415aee61f215922b004d98c18e71b74ec2cd92d58433287efbb9700ad523cf24aece6b30d8965c4aef6cc41972859d0be6473f5ed05f476295ee252b5ce5fceb6c932d3e2bf695ba16716b8507a87db37545359747b77426a969d7f10f2ca4d06fbc73bd752949be25b6a56f1829087827b2d715ae272b90d5bc20adf6475fe1aaec9cd74790df01e540f8fd3e93ed9441a09eaeb52c73dff7b71b3849b84ff893a0391c9f0085702b11f16aaba8ff0e3604fb50835b140af2a742b352179c89bfc0d4e013a98bc0d816f7d9c93f37b40b68d3b12835b3d1a759f50c8fece6258b5b11e2bf0bd0f9cf160c5d162b51755ee79d26218120c03909aee1633c3757a3eb91cb3a401a387ff8b24c18f6d31f6c92abff1b7969bbc4ad2d5b7d45c2cea1cfa1e622935c00539f6475204b12173b017887401e4be0ae67061c874d90b05495e68ea178adfcd1328ff7c08f01d61e4c389f1efbb1b462ff8e205bfe74f3c75362fdb5626e9f389455ae4caf39b00088fed13b1c3d43644eeb04f070d57e6b620a6c7cfebb6016c0897f4bd7568dc50cf105fbc9311391ed6bc3f9a93467a151c353986e030228e5d69068b9111dec876a91f4afc5c1c13ad3b90537f38bd235e915eb0b85168199f58b4c6474504bc562771f0833b41a632658b5bee28f371ca848f1636b73fd8dbe0fa1bcd66814b7843ed042d6743ebdd707dcdcd39bcb330774a616614f698a01f2617d236e2c8891d4860373f1917d3d4de40dbaa2076d69394033d8dfdb6caf37d1f53a030955cec1be6fc131cbe153cae0df6cc8646e5cf289d96c4e7f07bf035d10702e39742cb724f55299ab3509009c42934188929030682a837e984135e84bb8b08a481a948e63016477d93a377adc2a804cb3278fac40387bd6677205f9b3221b2ae8cbbd43ea3965b58c78be4f09c71ebaffc76e524aff609021074ef9b09a274b7ba62d601d05a50c31869fb23315d6c5d195aeee1171e15d20454eb4da390970222290fe81b0c9c795ab15a48da09ec80edf7067dcd1e488677b3546db280fac43a7d1cf00af8e7ba02538e711329d1623d925bff89b63829990a1e878c38994d687d46700e2417f18d087ea7ea9698c432e3101b16a0a4377a356ff10838e9c358a38ffae21e36865b199a215b9b2332bd7c920603e27367724c6c2df1292a9b13160282cfdda2518838b1098ab036b4f3b0b1a2fedb939fb6f3e4e5f01317da779f0e780538334fca2ea1bac5821f8385ec1f2ec32b19140c1c6b02860fdde082e5e446c2c08060bda3223c2e8b8d5d2648744dc435d2287c97e31d4ecc088fc2ce16bf4c05a8acd1d59071494793b80ee41d44432de6b8480b664df153a8020e6c140d182561b8dff0edae34acd750c18336bf9edbabff981c2ab4ef9b67d6e16ec5975b93575221e1a8054c811da5dda424dcb9a37c873d6d48c7b3c885fb395892390fb84e3a38fa142fe24f0ac0d434ab14c25072d2de9cad4943718fe1b228592bdd69964c789abf7390c69c1842ae08ec325b6422017d5d23bc69484b70815219f3077b966253dcf87cb17250b4ef00c621b25e1f5cd123c30f04a99516a831e3ade53cb3f224cefcb900d5207e26797d4a44844451c0d9b067485f68ac6fcc03db623dc99bd6395cde143f0d2a3744eeb1597435ff1ad5bd2b50e8a5ed591b170b35415414ce271129aabfe8fa9558876ee508f92e94eabcd2d755986d5793dbe36ef83b1117a7264cc4d741aa6135894c5f5cf751d4965fd55e3b2e17060d33f45a91583d9f08278e4adb71e0e26e864e4e079a064d3583d96871cb60053f611c0284eea9bbaae7202df7cb3bb10e8565aa827c05b307209c890f73dcb4ea156dce923d578fa16fd5db779597454264772701fe9e9f858758750102f7da75791a8a1294e206e0658efd6c26e8965652d4892b7514c70ac83f0b998d7024ea34d515ab93f85c82ee17e43d01f5b7c48cb13b687d1ca54b1640141200800a2f56911a8b8aa82853069ddaf66012cc3f6c1f8a340bff67ecccf45bfd6a9f031f6f37f87d6d963e7f2e93c557fd3f7b3b28f4a672c4ea00829584abfacc4efc1c1ff78574e5b02a8bf9ff229bf1e50bcdde0603e3cafdddb374f19a5dbf4cdc7ec994745951d71ef4f39946dedf04b483c4e8d9e39da357bfef40ee0914dafe5369b2ebef0cf18d4535cf0ee296ee3bfc6bbe9040f71cc361bb368b68e1f3b38a5d0f35b357d1f3e7a54792d9b1af5a84f94d3616e5b9c577d42072adf36e96d8ce1f56151f7845991e93cd2a5f023c42e254cad1be990a36396d0200a21116a7afbf10c308dcb202fe115a264f12084b31b32e1988e892556aa14b68c83d657cb9fd74d80fac6266c19b17aeccd579c48551421065f59f90b0eed6e51a8d381e3ebd2a5fac1e93edcc52ba959fe687dd0bf15a62d271691236bfa5614a2d4bd80a91900b91bce59b227ef8b8fbc2bb3c221b1c9d4afe1926d7fcb90a5193671d2c0bc7f0f9fade4a8c742d8e9a8252056f937a771d53cc75ae83824c404fdefb0dd6dd4ba93acb1c6fcce3b7e0770705d6cbf30c9cdd9a02a6158afa4d92043ac6a2b05dae97856fd0008e417d6345036829d8cd37be6322a20bca1cff5ae6fd08c525c8fda31a151fb1661080cdea5858a99f3a5e25552b194d025048441370bfd12bd187aea4f08d8d340512e61a7823723b48770c7fda02cd45101b1c276799180bc375a6910383f93b60e62789c3407e587322ff0258d8c25b5ca3800a59d5d61655c919dc3ec1d2cc3de6da90a483b23c0587dd8060f586c72d02525b67f6d3857eae8e80ff881660026376734314003ea52e3a3654ea6b3c8b0877266d6239421926fbd2d53dd87ab40df0c19e466c623534acfcdd593eb5f72af43b400712ab7799aadb6f6da7e95c97310aa43d9451097f077dd6cb601b6a4724410864696511d90ae181b71b23af5bb7a2d0129da6d43ab643a93748c6768b5f5e91128106a363e5e020342a7661e8455dacf528d5b876b59a711597cd26110a0175b8daecfc4c646b395342debe32b4028e4567b4d7533b4a5bd22a7eb48187b1257b53e49356e85058dbe0f02a1d4fd1508319bf588ad3e4bc77a04565a3e27353784a3469f0752ed1bc76b49bae667e9d6edc955c53ddca6e9bae21e56dd0f4128f38ce76875ebe3238044eb5668d0e0f730a5b9e1bc2ea5d57e226dba3dbbaedd866cb4ae57aec32633d5fd150cb1ddb84aa5b77d4a686f0a071abf1ea6b5b74e579274ed0fcf408150d359f55be735298df6b3274069c3edd1752e7d732bad8f8f808110b3d94fe1aae62789d6edc95522bdede3293020746ab6eef674ad7219d6738b9e31a7cfe01564f314eda535a6bd02c2d161d4a8d18df93344becfbec845f89fd4a3acb91f32e5f7ebdc5e2709f29f0cadf0bc7dfc0759f5707a7bf03fd90a0cd1dfdef8db5bcfff255bd090fee84dd827c9da6ed737f704e010c4e42ec41f7b77e82f320b1ea2d33fc92a70487f7ba7fff4be1f8028cdbe30b103431fbaa0ab7fbd88c6004fb9fd4af5e0c1be917cc85d73fc4163f909542ba244883507167566c96021699e0754e8d9fc0f5edac2c2d4046b4a192a2681e1430b18d11d2454965df7dbebe52e9c90fa5e0de7efa61f5576fc316a36a17166d84f674080348781eae1d840772156751734d28d90cfd8bdc2cc791f874dfd7afcb572f7bfa39b94fefa9aacbeb2e6d8b11fed667f56b07329463e306c5efff263ac4fe08deb47c963970032bb1160c1d422119b9ad2497ed8a37e040fb983ff5af2b1fb7f80bfabed6dc251e13015dd984f99769bbaed1ec83c941d7e880834c29eee585be12f8bab6978e8f5ee0ea216f27e9daff5079901da399756d327c192f6a48365d869880322e18c572370fe3a0f724bf22fb7abc157fefada15473cf55fca1653a56a0464150784821d6fba4b0b0cad77b38815423bde72eaa24b5eb38a67eb1549061309d134405e7b9fd1d8a27af697beebc6dd580c624a21d7acc42a5624a22ce972f16a4a7c9c401b73501a611e07f285f42747c157ae801b8a2de289924794f86779ce4725a62d91aead5948dccbdd752219682119b45bba2d0c6854b04919639c40074ab1907f894662c258b4c6268f2ce49acc42c9cd23a5c674591dcb8bf0476481d205f8dcc8e8d0f3c89f0c2b5fd1619be27f8accff0745c3f9525a6865b10885b359a4d42f1ad2ad821a1a64818cd3109047398d50be4a96e37b6f5e9262853ee67371d1f8fbfa4e4d0ea4c31cfe6bc85dee47fc7439fa2b78ce2803172d9c5b5962665b162b28422cb943cf1b5b9e78b5baef3a517a9a7dfb372b6a7b8685c5ef8fbfbb21e8000d7be14c738ccb66a631ef7584aaaf4860d0740bfe0fc21dde115d4374e8d5da5d58e96d319179a91168d93c79f1d621bae6d1b792bd9f90812053ca4ed4a90502cc2536cb80008f171fddf736df8a7a62986899c8fd7cad17028b0ba10d08dab614602261ae2b98f31d3f4c47d68de2abc3eb6a4656437eca6b205bea11d53e8142ae95be95b3da8cd18ef76ddec7cb851b7622d506a87aee324bf452277d932e4b7f795e41d434ef7058459d3d0eb7ec64ee8d4954ea0c4ac23a448a20d5794dc2c380d9bb3cd28561104a0bffa92c15436b516a7d459e265472d9540c1c849657fea4b25db5f08d377f8b0a1dda891a8f582d5a585bac33aef7026b17b73e43d8e1f540d990ebb9fdaa4399a9aa65fd4b406bef88025aa7a191dd2554c88ab39a93b696307d3656fa01474cdaa63fa00e04ef19ca1d428e60e2daaafec19b7b505fef43dc6731a6c9e3b8bde6953f40b7678c6970b825e62efd77987ac940706b21f5bc8b4e69a19b717a4bfc659f88d2579cf97781dbb9134bb82dd5ff48752606c331234c1a71bf3adb953b59c563fc752e0fb1e3982f83d01845e654fca040e700e69b193ec82fe3e38eb87f456078e88d5390f0b20cdee94b3ec55c343ff114e57efe2fc35ef73a7d62848ff7dd405f6c6b769349df166b12aea1338054674c358903cf8d8b48bf737394b7f744544d2feac34c37e72c9f5a59bc2d557d23962623bc62fd3efb72b6140e3abf81fb865ca7368ed6d46a7a85f224ee5e9f6884aa0d9164302b3f80510360bf169168ec1a5d8b38bcf19e32d84fcea3be09d1bfc7d701b3fb9ed1d0fd27d9286928501bfca543ef61ed20a1d4bb7631c925b9ac320c5830d2cd81df5f8e8e0bedbcee622a6ffd305a70de82a53b876859b3abebc5c901de69fea666d7e609a0891b7ff40f467483b87bd967f0c6cdab30795e5d715d559c9fb8fd95b789651ab5318a1b92cdd0db88b63736c3f12e009631799c125d08ea60d12ea03e806a438a3557d2fecc8dd28524555f863638676a8df56ec46e602e7122d517ab4cc876155f1fd07a70f50f71cf09a230f5c8864f3f8cd2325a822734953e5bd85aa3ed48d4772516d543e691a7c759ffbbc39d0375e4972e4a8173d7edb990a66e54fc1212888ba79c1581d7bd47e255084917dbf56f8d26058f929534ffb48ac8c558bfa9a8b9db2722c52df3085e1ac2a87660af55c4d5685d8c849c8634ea0cffda4d91ee1674e2018cab89a554a6f8a7d974ecb45ac30513631651af6660f9f45c7b1919bdcbe29fe62980ea659c28e58ef0f6440d2a59b764c30901cbf4afa9d72b9ed69d275310f02fecc99548619ba50de5d1252ceae54035be7437774dbeb7799901171b54ad0d6540098b1667e450fff2ab2e125998ee843e44e9a6ccbaf69aa42a7108ca0ad3db02fd1ce0ee376ed3d0300b7fcb421f11af3330032b4d9a7f22415f3cfc5761776e880566d7ebb32956d0d9a33534d1c3e326b9faa744da12c1a491fcaa50fb5271250a65ea70eb7a5d67523797dc0ac05b7593602a46634c29781b6325e36949c508e8af3e76d84f534a897ab4ec3c124abaa0e367cdf1d06b97780efac5528e477e88ba1b87ff8cc8901babf44e0b19d6f2f0e551212d12a7c995638ed8f86457e555586b1d89ded3506731853815a27fe195ab9dd25788de93121aeed1eb4bf8172ee91990d8b7c9b3dcc48e6ae6456b68386e3c91761d6bd0d43371515b4134bd8a59886633ffac12576c5f3a09d7424de3ffbbbf4793efc90446aaf3684ce09a50e73430aab60a5e6b588c99da6fbe825c7c9ff45449df600b20bf77ee9d039497d61e2d8ab6ed6ba045828635d4907ec5c7f580701c3049b80284817bcca4506eb12cc7b54e97e483c21ebacd67e2ca1b565e119d49c6e84813ffe9da21faddc3c4099a151cbc04f76f22472703e6ec03f218c3e5722c66d3e0988b2f49108a13f32f5445de7e68b10373eba433ddda86816c6bf2acdd40bc07d9cc31f4fb93e4f2c4c0c41d57daaa55b40b8eda685bfa3be43ef755d875e4150870f48268eeffa84e9f725ac38360fe6c036d711cd0d67cbf85522a2c3788fa84d802e485ecf7b241858c48097f055432227d19ad9b1d8a0046ff16f668c50a9961e4d69fe24f2eb0f0d8a215c99e104c785c4f26970e14a788c8ae84f3b3cf111f016610b17805e6bfc7509dbe5111a3f69f7281235e1e91ec8acd47d273ceb0b05740cde61c2c8120f0f110808bd24ac59d11dc3821aca83849cbf868118bf0d7d3f98633f4a407cc4ed7a43d3ead57bdaff4eb2e5bfeb932fba6af2f7bdd0bd4c60deb887a738fcc0dca647b4959d1ec76ed97f1e0b72edd5a63924e0119a1e0bda7706e1122d0d72c3b7e0802876cfca8714a0dd5fde04bdc655a8b8a984e063bb256ea33c6caf8017a2d3b6193a868f6494b506c944c51740c6e1fbc69be9c859daf3f16d2584e374f35fe6b8521828f6023f939c68842198291ec47d82d13c9355a837cb49d75475a6af0c81b1bdeb81c42ddd4b186ccea18a89ee699bf8c0bf77c626501dadd9e59c4ed5aa4d9b03b8e89121bc67dd8dd84ba0f3c18014b75d6a7d5f0eb0981546063bceb4fce36e1d4a869a4d35e10d9cf3b03a7ffce7e8dd94c45f83688e1fe1a4f8f0b3712865e2ebf17064cdc3dec5709d3a1a1b2b2426f4846fe4c19e7791b9b3116a2cdc3cd84cafc528a3bdd167a63b372f9721a46aef15663c7e7615d6023ec6f3a368c1c24661a128d0fb5f5ef0a34fb7a68339d0ad5a78bd3a1fcf0561291ada4fd1269709e02076abe9eb848fffce711b73ddd557f01aab18e773dd45825174035165107bdaba788abae04bd3b2bd56fe1b4107deff3e2e1eea6bfbbfac25e1a277c02d4eaa3f9788a02750468e6b09a77083a6a047e7945c50f979a82f21c49b4c537359682549599251ce618b011554411cfb1b244d6980da2a69a45ef3d1c0a708003e96e4de7e4796bc52ab1ea4a2104a50955d5cb989469db0f2d83230a7024c2be5108e72a04fdeefeddf883ebd6abbd52f757d6d7f25e51b7bf181ed348a838d86c4a54d208a2944326018cbb01fa3b254459404c31930e92f708f42ca7254892a7b48a3d650cda9c609b016bd8103ecf350f35795247f6966aa65ff1929d68ec07e65df15ee9a609331e46676e78508e2bf29a6564abb910a462d88c01fb43d329d436d964efbda59432c9e20370049f04f603021aca40426d32bede257ec8f48ab60a81aa4157d2ccdd0d10cb4ceff566b5e6b9905716cad83dc0e0d21ad9839858a2bf436640a637cf6a0d93600bf996dbeb02ac4405742d234975e5402412c50464504480cc5a6b2d50ad592f7119888307d50e90fb8e186480ec430681206dc82fa0404005f88024401e12db5af08548ab2e0d13bae60908b556fd839229dfe8960a7c7001103ac0554bca21df9c3dafcec16bb797dd25d28a446b06b6edec1b570f6cb01561db85a264ebe11066b6b62052a135b97a259feab4d43c2500fec1ed5e85cc64ece02c6b00aaf7822ea2d60a921c3292946f690ae2fa976908fae1af59e2065204c1592e69e41bf8a0830efa0c04414b53d15e10ec85e400f2e74852d8735fb200638c31c618037170dbf75a7b814a24510014cc8081174a5a59df2f94958562c837bfcdca0ae2b6c13718d61e5435e811fdb7a1efa5ea1544b523093b8ec837f06df50ac2da0dd5a94e6bf7f42fcdda06e1738c3fecded841f7f97dd8bf8afdd6b63e0bafab9318804c6f05d885dc640d35087f4023e4a96e8bea3178aadb92915dc8f549be799341ac576f1200f9067ead20da0b05d666da4276a8d6acd76acd0a69c9e550adb5bbde0cb97a45412cd19f33fdc412fd3fd95620529f8c2485c9183e02a86660cba50c456c2e324f7568bc7ee8a90e0dd5d3f184f74563165169108d8038d3695175104559a64e3d3104aad72b8ba73d05b22003183ef5b61d54b7ee8d2558fcf55bab5bc763071b209ff644d7eb4ff5c1edd55adbc482f6f5c8ca245f5a85aaf20c8492db6fa05fcf17935c2298e905bfc51f13f4afd3a00dd7e96f28d17fc90bbe4978d0cafdb70bbec1bf8086d000742f19e47311155ffb915f4692aa640c1f416b0637f0bb56514abdec81d8b5efaed59a50967cabd5a8b5f65e8bb3d50bf16624297b9d00e2aca621f25497060a83270106d5da750e252e3b10870aa2faad59c1187b4c53d1df1ec473ab1848ba3456df34aae3f27a3a5692b41949aa5a37d1dfe51ef770a5b49a91a4aec7f01152d891a4c09a81e79e974b39c8a3217646926a135da29f66fa095d095d39096146bfa4f0252a84995013dc2b70184c82de0a21279e8cb434835e07fefab58d2ff83cd208ff256f69895c8b73a5dd80fedeea1863bf98620dbe1066d62a5fb71a84e2e4b6d6f6f5be77f6445f3fb982f06eb55ab93a7decb5d6ea4454441a7433dc5a9b0999c94f240a29c9ed336befe7e16bab9610bf21e857129156db7d868b16d2ebce84fd6df6f5ccd6d75ab5745a37bc445aa5f840d2e5caaa2b57c78b935c560a4ecef4c89a5241770296d41a158ebc11d3e58c1126a9ac5c162e86317cabdd2ed06d696eadc5350bb0a4990293142142c40319c4636a6510ac54e30585cb26c34c149d39c296d284174a2d28a6aeea6f6e53a273e46f6559eae810512acb058cd1a93107690e4ec69c11c2847861533170d812dc0d9382b3615c706594b892c2bc4018be5e589a7d61cd10bfccc890993ab2b9954b1232ee4165c303abb284bd345d1b52eeac031b9351c360e0c8b026187276883aaef7f585f565c65a7bf157d8979a6fe6799ef7816036c090c2300c65229810b01e198c0aac0a98979f9f9f9f9cc1bcc0d0808dc1d0a04183860a464adb86e9086b12755f3f92bea270412878aafb52c266cd1bdb13dc8604dc068f0fdc26076e03f5c2060449f925c3502e99626392e304cb41222748154521ded49299941997b032615c1efeb4441b95af1f5f510f546b12f8baf1851bfb1b18f65427078d9c3272ba7cfbac922410c3e8cc419a830b81ad6a8a4d08d5234629cf17374ecc4c098788e001cdd5e71b449f398b111b3e18dd8782e0504dd0af510f35eaa1c120d9c7212888839d7d08b3f73c030db924914762cba5ab0a98fc817cab0501790909faf1d5f38826623fbdb42bab6bd745064806441b946c6b926f05b8304010e95714ba2e23d6b69567344457ab37f26c9c2ffade888271f6edb55b7c5104dec8fb69916948445addaf83f5da10883626b97d66abb61f41a5add65a35bcf55aed07272c6a42d040c66b3d14eb060b55cee6d14d74535a67ae0d9493ba99727572907b01038f4a8e474e890c3b27d8f9e5ac9c2470b016c02a1255c74a8f330e948f25a7184e30e70372755e9c7abaebca19811c97ad3337766e39c99cb95c97b37339b293f8e4a29c5f7250289e3a6387c971a08d0b4d6294f92145eca90d1593932cd725b40e387dd4545184349533399aa74d2873684ecac6022f098a4e812125e5f4713a97b301b90cb9761246bfb8fa044aa795a8988db38d20c06a03a60db927670b66fa85b3079133b73586926918b4502f37627a085503e6dc71622585c8751634e700725761de50aede42d784625f504c60319090ece82221e754e46c3a676ec9d94072d4ca996b1c58ce3267063406addd99cb79d13bfd8467860a678e3e69ab14ec60da0585734c379d39386067aeb3c6ce386797e91044a95c600024272dcc7d9d09c869c9d19d9cce33078637540addd3317427fdd24b754c8f8c335752acb042a72e1b3bba8a9e51e3e53c72fe6893739c233820c849bb7214a4f172774e30c7845ea15927f5b2878d99d30590294cce496cce49ab206970d234f77e3afe63932729cca163e3d52442209783a2d12ed66439c3a8983387651627eacc39e1ce38556270985c4c9933c2c3f19ee73645de9b31c2db21a7458d156c4128141b9053a4ae9433945983f554b726ca8f4adaaf9a286974e50d3b1998e2844c580dd6a8e60c53995b03e4c7a7ba354ed5b6ad55e55936954d69aadcbb01351580dd76c0c2097df29708e3ae569e7a74f0a07a49f5d587a27c78fe8d627d6dcf6f50ad559f40b68e1b9344d7f189ebd80357670f63a9b57660bdbaadb492ade5d75dbcddd7fb3a514731c1370dc4175f4cded103f286ffda296289fef60c3654d043105c41677fbdd28a0278b4c88ee8e08c5b688a02ee9ab01cd1d9d938c106d49c393638f8032fbabaec50129ba366840d97bcdd7a20fb74c794f5796a58d1c082038569c9c834e5851bcc0404000e2822c606cc489a1f7242aaa75b421213d3d10c0b4e06a61647f7de3434dc5c11588d45dd71fa121287ea6a6c097735c2d0b0b9710fda3181799fa73ba62a7ff35274022956a8844a035a98e244513903c74992375e5ebba5b1dd52938d4d8804a703e378e06c06e2ca1a720e8fe07078876b4ab271ed70562f80a73bdc1010bced374afd5a15d501df5ffdf12561bb9ba4fd2c6e5737698ea041a3956fb91a5df022b0d0a0dbc988b3938164cb1af2435503638cf1ed70ea94f1c0b5f8763392703632bb195ffee7e96ec694a4322cd8c6d8c6ec94bcc031afafcea12d50e37e754aeb0fdfda1fa291ad1be991d65a6dd0c7ba881abe25e9102e5bbb530af3e2d39d52162b0fa8c96262430718205be10a6d90feb651f29eee94aadceeeeeeeeeea63a6a8d3e25bbd80a6206d415a8167d6fbd679f375a073fafe14802246bed4d4713f5ad8bdfccd2ec539b01032e7a3cdd291d79513692189293c3d184b54e7a60146cf28215db7bdb12b5c57e6ba7b576f3a8607baddd293d398db0d65a6bed968c9fee9470947894722b28c9f091f7fcc5bb24342f7bba4b129382dbb2dc005b0ecaa15d1294b74f77494efe56ba90e46fa5ed04a932861f729ecca35c799524c3b694d4428d18d51b130757a9848c102fb61956d23ce14155fa50a19c03048a470a37cc183196cc3c315e64b1c3244e0a6aca2e664a295faeb0983daeecb460735ac1dc09f92ac17684a3ac8241030e0ca43978ca3a5f43d8985c593274e52e0b10296617931f53259714be4c305fd0b10389a9dc428a619a90472aac44a16b4c2b29cc912d245009d3c47603898985c8181fb67387c9e6a849c99152a9678f39858e0dca4d0b68ca27634aac2e495e306f58915162a351c47c8342c906aadc72be98612be1858b72471d17e0b84893a38c090396c9c295f20428659e25a610236282bc4082798e50da71812bb5b838ba72078a9c2f6bcca918cc2c30b61830d8784429a5aa4c24165018d2a3470c39b0d43099762d605c19b3f5009b6a23e74c29b6339b6831974c797281c891b284a812cc092f9c6cb83d364cab3d493ef4849993c5ab44b265961163eee11255a58472528a49a2470acc0fb127734dce84a1474639a6a785ad8598e9e4cd132e3364a47809b3624679526651c551c13403a424c3c336c20d332acf525925cf0b5b0e3b5de2984e6a82982959f8f294432c5e7278651aa932674c955ea24a273e969e909c34c696cc2263472ae0090266f2e82acf6cd99ec6bcf0a2bb6233e1aa6c5255de99ba1365d2f16112792a879c65f02cd5c073545ae1319bc06c4d5d65922daf315abc9463ae985e57260955361c53e59e28b3ca8789e7a954e1b4b9b8b364cbdd392aade03901ccacd2d5c25679648cb98217dc9512cd559baa335368a24c2c1f2d9e6c36ce387696ca37768ecc1c1e9b0e302a5d495b25d718938917b3ce95d2cd559b2a38536ba24c2b3ecaaea71a67589d251b8c3a475478cc12c04c2e5d2590ad126c8c69c38ba974a5c85549a76aca1495a812061f604fa69bd34c23b654fe103b3212ed9e5bdae798835aabe5bf0736509d12580d3a005aa7510fd5ab530fd53fccc822ec065f39f8aa0297b556bbc90e94be1d010d61bd7a394443d0c7b7471b4c82bcbae184e71774a72d4f773ad63c039eee743071acf7a7bb1d475ebc333be219fe661eed5e0ad0f71cf44b43d06f4f71ab8ba8200e7aef44fdea462060f1f445d4149e57cfbda741f6e2f0edba672f066b2de8b1cc2fedfb482b0b41ecd73dff2ee897e679d05f32c85aaa3abbbf5eabfcf5ddd31a11cf9a7641152b8a3eeb4645df5b3ca2c0d6abd75eff76704c7167753c71ab4822530fedf4b675f2b9f7f50718d93a55cad741bfde3444e8177c9987b4a29f6fda4d067afd79f8b6980e281db86ff7c6beddf37afa2c76fcf8023cdded40fa158d45b0bd968d24ba7be620f8854dde46bf79ee5303123978947a18fda3c93c9016f41f088e34702cd16e5d846d1c1a5ddc0dfa6d44e1b9d7f5236ab7fe91e2877b3cd1ee919e75fa91e8d0e4adc353a71f60e4a29f6feaa1dbc11c93173d0791a72f6281c23d7d1c632ff3d0739079d143bff9f82d8721a9211465b4998fa1e8216de6e38c06bdcc43da8d05d8e1cdfb79a3fb335f711f03cd380061fc83431f1f692038a290b948fed06e78863d4f45d1087e74a3eb9ee823f369998ba3e8230d4d84a4e83ea2288e331765b4cf7de8480b7a198dbacf38862842b0d6c21926c70e2e7bc1040791a7cfe28b3ace53fc2376a03416df23099ae74d54bdfd03699edfebb74a3159d46f9d08457bb56c70407d88a7bb11b27ee714e694f51d3aa8e8dbe79ed7cfbb794ec7122568d1a2858bf7c8d9075ceea7ee7d14d3a0ef403d50f77639afbf4ec35aefb8c147269bb94fb563079ecf3ae4b16d588915fd9f7f7091653ec296ff5c7683cc5ff1861ddc121b464a650ecedce83ea52e8e764421238dfa3b45ff8cbcb5254bb47b200d6560fd3c046927bc15fdb3b65cd7ed05afcfb07b8e6d4561dd5e9a11ee6641b463ef89e0f5d89b4c1396af1e3279bb3b897cf5d0c51fd0fb1b4d84dd32df50d44fdde87e7bd1cf37e9e2471a759346fd94bc591abd61577122bf08d543ff42af5eff0b7d457fe8962422896827784abb854e43af2e86e058f4f32250eae1665358f2d63145f5108734a3fefa3d82e486131f1149547d453f2eeabf41441a74fdc3f5757077425d10cc65592b55dcbf95290ef04d0e0de0c38d6cade592df62aa00a7d4baf776f616c4758f8487e997a2c5ea15df4a5e3a14f68d1eb0f803807e1d743c9e188bdaa81a358aea449804bd2c22ad208aaedf0bfe54500687d935cdb94d5d1f3edd35698979156a674349d3111b53029023a804cc983162e8146d800093160000180c08060422912446a250dba90f1480076ba22a604026a74a0261280a83180662188461000040300060100041188ac11854fc696ae111e10cd10d4adfbc73855d6c5157cacdaeffab8fd83d5225acdcdc68de077071832af5d2cdfabc90ca8e2f7f8c1279d154a6e33d9d7558cb590c132fc61a6a10901aa598ce0cc1f20c0ccb2640a9b6728ea42ba70bcbbf03fe651d285140cc105c091dacb07029941d5b2f1e33e25c8b669a1dfc76c6e6ac9840555cf00274f7292556e0b781ee185bca54e5e5559c2475ee2120161b6c648dd381deb760b75ee8d7b9eb6ad3e9a0d46f6f4b2f1cdee79429846ae5f5beaf9532df5bc74d7d3181fd89326ed2c0238d99ba2300cf3b1c8729d8563f61c925b8630918259a2b756a1069717ea2177a486599dad36d5b4743f8d45b02eccf814f269d4916990b8615c0150c1805a6a9cbe9367557b336dc615b3773ee07ec408d282987cdd98a249f89508a3bed15e2d030f1b7c65196289326d671f83f4c878ac08855dd3d15145450e449dcc2e46af93e83280e881ac1020d2cc3a45c96ef1714c1c9b4ce3e988238eb9141308294b8486afc40e39761217f810a6ce77e0eb6bd62d73bb0bb5e9de13343551cfaf0293203fc8ed9a3d3179c0fceae403a7d1ed6aba382889dd9c365da81c119667005bb2a6264678abb08f9c4f86c8377f47c0c10759f5adbf434f2d7a12ac4ba24b2c88528832468a6dd6d7603127a32bc643541a8053100f150193110540072110aa43d4d604e40eb86f280a677056eea8d98a8091b66bf68ac3d5bbfce958c2bc68ddf448399cb0e2736b27cbf2c090e7c3447f81609b8fa5236dd012b3b2e7092ec10984d1d100d3e4cbdfacc0a81f6b55b82fa622ad9fc8283971daacc47ac8a602cd2167804472ce35b200b78f860d56d07c8bf43cc5c36226dfe2e8603a3e007e1fd3ddf6f00b8a51595b6d6bcc8f6586c39f7f950014c6acf1d7ee5dfc5b21e7639f2faca85c490d85232a757185e1379443f4766b451b3978dcf28afe121259f5a428cfb9b8a05fd9dfa241b6f91df05bff4a848e24d692ed71484f1d701dbb6240c88174a71a434aad0b97aa508af2a69839902773466c15aa40a7e8ca38ec26fb4e9a88d3ef3543582d0950342440f1d07de537c36772a18692039a61e08724eca09b2e20a6bfe040f4c0cd8cc82f0e68cb5b8cb10f300857a0de483f3e358fafb7ca02f03c68657034be02aaf97dc25cbe11021142143908d6ff1517bbb0380726303058f17ea7a30baba110dafee98c9a78d0289b6ab0c425e5b131539ea8010898a0ccfda467de2acc708de1b9305bed8fee4859cc9b998872d7c3d40427dc96ccc69cd5f37ea15ab0c19ba41203c47439b7dca07b3e5849fd63221580870aa7f1b82d297dd3be103b97117fe3305da99aaa26500d855c84a1bd93cf9264cf7434dea47152942bc4684482632cc0d2a8da1d03804eb8638036b6985d590f6dc058877b64e9487a434499188dbbc8fcb1a8e226da167e70e075fbdf7c048916f998a9f18a96f32da0bf692e218396ae83dc27fa11a79077bb774e01c0526fdeb10ac2d3c9d01d230d23de224ab256f81d95cae2704eb805268241aef58b49834cfce937034b68750c284ef428d3655ebf60466ff44253edeae876ef04a7308533c65f3a2191604fe675815ef8ad254ae12403751020103a396798a16f409de6c177c113fb874b652893761f5aa8c06d4372431fc9c64b4394024a02263f7e842c4a28b36ec22ea43c877f561526c6d4223e0ccaaf6eab4b6d5a12e11af82674ebcdb3e5889b5ee4eb65b828ceb40c77c219825f6613dcff4ea2a0e0d16d1664db8be1aac149300db70f56bd3b7d8406a156b11107e30d626d10ad5fbb75bcca7bcd41f140fb3421d85e5103ea54945bd62d5749a7a8423c7360205160497a5d01cf00bbbb96cf0a03812aefa0931a6d6917321ea4591dd0b0511ecbff0c3d2f7b4c230ffb2ffff626608b94c6fa52a698bdb39feb55787a42fef2ae9f537adb7d501c308a5bdb9b989a9fe6b57f3b49ae23be0e1cf1cb01ddfce3921218a2dc91280dbd7b89357640a411a0b9f52a181364e7790298afa9213bde4ea2c53f71d4bec974d3e234dc72a791404ec71b1136e8c7613bdad0a6905f138d70b201d85633374c3f9731ef882e7a96f0939c5238d25c881cd8050ea74cf1a34c6df452c976e59fd4e40d99066c1ae90d6dd1352bc401759f0e42a8abf4adf5b6a0631ad27b030c6507a22a05c8971705026832d817aac4ea137775eebf5199d576358929b989bd6673f3287903d8509ff64299b3ef273ef634a9e55c20e7dc0bc7199779071513d208746ea4342d61b51297f052a79c589f141f6b48fb5885a62609d4de4aba808de60b2bea5497b2599df07bd949890766321d1fd058489cf8c6007b19694f57d25fcdb1a1742cb12875d87a381db63700d6f236b9dc7a2cee5c5e7f93ff13bb92852ce48c39ae53f3c1440ce67f81a7744082950d5d7e2a51a26fc946437189e0e40de978a6ab75ee422d02036efbb1b301b35b071867907547f39288f674ea7a324062ae8cd21a2f510d52a46d6ffa945b3991c109be2ec2d87bf52471c8f25b4c44964ac3864e66c12b6cc4b21198f8ddf31b33da55434e883a41ac67fa30a872049cd9cd3d8845b4de12aa4f25ff08c414f9e142d4bf7ccb32ec6b4830a392ee26f58b33cfd46426017271918797bed8d8f4bdd09569ce95cd8224c843345b67e4d1bc6643d39e1813bfeb1d249249c56e445d599ba67895a07c43fa7164723078dd1d15c1e9a6bdb2759666c06d0a48b9cd36ebcc7aaf844dbe2783cbcbaf3826f36b7bd63331829a9e5a261044adf67f1cc81a01331cc06ee82b106540203e234cc3e20d36c6c69d5ac98c930b87f633efcf8945b08d4a0c9a8a43c37d30eb00a4dbd7b61a925a5ac77bd835c4cd7e3ed79bf85bafc44ec9d609af201db1c6bccc60ce906ad489d9e9674a526cab6d1c23321b4c097054f62f65244e6c0c7b559d85a45a8030ea72505a4fdb5ad856177eaa9005d99a8b42a9885af4d202eb0bd029488a5019b0f31abac32b9376389d784f8d4375dec91a61cba3fda995a65e3d32adacb5ad33e1819f843c288e2cbc0dda463c69b0653cd530da60a1c59792b1ecf899bfa7a9d8fedbca79893e047a31e925855e70ef875be3d1eb4294eefcc6d1b38b092d854eb75036bc24bb5475a066ae517b0c869eb91caa8a24442f68899ac572c3a81c16eb4921d276afb4ce383f741f8086f2210e3f8797928702898b743a53c5b4d59d8141c420e221db033f651634ecf23cd438eddd1b3d9517f1ad3301b7d514b68d7876e6fb230092a1b5eab1f8c9dc942364eee66e1732b188514d13dfde661ae15713916075a818e245c694e46fbad8a73e80f074caee4ec23398a30f83b0e76731f2524a8e1893fbbb38cee05a0f159ff844817bb5e22801c6eb222bdd7b528476a6c1920008cdabace58c39f5035be4f5afcd1a294a801a6f74d42b20aea1440aad492cfe492a60c503f4eff54473b9a1bc1aededc5249cdc72c7d5bfa1440ab67ca8f334ea48c93f7616a9d4b9514357a2e55b32c4f63e698d22d754b10bfb81c97966f138222445ae6959923db6693ccd474dfa64767cdc863f20f2cd1682b1a82f03e652a9c5f080e660ff75e4549610143e1e35dfa75d5bdb1c1bd982873057734dbb6530894b52e6c9912c8b9d45182d89d03208974d0b8749a1099cd4a3c3317a5c8bed30eebaedb7dac9ef639f070642bd6013df7b5cd1e3b71685bb1297cc5853610912173779da50f3f5dc400d1b30f55208ef98e7e401fc5044421fefff76913d8779e11e93bfa38059a96cb755ef7878214bfa9d04526be9866ec0d1c136fdad2da0d20b60e4d5c5d73f35dd8684354afb2b4a44994f63846f3a0d3b5222caa64010772638ce33f271a7f03b649bd8ae339b5a2c18fa4f87398839c87ec7cb41dcda1fece1a80ef23c340593706486b07b1e18d80f827ceda83351477e8ed76d2375135df7626abda085130034f22880e054d6f84f18528d81c5adb32cd85275d11758c30ae049c7225ee1e0311d04db57dd4af396edd1bbd36be9ed649f12c82e1065393afe5f314c8db69cc2b4f8811ca4e63115843018ebdd64a6879c86fd4db45b5e452dd8c3b58490081cd7b08c9d02482c9c20c3d9bcdd15723bdef419732b5c417dbb7f9f63d471a287a80b8563e2c19692e596e5038abe631affae8b8866e02085530f33dc65ef6031ba69c318ee631cbc0515c723d82113835ef1d998c264b4909698caf265a53630897a2a495c5c716d5d8c542e4233ad5d542eb18eb04f0d00791e082b988b574bc3f128f9704ead41cd77a9426844103a98167d44d97ace544a52243e87c04e2d94d18cb89f0754e6b9a69b19b7c7bf4c58b3a37589bae0e8c8ac3e927a4b25190d030b0bed72c0ba734409e3b68097504ae3b1f8a48ae31e323b0fe4e2a4d7027d0ac60acda63a2df71d58b61c418ba08b0ae987e774c204c8ec405126d35e30b300a838fb01444b75f7fda0e2838a3c749c6b9c8cf04dd644420a278bd85a41d38b81376fd08300f263e09622509dde9e1d751835efe0d7d56559986d160d432021dd8df83f3e41e32a9058c54e60994f8cb24978289123316b9679a738c056994a4ca0b0aef3fac7013f7057a4bcedc14765d0ef7dc85013dec130b174e3c13e266cfe592d74496f1decd8598c8db4763abcf863c878c6cf804f4642f306171163a8ee22ac2d1ccbc1474467f20f508bdd7098f732c60c8f41b303fc6113cc74afdb0d8a8af79ca06ded9c1497f6b439bef2e08d0d36a6a03fc0888bd6574017c5c9bb845aa2723878d0857f7ad68bb4a9695c7ad8f94695d6d7e4a38e520ac37b7c7c1862419144a37df5c014bf1a6af88d38b2293b0b2792c52a82f7c90081a0dc47cb1760521b1e52259aa8452a46de052d92140987b1349d1c58dd979e0254d7f54ce0c772c3dc9f5364f3fdb2c89de47b390fe3c268438b06a516647b3aba8e9959401ed982bb16b901f496afa453e5d6b32bf9171c93f48cbb3a2acb7c85c2ff7989c71b011db0e9b6d61b62acf96b3f08619e84fb1a0a5cc13ce9aac1bbddef4f8ce4f285c1b975271cb9c422f93cd869019817d34a5eaa920faf2d4b24df84e4a0bfa8670911c6f3a2c42d6d46c198a021dbbde9d046e1451c5dd60c5b5bd27344f4a80799f54c7526c7f4b2746ce44bd45cbeeac757c26fb9660a3587353c0aad0633aee0e4964d08e25def8072bfe54b420b050d282a0c3aa32fb2b721e96d09bd1f9c48d66cf325de169cda579b32f3ce64c525c773d0f79d54b7e17e0b17eeb4ab505d72114f250d61c5645b96e7f612524deeeab54c6a0bd71bd661864fb08baa6e2a078df09d9c683f13249f2655f4d018a83bdf4562e57874e1e34323d47a49e67cfd546deda6ff61d1130b1da5694c65723f416081d3b6d89f2a7cfb4b0adcb46f6e4fff19877ae73cad32226ed0458479ac746d41a3c22b45f1dd622856eb457ea1e4a0d283276fa9d91131b4e4e02072a17cf9c2b6e5d4953de16e98e34052b61c3edc5efdd4a22989aef84692c3eec2171efac1a43d46c4804b506a4cc3f20efd2e1a8383cd313bdecb2840899b0c49d74f753eacd82fc3f1cb1ee71cc385a84837be940b9a5b16da816af903ffcece73d13f6e993c40d7e9b9190d917046adddec151a39315bc2f9550c75f0c34d67d42321da1b751fcdac4c4de99b907358a19584069f280ed63ae55f3dc88eb41bc1a75abc9490c5a8227dbd6a79ee2d481c4fdc1989d3c4f751a0ae5e14b9dbf4ed15de08ef136c73e517c36a28ff34f6b5e3a4c940e6f5c07990eaab578b23c8faf347eb4acc01177534c20596a8792fa5661ee71b96d0dc71419a2fa94b71f0641058859fc6273452ecaaae827f53be886484806f7d5764fe0c38ced1bec1763a8c6390b2563ab10bd05a245b66c9af386e6ca65d23ca45d8f8ace2fd12423e62e9152fee9079d7cae11ee0c46404c63f16ba36d3269c5da10f215e23e5cc3bb1bc26bedc6c97e77903caa6f4933c07629f00943aa6f11a79c443bef83a0bb521de7439a2e061cc09ba5d8739a15a5196e7d58c9a45f178c59dd9162efa08e0837dd0d3ffdb3f8dc0755158a8ebd9e7d482e1221b461966a1536855eb9333e1abc4a5ad08e5771fbab8389c10e5d13fe08f5502abc93735d579031df138cfd4655067855640cd072c7ac4b62673cfed9288feaa01a539c6e1c2d0783946eae08666b6efcf5ec235a83213855303ca62777f8a70dbd9588bd7b2c55f4702e113a1442f600ebf58410a41a685c4d8a99dcf81693aeb1d7fe9647aac1cc74fcd4f9d3a40047c4d99f562a720041283d6325943605fd7c171ddfa2bc8c82cb737beeb172c02b4ceb2bc618e63a86bd1c3d95cfc93ace95110ba10286d1830410fd85efc91f18a9cad9363b419e3a28f62fbedd09c648f5bd30095a7ac99ec7fe4e9ff83502a40a6581eedcf45a2d591bb0bfdba27f6b2f27486ce5a095102841bca9e1c09043f23b71d2aaec20dff05c63c222536acb6efa65c672062b23fb3f3f334108f6d36e2ec5fe0df601ebc87ec3a22a4944b6e0f34256bd081cb00673e0c6fbb9f47ef25965bb915629560cad0a42028f607b5905aff98c05a7e2ae8f57249a5040da96205f343ce035a89809c780b81e47aa17243d11083460aef2f99e399fca13aca616b53b2f895e37aa65efbf75a30277b8834fc5e873149fd7c666d2a6fa281806736265093ef5ee44c3ac18a198bd0e7b582d253388ec7a630ec1ee924b60721f0b48eec1851c2826900b68815eda050274b4f6dcc7c6dfd4f1e0cea735a12bddbab9a9b3d977907889782200a117f75b1b8778b816075e543571dbe5c96d11384bce3f971772bab81c616dbee9f29a78acfa3c5848b5837bad7bf192660a403dc88ac997c4e941c812dd4889402a349e02ee3f56ad1108e8c824c58f2b8ec01582ec8239e720f0ad16f10b5e14249364631e181e626ab5b04dfba6caec86a3f05f5027171c62acf2e1c2246e4487d39655f218faf1519a4b6aac9eeb5a2b1de13c2a8591c234a89fe2e04891ede138eb07296704451920370205497dab2db1055167210c6345eee3aef8045d2997921ef2a240d9fa8540b8c183c1ca36f44d5e8dee48a79a20a5a314609e767159808b47a4c2e2c0389ac983be28ada68a5be3db009d2e233058a0e9eb887203061f5b0b1a567ae91bebb30cd7a3dcc9b86782b838fc2509ad9ad6cec539bacb0970f19ee0d66d95ff0a3e7de0ac74037b8f9e73046e59f95bdb146499a176687bbc6f582dcfa49013d47a66a0a1a393c10cc2efe68854a1e66d3a25c4cf8854409dac16c0d82a44de006faf8133b9c1bf2ac77aacc6809707416b037a94ace98895a81e7f3b815a2d03bf08f6be3cb2a5799fe1d46fea7c61dbf9f74fd86549e24c686199fef9275c49ecae94726cc59238b2ada8beca99b882ed9c6b0b6ae9829ff8401b8d28edc4d3996bb17f209fb07f446e0821b00f1dd3b00c78c214c09d2aa3d769ee8539a846b1dce63d83e93ff6ef37b4e932b9fbdf5c5c807d02bb9f9e26ce61c44a3184f2754d61a5e791cf609813e68d498541ccac3e5a4fdc134e8fd5062fc075cecfead1b846be30f65e15a95619fdd85a11fff33eb5866150176209bb004aa0070190cb867409e6b01e969569b10aa9d9846f32209423de08368ec038fdd46d29217c11b408c08b003499c526c21eb776ae72370830e2c7ca05178141f819de12234413218bdf0fb2ede2e903ee0610af440143f41b30411f18e7609abf9dcdbec32dfaeed8cced654efe54bb2e828a2330709f3d3816703eab66bd880b27ac724df5108de342f9d13936c40a84de311a120854373bc648049c15579f3dbaf3e13ebcd62e703e6de78f8816e64f1a9712e2fd08ca699f1a6a17899f9a438b78ef5b01c1672e762ef253aa0e43f7332c07ade707610e2808fd8b66c29c115f81fa6a47418e0effaef627bf0e43fc33b5250594d6bb759fdde53eab960702882158e43f747456e07faedc73417faeedeadde2fcc039822196112f840888310501c1a7b97370d11fc372d02e458c7c2ca89bdd92d667b0bd25e8020d2ef19e789ff5ad0f4231021f041b44244255841e842322c5113f0df3cf9a2d0dcad266854014e4f35e0e63f629ddce20d4053a8ebb6875e9afbafbb2959bb2deb13039ee79d52811bb11e088c0207c76e05820f8d49aa54400fd7f5cdb2e9e3e076b2c849fa4db59c43bc4388269884d107f10e940203e0283fdd96c592128a09a7411a6407022d409c102e1134327b4f8437351237e1055bb5e7e6a49ebd6880b7d29979b870bdf02cbe7c78d22744238431c8b3081d08cf823d485c00f016c0486c8cf789d15621f89a344ac4e88a0cf4f7b4b4704a04f3e436f17a84f96ab4680475c422884f808210e61265c248dcfbf8b17741c250cc048264bd21dc6c2874edbf6574b3446a74892fd915f8731fa99de9e0262d16d5c68ffb88f352df89f8a70319da245d6c7f706a31118bacf263a16909f55e383501881f808b598fd17ec2673f70e2de957be63c06e932b281f8c5383c010e88a37ed6c8422e21191e35f3721fce4c2816a7fbeb462207420441719e14054a1502c84b70d32a2c5c347e39e42d4e9074dc999136707f904ba154658f14aff68b140cbb5ef2bfcd06e2915d1e2ff09b6abe2f919b7092116205e208e20cc204c1b01d4f7c96afb84a0c2f4415a3739ea936d6d1174b1baabd20bfbc7eb680c4a1f49bb5b5fe573afc98b70401446e023b622a0405040ec8350cf8816a34fcc5608840294d4900db0df68a55fbafc8c65cc02f5e43cc54d5b118213f14e0446d18706ce0a854fad43477c10aa362268fe73e0e221c48df093f823b841019416a6b9e1c4533e99561501200816970f953a6a9d3e15d92e923f2bd70da13804e0d715c924b9f94598223623a6b7be0844f976408d98a1dd828db8618c9cdfd743e1db1d73004ead119cc5fad16eac880ac4dd0807083128be19027a4b71258e1ff68759f0d66c994b3d18a323dcd18cd3fd406a67b04d11f9a6ba33f81fe1004676014597d65c88155df8aa582d9ccbe104f904bbc208e5106e448a44b460fad41c6a08f17db5444ceec813217e562e078218818f48d60f8a7db20ea351f273e5ba1157fc73551fef001839445f18f8bcd0e4a3d14d5431848ef013f80fd7883081ce1ad144b06810c67adc093bca4985829345bda9710f90e7d9e0b721cb61c9668aeb3366c6c2656071dfc0de396942d496aa8534524d91816a37785ea7b69e1632d42c2a8082f6c12f6023922e74ba70508c6c3d8dad4b0feabbd3855e720de3e7c033df67e219269e088187828f76292525dba9f2f02273c4fa3e72acaeb1f17d5d06ac51f942519e752883cc8c104a31ca6dbfb0d0ad27056cce2c74281dbabf04b164849eea78bb0d563a40e03af63efddf22790ec5ae19baeae5f51c9ac1cae8a9668937d4fa622593724a7cb20ba44965d7a2e73f0a75174dc61806c13c60756e02f90537bc085b4b5bc2f02329b9850037f6e4f22f9af210dde652fa91af1d5f35a972d2c2f29b3ff152ca6208cd670a662f7f9ce18c40877aab9f5a23f04a4b4e3013263b42d0be41490b6735c105bb62f59e221a3f7835e5a12cf6781d742643e8c94c4581d6ed1df6022d46aa8a570c44372f61d8473c759e464480cca582c34cab1a7341215aa45f418e9bd52486a3e347a6ead63944e7dac11010cc17783a52b2d843d0c56cc17d75cbf045e3773eb550c54a4d7b3ce7ca5f0c3b308e821abed32e312b104f1977cb2ae372e25ac8bb2bab65c1bfb128b14497321398e3de11c120044641a31b42fef43dff86cfe76fd9cebd7f39cb4d455496019d4b289f5da96e04d061397a11243a1cef3080f5e1a9c1e15da744691101c4795341f017cbe057837d3978085c2387a53d0c06528895e1e428fbea0f59518a7c0d26fb2674c41453445fe7e7beff06ea43a92e791cf61584c5adecaae6922ff22125a0eefe3fd6b102809473f59ee10b63178a7b177b358d86db064f65b867ca6e8bb5002a921841ead9742607aa4d03f7a8aa58b66c18066cb0e22572294f6ee02d45289e277b0ec81e36d3d36863c9566473e18689a202f15a576003f8ac00f240d1b0fdb6bcd0857a6e7b86eaa8d277b99e50c9399fd48a1cdaa10b63c57506a039eef84e4e922c267376159c66abaaffdbb186b6d4f691e88e01096f081af7add35a6ab5d5bd259549e9672d48f7b1c8be26f6de0cd6fd3eba1220dac1b416752d3a4b46ac7713fa55b3ee3f300c2df0f08eb5992ed682098e77128e86de69978032b80a0f26b909b04c3b546eef6e727836a12daea981ff85d0f6d3e385c7668be1d2e422a859ec0e921e7dd6bccf144ec4fa805fdd037905708732b7f5560810661d6678496bbdc706201ecd62c2b1a02302605dad60517727a20679aadc708d1023db52dac037851174654d5aee99c8c1c633e150c39a0cf06aa4dc5e2cb630f4d492616b288f3ba90cb38dbae6ccbdf9e038dee09d196ee63df81b309f644277cac67154cf54da4eb71fcd3c1f5789e73a8f9eb08d579e2b5bef842f0330ccaf29662ffa74787ce4fd8f1e29dc46133088960ec5cc62528f4dd0109f5720d5fb88d13a3415c1be9241085cc1bbb6c5e7e13cbcc1d5e340e91037b99f62001dc4288ca577e6586e5fc4861e3290fbc9baf51d78ef7f46720e16bd840c451e99ddcf172f27a7b35c6fef2e3c82535c9aa6f9d92384ecd130a49f6454c16060525529f68ee21c439ea9b1193f8a5cf7af1d0a09f4516a905b138283c05ec1c03aa4ed05d274155bbf5d5b7fad5b1ad1218788d6646543b7cf5d2b85cd366c53ce0e4fd7287047c7b2bf1d2a79e956f0af1d99ae73c92dbdee061f71e4f661646b1c8003c4cb356b5f25524ea1fe5ea07196b40c2221dd2abc4158059b9122be16b618c64d05a763106bc07882ddd768ce7bc0650ae0baacdb227a7514bffb8336b7783c048603927abc8201cc87214e24a7b0cffba678726b8d4dbbd590124811b1427c05fc205d37730a9914140ddf0cbf8573b05753da8b48dff27b6f7c7dd5679db2f1dddae9c7af267c218c62fb9a7729c846e2cf29a0f11140ecd658b5ac6e36b8ae8490ddb84a07214e22824bc1da031cb9fe4549ea348434b81c43c153a70ce1cbd08a8bab1c8cfe4b0c052d17ea21f209e1118525c287ae01a8dda41e658d16e8f4c015ba3fe94002a376311e901a18d15a199ab6e16c874089284775ce7594970a56836ca23ce68963c9fec58e96590a81b41b37f17486065e0e3f472549d33683d2ed59d6f968e08e311ed075b19590760400b20c23aca3d19ce1aeb9e844a68602b4515855e5f1b24ecb1c2d9aba5dae071fe50b4863e3e9723be9fcdcb60da082c68f3d7758b48333eff20fa89bc0aa46db3fe58df6a19f7d1d46ed781522c7cc0abfeed35736572dbb640fdb980e604b4ae5c6aedd02051adae4d77c9e3ee6985cd7542c50bed2aa89658e9f3522d7f47baa55e9f13ef23cd98b79447f27f1192ef038aa2bd2cbb70ff4c05e27dd8c645dea92bd070675cda7d7ad8bc0c2e3ce6193428815ebd698c6700de565f4ce8adb9812373582755924d124f5a8dcb9d81b2f44eb2acbb6d73c2b272bf310bb9e67acbdc84f8a6c260a85344ae80e3850c9a0cd28d2225f0bee6bb3023adf19fe1c224c16d3ad0c238b5472fe0cd50963c0140e592e9a25ae7489ac89c109492c885595f80b538264b2b04b96e74b57b9fade342c1c9b6b18c9149665edd7698c8e446493459060e618d80a9b8d6619a0b3c8b2d621f75c598de46004895960b53f09fa9d8cfc9d3d110f0deb8ec5521d51953ef6f5be2b1a2dcda9a5c4cd3452bf3f13190916eb4a5e1d104f1506bdc8e8cf770ae7a2bc2d6ac5b79eacbf4c6c05c45bae4f447058d4e1fc6e79769f0624ce03b8d267841ac2d2fa3bc7c7c62e1a634660ca15eacf306932c5462a6005e08aaacdb4fa24818b911629ce0294f3d5026c1f53a640ce2ecf177011235d0b119c3f957e07f1b41189a265c89d11c904ea69bf9c3ca62ad86a17d9ca1adac34d2da70756712e90dad7a4a66fd6bebd694088b53e77b6ac51a9396af2356a77c5e67a6c53abec0a6f8a0526332dd61942a4cad5bf7a9a8ab88aa5e5fdd9bec59f9a216d90162e1d64b5742ecff9ec71695a1ce4067978eecdfbe9e02a47137370bf21b931963b21002dcd74eb6d227419154b8ef69a70eaf013eeda25eae5e2137cb7e56337b32615bea5c5745d20da9013f264ee084af59f841b34cd65228fd7226611f2827be14ed1b0f5c84c56f3af448be1e6facb0a9dbfe1dd1c8160d56824dc12f5177861c7496cf06868e4106d7e14eedad125f216b6712818fe4ce48606fcb79d6f31a56606384d57d5429394dcde3da5c62259d35262b5c5e6f89c7694e03dff1adbd75687d829a46809679488135536786406d6dc269d4c0734f04f1478fbb1cf39facdc42bf87cb10450acd1c9fed1f5a10e56be65eeb4ccb19f4036de83023047dcc2de521a38ab690c6499c113c0751a8b09c73539c16030e9689d52bdbd0cd8c4e96cef17b8b7fbfed1f6bcbf61f9384aa415394febcc05b1522051710e4647fc027e8be625a2133c691e48a309e6f03fd81af657d13a0b243f5a37715b31d9af8d0e45d98122c0d1cc3f5371bcf25ab655a3ba0aafe770f0ea8d463f18220a522b0827f52685695ffb9d470335e896d8501313ed174b78bf98b3ca6bf0a0f1bd34a814c4153ecbc4db6f4835aa1302b2e41f15cf1d5604b730cc687f502f73dd15a84d490b298c8e9d77cf0d3e78199f63084760959e1ec7fe0158ce978e22e99714840f1f425ca2e3fadcd58b268eba294b89f5a6e799b71dc4a42b47376527678ec25558a98ce88fa1d0db7341bfcfcbb94e1c520e5c608e6417a4ac1dd03d6c6143097460de87e9c5a3d880b770a294140ccd9b4fd4ab73b3b4b31833a4350974b438378614420676ed08f09d0db01b8360f6d51a721ceb74d91bbadccd14c122612c3388942f7272515262179a08aa4d9ce0353c46ea7aa09c4609b7d4704106941a5687148bba795e1ffb2cf3e922ac1f3fcc448131cf3f9cb21b157d88fd6bdb633b8e216e4b3f3e6680d689f11124a8fdf78506f78682978c367354e234dc8dba6ac475e7ee6fb3c9878e62f9bf09bed28b7899423c99758fa49ef322b0851fbe24ebfd2314095c9ef755895b25439a5f7db90a99521d1e7947db30efe84c51b5983329e62f99dfac60327fea0790031d69e1f3fcbbd2b0598b9c6f2d318f26476e15627e26b0d6b27ad027a50a832a892f1abb7cf21d4bf9a6fcfa05a62fd4da6aad5ded61ddddf405196a55fc552a782d2ed840a065d0a925a1b626cc83f3d737e9d09f6af0f678609f301efd750755af7efdf383003f0d10df0322547aaa1cd2c0c19598951da7d35d1abeb3ad4b720f1dac250075b6fa5010573fb8efd79ce826e9c97cd73d78f088f4a13efca13751095d35d816235ac5ec0a324277032bc9034422e354cfb62c12218546c9896316b456f2473f0647aeb29aed0463389d1d98c92518d3081c49116005ed6d9011d45049c448fd8bcb6fdcdf94545df8683984a5c762654b391d629b68ab236b62d1c3a00e42eb673a2ffb48b0b36bba70dd2b1789431130b0bf3837e302d1dcc45c5bab0aac1204fa193ba45410380b4d3a958292b819245d14f8bac8c1917fc8f55059c341d6c367a511eee873a983b4bf2a3ed17923ad32d6439de64465637772e31287f89281863798f56d40659be0800f7dd3d79b10b9e5de726f296592329909670ac40937dba261d3d9a871dbdaf463fa7ed51fb5404a80e39c9a0a95e63eca79fa33cb856439389ee6e070a19c108643c7d400c7c775e792b0bf5973a1d152ed32d7856e48ef175e172d154887a8a492baaaeb615a6e1ced3beb51c93796f6c1b723576553ae0acd48f92dc65866abef77a16743997db9c71fe21be771f4bcd13734a8c72633aeca0bfff74fcb3876b1ab5cf66ef7d5a6a676f797d3b2ee3babf1d55ed85d3b2ae1b2749c032fb0fcec72bdcc433c39dd75e5b58069f4fa4e00ef9c57ca4efeb679dfcdb0e3244fd7fd94fa765dd8fda5434a1d97e4f518d49cbe2e7aafa79267be9cdfcddf5b379299aba352959f8bdecbdc79b7bbf7c7bb3fdfb230b54a2ec27200e923c92453503b842d6b59d66cec1fb54ab6b72114fb5b28af8b339425a994f2503a36896dfaf82505cc7621ce7b99658a6b997d0eb69549d9f7bd135bd68e58b3b3d0b6fc62cb9a14135b695f7de9f75866297dc251dc7648f7f73f5d44bfb189b8dddd9dda9724fa729571cfbd7beeee491c4f8b85116be6951bf1d453a5d0b55472e4fae34de2deea3185238a084ae26e085a0d8b02fd79eccfc3f982d18e76958523a02240ae6ee0b6dbc6e3658c87de997d12e028ad8aa82b1c7fb6cb8ff8228791fc9c1cdf2d6d8cf3321fc14ec04fea66521f078efbdee3f81aeaf891d2211e5248cf04627300ed6b0f6bdf56ac4039394d71266772da9a356ace9c3143864cd69831565bb64c59b102e5e43465324da64953523ac104139628519204638c43115ca445d2470810fc187e8b35d00fe06b6323aecd0487a00892c867cf2417381bdfb81096443e42a08b509268680ab94c336b9a914478d31d379bfe5ce342f8a9cfa6ef4c2e347bfa3e85be4349229c4d71a8f3de4b99d6f94ee31d7aa6af8b5d8ed7d1b2c739f04bb13e0f7d25cf9440ead7d7d1b3e994da5f28a54ff534ce773ae7b117a4c726df0f0e7e9806f1fbe3e8cf458cb5f7327bb77ab5de2d7313d7b6619023c0515a1d7121efe90dcceb91eb73817af4a94dffa547579edea6672e6e3a08c8082af777e40757bc53eb48c2c65ff147edc45f7bb0be17e4a005506e22225025bb3e09bb86586afcd3d34d7ef6fd9824badbb669201dc0ad7abf69ece9b1674cb26baddb05e201dc9eabffd551c9c63e5b126ddb5699cf86c02f7fc7f8af27a1c0b93d97f52bcd37942ee2f00ea15dbcafe0ed019c7b0e000fe0bb726f38d2edddafc3b3da1b5ec18fc318e5a143d208080bb83dbd1fb5c2dddb755dd775f75eaeebbadbddaeab33ee76dde5e4b66db7ebae54aab5c9acca29a59cdf757a84edae0b9dd039b76ddb6ed7dd6dab72e3b83be6a36e0451f472c582e50ea516fb47abb848ab1938366a6b9b61cbbbc3964584d9f793b90817e2efb3fab571feac9e3a838da9576aeb66b9ed726fa7784526c6e9c938c40fe7f85c39477ef0f1ec9bde6f3bdfd41fd6f9808800a5e8e6429ede42a4ba9dcc36738a0daff4f164432892f77263f19b1a96419e6fead92b16f3f14dbdf33c3f32d2f7d5c551a9aed9536fb0373dd6bdbd72527e309481cfc4beeb651bf77c5617b1a16ce328f97472d1a56fe4a31c7a6175d1f33104785dfcf14d1d04fbc13ff473f2910d83f4641b4e282b59407f11007065cb9c645db4e19c62c322cffbedc73905cc36248239f8a65602802418c8373509f9c88623d44c70912e01ba027a930c3030b5d28e137000bea9a1b8487f837c64c314c40db0074c216d0a7df4779aae80738f730aeef931978e8436f87c88b988ef219eef878f259f7ca423c88faf271ff5841ce8f80048073f40be00007d2b74908bf806e1e7f3f2277dbcc701f8bc2ccb49de6fa1ccfb9c2d807ce4e120200fbe39c585f2910df1feac96b522cad838d90bab6be3a16f4e11ca453ca33cc4f7379fe81be5232f9c4f2ed6a2a6eca9736c1ce483b2c7a27c64c3f9f461cff3ba7cefbd2e6eb337ea0d0e85e1f01c311d3ab2f9c18ed9ec0339a55c5b28a54f372a39256d3f43d9f6397be72967cb8ca4fbcfc52bb95bef379d7400e586dd0db65724833d36996d5b6d682483edf5f3e873ba0bc79fb16773494625db86b231ec579bc1a65af6499ffab216b5c496fbb5a996b5a82eb675b18636c97e8eac1151266733813fafdbbfbafe74004309e0323b8ab69d8816ec9752ddf5939c86b2ed9fb3e9d109e1092ed207c12631919ef084e65f7e1550ca70e3a8d4512ff67ece106807d0e5d2b15d86e30cd39ce459eff364cedbdfb8017e4cfadc07f5cbf5f77efaf3eccbbc05491f294baf69d57cdf7d1bbfc077bf7428632a298fddb8db79f8cbae1778e30607862327474c878e6cb6a3c7eb10003b1f978106df75218efbc73df9e8761f11b9bbf0473632dbb46953e41e4fdd7803f1e3348f8f1fdfa6710f8fb8810619ec0020d4dfd3c36d3db6d29e1e9fb2a7a703977db9c61e93a4fc15e692e75d15419c01222a0832fb67cb5a10426c22b0f68f2d6b4358ed4aa368a896a5965a6aa9a569aca5340dd572217a75110896ba58b508404080a3acb9bb13b9aa56abeac90ad3f660cb9a1058423c6d71cb5a106bb610521bef4f6be317de91b01b2fbfc78774bc0b71372fed4bbb727264fa755dd7510976ddf7e1f8d98121930fa46946f052ad3ddf8ef2ea7b6b434b6d38564aada59b7dca86e65f615b6ae948d5d8d1777e3a522dcefbd1c30f36a433431a8e124be7836c33e7b7e1927b414bd350ab471c618e5c84bea568a6106badd61e73502dfb99fee4be28165a5d2b1ca19ddde7e40871507a63c9a537cc09c710c78d90a6a94fd65a6badb539a4692690ede96fda7e1185e91b389a7bfbb582e04534cd8759a25813699a2c5114459aa6880e2da13cf563348d28ce5c9caf43bff32c916662a2199a86a6a15a340d0ed7314505c22116fb99b9bb3b9d651ce642561508876a95ea17f5b19444cf66cf9768b6dcb20675b5e57355f6ac01d1b4e78fdcf7c409b767524e09640338fedeef42236c4774a17dbf3a827ae4be43c23df732042592a64a6f3b241989fc2d23a13fff0b6b0d61341fc90956fbd3e624fa3017a23f5f3cef3bcffb8e8aca0a4b92d9f275bacb5926dd7b45bad0b3dd7f437c6fdbb99fb9c8e52e0ef7b9396673dbe6c2a03520f76e2b13ee6f112ebca18ccb479c6f6ba3ecdf2a5137d0e770b8dadcdf1a7dfb03d5b6bfe16cddd6c1703a9c9b0a62b910dd32270f99569b0bc7efd6fb7e7383c97dfb37b2915ce4de2bb77411fbfef417dbf901818892254d52609af214e5bb3ece61f317f5753776df9fc97dceb7ff26960bd56c64d67ea8dd5ce4825f918d7a9d925ad7b47a5599f2016acb5a0f637aa8b2ab13285c9b366dd66c7f2272bb162ceb102c1dcdf61f97760f4ddbff89cd7623f2e73be1b6943e54c909b7bbcd3db513c9f61e72db943f7fc2c07bb276ae7cabba03399807529a10a6cdc659898d4327af36fbca073dded21ff57b68199e5f0ffddfa34708f5df8308fde043207abc0cc77ad41e3ab1f04b1554b024152ba2bcb2658a45318abdcc0890463a51ae0bb1904a4ec223c849d8564145159f638d2063abf3bb15d8aff81c8b4cc2ec672f33093b7e470826ff949c646dc8491667e160430d5938d54083163748f7e123d320a50454a4f80c4ea6ea471650ba0ffbf6addea26ac76730339a9492006168b31f4880b39f99843034991f10022d480d58440218275537f0f8d9f3d060709e879ee160d72f6daecd0eed248dc81dbfe3f10e4d250c4d4a0250e1f13ba8ecf8998f2410e677682a3c2210829492003cc21b763c952aa49404a062444a49801da14ccaec67dfc6c63cf4ec77e85908267361b513fb1d58da198cfd6a3f87b222b2cd76fd93d1dbf997b62b63133871bd077a6cfb733a11c196d7f0678c35dda2d1b0c676db77854fb0abc853a0d16834fb3a44dc477d223323b28d8d671a8cb5386cec0a9fe4d8f604d73de882a33c3e831732cc204399186488e18ba6189ac080a10986305e80e105332ebce082184c2e30e5246cf318519cc9a3d88c8216d044692127657c06c7340bd3d34c775dd179aceb8e6969a413429946ce42b664d0622f298fb3e0b49c9471194bb1d7797b060296684b46b12f721dac6b2ccc4ecbd8a66085366da05161b6c1861950d8d84a23823342a005822d47432a85ad355c05521f58430d5f81d4133562e06aa43c100336ce022925363068810b3048c3054c69bc604d9417acb1f90527a8b1066317e4a46c711aaecf2ecd819b972e6e4d778d73dfe04c1758db829c84b385c1065c9fed962bbc925b7057c06ca43490df6e99915258dad98575dd32e7f08aa43c210b7252b63929631b831524e5c9af66056aa8b00215ac01c31bbdf04650548086ddca001b18f0362e205d44c3db54000d2d9a05fe7b54e0c31ea1a43fb472923d232759cc0205ce48d3820969cc8852821968982620e98ffc92fe40930217249082325ef8644b76fcec6558864309ec0867a1741f2e970be1327292252327590c0304c838d314813328888104148c21c303a4fbc82fddc71862cce00031cc48698099306818210c306a6000185fd8b0802fca4c5140192f46a0c48b2ea824a08b13dc808013d49c0e20290fe5a99900872492f2982087241370a18301b8d8a20a922dc890e0089912ec60a4045af050002db2782a9245560f44b2b0f001098b2b7e18225d0cafb0c24261399a5600414410578488b212d3d583ec635ac410f6b1cb155abdb4b17f33898b5c4ee262953792b8583f3f969e8b888bf57318640338b3c84778661181e509b847daa6cdb67f84cc45f0cc9a581c1185c511565c215584155255606131a20a24b0b2646b8ff86a7051918f6eb25c4d8184540d381f7531b3f1e3e029e4141629a6a28022892c502431e6092590b84f28e18418a025c0515e0de990479ccc540d60ddae2b464b2e62df158eaeb7a1554eca3fdfa56b189cbc25090fc2e423bb05cc124a80c94776892f390967259af8c2449516aaaa26bce4249ca996e8c00b972e3929632e4cfc74e9c2252765cc0490172ef9e8a60b5513df9d01a832c02472640ae926101da101260d47c9c5ae9ffde64e2154f9e8c64b133909e72a2e1d34f185899c9431123924529efa92a80322457d44fad4072285a414698844f2d42e57fecc3dde2b8998c049782e919332ee126609305bbc7860b5258c962aca23e6d69f5aac94f8e2123de8eb111b1209a201ac5b9c4a88c945ece770cc8f13ce2472120693434cc37c3a74acc8330637ae7141b3658dcbcf96352e519b880bd95dff48123a54d5d068b42b46b54af2c831170b242379e4bec20bc923f7d863d39f4ae423bac19619ebf81cfa4a8c0b9badc196352a339b4b95edd4a963225ba2327d22d3c79aa34bf9a955c66fb5efa598c662635aca5e17f3c774900f4486985319091124f21056b224b1fc9d452227d9c7e1a8c2c63fb34c2c5336768242f1053d05fd008ee2ae37378f6f6ebec8a16c8c839e408bb5d537373fd68d83c3e69ddf7600a9ee1c7ed207f64875db10c7fe9c19c97503f637b42ce7717cce37a166ef6c596b628a7d5d283fd012201d123de7cfd13623c1be1f2fd6370bcee320995bae8b593e9f8b29d68735ed410178e3c3cdd7b7f928e79b89251f49e558943dc2da27827c005f145db48fc371ee6f1e9191e0178dc8491c11172a221fe530c4633eabed7e72f71756176995d207052570d3ef2410fad3888cc4ed22425991fcf4732829a51308293eab7d06f181fa4aec5fb1cf3199b18ee9ab6ca372127e1b32718d4bdb85e9148fafe424fc396492dffe242227616bb3ae547c56fb967428f6ddc7f2db70d6a8a030cdfab0e6f6cc45ece330874cb6d436912d59bbfe91fa236c41b3b7ffbe9f511909fd2f1c71759ac5949b9d572af519e552aa45946a7b39bd7d150a5581ae50976075ae20065886a1dab2f6456b6f59fb5266d620a0a67489d6aacaec9b2d6b5557bb56455535a5a5caf6a3a5cd05cbd98ec82389ec960db1db7fe3426b61dbfbab412fa007023501facf5bb70fc93c8c19e1b7fafb5c8cb9582b8db65ddf4ecd8b43eefbdd9eb761e15e8a73595db57b8abda7f4066dfaa6b0c7ed95e81eb9f7be53a2bbf33617c445a0312067eff6e4f442ee6efaeb026a27773f99edab9da4b0bde7f413babdedfd7571ebac68b4cde94ba3ed4ddffd4387ae91bd4c8e65e3fe93443226f37dcf876d39cebacd761b4b1fefaaae9bfede76a48f2d22bb33dc913e1ee4a27fa7f78f2b70bcdd7cfaf631ddec36b739e76c739e73dd7bd76dd37a0145b5767d92e1d844d4a25a55d48698aa597112291a9aa6a6a5a996c416d5a25a5ab82ca022c0ed81b4803a404180e38c7dcee7bc0e3438723cfdb94676adec1817ea1e470eec6b3ef26ecd431cf6f6efbdcf7d3afefe876327e771fc38db76ecd9361cbde7381d1d9d7abf3ed2fd1a5a3dd6d9cb8c64ff5bda9e92ef998ea635176948cbb8e8e1c08123680d98f3f5bb2e27279cdbd373e7a836775716def8aa618fb7afd356eb6c998ddc8761acc70aa4051c41a70ac79f8df3e07f2106bf778c3d1fe5d70b17d1ac713cd6399f8b1f0dc7d9867d125a5354ec211374100c5f5f445f300b1ba2f3301b8ebfeda8e4f53daf8eb36d4358ce17b98e7d986b79ff3dd6361f61dc853223dc778fc47df74bfb8af9f3b7bfaf7adcc11ac77ff5084966d55c04847deda7759ac0b685ed1b8e4db6cd099bc070be623ba630e2502a0e6c00c759b3db47abaaa8a4a4a29e9eaa5029540a6ddada62a3a585a64c991a16d6555515959454d4d3531597e252bc696b8b8d96169a32d686b34c6de642937b202be0c855e1aa38f7e442e0d3e7a05ce8f5f4392b2e14c549f9d08ea7cf6d71aeca85acb8312e247bfadc1587e5423a4fffdeb7d6eb3acf52ab54b74c532c2b058d8bb4aaa96bea194f5b9a65b2525ca48fa3ed14eba46d156d9fb485d2d68ab65156ca4e511e5ab35476ca46d9a91b3b65a5ac0e930b8df639269db1fb6dcb458ec907d07ef73a9e4917724cb4b9e51c937352c66c66e88666d35243e96f6c249113a5592e44b35cb45496ca5a55b9500efbf629968b54774fb35cc866bde895d534cb458a44b13ab9eda5575859b6b3d862706ab9d06d92443852aaf8539453f995a37136be459ba8942af48946512a498463775c0ca0ff5d402d0c3261a009238c23aa6a5848dd1a16524e566a5838cd1c2cb91ba08b332b5844050d415f5a3b6b5830edda021a3a0c7c7a16f5c349a3dffd5e6e8519acac8dadadf2256baed7ebf57aab9dd03b34b73b739ab5da61ae2d9c5b538d9a352019e7a91fc6983d4edafdda69b1c3a8daf527533e723dc2c0dd645ba7ae5f7fb5cb0b5fa112359cf6a68a4fd87604f3169e3102c7993545d6aeb0b287a851a5bb3e91aa242ed465b1eb2b39c1ae3fcd58b9c806f4e6a472201880f42f1d22522489fcc798f8b3a590696602a93f694276f8ba726d210ec71f3353881751858cb1ebbbf628e953a79a1f57388aff0a69369c520ebd201a45996d8cb070415580726fdc1a1c7e21d32b1c63e36fe12882e1f853db4cb5a16a63d5e6ca6ad74b27d977e9fb8552896e1cd2bba51ac923ad607369924736491ee9ffb968b9ac8dc071aa715aad6d31edfa738d0fb9118dc9df3fc7d992726a8f5bd6b0ac6c9d1080b286e5b41d8b861a16d38e6d55b165cbda14517b0cb2eb1455f608ee3973af428bc27adc6546eccfa5cf6b54646d599b226b0aaa8d5fdf77b511388a9bca5a154f43d4883e08b4d61a7e77cbedefb63d2ce643f7b77fdfd95617b9589fe621419840b9e70701c185ec66b939e75f0bfe136c848282762af5f7e2a12cc0192c1d6389a574f7395f4ae91ece2c5f414fe058eb9cd405c614b425352cab3b9d56bb71f7ebe494f73a4d9973765d54b54229a59e67a9acb516e3acefe3388efbbe8d8bfc755dd7e56ccbb83e8c3176b9ae18af2fe79c5fafda9519afd70b046b5756b5ab346ee7f2170bae685bd6a4b032b4652d0aad8d5ff2af5700ac4541668f5f83c114ef9cd86e345f7e10bb85a00dafd89f3a0853d31bd970c779fc03c179fc7d0c326100e957bda38348008e4182b87b6d02120314e6fbc25550068dac7966bbd33403f40538cea63d2ad15d5fbefb9979668c33f30c972a141403388a206c2db0cf19fe7d5bb775e307acd8b4eb2cedbe7694d6bdc56d928edaabc5dabc7da79bdea8fb4d2fe0d5c58a28cd493a7bb56cfa553bf1dd81a57da5690046f73b2da3f7a3bcda307a611add67ba51b8f04b9ba512b828befaf241d8cc797e5c4cb3ebff0405b9d2f446f7cafd2d518c19b3062881ac00679248a604bc4f7feefbfe600dc7d8f62dc4740efdfc70027c62b713dac6cc9e396c10e6b37eb1988d7d92886e0973ebb9c8cc61d7a7da89ef273c7607969ee4d8f49d706ddab4b195e6a68f42a561559b072cbdcc24cc9fef5b14dc753da514767fdbb6bad5eae0cc1d080b1cc59f2049c4fdac2ff6d46a7d73f73bf7fcd86ff6b9cce51acaea2f6da4397fec21449440e68f3baa3f28615b76bfbe33f1eeca9a5f5e6e47fa6c9bbd57ec09b29bb538a410bfe36b6ff7b7c7d4e3aaacbb6a99fdedeb6452dff7116cd372dbdfd76df58ef4d9dccbbc50d43d4175c666dfcf07619228ffec7e736baf75ab5d2e5babb4dea7e7acc6661e7e0745d49834dbfb3eff700204bd2f3ba0d2c097f9e8fa3bf7d51f77daead82ff2a3ee6dd697bb1bf7f6feacefdfe9edafcecfe9fbdee7eebdef5d7bf937ddbdebfa786edbf6485ce8bf415142ba9ff3ff1803e6f979168b6dddcfffe12eb75dfc713636abb7ded8ec9f009df4d8575e7fa5c91abdbee7c7a9f72f50cb94bcfe755f19ca12a3d7cf4c41ad02f85830205dcc21942546e0cfec7f5d1a757afcc2d07bcf3dbf9e87c1ba3b5d77d01aef7bbff7f3ffb6cafdccfe557a2d0a2b1bdcb20605d5c6fe79cc4519c39b8fdf96b1ed5560100b402547ba1eba9c471658ee602969bd2ed211047dfb21e667d69d0f7fceeb2e0491793fb7bd3f2ee49bf7d4eb3acfebbcce9b92a86e32fc7333b1ef9f93b9f187d508feedc96b7f8939d7f7957c63efbbcf7fe622fe4b7b7ad37f8c01f1df4fd7a357de5e7e9288e32a955523db738fb47d0d47285fb5b908c65daef5b1add4529713baeb7363de4e5d36fca4cffc4ffa383ed38d9956b3d2799f60da9452f167df1e637b661b561ab0dca9d1f98db5c89da87242ca2e722149eba54377da5cebf4af19c9d29ebae62217fdc56c51316b36a55fca88d932375be9f41eae710321dd02cb2ac64a90f9b9e3ce29a55dd7751ded3a988b414f200a75dbcf5ad84be8c7c5faf25f624cccd39cbbfecc56ead405ba58ab0c226b566a86dca8fe111772d0b6b1c3ecf1ab5f3f4912dce5234abd7c4429ce479466bc6d5c7e24b97328733d931cc402902eed51696357e7d1a58d75936f7be11c832949fd0ffb7da2b6bdd6c07cb1a75fdac5bb74a1944efaa4c7eec0d2ddf32f165de817b098569a26e8f2055017d8bc617bfa056bd33bdee77ebc746f5eaf745456570da7d432f9db1f3901de97db6545c227a3127d3752e48677055fdadcc9c0f8e5e3a01ec0b1eeca84993d6bd8b2b685cdde9ed2bff47195d6c68fe3ea9edea7f46fa6d8d3f9257e9c70ecc01275590abed2ddafc79c4fc9ad808293d9667faf74f78dc7292022bd2ac80c084686f3f6f38391e184b58c04c80d7f0952011846963ff63a1f46e7eb62ffc2b139e76c6dce16678cb1cd365b6bede36cbd4c11e369cb9a99293355b6179a1a8b93858333b6cab8cbd4d660cb5a97a68d7f83dd54fb22b5ede32f4fdb7e96b2edbbd850d176065bd69ab0b263574e4ca8d9221a34b52f5b1bcb28282d5adbd2aaad9da9b2367efba5ca6ae3cf531bbfc501bb954f63fc1e95876cfb3106f595181157a28a30c28b741f477491e222290f16aa2c4d4817916042d21f534b24b145521e25b4685142bac8c316aa2a2e5c7838420a4b961f9000337dca9503ebd20587892e436cce63ea60e4a4d585f3765b2d613739385ebcb876c8593e243e15745e1a61397d4ae743269da598c69ec467b502965cef3e6424dff9259e5f7e25745c3ada6aba16595db0776b5d39195655758343e501972e60c48409e3250c145658803dac26d0e36f70aa5cd92a1f59d88d0d9580b09d93f3312ddd0358cc9f90d3ab0be775b434b25ac26e7270c488717db9e9dec5e423d71739d6d13257a8048412d32ac87eba4c014b46b247802c64922ded9856c092512cacee817d9a91dcf390b9f3c7ea7477e96c9996ee8195696984a54b31abac2ed74b23aced0e02421de18804172b4c1f49a295489ffa405bee85015d01ceb0deb6cc0f98dde0389820128459920990463a0a582241152e545c3eef522527611d7212b631adf3ee057bd5e75f72093dfeffc3303ab8870ee33ae4e0841039e0f0441038388d0142d21f3fb2d30d5040f90d4e258a0f7e4c8192fe90e2ff6325488944f645fe2309b0c7cf5c421835324f20043541628045b2843051be00a1b5d63d5ee6231d5991bf0e424a22f4837fa8306af43f54086a663eaa40987f0da5ff5f66134230a343203e94edd0e37b7c91b761a4a3230b7bfcebab305162efc29291ce13a11f40219d90244aec75429912ae8d91ec9ba064239d5f3292329d708afc61d404fe1e8e75bb7b38e7f436f0fc9f17631fbb41412e607777f79d9e0f8c92b57b827e071673ecee2478232c71f1e8494a915b6badb5d6a7136d322961c0c5265068d4cc336e45e4c80ee0085e38ceb6f759793e9a735a2f66f3519d765aaf42d1683423b46a5a66b4d00819414b8d04729565b1f5e1c393d9291380df0c7b5b5a4ea0d6534c8b494b4a9bfb094d2c52f4979edda6d068b422d068010d19d0a04234021a3b481fb94751ab055a6a481eb9399e3d33cff65c25a536d3b96d8865e7b6e196e88564382bcee30e05e55330169ee246bc9f4ebe23d2cf0a8fd70eb97ebc9e8fd9a69d52f9c80b4f00facc50f4846ba7f62b1fe3ad61e546bc87e2a27b3e6545a1fa6ccc7d77737ee5629a315b5e6d5933c3873464368f2d6b66d036fe3e97669a3fb5008d3389ed3fb148b712f233ac7462dc6fe68f227948e83fa19b89fd8c94df4ece1bbf90fb201d402617b0ff5bda198e998100fec044cff3685704841446d8534f2bd1ed791b80ed26b0ddbdd5f94bb3c6a6d9b26730d93362c860db4f73c6a6d1923cd2676357d46bcf252490d7e77aeb0a7d0ae509a758a1e16e755535c6cc1ebfed55533e45737733cdd2c52d33aa064a1771b76409a766842556b5ed242c51c3850e5506e8fdfc51a281ca5ce8563e46cb6ace3d4e2d2b9f34b5340a75e70f77958e4a3292b5f6b367ea0e2561560922c58726564f0f0dcdd0dc02bb7704c31915f533f18e985f31900235e5d12268115e28a5cfe7539268066d012671d15fa96a2ef4e69c33045d7a4a398ffb7bd46ef2cd4d697b3fe73767c6a15bb9e86fe4542e84b587dcc87dbb4f8d0ee5504d5c68eec7e1940b4df7749369bd30888b6eed2cd2b6ce992409c544927c01bd1fe5d591efb116c5fc9ee7fd8b5380dedf1dd145ff3078c10f9ccd84c6ebc16658a0f75d6ce66fa3820459017aef85452e42d9f6c799883dabe9ecf909fa71d1843ddf5a1be4a2f778fa0407aa021ce7cf2817921a251a6b59b027d6533799f9d4389c9eae7b84fdcf1e61890b49179241c41fe95947326c596accd4d04c810ad22148871a1a5a2d055653413904e11054058a4a5a535881f3524bc194329acaa8613f399dc97232d85032d46c4a51b0b5291952bbfed46345e228a594ba4615b6529d61a5563018e953ca29ba65ad0c2c9bb3e252ce329c8ef02d6b655839ad428e81e5639469e26063bad96f62ac2579e49a2bc616c814932279e4185434d8755a33341a2d8b917c9b8f26077bf470b4ef36dcdcbe2f06bccfc43ecd48740d9540a1fa371bf1df68341acde650060b4b8951b5b777a92d6b669a6c186cb4dca37fe6be0ea003c1796c102d37b2db7eec8d4017b228a8b2ed1ff1a16a44b7fd98b51f34b3f63d23b9e1f6b022efffb9906ff14f6efb757ff5eb827800ef57ed142b5502c980f49164fd5bad24b25512552a89a84b22aff5c8927c77a79bc317c6188763936f672028c0b933d52f7757f8737bf5c35f6c02f1ffbeef8b4d21b319def63b0912e19a91e8c63fbb8ee7f7481f7fa035c0ed69917b7abc3edb9fd317e62edfb66cdba6e461918f1ede6aa5a5096c4d1318f6a7be54270a58eecc2fe809b41873e198c2fe0b8e31eb2485cd8d2bece984dbdcb67163fd5a819e00e757f965297db52bce34f92e8dd66ddbb6edb73265f6b66dffdb6f1fb4d5f6c664fc1932b15f5fd8c9acf4058fbbde538ee3386e72f3bfc97d94523ddf6afad16f7ebb8eb0c96d8ee3b86ddb40342ca5da7e2b18d097f6f69b06b29a77537d7bc0f3fd3fd8fc7009969503e36ad39f4d341a7403385429f79de6ae7451d226b00f89d79cbeb052466b8b6d6cbfea4274e24e02d938fab8bae4b63d92dc8ad89f219322f3392ebc2bec51896e2e8d335bd6d638eded83b6aca531b5a50bc9dfe8f66c48916c3849369e646cab4d60347db1820d5650c8c64e2551ec2deea4101d1248fd106c01942e521ffa55fa50243982520a906e7a933e4b9b7e5b248fac926324962423cbc82fce482934c217670c7153341acd8b9114b265b6954a81c1a80206141851604c491ec964fecc74d2bf014ba01580b2e6c5d31ec52dfa9549840f41ad54216c101b101cd4fda1f3c1eb013f7d3ce41d5c2478550175b891c30d0e384eb01b7050c919418e29311b74d4a043834cca6c861d32f088a147d3c3a05f085d0000d34e940c5ad060b2b0c1a4896d78daf0c1c68fad9e357cd4e0200640d8fcc0a0833402f002a035412ef0a005422c18521364051fa880080d215a209c21803403302304344529d865189141803322a0e0688c2162209921124611300af085913247bc40d285014e90544b6282037081802d124046490914a0c502b26040d6085834e00a0758f1002c12aa80001511984202574ba49840142540418131263c5101272c802f6085811032308051004c4068224403441bf88003414e1872228482071d085202f240009e74f0819f14808080835d1f87c067d7c722e881f243051fbbfe0f5e816749dcb40d360b1ab49041941d2600b810bea061f8a61e31f09061c70c3329321a746ad061436c4a8e11e450c17103cc0907879b1c6ee800567991c06577701ebeb99f640fd9872f88cab77fc0501e107606918fb60f62012cb28209f105d1bedeb007a80d38deda15185bd6b8300387a53976fd118888ef471275df105d6c15c5ba58b3ebf704a92f42ad9dc069d75aaf50fbf381da502fa4587ae5036aa3678b3d3f364a04da026908c406387f02bd001cc51e203640a02d10280d50ea88202a178b67a9486c53d7fb6c46a2405be07c117ce886d579b65742895a6dcb176c59db62cdc69e91eaa661934fa422901ae0267d828c00fdcca7e59b5abebb059979b5658d8c947de9508fbbf291e8a2ddf26d81f948144ba02575d8b2a605995d14debac45767be5214a108680d90fb1da02cc051dc3bd37ead61b5e18e0b855a8cd9b2a6c5963db5d8b2966566772085dc90407a7e2491c51bef0f943e1f13f9888614480d900958133954312e3a5d66db66a9171e55559f8f6c28f6c400b4f38bf82504b353c36860258af1e144cf131c8cf9812200510449217415640a222a40a86200584556185d21021643b2886451002d8e94c00064926c8180cb8512132ca036c2091cd00509d38b08dc324bbe28010c13c2b080190c88318ed104051b387302192894a1948227685230230469a09cb182160d8d1654c0b48217d434b14086164871410d6ba6bc804a1a4e30c8814d9518eca0c6d31a3e6c41b111441b56661b2268512c18d18254942c4c532e28f1c216189868a28aa18b0c5533809162458313358cb1218a295723a0820ad60d573865e1a0450e6474e0a24a8d045dec508607309eccf430860f677e28030a0d106982d012420556d40cd10222d65c49238a4d116a18b175c46c438a86a5852c4c48bc30d594840c4a48d152c396294b5061c2a9891ca8aa70d9a1cb93171faaa0be0401c64a1822aca2c418e184d41359c64c41a144145ba460e28a6a8a2e5454550106cbca0a27ae1883451459575950a1055609ae2093b585165c90e9e1221fd1508c02e93bad76e36ee7e12fbb5ee08d1b1c188e9cd90e1e3d5e8700d8c940830d441e1f3f7a7c3800f2d3410080823c101a0af201911010043080108ab6110144381a8244a448018c1c416280a42407404002942860010c18a1010e78000910888004964ca0040a9850010b5c000319189934d1c00638708213143aa0e481271f480104211001141556589ab4c9c26c6146994cd385f9c2846136cd18a60c73862965d2306b9836cc29730493cabc613a4d1c660e538759659260ee3079984fb387e9c3fc61424d206610538869650e3189985766d42c621a21e71153cae5c40719fb0b03f2024a3046a9ad3e04969f9535b140b3e79c73ca39017caf5b05a4eca854ecd8e992562a9a110000001a5315000020140c088542a1509806622ee61e14800d86963e6644950844511aa3389261209a310600838c013023403052c200e2bc81b72899f9e7cd6fd94238a6202ef7316b79e56720c2df59f15d4193a3422d7f5c7fd6e72626066d5eae68c65d70846f165ad570db2644f984d2952ba1f4e0e586ca8c371cdb51c4f6f77d77b6616085a60f624cdc9e0dc6c06f929dc35e5b3213203a3748268a061ebb5924a00487d941f763060fc7d3ba613e0a555fbdfda1f2bd8633d15304cf0398122568d56bd3afce8293915f1dd891b9ec8eb639b13be4a9e945db66fa17a9b3bf4a9e6e04f2513a4d8b368716b6dc3fe600652f29387dbfab43bdd8c698d5a0cb29b303139af69d500d30ed69d5a0f3cc21a78306b43c8ee9eb42301d35d3f111b422b84340c03e52ce0800c03c5d1aadd1103ca840326ad89d14edae928aeddf06655fd8054580bf7fab038a354b85f296a9918481b5f28945d9dffa1c53f85bee953d5863a4601803ee49643661d45e041ff98531af7f5613b878f8d5d52e46a3e2865da20efa1102ca37fa4a4e02cec7d743f9475a8dcaf0e14e6fc3f4db09727d561b4b742c5a77bbacda1dc07866b5a3a32988de12b71c0b9b48dea7830b802e65f00dfc029ad1f82f5f7feda24371e01287d1db568c0255e39d00983baff1e7269ddd2618c5bb79c63a748679a82debd2c32c79b9ac3f2f755277b8fc2ef4a11311a5c8c0e2a0560a138217524d607c503ee1a4d9efd70ebc87c96655eb7040837e8ab04919e28a6c623b96b0b54313a53227d26357a8ceeaf5450a9699dc13a7d5bc691205c0716e5b6d42ba580d86ab6232670c5c8d1a06b48a0d346af5be24a4a5440b3e93278e1f1e152bdd4b198efd6f8cf45f53ad1aa69852b31c8d4881c38146c0b028b21603bf50a8f0cab564026c09f369ab3b91f7c962e517675f7c23f9f3c44b7c1574b2e6799a58d462330eab8c25dc34d6a9b2698575f9c379eccea95d9b3ee5c14cc0d3cdc078604a30c0aa075d0437c5c5d3c50504ef991ea190c5ee833435e538c176c606662cd193aacf527ee6cf4fe6a2852ff3d025d218a75801f149a5484cc8d703f25ccad9a19947fc4b56dc33cc3a60a2d88f42a0375cb49e7f4a18c5f6ddcde3c3b479fb9832bbde149554f6f0d7f1ebe1f3a32f3d722c90bfbfc9f25d95ee9fae61af2f68cf847731a078558e5f9858d84fc4628356dab389a8d38cb6d26c8fcf6311a43a8de227c46dba0d34b1daec67cb564618ecf124c93eff025b4955a2d3f738915c805796a65e3a6e0a7674ea6a89bcadd5a89b3be58fcd3d69de8f59370814fb7ea02cf7f1b00694e9b31fd00fa807b07d10fa4e9f0447df7e93033f2e0c3c36365993e1da49fba9173ab4eec947c54e6f0f2c184d855e9725ebf48be60afa19053011a43a0353fabd99c4b43676c5610a55ea1bfa35ba5325a750f315f2f13d06b113cc67a18a0d547a5e85b02a76b01b1da7c35eb0871af840d7b91f2c60b1ad80e5351a6a526f49115c3726f0ede861559fc900d65b5c675bd0e548220760d68d41e127c26a11e1aebe2c9628fb2735676dcd6e423d1303d46a2bcee761964ca712c3ef1b58f8b12b74fe0bbec74da5bfeef9416f90c34d028ddbef4c31a6a5a39e36fc311c9eebc68dc756398c1eac625070c1acfa8dfbd737ca4d600064b03ade029a51040f6d7efb1ad94241d662dca5776f80f9518c19b154c8b064b1b87fba8981f0bb3ba40fae8dd4c0c74c2c4c2baccd0d9c97446dc773b4d53cc9a60d72383a78acc78d4e598702bccc80dbe41e740e60ade3c209b57cfcee2a097d661a57f9788353118e2e6944f81a68a750c34635025b81047c3433d011b8ff9a7c262bc1f383761486ba3ad7cee4ffbac6ce04e405e454e9695045dcf28706c5fbfca4e6948cf41cb353b9d15b983130a1099f708e693f8e1add347df153023559984cee54ec537866262c8088731f39283370ef6c9cc245d2e4962e3f09646518c53bc3b61188f5c044e45e91260fcf9b7bcf6a06df0c35902601bbbcba0a0da24986afbd612de7f7720a8f1cd5145cc91358ddb0bef95df19f6dbeb02135836a1f405dcf40b85aa04e6ed39e907429dec5e1cdea3472e0b7ce4938b47012a8d0755857b8e80ad7632944e3f322ab2a7fb2d56160a9f3e1358f8584c399bdd8b103afc04ef958704f4eb73bc1ff53b89a2782dcf13efebe0ca17bf3957df2466148761880605472c82f24f3dc662575c9cd8dca3e65f68a713321d67177de77e55a965644712ee600d17a760e724c5da0da5d41a5a799449b9370c1281ff496e14dde1f5069be1799405be2554b72d4bbb836a9e5b06d31288b464ebc5a6c76569269ff06e52a409b6dca59a37fc7a102e8d9c359d447614a880409e8c5fbc5bc7c3c46536103e650ea0e0856a5aac4a5e1147c4fa46e9d7eec862e76ba00664cd79aa1944130162196065f914d019bcd1b55a00d5cc12efc0b9f1fbcbf48790ecd4c5ac4ebe89ad469c4bfa4479794b8114f1cd04db45ac61aece26bd43c51e1156b4c3c499faecec5349a97c9304edb9dfb44140dce5b07cbdfa8abe5fa7869ff93e38e09b24467a9970d43c77e62aa7d2020c6e613183d3ef7a630ef56446851267735e272af9fee95ec17c39b3e589b41ae20461847f342c8d22c4b3c28d6d1d723fc7d3c8d9ea04365266ca2f6dfaeeba9662e91c82d820bc157e222f70abe265dd3ffbb236990c8fe485e66860d81286a402ab310950e700f60b1f4ac262a068b292b3e3cd558bdf128a0643590b7aee983c538408edc7dccb1a8ca5791f9ba5212e63b6a3bf20e51151184497ada94a07f3c1c2b4d0aaa2422b4217cc9e1cf4eaa66a8bce14cd267bd6e60d13fc343f221852206855b9f71d7428936b37c0411d29eb12fe61da96cc93b5c4bfee701c875e6e79f4629f42ed817c38abfdeb87d6be6d82af3a342e3ed8b8b341776b23c5edc42fc1e9db4de12ce3d93c9159b9f6b6465e2de74a3d496662d0290d8d11efbc0a94a7da4b84d63838671389ae4aee356740782152d9565383ea3a22288649ef2b7045c9c5db760e1b05e9e112b6e4b50e0e9fdfa2cb9e79d7204a42778af7b0fbc061e16aa4ff9ec28dd39a248feb2d4dea6e84cb2b5c6d4ce19a453c1dc51d0f40c3065f7913f7323b0e79122243e8f6e481cc5058fbcaad7960870327cf722ff0947a260c8e5f71c7f72670f7883d64ec1a2fc45c88a2199f84e7c5f49be80d1c1392259b7580268f38fd5c41b06d0202454ba5e79d5f6a98bb412b6639ff57020da4b30d7a13a7b6d0d86685a55a629e10c940a8d2f58ba06403abafdb4e916c697dec40ad9b6c2c336a1676e88c0f00c65e445ba53614c3064658fe9c261781e36afccd7884808c97604bf4d485be8198d19e4ba3134092864bcaeb80f29cea2973c17e3d7873cdbfe54dd48ad7d311002e1c9365e0cd7bf4ed9d94620f6528c4c2cf3ffdc6dc82efc1a9a1cedcdfc230ca473f5fd7e83ff62834d36791115cba1e96a503af9d223eeca1c8ecc9f830348963098ab221fadf84c87c2a765a45570d4eb50d7f830a65ba0157e842a23a8f9abd0c9ff73348735d3ec253cac949a5eb40bb5ff23568191d0b6c02e2c8925eb976758de3e6fa4d73c2b409207bdcd096c1f76a6e06b30217eb22772b8ad39bcc819f97f91124240384ed84733141a4b54d5b96759bc4e90b3312c9b7771e8f535c8986d931a0a9b365b101920956af64c19f449ebcd678bcc7efc5c73a92197f5fa5629227d883c4dced4b2fbeb1ae49de71f6d6ff052c815d537f8b087b7c6b0cdd914b88cc104a10355ac69f383d3e3d0456c1cb22d3772a750b3c1626011eb3e36191baf8de2c459599cefecf5b8602917f15c8eb3596847b2f1f21b58b882995023a5e7cac2767dce1a8f9f16a5e8d95b8445b4e949ba84932f0b90cd15320fe3d237dfde415e6ab17d9f3a3afce9cca17b5ae3a2e706c0faecee3750f125cc104d5e95a16e6e481da1c7991cb9127801e43c3e0ee0280ed0e1f276d93da76783df2d360f54962d0ca421812e4269b2fe3b43f86debc6bbda8179326201011e62e6fc5fcf48ff268e83ec4cb3b1f44a6e0e4080bb7c3f1bea8720e7620f45f87cf35dd3e05e80f357f87f70db8b095b917b38df685a4bcf59a4686a3847828a571d019550116edcb2812f7e9a04f0d6267380542503804048a6ffd2b3b8f38c05db4ae9f462cc0da1a963ba847b0df8a0ec775f7817b3a7a24b86c7e57a0a4a10f19d9509415089557846623d1ddf938e1d72242743114aeb0e4c23afe0d7e56638747dea688a4431ae834284bda82f6cd161c620f6601ddff296d6c65c4a754e09f5ca02692d57705c944924b46aef351ee4bc2ca11bfae85e06c1dc4c59de534875e42a14c499a5a0a55f213adf2d4da62c1ecd55e0dafe9756133e86b3a038b7ecd0db0f32de7e3af94eeaa0d57653adf62e131b085c757585a7e0ba0be4a25a8a23df3de50d8dad786cef7142b2bdfb24d4b1fc205f8a450ed9e858d3f8240ae1972f3e906080f852a926bf25315031950d0f1f66720499f819ff31da02143f676a5318a04470038a9f3288bbadff3e00e028cbc9d1077b201bbe547be96ff810736205ce889171f2749ad182003d0ec8c8ee2a82b395a41b3e8c90b48a937605a0d90a3b8da2b575fc5496e2a24e04a4c2c8465d34c99ee11cd1c7d09b3b97dc65837547222c25eb5f8a9b87095da22870a2b1475c9306f7134a6a23e3893a42f69249cbdd3bbf97a0cd4bda7b64a8366971b6c215494d6fb841a4116a5b0f9c7c68cea28f1c973ff276566544fef557c389b13846d89d572094f8786e6d19858c4538697eed2dd0be18d2a14d581137ac04a21fef59a7697c6b37abdfd733a1c7f5c9aaa69d9e1e69b6b494ed95eb4f1a577683e94b9bb1631cb101284c2c4c9bc394824e1c3354a8215b9f512147e5bfdb71c723a7bdc60361c0aa41c676d00a2b7db66e93eaedc344839167dcbae76bb076d3d1beb383d8b5406233dcf0439af87f7e86bd73d42bd63bd86d47c2c4d8c7eb223f86c400b902ec0d55e3bb744eb2fbf8da782a49dced8137292f34623b034ed6c9da3588b1ec938cdd0b894407e1f811d5ad5d61b84ce5f9bdb8007cfb3520d857f9765f62d8ddda5f0973a9ac2cd0839190a434e5a62c7f67ac52560c47e2a2edbe56f6fa0660d8652691841966f69d88856ac048b93d1538e7ec7012eaf9d3af9cb716a1c13c9e20caf1c152ca5465a1be6df36b7b05d738c009f4dd60d720526d772375b058457cee3825413b021663aeb17fcc26b7759bd0f5e1f1022d7623bc47ef50c86ea07c5453cc44fb9452deb8c11ee8c535b52901513b031101c28efa7dd0fe62b3d53bb53f5a6a8a5ed680413c2e722bea9e935c6919fdbda6d5ed4bb8f9b891f8f045b60e77ad31dce4fd096011ec46a95f1ac1b7ffcf39184ddd4eb7bd82e49af45c054f568bc5f22626c512e397ddc93e023e7379d357ceb73a6447e5bea952f206553409643bc7bf357ff60860de76e6716306127e580df9e25ce2524115b0681bb4344bbeb76f49f7591f842b1ed698e26b480a78f6f0c5eee593ff2c5a8114018755abcfc5bbd4f11b3bbc5590882f8cb82c1e00f614db9e6b7773ffe1cb84d0141a6fcb85cd7101dddf7c211de38c05756ebd0b378884e28de1ee3101cf398010e5242a1f2d58fc81bdb937e76c28689e0c743ed976bf23de71ecdd6f501adf74ba9f0e4f00849b98371b3e330e49506bc13a37c5520a5bd2c3b78c3c9ff1ece4dbb7c87c99747627de3941dd0703a1c4c19c42503ecf6edabe189d8f2ba864fa9f052585fbd82fb9d863217421de006d2200b4377880232a199ba26e7c39627cb2fd2d1e16c81f96a5b044878a8319ec215b2182df011612e3bd1e3283997fd82725181bad0e48d7867d3ebf3312aaffc0fc9b8d48f61afbb590b197aec69df6c0968b90ccd11e45a560ec4aec522bd5440cd43d3542b37aac71db94355e8cea6583edeb465684b99178d30e2de6de19a45224584c49cd531e2d81d3bb95fa97569d2a56c39e38c9e89e46ce130e6de1867ffb2e5803f9e8805734a705a699666bcc0ade326b6236cbbb056289950494bdd34f0d2f6ec26f88ad67fcfe028954557a8a939c42484bfde452b644e63fd2dd404e6e172088810431525f89f7026e8f6bea92a27af34032955d78060083fd40e44bdd507c011c4e68eecf9a13ff40031b102d60a7abee8bba18a57076e3a3c9b070b03a6ca59684ef810ade6638bde1bb863bedc0a1da6a8278429f41e138a6766ae122f2ff014864d3d6ca0e17293d5baf400209a633227e9e824737576cace05089c6daba68a0600d23d4d72150bcedd66a158e2265a85445830b5532a67e9f000436edf6ec90a8d2a3554a1426c38ffc750418dcdcb5db40c1241bab2a1181c2b5cc243f4740e146aba55d3856f2a44a050928486596fa3f0a0ed9b058b242e1a54eab6b910102148cc87f67d0d0ed55ab050444d2bcae8e142a58c948f07100176f5bdb5a8522250e2aead060c1744689df83c0c0369b9d351c5ae2b0b212092344638cbedfe67840e0551151c5ee111b76faca9262327e697e398f59fe54fe1c3e196f39bf24df2cef2cff0c9f14bf9ca7bcf0c709e9d4a03d30f960487ac69389d2e20bb9cb21c9f288f2346fc273096c34f946438d861c0d3f183e26cc20b47168e390034347c38f84180a371c6e18e260f8d8f0815046a18d430d861c0d7f53031fbb670c01afa0760822d2aae28824b125904b23b04874faa18979ddf2aa5a31b7b392c7c9f1d406517e9ed0ca538ed43bd82b53a4e23010c6a8ed15ac7b5abdd874b0f7fa61cf9a1d267dc9025c633c5bc5855a93cb13c5bfbed84e9eb51c0ebb85fcb41ba980c40457629d764d09904b9800ddd7060ec6456c11384b3c06f4d4da0a22b942d551fabc8ebb6e6d0e7accd4168b6449114bfcab358eecdb0c0ab0a07af0e4c840a755152d8af1cc9fa9fafbe5040d191a1d04ee13664c132867aa8f079898dcfdc1db1e0f60d73aa542b75e860d72dc2df1020cd8652a6ec310cb6f143ee63b848df5ab40b254ffad4f3f2a33bfe7d88a3251630d93be51cb333c6ddf6edcc6a862c3e450da8ac972a306e23b74e8275117fad9d89b00420337c66ad01104f15bbb5824a4c4c608089730b4b0c9228075365139fcd0b9bedb38704fe1c7c28c4e80c7965d3751e6746038fc9409cbf32ee2f0d3fc911bc6da33ed04c7155800a800929c3763ea3f2deed7fc97fdd3f61686b14126a995f074949e622156fca4c88333ae414d1fc8e6b0ef9b1cafa978458d0174dadb79805743ad61ae0c068c5d1e7ed40f1767232ed7067f004ea14575b20ca0f04898a73b789e1301394b4b8b91989f38d36f6bf33725890bae0ee3994de5f87c60b6b68fc5df2999055468af7e00003c1a99682e2bf05b6ebb6815f67f27ad51976159dadeb8de690cf2635a9f8ea67d5ce848369a1941e00e2f815c8bc6199effc3c70b000b79917fb1a6107ee7e2489cf11d8446fa1ce223eb29698b484d47468e3f4a828ef49aa9277dbde84b6f0fe436f6d95bffaf40d592a58887b1c746a660df8a694d0ac050a9d01e1b38cc73e80b7ddf7aceac7c0139de9883ffbd9ed2ed57bcd4b91e6f24b0aaf15a6dc124b271a91371c862575ab3fe10f4b963211fbe877e7f6f09ee3d72fc000ee43370d138c5291ed3cbe4c73a4c3acb9c75145916cc3fa9ae775443c177584949c0c7940090525dda2343e937ddda34467563d703d0e30d8ee3a0b80e8af320380c80d360dc0dc671a0b8078a7b001c0cc06d70dc06e274509c07c539001c06e16e304e03e33a50dc03e2a807556c9ee940dead9fbb2b051206fc950220a85b2067c91b5b9d3e5fd100384de83a9aab7509608f2a5b54479e5d0aa28d47495720adc03482d00844151c5da0b402290aa6100c45306ac1a805a4149032055cf4c2921dab2f2af9fb202f833649ceaa9749ff7cc89351331927b5c5e4bf3df264a899d079cd12a97f8fb655fd42c9ff4ff9996849e4a8ba90f4e7b3fcac9a9338525924f9eb915f56ed449dab2c90fcf4c88b5d6b52c7b58b257e3ce0c5d42ed969d565a9bf0f7860684aeeacbea8ecef87bc0c342438575f26fdf7515e468d091cd42d93fff6cc2ba306d20a26e0a4b298fcb7679e0c35933aaf5920f5ef313fa686099deb2e4bfcf2c41b5b534287b5cb657f1fe1c3a42dd1a1ea52b9ff0f79b36892e4a87291fcdfa7bc4cb51371525948faef395f46cd491dd72c90f8f5cc1f5b63a2ce5597b5b487f05f3957a2e2bc3a19115fb4094434904e5513d1fcc07e9720f58bce8c56f56f9a127a3291a103a03fa19da6a481d1b667639227cb94274dcc3f9200ff85a5f316e5e2547fc8d7a281cadb2300401732b8b42a4f28d77d30731258b5b647e1639700835fd4683b59d99a50a28e5a8506e2133753073b8654689ed0dddd208f95f159f51caf8abda5cbad49180528bb7ff87bbe4df882a563ce084310b3b3e8b85d559ae817785064dfb9bee6ccc6f8b3965aac4f4fc5ffacc6b04bf82d5949d24ddc836a211d4a8bc5d6aefb50f9e3c058a910a429a5a5ebee014e60014fd62ca0e52b2657c051464d83270715eff06f62ed35be36f0d5b534eea3217368f4729b88ab505f70d821e26754b37f5e46241b30bceed67a401bacd48bbb2eab814d5601040e0123b3b23009e84f17a6931a59688cc6b20992d08210e88561646c34968f540c471894321444c60e53ff66f4caee184ed262684ae6ecb0bb45065b056c77c16eba925a13b11b582562521fbf94873d619eeab70531e24714acbe94356bb91915c0fac77c542a93b488724726e8d6fd9e1a49216a7631e54a89d4da83b200cc283bd4d5731180441606e3d7cc53ed285a6580eb22dc7c814bb801b0fd45c3a95f1d881aeb23c85cbb818bdc1267304b300fa1931a04c97265ced83100e11f14e58db4695f7c1c8f9154906181106d9835d501370c59e9948660ad847152050f176cf790761b770943a707586b658461134187b090bc5d141d0b490e01861f715c3c5723190abfe31685d8a2f60e098b82359419927ef08cfa9f1257c6a0c43e8a1e375b7a460c2084c5c8c69dc9315dc12a80a46e5eafe083b67a826015c0ad9d26a66fbdbc2035ae9cfb0c37387c1bf329c9827c7fe557029b6add91d76d70298e123ac84fcb22ea6ba6504c237bb872d875024238bcaffdc7da628e79d15d5b93620315093de0b9da45ab349c77125e0025d3e14192ffdddbd78c992ea16c449f177c70db36dc6ba90540e44ad7eaec08310b4eed30a3df91ac1d72625c5f80f1e1c68b700c63bc1472d7fac01e7b1e1e1d5cab15ea14c89bb6cd3f14a7e2074df41270dad480aecb3004e0041681fd598297d441085ee3104b634c22c2b123744afdcc0a9ad4e782f5e7edf042f138c6300dbcac674bcb1c1aea0445cdeaf7af129f5d9a11807c3999068be878336933957d889cd239c8a5d84869ac317676cb639f552d1ce6872a78a7c307e1282e4992e07d906961041e389fb11d767dc59235451f269a33781a175ca40d697112c4019f772636dbc157e396be82e5d36fd8ce34036fc3d0e34f1824c5206f6c0f7ad4623cff513d1ae8b997598678ee300ce48f30125de34e5ec7ffe71ad0a71b18c0b9a297fe0db5c3cd66c66b940823ca88e66194138a1c93d979f5c5497187c71a826d3789886a1e6723ba6f82a1c11cef5893ca3373868fe4625ca454c482d179f2d961b4724a0d372e84e8e47f24c2c08c32569dc1d69dc6e9f000d4939d93d25b814ed3b709af4f6e2132bbf7ee55157c27c9fe51f9bc8efea51a8bb93aec27ab903a9c277db20b8dde60a96d149e539fade01961f450c9521889251468649401d99e3538308dcd721a4092ff426d1dc4e52de7a05a902bb29c8e6cb4c6b400021a4be749c5117954894d4bc38d041c2e025eb89df769703c125d2fb449d3b9f825e20e0aa4cf309d77cbdef8d99d7d19fc8be3c44b942beab9e2dacc55fe5dcaa3cbe4402a6a29edeb54433a59c542172be559018c2b2cb972441f3078cf0f478c4b3ea98573cc54b9d39c7a69c7eb702ecd5ba52deaa59da39c4e6db914b92693c743facf9cab8481815aac13f7d9bd8cd655aaccf55654a1fa188a5167a1dd5c7da4214970dd09aeb67dbd7e6de7b9cc72a491c6d0e443f655c03fb3c9d27585162f326ca2bea75cba0676aaa1f7ec5661c3f898862cba4d0d6390992b98d475930ad4219128f516753fdec5e9f9a3261f88c3d61bf3ad11795fa5fd8d13bbed798f3ae5c2ab572aade07c861c7199fe5de3e246d6bcd7685c0c16cbee624bf8358efe934a0443d68be4c9be174e730b76c0261e92439a23dd8562f1e627c3a72067ace8fcc744fa54ffcf29a98ac765225a52e5c64f165bd3171e2256103ea07815e661cafc66621aafb037eb88be171d14db7b3fbf322f2ee8ccfc69e40cbb637fa6716fa5601922da82231017411fd955f291ff5b60f9e6aca056464fbc8ec546f582eca333af1de4f8be44004f5f49e37d8629824a304d5736822ee25d045c4a955c42cc154e267ae73830f73fb4400ca8662a96ccac90561a3651a6c19723c0b279ec9a6b10c600ffcf6f08802846d2358ec19624f369fe6c05a4f5c5fc70f1737911e444edfa7d1b5c79630f852ee8593381c1572ce2a01bfcca1b7d0d2760d39e25463f917a216217f95b7480f8c35d1ea78fcf2399c6e8cc74486bd29a4ec7aab838a5ddd2da8336f85dcdca54ef3e71ad737f8dfb02eb7cd92a0bd07e861bf766d8a1f898cfba50b5f9da3215224b78b2073b94e35e81557b6a420bec86ac754f18d25a279c6753a20e9cd8619cec2ed7c5e40f3fd0d91deb6d5b695c30839cef5afe5694ee893bd8c98ee16837e97ff8619d76678fae59038a343171f6e48881a14ded0777b8f31d467ddf88c7396e3c28b5a0c0b1130bdd357d4f57543cd76bce087367ddb90aac059bb9f7ad5b7b7864e05dcc0912e5e001fbb32cdae097e6f7dcb8f8ee8abc40d111d336e43d78600ceb1654c6ee7a2cf58a117111ee599ca301f8e51d390694c6852b1258436e3b04ae4dc61b19948414e7769919d74d76466c0c5f085272b4aa891b5615a2158fe04e2c0bcf9947c9d22d1e7c08a194ed6516285f5b22743f1b3219755b0ef53d2de56c62207b9865319c97aa71205eb1a538f1dcc31bf7d1acd549d4316199187d44f553d8f4ca4b7b37148ab57383e182e2fb1df4660fc5acf0218b81df436baee404be7a864e0456bec5e71e16de4a6da1e46a3db9692ae2cf9b3eef3be15c99c5d270273c2822b3fa32a769a55b1bcfe708df6e2bbf6b0da0711953c2b8f0c994d21a2b6a0141ecbdb3461eed3f8ec0d40fc4cd5f41728ed510676822062f80bd7b6b3303fc0b6abd4c540ebe99cbf3cae1c2406285d4d2d8e196fe5e32471e96ad92874b4617179ffb70175fc3faab89ba677b890e7b54eecd7b940de69829815257d0d18828705ecee8d1e1ada10e3c4ca96662d1987e879c69c5fa8ea3006da4a3acc9ad13b02cf6c9d79cb094ebcfd47826f4afcdbc443e6cba6e952acd52dcb85b5600fed6f06139ad32680b4552d272c4125d6f87e8d6a10aa2aeaaf64b83f21fbd4bf11b7135ae9e7160db71e7c092d171641ddd2210aeb44428167e1b1f474942481d646991391424788d606f8a12e0544bb11edf5e53413b556bda8acf173b43bf0a80d59a65678c5435a356746fe56990bdab94a57142e3d472623cfc1d71cbf21da931ac49dc89c53c667928155a945770d529bf1335a7869bc80588461ff00e7a4b7be008851b841f29c2050a0aed7bf5d9c8fafcc42baa72df290ad1460150a57c55699ea612977c7200c375657df1acdef3a79437858f541ac3d2dc8702ede5f19fab9913ab0983f77e0f4c5c0a7842fe316c6094f303cc84707f018ddb5fab080a201762fe80d824b7be5e314a8c0d071e20ea23f470162765fb29875637c24725d919eb3c9748025432e9d100f2ff89c44757c2511ec3fa40c4f2fd3f78bb6640696eddff515e7e79e9d2d2877505dc676e6d8f20375f7c126739b5d634082f53d68a29b1929f526bf701604dca66981f86a23af3e80e264f2750cb38731807453791178c54456fca431418662feddc54784cc29a18e5942485f609b4250c7a439553b91b4ac79397712b8c711e8dce38a6243495c741de7f4ee93b3022dd89e03c0ea25463854c1c6c11c6be59a07833e5b21d671838420c4a23d66dec9149024d0425e86718e431fa799261669ae56685a03c2b8c21199162637f1effbd81bc538df7cb13e204bd2470b042f2371b542231ff1db1437efbb1a7fa53dcb25a1213c96ce28db42568cca10984c75a1a8a2c27176572da05cf340fff011c729c307ce33bfdbde6d7b315393024c2282bc5ddfc46814a12d41760ec5f96df39c397d54adac182132eb666aa8766ae9f5f680128105f99410ec7f50859c95f61c5269a63cde2bdd02361958a9f1ba3bbdbe5197113579560252aff0a57119a638af448cdc2ea2fb021e9dd586c6b519b73d974044fc3d1619f30f3e28219e3efa78f54990ab470b95bb035c8e01a31421766157e5544f5c5d615890fa801adda41379d19b1c5a3cfcbe92128644abd19ba41779d93590e6492c8e906e7454805da0cd2679ebeaff1f941a278e3e34cd2cb76918dd238faf48b943d4eec898381394a41bc3328518124d8b3a954089d52c9088ae045065fd54058d9bb60516b11a76bc3c24389a0901f80747210278c631e381384edd8308b1c7f866b30852a49bcb74411cf8fb2c1901729206b55b077c3ea573a6ff8d26e6c808942ccb88c0222670bf09b3df601cd76e344a20d53feb9f016cd34d9394412dabbd1bd7fecc750dc6aef2a40230633318f76019b0e4c89b19a17d3c63b6c73b3ef5532f67942bb4a7f4a16c16a0d60c412b311501d0d51f3a5a0aa8d692539b87eceaded56ac132e2acf2d9738ec4944a45a44c40a1eabf38f1149be81277190664186be65eb12f61b027c3d3a0c842819830d212ab7635d58c976c14784e414aae792c9d283302a792809b2c02e5db13afd385c2e6625d9f79c07d58f6093bb4653d3d0c172a51c94a49c0756a9b7f8533499bbf3384a925b153d06169c6e02367f8d4ae0facbc659ffd89c3121efbee219445f61dd6eb9a057e199a973627288034013206dafa58b194eaf76090d801219e77cd01df6a1504d5f57ea58d351fdc20be8a9ad5ed4f2ec78320c47a754ee8b0ec4617a3935ac5139d188a575c3fc01f52334350e14d65891dfdd2615658da107bb3b5ef1bd177828c96c3d9df2cafd10487eca304507ef4ef022b157d4e2830eaab088f6e5151082f7f43b0dd0a983acd8d50862058db0c3fcc3fd5863bd0c7578144eb4e42431c2c4d6f435ce6f795adf34e919c367bae02b5241a32e451c8db62e7a1bf275780b85e7e0b8996a0127d920398a6ba60531d7ce9cab62e3d12c634f9cfb015cf027364827718d40891abec56e83e4ab53bcdd1da577583d8b2ead4380bba0ac41e8b817b310254a56957e14255320257693c97fa87266b1aaca70d561cb397bfda00dee61f0488c7e01295375aec4fa7483532aef4c6d1750f8b9e2f56d1816fb3aa83ec428485c02f2170249b52d0c24bd5854c8e3a46a92ba5410b709952e55c3673f58c7d166d5bc0bd547b7a17347ae9cae38e121b5c4f182a955e286be5ffec09f1032a063c7d48784518df844fbf46cda52aa51b1591514f4ebc0e1260219c372d5cde8c922ef99c5e5f5517c62d73d0591626287b34cd1ccaa6d35159c0ffa71b16e8eb13bc702168e33ba90185c946dde7a7a08c46f79d731d7a6a0ed0c1201094fba0f53faa6155be8053aa1b1a7dd85cf350332a20084b86c4482af597e17e3b3920fa2cce942185c9721636dec466f6f93660b7464c04f861d9aeea848617792ccaa7c5d520381bb1d360d38b2794b1ec9d9aaa44094ca90593e9ef3d7f15a0601477cb852a8294bcd4545727e98fa5d138ecf05f86c6f8723e886985c09fa9d6491975fa00577529f7b638f6d086ad051e3d952f215771cb4abab01758fa1711ee5313270165c8bcfdeb63c8317068d9268b0f7f6f22108a16bf38f02697b873309489b038c6c40280bd28bbaca0486a5adfc90dd0017ae736ab3bb0bdd306b8003c5d413fa2809168bc451b6b61c310af53f64b4d784bb43c532ee440d5409dd25c0303b4402ed7b67b94231a0a1d86afbf5398a48a2c024ce33238e93e27673f3526e87da6bcf9a16e991ad0d13c81611f4d5650d77366a17de0462de76d02b82db9357e23171c269c65dc92b215864b5b4e91ae6dc23e0bc3643a90103b06dc0c450083bf1767b2875fa0c7c3e264b91e3a5c0bd58727282493c2f87f4af126172e39992b8c331ed13e5048e8fb5eaa3efb74032fdd44e007273274d3662ab306bf8bebdff987815ab0156a6b8ffa0da1c7b79b86ab5192fd30972b1c2032eb2db38e0809c44c76538fe910e1cd1a4e785ac2f5150a0752d584b2bc658c8a01b524d339a4bb970bb5b6ed43e82e986a1fc048f9e2a491cd9bf2c78b08ef486aa02e3917217c8b671c7b45f8c8ba700299ed127c3d0fd3f12e8bffb7c03647876fa96552241c9eed3782df6315a43252a4508f12aa200cc080c7e3bb8ee8b8810b8def132f395fecee287250f6e7bef415ff10119afcbfa5ecd1be4add1ab234fa30b8fe5350c13d8c17908e6728bae1ba4fc1e1c072b8c94f42faf58efbbee59de4348271fac73e1dfe7d33ee46b7558f5c4d20bdfbcee3e82cb9f1618256d501a2e0e9a81b8d8f0d23c04dd23fb6990c884b9e026590f985b7b1d31d6cad366394c811a88fc52544147dafcf8a57bc068a26949fc0c26256b9fbc7c3f503fb9630a2dad9f2802495eb3b898cdc6aaf95c1c70bf3630edd9352a5faafdd8b59c78aa4de6a33af874ed960854ddad1495940564679810673f13cae7f9cf014664823e5d40ffd8fd6cf0f053f6d4fdb8a938c9b6784c72659ae660422036e822910a608c50087de16aa0243f3dc44511681a42bb82d9d608ec7b59f56da555af749f0f1a524d28e4610ecc15817b9f4da8370a5f98e3ae3911b0fb12bb3fb1ceb70323214537a74f0516aa8726468636e5302645dbec7b1fbec1f0006efc71af65f4c54d84bcf7b33f71ce6e233ee13c743f169279595000270ca94757f4f6a3f2f23f636b40bd317fecec2243fbad6db8ffb3dbf1a50eaba69872cfe5c2e5eeb6c29d2637faaf37aed8639fd19f8bdc51f9b1341b98ea34baeab3ea9689dbb02572dcfefea6efe9eba3ef0eee9d9c569472deca19cd46b1040753a4967e43b4e22243ace19a2c03e5d295f28df8435253d1f85abfd4dee5736a31ab10e155d39544596d82845fdf3aff5fd01e9a4ab2d3409ee730406192df3f389f53caed958614f1bc1e69361012689030b0ee149b52d508ffd7c21905d1a9050709f6efb9c53884318428c4858d1e84bfd64d10f1d6a601f320ef75dc27ef429e329a90884e58fba1f772fc0f5dd7c210f3bbdc3ef860851841fb59f2a26d249150129dffdcc5ba1fefeb7fa19dbc64b08bd8e4410b456ddc15b59862bfec4086dcafdf66f97f538585d9ade8b4735f8d084c9407525491555cc01a798c5cfd8d6915b653ba1421e095f9f36d36d9d54314e842dfd3e4845af1670a8b444320281f2100b47dcaa3cd6e88f414716585a23bcabe91afdc83aa9fbb4d781f7cf4a93931f424cf7ad41d3f7e9f84dc064ce7c3166bee18272e8d234196161b5f3700aa67c2cfc2473bf94adbbaca9a9a5e6d2768fa521fb6359db929bbe74eff0906b27e5654c0aa82ce381dfde34df49cb993be982fd633df33106db0ea409144e9d8af5099a657d6b3e7048111ef0a8856b7028255e375a118f55aa4c75ac2b912aa09fc79e8ffc4b9ec686b2b7c5022ba457fb392bab81e9ddf9cf7919a07dbda1f41c7ee671f4473deafdcfb01f24ec3d74992d38883508a31784d8eb87d995275d791bf8975626935584fb06f55c3fee2769115e970228f6b7f4d1c2c6428dcdee9083a82026346f49d6f21927895cdea9b7d85d8e79b9e044d38d72e1c925f6e1fbd26958299c4a6c2c84205cbd03a12a57526383052cc0c24c90f62dbacbcad10c44de686572c99702552f8b1b37a9bee72d7f2e7e20aecdd73046c95c818e66b66e6142a8523679822434c4221076fb208746202dc2f8abe131d096881c366668de033d150164ad7ba27ba1a3ae5eaaff0b994323413ab7198536fcbedf2ff627a7cac37bc1dbf624eae3f8b7e8a8805fd163144ff5b9356c87e379eec00102d365a4fe0fbfa650f0708340a39020d484505342dddb0f04658d637f106642c9b55fc6f26933468a51049b8ad3e59b05efe56c047357d553e9f428c4b44dac044f4485e5c85f42a394f96532c795071021e297290f51724e578b741882ca0d4832bbb48dc05ff6f542601d9414462538b97e3df592c901a4e754b7e4a4a76ffce0dfefcff0d54264302a83fff43d7ebade7d0930f5fb038945503c360d5bc206ff5ee18369bc7aed8c57d61fd12f04fe29c8a97c14bf6b434223105a7694cc71d795c315f3e37c936a20cfcc42600d05e0d0b936a2059ad9eab76815c6a938bba286fad6544e463b660065cf476d600ff0fe641cf3e1ecab36ab16e30892ee8ad8c18a724b8759a7b718ce9ae94728b2dbdad7198cc3ad36fbd4eeb8a1855e14d989888b6d65b34be223d6a152d3757723621886ba44e380f2ed0082086126310996e82ec742d3d52045bea78dbd8dc37c5303e583f18db09f8e075586237f08860662393fadc0fbc06afc49a57ad50a7fe9e7e9163566ff93850b988f75a1294bc1ac150be1f449883bc9590124193dd51410463aa68afe6c1b29f4509c660f86e75dffdaacb14f93188543f695f0ac49d11380853c84e2edf88bd83ed99dbf9012f91cc251a6856a240eabc5755dedbb71e30b8330b80b41a75f5c40bbf3929b7e99a8ed341cbe01bc1f824b8c0701e3c00795581cfc9bfd4b5f7de1ce533f9e60106009a1748f5e1798be5b3184dfd696dbd4249aa32e6846cb5817da8280d221e03288b93eadeafd768f4b0d2055ef2a32369e6f71eff7d8db951a88c2c2cec294842de1a62ac0a7c47ecc6f3c1f9eb1337935dab2fbb8e63637cfe067084ef586d862e2e9a48d1875f9b2df6bafa846d10810d6ed6422d8e3f3115f8168597f6f0d7d87bdbc378da3db6a32d2d162502a0f9eb7940ddcf532ab6182f57b7bb62317ec7cdb6ec463d6c802747482951a55bb7842247aa2e847af215ea81e93afb1095cd315b839e4073a97f717f9cf251374fc192ac190390c66451f1769f2b895a63fe6092ef8f8d9ec09a793857ac85684e0df92bfcb6a8fe11b69577b23b0addf2a6cb3806c7c643397de7228e16ff1b4fd8f3722d5450027b652fc7c48804c71ed239b773c48b72594db00576172d345b50f287abc1284ab52c28b818b1906429594439344c6a4e5585a229b40c51565751d52e659851fa1ab908d237e649808be24ea99b0405e8ababe1ca765307343531db4187335b096c689d668d582edac7047878825840ed90a00f8ef6681dd759f34462992265498c09e99f4f779829895e02041b45d43b525bcafaf8e600b33fa5b4f8012059284992b1bc86bbbea350597a498abdf3effd8d31ec6852fed4bde085665bef81ba98b8c9777450c473bc033efc8b7ce275868d1ab9517abbd105b9fc2504db4476eb44203a62aa451ff06f8c38697b7aebc416cf000bf6c17772f27122ab280afed2a2a81705c7fa12754d087973f7011c960e17fa3cf4df062a10882a923b035edc3893ba9d2a77126bf1ceab18b383c53e3f0f81b0caca25b0ec797312a8d57c1e02c096979b40d9e7e625a06c2b6884f990776698c2a391b8cdc50feb8773050fe4c8112f919d25b08647e972010000d2fe751f230b254e1c2e7162f552154ee96b0376523227ae1e8f3602405e3d0d0769043b35c76ec6ca95d9603ba2f8214038b98df3d3e65502f5f3105616d003b5d22e35a91563fd0b2630fbe67a14cc1c130240859c88d04e194c0db43c3cf40cb408ac76ee7e8a761c38c3e90272c712d1254a4f63d9dbb663c7e4b6b7c6ddcb6f9b45779f73fb6b748f1469e55c5970d2fe586e601b75ade37bebdae9fcde5cff7ed381d00f5c40d1876f5b79051c8c040aca7324ca7f83646348f48c1467e15e19cc18e4470b2d4c73acf9d72df9e3c8a329b951ae0da3d18da13229b40197f94e5aac6a58577c7be682b66955bec4f2a0c8aafa07ddf5efcdaec4f35b793f17395d791780630cbf6fc5fa5f1e5d15bc7baacac304111f49b7140c2733d98166e027945feae464ab690e085f60a729981d8975e68c2cdd5fec1ce27e964d8f7b5d8a0a9c23eea98c323b36287297a4bf55a67a6133a7ca4dfdb90d42c8b54f4a11f9e76e4ab3858a3828a9bddcdaa6552b7b64c2571f0464898c05b6f6a6b747b4b5d30c156e5abf1f956f6e26914dd8bb0f42cd8eee6e9c5f2529d9e66e27a61d76e4e0ed4a0196af29ba99ea099453bc7f5529d9676ed2d80f9f270feffd5505c99fbb99627f78de6cf8f75728257fd66d9ab2878c7c52597a1a983d52152027aabe702fde2ee6f65f1539d9e5b7a5da9dabb705fe7c1489e497bf9568f7bc9e1d78f3a12493bffc4652fb734d6fd84d8111480a44b634bf8ba2c214061c08c5c742db378beef95f8636321fff326c5b249c0efa1d682aff0dd9557bfac0739484e920afce4f1c01147ed3661e81413e6fc4c584a48ac7a54e9d3986cc87a81ae2edde442ea4632ce4d0a6929f5856a6af0f56eddbcd9ae7b8a668129fcd2d23a8c9bb8da47d4d16ae987099d1db582c719f7b87d02c20cce0ee19113ca715f61e08b0510174da6ff7d18f7956db67fb09d7987d98aad837189535117a8637c0f15d67009477cb2d6ae2c22e01c5d8d892456885dd30340a6e327eb032138d28c4fe50594e6c164419638a1d74c7afd4132b53b7772d8aeea61ce16159c97867544ace78921f2c1b4168eaf6506cd9514622a98eed4cd1ed3ad55269e2382448742a1c7689484db4c011bafad42dc73d5d64f93b03097a0f9ae5886745283616a789d2f1e5e0d88852acdfbb920581066f3d28336ba02dd0887a79f7ef3b2b055f9bada3d5541f4fc7cb708a3a03c112c19c3d868c525e4f9063946c7740195b7ca0a9e2ae213f4d720ea413b9a2e3abaae397ab33b06eaace27b8c3dadd840cbf5ba6d0dd6f19d8038ca627b818e3fb9d4951c7da80ed3bcfbc046910776a87389f367dfcf3b24c0c1312f8973796161ee298349cd2b5175cbab35f89372a179fcf2ddb5b798ec6f9f4f6f389c69bfe99a1394020321916afa277a4bbdf2486bd5f17d1babf8ebd59f4f2f8bb3bb7719ea973d7edf2d2871e6c796df39fa54cb17aa50e6a6872d4eb9eb8e8e8be4a6a24cb590ab1c5e517c7b18625a022387c174d22b6618d2f582ba289852224c9fc69cc3207ea084305cf5fc1301672e2a809331fa7ffefa04523a67b6fc43bd5b5319c4ec73d10cf44d74efacaf7939a755aa0a683a1aa5db6388174242194fb1bbc69602a23241a81cfe574e5245c556dbda03eac64ece5a5b19461225e3006aed1ea401a5137c07a50dde03604889e7880917cd7da84df57e9e70b41bb5e2007180489fa8bc11e4e8b5a407ed72ae35b15c0df7a0d3ae4a532504696e61ba6f9bda0166b2b8d7fa49ffdde1eb2414418e1df07ccba396dca45d2cbdcfd72ad66c54a74f9c87d2d8fa006e1ced177a8645d7b171f1ca95789a8413de4536d70eaa16e1c24cb1066947de1f78a3bc61c60d43a7a259c3fac2c88ab1acd05b89d7fca604c872103ecd34120ade48c48ec79b0f934c07e47b167163ea3fd0e07587cac6b01923b6ab899ad380eef4221f5b25d736e590f5e6754b8b526b3f3ef2b0f3607c3ff3e8ce465b32826e98132d27039d605b9dd9879f12692667763020c2f10976797c03cd1d17181f0966b1c28e3ecc02f21e5ed3c5e06af70544eb89ad87c4528a41142a17f64ebf53c37b596fe249c24c05a82089e627cfd0833878b6f0e42fa30154bad0e8e60d86d917d300a5813967eede2371672ac81cecfedb7fb665c905b4b82749747712bae563e28afd0464039f17d232d581f42e2d11d6fbb86ecd97e88c260eed466455c4509de45dff9b147036af7b13f2224bc02d95d399279ac5e4be329a76bbef6cacf5135988aad925d772a87af5a5edb0b4afd1250ad4a816ddfb33502d620cdf8af3cd12a99bac594440b70b449a5b9cc554a2d346a41f6a66573fddd506da59e98bcda9f3fdfa4f2e0f296c427f320db8f664ea695a14c39e5fc6ac557e06d6082f62a185e6b46028ca4cd3f5bfead0113ad9f5c5481ded52605c2253d698e7ea006260b63377ee59c46c62e8f88c628c09bbe306fe2d2976946b51eb0c6ff710e08db721f1385101ac163a52149970892eea45666c2b78e941ee62e6ec7d9a66828620fe053b607fb5bb1634a5f4a4890028f14b8b4a835ab988ccf2159ce73eea956810dcc221f6c88cc43f70a8724c6c748123940cc95ab39ad8d70e47aed04d303f3aa07d87e83528fa008618157ef03ed4dda648927cadc3ffcc0407f0b8d66c80a6b158115659586a5ecd8b68814d246a9fc4674578da4299a27eb1618fdb5bd4fa8d188b84116d4e4df7fad24b449f54d696d5344f4a3230b29e5db91af1d6f2c6249146b1f09444db30add7f8e88ff56644e834ca313fb7cb25e394af3bce32102f9b362d73aaa16b9cde0d4357fb3e5161b0b4ddb7c351321cd904626be85561da98739397c20339ec68289b5e58b83e0467a0ce8274e6832eb6e43650224bd2974c867ffcdad9440dca8c9fddbdc8b6fa701206ae776e55908691fd9a2842dd136482f139e1afbb4272de27e0b43df896a91e0fe088a4b2c2bf2d33f11c30c68a19e196cd3ca97331f742249d85e8a35c6fd685f28fa3e66afd842b8e28865035e071ccb12f62dc7100ef6952c032e1178f8db0958fd2b20fdce1b5ad8c04d65410646bef32a6b28709b5d8166e302a6e8e97742e158b353e26b0e9adbd6c951dcb4fb6155f24c93f5eccc17bf9f20b8751ada81f2a1d1d2489ecc3df5e8bb7c288fe91be75c22d6a4b3a33e58de84f6bc2b01a637ce05cab51484ed0a166bb966cc23ee56431200c1366c6f18f4fe64a382aeb357c7c9647c9340881f6ad95b6fb66b2d3e33e6aa1cb041ae66024b0e03ddb2fb17ee287be38977d06c267ec9025bdfcf9f347bb9f1490f2e492c788910cbf486a1b5e87588f4b4d6c31d4f140be49b8d0c689adb3adee6a82e89e9ef1b96b3f00a4b3a81853186140aa4cc599f61882a55ab5cb8a818acdb15a6e6037f85eb0e411a85ab26da0f994c16cacddbd562dd5d9c11b64e4d297bc70cf39c32c39e5b514f30442637573a2d5d5bc45367c74168e68a45e830e72b165b66288af8e19d8d2465a255c7d966a3ae693f92f20269ca9588f4ee5130c91f8cdb4820d022723fdf40b916b2d1c06cb2c9e5708e35c726644dfa2b90fe627befbb83b30a5a59cb119d0f6c663d7b69dd5b4bc410ce5050685387610f70f983f45e5d798b0bc6845c6998d7888412bf77967103b6e399099a41feb3166a887ae81b1b4e649341c9c9e51e6d6108f3bf4ad754598ff0821673ab777f87f861a09fa27096be0ee0b098377719fc331bce8bbfe02ce9e2b24f767a56402ab456f37e0eb21200575135286288306fc9c9dd4a10ef4572a7341cbeabf61d6d5ddaad70eaee1a23ddab6ef09b9c4d1d61807439282c37c798894d692030d16e8e98b9e18e1e7a28934b626b4d105e22494ce31642600badea37e18b34866762aba2c8f9dc4fe641e99c38045b426d6e40069e08378cdeb32d0abdaebe8851edf018661d4da4f875ba70ca42fe75b48cb0edb58e8952f04d181e5ab76413902e26fd1a0e93382b2e87da7a5aa1402b94ff02cc9d2a13d9b56b65feca5f5089f74598e42a5c91869c25c432482fa14f218b60dabb5f6bdfdec9cd2df10ad2d750570728fb69e244ef6734612630215ea29ae0c0c02fade927e1fd177ef2892b95fe9392f13550ffbe0900c9a6f7603e2cb13a77f70ff1fed2b4f4efc2396cd00dcc344809d8ac9f9b0517ef6b9839430a1ee307687a72e588fe8a926da4aff893636ca2f8ce337c7d8e30e7650868e440edf1e1f1a0efa9810626673372228e1350ab8179ea03b9dfa94e9995b3a3b6121418b48af77a4a2c087fdb014601fe736ae80cc26181978f466b221967a2ef7351d60c7878c2353c8141a334c6ce5521b8efc5a15a0b31e6152ebffb9dd5c3ccd35a5cc4d39979a1db6c296a171b5b4e62a97000ddc05b6f0a9e93590eaf8d09468900a2ba254a9dc21c8579009c427b485e96406b5ec6fe0d6ddfb165eb5714aa64036b1d99db667b8e66cc196c64827d6d967c693c8bbc9015506b93b05b317b76ec0eed64889fe618dff9aecbda004915fe88aaea7d9d06b4332245e660571f4fafe4da9e84d32261fb3216bf3c2355cfb18396844186eb8f55827beec0530812aed470a68ab3fb9442c9de9ef26b3a405bca2ad5e97e4c352b7c45cb32e890c498bf870804eb68013a0b3f8ee810182e4aad47d1d65213d3ce905b966726392ce69cf3955198bada521cdaebd77d538b3659c6ea428ad96d7918ee5b3d5d50289add16e33378f28653fda087a527ea08f33e8a1b7241a3e6770f32f0d9b78acab098f8eb1529553e88daa6d68619337a1651ec27659ee9683f36db19086fa73be70e04548dc10235a9a29c18c0fa48d6c04dc134dede492089619a1824587f1a9526397483f13dabae8f2b2172496b66b8c5d860d940216313742f3323a9e3c2b03e59dc7ececc679c49407b056d6c3806b9c8d0123913b6510314e2de36953533ae63e76a5d125dea4da9c5ea42e9f3a9e25599d958b216c2e035c13f04b2615aec119dcc812a6b17b1ee4a09340acb1846521fd056a366c9c7479b49c20731f886a56c21e0796d5ee19c0b6fa4547fd89d653c56e0cb5f3847c15b29eb1b9863982d94f20be6507823e57ac3e92ce3b10a5f7ec1398200a1fb9555ac267625f9868d5301a25969ae2a8d7bc0d19fe4fd8730eb7923e30c06e8fdacf345d2bfda7adfa2cc5652b8abd4ebef29f3190c3dc36f85248876fa2a3387d544ff00f64df30534458aa0381028baed1fda57a5b329ca2a628a30e834c6f7e09af9178fa2eaf2fc49348dceed4f9417717142be3019b94ea5a9feb4b2fd082e1b099ff8350ccbb653b8370b4f9ea4ee77cc01a4c9f4894453645944cd7019e4a09f647024e843e4a0a06f1b639470944c3e9b5f40e28d49858f86dbebc5db762bea9cddafd721e09b8762adbc1eb3f8c0a026636e1a943874b406804ade017f5fdf223a380321c5c98618afb3364209ac28d130f3471e4628473d2a3b23d6329bb11e2da10068d1d6b8374bb4f740919bd7f8cfd9a5de82236dd38332acc8e4440060f8be477c7209a745a4665b8bef4d7629a18813d7bd24ccbfdfed9021c0227a7fd1ac755f644eb46bc9d18c8ee4da56d2abe4595594fa68b54c09502fd59694cbae8dd33929c59fdf546020954bc63472b26f28b3e321f306c91256be8c02a7cdc99e6b43d350fba9d24df29fc8a93030f93776590a357e49ae0d03c40b5b300e499e90a2022cb8743b9e1af3834b633c8a8a6320f34c5d9ca0f298c6a17738ced89c2b4a1f826a3aaa76fa93303a979e3a04250ae7b211cebd2d262fb4533c9796e56d465e1a4f4db56c5027ecfec255186b21e31b5991f615164b2682d3706374d6caa680423159fea37b475770ec849f0534c9a516922989aeb2e61bd965ded4210a2b28e7fb21effe49ce119ac426e181f848bdf3fe6435bcdc9e114b5a6b0576adc87231ba064798107f311f292bde1e62a0f6f79c33bc7f61820d90d4ad01180a4a7096845f8ac9c2dd09c941843688cf3168216ea6e8bd5203853f6f6b5ab978950258a90b2bcb637ec844f9a96e8a0dc4ef08e4327e8e4a2ad493e1560b2da6645792b08d316c40bcc6c2f442727679eefd8fad4c19adb18e07f8bd74537f06eb9530330c0b992a91e0d95d6e62b3acb7be3bfa50ea9d83ad1d00a0b594a813ff505bd01f55381d386c0978bc589cb4028ecd7519d91144ca24b8b7e699c1975c5d5193d697d0240a382051943c2444c1a58f5867f364cb8927cb55b475c0b445f5a91821b175456bad3a4617d4e09ef4ef756179c06611eb4a49cdc0be82efeaaf5cdac56d3fa1507e285155d40212bb8af207b8bfde03c4956795f187a121cef8ae1750865d4e1914bca7710927441933b80c68ed16cd388ec6c8a132573a1e7abba1ad17e9f8e0b9a93c9d0db11e406c7983c42e394f500bf20bed9cfd781c9d57c2c3798e3b52722cbf8e603cd82c3b7692b38a02f42ad838a2ff4930ca63b8fe6cf37a56d9a48cf5878d23ef04178bab4855283bde449035a147bb4d108017f1aa777a72272aa7c389fc5a8db2170fd820991d8c5070834a17edf818828da398614b6aafc9629e4bab6c1547801a826dccdb79f78ccb38b8eec2872c5bc7d6a605831bb2c67a8c8052a65a19eb049a63606a0c1c2b67d82685dbfa6a9b3ab76dacf611c516c62fcfc8de84825393a6fb9d42ee38d3fc3c53da8557769b5a98c6528c5a50146c61927cea5089e9c4d596b529e74ddd3d9d13cee41bb744db68b753894486c6e897c2f5392e1baf303290dd72a530b8192e81c25c66cce8dbb0ba02196aa855bab08ab92774f8cd8262c3ef04f0658f30bf56142999d0f75997a18ace4045b5d1da4716ec64a3b3725e6f252df4134b2e405b41a06d669ed010ad204fb250861bddffe08f5486b81a01d95c5e5dbb01022cb05094c981e73a5c9cb5e9e5d03df8d04cb8129731b93d2f8f9916c2303010f75a17ba8f54f05a5508380159ff21084cda8d5a55e31ea9784b666c4306e82a2115d19f9ed4979b3774acdabfe5bfaff6874fb5c24e1053b42fd5892a9bffe38901c8ba110319cfeb39a03f769b67aca52710c7e88fd75738e1aaf37ed04b2b295326cadaaa6dd7346e530ef88484b1bfa769f50f672b3b82feb7d11e946dbe8c050cecb6ae9a441109ea7a17d7ca98694e4dd149d1e292123e28b31055f320b0f4dfee8d3e6e2520ff74245e1bfe344a8572c5b441172adc12a8f33b393bbeed8c0f7e2624b7bcb57b7da8a1f53fa5c8b599b7778bd85cb1e8b90cd987dea1170cc900c5e80db6450acc99fa4c8dabb159ceb1140a9d2aac75468400494e733781294e93b38273324cd3b9260fc7053b0ba0c69d78bcd230467a9a4ddc5d8c06fba4da7e2e4983aa63498dd69c04a5feae7a93f49c8d25bf83205a2cc0f6fd176d32656387f33c83ce4eb96275f532358b9e45bf6a2acfedf0147f17a310f8549884c3bfd80eb110ec8ce29cf41272220fe960ab2c59964e20a9d4d2186b84b560c40167412c0758c51503fd0ba4d3ba864308742c75b05dde782ef0432ac37deb30add13dac0e6021705649e82d2cb8e30b23143e04c643738ac69cdda9cd97f97cbfa1baae2b076fa78c3feccd32962bf2e9339893301b94f30fcb31e8564afb067320608bf2fc177624ceb4a1612a5ded969beec3a10e00b84db5e8180579da0abd40dcace57de8705d80ba468a365ccd0c8c29397fc10a58101a0b71c4524f8b8e3e85d6b720f08507542e71b29501daea58601a2f8c9ec9971cd07b38a18f3f2bc68ff3b6c7532d9de6c7f0a9a69637f427f2bc787eb8ff8e40bdcc9bf18ffad53c3610eba57a53abff388e44db0da5db1367e80b51fa709a30323c59ea2e0c4bb7c9b4ab335648d49149bd827e9d13fc7e7c977aa5d38dda79c7b9cb600b73a115989527e80f9759ce975fc544bf5e00454763bdbe29c47f2cc7d97aea1368fc44c3fc5d9115372a9e429188985911900c62fb737308e290cec5dd26a37a1d835414d620f17167cd15dfaf2c91c911c26ad604b79195bf96023745a32cff21320bd862cd4b8c382d92e8d1bd75e120e27905ee7e1748507fcd0fbd8cc395c46c641b4d881e1f4a7380a7abf9d64528ae1a0bd256c41baab928948172a5ea1c50862454782b70148575c771211488a495cc77e969c74b4fafe09711c6d7093ce993e1f190e4bc9645352354517c34bb8ca173ac6b7f6e91e56d16a6ed899020cdf524af9e93dd333b09b48764117ce6eceafa4a4a13702538e667f8b04e99ada0b28902161f7edd4f0aa5d13be4582d4553e409425091f71ea261820277a1f3b392e5e7bf6857c3aa5ea07f07211f31203572012654e67223fa74f351475492bfca950119b4a456c0d9a1938a56181b6ece68e5cb76509a6ff4ed0a797d5b55536190baab4be3855014104c153e9e00853e9596f5ba6e5fb6ba70a6c279c878609faa35744aeffcbcf3e230a0f945db6c042ca2fec4ba59e8eec387f50a2dbf815c10a229a659ec44b63b3f866979d17c97ab4d34f0e0819ae8f627bc5a03216b7c1738513aa8905c8763ea82fbf15d4a5b2e543a30a913880f81702e8531d9f9e3ba5a2aba1cc21a1c1201f9d6d3984d6be271c24567791a708e638ec311843eaa32c93b89971d0d0b948d414899597b7808d7360cd5cb10eac20be53dbfa3ccf25984240e3accb26ef25a55a3d4e40f3f83b978032d24a33a07c78b3973eb5ef8f7dd4c63c8c2fcad8cead4589323cdd8ad1a4ad21b9d087f2154d954c959ecf3377c2e3665e9adda15310fc12d38cc20ab519f7ae365737c9b6ff33512a8f82efc44c9b171fa73f85b7fb8241ad0a4d545ddd6c606cc18e70006e4e0d534815d66d7911161ba83add966935550e28e1d90841a64e494143b21c5ce32e664a6487df95e2aa585fc49fb8cbf32bc2be86957dab37855bd4c99c45799a1d39835a02f47f4bacb97aaaf3b6f19a82816636a2f80bea9e4942e18087dbe52b1ec92a8663ac03fecb21747e7b0d2da2cd2e9bb80cb5840c0c27960a577b4cda60e26dd948b112d9ae621d81e2a56f2ee0aae1a270146179a6593246baad2a5fe5e65d097c66e90eef4d04a0c2d4ca9f9863fcc54ac54d9065758b8609d958a5c65b7c49de60847170dce5e6edd6590943189853c27ea5d049dbb21ec523d46b5e0d14a8f528ad5c750ce35580c3a61b4a6e6ff68a6326b9677178fa67e84c60b15998e339492af73ad20005e7b0ae34c7e1146d7ecaf4874fa4ce97dc541b26aac593349b32020a8a6ba57cf188cf3607170fb101f86b4310221b0c20fa290d96e9c42111a286e64490c3dc45f2e8dbe1546fbede92aef6a51fa8480fa5b21785f59571792ad4ca1d7d6b3a5117b5d20c728eaf627ddaa20034a08e10b6538096375847427270c646ea1a797ba6880a0dd36e5d00490904022f9643f0b8fffb53ccc01546726428cfde3313d66d98468914c75230fbd51e112d38cc50a9c27e6da079282c651f9eacf760e907890bdd26f1838a9ff1915e20fd357008b0a166934f8bc4f83d8a71adb96ec23c6c25d95812a28d8924e8981bad767ae1e8e426b1af0dcb24f7306a4617d25598e3188525caef981c4dada077696cb01c2d3edaa301844ab3270a22addf3e2a8b59927e4faefe39db21ea7c8973ffe32ccfa1a245895c513d8f329c6a5d25126eb5e215f53554dfe601e5032be3b7725907d4c5b9c3f96995f5970c6a2e353535626c7e3d8a60b4f402efd6183a6cfee9139c5724dfe2bd5ea8e9c70c3f52b2e91a23bdea04675ff7651c06dcbc35215e797ab1df2a2ca2fd6f0c9aeed0700e01f5127578e807c3cc1d86d41f79f355b0124e0af71bef97f0b3bcbd17cb0078548b656eee0daa5cff37c2298827e7bd3b03050798b2517c069ca36c644bc8816d6c0875cab47d22fe0549d0bf73fbcfc44bd25020e502b0ddd32022b065a4b9f7daf4964d86fd3d8584856001e6c480cbb400da434149dc4b71f6b0d234497f840b36ff8c1da4638dad478c276773ce83ec65074ffc67ff750c22b449b4c40c040cb4966e9b7e56456f3cff643a34a5a135f976773473fdc3e159be69b6b87604e4b04385f9abe759f4b7a5859ce79531880c78018b2277113bec70c9fe48693c53402fd4c1f8cb8c4a371be0c46cb074312b2a2a1f601608bc56d09eb47c9010a0a0cb428865d521ff17b757f6c9cd25a9cfb45d0cd751bb4676869d73aced65105a1606d94c9a023731cf4c01e9dcfabb27a059678697ffd48937d81dad0bfa98590f977e248b39d17aa2d9fe2edabf51a2283f8583e17bdb0241992fd89a9007f0a923ba675320a45daf41b7f0b00490a107115362aa16f1570b9acefbf06645a41a3c868774c7249ae2b77375c94e93c4e9e2a29d8c43a60af2c1f9c3dfaa6a234810105db3795ede906dfc19b7e9eda26225e4eed4ce43e4a4e9d41a71556f507e78b55c018e6a6f6997e3051bda142d4b84c530181b93920c8e258c4991ecbcc8a06eea8408184b88def4fa906f59d2eb096532ea2ebac20f4cb79efae1814086516486f35de3459bdeeb8d9f12d5e95a08141638128c063bf2b52d044773427ef5dee7360270b4a507bc36d37c8881f5a86a99ef3abf51594c9bf079f391b59464c96fc2319644a5be30a1c009ef9c205b86ac903c21c11092daba944714d2d997e18b2e3502a7ab56661501c4c7cd210678f42b6ede3005168d7b55e3c5e70f8770243f9e1be5d489b090f30d9ec24a31eba25be0524fbe5b710306395bb930f7bd3ed4ffe0e2091be6495f4a40ed063bb2bd5ee21193836273069e9a82744d37c9d31b8d10d8b9eb4a87d6c8fa47b579519c85ff500f592df69984d18d40ecf2f3f593aae4493f84c69a6f3b6f09b4dd9bd81d3ea079234cf6ee08281cb5de7c6f1894c5d35757d598535a83aacf5352f447abbb3ed109c1d3601122ef7f6645bee1426f1039aebefb95737363a73999fde4655c093872dbfded9053c4752d364edc0a1be8668eb8278004bc7fada6e1f286512219da67f59ed3ee4562b4c3ebeed8c9522d32db4374feeb79dd4f9256ab82104d13267d0d5d05ad4de6c21ee569f310158116189063624cf8f687d8da80d2ecfcfb5390b568c8c941bd30b7d7d2ff1899dd792138601cda5046576ce52b82a112745d6948cde41b28493571dfa3b80b0ee792468bb009e9c9ea63c6397242dce0cc71666c25deb59e1b4f54ce16c610670b4343bdc5acf74c36b32b5a06f3e6cee020e8493935cadda985761b567cd3b394ea7a1a9c19cc3aa64fcf817e4a96a7e7fb7c81f6860d66213cf187bbc0dcacb429deaafdda5757a41d02918a122103cdaf0aa6558747fa4c32569e9ae490913828e10be0e6956c7e1317ce5e46823f9df6f275cd7eddde90f2ecc1ad638928e4f89d38beb5d7d661d44c3c099e22d5af1cacb7ceb3584d095a579600af5e8dcbe0aa166024377debdf49cc82cf03e2b786fea659f3e58c55917ee649b530caa93a3dc78abca9d0addce4d17bf5e74f34719e58249863107751ab8e964acc9749b568e811bc71c35d67faa6e904397d57165c09491ffb2bc69f7394c9923e83cd39337bdf7982a9ddb7adc1603da7e9b98b91a98d0f960386fe0dee54f01f3e8ff3af17690af8728ecbf7ad2baff247d9c9cedb8e07e04c7663cebdfe8f47d200e119d7a1b43d2e482ea8d9425f27fa32746a3a5cdf86fdb034d182e5ed8d6de535a8a16f27f361ffc852287fc1f8ea312737d35a0dac8ffbdd4b044ca5b84f3299c900854576290dbc266c830baa9966ae14b4f81260163ca8e9d0020bae9b32ee93aa93fbbb7a628e23782bc002c1846b87ccee32199a208c6bd33bc004c92a42a56640fafb6ddd2f34567dc5d606f117c5a6895aaa5708cc2909a761bf988c6e033be00accc897f2f0000c5a326d0771ec4008c649b4f25501305f7e2e6312c142ee4b47615bbd649479bc2be7d8572f091acf1358309109d61e47651738a4ff4d5d619cf0e6ae30989833ef2377ef907159caf89663eff8703c112fe00f10e93fcfe834addce21061a032aee988c762ecf6cc2a1014b9bdda2f7739425e0bc25a0818b2860b16ea7377aef064ad52e85f7ea3398ab808374d3e4efd9028028085ab7be4002b38a09266691bd868718505c36c11e13478ddc35c7b5691b09300c92ba25368fbe11030a2bb832adc916840d5069d92514c3cf50ca1b6083100c88e92b7cbf84051a7877033a754c4391a4d0316dc0f41308a79af4606db8bfa7aa8b00ee274b2b149cae04722bb7e0c701a088bff2d44399191901c69a394b2988f32b7e0e20fd1d0fa8c3772ea557c39c9e8fb553327e8bda430fa2aa3be8334156dbdddf01ad0ea21eb0b9a107e84070f1be01080394058be145811efe5a5d1502fd37c9082b369c40dccae3244282c08241d092872f204ae3aaf7b21fec48530f7b981f29875cf7d4a0ad91ed091ccd8266c4e45b93acfc37d34e81965d356ed50aa22bc7de55b26f49642248bdb744605deb38162d30fc2a0b24502df0c95d031fb3455f60964987088fc992d80bcc52f55c66421d3603ff9eb1555ee0e9ca4117ebff8adea3c2959c1ced5f0cb049613168fedb8aeead139b57284a1e5a62b56fa88ccdc54ca34fe7675d0bafb6831fac4f96869a717b688fe82f71f4117b846d1fd80eb692cff378cd599ddd4a3445fb898502a00abca687187461f2d34d8fc08ab5449785e395947e2ea5d6a817860afba6cfbf0723d100163adac3282cbbddde96d95cfeff4e85da9b4b6965bccdc1ce203c10f64a5fe6dc33a09439cbc5eafaf7d104c26d36912fbe55a7eb02aa0f260e9dd0f763d44753836033309bff29aab095be0fc70d944f494f170621abf4184f908d78fd945d44df28171fe43d50bbfc9cbfe7a7b45478e3d8c0712c9e6f8e509a5610cda471e6dbc1ebda75aa44ba0821262b16f9fe71f2a616940556b7d116ec7c0bbcb560be6c24add594b559094e0234e9302a6cfe8e38954615a3195ea7cdea8b36929506b793e50df0d3b98a89a6d823d0ea8123815e8f297250d33ac85ce0df2e55f01c338c547450abd85542382cec31a165241a29098ddae78129196488981e1b5e9995df3699f264e3470feab7656598ca52ae1ea71c6cd1c3f60491026bc4dc2bab9bd929fa5bfd00c1c893f6b7b4f04bcdcd808a619776144c89e308fd88681cfb309bd2dd7203da1ddeb6c08abdc051ad34e3adfc86539bee35d5a423ae301396736da34189d96982adbad2b031ece7ce3939af8dc4de689fd2aeb2e8a9ac6a2230034d6bacac920588eaad65a4ec5eb0e39891bec00a59b7cb957a0a49cfd0e2cb21fa43bf2ed23174592a41d33667ef5ddef028e86f899cbcd5b78873cfa6cf27c1a918cde36ff40d0ddf646c9f7d339e1a0ac0040ec096e5a64adad5b347fe40038bfcdf95d9a99d003dd8eb5185e0d9a7b31dd4decc53af4d2856649e62ea74e2f2ef373aee9d7cc15889473d724d809b2bbd45f7ae7a179ff682f29aafee556925d1c3080bb65f3db3388195d04958d674fef3b5528284231c95991735aa5d1b64f9300c8980c749d1cab0dee9f89b621299be67cc407da1e058d21e7df217ac41e2a559de995930b10342bcac95d02c7c2f981011cf505592aa2f9f4bcd1cb3028d1b2a82a79215daef167014ca99949638b3ca5903dc073ea50f486c695a35b43b98b6600a7cea88aa6ead88248013b7a48320c1bb1fbaadf40177dbfa50d2f32ac8f04bb85c2942e50f9a6a0e43d208616f32c26b98bed6a34ed8da24071facf8e199ca173602a6c233a40268bf9c9b4db9ac16d0580789f42cbd13331945f54bfb092aa2279e4f032e92cfabd04805a670ba89eb29349e015f36d34d3c9aff267f90f45db44489d070f39fc787de66dda5e6c32f371451af3c6a68417f6de7a4ff622ab3367dc64acf039266559628ea6a73935560b439d2750418678b18588647ec1c7a3cc4307c32f55ebf73480f7b5c43fb56d8c49871374d85a4f38c0fa2df8234b5056efb854814551030ed6a3a0c581bee38d40832fccd155c40f303b5048c0ca7d3e988fb81d36345d58afe29c46e29194d64174fa891b4bb45f88b4f249bce8df5e3f3b0b70b13abdff33aa42b5203fbcd31b4fd40c2ba096db2770a1216e814f9125e91b7f27c3c1b2fc7f3bc27de05dfcdcae5bd40eff33c98e7b52cf87a78e0e7b140cfc987e3adbe0bbcd5eafbc00ff49c7c9f17e4bbf16cbc17f8799e07f3887c37ae9507be40cff38aa8f703c90bca6a0396b7f26c3cf087676303c3f922e402af08e87d5feb73bd1756308463c5f33eeff33c2fc85d9ef30fc8786abca8e1d44b0f6778e185870c1174a96328b1458c303c8a47030ed5c3041eaa4ac09f7aa04287271e9c454b0f61d43855a229587ab8a00456ea9021273dfc107252751c5124868e91229e1baf89181e0a9accd6ecd084a6c0c7991c1704cd88f880c2100203d121241bf000b40606339a8301f5b1e33cf000e333560c4ec132bec17d5386b72c77ca8c6a7b8bbb23f98b9b1e87a1741eebaea5ac65e626832daa5c362e1b50c7ab89cb66452427ca84a69e974febe66504e6ecf0c09c960cd4ad74562298a6cc0d552d1d9fcb0604d22162a444d50d979c1204d261c2472484b505f45a50185015054b9258e6036f402c3f27a802c10948bc30c163c1440ca698f081415641398f470a6b832faa5a2d0c9ccc54b0e30885551513005005c4a64b4e8f9c1e2250a920f7d2376e5441d936556632842e9b950e4b0a54a105135ea08e16116fc71213ccf0c106c905ccbcde18808e0f873545743901affc17264ee0c1f2b9a172a3410b041089cbc90952f450e504235eb0201c32a3436259e18a2a9cb38643191d253b3c2e43564f5c409a9870e372d2ba79e958152d555911b9a1e246d58d6b4907ea65458812951b1b2b3950251429aa7c80404d6e5a301e3aad1b1ca59e1084a8ba7959b980c642156b15b2bc9696d60deb490b8994239cd3e6c8cb4aebc6054427c9c608d864e3a3c7100d825880e4b5c195270d4cb8798dae253a4b8a969c5c39a02b080bb66a6293031ea1535c36ae1b50887505349ae2b184ac7858160cd9bcc89513a5273f5e3716d8149980d48443f4f2018d80465a465421d10126b136605d6063041cb223c98ac80955a390b7aaad82c0156b849e10046005be4b28e1051a269630028815c89040a5a56a4fce4200e1030fb86c8172c2d2811598d1121f0d322842412c1cb0660d1198c181940c7a820071f110012e8610428b1207302215e4e0b4aea0220149103104103ff4c0830e5b7c8e088d38dc1005b5660e13d03e58430d05c8b028c30bb626e5c98d8b88186028b2c9409a2288803969e2a307cecdcb35440802a000e1cb2c4d1d2c5151da21002a0060270c26552ca1841164ba70296af2a30738012f4ac0454a942636ae56132924c0091f6a10000b231460871d5ec8d224e5c9911c4f8e551cdf1ba01aaac6770668c627062b0c4f031f06be0bd87c9163816f029f043e2abe26564b7c526018cf8887c18b086b880e90cfc75781d743e4c1daf9442f04757c38ac9bcfe6f5fa5c2ed6077a2bcf6bb9ce8e9590951555a00b5520961612eab9da302114a28ac5ca7169a00113c01e92803e728038a2b5421588c5c6829b213fca54d978013f585d80b35b392f2a2fa3d6cdeac98f1bd61655af28aa7a8060824dd14a073672c0a862ada962e504b94055ceeeb1a2cae6878ecff58425c49ae14c053840067002d842d50e9e1d3cab2051e8f5435c73026b8397910bca6ac70aca0b88aa155312a1d64d0b89aa154e2b0808849364e5813920d00d15d6062e2fad20ad9b56989c1e393d6c7ed858e0f2b242c109ad202b264e60655195411810476519100161393b5a22e8ad825648a0538e8fd6e7eae212e26a6255a0c36be1ac5860ce6ab5fa56aed56b657333045cb9b6b8767638dd88ae96cb5bb940225008f45e4056424cb0c1807544950e244c105ba09057d209201827f4c8a8af498119194833012f2090c569838a0106a5230ea214e1bb861a4247c21d2fd7970fb234ade9c20214c0e085211b0630e50630302081072800014b0c21346a50c3b95fd0b90d12a8e1c0171670c001c8941002081f7890a5a90a150c2ef061011553905942891248901119822f063ac0f080185cd59a149cc00d10bef8b040c4c003b0588002100045132f04400559085eba64695a9240175844a00a1bac6c4003127880031460851040b080421912a8c1c0170ab0820a27cc547db9a424eaa829830c3020e0802912f0441131b8f0a52c0410be7459527a02fb117524d0050474c02106335f86d0c11215a527301d3aa097418604bad0028b2912f0441345c8100033603a588af204564494e46747047d4d196478400b0860e1802912d0441132c4100017cc8c210303020742e31c7260b9b243470786c0d0020b1e7419f2c10516765449d8ac59a5e043014b8e2f8ed711a0115f11df109e922fc96ac81b7182561baca805187c433c21ae202b20de0fd08767810d8f9d9d5508eec8d111ea04c9018233e446b471bd5aaeafd562b1569e0771bf09739870779dc73769755f9b73425f62d2de9388bbe7ca31e32d0f73b884394d618ecf0ff5a1f44babafa5dad26de9935daeb9de485a2dc57103878d10870cf719b55dfb371dfda151a0509f2850e8b5b6a7e13c11e214e13ea3354cb5a57b4703338ee397252bad2043eeac273dac23ee8e818738465ce7f1e6dfbbe684f20641100457abefdbc0431b316cb8b876eb2f29ddf852fdb8a43f3e4fe613ebd046091b1bbcbcbf2bd5a1873654e5fdd0868a0d1135690d539a712dd7ade9db6ecd371b50e731df48dce3849eb4a6b639c1dd2f70b719e185e6b5596b7fe61f77f771f7235e194e92502934e79344e2c489919322274d9c307142e4648913254e92381972e2c4c8c8a8c8a88911132322a325464a8c92180d193929322a2a2a6a52c4a488a868499192a2244543454e9a1835296ad2a4099326444d963451d2244993a1264e9818312962d284091326444c963051c224099321264e888c888a889a10312122225a42a4842809d110919325464b8a963459c26409d192254b942c49b26468891325464a8a943451c244099192254a942849a2644889932446498a923449c2240951922549942449926428899321a3a1a2a126434c868886960c29194a323434441ba21a7a7717e2a10b059d47dc798536aef6199360dc5d03f0cb0d90cf8d0f0994043a02a54037403723504a2975f70cdcdd88bb17717790c7354461ae9c1908c29606b27077201eb69698cd28ec066836a3b05d7b93d29bd9cfbed96ca674d7def4995158f9a6ad355da34489c2a3ff46ee8aebe1095b3b2a0a74e408dd95962f45da5b732d2777582bc8dda9872d1da7daee9346a9b67fdbfb64d2e19cf151c83ac37d1cbb70779787ac07b072f01f0a050a4de243ff34eb149dfdc54d335ae9c574f6e393ab59774f18ec62bb735966b47cd3ac240cee3e86e01cac9355bafff8e4b735bbef974b9bfc30b42c0270f72e1e8273b8cd82977bf9b88c6b66c5d1a4a52c45ee2ee4ee50dcdd030fc1289d474de6ea641c0a2a2d0edafa62d2dd7b90b9923966b3471847913966a424c9eb7ae4ee3cdccd7a6eb296bfc9cf51296d6bb349b76dbb8f45933976b39df82a6ddcb47192ed5ad3d61cb5db399cd2cdb2317eda5f65dfdfdaea8bc9bbe4ee629871ce4a9ff1862a7fe7ce9c3192fea9fa584a6bd6a9bb9431951ac6d9e97e95ecee501eae80e83cd2acb933a65577dfe1ee3a3cc7dd71dcfdc693c1ddb778f899e177fb16e0ee14a6cb7b621aad61ba4f4cf196e93c0e8d49c62441faf78e26b4448911f94e68a513274a9a90e42709c26172a49db76d6dbbbcb5b57d16c2b65cc951a84a89a02d4ee871f82644a340a1fe7a1955e0e1477d5b33a7e96cd7d98e879e09dccd1a840f0fbd27dcbd87871e0f1e25bfbed6dd75dc5963bcadf358629c10f953a1e7c191bb5be0a107e4eef74d0a853e8d96e6896f9afe506d7d28d597d7d29fc2b8cfb25cd56977b771ff4a77ffe1a107ea3c6e5cade5ee9e679cf3d35b69f9379ffb33bdd8eebaefe3decce12cddb6e64d6dd7ee5d7119d768fd5aee3ec443a77277d1439fc1dd75d8ace59fc274b72d597395d479247f4aa6ab5988cc5a88c4a6598576b9669a35dd9db502ddfde3c0cc17073840151b1f6890f8410004c63875c08aa89a0488d0c831957c784060e4050fdd6b8cc3061134992a006163d4f35380245ef000b2c2b8cae924231c843983c20faf0a00a98a02c7d15455c57c05c48c0d38384467bcfc4b013473c6a3291085600d2b72de3e622c1ac63525c5a25e071e134355d5b3955a3d521db4284b0a4b6b4bb5a817fb5c3ddb8bd9ec10426506779c1d3478d4eb99c1a3cef3ad78563c5b8a45bd954dec6bf56c2f06ae89b1c01e2a7c7bb1af870a6f35021c213492080defcc19907ed4e3317306ec49420c07522b2ac6d174c159519c55cf151c5f8959992f332798e07df403bd038020084a7dd4061c33f55156cf5e51d62ae6d1970be49162f5881913b3e9916a51d69415eba32fa957cf981808ae562b70058220b85a510ff43ceaac2270dcf3bc559817b5b1a1ceca1ba4ada98fb6280be411f32326f5f578cce31163c58bbdc4ac3cda23664cccd52305d28fb65a3d62563129a71ea3ae7365471538424000234408e9f5c011a265b5e3078f7a3d3e780a30b3a26652203a62d13154ad332dea3a5bfc8845cb90a9aa02573cad9e174f1488b3c307779ccfd3f1c0a945573cad568be2b8ce961665f5ac7870583a4e2e123ebaead2a2df9702092dea1d7d2e176db97a52c869b5563c600a24b0a87704521c9c1565f18c2173c43a5af5a4907304d2291f4bc989453d58d1564f0aded18e35472cb0a70aef49c13bfa7aaaf04a687d200e38ab2216d107429042c1a1be2ad2e1f9705639383cded1475f3d551ff5e88bc78f7272a88deb06a4371ffdcc8034877a3c24d850efb3d1a17e94c343a6aaaa8a27050e8e56f486c7cc19ef684553e0e008a4ae23440b487568d4ea0887474787dee0d033ded10df5e88bc74c0ec5f17848b8a19e8d4749c0a15f4ec8a323848a8bb6a45c3d43ec3edaea1962b7a21cbccce0d033d48c0e4d811edd504fa7a76a453deaf59859d19b9e2a3329d0231dbad2f940878784125614a7a76a4575b4e844ade877c363c373c63bb2a1383c6652e0e00887e27c3c3a2b1d25215a40aa433f58d11cfae23163d385cc914e0f0994841b4a020eebc686278524c470106b51ef88888d508caf89b57aa486d87d74889d102d20fd68d48a7af4e5b4a274f5b5a4c0214a23fca84789ca086d70848000867aaf55d19831257c74454bf02848c72411e5a22d3ac6cc197a14654359b48417bda13e26057ac4ea1953428bdef49ca1472b6ad353c2ab87455d3d25b43ccaea29d3f26e06aa8858c3c31a1b5060460df0f33ed002de1c4f78ab700e1318f17e2001fe00636172629f8ff7792b2f470e325e46af550370d05cc941220e35719cc1c6cf8e385a8863863872880388389088e309c7f158211c73b88ee741ab1d8671a461f580ab1c560e1c5a787050c08343031e1c6378dee781ac9507470e7000010712703c010715703000fc5a28a0f15eafd70f9082a0d11b676220f8860dde8a7e3a5756d4a3204501e7fbc21ef405d27ae0f84794055116a0e7795e16edad413dd08d39dc308197e6042dc4da30b3a24f629ea6046956ab96c33cf6a238613eeaf2c0d8176bf57c445d84ad9694778515868c7f06d820063aac0d11ecf8d183c562adbc1ed08d1871d88f188b4533f0271e63d1230ef3d80cfee36de4f06a0388b00d247eb4f1046d638a232c26aaaaaa8e38720aea286d10c3017df5e908332616c67b85f9562c23282cb0e5020816b96898bd22dac24c122b6a26057ad4ea49e33908e6803daf2f8ad6ebd57ab53c25cf080a96b0c7aac56ab58c5a2ed7b76ad196165e822c3e1c56192008b672a07c3facb0022c232deab2f96c40b0f5d2c971b9e84b47e7f4540163383d2c0f02691530e6eab90163208e0dbde159c16c28ebd371b128c8638487d2d0c5fa7c86582c106c5110a42d90b66e22f0b2f93e9beffba80d0fd0f7b32a8aad98b810ebfbc0d647592da0cf670512f9a409d18055d8785ecf8fefb55af5f8b4284be7f54d59794e73404f07a43d3c90b27ae440f3796872c0f93e96fbc4b4cbf5ad58ac6fc5ea01f2201608aebef0c2c603592005592c10044116c862816a8a7c7c7c5e2478c3f5234c8f21b1154841230e6319b91c06d2211f209f17359263c40b968d0dcb8605d2cf676504054b0fb0e56ab568185713578b7a8cc562fde09902335005c3ca87e903af7c14a42c5087e5e2a161fcf359c57af8be8f8705027d31acbcd56a459b4cf97c5661c80611abd54b07908d19b13044b195b7c3615e0f140f5a515087c3be1ea75032107ab18fbedef83e8ac3e209c3a11378432c4a8d7846565febd3f97ac2901a617d4394d230fcc162b1a6f8a0184531f2fdb0582c168b0d13c4582c8ae5e5c35a7d3e465f8fd1e7c3a25f0fb562c4a250b084445f93152513db1ff5629f7b01c6c7a24ebf8f05ae562003beeff3becffb28a53f28b85a7dabefbb22c4f1becf410aead06a79200be4a2f5b558dff7b1be6fd5fabeaff57d9f1cacef6bb1582c568b25c4cae359ad56463cf40182e2b55a2d6aa4b55ab5807c82a8cfe7e3d35ac901ba3e10044110044190822008822005bfeffb7ad0ac84f0224aa91b3c0c7d4386b0f156dfe7f91a3455aed60742f1a0d52a8afb185d11be62af1e9607b97a7a8c7c3ee12aa4d4c8ea034d80b302f220162bc87d5854c7a31e0f088a7a7c3f2cca3a0ac3f8c45814e88302bebcd52a67f5d1950b9ee785a18a85d92b4fe7079c149c112312f33428382386f3e540f97e563a4a203562a4275c03430812690152d0e7c7eaeb6179d0e7638445874e80650804b1d547575f48519066e5a373586fe01039200c12c359812b6781617c629ed7628134e61ae2412bfa83e54d88b8cf7745e831d0d56a40f8d12ff662391112e147411e9607b128cecb49c00409a220011a17f298072420c27f5ce90b12033d88f5d9bc1810b25e37397ef4830436c45674489832622b8a839383b3001d1c34e1ab47478e8e520ece0e1d0a087758118a6115e18e4845c863678a1e3c1250410f292ca8208ad0870508087ff870203f5e41807c3150489055ec3544089121de054430b8c0631fc580877a3d3c457a3e234532300245e8c5400d32008f68f0f239f2c5c01f9f55ec85e4c7db0089c73ea00dbc20a06f0c3a40e8c5c098d0f8443824648030c9902b49e22d51f2112d71225c31216aa20993a22646454e8c604e9ec0a03c8902454a140ea4f8140e9a884d398a211d252129255151aa42c54a95252b1d2c79573ac07285094b13539626a72c9f16a7272d504f51505ba2b86c61225c75e1e241172f1e7ce0e5cb07207c01034208606421cc647546ab24ad24bfacbdad76b3b9bd85b152619630a570e6c5e19bc36272598cce5b9f7bead44d51e9765422ecbc114418330209634a20814c0955644ca83ac18432279829838299145000400a6700f0a9706605155858a105165c6821002ebc10802560782106186488410032cc20001a66a8810625c2d5006af06c1800016cf86e20000e37ac72c041871c7c071d0ab0c3c743017ae0c1871e7ef0c181f82108203c2182580d210411437811441851847784112b248ef0d89704125e6ca544125fcc975022897015f3623945433e6f091d252442228e083feac57c3e9f1f5f8f9107093124844e15108cb55820cb83be1e222342cf0b42e7eb212a22043d26dcf38cf08ef090f092f09af098f868921a38fe799fe7b570c6f3bcef5b83873572f82e80e31f0e0f26741a6081173b40b0461b62190b66f011f3346a8c50b3001e70887e48a3c61b3e9e6d0905c230ce819847b178e1feada07c3e1e1aa02120cf89ce84de43c641f0cbf11ea0c762008ee779dff7b97b40e0e0b80750f0267f8ad69f9bae5660b028baa3d1f2f77dfd39f7d98792368737adf4feebf6d3a8edda5cae32d1716b6bd299942828331cce358afbdba6da34edd6f42f7e1cd6f47525e9b574eb334c0d07d9e8c6970a1fd1719731ee7141d7d2b7e56a562ed8897e356bd7d2d75697ffb63149f712cdf46934ffd45ffa19d73eff6d97b7d27b526d29eeb78529456569a263f925d574a4589b95026dfd369b44c7dcecf7493ed5cfe88e46f743d1b17cad9fc439476dd73e8dfe6c4bf1d64774aee27c68ddb5a49a8efaf1a63a579980f2dff6dbf0a6fbcc99f547ffded1e8b52399716de3283a262de56a9ea4ddb9e668bd96ee7b6292acd434cf1afd5b147d5c6d223f07f5ba7e26ef128ed2e57f1395128e7a1c46dae6596b9f7471d3be2746aafd13ceffc1c54da6a5629a158965dd1d8b874dd870f7809cb9e97a7b7d52bc29fd1237a13005d3665398a64c99e264cad0142153804cf9e1394c7f32f0a1302a447f32a01428030acb610a9401f5a1140644c17cf912931c8c117290c58f4f95c1a8b62deac98baf0c06d3175b2dfb8c492d2b7feb33ef7bca7027ce5e8b334ffd7947deb7e5c7bd6cbfde16473bb5145969a6596b325b5050d0eb7acbc9ec364f9c4c26cb057d142ca25c41f1a67f96b4527a4f4a0d308002a8a1608a7c09a354b9e3bb7b29ba7b29d9a6fa1b46c1e2ee3f40bb97f259dd70d3d5ca0d06236dfefbb6fc657d2bdaeabf5f85fc1cd49317375d6c757e2e545152e4df90b4bb7ff1d06805771f8f9268ad96a5fc8cb73e738e7eaef4a7bcd527ffbd16484bd5cf694c5e4c526d6f9bfe131393fe9bae56e8f8e3f3b72d82d574dfcff944a2e3c6f8893e3131d1ad7f572cb4525d6f95eef24f298d74eba7d1bfedfcdaca68f9a59dbab6dec86be9d37de4e6bf45651c6eb4a3c6fdb61953fd53f5d26ba9ae375ac3389bb8aaadac56dfa465593fef5e4af6b65b6f24cee69c2cd737495892bbfff01006e44e6174574af1a6bb975aade1c4012ec3597cca82aeadfb9e1817f4a6547e5cc6e1642b94c18510c5c3221e34ce2f02b55dbb332fa6657d93e24df5b634baef5bd2d66aa7a6e5ef9abd69af8bbb971e36190a8baa0c2d094109bdf5b6a9d66fee6ea5fa71b9ea13dba9cf4ebb3e6de7fa379d475a509322dc71145f8a420e6348795cbd34474f283a8fe49231c9b82488fc1293413f65779db2f94b99aea65965baca6ee4ae34d99319d77010ce864cc6b853d89798a46fd2ea8dac9fef49cbcfbfef5398596ffe7dad69da9a7e5bcdc972f56fb2fd5a4b917f2365fbc45f6272bfa6b2b5cf3999695aa97d7fea65b76d7532a9ec01c0ddab42a14c0aee1e0965aaaacca040c68cbb37a68cbb7782bb47668c092578644a70f748f0c6d8b48a0cb183851934eeee1d8800cb147fd99866452a2de692df342bd2b5369bb3ecfb534a2f55b7be5bb6ae26762a5fead6276d62ec84c34d17db8d999ebcd59a666c6b6bfeb6b72eb66b37e662a72e4692ca15f74fb5bc9fbff64fa5696f4836cbf43647fdae1b6a6b8b25ffc6a7187bb76cd356d9e5df1b63c93826ad73364abfddf7c44736abdf5671f6b3537ef32cff4642d9b8fcd36ebf2531d46ee7ac49e5a74c8ca4f55f1c2e6f7d322b6933deda5ad14fdeafd5b2ecf24f6152dba4ddb67fad79b3e096707942e94dd6bc2f46dae553d7cf513557de9aab3c0df74cfa7f6f8bc361a41bf951a625ef89917664cd24c67d92dde6c94415b55febd8be8fdb55a728ad9fb4e55dcad99bcef8b6cf580e57b7a6d5a3ddb6a6dd5ba05aee1e0b74f756eedee7ee9ebbb7668e14b83b0ae658b366cd9a356bd6ac51e3811d41d07490f3317a18a2e6cb1743fec5131e8a5c98809167c1030748d8a20a0f9c8a2b2e807326e70c6f22072a649600c37d882132f8788115ba0e4faab0010d32ee0250535ac00010b0e22c2469428a32f3e0279c004a04d504463e8213aa31608059c0b59a2b72222079c271162c6dc1ba800baffd60860d178dfa4c86a812c86069c13fe04009ba0000f9866f21a10d1548f0a83be5a4e87abcb8c2af2ce14060e072e15598406181230120e1484e85788c0039073750a0044172e44f9caa8457c4c086177571802a3868428d139da06404272035e0496c38a091a901f27184391e20440a667c032a5e90522543e93f1a4041e29e1eae0102e698a3042430c28d5c1eab2f4178c07ba86c81238384294ea704e141024202fc020fc439c2a8220a1fd2c2075cfe00250fc281344e24b1011138102a1ec482a478c07d3c008c27b528a15b30fb3e1d01c2f00a74c24c816abe701e5d5c4089129070f8ce0f3e2230fb32858b2a8469962815e021990330a143180df88e146e74745140e83a70a0330001c305ae630510358cbcc0798e1a45e028536081aa1c243b6f728348154e0e618e38be38c36f1ca0c119392d3e54ddf400f5883aaa729b33150880074b44f8eb0d165a0841115ff82ba705193d607155b94810a2f000244bd0b8a83ee019c00808d0b85c5ef870ca41cd94960e26151376e4d0b49c3cd933709929ac0d14e172058f31ce3a018513725ab8d0b0848c24d1c1ce195560184dc810430b4f54813588a1c38e1826d0805a2e0f4e4a48e26068010d60304064ca8a0238802047192e61ac6c6040991fb43853b5a26db9917a5a4357421828e9e18703f44b811a2c3ab88d876f0237a87066800c0f9f106c48e0cc000c345f095ec430c48c32fcf3a2230401d040c1ec6392c20f09b373f6f5f8f184c36ecd14cf04407851c314aaf23090e3816c2799e24d41b48117b10d26bc1ebc0f5c5314a8f2ce7c30479014270c79627e7cd08d71a5cafbd265ca122c47428f8a042cf000a80683b784840278f174c6cce3712ae10c1a95175ed8442a031c68000f9e636004cc8d4995ab910304264dc8aa7c0c2a940082891639ff620b0c0930a2829167214512d59305d4a9d800174e78651879134d883184a7a5ca87180241075128e0e43a3479000910350b9fe1234de004151f67e18887324cdcc08497711d893064051e7c042b535ea43ce1e33a056084b89d271c572400739041430e5e63c283924106239fedc0438d15de08fd83333c2a2a6aa2f02d67beac41abf0c29d44708df0050b16f02b5130a9c1880788bc8a94595e8304261ce9450501243471c63910cdc05de08a0e7fd2a584264c200081177d41c68925e220c389ced470e4869b1c3c096e07270b8ce1858f2600f1443411c2375880181d3cd57082ff6c808010c4f18305ae01154e5c51039a13dc4836c1cc65c108efe9c00d0fa080c790d3a4d66e882e0bbfe00b03dc2813a5880f81010423527c013d481844620fe8d1c0812c4981104769ba0f2d1ae04511111db78096002f661b08f10a76c4c143842058701e13e08085a33796f84e1020284981dd719105256bbc1a21f0d0040490b141d7c37700600425eca0a1e33a7230233785180db88e021c608610379e3c670d288268c30584e76c500230644fe538ba015356d834f11b2ca6b2448004a5df14b111b1c11983dba83087084a700002fe82c30711e05c89c25f622680a68808d6b88b0d170604504214eeda6df1441ab013b8eb75844d50ad89b776805541f24410de82f58862c464c7591bc8c0136b5838c1596592fc80033480b384b0c0608ae6c241319676590c2b1c1c404e052b601839a8a50d204168910107431a84a4b921f45505766e5d2c5184af08a0040514e078e02b9a15b80700b1c65742277c51f2a307ffd630d930841948f8370136b42461060eff8650b23519f1c23f3217541942c60dfe79e172850f4e49f8c7448b36c41cac7f3dbce814c1f1807b2610634a097e28710f034a413ca00136f7a6085341014258e25e0f443d7007d8c1bd332b40a14651927b62b8ac50822139ee7d31c104213ca8dca3e20606d248d9b9b7e407d7132ad8dce3d14ca0f161cabdb08b0a42ca18c33d9782c50c104bdc49f00309317a38701f230d921305d871ff029b2957f0e29ec5049608634607ee54b08145911cda706fe20164f0e8c47d0825286e7c2b771d685ef8a2729fa1c72b19e1e1ce02540c60b2acdccbf85072021408e13e4297d3070514e1be73e4b0dd10c51d37f40517ae22dc6b54907152821ddc674558808b5c18f70f4a108ad0e4343194c6b9a66fe6ee4e1e1e4912439f949f4f9bad5d4c525ae94fd51bd594d58618fe73f34aa0e1f6e4062ddc2b4107a327554adcbc12361005ec880b508fcc4f1a2713f821c22303001b8ce038c0cd2333471646432870c6ab72c1a9484a4f149e09517e349931208d77828f344cc8a489c22bc34345c4030ad47866ac0d4bd07031f25090438d1230aca01e00cea049d6fcf0f25688319184522b9e0b47c218e240c9140f06285e42217441e3d1b0c3692c41270bef865a55900eb8303c1ed8b00244c5088c3c22e60863030ee8f0c273228d131239a060f0ac20c11b4882e03cf022a0059a1f27ba2a0f8c149ce1e3010f67786e6c21e288ce89303eb10a2b1ad470459a6fc9076832388006a2f840a02167c4026fdcbe17901051c4043aac7c1170322bc2834e5901f142448f122546ab290578c1a305ca6c8506541a9aa372063cc930c2e0052726583d51682e50e28334ac36c4f048a09aa2777fe562abf543d9f2ac17e3ecd32aedd66cb35d2b75f34fe12af96bfad61c7571d3eec6a4aed5f76fe4b6529fdb45eda87076fa421f23ee9ee3ee373e3d7c42164280c14f664139c2dd3d0f244001267e80f1450ceefe79c93d1df1458f352570f78f081f8a70e231451645eeee0dc101047a7a9082e2e6ee1e07327cc902830a3a50e1ee1e0356642ad0810544b8b9fb0780223d0af0810cb4d8c2dd3d032841731d2a84f0e1eeab3646108213483880002370f70f8b120765b070848513b8fbaa8217bc50630036504101776fdd40c31b9e05602998e2ee1e196dc430668e017ca0c8dd411c662e02dcb6080386fb97f8067d89afbbbb77640677271281f727376871b331ce3a8f34a12ff115ba96e6ee42a8c119552fafaaca84aa9b2af7aa2affaa7c55e52a54390b2cb8dfb88dbb9f715fa1ca6daaaa4ca8aa62a1cabdaa6a85aa133c03203cf0bcd048933baedda52f6bb6e5b66c5db1d86def9208d6acd75251da99b95c4cd79b53d4932ca71f57ef266b0e87916030f24b4c4e2959724a91e5cf51559c14894d5a36ebc65b4655715415475a7d6dde542f7b5d6952942c39a1e477a2aa387ca3703d61f23b69184c8ac4e69194f273399bb786e174d541c9e2218f936ff273c624ddf86a4a6b96526de9dfa2e893992c63d9acfc9bb6d7d668fed2b4e791cea3de16674ddc6fbd73f79e7c6bec846b776997f1aee5ad4c66cd9fcb62565cc64de5ae3ae9f7829b16e0ee57dc1decc06d75531b8662d179dcd7e2dc9271282809f9608ce813a3f38b14287a579dfe6b32bdab39adab89742377cd699ba47535c92efb66b3afabc2dd3ff010031a4c7bd2928c49462541b8766dd09b95d4d766b5beedeaa4f36856b382e0e105398ce37864bf09b4eb671bddb8898e9a9ab59a27695692deb6d551dbb5e53de9e74abf0bccfca9376555df9a374ce7f1a6ebeda6eb8dc81bee3be39ba618ef9bae370a7b9d7fea33752f7f2abffea7ff4c85d4160783e9fb5278c3605a9fbb6ea8276f35ebd1c556e7e702e5ee4b56623c5eb9b4df34ab130e43fdc54d51ee4ea99090d0ef9ab7a69d5a88bc3b1d94cb5517b4ffbea5e1b008414c868476dbe69a2bffa6ebed9e30d8ce38973bb5ec7135d7de5633de1a067b9cddf7cce1101e9cee4a7fd7ada9bb2be94479380448e7b1bcbfa9fe89bb87e0a1102afcb7fe2bcbbfad0c87734d467e4e0b913f25b4847c1a49d6222325342743b4a2225a7d2323263f4454abfdd00f91b526444e6aff4542e4d6671612c283bbc7dc5d5f6c37f9a692c6b926779fc241fed24ee1ec54774d72f72858e8f772f7206b0441836ac8c756a39a624a58b264397a52aaa214d5b4b47454a569e9290a8a4629452165c182654bd393d21295a5a826a5a4251a85a4d4c151162c31a5a724a5a8d89312d3962625232a515b94909a94b66851828a5282226d7edcd9e4c424b6f4381b8514b5e57136aa7c11ac59f3dfaff226cd6acbc5766dde504e4c47417ab8fb17da9465292a86b484c5c92906e40cf72f146989ca12929213545094074a514d59908044e08af23779e69fb2dbe26cce012102481021901cdcfdc78782f94295a2625c969e9c1e67a37ed7242af74dd97d53f6bfdfcce1307997cc4ada8b6f252f6e622a6d16772f2a2d2e6d16dbb5f9cb9c653f5496fc5359f297f59fbec44cb61af534dcd964ab51eedee4dd9dc9ef1dedeaf095ba5b6c35ca76edb515f7d9dd85ca2f31f965ddd5dd898c6a7b8bbb8fb6baff342b17775f72b1dd56e39cbb2b49e2eee5b5523f427717bab8c9ddc7a0c761285f4d0940081b511039c8c5b54309a50deec2401537ac983272af0cbe19c0121470971b3c2c8d300a59e36b44e0ac1a58285aa2c600f7a810d70ea008ff704f96e860e7e3ac1a560d58a20113677d6044d20898a8fb8f0b090b1b487197e2428b2d6a80e15e03c8c0e044cb8cbb801c81450f736471d0660133c81630e5ae14b280018438d6784b0b4e8d92c51a7757fd36151ef817022b63970a38714781115560a6a4c1a24dba18038a35ee728205c90394299c65860f330fcd8f7b44f83ca9e056e12b262d7878a1fac1bf29381d1c805be32dddc203a6ec74be9240129a55d593af9c9ca6907440046fe13083064e90c08b7f5128b102ce41c45d28d0204415961ddcb546083954913373ff2288395ef8a183b7841ce940070f4c38d0b559b3be0856ef13ef2a160ec29dedaea66ae62e9ccb4107a378eee2c12b59a2e7f21d1ecb024f87f7520216f96a06f002d7f77ab1c095e7b98f6ff4cf3d2d20079ebb9eb07c47cb73cff3582ca20fe773cff3569f15cf73799fe7ad5a227cdee7b93e0b9e3cafe5b9fcf374c6d5d76af9dc500ffc3e10060ff4bcefc66be3f36c9ce581dfea3d2fcce779df6bcb27e4f3be16f87921f83caff5795a3c1f9eb7fa5e0d60ad5c3a3c0b3caf82d7cad3c1fbbcd6e779df4ae67de08d0dd197e34979792c23ab289eebfbc024ab9beff37e7634f1460093e05059397d0efa178407ae7c40d6e7ad9c45833786e79f8761e89e7ba0f7796a78af0f89e77dacef5b79de124fc87b7939dff77d2d24efc8f7819f37b41a7180bc34f07476827c03f0581f100ff4569e7f4e1fbdf140ff6ccbe9cc7361e53c0df07c0a16dfca6b79a0cbd3f156367cdf0bb6f28c56def781f4cbb1f1f1589ee71a02936030e47d37add7e7f23c98f77d1b58b53c9b0f5c4d793ddfe979ab20cf06e7f35e2eef9bf25df05df01159f9cabd1d5eebfb3c1d305c811f90effbbc9607d63c1b231e0eebf3589e8e2221df920bc7e6f368af1cd7e769f15e9eb7fa3cd0b3e211f99c7c433e9ccff5b1bc95f7791fcee702bd156be50543421f3a3a3d425146868bb212b8285be3e26c878bb31f2ece885c9c39b9389bb938d3b9380b808bb302b83863c2c5191a1767127071c60117676a5c9ca5c0c56a818bb5888b55898b758a8bf58a8bb58b8b9574b16a176b0a2ed6195cac44b8589970b1a2213244489070274890ef0499e23b41b804a9b9fbcb46c78d8e17d0452f392e7af9e1a297222e7a0172d10bcc452f4a2e7a6972d18b072e7aa9b9e885ca452f29b8e8e50517bd10c0452f45b8e8050acfd90152e53b4074be03a405df013283ef00e9c1778024e1ee3a383b3b7038e3210e34788803101ee280000f7170000e5e7888431a0f7108818739b88739ec789843110f7320f230072477b7e0a54347c7cd6b473cc377c414f8ce8e10dfd929e23b3b3fbeb353e43b3b567c67678befecd07c67e7face4e09beb3d382efece0e03b3b41f8cece12beb32385efec2cc077762010eee84143141ed2e0000f69c88087348ce1210d6b7848031c1ed6c0f2b0061e1ed650c4c31a843cace1098e0d8f9d1c23dfc9a9e23b394fbe93637d2727fb4e8e19dfc91980efe420b1c3fdc78d0e1e54f4e10a226a4972510b948b5a4c17b59871518b005cd4a2838b5a9a70518b025cd4420117b594e1a296375c7ceae1e213918b4f4a2e3e7171f1e9baf884828b4f33b8f834848b4f50b8f874858b4f5db8f8f4858b4f1e70f129042e42b180fca88085325c64210e175bd871b105212eb630e4ee366278d3bac9e942878b5dfc70b18b222e7641e46217555ceca28b8b5d8071b10b9b8b5d8871b18b115cece28c8b5d04c0c52e04e062173ab8d8c5102e76f1848b5d38c0dd71c257053b3b3a628e4e8886150fd180f2100dd24334b68768a0e0211a347888c60e1ea2818487683ce1211a5a788806181ea231020fd158e3611a403c4c63030fd328f2308d240fd370f2308d99876988f1300d321ea6c1828769d8e0611a41789846123737e08e1cf10411b87842095c3c01052e9671b95886878b658ab85826c8c532442e963172b10c928b659e5c2c535d2c835d2ca373b14c1917cbb4e0621902b858c687968f17a8f0f0050778f8c2161ebe90c6c317d670771d3d6e76585e7c8765f31d96f61d16007c872583efb076f01d16112f177a78e802110f5df0f1d085200f5d60e212b7b8e2e216d5c52d762e6e11838b5bd8e0e21641b8b845133a3737ad1c7104352e8e00878b633e17c7ec70718c0f17c7f0b83806c8c5314d5c1c93e4e2982617c740b938c68b8b63662e8e315d1c73bae3d8bc76a060e03b5090f80e1426be0325e63b503af01d285b7c070a18df81f2ee9ec3c383c4a07818b3e261ac8b87b1301ec600e061ac079b1cb101402e3680898b0d90e26203aab8d8002d2e36008cbbbf5eee363a44295070510a175c9462002e4a510017a510c245299670518a2a5c94620b77efe13f2a403dac00918715e8c0c30ad05e2b8400063a81266a8490a8d2dc3059c08f9a963339f8f0717777cd016483bbfb8e009039e3ee3e840511d2ac59f317ffd7644a5e15544c910029bc6789300ad77934cd271100841705029e78e2092928b2701b28c078d0dbbc83b874a1a3edce92b22c3d2939c994aac49eb24441c9a2b06c8951514a82c1727a0645df56a5ee792369f974dcd12815a48d9bcad7fdfda7ba6bd2efba93cadfb8a98673c58271d4a6fa7d3152598f5c8c15039c711f67abf9960bd25e020c50827b5036cd7a84136fb048276208aa4eff547070e215549af646864d04e167d0d6b5f6fba4d9bc77accab479e8ee3226d494f8dec0040c486552f91b731162d49a56690d676ca7eaed69f4379eda3a67fe6dfcd9d17cca5c6b146baae968bb16db2ea6e34dd75b8c8eb7df980bc6517a772b1314b08912403ce58b60931841120a4802091f7f7ce88f0fd57d936f20092cf507210f930091c000121170172a4205891328d535a764c9d2eb5a0ddb5e23f1b27da6478441614fd69c3ec201eef4c17ca1528228982ff4733bbce9085ccd1141c4d027f74d992c4a141a8587de376532234e30a274f73781a8b6541bf1b9eb46fd9a6a7be2923eade633661bffbea675679a95ea53dfbfaff18da4f9c45ee8a8f5b96baeb95c4582a4eafe888e9a8a036d8b10f1fa4244263384156f8c33caef04429c719caeba7de456a41b1d6d9646f1aeb67c1cce527de44d5a8dca475493d8bc5f969696718d6a4bafc566ad7c3deafcfb7135d71cdddd8ad35b6c43667dadaf7d7dca64667d0dd31ac774e44869beadd1bf6d5cc6356d561a366db47c6d29dd991796c3597a71d67d6eb7ebc5b7f699ea2bdd6133f7a5ae369dc7cf35f7390783e9f0b087325f62a4f16963a67f23a9e829e69f70ed2ee174d5c1606f9e2566c2e9dac5490b1e8c0c50001378a9c10ca7ae9fb7b7410188dc33093b5c2067964a36ab754f5599469df1262b95aa6449794f9a3f77a464c9eea5663f3f148802f9f8d4f006944867d6b2acf4568ac9f29bb45963b849f754308eca5fe62cb52cfbcca2df66bceb52fef23e958d9b1e67a374180bc6510b88000e2f57c2d96a22c51e57c93f0af34fbba5376d0d0349e58abb91f78fe898df29a9ee9a34669cb33b7fee5a264a55cdaa318e665cabb4fc769c5d3ba3aa4c33fd3a6ce6cfe54e6a566cbf466d75df27f3e7e8ae4ed726e56878d35dae96b4b5bf4fc3367f8eeeaa3992d534fff6b7a829c6f2cbdfd18d6f4977369374fcb2e234dd35d1ec74972ed6f59fee16a47f7a2a4e4e9fcf5d719f9d76d54ae9706c9a066848a26599818c7a8ae12242d10c4453ae8305dcbd04016cffa41040e841210313ee3a94e18cbb8fe6b87ba90de8ac5cfa2b33fab7dd4b5d1dae7a26bb78762373fd6bf5a92dad371d2d77eea57e4aef5e4a1fd15d5e53dbadb79bdda4c5e18bc937f3974bbfab25b1d631dcb4fb2b7af75770d391f085ecb69b6f32a7e285efbe08f83315aa2fef520070eeeec2085c9861bc27ce9d52189bf46f51b18d9bf25f5d968d9b5e6fb94b1b37edfbb7e2f23bf5f0705a00c2dd6b184f27ca9701c0177a9f4f28d3ac485a7f2ecb98cbe1b3f37d2e2fa80a095fc28211cf8a15b058817a73783f4c48e107ef042e0498c0a4822f881745dc9dc943326276e009cfa8f11d15ceb91d6766d079a491e0eebbbf62cbb12b4e001881cee3eea58478f67ddcc50e819cced92d02a0072e4fed1545e1a6b5091d200510dc69d50be241e122582d82d594fe94dd41543b482827a3da55a6fbfb39056688c0e9aa23c778688647ccc657f7170663b2b979f2b4f1cdb3c43819add63ee746f2a97eeabe8c6a07bd2dd71b12aedd258d9d6edb76c93867a5ac24954529eae97fe3a9d2c432b36a5bca64af710ee7dc3d339695b9eaea2edb1cd5cb76b2d28410689bd54964b25c95e92a339fbc95a495517e69a768eefec3dde570f72cee1dc0ddbdf2e9cebc5d4ba38672756b9cddbb97ca670e67ab49fee75afeda8bf09f6bdae284486d7125249570848434db0b220c770aa871b754ee4454b80709c15d47c8033d9c2f4410a3830d170fc7784182061437e93c925fe26bf3f642afc7aec434fd4487cda751d9b57a6b6c45ffb53b87fb6b83e89fd7ee8b49fa64b773b820bacbfb3ecd616cd28bddc930c3dd87689428b403236cd179cc7fef99938c433058fefd391725ca58335fbb7b30fc7801f40260d2ea3e71ee9e0b345a4b0884225819afcd591d9a703747a5b82a76402cb14b9231fdfc53621c257d28addc36eb38e04e3ca4327f9f544b5419bc08f65a9c2e8cddb6a6d54de1ee473cd4cd4033aee9a0cf3937743aa478380543fe6b71e114964c472ae114f8fbcce130199e599c30c0f699dbe76ddbf0c41285d2b1fc6c4f3164a5b5b367cf41cb3769f53ea53b8c4dc5487321ec5b19f43947a376583fb35208676148c9994217a1612029108549a114262b479ba5e9253da4fc325752db9202510e804c327320cc298cb66b6fba9a9a0295f7c435aaf3088389a180bb0f7928c6875dcde69fa278973785c9caf277ae1bca842abf0c736ae8d717e7f4677a6d7e27aaca14f526ad72b15daaca346afa5275eff6b5414f5e0ca4f7f91b4f51dbddbba0fc4e6599abee88eaa7af51f356fb7c5e5b6d957ea678c360b32ff4fe943e546f186cf685d69668d55495a96add5fa02cbabf663dc53ccd585baaef53d9d6274e464fbc3be98977b4b644476db505ba4b74d457874baa3759f359526d6fa5c553555345e5bf96e969d44772b8dffb75ae9a4f3513ba757db33e0e88c99b395ca5aa7f8b2a4d7cd39f6f514723544ffebd9fd64375bdb6f6e514ce395c7d5d6f495f947136a3d54ed1fd1a898e6fd2acae5f2be9d36cddfbc891fc37f235dd996625ed2d57b30defdc2e93b4ee23f7c926bf56ab3463bac9cfe19b5f9f41789f95de5cc5ed5e0af61b5f4a85755174bcf8a4b66bdfb4371d9de959141df5dd42c75d29ce34cb5bc35f31b9ab398a3f1f79dd6f7b8ad15074ac7457d376ada6f5efec5a3a9b7d9efd0001f9cc806274fcfcb7b2fed677898e3a6c6a9bab4c3aff14a6dbeeaae9cecce1acde96026d5d73f4c97e4dcbaf541fd9f5b681663f3ef7cd71465f9ffb48173a0e01dd2c74dcf5c949abb634f7a4b65d8bc39b3e8de24df5e53df1d3ec0bd5968299cdbee89d79b3bc2dd75d7eb6fa934cb392b4bc95a4e3c5b9f3fc530c05ca1848cf28ae7ede3a77d25be9aeff64263f3f8eb45be36babb6a4362013e34d6b65d097e6597b7de65fa23b3357a52eb68bb354fd2e9f9d4cf3a4f2e79f5a9276d569e37b2b92d67fb7ed72234f268ca3a06a56b25d9cbb986645d2911bdf74ae5e28dbb55a5b268ba39de5ebb069c6366e32cd4a6586048ed27d9502c0c26cc954107931ed6ec9556762ec443ba1f643e5306eca65d16fe2271363a7fcf72e99183b955fe6aafba7ff4ca5bcf82848489b18e7645756c4dd7375e3ab7154b9a4dfc64c249d5faa6e285db352b984a3cad7b5cf4eba66a55a96adef92be18a966a5bb74d3d584cab896e5499b9d70bf9732a6a243305f5c6b96f54b0a547ea61bd3fc5219ebb014894da120770ff2108c096688decf61301f086c80c001770aabbb96753f8e0251204f05af052077cf04ad71eeca721aea6302fb86dcdd8b8720d8b8eb208d735748ef70b833a944dafd95fdfa9f742e577550faa9fcb2fc1975cd5220f2b9d0f281cafad98c6ddca4f56fcc0587f7ad3727d94fe92a1ba01ce10708f88008779fe2432fb696eed8aec4347acf1a0dbab8c9abc20f2c70f29e3526e3505090c5e9aafbf1d9ed1cee65da8e5264dde3a62af5e36a9396959fe57118aac4b88a69c6ca9fb24ffe53e885ee6836363637e5d32ff46f5136363637509cb68c5a9b39a08c6bdb6aac6b19db9c8b518fc9582bd578974b5c6652a432d6ed6f2abfbc273e82f2a7989acb923f97abb9cb8ce27ddb676c46cb5bebeb2850c654e858e9eb59fe5d8bcd3e9f24599fa65fe3dba6ba52a0197eda54df34fb5d7176eba75a7fb6e19cab74bf8de24d4d5b6bb4a4ed72d5fac98d2fcdbfab49ffb6819e3c4b9c73b6ea28ad5fdad22c4b8bdb9937db896ba7d5f47537b2daaebdd7da3e46475aded7146f2a85a93eb22dbd91f6a6a3f9776df757ac94bf2b69cddc9984e361d400a27451d89dc27c288551dbb54f140c857d110a1acb2fff2f7d1add55dcd66798b2d2181defeea5e8678cd3b66b6fae5dbec44d4bb67a0475a604a81c42dda066545bdd5f0a446d9546f1be96525a3e982f3487180aa0868271f75a70f758f054f0ce3ccde1ee545b2cda568fe8135ba595b6746f044f51dc7d2c5f0c1d6d954677556756baf589bb3f656d5b5baaff12d37490b6559ab6260cb6b53565da5669b2500b07dcb4b5b4f8a6a395da2e696dd8a4ee4ee5a11610f49f5adb1bf99beefababe99a34fee3fedf33965f5fdda97b4326182d5d21264ccb852a730ac1494131a77ff1a7d6262ca389c530e7ed3678e3e313189c185061e308ef93535eb7971497514e3306ed76ca92dd04c8ad109857c2e359cb1a69feb0e67b159ebef9a1f775e253a6e6c6afa38bb6979ad548c8e7a679cfbbc7b29aacb8cc9cf949626bee593ee72c5d99cb35d6bd66a1836654f310f7b5aa555006480bb931e4a796ac279c8e484dbd8d8dc8c363636373f423a484893b80cbab6e284b01d474de2524808a6737bdf7cc0e82f1487b73e916c6c6c6e64b251976f9a1569772b13c6519576ea5dce2c31ce44da412643132770318be2b5a23796c0506584a2260f9dc8b0883263de00983061019fa6ffbc946f9af68aee9bcadfb5cc6df9122369fd2479425dc9c07dec000519e7acb4e4610740b8fbb8a3d1a7d15b499a3f47f7ad36d729fa1723996645723262020535da4ab754c551bc69fe32637aed896995eaf095da965eaceb3fd191fc9cae26f925ddedfc399c73b467575cc6548c9868dd435633f7595b6ac4040a8a96bfebb559fde5a88fecaf426949e34be1a96be9ad24d575d712df74b5b275b6b5adbf34b10fcdfd4be128131d7127ce5255dca8b5ae340ad31ad02b7ca4175bfab7d9e7dbc6b5bb14dbf8d29aa53fa50f7d6d562a749c295972cea8b67436a3bbde36c59a96678ea263f9f773b97aa3e7d6b586e96c369b7dc61b8a8e5b63933e0d47f579db369bcd9b3e8d9aa6d5515bb31b5f8aa9feee8bc99abd7fdb74f6faa4afa3354b5f9f957c1a9dfd507d79978060b0289f19fddc6ed37adbfaf36f6bdec8fdb51ac6694b6dd74a9135efb77953fdb6fb536f5a930cda379b35a9d661d3ac54db5b141df17d129bf46d95b45bdb6b71f9f797a6190b578765b8fa5bb65fcb642fc3e5a9c5ec828725b744312110438628a614f3843940613f35a5bf335e1f10718ebb2ba1484a13e72a798af95246fe9490f65ef8bce4bc38e2fe49e02ab96f8b532a31d2d23be17e5b3a56303318ac66fe43d1516b4bf749a3d79eb65cb1e05dee44a8e498b3b129af54cd6fbb18cbb63636f8a62b6f2d1f77dad0efa269362a574ab1aeafabcddbb4b5963bf5b6f6da6de96c47abd1ad31d216b6eadd54fddb94121dcb92ea4a6b56a73f8916d1389395de1387c364d54d615aa51b5f3a96bb343fef4c2b423569f9fbcdba2b2d2fd171091de9a8848e48744c4261356c4d13933f4f5e7c295069653e375d6f96becd9b522448487bf1c617e39020a1556fa01d8dfe6d9bf55a9bb555b35631cd8a83aad5cea37de66e3bf749a65962a42f31924e2aa3f69bf6d625632afbccdd25a827d2e67094d2b5f586849b4cf3a4929431d25d2aa38ca84431d94eda27956fef273d796bdef7cf7aa352da1667b1ecfc53ff7497c6a0cf3224d76314121a4537a0982ee1ad31be91b4ac345a4fa09d69c713ef6805dae7111d3d8831c1604d929cf5f3b64d929c545b1caed666651d957c316eaba9d051ef74c6f93fd0549569e3a6376975e7f26fe4894dd36ea81d15cee52795d7e20e76a619d37d13aedc0ff5e6f9a4fba62e11e8c2c4c58d25d3ac485495699c5155a6d99b38e7cf69aaca649ab111cbd3165aa919baba6bb6d18bafc6fd97678ddedf516aa3f8a6fbdc6ee39deddff47d1cddddfae689f74f05fdc5d4acb67e50fedcb9f357f26be69969d2121285a2e2b4b4dfcbc64d3bd37cb25d5b2ad55d7395ab63e56f6db16cdca471bf97ccbcc5767f5b4dfb2ee58dedaa99d336a9c4b84a979bae56dea455bc9fcbc64db17a64bbf6fe94adb27153d393ce4f83884e1470c2c2c90ad1e9004e41884e40388d426140b4e695e194e41fbeaec99f326d4def6b6b5632eb59da29bba54cfa7878728011b35bf29e98fee2268c9f9c3c3756529e54053c046207aba52786942859a2ab4fe512d59757ebc72200bcdf5e5bde4ad3b67cf3dcf771b7caaec56176a65965bfebd49bffa4ddf8caf67d9ceca7acedb3dd52a4952acb2ad32736cfdc99497dcfb26233777e8949d3aca4aee5ef1b79e2299cade50b097959f0c690103b7811820086b20c93c8c8156478f192c6dd573a579c33775f5d40e5f396086854a102549859d02447142d2531a85a491c54dd88415e8460c498c921012bb45c85850e0cee5e1099fb1702980f0423d18a189d472128f2a764e5c3609f3129cb69a95c10ee06d5f29458850cf7efcb0b5e9a0fbc78f075f9b8b8fbce7a678854ae70779dc71c2e7285ea8b4d133b5524291263d9d6cc4195b996b76ea8df780a069371d992a4757512937444241744a4a224aaca34eeee9333faaff3efccfc5398de4a81b0366374acd5b08931d2182445d61cb575a5328e3b73df273fd37aa3d5762d05caddaac36128bc2feec4587e632ea369ebbe4f75b6625e5ff3ac690d54c33be3dbd36af8b18c6535ebcfaee17d1fb7c97a95c68a4dbcabfaa7ccd7b8bfed29bccdbdad492b7ddad20446f3d4a6b9edcfb63e7a8ae21cce4e4e331a4433a64f6633255dbb945b572c331a4461fb7158b775f97febb67fd3edcc52b7b3d570c6a4d604a81a233f7931b9b48fdc99121d69f7a4f71655ab54a540f43fd7be56f5eea528d0d7ea54141def163afef8e830962326282bb8a97cbc9f0beefc8b9b366ec24ddf943f97db62bb387779d24ea5d04b551a853d65797a52a2484b55b260d1e57d9ba938e1b5c0840c77a7957a669830e1f92032a1721f77884ca08ca5a5e9884473e0fcb9dc8fb8484445d21a221094b84844dd475bcee0640cdee2c5c50d606711387c8002359ec64712fb16a21206f82c6a466b67eee24e5d6ad3d6fd49356f2a25b39a6f052a3feed41723e576aff5934acf830ae28fa954774dc2b51b8583192949a2c3d72693ddf6cbf096555d92ffb6aaf5e364b693f6a59192244a5eaa9a34a94a922fd584891299927ad3b29baeb7276f95edd732bc652ffb8bb7a6e1f0d6e1f2b36cd7de94e96afbafc9f096ed9bcdbeae6299b632d9d5e19bae3799b6d9acaff799bbf677ddd56ed3ac3625667d9dc4acab28442053e7f149f9629ee81367b2e2e80cccd47dd99722b39b9cfe9d619f7341bb52b6db7667cc24139150e12fdb6d9b71988a93c9b6ae35b3de8cf74fe1cedfd5f6e697f7d45b5b538aac264de6791f8ffbb883e0e29137968c4a928c4341578733eda6ebcd027b3582149aaba44cf7d71b722f26664085bb5746ce4a337a8f84a399eb538ca340206ac085fb78b1d5a7495ebb6ded4247ba2bdd57d4c0080d78a074a741d5b559adc1cc5dd4c0c8dd450d76dc8dc62441e44f9966c569318359064614c8dd5b2e66e043cc20c7c81cee0e44a94a0ba39534e3702e178d84e114a6716f7ef95f9ad8caae8fcb8fcb1fa5fb262e195f9bee9e99aa32514d475cfea8f14b4caf05c2f94530cd9a7ba99adba273a76cc4fbc7a7bcf8c876edc65c46fd1b4f9176df6ab3365a77b4ba77ddd5f2e223a47147ea237f73a73e127480714675a54a546250b9f33449aaa740e5c5e443d1b1e24ead2bdd99db92f76f2533a6650efa916dfd3459c5b92d74fcd9da874add9a736790a634466177898ee5ffc65c70d3ee5626fd1b73d1612cd509476dc9475b9f4cb92d4f4875d7a4df98cbb54fe2902cc4214cb88f60be50994c1c8254fe99b740fd99a5b4d5a9441282852b45e97ded587e06434eee9b95866daef7494da1e89804a6ab619db18e3ead029d5a5bd95012ba35c6d1bd8792e0bda3d53069f7e36edbe659fe9959f5adb3fb393ca3b7da5d7310de417a2c2d3d62620c4ad9f2a76cfeb2b4d45a4b8fc44cb3e69f52a29d9afe8d02d5dc89b38ddec8aa44c7dc897395af3d0e9354dbfa64cdbafc396cea2fcf7b8e26c63d8fab3629dd436bf8cc659c4d8c73380cde26363176c251da76b1f9473bd33ca9ec6e0c1f4969fbb471d3c64db66b77386ffd49394bdeb8e97e2ecbc64d5a5355a6b7dd7a2bd24f552b1b37e9c7494a493b53eb37f33b55bb7375422abf2c632355652233fe146e5756243a8e74e3a6b734729ce9f09dcd92d00da89e0599f54baa93222b8db630eaba504d9d5edf6cd60ac651e597a749be49de2d4f92a73e731aa8fcfc3977d7f1c8f880eb74eeaee4ee11f1395ac3f4f5e7b28cd9ae2d317995caa5fcef4413738c728af889ffe29c9576db9af5d4a2c3587ed7a412e32a5787ef953769758b8e5155266d2bcf0ea82ad3c576d7a76d71164b69bea50283ed70ed2e5dc0bde8b1637f3ee428815781cf480aa23802538457c3cd8a1e2b7820031e2b42308128f643957fe57629bffc9c549a59ca2f6d75aa59497c69e1ee321955c56da1aa4cf9a576b72ad1f1a7ea0dcc175a67b66b67d91ac95ffba4fb53dbde13d37d62a09d59b37488e20db4a3d15d6ff868fc9a95f23be951876f9ace76eeccb42ab35d4b9ee5e719fd4a9f6649a819f95cca9ffd6c9631ae6adbadfdbe35d7d336bbeddc97b2202a85cec61f97aaf9e54ccbca1ff3dbcac70235a34033aa2fcdba7194ded7c2664034887e99594c858e3b7aed98f1e7988c3f3324549569e6437f664832a632f3a1fabeeeefd03834164d69bbefefcc7a2bb5edda7246e2fbd987eadca969add5309899ee2f0c96df566f6522ef4ed36b75a59d9896b6e66ee489d3959ae6ebea4d3fd151df9ad374ac396ffd379bbdd5fc407a06863e91d9ae7dd9971985697d32d18262f59e3f3e54e34e325bb2ccb8ee68b4ea6ebaceccf2d65b26f363a6a1a1da121d1f97bb7de4d6ac54f3436121fba3675787674d3e74d4d966697496df29e3a81998d9ec4b352dce6a3ae6af2d95745baa976efabef92595324f1ad561d3dcd12ad53847a544c719989a95806810ad59e9cb8ce6ff817dcd4f6756a29c9492645e9eb244412d59998d60661a067bb2d6f2ef2ff9e9583e59f3e7ba2d05ca785b0da5c337a09a9f966fd6fc3b303318cc342bd2eccbce2c7f77ebcedcba62a1637e5cc6545b186c46afc59f31131d731547b59dfdf88c3f3e7af75230d8cc9a399ceeb30dfa4c77401bdf5aed4b4ccb16e86737dbbd946c36f536efcfda666b3fd75c5a3033aafab76dd26ad0c537577137bd3381b22df76b8bc3e4c89cfb795abdb7ea7cae74a16399abed33c5b56b7be84ff9a70fbe14a8b65471b61587c3e4d32ea6e99e1e5abe6956bdff6efda4cd5b7fa6f9c4359d0f35eb8d3e79f3e77ea7ebd3cf47767fdb9a92cf65898e527467d22c494bbdbb313ae2ecc524d5fbd69c1d6fba6ea88d9b8eb2ad4e171fe9cf98093749ddf386244562f36ec14d547054cd4ab52ce58d416ddc74dbb94fdae95b7350b66b97be1e95f5c834ed0d496ba41da2c13ae38d6b29d57f061a7a4655d6569f0175869433a09861821faa2d50f93ef43735a30a3372a035bf19db8c29666c60061133723405d259e3dca5b4fc0d822940d082ebf0a59a4661b319bd5689cb1254931215a5a4b17e2e57631778088271acf9a16a7ed96b58195a5051861265cc8037fda1d7d26b6906e60f0fcb3075191c543347a3902187b364cc408600dc69143214400d05f3e5068b6fc903bed50616a020021068a08a3044f02b7a946002d896d21381980fc75c838b18b450830b2118101ae2bd207073772c4b3bb2da1e8bf6606e029dc7a0a08b4d0fc5f02f314e3ffed24e6d2757e3815b7805b4d8c0e3e113f0882ef00037c3cbc019303ace4330a090dd6ae66460fc70778f7a4b55996a98aa32d1c7d9a8f167b773b8af9bce481a4493cc7ce88c9cd16dbfd27476cae66d5a5aa5e1a7291dc58eea789652a4cd34ac292e631ae776e6d6d5047a9cdd77fc793d9b51bc4fbcf301ba38bf083b6d6f7f6aa1e3c5f4d637cb5b6bdef7b7a53a6c858eb9936e7c81a4347dda8d7cf25a283afee84cc378d3abebf8a3f5a1e553d91ad5554777399cf73531ee326b4a12d434a33b13a87c2dbb78f69a6afb43e5890bad741fb9b8f431cd1cdde55a5e4cbea65abf794ffaa479e6708e3e4ddbfb9f3f476b96ce1e87a16654ffec72cd3887fb283aeeaa14b6fdbeb6cea066411e64e91245c7d9d08ce27dab69d68bf5dfc8faf966a1e3cc4a549526252cb2a82625a62a4a50b2a62c4b51485b9ea2a09ab220c99e94929ab6e44e5ac3d706e52fefd9656b5babe1a02f6bdeb4d297aab88b2d6d22eb7daaad3573b94a475dbea9c3572ae85a4c06557a234f4df56b9ccb676e97abce3f55566d2b694dda99a3fa27e862fadabced97b2a60fd5f6b3aed476b1997fea75f84681b48949bc71ced16bbdc49870f937dd19dfb43eb125ad5971b492f74fb376aded5a9386ab369a712d2a4b138d82dac274920f458bd01109926cda7d4f3a54feced479d7abcbf2c5f01d61e9cc34add48c664c672f55f7ec46e29cd24780717fadea9bae99a4444d4e5ab3f4c95f4cd4e4bc9138f72ba92caa9800cf6c637363535fa5a7d5239d475aa5790165bc56e8d20c6e0850e1ae67d44966bb7647d6d296b792b227ef89efe729d997b25bdf24711859696526c639d9ef3771969517d3eac5b4f24f99aee6f2b6adeeff7f5773b2fd78bfb63299aef965f8ea376935ff35cfa96b65fbb5ace697e9fecacacff96b5fcaca8b699f6fba6e2dcbd5cf52a495d2329969da1b89c3a4ec5a9c6c67e672350246221024022fba7ba9f2a730131721e0228b17c1227151e5eeb66bafc597c2fe9e19c685c94554adbe49f16ee2a2888b0cdc9dee5e6a579aeb9b483a8f19870bb798dac26e11e5f4a6eb4db6efe79aa354e31c85edd7b98a93d9aebd67ce651caeac32fd26794fd97e9dc5e97ea58d33677c60c226c783591a16a248410a6e3e8454d0c3bae361a51ed6261e56260f2b081e56ec612de36125808795080feb023cac1c0022a651e4621a486980e0621a615c4c63bbfb4db86347f8c4ccc327c278f804091e3e0183874f14c0c3278cf0f0890478f8c21a1e3e8185874f7ce1e1136578f884093c3c802706c1710de1808b43d47071c81a1789ec7091088f8b4490b84824c945224f2e1209c14522d8452227b848c4051789e4e02211215c249200178960e1229108ecd0218e61c6c5316470718c1c5c1c430817d30071710c03b83846152e8e81c5cb25fee02efef072f1071e2efed0e3e20f485cfca1c8c51facb8f8c316177fa8fd70baf80309eeae73a4021dae1c20e2ceccc51d9c8b3b542eee14c0c51d225cdca9c2c51d0ab8b8f301177748e0e20e1c2ef270177954e0220f1e17798c2ef280b9c8e38a8b3c40709107ce451e23b8c86306177900e1220f285ce491051031bce262287331a472771d56887cc7ca91ef5861f21d2b5c7cc74ae93b56c4b8bb8ef08b91875fa878f8258bbbf31095d81e2a01000f95b8c143258cf05009273c54e20a0f959880874a6cc04325d2f0500939dcfd95138a3aca7051c7085cdce1b9b843c7c51d405cdca1818b3b9ab8bb053f441cb68b38ace0220e3bb88803142ee2e0007767f9d849dabe93b4f39d24169272707642f0c07742a8be1302ce7742d0f94e0825f84e082af84e0835f84e083cf84e0840f84e0848f84e0850dcd8bc5ca22836b9808b4dca70b189085c6c22878b45372e16edb85854c4c5a220178b602e165d71b10804178b6e2e16e55c2c1ac1c5a2165c2c22808b453fb858a4848b450a70b188022e167dc0c522365c34f27604a8c4a804f06da5999a744c213333321009000000931200304024180bc70312c16c9e86ab0314800372a67290521d8aa32849415219c20c0100000000000020981a02a5295a29c7c8d19282f33e01009b2b0265b1c6599152c7f19b177053c43555c0be14b1bf9c5fad8f1a6dea9b6020874aa802915f6811ad2cc131fe698eb2978a280d50a1ef92c4c70a46efe9525d1ac4e81034e235d5337003df3edf50b9ecdfb7fc52a17fd7f4086af45247e6af2f529cfe8e3149cd0db26bd903b6e488f4226b19e01250a3e9a2110a17132954b2729741f1642cb47251420a7add4522169a8e46a998080f4d3c70dbf53a0f36dd9ed1c8f0df14b9ebe397d2791b889d13924dd5ccfa3023e766edd6e5f676fa397900b4def30b070fc2477525eed178d3873c083c285cff25a8309d66ea8a70a0cda27360f908165e7a25aea728e39205ebc3182a9f66d740cdc774053a8fc169e04bd9730ba7ae045646414bb46fa4aab1c482844d99f544e36a4259f867339763fe05898bb9a20a5658329e25a8d15c75f9d01add8f8653879714a8aad6362c8b67c86edf38147d9b817573d83271a1192d830a836cba0e6fdba0230b64a236790111a66f81a5c10025160bf3b1c71baafe84642c27f24d5aa5b18e4a0d42349f848a78a8c5202d01ccbbc8193300d990b5dc5ef3a30dc7d2f8c94a8fb15806e435a5310fe768b2f226898bc334e1510b7a2d8832e01c3ad6a8577600183ffc3dc1aea0c6f80a6ea843ac6c2739af045a022eb631c4714cee200558fa4f71291f4ddc0f7c327e2b8899b4e1cdb3329b898df6cd3ab645eba7a92d95ec51ede37df57e99b801ed5c31978614f484bdf085cf2f9379c6af1120197c6cb5b66bd50fa4cc28e8bc48e48d19411a1545469298215a4902f6e8280b71a16f6ca743e3034371caff99d6ba142902ea7ea1086f52c049b0fc08ee310b25951c59f3b14f485ccc16fa5ff90fd808deeb3a1a08e59eac455db4ab051a1e400c887887a0bfb39a36a4e01da402a3c3a68f6a2efb681b4cecb44ec43a9ebffccc0595e83e47958328886dd0ee15aacd5d04cdba3deaab3a039a5dc52f0f519ce93515b2dc2513e911cc68f85a50fbc8fbb01215320a1f629f5a82146128b840010e72d8235bf803c66df8c2769a9b6728b807c3c830ccbce0954bc0b7a6c81a5479db1e6b6a1c1ab2054f8ddc9786ef7c608c6aac64feca2dc8299441eac3ec4006dee5b87a887605520c18f44abb9208536d5ff3a004775f8087e46a35398e9c9bd99239b8dd985493b305d7bc1dfc36a84680bb83712ea93ac6e77dc19b02843dc5658d2a48c3656cdee3e633a8652e69e51d218c104c913ba5229253f3b5f86fe348a62ac219d79ee8eb07221073e3671eb8a0e38b5b1be86b644f52ea8aede9db96da275c18687ce2625a8eaca81e37b2c1ea06d007a0eb32feb6e9ee7fa8ca87d8a12286ba76c42793dcb7842e72b0161d36b22fad1f79c893a5cfee9a3e5bdf39de13c96632e5a4dfa92d310fe400b42884e1bf8bc7d66877da31a00477e4e039009220603780f1dde98ee7f890819d0022e88a8ae4203cc01a21c1e19541288e058e01e7a016e78c7dd60600bce0e888096c3a436ff7ebe513b3f3a7cb90bd6b244751329e56b5e45f5930781b959a41239a2796118cb91708a260dafb8158d5bc416e3d8ad1a2e8eb9a4695851d5583db437d32eea0098e4efff1c8538d7b75b7b179ec8909901f5c4bd627463240404c14ac09ebb49dfa20f840094e90038886fcde1a7fcc3dc8ba721a727db6f1d62f32803a81673d13991381179cc736790a9155f57f35e2266486d9f7b1bca38de118a5c8eb9098f3c9de038e6af2da766307d48efae1b6e13f86d6ab2585d86dc3276cc781c28cf08a2ec7501620d10b0e2bd1bda1f7c9ec93531806e46d096a4521a3ef7fb00b084a6549150e5f2463db5db0e6da1a1c2d439b35f1c7b7062be2c5a844adeace61be787c05ebca890a291224cf00fbb807edcb6cb25f99ee38d82d40af621a3611f1685c7b2af18bb111591b4158ef6f0f334cb1e205439a8a88f00483b1421695df54f7fe4cb310dbc651faf56b3c6089113f87bc71e8011b0eb24cfeaa13e71c87319ac45469861e8ef0c66dd44eba1cdf368ef2a4a7fb53fee63260d5f839034786a9388b3d635b10174966d94df2d14686954b5e877c1bc53e2f96db90f8712979933cd632179ffc90b229d0bea9c5472297da974a00976f8de68b1da90c550fa7f98ae401e0ad26dfc0612ffaa83cd97de3432c98f8e8840dacedcd49d146b2de82944d8bb8ba11a581bc28573335f946148b0fd5aa7bb9162d183911dadefa20fdbb894b5578203e7524ca6d1869e0296931f0cce558f1431c54de2ce131f31149e4445db510120612a2a2ff227d3811547ca8f1aa21936d55b39b3ce73b42347802869f2287c36e9bd5e58c0b13e5473b2f08361a2e82357501170d42d7b3ad97a92ccde36342c70b4b442b8d351931650afa7340eb4088a34907604de457bc0bd4e77e585d152591cf9b366dc3dd46ba6b64777e2fdfc3e35e71e1b7cecf0730156c9c5c9ab70412c47c1be40be0da9e5dd53f4e357538d3d62570ada641da09a5f071f27c4aa9ee8f295d61b1aa2025a69a62468270ceb18b6def77924bdd6e97ee68d29cddea60a01cf9f529e77fa3498499ccc9dc7059a0bfa99d54fbb93fd9316266891df4f2429432624cb5530787f41792c433c1f2100fa8165b49d4ae8fe60fde9ed641140538a16078310cd2bfc96f5447273741d92b0eba157fccc94a8c22483acc34d222f5df2324e2dea7fb0367e354e8d76565969d2cb84dc91aee3a58d28ecdba617dddd6fe34f64deb4455192ec92b02fc8d757f90caed2538567840a37ab042fd0f91d221e0c22ee41025aa123f5a0a6703855edf03e4ec821f2a06ad04872b03046f0edc776d0035040448dcc7f29277cbb9ad186731b0ef6556859b9da7b5ae7651af672ec568a241597883a16f9a553d031a5426d87534edd71fc0d8c1de4be2c5630cb4781a1f40c770a345bc2e9d3cd23cc154028cf0f34efb0e84beebae9ff904d60ef88098bb49138caf8d30e15e2e8cbf035939a9ca1c1607bb793ad8efb3221f86c5a0e80f85e9ce1b5b36f69acfcb5890cee7b7043de4cd954f378b5c218a1a5cbd28585f7f7b527d303f92c92a5e2b7ca3496ff9cd2fa7fe91b3db65acdcc4df634e483fd35507818e0848bf0c69dde68d580e176f59aa23de20f1021f2eede580327e2963e955e0b57ee1b1bdf42cd74bba8149927cc17d43a69e12d0ae8ae63b550780397835ddaa317434d1eb604918909ad781231636d1140c1b3cfa67530fe761ef9b2a98ea0cde1c51f1769845824feccf0f9fd54f12871cfd878f0780fbf1d60f4ef606df7e0d4e16a36d6439c62a20a8a5cd7014a828e69af6a1800089a0671460b23438acd3796f99864be1b682eb30f5d50e90922a99a35049934a92238c8dce91cff51de3dfd934b783d8ccd7be13bd319551841047369a7d41671cedac2ff1c6da4b0f8922d4a394d442de5fa35c64cc6f8fcd322245a92cfe6a7b5b7fb10e81ef4e587994876fc98b3a2fb7c02863ec020dcd82ecde9650599cf0ff767e152b40d0b1cfd91b5007911aa8834467ab8048eae88771918c100245fb8d2c3a0e110169673a9b96c3fde2ffb930d2019db13224023e8a27f168e14d59166c8183710ead7229bc7a3e6b3f780111488e81897b0aae6c6116883ffc1192c84e6890a92148352b2c5d35466abe150e5bae7c34ccf91726ca13cb2e3f1e1c13af06f3aa7260736b08ef9fec83988549592c14a872ec98e69e05c11ef3e9564a6d90516ad899bd304b88d8b75a480ccd2e1c7a1cfaddc05a3f5ae3427d6079b01ffe349e67495e128abcf630ba66324532bacd578adb9cd99086dc15994f4d1f79a4bd85eb9359e6b0df118198f55d99d841e54f7fca270b7f48e927acf9a7daf36a5cac2ea0974045d4811f97e21a61c4d30177074876aad26316a95eaa476beaad9678bb64a326752da93c346c878c071edbd15f39636dc7da9621a8a6ca71973297c94afa2e5580bb27f5a3e5794f6be7c5dfa2a1f71477a0c5b8da0461d4183de6800c3724a4f023fbdfd1c70b9b7acee58d28cc2ec9c81996e13ad4e09c771ed94503fbe24b97f30215c7f1cc25315cc01b6d1e7bb2b28b8c6994561c4ba5fa605b35f358a0518b14159140c06934cfe69cbf0b86730630812fa559818e81608a55a02af318528ae8fe3d5c405708c9329bd78c36b00e3a2b45b31c4982513dedd629875a026822fc2e54697ac53b636ddb016519463e1924ba9640b02c1e42eace837c8b3892a7c0200d97d54de3ea9cf1c38d6e8b5695142abdafca574dce53086fdaaeb4bbf67690fe105df6319a1aa8d3acacf51c58a8b279faa6e7688613f822b87717733da453eb9ca6b52861d13699bc35d3a4de762515c1f0098218d6b8c82793f899343cde6823fa06f12a2bafe42185f8897a7b8e0f944cd0e510f3486504eb821cebbc22068640a3160d34c41f0976cbb3e710bf957012a322cf32e4e8ce574d1e4a2a404484d2f3ab355d144a703781422113c92238adfef0f48c356e9017455a77464921212c1cf496a528c66333d3c7804435da3d4b6fd8dc197fdb3b2f43908700d5f619ed33ae395a9925361f82a7df1bd1e8b154139c61cd3e8651d57b2cc48ea71a1411f29084c65ca89fff7661d0d2ad61a6de140e82448ca7997c066fec0eaa7b58387d7108a8c1bfafbf54e8bd1299e2608764891abf95f6eb6f7d5c31bff1a49c216b7ce8aa00773547cb592c57ed96ef8320ea846810ca8a5ef45e64ce028663af4fb2586f22cd354a86750cd9bee1b345ecb213e36ca183b3b538b928a54cddf5484742e46e920ccc42438580f71cb8cc9410e884c1e95191796b0ff55f07024941630d6ed2450a90c5cb8d3dbfc41b8e38126148635e3460901c2e9d2e3dcf22f3029c1b01a6fa8305524dbc8b9f52c3a87ceaeb3ed09a74221ecbd19fcc6e4c21d92855e936a902aa6a724cc817c08e5668ac0c7c10c2bc4278d8e57ab0c28094ba3ac20bb5692f4b7fb35c30aad25a952ebac0fd084cdc29a499d1b74dc2472f23089ea343465a12094566a9ae347fd5f38a78b31b62ad89ead323a884983d1bcd97328fc3d08081662bcbf0c021e7594624140058de19c854f9fc248aa069123e25218884fa4905dc1018f523c8e05c9ccc241f143a17b214d4a7099d7cd4b381c603b9cc94895d58970422c182626dc44197544388cd6b92a9b0612f683084a53b114c91094b2e5dedb992f2bf4de0005bf6deb0f0bdc143b9757f720778c692b474cb985d1022d2d5e7c9b639911bec47134fb36a3160449ff29823dec41d34c035476cc4406363d480de3e12220242b653421a15a5ed521f3d89e95da810e47e87a356a9c0837f19d72c092b2912b33a7bbfef0c6064828675706d9994ab3ae7dc235d9cafb852356300e951c8b662b1719382a78dd900856fb9d6b6a59557f38f7516ad15465e469e3ebb1707c97bfb060e48e71b39e2f5173985b27242d4c12a00d755a50f7336bd665bc125e8a756faede236086949c3d5ec911ea6a0b6dfd16e4fb2e4050686c38764a35275a0506f65a920254c173243675610d5b5e36404ec005e93e43a3482972e500fc9ffe124a0133690f08182e830069ca1310c66bf2dd407e8160034ec5098caab0c84a1297dd45d59d388093e9710b6fef01ec03556c745ebb103c72a805f058bad4de22772743a34a6d4c4d3b8a1116c7d4c06229ff4a5fb9d7e28dae5b4915c2268c0b9e8632a5bbe78913f569325ad9b329b6410a3cac52b8927548bdc64d00652796ede21570f3f2fc5c0cee8c0a2c2a3f90b051a11d7812b1d30b1bf8d544e381978b903f008cdbf3a0d13008bd1d80b0bc40cca00ee27d2fdf56a677b1bb9e757483e09a2df3de737c18286dd7813efba4d9525309b54d313ca9fc01779acf153291250225212a284eb69e5b521a11fa2ac38458338725ba40ba2dd9d7b7ecaf0efedeed42c03007458ca80f6185db70bf1d59b0faf906fd07a821fdddd4189a1cd360cf76fe4d8a46823d16726e2715b68c6c74f826cbec7b893f2ea5f48abf73109c2dd111cedb1816510885154d17d0935d62bde262898d45c5c1265a7bca14ac19d5d1a5ea251592828a246d17f9d90cb99ab4626480cc614a8812004f88ebe8849a830f6c29d754bfa2c732f14f3557f4060c2a4308b93536304d19626656af1622784cf0e78ee96bef16c465465c3d315e9055dfd49497c12f6228da394f56d95bc0f0644260eabdc14bf0a7ea4142b713113541880880405656484c378b00ea9bce23bfe17c4116ac23c0d8be3ae81138d83a533d45f311d0a10f8df7813dd3ddedae798d7243deb938ff57541e3625036effd654d855f8e459b31b83bcd6b08a6f3946899e113c9bd1f119318063906d6e2f0b3ae2bc9246e436073a20b3061e223cba9c51d8d7f7ef76e02e01e14faed7d77e2cc5652496d850520e96f86ccd3df7a03a946416e15e72de9fd83dc605516721de3767af2f9e518067105f4f87bb4572a7bf328fd390f1ed2b3ee51894deddc090188a1bcc2f363b2bb2f3d307254c0d8243056c789c395dcb1562c8b4addb53942970d4da5fae223045644c5fc6dd31697d648f048362059b5da8dfc6f652754ada18af960e30de9ca989bbd83c97d301256b37c92ee23bec863d9a7f9b07e28fc754c189a50bada349e56f95e326d75066dde76b855e16f792ed8c0178aa992cbbf7d0b14494d7e85ee60e9d05615d92c0e4b16f6ec8d9afdf610bf311c0a31f23db028c46296de47355db2653b57562f35a8e905c19c9e126a43a0e07adf810a5d66a12c9623ebec70a1d69a5ece3edab920b6360099a9df2f2088a1dbd2c8e0dcc9d0fd5e5949ad024d0d82c402a506ab8fc4d25290c5f20548818bf6683d12761d566adc07b26a6231f39ec1c5b1e26af7d594b17313101fa188fe718b009677dae0c8d53ae3e04142da5f6d3bd4076c54f3775c792d073cd84da7d7025903c5b908cac3456dc8c4a7c60a16af90159fd6ea02f0744b2bc702f1ea05646683a67648c7981da39354f4d258abc3aaee0abb458e39352c0e449282c57033d189305ab766fe04f79c35e02dba19dfb56fc3e6a75b3315c59430baa303324c0ccd8317482ca5842d23251bcbff6fe5b585b8762c5b906cbd60b16e0ca0ad2a837b7fdd86047b63e1b239fe2267bd4b61eddd6bd0da6d5c830a82d1035d1a30d9fe73cac1120e86f1fe8948764b9e73985de4b542c8ba75b98b275ca4878ca9025d8a3b40299849f41b7f01dd3f456c11fc919721b9c06c0dde4b6d9a22ad7abd2da1e99729bb383bef075e74ee91e41f76695a467f1596ca92900b6d3064edd5d3f33a7805935c02d98ea99efc922966d0d2d7bd6bf87fda18937316d5e5b7c73ef4978c34e4ad41b96fd4beba2507296ead3a778bfef7e1b4e1be6dc856dc3d9102304dfe139a2fa8680e5de887d3cac6418ab65f51f03b3e7c47f57320e3ea2a16d5b660270189cd275fea9d6386a7d5bf491c161bc28ec2f3663446fd0e4601e832257b976a08b631a4df8c9c4e6278c44038ab781e02ab1a43a91bfc2e6760464cea889a51f1970bbc07588a9908a7af5807963306ad4a57cbe6864d5d85e2598b9c8e5223e1ca1a1522f338edd78842ebc824699e8697f1f708252b028dfc4c7165e8a56b0f31da73ab71eb160569f8a5b71d8c101e79754d5d26230d80ab5d7a005ecdfdcca1c203b7f977dac8744901efc6069cb10674d2884baf10111157f76a10c3f623bff044a0d7f271b4d4e12c3590628806a43f3c2a88a87f84e9949369ccdbaf68a879740cf6f38e18b8e5247558f734a9ac52ae94d6fa159bcf5a3d193898c4349298146b8ade3cee785f7691027815cfa0690f7751e856517e1ad27ecb6fc44c4f48ccb1e6fe45b00b556f1f8170afddc15f31bca3cf78a23d8569ec74f8b2022f0700f7a62328a11945163121b79decdc5156ddb05e50e01c3aef189b61d1157386d4049098b5b94f04cb4046038145a6ac3fc1d144bf9877d9da82955461304ef72d9cc75ed5d81013f92461907c8979e11cb2f301250e0d1a05dbd317312b6711f344571ec461a3f7455af61413600390b751107c90c1f1affb88c68d3481071dec063cbca549223b6d34a1631fe1d2ee644cde5af17a06352bfe0fde4d7b2da0e3daf5ee2fa07f196c15974897bff81d61e435d489b27d2af986be6e297564e520c167617d323e94d1c6de1e859962a4b32074a63d8c8046fba8e3eccbde89c8c65a8fce93abf3db8bf2dd82a68f38744cd98c663a6c1e643b47b8ced4e8382f4f9232ef24801e6f0be047252cda4abb5d78bbba4145b2c6dd810fba1ebdd891ac38ecb7441ef62625bc5c86ef723965af821210f2ac7783e20ccff44fc0d60b258be4f3078499040b033918504eccfd647ca4f410a1c7eddc080e3421f4711933928d5af62f489dc09087393bdff1431ae54943b3d88a8882e9b1292ccbc506384b963915fc1610a1ecd41d36504b7cb8a14cd5ddbcedc487fc7600ee88249eea728ac6b8b78fe187aa05e8ed8216f9d107452a1cc0f05c0483074c5ff949301f9fe3c3942b6b80cd55ae5e1c7e18401809a5ce6b1ff47737540aadb96f106f7691e931709ebda44839852ef403659d1583f5390e5cf83280e0be6ee837d6b7566c919c95696ad77aeb91301587bab76b564f3088cea47caa69794445f5c2f74e4d1a034c3661a32ec8270d2d45b7d9bccc53204e57eb8874b932d0c4933535106e0e10593c95e13bbf42342209fe73ada9948470098aedf8ed810e607adfa7a7b37a25a17ec5dacf3fa4525d3f1a029b7ac08b64068cbcded6d7266ee2a550e8df04a1561e3ea2b1d9c0e7f1782f3838bb49441119ed74bfa7702e54a88354999ddd77af9325e073465fd0628a627dc0159a7391da9c322a6b15d802af107069d5ef217338767259adb17310c4cdaf9913d82d395dc7e827fcd0d27c493887247a9cc9a157c373f8ad9d76adfb503c03e44a795f499b823f7ff69b9c479c23a7f11f776436915cf735b96f07b2d63a52d7bc834f581db62c36a43346d7d4218025550935872da447019bc06232b34c1f710005988010cdec2eea65df145025e2c6b55c50b77a467b91901ad36056b203372be10fc682d64d7bc7fef30cfc0c0f9d20dbc14aef366739e3f9677c6e3c00c371de4e70dfff9369a230c530db6293c4fdfabd16895bfd64b44f703e9eefd42f8d3f165140316b529244787d0f11e321e4d840af54b0e2de66df5ed6eeff61b9825894d7dab9e9ef31b2a5b3afdbbd81e9ea7517154725317107ae2b530838061a34f0c546a1325cda3658f00fd7858e0c3a476796434d16f057d1188cc3dcc9b0341b253a19a2e8965e3f06f61ca9d3e8c9ee6c084ff5180536f3052eb38543076843e08fee0d3f62bb2d7bd3baeb27528bda8f39c927b2b2bfffcb9df83c96302ed5e82c4342e42cd9f734b8318aa69be2e9e201d47d78779840c179c8ec37a76d750e09dfe7093674021b4f55b708cff85a11388254d88830d4e6d1e8f63ac43b2610f483ad4bf95309d47e3583825bd10104a1ba3b5ce830e8c5808930ea274284c0941304ab6509c64e90f9aa8d8382d3c3bdc88ea2fd9486e38c93772665d353d2426e32e69778abbe50a342174d4b25ec58bfe550f375ca786e6c8f3d5661a1339ad41c227f8ca18f476bc457257d6ad6b6a9fcc564c6dd50aab81b2962ff5e3d034e1447059d00c4fa5d4936ca004aedec2fa03259317e05daa1e5d04b1f77063a4b314a937c0d04426cd991dc05f68dc37f99f151c6cd4ff7fab42ada690df2332724dc168ce9c5d33b6ef201b358b764b37c51f6ffaf884695caca94f556087d26e82fff1ba2434af893b9052aea6fd1d37f9cc490268535e6064f07b7682dc8f709b5bbab8867e940df8fadd940a8865de41ed839a63290e35dd577dc2a89ac58824e8cb9ce37a64243fe5447b68ec3a56a96202232e77196bee9f3a8db17f7b3854dcb871440b43855bce9d6e1fdc9485f59b2b33f8a8e1f8a8706aba83fc199317b8fbacb1f8b278c6a92558ab7b3c005a71c22fcdc766d71416c50e7e5905a02b4b90ed6350f22cd012007738df16b0d473c31b4676dc1f4a837d15993193be17cded6e673922f043bd2b455ce4807ee8e2ad64cbc45693182239433a83d44a8648120c66247c7a530c23b4c478600878b14c74686193f5382e921ed7c1d5b97d1bda96c1e2e852b5be654df1ff2a3ea0a84efee9c70b20909697a2e2a6ebb2200de5e82e8600b5b39261e9ac22b41706a02b0d371c344ed7e665bcf55dbfba1a78df805f1431059b499fcc9bbe0d835470bcc224458a89bdaefb2592531535f31b61ae10904f533be9890e0995d93590fbb4cf5dad08d3c16cf2a8c5b3eaf51b19e3b66331f55a2321e1158f985955991b794ce182d52a4fc450bf54ef735eb7c7d84f256979b6cb24b527877cc01ed758ae28850e3c514fbd9848f15135ae7feb59470336d008348c7b7f293611f6d3c439060297ae29016f12085057487388e8dc333b500d59a0cff3f2f3cf5ebcd07ab7414cabdf15406dd99e22c629cea505742622f121a21d679a40b559143edd23041c6866d33df1a1460b79853ee318666455ca8abe60c2a606b3a13f3be46d15d59ccbe4db48fbcd19ba517b3e9dec272fa6b200154117274da09a43d9ccc93730626ef9a6a40b89449660c03f8285bd03f16652884116e02b876ed62e183f5789a580763efd60fa8feb4aa6ada391b93c868089b4658c8a795fca8e4df7c20a5ec6bce3e63953df8a0a5b2593690d0f061fb9b0e8a13d4626ba5ce5f5a2a5a120c4e9f601762068c31295ac0c2206e6090f3758f163171fef3f1842c8e48ad21118ed83fca30a53388987fd77bfbf0461ca7be92fe4c77ddd27d3e381a87a8b3f93258be15e43481f43975df687af40b743d6c5e87766cec57a0d946a6f46fe9118f6cdeb68fd1650bc74948325fdb5de23901345b6d7958747c262e79249b88b3c423d5eabf767a3fcd6ed80060c1759707d24323f41fab51405edac29ed4ee103bf90c494c6ea381954a2f0c268f69aa394808472e0f5c64bccdecc7b87e8fc34a6cb81ea685140ace3d0eb36cb8f4692786da468aa84572b06655b1d5d488f4c35c958def19a31f7a3aea1fe5e3376f8238b79656b54a668808c7f043e42d4209f85a9d95029702e5ddc9f5ec38e4b09f8baa002809855e6c21c15883c8da6b95acbb99023ed0276975052022109844d216872252b3bf3a2dbac0b2d46deb08fad4fbde0d980c740147fa22b8dc88c8ac26ea98bae55e6215caad98a84646105982c15b879c83c5b6b6a8faaa4010f0e11244c8125e10aa0d6aa80d2b0c5bed83b554f813bf95ed2e54e6fd845ac6106b32c1921e06274280897936a262bf86f8634b023982f166a00a54fb4c3a23609327e2c02c00021fc19350fed96e111fa48912fb2a250b2f0f6c850fd698ef14c7ee9e764451e50ffdf2e17cf86cb1d4bfd56929639387e1459e3f3e4dcf0c2e0f7af233b0c90ce49f0a3d8b26b8ae43c4e2ddc4e59e3f9f27c759afc68c58956d3172c0967a3413a231430834807f85f734459b8552b33b45096803bcad2a1e04079fff6984667d0581ba0221032500c173da12f621606b564b00e5f5f57fa64cf0ed0c350b1357aacff45c35925f29476bb652b8fb606104c07245fa575a5fd00801a03361673721af9f2a0031cc4510a6488b910d985fe560062a65f8833277ecd0a9baeddec616cc5dd8334fd1778dcf4f9b66e71e69c96510f3cf0ee848dbd73d327092640187220e451df2da4c531a1022848296cedf69fcd50f6bab042bab36c68ddd0843d2612ee7c92fb517ac3b1ab37fb99c57d4bcc02d743d985380921cd4d8b0a78063026c720af59e05192c973d36463a5561db939423df2fc5999e89e863538df06fdd8a42fba346be291ba10b16b35a14ca539db0b4a0317a9527aafc05c147bf80f0f39387bd40b950c6b94e84216867f98e2c59e5b53e8c42141a0eb32016e1cb866a25451a0e33444f390a87babfbbc1784f54092a41685c0e78eb9d8413eacc21ffda28365bab4dc0023aa64e725a35d1698995aab2f268dc0f870650701717151bd860b4f962273e50936f6f2bc733a8091e4583d93da75b194b724b78146057eacac9c35f93df546c7e14bf3dde6464f85859d046faa98a437e8a136e10d0df5b95365939fe61038ca788038162c12053a60223e8cec033d986c11e02b17a56e5ed2d9cd7e6d0b45fefefe86a21af0a9f0e50e7afc8ecbafbf25c53f88264ceb4650790abc7b6a5f7cfdc8ed53643e16586f4125ca3cf7af1c88a9acc19e2951c78d72f2af47020c5b600cd8fac92f8a22cf9a270caadc88d1663a63e3289f304f3dffc26f1ee79af36ee4d6d5a5f05fe6d75ff65473d26386727715ca204da7eecc2453a25101afc33901258cea2a825223368c13db01a4135105899ee2a4a0c0b41b70cf2e4cc974c380401f49c30ea7b270aa0d922db7c72ef27ffd4c8bf2a10c9958422f7cbc2a7b94d0439d6a17157284c1e6725a338d38c1ffa4f6ceaba03c1774dbe7a3e0bc283c9b2e44c94ca1d16f1b7ed85cb1abb280b6bd82efb3020671b682e071e8cff8f38afdc68ed8470712fea2b908eab5433159f53314f28869f8751afb970bee9bb769db23d8f3b06a392e7bf5050adcf841c8ac1f702fe0ffc97e3a8ea77879e991765c2a20d19628dc69f6782c1d23ccd5f75ce1c8d81b4694894d56f30613ede0cb37262ffd109043945b9e6facc2238a5cc47f61ffa5ccd73988fa83629c6cac00cc7fb35728253405745c91a11380e476bd01618540e8443c3301ce825ee418ddcef67f09d70bc24f44b826f41db1c6698cfd4b8a046bea4450f0891565ec3b7efc550d01cbc8f300cd2dd9ce388b43a3fe94b0e85816c4b92f8c33f2b762a8521d962dece5df34e99a3ae686aea1f168d201c65c5cd273962ef517ad83c8cf6ea259c75b28299f3d9e9088847dd365240d0c0734b6bd91231d00b3f5776475ce9265a86ca4c13d747597ccc9dd0da897b14a3c5e46227d697a7ec1597d018b325e8fc93e14d1a2de9f4ff22066d7ebb8318713e3f4fd977f9ffe94b8c5305c8a2014fa309fa3f7e410d48b954e88fcff301ae85e1ba3e855458789d3b197495d069dcbe936324c46a5cf12507e2bebfcb98329c3f8f941bd2d8878f47438c00d5f879db370569e0f13740d3e1d798fde52a9a633af5e5168ffc88013dd2af1d397cc8408672a1ada8905a9d0d8f43412eff5a893fdb6a653d768199ff98020dda889ee9081c043db59e09e789622ac08b99c87d03407fa4bb6de3a7579a5d756014a710d591b516df34501135fa3195bcc6d6b198c86e44f54ba4738af34003c85ea49be5f70bc3f1c95296b6e049b8b88b7650b11665de106ceb6fb48ba77e81f07e0bd69a1a30f4d12278900db6d4cbed0a950addbfed611966407a91e0b7461c88332bb0225c2905ea7d2552b2727ac332268ad8038afd2d9685fa15bc6257f25c9423e3aa1c4b4b05db0f7b122599f2d4aa430957accbbe8a243246c78082da2211fa9073155e6e3e1bbd688058941d131c0e71cca84b24d41a9ce4476a2e290b02bb835e86f4cebe786a56429d71619dee7a0c04930af14e4bc4bf45dbb8b19e0a057e0d17469b566f450afb6dad06ad08cde0dd414c67b49bdd490925af05e1d5c08ca5d518957ce39354cfe2975a2803b6e949bd61ab16cd77db6a76c1579dac5e7a0c17165f7641277bf11b86ed5705bb66f1d53b2f40fbce1f4b3e55f8b0817c7125e4618c93b9469e8609fa82ae220e50c32b4322f0ace8bc800167498234a4633da9a717111f984f0719b405b6fe8a3ea66b181591baf4e6dd4187ee0555fddb0100484252ea8c8711728073c74dd47b45748edd9c0cb6ba028f79cab38d5c97d478745e73ee64b9a52c52ab1657a56c75cd312a9df548e5fb084b7db3585e0f0756415cae17b10c3060eb91889d66d84eb365c658c16d856991a33b2d26cae66a0e1367528297d40c93e77ec10425463c878289141df2661ad374005698e2544870302e0655a32f45007fa69cb3499964240c02658fe3735d4907a8cbfd17daa80e3afd991cad35748cd248ed4a973d3c74e716215833df7f4d130283269a48c9e662669c56e964be558c0d2d7bdddbd4734880e341acc2a2a2fc26f1bff619414ece0b6c247799d31bc6ad8cd7af42013d99eaf4cf7341580c909502559ebeed4463233df5606c6e8e169c3edf2a5410cbb0d13b5ebf02ddb823d7abf05e932e93dbee65c6aebef06db71442b8c1c4b0f1ac6098d32d017a05d3cdd837f7ffb335020fe2db8d0fd4073af82a3e40213b5421f9700fa50f747603a8e5fb7f82da1ed61cdf3d6a57bf1efe4e01f40ced79ba5426ce7ab5bd84aa783ff5623453afbf19028615c9610b09e4813b39bc662dde8d5f5f504cf7cf2b459a7f30c2ac81272904cd1ebe600ab92c0bd12b2883eb7164b4d505b2f27838b1d66398d9a10eeb9bd212736d555abae41bb441885f7a070a6ec6b68a951b37bc7f63eada6ffe696e460ced8d05e2bd6ec8a8186409e63b762c2f236e9e9c2b717e319d33a5c6c172d0e36b2baf0c677b868039bef68558d0ea381006215d09174a586def6f309a5d7d209f3ffe1685938043bcc2ca972ec7d4a1e418e706da36a1dbafdd4cbbc0b187b936af694f44962c38d8ffb3a8386dd50b44ac7dc2c7298cbf2219d94cd18019df32477eeb8f7b62dfe99f268b8616e7e7577db0c83b93f2771025280e86ecdd9ac94e940e99141778a53deeef584b99a7eda0e0def498a4bb9670fe149a97305e9c804055025720ca0821082141bec4589030148c093200d5ccd98e49409ce910b29b1821af64a1d473c42e17fa713576cb7a620ee32b3fdcfe3c3bb38c93090c77370547a5e330f368bcbe8d46e54918085506bd7c6ec03db423a7d60fe0db740796d211ecb51d640aaf1da43a753c7ba4d14666da9bdccee1b4379881776473bd994f762fcb4f211d0af5a3599f657408536ffafe15f666b3a4db544744e70f22fd63f446d3a375a77782aa2fae9c93db612bbf508e0212dc1025fd5b93f5865bdfb7fc48b7193399ce21dc97e1b3e839f39cb72d824337b1c926643e87ce835b1cff22dceb4a87874dc790df12b5bbd334f5a471b3ed70c9d44fc673156b727256c3ee0b961977936f068878fde313fb0e1e54bce3085ce62ab3cb4037bb3d57f51def027acc4c8ec5a2863d1fcededb5b38b2d94c322176edb135fbbc966f13fe2aa3e4eb4497df8684f75affc4765eb386969828de234775d3d9f203c60f81829f41cd66a1f1e523f53c702a5fe2e4d9ca33d022fd2bc09d2a517772cf978f3edb8b107ce37a00cd5fa31b67e7bc7a6a6fc1cf48fed7470b5d70d4c68fe4dde08dd4533bbfbc28bd725c3176663e1319e90792078e6b7b6672cf569f8bcd5603749053dec3b0c60ba9861f38cef523974018f38c52f7d7c3e2fae39151f5eedc5efc1b78916ff651cff13cce3da58b657774e2408ee20a74431bcd75ada8c51236e86e0c89f600d62fc1c0d81e6b983b8147fb5592c3401f0ec6a17be3969ba705785e6890c735dda9791b9d3115ebdbb77e13f13e5cd81345aed5d5d9a75ddf41ee8e17c48f9ef3c7e98c8ea57e24983283b868b6d44683ca8bc3bb347f4f6964887dbaf1aad183f2038ea4f6972ef2db371a470c8f40700ba284ed93dc75b7541c299694cdc976beab9b422a9b147a1cdeb716a26bead34ffaa7655379532637ce0aa6ef5905f9a5b229947a10c60d5efe6b51c0a5913cd40376036200da732f84f50952d1b507adbbfa489f58d49275e084cc165a62d3e3f7753a1dd2baab16536116cf8d9a73be3957498c471f407656b3e80a730a3ce280b2fe5f88242cf636e176dab073e0fae4e3e0eff8cc41b097b8225706a2ed68b355626c1c26f2f16bee2dd8f55080fdbfcab60d43d0a5683984e2d05eb88a773ec27d18bdaf3606f1d6eb9bfaf4ed973b40ce05236283cc90e50c98f0f94ce62675f0bef5d522441e03c0633a3e59aa681778b8c14672e7714886313a55ffd32dd99f9b9e7155d99a052b48b7441117581af151929d5c380ae0fa916596c716889514e6925ca2c2d739df4d4d8186d4daefef2d5add409eb980ff670166d286ffbcedff087ef85a2971f3bbf96ba315f9d50bac0c7fee089107e0cfa54abcfd8bd2092baff6ed93b621204b8edf9f3b39adde28cfca3bdcf40b2d06de1b1efe726a4d878171acd11f96c5f4d46ab9da20ec2040fc42a6d3d8ed1a7fe9821dedf2929e3c10f5590510699a2328a046d545c71d1b8aa9a953c046a8bca0115c3daf4707caaaede9c552515ecfb40e7690a7bcd10d70b435545248b87b957a74d8c4f1320c7bfdba0453ee320ad832b2a9b3d9da7215b584ad709469a89ad272663efb4b54c9b1ede8cd8977e7d9e3f581caac677e4189f441ea59f96bea003f8f7a5b22838e38786d7fab8b2a40c8ea29801637f7915ee6731535eee47acf4a88646274d3280edbb8e27706b9c92550e848662ec0daff0b1c5b274765223d376061f2328c817b6bc51c1115e809ed41e1edcd1e63c2c2a14dade148688a15b881ce83df9b4ab7d0149a3c8e2305f02fa4f8ef2e1b5492a8f97a0f82728a0c7bbf0cbfa8dcb4882658b950fda246fd78f77bbe7f1e626492f60ab6da1a02192bd4c814121cdf169af1247bcb0e4eb57280993ffad34213398eef69e547ce6a5fbb643fc658b618eabf3898b37d7a729168068c786049b9ababbbd4d964eb22adc3db0c78c514eb6360402687ac02ad42644b14d07da89f1d07f101f3a42ef5f5e02025ee41e4bbf0031ef283323d2257b3ef8eb6ab03c2eac79a2387126d5b9f4b1c8448131c169cbf3d9f814e5881991d6981750d7dbb5df57ed15da96d5b333a8a1f44d56b0b4325556c44f56111a95e193b7c713f21de7bf791cd82f09e0118b0d22132ff9a6ab406a27c82afc57678c33f41a36c019c952e4a8e0696a769bc0416667b7a839d7501864fa622639e645c764a1b9609f371cb2e18e59fbcbfe1f94b2c1dd66e62038a4188247a2d3d999fc99414b307c83d8121a80364597fb125187484e676fd793e69f9b9f93b480353b8335d7f7d8d3af8319e5ec694568e83799b2ffa553f2b8dd2ba1606a53bbab07a16b2d99431355320e81004f18c73d851504a19764c0fb9e00d860ef5ba38051f6cf9c5e3a9155b5e5fd240463cec544eeaa23f5a3b033faf9c05f2efe42f78ca17c3d63ee769c74b0dbcc8cbb10b199d4b250dcb15e1a6aa0f0772da15690f08097ef239b7492d8a1f698debb94e03cdb3c28369afa037149f8ef4bbba748bf3c47542a97cc6bee1f9182dc959e4a1d1f6cad492c8ea5a8509a0360984b01ccdd6650f8e56328c6b3ffc0c0da123e321fa79e017fd1ed75d84c446df576aa241e70dfaf73dcf8708e62bfb84ae18661daad9d9689e9c2aaf5c341b8a861072cd961f8024576615767c78f70bc63139d4f19f86e7adc0ec0c2339821441b74b9af4de5c1a93056140e5c3bac990e692a75b941b36a33404a069ef2a30db953041beed4c08f4da8046696574d7e16ec20792e87c885034ebc1c272bf7f70b5b3dbf0b6f4aec9b742f9ff569765300c9489192f01a9236798e51abeac7c5cc7aaccf4cfd142ccfdfb37ee800330ab03fe8e5df92b2c2ebf768a463040f8d234759f75f0f96b6555e161be384229cd65450961bcbc5f3ba0196283b1a2b7bc33312d396ef2466b515fdd3ae713bc7a5ea1a7b3f7e8657112109210fec788e5fd705799f8fa0b286c401ac754392d14e3eada14edf87ec881d5fc1e90e3b097a272a56001773c1b111003232aa31d44491e80d1424dc182998d79c3387b1d98e63fb804d8ac59a41b97261b2ca373ca784ab9186e9ac0cb6cffef6e283384d810e68afa646a1d528ded0da549e686940755a125b2f6488ef2a3a1d16628a31ba48a9d2788c519956598e695a983fc583011f136f64794719e7e11407e40d0c7362048550f8f2c49e4130373a2016ebda8eade3804f73c874fcd4d53a686ac7e2545c803340477034d120ef7b369b245221e924194d7f587bf62db6767f45df9613ff3363510db929f8aad83befc91e50335bb13dc8c3bc3baed84e10eed5e8d20c8d099ea33e7a79819e237ea46cb6c090cb120e8c62a59a9bafc45c69843d3f0e7cd6e22779455f2fef10f2a367abc72631e57084d20956a8217c0e0a97792105d830b85038883b0ed2d4886650afa0582e16b677c4267ba46bc3571ed7491a2ccb28621892af552c4425ad933d443e878ceaa7d1b70e16e0ddb0c78c10c63602d804ca208e3b348a987e44d36aae5f02c0a4a505ee5ca9d8c8bd5c631ba0ef37f4383aac774b6031d0fc0baac33b83e95b1854fdd261e0c45cf297ad9c931e4c04100a25b4289dc5959fd48ccbd4f426e0f8f7abbe16d32399f0a1c6325d3b6cda2ce53c73bb7f641542b0ef8bd7cbcb39c4c81831c987d7ab380b40e91813c83c561f098c6fcf7f0e97614476c763f0950a7448324e155e76d4c6f4b685583befd23575fb8684434e26c0edc04eedbfe6a8bf2e9a3b1307f29230005559742689b1aa1d6414d5b422e0c86332a9c5a857c6f1a8c5b911c85018d6b36ec6b5c83aa75f9a90c6c7c025e88d7081adceca07ab1613cb4611869e1d622f3b33d0a5d166fc4c3b4f7b42ec46b76c1e34305359f33268d43b3db5ff2800889d8f43b90380b22f19c07c7c38f199d859f41e470d4501c5e29c51dc9153b76964a2cf7c054d360e472260a363f5dc859ef68eddd7e9ec5c1cb127396a517be0b18370ee509558817a622035347efdb1935ea02cb9e654e1c1ecb08eafa78e2287c60c9f4a946be43f7d14adf8c303315983a86d7f4e4c1588a48064eb030cc05c2b66eaa3af6aa51b8f32e92f2e6b5fc55a2ead9fa72138e2c547cc18ab96ddf1794fd0bfe13306123fe54476b906d5423cd05f0dd72b90841afaa149861801cd2cfc0895d1bf2d0a2e65453a2d2ced8682e78e3d8a76255da354846227be9eb18a98b346ad7266fad0b70c9a1f7cca00484c79cbc383ae39c3c4aa1ea7992ae0e40b0c94718b95171c0c9c00273eaa24f81aa11a341e6210048bcaccb07b8c73b735ee8dbd0796706df803e2a5bb8b825991cc2dd4da543c110b6ddce16dcd2b81411f4f1a3b5f5dd1e5f7e8613e14dccdb32c215230d72a949b41e5eed74c1e2f330bc812cdb81211a5bc16b02d97a547bb3d5dc3b1521f1c308cc071144d6d6195c460c0080c54f561fdf70a5607f8a7090edab11e2dbdc60dad6d8ba940bd975438dbc1b6c0b595d1538ebae52b3860f569ea786b75cb7e4067754b2479d1ccbf9886dc2da6cfa6080d14efa3dd4a952d6fef83643ec807b383a9d65540e7f0252f8456e1095527ce440565152fdc2bae1bf1da201a4d2905021e33ac0692e300ce5acdc4cd316cf9296506fc0c3d1d9d1d0c9f936d9c0bbe7515309ce20411ef4ab77dfad988c38d96c74b1f0efab33fcabf7eba317c929b71704171e13a108714b4f45e09cc02406f961697747667d5db00adf870a115ad4366c96bd72bd009f6a1a194d205efe8adabda68d96a526d1d242856f28d6ba70b9fb7142f2a15ca1201ba572d6f61da6836c2071535accc9ffbbff37b71e5ee5e5edee8c941ec8482ce04f4c7fdafab9978fd0763d32888e9803641e18e78dafc0f89791dd08f4764140ddd6cf2855cd408c6dbbff117ecd2e82c05444805e57f703ac8222720321cbb4743efe56a76ccecfc226e4b287f75a0e58209f974a5da6a4e5d0eac8c74957dac09960ebb102b2b05ed6738adb4c24c14b24ecc2da436df960103bdc5d2d39e5d032fe162d205b7761cd49e966b83118f74bbf010cef8286790db3396ee24ade17720f2e2670b2083e1a069941b48de58a1872ab0116929b5bf1fcc9cbdfcb1d61f22a0634d91f48c056184f9c99a92d47c840469d32dca7c0bae9572c9d082604c761f0af30c1a45689de4dd4482653162324454e0d226407e1b8ec08d57ea9abe7eb3af3d5768c1967e091af01da1edb98c8ee581a6a2d3434328b5aecb9c66bff89b60d988ff9fbc9488e514e828fa6fcf1cf219f3d49ccf2805c09be8fe3a3da368f5319d4d16725733f812ee8b187621449b8a1ddacd7039d180022692aa95ada864c78994f97fc7220eb1ee1c762dc9e4910859c5d5fe7e133bab771740a063f9f71ac03d32c138b13d2872d569b58393b6cc5734201ca4a6820739df66ff041944444f4bee59ac051a33610f49f9ec1e857b61eea2ef39eb73425aaa73a69ff871be0d795f9efa9d52b7a8d40dd1cb2609f1e3b718c531f416de2d142ebede5edeedf53586e907aef3a54b333579c6b677ee1f6ea111aa616ac05d6892c9234c4f348de55abe4633f1ffd9fe1b55b59599d2f0eadef785cb0d419779b2e652d1ed6bdef3a9d57e2f4b98672ad88a3583a936e38580fc2dcd0ef8311f79ebf607c8d611d9554674dad6c27e3c05d8d318a1ce1f73188d9d327b10fce1a709c2368b6e1049d0bed51e2913dbc2de7d348a777449a5256cd1158d40ffbdbacd222073591f572df5f1ef1239254b17e50f0b7b0e88fef73a091f732733cc95d69d319241a70422e1a6525239ec872f0bf670b7337f5f785c8efad9636a666cc0046902eae8eb0e95d6e40f2c023d5a405726a77d1a7ad3ee9d6344ab4b48c2bd441c1b83410bcbf7da3a89fe3a26bdb079c9199a4067c9616c411af9cd97309b7122ef1e1fc7590268bda4837bff7b4e4d9a403b0cf78bd09ea63d5ae74df5954ea57d6d403cde7415d33cf365f921354f7243645b9de900480f177f983df3e6949b79668679552d1870c5968ab79bf751b1337c6eae8089ba8262367c4867d8ebcbfc3dc470f673408da133b67ea51bb93ea11abccd5f9e7735312454f604c2a692578ad619e8ba324dc5e849a3c8ab93c8466f0ff22caf67bb8b03f743af0e5830747c67be40c9c28ea584d52f69a869dc0dc14516a9a2b679244b5b70c1f186443f3257b6d146be78958decf68aa1b9d2de88bc908746c2d65a31a02dfa4ce02af64e38a295d83ecc49834e2f22df173c107213cd41f95e3e0453a6d536599b52f11941ef547b181e6136390ea8e8d4824321dac7fe143211af79e4903846767ee2f2d6a78433fb2af5ae29c8c930d2f16616d569d7824dbec08bb7108e9c621d364af3dd3f56bc7afaa03ecc232c0371532dc033fdda996a5d51069875623798dc94b4273ce6192e3a60bedbb3c0c7f2d487656edde1a2cad5a8ff7d34b27dfc579c8de7448bd2621ee1e47ac6d82d6b8eb90576477c209541c4f6067e7a0aafc1ac90035032d82dafe55f29264b4863ca5ccfd383357acc6b9a4c69792b4e14e30aa08f03854509e9040e5b1fc12e42ac6fa66c32324352195138f0880d86dd83d12b0852c75b5249b91bc339a5ed86f98330d064e11b449be44439a0529b556b76ad7c3405388971cbb083eeadd1b07b67c9fc660e852260e7c6bb686a2c095cd023a630a5bcf38a51cc86ac7920d3adbaf9c99a44b61033fb25e53366c67f1856aa6bd5c574c1b55f43f0df311df44b9e567a0350bc32fd8699f1ec5d2fc03600a2dce51917278f94f71e429fa273662ab5fc250af96b6854d99075458e225c1cc0d2fd5435e2126fca7aa6ba7ebf8e1a74f628ef71b98487bc11d4a3810492a14f09a113e6d191630d1eb4d44ba4d8380844e851e4881f208d21176c3d91509148958c6b5187b140afe3a8ce1380ad66c40f17b1b82a876c7d71cccf2b04b588de6fda296e84a71bad0c228d08ddd778a89bf075ed3326795343d7e0c046479c779d8013d217e559709f2d51a02024be9d861ef1bb072e973c62274c02aa6b6a49502537e90913c0e17c504fd6e6a586209b0452e6fb48ba97ff8cb162aade0f0d8542de017ad79d37979ca42e03c8ae5bc556faf2510661216cd6338e474481656d039c04d48eab98a09f7ab2c479718bbb6624236832defbe44cc122f5c36c1a178c1245b09c7bbe06ac468f18b085fd9fe21ab917d72c92f31e4a5be48bd2490750084eb4ed06a9a9d07ec6bcb4e34a903f013e7f429f25f71451659edd2f975ad0de32e05ae3364bc0979a6aafcba2692a36c79ea44041ec40d0059d5cf73466e174610de40335c9ad48167a0f7512d97a2b6cb335ee992d83b639c01d9a8fb01a9cb9598629172829f8989f70a365bddbb97d44aa990c5947a9ac07e671eefe3c8682017850d75f460afda209e387084ad8e11ed5cac5ec3eea25bd24443ae1869b030d6f41a6ff573048a19f45c463c7d122ec16bd7e32631af79e16359464da9380e800373dd4e5e844a5634bfa129e015663540ddfd8b4da3c5b9ca7f01131e151dba39a208cc56adad9cc70182205ceb56b9046ace1e2d6a244d548cfac5aece3a0077990b9915d3778b7f2441380f9509fcd89f0af3dfe54d44d05a31198716265637d75b4c5088be49930b08333469253f64dc15ccb9b995e15433d0087eb869b9e8d4dedd2bde5d1bb4eecea9b20c740f8c190f076dfafea718a3667afd9d8d9e2b2ce668ff6d346685a307d44584684982ba84cc3e8a213e005922b7777c50d8d3720fcc5649d9219d065e98d212ed5e989d10f96a7aa5921a5ca3f362c30c89d805e81f96922686377e4bcc5435b82ca75f3587758440af27687a408a0b6a15625424a9ed21c1ef58c7162d7a703bf5e36c061111adfee4c099351537b5d2a355cf4be00cb17832a03893de4543931d1f6d3ebcef84ef61f5f609ff7fb9f85d2793a787df83604b88b2ff7c6ad2f6598abd4d39596271b930005e23f602d042e4fdfbcef9174fe0c2b061f35472db3e97f10ab09a1f4dc3565231b48d39e51e002e5cd644a08e18db7a3be901b6f52571dfea13e6c7cfe179b52f8ef6423e3919905bd5e04daf08827c9bd13f6e1488cd0a04cfcd850443d5389c4ba251a3c06365e48cdc1ea5c2af27d262eab393745d1592c211b8dac952a930c3a2e24707537033da6f8d6c2e8fb07f3b0b0e6caf8aea09c8a105a8b99c2575ad3a1a4d7667e7344c1e6bc1d247c65f8e9770fe0c7a9951b645167c9cd1604abce2fcfb9b30dc15cd9993d5ece2203dc7a0a8666ce3dfe87c2ac8a6fa67a684e012f0a9bcc5ff25cb334f4fa9a76ad3514ff5114664557a32bc55643851f21ef644b0992d4f28e533b3ca43f8758cb88ea7f989d96dd91b965ef639ba60f14b892ab6de99bfba5c437dee44a6b5c485be7cc82d4a8ca08bb0de5c7987122a566589bc661e62792fdfc168c0fcbdacee2add9789f2e5ebc0ae92d0a235ecc7873ce1b40bcd74d1c3b6d5598aa0ed5b5f5007e293e395f2c2921a6e7f446a836155e4cafd075b2f3cfc60ea07893ef8eb40b9d59309b0601e3df57c2909340ce8089e9ed2325deffa7b310cab43ece4e4d0d27c773c10075228ab59e7f8182965abd0dcb6b652050b2d365e7b1d05f649b19ce7b62f47ff95a31d052ad4ee397b2cca64e292c6cb37dacb1aeb0f547ef44c998b4b1d7980441e01b7446e164fc643aee74afe9a6c5a8e69a0a34f1829750167641f1749da69f44809109a2a4c5d0312929049f38f9929fb7191ad69eb2da3e357757973d86362bc6c9511a347ce88106bce57e35c4dfe9a33d5d76a1a6d7cb74766356456b7a15a66b7d2e483647cb594768c48402704e09206985cd644a3e61a51bdf1c1f451a55910932e37e1d11e7e3d60929d96a29f93c7c382870d29deaf4666f9c6498becb1dca7a0f03a63998377b82d587aead568928700b483b71483f53a2543c1eed3b9b4bafaf2d45e10ff99da3943aa77ee5c2e1a84a6f0578654ed576171308d00096ab25c93f244ba3104662f94298bcb6a7fc1bbd7f3bcac219b574146ef64bcf0cd1feac4b74bc276102781f62ca04eb2c6d085012e0d06a2c2412ab1be32698f304d73b0a533c64920d8b33530b65e16fce14b4d615f1bfb236fab226c9c90b9448490db19bfe5d529963885ff0938a175e112d72c6c4327cef3f44702cdd7f83ba63ddea8565b001d6a23fe51eef3471f828f5568faed064ab3e2390c86882b5a6ee8782f0ec6c37d98acf968e5bd613da46c36bbcf9f9f0707590a19c28a509b27a8bfbdfb730032a64bbba14471ebea2c398a506612b785922597ee76139034cee2ff3e74a4d1eeb471cdd7f43068bad806a26c8d86412362793c7823e1e500fc4495399b0011a6988eb76f3be8047a849951eb253ab9bc2767db88e5cad1100b1fe331b68968198a2e033211de0c804009f95c409a4e13210a30cf12d725bae62b6f6ca513b8e7d9ca8eb0cb9b646bb178847fbd01b44b4fe1e6b15b0ab7c2962874d5fdf70a455f03d2e4454163e89b20e6a1d8033870e1e177cc6d856457c3624dd76227bf6b5d4f0c2243dadbc11235dd58a95a272903af1ee6671b636d8f47e90bc36d5c6e070d9067e7d81d504f566d040c3f193aee5b48195fc5910ecc4926d1ebb121c2d451d3e99a2386a69104bb5c15171ddad5caf8e09d7a051b33c1db342cc4ec3d81e59ff6f19983a44ca609a48d86a053d635ea49fd90b0bbe55be786c28205cbdaee73e6ed957f8e93b8f2a9209555609d3346a1b044750221bf76b3d1c10504ab8aabb28c0aeb89fbacce4dbc40afc27e0b3b9e86a3913cbcc9bdd343045e0ce24e72c6f52b6852683fd9a61533e5994c107ca65a0d81d4ee5322c0f7168e4c2b32eba8a7fd783eec9ef68d7f20950595988bf1f857bbd3e184aca1ee2e7f5f71fd30a1075e59ef1103345a8835806dfc6a8a246dd79b7551c26a9b16a7628dc65ea8c930abfc7a7cf04f1e475664e1373cfe6ca0396d4c750d114081f22d33d7981eb536e60ed2d3ea5fa72cd046c5fb110fddfb14466f0d8c35b2658eb69742505e7d1d4161f1d4c1b9af6e373fc984baa35f81392bbfb56c201ae31e2b6fe2a22680802d51d9ef98e7ea830eefa07667879bc93921d4bf4f117fe0d328d1586ad150b58685eaaf61e704a2fc6ec854d19ac27a9d236d88a1fc310c23255187c88a5e2f7684a02098fa671d68a592e2096466c5b87191efc3163193123feeaaac57b4fbb7d78352b01f5ddf29563946a45c325c74c70e2af45167619af91d10088f0699695decdea4b41d43be14e0b0fc95c150e122d56dcb90ced5efa1ec926bcb5eb55287c855f215bf7dd0159a7cfffab8b8f897ecef49764928f3792c5e100c8a3fa3044361276a2b987f57624a509b2a119c12a678d25791101d182af43f1d44749055116496fd0ad78598cfd78bb185d0a5b02fa302f3559a5f952b9ca6e639da088792470e04ce9d76c431d94f0633b7c9893c3a72e01289b8a7573e60cec814bd2e2a553eb1728a9f629cc9324f93f7e440a11a39a437bf3ed1ed4ae684904cdc4fa3546e583adcd55d48a8d0ad228e4eca534dfa0cfe35dddc7b817f9a01d9da79ea45cc49915a2935d6226b7e0b99417c55e29c070eca6c394f3994026fdffe5382a05ef7e348dfa260c790b9549d5ee560c7a83c1ef7f413a46885df2a15f14d81d83d4fa51c709d5ddd62a34b6a8d531e04c48920e525e98875cf188e8c800876a3186d081472c3e3c5180eb3f8b1dfe1b2bd18f271262211601f7b0a7ed0620c5dfdfcb981ba7a33b8b10af6995bf36b19889b9938a460659e2454ab63e1e0d423ad0d984d4d3c0874aba21623d301d12f606fc3296c427dd4aa1bd6dce71c0f8b31b1c2be774382b8db3f518ffa883b97da0dadd6dfee0a647ffaf1e89515ca8a6adeee729e35a3a3b43cbd4826397b4b7b627e41ca18eede322eada538090e4b924104dfa67909d1a4d4941ac4448d911e21c6068c8d094fe3359bc8a26ed66962bf618c6c93f9177d5f93527789b89ba3d68a9e1b467d3616e26af40dcf7e8e71e6fd1b88aa1340ebea1c0e8ed4400f13904be8e90cb4af01bb7be6b8843da9f878948cd705f2db20ff131ed1cfa4695d6352b78a2d9bee47e45f19a30cbfeeb561853cbb0ccd307a5c95c9824001f9e74ca4cbe5ea9448f3ad32cba1087603c3627ec1c11ce2d2a8692240d650735dd67199fcf92bfac80b38d535d4c40063632bb63ce185a3c5aa992b7a28b761f0683010d3a3c058a07d8dfcdf7e9714c9c639cfb4de8a8ee7bf4efff8c5fa8ded4d8d5bf9e00efe6edc2ffc87b886a071b6e65465192361c76ef37897f7eeb2d13fc3ed6fb02bf2b9b7dc973d4a5239cef90bf5e60fbf5d6fe601963d3d39fc92451d5fccfe61d8873bb2ec8e386d2752f7d3d64f3f01b90f6a8b13ec9fdef7842f7d7efeabb2e6dc7a2279860fb434b71fb32eb73f0f199ed35e4832a03f1409b38eb0b76df9f885aa7d741f777b24feac604b4dee56aa9412461c5784182c78129ade4459596463f7f53eac7e8eb8b15c2747a3dd1a848c6a5ce7974a54d6ded7b739ada83925ab0a1c7cf2cd2a1871513c77fc80f8ac1c8b12ee425bbbeaad522e8670902a42a597f04db17ce3394e0a1423ec869dc9de9f790da539d84cdf51c4b98c5803a02c6141d1b42c2fd896e4cdc9b9fc2854f5d67da69304f70bc3c4dbafa5354a0b896c60de031a0fceb8d8c1c2bebb7ebf1feb0d549dcaac1ed75ab7e4e6ca0842ba63a95fb17ec222dc836f37171981b6b15f6680a67bbead684b46ab654d35843f036b0a51c24a73d0ad34104b473c59cd7f6083cf94079024b5f18d50b9515dcfb63b4440189659718bd1e288348ecc7df7d80a9e8f65a8611049fd9d8ce67e831283a524fa8893c9d0d907c05fb8ea777c015a1b707008dc17fd1c2646fe9e85df960eb1e8c00e6dec6603ad15190860e1f1cf1f61a332f2df442b7a48e59fb5914ac307fcccd938ab4eceafd708bf0fde6d8017ac1d2e00cdc3c240978173f357d0f80847edc43d1498c1368e8251fe4ad885606a0078415e17dd3dd3ffd23d1385c5a02fea1688941bd1dd7e547001ae785bed2b7d02b117b4b665ce3306a37af2017009b41e56ea7b23e8a7c0f8ec0766a690f0d8dfb105156ef8042413af70ae21d1e75bbc6cf8df1017a144d736241c745774935344ee98b7c964c2e522ef4d2f71f7560704a505b14fb90e5b1fabe63b43a17c3c242dd49c46135d9cc8e1e118d401b64410fdb89d04b851870846e68db8351733ae52d52bac06fdb52efc13c8ff37187a755dabfc3018f6efc4fa1fd06aaf544f0f2333083af3dc6f335a957824187d3988ba57e605e43f68deca71724add64be8cfb852099ef7382343ffc9fd367e881f3b0aaf60f7c4d7d5c7b38f687be5027a7d675b827e4aab8856986bf77858fb039a83dc9cc4b10d281a01b026aa81cfd5ba674039c9cc6301ea3a7f32a4f28bfb059bc530d5ffaf4192a0a9eb0fd1137380a195ee1f3ffe308843862a6bf97e4a071f00791d7e7ad63f83ec2b22e3ddf41437fe96f1f32068481d3108e6b47849641eb7dc8e969a596fa374e1919517129d9d0d6d78056508c39e0df6b9eeb2982c2d98033193aeea3ebb47d7ca0663caddb4dcbd63fa8f6fe4e64427bf2ffdafdf4b89bc2625f4e9fb31e01e894321a337e3cd977aa1dc8fc5fe3fba2d504dff188d77e898ab92cf566377ff837fb106b8bf986c903053ec44f263acd65661423d30c33821061f32e594110ff80588f33315b06f6bb1893c25fc0a031cd0cbd3370f532c5572e06dd70ce7f3e9a1f0595c6ec10f5de3b2de433dd9c99e5cf1c6ee3762f30dca011f14be2629f56322724fb5f885e3785914a1bcbd017bef734471916ea0fc99cf8fe21fd6120ba7fcc1466ed1d7d570ae0e4e5dbc7602331de15d9ecdc821d65d79da065a8744cd47f37eec88287f9a1f3e8db1fcc7c88913afafdc66298b6f126fd1bee2fc3b0735baaaba2a324d607ba179b396e830e238d8d884e8d7bc018e1d731170dd447bca53a2b5a7bbaa8fbcee62bfaac97eaa96156d67395cd5140e54c2d2a2af27a7e2441758528d65e5ec7de319fcdab11ed2500de72962e81ea3d297683045dfcd54856393162bd9fd2a25313003e10d822011f3d30df0994a752c692c6233f60b2c8b3c0529bfcc878685bdfd1aeb6028971a0b16b86dfd40f6f34943cb19d1230e2ee8be4b346748377b4dab8de3a2456c22e06e0c0c72a54207fb0896fd02538d305e53d20b0ec8b7c8d506cfbace3c43a778f9e60b076ee3f6769cb070ddb077bce84044b24d69a8bdfd91738f48b62d1cdac43f20f7f56ec55794515eed0367dbac7d45f2afb47c04491455d41c5be1b78c410bdf11d4ad2a53b4c854f9b111babb072697727aa0ce0fb907f1d500f8436b57fae4511fb63eb9f84bb327cbdc939476cc577c11e6b4fb6c02f5c122e541003e2feef34b763424b3f66b9600dccd931e38d2475b0edb929f1288515b644abc3327e2136944d835814498caf7553c3f724d4c102922a772db3536562766d0b06b8453ad78d139d426ea5f882d11d24bdd728e1432ed77c77f3fd55b740c29187dd6f2a12d94a1a9f096f5cdbd00d754f24a23efa621338ff70c9aca84e4c8673e6c20c630d3e315c223fdf15ec03d1968a67b7593d1fd6676c5b586a40352270eef173badbc307a6be314255c84414134650469a960f0978dd09b711ad5d1dfedadfe916503e9c7d2b31b170012a7192916e54d541ac5ca57a4571c5aecdb0894084d0b95fd4673c630f7b5d55681174526928b8a9159fecd90c6d8e50c5f2285c1a10de52e5a3b8d273e075bdf8eeaceb75da6ad8120a90e252d9880271d9b61469d08c8fa02b4ce64a3379c1bbfe1fba817488ae9373e425e0f1974a9147efa9ead2d4105662eda3737edea4c9a63d17a8e567aac50897a0ecd72906c28dfc2e41a5df5dd61c17d7f6a60604fad402c8cab492d26f551585c095ce39c17e0fc598a3140f34aaeb6e486b2ac5d458a2f1cd1c54b503d34623dee4b93c9dc02fcfbab72f621cb0e90256ad537da4a4afdb38ef218922723d1551b8bd581027ac79c5578695975b096d0200d48b0ff1aa6548badd57fadbc6b60648a4f8f4ac2699d98343e7dd3d0632c2a0cf7a6b3813a0e21e8eccab9cda6a91dc71c3c42f3c2f564d9a998b107b93211ba01ef7abe7bd8e991bb15daa16745427db628ce4cb393599bde748c1731260609985c0e1b3dfa512a42f051a7510dc3e26a387ecdf236569ea6cb1b469a72336489fa5e9576c3d93fbb9d7e4b44216865aa75afd9c27c3fa497910acb90d1e36d3e958fad04fc2a4f33e25148ae183579c210fa8bed9aed5661943f52dbb362e25b036aee90ea2a23918228e466e645d878670c81e4b1f2669c1cacb0585afb9db3b64b7ca2f948a48d8204fbfdb2994e1e87a7d2a8a7bc49e553f844b86cefb2a414972f2a95b6b7dd650b18298de4122a258414464a5df39e162e4cd4d4de5aa5340605813ebe403367d7f157a93793f595f53aca8e2a4d7ee4074012a02cb5323a417d8eeb494eb0cc17033de1d08d65c4a4f9ddde4cbf28698699cc94163a910f1de124935da8a45019f05898fd4fc183863573b1f0d67572e29f8b97d6f2472f8b711b7cf173ad7f50d3a810e506a2ff93e270281a5ef40dde744ca6ca4b71e811c76d801457935455bdcd5f855c3d29d2a7d3b9d78b5c48b88df16362af28d795643c440b4be67229f7ba80c4bb4881cd168195b3a196bf2e5531c60d60dc6793fcf3866d691cf8cae131be330ac3c01830c027ccb88d1527d6425fc37f48b27e8afc958e277c4113910103c7d274b61f22b4a04f7b0c975385979fb794c20dc9b2ca5f8cd90be81aafc8448fe77ab128f429740b51918408ce4b4f3c05423e52a0347bf44ef670812a7a9c1a9bb93ad9854f1a5af49d0bff294229004042f513808a35fdc15d07533ec9f551ab32623d2dbfbe12029110a141e179e4d2efbbbd6e4212fa114b78ee6ad47d68534d195204d625040354bcfc573e98073d8bdf549a6d3485c382716bcfe1ac369898258ab299ab2c1d1ec2f3d79482903038841b46663e68ca4d6a4789f0ba25598530bdafd0783c2bbcff336a54a995f8f67d092546acc1150fcdc49b036bda364c1042cd0a88a19b9aa890001178a8b63edc7b773707e26ffca4bebd34c8cde6362a4afdf31eefb46bd79db3f49ade0b7a5a9e134ec2c5d0104d3848607b04818f2d42c7f07749c4d571d550c5023442af41bbb78512eab19f8f7674a126fd20467509033cd228564d178596f90d0c55260a20e153a6f5c6da7e1cdcdbc5a192200bfb1715311b7ce8f379c802673459710d3d2682b1ace45be05d605fed6552f7f77a6de7f6eeab7da6e7ce8f78a69d67a3fc478bfe9d7fece3df9f8a13e95e0cd73cbd49b3e371230cde67bf52a34799f978baf0b5ab12e5ffe67c2f027a8cb8d59e610e749a61eafd76f165cfc1649ef71f48647b7061e1669de833b89d8b5cf95e3384ec121a1b741b13b7430ed13e5307ac34fa1fdde82ed4f34bb3164292f40a57dba8d8a34fb76816e746a2c1b85197dc3081273fa7d23c6d296ab2218b76353f59388064cca506d4d95e486ebadab684e182f53a84d851e0facac28a7863c485c039e862ef22a152e74c17b250a9a10db26196755c0938a193ffbcc80e62d93468ea1998481dc5062a3d89ec69a2ebd7e3cc9d739145bad5a421896eb7db228eba4d5869885a212a382dec9184fb2bb12f43d4a175414e6eaf22f5c77ddb3488e539a920284462ead356142bcf3041cddcbeb22cf6a8f9c5261f769f22b5cee8f77b3a0580b44a8f0a72ffdf1251abc5ed4926ffc98851e150de81ff441238c21a8bae46611d933c3fbb94fc198502fc0d639b45bb04137fee531b1cf9ea4898b52fe3ac9f0ef9004831cd34490ba5b957070f302c8fcb117076f67ae27d846de2ebf9bd1ac7aba319d39f3b362c774a666225f58652f7dbede7de62e5d5d0858dbd5c90be6aa5dda51889584019af8a9ffdf3023f686ce85acf977a6e2cae426bfbc516c432f6cfe1bcaae638808e992e1bb47b3fcab9b00c75a8173c74f589730f38f4d6087b5440f413f12ca5612e095125ac4b300e424008c16f6fe99898d129bf596bd25b17060ffb061b3b00d5c43454bb21f063c7b03d062cbcd6be4ea4a86b8deab28c7bfeb87be73c8a8959890216864a6e34a58ee9ce02d5999ffc9decef9cfffefba7afaf3d92b490efd4a83c3483591a2748b66228cbdd1e7420fd46f1d26b0d8dbd83601e15a5d76087b89fddd684d211da4fc42a611ddac22dcb3cf6d6fea51dfc20f0cd3c3eba248ea9967fd84c248ef6b434b16e0b50447329e1ed4ad799b1a0e1e89dd434cecae57d6949670520c67c9398ec2220a77efa2e1893420bfd9afb9d50b2d8e5173236941285a45670292538054ee4fdc9effbad56206d54a78ff9d12916ea0f1a44f8169e209a0a3ee0fa71fa1153ab5ac683770a1d48e59da4ff59a8feb9c364f7a99d25aa57cbfa45b76a010cede3ec6219965f0550bc1443f8c6010a8298339343a0da30712d740cbc81d24fd937674451a1993bbd75aea251f0b43e76a4333aff96ac2b34df28ec2a9fcba1d5a87884ee61645558b3074812f4f53b0884ec568f5d8517cd01cade1e903f784c6e78869bce9e2093253f6407452ec3ecd9657efd68191d648124c2392f0597022c91ff866a946a2a1fe2cf37d61b99ec177a85efc6cda315b1b3050bd70186123a8d1695e5d927ac39733d84024910da51c09a72aaf082471980db826fa885d6aafad98aa269a65cab6b0b2f992940c0fc6fc1fb6ba1e1d79ca2bb15c0580bff044d6076722dcf7f8cf5fba79411e6319ac2686fa8d19298074af70b8bf8a241076ce88b093cdc27699b66127a3031be25b6d82f0543a5006033d02d5678a3870635f651351448ad649e90092cbbca12fa80262b42a9c2903454e30d56a1aa586d20f2494347a5738cbb5aecca0d6b226a7be8a595337738b70ffbf523f47c815732b0c02682e417c3bd3f3d5984cc04daee30c98b5d497234c12f598c79040054874b3ea8e01d91b50080a498c2025cb49d590dd207a6ccc7fe26321814095f28518458d7016925b4b39413882ea1fb1b2580e58fa7e69bd549e1b0a1648440ede71cca2d68f0fba44283f498c3c5acbbd885a187888050677f87728a62493270134ee7ab158ea1da10e82f243908e25e8908885a2746b8e448d3a02c9c3d0026f36780c81cf052efd83b1c9ffff815fad11688a308ce38e52c632eb9e4cb59c26e22e8a01d8cba3148c1f7af3ba17597cd95a3c9b78865fdff3ad553d340782be52f929d83f6f4d6044ae82731e51ddc9ef43bab1e4ae9a22bdae4e98c7bd3de47efaf2dfb54719408e404da99e5c834b5f3b8183fa553fefccb5f2bfaca3a4e5338a7810b4fc1d0f97a13403c7599a607bc7bd121beb627c83fd37283487df756ea7d38b8db9fe57ba7f9ebcc6cab3ecc7d70127a0f0e80a111b4309ee7a6118085cd548e8b797bfe2c4570a9ebdbfc2c44cc8852f45d3f7a7e6778b553f42a24c6c299b5fd959a111cb9c97b73c23a13dd7e22b980c645e40cdbe23d0ed56dbc18b95d03ae0e698162182c9f6ba881a4849681c3ada9fb6c906e7eecfa39e3d43780852e42121622f84235255fb3f7db690db4c3d1fb8d86e8b8c4eed38e05864fac37d926c0a29c4aaee6f316e37bdedaf8eb1fd3d957b9d0a24197a3539bc9bc76bb314bcff9e84db94bfded0c195e28254e6042e6a563e8bd12793134a919d48d012fce325c9ba590aaef758e70bd43757a60dc65a6a9138987008bc14718be687ba42bdb10654a00bc4a644f65e00a61238e892a70ee5d56d89b1345789a6f7e5000c82e5f9763385e62e1cb0864e1a2e89a7be3073adf92c4e8854a1b336c8c5414a768f3ab4a9eba29eeaefc1f221ef954297e597ba3615d3e3792a332ac892a0899fb259039fba984484cd435c1d8bb01d4bd3e63581dc277440eb28be7f1966315312ed1500e9b07a39ba5e6ad63ffc146f1fcda7aecb8645d2a29a7a4128243041bc1bef1618de2f9746160f47c28f6852e466e2558ec0f27607e9ace220347b6ee85d443c8865c1c782c88f2cee8556f6d37b7a1de249d3a3c52ad60b4c7fc83fb3124a6c7da2a1ecc2e9a1f0a17a5568287e0404c60c9b108cb381325606fc88043352579b5e3c11e84478e8ee72d9cd989e4ff6e3aa1234f7d01b6cdc03fc7c7e1ce17bee16878301c8d2de0f64de9bedddfa953e85570aabc00df6c5e8844c4ae92b82b2001c16a60d582f960e73514e19a1d7d97fb1cc82ebeb0acac2c1960c8144a571076c28caaed6cbf89f803a96254ee3588fb6c5169025046de87a04460be98b2f22da176a460464229239549091ff904654dac96fee466eccb16247b4bf75b0e4fd1feac1a67548d055705e75f82921562f76c5d3fe9823c76702c33a8b0dde91b4e9de9541882b6dccb1d4bc1fc8bca7d7bbf5e930f94caca5a7d455be1ff3a05b3d3b61d6c5736b9ee8451623d443bf977f558f425bd6b9ffd58cabef15e5f6e491df483d4e1fa493d5415ddfe79e1de7b0a0030038ce77a30822d93ff8605625319f6d363f81d237836a2ea2eef526c53c92d7a43100f0184195fe42e0636c0d9e7dcba9f69d905094cf23510a63003a30d874f7aa670566d89ecfadfd4622987d5e4ba5223828cbb662ec7c2fbc881f427c354261d028688879378dd18a404fd82784e63b983968f3a8af2d76ac10c66f8c8cf336bcb4292ae1bcf565d0ac5760f324bf0a78084515c034b32c8d71fa53f670d0cd479c43f16aabce9febb19edab9b60eed8de41cd5989a2ffbb5bbf1e32936f805e0e3845abeadb68c05ab6b8205c860b73f76572c45da3cccf247d89249ba60d3c72cde0ab3ff9ee1b025b3948adbc6e02179905eb6365be80ee3954ef3a69bdebee464ad0f1a49584dbc88c496ce703b18436259bee9eeba3596a13ffbdc222103788ab1d322749016a8879f4840f52841f018f87f09efa5dcecd0e34932d73fb92ba4b9580261d82c63eca0c56b0f1a6d25d01b70f661fc0f7d7ddc84f14d0addddb46d8cda3aaf5c2a47152135280cdc9bc45dcaccd52714e209f848bf84d1a43c37b8fdf9f834334f65e4ffd1f6f18bd3a0479d008bd179c9ef18c8baca0a2dd1e4d79d85858304dd3b61aa7d5a69b3910780cb355f8d01f33e13f4335a95a21a6729857bf1b2397d9e2ad270739354cf8c06a3137d6fb74b872ec25ed45534881cd5818e044ebf5b540210913abd90a0b29c28b69d1856f01e01eaea889ac3f67e058ea8d83d5395e3ca98a89da33eec58ef9ae016956c3c913a647632e87bc54810966d85442c0050325cc681b9122a2fc5b842860491fec9eb277a0dd3ad7958d2ecf91bca76bc0374bea8a1caef412040258e846f2ae86aae6edf2c96c7f6df29b9bbf1fe39249cc791304b4b8a886adcf68af96bf853c6df5a1c3d48289ea36041113896880178dcfaf39f10422de08a79af3272a95621dbe26231e5e5e9101d632291231d127fcc4481f6238640038bd525ba5dc0e18b57661569bee11712711cd0bc4a70394f55207e7464c7caad5888e680baffcb2810c7d1e95314e322b2dea7e024132565c788b293162ed4b586cf327b7c1f3684161d21186f0e3b87f160517884e9f2c9340a8f485ffef92129024c15cfc82226102e74560bd567fe2559b679526acf95f6b8a75a0601b81a91dc6382f59f912bb40211609d5d8cc201b177e7695dd2a8b5bc5fa7c19cd92c024107dce6a004d031f217d68d86fe4685c850abc2fd0f34a6b6bcc654317b0b125be78b863494ddbf6519b245f927a4673869f7213c1458717c6fda6b884862e74b6523de7af15634197513fa2b4706da848ab6bf8280de1fcf7dca7d7b127075bea38de190a33d7812ad6bb09f05dd500f75fa9a1d3eb76a31262f690d359d82e437d4b752c4c904872144c847f0c7cec02ceef18bbcffea4f7b79a966c6b566bd0d5278d48ba69a2e8e286422d8df8647a78755e743e06bbc19cec962033237b3134de52fbdb470be8c6f2de7fbcb0619fd679b4cffd9c6f830bf0f2fbe19fbedf0d8f9ce85187fe26d90b3af33de3c6b7d269c1a6b474088596834cedd819c29566442d925481809a5c6b4177062179634d4c22ae52cf659f28ce0b52d79ead2b242e74c2d7e4e30ed7839639a4222cd41b90e166592ca0659449e6a6634cdd459b24487757dd2c21c77f6df3ec3bf3ec2e2d387b0f9f7115e3e834e4b58fb3143e564a689acb3926e42062ad3d4a4e6b0a455dd81427059aeb8977ce9f16e3f1b51cca924d96924abacd385d081cc1435d724ac695957a0094ad3d1b2ab3ee3d0bace40117453a2998db18a357bca834bb394230b9b63192b760725a0399956ba348d452cd889640fe592d9a7794a91810db18c25bb831c109c4c539ac94ad6e92454a033859a6b0c2b5ad725d004d954b4540293ccf7109555739550b61be7fe4f4eba0919a84c53939ac3925675070a4137194d6c8e9558b333a880e69434a791acb24e17424750e3bc1aae00be0f86ccf0699a5264b349ac63c9ba5aa0074dcd764f39ded9f7248184fbc73575917b750977d619b7d44d6eaa8722a0a8ad3148bbdcdf5cafebdcab4bb8b3ceb8a56e7253dde1ca2a742c21ee650c6ecc1c6e9299b89629b83393887f64ade69b7fd83bc9e214912771c7ffc63f1eafe0086b5bc28f082e5ec50dff10d7e8bc8fdbfd092eb178074f585b126e05b0b615f7fe437ca3f33aeefb27e147a3e5a5f873c6c4fbb8ed5ff089897770cb7fe10743dee166c6ecd51490eddb390e4ace73c4fc1f6b8b8cf9827d49277cf18a93cdd959fde0010f925ded112bd606cf3044294d4948489649e5bb3486bd5ab11fa6877828302ed88ef0f3ea1012a9c89bf504ec3ac41e20554768931d9f4d430457f5ee328c897c4a044650281a3c60d1a0effd447f59460569a605c6fde96aa58ba462391e6281c55872fa8c1e5ea89deb10c3b21e8a41a09d52a1cb95c9bbc2b30fef0a0cde320669cee13566186bed2ec1385ac7208d6cdfebe27d0fee09dfa9e73bd6594a84a343f41b6369736d6285a7da9cb5c97bd1a77b56ddfeea0293658c6a7ab8a822d0d928237b5376fcb37fbec6101e2be50f2ce0dc4e9a390a62ec6793e8a3ddb5a52f6a04e5a24611d3526c86af495d23553a16564e4c47e0896825eff448b00616d7d47323d4cd928b92ca942754a2e3fa3e0025dc855e2cf29b51aba72f549d44c1f737bb9c2c5594a073c1673a9dc41777b3bea2e5b7e01f8cffc4dc2344dd65b5ee655a95a741f750c53c74b722e582528b03d3637f78f320a84c0eea6dfabc1b095776570d3546aa9f695138ee05aaac3ef8427320ea2d225e279fd407b68ba4e017de3b4957a877b1080c2f051a1c6ba5551048cb481dc419bc348900023f1a70d2ce4fdf84b8eb9df6510e60c22b079c4043120119812f49dc52926d915c2b2169114b646ed29e0609211eccead0076a416c002b2fa01770a7fa0615ac3aaa63748516f17f0adb09130a980a3838aeca18bc5dd77954a53e9a51be0a3df0aebace697fbfda8fe68b65ccbacddefdcf601da65de797beb5423e9a2f5d12d2757eed2c530fbcf9ffcc5b2bd4986cf0458d243576d4f8d11da57ddc9ae961c6872e70edd264ac65ecb3df0c2b8d308d6fbcb75b9155cc26b355893443a58b31a7f3a75599fb99e50db65579976c55da23a4afb6a85a6aab12098d1b62d2194d74b7b3a60b86c12a366798babdd9ea9756b42a9192eec3eeed06fbf0bdddcca042c50c0194e18332c69421a68c31baa3cfac4b57e1f06dfed9ec3ebf024ae424d10f61cf6bcd6a7f6cd17b20d2acf6a7525b43a544d622fdb5b224a5f740a42246943c8c2fd1bf50cefdbf507873ad94087bb652229c2dcad67befa3f7655f53de2532fb2db942352c3dfae3ca9268a88665139c9344951261fc9277c9d1e37fa1cfe10fbd4747f86d32fac2e13b9917dba39cfba29cfba2f09de0fc4993626c33d89add274a193fce0edd3fb22f2cef926cbd24fc7368ca6f72add03c4ac2e13bc9b92fc24119cb218cbf7e964393063d96b5a30c1acdbfd1bf59223bab2e9b4c5e5aa593ca3e4868565d299b31a234be343dc2bcd336627889a1274615e3bb6f6ee8cdf5fde6c669955aa14fe9055e3aea2cc16c86e52c4d01d304992e3bc4a0bd048004448aa040aed140fb73fbecca9f245bf993e623e54fba7d76c5f09dc4f06dd70a11a28ab7cfb7cfaed8c2dfbf13bf471eebf44be7579cc5940747acb2455569be4d6687f287def7dd3ebb3c9974abfc921e83eefaa4ee3d5ca64386b8cfac0bc98f85e0a77456892b4539ee1ebcb9f3786d4c4d95272b8d6294b31c83f2e806e247aad62659fb82b1f0537ce34cdaa4200d13001105036cd077809d04e80691d66b2608d513021d700446741b33ad0292259e4075d37ce145172a480117345b74d3a0a09b468b6e9a2c684e1047b5b30faa61d9048661f6fe1518be478ac49c556cce07fae8eba32fdc17b93efa8a2f843ffa8a1fcd9686b3f3a3af88ef7f1fcdbef4b42693f53f2b3f5ca9a4b53cca2c27bef199f7387bb1f7bf5dcad670ae4275f39344f8a32fffe86b562a44530023badba34cb24799cc479af4e768d21f2c148045cda4b14963b02b76982efa933dca247b9489962068a2e0497f729228d664927a3497feb119efe370a6b59fb53cdad9ec2988f112ae56a852a248672beb37f9615b338dc63e866996f28abd334cbdb791c6eced66675428769dad46422e2197f8c8eb0fe524518cb7af9428563b9b579542799486278d75391daed6c3980a7761ecda8c3f0fbdc7577ed867f6976492c7db673af384132350a17193fe5cfae596645bf4f466d86799edfdb2bdfdf43cfbc13e10663328619e857d36cb608fedfd037cf46fb5b7cf343f9616e67d1f8865f47eb0fc2118bb59fc38f9d10cfb8bfb0afb68fe6886658fd29e1863bee7655b675eb0712237be11e360ef8fc49cd50d4bdc8a0600432278234b1a5d9233b11fb8af3d941450a7a8544a7c94782a37292acf7e88c5ba4679f643eeee754a023c3729cc0727c0a287133a28f199bdd7c3014a7a9c086c7ad64b05783c691f61643c651713f9f06841135dfaa7ad364de41e22a0e32b25f169f38148691b1080b9e1e5026a1db580bcc7739336089e0d088874cb1b0e1675b4f083046614e008160d267cd122c7e647005f0837365b70c084b0e504590a5bba1b8709299070d1dd3925a460730c2055595c701b264ac0305aa5b78ba6b100cd966e1a22ba692ad0365e9eb0f1d28566886e1a217ed050a0bb939e38f9e40df67f73ec9344fff7f533dfc6cb0f9a0948404b10516880c8d29386668a8d07c8687ce377cbc9dbcdc603583acaa4cc03f3fe9267f1916e1b0ff858628d25ccb0b7bf36cb2a78b3ab70ae7f731a5fc2a73d4fcaf08d5dc2609b2e46d41061d3c547171edd3518982140960b588002361d38e24193a569f5d2257abf9f1fbe6dca58b6d5d2283163eae59957ad27417bffcaaadafa184bcf28ff0d53fe244182eeeed136493c914406922022891cba553e74f4721f7af272157695d2276f3e7fda6e9a1fba697ce8a6e981c70e56a620510211708056e5e5d1f11586f114a1cd87cdef217600432e3cd02aad34276f2e7f89cef8e89623061b24b2c81fd84356c59cd4d0028d378de7d96c4cba6964d348a16e9a8ea6a3194233e48e3b66ea98a923d2c0984b4bf3a4774b867e5a4d805a4b908a94b098a167ae68a1674a3053848117bef131b06383012246f4002f790a158d80626344ca8801747b9e04a2c99beb3d2a976455f23c095495b22dc22e4f0281718110ba3d49b34f4fb3c0025eb28d05ac74871ead66d95f1a14e463b3050536450c4002a12e79fe937830c5f773efb323f452eab1d9d2753b2be64be6d36832f648f7eb7ea8f32ca15099ff4ecf7e2aec2e7894e577954e11558e8e5feb39ee954a8cd155465c55c4553a523ac73b478a449dd0cbe5e4142982e9928d5d70becce7774790384dc63c2f85ca4f63b1304dfeac1ccb661da6c9d80a77f73fef6bb6ae4be938919cbcc5146c885341b912254e45c789b84d4a8755f865f4e6bfd9a7b9ccb79b95fd7579b514c85df06871b95a8bc32ae5833c5d94165152e7337bffa3defdafca5c65be5f6e89bbe091266319fcfad96796055bd1db6398df57cda75e71a6f826674c93d42b8d5641b779ba3068e9954b8f1dc3e2cc7b8fca8c3b9afce96832469331a79e57fad3d616ce49222966b2348bf58a81b8a6052e4d0071c40608158008a1fb87357e28e3872b9c855fe52c95b3f04c1126f460b32442f704faa186279512c5ae739bbd028e8b704e12e516ce0ee57e5617bec92b8f28ac064c0f45b8c70e363c84a184fba2595d93c6aa92cd606bd29f1a356c32571750c436991dca49a218b1e3ccfa5f52fce8ebfe0cf2585f56a98dde9c52c96b515e2d0dfa4993622871d7ca68fede869dfefc5969cc257e3a6325ced4c7424984729f955f9f954b998a27e6d743cfe3a4313086278da9306ad298e39b9c7bdcf288dd7966754c3df0b63c1e4df993946d912d9af68856e9a43fd916dd97df24dba21e3c39393aee13c3ed630a9a7b1f533cadbee7c99aac7da030df8af3dc3e671eac4489cfacebc4af16e623063ea414c9968c295048531540492fbb5628683e4ed65ef36ff25ff3ff8218bb80b0007145cfc66e21bec91a715f73046de8367b9c2fa34b5caa0a72f9b5b90b65337b7f26a3332afb0f4fcfd6bc3ace0e29d5bf62af90bd7f004ab4642f0dc2b6a85ef0677aa085a204ca0844402a105298a94112e166670e9b8f88951e423e5cf727bfc62ac015dc2cf2c4be9ea8c8128bb50a4960135e0943c77110c98300c346038a40e3072c68f01044b541b865279834e90493725fe494e86e3bc1242c617682303b416c27886d722f2cdd17d809da092675c4b15902c35953fed03c66a15736d9d780b08cfd9d5958aef6ce6095c232f8d1d8a4b10a56f0b35d9a15fcfc6110c39e36c12a39992bbdd50bc696a82761dfbf0d276b19276b394676565d33a3991146ba5608e36ae9fd59959e26f1cce893447866942b7efc1403fd3dc232cba099d1acd449f8836b5836c1ffa13cb2b1d6edd259b541322664ab980bbafb15b127976e577aad385f56d0339551efcb2df1886f3878cabf1ff83689cb5582367806eddfe82be2c7587e61ac52da0eb1cbc91b8bd5e1ce67f3bd866593297f3c0f149af2c706d9972daa4a3c623550f090000f20a850d12828b589ca2b7cdb6781800089b406fa570b622c73ef3b3a0e8379cc347604c9ec2f52473149c1d2f8c6088038df558332e078d5ba4d37a0d1dd9efde48dc562b16ef0a1bbc3b7b13e4b0f577973f7096e40e1da283ef8f4ec105170ba6f368946abfc759e1c0cd66082e2a5bb3114af4d5088f4943f4d4f74e81e8a0003e2928d659ccfa7343dd8bca691297de9e6a1bb7fd4ecd0d77e4a44aaa5d8a3b61b58270cf6d59a5e4768495ce01b33786b05212347072963420729734337120bdd311060862071e4749038987490388ae8207184d141e2f0410721a342072103d441c8c43a0899b08390e1d241c8c0e0841d4e50e60520657c20c34577f78905091c7577cfac208709a94b371bdd3c4052f57c7af38f0d5658ca03e887470f8f1d1e383c5ae0d1c3061e4348613ad28126073caa697c230e903146471adfd83e42cf65cd50778d931a665e5034c50d13d11bbdfa51d3240662d8877fe65f7bef1213518d690807d350adbbdb5947aaeebd9965ac1e7a354c3f21b92664e66bf8a1060538eb6f569641eb2a9f954aba8469f78c0b78504dbc709171d5c01586fdfbb96a7ecfa4a08689104f7525b9869ce50ad2a48d2667b8ca95a8f09b9a2ca0490e98fafd0f66330ccbfbb6080826466c214573400b031020f4c5fd25a3d09b42551a87358a889f1e59729b91c85064f57364c97d566e6429dab01cf258adcf8f859eb13caa2e5bf4483533f0aed29323a69878a2e8eec7e050d1ec322c18acc8c89584b4e4098cb56488204c3c4033409a0ed30c3998173cfa015b70b48ed841d2d5b06cd2b9aa4a243afe8519e59df4bafa1bcd4f73339b42c5ffc20cebaecd3229d4d1e80cacbdbceb7c663d3a69d30c5cba632a7c1bcac39cbca96cf689abd2ac4a33ef936c110c06833d71721f6662195d0a7ef56f13c66306043128306104129c2146890148e8ee26a10914ec8c5087aaf66303b2813811471620345657c1e5fc280304c48d12ccd8c93b2fdb4ece4e096d4aa2834e12268915d9da0bbbb71b976e8cdf839992f81026c9dda624af243da624deddb0ff599210baedac5258994ea3c3f49b9064e179d24b7723d9d2f829121dbcee4672d4b09acc7e4876ba1b89b70c74846f83c9904577c3bc744faaa5730d93618a0c418d6132a01acb4042f791326d3a32e648982360ba1b049a235b1a85361df1da6d9ef2a77b660440aebdd787139311312623b51e130c5a4c4654353324311999b2d35d93a4bb064977cd901a196a33b12504a2bbe6484e4ebebf54adecfb3a10ae9874dec03762d8fd0f859a15e8fc2fe4e972f2f682f7782a276f2fe0584179f4658ee356ba2f3362ac718236e56409ab94535a2ff43c82d276d718e9ae2952b30202681c60abcd1a0659acf91567f4d3167552d65da3d35d9353b3020d269c283d2606b18c68648a44daffb332ec1566938f930f85860634ab15cc8c11868a0a26a0d04256c159a6339f71c244a4892e6284fa1a3338009165a6015f835313bbbbeb98d4c033dffb98f45a33249850748861425d81da407763eaff85d883504add5d429b502d136ac7934b30130a05d3cd0f6ec62ce9ae5975d7a84c37b91a22606682689a134c336e74175d9051230210244c3144c9d7621a23189248a1ba6b8644dbb5b3b6810986ee5ead5635a91d7f9cab5ae1948f849348dd3528a1992a6880e8aeb999b688c55a329b19d51f2c6b4add9dff8ec3e057de7cfe632c7576925c480db1751454d44262626ac1089bc9c3e8d6c00c2a7423a04d0ed44d5cb7cfaef7645509bb8432b64df21d3c985800405b9af464ba323d2a6a12667a41138da9b1a4cdb4615ac1d5b44a6158d2ee2f59cfb348b95ef08756a9490535babb67f4e8a9a4d1bfbfa4bae04fbc602c85fa2b3f7c14b1ff1205f151b4c9646c7efebee5319625cecfb628d779a3fe5ece8f3178c516cdb7d9a5478a8149f395804ca09da5d32a55455a7c57e56e3949e431e7bf791ae1b8acc7209757e93d8d4fef7fab188823c6a0f739ad3732398b81385219c5f247ca6287531e9555d0ca3c59cb56455b1eebcdcce538532131100bf186c082863b31b299e66c8b5ec816853619cd493130e85a21a4a934b11dca180c9a8fb3b32e71e52451adff592d8a14799c55aa92bcae73f993e2ce31add2d9cc76dd0d0a70f7f1e9915906b9ca553e3d3d375a4bfa5c89bb7b029cc5727f8f7e372bb3b5eb3ac7995610574b1f29cc7452d00afd25eab5be7f27b44ae50761dba6005c117f263118fbd05501f0e96e619bcc0eb9db6432cb20a41e21f868757748e7675b27fda9c940fba4522793fef43099806820aec29bdd6b62e8ae81a1bb7fd4bcd05de342778d90a7f73e36fd48c3f4434c37f1e79c248aa177a9b4510ff765fb6149636e3d70da5b4d8637e36237a31eca6aff71e7f829feeb441eefcd4c1db9ab1ce5d1654d76c14fc96375cd9f7549c69e87e9129df2e6821936c4d4800e1edd261e6faab181a9264b4fe08c3156b0032d32d0404031470a4c8ce916c8d1ad563830d8473ffaaaff199966b484d79a663ad34c4e77e70fdd3523c2aca09ddd6861f3e7fcd8ed592cd887733636ab343592a96d30750ddd5d73c95af2a62680a97fd8dc5183ee2cbfbf77c880ba03053bcab4b3e67dcb627df8654a76987ae98232d0ae6677a9d2d5ec2e09213da961d94408e9c9acffe5c79e2761303a96ba8215fc8109dd1736a78c81ffb0fc55603110c3e694b1fa3110c33205adc84147cfc7a00b06ebaef1ee1a166a56e8eea0dab291e34877c7db67174c8e0c0bc118f63c09837d36db94a1dddd3d58404607dd99e2ccd01ca9834c945e7a2f16a320ac200e3a6ce2e002840d1c62e048011c5c781873ef32818e0247149b37c6d8bce145775fa11856c359c3d94d33821ace6e9a1674370f1a30fab3d9bede4801cba1ee4ed9b80fbc9ca8b3cad1dbea671247a08bb638d2dd5fa957a91b5984a159018f14ca0d2c1550c2c68d26ce7a18cc0d1368957ef1af6ca34c1b61b8bd6d08c10adf066b0347168bd5060bed4a3c7c1becb3d9bfef41998edf835c0fbebbdb59e13bc9580ed9d7910dcb97cd608b8d31366c4051f36a69fda1f09de424910f8eecbb2b499756ab95151c9c9b95457a85de98cc15276b38e270382124331c41e22c6751e956ce7a59d491c103a5c47469cad5fd259b3d59ff734ffeede6674f36e95c7a3e458e20d1d1c1e97156b543808e907c88bf52e851ea819fe5ac9f69ec7e92a8a893429318fc2bab367cdb12a2169290db5cde689cefd8560cda5a845d46b3bc59f7d9cd6db6f869357c2753fe446c33fea84ccac775df0d8cb9ca2679ccf5b1771fcdb8eb3ae74911711e1fe244fc7f0643b912ff6886793ca98f6694cc93329812cfef05b2e658d65a37a875772833fe25ba74418cbb01aa5b0d31dd1dd5006352234babd175c720d74b8d02d8000536f8bae36ae63db66e031ad22813d3e09246678bd2d8498384ee1abca03b66fc93c57a4d178bf59a140d2bd028020d20344ae8e28c069cd1a4fb263fe31be954761ec6fe2f36430733aa98d113432fa9a8e8f3ecbd32ae28434b370dc6a08107688044031766d0c50c8e98419319b4c860838c1b9021c4adb34a79a3cd64182143003220a33b7615c459cf6bc94028063fe81203a518dcc480070cbec0e00a0ca674c72e7f52b53486de4dd6ce57f1bd4bd5f762fc9bac281fe244fc314b7ae0edda99981b88d932eb1864c6d0c018750c14b410c38a5183182fbc00062ff0d21df14f1cc69818861017d8e18211b800e602a216906941981674a005575ad0d3dd75b250de640dd358579562e881a10318303062981c84f1120601617aba3bce2cadd24a6fb95222167cc10213b0a0b6c3821f2f1bab5bb522be76b602225680c30a4e5f50f1c5952fba2f72ba6705595dbd600c96a5175478a14377ec028e2e9ee822d645933854a0c4ec3a0f63f86bf95a9f5915ac5240c7ac52958214c890821e5c8ce1820a131755b828c0166d6c91832db66c81650b225bc8ce579d12c79dafba148e2b8f187f55e2b2ce572ce944fc73222e9335cf37e9995679f31bce411b82b3cffb9acb0367df85f86bd9ad2c625a05673eb3d251d04201102da2d0226b21d3c294451d597c208b2759d49c004c77c7d0fb259cc398bd559c8dff379bbf0fcf5c33eb5dbe16e75d8797c47ce5b7ba3f3b410a587081851558502c6cae40e38a1eae306245195680b1225b81002b7e7477e4e1f9190dbd5ca3516f5639b35765894c5001135013d06002ef8eddfc2bdf9385b55502232588a18a31aa6041153854714345185460a142670a1a4ce1c4143e4c219b42680aefee88333ff7a95ac6d94b7bcf6619b4c4abc4602dbfac93996669bd18abc39fcc94ba32066f61959daf705f3b5fddce579dd792628a9b36492184149f143048f123768f71dff92a1a01918008cc1d603e00468299218a39a24041146114a88eab0378c6f6f318fcef91f3ac32b647cec23f2d51f6308d753ff826bcd91bfbf953fe6cfd60896716090a16406105144140f103858f66c58ece269db4f3afe5fc18cb59c10bce1cd3a0fb572abd557a9b128669e8d303c3f46fb2ee80d893372afbe9c957adef7da55e75cd87fd05bf6b9f10d3fd04982770e82cc312f704932748e858cbb8af2d27c2b49ce812e67c4ee951aec526277e447c932b6d89c0daacf23c1164114127829d10a4417b8f2ed12b65f83d09e4c362bd90e8809e040a4100be60f1654b47fcc5a6892f9ac02156fb8519d6ddafb6891540c00508bc74c77ac1f94ca0c104154cd4989031410213ed65092f4a5e927c208d0f68f9c0079078604cab5498e68f41f198315805b4a177f706b4a12b794c752c5606bbbf494c5561eebdd92f61fbb5fb3356c37f672ebde2e497e481214b9cb1c40596b86189205dbce8027639d279f2afdb7a1fcb9faef3997d7545d1debff2d11aae76fe2cfe0533385bc90e88a0036007541c0883035dbafb63b055942914be3435511c686103606c4089fe9f3ea361ac2a2951a6af28d17a43031dd000150d0809230908242193084006ecc88017549b32a083acfd05bfdb79540ae5bef355adf3d52ba793208dc66e12d3e9f3bddbf92ae20b7e5d18f358e990213163ebd9d69021ce6252e42a426a418917fce487b9c4c1e5082e52b81c718981cb09486cd11d59ab18e66432f749ceea609dafa2c73a8a298cc2b1eb280da5f7352c6b9efc6b67d585c40e1226f4116558dd1161aad1115f3e893b02cb6c1d71a46593ca28988401336618d0a11d031d064830c2099311b9eeba6b8196c4dc1bb113bb9fbf7cc2b7791e8ddd3f9a5616be932348409f77d223fb271790e2025cbabb94ce055edf4dae7fabf6d2a5d8ed316ec51e8a05e6b0c0062cd000a13659a0c723b6b30815148183a9889dee8ea197a3e1db6220063d0f193264487c1bdeb2c496275b86b6f46c4161cb08dd6d439b88a882082b2622a274f4bc1bde0cc6727d1abb1410caa873d94f2bd3f1554cfd12c595c5b214f6e10ee55d2a49c7575d95b2cebba4eea795012539110742194d2b23e2c7ac550133ba3b7a1e755281fc49157040a6b7bf6f93444b7c5e8f65edc3f8de252cd62b8336acc13e9a8730a3bb7b882fdd1d6d6b88af3b0e41430f4142377e1a0dc42d21c07816220bceaca09d499c10b58f662148c0996fa280151d3f9a294081ee6e1f97ab6a58d62630c50488304de0278044026848c0879b1b9f59a44abbae8bf597b494400b125a685a9e680940104f04d10501640a0208103e00c20820baee9066b686e4eeac95cf6c87e4441ce9a379fe95ade21092d26a6725f45272e93c1fcd3e5d75edcc57b1be4d667c63abf3e0befadf2c931f3cf003961f8e7ca0c387287ce0a28afe61eb01f52066043d2ca0075b0f4d786883071af000040f3c3cf0d8e14b775d90afdcbb944e902ba6ba6c6b18fb68065d915669b7fa68f66b69961964b9c294e5872c48225046048488404f0472202006021a80c01104801ea0830708f180dc035ed0a14c77f833bd506241beeab08a85a77ca45890af62a7b37a75917a38d3eea3af2af35c255375afce67b6eb522f9451e7acd057b2ced67095f6368a3ffad12057d77d20ad6159eb7c15f363ef64a5511ca40314930e26981c40c6015238408b036c1dbb178bf5ea3c097aec5e9d7bec96c48c1fc4b8c562bd3a49143f9a5755d6b06ce283fa68f6ac8df9e0994f0f6c7ab6015da84c9132c5a3cdda56038e7832c5612a53eedb09622953fc6baf06c4d0dd3e2c960fcdb1f4574f4fcefe789e9455068460c262069621b034e978bd550c6dfe5aced622e9b46901255840071640846901411d73727254393a4ec493b2cea1f82aa6507eb3b7eb72c0410e3030e570450e52e4f0c59483027208731082d3e32efc04f957235f455fc5940ecac8573e3dee4284c1c2b7d909a63adcd70ee5b996c1d674bd600ad0a2805a6d29c08454f6280de513871e74475c0b073034f6c2414b96d3438943cd16e1e009a0a39b4902b848008d558ab39e5b9b31f5982901347cb5a62b7774479f57cfcca88811f04a92e98a90e98a4d38453833852818e47ac8f5e44ce88ea99cbca1a848c12b9a8b5da7137f62597339fe3ac7d9a3b43a3df4268df14c1aabd4840b010e02381bae8437565de7333be94a4ed312104b499608005e017a31af62f628cd56e9ade1eca53da67e671ec154764186c274f304bace57b1abf4365b5d5ccdacdb1a8e564bf3f568ae14b472ff684c0b6fe9ee9609d3206bcb8483442aafcdb92ff2f161b15e3d336b7b72f6a767d6b258af6b80394c564670adcc4c565c307d68983e2e114b5a938138d5a13c530fbd6ae3f43c8aa357be4f2bb321a6379c307f55be124cd60a4b3bcada376d2dff4d16258dcebaae5aff9877e473bf47f64f303662b1eeb5f72e995557c6f6a392536452296224f7b39aea923beea0a9b2a68a88dfb5428d89c796377c10be0dd642cd1d4a4b92d400d15df3c38f0661e8dafbd3d6f8d05dd3c30e5e6c65ba25ee8b90b051f1c4649bb1d5335376305549e3892a5d5a70531559054a15a52a475568f09945b9a9ca0f13f582461163a62dd8ea59fbd5d430d558505b8195dfb54238578a69f227633a24677d5a6b6684636052f8d1179e19e17fa1fa776674ff0393aafcae15c26096d70ad95975cd02b10afacc118fe9e98158e2686de12fddd486bb8c2f99abc4fd3aaa4229e912c694ca149594291e5b7707532ff2fbc38a98eef82d2b56b0745bc9e9c6d40a09dd31ffed6693be32f10b135ddf97eef881b64c77fc9bc5368b68bb3e591ababbadc7a46a46fd124ff5ab4e6e6522a6556a8b3730b1bb69e9be4589d35d372f4c4789ab96b63c2f318fc86687ad4b77bc56565b29980a63d6b6d45502cdfe7e458c803e3e3e9e47633e3d30d80dacf4a32fd4d0a8fd5073d57e9c685bd0be8ef16fde7558ba2a762c56a5d403679947e2c752f68a5e77adc7229176518765eb32066747713c6fa6c40cca2c8607e3c33732679065c6a60859c812203b8a5385bdcffee73659c436c7f94b3c761e3f36b33e640828cbc9592d0c56e9adf321d1b152ec8cd817f914c786ba5b4e40da709249240c9d19dd0aba06743bf16b39e71a8de29ae4a924949187d54eb759e2d0c3f8c30a23a33baa42ef334c0758d7ed081083001f2a6c5031e244c501548ca880709a42c51423a604650c4ec7d2f3d31e3683c1baff507edfa11c5bdc4fd0dfdacb5791a8eb62a68ee3ccfa077a0dfbeb24650a293e48497280340e20e60058ba63175699aa602765561b4351e966b5b14e8ad2077673b66e98e30631372871bac1deb0a372168eaba1d410ca08e9069b28248852eb686b38e950ea800206142f1eb5b9cd2ea3352cbd2416eb0514fd890e9ec8e0c9104f983c01a234859255a2e1c5c6ab03afdaeb3f2976b453d54fcd28956a531dea97ecad73cf935ffd512c963c253520099624c209490d24213a7bb6aac0d47cd9b5286fb9bc6fc318365c4002363cc08604d87073b281c7f48c705fa472564a07657474e5c87674c4480d232e464418796d8b7c151fcb5c88a1f799568afb7a8b70d10aa7a21188a42092111960684888a125a7a11f273f383961c3c91627312721505183d7ddf1710d37a337cfb6e2fb9fe36a7f2af5d5575bc39e9337efbce54358d7c80e61e5bfe1cef3680dbfadf2ab0d61d662f9d5d0f1da2c9333294b723dd11d5d374dd4e86e277d6a72812651dcfef82aa670b514e52f17916b88c56a6599cc52962a42659abb89f86d762d9838c164034c8c60626322c4440421129c847c6867392b7c9b1773b5d22848bbf26775f92aa630c558a23cf452ab47422d096249cf92108272109446d0114114089205bd10040290088066402afc74f19325623b31c5adee7ff26b47ee3185f2bfd253b44a510e942a4219396bbaaf52a8228c3262dde8cb5771465d55a58ca7d6c9fbdd953815be56deff501efd985a55b47e68b14e34a841030db918daecf8bb1139cbd62b71ab866596f7bd9f451a086080384e06f0a2bb23954e26c56c2dd24feaf2a1f2be8f8f8f0fae96e2f91f88e55d5229cc47f64f7c70765298cfbc4aca9c947451f275b712a39e32dded3d61bae74bf76cf57c7dffb6ea53cf4e0f093c5cdc2ccf96ce8fa94cc9c99b94293c94a784ee8ee04d4d965bac2d5897d259a507b91cdfbc268db1765077b6582674f70c635a3370d19156e90c5c66f8669801b553a63b565a74dad9d9a911edd0b0e3dd3149991693248916ac94e48bf3948486a64bd5d6561215e2949f93b78f666cbf8be408121264104386214e3230408624477870440547b074a78c3e85a967bb1667248d93112f46964e466a30c2a373f29693b7979111124ca7969344119f8ad08a149d8ac4a0b3860e17295fcd54ad3772aa38e53020474999138e08706e3845701cbf07ded96a76dfca5ac6740ac524e203a2778a1f495849715a6d49e95cd00ae1292deef5d57ab5b39bf29b74b56c631e58eb4995864a081592eae644040d22491099272241447a9c8684b1be9ad56dc67578d29faec3f6fb4963d77a0e7f2ebd4306302675c40e29a0532ac829d5a82650b7136aa85340734a2020a0506256f7f91483173160200629a7184e80410630100183100c51062f2c717a819e5e18800b57b8705d4841081742721d23b61983acbf85a00d6bb8d847f387b3074e6ab3ad97d230665b48727239dc8c938fe15abc8a13b90a2cb880852a58c8e1c402664185130b21ac208315905881ae50e4a44299930a635410e2a482014e2af038a510c529052c93c67c858d7c15696c0524023911ff4101d9225fbd8a948c806041ae1f589416d18ce2e0dc6f347f96651464f44279ea076584fb4a8432024afda08cdcf3242b0534692b56c1ce278d5589420e28c8503889713ac18b13ae9c4e507242905301d028c00f0590a7020831010c139030a1869309dda99f1f6cc46d264aa11ca3dc71a655fa9eb5316ff9cabbb04a15add2e92aa992b44a63b5140fa088d3001230801f804e10344e415e700ad24310a5ee1ea14f41989c4a80a3041594a0815309b8127c4e25fc3865713a8de0a4c3c97512c134839309042713ccc46363039b246c8a4e36454e2490712241cb4fac208dfdf8ca5771e20c5a8bcb11488a28d6f26330dbef6b407c15511e4595c293c67eae5d79b77258b514c368d2d2669593c66a93c662fe389aabfedad9941d15c0152701e4208029027861041d9c465062040498fa34c2d0083f4e22e440042544304284a153087474a7a238113f72224e44b39e1371229451ea0865e4310acac8a3e749998ee7c9d4518ac869d6431911a18c5247ae4a619413392b7c2711678ac336e34c6f3e3db3525b085d8a42c062429f42e86208a8b645a7008411becd6f96a8f74e70005800401a27004cd1dd51fa0400d909003e27206b9c8050e004c4e7042484c6ff85299d94a7308892d19b20975f2b3f96547f84a71f379c7ee89c4088e3044218107c388110e50402cac719271f5dba5993fae79dabba99d1745d8bc293c2c2b7b158387c2791669b6387c3b775298c0adf96947b1f47f9b1e7a1ea635b3bf9e858ebd4434c0bf34f3db8749f7ae45ee8538f26ddddaf530f7ae251a61b9f787ce9eea6a0bde1c4034b6ec9fec9a9a6cca9a689534ded54f34204fa19cdf2963fe9447381eeb6a14f34359c66e838cdbc4089cef2874439eb0f8919bcd663759d0f8956362476b74e9f664e38351ba7fec0a96ba7ee4ebdc21d34b8838a076de8aa2cfbfcd3edcb3d8a551f9db922ce14cb1a823f77cc3b64b0238eeeaee0fd3c7365f9df5f3bc0d881a5630ae51fe8f3df8e9d2057cb8e3ac2d4d1a58e1a4e1d293a78e8a8f9c11a3fc8c00ff00f52c8f5e947657f577f319da3ce3134470b73f890e38b1c5de488d2bdaab64a582d0b95b1658a9489a1cc0f3260c848323b644088e38b3872dd9d42a95878e56e957c1559dd6757b63aabc37d6af51705cbd622dd4c2f18bb51ef6b705c01c703e0288203d5ddb9cbfdacd2dfc06f746fbcdee87963006f88e0031df860091fbcdca8c30d319dc28e33e523b98eff04f92a56231df0f619676bebff0ab9c1a5adc56eb11b51badd50a10d32ba636afe12f5da48a20d0474c7d412f550f331283fcf37f5faf8b8aa86650f8ae881941eacc00619dd5fcbcec6b371840767f0c0071eecece08c1d04b183243a2843075a3a666b6f8d783aee2b6c66fcb10ef7b5bee761a69fe57de5e4e4e8d895ecda9f495ca3d1da4b9b5d95a53f6d4a9a7f2d873e3d19d3a1d0a7681665099b486428d2440724748fe99883ae65ca010362a5d6e69bbdb31cece4e0c71a62566b6869adf175afd1e4263fdfd648a12785753878a34b9cbfd461896be1e02885c201ea961913a67b4c97a431b93eeaee380635e6479c4937b0a23baf79a8e413d7fba0756a4600002028931000003020140bc62332b16c3ee9fd14800162b856b05ea1cae33cc821648c21c400000000000000461b004c9b09795e5624098c79f82d8f62a2db4da8cb9481beb753c7d4fbcc0011a86a0442ea728843db6e9ea28405b06369186c3adf84313ee780fc84c70dd954eafb8e16a8e4389d4f1da75f9f31a087b47002345f0c1bbfd168222388e873103723ea63ca1e609a996a569696e0a3f60d3a08c7bed33fe313dbb6cab83436bbd4b8b9952d20984220b5d43340e31c519134d91a32b30f0eb627444280abb4106955136c0a1596c7216f454370f684804fcbee4ac7a7682ffe0d3458cc1405c2943dbd27b3325f10f50d2f5aaea27f2d3b72e4adc5bd847ac9a6dfdc7c27041afe45d39257f942171e1a335331236d26797a7863c63e830af8a3e51cb410fa0ac629fd6f4a50cbcc48f437a1c08bf086cf3f58163f9cb40e3945e635adbc8350628b3b7b34966a4b2e01bf64ec1854e4a8e051e4bfe512fcdcced905a14b317ac6a40f5b2cbc00f80c190bd944b6bb630ba7f03118c8a4273cbd22cc8f9e6087bddb30ebf7a6b6ffc12d590f83dab58de83b9a3782789ca57a4e0fcc97bf2ede6fe54c018879a7dbac20827cd0cd8d97e935d4be20f409bf7581c2daa8bb29a1107bf20d7880db2d0f844e075c715f4eb36a621ebe4123aa00c065e400bfb096963198a776e239c4eb1efe551a5c82b0586a3f708b58a917a50281ed99a5f7e1e5e2939348ff6fafd01272f01f80fb315c175e3f219e0aa4f88ee3094e8e263e282c9b577ecb4e8a2d9d0496310523ed58a01061e5acee370cf5a07ae2c32029e430d5fa0cccd63b5089984902d232b511ff0eaf841fa90869876aab058e62c0870d821609caac5541eabbb8e42da37086971958920080bab497b46cb2b4b68a49260ff7573c5c9d89804227f2851aecad918ffea473d6ccc8985ab2d3797b4b868f9e5d6af03906a6027d77b8d71126b606831de9d2036ef4ddb4d2f4ffb2f3f5b5226bc027741a26d284f4251ada6b49e69f9378b523f9c12ad2b5288de3218940d0866c2fdf16cc6fae6b6c14deb20bab4735188e10b5dd66fba11123e901c016571bc6568980c9d96d06a1d3181a3a6ccff54eb129946118158fd231f2becaa3123e968f6fdf47ba0de7d2268d9c0903346c1cfa6df49ba212a7a520e919f5ba354cdf68a17b40caccfa6e7e030248d47b152b4c69a91594906cfc4e562844088dc11cdc1e242e82275d6da4b42adad609c6b36451a54abf7dcaf966c0c7a723f476e66e78f61e497f9bc700caf7dba1933ad0f604b6da4287284346d1e129fabafdbdd33bbb7a329a50bd8a029ca7472185f82b83e1e5e16d4a754b7fa3b3ce5f9761c0c7563518d204319a113d57f10c8c30d5714727695f60e7b8e66bf57a61b0b7b0f366c1791d5fbf3ee4fff9cb26c000aff73799c37f20ff7f192b7afd4edf03f814d57e358a5c53422421669ca69ce5550840b8475b1044c1d1c204a61b61af3ea82cf487e90bb4684b045599e9afb1bd551b2f6f1571255d2bea727d2cdeb2bc96045e717d3c866a2afe57b57e43f018e35c37a231220d8c2e3b74a2fd2bae8cc5cdf5f295a3ed21bbdb415af746739cd5a5afc47932751450118c85348455d352f8d61138536c4f6eac0829a3d7c85ae1dce77e8477a2180176e6dff21007ec543ba808a8ee9014fcc8953ae3228ded0297b856cb8babf4bccbcc238e5646c06c88061f154331648b9e51a5db10e04805b189393d592bf228a6764f3fc123a1f6bcb6a5c7a0b6aa078491fd3a65b78381de1687be99254ec0937347f9568980e70652b0fef6901549db3442fda73af177c3e07d5a6beee297f16a4fd0f942a58447b84fd2f8c1d8f721c126c6815bc626cb88eb2d44e92f2cb828aeb9b5f82120e6f7c5ac12ca49461ae18b69806e75305cddd443ebc832826b65fcbbc911366466fd5b8621399cadf3f5e59c04d0bb89475b7ef43ff753e0867516c47ccd4881d1921591c9c102a82371eec375745867e51916fa99dc2ef666257c8d665e05d9098a59788388babe2adf32103ec6661d728f40c7aecc52defd20a2a640107d9963c0abfe1497f9ddebf50b0fb74a79d9f683d5b094760ca50883e0b9f6ac473e7b17535ffd1b6f0e1991841f89a8253278c4953d68994ffff131bc4d17f5f3db01e74df94e0bbb7a21b7821b5d8ab51ce288be1a8cb9a6b6943d252bdc6535a151e6693030f5faecccc008e18042b78f14b4b082db325c21f58a5c85e18248ea611059641e32f41414727278122948112a198d05d14091e53d46074dc24c7ff3b3e3ef8246c8067eb55505655ce1ed4c27ba980a17d52fb6f41352802a9bb3c69248706bbc8f96cc7b5fa03c1686a9b24f82ca13a9491cb727baa70f123f5fe141e90cedd2b2e3c112cf6c86699d24c829ab3ccbe3a4d93b891e8bb57e3dab70076e508882aba4b2e0a0235922f89926e5a59bd352b65343e8c3e2682fab57b36cc73b18141ad8d69ac1149820456831eabc3dd96f787b163b4c5c1e7c5c4e4219c14760a74eddce611a3a7be33fd005236b1242c368becff57eaee707e31740bd999bc1afeabf6508294894d57b3eb7aac9f5222ab1b1b314731cbc2b38a914c266d9ac507850da4109eae721ae79ee1b2c222c20306ab08e04d7af322ee1382f3520da815a522ba98b7a3b82eb643363bfbbb2d4492da5980923f37153be92be0074007448856a67883d0044b52c135dd7ec6804f2777ab7eb848773adafab7f2eb8b143ea30cc60c772ed3d6c97f38ac89cecf23fc41b975080550e7b8bcfb0937643968804a77d67a1757e6d004bf122554b31cfae2e3e7afcd5226e5487be980deeb679523c47f5b715ff96c69ec96f56c5cf357b06ba36c2afc30df38c1f57417ec03ed40913d6c490e4cc3f70e21b20913007c888aa63a7b39bd732ca1be9c8482b151b7c2ccd78c1db6c7483d6c10f7bff1dfe091db70b4b1903695e0f56dcbadf44e072d3dd6262732ab6d0f75264f3d6c7e05533f3988b9599cbe3c4638ff120ee6d0859ad23d1295689043680d7892691fd6444ccea62717e8e11849571c493b2dab75e7b62c3cab656430ec5b6bdbc1363b7a5cefa14ce5a4abc73a2ba451b333158e906ce1129bd9bcdd1e5b524a8d35d43c6f44c633564bd7a2ec710cbe008e89f9baaf3cdd27b83306d68ff1f85598f4139212a1daf97a94d0c12b4d0a4fdfffdcb86cb3a4dfdc78911fdf2d661057422739c62781c2d0985d66a70e40df538c2566ad29af2c75c90aab6aa4f1b2244e2bd1ac5f9f17f857f34fa692e8a921e8bd5762c74db758e23555ffcd8c17acb49e3f6fa41d3ed2bc42b8b2711fc59d0f4b787416f30f18b05db9edd3faad4db22f21e400818c6ac4ff389c9acbf67242b0da4652ac9968d101d96e2a158fcfb819c7d6a0a44607448150a16d1b5a7c3d4f53b09f2174d7f1978512d3f7b1bbb8d3d73c28d627108b76cc98403cf6e52374c0b976c54001eb770d821c59586c4c3829a9bdc765c410316401df09282195f8e2cfa066a3bd912201e8423121ce11b38fcd9853d39991269b63d2d0920cbc629603b68639c8175808f3244162b5fbce135e046104264f16a336f290bf9b339e6ea99f0e9176d0c3eeb11635cab78bb81d04db82ccf3ceef2fc97ddefb3b8ea30bcff557f003c8a464687bac1ed9e4d251cdea62dfd71a2151bca4a6bad751afd2e36fb60d4f8f69409acb329b5e58a8418242130bcc76cd4ef2eca071dd23d1c053a50a6594057e1567f4ada5be2ada1f1e206c39f7b7f80402e3da83c594d87ccce9f0fb74a39f900aaf5d8c2db75db186d4a3f6a4394f80193d7139710b50a59bf463d213963afcdb5058605e454f6891ba1d6c6a3e1d1046b47b83537a0c44f3405e81cada1bd7dace161a3ed676abcfc717ce3ea444b70c194899ccc8d3854d2d2f9445f1b2d4b0aa98da0b0c6236c8a88d1e7b0d38222057aa45e223f3927064629209da4cea3e7c5c1478817588b20202c5d1bed513a0af75aab15e24d40c8ade1cc8c066517a460d5c65d137eedfca6f9a86449a200ec12cd4ca441a724e24eed47e21c414093faae6a9c8b6ad50eb7f297d646637a810cf7f92c0d3f2cf7e8a0df0b1aa7bf03aed13bae12fe6a58bf1947ece4c780918b3b7a926125b1610b56ffa57dca835baf1b3f54c82f33aebdb9f1f0c9840c83e52b87304bda75842a3f11007bef835e8c4fa3358910e54d7c1208fc2c750bdad7e36d0bb3d2a8d60a5defedfee6fa28ccf95d430085705e03f890b5b2aa871387b1e1ec19123a6fdb1e0d3c319625fb7829b804d957a0fbb5f7f926de6ac6ba994a8e857e5e8cadc3885a4a0f57d32e040511fc3dbd86a160765cc251aa9188b267a5225cf23b20ab391cf41a126d70b0867016a3122e3f971308aa40eb913be0750d3522f03e6f0642f503202a4706f973392abeb9f161b5da47a2d82eb47ba57f4110ff5fe78366feeec71fc9fb21195d8ff39228cf55f4c0c2fafc778e1ed5e6151ca79d71ef52ae0a87f77015615658d9f4915d35a5f28d2778bf43ced6c6415981bdaae586ad182137c92b1b65ddfc9cc0ca2fcce039fa81e6e5c9adaa7dee49f4996d696204042871355126cf3d6c6cbda28c33d91fc311d9b870b6ebacca7a96d6d9467e0d4b366a03c535c1b76c11418917969af3d5040807b4588f90868a4b018feeaf743cccf0e72001013396fd42102340639c8353a9381b9a2797224dc589ab87e6d184386814651341e8aa616742e8972eb20194156d2c6a0001100648761083c26fd712a3c8e0a9167a70c6b8da4bb00f1b7ec03d352d2a3ef1399cd5cf3e9d24f970f773aab39edd1e9f6f572203d272b2a361977c81856a0291e106a298399c6b6c342f1d661690a3fd7a4bb6d46c180a99ef183c254330c2e1b0398110e7f797ff868dc323c632afd0924f195c86965285315e907e92c7603535db1ef2a7ebbe6b31266df92478a7d9547246af57067757511f6340dc8e2819414add8970566ef7b231e9a860fece979579836775f3f27af5b0a106eed61c579fe37cf4e40fe09e5d1b3b9dc9dd8a344b3baf93b51b6622b4b94309f6710c0fa7a756a2aa4d90102e4cb31b21b89e5a24a8bc4a5b73bec3a56bdfb82101f80263a25b70f83068dd8d1e64cc98e63b3aeeccb78d6c9e3878b62a9c41abda9f32e3164453e971e6acdf7f29f55818e7dc5cf8d5c1a7bdb1896fba395692a30dee4dfdc509a42c658e854c1bdba8dddd1aec59e43b8a2c0a6d0fb8865c98d269cd04b978eefcf844f76d99251a999e2df85daa8352343c13bb49a0d681ea4c513829e8131c939ffa81a34aec3aae07773adadb9660b79ee1f063b8783e5f4a833a405deb1f5a7991f87d0bd131d8f8168600ddbd0058c3eb0017b83c1b05588996f8ca7ce0562878a9a8266d889a03b5cf0f2fb63be78735f9544ab4cec7ac59c16dda5e488b185b62bd528e9e19ffd4a52e5530df56d0f0a407be88f4de93a16986c0ed4e3e6433a8158e71c20d23c0bbfc107c79526e4557e05e681754b0571f8e8a5f7b03bdcad40d29fffbd6ea56a614b41cea3942dd73d6ba7c347ea9471caa6ebf7d7ab348fcb822b808929cf9dad5741f9556a6c73af196d4bf904222325a45a9e906976e93fe647b8d8adf09e5861e510a821b54adc469d9d3eac678229742a0312da848336eb6db59955c445d3f376f6874057af80d7dec8bcd18ccf140fd6b0831148c775656fd9fdea9c539f62adbfb21f3bd978e3926f4e64fdfc806779260b650e6d6cd0a0d534449e428a18620c98e070876f9a3daa5e5c5c8363ef1b6f4bacd049ae85b09707c1a5eccb2b26e5deb424523ca53d307b01855b4b62f416e2513eb84ef45f6a4f2f4d6d03edbbba4b05b8501561907c58cf8b106d6e2ef3b4bc1605a2ab601c36be51116a617b2c2eeed06e44027bc7b1f4cb023e3aa5de92803782256b75c5a0c7e9e30ae6c0812ece56e00b160d1a47562fbfd22db64204ea2657f713ff995cfec26972b056dd41671e17bd693c81feafbad81f73022d5f5e955f13a1de1a6b2f4fc4b975d0b1b21b4a213e4c67850abd75569df51a3c516d11577f2ba23d320b1b2e852469f02d762ef1684d514a8de7c7c7c4068f2475c52d2058964a6fe272491712c254e183cc293667605b9afe4c7151d6b79d3c4eb2f493e2fcb1afe4753e212fea6daf32293597b00035c4a0d08268ed71fb9deb84b5e5dceb2680026cedff5902994a259c44373cf7b15ec29e0893994772278f74280f5068023a0ee7b7b03393f0ef95f6acf13b6f4f3d3543124c867e5ea070a4fd85fdb7ff604b8ed13bc6a747abb9843f55de90478b66a21861d86efae0a8e53d0fb17fa9d055afe7a9e2c8c3f007c874fb6814077fc863f66f4141a48600ff7236b7602666a599ada2d22e6e0a968c803153acc98959906c20e6aab31be32554f4719a89b1a2680d12e9dbec0255699f3ea87214c999ce0c86fb1a23d7dd6d40c225bd449265bf500f6d6a01d7b50883eb7e4d32e0e657b50e39e36e1118274435dbbd6b30ce624509cd7894cb910836478f7fb2a15611ca792178d57f78984459496e97894409337b6fba4fa1d807f21a1d930801ec10608091e3fd4768f5f4b513906c11c750b15e72d4e6c55ecc9f87ce9f4100a29ed52fc29d936356e72a82f24966ea33213db9ed55439f90e7fc720c516a68e6a740d0c08cbc83f5aca4259cba5b2adce4b1fb689ebfab5ac149ca9957a6c801dd93b64fc75b4fa8f9ac3930145710c63f3e78ca0fbffd058d447b941815fc38ae182c1ae539b253bdf1d3b43829bce332157d12743c76e2a30826cb9de44a8415097f876b6822c22d094d35618045444bc515aa0ca28f3f14c24f0fdc1e2d3e0f6d1410f37c88e1057c35d679908e8f50e40c91e350472d468d34590ebd05a11b88587b8da6c8a2be46370a9bef2b2d42cb30e87904b5f6e345d335336bdac3f37781f8abfe29f85b9a4cb8d450a78cdda1be4a18b3f31908f2ce85805a0365dc7677793ab8d68b8c7af6438afbd211ff9109f0d322ed630f462f2f954b63d2408c0b2847113a23af6d434d0e746ff24299f9eeb1647e1fa4da63849f01379673dcec6875d470d284a24f9db9ff890a47b1fe0b55755fa998a6b32b88102dbc84f4c5796e7fd2334b6f15335baced92ff0a4cf253a6676c71983cf893eaabd3e93cfefadf56fa4da19a0bedcc46fc5b479fa7180307071475cc03d73cb686f2de4c9fcfb87d14d1cc2bf4d99fcca7b2a3127d1d0738579b8651cc32e3778afd3c698c10c4c1bf2d64a1e41fd4445b1cd10ad427cfd91d0630578aeab84ab548d00ad4a03d85d04edc9f4e8def432ad47ec3ef7ac9d20a44623a4aa3b87cc3a07b14fb0c07d313d02d1bfc704e01999139813ef1586549aabc0c1f984d3cf7df11b3e19ce06f67e36d4093110303fbf0f4668d8dce7a512ad94bc7f6008c98c3b6c036a0c7cb68d983a3c39a7e0d0d63c8a0ee75c70da24dd1d3484904804ad6e1755c92148c51b8d9e90cb9289cfad2180275a015408d83bc7ce12207bc48a61d1b9519839e081390850d51319855e220c5df296c52f1fdb01e25db2d048866b58de962590ce337cf77f986eac751857f1392a64f79707a3ad42bf6f1186414c7514cc5a5e2394c4ce8ecafa598f4f0740b7e06c982da5f7f6c866e4f12b07db104ff3f264504643826048cf83355b0323765b9330293837056746fa9cfbfc423e7eb7e96d0b784e53679e557bd2e24199f122a1c849de09a72aac58c58a18d1f44e116fa914c129b325f1c2e01cd00a4462d68f2d5765cd794064c37e278b90f351057fc2beec202c946bab29efd3693ca8fc6904c81a8ef85de40e3c3811ac6442ded8051ec73b2c8660982b8b271145b8d68477764362962880474e9765358c39295a8fbbfaa0c74cc2a2081888d02b6f094f9d72a9f722fb1ed0731a00ae9d41937ce9a420b850066b515d6a96e209c03433857fce0c51bed1a1cc8e8aadb4a60c999b8a894e7e351f5d470c74a5ce15c1617dacf4cd5ad97d738365df496fcabcf37998b52604ac87304a0ad52b5ed19b720b66216f59c48e8ecf3dbe906b1b271826f2c5872c55778ba6cd9a6ea1a42fab8ab515d39f6c23ad107179594ed09078d4ba3c8c7e104adc9881fce03190c68cad30b398d88c24f4f801f46252b6f12343084f844ada05d13e1b33c220904e913a2dc63d3dee26f15c4cf27533a8fa7f8e30d3da3950f93229f4da06748b773c07c24bd0d59a0ea502741418a980389a52ae296a3380219457e9493b46400291732e3fe7ca259a25e8c58aa173ef66abf170eeb4bf1f2e591b4952182315a9860a117c97c3ea7fce33a1a9334d1b7d7862fefdff14f68c0f1606f2c51524bfec7d4297fcb2d3100d1a7aedae7e05333a16448d3a7f0b4829c60b330e672dcb38c356804b8790c478ef74f61fb96e3c90cd01c19f0ae0a6094b7c3089c6e1cc396573d8c0783d561b072a6a3bac71d8a391e69be35442737055691335886d5bf2b264b7771892920140ab9c1ba4d7fd81c6f561e8e1344662c1769566d87101806eb9e1b9e5eb21a7b92c2e5ddc85d98b758e8935a37dc86dfb6ec9f460f80feb6ec7c2c5b929fd6faa2d7d692377f3b8e4bb33c898942c2c23e3a332f2796e88e22f720af86f8650e7710ecaccdc5879bad1d5e46c41ff96ede80bc0b69e64d3dd167915d149ae8e7c3bf8cb5fb0911f2794ce0bc68237a881231990eb324cbddb9b575f86cda0678d39c0aa054a20b2521396f363198fd1acffcff81585cc255115c2fbff2e3bc218afc2b4da254ae2d337c4e1faefb532ee53d8065161495441ac587cfd391717979bfc274924a39e4dcd2554ba1533a888da1cb52a47ea8e082b4a18b86e48cfe3acc54d2f4c64c82d22429eba88b1e9e663304b95df3e22509666551bf971dd3cb5fc054a82b2e0212600e549b476e726010037c6615cab92849bd247046008e9dca88207ddc5cc36310cc88bb284d0836a66cd231340f36f3ff6fce1e5bbd74991ae4364442d04635e6a9d8fcfa52a4425ae7c82df238a8be7e63217ffd6ac3104c988e2894caf1a12450da86b29599c4fb17097e4f9a8e7ee167d164d1990ad117a08469aedaabac3c919c301b5babc1fef4e135382f3849deb3aba67a045319c5b2d61d8427f138d741b8179bd8f6d36e73e4baabfef963e208ff419e65143c7a028394e227c53502eec93591522dae8726ecc0d5baa77e73f5ac39707c27fb1004ae16bf10fc565d31d4eb381c6cbeaea0e4f3b99ab9f6e30cb455a017996c0fa7ae1eace7dd644f8f6ed2e9d9171461403977559dea507539d8157e52a698a82dc88ff53678c3d0af010a441bb0107dfbdf19ba403412bb3a41bfb7468b6258a77f2d1e18434b97e09385bb74e90b03abf7a9a029d780ba2cf0efa68700a99343152dfa6adad5123f70c3d9eb4c85e3ecff798e54045b3dfe0fffe428bf31c611ab08257d3a75b296d2da6d7b94c2af28ebe1fa78b4cfa6a24762cee53b29b85cf10f3c42b401846fa1d800e6e549afc70e9884237f520da7a68a0bbf765872b57f26bf29425e10c0770dd192a94944d50d6a0654bd62e3513bcfa1648e339abcda083f8a522e3ddb0b4113c5b8e955f267a5065b64eed0c24c983cedf3a36e101afa0ddec9e7a2c607abab235b13f794d2bbe88e585dcd959693a93cb0118f8392a649b26f16ef1f93232a1fd95c522c740393310ec318d5a37dc382fc3ad1fe3fdb64cf44a71053f0ee0cb60b57efb47a683eec6ef4abff5fd41d8b055abc79364e2b960376d26ac7300ee4ea583dbfad6f2784362e8a9489aef24c4ab262b63df667dda2ae6d9c3eed0ff5a445d4f031369dabc861db4a74b0c5124f082e864e3531d483c780c009c2a66de1d0be999ad2deaa746500c2ba2e4c797ed4f57376ab2d2ba017af1ccdc1ff343105e4450dc9172bcaf03966a065669982f443f3cc572154758637eb7b02f3c396249699b59a461a74df339b3f967b1269d1f5551e78c8a40bcea004cd0fd216664868867f29524eb75efd9bb71e0f08f9f21314803697c583e9384ff5da01b7692606cb6c51356ab399e3a41130e782422102cfc5a095c3a918b06b1e2869730590f408d5d478367f5721e368d54e75d727594a778ac4a3e188a44305f0c611cf1c7ba90b0f284f52a245ac0e5667b3bfe9bfa32d077124189fca04c8fb7fecdb48c99d96d497c1fff7945c48c2a13a9c4ff0885a25d5fb843e7f80e21b7234335141d008ea78c3df60959c8467398beb19c997128ed67b122def88facf5c52f86f7aa1c65ef017ee387344a35c6e2bafccd256f35d5ca195cc43e88deec202bce1fcde419fd59d3abf12067be1d67d06c07adc87eff05ef3ebed3fa5b9621f0f3cba6b19d26009952968f7596f313dcdcc0ca6a423578720a1d430582ac2908a7bd295c8992353bb14a092dbddc4a2b9c6264a4dfcbf26f0e8fa4bd8d64e97081c7d1477365f97f79f950df70e9aae1224462120cb029c635b8cfb39b3653f13f795e3f2094937cfeffca4ccc8c09b8334ffaa1ae8750024ad9fb89fdded472fbc6ab425f20e96dd2aea6acb4f2d15830889a0531cf9dde2bcd713eacc09648bb7609be257521d59f2532de6198ee98cdecefde9a1751916c76494ad36020b95c6b8e0a83680eb4f44297ce15fdb21c6be70a170f6b3813f63e1eb0ddf1b239e80e95c1fc49f25672cfc0d90f849fdde9c3bf090e6da84e30e999500ee7337e691d71eff4df09e30c3bb222a55c1850df266e1f74c2af02c69bfc06c2f46d8656ab10151600a57737588e18e3ddaf228643cbcbe77487e3b9b5f14d4c8464e7e980d406efb926d465f5cd51d1c1dbbf12fc3d8cd4e43cc8eb91bb477679b47d7288e8f0c3ecbc2e52df50d882682b8911f6c5a05b27bae01988015d59ce3c3e032ba4ef2d7c2d9514318021404a9cb6475c559344fb1450b148bd8de464b2ac2e2b89cbb4b7ef079424a5b4705817ec876363262181dda41d8001a071411987a54eee566b1608ba445bad5ea61eb6fcfbb219dbc2c5ddb90a2c502cc9370288ac84e3e6d54ed60c690f1198778c213f9466fe8dfd7037a13744da90bca11633cb1e4c23776b90383b5ec52abac49558aa2e7c74d1bdbfc58e39531fb0c312705d4ead7e6f1ecf709a7173a8ab4e97cfe5ebf2644cd4738a9dba58f948316fc4dfa301186144c8a801ca7f889ef6091c811d108ddb5721b4684acad063fbf0a28c1a1b467ead6d45d7077af719219078843f3885a8e711b4be17123ace7a5f845b8fcb4da74ae5c847ca4dd3e49b65eb28877f1c89731902bef392605724ae61300a5f573f53a74f6bd9ee47ba098e2656df384a4a1a1d9b04df1d000a89ab229448d7bc6e62f9a399e30c89476593f5116b67484c6396dbfa9c863342f2af43cb0ed016852c07a1a6e94290fd82a901d2f25d0940a4df13a90a82d156a0c6db95e7ac70e622b829da16f6aa1e3d6d50d491d735a308de22af118d610934a3aa71189c5c3579f7d256755cc6379cabc02e5941225aa76a406fd3bfff7040381c01ba0fdd52395cc6567860ae2c47b31d62e9637471072611c886201253897644fbafbeee42b7512381d3bcb76d63bb465cf8b52c0bb3138ec68f3da34ef533edee7fd38877fe1dab45308da756d0a0d4dcf636d6cd5c09c61114a4cc28548121e9775b72e9fbc6ea6baf2ebf2e11a650ace454382e9ee75185a79e4e1e8cccb83f9987b3ffd946391d1724dee56532b60fc5e9c304dcffc8fd6d7c2638e87b8e0c89f227f28962fa9aba2ec7daeb5fc9a309d00ab4e8e0c4ab70bef20077f5d47794cef872f463a70f1c2cd99f50c6a176febcc741c9c1751e9892c73bf40be3b5471fb7d01fcb22397aae5dc2eb5f86bfb5d1ae5c85a384c362d1f9e58ad5840321e911bee8c449bd489bd07ff9c7442defde6ab9827fe8b20c4b105750d5be9fef585193b761faffb2a82ce4b535f8fe30c77b2ebf178dde7f0e484a4be4231e00a31dd514806505c1c46b3e6882fc31305073cf71b490a1e6eca614fee30c70485a97d470e1711e7759fedae6a25efc99d7a2c7dffe2fc7ffcd17be07d5ed178b2611152c82c69181fc751cf77d3b7fe8a93c7a9c04df6d7f1dd3d3e7fb2da6077bc637cf9b7c880e21e1cca517f0817da785997eb97c99bc08adccfa5610b78653c20a12888404470bfc896d4d03d2d9cf6396434126dd326a97733998f11e82ba88c456a1a897bf512aad9fca3500dcb2528128101a395e63b5bc6ffd24c069b8f5efc7de3710f5f458f48e279e9daf8011b60936c982df8733c5c275fec22222a7101f5c22c985ad1b890ba73a82fbaa697952833745d871d710b49bc5730a4189c90c14dbdcc1a0a49c3535a6cba9d394480ab004d2eed7acac25ec33c0eb3cecd59e98d8db11a1d84c15b03779ec0bcaabfbe04939211e48fc40244dd0499c3e075753e6159e0df8b6cdcdf595fde2531b193e435fe0fea9aec993d7f0b6d492c656904fc509f847516510e3109bef7f93e5c441845250ab4cb3b3d2cafb7c0d1ddc6990f33f94abd402c1755ea57a2996e1b774e5ab144b454742140c7243f8fbcaac21a2308baaf947f5971ded9372e0203b26b53c379d6fcc30e86de854320554b32e889bac7396b40561e7276de5c79d3c871f33878f984af500d76cc73229b39f2f6604cd5035d96dc396174d9d854461042fd80edd7ba511eb84f5bce64f05efebe22d3d274f4947b5a12a2002174ca5ccdcfbfff1064f612a49116e17d06b2332b310899e8e04c2ebc55e15523d411f7bc3249eab2d3d0ebb311dd86f9fcc5fe81ea109f8bdd57aa0030de1cfb89eb916cf9bd81e786e29bf627709d335a20bdfcb332a7bac7b5bed134b90e9e094ab1c2c654c7898a5ead3c352b313095b55ec91cde6aec6ba0d4643a65266cb22668dd9b8cd8bb333c8765ecad3a86bca291deeca48a15d7b24ebfa96954f699d41d1b624b03f55322c605b0d3f8f3066b79e53d2c43315e5cd14b0d8411a5378a7afc745c5974799a74e13c5500e2831a0e7353d91b0fb64a2e4f7cc3ca6d29f06f489827243b0299d19665a3fbc1e1a4e3922a7b83177e5cf012601c6c4bc460676ef8dbcc47ce367e4dcb2efd596f0ba98d688e198fdcf488c94f6fef27d368a419684b3aa1024f7c7c0d884aeae60f66996b998fa8b29dd71e46d7a2c6944426dcba89e9447d30afe444f69924c9cbf9986577d0b9929710928da1cff9a8b85cefdb79c1b21bb12c513119bf9694884c8eaefba81dd4893c93e0d97bb8b70ab0843e0d36a24d17747a7e7dc26db9bb61316f94113af354b21e46b608e7d6a57b3aaadb3402f68b8151cc740aa0eb10ee8da1e3003bfb004cf0132ed0289fe784d7c8af65ebce7d86ce1e12d438219b85389e7c67d727c9be6683a12871975e3785ed7ea43f3275a3b78b08c8ff8bd2cf4c2d3dbec2f39544dc726493ab3ef2536f313e6a77bb6d623d8e8e1b925035b53a66b429ef77111b22d3f9db082c9e24d5953a28d818bd47ff27b602ff680bf42f98461e12166a841f4e1227f83a97d0ba85f057257243dfec59ff6e8a16238188228f8d416be52ed655cb0f5b8d7ac508023a981cf238d385a66eb102647e6e2185bae0c4568c1efb09af0cea464cc2673eb0dd5263e473529a6b66e643a68bcbd5a15a0d817ca9a10997debfbc8bcd4bd0ccece7fed8b3d5ef0492dfe676c7bfbea2b52e792204566c96c39154b4a62f2c031cd0761a21ac84a8a9c694d53251b544315675a40e9b407f4807b8dcb229be9a54fdb4eb0a8e58ef6e83f64e60c5397ff2e62448ca933009c111b85c53b247456a8e0cfeb56f9f0f31db62cfddcb24cf35a54951a7724a5dea6139df116a6102239d2d6b2734e0b9762b3e0d11cbdb7ecdf3ddc8262504ac39e0fc33de223abea3e3bffff553220fd7c449afe7c0d88a5218d28b0ae7c597da4638503d427903917faaf9df0df0b526bbef8ca7427706644f8fa3e83610206378234a5605d68ab44f7549f7bc04bd3786f33948b60d25225c619da3fad7b76b38cde5bb24ffbc6f36eee4d57117037851707b10fd5b2345c786340c2156fb785fd1a24277bfb9a82f52be0c448104281cdf1dd4fc44bbc0ea535b918cebccba708f78637e2fc23e67849ca2da7055977fa38c8fbf2420b3d68df73d47f10afa87dcbe4315d8fca16bc62ec365f7ef8584d43be3cbadba65bbd11889596c90d8765e5f632487329acfd2192b74c4bbce73869f68da7e364b1d87cdeccc48547ee376fd39bfed6791e9df7fb7618b71342c74d4cf9706f9a4cc4c7daf4c59e5159eabde73f4db0c164ad5b5986d614bf8263ac728375e2a722c1db8ea82cb74aa312b5d85b66ea1fdf6bf2a6c8e781303f51d0d89995364e86d46b70f72baff39b3e822933376bfd511ff4fbd877ded65a629cb433e8cb1b313333bc0336ea17069d8c460c9dd54fbe16ecb198563e4aca3f98ed055befe295a58bfe05018819cb973a327ddc44c0536c1a8aa511ca8694549dcce404661bdbd14f915df2c6ebe782e73ef988a31b96a025608d021882b68244c841ef86217ed97146e3d59e84dbc61290cb312d4c20271f55f2a8931a4b1491113401ab6cbbb94e8a0151282eaf34eb423b31bff23745a752e1aed2fc57568ce222b8efec217ab499146b3741eb9433107137326b8c5d3411ce467fcb4fbad9971f5cab0334025f33a6d5eac4baf8caa953fd20418f3a08296ed1e9c69ad84f9f726c98465cfc0c440a3ea6b57fc0f8daf160dc557219709cab043432a9dc01de9f8bf4f551a1ab22d802d66793c887da0c079b3a2daf731cbaeb58cb648c6e350b3118bcb80b5be4fd7f2dee182271e7935c0466aad2d31a12f339c0d52029b5108d0702b9ee9aad2c1d0cda345408f90cdd2d60ae9bccceff4f921a8a323d3d5ac6af26e61db8cf7e8a0d8f00c907cedd730ed13a3182ed2871c1c9a2e49d40f5454eb101e9cd94b7a805120bd0e948d34bacac14f96d8e3dc6a262f3278cf3fdcb0e460618db29ce2df78e39e83d9c7773b3ee3f8e24f4e985e78ba3e307972d9fc277b0687365208ea4c1d38285bf1b1ebd6c386055c20145d8e6060d297c67962d049aca38828fcfe5bc8786c74cc5c2c21c0424839984708b7170cb78577aed3690e62041bd03748448f749d169cac92db3e24de44a5bf5f108c379599d42cd898235c907357437c2367167ca841ca4652ef32556f590475586821270b2cbded67fc0786905064d14c580608bce0255d33f21d2949fcf1de07eeb63ac3ecf8b9e8d632fe5baa3997d7c2233a9c37bce1c3052990fe6ec4f1a84fb037fb17a2f0ff6e90efdb52eaf07a0aedb8f3facda4d59fc12611e01a90f71f3657abdc33b0f7d3d02e772f32cc8037ee3935d6b32935830d69c034a1877bb73da3844a5e4a9f5fc6cc12692ba6eecae7c89bccdb462dc7c81edb0a65a97ab965777f8dd6bbed9d734cb36d2493d64012589bd6f529b157c893c866343f17bc87021110dac1ccdc560f257483b5a9f42ce5645f4b773fa84462d7227c85334834764c2e65d0c410a134b5c82e0aa4b35ebcd80987d907ed47ddc5fc9a3552e80bd0ab51a1201e2468e1455f3e7b1ca9b63c6f2dc1641065dfbfbda817b65e6553d74a42479739426c495dd81ad0ece43abd657f564f1ad5360222c8da00d1da0abd66c88176dd4e9ccfa113630606d7817883765665dd308e3ffb5a44a99f0561c2842e3b4e0cefb64b6b01a23f18e5f6b55c04d5b2202091eb47bf66bf19093e0392c6b0c16868cb8e698554ea7683c516a9e6ca7848de0018b77e31d86dd3f06256d0dac0312ab5bdcda836ab8e7cf663a01a2a1a2fd0cb7d5c5f1a8e462519e58642d1f4ceeb9afb76f59f7635fb051e116f555b1378a4432a0633ecf57368b993ffd94e14407e35f9711aa62fef293e824035ddfc3cd5354e21fc9576326ea79121ddd05e12a268064e07d0e023828f16fdb72920c5749765a32afe788067d0a16f8d9c09ae624b065d6cef8e79bcc44c02675fe54964a8236fce226ff020aa951bd64826571ea289017e424a53997be347341abfd865baab5c76fd44475f885329fc7457d2cd24c4bef6163c0b0d294905ca9f3569945a2bf191f72519a05948d7a60937c73628493b0afcb56e30bc0cb43a7eaf02f564dcc00b87a6f18d3372cf3d795b5082e9151e0854d067ecb439a02b17b352ec0374c31177da92308e8a294a00f2b70876d962f6fa30472e909aa6ca540627c651e453e8a3dc8be31fc86b78a76d527596768d9dc328ab584ba9c3a838b71683ccf914184f02c9b86595b556edc67e27d003ece8525105a4497b75b2afa1729b6b3cc5fbe8f03bc7adf08bea68d4f1d1f0691b3dccbc1dc52b44e9247326ac61ad3034e66d3cf1f648d0e1cb3cd89df5fa1dd0d1703be6c78093da98bad0c6f1a5632415cadb8ee7e146804d37f48136a81efe221432bde0a79a0b524c56db635c5dfe6220524a5e2028cd055397a26f85545c7eca12b78ca10faa0b2d88717032d7e816bfea16f0f16b589770f19b8fc850cd2e4e84f8717dfe2433fd774ff40c53f428eb7b89e4d52010334c1f9d27944070c42c64b59251c2eb6117f574bab20ae84404836b346d8391b25bf4236dad98b1b63a7e68ec0aef40502d4f8fc7261d7f63444b7622a10816899e8b8c770db00027133d1cf56716172b3f639b59a701b37af34dc76a7de4776ca77cf504ced0257c27a57e77f274532f4b217177aa9cf28b6bb5523adaea03985391a34f67f06edb10f13e83e7a38279b893db6647c8845a4c0259883577a83e4d3e1c945f044f3d1cf1e687369c3ad036ddddca3b6eeb42dab8ee7b9b81ac8f13750324d9583a1b8b5322b47cd4a040cc5d9f823fc517cc2e3e405aea619c281d02727987a7b134a15de2ff0b560c1d0791daf024011b7340df5da612c44e5eed676debec01f2080390883290a1ca257e103f15a8288c2db9e230f0b004600f6fcbc0b1755ceb073cafe3745497d4f8867d2d109856a73e8d16ed037e07fdeee6e3ab7c67e6a59713e8197ba7a7e8282b82e7a0f9d5ddb34e82c20a3bca7d42a3386c233825b5ecae0800d821066169f955c3effc34f0badefaca5cb16723644d00e4c3e3c564ee266c9c53a66a9d9c05dc2106b15f0e4876d42e342641401e4b8730f323aefc626b4af118df949e027c187f312f8240545fe6f7f14f15fb70388da8805f16d27abfae51cdc10071cbb678b26f6f4299ac1fc26a500c0e9c5827c737070880462100695918d3560ef30a3a41df5f9b433da349de06762e0507e14ddbbd0962c245dcc2b9a060bddd3f41aea6824e2a160b4ce4983e04ff88a1e524c56677d71305ebbea4387e46b28dcffbc20fc7db41a85ecb305fd5f204320f0182fe34bc02ff9100ffbac7e0a6a969b621b1486b9787d4e4ad0543bda6c5e43debc84408268df2b1828d14d0bcfcdab1bf52051ae32df0baef37ea77a0e311b0dc817146643c1b5eeffbe85ad49c757291895b08b65ddb39dae54bb1e0639fdc948440e1a792e7d12cc48bdc7f5bca4cc98f8ebf778de000e92bc5e805a709cd9cae0f9830be3597d545561d3ec418e3a8122188d250cccc578600b39b542ea3e1059c2b563486c897842a89b5196a1c35c480847eaea307fc1ea0ec38d904cb0224d2cb8f5d53afd32b917a4c7fea376030e6da56750540dcf6cb23d760149f7c16344a42f7607e097a3cd985e2b35fd77afe22e04fdc5b0f1c2e483e6b7a05b675c9f6035e29723934d60f050c43d381d328247a17829cf025431b8e7c803561508ebb35091b790b72fa5539927a21ad090e2df510662b897307a424cd380cd252f3f231326bcef276adb13f3d52e461ecfefa94ee89fb3473afbb62b0969f73ffb99d67b21444b644e207839278c6598a96dd5fbc1a185e1abb6395ff4927d14b707a2f1d3faf58f85ddf6ecf5329d8561f814c51de8f6c1f19e07f32bf9323ac5fe6f5bace5b476c4ae403085110bd2ad0585b891208a40b54c1fcb84d33d8cc3d9c47cdce5ae1595c4f80007cf4b1df3b644a123076d9adfe65f2d6a1b22393e3d01733a7185b04cc5f8b6204973269affa3494a963efb2dd603e7950d6056610685079e5d95db507a5c935cd306f02398d1564103613cfafc2a0ecdf7f793f0c21113d2686b6362a6e40f22773b24009ab6e91ab26964612cda4a36bd20cc1e9615cd0f73422a2148ab3116537ecb468e4371b3b65c2042d5b8778946d00c8ed0f8ec2bf70e329b66bf0fd1679d88e35d1c72361e3a30495efc0ee9712edfc27f0b6d6fe539bdecd1d2ed3f40e74792e74ef74ee3c8704ca4347af6e0f44d7a622aa6c1bba98f4d14643754156300cb84fff44f40b36835090dd16e719fc99e694b982988d8f24e9c3a8dcc088d95a7862d863c426fa48724bb0b90f8f78096b59735d7ffd708754e825fe405d3a71c2dc42eebd6099e514dccbdc75ac613c194ac9f0602280a83f98a7563bdb7ac498ea6d024d28719dc3ba132f4a5087a1b8d5d0dfb10d837a17150d1877b87eb3689fb6bd60d373111ddfc6a434983b6a5c0533ad4195359510df316f64046c7117e19b31685e6c60d4d875ce1f216280b00a546b19307ad0e381d007783e58b793a3b5b0e6406c992d0801d0df6c99ea6d6d771d9256ef949b507887a923bde54b2eef110861d33c1558870321d30100af179b872abb3c207d311b7759c32ad6d06daeef3280264afd14814569eb10fa9a18a25b78ef0e2aabfe2660403075a0c9a1c6fe3cf9fe82d8dad96ffd3f407cebc7ed4704c8092e5a8d95cc2e438cc606ac1e092d6ef72758c2d60017803caf0ad413bebd45eda701de08717c768da7b440b433d42d04dd0f6f973df16506a246b2032cf24e01e0826a0262da609b87914d4368bb16ab0c35c16c8cfe3a3cc738ef4e3a56a810c8c62cdc40bb121da1aae6c911907e75f42cd5e0a756212135650e0ead81f63ac5f8c521e522d86f0ce45eb8485f9281bb138b93430395f5e5e7db556b46dabfd0b15a9a47f75d65264f5123ab95422c8e5f8ccfbb55d481f8532eeacedc4f45d86da88a31ae93a0230cd3e45ea2e91624eaa5bc3b63d74f868be2fdedd83c1fdcd24f5c9407e7eecf780c5bc74701b792c788a146dd1107348c8f4cb69a39a93081361854d99546261afa9f2e7cdcea7e3e361063ec8f54e47bfb5cb382690b5036d20e8127437e17053ae7dd9ef5f302e3ac9dadcc197e37a346b3845d95e50e2f023fb184c9948a2bd441d02d840b3baa551082bc50f6c2f5591eb2f94ccf04e2e92aa970d2a53ec382f09c3841f15452f331062e3a5041d89eaca6464d24171c394f3f568962cb119a292da9d6d0ebc0836bc6a791260feedd878947863fc2f30bdb908a479091a50f70cb363740add9b19a26056101634e84226c971caab804dda3fc9475774245ba63453bfc01d3a1ea4771003fbac56a7d9ed25c1032ad0a72d71490e9c0f1eb888c04ca2446af89f6debaa211f1cba467c66f9b6a7f4f541587414fa11df2593eaec07e4ff0702e3716b723e172a41a4e58738019e2c0c22bb8b0daa92370a6c2533899dcda7685b4878e452d22c3a679a60e4e8be9e9c57eacd51d914a3dcd28aacc2b79b2e19173d3bfbc47347ad9f79b704e82b4bb1790546763ebcc0e79965aa6b207b86922d1b24279c28ba44742bee3892b74bad562fef0477547190f8af031c33a1a2a32dc4de3cbd3ba7fa0141ec0d55fe0ddc5f23d423488e13811d9b7b03356a9ee70d1e919252d542f4534ad0836817c53a76ed9dc4d45980654952305b73a36ee7964e7f94c7f33c22ae2e9d385505ea0b89fde8015c7a687840029af9eda6b109a4890a5a8ee18842210ba13602811fbaed726eea2d95cdd7834bc9df1db190db964d058b1f985ceb10b50f94fe1a7e65747bbd457b9dff8f80eb00539a7336d2b681619eedf33a09e5f4f96ed3fe12c6e93a7e259386865e6bb8d0778ede5c08c95939e6958adc40eafd8189cfffb2c65f99f4c9344bd7b29e470f32c874fd102b3dccd6bcca3e9c191ba38e3d2c91a0801a2032907f7c2c920085ba8a71b7b9939a0961dc9f796c0187cdbd07919eee538f47a6bddc982cd49c4ffd129e76933de8e158f44e1353c704c26e42891897f58cc8b2349a67859289c30e9852c974c4030d398932ff5a1cda80abaabba6ce93ef20d8d8392d4a0a3e6c7121c9cc24190ab68dcc762df4015eef827acccf11f554ceb6594ac7c2cb6e51ec7c03c3e163b942a750bfc7f4f1539c33073c142f0b1be9a69965399411dd3f6c4df9f5d9912eb3ed09e1191e3093dc1496e88ecd03654e65bc1ecb14ed14a60ebbc913cef32bff7f8f063f43a242e4b3402afca766719d56fd5325b3f1788d49b7fa3742d5e3d1b0c8c7a1ad9ef0100035f7d10a50ae442e4d2d9c6c8417fdd3cb3919b9a2bed815238e33b1161a69aec9ca393a875fd823e8ce9221a276e2b6c13b3427167c99f768b31db70f8f56648071e73e257eaed4756bcf9ef34286b3661a23f93f41eaff6e00b6070257eb736ef34aca3e06002ff8ebfe27ef0f3d65411087707822f40139e3223cc1cad7af9dd21afc0a6632e0e87e838fc3c112a695e1634eccf67dea9e761fcb4c24f0a7ffdf576b1b1c1d5c6734a50b3621a1768330275714d8ee24e91dc05f7e6871fbf2a20b1a7346fbcffb614e251bfd8fef687dfff77d06dea4d1e09ef2fcdbeebe6ffd56f4f9c84ea71e97255f819a5ce34d406435f24d75a1e1f2454410768a5a780d6cda24647a5f968b418c5739aea413e21c8739680824e1cb4f2e23feb5c30abe397e0e0da979fe79918f4bbecee5ebde3df7ec169ff1001f6c8a8410eb5c146d2caa3d36e11c7ae20a651ff4c49999c0c76e19991ef4ebb247fe84970837c9fef1752cf4da64df791adee734ccd1703b1967cda82c63dac5979609062423bac67a1707e7dee077330c40e557b5dfedcc785a3621cc2fd423cce8928a314575b0e3c4765335e778be730f55decab94424099b482cfc29def59c8a7dd88d6799a77390c2fa3af9a6d7d60461a26be36525d32ea93333af46bcd989f50878685f61081e87d42ef12d8c8572eac67ec459d29fdcb1c19ce4429163f726b4d86de1cea274d0084dad4b33d7b1c09541fc568b5444593207912ee3023feffecf97234473aba21b7232e6a497f003be931e5542ab8932dbb9450515ce2daee75b64cdc541981cc18baf1dfeedb6d3c76b00d84f9aaefab307b02334e24f24124432ba8ca8d32b6a51e62bcdaf707689bf170571f2e647c8e020d5a7b0584127a37f91bd0577dee6cd8ce19ca30d65a629937811977d423b849c5be4b3871194d13d1c0030bffd4091944b0a488022c9de8898e1aef743643b1f9913d2c2ab610e470fc6bb314b7a80763ed6ee8f8a585721fb1405f6d0c6472c96c81ed766bd466729178dbd1808002445f9ada8057f9a205ff6d03c2abc99b34f9afec32d14342f6343a02656981d58fb747a3b75b15ad7eb40ddf5d366e548869d231f900e85fd774feafe173e23977d2211692c3a9b67112808758cb1c5a6003548a0d1dfca9781e5a9086e9c53be47ae49a18d2bd0ca12306c2ace63d73dc5b9c2948992d06b900df6206930ba312049a632877b351eb4d9846f5de4c543aea061be8bc207885324a38547e7f3d59349d1ec99162d72b78372230daf2a88bd5458272240a729c737d4da7e4779e26221f1b77df5a7cec0b8b205dbff86f71732eab9423b990f2900a7f1d7420bcc7ad82fed3c1b131fac61c6aff72897c9d01cbc8e3e298a00e480e129cc344a236a38bc6e2616df6abae7c1523bc294e4853441792d57728aa18e234c195082b140f8b73d751f18e209a04b2d246df6e50e78c19e752146d54174a780414bfcd8ac2722d1b642df67df3126720a5e47f9f3749f790db51967dd570d05a50559f8f2308532720ed24d2da0840cdf964b575daf006cb5ed43f0f4d53c03f0962c7ee926d6dc18308d79b6956489fa64f7ecf84e123eeecd35f91fe55b3d8a62828db98ac44a27d72186deeee284d268d951fb1a5413bba5d9885737b54b19fb039a29b2e493bd6177cb8b8b6cffb5d130b4c9653f1ee3046de38b05db5966be41298d0179713bd666bdc720a4892d6656f8abf3d8b0f072d070649f09080f687f63c1ed34b7384c3e6b0bf6c18807109f1cf2fa67f3bef9d16c1ea1c6bff3498f6492b1267170f56c789b957ae4561f3d123af3600beafe857d945e7c8433463dc1e965b03ae85109ce253d331dfa4e3332bbf5d443c29d1936e7fd37c0bf41226c40c0d4c3f8d923f16cb9c57db5f1ebdcefcade110f8b40406d8e4f63d89b86ef0a199d8e21b224d3394322833c95ce1c12c20c08dfe018b4c1e5dd42c29f953a1f6734af19240f4632d01bd12c897ab7af67a58e7f75430a8a4fe6ecab10b4e8bf556e4c82f1bbfbf875fb5986293f516c9fecc4cec5bc439ebfd7e16eb935f111e5eb20012fd5b9f0f9033421c1b6836560065d44084758f3bebf9a3db94e3922fd9a1e4d3fe7f334225864bb23f7b9a8597bc0e627669a8e7a748c09b83ee7b26b630eebf43485b984f6fd7865ce6359f7623d119bf6e645d34e58e2fc7dfc1c1d4baf8c543a57ead500ce6d6a38657585975d34cdd9bfea4ea6b2b46243545f914f8f08d8c1e1e15d3b0a6884813e0947ce2a3a53c628b9fe87e509a0150f32cbe9d2e8b056c2429538326e4bfb499ddc79dbc203f1fff4492d53b02eeb9320ef3195dd7994e1f8c1f293c23a76a3fb13fce83dab58ec091e667b16cc1c557abfba1adbc852abdb1960476099d3b01adadcb883c1023492aae49404338730b75e4834e12c3a3573219da09d93a296ae4f6b7f5b127dcd245cf2549c714a24ba6f411373e1ed0be7516296514a96c6ee3c3ef0a4de11b38ba7c6cf9af282cc9c45b69803e45252a35c5c4cc950bdb4b1764a371a103a0454c7d43a1f60a2ae90b246f9023a2b642382bed9b0c17342974c85778f0fc67fe577bf8d1f944fb70d7fdbc589547c5ca4221772a83e7636c87e29b9445d1186e9b8ad585f1df53b2c92f788251689fe36e5564806ee450c36f1280720d6147c027f2fcc715ebc7ddacd1a016730b03f637d519b31a17d691d0470eae8bd36f944f21317e1bd2cf1c27a8b00bd7b9040b88341dcab28c085564b7a37b6f9e9faa9416db9d1e45f25056c49b8f81520f0382ba1ff8b564f2b0163221e13b54bbdb745c8081cbf9d5a6d94961862696e973f84a009a309e5b6c4dde339dca1df35a2d7d09273e32b59fab9863c03685cbd98454817f395cb899b8e4a0650990d3bd7585807853f12c26e50e1d04168efe08843b49fe8331c15fcb0b7df197be691182aab05bd0de73f9b45fe874654213e93079b68b5515a7db956066df092ecbdb50856aa976e3434e7e0c6f93b478202574555744ad7a67412f8205e6fb1fa7001d2b8a78fdfd01455303b797b450d895829a4d5a2ba62b02a1699a8853e8ceff64576084917b0b9bd90a708d8c22a469a245c03c9c4e8b4d608535eec1c0a8d28c9d379f8908c7a87c2e3792c55a697ceaa1530f66e05aacb55abd2c5e8c4ece658bcbc22a63e6378c3b129887dca929ae39155d9d2a9085ecd049f5514f8eaee2a50215fd10ad34070b4698b316dc88a65f651acaa023288aaf4644f456c886a896ad93a369334ca22e6370f1ed929d48417e0aad3612bc022e0ffb52a21d1fde6602e1f59da088ee4558519551150a59fb4baa4c05bc67222bea465a8cda8e81f897d27dd35dc091c96972e69f0e0744932bfa36816796d83262378ba47ceb4e25915c68eb21d1d56840d6fccc3c681035d0a16d31e1581e10ac338f0db408aa5b29fd4b0ad1b31536c5a0995a976bb165d6ac0f90be033ba95716b36a7482315a9cc79e97827b1abc28dd232c2156f70c4d1a3f1e7b18b5ed02ebffaf7073bb015020725a9e26fd291d43a3a18f2800f2e61b1f38e9b76f9f062a093674dc951eea14732d2d2ac93fe0ebbf79f5882a7aca7ba57e0290a1ff0a15c5a1cacaa78d6b2a56bae3b79a1becf7f837d8e4cf9ab559e08fd3c8ec1f34e48ff4376525defefbf46a85f2325c913adedc433e2e7a4078bc5e2fe9d90b6f62f781a914127744444b7ba0bd67e38b03f38eb4c50fd659e75f6c0983368ffece99a2392fc42e23e8f3b173d7922ef3c1eace06bb8748c0aa13adfc19b2953e196aab2b7834dfdd04f0520c03550bcf6df4ff5bdfb7ce30adbb27adfa0ccd0e1d6895745a21682d5fbe0ad39f5689ececa7d1bdfc8c1b1adb9286a6f0e9793671841244b76e723e94216e9a4ee6f8d2f42843eadcddf36912504bde40e683a38cbf970af2d4183f1c78f49d92b364324dc35b0c75c8cc00bcd02b8f818ec91e1f89df735f933e4ff81fd7c2cdbafdbdfefcb6d2467b0b5910aaa84a051780cbddf3d5fd52910d31be47256e5be39e40fbac5965af40d199210a9f1b7e81ce6c47a4f3b6427ded880db6007a2bedcf66b1ba6a7710dfcd079f5176a00fccc25b12c2a66c371a271710c3dc70cef254c0c341daccc062dc7800570be1c12b082bd63d4c1f2ad7cdb1f08b5a30e1b8973ff18040cbf1320f0d0e95b564fcc94dd68d97a455e464e69c1062b85679623266e3183c94e06eabfc4332d634e727a0038e59109ba347ddee28c068d74134992420d2caacc2c1bb8cf0d8f14dcf8c930d851b08ef59fd13e3ca3c2466cd912a0091d2bceae98f102f065a8566b5068b2b1f5da2edc7406d58ef240f780b45448d432d8c780d68f6d3e0b11a2c07fd6859804c8e28ad5342c666102510082c025e0238991920be52b21a3356aca954bd6eb9e39318701395325c5930271206a7b6871de2a5c37037e45e07ad5c55624f14416063c160012a0817da1134829299e3e370e3181a17c6054ffc56ccf902c36a8dae8d5c0fb6e9ce87d395963012805995e43275268ea440accf2617620e7f14573de637b27ecfd117cc1e1541bc859cbab0158238c5b54da9561aaba7d1875a62264b7650de83b32e588c3f0d0716acb3a1ce6e6cae2a080bbbd4b3e5b3609234c41de6681ccf7f04eef61c9030cb22f65473feb7fc878a5f7fd83475dd2f4b3ed77815ac595867cdcf9a9060b71672024ae3609cadd62d5cdc24483325c58e1ad90f42f012adeb74eacdc1e8021c7470fbf122618bad4c88876ecabe808d531a85de449a8271581be4de728f73f02db9adc53e64d674b90ad61994a4dc4eb16d5e3eb8c20e997247413a366156a7a4edafe662c3c3c0801fca05799c4f878301ab0e9c9fa6cd40b3e092b26788189c625e59e2f69273501502c1a886cb454180812a93f269addf13657a67e282b59e06c27a0618c129497386c99379edd48c6b8a8a8ffbe982ed66f201184e1fa0d8141168211ef4568c4c4720404c893fffe02f92879edc8ceca3bcc7fb6bd62cc4368fd2aeeee7f88f248694e8e7afc4a5c0f5bf8d61625ed227a1b1a0b9e9358b1dcf92f2138fd483127ea5046888b389201555cee98439f08df19745a69e9792057a21b1f4e75e447569916d1301505644f0a8510e970fcf3427cf22ce6f28471d88cd6bce768ca7f4d0d449316b9581221ba501aa5d9fb58fe4d84bb068c8f5f20af2e6fe3c0a3046199a5ec2ef5d6486d7355a76cb5f8790b3415c0931ca7b32ca065e36174280a6ce2ddc919951a7464ac6a4db61ff61de1f8e6c89eb23ded61bf7ddcd32a4fc7db45d0d9891684cb414f759961ad5a41ad3bb14186261fced6feda0479b05bd99a78d639a3066df1f5b08dbeb0c9c27da8f7744e3d98b9d4082b17b10a5f0593119e0cc63c3cb48e253636ee70b3a84faf0876ef0b387670e641b5fd2577fe838c920e2e9b9cb98ac698c832c9fd54f5fef3fff77c06c41cd73346751d4a7c70fe5a4e41a05cbf9bf8fa4edebe16a52c4c11b2416d30537691bfeac03b28c85341cff57c7fdb21bb1858d14aea4e4a0ebd69f1df88770e2dcfa40450ecf8fa3951eebbfec871d043295c0173ce751af4e0650e9842a69f58b0a1a0a1e795cace27e05a821cf4dcef531d31d0ad20f14c75352f6c4e1af3d57ad52fd4f57d52992e2ca6bedf1a7d7a8e97ce59fccfc40976264c844f881b0cfa9c107f351ef2968e53f8c84ec5db6e40adce48a63ea09bcdced80cdd57427f643e15e8f862bb02adf726031fa2653ddfa1a30f9af08abff25da4b45bb8a122dd3cde29cdbe1f6b0c24b32a94fe483420c91e8c130fffcef401fe8f9ef0451036638f6650a337da5bf11d1237f8216be2b09f3c233672c1f87a57ee27fbccd3e828b72172c5f2872dc487665538c96e1dd72ff394598f69e5896dd3b81946ccbd295032d9815f83a0d026cab3c541a1594bdd3e7818b743047817e7dc11d50b0d01751be0525324fe270d7e917acb042f3a35ccdcf27b9497a9a4218908c3ba264e88b02cd43e6b8938f3c13f03b02047125f5f327d0c6e2125eef895aa3d7e66013556a4c56cf3494f20a5a6160048de5ff8e33f605367c062c23f17ce7f489e6d517ece617e1883cac7730f69baba2760b71484a2723a5cf6aedbe68a33ddc3ef7791279d16b785e094d6ffbedb998f54fc060699a373c7dedaeccbaff5c696ee854b6556e04505bfa761079b2c0cad0ab2cafbac49774a7050348adce7584e07df446ee95aa46ef26a6138641b099895f97c90120d3320d098259e62c2ece5025322272071f92a95161fccba3b73d18f0485d3ac91bec90df3b30dbfa6410bf50aa7e74364bc4f71797cfbc681886eaa0621159c0707262a705c8f42cbbd165fe83d4aa3ef8686dc3a603e7c4d192587fa809a7c19cedf30cb51ee58875b508918bc411dc98ff163c9d7ab2f12403f440f876fc5d14ae2cb624a2e7f11c15fa9f49105af3694bc88fe1b3e49997df81e58fb997d339fbbad324e626a583282279cdb5daf409bbeccbc080f91cfb4c576722fed3bf574d918f7846d857245fc73457e6532a1cf991f0c9a41429c3cd33696396a092f4f5aa84ee710d89e9020cc064c670ec4186b427f2c1e6cb6555d4822578236e48d70ac0ac04ef7bdd330ace409a38843ea2e4ace31979df7eba9fc74c35c149c7c6a88b357b92c1b75d59d512013741b45969c54bfefb89f7cff1c35d858f0f1c2a2519b46e33937f56263f6b9ea349760dadf7993966e557cde75b29ee0cfba9d3d7bdc41aa9cf9541695808b3c7833d3aab16352fd8b749e9bd5c457edbac0f92d4cf71a8d96b47682a78edaa0d467ebb9f355e6ba5e65f9c7ae668e30b29dd089ded68f6b2df10fbbe472617596fdda6c8e00e664348a25d8ae7cc4af4c7fb7ff7368f3b1ec0cce33201ad74a300c39067802f3a7669db71d875bc3afe73d429f74eb2cde3d2029fc82e867610dcb487ad1df03d3a876e38c496bed99c4bfb9eb321ee7ca7c8590839b66fc3a4ec78c335bd79153afc23b0a0978f052beb25d714aa11caacf44d80d0b8b97741b5bb84406b7a69c8d57acb06f0f064f9aecc4de8a2c2b95f1d8b8b508e9da3ef637028582f5baa7411be43cd8b143887693c4d2b0c7ca28b3efbf27bcbdcf19be83a27921ad519a6e411e9970957d04374d9d60f8f65774d1a760a9c1eef61ae07a425e66ca735c7cd0ba663982e23600c8f8e287586cd41f1a886d5c21b03a283976332da9781a3ff87885cd539b0077701406a6d62e6515da9e2a048491f05c48a49f0d0436c6a10b36708bcfe0e2df91f851d7343b05ead9dcceefab81dc74432b84687ffcade5b3a5d3ba8744fa58d2fdcb4a366191ad292ae840b9a71855db2c1c3fc2a88ca0cffbcf11cc12ff71192b37b5ac15cb02a1b8247516c52ec9e7e1a0bb82aea480619518057c8ac85799c60c7ac8fd8ec3eb611a467c273ecd9108a56d6029db0420107618de084edc93d09c83b6232f5434949898ae9f826433eb3fbbed52b1509bb48cc084c5acad143b0d62c03953bb0f0a17b7e5f7e7c63e538cb505172b98fff2e166ceb5fcc525a9866879c87dae3be5ab65e3d8d5779da23cabe64ccdeec47d382475412552e0541d460bb19a65a2356f27acc394d441341416ffbf8572018183ac46cd1280022115bb1d51b776c61e005559738821c67226b73beccbdd4b6ef9eaa10df43f76d1d4489c59eac32c2c6977ef2157f84ff7a1d719fb681090cac6702e9d914d36aa8d6b5f0cd2e6378bd83d943bbe8376575b3cd78a7ff63b4424e9a1f7ae415048ecb6202aeb020011108fbebde7cf001c03300ca0135a56044f35a84502ad4e68082de90e6006ef0f5e8f0d1e6480d4025696375cc8dc4e9fed3799e21edd5138d627152ccf01e411dca1f8a83deac5df4ae736aedeb315b8cff43add3ebc97408300ef9c59d20277f77a5b023b79f6979c1f13f8090f613de7b6869cf0366d9cc7fa621b1ea57a83fd090ef76c3daea76c43dea3c9f91b78f84da99882f6db30dc984856055eabcc486a2a92919bf9c9e261a2ae020f53d35c8397e8b5d7fc5d3534f5aa68ce4de91dc068d8fee3898c95bf568b4eb06445d9e806805570ddf189d08211827e90559cb7d4d2dd2b9ed79a2363cc42c151f3077a339b3e2b5512316f6f3e4a30921af1d37ef4c42e1b9888b700fba6ebd4c7483dcbd6ff67fd50fecce49f1158149a069dbf459618efb2d401fa8109bc8a305dae68341ecd47146d2345df8f70dade6e8ac94f78d7b746e660a1f93fc6bd68b288970c507fec3cf080a66f8b7e45ce19bb579a6cd4ecbf97976770074c45be6f7b3c9fd32caff7bca21d921ea418cf19fecfd45d2ec28f8dd8bf4a4485e4e16f07635cc1ff84a0a0baf69aff2f6df0e27aaa28e13add852b72f69979171b47ecc62669807c24689ef34caa5e960c56b802be3338cd6f81b781918f17d670d4ed66358fcc0bf4341f8ac9327f340e67e400886ef9ba03330546d586b71068bcd17241d7270886caecdd8db48b953c1a2a9d4e25bc696684801413a57993883018f478c4838160f4b193bbb62b401cc4f9b35e8388aace39f82be8a9cd704033a7c68c657bb1034802ec60969e1c3f11963c37473c68a6eff74dbddb4e5bcd8bcd8b00f8b0f0e4b8feacbcf7b51bc5ab13675e636dfaacd39606b38f1c24cae8765a027b26dd44da7ef872c20bd0d36e8559f372c6dce3ad666c805258f92336b79cbd96340710e6b1916d0f391804c9846b8860eace1a39cde73784533ccddc1be5700c608f21165eff24fcafe7069f08fdaa371fa84508bc1ce1d89c1c2beeed807e77603e7716dd0cc9824d07c00c829d82eb0da413666f2e1b0eca2ec4ae1a36c2dc6672f0b2ef442e5c0b18ffaa917fdc17d56ceb3512cca352d659dbf9b8629d292cc1a7a630c2648b0998e1b3c3b9bc169a521a614138d89f579f4e7c2714a4f57f57e38730707ef9516f753130d44403af3a277cf413dc3f762aeadb0c918fb1896acfb4c189cec9c36b4671d8803c5be4f1cc12d1c6983e95d8f10509c1f9a61b491ae689195987e856931eb384db3e9688029d3229f7fdb2f27e092a098805cb6e1a2323a63ff6c58f370fbcaac70a1daaadde1c5bc1eb3feb0fd6e68e44fa0280a9897cda9798235bc188622bddeaa401af18f60e2e9525250db8c7e56dc54cf00f4abdbb9a71e00cec93505a1285fdbbc5187567f5acfd81e5cf8173532db56da124a29938b7a60aff8fed7f4bdff67148cb38c6ce416e21c36d0398a1455cd037f537fcae49fa79de829905732728bb1ce510cefc1cd6cb389fb3e35627ad3767e89c03bd2ab8da14c69f08d76e7ce7bb97d941dfdef5061746b857d9993f835f9e96075ef911d65d05cd3cd09419c0f2efb7d9f05ee94528b1cf6006f30158e70a751bac1fc0e9c60c03c231ca8f8f24e438d3a3f3e2b39e6795a452b6d870c0b1f114ed9f5c61c267c4a377600b301b50cd29d29c585b386f9f7439b375e6876d44814710c1e2961aea3e2d23f6bee6077d02b6494e67003cde03953666a98f259340f20c7bbda9d183eca7e0eccba974474b59f00fda4dc586bdf7c3f330fa1a60464adcf296a057e58d090a406253483762ffc07996fb9ac58672030d46deea411067d60afa0aa30082949d64497def516a58d0e490c5cbc86f11d3042938f47f85cbc7ab6fbdffb418fef709f5e52a25cd0ebc5552b1ee1fbc81d0dc764b3f87d06b02e49043395e7ce3fd95393038f3334b41dc55e0ccd7cbcd4d9af5932f58ca0c3ef9d34a670956596bfc06fa066b0a1f2514c4d2f8c228a17c7ae2001c967b200d1ad2dd1f96cffa2edd21836bb2d4a7effba7c99557e14e589f79d6679eca9682f1ad1d2a502b85e6a682f3baaf030bc79814dd5cfb23a65a6cf76895f992fbd935f4d3832a668799d9c55ff087c8f07cb4faa8446f164ba5868164d647f95818287bc120077f3704d6a92257ab6947f809c13bf407ea1f26423f41dd980630610e391b6463c8076c1474e267688f1dd068b5a8563bf7b465d39d8ec1ff8f3a04e0ffdb4fe1612ade3cf00abd10010af1570a6c690eefbfaf256ee7fe32d5c6489f9796bdc46ef0d5cbe7728e204a234d273060f61c74b0219cbe5f3d8da244412230276db2585e4839d71f58c2d0ae270c02b9503a1ddd54aded0afb711f866c0f46a4e62f184104eafa95e47872f3b482b3d35beb1f62fb1a83d35440b05395d0bed173b63fbe4b554f8ae7dedfaf9b0c4a415520446e8a6d93b55ec9c418a45dcc9ac5850b2105d813b8cb335e6419e2b2e1b70cb20c928cafe61c73da6d28ac82316c43db0cb004c52a833b51e6fac2a23c4f88ec53fbe0a4ad71e6e6729aad5070fdc393ed6058b627b4b42f407c07ecf72ab91c237b6345136849661a57ae89b3ca933c68dcb8c5bd0a549ed900c49c0086bd60c3ab7f5379ff5df1148f98e64ae1ad1c24cf0e6fae5ffd9eb852a718df05ee56dac13c675092d7c664d98641c83d45c39c913f9b063670b5dcc9c5adda2103bca43e3b2e36db3d3034056e672c5fd5325e193acd69dc82c58b366769484d6fc2efefa31733418de6b12c725d15277a975623540f84d409405441ceb02a3f84108e8b17712871e22c16243300b42462384f391027f3e03955f4cfd2dcdbad39464a647a90755dcca10644858434f2a12fba3855200a6d8ddee54fe960e70195f3b20f5107e4a1be91eb88f996167dc6e232031b7bcbc85aea537d5907c2c2ac0fb13037b96baae283f6e3577dec72e23adf2de724ac59f52d44ecddc1b6fdac900cfa7528c8371e88296364d2a02c585638f1c759d942493072717e875bd3accd7c5b431045a9ea9900b9db2f06d2a04bda0088f73817ed04b7be29370439d2da35708e2898bc5d1e8434a8b4b012d971087269393e6fd0945ba1ebcd4c687c8ca50eed0767707f7c8b979ec26d2eb1f05620381cc40c537248f49b2785f133cb86d397b8257640dffc276e9462cfd1dd4233f7a95cf01293460543bbb6342ae6c798b95c94838a0bf01ea961453bb899edb63f59e6aaccaadcd2bc98bc9fb30166786921e809f45be7fdded8f031efaf70caa77b06b6882868ae0474c4ffdf190f3b364e3ac4a6fd9f9c1bc2274bd7232c10e004bc03247390412130923c92e5c3dbbd13cd8f8d4598ea616448e8dd4f8195027c241ad34a23ce720e740fd702af0cab783832304b91f2c2ddd14d68cb07c56eb1384768cdf40fc68b55a8eda7e4c05d89475419402b1c3faa212fcb3dcaf9705f55717b88629d5bc7dc7ec3b4b82a734fb62eae974ea7ec1e939372d3fd6843bf3ae60c6fddcc7953a769f7c28766dd1a9039caa2b6fbc75d40b75d87c5c1fb91bbdb8db5000978ce03f7c78aea767600cdc74ea6ae8bb8899979a6b513ef5add17b2c4aef417bf8ef80527fb0cccfd8173f562c66603f99857a2d572bf5b38b7615900ff334b8c9f717c05a30a041b31bfacb9008957f44679bdf2e8de7a12c28739e0114fd40ba21db041860f5686f40a2817b86984f6095d4c63e513550fe0610ea3be5880c43d8c48740ec34b0389a0aa9507f10c7c5640a1e85c852ee202a3dc9546ea9531e90b25c14e3e0b16709497e9ecf5888ca573b30f9f4e5a4f4405fd47fd3a6cfe3410ce724d1baf3117a5b6c8ccee50aa282b8e1b745dfa940e07fc8a8af4d2107f3d808309822774a502a8c774d0ae71b19a077bd704211756babcda608e0d080da536d642428221ed66d949b4d390b44eecb73a7ddb30c66a11f7717f70800fbd11bda5db75cffd68c001d2e7b14fcbae6475142fb33ed76a699ae0c5c2ae54f8c2b25a125d1936e9d707b0c24dfbd62579750d0d1d3c0a92c991892c88caf91ee918e458fb27e1e2fe6b462c873d84e37983fb810cd76204e8d328759a1327456b38336c22e0f34b468807ca5fc79946337fafcb87b06ae8dd93f44bd37771d3eb805d997e9e66cffad20ec9390d31c7d9335c652fb9c195d8f7708b7d3c0f394f828e25f5a653f6ef00d3eac410f65523a9448c30b9f7c8a7752a078ea248be98036769a119fc97909180385d6dd0b431add4d463816ffbc5b72cc680d2b859491499cc680474ad490b7e030ad92e20bfa2003d6f2e5614304cbd4acff73a3565a6afc5e832f9c654a53c645eb08a7ef13a635d5402fc780ac0dc541f1c593a3ce01e5b09cf93d664215400d3fa3f153c44f47e50ef988c73b4abde12e46d3b7b9197b6353204f19d91a309444a528c9937361dbf0f4df64a1e2fbd65ee327cdf5a9bec94996d30700aa5ffb25d664569f736d85cedff8d52c01f623a3f08b6160a3c6ae77734a1a098aee24e2741c28e010788f0042dec0653dadf42e945ae4ad1a18a5b44eb0335deaab18c19d04060dc417ce632aec07f8c665d8c7f40cb0398b2ad685483051c50fd7c580003130af0f687528f83cad622f55ee82d20de0f0121d0eef55d3cf194d91c074fce47e1e9395e4741021b2218996ac9ff71d463a0f083d3a013d61fcc7eb3efb5f915caf4e7af380fa968448f2f00841d904ef2422db4794771c33aae3105f6c9c4176480313f6083cc9357d424af6e74cc213b8186314a3c6020ad52a825ce16027342e446f59e58c7055633d468e1c6d4d578af479390d9c4bb7707ff4cefe279125a3613fed160f8dcff7aa44776fcc6d3323e38842910ea3497af7e54884712f4fecdc356f63752ecb27aa6be4a6ec6380a2e659be18d4f68d4d9fed9e5a8789413c52171b4512be72c62b9d7ecffce452a63ce3dbb6ffe7274b4ac0732cd900e281cdcd95b5410f126a3f6b0bf1fac1661da073b5d55c5423b6f855ff562ce88c0e48b523dcae35f47ec573b5f4d9a168bd469eb9c5eacf9f6b05743ffc6d643082cccf52cc3a94a793a844fe40da8a768c240b82f78310b1a03c5bbf230ffe3646378690d4687e4f30fc06f5310a1027e570be311b8669987c9257ef5eb7e7bc6850fe0295315275e6a37aafa8b1e2e5f93dfb46dd10b43f4bbf9141b116275f01556668f6c3000d3456f49cec0d7fe65a1e113d5893f0848b0da91b3f1208608d0452362081c5dce062d3e7c303b0b40b26c67c1a64304e8e56e1b921f7557650bad38e59faa5c9d741ae8e1fbf504c067f8b1a4071169c77a23e610dee5d87e52e3f7f2cdc9889b928ef06e4e2a78a1785ce1c3d4fae09febdec70c1fde4bc45c1530a4d106cf83b7c7b31d8b7e1f318520ffab2d78b3a68d7f0d28721c373606be86b8ed81b8e7b0dc0b3d5e51bd3b9a10f1947e31ce1514c95753893f7dff997e8f1087993118763012ee3fbca81edb1d72c413a856f65eb13d7613b2be67efd60bee9022b52cbef3bfcfa3539c987d7232770f142df7bfc557b977dfc157cc3be421948cbd6c91fbff204404172481d9d8837f127e7b7da2eda72fb9fbc224b3f841aa948e2d07989b3035c12451be793239f192bd7718137cf4a065c5e3603687393704326dd4abce59986f9c74c59aa6524f6fb490549e8e9e9dec42894d4e5d7ec5de75a538d320461be86979a201264f33f251c39ed65ffa74c57f7bdc803f0fd7b09250c61be7796b41c5e17231fdd706fc5adab8eaca494cc5f06b205c105ea8fe923273db5bbdfea62c3babffe72e5f49b7939be7a9910c55c8e32dc7987ceaad2c40001d5d458728e6a3d676a4aef296d7c3c255ed79a7e56ac50e9c0a376210be388a8676e121a7bb7da61d5ef0c6fab88afed2bf02e33854c43e8f14f1b7811021777bef2cd3f5d965979040b6e09d9fe5501c1612892933f30d23d529de17ff9d4b4718b645eae623f96833ed5452c8ed039a50779bcc665439e7db8f59fbd1325b36fa741d0079c431c1c1db25530e8cba3a7ce0e1bf48776260e1425014cf9a86ae30fe6c5a3fd0144817d8066da3b393514d49d025afca0c356f3e2bebbf8599632b4df74e8e8c52350edc41cd72687ef939269764f6c45a45a3fdd73483778f22053a1d9fd82fcf549d308a201d8dd126347e405a63851cf5554dd6459b412b3e0abd19f9fc9bb42614f1ef7d328e919991d3dcbc8c62bf7971ac9c82ee101a7eaec4e40398b264f35109cab8514d18ec1add6e04f127ecbdb0e8f34014ea340b18302538d95b8a95566678bdfd03213b0138676c67693367012f0b520bb3113645b62502929a944445cb53b08d1915a850e0b18a2d05679cc5c2c80e6274b638d80bee3f1c67572ac9d4b3e6c6ea95606d4c3bb83ba1e4945c240f5a1913ec683e8e9cb4209e3108ebe46ad5b055274bba1752fc606c2bac2b0c6128ca910499571d3a0b6e4800e42404b5d8ce49128d14f5d402f7a25bf298bd6ded4f8cc1d17bbae658bb05a8b69d3fabc7ccf20e173e5b5fe41d3b55dee8ddccb15e65b40126fe786f84eb4b036acfb74311f884548179b0d88dd246306ee42df5ea5913dbac269594b8fe1a0d8f0c4a39cc402846ce941393a7e4e45b0e2173dc1495cbea2913c46467068f06516dafbc78653be054e8d772793582e80e5573e00339041e4788185530b4fa7747d1b236c25581ab3462a235f4c7ada43f5c032e1d63651a3c81bb45f3c7fdf2311e56fd6f2b2f5eba59daf453d338ef5024141ea92b94ae2e79043088880a099a45209e52dd812415c90debe3f1eaa2a3ee8accfd405b0c06c7057ac396f2f92381b338893405fd2b4be98a37a1a9d89c5df54c54e3e049c2b1f8a42af8a58f0cbc272d887e89816b21fbaf5898b8e2dddc58f0974c68f3248ed9f7bd30b566b478d5a96f127eee13c13b787673953e0713a76005fcaf35ea7d3da343da225496a3634f65c6b6a4c8b8a393c9425af20fed69df27f6650ceb5040cff7d997c3d7e722792722472af730b8f55791ee78de29414a1deeea4462be62f9022771d1c61d30739f6db1bd0ef78f0aba2db2131d4714f7f1760a5193b1ea823aba146ebbd1f88d43f90471e5278526966f79bf03a676a11839f4a83cd80eee6c2604723eeb1b4d4b95538c6fa0067993d4bf8d0e6168c414112698bd7b3a35ec98144f245a99aa827d9121c92745ed7ae8b61475f5dcf69bd21ca4bc90c0038b53d5d2bcac6dc8225a182c671c7a3efec9b7e1387aa14f56bc9561feb09a804797f08a42dee6dbb652e90878330701cab614b1efecc9a69204ab859be2826aacd9a27d579634ab888333cfd319354c7f58c6856b583c536a5f09638506b864146241246f2dcc884cb61fe0463229d78f6f0af3857d09c022a59f30b4beeaecccc0bff4f6663a0e48d7dedcc8ead42ec383557f91d86f1e08b3d42fe10605b22e0c7280c69c4ae670eb61c3cc5e7f6f15c5f9f03a07a71ae2511c7cbe54675926f6b96c06b1f8aed70724122b720203d6b18c6af7442ac29f87d33b666743521c1bf3fd36b5d0ff730aea22e5d664280116832f8e16fd4096f24ff6624571cce426066098d5af78d06da04050bca5b4a060faa42e0bd156557f1600ad9978f52c3cd8e4a879910e7c320b971e61b755a2b930f2f150d9f548377769808a5afee0b2051a677b8b0d4d0daec9bbb78cdbc4d623bee1e224585077c7eeb458aceb118ce1bd49e43c2c578c11e2d0c6421f57380d496442893077cc7f0db3b5421d784c86c45f4ade46ff8a008467e4681126f6e4c912778e0d10f9b7dc4bbc4bbe4a0674b1dd0fc83705171cbd12a9f6467806d10a03b7c887c943237333891c99b03d6eeb0466d608ddc9ba7792cfcdf5062f06be7b6191a6572e1a8195aa1bea7abb550969534f05b369a4e882898b79accf58443d5e62c476898c2dcb768f4f348d6cb36b9aacef65658d2aa0a1297b734990108d0ff144c06ea1e9d3a7a79ed58fa3e1745da88a00b4adf3da41ed247e8d61dd98dff68352a157fae7efa20c175eec2344b088a00e030cd14225b4a4c4fee992c1fa7c64647977ae93e2474945178c9765a649f8f8549305e0196068661061032a37093f304c80e11d44eff503e3473bb1ced4d820253f109972ea4794bd0c6bc76410c1be9429d62d360b8ca2b525bb573c591988ca65452ba3b249a03c7d5199ebcc3ba3f79f0306841bdccc34623b5ec71737d06ae1ad642ec13ee393f4741a33a667dab704be3c037fd20a512b11862a897a33813738693e7920a2484f0294ec43eeda8812b5af0b4418387c53144cdc4d9198b803e933dbf23f6355e9d372589108e12f4e6c3c32155469f5eb601fe8537a67dbff3b3a24155ec66c103aa2979cd852421ecd6da2468c03729fe06b587d949835d7a8d5020d62144657cbe1e8db1162206f1790d898a8946f5535820074afc2ae6d3e81c7c7de63ab55ce460c56c2fd3bd29a366f3979e4c6c1910e9d9c9e4931240f6e8873a63c786aea91d678cd96e2d2400d72dab983c2da1170bdeda68bd359dc2f098479a449a62ac3799fdfc188afd96f8cb74f4edc6ee227c0152dae700c436082842c265fdbe48dd5d43f0e99e8b0ac4c6ac640ff3b931a5db2705a3a1f4ba6efb1a15c649eac8230930abae799eb8e2044df54f474a6165c47953d5e9a0dbdec4aba69671f15c776902794dc8557fa7513a33751b3dfb4499342edea0e97d422d196afac73e35e03dc38e2652961dc4dd1d46273574ca5c4b3d00f5b4c71ff7471e4626673f8cb59a27b9c6c8292c5606f7333a539b7eeca3a7ef2a938220315bfa5bb8f5b882723fb7ccfc19250b8391d431f4d1778dadfa3666e65db2bc5deae4a2e02e28f07aad2764d272efe0a6fdc3c0a3cf3808810eeffb207bbac00b5c0870a07283065e6fbdffd7f7fae1ef12cc9fc1eabbef49f286c28cb2cbb3025e866de0448b8454b7e744648750ca065e95579cc1ba156891bd3cd3e8059d2b861f9485e25c186263956337ce10619aaf48aec5e20a5a43524d1971b6200718c18f9a73b25e56ef73f3fceeaea66a0d3c22260f4886714feea12b51e23995dbdde924e184f8d5544a613e2bf43b1ee263a594f70ca88b33059a1d04df485e573b6f37bce6f62adae609afba7c489b67436e5cf91b6936cfe350441439e074e6a7acb27fcf33bb7bece54650d601d3f3f7fe6637504e5ea989001eeede4d6bb988ae749aebab31a933df7bcf6fe32f450b11b76813c2785ca84399329697a06aba3a4d5c69ce27d3985ed318e51908dec231aa343afca754caa305b714f8df450381e2e9ef5d8a11329228775d7cfa59c5b065f36fb4cbad2c0e7282e0db0891ff35367f512b39308b5d25f4804487e40dd9a00cfd71d9c3bccd5597287ec39405dead1d50d6904ee96629b0a3756f30af27d9ecaa8c937f99a1d70391834e10317d8e00e6cc09b4a253eca8fe42e68e1098d4ec9eca411e015aff630791ba73035858b2d043246f0139fe535fb1baf51180d553939dcc56ff930e39c2bcf6abe5ed7cb15b4c6a13fc95b0475ce69896fe20c6ad987d89e5562804f1abb452f2d9dcd16b5b7a87eaf54411ee2c48058d08073a67b21a935840adaf02496c1ca2c3b618c2432f4ae1bbd3e66b47a516281cba014abd6f58cdb590238beb88544b8c21c8b6b6a0cbccb34715fc85cda581604ac8822a1ed680ba9a5f922850f12b2a6e51ec2da7d557e14d729322db62739129624d114ea15c16027bd9248cd706d73b984cc59be405b25677b91f60d61c3aab7867078390c6a0c7165e6cf1cae47f9a68046ec85d10748fe4cacc3662f60397e856042c68f391de94d02cf0b62b2af4eff67bcef61dbfe0b62f061dfaf74ac383ac6713b4c1657abcdf52467fc4bb1b8bfdc25af84f6286864e1f695fc433d2d40d7adf5fe5f55572304c16a20eac4aa45ed8dc1410f65464b82d20b8587e0eb023324cc7709cd39bfef1f9458ce0a4127fb441ae3a7dcadb5c41e834e2c50b49297bab7df75aa39a17ae37d7441915c82783ad0d8fed51ff672f2c54e77b0da6200ed46cd4bf377248a505066426a17eac38ccafbbd40603a88e4a48bc596858dca7631d4aaa02fcc964f60c46a72e9a9f649e90926a4738292fd77f27696de0eece6ea6e89075843613fd5c5d2a413795c6b646eb9ad866303c9e953a89712ec529a41abd9d105cb0e77fd951a7257d5f668546204b2f128799dc7e662c73138deda8e5dc53358a67044e256092a5445f23be2e7c9dfd856439861cdbfc25c1993d861cc0aa05a2bd1e1a924407d85fbdac9d6eab266aa4082a751bcab26887d146e5ed381fae7f7cea4eef8c619138d470dc2c6faff92a762bc1d3465f041e1ff32e1611b303b2f4e49ebdb04744f8c96793e69ae0d27a915a4d966e7977d33af21b425132ad6743f6657d4cd6a786a7b902bfa01c038af536dbb3b7f0e5dd210b29b3b0d3364d57446d131de5510c0fad9987c8d6e0e1d81ed2a46128d778eca411b49539eb548024a13e7477d9a6f1a6cc3dd879f3cb2c89211594b0645fab966b05188b2d34a5319682b1dfe544a9453dbf4d31f6cdf9c4c2ecc27ffadba746cc892f7d2d2080ed858629abce0ef50d4771b8570b79ac764a3ba03b81436a25f4ff4a9b8bc928855c63fed5cd8679a68268751cddfd823cb78f65d74373b2a70334760bee2748c3d55a0a683e00f7f263924afb67dad80156710d28527310609baccd156f6c99629ebd3006aa8559f63448908188386caf629a05306aad3a0fdfc5135858413a3b296a1f1a75a70e1873618bb23f639d267a00c0363046d1d0f6391499d4d8f03503c4debc0e6225a40f20747ce70d8f2b00ce6cb04bec57233433ac958a570759f7c43842bd0c8ba9fa4e798d373331108f074f319f2b5f2771cea7907e9b5a8d4469b9f8966c2b803b47f080fcb5318abe4a0050d08941f0fbf5e0a20ca4cf4731f7263a1a8b7ed32281303242694f007e9846c5fb368421272e06a4af0da5c452f21f9c10acff627e0f106cf1328030aee69789010b87f1db4796e57f69118c9e062d80ad3f06d65c1b6729f6eef17017521956af10e77d6cd26985e5c00ddd29db6cafbb6669199dafb158af4e34b1a46736994323c3e2c09ae7f9340f955494e2758e5e1098967b15cbd9f46058f3ada060ee40d7191ff0930371c6c8d9fa95d593f07f1c5fe1612cb11d00d4b26d343b5fc63548d41007f961f1434dcbb0b6b9562e67deb7b13e40cfe5f2df99c26f7ea7882cf71bbd092c9022231e52feca39781e50e03c8e08e760ff9d9a8efd41f82637e7f93dcf41ffaa7276dc09a91f4ff23c0fe2c1ed4bb5b387ec18809fe5908b283064db48a392f60e03b7eea7f0e12a6dcb5c38689a8b24fcb7eea8407b8aa97afd455cc1a4b9ddfd30dde064cf35ccfef77e8034d494847b305d9598337baf0a386c909e182e67f75bc9f57c58398201a794c4817d3837b580fd4101767ab197e8484e8875ced2d2373bfc30c1a1081cfb74b6dc68046d9cd784d688894cc8d5113b9d1196afa6cb8a28df82361c53300a003000a001600d000202a00800316e91e0f0a564ffd00da939301c9295966a752d0822ff881f14a664ac5b74fdd6f1ad272e38b9576c0b4bcb3f4fa1715eb7f21ef2e4bd77cc0ec89d302fde0a587e1ff9cd07a11de95afdb1b9acdfccce99f743c7765aaad2061437e72fec1559fbf84997b9d05a31708f52d674c775fc479f78485597be3d64d87668f771ea4f2ea9ed6e36783b110b3134f1e12c4bee6e186aaddd9509c5ae859f7affc40c4161df2678c50a5f8a3b43b5586aaea1200dff9e224c42442a9edd96b1302e7ea5f1284ba70357eb185c25963fa94823fc17609e66266a5707ae96df9025570d34c88cc0fcaf6abaef588bded6f29d3f22e03b579db8e1aa0bb317896db3c237893a0323d9c7d69d6f232e807bff04590be0a8fbd74486dd8be2468966ef68ae1117ae4003cff53facbea2eb97aed4c0dcf57b225ed0c3356be957983021d41c14bba73a785a8ca7ab3fe8f0c3889fb2c7a1c96cddced5e6040f2bedcfe41e98442a4683cd01550f49939b4048b432009d36ae8b4997b2bc783546bbc569d8ce64c761937b151fac08ca1a091da6e21a3ea69940c891facb6d6e7ecde2b55a439a56290e2efe33ad89a10dfc97197486f74e900861d0caa6e747a47f9224d33b9d6492d87f4bc42ffd9759247add2f39080e72e8e01ec567dbf83b708d59a351ebec73433a1a19b79e3f3662f3b8ba729afc4a57007e80f044bc4349a880616e71f1c16002b6eb08f9f01a61793b8c133e1f72f1195f30a8a7db4c037cdd850fa1f5014edbb0331863d1692d65bb4039d29d812373d4896f70420bc6a3744cb43d0aa2d5f4f643997ad4fd98cf81081545c2cbfbe90635dae8e75eef2b695d77578390b350fa1fa4b64f226b4be7c54b066db735e4bb027b6e95900091be946d33e0f37ce0337a01e0b001e202d80c8e6bd3d47d89f7585f0b668d5fcdb66a48c0ec63a76ed0766d576f695e1aa12725a40ba549b1bca02a5a9db5038407ecbfa2f05aa8e62f37bbec4ba2c60b43b29d5c3fed2c8ed617cd6165afb60f3696f04baa98647eed8efbb8687523deb0abbb48a85fc20047d12f629d84826dae0a30c7bbcb3bf3787d186d86d9c518718dfcb49cba91be9ce48fda10fec5a78d40037fc7078a23c59e32c8f5cdea85e02e24d989370ff2070ca087a1617fc81867bf855c986b26163deca5bc745f4a25295a061db6d4819d8047cd5545dc66e67ecc24af55fd00deca200ff46dd0c43aa9383ca05e924dff743af420182621c34a3b465cb7b5557d3cef8caa1a8827b1795c1267ada3f2f37a0ec884daccdeb1ee61d61edeb3677d2ebe59f502e99ce9ae652883786c1fe657887824aed13fa852c1e0ee5c4706d583a0d884b1b2ee26a030eb44e9863b5375823f4c12b980dae77dd7c448a0228aea726f4e5a8e11759554f8e6f72e7592acd47a1620dc3883d4af230ca975ca87f03ea13a7569f4aa63687eac28d6dcf83966a546487797c452aaf7d9915a649d01f690bafd8d8558566bad9abfed313e293023d1490207ce971d6d85742566568e1f08a802d70f4f2070acd5e2e8f691b955fce22b59fe94d49bf9817a29c58c74b91a16c5ee50ce7bcff5dd5be05bf250698626f290b9d943fe319d563bdfc712fab57ea2aac38ad57236ccfedc378eae9046b09527d02b5aa5ce8ea38b6b3586e6efa2d7224ab0de5b2f36cdc05d3bf5e6f9ad6921d1671918b8653033828fea71a9bdd92a4175f42c9f634055cc4a89d950e6809350dfe8940b137b3c832672ad4e5664f9ce7213e3848e3f7d5ee88be2e9fc17aed09feda48a3a3a1dc94c9ca45ba0e500f9dbe231e90bec78f91b89336eaf70a90adcca493b67b44c37f8fd6a7f21afd06d6164c8e8af28c726460fd46ef795727b8fb030e5230ba72312dd9abf5ffa5099c1d3d156e1ff43ee649857417fd628c53ee78aa6b8f7fe23fba21da1bebaeaf183803f4dbc7e4cbd9ac0f34a9fc1aa37ea20bf8e78348beadc665d08a469d3d40ed5c307a17140c418ece6fcfb70aa1e43feec6a49762c3504c7769f27679e5fc682a5ecc6749acc043fda3674956f67f1eedaf65b29da8bc899db6d48decb44278c3c0fe17de1b663f4dec396d5d5d1fd352ffdd5426bd2cfd4fe38a026c32314c11deaad8548ab284a6c20abc53952dff633f99fc6163e15778f72314984d5417106034b0a671ceca2ce9a2dba12559014c2039c19c399cc55a4849ab99c565b14b74c0564d2c49f04b910054af2c500516ec8f22538f1456e06a3f88e7ad5f3675bd422fefc03405d594171ed4f9684958f092a8d16c987c84c3862b900a46b92444dfcc108ea44e249a6568dd0b4567ea70f375a5d98c25f4b9ed900bd76eccaebd9c376ea35bd0e073ffb2b7260509f3fbdaa26dfc1c1ca92c0b6f581078d86ada9f1a539a5f36e7f587534fd73078eef37bcc1baa69253fc724d6336c7e261369ba076b2930f708615e32e6e52ee8258a0390d242506cb011c85766f1111b00eeb07a6900b9c6b44f1eed594386cc0301d223a919cec928f18de709dc7906cc6cd4ddda0bafc40e6147381413d96932e97f607c9067754bf0ba9578bb21363e445626ded6c67766f33deae67cdcdc97571deb4eaf1901deeb8f8460ce4e1830b6bd58035c1869862e5e3a55ae10387e61541eaea8d4d6b861e3d4d4b8aadaff30ced160074544f941648e41b72c165d3160538113e7d031de33d89c4aac68c714c13b32c5ac1a8673028090762cc6bb893853f5e2c5dfa13e9ecdd6cf990fad7067c60194b585a11a5e68a767bc8e2c0f0da692d2703e6bf1044a329210dc9335291db43d619a9f94589665dfaf5b59adfefe88004575dc67276a00cd40574ce6c608f0b963d53801030f0323bbef84767988951bf0075d5ae995f86c8affe0bc9898e33bf4496d10e00528e30d669095d0c0bb68ce281ff04fb968bdb2263e8458ed215dc3bb682c916b70531fc9c1e0c157859f4f8bfe18d65ca7676299d2a06a3aeb1e9829a67c904902eca0183f5aaacb1a14ee40898651aab968ef6b3a3a94a6b3e86b8ba60318c08721993225d72840d7e651f5f014d87d6d96e3817a3f9cf899a8a6f40fb39287c8802825df1d0e0cee488399646f1f78a266c8226a73111a6d60b32de2cbf0cba142a640496f90a72b44df20bb41a179ad6011af9bfad62f4f5681104eb31393280558fed38752617d6d43eb2b7a02eb3f50261f4262225a1c49b4bf6ff3f6ebd6abf3457862c8369ed1506f1715d2a901c633af4cf378f757747ed57ddf2c17c445b910d7f6cbf789cb609754c21beedc871aa5ded75ab19f994949d93d06b880788353d908854c21f7607480384b0878fe0d2d4bccfb9ac9d7f65d318f71d4524aa5adc74b49306d200cea4952d60af73b817151bb5119effb0c3bf9429b77c11b19087e89165a951b743e7a9688a7c5a04d813d414c90c78dbdc0425c0bfe9f1293052ae88df1b9a60bab5f7b883ba0c312e234eb026fc0c9869898d982a3ffb2a98da8be4b5308e94e1cf72938a5a7ca0034cf14833731a4959cd18aabb95177cf4c8a092609044b4dd5ccbd49760b6aac03edb8379d53283e0e47e5719b7596db74f6d946d72a6b2b523ad5162aab117132d50ba338c69be3ef4a1f62f50702c18eb62971b8fe5a746beec7b0582c8c127ae69b07f7e6a1f8f4621fb8c1b43b0d157d47f948c1a7b1250c776649ccc9b3310f26110ace85c76a04db80604b6dd06e40000bb691e6a3890966dc50301df01ef9b2c3d2fc4f419f7d72d711bfb9d68963a972859b7972e1a0db3e392b576ca6e23147a232284ecd9494a75bd0e3cdccf63bfc7bec32404e22524557ac751b2dc6aa33cc3929397ec75a8d290c2e0a82527eac5b62c43e3b384ce3eb5dfebeaf86b55fd457161df2004d4c8419a4b3d24becc1f17aa2e288db63b55a96b3416d4a3b2bfc9a6c6bb8c7c1f1cc0a94273161888d07a91227387c33d240b17fb27c8a8071ade1f11b4073e6e869efe27347e52aef54cd3d482767a30b846f6863c0ce6d6c026d835300db594635392134032552511f85b82150d57ab3ceb49c803916248c1ac1cff59c1cc29504c1017d1d7b6789eb8858c545c2f43f8493a4bfd3d9ee9894a5d6cf8e5400f8f97a69414e680891cb32823e8113adb154ad77f02ec3174456121aeb04d15046018c376791155f2e45f02f1c3aedcdb5dc9d3582a3487ccd9a4cc71ed0e22b41079b1b70cc380e5f47cd0d2e627368287346d0efc433084183be92fef49e0cfea93d7ad341f119dd2feec3d19156250b7a31133e812511f34dd00019c5205934c22527f2ada2e0f99d875e3a90a0ca489fa42ccbd732aa3c99939183dc541223fd1839c6601ef004f51495ce73e86b17cd991086d7969f2b2768c1bd5d72c79476d491067443438ec60548737cf538081a99ca9338353b5600db9d4a3ec8b9d0c568965908e8d44f3467aaf654f592c9b10ed32b65e690172fe34828afe285e994f115291396530f30c82e8c283dd8a8f1b60427a928fa3a756c19a676a519c079f0a573d58551edc3dcd2fe69c78cb735dab07decb3b2781e6c41feb573e1f823ee2c13dae7e46444e892cc49ab1808c3867bbd6ca0746be316138a0b7ce661a87d183b083b75b922006caa161c5c08dabc0da06a64a4edc96c4ebd47545f378e40831468b4dc659a272ea6ad15b7e891c090933ef3371d9166b7dd71b0c4281671ae24bce535ddfdb13e43d64d500b1c1ca476a362e189d34ac6ba856c5e6832fd003e6783fed439026c0d2eed3e0df85fed9bc6a4b7f1647f30047706e58dfeadae31d0ba81d5db7c2354bdb3dd4f4dc8c6448a9c3a054a93268f4e1010f08a3ae782dc87c35491ce8f784e11da89c83a80c5d90e3aa1a20cd9e38d46ecdefebd25838864d442d26409117168fc07a14d71813ca0313502a8f3f9935d3c0017e56a77edbd19147296020709ba08fc083f94c955b36539bb7bacb187167b80792442496a923d747b00fdbfa5bb9d3b7bd5e38b1e4ef45092071a792891072e8fa20f96ba8cfb69f2ed636eede3227367a7585b8bc7173c4cc0c3c42368cb8ead345bc02da557cf3d585e7ecf2b9deaec73d19e8414940321241588fa5026fa438788866a0734a6776672d5cc0cec3d5f2edb3b9d65747cc99cd0b1778e4d3303cdf2f27099c3c0325f1f9615e9cc7f1cf378e6ce5c46c7d7352f121daf79915c0900faff7a479cff577247d6d21d64eeb863c97fbd77d0ff9ae560fdbf1c2fff55ced4ffefe424fd57b2cb11b297ecd0c20eabff5bb5a3ea2b931dbf7f3b68a6ba3a12f0f55647ae0e10eac8311bc5af0fd37155e938a2038239103047d57fed73fcc431234e135f75712c90030f39ccc831460edbff5f5d1c69c471260eb2da0b871a38bafce01082838237aa78c3ea1fef468c7778c7bbba07b41456f91e73d58d2c37a6dcd0a00d30da30a18d0dd810830d16d8386283c87f25733f9dc6bc5c6692e293dcdae7eb3db4e7f25549dccfdcedd945eb14cfc0fd3b6919cc6d1228cf3d074d1d058e01e05c8143548d7047b27e6252ef9c797127896bc2916b94fff5b5c61035b27e4113d79d1a55bfa3064e0d9dbe4b6a9e9bea91a601e783b9591a61ea2b0daa3480bebefa0536bdd9fa7f73555fdb74d1a6375c7e466f92fe0d7d34e2fc1f41e38aa28146d2ffde261a05fcffe3805863cc773820d667d470c6ec8c0ecc78f335783985555377a798986165860a5f6fb51a53c5ba9c1913b8d17243839b10dc7c196dbe5a736f6e69cfd645310f962f6b5df456a332b294b15406080878a30e297d66afd2982f9b1b78e0c185b26420800c27c858818c236db4da00d1864a1b2063dc31069c31c08c011c2303366cd818c18604362f31ecf8af3e6c8444b7aed1a8e005c2f4595eb39f48b1d8cd325dbd63b1255deb144669b02471faea7031a64aa7348dc26ad944352eaf2e9b99eb74e5dbda915b2b064e0c08b4cad0eaa245b4c6cd9aab355cd64cad317280ac034c1da0270c337e08a5d5ec3c1b199dd0173eb9e9a22f17ce5d1d1d69595e1cacd67307c7cb474b751f3b0e97b94ba3741b0ac63b5dcf3e8cf92e5896b7e3feffc9ef8401465613ba4d30ac4a30a66660b8bed8fae2ea79367b5fd88efb1117ed5f50f3dc6ad0d06825d550a971a901e285d6bf17545e3cfdd71d2f78febf6661bdce9b84acaaac5e9652175b5f499ca37c971878bbb8aab60baa97eb76d105fdca05b9378f7281f55c503d142e92ec6c01e7ff2bd98d6c81f5ff556a8baaef37975b24556bb7e0f94f03e7d360a569e1abb532998c56b4ab15e9b053afdf1e59d68265794f4ee6232da6ba163c5968fda6ac3d77308b25b2b05f75a6dbe4bc2c807c182361610725710e8b3075ebe0a51a0baaaf583cfdff6f2c8ca071534ddca55434366041834343bfd64ef2bb737ad3717685d68cee4a7c8597285d7e7245ef8a0bac00e3bf623ca356dc6085b642c87f1d476b2d76aa028c2aa8fe2bdff49298ac42c97fede4ace451aaf8af5055a854a17a4685182a6cfaacba29d89822ccff578ec9dcf53ed2f593da296c53e85ce04c0a3752c4f0bf779a16852e8a09b0f2c052022b0b16396ba2e3d010144d401100282e140a3cd1e68584fa130738a184134027ac13449a98a309ad87a242c76ac926e8993767ae3843c3192c678aec1860ebffe9489b68ac7724744442ea2b65800898d062020526624cb8aec6b8c2ba0ae2aab6441f4b44b1b384124be09608a2841925a0fe5f08f338fea12f9779713566855e38a11ae47a9a29591ab34a3d738f98a7338f635fb7424196ee68fa1c7137a374bbecf962a9ab53827b26a9f1d8318f6325252a78592c762b9ada4637895c1240ae7e717ba769b1d8cd8c16334264202103124596a4658931797d348b7ba6be74ece9cc1b553b62ea88a32388bcf93b79df298059000facdeec58dd600582d50e01b42040553d3a72d52ceed72816bbbf608ef69e7b3717ad7c739d5dd4c6a8b975b9b9cfb74d23dc30228d115346d022ba28224c114f8a08abc5fd5aa1201f1b2f771b840b72535fe0b983e70e565cb38bee1eecd22eaf06f5264fdc7f9ad4b8a9e30ec4ba4c56193265a6ca2c512a83001158b59b3b445461222c113a4368e9f41056bf330497ff1f62082021dcfc5fed08c1e5ab105688255508325aff64ccfc3f991ac824fd7fbdc11c19d74e105a40bc1404d54e1049fe7f4c9cffdfe7183463c8e43154ff558f791ae31a03e4ff81c8faaf647680a07a2034c6393176ece0d4f5c962b11b8ec4411c259391b458ec46d27cd8a4c562371f36f300fe872d7e70e107a31f787e681047b94cdce4030b3e3cf940c364852153751800f4c0460f34f470f64079d88287137818daa18d1da8f87ffbca26ad1a197d3a20ee75cc77dd8773b02cc9cb31df1466764cf28e9976180213074c1260b28041327193ee0bdff982bf4ca043153ae8a0c3eddf87cd0cc47a27073239ec7278e500e49f6ed3d2a0e9c5031cf6c081091c64c041041cc627cf5cf0cce932effddae80d40dc3075c3941b10b0818d0d3fecd8e042b52f17f6ed8eebb9027db8675a8bb3e1470d65d400a686df7fb5380a47f14d69808306313450d150c00c59cce065062019f2e842063132ec6428ba3f5d2673ce52580d9e395cde7e06cf1cdd669510552d544955415545a90a12039a188c88414a0c406f7d96c2acb61416f3d11faa712e667bc71a0643171892bce0e685245ed02f8c6fed6e53174c178ab4a0a6053228b430a585a11682b0f086ef1d16645de2e8f244972532f89d2e5e9ecb175cb47009dae14264cb165bacfebb111f36f316ba821d2b58b1020b2b7cb002102d2c6861d2e2da518105159a54e84923052f290420052b296880821d2850f15f2d7f5d9792de0d0507042080140420446507950f54552a8969d5874d9c3b61eb8434279c7042d25f18660a809a9d009c09802e000fec98b08709624cb0b5f2d2ad29edf9eef26ed39789bb8b9ab8290b145978960c4a18a3041e4a482aa1032c6760c1fa7ccdab71b9db5876825004b30461c121536d4c113175c314d554d15712e782d707abbaeec3666ca6c4fab0992dc53025b0d8ee08a8052c03840262f0ff3158b5b6779c0f9b198731dfc9e898e49499e7c691f9fe2ec73829a0ffff73ebf4f2ffffeafd8c72fe2b812931b199f55f7914f36be4668e316d5f03663007c41a87cd608ea4e1cc6f64c6e1cccd7e4da86242583335b3c091254c35739991b29cd546963b4b922c40be92b8842d96c212aa4a48fa2f81ab81c5eaff93b0f0d059504d904bb0289cdafaaf98df5e96d77769309c9af2f23f65a78404734031802c84c0df3fd055cbf2fa3adddba4d966525a7c2b498579a9258ffb792475c05712632cb949e7738b93eabf9e40d59cc2d96cb3dba1994d093a9cb093397b972a77c7fc5696e7ae93df25e07038d6bb5bfef45f494c7b374eaff4ccac6d57b45c09fab9c9a56e9b94c4e5ee7aebb6e6b34a86bf2a0ed35bf378b7dc456d29fd4bf235092c5bca3b73bf7047b17f9df274e69b735c37d04c6bd59da69ae9f17d6c7c29f8824a389803758eaa1dd998e8cd5294ef5e3bc7529787250fe5d37f2d79f6521496e5dd7217d54394ad5abacd282015eb4d827944c209ffff34db8c9ae5ed32fc0bde623159bf49b25b51afdf244ceea3dc940be839d19ba147ebf1f0e4e0e9c0a3c20bdac5f9d1e2acb975596e3df21ca9716ef4a9758647a51d98ffdf25c16ce01db00e47109259ffbdeb8bb38f0fb526dff5ab336de4944e8ace486e8d9c0d39107249ee19d789f05eb943f787edc262b11ce0ce0871070871617022e08258d9c38a15cc0677f98399fcfa6ea71877fe7cb7d77b758cf57e6985b27ae3283466cdf276e0e554679f4ad22324e6714b7d6abf3c9ea53e40ec4387d47dea3649833a1fa94f235aa1ec0f85b2b7d7793a5b91b242046a0d9497af3f6cfab02e77a8a1ff7f4b75de65cc7f18a94a5905032a5c5099a24205c99433a6bc306584294323c019a1cc085446d879d2aa50169fdcf45d12fbf0ad665c4d9de92eeb4b2994d5653316b354f0e9828ac52c151d27b7a9bb519e76788222a58fffeaa27c5639d6c0ddb30fc9d7b30c93fb88c453b7c44c25e65b174ab1752914f0947a86e224c67f9d39dd9c8e38f544c9238a9830cacff24b61bb5cdb6d4ad238b6349334da8d707c7323bc8d71eb12de8cc29baca98da61a6aa7dceabddb556fbd7937172c390e9663c799efdba33d9316c7bb39730a6b29cc7fb1d88dbcb857e66bbe9ae8e8e42269a43e374fe291678ee3b2be92215398df6da6a9af4c400fdc5a0ad37f15818cc63f8ea5b69226119644789a8900544500f2a1cd46c35732e36c08d4d8d4ac086b586a4098ded3b684c412d5925e02fa587f0529bd51a2416904a520509480c2a5c248de84e39d6db216eb274b3cd13d59e0ff13f8f0c9014ece70428493149c3439095ea0595eba7b79c78a6b1e6dcf2151589fd919ee86b41e6859425aa6edfc7f0c86ebbb4cba4a7a4ad2811202560821b83e98e3bbc95613344daa824d9af03071f353d7c7c40bf63149fa0f320182e4e6bf6a1cb5790ec9cb77a4a719920e3dd23afaf2ff7db7cdbecb232323378cc2184d31d2998151cf1da43498d39bc463e79bc4313258f6d059389332a3397797785912b524b6040310b4fe6b4f4fcfad5663022129048101d91ab21e42991559051f50f1726d4ac30f46f80082a233455bc2a2a8a29ea2204a7aceb775bbb414d6d343af92590c8f308615cb12c66e1506f3000b0f82a107503c3820c913611215ea8be2dcc5598a73b90ee074504407be0e8088d80889b2422217be9eba7bea3ae6d61acd3808830314420ea6381832b4c5d0008644181a82440d922d2192a0ffeffdfe28ce71939e3b6871bb8d1b427da8b5f487da20ae772138a110d6bf9097ff1a0a3d09b978fc066585415e8292aed9cd73cf80f8d81a076f2800c180747e041e39e2083f82a4ee51771b977c9bd7d21ded0ceb0b35090cced7cef386e9de83fac25008614d962ecd926eb4898912ed469930d594e8528d22318102657644991835d1924899109293a7334fe711ebffe48d5575b43fd2ff89f3b5bf30df3e170deafb1366b77f7a0fc4bbf087672f853e702ae953f53ff3b17df5e1f97f655d20c7e42c7c51e5a8694467aa8486afa7af7c53a1dbebff9f977bcd850574bd50cd9e4b59d1d4f515d152c4a988920dead8a08c7003321b3411e183c812447e4490febfe3123635654a595efa439bec58e6a89a0db223ee6714dde39dfb0e29c09030e110010c5132a4811e37c21e227aaa843d409ef6b3b4514a8fa6d01f3ac555ab18766948efa057140c2d814285148130833032a0fa7f4a474b61c172f3607ad311030f3058e002362e18407881ed8208849021c4082150421c1823b4404d688196d002a6d0820bc20ab0082be8153c5500840224420ac6841448a180011e2e7882b3232a31ea437ff02e97b49b3956edaba9a976a4c9ba8a58ababb4c955b3e3d854962e9a7b367f2f17ed14c6b7edb69fe58f8ed6065118b5bb7cf99e1569dc03ea7c7de1046e3e9cc08bc98d7c38416f827e811f7c28419cff0f253042825e28c14cdc0ac5abffff99c8e57f168abf2ada44a09ba813c609432d8c9b8464be29b4ffe3a74eb7c390e73fa82fad4ae1ced6ffbf041fee68853b56e14ed5d71d9beb77be2902381170f155262b9ada463257eeecb9b343f0e667101ca0f326d429a383756ea18e90ce030fe8f1c098f0812af505d38d30cb64e469a781588fa10354a103b6d0010dc206daf87f007cd8401a0d50d1400b0d9cd04053d8001006900819a8aa2f1bc451b8586c577335bd6c94beba111285d50e8391e5a6d66673d55e40ac5db41b099a3d77769be33cdea541dd1788024cb840090b38850b1c102a7044a80009a102b250019d8741d9bdd3342ae4f599b6dac2203b0c32250c127ac0411092da010e81680981140199204c008e300123c204aa9ec952582e4c20294c80e7bfd69e3b7b8880508840901001044200688500480300600800a010003e0784718055b52f26fa439f5c354b71101dedab89c95a8bf1aed697cbc4bcfa72f5186c9be7a53897ef0fe3dd2ef3bd9bddd0003d20f8d080364203aa080d20131ad0141a208605c829404c580094f0c71e5f7b13fd9d662c76b33e6b4baac4c992fa863f7ae18f26e18f22e10f0ac28f23fc1dc2ef127e511f73fa78f34fbc3e7afaf83957735298639bb3644e057320e0e3007c70a99dfed0266b852c85551826b70e669f6024bfbd9e3b3bb54da40bc62d138c52db4403fcfae868fb2cce56128d8305d3671ca93849ffb5cab1f535cb81f5726cf9c7c921c611a7963b0e20e29012c70f38848083eaabe5b1d8367376b769af3c0783e5b9437a63cd1b27bce17a230337b2dc38c18d1f371668c34c1bbb366e6cf4c1061998f34c831948ea4b06e99847dfa8738977b4dab9c6baa7cb3b1aad5b6fbbcd5186b1e4a658ec46c7974c26bb15f1749eba4ae62e9bcc5d768ec98b7b38d90b4ed6c3a98263fbafc00ebcc1f935b4fed7a046746d8daaa5dfaf71fb7ff2aad1871a6dfe5f8dad46086af0fc63a9edfb5d60532c7623c96bd338401a574f92b358ec66e2591ab4341a78a3554d7dcd29bc4d9b0de228fabbc03755e11ba537ae8e0619684c8186feafb6fb2cedddc45b771c44c3e88c38ff729dd1cff0c08c2d1c745d9ece52179739ea3203cb0cfb66e8287193e5c6ea9fcc6e7a5fbbedbb7db91b9b797d3d2bb90152fb2ed19441a68c5e19495f06fd100173fc63c90878820010c868830c2c3254f851a23c9d6d647c1b356d826883dbdc360d8c71660c2fff97e6ce265a6f6cccb0d1c286890d0562a0214612627431686234a0e546cb072d520b8916903575ac19c01a14d620bd92d1aed63f60aa313149bad198c48c90826ccff66585e80732a1207d79eec5f7cb0ad122eaa2353c009230f608638e30be84f114c61130f800c30a306000e30a1844d27c31c517515f846a9a08d574351a7841871761bc7812666d3d45027bd9209b750951a35dcdba68474261f4655f41364b47fb52a231eba25d88c2846c76b7836c19ef603d93678d846cf48706d11f6ac44f3b443ff880be5cb1d86ee376e7b1d8cd5294d69ffa53492c952f90e29cee76bd33e6b9cb37a96f173c74a1d445045d24c005125cbc10858b0bb808c2c56fb1660b14b6b8a59993c68e304d99344f698ed21cd1420c2d7cd0c2a605d0ff636dad92ab666db6bc9472a099b9ab66e9685f4a945a1b106b6a97ac925da23fd4a6041b5279575f5fcfcc409d0e7613f3ca7387f9d541d96ea95033e39ec9bbdad2213558624ebbb6487a5792e29c35c546f4977b1d4b658143165ab258ca22812c7e6041c4ffaed669cca7fbf4a9ebb3d95c351af3a1584ce98cc592ffaf4634163c51d0c4f90fd160fda321533b1a9d06628dc656fb4e86e68a2bf20a2ef55783ed25aae48a5e7805fdaf405c2c760bada8590142156b54f1a58a5d154e55204085175464a18257ad893b8ee3f245a3ae198b59badb2e5a5f354bb3c1dc1dc912fb7254bfb7a87e4d199410122a781eeba6d8fa29acc229aa483c4592ff1bd5bb146ebe02b18eead794428aaaaf522491590a200fd35b875184717f19fbee5254bf375914bfbf611421d60b2b0b8bcc7f25b1a8b096604960a9d0a8ee0beaad6732ecbb4b396866992c16d365287e2114c127e0fcbf9ec07aa2ea770f183ef1abb42792c462b7279cb8aa79e704d557276c5f9d7055276e1356df9bf8fdd7739b4d2cf926787a3e03a7d228ddd2992b7e86ea4cd21903a0b906e08275ee66802524135b4c50e193d1e8c8040357695ca1f9aff6cabcfae08a678937d5363d29cbdc97a05aa20325e850828750092c4a24a94a5e58eff2528e8178079c254146125e92302109a1248a98c93223e6ab6dc2516b668a191090d8fa4ade1aad7d4b9133247a40a206246648547044968fe9887dc4ee88080ad04601c014e0cafed19ee9ad1b89d2f96cb2978bdff2cacaf21ef528dde5264e675911f6e59b6ca4a302231d473a8e244d26e36637bb912226dfbfdfd9713afb703afba4f879a4b38fa7b38fcb64b7225cfece5b910e3b9134dc35228ede4658fdcc88a96a499a35e2a9067194b9978c20229b6231d9ef37eb9792b422cc22ac14a153268d32539479a10c07968ea31d6db794c911264f9eb86ab632a92e6a7b103fb14f98d01ffac455b3380546d998e4ec5846c724671199711ce31fc744e8f4c221dcfcff0f613544d5104f43f07c0d85d8aa24e6098146882a2184709181f35fc9585532543adc69bf3d324f644232f4fa82082308aaaf63994d7de61ec4d3fff374ee4100d97a0c1add49eabdf518aaaf24dfbbdd1d637f0c2d331070f0145e02e2ea4320aa2a1053ff0fc4d3ff3b01e17a3170423157525ec4f4fe5f2726898ad991117120945496f7680a939af6dbdc3680cac60f667ea8e187da0faeaf967c59ebf281071f62f001c90707c2b8e1d4e9e5e472da2676da26760aa31406d6431ba49b73301896d23cb8e101061ea2f4b183103b60d9e1680720601000060d982b606c602cf8d2e6cbd5971cbe00bf98ff3e3e58aa0c5edfe5586ac3603152df1d4ddf5ab1b9a5b636cbb3c328df74dcd1be00d5ae83153a78d161b799e84b9b394891430e403958e0058d971fbc287929000702843854f5dbbb4cb72da56467bee948626b39c661c80d5aff7fc3ee06211be8b0c1cd5b5c2c26bb15c5629d7e407fbfd0869c0d3a358c514398b0862e350c551286f90f47031b210d6668508186dfff101a1a98018b19bacc50e5eb8839efd25e5ecdcb4d333020835628830ea10c31c850f4d5be8236d0d4550d5195a5ca15c318313011432e860d609803862560c0c1c0c0e5bb499020e5ed3d9ece96eece1cd69bc4658e5f1d0c162c83e50b48bc90e505272f087121cbdc3bbacf2b5bc9c6c18bebb962734bd9ede482052dc4d1c20d2d3461610e16c2b050020b1bb000a44b01c22e2774f1e1d2e675366229ac1bb13dd3b4a7a5e696c2fd2e792468660da3946faa2487b90b898b932d7a6c59638b9a2d02d8e264853a56c0aabbcc7167b2244fdf6a07ee1586b4bcd1c2464b172d4fb4e8a870c70f2a2c852a8090421cf54561fade9ee9a2f6f573d52cced158497176ec7987b9e5e97ca9de3b5a95c2e6b9353fcd8e83b707afc6e6760a96e535352eaf997f578724ebd8dc4eae221d7632b7d491ec6a1cbcb25b51efb25bd10e67f6dd1b22ca587267e7616ce24e5c96389eeb3e5cea868832df53434439c84d8d8590e4b31c22cafdf4613e44943970731edf4344d9d4badffe0d11e5ce7326ee57f01051c65a9bbc21a2dc4fb31c22ca389eeb9c24ef10513e8788f2ef72a010923c44944ddc71a5b9f91051c662ea8688f22e0fe1424872278788868832ae3c7d4344b93ca5323944944fdeeb4344b963de105136310f87f5ee976796b7e338fe01b7c6e1a86be27c9bd49793e5c63cde71e5d56133981bc27197eb2c15cc54a16c2c86cdcb75960ae5beb393fa52286ba990d804ce64b7a2a87e6f51fd9abf6dca8af4ce26c6923bc692bb4c96a4f3d89535aee47025caeff71be10764a3b165d84ab41b567a048d812fce9b66e6408ea536b6b493c1d2de0b84b2b15854bf375f50dfdc2fb0d3f166caf73571184be636aadfdbedb8c7cb308c77b01d895d514633e35c39a375ebdac4c489765b6272b444a32330cd6e4c966a4ab4b6d4ed91af26fa8a945c94c1b2272aceff88cb5fd6e9cc7fbb97f7776a5add3d58eadd336d6c46ab99e92f47f5cb6fcfccc09c33a770bf3a9d9b4499f9a8a7a82424504102d557acf78c840c721699cb07f57d41599b92a5e2a21c2be5a7cfaeef6d7deda1f9ef55fd7ab6b35f1dce057baedef3f8cdfa7f1e99caa3faffcab3ff95b7e4bf8a3c9dff17776f7ed7b41b773d580d9e0143f90a041494548d90783ae736ff515a7905527eb1ce5076bb475f2e2b927890498864183289fcff6a84448325cedcecd9c5dba58f1ebba575ea2ad99ec72051e7842e8c4ed44d904b53cddb67d602b1ee3adf7acdbb73622ace776adfa9c5dbe526b93f360c6b8275e1bec059e19e7042ac6461e58c951e30bfa8505a6969de71d439868456116a0050b52a7d8855e254d1a14a09558a50f182ca1154aa7cddbcebebb4badb8db8974ce8c94d6cc79e477d72ccad796e71ca11e21415c42948532818018dbeb8d4970c8eb080f894c49395a71f296048a9bf0e8542599cf93be98e4999f91e8fe0deed37ab525e4e7a38b989e2459420a230450921ca04e2ad0af12680db78b340bc01694aa2c98ad85484290b97386c6e1c2e868496a54f4aed3cfbf8dc6a4b3e3e4386d40d3429edb9ee97ab48879d38362fee4c404498238255b9f9a637dfdd4c04aaff1ad4358940e4ff83b960cebcb8d3992d8eed8bcd494d4e4dccd79e9981dbc49ad43bb779335aef14a6789b1788758d4b2d49b5966fba1467a9ccd20baa0a952a2e1fc7becd77afbdb4c4f3d55a7a2989315752bafaffaaf4fbda332731b72d51d2f9af50b2a090f99f9308a50765498502646b2d7d323d21f364ea959e24119fd0cab1b99da2f4e54eacb6e8848b139f131e7b2f2ea948eb342f2fd27e220db869409292acfe6b52af2cf72f842842582184104200a1091c4da2109bb8f05f5ffa02b18bdad7d4f5b96a96d298bd348a4da6262f2672884cb4b699b7be3491c91626529814bda2f4e5389d7d4858482820e99018f8af51faf2bc39c612a5af7991c423258eaa8eecefb652946874c68804233a93f3ff412fcea818ed2b88627ee998631607afa5b05b734a1d072f0ef39b9bd987aa32ee1e904a153ae35982f55f73bf4becff9913972cd9b6253a48342a82c0e6ff65b2de6f7924db42d68228db1fc08ade145151e4138b9a8afed25e9677ecf82a794154b24509d0ffd77d448c85896931e6daa2075b3c30c1830d92b4e1bb031fc40e70073a446210392112e54054428ea8894804fbdfbfda2d7596c26a59ee18a9effed5b2dc3f3af67bc4d3481c407971688c212e434890e481240b24412444840e206445ebf5e532854ea10cc4a03541565f839a7a262dcf6210d5ff2cc8f6ff391168ebdf5e8ead080444f55fe91581aa1c89e3089afffad219783919e4d7d467b9a5ee91a96d1e713a52b134ce2d8995cb57a0ad1689b03e6056db0ceaeb824dfdd77e05935784b96ab97754cf9c8ae31a2394ceb3d11c5db573acdbe2cfd6bf5c74f663f5ffe20f95167f6cfd3cf2e3f249f37a5d26a20f113e4fff7afb344f8b3e406ff3f9e7fbf562f28a4074a1a93c4d45579877f5a0acc65044174f2d6f9f1981639a7bf75f0d887591208a9c453610379043dca0890db46c30f424d67993a0f1af168b592a3e35d31f4a49736bead371cf14897041248b480436248c2154ffb5a7a7e3a82e45af7939e517d75766aab9781687040d59a027eb2b25b3a53ebb6dc92cf61cd1404b832fa20651a20656340842dff8ffba4dfac2e11c6e9bb8a89c897d9857a1b1dcfb578362d8bca645caa04d062f881910612007063a88184c61b0c3a047c4c0820bacf8ff0f5ebca0cb05572ea0a2103942d87411b2a3512c79dc3bf6c2bd3ca4f2285d8ea2243633def1bdbbd8d7bb49a3b51bb14214b0c08c68410dffb5dc9b52d102a50af0a8c0870a441029d8a35a8b710772dccf600efb76a74714740a9c8814bc441e3e78c21079b2441eaa0a65b1addcfbd7cbbd6d531737757d306aeecd34c19c09d64ca0459c60e8ffcbd24742594b652483b8b7f53612253842022ba20440a2688688a5b099cf2b9d16659769751cc7308e182a218655621882b8a3c70e13e24e0ce2ce0a3b48c41d0d2248f32486e1da35597d8ee890fa1a39d6510e411f22044188105481800251e78da86346d4b1a2ce4dd4f9213e50467cc0870754b0b81fae64d4b40a65a94f858ac52c15da7daaef42a13ebbd3dd7de810f10110c4079644076a70a0141db0406ca08fff11caf672ef1f2df7fe755c2779bfa54c762bba9c49cfcc527ab965b7a2cb316e6b8e4b1e6f9b38736f2e2b2a37c7e6353bc7220329880cc082b911e31dcd7626932d90c5f3e20256e20253ffe20274b6801515b8caa2025bea2d881931c896204d82182002c1128178f9aab798801e62024d245042024e0944202220c78c8840504420c97f0fd5992cd28f68de4bf580350e10e200de01444403f830e08c015844034ad100971a05882940271640452c80e7ff672ffe58f3238cf843ff18c57f437c23c4078acf81f83a5f3766d273fd9dc192674aea2b85f671c11c3ce6383107cb9c2a73329813f2a1c587152af061850f217b68fdbf6e6c528bd03eda6c2dbfb406617b04d1830c3d5808f540410f233d84e441471e0408f3a8210fa63c20c0830d1e5ff050c19cc23d3ddba419caeab214ce45a96327f5b9e34568b0dc5ab395668bcb5693ad5702da4840990470a9346629df774c71c70d77ac7007d21d3f42397bc8b9928345ce939c0be4e8d8f1851d63fe6bd05218ed99f5d06de619ad3db3a7278a6f60b6ed7293a0295360a4632c265ba2635291ec725c47b00ed7ffd77be940d32fc7744c753a9efee9006e4dc93a879939707324fd575a350eb135a8349545d458aa19000000500053100000181c24140dc8a462c1644cde3e14000051b47468dd9836d1649d54c81863600600004000400002775171a3e24156cadb10d96309df5fbd43f80c71507817fd707ca2c8e3c1d2c485cc87081f139026bb817ed313d887db583c563a9ebaeb5bb7465f92774d263f8d759f1c612a3d75c6e395969eef524e684d403039fded06032b9b8b89b1ad7039aab78d2d2f47692ba1e789b1137e6601a9be07bc8d895542b0971e36218b2f75be69489b84c1fe475eb70f9ee2f68d428f3f249d366b89df4583a6d4774b7559d03c9bdb48ed737f74ed6c625826a0006c7c21caf0724b86ad3fb229fceec5e1ccf3fd0df78cdd3e6eee6eedee483f8216b7e4a2dd6eeaaed2f7bd3bebe526a482e5357980d1cfe321c504e645716361f4761349d1818a174b657cc657a067877a3280ac2c82cf37b81a2e08cc74e7f20c4ebb55bda8aa05e39fb1f2864b60b424dfe96a000b7f94d84597095048a0101dd28cf37f4742b1717e4d0aa7bb14e47820446962155d27e3b9561af6014400add1ce74c2806852823a07782bd282522c52989188714c5fb60f17be64700c3be4e71ad3e92bd352738e569f5248fde93fbd02fbb45553ea760903b68a5da9b11740df2c9d7b33225a60b18279bc82351f7f7f974a1683920d4c981b42fe32395ec054b2949f6a39a3ace33cd21b2e356352a0a25343403a9037cc39166fedfce56c18b2b50eb9f6b8dbda5265a0e0bf4ce92e6aa0aa908736a8a57602b8a15f037d8171cc42a87db3094f7166539737a90b43a84344dc96d1377cfbc4f181761ebf30d4837fed1d28f2f0b81a23b59bce23fb8fa34a3fc048d61b171cf93ca840a4c69395ac7b4fad4e17b519e9ef94c1f3936f56fdff8441122b00ac6e16696e10909e7aa894156852d1bb91404fde7f1cd93c394617050641433236c309b9e31659f93dbaf9c143e3626036d248dc84a010620012796412a8d6661c6ca4a8a81a74b82e95e32a80f3d7694369ece556fe2148be8bcb1f3e2d4ee2f78501e83e229e331eae567489bfc48ead1b6133be1b837cc62c0e0de3a38a284ae8509a241e0ea0ca3d65b2d287c6e38d0ff6c5cfcba3375cd7cfc2f82f6f26596c900524ef851710d66d278b03d4903f76b00ddaa26ef7fcb507a889d598e915baa8ed2bd0fc63e68aa2cc46d96b8fb92625caa608a42be71154f9808222a06d62f4941b4907235e5e0cb6fc428297fa84e3914fcb8aa21f35eb1f4e3efa48d33287e4b1c525426070d5ecbf09e71ddd2051dd77b7b0d7dedda1b2cbcbb55ab504970ea21c04635b7f6d536c203f3d8a4cbead514548e382222dfd9723479271b35cf7887374495ba6a099ee386472ff770698215588c601b4dba6a8c981967ffbda66cc348bb3639bb1bc38bc1bdd7048808057c1bf480403ad0644329cec5701f92c861dc4ceae7755e8b3596465cfcc913fdfda9e5c348a921d980833cb0aabf0d0af2d233ac23c7f743e86033314c91709ba6756b364ed92bdc9d52003e54953eb85354500b251666f971fad0521fd20812e405fccd99035add92a61782ae43bb93141232aad6cfd8ad71087158ccae296b54447278c839e8536f0e86a55e2ad13e7c86df20524abe12cdccd9d45b144ef69afb4d6b1e0d50b99ddbd7187b808395f77aeb60e6f042c8fd21e501147273969bc6b4abc81744d90005d96c48a4b57bf18b91fe7c1d7a5258fa78b2879d15b9c6491b336d8b5b549573ee5c65930f4049f751bb77edb9bbf836980e34317c2314a1abcec9b11beb4209ee15943d8bff61b0b6ade42d640bd7ebe44ad29394855fc3fe6b19ac91b4d55002d62887bfe83fc7f431fbc3b5354f67e420a455fae7b4f572cd23e2d8a22cdce13765a36c309f4356977becf04e0cd9ebe9644c939131b62502b0676e4b20201db11e5fa8758c84a2f739c6ac1da4462c11ea5d729b9e3a3109166e6da97a247816fd0dfbc9a49a4f8996cb12f007c272c1dfe870ed836a4bf793e00ec2728fc7f1ed814d35f9384dbb6fcad0cb8eb2adfcc713b22b5b6f167a81ec187402bdb1ba080fe264942221a2b9067714f6e71dc0067b42e36b36ca7fb45cccb6739f75c0773b45bfcc395d0435eb8f8a125d7f1cdd1897982fb7c2b5a18919b3ee6e490276fcfee9631ed40c5ce0d822c663d996d823da258e0405d234af66b570e482f394e9590dc7a45afaff02e5bb4531429ab89aff67d69a376a83b935a9ca5b8157b7549994e1a09b8ab34bc5f63f2414f08829a599f4578deee8e044d68354970f442b13b9614a4ae3b7781411e13ad77fd496a91b72490e754019a7c78aa39782a073d3cb5efefa9d2f2d5905dc7873499e343c3e61aed628795593bbb4facec798bc7a4496508809e61e024b2b314f4fb35136312fad3a05e615c1b9eb706361f10d2bd919d7f1ba457288068e3db68386d44569b480dea4d8cb86702440cef39b136f09491398c313d2d8696d268582cadc5ad2df883eaec5d9a6c72eecac5fa2358745988923db4f7d585aec6ecafd99015b41fd0c2e393d81f081665b89158d5f39410969ae340685babfbfc2049d2fae3a3e76fcad774217616dc9d659dd46ea19b43661991db2d885eeca836e411f7ff34c034102160019910e831b48f54bc834c59e19962e952f6fc48077fcedcd2695dd280a687bfd30200ee1aa37c8448a0c56592691e48d459834139723e0c74c8bcb420a655a3e8d01f27b54ca76e627b8df283d809da70153c5483cb63e9801cad5057d7d1e846b512f6efcf1fd9b8a00210dd8cfb2af26287610d0e7096d6229e759c87545c77429723e4b9b1cdba6d30f74b67e46d8d73ecb0640e483c4007b1cca60ce7618a8bcfd7ca907090b16dd427fcfc114f471bfa1d20c8faa9c1d37bc9d01bc9e746a58ea6304698e5c5cc9faaefdb5e0af3de64831f63bf00451cc1bc4b68fcabfe1c62e029ccf3f9d82edafed7b461a2adfd28594cdc9a18e47af01d18fc6c6154cbf9acecec1d4d94be7769cc7f00cb771f49cf6463ce00169763628784172b58858c64f2ae55561ac55372d9c8ed559df71f0d0449495c81f12b77fb3391c31cc647d091c59c437387d3e0c54b4a1d3cc93572d6f4b942e41c83f67f8ef69f30fbf3216acf155facbed553df092374dd21211adf1331561c033774120c487b4f8880993e186ceb209eb0f2ac68b47612e1b83851d7c0ce61f90e669b2e3e364659c236871ff0ff5ac45d51904d0bb8515b40d6cd2c2f9acd54917957ac0dfbe845c9e9c915a753c5c33ce3b6ca9297faf3d6c8f896c91c1a58492b36934b3ad9d0e29898e4353a958e67a98c8c5a5d29c5db1f9f28ad797380010600a83d13176b3800a36f1776b94916e4d9b2b86448587a40c896b1e266c4648e2ba99176ba86e42b251df4d19ac48921d7283c834d02be558bf76b9c06ea22fcfaf6a8f7a3540c46f73a7c2d19dbde919c49c1f6f5ffb217fa421fe838393009cda0d358f2bc242293f1c0268eb2cc6a534196bdcee5c5358662e5ab1db400c9441768d050bd24ecbaa0f426f437e03b94ace7124ac2d667106dc9df2e05923249cd50e949efa52ce557ad99edef7ddc48831be8926dbb9d63299e09a350d4acdcf7034745fb19310180b52485c68306ef97d896f010ddc39012ed162bc5c07e5b47d2fa307974227a5cd3ea77b1351e0913563034fea7429e3f0aafe13a349e4ea1412db71df8200fd5fb4db617c34b842bc1b500a60c53153893bc8c74039ba79115da275b0d949c9f4c90521b9337972eaccc7b2c007aa5416e163665b51d3b22c405e61bbeb2465196277331caef2092e6f155e8b4ca1a8fcab8415f3d73096f305cc4502bbab3503cd0e90acebbffee6f1b5d5cb12c8c37cabe37e2ed497a4c9ca3475bb2a4c285638979b04adc41ea3405475cbfd9afb140bd7f899c855547b425dee813824694dde1038c6f22ab98cf4ae5923ec5709428eb27eaa863dbe40fc4ff665edfad82effbb1e811a7fd2141a9c46849f63063689aae5ade2d51bcdda66e335856cce2e66b1385d372393463290cc4e9a9490bf7843ab38953d2ff9437eb9d27690c0cc955b9ba451ca57a628ccf47709ae496133c905cd751494ee9a4e68ac14618c51c11cb1e6e0b83a90547542073e137bffb261c59008a99f9ed693070adebacb8409c5c4bdbd3d27303f4477e9e2f4a1cd4671b03836165a42d5a18b7855600ce7183eababb78327e1b097d06df400a421d9cb6afc5d5c9766e943f4f2c867d4761d840f4b4e09d6cbd0ee7ff11a93d81a7b4103dbcb8639c4e2bbc198667a3af4413f0fecf1b029d757b60d9da83476a2ae7d4a688b5c1b249e3b0ac94f3b97d71b690398ee64a46bda2edcb2981d1dcd227cf429693721c9efa560e9fea303ae39671d1df9132ad776aabecfb7866319c38cc495ae9568c30100ec09e02cc67353e868ae452f3f8683fb73a630ab79ae08dabb7ff4f65b683e4423ba5fa1abd67126c0346e2c80c77f0f634ac16539f8a994cce7ea77e7f3d21cf518b871277fe1923105b6bb9191e50595aa81c0604eb3c57bc109927363ee99f2c4db9ddb7af2e2bb166aeacd4857962b18ec4e1af6c5398b7e2c97edcd1853dd1798e86900a7406967194318f950c7115b2277443e2daa6d1c028af35fb91d7268857122687b0fe6a8b21c962fb1e371d6406b390313748dffac8394ef2be8e7a8a5409de1790880aba4d90b33f9974800c05cda41e21d43ac572d5d9dc1d3d301064d20acf204e6d44d8a6217b63a8c7b095cfb5088f12e2821d6d61719d13a82374022551b8f6bcc56c1b502b84eb9fa0deb0814eedc76308e83c3d2e493c2cbe96d7ebde5269f457f3511e7efe6aba1db8e6b1a106ef1f64c5845d9d7cf099e5872bfae2c9fb752a057057997ca3a0a203e12308d67fbfd4ae34dd9bd03c9fbc1184e3e866f7101569c4357808242985f5d793e9ad5cf91b69be5817c2f2d4c8f01757861d62f4bbef7bdd043f8f8fe9ee1fa8477ac2d2da62ee0cad16e9aa10ea8a989c909c7c5e48dfd7d5e500721d07a527f4f860ea3c92b3a65223646357bde4d1d111ac009c065ff50e68d6974e32227193e0c0dedad1e35853ef3761b1e029babd9cca9af399e0061643c11a65c42af84f2f8e3f38aa69d045a7f38cb18302b5817eabe5248a8b8a0d5d8a3950abe3ff237c4de42d16d2c614fafe6f3007bdca9ec0d2e57a4e82870a06bb56bbf3c1329535beb2c1773e0f347607daf93c2771108fe77073d8abf04379c17bdd89f3fa896df8890853ead950a3b40b1d200d72ae57b999796a75e3aea310c8a78e7533ce3b9467b9b0ccdef0c506464d5d1c46b7ea7a4d48d721375d5eb1b055dcd6c676562305f499efe83791985f600f54dc91ea511b0a93baa7e73b856ed206e6321a3df0386b1367e058292818868e4bea19f844bd6f6eaf38896e568190de16a487086bf3ac279e482b0913d5a2b4c7d0641bc48ce918d1ba6bb1c7e9c0d58a0eabad985efa2cfe7b04bb23f5d800d0de0ffa4c6c5e5061c192d4673fb5dcbee650739aecc817c64792f6ed9a97705d75d475b0a908aade2948910662f85acba260d617191717bd2f8326d92fb1728e93198d130b6006c25fda320d32351f912f832869a9a6c31af5d04a038160b194a0480f692195dd948f07b848ff42b3c378898d22b7e33d4f8e8f29a083ba70a5ba7be9372ae7acf753e42f5ab88762d25e0810f847d7b84e5f73506562bb5c6d815c74b84de897e8989dfef4330a7c0d06ab5a52fa8fad16913d2df9cea779553a35c2e7f41c6177108fb31fb5bf953128530674d1d37c8ad88b7c60a051af84d8437a9491fc8ca0311e78646af93fd18521f3e609387b117d4c0207c35d0efcdea697cc0ef4c96bc7cd639c9fd21619209f11cf635a92e17fe39df11cd2a0f68d87eacd8967e5a0feda6e7a7b18ebeebbd82e5521f98aa9f50763b981c3551b22762ae161805a1223a55c5e919f2f36b4272c0bc229fe64781a91a46e0e96256754f077e090855b316b7c336060cdea31c33433a5b1c6ca7fb66a2ed8ab44a8c6e5196458403d36aa07d1c7f0d678c632fa7993b20b0880d58e611b801154ded240fe3e649f264bfb04bf377d89d5da43e14b07e113b3770fbe69a3ffcc863133f826537c62f555f0ce22ffc9d84326de3592ff116399453711b6978da7dfa4105a5c2e7518740990d91f297002b88718ec311988a157922a7407476925ff325bffea886fe94e54bf5a993d0d9ea1c02c81682ee1d388a08579d75de603b53f57e78d5d53bf0c5927ad7891c9921482b5bf6892c995e6810109197b42db6f3e2e72f8563c989a865370f050b332209c40aa0b273d0c923bc440dd7c1708ddb98e1f1a3174e25ca082a96607728e86c97b666696c4e776430f37f5a754ea6f5a73648f2462bebaa5167c23ba80065d244cdb18b10fef334d91d1969d8ca27721b25e90b1233e8b44b8e4894912bb698b6d680d5f77dd81c4c02065586147f1be5384c36824d550cce5194cbf1c42d3925fd0cd2e816ea209b06bccbfa55dac3e78ca5a4be0e3f33181e3c6fd9300d9031608c73927e06285bb8075bd946a51785dd4a9c6d5d7e7e32768070797707b9a70d45d5d2c74f1f708d0a69eb50c231a501c6702639cca7f5c09da976f8d914230ab5c5ff788fc1e4a39481a49d737408d115a534c344c763d94b459879174a05bc8240b439153b372f0d516f89da1a804df2b65a78611da9d1ff922afccab36b82f6eb30e6f850998fd335afb9d29e4ece7709b55c7f7bf8f5413a0cd236e3edd0187dbcdc9455eb35bf0bc69d785d5141e92c219ca33d09d64b4c14ed0d58d9f02233dd4ced9a379993c0c8541c35c79c1af4e6c60982dd8cb55899a787534a69c72c00822bd0df36734805a65419fa87c7103fad03264e11307d06e614c1a34aa0884ad300dd5e5308000caa68e812df133ca6f14a8abaa185ede3ebb59041f6c566af611c46d5fb7d2b24b1e0b92d9ac577007b8489e6f41927a38fbd5b8690328c73b8a4e65b65c3c01c205eabdc3207c167e07b08b88a2b7b26b01fd1725cff7774df0920f5b76275d8fa2e0d9272494260db19a2304c01aa9ecbf699a21e7c3f6532cbd6ce926fbd4ba2c3a58be347304dc97f99783cd174f42e53eb8a9e2fee9dd2a995ba554d7d4e8ea475725390888cbb95119d9012b2eeb8e64f749d9783c902ea548d2bb17160b0e9c34ed05e1af04ae9403d7cba9588b0f8eb7684796e4b04883fb5580f6142338d970303248f4af43fcefeaa420f86b4630fcda9d6bef478c15dfc0fb2e733f16cc0848d3660bffc3443543c2c15ddd13e7202260bf07e6070f035cf4de1bc80a33ac174f870f8c009c64a57c4d4e34a3a62f5af16289c8a69550c1bbc7ece2631cac9a2001bc1400549b4138a051ce3c0f654e360fd2c0dabcd50b0b7da3bdc421fd7434b0f43cacc242138f5486a040a41b465cdee75ee1b27733260234e164e529d10e187ab413793dbc35b3a1979ecaf13528bc6e65e358dcb53c725de6e3d370aa58e04f6993298b87adb167187715659b2e85f755dd0648a3f3f5f009cffd81647764e134f8f32cbafdd79b00764ec24b02dc02c582b23fad89c39ac54d531bece98929bbfbce8c08f4adfc8ebc0911c5988cb0df10d8feb99ae89fa8f77e0fad3a8228d2299db807a695632a047f8457efb1e535990aeac15be2d6573cdaf0f0cea54c7f0baf0d5280815c605470119add45f0ba2cfe6af5c18ac63c75377891f458381eb3a14cb4ba9ccc397c67584e41a7ec17ee2207d48dfff7060125a1006fb1a2096e20e94d75eaaa0771c567ce85aec684b405ce200cf429c702508b899c74eecdc95ee08005d0fe2feee3b15b07728535421f9ca401a27336aec80bfceb6872f72865b07b0f343909ee7457d4a96142dceeb161a727d23811c0b71b986bcd54d17977f1996eb6b293b896232303aa8200c3f277cc5a79a465e127647e49134bd2b47b7c6fd1e2ff863dcd29d77363deb15f098ba98d8ab34060ee7b3ee1e16edb09f83953dc9d3202763e673a0653aae4a5006e39edc963045ecdbab1495c2e44b36dcab62694f35b99f3552ad5d3af077dc0f040ad48206eaaaaf22020dce7f33153a31f1f1e2cd3d6a85973b15f320ea047616cf67d2007e6d0e8b924fb660fef00f2451bcedaf860e5389474abdd8f06e40c1a7ffdeb8dc6c189c1fb6c74ee7a0c726b4f86cbc39ff1cdb88584be247c17e4bee8a1baeb82fff42c1fccc3efc857047e721870338954cc167473ecec22a7552e3b16b7d7da5b82189fdff58b3598b892907ffb496f919a471590b04106454b22e490a7027e449ae3379193e4670891575e9dc661796a7063ff789883ffe7b9962bb92f4ed55520a14acdfe49ee9c563fcb1cbc006097f2a2da17edc7a04e07d17137f3e483d29f0ea420143c9c43ed173f52a292aa941c611f9f579e0282501e701c5fdee386bfa7e5b12ed140bd15be54913c94d358d95405402762f0c4eab6e2f2c21baba3bb722887ef0ad7eb5157ff07b865ee2525f27263101adcfd8e7c9369304c8c2bfb4de777bff0387e31361c5e28a65ebd86ab835e89b22c67afab7c76e7be5fa8cc787e40348607c3e0b3cc394e1453d098fb14c9e8973f5501d72077cb3aa1f82178b461a087430fc62f06163ff6411eabd8d7dd19f4c6ba6c274b1b1b016edad5c87f6fd16ed7267d70777ef19b19d8d40ad560d5e1739eab8cae4792a7b5d89812ad88e7c475a8459d69262be10ec1f91c3af1d67fe77a6fbf7f0d4ebe6ef0bd166de7dee03761812e2b1858fb944d7cd5a22edb8fa5ff83fa1be9c5ba8b8e549cec2505023d195758713388d22c7035dcd4354d7aa33767aebb2a2c587b7f42f70e337791afd9547af8fc8c4bbb3f80d2f36a96502ed3e965fdcb566fea2c8a2253690b9f5a3801996887af6e44c10340469a7e4149407de51ee2e9eab83fc854b5612c13d0140c2e4d01afa309a353d007e0442283e07ad6fbbdf2507d9cf056ab1de56c919c71823a43ad718a1e45221dc2ebc43873db969d036e0950df683d9d3273c08647089cabac97e551e043fc87ee82edca133daf03c00070603a4fa91280d6c0366f347795597334fb68ea882d87ee1f87f4d1a9f638429c3a75637d78262143b10903ac4123bb1e8e36a56473c15d490fa19066d17e9c45aac5c838cf4c8ec5ca20208553c672184ab065802d4c959d3c778ab4c5d1bd93f5fab5fb0e5cd2d12dd21cffa1c1bee77e29327577669c64f005ef8fd06d8c3a44e0f032b233c7fa3fecc6bf2ecaaf87948e82476a97ff2ed5a830af9df585ce74a097dbf04e306ddda7926c2217eea6c8daaa92a33f95799daace88acc0b9aebf0f85cde627d9a58c003821ceaaa52245c59670bf8de4190600af0283af4d86cfe59a8c7f90edddfbd9ae15ea04021f3c0b8bf9259f6cb9a454847708755e370094511e715aea178c5d3ce2fb3afeb3456f891cebce1ea3e768a49afc4a7f6828d8036513cd1bbb5d07a327af24af4c7d8daa816a3c1a9be94ed60f70235f30ae3f3df0e469eddcce81bd2c8473c1f8d3ef02ee93f93367e53ecd005cf440cabf0203c08b06cc135c0e892ea47e571af10615223522013ea483b35137b6c8eb492810c6fc6d2c2dede45c22ffde58a3902cc5be61cd95100c3ac9ccebce100a3ec1c315640abbddae20eecdcb04ffbc9c58b7be1c60a40ddb15cd640b1b048b6cb462bb1c0b055d29a27d1391f7edb4264bcf52c7c9a9d4ee00e8da32a0b6876e82b11f41430552363e3eae4c4435218d95a09deb2f576df0efd71b34410df5a81ab656d6508814cb4702e1b0ede6fd08c03d1f1499ad31e046632a0472d03297d86444c3c6bfda8a4b8bdb748c683edd71551b1e28135637e064f00dd2720206e471813d9908fc5303d37916a54fe4509790419baebc00d33afa6aa037eee719b95f8eafe38f0f1267393fe60c9c8a1ed911a1443522edef65e21a9f4d90068a878b7855c1d50fad3ad9881bfc14ca70c4602352e27fc991ece95b7be797939d6f2e79e94c41bb70c2cc7e7433d907dd7ef882f38ceec0a43e648b072b19efafb84e60be3f5ee478c235385ee3fa59a1d6a324781a1b39fc67c9d1a956163e20c2fb873e057ca0703f6e1dde9acb60bcd2668b9b4a21763feac09557918ffc4f4eea0a4f0d9cc44eed83429381337c8712718eb9a927ce6cc0293bf836cae45d535a45159196d4839700f6cc604f3d2d8c584c0155e30cda8c8c01717fa0c7e5d9984c4c14856f7767a6f730538e669de833de781c1b271fb9a232d15700145b955f363550b57452a63da8dcbe91e46a1216106cbd184e90eee6ff0d15acc2b25ec79a74aa4f71d6b3a5005fedac263db7ec10f5071ab64bca6b59eeab8e7e035c4ee68396e79c28f6cd911798ed0d31fe99f342178078322a49077089b0b03864874150a5c689190c20ff20d2022679d1fa1c6d34f62e26ce115cdf2599e9cee7c1793f4dff71a43a40a56effee4d863233d7a6241d1fa82146042cca02ea20d2d1602c6eae8f158c5a0a60be1360dcfee05a38f682a99749ef3ddbacad74a9d813a1248a4bfca18e199e164b58a4e919862a287aa91a63fc07f7a07e88d0bee25109f496f98955ca29a89738fb5b3c33a96f1d3ad6b2ee027dc4170a339cc9b6e0713b9bd628f098fbbfbcb1a3ddb9436628998e592f72548ca331cc564ef923f4d834cccecf4595071a8c235c1fde16ed8e6823b9824be2b7f030fbe256b0dc35f76eabea2e804fee81c82dc47e262610a7697e2a5a4bce754320d4d44c30859d1f49b848b0403ac81841b9c660718ca144d3a039da6e17c39375be90011f2099cefaa288f46623856d77f6587688b8e38a1a8f5c204ae9c2d88ad622e89ba77061484eb283625d18aee09a88d852db82dec238b5803ba6a9309297e2a37ec457b29273b2be5218752f5f77c2b86e755a05613055ea2625b407018b5d088328b92e49408263f8bc8fe4ff2ac70b06d4006baf4c7afb1f389453574bd72162b12cc8e7f173226d165af8735e024371b730f4674135335e63f0b25d131294b529d9db250a9fb84b16c7373a116b8c16efc1ef63525e89a9063e09d8547781d7a24126d23bf138049d621daed9bf5b70e522760cf5816f23b2031adadd6ed6378beb1d596f3f7b6c509eb57dec36c6f78decc1c7b4c1eec68941ecf57ea3d5881ceb14220d00d948b310348ffdc03557fb6184df31324ff5504c56f22f8c056410f922c32bc1990d1ada45aea152e8a52aa58d591da4c0562c8aee15182408f5172f8f5a35675a6776610ea051930b52b39d65da2d341b02fbcd7e84508cfd7e0dcbb5b4054b936d41c91948b89c00194ac8f23d087ecdb4a508ac1265422edfdaae7bd768cc67fd19fe75b43f70a8bbd913895cd59899e4f020a1c64ef2ac2cbe46064637873425782790c2e5ced53bbe8e83ac5f56f15de66ece8a748f15402bb7d32bd70246106f97f728e36baeba596034846168b81921bf21f90e3ade509bf411c9576281979e61851f85e5afbb1b5f591b0269582223dd51b073ae5106852a4dfac27e953fbb4fb873668aa015113c7613d56bc3524fcf02918f95fbba16661198676028f3a0229a68883a6ac6c8d8c77e878f73fbf84f54c5ec37bfb9f25ff0aa00f76f9a96217d21a23a3d627edeff01e39000f644e6bfffd25fd1cee1f1bdc3de6bfd0010708781f28d2bb777de718c22a6c5e4c6189a11719d2f24a18b55fbd02b3db45270d4488aec686b5f6a7afdc2d132832f6b947cb6250a0e82204d31e3933ea780b8e90b4238a3ceb483b0ffe4760b0abb85931f74f3211c33cf486ad7c7b8cd71d6662479c8487b6b92bcbd9c81b3236b497911f132d8566d0a07de0f8e6cb0ee0c80bbce90dd211bd0439d123506de95391bdf26379c641d8e577281d7719aea805e5a0661576ab69740fdb5b5f578eb5ffa614b586c7bbacd7269afa70ee2abeb92ba1587c5c923aac7b87512589eb92d1bb1404c9d1bd2b4c20da610242f4043a7628c21827249f6ad51ec799c2018f0a6c3c0b501008f14ee6942152f843569b1ed03d866d0945e3b9fb819e4438006513695915a3035e1158dda51a6393f7be1ad08ef88673315be260c55a88e24a89dda96c308afa221e0a10d08d933006272088972d2dda687f27a873311c30a3de6573ae3561e996e4b0b688e29e6f40b32f9481d70fc4074c975633cfc71a966f0eae175e2e74e3026fe4c9bdb1ad0734061de6d7e0c52af17648faff0b7b861f19a8c50e6fee04514f4e4d2095157f5366ab71a00495ac353a5ac6f6b0b0c56baf80080356e16786042c5e72dd5e29da871dec73e6a1f68148c7117a84bf6cd00116143fc865b4d431edd449b5a2963e9748e6d88908f7909318cd48fbaa201dc64b87d518596ec00372cd3638f7fc326b623a1922db1a59d77be8148428281aeb063b60dc8e0a91df3f2a49d2304575a5f6d9c2cb8e7baaecbf6f5961d3117436a829a43620b859a8ac4de1f2e79f72e30d08122fd0ae2e3447e3ce626d4c71f1a7a7c126a4f55ad6fd380ba9e99cca42a759bae5304d26f4cf1b53d88cb112269b547d035d482990125688a3082401a8f1ce8e3d20ebc5517888c4d180d44c5108ab013f55106fe1c8eeb4f4ad70964ae6dcae3b82c73fcbddefe80dd4280ee3818bd4ae4bde751b8a3a0747d921577ad0f7936019f23378bf850a6501e50b19344e7734cbc462e0f492c2d36343c13785988e617429b96166108c12920791a3ef18b7f5e252631831a9bb7664391a998455aa7d742fa6b49fc52c7c759d63e36152ca18c6ab846148dcb351b07606333ad7668a6ed69f1d6acc9a6ecfaa8515845469a426d64dc7483d4d90b1e35e14386036d680bca3632e9403f385c2b463dca9799a3e57cc31fe40b633744bf5f7cb818c5af7a8f27c9a4d88b7daea99ba1db01bba02002d2bec3075796991db30ec1dc2f98929c0ef031b7446206c9454ad111226f19614f391eccad26ca805f039f6d3a4d712528a00408045172bf502b5a5c90488c4f2c6356061f01ef33fc04763c086600e37b65f581bd24a27c9b39a7f0c71b9cea5430e36e091998b19afed0741a66bc5c06089689e8111aefcece76e8a5c3ae27d1913ded3d60f17c989d019146871c37bfe619287d227453baae9b2020f46910b821c276fe3266707a64679050650a22e2133aa7a0f16b34345144c4ae87134dacd0e1ce3abfeab8a55a7f9827b2d0892a369d4051bb0c3cbac10d04f254f0215cd1cb4438293c497cd186130cf701ab42e94ff165ffc43310fcdcbbbe5fc60a25a8546e4274199d30d213708a60f739f2cad37ee467e370a3d517bd96ff405cfe44406081334a00ac618c80a681892d6c77852a57ea8ea3a0184b8891f25277d80e941b4fad9b3959fd37e1b4af560bc46c08c868fad56c0d58baf73e6466d959e7fa4047aac032ef09550d0c748b045fd1799dd0d5a3274c272c7291af54c621c1d35379f8b17b5ee8ccfbbe94e88ce136c3d990b9c0e2e7b2372080532e836a6340f730e95dfe9d74f031fde53835a19943bce7b52baeba50b8a2111fa65be1e97b5b46c3c0f2d16105a191dbb0ee22cd5820c28e8020bdc2cc92b0021f5bbb78e8d5b1045b43c482107b6cdf2d0dbf8f9ba124e761136e84a1ce9b0094d484fa620957fee615abcf8441348e477fcc64bb493316b0984de66e4d773e620a22dc8919621606c78ffe63b4d3db468688a05d2c5060305e203ce78059c49118577d4548c462680702fb76910eaff70e27419cdfc7b1054d1da93677efd4ca53a0becfea6f2d33880c4c117e2d057e322a7608f7ae3323eff09622bbe1430ae4eb25f090470ed5684ad9d119235ebcdb3e3c2724f2acb589d57fd55feb38c51f5ec2343e1305a75e0660c875c17f820675af3a382e2c3e47e5347f4172610855131b863296a1f0b887fea519a57bc76aa6df23d7fa723d3a749679d5f599c76d130c9f97a66f8d83313ee7c0083fdf5caa5fd459a72ccd671c3d54cdc681cc82f6982ae84989e663caa45ae7346adeb36bb7abcc2736b763c7d827a97c2f701992f11b90791307ea793daf7b20d0b57aee07b39270aa754d980a0b600946fda80db8432f6dd1b420ac08242ca0196960d12c2a00719c1ae439b94d43082d2624046a2fcdb36a93826956b827e1c2e6ee8aef4a5c5c2d5442252e5fded5a2fdb1715943b656dc2e84643bbd1d7e85a66d55d6678c1314461a71ec7635e8f94acf22c15f903c2fe3df71203d07a2bcb2b58f76d9b74f5ee302abd2a0cac12ae0abb0ad118db49ee5632a6e3afae38cb07831e5fd6243aba101d532e58c70c5f5c4e6f9a30a7600e75254ffd463fe55f947ca6d2af283cb5f3c4e7cf1afff93c707c4d037723a2be99b4bc39e9940d1247c9f48752772e23344cba813cc76b9ef7dadf50b7f51e76744be74741017857c9357cd9c09910f34ac55caa1b5a768163a6ebe8b289e315c090769dff8c983ec2d66e65a8b3131cc81812d8336c5170671867990015d7d66d12ef26c17c48d40d686fdd8bc8c7f07e87d4dbc7583a7c3fc85ad396adedb961bce0d497c2384fc8432ef0ae40df9b45d5b09e811a1aa5152035b26d4f396c54cfa6661389b37ec2a48711c33dc258a1f53ae0281633a98083bb118a7508d114fd003156cfab92284225f7c9f2c46db167386f730d7ed1e99508baa14c300e337013a6633fea98b4a52602153186d869acc0b44461b8face84c3be8e1856d51b71f38908bb1e57b09aabadc53ea2445fdca47913840bfcaf2d2854a0f6a18adcfdf0167d4ccf450e4a891e3485f6c58837e983a25d310392a5ba70acf99ad481f1f830a3b256ccbaee8b8227a35e8a58b28dc0d1da29a6be70ff16734d580b26606c1bdf3afbc7d2bc21ffa79928e43bbfe1ed7fb481db9d1b1cf91c7d7abda83ff750db30fc7e8bec7cbfb369cb36fa021e943b6b47ca28c9c56765e9f7c61e5d5110d898b05918bc778c726a64ccf68a60a4d446e104a32af659fe3eab134fbb3193f83b3a9fd39ccb32f2e2dea2da23cf9abbdc8f15f224983506056f87469511bff2f01b18f25c9228f152fc2310529a28c5553eb71a7a6801aab79a472f5800c4608be08b59f9e7152e3687309383a2d174abc57ba9e7267c834e657d741e73709fd77f76c0289d568a7adce14331bfad9a0f33a4ca976804efe23db5ced0182e968224ce30c924bcd082e995de57faf2573d4fda0ed66eb3ae0d759865d4b7741dedd3c69981fa17dcf3b8ba773bfbf75cd2445f642225fce3203d24f965ddda233fa41d66710b6f154cd4958f3a32a819881fc2ff1ca757d3386d408db447a20b68774b0820074fb97481611ec3bf3e264b01481fd107025c770081994c532457386e1dde683397b8f28d8307af81e240eacf4ca8a513044dee4ab9ae80e89c23cddde6ad871345553c3cf5a80cb29106d71839d1085a4b710fbdc5bc82c37f033d2be65661c56f33cb905bf0c12b4bd18c8e6f786430357e35daa5693099d67aa69c036416c37519b933c37ffd02a5f63e7090771abcd90f7d9e01bb9a3ec919a7cfa5fe38c82a80a59f5ad1ada62613959a2c1f3a7a0b7dcc2e5a2a32fee6e3a8f58eacd2d840b624199f7d1c640bacbf639708e3c9025d19c9bfc6794b5c0bd33aea9a67d79897e8c0e48f2854e44a3f0f6a9928c3ae400e01afc26acbd75282f76e5024b7af66fbd3097f6081cc020d62773e03438bf0d1dcff01f272f36ce1de013cd8e10b1279a375dcfc95bdb1fc8361cbe102636390cea7808793be4b2a13744110a504731e6dd06f0b740d556ef5891368763ffa4126389fcc256ab8cb9fe69d47493a615559a3cfbb059f7eb8e1d77859acee4b6c48a401b60123d4ef0296a888fb67afacb9cc5da7f09a912beca4a62b023f10b3b64bf9e28138d9c01f87c3757cc8b3cfbaac6358feb11cf8fcea8515b6268c34f672a3e83de3ddef7f2c5b5488546d26108b28e01074f50f180133bf2ab757ce0aaed3793727f2e987fca522f29e8ca35ae335544a7b9cde7b8013cb9992d660560b5e3a603f48907773a405e70f56685b9c36504e30181c096a0386a98a03aec70705033154efa3aace2c4f39474751e3d34b084f692fe2b0069179f0446d012fa9ad591608cd8da48a7d9c3eb06721fdd00e020618ca20c2e9ac66c59777c38088cd9ede70eec1ecf8ceda63d73edc792cd4414360d684bf57241b5d2dc6b81b5ee4956a6313fdbd41ad6d5e8edd070a21e755d6528af5865961676dd9f5940749f36c41fb8fd8e51b83e8af26d88358526b2ca4e1a2e1bdb7f9d361e6b33dc095d18ed43be423d466d004be044083623d22633eec7af79711f9bf9b8b18b47c4924fa9aed721ce2663a7dfa4bb13f9138e28f91c9d05cd88c698a00e34a80d0cbc402a7abd1f00457f2807404e5fea8e51103140ae88a2fa49d76bd4f72d6152104376652bbda6fa30dbab2c25a0e3417f61c985d7c90af74313fd1da67ce86adcee4955ff4fc502d24b0bc33cbce06ce002b7e5b367d9de5ad44f6b676cf27db35901071670afe22d54c9b1233db73836c4e6ac1cac0090b5c3a7f4b51db9de5d1734c631f49c18ded45dc2652548a659f19baa99a63d62f5cd9608b8661cf7fc88ec0ade0d13568d8cc4091e07c7d9d8544b4b521e616ef141187ddcb9f4caaec7be8d90cb66ec8ac2f78a19c48f7d18b55d596b7e6aeec7c3ec1222fad3d5bea16033b6ea9ff30f94e97d661e922ed23bfb9557fb1742bc36dca6b202f9218b2ff870b64c7aff4acd74513fcb8b1d456c917adcc57a88b77c978093edbdf8a5d4cbca0319f731c8b61253a77077ac2d575cc29c65290174ab6bf04860b8168737dfc9102903df24368f1f7b2e178eb3a0867537fbf301dd75b00070e5715cbbbef5f1164c30a0acaf78ea75b958af893f4cf76def228bc6ab77bb89d7977d26dd07a957c35321ebeeeb8e8b784947a4c5c44c85ee3dfbbcf8cd429f2cdc161c5203aab4b572b63f2e40759197311856661a94e1cc9cd9aca266af0489cfcbdd653a6ca84cd1b70175bd6ce932840f5f9dfba39dcca3b15b8bcc9d7a077cf38b4e43c2d0579de4186a7db488367c2afd8cc8732d7b3dde96a334335804c0bba48fd0465286160129e4481274a79aa46b54e93c2e6b557fb2d2e72de5f0f92079de6b46cbe80af70ddc05dbf944d01ef405129b75cfae13b113425d8444715edfb667ef8228415d7cecb2402405401583d9a714aaa3b8afb9b9b1626e82f85bd8d23c1677f9be212935ca19d341b726495f50d178a17db767ee7b105b922361938e6ef2420cf5572ab2cde3a05894170bb10df9a35a2e5a0d9d5a9078f169825924259a9ddd8cf0b7284105c4ac8453aea54056c6ace84413998ee6af9e2d8cdfe06af335164e90275613a357765f1c55c718d037941820d837b766264d093bbfdc7b703e589a9db324df79612fcef727e912acd2fbc12c395e98c7d8b1278d5a3facfb2093ba0a2cda2da673c300c8747da11574389142f859a588005ddb716964efe42cab10f97dd4a04a3260a5a6026825e1129294e92e7f63388d2923e31d8fc74a34a0a42857e41f811d3c74131ab73ceeba63417c3b902d4b962a476d8c8323527f6af03dd7aa2add82d9e5d21d4bd3b63f8d5508cc8ccd55c07a3e10328ba0c51bcbb616b001141ad6ccff3f3a52ae6bd1fd4ca26c6d2c219c7bf9e79d7a2e3c7ba9257c7b5092002ecd22082564eb9dc786681fe513752b2064882ce7dd52c19c32984ee445399c00e09bd31682a5f0d7b8268c65436ba0d4356018998a8dc55b108c85c25e42587866757b4341222cb7008dfdcfb23e80fe0352dff5687b3b98ef8289650de50556b045ccd304befccdb98564b82a096291f8cdf2760984f3a5290a44e3871e8295d4ce30812799d74142d12bd245dcf315f4b211384f454e489e87a80855f5edc06f7ba2ebd1219103f1ae4c0ece8e9e7b86b196566358b05cc4df733a2ca3e5cab2fdc14807f1aee61f8988252b09f975d591c82c0db757b294e16594f050824badd4fdc72bda7cc0988fd65817435a9cfc5ecf0ae843a427087ab7c2fe99ff5d4f65861a8bb8355a059593c638fecbf1e3a826ecba88b6706a738c63869960964139c3d0270f6aeb4a99ebbb0167d39f4efcbc8d84c7b0935899900a22b627eb81ea21dabe8162c59ef159e27d7df1fc13d46305e063c08e38fa1a2f902cf4a1ef9a9283f950a2b9ce2ef5120a77f313e7e5b123c64f0262a11e2327d243d216cc9c58fb1ef7b3994793308c3bd16c10a0d18170e663db168a09583b91ff6e79fed89629b3d856c6602382e5409d99925aa9c119ed429896044003f6012d99094afd538de684beba6b7652791a8a5cf3cc39ca1b042183b72a4339e9ae4c3196da559cc8660297bf7c499e329fb91e7279aa905c9257458f8b6d393ccb510c8b509b941841e1961a04bd5ae7bdad1b7132039f8d7582d268021d3a9ed91be2b17950d7a099dfe963834521e13ec9a009ef037c908b8fd40e854dd14a69eb2352ff34b3fc44cdd4cfdc9b2068c69794998649b60252d9ec53ce26c30323a7be0a1d9d2e8dcc9241d66c4f8baa1878a66b4c3d20318f7cb8119a5a146e839291b1a2dfb1c458b63f01f802ebb396276b7631605d285d631b0245bbb3efd7c7e23feb5c8456d6cee8430299591b9e3b0aafeae5fb0349ee5408ea192cbb605ca9957d1edeb73c077b7f08c421c4156c966017978faa5301bfeb8174e71731e1e08101ae94f23449880823d054c3666ee2df0d44641641d218e3c297868bb7b60d9d0a92abf6f012d403850546b74f4f85c1a0b4216084a12aee858dcb42edffbac874d5913fc4c19c893c06c9afb179fea443106df3a19bee90a03da1acb91ce8bb69102d4fa1c5c32eb3bbc478b459ab40428ea0d9c4381b988e037b5714621a2c733015a3240eb88844075a1807ab8be0318244ee29076f71d9f8a70c88e30f36c992efc24cf97666adf3d19bea51ee0ec446856ea36020bab08b7a543c33ac54794be993443b9eb60dfd9e55a112f64bd2ea993a220a46dca03c0d79634f6c63396940b1e7b052747d1651306971e7ef68338af2c5dddbcb20bab86bba3c2b43348c31284d9b759c7a62251a47203ade31ce59a9ad379f21f9b8279d35140f3b8c1b02a0fb61d1c10f7f5f8cf8fd72dbf29fe5c51026a557d536d2eeb105b53aea8897aa7a6f0670429f948c44e550a0d914072b73a8014b2010a6188e915f83981895bd3f5a0a3493aa1dc054fbd7f9d990dbefee196519f2ca6883d1118b85ebf42c4c50b655a7201d13516671a134d50284bff0ad0bdb09a642f3c149d889eb587d73424b1a32debfac2e237cc88176630518141aa95eff800710bbe87ba9ddfbb24defd95f2b706d96acb173bdb27c42d9b5a0198b2eabbc80641676ba36470a070db368460d3923af702771e00ec74270081b3a0bd4e043147b9479bab7ffc6c287bb0f5995a3a23f49393ee0dc99d4f9c6368b10a22322cc73741c13e90ad22260bb6e01ca9849e002338e225d1603c0f31949bc406f8a9f5594225739d39227158c362c3708b0ae5132b2224e2d300ee728f9917ced6901cac10590fedea57360968d08e0134e50abe98d3a12acfd393e93a526b6d6cdd88673aabfe7bdec8abd88bd6d07db79c2025e19661fbf91cfaf830f763b38f52a7448f1398926105ba8e4df2ec3e8b5089a60e6be3b649181c949eb2f9852670ad6ab8a9b538aa4b69d0ef48b3b10b06dbc8723b837ccb1d522a7ae506a04b72f50cfa0d8029744da6d3633f95df991d2dfe04842c66a06639cd166f741b5d9d5c794fe3761022f13ccda325af6e7a574a012bc766bd3f88ff20248afee366c2787572fd475d9f2a9ece7994603213016e52f93b13429d82d26d13b2bc0b584a4b19a5a664cc0bc5772d40f77cb956666e67125ffa396f8927a77bd2512bfec6ce210515b81ed8f56b4be8b677895b7e98d3ce2eb7959aceeb05926b0b2cf7c47c32c93cdd464445b4983eaab69ced1c1e33e60aebf28cec6e2f957aa635516e881ee6ef78862e828859161a6f2cedba7422d1f6b35d7c0e6be66e29510698b73d202c52dd1e043800fc32ef58e5b08312f240765450dec88fec10d5131b04d83cad60f921ebd85b7379cc879a4f33ff23ccedbc7787005f55e0cf19c39af55315289af8ae296630e85ffb7b3df5583ef8616a39011bf9e3008e02148d589c904ab567674b2c46ab30be054f9ae23201323e58848f789ee48d6d5f335941c3bc54de838887327dbd702d2bf7fa861dc1421bf8e76bbb4adf1207a43a155ed98cfbcee1cd6cebaf351d124c1bc1179d3eadcfde0f22a88c8989aa9db91cb79d54171d796877822c3bc2e1562a2443edb6ab0a2a7f37a14e76b028a6590d4eb80c30ccfb5bc114b4c4153b94e122fe565db7be43655d8b88053aec5eb9e2f65bdb00574971e4b59cfbe40db65204821fc04df783278f6e99e77e3888ea5591bbce12810546d759d278f5c90ff46e33d3f2ca3651533e0122c5e4135590d486ba75d4c6c8013cbeae0786b06c4aac200f828c8a6961c6cd8b538a82717996fe49da94f8233ad294faf9c3b24ac6737ac755b44be07c2fc96f6b688663dde191507238f2bceac46760f726fae51697c06e7ed9b5ce2cdb499388067a40388f59f7d87e37cb9053921ca5a26ac03b1f533367da375c4a86d21d7249a66257a9f6bfe3ddc0c35af0d945d7863fef070007f71c151e1e22bdfd4494841a7f405a87fe6214276c71bac5a6f959e7439650973e4072467a063cd51174a4373ee278e1eedc3b0525b8ba74abbe0a51bd950ee7a0b7f58503537e2931561179fd81f6620d96d05a1ce12a9588bd09bcbd0adba6a3b836432dd3950c29c582df42872e21225f9ac6c24cc0cc81add0518f518b0745207957b9fd8d4f974dbf1367bdfe6a757b33d6a5507c9ae0bd8483e4a5333d164d88fb4bba5d5280b4fed5edaeefcb198ff614ce7a3cb765dfd6a339b1d1a97f9a3e030d8c540eb64322bdfd77ab8493c5b1804716a94e973faa764e88fc59f9557ff259c8cdcebc62c6630904b0baab372ed174ebc92128aa5f1a316defeeb9768d8953f195a8a08e29185c56dd5624f975ba1c500d9e21e82ba46343579b241998baecd1d9d840bd48f094573a6064612bb4cf730c76199051b14fa2d882af6c2bccfc70d068deb1c24dc1fc5ced0c4f967b5b58408d1e0475d7cfec7395c6db77ef08abb88f7bf2aba975dd7f733362a3b61a1dd84d0af2fc069d688b404d460a4661767b7c6e6ba056ab141ffd59e5a39bb96a6adb350989016f8c6d3dbcabffc3958cd035aeedafbb0f1e6976eaf42cbd8110a3dd8b6871779a9b4d39618158b0876567b3306c12a1027ef9a5c4cf74f486c6e4d4f808d4656625e7c05754a54b499ff679b9a9e37d376f67b15f10001fe2faf3fd9f89a8df44ac7bfa64a7901fefc5a4232201eaf8d61ffd33814e7012e51a7bb1847a35849f522a00889dfb618038d4a35bca21f6a1292a921e0e7c6b87b77a73d00585df2a559aa3061a851f40c4d2d9af5e398a792f09bdd8b72dc03b4f648cd2b31ddc8610fda382040e8a8262ef3100d158888fbb2aca235482c1fd05fbf5f15ed8ee0e39bd643f3fd0963e77669a72f86f04157134a56a59404fccb4f288cdb5dd6182523db42524102b46662d011f34240237a017dfa56f857e732c8071e6ed4d9abf67ecee7a78162ad46393da94945bb9e91291b15d63871848a79122f652cd1311b5fb1d6e0d80b47c27e9179e7fc00651a8a4573a71ec348fa120d90f27041cfac1a3c3df5ca7fadd77b0c1f64f6bc62af40f5e47eabe93e4cbe0e25442b0043e3f55b12fbbef210827b03518f8401cf9f7d2090fe9c889bacf0c3b35bdcdda4e22a0c098a72fe06fada48220109f57d30a56a498c6b5060629f5c0859d98a5a6735067cca06c44e9351d531a26362006264bfc8c1bceec9111b157d582abcef55cb9be5241628dd22bbb92931ab09409f9d5e04f90250f9b4f16c648781d4abdc988716255429a068798c64497d20b22d0e380afe29ba2ffe570c9f2509c434964869e4800366b89bbd3950233276fb561e940276338d5f485953b494c32509e14b7b584002c889fc6cc68a92f262498fe9ec3288d2eea7a4288377af78e6f29a8e2f68501a57d0d9cdb246eaf32240cea76f93145b24a11289e0a143346f4f09285e8cb6d64799a83b307f8ff50d83c2de70e4d73ec80fa8bd6f689e53e57a6f6e5dad847d0c0a1168eaa546215fc45ecc8662427a15e44c356809791542719226eb21977f46c680c0bdef68f167b67af47b7d8bc8128e0d5b1a28e854d252f5b20b081af2a86369a710518a754092d1a3330205341c31279a96fe04a60c9abe2b97d9b953978cc7a12f30d2a0226f8898f7807285da2268f78cab075cba6b367af136caf5024d717a5cbd15f99e441c3afeed3c6f92130c2960ba487250a1538e7e1aeff17ae4451a27d51bc6943a6b1f56f41b27a56c8914944ea48cab098496f35ea1b8d44aa81615db4328fd9aafd9488c5fee59d2046401312dd60b3efebfd18f8f156168025c9a1bd64bd79303716913f754b8f8ed156f617c0bb6c28e83998f2414b0a0daee0f4ee3b0ca936616c638f3022ca543508ab108d4bbd342cc4845def43cb89beea0106eb8d748b6cf4c95c017e4c4a3ac25d17df8f525dcf903650e8eddf931554b6de7544e8f751ed159c2e2f5b82ac46240f60867cf508df4a693813f89ca9d0dc54851e27491527c16f83e5249e4b3f22c2433080ef379b5bcfdd5cbf44648dd9559b52a95dbf395aaec5ab17a56f213c2c69c2bed246f161dc1cda431d606573ddd67de005b2c9dd71c9d1a8f2da9bb7d928d76431b95f3e60fffc6c8a52f4c534489db616f207aef8ace4b05e8267287cc63357e603e2c377b98e2470a7f23c7fe7e86e80df2c4768e64ea4952c696606c15cb24cbc32a69866c524166c55d0f81e3e46fca39bd4e5651eb01c90935ab000ffad5d6735e85005b80a4cd24214a5d6018ea4b858a2e745c7552d999ee40dee0df7ef9a01208c0094b1794871dfb90ae984ab469038802991a76687e68800a764ff3123b3f0da7a4fca4540b379c45a779a0aa9f935336a453f87ec801d8b00063867ba8be5487ba668bc881a6a704154756625499248912dd347e642bfc949f7e801b158a3444cd9f06473cdc06faa139a002c3551667f22136dde5ea14fd273509d0ea69e2a14712b6f151357c87d1c558c75586f548c7b871d780a425c14ef869e8816993695663e226573700d9006aa4f4535aa75505f2a144e2ed4134376485125184b1f73a9da58a34b12226c12265a0ee0b4ccf682c1b3779af32bae8be28c10822e4a027948ac60f2b20d974c32afcafe314563a939ad2d1a6bdd1553685e0ea359defa12224e5eac09504edc2f99e3290860633f9e4b63bd8f6e6aadc1ef4cba681aaf996ec242c3519e4a2fd64d78c9c8de488e96853f347dbe9ccd45899765898c7b2e59ad29ce60d2b43c5309fb2a70446e1c0e13ce58bd58ec20a69911d7655e984ae43f120684b0f702d870c6a92c349d949e9b39fde5c9b62978d4eb8a43f6bd41a996b60af63218ae4403cf764210cbef154efdcf6df34debd58f1b66ea750079f0a97b9368e0a360d92df70e09365714acf2114971e912dd5777d1ccbf2d2a83d5c038001846bb18837b74ce525150e30bc0b079a353eb1b27183372b1ac0be01fa3e7cabd34f92318d3fb5d33e6e8936787da645c29c41fb3cc03a235a85dc4bcfd6b4e98073ba2d51f14a70b65f5e62bc007e76d90b3eb086e91b6bde38fdbe70fde2dde1272cc665b4677ff6f5b6da432aa221ae4217aa11be315f9391895f8c17ffacef7630d64537896d79abe4731db3dc2af978b478f43bc7bae44e06b61f9a528035598837062f7807e51108822fce16e1727566768a157565b7369c6151113804459f7495c46648bcebab6d8e187149d98a2e3360a0ecbfc0b607ac48160418bbcc1864f2845f26712844c931945e73fd3c819ddb145163bc851ce598770d4fc1f1f298d2f32d76709df7142bf3f29c5697e7c421b0baa043de67baa9e14043062b53ac425091b395a975447678fd136746f08f592427478487c1e99e52e9c75c2d362938bb478333b6a3a60d518f8f15222cc17690396145d0d6f8cff859aeb94371b12cbc0dbafe8e7be138020143c1444d0211f3aea1f861ef1922d8a15380ba9b46e141255c15e9e4aa08238d9039812855e3ac7bc5e9bb84dbd3016c94430de1a0d53ca5ce8c135db3fffe2a53e44e1b0f29b629bb4f52fea7c0ad3dcb59a72b569a43bfa804e05d07c20b7813f1902fd2b07983cf9a13fa8655fcfd937344779f21d62651b891d95ec9520a409d2eac6d3d22466c8be96b65512de6fa4f6bc0b50b6e6c48394bfbc4362d3334812f328ab4fb6b372949f6a89a23f8b2cf6a1e5447f9e9a8854f462853215981056e5ac11ad9384ee58cc6e3b0c79b72c800288a01ec4a0172092cba8cdf5df0787ebbb0830296ceb513f491f2cee7e01c5744800d66a7436f01106966c5d9ae21142efa1c065c260d18fafb302f3d46fd7a010c6c546318021e1ea4167b89c43689bb7ecd0e6f6cab5fc5fdc489233b6ca24a210eca212d9245d1edb0b80df7ea805d6564a3ba0271283dc611e72cabadc61848cbbedacfc66a08d84716c35080f97988d217223aca933289717ac1cb605a80f7e692225504f8951b3bb4bd4cb2a7f62459bf2b7c976f4044fcce3a76ac1f7d4bdc5bb0b6c5c2976fc84b5e1047cc41ba48093ccd7b6b23b43ab8812d90dad273891877f558444037165d04f67b00027340f337e0ffd68836ef72be029b6b089f8ac86f46f9403b40cc267b7edc5e2ddb7815bb72bc220bb09cf91bd00828a0c3cb79d11b50e9dfed861d15e38b6341957f32f5f19222a18435d9d02bd3eea26b622a74bea261164a8130d977000d9f7b855c4ac05f6d50d43b79336d5bf7ebccbc5fc42f9d5049035f92e095fef308da00045dd39c8ff63005e488b29f6f7683210412896e631445f1d7fced33075cd402215a67d31767fb5c0eb2edf1df30dfdf6b73c85b3ce93cab4ca9be8c67d9214073e821d9ba578f059ea87c4f921e4fb7e69110ecad840ea011e9bef87ad8de6eea178256ba846b0916e1eb12b724e04014598780899cad7fadd7505c3e55d10919c0e0613af22f526e42ffe9b8d149ed9b1b003587d2f46038100cb3f93b4f2eb618df9f49af6e4f322183db85644a1bd13f333c94d7b4a05745cbdd8ca45a613c6f6708a0d7d0ed603b03f98e95f33a923d2909804c77dbce2aad0e329603fb034ae7ecca9e006104d4650b3cd5f81886f219344af5d823712042a726a88b301d5784d40c67f20f8b1b2a8e4c93dfd5496581505204cb7fabc772f393bb4c25117b5f322247d585cad46044c9c3069904cf25f91af792840dc898a0e8c83df7039bb17c433d929a5919a80b974c4b1823d9140094c82cdc17a800ab6c1c85127f9b7b9accab494194f216763c21c1e1297dacf22fc8f2f8154a9acbb2f662d032964aad0c98de9320da00a936efcba3c934657f2903ba410718864d78d13fc9014307a74d05cdd6d77ca055831f94905073b51dcf0b571360728311f78ea3cdd3ac35dcbe453e28906f9dea726dc6181ef81eb513c993ac531138935a3c10856c9418accfb0314997413689337e22407cd0a4a188778d25a5b557e141cfd5cbd13f5d9dd114be4df070bb01dd30377ef16675defaa5b2634d0f9f97987d9a33c5167e3cf8226c9c2f57baa730d3ea8b3be75ff9c7f51929643a770c87150365b7a15bdad94f72a526ead36c52e26fff75dbb967b9eaa1161746e80c62192cec76a7d5538b65f15af7580513134284d11eaebc2c83f319e5475980b605d152f8b5c0a34be43317ef664f4f00841135a93f1336bc9240fe752d4564ab1d83bdb21aa396d925df00975963088900745b080a8f6ac7217839cc4c8bd65e9e862160a12dc36bed1a7e0191399839c92099c85cc943a8474c0e4f0bf8ea04a10b36bb9e65460cd9306dd48258cd62805136a6dd0b5cbb9def29852689e3b7aaccb20cc32d14225f67be7ec94a1b739ea941455cf04b96931dd1d410387b557f9949b18da4ca7531d311c75b5551b744f20181513e250034d67b528ef99023d58a2431a8e0ac195c3b38c6731b279eab0bda61e37c29974c14ef036c25629c674eededc587df5bbd40529f3ef9b98b7f889ff056904943b07b805479bb1c6714dc8fa83c87a16091107cc1f4a3cb187f15c074e071d96b3f5fac79af477aa3b8b1a8691cfce9784a0bd73eb8bbf9d8b881e44fcf9e8a86f1167a97bc54d2a7f7b447398e33be97fd2ece3f8ced7c835f2aa344dd2a74f6ec88b2b6e177875c74562ab6567182bfa4abeac052b6afd7ab7bdd2fc8d364a87afb6efefd2d7eb26227de664942bb6b6f0191c4df8ed93d3e5d5c1932b08f6234e3a62e1ed49ff07ab498e3417b7920e17eaddf159a7417e6ff63cb6f39eff3d4ff5ca89a21120611c5677c2c0efa2b78ed6b35a444797dcaae8539c45a2c83c843dcfc35f833f10091418bced14e3eb7fe4c7d2baec7ade52b2ea1ed93f351bfe02d4389f51ffede91f5c33f66eb2693d0e98bb59581da7cbe951747105682d232e78f46f578303436c2c6c078afcab31957af13d60c4c31155cc97894273366230e0bedf8963a423d48313e250b80270d828ffa5cc9b50a708a4be5bc2f0318b80d5fb5de2d8425d5de2dd5b90eb5127a8c2c9956388391387954b1047aa056b6c94b13bffced9075ab059ee96ab4614bdf341e80abd4efc7d122f28787ca53ccec0011deaaa704ccd2c4215347cc34da525b4f937188dbdd137df599754d246648e0bc791d0cb215ed9e88f70246779b7db2985270a09812a0c40d4fb92522b8b2155ef3f5fd4678bbd58767fb7d7a5a7b39bbbc7be735d04f61887bdda5492d3a9d645ad176b47cea41a26b120bf9e2c9f79c5d0faa7a164d383852d3fb7ae5171d8eb4f77898f1c82d1a244a3e2aedf7932d4588f267d9b10b85fa06dcc4473a8ddf1614b88445a2aca1cf7e9e9706bd5aeb9b000a0434fc835404421a97a938dd017a82c1826410b590f457b284bc1fe4de550c692b20a31223dcaf1447f70528630b14a19b85d4fb48458f75ca34b478435fb83e13edd79c1eec1de17313c867bf4e00b61b56f633540552c6f0b9ed45230eeccc4921719c470768c45e48c209bc6fb6bd4c38afad91fdbe308fdb24a5df4b28d739902a4a7094286fc968438657e99b1d7b621f0c7affddaf1c1938f18db5ab59907114e60c06a24b861be315bd0d15f5292d0d9ef43215a895f00bdb1caef75b614839221d5445966ac0d030d9a4233b4156d8f0e83aa7860ac7967e167b21bbd7586e4965ac9e6a4e650b0d9aedb944d9862afffc270b5c6792c84761ff155047291402ebfb7d26b906b65c371e07a4a065cca6f8bb7f1d4183244c0c606bdc9ce633cded1cceb6347b3670433a942f2c18a877c536f775072677e25ba9cb2dfe0642d7cf8a9db7d628a69e15d4c15a28e034c8a3f734046b3d09c57e364b0ec4b2ad7bc36dda91f0ae51b0545a1a8efc67d5c13936358bfa6971c14239db16d21aef2944785371bf53c47669fa39a30ab98865b0b007c6eb1095dbedc267a820ba9e56cc3b9ef24f7a440ea970e0899b57ea5258c43b2b4dc02ae620ff65f0c48cf0c044bd330fe86e80089976599c3d91aca0a6a08f5f33df86c8e10abd7ecc1850b3e7fb7946f2dc763d6fb362db58a970cb7dd82e9aa6b81f0fc2f873a204bd35b6d1fb8bd43cb79cce8c6590e1c19e14154b2becb426cb685a6b0b98ef9e56167374979120fc4493a4275863c52bc41033175cf4e94aaa6944722114712cf0f6a571e31cd8144866aa2602879659a307b675f0e5793877b32b6d6db7a2db3058b1471cd270fd99ac88cfc09996f18dda4f9bd73d08cc72d39d8e54bbef4509a2f758cff232611f0474e336a6c42ef9779de2adf6c8361048a39cff98ac3eb37ffddfff2e68d8f1387678d839be716c4af8abc66012702f504edd87b227dfcd4dad71c1edc236beb3308d4a237c990697e194a0aed0f8ec71c140e90540bada5e1c19629d76126b2c858ab6370914598e68c71b026865df9d6043d2c591a2fafa927212b69666cf539a47b3906747d9feac5fc6e91621454b08c0fdb43ed0c0899268e16597986216fc83c79e1b266cad42b32d324a0fbfcd501ec044b6fd181f3e7142969996d0b7f93e6149955e0b9309fbc3f0a4d70a1ae6389c6688453b4c4f2a85e85bd0130ffc169b8583d694d565e58330d125b22d0d961c8638919991084f6d9e96fad1cd844977596160083fe7814ea6d4456389982b24a0ce6a39e5df2c53d1f7162c93d8335bad25b36898ead329848e9b6c5325d4a8583bb3ff00495d4f138f7733816b208edbcabf7a5543fa1764a27995129494895d02de29a10f12db1a7c950de8d31c2a86149c1e0396430a1658ca220a6755333f9178b21af295fcc8f5474e081d83e47975d77374b325f68d8e276672783ef168238e7683c8222e1aa601108ac7edf415980faa1e9f9820dba28d4403e2d78767564a3231241206ca6d12496f30314253b19a1a477e314a16dc8c0223b60ed823606d2a0638b19178a8ae690bdae60c403c76e46d8c97b1e010a485c9f062f0cc4b88deaeacde714e84306296eb837140a8785f9dc6acbb879de001f2517801d7e0ece131774fbb692932c2c92be824928adf23c24a68e7240bad554aa12a44fd5d4efed2d96b4766b4902bb57ec0afe0fab1a2ee770b191efe87395fee284ed376ca7aab68ab2370a6b52e49e10614dea151465a99fd761f89f31392da2d55ee5675918b0c1b0493a83970e6238965149d3bf6d3c16bc093863db6e293f31678f18073938584531608ed6e5a1b11c3b00228af7def4593494e9b00bdb3390c955554a6874267852b4b3d5eb5547fea55f2f77f868bae4ff4b505913147da4f4aa125540f128f9cc9b2e557583e0b1511cd3d8d1a96ad4b6c5d90629780f618e6503356159ea1121702eacddf6094079bef8e1c792cf95fa2822cc46909bc4e6c2c70d4c0b1075e4d9aeaca18d2422c1bc5033c84a2f2911dd956b7298c26f1f17f41b0ec0ffdfa1b8dbde474d647b59ea60903f00c33b44a6d190f388bea577c344c753b47766327b9f7bcdb80bbdfd5b309ec5ae66917998478a3c116f60c389e00b380f9b6a80ba3ab5982be8ccef39e30c396517050890d877c72f816e541d00a7d7c070bdda59408ffd23875ec2a0ab036e8bb7090b7c7e08e83d32924d33d4ead57010e4024df5fe4b02961ca44117c246cb442071c95f664ce7692e4be280d625adc9d3ff73b846a6bd0c8f73ae0a417ee7204433b5f92bda4c93902e46d6349945a86ce54c2b3b96f3ece4d505d971c0d73bae610f82c6f3d7d61a3fb468b8386299edf0f5c7ff3bd3766bcbbc8ab09195a66ec34c41c251ebe3460aa3c1eda404d33d5e910ca065b363b8e5de961ba3ba00d74dd0d5943f23b1c55d2a45e657e9c63833c2ac3e5e80591770503957c9e2ecf8dea6bd3d2c56515a273ec8994cfea9b2a9cc8c27b764d3d230c9af37f03d5ab0a57fec6cf4913418d66a8a92629d34183be83f700749106a22c193416030072c9cd52656c8a15dce2bd12ad638d1d9148861aa9df8aa1b57996af1bd945e52379a333d1702b3ee9bd5e73e993ea99ca998aef496b4b1d2297d329490ad0773e89f5ab38c9f31ebd7d136ddc87f4183374e182d6dc633f9f28de847ba364213c905f323dc221ee8f060e99fb33be1d767afdaf25b084b012850a0c6117e2ad7dc803806a5d2a983bbd3b2a23c0199cca25af2b66dd9f4780f617aed3e8fd8f1b9d73ac8f59415db5e97f14c204f710ca44e42139bd39eb3971e13e9de3f48772220b2f1cc517abe155643f8ddce3bf437dbd5184655cd8f737047e88301ce1587d069dd7f0521e476f29a9856def84c282c3f87103e0a51b9dc997cda9e24890a496869e1c444626470ddef2dadc6a5a2bada4ad2b7d2cfaa7d539c6c738ccf82df2b1627c28e9d729e49ab071459e1fdc2cff09cf47bb8a77c2d7375ba2ca98aac738f18a55b372b122eb19dc42d29aa356fe18e0ece19ad052083ead5f1a17e996a40ecad1a68e683fc9b419b27d8c00439a1448d8482e1029e6302b525070b7b67b4e7aaf3cc6b9ce703c04671c52c98689b35ffeba2aefbb6d6279cd0b6dcc88b9180d2a8e0a663e451ce733850a38dbb278ba4fe9482b2508d9eb164fd37ef25964e9ea7fbc8446b6c4346d51eb234b08cc5fb4e57d49f737b9b6119e051c1b0ba3d6c19c40ce64b68ac04fc9f8a97ef972ebaa0ccf017399af2e0815d518b8ecb4d19ed7db30e99dd62064b136f2d96e66edfc55fa20f1cb3cb70a12868ee1e5c17722654c15e74da7641728005b2a138e9f4b930668ab5163d05081f18290e766d24a4e34609d8b235a4d420dd1b54bb58d90e3b8f4682250b8f4f66c1ddd8dad6cd992f6bf1854a8bfc3a72207c95dcdaccbd2a971d28ff0cf690387235f7203a1ed4febc7e8a69e4e73b58bb264b7c010139df84c80f6c8b63dd277d0ecbcfd8ff93d66d66583d29f778b81aed1c11092868a1bc72e4634889b65040830362cd2773fd2efc38bc9457c8c75bbd110a079130f38d0057fba390e750dbdcaa18ab8fcc679664cfb0345eeda3b2f90f2655e8592b95426e1a91c2a00b1e91e1478d618c3a75343489e00789b311aafd4989291a0f95b05b6c642e42eceb6be82e3df95e1b1725db9c3dc5d4206686cf390f1875bcca454b5118277196222cdf04c5d8cb1c8a23f35844aa329ac10753d02dda2ec5f2b0b6283c4c91125f2b64c9ccc000ed29f756128551caf52368aeca6465afa203550640fad21c79714a3d0704a13e2bec7a89efb63c57664343a3192d43caa2f04e49ee2022e85bce5bd810358fbf90487e8d4011227c199a9c483066e4fb317dd8af46c6187e55b70762e63bdae226802d20f3ba3f6055e197465aefa6537acd2c0dc78c0aebbdb2d4e8b34ff00070e12ffca9748dfc6764482b1f0937080e4111de8b9a400e03590c3ba718e1fb7568d627b16bb52603751f80af05a79024ffeadcb24879fa013d7210ec6b5985e63ece346c5e7ce9713fc40bba189d7cff44e5e228a01952785593b31469b3269f483e48a30f1d0ef2ca75aa2fc2b4ccf56fbc90df8ea4cde65494e49aa01748521a6763926ac71b1ec8c26f66b06fbda6e8b43e43bb8368588bc61ed09c5efa72b309ba37076c0a0a4e1b04ae2fa4c2140eebbf19953639284b67cfe6ee3e0ef1c82f154431071650620dfef846d2bad030e6624b8392fb2e55cb192c43d2f0e373f7c694d1416a3350ad63f58858e5935823efb44ec484c5db24c452080a90cb24c7bc9ec20851b4c1ab90edee04e973809be5f748cef2f0758cf95ba89a71ba13d27f13f2f1db1885a56447fb79e260c56b8131905e9cc36ca676df6b4f7ed31b0f3f7413da720b83161994d5137459b0a6d6f2193ef3717cd14bd349f8c9c873fd594e7c03543139071053086089904502e0e9a41eb1f7f5daea68212ea0d6e9e42e04c0542ec969c7631ba49f3e443aa5c7ea96c40c60bc2b175abbed4e0ceca35945210f22d773b15009d9f5a616dd92946eda518b4ea9094c22157bbe71d4b0371c88e8e9dc0a79fcd14e4ca484f1eb7521c0aee2053d83f0b47864252ee1e983dc92bf798490fb7f2149d5733ce3db3fea3c905ceda983833cc07f319e20eadc69351407965adeb381ba45d80d414d21fdef32d062233d4b1cea905ce29676e99cc79dcc002e70d380e60f388d51acad0c5f778107c23e500d47371c9ca5e596ecd715ec4baf0d630a23a4153af80891a4e3b4cea2342969a125457135710796961fbec09a93075171ca2a98ce2391f02d5c4bbecb530930aa5671ff732d6d330d42eb82dba70d7bd5799b888026d53c5ab0a5d63c97ebe2ee2ac6a7bf3d85c8bb84d481306364d7401f76ed5e040500b462780616bfeb441ede9055bab0e410d2f676e68d346a4e5297b8d3ed0d239ab155728d5e67fe5d722154bc1cdc60f8597d7799987c0b1021a4c82c9f826b7a7e0586079f36d3c184be6796956aeeeafc66a0e99fedf0b6fb5de52c42de6fdc1e6d5c013a43ed71be613ff6fcfbb2164dd16d0c0765cfa9319d7fd0382792d3eaa2f2fe778e949d62a8a1c9aecdfdee82baaffa125b5522a0ed833d06b20aaed71f446628125747db601d03c7314140f8aa2005e64314f522ac6d7c82e36f2a6265467827e2935ed017e610229c13b575f42b2866658a6c36374b975135a37e9fbe7db4404ebf810ef7066f7c62ac1cd8c8ff3c15805370bf3510f2fbc87b34b4c557b11db1804422f332fccbecf0c39fe8571a2be6f35692121d960c8d8bd8b76e2b2430c9990b4f818fdd60b00337ca18d4138dabc61a40d575ef55510215882ab47bcaa44d5a993a16da446173d044a06e9511c83c2dcb63fe08db0127759b4cdb011cf1cc5d68f902cad88424948ab067009ac9bc3ebb62151e3b9a5ef92c99243f1d795d8407f7de18354da2b8c9b7349cda0487e7118927ff53b2dcc54a776222d502092cf93c12ca50910cae16bc8d57bd42b489d87865961a7f769bd325c4a068c36be9505e65500e453d827ec9b6effe16901479d78b74a816df89abc9ca93ce477c3af117fb5975bb11deaf0fec06cef16b86b68ca3f13f7a60b1e4929178d35f5e645def10791c560c8f064e3b8c3bc01ca74bd809ea5b74dc685b42cd0bc8b3994778cd912511cd0850069afa8d23162dc8609c82743ffcec06969408df0452437deb35614db518556999ffdaa311586cf7173c55ab77db58570168c273b829072819ba2c2ae355484aa6fbcdf5a553bcaaf183d194fd8c6ce51df58e0e7746272561ab3478b01145c5dd6a3746551e69a5cb75361dbd184a6fe3b65f92bbba6988cacf130407e37c99c6d57fc276eff14c40b3045862e63e63b5b9fe81f583f58d03d02ea1ba19c849d429aa79787a56ce6e7a86322535aff634a644d359889f0475f8e6b79800471304cbab32f0f3ebb733b325f5edb8da95df78ef6ceed92377486a88ec81b0c37257ca3cd9fc769f23088c3ba4cd3605f77ba308e97f7fa6766dd4603c7738c356e1d7b36f56d41f3c25a083e72dfd969a852fc190891a74214349fb69562ffb68b36aa231915512658c3ac9f2a2c1377712b50cbcbfe52403268c1f401a565a0641c7bb1ced2b6fdc09e4cc722ad83a687eb995598f8af59720d3fa01bc85b4833563f096a0caa683741f4cbfde885dd89fb7b20aa2d7024987bd188e5416825301d86123bc0ef39135c7a3f0e28dad8de98f4e02f0240554d25b5ca6bd76bb6225fa5a08e49244dc9988be5d1965fbef9e94495a6747298d7f33b9b8a84e2a4c9ef6af4b802acac18526987619e09ce86e900b64e83d96ddc3ff6cd375ffdc58c9f346cfe26d2f7b48563c37796df6593b274ffa3dc35a4dba545f20ecd6b2e4874bc55bcda23ef12f39c2d1e7ae8015193df44460618157465b60f6c1c2c0fa5046920504bdc10ed654508c4bee9e00bbd325f6d5deed02c32a53f3296e5654372cee007721f13b916e43657b06bfa66ea1eec0e2afdbe0707a064bb731f5e5802ce3900d8c0603d1eb5ca11bebcafa0bdfd3ce04ddae776707f916b27e8224557f806e17745ae9df26b413d83013ca511e96027fec11412de148d4b673ceb0c5df0d8812c13890d2e3cb10ebd460f9c620e1a354d58a36fa9dd6218ed939b02cb23cc04b087a7d4aa6d601d36f2e6e1a03e5f497c969e1744af136b51761eb3423f91c20b35b9f02bfe6d0fc2af37f68aa7a494d86118295b40236dddfa892dae337cdd2bf1cb1e085deee0d256d723d979fc74fbb7bfd8eb2a2eb4364ce95c63f5b742fb20ff7992cef6f2a22defb1f747959718a0269faea870ab64408df838c4c3edc618e18af691445f64fc382f84bee2f8b46b6285669de8a5178fcbeae44cde5affd52197f54c8ccfcc8468a53f1d66024a870b6c71664584f600e83029839cb8284bb774019a173665ed29f11cebaa0b387c074d6803c6e52bf083a5d2a0715f7f2d370baff7b35de4f753582daf8c247962b1c85ffad6c891ad7d2d0e22daea491500a4cd5a0cb525ac656598dc4c18f8e27eb90f719f8c3f6e7844d2a477767f7b869ff1045f0e6e938810d02a1bc4103d7d8cc204e075e65c47881b10398a570b50961b4cfdac2769c6ac3d388ed4cfacfe9587e5c4e209549684760b9f7d78afd8fcf98ec27b63885ffd7bd74a329919a57a104109562df538d05bdc067cb4ba46ea178ecef47babf30677fbf34d273537726702d37a0f97fae6d17443908af94a4ea457002e89d9d1888a254bba1eb5b364f4c80da30050484b15bb7935a06056143f32895225ac77119d25b735c1cf6d71a71f101a0e0856443170c834b344c18ddf5323e305d9db55f7ec5939c711334dce1d64f44787aac03a04cdbb0156729b40b64ee7e3e570a709b175cc5b8a7078bdf6257a4d434fc332df57ae191e7239899fd77cd598ec0fdf31460d4b49885a68c97a1933aede95cca5614d5e34c4cbd26c372d82f883ae3cea8a31789941f696edfbd6c7525db9858e0f82de4d019ab06c2c7d89a52db13867b6723966029b6dccc12ecdba90843093845bfd5b4a5d859788fd1d7ce6e28cbe9a5b40a8c3d508cccf6361512f47878637af8a7810fe61f306dbcfbf6bb88ea8d7f16d2ed48133c8600660207f4525fa236fd107983127afc36dc24bec5649912fe65fec13941415b07ec3ad859b05a695e7b2791caef5e49d0f508bcfe4783cc251d8e1af023c236c10456058439025a342962b3ea7e763610cdfc6b3a7c96a2b945cf1ff2cd2c835485bff97672439f2a9fcedfced9f106c32b09b59bc60703f6470087f62dac55e77ef457b53cf326127491e571f026c2f8cde2d3604c52e2a45832eb7647a5e2039d0d1f509834ec6e9d60e26ec5f37a2f7053fa29f1de51e47d130e5169de4fd461cfb4b76b8f8595c36b7bfaa07a0896a5b6f466a79f6c5b4974750c7762a8c2213ec2176ecf59ab0cc2effa63b113caf753d405f610b415435aaa3da48d75b2da2bc2d1dc3dc3c7e9a63e3cd6375da722ac5a73c4dbb05ae8fb602e85c67ae8d393b3ef70b5f8eda7eb93ae4aa0f824d540de5e91a2fbc4d25bd07d721e5e629084f71a04f1d67416b7d7185c53fe39dd436ceef9d01241b4cca952c3cb46bc2e88744d153f34c0b591fece10640a87211a2785892114c0f1daa26e722b7ee39cdcfc1c147f76efa76a07a855f3c3f0955ff9cd65fce8e24e9c973db5887632ae7afec037a50e17b02a799f2bff9685c3f5ffeee702e99c4dfc984bb58f51d1658103acee5873bf82cc3bd516e2e85dfd7d3d755988223f79699b9eb3125c3fa95ff5fed1500ed2b8b0cad571dde47fd5feac1b9f776d5c9414ed8d26ec04b2651e1e1f0b10e48119f9d59ce38dac16b6b3a80fed6fee2cc7e474400abc0e5875da4d4c40277101ff9cb3d6564ca8f6f6b5d4dbc93eb17f586283a6c2c33f036bbeae61e8008c62e7fadd92d151a50697c1f23c697d7a8ef38a1809ce5a69f39ba69f017272e7d34e2ad7b7620fa1baaa9e764079928bfcecd5c4af617d4ce317d562ea232a23b1b19d479d083cd57e87f55053c4120317557b4ccda8e3b19f815b119226dfd7865bd680644aaf2cf33a47da526a0e81c78c20910fcb669efe452401c53b57356afd1229afccbb99c9331148439c93c643d72b861838efc8adf50006e88cc8be415b64739d55786d83f641c97b685426640deb180c454e1372228c6fc6167e80e01777b975a22e3c48356b74ccbb399994a6f8e4fc32bd91d26d8f0e78c91d4c853c6b6ff35c18ed93a3b40b71789d0b045299b29f37828daf43d3d1ae705526f0c3d3b42083e847471f66de3cb018c3f50489aed65ea2dda7f8e2511eb6aa12388fe80b68d897db7277644888d8f7e84e39ff78e03bed918b727ad35269335e68555d822eab7c7b38639a4139645f8205fe88349326c8faf2e7737b74c5d60de2aebffbccbcbacbb58df7a5a74cdcbb5ef90aaf1eaafd862cc67f42dab73855011c043c78873f3af0216ce1b2b8032b5c23b0a43e3160f9f0e6e3e8cdc07457c6fa3628dd72b5a150cf3fead105bbe9b841bd72c9d801227a29e7fcb7288f12289c730bc2a1ee4a6878e56c9f4bdf2516239026b88d4a5ab0817411aed08b12dfadbc76e53ddbea7b615b9d87a4cfba4f9ce62f594aff1a30721dc374c2299f390fd4e3e9108bdc4281f85f068b8a832fa9bd64e712544b2315b42d81c3e335f8e5759ca5865a1b45ea466a2a7efa82d7f4d421960fa6c53b11215839e20d65ac47468cfb7fb1f333b25292d54f7842290e8ab22c8363d3212f0b0a165237294ab91c6f58bfc88300d1b0baecea2cf5babc3b6ef0f973a234ec80a7a2f5b559b7d91c99f82e45252e7a7b63f69cb0d278269bc523e16ef2d7dec10883f6a2220b1c849dc731d13abb920d59fa4bfdbb18d8a9abe71e2a31f3ee7175388785d1e44a69fee27a820e5e7785031a05333878183a4168480256406890125fed87ca77a1fab4ef6583b4cbecf1e7b2ff2b604e3860bb679a11e2ddec7f993abf2905e2ffd5ecd9b5711f0ee042f22240af2101b5e29e62985b1a2ac0c51c7cef44b62476410e3976b326db1fae13c4ab4b5f5fa6bc1e20d14d2c83943386dfc4040db1bec83fdf3e7ba7e1840ba74738355ef57a8aaa2712822247c9e3aff77bf59425458445e4a0a92d6992a7d0c22b97564f83ee6ca33931389186f759faa395b644086654bb9affa5301604f708e5f0af2441b269131eaa518572a2272d148d9ad32f56b5ace8dc96093c4def4d2a91c33998e0b18e09cdb1c2a228946655f69277224fbffe10881dc344261664ab005d0224308ad77b84c5e9e7ee7b71cc40cabc7a201086572bdbdb6d7a5b60fd90e4daf25778c7da75408336e93e00dd5f206a2e2de0c24284fc1b0a5ffebb36b766c8298ce231601677bd04476a86efe144fe6e580f904020ac9d228061f22136a093a343c3cd442a54428f0e345849345af104f6c0a83cb44f1bf76dda9ec758a4623a120612097c74aead060778dd39629b6c4d772116b48bbc5c7ed4986eec15465201aff25ec63332beaefbf9235e2162e9cccd5c9ad9a08e58c96d4075488d5e383086d58f1c2dc002ca94e09339fb901c3fd6cb747486cb37fe1f20041f9c6b2ee6cc746d28590a2bcd01fe04a3a801f081d6d00769255e584d7af5b1a043e4d700b7560d405557c3af67f1838427c01648847cb29d05c1693d5ea04f4ec3bffcd8fa938cd6de857a49193dea3a1050ecf5a0bb94213abce7934f31bf644900e9465cf865868edffa4ef92bdba5a16dfd48a997cc6c4b2b6c4d616b4d074c74e166dec166c9a4af99ed5692aa419485cd3d7953e18698e139ec7307b74d8b84c9f94d86fac73a5dcf407a2b6456f4a7a111001d3a48b06a881e15f171e6f7eaf519c813a9264a86dc2a7d7d45c702101366752bf26ac066cd33a337c65c76c93f4e1ad7fc9bb0f0ca43a83ed7cf35fe9ad932d7bbfd79340c3fd8d61f84b8ad4c2cb605086ed891c5fc3da0381183866a179fc955b0cf88f1460e620b492de69d53a151c8d44e35c3d65bdc527f1855f13b07d45bc49a48362a2d64512432c362492671ab74b91337fa6590b739483e3a3484a2c8eeffca75b0461628471c25da07ccd4db3909b85e5a20df09d85ac01fb545625dc9cbdf78806796b5a38e21b812851dbbecd92e3b3594c4bd8f39e59085cc642e6b5a1b2356ae15f4a00ff9cba9e7c3ec742e760e0e4520d9c3c014b677cf5aac74c1c9500b94b02a98a106d0e8357ecc2f2ab363ae0d8c270c9a543bb257f33469b02384bf1a4843890ab329943bb10c43aae35e5d358f97d5a2a8f2ab8c36b86dba276f9fa4e607b04afdcc308850fff9c3eb46bde66026c08c89dedc7b6d96e91bf5fb3eb550182b39d5c89ac7554bb8fda0037d29f89f9392a2fb35c35b50efac2064f9f540fa8b78307c3d7db0a0d292c602f23f873220bbc5a34c2d3b6dad603b50b0934c05a6db27e46c0f85ce7493d98bb7ac56b24c54e94675d5517a5c73616e178eb40d5833d1f021fa8baa4e86160f1bdc8ce0d0ca102dc2e36008b27493dc0436c97d6e2976212f229713a2d33ccbd13921ce0decc6adc61bd398c989b4b37208cca4d57c9e524bb6976d7042811595439f4d58a85d98a36dad3f4112aa44b13601d13e0e97de50d4922c7d6f3508147c93e620cccd4225c750a539c3e482707112e5f8a79a89980a3ac6f993f7926f7ea959970a7c94d8a3b11e52658019ca09c0e7d8a0aadc2f322cff6707a65bae011430323053e7d3807a01f0cf0a1dfe6b2641a0d3e2e63111e29805f95cc3475ef3115dde93d3b0933b2de91a73d8df686730cebc7942549476e81a42bbad9c89a9e21a23c975f09d64bbf2f92fa5db19f2945f723c5e6d825ddbbd6ad9b96dfd7d510f3092755dfc7b759d08f8c37aeb1ab8bd445af960d8296961397e063418a41c36275670e65e10332b7a43088218bd836d5d3b702575a052f31d340034f64a2941f261e537a311c2c56ef4fd539915c0068d7f1ad0145acb88af7daf1c6ff510b76a405644c84babd7931bf1c09855705f2985beab880d6a30bc9e29317e3321726d4e10220cc70747369c4cf71a2fb6fe4c4c8efae276959a4ab33cb909a1ab307e6639cc65641a55badf6eb734199213c440289a37a1d936d0ec9c870e7514c6eb1cc3130ac659382816f6dbfdcda1bacb3b0799a61feae3be4ee21dded12048e4d5766a3ad9237d2aa62cc636a761a28868d1be495206466d1a99266bf9ba226ece9e8484b8c5676f1e1cc2986284aebb60c94b19c531931e2d0c23f09a471755ea49d3d53c4e3e4125db8cfdd7a8a09ee1ef9a7b61db2fe122cdcdfa9b750dedc7c2f329b574ff42d0934dd58db76f6796fe59b7312545fad99b929f4e29f4adf3f5c68827b446a87e28398789b9c98125baecbfbfac1b74a0103bae63d9ada8bd72c8291c22432db0935301e7cc2cecde1ca47b27e80f8677238210fc86a624c34042b68e1589c71df730c42a3bdfe2ac27d7ea7d15ab1249d07c6829bd2a9bfe710f3df6a8621675d990677b2f3381fe1bb132cf352a36b557b608bbb0bc13adaa04eb7c196393fc09f5339afbdc647e8b6de66672fcf9a9983191de3ba96133dc3bc2cdfe9c73e3e8c8cf133f7f46e7c3f2adb80197dc272ec44f95176580b6eabf159b6cf45776244f3e96c8c64825a6eadbd29e3851ee3ae106dfb0ea304b973cd4a136ccbd70dec7a5a6234fba70b1ed1b5a676b78fa5e187d8ffdcdbf9ef083fbf5f0d1e07028fc955efee2d7d1fb0f94716cada0a610c98e68ccf903a4ef5ac467173e8e0771d39e1a2267898171d47365f2c2000e6d93930dcade2c56d3cbfc620cdc2fa9fd29b960c2aca0295fb176b1618cc8352a9b6f160f80d29dc29a3c1a5138d39c97c36c9f0b59a1a5769ca450cbc24067dd65000898b6ff3dc41b19bb9a17548f2c9cfe2b30e4c8b2a1eda017e22f884e5697aa8d89640f0ce10dabb4132c001baf860f070dd2581e1b286c89fa8eb84563483ce08372c6cf1310218027c28e2272e927f46be9c444dbf78e27846f97bece201152380c2bf774aaf6d50f68721f1044f3e7837d864233c4db90b40163171986b574b2b258070b6e8b1284763613d792c257a12f5ccba391b6e26d06e7c5ca7009ec4837cc0f98192a9875cde1ac1d229e59a765260e8fa4807629311d01c4fb0316ba857fe05f70ebf8e0f260177d9168c8c03ba732f4214178f0f8df92a3f0fa73962e6ed2525dcb49768f51dbb442768824163d6fc7ffb059a75f8b8ca49eaab4a2aa3e03bf66f4ffd91307d75c6f1b09b53b29f2f2773c4641e4323296414a83d773913d15f593cc07c0c1bc4abe486aec3a48c1de23164f2e229ecc48a24cf20581ec64dccef38491511f79c852ac2d5457bf003e89662a48cb5dd23871aefe21f155fc9ed8bf6fffb6c9e613f7289c68b3dca1b0ba294ff6b0b1aebfbb05fdd8a65424cbcab8e11736776ca3b3aabd27044f949b970509b732cdfd4c9419e22f18c266172026148d51f6cba006608e035fa1b5cd74ad4666e463c87ffd0d58c173d02c2c9da43bd751415925df6cc2ba6f68d1894cd38e44d079e7f596d3a42d135b5f700188dbefb8e25dd83441e75d385006d239978968ee3c0d183bf9a60efee98fb78d1a50347d084674944f78781216ce77ea4f52a0cf0aa6c264507f34cfc54517b4e07a13197e3c6438a96d76571c93f0e040591e4d1ffed0f1c2807d2138269246da0cfde522f38edf7b370e03fe1885db5d3539aeb0444a6176f4f24f9e4c482aae8fc4546870b12f1b9e418abb48c41e71528a4474f881496cfa32d968522ac9acfbf35d9104338a2b2ac0387d6f44677dcd5d3453f318d89ae0abf4c88a724eb73a803aad84b3dbb4a355ddd1eca51e3968df81ac779318bfa61585f3c38c30c026a08e033c06955f1e4b743b38466b6b7200f8c9252de85ca491888fb5d8195e7d11d8596e4b583a4e63b576443f446b9f46d8f5bb4949c756164730496240a54f85e416a5552c9a93f38b5929706e2903f52604077f9623bc7927081cb9722a66b81389462b8acd7781e10a9880b41a3882060036acc5a371ee26f7e29f71857d73a257ae7b90c09ededd1044da2dbb2576052ab6a51dc44f00a6982a7b38a061c59ffd588498bdcba05853faa5d50cf4d03960c9344be7fdf68b44b56f65328e90972c10f748a35251b4812bc1b904d68e9d055a6d913860a1e2ab4a0e87cdf0bb89c67893cb8203784e2771183a3da68a319a39e15347b13af0b8d0392dbce9dcb6903a4f25b359131cd9e956c935b0494966438932bbbcaed34b374730e0050b5ca3f1dd82e8db25ae72db57d744c1741f18b7377e17c6e9186b6c71a9d46313fcfaa0026b0fb62b601f708e860742e567df188f44c1597ad8ea2fb0060346cebbcd0387c1f17b4711abb03f3ae1afaf0f59df867a4fb5767efd9f8cfa9259dd29f5bbc90cd7873cc076ec1f6c3540bfeb9d723929c4aeefc02f265d40f2ab9c35692d6cb3ad954cc7021cdc2cf875084e3dc548db3a0c774dbeb2315f3a9aa3221c33923fbad00b692bb984f67a437dcc4802d6e93a801442f5a13234f487b20bc40c5edb0d280878305bfb8370dbd5b170adfbc6392bc396106b3920bec0d3a63e2c2033c9fc41cd79282087d46019bd0bb17bee52c833d998db6b18100a551887b080b3ed9442ba30af4382a5406abd472328602b9f5bfdb65ddc37b6eb419bea9d9d3756f8d384820af18ffa02e32de964e0a5d3b1b75fd048da067390b5fdffed94e5a53099495c3d5d728d05cff30658ab80eb752a323b4b3d1f06677e858183d0a433c031451d9401a9afbb0bfa563ecf11cd2922513b52f79f0852a07daa5fe6941863a300160bbe07cfc5ea23d438fad2e4aef262ff687dfc376e850290e472ff55ee8107d4e8466d68c8654c9b9fa35e1fbf31e1865c5d1a5ed887d13b32076d49b83099286c420ab69043b9db3ed1810d97d372869ccb53d4e0458170aa6376c4126be983e4f3211e049619299c475e2852ac42daf960112153a46dab256f426463c17629a436362b00088a953ce04566d900a2a0cb62634d9bc22a05c84791fe1af05493bfe59243d8024ca91d7b0be5440343dca336a71fe205e02b3ca39b5efa0979f162addf292e995229142f627437e5882d92fb9d541538fdf9cf9f5ca790272e12f691da14ff1c3b5a63d44f8e3bc9c693e68c0cde53ebcb17b9e30fe44f4866f60e23632bdc692ee9cd6a8e4e0a00db836e89acc0e44aee9197261569cbb3766c6c619d376df36fec7a77f083e6343de3121c4276efed0c43340559604083e7e68d21b07f1349483918cb0e8c651e1711406e492e567cbc09e3b8ff7eb25de7f14cd2e20d1aa2ed42eccf34610a64d2fe3fe11b723a8e36e3a95036bbbd5b8ca76bf44764f657add728d678a598299e53ca7736ab1c1a39fca0b72c3f40fd74c6739f5caabfa60afa45f463d5ed7fe4b88fd92bb5a411de657efa3312b001f2edff252677969e19f3b19fc41eaf9951efff7144160968b7334f93241b6074ca902f16d780a143c294ecf0017b86d03a9bca96822c93b9dd085038196fb82e81bb4393c175cb2a559c92545c48d346b5974816a67c3017688439f9c1c6d1263b7481eb83ac581a88005559d73ccf6f68382cc7800ebfb65fb879b4b0334235195ad3e7c9c3c7f45bec6d044c54b3cd50e8512ca2bf71c10888baf14f4223d7455ee945b431e23b01ae22af99b58422731fe1cc4dbfe75a36da9b44c0cd1082e941580fcc7b6df20cea445ec9d5c8ff0f318f03c0506fd330d5db6cfbba23c73d296a4e49ed8b9226b631b7e5d8fed080cf5139a1f8842d25c35c5a5c23e34329fee4e64a12d79ad01697ae68596979b1016bd13d6cd40803c147a5224d2cbffda67902751a154ea2dc10ebc20df068d429472e697b60b67ac023d8ce6723f457c78e93ef87be2421df03fcb841f8d2ad5a5bb9e2ccf9d3032537fc9db16e1b27df119aa30713ea5962850213164d32c4175e934c56c5a00613488375c4e39ae1176606bf79e1733750fcd8aff65983642ac60d1271a9c4365af7d48b59bdc1e062eabf4904e181646d49eaca52d071d65c4bc436d00a7b94237404953b8b444933c3c5ee9b4d0e18de4813b507a58ae86a037aa94e81f4e02b6b5e01972ee68d6a711c85dca653a5799247a10471d5b0fb48dffd0f3caf5c90201262df96240f0848c02f5ce82dea42eacaf96882c58dcb4999c35753b733d6009ac80e9e49f00cbe633b7f4249e947bfd5618130e150d292b12e36c6b3b3fe8a0b8159cb5a2f160e3ea9ec271daf9d3ce3f82055384b3bfa081b78486cb6846601341db24867505cebc9a508214e07167bcf6877ea901102ff99b0a21f58c92aab856ee4e5bd579a34b026cdc24d09807177580afc0f5d5c210ed795707c6101d119c5e7ca334b4b3d7e7cf664464994f7d44e74ebd8d1d6e0166e75cf61df8a3bfeb605b059bccc8b0ca65a20a6cd62f9c9017e62a24bbc887753fab15f5198b1318c4318b57ff4f6ef39f8c622fd3491829350fd765e3268684d40510eaf26bf68b3e1da64e26e60dc1f402dac64f6e3517ef51b37073c321db4fb956c14adcd5f5620d565dd8e7ec916dd14ba60cb51ce861c55b7d31892a79a8e0c42ca9c32e0bb82a239ec55ed3113aaecf5172492f65d26c135b977a4f5c3bb396c4a2c33355918da88d80b852c39614db94a9a3b95d4d7b16c813911f5710c38fafb709966baa9ab6b2da64b8e83ca4dffb33ef12e818405686cbf32597c04e114191fea64c5b398c0901a17d0d540cdb23df83ce0d94cb8d2c1f3a24df6af2d18776114d16cd48a60bb674fafc53bab6ba2f75e1325f3148ac83c0c91d02d02bf81f86fc01ce554e7126966a937cc7597ce63f86ec46cecedc21f1e08d3f7992d834505bd0f1f9ef2c2e03b063feac80bb57da9b4650ba5eff7759df73f074a0a5f3b54630b080448845e4a90df898c4ff53d3c02b91e5dfa4d67acf9f589cfba25d377407b79b49cf7c787da3caa93f42cba758a78a42797f387394a118f87d9efccf2c19be11653275633fa849eba096c8886c845fe5f922a2001add80ac2d6bf306b55a8b15db4d0c1b1c39ff25ee7b8407aef21772e783e3bff3f20be2f7441c6e1bf1dc012cae1873f57389863dd240c335372605a187f05f0b51dd69678d452714e151eeb2dce75c330186ea3e0c9eaa87c632218457c4cd1cf2316e70717d81cf5df97421f6d3a41cff1916b89dc4f50668c665ff0e873254b417eef95a88066b2b0a1f1a2801890b5a78e117d3ae1450981454ae09c7c8b8fbfd1c8f92f839bbfa2f89d816ac57d12c97a677d8dbdacb261310099b5c319b00c49ca4d73afae45c12e30dc4d9dd39f0e5c5c7aecb3df1b9db88a07577d01ef7cd9ba172eb0bbe82cc97b1dfafc8d216984c7e24c3bd6ff2eaa7f3003b4abb3b689b43c81d423a1d80468ce64115487d9fb35098cfc4fc6547664a582bfe94a28a0b97c97a839b7f49f4fa3aba2e6adf8e38de90e46a7ae1f7a791cbdb318777264646f27cce7f8fe9c0c73bbef402bfc7234238dee234bb1d1119828359a5665cc715b511f9aad8d3810a925fff520615a6f687d68e20a7bf56cade375f389b700adc6ded373abc4a979fd23f54663213c91026245d1d814a9bd63888e3c78d0a44d00f6fcd0837f651e3cb3760ba021e151cf9825bb0608a637bc47663e06bf29bc3cdcfcd8b0f1ddef13f684cb859cd9af580b17b0c4485e82202d43e92cf8ad3116ac168562507a57c0d5789e525dc108160f8f20c43bae08fdae24594b8351f0705162d86ab3ae44d511eb11052db378c42b1df8c22ba92315d217cbfb95188d58bccdd13d62deeab55def673caae5271cbe55f020fb7993320054d2b4d27ac59334efd384de0c953f08be8d05d437a831aeaeadb1cfd02d49371000a8af56253f53877b1b353163a020c3f84a68218cbb6c23150b6bdd57da061720e3b70d26efa31884a5b627aec0fed84cbeae4ef97cde04cb5a207a96b6dd9c7fdc06e54ffaa9f737306dc943173f3d601c58fbf0f6d49152e4a6f346b67487bf6665c3104efa6ef4830ac353a9ae0bd9bc3f38e96cb60dccc99ef8efa64d43418736e5a54304b9f54eaef8c625cd8f5d46dca98fa81178cbd12eb52a2b74f4c9993bcaca369af876fdf36f64c717b0fb0364f27f3fefec4d9e44fa666209d5e21f24bb1bea1ff16d4d776fb35f9b731d8a5a981f0cd0a413ca41fb8f77479faa48d542889368251969951ba302a8883b511d3b5e37dbf8d713e579e4dc836781d3b9ef11f707b708e7839720fbb0b96d48b57fae034fdc875bc5f6682fee2cb87cda41c214b60ee573e9240071bd34b6817729c7ece5122ed824131b1c5b3f25a879c3aaed700addccbba69b3cc1c830462733d7e2f489d511766038c2a30b982c46676153b1d7d6e2470c8b7e90748f9fec97379f5b4e37f9bd3444f7e9565246f3ebf72e2cc45166ec2b4afa3b94fbe0edbae7e6cc19c5eb13112f864a28da29760e7831779b4af8c9f0e44d6dbec2368c3d1e3fb6a7cb31e3c3246fca7a6671080f355fa489f2b7a1e9c9e9e9ef17b909b3b7573e3c089bfcfe43395f5e35ceb1bd5e99a1c7226db1edb4ab89e450c49a29ce64707b2edf45123baa59ca9f9f95988bc289e3b8f549617321f055105a6d94f6faaf2164517b99c001dc9a1504aee55acfacfe4848951e81af6adb30bcf3d80aadaf1f17337df4aba7560f0787fcbf823c9bf9bb8b761f16df481d6a36e574c28a5786c7acc41ad994eb74584188f57290d01c1db457e4f49ffdf56acec90fb95737722fdc70cbf6e286b37c985f63e04bc1786f2793a929fe4ba9b7344cd1f47a61bfe4b8419884a42084c2bc6adac46ffe216ea2cc2b487ecec9c9caa2c4cb35b5dbc3b29882886452736b6536e9e973ef4d4bfce1ea3229f9442034d148be8b1d00099b505b74cec877a6d39082da9a35b96982d5d2d894388b42d4ecd0cc66bb03daddef08ecdfe4fe53eda98d70ae0a236c2038ff39e4bbef85a79fd1a75dc4aec1527484d31abc09829951d01876d9c777abce7dba14e04df89b3da386516e674b95bbedc8a48b6276476dcdce623da8fffa36dc95bb6d22b621f017d544404265c8a9d81912a6ace036db7d5ec43347005beb8097a434662f5f56aef1a6c717a9d98fd0d4ff958a8bb2ec7517b715901d4a9e22c66b150451682f3f1f52743eed71290ad60b41daccec84abfad4971c5a0a91b2d39ffdd199cc32f16161ba7978f447360257ed3fa6f01fb61836dcf9ddefebaebf003890d54173a07a2dd1d9d28d1a8fe2fbb1936e63687ed46a6cceb1610cc57006e48b58db5ca9d1e6df1259098702b692f7bc04b1e357f78920405a181f95f726e017849b17939a7ec6f494f288fd003ac2f430612e4fe0fa86cdf358228fc0c9cef4cd07759a54721c673c14fc220aa51a3ef9943422c88e7919580f75b6eb4dad912cda9781cc3f549caecb61c387d2cbacbc842c13a8a6a3cfa8f16c86cbecd3fe9a238992301a8c42024690b88ab85da58a1d5ac5861a7d7bfe786f3b8c190b202c3893c2a1f8d53606984964fa073d6731a241e7729d37bf301b063674fde91b5007dabe42e897041081184c4026feec0313873514d6a218d6df6c6f09356ac950f237a65bbf48c47d73a038f210a1f15ef1214f4accc5fdbc8bf1449c390697b12a9e3d9b99db6621a356ea273d833057ac2c58ae0ded3678c378eba5e4885b4266441fe9f82e3c119ad0118bb940082701f7dc057ebd9385a42cf58cda71d1266ddfae9a1d2e185274c7cc8c006ab160a9cc5d8a77e263bd8d98703daa52ec1080cf71251f70cd91efb74ed4c439721d8d7b0504ce0e221f2c98f6bd60e3d5a3f002f91c7588b5d20aa8d9f7f6fe25680c3563279d903ac8a27f69de517bd162a07ea64dc775940d1594c7fb642f8e258c62eb9ab176a8760c1511e67df24267084f1165c137aca9150b5adb093275d9a5978904f79eb638219568df090c4167dd67cabadc2a432625d5032648b2d7a225dd36a031612326cd45cad626255d2e6b66dcaa4204f8521b54ff25a91d29e5b64bf1400fe25f98fdf1476e1275ca024f811947886ba212108f63403151e4f5e6484c3230868390c45833821890c36432197a253c51918d26e4a3af8659abe29b807034a2fea248ee37db7dc064ba3295e4b626021fa79a16f124e16affa3be43d057ff8042da38d59258e50db158bc553707ddef8fd270a0d8fb431992203e768a174c81b58f96c9a61cf8eab72544ba42bfb0be70b8e93c0ef131315b12d0893d3982dfef0df5e80ec8c231546eb1c066a830840c7237097f307b3bafc85cc1a4fc92491f601af9eb9c3ed07d767b92c98ec53ee9f0f0e15698c05d985d06d3c022ff0a8a8f17a9b14747da009c1838901b2ca8aa4938baa8c8b037e6644150d4410a0b5491bc3300c268688858a0aa6e8608e02b668392d8b1f5b631b8d9a8da5db4aae6ceb002fa27907dfba4d782bbe000f8aecdeb4339254de1dbac0bbbdfc5687fd24eb0f9e0cf40b27b08d139806494279d00c8aad3a5c9c401e9c402128d5e4fc4099aaea6bf5f12112f31eb922faac502f1a3834831084a8a2b63011e232e19c70e288609ffa2071299274d2d438695438693a3871429c38cb89af13f62389ed54d01f585c1c0d93054ffa0b4f883ad6c409fbe184c120a1301038065c86c704d3b448304d2440a1003b610938d69a9ba1288f50441c8250c3f2801ffcb0871611c5a2a1c415419024141e705ce1c58b66103d7898cf0c555b68261045753279608200ca8ce763c5264eac70e3c40a2a7cde43c50e1ef645d5858d2124e1a48a227cf1a3d0cb5dd9802811e599a128204a745205c70d921bd5733fb40849b2c4668a0cb02469faf0c614202a64bf9811816250088045057a51e5630a4754a1e9640a3948523592240d1f2449e3461b7b58430f92468d3c744213094a4c07e573324514275340713285025078509101314e9e473a799474f242d2c91b4992c60e92a4a983544d219d48b106a90627526c7122451927527c21492c28410a20e210070d1d2449330749d2c8210e1faa23d2c0419224cd1b4033f422499a34dc20aaa82d4e30c0058d1b346d90240d1b2449b3068d1a502a7081345688b2271710802445218828d488020c2751f4800349664c9219731285e8240a21ede1c0c49e1c1440f9745072447182b206499242a00f553f1d29c78989154e50c43841f1e2048a1440bcc1861a7690431a6b38031a50cc2a34d5cf48e53d749e40f14ea0d88024492b404149018a67164922d1605f860a8528fa441c9e08c2133778c206663c918227a82772fc139a930a2441b20f060a16d5dce20b97203980f00372949828c2119a3224a20f6a98d0c1640e6e300943124ca660c203144c000092244d5fd8cf7f811213fa509dd9f2269c1309404992649c44401392f41272c14904825085a659759470128129aa1308b4a00a4d473e238ee8c867347d94884e1eb0054962142b8ed455f93839e18124893e584c4e12dbd9545888a88f2864ff379bca332b556931252b40312314847c3c68e6658a4fd5794bb180a80f16270db0815485a62433e68ad057f652568846d40903129172c2800e9c28208d0cfc60499258208303b420018628590423dc2841a3840c2709f080a4714333831304a821499f190d19d080a20331384180002449bac401ec19064822499a31942000094728421124c90c498a410c66408624955146a8c7013a1d901a450a94cf7bda09013c484a1620090e5042ba42152e70841f42c8010ea42152910e50e064001ac9ce0fb91b09400492d448363c78542702c81180499c04000e2a50785c8a0a7d4c47b4b91fdaf122430101557054bf48d501f9a83aa0e901c5740010872405917a9c00a0049274292af4c412de529f138d0f4e341e49a2b6902aeb99eff2a12133f43c78d4ce0cc9d65a6badb9bbbbbbbb33c618638c31d6ddddddddcdccccccccbc7af5ead5ab57af5ebd7a758c31c618638c104208218410bef7de7befbde79c73ce39e75c6badb5d65a6beeeeeeeeee8c31c618638c757777777733333333afb5d65a6badc531c618638c31420821841042f8de7befbdf79e73ce39e79c73adb5d65a6badb9bbbbbbbb33c618638c31d6ddddddddcdcccccccc8b237cae396be62266451a01269a2049d20cfd15b5633f44a8d01b99f9a105c5224379fe878609a1e78844d4e7a3c1424b45b1549a2b4892c60a92a4a98267cad0cf4c21148b2469a820499a29485290a459d92a2515a0cfca8746a2d1ed4c8a220a7528caf4f18caaa9fa0a0b4aec9024cd1992a4818224699e20e9a084e40449d2304192344b508224699220491a244892e60846308134b2e2b2d1426ad1051016b969e9a2a5456ea410225a74c1c5c60b1f5cf8f08c2cc5051cf58879d10a0ce5e901472bffe9c07c241049b44092248d0f7a603731a3ea3d33121c2a764892541201a9e40c48a8a00a4d3374535513a49141621f0c142c449ff9e9c4744231a3cf3f220d49b222029a91e4197926b505cca7230a51d407ca147a84171a2f44204992a429a221f2224992678aa68a23b4901a8105fba11e709445fd2226f0e69c113ad0c1082f6c872ab2f220a8cdb8a84494a772375a841792f474fe506929623f3e66423386f2c82402119224c95c217ff8f8214af2a32e3e2b2a2d459ece1f9588f2b8d8a7c2dd08a50a77a31df552d40d21516929b2b92124a1078d2a44b8808ba8b414516929f2e94ccf10974a543dcc10106856a3ea7308151068f2e0b1015552891f2428c40089219214620cb09988008c20499209241d847843920a1137f693746e10e2708424491e8f6322a11fc283c7065411c223499214253ae288e30d499274c43102a90b38dad0a1b079535d5143d515b523fa1836723c8223d83c723772d781636510660091248d073a20499e5048564d0f0ef0a7f2418940332120a24f871a8281900f04310641481a0ae808440fa27080243d118427cc084403a44a93058fd04bd20c208af007454892540249072056159a38a209ba62468485864715fa4e0b0a473441281e8f0f1e3c74fc21057e8843871f5c241d6fbc21bd91224992157cc8826bbd850034259274828d1b3d9024c944d2c1072aa4246b4dcdd3b2c20ae7064842e181d2c5152b6e0c91dc409124e93d9d3b61664c1b9a9024e943a40d38d440d1b186019ad0860aa436bc90daf000cc075aa1f242c48a0fc582d286244928f6d339c286212410c5c61924141e5c8409483ad84832a4191b3b24c91dca870d014892f459d983225078d88e1c244972d1b18726ec218924edd88310499276a07cdd438a24ed40d1b1870668828a1d6bb081c223f4301d6ac5435923079224893e6b8024141e6b7821556baccaae610022920e3dbc21e9d0c31f241d7a1084a4430f71d4cf4865e6e38362599124cd8e25499a2ba4c98a07cdbc4892a60a49d240f11e3ac3e854df22499a0d5492a4d140cc7bfe41332f92a4a16214b3a93a92a4d93185243d6846f4a099299a97925334b5d0a1468a068a0c4892a491423445530b22208a054451422449937257bef248fb91a11f7725f443923418780f9d2fa11f9fd04824aa1f0251212d5c8288dec5332b25e4ff25886752ca1344f42e2a2a2e10ba40e842c4b10b9166765242dcada8a8c0662bd0b1bb15cd05242e64a8501049d24481b222491a2844d547923416a88024aac20043365b7916214244414441840869b6e242e483345b09030ce96ee5598408110511051122c4dd8a0b910fe26e050cd94c4524a4998a2848339517d24ce58334530143ba5311e24e4514c49dca0b71a7f241dca984e101d2ac59b39595951517222b2e4462ecc747b3955017cd56429417cd563c2ecd563ccd56c2f00071b712860888bb9521ee565656565c88acb81089b11f1fee56425db85b09515eb85bf1b81009715cdcad784222aac5dd4a457d346bd6ac994ab366cd9a356bd6ac59457db85381f1741e883b9510457954dca9a888aa0fcd6771a7220a81e1e24ec55a1fee54e8c8c59d8a67c4853b15241d2ddca97c3a5cb8534932b970a7f281e9543320d00c89a059339715222e2a445cdca910a99f51e8c5dd8a8a16cd549aadb8ac107171b74204c810772acd5456dca9ac346ba6e26e45f421d0b7b85301b284396041b21398c3169224e99803cf410159a0a0f0f80284c24556111ff6635eb412a2427280220e3fe320461cd6e4c0a18283186cb3111c5400872924a99a28554c33ffe30d492469d38353cd2d50aa8992451a37e022312fea4892460211903e8c4e15f3a24eb3d187d1a9dc504992c4bc38c2e79ab3221f1fffa1649e45a5ba1f1ae2a1286abf873b2af4617ca8cf045513e4c28621fc70598321d63004697a149967594305663649dcadd84fd2510951a23aafa53aa1ca7efed38179eae97c0d5194e77244309f0f813af55b5ee4121a0199992c3fe3230d6974a1230d4310511e978fe9bcd84f0704f6d339928646431c68d041094494a7aac27e3a47485082890a2beca7736487fd748e5c6ac60382cfcad70972b1ff79501792a46180660167c8e20c28d2190a80c22326c60c1f141e9beac87cd006049a3c78446ead435519a620bda8806086783a33468aa84e0b7d117826cc0b7911757eb8d64766ad0f02d77a725c5c6b4ae6a599cc6c6126335b1693992db165660b6c99d9f25a66b6b896992dad65668bb7cc6c612d335bba6566cb6a99d9125966b64096992d8f65668b6399d9d25866b638cbcc16c632b3a55966b630cbcc9628335ba0cc6c7932b3c5c9cc9626335b5c66b63099d9d232b3856566cb929946c8e0802a34a181051d687840071a5248d218a820d98f1955a339a4fa8e8e31144092c4700669b262d8c124aa3a42445f8961b2341b8188e488e34554910f513294108a7aa97218a21092247dba683602c30a2429471c3e3e44e0a83e3464109294431081908e544a8865f91025aa428fc443b9f0e0f13c4413a5d90845542728e66d17782049d28b2824ae39eb242ce43f140b17c2f080debe67f36174aaea61266853553d9a8d389b23758ab6108298da114d198f6756fb9bcd666dd6a68baccdda7091ea5b3c1e1fb5ea70f1af22aa3a9ed07790c0cc1f1f335d3e2b228a85a5d9880a7d7079b11d205c641569c6a92687534d4e911e3da0cba2191177a3b754eda1630a389024984f67c6e3f141b550a224a5240a0a40a0128ad8c2195638a20a4d1faa138514e83843ea3863c8342945015285263aaa2469650a1d5090c3ed702a8903137540c1495568ba42f4e98c603a54f4e9c074a81531a34f35d7c44850b8d0c113b220bda07c3592410e9327c427a048f6613e2820aa330249059c8d740227882449ea694d8c44fee8a2e5c5e58b12a9e48c922a343da542a2aaadf8743ca18f59430f00804d384392a42fde43911ce984ac98f9238f279f9615309427871a9294230f9294030f928e2688710749929078286a088b085a26912042beaa7c803a2f9d992454a80865472e40aa17899ece1f445cb208514078f02080ed504578f0d86c389b0e686e4055d58335379bf6139a1155dc3df8916c1c3333f3e2089f6bce9ab5c86233b3983fb2783a29f5928252cd2c8aa05401820d920f81cab084244d2865a0a1a38c468624c870021949282af428a03ac880128a8e17c02149d214eabca0063a5e9044aa4414cba4e3053b2617a4f1a1444f47d7053990241d2ef8a2ba940be01896f850a11f238d313e44cd9719832349938e31c650c0141fdb411103109224e910430b301f2a242302205d7c18a11e3d7af4e8228b2c92503f363233546d92cc98169ca0058f0570b0e0062c60010ba658011e56f082158460053b2469a258a62355473402cd78aa98993228339f593b9f09f2783a321fd3114d199499cf7c99a8a876a06840031ad08087a240339ece8ce7fe87faa8600d1590a1024b89503ed6043c787c353943be2001ca4b0a38a1230569e8484111244a3483f29c2f5270002fe808630961804047180f987184247d159b8dcb0c0f663a90a410497ea023892889053e9d19125d41a1cc7c6638a2102523aa461765a68a992f3ef6333955f8c8a272a95ca8f8fc40a9c2435174478f1e2f59802811109922320298bef8e20b2aaa0051a21d4190b071a49a2324399024c98a1085c40b898241b29084466c48f3c7080cd38fd18f0020ea822846644575081d151a3c78d8f6565e2e4ee765bbf282992f5d94b8415af66e2fd6eae8fd67bf0d92e3747ce3a48fba4b091b9e2ffa6ebb3e1f7df72d1494ac414edb8eb60b19fab58e3afbe98000aa213216a35b2e3277ee3984ce756cb7b9da1546cb6ea64136aeac513bafc7dbde9a25689077756dbc286d4a2b7d7b06591fba7a6ba3fed331ea7ca5c40c3246e896ede6aa776dff6590933ebba6d768db65bf22192475d335b3e5a8bbf442ca7930f3659a60a6689a426f210b89406848469fad85cf698d77353a06c990c51bdfd106a97b3dc52097d7ee56effae6ebdf711e83f11b516d292f4152c0901fd37999a6f788e8a89aa669822c4636a39230c4e55c8bafdd5b33d36b9bf2b2b539eec7791ca2904ccfddc0743c9e779da4040cf241cadcf47adb85d0310a5d48c917647d4799473a632fbab8398fdfae1620912991e843a2aa138a2f255e90eddafa59bf1bc736db7bbec3f248a8d09069e2caf3968ad9346fa2a40b925dbfdd26b79f9cc7d64e6b7d7c3ad41039d97f420917e4b3f6b167df5e6f8c31dc826cef3d76ee55e8d6c6398a619e1117d304f150a205d92ab5f1d5d78feb43879cc7475a8602b52c48b7de366ef4beea4d9f711e77362130aa9ea67783122cc8cb6ea9f37a70c549a95322c8cd239d04255790cbdb9db596ddc62eab97f3787137e86ec4dcf3a2c40a72b6af715a5b5fb3ed68398f8fd409f2364dd3e46e344d9ed9097d354df6f347eaa4d3e4af648c922a48f8d5c6d870d6f88c4de63c7671a1e999b57247de33ed23098daa6316239b1e255490f1ba696f2fd666b3ed6f0ab2b276bfa953c6d7be8342a6490af2d2bb608d6f756bebc209af88484854b96331b231414914e4ebafb459c7d67dcbb4e40c16978d913176b4a785c2bc7632abed1032af469f10695bf79cb9eb6ea55de204595d43b69e9b9fb6d6604913a4dfc996564ae9b3bcde723762825cf37fbdb858ec5fffbe53b20439e17458bfbfceebbeee4809d22d8495ff45b7d6b976dc664912bee37af146e670f1bd8ff3d8dd03c574181224c7492b638f526699d796e4b708bd0c25a44f507204d99885d727bd9479d53a61e747094a8c2027bbed59c7f9f3fe6a91c8344d53cce8e3588c6c684911e45bfd1fd9b6ae0d3ad8bc8408d2b9e172d4dac7b859ee583204e9b8b1061f5dd5edba0d398f65fec342ef1212b930bf90a862254290d7d5fa2bae7819df662fe731bca251ac60485431561204c9ec91ef751c2be5c83c66f379cfe31910559b6da3949714da4629b1bf0408727d5df4daf92cbdb1236d404a7e20fb56a66d5fa41432ffd91e28f181bc77ce06a9e3c990d1c8af07f25f84cdb117df9a3c5db49ff71f253c90f72d7d1effc5d0260629d981ac4fd9eb59613773bbfc79cfa69b16253a90be7c460a99df3d2f6e939303c9d6d25ae9bd1cdd2e4a398f1d8744150ea4c3e8d0b6bab8365a5be3bc4dd5698e253790ce46c673c567c8ec98721ebb8ff9c6cd6fa9daf98cde074a6c2029f5eacbb1b3175a873124aacc90bf9cd6d6e0bbccbdd8d44351748b921a48e75864af51fad1b1edd53490aed93aed5ccb456b9b35b72e99818ccdadb9536e0ed7bc8bf3986d363023fad6b50c24bb9fd45b5cafad5d6c82d854120339db71f7e2777456e8cd79dc2a2530906fc61b9fc2f916b56b3dcee38ab2ce2d24aa5c4919d25a16abab75fd5defee9221636d7d2d6d2cd2551fbf921748c7b5f65ab0a3b3b3d92e71819cf6db3facf6ff17f7978c21ef7b5debfdcb1c63daae084ac490b741d89aba836f396cc779ec992198177d3a1b3b41cda1174551cc65b005eeb11dadbe2a33ea9cf318368f36a2980b250b2a8bcb276d934d7addfc7436ee461be798d18709598163754eafecf69d3777cee3e6a202e91c8cccdd9eb35ddb6a5d2990acadcfe82cf79b963dc679dc5c98a128e6b22cc2907efd51fbaca3fdda99398fed6f9c5d8b3392521629dbd6377ab3d4f9b073c83455143a16231b9824325af85c7de83ad6c9e064ec3e44d59691357e7bee2de60b23a32e3616231b2f9044760eeb7af4d535db659b91640fe16af5c5791bf2f5888548c6e5ffdc8496357bec17e77105b9923ca17b6cebf5ca666dcf79bc61dc42f2fdc256237deb9cd1c89cc7ad0a3024f7a3b76f8490b9b6064110bae7acdab5ea4706d9a4a4f78c7f67846ed95bb582623a8c7e248c77bdbf8c3efe5be9c7c8e6f7da2ef66f6aafb7158f7431b2b5b05d643c672fea7e566ae733874cd36331b2b9472473ccbafb4e069945c6603e9d2371a140b63867b7566db4b42ba48c29d71df93d59c3aebf5cfcd79a3916239b1270e4336ef7b95557a3d72d384d1ecfd769fa108c74b6eb55f6eb32e89e3ae7b1ed6c1e34237a1f1fd334f3998cc5c8465ee97445ff45ddd1e66c9df3b889626e065e72858f3257e383d3195bcd5609acacf7fa7adc7ad2ebd3b2247605e6335ba6e9612a10b5e1c76264632b7b6c113ad8cd1d8b8cdb6f33cbef4e4ae36ce6cb79fc222425f4a29569fa109069b2df0151759aaad08b40d3cea9178744a0122550099fd97b778deef176fd50f1eba78b8f8a831588e231253f7b93a17b945d74479d6331b2d97cc19c37165fec059941e8fe9c57637451e775b1a61fd98c3b8b914ddd480b1985cfc2a60e42fb28e7f14c4804ab1af2e1550d4d8f2c46367e02c79039e6e6cd7163e6a037c3d8683b1bf95deaed1c13c86fabbbfda573f27a7e4b20ff3d3285b63d7576d049201bd7faec3ae8aab769dd76a88d6331b2198111f9e89cacad5a997afbdb11c8f8b6314abb1d737a1715e285f479eb57766f74ccb4af08e4aaf7c5c87e9bb1d58ebdb4b318d904292299766cecececb3aebd4d53880a857e9adc6331b2e91071ad57ece7e05b8b6133638fddebf2e833c2c7cc3072de344d93f3900884821779a3d3396375adc6089d398fab23f3411bd1c344b7d9bce748e86336f4486dcd626403c4455ac8de7270dd672b6cdb388f31d022adc717fd3686cfb5785d0824e3d5b6a1bdb4bae7f035f3907f0f176d96d77cf53b86a00bdb5e7cddacae56af375b4bb5b02c06eb738b42beacc245570b1792553759b3fe8f596ad9e43c4ef5d80979104e7eeb7174cbc276efa113c4ed0202796773b4f536befdde6d9cc7202a64797d405e861fbb317e0621dfc979fc1fca5337cdbc22df3adae2bbfcdef557e63c863fb6801c39bef5da41f61673b39b6290783c9364bb38cee66efddddb9585d1a94621ea7a40319d6e31a30f13c55c9920b2ae8efcacdde9566bffeea53651888a7c11bac3d791c2f51cab52327b993a870dc6bf6d4db7a285b4d32b64ebae5b61b376711e6f18b3e3fa48581740e46448adbdbed8f3d3d99cc79b66278ab9ccfd903e9f8b753ddb45db5f1585a0077cc8666f3df36963a4efadc579bc79d003d21d3ebbfedad93e1d759cc7905f072483b3ced5ebde1827e4c9792c438560b87bc837eb73cbe6536eb0c5cb791c12551c90ebbbf56dcdb173cc59e43c16c5dc48240b79dd5cded0b98eedfa659cc71bfb991bca72bf116021a7d7472b37eb687b66c879cca946a04d88ba1be7230548ca4bca8b9094fa45ea23d9c8ccd06b2c46364278c87fb345c8a8e5efb72ce43c0e4d4eddac491473a7e9435415816814b3598dc5c846045142b771d186937a3be793f3e02845a52525e54548cae6d3791d1281d887a8ea2c46361c80b23e6be37d675ef9bab9388f43ff79922e08e7746fb5c5ed59ca795cd9ff6a237a98c6292f41aa99b27a94e2324d33e54548cac6238b914d890c5c6ba0d9d780f3142c02dd14159200531a5812b8c2025304aca04015bc6303ddb91f025d400313a082023ba498428a0c5c408a2b1aa5f0e6391b0a60c05d052e9084921159200a0ba040e15a5b80613307a502cce3e91c5914986202f3433b2430f347aa88c01410d850f10051688aa638690ce880e954513880ab993fb2d9d1002918d03154dd6cf8886b0daa5fbd056c7628000309d86ca440c026c98c81e20030946703f3e9c0c0509e081820c60205e0cd87ee2644892605083000dec44c118a00a80880fdaf36170040ca13950666478e8dfdcc8d9bf0460325bca93a9b8d28c96340f249b0195028f998f9d1b0197d304001a4365ee0081fd20e7490031cdcc00666d480063390410c605006192f70c11862b480052b50410ac298495285c000519f18cf1114743830f76da5f38bcd094c500212181981a4a30966483a9aa0891c602880d4861860f02179200090e8801d24a96a44079248928402820e3c213d128a7aa8da630d3d9a207a981e64f4b05af448e9e1000e48e2840376c082c4811c70400c69118a901c6892cce2115920e2d3a158baf359c902491643b2a0408924652161f1862461e1052ccec02208120a0f2c7220cd7c7c4892261188a82e92260c27583880872624942a78acc1c30c28222a08ca34adf048398421a4dae1804254cc8c87026d409468f347664c54c34934038507caa72324824ea2094ea2072449a26207ca099aa2a2024ea02002af979d8bcdb7f84d19c8da8b5a56d946763dd218c8c9f8dde7bd60b35f87815cad39cea6abf172ccbe0c19977bfc98838d23fc66c9901de17d089d6defb5f802c9eebbceefcdf68db2ea02492b6c18977d7863f71c4376743a2b74d4325ff7e51043daf6bede8790a3b3d16b27a475b4c2656f9bbcba3527248bf1cec88d2dbe90996e42b2c5fe99efbba76fba2664e4ebe65cb523a3f4ae9909c9973e65b7b7c18e7e3121ddaf576b74d3c2d96d5e423ef7cbfdb3eff64dee6809c9ff0c5a0717f51befad847c37ce76935aafedce2125249d8e272fea209b4cdf2464d759f9dfa22daefa4d1292f6578ed6c5b6bf6024e45d8db2e59a29bbafad07095923bceece603b65661f21a765cf7fbeb8e0a5d61192d5be7fd9e4c9b339d808f92cb3f1da767667d76b8c90d1bd3dda8f7b3d48d91721fd397be7cf672b5f5a1521dd178dabf93367b3c144c8cacdaeb5ec22651e9b2142b69bb536b798ad6ca33d8464af56dbcc3c9b568786906d1d5aefe6f6d9dbba101246d6737e74e8cd1242b2c81ddfe30b59838b3b0ef9b87937bef7d1d5ecc22163bc4ddfed7d2f3e1d846cc8b7c5b6e074ccad1784a4cf5ebfceceedeae61c0819db9b2c5e7ee88dad0a0849ed83af419e75c23be31f24f3361f7f74b4b9c86d711ea7bc04892c46362c30f1839cedb2f5b1ad7b591fa4c74b1bad9332b6be46ee37e46d9451f73042e66ed15a01f11d98f0c1dfd1775a1983f4d5673898b821979b96dee66e7d5f5d534f790992c20d0993362adf5ad9fe7dea1a3664c3ba76ddb9afbb6e03fb0c267b90ec8cd558638bd0aba56bc89f8b3e9cf3d2ba685d1c4df420dbc15f347a9bafad852f4a1335e465bcaaebc7deadea6e1cd20224c5ae4cd3102129ce358b918dc919267990af59e85875f6dea06d95f3382482292f4152aa0f202922aac8e763545a5266422faca182091e24eb55fbf1a4eed141fa9cc77e0749996de62284ce1ca59529133b48bed4ad66f0ef7ae89cd641ceaecc7e2f76fb68ad7490ef6e4f7e6ea616421add03267390fd60843edf84d6a77b4b0ed2a34fe66b9f3d52bf1e0709d74fdadc2f16ffd566e120e36cc6ccdab61a69ac363233b4ba0b9337488f90b6f8e6ab2f360b997f98a421edacbce663c691d947a90b17dd5b98b841faabd1328b3e277bf33658039336c858a3ffa38dcdd896b187619b206c901056c8bcdc5b5edb83eb6e8ed4190292029b47292f42a6a95a839cd5a37dcf986b0fdbbd1640305183e479eb6c0f2bb5d6d1c7d2209bbad66ebb471a29f5478364d5e37acc395a5985ebb9c919e4740cb6eb7a5adbbaf96e4ccc205fa5b6b93fc75665ee1e91212665903c394e37a9b3962f7ded8c98904132c71afb572dfcf518da24d48f69b29f84a2f0814cd0e0dbeeaf1fd20b6f739cc7ac81613206d7fcd88b8d357d7ecc793c93840a8136949da6bbb1764e5367865e9849224cc4305fb5b45f5bac3de77d6854519e3a4d53e83d9e182aa4b21e17c2240cef3a5e70ae581fb4d13d189c7d733ab53d2b6cfef00b72b1757d7673fd66a5de9cc72ee525488aa7a33245c1c40bdc5c65cdd256a985b1b92e3c1bdbd23abd4dca1ce33c4eb16f9412d3e154139214fa4629cb45265cf8371facb71db37651cb792c1ac540d1888b8f4adb026c6ef56aedb9c6dd1a64fce69bd746e79abb4e3d9e59fda3d22030d1826ccb1a3ac8fc9f7d6fb61504932c4867eae6643a2b6d079b731eb766662c46362e265890cdba069d4dcbcf98f6e43c66579071c2bb38be7bef627f3d04132bc8ffa737fae4e8f7f97cce635705f911367ae15f375d7beeaad935c82c46362a26549073567693f2c3087f7af4782610b8e13805792dc765dd84cdcd27749cc76b39becdb118d9a4984801f2eaf62284efc1d51a3bf77dcf2d8fb53637635b6d1205e96e8b7771bfb7cbac3e108b918d8cc91972b1e5a08dfe1e3e9bd78251b90f1328c8cb3f5b651446fec93a4ea59f201d65dc3d2f53187d42a6448e7a6113274886f6e73bfb8cb5fa2ae53c8636860aa938c1a40992bdcba67dac27b747df5db498a669f2314dffe9dc3b231bc184097245a78b31857cdfb4fe388f3715ce783cf34e936b7d642d41da67b638ce18e37cd52fe7b112a43b07aff3ba2abc0bc69504e93f9fddc5fc39caacd6161324c8f962534b5dacd4f9b109f31f47c4e408b24e678f56efbecdb6d08701999a1841b2b35add63336be6fa721e7f189de8854911e49dbf9a6bcfd1c5a87d8b08d2fdb2d141fa66753152e63ceec7ae932cde5c5eec5a1f7116231b6b3204e9bc5ccfe9edf2377bbd0c132148e766ff73778cf673b03d4803932048369fb2d71c8b6d326ceeabc919f2363c630204f99cbdaf6db40d726bb5b24cd334b118d9844c7e20abfbaf7eb698e3846f721e3b2f9a890fa45b3a9973ae9741d87f7b205b7cd545fb2e65d6c1f64c30e181b4363ed61ada7676f19dc90ee47aba28eb782174d7ec7520dd9dd3b665cf368ff152931c48777b2783eb31bb22e3e7e2c28184ce5f63ef39dbeab3fb8603931b48fad839d28ebe9ef5d9e00e4c6c20eb8c2ebef9dcc15ae77aedb118d9704ccc90efbeb7dada0bdfe4781b6e4c6a20df3167a753da9c451b8398d04052d88cddd2599ff31856cdae99c5c80688c90c6074ce9c6b7f10765cabad685babf1aee5fe276b9cc7d3544d5343622203596f7d75dd8ff73d378c233089819c7f9bf9c6c9e8746c2d0c644fc60ee374ad52669d97d1984318dd45778e5f5bcc316ef1cdf687acb137a7bb266448baeec3ba38b63ae37bd85108262f901c63cfc50dd6875cdda10f4c5c202f75edbbe7bafda86537fec0640c392b6dd4b67737da7cb69939649a360c6d6022863be7b55c73ed357c9039b8daba8d5aa72f42cee3fa5564b5a413322ee7b35576ca9abdbf9c57411e259c90ce36666e578d1f9aa3209e394dd32446c926243f1a1bf2b51f1dfb47ab91688aa6695abce15a7562dc394d48ea169deeba7b4e48d933212fbdb1cec66e7d7ed15ac79890942dac7fffc218ed9b7665c3979014d2e50e2ea74e7b5d670969af853e9d9dcdbe52d69590df78be77a947cbeebc8b12f2c50b639d8bd955198d91eb570d48ca8b9094990701e4c59bda11813a1ecffb24e4ac8bbd236b6c424948e7e9ecff47d79e832ffe480192e223e545488a5b33288984ecf6565df48ece7dba2889f4e1a20510394dd3344d90c5c8268c1248c855175cb0dd9ef436b695f338446d440fe30c1fe347445f0df9f973065fb7b51c33c7d5f4428ed4fab5f03942ae76b7e79ad3679bac9df398ba9fc842d983665e5e63414923248db0b1fb63f3dd1779aec0c808591b65f4d9e95eb7858e721ec7182aa4521fe4b2796c11b2d969335bb718b5cfa822646cebd9fbcf5bf516991b414922e465cf1d72bf35bf794f4448f7e5fc6f63361947f74a0eb19c47e8abf98aaef16bcc1e650cd665dfbdfd9ffe40506208f9d37943d86075866f7e2124acec99bfc5bad967e12384847f1dfeab1edb743ae390f317736eb96773f6b21eea8628d154490192b292f222240534b99826d08ce9324ae090fdeee9a3cb32c83aaee83fa41907258390fe587baf7d9dcc7fc21b75502208c93f21b4f7576366cee683665e88501208c95ffd7f51870d99afc8bca9420920249c175a667eaef7f99b4c09257f90b5698dceaeed175f73c779fc1eca42d4104af45999a699d0342d1794f841be65b66e9b8fceda16bb73913ec8ca13de1559a3b732636f2624fa4817672a94bc215d64fdf74e36d763cb66737c908de965efda581d8cad35b8dc90b5fa7dbfdab1c6ff1c4e9333926962deb03312e656286943ce0a61bfbf8bcd7e3d1925fa6c28d1a7232a61433eba383a6646e9622fd23dc8dad6dfadaf1f46d8ba398f535e82a480a89016d304a242769aaa69aa8f6495ac21efe2ff77bfac46dace2ede70e56644bd84123dc8051775d6ef5ecabed52f324dd334239aa6b678c3a2cf9b1135753f9d2794a8216d63f1babecc2a3f742b461894e441de6bd9a3ff36ceaef7effd16519dcb8c123cc8562be5fae0ba6e417fb889d3b4d9cc696290c5c80649c91d2483d646da5f27eb1669b483bc953ebfeb75ba66b7da3a48171964b8ae57ea3532ff50d304598c6cc028a1434718577bee45065d2f876f45c6b6319dccbd2ea8d292c24222d01c64335fac358badeb7cb0c941aec8d7c26e8bcde96f31ce6318070929bb93f6b4bc22abfd70906dd6196d8bebbdbeb0716331b20941c91ba4a55eb9b11aebbc0c574d43062f4252364d038d23649ddf3e46c7ae1fda081b21a3ede9bcfd2dbb191a46c8c8eea3b4c50b2df5e7e822a4ab8e99f7b20e6173c740a3084967b5fdeeb17395e1738990eeba375dadad5dbbd5102161eb0bafed09b9aec9da2124ff7dd3f5c7387dd96a08e922e5b7babeb5dc72ad0b21ad63466f7b3769a5712d186808e1bdea8bdcdc1ce1f78cc3c5d68dc1c8adb1e7eeb56efad6a5ee3dd8e87c4e82060ee9ea5bec5a7737725bf7721e6faa8dfd7cd556a019847c666dc1e7bebecd5717e7f1a713f3a4a011847c3376376bb0d90addb46ff10d348190ec56fabe3d63ffd673721e57b5534dda0c9da0018484b75ad7ec6c43e6f15dcee3c647429408182f012841f307f9f8adf8a2c3469dcfc63e0fa2366f0a1a3f48db9a2db7dceb56576bd5e480b81d15347d90adb2e50aadff7d96b5c9dd469a37e48b7ff9b6bbfe3d5d35721e579bc62548d0f081a7a57143bef59e1bb4f645ce63c647d0b4215bc3778fb6ab76cef63c76e5d3c5345547288ae3adb5d6dcdddddddd19638c31c618ebeeeeeeee66666666e6089f6bce9a81a4acf780462f294384a42c6631b211800e346c48f7f6359bf0d9e9cfd5f620e3e2bfcfd56b3d46ebb8d9e84dcd1a92ad2fee3a6f63ad3646f520ad3b9f163e74ecae7b5ba3c6f2e570b1079b7384f1c566462773eaaea3f63e7e1102aaf8bd409307d9d3be6e3cd9ecea98313c48c69eb7fb6bf6838e69c71f9a3bc837db337e34fea26ddd0eb23a735e8fe9a477cdf60e68ea20ed73cfc566975d66f05a3ac8fa3ddd5747e6bfdeb47390f15e66d4c27add5bfea61c66f3652cbaa6d4c541def637c2e53436beed3138c89eec208cb3ddf6e3c741346f906cfb7eecf726cfc830529f4d1af2b5b61c6cf3ad7aafabd70dd2adffd7cd93da36c87f7d216dcc3247a78b970d32b668a79bf4bed77adb5d8384cf2efbfcded76c9dedaa4146660bf6856cb548df6f1ae45d0c23a4ccbae69af58b06f996bb95ae696b3357d73d83a4b4f65b77c1cbf0f6bf19a4c7199b7de7e5e674fa6590917d652dda66f6b9854f06f96ebd75dac9dcc70ae1a3212b5cdffcf15d47eefe31c8f90e63abebc1d8dcdf8a4142eabca67f43ead1551b06d95c5c3fdfa37ea1756ac120affde6da8d6fbe38e9d72fc84b678c76c667de62acd50b923ecbb4f2ec1b99fd58bb206d6d8cb5eacb7573ec552ec88e76b1b5d3f67df55ddd82ac8f29336fac6a41b265c8bee9ab1f6d8c340b92cee83ef247cb20a38e6241ba1aa15738bf3d63cbd12b48f7e2bdf0557bd77b736a0549ed62a6edec73dbdecf2a4846db32ebd6b3d7cc7d5241be5358df7c5fac7ae3390519d775eea96b8fe9ec35a5209fadd459bcd4b5e7ed4641cef91d6775bde0d7f6ce9035fa85f0d508e1bbd7452848c8ce27bb6af92db72d3e41c6b710d6bf3dffb2d8a213a4bbcc42a6def4459e2b3641da47e97ad0ae09978b8f09d2eb8490f56d0f2e415a7f73ad1721bbf3de0695202fe409d9196de7983b4c82646eb3ce7ed6dfcfcb201264b3f9bcd9fa864790d1c5b68d56462773fd4690ce1bbbf894de7efab408d2dfb608e3c70799ad4904d9b53edbaaa5add9f8e21064b53ee76b8ed54b2f83429097b63b57d7b7af36c320c816ad5bfd2e7f9bec1d10e4ac90f9b6b37bcc20fb81bc6e3d325ff7e9a48bf94032e89ec1f72e75ffdc3d90f52bafb69c756c35ca8f07d2b5f6eeceb7dd4819bf1dc8f64ddf23adf6efbcebe9404ec636c6c9de0bfebd3990b679edba66a50d69b338900debb2eead577703d9ee3eb76d5a6add84cfd940fabb5f6ffd67f75b73ce8cda6fd1afd0b9d740c29fb0b27a978dec6f391ac8f6ae5f9fbf9beed29f81a4f02b6bcfb1177b95817c7ddfd7357945b89c8d8174cbd8ec4921bfad0e0319ebfc6f6fc50927645a86f4b71c4f47a3c775a92343fe5fbaa24f1a6d6d97db0b6465cdda36cbfc2d57ad0b248dee35bcfdcc3563c8bbd8aaae27ab1ce784ae1143fa5ddc225bff1af4e8bc13f231d6e072d6debcce35e7847cede8dbda70bd5ed57d1312b6ed78617f378cd35d1332b6d7ec3ad8d5bd789967423e6be3ebf6d31b42cb1c13f21d73f5fbcef64bc86967bd7e2f83cc685f4b4867e17ff7476baddf5a09f9f7bdbeeb21656e394a09b9dc3abd0e7a7bc70a27216f3fdb9a316ffd6d1f9384642e3a73cae6eafb5cb348c8fbb73e67f359e7e6650609e9bafb3aeb1f6b3767f608f9fffd6ddd9d73b62de608d9dc3f658d6ffb657b1b212d85ebed5a333257fa8c9090619dff6b45d61eed22e4735b8b5abecc5fe354846cfada6d77e337a77176222465eb229df0355eb3672342ae49ab6dd0a77de65cec43c8c5abf6834d27fbc9b00d215f5bd6d969d95b685f77212474b4ba181fe3f5e6432684b46db6dff65ab3f5b28f43d246dbbdccad166be387433eb62c9b0e5aff87d10e42366dad5d6bad20e473eef2f4e8de318d3110f2efdbaf8fa3f5c72820e48c6eba7f0b36cb6dc23fc876aeedaacd9b3af3e60759e16567ecf274f7f0f39efa48369173f441322f8f6ebab518f305b90b39de9073725f9eab3666bb32c879cc2c0d39f8209f3d5ede7aaee777bdc779bcfa0b39dc9097327bed2edb7541db57c8d1867c2eda6efc6fcd79a333a79a5b8462b828401672b021d7b42ed6d77cb97563b59cc7cc43c8b107e97f9fe1b4eded5ad5ddb5de629aaa14205ca4ea6ecc626463801de458433ea73f2bbbcfcf1b7cce79dcfc1239f4206bd7e598ffdb4ba9c3c979ccbac83475403e9ecea7936eeaeb3672a821e18dee28ad2bde08b945cee3694a22da7cfdfaa13e33a24e10d1344d53354d4080cc70e1a323aaaa8f21326d804cd334d5ce34b9223f3621ea6e3cf5ed34857ec30800478e3c48e71832ff1bdfbc8e617540c88107196df51a1f8b34b267ebba83b4de0e3aeaac33c86b393bc8e5ee9dbb8bd55e16610721471de4bc17d267635cfcdcbf4a07e9ef5683973d5cd6798b73905c9f33b57e7fdd385ddf20871c64ac77ddff07637d74d6fb221172c441fe6cb7cfaf9baf3ebf8ef378f3e01839e020ed8ab3e19dac2b85eb43a056418e3748d7adcddb207ddc2b726f380db95ae40bd7a4d429576b398fe1e3400e37c8656b75932f5dd4c55579866eba3f44a6c9c275841c6d9097c1fb1c63af45ebd41fe7c9e460836c7575f3e8b1b24a1b6d1bd1c33c167d9098630db2f1737e6b756bcec5feaa417eabefebfb6c0e7fd99b06b9a83b5ae3a37da1adeea241526e7459fe37fffff93d83e4659fa374da7663f4f56690cdb3f931b33ce7b3d396415a0bab63abebaa707a5b32485ef3dbd5699fa50ed9a221eb9ad3ad6d95f6dbbe750c92b5f77c7dde39fd2dad6290b12fdb7fb4b938b9d21a0669179bcb2984ccd6d6af8241ae3aadc73add3bceeaf50bd2b2ef091757e610ba55bd20ed74d4b2f7ac1bb37e6917a483b6ce7f2dde8e943ee582b45da3ffe2c5fafeb2740b32aec8625cf6368dd629d582b4afed6b5a5f73cbb549b320a38bfe91f2adac9f6b140bd27eeb369f3b579f2da35790fd2d7a5dde627c0b32b5825c6bba8eafddd51cfd45ab20179bb53e66deef61a4930a9299bf76e7699db519e714246d6daec6fe4ed6e6d32805b95e83d456d74ffd7bc628481a1d5b67bd315cb1bd78866cf76c2d7ece277db52314e43f87d7adc77139641c9f2027d308fbbd06676d6b4527488793c6f7decd9fa50f9b20e743f8d8f3e89c65944226483b6765cff432537e0f2e414ef7165f7b87b335e7500992bddeeb7132d88bdb7d49909399478e70fa74bdfa2141be175d656c99df11a4d3a68b3238e9f5690469d7a2ad394bfbc6d56811648c8c3a868d3173ab3a11243386cc9e5617d7a3ed86202fadd0b6a5d3df53674d0832bae560bb752dc3176310a48ddc6bc1bf0f3a770804d996a3cc2ba476adc6dc7f20fdf662d3ef5bee6f6b1fc8fbccd66b117275372ed6037919474bb93a7b7651ca03795f73436bdde78b0d7b07b21973e5c8e2ac1e2bd581846c9b356df3bde5eb9c0319efbd8e3fb275f7d98a0309e1f467b13d7803e993ffd6cbae8dbf9e17e7f1c7cc8faf20870de474c7663fb61665d3a319b2df556be7d7e991d25703f9dec5c9fc60b3fb5cbb349090adfa1de15fe89abd33901e9788a864f0aa959196a22086310010425949150063130030302c1e8d86a3f178a2287af203140004597e6a9a422e124983c1500ca3208a82180662100008208410628cf14a116e00c03ef6a440347b8ef61d6168e8db8d0df74bf5611d510b68ad2dc4e0fd4d8a705a5d0d20b5810f42cc59fd9f4544d406b19c83a5727210ac88d8bc7a22b7f22c74413e376766d43f788a5fac2fdf055a20aaed78093242b7db8e7c7791f20bbdc6d4e1d1c0af715c1647479b78593ce764b6f93fdf909a5f5470cd7fea27029f280eab52b3137df15661e3323b8ff7015830f651a7273da49283635c46bc941e5f330b5c53695aa4c414c740ac11cc8cf434f43e3b88e4e300cbcf1b5855b61443e3b7cf3ae2f58cbe3a857309bbf341814fac97189f5c7dbbd563afe96028de3f4d559c637775ef79a3917c321f061cf708f244ab71818e38d3693729bbadf51eb454afaaa0d78a3f4d96679d2e21ac2a4724c040e4260cf2b64988d8840a4f118c18df8bb57c4b12d0f1378c3da0aaf7845d991ff3d2e20506a60f0aa374a0c8efa7fca2a0a21527897b47f660b144cda256ae37b42d3e55cd014e24343b0245cc00d7683431844a4d024634b43b04256600d928b43186524d014344b43a0635a600ad515aa79145472ba4a62af3940685bac813d562c2a15454487a9b188e64b508086641d025fc6b50cfeb6ff590124f46d0febcbdae95a0e40be251b52f6d3c1b83e9328941be548d995566b1a4bacbc4fb17900195418442a82bab0fe602070a2030b4eed4f5c941dd598cb823f387efd4d4632b82e965e5d48ddeaf1cb73606e2728a1388010a05ff86fe03848fab7cf105a2e4006f4b5c860909ce6cb43b4223b61ad1179b067e396222d21b4b2fb206a21d49117b35622ed207421dbb22a26e4e92315b2af3bd8e0d743812230636e22eb60c9c3ab2a2440bdbb494cb10a390de587a252ed413d3d1fe6ff0381dd8e5b83a72928c8a309d4fe6985bc7bfc47c0397c5f1dbe276fa2f2b5ff4b24dac5c183104e9264bedc585d4cb8a6f1d058ca38db8ce6e276d68cbd03a55b24bd0f5d2245eb95cd7fc4bcf0aad93978332a153c81bf241fd5a11d38da48bbd066e1cb1474aeaa8c08413c8ccb8a5ff1bcd97b5b4daf0a490ab1173b17d4c28bf2cb7e87f9bb272156230d209961ab214a29b2415dbcb2d9a5f38cb4feb8525bd6b510ddf74b7417f656659966b0d9b666a9e068e85db46721620ba2f4a001204182f961cef46f5396b295ef6f0725dcc37df84e951d1da2c706a31db74f290f1fde9e2a18c31dc6c1ca3fd8cd71ea7da9dbbdcddb6d970c5a861734295122e6558f85afbaa0ced3d0e494ddca948262bb01a15bd7ba69d12db681ba49b30cbdb7d19f28eb2710b7b85237b6b0e9d4bde82f1c80913ca03e9e787d9c605cec4ea6c6783853117b6c7ce70855075dd0ab7eca674514177509eea1601f155d06c868f7e9ac0a2bb4a93744b2c62e552a7df82c7c80e9410c6568f1a106f5926ebb53c43f68cca8287dd6264cde7ba3e28ed5506c217ce7c270e662b2b5907a87fe8cbf965efb3b7b4b5c7729ea531371476482df40841102bfaf61c052b0f0f36752491b2b48b4d352190a2dbc7847ac805d697dac9d4db56be954d37ef10cef6614f290e60e834e594ac79483a807ecc9167e8b4e7fc17e64d6a7197a7ffb60324d2c0f265701d05829a8c5336af89c889c2e384a5052572bc0366cc4b3f92f636866a4a0ba2bb4e41041b08b68d12286dc13911d2ed884c9befd5c8c24c76df134c28b0aefdbe99986f8071a010630bccc416981aba14c7171b16bdf4baa4b93303585488bca03c33f1fe37cec4ed5e7db1cd7835346c12ffd18a84c2146237de06cdcdc199f64778ebcd2534020bf1c675f868d73e0457b31e2ec063f3fb0a85487dd6f5fb2fd7689e767eed5744420be03cd8cc05d25fc961271803032f9c728593fcde6332a54317018b5a471ad5e0677b35288e62d90ee373d126c2e93a29f3d340ac41f68f323ae6e2f80e81b27bcb68aada9e470c4716f437ef848369151d5cf6d460e9a38c27532573379c0dfc3517c428f52d4c94ae18c3bc42254650002d2c6493ba240c163a89265f3d0fe4f44eb282f3933c701949a201f1c217b46b28d8f5f9340f6559debd6f3a3d304a6926664c1c7740d4ebe62c7e155015dcba450bd716f65abdc3fadd02458bd856f5b550c362b9c58c56695bf06b51864573ab2e5ad8b6b8d7e28755e416315ae8b650d7ea1a16d32d68b4e06db55c8b362c9c5bd868f5b6c55f0b342ca25b55475d0368f1440ef9b59d3bde22f31d0369abd85d6897557dec455c4cc6a0f8559e314a7dd11cc36957b5cb6c70c7015b75b281a61ea2d4062f8d92666d82865b3a2cbf715deb6b0cd46144d7a1ffad2adf582b639a0c6ab5f23da3b046c3335cbed52e8d6b32de6aa03d6be437b4d3509f63115927af415d07ff1a85606d26c3d91b16695c36eb171bc86dc4df0d3dbdaa93b17e8d9977d0a5959b8c626fd8908db0ce78dfdc349ff6e9ba2abdbc22ab74781919c3ce111c1ef12dfe652bec7b42cb849dc1fa7340d3d0f77f6d6337f7df82cc9804a31b7468715d4016c709787d4f16e4065c7fdb19eaa279bfa83073765a6739bf1351e54f40e22a2ed8f7640d0d477fa262f489c1ae543fa3dad1213ec7a51a841cd666717e315c756d879077f10f392723212f29cee423df9a0c0bc3faca117b65bf562bac05247f0d95d89e1da5722ffc705194fa79afadfc08b0004faca5e3497d63ae05195f38e2262756700962671bde531c050685e24b445c7a0a995f26d17990d010f188d9b926e1d70e778b0f1e1411ccbe087cbf73c6a8274169ba76a8ed86ea20f28f3d7f27e94f2ec26f5cc5086c13f48d54e9f2bb5f0e7aac40cdc85e5c5522d0d07c8f135a0d67d9ef80b87cb7c8ef882c1dfd379e4b1ca17ff286936d98fb4fcbfd43a428c3b888510f14cef3f8a1228446e62f3ebfbf801a18e91203712601f84ebdd7b4c8d53ee143988c3ba2e7cf07f0de2e122141d06a40c9bdaea3c5d667edada6624e0edd34b520922ba36ff7db796b15cd379c03b7eb0cb5923ea410a9d47b0e2f223fa4fbd6e450354e7f2504f4df5cf4e1ac89b8eb197ca51d44e6c78fe0d066e627ec8ba4549b6f0a72cd04ebbfe59cf1eebb1e294fc75bedce8f02c3c5ce6769e661f1af1366453ffaea09e3e2c3dfb8dcfbb9def19990f05124a4d3bd87cfa9959ca9249122ee17e94c077ba424750a2a93e1fbaeada94959aab5b77616f436a9c6f788db4ad2b58c1a6d0e684530461ea6ec69e43b661f939b7b3cb02792844409aee219971f726fc7e7d87c8991a18a7adac404bb843238f551c28e41c55f69e772ac4eb54eb212708e75e70f176c0addba40d31567ed46f177816bc26d2282a95b7ef80148e8d3b689cc47ce52f679a1435fca2503b85ef17607876b7f92923f06f74b9908c0cc0730b031422c631b3e7a26ad511bbebfe0a908d334737b839333df9933a4b779edcf934dc68fd7432a623579ebe5cd3188f5d1aab6bf1ce093dd198020139c92dba6a4850e0596c1f317978aa487884803257c71fb8e8bbfcb2566fc88572d71082992bff27a380da0e51382d7f438f4070dd56fa239e89f7e4888d5c9ed7685e2290dcdf23aa6a8c02fde2cd94903d347eb7033a77246c998f064b19f922b17855903e69d553d04a0f6713a78de139d6d1ceb4694ce727a5c27ea06a3249a2e2a559a6da78357142b20e0089356cc31a9083ae55380fa68403147bd78ece1123224cc9f5dbfa4b07e09f8bd21a114249896b4df49dae845a4cb3cbecef597936000d8be25cd19e5b88bef4a557c398606e21965771450f4a5aa10597598a6fd4bf570190c308bc353d738c6f7891c4f12ca7b93a81fbc00505211291fffb4fef73b01f66eaac5a1bd1756c0818ea8abdee109d333e504cfa007d240142d0039dc1dfde81437d3e7add96ce88716644aaa3e275acd46d28fd9dc73f345f6b3f66ab40446a8c05e06ddd0845d07bc27f6b7cad87d00408eae632c5022bc42de5f160def1dc433bb5fe55d7b31e80aaa249219f58926c5970b2ab480bb6f8faf43ca4d833d97708dd997e0897c4ffe2efcb8f305b23c2015a9f70502c42bc0e5242403c3c90272dba7f8fd2538604a5e6221148eba4566f9af324ef844bee428366f32b279beabe6d0ab7e9c63c7dfc2e59e9281e8305eb71f0391a8aab77a1fb73db179e58d100ad2b09be65dddae58f344dca000f71200e27b63e0e7f9563481aa3dd2143e80d118a5aca32c02782db66e6439c9cd38c4fba064de9bc0de147522bc701f2e00f04212ef34ed702e017b623e53f9e48fd659da91e073b3ae75d2861cb3b41a77770d3627fb83aa75a2e88907482fd8586f69c8acce86d35a3b515a06c59c3e1e16c5944bf4e1718d6261d46e2fec908b569dbee34e195b6d751e188d3c60822849889cde55ad4c92d74111e0ac2b65a50683789c797e0d99f3b8b9398b744d2cc17c366868104f1b9f9db742325431149ff2546b7a51514829021be68331d8dd47b31d3799476ea3020419016cdb2c37e6a0a96dd19bc7e99a554f2a2ca1227f5db47b7e749526c9d65fee40e3fa2d6c0a6311e014c430a0f3220de787861ea4478eae17993b2ed31ec8fdd1ca7884d399f2e31fc8c9fe12c6c38890fd6b190bfa1341e63dd6f167abe9986a0853e59c434b30c1582a5385524445e2d7beb7176add1b8ff3d1a26b790c03697bc14646b4429f71eb20ae4db9a8e6b2d5d05e33fdb4e71caa9d35f692746242f49b6ccf5d44df5fd1011df591a66e3b9ce474156ac66113d8332499af1a49a1bea188fed2952c70de93d1b7779edbcb4c5e51b5c3816a728bf0308616e637cef76fa6d26ce824c5930dcb0dc1782df32613e8c29dd89461967585c0073f204507682b1cfee09e7cd246e0259b780afbf3fb8e297b2df2898245b5ff37a816a148cb1c185a76798c4209ea1a6cb3bb71fedf267553e622a49767e724aa594ffe57652577ca2036b1429cc9e7a528185d4302b9dd17fb3ef69c66a59620a9ead454d36ba99a88a8b94b1ed22a5677ae3fba95d37a179571f3d4ff693c1de9a1a36bba217ac15f026aca5ec0e28188eaf2cc243f89bb9591c44da6b52e9d773d17112fd1eb4b9df655206a6b87a074a0c55fdf375e319590e62e4969c93b0e254a792ee253a25c9dcff4d506ed53961066aaa304f2b18d2b4f965137a6f668f9e2914cb8b78f8590d852023ef7afb80f030d5659ea1613b2dbecb41277da0def3deba0051b1f5e71c93259b7dedb64d844c61cedd5a8c377739bcf408afc50546cab65551eb3d8615e6f1a500fa5bad569a772507b12a557abbfef016b4e869298811cee56683b7be466088f2050cfbed4cd922dc70c3753a04dc841acbf06d11da42057d57e45f57fc304edf0d22a84a55d982864e8c754e09c40aa1fc9e134d83b965702b8a48bd9075ee7108fe36a68d18034e3f5bf1997d1bf273880807d32fcd6487bac28315d286ed401814f921de50198bc57604ef4674175d442492f921d0630d261ab364cb8057aaae69a6b334aa85d99754146b59264b6cf27be1b87cf894fa7fec72f3d4be3dba0344647c0dcd4fad9a0dae4e20a12947f02c2f33c9ee5110e8a83a04871538980d9dca4b797ecdc32a735a53528123741455be5d05e27968fdfdd2cb8f81e75d2d902be426a9ce9e718a14bf41134e6e93b4e3e0ae32dd9e1bd8cc355dc6c58095a4e90d8b3169fb0ca5cf2c76dd4b0cc5bc19b54665370d63e51191cf0c7863d68ebbc1f22335fbd4247b004a43ff3e5e398e32b2b5b00fe91668af55a94639d340bb836564cc5503e18b7423fbeb9204554b9b41987bbcd82afff1362d666b76baaa3beebd58451579bfe256c66c5291132383fe50a489c041394b6fa2501b98b8d9ad5cfdc79a3a7172817c0be07f91154af6125638cdfa772e5390a4014952440686d30e572d1242c68d4fa8e1e4c381e1c461badaeb023523944d8d49a3599240faa8fa8462137b89c17a6117f2b3a9114421b9a1df747148b22681bbe9617c0a6647584499be813f8474cb31fbcdca8572c269770c1fb721ed75c3be1d0bf36bccc99d533b007ba7f80ea2035f5c5587f28bc08da9185a7e80d62ae65e90440ea2df604b2fd2b747feb45eee25eeedff5ff7228f4823e108e7506d4b0b8a864065686a2fda4f6b056480ad750c41e436abef1444eb1201061109633e14a99f081d0df7ae3ed84a69f8ff8095191e9f35e7a936588e3bb6e518b1c8dd5520ad87c6662381c920a44b1368709cc922e0763574ad0f8a5e106b4ddf2cf90331013318ce0698922771336c3e778b6f92f8298132ed5519b82bc9354544ccc3d8413997b4e641973c296e87ace7f9d26cd58a13461fe902626f4972106d7c1d4b87e6d33ef76f8a840572bd0762a0273f4beb07e74e48e481c7d61f3c6c54e758dab93d8525966583625153c95e472d537b64b9a4f532a830b43043700aaa39c8a7f6e54bb1f3185200f12e9e24e31a827493a4bd3e44849844d0a617b77304829c986729ec4b6c0b0fd58e2703e6fe4d990328d6c30f0ca073a0c8da671939601005ce50221ba2c4a5375e525649a12d86d8d6b67dd4dae83d82f2d4e519410a4b7285480feb5919d995fe3bd95752841aad49190b4d809316bc0b658fc75b9979931c9eae19279e5eb6fdfca417fb9a9f6ea5efe4ef1a370e67c68f2e98a3695be1d099f8731f5aa5f5a14b534368a4c2b4c7b5b6b542709b4becfcafbb8e73e6ce0f7e61edaeed2dd28736ebedf03e8132c3cb8f3c917e0f95cb08c28b3c93fa4e1a468a4e967e8058ed173fff9bc1135503c50e7f6aa0b6a80cf1941a4bb1216db06d8a5d9387c8451f889420a5b9d78485512e496d262e2c170c11a5fb88abfe294daae3615c6234a3b443ce6b7ccf98abe848e7cde138095329f9bf2f591a577b1954dd07d5a278048dc3eb1a1ede406bb26b7b8def94b67fa1e91a49291c11225936b163cb38a1b24b1b2504ae35ed4afd2010d96493f0a2e8c791a596fad4dba977ade6be75e6b32d8592363b0936cd8f9e47c9e3a6addcfa17fce0df7b885d27a64dfd6d8b2ab779470afb259403799c9e50a4e2024d09b92b43ce18776002c5799925932a561ea3512179400436170d10ef6866a5f2cc22b97c81cb495ee77379ab21e4c2c4b5d150fe7c5327dd6bb1ddb9275bd95cbfbd760462a798f00a2787c4366d2942fa8c2a779441dfa3a40387e1b5831698a9b4148380d157dc25f9186455146cd767dbbf4b858576b03dae235e5e9bd54039e34f55a7e26c60b1ce8a51b4de9c0d82fc766a2d032de07011ea0732a90431bac4dd5abed3e11979e3ec6614d12041bdfbcbe16e01f370e9e2c3887385e0ea7f50cbc3cf6e4f2dad01c243cb7bc6267e100b6c01ab62b2f0f0367287fee581ccde8adf52ef81fa2a5eadebbdbc21888550bbbf25b110b3e8f17720570f2421e5ccb58b3c687f050a20eb9f853c9d23bc9e2a2c6a79994de238040d1dc7a0f266158d08a800eacef02346932bc301f712801f068d24a095192310014fb0d1723eff46c802169607eb100908fc48247546758569e9f109b1ef79c5c4a72cce6c4e8c841a88e3b278426c307c76872285f3a058df5271786c874c2afb1b2f8b1fc73bf7d72d1b341c6585f8f9fade1b6e5d6bdf442ae84a7de9d14c82a49bcaa16aa217dd2a1b625812c352cc0ad60a958382702c2f6903eba36872d22582cffec8d4885a1f7ea2a8c21083effbad3cf83d14b75e746c67a69c74fbe61d8c6be7e5455a24f92c671b98117011c1ac04dd9afb756cf440606a1ac349509f3bdc171f51fd526b06fa21d41a74176fc92029899cbdd311876cb419644facd1d5765fe24e06926bf1c2a3c5549383c287d49bb9340eafd09642634eef18504e04ddf0ccf62b06bfc0455f1c52e2978cbc105507d43c5d391fd6e60e243005886e9082a488c80f09564067f9329d546ce0b47435484e53bfb8a3010bdd463c8d4901bee7d2d21222f618e5714580eacbffe81fbef2739c0713a880e8ba7111650da432a158a6ce78f77644c1ecba2cf34915f50ce62d35dbc9103e0aede0442700e43935d1732f89e9816ddf920a10589d0502ff690d3e25663d2e84f49ed758909865c01fc44c568d0b8bba6e1f9bf59e56a146f1dcdf4e4d458e841d5836a6a57c2ae2e04f2f195093c09372dadf73ec1ef940daa4603f770d4ac65338e0f90cd03d758184879c4d17767de5108b0d4f22db99048f10f46f0d321284058a158a716c077093ca5215b380644d1d922e1145a7dddf716015dfd7499a381be17f4ab2ab9fae43d5db30973aaafcc5223883940d0d3b0a7833903c90904293c4b7b88c7e6a8fd734f5428f0a29c4648c418a6e0357f2ee9c883daa3135e28eca0a75e06b18b51f8081c452114027f1b64006c59047e96c200505405f048f82d108130e593f0190a0241d0178123e0b64200c7a9f2fb40a87ccf1755ebcbb25c509d595394aaae116e3932d53b538c88877f023f9863bf09f61898f179bc670cc5efec43c1f319c1ec5d8d737aac1a2286c1787242140145936f5397db79dc31f8626dfedff3101993725363c3246bbb538d2c454bd0a25cdb69cffb77f6130391dc204e6d7f6fd2bd7aaf37f649bcb48f6ebfb5533becf4bef2e5e4530a49a088f88fb1581d153fb0200621229d9f3b6f87b6d007c589f3e4b8241b5549fdeca443fbf6fdb5f3ca34f45abf8368f47e269ce28e1091d392f91a755e647abcedaf931fc270206837028b605727b0649f35f92ff79edfef7f31971628892ded4412ffe68702577d8c0a008cca2b21dbbdf19ab264a423fe5105c80880bfefb1b04635a45ceed05040a342edb74ab7298c817ec3a69fce4c638ef407b43a4c022839aa1542ef34c57aaa6f86ed755e9199adc08ec0cca900b9f9055148b23d931eb70a9594e72389e1e639be4641befb49b6283b53a6fa2b48a27bbc90dce3cb532319a6fbfbb9c132e1ed0e52ebd1d627472098d895c0fa10cebf8d7f3a3911e0e5def01884ceed6d2bd854ab07808e83942d3b37b0feeaef0f36f0a87abde0620696634cbc1466917b0d2ed67e70bfe1e5e0512cfcb27c80e8d7537cda2a9d93649fa61e2fc705df0b6ad3f520bd8faa12140f6b52398b8b0c503f11a6eda3977e60cbcbb8fa55b389668d5f6e258d8123608a591d9cd52dc24a7f7f8debf662dfe958de04526bd4a9c8c37b8c44b1b7ca7bbfff6357afdf6edf047f13988dbc4cb4641bb9003658e1c763c8fd206b96821d1281d2fee0be074c3b4b56a48850e09a8eb9c165631d805d85f6d238b847a676428dc9b04b3b742021d7dd890972be0a32abbc913558a4f417fbfcfb396dd189dde5623fecf9af4dda686518cbd2db55081fe335ad8081db398c83421cdcf230ffd96721e7d8d921092d617cac5a8bf4dc3380c0d83ae6f739318fe3936f4f021b45bf1a112c9167a48a04079b8422ab4e70bece405eed0cc0626fc4bb1e5a1cfedd31c74abfc41b64ec5b6a9419a002fd88959e72a605ed59206ae936b79ec0a9743f981e128e05f709a83521d048c3fc2fd7f9607042476c06ef5fabc2332c583f68de3c4bd884da90996ff53012dbd7e66ed4ae4ca24c7e5461b4221fc0e77752678ee914ec852658452bac7c7320bcd5c11f32b09bdcfa49f714cacb9a2076eaed96c46b1b95806c65e154516a08832d769fea12ed0504340d1f8799d77757b00f3a568fc137fd06346bde0d06ae1f50856b6c0d79c1ae930ae85c9b9ce4d3d83f6bed66187a6a6703346b27bfd6bbcb0daa3801ea1cec5af7b85a76c7ba94a3752ff3d6083e5f4ced826068453092c869d74847ed76ff96f6b8258b2f752d1041431af1d6847f51ad9911ced22d2c5ba38aa970f63c7bc28aa647caacf1c0db3a612a35d5fa9b74efc917b9cd8d727ecda576f0fcd0f92fe45e0be1fbcdeebf68f460d09511647b6448a2a791fe7dd2a392defcb3d7e461f68b0b42ad999d645549e2411ffba6f120fbac7af9c24e72d818e96a8884a75b72eff36d0e081ae6b030de0c3037d1fa22e8a99b29616cc7924b0319346d756f3ce9eb78f62d9fb0a6319845546cff6f395d16e19e0f370241eabb1cc453170fdeafbe019f0130e28814890900510e15cc9d3bf124b28bd4eb0018a8aad49acc320cf08d58f7d32f486ca56ffaa2c83b4b394cd1d5d9e39cb303ea2de75b2a3f5c7e3261a345f2a3919d2ebb0e1a873e9a76d568bcf8eee7649b5973acf71bfea9e1be04c6e36f387bdbcfb65e5efe2d1a73c70e79c9569724cc3fcbd7ced46ca039b2140a46307f86a172b558eac56da7922d72378db41c14ba8b0d02f70c436c623a01828d724342d000c5d6dc9e70f40013035e4d1c9a5559ef39bea42c5cdc001c6e735390bb9370ec05e99fd364b6b5068be035e66edbaa08b38db961fa35dbd2ab754f1f8dae74c8d07cd36520447eb6d52e78981a22dde389761af78e72d7bfa7ede68cc5cd4f175b5b6817119e9db4c71f2d4298bcd5223f37d85d72e5dedd8466ce5c0679e85ec930bc8da509be3654062107f0dbd5789fed93a1e2ff2398ad48317786135b7b10d3b8081bf62b3bf4e26f338bc7a38b8727e083d4ab3839bf8427135efd0d82895bbc9f9e5397b117beabcbdd403fc0c9f13006e2a699819352b73a9627ece9df07df6b4e93e8f8a3bb9ed5baff4915f9987dacf7a409549f77ba0f8e58b5354921994b0bf061a32ed41e3dfcf4d534caac4356eccae07c98a338af9245ae7e1addd5dd6c0c534431875f09cb334c344966eb19066d6b150b8198f40a6d66afe827fcf8cef3f3c3b2f3a31c992e11fc1fab3f4d802e4071ff7f1dda64cf41867347a4d25d98fd5557a34bfd07b77efd1796d92ffbf28bcdfbd3b8ebb38ae393879a8c44d60b8de1095c7462fdf67077bacc80117e78ad2c4cc86081f24fcfd1238d23d1d018e194e50f287ed4bcbc51aa836cff51729b9a019fba6ebdf979dfb7656ac33a5ebf12f9524af9d68ee00f30a1500ab688edcf2e495afaf7504a8e340e5bd8f9de33878933691f04c72feced32bbe92e301d7550b2217708cffbbe632094beb5a9bf23cfdbf5aff71d9d8556ecfefbd326ded2d331e1ad67f25f966e604c89445dd307500817c9c5f9d5fab86d44fa7ed2737c43d3782e759da40ad9e8418cbcfe50cf36ce4de3e479a4ac5e23c690b73b69f0d27c5983ba79f37d4d97ac3dcca5ed0dfcf65f11b6a7862decce17ed67689dda38a4df71563a9329991b1db0887b720a30f28a74ece4e1a96ccac07988f462854e67f9380c242ef09a8efe95a6be4c02706f8a767765b67a7bad4e8bb479482e163264db6521dbaf70ed6bff9820e3c1f850a06e9b37b2eb3ef95956ca3677c0c84ff827c61cd492197eb3d2e0f8910ffc73639e9fcc2978aacdbf453d307f5788445cd48e339a0bcd69e4693bbb078598b3370fc9a99ebcf8217dcbaf1e6df993c58d2450c4747bc637db8d7d6fda1a3beccc7b28ebc28e5638da8f94a997f6aae02aee6953dac94fe83a89800394ca57f00c68c63dadfc0476428cd7ae79ae796f362a21dacbeafe232d827ea9b47b8d88cb18f9ce661148c2dcb03e7aa80730bbf4b12c9b38e77db696593c1b9916ef633bd1bf164a9e70353d5d73bf6d4ba405b24700917aaa4076f11d10e9ef21268077620b0c9fd4075feb4d4c298bd85913b18d854e909371b241a2b4cb7b6bade9fb7fbc8dcb67f631955134197d30f13b7a8fae12b7307426f55ffe6c477d4bf90aa352da52402608ad4e91f14d414b0fda2cb9567643c8f6994a6a0d1704e7bbb38e9007cb4c52748e7b575960bd572b1cd338a900d8dc2f4b95a2029c99579767e666e17b98f2ae3e438f99c46a420404039b0d0cc2f151fd5359a1ce6290169a330b9f5c73b7fc705ec9831fe2b949869b74ec5e83adc3101a7a6886a72fe89a748eee48d4f270dd00f1e982fbe3da37c00062b8bccfef710dc6a81764d66f5d05906166d02da05b041e7fd4e890bd5fb63d717f5e03f6f8271f8e32fa46e753c073c878a0fc3d2041f4b1233e3f077e6e22b99265becd40641939794df96b46c6c78810fc45949b6054245f53ca2f71e2dc14f97b32007710852f07930f0b2cd3403c85cfb83e58e073719f05728cedd3bb2b3d194f96d3f0bdaac5fc67f3d782be3c84c7fba177c6ca61f307addeaf3931510fc45d31b9504ecb6baa7c2dcfe637eebd6d6203863f3fe95d91a5010dae7950ff0b6c14675dd77f8e2bd9b37f727af36be013e00dfcec11044c80db44288f783507e69878ce26e0a9c6e29ad5c8e0a181ee9ed023824f12035680596bb80758461dd835597f05605ef5756d2edfb8f1ede05351ffbc2752e1f3b016ad321a362abcd28e251fcbb663d246d1402f1e10990faf604733ddf1ecae3a66649f9dd3a1b9ffbd132d4bb1af11ccc174106ba9df3dff558c03489ec2446776e2e52190492e3677fbc865a4dec930d5a14d185ab7bc0284581a4b32fae82e1b384cce3317d7e8409fcc7201be49c10488e2288694d029070641f57a0f8142b559a5622366c4f91f8f41f315b905711d18a3fb5b310107d5d000dcd57a641e325a7aaa283301becbaee001fd693f2626aed0ddd517d39048fc4abb656841ed2e669be012e160271bb44479cd8dfc83ab08427e845060c849ad94db01bd5156a8de42c76f236cf1920e56b8f7798c61d1fef9a4588147b4dad0d3ddff7afc5bb02f4157ca808cfff53b0dd86a760db56cac048ee18e3b6d0887aec975dcf9c1aed39ba265ed82ae74101403e46784fc96f9a7af9444a96897f7a73e2d8718abfd497ec01215e841d0055022443f0d9ad14a05e097e12380a508e89c760496aa059c03ef8104419aa3f0292f801b06e1fe6c81832d45054316c108901d5428463b35885f8a87009726a1b3da1078ea367016ac071a0c6b8ec45154b369f48538087e65089ea88a4147eed153813ae0dca8722812429f1ac56f4503e04bf3d041650468761b3a8b56ab3bec12c0960d73d3631b9cb6c664d30b1b886b0d58535a0d22d42a2890e4319a7e519017c1c320f662000729470184aac04f835c000b8bd09f0518a0141500be123e0562210c7c927e0640a012f420f00ab85408c571e0097a9900c0cba003402be2d02329c68025ea6107c2780a84746a541044e419b9839fe63a538285e0e1ce0686965d0606c5e0fa05f9317632c0ec2a7f75871c9b7ff2569e640323df9a8af16a107e6382da6bfc5e59533c6f14cf03a726b0ff07358e2aa814a3fc686a78aef0fcaed790115fa9eb3264d024e82abd80d0f7a74acc55c46a8cdb3c593cf95fd08a08b6a659bf02883543418f71d2607bae5fed5207952f5efb13ee73cf597cb8c58306bb16d4c7f59d25c23ced72a6bd04b0ed87e2534233e2aaacdb04e9c0c77ff20235d952b1d9e69c8c7abbcad49ee595ead1070fbc4a5938bc1bb1b4ed431185dcd7b92dfb8d04d4fd0ebfc0aff2f17faee4309abdd8b1e6f2a995208112a5ba83d4fda2cd1491bc9e199bdca4223522bb30dbe198844c06cac52414a4b7f46c8bb4e74c9d4fc90483b53c348ffcf9ed89ff800ac23b2957a711031638dd9b21751184fb3b7c3924499647a200cf88888c0ecda7d14584a7eb5dbc02d64bcfc80eaa3d826c087a67e6c4885939f74f23cd37808172a3ae9b79134c040da756d6da5372617d339c8b5fcece04e71cd32cf605d7cb84fd98a3f5e10c6c844207c88f2ed27e838d0d243af92d9e7aae63909863d5493ce0e0ef45560a0eb9ae0243cbc74da16d171fdd1bee46a23f07c300cb726c60dfc23e271b623f185f69049fcd4921e50defe37c4ca777bccc8174fe80797015468743fa65c57f7937ed54214694d69028287e7c7552e976a23bd5c7c46f7f52d1b3473333e136048abd60f26d47fb65d7ed16814a7eeec98e44274b5229637ab96295fe0a579da178f26631d94cb012e0e1fa37facc309a51be243292a89d42412680d2329ec4b901747be18fbbb520f9a4f6e6c181881ddc5bd3f773658c9a160475b9b520a8cc5fc6c470c55e364bfe6263a07d44c8a7ed0a045456992116ba411c0caca952377d9b58b27d6758c597cc89ee5835f0848af387dbc8de13765e2dadce1fc03fec394a6d5b747e8040e08010ecb31ca4db31c7d985e7d054823549a65cee90d3d9afd64ff61b97eb7c079fc453a55c4d97379bc2bea3b92c6496a265b012a3d3f35ab0fb09ba21d3422896065612374eaa805ccc417c6c2f8673b9c33c8b5717b042d9e0db14b067ca06c4ad2f42c00ca7ee9faf87c7b6ec446f8243a85f218f4fbf77410c8d741d3b0463046684fb8e1af3991fa593d1693ac60d1ad5ab4e6b0982a20abb6796f9acd62ddc323d76d24f03bd37cec28fbda6430494361afd74eb3dac0c9bb227509b51b0cd5e4a4303844c641e091976e1bdf707114ecfcf919b0688038a73a1cfa39c050a4a4fe8c1403fefde5d778714353f6dc412f727c54a2c4a9180944588883af17b55f6644e906f7c5de55da56464604c647175a2e28a290ce1b284090410b8f990650c1da1771adf39d831f2cede93121b745fcb95b8a20d428103ff73d7578cd646f65c869b86b44f0b2be734b387fcb464ba2c3fc7b59f8cb0eff2c6e92e05f6cf5dcb173df2c5084ee3ca5606abf07222b057178d722d3fbe2e4f20e277b6f9fb3b4de253680b48adb05a8bde23242d1270b66989e17f365d2d3cbf7c22a1b11b52d023f1ea4119fb2aeefebe5bf7b7d949e8abd0e57048dee8979fa8f9a923689f5399e2691e91dfda84737a3957358218f174566e5a07857b28656c36f624bf23d917ecdb60b68fa859d0b0fcaf511b0deb591e137131542ccb16436dbbf1a3aacd7008e7237e8d58f9d026932331668169d0ddf7ab3a9c8d73c1fd305e7513bc7f12f2182f9b58bfdc36a56c6a6679f3ad8875b493b2c5bec3aa380788a6d35c4ce754139e74f2627efcba8b838ac86085f3ef65b12df9145656a71bef88ecca6ee97a02b79e2691e0e9036be9d6a3b10441186b426bd7338ce79e806733087a7b8f37e22bfb3247ba09399f6d696c85bea41b139e723c22f918fab56ac96075961f4b787ac1d0a70bee3736d882b95246ed29075db870b758b8e07e7e87e6c4852e3d9b45ead8383c59a08ac2851b403557dbc3b2237556e211e8f50affd7bd30c6203da89b9f022e4b267c037bbd31ca665aed9eb1ac7dc62769928695cf1556b17d16ed57ecf342bd483fff7f4e80db65973fda918844498c6fd469154941a01bb734d950e6b590129eb34626b0e3b2f6079ab3c41d6ce99f330d782e76499e23e6fdc18535eb0889759789ddd6b3a7af0a74a96a5fb37bd9a3c6c040ab2441f66a4d0ed1f14d1dedfc59af3f68d8fc3c98596a859033d7bd3c167e4313fb2d12dfbcc7067cd2308f02e73bbed98e412059b129f5a73fc6f8785c1708654789d09d28b46c747021d65f592833b9a7768bf3e2df0a397128f9f090b3011f5b5eedf548c04acca020192319d4c3f77d95bfd5f82ef1b2f96b37471c2fa0dc4a5a7c1cfa211e378e223b495d0f9cab91d85a2614bd1d6614dee8a37d496ab68538f788d73d28c9cc3cb1eed4a5b76b0f335c9fca8e68f37ea8d85783e7a935248f05bea224f1522797347a3ce77445a3434d5dde8e119f4fda400cbb37d91e6fd6bdbb6cbd441afaa21c1149b5fd04fedc83bcd6caf7b9421462761a29187c126ec80c68bf04de83339e9ab521fa28750d5bd8add669ba83d2fdec9a1e0cd0823f263e7fa25a3ae301731c4aa98bdea0d95bd4eae0e0630b73f315007b71ef4ef344d7ad62ff171abaaef55dfc82abf1d53f136cb859108cfab933036d3372c1e5b90d022737efe30c27f2e1c833fa655ee2ad5fa0cbfcf895e60dab807e3fcd3861b0467e3fb6f007658af778aa47a66a5b18ef77897989756c0c99461874aea86bce804951f47f86cc8e33da5f59f5565806f18066aff4ceeeccadb52e530849838132516863e7c44c6138f4f428cefd85952202805b7df9cff2fc7a9d502ffa304763b8ff578efc5ab580dc98b4b99fe5f090ec1c58983e340075253120916f10e6e496142b1f4d6627434094f714a40fcf3f8524de0ad21584b982bc3d74b3982e3c0647d59b471906934a7820f8e65a48214948b4b3f818b5af0b451b610cfac76ca0c2d301aa7eb92000a894793a057eb8020883764718075e0e2761cb025eb0b91f5387eb73c44c800038c182604cc096949dd392206804592ece1bf5ce6f195d83aeff758142cb7a8d19bccc43b4366ab5834638b1db7c2ba783db2722bc811b1ca4c61d0b14deb70fc48d8ab896aa50a18581f37c38dd83254d6510694daa145052c84b78a53020b2117168deaf9663b72d18e6adc5306662f1005a338e0a04020598bdf848894e1687f356fad082532d63e4fda3f5c796c836a1f2441b498767a52cae94b92207349714b4a7d3693e8b6f0bf7fab665db7ddb0fdab158c4141a7781c8a4c672f57838789925b812de7aee0f053ca88232ad582e98af92bac36f3943110cf07f96c288d3df98d8c515a29777ecc10c7aa2b976b2490df83365a5ddbf1994991e9ce856958325024411c9f7f78d02b50bfd9e73038af47bb1480d10c2729cea20cf4df1979f3535f3ba04f0b63c352d62230ee4e14c7032078026815295d3f8014b0bd366f2bbf5f91101fb2388f7217b072e05b41f270739d5bef78bb8a304265a9f32b153feb9dbf16be6046a15c917445ee7eb310b12b2871e156e0fc5d0c9c26b6e571caf9618b819e42c33a82b583fbb44c150ff333737b1439479f12b51352983688060b7c29cc260962432318a955ef92b2e811b2e89f6e75f20e1708b2022414f8aa5995ea3a15b13c25c6d4fe327aa2654b2798670ea8752c07fb5a47c6592fc2bf5a9a52487afcf7a2051d3bd53ff175527a0015553335aca6c03c55c177d9f94c88dbcfd3efee47bee84d36bb7c282354c7c0f84716084b9e754c3ebee02f4d027df4b08131311d05504f5c426d961a24e72e3b8bc0d717f6c3dd9a3991bfa53f4f2c448a18701cef19701eef285d252a85edbdb9b502b196ea77c5a95567de216097a34ceae75aef3c1360a84a8244381cdf0edfa73b0fc76958cbc2394c54c11417bf479dd86577dd4a8cf4bb52848bb9997263cca7bc9dd599ccee7d0aec1adfad55aae38cd38c5eb40dec8ae2518f48851129d3d7e38bd9d767b43158c59e3bdceec2fb327857ca6780227ff3937d393272989a76592999db2ac03af9261a0ae45b6627872a0e528b6ce6921c0e974678d446887821ae38a8798bcba3706348f6f0ab780b6c77f50f2dfb0ffdd2874a30b7a3ea6cfe38b75105bb34a7fbf39b482e9fb1eeeb700bd1a9316f6acdcd3ebaf6349460924e130856920974aa020d9c6295f11a6b1ed8dedfade8653233aebe4e2c0399aeef49169b6da154cc2471694a613ef60bf7cfbfd636dd1da0b5bb05b259715543710af6126a5242a6f7d4d7e324887a08578eaea5c8938cab7c5e3e4317122e7d08aade36614c01a1f5d36a54ba51abb90a433add9247384874e7d2e001d86c6600b85b2343b0f690e310b825873f91141921d9ca266afe4c63a244023150a57accce96947b3bcd3ac39125d4ee472e75bc831becc3c7c651aba627976598f72bb75834ab83ec878012c8355abe7ad876a2ef96c630fad84c96f710c1ae2f59613d3c52d38f054667c0d2a9259ba576ca828c9fd632178973386afb6e845c8a2e1e3194de0af1a16a4b04349c9d154a4ca39a3f8d8c4f138249e41d77179a99da09f9e4a73b64041ac16073472fdfa89d1ce7c15946595f0db3f9972484f290a9a91fdffbc3c114eeea4106dd91b89bb5f5f99d01323385ed83d1b5693733a5140b06d832977036e2d03721c7002425845bdff156c3f519d064e1df2873fe72a8375a50c6d5bf290249fa7a4dea6b840f0a6fd5a8536ab3238eea474bf74ced4771158a8c7ba1f32401cb8481bb0fe5d259a16abffea8f8ff1d15b0542122741c32088b7a705e96178daf28379ee9bc3df3ebf3e624962493f6750cf6e82009636c24f15cfdaa8620dbd945875664ce01688cbf318c2500003e9246495a6cbe91a0e0a41399b4d9d6c26509ace35442075ccf188d29dae494da195ae388e3a6c8bb29a1dba574a1130845468714d97f91b879ee28d2768960740bd49d43c94b0c002e93a52e3d07e0a447b26193ebffe2a10ebe0cd518d0221a2b118b3f6c170b0f185780880d7a2f324ae4e207539dbeb167b6b840281de93191ddc5d9afd9ea50838d8c3c64671047c4fad289d5b887889ab42e50731d16cdc1e630742bb263bffa6a56cd445766b26d1f7640f296a4bbac8404307cca0176f0a51f0d8983a0a7560f38095ae39b4f588d0de5ab01f419454c424064854ca8845cd36464300e277180bc74933ff8b15e0d4d47344e9dac182d686a1b4390bbb68b10ad57394f83cd480c4c50510e3c9038f56bd4d061e82b60681ee5fcf9a1c722a4e39c7c01d7ace184b9f2c38c8bfd5bd99682ffe893650c6c0f83178d0064a1bc2ee449e3ef2e71490b473eb80ec1efb95d5c3ccbf35fc755932528351aa6c50b38c9ae5eb58e8fa009b47be9279b5c3fd1bad68b95960841197d52ae062e0c4c865701f26f59ffb419571fc3c056cc86dc3607e4caf2406fd5154c064118a3482dca517d7dc6f6aca2acbeca999a3e5ad0df9cde74b1e9e728791ac3180d9fbe230c5ce73ebc309d457532e1d519a20b491f3c8282cfe2ca6765117c4c92de8c08ff9fd11abd2e5e1b186dcec0372c8ef3d1542a7cfc6a5344b15a56d13b06fbbcf2a8f9d416c98ddfe04d96a5d9f90d8c8e9e575d51b772302c41b3b16b9bc2ee9f198863f8f5ba4cd7603135dbc961cebfb34b473ed4a367666adf2b07e44ab1fcb10d4217441fbabea1eb428bc7045f6f1ea9e8aff9db94971de26ff960e4226aa964e21b8804d5536cdb05ba46acf8689183624aa0ef0465c2b963d1ee809379cbfd6a98831af3dd9d6629e3de56a084277f132b67c17717f385cda1566f9dd9a1daa4def780d35d5827dc0f6ac2639ab78f7f08a480cf18bef0e69e34ffe825884ea2a015fa5c5ad34d1f623f0822e9a15450cc367dd8078bd75b71002969a06734dbc47bf613a4c744006e8aa37e01eae1070041b5a119fc7e0d822e04660b21a16af8c077c59ec97d98f75350bcdbeaa548c3006da4ef08918ab26f16e1f8bb5bb88638a899514e0009573196ffbf510dd0981f02c61c569aafba7f025602aaaf5cbe2c517cef47a219feb00eb44119ba8b2df1109b655f1eeba3c4e84b80acbb2c7b5df040c303f311758e22928fa2ade189f1eb232d4940ac1361d17645ddee7fd893a1095d866e0d461201c08d3562b32ec556b2bbc2fb01db08c671fd1ff9a64d33d082889a0ad53af4d7ac0f02e01fc703a1baa7145e2e9123f903c5a185fe094233cf3bf18521960b11932b412bde418e556843bab311212ae173b6f0c1e11e2192d86b89ebe484df2f7c56215151f96afee4eef2eb1f225af644dc578821727cb4f28e19046c0a0d1be5c1bfa490fccb8ae8478d2bc424df6d34d6dc9ed8387d41702720df1c382c0d44bc3fd423cd4be54281e88a6da4192870c43c1f2eb7fa711fa4442a66543ac4f81e90483fc3b1bc5351c11557552732ebb7ef0f4022ac4d17ad720b3975029daf394684242fe4a03e94421608fecd0e51a0ecaa5d6e82e08485352503305ac470adc8bc90cae9820008abd270a8368871721f487a6c518603b7a5adf1d1111b2fb34196d35286003d19dd652f979e137d79ac4932d541ab2a0fc05855515bf0a132f6d5b1a3d2a21bdba1e6732c65f9d5304853839942e90184c5993db5c4a4a22a86cd323a89becb26a4a6de39b09ee77b798957db5a105c1212514d3470fe412d64979222d99c49e6114b1b7bbcf2a2531f5cded156292d56923e7e673d475040bdfc54c4b6b95bbeff75c686dc8f57d2cb7d496686e896635a38627e83c8de3abc2787f0b4e801ffbfbcf33628e816baed975bf2c60987ea3c6babc4c723c0f9c8cb91514deee9e6947246f573e4c7d773901e0ef79f91796abd65e0d5e732a3dff06a842fb4d2c9d8b8a7629190936c3fcf779fcf36f1451d55ca24194ce40fb5472cfb10cd51c69dd9a17dbb6ea4e1ed34f668e20ddb50a1142bfe01bba81df274a3855aaac96101f9ca0ddff95549483c6ff9abf6960f94b206c1caca4d548acd3f97588806bbf432f73147ea954bbd07f457292bca089de8040a0e69277e07e69a1cc9af124c1ec0318372cb8045ec7a52953c346eb8728acb1e2117f7c85401f322834281cfb982f24af2431b447f5f27ee579185f6e7c6e34579bb27d13f53b894f9fd9933ed8d1318bb06ccda1221af2236f9ef573e7ad68d6309f988a028ff863c275386c425812a24e18a80b07df91b9a316228a92e2da39a3e9f48213cadf61dc25647a16a2141a996a69d755cc458c321aba0c748f666112400d473d4c11617e70765e82832b0869b98528a33ef8ace312d96f3f4d25be8f2c1c66d07dbebfede4169174be0153cbfd9568eaac4f9ba065095786a87e5004f07fc71c085e2f5740698113dcef8f07738ebfed0dd736530d3931dccadcb8b772f68be37aac959a3ce9dff6180941f5ce55665c13324a037185720764f3737eb0030294b91ce519c05f3467e76f8164cb9dbd287e034b6b012f93816d221ad8f9cd68a03c2e3a8ca14bd40d79bdb1842e52e7cd094c7f329efe79e02981def22f01d1117b0650b0e04a2be79ff942c6bc751309fe40f7b7fbade6585f772c617e3c890c2ec07f980b16027a68b46c926bc8f205613c4f1acd35dc9158a4a6b136f745cda0901ea9624cfec022b8f034c55ce20a3b1a3badaefdd5e2602731221807a2ef05c68dfca3c88caeaeced8f973ea59828fc9b50b997d27caab83936ffac0de02e87b25b9e1133f12a90341c7df3917d21fa7ad18eb89f7b300c75f10fd712e6fb217bf36d3be4d8cab614ea644c026ec29e7f087a1e93e19bfd371fc035c519289a1004ba914541b5ff63d786680f7da09139a6397226c705bc6279239afba6c7f53a6416556abe8680eefddefe3795158054039e9c1095d23da86022bc49b07d2abfd97a94574d6626026768b21148068095d7669e8031e242a81582f8597aaf31325312bc2b8c975020743d0e22c9c47846e3c42d65d4bc26580d1ce392f629568d11d74456aa8609ddb3af0d9f72be147300a636c97b0aff027282e449361fb3c3d5f8f26978d09eaddcfd37327684eedda2970b8bc13608a547a473fefa09a2b242cfe106b75ce3c64e6c34cf1c6ca183c19e4fb92e146f50496a4764c443b8e7b2365b7a912454b50b02015d7244a0f66c5bbaa9a50377524a0b11e4a349f3720c2f4ad2e64a749d66aa9ba0eda7194e4bcd391ebc9977855fe2c9cdf41bc630180861a18f308727dcbd0d39b976d37a1dfdf904f77a6a40ed06dcd94738374389027d9018b1943c5b713325dc8b5de53e34db7cf819a55ea2fa52b11b05b2f06f31084fc2a47a1f72d6098df6e66181ceb8d40135120f055587a63e793dc731c3439b83d9ad34d6c234c11b5e368983e17679ed558a5ea7a3ad33d5b9ebe300778ef8b8f578f6e30d7363c82cd9f03e1d780087c4c182d72e0319acec822f2de2a32f24733f57413f6d2e693c7de3ded77e48fed03176d03e7629c68662b9410a68580f98cd4c0ade2e5d113f8d72379e78ac6038525b81ef549b934f85e4714425e6990805ea4f011f7e62088d86b11de93ae990135dfda335ba4d6899d2cecc2c7cd8497f5226b3c59af0264c537de2ee237fdaa594788747be37d35c4755475ddb9647ceb63b2066cd4ffd3500d1c8e023e5a04220b3760b46439330f0f0f0f0f0f0f0f6f4337426a6b9f908494a454aa976679010b644a29c99492d81dbc0b9c99f87466e2d31546ea6e34e301030b400bd30a89305b754ea153cc4f17311a1a68000d2d6854c0c3c3c3c38bbc0c801a6220c214d28388d6a39e7fa625c4388441e5edce5e2284ea63058d011b39ba7880214c3a9430d9298b440b63214c21c78b914df249fe104218b465c813f91f15620cc21871e5d6434b9e902208e3440b19a91e545479a2102310264f59952647c8881a157284188030a5e777f438191b6b016084187f30968e74f988efb9a75363edc3c647c12d4b31fc6076b7083243e465059386161e1e5588d1077357fef6f47137b6f3c1246eb35ebf9237ddefa26b68c06fd898c0923d186de28afa0e1d3d18e5524823aa3a7f3a711ecc5f174b955267a972120f8697a4b4aca5548086062440e3430b1ced12c891000b2851438c3b986ad27c74ad38f91b362002e50a31ec60be0c7df524667bb5af83792dced6c4fa09da6e3a986e5baf64752ef5e139183ce54ccb19a24a29530e26a13e57b8a89e9519d6588b83f14a456cb56ec2292538184ef6d8c9d0918488a637e8337975ebd52f23c70d068ba6bba5847e25f3b4c1782a629b087a7ad5c26c3089f59c82129d3c8851d7b09af89d69d3fbfb5d0dc6d1f62762470e252d4d83316757528ea8f360123418f542e3924e6a44fcc81990e79e7592d8f0dd88194c9fafebc329b7951896c134e23e23f4d998fa6430bd560e39a29ab0a4d9180cf2246987284292490cc69ff9d4ad94264cbe6aac1962848131b7d9ae20536c3430587a3cfe09791771fc4272a23de2880929cfc70bac96e87069d927e98241e9d349674cdc0575e242a272ac3d2f919152bd05decf52e447e9f4c1d58229e72454dae4f24ad5c982c94212972fe88d05632491b3d1174a4551b942fa4b5e9f1a1d4f3c620593aafcdb1dea296787ab7067cb13dbd46a6255a86052e6de267ddd4444740c31a660c48e14d192429784670d0f69267f4761d7125365924b5ff250307b2425df15c2ab3e9e438c2798cebc4dc6e52427182d8f7c87f2fc155d9b9078710fa1cc4c2618c4b6044942924b583ca565c7d2217fca9460b6317defb0d57f214930f68dca496adb87f00e09c6f8ddf3aaba54b61ec1d319b135ce74881ac1b8af27c1be6e2c9f72885104e34a34a14f45869e1e11c1f41971d72dd4ef538660505921e5e715e517662198aeb346eaa80ad2471b0483e855f79c4a6dafac4030999950b9206db573f20786f7efec4e7af744f6d00d317c9068a6965ddd0373b6c41c939397fb82789007af3e53a24f9c24edc024277c56ba206f92b6bbd8a26f241d982aabd675f47091d29203c385e7eb087bed220eec0992b642527cd9788353fc38ba446f820ef2183630ba6e8b6e0fdb298aceb521460d887f49f4d7280f950662d0c02ce6e13cae598ae4a1b121c60c8c5e3aa5a81246eeb8be8618323069cefde76f856889c5ba9f53d8cfb8d2aec1c254276ef73aebe597e91546fdb4edca31b57923572073ca9369bf4a2b0ce7a3d4bdc8c6fdafac30dba7baec905ff2bd5d85c1e444e5e6fcc83cb9c69aab1960a8a2ce761792852429684f2accf196653997fa4d1f15261d523855d7317e3a790a5418cd4ea2744c5863ad057e820fbe1b3672743185b13b6591f11c23966534b4a0d1821c5ea0c07451011a5ab4e0867f71011a5ad0f02fb8680ed0d082c69db5c02340430b1a76c39d8bf3850d2df3a251f05186c3bd68418e2c85414f7a0f8b2426297b5218d56cd4a8f4f1f6b98fc26c71d2cf88c5eed35714c6d1e965fdc36fe49ba130cfbc8f288f7b8b2c0185617f3b4be7fe2cefdf270c2e4946d0155a3ea25d066078c2a04694e5ed59d7fabf138614f22c56a8958afbd4d80d745181b3829c30870d911e72e6dfb29cb809e39c271d99a142de4713c60a7af1223444ae8533611049c5fbe69e4bae0f26cc79c5de7b74ec10a2a55cc214b45c4d9f92aa22644b98d46595136182ead866f2004625d491cfcfbb1682caa2008312e6a0ad2797ee5b93e1f8c8f3008c49186553c4055d59e46449560086244c1529ec8e55d808ebdcb8617608302261aaca9ef8b521b2eef187106040c21ce36e73278b47109700c6234c6d415f0e42bba9578405301c61d8b7aa2ecfb7b0145463edc373d8e8e24e05301a81d0191e3e6444790306238c713f62d1e43efdeb35d63e10522f602cc2544ae26e9b84a4425a6aacdd5084a954764cf86c3fe67101231126b566592e665c46b4cb4c000311066d912da851fe41a4d3210c7f61db43caa5b9681bc29ce467e498a51121a51026e5392e9f449810061d134a790ae141982508f5385721447a2c087329ebef2f5526e60f8439ce6ea464656af2640161b69446be8d8a21e4db3f987d62593c6bedec52fac164f7df69fb631f4c29ee6e65adff50b3f1c16049eb6666df8331458ed029b2249f5735d672b4e0468eb5ced10bf0e223c905400c30f460bcb4f1faa542ce53aac6da471d0d0d34600b30f2600a6982a4b82e5539b45c808107f3e81e93b318dec1a4646858588a4e96658db5b230ec608e26579b3ee26bfe5263ed830d07a30047af200130ea60103bc23f8556eef5c3737c617e18061dcca924a54fd2f75ba489e760d64939b722bf65cfa68f3b030c399844d8a9a0ff63df851e0783a58689a7541bfe4170308e507955fa6358f5fd06931033ea6bb47583f1d37376b8d5aff8691b0c1adace3e87f8b922369882fa4cf652daf4e49035183f2d98fb051d6f92d460887339fc7327b17f8d8b56810d143806baa8000d2d380dc6d02222d553f5ee99ae00030da60cfbf57af7703eca3318b7be26ee8752224c806106f359678b37eea9457b30ca60f2cad24946844f8da700830c067517ed83b4fe1ba53406e35ad871cbd7e3b29be50186188c5a5b37bbb67d9d3d6130a7883fda42ae99f6d100030ca6b3b00b914275ea60d6ad01c6174c9d5444af9b8100c30b86f43b97bffd9e7459140230ba60ead4157ac7cc264b8ad987a11c6070c16cdd162752f27a1bf9ecc3181030b6609049317a1e52d809920f3336c0d08229291d26ce8fc9979033fb30b5d2038c2c98f7e2f3558a8afee75830a8591ecb3d59bd3aba82f9564b7b5cefaaf3b582e9b7a3e413d23e29bd55c1f4c9b28856bb5890142a9854e764a53fbedbea3b05534d2c7d77aae2e7775230a9a06137d2ac24465214cc7d1a298d2c75d9520e0a062d93b297d095ad84fb0473cdbfa5abb658eed209c6d5b4504ae57cf6779a601a6d61ff45878ffa2513cc6992b07fff8ba054b40453ec0bd3d6ad3fd7394a30789add480bffd22d4a8239e8a860fb5fc94bad483089bb9f117942749b0fc6114c49d99a488bb011cc11840a2a87d1cfdeba08e6ad6c6a3f4b53438f4430682f11b211f3b22f3f028c21984bece28afc8a259df6f0281f8607430826ed4eb1743e136f97140473bea978d6ecd68d05046377f5886b7a7779f903c3488ab964a13a9f181d0e183e30d75d075d6e32ebb33d30a8b9a43d7479903d2e0fcc155ad41d988358499ae02af945d68131e4c73ebb49c2c4e57360743b717eee57a16aa3a1c58729da62cb60e0c03c16225cccc98c8ad40dcc1f44880525f46f5ad60626fd172d47dc9aeceed6c0702a3e8a4eb942be80410393b4483fc2d7e4251d9d81712b23a7bbd58fd41d18323005a97dff932a8ec9321646d3f2380f23a2bb25b0305df06afbbbecc9f4fb0ac39f4732f3d167b1a72b4c62a284dc5e893551d28286161690000d6d81005a6148fadada644c92159b5ab10b08801546133af5abd492d72cf00f1b393ef05620805598c2da6ee7d3a810376320005518e4e7be3ecd5f68130502488529ed2911adb40987aba09d8b0f1514c31b282801a0c294ee84ae4bcb97eb2fd2028f403985c9c427ff298b98c26057396e9696ecd1430194c2204566e6ff7bc8be202408801426f7bc6769a476d2e6e8628b1a450b0f8f519852f8879265f7a62ca6111e672887243dfc2e5f7c7461833b8ad0610643d075973fa67a8f9a40e828c32a2a3ce928a24e7e51e82003ee5f5e9d25954612133ac65072bb2f31cdd0b30b5c4287188ebb9611cf7f346ec1f9280b833945131efaf24608dae90083214938713af79c599a5f3086e917ada2aae7c4e4dbb9282fe8f082e15bc4bd54e42e186f74ce695ebac73fc905d3e4169973514af6f45174d0b105939294f522df4d9cc65a3098ce2945c991d4a0230ba634f12a4b92e4ce6663c114f428a56c47f44b7e35d6cc6868a0010ea0a10109d0f0f09041c7158c3a493c699153b357a264830225d9e8a2eca4a500083e0cd0610573a4ac9491a89d26710f0f5e0a80e003004ee8a8824175ca972e6fc99354d5584bbbe139fc861766aa800e2a18640979f398e421775330a470175b17b4e335cd8f0e29184777ac185636f3f9d482060768584002343aa260f41c6f25c5fd8ad599066868416303342c20011a1d502874864861fccc3f5d634d058e010f0f15f80db41b39bcc043c7138cdf5f51527f36d52d7e31812e18e0e1e1e18163071d4e309c502297969610c4af8e269844926a0f777e6a7eac4da18309e6642a590ea1b53721a9c6dadbf8e0251872a86441f95a12d26a724a30ce4fda14f14f27d54c82797420c1f82bea25071d9f4ec5fad071044350cf11092a4a90df8d604ed1a5478f96fbe6a4a308a6ce26259d8aa076f5c2367410c1e861c4e517216774ea5e740cc11442cea3152d76ca2d4bd02104934a95e6d564769c488d0e1d4130c7121533b2e59ce3b36573e80082b12ac6071ff57ac2439258213a7e608a9d8492d9fe8a7caad436b8f0f020f6611b880e1f18d447f47fc7fa5356730f0ce2a942a57c499bafc63c3047c822c67284a9b2c80ecc29fbe46025569e832e9fd0a103b3e7fc1232faa774e7e4c01cf793f3466a383084dcc93a25f5ff0df50d4ca9ca7476dc49133a6c60b29bd1f7afb14b538aa1a306660f22f9626db8458e7ea18306e6206b92a3ed857ee5ac858e1918420a7bd723c553493a6460d0f5c1f452ca1efb7d2c0cefb13be446fc8bc8c2c214b73d6ae7707e2987bcc27841ee5225a929617b579872aa3d15272e88e7a915e6d6cf9654e58ffb5cb3c21037437755ffa2e4945598ca3ebf8f885615a6cd946839e59c0f6aa258c82215a6cbbcb99c13ef0a59a0c274ebf6e154485b3e39a730c7ca4a4a23ec1b3958604c61f0109552ca1f254afea5308ff04ff18d70fe1eaab19611c88214a6109479ce3e7d7a71bf1b4116a330e58e7b1a4134925b4a8d355118c54f951eb57f2af13d01b2c1c586c270e2262d9bb4acd0528db58f0485215e4acac38ea8c6da65208b4f18546d3dcd495acff2d558bb40169e3065ae97764f1225bcacb1c614c8a21346fd9113ec52a6fd7d1865c1095359fc4fe2d69ddb6235d69c8bd37622c86213a65431d789fb6ec9ae1aab09e3ed44f5b62dd3fba61a6ba90b1ba78b2f361326bf8a1f275d5f5ca45a70234762c26041a50b2b571344446aacb5e0460eb2812c2e61fa9c3e2e2ad5d60879324b98455987b058c23ec4a2812c2a6108b2697fa6b273ccd696035950c22074ec7394093126ea933086850bd1ba6a24ae92306beaa591266143422261bcb738a2539daac41224cca2428c70df0f6a52e711a6ab18177cf257bec91106d331e4771aedf183dd0893f214e18410c16cd28c30e78f9dae5f21f5b96511e65329fb9cc9274f9e2ac238ee3da3edb7f2fc441862a7a0ec65e554658908c366cdac8dc9da499243982cce9ccd07194bdd19c2b0ef49cbe6c2e4d9ad1006d51ac22d564ce6a93c3c70b8172dc8714701107c0400085910c214a367e4d6764b8a21b40fbb2c0661bc783d31fa395996bc200c49de09f123b1cc4e0d8461ce65948e8a1ff40f0863a898dc172d79c59559fcc1fc1366accc52fe3c310b3f185fd2986f044bd9c9f7c1105b2f2595a4d3635e0f0f3164c107b3af873455d11e4c41c8d1f1e446bf4bab078369dddffd8fe855963c184eb52b5be8feed13c18361bbf3fa95f60ee6ca932d9cecb0371766610753aa121242aace33f70c1059d4c1d439e88837fbe5a9743a1826c6554ab24d5cead11c8c6f66a3abd255e4242407d39aa5aca7a7d42774e2605c0f7796173a7030ae99d9870bf274c4ce1b4c25cbb446abf55da5dd602c714b4145a9ba384b1b0c1bdb299dc50a1bcced3b22ffd55e94145d83b14ee48aa74feab35435183f452a593339c8b7d360cc095ff91192a48b241a4cf9447eca10e6198c6ddaaf636c9abf04cd608aee22eb352c9a50aa2a64510693ba1b212f7e4f0653ccb17062d26ca6c8c76036212df34b2589c17429a87e1ded15742585c190d3d6efa4ead494243098b33e77309bcf174cd9c4a28a27715e757bc190824a3255933f7ba574c1ec7f9def42902729cb5c30968e6f2642b21ecf780b86a81f2584b0ea13e3d582694dad72fcceaa77370b463951a5f379b29b5ab1601cd5599613a24d4af30aa6b0fd59478d6905934cf68f3b4a8ee79454c1b835794d7627557d122a98ceab3f2b9a369192640aa63d75bd5f4ac63d77523056866817f92267b68bc2b1c32585d1eba0603cad94ee53e8cc0bb227183e4e9e30fa932cf79813b4b2cf394284ad09c66d131e572a74d29d09269d3e2c89c6e5d32fc1b027df4dbd64cd1229c12446477f3ea1524c4b12cca9a76a5fb3c2642498365436cbda826b5716904216473075e990f354e794e36a239846082d22d4cbcd7f8a60d056ada0e324f5e7b03040430b1a75011a1690000d2ebc40c1166a4430afa7d8b6cfebaa1492c30607ee3e90c5108c3732d64dc7ee3295f5f01082498e65af74a3ab1e4e48c82208c68cd1d9a16a9ea51d0f0f0058210b2018eed273ec4bf331e12106b2f881219ad6aca4e2f4f42a3e30778970abafb6d037d758c3f1e1c517391cc771163d3077deceb58dce4ab68d1c669b050f4ce13f7f6d9b8b0ed9da813925fd6b5e39393e6f4ac84207a6246e23f3126543c63930e851973c6f527d4bd60b59e0c064f5a542b05c69a7a31b18b4424f87da883a216f210b1b98438e962bfa05a5b4493c90450dccdf9d45c4cef7d1b28d42163430880ba7d353d6f0ad9c8442163330c44b5de92da84f56a2608b1b5e98690bb2908131c3b5d4d743462ad5be102316a620d1935cb45426b65b2ec480854925c7d3f5ab6b4ac5f415860b2a26bab64711f95d61cacd4a4a08f1126df256182396f8925e49cd66b816831506959c4e091d4ebfa4508d3557c1163752a22ebe78d2858d2db640018e5e0189b10ab3255dd3adf5d5afcb3eec0f315461ba3c2b39ba92f2a7570b1a34b4a0a1050d2d68380a9a021e1e681f568718a930867c864ef19239f10dff40157cd8c8f171e3867f78d12858bb618018a8307f2775dac6235e7ec8294ca2d627a414e6bd448708314c618813d383e4b7fc214f928518a530a88dacc84ff13c7c0e298ceb4195cf8fdd859895e9284cfea984509d5fc4a5105198a49afe05a11e4e3ccf408c50985c4b948c999d94da9a82c21c44d0cad974fc42d0ca270cda542cff7896212fa29e30c514d33457b7c61a0bbee80f1c5be017313a71c8a6ad478f87306d08313861cc104965dc566aacbd9f0b000e626cc23cbaaccf36de1aeb802e2ae0e161ae88a10963eca7c8eecec494adc69a2f51c19503c4c88429eb7b4ae1930af3c13a25c4c084b13be7e87ac1930c317b78e4c0b105171e1e1f312e61d4f6bb0a8babae33046258c2786f229ef77fc66a8f5109b39a9a4e5ac3ccbc932861d0e72762446848b10b8e2d100bdc930d0a4cc210627f98999af02177bab0b1c547310ec4908461562b89bc7d593ad901312261300db38ed725499bb68504562161160f13df7212de1344ec1e508256805a0e1c5b70413a10e311a6bc2345655bc8b11c2a00f010c311c61f75df31ae5de72a1f311a6150dfeb104ae9deab1703311861fe523e267f2b4a387b11a6ee7c29fdc65c6dcd0f1c5b281b6228c2a49f133f8450d7d93e0163b06b542449d1830c311823a41c429a2d7d1f2936920d1961309d460e22d7c608e1078e2dccc820030ce6141d11f4b94ef81a71f105938c116df11452e507820c2f18fbeed674f67f4f3b5d156474c138f21e724bbbc3f7cc05639cf7288b2d5241c6164c414372f658bb5acb6ac1706739548a3769cb436464c1b43d49697dcfb7a17a1958308f34ede929ea46907105e39c1c5da645a78ecd56307c96be28afff6cc184b6c8d1393290041955c827b5d5cdfc766190410563ffc80e1d65259ca59ce046a3c06f988c2998d74cf252302421dac3bccf838a7714cc637fb1f6c2733e8f0d20030a86744a6a990433e1697f8239ccc5eb15a1ca3b742798723aa19d3dcb92ce5213cc7d715c46a911b1549860b48ab5adeb4b1eb46709c6ff5db7121f3c5ebc4a30e7d332d5ac78398ff20b2f92606ecf2d39c6073569511948309e8e246549e49bbf1a20e308a68b37799b2ef2846f04e36f4bfe2c7f8b6711ccffb13e92ea49189d08269d3121f2c63f04839c5d8a13a24230574e9ffbd485ad6406c1a0276a9927395a8940306d9747f8c85eeafc812927a1b4cec49095d8fbc06413a4a48b1727e1ef81219dcd5dbe9ede34f1c0785abd25f72ca47c7660d8fdfe55af9589b60e0c96a735442cadbf951c18e4ab465c9771604a29172e37b981a9cf45b72d5be77236305a4842a5ebce6b60aabbb3b3f78ff697cba081e9f63c47ca5f19e272193330859a9d9e58c182da65c8c06842627c7e512c0a69a74aecd30916862065724e69a7571873d5b63582d29bbf2b8cabd7a9b5a6e4b8a91506a5ea2fc590eb131756989359a7ca4dd6d3d9559824971e95f2f9a4e0aac294525a8b7ab721ac4d8551542f5c695d2935418539dae81021a354c7083a85c9545a5c9e780c292253985b2624a16c4f98cea15298f2670f2aa69228f9218571ad757f3b72eedca3309f49cb232f8bc2581d3af2e321556c4361bed355ca666ebfce939fc87e9e13b3f309c3856cb933e2d7f9c713a620d16f27db9d30f78ccae1479eb4b339611c3f933549ba095376bf2d9de029775213a648e17c6e24ebca9809b3cc5c7a8753553a870973d4eaa4bb737d0973c8a7ce5a2679b4ad2d61ce96ee44c5889c9b75254c7b41b99d122bb25953829cd47d98e92761cafe9096462d4f58256110d671a2e935ad279648182bc951dbda175b59818441ebe8d121afe7cbfe11e638aa6639976fbec511e6942645e5c4c8e26a238c9246a5b1b29326228c305f2e616245a87b087911865827743eed5811e68bebd039f57d3e281106af78ed517911612e4bfd126631b6c73d8439480c15a1d22d4eb88630453c3121d9a38414dc42984c77c87b77a5d7c32584a9e4984ad162e5e4dc0ec2143a65975feb69782b08e3e7efa7916d0361cad0102994a97aef0161d0b19444134a5abdfe07e3cec4cb0e29723ec90f66391d7eef7592eeba0fc60b25d5bc2f760a5a3e187e2e74a5e0374a64750f6611a52d29554e4f52d583c1c7bc52ce97e6c16012827e7d13d5764a3c18fd4556843e1d3f72f20ee61aa5ef2ccd6907e3e98513b93baad696753097ee7ca53da659793a18b4a50a573274b2d8cfc1ac716159576ff1477230d7249b5119119205c5c168a64ca8fcb1a363070e86fbb871153335b6bec1f8c1e3578a923b65d50da65c7a674ae9ae0a4ad20643caf993bd7472b6246c30ad5bb8e59b4ee126640de69a942729495e0de65441271db75fe43f9206530e42a913d16bdb7244835135d3eb3ad8095bf10c8637a1bbbd3da2f58666309bb4d116452b8341a8532989d1cb7e2a2483c1cbd446b8f14aed8fc1bcd75eb27ff296c4c560d24baea5544da91c0a83b92be8d23679bd94c0601ad393e2cdb466eb2f9845ad83c872ab1cd50be62de91ff183e75eba601053ad8b13b96098a0a4e54bb9db82415bdcee24cf23f93a2d18f5ec27dd5e9705d3e44fada15d39873c164c5db2464e5e8d35f915cc296f217dac3d9492158cfa15b154073597ee2a98e6d449b5f8ad242e5430b7685321f2650a06bd9552ca69a1cf220573300f3337d9a260b0f568d95c8382d962786544b9f330da134c41c48f7d2a6d04cb9913ccde25545b5049c653d60443d637cfbf9ebd6f6782c97eb4ab5eec4e3f5982e9edcb7427794ac98812cc27d62992b44f72912418336d6fd5ec73f61009261521a84d2b1dc13c5acc549424a22719c16ca67eafcbdd4254044390977366d224c98c08061d41ac7efcda11a91a8239e6c4ea24a2e71195178221659985e510776196060c2008e6fdcea346628dfd2801c15c39e4fa32747ed07d095d42dfd828aff8c0246fe16ff969db43ac0703e8814196840927c663a97d82ce417630001eb8225a3d5ef54d773430801d68413582ba3ef5531778662e18800ecc152c7f0ab2b4f7d44bc630801c1854687c9e5c153d49968401e0c060276458fefd907be1220ce006a5a553594e8c906766060660039365b913ff3c9e44258930801a18238e4cb83c5e0106400383b6163f378b1eb4245be011c0f5a251d00511c2006660f814b157d953ae08fb00646098d892b3e85d64b13716e652da7c929cbd4ab1030b738510c2bbea5252b3fc6f91f6bae315e6385b11827b52f1f3b7f3b000d0a0c315469dd176399c9599b66ed8c0f131015c0f74b4c2a04ce850b245e67b973a58619e1ba533f12ec9885b598549479f12c24e52d05176a14315a66842c768951f9dac4d854962a90baf734ad829e585a10315e42c3eda6ada276d5c1a10011a5ad0708087470bfc0486868e5318d2861a5f516df9c46f0a73e84fe91e762d8579cf65ec53ae3c499f5ce8208561a4f5a5edbc7aa98238bcd822070e2fb6c8310ab3e9edeb1055e31f3f36d217386ca42f7088c2f09f3b848e3bcac1c51625c8c1c516250885d9b3529061a7ce521a14a65425b98298c849253f61dcb1742aa9cb4a2192c40a1d9e30ae6556d76aa5147fd40953ecee70499b862ccb55a18313a64b6da9fb713d04d111063c3cd00d12746cc22827a731c2df83ce1a0f0fe42cf0227468c2149a963aeced99484c18ae3ae4ab8aa2924aacb1f6710963f5966ecd975029a325cca6e47ad2a66b47e95f09730cd36952ca497815d558434a943aaec4728667cb4551cb4fea63b44e5de77f564e051d93309879b614725ee949649230e812b2fa431821b1bb8c844977c88927c47dd01ff6a00312c611b1b3b7a542627798071d5af8cd9fb7c311c6399d45ed9aaf97bd1d8d30fb8cc821653f31c290e298bc93ba16473bad60e8588421a94b48563271f72b1d8a30c7b3fc20b612ce4247228c1d45f4e6a868a3bbef408471648a3839d96bd9e24a42c7210c3f5252929b9763fa39011a5a78785c87210c9ee22eff25154eda1d8530bf5e24ed22272f84141a5a94a183100629a3a346ec5bcad5a20047afc0c30305387a056410c69c18395cfecaa7a60bc22841b74cfef4d1d080046804c2ace2b936449dd5870410a6ce1dd15ab1c6d5f407d3c8fe68932bc70fe61479da4542fa60ec0fdda253ed7c307827a184b49c4e5edb7b30c90cdd2e916b7e62ebc174f9913b87388d3cca3c184fdcc9951362c2e50c0fe6dc503a6cab750753a40bea2145b285f8d9c1ec21ea46f588141d3b753005a9262aa2891d7430d8c955cea542e660d4d3d972b59a6e179283219e69b7bf98f99cd7389827ce5d7b4a952d7fdc0107a3f587d0e9fbeb7883d15484f82f67ca725248e87083a9b3b9c90921049316521d6d306b4891756a724e1c42071b8c23f4b9a4fecdfd0ad0b10673be7b48296adea7c4c343081d6a30da450e56b9fbc34b5a63cd16e0e19106537ebdcf16561ae72b1acc957209f510cf3f6ed6582b868b2f7038063c024320c0118c1dce577eb535ac7335d60a4d312010c008e6740bd1907d93f6322305018a70acdf9b134135d66a060420827962c65c14496241da359653172671f1858d1a19f0f050f3d3c7120438a4d1129773dcf25e40002130d97ce7b2778a1408100453c4c9aaa7957b963c35d63e706c0104536ddaa6c80b277bb25c70f1c59fa96941801f3cfa6dd573f09c6404475a00f5cb81003e58b4859121ba3dc90e6d81a38b2d6e786a77400e82003d30083d96a654fdae530a1608c003e3a57e1aa154ce65fadf81c94d52c8d949d781396c7c85b86f93accd81d192d01b42c6a4981207c648f941282182b211961b18dd2dc6842023cdc93420800dcc791d22972c754ab849011e1e384a170850037396c7fbe42b42bf9b6860b0da89faa193e9f119025120c00c8cd9a7eb525b3dbe3402c8c09456d2c5504ad5079dcd8885416b4cb58734a977e219b028686f31939fa4bbaf4862c7965ac4b4f60a2965862b4c25f94cac3a74098b7e71c38b3b2d683c400b0f8f1b284001eb0f66b4c2f82571acebc4f6c7ae3061062bcc15752429d5a37b3f5aab30e5709e52b2bce7742f0933546130ad8ff73948b7a4c244c28c5498f75dc38490ab919e438419a830887deed02927a475afc6da47b261a3041f38ba066f91805232cc3885299b8821c26e5e3efd1a6be8c3b9485d740db4a1811c5b24bfa18587070540f0118055cc3085b94d564e720a95752a2508334a61906b5a5a1f7e56749881308314062df1bf54e5745d4af72fb8680e8cc2e0b1bb4327468a284cf1f65772b04fa1e357426150e2f4e99fb662300314a63065a1174f277ebe19f88451c6434fcd44519d843c61566f730d99e3cee884a93c5aa4582a48eaf2ac31274c17c73fd207314956ca26cc9dc27c5d8eb6260c3f6e41e57abd9f0433615a7f397539ed56ce1f264ca523a83c9d439f50e94b9854c6b8664d48726fb18429a730e51bebae61d14a183e68d16e71d348caabb1f6c10517fdb11f357278e18905332861b034361e33cf547d28d38c4918c485e420fcd4b6fe481286b5fa8c74fa481845a724f14cc84bd69119903049ed097f2ab47a9a6bc6238c59fa51aebb43f423331c611e376634228dab8f90bd749fb6c61ad96206238e9e949794d1293fc48b22331661d2f0d1c82ade9e29a24b6a62673b2dcab22bcc48844124c49b0da12d9de820c224fa41c49ed8ea0ef321cc21c4eab54a13dc3f8630c4cbfec82ba33aa46c214c6ace62d5fb87ff132184494e777bc8f191a3c8660cc210d4098f587922fea28d98210853e8f5f8c17357ce1c91dbc38c4098b48ce714237eefc68285198030e77fd3d5bc325d769236ccf883e95a457c7ffc770b397e30a62761f1473c7f64990d33fa60d6dd970f337a3e98c7a45c9e442d0d33f66010aea53c6bdbde776f01175e78d1650333f4606e13af1fff5973d619c18c3c9c2b54e9cb18dfc030030fa694ee1e543813415dbc83b1b453e70e9f676a5a543b98e5834417651e4f82e4f20e33ea609891d5ad20f3437b8c0ec64fe95325516225a56c0ea69ce3277a2521621e948339bcf7a99b7822c8ea92e461461c8cfefb123cd672bfc739cc80834967ad9cbbbbc68bf4e30c33de6090ba1ecdde34bb196e30674b2929a5f4a53af56a6983c1e44d8f7eaf173d6283a9e3e547ec3416ee396b30f5e82aaf48a5238d5a0d268b1fd352750d6d350d06b127d4d5040d0d660fb338a9973f652d04cc38c30c3318923e09162b46cee69d32185b82969482d0ea53693298743cb9fa57650ce6d8ad11f546e66973c56050ad7c51e24388d96130a5759ff0b62518f0ed9359f51232e30be6f7d98a97e45dde0fa9c20c2f98438ee36d2945081eda195d309a1097c9e15752fde68249bc24b595976405af6cc130b952c5113bd24ab45d70266668c160a2257e4e938967264f624616cc9b577e73dab31f52f81133b060b6d2f37e21be2e9a0d1b37881633ae7045b2deaaad9067d5580fd7c2bf7174b1013531ccb082d9459e4ade39fe96622412985105c3d749f556b1e449785430078b71b361af12664cc160da258267b887ba902fcc90825154e5d55139a419a5ce8882e146aa8d6da90e95d70c2898f7edf4c7c64497bf673cc174419fe9a0ea7f37c6194e308c9ae751e1cc53ec74130ceb26f6d7a2a9347799607ed32df12f397ef2dc8c2598cf530e167b7a12523843090613d367128c3aea3c7f98f03f2b418229ce4d90f5249a71046368b5af390bd2427f8c60ace4d9a9ae7253f45904f38f989f50a7afa31f36127f31830886e855317b524b3afd0fc1204baa8948faa6d46f2118a4e4594f25bb73c26843051f36d2e69811049376f80f6159cbb72540306957123331e5a5c4f603c38b49d28bab3835a98fd445670e66f8c028294e6be59ce6b39e7a60d059462387d1f4d12e0f0cd73a314bed8a4e4ed948773698b103f39f8e45d62b91b4bd3a3049d9bdd0c9ddea7fcd81415d794ed73d75ea331c184e828c982af777756f6010bb11271baa849cd00606f3db53e9f35ba5e86aac7dd8486506336a60320b0f6926b95a142d0d4c6aae22da08f5fce799310383eeb0ac9c77a13b5f3364601259fd2c87285af9cc5818bc732e1fd7de6ef5790d3260610e336184901ecb40c62b4cb51a6af53f25774bb36980d440862bcce1d923495e6798c4c8b6c2f4162dc952ff64a6a2066e68218315a6decbf5679ea6626833a0058d08d4c0d10ee0f3a25180850220f878808c5518649f24990f931a6bfa0719aa307eb07c75a227e1625f63ad0aa981a36b905418dcde372ec70e16b373c3c6ef7d51010f8f1a38da013570740d09f8163734208174c3e3865742062acca2557a45c9ca2c55d758ebc286c93885d9920841a514bf7462de43c830852958b63f616b6ed9ef52987bb393474ba9d4682e831406e9177ca2ac6d895491310a43eaa0b46acd9808f9230219a23028eb683149e8b32482a130dfc5ea4f3c29aab2ffa10419a030041b4941ded7d3e4e41326eded53ffd6371bb9278c92fd846a9b14d13ea9138611caca7276b9cb71e38449e4544984d39d4d94d3bd7388b264d5adba0c4d182fe4c497efece51241cd8439f6e9a03bdba6cb24e5c9c084b92ce7afec98923c65370d322e61f034d244551239794e3901f21a5ae04186250c415b2bcb8b76391369250c7a4f9ed74bb4d0e651c298a15ea2a85c8892a449983bdeaafd841589b949c2a4567e31c5437b1991309fc895b311ae23d117820c48184b5cc9d1e6e31e43081f619c147bbb43a538c21467fc83887b712bc55690d108a3e58ab6f9113a5e486284a93a8c2ab91c3ac8b86f0a321661c86ef292ac07f3b858086428c2242c94fc7c0a41fd53321261b820395775fa3071224106228c1727e976bcec9e2dafb156ba90710893c5d0115e2d5d3ccb8d761c1f072d20c310c64e592f7a59b6c46cb990510873c81d2689d029abc70961f8339dd5889eab84103918c81884b1439cd50fe5b1c2bd9a2a9021088396edcbba957e3ccc390619813029b1772d3934d34c650908a3e8ac9dd35a589c5f760b32fe604a26f258a7dbfc60aa0f594aff6ca873530b32fa60c8dad1d2980ef3c1202c8eea76d0f649d92bc8d883e1e36513225c8a5631f221430f26257f2e6c6e555b8c0332f260cef693e38308614cecd9041978305ed6f3964b09a2fdc30419773029bbbf102c9a0aca4576300839313546e6a9520b7274f1059aae40461d4c215a58cde94ff16cd3c12444ecadea8fde1964ccc1a0bba154a7c98a8fca0153461ccc167496cfeddde229d37c830c38987b25560ad59fb3dcf806e3b9beaec7d3718351444e7a4433de93966d30d74f7a75ef9c9316b1c041061bcca2f4baa9f5f7e764ca32c858833174e4cfe78e3ce2924e95c544728f3a4a83597d3f9dbd550e6a3b1a8c9a753a9728ff0cbdcf600a71d1d76fa10bc8308339766ad3dff1fdf288cb601a3917226ad4ba332383e1e4255987ec1faedf1a6b1f3612a640468d87a8449165d2581c0a8542a130280c8360f97514c3130000000c1e144763f1589e29bbb20114000442343056343c1a261e141c180d04e3703014068403623028100883c1a04028748ea6700c8b0f2515e935d557e99e1353aeb200d9fcc6754ce80c44c8119e6eca14f2955f0f1c331dc4d506d130fd1664da9c86913a19b432400ec18bb911457805b235891c4adeb651768091eab5d77e70eb293d81c4e3c2663444d433d431c64abd4c5e2ac4d1c74d6a65fd83cc3375796e80d929b7b27a360695d2efaa2e71a4927e3b85bf4b25079595fbd2e12d5ce34315756e9604bdbcf2447447949e31e3d015327d4ce6ff8eec4b19a3a68e3a2824240fec1a0c4a29cd8fb6e60e69df702c238fdf5a221c2e4854653009cfbb492309094f03eaf9fe9a44aef6c32dd154193276cd51fd88a713bc8bbb72614d62280f0dcb61821b33646137b0ed5c93179a1cec5d49039ba3d9b0fda804b3e9f375fff67495c531f413d2479b2969aceb4e308b44c3595f73e813bb039d68c5d33d15d074468af96cdc2007853353c5e872a516d2328d89ebea6ff34492bfb54746e9263e4f46a88595c6eb656166e4b4ff48995183760f133699db9bd3f91713e84bb02dc6f0280d3e028b4cb443aae45f1c59f868e386af9883444a0c3313163316ca65789f5ea051f15275dba98b19f039901ed51a26979dcee3cc0057f7ce0115f98c7c50561c4c70dd3a131d688e4dc0c8dbf95b9318d566b8c1a7f13a1820aad3e997bddc246f0501819d6dbe2124d360abe4e6f8b9d368b0e6c9650340fa75193d3f6165c390a5645f63a4630ccf23d0e988b646e2c50832237d45d706f09fd133460c2820c85317b4b310e3f2236f200911d6195baf9b946e95aec8046f4bc1f070cb369f9d994f47d747526de2682ce100b47127ff8a3d2a827fdb6947e9232edde09dc9d118655613024789b870a8c4c4a9023cfda92f006b7e5c2e9d26fce8d3531917d86936c09666eeb5d85105f5d96a1285d0cdc6088b5a9ce6545550fc62a55a82a94c5285fea0553886d36acb8394807da585bc695904589de91399533c582c41d09282f086e43284ed66f127a2d58407d58c66b9c3921c44d3fcd24a15ae568a5ebe6f550257005784a41641645c8feea284388cb28047a9670165e2f8fb11a03a18ace39a114bd716a330bd6276af016d6933466fd716a0dcb83e56b4553be0fa08563cc701c9322b365491d6fe6e067f4ba7832e5027bb66a42fbf982ccc6768fc7dc748ef8e74372037cd9c2e33969a5a8ba4340cca6fee1aac57efee90f26fbc8d5fed521609bde34a2c38edbe02fea1995c710f46cd8926f3ee054665cdef666013642696a1346dce3d9b935fa5dd876d522b5059bc58d8f7019d0b133494c18577e1ade092c4b7e1b12250b93e882fcc097e2ae27503b3d2c0545f0c15adc5328f93e951e631230d6579f7ee5aa358ed8e6ebb9f50ef288a543c45723ff414f08891a6362b4a7c585477a951373813a589b3513eb6ddff4d1dc76fbea8e4b03563d4f4bf6943bedc84aeb862c66254a72fc70a4f997d631840619285488e43d4e4b0618555bf6ff7993cd9c8e83d4507d42df8ac79ecbe7cf2cd1c8d51ef005c319d1b591c3dcaedeee1909129f14831e8e934bd575423155c6daca0b333ac0a6288eff7190aa94d26df0fdcf706f610be82a7eb81c69d36165f0f97435d6808c0b1cb38c91ef6680f65f2e8219ef2f12c2412c80db41909203cb5da2bf76d21f0a6edf92ef1be0a0dc7415ae763989a43686280f82c90a64bafda406d829e67911fbce7e50f8c9d9e4886e2a7f0ae954326d3c32b98d18ea3780d645642a76850a5eb633d09e8062181b846d2463553631a11bde2cb0b24489d5c41f0e67fb1e1e37159e99ea2a66f93fdec382ed9b60121641223d50946ac1edcf085e577a3607d2305510bc6127b38b6ce897021a09a554ad83c3d3d47a05f281ba95037af91c7229f145b624c8e36600b5adef16bcfb3e1760774c91ec83d9675d492ff9d7f424f813ec8f343d935f6c7aa7cfc9c9ad4a26408f147452bee24bf1154bc1421e669da917700f30d08c8414dc3d346bba0a73dd70d13a03c2c17d109ed09a71b79f24fdb04b7e9f589a33452ea6200b3c60c75e108fb01d33f2326022f2babacee5cdd3a9dcd48a8142f5dbb74c2bae7f146779f26c070527dadc04e5142a12d06ac44d69c68bf20ab902b3411ca990c250ab4a8295f5cc92e01c46c6f8313e0730889f94103c04a271a1a9668810665cbd920688daa2ab0ac9089d20193eca01b8c49e910e2df46f68e6512ad5385b1363d016da0c30f6def5453d35149ea2f30c5454043b06f36702f4f07841ed2c652a2d80f84f250e051e9685d1b9bf4710af4ddd3b1d58fa546fbb26549d5cff4339a3fe2537e46cd1a727a1f3f31cace2767cf447b6a6c2b2cd19c2803d01efa9f731c22a779f5235b4e70027e715161c209e23408ee15fe61c60367185730afa1e881b4be630e3034af70f8f12a7a902977fa47676b95d68889364acb145d639177c06c402624e91f0de01b5dc7d81f6f3157d71c7eaa54ed09b7247420260af12fa8d1892c80b00edbbf50cff96ad8f070f160ba02ccb6cdc5422419fe282f4a314fc05b20ce8d96f4f17d56882b6ebf66059e8d107135521051db202c099b7307e57c67fdc9b27b44e03c49d1c0c37b7104c0a645017d4c3bbf39d81951e3fd03ba90de3ac1ec34e460461009e16a010e9b2fbf61e8843fea3b97269bd44ddcfcef2e4f50978aede1bcb4735261489dcb44ede40c69ab926d5ab441563a5cb5cd513c0f480d8247ae1b730a9ca315ebd32be660f6efd4c762259ef8d71dd7b3c4ccd7b401479be91147834f9d154b8f4bfd1783a4174d4dbe951736a5e4549c86cf4313a4b6b52a8645720a692995df1f2ddafc3fca341e4ef76ba08b57f15a397087c02db6ab1caa3c75426fd433828c9d4dff765c57a22196fac2487ea9d1ec49c876ce273a353ff85278af8ab87510bd80ccd5a2a3672c6a1cb965cc4b0ace8757a1d06370430b48829dc1d645c47c30e91d02628f193ebaaaeedfc51f1bda6474eadade3e208cee33da6295565679256c4d188b64fd6f31b0614a508f9efe35e025317eac65e6f1b12ff2ace24a92b0a39e8e280de735f5e47a8ea2a9cd582c7c9f913def21a62c0d9708ac19216f7f90ec15018c5730f95e1f3b416bff1787514842ab6e6242c89073f69b261dd381c169785b0928962393eb211141e728da5a1147b02fdc6cf000f60b8adde605862ca16e03b1bf6e64e4f14ce7584274415967a248abb91498aad5e26818ae60c8a3985822946614154bf53ec087e4a9f42de02d4853b0b5020a2650ef0c9a8866091ddb41163b0f292e0dfa48426251b06f839e40f4b681606e5e4991057489c916c23a1a10f111120ab4a0eea4af2f70dddbfec18ac75d8058ad40d59ef704ab045c8e0506e67794163844b9efd8016364da9804ac9febbadd75aff27748af82661223f915301a21dcc370238d0a602024bd1fd430247b53534b3682fc35b9613ebe0830be262ba420825ba247918cb8c481a0ee359042660b38e7d2380f61c534dd182473045b738b3bd88d0f7cee823bf926103a40ecf63ea4bd14bba392e0725d3e2de186a6ed7d2dd18c0184147882d7d74f20a07f02b5e7c63e49b7d6586fc4b740f60a502dc94fd264c5691ed44e71655069eaa4c6a162a7f9d272abc898c52f2ad1c7f8a443fa2a18d66d8e95035cab372cb177a34618be41c976fec7648352223915586f21a5d968298ae628298b34cc729542169ad6216c156184b117d152582cd571ac306c5c6660a165f72e05a864c8b45e53dd71d6e8f2ead82e65944f5b7e0a3d16f1b608bb280a4b308d129e041a9c05d1260d01661bba70b3bd1596006a0c0e61dce18377031308211085a10ce0fff7f2d175e34669d839a773b2ee70f4e5ac544f1096d2479c1c3d1e8a42363e4511da8a34528dcd5a5986636ac1f1e55b5597d2fb1c7e13d4884e80a663246dba34bad54f32c81aa4d1a664651e8ab850c73c754189b59218b021d5a99db2c9cfd4eda91acac26cab18ad5c587f90ea981b8b1105e91d4b568ba85fbeee8fa609f2542566d22726436032c6f448b381725f00d7f6da63a51e2e5bb3f2080587eb64acd05a38f7b5faa830ccf81e6d83f6050098080b0a6e49c80709e02b988b6e97c2361c40a28eb5bc1f3cc321f7b2dcfd0d10510144060a3d04a1375e0e0825c87bfc2fc3cad2be668e87590abb907db333a2fc263766775c14e4c38ac3f41d5effc1410dcf315b1eece3416c9780ee738e6d0855aeffe1097e9329fc55d3e9ca9087c6df6b80a17e541c786e827099ce4b58a149e602dc906d8922365610ddb843ae98151c14a236db415ee677135d1b7075275f7097a2c7db3723e3d758d21e5462c54bf262b17e835eb50eb854909cddb05de2a3a42e75a28776ce6f754706652b4c3cefe11a91deb97fe26dd998a738d10a1fcfa3090139df3e1792db3d3792fa1a799d5734fe19a0ed81c15a46af7498e80f9e7f4dc8631d6f358b4fb7ce6d8ef496d391fab6b460156a256ac6030b05a1b43bfa89002589276a70e0e265de40dc84a84e5c84865a088ac1bee1ec06abf228b313a4ebfbbf9dcf8943699c0804b3d9fd09db19451f95616d378139a8d9e406c494208525791223aeb81cf08efd360eae8862706d403ae2817bbfe4490fc935d1bb4936a7d0c9544246d4d18beb20606739bb92e84044a44f27a1510d425446305293c1ae2b314d6feffc1b34a3638c94be379af27d12542d4045a5dfb6c7b559947740d5a75e6c831d8220cd4cf8a7e00e3812dd86d3fffdd6d5fe91cdcc08177ebd8e7c15948781be697d412d29e519ede69afd26dbe7d7d40ab6ba565eea80e62448f634b9b4d3ec6d36086214007381ce5e45158e66f6c7617970635580749fbe6003ee96cc3aa74de57ea131aca128ed9258365d8c49e1ee9bebad8baea4ea045a1dd445eb9aa551bff1f35282303aaab55f3159c040cf6999ccc99b2c93d965f3a952315162c2efaeb5d8ba5ab5142e77a35ed59e91f440cfe965d6baa7c729cf219e87ddcea44aab19f5662da7aea85642e55cae1cac810e8ce49c94c99ccc2dc588be5b20033a670bad09ea292df6aed65a28001fb8419a0bfa913f9782bba69d9c96ee5889373a889929155af5a87cd6e6dc1f13309c0064880edea01dda712c0691a2bcdc7a8dd32fd9d35ca9da2a44f5592f67f7b44a519e663a2d9c5b17032bd5cdbeaa0d0a7d36e035512c2eb62b309c35cd4b9408d1eb36395c47f42b69baa83781988d9f9af68c9693d0a2cfb19685467f9358aec464b1a400e3e90b4d15b63f6c618fb20b0e7bfca8b3d223538cbe49551a50f524d8696eaa54bdecccda325b59721ad7040b6dbd9576c28307109f07c6a45f02ada4021f4c27c70ad3fdd457f540fcd71ed041b90b96e85629e021e26b91fb357836a29e1b315ff8a662ceaa88cbc2dae08ab4cab5781264a89cf863656dd54a781afef003aff17b00b649223c61b3fc4ed93210d4083df7f2054ad5f8916a11250776bb474afcde8070a03ac3ab4642a82fd0d8b21d44f9e76c5f7b4306a0afba43828c8f86006948b2a39793e2c5fb67cd36e357a3ffaa4b5257f13d519c84c7e16c014db804ae7b16153b2ab5159a22ee0a8e47d2fcdc0c58a6f5bf55b235a4d367064e0e4dc820982ec17c53e9e09d18cea18a9b89ed4cc921459958cc60da9cd53225ff0e63ed03076fc4a041e6dc7b4e4a14fac99986f362dd8951d8eb7b95d7a28611de36663c2ebd3653358ca005f881c3d9897f0a16e74072eacd5f118d9b2a5efdb9b7b9e891cc2303021a86e3a6c8a1e43f0838d29c226cf57ff04dec33e016d6a36252c36ea35849c8b21a9763f5cfa7921011dab0f91be0ad109e2b39683674bf07674f3dad160c776503f680490ccbce93eb4cfde05655613ba1a611f364a06e2c4a0adef6d9cb79d12ba577a44d7295a8af828c9cc08b0db413592262d83227468524b572695a32bb50bb6910c25f221d1e459e6ed04a760fa838d2590b68433940f58691bdd1f17e25022389e0ea0ca70c8f94b603dae629bccf5a3dd7fafa071d093a0b6d97fc204a4c3ff45e90b1cab00874ad7d756880c480951be64e1ebaca40f0d107a5da0f1d81d059d59379131bb2288c1dcc769827490f4395a5248b4d40e0a1738c6c7df960866c8818bf2f167307a65a474d0e4e7f121d42dae258bd8e368c5f2520a4508a530495d3ac908ef6073ff0c535069c532deb887e8605b2867889a39b28730467b1df045fe000e47e3989990ba1981973806ad4085165a4bc1593924bf2b67ee49a022238f35b57b614c78c50f3931289fbcca9cdcb914d25302c994c1975870e2564a21ce049701d4afadc798e713ade09bd7411a40a316586aa3726e33c138cac22a532ee7c0a93fc9aff14bd712bc8ac13544d8efdfef2e7843779aae61ce72de0f97e7088bb782d4c6e9896a9a7632a536222338323a231b66e690cf92f64e8bbb466abee417ee2713178169814ce3f3cab878fa7828da4eb68c204976587778f0215358ff93d86c9f1da7f5c1a562049cc6a3435ba8543f69b84b781a58a98b04a45c9bf1f81e90db6a5a88153d7df27f23b4aa50662c433b04c5b20103079344120d91c52b119c1bb22f9100ef26cc7d746f92798e276ff8e7ca09fd068f4be423fbf992c0f99d10f7766b89b22c683c64dc34cc33b35d65871eb3f821d063403ed749c9bc0d400fea5c9ac30075cf431c69a462b1a7b219f68ba782255c7e683e988dea42343b4deada812efc2515b95025b3c5dfbd1e36217cb6fd280f0ea49fc22e81368b2a4fc6950ca1ead6c41c756b5cae6fda17481f4cd02a363b2c0518ccb650065be36a855cfb91c4d72196970e151c9429e25dae3d601bcbdea7a8468b85b3b2dfcd06e1a4d8f62a5f9ba3c8de9dd1429530265b37467ac74b3bd920d6b3e86f06b758f4fdc869a213da35b2971ae65609c05d6d6084e0d06e1c838a1dfeb6e0e9981011a7c0e427f88380cc0c2cacdd5752df7ad611a0f182d975ce17712a9cc292b30aa502c2ee3732fa41b2645d9eb76b7d081b0a24508b99c398440d7745229514a28e27a06002167b822982287535039c5f900163da00abc06453a610cbc71752de78db24f47e2841492a81119999032364964a5acf9aec81ee548df6ab3843717447163c286d6a084cfcd2b9f4dc1449111b2b9368afbc09e623171271f8f9ae165710844a9f0f6990e8a43c38f90d9b11560ca380ffe0eb9b59b411e0f65e1c8c6e28f2a1e8049692e181536899aa314129127804e3cbcbb56df21fd5be2c03c036039669bb355c3162a2d55eb00a0c9ee55fdc4e397f151504888d76721fb5d70e156d8a1258c6b39e6bf19bfb5130b57122f4b3b31b9d227e542b7e08768b3ddfc906067efb8909062ae0341bb0bad6e9bba5ed97ed8236beb408a1c2934f37a12f3feb891a1600f14ce07c2e6a83553293ff1441a2ad5e526a9a1cc1bfaa2e0a0097797cc20903967660c36109c602819029fc977350b10d505799971085720cb2c3d0cbb9cd37ba15ebeed57a3444fb18a2c4a10f80bd3b80240124d00086ba8f8576700a84c17239c5732c7a5725b5a22133aa23f7a9d9de905924c9c82c19adb4ea2c5575e61bc497c11f0c04cc23a32fa9a003cecc551d9ea042384a5f201ad5cadcae108a6f41aa960b032dbbaa1a7911ac62da0d819b7e744fa4ae22652b694bcd044772a323281d90205c5d5ee210a6b82251577c6ae0d70c99148ada5170d36751954b0bca23d7de4e8d8a033919278656d6e21f25c700041b0d6296e629f5bb8405c4ff6710ee00e56c1f7a4763b50ca5f061362ceef80e1904b2c69529064cc6b7c5c548fb3b64f259dacf03b36d9ddf5429993ae44100a51d35c2596505a5c040501a0f17751d00d8588369aac0cb8094d3e8b7ae995e87a88d9a3154c393762ef11323a406759431b868e3836867b8756c5dfefc509d8b75ba9614bb62e242becc15a7e25a13b623c0906d6a7a563835424a72c5f0f24e9e428b113c6901459832a3faa68543122575a27282e598e4acb2094be296b260f68461de88f59a22f600c0d4faaef59df66ba38876544c45fbcba2a6fca748dea1c57eb3c297f76d2004cd7a6d2b1001d19559189f29df22407539e9d4a396221bde703d8a83f2aa3c7650da8986bf9018711c782343210861750e0a1ba40ff641ee85635ca4642a6950fe3542eaa1fe3fd05ebee38f56d829c4bb9774623c4aaa54368ddc6509a01ed3334701e80e6774965fd7ab1199de4f99261b602e0d6549df6895d56053b1a492eefe45a0f23c9e58b5eb4af03fa7d6d9ef477a23754125a9b0a8e48f0253de535443e94eb0d9603118d0079697ab395ca1f16c9160c73600dc4bce18130aa31073abbcc1853ebabd656e8d6b432b42cd0597b70f674ca63111b6a2ce57ce8a60fe24597b02aa58fed7bdb80f5343cdfc92a225e8b02fb39eab7c6ef4e5f3cfbbef25b67bf053f58fa5fdab7c4ef565f2c7d4ffaa6a45f91a57df278f53447bbd75979c211925976126a1b251a27ffafa7fb836955d0e6f734b63f4ee894414c595b994e62f69f5dfc09093d7a5a1b410e66c398bc6f4b47a5a07eb14f0cef5a8ecc660f3f7dbc9226d7409b5368f9eff0b0b0c6bc9ccccb0347cf5c992bb49e631458b127ef44c6eb62acd7b203d62621f2d79834a481646816d50cf5669330dbb8d911dff6730373b8ff0674dbd82b6d95485bbf93ae338754ac7d72adc3897e2c41f29bc42a124704c92725e30bc7044937127d24963712a3a2929a8442e22bc9d625924d8c4625518955ca5383447c24aa2bed158073893be9c9b8b4a6a3edb07970af3dde535c082d078f661e3eeec771f892a7c89c475d1e2d3d08f5f8eb61d543aa87411eee95c77615349bc7c523a687821e1e3decf410cee3fd75b7ab207afcec71d68fed4d21c73a7b1fcb800ced88ae215601f185c8a9718ca8e8fea2304402136852e773ef5cc6dc38154709d11ba8328f2ef69ba23cd8f38e0917f7fa4d3b17ab02d6d936067d42722c0d223844530d6951c4dbaf4996653eef3684b379659624809d510afd6d024c57b6b4c4f39aa96ab5c3f991a6838adcf05bd98c7e00db019324c72d133e925ecab8105380e606e7c17f6d8a48295206ef1a84518b7de68e2e209810874e1c2343320a40f1b4dbc21222d3592f062ed80ae8c9a5e8611c534ddf0bacc3d815371278144dc15602ca81bca77d12b599654bb48d3530dfa0894c2a0cec0a33f31ce4b295491e9a716dd2b44043f846b544cc29aa5b420353b830475b0ad4b1d921f2742b8475a9a468637eb2c9724e430dd83247386e389c7a2d01701a0b6127b160a955e938c289cf05c7b3e2973a37ce871adbbdce2e1ab2114a9165a43179437663c6b010db77abcc2786499a1254e337a76a58360c54210ac9fc503d6965dd62437cf0db39b438b5e38219f22413635d0cd2dad7ebb4f8f680eafc362daa48e534991ee59ff25e409cc2097a5bccfdbdd82d9a0af8053c796d80d2482e28b3e47db119aaf2e005942160600ade7379ecacbb61b786d23e501ce9966c9715392a9d0c443b8730c07e787bab84968c58414d7026386141914f631fdbf871078f5baf3e6d6c3d48e16f255407b8e434084640a7c4716e1215323c2666ff88b68b6bd9b11a482cec660cb8586519420036266ed50e4a46870e6a49be064bb1b2fe617a189964cc13a7b6f55350c9e47a48efd563925ba6b746bf34499f85b1ff15f89f6e9148fd39c4839b8f5635a0226184a7aa63ee8d26ee9b4522bf0612846a6fa71cf5a4e7e95cb70bb944e8f0a2083ceed422ca8bf291d12eadd76a283194730311534b83d63c31940547b61049726078751186f9f6808515936e46c375436d78272173d80a20eb96efe242572ba38394724d3b7affe1894e34c4a46804be6ae3a2ab85007f400f4d00ab092d0c802ee0f9f1936b154aa37814cfa7de1542c0e2e67328d7387b3b7615ac044b68e4871e358000f4eeb2736a2abbb01501f26812c729f5cfc034b281e89c10709c5000e29e3ac5c4e65a59f09e34a7257560d1493dc1669ed86e005e7c7c2eed258ffa9cca61f7ae1d1cdb600b5bdb86d501c78206f056b279093c4bf34f53a9f7b40f8065ac623c7389285355f30ae06b429715bbe542a81fb1db278e316c68bd5818da16cd9d2ef063ba5026008fe0acc34c156710c480da7fb0e34668e5876c504a10e8fb7c4e6f07238189ee2d273f5b785ef929d876f94422645f08290457f9b1233676e2b6d51c6525448b06c9988d009923c72398bf297d971300f41a14f2951fdb95a06c4961c8ada0df257e68371bd16c010e43fdc3789c5f671e770f36a86440c45758b44034ff1b16454adf482ba65c0f93c26af075296796c02ff744904ef533db4090cd329522b2053bc24489a4ce527f9c783c227482d9110368caf03081ed464811e98626af381b718793196e6a3439e036c48d52360bec0432f046263ca3e706345dd7c283151ac431b06a5d9bb21278077bb5be32ad756c55b166646b427c3b6a6adad3b7dd5225b3b9af5a103a7d6e99d328fa5b986427b6484f5c5b1a92390a2607ae0a540a68a1f5e8d9193a4fd969267aa04b919c53642753ffe937704d27d1237467a8bb68d66c881de86f74dab694b0cc64ca809fe4a20bb3b4433a4e6be4a3e31d41d3fcac39078512da528f71927d7dc0494c51c15a490fdb2b8eb667cf9e5898d82f57384295f5aa0d761a55f0915048f53ada6d1515c04f93a579fa1d761a47a3639708c398914f465a8e4c3061367ad1368a6cb4da48f46070460b041ff58eb43d232355da918aeb9cc26f9cab1ccee9352a83eafc84869b782abf711c145aff6ce44d10b77ec1ef51193420ced8dfc141387e2c201fda4046f30455b2ab0ddb4036c4541a4a690e27247c29e9e5bd67e90c1627d3e7302ada05801bc118252935b9d6fd729c58552b9d376586f1308bfcccef22092febbd8ed82b390135e3154ea3e7e0fae5fb6a088f70c5c0f2f1d711ed60598fb8e9f5bd388644d7b493591821bcda8165c9ca8d95185676b71ace0a438e9585b482291764a89f3a65e8d499d080b48c851b94e44a410f8d8c0407daa6485209474185cb1711ee3a1cf3ea0dfc4645478157512e0433e4256a180698ce52c6e88255648aff76ee29943c60257aed1187b9a5be03a71ad645440c5c22a8a4542ea834f2f1362820b55135788acba4ba50956084a7ef114a81f87eea84e022f79cb9e30175a4804c444969a55df25710177434ea8a4fb48337c90f0dd289c71f3d0f00214229f0e9a381ce1d38bdef8056cd6e2a67e19850308a02b5743f285e818c56861ea407b2313bf1e9cdf8ab7193622de702fa0e09ee882b84e3c2c085d42f326fc4c423f783fb2a93b223540823bdf4f3762589803485210cdc40243ca219683c6a0a372b6b32ad5fcc174375c931adb1a6d58574d697e45a66fb79181559c8b71f816a90eaad50be42f3f741fdc7057f5e9544c8b8e4d94acda85b543398d3038c13f3de1697219b3407b3519b8d266024ecbcca858d4c3d2788c22b0ddb80641b18ed807b5081df10982f55b10c4cc49ea976c3cff91c73449f665d5134702930e091782ff2f88d7e5ad6582a0d0492edc0f9c3a52f57c00fb20f08e607104f2d0b7b96e92f97211632e90998c85d0fe722d2adb6524728f97f6802c85ea002f3b884379fdf4f23f8dadf5c0582ddce2ef23de637d3bcf6fe6e7b6b495e266b758f847d9d83dd179e38705a9ab87fd8fefb850bb4038a460987e2e52746597357b121ba6ece14ad9c14dcc082aa3d8a5320ec01bca340dc06cd1269c3bbf151efff71bd48901bed4d84905f411f3baf9166d0ab7cd4fb7fd40b20f4cac7bdf161bda88f355d7706d8eb7d4ceff8b8f11a2fb87a852037dce3d5c5bb25d156d5c92020d886c6b87290b213a4308548b3ba0cf6de6190751fc72032ad26acdd500fc1a720eaa4a762a274048c70c3b8cc2c3ed7b7deeeeffd78ffca3dbeb3f8ab9ab7579653a85aac3ff87e421a62fb19b6d99adf42cf146c9967a0e52381cd40bc2d0b75b7098c37d644725581eb609e8cc5c044ec81dd044c49389bf0a85165ff8a24e593f4fff04477fc0c5bb2de8c423ea20969dfc5d95a4db48b8a18bdcb601bc2a6d66c0700d63790155d884e9b4fbad5242e16906233635d2d8d453fef428faffac90deeb00840d88de0dc270024710739a621fc6ac206054925b84a81d3e89f10961b21482f37482a0fe20d0842ef222aca9c29825469a7d5bdf6bff881e13124dc0c6e5402750450015da19672fcc76340c634d8037cccb65a58caedaf501b047c3c69b794a256b5857c0881648784ef3e1517033a6d15e33111774898956608481597398027af7cab65075a5ea87f8909b13ee98a0cf1a40c443a51fa3c0bdb119a11815c7ca9f7ef86f13a1c6fb4c8038c7961c2320aeeb4e758895cab3cddece684873ad1cba72cd9aa3a40283c117722a0d197dfc71833ba3b1bb164a52f4fc7a1266b8a840937a33eb338f33360d475859b1d0dfff8dc3f88190fc4a014cb31b55748203c6ab6fca35a1b1e64c1a3eb2217454abf630c48495fadcf049f13e3306f652bfd225de84581b01bbee778bec44af16e551a87014bf6a1037c61f558233492dd320cabd87ea73802b3237cc792d2e520fd46380fc17e089cab21a84e3aec1e561646071b01731ed38f298e59c784c6a4625ac65c1123505716eb5e22e8ce81d03a7019465d22849273208a9b5b722f77bd37353bb48349da92b91bae90a610fa435d58f7b4ada1e16909c43de3fc97e094c3de8843355c5c101c60a0767679c823b7d6b3b7bde87ac1f262c0196de7c755e705b049e05096ca9a6cdeb141029844fe645fe976a0553c32ece269c82370576322f4ab1d4d8858dcdbef59f6e2ec0370d2ba9f39b8ae1e30766c2cae199eefd86c44759c75f43a2eeafc5fa4092f909c967278f689a8384cf8b0dd3e040fe03c2598811955d1301ebf2c6985e70ed978b83e131afa13c6af2b5cc25ec6e97733292be305b9a537430c2a6ccffcee4904c813933dfa228373af89f71aa0d6d216dc6d2ffff748a2bc0c527ddf106149a52584fbf05a780b8f179782300377280716d916ac67813ac52964e22bf1f453b7595487f3fc078e24c4f35cfac6c27ad708ccdab1bf84a8969bcb8243482399106f3b0a3d280e4187136b8e65811cf974eb94ab2fcd433aac47ddcb36e61d4c0f79f2cf17269290f1f3159e2f75b1f0dee813eca4021e89848e0b3660b46439330f0f0f0f0f0f0f0f8f3121b5b54f48424a524a4aabd75247e420a524a54c2989b7483b7a3a33f129a6c9de6498f8040402c20bc70b970ba529194ac5532a28a5c7a80127ee5f214f5a8e783a3168c07a4a297878ca32d5b663164c8ed729a9f85bf27ae3396c6c60c60ccf614305336698a143166c66448d9d4468e4d7c4823fedaff9a34760c189ee1077731c9d745baf489ecac9cdb74757703a42ca15730a19947dad605f82662425ace3dbc70acecdc3d44790ef216dabe0934a4a664a4e1e3cffaae0a444492b62a954b09a76a742aca8bd6799252a785bcb1d59a994498c769c82ffa484d211377595af29f83af9a33f9b8f1252540a6ee2a65df797284953527016f4925c3f4fdb3419052f2612c54f6645c1f9c5dc94dbaf34e8e0e1e80805abab13420e25bede9b147480824d136356ebd99fe0d4fd57f637992df6da800b68514ed0e1095ebb2477ddc43bc1c41c925f98162dbe419c603d922e696b494d66cf2698e469a1e67e5737d2046fcaf467d7f6c7c8e94c70494fd9e40ea57dbfc504aba5c77b83ada714d24b30392c55292d1ef27f8a25f8ad0c169e1daa76639560547559b885790a756180c60942072598bc41529c1c3ad4e23e6346c724b8b5f8e31b31eb3eac1a6934eaa6d021094ef848c8e1679d69523f414f60c60c2f3a22c1d786082a9a6b4ec999ea80049783526d2288fe11bc8ed021c7ecd31bb2c8118cd60ccbef49d4434f7e8398024e021d8de034e4dff43da943ca0d6211e86004abc12b8bcceb9e47c72c82efec5082418722d8efec7927294b29c9b023119cea94dd4bc6122ef22e0e175d74d1ef051765800e44f01526d3c4cfd095cf6c41c721ceebcba2eb4e372ae83004fb1ad2b43f3c2479ea1bdc51082e67e57491a5ec20042ba23db5ad8e490a5dc0050e1a9e6c98391737726cc720f00b49a530e5a2a26e0c3a04c1994ab595f54d67aadf11085627e5b2720b992de21d8060628d797b55a9945448c71f38a1ffa305b788ada1593fb06fb62629546366d3fbc05fafb7794bb0ccb4f9c07e8c6cb2d4b4072eaa78aa4efa3cbd5c3d30da79a2e7f4f595369a072ebf267597206944060f5c063d12ef2f596a767760b4268d3177ec16f5abc30e33e8a8c349765042c7545a0ac90e3af03958c88a215f4ea2b21d73b0191d72603fc9d131258fef9713904003a0868e38701b2c8d5c1711b225b57ff1451b1278c1c50d0a748e66c0052420811933686400ced00107f672f2159d624cea94ae60a1e30d8caba958a752c8775d631b3adcc0d7c7e049d3db36f023838aa0a116717b3674b081754b225685a05e035b6eaf6ea2a63ffda506d663cea1be7e3f8f9b062684a42fee6890adf92fde810636e7e5a4824cb3b45981a0e30cecae8ae6bd3bbfb69c02860e337071623ecd1c3aa59c45eb858e32707f994d591695e4c46fd0485e943b420719b84b4dc143b2181c5c181f748c814d31668b90f24bed88e4b0a182d221063ef2fa8dd96ec4be48018d30b041481229e4244fe434707071050c5c98fa4d41a62b499e44838e2fb09147d4d94775f111aa91c44a0076d0e10526ad28d15841684613e902a7b5b784a4bb50a1830b8cb6eefc93906369e8d802a74bea5f67ffeef4592d30aafbb3d783e7deb4d0d09105ce72d04188125a648c3916b811faaf6a21bd4d4cf10aec5a0c1dc945a9521347d46105365db2fe0ec2bf62765805f6ddf4e7385eeb163aa8c0bf7d324b093a22f7d33105c6826e0da9e33987d0b09156061d52e0af920ceaed453fa64a0abe880257a36e528aa8a3aff324b0fb4107143ccf5fb5fa136dd0501e743c81f7495a993a7a9ac72cdde81518740263df1aa4c5b1bca63d4d60438f6bf64d4a3d05bf461adea083099c1cfba469540c9a7a61435b80bca0000ae00e1d4b6092d6e49a827e97b549097cad779654913faba808858e24702ad47b2d54590ee16f193a90c0e71044c8bdb1f65dcd11b864392d57cc15b91742860e23f0b529c4226e4811f8c937713787e7583a18860e2270c94a9d48d14e770c818d1fb1b5e4256971371a2dd00f740881fb4fc24e488aa03cfb0e7404815359a966f235d79368aed001045e34770a399ba44e3a2b56e8f80117fc3d04b52153238d469d0e3a7cc076a4944a074fdd7b658db416acf5800d21966bcc53513dc303fe92f6ca1b32d4b10326e6a6354f162fa7d420a043077c090922ea256f8624e580af983daee4b30bcf260eb8bca8a34fc449bd7f75dc805352d3fe7609ad4ad7068c52e33b4144936be91b1d35e03c09fd697f6b9dc65423cd4632d322030ec841070d18b7affeffb190aacf8213622a88adbe27adb194051bb7bf2bc62ad3696e0660c4824d69539e64ab1efd9297b0e0bdd7df630c39e60418afe043bcb451ff25e84b2379d1e45cc16669ab6822061932556d048c56f0371e36415465d18c16197080b10183158c655325a3dadd5b560b2360ac821dbd92ae2fa7a42a7acf8dee95ed2e239b0418a96054a4c9c9f2078931a660a082d5089e9d95a2ed22458b2db4d8420bf30818a7e093d2eb2d49957809cd14bc4f4e5bfae40499925a0abe45e69d2521318690cad17e72dc80410a368b7efd90745809114f41c01805ebd9eb7df1828aba9fc11005fbaf41dfeb4b67356d8d2c2c1a80110a2ec4fbced39b2b928ae2e8030c50f0ad31c7d4aaaf9553e4e200e3137caea67753be9a2fe8a400c31368cf76b24dda453db5000010303a91a7eeed0ac13c5e3f80c109f6433669e5c1e2a9bce826b86c3a98ca21f754a4cf8dbe40299a6033fe691f9591f2f51f061899e0da6fbf52abe4fbab6082d3bfa395426b525a62b904ef19724c97a24e12ffa025f810b47defc40ede2eaa042754c794bc36e9a98a53820932fe645162b973266b128cf726a96ff142352b0926ae7ebeedbcf4b596014624f85361a75b7df6714220c1a5b6f4a4b324791e458f60f3ed72cc19b9bf73c411e40fc1bd73f746b0a63ba3a6902445513a23d82a4b5dea679d37ca4570327da9a4529922d8a094f64f113dc97037115c5e6f48c132e76bf8886025e62b7dd63985db7b08ae93e98ca9534ba5983704db9382f9067d39f7ad8560ef93daf2b28d105c3013135372b389b006c187875022b778cafb7bc31004bf195ff3dd0895589a0ec0080497f306cf3c75a1f29a040b3000c1e48ea5cc2649f78ca32bc0f803fb9927e9896b69681f0330fcc06898876c17327ddabc0f9ca5b6564ca67225e52f1060f0810b9195d734e7db905335d26ca4f4018c3df0b962e6deca9962d10327a964f2daf423b7926aa4a5d4346c24d30318796062aac98a936e6cadad9176c34990bc0bd4010c3cd89ff95fb91e52793968b4e0468e25598071076e82d4d518d592aeb809fa0476605c84ceb7119279bc8fe100461dd8d2a419aa555474a7d448ab1bc0a0032b1a24a49cb2eaea8e36ca66ccc0d145172968c0161478038c39f071640ae197b264f2a01a69280032c090c3b61be32565eabb730088028c3870494d44dfa4a275f90b07f6b3a62447ff7f48ad1a09da8b968041018801c61bb8f83d312ff807bd34ddb08132100b186ee0b754a760992f681c2260b481d32f4fb596db647c370301830de5a997875f673c598db4b741e3d0c05803a7329e92372a4e022fb830247f80a1063ec4cfb94d6ee6268ba681ab89f599a496d22112c3051868e0feb285b28d24e35ab600e30c9c45aad211a692d000c30c8cfd071962d41ad174712307a2d18243a30cfc7b99672b4d3949ca910c30c8c0e6fb243379cebf51df301b03373ad2bd2e26494a4462e044040f7d15d1a46a65038c30f09541c8b6104769a81b0c7c753279426e48a9319906185f60fb5fbc62975a47fc6b24046078e11113c9fef9ffaa3287d105b6d428d51f5754d0ae6aa4599a4ee003341200830b8c48d7a98269ee18ca13dcb8a10005cc98817c035d74d127c0c08c195be03d87de9e28e1e96987a105f64cc7d041ec3e30c0c802e721079545971a4d8f1d16d81c796326216f185740e74c1db28bfd56e02a66f68a0ec96973ea0930aac06bce6221a6f206993354e0aa468b0aadf89a9d62028c297039a4d32a414bff27111a0f6048814b49640ed31b31e8e93900230a9c087a2fc4d39ca43d9f000c28f06a6a44ccc94d5d502a3304184fe02ba69096f49350e67921c070022342321d794253232d078c2630f9c72af37ba8a568aca1000613187915d4af36a95cf5daf8c2d1043096c08e96fa9873df35d26ac050029faf3fad093d31a5bbd448c3190148f068059bf52d66ef901923bf1eace06aaf764210b9c72a188f9ff4c5948c9a6378e0a10ac62a77084a4491f719d3399a0137f04805e321b2c6b0efaed31954b06dea962789a6b47c993123070e270117094fc16a8af92586a89a6448c1c0c3144cce7275f3a8ae79548f52f06ada31a4d51f7539b413f8000d16789082bffd0e3dd31e09ccf01805eb39da26ed639d5b4913f8008d2f3c44c1a68bf729df42279d6a90808b1b34be7037c1bd4728f8ecde762a4ba06072e61a75f22d9fe0f45bea88dc9954d248199e073c3cc165ab77d027833c61f94eb0773a832cab0921c939c1b8baa88852f24df0b9e2080daa214df0216c7754b724ef0c658293a63ab2a6ce23f53e98e04ab9e48b1ad44ab29c4bb02151c342ecd38b765b82c9a92a497ecaa2c6dd4a30394d7e68087aeddba504374a2d26097253fc5d27c17f8dd08ea0a5e67f4a82cfa8adf7613196dc4d24b88ffeff9699ef7e3190e0e4782c31cf1174102a8f60d7f3a71ba5c4930c298e6035d46628651db61d6c04971db7da649fe7df8e119c4a4149b3d32f82ff68317ca32711324811dc774e7921672d119c8e57e90c4ac8eab210c1a9a484d4f862a534bc1a695e3c0dcfd13772e038880bf03804aba1f7c1c79432dd202f52d080b3f430041b736f8e9c4699f6da0bc1e656cb1d25d64ab6122118f91d6f7d62e6cfb11e04eb112be9d19f20b80c49738f504b1de225106c929982b0af511b6904045f7921f7e9789327e80f8c6adb68c7eab7d1cec30fbc67045922aa059dd85d7af4e13cf8e0b1079b31060f3d9c7aa33fa5e47469c005b420c7068f3c9c071e583d3df244526d2a6f907fe1450abe01382640812db4d8a2015a64200236328064021fa021804a78dc81bf0d6e5b25a2259196871dd8fdd7f50e0f41e3df1e7560cb5377ba9c3f5552e2c276e0410746e926d55deabf23a95c0088e03107ded42c4ecc7d3284ea75f09003ab6a31d364c7a0db937160d399507949b5396c64408b2de0c05bcee94ba998ea2f21273cdec0e7ce23fc94d64d88f90624e1e1063eb5e670ef5c9a438ab581bd931f6a4c4653a173b4d8821cc2830d5c9a103fe4f4e59f7d6468f0580397b32521c57cf2070f35b0d92e53f576877c927273f048039323270d41b99fb7bf72f04003939406952f23581eef68b4c1e30cbc66cb6baa63c94b9d3703139210adf71d595a82cac0ae07e193840613912032f09d95425afd6360f2e4cd21aae7564c550c9c309df933b7835fb084810bb5b2d21002031b9a2f495745be53ff17d8531aa4b5e6eceb96e405ee5255b6d6dcd0340f011e5de0367b68470f1616e20227d2eddc37d85b6074eedc9c72b25a60ec46d5e999ba9c72cd0223ccf46de5a6e9742616f80f5a91b3d66fced75c811f11f57e5267d660b202a355edfd29248f6d972a7059416d95ec8c1a3c4205ced6f2b628d3794c810f3993f6bc9162324f57800a1e52e04b480b3d5513a4f5ed110576f3e514c735a45d4e99c0030a7c861063984930eb9026e4c2e3099c9b4e22688eceaafa3b81dba429a7984a9dab83155080210ef36802579b635df44d954ce916e1c1043ea46c9aa563e6b10446dfaaa6bf4a15aca246f050023b4282dede5df90725d982471258f7fe2816af3ddd5624703bdea7bfedf4678c68c1e3089cd064fb7d2d2286c9088c29c91a52d4eb0a1e45e0236e6886c7495a821c041e4460834c49176c73fe7beb31044ea60a91738a71250979088111312747b5adcc23084c122aa44cf22a0f20f02121c7b6cf4954f7a6465a0b6ee45063c3e307ec7dda34fe39a66e30053ee0dd83728baf21dd644b8db413a4007909bce0225be0d1035e350615834e212c49be465a5981070f98aca24b34da6ae51e3be07a5dff4ab958238d460b6ee428393c74c0bd8f10aab27257ea9f032e5db598a794e57a4133669475c10307bcc6fe2e3d6ec07e5433559bf2353da7461a0d1b393c6cc0267d6f955c9e46b68b9c056e47c3a306dc45ec51a5ac2d66b088c08306fcf7a6a74ee9e324b8d135d02c181d3529ffa859324991460e1c292841eaca400c59b0f9a2b3a59be853da353162c1c64cd66964ff38a41003169c46ba9c41ad826e88f10a2627156a375fd4158cf4aaec20820ac973e7857371a3015a6cd182193368588c56c46005e36b1152455256a95b05d7d93dfb3e6570f71055704aa64bb13685f869cc18a960c7dc3666f7a9fd9ca382dd08c94e6737113b055f6661f984c590738818c314b9aa55e52c8dd1d42ca34ed618d26dd6766aa499c0868d1a34ba485cdce1214629b8bebc3751ea79498514c4f4be979a6246c19b50eaf52b8735d2708b1a39b8e0008e2eba5080165b68a1c5165a68b1851618d0220311d0824999ab808b220a46a407b5cba6a2878c8682cf2721265332573140c1e51845e5ec4d9a945e7d88f10946342519ef3bd4b37ec4137c0ac9b736d6a89887189de064ce75fa3ce48f5613ac430c4eb07e9e3668d9654e21493818b07088b1093ed3636a11a62e52e50a1b626882d7789692a9a568b14506228003c71a62648295e456ee27caf276d4628b2e1a139caa84549a218f5ec9b900a315625c828d25d4a5b78cf55a1e4b1435a9d363414670e4701c38ba125cb692b13b6f55de2053236d8d023128c176fab84954526692a449b0976152d46411de16920497432af9313b4511033122c1a9bb2074eea0b682ed376cd0b03b400c4870d952ac8b56e3997edc0208311ec1a70f96a643e893f96b4770324cae5725a9d9444412a3114c6c537aad47a6f4d931825127db2f4af61c82ed050d37bb8bb10836e91c159328fd0e221e85188a60cc738a693fe2798960f74d88a4d01ef48b880836289d79b2e8e468216bc1a1c145dd03ee109c8a95b7c4daab9166086e4da84ea9757c73eac210a310dca53549323b48fa3d1182f114abb64f445cd42fc41804af6bde71b437444e108c972a59225e786bcc6ba4adc0861781e0be53d49ecaa2e91ebf91022fc40004bb9e556ff1b56bf207fec2bbd2c8bdb30c215088e1074e864ed0aa8c90009f10a30f5caab547d172ad97748d5c420c3e303a6811e11b74759a7a0facb85568868c9ea3c50fc4d003a3217bd6504d4b22796083f0902e3f2fe95c1762e081dfd35c53c29412ca3bf09691ded36f8e88fad8e18b22aa73fdf31a69756077e455363b9d0e5c5fd4117aae29a84cb939f0f6197244cfb9dc532907ee2a6dc516370b1762c48133f5a733f7a4706064ceafb42b421be30ddcc4cefe65b981bbe0be9e326d884fbe0d7c1c25b24ab610830d7cee0a524f6de7b57e6aa4ad81fffbecff4ce376e5500397634bae1d93c68d1b9e062e9d4efa13cfa0611349f7ca83e40c7c9410724e976fd4c5941a696660e4e94b2252ccad0c6cbcbe9c734ceb924a9aa3b9487e821b9ea39c200619585189227d6d53236d0c671e59694a2d0656638ab9c9ae114134b00431c2c089b098f64352cc3e18f88f16169479ab4d8e91408c2f302aef87d2ccb92d6e9404de8517f80aa9dee3b9f1235d606c94e609e5f5e29ae4029b73cd6d4cd828114f13630bdca9f10c95a216626881510b651d923784329d2cb0164310125c628658e0748a3997a8a0235ffd735760b29fda6ef2743926252b701f2aed2c998a355e57818da59134354becd1392a303a07d3f9da3b2429b11a6953e0c2c7ff3a6a6ecaa954234d0a9c950c49a7ccbc4446a2c0059d114385b4af13d51a696b218801052ed365ccb81e47984fe0b2b86aba31218244538e14d810410c27703a66a769319926b0a33d3f54d289099c6b6b277dea2cc325303176895e9126253022b609ed96df22799e04fe235b4e503b2652be91c0a5da65151162e63ca523f0f6499d5263954da79206621881cd74559335bc7b3f5223cdb010a3086c5ace7fb7691a3452fb8d3b33821844e04352a9e933aa246b085c4a7731e9979043525c084cfed1e29152a74d9fad915670e4c061e30c023182c06afddff5be8554c2d222030ec84004baf8e2040f68c08c19ef89860abc701578c176851840e02387b5c6d5fed1efb161e302c609317ec0a59b6c95b4e9bc9bef183ee0eabff7278d27460f38d339692d57a8a044bb80183ce04d73e59113a4694b290d2b7a88b103463d3b2fc92b0fbb910ef84c32e68831b988a63a07fc6bce5477fb49e4d662e080338f9c5493f73760e409adced33f1862d880039c08cf9b25e97bec3e158cecf15477f5a5af4605233f6ad67442648af6145c060dd994b6ce501353b01edbc264d2e9631d96825196ddad4a65fff6470a3e68d2ae4e3712832419551b05ef31d7c79052ca8f1aab91562307172b30d645a30c5170a9628c1d42893a335d8db44caa38b6033242c1574a224529710d3223a0e03ca449572363b02ffd27f810ddd5d77c927d9072514c8b0c38c0bbf8220519b8fb820c4fb0994b748a87529f773a199d6092f0fba821d489dcd5066470821beda5bdffe287fd65a12063138cfab497fd2fc752d76568824d21fd6eb5780e299a8c4cb041bbc44bb2490626f8bf20e4e4d23b694bc4041997e0fc3b8752d7cfb5ce58a212ec45d7915ae973ccdfcba0047b27c4d742f2c89804fbaa27d126e73224c17f8dbd28cfcf2163908c48305a2a5f8f4e823e2a03125cacf348224ddc2ec87804bb7efb5927e9e4b9ef0826fa9b66cbbe89a7f346706bd9e79a274670275f456d1cb9d79d5d04f717ab4aa915c16529a5cc3b79ca5ad944f049e93a11dbdc537244f05555715b63a52c113d04679f4fd4784ac92b260dc19e7a89ff1d3b3db68560624413797dc44ea247083e272539550461f69a41702129e5a9b7b1bf4e10bc650a2a684fd5039c2023105c7894689faaad363f37640082af3e15f33af687468e147c91e60f6cd75a249d5367b3f2981fb8cc1941c4d5f4411fa5566b5bf36b5c1c4f34bef01b5fe05824c8e0039f2b55ae05197b40aded84f25e53da480603197a60edc5f4b49d87a4cf3379487b9bb4ef3c92aea3810c3c305a53e4a9ac216e6db0469a17ed5d2075818c3bf096d3856429febb586d59906107f66a74529d3585a424c70b1975e03ce4f3b428f21a8932e8c0765f2a33515a3f9ad648f3c2067216b8c998039719d6d9e46a726074f342551c292a6e70780964c48153aa72678e1fd93c66ec0a32e0c09a0875a5335310163926e374c173bdafe3062e89147c9372977c226d603de22515c911e375cf062ec8f83942eef1d0a0b30626f88e760941d6e566d5c09d4649232ff4594cc934f05d2aa297ba0822f468e05cd2441749f2abd233b0e9af36f456e4aa57ccc0e7a9feab495f414e481978d33962b628f53e3224039b33e62c495a8b0ce518380d316ed5e8ee754a254186183820230cec6d8b16111fa920030c9cd20d79a695a1fd5f20c8f80217f2b95712ea933686d0f0027b1a3e3182b264e0ae0b8c12215510dae45c60359dc817af94b6c0e8a79c1bb5d4f77aa405de5cdb74446ada484159e0765fc732af57e70e6181bbfb492b5a934e10cf15d8d4ea262ae5ea50aab502174be68d21a8282ae25a054e0615d5be4f757fe7cc9821830a7ca84fc1c44598104a3a05b65b35f6a4cea14d4c2970e3253b8ddb8b484a13053642b45819dc4424cb4081dfcc1e2a8ebb2425234fb82f67ee04eead83762a4fd9bc824d6024e75869344529e5392694efb3658d29a42c81db10aa4fb608b5053294c0d6ededa72f51d1b93256909104fe5350a1a35208a9420709ac67b869485abaceeb2338d691d35ea669047ed4ffc9319d9c31e32c0e328ac0b85f36ddd490085c7f705bdfdd5279913104fee45e0e35bdb0b31b0d3284c069845c55d922230849ad749dec723287175c68e0466f607b200308ecc4aca53f758a3925f90363704b63e6dd1753b620c30736c368c8e841c9e0017bf162d555b56ffea406942063079c576b281d7453788f3366b4e0460e2f3c083274c06a66cc3984a0e1a6fa1164e4a0dc7e3175ff8e051938b0b345634e704d31fb1b6c29659b670649deaeb962c84f8b20c49b820c1bf09ab531638990242765041935e0b209194b8aa56c70c181e43366e0281cc8a001972a7808edaca79de41fb3e084243522869cee8490f990056b23729b50e1971c7cc482512958127d13359b5748830f58a0ef6a820aaa57d493b49a3ccb1e4d57b062a7720a3ab5db0a45957a48c242061964057b3a66cc1fdfa4986a6f7cc102fec286aee224355228212fbeaf0a540a75de49fb099954e03f298f28212accb415c93ae8ebd66e9080d8ad59093e4ec1a78ed0a31282d0112153707b766934c54e29188f144b7e1c99b4a73429b80dcb2958688ba697320a46e9fc60f92a9ed3591fa2e094c79f9433fb72d3130a56744e4a59e6a0d1e20414dc79a83e0fda8f4f1462b28db1fcdbdaac91863a840f4fdcd9ad734d35937a1df8e8c47d7082ed3ed11925d5fbbbffb10956c5f24788e62e2ab626d89cda749490d68f4cf031a78da619417aeeeb295070021fa021801b7c6082d3a172723b9d3ab1342e6ed820c11962fab804671da3c6eca492363dd6489b3163c68c0e68b1851612c08002dc1b90fc4af161092e5432f5ddd6a07f3c3492175cbcb1c0d3096cdc6840f2828b06ac81e2a3124c3aaf9843997f8e9bfda0046ff6edfaabd19304f524f8714dfde9dd2aa693241811c5837787de3d8f44824d31b9566ae7cfbaf003126c2ea5520ee54978883e8271cf9e2cd3bf594efa8723b832fff8fed9f4d108f6745a8d765c8b91941f8ce07227b73f09f93a7ef063119c8668ca4c89a24ead0f45b031f55b5626a5f9d6fc48041b64c829e59cb4acaf43b6f0810846e555d08fa1ea53c80fc16e4e272187e8c3106cade68e8a26f4c4d41f85e0b3a6ca9244559b90dd072158cd49f976f613fa921f8360b527dae9d54b3db9fa1004db16d4494c8baffdf98f40f0e1c9e4269deb0720186dadbd1d524c117ddb2f3efec0a80d322c5e103d513f303e29650e1d7699bcac83c1471fb8b8d7975797e927311a7ea38fe10337d69de4988a8468e11e78afa0df752ff8a107ae23951069554bfde9e0f8c8036f1f2b92f094f4c64e1b5d9032047ce081efca6b394dcfc23bfd710756cb4d6f527f69418ee6c30e5c52634adde8e8764ca903636d52cf723a912fda0f3af0312499f39afe1f7360443a2d3f21ed36c6fc871cf8afa064b2ace79dcef2110776829d483929992a09990f38302a26a1e3e8c9b13ac67cbc8151dd793ae712a279ff0f37f021ae96c689e2973c79818f36b062ff914c722af92967cb123ed8c0fe27fd1b542acb7c9f123ed6c0c9d8a984d20c491c5cac171f6ae0fa4d997e0abdcf7da6c166d4f840032f9a9521585ac82dffe30cdcc99824050fd10f33701e296f44cdf6471958eda02455068f9653f0830caca4d8c9d5c61f037e8881099af268efcd787dfb4718d89c967b9f2fb5ed8a3ec0c0e7a0524a8a9b4654ce7d7c81cfec761df32e5161faf0026feafea22eb21f5d60cc3f075315838841eb35d2f04a0d3eb890a6a45649e70d8eae912ff8d802efef9a4ce87e2c65f91a6938b8282df8d00267e95e35d384605b1678ddcb11fa29c898597917366ed0c002ab959b4b42eeed3ca181830bc2828f2b7039989652d32935d2f0c30a8cb0cbfadb552724c96ba4e1e81af751052e26f389969ade23bd393ea8c0a99c838be7f4a9c24a16828f295c7d4881539ffe63786f28f01105c6b5cefb92e9943bfe8002e7aba669924ccbf59c65c2c713780972926f50c92685c8096ca520d4468914dc6296081f4de0ececedcc474a0e8f0be183097ce50b3afd268b153f3880f0b1044e54b99907e996dbb4317c288153f25ce28d6b08258307868f24f01394298d6216eb24db850f24704974344fc1f25d2afbe3089c9d7b56a8a7f248b9113849933798e5ca471118a51db47eae8e296e0c143e88c0462e2d42771aad1ffd6bc2c7103895835d8ed61b5397324008fcf7557592a3a2dc844a193e82c0e8ed52f23d8af85b3a0c1f40606f54cec1e266d3d3e7fd80310f26477f1041552f1f70935f538af8d9470f88de1953cd552cab32b6c7f01c644a2aad91f67ee3cee40a3e78c0beb678a50ea6c55fb4032e551295830c2a4a2cb50ed8108fa2b565c9d19e02143803906f60c68c1933707861e32307bc85a45719f69124c4940b1f386063d6179d379fcc6a9d0ca4143e6ec0fa96fe98a23fb796f861035ea4a80fe22afaa8011f16aae3ba994c42947af8a0011b928520ae51fc63d299055fe7aa5e2a043bb1943b78c88211a637c87a3b8b6beaf400128b42a98d31e8178f153c60c177e8535afe1921063d7905a752b44eca21befe8ece156c0e52fbce621c25540c034660b246ea4d3ddfd6eb8bc0d9e438923de514ab9e08dc55bceb9359a6213f04467b7fc8dcf974d0bb10124a973e08bcd65f32ede974aa4060358a12aad631acfc01db5bd9da366cb3877cc0dd081935633c1573ba075c44d752625a76223c60bbdb2bfba885d276c0be08116c475d94d4ea8037e91fff4348b12d25079c1275e341e898f7c5019baafdb5d2ed4e94dc80094a8688faa7367fd006fc8e8b0879a38abed48051a35bd9399632b402d080514987fc591b29f7b3e072c71169b34f845616acd768659094c782334d4987da0e0bde4d4f92a04fc96bd32bd8db20fb3a23858b5cc1c65115720e21d3d256306e4944922521c81c6205ff12ad5acf4c4d735661b2e0aaa9fe2655f09e7f6df487a454b0112fa994dd3c882c49a86054cc1e32675db749d22998ece1a7b326b72c319982f5b8a9a28fe54bafa352302a640549dbe2dd3a22053fcaf49b0a32e42073340aced33f9b3ca5ee4b098982b3e0b14a864877a38442c149d09031e974b92642a0e0c736c5fc63a15f1ff409eed426a6679ac674234f70597db5bcfb62451a3bc185a8ee649ba29e082758f3ee64ff7b3a68bd092ee55431b5e8e4f94d4db019fdd3645e6a4f4d2638f59758ba2ca9fd10136c0e6e962aa86a3ad9253879273f830a5132a7902538b7aae41f9a45f44ab09f16646fc85322bd9634b7ae3e097ee3faebe9cffb133f9260527e3a2d6a234acc23c175d61c2dcfb4165a487015761f43c8a052c4a047f0174476915b11a3ba8e6037e6760d2286598c368293f43ee249533a9711ac9e5a456c11a96d2e82ff4a4205957e2ad75404a74ac6dd74ffa85f2682913104d1a33aa2990c22f8daa04ee792f9c6c2738833ea7bc7102891b1f772af5308c6ef53e9484944ca09c1760c4aa8d1f126c02018a55df42475775abd092008cee4673a61fe23e4a809100836bf490d299dd4baa0260020b83eef0d162d2c784e13e00f9c68eec578e9cb33dd04f00313f226dfa46356fa7a1d40803e70fdaae555793d8a960f7ca89c83c8a7ddbfee1ed8533a2664ff27c9ab1e182b8d225cbf4d4c9a07269e59b020647b87140f8c502a478a3596fdd33bb0218ae6ffdcf4aba71df87fdb10849e75607469124ae7ad9c7d3af0a92798a64f4977690e5cd0294ab6502b21c6c8811bdb507a648d3e691cf851b5a3e3a83c2584032333d3a95fe40d6c599239ed836ee054ccf27b450c53b10d8c46cfa4bb535e8e1f1bf8f4c9a37e1b9244afd7c0ad5e4e509e1af8b48dbdbb9252779706ae425c2de88b896b8d06fef365c59c381e72aacfc086dc78d994a87863b51958fd98eb4fbf5344be0c9c9b4e4fe6fd92813753e331df6fde7d770c8ce7a9241649589eec8a81c99fe65a22dd30f07e2295debed62475c1c07632f97f7fba5d93fb052e6e9eaea02c45af5c2f70be25bf53ba0ddd97ed026fa6f7d2225ab0bb2c17b8b4e69daa73251d25bb0526b59b97855467fead16b8cccc9723699294b4cd02af31fa885930192fb558e073e8eb9ca87fbafd15b8ac29079dc5ad2e7b2b30229e43b036e9fdf12af0df1dec6490ac224a546033849482f9a46c6a4a3d8d9b72ab921478d39bb3461b09ea465160db4fd53ed8a649395060b37c54f0c8392ba69fc089144aec4bc809ecba68f8abfdff649bc09d90e857f1e4df4799c0a88adfbde5934a2f4be0fc42e5de88d257224a606cd773b0d34147962481cb4f2aa2aa4dc5844860f4a5dff020fcb67447e0f45a963693a4533d3502e7172632fb7cb3e6b4084cd0542d2aaffba54d89f07b4abca0623f0436598aa2eb34a488bb10b8917ac1f4aa53043d089c85a85dc27320b0eb5b17cd6bb3b9e807ecaaa94b0cdeff29f201df22a3c9c8d9267fa807bc5e92d57f5726f38707ac8a4e9a4209ff08393be0f488a42dd557c8bf75c004616aa7195295da39e0324b1635ca2cb969098003d6c6a4e8e9ec66eb12e0066c90b4b6b6173b882c016cc056b0983ea5a8854509500376cc2f5945ca909f24000df824c382e6537b92ca59709ddfa53b424a7e1759b076a6b641c60c522bb160537373b5a5dab44ac28271ef60b28469935992af602bfa75bafd75c82d5dc19767325532877c3995ade064a41cfa3cd5534a252bd82832e56e92af828ff77f27732d784852056726fdad3e948e3f4a05639bf756648aa71ea182cde5162258c58b203a059f47f209a5644a326f53b057693de7c71cdee952b041e5181e942865272505ab19644753fb9c951c05ab9e7b629be40e188028f813e969292d9a90985070f9b369b3112aba33a0605df3080de9ad82b27c820d9ea2acbcc4ed2a9ee044e3998a1576824ba52767df585fae71823b4def29a538b6fb26386df17b4f838d95ca6882179d420a3287ce142d93894e49ddf7cd31c18810fd4bfee8a5892ec14634f330f75882d5887942eeb7128c58048fda796b2f4b09d64b42889663b77b26c1f6ada5e7ae24f5488251da368904275cafa4e45ff59c16487035bad56b928f607f37a7183db9a4be388213b2bbc472d48c498de0f2ef5a2df3525c0923389d2ac4f4b499b4bd4570a579d3798cd1f46e8ae0bf2fc6cfd024a94f04134f5d4cc92cf305218249e199cba2e6113987e0437f4fcadbcf905531047f52624816c5358d5e08c69274fdcf55999a44083e95a70f593364af67105ce6da82e0b296ce9cd492403021e8da8b360920d820f9435dfa482195e40fac961a651994702d11e2076e74aa3269e5153b47fac08a12cacf54be897d113e70faea6a748708d1f13d70da293394e4e49a263d70d9572b4d06397ea53cf0ba397a7b42dea04a78e0255be5a6e5fd7fe80e4c7c5f4d13f3b8396407fef692a759882d1eaa03ab55c2237be8c046fdfb1c648890b4670e5cfaece6e37127e7550e5c921d446853d58b6b1cb8c929489cb89722513870912708cd19a2e4ade01bf894e9bfb265ee0626a8a50b12b462d0126c03972a752911dcad830ed9c0af7f3c0f31e5f59ce11ab8ec987b29c343c8ba1a38fdb739bf47358ba932810fd020c100d2c09659ba9037d9b77fd0c0a88c1c84ec4dc163e70c6cbacba944e47676d70c4cfcfc94acc192d4b60c5cbccdf849cc92a62819d8bdec185449ddbb730c6c4a691e1273c8184d31704a2b881034ab4ac78481ddea949426ffd5b380810b1e3d8bc48e6842f805be4b822a617b810d6d3a5a43e78917ec029bd449cd1e6348ae8f0b6cc564a62fb40546a50b6943c94f0b40184016d858395c6428a1165387055eaf45555cd7d45777052697c7ee4f1983bb6f0546a89b8a14325ee8be0a5c341d4beaca2df26e546034d9d88754973aee53e0b54308b9a69b774752e03c6ccbe2e6f5e09e28f0d1ca74a6fa483a7aa0603372004fe0b3ed56d079438a41ea047e937abcba4e96f45e9ac04f2e254547caa55e6502e7d9efd746dbac93b6047e4350358f9272168b2981c9196a2f51f34a12b124dc5929a8a44c6848602c990613f61ad2373b029fafd6f92a42cc296404f63b6e124ae494497e8ac05f87b2e021a6a63d4418c0106c86090620044647b030192369d6741058fb1e151d2d6e9206022f4a74fc983b6e08fb078c48e2f53d9a6f93d6075cfe911ab3d268d5b607bc9a0c5db595079caa0afe41f6968cef809131bcdd2c7e854cea80ed986e25541c5d269303de2f8ffe18623a6d161cb039fde37a5ad0a32637e0f7b73f834a9d43fa6cc0c9f7cd394ac6b425ab01b79b7a5574a6d8e901d08011ebb4ff2131df320b269d5abf0c9a6a4177a11690210b26554822a5309142751f0bb6ededaa72c5b0711d169cdf7755f06841a8ca79057ba2927e4ac2e30a3e7de6f2fe2b916de956b063aa6962bcb4b7cab082f532939e2d5d7bcaaf82932cc1335a44ffdb8a2a028daea8b4b065e2481c0e0643c150300c86e193d305f31308001838268e4582c188a0c9c27c140005462c344e3c2c121c2222101a87832261181c08048461302818080302c140201c0c0fcb0a3d0f70ba8f5f491016d864f976f89f9f6cc6117429941c168278c0b9b07d0815e404de0c45821520e670e1e5139aceb8b6510afd64fcfb39e4ce9fbb2013ec291e18f7c24987cb2b154ec964de2daae97b54239ed0ad7351dc81fbde55ba856e92bb72ffbb4b17c79a275646910142c82f341eaa41b97b68e27ac682828f1c8ed83a29f60620933f7c98077ff8c5c23660953125fc191e84fbe020fecf5a5be93f0b2486d02df86a78cc77b6b8e483bdd94b2bd69876c18ea9c3e9b8bfba31ecae906f3034c80c7a060782f402fb1d7c662be5a09df06fa80eac0bcd3aae9906d46562c1d45d0c3f2365681b6c0181b3efa37c48116e0115b0e65eb8022fc6863fe4a0d763f8b5de50f777e8caca8096f7719c8de7cc137b751e9237f21e3cb55f4d48255409658606a28d3c6a5412880d5143821c3e4bbafbc486aec342c3dd306a78126e0943c014bfdf2fd9ffbc768945ee80761076b837d7b9864e17dc05fc42e2a17a082bb419ea09417942cfc88b78ac3db41025e402cd790bef83b7f570dd4f45ca0b35c1c20e81b9503d647a987a15f4198c099742e804a3b7063ce5b68c772b397239073d8303417a81d3ce2f0b9484a6c08f50cbb5f9557c4a2e5edb70cee08a250e1e1d374c4933347bc6402890220472ac522af9fdd060527b71f1474ca16e3143847d0023a34a28333400f989c245b4c8f72f13320909325cc1b101a820efbc43c2d5a4655adf62fdfc53d0f5b676af1c79cfd00bfe584299a141e82fa41eaa87cc1aeccf08436b9da1e03067d8273c02a7287f9a631cf4bf3f725e3cf5cfc5a076eaac93f035292c16b610ca441f2f119c84a661ab00cc102774121209354308a15dc876c2bf01dc48c5f7512f285ee09a81af08882f2eb120a570cf3f5ac4e9a94703e96d4c5ff2c32cec270550797e4294904b8893a0065426c1af2b1c83ec1fb8460349ff27e0a12b72a9f95304a5fbc2d961f4a20629415cc068e654e214fdb546c9c891cee1cca69d54c4a07118669813ce09e1de0915941c0ce00fc382eba0d3a06cb01a44014e82766a5c03693bfcd7112c0272d12f62eea86ff4ba7b6fbec6b15e309a31356a6562f1ae8badfb56acadcd8aacd5cc747f2f97081490c712fa1c5f8a39ee7d7220591a2265feb4b715eb5f0e2ab1021add3c48f29fa4b4f451ff77bb3c8e8c08701c73b9772e720a97b8b690712a86b69c3875324825807cf72b681be8888ad73b25e053ec152aa72a4f20447dd9e25f5c4f910f5f588d9d391373a491bd0f4807514725378a640fb1cd88b9917cf0329a302a1ba8b535dba013232201cf5a5b87e2974edb1eed80a2aad8f699e8aee1773c94cf5df2063acc00c022b9b900db54fb2fb96be32f8bc82d66c77e572182a8c777095db6e84879f0c1c4b91fe6443a4ad5e4722d66fce13534d8d5ca091de539a65b59985e316c2a8cae81fbcd2ab326f80100310aafd155cb3bdcb99ad4ed9a9cc445b593f849c592be9de4ad3ae91a3ce030e74ad4a5e55c39d6b5faf9597614b79322e58f91828ee657da92d9531ffd93648bbd4a812cbfe527ba142e6b4ed819d968642400a1d9e8150b031e7653e983d2209e94f8ca91f889e33ff6f624ae3989c830eeed45ef2dba9d90422194841513e6a2d06942a515724111cab1d812ff6057560d02cf3f4a90b1ec6bb134bf5bd0732520061c29e3113b1bc40f61607a7a08c1848c4aa153b27c708c8aba0d9dbc9f713e81d7ca9032d8af3396d032ac59ec61a1340280c58d192117ea89176d1b7b498168928e093ce7eba743e3ddbadb95acadfb5ed98da9af6ef7a3db279ce88a6a7e6af10cc06cdd5f2c32456c144ece54c0cc9190028d80926471a24623796b91389ed04f6c6f566d0e16ddcc8959a5fe8e509032ed539b38d83c9c803d6dd24db586f08fa8f2eafdacc79eae9d06860c42e295e0d05bae40b3e46b314daf094539bf80180c0ba573ba1835ce2964cac961a85e3c580c5feda6dbcaa94b5ef2850c357302fc11dcffec3860800fca504e407b72fd923b7cb1da25148ac503fc90d4b3b4e0464ff9ea0dcfd58dce9ee043c6f49b1ee0c1e9239c8c5a547805f3d4504601493944bcf9307907d60182d40802b9a1feb9638a14165650c081d046848e0811270465a18a423908f9115236217d0a9cde4f16ff327c68710f33dcbbd31c94af58edd4686cefffe637aefe4b6a405483c7499cc2e07035820a26473d083cee6b561c953d1a211529e8d3ebe7407ad73fd316328e48918d144de870ac3221ba37b63165834b3125ffeef762e3434340140c7273b808311b62dd835191475fcf0f3aa7c5aef663dbf6b955b3308e298e85e3acea2ec7bd46a2f949d805b6492bfc63818d05a83342614185ae81061a9a299a2851a5aa490088d80cae64deaffe05217fe3b47893f8664bb16c2835e803e8e9d1f3927aa60a5aae012d3c5751a50295019481192141219ca4ae09b6469bcb85f67ade63210000e10b4c56ad48c66aec4a5b1575b37196a23dceccd8811d41571ec328ae81eca57a7dabe1a4f635b5a10905ac8b32611af6ec7c027d574a090559633b95de2ece656234bf422f2f9621e535a6e3940b322791f5f99c4ada6ea2e1ec3e5b4e715a660a646a68faf9cdb92f5b36dd7d602bcbe6cf4390bd87626a1618c7d9751a3f40a926835c23d525905d5d5be3b9ad2c6d8d02256d755f6692f0f710c054064598e0df5d47e94192eda34b7c1603c8732d6deeed3e9efc0ffbaea61e78ecfd22a2214e25ce8150096d90ca5296849ab31629926554339c101dd6f88160be726314416170260887ee0f4834dd18684c104d2fdaaa53e351d64c034a730af7c3d3c8ba64a11d6c121aaab4919fa90994ae1d7443dc4b69dbd7daa9054592532b53c10e36207650e0630c1776d9fde6ed60a97a248e40f4a42ee555341d860e64a3d13955da00c5b9eb23d732a908474ad49d80f2b0d767f813add37042b30ce9dcf8eaac7d7f82f1100cbc123cc06206fd97e78d155decae172ae58a8f6decc3103587d4d0cdbc87722ad7f9129bb66ba0954f4649f4e224c362b9e31fa9f88538d8c39d3051d2b68475da531fc0094ebd60c52ac2e38170526fc4464d64cd387fcbd677f3ad0cf71031c179b0a5a1ca049b86bdfc1b55b8d926340e1e42c7e6d3f9c345f84ea7e75d7f9247f8e116d9099db5cb9d53ad727af78fe0347643149152c5e665b8b402a76cb87ece60107201bcccfea7e5df35d6ae7f075186a3531c667295af6ed3fad1e528325c56f5dab99cbb470d78c502bc7c43a63199b0b8c573ba3a3b6669dd25074bfd4e3e6b6241135cf710f0a496b9d9fa1f89d68504cf65da483262183aa10a9080a7f95c62ab7743b55754495ca2361141f35b27d13b898d4fe498819789574301584fff2be37d434f7dbe30ab11d8f603c4648e2a1c5e36ba86d0252a504ce2408cba2e9338bf0791b6d2c2a9af4da5d051c906d5b2f9e86cfe91abdb7ff281adf8f188dc48c15624d70f908f33d9d7e288797b41b5846f0dafa65b4bc8d81653346ebfa19f3cfec6e3c743c8306255eb6b4addecd55feb25392f55597f20db544e1dfd3260d71cb5d44563c657a365e580bf8df1f9fb3383ff4d1923012272d67427be0d275922c2da74581c7ca846600397a2e58ac1548343cb0b75d07a946ce2ca9f2cd5fcb7d3c642b1bbc4a9890a6e16093d3cc641443b7464584612baf3bc824341458ad8e02dab48a833d4b6bc32822a4c17e03ed5c4b0fc9cbb95968acadf06880ac2c6b14492faefa1769b704c89b202b27f93d3d2c675951c6f32f07d9f96718d55285f25be7973aa2adc73aa46587cd51353b1f17b3f2f5279aa86210b5a9c3b18bd4fc878020f0d2ded14f6011188453b857bff2161ba5a6c73040350dcac56f6a0611917619bd9ad03a4ab53ac8e737b327a2fcb86e38ada87f115058f64b4fcf2798fc10e690bdb636c2adcf0355b28b9b09110c19455d8aa6d7121b28d76797d2cc6ea797dab4b87c376127b7167d51d2dea67d7f288b4d10442a9a817140106ae9e3ad25041c94e40cabd467172548b8e5190b67346aaae4ab45253f2aa68cf3104d3982c2912e086d4681bc55984be9b8d2e578e69a8b2364dc8c56a038cc7d7783b5f67493afa521eaea6092d4b764854f9946100ddf67b775b42cc84245dfd1152893d2d11a3a28c42d52a72fcb1a833654a8a548d5854e725d4c09611b9f77b0356fbf4cd4aabe061134a0247667b231855219b21d394e66a3a8fc3b8d330aa1c60fab3379f1a93d3c2323c2369da8493e746af60547391ad642ca084c4cf179cadf35ea3495c2694ddb220395bfda006f6e31119c7e02d0d66cada4dade822b09ee8601c3a8d0867b09b7a3155749df05fa874d12ce557e3c9f9fb2518c31a1e86686e597e20a72b5a3931883fe62671244b3ebba5f291dbefb72fc9190ccb37016c40dcb635ceb0587a3758a6a3cd6543d5182f1982f0347fc3da6c69bde2b636f27cea1a4fe6d8cca77e0a54e961bb4a01164223862dd4181010e353d3ac76c414c3d2ba574f8a12e4d0da148bc0fbdf07f41f85b2cb699ea24c5ce5e8e9d0ec10cdcefae347e3c4f2dc7e1d36e182f5c3539a68c01c94a2d2616c626236b221ff9280f9840b299b1dbddfce77080029fccecdc0ca27826ecf4a2d2463ba44f33e3ab6069f4420ef133784a51e4c966cc107274824dd62d0caccb80b936b01dc483035a216c6c4a3c88274d5a2179f93c3c06b42c1a44249a90c60de23bc4314f3b316a0b0fa55568f668a8aecf609d501e29e9277aec0df93c809f9638c40d766a7efa69528bc9d140d52a9b453c939f6571c095113098ac0f2001c41968da1529510d14f915c25bb1a416e5de4fc818d5769f94a55746ce3b499e2199309f965543ea3a25cc87d35d8aa2e8528e878f0ab7bf9eb488947eb87551dee5515f45554a98d2edca78186a4127ff35ffe5b4650e75d940c2efa3f9da2572afac1bd884f1a87a6ca08cef4d44a065392a0264612ff45c229b3818c49a86d28f9ed3acc37400a2aa010450f186d58b103029dfc21d456be086c0f4d8aa5c8f75d79209c419c3a92dba7af37604205779f82885c043791c7ab58cfc4e5686fe831e62cc821d84a7a992d3dfd1f5528c0be028cc66a8d13b58b59a959639e428cf79b244e13b0143ca6c568e55000a3eab6220ae32552799f898ba569dafbfca371507e0799f2a873f1b8590dbfe9104d72f481b53e1264eaf7d617691560a823feb120806f0c38bd6f81d6b029e9fb0fcf1be4c6e8cb494f8ee4894ae4de48857a17a64ef09a06160b6a3cfd373f8b0564f6e364625a9f698e29c4770d7e58aca888c1d351af4496ecfb33ce83a385656ab9cfd8250d6171474f91b92a3a9f57312493ee2f895f6d2b591cb849ca9c4ddd2ea73e29dfc7a3cd7e70a9b08a601ca7f4efc11b1ee83965cfd819e3323459c45ee23bfb0914bf361928412b0956a130d3faff8a0a135eb985c1f0c2847f1a0dc1160072b506076f8c347a7381852e0083ae2989217dc6b735946d5e6bc029041e780d9d72b757e506cd4e08f5c7640c65c997ff331ca9a0585f63b76c38715f5dd4f15c82ad2e68e1e450d5ce7ac2df7d175fc65a0158dcd02dff4565d8ac24ceeaf885458782c7cdbe78407d742c099b77d4c3a3405b58223959807db53f9a3e62d9c1ecbe31fad605cfdd4778bddf4a4a8cc8eb5b4bfcd18880d8407bc56ed43367b6e37332ec8b9b293d5abd92aed8ecb341c526874dfb6d3ada44b409caa6924df96d72daa46893cfa61d9b3cec66cc096d72e42e2721e7ccc2ab7d88e336333af546f03374c357f152153c391abc353c518c94055d0fa1c072bc965cefc95e1c6471578def335f1a371d63f62217f7423ffa45e36d678c97c265598ee00701a19636308103c6bfb6bd220e5d7b71bcf61d3e83f1efc7cf9169ba9330c7a6330cfdc88404af186a05be6e90e38524a116786b4585acbd4026c64ccd092acdcad81640469df7fb0c38d690a107e1cf7a305e41fbfda779e4aa5a3cf11d3f0cf208d4a2d4676e2d123be85a9b62dfc6205ee359cc382ab5dde4ed6dc08fd39666181df734747610c207b2d71158fe8e75b64ac8de9528ef107d27ee5bdb83d88d6c328de948c0d92b9949b6c8e4b0ddd6662041da419b333240515dd66718226336a9728fb205414515bacda8e0e8888833465108b42a7da409b4faa1f4da78b611c31ea4d42fcb76736363e651c0ec1a097d47561b581ac3f580378ac39db31f9eb50cc7978e8a95c76dc702a4f83032a80f3721119e1a5c15aaac7379272073e52cc7079aa5638e3b0eb673c46c6031a58323be060cc195b388f0e45b1afc45e0df96f3ea6e2a955d97a47343e62ad193f6391301541db3ba6301b3faa16466351582d9444e695a5d178ce842a6bd6f7a92507c2f9243a3425df218082be8ec18f288384d2e073ecb93a87212bcacc7f4bd470f2ff7a4aa70597f52b4812cdb4ce79298246b2a6ac9b1114db1674b65f44012c6e204b51476eeb1879683190667d6e73a00158102033024595516da4def680cc36dda08f18113e6abf6124ca84915a7a810125d4bd0b78316ce44c95200b0f154699820411dcaf534c49b214ba6a218b991be40ab3b5e383db51972fa382deb818902d46f16ccaa034dbd26601fc97e33b5b536570db41eb00dea66509be6dbf65fddccb36194bf20b4cc371e0a080969f0fd65060bf0e64ffa3928e36ef5fd246cbce35539384836efd3d5ca23c473ebcdae16a36878f3cb06abe378975d565e0390c76bb50065d890dbd386d07a044f6354f9272812b4eca08bd9365b47b1be49de0e3b3886f16cf314641ffc15fc508f46efa923a094f3a8bc375bef412450298879b4d748677208e45769e5c8992e75a1f5642b2aad4d544bf02a72efd5d55e7ddd022f8b0909962a9b16600607cb0edfdbaa4b005b6a2468d51549d2cc3ae5764747baf8fa223eefb5e73fb886bb6b4137f7368b34a78fd16ac34eea55d574c339c0b9b48e37acb522f50dd37859a2573afffdd9664864ad496e8bdba7da1b36bfc3b626c5d212210360872760727ef30da783335fe8b1737c41017bb00ee45401ca59b5cc4eb9e92153326314b4469b3556eab840fd0c21e8ed94afc1585c2b84d973d007ac707ccfa198b0e02a4f1858a10a54a08fe0c67c9d61c280cb6a0b72649f15b6ba258e1b2ca093042dea9c5f46394541633b2ec86a17014d230bc6ecd105b753cbb8cbee3c07b59bb7c800fc0ebaa46f9dc0335ab5c13546de116806b87a2bddac8e24c609c0b6f5f9304197a0db82d6a3a4eb675212685b89d1a64972b656dc9c6b691f30713708628147ecddefc1a18aa860a8adae579c01463f9a362976d069aba9ef7580866639c7481b9ca9933445abfcdc445678839157645b6f9949b0dbedf07447f3b78b9352915dcbdf74ad882ef2c6054bf4d017e90383a1817fe7c9c5bbeb3c7a1976b22874570436d64332500b67a511d6308ccf9f1230493dfd1cd4cd03d3a6cc3ac93e14145b7e368d87ad4fa8b21436071b910215b571658a16647bc7b64e2758ac084821ed9f00122b45dcf4bac0039020299be99987d90443e68be7def628975479732951dcc4ad6b315e19519b3f7eeb329adb56f0af64168e669a8d7ffbf51d49a5e20c3a1abd676e73ea6c0ea9ac04f7cbe3efbc8007f219f69d05deb114d617d0ad815833ba716b12c16467222189bd1102ff13ffc759ead670b5b18868dbdac8a85750fde785e1a23def49794d5621f06f9826b78c9de596cacbffa02fc41a3e0797a160bcac175a710e61ef9e5fa8e3df44590bb2951fe048140cc9afc38a1db64005f3c8bf8006694eb8abaaea883a72ec2b002eb82d5d394c87e29754bd29403913b45c39169d5817400a1f924614cf8573904e5185fb575d2f4ced6d452fb7e8d5086676cb48efac17063a740666f0a2993ae9a816535792355a989eeb3265a1c62dc22de1a4a7648672edff76dac6724b005471ab3f62cb282f46b975eb4097ec61a16b15491a77c17fbd01163f5f219c1df6b953e4ea807edfb3813c1fb4ce69caaebd4693e26089392e6ba5b6ce4c8173220bdb8762ca36d53170f2f1ebc4cea5e71a82a1ae95f4ad34e81d1b01d1484fc24f4e9df436d0c73a204297ec1f716922e77e7d8769c61e94cf51c2ec5da082290d98c83304fb0060bbf7b33abc96cab0ab3117b1bf6b125671e7bf5a6bdcc532752be94c66d0c0090750151f90bdeb24ee63c26f540fe6020cdfc12fe2f9eae2af22a68d70b15ebdc4fe626a6a0de792d8a375e826f45a848181888b855b1b73b213ee52e0843550179e1c0a68c6b44b0ff3c07f309406e75fbe33d904d2c3c940c24281a6a46aced01dff8ff905128a46165688380e0c2d209a4a109b1bda1614a5fe380d4101eaa14780a43602c14358b6567ad16b58007a1a23d433124b0d888c6ca6fb8fb083f403010f3911c06a034c033b82162f797a4f99b7ea3b6a41c00053b4df0b803d0d99132ca704882dd4d20ee014e0de7e708f83e974657f0f6da0f8c8adc6455792fd900e69ecb115f9185c626d200a45851c6ee269deef68858dc0f5309ca795fd5806d6aa533e1d03383b27e1d6b300040f2be852349237d1ac4fe182cd00eb1a5c73155c35ee949e4846f8d95a89c7d556d082427b686b4fa1e5ee01da1a961ea52b7588836b10e6101145db80d327d0584393592bff00c37d0c7b0f6cd9805d1ce70bbdaaaf68c1753477dc7a175ae0a0254471ea334755e8f07104d2733614802d63b2772c7727e22a3d2e369b53f5a8eaff865a68f90844a65f474e0ffed6b9848286a93331f8b57a515b1aac94f4b1c4b75e99a7c5140efdfb2e82d2a4a08adc40b01779ccdedda1a5fe6a4727829a79a5d082742a0771ec88b82813559635d0e758ffe82c80991473f724bc61ab29665c8480e53e4d4bb24ec1da62984b4b7799a3c10a2c9bab4d2be89bd0b971d8af469aacd0673280eab61fef85d8119337f68795f02571822c5606e764a197534e24140636ee8299a2c66b4bc1f019f9aef0a91619cf8258f972182f4f0032c9dbf71834c41905879c655c06dbdc0b4f166f62b38f711c86952943947da12603096f5c51573db48947358515143519087ba37c1c07d6be56312e29414379b8c4e1882f82fc2da19e509e400167febf68daf17409795f045f1b7aa9a09a5c25f7f21e0074921f1fd0d0f7a35dcb91a0e1640aac10c004c131175f4e93fe877897378ee476b417b1d9a682edb8b454f803d681e7df350bf4bd9e082b48ad60e02a032df1586a11a4b4916e19803ac6a0e9b2f2ec24a1e0010f0b3e12d8cc7a97ac360c586bfda0b4487e1ac1f08601acffbc4f291016d0f510368a51eba8b8574ec2184e4eec92c311c486bc1188fce8027434708a46c2c3f21e23c47e5d5a49e9c20448ffe258c98258ca7895d89905580a01ab089161b1942121c3d2d154492e6500c30032b7f1f0734b2f007f97e089e18f657d045c830b8c63b159a62c0e2360a3081bf0668c8ad6004dd9ace0c15601571b14e954c64e65133f07cc87497d7548b2f2f0e484e2e7f795cd067c464686548668f8d81723b8427d8cf85d74ade7e3a1a36cc6d140f73cfba4fa88c2c7df0548762eca7ec398889d69e9440bfd278419e7bf81350c4ccf000f2e83a5e069e2839a6fae4f8faf1f1f24be217e5438456928a1835a9f5d02df7973421512e1c3a6f7712b583ebaf2c02764d7faab2971645037b17731bb2f3d408e4cbe348a730714c7942f01ea9e8187ac4b320e20c24199ed9b549900952562fbd7434579645c1788b96ee5ed4be32339a3135f52b209c90ec0fff9e1e26a908642c74a17b79081190970517153859d2029c148c457c04fe99d3dd1bac1961faf4316f2f30c221a26846cd1b0486802e63df3143f6144555c09349abe3b8521638cc37993c48555ea5f60de1f857bc366d711f17f6890ca5a22fd6ac4f332cbb5ff5ca6a9c1e77c98c06741a34499f44d8c6ae074715a0909283a28637c1f24f5e7381a1293fa8e753d36ed198e9ea70023c3cb8be0e7f7046382b1d38d61b4bbade954f907d3c420066a9cc37c5906bc702693d6f7e9d0666e00cf7d66aaa58d5097c1b63b20e192e11214076b048b7b23c765f3b379a00b0471a5a260aab879057cd630cf0acdaac222a53a9866da61c97841ea67050addf463d95efb1b46b06a21c717624864a23aa2c512c1087530c60549623a53df694afa694df24e9ad93fb6a2b5637a187d9bea30ea90cc0a245215d26bf8cce53497bd00441e1970918924562eaf68b547b0ab63d760205927ba5451acd96d80c51edfc163247c3f0d7fe939c793bcc06601dfa1ddb2274448c3a6279076df8b779753685122d2794a99a2598b14bf2c65b7a1645aaa157a8d002eb3304a59e0412cf8d3f8a08c283ffd9ed82409897e26f5c4828772659ab347e22d7e921a3237b51b16ea134909f332474f7cbb3658a9f57bd4f5e45812aa9767b7683102993db0616192740fab0809566d6fbeec361733b792e475d96b7b26bd2553d603da20f78400fcdb5965fd0505473174ef239cf39407c7fabae2f6662ac2e3ed5f07184584dd223bc8fe908a4ef10182ed367f32da45153e78851d45175cd954598b5263540bc9bc4dd2c14ff169b1d5aab148f0d0777c1c321776f0d5dea17437a111371e9e61bad0aac889c7a93f09693fb519c96b452a7e486fe9184ea4e906a35619ac5fa45ec82e07870f7090cc049ecc624f37516cd9107920635d45641b65b45b4a1ef581807774a49f31104e6971d4c71413156b29b14f39d5fac2180d2e6de38004296f63410232d95ca1c11dc9f5b546423f6e9dd8c6e4bb659b254bcc2482ad9328d1c44384237390af69c2b65413d703c9c9e78d588adc82415b55933a0d42d1dc4e4232039f2d369bb16ab214b2869ef1ff231d7f26ce8456fde1e559af494db7d3df30e3beeca0eee54c180e6c9397894c6afe93330ba25821e23aa9edb29aa939cf10eca3754da50d4c6038edb04adbc05d7638680318f5183cbed609baaf7e3c5bed654fb335ecfd76482a56b98005c10206fd665ab4407a525087a29b3d42e122a45c9e3f743ba6ceedc466a3e86305a39808ca56c04cae69a7a205ee907b72092d4fd889c33e105ed56e485e14bea958ba6f6cecc7cee673cbc84bccddc662c2bdd334752d9d5b1f0ecd19f8e3a01e69f44a27a7ea05e16ca234905d243840303498212a83f9615a984abb1309bc07eb64ca98943818b9de52ecdba481e46d4b64f61d471af7e0c7b0e054be389a8b1b7635c284000085d81684e831b977f0a18532dec98d1883d7561293bb43949408d057815d420fa1462d3b16f417dbd0d94474016c3187d406a14dd9cf2700f21ce0c4dc7d44e8ba2d66c911484b059e29392c845c795f5e319ae10bd6fe2815d6f15787f08118fd6047ce993441f85648e3e44fb802848429e33eb08096d46aaab8807f48ef8e3f79d602445b4c5816b9942fd7075b7336709e2232cda8415e9e224dd891441bf4849f9678362744983b0e2348669bda4206986721d6649afb7817dcc1f6a50f7a411a2241984425887dcc03b83ae5f7ee320f4844dbab088553f4058b2771226037ce7faf4073f0f6800ef4a8d69f73256cb707b152bba4e470d57ce9c142d9f288fecb1e41418f2ddcd5e9e5e4d3bab4a23833d5086bcb4d97e6b549e465cf603643e3a523b48e83b7ea2f3a2d1fa015f2779fe271c6e7efd34ff100048dd842571ab924add6256e142d5ff9f192349151ad215129946e8ac62263165f27cb58d0fce9771b92fac71305d48e56a627c91dd530e6dd2d8dcd4c2a8ce0ffdb1038e75d33e7cfaf85ee3fe31e21ccd5789d53c9c9c8ac0e58456e81ef08203088b04656a2f6c1772158a21602fb5ea06f5b9a85e3d033d9e616343ce928487d5fbb87aacebc0323bcac3b41faadb610aec92a3777686a4f7e9b72d358e7c388055975afdf31f59935d8f17d9019d41cd52a4da3755bcd07dee3829ad370808726e96a7689b34f59f2b93b0331bb068990770b5b7a74abcf79241788ae04de0a3b1225763ceb094535841f5e22a9f26efe845601e7f0fedf8c0fc8c4dcaba9c98a06b8369132df4254de30e5e641ba45ea43acf7c410001e92a8dd1017c4ce3bc09fc4f7ac11769b14e0a86626c7106ec6c498ea7bf3e975eb3d090bea00ea65dd7c91808b0af15221fe364a398b81bc02c4fa6c5976088717eb70603163870be2e2010359a1b18472194128c47782a80255c8b1eb3486a8122aa1a2c80a870b1d24842ed7367245a1177a0a670dc2e9195354056fce67b608ac6dcd930e007c873bd9a3b950b9cb0b634b7fd8ebf2027fbb1e8eece5b82580edc5fe1fb5be43fad4d1c4c235f6b8376124423c2b2ada5cbc80b0a23a3f571d6e9f1dc132c3a6f0c5bf03c5be074ca18c266c9ebcc9da170bbcdbeab38ae09d061c7060f1886d5c6fd292ab2ff6b0724683b17211393e8832ba08d6544a4094ddabffd05cae4206813246f5c6c20d134680dd3a46285810fde053033d8eec23708a709b17218338e15af105d5dc938c2d4c52bcdd903815630bd56ab09e79a1c0b437343d4dab09e282a56f8ca297a8fb409e3eb22c561ca4b9a30e5aa490f90811c5b2718c290d9b4f040df8a3d01e70d4d8a0581103cb713db0d670efbe41ae8dacb2408d2d1987938651a2c2e850f523a642df26a789abe881ae21d07fb17059c355cb0a945840866309e783c5b7590829ebb1363f7d38d0cee8f05c27e102e268d47c7b52f737e0a8f942846c1f1abc1a9ef4a099dc2893f1ca01a0458735765c8c3f08970e59a4a33c3f41b870d23d9d772956e00aac8276152022d503d578927b9a4d0618425d3de94bb6ff87601e68630b21ac07eef95c708b0ce07d90a80dfca2ddf3c7213140cc1568a30596d1bb093fc64f85ee6e68ea7292d435e11ee041e13407c11015a6a07386ff668ef2bc26a7af9467f672845016982dbf37d55f368487ec60853ae288ec3037bbde789fd0102a4b25d120210fee66d60370fa2c5cb6b03832094ca8b7c5db58695a0f55a0e8b80dd9cf61ad05abe543af922d094e8f9bbace9a66b18e8185a2717ba8f94cb303dced1e55cd0ce2740a822a44a71fb5625ed980872cd5af1f126f16f7dee4d591b142ecfc77ee26d8f89b7aa5eb9794dc1b98c7e3e473a04ca094ddc8f92abf9147b9dfc9939a32441ddb816c551eff16163beb5ac283990d0eaede71a5b27803c4e21a3fa87c633242ec80af7d21883296180f29751338d08a21072cf812ad73e699ec72b1f5d7e2c62d44ebd65a7540a0da1c514da27e5ac6d3cf13ff5499c161d86876f49659e7c90043b2324730db637430337939a52f5fc28fa2b870066e3b1053fcc6b80dea7b5a179233b810443514a2bf0b5eeb1f533ce1c9c3397cee5b0f5a3cde27d6db953bdf87a7c99d10d165ebe381fe6829f61f2c60890eb7956218a0d0d6922033f802638d5076bb9489a376b6aa8ac67f25c4280922dcad9819583756eb7714b40b86bb8adfc21bc4268a4f8a1cdf12708080b7b98456eea30a55a6ff8bc5097b9e586382c47db412e438de5bfad90266ee2639c3dc2bedb3a94f9a84dc0b8859014486015382105ae040fc2514ec7802fe4044df0a3760b46439330f0f0f0f0f0f0f0f6f706ba4b6d6368024a49424a9adf153f8ee9029a594648a8433d417ce4cdc86362184d1863fda080d02010bf40ac50ac9f499f2b5ab325cc1873eb19c2fda07c868051b529a92123cf3a59820b00219ac6064bd6b472a993a476d6ce001362820011b5bd880c0183256c1a43e4f3166e855c1c61acb9f53da53c19f680d31c8fc7ef9265430f92ce750ff21afa77b0a268e8a96c73bd8b6ada6e07a77f46be99c25eb2d059362481a6aea99de1e29b8ed9027e4cadd28f8205a9265d09e29658b28f88faf7fb6d9d3a67c2818d3f8666a440c146ceee8b952b0de0b1df904bf25748af61bd2a8c83dc1260b59c53f923e25f24ef0f9647a8db69fa4fecd093ed8a866ad64294a3a6d825162c9dfe228799a9334c197902c5a24277bf55226b88a215a44537f25e208136ce4d0f71f73e275e55c82495a21e85e907973722cc1badec678997542094d25d81d913975ca5ea276a504b739a9c99e96d6f58293e034b7492b957eefc59304a373d25ff59e48f06d25421242de9f881448705192529f839074df23189521287d9e2a6d8a992378492a85a44935b6ed8d606c5c536806d5604284119c34d3bd2d79a47fde8be0840a26b2e45c5322a22a824f1354aede4ba52e272611ec560a22654919e97b47049b738a0abefb15aed14370c13268bbff6032a225c3109c14d1993d665536e11582cfcb9f9d2ea7641082dd2cfa73cca064705707c155f610e99a5434b5a80c41302a7b6be39f84a4ab77b1c3e8588055faa2053bee003202c1081193fbf9aa3db72203106cfa9317539ac4913193f107ee4a4726c30f4c12eb517a55374ef4f481534ae8535dcf49883ac4088380a12351061fbe14f5141a5e3a4765ece1f275d3b496255af0764d20430f2729fa2495b6cf6a870e301cf0001c376e84c1011d1798808dcb01cc41461e4e061e4c9b335a1aef0e8c48cbaf1284251d499b63c781c0160c68c08d1ba87164f941861d72edba183a090bfabd066edcd8a1038c3176dcb8b163c78d1b3a5c0c2efc04376e7419c3860d6263d84036c6172d6807e0d0e16274c18518a70b07dcb8f1858b21c6e9e24b11400c32eac06fd22554acab15931ecf72d482430162635879e15e14d70146ea800c3a3041dae44f8d7539863e073ed6c64b6152f3c80fc981f59c444c1b458e4c37f9e313e8a28b311c1007d6439d44cf312c37e370e0369ed2759d296508a63770c2bf4f3cb6ae471cc97003b7f77f9db263ee48a78c36b0c17c63f9a70f2d1dae2081a12090c1067662b59f8c41d449f30332d6c026e9d95c443019fd35db21430d69cdc135a5e995430b87abe0ec28c5011969606c4dfb6e64730c32d0c05d4a2633a325b520e30c8c9af2717dcde61b649881cb9644fd5f1acbc0c9143f5bfecccb5d2237c820039beede4698f24befa731f039a434e919ba83369535c81003e3be9e3f8fba06adc130f07d26ec5e82fbc43791db820c30f0d69fb55792f294542c878c2ff0232baff6f208870c2f30c1d4b485f4503131c8e802bb49a8532143e9a867c220830b5cf4283107a53c684b627761811b374c05376ee028db838c2df0d9f71b3d564add6b818f29f7c40a53da3cd959e0e249cd8c604158d0278b50be279e2bb0fa95449f2e09c9feb50297525636f7f414f42a70f1541236ba1a29c5a9c0aea9905e9ffadd53700a8cf29ca2c4b558c890023f4a5f27939f4d57d089b1838b09686087efe080b9848c28705e22483b9d37275a1e0aac5b9ff6e54cda4dc99fc0e62611c72d74c6de9dc087bc632da2e926b0aa96eb25242526302a3285ab9bcee8c9b40446869434428852c93fa4045652c81bed6d97174349e02b67b4b43e993de206097cbc48e3e621e608fc85749aa2c734b333023978deb6089c58a848a9b359f24e128111a6eec992a8f5dcfd10f820737da759c960260aa1282a276da7c552818c2070f6616363397dcc07029b2b22ed86d2b92ffb03c6f622e5ec5bdd26293ee0c23f72fc1b0f41a67bc0e7f8554a1649a23b3ce04df379695bab8f15db01373af39bdde4a4a242142043074b126f2d99262707bcfb6637355183034e729217e468ac0e326ec06b72b7aab497954d9561032ea5bc9a9625dd0695caa841b6412b92d438914103aec7473b0b4e7eedc8d49bf290052bde954f23668dec522c18bffc94549f62400a1eb0e0a44790f69af335ad690b1bc40236b6b0412a60630b1b840236b6b0412660630b1b4402362e30011b71f078059354c8316b4816cb84af06011eae60bcb5caf2ae294f9e6d05a739ad24d3525975b783218616f560c517e25b5269d15d8f55f02571729694d23278a8825dcf6717d306a5673b20031db871a30b47810e1d607c9123edb871e375a800061ea9e082ce6f2a29e615edcc0e555470ff1949b54474d4a46a233c4ec1abee6866f28e872918ebd4fbf020ed8292799482bf0a0dba5ba204bdfc48c1c90e161a3125798c82cfd5a33e652d3d9aa31ea2e0f562a5fa1c52a964268f507032db574590b955bdf40005a78334117358baefec797c820d795355ec8b1e4ac9c313ec8590d247f1d31325280f101e9d605490da994da8bcb93225ede0c109369552d1941d3d356463f0d804bb492413dbfc1f3bed9a6025d9e59dd8a273867960f0c804eb6bc18205d1a14665c5e081095427a9f157c97e31868e9580c725f88c3e22c326059d7181c723e47d06cb4be5183a2af15bd8bd9f679b128c8d92b9baae3777d4bf1843076ac063127c0e91458a5bbfee3872ec18230cc22848470c0d6c2567815f053c24c1061f69226ccd4830ae39a4a037dc8404a3e2263565ccce27fcfd82c7231889c1435bef7604b71f6a6b6ff75386c9a3119c4efdaefba57a30820beeda9b97443ce0b108268888a87f168c828722d8a4f4c42da12af7c41c143c12c17d9b56e9b859a445cb0311ac046d77cfdf158eca83c721b890f2cba2ffb628bffa0d1e86e0a4d999f87757082e87c6ce924e242f21941e8460d5d3a59844dac9fb111b049ba2c6cf79d46c33d382e094509bc54ac4f2897904824f4127a154f4e7c8db01c1d7a63a9d628e1172c5a6e0f1072e0909aef9727f52e843e0e107ae3704d11b5f1bf5ad471fd8a0db69d48be9b4343df8c04b0ed3bccb252478ec81cd173be8d254e5d0c2d17d881d142430bab00b00103cf4c0468e6629b45f27ab5efce09107fe93c633bb94c9e2c8f0c0597253bfd1be6c7d2167048f3bf0716d2d2d640e4916ca2d78d881cf509d2ce5300d95d781d321655656cb972548b4e04107ce7474bd749df4e50dcd811f75dde65532d6b8eb21073eaf280bb2627c8fe92e81471cb8bc9e665f59d4697239e001073ebf9a8e1435291e6fe06356341942f28d091e6ee05bbd72fe0db24c9989a305270c331978b481cb394951cfbfb1aa325f78183bc6d871e5c10626c9ec5e4a94aa1ceabec0630d8ca757f430b563297335f03149fb8e96df64d27f0b113cd2c0f765fecffb3d61d2c62c78a081c9299a9e2382d23a4967606db227a146d89ec528163cccc0b82921e26e2c9f1cdb32b0c92f467190819139e93791e4e7381a2a24f018039b338410d67b31256d1bf01003fbaa498a5d503a3b84b6048f3070a66c930c1d47b55f0a0cc7943456d25d2bc1e30b6cdde8cde9b1cb74ee385aa0430cbd828717380f6a7dbfcad3877ebbc085d7a9884c4a7bd0e102a3694925e854b7c0f9980e52e2297f53d10a1e5ac0721e0d2ae446aae091053e6fa8648d1d1ad444565df0c08297aa479859fc0abc84a851cb46895edb0a7c90dfde2e32fea9a90adc066d7df24ab8fa54e03e43f57966bacb4e81f7493172aacc4ba18ba5722995321f055e3325896aa5172bf350e04db3f376d929a1db7f02a73a7947c545855cdf09ac5eea076dcda263f49bc004cf5144aaccd39f3e133855f12c4b0aa1f2cc5f0223dcf472c9cddf48be12b8142237affe78d6f093c0968e65bdc89b62d23d1238b9e329c935f99ef547603f256bf650933ea57e47a5c949d34a70e71d84ca9643095633b86bc8ec24f88c9a63e79f940497512d491db7d4798491e083ccd9d245de8ed53e0d4864aade848aa351748f821d670336091a8f60848c5b413c54e7c71dc195345de7666a04bbaee7495e75868a0a23f8d0ab20624417c1e7a9ba20661d73f614c1e54d0db6b9f636b49208b6d2e9688f31b6f588d84c6434134a7608c6538a489fc97208779b8b2d0c0d43f0d9b549ff540aca275508464e4a8e11062118cf214cc5d1bdafef0f824fbdfb510b7241b01e96dd3546dc1f650a04772a846c49f99eb6090204ebc14699aa49273de9fc81f3bd1391ef3c7ee07774e3d5fb875cbbe9032f31644a9bc6bccc357ce0fd5753cb93660f5cec8951ebdf54e8bb7ae0aba2a8542a836ef6681e8cdc12cfd7e28907ce92103d31787ee91eefc0c6ad1c4c5abed8818b39cc3e3de705cfd78157cf9f8489c6b4392b3af04175ca7819a4258d990397530a49454c264a47090b68c8819598c13e84150736554a4ab27a8d5b88c3814daf9cfbe1f71bb84b4ae78edcddc089decda0cbbaf74edd0636a6ca2fbe9577da64d8c075ea5222d89fda5e740dacc8f6d49b37a806de7f84083958c839669706ae2496d03946fe247434f09b4262d299fb5eac7206ae84f6cf6f1e4a249399810f22e554d9eb32b06e19926d940e19f8902c54ee8da977947a0cac78796bce9253a5b41818bb3ae175a623e78ce068810e31c2c0ae47fbcd29497eb01218b8cd67a9fd93f70556f32af57684c5909917d83ae59d25a9c9415845031a5d602ba5a6cf6a4ae988991d6870818f9c45e8df36dd10475e70b181a5b105ce6dd369519a94e8a4a3052ee5e7fdd5dc1d92a259603746caa0eea7e244110b9c5b6acfea96bf026312526aaa7117216334acc048099ef267a5142f48a30a5ca5bfdbafbdf5b594a5025fb16c83690795ad73f431ba382ac017d098c2e291228aa8a454d19002b71f4443d787a8161b1b78c016c50b34a2c0a9260bd1b41e9e46690c1b1798800d30d08002175fad339b526a39b883c613381135bff42e7a8aa71750aa8481861358df4f0bdaf55354d57194c181461318b5cde7fe1a732c956101183ac240030d2670da83c48c6e4a62eab30c3496c029ef13eaa9367b4990d15002a7dff25f3bd2e4895d0e2d1c6178914a79b145175f58804612b8d4eb4a492b8a042e289534ea0633a55febe31928a510a071043676964ef622358268530a348cc0adc44b7a22e6b459544da051044e5409cf215b9e38a944042ec35a74d774567c1110680c81afca49bf52c4abcc7aa9020d217039ba74090b324fe62a08ec290926c1454408518106109890e495e89646aaa0fe80afd69c534b8714ffb328d0f001574aa594f4ea95e9b422a0d103ae57af92a6d2ec3b960cd0e001db63a3b761c98249d61c81c60e78136197ee62e80fa937020d1d70e2ff3175ac0b3adf99432b0c2ec45893018d1c70390691d73dd3f512061762e0d811061762744103078cf63bcdba1464a040e3068c77648d314516c976db804f4b216b9b29dfb7c91368d480dfb6d23ebf4d3d7dd1a0011fa459fc6c153263169cdd9bb6dadf376139b2e0a4de8e48f2cf34cc8805ffaa9236d7f2657fdb033360c1495131534fa65598f18a728b4dcad2d9aec00c57b063dfa52b2c58cc21cb21cc68059be49769d0bfe725439615ac66b4ccd851447432550366ac824f5971efb23e6dda76862ab8a483074bd974275d71093352c1be86fa4dffb9fd928f0a46435747e750b9b5834ec16d52da1d4d57bcccd514769e1434799fc508334ac189ec7699b71bc2724a0ade54667c932034e576678c82db6891fed385cc1005a73972758a9332a599a8302314dcc8bc4d2a5eefa5e08282179dd926a9ef6b62ceccf80497530eb641095d5a3ff7049784befc38d2e4a77d3ac15f5bf747b335c78e0381d3199ce0b46565fe379dbced83a3ca143163137c648dfc9eb7ffda6a08d8d8c2c603b6b0c136b6b0d1802d34c1675226f72f7dbe3023135cda10845ee650113bba0516666082b56c11746307675c820b41648af9f653841cb28019966062daaeafa0aeda2cb4b1c5186654824f7192501339c434da86125ccaf190439b9981044617ae6312dc067da6a794d6955f39b4ba0b303e083324c196de0d495e3a1b21ea48b0935da2e64c419060b2281d31974ee2a6848f6025fe577a9852e121770413728aa5f8e9d35a4b663482d31bd916538f8e3083117c74cdd74e593daa25bdf02202691b8043cc58045bd1ef2aa9529e79438ae03557f2aec816edb42711fc5a0ed9d6e2410463216ed01f2166d6c839041f84c4cc546117ed5a43703e7a3a53e45821b8d0e3993cab4fd54d08ae8450bde412f39f84e81003479587d1820d2462c62098a4b1d2d5f34316e90b82abd49bd9ed8329b33b10dcdf6e0ebab29724150182fd78edede611f278f40f8c69ab3f75b671d32a40c1e62366f881f5ebeb11d2ea2c5abe0ffc55c7cafece5515743eb0669182f0f4901df2b3072ee5a818214e1296ffd3039b4c3f72929a5ef3a57268a9000538fe781738ae3a2001731580e185eb08402066e481d39a1154d2a022a6d5f1c05d06559363d21a9edf81ffdb20313ceae474db0e7c2cf1d7b2cb1dbda375e03705b5187445f5e9920e6cbabe9873fbd69412e7c0279945e65dcf4cc927a6fd8b15580000aa98210776426e491294478ec98f0327b2e385507582039bd386beab3db7d1ef1b1855c942afc8f7b6f204ed08c60c37f0b59752c5bcb95deb6f0393763d5f48b55945c6b0819359b2fea8fd2079b26b60b744e8babaae8d9b570372848c41e8e86f1a1899befdf2539e986488063ef6e556aefc9693b2cfc08714da64c87631490d320397829222476b4f2caa6560944ad31084480c79f46460cdabb35e8292a5427e0c6cfb65a618835c0c8c25193a657dc8b1b31e0636a7dad0621a4212afc1c09eeddda8d2a72ff079eaa3a36e4a2add78814d7709a61642aebaa60bbc78784efda6e102a37394741fbf32627e0b6c0c4942c98c76fad1d50223536df69f8e27c966814fea937ecd251618cd88a9be976f17b91c5a27f8828b1d3ac648ae03479da0991414665c81314f2999e5106b7df3de0596cfb002a736a7ca88a12be860e238c1175ca0209d40877b5186821955e0728e223d847ecf2552ba382ec60e1c28e8620c946563030f6080316f30beb89f41056e83dc8865a332ef1de98b8303477f7170b0808baff2850e0bfcf109883106056edcd0e128d071bc281670f10503c0831953e04678091169d3bfa2648614b8b4416dc40b966d6b93436bfbc60d0e9828b0af395993ccd2983a0a057e948aa62a3fbf3c98f1044e099154b9b27437955608339cc0bbea086d79a46e507130a3095c08491e6b27fead884ce0a3ed77e7cededb9212011da7033798b1042645ae98aa4c6707515202e79f747d103a1dcc48023b69b494bedc9954f410099c52724379d01dcaab37338ec086c474ee1743974660527a8a418847af084c5caffe4a3146df9815e30b2e3870e346210263fd357a3b9dffc5b0cc60c61038916b4cc4d19f35e9940b6608818b7d2e4a853ecf82c0a6aafa87851ac78e436ac7026a3003086c28c949691fd359321983193fe03e65f41cfc7447440f0b66f880519372fabc37cd49e66c1166f4804fca247aba8a179ec61d3ce0c7bba36fe48ad88e2f8e0e318a0161c60e184f6a21aaae9f04e1660e66984eaa246da9949e03be72c774e1a9ce269970c097dd56ff58325def8a0d66dc80112965b1a0aeed554b1b705a7931e36a8ec9d3ad01e76a75f913626d0ce50c1ab09ade5a33a485560b0319b3b0c3b36f5398c9dac5f8e2a4d3012bb2d08245b5bb78569b2423167cde18740e1a3b5870a79dbf6486e0173b5d84e185eb0b64bc828fb12f0184e025c5531a565223bac6c60626604307c1ba9f106db59932725210e64caa83d0fb2924ff77081f81e053df5396a067396777820f40f0f9bde6e33105b533b1e1e30f9c773495f75250093efcc0c7902fa5595bd9c79429193efac0a7d7575aabe8aea0c307c6fddc4a3d85e41e58d3b4906741add6d71f7ae0cc3a06f724624a4247ff9107c6266ba747dcc9a22d1f78604c44fecf154d738595430b8cff820ba2838f3b70aaeee65e5ffa473d36b610400e3eecf0f9a638d16dc4b31c5a2b383a70b8bfd58193c147a452f1e4093dd1814d2e6abb746ce7c0244d49f7a27b7268e506bc0b14e8700a9c073ee4c0a8512f21670b922cf42ffc04a6031f71e084ce6bff4da1b36f0a072e2328ab0a96477aec38f0f1066efbdbdb424e11512c6ee0bddb46787814d16fdcb831838f36f0e6c9d2fac95117526e470e1d384e7050290a6480021420a6c30b2f8a083ed8c0e68a14a93dfff4d28f3570b5d9be59faa5e5fb871ab8d6f02c916b64d221fa9106bef7bf9392b42f42d51f6860474566919474d37bf6e30cbc89a6cc6a29a6f4261f666063652d4d32bd9652df4719d8a4845f977a503a499e434b4df041065efbcc1c03dfab66427e4e36494d7268a1e08ba303c78718d84b217ed9b985d8df8ae1230c6c6b2e4fc1c3b4cfd361f80003b7a75f728ed61633828f2f90f369ce21626e8ba60221c0d11f5ee02bac56c453e66f497268e1c8808d2dc84717b8d2bd110b1d65ee1d2e702a9ab698d47e47af6c0c1f5be0a2a5b8a9152adfb82106185decd80f2df07ea3465e8be6230b6ca51c44b4ba5ae4a4fdc0022337d3b78eaedaef908f2bf0315f658ba57b52d2b815f809b2cb3dfbf85105c63ce43abbbdd2967e50810b5a2c988f58f89e3ea6c075468f9e3a434a7c488193626a9d7a2df2c75013cf131f51f880427e31eea952761727c761e0e309fcf9dba64ebaf7e39999171f4e602d87aadcdf4a11d9a2253e9ac0240b21656b089120a41f4ce04784452dd31c423c1502bb858f3bc894d2561c9d600ce48de304607461f0001f4ae0aea35958974aba52b4c24712dc16d3bb112a6e60023992188701376e4ce103094cd011a39a7dae12253f026b2e4994c80db5592123f09573cab896828e6b8ac0a9acf02836e61584ba880f22f06d2a78d6d05be1ba6b213e86c08798be1f2a3c4d16c78381c330c013172ad0e11a2895237d7174a0cc0d7c08c13d7d6abef7dba1a4021d636c91868f20703ab3b957a94060af445bd03b293d7d82838f1f305a216d52952e59d5ee0336a88a646eb71d64090350838f1ef0f93de7dcc9d6d5d7c4032604f53cff705bf1b1032e8390a20e387d4164c93622fb8765630b1b5bd8e8557ce4809718ed3bb73c53c50f1c70c2f4a4511d947e52495ff8b8019354445f93b89fab83884de0c306ac4d7a2d21847077cd7ed480fd125943ca8bf9e18306dce8186a84ccd8c13212851ab3e0a2272931c82c416551d240b1430d59702986d6abfcf70c41e9c51874a8110b4e459219a9d75cf72758a8010b6e75344c52a6e41eabc62b38e1f527b45e7ccb9e0b430d5770414da9c896bf2b06b7157c6f1659fa9e55f28face0e257084144ea5bf09c55b03569432c846de88b2ab8eca0294216eba81b4a051b64c648769a6dfc34d77109811aa8e0f5b44e8ac1cff2f9478c8370eca9710a7645c6cd8ba233342e7c8c1494aa610a3e7752dee57c8b399dc610354ac1e6502a8927350bcac73cd42005df159a6388aea87aba35d41805efaeea2198f47cf92d68a8210a26ed56a5e5e46d691dd60805e3aaa6f9d53e3540c166f178d27cdcdda2c62798bce973507ad74ba9c7136c9e7cd283dcd4e804e71233aa44bfd0a12ca8c109fe6d3c27fdfb20121cc46a6c828de6f76d3aed4454870b1d61580d4db07dffb9419b094e78d69ea9742931b939b42a063530c17a301df3c70fea4daf4bb021d8764848a633420d4b70aaae9e63142537422cb750a312bca8f98ba81af364419460ac430ea62fae5aa831092ea4cb9433955fccadd790047b4ae8f04832640bc2ab1109c673ed8b9ca06cd4cf1c5acb831a90e0afbb228f0e41dd9258e311bcd9b8aa988b0993780d47f0d94386beccd22c4ad70836dc4f07651d32a10623b8fc20ef42d59563c721e6801a8be03ae7c5494a868fba48166a2882cdcf5a9b58969e16fc070347f72916811a89602c2421d2d4acaf236e705003115cd22023c70c22631138e3a2c621f8ce7b4994d68a212b65085e23778f2cddd933a61405d42884f9cd27082183493381d15b83108cdc284ae4f4bb210779d526a0c62018c929df7e754a923e6b420d41f029c2bef2b90969a5d608049b3428a549281b108c5ac664b5ead6f803dfa731dbe990fbc1ec5e55bb27cb1a7de0b49bee7ade4e1042abc10746b48894d56a4165f76aec81b37613ab7f6b7d8d1c126ae881ad4a6f13cfa6b684d40535f2c0a778a523a2f7d608ef0635f0c0ad69932631ff8d3af91dd8eecc3156d96d07264b0eddcfd014a1461dd8607282bc92acf59dcba1656450830e7cd6c5a45d42e4dc9e9f0397eb94ac499e9d9bcb814b9b344aee7890691407b6ec4dd7f78dfa2c7138b051d34949195266c4dfc095e63d91e2686d44ddc06778528d1ce936f8b731d69a8f121bd86a1d393a5f7bbd92d6c049c8912bc4a44d7c4b6ae0e32569a93508a1cb4a6960359d8bf6efe7cb104203b7eb163347d252223f67602c47d2bfdb1c3330e66e5b1dd275828897812b2522932599f44b78c8c0776513ca3f6dfc0d9931b0daf9b2978f0eb14e2306565f5d6f57fc523313064e2591d7bf3ed7791c0c9ccabbe165ba52b4e80b4cfc1b61315a1ce99ff50297d466536eb92ef0a7dff6c13ec7acd4728193d183f9a515a13f64b7c086eda5c62879d2a6b5c0698aa2524fa259e0b76b6d62c8799983140b6c348f9f4765afc07af6fc31c7d3a52cb4029fd3f44893a0a24d50abc004212ca7ec954ac94da9c0bec88d39a81016c934054e66bc1249e74b152229f0e51a4183c8a54382320aac9b8514445fb9a832a1c06da860aa2c9d89d69fc09932adaba43b81df51ba62cce1e9d3a8097c85901acd6702a3a642be3caafde359021b2fc6603906d9b7a612f811b58ff9fa25940476336af85eccff202430a24c9608d9edb45a47606d2f7e0eeaa1e349d008dca60da57ff222b0317f478e9913811f573f9554fa29350e813555710f293c21307a27679f9c267b8c5d1058d5ca9072b4030267ef218b12964564d00fd898e29d2afb33a7d0075c6ecf65b2ca2fa3567ac0e9602a7fe2a4f080c9a93b9a49ba3c95db0ef818546dd7064df144a603c6bec2444595d4ebb11cb095cb4a72f6ce66711c7041cf7c736faddda56bdc805327b2a434211bf09fa7314f87906fda1ab0935b47ecd574f4b4060d38f513aa2f79c5b59c053b6a21efddc9a8db952c180f12e26de6c7b87e2c1811d2858a9be9939081052f31497c2f9d4235f90a36bbb3baafe94a52bd2bd8a0932e765d48a14c6b059393ca9a83b5ac60fd4cbd0a3e4b08376d49a7cca2a30a4ec5dc935372489b924d055b7d49988e39890a36861092734eedac693c051ba92208d19ef89f33145c0b6898820fd26209095ae3dbc74bc1fee668427207d34c2229f8d2f9d1cc64879c9a8d8253dd88792354be4314fc6810c9724d3779c943c18b44fd4e32dea0e0a2c5db20ba235d2bfd092e7708393c8614c26ae409763be4ce58163a69e67482ab144bef7258999887138c6a93d9e4ae4896acd9049b2fa6ee3e32a5c78c26f85839ebb3c61e69419960a4efe51482f4d2d7c18449249172df622ec1c9143cb3a6a5aaac5a821149a925510a82e07d726b5ef2afa0f94070266408f393f963ed80e02f89a0a1f4d286eefc819164eba15ba134c8f88109f69e3a338d7eae7de0542da7de562b9129c6074eb4a63d95bb228910db03233fc7de4fdaf4938e1eb84c5a73c84fefa0993cb02ad613634af1c069c72da59eb9acbf3bb0f97c82767fc60eec06491b5ca3d8491201831675e03a8a75e79c2157254d07c63206a5936c9115f4670e8ca7ca206388460eecd686cc263c64d17be3c0dfa8e6e63af9164385031b24e80f2a47c9667dbe81b549b9574fd44c65ee88e106f3fb6fe7f70b2e8e18ae23dbc06ae44c3ea2d3a7feb46003934efb95fefabf3f79166bb1063e8b07f3ed77b38fab811321e58a1213d1917e1a98e42243772cb9a7bfa381b7092149db889a75e93370417bd0d4694f3b7333b041a8bc1a2f8bd44b3965e064875b2acd8999364306fe4ec99222bdbbbcdb31f0b931ad7c6442a08518388f563fc294302d790d037b22af629b0c4174a7e4d04a2dc0905a7c818d18a225a8e4926397fa85165ec01468d1852b560c92cb2aa5985482a50517d2a5ca936184716e145a6c814dba6b9fbe628f086e0e2d1c8866630313b0b1636c60021cb05068a185d3220ba605160a2981165760b466cfdaeaea6b8bb50019066861054ed6d85dee5e5125ea2ab0de952e89f85a323df4002da8c0569508caebf2efd42b052da6c0ebfb69a5dfd96b74a5c008b541837e8f16b4a951e0b3e429b5dd5947d4f5042da0c0572af51aba792d9ec009a54330a1326f94667368556d2079181ab891650c5a3881b1caeda7164f4de49bc07a0c399e3909372162029bdd2445123a889ae812b8512926124f25707d2bb12cc34f021b39078bb1d4048fda2381efcf9e11d7fa2370595a7a1f9407f5593702ffaf49e974bf6b99ee2270359aca6dbcb75a6b22f0515f84a5ec8b7eff10f8d29f6ba38a84c0c6b21d592f9a93dc04812b619bd12a757e3281c09d4c3a7b24d30f98a0397ff04a412b7dc9077c8e7977cf54520fb8f748f5fd67a1829e079c1e57b318f1be436807fc4911ef5c121d305a2cde955273c06e0a1952c896d702075cf95edc4b934ce710a4c50df8ab7c93b694bc95dcd1c2065cfef71ffd8a17f276b4a80193269824cd7ac9bb375ad0800bc1268594de654a6d66c107ab7849c8989105dfd1ba946c9360e9df587097b935686adefebdb0604f8b8b77d03b4f7b5fc1267da3b39ff0cb5b5a57b01a74db238af58ad0b682cda3fa94d0e21f27baac60b3630e65c164ac28d255b0e1a643f2dc54f1185505576ba7b3aa8d7e8d9a0a36e29dae1ed724ed525470273db55fe490ad969e82b118628c94276a0a2ead56cfbb735ff452701e528a112dc7cbf9430a2e4dd4d39eb37b8898a3e02a2fe535756ab52b290a2ea58f299d122ae2d6a1e0ae4774c49c82f60c0205234a63f013fc089946a8969e94a3e8095682870ca242ec049b3f2775a2e3a7d61c7282cb7173b668fa4e63ea36c1778908955384741e2f9a50d2c84efe37964cf06b9d930c15b1eaf498e0db43ec4b37e61fb376093e76c8912442fccaad5982d1c8ba5582df929a9ac3cc7d45a3042742c42cd974c658a29a04a34aa62043ed2da8b52451b220e3a75e5224b8d6984bcb74a9535a48f059e47be8603e828f29b2950cd13cfae8087e35457d75e27b758de0933c53afdd63ed298c60a4879c91b2bc736364116cac521a74288b7944882238cf9932af3289cade26825395af6a3bc9a415728a08ce377656804330c9e42695f431c5c43704972ea69cbadb7527e71482136b112253660eb23d842863b0b89a9b6206c146755d13a1a729a95410fce994d74d9d8e869d81e08465aee6d1699f441010ac59778cf92b850afdfec07936d7bffb2a15acf30393d44573d3362de84b1f3899f4f5f8ed5e755af8c0b5bad5e40bd97e54ca1e38bfd431b5edb607ade881310bd3e6eb77af21250f7cda969cf8e9747a53e181abde9cce844a4d975377e06ceb742b857765b4ecc09a4850d1939cd4b2aa0337d1dd4a3bdd4a448a0e6cb699aefd909a03a327eb5637269996037feda6e63147f41ac5817135dfcd6295f47be0c009fd953d6955eccbbe814f4a081f0b4ae77817379823c970cbd136f01d5b37828a101014800dbc29fd418479148976af81b111af0a3ae389f4d4c08a0c7da79a9a543594063ef44ee6cd29b134b268e0f55465ce9ef9b7ef33b03af2ba479decae1833b0d53157363dd2b72b65e02b68d357593793c4c8c07846091e54ea1bbf740c5c9fea335da23fec1403a7424ffe5c4a448b2ac3c046cd1959ccfe4566c0c068d0567b4104bfc07586e0f182bcc02599428a88f9eb02bf751593d2f47181b3a4fbb2ea2d85e5650b5c65fd20d3a4ef85a4a205bee295d41331bb4c2959e083e5f62fb10d8b55c102232c8ba5ce26bb0227b3d4fdd52651619a15384d1fcbdcb2d9e8d1aac08d4833952fc92a591915d8da167df7a4a64c3d054e7f571231674b0afcb6bd8898c98927ad28f0f9233306f118a3490b0a9c485c09b9d3e94bb39ec0f5c79c325dccf3f5ca09ec579b664b3781b5d12d66723386f299c0a9f79026ba2a33be043ef8a4281a456c728e12b898ff944e891f1e9249e0a3889853c7cb413f8804b637447e5fff09ea1d813b5da739067d79296604764c453ce6a0725e844ca000456037d4c9899e2775d7446093d0bdf5214534290e8191e32124a4f6244d6542e0b373d404d3d0be292e70b818a71e508020a0955a52ca2ac1c3ddd328fdace06b4105000277a2471c42017ec096674ada92bf55aed1d1a0003ee04e64d0a1f35e6efd0d0705e801e7a22663c7f1b8800b0dbca0003c602cc80c936f1253e5ba450e0ab0034e9dc64b29b5c69c357643074cbc8b41df69ddf353b7a00039e02e549b524205c001ff1d430711537c741670e1050e34c60e6501175ee0f03f0628c00db88b1eb3fb4325e8e5779c85c059060a6003ee227e964eeb9492fd05a8011be2e94f5a2359f20d0b4003aead455e8a36d3eb3139b494371815b8fd9805db29e6383abd6e0ed5f9172b00030718626091057cc8828b933d66f4ca3e69f7c1b1e3c610e368a0051fb1b0ac7478081e3432f880051f4b6b8a93ce7292267a059f49a72821c6f6c3157ca5c66c6923ed06f9b582bf2b1d2a241f3f58c1e6a86f4274b86afed22af88f3c42574cf1c775f4a10afefa46a78e54f57ffb2315ecb95d7896d2ec6b3f2a18ddac9b60ca7d349a4e510ea6378a6ade146ca90f1643bacd91d4a8146c5b881ba6695dba37a4e026b5f9dec56e14fc8d9dcedd561105973ce8cce9db534c950a056f6a5f5782bec9a0ef0728182147fe5f0eea4f70797793fc623e3fa1798289924d4bc7ddceecaa13dc7a2475f31141098f7c7082cd223429996983d2b13e36f1a109bca13a3e32513e30c1c5a08386c4bc5832dac5c725b864ba7372fb6b89d1cba159a47c5882bbfa90548a64f5163f8756a9faa8045721b46b497dfba0e91c5aa4b6bcc1f8e21af04109ce638c28d174ee9849e863129cde04914d968e24d88a112346bf10178f5d043e22c1eaa411adb316f7ff3e20c195da7595eca08f47f0df1b838cf963b6baa00f47b031e45831a51311a34a1bc1bafb687dbc5c9bc6c2850f46b0934c5dbca4758c41868be02ce4adb4bd493fdfae08fe43ab55501ac2e123119ce6fc1ad48990d32d09116c12c1f7538d8cd4f71e828dd949dbfd53cc0e1f86e0b4585a2abb0d19ca367c1482495ebebe6fbba5806fdc08c38b84868f8c7da8a4f024e25824100883a1703810808102e603e3130000000c201486429160248db465fc1480034b2824443e2c16281c2014161a8744c24028140608038160180c0884c2e15048201ccf84bcfc23fccdd9b69cdbc999e79ce7382bda9ccdcd90cd990f73c6dcce96250f9f0356c899d94abb0c693383abc7709596ce29ca2e9d82b9731b616f895c47837bfccc3d8f7b0ed169fade7f1c99303dde814deb1166c45d17335dcd2866080662750208a22d4acfbd8604ee0af7cbdeac53946a23355256678910c3f65c9854a1168be89b326a1661224fd6afd2d834fa7f6ba163cc780669085c05be215e24f785c3c2091c82a5b011009d7eb9d6810d19965687ffc01b79050cb0f5b5b7672bd90d49735ed176c4c29d8d26ff6656b1e3c5e68d6ccf32d42c88053d878cb4d89b356121c6e908fcc0d1bd5cad382fb3b985f4189e1093ff24a0eae6b9fdfaa5c7a996959b0743986eb3e77bfe4605f12f85557bcef57a853fb8c2e504163bad26ee3c3ea29dc52ec3ff66161d54e2d4b1c5b3e2b1d8a8fa2dc1c74d655e42884f4d4810f3bc45686720a124b49317bc4b00dd21d03b6a1336613f500a90229a4311fc3d17d50eb0280a5da010519193d28dfbc28a775be808433e704c21984d39b0c669aad8aca0c5806e182d8d867bbadb9df3cc3f86b525e4119e3efb0c8d706a78dfc143abb1fe5c0be0c2c521203af53ca677c9ba39e3cbeca9f2bebc27be75013e16e90f2092a199c3f5820ba5d5bac5913d05fd3d76a36e59f99f62c3158687dd53c9fd0d0f09e2b431be040f1436264a3ab96e5837d71dee86d59bb134156ffae571f766bcf11e8cf7cbbbf24c79a93c40de9537d1bdcf1cfd78a3c48f9d5008e8e819fc591a6ada7061db859d298fb06cbd5afe4de88881a8bb791ade8ab79f6fc28c1d2a15f31502527a13e44de07ae7b742f73c0bb357ef11f662e12d4432e6e2d6e105279c8059942ec486ca86b68539070ff4f306dbbcfb8c7f6469779a9ad6d6e460e0f52feb163274ec32f5c5b8c9815381b6604c5444de1d710bba30f9860b3a6929b90bcd9de75673efdc656e3f37b43bd4ad706fdde56e6f37843bc48d7add0ade9271a6ee3e7ea8e28d8a169d272a257e744752c67b9c5f0aee9cdb39a6a5d6a90aa9876c593a757dfb6dfecde67b6c4b5627db17d51a7852e9d76d7a612591324a962f1a4ac820d435cb2b47c6a3885148b1210d1152344927471f21a5202279479bd1ecae196a8e968b5c85bba4161d4103d0b8457b982332b1d19553c8189acb7927775e74317ac0cea48f51a9c552f9a8b1e799c1364f4e29153cc4d252a0a897a4a537a33a6f15869f05fb91614864694b76b2e641ce1cb30551477508c0e4932148e488940265835ca018c424be145e26d16c32e43082fb265165683c125a0171f9de345d0d14722bf73ccf4b5eef8122d7d0dd48bb5130da3845b20b0ce1fa21a9263f386e59ae48006dbd0dd2fb4101030163b313ad0e806b7b9135c8d4e8b36d2784b8b4ecba2f7e6fbcccb7bc7e71d0cb1288cf4d76b2f0555c40df0435082727b1ed7296cd0a83ba6c4df8d4210763e4ffece9e133f723b60a90efc57503079ee3f01ea559081746cad4dca70c00ea7d8b58dd8bb06ee401d05e25a05eb73dd78d934ea15db7a1745097603af90d5070b4aea9f052948fb361c7552134e496f19f0c34dbb77ce8c0d557251e7cf83215a122f935e5bc50db6d668269bcaab06e8297114998b432c92ce1d823e6783c1aae200af5ef57218fa4fdbf9dcbb015d3dec5fa2e8ab104889334ca906cf1b43a385ea9c07b2c09dfba501a262c88e36d4460cf7c4c3d4c0552c389c1010e92b78295a6b1e5d9495c0afa9a342b588eddf8989866462f8b5b2440cd9061b5c69132b7c48b483c7767d28780369438019d780586ca75b854e7d910baa21e6ba3e173eea11140c9b570b904fd41ecc560a217958c710becbd45d5389aded202876af974244a67c73952ad23619c2359f59b6dcfe3bf27b20851dad1a8b476a1b437443670d596c9630e547ff648a3749d2e00722ef2c6299394c8e993409bcd1d51553acfe093925723dea2900c7a55f6702e5f1f0a5cb3a2e8c8da7eaf2b330320861109db08ddc8add9f8b2359bb21ad000cf634ac2f598b19572b5d7708f19919632f6ae6ee4c0193252aba1ef3f8d4c441fb35297b165a611137747194e85e0883819076371a71099cc6adf9bdf4987f02ef182fae849bcddee61fa210009f1bcf999142eb03226982cee916ab5b02d44c8638000b246e30d3df95dbfa1be68858ae49e3fd0867190ea866edc860df2691ead54994a8555a2810f9e03a87b8adb40b4b81bb791cf86d3baad762f010aeed10190c80f89f19d2021873ef935831249264f3377a1dc9c5696c5c95484ec51fae7c0ed7a521bd037d182153ddbb0943d6f5440c1bd0ceca4fa4256974d0162b8b551462b1798ef7f3d5d5f44ca9d8686cc723e80a2c24496afa0f1d15133b6cc24fe824cfcb0e117cfea3abf962e31cb05af36e1be6dda2c07ec38d240c0761465fa1274c1a449b078c7b17401a5866d7a3468687e756852de462442d1c01cffab5d7f9e8b21c98e0b29fb214d56d44b4e6d41485d8d840ea2496a5dd51c30814c6f83c87ada6c8dd6980db039807197050b388d3433f6c9168949cd2d6bd8e0a70f29b7535694a600fee9b95fe98d8f1a6058cf8907852b3ef7ce25afaf81217188683c5459a21db11b15543cfeb47038fe752987062bd70956d0fa79660bfe538c4d10506ce825ca1ec9f18ecfa36c6a13045040410b2442bd5599307717502e0e697eb016517d8b090b497808392dc5c5a9bc8bb9d83fbe2f26606ab7520f9aa8cab5345cef62ea4796f45b325e6350c351528a86e3341311be125464182f4c566fbee8a3685c6452dec82941eb71c65412a74d56baadff13ab9b36d9c4a1ee4ab0575c83e8b488d15d9d3da1728f98bf1c840d621ffefdc294422da4424ba5e55013bcca5d07a3841724605c56df971d060567a16871405fcd63ae10619c3b8a9cc4f2eb4f6cd9182de961267e1278c9d59e06d00f5ad3a38fc3f19846635b4ac57707981c1b374094bb306cd73a02b7ed97a5d36822429e2c784579fda9809789b0e06ae0114ffd2da224e36f20126057b42f015905e4e28c09a4c915f4e2cd73f5ae06e0c45fe7bfd04b68f4267c235863e814c442c77631c9f9a34274e0b1655836d63fd7febf448e5ecb4e9aa1f103b0aa1bd456b3727e60278f5587faeec1aef32fe58362324bd29f6e4d061b7a9535998d0965b54089746c243fc63d7214d999f9a7f864c94e08ad517a298a654263eca4912262adee08a1b85a85cee0b35e8b67ab9c8e60216e5a5b67aef7004410477468d6126bbc8a81240518a7332a8a205f5dada0225f270174a03284325868ff7e2e03d05ba1b6f23533bf0a2aa8b73ed36dcc197ef27d2842010caa5538c60eaa81338aa1a559043f5658963ee5da999ab2847181ec470666b9ddc796797f29e3ab2270833f833991556e4b030611163d0ec60baac74a6faa4fcc27c226b87b2700c9e5a6cb4379bd3aa6b8cc9254b43eddb05e61c43ee4a91a1311891fe33f358e8da3178d6b49eed1ce3ca810fc8f4675039a7e6af4340a0e8440a270e14efdde9276a19776a26df890ee0ae41030858a7bbb4db02d4ea274cd846b7151ba7449874a4c410dfd89112d21f9cd127049c80c59dbdc98e8a95e8d72bfed25cac77c01ffb338001e970f2beb1e1a1c812977ff27238001b4d8522051521641e1e23e3480896fcfe198f6855ba8f995bf1a80e8bad7f4adc6143967bb1806abcf8e590abf26801b2244ed42b22fc25a3f248712961bece57c951a7e6485e4e4553c6ca8e61dabaf409bab6bb65cccf867e69054d0f621b84066e0f219d3f34702d9aa420139d0e421509e577c275e59fcf874c6acdbe1a741a6102e9f9464c478da4f748e9e83c6c3853edbcddaadc405b6e3a0bb8cc7f1d1dea915fc142bb8868bd9366b852fd7918f263e359a32d118704358481daf2149749c8e0003034d731cc498f0963b52301352e8d97f3528518e120c98f967812fdb5e7f95c63f57ee74bcf85af6a46e4d019e8a4b4318db5023c221cc603934afb720f8d4dd6fb2ed54700339a87fdf43379ddb237eb207ae32695bfdd68bfbc8fddec44cb117b95a286d37e49eb5ae809417641b0e7ca3fcfe6c6eb8ff424da114e99c39f4674c7c55376e0864a7e7ed14fec299d727f223e655b7caec63ab0535a3d14e349cd31247c70b4f472f4577851691f627052e77b25932965753d54abbb43e051ee0082c9026c74c450ae42203b2b47bd9c6880c284a0530edba2796dc8962b62155ad3dd2d6f16cd0e7036dbf80976396180a58f54e25c63a46b57cea14db74d96ab12d3c5e754a7e3991a0f3081a5105e3ded0cc4f3e83e9522a11daf3d8876f3b1eb4da137463fac6b2858bdfc14b1dcb80d0cc2dc3ea1e6c4c21fb0db448df5116580b6e05c40590d9240cf3521a4e8c2352f837302892e1a94c834369f51057c6bd385d786d3c97cfa524254632d407bf81cab69648308f9bb6c81c8b1daffecdcbc1a04c040d53b12e167491343c9244bd10ff11e5f2049c6a3621a5dae536c88de633c4556d5253f9d170516d98e0d5b654e72527d520aa0f16ad7903bfd25a5b1837c20d7ee24bb6e640e5cf09d0f98883787fb0cae54ca35f87d3bceb2f08857d634e990ff8b2a32daff820fda3a9423459f74c0e662ec0ec29b68fd39ea8c840160a94ce98cf1dcb405cceccea4317f8c473c0ea007198e6880ed9a8fcf4a83c2fab7ebc7be473aa0136eec07269d701849a0563825409352a3b0104a167275254581b92f503b8c2ea8518f386c8b19623ac78d146d58447b1d32fc08a355c93a9e77e16e99a9ccbff2593a2afcd7911958e9c2a3aee748522a3927e5e8712f11214ee84a11aa37e3a19365e9fe1746c86a377a96207a4615932a55eb100ef6ecde046f73e6a2a5b309e53ddcef26ebc6eff80d1e1935d93590974c0f1464dadf78ac48c95ed67f996f064bb784496ccc348cbf448aa313222be76bf78f292e4b81c1759dce6bb36adc3548e6825e20dee8fa622b0991e17915b8f8c1fdac02257dccc73dd8f002ad3061af9497b795b4d80b0084f9db04fe7247c9c0ffa97804feea0a05bd04ef0a0d7a6287c003033e40aab411300e60405b63ce460cbb452481bbc0e7c4461c193649fcab96ccb365aec8185e03c7c7cd79c094ca87856ea12ddc224fe4a8652c8a4b7126d55c0a6b7d70d25dd3b9e0711489252245dc9bda42d29c1290e7d5744b9225d4b01246aa98074f3b4c08a8adad3d23eae3cb180425d25d24e5ba7fa1392e3c45e92c7298e5a96ca3a719510ea523a78bab45a4c5d484f9ce679f5c7e26339f24ab053759973a37cce2bfecacb5361c0c0bd66a9fd3105b61f8e6837a87f8a2254a290ea10a9fe2d1aa7f228595dd2fe6f670b2389c9baea04359da53f024271ef25fb93fa9aa5be2662a96fff3e7696a2686435b2fa4a152f89a0e49a7cada4b30ad8c48fb003fc8158cfe90d24b314a0efee034d51f831a85f8c61e2a596954d4d70803ab3f3ec1b4d73a4bf8e55ceff43120e5c184d88d16054f6790590ff38ebf95f2201ec106305819f1beec2b43288995be639f3cd3cdcece819ead019ed9c7ef93eb51cd0d6f8f755420932c72769ce1c4fe8f14356b5973fc67714335cb4f3ec88ec299de90970f5dd73418865d253dada3aed3f96b829bf2284bc8cebe52c673a8f426b1888e493ff4cca36103a1009f3ee2c537e14fbb89d81313347fe8370035d514c86d40bcd159683b6053302ff9c96d679a30d68f33e669c78a1dccb4f0320df894e9a9b54f462d27ec08834efe3e400d221869df9cd1c220c1d7e235636327b4cdcc1d085beaf1c2113c30cd4e495f32e2bc5cc99d1bc637da41ebacdb543a2660bc70f20e9df33a1ad3566dd64a21b5475efa082e88b0bdf3721601ecee0025e3dc4deb67907de0c53efef0de38b020d7a42e71d4c8898673d0e1125f36859e1f51c1c6c185c7eca1c289c5848350a435c048cafb790486545e2c1e61d0c5e7071b13e7363184aba2434849911e806b327f01c4b58117ad9a256c56112184b25ff1187f54210d93ce74256f0f48ef5393044dfafb6751e71d80bd8822b506472b54caa1199a8817f15dcb0165e31a1a9039a00f9797536944cfbffb99801671d38f2e91e39a4efd422a11d0d8715c17562f42613a7461f90e6c42f9a74d86309d63c1c4e4877bf145f5a7a25256d64268cedbaf2c7661ff060d8207405e31db6c642717a6316b8cc16c27e944218dfabdcff02df4ebe2e7e7165934937e983215e16314a0a8d79cdcc05286cb6cb894de0d24c0069a9d852e14bcbffc6077234a2366823ffecb7f598eeb98c6ee7039c5a890baab20df2a49e4b2a59766d26480ac8330726356198778953cdd4b3eabb5400e8ba341271c30578f3f7a18a7a7d6dc6837beb0659bbf598dc4109112b7251148c97a06ec0dc844dc50f234e5258b828d78a6f364ac3261e2d2a60ba478c32af82743548901490f42eead5dd515a1f97d7a11704265f15fb8b7af19608aa9fe8509069d66c172e09f8291af272c3effcbcab6ab9fca97076e60628f93afe921a0b7e52b3748f14d5e1e3491281a5621d096f04a066f9fc2e306481aa260acdbf4067db93ca7a3f06be8e6a61deb5a681befc1d310b60a86c2c443a03c720aae06a260ab51bae780a53bd0222ba3cb4f927769fc084178f13aef04b12f5d1f74e43c118a49a0d519508a115f5541c00fc904813f3300135cb35c21c806ae3ee9111634d7f9d8f3c5750c4f84a06b5f3c9e8744444e590137f527afc65c7a02750d0a08028c3321594737515e0235dc67f10339f949467c24a7c3d7e21eb99a524dc37867bfc1af616ae3613652010d5cd4d05b67e0b2165a472b8e07e0f35a4914234747f81c67fb88684cd24e66ef858f545da8249b63525d37ffa641c20c5d6bc0c7b14e0d3155cb7ac6c0c5bd7955de2430101f3daed09c603d7350df9f4c09a813579cf234f9feb00117b9507bffa46135451bd260b384aef457b1bfc377943099bf87753ba518e0a2795d583b017f217ad58e18ab08ef3c2757d81348959b1630a97cac9c233790d84ddcbd94a404001295a34115cd63ee9b6201c79d720014110c08f40664d240566c2bda1b5c7bfd148953a56a4f16fe1fad1a26eae501b49962a50157555bcced0460fe98e3ec22c20ad98b7c8aeee7c284df811bd5e36d021a8defa91af1177e25391ebd5e27a6946184ecb8845aaa2951f2370586cd6cfa5fb55a6b6f9539fec900388af9224beda6a9c2c8bd12d56f7431dbf5a701948eee12f1a36fee9997f7d1067ef4accef88a92088a254f5a39287927a0af24c43f9d9d54bd043c404182d8a7eb26a7d6c5406cb700b7b47ee9d0f21d5bb3fea7dabd72a92a41f5b877318f729aac74abe9160a25d123c13af47541a53479e17158cbccdab65970878ced3a0ec1bea7c997ba7e9f174d73bede04133368b3719b3da7b199f52df3696e6f7498992c79b6561aef1ca30198f2338862f1df0a60a61c10156025dc54297d3ce78d848d3209c8f49b24dd8a036f7e6f96b1755a8a19802e735a6fcac53c7fb899137b083b22e5edfcf46521b0e6530995ee7ae497010c462ac6d2ebb119a04487be53dba42083180fea1454b32bf2dc165dd439301729c7d0761e49dea2717f64673411f439057300b270c39fb61596b13d0c10ac48a4cbaad4a80c7ce6e4f80ecfb72a8504071bff29f75dba42709c43424583f4f57a6390c7ff28c4dd06b91eda67e497bfe01a8edce4f93f612149436c2368c82bdf9b7426716ac4fa1cc5e176126f9a76516720f119d6637f51bf27e1efe978aee0b513920d151fa2928031df16644c42b622dd6158f8274e2512872bbfd6feec1e0f4215e0995db5c179d4924bd25543a7bf9906fca12504fa3a394d413a56ea2c47a4d492c343f1e5b2f90241e66455d70d44a01b214061f6f862689c75bf0d80b1ed54b150fd5be1ef1757fddffc72716ba1fcce9a4f62872cbda99d3a80cb7f213ca5356ee6f36c016c52259fffd302f28b13cb23173f0aa0030abd1412f6d972ff5590f8d67e45fc7bdbb6ef9850e9b93b40caffdf968b97165dcf725c360df884d2810084e85584c8374c4b5c6f59b75c4362bd01d2ce02d2e8c9d662b640b0c0bf76641ea4d0b637b0a97b2e958f0160bdb7b17062151805156602f7013067b7541e85119467240c9124e239861246fc75108cc77596c5d5e63b143916f5e221175ee1295ceda8ba483f321bc5c275aed647182b655a0311b7fc4da579f939bb29e154340e7468adc46f198c96c64ba9e3914954704dfd6f831cdc13da1f1281a46879db31a3a5a9afcd38ab73d52521ed4a191a7df8d845c3b2b4fbc06585c322c4e4e0a2c48fe3833cdc6818b6f02ea9cadb61619c8525302055b594db7de4db4ded5b1e8442bdbe73a756c65d56d089c9535301867813d5c8efce87f78eb745981415a0963208730d204e2b912d1627c7a48e4163aec994d160e678a7bdaa9ea4e8659b924114cc5d9270d57a6e4ed1f34dc5889846260fb2b16420688ecd0408268014ebe2600eab0f03a790612183687b195f9f6975fec47c3146a7f816d1d1b23dd610a23acbbdf419c90f19c8f7028fb0f3b1c9a64a25cc2a7944d9bfd29f76aa05473cdb075de04f6a1ef2b809d816080d885c6036c25f7d26f9e903e1c18d9a2c690f2648d8bb050da8c4a2e310a61b3e61f9cb08f7fc46528ee248d68174c490fe837129014ca4e0ec3506b9f0efbdc38cd5b39be743d4a8e65d5063c1a880685899f465b4784a730b496863443775f0351153267d71300d5c79013346b800889fba4d8ee1512c6f92b317e50d753d76e4c373c4cc2e10cca8ea7c2c120471e93a32de5842ed50479e062816ac6769f781a2a47fd5d25b9dd7fed580f473052298b8770e50d566d2f9c0c8ff2aa036f7d6a48a1a538a6258e581a5f0e2cbd3fa9a0249c8d6122b60b6aebc811d2797b44cb80ff27825e2120ceb43e045140449c346dceedfd4f5ccfbc488d65ed8e282522b6b2946f00fb59979609db58ea6d03c2569ef1a1340719cf51190368b557ba40fe7e21711349b696b1dc0012241c428b9420b7c8811f71c35648c9da23ae92f952f8226a848ed611deeb90a5e209ec843d614df82dbc0a3fc2acf05fb8164e0837c2c21ba24cad43042d08b6824f43da41fe44ce9a6bd66a111c7ecc8a84100c0990f481cfb0b0754382a2fee9bdae163e7f2c24072fa4ce9450782f33fa58c63eb75bf7e2c420476291243c88091a2908a405e1bb17f54f675d08ba7c96204fb097dc26bae259b41ae333a5ecb4be0415020b24eaa09301498f69898642326b1d2aec9755f26846ee8cd210e5bd491d26f8cd808656e44eca690ae33e9b6882188fb299327dc88753a326de4eec2913dc8e5095d7dcd963872dd4e95cd56468a83bf42586b56803cb0063cef42b41c5c863e691d22102acb329ea2d47f5018fff59caf96ef219e525abd6a29599aec65dc650b04c327c1f454af352beda3a5e22ba32eb0fd7e44d34fd07b5d2b311def030f7caeede5acc3eb5536a2242406faa2ef235099680394da385e27887c3265959f760f80980c611fd3a9566627a32ebbe6a165d2c00d84aba57944f1ea4cef4d592179623fad97d2880e600e635311233b973952902bb62b658a7c5184a9b21b370b8fe60d56c3236b2a00327f4dc556746a4323b0d17559d6fa49878230f58eb4d01f3faf44d67e5009c7f33dc416c018a58c551848797444988cfc275a8bd2da85a363b0a366d9a52086722cfec3b418f8d4702f636938caaa574259fea6148382440c79c528fc09190b0edbb0b9cd02c8dec1fa66797405aba8d05a58328e3ce3b37804b24300a9f3b875a2d8345be2d0c30d9e2e40a55a5e0760de9f02a66299116412797f21adc770ee9c3e5edea62d19306284a0b9fb72c65f5c1d14b54ec9cfc9a5080a5c162be5c52eb15930cb3faae303323fdc3ed1780bab1721fa05c9b72dc1e01a1c55dc7bf16a804b31c75b800dd1c6df8ca914b40022f0b26130c1dd8083c79fc546fd6f8eec49113e4ace29fe1dd1b40a255c412c0986827e8ead4a25d0b36f993f9e187574e9167adc002ccb28fdbf0f1643a3a91621fdfc0d7e7de909600e8d9cea1b92abde5460f34b0d313c219a13d62137338ce96cf2c3dd6c07601270d178d1d95e15d042d8ac35a5f2f0aa294aecf4c2a681fcd1e92da2e6b5081f766f281bfc4d27af3152b82f1b1427a608da3dd2751b35b1c6f9ec44bd57036974ca64ea777ca0e7c14fcdb91942455d7dd46300c973166ce03631fe6bc8cb7b801a0a171ea0c086b474bf6786d7462ce501a4161b1cfec274839c29599c99eb10b2542a10f52fbecb082e9ad4b52657afdd8670043a599568836db4903b1f1b225dac8686c7fcaec5c47dfdbacf94954e506fb6013f8decf2620b111829b3902b0b1b5035b2d77c3d79ec9661378b51b25d8016c670a4cd0e68813d9933f8d024e4d4eec4ea895fdd6966ab3817a726cbaadd321f029015a4f65bf482190de98a213feb9815a65caec141e687189d56712608243c6982c7eb0109894967865003a63b1f83f84f39fb1031a1662b3dcee42e2162940d082a25bd3b3513a31b20c2530e9477a4fe9c056a49f9f5ef36461daaa946f289485470061560fc4b7bc110253332e90621105bca9d8d9b386a7ad1a675b15206daaa00a2887a7558d5e9e3656bbe83bf25e8964558dbdd2344a0d7c7ac3ce03750bd054a46fc29e8bc11d97c13cc71aae8cf9fcd03d8c784aaf62f39f438689035ef0f96ff406b4e4b98473cce6b9579eb16ec327bca6bfbb6499da22364a10312ca2bdc5b08344e0e1838797d282b8043aefdf2611f84e3d717e06bbe1f6a6357fa453852d4a6407f4f52ddd4537c6982a9e2af7c5901d2c43b1092f0b3819ef564f37a6a04a8d4d81a1fa7a346187a5ce904d719ffc6efb74deea9ff6106e4907577022cb1868ee833b23c5cb608cd7b0fd308f207e4b9491538069991c3d1e00861801efa834f6a89de69b647fcb9e648c231ae80cc31147fadb619e1b480dcdcb43b58c1cdd65e0f8a25cb4e25cea6123bd5ca4b60eca7b0a56a92c4bce58463ce48223e21de84747f308097df3686e91605f7971e1e108eb7b974b29e48a93d903d65bfca9557523a3140b35d7e12bb234064d7dd704e9e6df68785545f580356d1cbc54e2e53ada37445e24b173370a360ade6dd16cb994b1cc358c9eae0e1a2b26361b66ca45216dd374f8f9d13f6b8f871bdbb9de2445afb3b04104b479fad1779449a092b9f154d4302cdc0b02ac5f660fde041baa3162988bdbcc002de380e0bedc3323d07050045dd3c6beb68208850196fa08e75719335321bef6d8bd7a22b784f4e1712167cee0e471223c23a5c776ddc9249a5bcf4a71090dd04e188f7949481e21f33ef96cbc8c2b96db23b55f6f6358347108e185d2e4f93f0f1120aaaad6664dc4079e945f02553df450e07a40e5f8ce3ddca2ac00b6fef4e44e6a805d241a167ec797b702a0880220491597fff8dc17b098f00bbd13516c9469b39182610e92e02899852cbff53c19325697d34d573cfc48c76771155b178a3cc8e56de086fdb830923674d9707a3186b617b29e04466c919a8e40dff20c41baf878c79bb1b11c7ad855de0864a0d5b7200fddc769a6364a32d9c922c56242609419430db85a12518c273ae4f42d96cde2c90d7ce90f47b92d44821743f443ebcda91c62048588b59c67db69e7d45355e3727cf0e42ee7a87382c09f94631427b910681c5330aa82c01cdc0767ae4c9cfae5467371bdd3aeb85b2ad2ce4f864a9db95ce6d71ce22d22188cbe5b4132374ff52f40495c1e914d66e03ab65cf49f79dae419102e5c47305c04e98ca4da7c03ad1adc0ef4e3f841068dc861f24aeddb62b85bb83884f1aae70fa0d6100b6a087a25ae0098821562d46db8bac21b4c62a9d093abeaa546543429f02ef404f1a7d32a343cf94f468bc9798a594756c889aaa314beeff83a9a44de5efcf9ec528b738c7ff559be327244aadb8b67bf8868ea7a808a9767929634c99556563cbf7a72813e570ddcca3b8b6ae791702e38d793270b2426dabf358627e4d78f777969adbe8af345a845d211b3a44fb070cd139ca575ddf27f2d25f23b67d48444357ff888f60008015f90336a8d59969c005e0cbff7686203a95207121f3ec5f691dae75e29a5fb83a82d9fce81cc6e1f641d445380deb97657089c741218ddaf56d3b1c91493041bd1aa3386feef390d8f724f0c070da501787f361ea0be9914f7b08de820ce6d645199cd232cfdfd5df43f76d37ded55b4c512dead744f18755f300aa1676cf627a08ebb10e0b5645493d57ff93381ee7efa7eabca6410d49666e884f89ddb65ce67eae91725133a9cba5ccae5f4ff8c3ddd9374e14e9be46e510c54cd9abe842384b8973010117474ea30c673f0e0d6928b5ab803de511a18d851dfe4253c6ba6182d9398c2443fd5d26954fc1c60ef59aac3f7b9ba9bab19796e229b00dd517d61ba51a1ec230af124a97fd046ab2e9282a8d14915145020663b0634bb8f8b29c0a0e86c23145514597781bea48cf03f300daa13dc0c5256806f8beb40b65fa3f8503a59af32ba7fe79939ecbc315fce4f73ae7d539fc51df5c81f208094012c89023e1bc4920a3150b46439fcffffffffffffffc2836884b6196b5f2699a4d43fb65e9f93292599524aea8a507fdb7bb1adce6bb700705002370ab70a2a0b6783f633402006064ec07808a3a48a09795f762b7b3b7018c25829438599f9948102331e8d11b081a310e628ae57faf7251fe28430560a3955f2e8312999580d1c8330b9a85c29994ed23e98046112426bde9d754e218a0261d07d7969a132cd4407104693e521f7d357d7e40fe60ba2e53fb473d4b2f8c1a0f7df345674b793d70763e86041041737efed2c1f0c6bd97abda71bd6e93d184497ae9efc5c8c9f9881430f063993f263296112dd1603471e100d0e3c1872492a49b12d6b2a5d068e3b20871d8c132f3b826ea7856a1403001638ea6078ad98aedfd9a225a7834955a4eb9a373122c77d1b9cbe2570ccc12c794be95eb864acd982430e5d8bfa49b2f3ccf19a0c573150b0813478607860666046197130fe7552e3a1974fdfaf070e389842ee11d52945d7f7eb0e1c6f306e8dbe9896f466a204898101316c6c1b38dc60125f5ab6e106071bcc7d7593ef53487a13101070acc1bc9ae57741d7a5d21b047567dc020e3598d5c45354f6e3ed48c958ab71e36f50461a36520d1b67a41a957fa38c1dbc7f192420f63ab091032788230de61093bb64954b86771a69f49a064ca08292010e3498b387c52461225f83be41195380e30c061dfe9e2fa4b012a5b4c1e9b7810dfa0635920d1db0251b3ac0097098c17055f24d25a70b950f9571018e3218ff928509db5056bb93c13022fe95bca4c492c818cca9bbb2b7544c072c050e3198a3e536e1f14708cb3a0c869c336554cd4704638ca154e00083e16db4d5271d9f274f38be60f894945f9dd6923eb91a26070e2f985267c7e40f2a2718630ce50347170cbbebefa72b574fff655c000b1c5c3057906a7f41733150000213ac81630b465739bf1b09deb5d7c0cf000187160c2178fac64d9e4923e1c8827135ecc297b84ce0c082c12ec7df8591db298538ae604a5a44856eed2748638c31ac803c930bf1c55392048e2a18c73e575e388d3d1351c1246fb174ef6939a6604a634244106627cb13a0148c56aa74a9724f0d237044c16416f1da547fc4fd13148c21737f36436f434d4f3007a1baa7f3236278de09c6ebeba4d7e72618f29d0863ea7cc276ce04d35dfce533a14e657909a67abdaf0f2b17613d4a302515d45e5e11d2c328098657fb71fb2072b86023c1d43eb69eb49e490e3a828d911c46e02882f9b292522907217663250229824ee710cce152cbbe49eef0d86e091c423059ecfb750ed7dd49b5040504c3a419174f9d4812820c0cc35f28ebd2ff22ed41c55cd0fac258226fe4dd30f5c2242b2cee93887861eaf91369e3ad257476615041d72bf2a90b53c8ad203ba48d94839d0bb27857c8d6a6ed1e63d973f4c5195b3109e38db791831a4b06a100bc26cc7b5d8d7c0b835249453ce7d71686fc513c4c8749a6c5ad85717f84cc979b1626d9ff9d90d35c3ca9b33059c8b8f0105516c6f8adc94959c82657120b73b00ea33fe8242c0c3ff163c40e93d542f015a60e5a34b55baebfe40a536e919ebf9f43c9cab5c260c9266dc6dd5cd28a025861d0cabe52d12ec53dab30a8d9114ac7fe51492e551844a6bd455c3f1559292d3fe975428521dfa7a97e855c21994e610a23fb22a27fa776c9146657b3acbb5ca6564ba530a5ad0cd1331e24a94f0a43f851ba4cedcd24ef4761f2944d5d52e5a5f14561cab75a290579288c7212dd84b8bd8bb183c2e435c2528c2c132be29f30bdec7dee2f1d2996ee09d37e6f558ca4d309e3a5668d50677db23c9c30c7a9d23fcf3e56a9b30993d25b39450a61f683ad09739a8a093a6974084225135ad70861c23023f1b2043ba9e1954b98c56554f6c92c215ac268651523e71257c20c5921e694982961708965563a65ad09f224cc7e966c7410af2a2749984f6915356b3d267a244c324ab456f81bbfc842c22897bccff28ac7fff61166d1eea0ac3ea7a4e408e3248addd908437e4f759e57216c4698e4979658f2751127a1bc3c8408524598724a1059eceee74b4d84513f449c7017f4e85111611eb9172e589ce8f9218730cebd5d9e53223f7c8821ea1b359284b4904298e25fd8ab6f5d8e242184d145dac5f828a195e7208c25f47da49935152a8230d998aa88b6a022693110a6549e4ad47f29b32002c21841cc6bffff07c3ae78ab5ebeaf1ff9a130eda7f4b3de0793e88fd65fdac34b870fc6d221f7e2a9f76048fe313c62877f5b4f0fa65879a62f25793089ffb7d3e21659abc38321d7e4ce509d456e7f07838ef2cc0e66754f2289b7eb60f4a03da4b42cdd4ae12328800ea6ec10dffad91ead3207b387d6940fa2a5c4dca2003998fbf24452c943e26048dedba17f3412a4a100381872342de35b6fb9737a83f9822ef5f0e979544e6e30ac6ec9a888ccf5d1b7c1a443bcefbd9899be3e1b0c56c235363cc6f885d660c8b9f9e94ca538b1e36a308d4895eb2fea6930aee47cf1a9ffd3480e1a4c419c9ff9aa256db13e83d9a468c93035aa92d56630ccc752132929cb099532982607bb6b13e299a3420663878430a6372a5a5dc660f4dc29463c93c560f211b978fd881bb286c1385b51422a3d35b925188ea12a1b694f5f3077aa7fb76a7fc88f17cc4976bd85a958296dec82b992fab64acf3f9ec305a35795aa0bf5313ebd05e38d57e70fb95c49755a305e3a799eb3ea3afd62331490057387e4b69792d2e94fc282b1ce3ac9f8f824e2e8af609219aaa532defcceb702e2773c96fe1155c17013f594d4ff3c21712a9884d6c4b4d27fe94b5330e80af627642b05937cedd25e9d4c27d95130bb752cb3ef0a14be74332f215ee509865b8f717146b632632798f774cc12fd9eac83b609667935ed977a32c12cd721844b30b5b2e112cce925d6de3a68195109061d52e6bcd905e1d725c170fed1c727f9aa9690604ad53152f8ed9e9c1752c01130533119c170a942d07d9db37d9e8a6048c27a72ca257be3f40a208241a28ca88a91ec29e7153004a3a96895537f4a59880284604a9e7a3ec5d472134ac13088a50b297e47c0305d4af2c182d07809fe2f0cd739d4e979331dfefac224b353a530b7f6c278313232744f5e9842faecc5d83995f3db85715de5e6f279498c5917866c15c53a56f8503a940b53ef4e8aee2192880be2c214645e97e81423a8945b983dd6fb920ed726c01606196d73f772511e24a016c66eddbae461e634946861f0cefb984bf75039cdc2a04c58ce963c5bca7564611cabfa4c11722c0c631fe729fc75953861619411ca530435aff6ed1506f9f3a554541235b62bcc2dc122a41ccd3071b5c290a3479a5821ebfb618549fddbdd53bb7fd87e1506cdb8930f2a5bfa5d15c64c4d0941881cfa74a93045319d5efc42a830e9b85fa9273f8541f25cc85cd85870dd14a618e1d44ebf530a8350572e661b290cd1f6dd763dcde8708fc2144edc4589708bfdb6284c7a31bba183426190dfa1e6d2749e561914e9c4cbded922fc09935dce8e16b395728a3d614efd6a3bf2e2f5aed2099356779ccbe179d3b39c3049d4ce92266fc298b7d6313ff5cb6435618aa73aaf6d24874f66c294a447b1d4b09c53b5983025b30ed94a5bf457f412c6dc52625da3a3a65ac2e09f442e91c4f38e48254c395da570e96927344209e37dd09fb2e53909e34bfe78a6e22909d32789332af983a57889846946470d4f199e119c10f7d596c118caf4f2ba875fa64a06c3ac794839e1ef434cc760fe9338b23ea204f5a71850296c961695c530983774de5fe5c748f20383d1d2b8cee7ff0be62496e339a8a4eb6e73720ae2b36c85e5d105a3c950932ea9d86c2b3db860b6d149c731d3cdf46f0bd97552ab164cb26eaa652b5ee66264aca14fc306b2db814716d0f1f3224c23076714d3838207164c65256e94e7b42d3cae602e65639ea2468ab896580d53567858c194f74a4df8ab2c964c457854c1f8d1ffe2ab04ddce122a98cbd2c80efba78318a76010b2ffca76c7837b09111e52307e89119b76f9fcab838228182325fd9b955216f0808239c78b9e9f72f48f952798e5ddb479d0179961999d8387130c3329cae51cd63bc96f341924f06842422fcf621c030f26982485cacf7b48a3a3843c96607a5753b31234069f87124c51547c3cddbc637cc85813030362dce06d604c108210f095f64882073c9060be8e707b3a3df668c97dc1e308c6fc7cdb17ebb81c3f848711ccd142463df582ae24d2078f2218dcf334846bac6f8ef2e0410453e7aa4a975b1a824988bb4e6ff11082e1a4ee894a21f4c54e308c7dbb9643b9db48f081618a37b12e59857e61cc53162d5b12b71c725f186b3ed552c3fc92e5bc0614111dbd30ca8b0e49bd041d7288f0c2fcb7b26f21780a964cbb30d90911b4a4a02da8ef7591e75cd92b9872a1e5647fb612c7854105b311b9c27912b2dfa21c31f2a70eb3a0c316e6be9c5417fc24574a1203033d18630c5344472dcc9d35dbce9456684f6961d02968a8b78549299a85c15625260b53ce49a7d873652c4c3ae5ca93ccbaed14c2c2e069da74ce64e5f4b98e5718bbdacf7792fe77571dae30699af4371d2a3ce7dad10a434cffb8dc69647f7d32f6d3384304638cd1c10ad3f864356bdb5c0997d3b10aa3e5fba0eea49e19e85085e174e8f939c041a331023c0f74a4c2e4329f7b2c8ad0b1827fe84085a923567708ba7c63c5e544c7290c9ee6f24254ccc841aa91c68d33c8404d7498c29436fa417c45aacf3db071c6096eec253a4a614ec173e62d78e578925d1f3a4861eebf35f959234ffe2cd101d1c1758cc2a4c39d48e695547aa92c0af328e195b2495d0c1da130689c27bbcff2f711ae403b40610a955eecf3488aa1b78e4f1824071d545937de0639b041b6c3132695b835264f85d1a771e34590fa848e4e985e23453f51fe196798e0d4071d9c305dcbaec7db0f196bc4522a2303d9b10983f6a94b8a569d674732d68e8d336e9cd6343e043b681390232de8d08439f27ff220642713863989f712a29fd26562c2f4c94eb2dae9507fed1206dd79424a8f9c3aaab584aa23e62b4db850072a61d2da2ed692844cd94dad83128618579683b02c49e19540c7244c62419c4823e37fca27634d67604619356ef40cce6a7448c224af575db2bb3fde8f84e1841ed562112b749f206188233ebea894479867c4430425b28e30c6964e12b2cc8d3027dd2108310bf3fa3123cc5d2b41ae04a1e5aa2dc2a4de2ddc27e6841cd48a30b6c77f92e93a237212619ee41255b54584b94aa5646f7934d0061d87304dfe9027e6892779ad210cca2f0511b9301b3b29842959ff7e8ed157f33c210c9ee3e792131984399a668c87b06d7d16411864e4a598b33810a653eff944673e7c5e0161bc4e3aac94bef422437f30be75886a79b0cde81bd4b071460d2074f8c13cd79ff695ef1d731f4c3a7ae3734c5d7c9b3af8601eb153224da7875adfd0b10793698951e93f7e435ec618a30c1d7a30bb8952ba9245ca65e7c110d79385c8e94e2733e1c1bcf93d16376542cc10093aee602eab8a15254f7630e959f96c48899192b6a30e86a0b23fc71ef5cb693a982559306de95547a890be3107d365f5f021e7b782fa7230460e21fff563bca6280ea6b9601eb7930eda3c380231306028820e3898fbb44d77fa88625f7983b9e3267c7b753adc604aeaed6404a196efe28e3618741cf16f3a5eacc975b0c15c322a5b6a0f95f8411d6b305d57c8cbc147fb5dd5a106534e1777429851c971471acc29b376245af09c67d4810693b89265f1e2d67106731e0f3ab23aad7b3a7598a1a30ce6da8e961ef9e62fcf64302755af9274cb8ba5948e319894a5d457d9573d9a5087184ca6e25e59f2bebeb07784c17cc97b84ea297580c1dc9e6309d9d1449e24ecf8823154dab0681ee75e6387170c42f99ce890e30a7474c12caa62c988ab7570c16ce15497752a756cc124776fbc453fc2850e2d18af5ef74d05fb3897ecc882d9c4086175fa43b0bc93b156b2091d583078593ce541e92041d664dc681bdc48cb29745cc120b664bd8fb0a07492c6df60247458c1249e9752be824856e111dbbe362263ed460dfc8c8b4207154c4ae7f34a7491ef9b2103c10180091d5330c9deee284b21edd26a850e2998d2547ab31ca794fe3f0a86ddf2c841c72c31f291a1030a26a12a28efef70161d4f30d6571e7d227a6adc103a9c60f6d4a347848409b9e464ac1513424713ccee275e4b54aa850992b176b68b0e26984daaff4eb0cba3bb97e139406f63cd3a96607cf5908210da11a14309e692ffd68cdd8f5a0d6446d748c3df868d331c2f0523400108c6188334a22309df46d01e420551714400f0840e249842fc3ec9ed18255f54888e2398c37b12570949449f91fdd0610493d23616ef2f8fd277923d7414c194ac2e2bfdd9b67ae8120f1d44308b4851b27c5e8e4a7a1d3a8660f8d7c8edd194086b4f87108c592167f4ffb94bce81812318c6eb30c2e2538255489bc0010c773e8ee46f512612387e61b60a539dd86eca26cbe10b47cb9caab16ad4c0cb40a30c397a61baadf06217c6a485928c355e9894f6961c612732d65e0767d84083ecc2fc966cb764f9cea582347270460f1a0417c8c097610305638c91460e6e705217e6fd78b23d89989eefb3326ca060066694a183336c90a0ae56c0910bb3e849cbeef87061f25c953c67d3e9d3ad5b98635bf0cbbfda41cdcd610ba3951ed39da487ad7a6a610acbf38e60e1bb744c0bf3b55e165521b48fa924143866610ec26b525239fd39aae39085d9de544bf4b2da4a411cb130e8b855faf6179f4212072ccc398654911b59626285e315a612ad4ed656b6256b0e5798f4a7f411b4b3f5c511472b0c2ff973be37915cf671b0c26c2f42eca8a45444876315e66d13fa626dd35694f4c68d37430f8d326c906abc0eca4011b8a0060018c1a10ad3b6c9a7f6df6eddd2e04885f127d68358c5b67d97a83064b94f9f2646d0959392e31486590f49e280c314e6f46b4a59104ff1692e85397ad22108f70bf9107c3446200606cc18811818e8c018637090c2f8f964a8a8898ec214d4d57a5f885887958619efa2c83cd6fe509834b6d4723c670f7080c2587ad243d076291f1232d6d0b6031c9f307b780c612d2f7a47ed9e30a5bb1c1fbd7119a70c393a618e3197191ec2bde3be64a0208b15383861ba60e931c7b38854934d18437877140fe1ff276263e0d084b9e4e64f503365c2144786b22bd3bfc08109c32753b1d2449aabc0710963aa9ff097fc1de32e352c6132a1449f0877f996304725cc5f9574ce7f2ea38451ee2eb6fb6dd44426b1fa9998ee08faf693b1468ac02109e388ddd115f73f1a9130868cb8723a1f27fdcd0109a3e9f0ccfa0d353a35ce40c6f10843d2ce12ea16ff53ce7a60e3735002fc0087238cf1d9b2875ce2137034c27c65daa5df7eeafcba4119c8c1089350a224690ba14347ed171c8b30c8336d51fab4894990220c23f52585ca3825a3cd011ac57024c274255b95c389c958eb341a751a9d81b3438439c4d39c471fc958db47b7349694868d336ed80dc26883594250dbe1c43ae80f1b4c6147aa4593abf1c1b20653f64b951f5613c7623518bee3dce28abc9d531a4c924288cad0a2c198163d480b5b9fc12022e6b283f97d4a9ac110abd2fd7bd3329854dda4d86d12321852f27ca3151e4f98c6600e537fcdcf5a621783b9de47fe23c9ffcd1806f37cfe9f705142550583d1de4b46f768651208e30ba6204743ab68ef05c3c758b270739bbf6417b0ce26d404317a4789b96012c9ea2b56ae101e4f1b67e409630be6533284058fe4215ed1824988faf82d162b744a66c11027cb4e106e95ddf5c0461a663c89411858305e5b95eafa3afbabac208c2b983a3c5e88e65ba1d5b182f1f397d896ac963ba5aa60aa160b299274cba57c2a1854851439cfc147dd3305d3dd7b1493659182f9530a2e63428d82e9f4d9aa7cdfc51625148cb17f925372fb1df5134cbdd96155c24b92f085e104539ae7f5b0939d208c2618648616e126526ecb1e0cc26002f6de273bff787f7210c612b09453c4f7ca49fd26c377108612cc7d236e7e157ebc2c1784910493fca9d3eb9d173f289d9186191d20411848205dd6da35753a0c8871c836d240037b10c611380c23a8f9e93a240f855104d34e6e65ec056da4814618443098dac9a93a23faff52e36d80c60dca40038d36e30c33126da481068631041286104c31a2f5f3920e1f3a45c69a06c6182318a6687d6e23929c6c42fc018c740827dc7d2f4670f0f10b435061ed93529523a4f6c317c6f413c9d94ea6ff5b7a61ceb7f0218f5e0f0b3f2fcc25ce639998eff0b6efc25421897cdd7186c3872e4cb32729ffc80e2f415d7261f6eb38aa53e592f4d4bee10317862826afec736b753bb730974549410715a4a6986c61aad634fdf079265abcd4c2ec914ad6a79fe546190b820f5a9874d8b8dc7df92c0c2a7fbf1fe7e9e2c8edf0210bf37cd995ca444b2cccb61e210513172c4c25b994b6607dbcc2b415f9b5a4e7ae305ef0fce941eb34444a2b8c559b16174197a710b3c21455a26bcf9fac45c9c72a8cb2f19fb2a8bea99c25636d35f0a10a735f9a9790162d7a0eb1c0472a0ceed9f49da77b8d7f64a4518619638c199851061d3e5061a7305d4e397448994fe2ae0f5318258e38f9a92b87b7df818f52900f529852860a9d6db370d9761406d7d1511db449a9f0210a539e3d95f2b89e1825e64728cc215c298b1eb2a030b75558fb089553840a0d1f9f30ad87917849a924af2f9e309b6993eda4e2de32a265f8e884794543355efb635cee8313660f3e3a8470f98f4d98ba93c8ac905532d66a70a3d3a8516ae34313261d43cbeba578e1b3f6e02313e63d19c1d2363624e8c607264cba7d9dc289741f3e87c6c7254c29e5cb49930ddecde81b669c618605c618037530c618a800c0c587250cf249a9eb590f495755ad84c96425bba4bf38418614111f9430ede8a5511d2d859c3a0e1f933024cf8ff6f24149bc21b14f99928cad8183bf71fc1109d3c70f22debb95c6dfb861010c70400c0c8821821168a007e4111f903048cf8e2621761e611295b91a164e7bb54a8b0f4718ed3bd88e078f7a154d679c6183046cbbf868842145f21073f11da45a186110f727721e7b89f7e5224c4129ad7f31354518feab5a23774c909927c2a4cbe646cfafb347bef081088367f7202c99de05391dc2982ad142fe4ba4da785cf830c47d14c2a4a56321f444de9e9d091f84307fae3c158bba222ae4c2f0310853b84bb5753da7eaf282308a7653e13c53d72a1bc147204c2d9e1fc94b8aecf00c3e00610a6d12dc428c94feb7910334ce061f7f30ab8974fa6247c6a9948cb51ef0a08c5484061f7e3045494269fe763efa60922c626fe9bc2284341fccaaa6246509eb88f51e0c1f64549c9fb01ecc67b222f2e7c98339c99e252d953db6bc2a7ce0c174f59323c7506175d28f3b98b3a5eb10dba10f3b1896e245855d4cc6da1563c1471d8c15cf4c7c96243a986c52994d1a1532d66e2033fa0c4f061f73302595836f9c0e231ac21ea45106213ee4600eae5931b392ce103a7571c074459cffb87e95ac81c68d3a7cc0e1f5eb9873667d51926f3042ce7c3593967a4fe30469860f376479e27ba54c64d369433733f14f48bc78c992870f3620ff392c8d1cf93da99e1a3ed690b2a85a97730a4a203ed4905645f0f865aa19c23490beb6f78325870f34a0426cbce362e9ec1952a1ef74e912bd916f0d1f6628ccb9fccb778847e7f0518666b7829ad193c2a8c4c08018221003052010030377f82043aa6a219e923de1630ce82efdb394b23664031b29e0c387183c73dd0b27bb33d20b43aa2f1792b42fbe3330986639463b2baa46cc8ad9ed21044be1e1e30b065162559e23e9875fcaf80037e1c30b0695b3e4eaca5df615ea8239a8357517a16142ee5c307ae9f270a65aa9287c6cc19cba2d651bedf92ab5160c9264f68d05350be63c31dfca7a7bed2c5830298df89aee6123c6750573be8e2ea2a276df3d2b186c4bad2aac5527cf84955f86f041059350495dcea65bef43ff988229e74a9db746968241626b8a897a11d1520b10a2828f28782962425a8da806cc28a36d6440a16008ba744cbace4eec0b0a1f4f309dae382295b897f0e104939abd0c611e84c930d9f86882c707136ef4e06b50238c8f2530e0430908f84842033e9090808f23dce00602f830c28d1b6f038e8f2214e08308370881013e86501f42b0810d1a0310f00886033c8051a30c342c40820f8f5ff8e2d1195e2307bd3880072f6c6083f60178ec82c3431765a06181303c72413c702186c72d08e0618b5a24c08316350ce0318b1a65a061811b3638e30c122cc043160ef088057bc0a2461a08f078c58d2fa30c342cb0810e0f57d428030d0bd8f81ad83863056178b402fd0c66d0288880072b6eace2c3431509f048450d0578a062001ea73080872908e0518a007890c21c2ce6e7be2c29d6ef0a048f51182f646ea5e4c87fe692b1766383e0210ac34910d533b146e68743612efd1a4943e5010a939795bea825f4f8842188fd91952304356a2a62a06003c80c0b8ca145f0f08441447a4bdd0a69545f88e0d10973ed6b856fcf17365f74c605dc8c1c68608c1f7870c29055b4e38ea4cf61271e9b30d7759ce84e5ae5c08d31c6f0d084a95b2e4fe4f4f1eb7c076760608c3170e0910983b25d55cb63c00313864b52dc7c3b889f1cbe81c7250c49dec48a914535f0b08431aec4884e1da37b66b5107854c298a5433db59f2861aa39cfa5b233099387081e1f4aa83cc2968479c4c4c68275f49461240ce7f9644e529ed35307097358f12e91bbec3995c7234c62f27eec43ac7a3db081027f1d98230cf9449a96f9eed108c35c3c7d532fad6e71469894322daa1ed4a5057711a6bb0fbeef29238ff03c1461b2e8e2315f65e75d7924c2b4399ea59727951ccd0311e6e46e2a4832a14584ef3d018f439836e5c3488d943394da109efaa8daee17c274263f8f28bbbd9315214c725ff9fa3482463f195c1e83f0f36c49ed3c046188752a0e84498a560c08737b96f18963c2e61a68dcfdc1a43b82be9b14fea94a7e30e8181f6fd4c99cfbc4a30f06655afcd26ae8755ba306fd364eda7af0c17cbd22136b2f1278ec616d78e881a453120b0f313e66f8028f3cb89664a747ad8ebefd0a3cf0606338c2e30ee6203ee94f6ea2ab82871d4c7bb22fb6f809cdcfa981460e6ad4e08c336cb0251003036280400c1480a0e8c0a30ee630d76e6b13a2e731d1c17c67d7af1e3c8f39182f088ba24c720a49a1871c4cf2f9ab3a2996a59c338f38a4071ccc39b669b57791c71bcc7daf6e2a2fe8f627b3031e6e387d8affb63b95b6e191a4d54b2663cd6dd4e881196d34f06003713f69d3b6ce93dca0460fcce81a37ba3230c618376cd448e346bb193b3803a1c71a4c6fb9748732a9c1aca743ce97143cbae9a4c13c729d2a4c84892829683097aa8f92e44b1471ee194caab2ddd26a78bfb966305f4dae907a30a9bcf72123e6e1bab0248456c933e1c19c435b78cf967758558488d2d9c15cb2ab73964f12ecc63a98828758212b6685db74308baabf8e16f2dce53998e279793cc92feeaf1cb8582faa447fe26090951622fad4a8b41d1c4c4905d7fbfe10cd4c6f400303dc601cbd922d0989b5c1f4e5397c2fc60643983753d3f521a4656b30ae88ce3ad594d3613598dbd4bec47f7acf621a18800693d07be94756673044cd0d8b55fb494290016678bf52366de16bff736528cf579678f82af7c9603c393975f544977cfa184c97ef3987cac8adff88c1c9f1b63dd49f9b1e065318e97a39444f0c910306639d99327f516ae4ec2fa47223e547574b222fbca572178c3e422871cb664a81015c684f655d4656ce16ecf3a0f447bab72d5d0ba5f39c6654d6d6fecdc2215bd6c8cd8b61420103b080a7d71c193978fa5ca1ec17747c39252b14d143845827a2df2a5c1e7794120b217fa860921842bd6be9cebdff144e7edfdad1df3a2978c1647b4a416707191085435ab38a58595a3b28607f3259d4e49ff8099f095f1d8fd59d7342365676a31d235e450c6802327475989409e414ecd4bcbdc29b4b40bde3ed56deecaa90014af0d2884a27843a71332009a59633292a0c4002f2e3c595fa08c6d1972c45eca4b4b2114cd1f524c50929025a548a949245b1141b110c42c9f814bd7df259870143b83d82bccecb16b34d0c10824107d321b32b899fddc140e959f809d3253960984388da294bf40b436eed911dd717e6f41c948efc25556a2f8c1bdaa176262fcc1f734df4574ef1bbdd85af615226e88aab31e922bdf1a32d5a59bab97023e9911b5e41f85db8f092ef9f0a8d6fe155ce3cb3493b3a5b90e7f5926ab530d8a828c172e4f7f8d0223b1d2a84d12c0c415285121e4c59a43a480b1553fa7b2c529193fe38963b395868a63eec45ae3a855f61d2397c8ed45859dd15caa46829e47a044b2bf8be5049c9911c2b5afb4e6a3f8818afc289a9754a45d57454e1f75bfee55829ea5498c2e6a4921d0b3d294385c14743a7687f293f7d0a934a21757eb1cb7e2f0a0b30853959c69c8dd2fb29924a610e93e396fd8c07ad3f298c7edf4994b83841c43f0ab3950896c34b4dc4f84551bec88c89cb6b288c236eb3a22561541228cc412b451555a14f1892cab7b9e81f12f5e3098350d9bbde743e7afa74c214cf6d5bf2af085d36278ea434bfbf4cc90d954d18cd752d9c12b226cca73b2cb4d794fe8b33618871134e998e1f36c5c47a1d2c7c76a77bb92f61d44d3d6521a7a4476a0bb0844949cb4105f949859754c2146a65f2c84b29615e8b2264a77021494a27815097f36479fa699230473bcf22e6bafd4a9130e48e94cc2f04a57d26244cd5e7c1eb7de7477f8471c259289323c62b4447742179b776c5d04a23383bcb95ad14762246182ea2c7bcd32dc29c6d5bde4f8a307f9555b8f6dd5b3711a63f53df9e3e879e1043c42bf1b4f34e0e6a3b4431a9bb5f24ba7c0c61f48a704a843f1d6654089334bb4f2a2949f22d5563c1b0004218c2e85c219b31263b6510c64ba373d2058f2823fdc2020491cea8c9d9de06820f12a2e7ea7ad30362dbeb2c1ee1dcc72e7f5075745ec9048ffac12054bc4f1f9e758f9b741525870f86a0e452b22e8fd41dc28505ec61ad2d9110947a3858d03e696363ed3c984f279d922aad9d85090fe80c2dfe3d498864f13b20d2da32353b989386fa89abd317aeaf83a9374f74a8d27fca79d405cdc188223b25650d9197433237794b88fba97148a86ca9f0fb210d07934edb177c674d5aed6f604288a6748aee92f2c80da653428a9c644adb5cde0663a968699122365c2aaa04b9db1accf9469efad8cb97ac06c39f48a583103969b83a42b85cf30a42868682cf758844abaad319ca67deb1b47274a66d06a3e5b0f859d75b6997c154414b87d2ebc89029b12be93194b52fa6a9899dcec2627075c6b32aa4340a83414e6acba9d34c4f70c1706c91ffa6fe73aaacbef004b112c7d2f45e30e55877e99d4a871d912e6031c4ca88a813ff5f2e9c75733f29fdf3d942e761f611625eda73b5902e2fbfb2c05fee4fdee69d16164c61299994246bd7737905254553a3c368d10d1d2b982d455d2e76a4492b56613797943ae8a95088262c23a94e5f9a42e2af33e3ea2c2d05d3e94b252feb8b422259758ab8bd408124bb4edba40fea53fe09a674314b926c277095527487f0f14b13cc2656742cd6ae5a9609860b1aaa4f65848fb3b6b0802598c3cb271de4eb93ec4a30bee59b28426c88d1a62474399ea1e90290505297bc69c9439ab5802318f3928909cfed93928c6038b3f8cff98b6048dda5eb927b2298820a226b5c12b1da1e8249567cc9e593ead287162004931e35af95b907031193456e74c0309d9874e661ffc220bc4e47febca4ea7c61b6cb23ed428d900b61f4c29cce4a5744cdca5e7861ee8bd61d17f35df8f15794c812d68549a84baa77252717a60b49b2246dc28529277b95ba5ecea2ea5b9892127fded52962a98a2dccab7a9a922b5c0bb34dec54c14dbc63480b438e53114b446761d2799de593c3b996b228ba08eb148e852a13643c448717c1c2bc13b322aec809dbf815a60f4946da89743922ea0a5365bb9c9cf89c2bc55a61aa5211b34e1d46c594156978bc8c972454d7ab30e54ed172c98dc892a20a9398e0ed274723473415c6f6cad3a67f54186b52fa58e74e61b214bd6db9f4fd2a628af7de53bcc25218e5ef629e5d69e47c92c2789f5ac9e36514e6ec14bd7f3d8ac294963f8c94a82a59140a7d4ba48fe381c268ee6945825be8b57c828b5f732967957cf27ae21e2d173d24ffaaf0e984499f595b291155173d4e1884458be623b409936e957a50353bdfad268c575244c676eadc936782ff2a516d499830a9bb49f95a5498fc12bf889b0ec94410a9f49628ae47bddd5abba412c6f932955a8424fdcc28717cd3218e9787f52661d05177bbe32e6f7e4920ae3e4685302281c93ecb275de6d49030fba85239151fdd6e91b1f60843bec8a62e7e658c46df00c370049eea6147dcad2935824932e691bdab2a8597a0a1e24f55901f7ee5a61d86c10893a7702742bc24f36a2fc2e83977989a7db310175b43188a30277d229e68b1a04350224cc9f3bc696d57080311064f41a9e77ab50baf1cc2f4621e57c3574318249d909594c8dd33b24218b3439dc97a09cbde12c2242c5bbb6ea73cb9a04198b2754f6586ba76d205611256a35f94481372cc0361943c4997d5a5957705100639a264d49af40fc6d1b553f14ca77799f8c1f8b9f6e1aa651f4c91e25f25f93a1f4c49a848496c3c5c2e59187b305ce94e6a4bdc30f470505be16ae4e9f82935100d61e4c1702ef36f957e9382e4cbb081827c4018783068939fcc4324a5aa5abf096e9c51469211c61d0cb1f77cf34cbb8248dbc1a0643c3b9c97e585b80e26ebed1cba5e9e1cdc58d4811f844107931239e9fc217ba553a13998fb23cb049192eef33a0c399874df53437785187799a9851107a3e6884923adee924e8283713635df82ee1094fc6f3024cb101e1e73371843a7f0ba038d5ea8f4af6652612012070402711c045248f51e00c3130010404078441a8bc462d13c0e85e1071480035228245836301a241e18161808842351201404844382502010088541814028240a27e196ec03ab41f74a5cbda0cf38737cb323ed8a86cfd25657810116da0600aa70ce296e6ae67566a0e70b7654604720f737c5c02e0d64724730741c6dd034c8fc50bdd6880191268b4923507cf74d766582231df89f0599770a1a4c373db28b2b0e747984ad00cdafcf08c0997b5e6e6eea267b805f181beeb6aac9539bb444f24f37fc683c9ca949faf447fea535fe479cd290c5f455fae73b6e1d5cb06d98220fc824eb376fd0c1de1d4480b743ac6e74a47b0f15e183e4091e968a808de6f2541c8e584911c341ca0bb86b6c938d8b7c3c2b5b544b4461523983f31de5922592fc69c79a4524c8671d92de4c7e09eead39f5d4bfe1a94df4278e6c79a33ca999541f6dbce7ee7fb86a418d54ffd00120f8140dbc9d22f28d7ebb9f1dee3f4a15f72cbe0681c61d3aeb4ed422a08aff1d39bc1c530481076fad49d9cbd92fe58118611efcd5e88ef6c36e3b08c946c14b908e148a6dc8692dcdeb6d7b5af9ff140f940d20c69f84ff2d9748fc199f7c0bb77bbf8d53bdbeaf305628eb7dd3512db1809933c2bbff7a5fda6e21c36a472e0fb4d899ab41175cdde6bf844c934671899fafe9b6045225aa6d60af4015ccc7662a87ee9f1d9487cfe553ed082d1c63fd550089f109f0a6476ec7a7ad90111def3be9d273a26aa725beb92225aeee3fdc6b4808250dcc5ef550e2b79094d8a6a18a5a039dc6d8357c3774190f03370365a7a63c1c251171ae9748682c174f23718da31b7e8203a5cb808e5808638d4d6dcc72b807b6a18fc51b26e5a233c7511c35f57d8d463b36ca40ebd4c484661f9c80837d1b25c6513ac446e73ed07f0e9d13fd339a4114886b7d702ba65328781a479cb5bf8ce1ec300d47a91f363af189a59d4e56ea9f7b9ab99c439bd905f5c0513afead53f1b3046c811da319a4ac96a56eb9ed2007c14672098d6710e0e0debf6e209b3fad0c9146ff7e6189d501df8bdd8147ca7b8f21e8e05ea2633757f4e6f3966859bab60e8bb5c1be05cd7c762000b61b38b6124703725a8808cc2d040efdcba015185262701af9e67ad7d2d63104284feb64d6eb5f0993002109aaaac1112dd71c4e6e0b4bb5e4d97cd9d90ceb3f0e98260ce2bae69033045f53409dfbba11a06654e95522090e53da0559ce056640975de35c9600f50932ddd94450109a84a94701587bffa21592dfb9e65012a0fa4773ae11140ebaafebe8fd28132685d21eca13d6a10f9ae18ec94421dc56084c35c66e1d1eca59547ad0fc0ec170bca1abe70d078bd5c17ed7772e7effbd63e4f37c520dc8aa5b2a5dd5887f3a93080e6504827ece1014f80622f0586fd0d16ab76c4055f7ffbba49c96edef3a47c98e35851308cc6ac01b602cc3fa2f23a0a7e6a87f02ed28826339280daa37f1d8ef6e28012ac3b7992e2db11c3c4767b0fcd54f9a2def88673a7a85ad3d44afb14ac087e5771d3c87903115f818a8250e062f6d1ad0383886173cf08a56cfa2c142da31174c58dd48e3914fd2ed80b49b531898070f7670907e822dc10d19421bb4d67440104da63b2a891a3ca4d51acf651f3c71e361870a357836b71da9ee74a069a5b6fe107877373c9b81ac41405283634e106755661a13990f1ee43a58befc278558c50436c5fdb45d569720729a7cd112c1206b628641d3171a19641d5baabba99dff00902c5ce031d27cee7732841421211974f0002e3e1cb08387e50c1a123dd7dc2192f560b40032b1c1f30413780ae0e0715db9d5c76af008ef83c6ed0f548e6a0d9ee7a387811d6507cb7d0d40fdab60c7a3f11fc20d9ec428864318099b40e6b307b4583b067722f1f9925eb156d5cb4058ea76f8be871624460c1a0669aa32683c0155cc4212c68ee001f6e97a29fa702c27bb2822a855719d837524ec18bb7678848d649a3dfd5dbbb5ff0073e0de55a38c9edb7b04a6dd12d71b34ae2dde6aac54b5f57a3d1efb775e284666143f458a510dce73dfbbec702304ab4c344334181886046dc4a1c66408a01c8b2d70ae21424e16835246a5346d0dae1894169478c33b565351cc4ca7e176a8e929d2614cc0e98350313f9e1584987a8375820e80054cd8f0c08b0e06505803113633f98021e3e088628c88c30ad93daa20163c484fc40afd50a4bb448f3255450e5a401a0ccca1b83c4e32bd7500e94f16a58d3b472e6c8135694c26de69913c7710d4a8ca034d91f673434fd44fb3e5691d2add2003467379045a5d75f175d3db4231ae012afab4ff57c033658c6d6d4f6eb167658f7e374eeff55ed2110f629d4a028503cb647c662edf598abc8f49fe9b384c8d9fd8c05cd5af0d549d3c550297945fd5da01e83faf6967c602eef7ba0baa36279709254d0da59235a64a31b69feddba8ab7b6a2354f20fc11ebab08f3e441faf066b10ad9b25ac15ca5516837a0861300f507c4e244389afa32cdffd7e37eb3294eedd42b1644ccbc68a20f37635b74cb661cf4e9445865d99cbe342c009b91a06000b63ff24116ce487b11a992c81c9b9db81761434f949930bdaba7bbd6c038d55283df3271dd4e7520c73008b4737df2c31aa1888f3b0e1878b1cf0b921327884844bee5a714bccac1ea25bd7f0f11e1b6e82f1d96dbe6a0d516fe8d1912c35f05a0e75b787ed813de8600d653d9c6b17aef7d2fbf5809de265ff366719869ee80311899639a26019e509f8151c866ae27e19840b2463f1408e11784a253050c4c141771070e9670e5cadc8b03d1c743106f610bc51c4fb0a2262485030b4fe61fa81a9fcdffce8654d55a8bf96213db4db24e8f92428c50414d5f7ea1db0927440a7dc31df51da2554386a7a54a906b88bc36eae965fdd8d8657d007f771fb405e35798557e9a6009aef56e2fa71caa281d2d71190a7f09990632fd0a90bd39bf0d50cf8b1a02981f8f9c18c838a061083a9012486c106702d84a56ae81d3e989721f1561cb282b4b0546640a1ea5e6d8e9deb6e2e06331afcc76b697620892e0bbe5e4b2457166de73ab573f8611a1f80b5d6c422c073900b3116aad24de0f3a0acbf719c3ea3988b7180b8014c160535c3871bc6433615e2ece25ae9c9197d090c624b1fd1c0c0dc0a8286d070801706b12c6e587ff95a8150da4896b50d079916720ae98d2bef454ed81c969944bc4257c8bc372dd3d215fca33f816fbabd579cd2d0d56a60667ef624413aa08a31415638ea0526713b34d30661cb537620f9b9eca9e0ca09059b34e11691a16421408d55bfd206f0b868ebbe6e9524a87530e7eac8c8f0a3b3d3c0bb8ec320c000eb4b950a0f5a89df0e3def45b0507e33fc65716c7f25654279a5fc2bbb791669c662cd9ee4aa39ce62389a39c175a76af872761a0d7d6ba436037aa1d7973c62eda5b1aba73eda03bc29ad35bc4e73cd0d34fcc2344ae4f6ea2aa4ceae9b1271ca760f1dbc0a03bcb7fcf552b02ae762167f9c545a6a41dc9e7235f35e42152451c14d5d2e2473e2bb1a7663b038cf79f831fbf1a41399f39950df4cfe176f8f416ac5a09f7c098a38c98b3c927aa2fbb697f0b31de0df2935f1839ad316e184e6e2be53b26e8ecdd88875969f73193c263885ec0f809f6c4fb0f29ccddc163e0a369d4e211774edd66eca0ba7a4c3ed95a8ae40cb70c1c693aca14c43ed386b24c54a524893515afc1fe2048b2c806b04fe8bb54d320e34542fd88ba4066eb37e8d2dc3d1c5c0eb4aac5923d9a76f3f610fbace67f550b5203e31a6e93cf4aa2ceb2eb429338628bb8c8d74bde9079364008916d853126280f980d9dd74e40d3449a65c58bb6e46b691fb52791582cfc56098ab40016e046131a182c29043533120706c40423b3d7a72b4dd417623d49548ae078f503e39425e044db78c0b65a8d5c12b5968e9c4897ce33b75f273e1bcb07ba86b3680161be25abeb05cb2cc30ea5db9a3ae57f41bbe72e94f6d8cc3bab49f93700c6dee1444990c542a8153af88f10494ee60e068957e8b5f945f483b67ebadbff389931e049939a0f6dca66cdbcc92fc7af95ad9d400f09e415a5ddfaa0ab5b495c9848644c8e68e964b84bcd453f28e142b58ac7795d12f3f0f47f20d966f0252cbe851e5e4eb152a546cb880600923287414314b9bc651bf76675cb6ad8129fb192e57bd2a386935e1352ee250d3c45bc5b02d53f201f0dd3199001ac0c98be3486af99261294008cbf83dfcf5f828e8867b750a5a3f6199b715c2a3b2e9fc78cf446b11896e53a15d74145014079aef2c1db5058166d1039dae12685e5f4e9caa0f973d08ff0af43e3310cc9a5e573052ff7610ee045b505e49106ee9dba0a4070bb03f007f417c967e3c455356bbfadf7ec49e47a40c2852004a5ddb1ca97bdc04e43cf650f8ea68bacfd829b8c90f0c9b24186e76fae090eb90a0007ce265734afa1281e43bc97fd59f05333e36138d5b1e2f802f83d18091015291935b2a23e02553970a05e55fa032c6cd4580f27fbe48db17a7788e5ee6f4074a331de655c63453a71d691f68a42db119e9c977de2b076dea2d63df8373c0090794bc017fd689b3bd7bb3205c9c8b9b60ae44a9c961ecb7a156be601ea334a873a870dd16fde4c2239bb8ee4788ca3b839c18cefa2c59b21f019f0b37e30471098be5daf339f81e07688ebea1e0a691e6851d422d3b74aac8d33de2ec6894006daddeb1b0448d6165971ee04a4bbbb7465093ab6950bcf93a930e7c00f8356e9d1bc67da851e07b79e39a579b39caddf4bda6333dd9c46c2a72cb2b034b261e209c301231cdf434b7e828f2c7f4464fd6cde242445cfc43c77ffb02800603bbe3c6fa133f04203b8a7d5bd1cec0efee9dd082435a3fc13bda89336040fa736b11e51fe7aa5ccfc20a6507c42bdf69fab2221e1e382593c02adcbf45b5add7a506c92e9ac02f51e620d37209ad315b59ef012ad22d3b3bb85ae67a4ed1f1a4a12c901c2549273db085365ce1f769958c7b0f1bc64a66dbe8318deabdb8986dda60f4b6e648a3dbc21263225b8b325402b39f406b7d23937360fa309a5bb68c60d1beeb67800a3100047ab552c63c5f777bcb8160ed2d0bcf74bd604723d4fa42f45b13874e9e95186c43835b3b80b5b1028acec69d0f71325de08d5327e9d8e0009896f340190fc7232b2991e1bfd1ff4133eed2cb74ee46c98f7b2a170847355a911f9cdd4c6c13ce38cddf9f9da12caac149f750c72238ede31641f0183f2e2bdc266a4f1823b01a888fa2f31316897220b27bc7461daa80cf6fb489f53d9f93ff6482e016258325523d4ea4d421566ed4384a3db5f5d987aa9401250734f5b7352f0d87befc1dbe41c92a4101e73a0b485923ccf99520071d634a14a8ae50d44d118de7a11743a78e936444da33f07ce3fe6a5bac02c14ee24de0752df349db8d0b2c2ac6960b8c58d3338be231e3a0a641c3a8326240b4593cde9804adafed63a74f0fd12b1a9ebefe57a445a814301addd9afe8522aa3f6f7206153ab1e480d043dbf52f9057adc890c5942f3801b6852d1c845a1a404a83efa945e3860990f826ed5a85574fe5182748320a55046810e52e0494a23c9f83c18c71a462c071b3903cd222be6baa84c8b9e44de1be90a54b6ddec20ee637e4e31337b734f05942b1e473d587bf3a711b1ef22391f2f6601cc8ebc6a92d684e72b04a1c99113ba37d665d60d8969bc13a5ab1da250b79960f23c0eaeb799deb659c22b917ec462a4f3b1a2b332cb16885033f106b17d04d12d2821d354305e6d04ad0b3bd3fe31488089727e858e118a5882e59628975ef7ad0529ad1d58b22b32c00a852d7d66b26225fae6fd03791dbe0992d2079d71b5e93972423cc1c3b2c4750307e12b136b6826583a11450877eae119c3ca6d26d6660276bb2a2d51445e14743b1465846471ebc9a8820750ecdd4133e7956a89034b7b6b427c53840f5ba764501366f91bf26cfcd4f040c261466dbbdf1880781125f3c2a946a936a61697763341490cd65e0ebd9c712e71224408a6210b7762fafd5faef3a9a5871585a6e94033c199d77f8e7989771fee7191990da6b5afacf4c3f5c67e0fa39af96315f44d15a02fd8e7cfdb37831e87e930a81e882b293284689ba1a5c3e7126bcdb4498c5b045ea120e4abbff1b4eb03c1799ab56e1155ae0fbad636244d1a9323b50fbe84836e19a98e52b5a490432a88d2a294441f4d16d1ced6ac6e02040fc82a20cd1b63b2a87f13c12441a199da27b8da08a9822f645096078ab13d8ba83e78a970596418829f786a24274aaac14ce13bb6909a26a42ef5ca50e4af0810e51f85df0ae9e7e0f9a9e749fca9f2a7cf816abced80cb722490ce22b75e441342ef96ca933641dd02cb38aacf8820ddfd85b9385822e5bba7ae972a817df8a66f96db2666e915ae768423ec88f188968a321d62a8178964af08cd2e104d774a34a87c4d4c00fbbb3834cd3a8b2626b0a015b393137efc39fd82249d0b67ad09043473ed2316bc27c142f254b0edde9f1805ccfb2986eacb51f67499b124b08405fe7f824b6b39651a4f9d9ba8753a5b525b5a8ac00b4ee70f035ccd38751480f8833a71680278183e8aec9d776283fdcf445b71abbbd05a6896653bc7d651ba5c680841eedc27a3266ccc67411c0b458d67f63d5a6daa13f31b62eb8b55f5c155ac30873801bcda2c0c6726a64f54527511838329578edde956aa40ca0d8db018d20f5a86401b8f2f8d5daaf7edad1667e97da463b3451f9e66606912225bf228fdc820ea3db5d6baad7c2905e8c22a22ab237645cdaef6db8b36dc9dab536a4edb4b8fdb24f3f250daa0e47cf7924e1a51b890ba5d81fba2d363dfefd2e90bc52a74320796f53465c2683f8be470ab5b2739c76cd445a8f2004b672115ecb8a44c7e29e2a04b4b5c07a8201c512357b4297056ba1eb08f706e259408c3fe2778bfe47e5cfdb356dbee57a0b07a26938e43e3c81147328375778c26e7706c74920e3f8c416dc7fe525b4b781f7de7cdcfd38649ffc9e71914596cc2a1a8a9b1ca11d31346982a5e898880c5dc092585f19bd2d171811440a85995b8fde6b4b1646a1e6557e3f4c95479690c1357fda02fc43e872074bdde2f020ab9647e6c9210bd745e1fd1f98974459fbd8cf1eacef6fa9b5513f524b8e203631c4d520f7da9f0f27153f976a125374edcabb5ee2b6024c33b19fc9fe2bdde45f3619a50912b174cb0603c186e61e065092a62f25757e7a7512276899aeea41c12eae45a019dcf612222d2c1b0d273a91d172cb93288c354e1c76293fcc7d6058169a804abd08557db0a5ab1086082fc9a4b7a74bc324dc109486c8778003825dc43babf0f8f04e2df4c69863747907ce3acf49944c82032de235bdb5a32fd364e9f13812a59aa92c1471b97eb3374791cce52a1e011231eda1f1b87bfd4b192eee36f40b89ac4525f0803108f2738964fd711cc844025c304b5d48020616223bdf31aa4dd557ca1931068551223374dbe4f2668037d05c35847884055e3ab5c152ce466865d15a01d3d5c7b02f2b1c42db858cb49d39143c96426b9a6551b5b90949f2eb87631757679534d21a64ddc36db3dd172c3ab28e378646efab7b6562cb76fe7e54c62ad2d6f12e008c49df8e2525a89169d3f409075e6221e99a93a6a7f463960fd47524e9954df4f609c1ecbf7f23e546e6c04dc4144778635ba1794c04b00af52d6d32684898e9317bf3b460627abab17cd42a055b28fd6e2d58f5a9c6df5b70d09596a29272329197c07b0433454bdfc25c02934e00115eddbd462e4577f2ceb67c44982d2c2b84eca7652f46466548bcd9fb28a60eb0f2941fbb67b2bbfc6d21be3379eb37bcf56c6b44ef24f50861b00de64cb3d6b0c060d955a50f5582222eb2802b3ed1de51bb8af2867d5989c45a92e7c7ddaca24fa404690d4bf5dfacfac2a22c0b7252f2e57fa74e02eea5a1b9797490ce100e487ba3bb892dc693cc60c27720aabc9e8057dd093b5d4d20505918635a1af736eada7b781254afbbd4a94c720f1f73e786d85e86d327affc96c1ceec4e1b28c70b113a61d5a5d15cf859471f435ec632113db6421abb55c6dc95fc8740acdfbaca88f5f52cb5bc31314b791509b1598e3773dae0ed3df6e13fbb43d6b91a1609c5d2447dfd4c8032d083b921e4939953e954a1c63393416d3a1d83708a9cabdd1e34b7bc53c4983f97742da9345aeccf5ee4f454e77c883d2314b1c6c65c2a926b63776e5f2b2c95f4d47ffba37c9b6f722cce51252cb2db3d87c35d2e5d25f778fbd950aa35a99f94556eeeabe5e60212c8484b072b5881cf2791bb6e618d4a407ac5841d1d84ea0053f32eb4d4e4abcf8dd8adb4b8f7d8e4122b4ded650b64245d8363663f109ed85315872e1c3c94dd2fbc6841a0845564b617d80697d51267c3827b588a48754838bd51abb13825cd36b931d0aac8503dc661e99c20946f185361dddab351e32385fa2ceee6c618b20a4d58cac8efd71478dc3821f1b44ad685a1a1d05bfe0087ca31db53adb69e9a4373a72901da6b761b2d5b04a470d90d312607eaf6189a71e1bcf7f79b9a58cd4a763a225197c3578f2f79646cc8b05c435740658e9065bc7343a21c4accf676c40cee25ac331941db3d2b6e27b3a44f56a9019e9e3586225ddd1bc29dd9b1a29a384e151a30ead38fb03236ca6dce87d46081ac9c54ae9254f70d22773c86f2b8a151e89f9c1fcd37dae479dac3207c03324a6dd0a65bbbbd76482a02eba30b02f58699f2cae7246e94b5e2a98c206435dc03cbd31989c0a83292745c8b5c57ff0749402fc3ca04d964d99a6fd5797b90ad30eb9b7b31f30b9fe80e2e666475596f17709dbbfdb54212d0d1a99f1209dbf446610e7b7f15370c45d8af465fd70c690045e0540c0500a3b81f489fa60bc869957c5f51f10e2e3d38beb1f8cce010c5b01ba2422421045219cd85c8ff667c460c49a519b94da79332ae6af53c562859499f8be6ed3353721e334b5087eb2ebe2c1114d9180e9779a9d3f1ca20477a6ec856c423be0232afd67689ba661cc549e08f75ffbf24fc42d234994e66470d1ccf7c48f86a1b48a81c4d61181019518afefcc281180f5d19c45e3f59d138f45c6ad546715851dfb6b8def5c350c277d8af1b072bba022bf1b1f7e1f8806df8ed2d6f6dcc4b30fb6e1700ffea230b5874bc89e9102d1629c0056438c1dd1e08c00abe1c62cd1e48c001535326ec0727683cf4b60bdfaaa56b9f5ba5064c2973f1fc833b148588970712a192edd4ee483f698fbf81a6fac471423c4b97aac52119ae5af7846a74f314cf587c0d12466d756890513df5c7fa9fc18330832df9f8473bd3ad7c1bfb6e6207ed183e4d015c58b5fdccdcdaa1fe1e0dc8b9792fe1c8c5933d91811967c0fbb31678d3fc578f5546f90389e232861b30088774d8a459628238c8554700599994923bbcdb7137eedcb76244b70f044c59f7e105a3822bfbd42e8e8b73b93f2c54cda5835738b51af0782c0b50fd6464177b85e414aa2b50071e2949954e43b0d24711abf56d6e800ae6cf8231d20a8e9ef672128b1fd670cede931905f65c8840b4c0c99d70ddef68663272021b8bb387ff5a0d7ae1b24ba65ab0783ea0235f20b54bff568acf1ec016e7f6e4cf6babb5914c03d55299e6c6b84bba1e92d4fe9254b982bc3f76b53c2bf3a007a6be2e3d18c0b3c860392acbbfdeba93aae1d1957a59f1a809784e3a791f428a3fdf6183f99039506c64624ed6e74f263aa7c19e14d1dccd51635f5e004343aef20ac11ec979936715701a820f9b34733b456e29e0aa83e6d540ccd037f7eb10c9620686ff5a27ac04f2a71271908e01c6f6a07bdb7cb16d84d8c1c7a18dbed310c2610bf5695c3e95dcdda912cacc075c44714d4bd0a775f65d6a69ffca7a7e3977890c910ed3f27fdb2cc51605f7446d9fe5a7e13c48c921b2f34d70f8e84469e6d9b43b4ab2854a1caccd81ff6c2e0d963dea992902e5144288d16d0f8188d01ebf147849a7b1e9b2ebb155f8f2902cf5a7f4cadd768c25fb907b97319fd7e897ae89caa42d9b6a8c066fa17e3b3235587a0376fbdc7bac3afc35c8e16cc58e5311ac5beae69e8ca7e94100268553f4f76a5619f0c8fdfa18eb468af8d51b8928dd351ae37813c47afe73c7cbcf928c808df3ed395a0e7e0cafe003dce4eb74dd71cee51574eaeda133b1fb0a3442e710a6f06ab29e7f33c0b31ca3e3b41094bd4c48548b8cc1b4259c8330dc00a3dcd8ae70d0a77a6669348ca8638c116ba91e9658d3e85866f77bcc16400438b68a58707ab1fd4b02e53814a3c7e2ad4542224d5c597e5842224290b9ea7a9edd8978a8c8fb94f85f837ed90d075b7c04992a167b3fd3e1795b97d87c3a225961b1f4de9d41b6b81f11ee6963a14bc8c048e9e54260adbef3537cbadc8df12b7441ae6e1aa85a58130094f186aaea06607c1cc3d4bfa06ce304b5c268977b03d6416a6748ad3a5d9d4ec8fe94ded80c0a2f0d0cd3d052a69b9129b217819fc2c4150192bc729b5edc0f7609d31a936365ad1f306a6aef0d892ec27834b9ab217dcab7b3a82fbb2dc5c530dd7018b2e102b7ea288afd850f1aca3927042afb6c69937ba552121f5b2c9a78fa21321eb629054e83eccbcfa490ab27717d435126ef5c9eead9964cc3376221e000aebeba8c9d3b3b041be98e9073aa21758b5ea04d0832f04fb99377872248c54a67ee7bcbe82db24a0edf465d8654d0c3ab5c41aca66a2533c05496426e11d3c658e0a773ef5358348c3526acf586ed06d857a2b2b19044522f2811db6f5e35f22f0a1edca8c8d49ddb32fd47443d2394f556ac9710c510ba4d793013f3063c7e78973dd0681cd02a6209a0b4202b596720e93945e4ed1ccdfd9490327a9a90d7fbc23832472af6e420b18759fced10ab618baf16c4043e94d84e296d3e5295fdeb09fc8ccba2dcb41274cc62b9af00f95a0885d51521fd9727c9a24ced543ab38e436ae80ccf88c4fd2ab1f58046132470bb8058387e098aba5b9361a140c824c8ef3ff12dbb787c9d6b612eae4c248319c3c865dc3c913a47efddb5d202d61f45f921ec60513803fc70afb50bd64777b10f7d649554eec1335b980054a1beb72416d78a871fc323589a6aa87e89d07b6f282de5190cc753336c96126820326dc6755b92e70ec41ffc6a47856c204223924c42f7d67ffd792fcc3889e63f5c600258311b93510ee600a2636b2579357ddb745b8b6489dc1ef29703ea877eb23994ce8afcd7f6e66999f30a06431f46e14e3e5c1417918ed5464d2b723fd0c527ae1e8b04e8e6f1dcf5e2b03cc89088f7271ff5317d2239d87f25df731f2c829d2c8c5e57912ebb159b72827ee8389b35f75f29a6ad16fe6e16c44b555aa9944dbc96ae0b1604d59038216957ef4fa29f452b5b27657431526f66125cbb6c2a03610ec9b82d0147c6cc12643f1143a270b9dd52d57335adf404945093315295d7f489ff562949a17b3bf97e1c08ea044dc581cf06d94c156f831e137e0c928ec018772da783813678286d8a0089c0a3317bc723dbe8bd8610a045cf614309e0257014c5b4cfc82688b70127d68400c8a3305523bf276903dd471a6a6da0faefaf07f25d0d97c713c8c1770ff55602d7dac5ab097b26206133af8bb155783333144b45dcbfc574900268996e2477bb7d7fc3b8171a2ea12d04a465af84e82a42f53a555cfecdb13fe2526bffaac5de2197c3a5dc993ebd7c9c77f14b3e86bca1579f2eb38a490b700e7db049e685b4bf6ec9e23fe9910db357521408aa0978d43b66ad8d80d075f49f89147287d0c3089df397e27dcf21f70f82567237c0dcfce6b85f3e3333d4eeef15f53931641e927830e7a0cc89c5d11008edbe92f904218d371beeb1b4f9136828205f1cb710e29b8115da70396a815cd9112791a240742ceaea4caa9a962e5cdc1a7f753c20cc7e89026850a342758d04ee47b4814b43ceb5236fffd5cf328758afbb723f92557fdf70bc16b3cea036f2deca4f4aa2bf7a1576b33d79b110a09f6b292885bccf7c6805e21b0748488ab60025184a53a47bbee1fda045ce314cef2e9067044434ef088a12b3d6c1ce270f2b378ce9436736e21b38c92ca8244ef3ac14a3af6e5803cc8d4c81b0f3fd412f3ef668e34ed585b62603322cef5bd8fa79aba174a447c8f9498ebb8a951545a2a735e0662bdf7fd29cac541eaffde43b21c429ffa4b86ec1725d6e832116c3f6d979641778a698848cb70ae22b03b26abee697fea238f8f6c61649aaf73a199e9d72fd3e4d4f126a17c802a286803884131fc5d0b14e93fe56586d542ab511f6d0e762e83560a930361e55fd88e1a18a7cd2120fb875bdd120b73f3161908119fa0d709f304f9d87efc53a2ecab3f9055bba5a70524a91cd3e0ee610e55bf8be531d90db4d7d949ad615151ae345f0fa522bc393c02a9796d7bcab3fedf26f1e1662e319c12173b200c6f72cfec3913c7cfc58c395d3b025a6ae4a8135baddcacb538e195a2b5750f1cf7e90227273684425e01d8fc67c35dfc8ea2f3d768c3122bff90187d207738ee4d758b776e53cd68da3118d063a1d0f5bf2a2234969fe0d76416fb69693b1e6badc9160d6b36dc69121fe2661911e704be85a8ee1c6d30bed5ddc3398bd76e0180674c9aac92531dc4ccc3c3c1ae721c152258507a92cb3dce4c6668fca56ccc6348c7cd6dac295010453725ee9694c2ed040525f66690b823115f13838bf5031d7827480bba37a936e151d4a45215c147655c01b5ea6b2cc3928c21dea76594baf491c37ab402416b2a244c99b8ca18046c01d22a03828946c15246708e70106ad758347c2f6728d04028a278c5bb2fa491b30737aa950fb2482bba68b5c12a7435a515a8e890088c4aa2a615a804cb91d968af8a7a82ca2c455794fc53b357193a46b1ea1adfd640a53250b7255d2eeaba76095e6744c7463c031de7330c3d83434d8553522cf484a1c3a19750c5a17a5147a51e88f5c49183cd373afa0afd1ceaa9a3b094f7afcb701488a8655431d4b9a827ee08ed15531e869fca16a84cb08cff9f241f4ba8f0285f65c42f346ba943ab6fab32d1a244b1652b6c987374c8dc11c732ec5ad6f2fea1b02efb1dd8cbd1cad184519ce80fd09c1b9db7ef07ea3f7432a999aa46d44d422bf2c4f9ade8553db582f75f8fb2e47566af15f565b95b560d37a9a72e109848ac3633f5bb2a63d00c882c1a059c18ca30c54ba7284465a04e75cb4a8c8e4695416dcc018d169f8a942168a6d486701d30f79b58279cb5db3c09a632c18b06369a218be336273cad4b0fa10a9dc27d41bc0386ace2214127f248d3a4f32149e19d6e89dc6af5f07cb95bec3cdcf3b884982df87ad8609e354a6c08ca6bc63dc526c36e88adc6c68645d2d8ea6192838fc62780eb764ee5cdf44f346d2f41cf5ed6b0dbf0b46dc734563e5d6730a2d92ce4bb2a5c2a8e601923ae4604a3e05c13e81eeb227c5a918498e897caae8a611a735141f7b59ca22138fce786949d91d9c831b418506c1ca03fc8bc5c0c63da50c17a462d56fd724401c0d66e5c85d05a5e48ca310c2b4ff902402288e3a3f6030b26694086c9ff4c1f0d4e066ec3024aa10591d94008b37af412a40d7a0c9814b8095871b0d817a845a4831f3af5dd66be611c05b01d7977c159a1431f0dc1763b774f348c8326176d47460d8c9014bfc7263c1f32a0284e12c6a2b17e64a36932d79cecbdb41a4feb715524f2011b14790be7073739ccc10e38335c4566fc709bb14a447a85e0444b9b597196626524361376c9ef1038ba6cae039b315e6ca9711067e837c3661bfe0a721bd86d2cb61191d80cb6c5d5b9bd91136c399ee3d8b691d98d65e51887534f6c6677d6be0b9dd88cc9a128cdae8d7af166dc144cc86ae5c8c6b46caa02beb045f90d3660b0a4900330333333333333333333e3ca4bfde37ddc1ff7de8d60c232aa45279a9292921209ac35aebce01de7e01dbc8377f0d67401ed0da10d1f0e4d52abe0757486b10e62c41ca48223a94327bb321b1fa7e055a63ce1d2e660724e0a6e8ebfe737785c565f141c4b1e13738c50991e149c94426d5585f6a5ee098ea47c5b29c7098edda4077297396c821f2ed492e698473a98e0b57d479bedef79094e698ae918f197b1129ccd6695ebd2243829e68b15296548706a3e6bf050691b2d3b821f3ebb9d49eab09fcc08cec713724cc9f4712c5911fcee30daf6690c11bc4f93de1ee45552ce109c24b143ffe8393cfe08c10f6311d467d6233d4170e3fb6e36eb2b3b0304d7b3cac52c5de59a1fb831ba6a5f7ce085cba91a626726b7f4c0b188eed1d5b47a5978e0a4c6e6cb13d9819344ea6732248f611db8793a3e87b4505a7e0ebcf6d0217ad8e3c0cb4813d66e3c546ee06d9eb7cb1f04b081ab5d9eaed2b33adab5f082d87f94ef83acb569e1b6c725211d3c0baf652e4731480eb46659781b24180b2779e6c95154d58c312cbc489a7f2bb2fb8faff09225c9a1f9871c6deb0abfe3284a9fc754af6d2b1cf97854438ea7565b56f81a3de4f918415388aec2bf64112d4398e081aa0ae762aeb8acfee1a334156e69baef49bf7df944859f3a948ea297a7f02579a648ee5c1fc4148e678f9e2d867cab580a2ad4c986258f144e942011fd330a5f2ddfa71ccdc7cc88c2df509a156982c71d0a377687f7e8e0592d28dccc21dada625dea3ee1f49d947ac8924b9927fc8ea12558b020695727fca05eb3a74cc509672285e718de849325a7d4d392ae929af0439248513535d57932e1a71c4ff89c35740e0f263cf7499b263269adb984f763ee36d1e953ca58c2dfd80c7fdf1e5fa412fe86ac1e49ce9a630f4a38496ab2cf5c26e1ada698aa6221ea2b92f067b3860f5f8984ebc9936d65b5ed0f020947cee6db72103d6df2083f847f2c094f911e71847329dc86d481a7a46984532187240b33c2bff0d03767c851c72ec20b29592ffb304554116e064dcfaf19bacf4f841f7bf0a0437920c217b90b73c1e3104e6a5bd6beb74a1343787fa163f3cb66e1e342f8d32621dd29ff84f0c2079d6254f6993708af3abe93f1141fa515841b358636cf5aaa3981f0ca3c27bff1f3ce00c233ff20d774567cf40fce775027a942747cfac1f50fa66346abd14efbe0fde5caefb1458b1c3ef871920d8f7b0f5eb7e414ba23f5e0556bd01a3bf3e066a4d57469ed2d9c787043dc6f23fcc776c13bf81f77297898e2b6038d29d9071d07621dfc4f39cec17d460acdd1c1f19caa3e883707377c10739a433e084f0e7eaa34ed992da5b7581c9c34ab9eb482e58b191c9ccf9c2db25f8c31526f707cb2ed322d37b83982abfbaa4fcc698333f39f4288f6d92d1b3cbf98c2aaf755e6881220630d6efb848fc963560d4e48993e30b7ec3fcd64a4c19b481552485ff5c6340c178421030d4ef504dfd8b0ac40c619fced38168fdfcdbd449366011966f0524e89acd441a357b803168601a30c4ea4cc1f59c425bd5eac066490212f937073e943c618bcb4b8decc95a1ff3b2eb6bef0a28b2d3220430c95e6b0fea3142eb68c8c30f8e21ea4b4a9089721cc850c3010c3fb2455fd525f30a656c6e688be1788e497e6735e0b97404617dc0e1b724c4939d892b8e057e7cae361477af1e82df8ad193faf355ae1046468c1ffb19c3286adc6908f0c0332b2e08cb77a78cd1d7e230b032fb20232b0e08590c1be821ba92e68f274d1257fc8b08293c26a32976da4bb8e2fbcd8c2aca08b14aca00ba4808c2a781f89a7d45e953cbe80046450c1df4e9273e8925ce00432a650351620430a2d230ace5cd0147a26c3c5568d1a99749001053fe23f3e5189b858be31c87882d31756db2d212d4b8e2623c87042d540818c26f89299cf238fc2cc4dc6c542c074f1850714d72083094e6dee4de7d31a1daf8c25f821cdfc440c2a5143274309e590638979a2e3586720230947e6f6c163acb83a073290e06dfe9c25a40d1f117304c773f81023a6c4b88a11a86ed5f4f4be087ea8b6cf1fcfa9559808debb89440ec9330427cf769c329a840bb1230427b84b47f3b17e529d20b895dded953d40f03d677498f0d19c477ee04555e9fc99c423f73ef05faa8349ead1d7647be0bd664fd61ee450226878e0a4c8a5327b074e86f71073c4e69a4a077e900d39c8a164fb2293037f656cc35598780f0ebcaa8c0d9afd22e3065e4a15edfd4b82e71c45860d1c8d1e5f4ff239f8e8520b377264c4bcb1ce63b2d0c2b9f6d68aba6916cec9c7b45923a1a52bb2f07290104453864f6d2516de7c8e3b280d8fa25705168efdff6726af8e1e79857f96a5239f9493d4872bbc1e4d4dfb186a3d6e8567993e9ccfd979b4125678eddee5a1a9526125abf003adcc9a93d554481255f8697d36e498c35a8224155ebbc75a1d9ed9c1ffa8f04c730c7b4df6e6fe9fc25b93e41d95b973646f0a7f2cdbecc77394c20fb12395f0b1a884b449e19ca71ca588ee8d3b7b14defd871134ea3ffc220a3f720eed22c77328fc10ed339c87b1fb9941711c99c7c19f70433e881e8ba6d58678c28df6618794365c6ad209d7cc2549ce0ed207ff72c28b5531df99d4a3fcbb09d74396ca13a2ab09a7ec2a64dd0489566d26fca0fe3ce68f35cb6a30e1d9bb745f889472b0f5126e1e0d9fa36bb58417251ecbc420e991a795f0dfaade3e085f29524a09a723db7d32fa78949293f0dca26886895312fe69ae0c1e1d44a66846c215d90ed70e2b64c925249c1829ddbb33a5922e1fe1a51c87312186565c948e70a23e78e9c923252936c271cd718a5ad4a41c898cf07d653a74e416633b5c843749b3e414b25d21a1225c4b216ca203a99839897025367ddc610e39db7388f02a5d468a9eaab4ce1dc2ebb40e54428721bc1c237d20e139469314c29b4a3f214751199226841fd65fab669f2c293908af2e0495a0412a624e10defdcf0709afd61e7781702b4d88b163f848313a40782672f75761d2a4cc1fbcdf902b85c5f62d8b1f5c8ddc954f34d607ff438a629b35ab6ccef8e0c6584915d6d91efc389e1cba87b3ec74991e9c98825d5a87fec9c195073f84e8108b1dc777b6f0e0e794356f26bf15c6ba83ebf669726059b3a65776702d6cc2255955072ff769c7b9a347e242073fd688ec40dad7d3630e5eceee961e678b62b71cbc4a1a25a925264b8c83f3f663f963ea8bebe0e04cdf5f86fb4fd5aa4911e30d6e05cfa8e571109f330911c30d8ebce6ded0b8b4c19f2ca99307f5fd5f151bbe10ce53071d522cb9585d839ba14722db5c4c88a106bf7c443ab6b7480d49831fe608319a79ec65f5e130c44083a795455624bf60887106e72fa9484bcc47b61dc30cde799aa7e89872989ac528831f2d7325cdfeb125916290c10f324d6fad3706bf277ffa24993e77c7108317a1e53c36c86676334618bcf940ae03cd124cd3c4781c31bee066180b2ea576c966e20557d5524564bef34f7e17dccc79429c489ca70c73b1759683185cf0da5375852017184c83185bf02ace3f36cdac11fed1829bb53abedab6919c3fb64819c4c8821fa2e2be3d858ea7432c3897f264df1837358f71b1750557d42c64a4325bcbf62186159c1c6677dfacf6f5e657c1dbd8398e3368baf78de162cb84e1c50b900831a8e0df98faddfb7de0999c8217db248449a99e66f2c59182638b300c181d460c29f8fd3972d49c3c59c48882575953d7696da9fd5070d26a44e592cfeb63f182055b1ce58a184fe8b25648bbe49ce0d5f747e9552fd61a89d104275eed82cfa4907e7ba18156c46082779b43878f30cff0314bf0a254f5775039281b318612fc3084075feef9c24d6c8f102309fe4fba0ade59eab23b48f0d6fe55a5031569d31cc17f995eef0c8911344611300611fca822fb3f6a6d086eb7c79184c41cb9c4b5841842e8da73bfa46304c18f4655e5644323463e06100c125307296ac80fbcdc71322dedf8e08e37a6c5b85c7a40e6efcec14cf43cf02a25252ace63077edcdf9922bad681a329f2479be53843c639f0b3e6385eadf78eeb4371e057faa0b6de73032f8b86908378897677c6b0814925e7ca746731520ba746e2dd2c2b5af8173dad66e224327766e1d4473ff389b62c6e93bc19258e85f7e95aaeb18485571da444b779b5c89157b831d53be6945f336c5ce1a470999c337bc8f05be1c7d6d829bade53dacf0a37a510555931ab36c7b10ab7831c72982c5bc9660f55f83163f9b6844db726a7c29c474af2c72e7563a2c2f73c1fd541d3fc3f7b0a377dac1e3d336b8a536ed55c653e61624729fc6c39bf5d7497983325851762dea8f48e2c878f1c459646f2460ea273bc280c8b312387394a6ff443e1791023e4b01550381b3165a3867cc2c9f1519e34d2e4709527fc888f91d6f368ca612736fb73959c533bc6092fee4f5bc262ea33da8411368410c4e4628b0c03d08413d22f7d1061fd539e4c381f7afa1c652dd9670c261295d26097bf84f3398a9d27ceea52d2125ed234c135bbd587a152096f43fa8fde424ca97228e1e7d618553e3aae8f3209e73aad87906f3b7f1c92f0b27d881ac3a5657723e18739bd234374dc390909673407a17a62e411841b4d6e12e308d77ebd3e8a4eef5d69849f2ba78df0b072dc3d23fc8bd5dae571ec5c2ec24f7f4babab15f1796ccca525c2d97c215b9e17d91c8a08ef3f6b4c1faf5fe6104e84b7dca14c5b6df20de16be5cc8a9d42782a1d45d4ea3808e1f6dc6f068d109db3c7208e209e839298d22505e15fae79fbab74200a89946eeb3e8e2640387193ed347bf41fbc8f43e71cc9a3fd4084f475b91493c7edf5c1b5e47f29a7cd714735f3e178fb38b6700fae87afee20c5d5839f83d89a87b3bb8418427454f93e78f0ef53761c5479e41e7707bf2c2c7c799437c729b583339a3bb68cf920eaa30e7c8c8a31e530c71da3832ff6ff9dbeedd5b49c83511573dc217b8614b61c3c2fadd70a9e38781f793c34724f761e0e84a87cc125f75418dfe0cabd44ccb176f5c92c089552f02ca4bacc32a131c22878193af41c778c1de51c14fc8ce91d85f4b149774ff0c3d08aec0e7382975324f6f6868ba135c1174d299364d487d162821b7294a3e4b496e049fba87c903257564af05245c8d2ecefb72a097e10527aba8b69ec4307129cba0f59f5417290d271042faf5fba8c7418c10d1da607ddf3299d53043745998c670fa34d0e11bc645d15dc21b81e955d730826590bc1cd1965b662d9cc1b0427be83ba9f9b6513089eac445947da4af0fcc0f95c1f7da776b3cf077e84a76fca6678cbf4c077b94f159172ff8407de5f8e63794739c00efca8e3ad4992538c8e72001d385ed6215d8b79b8ca0172e0845039ccd9c73f29e40038f03dca41c2a6567b0939c00dbc288fa255d286f2200938800d7cd91c2b74476ae1d846b9ca10ecc3e769e19dd684c7f9cd3d3c66e14713424c394e9bdab42c9c9e0876962b7786742c9cc91bf75148f9c9372c9c35efe0ad738ae9ef573817c3c71b7c57f8693ac5a887a487dd0a279d44e720e5ce97352bbcdb8ad6395ac50a5985f31f4a6d6c8d4aad0a37668aa7cc4a332ea7c2f76023a36999dfcaa87063b6edaccf1fc7fa148ea57bc8a19885aa4de18d67a57043fbdcb3fca3284941858cc20df691d78c2b0ab7deb447c35068bf390812ab8282abb168159a7ec2f598bbaa247b2a0f4f381a835cf8d8eaf46527bc28293ec55776f992137ee8f7d6482321addc849b3caadf9837849b50135e8478554e294cf62f139e4d5d761c478bd51b269ca834d9a1b2be8413a2dd87d1375eea6a4bf8513a97927b9f94a7ae84eb2396ae634a53c20f52ca42e6ca2c1bf524fcd414434e1d95fa7b24e1a5d4603967ef38b427126e0e326f4bf44f8b1e4838993654f6795f34cf233cc93efed31ec5fa8e23bc10b52e6a348494368df02bb52a4c76480b1b46b8151a953ea3c2ad6611aef4864647b5390c1945f82942fecd914984137a2a6787c84abe20c273b7b69aced4f09743f861b0581fc28518b518c20fe672fc9bb3570a2185f0ad627658dfa7f21342f8311a72c60aa61d4c06e10762ad599f20fcee50bff271a1251208afde526c594b5c0f083f8e393a8f473a4dfd07a7cc3ca60a17c362ef07afdc36c264fae065c8c79afae083a7c9c6638cedc1f114317f242322293d38f741d24abfec6a1d79f0623c6aba089ac3e8c08397af4e43dbb22ce60ebec4beede0fb658ec3b5c73373d781d90ea483db95b263344799369e831f74bc695210e5e0c6d439fabcc6c1dfa4b943f1150eae654e92f9dc342a7d839721d9f12de5414abac10d1d87d9632b5cfb6c83671a39c741d2d431996cf0ac5243541fd7e07465956c1ec658caa9c10fdb614e558fe9bc4b83ff7190c3b8d5d67cd0e087f07392dd3378bdedd1a5ac0e0f33381bdd7bc3bc6570bd83f5b05346062f25e6495932ba778cc17b4bb9376214afaec4e09d057b0f6518bcecc8476390180ccefdbf77c871bee0877e29671b8f17bc145273f061da2c962ef85a23bd31fabbf35cf06354262651f1efb7e0a5ec142cc3460b4e6bc654a9e262684d16885e1d7c482958f0b5dd6247989bf82857d8ae6445836a0527749810217c7b1456c14bde6973850a61b64205e754623a4cade1a932055742beec41ecaceb102938a15fbd3af351705cbeec3f780ebee3a1e07b0e3b753a9fe05cd01056511b6639c10f2ebae5ac9247e99be06594778f3f8c0e1a2678b933640a1faf5b64095e84d09b11568267f769f1ef9104a73e7f1c7ae88104e7fa7344f5f4b1cfe3084e0e3a42bc4c99dd6d044722653bb06c566a17c10f513307496622f82769a93ea423cdd921f892751fe628c71c9b2b0437e58e62f28c06c1f730735467962e4305821f27c96129da64f8c81ff822b612c2f47a10a50f1c33fbc91b246687600fbc4c3158a69b8f5b451ef87d1a6fb9fb8314b20327d9c7a1e7f9dbf6381db831776ba596f9162f075eb4ec100f217b640c07aeab4fe6685bb63206b8819b5fca26967d5c6f001b781df97a35c7e553d7c20bda21244d7972eecaf058c93ccc5acdc2f98aa134e7cd70175938123d3bfe2894e68b36165e7ce8133d473e2cdcb056a1736d7988cf2b9c303194cb755ce16a85bef4c85208dfb4c28ff6917fda9e70b16185d71f555cfe2c4a8e55f8d7b1c74345b0e05154e1ab767c3908396ab44a2a9c9e994b97b7a35454b8f5a9c25a8edc29494ee1e49bdc418839c8111253f8ef963c8e42be147e8c9ddd36ea34073d298a985635cb1fa370bd3ecc3c7fc4ce1ea2f073ecb952ce23147ed0724b33d11dc530289c1ccea3f9c935a7f127fc1017347bc7ed61c69e703e481eb6aadc0937b9698e6cd2478d2c27fc568f327290183ede4de09303a9f50fd584dbe9cd639294cc5ccd8493e67148ee9a63d78909ff35c2574a6dbd6dea25bc99bb54b163094f428eeea38e2cf66325fc28b6e3d764172585126ebabece7c7196fd49f8c1d29b6f4f648c92f0d652847f0f6be922e1479914113465c57490f043aca6f031e5e0ed23dcbca5da97426f089a23bc28a61d474937b3aa11fe878c9d3c3a9ec830c2f30e3e9c644c49c3a48b7025b2cfa25fc44c114ee694e1eb2b89f02d3a8ec73c06117e0c3955d33c840e630ee14a6ad792397b558d219c7416ca3fe410b92b85702b7518c27f74ccac10c20da95f4f7910aea5cc9c42737f50124178919e6adff1658e914038e3b1fa67f324361140f871d47879349dfff3073f6c9c5ffe2d898e1fdc10cd41a3c61c65f4fbe0c72c6ad71d84d2f0f9e0e5a9081297ce93f57bf07269d80e2b7f90cf430fde86ca520dc9fb3f79f093bf44a494c6833f371e9d64b21bb3efe0aaa7e710edae1df51f695e8f3267b90e6eb7664df1618e89321dfcbba43ed391de4a9e83172cfbfc6a2c073fd2ae58173193e5c4c10be396fe29c2fa0f073fb4ed896e1e43a76f7025888a7bfe4e9aad1b9c546fed39d536f86e1ee788930d6e6cb986e6c91b9daec18f3247fd1e199d326af0a312ef8e47ab7325d3e0fbe5b0535a5b0f3e687025ddc392cd61a9e50c8e4d6a0e22df9ce4308367f159b56337ff0ecbe0498cc86f6bca9a0c9eca4756f48cc14d93261e3a2ec570598ebd30b8b93e0a9ed181c18f88ff5af7dc51e60b7e468de778096abf17fcb0a93f66c4f4a1ed821bf37b5cfa7555d2b8e07768161beb3129b305af42a37cc428b5e0474c512aeec982df9192da771c2cf861e63c76472186d4b9829fa3beebb11cbaf35638086fabe0a73877f31012fba6829f72ec9b704ec189704b69a191829f3b9889cb11053f7b700b4bf93ca40b147cad309adda33cc1cfc93d4c158ba810e204a7530869823716b3b2e4ac262d61825b2949aae6ef9827bf045f353a7b234b18f195e066f449bb764bc93992e07de68f526c4ac99303097eb2aeb5f18ad27e1fc17f9ff6a8e41d7dd846f0c27806499ddbaec345703bd88e93c6f441083311dc9a8ab89b5893cf1d82f3eed2df9999735e85e05884c672d70e490d829fb286c98a09047f237bfa851ca4cff2077e58e40c8b613eaef481272947c991e981abed9653a2689685077ed0d1367fb13f0eb2033f567c681b3d126975e0ffe63953150b90032f6cfec366bfb2fc1500077e249d9d255d3e89ae0037f023bf5c3612726d8a15c0067ed8d7f93cad3286ad165e6cb41c2a365af8519a986d9259a7c92c9cbc621d694bcef943167e858995e314652bb1f0d45f2be7e8520561e1b84792263d5fe5fa157ef0dfd3ada66d63b9c2d9f48186fe68ec2aadf0d62b47b0b14829cb0ab7630f2b7d24e12adc8c728b2947a80abfdea227582c9b8e3015be5fd545e5303a8e0851e104fb0efaaa62adc7398537f641ba2bc7b1bcc7145e6dc4d4ea94c2ed305ad424e37711527895434f72f5d1dd3c0a3f4fca986286cdde8ac271f30b17257d6a86c2eb3a3593cd80c29fa9d7f8cc7cc20fedb183af142a2d9ef05288b38b3df127e984177f412c956685ac70c2ebb1f338a6ca26fc186e42c2868c29af09dffeb2df8924136e6a8851e22398f0256d5427e98ea27309cf434b59f5bf25fcf451c75399551ec757c2491f6d78743c259c143cca3a940dd932092f3c580a9e9364f99084ff31855812b6634a1f91f082a58b1e3cb49608094fb3624e533fc289fefb0f153bc24d1edb46f8514cc5a7d258b95e4638eea369c5738e83f02ec29f0ed3be43c7b1ab5584efa16a089dd22a443411ae660e55d4a453f78a0827c4b4f92b26a3c27a083fea682bb93cb5c7aa21dc786bf5ac4d9e2f2d8497aa766df3b9ce4d42f8a9b9d36b3a085f543d8a09f2d6a320bc694ddd51bb2731108eb8e4b8de83b4660908c77fe5d4a492471effe074a8e17fd38c4f871f9c4a135bfbc32746fbe0044f95cc32ed933c3e387ee1de7bed324bf6e078b40ea2a64eafad1efc73efeee8687bcd3cb81edfe6af110f7e0cefce76d91d0ca9af52f6783b90e6b73ab81e5ad6e0db35b92a3ab81d8748afcc1d2e85c8c12b8b2995dd2671703d54ef8f573a38781fcaebfa63c8aa746f703a7a0e2ef391f345e70657d2628769a80f2ca60dce46878e1e877414346c7072868f6369bf64cb1a7ceb0f79de72f5fd6a703d7be4a04572be701a5ccb7739c75f4183bf2134a4671a0b5e398313a33d9121a6ceaa98c18fead0d724a40c8e89a7c5ecfe4af927836f39ce399aabf1a8ff3178926e2ec4f42858ff6270c2fc6d75dcbacc1f066f2a849ef09863c607839f525ef320390cf71c5ff04337d5140f29217d78c18f3653b8ea4bd1bfa30b6e48b19c2485b372990bce59f684a40f72d0f1161ca91cbedfbde318b35af08367faccc741d566b3e046bb1cabc574d074b1e0d55b78c755dd26ed153c099b434d9f90c9572b3861fba543e47ca779f1b0a3b4c20d99a952adfb2353cb0a672efdba8ac52cac5556c5baff63ac3437d31db3a10a2ff664ffcf9b6e1e7ede48859f2de95108d73aef3750e1c5ca1aa9627b8698be53f827d33d99f2fba5e698c28bbba41eb2f3c4985429fca0f597a23b59be5092c299bed8162dd9a370a3e40fda91dce5ba4c149c654578b95876677cc7a1ba22caebe650b829ddff7564c7b61cb2010abf446c638aea486df9849b428ed581470a911896c28627fc8f5bcd42a8eb550b373ae19c78960e82b1c50a0e521d48c20627fc2862c71d346724e0c5c6265c0f1bce43e227c7686bd4d8a270b0a109b7f307edd8983f7a8f63055e8071180e6c511b99f083905c6d5372112d4c50e92932965d1313193652f89770babb2445788dd259870146172940b584bfb221b572b08401461717d8a8847fd9f5b17b4e295157b4997d887a7a109d7b42cbbbfc4938eefeb9225473f6201e614312cef976678e241d093e4e2675f9e28684973aec3a5d5269afec1187f1b87338c29678abb0b70ef70a79aff1289bd036c2550be92b98472184ba1b8cf0e37c1693a6cfb1087f725853eb137b091b8af0673d7c18d357527f4d02612311fe9a7af9c7f9d307416344d84084af6934c2a52c1b61e310ce8c4c276d172f4def8621908fb78314a23c0b001b6c14c2db681e2ce6d01d734cae0e3608e17b1c678cae0c51a9a941f821a4b76c99b3e5941e82f0831c66cc71777ebd06a292abcdaaacaeac7c5e51228df87ff600e1af44358b31440c6697fdc10feeda2b751cf7e4982fb65081f9628b177451aa0a3b60c30f4e4ecb24b12f52c7aa45b0d1072f870813d9fbaa2ae40d3e2053a7eeb61aa6d26de961f4506334dfaefed88d3d781163aaec9f8fb1adc2c596bd0bc0c0aa056ce8c1ff402cb43a0cbfc8835f21e4683347cfc596a5800b30b6b00ae374c10518c9800d3cf871b9a475de1cc3a4f9c25841171f3841170fa851a3460dde2d54e08215d0c61dbe08030c177cb1c51602d8b083a7f59623dfd47182159814a4e0e08264d5c129b79c3938952437aa0ba81eb0410727786be6f76b71f54e7080018651c1165812d8988313527ef3205c7a6ffa2a0c1674f1052939f076112f2257259135356f96b3db448c1e0bbce00d6cc4c12fd390d992c1a3a6145b75a0cd7bf1451860e80aba48c11746058a81116c711b70f0f387db0c39763ce98336dee07faab75f8c1eb9e106ada32eee4ecb23d4253eaf8a791c39ecb68f8160a30dbe7a6c99f6d1c63cd9dba20b161c5b18166cb1050646b0458d0d36f89f39a5c89363b61e8b8b2d2ec070410a4e5a60630d6ea6314f79324586ac1729b80d3538d61dfc88bc5e5ce02eb091062dadd3ba34c35633cdb6e66290b953b58f49738213787101167881811a354ee0450af2be0883051b68702c9ac6a0622f7e1ee4622b055d187206ef425b7cf6278fe09a2f5a00066e6c98c1f7d850219fd4d25c9e8bad93822e0c29832713730af3fa4bea09e38b1680915c8471a80055f05da4003130822d686c90c1dba4314721746757c7e08ac66cfa7d58a11d31f8315b8e223950cb09831f7f0ea2b6e251ee3060f06cc6265708ff3959bee05784201ff786f178c10f7334ef318dfb8574c149e96f51e9a639940b6ece9ec1453ad58c640b4eca665b16ee344cb4e08b5d6a0a92d2a5f6f70e26fbdd7b2c389d992ed9edea9d2b38b235bfa1d6a3d86105e72a778ef139b09c1d557033648d930ace9b7cb470e9297819fee73cdb43b1b414fc9118b2759028b8b6a93f8378471681821f89c6a658988fb59fe07ab86fada8589ced044f52da48f1f19a857013fcd0c346abc87124394cf02d36422563489163096e481fc12347555a2bc10bd1c166869cd2c649f03227b3489535d521c1f5207fdcae1259b53d82e3a5213ab5467053e5e8e314db577711b4cb9ed006113c3b931ca6979cd9d3b3310427a70c95b4729c3aa4674308ceca7fe4f164f74fc9b3110437f465cae1a291f3793680e06d0e391ef994424ef36cfcc08b6079e22323692acf860ffce875de4198e46dc1b3d1833f7bdc23d1e581131e9f63348c45b703e762b4f7adeaa0a203bf62088dd530078e7814e9adbe0d1c789fea714ef213a6be8d1b782aee5d975ed299b7610327b79a4711e9e31baf165e8a0923293b8ed26b87167e471b169ebd269c7666e1857d1c449ab2f02a3c76670a1b2967b1f0259a8ca51c62d607165e0e136b3ec7ce9257f817f239ca945aa9e50a27e714d3e5dd61c76985b72efd59c3c692301d56b85146e3fa833a4f59859bb54d3ac5b8146355f84126c5d87452e15f7d6b06f3a0c20f16730aafc7dd6352f630a9620a5f632bd67df4518a29851bf31e2ba490c2b3f0968c12320abf26b4c207d1ef3f88289c4d3d69fa0c853715bd4dd3dfc620289cb6912c62295f0a924f7829115390a858a9239e707ab3739035b30c914eb81aae1e568c8e909f134e5a86ec3064aba0fe26fc38720fb1f3cd4bea356125bf9cda1f4726fc54291fa61ef1f83c30e1a6ed914b96dcc2765cc2e94b1f65d7b425dc0e72b678f88e1ea42be1db7d18d971e71c7253c2d39c695b622b33d793f07360725922857f9925e149c664f69fce718c23e1c61434e7ce3239430c09275e24856675966b1f4184fbf3924d1de17d14c226767dd49a36c215f54f6da119c62319e1a8c744d5ca1e4de722fc8f467324e36d174e45f841d2491eef12e16f4c1aa53ffa87194484f7e92d857d203165f0107e040b734136fca268083723e51842f028c28485f0936407a9912384ff39b3cd86078f633708df0397bbedc958a320bc8f2aaad53eacc50a849783b07eb36eeb6105083f9c995fd6fcc1fba01a2ac37399871f9ca416aa434da60f7e289363f48997d0c8f0c10932397d58397bf0a37a771817a207ffe8c129cb9d3a79270f9ec6589f6c62f0bae0c1afdcd9730cffe023e60e5e7b7cb183539a3b2454eae0873972f4317d70c1a583ef992a31690ae123998363f9a3935c297db372f03de54aee7d1c9c742ba6ea3d1c9c90439979a455abbcc153f5a0b28accb4ef063774fac852eed82dc7d106ef5d3eb00ff9f38d071bbc8f4e6a9dd3327af41afc8ee73ebe540d4eda8a9d95638f2ac569702673e821cd46839f912c26979cc149e7a3d616dac3bc197cb3dff064d3e797c1495531f75493c1b3b33149511d8393c94672faa468a38ac1491be722a1d3a34bc3e0c9e4e06d29f64a8e04832bee41ff57a4aa07bfe0f95a88d029e8052f7b9691986ace65ec829f6ae1e319f34873b8e0e7cd9888892db2b92d78b93fb5848a18fd3a2d78136361426c89e7c8826f79f278967df96b58f0d275fc5a26623969577022222bf67c58e295150ec248feee5855c1894924a6145eb93a54f083c9db1e3705254f8e9143450a5e6a5289e935240a6e482ef2713c1628b8c96da225a67f7c79821fc6c58ccb92db3e27b8e1710ed5e2728cd56982bf39ce379b63cd39c304bf54ec249c4bc8d6c743865109558ccffa7c26094e8e2a4729a305099e851ca61c5cf9d7468ee0bb472166e690b152c4085e08398e6899c2f5e58be0a73c29c7131e4470f37d184663de48d13104b743c81d875e087e5c21c79572e430dd41f0e3102ec7dbea1db440702d8450267f3167f3074eea891dc5ec286dd83ef07e26bc6358c90c750fd61c2a5a2c0d0ffcb2b90e3a8ea6fb8377e0e6aaeb491e1df8a6ea1f07570edc8eab3ca590c581f315be56ed0dfc1c269329f769481060033f3e0a59f2abb570724c4134a6ce725269e1c76130d114d159f8df79629ab01256230b3ff020f7e6138bbac4c20d19636bbab78f21b0f027d8b5759a0c96bdc2f1d86453b694ef2a5de15a8a11e71f6c851fa6ac3c6b6485133bf038b29854307115dee638248f1682670855e19bc731a46473ee38970abf5a3e920f2544a643856b1e473139fa3cfad829fcaebc21a5c542d29829bc4a2e5b29973f47560a6fc42f420ab7ad4b7286ef706a915178122b872963f7077922a2f0abb327c4431f0adfc3467b8efe79c20714be450a0bb93d5e8d9e4fb89a3656e4303a5a763ce1dc8da758673ef1309df0443458b0bc2177a4e1842fd9999673d4ea39b309b77275889af346b2144df8914afa8e3a997574c9846fffd92c5b65e5ab60c27331b32c0f3999e6124e4ae7ccad29470cb18413738c1243ae849761fad206cfd9b194f0d64cb352ead6ecc824bcf4edb61d7e49f869528cc9b28f849f72ba9c3e9258110f48b81d2654943693db8e47f89e82aa457438c20d29738e432c6af67423fc38fa2883947b349a66846b729eb2868ae93e7a11ae67d9e09f61430ab5229cea286bca5227c28b219484b31c45af20c2b394fca14b88d6371fc22d93ec3996cdaa8a21bc0ff5d01f47918b6c219c1c4426757c84f06bdc73902a1d8493d7c3741b9ded422a08ffdd53e64eed953b32108e66ef9421de2ad70908cf23654df0f30f5bfec1d7709f29b2e32d1ffde0888ba528967a7cbe3e78a9352b92d1dfb6e3831ba135dd6a5f9c670fde64694bada93175f4e098650ec5c62f36260f4e5cdd5a0ea3078f030f5ea61c6bee204436b63b7875de1f840caff0cc0e5eb40a390ec283c831ab83df1263989842072f4a4a39889123c65b3f072f7d98e39e88c90aebe5e04627390e6e4e6917f5beeecc818393c7db3f9387cfe50d4e270b6e91376ef0ede5235aa749744d1bbccb1c25a2648397c390cf18d5e39e5983e3817f320feb715d8a1a1c1b19f1acb1b672d2e0fa892509963b98074183932ae7e6317557cc193ce94e131e7d33a02165703c89aac5e8f155f964706ce63c8cc1f2e6c818dc48313c3226710d8bc10bf9d80e3eabc5360cbe44c8299face4283bc0e085c9e123491e31a45ff03fec0e2486da94587bc195902991a84f92d3053f4afa63c2528ec25cf02a7d18973d795bdd2d38e9fd2b47f7214b460b5e856745084133250bfea754b9b3ad58f0b4cfa34a41bd8297572b86902243f2482bf8d27188ce548b746615bc8e9db3974905ef3a96d95c15ea564ec1cfa27541baa30b0335d6e04fce29a79c3e31c73fd5508313323b7a98bd37c7d2a7c14b52eba1e71c26b9bfa0c17bcb29575a98902c6c6a9cc10fd45c2c977c7e50c30c4eb5a492d58eb5366519dc4c1a1d7358ab99f385b9e200c7e00bae4106ef3dc8df352b1d440f1a83ff95917cb3e5387fe41083ffb16dff88ca61f0cb6d523078b162eedb58096a577dc1691993bce0c714d27f7c1c540cf9d305b727d347b221d6e0829f43b68fffac3b47c6d4d80256eda66a9fb121993a72ca9152430b4e287f0f5972eef5ecb2e07dc584a5a5160029d4c082d71e76567d566feae4158c2dd917b493a68615fc1cc40edb516d35aae0dda58739f8daa648550d2a781d9322f685cad86b4dc133f1b39f4b661f41ab21854a225cbdc4b3db3f45c761f25ca79428f81efc6f76924f179f5f64208c2e9e6a40c189317956091ead553abb45d1a0c613bc0c691f9a48ac66ab38c195892cff0c25175b2970810ab01650a3095eead452dede71b155bc50c1162fe8a2aca67d00b7c81a4cc0446d6525cd3d6ea4da530ea15e61420ebd1a4bc0c3ab3a66a4bd1a4af0a3e418d523bf6b24c1ff782e33410d2478c95cca23357bb8ce41358ee07cd011933a0acdea670d23b839cdb6f2dc45bf0b1d1f6a14c1cdad31720ec254938670c0053588e044f5285ba7ef188217a3d4f26d9ba7206b0d21789b377e7387641070950bad2f0f0f912daf48d95463f0fce7670d20b8f943baa5af508d1f78317d1c8597dc686c54c3075eb28a3c217610f56bf4c08b1dd39e03ffe0f3b13578e0d6467d798efe0ebc4ff2d7b93e7fecb8d6812f2556fea1c8f9a6a9460efc157b8f21fca4f848db410d1c78221f85b49023dd6adca069cb3ad5f07899f56ad8c0e9f4183643566ae157a7e4db964fe7a9a685174d93254b9b5938d1cc276f92ee78a3280b37326bc5dbe5dbbdf3a20b1ab1f0b5bd43f4f54fb1013460e17c14ab932f890abcf8420229f0e29c8374e0cd0a345ee10a67a4a245394b5be1c874960929fa8b4a8815a0c10a3707d6410aef1e1e73ce2afca02124e4281d55f82d39e88b1239346f4c2a9c50cb7d11613c5f5950e17cb04a1a73189e39f23885e313293964ba47e9834de19da4cb5c67bf1e79b0145e5aaa0b69aa228597f2f45cc8d1cf7ab246e1895864b5dbf049461285e7bd1d9235191de51e0323d8a20b1aa120bcbb2ea6e6ccba5c3a68a59453d88e06289c641d2264dfe827f6ac4ef35a8f9334f3ce41e5f65852746c3ce1b4af64acfc49245cccd3e8841f29a7f021f6458f5f2a3438e15ae80ed3d6e6686cc2f18f5972e4f46868c2bb0966131fb287d9c74cb895d583a68eee474303c384118307c48006263009198d93b4cf6040e312cec73fd541ce1c96a024625cc2e34443e6b633ca448e9b5425dc4822f9bd2e4c0947aa7e23c3497e406312ae6a76da761b25e175745e1725e6bca69a48f8a1e55053884820e1a58491508f5a2585f8115e24f7ff984c39d2d83ac2f7d0d73c8e36a68fc735c2cf41960d29d46784f7b92ba5c71d5c4461e6716791f1e63dc9c358af082f04092a398eee72611a89f043aa7b54f76977c88108673de6f83dc474eeef1cc2d99088507eb51ed6c410ce673b0fcb3b3afdef42e4b6a22a59ef6521de5539a8ca1e2ca5c8488f107e1c7b5699746d15680cc2f3bb10fe628809c297ae18a5a7c3e88f628158a542522542d6ace63ebe903420fc384b984e9321c337f8072f49769c2b578c4f1de407277fce2629c4e8e9238f3e34652d27ef51976a5e26d197eda5154172f0c1fb38233eaeb2ecc1b1b4f133db512fa0a107d73bb67e992a4d1eb677061a7970ad2622bbe51c68a08107278878184248be71e3d1b883375152d49e790fddb71dbc8be930b2265407ff553ec8418e2a74f0a34d26edb183e409cfc192ad16abd6146f49096b8d918a395a0ece07a13f556d44230e7ed2ec5959b4c39472c0c1ab8fb6159115491de40b2f24805b031a6ff0b2e5ec9d3199774cef063f98921c42da1c297ae6f8c2ab0d7e1ccb79ccae90d35684061bdc482a41c634668af28a69acc1cf1e39b45384d5e05ca808b229d633a6541af2940fb9cbe9a3c1cd39a38f7a449be990c6191c09e571ec2853766c9bc15b7ff140727f06b9b60c7e871c7a90c10fb2cdf585bf1c61b531705fa2219231657116eb115b44424aa798187c390ffad42a0b4356b1769e325612369f11338520f6c126248a60f03587f8ccb3e182b8c00b10f080c617dc54b38f1eede8b56337a0e1053ff9df588748a769ac9d018d2ef8bd1e7c5bce2c122b840b4eff871ea22593c61614abaea9913497d4d8b6df88f513a9a6a105377518bdcef3a65c9366c18f5268cf97333b738ea48105bf2d6a7aceafb973ecb98213ca254be890ade0a5a70eb36c39c8c6a80a6e5db8e49155f0cec1a854f0e353796c6c7aceb1e262eb042bd805d098829fcddd33cdc4b8d88ac1910253321540430aaed445394b9675952e5170c58309e91edd52b98602776b5b25325257e3a7bd91557c825f79c343a8faa8734e2f136838c1ed28933be6b553f9c409349ae06a872d1afdeb3bf630c19199edde90f925f8e9f279f4b14b3494e067ea4ce96147a6393322a09104ff25bb47c1e3be889e41829b7b735839a87d84466dd553bc624decbb3607afacb9e6e3a36104b7d3ed338fa56814c1cff1f44732398a06117c8fe9e3991ccf07991c34a03104ec3e4aa6ee89da808610bc770d5e1ec60cb1262708defc4848fe7cf141f00061ab1b95956c2dab8de84afe33c973c77624971f387296d2c5687e631e4cc3075e0ee222a5b1e84d6147a00b347ae04a38891e476a8e83845790041a3cf0d64224a4740bc933b883c2d42a3ac6cb5536694c7a69e860cbccb8cb2e29bbcce8aa8c14c22b648ec3252eb668e4c0f9d06f1d7ab686988368e0c0b9f671f1ec48534e1b1a3770fdfbc4a3d9102eff84860d1ceb9cd13e6ebeac4f2eb6bc08630b3050701816a05560462dbc943752c59c6bb2f7dc8119b4702d3349aa68698d1a555f80b13366e1a5cdd41dbc86b270227c74f5314962e1a47c9329c33278871558d4a215dbd52255ee362f390e8f36f2fb0a3fb4d0719a438e2bbcfa9446f36d8c291fd30a6f4a63ca9d31acf05346d8a8b461269255f89dee293586c7fc5155781333bf59678af4b1920a673aecd248539f6c54385d696424b8f97d4d4ee106af0991a9d9716a4ce19987ac3158cab210520a3fc8ca07673e299cdad05bb79d517877122dbb32224f8828dc1c07eb21797684c2f758631e73ea008553214a92b3530f72e8f8849f2f8f6f867464966a4ff8416ffed0622abadf9d70343dc6b2c709bfc62b4fb466e4ab78139ea598796e6e4d38e79fae836c33416b68ef50fe03137e0a2657de1ea690f5125e4a0f35f96b7dea534bb8e13dc8c16f07aa15530967d5633bda9794f03ab68fcba1949370726945cf1463dab428093f8c65f9bd031ff1c922e105f7b0963fbe20e1fd47e4d4563fc2e98e93da54d48e2b48331ce17534b39e7e723ca311be79d49683f530a964b88cf03b70e9e8e55e04d9a723781ca33314e18b847ccb895ae78c6724c2c951da76d0b1bb72cc20c2894f95b3d2d9f57bee105e0eeff5ca1355529a0ce107b7f12ab318f3854e219cba0b3174744f88b32c4dcb3be32ea55bac374d06f3c8caa4c283f03b44f1118bc8917546108e66fed01dc2a7e5c01208af62d68890feb2ab2a80f0eb62b68f273a3089fcc1f5c966b13d9ef8c1c9e01fa3f45dfab0923e38a5d1b3797ff0c18fd44335cdc27bfa7f0fde8f45cb79fcdbbf7d3df851a4bf27f67fc5c89107ef3d0e9d2b4bee8c4be3c1eb309447d25cdfc1ad30f93e779ebef1d90e07d9265b07e7435d75103ff2331ddc181ea6e093d2d46673703cba972441e33f2296831fc4b4f153afc9cddc3878722fe629f7a4dc3170f06efb93f4ff779027fa066f2d438ee4a7e330f36ef0ff7d6dc2e5c891c56df063dfc810739633d8e047464ab9af424ed98233d6e0686c984b31c786f7c0196a288b787cb2b5310dbe475f479d91ef230dd1e084b449a288f765ca9fc1cf72c93c58c80c8e7daa9ca9528644580667d663c911aadc543a32f89d345888cb6163f0358568c65c89c1f14054b335fb6c0e5918bcaeebacbee4e69a0383ebae5e31878acb53d517fc943c0a1d741879c1cdd453c9c3aa762fcb8c2e78ee1b62b5fa9f7a79580833b8e0c7305ff9349b2db82eb2c12635a5091e99a10537cc7868a5edde285f169ca9c98e3e87cb2fdb61e1b2aa78cd4e891a29ab37b96e91f20aae45becc71b48c153c559f8f3fa70b39e5ac0a4db47c4aa5c7cac7bbf95f7d67474a05cfd3d7490efb7328dd14bc34bbd9f00831a637430a9e7b581dbb07761f75a4e1408d1a4e9811052755858d51161dcf57a0e047592343d966b7cf3cc1bfd41922d3d638cc70829783ce1f623af72c95ecc0173498d104472c46df768f8a89640613fc38b630591fc7db12aa194bd02b2eb6eab2264e666653c7a8b4fe511382194af0725dfada0cd28c24f4b35951ca2f8404cf2ab8a85a08d6a8b10500dc30e308acc86c744ad6c59667a894657694a24db2314930c308cea44a9875190bb57946115ca9243ed9fb23bfed10c14f512de5b29bc71e3d660cc1b35c29b7577d9c334709c1cda1c3346bbad4586d10bc105d913768464bf00082ef71f420da27e9f3b366fca0ecb2aff77439f5d841d4af48ca9fc2337ce0c7fa383b6bf7356a7c61460ffc9aa88f1d59c87970171566f0c01b9f0fe5993eca5d31e2c18c1d3839fd85e4b4195386ac03bf2ea4106be55353c766e4c00f36513296c77bcc51b89881033f4d4ae49f1ce5067e2c19daba932c821936f02c4c6c6b8e8a149d530ba7430ef28dc84f0bacbcd2eba3a3dc453c347314a29d2db3f0e3e389d948933b458981116cc1820d5938dd414cb5a33f547446868d58381d33425c7f59f48e030bd7facffb7e62c45ce52b7cbbf2f90f993762785ce1755de8988ad8a874e903e3e10182c161c16824128642c1cd7e23006315080010541a0c45721c0792381c7e1480015d1e1428281c1010120e101210080a120a060806060008060606000006080685414276608e65301f113ffb4f02b53e38081a6d1e2e448eefe4e0fb5cc5a118bde5a418ef40a7a3ff30dfdc617d9b34ffd7f073d12f7e1bb37969b31c27467445e227f755a721f8aaf4ed82f77e9665f5bdba1d93731f72fa089045db88560ebb5199a4d834c95ed3e0c3d8e404d1a98cdaa2134f16942e68ba9fba41b2ba300222fd6a06b1c1ca6a896b6ccfdc63360f18c3951a5ac915597f4aedadcef6e00fc63a94a3a3b4606bbf1e2970253b672a21a18e88f40dbbad6a3435a3816bde8308bdcfcb169b680e54db13f5408b1acb24ca84317ee7365b9dbc93794b5579120d7a4ae5240048fbc521b26c423e8706e73d5f85f5ac1c5afa60d3a589fa8152aa14171dd0cfd013d14c91443aa1ce2f0943e1a836035a3a983c7980437517be3861a1f8f79b8c1b1f927fafd06f66e40a472275af5edc8473ad22bf37e0afda5492f7681368876d3003b81786e5f64530fec6c8e0351b39ae5ce166f2c92c2e4f8d4fd982f223b601fc623f544a3904486588ffa2f4609c33ecec7c0752a2af78c9e50cae72365db4ffd4d9d5f034c4951e28cf8a206bc3158b8034ff1d147bd080f7bb7c5215e1bf86aa9ef40ebca6dc46c27dcf35d31a2dfed9e13d1ce94590fa7dea84d510c01c78a5e7e03ea7262b79991f097b16d45de0bc047f6bf565f0d5bd5466c0150d689bd21e2d4b011cf4ce4d925ee8b33a8c9d29b4da52c74554706903e0f8fc281d6b396f1f8c7a4b0b1c7cfe80e15baf1acb1670945e9f36ab648e2ae2c6d954997c2cc80ddee4ee6cb41a4e12ede533a4097a029fa4a4ec773ec05976c55a48b8f09349043b5b4a28a671479ba5b3069e22dc94788a66345e7fb8cad240ac505a4a328a82ff007dccce8d4ab68283ef9412e3b125435a8180fa0a4a56afb162ed4be7443bd5c403962e29574c00cb41c01e849dbb75a9b68cf96f89d63dd92d6eed27aaeca953f48c9963ea2c04db3bbe3d7f183d25a25d32a480d55f84827834a980560455393d3e25adda443cc509046da84f6a044407c91208f7a3f80a52b9c0cca6f2890cdd31822bc35c105bcbce2f53bf4d765625b1393ed2d44b9268cd2bc06ea4e09b5bcd5bac084fed0034fb21abf6115ab06e62168ddbf0feb95e4f7a4ce9cde2074d78a9d21c6d57f7b5cafbd44dfc3f328e2e5b5ee76589391d0fd824bbd2c3b7d43c9e8a2ad048888d77a5745e48aa7f2d7e3a12101ba7a136ec898891e432fcdabccf66ea0f52ae4d4dcc70754345454b8a8052b684ffff4a5f26add98134ee9460064916abf75c718583de2c9ceb02e2b4e15afca12711fe70cba5fb1bc39e9e95e39764a8cbcc907b0dc5c5486c44c9ca42be1102f402ba5b95234f000fe40158a79905ffbc14f40d2525f74f40a85166555322f1f70d2f4c06fb2b339a1b2af326ab74f181a4ebcce5d6e6b32fb9051a7e9cc2341be933e18cdf05087ece950e94975163b55431635214680eb1e45e63900ed1901c956df73349cc845b2a216ab50ef40049e2a0fc6b9bb803e146d5aedb7e4fdc789983c9ed8817ce44849c0ca78be6c68b159b3b8a97609dc37edcd8158226241d48b004d8f039933a65a25fa0236cb4cd74d06c94966ecad561f4a198f8487c8518669a09c76cc6ab824819b89d942325ca44e8dd2f9795174188ccc62a4d2ea26b5656cf15d7b2eae7a0bca35a67ae7e83d26ef945f036f900b1030d8f107ec1700caa943234993983d18a22a49650d76d20897aecbc70e29c154e2578275b2af301cd9257af42bda9126b6a1b51d3cc92457abbf8a38e9985799d8b2d951ba41710f283d174e059d883163a0ba2b624bc6ed34d99ce504e31b2c173a342ffb6f102b49e09585ad229b0cb2bed38bff2f439d37a65995dbae0abbc1048961f62ee362f04b4d213c22eb7ac8b7342784be7ad77a30a41233bf7b46ecb290f83833f9bc307696ea4777580c982a941c4f715fc34589d90f834a4adbccef9fe469290344b86a7dbbea28281f0561a5fbca2408e420ea552337eac8227057a935e8a52d892e0334c9ce596c635652108f790a16cbd412e96f3a9631fa5b76cdcd1d096178a1bca5a55a454cef8f0f51949590312af52fd24695dab7da521e9811855046b01d728180095e577b1c659665fd3208243b8f42903c07959e8901640a990bb89bc9cf2b1b4b0a014182183ea944e1013da0aa376dedf002b4136c0533c1ba5ef98054da57ff75b7a8ba7f0905690241fbebabec72460bbe53b34434e4158a7a4c3bf7172ab95a54de9030704f411f2e3560e193d60d7a90b8a68fe143a8300f0acaa5600cb64c89291f98d8fee99156c19c26cb4a4c86c005c5b4c74286f9e05416ed8adc9b0443dabcd45031a54a44c719b5c5a3f1d93b47208fd96a81c9eeec29d02c5bfb7d31cc52c95b380ce2b6ba652f96cdbd52d64d609064a090f33b5bf85ac07c2019eaee010202013b6c352bb2ada2e5dbe4661697ff80a0678af24fda14cada154ad109d768a34502f1ba57b035dd4df1e3662e40725b987da53ee41685d6fde29cc4b342adc9b43efd6690cfa5860dedef1a4361a948e62504cde6d35ba2d5a49d758aa21ec1439e477e9424faf023dda2409af5aec88c2b99ef188836c7cc52ba3de648ab5b38515a91687abe5cbc924935d37f1407150cd2345a7b90f4f2e6af4e259950ee6a07676a7f2301b1c4df9ebf65ec7e7ad5133889599596d0595e355e213ea6e7bf67101aceae57912a7985c82d11e8475c5e4aac4c3f01d09cb25441a6adb32a478309590a6e8a47d3a1c16a3c61608f78e70e7b7dc2dc45af4983f1c94cccf2669dd08f98f5563319a15505053770e6f11298d706013bcf3c60e3c727feaf918fe988819a1a215ecaf25eecb1cfe29232477c21f97a611945678d33728766099f9a14ab62dd78cc888b346b0709f917598638607ca9305263612d850c308be4401402ec908f0454546af35427ec40a97580069739498b124c803d968a10f1b8a7afa46ab84ddeb2511df72a696ca1b55be0bd0ecd58ec06909711c7b5a46a28d5547ee7cf1daaffc286401a45201e09ad4cec8b8c0513b6d0f097de3e6358420431f80bba358630827fa5459c551b535731f80e59c2cde40c25e39b763f82009b4f293383d5ea81074fda72bbc32106e688a10450914041825764d7fe7f58f2e0081c24e56152e6e8e475250be06c6d984c32752011dbcc593b5fdffbfeeaf99c5022f40395dbe3bec2e11dc014c3c3a2311c13d063a2f0fbad1447b00f87c80490824da5260aa5c85ca450b745158e263cab2ce3196d4056012db99b102a3100c6a3f99e76a64bac0e628ae133b3aa59006bd2d43959391a90cdacc87ac62a02ee4288121dc11159330b63de8a333fc1d77968bb7faa4b0166a25258b4e4310767b7a0ae651928a396b91d6d7048fc6fc437a0ef7237ccd4d4e66b00dd59f29ab01b4c9373985b515a7d989d8d5c8afad9b9b72a3054e9f8e195ca45e01c234ecb5d4994779a860d15374497bdee5f84618095df991e2744ae8a492ae497b846f48d88e4b58ac7ab46dd8912832d97873e79925ef0151a2d5a415c4b0de7a664f6a342d0ee6b501e22044b6513ca105ee3ca45117bee4e33cb01d461e78ed9c4204f99127636cd27ad2995adfe6206925d63354a8ce1679490e0f7895f46a9384f28f8c4a9cea12b48b675492948cd1c96aa7a9bfd9cb71adf730b7d83b4d41c28781d44e55213cfca21db544ed4b01a96fa24c8bc80f16b34abd5dd1b6899832e75082d3261a560aef9aee4651f5a150498e30e4fa30acb318fe21e917892ff3621c4646ad88b2cfa491a410c4a19df7c6091657125e3f49297b7a29809c2f7812f1e64a04c27dc801b6248c043de9bd5094749de19cd6007d97af0837aa29414e5148890be1f44c0550d3c8259770c19462f8b426bc3daa66ec46d72955555e0daf4245bc289eb0c5213e4353d5014c141575761f954003e3d0381c8e60a9a3fbe47607feab92f4dc8c66a6edad89ef52645c7d3f5570a112f49ec6a26e92761f6242c5ff7634567ca4fc4754e64e5dc591501639fd3c03633e8a2c63ede1316ff31e4988257ef77ffbcdf7a1690f58f47c9c42d73bbde0e289a73074cea6149252022552b3fdb1f9229034ea5747a56744e6707b8d03ec9245596b9a45173a2e48c1e24a0e1c0a6c41201584a458099c7f64a85d894fd7c98d51e352810e725f2a163144a8d3750888740a5e9439186063ba8a88209cd34aa2f4e11e91e0eb79f4ddba3e334eb5128b6cc825536940f9114ab0de49509212767c01867d7b918bd325894adfa7fab8669db630a03f7f3ea91182dc083cee7c83b1e644a082ebb1ad0b8ce949f2a433e7986f08c20120b1651fdc53667b0d20fb9a07bd532eae5700d443a401ba89f34502ed9f5477b62a52c0c666ae6daa0a4c659fdca3d28aa184d1994b09d9ed5661c919d488dd708f5cb11803e6132da66803d121633adb0b90039cf3170e0b37e65862bfca93611503c61074734b1535dabe239326746a2902cd32a92a0ad1aa9dab303e5c74bb4b2713989cee1cdaca26c402c1c868eaa0249d7ad4af0d6795e3d3680d09b79dfae20410ca217cda3c1760ff686ac454c08548961c5996541617154f0b87333c72a254de1e097bb386516a314600e041c444a7699478e5b34b5385e3f226ae00461af8b234efbf4b22d1e89710095321397a22a98179e92936eb336ba64d1bd35f7f30d84d07f344595035b30978e9b6c9311cd1f7723ed5346591f1188be49540c62932a08be7228441999a9d7a9075efd56ce0034a8c000928ca8416f91dec4a7433f24562e75db5632be999334f2a6726b557845a773768d8c9b8495dcdd4e474c6913676198968f139478a3f7e4da5a1821e8d25c52de7dfb4358ab744c8e44ed8a2e70a3a749341c53b230937dfde8cade31f4db385b80239c1e65f29b74681c5d8f5e876f113aac87ddaeaf0da31c1ef83695c3253864d4d8329a37741aba91c3e8a664a83f0b7eaf34b87b879b1757469a62410d51059f44711276347a1c5a3c7c7678d2f3a75368ba8c0174a1f834c1dce04c7c4fb5455f438e008cc656046e1ce7ff5c44b86bb6ac6fb3438021ecfa34b8e70ae7718788987121100e52f5b6300cba7ae0f7e0aa7d9326479bd643658ffd181b0c1f01042f5029cdc15d051113fd001a253c10c78b3f10357907dc7407ce136c8ce71052a0370a826f2a24443da59a7a38c2807410f999029f05be30586edd3b7c76dcdb8cf23e5c88df94e1ef26c93f972c6cc04283f90b5cc8f3afe258b0f4f9c19d1791ae707eaf9df318fdbf526fa68cdb9756d9f0ff85c2b923ef549a5ec073d4ad22f2904404ec439f7fb5898e27ac347a2b0b1a1500eb7f6f5635118d18852ac24ebeb50868c9efa0ae0cc5d007a401778f2e11cc6877d98036bf02fec3dfaeb801338993300e53cdb670634730fc83c9e28b1e80f620745123475ea5a4252ff5d807aa8542fb0c6f03bdb04e8525d45e9e91a3ec93c55016a322d8a7f02e8aad2b2be3d708ddd5d779dd63b89385111d5d8367e74d0f44be72d9280c1cc985debc2eced4503d3f7878fcc9b1bd1904df9d9f7eea4bd11db68b4f84df69ec13e23c66896b206517b34ebb6a6faeb6aba287327fb50c167388ca8f4cbb17044f47e952efcd10cb1e9d56c017fc51dc959d88c5bd7fdaf374f10fc9dfa2b812aa11ff6a9280e32e91b4a558c522727a8faee1a7ed56d7f00d2debcbbd3d9f15524c4491ce740c818957da3f17700e6f4d2deb0a37628284503368c8ad4b4a753eae0a4d637eb130700519400d800ea3c0c686825aef012d8b8fd9c8ed0b896918621602d239a05e24b81cbf602b9d0fd0b50000d3c38c7af7b035c6b9d8b56f782f5957386eb766d713d478c5f23e3e2507444eb57a78817369d02eed898104002d7ab2bf811f05f0bf1ada8770030925b691310db3640f63a41718a91e4b5d5a811222daf022e668e0cf15b5f4c1f40f4a14d36c3c7a7cc897444652ea15844500d1bda8c6fdb8c9946f094d1a96906f6970711a21cd58598c5d391bbfa0da0d26207503a85844ef6c57bf8683cc2400b9cb0df71e41fcd2ee2263fe17adaa5ef808f106af4cbb2a01c25b09da9e166c64c1749a1a04edecb6ee4285d50607d1a546040e8f2671d9a63500dc6a8577bdae4e8dbb37371dd292589c2163477b47401eacbb169348e6f2f1d141bc0ca8d7d2aa6246e56a4949c6e45f2ea7a064eb745193fd209a9c2fa0f38a01437e719a862ad0dcf6e347ad359e0685f6c6f138a069fd5dcc685f5d50031d0f9607636e61910b61e9e17ddc846dcc89508ad55a9045e1b13b70a17097c4022216d2102e80c3c17b6772cdd19298762b73ebbade239b2146a8db532eaf6d378ff39acc89550a640a46bf1fc8d04f7105280a6858c3e9100aa50b2f32b205fda164ed2faa9613b8b34b6ac1f1fb26e684c67f17ab955bc686244bd6d2e4124e669db06470e9c2f1a9804ce35a3eb54d1b3137bad0f8cb6d1140f8c235c56955be911004ca25d810472a90df4b6d1d0cc026440b77f83d2da8f0a17e009b6286dfb5d3f32e22bbce92081572c018b5839740794c965b8586434577dd6b4ddb3b36c6a77ef30745dbad89608b3ffd70f06d442ec0da09164087dba36f038c2820d3e6088079a6821e09bbea05c14801a2e787160dd9db28b0e2414990c6affd7ad95ee64fda22f1e1c8d6134e2054b4a7956ac8d46f5bf2813e45ecba3690cc10991684333e92900dd4fc9aa2980a385e63174ff12f998cdb4b39dd3cd4a9aa56d61e93c5113eb3473ec4d9a4c3b55d865d770c1dc69a5b6a8810e91c955d32f3208a86098500cbeb1628c9a93aa33a9b4eb22af1f7e33e77822d73b0fc3059bf9ba5f0f0726fdc4f4b639dd30a748eb8ea9e93e55cc35969d6d4b6c70345c5d16588c8233799272522d2725a20f106f60950a3eac8fc60dacbbf0e0e270a38f44f1820697be78ff8630b20a7386fb94a5ea3c5d11a0cdb9b79a743bf6f7f7de689ce18a6eabc4cfdbc4062011cb263a198ad9f06dc8364a2997469db87f15c8814167893b3c7b649e7c27ec59825a18a57a6c71852b23968563e12e22de09ac620a5db2036cb0f2706a4b579c523cf402a5b41fba1bb72953e281c457604453293ea7001e1b9147763d2a8ec587c2563f7ffb65c8f217c7395b56d648a779bbd6170c0e9b99337bbecd9ac8a82ec1b880eb4b33a5ee01484550b3a2456a02715a5b77c2c3207c6c158a69c578a549a82c495e5aabd192f10566b7a9c85941d39c1c580a6883081eb744b320304d88213f5fe64b0012f0e665dd7d62d9be9c345334a2fa71305ce4e2964071a768b7506f17b670a94ece1ecdf2e17691168d6c3ac277e2347525738196b25356f602a42400e3682b33f497062515a4bf4fed6db51d3933d5f4643451866922918cb67fef684d5022948a3515f854ae1d8e0530e84e7f274041d60005b25faa0f7b9e6137c68ac301c8dd56771048f7a4ab331d60a333053a139c4e21eb48a94dbd80872461441c324a515d0e0fa6c52e5d70e7d7e1ff1bbf41ab3d5b778e0e8c355e323b6fcec0eef53063b366676e23a35971d3510999cd83d27721f01e6ad1bb7b26d37ab5f8826089200e8fbb587294f18deb220b75a1b437dc747e26912ec11934b95e1551ff8b22e060eac8c09ae15d0b4b6be9afc75dda0deaae5e190305338f758410ce49ad9b9b75787b61a42e5dc2cb6a147a351df04e35d38c992bf985dc1cb8e87808ad4e54012de7ba0d2a593f142807ca71dbd6c5706194d3be79c0c6c11e99a2190391dd1970c88e76ed7dba93a5913e8bee33e0b6b32af9ba6f62399a6649ab915e1d6381083dea087d142d8171496ca623808337a0e0ebde307554e19b96ec224d0e9fdbc74834bd0a4a5f8dd8d93582a7634660a9a11252200046da4066c5bd4298b9669ee185d029c790179a43fc08b2ee170beecb6d3f57b19ef4e9e14720867500f83ece013679e5bc5ed613b8cbbbc4d5c9664aa790984b227b2d8df330200f0cc0a16c167a0225d6e1c1bbf3623e19077a6fb8fd20941a0b9309196968477e8c9a12e6cdf7c8ab8498509b2e7baab63fa76c25c4a5cd06b5069e952157203c83654f51f5a2b2ad1a434ed5ba2842e875d78d06dc8f595d91cb28b81a0bdff4da939462c9e045f8e914be8d359d3eed869d42da1694bc64ca56acf6000486c8e844982ca2f8ef43139f6e93d76016772d40177d16d7c457651ae4ae45dd2cd16a30f8b8f2dbf30e942c9011d8a2ae5d2a2dcf237e24e84445b37dd8435f4eeca89af80e7f6c0386824180445586f6ec821d866ac93da463624e075f1772cec40b134eb44786f78a96fff28e09468c81594d4c71260676f3110589e879aa0731a888f3b14428a4d5422d63f4076c15a77c2862b420ab1fae43b6e5f0c2f831379bf3af7fe9748aac1842d262487139e3550d54e1801c63445ce6602a56b4998b828a0c49a5485a24d2fe624e86b88d66051cc1a01b960f1cbdbe3cfdb50290400132a07819de3ae92c500584fa09beff22efd3cc9f6d7db49e5516f0d70b15b4168441806c818284fa3b031d5c5251a70e21e28e4a6ab554bed883d617c2418cab0310a7e6aabe1ff60e9ef71c54cf7c51e99d4ebdd9b48744321d5d1bbaa2ed6e2f6de673a1d97bec65d8abc57b7a749727fb5fa28cdf46bc475dd2fcb79e907491f6b935b90eeae631bb3f9576722ca14b48b7be2ee3aeb1d7bed76e5e5ddd6fba1f5dde7441754de9fa55bacb8751de8913bab1ba82bb94d37b5e0c1d5cc7baf6d3ede5bc2a0b435a4e2bf2eeb0372daf015d36b5bbfc43963ac8d635dcade75d23bacbdf7895d9ab6e48dd1ababf5c776b650691c7e9c2d415ac4b52d74e177a5d64dd8abaca5d8d783dea8ab3dd42e87011d2eeddf4d6f476e9f5f0f2d405ed9aeaf5ddbde744cfe0f9583720d17dada1678d5974ebe93ed4fdd7fdd55d10bbaff990675408ede6f4727a7d7a817a35ebadfbeaafe2a66b9be1facc689bf8c930abca77684f8956c0b8e719c67893cbcf07acd96a53f5706355fe36225188880a23fa090a0983b42efd80622050158e645920a3a3e79efacc0535e24664cb277d86a2b1fef7660e7f141ad76eb950045eb3a80522ad1dc22b8ab5b73c6c58ac4cbf9b9b927bb5045957a867ff01ed2017181711feca5a8f8696be0879c9662ba0fd6f2b3215cbedb600ebce62638facd876494f9e84ed5107e8e6d857f580f4dda4797c5757f5cc8de220280f2e4dda454c90ad1cb649e5d9b0d7d09af97b2e89b99a867dbb64a904208b51fca950279fd0046d09cd38f5ee40d794bdca940b38025a5181532d6c071840baabd90215e000569667e851533f098d91226053424ecc8565812c46ba5997b570300abb2970f659d5a6d528fa2578736588e78d00ed9a2555573bf7ac19bae4736776cebb85ef1df6f67cccd87173c51671ffeb0b2a98163707103cb76c5f70a4a6f42e04897f5d591c2bfc5ede80097d1e43b30ee56f59ff5a4fcab15fdd6cd74f8927b0eea5398ef55fe7d3f44d6bd641fff3c20beecee4ec0903f129e187add46db60bbd1da525e3033eec967aabcf936b6c2e49fa8003dd1574f501219874cedb2632f573b05c430c4b21e2471ff5086a4e74c6934d3f9b8368e66a687fb288eca34891b8448e8e2cc1b5c8e438694800864e62d8bb3d1cdd1b9f2ee83b8a2798a4a75dae39b5d0fdb0f59b374aa7e72ff32137519b284623602b02a7755a09f82ee10b290d1be4ad3d814fa6db0ccc198eda902a1181439a20f48686f5f016d9334c4c375937569b7ffd5523c106436c2fd8c75bb943e57a0c54429001ab84a78230814c789c1732e94046e8c4c017a30399598114fa5581e2126808ea8e405601b80c6418e6bff97423c83457052f37c1effc7dacfedd74108147102fd5fa380bf3ed070668e82ba913204d78e6272d5ffab557d7347489406e20b7e00a411a0532ed422048bfc0af00a0b1a80be0815509bd41ba5ee0270aa55ec546810cbf0722e39a7fd5ffc005aea5af11c90288bc48f03209ebf526023c0f64b247505134a600e5d7a8e7194f18a3d485c363d1b58e491b807a3bfc3748fc143b25120b08c5999247fcef837a74fc149423b1c2485860e00632697ac8ab9eb3c1aa39f64b0b029001c102d902b6026481a4106432498381eb0f049169b06924441db2e7ec6b6ac12881c31a647cae7bb34108826c1f85c3707edc54d8f600ad48d08d505c0632f9a6f40035952023490215d22cd0eb60a01e373c18580c2ebb02610f4d0913f8410135124ab856bbd78c8f1a972a8155098d446776e00d44a7a92750511cb095f42fd205d9ab573352e50bd60dac46cdfbf659e1d2c7807d94fb0ef2360932c11d28838bc08f1235000187ab2ba806eaadba654f65ef868daa2029c195d1347b68a1c87aeaad0b028ab53b972f4074904207f003fc003fc00f20db0889adb5de499429c9f2a95e21e722534a29a59412c3d7751fce38df70a4d134079709030935092849abb65d984491a3fee424a6e2a73fd8564f1746d50dbd3b9fb4051d2acb8569cee2bca8a43f8ee75de8c00597d4c6926c49cadda2f0b4af6ab999afc3166693740853f7512dcc277ff20875afca26b409c0600436669420081f1011392cf01a20e03043072dba55adbba8662db216c654740a22f24255981db3308aee1953e2d9202965094420821a23344af0e8a4ea90c58c1a353c7410e2e181808e5898836753caf4336ad4400974c0c268723ed9548b5f613a59a7962b2a115718bba40e2f4abea9e9a81506b93f71fb16962459b3c268b296ebe8f419f1d32acc419930419892df249d9781d44e154e03e407346e38a02315954e6a54ad96f11e21353c6c9c19666a830e54e8f2d9a6b16919e3a5599f3f5e44ce969119a9e314a652f9920a6d4ba2b63264809c8083c314a610a679b2c80f35cbf9606b187494c220bedddc454cd0771529cc7ae55f41f4543d3474002272a330a6dc29b92d1fd5ea1685d94de9c99f243b140695a49473d4149d3a897fb0257a07288c2e6245895ad3232ffa602b9f30594a1f269dd88e12c4f484a99389df9315d64f8c3ed8d63a612eeba4dd7992d6a5241f6c8513a6b53bef52ea3927c71f6c3a3661ca3b2a558a73fdea8cd584c92efe255325ac8e124a5948d09109f3ff870902e19d0688475a0a3a30619244d733bdbea47b4c973069359ddb36276809e21f7458c224b99f08ed163b23229530a7493a8a50514c5aac3ed8da2861ba50a92f9ea75caaf5c1662be89884d154c7bc18714ad8820e49984e662f947685f3794522615272ca387d79623d27618384593fdd95595ee41e61d0caf124b784913dd20fb6e208636f9a6625a1a4dc973fd8ce4464d89801f27b35403c6c887434c29cbda2ab6ea92ff7920e4698edaea477d30f223b7e4de85884a9c4f1f867e1977c4c14613ad1392de82ceae78e44986cc484e9a65a07718208533a5954f81cb7435cf77176b9adab62a525cf8ea13f091ac27c27ae5fe5523a9e2d84693e9c924d90760721cc5616b4bd9f18b24feb1884c184ba98af5d259bccd621089312ba3a84b0a04018554b892245ff75923f40184b527be6268a8fdd3312aa20082724e4e81f0ab36929c7cd9c5bae5c8c396b3bfc6012dd52a85a1377f4c1e4256a46f498d09d8422aa821a78c4a0830f26f13e42cdc9792fe5eb83bb6f64c8888d8e3d98b34bc9b65e930439793d98af32cb3a85f3141ec22c74e421f5c935b4db1380c1086ac08083e30735424254d0810783ca89a73b18b6949ae06f723c39be7630db6a9f60a5e44fcb50471dd00c2b4bd72a97b36c2ce4f88f2a41caec4e07f3dde75d0e6929f44c47c4d1eb113ae6605e7d0df5bfe35e6b92834909ea5f64b8dd5f34fde360f44ee2cd3e880d07e3096af4fb83e90dc650928e39a69d6e2fd5e106d37d3c21f465bb0de64bef954ac5b4adc36c30fe8d09195f4b0d5f91d0b10663854b265a54e6684b01e149c0c10184ffe85083f9d6f32fc813e75d4f3ad2601a17bdcc4b9ec3554783712c8993cb44cb9a1b9dc1d42d26754bbcd196630683ca9757fff94cee313bca604a2f222da7920e3298540e65c9dc741a135f0d19cce81883297fe7fc8d0e3118467aa83269fd92d8274e4247180c3aad5ce5e9f99ca7c4c376091dde9c86cee9c4afbf600af1966eeef582f9c58210e7e9a4527717ae7ab313b7994dad98756fd17392439570c1a44dc53cb1e537efa28e2d984d7afb8fa6e374bdd81d5a30ed7d527ae6dd3fef25c4fd7cc8143ab280a990915dead21d583049a635cf4787c99620918e2b98326d2d3ea592d74e4ac60b38389a091d5630763895b3b37c324c4a47158c1754c5d7621d543029c9566684c7765653c714f28ef3ba9495d7935f171f1374523ba460cab1f4e7a81b2a5ce88882c953b0f6b8b5143aa060b279cf13f3781e9c196a19e8788271942499605234f931718269b404dd1e4c124ac95213cc3177fceeb3f466e799604e41899730af6309e6a09e942cda2525983a9cb8f87e9a76720a868e249884db074f7e695b4307124c723577e4ee925462551b388e619254924cad9cfc388c61b439658276ecb929f972068e6298b4f4092bb292fbe89318a6f333311fbd3bfe05710cc334b7a2ba392ac9deaa55814318a693175efc4d46048e60184c67c9057d9ae3b10103d31d3c484be62f4ce2e2d45ce9926f2ff4456ec9526d56854b8b7535f2b282f7961ca6ed854910f9568fa6b2534962c3c6c13b0e5e98cf3e872d41d8c6077b17297da5dd84fd521766f99225d989df0825cec59ae95ad1dbce62c75b5792df4f52427333345c98bce5c63ea5a758da988170057de6020e8e5b984ee85293db45877f12c400872d4c7262e1653f6ab8273fd86c80848c88c858ae85b18332d554911686cff6ea9372b8291316c72c4c26e7e027a87d979c9418b42cccd9a4a44b905ac99249e2e00003472c4c9296539694c8115a270fffdc12b4b980031606a54774897657c2c0f10a634813b30f0e5798cbedb35af34c5a61bbdaca96b6654b75cf3ccdeece25439f943870b0c23c6641fbd4662e5c3ee158857176f4a792649978b104324315264d0f4ad250f231482e70a4c2b4ed39c4ce84bac0810aa307cf3ed87c00e21882d2672f23b951c34b1a9ec224dc67c9ab25c94a6599c2ec1dbef2f7e875f1bc14c6b724e50b7d5143059b145bae2eabb4bb751d9e3da51c1e85d1475b4e59beb38e2a0a8385eb9653ff3842612e25fe277f89bb711d14a6ca72594cbcb44bd18280e313be8f4eda4c505d042e9880274c27b7569cf8160c383a61545d51d144ab50ed45e0820924270cfa4d7bd28fe979d2de84f16af446b343cb9e18248980b489c80019b9a109639d0927c73b7b763765c22cfb6796e34149e0c084a95285ef94175fc2f4a6cade74ee78be74b1c16109c3e79874262c8921c408a4122629dbaf99f820b231250c26464428256f1abf4dc2b4b26179b115d44d2a09d348936276e364ad9f6bc9038e48987a2c6c54ec29c9b40477c00109c359b0923fc1843c1765d4a891c8e07884415e520aa9e15102b6906f157038c224267928f996653fe97cb089fc071a39333c6680848c74c88848238c96529ef49dd6f296bc0c3818611e7195f7d361fa10bc8d20c0c0071c8b30474fea55d7ab83072545184b94a45b4f8e2311a68c7867a87aa8572f5372124ed7111019365ec08108639e24fa872d9d43986a4457f024491bc29c946423f3528927d7c85108e37bed5a901bba73bf1c84307a0c7593d5ef20cce339f24e0ce5f971148449a92027ad053d9167b22aa881c7e1088479d4847eccabd97ad00d191f62839b0310a6d8d9d3e7fc273d6a7d8c1c16e4c7c8b1914118b9e4f80356b1ec6a634cbd5bb37cac73107e69a2fe317258c0c1f13132c2c1a17e305f366972beae9bc5f203ac0a93f6492ac928413ebfa930bd68115969c162afa830d8a7480b699652509ec2a0e24f990a52f468690a83dcc53b154f5c44a530e5ae20eaba39290c361f74bcaaae52988cc21cd6a669aa4b4461f435dd925e234a6a4928cc967368adb1d4410a0a63c5d2f1e3efafcaf409835dda2755923c6192c4c4ce897fe9a64e182f48d1b024cf0983c79ccfeb1f59d9df84b9a468116abce25aaf09d3499fb9519e4c18f6b26e9c7c4225318409839e494ae91111a7722e61b293827a7ac5e7e558c2e4392a8ad2719ea25c09838a0aafcb2f254c6fb735df97c4f1741226f5f9fbbe939230a53f490811ad2261bedd5d4be13a90308993d352546d8ab6ce23cc335b7ada29b47a92234caf7b4932370b96fd469894f849a8934c18614e25c9b13bd48a5ece224c175774db3d5bd88b220c625e45ceec449872927e6f225dc562441877f4ca2cff9e68da4318845736c9f7358441e4986c117ee9e52c84494a55c1e5469f1426218c6d3909ba94386f826c10a67c26eaa912cca365451026d94356c6c711e656098439fb870061b6bffe2cffa46256fe80074f1955dd0fe6fa792b15b7b5d3ee83c92e8e0e9bd712223e984e3a0be1fe274c76f760d029087562c57ac57a305f8b1e1d6b1ecc2e7ad4f9a5783099ec15bff6bb8339c7495a449576305556f2ce72296bb93a183fc95972796c74481042c8f3cf5acfc1f84969a8d5fa3442d57230c9a8feea8dd697521ccc696d9f544576551c0e462f9d4bd408bdc130ef234225533d4ae406937dbc29b96b0f0fb5c17019fa3f8657149d840d2651c7732baf3598048f3e7b6aab4e76d4605092da2739c53b7785d3609273eace1f677f5e643498a4554ad94f9413d9f1198c9fcc44d39db1190ca7f38c302fd9affc32984ed465a98eb7e1276430783c75d24236bb4dc6603a7949145d256230e5d35af154faf76c87c1204b67f3e3c67ad082c15c2697d2299a4c4ba25f30765730c94b5e30ca999e57f4e4ffc92e18ed635fb7ecc5ff920b66bf4ae2762ab7600e7d1d4a27cfa99d168ca2738e995fa7f27559308a2ad9e7795b2c9884efe027bbed15cc2ba78458e8c5d69315cc41df8ba59baa6052ded9d3b253d03351c19caaa37988eea8ea4fc1d427e90bbd242779232918ece4caae2ec9841645c1dc37faf7f6de2a28818271ee5a4d499a6ae9f20483bafaebce75ae753bc19c3c44aa5eba09a6607f1f3d9f5bd033138c6a32c593bb4b30d78f4a09a61b694a2979b424985c2d0927f2c63d8a86048392d23e8eeef079bd631877cf4ae82842c6620ca3afa969db8a613e41cd7897f061b4a6c43088948f32ae0dc36c5eda73ca63e8662a0c734e1f2b6997eb9e4a83612a532a0973a6835e1230cc298791a7f3bf308d6c8c7a2f6194ecfb829d7dcffff47b613ae195f37249d0d6f3c218ab5aa12e8db9eabb30fdffeb29f9dd924e7461d86ded24654f2ecc23f37bdbcbc4380f2e4c2a9e3b4e7dd2fa975b18fc527c7f4ecac2a5d8c264e94185c9762d4c6a61f2c930a13e575a18d552f6f924ad5baeb33096bc54a7722a0b9370e287fd598a99672c8cff7ad2c68dde37818559c5da4d5d2e5f613893f2f44f922b18b968af1ea65698479aa5ed9c7e84fbac3097a68a7dc719b39c5518ce7e2df4e4ad68a20a93a47167b25352618e514f4a33541843c9d36bda97baa44f915222fc047db229cc39a6e9f025a514e6901e3474b2f029aea4305f92426d864661364918dd1f11da9e8ac2dca33b4ee8bc1439148651da4de78a7cba101486731925cf7d8a957cc22016432825c7d013a68bdbc104a5c24e184fce29e72f53c1c7424e98e3bd5cbabf8739a54d98d5e4fd88993ea9e49a30c82bdd75c29e09839fbd27956f553909268c71a5f774782e618a919febbea48f53b184397e99c99dfaab84f9bfe4fcaf4b09f39e64e296ba9c525027618e3f5acdd3f98f1825615215524f4c353bc945c298f2a9c4c95d694d3b481854904f9d5a2b9cccee1126d5f93d7ad6aaef758e30e82c3541da9b6c5e5d234cf9f49274ae7e57d531c2e426a7ff1599351b5a84b9b2945cea278a307c8ad7e9de245549893056aa4ef52375748a0883c9a57b74c58a501ec2a0a4943f076d6782aa18c29c93bd5ab6ba10a6de17b724ec57384b7ff1c38330d7985c2e72bfab654198f2a4104a4e6a5a9403613e91f1e649de2f95058429f8772595a42f53d23f982d4992a0e412f4c48ffac1bcf9bb21d45c929df6c124d42939db9412cc523e184f8af638a54a1247b907a3a9f423dea2be45d3f460ce4fb597d3cc83d96d47b6bec9fb56e2c16079754dafab787f7607934ecfad346d3da9991d0c3bea92f4252c8a685607835a4bb24952d492974407c3bb9fa0533acdc1242e49a1bfe29efe921ccc9adb41e9949224d57130557b9f54c2c16469fed2c28434417f83e164f9fc2eba1bcc7e552a4ec5d0d1416d30578d8e2174101b8c57733d622d073fd11a8c9f4b2853f293ba6851832997905d49fec84a953498b37db455caa2c19c4cea6798588207f70c86ef4a65a7543318e4fa49eb6b49977019cce9aa372cc5899592c1fcd94c6754f2b4dc8dc1a0c45f0c79366bb75589c1247f0c154d8979f5255961307dca5652f894625fcad7402f984281c1b836d23a97dcbf605dc6bbbd785ac8c5eaf86c51f289ac4982ffc1565e30499d8332fb924baeb5aa0be6926925593e8f6122545c48aaa7aff821ac2d187dc6e74eb9a985bdb2775da6c6abd7ebc5d3f569a9da56160c2346794ef9ce55410d3c6a5058306a48efe05a5fc154826acddeee18ff90154c67294ec6e560150cead57639c2945052904851c114dac376baf2d3693b3505c35eae79de393fd8a4600e1b65926c4af688b61f6c782021231905c3b5883a412e37c6836c302828982bcd9224c90a5a6e4e912718f54a3a4ff2f555e96bd47082b93dc6a68d58dca86e06d504c3253968cb6ef557ca144131c19cf27a4b92a4be874e9ba09690a4c52a2c2518c3ed7c734dbe888e6f25c1dce7a6e494d57bb70b09c66cebb174c24d10fa3e3b86495909fd1e5c94bc26c518265982bef0dd9e2d86717b33440cd3a9ecbadb6d7ec8304cc2e30459329e1fc46cc230ca49f15db9aea4124f1f6c3ed0609843094af283add3c88d326098e4fb13c424fffc5c930fb682444066e42f8ca162414bd6764b612e41872f4c62e1b405ab245b762fcc76268a67bf796198bdb45f8613afe3a81ecf56124901e2217d1684ec5bd73ed8fcef5806780793a4bc4a76bcdbb8bc201a37928709d00ee6b0aff77bb2223550d7c1b0196332342f1b403a982dcc7f4e594bbe2e3938076349fa4e89e19e1fb3837230d7971c95837e1de360f0a482d041ac2465c3c124a527a9f49c341584928d1a6f307792f44dc5995cd51c037483418f29a57298c90949aa65886d305b3ca9233f55de6ba0e7410d1fd0280107870818816c30aa992c39467e7efa6b0d66d39ef4a8c5a806b34793b55cb2c8fe5b300d064fdafddf4af2d1602a9db486e797eeec9c3318deabeae7e33efea56630088f23f65bff83924f1a60194cdadc73d6de3edd514206939ce3def3d7640ca69cf3c8d28ffd95223198fc3b89bde25fa353270c0659420a06a3b598b0a1bfc42f98dd84aa28fb9f7126e805c3c45ec3c552b06daf14b4546fb6dc74494bd205f3dcbbe9d91dd11d6711b151c3a3900b66ad68d5237fadde1d8d238305885b3076f05379f1839ed3e1c7a1164c999697fb3e6300b39055d66856cc5bbaac7026a79cdf4b59c482c9d34e094ab89c2b18be6409bae72dade2bf080216a040042e98809a26d00a7cb977785d6d886c85d1258daead94c42a2c77a544f9091da9603cf9ea94ec789eaf94e0140c4add892daae45c4b270d11d0484f402998c410b95ee27a2b58ea83cd460d8f432460140c3aacadb3cefbc14603a1602aebf58a71293f07133ec1d8714d977f3d22726494ed009d60fc142cdef67e12bd0f0ac22698ca2413cd32edb4ad432f036482410997bd8ff9f62f7a1f888c849c11912598367dafa42474b76cab1aa804a3f89f244c8c8b93ea43a3c68c1935108660124cea5795df7f1dd038211e6a238804e3a86579f224aaaeab814064bc40051c1cf908448688d9cb4812101a770c63a95c6277d06fd501a146e2c103746424e5658c243744560535f0a8c0196359ed12844a7e128262182fdd9e891a9d2742ec32561084238651de2c98ba4f0dc33c6362c9134bc7eca84e1826614de5557ad4b5985f304c96f2626aa2c5c1c1c131850386490e7d27c99b17edabe417c6fc53dbf9a2993b8dbb3ab116eff255d3f13b7e9e9ccdbb5e1876adde4b12345e183cfdbbdededc85712d49e275094a5d1894d29f4d10a57d827a2f1706d5d034b58f7f4a3e8b0b732c3ff1e27b720b83e7ac7897bee6399dd8c2942eeff5d80515cb502d38d178f9acbaa07a5bb93c7a4f0be3e7f50adb4fa5fe4274b859983edb28397d9770b230f889a6ae73f0b8658a85d1cf47ffc84fbb9d5f5898ce4407b7bbec15461bebca15a6f30da9a2f7d30ac42e2cca8ce5504b277a95b4b2fbb7362b0cb282ca13254fee746815d65636bb9b4ba3f19d756fe1ec842c13cfa20a838c097f429c1c97479c0ae3792a49e7d36e264e438559dcae4b924c7a8a4259123f5318ecb7cc47c7afc4b74b61ce65ee152f67cb49795214b5bb3aee2bd7a76dad658892b398d5a33075da553979432913af1385295dd8863eb19d47edd9830b85c153570afaea2bea754440e1ba5b8a5a49ad4f982fe9bf699fd21f4dda13065b3dbd2b4149272849d7097357b8a09fa4ead239bf5e384e18d46bc7b84ba10f3619c7ecdce881b94d98d2b7a5aa9cdb7388f35d5034613aadfde741492f13c6aaffa417ec04214a4988e1e038210f72983098a5d56c0f51f273b62f61f4b10f4afea44a091a42219c258c9f4227259c9c4feeb815e12a61f8b060a9c3dc892fdaad0a6ae06183a384a9848659c951547616651c25dc24cc2f4ac57512164a9c931cc249c26c52c5933f6ab288ae46b848942b5cca974ebcc141c2a04baf07a5fd5dcdef7b8441eb99de7aadf4f88f239284d59ff8a88db811e5fa12b9ee107b7b59cd3ce11a77a58212de3946982ddde52aed27d494d8224c9e54f80a72564ded451179892faac43bf3126152feaf972daf890afa21c2f425871357f62fa9b97708c355db7fceb69e214cdb77ba5784acfcd10049f60a61ec38d144b124e57ac9470873e6b789bf3519fbfe06612e3be526990ef5d3f69f208c26331fd5e402618e3ab25f8210f5b59d0e0e10a9a552b1512bfb0aed4e69f13c07d3ae87bc0e745043c62d10ee0f6ec8b9bff6d12b91c1f9c1fce147e4ca8c2cf1751f148bb714626e25df16d54a506a3c4e759658f1f8603c133de588aab894f3116e0fa63ad94ecb98d827057170e8800648c88888c78dd343fa29492d0b7342270f57b2bf92d5925f94c6e121edb157d24299279d34031b77874cc35a5ebbb3dae325b19e46bff4ea23221e36446670eee0ec603ab7ec7152086509ae0ee6ea4f82b613db5309277430aa7b8e65793ee751627330b7c94a9e2773474c2707f3f625f18adac5c1249ed2d5c1c1e849e9e93149c91b4c49861026ba17dd3de706838652fa96a349137f5d1bcef8545bb939ab4b66d244ad6851d7e4d860165929cec7d21a8c76764950693f4689abc1a4be453c76c54e17940663ef2549c9374283a99327c9c4efcae549ce603a25bdc74fd946891e3318e647c7d22e6530ec08919e7193c1d41a97b28dc1fc39aec991264a3ea518ccd7e361e6bc8292c63018cdb4fcdb6779972e81c1204dbb2f1844ebe44ef67bc1b027ac855c3bbfbb0b26a5d484e90b71c11cd482103b275b309d64668210134f44470b06b9962a3c56b260d0f9d35b5d8d05a3e737256cc65730fa5bba7072ba24955ac124447a6ead4fd258f2ab60ec92fa79a194b426f6543048b91735da340583b070c19485f2517a2918f6635ce7fb5130eaa8d86f224ab47728983ba885dc18f917fd09269d24bd52b932490e39c1e8490aed67254d304965951e6b4cae799860104ab4a52429f7a02f4b30a7a6aaf6f77c4a3089b85df83709a696d71a1d91f2be1e124c49990996963c865174cc536951725bd08d61b494214edbb675febc18c613f6c4b3b29c180635af1794cacfdaf961182f9a7af219cf345d18c633f5f3e8a76098ff656c642bc030a8feeb906f3a55aafcc214dae49c4bb6a074dabe30a8d0e14e16a1f2a8b917c68bd6a2836b658d14f1c2a4ca42dd785dca268a7661146149ed6249b2a514e9c26c99a7848b622ecce19df359958f38e1c2b4fde14d56ad3a93bb85592dd693203d4e3c6d618e596257b9a96ecd502d4cf5be7ae2b9cc2749b4302729c5ca836cd7d02c4ce13c78ee139dbd735998449ed83f268a85295b49f23142b030456dcf35695ea94a5e6110aad54a1631492eed0a63bf9eacdeba15464be993480baf92c40a838725f511b29d6f5e85b184d01f5a7c76b316d8ba80d905eeea22f0a103101a17280e5e0ea34001021084e402fe22228f6a84d858000046446eb8800001781d9c1b3aa0716420400022218f6e788d101b09880100020000000040000b00000000a080f71a342e20d2ef35683480012b0262e32c20008900808848c24100019c91e31c0508c019395e2384e300001800041210f21e0c0040001e708020d8e005828cf800c477812023323e448e87470252173666a05c24af3123152071615cf53c13363d086dd25b00e1cc4804485b1c206b812023211f22323c3c1290b430c5afe8a973bdb3307d2749127a61f2a7bd6561aa3e0b2ab7eccd95508d19337e06cff8199bb1c8aa64f9bb1cb38166fc8c9df1335013168906c88c54807c050d90198900e90a933039efe53fce2e5e6346fa1839337203d90a0419f11a336e7878242059617259339959b26398f083fbecc69987bf1040641c10f436448a8dd00041111a202332406e241e212dc8558cbcc88c9f61805485d798910890a94840383352011215083222e3c60c0f0f04e4294c996b623fc8c714e67eef20634adaf81db314064f5be2fdb32895a693a41885716c8ac2943a7b10f2f4e85bf364288c9e2dc8bbd7b7fe17144611622354e446e315e4274cd2579f9f245e8ff484493095bf53fc2a41cb101b40f84e9892bc8e4f52d65f7a13274cbda6b43de8b44f3a64426ec2e8a62b3c090fa6e2b7d4c4952aa558a92e7d651bcd36794866c258f964dc73ce0942942270c1041213e664b2c7eedb4a55335ec264f52727597c63d52a6909e3765ae94f59a98451fdc4932ecdfd9f7a29611c4fd13357ee244c954f98d60f3296ed9384e1d35c0a3e62dedeb48c44f2f5ae2297d6522be4b28dd07ba71adad9848439bfcfc4550ba33dc224687a16154a6bfae97070a4238c955f6ed1ac64bb20fe60f3b05163cf04d908f3a8df761125c60873caa8ee9ce453d7cf2fc2f4ab57823a71f74b5a146192f784caa23d904c8449ae54ce9ba1eb732d8c08539e7ac9a3a64b16cfa143983aa7e792f25677bc3b6908e30533adabb2562a540883ca1bdba5122184f94bdb9b0e2ad7c120cc7f5549befdb5fc1c09c270f2e93022fe48e3200361d6bea43b7a98db5c8e0e006170cb5b51a2a585ca655243860e3c465ea4d81f8c6392e82728a52ecc637e309ca04a92f298c925057d1fccf94f2a4916bdd926a7201f4c3a66265f540e22eb0f6a84e8600fe6dd3693c3789204bbfd834d0fa62ccb97041f59e447b4069907a3c55392924eec9c04b58c0fc183c984efb2587ba972971b7807b358e9854e2e27fd441a20891d4c1e4caf6ea7e8880f505a1d4c9220ed9467e9d7ce061d0c7736d25b3cb6ce723f0793a404d9498e92fb602b72304949dd79cebb566a486226838c8371b6f333c7a46839367e4424657088c68d942a483898f20815d3c193f20daaa8979c87a757fecfbd56da04991d0e0e37184bca33f7bf62b6e19465477f967daacc68241b4cd963625e0304366c9c909155410d3c6e906b3065c4c2dd67cc254ba5de56f24dd492d4e239483598d693f89877d134985d3cefa9ac9dd4c8190da60c75722c693c4b797f06c37a9c1394ecde0cc63329995c921cce949c530673573ce9697d2383612ffcd8889534f623442e30432167390663f52561f721db90111b2906d3e7effc24491eb530988338c94b54f23269a604832673c962ddad5adec5ed5a56944eda7112e4174c3254099e37cdf482c13bb848f5f4c92e98db243769bba28dff13a8a0061e2e482e98542c754bc7452fc73fd86cac6dc1a467ba3d94ec95fd93c8d91101c1d4827188c0059d055350c2633cde050ba6a454a509f369547f2eaf60b090a3f27cc765f52aad6036314ae6b3831ac82a982ca6c71331f53962ccc54052a1b94aa55ea72d6259ede5144ca17aefe24af61c264f2998af3ca5d352b28a783ca360ce3115fcb2bb75be100aa69caff760a97c82c9478967f1b2934e30951244454d5213d7441f6c2320890648c88708da07b209869393eeb3133da88f950e24138ceb2979c96df15c82179eabe7243d359e0f360fa3412ac1e8a59f6295ee83d13c904930894b7fba6e3a5b094a9e4622c1bc2545ff1e773fd8f818a61b517aa55f9d52461f6c42e8c17b84241e232033decc18e6fb64d9d1c35a9f30fee0699462a0426c8ab4301a0f6880bc203d80c43056ffc9e1243fdb123e1f6cc330d8a7cf262bba73e3000ac3a43fa4cbfb97efbafec1e6f13e001939323ea42e18d6ac59b433710fb54ad1732895a4e4e2df0829d6c030f8a8784e2a5b481715d1b8210210fcc2e47174cae7259ed2657c726478388d2f6f230830f819274404692f03847d61d269eb63ae27a47fb017869357c4ecaeafa8517961be8ba6d2a9b57117a6feb292e2093a8a757461182594b01667920bd3769f24fb4fb034b9425c187c2feba756d589ad6ec1c69bab5a0adfa51dd75ef25e50137eb0d53822201c1c323e3928e0e07819203666d4401c1c2c40c1a22d0ce22bf7c2dce42446570bc3d5c591abf849848ed1c29cfec4ec5de66fba6916e6184b4a897f0e29a69785399824a8fc21be047142b1306a7ce509164d4ed21f58184c5578b4e4c19368e32b4c9dd4ce6d4cec0a53692dc93636bcdeeeda56186dacc489134dada88f082b4c925ab14f4ae9e0a75d5c853989e96c71848926070668daa80a9368e6a2ae63d2dabfa9309992b257befc537e212acc16a746cfe675d78d33c0536061561d6fb39d366aa77da0298cfd5be779f6a4507e82a530b577f04b625ddf4ea79014e620c65198c3a5d47759944461300bf5ef24e93db92ca130697a0963a269e75853c9fdb48f50e169003ff103f44410b013e911a7656aefe43349c809c3b48285db8ba5ec953a4775f1e0260cdb167af5834c13c6ad784947e4a5f5e83013869d5382dcd88c983086502a7e5749f2323b9730ffecd58e7f32a5e7ab21a3aca025ccf296e3f2f9c70e0faa0496bf4af5c196dc10c112e880064872e3053b05a484d1eca4a9f211da4eca380993b0f8d1c46e64953c3ed0c899718792304d89956a374c3ed8426c00e13d641c1f7c62244c76af5fb515e4724148ce1ed1382322344a900609b369114ff3a21f7e1f3e42b170e9dd6d2e887a59c54fb2ffef9ca90374844168d7b21cd637c274c19364d7297950254698e452be2df33f96b4651186932d5b8a3028d3237af47789308baa8b735751829039228c95e4bcfc5582fcf2d0210cbab492fca8723f416c08a3aa0725cb4e8cebfd0a6174cfa1795a374218439ab60f2dd5208ecfda56595171d30ad71022ae65dcca4f4198468bd82dd11d1716d141c8f1b00b84c1646a494a673f893c68dc08f1a8e108085332d1d3abb5ac05ff60fc2d495e4ada154ae68a7e30e9132e9fe8174fa50941807d30f6de9d9ede356444d33c66dca071c807f3788f2511d332a2f4b80783b0b21cc6b41bf22325b002eac1246fa851f2a8fc1425f6c126e2a1c6031a373e46540598878951a8642c858342d27030168c44025130105236cb0100e312080020601c8ac502c2509ecab9aa071400037044263e3c242220180705428138200a06c3602028100604018140301004851e12b5466aec6dd202d642c7d1043a3303790457422b52b1483765c50ec603e390758149ef1a47ccd51a256ad7a2e30d23725ebd5c390a40dda5cabbb9717f88fa83d8b896bc0928eac4fa97dd102efab30dfb58fa3bded1b9d10e8546d76a118e1c7db2e8731f9f7d1d4d1ccaa4110e4e65f18d10bd829455e32ffae1b30f91a700bc1ca8a529d8343d6a8c7151890a723baa76852b405a0c5136421b3b0652fa88f6dfc192d60c7b43bffdebbab681df7f89f73aea123ce3b6576a6073f0fc6250e4f2cb84ccc00f8aced3ac643766987c360ba84482a1b0f5c1aa89f0bf35e10272e09a962168652212740c44ce96238fbd12658eda8941beef21a08cd2a481450f73a0818bc4417dd0255067cf95fa01007124cea80f88831e7652235a0a50afb1d7e731cf341e044ae71b71316280a05e5c349e3fa2d75c7f7a3f2d8db63885800b15fbad77d2f0671046049a64c10145aa098011d16299e65000abd35f9542496231ea457845d2a067cad9e46a030ccbbbb8ed374887fe5c083bd8fc8e6cc4ebccc5cb17862b9da243a72582a8118a599d8b36ba711a87d89898b4002a655995ca2be404f2a23bdf430e88093b2a0690be06977f38b56e30d0fab96c7ea6cc82c62cff16440f2171544c2d025c61f1925ad00fb72917faa9302493f43b14ee2a6f4e310a7bbcef9458daa0e7863a7c43752e0fe720907224f53870c716e0a3225ffb68efb1cd035717e80f26283493f19f6d3b39b8414abe59f047bc24ef9d6d088edc2de906c7843cdd04319a535bfd982e4459fd26ca2b9b1228e28e41497eeedbaf08a387f5a87a5e51bfa44cb2b9465972c196215d675b0f2e6260dcd11756f37f755e2c86b95d9a01d8102b782d16da29d22b8ae7b4e6f8883ad1f322e26fa57c80bcae8b747476d411b7adac62c233c2619ed943a4b876ea232ad1861471462392ef62494bf68e048d312d54568079b57794281c91f827e3c0a18f157c5b0e1c65d24a58a827e17c1a5378c930ffd84f3238cd837c11b905fcd88eb61f864b6079f339f45faddd987b29f6f8072eaaec4800ddb11689b7ecd78fb5204973113050a121e0fac233b40a106490989d6fff2515f0da8486682c5b705cf471df6279ec4965f206ca8896e32b9c34d589a659f18aa264672d106e0e19b5d3b1c66f6390c4519a8cd6d17f805b2f8677b6a7a8bb647356f9bf3effacac6914789ef01a39d5d775d634ec2c8921437e56501c775ea6f98e4ef902b0adf5ee88f9aa49c5f7010e6b16e6efebe134896b0c5d3c43afdfc09ae7f0588987216b055276cabb102522c103b7badfc9c42befd4e2deef1f42548e55c7de9985927e5a47be46ce1ddece4bd331a29b5a9d035deab7216ddc26c9261fe9920ea204740d41889a950cc5561d63b0070a4f8848e66172d69fb4f4a9ef5ad9a29ac9fb3ac05e14932e6ecae241ff586980da5d5d4a733f2f2a57d089ef538953137aa0883dbbd042f48f1de869728dcb57129f478199153eb1a7fd9760b4d585c6c808a537107d605516310bec2e7388d483190439d129e099ad6b04d5548a42ce1982aba0223972b107efb78e2568732db94ee9bee544b10f31adb6b1f9d8867460ac1bb5c450e40a5985c0c316ff80aa34138a361bfb291b466255a3210c72f296335e37c5073a314e7775f146eaf05ee224dc4393f28a7e8ca114448643bc0083899467574d3303e26854d5aa238a4786530804fb26961674882cf1a3f164063260b83eaa32e434b781cfd15f4a93c3bf8358b63dee87d00e9828574a92a5d539b45c37f24bd7a8c5d683da32b2f45e6859b6bca23221cb529d871ad042bda4cadf28ea4ba5bf07521e0cb0089868bbac1be6d46010ff54b331c81164211d7baa4711a898b9b49a27257f7e4017886c659e9aa19e4f9bc9bdda25c66d0fecde1dd224c7b9eab77faf1fc35dddf7aac41fb3b8102a30b9442c76cb10dd6b614bf1e3e692041d902e9b4e035a1d82665a484b778bd1bbebfb73897f9e8e0f23423eacc2d750314760364849fc49bfe1496c7f5671daec006a8f4b8305a75c00b937b20c78b4a94605cb0ae9324c8529847636b603f7ad512d5e02c681621a52f767082127b737acebb3b5b11fd93d8039027f1a47afda0ee071645304ce4bd77e458ee384159e6b5b587efef9df2066adc1e2932155486c8ab88e3a39f5616307c6ecaa44b573ebc61c2081d89d8428e997d17d016ef43d30b788b71182ab1e471588c1cced97288a4640029a6f51a74a070490db2c59403c751c0aafbe6102aa00dc361b6bdec27aee50f94a7be8695ece5ec5dcb98a80a8e18d4dd63c68bfd2bc9e14d42ff2ceefa39cef1e11084f0f1a99495a9d4c9f412748abb5e2fd1e0f45c9bd0608118b0a4460278b3723903530dd94d965419d70ee3a751e3e04a9e15008f8e51223fd921cd8a55f2ca5702231a0f64920d6b8679e79eb2c9ae29045d1bb37d4905abbafa59cbd9c5b197790ba6fce4a497be58a6a2f7a1cf12591b29226dfb22d392429adb9abc66b59f03ee64fd6d081af2197e63f971f24841804c03b129090293e14dc23bdf2f50bcd29f3e25e29e31f0b9ea46ef955319c7d788e140e97cca1ad466a0785adee2a000653f7b5b09b13463c8368dcefebbb4e761c2941bf36b6dc215e57731fd5c6633297dd722db240eacc4c98632c89e6a23d3e925db23fa3c0d4b176cd1cbc307bd7b22f6081302011e1eed53f45c63cd3d845e220701a9600d2670f78b802e9fc65c602827741b57fa7653382338de1e8f7d485d20a0bcc46f2472c9fa2bebf602c1e04ccc41210268a32699e42e3b906e50bcfba8837ca9235d17958ce868c2615f02ef8e811ae58cd7ddb196f550b521519713a543e5f8a62024e558258d37082aa6938d7e701d01b83d938945a8c84b4d1cd167b00ba7dd9edfe07c50e653f2844167133d42fd970dfacb889930514cc0581bc948ef89fcf0bd66b7968021791b468518ab65870c72a1ba2ce420d3bb897c5a55d275278d0fa7f67e638e742e83e872b2302b4b5f7bbe0a70d290cac1ac56c52d24fe589f3e1d3afcb49127ddae7004546bff062c62ca2dd8885450a3219cfd50fd84e4da6fe0fac56bcf6f9c6bcf64278b465a209adfed5c614a057ededcb7bff2c0088965aa85afcfc4d04273366da26af227c9971d84092bebdbffd14a074121d1ba617910216d1bf8edb6371c10b601340450413381867dc080cc733a50fffefebfafb7f80b51b00fdb7cdd170e0f13237e0679bac452723101dc83234b5bb1196f5165dc2aabc208994c186295fbf700b7d18e255965977a86f0eef1a2fca416265cdc114473f318f992231e0982fc26a1f35c3f8f7f5a43736b3dbddb3d1056673b6da7f83a3b609a38a86d8abb86a566bdda4413ffadaa1d6551aa6a0317812f3fc1c7769a3b8417da10e8a08cad44af99a7a8b45329702d65d234dc41b0d419d5e12c5abbb88b95e9f8161b439610ea18a474f62c40284c81380bcdf8c038a89ae78c34098f036c5047fa69d3dd1b878c53dfe695b5f8864d32d390a5d7ac2f38cb4d087e5984056ea6afcfa9ee0234576a10a784af94d92fdc6f56543da896a18e9d798f36a8506ea22f48f3eb5c22052a76de41527395f18b92e0b41ce1e4f41026061715a73fd0ec0eed3ffc31e1fc66525340bae443a40628a57f349ed51beda8ae2359c5f76249fe3f9f8429858fa18208315a9a298333d2380014232466f68e3d545191ccb7ab62e2a2984d5bc7ea28cf47c366a6218b6e0a8f67ec25ed86fb8b580b2cc49d5d9fa7647a0ad14fdb25e430260a5c28218c6e48745e3c70a16aacb1e4f074503a254cceb27b9cc0fe3cc191a1c72d1771522c51ec0a18d9cd7b6d9991028474346be9c31ccc2cf988bd23c0ee7c4cc1f4f6ab499061df36abf8824cd42b292014f9be97db9a9331eeef10ee40cc8561cfcbe8e4d364cf0cd954ff8d7ef613bb4be68fc195bc2ef2477dc962cc25551bb74b22d0477100727b0964eb70bbc1ac0baae647db86ddc9b4ab7cc4d0e12f150e764f73ab808b767bf438e3a5d42f9fa298dc7abaa9639303301689d189b64235f0eb415965d1ab3d466d6c6d1b07d4baf6075793f83dc77e41e28cfb80885827f8a506d32265c5918d036c3b23798a08f4b8268671e8435a73d7d9b09eaf040dba1459bb5d3375286baa334784e1864f848bcaeaca8607d2d18571c48938125c65eb8dfec2fd33904df9d443641eea5c250f71caddca2882c2824851a965b2751e525cef9efdddc11c54158d7dcf74f83f29fd681d17852d9d57f3948befa84fbec052cfb8109234973ca65d8d9e6bcef4f9854e5803b3aba0f2e8abad37568cc5464bde14ca3c5e76839ae278c151449cf0bb104080756b907292fe9ca1f72a631ad64e02e3066e31aa73d1b795b1c0ac4939a7314e94416dad496d5fb159fe49f9a1e5eb428c573b8aa35b47385f21441503d741872fd1730b2cbe066093f3d511c0dbe264f381361cb6f93e62da6e55c6445320de65a10040c8cfba98f908752c813f049a27450466a0517d21ce714b064cff6992cb05c330ad441f9b7862a904439ac5ecbc22b8e72375c6fe8d7c955391cdb338bff23904a681f7176fc53f05aaa07bf301b23ee5bbae34a9354369c25aafb7a39a7767509f09f5a364eb3ef5918d91ec179b9b594392f67a8fa435de527bc133ad6b489b2cc523252e7101ef287191f1f1b9854e4d2b16034c533418f616e6c3a96d426c1e246a90e70b8edb2bcd8e6e21fba0dbf250ba4d465dc788d6b750c00cbfba7e2477bf29fc19015786a729b9e3058c35d4a87b0236f7c74341f05766b4a6c51b7f990ce6736332bb7fa53b9e2882e5ee4ed3cdd0379cc9711b47fb751849d1f75f8bc1b060da4c1fadce78177c2f5c4daa6ff1c6d23eb9d9aa9b6e71369f96e243fb31702478756c77a7bebb81d6d48d478dcc6f9b44f43f9b8f29626f7d994b2fbfdb7c55d4f231dda56dcb0a04bfae52a949e6222815543d7c8a555e8241b5bb7173f9dadb2bf16c6d359273bda357b1ad603dc39140a0c02de290e0ab967f7d379854037bb712fee010c1b6e6b09d8e791fa9f9cb3bc5e60486070ab8986d4b3c480dde0e139601c63c3db6e80381975661f74cf32d8af7b38a2c15aa16710675ffa66547743fedadcdad5306fd14dd7813f249c0ecd44af802d327629e3f81be6b1e6010e004cf56a4faf4880a5e88267aa3a6143b4023ba0ed8f2d6bae6b3be370c09e5b89816926aa207db24e6c87cc073a506722c5a2d12d164a4fc0b025df68d9a52341d4142cf498c1505cbd386eea446a0dc7d8882e70b3adaac404fb45050d1eafc23d4576b21188fec0d7a55ccfc5c0b11104abb3bef167a6e10db4168d7bcd61d62c2df5106c33f57ce2cd51f9c26a0010e084986ec05be05b01c67d8e24fa75201be42b8ad29d07e5531891819b74cb2e78aa619632a0569c3b08210530dca661705a0b594a0f7b21cf172b7ae737aa68d87521b74265216d15274714d8592723b617e08ac5b7080ff010818583fa4619a6327d301130e484633028ec95fdb9c1e4906da575af185d373bdb36a85b4544b350418102531123f04398b8476dea4aa34d8c916dc5766f20dc60760d6fa2cd0aa41b9241ce69291ae44cb7ff4c2570c5b68a30001c1dd0fa442f211b1033453a6a1d3dde5b2298cc981ef9156de0659c54ab5fe378236de6c201e347b168210166de6508e669563220711a4702d104c0553efbb62bed3c724155ee6b7833365d579124d058ef11269c5f0f9eeee6ee19ed91bd1bd456d8ee55ef52f6673fc3bdc53f5d15d61e617c144c35d3752333791a4327592d30f0bb0da70f402e6e2790a3fbf842fdaf8da4b15611049a6b1f4a10f5446041f13edc03c0ffdb413a5ffde64d99e149fd68d044bc15b2c549f6ca4ebb5a80e1a4f9123eee4d989aaf079c113557af7f72a2af066bb5f58bb64e75792814005e2040221f6560e26ba7f67e8f86d98916a0442bc6a16464423454616b05976c11325b58510740ab5b4e222030173489cd49ebc29db438166d9a4dc29c8dd41a9c909833e22a67e56fdd3ea909ec3134f92edd68f6fa546e32e9803d93be02884671a1bea34854efde78ec6ecbc2487b5e2e6e48ab7731f0463cebb7bb9f6705b7651e911ab10027e22a19c9dfa324bf79f58f9550a965bda60f8fa030a6b25d7066f22df78ab82c01273d5ef08059ac93da83b522fc007601385e234f9207022f1bf4d96ca922b3d0fbf056b0aa5917e05a89962c79944fa505135c0339cb9c0a628096b65abb3fe79304a144fb142a8e0eddbbd63220c0f5420e1245927826e4124f7c2df0c75c9d6bc512680fb4bb91bdb0ef343ca5ff242152ab8f9c9d92af3b9913036e290d36d60ed53254d15ab979b5a2069914a0bc11fae5477c133419a3f144dd01704a3e7394026ec52d8f0dac21633949149988a4313cf04aa59ab45a70e03cbd269034fa25297cd3f4cf0185e94cf02cf8def2d91642c560bb061377f9ac56471eb2e908e0717acf103b1987701ba470dcba4c3147be0a26cc1915c99eefc75186ff1252abec10e15a52c67179ec0a4ba586ea31b0642303260191e6e2d1d19deda9d37e4cacde5ca9526068caaf02b6233613aa1d21b1473d9ce2e5d95ce5fae69681356248550d7eb45ce136b8028cbe1df541cad7953c25b197ed3adf32d3f9dd3442552f7767fc5c25f445c1a7ea7461007a2e3c30d76123307ac379982cd193ec6cb1d2a109ec509f0588a1b70a4fb0def95285a750f55987fd7460a90a46fc30c4dce33ff4b000f0e63bb1298c4681830dcdaf23c8a67db30e028f07db816a5ea9606b94f235838a5aed97c9fd322b9d37e887aa7596c4d79b6c5ef6715f28c44bbc4abfa191fed1d3e4527f0e0ca7865daba8858234f57140e3cb1e754f5c78370c587dff4c6e5d8f2418142ab553573f18979b05f95c489377cfa275a50118d20c6a33bcb519bb26b5e2c495b9a3a6749b6b4f921db042f860729ba77af44107b6a1d676c4a7c99b86565d489f1b302186761f4bbb386773373ad5839b53dc23655a6bb4d20cdce7d4dd7c0ab8e90211b3cc0cf6a2308ebfc83fc0c1e834d6dde20581862b576c5179b4ecb5f6a04a3f784bb73543a44b59408ebdc6a1ac8031710cf5c31171493c14ac4c3f9abb283cadb04a5adafc9bedd363e5fbeba67692caa69abe260c0d791cfa6d37e1055381dec62006db84d9f47421ef5e7268e5f2a85b3cded468531775db7d6c33763add8c7f2b89682527c30070e44b4d54102b476757c037d26d21613222918291bd762089bfe886d8172676723bd8c176f67a69663d59d06d2ca0babc54bcabf570a740b848f8987c959eed4a236bbc73ab7ca7138cd3962e80565749aff806cebe8bb2168c44b8d2d3c1c2c7fe26078956a5e887925ec48289f394a19ad789321abd6205972db2e6144671c9ad757a31db7fd97091db7e875762db7c01b601e4862242eea8ba4d980107701b795616499789361055ea7da88d4b1ff4c0455740b9cb2290e2d6c69268f7596bd5889b86f0b92823b0e43c4955236ef6f26c6dcd4adb9917de652216aa62b551a55af86dab96f4263fed1278db316990c2ffc914b2fe6f424254cfde29d37699582021d6708ff2ab76f21ed6a39b13054b6b3fa60e9ed2b681b050dd4b0af3efaa0ec45a912e549b4b682d289b1df507464d66ff2ba8f2e45e9d3bb6ec63c10fbd82dd9a6e49c945233587e82da87eaaee4490390263344fa1660982dc1959f12574ffde37845983cca07922fc0f75319ab217e664ebdc79b194e02ac958ab816519587e1acca4fecb0bd963aba110cc0a754e3b1413448113ce9cc9dec2b2c9880ab7fb3fc4098a8d445926786c6669651713894f8ba69b0698d636d372b785030a76773c2ba0a11dadacb9cc33f18f76e9134898c88705e007f12d45c3f696de4481a13c4486fcd568b47437371ceaeb5c3bbb211b4f9aaef4c1e3a0f96683ef45aec90f51bf23e2dc2dfb9ed2d2cb7fb80c7520b9dca4032174de30e58bc7f9d9eaf40e869664e87d170c6953c16d12b4758d79324d47c9c34f97aad2aae7eb61c798f9af34c2b96625e0536c5b0931fdceedec387b929fb164a0d5715f16f3d2de3bdd5f7d282223f65dc46e093632d9e08cedb10ed5d3554c263aecdea0540a43a5d83c29314bad4497d3e7f1c1edde729ec51d853d29c15977801fd36064ec15ac8fbbd59c7114f4dde54835c9ecedef3255a250cbbbbcc45151c92a692a04141c321052343bd200faea101ee8c72d9e605146904c45061c7a3acf1d34627048a871c6deba7dd1a8e9801162eb2b0540608481cb0e7bee262d2b89f93e462ce078802405eb63f2695d4edc9d3cd5c02a22b6c7bd95d37407b29e2a113da40679f1304aaa5ca92aec08acdc985f77f232a0b0f9de32c0fed3bb13b859e65913248b6bc2b71c186f5ac48375fd05bf7f45ceece8ca65b4ce327e75264d1d12486b1b6e709726058d8a0068a1107ac1e01a1b6cba60b1ca0054543059e9b5da1ba58686329ec3245355cf99e2d04e27d07f2446e8fab54a0ee41cd453004aae1b66c5d69e7a2a8a9f87dd546f7321d027a2c7aa358d0405fec0eb110622567ce455278607f05c1b032c04f3f1220deb220e2fd5f5552769bcde517a988ce1354fc0425935c8d220ea3c853bef1109994bebc66f2c26c29614bd832f0d890382d35814224e0c173d2e83b37cc3d8c8779e6a272c324ff5297d09e2f234b3f3c256219690a43d4c2d585a94460249e5c0c09d9448e457cdd9921bcef82f63ff8c0c8ca4b410345011c01f28db20a6d39865840299fa90757222b5f0da0331ec443241d43b115a342ef2a6c5c3395ee0b243d2bb52c3b9a82fda19439d382d48894b31eb3a24fa1cd85f42983a9c3dc992a52230a8d91e0e4badd33f5fe7471d01ba4b7ae89b7ee66e220b4288403e51640a34a984a3367834bb2ac8db6bce2341b144808087bf0cc3de51b9b0abb71f75d34f1b124c4a6b5b1c95897b20d828842804c2141cda623d4a2ca134e219d7dfd0f2754e25900a3dd8a30f1fdc87e27f58958cd7af7d8446a7fb3d96ade5e1bcb3f015de47e3d24db8508579347befe9324125afada274e2981d0358d5248a18122e124b615985a6c0eaeb99689270c2626c2868649c5ba7f6f17a0c7438e648f89d0cc57e7463b43a9262f489b0e650b9fa04306300b20632a90e1b6f4e3d69b3a4417cead386a6ad8bac776f04447afe5c4d6868fdaa95000ad5e1ba60af4460e710147baf586fb7a8816adfc98bd6d43932cd16b56e1d04d9d5d8bb032f57e89cca12d3cbf259ce0a8b4e00f8341494a94aeae4915a8f8bf7a202a3ca1291fc211451a6326810a12e25b1e4f9b2b41b44eac41bb016ac6c0d8c19cb8558052632c3892187a008f60b18d9471c356e8082187c6c16af703249cb6ada558ae264398b686694036bfd2e64d93506574dfac010bda8652fa7792b4a1032fa3b77e7db3e9a9a9d665b50ec1491e483b6b93959870cfb8b9a3e2c5d724ab616f69b970a425ac3aba6fe3677558506eae0262bc934d8a1ebde696c56230e5b55ba283e9c44c117a9e745f38e26cf98c39a435e1254db2813ed291394a19e866139934a3623652fc44808d60891689b3d2a35be87ad5ebd891bdf0c03d1bdf495c5604e5461b57736aa4b802a43d9b406ca00adcd9f337f13b8c64a7e58700aece3d08afe38918b9e9bacbcc02e67a94d16abba965ec0c53090747b6e93864c03897375fbb6ed9cce93bf84511dbab07a8cbd75abfdcadd7b0e099b19bf371dfd0a", + "0x3a65787472696e7369635f696e646578": "0x00000000", + "0x3a6772616e6470615f617574686f726974696573": "0x010888dc3417d5058ec4b4503e0c12ea1a0a89be200fe98922423d4334014fa6b0ee0100000000000000d17c2d7823ebf260fd138f2d7e27d114c0145d968b5ff5006125f2414fadae690100000000000000", + "0x3db7a24cfdc9de785974746c14a99df94e7b9012096b41c4eb3aaf947f6ea429": "0x0300", + "0x3f1467a096bcd71a5b6a0c8155e20810308ce9615de0775a82f8a94dc3d285a1": "0x01", + "0x3f1467a096bcd71a5b6a0c8155e208103f2edf3bdf381debe331ab7446addfdc": "0x000064a7b3b6e00d0000000000000000", + "0x3f1467a096bcd71a5b6a0c8155e208104e7b9012096b41c4eb3aaf947f6ea429": "0x0000", + "0x426e15054d267946093858132eb537f105fe52c2045750c3c492ccdcf62e2b9c": "0xd43593c715fdd31c61141abd04a99fd6822c8558854ccde39a5684e7a56da27d", + "0x426e15054d267946093858132eb537f14e7b9012096b41c4eb3aaf947f6ea429": "0x0000", + "0x426e15054d267946093858132eb537f195999521c6c89cd80b677e53ce20f98c": "0xd43593c715fdd31c61141abd04a99fd6822c8558854ccde39a5684e7a56da27d", + "0x426e15054d267946093858132eb537f1a47a9ff5cd5bf4d848a80a0b1a947dc3": "0x00000000000000000000000000000000", + "0x426e15054d267946093858132eb537f1ba7fb8745735dc3be2a2c61a72c39e78": "0x181cbd2d43530a44705ad088af313e18f80b53ef16b36177cd4b77b846f2a5f07c306721211d5404bd9da88e0204360a1a9ab8b87c66c1bc2fcdd37f3c2222cc208eaf04151687736326c9fea17e25fc5287613693c912909cb226aa4794f26a4890b5ab205c6974c9ea841be688864633dc9ca8a357843eeacf2314649965fe22d43593c715fdd31c61141abd04a99fd6822c8558854ccde39a5684e7a56da27de659a7a1628cdd93febc04a4e0646ea20e9f5f0ce097d9a05290d4a9e054df4e", + "0x426e15054d267946093858132eb537f1d0b4a3f7631f0c0e761898fe198211de": "0xe7030000", + "0x4342193e496fab7ec59d615ed0dc55304e7b9012096b41c4eb3aaf947f6ea429": "0x0900", + "0x4a83351006488ef6369cb758091f878c4e7b9012096b41c4eb3aaf947f6ea429": "0x0000", + "0x4ff3897794d496d78686afcfe760a1144e7b9012096b41c4eb3aaf947f6ea429": "0x0000", + "0x5c0d1176a568c1f92944340dbfed9e9c4e7b9012096b41c4eb3aaf947f6ea429": "0x0000", + "0x5c0d1176a568c1f92944340dbfed9e9c530ebca703c85910e7164cb7d1c9e47b": "0xd43593c715fdd31c61141abd04a99fd6822c8558854ccde39a5684e7a56da27d", + "0x5e8a19e3cd1b7c148b33880c479c02814e7b9012096b41c4eb3aaf947f6ea429": "0x0000", + "0x5f27b51b5ec208ee9cb25b55d8728243308ce9615de0775a82f8a94dc3d285a1": "0x01", + "0x5f27b51b5ec208ee9cb25b55d87282434e7b9012096b41c4eb3aaf947f6ea429": "0x0000", + "0x5f3e4907f716ac89b6347d15ececedca0b6a45321efae92aea15e0740ec7afe7": "0x00000000", + "0x5f3e4907f716ac89b6347d15ececedca138e71612491192d68deab7e6f563fe1": "0x02000000", + "0x5f3e4907f716ac89b6347d15ececedca28dccb559b95c40168a1b2696581b5a7": "0x00000000000000000000000000000000", + "0x5f3e4907f716ac89b6347d15ececedca308ce9615de0775a82f8a94dc3d285a1": "0x06", + "0x5f3e4907f716ac89b6347d15ececedca3ed14b45ed20d054f05e37e2542cfe700e5be00fbc2e15b5fe65717dad0447d715f660a0a58411de509b42e6efb8375f562f58a554d5860e": "0x8eaf04151687736326c9fea17e25fc5287613693c912909cb226aa4794f26a48", + "0x5f3e4907f716ac89b6347d15ececedca3ed14b45ed20d054f05e37e2542cfe70e535263148daaf49be5ddb1579b72e84524fc29e78609e3caf42e85aa118ebfe0b0ad404b5bdd25f": "0xd43593c715fdd31c61141abd04a99fd6822c8558854ccde39a5684e7a56da27d", + "0x5f3e4907f716ac89b6347d15ececedca422adb579f1dbf4f3886c5cfa3bb8cc44f9aea1afa791265fae359272badc1cf8eaf04151687736326c9fea17e25fc5287613693c912909cb226aa4794f26a48": "0xfe65717dad0447d715f660a0a58411de509b42e6efb8375f562f58a554d5860e13000064a7b3b6e00d13000064a7b3b6e00d0000", + "0x5f3e4907f716ac89b6347d15ececedca422adb579f1dbf4f3886c5cfa3bb8cc4de1e86a9a8c739864cf3cc5ec2bea59fd43593c715fdd31c61141abd04a99fd6822c8558854ccde39a5684e7a56da27d": "0xbe5ddb1579b72e84524fc29e78609e3caf42e85aa118ebfe0b0ad404b5bdd25f13000064a7b3b6e00d13000064a7b3b6e00d0000", + "0x5f3e4907f716ac89b6347d15ececedca42982b9d6c7acc99faa9094c912372c2b4def25cfda6ef3a000000000e5be00fbc2e15b5fe65717dad0447d715f660a0a58411de509b42e6efb8375f562f58a554d5860e": "0x13d4fe63a7b3b6e00d13d4fe63a7b3b6e00d00", + "0x5f3e4907f716ac89b6347d15ececedca42982b9d6c7acc99faa9094c912372c2b4def25cfda6ef3a00000000e535263148daaf49be5ddb1579b72e84524fc29e78609e3caf42e85aa118ebfe0b0ad404b5bdd25f": "0x13d4fe63a7b3b6e00d13d4fe63a7b3b6e00d00", + "0x5f3e4907f716ac89b6347d15ececedca487df464e44a534ba6b0cbb32407b587": "0x0000000000", + "0x5f3e4907f716ac89b6347d15ececedca4e7b9012096b41c4eb3aaf947f6ea429": "0x0000", + "0x5f3e4907f716ac89b6347d15ececedca5579297f4dfb9609e7e4c2ebab9ce40a": "0x00", + "0x5f3e4907f716ac89b6347d15ececedca666fdcbb473985b3ac933d13f4acff8d": "0x00000000000000000000000000000000", + "0x5f3e4907f716ac89b6347d15ececedca682db92dde20a10d96d00ff0e9e221c0b4def25cfda6ef3a000000000e5be00fbc2e15b5fe65717dad0447d715f660a0a58411de509b42e6efb8375f562f58a554d5860e": "0x0000", + "0x5f3e4907f716ac89b6347d15ececedca682db92dde20a10d96d00ff0e9e221c0b4def25cfda6ef3a00000000e535263148daaf49be5ddb1579b72e84524fc29e78609e3caf42e85aa118ebfe0b0ad404b5bdd25f": "0x0000", + "0x5f3e4907f716ac89b6347d15ececedca6ddc7809c6da9bb6093ee22e0fda4ba8": "0x02000000", + "0x5f3e4907f716ac89b6347d15ececedca88dcde934c658227ee1dfafcd6e169030e5be00fbc2e15b5fe65717dad0447d715f660a0a58411de509b42e6efb8375f562f58a554d5860e": "0x0000", + "0x5f3e4907f716ac89b6347d15ececedca88dcde934c658227ee1dfafcd6e16903e535263148daaf49be5ddb1579b72e84524fc29e78609e3caf42e85aa118ebfe0b0ad404b5bdd25f": "0x0000", + "0x5f3e4907f716ac89b6347d15ececedca8bde0a0ea8864605e3b68ed9cb2da01bb4def25cfda6ef3a000000000e5be00fbc2e15b5fe65717dad0447d715f660a0a58411de509b42e6efb8375f562f58a554d5860e": "0x13d4fe63a7b3b6e00d13d4fe63a7b3b6e00d00", + "0x5f3e4907f716ac89b6347d15ececedca8bde0a0ea8864605e3b68ed9cb2da01bb4def25cfda6ef3a00000000e535263148daaf49be5ddb1579b72e84524fc29e78609e3caf42e85aa118ebfe0b0ad404b5bdd25f": "0x13d4fe63a7b3b6e00d13d4fe63a7b3b6e00d00", + "0x5f3e4907f716ac89b6347d15ececedca9220e172bed316605f73f1ff7b4ade980e5be00fbc2e15b5fe65717dad0447d715f660a0a58411de509b42e6efb8375f562f58a554d5860e": "0x00", + "0x5f3e4907f716ac89b6347d15ececedca9220e172bed316605f73f1ff7b4ade98e535263148daaf49be5ddb1579b72e84524fc29e78609e3caf42e85aa118ebfe0b0ad404b5bdd25f": "0x00", + "0x5f3e4907f716ac89b6347d15ececedcaa141c4fe67c2d11f4a10c6aca7a79a04b4def25cfda6ef3a00000000": "0xa8fdc74e676dc11b0000000000000000", + "0x5f3e4907f716ac89b6347d15ececedcaad811cd65a470ddc5f1d628ff0550982b4def25cfda6ef3a00000000": "0x00000000", + "0x5f3e4907f716ac89b6347d15ececedcab49a2738eeb30896aacb8b3fb46471bd": "0x02000000", + "0x5f3e4907f716ac89b6347d15ececedcac0d39ff577af2cc6b67ac3641fa9c4e7": "0x01000000", + "0x5f3e4907f716ac89b6347d15ececedcac29a0310e1bb45d20cace77ccb62c97d": "0x00e1f505", + "0x5f3e4907f716ac89b6347d15ececedcaea07de2b8f010516dca3f7ef52f7ac5a": "0x040000000000000000", + "0x5f3e4907f716ac89b6347d15ececedcaed441ceb81326c56263efbb60c95c2e4": "0x00000000000000000000000000000000", + "0x5f3e4907f716ac89b6347d15ececedcaf7dad0317324aecae8744b87fc95f2f3": "0x00", + "0x5f9cc45b7a00c5899361e1c6099678dc4e7b9012096b41c4eb3aaf947f6ea429": "0x0400", + "0x5f9cc45b7a00c5899361e1c6099678dc8a2d09463effcc78a22d75b9cb87dffc": "0x0000000000000000", + "0x5f9cc45b7a00c5899361e1c6099678dcd47cb8f5328af743ddfb361e7180e7fcbb1bdbcacd6ac9340000000000000000": "0x00000000", + "0x6441fb391296410bd2f14381bb7494334e7b9012096b41c4eb3aaf947f6ea429": "0x0000", + "0x6786c4cec8d628b6598d7a70ace7acd44e7b9012096b41c4eb3aaf947f6ea429": "0x0000", + "0x682a59d51ab9e48a8c8cc418ff9708d24e7b9012096b41c4eb3aaf947f6ea429": "0x0000", + "0x6c63e84bfc5a0d62149aaab70897685c4ba24bcd9ac206424105f255ae95a355": "0xb104000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", + "0x6c63e84bfc5a0d62149aaab70897685c4e7b9012096b41c4eb3aaf947f6ea429": "0x0000", + "0x7474449cca95dc5d0c00e71735a6d17d4e7b9012096b41c4eb3aaf947f6ea429": "0x0100", + "0x74dd702da46f77d7acf77f5a48d4af7d4e7b9012096b41c4eb3aaf947f6ea429": "0x0000", + "0x74dd702da46f77d7acf77f5a48d4af7d62556a85fcb7c61b2c6c750924846b150e5be00fbc2e15b5fe65717dad0447d715f660a0a58411de509b42e6efb8375f562f58a554d5860e": "0xfe65717dad0447d715f660a0a58411de509b42e6efb8375f562f58a554d5860e01be5ddb1579b72e84524fc29e78609e3caf42e85aa118ebfe0b0ad404b5bdd25f00b304f91831830500622780fd38770500", + "0x74dd702da46f77d7acf77f5a48d4af7d62556a85fcb7c61b2c6c750924846b15e535263148daaf49be5ddb1579b72e84524fc29e78609e3caf42e85aa118ebfe0b0ad404b5bdd25f": "0xbe5ddb1579b72e84524fc29e78609e3caf42e85aa118ebfe0b0ad404b5bdd25f0001fe65717dad0447d715f660a0a58411de509b42e6efb8375f562f58a554d5860eb304f91831830500622780fd38770500", + "0x74dd702da46f77d7acf77f5a48d4af7d7a6dc62e324093ba1331bf49fdb2f24a": "0x02000000", + "0x74dd702da46f77d7acf77f5a48d4af7de5c03730c8f59f00941607850b6633d81fad1867486365c5b304f91831830500": "0x01be5ddb1579b72e84524fc29e78609e3caf42e85aa118ebfe0b0ad404b5bdd25f01fe65717dad0447d715f660a0a58411de509b42e6efb8375f562f58a554d5860e", + "0x7a6d38deaa01cb6e76ee69889f1696272be9a4e88368a2188d2b9100a9f3cd43": "0x00407a10f35a00000000000000000000", + "0x7a6d38deaa01cb6e76ee69889f16962730256ea2c545a3e5e3744665ffb2ed28": "0x00020000", + "0x7a6d38deaa01cb6e76ee69889f1696273f0d64e1907361c689834a9c1cb0fbe0": "0x20000000", + "0x7a6d38deaa01cb6e76ee69889f16962749d67997de33812a1cc37310f765b82e": "0x0080c6a47e8d03000000000000000000", + "0x7a6d38deaa01cb6e76ee69889f1696274e7b9012096b41c4eb3aaf947f6ea429": "0x0300", + "0x7a6d38deaa01cb6e76ee69889f169627ba93302f3b868c50785e6ade45c6a1d8": "0x10000000", + "0x7cda3cfa86b349fdafce4979b197118f4e7b9012096b41c4eb3aaf947f6ea429": "0x0400", + "0x7cda3cfa86b349fdafce4979b197118f71cd3068e6118bfb392b798317f63a8910c174c55fd2c633e659a7a1628cdd93febc04a4e0646ea20e9f5f0ce097d9a05290d4a9e054df4e": "0x04e659a7a1628cdd93febc04a4e0646ea20e9f5f0ce097d9a05290d4a9e054df4e000064a7b3b6e00d000000000000000000000000000000000000000000000000", + "0x7cda3cfa86b349fdafce4979b197118f71cd3068e6118bfb392b798317f63a893e73123ebcdee9161cbd2d43530a44705ad088af313e18f80b53ef16b36177cd4b77b846f2a5f07c": "0x041cbd2d43530a44705ad088af313e18f80b53ef16b36177cd4b77b846f2a5f07c000064a7b3b6e00d000000000000000000000000000000000000000000000000", + "0x7cda3cfa86b349fdafce4979b197118f71cd3068e6118bfb392b798317f63a894f58b588ac077bd5306721211d5404bd9da88e0204360a1a9ab8b87c66c1bc2fcdd37f3c2222cc20": "0x04306721211d5404bd9da88e0204360a1a9ab8b87c66c1bc2fcdd37f3c2222cc20000064a7b3b6e00d000000000000000000000000000000000000000000000000", + "0x7cda3cfa86b349fdafce4979b197118f71cd3068e6118bfb392b798317f63a89518366b5b1bc7c99d43593c715fdd31c61141abd04a99fd6822c8558854ccde39a5684e7a56da27d": "0x04d43593c715fdd31c61141abd04a99fd6822c8558854ccde39a5684e7a56da27d000064a7b3b6e00d000000000000000000000000000000000000000000000000", + "0x7cda3cfa86b349fdafce4979b197118f71cd3068e6118bfb392b798317f63a89a647e755c30521d38eaf04151687736326c9fea17e25fc5287613693c912909cb226aa4794f26a48": "0x048eaf04151687736326c9fea17e25fc5287613693c912909cb226aa4794f26a48000064a7b3b6e00d000000000000000000000000000000000000000000000000", + "0x7cda3cfa86b349fdafce4979b197118f71cd3068e6118bfb392b798317f63a89dd4e3f25f5378a6d90b5ab205c6974c9ea841be688864633dc9ca8a357843eeacf2314649965fe22": "0x0490b5ab205c6974c9ea841be688864633dc9ca8a357843eeacf2314649965fe22000064a7b3b6e00d000000000000000000000000000000000000000000000000", + "0x7cda3cfa86b349fdafce4979b197118fba7fb8745735dc3be2a2c61a72c39e78": "0x181cbd2d43530a44705ad088af313e18f80b53ef16b36177cd4b77b846f2a5f07c000064a7b3b6e00d000000000000000000000000000000000000000000000000306721211d5404bd9da88e0204360a1a9ab8b87c66c1bc2fcdd37f3c2222cc20000064a7b3b6e00d0000000000000000000000000000000000000000000000008eaf04151687736326c9fea17e25fc5287613693c912909cb226aa4794f26a48000064a7b3b6e00d00000000000000000000000000000000000000000000000090b5ab205c6974c9ea841be688864633dc9ca8a357843eeacf2314649965fe22000064a7b3b6e00d000000000000000000000000000000000000000000000000d43593c715fdd31c61141abd04a99fd6822c8558854ccde39a5684e7a56da27d000064a7b3b6e00d000000000000000000000000000000000000000000000000e659a7a1628cdd93febc04a4e0646ea20e9f5f0ce097d9a05290d4a9e054df4e000064a7b3b6e00d000000000000000000000000000000000000000000000000", + "0x89d139e01a5eb2256f222e5fc5dbe6b34e7b9012096b41c4eb3aaf947f6ea429": "0x0000", + "0x913b40454eb582a66ab74c86f6137db94e7b9012096b41c4eb3aaf947f6ea429": "0x0400", + "0xa0eb495036d368196a2b6c51d9d788814e7b9012096b41c4eb3aaf947f6ea429": "0x0000", + "0xa2ce73642c549ae79c14f0a671cf45f94e7b9012096b41c4eb3aaf947f6ea429": "0x0000", + "0xa37f719efab16103103a0c8c2c784ce14e7b9012096b41c4eb3aaf947f6ea429": "0x0000", + "0xa42f90c8b47838c3a5332d85ee9aa5c34e7b9012096b41c4eb3aaf947f6ea429": "0x0100", + "0xa8c65209d47ee80f56b0011e8fd91f504e7b9012096b41c4eb3aaf947f6ea429": "0x0000", + "0xaebd463ed9925c488c112434d61debc04e7b9012096b41c4eb3aaf947f6ea429": "0x0400", + "0xaebd463ed9925c488c112434d61debc0ba7fb8745735dc3be2a2c61a72c39e78": "0x18d43593c715fdd31c61141abd04a99fd6822c8558854ccde39a5684e7a56da27d8eaf04151687736326c9fea17e25fc5287613693c912909cb226aa4794f26a4890b5ab205c6974c9ea841be688864633dc9ca8a357843eeacf2314649965fe22306721211d5404bd9da88e0204360a1a9ab8b87c66c1bc2fcdd37f3c2222cc20e659a7a1628cdd93febc04a4e0646ea20e9f5f0ce097d9a05290d4a9e054df4e1cbd2d43530a44705ad088af313e18f80b53ef16b36177cd4b77b846f2a5f07c", + "0xbd2a529379475088d3e29a918cd478724e7b9012096b41c4eb3aaf947f6ea429": "0x0000", + "0xc2261276cc9d1f8598ea4b6a74b15c2f218f26c73add634897550b4003b26bc632a5935f6edc617ae178fef9eb1e211fbe5ddb1579b72e84524fc29e78609e3caf42e85aa118ebfe0b0ad404b5bdd25f": "0x047374616b696e6720000064a7b3b6e00d000000000000000002", + "0xc2261276cc9d1f8598ea4b6a74b15c2f218f26c73add634897550b4003b26bc66f2e33376834a63c86a195bcf685aebbfe65717dad0447d715f660a0a58411de509b42e6efb8375f562f58a554d5860e": "0x047374616b696e6720000064a7b3b6e00d000000000000000002", + "0xc2261276cc9d1f8598ea4b6a74b15c2f308ce9615de0775a82f8a94dc3d285a1": "0x01", + "0xc2261276cc9d1f8598ea4b6a74b15c2f4e7b9012096b41c4eb3aaf947f6ea429": "0x0000", + "0xc2261276cc9d1f8598ea4b6a74b15c2f57c875e4cff74148e4628f264b974c80": "0x0040fa7f398074858a02000000000000", + "0xcec5070d609dd3497f72bde07fc96ba04c014e6bf8b8c2c011e7290b85696bb30e5be00fbc2e15b5fe65717dad0447d715f660a0a58411de509b42e6efb8375f562f58a554d5860e": "0xd17c2d7823ebf260fd138f2d7e27d114c0145d968b5ff5006125f2414fadae698eaf04151687736326c9fea17e25fc5287613693c912909cb226aa4794f26a488eaf04151687736326c9fea17e25fc5287613693c912909cb226aa4794f26a488eaf04151687736326c9fea17e25fc5287613693c912909cb226aa4794f26a48", + "0xcec5070d609dd3497f72bde07fc96ba04c014e6bf8b8c2c011e7290b85696bb3e535263148daaf49be5ddb1579b72e84524fc29e78609e3caf42e85aa118ebfe0b0ad404b5bdd25f": "0x88dc3417d5058ec4b4503e0c12ea1a0a89be200fe98922423d4334014fa6b0eed43593c715fdd31c61141abd04a99fd6822c8558854ccde39a5684e7a56da27dd43593c715fdd31c61141abd04a99fd6822c8558854ccde39a5684e7a56da27dd43593c715fdd31c61141abd04a99fd6822c8558854ccde39a5684e7a56da27d", + "0xcec5070d609dd3497f72bde07fc96ba04e7b9012096b41c4eb3aaf947f6ea429": "0x0000", + "0xcec5070d609dd3497f72bde07fc96ba0726380404683fc89e8233450c8aa19500e3a507571a62417696d6f6e808eaf04151687736326c9fea17e25fc5287613693c912909cb226aa4794f26a48": "0xfe65717dad0447d715f660a0a58411de509b42e6efb8375f562f58a554d5860e", + "0xcec5070d609dd3497f72bde07fc96ba0726380404683fc89e8233450c8aa19505905fe216cc5924c6772616e80d17c2d7823ebf260fd138f2d7e27d114c0145d968b5ff5006125f2414fadae69": "0xfe65717dad0447d715f660a0a58411de509b42e6efb8375f562f58a554d5860e", + "0xcec5070d609dd3497f72bde07fc96ba0726380404683fc89e8233450c8aa195066b8d48da86b869b6261626580d43593c715fdd31c61141abd04a99fd6822c8558854ccde39a5684e7a56da27d": "0xbe5ddb1579b72e84524fc29e78609e3caf42e85aa118ebfe0b0ad404b5bdd25f", + "0xcec5070d609dd3497f72bde07fc96ba0726380404683fc89e8233450c8aa19509d4a4cfe1c2ef0b961756469808eaf04151687736326c9fea17e25fc5287613693c912909cb226aa4794f26a48": "0xfe65717dad0447d715f660a0a58411de509b42e6efb8375f562f58a554d5860e", + "0xcec5070d609dd3497f72bde07fc96ba0726380404683fc89e8233450c8aa1950c9b0c13125732d276175646980d43593c715fdd31c61141abd04a99fd6822c8558854ccde39a5684e7a56da27d": "0xbe5ddb1579b72e84524fc29e78609e3caf42e85aa118ebfe0b0ad404b5bdd25f", + "0xcec5070d609dd3497f72bde07fc96ba0726380404683fc89e8233450c8aa1950d62c40514b41f31962616265808eaf04151687736326c9fea17e25fc5287613693c912909cb226aa4794f26a48": "0xfe65717dad0447d715f660a0a58411de509b42e6efb8375f562f58a554d5860e", + "0xcec5070d609dd3497f72bde07fc96ba0726380404683fc89e8233450c8aa1950ed43a85541921049696d6f6e80d43593c715fdd31c61141abd04a99fd6822c8558854ccde39a5684e7a56da27d": "0xbe5ddb1579b72e84524fc29e78609e3caf42e85aa118ebfe0b0ad404b5bdd25f", + "0xcec5070d609dd3497f72bde07fc96ba0726380404683fc89e8233450c8aa1950f5537bdb2a1f626b6772616e8088dc3417d5058ec4b4503e0c12ea1a0a89be200fe98922423d4334014fa6b0ee": "0xbe5ddb1579b72e84524fc29e78609e3caf42e85aa118ebfe0b0ad404b5bdd25f", + "0xcec5070d609dd3497f72bde07fc96ba088dcde934c658227ee1dfafcd6e16903": "0x08be5ddb1579b72e84524fc29e78609e3caf42e85aa118ebfe0b0ad404b5bdd25ffe65717dad0447d715f660a0a58411de509b42e6efb8375f562f58a554d5860e", + "0xcec5070d609dd3497f72bde07fc96ba0e0cdd062e6eaf24295ad4ccfc41d4609": "0x08be5ddb1579b72e84524fc29e78609e3caf42e85aa118ebfe0b0ad404b5bdd25f88dc3417d5058ec4b4503e0c12ea1a0a89be200fe98922423d4334014fa6b0eed43593c715fdd31c61141abd04a99fd6822c8558854ccde39a5684e7a56da27dd43593c715fdd31c61141abd04a99fd6822c8558854ccde39a5684e7a56da27dd43593c715fdd31c61141abd04a99fd6822c8558854ccde39a5684e7a56da27dfe65717dad0447d715f660a0a58411de509b42e6efb8375f562f58a554d5860ed17c2d7823ebf260fd138f2d7e27d114c0145d968b5ff5006125f2414fadae698eaf04151687736326c9fea17e25fc5287613693c912909cb226aa4794f26a488eaf04151687736326c9fea17e25fc5287613693c912909cb226aa4794f26a488eaf04151687736326c9fea17e25fc5287613693c912909cb226aa4794f26a48", + "0xd57bce545fb382c34570e5dfbf338f5e4e7b9012096b41c4eb3aaf947f6ea429": "0x0000", + "0xd5c41b52a371aa36c9254ce34324f2a54e7b9012096b41c4eb3aaf947f6ea429": "0x0000", + "0xd5e1a2fa16732ce6906189438c0a82c64e7b9012096b41c4eb3aaf947f6ea429": "0x0000", + "0xd8f314b7f4e6b095f0f8ee4656a448254e7b9012096b41c4eb3aaf947f6ea429": "0x0100", + "0xed25f63942de25ac5253ba64b5eb64d14e7b9012096b41c4eb3aaf947f6ea429": "0x0400", + "0xed25f63942de25ac5253ba64b5eb64d1ba7fb8745735dc3be2a2c61a72c39e78": "0x18d43593c715fdd31c61141abd04a99fd6822c8558854ccde39a5684e7a56da27d8eaf04151687736326c9fea17e25fc5287613693c912909cb226aa4794f26a4890b5ab205c6974c9ea841be688864633dc9ca8a357843eeacf2314649965fe22306721211d5404bd9da88e0204360a1a9ab8b87c66c1bc2fcdd37f3c2222cc20e659a7a1628cdd93febc04a4e0646ea20e9f5f0ce097d9a05290d4a9e054df4e1cbd2d43530a44705ad088af313e18f80b53ef16b36177cd4b77b846f2a5f07c", + "0xede8e4fdc3c8b556f0ce2f77fc2575e34e7b9012096b41c4eb3aaf947f6ea429": "0x0100", + "0xedfb05b766f199ce00df85317e33050e4e7b9012096b41c4eb3aaf947f6ea429": "0x0000", + "0xf0c365c3cf59d671eb72da0e7a4113c44e7b9012096b41c4eb3aaf947f6ea429": "0x0000", + "0xf2794c22e353e9a839f12faab03a911b4e7b9012096b41c4eb3aaf947f6ea429": "0x0100", + "0xf2794c22e353e9a839f12faab03a911b7f17cdfbfa73331856cca0acddd7842e": "0x00000000", + "0xf2794c22e353e9a839f12faab03a911bbdcb0c5143a8617ed38ae3810dd45bc6": "0x00000000", + "0xf2794c22e353e9a839f12faab03a911be2f6cb0456905c189bcb0458f9440f13": "0x00000000", + "0xf5a4963e4efb097983d7a693b0c1ee454e7b9012096b41c4eb3aaf947f6ea429": "0x0100", + "0xfbc9f53700f75f681f234e70fb7241eb4e7b9012096b41c4eb3aaf947f6ea429": "0x0000" + }, + "childrenDefault": {} + } + } +} \ No newline at end of file diff --git a/zombienet/0002-validators-warp-sync/test-validators-warp-sync.toml b/zombienet/0002-validators-warp-sync/test-validators-warp-sync.toml new file mode 100644 index 000000000..dae6e406f --- /dev/null +++ b/zombienet/0002-validators-warp-sync/test-validators-warp-sync.toml @@ -0,0 +1,35 @@ +[settings] +enable_tracing = false + +[relaychain] +default_image = "{{ZOMBIENET_INTEGRATION_TEST_IMAGE}}" +default_command = "substrate" + +chain = "gen-db" +chain_spec_path = "zombienet/0002-validators-warp-sync/chain-spec.json" + + [[relaychain.nodes]] + name = "alice" + validator = true + args = ["--sync warp"] + + [[relaychain.nodes]] + name = "bob" + validator = true + args = ["--sync warp"] + + #we need at least 3 nodes for warp sync + [[relaychain.nodes]] + name = "charlie" + validator = false + db_snapshot="https://storage.googleapis.com/zombienet-db-snaps/substrate/0001-basic-warp-sync/chains-0bb3f0be2ce41b5615b224215bcc8363aa0416a6.tgz" + + [[relaychain.nodes]] + name = "dave" + validator = false + db_snapshot="https://storage.googleapis.com/zombienet-db-snaps/substrate/0001-basic-warp-sync/chains-0bb3f0be2ce41b5615b224215bcc8363aa0416a6.tgz" + + [[relaychain.nodes]] + name = "eve" + validator = false + db_snapshot="https://storage.googleapis.com/zombienet-db-snaps/substrate/0001-basic-warp-sync/chains-0bb3f0be2ce41b5615b224215bcc8363aa0416a6.tgz" diff --git a/zombienet/0002-validators-warp-sync/test-validators-warp-sync.zndsl b/zombienet/0002-validators-warp-sync/test-validators-warp-sync.zndsl new file mode 100644 index 000000000..2110d6373 --- /dev/null +++ b/zombienet/0002-validators-warp-sync/test-validators-warp-sync.zndsl @@ -0,0 +1,43 @@ +Description: Warp sync +Network: ./test-validators-warp-sync.toml +Creds: config + +alice: is up within 30 seconds +bob: is up within 30 seconds +charlie: is up within 30 seconds +dave: is up within 30 seconds +eve: is up within 30 seconds + +alice: reports node_roles is 4 +bob: reports node_roles is 4 +charlie: reports node_roles is 1 +dave: reports node_roles is 1 +eve: reports node_roles is 1 + +alice: reports peers count is at least 4 within 60 seconds +bob: reports peers count is at least 4 within 60 seconds +charlie: reports peers count is at least 4 within 60 seconds +dave: reports peers count is at least 4 within 60 seconds +eve: reports peers count is at least 4 within 60 seconds + +# db snapshot has 12133 blocks +charlie: reports block height is at least 12133 within 60 seconds +dave: reports block height is at least 12133 within 60 seconds +eve: reports block height is at least 12133 within 60 seconds + +alice: log line matches "Warp sync is complete" within 60 seconds +bob: log line matches "Warp sync is complete" within 60 seconds + +# workaround for: https://github.com/paritytech/zombienet/issues/580 +alice: count of log lines containing "Block history download is complete" is 1 within 60 seconds +bob: count of log lines containing "Block history download is complete" is 1 within 60 seconds + +alice: reports block height is at least 12133 within 10 seconds +bob: reports block height is at least 12133 within 10 seconds + +alice: count of log lines containing "error" is 0 within 10 seconds +bob: count of log lines containing "verification failed" is 0 within 10 seconds + +# new block were built +alice: reports block height is at least 12136 within 90 seconds +bob: reports block height is at least 12136 within 90 seconds diff --git a/zombienet/0003-block-building-warp-sync/README.md b/zombienet/0003-block-building-warp-sync/README.md new file mode 100644 index 000000000..311d3550f --- /dev/null +++ b/zombienet/0003-block-building-warp-sync/README.md @@ -0,0 +1,4 @@ +Refer to ../0001-basic-warp-sync/README.md for more details. This test is nearly a clone. We want to warp-sync full nodes in the presence of validators. +0001-basic-warp-sync chainspec (copied) and database are reused in this test. + + diff --git a/zombienet/0003-block-building-warp-sync/chain-spec.json b/zombienet/0003-block-building-warp-sync/chain-spec.json new file mode 100644 index 000000000..8c09e7c7b --- /dev/null +++ b/zombienet/0003-block-building-warp-sync/chain-spec.json @@ -0,0 +1,192 @@ +{ + "name": "Local Testnet", + "id": "local_testnet", + "chainType": "Local", + "bootNodes": [ + "/ip4/127.0.0.1/tcp/30333/p2p/12D3KooWFvMbTsNZ8peGS8dbnRvNDBspstupzwYC9NVwbzGCLtDt" + ], + "telemetryEndpoints": null, + "protocolId": null, + "properties": null, + "forkBlocks": null, + "badBlocks": null, + "lightSyncState": null, + "codeSubstitutes": {}, + "genesis": { + "raw": { + "top": { + "0x074b65e262fcd5bd9c785caf7f42e00a4e7b9012096b41c4eb3aaf947f6ea429": "0x0000", + "0x0e7b504e5df47062be129a8958a7a1271689c014e0a5b9a8ca8aafdff753c41c": "0xe8030000000000000000000000000000", + "0x0e7b504e5df47062be129a8958a7a1274e7b9012096b41c4eb3aaf947f6ea429": "0x0000", + "0x0e7b504e5df47062be129a8958a7a127ecf0c2087a354172a7b5a9a7735fe2ff": "0xc0890100", + "0x0e7b504e5df47062be129a8958a7a127fb88d072992a4a52ce055d9181748f1f": "0x0a000000000000000000000000000000", + "0x0f6738a0ee80c8e74cd2c7417c1e25564e7b9012096b41c4eb3aaf947f6ea429": "0x0000", + "0x1809d78346727a0ef58c0fa03bafa3234e7b9012096b41c4eb3aaf947f6ea429": "0x0000", + "0x1a736d37504c2e3fb73dad160c55b2914e7b9012096b41c4eb3aaf947f6ea429": "0x0000", + "0x1cb6f36e027abb2091cfb5110ab5087f4e7b9012096b41c4eb3aaf947f6ea429": "0x0000", + "0x1cb6f36e027abb2091cfb5110ab5087f5e0621c4869aa60c02be9adcc98a0d1d": "0x08d43593c715fdd31c61141abd04a99fd6822c8558854ccde39a5684e7a56da27d01000000000000008eaf04151687736326c9fea17e25fc5287613693c912909cb226aa4794f26a480100000000000000", + "0x1cb6f36e027abb2091cfb5110ab5087f66e8f035c8adbe7f1547b43c51e6f8a4": "0x00000000", + "0x1cb6f36e027abb2091cfb5110ab5087faacf00b9b41fda7a9268821c2a2b3e4c": "0x08d43593c715fdd31c61141abd04a99fd6822c8558854ccde39a5684e7a56da27d01000000000000008eaf04151687736326c9fea17e25fc5287613693c912909cb226aa4794f26a480100000000000000", + "0x1cb6f36e027abb2091cfb5110ab5087fdc6b171b77304263c292cc3ea5ed31ef": "0x0100000000000000040000000000000001", + "0x2099d7f109d6e535fb000bba623fd4404c014e6bf8b8c2c011e7290b85696bb3": "0x08d43593c715fdd31c61141abd04a99fd6822c8558854ccde39a5684e7a56da27d8eaf04151687736326c9fea17e25fc5287613693c912909cb226aa4794f26a48", + "0x2099d7f109d6e535fb000bba623fd4404e7b9012096b41c4eb3aaf947f6ea429": "0x0000", + "0x2099d7f109d6e535fb000bba623fd4409f99a2ce711f3a31b2fc05604c93f179": "0x08d43593c715fdd31c61141abd04a99fd6822c8558854ccde39a5684e7a56da27d8eaf04151687736326c9fea17e25fc5287613693c912909cb226aa4794f26a48", + "0x267ada16405529c2f7ef2727d71edbde4e7b9012096b41c4eb3aaf947f6ea429": "0x0000", + "0x26aa394eea5630e07c48ae0c9558cef734abf5cb34d6244378cddbf18e849d96": "0x00000000071c0d84db3a00", + "0x26aa394eea5630e07c48ae0c9558cef74e7b9012096b41c4eb3aaf947f6ea429": "0x0000", + "0x26aa394eea5630e07c48ae0c9558cef75684a022a34dd8bfa2baaf44f172b710": "0x01", + "0x26aa394eea5630e07c48ae0c9558cef78a42f33323cb5ced3b44dd825fda9fcc": "0x4545454545454545454545454545454545454545454545454545454545454545", + "0x26aa394eea5630e07c48ae0c9558cef7a44704b568d21667356a5a050c118746b4def25cfda6ef3a00000000": "0x4545454545454545454545454545454545454545454545454545454545454545", + "0x26aa394eea5630e07c48ae0c9558cef7a7fd6c28836b9a28522dc924110cf439": "0x01", + "0x26aa394eea5630e07c48ae0c9558cef7b99d880ec681799c0cf30e8886371da9007cbc1270b5b091758f9c42f5915b3e8ac59e11963af19174d0b94d5d78041c233f55d2e19324665bafdfb62925af2d": "0x000000000000000001000000000000000000a0dec5adc9353600000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", + "0x26aa394eea5630e07c48ae0c9558cef7b99d880ec681799c0cf30e8886371da923a05cabf6d3bde7ca3ef0d11596b5611cbd2d43530a44705ad088af313e18f80b53ef16b36177cd4b77b846f2a5f07c": "0x000000000000000001000000000000000000a0dec5adc9353600000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", + "0x26aa394eea5630e07c48ae0c9558cef7b99d880ec681799c0cf30e8886371da932a5935f6edc617ae178fef9eb1e211fbe5ddb1579b72e84524fc29e78609e3caf42e85aa118ebfe0b0ad404b5bdd25f": "0x000000000300000001000000000000000000a0dec5adc935360000000000000000000000000000000000000000000000000064a7b3b6e00d0000000000000000000064a7b3b6e00d0000000000000000", + "0x26aa394eea5630e07c48ae0c9558cef7b99d880ec681799c0cf30e8886371da94f9aea1afa791265fae359272badc1cf8eaf04151687736326c9fea17e25fc5287613693c912909cb226aa4794f26a48": "0x000000000000000001000000000000000000a0dec5adc9353600000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", + "0x26aa394eea5630e07c48ae0c9558cef7b99d880ec681799c0cf30e8886371da95ecffd7b6c0f78751baa9d281e0bfa3a6d6f646c70792f74727372790000000000000000000000000000000000000000": "0x0000000000000000010000000000000000407a10f35a00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", + "0x26aa394eea5630e07c48ae0c9558cef7b99d880ec681799c0cf30e8886371da96f2e33376834a63c86a195bcf685aebbfe65717dad0447d715f660a0a58411de509b42e6efb8375f562f58a554d5860e": "0x000000000300000001000000000000000000a0dec5adc935360000000000000000000000000000000000000000000000000064a7b3b6e00d0000000000000000000064a7b3b6e00d0000000000000000", + "0x26aa394eea5630e07c48ae0c9558cef7b99d880ec681799c0cf30e8886371da98578796c363c105114787203e4d93ca6101191192fc877c24d725b337120fa3edc63d227bbc92705db1e2cb65f56981a": "0x000000000000000001000000000000000000a0dec5adc9353600000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", + "0x26aa394eea5630e07c48ae0c9558cef7b99d880ec681799c0cf30e8886371da9b0edae20838083f2cde1c4080db8cf8090b5ab205c6974c9ea841be688864633dc9ca8a357843eeacf2314649965fe22": "0x000000000000000001000000000000000000a0dec5adc9353600000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", + "0x26aa394eea5630e07c48ae0c9558cef7b99d880ec681799c0cf30e8886371da9b321d16960ce1d9190b61e2421cc60131e07379407fecc4b89eb7dbd287c2c781cfb1907a96947a3eb18e4f8e7198625": "0x000000000000000001000000000000000000a0dec5adc9353600000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", + "0x26aa394eea5630e07c48ae0c9558cef7b99d880ec681799c0cf30e8886371da9de1e86a9a8c739864cf3cc5ec2bea59fd43593c715fdd31c61141abd04a99fd6822c8558854ccde39a5684e7a56da27d": "0x000000000000000001000000000000000000a0dec5adc9353600000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", + "0x26aa394eea5630e07c48ae0c9558cef7b99d880ec681799c0cf30e8886371da9e5e802737cce3a54b0bc9e3d3e6be26e306721211d5404bd9da88e0204360a1a9ab8b87c66c1bc2fcdd37f3c2222cc20": "0x000000000000000001000000000000000000a0dec5adc9353600000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", + "0x26aa394eea5630e07c48ae0c9558cef7b99d880ec681799c0cf30e8886371da9edeaa42c2163f68084a988529a0e2ec5e659a7a1628cdd93febc04a4e0646ea20e9f5f0ce097d9a05290d4a9e054df4e": "0x000000000000000001000000000000000000a0dec5adc9353600000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", + "0x26aa394eea5630e07c48ae0c9558cef7b99d880ec681799c0cf30e8886371da9f3f619a1c2956443880db9cc9a13d058e860f1b1c7227f7c22602f53f15af80747814dffd839719731ee3bba6edc126c": "0x000000000000000001000000000000000000a0dec5adc9353600000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", + "0x26aa394eea5630e07c48ae0c9558cef7f9cce9c888469bb1a0dceaa129672ef8": "0x3104106e6f6465", + "0x2aeddc77fe58c98d50bd37f1b90840f94e7b9012096b41c4eb3aaf947f6ea429": "0x0000", + "0x2b06af9719ac64d755623cda8ddd9b944e7b9012096b41c4eb3aaf947f6ea429": "0x0000", + "0x2b06af9719ac64d755623cda8ddd9b949f99a2ce711f3a31b2fc05604c93f179": "0x08d43593c715fdd31c61141abd04a99fd6822c8558854ccde39a5684e7a56da27d8eaf04151687736326c9fea17e25fc5287613693c912909cb226aa4794f26a48", + "0x2c5de123c468aef7f3ac2ab3a76f87ce4e7b9012096b41c4eb3aaf947f6ea429": "0x0400", + "0x2f85f1e1378cb2d7b83adbaf0b5869c24e7b9012096b41c4eb3aaf947f6ea429": "0x0100", + "0x2f85f1e1378cb2d7b83adbaf0b5869c298ef7dc060436e4ed803af07632b89b65153cb1f00942ff401000000": "0x601de9615313b00e8cea3ef84e79e50b2fb70e2c8a47cff478b9fe8b3fa8f2db02000000", + "0x2f85f1e1378cb2d7b83adbaf0b5869c298ef7dc060436e4ed803af07632b89b6b4def25cfda6ef3a00000000": "0x601de9615313b00e8cea3ef84e79e50b2fb70e2c8a47cff478b9fe8b3fa8f2db02000000", + "0x2f85f1e1378cb2d7b83adbaf0b5869c2ff3ae12770bea2e48d9bde7385e7a25f": "0x0000000002000000", + "0x3a2d6c9353500637d8f8e3e0fa0bb1c54e7b9012096b41c4eb3aaf947f6ea429": "0x0400", + "0x3a2d6c9353500637d8f8e3e0fa0bb1c5ba7fb8745735dc3be2a2c61a72c39e78": "0x00", + "0x3a636f6465": "0x52bc537646db8e0528b52ffd0058dc2705bea5c66d15541040ac6c3ae971bcf5f1040221a8d1f8b7c17e61bf21cce87411480b10bd8daa5e3c2b65f8c4aa364e5a8ddf27c0313827d06a2d6334e074db9ca7d876ac8d052862b9984530f4d340bda4edddd6e4de5ba624536717e1139e14df2d875c7f7086dc3fa3d398b64f50fffc58fbf4fb73ffb0d39cc2b86761cfe6d3073b9c1fcee7fe611b7f4875f9a7d8cfe208fdf493f4d3b07d9a73ad05c8cf39e5c4dde679c3c8391b9ceec4fd78c824d218e4cfb79cb8be9cb301eace3b7f8a274c182edb72e2e83349855e7e4a2a795d7e27998b94f46d92b42eb543afcbcf5f5c9e5bf2d52dfd9b2f93dc33262710e3fc79b943e09fe2d00f8dd7edc4350b92a692a2facb7adddffddd3d89e9babb7b8e0b9d61050b2b4ddf3efdfdb3f629e9edfe9c18c4f8715bebf42d6f6e3ff74be1b9c0c8b9187cb89c83618e5bce6ebf90cb3918729745244063391ee9710cdb87bfbbbffc6047f275725e904518e7f53cec48efa788c4bd67d104a4eb81534cd2bd67cbeebb075b4c32deee59a43da30fd2d0c979410f3db2fb2972cfcad89d0f822c7ab644bade3b59d6eeddc9925eef8f34e8645980eb0fb2f8d9b2cafddec99273d7bf234bee5995292c3051e1fa97dcb31285eb5f76255cff23de917c3f72c8afaeaeac6ef74e96462e7d70864ed2ef48be2c9ef05d8f1cfa6e67cb79dd0ecdabababab5b225d7f4a72cfa6a53d9bcf739903246f521840fe916b813c65e41c0c50b7b60fe76078bafc61ff4c1bbfac7d3887b3bafc37979f73580490ef389f9f5920c8edc3f3cee7075fdc3e4cdba79f1f9c200db97df896f532c31e008355cff83a7dff7e1791b4e5097b400cb89ef19def24533b4524b567fe2c96c02292fe233d5900f2edfed96d8f34d29c2680f3270934e67054b7a4b7bfceaf95fd879b568e777e395e3aa7139fded3ff484f77779fef3ebbbfdeb6364afae9f74c32bf6dd9ef3de714c839e5df7e908f8c208b30f2fbb388847f8a2354c1e170305cb6dcb3b2de7eb0436e9ff9edc33deb3fd24ed29ef50f602cc7dbfda0ac7df8f683331cdba73fd63efefdb2f699b79f45244e492a38b708a9def9f54e6ba3643efdf9d41acdeb964524d396f3ce3fd247fa2f7380e4736a01f2e59c72bcfd606d9fb266c81ce3fc32bc0d3a198071be3f3f8b27cc0f725b7bc6cf3d2be99da45f916a7e3f38c349d21b7211a2597587be4b05e6ceef17b908d1c4dd21112e95f0ceef777111a289bb43225c2ade9ddfffe2224413778744b8f3fb594c4271b83bff48831d3217995ffa73cff86bfb94f3faf3834ed69ef11719fdf9592481f68cdf6ded19ff91063b9c64df908b10cddca5e2ddf9fd2217219ab83b24c29ddf3fc51346dc9d1fb60fdf7e16934cdc9d7fa48ff4913ed24c72cff839a76dd0789903a49e5d06c80dcee7f629f9a7e59ef1f323cd2903720ee774f91b22454f610414ad21610c79c190160c991a32c6102286c0604808865831440c2057860431c469881042da10f28221370c9961881742e6186245881c426630248b1035848421248e214d42de18b224644a081b42bc08a11212032164081943081842d6102203212e102286902e84782184cb902d845821a44ac8154248304520a449080d3d36e8a9ea99a3478e9e387ad4e8f1d233464f0c7a723d5b7aace849414f153d24e839a247aa678b1e287a9ee889ea71a207891e237a44d053821ea89e247a94e81941cf0a7a5ad0c3821d11f448d1d3448f163c3af070a046c68e1a3b69eca0b173c68e193b65ecc060478c9d17ec84b103c64e979d2f76bcd8e982878947069e251e259e241e2b3c31f0c0c083dbb9dab963c78e9d3776e0d891c1ce971d2e3b2ed8e162678b9da99d1becd86087063b74ecccb133831d3976e2d8a963c76aa7063c1be0b9b293c50e163b2cd859c18e0a76a476aed8b162a78a9da89d12ec3cb143821d26768cd82962c769e7879d1b767cd80162c7033b42ec7460070440ec0072c70e13901b00b9da69dab101481640ba00f102880b807001b20490278094000813407e000204902cb51acc20a61390a51a0d80c850fb52bba376558ba3660710a5da1c409280e06a5635388058a9cda026478d8e9a1bb5aada1b35366a30a86d51aba236454d899a096a4c0059a386a546430d57b3528361e76987089d2f74dcd0a9d25943270d1d2a1d18e88ca123860e1b3a73e880a123874e0c74e0d02943270c9d3674ccd0214387063a32d0a9814e171d2b9d3774d0d0a143470d9d17e8cc40270e1d2f3a67e808a143848e077478d0c9a263834e0d3a34e8e0a0d3019da61f6afc80e3c71a3fc2f8d1821f5dfcd8e207173fbc981ef8f1c40f277e90e047133f98f8b1c4ab04f34b8f0c604288573fbebca26a54d0c8f083aa2af9f0c2c71735197e88a1d3c33787ec8e9009f6434d1b36573a869a0bbc2f3a778473d49e422ec2168453e11630203c1bfce0522347784527869a17542d68984219603d7442c086a8f902e34167294cea425023469df26e50e3a5878c70a926d5259b1d6267c8bebc5eb0d303cc03a1d2384307467490a583a70e74a851eaa0033d8e10bfc070a8568c3db8be78687c74f408e2c545b481cb4a8f14e06c51657851856d8c3bc8bc78c5c047d40f2d6476c83421bb416581cb062f2f2f34646ed42d7c24e164f1ec78a9e15223e6c54b63d4e2c38a48c78c1d95a9068d5a11f54aad04b51d5c68b86010adc61a6ad4a0597aa541a39463031033726aa08901c9445e21ade05841c6602307a9f423f827300d1b19f4d4f1657000825718382da827e8b0886ef4808123021c23708ae809030704384ce02c81c3054709215d709cf8f1c1f900ce062a10383e8852b468d0b2c26962c686991a7048b09303ce08704a504bc306861a326ad8b0d9400d0c6ac0c8990187861a3a74385063464fe12ce1c4e003063ec6f831831e40f4f0c04b8c1f67782ce8d104a8460fa79d257afcd003043fcc905981103764a2e8a1030803d005433e007ab179b201818d10f406a01dd48ece868e0956431743dd42bff4e8402743074387ebac74576a0a2a15758aae06b1aadbc07f60c68c971daf2f558a1e5a3aa577aae9a24bea66a077b8946a9868c8a0a1010d1d3435a061636606345e66e2a091c18c1c346bd0a83143c7cc1c3469ec0411ce40860b4d0b605a68585093aa69a131a25ba2c982e605b52d343e8c1bb0c901f6011808604f3c88784551b9c8ae646dc072f820fe88991bbcc0e88ae888e88c70c9d03dc18aa8319851a3f683c8c60c1a325f646e206303992b19ab191a66b0c850f1a282460c9c20707ac00142e60a192b6049302b3255c8a4e03df03f00d181831d4419d4c6b0517a55895334493b30b41534565c4b2e1ccca99682300d980e5e0d6057604cf306e31a3655231b3e62f08241f5e245c66b0c0eb48c408c3f8c1eb08943868b07e2d5454d1aaf2b5e5b6cdc78a9604c0387089c271928402e5e55d86001af6aba8871d4d0a0070f3634d4d8d1c3071b2b21ce268b8d0d3367d4bc01bba1c68d1f2ea859e387979a18c09a6c7880d95073364b3565d0ab9a19d4b2b091e175831a2b180d3859d4d421b6f17ae2d5c4cb891f37a89101784718c3cb0697198e858f261717ae12b846e02ac2b585eb042e295c53b8927081c015e59a72b5c07584ab0617169716ae14b8b270f5e0f2800b0bd710ae255c4eb884706d7129e17ac2b5838b07d70f2e1b5c50b8a270657171c065848b05ae15b8987055e1bac275832b07d7932b042e205c1f7005e15241cf54cd183364b862a8b99a2943ac439cc3e606110e9106e21be21d40bec0711273220a8068b169c3956433c3aca3c6c4b303073cbccee891c30e0e385c841b7899a1e30510aa1a143555b5276a4e4c2d707ee080033b576021d88961a7069c295810af357464086100bfe0203141600387cc13322600bbc0f100e8458f25c02dc030769a00c1787d51b1709d51b3807140acc1cb8b9a06ce11343000c204a40c58075e65d868a9b1012ba9fe00dba1034137448ba047053369bc6cf0838d1e3df464515bd0566ab8ecf0a0d34563f1d9507bc0116205162a544da22e5195a81e783272a2462a9e8b189731052e3b64553eb8689911bee18901048d57171d2d3836e0d48033838e0e325f006143c68b8f06b5a57a458c8b2ae5aa636c72a5d1e303a21db58a1a11b5281f31e058a92800a2850f18ea0a5e50d4cc21dea0a606231a43b8801101f321b4e25b8833a82a10e5a849c2d10296c533e3470c6a5c5073064e08bc1fbc19bc2b5e0c3d33e8b1a367e81a7a072057b4161b1d76b4b8c2a01903e452bb41cf17206a0021a3cb0244059d53a743d7d4d1d075a0d3d2fdd005d1c305080f3d5d741ce87ce87ae878e880e0d1d2edd079a0cba1fb40874377031016e00c61f3464d0daf0d9d1c7ab010b700d7d0b9a1870d73861b367676e0e14167079e2606e060c101838621e3820ea39de8259a896ea29fe82e1dd55f34091a0a97aa5cfc106fc423417304cd123448e41891e38408845884e834163192603402e4400d23c6464c8e980c649e64909029a207971e60f4e822a7889c12e488409c41e4808885468a460b1a15c4ba88c120e60558450503b442d64566860c8c19ab993b66eae8b1460f1af460237644ac043124782cf168e22143ab8adaa56545cb8bda45eb8b2fe245f042f838818f2d3e50f02178103c11b12b625bc4a44627c629c6a81814b12d3113f810c207123e86a8d9e0b9e1d1e1bd018301ac0cd8182316a31763163eacf0d1021f57c8acd00981ec8a0f86efca1783a884480271095087ea0270872a06a8e585e5b5c38b86d612b5205a4c842c08c908b1a835d42cd586fa058d153eb4f808c2070fad21ea0b5a21086f107e69bd117620ecc1d3a2268697453746774617032058d870c026861aaa9a1bd8d450e3854d520d1c364d3638d4dc5113860dae260e1ba69a2f80cca16307101bec28d145f42cd136340d4056d0589aa973681e3acb9ca3710d03902ee6d5acc1b4c184a3c76a5acdaa49032052736ab66066b164a260b2604e31a5983f4ca989c584629e60e6a609261573cbfcc05cc1b46246318998219843cca46965c6309f260c1357c50215983e3c994d4ea8b820a505a1261098400424f08006a49000144e40c001941820090953082300100091282c38c07979c648c16a072a6a528125539a00b1b0640938fd4914a0254b5abf8127529c003dc1f92b5c548401202551a440292a025a00f94c1cbc0c34bf24d4c40914222946455058f0e0956c3e89895a68e1f556b8a80848a8c7c7c0310f03b7700099c7f1e05c090935712224d48400349c3bb848084a1323a0274b80802cf0018e1d2cc50251a02cc10011900b434ba04cb94651805ac04013141ee70b03ad7003067a22c5c992274da4084d71c1060c64d4048a519426464551a0c870ea6021a1264e8c9c3421328a228500301c2b067212454a94254d9c44918200a3282e4871e2e4f6c0a9019429170345500a80814be2d020041c3a7270e660189c19b01ccc82132953806c70e26022175680628128529c2c8132e50255a0894d06a78a8b9e1035b90010d19215a24079120568ca1228536e91901429284481d2821404581c38780a14168a8880a25c40ca120c00057d80f3063f018ab20294a2282e18357122850a14178680961811499902444404c70d7e026589115011910b4e6870dae0222028465088a4b060e44293252b2c8132e53699f2244a1322127ce0c8802f0065ca25020a2292e28013481c361808c80520171290c213a015566892c20f9c35b828c809100b4ebc7091511420222016a240791c35a240b1800d4e1aec044a5114284446558096604068ca94264b88809e084171c116e4448a90ed06070d6e614913a3264f9a18011d0086734606a00835816204250a14191c3338c88914a10a2c013272c109501520189c3298480a01429e404215686214c5050b2c3172024404e54914284b96d0e880367c2083918403c3ca97cb4820e770b83b919cc66355b1aae694f951efc6c6fb273d1bcff3bc27c99b1b8ffc1beadfc75f7f5d6b77adb39bf963effa7d5d2be576e68ffb03e9f4fad1a6def7791fd013fe987a7b5f777fb4995b74ff9add99b2338b226566ffd8fbbe9deea87760f7d755efcfbbfb1b476ef70aced44a99ba7762f56e96d8ac3a99bac74ce64a65cd72cf9d3f0fdbe9d7ec2f661bfe987ecceca4dde9e73895dbbb72f5cf690794fac7edb4d93f76fa71d3ef73f7bc8fb6536766ca9429534ad9fb28f5f6ef83317b53a64c993fca4e653e66a7cced9476bb575ae5cafef19021428430c8eed4e5a20e7a575abbdd0586cc937a5ead1f738fcc9f3b6598bb5377efd1dd2be0eeb0eab5d6dae3d8b4dbdde79c3e67d3f6f6a6d4bbbf6edaecd46beaf5fc2af5c09d3af5599dddbf70c645db43f7daa42ba5ee2ff6f656bb7fee1574265d57ef4a7b093b8d31b37fd3dd41b052a069482b0dc31aef1a760fc309788712a8719ed3bf5ab97b727fdc13e4eb09e21e0176770a1386ddd3b9bb99fba3f9421aeace94db2140a97baeaf7eeef4f3a8473d66af52eade5fb7fb473fef8ffbc6fb3effe6fc981bf600dab4ddbb2937b37fdd1f7bceee7d2008a34054a820303bbfda1dc0cc3e3bef98b9fb989bb9eb66c7ac6eba772c67ee58ecccdd64ef581d77ce9a5dd771d7755d73d779c71db3983b666618666f6731c73033b35831cc95997aadeedd9f7b8e7b7737a51ed0ee80f6cbddbfe6699e6e8fba7703ba76f7a49501ed7937ad1fe5e9cddd1f7f5fc7d49dba376d4f61d20528c0030e9d7f780218051ad6f41ac7a969202774cb7cddb06eeafd79b57e8e0098ce016a351d6f1a86ec734e9eacf6febeef874c7ba594c62adb7ccc95a73bedd0dd457710e6e476b06d427666a6dd4ca33edd41f77228655a29ad94b6f70ca5b45217a5d4044a5db429f5da5e2b58bfaf6777fb6cfe3e9c0efae3e993d99550e7ea6cc3ce6266af71f7cf6b6776f630c63e67c8ede2768ecd364077e8edce727767d51866f7ca732661febc69f3474343536b7dbd5e73ce5adbbd84f6f6aa57efd09d046feaeefe7277eef9f5d7947e1f87cedeee4dab778314ac2085e9eee6ea9f37280382ec3ed929ad15a4947e1ff7d7d49d7d4e0ec3574e873e42483d44e2341e76bb7bddcdeeeeecfc7ddc55669e9f659cf979e8ec79dd4eabd7dab47af5efebaed56937add4a35e53da3da7cf39270c4c7be73c3bca6af72e403b28d33dfba360d7fc79d8dd9336a594b67f4e00dbc7edec3d99bbdd3fff3e76e6767667766f6766a6a0e73ebdbda977f7f4cf999d5266fed89ddb9d32333365fe98bfef6b76ff989d7a5f6f7766767777fed8bddbbbbf23dff7cd6aedafcee97d3319113c99effbbcef930d4000edc950af7e9fabbdfdfbbeefebfebe98f77dfc7d9ff3f77decf4fb78ec9a366d9036edcfdbddbd73ea7ded1fcf39448a2153380f6e0c10496901a805cf8805a448219a0242009e0011013d2192b2c427841e20284446207c0043d323a5a60b72d2a402424252402852d3c3470f1a1e464550aa00dd742d48e981620120a22996031b98284053a634b9f10b00450122ea0e5a806214c50522cb81058c5ce8c14115201696184581423465899322282ddc0cf1e95145ca058c7c3461a1c8475013274e80827c42e8e1c14188f40459d20314d444a8024b5688028585a2262cbc102128413c00f8800807690244e4032868895115a0283a3e3e807922048505274b8c845c686201a027b4ce88a88951d1073d1cfcac00a5888c7190220c00052d710245a8022e4c99111575d0e29ea22220a2292d4871b28408888a142740444b9a143d7102b42448904f083d539c0015fdb0587062d4a4024b58288212058a05661f60c002508880a870608980a200adb00400414208a1e709d0122946533040e4a4039610101520a125559ab480035b418ad09215a240596105294234b3051f0c446401275284849adcc054017a12054acc94169a181501b500c5e87b449102c5c84993a2284d888c96184169a1022e14d1584f80808a96b420e5499426443606c8a88951d1922022291730028a92032bf2596116821150d19227519a2c01226a32a5052943c62640424e9600add084488a51d112a1264ea4088140250a90b338c8922852a014110151594224c5a8c91328424da82c2982b2240a94262c38b18005a42c31aa02c401952840fe03aba3f986d44cf805a466c28dc462c247472c9009b7c20e09e9e8e8ab48ee47484820528b09231d1dbd8e8e68bb33399a4847474794c9d13cbacddb64f2c27c611e1d1d1d1d3192787474243261a48f8f908e909c9190e6d1d16482348f909ac96dde6ecde485f9c20bcd848f90909cc9d13c3a6a2647f36832e1a3a3a323a48e1e1d21cda3598f26d264723499091f1d39133e426a267c74c448481f13a47974e4313942ea98204d24a46682349991281346426a268c8484e44c9026d264c248938905bc342633cf168a9a70ff6aba2dbecb9bc2179ba6703ceccdfb193b61ec6d8ad3fa61f6369d5af6e654377762d95b3bc9d8a6622aae72ea625e8cbd219102ec61fec6fa1bd3105beb6f48a4d0bd3775fb6f48a450bfeb5c0ec6de580fb373b4b709e5b4fe97bd4d97bd7993686fdd14da9ec24d2876c109ea68a220cbde9296425bcbde904881f5f46f93eaf6330db1517b63d949e5b47efa9ebdcd29a7f577f6d6d5766e7a36a8a2065a90800530cca9a9c58c21d4f882082b9f3dea2b9a5002872dbef460a37656517915a572a2534d533447713977a250de975e0555e5509eedb450755fba13d5955367bb266fa25ff6d477757507185faca087266a3ba6cecd37bc08c30c20a4c2b0b1cdbf6cdced8f8b1dbc7842862cdcb0b1cd6d87735a7f39ab6eff1cc138bfec70b7bba9cb1c1c59a4d933361c31c711671c31c611611cc165882e59dc0c8c9c1b4288db0f5ece0d41c5104fcc1a30bbb3b395796543826b58f0c86e3ffffc59fbcc3075fb6f74daa76fffecd86d16fe36cee5c961ce39d9c66a98c3873984d395f5cc2d371260cf8668c26947f0b30d091c10081bb60640bafc2fc840c4b644c4d6cf362bf3cad61609b64773ea809492486310fd0f6e89d41d5dfa25eb006cc6ed2c11b57b26eb7724253b57a2ca562d8eb568a25fb228f9c105278b10f8e01e4da4ae9b0b281f40e625e61c9ad77fde00f293f00738346f3f0f5e6ef8dd3759346f7f8b25f065d9a0f9ac269a5cebdea8de21bebae0b73842f8ad16d87db348ce817ed8e586cf7a10ecc26e3dab45253409ebc33fd2247c0ffe24c16791e1bba8a4fe9c96b936cbb6936bf3590f9265b3a8d43be477e08c269237db8dc9f47f5c3a9f7970bade7f28a8b003166eabf159fbd878dde7cc98b0204667f3d1a38ddbe9b44fe9fd8c3baeef25842a30699f8ea54c689f79bbe7d1c274bb1fd2519edb3117299f9a5bf219b7ab3fe57edffc46deed07694efb74443d45254beef7f5fbe6cf32ebb00559a2094852782dcffb727edf64b51fe99125d2f5823c31c967bfff6cf98267cb79bb497f70762bf16f25d3b34ae677f737e00e425c8fec260cb7accfb9197c18c158fb78df3d1549a85fbe709564c270eb83dcb8fbbd47d68f2cbde73fe21e0c777e7b55f502d6fe793dffd83ee2f3c7daa7f5cc7ff339ed539f7fd63ede23dd16297e25914010fc231d9236f559396cb9f4451dbc07b95cfa1e07ceb8f441fa208834c367b1c00727f8437ec1076b908d92ef3bfab93e259feb03bf3ec86a3d5f56cb8e509ff5ac1db25cfaaddf21e9d2a0eebf0fbf92f3b25c1ca1d56ab56c50f725fd22a4138a906ef841de9f40ffb3a5f7a165f165d52e6431b4e5ed86ef912fd27b89394839935ebfc41cc4fb693be2210976a147fabf48b6555209cb06894f5f7c16910c896314df3df03d5b8a0f5a1bf0bfef94d017ad0d6895d4f7be821ed9fa2fa8d64f49fd6a12fa2c5b7a0f3e089265f836ace70bbe47dab0ec08dd830f5abe1c98e3d2071fecd06b3d68cb794befc3073d6c91e197f386df8d002601c1f767d9a179c57a7ffaa02d5b1f92e0bc1e396f8be43b2ff3ed8618b9fcec359c0168182ebf8b87c8d1446a7568039884410f5924c16d39a75802fbcf13fafd595f562e52bf6479e86439aff760fbe5760ffb32c8a1279e40df6d59af7bef950e7e210d3bd10095eccb22e7edbc9fff89273458c5138edcd6b3c8790d8024c56d3dd80a5926208d10e50e1dd52421cefdaa880024a7db7ab04396480292d31d62725b0fd6b0251a00c986db7a100c5962121eae1259a20948b7f5a02b641da0cbd7fdb8997976aef2a8e97a0f86218b65cbda2e5779e474bd6f91cc3a65ebe787e209fd2d5b4e164356927ecfbacaa39b4588ebfdbc959c17ac618b49e8fb4f71c86f903802d2ed3eebd9f91e34df46498981dbcf17a94e2f892c583c0a0ee4c09405072f138710ecefb7e99e67f7ec9136ddcffbfd07a3ffc1f5ffbec9ee677bedb5c7df5ee59eecfd11f7c8ee821dd90fce59c3d83f3b0f3b1a765dbff7839d38028be17d3357f3dcc976ecd907d791bee95b803c3b3082b387b193b89cf3418ddb405cce7540eace2ee778b04a6264a5ea96485178b774e1062b2eff6bf6303a1597731f58e2ce2d977340bc71752ee780e0c01449686541758798dcaa1feed091eb9646658c2103e296464f408065dcd2688632ba5b168141821f504cddb2286aea561c8eaf2effe56f1dc0a179e98394ecbeebbeb8f300fe6dcb5c08dab8f4e9cfef49da28e9beb8f4bb2f6e598474e977cf7788afaeae1ae9f6776ddc79c4964b5ef2821d3689733bb2fbe24e9184a72233ad512b7927c1eeea5a9b24f3fd4998efd608e4dc105eae51dfce96fd54e6f547bae47592bc6d045e7ea46ef69739ee7cd9eb3ffbf9fd5d3401490adae4fca60d23e76e38e3d6ffde6b7d25cbd607b1be7ef7dffb834d860fba481bd7f32d91aeeb3fb2ecb643af4bc5a8afebc31769e3fa79c70769d80f36699384e270177c7afd82df645f16934c5c8b24b8fe458eff91ae6fd2c317492f48c38f643aee8bf40b76e147f20dee8bec0bd29edc7dd41b3ffc8f643bee8bbbd9d425be5f317c316e48f68fe4eb8ff88bec38e8fbfa6e029214f5b9d63fd260f308f3bf2926f9beda24f11bbedfd0964851e070b749f0c129ce1a46f05d24f8305cb04317093ee86f5c90cac1735cb00b5d64d177a9f45d81685ef0a98864d230820f52710416e3820f7247bb72e0027f0b2e86eb22792ed8627d83e233d75aa0f8618b2cfdb23e64852dd6832451288afd21eb5b248b26b00e8b9fe707ebecd8336aed752beb996b7e5ec029c3482fe76e78c19461ec2ee76e880173ad83eff3bceffb496ae0fbbe2f0d9049708add5cebbabbab5ceb7e921ae8ba67711124fddd73e5225420097c5f0f32333373cf6772dec9af906baf6f3203a3ebc50f1ffc0c8cad677dfdef33307afe7af5bf5edfaf31c05792efbf1659de777d677d7dcfbd7a2cf0abef2495796bfd6f72bbcbfdddfead36a8fef72c1ea0b26c8974599375abee5eff880fdd6ed76675edda3ff9491bfff9fe4ddaf83b59c291cbac537effbd937cbfff2a9151788744b873caadcf621226b77eb5cc3a247c64593fc8ff7bb092966888ab70b86b04735bef6e14ded6d767f1842997f547ba92444322dce69abf91776b5f7f164954dfc8bbad6751c992cb7a9c2111eee41aeb2bcb32ebb83842f7b592f3bf725efe8e2cfbe733b5b5674e8e757e5880ecc1bc7bb0f307a7c86d03bfb625d2ed79fb9899ffe33bc9a1dbfd6693437e5fd33259d25a2793ee43dfd5d5d5975b9b1cf25bed9077cfde7f64f93d95f10a511c0cb7ec1ffabec07075a9d4921544bf3edb72bcfc4d86fd54beb27f8888ca778b90eeb42c2261efdba86febbd6fd9a1f9e5521669d497beb5493273d7bf4524fe1ec9e57cef9da4d29ef7b61cba5dfa5d8974bff9555bde6efd26677777fd598434e423f0f77b437e29b5414aa892f96e83f89b59b3e2d03327768055c6166f787a2663152e32bf6fee52f1441f1f7cf1a5ede37df8fccc7359dc7a2e3243f2b9c673f9bd24f5c93abf4e92ef145b606b04d66b5e904524fcc1c69eb59ef5e04f11c9b425d29d2cb2458295a4326f392feb2749655e16eb2bc99782ae6987fcda8ce077feb4cf35fe792bc9b241f5c1a7e250bdde8321f862fd14e7d2e86f59967f82952cebb35ab644baad89a405daf276c19f248bf49eac33bf340092149759673eeb8ff8d0ed7a3fcb5be490b3fc59e4541afdc726f97e64ed7a6425c119ceff489b6987e6f52c6fc1e1ee8feb8900f426a431a8fb0f6ed9d989a5677c3937830fe0c4d23e7ce9037111229e4b043f12a7f1f399ad6793966a36ef99626c58da2776e9d70c29632c85eec4d233fa3c68a090e4fdfc23fdc2584e2c977ec9685cfa37fed16974f8e004104e4051e17036efa96d92382a821a4010431027b0252dd56cf399626cfc494b6dfb9e6900b6f91ec93400db0d8914e6df50701afd3975e97b7f432285ef3d7b3be234fa9fbd219182f7d3dede69f43dcbc3b9dcfc92a7980632a8800837c230820c2efed21f1283916d483a6071821f7e98a2cb1233b0b18dbfe4b9f4594ce17138dbc4d23e1f5cfa9c436dd06ceaa63f80cc454a1ea457d93e2dfa1da547d73b332eb55de97df43f1bc5b3a5f742ae44d5edacf7a5dbf276a92b612d6cb89e25a2765f1e5d21241fae17450049b9db7d496d59e57a14d6a2e9765f66e052cf46e1336e67a79e50b0f30515f4062df7e672aee9cb053bec48226efc47afabab2b1f6c2c227033632861650d1cfd39e4575797085c0f1a86c8e244d48153125238a3297cc62502b7f30515382a0248ca55c1b1164d36fe3203b77be60030ef949cb3a1896b342f1131f0193696c5f11936b637fea3283f7c000c18bee070b66a7149389c8dbfbcbddb720a48c34af253d2bfc506ec7c31c5958d9fc506c0c81823047758b9bab2f1c7aeb77702d02925bba7f3ab5df7fe951cbbce6b345cece2b8b29e754fc979410fe97b1d9d9776c3c0bf72112f65d77f7a7d67bd7befdefbb3bcaf9ebf7befdfbbfb77e4bca047f2ed90b41bf48f74bff7d37b1bfaf3f6b5a17078df7d5e47ce4b495ab9c6ed06c8dd532ee23d7fe791437ad6bd1997a767ddb3e865ad52df7f5adffdcc7fc0effea67d68cfea774f848b3cd7ea770f9263d3eab7c8dab4caa3fa8fac69f5bb97b50febbbe72db71c72bbe7c145ea3b55e891fcc1eafb43dd9b9ed5ef9f3fc5fa36dffbed6bf37dcfb91ad4b89cab018d2beb597d6f885e96e59ed58e6bfc2c528f9cf7a891ba7625406e9fcf9ffe47cedb91f45924a17be6da08fef4711082c503f8d3af4f6d592ffd7e8a4355926280017787ca0bdcc08eabeb7d11d2f56cd1674bfadd91423529061868951695545b56015924a1a34fc511bc67ae95dd47ceeb911d126a6d92f8d3f7a736a89fdaa1db2dfdf677b353007e0f4e1109f8e1775f49f1998bb8de7bd0458e8074cbf0c1677184ea0aaacf1f9265fdf9e014d9835dced520c6edb8e6b53842f78b64fd16392f08b278807e16eb4592f52d92e7b3888465cb79d996ac6716d90ffe9106990c1f24f9822c92c0367cb043909c9745125a97b956f283e00cc1f08f34872059490c8ce183dfba654d526f688358df2059b27e2ae967cd568be4cb22f956be5e475292f68cc3e95a409e32d22f65b77b9eaee3b9dd773fa47b5913b7fb9bfe695b67e4763ab7fbb17dfabbaeeb9e733a1bc4b923907932cf27207fb0b0673114daa77c1adab8b92573b9f4bbff6af7f7f3bee42ef77b229f4f594b689f7264a17dbe37d23e35f77b15da87733534ddef25d03fd4e6dc85b7dcefb97d6c6e19bb2591fb757fa46fc0263f21a608eef760874df6952171bf07b927bb6b685e5dfe3ed63ef4bfefbfe79ccf0671953fcdda074baee797b21bf6e176ec3e092ebba1fea37fec9200f20e19230d586eff386120bbc029c3487b369f67fc29e92414d0b9b87bcdedfff096e3cd097b46a3009e230826bad0800439dc20031bc760d4b288643e1db17ce9b17d64b73fac3c6d783987a58d4badac67937336009968ec1ab6cf1c3b00b91c1b745dcd30c70d888445871b9e7000a34b1177d85804e7734ec943ce7bc1497b36699f4049f06cf9d7fbca1f8cf6cc96467e5b2f6b1ff0fd6fdae7033f16cb96ac6f3deb0b7b36b6cfc77a7ff023e7055924873f451278dcd096e1b7fe23f986a4915fd683b379baeb23f98224df168be49e95b23bbffacf95b771d3986be5cd9db6f4409fd323f98a30bf9246dd9d21d86195eb3d93652d2b7fb0c9624ecffcbfff9e7d4c52b29cd7238dfcf24f72ec19dbced69ef578bffa0fc5e170389ccd7fc645fc7acee569cdce03eee493b1d6366e7f289bc187db7ff3649ccff34ee110c96bc42b6d5cce5d11e3967ffb0a16f7fd47a769fd8d047c79df5988b40fe7ae7cb9fd46a67882ff7c1681c672d63d11cecdc074fb75fa67dafa8ddcdcb44f3f933c3d6b9d9ef57b47922cce581c615e9d9e4d71d6b37e0a8c47b7b60fe774371d0790c9381ff48e9832e2703818669f30bb7da4670e23e736c0e57e709d72112e1a3977258acb972fd885948bf4e5d2bffbf9a00bb26b24bbfd4e96936888af6ebfd177e9839ea8e4d52473adf4af9cc4fbee5924c1ff3bd24ef2ad64d97d39afff47da28f1bebf6d50f7def3a5245f4f54f2c1f52c73cd9f8a48ba196576fb67dae6d31695700da2cc6e5b3bb9d6efe209b3eb96b9d6967b3691be498d00b9dd7f2673bb2d274e54f2baf49da4422fb5cccecc5ce3205dbc7a7693bd05387f82977332bc60a6d132ccc0bbcb3919eaa0fea0fbd0bc9acf62166f06f1724e863ba693f37e97734c42b02ee7984470653d6363d620037617b9d621d7fa99ccc0c839a62dd72fe7985070cbcadf2f32c9d4c495f1e775d535763b80dc0ee89e1af9ad1d69e437c9c4e170b83b9f92464eef7c4ae7146386ee0f3a39599c7fa4bbeefd5d44e23d73f2eb9fe2fc16bde7a40138b18cf3d9320d387f9224f0052739831657d6b2cbb9a52fae2c1eb688645eced55075653d6b71885ee61a7f8b4886e6d5a5e15a7f8b24cc4b9f4916f927392f9343f3eace771109b5b19ef54f52c6b57e273530724e862362345c846fff28e322ad20fac10e1685174e923d58d833ee597973e7bb2ee764c8e2ca7a361f64ea3db973790e20370876cf3dab5df7471a04596c40bf2d912efd239b3b8b2af8b7bd21d96ef4dbe64ede906cf48f746d9fcafed35f1af95561e4cb29cc777b3bb22d213124c2f57eda3a92da72deee3db2f4db7d93fe47bc6d2ca6e0b6f9479a927c9b641bf74fb521f9517733813c34726ea98ddb5dce2d65b90c63918479fd8fb423cda9c4a70246160d807429b521d29c0570b204e49e913091f8a74842503fdf20b64347977fb200b202e81d2ff8a29d9b8074fbdb863df3f7a79d4742bfff91e6f6b119a1dfdfbd9f4f6bfbb82dbb0ff2a7ef791fb64f5f6fbc2a00590123f8a2bf84e5f2fc72de69c39ed1a70fbe4c18fdfb878e2e1d9a97da0a80dc20b74f7bc824c8394a6d8497a77d4a23df3ee5cc48fbd0f7d7699feffa8fedc3392531ae7fac7fdae639d79fdb27259834ff120097ec88e099bf976002e096d4a1fca71301d3fc69ade328fbffe0961fb478025371fb6b869431f214976f49adae9b20e91af5a5cff405705954d25d6abb6e7e51204319f90b14e42eecf21727e052afecd2f940a0187279faf7a5ce65066282f173da87763f85726eb524bd030a19dcf9a3fffcf4ec87acc6ed9e26d9badbd95228e73e1b23dbe6b482883b3830f586153a9c61732ba048e3baec7e4ed3e603891abdcb3928a86ef9777aed5e80dc3eedd94a96f5d6ffb8c8e77111afe3229d73116f2ed2938b4cae79cfdeb441defb7b5e93b567f57bef67b565add7fbeff3bcaea3d4bdb9487def6792ef76d523eb7b5d3f3bf4dda07e4a96fe4548b7bf497b98028bcbb929a2e829a06e0bd13ede50fcd3494cebff61a6958d85e5c33c2b3bcbed2f6197610f8021978342ea1af5f5279a4d65be006e8b27787742b14e7f0f31f2852a29974b446fc95d54e8d9a463246f433581ce3c9b30da339d9e75397358ac6736582e7d1bcf8ed0d77bf642055f6ef7448c4471fb5930ea1fcff517a07d38f74413b779b84c223ee3ed248ac3a27f9240272438f7441bfd1ef9002b73900fc045910fc045d133be3ced53d621edc329788015b639447c76ec9b9e35ec0131e47ac677bcdec30fce56f25c2682f4c412414081613583191c61632a2ef3cf8ef40fb5f1330466940f4fe0da89266eb71351dc6e9e198ce53febfb6e0ab2489e32185bcfb7f5f3bfd6f741f4e9839d888465025292efa72d83be9f22fd8f0cf23e4a270f6bfa1b15ac19e286d3283c37c83fec197ffdf69e7e29c473ebb31eac64ed591be3f77c9b45d6cbfe536dfdb59e4065de69a98dc2733d1b44bfb32358aeb3677077f7f9eeb3c7985eb610caa17d93dc44c5718650cef422c4236344172210e2f9beeffbbeeffb84be28dce5234beec25d7478867011f0f96ffa67e6f530cf1f8e3d00a6b7c9c4629e5b16461221f331b171e61b1623ebd9477bf67d0c19ebd9f733e437edfb1639eb190cc917f6ef3f32b0960c599bf63d8c1c7bf6fd48f2f4ecfb1739a467dfbbc802f4ec7b912ca167df87a4093dfb1e2451e0daf7ec3f449af67d3f7f8a23f46d59eed95763ba0b0128da92874b17c6e2b22dc51785786e149e1b147eabe4b9b30c6dc973c3cf893dffb70fecf9cbd88d79181869a467fd317248d3622cf8230cf94e9bffca9189e28ffe03f3e3f7c3902c22196d19fe57fe15df4596e3156d19de72e48bc20dfabe1e39d2d8b487cf96b3fb31598e2e11c808c67f1739739aebbb63f7fbf0b97fc2a67dff3d7873bf77912212d1d69e7d7fc443922c6577da3246b2ec67bb882ee3f89e9d43e61879cb9d638cf5997462ccb83c1632def295467e8634adfbafd67194c9fe673c3debc68f2c6796ebd9982582c575e6be7624c039bbdd29ed989cff8948645f8c6cbd18d97a3306237fe7e22a76e8206d752c2fac3d63ffce3fa8b3b567ccb3d6cbcc9fabac72e9e5cbb34507a50e7ed37b1aae75f4e69486d487be77e1e4e2fb96eeee20839f0472b808e55cfade046bc8e4bbe82efd4f4442ffa667fc32aef14f19c930a21d2dbfeffba8571a399805e46286dc1b605b9670895cfa538c71be1091cbcc52e8bc80bad3d9656a4ba4282e7df79f618cf35d88b75c3acb18e74f2e287da702049fa76e39a48cf5112ea202d7c42fdff5e5ec87884effcc9ce6e1f74cbb7eb5338db11c5b3cac073b474906ba30a28ba62bc4c3c5849eb946a74d7186e4bc2209640463498488ffbcdeffc88b046718b2fc27d6347fff58f86a8c446e8b88ff2421517fe96daff72f40fb94d5a7be7f09edd3ad677d68cb1b6f81ef42442eeb5d547253010e42405d21ee7259f4b2c8b23d7b7d2491c953617b4ed417e7943377bbef723b59ff94e0b4ee8938ad7b7e06b2c548ffd6b30ec8d408648ab19c5aa696f6e96f6b342f121fdae7733aa27d3e4b0454fb44b58f4d4f2cb77bbedfe8b4eebbf17295a55c05640463f725a8c045d0184ba6badd3379d23ef5bb670137a56fed530acdeefcfeeeab98506dc95c5c44e259233a9be05af71d4219fb482023602d3dcf184baeba5df7538c4a2255e6531212f497de2644fb94d3a97de8779f0f8b48be5aad106f51a28121b8f042758598ea4e7a3d925e4a7208f363fdc3b66aebcaf176b652e7fa7733f7d9c0df97eeeeddc75dd7f1cc72bcefefc85276f9fbb2eb3aaed1779f1e59c62e7764195eae43b8c66347070d27173d3db2b6c6e8d9dab376a6773ecffe32bc73ea08d7d8081109e480219433847f96c3c577f937936a72573bebc541bbae637ecafcb2aeeb28384da0725ba270fd2759f2dc39e79ce5ec9ad0b37e27857898cbed233deb9fbc85889139bb5c29223d6b4a96425198ea064d8b44a73fa1d333de22813b9fff36c5a8bcb94239977bec596cda92c89ddffd4ce7bdf7294444c65bbce9348020c8df24ebdce6b94de476f9df7739b7ab48c25a8fb450cee5a79c33e43291cbb2d28bf5cc93fdf5a6673b7276bbfca6946b311691944244eefcb6b3fbc299bffbd2f3f83dfe208fe716fc938b7e3ab718e7cf1890e075dd7bd77d10a5f426d67d4929ed485b32b9dd53727631cea746dded3a4b72d9755de74f20b70fbd6077777b7bcb00f697eeee3d6bbfccf432fbf897dd3788a8af5f5d3107807ee9de414e49a2795b095f5d5d6effb2bb8d5a890817c9a76701e98325330d625ba70b2a7391c945f896f4b9b6c6382f7dfa5e7b367ffec806f885970dd0e6394b505582d9ed449313b8ef8201726fc0eaf60c07708740ee12702901167480f38f984c7c694288af7d027039c744976be4d7bfc8fdb84873cd595150b18aeec585f791f1b1bcd4d7e55c1371b0445ccb69006b53a8456c05e1a247bc6050239d02165601d3da2226468232623130323cf50a3855408239aedf2e0196db3fc4c80765c84528002e915bde9044fe4454c6cb3ff4978a5fce5d4af6b8200d295943ca2ee88594e471c12fa4e4cc056b48c9980bb2424ac25cb0155232c805c190923c170c434a861714434ab62ee80a29c9bae02ba4a477c131a46477415848497a419890927ec19890927dc1180c2c2626e3b24446e3ddc9ddd0b580f30aa8286e49a19a8082f280169d86c262ced9ed05ea0caa840f4a24415929d1841249283184a7441b6228a1c496fa8d97734ac8515d31b0bca6568b0360750ac1277189dc12276831e10ae7d42b146364282e505bc46496f012c25c2616de662249092c4b58dd3126b3c41bb5c28493891a6238c74416d8f8728921d8625518170c16e04cc1571954d3edc94dc059c41836146e323373126d24e18593f8726524c87c11d39fad4a9ebaced39673887333b72a798ada72e6b69c35151628d87f3f8e0ffbbcf88f0c4dd9552fc8946d35f323e3f15134b6b574ee3fd9287694d31aca69dd4f4e6ba75c12b95bce2a9eba652f4199ec74923dccccc7bccccfe75156198c2dff636c29b3e5e8d45aaa6664ac66aa50cdcce0bccc30cd3055cd34cd3459c954dd923249d9d1025bcdccd4cc8cac8b4cf6fd4de53f3c686c6c2ccf6a6a2c0d0d0d0f4b79f008656f4566ca66acf7f3a7d37492eae9659c54cf1ff3b3cbf3ebd9a614fb39f52fb2e78e71dad74f29191a1e5353aacb949215999fb132a599b7223369ec0b323b3363a79722a4db39ae75c76c59636c39c2d85206b3e56cb4e5f7bfc8ceb195950b9ef84007d0880304b6594b2db551830db87165fb5c8ba8343c80abc3043656d73147087032e08ab0816c2859e9d2050c4e6063a7b95e7ca79af1c2644714554d6ea585e25ea04e2fd0a714c0616b24a66e4999a46ee75cb3d9d8b2a54c566df9f7b3b9dced9fe5b4388d9d3c5bd6a7ce962394d3f8525bca8e2411e534be6ecbbf50311dd303c83925cddd999f89e241130545c38366522d4eebe741e79cb28ac9c46233333433333c66666a64646256c94aec9564373ff333df3236375f4dcddbd467ffe161797c0d8bc682cf36997d61c6be20f3ad076913283e65125f4f712fd8bbd5b7555b5575eef3a8ea50ac677fe2a9b5bdb853d15792b1de45e6adc4629e6d4a32d6a73e86a45ae69496a9db1ff3e59c1a81d32d1b57c563d556fed34f4eebafa3ac9f6e3f68ad1ad731b69f26ae9fc62a1be7d47c9a5652d3d64f9d1a14fff55552b7ab6e4ff194ffc8c4bac81e05a74d2a1e305e669e9d36be8cccb3d35c6fc563cf4e0bbf63969d063e28533ff6e28c65a7b15ee65f3c2c3badf5330fbbc92ccff7532dfec3c3a2e0b4f93330b6e42e773ecc965566b42553ddf92f5b8e31cb4e73d992bddcf9a22d65a12db9eace07a996dbcfe33f2d5b56962dc7caf3f178b6ac9dadf3eb6521c1348c1c20e780ec3face707adaaa68d454e1b4fd127a7e12ad3d8c44f71fee3ef24d3323631ddae2375f72e5053fec3b62e54d40b7d826a2aae7226ff513a8ab1d5af4fadf0c9296c396979a55ba8a5c97f94beb7e255eb4c4eebffac37b93375d465bafddec5f2fbf7f4bd9fdfb2b63fdb9e65a7cda6a6f2a2e5f6971d75fbbbc97fc62a1bddbbc828eef697dfdf915063e950936d2fa34fd73673d38b97cfab9a39a8692515859bca35531729a7b537a99a3edda64ffed3475831061a1da86105b67e0ae53fae830dbe1061441054d8fa6994ffcca519c0b8b2baf2c2d64f73fe13e40a1ecab85ada62eba752fe339da82307263a70c0d95a4bffd4e23fb76903bf5bdfacff89f21feffba784fd55fc07b42538adb79838ff695914fa2793ffb0ac0afdb3a95a262f789605a7f537f52e5cebf72eb7dfddbd9f8971d2aa0f89f6584515ff61b0b2be9c5a2e2f4d1beb5fb077f173abf52087e2873ffeebcbc974f92b09b333cacb6827d4d2b4bd443b9f467e1496a60df6adfa254b7dc939f04b2297c32f792effcc7f6e283c3f8c9c52e48b9c5d5ce4a422a717d6f3d72a4ef347c169e2f38f22682753cbcea6b6a5cc4e2d4e9bb6fc3bbf9c52f367b30abe3f4f0debfb13a995b258ef5f82ff80e0cf9c361fb4ffd992a5eefcf23d5b724ec6b263674b22d569d4963c7776cd8a01e49cb273255b3dfd92a7a82dd996b3cbb664abeb930463399d9e6ec9565655fed3a50b1595172fe594f29fdb1fc5d8fca793ffdcc4f7befbc207bf64a9af6b3deb4bce755ffdef4b22b79c4e2e7b7b41f4eccc697d7bc1fb723a85f641a95ccbca589c8baa76fc884079b6b2d34a9e6e2175d31480a327fb76da7ddebb9effc3faac16188ab19779feeaaed7088b81791ba6698bc9ccf0a8f9a4a56aa37999272df1d86c6c0d0d199391313c666448598ca4e120f363c8fab15a1c040c6b6c6e38480f1f381cc425be46184ccd746fba8db445df738deb9e7ef8370773b85ce35e56bcfef737f7a77f0bedadab9ce650537846960b1c5b7ff3b1a79ce6b73975e3d9e404c29acaebec6d56fd847a7d2e741c2bd7385c2ee46155415541b1fe6b2a707cb6815fb693d89a4ea3eb4b1e5ff65db64e89a1958176ec26961dab954d1dc5d83e5b9dd6cf36cfb28dfa0b69762b0b9073ca59553faf7bfef71ffafc1ed991569c9256fc236f325fc91b8f3f8ab1252d8936eafdd2b4794d947e47ff567f69daeadfba168bbb5f9ab6ce5b323154ad5f9ab6968cbd252d89b62426116c32bf346d37245288bdccdf78d8db8c5d9a3619ff987f8cbd4d2aa7f9c3d8a5696bf9c3ec14a7f9b3ec8d89d3fc47bb346d9dffcbfecd6597a6addadbe8347fd14ea71b3bcd3fb44bd3e6f9837636396daa7aef8d9dd3ccd129ef998ae7e3aad69793aa658f626cddb3ec518c8d3edbbeafcfb6fae584aae4477a6447ca9c369f92efb4396348b3bfafea8ef44b59f9b79cf5dc31127dcbf5ce62bd7d4104bf5b2dd0bec00a9f3efb4f685f68bd7e3efb8f52d873c73834ef2765530affc624c4f6b22f802efb42f879569e95ff88564914ad67e534ffd02a85e1f753eab3e583560904676eda52d6b24aadb7525fa96539ca69fed47a562cabc47a2bd5be409f6d4a2ccb4eeb9f504efb703d778c44def7cf2546ce2181eb27a927ce2181bb9e37359f9efce766c58f626cb5f5dfaa3259fdd638de723edd9884d858764cd9acf80bec3411aa6c4f3d778c7fdb9673887ecf1de308ccb5b256f6979fd44c629c498ce5e7e459959f5397f9d4b7fca43e29ff994f4ef3af751c65b25b7e52d79ffdffe23ea74fea73e2336ee94d7956de54cf2d3d9cf7e4e13aa82e1d54ec9694aa63a26add9236d128dad4ddd2735e5c8be7bacab5485955b513aea59eaed55339ad7826ee4beec253d6eb7389712a31cedb5fcea7afea32074715a9eb97179066c766c5a9288e15ff52d361ec26e78be9b159695cffd2fc6cfeb2390f9be3d8fc039bdb9bf74c426c9dc454b359712bdc92676ba6b675cbd630b6aeb1b5b53508b6b6b7ee9984786c24c9ca6763e2b1f52fb9cd3b9b8b3697b1790f9b7760737bf36712d24ebbb637fa4731b6ef6bcf7b2b2eeb3acf8e6e6f4c426cfe4c426cb718b7ecb4f94731362621366fd21cf8fec334b6d34f5a7a99edfba4a5d1e63df8fcd57f929886d89262bf44c4d6faa5d096b4f436d6d7e797f94f12d34d28be5c3ffe528e0d7cfe318969664b5a2262837dd2128f2de6976636985f92d9c64f5ae2b179335bc741e6272dbd8d7290f9cc21cce78f215d1c643e0c297290f930f2c541e68fe4c821cc7f91321c64be8b8c7190f92239c341e687240f0e321f24652c647e8bfc38c87c16197290f995ac1c020799ef91351c647e47de7090f994ecc141e63be98383cc6f128783cc3f8a99ad05e8d15f8a6123494c3c36ff256a6ba71d7dfeafb25addf3879dadaed70883f1e78fb111262ac34698c21936c214e3c146986e646c842987868d30e9d8daca9c6684a9deb011a6b1071b6192f960234c6febe7c761234c331b1ba1964536e276ac61236ddf69f3f98fe86c2a9073ca6e0a89b1ec9e3a29f1add05c14ebadb00b4e500714d3101bfd9b9527ffb137b732886013eded86f3eccd7b1944b0b1ec0de9082a64e8ec0d034a503029b18226ae6c371944b0517b3b52c38730b43c75c9c1e6f4bb4f5a0a6d5e8faf55b79f296663799de7e35bd489aae3f134f63c62ddcb4ce13ccc146da2f65dfc1b1229906fff063ef9379b07e9e76ecbbe86e2fe657fc3ddfe9c8b1c7ccecccddf904821e667fed6317ffbfec614b3f98714eab6573914cdbbfe0675fbab6ee7d89bf81cd8b7f63692f6760404dfc6de68aec6dea8ccdefcade778d81bc7622f636f74caa7702c6d82c9be656fd487a5b4875dd2b1d5677d39e658f5a6e66d605ee4f1dd7a98538beaf657279967395131958dbd895f639938ad7fc6de50705a7f8cbdf138adffb3b7251ddbf7a1bd5128af72a886a2b15df53cc5e3c9441e33cf637c2fe6f523d46d575d280e21c40b47257abe100422480d636ff0b0421925c6f9bee3f44b77770edddddd8180618e12351a86620e3a345dfc10c3d02592e0e038b95d25d205e1be42100b1a1d43590c66b090470c1fc0843de0f020268c3131cee79c58c8030b0732618b0576266cbd80e411be154f65a10f2a7068c287818f9a3096450f9b7044c10dbd0963399b1ea10f236a7c846710118387cb0c8be72d3223e390a9537322dca0508e713e75777797c1096169c4ea87231731950c7d60016343191630ca41f82d8d342714a35e1d842010aeea410802217e10be72086761cb0990d2c2708c560521fcae60fd087d00e1e9842d233a2021521312e37ce7aacb5f1391f07750e37c7777770a24a470305bba13864174e5093d2e6690b053639ceffccc3b406a3a3f40a0cd3ef0a0831c0e2cf9383e7adcd8d4d0c878ccc8c46260785c43f3eada24e1b93eaedbd202b608af5e3dfc3aea125fcf2292f18f34c82212d81f6998ef594412c3748c7d0ecf902305402105264fa618e1926653cf3a4bab95e32d5a9ae6985a9a6696f6d1021391cc2ae66c82b7cc288edc1614de64ea59e3dc0027132ea97d7c1ce793771daa00469c3052654a1b56538caabceaf0c1881025108922c2021339bc3079c2822b8e26225a4c1832c5101550f0d20585145410d7984943b0a0c043029e128ed0c152470a5042488615cfc4a5a053021d223c7610e11942045ca30d9d39549849319bbd0c669f336bd54135f322811c26847264630d1e7cb0caea773f4b4452ddb84e2f483a2994735b767eeb33c1c82dd2a8bbad56cbd69b198ce574fad6b784e8a72212f08477f0c90d15df4d8cf5e54d7973c1564bd602bf7ecb96e07f7fc46364949b5b3ef944245f3512c7752b747363be18cb822c0aef5ba4904c268bc53e161945764b16ae7ffdde1391781f8fecc278304484622808bf450ac5ca5819bb2d4a416e9fd67b6194d82d85aeff4796fede772292d0bd1cb742b10b0b613a53c613bce8077e39dedc502af47e0fb6c872bce00bfcd7d3fea14d03fff5e10c403877c07f3d8fafffc872ca108f4f45242f6b44ef28bbe3f82f925ea3f009d7b748a1f00365b2da7a6f916578c177b20caf0b7cd7d719803a77c0773d8b43e1f56c697443f09d74bd8b485ce183e477757555c70dad5078c50767e822e975d156f97018f9bdfb1669e4d73ba8919d9c2c67cb967f5bb29b1bd9188b8de16d7d19b640f0f35bff48b3482a2db2acb7f54e96357c7f3a03c073a7a4b7f5fe21c93d6bfd1107ed90b7ba67916595a1d92292eeb31e299e403b6a67cce4a00890bf2231f2cf0180ff334aa9fd2029891f1b500308aa314b07738cafcb393594408063ac76ccde185d7074808291950608368ca3173433466f8a668391750508698c2214514fb33ad200c20318a4418307b934965890460c52e377830fc8183d393ec861a46f7840c358d360638c0ed0183b30406862ac5b7800c4c892ea800553d0e2780206682cd102348ca08931d6343ad862a4657460c6c802c3832c63ddc28df15b0108478c314e80f0c4c81ac1075d8c303efcf8800e5acec0b2c6194a5db8d8f2410f638d0c3c9861ac29f062fca2e8e089b1b5841523ab08da0c660c7141b0327e977366fc70671c3043cb9d9fc3050f6a9871cbe761dd3c7f09fe439f7f8afff8b3cdf3cf9cffccd6f31f61c17f68cf38cff6f5fc44fcc7d5e3397cfe09e53f391ffb98e757c17f609e3d781e9f5ff6fc28f88f8fe7afe23f33cfa408822deb05e19bb499fd07dfc177e48d55aaf99bf748cbc1dbffff481cabe475f0b3af24cd2a75f7b407c91cab94f3cfc18b648ffeeeebc3480fac07effd072f439256c9929ff33c481f568926f3f56948259b57f2f15978b9207c4cc7d6c3c7dffc8b54f261affcb02989efe36bbe5563afb86c386f639568fef537f68a8c4de9fb9a97d97cec5b1f432ad9c4fee65d37effa98bde236251eff7a16a97463afd46c4ab0bf717deb29a9e4b25704605302df47cb5eb9b129b5bee66dec95984dc9e6e9c3904a32d55ea1b129d5aff9b657aa4dc9e6fb9d66af4c9b52ec6f5ec65ef16c4aaef7f19dbd02635392f9d7cf904a337bc5c6a6d4bdcd07f64a8e4d49f6aff7e04352c9037ba5655382f1ec95974d297c1fdf81bd42c4a6e47dcd8fa4921525245b8ebdf2814dc9e6fddf5ea1d99466fec5fa7ff227a9c481bdd236a5f16fde8a1292cdda2b03b029bdde87556259a5f19560af04f34ab18ff92bd3e6e3497be5c7a6c4fa1aab54f357a6edadd27ca57e257fa5eee9e3904a3656e9cab4d9bc0fabe4bdd2f74aac9fdf83548af91bab646395645e69e69578bc12cdcbfecab4d55825f095c257125fe9f5ae3fe2209047b94b43b2d3f865e4cc69fc3c48149cc63f4356711abf0c399d9cc61f23abd3f863481ea7f1c3902a380de734fe919c4f4e637e1749c469fc22c9c469fc2139999cc60f9213ca69fc2d52e6347e1679c469fc9564c1694d4ee3f7c819e534fe8e7ca7f153b204a7f13b39c569fc4d4e2d4ecb398d1f691ed1ba12017283fcdc73c738c284e25abf0a5cebf6c058765303317ee397b381184ba6fa619cb7ac138a8b14f10f054dfe8f6a4e8f6a2fcfa45d62087e3b8d51dd62d5afbbbb9b54e02245303f14f47abe4530b6a9b856ebe787d97236bebe9c512b98ba8d858bbcec7f2d8e9a358ddf6539b4207b5354546b4671b5519f8dba1dc5b5fe26b6d591868061b3aa4175db73652dc752e6e4d04bfc21309cf3f54114ea5f41f4881b97f841f44926065121626010751abd20ea43cffa893a5b06512d3deb328836f5accb8f36d1b3fe209aa567fded14fbf7be7bfafe9ef39f98a5514eeb87b1ecb40e46479bcbe572b95cee874659772fb7dd8b7b712d5ab468712d2d56fdaa5aacfa55454949494949b5935454545454543b39393939d1262ba7282b2b2b2bab693535353535f534f5f4f4f4f4549f260e377113377113a70277e12edc85bbf0f0f0f0f0f0f0781dad4d342a2a2a2aaafb69136da24d54ae2ef234e322e00fb59c862068cbefd97ca2962d6f7a36653d9bb19ecdb167734eff30ca25866054944b0cc1a828aa16ab7e54542d56fda8a85aacfa514545b9c4108c8a72d9b28ab61cc3a81b2515e54d2d56edffbca9d56255fb7969b1aa179d5c2e97cbb596afcb75aa5a4719ada202499d5b555555552555252525252535a5a6d3749a4efdd3a98a95959595150a3cc5533c35f33a3a6bafe7d3ea55fa51a6fe80538db2060093d314b79c5d3ab2ec926b531c8173458a3c5abba9fba0f97c51e0daa4f1cf6c62f1cfecd234c50880203272a4b9eeb41276fdc18ea47736f98f6cfc88fa0ecddb3f9b6617ff91c9c6b1cbfdbc26a70d5cf3aad9e7d1ef1ec67276995daeff6c721f6532dad44e1e0c40ce017bee1887986b1f0a5c842f5bb23d307a141929b841e1d77cbf47d13e6590f83639d7df6c797d0fa9f17d60017b9c29987f2e314f7ebf77699f92af626fbfdfc5689f3248e639285dcbd7e780742c3debb7a433f5ac9f243da967fd4f3aae67fd3864fb20dbaa67fd3dc8be21bbaa67fd3664d790eda56773d24cd9e43167a60ce9627817e7e2538e854bf916cf79141ee53476b716b0c5aadddddd44aa8f54d6a19792bdcc5cb3e03fb58ea34ce6e57e2587442691d9fd50277573924d5cebff6c39f3d23ebdb47be9597727f5ace7979ef59ca367fd234c25d336960e8ce5f40282b1e78e916f0a5c643ecf8b4596cb392c926ed95e5860753d4bfbf8f45cd3ed7ce1fc5318e2f926b2a9b8083b55d3d7677debc1ef20c6a27ef7a74ced1334ad479111059628ec68ca65d9a245ca072c9ca684e0f2d43e41dee58a1ed13e412ec6d5d597ebdf3e8c657b89baccb5efcb49e54d5eaebf37799337f984729affa4f3b3448ce564bafe93a9a96758faa7b364e99ff6c187fe692184e89f3ea29be8997f3b8d2591eb445eb6ace0d8126d2963fdadb63af27091cf76d4f765375151f97ca69d37a93a8a8b7cefad6e7a96fd6acbff9494df1677529df15f4ad99ca367d496e3d06ca367fe2328999dd481712289930505a5df7bffd510fcd68b1fbee892c17ebc8979988f199a5f24f3b197b132b11818d8f8728921d862d5cfeb6833d163bbab5dec76a7dd77d8c25f6a3d75dbf676e4bdf72fffa6dd1fdd34adfe47acef64fd47ded3efabff512b46bfffa8be077ef7fd51ebfd975a369817addf7fd4bd0763fdf74720cc78043ee85a6ad9446b0567fcdb52cbe6b256705a7fc40261fe1efda3eefb996a6cafbf3deb8fc0ff7ea9651bad151c97bdb1beeffa47fef4996a6c626841cb2dd6d3ee8fbeaf7f3beabe6b59fab72396ad7f3bf2af7ff43dad479f773beae891b7fd0e01363f2840ea03b8d93c538dcded8dfe6dc9b3f90fe11c7d5bd8dcde965c367fa6191b1d9ab4b3f2ba62aab1f9dface02cb53a6aade0dc8ec49f792baf1a5b4d0c667481b0f075139b81195d60cc0b269b81898d2e500616139b898d30957a3132638f99188ceb8fc21f6f40d6d7fd91f74e440f1b9a97752f93592b38b7a3dab2a1b156706e472d5ad9064db69b9a9918cc08fe51f8aebf2db56c36d60a0e8dccde986a6c3ceceda8f5333198d105b2beee8fc2f7bf1dd59f89c18c2e90e57f14fef7b723fa33b1bf1dc93cccdf8e627efcdb11ec5d7f3b7a3df8b723f1bf3f0a9f35637364ec18b31f636330f6066665a395bdec8dcbc6449bf347a17dd08e2dcb2c1b56fbd9da21f4df8e3c1b760821f41f515b3b84f96e6987d0cf7f6433595220fb73fdaf7cb074bdf83cfeeb19e661cfb18f29737a46c690dfb37e1812468ea4cc458a64488e2d16890433671b20b74f10578d4145b8bae38eeb4135280c1a836241b2a09ba0b7422204e5f8d5d51dd7e71060cf1d2351918f21fe21d60fd51ffa7ec8fba1ee87e80f157d53b7bf0809d16422b6e5b39e8865cb9bfa44d596b2ef893e5bc6bc27f26c39764fd4d932ec1913515b52a9b9fe44fddedc6da9ccdc69735873c600e4f629f2bca2aefba12216ab55148645afd758040313f345ee324533333c8a643f34bf48f643343688686868841186e60fc57ec87f08f643ae1fa20fbe92fa4afa8766dbf2f6a4b1a56cdaf279d872e62626634b8fd952068bb1250ccc9631d768cb97cb9623f8546a405b862d5bb2a8d45cfa546a6e2da9d4dccf96df16d7f3efec10ced5d5d5a5b6a45273dd96df16d7bf34f2ae5b2a33b7ad6cd227c09b1eb3c8b44c33f3f26a6c280f598d89c9843d9b45da8e21172132a2b7fba1d7a532c3e2224446dea53f24c2a512bb5d1122a3befe437ea9c844eff95d9e8db5b80895f07645a8d0db15a102738b506951e94bed4dcf66112ae3eda7e2ba5e848a77dde6f46c1669eb47804c9fc8e87b7eda7d5eebf96b6db15ccf1f82a14b7cfe1136fe2bf6fc31d78589bd917763de68e60ebd2e8fe7978557e689a8d0cbe38de89d79a3d81d12e1da1019c15cd91319b52ecd1351f1aecd1bf5ad792222a3f1fa7822a37a7bfccd1bc9ee905f1c2223d725a2d217c736d7e61b7977be0fd2b936bf0749b936ff86ecb836df8664716d7e0d59b9369f86fcb8365f467a5c9bcf830cb9367f8604b9365f866c716d7e8c7c716dd66a23d7605c83e1da0cd764b816e35a0cd768b826e31a0faedd70cd866b355cabd56af3917c4cb7ea26a9154859b1245585941452725f4460031ee478aafae28a10b0ae48828731368e35a717d4000c28a83881162fae08afc8218771e6724eaa885b8eef6b744edd690f358c327777291bae63b1b9ffbbbbf70da3eb724e0a861b5ece5d41c7ed2ee7aea8aa01581241dd1e21c18f449090107fdb2877fe5123cd1e01acedd34dd6f993e79c64cfb9022e5cb8542e5c64df5812b9a38f7fe3e0e978f6bb9c9e7161755688e77a9ee7d93f03acb4769de75531c6f5aa80010c150b1876f0210926a82742d0c515705cd1e50a29afbb2e2d83c7e58aa4eb793ef4522b1db9e08d29b810e38c2fbcb03117ae250fe74ca29e2d5e688163ea06343eb0851561b0ac30e3035dd7759d155bb07458baa2b5250b261e991552d430bb12b028a55356f4205af154831532b8acd0e1735070cbe7f18cc86d69aa62cb16301a872e72d862820f6879838c2b1b6dc1a53fc271691572e8d04ab1cbb92d465cf0726e0b102ab83296fd749bca7f3c2fb3ca9b534ef3ec0b1eae8b6745c5349ba6962a4f4c3cfef37d7f49a47b1b25df69de5447da34807ef79d7da13eb5b10d9ce1870485044c9bf77d47ffb347ded3ee864403e61b245801ce569ffeedc8fbefab3df2fe85a3cfe25c0c1964c1d9e8d3eefb65fe4343701cd94f651f71e73fd934a07befbb299b06d0f79ebe376d1d3992b643eafe3cc0e33f6de3075f4f77faeb5d4fa442eb59df302caa70ab475a01e20b302f3a6883bd5b2414a6c2bcdcf994eaceffaa5ce6e9cc53d807f3627d507c5004ad4d3fab71534e9b0fd3c5a6adee7c5733b55337b5963bbf2389ba17c1fa300f635f807d7db689301ff331f685ef619e6de0b745d2b18f817dec5954e1fb986f62baf3452d777e8cb4f11ec9167bb02389fa55783debfd895468bdebfd871a70033ffcd737e026fef8ad07a7a802eceb4f5185ef611e85a4fa30e1d787b136f4679eaa386d3e8cc4d9d0e974e7cb58a8182975e7c790365e54eeceef4652e634a2a27987868814a0801f2252c1a601ad7f7dab9f48059b06bc5e10fff56c83f9b648baa70ffbef41d8b3a882f81f0c09236dfa916cf5412a2a806d49ad7f3500fcd6b7a882f8af175508bff52caa30da97b5f1774d89644856929dd6226ddca2c01d42974ada34928d9d763422755d5d0072775227c5643763cc47d9181bc7366698819513d440b5851646884ed45063e3e51c1549b0018eded9efd97432a767b418dc510618531da0428a2c7ea82148a25688e7765dd7f97377871b556429430d2f6164810596ee061afe676b7ccefbcfe83f9ffdb276dd8fb8ebdd6ee9765d47ed1af53f27bc9ca30204b77c9e6e066f94c5a8e8c0951e65e5786354d8304367a9b8b2e44d41b798428c18a618c30a8fcbb929d0b8f4726e0a32b8413abf2fc3eb799e57dff33ccff3bc8fa43da39ef7a3ccfb59fb08f15c5a1b07907368bf0da57d3bb8f38914a07f2ad5cc9dc0eacea992e7ce1c0a7077feecce2a140c714b2377caeeb4bad4fb3ec08ad50c99c21e1083975bdbc7610f88c1a9677cc1c9ddaeb6338e31e672ee04572e4f39317af7a5ce0562022f2935c11b9732d9e8c7fa87da28a5b3ac945213b07129a5fea3db8e83cb39135c71c5cb39134071cbf1697b1726f0c10447c891e3e58cf096cb43faa7daf87352545dd8e59c144997c5fe233ddd18633fffa69c1d19eb99900c9c88c1972b1a8050818d5e71e98fb92a2efd9bfee11c0e8793c1463fa77fd8466de95688675c63e45b8eb7dae6fc26c79813a3783907051cb77eb9e5f828005fcc3e77a6902f403a521b92aee58ea45c73bf8d9c7ba2897291eefb3b2e22c7954d27e9144e97a7e8aaae3f38d4fd34724e055d9e6ec9530eb502dca5560ed573c7d8cf391574f1efa4fce7880aa09ac65153b92e5254d75530d534be65e7e5fa774efe43a7a895ffb49414e788102969f5c68e366e8a8bb42dfa7ea8e8b3fec435ff69c535fff9d4a58318dbce2f5c73ce498971cbee72fd5b88b1ac01e01f87fa1ff6e11f0fa54da8c4a9150cb4899a041d536a06680441400353156030482c168e08048a26d7f80114801098b2545a9fcaf42888814a19430821841802000020002030334c0002449b3da1f71f290cec0d6a2eda91cd9c18032ff0dca5a25b87109f947ed71b47ba08981d9bf2eb8274611b9bb453e3e7ac8da1b052cda092de2ca54eb20d304da0082804cda4a1f3c20b1db431f06a0187aaa837b4efb965f92729070114f4bcea59b995635e6ab964472cf4a36ab4b19709c2ca9de559662a421ffc529d1a7b238b3f2aedbd15738daf14666fd4f24795bdb76256f795d26a6f84fde51b60d7db9446a2d08e90226a9d8d122b1a6cb88ea4185e8ab8e828c0b547bbd13679d2e8eb62008f3df7c003341b2a56a1313c5bee8c133f55ef2b6be3a8f64de1d95bb8527d6f859fb221f71f6bcd8b6c8a9d7e5ae19cbd1d83b6ef5dca812c912fb71f29ccf63e3881fc2ddb5d2cbf8e2013a84318cfc5ea0ddb63bb22e7865cad61f213d625e0059b3965d0d0ba019f1b9c8f463543f237858748ee5ea3cc8ce2e9169db250f69c42c4957ef25f05bf927d337805e7a9c127a68ab304437b4fdf1cec75c4976720c5cf675cbc7c9600104ec31deab1daa6da90ea2e68b2d3fb183b47fada1739a348859ad424dfcd9b5a51a636050bdaa24005bb3e09d32a06af898c3ed46bd7ac2df71250bb69135da4b3230e9a600952a3cc8dcc1ef833daf8af458999fb360a2a0b55ccab6b747098d712665194cb54527b1c476b4e091333379dbd6d06867ef58142c14f115b2b940d25b4d25e5dc424683aff6af940c398b7e6d19b796ea7247d57f6f1b29f5a3042643962d66c46aea38ee900a581e4719715dac6a44d115c8616ed20a5c64ae32713d2162995829cb618dba14a67e654b9a7aa0ad972cd3565b969a3ac543a5ca6a717047923c352c8baaad3acab74ac18f070c0da4cd214b5d89b3bab2805208b9e7870c36ad252ece6816850c9a571009a29569705ebaac0e73f4d13750b574673759a8e009465223fd72f4de9a84d861936f12ed4982c4395d1424c35e51c48136a3bb71e1d79b70b521edddfdd699a24380f1faa9b9e956b7b3d41859a768f66a9d89e720e4ef1c4423e57ef896749b13efdcf6ce9e1356c06ce10e06fa93e1305e565f67da0669b831e088c05fa2f19bc2ebf67657f7d691c9ce87d1f5761c8bbe256fe5cb3f027af104fab7322ded5187c0c5e42ee42ce8a270b5b252e3f050235054a7b7bf28ab5bea6c6b4f4964b56e2cb65ff8d2cea2a8a4b3f71ed05c0442127b2aa9aa3d5949d533991365698308826d8568c9a443f916060611e49de48596136deddaaca8651c4e1c762906a01a1b40da9f7888ada2ba960e322b592daa3de0455ba1582813a54d77f8d896cf131506088a9bba9f2e218a5a46f5ec66a4faf1697b1753c30fa2d759189369d38b8d6059246990ab474780e435d3f1772b935304f056102dd56386b9372ef6b781125778254d44995bab3eda025a1b15072bad43cc1afd5682b16ce25195ebae8309c013ed5bbfaa9c7820349259df8681249971dfd2c01ddc3db884b424f3a0126be38a999dec88aa3d05b904b4bd83d2ee6043c1d394cfae52f77a340521e074963371f336152fe576a72774a16cc67fe0188393e5257b75f154c9409184dc7882e8e76c44c617f8bbe191a9eafe4d9243cae15e21df29d5863ca452ef3245a288ba66b90922243c5425aba5c8c845af81354dfb276445e7638df27521ac547dbc3cba9a395ba982649ee347c062f49167dbeb91b7d93f4daeadffcdf3b893749e28ea96747a2251dd12dcddf32d81258ac5e1626fd71cad511914d5d789326ef8c6c39e122d373d120c1974946253fc4bfb63eed5e467fba65f46440959dd836ea6d27bc4a21e599bf34788ad8bd92bc90f8902deffc3049c8b70c9de0629f23c561da895c73531c7471978f3e349ec1402aaeb5a6420bfec49334613ba38eb3267466eb8ce8d59adf4b3e7fa89e2274c0b2c1d5e34047f6fa091d695fe9d1064ba530cecb62fcef245c4b8f43d09f1cfb284d2456bb7aacc819ca1888b7dbe2ca0ff83c8994bef1da1806f9c27a7ebb01f4066e489df2076af97793f05fc779fded8cc8857e1fa2017d627eeefda020eaf90d931dea0bf2ef799e2b75c10b0f8c339059b86124a8db6a81dc435fb43b9890082202d636438c8a86aba073f75df8e3cb00f25ec6f0720a6cd342f71ee185cf3ecc02d4534e7487cf64be6feb6ec18d340400426452f811407ee619e2f0a440bf0d20f1252a42c6506345663530c1585405f964c212073a6ff8b86b0d61a2ae9c0511f5e57ec7bb433e6514688a56697d15b4c70ba4dff9ab8ba44f7a83dc758e4cf30c54e781d89513d83d6c9915014b882ac53f346c1c4b54bdb424b3db0688c95498cb9cd1f3912197cad54afadd7992b8f40e38baf374eb32a5e899740e46de3a43a8f39bfe7c9f77fb734e886a2212d4c175032075d54e4f44663a096901c81b5d71042a0f83841fc18dcfbda063064b2b09936097c6d75db9116b120847d2f7e01baca50e9ee5276548f69e0b460954143ed52ace21ac28e0c83f9e3d68202df581b25a558ab3dea6f7903571f32706ea6f46e7d1e53f6d15283d8ec329093f492b9eb51e5911020f4c6275593de84cc4fa54886d513dfec010bf4ac8111316299d70238b13cb64835f4849c7656d2e0a85cbaf474a1b9ef97cc84dbcad93e7bdf19c407ddfbbdfcb660f84cb303a705bea1c8ca65fb7955df5a2799b464b5499d4ffb010399475a9bf24a1a5aa03ef2c7dd2742b2336842a1b2c0ad87730a7c4ca3f80e15e3855f446d650449477629f313c4f6fb4a4217a4a3923e2f24408ec1d306fe55d3ce2f2f74536516422b51e0064cdd56b59be5baedd1e0d3f181055d0133f7cc1fe93eaa5410f74a11b148f70902a9f4e2676a6b4ac09fe8adc5f2a4f92aa5b8eda56735b937214772fda15e585d2c2a28777e3e755039e14322c5e88726dd471b446e35431e4a16d93141a72c3cc5fc1e0ce7731033d8ed114ad8b28fb8134a6a282c1fc20b6c5bd8caf1dc90d42bb6a4b01633d71b7b117faef2ff0a21c6a04c3e80736d1bbe6e843733cfd43091f91d2457f54f5355960682c21079aa9029fd89d215e307f6a4302057a57c42e62e034e52adf29c32f54f2d3d99d80053b9635ef497c564f733fd658c2b2819f189c57e9ab1ca23f1f2edf474eabbaa0371d966c29c6a30ec7cf09e866b7f88dddf0491ded82d9750976336d482c070633bb3b74b3fb1bd64564faff999dc059947cee8bc9d7815955e5e7a395c65a9988e184cfa2aba11d7a67263257d80c86466de8f6608bbe5ce27fe78cec69c143a2f2fa3064b76765d5c5d29e4f467b4d60793bfa8711d61bcd37bd612e548d115f7e3601fb39192e1fc068ec54b4eb8c9bfbc03f0d32c346d587193b8ea03f89d7c3b0388800ce66685019b0302d438b54cda15e038f79fc73f15bba5e4eefa3854b37d6ed5b9871a4c68d574525fd6e5007eac2a7ab425f0f09b43d6b8bb407bdd007d57dfec15cc52977cc66dbd6aad4cf6ebb25c7c66c7477729dbd1e4102b4911dfc436e9c29b3aad343aa41ef334246749f382e62d8d479cac350015db653088a4e93b24be39fa1beb8e1add7776c9826d28c4e8a96d554e4e62560277dd21e413f73e700c1b10e90a442a210b5c402d206d083c21b9b9a166ce94ac1f82a51040b1f3125afd009698a7b3faa49f4758298e68b352fbaf599bcece122f88bb819e604cc23088b88bd725c06333d184a2fe4285c4adb755ee942c78e0dffe0ca705cd465f6cf41406053e51ba9f5d14894bc014deaee964462db16cd2b60296041e8c4028d9db38eb6acb9a1451d6f72e963f8a96bb4ca7913b7e16a174162118e1d94dcb0593f660ee859b9736261b26643ce67ff9f7cb76152f0f222db9042a421a9d4b642938423414faca9aafca9ac52a4e42fefe1fcb3a9769c4daabe989c05ddc21873df5c3296475701c66f8e8e05e3e4626b0bb90c5c81913b61afcfa5bed9f78b5d667473e9752ece61a6b78bbdc0c0f572d765d9e2566c5112dffe7a7967743bb8568337ee2c091e2cd153c350a1ef642ca8e59a70b18be35a73b227334de8796aa5a5f908ce82566553f0758566843c57684a09a19e832946a089080e73ec42c0e15dffa5d3fc3a22ed6eee702894ef04c694441d3882bab5e3b19b77bb39b9134f94c88af0dacdf39e785c32ce9e98219758ee795b329c4a3283c89609c765679600e120e0da74e43590531452a8370f7acdc890af227ef833ed3f89dd90c8c4212c55c15753ebe2d2d9f8d70034966d14f77a869443d5e7dd4f25387ce285d9fe78cffb02122ba74e6ec3d7316672bde6f746497f635c08deb205c8fba9babffdf05937e10c0ce4b7c43971ab6ccd20005665d40581912a831504cc53d99e2078a2609b21aa271506b5435301ef04c6d6457862dfae833c81651b0f1ba9dcc52a791f2aff85393f5d68a7855ab398698775e08463158fb97bbf52eb4ee24fac01c1a5354f3d257ff4745dd952081fbdd08ea83dcd92f6ae1863a07dd78ba04d4290fed64b1c6014814cd81c90ec593fd8fc8c9e66b6c4284518ca65711cdf049454e5a59a16f34e93c1fd23c2445da0b891957c66f9b7466cbc1fc32b0a382351bc5b631b89834eddb2a8d35d2c4f2261733aaadb940cfacc87c0403e7d8ba238d3f1314c24f4941115f54a5041b2fc58816ae3864ec399039a91f8502577fc5e9918a5c0e752ab49da983b4b752de08a80bf1d76cba9f556393aa8ee96a2d9d76f3a8fec390782b9205334370d4f25d0f29510e2f471c6684044fa496ac299c91e9cf4dad132aba8437a109d4b188b6381dccd4af3d725174dbbca4ba552a85440ef9cd9a2a2ce1b0658827443de4eb17e69e3ebd75c3eacec4710b3ee4c188d391abb468db4fce44553bfd415f77672ba125a64755f54e99547dc8ca391131c80eb8b647d9f2b18d07b17d015be5927b2a40a66bbdc312e16731e49afc6efadd90b887593c8f0b8ab7c48a8837d2918e7b9b2ce4b12cc5211481b78b474713c641762d99a8aa0aa5fb258a62a393fb6320e78f168251db5e5b57aeb0d02ea9adcc80d00245fc80b9b1000f99ebffebcd30bdfd3fe403aa53a590a57f94ebb7d4511a1e9e0442465b695f028e6eba26cab08cce4bd653d01a2494db4487a3dcdc294f84dc59c341ce6f57c976863b57e1c376cd23fea5c09ae5ede3444c64404597f3f3700f12dc40fdbfcc89cb974adddc12545701f469326f207c133d0e620693202b01a031c8f102835953a465db484773104294f553ce22e39a560466b228950fe8594d689cadaff46a6b27f4e6e6b26313ac6365b1a27268a86e41bdb58d392abd83920819849a02631c279e96adda9889d39e3be786535a4ead7082aa2641d98820623ef3a77bcccebe6adc609d7d2eafda7efdb410b95844a8542b575d27ccda880f4c430f8c40f9ca179b09fff92163c85214f868e9a91fe9dcea6fdc336d507b73aaedbf3275509a8ddea008f3a04e5847b6ae6b006c7b4655231cc72021e81cb57cae946139c848cf14f3eac4f842b16b066d8e2354def71ae1498e3b6dac3375d7f1653a41de6187acfef508754220543135350ca52ba889eabb99aed267c66d7a7627acbe02a279711a81bbebaf5eae1c9133973f49e66f9c6835bea8ee90033c0dcb5a3dea5ba9d7c3a65c454448d6eda84c84ecd37120b0d8e18f3bf1491ae04dc9fb8a980d92486aaa7ff1795f166f6ce68079ac865491a2b63250b5eb7728acfc0968cf9f753ed242c871a6a4d98c43393c93d281ecf57fe38bfbe7df0bbf3f5cdd764dc7cffb471273a2c76a9810446a1c374f97bc526978649b85703a35e6a6e02429332ac0d99f08a41bbea26920312ce7a72d4ad4bcfb2682e1d1de0174d36aa0d34abd484d402463d7916b493b31c135971183a4214ae08f2ef4c8d5b16e2ff523225e1d5e2c263d71fa0b1e0e2b9739fbc53e46261cd3e6f7430c2baceab7dacfa6e80f3c78406efc56e47a642c4de674c8b5d7cd35161e98669cd281daea8be38ce44a3a790012e4e78f2da5c1c0ac58c0340db48d6e7a1e13255ead70dd2e4a27c4d3af4bb7a517290d679628884204515a079c8ae88ecb4681903a942059fb88880ddcb8d47415132e20251249bd05aa612adaabd5cc0832e680c9faef141a7ee4abcf6c5613159737bc49cd19fe629a5f1be80b35cf8c79d78d591a96cb121569ae5c61df116e892e4090bded4b1b15628c1f85e2831f5414c8d0aadd23e8950c7d3694885a57ace181a0bd4b93dd6742033b4b982c5cce4c66586c3f3b1f682a9ef939a64b197e260b6c5edfc670847719916f30c7127d7b410344119fa2c0af3f72066cae9a77af860fb5ebbce040579efc9174301ef5ced5da526b5f20689a7905da5c733fcb7f638ffe874cc977a9d16eabcfd6f82734be5bac2249c28ebcc59e54a2bad5c69e546c0080ed310755e92eaa5a74d9537f8921efbc44ac80fa2a7775c46ac323495bf245eec0a02e64b99e2e6f0acc6f7a2625bb6d81124e2f7c857d09d08c9f065f0d70d78e3aabf2c8fe93268e87915e7121768a0c5dae4a80b502002d86d462394dfb084145326330cf6ed07196d69bd5ce3dfbbfbc629e78ff792c4071317a6dd87cd79fab4f436a6ec909a98233ddb29e058b802a16b4c66d537bac5c8aad17412ae3d0c3f3a4f663dabb21d51b393a96c23e41cf7acb5d10044a7e8146bd97e9f43dba6bfb67fefc076d7826dbf2fc01672e0762c4708c5cb2088307d946e52d7cb124cf662a0cb003702adaf04c0345175b811ec78bcd83989788f4e1b4389789fe4dda5ee4d74da8d54fbde74852312ea0c790ec4ce2a98a419d4b735def0dc3f20e6c0177c1f676532c82487b1d5648541255404d0cab3c6bbb9483628e098e6e823a18406276b89152fddabe3ab79a3fb063e06277a710a31197a50d815da9ba850d8633da651e85bf2945d10f58d559a9eb2790fdfb65c841e04bd5afc866cfe55bce1dd1738d1d2526e04e7fd7e59e1a35302c3511cf560c8fbfd080e1da8b05137ebb0e1ee3afc97bf49fc18031abc78bb60536b59514be897565860f2b22db46d52daedf17e0476a962ca623042a2a79ef79b8f2815258014be8610c01b17c89c7a5111ec4bbde5e67255f7ff18b4ef6a23a5e2be22186c1256b5b985809773e3ed2de021e8354add2140e28e298e31c7b19feb0c3fe768aa8c33f9ff2c6895f2fc12c01713c7147e1ed5238b6f429e70f6dd475e5f2f26645005e23ca1bc15f2872c9f985331550a198863031a98da57103656a596bbec59350157ea33a60d0428a78d711bd6a807d49371cc728c1abfb8a6aae18c34d0b2b36fc0533f4c929d6d01572763904996ed490ff3f41c37a44fa6eb8993050330e71b5463223bd8b53788cf6f648d7d2db560290c1051e6450723fb978a0c213f0bd1af931561c271da4881e8397c02f79b1a75b67db28d6f6f20051bdda420fd7c4612b20a3cbf9778f96301c4a70981940a072d2f16feae28627b44a64981e84bd589c105d1406eacd04b583fc2d78b2162e5c2b6b32026ca2794d38464d52211b47f2101ec587dc58e470e0b8bcedc70e31f5e02e2ae12d31f99fbe766657e943ccabf125d627343edde9df62e9de6a5b5838121e3f9fcb21d66a893986b295dbb78e95dca72a153110bcbd2efca9f860203b717b06e1b2a2640dbd133a3c80d3b7f474f176898ceb2b2772e0dab1f460367f61d11795fa715b91d3f17e507b47031d7a432bc7861d1f45db58651491bd0fd197a30a51ffff9fa2ccf40e934aeb4dcfbef2fb76d4e74c7233840be7bb858202832b39cc70328959c10045cbb234e06a2c1281acac44e7a2f84c7f7fa30f4308bbd045612a72f1377d55dae2ba29ef36bc95c195437c1a2cc4313c5528f6790c9120f38157128e41cbf10406a72b85e0507b7e1228a3f2a80e6b85d29554fb1872046ed90deb2d0e170023928856dc52f3671e60d928cf63edf42886fd2d4bcb17359f59a07ce5048edf58f6fd55965d770220ce1fafa445a0c11ddc30c271540d3f8135d170bdba4ded555d8ad4b3534296f2b0a762da4bea83d60d89373d5c742bb07c4cfb76c0b823edb23ecf1871ed27300461a3d38e360ee80d2e338fca6ad682b06481e8e83888d758f9c4f37764f83d8ac023944ead7e0a5664559e8bab6451b120d42088dce7ada71cf4a1c3bed6fcb48193d39482b288abba24643bcec7442d5759e9b3094c52dd1a1390a166713d6a0a37b3c7350481b187e25d71cbfc1feafcda1eb22b5d8730dedcf9be91adb6ecb16144a25631a496c5c66ebcee41d00b4640003eb9b1d394f5f8ee79736e2929ce3791b1cc3df155e6dd80b2bc7f91ad13c1997874e15ac12488acc3f4b56d50582f0423087562b03c5e14cab16d17910cd795ed4c93e496aec1c8e42b259af81988b6fa1b51a5e488049e14d63290d30463343ae3cf120486af08f1c0f37fdceaa01ae14858b4a7b33a1b33c1a12588bd226edc4b4b9e1950f63c6c1b91ed74cdcf08498cac054231dfda990740021df3369b6b917e083f70bd4650c5f4debbf50c304c2c02920f1ff289abb20c24c23f22a7fd0d09b3913960cfa389173cb50b1f33181fd6abcfb34618c82a073353a14dae776fe127aa44a747296904b4dc0cbb7d2462e042552d0f9a7770e103a780df978a841f8cd6e4d18a91eba4e27d15e4e2bbb5fd1ce94b332fc4737a572399f759f2d2de70d92bbdbf391252b54d4f9d2a85d6aa832c5ac0f259bb6e013ba2f6d387fdefbe287834c341e91eaa162ea5470ea969e6aeacab5edc457b0bfcf526f0be7d998d3acb54876ee31fe93b885ca08e03fd1101a9f6283b664cd8b66f7ae81af8c330f168269a3a00a2c0835be103c2365db06607e1b80df50733be2f24002d1af85080efdf2891ce24b98475708d11c44a9ada84e8ec23e5b6540f76a277df5beb0078ff182e145082c0b48e70b745ad2651bbb70bc8e4a44da8bfd261341f8b1b9f0eff3f5bf8f4cecb0fe8637b6414d0fe7dede56801b0c153b3bc732fe6905419785c47e72f5c74dca782f222d82c987d84d46c3f83849577f2f1312d9f7c43231455d231806b26dfb51d8ba02fa1be8a6899e3eccb2f5296a1ce3657f20cd4173ab6d5c6642f29621cbff6f5f3ffef84151fb5da5bee1f5de807e861ef139f4b5d0c8eb07ada9136931878d4dbab9de4a8a783a8672f17e87d089e3d744b63154270cfa78a78fe1cfa704129f5fa478885f56fe8e6cd20e377c600fdf148f36798aa837e0bc17ac305960771d14666f739edab5822869e9f5131173b050a24d269c7bc12eef2af6cfe83165b10677ec60f740aa0e14e8edfa28e6412999eac68e15de1d1fff9b82492156030b289ba7836893213fde4d4ca4d6489c2133a8332edc8f8e483452c05ab127846b65fc5930b858c4f6fd0adc85e18fc675aeafc65d9b9ced088ef36a8602b422833128d4263be2cf94612d8b6809da3fc68f4611417b55ea332ec007171df88497c2d16f589402e30bb79ed859d31d9d83f6904bff7f2431e7120b7517bd94dc57fe1ccc3965094d4c8e8be8a2e5481b575cc5ec4f76122c0318b34f5ea50322def9c366a9a9956b9089d7326c330655b82edbad92b26b29f2fd87ab40b833cdcc711dfc62f9cd7bf9eb0738d4cbeeeec7ddd4b6015639ebc1f7d1c01f716642d54edbb40c3a5c5aa960a7dbe6a063966885fa62d49b28ea00681281ccdce548ebfcbf26e751f1b299a8dc3c21023195d54d3347bd3ce61a69da8605f89c6d161737efe6b5e527544cdf3827f3e62ebf33a7d7520031b79f3b8967ff40822c9467bda6d2bf3c06b0e5b42791c3b04db66ba7aa48f10424ed7cb763ab0cffab6af5e679af3183869c74b4e2f63d2ee48f26712972c2d4e1e4849dc4c75f7a3100f5a165f5e15697dc70a5a3ce1f9d3998e8ee3e110c87ef83a5fbc2a20f64abcebe87293ac719fe5b20ac2afcfa7dd558e9be395d0d972a074512b12391d7a856e1eab046b1542cf803f928923f0350a1d76efe4800c91d65eb9659b051104f7163bedae3d2157fade540eccb48e49522780ed91c88b4882f1e6d5a9e4e956859ef320223ea68a57c7bb565981bb56086007e70335f42c08ff6d1ce6a24850a91251ccf43e9b9617100fee0f149ba9b3bbc944eb403286996ff9774228e77b6f9fcec469e0a3704fee9f7077cbef9c3e93117f8cbd5d915b3c53ad34a76cc7decdc8fce33cb69fd1a3db3d11f9963ec58c302e7288290b5a3614a0be453e048ac50974dcbcf9dbae46a11a2d01ba9d9493d386007defb4108f7504d77601c4925a6daad1c5363942e014ef97b2e7dc4a6bde641f4abcd86c5c178ccae69e39aed0f09980653a7d598a67d87587c06110e005a754854af55ef79fdf2034e5efba7bcfff842e16cb8ad4f524331a30ed6ca4b7d121bc637b804f90307a5c9175644db5e0917add4308549dbd5e51a4b45a208df16be7b50dd54e58545da5ec79bb82174614fea40bba4e5433357ff87c143611e007b7ed55347838820b5364dfd7de952e6fd5bfb344420f8ecf3370f611f1b8ca86c9f1dfc5151f5c3b3de30d55ec60359d2292e926c6a7595f5dbc7300730539f47274c2ffe43306cf8ee6bb2b18693b1dc4095bda26f3360a7d247ec03d03693849c8e3f91a802bad6cc4a9a4620f93c0d3e1a4c744c6cccc1d7a9e955431c43649497d61f3d162d53379f79cee4cbe6bcdc2d61751f09612519cae44ba28c9d9c481806c777a2070465aa43dc7b50ad106e6dadf39cd189d95d647b22a8ca6d931d7cd4da0bf9fe28cc59e705ee3a7e3368e5df8e746a0540ea25c8e769f6dbe1d9b5ece225b7c796066500bf6ebc057520700e429d65a4bf05bd72cead9948242ae7131f21941a627a08c6ee5635749207777ec455eefce4c13a8e956b4509c4a88bb6d7190c73399899730719b5c97974cb960cb578e885a1e3d1e1099dd25a1565d2e3cbce28987d10a8a7d9125c41f0163c50d7bb3bc7eb4a9bec9cc4facbcc08e202dd50f820bd081f446fe007d35b7c60d01b1fa136a46e92cf79aefff226dc939b539589da8912f1a6b4481dfdccad4b23ee88ac7312ca70d9b3f2073aa1a579d8fe7573bc7fa2bef54c696c6fdf7e78d4519e4c70c0c24eb83497faf40dad191c6e70faa3e93ef1d4c0b9eb43ec8716fdaa9ffe9f2dd04c99625a72e36f9ac234b0b681f406702964653482d682f1f31fef22bf1fbfcc0abb32da30bc7887cd6e1080f2d610fdc3c070a05da2ff98797c02832842b37a31486af908247de748b1b685700e2429d718f46ca2b34d19be94d8182562464ef17dbbf66dc9a516a2ef0715879561d631f86bad67db5a330f5d3143db4864349b4e5e439e6a145353bfca7928f2feace6eb5e4f935d923728bcfb8b800028ef0487a83fbe1b680b9a91fbc49d1847f4b0bc6d1d810bd1501affba3eb024258d5b681c5257f7bcf598307101af3bb497916507da43ced9205c51360fdcfa0ec6f9c3d6907a49c3944942ed6b1403321c3f47189122d85e842c1bfea28ac0f786ea654cfca4badd50bc7521af9093e0bb0ce3af8d27af12451912a7e03f9ba0884a04a6f63c436d3e690703b53f8016f8b6c884824a342b9d4bd811eaa40f474e30e1c54b7ce52f91e860f07616aac89882820a69da2f20aa2f5a7e48b6ae869c82ad837cac55af7c9e5a685474730c7618be629379d8a54b4bf5d6354c2dde2d975fc749a699de5bcf32dfe13e06054df534d6608db699e91390cb95e39778d8f185a045c9a6ded78cbb8bebb23c9a1657a9b4865c78a40a224b6ca1b7e087d0ad7113b3ea9dcbf4e5c377949d01abf2c32bba86950ca4d43aa3e271cbfa86608830fed8589fd5f57021ecbd1e54da6438a2454ef04a642a9fd42f4fab6590527517b7dd68956e86bbe299359914199efaf4397ae386da63f6c14e4a36c81f054218576a384b5108b23cbe4cac271234053799dacbd91adcd74df345dce2345c3a9707059cba4dca3537d37ee0ea4cec7a8fe6a79446e06814c687a9c68818f439d39aaeaebcd3e2cfe75774397f32430b66bd3877911495c0b90ec1f01cb2451211e8fac316c1084aca550cac2d1cf43f788f935a21c0a17b13f88290c543e6931996c1e25e5f1cfd818d68f1d718a14012873a7568e4a9c3ace3871dc91a530c3f8b3c08a115bbc421eedf719a144536195e12bdd800777f408e6e9aaf1d3b1b0bb87ffc3c94e084a0819bd9d3abf590030552b44ce0fafb924615dfb78247e8c7fee88ab90e7e2fb0f2f3d0369979b5e7fa727d6eb82cbceccbe6c2cfd363a489149a724e02405120bf626e544ca0fc97abcdba3054a11e20cee642794a24f5032853cbea21072372b64424a011e0df224280a169000737dc72ca147dfb1449e13e8e677e24480e70a695a9813dcdc5b1e3b94335331815de9e233d6bb925f0d7d308a3643d99d31fb370823cab57677ea5ad136b1b45c389b3894ae5f5d2f3b31d2ebb1e1d08906819b867c13fbe7975824bee474b0ed6c4081ae26e36685abb04e98ba00838ccb34b2f802a02014695e898ded7a225c4104411f9f92120a301c48795bcf49412e8bdce70dbf5198df4dc5113a9d52dc7ba1289e4b06191ae36940f6b13dfc92f6f18f669e92e0c3d5136c18859daa4a2557298b148187e29d8705f06809d260a3e5f676e2316ab31df76b55d641610ff61c97b823ed61e45fb25b89ad18d96d9168c1be55674a901cd7174e0498f1a1959721c12bf32c59a6d1b0b7acc96e006f714aaf451669379fb9637e82057cb1e8ba710c43ed117efddc17d4b67ecbec195561f8d195d0b51a654142b29848324853cc777a8fc1a561382e3e232004670bb3cd56762ae8341344618d4d47208f9ae5cc52718b08f889da9a97e06a80f700af8dcf73618428cb7379e7c5b5052b59918b751a4f32224c61a66047a9d609ef5cf7252744b2127b7b0dcd7f4d8d13419201b06df235b024967dc87a922b4aac44fd1ef94ca98805203aced89ba43710323d67c2ffd03dc483c224fe43dbadfcce5aea24ad2bcede482b31e9ab7a13b324162d56635f5aebac9329954e1d1636841199d58b8b0842b667111739611d939f35196b8329c0015252758e78be56e19c3f1d6e7b73d4973a07edee1c8f31d9f1d20c23403b2a69d1afbc501a68c5d04a537f5b6f7f7d8170edf8dfa4ea72f16891514b5d8cbb41345bf16564761d5c89ee29274db5a9a08350f6a2952bd1a9f8631b85a59ce5d056e32625c6c658d19dbc973c83bf165489269f42f7c502bd3c52ebad09cd44b8ab5e1762a868c442724e615c2e6aabeaa3e46c1ab7e6104a9f8312b55e914184f51eface583add07f9da6228d3b514c31ddbaaca1e2c5399ea3000df3342ae2ce01dfebf17217421e038494c4b3f8a52cb91b36c77299fa336043a349a2a5f5d4896a244afe4726e94f2f1cd02e89a8cc400eca22979c08e8fbee7aa0ead455ad470eec33fc85211e56fca0e18f50dbb16823a543c0268c73bdc87d7a13ad4465e7599845368875b45e64e77f9ac29910108941c4df04224bee7e0af26808da05b6f30a0d558ff366302d5b9441918617386790e390d43dd8972b5025c6c003aea8906482493b7eb6987e1c89e66b4cf8d66a6432f225636563d47896aa06320b538703bd7233d2d1529cbe7c7ee2dd4423412b1455299adba9e05be87f440f5e14012d7e44c0a70f243ea516758995fb4641dad229c4b2df55f4ed2569830fb84e55ddc040e7687690240734c5bc05981b9d3ac122aa490e35b08dcf2cb5ca0f9c3affeeca04cbdbedc41302e1f76e456709531f609d1747f497efa36735819d20bb73efffac7e9eff52b0b59e4d3378f92827dfd5044f3097f927bcc27172a13e535873bc7ee971f36e2991b3e5285ca6d09c9b166b6b2383a7107018ab6b541699593207c7925a0c5050de446612391dac6b634082cad3711cadd66844153421af90ca6ec7a352020316d835c08861f77717622817ab926a6137490c2df60c003e46831b23ed3539f988fd966b029475e30ccfdedaa30e92e76fdaf4b1ae25e58bc6623d7b0929aaeac3d7481d9575a46aeda762796905a64186caeb0bd257d996e9fe03c602b168926d04f5b3968ef45f001c25311619ac1114e23fd8b1f382abdc0b49754ab15076aff23f4159bd0fe8d8546f24b812f542c1fab63a0b775563536c8fab6282a5450a59ba64521d033718dbbd107e388c2a55fd0e4711900eb7ffb18f10f7f17ec0bc10d484c1f998edbfa3218494f575857949868393238dd66bb50341c84409381a5c5858a3090a9a5e3b58533aaaedaec495607d3fc465dbb027d826ee187dc2d706a0e058902d099e6bc132daee3c5c5e197d20835cc6942e849f9fd7a20cb3c51f707a9caf9af5c11c19bb3a83385cc47d78e1191d231f8ab3d1d9e783c7a17147e14f2e8cafd98a4cf8f1f9ffe7b1de3e8fd2385fe6e16743bd53565ba155d8b4f40320b9581ece0b38edf24bdebc6ba75c1fcf395c07a846cdd367357e89fe81b277ba6c89bf63ea5ca94e4784b72e43615990f0c0d770aad51c735ee622927c9ac3672eb2a61cb3aceea79f601296b9ee528c6354265d92cf2e03311e97acfc850799408287c1e6ea83720320c2a0e42edff504fcd6828da11b30e5fcaa543794f068cbf16518f78073053f5056c69896d91d70b7f471f180938f2178d0d091153276065fb61fe168fec0a4768f2e52ec14dc5c6281026d6bca4897d4a04f074a38b2710160171b4d2dd41e0245ef7da941f11e1c2d79d6ca006d018b395bfc52b92cb4473910a29ce103749da71e5f1feb322497717a0000ecda2e68e412d0ee340f70aab402bc7fabf24c05ecfed15496fd39a6b658b6f098bce75a34662085a725f364e6fc72b38368ca87693e2ee25a06cab2f13c10633ad73b4610a0edb78bab3efc06f38b29d3540aa3f72bc90efa1e3bf52e0bcd928af40844e9cc78b7df1e7d01f822e608089c4007f1578090ec3b3c1e9b25b89c2b07d48d6c7c7fdb0dc4b3899a32c7456fd13a8082b44dd67413911bf6bc98e8502520b8b90edadeca68de6641375b4cb4535fa9283490fc18de9d4df6e03e9b66b13103cc9ba7d5f3536e4e8814b5d70c2f01c7fb69161128cd4aff928ec7a4ab49e6bab1c7fe7927dc7adda2a08f0d1c37391d73346781c2aefa346499ea2e5d2ede2108fc44a03a8c38166dd0c4a95162db39cd9b834c7d804c4d3ab9eb3635502deab599d5bfb95d72b59c90614a5b0d74eaf9cdfd00982b5aeaeba9ccee837140dac541f5d0f80381e1a6a9730c4f8f7e8b592c539f96e73b7516b998db9b6940b65d5a0f0221eda939af58f93e7d77a24b06c49ff53589f8342ac16f6fc94ff29dfb4a585afee9ea933122e8981223bf3efd4af410088ffca42ee334992d706f527a8d8f9989af067e5a9a2cb1dcf0bea18232260654431dd51726d2483c29add94c217d05bb8f8df072f3290689d49212f3cea2fbc337c2c0a9812ee72cfb1d7acb55f9c35d61e5443e6aff8c447252f4aa7a1c1a61c06ecdca76b8edf64881827726e0601e59b5fb844b99f7ada63a13627730e93ec90310a41ee5c744b0c1870a1844a91155f0f08c4e160f9e8ba0a0decef6d6ced168987d707bfbb50e03073924d44e4238f11b1b9002bcf11b746dce92af119f877e66bce32ede73a19bab999413994040d76bc561c893cec7801149659b8ef301d6256931b573ebcb4b5ce0df690b72f8c242a9586967dfecfd553b34ac214c3dae889f4ff465a916d8849cb108a4dc1bb46d2e4753dc85642762e9d4283287ba83db9657a2e62004f4b0d620a89cb1a8784b54e34aad099ebc1ad7da627aba9597158639a5bfc526e68674a278d7b0587598890192478083dc5015f28582c8c423d6b7df57e2c03e92f99a19231ca14db9243bf11a8bab0a0e52b3bd1d4669e67461f34c00dfd0c48617bd01416c725384c3ab741c7f9c3b6da7fd521d4298563ef8353d0496a901838de4541b4559cbd589b083a13a5fb626ea4c8c042f89535ce839ab143ec88a50e3db04b581c7278f551a8a0c88f2dcc588482abcca00ab84d498b8fe29b401bbbbfa42a4cc226c1723dd88090ae5b22ba8917b5a060f2238fb5182ee2d41fd51783b1ce5e08f16626f5150a104e8d63eb005c41916c3b78ff58b8c4eba0dbf7ccec9aa6d2ccc3187a3a3c3e57243b0440e80824362fcfce297ae40bf0dffc84b19e9a44a14ae41cb024b9744ab18ede9e9e2cac6836fdb89bfc895a99275fac83c1baa87f0efc48166f755a68405144199f467d9e57b559937a996ae5a5f18f8295ee7db7a98a459850aeed35e90a245a0c6575de32d8eb0a2a7de98741e77e219d80119beefd82086712390ec8a2a37e78aa88704aa86a250c2f91d85cfafa9debbb80f82eb54509b6bbbe00e6225374e4a47fb5554c833ca88ba7eec656e9a5a403f7c0843687d500d2e66659f5ac6e26a922e047d0d0c9e25d47b4e48879a0caef173b279536eeea391eaacc1c095d2bce4f5e1a700839dd29cff2c1a158d13d28883594972f14f4bc943dbf18c1486a472445615dbf93f665fd7422e5b5382f6b9d148d0608df5f9209313c53139c979a50c6ad89841cf08101b2c6a84b18fab12f08fbfc2ca794c2c51817d67f36a01b56cb36ad8911d2fe033cc1359926ab888be8b04e6456ecd63805422364c752783210f6ea0dbfbf1607c35e9d40945be8d54d4d7fc90e7571e20cc7f8bda3b6547dd170a3e917606cea48db513251ec0129194d1342637a7ba2387ed867bfe195e002436a2f8f93fd2e79ed9eec513d79d2a185daa39101982a301e09c7c1c225ac2a62d9a3fb42db993f9a3f774e321c6843008dd08f367d3ee56f0543be85e04438800b0a64513803b6ab4a312663f544386e738e6f443e3fc04d0a4f81269f1ce79121e9f9466cad5c5eb475f26834f72cf718638528c3e086107f32ae58b8e29b3c9b8583796e9c240c0faf9435b770d89b007d2397b2746a1d9851f1b9c9b59efeb06afb1620d64dd67687b5745721b673c94adad2a4404c602bddbb2d23658d969318f449527b0f128cac9931845bfc300420011f792e93535fd3ea2126caf2c6b864cbb6bab4d30214e246464078127706dea43bd1db23d6563ced164200c029ecdd9e3330ef7d76092a4c2806c348170dcfdc08625bcb20cdcc4ad32ad2d289bf6af7d119037f605b5afbf130a6092e072dce1beb864023a9f79c5a3a5f98dab3a65515609b404d5ced7bc1375aa94ca605f68846ac4ac88bb194d6f6da7aef92c63733af3c43c30810409578e3d8e22d31d0b06283d6924c9bb4901042a939610c44bd3feb72862b816866ad20a54de52c01758813ca8b6ac36be0c97708225c799a837cb3cccc967080bfaed5163cf3d8382d0a7c5f482bfbcdf0a4ea2513670bc39a5e00fdf19370dc6542f5c3226d04b4d2c0c8a65bae7c481bc49ac926aed78aac0af2cb110aece11b8f2b6fb6cad716f50441029f1ffddefdce253c4708d22604a82b2f5016a63271cd02e506c7ee1c80cb4ced4e47f6383fa6eee82aa0472af9552e176672d5592362440923fda58a8509a950cad878e023c075f585dc3552eaa4af92367ec4659bf5cfd8e6afd04c17df69629aaf19d1855479b5f1d7c722602babecbaa9186e8895c4115b8238bfab23413046e63be7970c971d902fb5ca2ff4dec4a236343bea3abf1845c1b469d73a1582d164635cfcf2a04ec1e215e89b940fca1cbafddc50fca43805600a8e086e85195800ed609459d79690f4a0514c53c0f32c1c1b08222a1950c04972abeed430e0f17eeff06544212756bf8d66c5dac489283fc60846329da5412f28a0a83d30ce860d16f201887c0af84de974d009e0018964aff08d4a9fb14785d023bc804899568df516bfa449263b931b92fb3cd3a4f2708fdd851783d1362ca45ff38a0cded50a959292da82accb2164b0ba6e0d6ae606d2109b53f666359995afa62923bb4c3254b0bef114505603218e9a56528fef28461b3483249d1e9be49f2445b7a3b878eb4cfb1036ffc01f57ad9908c34da333896b780d4f748046462c192b269501842175b0e67d9d29481ba563f82c6b56f3421c637bce9bb65f7017c4cbba2ee7d9616657655095c1319e295d1e00c640eb26f2f431e1ae4587a4343a79b870d4528c8e0f69595a624599e938458ba643a20681980ab6970b017ac0d7efb1b5e83f1b1d1b063926abd58deca36d3565288acc2d4088b8e13896da1e46350c39c5e761abfc4703271e668c1059894bdff08f0614aa4772d9f72e40ca6e04fc34193503ef1c082e75c79095e9839c18185ff4a1551f946cd70456766db1d0a4f39e59c52f62cdbce32ed8a8d3dcc41fbdf155471ab636e474c6eed245703acd50824f44e0d6295915c1412371d0dd2536287265c93a71b58b937d47b6a8671bc106c95d7aab570c244ccba305e40c4d55127dbd9c5a9ba9447fb46c313266191a701c076b04f76ec98206554d22c4669aa6c85c0c762d20bbe3f6a9f1ca611594438f7895240610c58f75aad70cd75470f9a007a30d8b5a66b6cd5bcf836809b187d1ae86248f569bc749900106cc2baf43949fc306c496b9a6a34ec5baddea773dcb7be6e2f04e0e8839f302abd884c807aa696edf973eeaf905e67d6d1ec80e4026696ed140ae4713a22c64b59ea37cbbfe1b35c3ca0e35f07a2990236b360f30ad7e84b23b55ba60505cfdf008b59d44305570b0a6c4b61ed2fb085c6be5482d0a77ff94280fec16f228d04adeeffec810cebc7be2a3943f40c67fd14b0b35c4a3764e1b639249720dd7827cdb22a04e235c9299802b7cfea5b7e9b07a554046400639ac884ba9f7ca5c0315daeae4ad29a4a780f5bd9929780f771215b229624002975ab68241f120a3611a03619244c0cf5d911a6e70449cb23930c93c99c03bb04c229418c1d2547720e3b3f5f11a0e84f0f51fba5af4768708271f0d432dcd8c1f808bb517fca85b7eb18b4ed187e5fe7dfb29277cdf1e9b9278c766e79590745e3c9648a4a94b9f5111b59ff4aad2266bd4dcbb7515195fab0ba485c4b62bd46873652e4fe998bc4fe4ac1a50d4b0eed44010b2540927b78d2ef5310655f3234c947811482fd9c0c6cf8d57d82494189b66697dc8798404ea234f787ffd54f04af4e555e138802ff0328b4c181612b4d80208dbfe6fd60659c42b0cf8af21dfba0e02b4ae205ceb8d55ba19c43d801e50abce08d4456940b0b515ad01552167dd7149183d6d7fcc918d5c22d5a993660a5d87f30da0149d4d0b147d6e525d843a242acd3368170c7568ac5c4dc2c83b5623bc4197daa2e0a13c07552705d4dc3717ea70adad8004b6c51d8766d22d61599fb693ace46d0d7edaf0a79f426834864b8fc06c7faf4e63cff3dfc316a740bde3001f18212b212881d40245ce78e0ec4fbc0ff945338c6d62800ae1449d7404da370037535090fb2dc3507c93e817a9f9b5dc5ba646915052059b0130c385531acd4a53d6ef234cad7165e4983c14920a6b1dd439844c3e49a52035ee80c3f81caf89952fc057db6bc1bff7c84b99672ba5292ae0930bb2ffacb8c8c6eaacceb0de43af7b79575dc5e36931cf83f68165942fb9de822b40f64fb78069f6d9c4665b39d6c5a356dea6c7f23c5f638119327621b2f5921f5a38475344a68af2ac4c0f4c06f1dac3127766da05c31da5ccbcf18b6cf3ecd417178c808cce370407c25219795f8fd540c9cc89977073c9fc4d8638d7f1b18a056b42ba00da1a4ec79674148924d98e4d9dfd73e5260f663507af88dbd72c9b713ed17a919c655c4fca9b71693899e4d54dd0d7fda5dee217f563d1dcec94593801d3a5284a4968d5cda17684dd1ef507c10caf1ca437aceb7d0af69e89afc4bab4a19d5a3cb79d2061cea20e49495ff6ff4f5b6da38298de4dbcf9188b581a927935ae7da7395a63d717c8ac5eb18f1f5915e6dcfb781c4092d23fa99817d889bedeb5190597d71c448f9b70346d64334d17cae44d9fd1cd6abd6a1d1d331ac63e837550d40ae851ee34b314dca48f9de4d3d458cd0c7b422494006ca41798f11c6fefca4879067a76c39bcf38af2791c92ea1e810d041f517ea35690b4c32cccfa2ab67cad629c718014d15d0e79764f1e06fbbee7388d92db5c918955692f2e4e6ce4802bbc02cbc6016b35dabd0b3d7c5e6c50973edb775a83f9f51cc05e6674d572f0bdab7dc19438ace4fcda2c8787ed7cc0094b21c5127c6ed177f6364cdd5e92076b7d9b7f9a4859ac965fd2e454bdb77ab009e205a2b8cf0ec12c3cc6800e2745c3cd5d021e34bfb6cced466231f4446b3b0843de787a01c1c04d7b7380246d95a9d82bd2cae6d967b9edd23a9f8e6a2a61f96d5e277650b2057ad66aa25138cc5e91266e3be80733d781805527e225d91b044f2b3a1cecfabb3ae8e095f8b1eafb2ad148b8c6d410701f6c4dffecb8f431a49e70f337d1dd584b9a6095069ea7d87e14402c5483ccb79c815860349a6fc9eeb639cfd52fe2fcd5808d066a66c6300322d74e199fa8c014c78b5424abc5949dcb81c7e5cae3d6dd813a45c4582bd5bfb8450b03ae76ee5ea2ff5a1a3db944a2d5c638414ed72eec1656b46e2167f77dc9761e4243021bc45f7990b953ad35eda2538395ed7870f350cef1ba503aba18f172dde2d46de2343e22de42d3ea1c2ce175a73bd67f36c108ad619e7fb06bd27262e320e95d21774dc1b8cb65355f3db34c36bbd2f596b1e4334be8265e6d27e6667a8e163336ffaacfec3473d3bbedfd5b8b964210594fd36304cd97cb09daf43e20eaeebe79f08829e0700e685a3f98078c98e302a0f5fdb7d8d3a1ab30521ad8f42dfd27c7f9974f55f2978efe8f1124b194eda34f5fc79a825b8868e3796bf411bfbea58b0afdb042941457f3f321dc53128a36b89dbb2dd85bb9f96faa21961a3448033f29b2ee181443999cc30fea01d5e24ccfd0c5463eb8f51e9a1a8983a30093ba61393e1e8aa5287c92009f1a7058b09acdddee731e7a6c4861afc55c7091f2aa5c8ef698fa39eb79e0af6ba2eabbc5df753a0b36cbdfe42d955a3143cd93271cb8f012652c43e54775a31cc3e3c9b60fe8e495c395937bdfb8422a340a3071b8245a12c4f2480edc87ea8fc93101fed600f825a4314fa0482adf0eacdb07a029c1f2f07de4c4fd3a5c0107a6631078867a3a812c272cd2b97addd7c3b4c0c7c19e13396721783c4f9272ea714669dee5c51d246dee723fecfff678deb7c37756c4538a2b1945250e67c6c117e75d0a619163be94891e7f13ea29b7fad1edb8988bb19ca2af144ac13acdabff82d07dd1a6d72f527d0c1c988f3ed1bf0fa1cdde0bdec42b838035e659a8b2c53e1231ceb843235bfb1d7eb52dac6a6af0dd4feda255bdfbe90a80d5b24ab259f358e4f1fdd42ce99778784529ce1088e9ddb178ac67345e4c80bcce2120715a1ab4e17c0790abaa4b395439b5153ec5ad3fa4018bca598f242c5200df5c3d4ca72f72f4236e70b797355843533b291a40c63ad423ed5c16838c586142cefeaf2f4073fd06fd1198db10d4e327a9454a04fdb0afbd25dbde2d15179920e06664e6986a9551e6d0e750231c91f81065aa5cc56f313373c110c7f022e05a39b2b021b6f59d28733088dc715c31a410483dc68f022e9286016b05ca4a306182b829f5fa3140aaadf5160359b14eae2daaf5d9e0e55d52a97313d46c0ab641de352f092cbacdd880d85e0ee3be096076fa84c23cf3280fe1d4406da0bf9f233e1ea908944b8bd1964c5f8f0f765ac4c0fe9a9e0dbbbe42a6f8fb8b4b9e423fbf56393256669012a4dc005c89325a5ee52f14f98402e22b5bb2ff04692007853f3f3a376c7cf9c23c987b5e443c2d488a05c8295d0f0f84c6ca7360d3f22f3632f0c30377167e8d75b10c515fc21ad07824e0ef2b53971f73edaf1653c5ccca861cc44cae8354b3e92b4d99bbf7e590ab7a982dbfe883b872b2bf4c72841209f0ca3895360cdc5e7e484738d7e4b6886e227c1e2e2f1526d583117d7c654a75688b0c0f1310b6cf6f2c7dd49dc3a84d0010f2b83839852265f1fff3c307404f18b5926493b6de12288b1e703cc5f7f5ea06ef1dd2011202c80ffcea302c6758de23336d03c5d25f3df577790a4de49efd09472ba626a60573fd3320afc9893dbb35cbeb08014003cc61bdff6bb1d5c46590164c2c7a0b82f1707b8f42789a6e9b96640fff53f71c62840ddc5eb34aa4d3f3ffd8b9cc782a993dc2fff8b636b75ee60087bfec016c72cba50c76d30f9da80d8638f029d4db221e00d40a908fb7ca99f32c671387cd3448972d84a69459dd1a17f1b40e5512b04e46cc28b5c8156ad54c13720b0faeb614a97e08bb4a78039ea027bfccff6db0637cdd6e0ec832423ca170ad3deb57ffe3343c34f069a5349fd8eb8de975a56587044472f49b370550fff00b16e9fe39ce7e0afb4b8c0e43e234a4db043c88d04284e5228cca274ff92236ffe0f68ee67be398a65dc407da34fd518f807840732e79b881a06fda39874e17964b0f4742c2714e3e70d92055827725bbc7942d9dc4828d3227c7735267230ed24fbad8e9883907a1e336963125a2366390709741dc89e9448fdd2145be4e6d34a4d1d91470dac20b4eb76b970ca456f8572fc4633fc2598247b792687b1c7b331a3b4461f0012e4e9c9ee9b04dc587dc8371141a655998648ff89e91ea9264bba62c8e825489d9c7b6913443b8b2141bea76f451f489d05cd8241023b948b112a703a173fd65c849ff96ad41837bd93a18a59dd35fee4e6f87a9e5aaf775041f5da2d8112a138c0cb8594454a45ca083886b4e310d0137d663668d80b30a9ecaadb345d561049d459bddf0ae98bcd165f3d1ec3fda3e856e095d7d0800feb329b39f95c9fe291876825792a3203f4cdc3addca09a724f687018a24e104c3dce0bf414d27c0bb2d14bfc1f5860f3bb1d1b84869e0c8949b867e390207d96b9506fdbd486deefac20a0b1fc459983bc0e0b98aee13da113a36ad2fdc654a2f9fd8128409c00ca213bd405f60f97c85f4332738288791db25ca328b4e7b3adb7c0fef638f5cb4b1b31e94e47b9ab1e2e9a13753e1ac41f849edc5777d3e79e911225d79a952d93ac11bc52b67451cd6b87cd75c58e5ec3d2c1790d1637276914bda8b547057b6f09f9056cff04dfd118043bae7b50b13af90a8cf86475d6391ca5b22b0a23d769a35ade080c36174db8df670d0b7555619623dba5f03abe959f440ef0fe46f5c3b243c71d9f7e8a50aea522d08a3a89548150983254d893e3c26c8258f62008eac8e3187c0db5942ae141e62412aec3771cd63e1fa224b42a59847c6e7484ed31b26a1fd8381cc0ac1b86e6873ad8ddff7a50b08836baee156592c2e418a7c6b3294d246683b645f046163430e170d4d989fae7861782e9e85a4a8d677f8bbe23ffc486cbe8efe00a4832bda64d452a3000311df6aaa591268dd750376ae9ae734810613de6931414f2c3d202c82bec35f761dbf0588b62177647db07a0e9f9643862cc51e6403f02914657a27f457d717f19afa00bf6084b6b34731f5b9964ea7cea07d5fc5a85c12a00210272f19b38803a94e9a412f7de54621888be887f213fc7d9c336a07c5cb0bdc4dd73508109047147332fdf6ac19d17d0afb263dea52bf6a651a5a55ca912ffb69c71e36c05dc2484dc2aac4d0f848e18ee5718edeb123600c02b8df0614883dd215e67070e83b980571522882f83a1e9ce8e828ab2bfe34f42558e6a9817745974927ae5333d244821f67d9df9c95495ea16745e861e5f70b72027b74e25f0d125871b44f3463eb9a068bcef7cc2e5516bb9f3328e897121b563e302d606248b579f57b2deaa605316584c1ee99e24c5a257a590124cbde59c00253c501317b40c2afce813c59075b831ad1e47fd01ce1b9ea012264450f8d083cb43541c6b772bb254f0ea63ece3168b9a27ae823339c97bf53bcc40755ef1ad035c9046fc2f43d0730498fc4f82c511b831d1f6f671ffbd0f60aed8d1eda8b0a6c318dd5006684b14fe4a9b0d4289fb208146c66bcfa93837cbdc17268465e11b3bc4ec000eba5b06edffda7bb5b530b8f076b6de88621c368be445efc6934f7da18263e4945fff49415072fdc2432368dc42cd6fde6a9dbc6999eb59bea5f2bc2c1a311322b57dbbc7d66815760a6245b0a8801501fc46d4c86204072ea71310dc2170ece85a1a4e93fdb5f3279ba9ef0d8b214e89733782b3073cd170ba670e47d832f8d44d8cc452cbaaeda04e4bc0c8a5243f98713b02cac0047c88d02d7be6a9e661c47367b2d984668ed2631e89d848a37ff8f8b7466fca64fc614be9c9da3cbc16fce7138d531ba6d1cbc5f82cc7fb4988c4693f6fad48d115a621fa5a1386c3a158c28c25a8ef9dc7afe78faa7a3563650455c591a0d943da8b1854a3284fb24f48f8520da6e6ce0a50ee7b2deced0701ec88adfcbced1801c4f10b605819f80522e20b15084141fbfca155e9687368b7bac253f77f83bbea5c3a0a03ac22aca32e7dfaa5bc33b3ddd928ab92af639675012a1f14c7ac8e4370cde2f184e1ede5465f7e7e567f3ba9313ea13e3b371c0ab55900b58463fa2b623f2c2080b8bd6f0f040552f2a3177953cf405d8d64f1cd6f260cd10bbb1601cf91239eef86da322d064eaba184c4ff115858062197f40b1b91bddc8db374aadd3248036e6107e12d416d5b45e4e0a87f5e7366446c84d16451d93f985e9dc5dbc64394b513bf93b560abd74eb79bac566cdfd7fb0fefe8da81f9fcb266991ede82bcc349e8bf7302175f6abc5a2416043252e160e04c232deba30f24f5b9cec79f61801c199db899468416c198e9adcb709034392d70076c5eaf585cfc1f102ca014081a18ae0a5e32bc6acca708a6c160c57acc3aa20169e2bf91ea91c10aa8a2a58b2d498c69fb5ee647cba92832fa4473bdb495e17f6bf2cdc42a82b2e2bc0f7aa8884b16f147021f23b06554a56a21f9b0d9f235bffb1a1a9edb2e3b012cc3777409541acf5b275561741ece824ac7d026f0c39ff21e284ecaadafa04c34bc72ec88c8082cc287295d9c217ef957de0e1bd90401db8dc3b7361777cd144deb06d680236367a2b055381d645d6330af6005ce078240fe72b7ca0c303286c50f68ac2a419106b6d863a9b97f5cd88fc70aad3b8cb8cad39af5f736e88bd98415ebd8574e562f1c614eacf50215e1c82a930f3e2e82df5a21da4bd7120bd320b8f2eb6cc6d542e995e34d900988f5a94954c33bbca43fb6ade09cc973c836d9230ee66a2c0b4831ad630f49691ee0a1cb1465ec1ae046407f225fa016553e46bfdc4ccf459105440ee49e7da5db84014e2ca337069a8998de3b3cc25aba6db94dac1530fd577f6846e09e4e1f5c9914d6b0c78bf7ff28e9881437cc0e00ac98277405a7e8066963a29156f6a41d2d6a9630c903da922c9d720ff8fba2adf52e117f595cc200ae9ef0e7b51966af7e78c5ca656c549b6d617b8ed4aa9127f7ae7d80630f5536ac75ad2b56300dc9e1bf14a71d6b7850c312c702bfae60d0a6d50f76e07a9e3f6347506f636b13d271468b01ac1605de2217f5f0beb2f2e85c26ab56e6a91a108275b0bf844c958b0af0222ae7ecac2b96ea4f56f9ae0aee3b7845a7dd181ee81575fc1f440eebe579d10078131b2379103566478b904b5f28680a5fc9f3684f84bd7eb99967a9f2278595f330bbc41d10f7088c540d0d19a93b65aabbee34b26f2d6002aa1dd673837adbca65866675b2a125a352fb68384298c38e825f986f052db5bfbd1216aa11855509527d20411442b376f6424aa518d081a9371b6cd032718b22a6f0cc6e9667db39525e5a671575a706e6e5e644bf8b3b158ce06bdd9593f11a0f1d641109c25c9c0ab03b9349633aa57318914c4702a53223eef1923db68cb1c5020d905c01d0dcd38433006ad5255116a3dc5aa7e084d8ab10d1f270d1c786724d6c8760784d109314249181fdeef836d59c79ae748aeb2fadf41ae298d92d0ddba53f1f1f1ff30ea125361a23578ad4b937caa11a384fafa1c4978c19b71ff2f3b609810971bc6be60c0fb31acc3ef21cde2b85fd21aff4958e738b981883b7f30dc7ce85aff73e4ba80d4759108954456b8601d3c93c4010b24ece63159ea22bb69f02b60db70af792a2fa220b1035cc745c6018a5e967edc1bcf2e9a2ead4ce3077810b6f5c2847b43b85511e3ef8dc4dcd31a6158fa8ab3ec035d8bd4f134eeab0bbe1fd52c5c141e06577af66599783ab038af045885f2aa6da28f48408f667f8ea5b59587b5c475a1b1e55262b217157993e50549d4927766119e674a733cbe8caba446bda7cef55a0364a41e1aaa69f965c139b829dc7427e62d36873d7e19081ee1ca699422c92bdf8288e340b8ca33a9a35c58a92263d3b5070c62ba0b6313ba837040f4639a22d34982149ed38fa1204117cc7af4c959e0650dbb161f97ec704463b16a1589a08c096da40464431affdb0d6c0ab84e554db7aea6cbc316ac951f79a8c9459daadaff17f543c9c4859d36334b7518c52e9e06dee6723a41f96840d2386d329eeb316709c1d95caddf882b8cfebf27ee199854b124e1adfcd81b3c1e5f4ccbfe23430cbf72b49bb05c7879ed1aeb2acb1bc7325b900938318c7104d8dd2057eb604426907a637ed7e19c6d7336d83d112b83f3aa5fc6ff0984ad06cc6b576f6ec62c38f37f7b5080ece2c62d3025205eec7ef77c2f5c618669f1e5bf20d792365e6c44d3983f83128179a89c7cce1f7f9add93c736c36eefabbd2f501d1d423199663227edc8b90efa5256ad8c8206dae862489644544aae2b5342f9a2f4b325477667b2fef471db6c1144b15fb04e1f41a87a43f14ba69515b4cab35d5ba326fe8ee6917f867b9bd4e50a8f2230b06c8bba75dfac5785815c846c533e0983e997aac1dc83ff73577f4740df9ca1fcc3bed13b611da3ed055b25b12780f32cbe708a56a79952bc6e3d23c99312246b90d38d3e3c6c2e3c55ff2f04ea1303ae45b3617988af9ecfa86c6808f1b6bdba20c4cfda71e6430c58fb6c0e9c82cc0d0a9a2e63bf8e93eece12a943eb702e748081e9b4d2e37fb920ccbfa6dde536d9f6dee7f7abf2c6c13b0effcd1dd4990a52cc3cc6687ca703675dd1cc074b308e7fa60d57076e56383d5e5f02e1bfe29592fa6ae0e8c90ffbd94e8757f89cbdb196d1502cc598368cba4989beb574a8ac3022354c30a9fcd6a560c9e53c4975fd7757d74334459df724a84d6706975a56094de3e9d39090389951b903fc16fe19e6f8c80716f7b9137f6911e40b5540f57c27a47c68f40475000231e8c613fbde599d0a08d9276c2f58fcb1df2f111428af03f2e9d7d1c942c4fa58c67eae69bd5e51400b097d544b6be51e8c28c66048315e3d5cdf79aebebdca54118d5f32f5e024144027a63c6f162cf51527d130c5a068c8a213318b7f40776de479df22dba62d764b5b57b88c7b864a62d088466307de16f7e2d36751ebc1e2c4d046d2d1b6fb46f14c592589eb6fc5ed105b73692674121b979988efaa673f93b32dfb4641ad085da16e1e3a2cd9a9abf9678d3c6db4ac74c51132a0bb1d858bd9f247c016bf870a2dcb824231408875fda4ef8fa2c52114c7ee586e213834540e622014d6acb682ffcc2cccbfd13571f1266051578669dbd9622319359d66d27f14042feec254952d3549d8b027bad35780315b0657360aa18ef18c10917a0a9937d276cee2e8a4495b1c2d7c6c3ac8a06a69f31324b026b0295fdb3474d5b3c1b606eb62d851a1e2d84fc3576f99a300016aa356b11baba1e0dfec87eb544743ba15c92ed5c3565706ca8b521e5ba8631070dc1d1d63b5307fd2e871acc1268f0da20719202ff0f99f7f0f53aff74b773676415e9b1455e749d291e126bd032798675fd4725a2cb877d0e83d9840c74cc6e93d4c0011ca01a4100e396ad83ad8ba3bf80d3e3a93b4048edd829678d3bf9060af91c49c06c5c92d5d4827b310cc21d75817f02cd8bf4515155f90812d30ee24f69743314677574515b5c0463fd3a49aadec3d61be3067e41755a76cfa96e44d4dce5990ec80cb998ace89e5c8c728af14cbbcad72ef6559e0d65100e879db9934b71c4856f53a49b490509c2984f8612793afe9f285d17338e80a47ce8392239a1c451025f70ff2390538b89a5c1fade36f54e28cb17391294919f780f945fd047b639787c588ff4e9913e77348fd09e8ffcdad96fc16f52f72967b50d2de81ef3037455bec94e6e6f36809804b7306776c639daea9a85fe120c60eb6019526d3493490f65d5c9f4d7260b6dff838b311e1240f849c44e40a8a082a86843461a033df32f2c5f10c13c642b22563f15e83696f71c875294cf5706c5fb10fc7feb9ac57a4bb033ae537d35b204c4a9b0e6569c85a1e4ddc8d48371519ad438a7623eaa621a57934518f2d867b65d6b2582daee8f78be9db3a1076d945cef90d7edc816074cd461f4a71171da3536e38517aa1a7c1bd18433e0d86f82da4eddd6defbdb79452ca5d0c400d950d3e6f5e740975cdbaf8fce0bafc74d1527e6a3d2009cdaad4805cec0431010a42e2484bebc5dc1bc82648c8c802f16f563d929511f2372dbabab4173f59e082b69c28d2dfb188b60555c1e566f563f5fb665d50fe7513f250516bee2322fa4d73376bb0ad185a31b482546188288bad5f42d1b7055b39ad9e18824b4b1f134254c2ef9ba5e2eb0df3d7c777909f766f3368eb9b762b2202cdca2ab38ab0ef25dc8bbffda6159d3d7c0ef45db384be2dc821118538dc02c2f466e122310516687ed3bcb7857a1f74a48505373f852a0e4cb418af5bb8f3ead14bcae8a814abffdc9b8a37472e4a513497545cc446bb40eaa97d282da791bedc219544bafa8f0a8b045ee1ca9aab9b46b33547ab2e54735724faebb5e63edf483aebdece68ebfb66fd75f24559ab8cfa0287c3e170385c08e6d09c035f0c94ecdae541b7d977c6212d0c43f4b34f038be70ebd04bdfcfc9d57443b507e46f78d0f511bdfb9bffd4380fb7dcf0cf913c1d26c212cf594481e99ad79953a532289efe7932c711903e641871ec2768ef7e03fb7ffa15faf52889a427a82c8311ebd3faf8e1f077f9fefc68f553cce589724974cebe1943c3ac4d9cf67b73b1e64d92b566a679633b618b63dc0284f3993415ba7953c3f7aca5717d1a77c89117f4acf522b83ac64a0d287082a673c08959a4a9e59115f10bd3ccd267b96a083e7be9f7d67ff9cc4e78be0f7dfb9af8a526646b495fa871565d00e43258d4b262ac310f2ad34a31495e411162579a445191b221a95f52af9ae9edd82f0b687ceeef80fbed6a3b21c23ba28431c2c5219b4c3952f26309e9030cca68a3fbbfd218005ff7bfc96f715c328b3123746592211934ad04ba6a0fcb47485f1028743ab63c4dde6aa458b8040de6bc3e826eef4bda86851c8b68635ac210882a07f3db0f2c4ad5fdcb4174317efed3b73e842cf81d0bfed1fa29846d31aac9ed1a334dabf3ec6f904b77b6f89b1115a6febad22997b6f0c245555adb5d65a6badb5561c36e24ad1d55e115b9ceb91bd169b35f2077ee1a7f1ecda9b74034caa3c2d74a9e88dfbefdaafe62f15fd7a908ab808a3903f10346b842888e2f88de398c7118fe3388e771ced388e63ddb99eb3d974d10f346b5cf3828bf3675e604bb3863541f333cd6c9a26bed634cdaac5518faf6bab6e6a7ad9d7eb553fd8ce318eaf175996668d7bab47d54d1396c1f0af63fd9a69dacb34807db234611606abb9b437566dbaea982607f60d017009dea233186d26c6625acbec27e1e0dc9a8c6adcb7d5235a53adc6fd593daaae5303415f746b2d3565b058f176496c9408ae180d92967034eecd4ffaa29a878746c3341a8d766d85c1768effd120968afefa6d7f8988e218414ae36a194e998473c63b7767c7eed411b64d2093e1e0e4e4cc664d3bb527043f112747cfececeaecd096684b3c97c77eb3593dd2d1d9d9e1e1b9d1683d3d311f1f5c9251ad4040f8da71c7a67b2abe9687a67b74896bad866bb55bb322cd6c227bcc1a3eb4ffb13f15f7f8d89f5bc340b9abc6fd1bf5a866f0d57b86e53d77c5caa01e55c7251989e8a8e26523cb7bae3022cb219308768b156916b2f29ebbac718d8c4a23f2351a1989a191ad3cb0d247cf4a5befcf4f2da82654ddab9baf4ef6ce1345672a542ccd48aad5665b3134444434a335dd6645452c74ac458b16b0162d5ab468f1657c5bd8da53339b6640b4594dac41b66c02f2201bbed1846c4dee33dab56fab3f43f1b5280ac27e0800fefdedc15f5488f4d3ac71ffdcfaaa5061eb07733f4fb309b5e605e58a3a7487ecd0a7c2244036c25d5bb1b862e887be2811d1ed5654c4a285db6824d228864848e08794f145b24848485506db3986cc26a2dbad286909572b8b92058b580bb3a9fc8c32bed6a88ab08de38988cc1a96bc990618c1772002dcad88450b23b3c6fd5a8f8ef0b5b51e552f329bc4ebf8a2b5bf7bf6778bb6d66a3d8b9f495cadb579cfc0d246ddba3316b4598dd455377f6b43f445599806b06f74a76817481e64fd30d1d769607bdab03a5ba4ea464797cce72e3fdf33a3d8baa88da02fba4b24dc8b180cb3a9c6b03144d8cef147f90343717c91a5098b69d9114ece4c678787d6e3f353030a12f21355615b3144742b62d1c2081f5d5b9182fea21b036f510838216bade5d72a02b25eb2d65a6beda006fe6c91acd7f879eb625d72496bd6c05fe33e07f64350ddf2e8d23d3615b0f315bd17a05ffebefce12f5f20d82ec1570ffa8a6e0c7c05edad6086596b6f40777e8ccb2aabf7265a74e7382f0dda7a07f9b768e27b4b6c7a366bd8372ff89c03fbe641149b4d1fca817d83f3f6e0e782dff96d8da4afb37b13af12ce68ae471fd8af393bd5bd4935b951301e3111b8d136b6641b94c1d1204bc4e0deec6bc6bd595ec525e5d86eb3269d7bab269947c7e78869dc11631c866088437c86a82649fbb9ce5967b9c4d556b0fcc8b004cdcf926399bd963d332c3d2f1d7fc57268268c06334d8f5d530d6e8b5b6f9af6edbe6f1bb797682ce1a01b67f418c750186aa2dfb967dc974ece399b5245659ef50a559e505010134035dfb8194035b4de22dd0d70aa8914ecace68c2ddc52122ee9b7d5cd48b28b8a9d1516b4d45bc344cd05f57a3d23c8a0a939a0ded501add15253a6f6e3fb9644d67ed07a8b2aaff2ec54af17c512d490277dd658121b9cd1cde877e525f9b03caa06c201d56a49fc18fdd45ba4857323029cb3ca8cb8fc86baed77e51971d9d8711a971c4bd1dc719db3feecac9ff3af276b8db465362f7839462f4fdb12aa9209355743894aa1d26bcd95636c24fdf3986fd14400e9a2d3b8d78afd604755c2f51642cdfda061b53acb542bd2ce327d20c7c37842af3527f3d07f646809f5196791b26b748bff64c2d09e2570389e1de7c3e39b66b348d849ecc383d65b647b18e7dcb5d7413e77199e3bf6dbeaac91cffe9df5a6b55366038c6edbe3711cc7711cc7711cc7711cc7711cc7711cc7711cc7711cc7711cc7711cb168e3e91609635ca3618c6bbfadce26264b831f1feffdfcf47a3e3ebd5e8f8bdeb6b7ad4e4bcd56199e1a0d9ce3a9b7c842ddf8aeba6050b3d2b470f4ad67b519361f1eda8f4ea3b550f125e70a638c4b1f2743d78e4d04947ebb91a10ce63c8ebd5a267be537b7dd7078ceb07e20c7c3ec558865e8be619045caf64ac7501ae65ddd5bc6bab7fbc4bb4f50ecd57dba4f3d3eeba9b7483c79f6ca066d2909c7d333e3e1e9e909122228485ba49186e65b346ef1a9b43133bad974199ccbb7a8ac5d6e64afd1b0d49beba191e4575a74973f3aad6ca1a2462dcabec31f259863cfa9b91ec75e2f2ecbb284f59c5697b33c5c9443d27765122b52e915e631c7a6024ab7569bc7ea62386e9964ae694c342c920df4deb255118d2d967bcb8e516ba54f5c6fd86967588bb065aaf8cace0fe44000d66e9178384723866627419a571a6aafb2e72b9cb33d5c943d6334db5e6d076d755769e39cf22e9fae5216af52f8f5b3e346cf5e9ef8142dd2e83430e8e1299e1de4dfb8f2756e1be563073fd06cfa5ce7b43f3bede79cf6abc539edcb4efbfab41fb36fda27edfb540bf47df4733c2e3a46cff1ac16892c1d9f1bc7934e0b63be5b6c15347e94b4efd88f5e6b8ec777ce8fa66fd88f5e5626dbb348d9471f3d8fe8b661f4a1eb30bbcf4e8719a5dd9b0ee7689edd2d137ef3e685bec763354773acb3e3aa84e3d8c39af3713c3a769f33ac45a3e39cb8de46979db51ef59ca387423f3a6d44f78def47c72602467474b4bcb78cbe3e6bbd6df2b1f39cb516edd863a791ddf62c935891b067a2c71e137aecb0d76374db3035ec34cf7db397e716c1dfc01f3a79ee325b2411c561fe7c7739d7fa3882acb67d9f1ef41ad9e2ac35fad14551747b6ea3174791c6c5dd72a45be748ab93d7e2961e74129be6a9c12d02fd737b7ebef556f7d662b3e9739cf4721ad79ed8bf93036c36c57cebaf9bf675fbd77dfb5ac5ebb1f3e7f398cf5c9f3f5976fe64f4670132375d03999b9edd4471c090510db4eba0384cd78e63e632cf91c2ce9f05c47ce63f1faa41cc673f1ac0dc74989b8e73fe7caee339d29cf3e7437f1690e338ae418ee3f8e738280e183e54031cd74171fc7cae7111e839521cc7f1f3a11d68a0e33bce809af2107111e833df4171e0d8f11d5407cd91e6788ed4ace9ec9673d614e7aca9ecaca93e6b1a3b73a4b033475a9e9bf6a0db8bde001d708623e8514435b8b70c666d34f69b331de499e8006e1148e3de40bf4ff4e719e4815e6d20ca81fb145ef108f1c918a9b3f7fa6ffd585b5d35650fd83155af32b82e8a9ad694b484f6f377edda2da0166107a32bc229cdf9bb5e6133837a84773cdd5bfd8dabbab75aedc5b0ab9b45a1abf7aca0abdf1834f62aa6f8695c5e0884d669eb4d28f5d04081ed495276eaa744516b1acd337abbb70e7a3d124dd7dfc09fef0d076c8dd52ef500e7a3862a1e47a8c0c3619db0814956ea81a52188375569b53136f8d6ad1367c04ca5624ef0822e253f304c9a22bc948c9d3ab4d15b8cb5d16f64527052d8eaa51e845ae59945ef4c3eb1a384def982fb6280fce0a578aac8d653ad328e2c09612bbde77dd29638ea1345cce8520f88f45b5bc29523572919dbc9b23fa4802295064037e429256f894f2bc5c30b4e2989afad382b398344554ac684c6e82dead03e2488524826ce2abdb310114ec24e113d85de59d64368081cb687901ca4521c1ec89670aaf1a4d27a29d8b7b7be6342ac4b6f114848ad0b171d78b5fc02480fbc948c65367a67120a718a29466ff127434d255195de1d9c46d8a564d797035010bd45924de53255a54befe39cb64c38ee94142698e8520f5e5fa052187e0c50cbbcf55ae62dd2972bba948c8950f4ce2fc6a0ed25a9ce48e19091e155e68c68268af04c96b18c11b90b9b714144e3842ce36334234378e6074e991e6740087110c93019cb44e19cf9209b717a911973660d4c099c32278468869067ce9489612433251cc28c4e2c53430e9a15341925f98c0d26192e386782d065b68466bc8848f81c09c9408166ce8867ae40abd10c980f4d8f98192be11929ca7811cd2891cfac5006cc2884c606bc4a395bf9e94ed5dedd81533d98dd39ded226c8d381bd114fe55de5e5a97a759fb62c2feb70efbbcabc7b05ea763c7c957b1f0fdcd9a99e6eeadb029f2ecf5e7d4fe0957dc2b96f07c3d75a6b73c618631fc771b4d6da529cfdadfd35fab3c7da9f81bc00202f0dfa96f8626c36651367f382ecd96cca68fdf087718da4c7582b3f59afb5166374976fad78f517e77bd1259eac38e75c7ece18573dce5586f59cdda2376c6dd7f51cd7f9b9680633c7f12eebf312b7c02f5f9f9d77ea52603de7bc5597878bacec94f9f8723287312dd3315849be463104a770ce56657dbc9d9f6497b481ac7b09976b0f43de7a046e4e5bc0ba8d9bab3ceb343078eb349d75c739a61d64e0e95062862dbad4faed955aaf5d5f4d5bbb1e0ce993c4a555a0db03afc2ed49a25eaf52be81ff7a154dd0d77116579cf5ac16c9bd5ea50c8a3324fafaa967444367bdb9c8af67507c95344f415f17aa79d1adde2c0279bd4a2dfc7a06c55709d3b3bf6e33a2d2d7836e462d5814d59bc572f47a958efc7a06c55709d33833da5fc72dbda0afd792968e8c5ab0a8378ba5f6f17a9590fc7a06c55709d338b31d9a8dda5f9f310021fafa0f8d0120b938326a516f164b3d7be1d73328be4a98c699edd07c6a37bebf8e410647f4f59e0364f00287e4e2c8a8de2c967a46e3f17a9562f8f50c8aaf12a671663b349f5a905fd7200241f4759a03221003c60b1c928ba37ab358ea19ad76865eaf12057e3d83e2ab84699cd90ecda716e4a8edf6d737d080127d7dc7021aa02029068c17382417f566b1d4335acdef39cbb93832526a41018ba4a2183718442f86702b906c36ee0ddfb837ace3811b39250a9262c0788143aa378ba59ed16a6ebb275801928ba325a35c0b2516141425dd6210c1187ab10267f37b5d04ba26fa7a8e57b3060e5d054b39250a9262c07881ab378ba59ed16a6ebbc9bc5e2526bf9e41f155c234ce6c87e6530b72d436746361e40297c4c3470cfa3a8e930f2619152ce59428488a01e345bd592cf58c5673dbcde89e00f0eb19145f254ce3cc76683eb520476d433716462e703092969ea060d0d7f5102800cc609251c1524e8982a41830eacd62a967b49adb6e4638b3bc27cceb55b2c0af67507c95308d33dba1f9d4821cb50ddd5818b9c0c148525a9a119544067d3db64b6241000030834946054b39250a9262d49bc552cf6835b7dd8c7049f77cbf9e41f155c234ce6c87e6530b72d436746361e4020723496949c68c1ba0d4d40afabad964ea6958100000cc609251c1524e8982a418305ee0905c1c19b5605174231a5a6143cb7b0ac0af67cb94e4d741cb14c3af8b9609865f7f59a6d4af9796e9855f8759a6f7ebda32e1fc3a8e65a2e1d7679609c9afef58260bfc3acd32b9f0eb3e9629007ebd66998efc7a906502805f77cb64e4d751cb34c3afdb2c530bbf3e649998fcfacd32b1f0eb2c2c930cbf6e64998afcba0bcb54815fc759a69b5f87619996fc7a926522f2eb4a9629e7d7972cd3905f97619994fcfa0ccbb4c2af07c03251e0d76958269ba5baa74f94be356c156aa708e04cc1bffeafbf4e03f6d72dc0f9eb01d8f9eb00f0f9eb3382fe3a13fad7650cfdf50a58fcf525177f3d07e3af2b29fd750ace6a91b68cbf9e74568b743dc6792dd2751867b648d75f9ca245ba8e3b4b8b741de9d416e9ba8b736691ae1f9d348b74dde8ac59a4eb2d4eb748d7599c368b74bde8bc59a4ebb7d3c8225d273a7116e9fad0996491aeaf38972cd275db39c3225d2a1aebecc53832988af346feebe8e9a7d01974029db5f3e7dc3376c5b2abd563a761e7397715f3d8774e9d7357358f7d76eecaf5d873308fc72e3bb7f5f1d8f5193bb78d7aecb073db248fdd3ccb735baab753bef268380c361001cee5d44ae15c4ef56b44f752e589e8ae57352bc420baadcee943b785da6574db295bf5d8df1a599135337ad3bee646b8ab847f30fa559ea59a5279336e8052533396664425594a5a7a824ac2253df62f3b68847bec9fd1cd480dd7cd76eb69d9dc860d4b0c58fb40daacbc38dba83df64f033411dcf0c00c475bb23113c10d6db33133f280164149bb91fd46b9717cf938679ff349578b847d8be5cdd5d42784708a2d25903085ae8717352510117eb50732388ce912c4051282209115e69081d72386ac333a22241ce1252c011128d4c0c584294af092664bf8840b574ee083102e1f2768e120d38da03ba1491256824033259c80133444818035c58820cecc109aa0cd1340bcf940c90d55cc3819ba0a13840a6d94d47082324c3c18a306092134b4095302191680382289102098b9210d162354e0208a287e2051831211ae1491c2f77ddff77df58a13595db6a420458931bd20e80b1282d8f0f402991344208284f033cd0891a5a3049e8e26d23cc4e7127a7cceeeb7abf47ddf057a14896255e487ad11c6bcc981e9f7c1bef2802893132409149128165a5882462b3bd1c2536865245a4bb4866811d1ca45b4f0105a369c3f3014c717599a3272776f37c9bd5d276153f776a9eeedfacd57e92a5da57a95ae52bd4a96eae62c8f3745eae6ecd454b5bb5d92a79bb34f4f96c7cd599dcefaf8ea22b8390fd8b8b90d947a5c5f7d833bf35a97d06abe337af59b118f57c7252dd1bcfa8cab642ac1bcfa007abc3a0607c8c0c7ab6be080085ca5254e9efe78f59f2278158bb556349b6c93aca91b60b8cbb29b013211f3b4f419b9929461c0dac2e117ae7a46d76cca41ed550afa478bdef6ca8a59e2e469cd73fcbac5ba01a85da59cab94e3d76b39388d2ec5253f3f3229f4ec5ce2e3398d0f49c6a2746d498fe7343d262642dbfcdeee121acd34c99c451b2de1e11983e824dcbddd253bbe63863df48ca57bbb4b7496febace87b7f400ee9299e734da62dd227b756fd7730b1a837bbb9e465b191220b09e82af3c20a29e8cfd344086c3c608596276a9cc6b7aa5eece5c404d655ecd05dc5486ea0ba2a065cc4e8b18aa1f231ef49002892198f86063450c1018728200c2abf22901f1c2e585228c40e31466cdd83543ce194957948f1a6648a28627217ed8906f789203c8981c80a0d2040f3acc0026081f6fc2207e8040e583820f614029524723a465e9ab5dad6e8f5bda00575a57ddaa3b874e14d19d6347e3310284fe43b13f4675d809692bc63bfb5773ae18638c6eec1f8a2d768b51119bf12763d65a6badd5daeada005b895759342ec637dc92dbbb4bb1975729fba4a0eb571a37973f2760ce19dd9f874e7edf9773fef2f77d9f8bf9dc95778a637df988e67bab34b1ee172abae39cddd1b2aa156d49e3ae886efdb2ec2097204714f1206a7bb6cc55027368367d4c501122cd6fcbcb4df9bfd3fe0b225c31e2b7dde51d50570682d9e7339b3218fa7590891e65d0fcb6f7011f9cd83f27b3d9f4e126bbf572aa392ecb91c95ce6158699e7380e1c9fe5a01e5c29a63ce1a03a1cc741af0e17d92e5c64f5059900b184807db061041f9cf8d064874f903c7ee5f9f0f47921ab6d83bcb7d5b1e8bef2aef06aef2b0f87153e3f551956ab1e5906dcf442e1a211f4ce4f6015ce7d5074ed74dabfa92a9c9351648b148f87733952eb9fd5778573301d7cd8f2a44bad7f5938b7012ecaca3d9c6bf1c313434ce952eb590be7725479f3e6cd1b2bbef207e22cabd1fdf5defa063897b770917574572dd4b4254aa2dfeeedd3db4f2a4fe5aa9ef54feb09e7729a58c2422fb59ea1b670ce3a815579f76d655e9109469e9cde7e4f34fb41bdfd2eba7317a8ed2786ac968903fb1d84808310fc1af56b3db24cf5abd71564b56dbc55ad16abef8d096d7d6b7bf1ed8729460b153d543867c99f0e4e4e6f26f9c01cb41330ac584211872c16e49088664988e37805a7e3647c895680bebcc8320baef270608ad14205f8611ef766734296592cd83433486966090096f16f997bebf162c24e007cf51ff79910b0d80c724b4c972bfca631415928da105a2643eb9d37329c0a8094c0c9917a5a3d9a4de5cc723c23cc7494b6e8ec7ca1e025c40ecf0f49578e858796f318ee413c5aed19abff70095584e0e7617a024f5a7fdd678773396a7803b5440a13da30712487df3e5c9ae8fd20c148942474e9f6c1f2dba792397383086f8cd031e44dba7d70f8ed63a5c46f9f2b1f2c2d3e44b444fddd3eaf2b9fd795cfabd7f3eaf9bc7a3e3d3c9cb33991850c0f4c7c6872c495df3d25b061099b10ac38a10211e9eeb1f2bba7891d9a10f245ca153247d2ddc3fbdda375e677cf16d70b494f941792297f774fd54baaa7c94baa87ea55d5b3fbdb23f5d7693d9cab5746b6ac382199e2cd90b4ca6f9ad65f29765052f5419a3457e9a6ddf09be6e2062468b2a42cb1cb92ee1e2abf7b9c9e86fcf5dd03d513b5040dcc123431fe9b96d545ebd245c37a39d1aa5e4e34decbc9eaafd3743857a580e255094105104a3062ca6f9a5391d490ac1c74d0ea6242ba694f527ed3a0aad090c58b0b6894880122dd341b7ed3a4fefaa64dfdfd4d1bc1f08c607878d4f070fddd3c5dbdde5f1eadbfceb3c3b96a35441026a0f8e0893354d228bf79a4feeea4b2d2a4f0c31133e9e699aafacd531535044b08266868d811916e9e1a7ef3585d61fdf5cd93c5236664c243e49944fddd3c50533c3ea67886f0ba783c3a1ea7bfbec3c3b9edc48ea9a822524cb540c3ef9d094031460910281cdc2c49f7ce0cbf77ca1c4105c994a91a76a090ee1daadf3b5a7f7def6ced70b5d989d266cadfbd5335ea769a8cba1daaf169076a7cdad98d3b527f5da787732ba24822a48d14179b23a0fcd691009728322d0041c20e27a45b4786df3a5d1948212545083abce1724af7ce93df3b4e7f7def3ced0cd1a203468b98bf5b27eb4aa7cb950e564fa7aaa7c3d3b1faeb3a3a9c2362d3c49724d448c98a4aa77eeb0899a201092c4f5c31a12add3a4e7eeb4021400d1537663e6063048574ebc4f05b47eaaf6f9d291d2a91481b91889bbf7bd625eed4883baea959d6546f4aebafcf76385787f0f1440813c61899c22385e1f74c2acbc9c9852d4e4ca124ddb326bf670228a2862a40a208614c0ee99ebdf07b66f5d7f7ec6a86956646244dd4df3d0bb766e1d64cd4e588ba99a89be5d0c0820845ac10a5072e5f5cf89df332b264ca1a2e19485022dd394c7ee7f040a50405ae3146207993ee1ca9df395a7f7de76ce57069c989a225674a4e931caa9edf39555739505739bb1c29adae9c9d43fb9d133ec1099fe0e0e0d07ee3845538210e1627281ca9bfbe71a672af74e3508541da8441dca8e1e2f92deb0aa1645921542f84d29221ca191f3e64d8c1ab926ed992dfb22aabbfbe6557322c7009191170095994cc876cc8ce6f19545757974cd7e56495a5b7d65c5674142b53feea2ad04a37b1a2cad250597a9725f5d7633d9cab65821882ca961dba9a3ca5b685df312d28944cd165b1c1865dba634a7ec77688b082113b62d0b9a9926e9de4b776faeb5b3f6910490c44128bc5b0fec6b2c0581518e38131abbf1ed3e1dc986508104ce0a16b8824290bbf634e3574545cbe484121871ee98e9de077ac2e2143171790eca6d44049770cc9ef98d45fdfb1a918d5126d9670f317d6f575c1d47c5d5ca0130c7482814e30d80ee7162085cb0f2976f84164f71b56c14441452607246d8c18936ed80abf613ca826ac4ee044854c0ee9861df90ddb3018961518112b517f619f15ecb3827d59e69705fbb260260fe7727c70240c12416cd9c1c41babc26f33688a561b116490a2254cba4d1bf5dbbcfab1e48413aeb658e121dda635f2dbdca6c985c48c8264ca5fb3c95f73eaaf592565424999bb6d4afdf5b287739509146890f232246a2b4d5ae477a95501178e58f9a1042740504977b9f596c8efb28b053655a474ecca2c916e13eab7e9f4d7b7f9640ef97494603e1da598b24b89f5b7ccfa9ccaaacfa9e4edd2eaaf973a9c3b40126b9ca0e1618db92185dfa5539625b4788152c24b0c26a4bb44e17749d543af0c9621b8146193eef284dfa5d45fdfe554ceef92ca0bd9c68b9bbf6457ce22d5e42c2e2d324baba7a5f5d7c91dcee500f2e508167638e10986ad21bfc91ac4944eca0a503790c025dda409bfc9aa024001b36608992b4c82a49b14f29bb4faeb9bbc22b19e90449e9051a40f72c85f12aaaaab8ad46d9287733c49daa059e2e4c7961382fc7e591d200c1145b4803ca52993eed7d5d3ef57d60d26f8b801ca171d2944a5fbd5d3faebfbb5f5e2ca415e517290d794579317d5df57557e41e5d72ebf7a38577b505d4a1429e28d115da92de1f7a895801d2660713a8344d491748f5b16c8ef71851b98b4b015458519a8a4fbf5e3f7cbe9afefd7d36b081ec1e0514cf97bccc263173c62e1b10a8f3c3c5afdf551877320b841480c63c490b01322f5f17b749a8185146a006303971ee91e9f9e84df23d411dd0c312c41262b041de91e7723fc1ea5fefa1ea7462a6c456c83ad886e443522d75fb1cb4accb2ea59ed706e5cd2c4901e42662823432ac26f512a07942f49a69440e5871ebea45b9c72fa2d3ae143161eae7a38d1a149ba45de87f05bb4faeb5bbc12b1701091080e2246bd7e8b50184af4210ec1525d584ad461291ece6de089335474d0d1c2134a52107e873b4c6d21c14b218a3031a43bbcfa1ebfc3ac0d58e04161450b0f880ee90e7b3c7e875a7f7d875b215711619422c229e3efb04a2b6ca215527585505de12e94faeb600fe7ea159e0e2d4360d5d0c312e98edfa09690962c3fd610c9024256bac1ad0f7e835d7fa5da8809a31b42058974873a1dbf43a7bfbec3a7bfe1102d20182d62c474835db0fe825957e0942b100be85402160d52449460f2258874834f7f75bf412850eaaf6f702a074b37487589b4b944dcfcfdbaee4ecddd714d7d5953bd292da9bf417a28618286aa89287a48f737f56f7e7f559fd55fdfdf15ceef0f2bcd47244dd4df0f6aebf3f1776bc85fcf5d9fee86a9526303902e42ac4e904ef13b5bfded42a5851f94b8e92125ddf94a8adf994b0a2c418ad4b0860990746737bfb3d65fdf794bfc9db9bce4285ea6fcddb949aecacabb9da57232fc7802ca8e24ba256914bfb156d5902436403142e40459e9c650fcc65d5d57968082c85615978f74e736bfb3d3d390bfbe3354f83b473dc1609e88d9b88badc245559867b10ee7aa559b34407a3abef850a4eb3776c25039e81842ca933527004937de3df11b4bfdf5df98eaaf8e3656879bbf4eb8488d13d743e1a2acbbc3b95a86479612c82c59410a98d489df57aaea5a31b9bf2fd6022e91ea25eaefbe500ef0f1376bc83bfdf59c0670975205f5667ddb285fc194bf396618a2ebc70a3c64f8b2246de2b7b55a00102427f43802831b2ce9b63daeff6db9feda24346e91f56da3fefa03aad2586fd62ffe3d8af96b9b84e9b643ec1313bfad53056c8c10420424b844e892b27927217e408982cc942d4b6cd9f284e50b1a273a1de9016e4e57b4c7922f639e44118509e9c5b9ba0686105ad0a1c908315c49ef9aaf00981e11212c99024c557a95f8eb38f0affb127acb9ce82dd3e9abb76e6ba2b7eebd75f3047aebdd5b3791e8ada7deba6ea2776cebadeb17f4d6bab74e1ad13b76f5d663af58158faaf382de31a7b7aee382de315b0c7ac3786fdd0683deb0abb76e96a037ece9ad9b3ff48675d1dbecbdf59e2d7a9b5b597a9b5ff436796f9dcca2b7f9f4d66961f42e7b6f9d2684dee5d65bdf71a37739f5d677a0d0bbe4bd7520357a97bab70e8484dee5d35bcf50e84d3ad19b9c7aebb318f426796f9d4704bd49dd5be771d29b7c7aeb2f2bbd5f576ffd9545ef57efadbfb2f47e49bdf557958be8abeaad874588553fbcb8e2458f994da2f3f41ebbdeba57d1fbe5f4d6c75e888ebdb7be0208cee5d4ba4cea2ad5add00a0a940f6d1f3acc6c0a5d08bdc7a9b7ee60f41e796f7db645ef51f7d6675af41e9fdebab6416ff1eaadeb287a8bbdb73ea3a2b7389ba2b7b844efb005bd45dd5b171241eff0eaad8342e81d5ab10e6ee90d5abd75708dde60d65b9711a13728f5d6750f7a7f5d6f5df3a037e8f4d683b4e8fd59bdf5202bbdbfac87f2200882a283601623511ff27ce8a6d9149679d14bb34924cd269066a57768934aef9cf5d64d287a67adb74e12d13b57bd75b288de19eaadc79eacc780e88dbbdefa8bca7ac983de7807bd7110ebe293deb7ebadf7e460bd078bde77eaad0745e96db7de7a5011bdafee15c03583171d4800697325cd497cbe62e4c7679f65bfe1ec349d4ea77b4ab3111fb6f8d05f665398035daca3d9048a6653b6593827d3c9743624f496e9defa96eddeba6c274b72a5080f9539c9745f14fd5f0ef779fd7e979f7fcf3e7b95faecf92ac5f88c6585309fb1ec867c76d06ccab52a7d0f66b3e92ba5d0af2dfd5aa34b157419a5ad1859e13176db8d56b352a489ff3c5f2516ff55a50a840b9fadd98471ff15a1bf5ed1ee65bc1144e9a4e82d9e59e449e1b42a9538691e4b5f29320196b2b09489e505c58ac345d8afc61883a8888b70d5bde1f034deb6eab1ae1be7a2a979accbdcaab13cb5bcca9205b2e441014b59cac298a5e804acdc71428c953b49e81268069aac9465d1db5e5d5d5d5d5d5de1943b5b39a598c4ac14d55ce9944e75b553024599c18856caa674f5945a47f629655bc49f12a8aa572b3f1ab4804a5993a0527fd02554025df172674ded2c3f1d325a962da8284536b652e45a51c246182a452432515926b9955f1ad16e5c510964456f7b7525b228c5234a71a66b34bfb528714cd6f78b5c6599ac0d6f947345dc339e342a7b6a4eac4538173a765c95f1557f5199b2579ede9465caa82da22255e15c88da2b32323c268fca9b9a6faf663a92aed2b6568f1dbb6851ab12682eca0f8af592442a3f5114af704e0cfa7c75f86660448ab637e79c69554697067ffb571df40e1f7c38572cd2ea598de09716c1ec9f69dc100cbf0fbdf13d08826808becf68c6e126a4675c7e58c962d046ed267e9e453dea10c5a9a0ad6f1c4ec4b966d3222449ad5e95212b0d0dbaeb99d6355ba6cf33ba6fe01b2d63dabde1330404b83a5b8fb2676c367dd93400f9e6cd6f1c3add67b483d16f885f91b2571c21b83f82f8419403fc23fadf35b5b4f52dea19cdde1bf8b3df376f42803d3f652c6af03fd75ba4812e82a7067d8b354a54d3b2e72ca2f8f6998681116d7d572b119b4d1f885ad3003a2e00e1c20f016e463b80d3ad21abb5b5625a5355aa32aad7db757d6f5729a7deae5f1ae700ead1f59c8c36d5db1dc0bdbdbea216fca0d19bf6167ccd6d94d72da8325e7fbd8a4f1863a85b84c590062ec24966b5f681f6dcb6123bbe50160a63df007f7e4f31f41c8ae199dfa2153c71a8d634dadeb8c7f5fac65d259427de0f8d756b64abaa76aa4a5dd036a1592a5e2d4700639d2a5230377d8ba55b290fe01c0c45f57d6dcf8ecfd0471bb6a610b551e69fe5b4786934f75bb652958751297b355e9007d1f6642cbbce4fe801dc9cbeb8a58d2b1f04cffab9ad36fb8c73b6fd2dd7a2ecb5cf5b97b6c74ed06c7dfd1aab36cf15004e9ec67cebbc132e8df9e629ddab87d64df00b40ccab93d6afd9047a788a2f7a782e8179ec7412d3d1699c03b8b7ebaf13832ae6e9e7cde8c49d49df396346ebd23fe2579ef884f6df3acb0d51e0d31880d72bf2662ad2b595b69fff88fee342fe23a2190079cd831cfb2549bb23f393e8a4488aa4e8a26f27495288f42092249d244992244992244992c4c9f199a9cb4c9224e943923d64a9494d239dd42167a4e79038a4cb48922449922449922449922435192361a4499664261de8ac9782ecb553c445d97f4e1b2996f072cd45d77f81fe73ddc783fce7a219d4fcc7813c3bf6d1c5fcf432dd6a6151d4bae2a0f3ea70d1cb814edb858b5eaf97bf5e4ee3be5e2ff7e979bd5eafd7ebf57ac972665eea382cc6f3bd5eaf57cfeb4543b7c8836ebdf3d241b7d5fd6bf6ca7939ce4bf6728d6e0bf5afd7ebf57abd5eafd7ebf57abd5e31747f4e30747f4f26fa2a5f24ba3fddbf72d7bfbc76d64bc1e73fa7888b3ef739352efabcda5e2807409ebe2110a4f56ab356c7c32d6ab1b5d8626bb17f19049d043ff0033ff0fbec67cfcff3c901fe2e309f568faef730a9ba53ae9276faeaf9c4dddb122eba8e4bbab71b8446df2a7cabee5548020f540f3ae8a15fbbc4741a97667b9eaaf4005586ace71c6b9175515753ac47d663e7cf75d26d0a3b7fae979ed30bf07360dffc580527bebe66b1a184c7eddb8f6118e210dda367979db11376bedc9a178c2e827ecfcfc76b36ddd0ab79033f017aba9ca17af0a2366e0fba350dd0d3e50c9407d1254e9e9a68d22db2e29b674dabf9a138f14737554afe2ee14e1af5c8fad882f6df22f9354b95247fd1adafeeadbe58af5e170c81b426a8faec3f4cf4b6bb7dadfe7abd17ad36eee9817babb5d6ef73d2def2dbb9da8b737514f475a1223014185bae9ca1d8fc7dd94eb1903f300cc12fc3903f300ca33e10fcb21afb81e0976308c2e6efcb762a7f6018825f7efa2167305ac6e49c79743927f18484fc8161087e590b981cacad923f10fcb2182e536e905a22e74ce6ea03c12f73d1d223e74c43fec03004bf1c953f300cc12f47e50f0c43f0cb4168f8c1bafd40f0cb27a86073b64ba6c879899c57e821e76c06e37b31d8f5808688b77e1b418a2b34a001b3c3e38a4081bcf8e6308761ce91befda56bae1cbdba5895aa522ed14aa215fbdf1d7ef5af32cdb048d7ab93e1f9d5b1de3e7386e863eafce775dfdf33c07afb5e21787cebedcbe07f2478ec7914abcfc6797df55bb357276b48d3222a81c704705b2d8665d4deffdc667cc1effb2c0c58ad479fe31ce22d8390a4bd49f2ae3c2da8bf713f06f2050c9cc2ccfa47adf94a5951b5028be8cb9010222e2229a2158874ecb819290a43a464851344552c3e2822f2e4c663280a16405a90c04209d18e1622ecc06237a445f401113a2f5c3d845022081e0c584154f0a10513ed8344914ec3c614d9a071b89c408b3810e1a0c32945635644515a714249eca981826c9321493497c49001c68b3cdbc2e9c8806481b25c581b9423738891d8f3a5c52c0c0b7287a24c2be2b68308686b684665137980a8105f3ba0e26bcce9563c07e1201494673d00e92a353bbbf2d3c4070cc393412674b20ccd2c6bac9c1cc4038e48e341766b7c195f70ef05f7332fb8f55e6dda10dbc132b93373ec84b22a7dd96dce8ef3bd5d8b5ed4de7b2f112f116a24a9883d59c22024a0f87ae10381b0f98dd0d6efcd366808f26b42c3a277feac944ffa5665dc6a9dc80e38b06ffefa0df1b3571bf9bda0770622a37307e1df10ffe702ac538fac59c3fc8a8af588a719503fbf0ec8e987e2f020e7f0cd14babfe8ce135a3c33ce395f8b03db1efc7dbf5d5cc415875f79686cb8d7f2b2a665dc17ed5ec67b7b86042f5b9248abdb31da46f13194e0a1c3951f8e20a24c5a2b88820b550e3bd82401c142f1d66553aa8cfc96039f4f5da56fcf40073cf67beedb5f7c4fd4c6edb17f32a90f95ed6449eecd7acf11bd653b190f99d355cabd951129222223521455e4a368c8df5d0425dbdd64bb22d9aee8c6c3b91a4c52d0b1c044162042dafcbe59d00684221f38a10b018874dfba7edf96d270e95a808147096fd27d7be2f74debafefdbd64db7b9e936b7db8deaef6ddfa0feeefeea9bd45f27eae15c8e11ca08e11df18147922652277e1369fddd41260a0626929cc9926ea2267e137101e20305365c88981242ba6f5cbf6f4e7f7ddf9e6e43b41081d122e6ef26cad257445df4151196ee1155e91e114ff788acfe3a910ee7aa9a214852c0c1c81a18aa30f19bc8490a0612945c45e124dd444fcfe6371114971f393061e20b5710e926da1149fdf54d3445fe26a2d244da68226efeee21bd1bd2bb213d35a4a786f4d4d0d00ee7f609bcbc312187373a6459e2f7d00b3a2755218871626647ba87b67e0f518006303a6489a2030d27a47b68cdef21abbfbe87ae62bf87b0d20c1149331405e3f710d4d6908fd8d690bfbea24beb86747ac8e9afafe0e15c03c20061c6092423bc014289df2bac9ae84a82ca981c88689921dd2bae92f8bd220b4b6bab0d4f47e449ba57a8f9bd62af58c1a56545142d2ba6ac68b282eaef5e51155b115b115b21f5d76d3d9cc340cc112c167ab0c10d9a1489df362d105c684184351fc80025ddb6237edb7a1f6831028a1e5a745049f70add0aa7bfbe573cc17eaf18127b6203137b621363eb62c3fabb6d595536dedf6db3faeb361dcee588caa18525588c14699225cd6f1b9a188ee872e689d319a874dbb47edbb0b4c023ca09549e5449b70dcd6f9bd414d55fdfb62adb94980e156d623adcfcdd2aba624e2ad43871fd85c2453128adbfae628773357448783494800505efcc6f1552434dfc80626a0a4c1050d2ad62cacc6f1555171082e68a8b10639a48926e15bc32bf555805f15bc595fead020b96460511581a15512a7ca818f277ab80826da16e605b2a78fc75948773475a469280b0638a2921647ea35a47a889828d151d2a40916ef4ca88df6856932294e8a1890f51a8a41bed15f11bd5bafa8d6ea51be5824541a3c0a2a0538e7ea355301eda8447054361280c95faeb670fe76a9b1d08563d429042e607dbfb7d563160a80851040952b89192eed312f1fbb422021b2082d8d1852724dde810bf51a7bfbed12774084c84130c4c04317ff7097b3a614f276c77c276276c779e3b40f1c16e2a4a93273ec6fc3e3da0c5083233f08010c142ba4f31bfcf18d63cb952c1071a76a090ee73f7617e9f527f7d9f53e6ef930a4c1b306efe6eefeaa9e9716d79d6566f4bebaffb0ee754b8e131f1810213bb35a910bf7d04a7204ea08a820c9523e97630bfbd4a032b5ba8a0b0830c4d9049b767fd76abbfbefdcab198381126517fdda1cc29f7313584d7c5731dcfe9af0bf1702ec71122d0a4e08305166ab0f2e5b7908c1dce7039c1c525cc07e9160ae2b75056069c78a932844d1125b449b71010bf85b690902982902982909010d5df2d54650a4199423b5348eaaf07f5700e035d636e6842c9921f74c0fa1d54a138320213676ec04ac2857407fdf03b288745489a10aa70c16221dd423efc1672faeb5be849680898203060c4fcdd4159bda02ebd20acada0aaad20de0eb2faeb413a9c8bc0162740b0a10ae10b0ea997df414e2190828411509cb070440de90ebafa1d046505c5ca4aea863160d21dd4c3ef20a9bfbe83a682a84a266d4a266efe6ea0ae2935535c3ca02c5e8fa7f5d781763817aa41c24409251f9c8025e5e13790140792f0c035029b2e1ba64837d00ebf81a480a1ca13504988d8ca4a375097df401b0808ab0d109136517f37105409e4a3041a52d6ba4a205d09e4f4d76b3c9c7b00970f7690f24215ac255c7ed774a60c3145550a2a84a14a776dcbef5a16053c1c4cb08107066b8a74d7b4fcae69fdf55ddbaa7191b528646d4aad09d5df5dabead5a07ab55d4f4aeba7abe6f4d777ede96f6d08c9e4070cc944ccdf1f72ea879cfa21793f24ef87e4fdfce8702ec79414ab16744873830b4c58fdfe410ae2861b868042450712e9fec9f2fba7eaf0640bcb0a63acae9449f78f0ebf7fa4fefafe99027fff50b569d3c6cddfedd345ea7cd4903a1f2ef2c9278b7cf2e99101a022a814091a0f7349a714325323c00040002317003028180c8a06a328c9b144f90114000e63b65260521e0824f220857110648c210a004000008000008001a588ca00984ddc1a451fbaf9cfb961c8ff7b3289e65cadbdd42222f66c6dd6ab7c1bdf1cf354aef12ef285df769b694d6dc1a333d0b5a3f8c7961c74a0d18c4e3dd24813a7e9f27ff25e9a1dd91c9326343a600a343ef5146b088a2bb11db8e2bd1fc26e9e0acd637d1d476173dfddbcad8b587b7267872f7993e8e7dacf52614b1bdeaafcd3e9be3aa9665dfd2e71d8f38ead7514b0b51baa7d7eab015f23ca901cc98c0413bace4278363584745700bc2f7ca205439e90da644d1af0b2c967765d8da3d3df290edd2f9cf2f48b3bdca118d5d6219b6ae8b4c33159f190f0ad12d23661796dbf0d3d46d64c6096ca0565c41bc82e53925b09e0195e730346a1c0642ac0762c782667a657bd58983e3a8369db82fc14df4c805591c5fbe8a63c420b9f468bce76a6f6e011d37547d5725005fadc4e0fd345637e8e0cb18c4ae6dd1061371328c52b1243f80e949c444a63ba5761a8afe2977ec36594747496e8fef731c433c3eeb1d0d4fcc9fdf6b11859b24e1a25930c61d914d4faba54e98d5ed4fa360a3d63e77d8aa7829e273a89c921b8aed9487bb9631e24bb547289d42e9521f2525d787d157d6f87f49345f26d501e97472c6e0d1eab884ca5d0a58fd4dbf8f3918233d097deff66b59aafda223d13c89e77d3a2ef63da73678d9de076508298328aa1c0f6c8bbaeae0dcca05c424cec1a375b4140a5c3e1a600280a39219be2449bdc19889004b31d38a7abb8d11953520f59b045ae700a7da6a498aed89686f7e93aead269de6e7c31b9abd054b762f40cb42cda90661a5b4a99290828a69def47260d649b6b29dd69b987a21275aada3f4c3616e554ad56832442805a962a68854d48274f4a9379ea14136e4c3e8b34d6377e003f991b3b73e4954f2920b0a5cbaf78b2852ccc7d6f7bc11981e052475c585592380b8d49ed07aad0b6b4f3aee88367545be5f15a2636cf5a225089fc7fc859ff7d41bfda5379385a2293bc6a1e9bc464b9b2a059484323829b897417ddd929969b47ad0a1ddf0339df0f15388d7c1e88a801cce4e69b09988e7b99428bbb2bc51bb64ddf4f022d57f4feb932ef720745e33c403818245b4f786ec3445ff9b020a71299336c04ddadb0b968f1081b6981a7b05a70276f8a3e2475ec520972156ecb28d02b08c266e1ea436434c9b06eb243fd122bf46219028429b3c013b9b6746ab970370f957df6fd2f64f4edc25d7a53f220339b5337a1c145c448f46f9d2f638b9a1288abfe40a3ca171740f87c1da80fc6349a5cb97f64094a383086c0dcae24701e8b0e17ccc5c07accae4233dad06aa3f31c706e046b7e50e7e9012e270c941886884026fa8dd0cb94b8dfd96a9bb854b39853bb78b30b30d209e810e5f256e703a6a041dee10f28cc625cc31ed8dc3520c2f8593c4632196fd7a2a8d9a0739414c4692c11b771f94e059c92aab39bd05bd24ec8189dccbd68b43e246ae26b2169b03bf141a60d200e280b6e6826703a008e9eddaebb7c5c3e5aa63cd390c8884e9e5f203755dec2f9bcaf695e54d5990806af7b6795acc3450fbb5534e322068ed64018e2f83ed9ef25705b193f2eb28fc91190b3c4685e17053dbe12eaaad9e4b683e9e35b32dfa544f1e87e301a1c4a6a5519be059303174506af9813a142da9596c9f9be4025a77ffb80d9de20e5db07286065345dd331684fa9dc4c352704695315322e616356d685f5aa05b043aefcd8882a949190870984a2a0ac1711d1f32bbe380a5169c09fdb553805cb7839203d0e961b5c3b6712d736b0178dc0c8fdc3604458418ef59a1495ec17edbdb0fd93e482a505b98e645884c80cfccaa2ebcc151a4a59593d76476620572e0288eda5954c755846875d40ac7c68114296488107a4231b80358a59fa70b383f0d36f684a5119885bea4c36415da8f7e8e4b551d995b449472234e3e14f58c2e75bbb060e558b7014abdce08fc5bc86dd8d83d18df260375ab882fcb42866eaec9ad651cf204ef9790048beef049781281b24a0b6aa0af03f183259e16a4ae993d60d471ad0220cf65220df03161ea5d63150ac17e5828c4130603d662d2b7bca17cfeb7cb85913df739cc507e3edafa0525d79d0c432ee0611f908d7bbc0a4774701c159ccad6177486e090f8e1c26c2432b74dd77c9109bf448dc2b1a4d2594d3f90b5260b1f31bda13e67ff72b008d1c7b5813333008eabc0ab8e65b7943d79827b130f38fa6b3560131e08352224285d60ad88cf75986348845da5b733b53b4f6b2a722259df684eb8a197029e92f568a2328bff318b4f6bac434abf55c42b357f0955d4e879607e48cb80849dc3cee84f722fe6daedbfd4cc20cbaa08904212285acc9f76b2f9955511db465e06dd0a82d9052fdd53da7c962ca46052153222a0d7e8f7a82fd0cba40458bd8105b97c4fd3bd2728d0b0797559fcbe18279dab5b9a541ae2423f23e971d0fe1d60ef654a3b512932aa0ea8e369d8240358f3d177e410c37ec8b918b4a7e97a243b382ba64e96924e571117f7458b358242b6795f0afb0fa388445fe239e16cf02d1be3e6df042448478e1bc457769af7e9705c560038c239c0132892f0e270e30408e3f8ff30388a864794d9f1d8511e9940a802a88ad15716c87d127116145711ef66089106924c820dc365d58b73e8832248a65ade3ea71a224bf922885053a7b8371eee4c3988b55d516e545bc6e16468f800f27808423fd5291b48db1ea191a76afe23448a6af1277c06994c93b41354182680169c2843ab568f9ce0718fe09d05f3aa1f9b51aeb41cfa0c61be9935ed6841a1da7d44b15439934f91c5c412857b17b4fa0c89dd026c15ae385ea91c9a906505e3acc355dda2e9ea456de8a793d5f3299d80a9230f0ce2c4874d849c8041603445b9e26aed63592e7b51d4bdd4c88c9c39a7cdf40ac46fd4b48daba5523e440b8a4a44147ebe6666fb573dbf1b2359bcc94fbb656bdd1871efcf0d343a5a1cb3f4c545905919fb0e9da13a75a97a7cbe996b30fe5562190c095150871da3088bdfb3b235daf2378c3f1a6df731d7fc8875257a1b79fda8859abab929b323932b3e8059187c810170466ec1a012018f5c16fff5837e1e203cb1a110f85a23d09b23acc203f0818b7cb9b082fa1bd61446f68b5e03d58a54e2bd96a32437b3e817b459c661966891ada83fc2aac6da067c268ad1b58a88c1fa290553f059d3c30638956901de9d54485f5269b10befd432eec6d1a1ab0cc8a15b2453f2562c781526acde7801927b34893f06373533b7d45e6b48c4ba67d09900efee3e23162e238ac260bae8ef40a22b255264c0c3f21ede8b771d884692692e93f0ef41656946de9c9c48df1125a902fedfe302622fdfd23eed8633a1ab3c00a2b65b20fc67d2b913327b3a62b8912564d3781ccfc83713331dd27e782955322330f1ffb9859f8806e76ddde7021ad993051d9d6bd4eec18a7b4644e566a86d4d83864c2c426920dcd9c8895879b326902b2a14d5362e25053a64c2083ffb9b8d56f80b492721ce0ca09e3b13a3efedb415726ac3181ccff810dd5bb28d7e79890af32c6245a2eb3e2976034bd4e6f084d2f5aa0fd257493ff6cedd36c84b4087cd42bdbc44ba029a51dca82990a4767203b454a90ebefba85dbba68e356900edff736e47094354ea2855b898c9edc0ecc225bbb48fd7ee7896122cca36eb0bf3d850d82dae5665cfa9314c8b435b858f38556080139d75cf5da329d6345f4f12ae567c09dfa7213c130a08a4e90d4aa3b149384fed197e3e9477129567cc28f4a3847701d85cefb443f7718209ef39de54064c203cc2e07a9e31d01a934a57080af926fee6e5d37fe1ea521f6c674e5c8b44ed060246968eec115a0bb88e0098dd38be8e0d2a7832ba10eae4b325cf893e13232ffb06dae919aa1bae8d0d1f0b221c40a6ee862a2cb5e4d0714bcfb4a90fa3f3be27e64e92fdd64562c1caf3a87e0e96db1888000949f57014c69543bc7f05861d99cfe52db502d7b1af734c23b289c6182b2422d007b8195c932001c6c3fef3b1b3864f2067bf37cf8579a0c28466851c129e684437b6a7ef03cf06843c5d3cd0fe73ce5b215f54e373ffcb424e03654e12654f6e8fc1f49d92fa264323626bcfaae27342a64a33d4f09e32ed5a62cc85f0aa4c846381b297093222f4d1220fb4f64465e0ca7dcac093f3eaed7c36a7d09d4cc832dc5c15084cae1b96644ed951f18fb6d95930056e34cf3a2ca5b6ed0f6809c9da8b29517da5f008e020bb6ccd9799773b5999cd49eb715def6ec2eac642b4adc5eb185e9adf196977edbbd1331e867a8ce6dcc46cbd39d08edf85e80a3a967fb6a701457642b3b6f5be1855987f1396e9991d913a99868f6a55c23cb36fa9191101754a98a8e72a9f9e4a4cab429fd3a2771ec26d668590e339a39c94d7c687f245174db142e813901572587117075de0f83632315b37d0bf7dd455e649e9be53cf49c7cbecd459dc72821124a79601cc008aab5fec97a88375dfb7681a5aa4fb89d8bc40096e803c76a7ed9b7662c06a3f1327fd75d9dbf0a4f79729b0362ceca7466814a4fe0a9484d16b1b6acfa8cb5ace96f5aaa7f7cca31322ead83cfec42ce1205249bb845c14f9e1b7208f9c389a323aa820a8fe11c95a6a7d5bb5d4e35a7b190a4893578512ea3bed8484ba220a610d20c59aa5dd4c2b01dc5933a08e4bd6993e19922d8c509293b355736992042b58528d3280039fc0c3069eca6881234248338b9553a350df75c7f3956501c5cc0dca0c3f662ca6d9e0cce59f58b491c1aa71572702580c50698b7895da5a3e135e2d7a423024aaf7ca9fa1490b24cddc0e507b86167f263e5cf811e1b086eee70a1da8813507b5a0842b5e38a8fce326bd784108ed6a15f8eb5454850ee1d5a255d5cefb00b5e7a72234919859216dcbf7764b37cf55a32db7f6a6231a9b4c02b7d9478d263aa4ddbec17be80ae16f8bebf5f897e9658d4d7debff96542c658de7725754c5823eb134829f9c0da5cbb9d2c2f7d5f80e06ea244ccb5a3bb789963589fa5bc947dea01e293b509473be1dfe3cd60277f28ed50a59ba77c0c297a28d9e84629d64c80e9b7cd8dac03b02ac31f44477532884c09b00be7de1b71ac0d2c33c2461bcfa848d4983c833663d383393c590b80e9c8d9a95853ab5783c55c9c8c4afafba0347a7d192498e7b38829ddf3b8af967fee0023afbed51f7a5c73008cb8185dd3351a53b62bbd62fc74b02948005829938a905225c98302e795aefd0c2160bd7d113181824a3b8bb4ca79a17108c11faf4be4bcc4963b6b280f107787126fe5ce523a487019daceda0ac19d0a6a982247e0668f07955033fd80f668381a07443e6d854dc63156722aa9d71c9a90824dad985af379e78812b7e0451f161351403e74ad4bf00067c7cb433478e438e06779f3d7a1e4a8d69c1c63c0590bf4ca3e18b9841e8ae086ba00822f9478139895d6ed92e9e2a905b7d2daad989f46def36eeb5a37e2eec5899755eed980d75a2fbf5a1ec6e20b64fffe8681d999cda81e790dd0650bbaebe074bb9e78dd9ed1d4f7ed8e078e4ea2cf3f5cb501ee9b49cce98601063ceab24b4883d670fd72f301ba80ed73332f0edfd2390ff702226192f95a702cd767276ff17a11b87a79bfc81bfca5d02bfbb893eac560d4150bd82989659a32737d06256323d8e61020adc76a3a741101b506a1d501416d1a4abc7b9108bafc12b004f93da056573d9993f7a2822cefcd1034608d60f3be8d22a0a6311121eea938d6e8eda267294070b23e8f64994aafb95edd4708936005ebf2ba4282e2060176d1be11558a6664ac90e4eb923ebd25aee87d0cc9f3a83ff73d935c7e72bb8e794e6025fd5bb33fbcb32b8934164120c8fcb28307face6c2afbdf80ac6a06f640a816ca84a31380a3b138f96debb920689701f19327ae48be359c9b27963eb0f45687fa95f7d888beb4836607bf022c9a71b9d649079a271dd7226be9762643c9730b0cbd1b6d041baa65a447f231f84432ae121000326a32e488a01ce3d051f2d4e8b23e53eb0b4d5cb81712760de61828c35058b8c9ae89eaa85f8293eed0b70104e3e9f0997e4e879232bd392902816b792bf96264f442cf5825da4a24d6cce9a453c8e5188fbf0d508c5d468874c374f7050d8e58726810c81f1ed0062d303d0bc289b5ae7986409d6c586e9331021e874e0afb0e915ab0c8403b0c9e022dd6e974b7b61a60933e2b0c859df4bd48e0839288408cd1e1276550810871c30bd053b50153ff758d83967fd7c1779b9bf0b829c30c98d5c71df838891845b1f4256bc45c2268a33e40408df62df428d7800e4dd0bb0ebe1bdb84c7471986c0549cc2c0e7580fa422decd6a2ff549fb350766b16e063da049a96209dec7134ac46add0c501a430358fbcc7ba82496ad61553a5c3639bc3560d734fb13f0eba8a6f1159dad44f5ecbc6e0501422e8c18c223d80514bdf5e29b1734313cbff05a0820bf9bd124602d479094885b834e18772341028abb2d6616a820739c1305d59fc53323b73b7b3887b2da9def9060bde6570e7f40564ff7e598516f3cd75557c0736d25924e958430a0519a643a183d598fa7c50ae46fa34989ea6fc4fd0b0e5e022ea0b7a585b93f871fe91f0181aa7b8dfdf48a7e78dad35508581b5f75b7bf79898aef7b8724b53680afdbcfa692b72daeb800667fdf778c3232151a33c7dceba9e904e33642b15026e027593b26de1663fd3134c53efd07a705a2a277b6d65845bc8df1b1b0c754631ecc3dc1f622c1f19608e17727da8ed45c7ad38259694776dd713bef6ae7fb8296455859cbd49fe76b64e62ecb46dc35b2015882b80bf432c04c5e97ec9183d0e94053ec7ec53a24c887bbf63b4a30f3ac740cca4f0c4edccf83663764d3dde5118177fdccb8fa922b500b99e0dde9e98efb900cf727b1b715094475dd1506f070f3ccbd5330c7839d0bc2b1355c6351ed1a4b80b8b4fd662f1abe4677a0d39a82504afd79087554074b98613db55a5161c4825d19e5a173b9486a4fe9722201f8ac91b2deb558a83d1560740d10cb71fe45cbb125f1536f657daab26c995ac93ecd5623d0c49c6c29417475c8b7d7da8a49de1ef82e5c50aa85e92e9fa25bd3b59490ec1f182f90220e6c9d3dda03e676f26ef384c027078964f11b3acb9edf188734ab8cdc889bf71abc4041a7fc64e733951f06b49b551777b7f481df974784aced2540054d8bc14ddd38b8a593657a1390701e14b6178848346e0d2431e3b9f0b83b51e02d49bb82aa8d0907e71362838655a8d1c0b798a4fd0858ed8623f4cc7847f758be0bd1a894b489a54176f34af19786657be77fde0a0f66e970f0ab6c323ed33b9a2bd58d8768f535aa686c97b5bd12db1acc955066e47cdbb8b0ee0ffc4b78cf4e1104824ce3ededa5e168f6f0275c41120fe2aa41c4d56795225e82e18437c9f2464bafda3a7acf8825ee5eaf8346b494ff932c0dbfa1873afcaaf0be3f2d304ae5ac84ac7fda4baf5d3cdd94c9fcec4ea83d1ad99600b89ba928719117223081699196c1f058f0e2f458a9bdc172edcc8d6d833ebc5cf929a0ed3937dab80888a2e19ad5d2c6dfac4d842a8fb5515d4e7e278ea572729b1d75d2b8cbe65bd5d20ca72c5e70116d4a919b0c42458a8d1c5c0db95c7147343cd1ce75ae1cf0ddda17484995281ef575a06558fcbcd8f8955b78cedf8f3ad053482da3ca8a8c4268c6388b257d160cad4bb8634a3be743f3403e6b797014b9327ccc02d405f9a2472dc05830a1471a264ea008c968296e2da4bc80fc303462614614dcfcbcd71ac0a1af1cfc79bc233a4ae3c15691501abdc18440c409000d811e89caaf57c4734b1cda101535ae6a08390cc2aa778a44403e75b276fba8b20949531fa481ff8a7176f0e4a8b527acebd2ebc2af8a935aeab85e3b27cbb2643d4aa8d5cf052add460e5d0d50248abd0a309f0bdf883f843ef7e7219a968e64f9d6f7b8cf3334acd044c63a5316e1d0ea89e1ea13c6c0faaefe7c03c4853750c8bf591e6448a6a3dd0734e7ea2652b0b321d516cc98fe5a042bf754a5d6ed54e28ef1de1e6588cc280b1760dfa4b92d322d025f4c6c0f7a1800b8c9a31a8dbf9dc732d59a5caedf273963ca7f5b83ed9bbc38fff45702159e04ad5a41d9980b3d6cfa40f4e6e73096b17843d47887dfdfa867cc0899b16f57b5dc173d407072095ba490be9601d19560e025affd6c3fd7c72b888ae28a0ec089ea787cf622e4038bfc30c9e3b14430b77d3f9e2e9955c9fc53bec34e6b2a7fd9b6ec69a7437cbd112e3a8c52e5115aa5dfe206b53b0407521bc8c62b86fca04e4960f144d7f2f24972c019d6a001e8f5f0884b4f3ea124aab54cbdc12d6d0e7fbe953293a6c1359f3d2593f794c3a2898ed250cacea02ec41ecf08312b999794fafba93070a0883a7b1524cc59fba3bb85a02942bd2e0d4160f1686b98815f8b96ffccd2b406f581661b70e921a0a2ac80c15ff39c9f133aa0204cec49142024cb765397103bf1f92989af2d67116e133ea8a8e86bd323b5c256584661fe1f366d40e3aa20be536254ce39ae4e65f812459779a91ea84a1dc9f0d58533d205820462ad75f484d43151de8b765bb8fb060fd183739f834c8aa4387e483075a3c89f4d8fce0e7a163d0f7bcc9b276611b57122ade7f5a03916a861be49a04068093a094e11deeb2fa00b21795218eb897cf0e59b555f738c92299d7146995ea78b274a69353f632d5c31c39e4cdbdf79a91761bdf08305938617d5efc49f755aa0e6b5c6bf3223c9f9058b33fdf2a88479e96428419e484bfd46a8a741f31cfc7d4d521c11a9a1c680b1bff22b306173095c6d3224274dd22db0e4fd467f9990fff220b758e21b4f406f9acb6eac1c993250de2d35cda3156d71ebb25ef04476e3e0cbea9882590744c3be8cca0ba5146fbe7f3dcef9d7a74ecb14884158704ed2aeff71871939e9874a5c6606af1f80cf558c1857dc4855de7460d4511126c922215c3e742340b31d1b5c3b75c848a71d71e6f411274e3b1e0bbc306801ccafa0c24e30aeeff05bbf99de67a47cb6f9dd2a471819902dbbe4e13f2b700ec818e7d5cfdfb759c0909b0ffb1105e3475cc00d382ed67a2a9321e5eda935e9960ccbc2da161f65fcc009e8efd76dda3fb759ce9ca624b9a51bc1d25470eaec57cd68bdc9e40406221871d87eefc7e074c5b064de1f97a7f874603671b74156e2e9e2b8416234bfabb8b9b1498df26dda1995549ecaa2d6b47e406daf69f5c8824bd11d2bb6581f3f7012b74ee68ea0624c3ef044597e1bbc7bfd047c087376b1bb49a575e6b4ed52531f1ef9d8671e77c8c6f500a0365ddf2ec8f8e58d0177d8d8a52832298678e6f27781d35a3c4fd4c87eb124863e8f644a01a630ffcd5c45a137d57a14fe3a1e2f87d2f10cec055a5753c351b3355520821ae8e90144e65b3d4410b5046da3d54800aed416770de131bea0df5d0e6fa1abac86944d5bcef9a9061f5dc82757e998fdefb399ec5584834dfc4ba4f47db06991bd1c7ea8452443fbd12c9c65979f66093a5ccbe708e1a5498a1722619a93cca0004c355b70edb15d1f5b3a601aa6b8d647cdb147a034da108e24448ff708a5d34b979327258edeeadead7b2edc619199863ed14db7b5b4a7a54adb74f45a1988762a2e1098d81277134d7bae71e526b5473ed7a8729ddb5d0ea3d3a20a57b13c4825aa64ca96e0909c2384d7b0f30fb3de700afb1887675accb6e401711fba90632520fb680e5f28c518b2784dda01017dc01475a245e3560d324047758ea20dadcbe1fa590b641d3051697069985b00ac1cf8a5c1ff7ac920093ab8606e52264cbf63d7e8c034e32699455cf8d1a2ba5a7f80f77c3795940d8de31759c66af7c333103b7fce6182eabd3fe7f051c0a515c513bc87a5ab8b292a6d7b350eabaae8b4584e6d1dc67d85f9bc0c6e912f69209ecf93291272b659e2069d0354c36d4b744bf9d744e05aa98d94d40ef903cf4a33e4751b66cc238a9fba76e1bf2b136819a3be28e6fc16d8293d73b75ddc691a393f7793154d550d2ef70c8c99cf9de43c1fb58b1770835e819d8c590f9c34995e818d4734319dee4a18cbc9a4372002663e275d4283733c6ad80f69361f768fbd9cd1805e454074d487ebd9e398116f14f22810f94f826c01b903e9b463ae9d050e2a6cc801c4ef9e3d7afa8d174f172e44914d8208699d85f0fd77efdf3d7b849e922f52f24d5238bfa59e9be8e411b3cd0ed770f34fa8ee9d7e318632d1761fc03287710e6f86415989ed8e6857b82f81597b2917434d1a72f0423ce20afd758085b152d02e07aba0998e7c9a75993e3701319e70ba52ee49d07e65bb3491c9eb96cd1247779441ad1284a30be8b7a162188d97628898a318ad09485f419f44a2119d7aa6bb5c63b4885cfc65dc284474847c8954964f07f8db960545d6ed93fe8aa96dec8cdbb208c8963dce210dffa58f45a63923feef227c4f48659b27a2452ca04573f369efc7cfaa39e90f7bbc926617be5839d9aec0b85b0765f527ffa39deb652860e5a4a53ab64d6cee1dfd345a0cf0f7f7a54a120d6e3278d78ee1145c456ec76d58f454525baea32bcf14952008939d338160179510ef2ed0324525a2470f499733857cbb5132c3bede02842e5b5347186e8f5b13c3aee74af83818643eb4c23e7f10ee10696b90a07df12444af27d75765c52dfe9211869af2c1dbf0ad9bd768a31c9c3fcdbcc3c39d54a0b24408969c8ba15bc31398af27180bfdd0e47009e8a1a553623bf67d2f281a428e1ee9a70fbb98cbf234b4a0814be188d57bdf432380d3a6a6b1a800cda084184ef999d3843bd8fe96f4ccd7042670597696c382cdffff90137bd36d7bf6cef744ac335c5dd75ccc5250a954a1b600200a758f655bcfca7ca6a2448c340b242dddfbcc5b02ac6c7b62280067d49712319607c255c78b4466610e790eea16ebaf9765fa21c5a198e64260112c05ba9382fcb9573bf730504a0b59cc16f914fb8c640d9009bc94dd1128b260184970ad6d20af4476c883a557250abef836ea1a5820ff10456a84b29c24ca66aecb31dabc89b2d98c4dd50cc571101795fbe9677c2eadbe349b1fd22ad5ba6e6490e96934886376c79567291ccc13c10020c25811ea15691655a0417566a5c163dc6c60336e6cb08ca53e903ee50bf82edd7d5508169b2cc2fb2d9e9e79504455bce4ffa821b3bfa8c1f31faba1f9dfc81875f7b2f2f97fee01ef3f56f6e85c3cb428310494dea4454f03a1f182da2387a597878ae59f070b0b640f124be61e2d7b6459dac9288f438ab90b7f22cd750f74209e10d773bc2495909d36a13d4d4b6a3d5f96f83d9096a87b722d757b542efdf670a1aacfbc40b7f724b207ed9b0edaece34e7d1c4d651552af72881d9803e3f30b266654b461d6259f36125db2a50da697d4b561f8259f3652a5b6db06d3efbd6d24452f97b4ff27e13243ef683da48c43c1cc9b64b5a176d97da9fb86f02816b329924411489c501812d3a8ad606d0146113ecb42bc8b919cbaddae0c66948909a629a246d0683b65dda31d4605f79413619c505c757c809ac4c98c7780d25b9b032f0e313789efa0a4d4e017b329524018d15b9be4dedc4edbd35e8e6568bd34cb99bd4dcbcabd3f9699bd302e137b732c37f56a2c7ff6722c43eea558ceecede84e60ef233ae2c732a8f6c9f92658b9894c8f9099a9b60eeeb0c336dc80f19d7d5f01ff46d5f07393d53373b7ab2b717fa933e5865227e6ce566fcaad56ffc4cdaa0e999ba49e39775b5d99fb4b9d393794c7666ece708a19db4b663659197d7765ccb0571031c520420edda58622d7dec7e859ee4324e8dd874dd0741f3a41cb3edc04cdfb3009baefc32668dd8749d07a1f2641eb3ecc04adfb30095aedc32468dd8771da6509320ea2b50a79f4e6ce337e5499f81713949d4e2aa5969b2ed1e457350cb309e37bf76a8b26acd7e08d4250436914111a5a5020d44082e252c352148e1a84a2286888124553031214c41aaea258d4e08242b0861214911a5aa240d440f2e4510d5ae2ee01b89ff6b6d23e1967470a5b710389bd8a5da7bd22d105c84b6852c3703faab5d09075c08e60a65707108e6cf751226990044e47cae72df795af1f7e3ea1c14bb32978fb2d28a18b0c6a47d24407e86db92a3f3655e1bd88594e436c355481d09f19fba94f2da6039ececa6a2ba1db369304359350a72516ec7cecded51b1f4064ba791aa75bffc0da11fee13708c8b8fe9057483cee7b22cba86e228915ebb46bd2143a590672c82bf340cb89463f793628d9af50fcb3ccf3a6f0b8bada356f807fbe0afeba3c173428774d5b014a5a52d0ba091adaf7b38b308133229f6ab544fb40af14949c3dba11da660d92e10782c84df32ecab758743b1629114645abbf4998a1d0126ffcc3a1d0a04dc094fc10e31d7d7de0fc53c855d810ac644a513cfedad39bb61fa8218009f4549a1f1920122cb3b891ded681030a6e5060c54291c8a20af0068ff9f402097710e4155177efdb30b243496212cd5878d58650fef871238488f6cbdd69070db7f4f8a6dfe1baeea6ecd48060088d15101c0f9d5c2b175389f6f2d540243d884f0d4c47e0373273a72034ec36ae693fb5837a6a3bc959b85c6f026966187a10da300f610d99c0c3e17fb340959320be4a683ed3b530f8496ba55a0a18c527dbd8f347bb0e23647156a450f3431d6b8d4bde9ad9c55d0004012cc62418eb10e39509e6f77f6afa0c86d96fa2c3c27523d82fc5c040b2cf6a34bc06d9c26513a23dd1771a42dacab4a0c038d99f61c00b575eaa0ec8dbe42a773942d82a923816423a58af41b65d0d9528b05dd75c7540561e1611825c33ea0179594483e07d108218e109a7d31460277728bfb887a89155ea5ab6e5ba8f8a83c5a70a6d376ef4bb60dbe55bd971c4df31b61d48189dfdcb5870631e3257e67b5248517ace70b1e4b42253518224f0a8558b7719c4c91b9f837b10637c4cae21c6f8807fee5fb803fa7bfffed9aff4fb7dfddd5fe8f75efcbfdff41f066e8a505cb01b44e6242e3fd2aa67ce67fb8262fc0fd4ca1be1a153cc366ad50a9a5bf58296ab5ed0ecaaf7bfbdeaf5632ad5c303e73572c07558b9880016a73859ad0daa24dc943d1d3b0730078a86e85d311e69d769b1a321ed96873ac9775ba1240f040c27744670d94e8209d3049053f4c9a2850a4137916854b56a84aca506bca0d115e44185048d4dfb3b3d445ff8ba469a36cdbea834cb51a259105e39b72c1132aa7dcc519557243af3b5427e3df49070421cc0fb86d0a65a0059a4a4bfb360f8f49aea084dd5ca39254d8d255aa69a030bf3321dc609fb0966247802d67e10b5461567ff8980bfc97c831d6aad5181183f71c0ed3381b7e2d0b461b383b07dba7e8d2bc92670f843ef8d20f3bd74163e3aebd215d8c890036988238459bfc3263b6c3b889aac2a461f0b643f3cdbb402773be3f5a879120d2393a1c06c8b5a8ff8884d14206f8bb414c7f7bb5b3cf29f8472ec94409bb3e3083f64efc0f9e6d220114a6a02770391b31b86f320afa6d3d819bc6660a35281fc9d170d061a10796ad6000a8c8963684011c2a4395464856a656eef0193c3b662709eeed769bd3e60741b0ec67a837b4feb9f8017f097510e63f42b76fb0a96e306affe1b5299b0e38f9683ff51bd6f4144f551c2889b1838dc22db9a3b1a742e75ee1500231fd565b5adb1efc7afc6600cabdf84713377be876af28f2ccdf3cc6ef971b3ad034709c6ff8b30f1ff76bfc6b81541088d6f53d1f24d25a98f339704038d51be8c4e0ec2745057192a9c444530b38ba8d57ce150d15774a8c0654cf19debe0aaa829a6a8427bbea12b4f1a25b2c63a94b5fe5c9429ec51a617e22b3c2305c05f5104ad034746582c42dccdd1ac5d6ce523a2b383d4e98a83ec19aecd590f1c6097064cf7f0a8bf08d1f78e55b0fa5250c99ea43407d706431cb42248e4122686fc1f674860fd0765139f1526f451b415653c9dc1d2cd5f9fabb4bf2a0d44aea6bf4efd13f89726d653f60e3bfde2c92a1b871196f7f997e85a93fe8ec948d494be0597f2c6a873f780c8ef3c8238380acb747b462003e6463410a1fca9ca27503af986504a7365ea451c3588a5f7fa23c4199a0b1e6661587d821320eb323998176026156a3a0dd04495acf90425724b9c67f441b9413a1b02d145921929d5054711fe604cca4fa26084ead2912aecc4c16a261fabad8747859d19451036ae8d8be5fa1f479d40051d1144f6be80826c7846648a34df0af6f61674cb0d56adce67d8eff71f9ffa18e951bbdf9dfe66d671a0aec902c08e8fe2297c8edf6b823ef1010f7f71a44f2318612539b58ac033df658c0fc3b2747cac106a417305a37baff069cafe733c1a19067707713a6d543abadbed259b5f74247702e053801dca07add35e77ed4f4491c5624ad0abdc05c4d8641e84ef293327c875ab65653d8c81443a571f8e5f74e5a2f240996c441eb6567d64349392e7bfd5e513453d76eee12ea57dda92c2bb35c99f8f44a18ffe04e5aa8be4fa10d8558738bfb1ad28bc5c357cedb7e067861f186dbbb3a245960965927901f5c23f9004a2e27005ecb7c3b9352d4cb0c0b4d2d630d8e32371a64377dbd2af7900d45ecc30950e2643c76ccdb546daec8e6223e2bc9bf8b5425b0a2d24ad75e055472234cb101a81e7180dead1278b1e09abf3c7355aa4d5c0d16e97e81a711646c6975c6c3efbe6e8687a5a20304037f3e9cebda2d2978bd3028f7bcaa1d09e3889a49472e7655bd50428c67731fd43fc2443e0fe4063a48b880b9b75015dc58455fbeb2f81ba2923730cea811425dbd4dff6cb20e76c33e8e4f528506721fffde6ba9dad16a32d3949b4ef1930f8bedc94df49b7c002e4285cc5c8124656ef9021ebe6b9018aa1a7481daef874c19e7d710e52d1640a4809b3490562c75feba6ead42d17c6d2e1fef5aaff8b7d012ca1aa424838d4280e5052d2ff93cc1a20b2394cc889305e324ce2b5c643a06051cc679cbfd0811e503197350f38ded07eadd56cbcc0f9d222a21f14be5741b8ec479bd388ccc517c4d42908d3c398cf37b2ac243a6c78ade57ea7730c51b30273e2e5e637363ec74567635c6056609a3c6b59eecbede479deecd0b2a7a4655f73e3569f2ed324eba9e1ff1520a2b4905af6c0af514daab6ac69232e5637be5091385079cdc2248c063ca1b985149239f1433b06081381f11b81b3a968d90775d6f3e55442a4b8f3f3184fd181e4e6762f0afde111dd22080814703b1e8886fffda8de78dbd5eeba706bb1ed45803c233e05121f04a613dcac7ba7ee2b34ac59a2ca9f439ff06f7fd158f84f8d508a3d4aaca7505c7bd07b54ecaee5bc708619bfaa0ed09709df31c6c4f21310f11bdb3f2bb89df2ba91ee7f7d425116290b19ef5cc3cdf56404b46951c0c8874c678b38986c88b2fb6b2973c13a57269323f5ec79f876612d4b58fb91c44586ac06cedbf7ae14b67fb009cdb3c5318fb90adfbd6c6c48765b0b774cb05656b1ec65762fb6d728b2aad69ad00bc8a0f692348357eb87530467343bffeb1284262a3fb3dff9a8b075fada0b76e05065b0307f14a765f79af62ea7ddb916e23d76c7dee007dca04eb8d8187516c2ffe7f505d3f5abefb8b68c124288937dadb9930f77d395be797c2a4a7e60ab9aced9cc3d4976c77f1682e847b27571f284d8f3a514482b3f37b000b07950c45955f900f31d95c937b99bab8ca47d1e7c83b0bc459f11a19f74fe2c695e72bc8ea395cedac4b38abd2ff977eaf906c0c43eeae22a93032a7870d01e19bfafae85ffa9a0f3e069bc0374bbbe968176173b38e4c5383a35d80e4e863500e21905f53875dd4d78a0a5167e30fe1b87d34b058bcc55e4e22c83fe4f84cac685c81efa79ecae4a310b5946793fd85ad07c983daf78b0522b20105b88e251a85dc8a7348328f4ee44775e3e901eba5928c2e4a900c02be691f12fc17f014a3020f68370e63c242f120f822210281406693e1e897e47446785755bc4c3899b5f5220fb96a21742b45ff1c207c99369b21494b365a757307b070a11cbf3123ecb1bbb242996e907319e05e397b96caa6efbb7658ba41e4135c5f9962f7e0422be3ebe65e6d6cf69234bc15fa008f0780137fab22d3b01f08091f96e154e218c8c7c25fecf1851bcaa7a87a93572ee23fd5c07eb0bee3eb4243ec8d5d3fcfa0ac38931664682b7506be24d5f7927abb13d5220ada0198536889fa4369e97f1b60dea267dc127b17238b89b29de63cf74d19bdeab661fc9728796dd58b981f0f37a98124019be0afeea881ce2a58da5780f662531b30dd0e44035eff384e77c3c53d7204712b82224b5cd96cd69c6e05b61f8a8421f006741af436c36b573e0c32d62067db26a0edbd89616698bd4a2d7a8626f15dc670aaf110701e6f24d03d90aac01b7ee0ce42f957f7fc73b9c40d290d5dc885a1e1b911b9211398e8be81a3454f1994a26a89ea276f884bba7e8290b0bdf5ceff1eb39e70b749210340053aed762ececa2763938c8e3e1624aa79e9a388a5078631b4bf7b1d71b7ff492b03879d24f7d4efe48aced9423421656f270c5dc9b4587fce4a1197487ab5b92b7eb079cdbde043c6b60ce5c3480b4168ea0b335c2e32aebd5c4adfa95ccdb8796f854e097f0abeb4cd2a85d0be6ce5463bb14de77c2eb2d30cc17ea507d6a066e8727d2133c62a286376bbfeb5be8f6d4fe8adc44371685ebf9d843edde3cadb992ad9eae20272ec18fff90f44a602c13a64218756e0affc3ec8919d70f11b86eecadd3e481b5334de453a4ed9ba318a7df66a3366e18e16ffc6386452e0f1cfbe6f61e41b5b6423fb1cd40ae5a884dd68c2e396fc5041cc957f4827e8b796268b7c97c25f80ba7c6f8eb8906c240a52162402c6669a62f6953c77525d241e13a0cfc42e8e9107cddd492b08a452e9845a16eaaa942df3428150915e8e62a0dca2a6af6b7cb3cf2fee2a267cb831de31f1357f1ca9263a3cd0c05c87264e20f192dd59412abdff08bd5bfe8dad4ab0674a81cb631a1a202309ffcee12bd58ed3ab9c01585c3b20eb9aed7ebebae70c6326ed8eb19c1b05d48de221504784f30282bfb3a52e7d0c0ca25601abf79eabe090855f26880bc8b7f1d77c6514ac1f09ab2d29321de96c570d726f6467cde180f895ec3c2fbeae36680c66c08d053d7816d484578de03d64c22457b53b8cfbfa40fab9ad1cfd93a8343d7db99529b5cece2e0853bc7f0c20f348797e8f3bb208f2949e64bd788c9626880e8de2e21d092e906cf31e2bf756d33f27d8cbbf2a0a06916ccac5118b3cf9a15dc265f61f874637a17b3f026abb91651d02e2f74be731cc86d4d32e174bf2bbd84c31a6ae5d3f7e9a129d24422e2b61611f852a505706abe7f21893fd5bfb0c0f606260a06871796246bff54b9b1fb4bf57cfc8cd6d72ff3bf9b31471c84c1a51be4774e5c0a17911e8c35a3f4d281a061e94bb5a75df2b9e1f0f21b6bcb0b38a37a3ef32e549b74181e32230c950bb842061fcd8f6ece4b4178ff27b8b8c17da1eb987ac6c1ac84bb41afa445190652c6d8e21457b8677813399d59d40c9d5e4038edd0eb97992cfd3607ec75d1b5b2ff97ae76d93a041a598b9b42ae5e2a840a72058ab80e854af4e2ecdda6387520473db9c345ba22eaa93d33d6991ffbfc996ee939d17cde47d2d4b73b4d10ad6dc0762b0b626a8a411358f362e89a942c7a8b466cc21770b5061bcf26faa3811df30592b8194850377900d4da4dba5196c6eccab2cb091bf4551eca27df5afccb0a955b5f43ae71df731ec3b02c93ab27a000c7304cb3011bdf2db43bf375f1910d58b6c44e270423b5e480160b8301e05b28c0e2404d403636e0114aef7181ca5dbf17a0e274f34609e59fb6b023a50b969862209d4a0e0d1021305ecb60a13fbb58ce2f81306dd7ab2181409975458129d875694ae7a13c1a35495fc76647f80b310acbb78c4ede3c2812947472f011606919b6ba04dec376b3e734a07b506804377c7ea4f6120c7d851690fbd561c01ae76b8b79e1935c122910b263ec61be06c75bc2c7f611ec1508971615c22641ef0e0e3ae003ddd25a4ccfed87b82815fae19da3573c813479c6997212fe2b215be94be2921a73dec66bb97f25b92510bffa04c25a85bae883704c91cbc82ab3fccda12732d94a36b29823ca4746affa834bc7be26afc809abeb9269b5493fa9e9fd27794d1711a85adc3a93871591dac2ce4cc39a8332dab7290bd7423cbb63678dcf2dd2ecc72f243ec9489834080d2c2ee06f4ea5c20d69b9afee922fe2ef3e4c99e80fa54785d4d32990bee9051f460fe02dda20af7b70bfca7a1e3c1b5c42d2e509961a102745eacb08e8b3019e79dcc019444bd2042d4cef10f1bbc665ee2164051a4adf0d4a278b97dc50d69d387b4fe0a015a2d8d3b95492bbf54b10400af950c9cc5b7910ebaad51e09a0129f5183660db0f579242e6ab4d0e70e98d0590b427ba1969f05e949c2d28061351bf832e63c134bd2945dadf3e36823faa5f19bcce6b8ddb8af81b643b892594f0a4c35ed9b77383083ee8e06ad155ff61359a939f6dc39ad6af842b56b4304c562089a1e5f2b32d21a8a6cbe96cab661da67ce0baed4719fec895af4179814f0741b893bfdc21d275a4ed8a4d0af98d641ff746396e0d669c92fa6ec5094db8a3b10ac697fe3ab8aac1d4318cfa36a0f56e91f6de0579aee033f77e9a757d7284368ce5e8b374a08540c8bfa08d55ca6fa036d8c30ff3d6d8dda98c2b4e9742c68f1ef89c5935b0b930791aa39bc756cb2139b6114d856d9ac9cfa940abbd0abf6d959b56c169cd211c2b1cc72a89f43fb52b0e728ccb64d4b6b4f42e9d600dc0f8542ec300258f2275afd6c32860ea73f82f1d77504183906a9801372d70f56fc6700295b99a83e4cbfa524045a33896df7015237377d9748a0341e5a5faecbdd0d9f902f429913836eddcbeb276ebebbb1d2223beba63e69fe30298f387d87e1f04755200ac8cbc93529bef2bd80962a25ae865e4bafa033bf29b0924957812c228984c2738812d203f65af8ce20c281e940e9c9f0c6b99c4906ff397173ea1f815790e3a55088e78d3fd82f3f7638e30cde40384373c1e02477d9579bd6de1993560dae29e0b12e0624d3639217ed873c6926b3460688d428fad0aeb687db9fcc3b36af336f75806f920060b7c3c01ed9e76b43a67d31330f578187e684b9a7cb0773178070528f9e1be1f3c36a96f3812050cf491a1e414efda1f46dd1c4da13b7dc1614a627d27366d30dfc5e01a52f3212eb208398396a4818d774364bb814be91fd7e27f36f2ed61cbb6f81bae58dfe61a7b19a29978c79f9f1fa23c3da26a9124c81bd8a6e7ee09a5e1f4e6056c1ff6bb2a9131d0e2e5edf6034a59d4dc23beb14d673b55ce77618ad1e91b88a8ce0955291f773ee8be47fca68f44c64612a62db627296d50f74445b47222151142e0235701d166872d863298bad5d9a9bba068a030bedc470fb834cedff4dcca543f36e84515722dd1cdaec754a591e2b2ffe70c334d0c4e12e10f0fe9fbae674b9e743ed0f59d674a45b17c44de1149d4a0078062924580ef266394d70d4f5d436248bd5c5c9156342b9fdf0496988349aea451d356ecc639f8f2322aa0c354933e1aace38c4602b6419d424e27e873cd401f4d74bce890a33e6b831260c899a1be34f709f43f68d58796b80e3fc8d3970d8a29ea3d70353f550561faa2a76315adf09ee7f9414669e1ac9f87261dfdaa4b5ceaef76f9d34f9264711033aaec27ac1e5998e95ba38da9be1f83736f0b9057d0f6477ffff38ab0b9daee2b907688bf35681ab33372cd4431464f335ffd6039d1582957816b91348c0a66623b9422795d797eb7a087edce1b35e0debaa8ab2d53665c8e43880bf0fdeb1a0b38273e5bdfb6c16d52ebcf28db601d146b4bbee54857bec63d10fc2bcb486e7694eeacca8d91307cfbec3935d2d762316c5616d02c1f91553520266aa56949ce2487dd2617ac020d83e9b47f5d677cd0a455a24caaa1244126eee3d6c6ab6d94608dc5d0675658c3861348aac0f46deed4d0e19d1981ecea73f28e2854c62f5f7f5078869f3e18f7e77b2be0843935d50be495e9b5635978c77aade571ee4aac066895332300435e00e79418362cd2cb03f4164a8c1a348b80deec28c1553435e97aeea1a4cd3c0662108b32aeeec32ff109f58914e20c1b742c715e1b50b8b47aee944b64b11a691454887dd9a2316e7f7cdf38d195a2e91aaf97a46fbc29efb5dcbc63c89ffb5143ab8d4b243470a158b07850bf1084295148b422e024817830aa615c8874c208a8537a24385054725d462bfddedb83ccb227cf4b34287970257f545f3d34148e8e6c26fff538458b91ecbfd59229f178a2b395d68da5cf4a30ce4452698f0135306e7d7fcc0348ccaad10b25df686bdc8d131c46309012e115894744663756279538b17891640d460680f95cb89a5ad0434e13905bcf9da7a6fafe672301afef0c3b72d67aedb8ed8f147e68a164c60c4b65906765e564714f0b0d534ee1f1f91da6dfbe05f3109670b76be344f044cc2690898f25de1183e8340e5bd5c2a27a4b585e32b428d90a675a67cf610fe5761228b8e6c029961b36b64a840960ea1c3e9529cb1d6ade5cae23f6bbd671546d28304979c51f7aaac25528ddac359a11f8cd632758f401bfc44fcc19e76ba8832bb11ac80458cd8a6433b88821cbc80374b057dbcf4607d0072504c14434cf61de7feb2515dd597999f9270227dd9d7c9c242e9d5d8436a46d2195accd86d90c28ec236f44864c1363b734f6570ad865fd5edf34f4ffd3e6493b50b84509bff453382097ccf9ada958380befe2966463f86ce83dec5886e8fed484b62352fe2754bb457f411da78891766bcd7832f9eaeac14e4dd693b8c92b34ad0d821e81573b7bbff2205901092429cb9e2d8c841b730abe05b9d32a3f4081928fdab4395960d8a26e70b81d73030ef2a1399f708cc4663780716cc78ab8e56414b0f1139611735caaf157a9f7829f4595c4bcf35fcc61d67507d08f0aca0028e4f508b4b052737bdbf46a0f70a2d9e43d2b24b07dcc6f3052a2470da80d2c50d72204dca81f9246d4034838aeaa262608096de97dfdf00d491f0e8be274f02ca624a3fa3f5fb46af1ea3aa57e5f70ac1d478a4ab7e0f84460013ea1ba3d42517f185f1c7c854681c4218ca810ce011c30d5d205054c58812d1ac946d38671a00b3ff3ed8ad2be3f4f981f8c59712f14b8dcdef20d26d8082b7f139935e9cde131c350ef97b1f3c29f459ac456f6b543bb94b1407cb50d0ae19f0ee428f79935c73999730e15164627a23e8b7fbbc123ecc78bd505090e9a20c2f485f0cb8e074e16edebb753b9c41b74366539b9617f1a0753581eeb44402e93c5cb01ee8215da3f4eabcafc2c639ba18e55f192b29a24f8011a54716beb885ab10a79caa3bb75a690bedad7b9f832fdd5f112d579296fef347fb487e506d2d835fe05988130b3c7ab8058f735360287bee1775848e5400b473d8dee5e5e49e088065727d72673f788ad2d63905209c9c90d5d7970b96494a2733abe136454a48722ed19f392a6caefbe082c109a568ec110cf146020410e6ae3584444cb4d59754ae8b4d22d52c3bf7f2ec907dcda65e4ada081231df4562a7032f2c4d964d10b8a3287c615e89151649e08b97008e464a0867e15286277cb9492b71abf136ff0497ce8808a8c8cbf1e52f8a856dab61e56c1755937951ec7c70dea12eeb5ae848384457212c317d46c942f377f0463b30043b41f0a0f67bb12f4c0807f0c304ea2005dffad0057602fa7c633656d18bf681bda8ae2f7ae73b336c2d034719f0692398353aff7d16ca0671c61d7f5adbc448a1cca51f4de6d571c4751706094f1be1602be5664f6eaa6372071c8ccea8de6a2f06ab83c2273be27727082661878944d76228ea601c1531a01807d7c2f9298f0fa4eb8a4a2f0fa78b91ca7e0a9210dcce16249532a4630746383c072e303df59aa34a019e25af6f2dac4809a77d6f90a29ccfb9f94ca4e3d39473035e61594a49bc296d3ae5b1a804a7323a972a1552b56aa68f12684519f02be794d06c5e9849dd3f5150beac987d80d2572ba1103230c1030ce349618d8519ca772d9921d12a89f2c83103af0fc8b5d189dd90841df9c911ab72d28319cad9b544edabe35886309e107ccc09e96e3284e0c87948b598caa90b022fef9b03726c59b7f23e02bd34ebcc9a185d623f599527957a4846003dda1bbdaba360687323df9d5e9f0a10c911d45c4fb8bdf0e8557fdfb45323174f49bb33036a7046e8deb57e602cf8b890657da8ae5c1cf97c763bfcebb5e62307de5ec47f0a7c307c61702dff064907854ca69a55493da7273bb08222b17eaaee7fda7c0a18644d84505de606da2be649464765d77d111159e96a8c44d66b4d59bf162a1aaecaa8c464fea76cbb117b55a71c378cbdb38edd1e8ca58e00d3c8c97bdd8f12dbf147c13c01a4c9000e26788631d62d3b736036882f57a397a5ee2ebb0a9946b6aaaed0f56182d795941216a947afe632b3848765c00cb0cab8aa3f7797978d3bb54b088a4058bbfbb660377da40625952210c0e8083d1623402060ebdf8dc697ef93118f15190f36c911d9088a78ddb18ef7b6a353d16bfb28952f3df6a520abaf632a00a5115ac909586741495e172c1862cb9af65bd2472be1ad2c5a96093ad05be1d113c4ee6ab50e50d766d3a1021a790b9d20a1b40210d2c9875b8ba95b5b8e1ec3e2bc85d7c5e11ed456d4303b49408a6ed7d5844ddfc2185ed7bba8a265c63e47992ca6fb0e597721f44e1eecc1100b11e6f0b96ea0e4d7a8c35d79e865a32de001606555fe5c25aab513fe434779afb16190168038c20cc496e114c7987343d8adc9976bfb2e689e1e3da4af10f6b6c7f726a9b9abc9339e988169831dbdb1a52af73cf6cc7a83b2f18f6306bcafacb21fa1934c92eeb94f26e9b06b128ceee241f294056a7160f6273c071ad600cf1ad829f164590cd507011264bb0741676e4008d28fece5e0a8f59448e4b35a8b735c1ea2880333f07033d8815bf022e2285b0ca525ce3acac512b4dd1353e1cd97140886b1d48d03a6ca31c2d0b83bf98008c8ddbbd66a6bb192ec0689ed92c93b35f246f69e8e946e6af5e81ce52af6a9c2590a2790e46f6ccf38456a017aca453e93560f09b9981c2e422e2c834864f3545b026987fa5a01d729ba98fe84c0316946fcc4371653dd99f2e560ae0b4bc60162412b0bf3975987ba294b59e2877ece0b71b6c448dc2d99e1f856e1cb4b84699ec1311cea4d29a2e3824bb703480da5c08efee30408bfd6b9272481a39caed7c82402eb3988a6893a3f5f8576a84946a8e2f3600f31f7f2c9fc27aa4a70b6bba8f6609764bb2ae05e43411b34f4ae1e665084258e4c0904698c2d6b9da455f2b3a68c876c6ac722e7cfb22d658f26fe74a9b438d48f51f4529e02b8178f6457c832f4e8ecd3a7c762037552a5ee8b7fca1dee72be675431a99a289668201f6dcd4b37bbce033a3c59e5e115ca00f7702a86df9b5b27915122a7357b4a16bae0ccc855103271dc4dcce6112965d42de6ada72f51909a08351900146c317ee13b27d8bcadc42a860b1b0472ddb272afcdb71e89485044019a3ec47b3ca960463d651eac263959f01380f8f919e2437872c0759b8e5c09264e0dac8e027e662c2abf75c1b1940c21e84e87a754d703713872cf1699547109aec7dcfc3238ff05873f96c1554eaefd0bbe377a29e80de5b85d83700e540b3b9d3ce0b024e41104ba6537aa5820da19a703cdaa62056cecf1c67c309c701df078e6b31026ed56e5e75da84c77bcc89632450232d54fd155fb03b19508b05002a310906a7321517d409d21fef5df3695bbdf86bd0ea43c5562c30411d9ddb6dc52a694640a3f0a930a7a0a49baab77559830c7a7c6777777ff23dbc3af9478632d47f7ec6c867d0c2141fd7d521f305e89359e34a89f2251047586342258a1c41caa2e939821478d31ce58718253da345236ad60819af6464ec125205c6567da9a6823679ae60f3346ee1760de63188691e6d2442a1f46397f985e029991b46dc70b368d944dea038aa0a0f5b943c690109eb08365825dc7d102a232658b9cf1de8a5c2959ca28658c314a12c827121dc3c14475d62d7fb617836c624448d8633f4c4c462c462931a90452d65fdab4171489667452d77b3e856ac618e35c4f6e552aa191f387b9bdb0756d240ff5fbcee380f5e2a3e20ba5336872386b2576d1054432c618a39452f20aba075ac728a594524a19638c2d85849146d05926d823c5467bea07817888c5253e020285fd3c94f1100f4d3efab9d19e192c7105f6386b0510c208218430ce28230ce6466512ccd2a13a91214bb22c215a32549d159fb92f0956fd21843470f5603761a8773295793217c166009b4c5af4d87fb0496d9e73a7a8a87987818ea8e4ba39bc3337ea10c3a28c58ef6059e0cb5f722ea58cceeeebec316e841c9296d25f3e74a34590c51e93f882b9517e652a7882c5abbe22131cfabb7b978461779e654a39290a4b02859a1b151a827232544a29e5bb9498942e4991247f3d967a786c1913844390ec52c2b9514a29254c179bf5c984978c31c618a5941247062ed7faccd15bc818638c314a29a5143782a1b880a3ec22482388e2db8821cd1831677c4c76362f6463e3332c903354df81a33acee6f3bff9f25ff3d001eae8201e1c1d280cf62f5926dd3413a9bd1c1bd4cdac0659616b6bfa4b2468ed30b922b2ca8e48890414230a7a7102f44550955c34fc7e6805917b9c3366934d36516cdc666e34c610b988d18f986a8c928a18e3a909378521a5134fc5fffa6408598b17f8dc286b21e4594c76648c31c618a39452da3cf119a44c22e7d87d85aacb44af46e832665294bde8eeee32b6df444d34708da62db0eedb24accf199dd0969312ca871042093b1a21a53db06c73229b2fe3cbc766b7af611b0efed14b3db0c6a45319d316a94829638cac448c3e027667c87d53e3016a1c4069b7fdba1a3f2e25bfe66103d4d11f9da06c803ca00ed4d16f32c1bc984c5b47eae6954a7ac9f97af17968d55c9ad86f823a7352ba03777a073939f84a7ad13a6e6be277a4eebb40f587ebc507c2258645efe0cf0b9c4570f9a432533024647a073b14935366922435b94919a3141a236f949a4cff9e874205712224b4519b1d2fc28c9874f7f7624449ac0b82094921a1fe190fe551b0852b7f4eefedbf444a229f40a1a6a99238b842f59c2b01d86555ea0cb03451013568d30038f842264d28312c4a8ce314f772b06a7bf79f476c9acc49229988b4eccbcb71c4c663cf0676d99e4b3f622a94d6afb66e07a6e92b70bf34de90e9930336afc9d2073dd18b5adc622946295b40a564e7cd64faf73c14cac66688cfd090f7cc209baf7dc47ebd38434926ca038d5ee4a6324bffa2c52998905e5ed3344eaee2ca575a6f1ad72bcd378de39526378d83ab12b7c90702137fbec3ffe88e27d013101823f1e917bb13adf33f4a2af590118b353edd9afead293881788d2fe9de2ce504b411f85eb77e303e0ebb11f8f1bd3bfdf01adf6b746e7f7dcec738b6dca394524a19638ccd33371aa57494ff6cdfba787ea64419aa47f6cabc828357ef41e95af966d81dd5d97bd97254a7a4ffb68a55ffb83c8440df1bec4a75a70175f7652132a6fb4c4b4d2fa58deb53f6481829e388b0d715eb866c4d7f944d723dc0b9d12b252a425d5ca930702ab141d5dfcbf1cdb81f99a90c650fd8f57a977ac8ff7e6d548fc9b41923b2281bb03303180fadda3ac84af5286dc1da6ca4b4b1e12a090358e9c7d7ecb509975fb46eb78644ca7ac46e8b564bedef8c3126b8628c53ba122637794e789e6f3fa1ad9452c640e73b16b7927c79c1482e47e05b943039355226a794513a9531658c5101793029860e5ec293d2cedddd1fb58355a5f7df4e229fd88169fabf4d9decda1f720e2184117b77c5103b222cbab92f0ec9b51e3b4f2261ba74e9327d90b24a9cb3e3dc4a1fa594b2b4a132cc83d08a3d4a4391509913892e6397b14fe10546fb48caa67ccd378d944d140ff451933b0c7e8f1d9d7f2b976cc881b521e421a2d8cc629c9352938905514a8f95480aa29991668c2065267f9b1f9bf5e933444248299f638c12462965472b40992d605d8c31ff79f43f948cff6c62fcb7ae17d306a580ac173f3edc940ff67a8b2af693ba806e32b8189cc906c8a634b05e1361afb499b48e2483b4aa08114efda0814d01b15e0c5797263e0ce771585420fa8bb4cae6074eed7ad35597083930a8feb04a8e0e1a86e3686afc35000ed51f66dd733562a0fac327f273a35b946d8112030e688cfdc055a4ca773f62aaf29deb266c131554ca29698fd89d9c78ebd35d069a0d8aa46da5196440d9d94da67fcf43a16c6c823c212434434ccbca1333ec68d0abf092242d470fa594d216accf1c359e307a410e9fc2f424e39c93e161d773251e3960a2417c3860abfa0f3940395472a654ef207cc97d471cd6df1a7fe89877cba457c067009aeaa68f729bad1527439aa6df145fba52f7cd2d8618ea4765cdba1f4a23a0f0872cad7187dc7b805576356e7016e1ff91e3d7bec6183fcacb3c59857decbe24ae0435354dbf0c0c5ad008bd015dba74f1ea7567fa68638c1c638cec0426d9a3a0497fe8b0d46362f2bd63a23552168954f9f1715a8b5becbcd483df25d63986b1173b9451c739bfb93941e5c7ee9b1f8397eff0096d71288c8a1f464e760b7fc71328c40176499a9b35967c6c855d2c01d92e409274a90628857579e7d1dda33bc767c618638cf993891bb1ece1aaa8fb84a911a67e94865865e76d4d3f8ce7ad4f19042c71450a32ae1d48265a3254d9c71fa7997bae431f18507f9f506afc2fa6ca0e026d4d3ff30028723dc0fa41a0f5097da40ecf0ff461a21fccd803a13fbbbb7bc78fc31c343b0a9b7e26ff221d7c715075af82ba0fc117047a85754dca01dd57fd7888833a0542106272508fcf60ae63a5ffe8f69f09e6bf5fd5cb7fa41ae767a82428c744fb207860b158ac157d0d869bf1f2428d01a840898ba51a6718a9fe839638b934f1b7971a3fe37cc02a89c657fd7f60553ef683d6ecbd044456d921d92e95d4c5ad8932ebbe10d408975f66e75b13bf582310d57f13105d8947d4e48882d7dce28c524a29638c11670a97bbe646d751805109ab9450a894914b3da27b1258e543f9fcb2f301d6d83918fbc26646f2a098935293e91f6709972b4ae95e7d0521a270c8f88c1118b61ee61553e2476670007d0105e7c0cc10ca8b012966537a703e918f5ef56e08f67ceef28744312487d60acc103cd0c2d74a4f5d00e4386c22e4cffcfb1153f57fef09f5ee6f6f9aa691cf2528930aa69ff14c0330c2144698a65a8475cc3e3e6dd5f6f2f1513b30ac09c576ac176d24875a6f879217ee4b9ea3368e6a26129771cb039511930f8fd8d4f8b00424c6132c5430eb9622076cea129923d9a3f8c8aa7964734286bd76f7ef086f9539ce87a51f70044ba4f8630f5f3efcc861ddbe7663c713a8ffd6f8b3d423da90533d7ae7f3c86684298c30c556034e38c5ad1ddae75da2d8d08bbdf9e582cdcb00d42a197e6d3a195ec0829efa6d3e6775eaee44011e2fc30b51c0db744aa4f42be0bb13c2e36578193a253c3e679504b5f99bf72c45bdcf407b23f7a6eb3e5759157c35cdfec48212e1e324d4fdadb1053627a526d3bfe7a1b00d9680406c8bc79d84c8f0de7b9d12254bf68a26785652fe952ce11f7811b492c2bd005e86cf59cd1550f8de2f8edf6f56b73875fafdd661d5007ebf9b70eac6ef374fab08f0fbddc3291bbfdf4f5895e3f73b0aa74cbfdf3fadaaf9fd06e2548ddf6f2aac2ac0efb7154ed1f8fd0e6a958edf6f214ecdf8fdc6c22ad4afccaf017ebf8b3825e3f77b0bab0ef0fb6dd447ad42c0ef3bcb99b06ac7efbb4e02dc09ab52bfef4fdca755aadf6c67347d402406ea986e0600ddcc4c07b560b07f7a00852e48423dd0228469c70748e899010097030b88cb8145657b80edd4fde778e213f6f69fe366466362c8909941a386c9c60deebbefbaeff41cf75cf7e178fe9cbaa51edfc11e1edc76c3650ae06c38f8ea8fa1fb260cdd0bdd67dac7c191ae905ceced9f38d21047d2c2debe0d8ef4626fdfc491b8705a8bbd7d1a9cd684bdfd199cd623c36932380d88d3acb0b71fc36942eced534ecba215692a4e3b6a9afd14a76d619afd04701a51d3ecefe0342c4cb38f004e0b3a00a751619a7d03701a8ad39e30cdbe0e4ee3699afd02709a0ed3ecd7701aab69f673702423a6d92700472a6a9afd0170a42c4cb34fc391849a66dfe3485698663ff35998ee9b1fddbaefb5eef32aaaf29fb2f98c85531ff6535786bd53d6d1c035a3b2cf2e4edd1862222818d57d2ee22dac62b21169e9a98b62b1154e6d152864a9fb1cd4aa5eed6793b11f4e7d994fdddfcf7c3875823e300a36855318104685539c82ba8fb9302b9c6214d49db20f547f92a01f746547381d006e5718f7f1ab6eadba1f3920ae52f705c02951b2243be2b59272f25f82802a442b2927effc55dd88bde5a95f7624031700ee60506644bf7e99bfeac2eea34b85bdec0802b1b70fbdd89badfb31f0d00fbab69fba0058522327009ce3096d673e7ed57e1e9df992874b5402e52752e12f078027d49344f835c2a036bca5f6f3164ec9f77e6d967a402b4a38cdbac9200832cc1da5a139c51fa462a5967acc1fefce9c29d3027d1b7461a67bf6fa73b0e7ede09784c22a29f377df35274171dbbd79a1eeef600fc21d2c58425e0ef6a012edb727318944ca489dc7218128f6e092c642b4da5ee3966c53ecacb4b721087bf07db0870327f8c649e9d73829d873a9c7ecb6090a3f86236c0802b3391db0454ff5ca838f205bece860c362b9a1ab0366acf37bb6f468d85ee7b373fbb0a9a424519bdc5753e7673fb98fa6cecebe01d4993177c4a662d84f200da8185714a7b01d2cc51ffe7c2deab7afec6b08d1ec49af91b89f29ba52dbf38250448aa854f83bb08ab3879f638d2a3c41fd76d41802d51a0889c33ee3fa39853d01aad82a5330aa6c434424a808a7becdc203a7aa4c014b859f837dec00dbab70cb10e8967ef0e4fab774034fee6bc66d1529bc2a97cd39272966393e9b1a2712d8e5c70ac108203a706a76275a6588a644a938326e071e046da907a8cb8328b450616976a61a50d30ae8e755f8455ab52b081fe38a703bc0af82ae1076762842048876972e15b2480c5305160c62270a3b2b25374c3464b418982f75da155eaed546822e2082cf8af44b78044106aed5e97356383e6705c3e7acfe7356dde7ac6cd4982123062d2979f9527712927de94b9d92d2679fb37ae17356317cce8afb9c55ce6abfe376098e33695028fc18388ffb2f5ee07070a82ef4e3d7eb05d71084ad96d1d18b3d7eb1e746157b21817e04b437b0f2bb0df6463e7f16a2ca4fd8e3fe99e63ed4dc7a6ea6769f2938849c88b23daed791f6f34511f5eb27cd8a3db429ec71048a3fcf13a56391cb81e2eb753464145f928b5e6c0dbf47f7865cb61dac2b5c5b7e98f04e8b75459525ead74fbae30fa7bee8457dd5299c621d13d425aef038d9b6137792008e3f3d8e0e081c7ffa3867fc79acd4235e9103290e903838de86e823e827c6ae0738030714d61ebc7eb185838b91cbbc686f606cb57e34aefc71a855710a512ce510c491177bd35de3ab5571e85539c630047d810138e24fabbce8856e9b669be6f923117b1363b0354540559401a4e1ffbf71e3e78badeee2cfd630571b54e8e745ce434d4ae06abbcf5463cb8615f4e3d76b6f56d59449bffad5a4b2084e1d10a71702ae4ea7ee147f57a7e7f71e5a63c842e3891b628397ba5700fdd41d88e080dee06c70afa5062763050adff4847ef107a855fcc5d2f03387e1aa3c82ca1f9b88ca40ec12e257a603fa404c7810d47120f843839bc1411e2450f832fc32f8618802ece1600c0e965894870d0aabc2180eba60100f7de1e050d3f0938ce0a645e8064e7ca2f84cf9f9b9a1c42d6cd2a567e54da0f09d09744b3574750224799df0b2220bc756dc012ae271c25ed429a27eb149e579e23d3d0ed4aaf8b38329b6ecac3c0bede2a7f26b6fe4de7021d2c21e3fd1162f2aa2c21ebbab7320f6f86100837eb1058de20bbef6a62bbfd6d21b4211da424bebb057a942d4abf8d2db8be051855cd8732bb1ee1536d0c2a499e5452d9d56b39af0ce4ee52dd5f0428d3f4ba05ffca9b0f4a3eb27822abfc4ed4aeb0d08fc5ec8ced5bb6fc9ae366ea32085240cad346e57923302dfdf091d25a854e9c9520f50d7950517d47e6d0d7f3cc5893dbf38152b3f1b716a5d5c742a3f1f754f4761d5328188b9706aab58b1c3af2fbaa2d87eb814a17e3d31e9409cfa62cbc5a9af5f419cfa9c4788535ffca95438c52d22eae7565af5f32b7588fae1a89951fca92cb457e800063552910cd4048d2d4e9dd8c55666165cb17b5d81414cd08f836ef07eed4dec4e34c81a3f762cb435fc469c622c5916faf5e3d756f78a185851bf6671166e827e3cc429ef4e4060779b0d481c13715116e8a6020ae58d1c8c5346da6e24712721a49c95c69d84684a66d3f86b9fb3da2d66d98bdd89867571a1739240af4e12e0df7eeb44407afe5dc1f7d7be243c597e7c589add0f9b1a5535beaac6c7691cc66123d9c7245cb3eef4237ee7035723f2d7c5854e95aa2ab97515fd548ddb1589db55e4785055f83be39299075a16148abf8683e3005665f2d7abf3f73f9b39290d92714998a6794801dd69c2284545285e90b781df6462ef701887530b21dc66d80961af83b0d75c15d42bbf0338f590fd0b66be2144bd3237f110324326eaee76ef6e6feff676ffa1113a8311c3d6ece7d4ad5b82e1051ca71b364c3568cc9091112386c2bc94368d944d4c466f2ef5e330dd6e7709cc61662d1af42c28bfc6dceefe12bb6ef729e95e62a0eda6f87d57d5fcb0dbdd87bb8421057cd7fbb77b994de0b367edc7aa91e6947feeee1b77abdd95800855a0dfd736cd78393835e4dffe93bdc781df886fecf57774c83db3ee7cf8717a924ed215be0b216ef799e0cf55650f29cc370db71d5f76303eec9c3d538c5d0f5e8f982a7c29c828a57c9798c4a4d7d825e10adfa9e0eef0c6468feefe53da9003ab57ffe07fcc70c297330ba1186394d284ea41a1e6464d419a97c327cfdea29412c3b028636904d24b3df8796e14a3a2273e0c2207b1d6374a29a59432c618db05ebf38aefdec88f9a8a42a171dc7a4bda6c6757d9fdd6c4e84fe4dd87d8b418639c73d2dea22d6fee5bd45bd1dca84de5b701428c5146982e3c89b076be3531e70a4e93f4113e3d57e201f17be80453ba54434ef59fd8b70f5cb1d7e6d6a51fde0cfb65ce0e4957ac93bda51b62e745c02eda421e0da1e47c602863e46f1fb8eb41c687ab23fe46eeea48baca6e3958614bc84c76aa9c1897b1883c077bf23df6e4a3d893b5853a28e3fa0af659d7575a8853d8cbff30ef6d4e5658eaf14215146ba12afda769a4730b63b0873fadea16c6e5c022b928afe47bd1ded474517d8b2a3f3641950febe90822dfb4e662d5b00de36690251ab0da8f7158d5b00dfbaef2fb8a8c51e5f79133d991df58a4785265073db3a4b0523fcf1b6b71ea93d9e1d487c3d4c33a9ce256932ad949953cf5c381956ee00f6c6a7bf91ed814cccb1712c406f940ac0afe348d7cf9436a7c3c173c13a623a809aafc60104172bda85236e99d22ec658dfa75508d26823c7a0ad4217f4aee2754f9f0a755dc6a9ad27f35de53f9301bc72d2c3d05f2682c56877ce923a8e93ecfa8fe162d1f05c2d6c82aaaecda4efab1871c0631d8b0bbe6614f76cb3aecc91df65ea874d0d6cc981c82f069177bf25fba06624f9686a0f2fb49fbc497dfadeca589ca2b4c2325ed1a4ba95bf6b6eeeb2c59117bab7133b0275fcb36c8e1d0c05867f30215f49ba9f2bf47710a56f95e0e4ec927717d853d99752dc49efcd975107bf284f51556f50f8bc5626225bf8338b5a9ead3c57a5bbf1a5a6896a5d95a7db02ccdf6c04ed54bd9631c576d7138e36417f49bdf030a7d28ad5fd1ee84fdae5887694ed8c38a75dc624f7e2605fd2ab520bf0eaaf26515f49b55be7c29492cc384b633d88a9df268ad2eedbaf3273d73c23d8a5b76ca9a7f383585819808bef0c354b054c873a542d65261f6ed9287531fb37a38f5b98ff4e1d4d747d2c9d17ef2898c52e1cf1406ba822ea74eeb59b765ddc47100a74a0fd9461e65599665fd5fd72c6866d9c3ecb3eca83a15fea1428ae591b1a00e9f2fc78c8f8d3ce29e8c057970142d9bd977f771fdf88759d079380ae4e14fa08eae87ab951fcc873d1e7e88e91cc9a3c95d0f70739e5651da499ea6c93a2ddb6612f4c37c6a14f6a0ff34cd13c8c3a5581df0a14f2ab606fec7f5f327153e73e73eb16e95574ffde4d12ce2d409e661253dfc8cfbdc0763b52a16613e58cf9be6cb2dc097612c0fd904eae0c2a923188eca14b54af2b46c0279482b5007fc37519e58d4a4d2d9c9a31eb3339966117b5b785a25839a069aa8d4acfbb028f06510d67d33ebac3319f452e567cfcccc2c5bf667533b6bc9035f5a813ca411d4c163a5c2ee301ff68eb6868b34823c6418ab630b5b035f4aa30a7f4615d4ffcb58af0a3356c6246bcd17a78c38d5c3fc24b36a0f7f1e712afb197984b57290fe93473d24a195df3bcf9342f2cad6609143328b0cc2ea80cf9c1cc2d640199471415f062d2adb81611b58689a0679eb66e6c85c12f443d5c9d0e173fd6085023b254b564b20f30d7c23d710429844728c2ee690c400a94020f67aa83affb7aa16ba8999b3ebc8f5bc5a2d14b95b684b0f35d1164e9dd61b4b6751a27ead056ed0d52ad872d5102bb4b235fe730594e10b06cd49a9c9e4cd23e88732d5ee866c8d7f0bb52a87a5f1f77642757f8d3179c56f5467c2aa77412c9cda2a5e04d50f6af988a840e425561550503fc8059311eef070cabf5d9c3aed7812f5834eaa0f55c7e16f62820e6995d734fede3494aae7a2f07fa65f9b09b49dd1b64077c3381a8333f22396ce561172e2a8a8bd198891a8f061aaf93b46977ac4d0b46d7ef940302b2c6931629adbd8830e85edb319323eb26464c9c822bda67dd6efdda576271f5bfd6bd8eadd8e2ffce7e7541c7e79e75e301b0f6774321d5c1a9c263dd6652f349094d841939d1da8e0ca125cb0cdd9bd5b317607444f44396a96cb0a52b297eb78f38f886d1a473f868342e349dd897f57349e76277e58e56fede7a0a02f5d13753dd459d8831f040dae7598a69535a15b321b77f2df1a3f5691385ec8c274c594202afbe7f0bc2a8a2a77a82aa8869580f0b0002654bad30f9727e8a9fe393db43139b05eec6dd53458f267ce077892c00b2780a2490f6b45e3a1d4f8053071558e46b784c6d378cd937005b20026aedabf0026aeda1d9425343a29fd52fc736870529a8b559bdb0cecd9b007677085aaf5e00ea70afd96b469dc565802224fd082dacf251c505cb06a7748d0fdaf5b9bc5e4c072c11765cfa120027af9b0a1499090201042f86a5513f58b6a41e1179b65b0772835a1f8053db65a2d085403048240104b8b080241a01a5d340d7914424643746832b443dfd0465be58ba37ac4a953bf026a8cc161f8f810b9b6e8d76b6f4e2ff4809a2a4c85df437bc3117161556fb12a08e44d2a7426b5bfa85f85232a08b151e6ddea8ceec0f8f30e4c0627c43a974fec7cfae19bbe7e5261d7722d68276169e067f5eb2157510f6d0d7c1c9e4103fa75ab5bada2f1f0db8855d5781853bf6ef5f7cede34511b310d7c58a36b2d760c7ddd1afaba15d3aeee83810f879e40bf6e56d011a8c0840aeb0c20aac1284ee2c62eb81971a9d1680b976ef08d4b409c61bf7c4aaad635769f076af3cbd51f0cfc276cc43615a029157e4701721e226a17bfac20d42e3ea27edde48b55f1503fec9d21232afc9e25885c0bfaf5d0107c085953a0c154bbe3d7d6c077223aeb56b9024f6d22f6e07f5ce3b724015fd590a93dd4aa1a73526a32fd7b5eb76874ddea1935a05a7f01f4858f0637025441ab58a155bd76516bab8071d45b6a0c81c2d20ddc8d6d5d02d23380e82cf558538cbf476e87726009a5f6b3528fd6e1428d5c8c0a4b3ff6bfb81bf483a31ab95861e986fdd8ed7a5e7b873dd82df6206b68ab7c81a5c25ac261bf3b243570edffe1b597d4f68d39fa16fd50d1e18fde2ff8e2d409b6a08e0a9af00e4c41d8f3b335f0fbd43dc4a958e1779626f2963761157ce26a2d9cda2a5db02afc36fa9ce53a15cae8dddae1d4077f2a7c58e448ebc06e52e1cf2c40e8f0676fbabfbb1fb6063e57f8700aa79430810f59ec4116e4610ffe0b3ef48bac0a3fb25a059df4ace0c756dc8952a114ab2f36893d3398013f803e82ec8dac39a0cd5718c0a0ce0c83f6861f3ebcc2a97df804d81af87c621f9cea0adf06f8413875a3c2ff01326115f41112c2a9adc245940ab554b85c2ae48fde28f8397038f511000671ea94e2d60c15ae950ab9637803e7e0d4c9042b8ebd81b50a196d3f75dfb4022a43ab0200b323e8fcd951c685c422f990a6b0caa687c464a82ea949dd8fd4438a5217be738a531ff422b238f57193d3dcdcc888fa3997ba91c984dd476ad5a5375841492d4ef1d4ec8853cc5c482c66c9bc8cd88347ecedcbb05d3c3172ad92a0ff302441bdb731711fac375ef317cddffcab5537bf34de0de7b9ab69b8de781cb6e118688f6cd8f8ccc6bb8d8fcf47a7c7f1f088536e63834279debfc9c4dd112cd530bfbbcf6117b926f4c3a2d47dd3e9e38fbfbef9d1cff4fde7d9645bd85ba34affc37e6a76d4d5a031a3fb4c2fd37defc9e83e0f15a3cb7c62bacf86494033a0de65af0c003a14fe9715d57d2c0aa7e00d6e2b89fb3ca8eed313f7ad95e73e3efad68a0d1a6607b940f11e87e1ab55cc3c5e0c4ad02f3b621ee8a2d014bbece8e326fbabf116006e573e30c110786ab4a005364db30f03125ef72686ffe6c2f01fdd176c1efbf5d77a93813b09f1f6a80a32385a799fb3dad21d11b023051d1e2c75ffe38fb7442f728483ea26e9ee7878310fc31d51b8c2cc5354c4a9d3101d38d126caf8691f3633146193cdc6c0c23b2f226e93a90f6fcae8db05fd3628898fe5d2926a320618b36930193d749bbbbb79316e3eca6b150f41a8cfce07c6f14a060a283f377bd9cb975363f8e285080aff232a0ac2362818803ad355e66e69fc913932fb47766f006b414a2e5b08b9bb7f6fe1eededdc61d68ee1ee72faf296851281b5e158a72499886872a502f6d51108c319cc561140ac53242deeef80298232d998f6188a828089c1886cd396d5c41a8aeeca93999c79cdd3d7fd99bf3677f4fd39c1deca67ceadd67a23d9f605be4a62ab05ed32c033090753f96a6bf3faebe593706d45ffeb7434578f801b2b6ca16a6d4fe21adca59f5ac3a4ca8d72fc9f3421e2fac4a92ac853c26abee7329763fe277334da8425b06d21a5fcec7e128a1b47e356798a6df25778a0feb16e1d82113185a0f4966c57ee1ea805ca3f7472cc6f81873d862bb3bb1b918ff7cf66214babfdf8500eababa7083ba6dc951d7d545a7d22c51c618e30fa2b020fe0c630140a2928ac05cdc0750d7d58527d497baae30ac34c32e30c182164840794161042618c8034b851e72aad0b245123b34d882cb166ae081ed2183c15a931ee7cb462aedd30d81173e3a7861d1d30323aaf0000d586083214461052bf0a4000c29fead28d2c41456149e8085a585a7987eae94d27fd0050c301da7262a432915b02c91456271052be850da052ba84c951c0c5552dd2a2e29ea47a1cc00084940c2105d24618515fcacf81f85c222830758698214a0d0a0084f5c8941fd4c0c797a6044142bb4c0e4f50fc42a28383822092944a1042ef8c7f46f031858e0678b1457086309415491c50b725084235eb005125842f8008b140844c10509b478a9c0083ac8224a9421a8c064052c7801152014094e4c38a009275842093818421255a860c21646208494524a29603efe1447909ad0993e9b18429e40081a26a8e28a294498011458780a200d8b2634a1e352d4efb5d0821cb55d5a5842d69a257091832368e1043d2862d5388c9deabac2e84295755d611ccd1bf8a854b0042030907022075380d2802d88a2f841124f68190285ca228a1ca86c3982082bb610644416cf62b148f099989ffedb000a24559ad0320250115c6042164f88f84901128ae0420411522c9607fc60092ac842062e2eac7041e5a75f8427b60a472062a2488a249a5471e2e708294d3401b40510b0f4e4e04089297494782541c78a19c4ced4b680051e37f104284d749c3cf1411195d5ee20082c28a83882111257acf8b7c4a88ae5ab2b8c292ada335b80bb40a89f8c8e23258e6a503d0c1254f8cd50fd7795850549e86942ca162656fe9453eca4faa338e5bf83532fd5dfc60141f6065688048450b52be99cca51631735da38827ef4a12b0b46a834aa3f34716ab759c56f9dad12418c1bc4bf83b14e8a11f8fdb25392b392bfa520b23bed6a36cd0ef933d511e4c386203f4026426c56cb16ed6bb978a71835838d0e14ff0eeeee6462e61d6c89ddc19c3333c78da61a5136ecb54692dc45d86bced1348d0477b0581a89599cfab6cb163169e7540ff293cc9ab5e64ac6afb79261b392f15f8d19646c1a89967e3f07ccc7bc36a4fecec29d1dffbc36fd98d2cb520fda49622d7d978080a04b972e433576473ccac1ca9da53217a4f4d2750f51c372f6c6bd1669950d4cd3df3ed8b381038fc4b3714170d89afe1b56c79459263bc8ca02876a7f7f6bdf0f593e3f7b8d29cbc77ce85a9adfac5a0972562188f19a73d833a77d89db3824b08b977a68a46f9246fa56890008192f045cc950c9e84e41627c8c4e095c8940c6c7f89c553faa39c80daba37f72386c8db63725ac8b9c0d5bd36d037b1d3b0f870e6850ef7c49b8fa3369776ee0514046272546b7a52031bad3aefabbd44352c8da9ae6536cd2ae2bfdd0497774f611c48686acbdd9da5880426d1c4c38d8eb2cec7cb0c7ff896098860b34fa3bb393a47d64f833c31e435485306633750c8704da4882d6a8ebc2821335d62d4152851c3fbc02856010e5e01574296dda7b85091a407168b66da651ce14d331313486b6c9b4850d7605dcc10a0ae2d407afa87c8581f415f0676e948353ba586db8a810540254c9ec4d12f686fefef73764fc500c380489d81a7e22b008085ba43eaaddb75da01c0cda1a6ef8a7de2c5b8b534df8a196161f5566ee85bc44508761e987d7207ecac54a847f074e1580e115508769b8053b4839ae366e403f1311e80787b4186159960cfbd47cc6985583f636ad8af1a55f2bf205e6b71352408506a87088a6428f63e8da80705543fbfcdbb4fba829497ce1bef764fc6743e6bf18d58cffb25a7ab82a1a5f2a3d8d199c0c27835b4e3ee44affc221698097b8e592b07fc376137bd0bd189d088088f142c0558c183f572d444a975363e4ac62fc6c1ab601fdca8d031aa3c7c6178ed5102b7bf7ecb9e7413813c01ac3e210167b081288e8e0f13b76dddd9d65bf599665998c09676bfcb353961539e2cf03a7369e04fc246152dd1d8baec7c14cd46fdb1b2ed62bdca0faebc0a902547fcd27b0375185f21d5be35e6de480fa0f69d59c1b58ff22fb45f50dc3d4aa97edfd5d1301102faf9562d7dc02125053240075863f1b185c5591a629e2382de793beaf3375b351b798ba65757bb8aa18bf6d1f83dba5217d0c4782e14893f4cd6d5fe2e6f7c675fc2dcde79ecf73766d92b942bfc8114fd235fea08453f3b90577f885dba669f662077f484c89b4dfd83372423a65339048ef836866a872a551f9718208e941e5d78155db05178bc51ac1ba8c822a3f2acb814525f56d91f30a2a2f1121b74d235292341287636b60eb57da2fabb49fbf714a3edc39c334d98e2f28c923751e7b9c7133f3833a7eb9124e4132d2d3b9fbd8cc3aec5b7345923135daccd5fc96cd2afba1f6cb55863d4e3787c4541b07ac7a00a63a56bb33e2b4e58300f34e02dda50bf74bec1b5bb9ecdc5aa0824c38bc1c54a0eb7a15d5cff41aaadf34e1f0729c82ccdf2a84592bed69abb2e79f1a6ccdd57c12c744089bd5fcd65c69118ad691709a94713cd4f8b9bbbb8c08a85b02b253a8db4139f92b59d70a04f34190a5dc9bfffdbb20d97b17815d651d4b8e34d0b65535bad082b5da1b1ac4a11f6a6dd29a33e2b4d4cfdce9477c7eee3ed819e13f75e7fd3935a7f6e9c7e7d4e6706023fdd388bf372cf1b78c43ee02ef6abbcb3c789779975b08389f5f6bcd5586ed66dd94dcf743ed561a49c2d565d4947b330fde8575ee4299b3dba6e95d02ed82aecb8ba2fa4d2f788884306b158d78d1f28275f28f8fbde44eded1b08a5dcb8651edf9afe263bf2dd32a621f39b92dba8a0f4b3460753b1ffacbea72adb98abf252002a8db4101417c1cf6eed285b74b975594400356b1dba659f6b6b9abdc605b9bbc49595e02b72ea6c60ed4fe41a31663cf6de376353921ae7570131081f0ea2708947efb21a56efb21fb3bb650247b94fd16d59c55ce8a3d761fd43826da538ec9f6dc0e3028923daa497ad70092869cdabb8ace958020a062afc94deb6f58fda7c64969d9dddd5e7ac93a1fb8627362327af747d78eb6ad9282fd6e15e209705f2190c562b1e0d27856fd03952727c5dddd7d484b0a129b6a2449d7f95ecd59fde84702bb544e7ec631c95e0829486c6a9236407ce64a35e464dcaebc6309888c2a716858fbb18c93e20363dd3de3cbec7c607f8c1d092bda72ca35081debe07a1108efef770e562d629c0f2cb1f7d681d6bdb1c7ee8ddda8f2b1caed33f6e9c7c23af906d8a57a27a10efeb81a81ff4988c9ae86af2cf706f26824904beddaa51fdeddd6f0c76f23b1ab61ab07b1da7a80eed009ac5446d25db854f88ca4bb7479e903c3ae078c75c0eef3973fbccf5f7e977050e2f18708620948a56fa8442c98ca9a5432442410000000026315000020140a068482f1804cd223dd0714000c7eac486a4a170985610e04318a216388210400001000011019a26d003f377ab5bb613e6773c7b7ad172515035068d34b4714ff6b36f4da4ef25b18a866ebec8c5ed5ae3710351334a1fa1a35d8d8b174c1cb07a3cddcf370caa0a4f18ca71e5e99be454e27a5bbd3b4d9de8b10fe01d9231f0e709c8260969bde5826edd42652b55b6554c5c67c4d7150b093502931c43656081818b8e4e561203835855b6ba3580ab49429c045171e75af299afe39e2b17855625631fe2efcfd492ec320efbd472ac454ac0d48a413ca6d17ac683863bc003292ece4136a8965134b23c12d9811a31c2595432145f489df68ed5a99f95d0190fd1c40642dfdc45792c4d0df4a89242691056950718080448f40d6cd63c0ea76833a10ac5466b185d102a1a4e7355839cb5ebea1d23bd392a94185e4cefe1e6720f9f29400cd831ec228dea517aaa5eaa7fd2751af49dde8252993126f0f9581b70a8d136fe121d0b971d95314e17ec59010fac9bb7099e2554d4da0ea396844a78753a088b156a13c35b0eccd07f5e52c5748f32479dc65943ed57b9e024b7540fa8ab3b360f07ad38f0b4be0d40889f2839aa56db96b347e05a1a45117722763bb0b7ee74d12c4ae40f590b045b8f6bde26caaf3b7f07379cd3891ed63cd382985e53184e3302c1af07ae6302ccb3d0162060a79104d50032e374709d3b2352aa940126a124d79d880190b85fad77cbe4de686e74b00e1da5deb53274074d1fa9f4eff58fe56dfe05ed1b5a4a9dc50998fa3b8bc46dca3da9ac6d4d5e4878a5ce0b424aedccf5ed6175071495bab00615c6cdcc45b2bc936a77e46aec17f6a6741608de59c18565aaddf8f9a98592c3e6b9420226b4510ed8b4a4a44a2c55822d87c10f7293f21a2a941620984b42b51d2a423d1325f18d98a3fb7ecfd3cf49b48e4b259d678e7efbc6f2b21d87ba3151f0b7b8d3b16a8fb5bd8a3de6f575b6f462d367ddb2d0d6d04dfa37731b791b020fce10a45e8bb872140ff05f1fe61eda5fdf7a7cab18fe24cfcadb5fd4971c8d79274cd9c019e3c3c79af8af682a2ea5bc6855f9267ffff6699908005ebd5250ce40f72aa81d73cf979c417a32fa676b7e582309174706aa7ba44be174219de7c7521a5fa8e07609ddd62350b69e245fcb8f7506f9c9f803b330f2be207b12576284b970715b935c7925b216a1fc0e73f6124472a400b158805e52178a5c404a1d65c916f563415684f90bf0fa7309fe49d7c9eba8e64cb5b9784778059d16543dd218e5fd94730d675b81bfe103fef107f1bf9ae58375e39248ecf4625eca9d31ecbc31dff20b894cbdf73e49911aebcb04e34fa05dd270cf385476397d9e7c91acb81c6fc04bfac104d93f05f0f053c667b47348c0523852215fd47c6913073ddc5efb8378a65e143b861c687e438d9cccb30a89f7072af4b2aa5199301ff23dba48c45f6ba662b9a912fbd49533e14de2da404e36389f052a431bf84983ee5c586e2c73345554642e1ca39a4d0220162bd7e23a0b0e6cf0b1f051b8b9c8c9c11da9a91742d3b660994fc5a78fc09fdf2354dc9570ca1f3a798d40b861bc03d4916845d32063d460b76ec4e5674535bb53b1631522eb46873e13419d20412c24cd02ea3b0be9780bb0dd7ad4a4146a9cf9bea45012b4c3d643de2613dab88ed250903698938a8acc6c28cd685da380aef9d36c0f5a0c1815936a268d3e41957e15804be307f9ab066f5d8ef7701c57a22b946c337bfc63cabd55d7e2f1fab31951d9d8aefaa2cc918f74e3519dfba80947184987f5f22e05321df6a8ba96ba6edbaed4521888cb65a410f893c97482b2ed783d89a103583856d1e6108849072e3036ef39f6e76fe4d3a4b6111fa7badec21f45caf9efc9fb8331219df48783a862be17c2466eba2d3679f8e1fa728e84812dcdeb1c4e0d023e7f32e1f0768fbabe1fc896184363d9f314006a945ff67042a23813cc6b53ffaaafeb0f26480e5458c774b0a493fc8f8ead0774708975ca0d9a4069fb21354fc8037cb3c8849c2fa42c7157b89286b2da1f412556e66475199af4e450d6169a8cd68e07a65ffdd4c2857be10f02df592be35e5275857609e02980a7cf32b77ef9dfee5e6ebc6e5c675d3edc675d3f5e6f5e675c3fdc6cd6eb8ff19b99fbff927c4c5f109072ff7473bdd16df1df5eb8de6a5ac0df4edb08ca5579d79dda5ffe12a1d1018cf8bbe172168e0a7bbed5e2abc6d54fe26a8529266ba3c2477e67a6045d4e2ab5ef98173209562a4e71023df1a619548448218499e589ed90975f1c6d5aa394337c2e32f214e547d71aeeef764a028aae6798cce850b3f839ccee4ec37a4be16eb11e22033e67587b863675e130318f1d1602a414f824075f8357b151b85f110723038cb02bd8b462c65d0621e7993f4635267c8ebc5068d3df1cbb41e9b848967f232ed17db6dec092ff3151bc2658e3dfad7841f8a1e56c5e60c4d19dc88b4054a00ece8b87ac0793ebc1000eec281cf8b27615aa01f25e3afd0b8f7ce03c7ef8dd00898ad28e62dcdb11b408e27de71f3321341d9a8deb9f2f2db0471ca006148ec19054ee8302e35d9378e072dfcfd44e840dc61fa2550b52af66a96937c8a8cc5fe737f43c1a2f6591404c2729e645b9487e70efa7ebd05be6287de044badf7666c4ed4feb51193f42bb9613f79e293a96b204aa383eb9f941bed3347eef13eb25af4434358111b3096092ed31eeb06334504a4edf4dbe4ba59e782fc56495355beb7c5d6d9440b7a6e9489855ecce444c882fbbde221ce00c275921c6f93d8ef43ae1d47815c49201edf2aa880b29edd166c1748b11354ac7b365c6a14749454893af162fa8b78e3ab25bbca0ad69dcf3c3ab34d3a48ae6b4bf76e97a6a982c4b8449ac10c405c6cf077dd54940365253560b7ee4c4b4475403c65d28c2c759c53d7097235521fe5f5a8e9429965a43a6f548f83ca70427f05c3a35bed4bf1b4a95da7265b2513beee10942f2ca95c8daa5c073e6030fcbb60f5cff4cb003d2df06405e2d2655def0b0a1a7243423c409eeeb5d22a0f4cd2714d87459b6095b510db21feb7b7b4715c9b615d43bfa65f89bf941b27c80b1f41b48611daa2b11bda36c5bf3224ed2a4ceb1a377d3129b3af4a492815c298e734fd96cd5c056a1ccdb93527c65e4d47f11ac058d550140ca90eb39517638fddaace56822f30768faae5237e1f55c3d08f3a6321bcb90f8a599caf72c97404484e985f8f28e3c813507c10d42ba427ab83cdb76e86856260ed86b23e5a13cfe74041450f6013d516cb0aea6afa82aae6d1ecd1cfaad36d8a85acf24419c50e63da3ec8ee934cc4dd000e177a4df31d369901ab7670dfbd494b27a16dc6c8e8d77f585cfbf0c45bc9c8dc86a4584bbc24c1385289310d0e0cad3efd8a1a862d8b06074838b6a9b52dab0827a2cb60646f16cb4323da13615342c9cbfc866646e513f53a5b4101d92eb63cc811e198a2b76b99d21454e32f02d8a06c56446e4f90783f0d72ad3ca1f7eeb8e6ccfc8361495f79a53e1077d316e7dd8758c5d582199002e666a8a5e5c64aec2e37384615abe9c52a8eb4e915455144c7949fa9b7a7e69d81b275aef28d32a69e4cd4bc11e196cbeec649eb0838cad87536708627419d5799538c0924276af4e10eb867dd6448fcece16ec464d78856b2b40a8b2cb5745685d2544bad13666b4230d19cb5ab9ce9bfabd2a3e7f6df82b02e744bd312434e1c26c68a79f5a8d9c56eeac28360985291f9b020d4a4f30fe1c1da0668eee333f91d4208996271b57e39e7ac239b24d92f61561ee335007e08b3ffb5be396983ea0ed3dfc4f24cf8d3cf8a8f4a662870e057ce0924d031c1ba98bdbbca5dbdb0478352e6478089d96adfb243cd5adb2af412ffa8e85a7cc60d3bf0ed0fed6ebb638efa405bb183551b35f27da1abf6d5864845270eb8c284e1510f8cd8a8212aa13780eca6db6a02a406ccb9c8b7322c2779828efa72238792930b88594e3f7b14bde0bb007708869dc115327065423b7671e35380a8476a70b0c4ac28dc9ae62469a98a29aa88658c1f1fb472ea88de83eee85c72b295165d8922a560e249541274f441cea02cb6b885664052e1022fe209759114609260e06bbe326c2e73e725dc281136b5cf0417abfb3451ae9d83dd52657041ad6e7f70efc4b97ca602623581181f991d06e3e3fa25803a78d12acef6caaf1607127900b7225dc81e81cbf566ab1d005735278e026e041f3d10613981aef008fc75bc277bf243cb94f7af030530431d817a0bf7132e252fb3b6c64c6b5ad53b29290a38096823f22a23961fa0289add9851277f335edb08e90ce950575c2178dff053d927456ddb8c2ef58e68148685b7c4a07a1e429f28a71df96ab8b68d294411dd33ff1137600ab2399b1a2b9a9901fbccaa97fe886263dd8b4ddc7115220416d52dae0c3c04f9388ecca5e76ad5c84d09f60cade4ef45443733f0bcc55e1cf9609b4ac71636e7afcd59e03c36a9e2fe72e2de3108d2f1b75e8556ccf2218a9739721968add49eade57e3ebc15328cd1332c0f0c0e3de997b853c3a6b134da6865fdc8867a7186017dd812a1cb9026f321e49c27f409b26906afb980c5d95ad2042394bb1db3a7a094b4ff2eab417998f6eb7ebbe7bae9369b7521b6c87cd9a666c302ba0e4deea8a6904a0fb8a9b8f920420871230a3a4eb5e4a9282855b3b15f7543117832439fc1e7aaa3166c2bc45db3877a3b80569c606268bf6327093146a0e035134298f03c8edd736412c2ddfea52cb484618163e0b47848662586d1f4e19a6d17da0a561a68af1cf04c62c8760cce06f41f09ea0496fc186c4512cb54d265344864f81c47d58c043438485835d31b1526da80b46a3bda075df8616580db1c5eb11d701de4f37e86a8f11eb472ce4ccedb8884f038e617ccc486b6b4b34d7f39670a0ec2a013ec0b99aee1c2d473e2b1922d5b8deeb2cb9b359ab4ffd1293e64702c53a927288d6ccf579387a48622df94ad0e4540b3c0a4b5007de1ddb3d64caa4bc2bd52c75c9f901a973a0a41850961e52616f6aa27e4cf08c205b20704fa84588c891be84226479e1ac6411715d6dac3c00855e37e8633963f0fc291608a95132c856caa8b9b5a6ec7d62a15ac6fb4fea54299475597523ae48917c093ca6ed24d70138c4e89a67e8df9bcc7b7998fcf8f6c32a4f1d5baeeb2ac984c382b70d3d1c0a154c5dc28d60c2adbc70a01881e7d67c980f2e336c783399058070b2209bd4afd09e929226780fd5baa2ef7ff464031b05824a8ac42e889e85d9e6212732f4c3f30d3d8701ab37ad231a655a1ae023728a303557d5e6e98b68bd7da95b7b15e9ffb6b4de2daade39c9abcbc619885ece1414cb595c3bb9132b41e142c87403adfa706f0daef98af834507c9c0628962ab657a01d6e07e5aa1365d2e248eb7cf47b0a5cb42cc2b3c32f91000102ed721b51bf1602e7cd3d5a4f6606c96b6f3aa1745aa0b1a8ec0285e46f0a01d91c846d82b83100475fa9ed12250e13641b51128e5a69e7e123fea07aeeadb0d1627483797071053b81628c8bb42d0f5bc20cba0c0df9e844854704a116c3714381c7478f64e1daf2838d8fe7fcbb629cbeb2c21c3e060d1d39baf60068327c9284575ec96a8401c4791cc48ca3df2c1a1b421645ace5c1f66139222dce0adad47cea0174ecc3a374aa3d1f2b5e3e8b48748d85e3a940d7352a805bd7c9591e549b6a6330f31d851531a3f2780ea1729b0bb145e920610369e00446da2a428912e1b8509118fb39029bde68cf02093b047533bdf8d114c2ccb956ea7a1b90b6d63684be84d37273b9f06e65553e8ef6db3d2bcdb4e2bfc126edbb54aba3015dbc3e4227b96648af98671ed6d286f375d6dce40cccd60d6b2c34f910ba84dd22aae682a23a4676b7e34f3f9e0f347301ff35be4b1808765246aee8d0193030570510c2d16471e8620985b52f82282c0422a2d603a3d387599e1c247324cd74b6ddc7ca1b1ecb890a1c857454728a2b0474d72a7bb5163fa5bce701076dda943d32e8bea413af0e13253de6aa3fe9ec13016f30b828bf3be7fbcffdf3b36ef11b2a2bf4c8f95fa36aec40f96d5ee2085578b8008530dc2dfe6fa573f21702dcb2686a64fef40160e8738c1810b2ad7c298d37eb0a3953985f0af10e08d9ae67041d50d800c38028a0e38c7b9aad57b24752a67db9f771b5a959570e2efc39c8f900a6290d66f26cab4d3bb540d069ddb31bbb7e96d10cadf7dfd9337b95f65e006f0dbc8f732293c510cb8530fcd93e15e81564ea390a80ac2876b75f24e3ffbc217dd7d05d35bdaa11bc619e7734082034c7c485d2d4041b9610eb221aed05182242cde059461c3c0b818d4a0c03c83bbf864ddfd745f9a937ed2936394287350b4ccac57c6601b65ecfdf0a81c70e6e285dbfa38b2c47654621f002f12c1b33a5d4e23b45da467eeb6578f86f6840cda781b221c3f18b97ee9a2dcfef2a9c7fe16ede06004d067436990090c2585ececc6ee86964e02dec5de68eb3c64cbd016e25746bc18c491188c1d2c7fa4115bb05d1c8285ca51bf7090fe68ac50090f3a140ae70d88afd2d085b8649d146923f1facaa537badbed807b80d662ba08b0b728dd9f2e1193419b1a45bc6bcbd2418c07ae1b037c0b225487c7db855d19d05c4dcbd3537765cba5410df7b2fd08e030a4ac17b207bbd4cb9dfd6baf0e3d31db8918784055bbc3bf077316ea1dbabf74c831e5f7518556550d3e372ac08b28d3082ac26f5b145face5a8253143239e8c214cee939005d1eaf77063aa72b7e1bb6f656eefa5ab54d451f953ed61f788461d8cd50279074d341d1691668feb7ca4858e9d6c907e4e2401c4dc29077416014ac9f1345935b8b59beea17ea7d924471b9efd8c03971ae0d9bd40c663029954e9068f3974208b688caac6b1e90db97312e01d8d8cb97073cce05fcf85ac5fcbe831613a58299f07d9869d42bb3c84f0f448a1d9ea9850a457d590353d8244d423ea95e07fb494530d5ffd538fa03b85484d939dc0d59238cbc4726f7139d3ab699bd6a0aa99ba728aa1226a4235e6c09f038e7d3d3d28ca16ce021a2fbfd85c90e66281bcd2124808e92d5f1f9d46baa3577d93e58ebda38671baa9a212651f8bfc72b61bfc85b80da9f4d6aed850fbe6512c3b1740236bac56463a497df357cace294ae31493ccf5915e9d6313048da035c809e8285a23b711052196a82eca830585de3fc564e83cc0a965dc1b25cc0432e1db47f6d89c0969505151d5bfdfaf4cf02382dec9ea91e409b6411a26b5940a1bafc3da0c14b974a5ae1ba534a5afd108e6341f8f4f86bbabd07300007223fefeef8543fbc207342018e957b88802d6ea17b398ac4cb5a05133f6eab839537f6b0c89133f3a114aecab88bb776e409612419f09dea656c16b5f4125a3cca32a2c0938707e3663d6b8b2c0a982598d9b4c8b30ac2fcf5a71f858c0f121e871a09f8b2b259ddb6219cee464432e93b2143d405bbb5f30dabfb20260b2bad0be4cd45edd8979dc1905b57f50652ccb16ebf50ce68ccacd66f35816138e06406d5a45cfd67d1ff2251c29fb378f11410dcfa5d39886f3d6d2e13838bbbec5936ad912c562945812d4a02926e50fe89ccc29001572fdd5a0bc87c1213e829ac54a8bd5e39294e71e696941c1678698f5c9eb4bbd0c5b3dd834b54bd629eba7d45808c983b1f6556570f3d2ddde0860306105f3ef18b52632fce3f7d8511cd4d129180758890113d29c1f46e3910a1b7fffc8478a7e15a0c8a86510861614af5a86c66240533f6e2f5e79a997bbc05bb70fe5c69bc5c0dbb1242b2018f684a596e6928496b605db8962f91ad2748132247db5855f27989e869b4781c37df347cef335cd61ce99393e9c78dbde19e55099cd48251574931e615d6af68fb1830e742b6141c7347d0fe89f03866d4665cec8e188333df6a8168510f52a929de4180b4dbe97d2302d4dc733d3d057a0d44200eaa8562a464d429005cb804e00d4154df4a7e7572cd8c56c6f35b860416d80f4b164945f3551ee171b0e922ee3dd3746e77432c27457e49c9baf2c463c4eaa5fc1b2e55e84142773acf6cceb70becd25377050d1901fe5dc4f82777651ca2671a0994457372a88617f47e896365fb7d0d2630ef70d250fd8ad269a1d1b8d615d45b0e7947953867fbb8f82093ecc07501ae06f0699fd383e57d23427fda748868d46831683a375a0f2d522b6bb988dd8e5693d693882cad11b7fe74397496c79b7cfacce01232020fa2b13ff0b4017aebb24b37d2cae2ba2c8680ad70684ccc01923369eb6c08b2ccf3050a85baee3b5c7657c13bcbe4df0720766037f093ea6f3a745f7031b3367d24ac6f694e34c533c47838623335f49ad5a5f7b85e1980283841adb9be0a4b930354214391e796c051b3523d11987dbf4ddcc3aab83bc818191c76278c5e9468d01a3e9c3e3d08526d9c53b39e550b80abe04bc2193989f124ca73b3e4d373c7672e2df8d45a78c85608d61c02f81bfde5afab9fb845ddfb173d0e7c7719f65b89e3864524ed14e54887da35e192aa967a49e8712947449ccd465497a1e3f8e5aa4a56122385703260e781dc0e123be0688fcd2b4f655523941ab242aad40c461dc386c160a8ccf39ecc017c52d19ed38d1772b5c70d74e2a53232b020d75c6a0bdcec0d2a7e97114ff66f49aa358b8ab63a4eb451d5230351e20ebcfe4c6470b9825caff1e82b2d7fed3771902c1fbeb6dc2840f31e93b7bb44c18ad3244d152521210510b6e3d507eabfcc8f1169b1c503631c8d29d95fcff95221dc492a06a5d8f55decefef653e219e8d2d9c655507fe0de978befb3b694ecd660db1927c5460fcfea93eab27cbcbee0521ccf339dacac74f9323b149e9e50eba2bbd4e6d6f21c54e2fb489236e3266a0b0db1f27171b70148cf8404324384b17013f43c45f32690e92d3f36d24a32aadf180d936ce5e22a2f52ee46923b44be6ef6fa2e25bc257dc63d0b5ea5d6b7f54ca1e233f12d5a6c3dfac15e865f0aaa33e5d4b3cd44b62b3621c592e312da4c3884468c861b7e9ef5cf32d613a888320b8d506ebd336f404b7532da2bf93ecef13402278d1ab0c3cc38e2faf85a00508c675fc1dda1d407af9c9c9f0cbd45db155167fddc2a4fc702f7dc45e1205759e50691d2bb31c9c6ee900b63ae15f43c32c99c3d7843606f724374933b22405aa81700fabec8d034a6453f305f68eecb3883a579d90febc625194b9a84034fb72a95386aaa662e6b2b1e6353d5e3416ce3ca67e7463845179c43f633199ac4404ccef2ad050cfc2b1c575b368e14d928a0759f34664f3d078e56b17a027f3a62e19cfca5bdc81e5eb0cd7f73aca6a40adc8c45fd6d3ccbdf78362ca2f9e8e2f07e39b571dadfe9b08a959def576d7d7036b8421d45ddf888e6a0cc384bc57126488d1ec6779d9469f4494e60d4467c2e12223a7108ec564e2925b9e8f5c91119c4715a42035b4291474eed855cb294d69f232029db6b03b1a1751d34b038dbf934eb81d46c078fcb474516e9e04e72c4979478a2fb8697715fb18114e0e0320c3bc5e79835ebee5fb3468706b1d93d96fa2a3373ac7e5e28182740483bf5918405222e13641475310e63ac60295206a11fa141d1014101a4fbd0565e8845760fe424294b458a66670dc144aceae853a3009b12770926346606fa01cfd1422cd536bd7a34849d6d331d7370dd0adc365ad720063013bbf393dbc3abab4986280821944fd81837a65850e83eeae9c9e52c5fe403eafebc83775203a14a3d0d351cd0f0b40f6a9bc8340168dec8e6aa8b588885f687a1758a2a894ee7f7e5125d522cbb5f0389764815f3da7467e0e88f3c03ec8d2e15bb01ff53e5fbc22fb4211fc0dbc0472aaa8cee97bafc190b33b3cd1ed1bb281cf6c19c94617c5f496752448656e1abf3e37c3946a25845c2efdc8efe6df563c24334103132586c1aa7627fd95707308f33a693e7849698804bc99b6b0874b5e8ef94414c3320406021455faed68f05376b6290dd288fd50227966070afad681cfbe241ac50155ba00279adfe2a6d842f14c6fe54115efdcd503a264cccfc7dc9095ae909fdbbff6bfb881679b9e3080c4aecffbfcc043d7c2d731a652f60737a209361500c02e0099f2e9befd39d5eabba98626baa854beca1d51cdde819368b67195d12a0c3e7f0bb7df218d6ec85ef0757e764b87d00674148dda870d6e5cbc4d1476b30890f74687b7f64378da481c37183347c4143b0daa1aa4c6cbe35a98d9d61175204c73603e6814dc6cd00953b0f10ce6a5b3cbbba2e703c9fa0879297458793d95ca1575e03ff265f9a9b569383dc5e94df14ba7204f34aa86e51c00df341bc3a8eb57df6b135f16b01bac7da6ca265af0e8ef8e53d2f69dd9cc1b84d0a1337a8cf0901768be4d5a23a82deff1a7f68866d9280d7df16417bfca2f30ce5b117c81c7a2b2cf789ef2372465e31a9524fa965c0b0a2da0c325d0f69720d25bd49038ab1cee418a6e2d4b8a129f9dae8d07e64adaf9cea2485f477f03d72609bb99b0ec600af2a1e0a11946d18910f495872d1fbe0e3090271c03a9e74095da90234ba249616044c3d1038eaa3183a8aa9a8c84d3b673c7d840476830e244d1b19e6fdbd6c05bcf336eafa2128072b3d97dd345061b144c4bbcdafb0b16db59a2bfab1b847ea40d183c1bd7730787bc1f91db15e7b758a966992baa6837608eed4519ba45a1b611564788fce29e0f88d5c11a26746c6ee0a3e3327efd8a31774adafed080d4a49f3b0164903009f97ab9e532770ad0cef7beffcb607216eea8026fb497d6a48e4c0d8706282abeacd1a58bd876d39e5ad8b54e78a5f28881af27653c3539fd06f6d0d66463bd557441440469332b0272793e782eff237c73575fab3a030bfa284526d95d89c1b9c041ff0b68113ba713e191e2a40aaed97bfe44420a1b0954480d8fd5db81aa31e003afc0c292984250ed599b2df105124eb7afef43045f617c544424b07f241d151509992b6f19c09479a5c92185165bc595bc5a2e02b72bdec2eaa386667911b5a54d2c1f9e75886ecda5cc1a5af1ff3203c863f98a20f342da55da62b01ebdc48c93d3cc5b72171df8fcb62b4bcdd533d90d493b07ad2428261984a075387f702adea3ff1446183ca1bd706d870f82d6fdb22c8ef905a7cf0d34ff57570eb262cfa2e6255c331b4028c215f1eef3de545e069eca52d5a3725b57091296aae4b1ffc87987b28b2e591d4717015e6319c4182193e4269ec515993a83aba7c21b935190e56dcbe33aec6d6b501384c590fd7524464c370043c2becd636f574dc18d26ae1c27909b428be71190859d94ba77d6d64151825280178286b0fefe7f6a5cd8af505d0ba29c05bea2556acb0fde62878136fbd99d26e290816ca8afa03477d6be4b0eb4112c5bb7dfdcf962828bee15ebce9ad9bd001771241604289db16074947eaa6201a308eeacb2bbb75360e4ce80c32a2d0de4028c00d3a9ac8af175a42bf564b87579c1cb37f065751b9e53e067a70683c34e02bb34b95736acf6b31605654b86810ee64512a751d395159f52bf1905d156b3ab709f68b94b976cdc737eb6d96d47b816e91230cc51ac78f7de223c70364cc2214a043d4fb9349d26e94309540078b1f1472bbae0e84893f523c31440cda29c352a067983868b1cc4935c0981a7674a89c48fd3daf4524b300b9956e54e9359ca7016a9f6c29b1fedbc99ede995b41617240a0c9ab53a72b7996533f3584d3e2c6a0011346173ec65ec01b23578c0c358c0fc7af9173b081689c5930ee4b510967c602eaa2fbc28f56e66a41854363cad7b2ae0984cfec4fbeb4e96e5d559956ffa7fb02cc8991dc870b08bb8606ad9455950e9406c6b592f9d210950fe58fdb8ac101cff9f60f0b60fdcdebbb473122ef8730552e1479f462dad68a993a7dd1981b21634bbcbe9294d41493c87ffe26c1344572f38ff84900666250402bbe0fc1e86ef30d48521463e973a42085da45dd18ed0c3d59a56446cfe06fb8d63941d79a62f20715800baacf3fb40c769d0a7f0c290dff1764538a7e37f65a996939fba1d841ba9b90abfcc65e757b9488d38f884421da665881021f1c4365f1291e56ef5ec76eeab0d204c54835f82642a13fde36fbb80ebecfe4c8eebb56b13906ed4dd22829b48a0f530b6824a262be77b9171bd08d8c7154f5c0e90fa4691d8fa696a7835020c1841c5d549679d3473b4f07d3774390e8fca31f8c8f30c524a5eff708ec4e0f53b3ac9da378119046f14693918ff3499cbea0e0ffa0d6dcc21857c905c558d5039e1d0505fc174d2faeed90a3d1a24a80c6b50919e7f5af72841c03bd895ea3ec4c84fe85075c307e239f915c38262b98d49612ac3aa4ed0f464480ad3789251b2131012e1bea8efce99b60d8bae984ecd3f6696a61a0541d275d643390998aca62a9789c04de27e5644fe52182ca337614e3961df5ede92c65cbf8a5c6c0cf4045c229310dbfe9ef8a3a42fa34abc7a606e7e69466f8486615ef139d4e257caf13eaf634f6188bd04c037736ea6a7bf783463a23f40802f43bde08c80528aa6422f4b869541100a3ad677c41d28185c86cc4c3b509dc01dd1581a1f09635c547ff7b3bf792cefe3d3104040fea7302438492345dde3ec9cb916a01891323a9c34f5b38ceabe47c1919752dc444711de5670f702142570227e161541c4d1a900e32fc6b09b4e1cf4082e676647ecf0404c70c575bb90384078b340a4f4ae76c5c1ba1088bd518c4fc14d8c32e0cbc65c0a2cee3fc6edd5ccf2228f93a93cd622082c2cd4ee614776d45c67021e28ee6ec77e8c437f85d923ec6375a8d3d1500eaa5d465d08b9eee0ce59b9191dee77e77c37695a57a546d13b9ccf42bc8fc5bbcd2f70952921c20694411858e0225ea94d7d86d48e046833cd401b72ce94a4415233dba5b983e06a662b69f21624abc613d80398ac2177667f56489b48fb45e93c95636dc8c465aab680f040ab0c773e10e05cb129703c1000b5fc0dfc4f32ba4be3d9c738a9d6a4086c0706ffc4d1ec08bb5f9fa7df24a84a3924857410f2cd3e9186b42a0010fc0545b644de56e9ba2f209d9b6f95b8eacddebc8e2dafc10acec466e455d186e27443134b6e27f591f096de8bf365756a7eb12743c33ef03531a8e950b450dbd170524385d6d2ac9469357be0fce6a3401f647c3dbe8e8343f13000f3b624ed16f704855cf53804dc2af0fe7210abd5319f1f6558d5ff40a4323601c95ad2661046d2ec327f660e6b69aedff825b90b6ab01f5354e03d653ebea6270688e20095c6492fb5ebb6268851e95f27e167b77b19797ddaca6518d7283ad3e5eaab8ca880cedc37f667688085d4a29c1d791e002d983b3c31a87ef5229d3083ded95ead402b78f716989e315619b641f11203eb49b1a451b00701383e382e080c84d2c675be7eaf48452879f88d7516ca04baf1d5fa54d610f9798cc7928de56bfaf705389d6ce4438a16b7848ebb9f6ab418369c51f72f5bc16e926628666dc63148df66686deaa9d45aa2f36e54f14538ce105ab35ac04c44567be15387643ff0fdb6cae0ce9c3a85ec15cbe485e0b4e96cb12de946be7319c3527b8cecc58fc1a92e687ca66005e87e91fa5622e978abf8f508316a6ca220445901048167d8e7bedf98347600101d1c120506002028eb1750ab4c2be3473d6e4d0aae7c2cef6f8577a200bb1e6554b26de6e965c2594d76ed3167b341c0c49b48946902a4d808b82e1a36af0594909891cec57a4486581e37c04c89daa2181de7104f1c8308604ddd1e029b747f9b7af0f228b8690cfc0c9b68c5a34fa7c4d078dd5577bdd7bf443d9cca7823fc1635b5c0fae65b621276768c030c35d9d073613591d5a7916ecf5af7cdc0897fb68432d9e93f25ed5318b86a6a0da8e4126a42a011be885f0bf5df2071412bfecef2b8cbdfa90bb7e2ae8702e85bfff1e732bf541dd2cde0f5e39f498b65be3b83933cd624465b722a4e52e3038d4efe310be15d4735ed0570be421de546880fff9d502ea49ddb98dec4eafee9c93fb1bfc77461fdc35fb034092401cf74a125015b6f09e52eed0138c4ecd49ee64380515da49957512dad8ee044eef060b7b1debc48358e596e49f3f82fb38179e4d3fb25cd99af884caab2f6764613ef18030df31784ceab1bb40e9382f4aa696f334f908a370ddc14b70d316e9ae93f82b81b2e66a5742bca027acc94c7f1290aaea9130b9ee841123dcfcf6abbe94fc70621c8af7e09ad8b3cd3258f3621e1622fbd99c661a8431754dd49f7dd1bcc8a40c81628f2c0df6e23fb897841dbc7b82c2ab877739ad4e43f7466a987c20c4a4bf0e286fd1a34c02194c46280ef30eace9cbc62dbb1c21be78599d20979a5d8a4ecb31b5e8125031da50d8b3e645fa062639bd1ba340b2e22fcf06ba4bb2c58424ded7fd7c35068966bc859fe48fbf1c2e770cb89de049fc393e913a487a0c228c6c66f564f60d4c248247a51912a1b023ee9e92d43b3900ace1e1ae30201bdc4f59a8bbe512220a65cab16b6e952e0f98c7750bec33a11b96bb3eab5b03b4e03f338748eb3482e665a6648a6349291d068c522be61da3689a75749551c5c9e82aca10e62b8f05bc3e2882d4a30edb55d8f1b3ffa6f2d10d26f52b9a38e590aaa0115e0992e3406e09270f03a3093b26d5a9feada1e7aabeeccf8fb128be603fd7313c5e9754fd074a6e533d6033b62625ab2cbe7f47642cc719654b3b96f7aef73402b1241399e6f87b63dd6fdad57aa28caa1fc35dc11d0f6abf552a2a6a12b9739d226eb8029cf4a6b57114104a492ad170fa75e807f24c175ecd20a31a3205e51248fbbf7907e1887f013634fc21fb051d6036cf8f6dc220ed39e8d3056beed5f593fa00357b9c17327d14c480683390b57888e0aef319fa56805642d9c58965676340629f1729becedb473059650264b1bf2d6e595e7eda059f595a7edef8792c9e5210ab5322f509ecc5037e759e8242496f119a56d6d7a6b28985d7c10e23dbbbf127227460fc439cd8e9c30874c80c2a1951c1a15259f12644173088300c6b80d8b840cf6bee1f2e0f98414674ee7f323538b29a5f10736aa39743978479579f40c5ef5832fa211e315415578162ec1cfcfc0ae524fbe754e3c15c56adcd7e1d1dcd0cef8b9b7f1278f56762068242138372c5edd1f883f9ec903a12333966a5c71f6293623f7a63648e5e3d7924569765104b58374ad2c2654158b1e94c3ee075fe40b20c52c3da7450fa7a3bbd2830652121f56299af10a5739b87328886e26d810334f30481695bdfdd04650eb668628b8a487a6f045d423a90ad6fbd112059bc812c9791f9c4cdc9d68cbb791b19af03cdb7756b30e9a0d7041b58e091507df2eb039052d947011eab0a72046e9c55bc1a45f708633ce5b5cac508e2018c9a512fd48891f21888094e6ad3ab2bc8cb4ae8c804e0ce9ffcd5004872d3045cce3680e2f676912562a57f6260778ff8100ed544454f6b672e27f5aa0505ba5a648c61c13eb16579c76fa986fd82e6cd63cdfa1606b17d2004f643ae1b0fe0b13f3eac65dfe906376bb02b7e11c17438ca458b01733af6915ee5b8690fcaff7c0af759dd167b599dfc6cf9aabbca6cdcace2a94deebbdfca5c11e917e64cf961cc8c29168c410eca9f3f24fe23cacf51cf6c8317f69c2601b90ca761b92f3e2c063f5df41f9390af587acb014f8a19714c8de40dc0b234c71b2c5a5b8382c0cd9cdd1fc6e1690d48cfbbd17d60e0f325ae3feab4f9b7ac016bad58e96687a308263be84c924c03d663617ef8b2f88ce62381935dbf9a5b275a3ccebc298080083cff471d819e23713fe105c30c203e9ff99ae052fdac47aa6ec924e5a53831a5fdce3251ee85824284e0b1f45676e5cca603a45405eb98fc64f4bf8003d736e869ccd99c66fb4f4b4ef1387e0a145d81eeb6acd91d7ef274b60470a04a32d43fe53f98c2475149ebb0a425a3e87d8a16d664e5073abd6ba43d7748f54757d592b8c4f857b51f9d692a5a1b4620da0d95a48ee1b2626b75cacdb17a34264f91aecdaf6ea5e1a2a381a92af3546c204fac86a40a2f01f606264f4067a902c4261e1af98007c2c46e3148784b0bcd7ec6176197d64b76e104d7236eb577cb88ff4a3f6530b4bd8583a5d100cd02db88c64b6f5a2a456284cd022cfcd2981e8c889eb54464ef1650c1b0ea803974e05d727bc01723ec65d5dbd2c64195bc2f02acfb62165ef97ebcca9295ff6a31bfb96ed8b9c43e70b0e29d3925d43d6d07c1d058398c570da909dd23be25bb2c02c1b9a7f1885a5d0f7bb13317c76a772e698369948411a38a4a8161d0dd8be8eefdb6314702ff4ca8ef02da3500cf1b399dd291d086f008708614a1a5dde41ed364206e13f15fb00e54e5e4fdd7580e5bb09b507c542b09e2f46a82d558e6c640df3164e51d9aac1864cc9b982d6c8057ae2d6fcc31ff22533ccbb95a2df33566e071a306158dc01efd77ca1ff1a7b661af5d15330f2ed55efb73d8008efe6dbea3c03743cf21e8f5a6b0828900370badcd8df6e51610c725378646c5fc300e886b22104343ea1de191b41a05796c850e3b59c32983dd6feabbff874253292c6e464e00442ee1586b0b9452002650b7e147c55ea8aa6ca6129ef227f7b6d7c13442e7b01f48d98c47870328d9f961d9bf49911861d935cb18ce5246b04e0f3058d33e46a9f28d6e156dd62b5c633440c8221d71a5c12bf5b6104acea94e69955e2119ddf65b9fc2ad3d5a0524ec4fd765ec97cb9003135ce31d674d239b0a16a59b8021dd91aa215589618ed0b2e3fe62c57f42d1ed600707b5b764abf009daf8ed9d54d458a4b7d91f51a8961229a4ac01d6cecc9d9c1e0125eec0f2ccd9bc400e3a6640a1683e8596760ae73bcd9469bcef912c2109f0675eca2438a8313e7d4c06e087d1f8a07d8f50fdb963142c75e43d59801921701004f08b3ebee574a90c92066232a037b9fdd097e325f300b6f1d6793961180ca73dc58d9d5654cee84d62027f215736c2462d8e8694ac8039633419c563e7e0eddff8c591c5bec7f5e34d809d010301a55c65272a1d50418c7ed0edd1886e49b62e8e0363bbc8fecbc987fd5bbe31d1805251d88514cdfbf7d1a113504bf64133bbd776d5af1fc3e7785ba58d4334fa0033e20b4ac56974ea8b35bb11b8de12a3f2af067742f0774b2cc99645e6e3ff048887514dae88af3aac78f2257a5c627134c40a14aa10f42c8183d01075a6e60ab94012e58a7164ef0d714ad96b27d0395a7d9df22af6c02461e475fb1c03aca0e515831ce2164b70877ea4f359036289e1c257963aa827eeafd8f1a184f3dbec109f7728a9f03fba01846a9d7121c5bf6957a00a3c95e2e3effc5cc96c214450a383cc00eebde608a37f6e52c53802b6820b1208622bb870204332df8cb425c0230bd3faa6792f514462c0554c96b39301f7bc884c07d0b2b0ded407a6dc6829202e89904385e4a6c2d0bf3550ddf5007e5d4a18311882362ac5792cbf12b0ebc6aa7a358f5b18a72cf9821d7f804ca206bc804a0ae734bc86573c67e1df8b8e5f119274766ce6e6ef295af6706876065bf6b7535256444c58445f70e80901587d9b333a6f9dbf1440173af9e1632565ad9bea741eff3896e64f17d73427b24bcdecc50b80fe31ba345c84fbc7b3d82722a46070faec51879c1e5fd0b854eea3e09e2c0bdb009a06b7d5188c9aa1f904185bb3bf1fe192b7eef5ef7791c7c081d986b0304e3f9d3fd173af2d3bc06736aa03f50ba787f0d497509a0cd7a16629853dc84a8c852cb0c9b1cd72112120e5ac57f339af7448fdfd003dd04ecbefb19fddf55778b869d191581182c84da051f55169196193cd91a6dfa028b2e6411c7309141222c2d61947e5c5230cd69df1391f3312bac67045c1adfa0caa9022f5cab051d5302e36541083110d2abd78e7e1abb6222e1d488dd89a20e75a1112c001c4fc9f8747afa88516463aa48bfbe606a219dc30cef0af2f8d90fbed0344806558cba64997210f779e9e45f2b080c3499416fbb3eac1a978318ed9f4fd34c6fc764a689177105047697acf52d93e8472a3190c6798901b36dadae0864eed48165ce57765d61272031a229d6605be903e38b3ab044730ca7f0bf28c182cb4d892ecfeba20ee72dd13d7bcefcf92a053ca91ffaf301a486ad92fb396cbdebfb3920864116a41a1d9dd27f5ebb53e4dc6a6fb4d656a6f4678b7197132046141040add4b2a135c027da490c22ce2ef5fef5d569ab94275f00b9be8a1055fd0055a19870788f1f37e61e9bc24c8b4f1852ec1a2c3e622a73ac3aa7fe57c0d2ae1bf372047d1dde0b6e46069d71113e34824b9bfd6d7206d706b0df6b389a17441954b45665bad78ec6e24a5d222daa406158ee49db4f3d964f8d7d908a132a805b65b5439f62f2a679c764b7260c2eaecec3a35019a0ad9fa4b35be9dd113fbd9e3858ea8f243c038bf283856a14642f6cba12abd3b9c7df3843e148522d2a5906b93e035ffadf996bdf6c648c45c84aafbada85302d3053b4c9ad653a57490ae578358f6bbea9901146417586e9a11e57cf050a4d574fe8087db4b98035aa6e4c84c416f19832c0737f5578829dfc64c38ce32768adb9c81cc655ac1d12e19d0d696da098f8cc2d10c66a7fb8b7004e71f91376f2f35230988784b8e911eba83ee469078ba79d1cd8097e472ecc0d5f233bb558e6dcb17509010f71c225cab060d5991004bad2014e0ff4eacae4337dc325acb80e63e9c5963b7ad6e52012ef07748b21b7b5be9eb79dc97fd8820c848c8ded194317ba556b9b58e364ef82b6986629f56d4ff65aae25e90e94271f0825996302c5cf2833cb825895c97431e5f417a483b8e7f3a61c3fabec64f8b0688be30a1588d2a337a16eca94e4228e6c5e608586cf6dd13345b408b2650a62073ea6e3038a6e0aece2bf6800513041f4073de9d53888969ae66142d5dfb7e2f0ad8a99607f2deb46db4a1dce01b311608e7562587d1adb9a1e9fcb326025410c629e0aa2148a5863d6a9c76b353c67c43ecb158ff0efaf596570614edba03f98e9f55c31daf8a7f4d8200defa06055e14337a8db6af16049fdd257aba5f76c54556489ad91466f800751c79af635c37aefd3e665d57c830805428f0ecf2fdb2caaaf55e2d8ccb7f96219c2fd5306d931771d8e206a761300e50a937782372767f7edff86c5adea81bb92b8128cd17c23313bca0dc00db8bae083496e215a0de67582f6f20ceb75dac2bb4afd98f2a6cf9dd780eebcd6fcb66203bacfc293944cf93aa72441c23df722f07c07161bebe940464f8a8fbe6f9e3cea2cc834b7c30236d3bd6db17479b14bf63b8442aba68ac1e2573b79536e6155aedfb17b82417273558261b97f9bbaa2a80ea96273f1e8df597d91df824ace3452377c5b96a48bc8b434e838d2d95ec218a35799e81833c067de69ac2836a0f57cb37dc07396ae0bebd0abbb1072bd0fb0a2c30b2d9155b304754b175cd6d94cb4b44758ebaa38dc8aaa5957a9dd012aabf7620fc425d05a30979ef357253b1227b89569b685eac691d45aea740f30701b341b51869572430f1a1c70df06150df65eacc14a1df08d446bd51ba730056b143b086031a500a2a0129211b4f45fc11b311d37b0d9e273fb6b3dbd0f0cea5d13b3a67e5856a51af7835de2fa32904366fad847229dd4af2a72d3ec12cef9e301d8ea34b18c6531867e4127fcacfe419240b427c662b6fbbf0a7b969abd2fe6484fd6da591b8e03cbe21dfae0809d6de5b0418966ea510503b8f3f3599050b6dd7e11924f429dc69b2d1f82d9305a7401e716fbfd53f8d51b0b7fd5f3ec53eb0fe8b76062ed9260a740626f8ccd3cbeb38d646dee589075d039ef7208f83e3373bcec38b05820c05fff0b681ef98a384bfb1b933198f2df981e8445729247cfe186dfc7c756683914e5df31296b162623309c2f86db977e1acb8904f8ef9e3f3665826c1c2ddb5c3411f8424d040afc982042b8845f734d2a7c0bebf0f0da25684e54bcaf4062271a080c3a9b81f601378c9ab74a3a22d8b902256efa8c48c3ad3dfdb295c5fbecf286eab755cb8be7f5205ec61f17bebf8464f817713224da978b44f8dcb9a7cf720626b2a47aa3360b91d712477ed3c61220e6029802a904ef16cba696206f455781a71bb73f0127bbd002670c9ccf1f132ce0a32be28fec58d7418c1f3c5b4b0681c3b73fca26077a88939f4960ee07721f1859e33f9b8b2191022883e724494588741e2fb858c1c823b34c20c2b1b96c609cb396d4ef7891870ac572c5d5c60e60f6709bd613e3809764109305f01adae46105e533602452572f8690f09e8ae449ceaa82386c90d62c22c5236a31aa3589c6ac38f864238ab0bee3a86dfd674db6281557a9e034975d3da35976d6a3a42cd87494821d9dd5ca0cec2753d1a45f6f2dbae438fd077500bd3268389c57793af96d834854b5f02124dca720def1c66d285c63d316ac4bd482bc492147c60720ca0de0c1f5bc4608679e1226a1150a717a45c66468b04714ef17f88ad968169a43c80d6c7cfb8f50dde222eb957e7ddfe5ab517e8d17bc5aa3426097ab67ad2b6cb2d5a56e70494673a465868277ba7d2d3f5654d180fbb6cf727fdadb85260e3383e183ab0db365dd8b661a2189df0a9f93c0fba98640f5699859212933ede180d2056bf8fde789aa1317020d220b5236fb351112c838d25f3bbc054a197e76e4addd2b0edaaa3ba1eae32a39aab52c1a40d04f85e281a69e5f24502deabc3862b004d6ef22c6ef0e97bc0b7d3abccec9f40517bcc06cce9e5e83ca14229120ba21b1e4672f6a60b53543adb6013fd60c89de7bf59622350b5e054260ba5eaf461269d222dbcdbb3b3b1ed46269929396bae63a0ed98db64a35791f5a307c308b242182d1f881335e7470f24145446a9d71065a02759047d439e714783dba45693f8f9de0e490b834bf861c68d9fea284f4cb9c523efe3f9393591e25ec9cfec12f8f37bc8e42dc3664678941ad2768c73927711dea2f42af9256617bd527f339c68c3f95bef715b442c3eef239e07525cc8e68d2590e6d4386b111b42ac60507b9c4048633a390c89ddd42feb4995b8bcc71d27acd4d4452d071d5f1a7327f51b86b8b0a747f1b46faf80789b81f4c323f67256603040095a34d6477f47a17098d4661829be1be804e3d69e95fd3a0a3fdc4457c76de7908b73dfcbd24cb5cf69041be019449a573b5d6a972b1dfaf351e4c20224e70323cde8986d509413cec84b017c7f3de1349a6b78da62b5389060f37d5aa4165bd11c7f6238de1c0c08deb6c760e108897a9e5a53f281e8863d1c4758dc0a15f3e3e927cafb6f5bdcafa920bb86924c119c44e1a482208278e222140bac402068280953cef01e60838b1da8898112ff941a4969c496309ccb300fcdc7ed72457a131f67886587344b15e2c0b8753faf395b432ac1bc82af3a2e0967c250532f0858fb4f6bba4479952b1795a2164a78fa9e3aec4203aa5131281faec613b4489109bfb9d1a6ae516af4f11953d65f3b1bc15f2d66a46ed473caa07e321e697b06483957a0f1aad5dee791b005fc57580cb4b4c0872f597d7d7b5ebe53b9c34983eacd97a18f83cfc5697f8f9d01fc280f2af023efbb1a795135a1fac6d66ae10285bfe7efa17d4911fa650b088dc6e393340ec6cbf9879649c27f59733638e4c1d260fdf808ec9762254e6cb8b66e2694933db23a8052f11526c75083f212ce0b63831f5ffd93acb91f9716d042706c4d6ce06350b72b341cfe54fb30721a97e5a2e203edbeaddc482a634efade976873fe04b93679877fa4e17e1ea812930c4a755365466517cd649e53f9c7dc99406f9687c1620636971419c85cc37539c80823c9c4472e50a7c5e95ec1bd725c02bc768d31154bd70da3b2e7447ae8b438c6387d772904551fb9ec94b8b4ff52c3d9403a6421f946e10197a016be6e401c531497ad9ac3317465d6dc5f676bdecf28a67b05380d5205f05dfcf1ea6bd031f704d3819bb332e8a57cf1d74d66c82470db235f66f96483ee3b6f80b4e6ec1709f26c59aebb28d54b757a3b5e7d4b4ac6f53f596c6d9f0deb1f2ef343311bbeaed4320f22837114d58880d05b250203abbda419731584f001d30e633aa6f8395c04e1597162a0c3ea572e58c5b423eac970b180fa25b770b9380c120bb37e7968ad5da8fb6a58bbe1ec88f368a628a8cee426274f5a20134d933d785697cb3b58859573addaaf3e0adbfe6d182c2aa56d8e52d0270c2b44813560bf3739ee468ebe74e1e8d910885dcc48504a661506fd7744fc0091f8f26d08893dc85b0fc8917e273f129350bc268ac897f1792c2752112c64d3f2c18b4e3fb38782c3354aa0b81689dd087d742895d23c98aa26727e61125f764f4b78d78a6a82c543ed5423d88e395c2ae7e279794d67a6159c0faa5017b0c4bc03d231d53cdfc3abe089dee5c4209fdcb93acd3c802bcda62170d47156adce77cdaec5b6801e07adfe56153b80325d3a37df6fef3bc623423daa63640d08dd15a8b7a2a4f4650951702fcb5307980ef1fa87d53d874931b0d4e7198907b9c0cd1fdf87a79111376184e88554b08143c091235197971cdce96407d2749fc271d286c752fa1981c2871cc049990f6eec5c51a657fcf4ee7862f578562df757d674f80edfa86a826d080405b1ee0246e60e978241a6db45ed6f59a90b82843820294e269e2ea0e6066345992c4a607641c1fb48eabf528b2cd2ed3897fe3be18992734cdf1a1b6a0497443bb6a7688f70e0b3220c5d0c73569e889976c70e0cfdd9111ab1829919d9b8a840e6435dcd7274238cb8689abdba6a0b6abde895655020f6f4d28f7edded0ceedb8d77c64b098026a210ab6a5261ee66b6d73a4369f8001ffcc67b25c6760fbd5a8bb3c4ad8f3f50b00a85ff61a8ec6e96ed2ca5aa537f9b115af98f82f46c15780f44a57409c69884bf8ddc48625c4376874eecaf928426e751d125a4db14af2737e227c98ba713f69909b2b46ff00cdd9c58e647d9b9e1762da520607e8b39baf9b707584ee277c55e51b8ea0e9cb039f2badc3b1acb230d6da22f8525f3054418711c66c69fcae312bc3808f098c17ed489c9861702ecbc2f9b9f4e48bea2914c8c158d1804b30df29af34522307832faf004ec720a0abcb5199a18ecb816e6fc5a14f5090a541d264cf9dfa83c31ead138ca5615b8b72d94b321feedefa56e8c9b4baa64c358a974f9af29134fd304b798243ceb3477b032f2c638f73260c9685623c57ac0b33c450821c78b6be2f9917ef5779b137c3a4e06c28f287d7645cce97c8891825c1d442cfc38af9b9b6e452c90a71f27b55388ff34a4f56b5c5c1cd69fbf19e7947e1aace7b60378e375ee65ddd702f986d9c24c19d1ad712026fe021b3e98b7254c64634364626b2ec625082138c9517441677e0568326eba12b939c04e473641e49c49eb2ecd72bc0cbfbe8aa3a11d91e2c32db1ab2d90dd16b001f8b4d9711d3f684124c6d9bc2a9dd885536613d295d11bc51ac7b2005a4d299a6952a903c06235b2f97dd6c4cb364d41e43bc3b445b4a2afd81b86b222729418a62806ee42af7b877b67f9a1b17ce61f9bc907f2be311b7d74f70f300b4ad76f08480d3b6aca0b48ca68a57a3930f54f438fd046ea82fbda6c6cc76a87ae52a03f006b2b75b0b6f4a832e53d496455c3a42dfa667a8d380a72e8a451812f4964b21b1a67afc228554a8488757a01523d59048a5e4a8b1ee4ef13a45aa4c98dc39ed77642bf094d45c41e83919ef5b28eb06e99e8cf76066173d4b6cf7fe5662d18a8afe4c1c26dea11e79857135d25878abc0a02ec408a62eda2d7a6f861226c9a99b3daaefb4fa3cd07b9199609fb27c0d1af0810bb335c28dda91425542db993dffb7e127725c5002cd793f33197fb82800a076178913b54359a44cf5328e149cde8c1576b83544eaabf341deb45f394da6230e0862a1c7c6c10bdc66358771b5a144dd2db223b595f8cc32bc79225034fc05dbaec1302904c7291091ea791793e7adb41ee4b22564198445745750b274751cfa052e18f550c0b4a60645c6cc6eb748a4836e3e75153787496ff230f6b5fc9abcca6a19ca4288c1185fd1f27988a21f8d1784b0766304d2da89be3781df4b8807fd735387a4299674a0c2e6e005fb953b5ff8b1f6715fe9c71404a246f1cb0f06e96c8897f271a17556c7ba0896568590e2aa41638317bedd969febc50112bfdff8744e43475d6974bdc6118a322f69fdd07493254c9fcb47dc2cf87d89a7cbb65e2d2399dab7667174d7fb21e427b39614e2963155f18b22ebd8ae875c2dcbd8e864b903c13a8d7d9e1fbe7b46df27820759b844fd5bf47cc3a5fe53d3d48a2cb344217916ece7e6c00cb847ed9918a887c5373da3ca224341025ebc9437b903de8bd84d977493f3125774bb0fc9f6fc94e7a4af7397f97d60356babb2939032bef769694d79c291356d658ce5441a67ea118ccafba69a4c8e100d730389aacf46a6a49023bcdd9aef4aecf81a3a0b9144cbe9cf3cedf8d8b4040c3f6dae8e44a38c7acf6e0582b709f90fe38186b3d82ff56b800867b1fc4102bce2ad5ffb856725b82f08a7974f2ac14e52f75422834f1c589c6177166ac552fea5623afd8587d512973a993551454aee3e0ca01b7d9a2ddf44b0a6b563ae335946e1c485a2f149787a4c6e410586f57b1952f53c75be5d87123a0a733d3ae465651a508f172ed5a1768c1cdf1470cd7f1b61fc76c67daff94c3dd33a5de821f08b6d128fc6e72ce24821ea945366e05f0cef469c064ae33bf48912f1b03ff9ea20a5e7d0f2adfdd48d5de69812a8a921ac48f7ff90b832819e2c472c82aa584360633c32185fe92257eed0161b2f8ac8d836c7153f6a51957760b35a1d6d665b58e35a1006dcb37450e0a6bc425ddd4cdb281d5254f991c0a193ae1c1824191b8fc29133cf0f180cc62b2bfee3718f875f909792f7c51c2a228945b5413e15d5515e721576889f942c97657382c29e6bf201584929f4fbd60cb1dedebdc87952e8b516c069a701a15e0bbfed1af9517de5e85ddce3281d07dc20f2a0a65411ca6d54412d5763e3f8443e889a7aa49a831bbfc9c0e84fb1f04feec07c50031c2fe91e2fb02ce28261508e48fcd42027aa03d27da747b3de5f95f95ec1439262726c9fb27a676d70e0ecdb2472570b1e70a40f853569be95cc57bab0d7e57d7a6904c749dd7955cb55d35113aa481be3b98effc3d662f7a595dcbe57cc00cd7abb613a31829756462e2a971b99e8283556ad0469a981590da86f1e14e0c7fef61ea00ddaad8441bcd631e9f68c3ad1e420d59d24d184a3c5e0d1bc3f230a4d24fd4430a2907aebcf28322a0cd9cb368e53a3420f121362dfc4843b190fa026f56f9f006978e6913fe3afa1ee2641ecfd52cb0f1f5f1fa68517d9a4567e0958255c28787f6eb294cc01dde4a82c7c3ddd2934ebde822a2cb0c1d0343a7e4cdbc638a9345a78f9380a96af9ac1131c6d3544e4e734cd33d4e08fe77fbc32638b88b7a61359c79462b131b4a53dc3be738de25aef21dce548b2b462125d6e6e167c409bcfadb1dd3e8fd937017be40b0e2bd58312b13438d9aa92e10148bb1b496aa41d818446d48a688b4fb65947977f6fcb0d6a113a053bbb363a85da5dc6f5f91d54db2892d977f8c2c2e404ed0d403c8d607a6035e915164a5b5ef8bd462a04f24073f528649bab27510f61af5669bd98ac7a15b3d706e751f217e4f4582f9cac9e42a310c3688226076f761f5fbf102f2c9dbe30b573340401dc9edcdd2b3a9459a00021959d441cd1f59b53b2ee7bd62e8382351bbe8b5e3f95e9e282bebe9c48f34a24fe377ca46f559c754e46746ae5a2ec32367eaacf56ea9360b92b31a647542b99da86c3c8a6065c24e5f9d1cac23d41b8810bd2a2ec55f4a52ac3cbe8de1d94916d392497c31c0767828883f938c090f18db508274a4e80d206f0aba97f07f6aaa59b062bfb7c7f5f0f9d381ceb4b9e54103be29ffb4efcd43d6a076e91e7ae3bb79e2b1bc512ae0e54648327e0e3f47d347f5113aec681dfc71800d151773c3aed31c03e45d7f9a7917f144531fb726acec0fa583849fa8d7c36a39a7deb0c8c150884985f402e50b7b384555610150b7afbdda9d5a46c19e6e79ec9e14c835e9685bca997141c73f701cce9d94f4bccd5cfa7e1f25a0411ac7311b08a6e10ab633782e8fc2799debb0454b5adc5dd77ad97aabd3eff8cb373ccf8032ba461549508d74f7062aad45ac7e2012a07bc91ce41a253bfe2463de0e5ea2ef32186121aa3c65fea6c667c55304f776c621c0d99189ab5bd761deaaa021c011a6681084af1e61ffa6420b2a3ba73675505e7428138d47706aaa4b630e1fcde009daea0a84895a12bc1123826f9d69d0f3670778a0320039bc1ba93a43658d41139fd7f296b863e6a6ddf6f5ab75bfe707f1d97eea9fae733268e17e8f703400169994590c6f2da7d55b7a446c4a59e50c62dfac0670f261a87374b894b405cb3623370e0530aaae36982f31f57abf2c2f5de9f0fe8c5d2a226aba51ff0d13108b4f8c9d421ba9030d66aec6d88b0226974e219ccdf3617568483af6fb37485170b6e3977841048aa70bf493fd4a17eab2a20c7d41f4960e91a5af8d09ca7da9a34b62bf95fccb07353915d15cdbb238f53d6dc1b61b6c0ac853ce216c172183d971ebd9e90f19c7ed23efd3e642e95f8488bb55c8d4ec472bff9a8fa8accb8cc5a586990efb70f0b3445113479dd6fd517aa15e36de2204ff7c181ac898bd7129c17017d91cd8f8c57d01cb4ae8995f7043474a9e4fc1062d7f4e8d0d95cadc1a2f4f44999c02d962e8357981ced71fe64968fe72486563b6e1d070aa5f523abca9f9f99ac43fcafaa452c72684492e40ba4e8a2cb3abc21013b23508c34565318e4aada1cf6a645ae0906edf030b6b4936460942c603f5da29ca358473e7d08d0d53c1b5f059559cfae579bc3ada96ccf0fd56cb7a0b921e408d3afdecdd7cf36ea83ab805ede9c4cb1bd30ada118e2828899050829e0fa9afd306d53f0b6b42edb20014e80183d73f9f7d06c69950d436f8cb1be43a12a95a7b1abb94090d790875e92232cb213b516b36f3d9d4a7cbe67d55568fd095c70db0a52b2e0e18dff623b24735d002dad91ad0368e0f6f1b3d4fa3b2384056301a34e6406359f9d408e308c5b70a75db5f395b479ca2b001a2a5104a5f89c922cd4d4482539ddf20d589023bb0f07c70bd3405521b4a86b12036283a768defc7ec32b08a7e63c09cf4316cb6c9e242c56a207c422f4c97cbe0edbc2d3be4c7d3951567afad7bce6a7b8b1f85468f97d63e6fa833944dd5d8d31fd0c5ffe6af822ac9245432b75deeb6296058345f3a8ee36fe42aaef01edd48c541099e3182e76d1f19df1003f14eb997708dc25a42f46fa300ae10edbf73b45f748f002400f93de9fef9fe92c03b48e9e8bb41e92c868328c78eeb53e92908dd85596f04da1383a7b3e6194763d55187e3220780a7a20fd501b31e5cd32052925640135cde8219955aff51ea08c5973acba08816c681569be9120c785e0ed54b9659a071a036ea6b21ef874512193d0141863673402613c2b0ea3c6161312f0007c08161607bd97bc404ddc522b42abdc1a8af10abce2eaa93465d1ca1adaeaeb3535e269beec6977ae59c76d0dd5d5668c92444c02b1f48dc356ae6ac0c9891f597710e8940217ddda28f1d8c815af094c48194d96dec3f397a460e1358919defcbe6b6011f7e04670e96e5928a9e08d2aa04a8852c9ee8faf5dfa748b9b063d7013ed7c8c911b3d8f8da1f3a62a87b4dcb4da545ef17931d8beea0bfc86dcc99160aa56ecd047b54248b2f00ad85d38d7f693afee7fbfa2128d018f99937dce7094db93f6bf0eaafb4d736bb8cbf5b7fb84efb93150127f16a30b353a6a9c3d9273ed790d90ca5cbbea36d80555c025cfa488e25eba5ae7d5c566d8540dcad41c80be280a3def80daccb8be3958f4e144d8a52290017864e4d2fbbeadb30200f88fda044e0c7438d8fde36cc56dab54264ce91140c7cb7ca2835e28748aebdabff0f0c4ff606b69fec88aa00b5dfdc10be5c37db87e3ffe8c858207429d37d1650772c9e7acb15bdc46af3353219b0919c792334a74a3ad9fbfa646340a8961037ab4f5ad1879aedf735e6d092c6b778021529a342acf125a44047eb094a8066fc2803844994b947123c1d4548a7804a9a642a1b99636574b67531884f4068d3f3e84d795368fb21399462c01f79ed4c89f24c58755c8423fd8b821a96257874ce559d8bf92e8f00309587029872e9a0ab108f846a386c245dcd1892b5c3dfd9a86ff085478f350d12b4f9f11b0bcca23d26af3c53ffafc6120f653cc441d675ca61e6384a09a7b75b347b24409633e3bce09011330ca3e3e0a04728a926910a36e0ea91c9b7340c50e2347270ddc2a8da6b816507f0657d532678004a8fc4b13abb08d28a85596f293346d9d8a42a5a70f662fc9cb07a6703bdb242fa139c2d861a9575a8f6207a10f8eee1d6a0d832fcb39f65461e7e734be83f1795bad95d21646a99a9fc5f0bb32d672780c27fdd0bcdd9404d343a7526291ce4238dd835790efd1429da4bf874964572044e403430fa9161dba21f70f703e2f635fe0c77c846b517fd2203039cdcbbf8ad13c6f3af43af3a8c92fc144ad1b4f8bf6a71c1ce9a238d256277e93aac5a4c0521a150bbb01a873c8907479f28d820a51639650d9c6cec2d0885d7724955b670a346725a04e2fb3b33082e55c3191ed6f823abfe671ee28921d919c6a1eb5b11aa0ce8346d739ff81fdf452ae2d74dedecc6bd93c5d0b45f94d5cd1eb40b15ea3a2524197f3ebd198520ab466c97b0231fe8629a8c273399e17e7a23a7bf9b76c281555653a07f986c72bfde7636d4616592a953a79afd01f01286a30f83049e489b395903b0fec128f9f3a074d986f9a4764350f0bcd0e54c6a3cf8b29778b775fa95b50f0af0034e1ca4ed93c4d3206e7541a01c134c62291125625a940b12a8e308e2fab3962764a80735cfc823d0d08aab4728565565d1fc6a205fcdd42228492f1c3a6f17cb0f6b5f9ca0674a8c592f11fc6a98a249a52c1e8f548cc28fbb73bb6324c895f862b2565a60b31ca61cd461f52b523193e27c598477e46d043e87012314e7234649958b8d3ea9fae87ec089af840f1304e061b65736c7d8809075585dad0d3699a7904aa1a3a8cc510ef7d5331ab1aba16bbd2d94d080887f8ce7d5d42f4816d70ed637486f5d00e654ffe14d52c3081021a8cfedc416ba75d7d6b2bc7111729e2409f4a03602f510ad65b33ebea2e98a54988e063a851ae3f3c0ff43b8136206132793b4a3ae32ad5b60358ea4fd3fe03e4746a945183085c6127e3c0b7ae06fc6c0b4bda553f12c9538a0f9f0fb86925a00000393c60b2c3b410cd4532ec09b069c89a819f95ae60af26ec66cb619c502b69188ff2c7ac883445008b909a77b81f97c20e5027487020e1100223817a1ffc7ce380412c0151956c1884e72bf1dba0202a6b25e2f2ee1c876f2f0296bf6a8f790fda128c9ce581e5401caecb39b0605606d6680cb2c05b33e8069569f9150d0ec2f9defae1fd30af0dd01e4ca75f829c81d6a29080cc9667985b76bfbc799a2fb65ce1a43382a9a512bc0c4185d562977ded362468a8be1e9a3fcefaa1f6f33374fecf3ea3e4150b0dc108e5335ad6e317f426e4055dee4b0414ba9e6df0f73f270e3701fbe2031cdf2927723840115c8a08aa8f7072f303bd4bc6554edbb2cedbc12036ce2866200434117ae83a45e59526342d1ead13981147d1b91dea622ef50c8d1f378b005fc06d5ee210b61f92c6267e9cf46efc41b55d37b800f2a619c2c2bc255052399650e2a5a8b1f2818fef77d1ade04b052d45295a6b561d92455efc1981e3260cc568f25a496a164013bf83ab9ba1b17df021668be876f9b61a194cdb92a731269fff309ee834e8563d5ebcde36b8f801bc300c2c1140cbdc12fc28e978889f12dc25299acbd37b8fca11effa843e0b72cf0b0cd474e678844ac4a41aa39b4d6a0d4f43a0bc05c4034134e14ac7444bbd301677a2b10837d4e24cd467058d47aa21cfec762bdb72794a0a2bc0143b90596fb4a0ef9e67afccb35bec6c54f25aaf8d4edf6d2d1e73f3cc18eddc61cfc89ba29bcd1f5aaaaae0952e79f042a58ad68628d515d2a6ed0e9b10e5a42736b3172dad43e4659fcdffe275750c9be96506a066e6d1d839c255446991457248e3597dda864b4f00176959fc0f3a7c6f1bcc672f2b0ccd4a5ccd46ecdb3b9a63a05a85beff49e8f88e98d3890349f92207d338e1337b34472e09a0b098cb43c9c9a034882dd34d893a364f65131a07a2f5da74f592834db75a9b3066030ec30f5f28ce8bf08af571ff4c47e8af12c6196c85b0975f88a3f089143e835b7a1879ec3443460d62b2b60915b88ba0791c6475388f47c398d026d3ab1ee8dce226203e9fb13820fec0fcb9b34f82e16e60a8efc192e0c381a1417306bce978eb8d05e958931c9aa533e8d0116f32e0cbe982b0e6fff77e19ed904fd2ecede624924e8bff0545de88763d4decadb6b07dd6115e9d402b999da3ce19a66317c02215914d15bd016d5cc13e674c27a7d0e2221cbbbccb0092031b42d52749212c297ecf6c4ee983f48ec0756a318aa168b934838d3c3324a0d2498ba3206a7cf8fe2dd13a9a5c29cff5c905caa9469760c80739a75f67f28cd02299e57f46b8623484cb00cbf6859a4298e5117f1cc41a61d1943199dbdeeeef45040870d5d3aa48763433f2370b0cd2a52c4bcf9440c55f3492e2e65f3914b0b380a655cdcd48d49608c64d981a47de3933385535bff5952d9376b4287d3ead35b307663c79d71bb4c007e68fa298c080ae047064b2f636c189278a4bb97784de29675ad1d8042baa85a23f810b601dab69161cf493108bcf7f6a1f67c397db571cdd169caec777dcdda34abfabf6ea70beacb82593c7f680a58f8a09e988f845616d2a7ea2a15bb166d04c1d32ded1ddc3be2562f6c252a7e49de157a2482a4ae92715ea1461a3a0159a990fcb344c37ff81bb0998100252ed801d8b98b067d53d11801145e7a6300e104ebe01405445f37ffe7856b66ce4ca3ef3838e0d6439df1a4e86fe767fd16fdceaa0d20c74fe91f3bcbfe7078fb3cbc7a67e61b6ebc8044cbe1a72fe2f7015aa71bc464cc078b630038a555d985dd73c2ad80e4ca6bd372829564690c857f14f3671d7f4737d7ff5367ec68198a1569f563859f5c60b7443980cc1eca7e4a0419a6ac875bfc598fb890cedf31f430ceb416d344b7eb007ce3eb9c8483faa824c7313b84269850ce88471b05838889064ca57c31ac480678f38f3d94c14718995aed836bbb72986dad40dfe4a65a24f2da03fc25042a6157a95a30d815271df1a9248c74b1cddde5f6e2a8e6ba65edffe82edb4892c5790a2d1d4de854167a7c90fa12714c2f6e57e904a07904ec884081213bc56c66429fcb8f0660d4baf7a451d822a6ced5476bdbdbb61142c82664ef2df70e5c0acb0a680aa44ffa19defe76eeb3fb7cd3cc96d0e74806897e589f475bd55a0e5b49cdfda846f490ca54327d6693e964f2ccd8fc99407307fcdc79d6a51e587b2f0af583e092252c968c138aa9a43de5db8ef2713f7db7441a556b62cd1699b3c3c3e289f9c9c0cf8f063210f343b2dcb5d7dc6f246b4ddb4d1876bf1dc65dfb4c4bebbad6b49efd997ebfd2533ab47387d96e4f43ae3b636283b4414afaf6c990be5dfbec35d35fdc4d18a6530fb3e75ef1ebbe8461a4dfbfb4f697d52c69c3dcc8eb42a6c8403991647cf6299ff6d3c71de5db6efabc97beeea34f74d2177a0cc5560aa9a5b4565219625c99819f1f0dc4fc34bd01249555faa6efe354865da67b718a9e647fffb2bfa7bfda4918b65d3b8cbbb5daed8772ed3bddf4dd8f3e12d54894462f5c6bb3c3eeed33fcd27ec23052ab6297e95dabb3528652ae7b9209d7d488ec4b58dbba90e53c91e88b2dda212d405ba347b38436cbd60c3404a1826290cc913b4a3eee337cde515fe8299fe9324a285f8a9e4895862fb65264a0f0f8087991a0eb22080a1a81b5f7a2503f088a404890b5f7a2503f08364a173af71a138a0a78b225148e3bd1c87daca9924028b3e2ca7cc7a23964b1240e6d3343160d59b8b39ee4b8d039cf74ef332d1b02b283cc7d1b727517322513e9d3208c865d349443287353dcce618ca57470efbec3fbe931e442691f289b18f787931a55ca956a94777fa1bc8b13e58d5f29efde694ff94ccbce16946b32a77be7beee9c0e1947f94bc651304cc71ed65d068ed3e217ca5330eca461da9dfbabe3ba9317fa4ab8a686f49ad125cd0dce4e689433495f6c09098a693d1283a7eba2eb64b4b16449cc85424ae1b6a3bed3657cde51bed2492926ce76295f6c914cae26ae26381abfcc0cfcfc6820b6b5f7a2502c6bea80f47bbbf79a93c52e53d7e1549f8ba6eddd5fdbbbf73b0cd3e14aef7e3acc2bbdf3be9d708aceacab190f8943dba474a4fcf457ca4f18a6d387999ef23a1d05c34a3fe118327dde4b5fca9aeea55ea5772fa5ec7778377d47f7d2a7c384e3f44a385aef761f8de109f974fd1095b1d8be96acb985bc6ca150e157dc0cdfe9a8cfbb8c0fe5a42fe5a510377a7767f8620bf5c5968c2fb648251cb345e6ecc470cc98a99a583af9f991959be2ce4cc167e0e74703ac19ca40cccf9c2d76ce392de5b6a3fcf49a947bdc86537d199f69e9b8489f993bbdfbebd47da615aeae1637660ec498e832f37c8fe862f366a79d38b4f1a7a35e8ee8d29fa7400c65202dba0615dcd3bd7337dd3ee52a1db61da58461a5a7749d0cda60eb64f8e55d0686a5600fc350f009cbe86497b1291ddcb7bf381c67865fa6973009d7d4dcd7885ee39d7bcde8db4bd36efbbad0c813dd98fb0394f568c86b8484db970fa515794e94cf3fe5d38ef2653f7ddb475fe9a42f65ea2ef26228555b285f6c9dbed81a7db165127db1d5c5cc16a984c7496318895df49d8c19dcf0ae061257240e3102c547d9f203b78a2bb3f10f6cc59597178016ae7c877e48d5f3105ba45b207ddaa7407888ad9a1aeddb6bb297de3855b18bf498ede767ccb46444bf9f8f3ab4dbbf2c86e9347e6537615809cb88601b96d191317dbbf6995608e6a923633ae9abd922c2291da56f4ff545b7f6a2dbcf6f2f7dbbd9e7bbd297ea974ebae84be9289df454e35709c716e9a2df7b114e354eed4891020f4ee06447e9262ca3f312bd84613a2f7bd161dab7ef109d034a20e1890b68685a220ccb6e2c7d243cfa745ca2675f4ad3b62f25c23535a1d774afd9aebdc67b0df71afbecb1b5512153356f3fee9345640ecd4d6cd9d8ca7e4cd5fca4204933b33922ba947cb8a23126ad953597559248b0649784e879a29914095aafb821cbdec8228d43959c9a9996906575958078c3ccf017a03daedce0862c1edd129b7559837af2451799c562b1589946a94a5c913ff97451e5345d4e1f0e47e8014fab93dee9935538a20a39ad6e7a13264f70c3049ed6e91fac04318b214a5a76dac027a8a5332f714a0753e9a6cf64d267f2fd4c1e7d268b3e93439fc9de6772f799cc7d266f9fc9f63359fb4cce3e93eb676025c88410d43abd0434c2a0a48582f279b1c13b1285bc8edbec923caf61f19dbea6b1a2a06058095a51a54b5d81e8ec0c51d5f79ec3b3c4a851176eec398305573ec7c7160c4fcc22c9933c9fa387667122cf4320bac4aca5663847573c4fb36f2f8e9117aeec9f79d3459530fee40cdbd0d4e8a4224a33d1a45f77c9bd7bd7494f7bd7c54945b4a3d402cf3ad194807dbdf172afe86691cda2edf6bbdb760f257a2612fde2f08a3ec2212a775ff82cbf98c3d71cb877b7a11ba125fab65f9fbd5a955dc3ae2de85a8c03bf3498612737ac91b3d7a89fcc3294837db5e1fd667827d477950f9c1bee8752da2c6fb3c84c4a8baff59e33cfc69ca2077308e62035d71f2f097349ccfdb03f06c495c9720016b0c1d9ad7883767b613030080c0b813568823c4f6337bc12e8e6d44bbbbdc5b058869686532e6d66a69544fbe254e510cab2932909d3030d59b68087c7933c234b18b178354b2a521b53ce796f63db51074dc3b012b4aad0c5709e74b13b635629cd689665d9a946c4cd88c0a17999aa3ee5d9999167a785fc08d260cd3020ec3c519329f6a6c5d120fd15375ce5db60f78c46c5aee675b16b25907a5601c306701557faddb11a675927c4aca43102945393b29364c9901b213e3f82c0accacab31764d5b07142d59a932787489aec5dcfc62c91d56ca12d1db4b59a2a6a69dcd16c6896987b88c151e4c5460e14ea07c1d5aa8bce15f3c1751dd73dc41479b19143ce6957d765a74a4619623e9a25b4425e2e986649913435437a88e9e1e57ac1bc62ae18ce1671b55a1d86c68ae58ae1e1a3c60b1874151b66a3507395a3596068ac58ae181e3e6abc445a83c62a7a34583456ab154d0b2abda71936d21ce3ca6b344b7871a81c7c71bd7ab061754f36a8fcde19570554a974028971a3125dfaa5db565cd7719dc3c5b259d1a811dba28cb83d344b687bd878c111f323082c87cb7a396c34d8e2613b4fd6060e1b2f3796090f2bba34901cf25324c80f2137384790c07a2889e1e1831557807e04f9010403e2e142175552dd16635d276d78c4f49034f2864804830fa0dcffd16d9ccc71bd9aa55fc0d249906149eece85b4d08dfad260a31a943e70ef1e72f7ded13a49f5fdd436a359c2f853eb411aed57346a744bf40250184836aec6f0a033e28a6cd06bf0d9edd7e125bc73ef66e806d71cf57c34d87fbd77ed5ea4d5d60d5f1be98ec6c566511207896788376cd886384f6933959ecbf203bd3c4d9d81d7f2b8c71c230bf7d07eebece779d693527ad6fbe6799e77ee0b676c0dddf02c8b7cb8bdf33e9abbedd3b66bdb69839a77db2cdbfb1e0e376c716c508b59d34e3ba44376972be64d8cdb03c73498fd93c2ed87f127025523ba1fe2533bc46bef3c731ce5c4167ba459ea61eaadbd17853aaa491399a4c12a711aacb7b3451e69547d9548ecb5a87b14f81588caac1ca27245e57aed8b1905ae583c3eb9c678882d98a9aa625ca9a4c1fa0ccb9d06eb27852ba7ac4d38ddc88356e220f105e20df3d3621c110a793ee4f2f4a1691c71a50c37bcf9d6e89621ded2eebdf7765d4a323379a770c3e8130407bda49138b4bc41d2c8badddd6dfae28692c607874facf206f99b53cd4906f2071d0653b001142d450a72761a5996652846e415dc20d3537a148b434343938216b5414f2fcd810e4c34410c5d48c10bc400a345a3c8947699524a293d856e509397b36cc615395469400e6da091c525648f61b42267437c51051429a8428f0a8030040539fb8a358427677f3122673d446021672f856e64b11e1c81232160082264fa1bba416f2290f4a8c8e189460e67e44c256736e42c873472868224c09cdda2a0276747d9e4ec6f96cca6670814e4ec2356a6032a9a082253c0135114ba9145a146a60f152144cf8c9cdd339249968e4e11c418999e531142103b310959258a205240809cdd460186abc5584d105a90f5d414c139ab91b32ccbb2b39aa53ebac45c4f2917997edacc467616bdb3e50718b2e8c1177e983831452b83226755d080660753f8020c9d24b4b22539ab020956e4ec349a45cb599665d96807379663de71b133bc78b4f79e5033ac56356dc990aa0448f361013da19e68925920b23062064dd505828b18f445391114041a9bae77b64414bc40664dd3b4d3207c11842733d35059eb398112596341ab5efba34bcc5a56c49156c53c8125485a1573682f0a8922f754837278df1141bd44aa3dab59560f20834bca91878b2105f0220603d0c2f572e4e12227c9ed72e4e12249017eae8c1c79b8904245836b73e4e1a209aa2cae28471e2ea4001ee16639f270d185ca095c7ca1251f01345531d397f6420a99e6d873040ec5e962086eec0102498e3d401891b721c71e1300753c59a2c0f86329cd33356dfd992ad9627dfac90a77d0a499a40b00f1939f34283f693a30834c1f76900ea52a903a48faf8045dc004364b08a50f95ed65593f6995fcfc3a2805d75adad59fd962ab882af92b9c3801284b2a04fdcc96d8720214647de28a9ca1226d00c1556356835658a10609e5809a856679ee8ba0661965f90ea743d22df2c80f67827086d70a2f69b4a60f92d84e93243f4e0421cb276450b384bd24cb870d463d92e5659d42969776489697960a4b45db6021b5f0e9427ee15333443eb1ca611fe138eb0364e346e39721cd626f6c911b7b636fec8dbdb145ec8dcdb13b56490ee993e5b9d08dc638ea14341acda6fefc6ca11b5d8f344bcda93b35a7e6d49c9cd9125b3b4a787c6aac8769433726860123ba00e0e7a626cf7469b2836c6caa1a871588c70d9ed9626353257fdb07757d5041d9e736e1b13e1ccd7dae3f3eb21e4e9ef8110426a483105d3c10ad903d309338c2c429ba45a6817e92a6c11a9a2136f2ed4474f1403d9039c693ec236e91a9cf159ee4e863852d72687d7cb6f8c9d6a759a2cf15ac681612901b7d8c009467c8910a3168b20c51a04f9329f6000dede08189533be489c31c62f6c147dc22630fc42d1a89db3c3434b9af10db014dde22d74f1291370dde1829522409ce9128c0901b89f5c9c9d9d949d248942cf16162254f83f230e878bea027490c1189e14e2a633c2af5f80331e4e9cdeed460ac8127db49170d368bc4a913d56205c6624e6c036e0f4d1557b493a8ea8709c832f718bd85c9823c51d060c8bd0a23f7971869b01f672a82368c1134ca7d181905d0184e40d9438382ecc51772a53396819a259c4672374b7711841c71c88a0fc137d8a780912e3418b97b8cdcd12587987f40c5157a525c918fb345c3332da94d19144a1c197b8a6e1cc24dbdb6db6f384efba55ef6da2dd6709099e7e35313ebe85686230eedad8844c99e55d65a7b061157625a637615bb553a5bc64e947a79adec342f917934935996c52aaa2a204a7e5bc7d1d99ce4a2260a81c82f9c91e3b3500e5e68c38d2cdbb8f285b44a9c5c6bf5116333c680c46b73688e0be53033e56e846883f15d77e3dab822bfd0bb9103cd368a3a11271289449b4824125991481365a24a4553d42229baa1b6654207e4071d6e9e77d103c921e6781e628b5ef2f85e1f8c18b7c7c3f80324440b1720413daae2251039595681849decb595fdf0ad842c71b89a715483a89924c6a03b73fc0cdda89a0dba33b4430fc9d95f6237118c394630d59d7b97b7cf005914c5851bc626303dccb709721afc4f13458a18094216326d5ae9875570c3991cbb71634ce9aa473a6822884c7118794ec793a2c65092572231dce77005529ce77972b4aa51524ca49208273b59d2c7fd7a5493dddaa4cf5e633f7ae314c52e92f6793fd39ad9b5bf320dc3742e86d98f0edb9e5d510e8943db68cfaea3bde257f6118689bceedc76ab3d7b66394fdbba5006d469419f3c49e10923908c138acd913ba3fbd347bafd52dda62fd5da97ea6c33b99ab89ac8202020a01e69f1ea2d5a8cdb0f59150810d00fb3abcdbad61ad2b9571d2ac35dfb0764ee7b9cd231bafd0eeeda65745edab9cbd88f2ea3f3e2aee154cb8c6e71aa239008e20f21d16566ae08398434811c01a55ca6d2f6d7e8dbeb372ed53cc4d6a8d5de0f201a6e0cd6de8b42fd2048234392439b5d9f382f456688916232625e4cdadc948e3a9df4a13ce513ddfb42efbe7bee1b75cacb6c9145705c1966ad563a622cc678d0e18a2b73ce3963766b310f40329c6aaef4d36b4c4739c5a9c62e6edbba14ae54ea300ce5a6c34e2f95bafba5fa5d37a58d597d712a7b37c2a90c7bdfb68bbe947d77d1435f4a4777d15316efe82e4aedf01ebaf7d04cce707fb40c10a098a67c3af528dd63cb7b6c99be542d7da98a5d35f735a353981e427e4423576c47644457935db6b86e12ebdee91bef26f49d6cdf7491c6e99c50ece726d6a46f6c6868fa2674d15fba6f668ba88814e130fe28d13e9acf4b17d1377dd32de822fa265e30ff00a4def8a1e254db7881f911a45b9a8633591c25695347afe17eba992dd2364e1ee10d631a2c5bd97723d8ef5354f58c1b0dbbb6bee924ba88d73c681cb412d987fb4b7bf387c3b099968c4d9574d2431a049aaf466822cd32b3226da459ea90fbc596bc29822377244f8e92fbc956119c9c1d253cd927fbaa0d5da4735a49fb344eef74ac6fa66aaebc99ac4b4328994699151de5d34a1fe9a65166b78cc7498c931f4052020102fac1b4b5e8f735da4975db4aa34fd1b7bf441bd660573412e1687d709234362938397207f5d99f3eed29a3934a753b95eeddecb9d7681fdd6ddb4efabc3d75ff6a3761d8e81a9631c9a4bc64b5cba0dc3ebc2b199d17eaf73232ce7df4c9a07e9ff2c1745e28144ee9189d7baabffdde6ff81e86721998fb08cbe8bc4cdf2e83f2d139946f9fe9a3efd6c3d88f0ea37dbbbd8ed1ada69d3e9dfa6ef4d2b7d2a8f4eda4a75edb49df768c4678c7f6d14730edf7b1451a7da5ed2361ea1a5121d2a6484e698676e7822eaa507ab984d2f7982d324e23bd07572cd4c75dc6d73de543f9fd4cb75faa6ea72f551f4a652f7d4b94528a83524a51534529a594d24829a5946625d26b45a174ef122551fa55a9547abfd47587a19ca3b4eb3eee4b357e7ffb4c4bd3629a45667a1e37d248b7a4f8a08f4fa296b58bde94cefd553a87613a5dc2a97afcb2ef308c746b755cf7289f8c8ecc09a7babbe99321754f55d2b9a75ea47327e1780ea72ae621b62eaee941efd18be8b72e34a254f260a193fd05bd7c119a3c67509e4ea69db13c451867f4893e7724d752c293a74f9e9e2c325b66d054cdd9c4499e4524cedca1b8831a6806379c3bdbbc22a88a08d4134e27363c441e160fe6cedc993d789e3ca078eecc9daec1bd84b7c84ee835dc453b9ab5386c9a5463978753edfdb5ddc31b27c21268aa66ccf327679b2d5e1325b365eecc6edaed93adb933adbd77a7795a49fb28992df701dbc3f6699eed8bad0edd706defe6ce74622a993ff38a3c67153dd349c7162d21ea20a01db225c2b09956037dd3e666e27411ee9c4c9a25ec2e0228dfe91b9df4d7e824f0de843f55364e95bc992d323af332a48f2e53fabd7d0da574903efa8bf4d1c4afd22fbe53652f7bfec754d1897f78915e3a0fb1d50316838148b8f4b9a68a9ef48d7e1fc6d8c453c695b8321fa76af4f5177a5c0e1b4702149863e4d0b756a29d3ec66791a9768d6aef6f9f0f2dea6f13e1d40ea2cbe720c2b1c14dbe747fa16f5fe8338481b293af8b741291652ad14a9c14913c3eb14ce9c4892bf41d96390dd27358e2b492cdbbb36b1a4c20bbbbbbbbbbbbbbbbbb5b7677db8bea6f49109bab17128936d971b6a6e6f6a5e67cd7ddc3b0d0bbc33ce9356d7920b0d57bf7852ee2ecf62d13e14f76dc66cb4099306bad5ad4773f0d6e91ab3671e85e8e07d4a9cce0662fd246fa488ce6c5f5b1cae10766c255eede5de6ccda7de2d05ed4bb87e04d7409e52ac5edbcc3f4a13bfbf61cb2778dc3794e6a9f30dfbcac974344962e2282f5b58b7c98d9625bf5ffc47d1357ea43f76e441209e170fbdcae3d9cf7f09d1f6afbdceec3fca9c13aab7d2825f76aad943951552f71c87da806ebbb5a67d7dd13964570779106ebb57ac366a23613b9be999038f4ebb970c32ec2a2d22c757ece39a7176b6863c53ef44e280a1ac11f4184fc90427e48ef2fed7a892ef337c796ee947b188f9029f707704b8cce7d36b6157af7d508c16a856e635ba38732485ba0f7d116987d555f57347c61dc9adac18633d9e2983f32dcdac9195b52d1129ee8a1bf44384e5508a7ec438fadd7a74afb08a76ce83b460f61191d9911aeb161b5ba0b615bdc778444b8665e08dbea1e5b1e76bd7c39aae85b3542dc568c090863121610862203c25845a6bfb305e647e2e6a1b75fa4d69334190aee0bee15fd35bae8f222d14f18069b91f2a5d24f1806bb33a4df4b895ff7270c83cdd819aead10dd4dd3f8c1c34402cd1a1bdc340dd2110eae8ca3340b77da9d5e2a992d293ccdc2a45942a740d1a5e485c8f2d2a01514658a61d1448b08441095f2c596e94727c5ad398c403274e3be93e2663f82d450c4ed3cc369787dd83499c62f9a88c211b01006115f006ad1d3f0499b4f0060dc27cd92d96878862f5632dc50dad8d4e98cd3194e25ce8c2fa5fda92fa5e11f5ea9cf380fb13503c32e4e3db66678f8149ef1451a3ec3e50c34c8af741a3e199fe1433d86487fe9b8a21df5c9f8a44dabe869fb1c9146a8cffd2493d0279334d8207d0acae8933c9fb459c2f45367c212d0b27c04a81722486f634ec99ccc268925d08659ab06c15879f566d4dd4d273fa7cc892bf311cb9d06230f8e1fd927cbf679992371f0e28a892d8748225922f9b6772a49dcbef797e80244e6eef3f9cbd9261a8d46229168f4eddaa7e592c9f4fb1da5d26fe932285f4afef4d8227d2989533a4847f98efb139639fd1e05cbe8bc4ef85e86f4d8eab24fcbda2512890359d3aaf921448ca08efcf8ec072b7d2fae982fb644210dcb1c9b371ddc0a06d7b605940830b8f9b1a146534a299594de076aada596524a297e5981ac06a72b8c44c861244296407f3ed62c210ba6074ae99663cf12262cb511e7ce6740e858a64db46850cb6d93a495f4cee5473b3f3932269bcc16dba26f9be8a265fa1e22b2c03448b7e89698a9a27247f2cc16ee07faa2696a32c5400fc9b49443885e8a6bf397d2d1ede84ec23542bca5935d473bf7fbd40eeef7dc2fae21fdbef4e9a476e8db374ee9307bc2589283344e499be812caf47208a594def9d5a01bf64e2b992da553d3e99ba777327d67fa52db675aa52fb5e11f5ea59bce436c99300cc433ad9055c23a1d82a6aff4d100c38d2daed59deb1b7a243649a64b32bdc1992dd6de4b7f3fd27782d11830cdd243440cf490fb23b5904d1aa4177d92678786505bb48a9e760dcd939f2f32a5b28b4cfbd3b2b599f4c556e672b0b971dbc4151ddc8a5d158cdb8f65ddc5baa8427ff821c8cdf24066196872109bb3937e1fca941ea477c1348823bad8d3eb105950f4acc8928a200522c7a0adfb81c082e0e689551ae4bed1bd4f14fa40d64f4d5cee5c7d78999abfd3fb8bbbf7790fc374267e79bf1816a7273bcf4fcb327423a523f4d15f211ca76a845f9faaec33845753458f23aed07bdfa78abefb5834a54304b79eabe1a6f1a776bdc110c4f0931590e310ba004556a7bc46e965f6507ec391f6a53c8739eece7338dc3ecbc50064798e468a71acb8707978f4f07129b970c3f81379785cba1379e4b51c7ba0c8228777053702653f6861cd29d2826cf1405bf52f1d2324bac8cf8b20f6704d5304371408892b93155d4279561c2382084ed7d67329ae5fccb985161db1d08105686a9db3d67a591bcc76b3a19da15b35cb74e9af441c62bc2114b78883a4efc6e100b27c78036a0000c061cdf2214aeeb8d26f2d6e189b8820e24043ded0ef5f20b6b060a20b6509c30b510b395221776c61916608bfdc8f724e29e7074a1c06106f90af3400904359664ef89899899cac3bf372f6f8653bcc07913ae8e2bbb3ac3d309bc956cd128a38ac5bc5f2f7e44597ea925db6962d146e189b6020ba3421cbb3a6aa87980b441c5e30d1a5de47f2842c1044e6791f335319ca81c6184331c31ffdece7810dc6b366cbed97c4a1c61be2258f96e3c3d8042666d0c5568d7c8c57e9d67b6096090b2cbfb3de5213216ffb96e60909db431ac9939ab860e931a3138a438778c3fcc42c23b05e592fb86174d2835a7befa39356d95a71443de8bad8639ece5833ed2079c64fd3deefb2ac434a2badd92773d5c1cbd9bbfe428d6adaa95671d75f988591ce9a793674233e76ee7e16c3997bebf8b067e7b066cba4b392529a6913d3ec21a594ee44eb7516636a06a81c1f67a593d619638b26a594dad43cdb2c61bc8c93ce39e7ec8e0b5aa0546a418a49062d68215a21597e5a1a032dcc89238ef25352e9a493bdd560c5799b25c6497134d88f361a6c1afd15adda0fae0d2058b38cc2e0aa3cd3ea0ceaa4d32c3dddebd9d922a3aa3e461a7d5e5ec6b85e965846907a1bb789626b53cc0210714111d900446e28c71e17d428e17239f6204186fc9ba172d68344177296654560308a9ba98aa018ee28c71e247e4023dc9b630f1231247cae82044e0ebfaa2790544a2ae795d3e6d974d22a86541837a4d9569b9d5a39a2b2fa7431c618230ebf1c278d94ce397bce49bb637777ce6194360a3244dd21ae7cc7d67ec0dcb4717ee243b905503f8632a8e7082472d8345dd3437213c91da3a1a109a29b8432890872b408430c2ab0410f6243c8a1f4d9410e654c62d1319913ebe8a40540c8a28e4f72d7cc9c808830960862084bc08101931cca224608914389238fe48eabd9e24310648083253b4fb65085560b213721db196b42104f8c410b1e317e5a3d45ee5f9cece54682152a4600c1003f99126122532264c8f43674838239461c0c90b96ba11b5c4619b182fce5ed59e8c6c6534365468352e786a8972b9ab42afeb42a66ea04045a5d11c4a26955cc4ab069d56c41a1622d6092eded9b25a4ff28fd8cdb90e2db9843546e154185ac69a7a59f9c653c4520e9236ed8b19f1690868606081e1a1a3b5bfae7e7a68b4c234f27a42e58790576e1ca10e544e264c9495114f6ca1863dd682127e0450d4ce0042a827002103d4e94411453f8a6894c4f8375a2072567286591e98d0d5880844c8f0adda07726891b90a10c487821451450340f144540913296b08230327d4a8480154f38a2022504f2618cc1586b2d8ceb158b7fb960228d1a8df1f33dc4263e31c618e3a682ac09b3091fa8481423dcb630ae9cc748f923251631461a638cf81469157725c4adb13d7b26d1c959653fac92892b1fb1684a29a594524a69a59452dab19e2554fc4049cc162bd151634716d4fd4e344b89c32c561da02eaaa88c9260439e3f9805716b27133058b58cb3e7258e9f0ae8c5157a0a647f5149bce8327f85ae4f01f0b933f245e9e0cab7066ed7168951d4663c7d7170e5e365164839f6576badb931ea92727833496679f9cc8594e76d6c91a48c73e5a3ca6c25745165e200274f7ff28bbbcad6de3b43301603820f9cb83b477cea1463c874708489c9cf33450bace8eea69466b5577727c623d6c3478cbe6009cd6685c4b559ce9d314615f4102aa707c6cda1bd3f3589631aeca15dbb0f7d1e0dcec693ca39e59453ce4b1cf6889973db68f3e83135ec9a314d9afcc4f468c2c34793263051a820df0cd44595f06e3d37dec881916e8903144f1cb9993f11078e1c4bf29c3c730a2249b6b879d6501a2bf21444bac8da125411f2c431ada5d1e012f6abd962f1276d7b848a1f1b6a4829aba4524ada5236c1d3b35b543ac618e3a3c4df887059f3190d54661744e4f4e2662339311be4a9a494524a29b5d6524b29a594ca15488f70795e6ce0c841a9b53725c79e2558b841ac0875b670349e4e94c3a756d5c7305cfad076f40341672b23cfa3c41b503d327dd5e8274a5c49a963a044707e86700399be5a8066fb6dc3a7b8321fa2783ee4792e8227587e11661553d639e794724e29a7aca11b92ce1ccdd2b3e79c731291854b26aba856c898f4913e32267d60af4019963e71a51e003cb8dbedb577f64bedc09c2af53a890a52b841196a603ae11236b265bfa38629705a4038a086a755821aa6c069592c839aaa6aa7aad630058e9d42078253e5801a9e96c51f0d2f50997d5631a2a422d8cf6c1ad44595b0d264da302b1aa48ff93448e792e812338d5d6be9ec209a0eea201a1e164b071949b89904b61dce9b36d3264ecec4f521d833dbac26b38c7e4183324c812850a6ef9d69edbdf3872aa1c8d4c79b3eddf4e927dd12c21d9b35dd90da759ef499dc073355f4351c71a1d8bcf8dae17ed238b62a9c357348a6dd1de4136b42df1d343b1ac213e7389c4428928963278e3671b28933a5c8f491dad4bad32db932ff40e9251d22aae88bc834894b95c8f41587a84c73ba90e589a94d5ca19f5c1177d263267786120a9c4c3f71664b131155f447e4c4c0064ada2674a3876472c68c8a26964bdac6da7b51a89634422a8de905b4b9276eaf4212e85ca3cf3d71b5980b26ba94327d0f99e6ba7c0d5fdc17fbe1c81eb7e87dc3dd8e6e7b5703127706ad794c00bd10b6c686d5daf01755f442d8d60d2b35e4edadc4a79095b8c6d6c5c5f71e0d43bd6733e4a641b06d6e6ca88fe842730fd17ac81c92da214b452a71673645245132809be8c283835c95c835895cdb87c329b9e18dc3323aafd0b9cb78dfbe830b9df3bc875216bf369cb2dfb8879e7a710f9debcec9fa1a90b832f4e1a896082f727df7b54d6f32c8620964a3c4460229b119722381b61c1b4cd5b7cd270c44a14cf631f10a64693736077da4bb69cfee96565861450e591a62d20994859f5a4317eeaa4392c644972fd3f7882e2497d9b6e3fadb2605e9690d5f5c170ee2dae7a05fb1bacf292ffa4ccb1b8930ec7e34d36a199dd7fde832a48b2e733fa29d6374993f19882afa3801c0832b3ae9323342291da48bbee37ea44374d277884ec2309d1dd20a3be0e2062fa0a16991f09daa968e9c4662cb0be11da593b08c8e8ce8252c33ba5f6c75a21cd1a553de67b28747df2a4774e94c65e69eb85a5c999d3d9c23aed05beef3eea98b9b61f0ab22973ff50b72460e929d9c06737696d41c6a9454a0c9a1a4024dd67a882e33d3675996512a8f9044642a83c840f33386259206692c94e1304621d37758823c710f394d7bd29eb427fd86855bf151e04fd735721a046bf491adfa4e466a73844597104e7419407499b9be91ecc0a20bcd0258c2cd00b073654ed114c52eeedeeb87705fe37c9d33557513e3f60d8e146e289db48d932adae64b7243e9244ea18323b9737de7c4313861840b4b2801c517805a7d443a914e52605c0d770e1ec2adb48b066be3cc961cefa1eff01ec232ddb94e86bb875316df06e736c5d5b004c2d24983f5b80877c2d480c455d5fa6ef694dd54442986c595fa9211892ab5b6760521503124f4840655e8a20a08be0630dc50f2f0742f3ca28b448991690c43a6d18b4c1faf90e9398d4a9ed992b2b4ebcedd5afce2be61d8760ea7ec630bc6bdc3298b6d8b6e53dc8aa5120de322dc50eeb4f66672b51c2fb9d76aaa68c6e4d21c99dc28a5ec2ad2308fb8428f852b314824d2189dc2912194083231b129c1e0081f538e3d4b96a084114422a571efbdac958bd2d31a4b9ac8deb54a744055634224887e712bada75bf670db28addfb66b1bddb62daae86128add46a543bd55e1aa477f9c034cb8b521bcd224fa190020d6f96415759d1a55b624b8cac0b4417fa18558dbae25611df80d8e29aaa992cfbae596bad36f6d859638d353eca1669f871657ef20702ab42f0f9810abaa88223bacc56b7b8af1475b839f440a6b2d5511129ae4452a68d6f5cb93adc4c7f587b5936bae5029dbcaa32ca31267b8e115d648e4d5c54c84c0306c61583b9f75e18d72be67ac1b85e30ac55ccd535ba67ffd8c8227ee479d8c82e71ccd7a3c196f23c7ac4f9f81ecd127177ecd851bb7da77d3d627834d83c9a45daf77b584dc6f0e8d119c681590df6bf9706adb0c28a1cdd3de2974c91c3db3fb8a1cd194d271f35d20580501271ac001d820c60a045567d2ceadd034518446826788191eeeeeeeea63dbb9b3261c2c4a7079f263e384a39f62c319243544f7592592a8c484204303ca9f72ddd979d861abac171b53ed380c9cebd725cf6ec3e7030cdc2d5d75aeb39bc7d309febd560cd7e3badeb05f39cadb8706bbc6aed898248ceea911bdaf823457df7f282ef6db63be77d355a555970fbe14aae7498fdfe61766d2c2348bfaa24c8fd4af30441e719a748fb4bd355c7d16870fb3e55f516b5c44b532f6eb8ca3514325373ca6ccaf53d82748ba8de064eaebd53a35922cf929c5cebcf92277218bb90118b5c3fe9aa59263dcdec8c08223c8944c8f133a24bccf2973474a3b69438e8434dd3341ccda2e16e1c0dea109365966326571b3824061b6c9ea661a359ba098d1e2880624ddc7e54e95ad34515ce4991b8420fd420ed2f32d56692a964ee34487170399ce812caa3df67f473e77ead440060dc89a495a4e44b13091039464ae2cb934a1876b19d2aedb0119e69350e9be7c6fb911d7d8461a393704ccd9e58f2ccb42c0ebdfd3b676862dbc9eef9d9fbd15ff7a36b3a0a86dd9b1ae5a37b6bdff7afd1efe8250c5b8d7034dda27c2f5dfa8bf41289849a2aad278cf4128e5355fae88dbae0fb46dbb8e38a761957343b5517a859681660ec9b21779374644b2b99aaedfd2e64e38624301f7ae82fed63d5d1c073650ed10fc19138589c9e1e239de6cd39499ff6d117e7d612ef207d8465644aa35f1c5bb195ea1da4df7b1930dbefb8279d84654cf8e2b7ec6db9433bb774424316985802354849a49209e5f6d36442e9e429e5744af9c5d7963eba45f9bd2d7df457e9238cf28b47275de6a67c32a593a8a4ac1775414c95a2190120002000c314002020100c870362f18040226b931f14000d83aa4e6e4c1ac9d32888614e216308318400000001181111d2b40920ba942a8cbde81c14c3fdea0a82824b6abf724bd5fff5abba7e36cd081f42841a3e8d589954911c46deff43d4a417d352a5d0bf67278edc51fbabef31ae69679b2f1dd7c53718eb4ee3afb8976666a8e4a974971f14e7bfdf38fbde0ca8c6dc55ec5d42fa48ad8d2ecca73c5f6996e2f723d936103f66e275c9145969712f4612c0b4531b223d07758f54d2fe9d5b254d19c99c68c975561f8a97eb15c2e93bec2a5bbd2d9fac9874f4cbf027ae16f95b9dc32c4f565a0d2dde3ea347a88ea53a6e693af227ab8074de3b2db55046dc750c71d88229e79b6ba4ccb7bf918896d0459cd1e659e21344a9c2c948c1f580b88168fd34359736516b624f824f43d4cbceaa5157c429c9d4203232548654acd9f60cf4d5077d4f4e1249fd985da0b1dca5d7b1915f5fb94fc274ae5616a5b25e6d5170303271e1b05d68bf326a82f8d260bbf471cbaf7a35e9025707ca0043f5fb7ab67a840b8b3263ecc7a7ddcd4ca2c30387d8b46b73f237b8df39c7a7e7683cff2cbcbb2193eee4b46756a84d1c2b465119c01182ea14d3cd3412a64fa67c286e7f82c3ef05221cea799a616fb80a57c5270400df974dea340696b8046bfadbb678cce68e7c8d00faa8552057e02b5fb713560ae861610cf83d2e4e6bf81943810214c7fd0402c44a772326ea421f35d8bb1800341d473566097ecf7143f5b041522aba2f3f8bca1d41f35ad19a653f799877d78ea3a01b13047cb28f6a8a34ecaed45ef33d6bf160c45298e1c6ce52a533d086fe5afa71844882ac39f71a41367c99d302a77f10f59b4bd36df93aa7b2d1552dbc1232add751559116803536225b4b0a121dba218946e5136d0e835a344bf0ba5a7ce6e16d03068bdb3a852a69785b99a7949d32f24b00937601115dbf53e1e5f7a7287eae0f4532a0d76323d926d91bfc7010afc08de1a571209d26f999481337e3352418b104402042636689f359abcf1d93235f48d5872853dc3f7f3ad2bd331c7cdc15c9d26996c706b6d20785c8e81f1f91cf58df09529a6e46d78a0955eed735b51fd8ddbb65e6aae25de354b86647473697135a38ab943b7235353d654ecb64608e63cb11a83e88a9c62b920735e21b7831b5c2f39f289cfded3b3d641d8e781679335c8a9079e768c77461e814e3e0021cc9f25738c7225091cf68dcb2006da4adb010fc4f8c0546d0e76c93b1b3224ecc30c4ff942c4781139cc82ff2293312b3bf47f2aefc7eae52618ec3c09fc1d3df7203bc0c850d1039b68cbddcea7fb8c622d6b9d9c07aab8a1e94e21dbda163626c5317278f0f06ca4ba80220e22ca74839e36d422158da641399e40921df24305e7e82ed435c376bb999e24e1260d285b6a889a0d7228a209187a20001d53b9d38256b37eb7b20a24fc8d251f69ccb4dcf071ea9432bf5f31467bf588e446c442d0a3fcac9456c150de2f062ac465c443a4b26f569e763cc131502652ff50963634a3b7297ed7edca438a27f163d318a37d428c664bf4e2f501236ec7c18c1d4c97aae59be2c689146c1d4d0d90e5552f8216bc9e2b6de2c813ad9c1b7fc487a7c1bc86c4d279e2870207f616f06022e5fd97eca0a0f0c05f5271b51f3d21e15cfb147bb692a72ed7acd5988deeda8b571b821aa63329dc7676b69b753e1e288a4263bd9221bbd1271b345f8a62c23ca5c946d31b0ac65ee7c057af86d8e3ae749202f030baa6ca2cb060cebcdb88593f0733d995bed518ad7b36225a57ae709c23847026329eceed3d0a0b680e29db470a3ac7ce5b80c0d368bc1a3993d949e9a83e1a4319b740b02e58ad6a0820ab7a017adb3d06d00c8ba74dc4ea33e52ebd244af9152de63303c1e2b38e0b66d317bf611b9fb039518c189f8db5f6a5502e2c9210746fba55065f4631a725041fed2511b0da29ec8373c15a5efa4bb7eba86802f5d5a1dc854bae79daf08254c837fc2b36db3ebe78d09d7950dfbe9a5405010ea4de4aa0807a1340e88284e10cb8d90a2f13935479c3f43df595b3440f2889b7f35d87b3356e3b162afe6b4927ec0ebe42392ec9fb999b4fcc2588912d430e4558d174f54bb361c4ff8ef4b163ec0e790f9dff1ed7c29cf8bc9e64f572ff7dd22eca03f609bab34fe18a4762c25593cd178674ea7b502874384956254f085b83fb3e2d7f7e318f08a01cf8b8c49f495ee913b6101418c659b00c640104ede37e2a3a4024733529d721d4003b5e5b786c29a7388450b3a641ac258ad9091b9bb5cff234c89b72c886b043c21cbddb5c6102d24264426ba7fbe73faf96e1dd2e69c50fcb430792c64a0e3ff061b0439e4c8a88794aaf6d299ee70f8fb3a79c779936d6fafa153a11be724991b0069a8fa52ecf46709a6c4435665917ec884383125606c7775c21a035430de4293ac57cbbda062ccfc23f546c2be6049383bd3f83964a411475d24fbd8c9e43a6712183b120f5009fb080eba7d0b0aa3ac2149a8229f3091a94a820c14219ed5f5914b32bf934fb7ac5f128218b94c128b2b691350239d536e907b74ac039f3f2e69dc721927668118607c1b68fc0ab47f85e79026b2ce30f14c29834ec3f14ce5fbe05d0f1e4e60c1d9a1ee42a79c41d659f2f24b0e1f82edfd56bbe680cacf94694a1760c3144333d55bc7f4d9529d5f8489e2350ef480b787f865293c5152fd9ef71c432a358a0829676949b0fbacc62b9b36f623f03ddffb1e3e91531ec8c128d54407a1e2b82880c465afa49d2fbf0fdf099a8b6807bb2f881ee22caa658f6ac1f1616f133d5ca35d2193abb8d4baac97bd1c4cfc3128866f8eafb35ca1ee82f2b005b8575bea9f27ec427885d50214f1fe6277d71b09b07130556a2cabf00ed30c9c20d0dd9fac0dd6696baf139e6c07ca531e1c8c6e1a3084b0e6e0d50ae7275a2e14140c8696734ef8fc243ed3bb27ec253e2366b99804ce1f90b094e00a5826500b1ec278068d9c5bc017c71c307331f2b332ecbea224bdaa14fa6357ab8b300d2dc81d97144029a9c1f87efd592fe367c51b3836c8ebc51fe6b8b85c123e6f92a9fd7f0dd62bcefe2b0f00a51c75250851df3e74ba4e678e5c49d88dacea588556380fce909cff8039b82d811e0585eeffc727834bd8b8dc6601573e8fbbfba51489b97f8a12d8fe4849761f6578df5ffb8cc4a5ec3a3cde9060385227f28411e8b282fb124558a600deed95789138c922928e4de8c03a82eff7fd4d05dc0501e400388efbd2b91920156622d7f214b60721ff5e1fd3f75e121ad75f76fef59fe249d860b505865d44e486c6114580aa86ab911ce832c155ddd186b5ba2279c00a40c89261e14c926b4490364d17d1559fdb6f79d73482d6b400d73fb05aa973a3fa096317964ade506c46db2aa910f91ae70267c83d62a701817aac49d2d8db3bab47cc7b6198bc75a327cfff3695f557dabd673448d45115a151138d2d23dc5a31788916aaf88f3ca1bfcfb1240e0c32ad0eb13dc1f5b6342a3090695c1a87c735bab6f26a0af94b57b5480d4c9b4e91935899a82c662457cf91a7616ff42d541ba3ea0d46724a4729dbe6f5a063af1fcd6708ca048152fdab2efc64e85f9c2f93054524e7a21a743ea9824712d23921a4a1302109b4783609a22ef39407ad727ce30a02ef619ce69d4886502ea8703104510f52d1d961f642d0212b0bc87d6833b8e34fb6f4cdd55aae01a63fa305fe141d7a6b20541e3054642392c0dea3984fb6f7b3a8deb36c1111eba7dfcddbee491bf7dcf1fb590aeab2716cd9993af260eabdf21e3323295a34bf5f9583f6ad63030ccc6e32a2025c429ae047640390374f01822fa23c72ce5a9dfbe386498e706069f4895cc518b49f9069d39b2587875200bdbc830372e4003d57ac6448bab4402224bce39b1d2268de6cfa0e1bef3232dcc66810fa447280227ba8c81694e1b55a060295b66735bec3b69929d90e8d51db54bfaf4008ea06e5f7eec487dcadd53cb657495bff8dbe03d74bad404af68385497799faba6eb2207b6a943a63b3f03c68c80f3275bc1a0bd332ad79416df200bbce6ec7c8551de0ba6939d0f932a171ce482f1de0fb926b94ef5685febe1610cc3edb4aff83805153de8ea2e9f9e5e332a9967feddbc29c5a00e17b75b7d59b2a6d53c249730a749b6540c870f18388f102b085e44bdb44889c2d956a70360395465bd0e34b40ab5c009f2896278258f705fd28c2f2596a79c10de9d6657336b44f658b390e4e4149bb3682dddd2b2fb48dab1750726bbe1a059bc60c0831589ac33f750ad1beca08f5cfcf0a407dd8c09b28fdd3d9ec31fbd5ab1fc54978bcf9e6a857bd794c7bdbb3a4e9298d07ec1c855ad8030b0520d42df0ab770317a3235f78d1281622604401195becbee5143bada08130deea7f2301e3b6172d7a8715e223bcf31645b27e0e558e5e5ebe610ec4a8b73ee667fba15728dd0e2234009a7ff9e6b96579a3d04fc84dbb5a277181fb379315dd0dd7d820afeb4cc69668a8690d493f3a54604f06fcb73e0dafa07befe56198d76bc7e67d47da62698b0f3b1047386e110e2b5d84da07ce4930f25bcd6ec27fb98c2c7e52f5a4e3922fcd05587c3fcf4649449b8fea7cd947df28f9da4f6506fafd229008bdcb101ff1ad4e0493eff08cf720dceb6852e5d40827881130e003c2ad9e54f15794eabae60b5a7d29075d40c7e9c264d7c5c426b00719a1ac3369c1fa29f1c89d192e1304542709ebfd8a15e724912936aa3cbb48ade181c70ae8b4635c3d604a9686e8432bfe909f6365c1ba7ca85fd3c424f9ccd91556dac0a137022b57cbd5615a189f8c4ac403204bbd61ddc4fe568aa1b24eee6676d8e1a50c4e4d3aed42f21d0b0aa73a806cfd3561657979d4c335be15f0579ef847107b0562dc126b454e39443267a441722ac106583e7e06dfbf9704290b659e9e11ec901f31b00dfaf26e5edec1341ee2bb3260a1872ae7b471307bb97c112be0bafc848de2b7e7860d62e517b6f7516b2c5f26493d6f7cc9f2de67c93e44214cb72c04d46277184c4fa5a31bce681153484768618cd68e147553953517ba55c0ea5867a4088e2d480d31ae67c9b6a3c6d6bc9e21a32c85d89a9d4733ec97e5390942daec9e6d357b2609a6c58f20a76b7e0040ca48d0bc2fcea5132e9ece05fbe4669e275c5cdc02de7398513c79190f3ea0d16410e7bec85e7b055beeec354deb090617b64c72f4075e5ce16e590b599ad61cd043b603f9085be0d6af7d586e7359016dfcf26e5e18abfc99b32e4915346d9b76309d25ee98f7da743adce963b25b7eabf7f976cbd9d1af9f2f46a287c8cb79ec0ea37861a60838c00d2ec566bccd4e812f0842d4db134ed77359df06959eceac428239e854052fdd86d14941c71bb9adc208551f39e3329769104e0c8eb24742735b828c2e2247d8d6453ebb881ae45ca8887521833b545886039302b9ac39ebae366307a4d9d9d3f4f40276d073282e3c51e5f510da1cfdeb6ceb0f45bdb38a64320810a8efd61e0527d6290adceecbd775d47ea2b8f51cf6d06b68516b2dc305f11b656f6c3b6d8da64d4c7741a2a6c056cb64713abf8bdccb655be03cf8292f271ec0e8a468391b4dbf66d975a8005c418b8a56bca370a448b59aa1b37294616ca10c59fc130d7187520dc90a6b6864d2b7432d4ac0e3a7c87853c8dacb90c9630d7c2272fa499214ba803da96f7aba412410cca108698f74e0b2513748833427eca73a3e79ca3c54c6451611367efabcdd872406d8d3b28abbd91ab70c0ce1542f4464e957a59993dbeb2518961ee07201170b1049a010c407c01d9643bc42948e676f14b6271a416e05cb140832cd7601a2906a16876605ddbf5228a00b78efaab6662889966d902bfa16467ca55db9c9dcbbf43fcf4ed74d7bad523288f494f1c3dab4e2422c0ac4fbfc7e7d8cfaed6f97981546c5171023e460c447414c1bb1fe1807374da41f78d4240c54885655ce55fd4429484da394f1f7949b0239512fd02c2ba92398bc4d7eefdd94fdcd14532eec727be4e5c7bad11381c7c2544a7d9b67a5826f11b1e4776aae6eb636c2d0bf32ea995bdfb803ffd8a7a565e23587cd66c3986ae179f6b129399f286aad7e4ce26efce8532c01e85850ad58f609eb6370a7dce18b8018c85824f37e545b95df1b7e04638c11546c006ba5c904212833b48c0caac3a21e367370cc100df488d20e221d1c071e06c16c28682b7a877475b6e123dba7c4420809d3747b653928c2fbb55a8614be89a5aec4e20b36b0430939971975ba923d92c54a8fe760f14b5d9e78f6141234b6507fa84b06c87f47c7939de2dfcbe6f910c3d056fa4977b51eee5781c4c536ab4af17fd623b417e3be4c567f18c29353d42534ab4a460b55be0bb5eb85295b16697499d165de1d5f3e402d51563c83a3e25503ecc45343253ef75d71f358bdae4fc65dbd7397c8937530371b34c803d3ce74b464840b595b84ca08671a610281fe8c1b3db9ff89387926d21acaea6967ca3174cadae1dd6585dfb606ab396b2827e0a57195524ff9a1326d427964a27fa8d3dbfbff44e550fac481ee644e1d89552af309c9efe2cf841d2126fee500d5802f22b60411c899e14b0ff836ccb85429ca8a2050d4d623e691184941ee4afa9e29ef8b865110d6861c4e2f305d9a8351979f4ebbbe759ee0cc9bb1637a78285897fd321ecb937ef08cac77666e5caaab72e2355b11416ae100b304799f5d416e59066edc0b833c01a3410c1b9f04959a1b62e320316a9268d7f1774a215ff21b62ef2896b8353335a32903abd275760197aaba2c6d8918d24424eb0f492315abf235c01b1a29a8b713d121f9f32e0d48a952c2882191fd836e085cceac673cad2f847ff40292e3c12f2764d29c4b2931f2c2b489abacd2b32aff72b1b0c58d242fff95c88cc99bd122d147f6d6a270403c0d8569dfe492478fb051fdfd5564286104b78c1c9155c048000d26276925961efdd6b8feae919f1a5de380f474007b4f9149ebe8bb5e229e4fea7ca54e11efda06d5ae5b930406e0d8a94c16bed42ce73e6d217c6bb5fda479247be06b8fabec7b99954952feab89ca34eccc4a3039c527955fd78f12c85f3069a7758287c57c27a9651e4304e07e4acafc3f6b56152fb7f5d0484656d3c378f5ab38346e70735de0461db6ed4d491f9af1699a0cf3dcf6aafbc3b7a77a053433abc351a6af39d59fcba3aea431b19d7571ea22dfb11c71321ace3b8c37fe23c874766ff44a4320a75d54130d80ec7621df0219b8a8d3e116f5e1d0e85a38f0a84c15dd40af3ed4796a5291dcdf7b012b89c528a348a06b5f9db31b4e18d345f36ded3be82a879744cce0847b0e13fba15c013ae892f0ee8c7c6840c1a5ac7318de809d9c2ea43f2e3a5136e8858f82bafc7b14ad066120226191be7ea480658ed5103c85d3224b110359236e647d5163f2e2f9603a6851bd367e395c84b4898196faabf63eebf1e4314493bd14f09eee37c4f42b4b86de4afae186f5b5d75fa5941dcb0ed5afd66c7f4070ab0b4585632d3c0e8de767d1865ea03bf397cd50e4fe0d7e0203a00b656f2003d3f6f591ec2ea1c6ac7542a567e39c366ef52ae892915081e360014adf7a594227c8f4bb03c4371343f4e24ec34bc23ec80cf1e7db1534186dbada216d7b9cce945e87614222eca43e0e33304c05939e44f5754e7ede6ad2f6268fc142619351163e7b34778a5c7807ead0695d07113e7a11c176a94021c3ed9d0cef3e4b274ae293aed5d9d3e9cfc45e35f5dd2d4b4ae761229b05f8c81db99330c207131b319a13d527980cbc82cb0704fc465ea75f02c4cf9bcb9c320a9721e27a4e9c333133737412cf8af2b3692334d3c4a0fa6b0d6eec86f69c1f88582c6d7645c9cbec53a8a47658ec3e1de06c9cf5fa67e4031e6db6895b6ae9315f67339fa290511850ffdf64f1dd5ed66c08cfd53fecfda9b66c7b49d9fa242a835a9da124ac62891871c1fa5be35e0ad9eb2b27ce3cd42e767ed91e4486bba2ce72df8c112bdd0b79b3562ee5f531d0f01a97f64238ea8b28f054fe4f37b6ae859f56d17acea721d3b7eac6f3d7a85f2e8930bf1856b8ac75cde72eadbed59d53f717cb8cb54c56589f60c995801e94391fc4724e35f0551e3b301f17e40584ee415819b3b7fc12d92f3429c3b54079acc852647a023279c2d036a6e29d480618a9df651e150166a137efcc2e309c3f72b8bb590cad436477909f0b8b0d94b9e6c7e1d682d248d67403c55ce1ca82ce8207b626e4ba6d737a16c7612a61e93984dd6c28a46f281fb34122bcb7c248ae39207674ae287a1f79387df61395cb6679443e482493926bf10290ff5666ae014d6b82b6bf139ddd89a9e34460640b3caf0b70485f4d37ab6ab6362429ad0c8658b76b900602ae8ccad4bfa3582bc323ae946d1b75e44d88ca86d0e5f637a6150882ae0f6aae405ea12d6df77ac663e28ab6e63ac9d3b8082c951d2f83df4550f2e645d4892498c52a3dc023f4bbc5500a3dd810a382966ea97d0697cb4fbb338a4f0b078f367ba4938059f5ed83a54f39788bb8404c35309bb49943d730237444bcd1467f268d8c83c6356bd751820ee3c9c576d3e538d5a87d63e0fa4d35ec0ecc6c08976f9c768e593d7229d4f3e4670f7063698fd873ed5712c0bbc2fcb87a08c432d3523798cf0119d51270496dd5e2fe3e56132962dee12d2c2f3c8f41f8fb29fc13975fe24ac35d696fc52bde035f4134584a005936c80a4102fb56d517fbca6b689da0b0cbe0652848599c0737597fbae03741fb0bd756fd40ae849d8c15ebbdb1adcfce40091323cd7f0a2d06f0fcba69a35c0db345b3170d6d03b7cccaec060b39402f8ee244c649b2523db53c25b6b4ce6512e08d2dec88ec22b203a2901ce5f433f6748b6aff81085733a1174ec4a3c55219f0962c00d8dca47fb4953f768e0a7b45f2147cbb8fd0871a2f180d82af43d00b3cb0b88fd63ec21e89e1e2221d69c7bf8a01cac477750127a4d4e99ad4c9bb999ef51e493b8c8e39c305fa0b86284dcf6c1e714c40330316d555eddba7674a83d2bbc8762a22f69d4ae2ff64381a2fe68a6a2e07871d4f27a9df04aff1ee0ba714262ccd301c7c3e4f18ec3e05a0f9d49c0bf6bb287fc7611aaacda61533305e7b488d4e03701f8d26632847e523797198d2ee4c8c8865cf1876e9b2bcfc1cdfbe6271a231922616a2aaf372894f6f30d28b065e3274f89faf3e02a24b0a541f3d5995d509511d0eba57da48e857f40882b2a65683b1a48d59bcd823f34094b754365c2db67dee8857b277a206a21f42de6ed2c55fd11ffd8d665114c780d8b7de7e685dc7f6fb36b05db99afb5fb745d74984524677c6babd41bc08b1228a2288dbc19af134abd271d76de955e0c5f4aee350a12487ed1013310a72ae9bc44af61a650d1a8b3f006044ae90930423b0b45a9d669502b90bdb098f9b4ecc5c8622e7429a0dbcef72b0cf84c865a8e938138af67523985787f0ab9b97da16f9cc888beafe8823e0a02b8b200756d81ee15adeabfc0eab9e6a3180a6f7c7bb1dacc65de75bd082d13b1b5fd055c487c6f1bc423ece879ee7f87348cecb87b6a8513032635bd29017deb751c9e08582117b880f6c355db47ad76de14a141da2bb894063cadd5c70520ba0015b286f61eabed0d88ded949251b40a1a23b151a1877329e86cdbdb1310936f05cb4550e069192a93af62573c4b708ab221d94210b874e5825bd6b2c52e78e8644773420f3759c654ba1e5d258ade0963191b5a1dd2a1ddaca4669666deb77496021ed3a14f0ac93b0c7babdc59789ae8a7280149a8d38d1ff581fa38b94c16ee8654ca1b6507a9a2b8b0bbb06b58d49e109e2d9019766926667e3fb0c2c2a41e783f7e9eff953e86750555e0d24aec890e9951583924fe18987eb161cdb84b13161c8d41caadf7e0bd6f64ee815053ccc00bc967c84584fcdbe49890a747cb647d014d48deeaa024afde198581960957923c48d94b32e93af7f9eecf7c635c68c2742cec1c880f20207dde5f8ad586e55d6f0987ee5b2519fe8cef1379f8dce8b39a42e38889f7e1e4c6aa3066b780d7ef584c3fba1b54cb268ac10ea5db8aaf13f1ee2136dd87325541233fbee10f25b3d1515bfbf8ce2e3c66bb0867ff1ae219f8805caad54b0a3191ed46f7e61d18020c3c9d8f475d077e188218459fd796c46b7598899898b03a0464140a2154bb93cea55f60d6d6742781cb0494b0954483360abf280d7460b52d3fe84d95b60ac9ed9b977cd6b65cc9cfbe7b08dbfe3af58c2e8d8b847271e0bc4542c1989982a0a22f5453e0852eb76d6eb362ffe82e709133431e846acb9184fb4f44918fe97389528384c01f81eefc1a1fb7157b3b864a2007111d5fb554a6d0ff87097af4ccca2db136b7e23f79d6aaad245ff998800442ab84a424ba24ff38599655418db7c07da9376e5a474a9c7d81668f8acdbe6e17c3f49035e01954d57538a98ad3667d2b332916cedefb47e5202d76441e6acdef3c748ad0fcc267698327f32b8331bf3234590be98077ff14537119c4f2dcc7f64c25f2f76bb187dbcba8e5a227b4f41d43a56b6449279adeca752e449362c7d0818fd99a4baa620e7e057426b15b43daf1832c4219959dbc6534b6667d13312c8ab12d38f7f5ea7f8b69498eb8abaf8a716db958eb8706451414f956e505758890ccc4bcdb44e3ae04d1d2a21603c53367a9e6eb009d4171bba4779cba21816fe0ce2c371dbb09f018c59ca15813e0a04119475bab3c1f0d3ca79884a1ba3d8f264367db973ca699a26bba920aeffb8c98d4acd309a8eafc04f52a4f5d4ce1adda7e0dc7d89e759e2ff479463e34a5676f56f796f6ad4e5ee454660dd642cfb5a874d4886a402ce029744bcefe5f81bedb06b1dd5b1f63d6da35bf22636efe884ae42582bf46baf6e0592385a31c853d564e89ab538bc12ed56fceb7dfb3e037b93d70ae7c315edab48a9c561ccd9dc91955660d371eef1548c88cdb8acb9fbe5ddc4cf7e4f461c335e0f8a3e8f2bfa8e3ce1227b17cbb943fec9e811eb3be16b4bb442d3cacf352dc9feb05c0ee0b3f044dca5477f3994cd25088209bf36fdd82424d8d695d0c5a722aee9ecf28c352958b2882a901e93c4c10ca49e7973e17f96fd56b99efa37dc15a2f45ced1cd63b972b18c1f1d7a7fb6dbe4e483b91a6d842d8e141f3bba1277310b41acee62472f184d24aa2e6423a3943c05e408a20231099174373c763e690950b0ea6e31075d414818c4536aca58f98f5ebebe3488b4a1ffd1e023ea9df786666643d2a4b729f0060eb53886182a9f03435cd75f318c2549a79366761dddd71ddc21d5ff01aec0c9a33d4fb649f99759a393764df9c3db4119e8d73b53dac303a8ffa3f92936ca1f4abf8d0629837090a863d85775d3d4e26e99dcf03cca4c9cf32c9d98154e391b6c5ce72f28e912181ee7fda8046181f69caa360506644df207ee55e8a17223bcdb3719518dcbee8796a0a5ca43003a757a841e88563cd4b72eec2c62f2b081a60213e25b810ea0fcea7dad26861dd7ed56c98767fb7b5a5a6aee612b843a9930bfe15bbf25b6c2aecb170361c67faf22a5066ac01b1c4c9f416b13f4a1081604bd2054f9e46363b67866577d0c621046f7188ce6397ac60844e8d8a818ef1fa09e851958bb252381b84afc7f382fbfb9d7c857fcd79fc8acfaa2f78f88ce3899e0cf7146b5011880d1a1946103572b4eb3092ecc77c4d744d03229a4be0599007439da4ef36fc69c5f59b229a46e13c616064829997155caf03bb0fc07dcefba8477adce1d668edabd14a708fa5a496950148901429609dbafafe7d6ee9432376415a9d4ccdeb11446da6a17bfc3015c6c449a0c9a371fbcb7b24c9d75757ffdfca3f0c043d91124c776ec3b5083ff0cf93448ac917acd9ebb2bf44aa4f5d78b138c50cea407ea53887ba4c162dee9ad00aa1e7ae861a8219aedcade0f05b14374f8d12cc7562a4058c2620308902948dfe6099b0d6ab962ca116d6d97668413fe562fd1a6308ac0f25e8034d8366781757775c88417709273b14e5775f4aaae8d7592b848a2eea62cfbfba52884742dff83cb50b1ce52b31c2896077430c9b3e633e9784b6876e32d065276df0451a1d32c0cc6fbd87e75b93049d37e6caa0fc6ca82250742229863dc0c0dd58ea17249bd06bf7856b3e28d0f5959e9dfd0de7552a892821c1e9cebd1b54aae2bd5adcc79d6937997a086f2ac7e8d1491a27ecfa0e6bddf443b797689d04d576469934cd21fc9f80e65b1c8100fbf423a0a1ddf908dd24ff14bf8f05b9fc8506e4b8a90be677da9560c2afc83de491cc4b97f94403abac0f0a194fe64be626019bf8426dca240383b6b8b7f8ee2d330d5efad6939ccd898bd0d0d8e0142389737439d98512164a7c1a6882194e72292abb10cb6590c0ca258fc4eb274ea4e3c342bf379927aa4477b962f150394ea6832831c8fccf69a5d8a0b32128e18a4344f1551cb06d2950222ba23b1c8c89655dddab670f4846308349cb9c0232deec73a8f44e26fe46f03eb45e5818b704bc967528a9e3620ab508089ca2b9588d23a9b664742892c6927e758dc1223fd388b65fae4fb55b416935de3d050b654918b8e9f78d91b49b994a69c179e1a2d80789b4e1a70843111a8d3bb7350fcba57d154838bbc1dea4f9159001b5bdc71f35a629f69448d2650fa0032919423aa1ca27b606a6fb4256346dbd0a60e3384f5bc9e1f177a4205e8be892ca8f6919246029471131f4efa04f69f26a3d23a3ca07a57a993acda25b63457e1254400a61d9e4e5cdfc13199bc835bd22a848d6fe05801ae7b8fa8a10b30bb7d9c8e4cb583e8aa5ae4830c9f14b933354dee709f909c1df1c744e6124d06a7708e5b1dee475f0a2c06719bdddbc88e4e43b50733fb956e4033bddba17c3c2b66047f32f304e54f56db386d4a0d10e52f93b8440a164719928625ef4a683e9666a56b0320717c0f9f14fb5edf9fce986b23cff84a2aee5271ce5f9798bafa4fe6f6c7e7812711692e83363c017b946c0f91d6f8be40eacf2fb827bae3d54d6d4f9387b8e92f3f82a1392b25f22f64174a19e14149a533725048908b6292200567ed9fe2010c1a7ea3347727ee569d472d03e260065991e26af101c251ba23a7d7480643295fb203d5b7a3b3e6e25af36a9c47ad7b9c38498e771dcfcf31574b8390c9191075abab3f89d01bd559effae6fd0c75e488cfc6379b8bb0dd3be1f5374c87758c011ab5b0313a0b34a32375b190eaea9760f336c664952e07beabfc838b9b8a81a5c420bc1d2856a83b67e6ec93c1b6258aaeca26b04145cf218cefd7b613aa83e3b9cdeee9fb3f1f228bbd6ae65f874212705c407752a0b1d740caae552627de4ea66e87360a180e31827b7c9fb0835d9c7ab168706ff865dfc7e1deb6e85d1a544baf53944d11fa0f6cf35c6355e202dbcecc76c52f3d188a43db6cc9dd73a4f8469a824b196f62124d32370f5a85ea45c14abfd037b752383cb7c5ce4725f42251b4ddcef8b6ad07e5449de1e58cb0fe500f6251ca865ad36c08340110ece19144bab4cece6964cfe2cc3bd1d29d1f193260ce92e734034630f2a35dc34103de3a7dd0e8b8c03d6edd8dd277ab4a7785710528ba32afe6a9225ae4c188bc5ebd50634fba8cc59ea7aaf3c7d0d052325daa473d2871e2f3161fccc44876a1c79595f6a46366296ed1b0c1cc49bd5cab8e05f59855139b602dbb0e1825889b2315de3594e6883d720db5558ab61b58efb142ae4c35b26a14972fd034ce7a9c831207f2a17aafe4de3d0bd1dce621babaed2fc8e1f561660b9626a7902a80d0ea0867508c9c0236c2900dd38d595b3d1c5e5d787b3dd92a23edec5716aa1e56d28f1987b215ce02664fa6b212ac3c842ebb7c579b3881c61ebebced8a06acdacb30ccdaaeda6ea8dcf57cbeff0eaa54252f18709be6c46358d9656054aebb848c7cd1b53223f36e94a5262215b004176f6963f05102b736dde71a69ffc688758c0961515525228552c4f630ec468399d151891721bb00a3a1ab5a92fd29ba2f3f465784c904adae0b00190ca8b0ff5cf003e63c258a2dde978f139c25b7f591c064e854521d02336fa3ed5953f9e6b66795c1a3c58affa386f8e752d4d57b8b57a88a4bbd31a297bc82da93217c67f803278cc3b8c6618154445edadf6e512e79dddfd81383ccebeadbe3ecf42e7ad28a18880963254c9e661bff80a7c84f7d7c53aa7fe71afb3c5004ce2e37ce595e88c642599718affcbf64c904780f035931b5c98985693ff9c6403a2b10be6750e6eebc11867ea182f0134ab5bb5c79a5fb7f527750958cb0e08229b4377591d30f91d1a534ff23e87f5c2054c11a57ba12fa3c2b976c00c1346185f5ce14de8ae218266e45ddddf98344364f4f214178f6954bcee6855bd3296b610e0fb69c8bd219aad27987fa31879e4743006e5e0f92d6bd15cf2f4b95fa0d8b7218ab21647b0bb1c0b83fd16ef26d9148d389bb09c5963cf9c9d4f6f539d827fa269cbe81c815c4f0752eb32d86352ba3e3e0427b78244d1e360c5504f830394b40948ccc8686a6578759b6991a4df92984876467262e6636642cd55877420344bc6569715e12071e692a7cb22711d90feb500f83d3df3f1c32f2183ef4fae32de6e8afdcef1facf6bbd531703d0a2a8838a503aabeb506bbf9fb87e2836082e1c9c55b0e5c082c0005b62a7778580858ac9d4d82171888b2c605d24b137281cc4556faf70d786dcbd9ecb11836b15e5fd7ea078399ddbeea4c4394fce1c704fde18a3588ba080cec67ea2bc3312cacb2a500d60b8e54ef07e95b3ef689ededb284e8c9c30b587443c77a1628cb3340ca7796768fafdd32442bc2b182d8c30fa54967c312894179aeb37e6002bf92f76af8adf5c12a0a43a7b419a178ca582d4e7190fa735411f01b8246ddc10d866916bee3dc55b59e58facc2713350bdeec614c937012efd402a99c5ca83b1c801d879c456ec4ae621d58c01d65680e52329ad87700789000a5c802ec7734bd7048b6b03fa009bc98e4a7eb2c34d3bd5b811042424680f5bc6c257cc32726f24995f53cd1ff4c746631f090e60892fa7b9e7d1f0d683121e2fa89b09d8594974064b33d11a9ea517200b2d570355eeb9a39f8cae722f970053879cba5ffb90c551e72adaebbdd15fefdfa0f3cf4e04ef9b67f9fab96bfb5d72d6cbf196fa98f1ee1e878ffa145c9bc4aa3e411954494aff8b80bdd7049fb3c814ec70dbdb414c34db693d6f5787592a0f1ebf7c1b89bfcb3fff936d7d7cc07326a52a57206c33ab8571101a11ce9245a6196e1e5f922edd50a3ad03b8e36fcc21667453c4399b0f4ee39f15550985c911cce8d26cf7ab1b815e7216a8e96b8a812462207f8a51e9a54923138a0ac220bd00bb11ef91e1e79f53ac8a70783e7a6998680e36000d002d53ba97fd51a09774f18d9a4220ce50c39fe527ae1024f2f285848ae29af638a1f5eb056305609f5e15cabd7b90f15442765684a010cfe570db9ba66de5ada9bdcc9456a3957bfb48d7f8051ab2234dcce2b8b01ae557c182a8e9500d4867951925f01016cd52046d8325f45add9128200fcd952cbc13230d6ef0563b0bdaafc71ae3c4f3f6e68d5b873f164fa1b479ae98428bb1e3cc034dc658b8ea4980e0fdf7adf91d61992d50a36597734fece06d591028ac47ccc25d7d988101e6b5cb656c34e362af5094e92c20e0d1f3c36dbe2f8cfa0a84372210ba20357e76f18e47e3e4006f1cfa400d087a61fcd2f101b11befeaafdb2994576a78f75839b6a69174175af485abc0e0a6dd81e12377ef180101d3f6d21c36eb58ba64c26f12a1d97c086ca5846a36eb3462bf35697fffc9bc1b97d0266f85c8118dc6594cf4ca82fb8405433583610c50642728ab66df25b1bb94c5efba828b7df6eeaec9530cc9ea33106e18a71790e0e4e531247379efde8299d39980e50a9176e18ce6aa82ff11b5c7f1694ea16294f737bcff2a0d9b4b26bd39db9d4961a32210e5321c667a95be90acac699a906accaf29ef36762e878789970f13fe6160db446ad1d868fc6857e68ee66d6d085b863ed18846e55e501a8aed72dcf5f08bb7ff70b48e50a144e7a037d214043015c69b0f18f16fdb7f9166dfb55a68480567cadb4fb21fe84559bf57e35285b43e3aeb1e5ac4feee183e701a834a55b0cd4b2f61eb9f37ca8a9e1efdc97921304be01b18e1d8a2d50f2c7978d090590f5c8ad4ace6b78b551e65cea365c8e352c5b4d6ae57c5a32e6eb632426b6e6cfb290198222b29705b4219c632e95124ea3d35f2925e45358733ba4ad75a7490ee8e77f8c623bdd3ba5a7c054e922fb8269e684ae154ecc668738bafe1a0188dd3beda462d254bccbb8187a9cc0af38f7b86ba2750b4a83b7a69da5a8316aec1db8a4f1fae550f37deea4ae9d47ece3f63fcb8abd655a94e4a13c3dcea1c662ff564e18662602ee74df9841be146c1c537454c28c53f4ccdac8c632c4991e8eacc8671782fec92f9b7d2495692f01880d715eeb4be0389c94533b93703818e330155076f9aa9be168007635b1f526d1ae5c92df354b5595c8f6bed3cde41ad916a3ee440ba1b65a93bae9994c4f8e601e079493963ee67061874ed5da40cdc0dad7247f68596979ab7105188584309ab480e699430b5a433b3c3013fbd4a1103ee8b570ddae0e9bc2f4567a94da0d98b2f182c944f3b919caf8c009fb15b91057713ee370272adcd8199de644ded7c908803c28b47eea19f486e21cf39d0576605ad7018612958263f1e672d044026f20eb1998ce3a4eb5704353e56b9b34b852dc948dc9bdbf1e8ea0a92a11c0e69c1ddfe16f39449f730681897cab56c9ac9b7d010005b1a93201adcdeb8a75390c6ff54b5bde37028332de40ec70694ce8fecd780f66c031cbfd4aa7c7d4383e5db1d2451baec921a76bbfceeaaee1530d7bec19525713c63f37a1b18df145dcbed6e981d600a6e1ec26b540e92601e841eb7114379313738f413fbade80b2f501f9bea60d12a43a045780cb4b373aa6d610e206ca386b99ec7fa1ab6681a9320bb86402d6456a8866ef352efd816affa080c327aeb164b11c3a51c467a5c78ee303787b0ac4ff528feec12ca3914f778d57b31d8a7eb2b8763d95fc40662d230d0b38b0e5054c775ff232cca8182b3896e6c8d1a2deb67ef93701b60768ab267073c7c89a070257ab1c771f9859c63414cb4f7b00371efde5d423028016cd0110c06e83069ccc4b26762deb14628080bf62b3700526069166038d95e9b1b1124775efc20bab823a52505ba008f3be66d99009d7730983766a9101f6ec5da1c69405036fe5de161db26d6ea785083ac9114148221fb14858b5f911a561296961b2fb232b0adc013b6802a2df0a766aa0524007f932a585782a54be574ef37a4db1095cce6be681302874a9fb8b1f992e2e0fe8ddbb4e84898cc168153e21d70f2bb2c8b9492782e169a753d9cb525e8d49a2500d34eb0a2e938ed7307563d48b321749501595da7af439ccf5861677e3b90632669029110ba5d59e2bb4b6f832425830e954a04d994421dc8a739c9878282fcdc5a928e20cfb8498b4687f921469938265809e04fd7ad21a0773a96e034b02f066edc0fd0afbd445f88487a72973d333ca5037a2d9f90c5eb0619616dc4c575d80569d9827e26a277d841586f9eef6394a325b5a9da5816b0690c558aa29a1331c55c38ec713216efbfb9322ff24cdc82e4d4034b3d65ea8c84efefbd3e5b116471a9920b77088a9d898fcb49cf8c4db764fbebb39b39d7b7b25b121d7ef7958930de12ab652153b0e893673c23b12b60c96f8a7c28d0ba6b5fccfa50594cfa6890e32988cf4ac0cd60480a57a84e4e25f21ac2eed7c9ed841fad933e2777f7f09a13bcf80988dae86dbe877d4e2e6d57c49d22625db425d9c44611310c5f46514c1a2c0f6b61ff1620410c05dcba6e427b9650d3baa8ab87e031f5a8cab39c1fde0be43302f8404f5571e5af37901145875c1d3fce05d7f777e1ef2f012850594b004888133fac7d0e4dff2b04711364bc49509c2f7e629b3a4f991497d542efa31b4eab2e0625f9d3272247e1c4030f9a19416fb5935622bcace9f200fbd6b7704e655e223135a99f78ff664764b0174a824c4ee3b0192c2768094d7c30fc2ed5f70cc6ab68fa1b493902d21cad675f0934c31e77b79fd45011bd0e6c6fa386f39f942d62ba5adfe773eb958318a52ba4c81e075e1411f7538ae17ad9d89d7f6875940226f6d00f823a9c58ef23480e25949414a5f090ccea2542db643f22c2560e906ce76684a618e231164ebace175f8b31e3bfed98153775b3a2004dc9314832d5ae47daf51ee6a813b073832949408cf1ad7cfcfc7f51cd2bd4bdd969751863183fe118995b7a268f35ce53443b6e12cfa4bc34c084e6d3e9215b6b51e477cf5248998da802478bb3192781efc22bfd2b2f85fb536a2e55e0295c094f92ae205a15bbf77e709b5a6b8157832c2d3e67669fb054c1f37c535fccfa7e409f4cd81fe8fabcf52ea3991ccc37016b661ff5bdd1d362ecb0d547a4351d567e1a839ca9e3c5bf27a9769528d86762e0358b7cb1b5a51115ea31c2afa086c93865c2410096e29f320b21f1c0887f25bcc50b320934fc9daf1d6589cfd66bc267f77d1128003c18b5c9ac345e883ac5ee11c5c41f543df29718dcda23807b1736fcaf2613003844b709a76c35d89173668af77459416981f036830aeef0eaeb1ca09d711bea22350f2a49db81dfe1b5a97886d6a15cb98190dbcbb877a3fdf629c30f16a6dbc2d674285f746fb1c2e4dc81f42b52f064ffb8a32da578d4ce9b76abef5a91947760496d1281804ab6f5bc079e19f02fc1ac63f76402f7a01beda48a08230a9dee6de6b8c8998cece6a4b3d3a37e880dedabe170153c9abc9541140cd00b9be5a0678cfbb72f268f145f265eaa781c34e8572b96327a64fe48d57827d492fa52f48907c0726062fd737640661d835221668cbcfd44c18b8debdf7c4efc016d4def3660e89921e037c27c7cb8c350741ccf2ac9b16c7ec14acc7e4b10d25ebd205d7728224187ea93867da359a8a6ad25d56a6e55c9b56c7b5ee3abe763d6b672725238c8c6037e8435fbcad8120f61617690bb2c454aed5e3c3a96f7d9c09342a3170040ba6e8d29021c70326cb3b8187e14c9aa1465b010a933724962ee485d4a46e1aa9d2d34d485827f3cfe37039050029d6d9625888806cdd36c0e1735c0ef6da4e709518ac074949a3c299b54168abce3198140d1de4d1bd92e48d59daa82abf6efe19c71cbc62f58b4efe1290d3e53381f0fe6915aa39492fad9e830408fabd9f25f0aceff1d41e9f51670e3cde28d1f89dcca8c1205df05b2e6c49ce41a81a9bd1907fb8fd2827f41952e5cb779f1401f3893b6a09bd0aebaab5b136590235e02f74f98fd49390757d8e55df118b7accaebeff5e707b4c2dc06e0cdf03d6444b63ddf3f2b7642e585774130b8433a322f989b485f6947a1e9f961a9dfe9fc3ef02228fa1586503d389ccf054d5c5c75c3bd96e065180487783ece3d78f79fd925ddda2c371ffa5fcce34c1e1462d54090d56ad53fc0090617983fc9778893d016ffdd7d809d440149b3447c6567ab62e4b43a90c784f4d42c777dff2554775e325d1c3100bfaa1bb997aaeae09f5c8ec8418345aed6d18de9855388c3fc992e3dc70ddebbf76cceb7fe39883301da39e36de59900d16d9db725f864746823bb16eb2820f1f6e4f39a3026e90d14a0f773fe6ff0ee3cfc8ba0884e0fed0e24c5a4f97c6d0e5ed7e07606e06aa4d5171cb43105dde426b9f227e69800a77f0797874bc401cf356cbfc83f8ff46cf1a38163d58199403ff40ab0177d994b63771332b0a7adf6674aa05b720dfe350fe87907ce62cfbe711c34b75a3e6eef46582058425ceb36c6e28e091e262054059cac5b54050ce32e8c3174db761ab30fc66972d95cd0d64961633405807e0d007556f5102dc2a49007832771b84cc8253c71bd62b07fea9e4772a7ec7aab58b23316faa9baac9a6f32395c986bd4398077f13cf5d2c0ddf0e6ebc469f46eeaf22f1e21714ecb417406a1b90192da3f1d4495c24cf6ffd5d101283b4649796ce7d5a802891d59ece54cebee724ea25adbb4f35512ba0d03570b1990b77c0578cd3364b7e51def8863d4268d1d6f3ec90d2bab2f557233d0d94e1a364474485c72e07cc9f90dd393aa587200c7680cb7e0011238712be6eddae7d5658a5f87a60432fcf767210978716a2283cb0220e5f9724a85107dbe64019e2763722dc8c6319909ccd96161512e216b137d044536ff5eca2434182c31c0662e620cc56752162d5b3fc894812f3b6eb862b4d7b8da5921f73e5d03dc8599526509cdbda1d6e8d80198018a5a91665a9d9fa7b3e841b769834d97d49932971db5659070201c4116ff76a0ee14eac2ca39ebd5baf84eebbf3cf8c2e3af8bad70d973f1067d12744c4b652ce27c2c1b80112115cdca62fff9e27de482a5ea08c6a63b633c514c73b3b52aaa47b1d79a65ede2f58f5b8cf5caea9070050d4159c71c0b9a4d6b552531ecedfec19e61b69ce0591ff82c6197f73478eb6b8fa7e62743169c92b5dbddd52c185029dfe5b558c7af6c7a919dd947c7492798112fd9772a62303014c62702ef28846a4279d129118662791a8ad1c157b9095dc022165cae6f190824e5841cab8c67a9ccbb432a5f18b8d2d3c89c57251bd22d4b62e4865c1d230c32b4b81b825fb074665699c8507af4c97bf4582ee10a1829b3994710b27e19cc96a1d4a8b8385e9b3b95b13694750eea4820d7917b322e6817dd048ef06d7bb21069cb5e5faae526cc71d9823d58635d49cc3221eb57db3aa779b5d38d94c87261048282fabe0afb000f5c53a3ffdd98b3eee06d37c8536e50fbb6b54f197a94bf8ea7ee7036feb187fbe2dfe1cd57637a54acb74ba3bf009e1bbe308a2041c61584b1644fa562000ee0320f33f50d34ed1900cecb47f91eb82a2ccfce0c8707651e422e913aea2ccee913e8ec318dd4aadfd9f687c8d27e61d30695ca6599c07f7b0a8c03d2f0f4859cb69d082462aac6634c59928942cbed1a5a18e439f5e9a3e8dda433d092d38f4a1e2dac01f36cb7cf9e0ef41b21cd4e9898557d15c1318a884a3090364245b86af30149a19866506f0d5e80d8f946142bd4930761b994c6fee386a891704809442f8b43fa770185123c9dddb3918e6c9ebfae5adfe866f66c0627553982f7cff6112636462ab2aba37c4f6ce5cc77e3570354785c2436e49d1d774ca2b9b8299805896a4a88390ad8d821e0c96f8efe651e4f7da2bab403a1c7fb3c74529390ad51ada3c59fa0b02bd22198a3ea84e291957a0554f22948ae7262f74fc312b1dbfe4363a32c04f7d126561e88d0806e2658c38e83d88ce9ce88bffefba70cad88c090f9ad9c9f5c7f933e80b69862dbf43bfc3f0c726eac154eb71e6a8db358a60670b5a310b6541c22c2d81dd250fea72f899a641570219215d52145c703d39008b6e5d31775ca33c57db4818a6909f69ec5ab55a5a6c680775d281c593cb21efc3dffd4a1b78519a893d259d525ee0a9c8e980c9d339c348dd0315746f14b1d2276e5b34c83c60be9ee5bc173c5a7ef69a5fc66a93482a6cbb45690d0ee970e577ca1998c614abb5f35711e00bcb9f90fc50e84b8e47cabad62526873921bf58b5f8bce088c99cc48a377114c0699796897242ffb2d7e2ff4e334cbc1330d3ae17b92e004a56c937c58a6a44b386bda191846ba8d140d2a09bf46f374987d330d8789607a353f5b84bf979db1fcf591174074e6fa06141bc046725d481b39c60ecd7d1a5cd9246f3485719911c2e8a8cc71ee9b7897ab385dc5836d28aa8de7da5b44a6b16545267a718db680765bde65630bb513be1fd796097da98d13ff347daf38a8e535035682b3c530fca9438f7596c07b04ca338132899c851a09b0f0b9cd22807fd9ad4188c16b68bf28ef36a1b1942d474886d7d87518e2bb7b8fa8cabf6ff95038f39e724b979a600f73526f0bcbc59155c609ab86f5e13a009ef1c201616d9f3dfd08816988631464ea60baa276b8f114a8b25d11aa70871de5928f7c6924e22bfd23f8a9330e5144f429573b65d72c21697a070924a2ba0c96fb84b83bc369809b41dea790ec0ce2205e695cdf5e74f53a268ca6d222b21b134638ff7bc1752482f8c47c3bc972d15be545b56601eaf81b55f0d0d3c1eb7aa26dcaf3831ed471bf02cd247df61dd4a6919688ff15bf80b1f16f4d017a30d7121fccbd875372f28d585797202fbbd9cf499ab0082c1dc4cb12303689fbfa89f73765943e4e9c8615c9ecfbcdcc0554a06fb192ae3bd403e834b1adfb0fbf1e06e4f8e921afe7fe707fd0ec9aa0150249c80dbeadd8c3b00e409f83501c10450830edcb9c578e443ed9a81130731c15e41cc04bb00d0c19e06d28f873915cee7b67fd4bf4f1e56fa0f53a127d00337bef1b0daad2867259ead6dc3926d096c053bbec653f99cfd89e433ef5332b0eab9cf96ec78f7e4b9284330729fc23464064200f9e6a9d1da6b487db3c210763d0b6d3bb3070ea95b5d2604946214aecab35051e2f8bf9262be2bb49125ba70a599fbc0377f029a79f32460dfb0df9ee305211a2b4d190a82130b26a0fdad7270e88a5b455d73dc8bce88e1c9f4f6e02a2d9a558a1631406d2db441b32c513dd7b2c3739be0d2d5ea4b5b4a460d3d90af5f9ec20337c7c2c784cb9fd796b1b4e2ad9615a258bd11ac8e92652188896e7799e57f7f249ec23233294b88c8151b5995739219ef0fa17eea76493ee56ddd7c5cd47ff779c361a83f2d1407c584a4c9b64193f61d14bae8bfdd45d151484fcbd61566cae7b8f1eb51e8c958e398fab2bfa08c2c8f8611bdc7466b23230bc2469be14d2c9aa20de768e4368a6ed3848867296d88a165b009de62a00a4f512c93ff57a70dde11ab1078a74b5115be11e43c39517a5ca141346654353c9094d0d4bf35ceb90f383776c47d57420a0721040966b6c1d62e0e997f7c2d3bab51e0be7549c17982bf84e6950901acbb71a44458ef48191c6a7bc6965f61624b48fcfe41ea4c1d54327cb34f7ac221d063b2e0b25520fb4db3523d39be5f20c85ee5a5d88c7da538110dd9fda0d0a62e2391945220732a02885e7b98d0a58009d00a3a723ffb9e5470aaf7e0ef1eb2012ad42522930da389e2b7b8bf6dd8dd44687c5a40585df8dced13e219fc298c74662d9714039092aa6c3e1642880c6f8d4d5243dd87be90fdcf3fbdd9bf5813124fcd7bf3dbcab63201e88d252c876ac5194b4f6d086296d5935ffcfc5cb99cb54b966906a39cc28695a1851906484b392c27b7d195917dabc9f5b3958969dc0cecb925d8f55639722da479ff0d2c232c87d3981ef17a9ab12cc8a61b04ae12edca7f2a48f363c9d18d6d5819b19ee19003bd2e87455418272156ad11ebe17492f9dfb1d3e128689c15a16844f473e628f61fabd0b88fadbdf124eea6dad220042b32e11d87c6a1d5787dc6761127566bdab8357ed1b4768f90d6611483161d22b4a8389186d6996f643496734388306ce548ec410c36c00198ff25ac2f229b9ed53a29581cc744e2e7d8d92490dd28360c14c93598baac073f86fa123f80c531a66e8affc021df8e749432c82839a3bd28122108a8442bebac3434c8a16512ab55955018555f867b7642b9db597894a75ab0000c81f2d1ba821a1a87a1b3ec952e4d2cb8286934537700c6a8603d545bd127f6cfd3e51b35422d0b2e2f007e9a55c284005ddc70d85b2f8df037d3c75406614e0e6a1c077ed9d8d5632779eb870c6ffea54f21ce636b58c94e3a5c437c7d6976d98b3cfa85bde04e2de1553ab7a214abf4ef299652a954418e423bb0da3b49a2d341cbd563cf535779fd9630e88c4de16c02a17d7c36ffe88c10f29c1558e764ba7925ef6c544493b7cdaa8c64c604378ba3a02986fd92a4acdd341a8c0c7bb3f1f7d9d9b7ee58154979b526944e9f7751faa3274230904d4011379cf228be33625d4cec99b9baef68f972fa9f3211a9576535938589bed476b56a5729cb1d9707d4cf43fd6ed0b4905db78439c4318620aec3353342d9203c745d171e85652ada68c2171b556c98ef3f8b0b54a9d2cc0e83c6e5c83ff7c4a6ff5ba22e18d42cddedce718e217d9a21d08d61bebd244b612990112b9fe9cdec3872d4be6d386a73ebb4db0ba5d9f7e21476f6e9ff929a608f52fc711a40d68bb7fae3b06a76718dc9836f5b2cf5a0eb86b529d33183692b028de22db2127c7201a1522de8f22716d09b6cbf1c49b72fb7bd212cc57280db71fc19b9d210a6aa08410440ef76b5f40f73fdf1b7abb41b532e84a03d65dc1c8fe63badb0f5227ab847eb58772ce7dc80bac4287e1e1f5677e908d91a035e5808e9c907eef7876ccde5de7987da71a8f81ae3402dbce411f4515d24fd23bf26d7005d4794ccb965ba56dd83c7cbdfee0cbda9ff7005a1c6ad912a44754dae1a1da6ac49bf288bb8f89c8e7df2d49d98e0c92a304bed8974b46f3b0846296b6dcdc1910f82adfe392decbeb5711772e9f89cd384dcb39c44a1ee077eac184bcb95133c273b7654fbc32ca2a19d783555687aa08388391ab83687ba635e7c8117a620d2658e203fdd06f835623bdc0a9d1f67ea1899e5ec8ffe6224d1db6b3fab67e1252a5ec256ce229ffd39d41259bad1d69a46ce425a005628427d395c0971d0711cc7443e23f3e34b53066a47ce124d8a70c7fd9bf5d61716309d63bfd3aff346054cd1ea73bf0eefa90a1b8cc407069946ddc5b35f7affb6a2bdaddd9792c056c9aba563027e1dccb3119e25f723815bc87de3cbcc770ccbc8ace611f10e47dbe7bad85a2a77b17249c3cafcdd1ddac81f09bb8f70aaf17cb8ca28a5c3a4ea836ed7153f86773a160bf24457ba03809a6469a292ae03a58981a8b1fd87c42431a23ca944501a3c8be85c339c32aae8344a51882a537362f392112efd0a0f62e1b89ce5abe7b08e50a1a3788f8f5dc9e27d7acefde45394b27f77cd22ffbcb26002665e29161b6a908a2801615842cff2491dc1c32a4319dcf622f9a438eee9a029eb80f8bf9e3507a5b7ea938d7cd82169964b6e7d08118982b1328c5316951d699ce2b967607a94df334ec8db489cde8f03f85c1953dae353bc7580d248af29958f941655da334a4135a24539e0e9b88bd898e944d0005640dfd4fe42691ac8b002d08bb1f63c602bf4fed452c190a7b00d93e16cbbe4d24eef721a64d3c84063a32a86539dde55e826264557fce17c04e130918369ab05f5efa631156d2c60f7469b5ccd7ac47dad4714a4524af2a9586f3908d7b2b8a6803f3d6fb58d5a25a412963ac1417ae9edbe25ae17a05e2debc7b470bbaa96e75c3a5966d9daecef89f8b201277a91b8f1467691344bb74a9292822fb6a2fb56be348509e9fc25c1cdd7a0397fd918aa89d6cdc1d51ce9ea0f12ea90cf71682ee34c3362e86d4438df91269162ec7afe9c55b9446a957c4c54a3ff475145d9a6b6888117db4e5eaef1386c221a36cef00c16a2f045b9975e15386033a40ef364c3a1e6c33b74e531ed4592de4a34a84388d2c3aa5a23ded91e4095e5c759960fcfd6f92aaa77372c47715a99b2c48d256de89b11c09c7b5fe2fa78ff22af4e45b99d2c964e4b11c5a37764aed5b30d611ca8c24441e99f960107b4dd07932c5a866d51a0e0cc1ea511541673c44bb94063254860f2679491490640f8c9fb0eb357dbc7ff6c4c2c0778f245018f4da9eb1ebd01ded5c649568e9c6711d811727595ccd4fe4aa0d30647f6796dca042f24746ee7bbe4b5d8d97893cf04944666375771f159ece94482db13f4715a4cf55369689a23c53e170e0bb06bdfd2ee15f9a89d5fc132ade3713db680b7fc368d4bc6b39f6e1f68e7aabd8882deea7461df0211c5426e8cf441e863fdb02e051f5f2f3a15ba7555ebe67b8fea237905bff2e1c2722a412b7c860c3317b6b882db62da647dcaf31a8838088e0c3c0bf4dc411d807e81401e72bf114dea5c1c6436a1a298f332be146f2fec813f1bb54c5707f06be8707c016c9c5a31013e10581bba3c5858750e91514464413e7e88f71a89c92b29b5ad9859b920d7921c85643e18b4f28a75b7060a7218eca89946b62daab95ec39468e071c3861bd773c5cc8b3b639fea3fa278d65a75b9693f2dc0aae433aa4758816d5be7c52e8f64facaa5e78a359e4e39da8ba28991ecf200a3eef2564310bd5b2cd0bea1978c5a89b1a8f5898c637afb718dded1d2f173809134cf29ee3875e5bfae417608fc2bd3dff223a854b9702f9aedc06302d0681c199142edf24eddd33505133192ea41115d8d5242f14791067755ccd10ec98b1b8c4090d4dc5d1ed2bf804ae8eccba4a2d6b4a1b017080e105656f642bf99a80f71d98b7612a1c337048da3d10769c12373ab3c3866560abe4dddd8a01ebc0f00bf670b36e232e160ed07f6a4794d1f717cc2e2daec2e579baf1eaab862be879a8c33f7b9aeced65941ebf2bfa66d2662585caa87724d639dc1a87662439131371e71259f6307ba194d70fab5f59190a0f057b301220076dcbd02320d558602f7698781f5a609ffc291d339618cdd6f1a69e4fb7eccfae65c7507d1b361d880b2982075b676f64d5f3cc909cf50f833a2ce390eadfaa0d41d7996848ebcfff4333d9cd080eb120ecb0df74f92347bbdddcfae579dc14eb05ec23fe354b5d0f09fe94beb6d672706319ef985e5693703c818356242601effec71d2f204746608c53a1ae490d4a2dcfde8ee032c99d7057d6f635b260e811fd882eb490cbc510f7e6ace0c08a1f8e38406963829bd55db708381155a81a52c2b2b4f67796144d965b2c8d38908aa8f0372c3d91e0559aa37365a669c661380d49c107eb60a4c81440c33ab19a71234d167f8a89dfbed148ec217ed97447ace156b4537e0d460728b5c280385c65d313be39baa27ce3f44a532fcd56577947b2edb9bb24c95b5932778e2a6647b2e9bb4df2267eb0ae8329ef9ff284191aef781769b9575becff7d51c5d028974b4c3981c4f4c7b0fc47614d2a99791868f6331046603f3a2588194fdbbfdd998f8049a565bedaf51df07d6892174d19b4876a3f5b1c4f7c5e414373a2ad9874351fdd092dbf8f6fca5c3c645942809a341e7c16c2ad354c759dd39a81faafd4cefa27aa40a914d3c4a5c0419d12e10e039e1166564e1dfd0d1c11991b2f7075c212b69e1b2b1287a387a133a7945213e6d503ce976a5aaa58ea3cc4b0445b24a2c145bfea82778d1ccd086baae94c9518df7e185e404fdeda70e3b0f2dc53b1928b977cd4d9dfb526cf374b9e4b34e740e4bc975f1ead44b2632e1875773861a996388d9566c8037d19943067691b9d7a429418d5387314e69454c91ae403f99d717c92eb0f78fba6228e61ae2e7d01caed2c9fe645013b56b9926db39f0c4fd6d067c8b3b9c9d0bf72e040195ff6b7d6b8bcb5b78b302017869b8690e2ca1a728c1e17d9d154ae12031f01129fd4e955090f31098d3a1655ba9b87b307a9c2c78af427e7196470eb453410ff9870e70669b60651a221166ba6299bb95571825247aaf92e028c1bb5ebd9915c5215c462c8f4b1dab28a95dde7cf2bd1f5f76a7447dc3fb6521dddb6e7b1bedfd76c53d2eb43eab18b23a4bf06d4503e84054e87e666ae0339f87d1fca555bcfe01459818e053bc99aaafef4d5df2810e8db206e31068459c31538142c9311042036c65e98090f331ed60b0bd4b77f30000ed4636610e83a2a664f2e3592c7144fa89ef3e78fecf4fd4072168168a2c5620a5c60119e48cd8393fded6962b22d5f9091f83626ab1a0d1e47aaf37cc3c647590ca6450844d460c4bd8bcad09294af2f527bf63d9c9457826755ffe4d1494839a39b7aadf80198fd179f99d2260bd213770a450a58b7f4ffabeecb6c0cdf066210570ea29d05b2905aa47413d512980a404195bc1ebcdcb6620b62b120142ba720cdd4c36ebb705bf42098a0afa6b4287782b38f514d22b5829ef4ecd30240311c795e886988048f34acc6009a2dd95a82aafed03326905af4abbd258a09f834a312e5366b35b7eeb502ae405a21b5f8b2e68c3a9e71a28e7c2699908de08a9385ed73ef65351af80faf37f78d8a2473742f6df73d2f508cc790c8c0d23539d37f74bfa2a0254fda72ffa2b3acfe4d2fb99ae727738066ddd0b6eeab8907d00fe444a38ac1e8bf63c67f93a9de4944a54deebc49952878bcccab035b0e4871cbc220a5d7dbdd2aff185a6bd5a118a345a29c6c9111bfd96833283b6839664153dc43646c2deca06755095c82098e30213c985dbc15dd1bb967370078c65577a50302ddea93917f2060be444967424c96f6bf3b2dfcd1002138439e400fe3d66dd0d38969df0f61cfea2fa041ac8aa7cf89b02bb63053c040cd416af653ba2e8b7b9271f0f56f0438a491614917de7208e63e4c926cf514fc97a7884602c348dc20d02d14cd5aac8ec0ce7fba996e2ac4a2dca2a347876f88a3706f6274e21a91ff43596fb4f088360d314b7054c25c127840c00a7338f00ea50f952ca8b5cc6c3619aff0278317a7700498a3053c764c0c85331585d36466ac76e78b7dfa88e42ba633ddd3f1491f84a0c2cc1a6d56f78dbf4bcd48c53b9e6234e32d29acce12f06f793634c236afe1f360c294c78feb8d548dacd6cb5f861ff590b7d8041037cf918c25c5687e0c2b69f30bfeebabd6b76024f11fd04855e36b2f5666de151b8b681cdf2d97088a20388f0aba54c08123bf6982b2ee4a300ffc1bf292f25a5007df8b73a5dc5dea2a3226fb400f91deee720601ab4c63df6dc4ecc54661b9a18c93fcdb0199bc410ee40192b9608f1000b3a986ace01e7823ba0027cc1544ab4a003650d12af1227d66db74c78934d7216f378a3d20711f15723fa7b4bee9a880a1fe5060282738b75afb5661f95cb7cb635f42386dd299b28b9706f6c08b0a10cacf71fe250bd90b246b430972b6cfd8a5727b62119ca3afa9918aa36424121fa1ea0638cd4e4c0200585c1023d5127e93cf585a05dc7d485fbbdef7b68d6911d0ed0f4ab6be285a842ddd051c4958d8d9d89d403560b3e78a2d7cc37d08aac26783f31e607dd375f4c552ac477809712073838d30f4e57b24e729d580b781294d763b25c7d3f55e34fb5ec46922d948d2d364951129ba75bbcdc8592a67fe1355a5a4b42707d14b938467a9d43c5fea7a2ae564c0467281a6e3ce255ab60f0b7eff7f7f24dc7b057f2f02fb5479dd954f90f15063d1b4db248429d627e8f234dc2df15d639592e550b3915c7d8ad4e9e7fdcd4d43480930c450dc942630f3238c2e7b176e912e10d7ba2c590be9da0367e4ce7ca02c63a23dc3e2aec3f6e437fcb0753447260a556f8409e24486b2fa72ded0c624b14b0c53313c40244942de5f76e89cc44f1cd62124f48d18c1a0da12d775e9c40beafc8a36770863c9a58952e03ed141240ba04e0dd4cf88b11507b9bf58bf4746bee50cf3792d1af80db365290f25837da91f429a3d064a195876158a32c6ede883a0e245951777c163106de1822c82f093abda3ce1b5fcf61a77972af3de3f7e2892ef55dd25d25f6ae7df6b01927187925ec17feeca6a64f00c64f1646e4d3b52cf75706bda87454d01115b5f67202741c19b5ea04a3c9ae94a999346ad0e90c74d519c4aa4acc6a4a58affa368c5b66afbde5a6b0f7c4fd1bfb24dbce0df84693c0260f22366cb1b2d2aa24036ac494717f296bd1f4b6ce8e75a0451910c3c3fa2a5e48ebf0e25cebfe2738a50b44f313e9486e6ae1e50cac3f20229fe1934a7005665dabffbc725140d13788984a5c12b0c8095a648699946bc37ad362d2ab79a84cf8d4bc0346227a52b40b2ef6e7f1047bd98022b88e71767e8eb8f61f70c81436bbe0a17abad222d0010c26f5b0f26d4b28a3feb1eb2a34af3ec491d63ff6a7da0aae0fd8ead6977fc02b61961cdecf001a6984e41fe7bc0b9a8ad24dc6f3789b8d3eeddd443de6bd3a12abf34ebc2ee7152cc298369f2720e10371929b3b5c38e9a6d7ea91aa4e14c00115beb87b0aa0464eee762d5eb474e77a11bc9a8fe1462cd018dc18060dc3bc8f0703178b3177ea984321610afab35cdba70e7539a8120460383d11889645987b070c481a35e713a2db71acca2e9d06046a41dc2fff6d4d9c916443b5b83b7d99fda4a3b0998c292787b65e8219da5edc0a63488a1c04c37e1ad4469a2821bb38dc9467f05b70ca3f26e640c9b14675042f5125c54b060c70b3f1264c61d0fd0479034796340290a7230060b9be7969c07dd2fd7c01dd33f8542c67665266e51f59946be712209e1e8bed1847982c4788a20839aeab4cf35ffea55e9eeeae1eed2fe8566c81063f3fc8179dfd25004a41e41e347012c2dfe66809d1d659bc6ed1e4a0977a229ad8ecf356105dc845c1211758837d55fbd9ccbf6f7de4cf0fa6e9423e2ee376b303dd4dca73b490eec3ec7fc1253f4fc898802d593edf976c9b38c5d1f9d37d0f7e6d5cdbac952664913596750ef5805413101401176be1fffa292bce4dfaa2261300a0041254eb1ff38a75645bd4c1ffd0446a281ff6f5b1bfe820ed69d32dd47f8cb6f81843801e0f2ff3b52c103dd60abbff87388133e294c9411fb777bbc9818e26e5b80eedc054c40282b280fca915e313c911aa5a0d8f0348ed7072d0147c898f2317b2490e16c0cdf0f3065c035724a19245de8926f3647db59850f32e8e29fed16aa27df418ae91f5c93911aa0205a29e9c70aea54db6c261a569a8864f1002e89db5bd07ddf76ab8c00c767d1218f40f3db05d211f027cdf7debaa0e731a95df72381ef094df45cb9ca0ad7a81ae600c2df4977fb722de929c7f0922f41298f1cf79d0ae17a3e46a6e16cedff9490356a5d211cf8dde32f949b649f493065afa31f1f34c325c425132d0b9baaa196b8302354527579ef2b759eade6745eacbbc11dc4219cd6f1a01b9f05f82952f8233485c9f57079107a80aeb11def45b06c5b0c46a3c7040677bc09de93c9c3b21be4877e02f90f01e77492f25260e3ee9764df120b830c48376c86d206b5c0b2a4b4311b845fe757b7522f500e92aa636b539c14c01fc2c20fc956ceff9b74fd087c3314dcf81ff8e24c06adf0ffc669634bac29c1cbc0d142588bc3f52de2881b6290ea00750ecb9b2a9e204d45102405fa58af25c02166482632df5cc525c751ac1873aeaf9cbe6893f494c999b4bc2c68837bafa865949bbdc3c445290f6ddfce018de6d9bc19ec01931b1c1bb918f34755fbbd61f4bbb089c60cf9b14b169ff0a077754b47a9c4945a12ba50830f3fae3d6fa0e199f5a77211a942351979eb19cd1b9d97ad5bd340873fc5c19e7a83028fefc6f571883490dfbf1bcc41e89c0b7a404ae77b6b2318c17813d9f0ff29b1b147d49cc7553646feac11a47bd2a20c59eaab4b730085f06c0e6d19fb26922f1ee1f05328d76c7e11f0aa90aad0f7aff603d92d3d5eb7b99ba39ccfad6ee3abc1f400c2d770b0af645bc652dc6666bb4f24b8c93400179e52862beb6a1254c97ce6e9de196adab02b623ddde97d3cc766173290cded705e1a8e81519cc19836b77bda6478edb5ea9fa41ef2ed53561d6a4bef3e62ef84f0ed339b1eb4c9a5677644b12824557b609541f6690976ec64d7456dc1d712ec6fe573990a63537742e586d07b7a6d5d6b7a4f2ce3db2328905014762c809bf622ccbe50ccda3742ad30c9c4c19895b5681e6f338b92895fdc217f2bc408dd02502f9027723ca9b8640abd22b39a4332240a7634d9048838e0c3f5c18acbd36f6f87e8cb9ecc7abbb8089366a7d5eb6c3d01a7842c4af95fd11fb3c48a793e2956b3042e8fe14bba5aebd16b71cce0d5d892645ab16ab19da271bf8b755d4938cdacac96e64da98c20e29cc4acfdf1b4f56851e2d80e1dd277e1fd57c86d05340b602fd52c8059e3e3d7e90b6ca7bb78087c0d1fefd90b1a17a4db4cf817b33eb1d507a83929f9099c169f67c27eb6d83b85f48da368c4ab28db4a76de41bd9967f55e0fba0a4c55aca8222a3b79b53a7cd58a462f6234c2d210c60a7adb9d69370a20993a7fd0e60f22c9a0c84f3c4b8404f8c09a47f412e9456f8babe3c636f2500f28d49dbd3abe8678d4dc2535af179d4d2dce797e0f26881cf05b0ed804922dcabef989484114dad59acd856edb10b41400c10dd2dae21503072d95f2615451580a6f8e3222dfb890d5dbe27e41b46ae2720546ce8947d2e6c8866e7467366def50ac1f37fad5169d263cff7773dbb35f8b563290a2229df50a8df80b2841ed830a81e21ffa07f2435b0652bb8c51a279e60816aed7bbfe5ea35ccc2450eb2b3fb5728bcfdd1e5357ceb26b597bf2b71872231a136e67c36845990d8a959035456032a63d67a5a30145ea503994e4191ec1e2aaa874b6a029bdd3c29cfe5f63c1694a45895bb0f30e2c0a823ea2fd6918ebd058120d24bf33f000cbd49fc7ab310afae7e6b5ab39dd9481695cc0a48eca57e98a0f6edf52dd99b6cb9a59429a51475077307a907d099497bfe9e821dec58885b34dc62a1199ec80765f70df91ece8dd6a03c839ce9b1ecb949cd63268ee59e7a64b0d670344ff31a0d3fe4c73e9e6080cffc5c19cb9e460334199c3a78bc1b2e2e48ba5946506f7a9e8b870e560de892cbb5e41a8ad262c245c9e55a720d452162b96a66686a6866a6ccb2168b8b926bc9e5e2e2ea2f5a0708a00ac46a535b6538fee632c3bda79cbb56eadad59ecfb44be9071757abd56ab55aadafbdfc9801c9e572b95c2e170de887f250ae191a578d2b860ba929caf52ecfb514d3329454f8ec0411bcd4f9f3b3e7e745aa5a8cf7776d32f33edd62209583542c757bc80191595c4e539d5b696e37a05c6568af3f08e7466b50f24d1c0886542ce34080d948e0b404850a25966035a1f90915d0da63da63271858d37e25d39540e6655e93e156503c009fbf04503c003f7b7982811f76363054e881cf3a205eb5af407bdc8f36f39b0e9ff619ae37ae57edf9db88423346d51b29f413126fc00ea73d98b781049d9376a8da62d29e3b92f6dc6d7ca19ee448edf9c6c4855a67be7f659329eb3efe2dcbba8f5ffbd3fbb8c6cd6721b1cec73205fc6ab5f2b195bb130cdb84e924242714a93dc76213047579c1d23bbcf26f2425cf5c9eda9a755f67a9ce318ca53a9defa9786ae79380fe01d37d3ca7ee536d1d4472ee6c0cd18f4e28b95df21d99b70983c6aea886a391e11ac991609ac6096d6ad9fe9d5aa0f72eef4e2dddddbddd0db7683c4c544a9807de2e2a3f90de591f13892641c15ee63faf2148a1f2cecc333fceea6cafbd6927c6f3f3f44ecd0e7d7ede1dcc621cdcee803755724de8d3224f758aa974915cd3a6c970fbe3dc3e5127e7dc1ed128e2e48473ab0326f3098d829af227a1981cd023e8e6dc52716e7150316ebec62fb3859e34de3c2d736e6ad9e6cc6cce6d13319e38b746158339f598266c621b746e8b284e14fac1189b7308f0817e53c6e6dc01686824cdd89c3300adc9e21c8faaf1807ed0b91d4ce6131a0545c5b3a27ad60a9585733d558bfbf313655e724d64ba7ec935a1dd760bdf66b3395700cac5cdc6a43b0b0f1695158fca4f41457902c40a6a1555b138bef8520c2eae2d6973903c65e5886873aee01d9cc4e2e8ef25f848edaf41829eb039b7332305cee9987840bf89c2e65c0b262c9cd3f14d709b7304a89333bd0c87c3ff40f1d7b83dc7b429e0f838c939f61a0aa71ed88d630ebbcdb91cb4051ff4e05df483ceb1508344bf89b339a7faa2699173386a70a0c2e6dc00ba702ea79ab81b4d687c6cbed0af860ad114ed6d4ce8dc0d19977302d887ada453fdcde449eda7e198885b2e88f66085cdb9154e3882b7611ce4362fb5f151eb508ca3f105dd0eded89cf3b024657171692fdd45fb80e68bc780e8077336e70240e96989016ccea98063730e00345a10aacd391c9a1816b0b03997c26f3feb51284a592cae88cce3f03f321d94cdd2a9fe66a22f9a2af58959fbfef321fc98d75638f598a9ceb5f445b562226e716cce97428dc7ada76ab8754a04b3be3d3ba8e5d89ceb564504d89ce3e83c42bfa96373ee6990b02465e1d8c5b192a7963cc553d47e966271d07cf1543f0be80423d06fb6b0615c13da563ad59f718c85e324cec22e14b8078ba3df3914aae909fda04d127636e74e989ee27678886c90406980f563a2130f28d6cdf6b4026cced9548c6b32f3936322a6d21e4fe954bf9c827eb06773ae46c5b826f425c7454c45a66322203467a3ba609a01415a0cef88d5c60bfa4158dd7fba37f48636e0d3de81f06f1847671f7be4c36fde617af89a16398f5c57ad6bafff97d307a8fd156e8e7f370f7ac375e56aeab9f188df577fe7c19bb3a71ed039ef2ab150e89c066dbc58c85b64b2621dbaf70ea0f70e189b9b717c9a9c01e5f7b8bed19b3bc640d3ba09cabfeecec528bb5bba43e85cba4008210c1146e8485d60eb7c44e0af167d1b07be38ba75f7d2254e8de30df30e3861cba97295ef70159beaeee391e1a43d825150e835bdc39b5466a1ca2716aa3470fd3889fd0ae5c5e124e88fb168dee1244ef5735d15eb6c7b9c456d156c75f38e95d1575e4c4a6b5037988c527a49baf0d07d0df04244514a5a355a609dc510c2a8292cbc1861e2c50701e4404f758bbcbc4005197891818cba455cbc1c89777777bd07e70fb5226887eae88bde689df87d80d8d92cff77361d4df1674ee8abb808b63f5175d152151c8a8a54b9010de20e2fb943fc886508b14b0e3b4001862b2a7191c312e10ae6840653d7284912499e4cd102830371050cf08570922cf0935c1159052076eece76842f00156adf50a576078ed73c6ac307e23562988cd371f8bf19a7c296b638951fd6f0135446a11d028f69b9bbbb3b33bbbb4b8feece746803407c77976e7436c18068d105b010e5e66616742bd5b12240bdf26cdbb62c7b53f7e33de53f99fb28636f7a1bf9304fe427eb6cf8f4dadb48e0f4da9f3a1b6c03b5fff43612e663303d8ca9c3ba184e359cba205ac79265475459fea8fd9a8f8f7f5201aa9a71369889b3c13a096c6f83fde989fc642f01eddf068b61c3be86adc3ba186a78223fa66f55e5eefb51650744556568cf67f061ee6ce4f3dbc8ce05acce24b074581d56c27afefdeeee1856dff9cbe6388ab23226d67fb9fbb19c0cfb31b4e72bc38f0fceda407bfbab045b315a051fcc48a23c3e1ac6cfc3a30b4cc6d051162e35c6960ef55bd7567494850918b78a8b1a8150e3df88b2fb9b257e7eac5facd84709b335a2f163e499b2e3a992072e9892648b4d0c7376447ef6b7bb522ca6ae51932c6a7758f74dfac3a2dff415c4000911484a29a5921d322600e580a48909905026a07062d54f7c71021efebf091640a8b6402285d0a2c3115478d1610b0d3a0a2962600458e504592069a2a5dea8a1355ed7a889154e68cbcdcf809d9dfd3d39806de03e22bdf1a9477cc901cd1ff9918ba1573ff297fb4a50bf10c0daaab1f3ee2b410cf4477ef16d1460aab103414c855d13f9eb83aa7a9fe655c542f3d7c10d5f885054b78aba39f0c2a546098f66bc25a32641ac24f9e32f6164057dcc43edb701fa98090f7a4a6d278cb8a27af7b1d26c2c5c8dab803210cebbbbb8b104b19cc548ed25b5d7df37f04e06b86dd89e2dbb535d3c3d3d3972701c8dd861a0f3d99ce6d14259e9a7b99d6ccf3a8abfb8cff6b030b03dbbcdaecd699f9882148a39c675c03b1bf9fe1fdbc82e067ee8e37c54bd93b67648506f43825f4b010a9d2aa03f54a420b60895795136ecaad50a0a0a0ae2968cb0b165a32756b539cb4b9be29f29a8fcdf168157d4abdc3577a94c4434b9b5571ea0a500853a11acb3ed467887e7119aa4f22f2fa9fcaec3f6cce7ef2ebb3337c56f43e5c9df4a73528a4211050505316fcfd3c43fb66e3db55aadf679797fff019bb31f143429ea9b835ac789608f9f6750f959e096f3cfc9cc44ad266a2566a1ee100d910f4d6da373569f1dd42dedbf0ee22a9c72c94e36e5bf4d6c133d3d3972701c0d1adc921dec5848e3b8a5853ad1f6c0f7f7d76a402613b210b77a4a7bfe454de479d0c32dd53f460ff467d51e507b9d05ef2cb7d1f696a2edc1de86af504ba046d002550595c269c81e7b4d4a295fc3ae547e2bb2d002938f715c8922514f691da5a5a6e2de49d5db4b94b0e5cf4cdc280653aaf313db93bdbf133551018a7c9f4062b985663df889ad90fbb88adc42f9e57452a3d5b18daa176d4e134a5390424d2b45e58f2888c8777835dd79c5add841c0f49ac641272a47e4478b5c07606783d9605d0cfd3df810f0518d1c1f55c86dedf721ac6794a0f5c3ca5159541657d2c6e88162bb9caf529042b3ba3d337b7eb66103857228fb5dd230bb4e82e529e69ae81f5a89a0cd518259add36a9d1d6a1d9bfd45d23a41adc384db7699d59e867a42f7d4a36356ebb8bb43d6a232e44056b7d005f2d4f2f2b28fcde1ef1e720be5b81fe887aaad04a891b4cebac01cd81cf6b13dfbb13027a528d47a1116ead7409cc43f27a528d4ffe7f1764db439fc0ee4abf6d8573bfbc395c50de4acd9a720e452ed3a84973a80ba46432c51b5de94e6dc02d63e3a3a6a94114d62a72c22e65ba8a7a8f9654e2527aa44bd71059402142772c5852e872e560a4850fa7209efc415f32fb3b4616510db637afafd72b53dbcf207a982da520719c4e62ce15467dd278be20bb0ee934ac0ee93466cf749225ce82a579bd38f6384d20eb573ebd591efb0ab8196bfa0bec609fde2172d58c787faf4302fabf00ebd82b282816416b55f3e219dec4e5c49a3f8257e9142dbe39b66caa0cb7768bea78ccb559d88a6531a6a1da905c3749fdcc2c8897c2752147b353a510d117465d8546f922bf90e7520fa8fa2be4936d4fe5e6d8f33d2f6ecf397dacc456dee52794b6daea2f6c7952bb9922b05c09536a73fc6b81e520eb2c0d2f289dc1c00218b2bf90b2d9dd48e4ad854b752f003900ae04a87da3648a12446628e5fe2176050a9fd722585a2f40ec792455a678dbe78523b4a24416040513fd94232022308f5934f3e0954fb393252af1118536a7f2cb283fa451e221224107018aed43a6b34840a6abf7b699d156abf73f207949f4f326c4ad0fd4d09caef10908fc3fc99f99898a72fa3c37a0d7b882814a548744e4a55aaae407b5df3343f239fd854bf14b5bf0aa5ba45c21c6a7f0dcd8c944ea4112a0959446dd9fa47a1565f6aff4a0acd2003735e6391f65a0ac52f9bd32fc345256c0bfd948b602c01fa63b818c6b2d06f02a211287e59b9746a2e8af6d2aa58eb6ceda56269b55ad9f0b3c44a11205793fa39abb6890913f57325fda5f6c36521b0d47e9c661254fb864c5d1a59e5d40a75bb1c75bb9e8a6aa2ee6b6e05eb70506bef4af0ce6ca24af52bb5df93f01f76875b9bea4715f1b55210b5df83b647e968624747be33f30dd37d936edd87cae487a25dccd73e6aafffeba30fd68f839edf63a5d78e5ac7ad60cfb1b052db7fe8209f93d25652f220131065a1ae4382a600857eada4a4a198a07bea74a05f077590efa8828e1ec0eea9566d0f7caeada4a41463ec3eba425dba297e5e4e6dce7e9f3a4606da5229a59473e54af98e75b283b07b50d5d6d322c671c58afa8227155279c1120c6050210cb8a85015a5385185922258441942f203ad547845cb912184b8c1d5c5163f108a20c85042b7f20ed3096416355601000b02c1aaf04a0ca290e669fc385ef7d57736528ffec5213254fb97e3f83c1e0742fee6f7719fbee1a9452aea1cf8e112c40227f343082184703e67a6ccb565779ab529ffd9858bea4d5c2bd81d6eb50baad3d4af5932a8fefca4394a7d9e2cd45e2b5b84d94eebce6404ad47726c38438d147600be90b84e57cd1f0ed315e55ff72c4d68821236b129236cc2c464844d684213fc1d09249cedd1da4d0e3388b33dd3835128f4364e1cc6f1f90d1e38aca3539df26a04b039fdb1f3ce9398c4a2d79ee76d4e1838dbb34daca71235e8c8448db1c6e78931c6185315fed60005157ecf18638cd1852a460405810809546cf9899da5c62d6ac46a8c31c61b75594a961c434028e68c188649214241d7a0803c157d67a5e840870844d19212450f11e081d2cb2e2855ab8abad862071981a32f9dda5547e009a04e6d8da927ba50c20a5350d30d01d0820715b878f1c30bbc60b202165e144151188dba454b10a2c2d6124dca0ff55bf85ceccac085123ec16e0e413b104695dcd9f8d8caffc256eeba0f7e0888549f765a5b6653fd348ab8d40c28e8477d053ce79b9ea1474c4e547b0a80d5d4c1d9bc5009e324f562bd39243ec7249ac6eb0ce34f7f08b963b7504b11df414a29e50f5030821720615151855505952baee400042b60384141144140c8a658416b872794ae8851b4608a1e5e43614bc056498e3046080097141900200846528600c011c5ea1a4979c1f4a72ea5f420a548fdbef20c623a5ef50e54edeea851942f5555d7e88820542d46b9c3585aca855a90ca1f3dc98e91e00ffdd86584c414779ec244acb6a2c2671eaaa8f07dc0e7221d42d4f4df9cd23aa687dd42609d3f613f44a9eaee6024f6e13bbcfa767807d3c9ae7321d67e46e21dfdc3e2800f77d48f5d7cc5d57d2aef3b0c728783f763020ee3070af69f10af3aa0a08b650f5f83f1e32e60ba0ff57468534b5424431847da9e609cfba60386f43875db1eb7752bc4abf6262e6e409b5ad60cdad4d68a446b53bbfbbd9bb3b51765377f92be2852fb073e77c1406e6a3f3f7f6b7d6ac23dde0d55054880f3dd78ab03df91000bec7cedfaf2582bf575f8e985e8b54ebf00ab8f28b4b89ec9b471bc9e694d23a830fbc180f6fa3ddf89a153bc43015e60e8f6cec7e6f43fa13efc7df80560e22f41f14585eac1962da4173d4829a554819722546104282e20c2059d2404138694219a8a2a264802bc92859222f1e33671c5bc1206108e6843b8c088c88a14459cc00737cc2956469a8afabd0f2d1e27bc07f55b24a4badf2a1da1a56e171f688110aa68b9c1122d48e2c7b11c61a5ba472104510a8660a2080b58f0e3af4ae1080d848c8c8c5870c38fff0cfe85d6353a62ca1147645a767b365effad3d7f17bcef5437e7f48257f7d30b587517e00bd9e905b9dc524a97129a60fc7d7a6e4e3fbf8908b46b3f991e802350adaed11142d81b1d11449552055a943c95f24265ea1a1dc13a0248059de8eda671bcdef13ac5af52f114151d1d2121c9d87da1df561f7513655c0f821d4542f977773b082184104208216466ee7677a1bb37ef36ec61629cefee32fce973be4318338fd9c4a07c87d9c4645c8a02e5668f2d73df56b185fb37dac389c9fec9e35aa4221ecf3d1dde411f150f8ff612f0791b17949188d062988d8cc0a1327ba89bcd41fd6eaaff5052752f0249f522aaa83b447ebab97768da3069a8e7ac1046201f9610621d01a38a255354f80fb1183101054208e1a90796a37e5b0e8690d0208a2a290a5bd48c410aa0b8e0cb1660e030742503362842020a1f562084112994cc304b99c54e477bce037a315a70a37e5bc56680a562180d84a818851edc2a5b2c11e40589269696fc402427a298004e3a37fac333685529df291d5aad563b1ce105cca473a3d93eb1031dac503f1d15f2582a589c7051e14348045185464418c9c0880c7cf004880a9fc7e7891315be0f1fd6088aab2655da3a271acc099d60590192a7763a1182110864594ae221b1850d50758b8e0045b5d28c8c60c1beb0d59f856ce4b141961bb22fa43d66e6863437ff727333fbe021419613b2b44186c8204286ac8fca0b32a48656dadd20429a0767dbc9757f743e8a509cda36b406d55e3f1fd12bc4c7d6bf0fb70b226408f6dfee90d6599e9352f6b129fe211c7bc41844c8108fa73d5e0a524377b7c733a4756a689d8f37d03142d426e1ec7791cfb055d7263ed728e37d5a4b323284ba2e33011b5a49467e508178980c34b763f4fc966f0f4de5e5d5af9432cbb239b1971c08b03a1f62523a0c1c06ac7c9a0f8ba8d4dddd2625e0869658fd9c60e0971d10afdf7cc871434bac6a9f1c306374b2f380a972c7455a45d0cf592c2424f41d47696f635ed39eb34d487bcb4166e64c2205e140b043b86e0b5c5ea406b3ede90598ff1689b7188eab0d29d4a789ee2e96f6564224ed2d0cea20ab3d56dd77972bb5a40775bdbbefac2f4b4c7e7ddaa769e6b7ab756a7ebf5bbec3937ea8cfe3a11b43083f20f3634e3d32245de602e78145aa1d8043754f351d92b38b9fa6fbe6ce74dfa4b4fb509eb3f6b2fb78ea56f82d1598d36bbf9d7a9c3ade3856c241ededdb9801fd980933691d5eb5b73f817c27884dffd6227d80dd656dcf4cdd7724eec3ee746b53fbbb9f759f2ba9fb2dc4ee386bdfa609f473d6909f1833289d018d988052f92643710f399cbfe1792b6954fb655996bd7c9f6ea19d936ec6dad4b6507b926b26ed2dfca73931804895add6e12a65c74aecda82ee774ba875be5e62d24f5a67c906beb7eaf2903c3ecc25a04832c00717ea4a5ae7e101647436fcfb23e3f77765644e640c3fdadb476a269dda8f5cb7322c5a75f96568cf480333b4b7fbdc7dde02121894df1ec6098912c3a694d23d761edda5ecba854bdaff407dc6d8b967d3e1cbce3d7670c2d8cdb781e653e343b8df8120aafafd00a26a61b98f85baec39eac28f4788ac0e39fad9cc1a5ba8df6e0fef3e8f70dbcb511bbee6de428d1f2b6cd96d1c39288c4125a0220c5457f8f0a8fbd204384cd01d361b6a7cb871c2232a61c224e878f1e85015c173e2068e14aba3bd8552aada833096a0db9768eb1be37f83aaa11bb682083b1d081dee8e7686ee0edd89488e476032bb2123460dcd8c0c8d81396d5a669a37ac563c28254d89c51831d61833bd4f9b30e639e7d6850959c4706664680ccc69d33008fc40a2800c491998c105a4cb15083119a974dca360e3841b9b1a34503364c4a8a139c26225090a5ad26a11212464c4d0901244444d141555393aba828464455252162e97164a4a4e3049e70ca537287884224c663c37363568a066c888514333234363606e58ad8c00011d61b19204052d69b5881012326268688a92942c624f63604e9b86fdf8142dc0c711047e20558007ea42289b586366d6186a52ca8e060d4229a536a1d4349865d9369526f59245ccaba19991a13130a74dc34c33d6d025284609b1a247c1c60937363568a066c888514373c36a650408e8088b9524286849ab458490901143434a101135515454e5e8e80a12921549494a2e083119f9f52f145e365e27bc6e5e36af1a2f1a2fd46bc64bc62bc6abe645f39a79c9bce82be605f33abdb697f632bde62bbea494d2a7943e6334b9c7d8d160828ec5d88ef1438fb086eacf32c687f26394cf9551e093164c663730a74dcb4c533260ca528cd221364d28fcc7e3ff3b00f7df1675ff6d134b2576dca360e3841b9b1a34503364c4a8a19991a13130a7adcad1d11524242b9292b27069a1a464d2dca4b9c91402697a8dddf421e82ae794262d738f99d330db7bcbe0e61daf87612eb3cfa64f475fe218260a26b3f87abd5eafd7ebf57abd5eaf215f6e58ad8c00011d61b19204052d69b59e2879c164c623be6cbc4e78ddbc6c5e355e345ea8d78c978c578c57cd8be635f39279d157cc0be6757a6d2fed458490901143434a101135515464456935a4a3060dd40c19316a686664680ccc69bb61b53202047484c54a1214b4a4d57a22243444833324209df01f8f8dff7a78d838e1c6a6060dd40c19316a686664680ccc11162b4950d092568b0821212386869420226aa2a8c8cad1d109ac19f9de29927d111ca8093c8c307ea0c4c741f50365fe37e99ae69ca1f406857a08461631c905f3bc1c2a158f2e72c5ba2123460dcd8c0c8d81396d1a669a37ac563c0001c5e0998586c9ec690ccc69d332d3941590c115c3d80d3730a74dc34c3332c0c789a50e8a68fca7aaf15f0e9bff74dcfca7c373635383066a868c18353433323406e6b4699c24286849ab458490901143435388888a6cd85484c92cbe5eafd7ebf57abd5eafd7ebf5ca5eaf9764254a4b344c66386b9a7386d21a2854f7bf82e7e550a97a78787e7c8a16e0e308023f902820435206667001d90076b4c264e6c557cd8be635f39279d157cc0be6757a6d2fed95bd4caff992af1a86ac260ab89545ecf41f0ae6bf1a31ffddd0ff6cc8fcf733ff7534ffe1d4fca7428cff3c19ffad30e3bf1ba8ff06a0925c503364c4a8a199e17a78787e7c8a16e0e308023f90282043520666d8b86f034a34045912c1901b562b360204c44798c54a386806674f3099c535cd3943690d14aafbbfa11487344cce68ffd56cffc940c5d7f6d25e3550a8ee7f054f7239542f13cbe70c9a583e67d004a18931c69e1707d65d319618f6d8f27c3c11091aefc8fe9bf03f53fc6f9bffc198fea3d80e5ed39c3394d640a13ac6c138fa2197c734886910c3b48c7971ccdff9b0e7cc76fe3a176fc02e4a68b3757625774787f0e50eddddddddddddddddb0218410368410c2861042c6b1ce45dbd581d07b4743b82b3dba943a107a775dbbbbfb61175386685d232f5a35092b758dbc4842d535f2a2c8b99bf93eeddc56ee36fe1a6817f3dcd1ae06a89d1e06a6b379000dcdcccb3c09683a09c8689d4cb73f9048e537c51334c524787200063f21b0deb4d7031802a5375a68ec4e0dab68b375761f528f54833176b32b4177b300f573a5947008ab1c080dad8290c3135aae349150828014456b28b5f2c5529ce24a500a6a50b4260917248104126134667d2eb62fe1c77d47653bf3bda34e3d078d7f5de030ea6eceeee6f4360cd31b1cd00f35e1eac7dd77329e1f3304414914b7ae11e1f620b07824f49b3b284f39ca860a14109e6ef6807e1e8fd6b9d13ab1759689708344bb0685e3a9183e0a158361ae61355840b1179a4e87e93e140c46f331cc3c8d4cf779357b98ee9b35bca7b29fe9b8209a51ad63de61defdb5a3fa09e98a3dccc7701f911af37dea11f31a9f5c680e7b98a732446a0b8995761d04c6015333339853f7cdb9751fd53a1397753b6fd4ee38f6625454b2551886614934d635222af2bf0423300a2459832d7870a4a3f84f5d28e8f71ec70382b40653f4c0d81596cb1a1cedf02fc591cad403742e3249f7a6e7993ac36e4c4079e3c137aaea0bc0fe7e00c87e46f9180facced5d3a90767ee9ccd1b5e53e1110015e47cacc33887cec59845397fce2e4ad3ab4c3894670df40ab7530fd6d89db9adcced796d08bdb339a2692b6b6b304a6c9adaa3c466a6c9530fafcd4159db9db92f46754ed5ad71a76e9d4f067f3042df1cc6d8157e9dc39999b48dfb6454e7beaefa6b9a6fdb729f7fc3f63e186fdea142a8c44cda688296d10c0000044100f3150000300c0a870342a1509646cab03e14800b739e42745a329ac6b21c4761180419ea8001c010430040000666a4d100d0df795064311ead65739644e40f58d6d7d089910dde5a6450c003bd139a4667ee089c9e430be73a8ea1522c421564652a7fe88484262d4c7de3442e4ef095221ccd1477b1c01742d59e3aa3c6e52e799b1cff4561f37e28ac7fa8340ae0d1068d3c4e120163cf1eea548dfe0bf4beb6593d54af47777ca04379f0fec2dd2023f1904834a15428953881b2ac036fbb044643a4427c471ec9e520e7ad5707e81ea4bb5ba0c62270eea9deb49f2f50599a0e9c6d610d24017d80d3a1af5301defb70de48e57814baa059841ad02f0f1d09b6542fb7009c8d5a04bc01bc8bd6674c5c8a265273a75554ffbfb4fd99a50221497fb1c0cf988a351ed7adecce5f0d696c3c5a6d394f4f1b5f0caab64af889fab749fd56ad7bd06a8b72d83b1130f942ea13c1a34ecbf1812577ca9ce88a0df98353b6bf924231a30d45ba4a4154174554da53a0e06540ddd5bbd80bda6c712eacb2d393e50256644ca044672ac681d5721d57a3d2b36f5405d131f362d02d360f7086530543bdd056e6eddf7109841946237ad1030cc04927252ec439263842eedde300f25ce859e460148ee615029f20850064bd01235ede6813610c8e1b4cabc0b2d70d78539d0182fa1f40e5e7ef2d82ae070ca3b048407507d8f2e7f7f9068c31a46d093d8b956fc0840cd5ae720f788626094a750bd69c9ab65e7b8035c270dd85ce8e1f1576e0f025819324b9ac586f30663ee34bd12d18f376b7cdf40cb4ed162c0d75acb7411c4bb89fd10db699bd050b32b46edfb6b6070ce27a5c241fb61d386cf492097ccc059af8a3d4e4151d119235a768d469e6e1d565b567de6a635441691c91e989be5333902c92ec5e6cd308b17a601a8b6fbcec7c83d96cbb208671c05380771a8d0b0c3fb01ce3f1dc746f70d426ba00501d3f5dec5202669d090c68718fd50413988a3736373ec778e79210a88896951cd1e514514a581fab28c82f151e4da366e94782f3f1cc0644c571be3c65830a381fa0364adf7ce615553fbc65f8f26a72f4d6fb6c8802e65f61435ca39db3f85d08bb1f76ba6c8c3e0712a57d29c52e27e3dfb34edbc418de55a8696282919a75d60a099e5817c4d81b227c19f827ac932a455832c5bf4be1bba015ab1742b8b3290a2e9b9c2a7537624ee69173af4e34ab5c49b99b3acdf78586521b7d628addddc1a36859b8f79b8217b781c8f9a4b6b5827b9a1411aa811218fdc68b5c1c0678ca25b5081c0e3152db45eadd4e8cbd613bbdc991b758dd7a5e765a9b0cd76ba891f5c209e04be9196809ff53ff3f22632d9b47cabef33078b50740ed0f2a020bc8e1eeafe34422364d82264cc67994897afd3465dfe7944a7bc6e2a997e58ef464802fcdee263af90836a2f6456aba9cc6bfcb329d99f17daf7ff2a909b494ce5df9bf31ce92bbc7ccb9e732906ad4ee29a87541ee6d76349eccf5c9d7c0814262d4badc4123a4c6456da52462decc5c576d7df7858901dc2d0dc4e88c8712637cea1d1c82ade48f21dfc7ebb2e7e574b54cc80eba0634f80db44d8dbad324502308500c92e8eef143c24328020d45474dc3a0cc2bd01ea2880f1be68222e47278a9c6fc9382b08621dc09b4729b04c0f0e63d240dad460d77ff87d4595350c42eb728852205a312d2d76990c9f1c8589fe4f5b130e2194834cdcb54bb388c7f977d7a138e3d9ff8d5caeeb69268aa4b9fc0d43bef0c71c3dbcd5f1624ca8388d0b2805287b3f16fd9a66136baef238a7c3280add275d7ef67c499c4f69a30e7bc7f668c2bed1f33779ddbd78c0dc26615d33e7df328ae240e8c8fcf4ce12012810aef8ff1cde2e74124317de6ca06c6e5bbb7a182ad3c47c65067bf7135b1ce697ff70979407b833d413c20ef708f8007b0371c9e8db0f939ecfe9e184a5acd59e91c9d118338d091aa496bc48dd9d280ae2e67272dac33c635f0a1ba7dbaec6ca50c351344113856a3a59b731fa4667cc076c0158307c29479f835bb30b24c4523cb12808b0eedc8e31e260a18082814243048c902d9502516e584269c4fc8e50a923dec5ff2998a842f9ebc68a435962aad1b0ea76b7e793a917275110c2741018cb14be13ce6526387427d424f1e13ba53b4add431c9349f50b3c61f3d8145d6be94aa1b38a8e89213b584e434a1d253546f1617772f5e838c071b9127e40ace55ff197b06f35d83245b85ea3de48088a4bd2c4597f3b16fb3a7c36cec6d56d7b1ae3ad5bdee3ad791f54533837cf5a69a32628daf4f4729ef062f0a7dba733b7c8eb1d214936c081c953662ef163e7f0ef3ac14ee73bd0ef6c2049e1b0bacc8f69514a06b623842675d886316682a7fec877e8fdb7e0ed0eac30edfc049b69a97f05533d1fc3b387705fc2492301889e45595ae072f3c8c7eb33e420846a20a5c82b23668940c50cb45c23d467fb1ea415cba14f47f037a70ee9f8735bc0da0003facb729d6a0d0c8ec68413ee73e37905d0bc6de20c572880da62e3251ffb982d41c8f2e90906532fbc2d2a7d25c6747b0511a9653a66f7ac49be061bed33989e6f61b6c90dc4be5bdc3be29e1aeb4c1a9ef16a63e094e6390511fc83e453f3ea7c9c69d8e1b4b8eb7185e9db238b3ebe78810e2eeb7ad202bcff13112d149f469f42474bea1ab1b469fb67a6fa51577e5b8ba236d484af528b1c59fab24742877332ee36c2b08022aba9cc045685f7e274b3ab3c7df070bf839f6f3aa7f978549c77b6daa1de3baf7c5921f4386ef2ff34066265667e2c45376700089aebde43b96749b8e7fcb4a5ac0119fc22ad2d841b284f0f63f353c88fd675a0f69aedfa8c5f655b6caa866868907981936b27d7f19619686d503d90c97178534ae06ac93a59520ba355be79ccd1cedd67e15497a3c997c443ec9658b23f1b5f79164d52132b29a612c6723d1eb5ac9b8fa9f21e760f1d2c142b937cf45b41e068fac75015587e3e36fb249677e54df3fe83c318075450f819cf4f4b843a55914ca0da4ea296f3f1ff1a510220a5d8ec022b45f7c97acc9a07e477da97c98ae1abe554431dd3ce5725b9888549e545ba8ab119cc04468b444d5f11ddfca9cad6e7c4eb6f3b9105d5d57e996b4191d0e3407e46d068b36518a1d6ea0a3b52f7d87251df3a36fb24a03f7c5db8c5c6302b94aa5206179293f052dc9b3eb7aabc04b4cbb80848953ed738a210d7ccda514ba9cc1476f3ee61d96f4ddad8ad5514f76c840e205457dee2ed0a9479217e90e66bcd8c31079dfd0bd32111d8208e3091f20bf4f6f4332f128b596086ad7af5e09de29efb4e7c07e657f098fbcd34112aabe7b9fe07accc09279f643e3b8541f16f5ba4f32019bcd54c1eb531607796c2d789e33ec72c23216461ffa8ceaf9fa3d866307c0a8f6e8ad6c5a0b1de239f2ed0d923623fbbcb1dd3dc3ab19e1a610974a85f73b73ea315834fa5a42b2f7f88eaca71d4ba180ad2ad6c5c77d213cfc5d4a8cb2c3e633c4f38b0f9052ec8ec2fa0ec23087d612a77d41a8ae25706f71d8146c30a9276ff05138c6b008cc40ba9bf70f9cd2bc6ec67090291a34232045387e6d696d03b96703894d074ec635d0c15fc9cb736850c7ff4f6b98685ccc06e4eff2dd1026323aeaebfaae06e2b6633b998790b4bbadca9fd68700d9338de539cc53839f6a9a8b721d880bd8d9fa196dde968eef8e37297c57dbda24f3c4def97686c7350027db4760ee218dbad664b4c05b1735c77cbc73b876000e0dab04e5c7a3b34a60dddbb1a60214d874e29bba9155e5cd1475faec963ad9889d4abcac18a2bda43ad971332e3cd0f961263b155b63b19f6689acc082ec27d2b72a38bb609078dc85bd576045873172549ad4c600bc0e1706b5bf3426dc6ca74812025330451d81d8dc643a16817af9e13385942e3c6c0de4302501a43bb027fe3d18b6e156e19aebfe6b0bf6e3bcfd181cda0ed654b92a432ba0ffee05f613fc4bea680446ad040f6d0a2ca458b65c050769f94b1fd0ff492b6a0b6fb50ef3bd9063238646f96ae75a893ae15709aad73aacc2b8a7b8fe44d356fdf879ec319ef9985ad49e17cec4538b4e00bfb2c3d8495d1480fd1f1752767a18ee8459454fce06c8b02450d6dcf50dac32e951453b0c98e1fe7e2e3de61566cd3079f4bac8fe404acf07237d0406210e2f7f800dc2534ee689f48eab0fe0af6fc8e60a821759fa76383dad01a00b11f260b7cbad6079e05eabd455f88e608c3615ae191cd60727e7de3b08ee483d60689edf9bb43f765e7e0c47ffd1047d13dac247f1268d2c8e17a765d6ff48fc1fed4b3bb443605a5ee55b42d54464497785436a0f74fa4669dea8b3921012c73af3327dc75f82126a68fa36b0e8b059e0de4009fd6a20d715fda45227b9c1d82e37007702c52cf5cd929535a777269869596a7766301d9a734b98e9a8c8701e3e1711199a014feba06b91cba96b9234a97e3fca33b91d122db83a8d142202854ab2a3232761483f03f31df63de5cc63b58cd8b61252b9507ef8bddb540dc9d8b776b8b3174dfb65a9d626ab4219b01b1018d9146f59ce21a0e7e386986366c79d2552b165518b868187328943d20724055a8b1cded27b78c287b9494110f2e8abfc61794176ce0f999953ec14992ccd6cf66c85b5ffd798751f23458e4c177ae9fb3c30bad18d0ec743b40924b4a1f2b6e89250695e6d01935e3c0dc9ce3f2c61b062e739390cf475be0eccdab1655502b070949a5f499d59b9ac08c19cdf28c1850e5d2d072bd0eb64c442f1810fea0762972bdc05e69f2dca7de0a19b63f62c1f8bad0b49ea1be7c963d85f51195ce9f1bd0aa38a80b89c22216d3e88e7012934b65db8af45671f79eff83301efe623453172d0ba0cd103319619761f18fc4e34707d1358c32015e5b58d2e1774988b84db402d051572d3f9497e57ba965e3e3cad30a43a1f25ca25b43901580108d3e6170ce01806ae1a09ebd08a04b3e1169dbab9c1aff4cc3f15fc8e5e0eb16fc9ca8d0cf9fe54a5da107fa6daa580e007636c7146ebfdc2ae45a23059b58d90f203b48680743b458c6fa916e3a368d1720cdf07855e13bd1f53e9b0853754827d16f96e2d2809ab3cf1bef14fc37c0d36679f5a0ffbeae887b99a3959b64faeddd853285608e2fa0c9bd35febed6fec065e0d983cbd8c187924757664a8a1a52b2b8239682a5f8d825fc5d821967a86390d3f67f8587cc2a46a274bc406449f1052635c7680e4e9e36f918cf459759ebe40f5cdb278094c7a3fa635c163f922471f31902eed7d9e3cde038abad625f7da1ff2405cfb3bfe5813747a1607bc223ff872c27663e9c8113ac4b81c569b8adb78072b471bf497600deaece3631666a400f9d1d4993bd3ba4a98d7598443f529d7b2949584e26a8740af14e66aeb2974dfb2908a269e07a87a3b6d1910f956deffaada1417e463f4834feb0eba9bc4e7a58344bafa5de615bab8fbebd6c604a6c28f82923c423f7b18ddbb6c0d221a0839a40a9884d694588150112893a8a72f7c1b4984fcc1f0b666d4868b0b6f46490710d47defe952a20bbe4d46929a59086fd39989c6ff4e01f33775112dd31f0a1c6b9dcdd8b1e40c4aa7e3578ba4d29742bc796cc7fff3dbea996345ff02b705e253a16c7400479fa29d257c9024a38eeff5a36f386354167a59e86e7d04bd74238bc3fc1c020d8ef2eed09bf0c5ab03ebc5dac26c2670a53b3545997577faa1e952be4843c1d474001a87bb15bb92d4b919809bdcaaf77a2427b0113d4248e67253782de84f09487167295a3aa038e9091ea44ebc09b7b2e963b73bfb07312b55255e0777fb601fbf2ad561399947d7b55b36cadfe8c39eb7f07a606a4c348d4dfb7a7fa07c989f72739c42e096be1e0d3c35525f4799d89fd26273a32b7e1f2a7e01a83b7e4029e125218276b6e5568d97e7d5f78c449bfad2b66c991452890e2ec07bd555f5c99dd50e59212de51211af6cb6827cc6f9b17381056e433a18e4f3c132a4c6fa4708fcf3078143f80a1559a6d8b9d10a0689f4fc61425db27380b00e6cca6a2b6047de0ad42af6b6b7dd3adff083c0360a6214cd9e984c8f0a48f8bab19ea50a2b93d04bd972a3a04c05abcaa20e346732c91b58a5944c1505ca36ebcfae15ad9c5c0ec3f3cb6acc271be14d2927f4afa1282d5fc7673887d6c08a279152ec4292fa2a7e6a99bf32885ef541c4fb45c9d15b181c8511ed5c9d4e44d8b656374146cad1bd6c602b892a816efe15f4234266ea2f44d45a8abd90b06e54939d05055bec508c5b76017b815c893115f164dc0b9432d6417efbd137f214c8b4370813d997f26f2f0e8c44ab8ea402d6defb8d32c1879aad03afdc204fa3b03a8ad00baa06f5196b4e0cf582ba8e2c110856ff46edd4e524583796744266489d9423e3782aa9b25c515402456613775356028df4b348b77d9fab9612c22285022b40352346627a1388015821ea8a747d8ded86809e215402a9017250fd61b106f216d465a4c6e822f930ae7f723dd1c57a98dd91bd0bc3a94b7d01560477c0846323eba415220177449bde08ed83a2c3504e52958936ca2639b5d8e6a71ca5669686015f1c645e520dafad39addb64f3cdbc510127c4e495b55201d0afc324cdd87093bd90b6f83d0e0404a25d82dc0ec66a1bf7b601f80f65bb70edbd5edfeba657cb077f4969d7c5e323f5719a7bfb8662ac24996a8f4ff1a8762f057fc9211fd0cae983efc840ec734e605f5e6509629b18e64620206d58253bb22bb03f480e42ab31a796e0f5c1ef57625322cfd184843e6bcc2a34c1f58df7222a7c3790433b7b6cbfd47646155e420cdf42dfc1a27c366d1293ad92df4a7d01a81cfbfbcc98a3de66d935c8afe64335bf1480a8af8d443af41b0bfe830b940a718fa880fc74a83a85b7952e907385eef739858601124a21e0f9a4c898b21e04eeaab7c59315e9f40da080a04b94bd2403714ee0caf44d35319f9ca87e1a6bbb5f7810a100f957f95485e85713654d9fed3233fca2ae425c10475ce586bfbd55db18562ce13e57a293b11411363781cb950e8e70e38012d6c02e4f06251476c14a2a30e21f8d950860e20028f8bab834f1efae6c6e81c0ec8f4c834273398f06eac1a065717b58410a03c109633270851a1f7636684415fa4cc530efff811b7e2571760fb11ce09e797d0e599012013e376f2e04c6c68d47cf077ad701153f3ea2fa478348e50fcb2e05a1218e862bdf9616d603a8fdfd2adff361b9fef6f8b19edfcf2c1dfee1fc8e86ee47573d83ac16d7c871877bce5751bf1fb09948bf858d43d70f96347b052bf2054008810e84037daf658b41a34c7fc8678abb1b11eb37b2de8f8389e05bcf1fdc816cc736219bae92a531fdcd671b2ea485b6e56463d81ffceeaf34ba5062eb537249b136b9564b2a83373bc8e934bfed3d8c66ad83f0e5d851d6cb23c18832a22f4ef93e2641ddf13c8791f06cb2eb2017ae72138f89fe1c85f2e57f2438348e6999c0ddd0cdf5438622640b6ee30796a4b4d4fb517e425b1aa9de428eb56285414240aa00a418661b5724124e29d1f08af8f6dbc8e834f574e6c085950342ca7209794a794e14fd583f4c807860e87029112a7c58db8915d116499f0af0a1cde3867eba69ca6fec51a16c8bdc9e750c35181d4f7b54624dc6692a9e083ad55f69109ba79b2e5a7c8706b0eef5df4ed13f955833c4d9949e2e0d44894694540fff8cf50e253a26f4c094f4a176a162a8ba2be16d894b1c4dcc572d01e04e8d3fa6c19e007a0886600ed5e5228041dea74138ce05528cba891a70b935ba81b208001a75e29d2736b124a85a6ab50e440b147dcaec352575167876bb07fba9e06f3390bf2e46039aea0188f8653456b810805cd2dfea5fb7bc626194c123e97da879e63bb3be3b830c86661dbc29d29651e32fd9ef156c95128652ab3a47c6b5fad20fe61d18d1220bdbf6ee0dedbcfb5f549e765ee92eaa151908ef72b29baed959920547f96ecf38fded3dcb668c9c703d66f37efaabc092fdb4594b46b673589c67e4df6a2dde5c9525290f9272ccc963edc4cc47133888733c4bcfae643754f248e984f476e63653a7623808c32001a191fc4ede45b04afa77da9e2524d9b7e62f44bdcf8820c421022116874f40f2338146e3137be335016a8271ba53dcbae0485fa7a147dc45253e19c2995fb68b0385cfa8ee0e7b51b0075bd75d651fe7d3ddcd948edd303d9dd149c607b32d0a90174e7fc22f4828b0403040a1d602dc520ec239a1f6ddf523322c9fcbc2ea02585f92dab2683442d7fc519df92e6f0b0a4925c5224814128538ab53962f56acfb068e00628c54edf44a2c23014629036ac7211e07919f61ca1ec7bdd6d8cdc431e8a17b15f65268d5c5b46b84bc09706b708f62613b480bef02adc1b69a6d5bf041afedfb1015a439e3fd36516eae779024b7e607e1162b1ff69c5cfbcc25210fbe20257827019ebd97ca25b9d8c639c913c715a18636bffd41acac736f050da63012a53f6c1bc918b21b064008b7c952b505e20fdf3e6ee9b3487d92442d100326ea15da1162c12039e7aeb3698754ee526c182ac4b327596514a2dd126671513eb609fe787f0c98e03b048881f613a66cb636ef4b84e0d4066f1608439be18e23e60e42ddc1d423ef57f7094587c682ef243319eb1523482389fcaf2eda2e0e9a635ab67b0b161d8c4612ae2a8f7a465881762b3bdb04203e209d4c3a5a1d307d42af73016e1c4a47140d305e5d3d32df9b6780e7f32a5b7b7532ed50614b8316ca9187ff652744db4e18faf3658f9dac1000d88cf283a3fd0e5d6ca229599e6aaf4f8ead73048d5c0803e47a5516e8e80defba2929ced244a30f268398d2e6aca80367861d605b2b6f7881fbc4bb86d0b6c03bb4704782251d3935d398863df66aaf9531ab00649afa44bc478bde633e739abc7aaecee9387af290898538521e1e1160f9429f50769daf79506e85f3d8187c20d89555cbb5c398c502e376a80b5a5d8f853ae59648e99eeba0672e121da01c3f72783de68543eb9f24feb5d9dced675119f758ef2efe309099d5c33536eea5fa0c5960b9ac90694b498f4eea9ce02ea426d14a9b1bcb9bcfe8f743c69175a6d5f83aefd3b8a60d932c24bd6bb1945dc905949138db8b41f2da228efe3e7ddc80a689e77d80398d4b50f1a414ab513a3f9abc133a91623d6d20b8ebfd2d65d872291099a3fb4c807b087aa0c1902bee1e3501cf16b2ca68898a045e3c70974da81b4a0771e81bd8e54cba3ab409e92923b0a21a569701e95f800d2f061c752c3abb3d8debf906bcad8b952344817d84a486d4828201d84bacde7a8aa2948dd5488446ed65d34c3747b3631ad3e9f16735ffc227092f697a856f366b8014bc69557d6a7062139dfcbafc5c7b1c36662ce66ff97d3778d183693ecab98cfbe941602d6e3c60bf6a9388147a45896e29d4925fc7e12c72d32a25b5b256206640e8fbd7224202e584188f52ac593e13fdc7d4a5e81337af345796bbd65894e0b5e86c19d9c8bba0663dc6bf4a4663b167bc14592577c803916b0d4e2467c1a2d12ac1463744b18af81d28cbe8f822dd51c1201c4275d6b79e3acd08b00f475003057088abf5fdaa30571ff0e62e419c8d086994cb5163abb796e86943142a8e622a6efa5ecfab5041db15e26d1ffa69136f7ae3bb9eed26407385b72a02e81f3a2daaa6e094e5208c38a84624c64f80fc81d7d9caad4b0aca4953b0eee10529b4a4272f97ef05b49630273dde627f6121e39afce8e575917826fcc131cf113fbeba77f2a265f3eb38611c8a5418bfdbd916cfaa06f4b95fcc0ca5d1c6e5bc48cc1e5d221e3664f098c008ab98cbc929299bc6dfb89ad30580cb2d69830d9a885934218be71570c08f8aac52d4e32de961368063aa7f72a913d98ffa0e09c9d377ac42652ecfd29241ee6369a7b3f4b167f4d7799ac227378aac77aa1c431796c6cc27a422919556157f6f7e7aa17aef1d26239afbaa735b7f0d4941546e6e0bc27bbd4b848c2b55e3f7b7bd639fbc31ffce601225043bc09205b307f020fbf96e734a7f9fb4919dc3c30f82b8b63a35c88b6e007f6f82ba89c30d72238288599711e000fdfd84d8d05ccce8b02f63a5ef4af63764936f48a9b443fc621cb15e439b6043e98eb58d7d2ba85d0ca354025c35524c05393588c75d4ca621ed4f66197e893b5c4e5b04b9496ea94334d32c446d995da7c8d59335a42c5a0d4ae1cda57b3c3b5b6d2c68cc873e056726b059a22e7435eac8a5889bdd2d456c6bedfe386bf519ac9dcac4740f7724eadde37cf22de3a243b3ed97a4fb834107019bc8b97092036d60095ef0eea350d29cf0be4f1e6dde1691453de32a5469d2e675f91846284cd5ea828a85746cd7346f2f8426a1c19df7cb0815d9e9d35290772034f21c756f88d5006de821cae6851125a5da3dbb48b8725fd3a31a25c3195c2e8ba295521c62e856e66d7b68c3d232e2dfd4170939f514605c2a22f3766d52b5005b92c11cc0bf8acce29789eb33ac008f303651b3ff7c645650391f46d4cb1a87a743c524c5842490101eca0a0b56eb74421541bd9f0ac39683603c50c99efc3704c9bea057ecc451baa944b0822355e3f59108a67da51103337deff97dca62378c1d07f17393775e3800100e73e5ff1c24bfcada77d9fe10226546939a9d4b58d995fe679981f0533832bb11a9dd3eb602c7f65e3d35cb15bed3a6e1d8b11e1aa40177b45ed3199edd77e0a2efd584996c46a8913c6fc8589617fdef789253f9279e76c161cbe4d3a8f439c28cd963cb8163fab40b9a0cd8de13abf7c826040a5241fa1a8fd1324eb40906f1f40f9188944362e983d39ab89b87d622df8b32b19c1dbc4d97c4fb9b4cc287ff50de6ec9487bfc6767b2f1b781c853c9f139e1dcebe1c1d7927514b68eaf2b0abeae8c442c775a20598b91456910c819132d19b30fdce330aae2ef32a90db888ef5e09fd5d747c5e54dbf130af6c29677c120ea94e61812530b76b1e60c6dfa31a3a4230c36a90fbfc8846bf932620e3e28c4e863b7992012014668cca1e7346d593a8ac436de93830077cf915f948917d0c21ec2511d831b32ecb99c77fa3c8bbf8542b01ca252d179aed4c24109a2bba3fce9bce75b80f13a3ddf96af9094e3157edf29411e677afe1127188f442ae59a6a5d2ed26c9cd0ba8d9ae3560790e40cbc829b4950585e97bdad5b3908e9a4e108f6a3274fc7915677b74a16091904c21b546bb01beb7c3fee40c8b41ffd07bca9a1187d1eb35cd2cce60b1cef23d58618b255a12ac6a5f0badb9af5abf40bf5d0f201b91b4b9cafe4b0d66c60f7146a6ee25ef7f750a81d6f935027d00ef70e9f48b75d800b0d305db87b8d9e39a1d152fbd1abe6e6dfe0340c70fd2887af99a04a36f173d5ee7210aaca7db26727a5e5e261dceacd56ffcf3fc2038d07ef4a93c12699dae49de9079616e9b38dadbe6bd1c3bedc967f150ef4f02783de16eb8b74121287049024fef877ebd8de1682faacb7962a8fecacd5370bacee8c2302191b0fdef9a79d4a18cbe5ca5ef3bad66fe55fefd8d9dc35e08e8bb11ddbcb9bfbe9b122a6b76b8dc66d99a064f26faf3096a4de5465ae8dacfdbb4840f9334475b8357d42140802c104bbbfa2fffe827859ed650ccbcf5c7dee37b288b34158a355889067261faf5af94d6d2e2cec9caca7edc1662e68a74fb41d9d5ce39fc9fb38539b89e1ad71a7fe2d658385463eff43d63f106f2171c92c559885af31c0ab7ae85a8e031208e899844278eea1d3da9aa5e4a94e8d9b73549ead2085088f13bdd567e1e2e23923072ab76220881d015e3fb390687449bc8ad829ab6286823d18bb8440dbec107c72d15f281e316e4c0548aad0fd8e9d7e7706a427ae31db9c2732d4cfdd671cb99d5c7ad6555f9f583a60930ec2da44dc5423637f9a3b655c284b8269e9a0fd9e446096468056e7a6d598190398341421726f366ee0261c6d0ad1203198378f1881d9ffe0061319105af7f2f9fe91357cb5033999c0c0f32496a4cf22878368556c146eb1ba76433e3921bfb36dc09e1fa738ef170a634a64ad9c2c93d485a7dd7d38a0895d936e29284af993f425c224c3c22c4916790c311bd120eb970b43307cb9273c98501ead19ca024ddca20ed8a78b797d4b164b3223f8a33790f94f1daaedfcdf523808207e8383cdfc5fa2913507d176d1630a08b2f8a090081c41ad355ea28606ca789cc2aacbe421e13bfffc564ea6e245acb8f7514f6f94da50b60fa95a3d256697aec17c633e1ab278fb4c4ba3acda78ffdd628cc1277f9432d2c681419a8973500ab4ded261d4b42b3edb16f4198e89302822bf11727fe79a5143b43d40dfcb1ef38a6ad491a788c4bda413bde92ef99e44604db7fde66a0ef8a3c4c3a30498e5ec09b2a65abbd1c10e906853cf88a05119aa502b8f8fd03e9647c81e1887ef754ae16fbaf6e387db28643b8b66a4e9e7faf31d087f5e1350257bc5c802a07998bf666a33fc3f60b28d9835dcebf8c5809e622378670814c30b9dd457159fb26b53e1c95e168660809b882ddc1c3d4549288db429866d34c76220154ddd4ab9e5596c13f60e13cf96bf5f831737c841c40aafa8e905cd01d7ab146bfd12aad52c7a685d8515377ebc87b2a9b1c3e6a93e0ac6a54d68a86260ddf635adaf7b09598a7d21c060a04e0c9273540f449264330a1d8d3f2a33cd214b7d4b76489a8eb05246f31457281204fd66872eb4057ed6284e4ad0f8095c26b3f54a17ebbbc427c68b12586e5104ff44053f4ac1f17aa6a8a59a4567d7e30b5394b69ac5dda4b22ba27d1c0251d2e60f627cf05e2c984cf15f84e0a9842896dc232d1347dd9a1fb0cbf58c2c039258059c52731179d1fcb8913b1c1a158858a408172d333c2f894e7877824f0b1507daa2733aaba1cfcbeaf786dfac0cf36624f6146a8173df41f0daaceb7d50e5dc2472c079d5adc8361313e20d8f575c031ede05dddbb7865d982f272dce26837d7fb61c6990e77379061ae6ce5b9fc9f3355bd4ff9a7db73f940fdfe0eb48a1152e89c4928723e9edc14bce90b22779c1b72f36bf24856720cb33f6a809bc9d2ad421fed5227bce558898c88f3f23114a453e7af0b0a760f9483396b571a37a4f255037ea0b2986f9a1f398bc80869d5d7d1de832cc72a10645984cd021c490e97306ef075a842a3791b2ca4816f3d23733ca16e917342c1203d8c846b1ab1be32eafe49924c8e1b81489d892215e90c59026138d530d1577b2f8010057e218bf92446dd05de7d6b9678420e0ffe060cb9412de06edc13e639d5a655b08ac6e3523aadd118ee1b108b5902e9f0cd94f59f9b21b25abadd84112b91a60ef254c0f394da418e64431e6eded88c865a976ae8b6332b92e02c324ce0238aeeeb94f1776b98401f04da5fb7e167fe892753678f7bffbfc1829c2ad70529723034e1bd36b9ca33716114aa3b90bd8c13b4fcdb9fc8a0235715a1463a91d7ef8814842283b1258808f4ad671c0e7611d7ff28f98300b79b63e266b7d0443ff9c99f7ca59936cfb22543b319f4ae0f8612416d7ee803b04c63c275d721f6fad0b60dc7a053fb11c72e33a78424addca64160ec17c71c5f0094cd63d2df90d565873ce629ff1f0bec2412a5253932cb1f931b36f566f757061fee36a8b4c4548793ff9e71cf318743f4a1c9488be3994f7b7e86c6e1281db3e77804bf2c7622474d9d9c4ca648988fed0e6d84bca4aac8479dcd701568470bd61f0c0b2888a1f23a35b559c0b816acfd6c1e53b892f17b9ecdb8650498a2eb11c812c800ff12a02b563beb2ef9efdcaaab337fa9572e92eba5aea0bad4877fce4843427637b7fd88535a30829795afff493e9998b5aaae17cbb6cb83b561a84061beb8619da0ca7a2e5142f10096d9890c65502ac423a1b164c23afa34de779d0ff0ef6d883f44c9628dab15b6d5dd7ce05118633e841f563d65ebd0b2614094a2e1b0de1f9c3f230f167c8dd67e2a20fe7f90ddff7b06f55b7462db05987eac44bfe9432bff0133585b7494c9a4c93b76edaff2e6d22a0e93e1e9e1985c9f8dda50822ae7747a8ab46ba238f438985335129a133fba59fcb64e969414be2ed36efd293956e3c89c5ebf846283b6a2d78cd2a5e3858e18c51cfa4255a0121b4af12ec4a3295fe949668d6907f7b5f0b9f9640b239d6d6367073680ab93f2cb1851ed0aacb307fe3145bdf49df5fbd414e3c225d645aa86c26f5896f1d7aaf13cc0a00cc2dedd5d3453bbba6830d804ad401b8fab7f6510dba3a21d65d5468e1107a7ddd63c06645ce6c7f89e82fb95439384811ad02a1ef00f16bca32421490c87f6d3d140d1ba6234ff9a3b74e66e774aec2807fe18700587a5090a6aa5f0f00e868f459d93831af24186695eb3c33d68cf6df43d2d3d1bd6f6420f1d7ef572d8e5d1d60d9f5071ffaab15d02417b4d6c94ace7a8246fb6bb4c2d45f04ec2f44400dac6fdc84798834340191dc18b137a836fd394798eed4ebfc39be3850e33893de7f928deb834d8cfa9342e2eb1250506f0d555007098d0a21cc8e4e3a636eff171ce4160a842f925ff50c3b1d257d699723b3b0d47225b092139d06c9043b87c0bd370d17064c7fa9b35ecfa6c0acd3aa3ad8170850d3c9a5d2bcf14221d77378e28dce1070af311c3c5a72247d9a43eeae38c0fbede23f500cb8a372df5b2c6a1437725dbb82f94dacfe713b8854c1607ef473d8b437bcc5866cb10a8bf9f7d056e6501d2e89ee8b52546ac2c1153f00b7a765eb435eba65bac805fe592142aeafeb5019d6d0f15a6730ce85837e2a08478c2dbef608b0c7912667f3576817d187ac4a22836bca24addffac70c15cce5604b0d7749d856cf75f7875c50b747ede9a43d3878708283283bf908efb14d4544ac28e96a7ef042a54ed91599496c40bf9fbfeea1a49c7760449c5d4f7008d7224d4b3829aac6b55a3f2295966d31821e20de126a7bc34a17d65f1db753ad4868a847c95a79b6d1b037ca9403f12639c5d94eac2e8087b0d302f914fb068290f77be34a0ead151b5e673273b54c0a986c85101d3616387644d3d535905e9baa83618092c54841479a06043804e081821364af4a5bd7151e77606ae4756e415a6debc80fc31e0f40665cb4521ad0eb25f09c307687630652570d83defe779d94d8bda202dd68b64b7f64c39edea7b911f58b975bfbc2de1c1ccd55bbd5c5d98b3e3a6a44899d4733dd5e73f5091ae98d83cce87a252d710a63b68b6b4d738e78b24b4413463850def2ac43fa0a87b9fae3d84a53df24655d86dd6a367f92eec4f52a2dfe029bf0d39fdf1562f38419a95fa3f1a9cb44d35b944c02b499304de70db1430a535522898b77eb812a54d0a8bdc3eb5c07f12e5c215def461a268a01fc77e2f3d43ae5daaf1092ad043548adba9db475f903985cf2075e1dfe217d44105466f6f107349c0290aa4f7e2f5d3a1dc1c8fde1ca8c79332014b6cc839bc05faa9e5405948cb91955c07e00dadeda8f0dea0a8e6cc6a6d93d20b769821f5aa4db2c0998f274583e6e5a648eee828a996341e1497c63118aab06c06c7195b80aacb62d6be6e8a10fc50ca56b9b94ab03a3c338e3a16be7aaa6b3d385bbea89c81b5b907a0c8c2fed07770ad3b1c8390f4c1215313e6981235160fda3bf0f46421b834856c83c6c42a575cbcd255cf4e9419f56565647bc1406f4aeceaa601732585496c4d0fa05b221b55179c211b90934b756c7c00afe15894635538d2267924c6c244a01bb536e08dcd1b35887db3d746dd7bc14923b54a3497da8637dda356beccdc18696cf58e69ab762792d874aa1e44c5a8dd577974d1c667f1e8a0aa72814bca3f937786b74a13198b9355606f8b11fb174a8b8b2d9c81bbafe90bebec204a3a93050328eaa3285f2f056199597bcc353e14b308bb5c3e22d91edd401124e03727bad236899b8fc26d0b51dcacc2b0da9414aa7683c3af2393cb106ca6223d4bcb37b77706a31bff303252e2c015dd0cd91a28fe85a9c28391e988ce70827c1db78ced988d71963e3e6de4b0588101375f33b79cb8d57c09a1c3637663da52523aab19532c031b883922e4ef836f34b515b3dee861f7907fa04c0cd4e083d68225d86851cb9f671c81672804714a9020acd0ca0be4f9c33eafd9286b844fab9c2a8580b5f79c7ed7992bb34c19eb5ee1aa38ebd620c999b91ffec0902d5fa933c47667c0628272ff3e9d58e623049ae0c3caa0f7223a48bd647a58ce1e95a62ba21b7505121cbd13411982ddf338793177c821ef7e2454dd2180c7e968f24cf53efbf7e962f769ded72c00c7312ea2dbd47ca95ca1eff0aeec8a8f331bd7405b44840cb0ba3b0cfded40a208730fe62e537c99561714de7d4f3c81e0824d7a045db8da8f9c88e1114a9a0c97e1fccb7f0126a95d942350ac132157eed442117c22cd69487a004c08d03eff97569e18adf23d2b54dbc924433b6efe4e20f42311ccdd66943bd5e31db7d4b1052446626486800a178347c5475274751719c7ff66073d2120652cdc9f1affc2a350583ede431cc85ec00dc441a6b529592a8aeea99047d257a05744f281982f8178c9a1364e21c9dd63fdae573e2b47f87b138a4b3597377a507e898492dfa61bb255c40fafc8bd9349896abe0a08b191cf1adb21432660a663656c53bae3b8f7db657878b2bb2d93e62ea399aeb6f5757e775b8e7fb3696da882d24d6f9465f1566edc0d2d6c08d2cd1a5dc56f30f9c47f78fedbd58d09ddac40b279026d513b7f9c1ab721dd1f8a6835218b0c62d8e03c6af48d530d6ba7c74ed3f11b51ac0b9a8f319ced2cd2846b6ed308a1551551dd598fd2f406e8731af3a8da3dc6fbbed744e4b22b3d73d5331bbb7245082ac9df6d72fd59db877d9b8bd442d0f671ef0eb2b872439c1eeb6b92368c52b662536b1e85645705511119f00949d5f2890652d1a71d5457674eb761b9d02657498f9b9f7bd8a2cf1cd6b1d5b45d64c00c7f2be2e546ab6e74d491898addc3e762c4476cddd4e5711f5a4ab6153e333204faa185c56c2a81628ddbf4004ccf4ce649c120d20fa33219d0196dcc0c7bafe2e34eaf327171a72a52c93528559ccc5961e1d502b158a7a701114c47f35cfedff455c23a67f5de66887b46bcda187dcb46f00c16e2225e88fc9fcbe5667a0676bbd527d371bd08eb4abec65801c226e021da0e0526dd1882755901482e8070572a43366ac23da5a14bc96330afb604b5367f64ac9fea96321efb0ddad7c329b3feee015b6a668e4473aa520c94ec95d4e367b50fbcdfcfd81e173052526cb65a34aa748476112c0c25dd60eb7a532d90f541bb948dc26018f4443bc714c6e67c0a18dacd5c6141b2ba8cca6bc820658d6de9523a8a42066fb0b3b56eed8f9ddf2d3735f87fc49ed82542fc2f8c4b1b71b1ae93c62ea07f1cc052c9dfbfa52f2f180943ca05feb7d5dfb04f57d4de9136542f9facb60af908e6c4a047496bf382a1658ec47d3a27be7027ec8555222f9f16dff7029983ba3bf0e99a913ee6f98ae03bf295f86469f935e0c0e89658d116a697009a0ab5a99a7434d55c25a770d4b57a741a55dcd7ae33ee33e819bad15413789fa3f1a6f116bd45c9cfcd8f0de0fe6c71814939633b8cc3bbab36ba416e0fc45a5ede806620506a6e5e222f0eae213c7e3a3a359fa4354a6d9e04dab5ced04bd140a0ed447f9ca458a9899933495f0337ae057093e52ad9aab05c6e4e161a540cc70e4f24bc7140ba401b836897e8651eccabf4fc3a1c0c829668dbbfc3448dd5cb20575753e7b1fb97a098386a10c9fc551021eb23eb681dca3c440b98b9bdc80723afdf5f22bbe5989755e2129648133ac04c1975118af613dfe9883d2982cfae922a19b44a875ca75ed3f5b92f704a2a0c8ac787e9f090f1b0c6f66223985544b68a5696f2afffd15601443e29d2afc6d9ffdb4f32b67e4e8720fb29102cdf4383e58ccbbafb106264e5a2fe47ff0f0e573c2e98b849a9f1b3129686a071e9b57540cbad68dd72e553dca204ee40ed99e0367ca45e42ca058f9c14da69349c183d8fc49d8236a0e81c9daed7082366627e9c95f72f67acacb320ed35c915d1abb8e49340e41bd3c04cc591a115e1664a9b6d8397097d9a6622a7c60e7869194589be5c0c7d6f0854dcb9f535625af328875c6266c3c0ff3df818ade7283bc1aaedee4b29e90666ccfe4e2b0d451f0972105ad695abde4cb846bd99573060701d477496c20f5c8a611cda86e46e6194b8b2974cad6b5fed3af15df6447fedb1922d54f71e325e7f27a8303fa42f60c14c6caab36ec22053599b1091540235472e37e774eaf461a4a64c54b9a482629495326b53b08c825e0d19a725f14d12fa7e2efac47bb2cbdc598af68f24e7f338c5d55950b2d47e09b6037e30247083a29038785da56134b57863725531cd427c13af0298dce83e68f17d13a85c2e28fe7d34a05250583b1a4732ca0090be652819091c161205f7edba145eb9f65351a85ebbce227e9aced84dc34de4cab1af36f52a01f287f1b8d58f35a17306270a19c057809e24ff4b78082df40a77de032f987d7827299f616be6249f4e5a3e190cc6aeb85ef605e27b80001a4ec66357ef621ad06649953c201770cdf8ab794ba2d74500a65aad04b9c8d793237185e79aca34c5b8d3399be558c70e4e907e9e70d798d62cb1888b1b0d6aeea4ab6c90c9a2b045221ec0a1df232b6bec70ebd5accf45927311c96e3a463d03039a27c137caff88b36ad06388da213ec1fc0c75c6c646faa14da7fc748dc01be9e6b62236053d79a50181fffe0700fd7d78815c3b99445ae88140febede19210e7dbb3db72b4c55b63cfd78aaefd598595faa58f9e72d00bd9b102bedc15fd31f33fd3a840c19987a91b1142b1f26df9635230eacc243f27b4fce3b1ef62cbcee2b30f3ac216218376d40db08bc35ad5f4a6f5ac0008a9032ed17e78798de71ba21709c63fddc113f14f320799c29e7ba89871e9225ac953bc3457e45470ede7b9fcd70714abc8dc19a4802bd06ab1acd59d69816778d55e7c9f10ed257d28e9c6e9ae0e3d53485a20ef6c2a46d6355a64ccadb040450dfa5f12c3f535c296e08e1183a685b57ada0ff8960a6a0d895e09c511fc79aacbcd03026b47eeb3ab4eb57beac82492303e129c3e168028c112518bb7ebb9fa4269617cb2ab61484914993a79a3f117434dc928047f2f549bc7fd0a3c4e593c7a554d046a94e549cf505fadc3537301f3be458add522bdcf5fdbc9942dcb34c158cb713e2df0ef0db5c7ef5500bff6bb2822c71a88b2c094d1ffc7e130a53552b4b3fab54e8372be661614df20cb7396a788eb565e30979e3c2b318a51257323b0f99ae03d179247f888ab731227111791f95134c2efd1dd44ab7857440fab93bbe07e6ca3e9b0d5468aa1c478ebc60606f5716b6a1bb53c366fe23fcdd205369178acd5dd230abbb0734270ed92436b5e73d0bfab9e956be48f2c31ad986acd2dbc06dea2763ce85e55ca73fb13e3d12fb5e8267cb7450057e4f4a0d1a7e3436ab139ec637c0cfd8d5bf14cc6e43cc61665973524a86121c07ae6611e43a5adbebe35549b42a4488fa0914243d47d13368da811e38483b67ebe5a4ea2f09a23b70dad0dd3693ba771899dce8da192f9b9536b801d3e0391968da8416b762aac180e30add2c730c85e400c27b419a2af339e481522297e3283566d296aa7691c15a87bd8c3a831ef477e13e9ac3d44e62731757b1838ae3b88ea3a5db9eb9c7b38bf16aa75cbe2ae9faede2366a17f9a208b3da4af81b2af3a7ca7b6a8c874d5f0d59e20a26143e911d4ba99925a47c5cf23c72d788bcb756a75653bd55cb0c13cb93e930b2e4ab236318e85d441b52af6793996abeac4e692961f7d335baf155068a2e99625def90b8434b80357e484381a791ed7ebf981d7dd5c202a15bd2536845660dff3a9a1ca6e9b7a2f904d1920cd7954918812ba863411a026440fbae3dcb3459d8a2c44695d23eb3abb56e2dc1935632787b33989b9876c6c3b03e50021740d3ba464d536e85a486004812ad1544ed10c4751ae13344102c44e50695a569ffe6d4083d5b26c2f46d7c5840c27856f45938bb13bd91e193c4f61c1f5ec2ce8e3a322a3b535a992633c3c82b62e5a3b81eee21d620fab80af635b1f85da35d204b07e5b6455d3db7e352f9f2968edce3a0c34fb0ac425da027b98d509d0862ad351763d7c4c9a4432b2cf741e2385bb26e7acb333b093aed231bc695330b188f140dcb89d752b9f9cf11716a076bf19690e8196f0ac4483e775625500edbb88e78a3befb70cbac0634ae3b998aa0b34db39f0c088f2196550552015a405c20cb171bccd981445b21176c24ebbbc32b2931c15a0f84fdeb8a9650f6af563f60b374da42601a6f7c9e1ac6ecb854712ccd5a9e811cdbe2229d8d517ff7e809e758bf7c03a88884bd33f7f1cff3c4e317d612e57c42af0da0deca619ec7a16f49131aaa68d42c4119565da3af3aab8804cf3e81b9c4cd389bf1c95c229a049b696d2f20b348b68d1c41441158a1a866446164e9fb4367dd861f287bd543eb164f57b1e80985bfbb0a75be18ada1ab4079301502bc7fbc086d2691082f4fd94c90c9429c99a703d724ea4287b70d58759a75ea71106b0e9745fbac0ac1d4b8d6b6c06b885413e3165f0fbea378507ff3c470a9acf63d241777f486386cbc66755d170e8a5a80fdc88d26b018aa35739993e7e8992ccb2ccd4edcba28e6cfb6241efe1689dd2f1aae17a4c1f765e5136a2288db67da39672195e702596a0b70b47299741f4e83390e3f342c046f2545766807f7b5d7ec0d867ba5e086ca36067a3e9c48ae593e101c9b517df65a4d2aaa16d3c996a1fcbe7fceb97a1f5bba29715407900f03f4e072335c34be830a347f75b74431496e01bea0f6767c3e7094be5669b514526fe0e55e808ed29f649542d2b641308cbd9b5f899d3fa8557891cc925bce017ebb6ad0c2fea702cc88dbcee394038c6f0b340db763e5fe15587df7d396da30abb4e3261a3fd41a36b17646fedc3ab94e585e989dab3ab2ccf4601e272bd1340e99ea9ed62eeedc719733b3077aecc88a1a77635003e81c1192d817f52afe089fd96df5f61f2c0f4bcd6490acbb9ee362247b8553860b5192d32b9592343c52f51897b45e3184c2986c2281572bc18a50463a2047c234fe0e7182c4f80600dd2c282209d207027be10db7e706e71f17e9c2ea51fec610fe718a5bca17387fed873dd0ecd822921d280487ffce790df1cf8e538c3d2864ca7532db8208f4bddd4a0fa9fd8b38b87c921d762cbb10b490af8262ecc0eb162b9d4218721a71c19618ec7e0392b9cc9ee104ed99474ae02a21b9af976b7b126696d075bc0c253106ad98fb7112844a6f5c91d2dc74cbb81d0225b2ca6baccdb8a21d08b3c500449aff7cf3bc704827acf1ed25ab076a0756d4f435b96c7b3f78793c311739242dff63a0ea974782d6135d49222f2d66f3a801a9f87eddbbef3d4f8bb9d14697587ef19a190562f768ee29b79563f4a7c3b9f906cf54cd1badba02d2ed7fbd5748988759103732fb186d2d1f2107ee1e8a706dc5e82149d7ed370d425a67c3f61686c652616e5eb6d782798ffff47b0a697a02a767655534c1bc1ecb71534b1798dda5c59adcf3a606a08c470d630c48c04ac34f6fca0baee01628d8b52d35257db44e995f3902ed32a84115168399107f2e4ce29733e5e9885f890cf53bab977906c42053d92b91831f42ac0f9562495c29dabe1162d57b2afc61d53cc893522818ac13cb266406b6659c2b8256401656e6cf45e43faa3b08a05dfd0307e6abd2365db2c7b13cce1b62c296d1b839899fcb4ddd31d17929822870756b93efd48f65ce56c0456ed0d376d7ac275282beacdba789253affafc7c162cb205178b92814a447c0a58563eb212e2c57693c25a42d820dbd5bd9df82c38e2e0cfb37823dbb5eb9924a848c83f5e87d53b324855499615300e8bdfc06beeb90bb2357e729f1f5c9c1ed405708ef046d602192ed4c3314b2e882ca19a175bf3a2cf2fb579ca16a4a008147684d7d10cc978abc1a15df0999d00950b92c34fec9340a25bf95e3b82f74b046c20be1ee9cea051055961e997e5c2e314141177a745c328aba7fafd05149b4430135836eca3cf076cbed9c34c3f7a93b6a8e8231266b24368a8b85c694bf7a30a046b74e25955f3615d404585759d215c38ced97abc0eec14251fd0f3541a6124ebe4b0309aad20459f750910cf1fbaaa49b7ab023a7244815e1ae4f5a299123548226d512bf52995aaf7112bc5470142587bad7258cad0ffbd9e5cd720dddfb467a61cac7cdbf0ba8be4be4d5690cc6bf9c2cc7ba65e8ba0ac2bda65aeb969502aa4a1c78e079b1cab3dcac5fad3618953660b40eb0d3487153decebee55dd0fe8f3da986268e7f980ef85395d02b277eae1a079b0ffe9df8b7a3dc7fa208125193c597c5cffb5f6337d58374fc5fe08c0c11bc6d7d114acff3898afe8a8eb47c8855983dbf2c57e0742f3e909cca9221ca770001bea85283cae48429e1466ca9669a4823e5b2842c89a462f6bad5c9b257e8ba1d1adb45382c5ac695d92683f06fe1838ee8da71a35741d3e208e664abccac8f311e8eaae2702f75199eb32efa0ba3d986638fa160096ff52c6aa2eccb10994de7ca31d1b41e75e765d546f0d29ca1596c66e0a59117b2fffa8965517c79dcc1dca80245f37960f606b468de54d8c53a481070ec5a136a228e38daae7fade7a9901044410a0622afd92bd0f75e4186316e3848d975077b9cd2f9445cdd53f8b12d9e9b84a1253f28e335bb5e6b7ca142fc70ebfb9d1a0160d6b740c7f920cb6156ace9a7ddb1eda66d2526151335eb012482b2559b3dca10980349a6a79919a7f0a27b29ac634589715ec9960c9368a5d1e5ad6a54b502b99f81a72483e6db177ab9e6a388a6350b3c9d4dc1a9568f6ab37f0ca9fdff3cebcd75ed1a9d65ac211bb086247334fbbe72d1f2a1ec892446ff72e425f1bf535a9e352d6237dba279d24f2c4cfcc15f18a38073955ad568e208d5c2fbf44f6991d6bebc2314ef3114de5d33c5825c412fe9b8e1ecfdf5e6895522a11fd82c8520579b6699f756cacb3ad361d18f726f57607606f009de77c45a37eac0a5ef501ad875632f44b65696370613cee04283529bc539ff8ab8dc71609c730c682f997548c6b0f878fa5c3a915d4a414fa995b038582c18ac14cbc38eab69a0cd06cc3829fd1955736bea523608fa040ca5f25a1c3b078bd821a093775a22e45f03aa4e3f578d18e9653a78ee96968390f999a04d4d8dfa929f14585c3c59118acbac5731584424fb8b054105c18503ba616bd91acd517c11902cca023e9b339c2745b1441bf5902189d035786038ef7468d2102e1803ee2d0823380214cdb80aefe2d9542231a63182d1a4d1e8cb9aa95c7dfbec0e45fd99e01260ccc7f6a8797f842dc1b1c4fd00adf9c01213731d793ed1ef15cead98ef8ad8600a4b6c22c0b4a6ed630fc3fa6edab6ff1cdb93da7fdb01482529b0927e00dfd849da00a5f0a3af79ca756dc81bf38b55dd992a5c4b195fbad7879170eb6583ed6e6da9567f48b6c29165e094ac78c9314885ca81dce5e83d20ad5abe1748a85a8d77797f425d0c212ecb2d9c4340080c304ee4ec611bc1b4c746c2028b213cfda833a8ab031bbdc86f1573794918cdba8b9b12446de3ab41f432c06b0473964832934b056df907d4d0886174e93207012ba0510a2a720fbc123df3d7973994a2d37d17bbde65a9052c32ea4a9eeca6376f6892efaccb15f2d2f3147e6323012227e06df99c3290224fffdbefddfc0419b35c43f3ff605622c3f1f637d705c6f85735332539ddf944f08899e9f10db362360f1fd81887d9ff1b19883e160d33e8c43303543637dc2ef471838e675033d6422e75718011fad2d2aa95b88ede49e63515e01b4a67dfdfc10a7bdea9fc44c367b32b5bf1373c93f7408590214f8917fafdac853e0ac06caf29c02ddf46e3e9fe6dd42cbdba99f3443b32c03844d7317c46b3736ff1790c121d27f58d02e031464a79d14cb8fa87a0f36cac25e161ae0c86a579ba9dddc25c9f22a894988eb3f082a4db6364d4124b163c427433707bd65f8749a2fe9e48dbb95ec522ee1532647ef4c3d3914245934d1956b65e62c480e138ea381c008cc36ebe541931164619c0423ad3f1b006b7180a670f8ec42a1c5d23adb34f94e8923b6cbdab6e9bc461c1d4e760aa02c9a58ee57060bdd61e69e418db70caaf5d627c8c28b279b0616573d1156edd31aa896783d6b9d8476c399596f6212b2118d9c4252fe9582c1d7eabfa64b63eda4f02831d2c26877654ba5bff1f7221ac21e1978691d5f0ad133cbae796a41c4cfcee54742b8c32b3b3ebf665920ff9c5064256242f9b3123960dfcd6b8b565b05e6fa4c242869cd191ecea62ea130c4022cc75d73722e90214529268a1116da3c388f7a8444f2e38fdf232d6d937a6f927ff639e46822a590bc08334165ed541fe4df286b04e692b3dab68fccca13f343cf2b7194d964600e40c7e01bd482923517bb537f3dca989c551a8677e92a8dffe401a379e020e8cfc5a50f208cb28d2277a3f0b757346c8c5949910935816cff07c5907aa121737b5ec576ba6eb598ca38df1bf9f9ce58e7bba9a326f1f79c371601885cbf5f869ef7ab61de86aab68cb84ac34d8b00719f0b9d79202e34709ccd22c865f825a4592229932b87c68c24e499a99b9be071737a506e80644bda5c286e0eb35d7619a4a482c4f07da3b2775941a499b0b3f5fbd3a203fa0d071d210b358c4f40f33f26ab5ab24db6090e90ce194403aeaecd0c710dd65795a035a4ffd32be81f52b095c2eab2cc1c50ace483e6767ff07df49f26d32563d8694edff2c0dbe650cc0e0d8a78f71854d8b0dc1758155cfdce898d62e813727465086f016468572999e70c8f7599d3a1354c71f3e8daf43b02ade7568ebf03cb076ca99107f81f04cd8748062fa4fe710ad3539493982f23e810a3cd7cb9e0fafb22b939132219f65f8402a77a4dcdf086e2f299026bd5731088b97434779c3a91f063cc9067807b9929ba5c956722dc00482329780a6bc4934025743e02e0ac2b844c3acf44352203a5947804369db5ca4da286d75d68d6f2422be879c3877257725ca3a84f81a73e5a81878164c130a1b53ab3e07a3aa990fa30144de232cb800f910443088a9cf5087ec0425b0832b30765179ff971f412c48acc27764aa2970c71ea67c2ac841c0264e80213a495c07cd7b548cff5f87453f314772d0b64523a4ac346e9a7638a9778531cbd0ecb60ebf7c801ca4a42628954b6bbfb9fe749bf421efef45f71b970627fd0832f00159414c0fa210346f2d3845a5bc917325360827db8e375bae6c56f410c79ab3a7fd70f1a0fba61c1a621bd058c16c7118420a037f5ba8d7260f0c897b19b656565d633d350859f5648c8160c63f2ff1b56819d5bc90febcf4eda5c0e7a2ef10149d7a5baa45afa7f7d423f54220f4342625a48a5878718f04888ba5c4826dbdcd4555e8075537bc473dff94aefec723d89d81838f646611219e01df5ea5515f5530b358e16783424811bcca5e5316dfb61a8f67d84106c745b0b170bbb7d42d0197e39d1a6d12114ef37488a0715d063e84e98fad7e342e2d5b3b0843cc1f456b10a9a2644297791076801cad64081674c08925bb49b324eabfe08f75c12f46308e710d3219f6941204b87322f0d3164ed50d01c8238460597a83a713ddfa14a8bb5daa0e983775317d21c24c17cc8da373c96620ff27c31f57978213bc7edaf03112cfb1fb3997129eb31d5708c2a6b605a7d4b109540c96803eb938dba7cea21c5edb5ca7255aa480ff3ae4d1d188366b91b984cde973022509fd9d301368dadd383439e5dc7eea2bb0e9cbb660c7bfc0224403fff18a8daaba7fed38c06919eea938ed5adc281643325d1de87365f00aff9d50c68036a020b8313f0a67a971cbb6350e14f0a8c06aef9954c8efb6b4be2fb4a13d688109098852e02fc55bbcc01baf052648cae44a93cca4fca29168dfea1d06b9c9344b8a374f7c1058344a47001c9ffc08ed3d0156652d9f2882615ae76d8d1377224a84dfb298c83f2cfb6c3cef1f849519c9ecf5e7e52e62ce9c66f85a9400c63fbd02b3d37e13f2c89809e12741be003afc88f995ba60b5477650f63df60bd30ad03921bb65b4c84582d912426a153b537c7d824cec8e1dfbb33b2cf30ccb31ffd013232d3d53c691cc262cc171a3ae9da97cf5fe677433728acdc3294ea79dd250cea4d9aaf2bd51a0010b586dce393017e292cc0459a835fc652d7cd66abb0500d95e3f04eb309f3ca90dfec8c1119c691fff1a04a384ddcfceb74e3162940920b76e64c80cf4ce4a5b3cd449c839b19c2e0068dd8c46fc506e9a905a4a16c3d21665a0916014aca5468390f8ebcbb375601afe0544b194eb97283aaa499733fed3944241086a1d82001691b75fc051a011e1c98651318b48b8d109d821cef3663627986bc49ddb55b93d60fd720a8759f315ef947b541981c32c403ad430e5b9cc87be358fe514d4d9b6ed211c77e72c71c136412728fb037faddf07e84a03a91bb28feb3c44a96604d781e3b8d100b51b651e029d19fb6b5261307ebcf27bdadeeee7c8fc6658785cc1323aa2a7f5d12d639ad71e91332f80ceb3111d8e701a815ef294ce7e2d247052c99e9b64413e61c87e1b7e96198f8a6244b94b124e7b2228ef653dc35b48e15a148664a360d21278907392cec5b37ab60d703ccc71f2016f33eaab3fac531ff92d840230d2a038b5e15c3e482c42b2465ee1e0542fa0dda7d5f5241cf8536fe98c4de80910eaa0bffae1397a1967e7b00f2afcddcea6023357028ec884ed5e5dc38da4d704f6259f3058d4bc2614739cc6230c9f1467c35c8bb4b6421680020c365f619a407f45cc5bc0197069e6991aac28e3f1589bb59fc99ca1b5aca2284b08e441eb576f2b708b64d414b2223e46f6fb55225005395bb1c8ef94079a7df2d26437051e149f6544bd10a66585604ef294c9bf9a783b17117ffc3a05ddd7073b629bda6a582270f3ed12e3ca40a9a83cd1d8148aef840e8fde04a4f2942b5900b7c79dd941f72be5dddea884c8421293cf08e6527400099672e1288e1ab957df1ba3090fe9aebeda5b48ef6bac2a39fbca30b5baa3691d4ec7661bcd2bdf5ec4f2d7cc3018e9e7da8f9571aa2501f9d3d5678091ae413e81bc9368f1193fa95ee437cd27447d944094cbd034a04639f5a304cca9a0e7589ed157bf66477b1ff7608fca9fdfbff9b9b3b641b094267b4eaa879bbbe76e5fe3b015a3336b8f3383f78bf2ab4a968fc14f467b68e18009de8475ceb0a13775033867d5ac9e00a1c4d5a6f5ad2f69652ca9452b009ff09ad095eb8a6db7857f58d46d363fa463fc134fa3b11e9f6771a6f2818488a1dd0eba24361a01fecb7e7fa36c95879d371110431afe5b190ebf9bf96d76a7d656fd916b7ba6a3df72a22e7f8661fc8cb2f2f8bcc402dca7903099ea45a6bad319c9925d11b7ab228d140b9602559a94af294a5861dda3ca1823d7102aa82c902ca87294fbaf8e1066773434f1625fa54cd98ca2689c6d0a40b13830c459e89145190512203021688f0c2e48a270f6aa9dadac31319ea931b5ab0b8169a685f3248d3b2da7452da462ebdd1dff1a79452002061e5652d38a4db6f474f0bee86647a59b8e61d679836600c2ef3f08e993bd2f87570154d0a799e770156f23cb6e23a6b711d851f5b85978b599ed8595428782a5c7724fe3c65b26432b71b8070250ed368f7cc73698f10e4ada265209c0cc442f66c09f6e59d1439ce10eded5aed97ec58a3e1c4d2c8c1810e1a3b3d3e1198d4c795cf0a885151974f5e3c88b9590766f6251ffbb0eddd521c6ebf7d7d1222190c84c4ca6019d3ca72c752daa42802f57dfdaa61336d4e2a72370b4f3e661bdddf5a2b8714b8f2fb8bb96c96534a798f6af795efa4cd6d9fbf688773fab794d2369b062510490c2d24619e2419a1460b12aa27ac16992254f8d61a37355902b01801451528a84070c50a2c2c9cd43047c0b086504a29e5374f6e562062840e55508e65864ea1c2e3bc10224bee8e5fab51fc54a5544b96268cf820c92029cb113b6a94c8c2e58ef6a7c097f7dff368fdabf55feb83c8f79ee9eb61cfe3fb9818c4f5dfc3c4204e41c07f3ddb2ae879dffa59f74734f9336bb63694a306866c035fa2c758d36511efc8fb22f0bda7542c7a7deb0556af138b5a2f4523ffd61bc9f73e5777420326282dedc1d7acfb23db8e5927ee402261040d9ad896e779b226c2082f9cec2281af50be8b6afa2443fbce18305f7ed17c5934df138d48903ff4a7c8f2a74a833276bef4dc9334917a35d83673cfeb231b0f31484bb4df75eb7974deb7bee547363adf45084708e97be883e52a11e80a5fff0f03cd9e6be5fb17c97759c522f99e68f47246e5f3f0ef5c9cd978487146c51dadf73a845d96423590d65961e54f4a933ff3bdb09b73d22ecda17817ec8c490342ad9cc639a409dd751fb61ec4c2c73ef20776598706652cdb8c9c8a5c2edfe57bdf2c72bd271a59978b46523462514eeb123d7fb67963ed4efaae679b0ce57bcfc3df25067105b1cd04ccdf0104a9087786ad188bdf54d11c1c047105f9bec500efe543c0e554e412adfce9209ea844993eaf7eeb8beab7bebfd5bd271a79dfbd518bbd8732eb56e87d51cbf3de251abd510ed9f6a1d8514693b2497fd2bcccd77dcff53e88fff7dcef7a1ec2513fc793f0c3856c5071509f6bad5855378765cd1df9ea7aaba3b48a85ead67fa13bb251f9f38be4cff9522c7a19c97f8947362a8e3fe6a5dffd9ce6b4a6893e4eab5fff93e0d0044ad8d1e7d66ffde873d9fffb22ff4ff4c4a27e17787dfa369da2e0512ace18505f3e8fee5d0ce2dfbd14833815f9771f447efdbc56fdee6794b65cf5bb96ab25a97cb16e6fc640954f9fc7fc2a0611833815d59fe2cc16a47b29ce5adcf179dd34f9131e712ff4c1f6f31d6934ff0efdc9c73e9efc99fd4736125adc815482064e6cce69b2d6dc8124dfd6ddb5c4bec964322677cf75e00456260c8ac359ece6f67f356883fb61b1e0e5af6003d806eb6b89c81f0f2ad8facfbb76d878745be0c7be88be053bf96c03c3991465b656947d2c55ee68afec6ab70377c04422f5c1270212fd9275fd170bd9666d38d47d0d3656e7777e26ffa8fb991477207993fce9de99e48fd2d0ed5e03275609e7bb9cefbe8ff8d304dfb1ad7b9a31a03ef822d087e9fc4e0c49e902490a7db0f2f90279cd5eafef81746fd47ddf3ca8bbdd779307517121184fdd3752f7d475dd94dbf591dbfdeb7beadea978902b7dafee9d18a8fb9d1088fce95ea7fb5619eb504eebfe25fa93d3baeec5275676bf430ae588b3ef63247890fcee7548211c71f6893c40fab00f521f7c1eb08f7d4c0c421ff66ceb5e042964bfebbc8060c915c0652f3454dd0e08fbbdcf3748f4896f47be828560d81a796ebf7c2f949d2bcd1725564daa246c7e58ec8e9c5f65ea459b78cdc91d193765b2891d6560083e4ef31acfe52e3f20dd1935d9c48e3469e5abff4db6e1dd4343b50e5e5696071848135684115a6ca39fc19dfb00fea93fd20ec0dba2080c543f47093b7653d61bb621654c6c0b5944807471eb7718b6c1ced46a6e8de25632b70e89b9d5b3b2da4d4b4c52a889d6818586ba69484b3714c4d8ba4ddf3def251ab9bef546df473ba1c8f5df090d609b953ff54d6800dbbc6f00dbe67fa291914cfed43fb2c91f69b24619cbb6d6b36dfee8cdfc8f6cdd14ce5cdc81d438f953bfa15a8326d6f5f903a9ef79d666ddfa1a38b1557efd7e924250d599a450ebeb7b9314aa9f58df25d67770075238df15ca5752656a75538c27ab81be5e3ff4c176cf77eca66eaadfb5833b54af56ef755d68821df7a1b973d87ec3c73edf87c5c23e2c96079c31e08b604871662b3afa22850f30d8ba975fd4bd148bea7bfd23db2d72d96ab0b1ddd7df81d489550d0e25d95116fa60e9f31d87d8fc00a5852cacb842c35ebab9964fbe9295d16e4bffef0322979ce6f5482e8d4777be6d59f3ac1c7b35b28698a24b1a234b7a58c346bc61b2a68d99124c29520596272d1e2693c89552ca1987bbc1063254a4c8a0851a2c2c34998207a90c0e862c5ba496f0ab2b5c58f71cde04eba362ce39e79c73ce2b5cb430320409a41470b0cd3477ce39e79c5fceda862e1560acc8a003939929407089818a2b4c7460340095a58817485071e5031a299d264652b850a384eba1c817567ee5ca085a4c388922aa34fd80431a2f8e988200586438024a0b16397421c50f3e504a29a594524aa1585862045a72534c0461a3515c4a29a594524a7718c87fecb9fe3d6c835d72210127a2f0d084e588ac2dce5c5121a74991264fcc60254a114c707028de804e1c0cab01a3d3c4b160198105154cbcc062c604ac2d9e3c59a214849b27babc98f8110f63de2031c40b356099f29c95282a5ff28d99c27577e22c2bd084952d597c81c4cb5499194baec0f04407295c38391182167bd381e86047cb36baeb36b7c99e76dda54c227b5a7727ca3a83da878f4c27c1cc2cce21b3bae7dfb1879ed37cd049a778c2bc3e68d72d95c99852ea31f3147fd0eb2fbd88adcf5eadf2877af2a7abacb2ca8e79dacbf3798a9465e8e38e3b578eb992622945963ffd1e333f03b5ccaeb1fce3ebdaf80a37bc44ffb8c1652019069779f8877f8a2f83feb43b0ce442dc7d0acbfc1ecdc65a5ecbcbf2644e29a7b5ded7f758a8eb2c9e353c6be49c9d0b3feae59fdf2985dd009e73ce89355a2cdb3fca50b2fd9ed378ced7654aeb0b92a964fa94a7052b2995799776b13801dff33bdf231a39ed8845e1fb88463d1fbe518c17ffc866413863c0051fe3828f312bb2e07bde82ef7919e18cbecf1fd93efc97f145ff329ebe0c19ef231acde81fd964bcd18c8a2714f93c06df00b621c5780c4423230c1e03d1473cb27dcfb3ade747598fc8b618e205e1d10c7370feebd705e1914d0cd916866cfb906d3242b659101ed9186cbd53fda8b7257ad50a9647f430a33f4979d0d4070b9de67f3aaf0e5d5716b2edeb20e779fe6370e771be43fb3ce128de1d91e773489c30271c7d884e8b85f07d7ff0bd8f219e7fe1c8253b8ff339e17874731e0724caa1f43de0707c9e709c97e777c2b1efced3b0861536d49138bf131e5d1d71004d03e76738d6b8389f43e684e0e384ae8785e38c1b7b1b8eaf1b13619f43c2c2fa603895427f721a94d3e4c34022973897785fbdf9e5ca3975a5c089ebc18e8ee39c8e30899daf249bf9a79b6f87732e71c7c578f92ee5423df4e53b94074d318718e29c923f522a9c54f207e7342a6a74772d58bee3c44d9c377138a93495a6d2954e257f80b03f451c8e891b1af2260a12e5e0c08d4dc0e4dcac6f119594586105218c501652104e80bb630dcb1ee9788c283cb4765ecbf5bd4058cce2f4effc8e38e67c8bb30f76bebfc551e777de493af9f3849e6f30e7efe88438a1b58285af304aabdfebafcd3e13c76791efabeeeeeef4ba4f2969f2a7264a29a54bd9b49aacd297a27bd3d04567f70ef61da197ced9ad3a67ab9bd22a25129a90e70c0116e28030420abcbe0cdc7e497fb69c68269a2128173e5f57cb342af2ec98465ff67c815becff4cc1f6bc4c26853eb0e67ac19e6d36146301b874000bb832a7c509dcaf7330609b81a890d9c47b4911dc623f5a69cdf39a44cb3a5969ad34031fecce652d50b840ebc1e6bc01c01bdb61411bc27a6aac99322e18f9e23f1c8687913101a83273c41d2d2dcb0cee8e5ffbe1e3037c9cc3ce7f2a9b0a4ead31235bccdcb6dd563a2a4f539a62f676bbfda84f4d5198a0c0708e48dd8003e788940d51a02cb5129813c58b121f43bdd44a9dd4482f9daa23a0a8d1498dd437201f8f14313bd478a48891d2b7ac36fc3e5c14d8d086871914d8d026046daeac466859e5c614793e63a8f8757850ed3c2a291cd5c74b121c6ef776766f3b291c5dcd832a0d8018294bf89005408c143143b5193e9300307c9962070402c0f0458a199271b47bdbc94f4eca62a488d9e1766f67f7b6eb6a53ef19a45e776f6d37e7f5f0a86b9da68f7db22e8da56d9b27853c2b73a197e7341b7f87b2bbbbbbbb650f0ab6866c9320d10efa9ff77ab9d0ab461bdb59f7b9b13c293d2576ac5df953865eada291101b156fd5c9f45c36bda1e9796fbd9a1372a6dbed96c456eb53857aa2893b565c95fa1feb541d13e5e54d1e99e18e526949ce8f7249e74719e5098a0cd51d254e4aedfc28a7787e9461aebfcc492ba43b4a2cd966821f6516053f4e24a52527aedc7132cd28b21f67138c1f2795eb3f71e39c7222eb8e936a86a9e0c75935fb719eb9fe136b66e59ac0b92345ba1ee329136d9272474aa95cff9142dd1fa9d4f5a754b4eaca1d698e9eb9fea61e79a2883b56a5bae4ba6749284d5f64957c73640a8f73ccb4a24bd40aa557d76d236d731dcc94ebd31d0c133ef5a58601c3d379bcc40d2b9705d21554a67021e608054d535c3db504973b36d51d3bccf5aeea31d7cf4061024a0c848dbe347a13099cd8b9a353b9ee6ee5ba4b5d7f1e9dcaddcb1d3de767ae3f7390bdff3240a2ef8f80673968e4d1555421c2e72ff831079f0b442136a71e3ec4fff7219d942871bbe27ff8168044e1874cc59521e3638044322cf81e90c882d78db17363fc8cf19e991be36d0eeac628c3c28df133902886ec08a5dbf31580443db323eeec618044b30a5e06125500c3092617c6530012c190c97e029048e67329c85d0a9e0724a26082097e07249ac0f29009c1e5791d908867c7b6f33920d18ecef398aaabf33820918e276fce5b9028872685867098c04102078704575c1c0c2e0ece5b2717a704555c9c31572ecec740229c21da0fe530c25a31d61ac0e2b8638d6b9f77b8b600d7dab74c9cb9f66555415cfb9fe4daa73581c4b55f23810dd73e0c24b243ae1b7b10248ac196007361ff02896042e07f2011f8aabae3ce7d8577ecb92fa0fb7abdfd725f4b44b92f3134dcd7bb40a2d77fef72595d97eb3d90c8555b60baadef40a2d6f5c254b99e19cf76168009e256f96092eebcb9dba25b90c30fd5a65ad65e4d8937b6bead696ee5b9b5d65a2bce6b47833bf630a18416258a48820a1f3276f4e6eb460d246a88a105092c544b77fed3a8c0dcf9b5249ceefc184834876aad3a2e14e941892a374954f02d7aa0c0823164a6ee38e34b0e253022052398b418f1e68826ee7c1848346bad74bbdd54a0c2050c324dbc6a2c819bcabaf3655ec4dcf94f4408ee7c5a8d0821eefca122a898829a536d9ca25089c2218b93275c8b2c3cd82f83a924b7be8bca13b0560008d0c5aa4b955bdf135d40a2c9480d527ed022eb4a198f893beef4383144d4ad5f65530529983441040b524800e5a6cb08902845820b9b5b7f7a7fc4a44d15140d48a84052c536b3e8d283102f0baeb0a34cc6369ccb9958106768b398d99c71a700ee9c5235dc2995c59d5c906cc982021d3c18920b5f0a3930890bae214a2fa6262da4a7ef07275e0a2e235ee850a86a458d515289e0bca8354be810ac6ecb902693470a750b0c2f48f6a6c7a30813e52d4d30f01882dba225061e448e6c19f38407529519a8cfc1a2659dd75a31e8d245959f2bb82ea6a46c8ccbb82e8ec840073bbb8ceb220d00907851e40d086a489aea1839cfb16f31096e7b9e4be9628fcbbcad3c07d5583efd503220acec89359a459250e188cd68e785b89b9bcd29f66ddb098500e0c8cde624e5b374cfe8c8858923900b7b0fc7791ba55190204284e0ae7f8e7b61a10907f8e28bac5166dd9ccff910bc98788209fdc585b12d278cbd5b715ae3a0947a1022013cc536e86d98d75f6e5aa8609edc51d4223814b19c7f82ddd35cd88fbe060ad49b9cd0b1c00e625c173dc74057566718c8a75873fb5d7a3206732c35b718cc83c2cd46b080d3ac744bddfe5c4bf9f270d655fe04d368878281fa5b0ffb58ce83d03f9c56c28f9d43a32302c9f27ba38f348d2bb6e1a9e11bb06ffeeee839b631bbfd7e866dc8b7dfef51f08dd8f7fb146c23e763b19b63a517f6d591b173476e57e574ce2879100bf510b7bebaa2e2b97ed3d2c9f11c15da9de1343f83c51dfdeaf663d52072793be8cb9de3a07339e618dbb9b66a346c038b6db4d886ebfb3b8b6d7cdfdf6fd8c6ebfbfde6485af08d9ed234fa463f358dcef9b1dddcfec6826fe4bcce8fdd05dfd0f96ef2254953b8b86cd886f7bf5ad7437965d867ae988172574daca3601a7cf93be92a754e071ba3ba7df908a5db37a96f3495297da3ab9a46f78d1ed3b2a6536e3ff912032931902f5172fb1d89c770fb1d06a6d1af139e00fb1c49a56f3813a6d1ef4e6e3f0e95db6fc39ea2a3601afdb0b0d5308d7e306c2ca75d856dc540cd40fdadb0730cd41f463dd891b360583916a8aec8dd907076ec1bece948d2153bf6111eaa27dc0eadd44b5cb0632bc16e4f550cc19406ca12942476ec250a8ce45098d24c1de5891d9bc93e15914295a62939d8b19b5e4c8ec3228527a6272a4eecd84fb72e5c18e9e2e607282b33d8b1a126c09acaa2089b348d6ba99c1c1555509921d253fd85891d7baa02aa21724cc034558769c18e4da581522e4d19aa9c9baa3141d8b1ab5e4f5db6c0ea92c552e7fa8c153b76ce454595a686a099424a6275f5821ddbeaaabb4db539c3e436e50688a936423456b779b26363d1dc1553b92bca7456bf01811d3b6b3e2d2d21c40dc9c98e7ecb80ea48921f89620a8c4b1451a2b8442186cb992ca52558ece84a4f309eb8c0d0c40506325c9450e1b2e4c51214253bfa12ceed892c24a6284aece84c2da521d24861d5e44e831d5d09b7c50d6bb29ea8f8d304538986a7efc6a40a282b382911d8d1713bb928235f0c29e253fe658a1d7d6a4c054a5262c24889b9024b4a892b29313938958711c28e4e0584a3f2059557f918afaa54b919d8e0925879cecf006147cf71172afa06046ee5576e055261ad5003c814b502ceb1bc0d0f7674ac3617e4864073c5d4c2a73484153c0c8126cab3fc4d0d76f4ac2532aa2e5a246175e982885c973650ba643d7143a2624779a34f4833507961c591a4263bca233838a9b42489ebb66407a5255176944a4762514837798b2262064b0b17555143965882e2831de5d21230b70549d29314a62d48ae6c51ca3145b9c18e92a975cb59110595155c344d81623f6ff965eaf64b2a0f9ae0f6cb2c59180f629c1a9c3c63e5a3c4926fa6740975fb5b090aa766c9f5ef97563c68e6a27c725a3fcf95f2692a197971b5aa8c81fc9bb3f5c29b5b94239ae4a465a85879b57a6d6a509222a5345de1aca865802f4a3cd95ae2914d4a59ef63205bc50ebe1448d0643372bd908ea266cae624ffc8f6b54257c82091d7d55951eb67452d57382b3ab2e590267487d41582ecb68701d9ed6f2155b7fb1a8edd74ab27a56ced2ef4414122da6275797786de6dc9cec12e1fb3003e9642d236a5b777f05506f244727a908b3d7ec82bc48fdc35f0f5f821af8b33297ee0cf01797bf077df78a2f365f28786423a2b21b52648a86c4eb3fea3dbe2cc028d1442e34d53968d7d838f4e1ab29a2b2c4b36dad5f7c49b90fad5c8a9fbfa4642e8cfefaa2864be73d9a0ff6397019070b619b2ed897ddedddd3d0f62a64dc76eda83d28e763de83b9deeeed5ddeb8feeeeb5dbdddd6b37adb46b57698fdad5aed2b047f557ed78ba5e7ab35dca1e3de99c41d26ff4ed1eece8dde9cd3b6ba55cc1c06030180c06832989599c9c17ceeb735e39e0386f4c84812f58ef4cd17df7bb9444b97f9dda703d832074627f77fb9c507ff495df0ce57fde4f7faff6140c21f43c1cf57eca64d68e5eec5d9c9e373de8aff733304862bbefd75b80840bb08dee2995219359ae79907d9ca73fe441f43f1bbe9c063e4e689d063e831f607071c4e934f073b8f5faee2c4fe81b0b4fe80b0bc7d605c597d3acd36efdc4f979a2e7c9efeebb47cb1934a5777b3f767777777beeb3d66e6f47a1e594b8fbf1abc3d677e151ec82633803c17bdcd6bbf75edf0aa86cdf762f1cbd1edefb4f30047f31508ebed19752cfeb72f878c4583b4b782fb95852e537db70792f5d9fc33de48077fd7d4e087ff4edff5e14a42cf4f2a752faccccf45be2d8ba14e7524f1c615cda458a2fb782cb5da4e0e17eee7d47df7bf672ec6875debf14caf90a03ff25be2803ebf7f55ba0582ba0a28e1d2dd11347d98591b3d369948ab30edcbfe6bece6377fed49f320dc77eefe70c9a72fa8d13fab2e7795ef764af2b90c1f673b7cbe512e56d47a1a707fb150463f9c71d7da36f6dbd8be3bc1ce8eb89b30fe4175fb03b56cb6d12174873ce39e79c5ab6d8c2ca953f63c8c7837a9aaefcd7e8197dec3352a52b9fbfbf7850470025b6fb640d17727d5f1084f92ed1fb1ed6a0025ce2d823a3ff8130c348f90a7388bd2bf6333441c8ed76f6d2fa1efc75fd9c52a34b9cb99e8a2f97e89910bbde832fa9c658c936b7a4a5284f56a6c2e4ae661645f22047727ac9a90a8220deefbdbe1fb712ff97962ddadcd77f200852ca693ba29c0af9b6402b85fdc04ff080afeb3d68bf2d90081499698cdffbbf9e411082dcefa7260882df4f94524ee340dfeebdb0ef1667ae7f072a20769981bc10e4944c8281644b34a1e594d32437a5b0f9359461c620774e5152814ad851e2a86efb3355f29c2633b3bff4319fd2e94d3e376faeebe75f5da0cb5adc58b99fbf725caed073c8913c612e51feb844ef1369e174212427ef918abceb8bd06c4eafa7dff77db1b0ef9c733a61c71ebedf2be4eb66ca35c0652d592c5ddc652f97b56425dd9d1e0f8dedfe73c2ca6f70becbe572b93e27ece77a97cbe5daf120970873c55c2e97cbe582c580b03d4ef3d6cb157af7f57a851cf0ae0b043f19c85fac000c247362b1e9e5844e385370fea3af1fb621f6e070c99eb0d8b740d87f1ee8c1843d831dc4fe85a3033bf060de97f862a059072fa7bdc41eb18379633171c86931256ceb7b7a767660b158ec5fb1900023b0b1afd669fd31716708471c3d02046169072fd08a218c7d77c2eef6dc0edc715ae3709a1c62200240d94f1c77ae164f974b70594b9698fbb95e5ec89f1376945d097ecb55410d33f687bc2e79bf594c3c0184f9c1f7531c5dff89de77f70b47ef8b8923901b73b99e9dc6379675edc73e87bb4ce8ae15c7be31317489a06520091359bccc409d43ce5bb6f1bd74fd50d390eff964ff999f681968fe50dfc0611af3a5ce7d39cd479f11e7ce2b5f3db7e70fdba8f7831f06ea8fb90e13669ecccccc93999927f374f38db5878164723e9db3328737586571e48e3b35c6da8ed3e7015f134c9897190887813c7e859681b2eed853c1072c6bc1c2ea71d846d8d7f320b681e463af49e1103dae1d43b519bad25fe08338682fb3d5602199ac7910ecfbb99b89ed86751048170693fd912de719023c3fc11751f032d1c8a973c471e882df6211cf4f70646b71ac5d1004c57e863a73c10745761a4ccce1b19c3f22b23018ec3de070fc11c679753e271cfbe63c2c0473421c9d30f63938e2009a46ecc170ac71639f43dae9b41a56d8873d188e5c55e3824d60e875e883abaeace5b1abbe17569f41b33b12c7bd2b7dc4e290695d66c22acc6526aca42e1017bccc84150cb73bf0b1cf38b9fb56add5937df7934da92696bdebce0f5609c3ef41f6c5b2d0c3f88ffc0e3f9c170e48543f4cad46cee34c1c1c1c9c9fa109325e5447fe13159ec1a23f99d00e25f54bec3895c239c36e9eb7d3dab73c9fc33bb43f799e41229ecfe155f729c0c1365cc8a9e48f95424426f87f22305e06052f3e910a3e94fd054f64f616e4b8209cd1bfe0637c18ce76c470b623ce18207ecf1789013a5970d04982c5883539580069795d03a1b33618207ce5e5ba216e799db367bd4e66ab4e0224104d53c63ccd97d5837c24b7bc4ec7649a2c6786b1afee641b55555521034d1a65ecdc9de241f25fb46f592daf6ba6dab7acac2c20f2a78d1dffceffff7faf8a40fbaffaaaafd2d18940fbaaaffaaa87c23e791015176a1f206118ef2f9e959595e5b4fe2e060f1f5fe0052b6ca6d8e4961cf12d2ee042ae25edeb738d08a49c4fbb70a4728a3d66276957b1c7a44dbb6743f1e4e30bb880c4e6089b26595417545703de352176fb93bc7c7c811a97a358f8d242d3e5a8a42fb788cb5149b7fb3eb42f36ad6efda82bb26e157ff8dd80ec8e406eb7dbfd21bbfd6065a6cdaf5efd49c51ed7ebc6fa7356b1077d797bd4dfd9a9228e2345a46d486773d2c0bcfd7ec3dd6eee21d9d1ab4924a8e4ecd9fde524e9a0ed124bf0dff153af43fee8e4ecd84c5f241353333593753925ede7fcdc71e24d5a86d61c34b5eb81a5843cffe9762967cff939efe998f9d19ef7d211823733f1d43d5826f7a1c5c4403558a6afa1953f4dd5a3c4ec266e1eda9f81b837171b05a621bf134b903fb2c5212530501776ec269ab73463a96fcc97efd1a858fbda6b799d7b1e9691b1ba894f5337e91b93a984eeaef94f0fe508c5a6d15f4397459cb9ea2a69951ba16fd06a7fb214ea3c76b968dcf2baae25d6e48f7ccf7a9d4ca4c91f1d9c1dbba99b9a264e0998cee96a9da41c1e4c24aefc190e154046bb4193d17288d8916941e3ebe6c0b96c2c175a2707cf6b87d2491e340a09b18d66a0a6c91c363de5ec9e3b4ca64fcf0e5fdd4973da1c3a0077a131d0147f6402b8d2935db1e3506d2866c3c7e3bcfde1153bf298dbcccece8cc3c809cb357a54b02c8e3db7ad1447a32b9f8f26f3d16c1ad51ca5c29b2815982e6d1ba54c9f7ed75adbf5dc6ac960f00f7d119cd632cc62e97fb114ecd838cfb356267b991403d13fe3340f8d8dd1c48eb599fc7ae79dc9e725a2d8e2f212516471c7ce758e568a8bb1a455633e076c2376e988c325ca1888fe055d58cf72e541b866c25fd84cf887829d856d34e8f4c2e152974b1c5b864b41d90a3d90a8d5b53a113665297d0ba0b0f5ebe5a823315c7939eac8d2a5f5e7bf6a388d3e1dc01256c7e5c0698dd5420363ce29ddddc5277777779fb25f3bd7ffebe934af041edb609e724e1c9df8441bca5db5acd166481bee58c39d597ef9d8a756ab31820f1c595f86a8aafacd15bed16a9886cc72c75a96cb625fc1a8902de6d0c6835cf4b1e2683ef619fdd603a8358dda320b856dbd477f874cfe8823b05feb86be7f3d0fd7776290ee5dff1283381575ef1267fe3571e6570c54bffa19db594eab5f3f0463a5486b2c97c8a28cdb1d964315bd5a9349211db52a73d4fc26abbecadf1c634ba0973e093e74882085be2394f21f61f9c187bdeb3f4f6cbd80c81fea33f931ab99c46ab562e24bd4d1a2af1cf2879e2063a06efdeb257d5848935d38434ffecc8ac07f3d28b2fc697f7deb5f624b647feec0ed102b4b9039a4d0f7f475009142afa7fc632dacc91ffa3487d653f6a4508d7f78aa255af9c37e3b246ef669320b8ad8d90e24efbbc7b97d5da3cc2fff8de5cbcabef043990cf6fbd7fbcf9d1d2cdfef5fe26cc7f7b306b0ed13dbf6fa198fb6bdbe6d2ff1e58f8c81eac7b6589907dfb7dee8b50309c9f52fa4d67fe26cfec7b8d93462f9e3fad6cf6fb5fe138d5aa251ad32185aacf572d487affe67a590b4d51ffd368d2a59c643cfb2ca0069eb442b9fb0f4bfdb1bcd910d699e68b24203153a8c913514b1446e49c9932f5524c9a4a8335777e7725411365a2e3ffd915d0be36e0ffee9ce74ce661c53dc01986760fe5cccdc62af19a873af9479824c93eedea07706c6175632b851410635b43332e490666882f1c577b2d25aa3ae9248b232bb237380124c0188ba228204575277b4566677c4cb512c48b16f03922dc858d855065b2c5826032d1654a2089b7339aa0a2e071184a91285829cac65b437510c9171396a851ab620411249282811c3840b2637de9021b5d65a398082439aabdb11596b240d4542c026290d45e342eb7294162c53522debbcd25a690650589ccb5157b8d09454f941b2f672d4151c9eb8f2441645a4086965aa0c915a6bad33cc550d449e5a628604c1c290145ddc91712c1525451452e48cd4bce63f4e4fac60c4c2de6470439b0c6ef6438341d513187861e9e5a8dc981f2ad6bb1c950bf3349b0c5551556e789f28235f5c7a390a8ad51dbfd62447dfa33e6ad77a33dcf16b90e18e4338b424943b32d55208eec863ae3c73c44b142fa4a2aa9031442d3de9d45c5145d2940a498419e248ca1b23382c490551440b36f93aa4900796a678818d1a1e8c28d964eeda1ddae5282757dc9ccb514e7277b44f83068c90220d0f52acf04413f904eadbc242adb5562b48da90a0852e7e6823612002ea85c90313921e28a53074435c86247838628635e4b9bc4f9496a66b2f476959bae3d73a2f7ecc60f065c99f389a21c907334850b88105304b6868152d545beb981699289076d6b82e471589e2c2b81c55e48b10b59ad76ad349e9a44961e5e5a8225eee68a558bf1c55a4cb944e8660353de09c308249912f2cc8265145b87407966680c10b93149535b65a8d2a92e58e5fab8146abd59418695fab81a10245052a0c946432c40a0a2570814c0a6b9a904b34704314370481c20878b805e15794c8d8e18b2263b07893c3937f2899e48eb21741d21434ec60868724ff503cd29ccd1a2b68b24082839632b51a184fa3064a22e112e4b0830b6c567022428bff20388cfb5b28773327542a31c0a188353621ae289bd3f48cdc0808bcb06213a2010b36a749a9bf6ad83c30b9a264933571c6a37e97664c931b6c426057b6fa9d68d44e048182954d488e0bb62ad27c83fe23db375f7456548d3c230aacac10c2260414c25637e8d0680055aa18926513e2512bc860abf906fd95f61824b26461a37da553d4154bf787bc19cc78d059d39f4f43ebdede3e1b0528eedeee2e9f711fdb867090f083032a574a29a594b286d736f76e8b3ed3e50d1ffbcc781835154fb6fa4e10e89ebe37c666f48db159f913847d79b98b104977ac5d2995dc18f4c18eb22b7f2e40bb1c756b73a36e672e47ddc2005d8ebae1703f64a11ad45208bac45045893636f90d82d04d10c672d44d863bdfdd8bf8e4532674b87df2d541635df1847e138e6ef747dfe64aafb790759b1d69b1eb1f631b5dc17510013c639c2fbf7f8ade112ebfdab5d2ee64d470afd49bb8e8b47d8bcfbf63a07ec97dfb7b6ebf8e22179281a5c50b2b35866ef317e93c76fdafdee9d806ffcdc969934f5934b998b2d8c273045600545d5c58b20605ef65e50e76b4ddc213160d48724aacc87a7ab09e10b164c8fb2cb1e3d36684c08eb44ea6596a951953ab21c58eb5dbbca209d7550b433898ec3834a9a8b49082d3224b0e0e60b023e3a67094bc48c17991e28497262b2f529ae84840163bf2540c8b6a4892253b8a6cb0235329cd2b8e7c57ec204204b6d891ab3e2c2cb0a4a061040b70ce958b62418a112324f880b2235b794ab92ad250596151c28f2a7664ac1615152b50210390a114a884ccf695412c95b2810000001000a315002020100a87842291480ee4d1b87c14800b759852785295caa34912e428884106194000218400008c31c42034662400841b56c86f71fa51417e37e4a71efd4006fa43a135d3dd9925766406335d98e1f3e42b414e52679ac03f9b8fadb2dee55c91f30e1778182846c5412f0ffa798402a4f097292e3f54c25f9bf50c5a21bfbc262d9f838ccf13512bf4298100996c1df0280bc341994c870ab3476634a005339c3ac35044545f0142aaafcb7d202390cf0e4619ad6342aee4896ff35f326500502460c97e021613232e30b7204cbe25e8d5798ee4187c6478ed99443a591507d17ee3f4044db28af9c22619afd3a4239f17ec93c277f28adcd5b12617c01685ea8c298b4fa3d65432d88bc90533c25157b51de23d6e2949123f0c450d880224a9cbfed72488d92710e8b226ed4321e6827097a4e04bc62e2e0a1d64c3da301fbb474a3afd9bc9cdbfe5fe9b20f199d9f0a907e4e9c9b7781d3914c58569fa7036dea1014008d1ca956e1b0e3c39d5e9635d5bedb8c92cc67d04a3ffd72f4696a91e01014c7d70bb6622a11365cd44828e603513073a02a899d4b7a23e85e5a2bda008ca259ce31112c100d47009363ef61b191bc7c2f60784631d254fd276a92219cdcb8de3125580c78bf433350ff14ba7523a0f1c12370fb631cd1d121e115a9be519f7dd5c922f02fa60dc1de55cb7e4790180facc5afa2332de183dce8bee354b21b45c62d69236ef2117f24b4405fee26f65e12f22c25bca9ece60c8e7d7dee37f2c11ec111aec2cd05792923b459d8e705ea15603cdf780e8559bc0a233c572a11c44f6df3d4f933733e9aca49e091339e428d9152f693fdc3274887c1ed38afac03b8162d70c32bcd8c4cd123276b829764c4c55610929aa9823c6b90c8ce21238edeb5e85faea737c4667331cfe8021ce1b669c6a789678861770b2a3962896746e7d7c7e501f18f50377e6323d27fadba284e56d9c46440ddd99b80e0fb7f5790c79193e6991c8db54f059fb01842936962dba14f3a73b0b4f62b1ec615d9d9c4d6d16f8b58f5f86305e517f4a1e0f6f61257baaffdd9b5ce6c01a6f85ea0dc58fa0ea13706ffa74c43727a1352e52f47229761169b983a3e23a562d7721af8002cbb03f866f0426f1e1801f62f99bb2933ba87670e11faf1e3b5903e40da79c3c87b1f4f819f9e01842de0b853f88576255d8a9ed0af895fd66348f2a07b3a5088497b9551c71de3680ae0322c1aa22931084f63b6492df2705fa031976583f01e568ece8af6c4d79ce5f5c59859e18135340d925f33139bc3a3731fb57177ebc4469508db24e289f37635f6008889323ab619f542c1f4b14be1d67b08fba2b576748c32e764558742cc1c40d583fd07b256ce20f28c3484c020155d8c8e2adb1110e8c9e3a4df7c2b33e58a9a7e91b64ea71afa9de5c7a1477e44288d3969f0dc465e5303d132924e1a10225790e101df415554b9efb7343201ce30b3b7ce9b45930a0b5ea74e296274da48b24628f4c94f004e06a5c089d88ff510c7d32d020ce5749f9dd54b6144708df323539aca15de33859670536ada3ee520a7b26f779c21fd5ae9b08c9298dc99eaebf440c4ff7e789ae30a8d221a2818c72bc07627527953e2e6e5037b9e2df569de38a95a5297d9c4db126b4c17b7054d2185549476bc3a402612353abe2e2d53945376a5cbd34cea40eb305f028f655648a4db4fefda2b4b67bf9d85739d13963ec899175688d8a2d0c33cc15831e10040f02fd3d316878e79a528b78a0c6435da6c25ce340d11c9fbc71456e2794791c94c214d9052118ed20add31cade11088cf3a407de4bbe8102d7d28132a9bec53c4881bd99f47398332c0cd517ecf456fcc1179316f452c7e6c3249c4a0165eba2ba422eae611a5465b3e8a3faf6b35cc5c1a216b649e4bdb8afd3077940ac2635f7eb166dc10edb075d5e1735021fdffd40b51fd28ac28cd742b756b4db53133ec632cb56f5942347191cd7e6302f14fcaa6645482478598055d683c69c1a592c3f6ca1666aeba3d043e40afd031d11c79084d629c932679ad7f8e465826f199954919508baa410c204cca2a60446307875713040b242b6a223352e6452fc5b9374f90ddc0684fc98add717c36c9fa0111575e43c2514e535fab39d6392017714691276deab6621e547308212a62ac848a8d78dae616383eeb0f4326fc1ef4b460d6060eea79b96318c8ea79513259e56c95160e62cec5ac56e93331a81a1639eb4b8a6a687f4c1db40cd5850b11a45db9b946fbfd284085c10500c37e26354723ce43e00d335e227acacae44a6b3539f363cd35fcc772d8726ba3f44ba304f0c2f9722b894eecc9c2da2496672c8770a2ca56c63764ca5738923b8b37d5c42a0251e6b66d6c7d9abe487c1e7231dabedab3215adff85e1fc7f6513aed6eb14bcb70c19aaf24ad82d413e364b531ea42f54cc7858d969f07116be9bb2e0300c32915da84f61a6b1298c5472abd8e95228a1c660a62563268545b54ecd57ae16fc10aeb2c9eb4855e941ec079276aeb134b1c76d03d8f5245b8379f22e82b500939b4bc17639f547948145660c96af5ce8a391dc34fcd46d3fa2b3ea23c6745f799f3c4d42e8952de67a925d8f1189692d24d0f8049f239026bda8b68ce7d62a5c0ccf09ca400f349a7631529ee4e26e7332fc4ca9f623c486b32b20edd172fccb7354c6d032b3b9ecf5a5cb960c5cfc7c12ec65bbbed92983280356d989cbb4ebecd6569020aea7e20352b3bb2fb69aee8a6a51df0d067ce3790361571e73820820437ea77f28791374a8904907a70ad84b30595491009b21887301019d8cde5688095ca3b56317d39894ddf196e1128c5629707ee3949ccd528ef7c4e7a0754c231dbac62e0577390cbd9311f60ec3cb8e96c3d4f60a1b4019771bdde4bd4a7e4b780071829878b16304288c69aebc0d34444f6ad1a07dc9f2a3083f214f54b53511246c4134be6b180219d9fda0be16a1c4d1ca6671df8f1d72fc22fa607704f52c9c810cbce163c7e8bc8843a770b96c958ae5387cb6df7f36b210fbb3459225f06b83e6c7bc8756f9403ba56ccc47c59043d04d3b750a516e60f56928e3860504956004554f6b7a0affb84a0b536980414e880133edd9fbea08010fd5c0918a1ead00e6810c51030e0d6e17c14d3ef862857e0dd2c503b7db37c234497773a5483ec6d7d9fb7beb43b7cf25b7ac86ef370a2c42ce03b60848e6f18930b6f9fe19961a6450ac39d9a55de5cb6943fe226ca981a68d77a989aa17ab483c4dd957bd2ca80f6e5f9f61c0f5a5f5967b02d7f6161d0e58a7a2176bc64f8bbb0ff17797b7a1796d40b092b78d907b9970055e4537e2a6e3713772e07a58a7a326cd600c2add81aba8dc9f1466474ee7e126fe0e021300c97162ca537083d4c88bae6313e61c46bd7ea07e456504707cd1165880b636213703c068645cf205d855307ced1c81043a691c2351e6e571d93375ecebf127be3a3cb2c2094cb8a66d47dfdf26071603385f8661f65d099ddd7c22648f6c5e861de69184ae19ca63ee7a1d68607f56aceb9e369b12e7b1ed37261d9dc1361910e633806d77a8ea7a067d6cc101f39a2503ef46247f505726e242c3d2103140f401986032a030bfdb7ce5a6b1591dbf6381ea31e88dc47b03634fb1be460c0d9d7d91d7965e67c9e70a5c833de5d52cf12f64e94ce265efcac479f97809ff9ba13cdb4d7094635a1a63fa04eba1b1b1d3b71b3f2c2b2716cb92b0aa7922872de90d4be1bcf06ea21cda6653e7f2df5f3aa0b33ebead26ab7618e26cea3040d8b83406460f79390839cf83dcb63d26280333194036c26b5716aa10542667907dbfe5d3f1197b4aa0c4e85596d297f8527defa46280de22946203a49a2b48b363850ff260061072a49e42a7aec7b10d5b4e672716cfee4ad125bfc9741eaeffb3380c34cf02faf2be702901256a5ed80e4f684b87ffd87d962c46bcf7bc11b667d728da01e4f5f17adafc0e7e61baf43e3636b695266a2433559bcf5d7698fc913c59b518aeb4b6c66e8e8c37d30ef931212733329cdbfea178dd2804411f2b6591fffd5144a787f4b3cdb8cfd083257d81bdc4edac8fe5c2844f8228b6cc45da57d72b8fb0f62f30e42bda5dd50fa2e834090f509caf7e35bc5a4d43ba829916776256218d8d43d1094fc580796356c45ef880bdd476bbe9e933150892ef8a81b897fa46de126617d52c23c9d9d0aad823cee0e4f2bbbc0758b2ab0ce0d4d343a89d4779083544521a3b80480319c6f7d9587e08064381416a74e492478bcb0830a90598b90a2e3c115ded0db6fb04a438da79ca254c54bd77558ce2612800a7db2cfe7917076b8df4cfa1cf1faf20a668dde5d128fdca12374ec36fcbb1f77f7f1c9ee2bf12a744b6b53137edcaa9b776ea94f34c1e4cbcdf2bf02092e0a691e2883f16c852b119eb012640e7cb697426b133703df66e6615fc8753cecf57e28ac8ae88a6bf800b5da25343b89b6f868393bf0c79ce2719fb825b9568569be30bc3d5806aaab6a19f2d056857d275d563211d0c11d40d06f65d7a260cfcbafea4a6bcfd422c60ec284abb50a98465537bb4a7c83322aa591b6429b3a6698913153b799a0bf09cbefc7714b60b7ac6d3e20f984bec3620f53ae44153540daa67034fc85b09aff1f29cc015478da5559651122ba8501a67ea0ec02db71dd865bb49b381374fb84a23e44644badb7f17a6c4f606f6befe6b6f89a0510a10677bbb4d7e9e165010e3646b67beb85eb102a628ba121075705e536eaa2d6fe55bf0fbcc4250cf007ea711cfe59e5b6720850484d2890d591e5d12de986e75afceb10620b96f6ac0e27c8ca34fe4ca17b1a0852b392455912f45817f39cf02a968331031c823ed8e022c0d06789c61eda6832e9c192ce590b5ddc885d74fb570828880086f5ba29124dc73c9e1f18f8cb83a1eecc8d51ffbb0bbfbf4e671ea104004c4a97b1aaa346a87e02fe9373371ee006d5a4d4012c166c0e3f40a72c77a96bdde65caa49d3f084aed6f65c532abf408ae08cf3b513491a7e09140d7355c89410f16444cd855626314a3bdd0af929772252f17672207cc8239ddc75418d3aeb0e598d4263cd3eb0c4122b866b042d4fdd6cd32b200b0ab1e8fe08f5ca4dbc302807c2cdd494b91dea769564b1aa9d4d83529d1d786f471db47f9dbf812c3843e433611dcd4b07391db4d6e4db7ecff3cfc0c1ade2f451be34cf7d00d86a482614d1b015dc99437ab043756346f361466ffe977d8c6a4bedf4a37414cf7f8814a98a06a4d11ef139081432eb85e856f033ec62addfa2f91c683f52f6bc17108662b6cf4a899fcbc4e840b858fe3127d9912e4ba84352f88c8d34b4338473dc94c206deef759957c90157d712a0e534a485a8ea755d2acd8ff22b5ec754c21487e5d96551e275b10e20338bb0fc243cdf6511a04d66dbcd9a0c59b1657c9a924c78be832b1242a65c02f041293079ab2c598aeb600211b909fbe694ceb1a91faac3f8037dc370d8af630e4637d8644b517309f9074aba1c8edbbfb857cc617e972f10c672ecac8da1ccbc7c680d4d8e38dc6f83865446736e90b4b6540e1145a7e7d4f201833df2c22317721320a43f922a629b197460b21bac26470e3012cdbc5d384c5bef5209805648a41a4c5126e3c7129dbd1d48718bb316f71012f504fe1687cbffa8358f0667e528e578a0463ab8839ba62bfd36e8216d8d9fdf301723527c20f87cfa612652e6f657fc92d57f98359e733d481e8f1cb9ad14cdf7f725920572c678782d9271c8733aa1629a9c9dea4012e5b0e8117f1bfd81c74324a28d92661c9376196bb0ee04e3883889fd99a18a4131d189d9a0ca03e73d0d6c167d53c7f58db13aca35a323c572473bd03de31c1d69aa4337b8a794cf5ea962fd49c035137dc600bdeb23f5fbb080f30e7bdb3c5de8a24954aa534d0ea27fc20270eede74fc4d270781c39119a859af32ccd19c9ef0875032448bc93808db1c4304694485f53b4dc507b52879a027adf3476269a8434272bd12220505682fbe978702073925255462b3a5cb14d90742cc29592266dcbbf28dd6c3571358bc6428fa28993e333189c60b9821cc89f813ca58b0c6a3da74fcf0387f888f1943a1e3306ee4854577b25500dd28a84ac5fd1026f76933435ece7f38bccb0ef16c744284abf84f3a24b9f03d81a3c09449f6e2e7f684ae73749e08bf4992cfc9d7e4e95f3045ee15746c3aae5ff62b10cd9fb705b9eabaad56ebf5e1d2a4f975dc27965097c833016d59eda716530ad9e29e58e2f2ba171399cc3303e5ae3907584bf5346e6b119f0b0a9fb2f5a617bdefade95f4a36e2861a4ccc047b3a3f655b4ff9f65ed139f7c1308f32a22c6f5b56c0d38054d024fb26ad4b7c6220ffd7912ccfab37b59590f1a3490c53afa5870b8463d7e6251a9448d575e4327039a2416bb65d25cfd6ee136d595e6bf28e96b60a58590a805f166d4d0c8ea09eb431a81be04dcb6a5475dea07f0593afbc7dd138a9ff99aac9778bf440d3e8473fe34a663d0c018faeb09bdd5c1d283ac464cefbebc942b49ee6f4bccf3c2e59248cd7bd3c5c0d93c9b68e34c70403a73e2d839b36ad4b424adcd7eca3a4b34b2e4aa294c847bf977559c58eee92299ace0fabd21d7439c6659d7c74ddc78e9d0710354e45f45f6fe396a247be17fb7067eeb344b000787dca4e003d133fd045d75c97e176320d3264fa91324411d3d6080cb99eb2332a26f204cee4d7ce2d8d399618481dca8d8bb577a183f1ecf971c9b94ee228dce1384ee7c76a9715861055cb09a933ab602b2920d41091eb5c14ff15809430c901aaffcd70ecc051e87fa6d62f9a97e48e4a60347f96d0bce2e8dec8ec3ecafd291b9a2dfdcb1219650d1bcd0b4685a4b6302c3351a5f030a799172a6464d3032889fbe1777d41f42a5959442551cff5242c8247a03750457c416f0662b6041352d1435855348f81566769593b1c8eca46e86ae44ffa4eb90a2af3789d3ab3c893b54778742271342e390c1c686d59a22586fb3ac42439011697dfa5127a83f1412d36e3dcc7697087d07b6fa472b9abfa79535fde6adc820c5f0e65d1cdaec158fcb0fceee5641ba6ea5623b95410ae1367bd55a265c78805db58af2fc062916dff637a9ccd0afce96c349867ae41c7a1686d68f056219aacc81a8c291930f9f4602a9e861aa0af51710e05c594434d56445f23cdee64b8c5d43eafe2e6904e97a8fe59b36c8ad1e7e8574ba29ee1478a180d0bbffe8e5db0eac4b47f9f78d91285fa3414aa7293409848037188c0750cd2a349948b7c32795d0e48b4566f7255249f48d56fb2d1785ec3e4934a3365cb35af90b02ec768685ba199072041c4b3d6fc7f099495ae99da95255adabc5fc6b3ea26ee056a445950003afdc03891bd0964ae84e6f6361b98999515378da4037c2b2dba5d5f91da039afa378fc7a382be0079792ae9fcbb5e2920e4514c155eb025bd08bd142b5bc67fc3772fa6feb355d02c50f969b281e43804804b5b01703c9fc386888d4f3bc808c61412de13a3b70e66518e56ec8de304b2901d789effa31cdc8d12331663a1a9c61f62c0584c99f5ddd782158e3df32f5509d191e06bf97defdf704688d80fb48b68b42b0e009005d8361b758ad1fbcbf655c4b2a965449a359738c91ea7f13a528a83880022c893f51020941815c8f6996c36fdf537c9ae3e11272e9334a57a911f8a563c47089a4f33c6b05bc95c002db0e040089ba31503222b6b54525837bdc6966df58548e790a38941b7b28df87bc6c7c9c66860a15913999769cc125ef58f83203833625062ec5675aafb700b489366786c14d621ca9edb5297f590ca43f71a2c93211cd7e7338dc1dd8ebb1015c5d12424470c46a0dd7a8f2862171319cb482092da1a9866e56b5f20afc1cdfa0247d783368164409d05f95807e1f7fdc143ead9269e916c125dac91d2cc0b267fcb641e4c08a28067b4508b77d106fd4849642696f820982df7344ed9910a2004ccd8704c2cbf10917ad74c242e5808f9e39122299a3ff4dc66f3ef957666564a3225cbec265086bd05351c3ecbff9c41219ab8cf3ef1ac58501442b083c09a8d43b26b8d1aa886ccb046d04e152f471b77432816139a18ff6f20edbde91351ce7c4f4be25a6109812ca62dd66eb558ad28fd6decc03263fc7ce143f60af7575d2048213b5011b78ca3e2c78c64dd6e38291b46b34ae6abc1f188057d17296bbbecd5b08c0395120211f3e9e2d588d34975f8c6cdaa3929ef58fd1f393b085bfb0deb11b433c449961c48195ff4011a493492fba7d4e54e94a3c38d1521280116ba5ff19b627d858898c403b4dcb0d1c80e39fb9acdb1b836d6a7605e93281cb1f64746e6913a8910e27abc301b4ddf69a77038cacf1f36a4ca51706aadebe9e0313585adf901482c0de6b14154724ff40308a4076d024ade8b088441d7fe1d17b15bc8bed23921b6101b05f9436b382ebea409e6be863b340cfb103608a87da97775e8cd056c688353112942790ed56ab9da46322977ba0c426af2046bf4025cfa04ad8809dd04d9321f7087b135cadbaf5126dc5c1182421496f8b50d230f5d9a103f0fa1a2d42b4407f876a92b416be125ac60f0c9c10ead93145134cd1cd8b968768bd42af51da46073c0a19dbb405cc30976c1f3874324bbd425e846303b8c8c01542d6863233a122c1144910a8b2396ad987306a4a2dc8273a40d741b60625150922d0167738bac7e4f6b6ca52ab1cd0ada5e55d5d5752d9019f707206290d49b31c3018af200419258098cd159141c34d1fd803ad74a382a4fd32c57d11a932a0c6d9561fe176513e0cdbb61cee338fb54769c20084255b3ded5093fe0783796b18b85d5d93e650f5e39c53e317bc0e881a75c542a36653e517bc0525f5370c68487183b6e3c4f7e9e9ef258a0d67e09474c70c475e761cbd19bc62cb9714c79e9cc13e1d884e4c226f133e181068456373ae0cdae6b2d141dc72b477b0d864a57f5b5d3f32b6bec7e8d262d8a694a030ea07071418da035f40d4bed45df3c18aaefb09817a51e667e81e97d077849033beb4ecf4be80b504de9baba66bca16141bc89bdeab8bd66af424b266e6fb79d09ce85775615d43a99ddef8ad2f6813a7b0f8603ab8f6e6ff9b3064fef8b1843c8915d4616921e58c93593760469b8dad6017e6b289acf31b8b2ca59570ae8bbb622a1538cf7bcd7e41bf59101d56634d39064217d21d63ec8cce3a1b746ef54fcd124607fb34893792122e5cde06d875e2a7330c20383d2af3b07dc37a3389d1ec6197ad2e0586a46d700cd656a339bdc00792a796e3a07131a01a9f47d872938a326074abd889f81f8fad0f2634c1b8c6eac8af67ff9fc9ad0c1a862748e04fad1c8454e6cee6fe83804a0f16f68a6b4737aed674b5caf706e8ac8ef421e6e7f434e799bdfe8426c359764492900d6a7f1742e3a32a0277731c82430cb7bf2c4fde836c50632ee7cffdec82c03e336a1042d3307239ced5b886bc13a23e0a7bfef22c1721d97214e8c50fceb2b4189a583610eda9ad21a0a60866a165a799184b520baace846a7e79a6ac1dc74cbc53c67f30ebfb622a9586a3714e9592da159a5debe8ef376708fd5dec1b17e83d5ebbdb41814935a69ac4c5194d13927bf7fc501a5f5fbd929251f9ec33800c5632a1236f844a75b49106adad7254e06e48a1b13ae4cc0f09463d6ff283e68cd7a01c52720e0c22c9140447295798afb48c93c242df820ff06d70928fc6959be3a16cac9854b572de7f7006af27ec864b576f3b4222d91ed2aab11e66ae912c1040e0a1cf1cdefec6eaa263bbb85ae81c724145f093520014597200a9710d9127f0b3e6601928453910e17a85f1700038486bd8c132556b894455130a96cab26441c0bc9510a2977e87ff002991331f5aa8d4700632fb2f11ddaef0203ad8c14e2cc2cd7dc0163e03582a78a78074d103cc4790f0325056dc2f044b37a80ea8397ebb9581c5d22123415506f9a081e9424dd3413000460cb144a8f093f619971faf6d98a3bded5c8150e7413166232ed59b2a4f099696c62f47a84c39930d26ba981b41aa55d0469519d6a4acb8269aa5471e45c94652ef611a88460bb31c7bb2596fcf65929e2ca36297de3ff4e6027e4d503135649d8fdd91669d324ed39a1061a3439a53a5a60a326dfeb45eec36f832018b5b444f0bf1524b45cdbcf6133bd96a4dafa1c9efca189f8e1260cb2246d8f02c4842406161ad13ed4a55a87b294eb4789fd175437eed5b028727ffe7439b1c3b4962407c30ae26991404edc855c4a01c5b54010584e81680ce4808f1ba314b00ccfba5f19c02ca4a4e14357bfc41f98a3377238f98de8844d1c3ca022591707774f4352b2a1c5d3e856ac9f5660bcb587e8c3580d6c76fe9c396e06ac5e8040ccaa46037549f8d900d9c29c40f96a1863c86aeb7c375f5d3c484fb534902702304fef264025ab229fe65e509871800b224b20ccea288fe7a38787a3b7442f300101b79ce690583384cadcb2546d468d34318fb3280a853cb6d60bd4f33d2143dfd03c062772ca6c9ba4317a3b61dc658da3e35c91a2fc6e2b1552579c52359a109e27667e7294cffddd04a0937808253b7f84dd977f9a1e00b88a219ea7dc50d329fdf0d9d0ca0c23882d1aad09353bb1ff4a53f3250949c67d1da8d420556b4d399de51a0c1dc77b64dfd5c669e6d84efc158fec2313fe629cacd1733c4a5f8c71a77218f2a56950376c64d7302c4b56c31fd3d44959d6afad7bdd39dbdb4d514855c480f23d106dd50844209b0a546723a4c4c82b2f1d71dc3460338f8d59c139c842d5b22de4709c1317182084200a338990fd59f7ce54cd90991d2128454a0823be45aa93e14446f90d808f06ce74c76f3ed2ed03b0dbf5cb80fce8936eee86dd636dba53a6c66c51b567c1262da575c4e3f6bae55ed01830892898fa9578f4596ef081fc05ad20c6c58fdd0dcf9f316b42eff73de75ae1737f10f5f9aa6898b7c8838197a4cffce9ddb44465c92bb6e77a1f79a172ded3a893f17beec8431da52cf0ecc0f78ae3d6ecd3da29eedf8769669d3e9bc7e6e0e59a160110e4e82af2894800721638c65f60fbe3e80b6d89fc99b52cbfb50292f3c7f513bb221f42234d64197a56260bad1f06df318236cea18bb5700149cb01473e335d7cd6ec47d4ae8c902077106287d67db85809837bb821064c9ae0ff9552405a68929e054cb5af4d845b6d342184b55aa25e7efd55e379ceaa9f8672817762842a1958162306ff629d0bd6d6c7e991d61c00343e2b400244c5f483a163a9a76ef38b8e8750e0b07d4be5716ec971e0eb409db861747134338c8989bd4325f3d7b5c999cf7ff595f841959225f04d5d170522941e4242a972e12f5abc280ccb6738669007b59871ce7597f9596b003b41c233b50f35c950793c3a625898fdada168fd0d18c81db19f51313161545845d8db20b339eb7acc5cfe4119b746d671b1e80c3fcb29a0c86b22088fa77ac035a07eec5bbbd364c0d7c4258c3591cc102844fa863353590206604838b78558ecb075a4cb6738bd2e4c203ef9a6990b8077c627f472d0bee8c2140ef111468d8afa022d9d76117ca87b7f562bd0892f16b574a924ddbee141166977844943c09b3e65004a5bf0c34ccc6c60b70d774f6758b860de80cd08d5dae3d0951c5bda3eb9f3ead78e19b26e2b16cd280641c4a320c051ce540433997b337f5f2110be182a954eee183ef8a93ac4d9f250531e5a921c79e78bbde82d4995da489170a73f9e4db0df0c203655f0e52f41305e35365908131867fabcbe1a54d0198c1d001d11af417a6e0a7e75a7fceb8645563e33fe0ea2aaa4c4d84c7b03c7f05ec37e2bb1e011bca3f17df3076dc4ad2d2d242c42ca857cdaed821a0a95438cb5018b43e305a8d188b890adb3504180c7536a90daa81ec34130865db10c82f9501b67a8f50a0c02fa63b8a8ada4967fcd9d7096f7dce32d854e0e25339b217382d835c555f6d0d840f72d0b1a8f8078f55d7afa537ebccdc6a75981a74f60f7f11b600408a7e417ed66b889f940fdd779c7bd194af212853687378f56a0b70fc89b47aebc32dfaff70e392f74382fd14a06f4e43a74479bd371bbf86a09ecbb77bd6acd63e2673d392f61330dedc85c4531316016c08a1c3ebe72060b7a0df1615da352da4b849123a5f0390e3536b2b020899d99ad0c2ef0d9f963e0d203764cdd806f92e8485ad4537bac7756ed400946897d756a569b00687daf7f519e73b75161f0cddcf79cd4ab672e45a6d8e97637a3b2ed1e520a983782752ec266d22ab3b75479ef5a9ef74f5c2ffefa4523dea50a3ff507dadbdf1bcede271233116c70a9493fb8943b903b84b2b340b63109b327c12914cf9f200eb131b0e0df5f7982482c37bf784881c7d8c294221c8b989cf0dfbd0367f07202207ce08d0590e9507db901a2def4478df167fc7801bebeb7633ccbbe3aea84377b533d6d00c90cf9ffa7e20d8809bcdad167cf47ad4ea2790177825eeea966c020f83dccf087dcc27cd05c1bfe448419ca557c94d7513dd0228139b7ea58561c1ba303064f3b5b90580feab4ac661677cb373f9d52d8044c40e120b5b4459c9f795aac1ed50be1c1d18a72703a42ab317bf108bd2c701a955618b240e2fc14c22221085061c28cbcc83c491780115367c83057491b516cb15c1fefb132bdf617eb942fea8cc97526fc2ba21cd3266d82412876b9a0f83578e7faa3e631c16f8749329eaa03d7cd9718b44d2c8429539cfa8eed9ef2d634692c048645e5f2c641539c83563e8514f758bfd2850d7bfe15024421875917ae698cdc1108304ecd83539766e0b22e820edd45121b336072e67acebcb2e1a9beadb84f06248e4f72b0a00288ba59f67ccca91cd94b56cc6c1e8ea77b385bf2c7ce0aaf026c12fd2d7428a5e482a0fdb2362ec17be85fe971759ffd054919a45cdffa348ab0e5e812030159cc03b19d78b2d57be5c076a2d09f54a91f91a49ba38deefc794483c836ea63ed4252b1312f2617c9948a867d6d7c9f95ab61fd6af15f95d66c265bf9a98fbd75c3e73aa30e0af0f8b1e035b13a9d0bb613e8de0556ae00be92606bc95a90ab20462a7cd6b5b9b5fb252926704257f87f4e3887eccb60620efbe4d17cb84c9424c342f989162ecf423588cf36aad32cc19b0b0eb1b1ace3a87092bd070b0464216d203aab6ddef2b2758374d2f10094b4152c7ba5da4995e89bd4765a7adbaa87d5762846d79d52e6621c10e203ba175e7edf5ede13e761c2cfaa5cc17e5cb64618f69853c091ef9fbbabd8b57c9e2d72a14e23d3ebbb67010d8f472c9805423df1e68dcd526e21891d5b0f235e7a59aab86e75f882fcfd28fe8e2aebdd6d2b19d8b9b19a2807349686eb1606dea25cf97b2e587d203232137924fca1db45dd2be7de55b6a760dbb01e5b2661b3381994eb61a24ccbda60bd322646a7ac4114ab145a1c3473ea36a6628a5fed87a616fc3ea760ca41579e138b62731e66143167f791c88a67820b56292353c9f9f318db9a142be455127972a298055e93315f9e548c6518e1cae6fa1406b6ac8f136f4b0a217d2de576672ea170824b1dcea9dd37431707a0bf6a12d411f163bb30d90b5d972394d7b6c32e87b7b5b7b88f6c88e265c2324565f92fcb17008f6c5f9e99323271165a0aa6a81494490d7b935981dc43c5296da0ad648d16551654ee2b7a98bbe0f8d0a5841d2aaad430aa8cda85a2ddab1be7d2049f241c3e5baf1ec8d341b36f8eb49c1ee56083c6d28f71cd6f4653cbd98507081e795a066e226c0fb4d2c6e1fd7fb7e177bb0d390319b4e0948c720ba8b1ae58c6280247b777a7324eb1d57c9fe7330ae227f9e4a49350e44726936775937367fb46b624b6f69ce3a06bf0e7f9480d29061d72c495e0d7b4c9d66289d944857fc37925368e3c34d03429e1e5273bc27918535a15e7336c140ac8124e9c2e9b561880179812cd08e33ce09ea7ac52197b34c9da3aad9435be0d6aa53357fa1303870de46ddb4358694ca249e4632b25d56f4b458f9b0dcf5c91764bf8a94524c589c3756bc0b0af4f2badd46e6ee0ac4e6156723f6f6746312a50495dfe4d5a1f1fc85412b61363b5b1bf5f6d29e1e2c6946868852701d5bb057df911b71f550481dc191d8901a445c12b698ade5583ae67a0f226d1247d3fa6dce4a78a0cfe98d9b4b0581b47b88f36c34abb8e7737964df85e012e77640ca78265a13f4d3428329c1e53f6e152cf4715f8b129145142156d8dfff644233ff2d175d79f87bd37e1948fae2f574b1d274b0ec13bde05ef8db54f1982cc795a40119ee01099662614e7201635d490e7243caae301d912c0b703d8dbba7a78d299d1fa70293202bf7cd10a7f8bbb3bd231bc8a97b0b0060817e13e747687db9876febade1be570d631772395cb676fab1fce95322ec350d9672423da22ea02b930f3bfa90bda31dfd614047feed67c1e4cf11c4ca91fc2f6b74f5383521e5c1cec9e3d81a8e5d948b5400c87f9a0fac49508eff3e1f95911d5dd13f3f1df388466a3af4b1c397446e11f2630b55b061d9b76f2ff7b71885a8ad424ad8f4da9f5b901f5707b438c2246983018866826cf7eade1b921ec3251f5a6283d37699964998d56472bd1a08fe463da4a91ed118af90bf3a7ff0d49fc554f5aa79e38836f6b19ba777cf37f8241aa725dee6d7fc0f1dbd3e059c6b69b75dc280217426f1f3a251dda3fe726e24a0bd10bda449bd55df63cb9bc0ce77f0a32aef85c78d69978e9de58392a631dd21c0dea658065d1a5c2597763c846ac512e0cd160e9448d1d7cda8a5198cd8a825434d4a0170cc266aaef5a78c7d030ae80f136dc41692473b059d04b1e972943d86086ea019511d615e21272599c051e54e458b5873eb3450437a251dd7fcaa5f6b549f1cd2dd1c82dcb680be32a1af7f38f20067e7a249edaf75ff4699cf5d25b52ccf8c35945cedb620f0b9236b267e804141012da3b543abe6bcde1f1e363621ae94da82f61cda86eeb1b2c0e4523035499201ed5ec1933f73104ebaf827eb8bd1fbaa09f829a7206e8369619a47be9ef37f6563e31271c9cb44bd580747ec37cb50f32629a6233c07239cd8ca0b091cb24cce0bebaba8362701d7570b8d13a960d6ba7316ccb9d12737436617e8cac9921a7e15604cb692da81213980b4742882c2cf0d38cfe221b8deae1d01bb26e4ec56486e02cda2b172a7944cc79857a8744533ecb0e5d8d59a67be46712c48ba5d2fa34fac8fc8b3b12f3bcf5a06844ae2c44462761d752a8de2cb38b0018e868581ebacd22690392810704661b5b3536c8281fcb7e1ca7f216892a1b09abe4260a96cff14138920f514a3e86139bcd09875be55d9b45e8a674980170c2731f53f221f7937c9474a400d35900bc75a41ff4d50beef98cd403e4e7b30a89041d6a107bb376dccbb081368e5d5e64828a2e302ba0b5123c048249c60751ae5c747716ec4bbaecfc1c687ee3c34bae01e5ca8c22e1d791f12a72be28d16c4e6efb85c162741b27f2c9d5c7cf704734892da45aa0cf00062c390c5275fb52c7a2f900c1029afdcedd88cdde606925011a85c42d6f3c70af95c399ca9a7322b5cc23e58543fec4ee52a87532d33106f8a4d0167e9b7db2f16e016a0c86ba70fe6c03a712897083ead0d3b31175c331db5963c30408370c08b11e7f457ea44954a54a6f1c15ed771eb2a0fb1335a42967fd05809a1f6c3c3376a6fc80657a9842eda066c91f9fd9043b889f822f83c11fb28e83fcaf2864e5785d0ddbbf60da73b86539feaada1b551eccde7df9deb238a963666179416234587ffa89eb4e19b2a992ae6d68a07e18a672a958df1c69c1f872a9e2c9b2cea80b223aa626b3b23d81ce4a598d2d7c374cc978fff163eac42c84a6c0cf88ca6c279579ded7d6f621ab72e180f5b0279c7b72dbadf5e04ac30d0764834e83d5f15b1e50a9046a94c6fb653af7ef215a08428809b8a58032e78dad5ab058cafe3124c000144920d85333c62dac934dd83c7060d7dacd811dc81ad13275a020e202f8f9b8632577249dc1a8a2dc25ffb727fdcfdb7f9af9ac6ec8a6f096260f7af709b689a22dd2f187ebb73df5e439d2f9361a14b98c48f9eca0c5259abd3722dbb3bbf98b83ca4f440b6552b1837d7aef7c607aee0a78e18a0ff430591b78f62cd99fa314bf7396733b2e7d946b5ebc34d0d86158bc6175d5cdc99560970f34e1aeff5650cf47e4237cca0b53a981862d0e8931b0ef3d8500496e04dbebbbf15ccae0da299dc0fddc597027e4eaafef71bee2d6d74829fe1dbf3d2d7836115a80cc5aace84838ab19e5714dfb010b8c79b0008c6f8d350d3c8687dcb2e48ec269d8ccfaa9d773e7207e1dd28a3857144a4d52057cfaac8bfd41a34321509e9b52c16358b068917d5832c6654647c4e2d305072df04c35b2178bd08b8c29e91a417128ae5ebb95df1a24a06d1f1b1eab3b5b1ddf1818d613909ce3234242c2aa8cc5e163009bd4403e03732846ceadcc36fa935d62209bad635a69c4e13a71f4bed4f81a9d514aecc664cbfc638763add6f8b99ec84a280cf5e9683e07b20b11f1cb0fede3eb5ea69af42e40ec973ab65fe40f5bf283aeb72556df84c4808cb28231132532d2dca7f78a99c857e7c67ef2b1357b28b994988d90a8640d8ed9083d315e71d5162c7ce8d08d4660ba990109ab991ac399b951ce4c46b3fe7873a39021e7e12b04e653a305ba793a54003c46769dee3197f2ad465a298cbc42f3d7a1f480efe98d0253cd95342c4cd22049cbf0b0c267a104c8b33f961e2af95026fbda6d204736b18b49700f8e87a21d096acb73dc1a7ff29f49032c5c2e7a45594740fd4800a368f7e08758f2450a803a717f5ec012549c04bac9163b8a99ab94cd5941671781c0f8fcfafde5279e14be8e5360e18d26e85740bd89085e1df9bc3189fdd34c6e9538f47d061a2c1fdf2529f4b2494a5f0f6de1e40b3156dff9b59f4299844a507cbb36f7bfda9c35454f47b3690986958f46c80710edd2d116c5e0e4faf7c5a68682439bb925a164969048d9e86f2474aba183af13f6dbee1e839e9c36e0a6aa342c86722b6e171280e1ed4e58531ac8129cc0beaa041af085c63c777a5a66e21c2b58b18379948c3f5e5fb3051517b889348deadb315425536fe5dde70fecbfe37a5e3660efe3c87e8ece9576674bb93db3d0b47f3a771cbd8e7fff13887b7bac15735d7c6d9c6ac15dd22a400f3deccc24949ae9219c9ca1e8fc88fd798cfe5ecc8d259a84fd442be39514c7d0b7fd08ef112fb70df9f231362776ed9a766409b8b809b043c681159bcfb72331de75cdd6bebd4c453fd704bf7dd295499067191511415a4536897bb943a6f5f5397ed9a8ab85ed1cff8782a3e889769e5306737b81249517ee2a105479b54f45be9d580f83d85c8039d9b34ad6312d86c6bc48c30a3dc1c3297d855d49ced13e2f353f9e41f252b6a83e72b6afd2a5e7cf54068ee99f2191935166c7d7135db821b3fb4b5b598bb0413b1a30d8bb731e19f57b969083ee6bf340b5547dc0fc121de1d079ac11336e0cc865d22986d3b95826fa6de54a45e9d09f63f2154a6da26dc785d33b36e6004414424fd95dca6682601fc1d61417713aa6a2e0ad11c03e6cdebe24ef83ac1191f5c77f43135db0e3f7fe515b185873d6f6ceebaf125fef4500e4754569303240d40e6d5c7308cb1d27efe375c5a6d613abbd2b196cb04becd63fd8e1612bdb291848e6089a5258991254c0f4c6f4737f27c3e6fe218f59b7e3fdb5423066cd2eed6fa539b4d0018ae4ad65ef9034d7b1592556e7d5aa8051bbfb112236bc6869385e19ced1e2fc2c4db4625d439cca0a309146071687ec3f7cfc94e3aa282c84f080aaab2e4462f841537eaa96378bb74e2c2c7e24f38a3f720aa5f37b4b34bc5d1b2e38ae1fc869fc8b558a35cb6e509670a24a4fd9d024ff6b2d2ce730e0f0359495fcd98a2199ca42b35613db27865dafbcc75c719767c48708733e65b53d052ea0922b1546b33d76cd9b1b0b11bb1b79aed07a20e8c4a99f0a37132c6b86903f556db9130b354698e3303e50c73aabeaa670a6aa297c1ac7e9c66f708fc5bdbf397474a34274f448d7935d8b040285192b6600cd66042fd41400815fa9e2f838c315c1a02fb820c1dfb36d5fcbedca77aad73ce9420e7e52a757edef19e21909bcbac302c7b1075db767ff16e8c933ab79b7ba4b8938f1ee2e759cbd383aeb18bdc883d21b8d03d0a312cb2c307d754889022acca3169054122d191dec1546eeb6b12b8bc940307c36fdb9105d512b5d631ac344dafd65096e51dab35d1c33fb1c1bdc6f535f8467dc5b54b888c6e58e2894fea19fc684dcc35e22e1126a7520d75d736a8ce3e91655dc1e8f46a99cd05c5411d72b6e1ad6b34e5bcedb860ed62310e96a431fbfcebfa14a444bb134a73251c3c1a33f75c11f66d97c813fadfcf0af55f00efd498ab1bb1ca18dda349098c881046e7989b162888d63841172a5bcf65b42af81887b60e9aee9f27b6eb1a1957e6b1fcc8f807642e568621dbbabae3abaa7d03ec80b915f155d01e6aa08f2f6d3e24bbb4c881a49dbce2de1d45f7f999c8336304838de2d3a57db7f23d8fb29026021268cd3200bd7d8937fdd2052eddeb68d8510bf3562438ea0eb21f5eb5611f4f8e6e0871716e786e552e4b480c5026bcc85b7103d65c7059dd30239ea05a4cc337b658020b061174d5c00e5a19ac7c3bc42c2b9310dc491fd37d50451a67aad59a6f12ea4b65a5077e7ea387589955108d7d838457d82c90bbdf0d2ff79627ff9b604de9d2cb55e10329daae942a4c5eec8c53525c53367b1f3e971b63f4a05e36faa7065ac35eb21ae0dd8dac51b045d4fa68babfece59979cb36c54cef4e4363d86377d0d667bf30263d4d70c9f95d620d1a9b1c584bc3f08a940dbd6db17528dcd1ffe1e68d28b54be1347ce0c91e3abb56dda5ba9b32ed448cfde6368f41910160b19b908f3b5609cd833827c990579298fdbc527868f01aa563851f094847dca8644eeb300d19dd419da702f8bd79b2b77a2d210d5f849bf6f771533a9e9a5593baeae695252629a61fb341478dd8151571873031793548503f53791692d5318d39d93abd6cc5e77f5683bea336673382b647829101cc83b2c49405d10a8a50a5b4ac733cdfd8f55d4a2cc36d2144ac51c95a6878a041fe2c79302e7d69b11112d4214a3cc2d6d54ad40b9b08953842f1ae0352bd98938bb615ce3b9197a8b39e25fc62dfc7ef784695315aceb4724bcf8d3c7a4c0105fdcbd8d8508adeec78aca5d536d34840f2d0d4d33019e7fcb75df9becc703243141d5cf935f9d42f6b36e3f67655845ab79add0f050c0ce40e7e28179673bf54c52950469aafb65303d0e27bae68c18b741ba8f7c1fe8f9633066d49c47b523c6f09d7e441895fe50a71dcfac15e0fcb13f5082d2fc94a5df8965b5e11ddc4d11d36f69cae7e93694e214a0996a0705ea35139b3f6b9177343ab967c6d848a43367be183948083883f4765312b5094103b66e8c8d35cadfe3c776154649a9385a8442c86a391b23bba19ebbbf1d0201471337522399b781b5376b9c3ded4db9a00f271904655ac4ae42474a2cc0b1d0f3528f72c57e5e62f800a2c6c8d19413e51c42f6081bcca51ef3d87f16e16db9597beff6801a76c3adf2793a939d296f302a351902a3a9dffbd185ddc24f9eba8d57dc001be14c69b5749c6f6c6a7eb6163b1e1571c6f107abb3b9a1be3f3fc0f888e69d07bf07c24be2e01913f7df895511c74ed0197681ae1f86559ecb64a09f869c50412a9d8ea3759f356aad509da3617f739cb1b95da1e19b5140e8c0ceca139274d2ad7d41247b275ead5039d27667547520f7755f9e2c5c2c6836e5f4027b0160801bfd3240e73a65d1a97f3bb48e431c241028e49eb846939baec30c42c717f4cae9f89a62d2fc51ad6dae1cccd268f606eca0c96dcdaae574fd31152524bc787c244c69b31543bd1c488b29f7101f8e425eef3c6abd2bc0c55b308b305b30d1e7387462087522117596cf1dfd14a079430e312f137220959a27864c8f7b4a2a1fdecfdf7bce75508ee81679731f32d1f7f3198e3f535bfdfa09170ff6cc0a95719916083a0e8ad9d3e47120c9e98ffc7e9398dff0d74fcb3361b51b4f24d293c722cb7a5ad8954b114192833d3b1953ebc1e8d12fbab74116e56d0e07519ad354ab86056234cef3704aa91c1262b4d3db4fcc12c7a554a10051f52800a0dc718c519effe3fd329057226eee2121d848fe075fc439955a509a5c1ff8506c68d54e6bf338995b6750f9433dbebc89bbc3be0b8c650f2352925dd5b22dfb43834f15aed82f3f70c32a948cfb11b4867088fe0569c2f5ba14c012f23df7076eba3b55e2cc70527d42ab4e57191f8a25ceb41b9d4d01dc64a8efc67fada379ff5d48c33a0e1c3521387fb0952918d8427b695e8862f40c222f20d30a13c7c6cc8a679fc2cb8e469018c977c31168ad502dd28428530d1b630e95ca75d90543b64da9470b0a08ba9b1d45b956360cd7a61e4d9fd5f339de6c508b4831b55a222159f573d55de262c44e3efea22dccd0063826ae5882ffa001f78930142330c4d652c067431fb9a574fd40fc436b4b04d3e9cce778c02569f12dc180e092e8629fc7c38a70867360dc6da0d5e37c9c14733816595ec3e418887e9b9119fe8100374c6823bf5d06d5d1d1469354971be8640850805827dfa6f0475dc530737d8e552701cb47a35498dc2e01285f910aa51583e810d622bd16b450a78089c1dd5731e1cc5bb4fbfb2911b3c8aea094426c16985f27b085e89ed84baf28f85eb3cec2d31c65116978a44a575087b72150ddc6460babe6a332962faeb90c4002887d174dd01bd4194e04488afcfe402d54b05fd70f9d19daddfa35e9427eac7f4ea0c4cd24bbb1fa04e7543df268f32cf221b57b1fe9156c034c4aab5576d7e91820be44060874136b4bcbcc6b4f27681575d3ac2c0f396b3fc9e8913c225bf35b8cbbd3e4825412f53c6b4ce56305961ae0abf8ee5412eae33462bc489b6c28a3821e2f89afcdbc34fac7fd573699e46f2b381daedc84b34f91718ecb7de5268734f547f15d21089c88b5bb1bb1b5df6ff1ba655212acacb9a1d7afc9927abd09fb7493fe49bd010d73e0c6f5597c7babcc094c915c4a1414583e6239f0b75537d975540e73f3e8bc0d1b28c073072a1b0596bb36a69696c514dbe9bed38ca8b624962632461e4a784c64dc0f258d2c383d0fdbca7a08c39224e0dfa6e13d99d70cb5290cec2d725181c86ac2e4dc3fcb2a5e630ac60ac4085d76b162abf6c90a3969274ed348ff8c2493ab00b7251da422eacf82247cb2f0b0f4f31bcd82ce43c6a8160ceab52db4d5aaede3f71f878cd109e6795a01b7ecb23365511cf890ce9a45609fe555ee41680d902141d3cb45b46400cb08e53b688df527b14ec84264d0ca6ed9b3615f55e4f4384d0c6d4489cd75ead20a2db945632752c965d06a402f9ce0b0adae1caf70a77ca665e2b3642cbf93557356ba98b4a46dbea590f6fbcdfe359717f1c465a2b7aefa88894f329c22f04e1e3199189a71955e727edb4638f849d133ea0e747b1f3043400d4a2b8d881794cd24cd299bce6cce65ee63603c56d076a4a427b7b86fc48be1f436ac3cb21d969014c803f0ab5dcf72a72e8293db0c4f0d8b4751349e144df9aaaefdfd875a98cbbf9858aab07326e3e44d91c51813cdb84861117bde4e5dc7a84f9bca8e960801c35d29e3b592543f5bf197982646e510b6d53c697bd3518e462d7788b04d66626952dfc99e652064cf68eb6993eea16ea02c30361a1e69c413590f44cae96e8d6a9e1931a45379a742c246bdb78a11497d6622eb2e0aceb201781a1a22b6c1425319aaaa10a9dad236d99eb1019b884ff186d6759e18948b8fdd46d6cf83f5efbc390a14e97f8781fffb40811dfd57bc684fece0db13aa6a1c214493f19cb0707d9cf72d9602293ca31bbe5b8d3834094a8d0e92feb4aeafad66853ae8c3b6dd410abc5c1a5e94137b325200fcb0545d6121a790afc49a8c8cec757f68f5991b0c00fa1086d54040d1452a819a5c1aef4f3738a6db25505a7b703b11659fd0897b62c36f951a4e0abcf1aa3e10e0c9e0e44fea6c1a95cb7c5a36d9933f9445fd45752fe1f1f680af3e92172c55fc159d0f911f9ed2116a323ffdd64faa074e649017f7cedf28515ce3d46cc6d01611617fae87d82df6df30546c589e9817b9694e4db98c63b0b9ec204eed41186b62e4dc60e69dd9fb5e3db4264293b1111aa1be2bf9bd982f2635445f53c1688e52143233a10d374e01895d072ab317b6e878de15a65f991f93d367e634a8019b4166927c9a24b98ac0725278a7dc9afc616e381782d503be9e49422b45377d34ac9bfc0089b037bdfbb0fc7eddc00dcfcaa597d181e39b7ad17586fa8cdea62d8dd906f56333cc6a831b9d03cc5b90c37339b6227489ea9900dd5e1d5908c28020694d9a3cf024cbee07ac85add847c1aaf2b0b62b303e2d512b7c08fe72e17c603a9b7c581a7b3fda3c522c3e30f080921431e3a4596ffcc941612685df714c506e0b216eac8cc7d9691ade7cde74edc2fe418da8b3fde3224eb882444a4392807d44d161e5d6c51dd3b771dd6a4bb30b91a20eb64766b8540336473dd097dfc3490e8de885f345995c8142fcfc62747bea4983f02bb69e1d567a7c0784e456d326a41e697ca8a8f342c28203df70ea7534e6df76201d93acc2f9951ea250261215ac58348ef6f32e1e7fb5e7e69d9dc8107d9a253b4ce4efbd5c6a8075b23434b293fab8042e9ab62161373a9679132b775394c8c6013489ad462c86a24431ed68abbda932058ddd126a4b088e3b4575752404072d48cef7d4a83e6ba7034ef779bd527ff4906b6a62e15d4869f51bd30047a7b60c9e29522d6b02cbe992283552200559a76552a5cf4d684686376d2b69572d58ef12c2bb560c54a9e17295c8727e5ed5b601eebb2ed7bc9d4f52001b12d5ab85123dd70db77b346b12eba8aa653bdcc49ed987dcd9b6fa9e8073c71a311a06c31442e39f7d6954f05400284403af0c880a71f3a096289f9c97f8756a485baca2066f230e0abc16cc07ca8119caf43d44ed5ccdea9af525b3fa5e9c363121d15e6e898215d6a9dae0764599d66bc88bc037f291c879f31b05e9be4467522ee6d36fb1ce9603202d0f6cbb30532a962f3881e604af270b9d639d622457985216e1657d25bcdcc0df17bd023a5729c4f151162514e1874e3d970be14dd08704172488098370c27a90f74e15b0a5a1612d29feeef192c412454da4040ddcd0722963c254b874dfb158253845fbf7b6015b0833ca2cd024c9200096cc9df8a4a41ca0881011b1392401da68cec795093af2e2e7f83916089733fd4ec303dd9457e17a2e44eca9615f62ccfa3870ce672f2f34a0e92bf60072799442adf708c17c163c0d97b483fce83f12893abad2b3c66c1b1ed0e2484170799a561b5a6c9d6c3fcced52b61e14cab90698d4dc9093b6e09aeb491e278eee4c6f257fe4fbdf77f79a672cd5515c78e49e9dc3e81b0d51db9f4d970bceaa75e531383c16420759d972e8f7a7362410f0a2aeb0014fa0fa76b4546b42c8e15cd4f7c0b5228240e4f0c6ef6007debb09c1b4c8e63e21798d5554c21044bcc2a6e7a2136a6757bd456ea7217bd0e88aa3226d65611c205431747d132602d7f7409cd74c167471f8a7041f60b4d41ee292d551377078a5fb9361961513278cacef52356f1064a44ae356380b8bcb6739254beb5a3a6cc5feae7c4f99dbe81073fa38fd3905bc159d13b4e896bd023bba3f076e276444d87360b3811eeede71b96cee3682a27d35282fd6f44989e73f4138c4a29eaacb47cc28f2c5f2f2933218476c497737ed4501ce3e624598b6ef0efe7de6a37d25a051264094fb366622ad6f1be5ad5c3dd49655f0977c802a55e6e570622135047dd0ac1984ad7c6b0ef865ab2e64d15ba658edf0a89595c2a96470eb134283976accf90a919931e1247f39f15296f667b09700e990ca37ce8a85b9fa32cbecb6238a493a416201f6b24beb3e45371c973744430ae02d85237d510c2a5ba7106eb4106cabf774d04ad3a08c66e84675739dad7b11268a21bf2f7e51baf213f4dbfde2ce71fed0c043d37ab246a288d96ef4131124c9f6aa4eafba1d2355ed51a15711e2b827329ab1415cf7c61cc6d2ff614a2c9d05500b310efc37d8bc05f853d6da21d0eac7e9a91ca31841402d249db15a370a44f75ed3991ca3c7a51a9b84e5d765d316402c11ddb9df587908e08054961f9052c1aa5327002f1e2f8bbc3bb594fff81d8236558cf5548a40c4b1b03721e3275c561aa58b28f1fec346091afc844435828685e71cf35814d50886a0012f3fd0857e3303d15f11b2a0a2b6df2d71a9fac43b0084e11063cb411400f4fb08cf6e9021a498f018f66bab530b20e58f0fcea73f64c6d71906adf320cff8f1556eb4c9bd4570d362d1b7db57e8115a8bfde565a4a2430ff5bee598cd0e01ffea8b3fbeba0db07286a4fa5d61aea62608f6ca5376ca18bca8f3a60585fd47d8cb7ae18b2969adea0748bed7ec7442d0f88f5cae067d899c0b9023a76c6388e22cc050eb0b55801c1634a971b4ddbc2d3ad671a57928a13f321d9e335e56ad02fa724fcae024189cca7587d498df9fb9c978a2eeee7ce73c892459f0452ac026b20f1f08dd4d3daa48b8a508a5ca9aeaa8f67bda8fd57c837877d5924a4caff0f185688c4336beeb1034b700f52ea05eca02e38a3fe60e813522e082ef724cab829511e0fbaca9a8eade836e33d3356e41bf2e1f156984b189f56a464fd8cea525b448225407c1a41c845ff5e3fc49c0fe37ee766f0c0caa2efdeae03bba2c0d1a28b28a1d14abef0274162e109a54fd0279f86e9650d038bd6e5648611f1dbc141d110eea7ae35468598dff70a8a31da0516c45f40b5dd835c419fb1844fc593d343d78a2a68d05794fa08d5630fae41036431b8bb5393e139eba0eba01ea0876ed7f6a93ded9cc30733f55d9e67cedc7e741a20f5ebac0ad89fa43b8cc78aa42c886032cef1c11293cf21d23ea0873c0ef7e5be5179918b97d783cce1c1bb965c4dc30b6244e16bbc3d56deac22f5cd50625a95d3f6e4272609e3c0d85dcf23d66bbc3197e73a096ea919a88b1f0c62eb4eafc28162623cb1e96e523f6f08a1fee78a64f6949d1b4cde3361ea87455776582f7df350a30768c5550bd648d6f4234a66cfcf3696cd337357153e2a1fa3f57a6fad70b369b6766b46d9326ab4ddc3183f9e603c9ac3409017acb59636ad0b1e7a9c65743aceed41ddc86b0f08caf5e4e980dd5f326eabc28e1ce681f62307a2f4b7e2c031765f8931c449e8178688fccfbddebf6456f475de2791e12f8e62c434cdafb0a84cf00b5490a36b4548e59bd7a9a40c4417cad2567e8ab2725dd0bb6e1f31072dbe354b78e4695ab14c41824da053256cf8a8c08e118f0d335bef1d324ab554dc01e4b00962f272e9c092fe1134f4153f7f41f4362306088e5e936642260e35bb9a8a7de6f7833a0ccc26ee9ee69d5c5c48909f1bc01028fe64a0fa55bafe8b02302f0f8c2182c254a9fd584ced26ae066c3c56fa68281489fe8cf0dfcf08cf6a93fa9a83d7186145fb5034485b99724f8a3603b54fca0ba520d87a2151b20f75931dffb2dd128b8d79fa20a8da46ea6b97e8d4c4f2ec12bb07b6ba8d781d3712af3ca4d0b1a9f37c57ccb2bbc4c53fc3324feba1c7745f59ea2c010932994243bd40268841bc44eec3e40bbf5ab275a02b1abe007bd399e0b8ce14b12a28ba0c77864c89e05cb5a78ac626ba25e9bb487c5956c8eb16f42b8423536878352dac51a1c92b25b5f583f3bbd52c50a235958cf45b0497cada21a28ca88424c69bf6a8f5e270e27599802c69fdbc0af25e8f70b6e5d66360f95499904e5516c39e7e3b1486bd57a55ba24e002028c164d3614aecb67d141a5bca59172acfa2819f860aded79af98e2107d4da7fea951f16803630644afff40b27e8911a23916f958d579f7780775556001a64a80c10b3547119c29823c8e6f6221ea62f2cb7e7a16bae65baf063169db7b4451bb545da46b5f77f1ac5df1ff72ef38898164d53b23bebcdcdb71d7d45e3fa3266cca0027f87c8e8eb6bfcd5213c01407ee2810ca8a1ac73a63fc5e345be45174753380949448e786161e1de2863e4c76f32fecbf5443b07c1e31c7c579d4fc94632797586f8f363baa792849914a98484f91255aec102e65ff4a92d5602ffef95c437179a5d2c483b724c403d0a1b84ca08f7fd3ec0a78abdbb7b05e1bf110308fdff06e141927fcbc11f340c27e9bc82fb043e1db113239202307473cf339cb291014307da46022786cfbe62bce9f8877ebfb67134312a92345d24a88082d63e0cdd4b7870203e874255b458fe5f3804f3ed0ec25670af750b50f60fb8acff97d707741ccf770928634dbf63d6dfc3925ff9efe8f32ed70ff71f2dc5907aba4cf35a661875ae222693057ddeb481c668c00656b714d9e0391bc1c8c9983f2c308b2f4e69c1620a6dc6c3cae2e3e1873ea13e31d1fa29cc15806580e534e1a48c319f6f28f00e3c817ccd451c82cc28bb95623f4d8adacb54eb9ad0fe7e0b52385281a4930c28c2a33d1b2b2a141aa219dd6adee1c59b172159e466d0071b092368d658315f9e65c6f6ae63c376adf3309f05ca3411848e14af0347c2e446915951dcb91f2ce9918e09d1cc1201cd1da2e7ebf6c3bca0fd8c40985e2519c892915de2ffee9b50d76e54051d7d21353230092ff24e0dbfbadaacf9f85d56c9873974b4382f424ce4007b93d01a37490155e716103de87055724886d57c68468f440c5ab75317b0c101260d3d3f70b790b4e8a10a3fdd846b40ad1731a868d48bd5dc0ffcaa7c3fd6c0a4ce9e085386670175d056a53161b7b35caf514e93f4e51cd54e6906356c390b8c93dffb07c7b0ca69ffdd83bb7e0d309ed6c788bcd3e3d42584af4e49e103614dba2b78cc23b35b2e8228d81d849c7ff133bab5c8adf8264479f86126e1090b897df48f9b3778171049ff92b44010f1cf03f02e32e196a14fa09717cbc829c7cd619bf03ef832d995f2848d0a265abce8bbc8fa2a2d0e430071e8feb302bccc320a05d15aa8a99e88e414f2212bb81466179e51194d1c5a01d9e274d85782d49cddbc46cacf3a14ca1072be7e9bd1799621c41e050e4f423078e025acca7e2d6603f63ef4d61a3e79518036f0e1b0c4521b6dfb4fedc938308baaa7f8833282e24cc0037ce1b27ccb0a411c3bc735a0907045a6665f80e92ad1a844fd47eb4004ee922f5c7a586c46524242603aec740fe292eaf876d47699158faec3c784d0267142d2b9a4dab63acd4ae53ff7caf051615eaaa09d4293b0233e3498667e97a475e6472d8e9cf44fdb26729a570994d223d1e3a424ed686accf7f895d9459b1d5a95d79fbebf67057248922c6751163a1dcf8471b41c49470c29f1f922a2f4d77eb6d00da4135cb688e9960eb28eac5ac380a376e04b86b623b7b4a2cb1a051f5851ef0489afce94bb94b7a8c3682c00b4882051e8b9aa6aa6bd60eb0fc43410255b19749c08d4091fb20dd8e11bcf3f810ab03e7ccc98341747201268c649bee58110048d3d2aeadf623ef2a1fd0c77c30bd7224a93562dbf6377fad34b0dcc60445373e1ec6b2c591b0a450ea32f78623bba2cdf0ae3b1fb4661d4e68a865e6e4869d9727b371a78e87d7439b3bcf7125c169d47ec8d13da26820b215ca0cafac680bde1e66288d7cd7b5585dcd062272350ef5ab500a3644d368e5aac7379843a174ddfdf46150f926dc60d0fd5eb3b3bd399012c1ba36ad0d9d5d77e88c601cf410e56f83cb43896929e6745a0f8e4a43a30e5501205119577328783ffba93e6ece4b52cdaebea94e26dc7c228703ad2cbcfdedd949bb20ca040b786c441791a45a7c986ca4bca556d2b9a42c273008aa13bcd3309db652f608582348187809e4adfeb4a24d5abd7aa26da0791be3b799c1f3c83586ece4d63a83149309296f959fba783ac627cc009ad18f050c88a30be908eac13d503fbed0f6d981f16de606ff194cd97ee6c43407c894bb5c2ba7ff3102533fe649df333b924c2d5d28bc0149723578881229239fd802a8fdf6a56f1bb53c09a91a913c499649601248acda2845d015c14af42f0c9971346139de39630c20c5a60f651f02f6926b29632b641790516582f9f0a3e42e626228feb4ed6abcdad4a614e3cd57da7fddea64ce6303e4e2e6f26bbe629549943f6d6081ddd6997b9008f0dd4e78b761249f84f5632e4800c4fb05921191a6ca173fd1f17c4af8f2171c00509a48c4f8d960906cb86e3aab7b0e79fef798ef5938c8d822dfec981f2dd3bc31375fdd17635204acfac307a3487d0451171c12ee1a209f103cb2b7c45fe2d358e9c5d2b2dd9d300271d3735bb2514ad3adf67699c2b3ef3f802d0d1a08aebd49361c0f9c5b134d1f214768bc6e1cc588fb0fa3aec1221bbc1bb3757378d82807861fef9c634f13d9e9b9c3891039de80cfae1fb7c30d78212860da6046beb7317bc7e760c7777eaaa046cc4f02ba68314657309c19a50367919a9f3e8ff1b54898cbec5cee96c4aea4aa90266193660efbae272cac2e61f18c5c3202f108aba1620d8c2f020182801ec0f2100a6d1b363a90ae07c5bead6fe25784bcefacfb1bd97b4b29654a32a5145804040483043fc46088142a539e9052c462600b059835f57a71bd18d1a8dee2eba3d6d0f057b6592fdfb24adb238558bf2d8ff555b64232f7d547ad61f92adbac6c7b96678568f9172c991048ac96ab86c52ae960cdd130002a180e31f0dfe20d228a6079ec8b68f9173a2d4ac074b27ccbf7fbf2a88d96502c58e6434bf622d34637e410d866399b65fd0a673839d69095ddc8b1feff2dab94838363e87a1c1c39164ece6549232d9fe2d135faedfa95ccc655ba715d2ba3d168a5aa5aaa2a23addc609a6be5b7ea476f55a51b3682aaef01c7cacacaca252556246f304d2565766394d9b8c134bb7384a3aaaa1cd670dc108bc5fa4d8b2c8bf4625996949794524a79c9eaa5bca4252d69b9110dabaaa48d1ba7972775295ed8cfc52e33d8a5c205529230062e84fc4197f7eeb7efef377f98b53710deedededf0fd2d58227537d6edb0fb750bf166e2ab4143448c023aa19c1f73a113899452d3fea6d550a0c92367f93479ec98fc6e79dcf26c0f931d22c49789155a86425608733770ce71b303c2fda658cb9895df65cbec76cae9fb47d59178c75c6cab8028cbbddb238898a6a94a7a86e43712c800349a517ed49ad1cb6ce3afdee22ac6cc850443c68f5a0343662fe58bc7fe07cbb73c101931dbb8c25eb0582b2596910e2396571942acbcb3947e20a9c0e8c6e829a03256328de44cbfa24026eb480e816de68335ecdaf877db87d90e1cd678d7adfb2ded60cdc9dfc132a2b1d241964fb125df6df22f292fab64bd351ad1b0ac2b46964cd9147fd3118db8325ebfc5b7de8aa5527c7959d688c675c9aa5ac96854d9c51ae3304dccb6fd6aab321c9d384cb32f7fa755551fe3ea5428d4cd0cf86e9b19925e2084f0dab0c931cad2cf4a961a4208e146194ab028f22aa6e10c3de841163d903b4d3451d44415192eb420d4d811420861b8ac86f35ad6b2afe557cb57825143fe84ee1d3e7efb30779e915873dfcc211cbdaedc5d811e1743767e6420a4040c51e2c3f70f5e315648603fa900c217865c1a7b982afe00bc80d980ff227c09af8fa51e3ee1e8537c1919653060f6acbdf752098c0a28ba98efdd0fb01212cc7df5a92bdb80cceb65bc7e4735e05bee610f9faefaf70c2be8a073f3491852c6a7c409cf7c34ccf782fc5021ca3f29ea7192c4542ee693113e148a69f4c97c0f3303017dd2139db0d4d2eb033be3abacebbdeff8de7b3038ab015fbe7ced17b9c03097caba2eab8a80cef71edfc1a8117fbed23eaed8e224307f17d2c77e349c0e3a6174432402b044f38a111d57b3190c2430fc7d78cf26b0ffde87150249673ee8c31762ffbd10485ce603ccd409f1b21b9480adab29a989df14f9f92f2ad8f614d08782a53fb820899423528278ca114454a4f474a11342f8e0eb52ab6624c69d2396c2583e225c1fbb034405888a8bf921fe7a1b403afc36808080522faa1c71717952a031dc3b1f2b78e74365dc0b1f6c53206c8642d127eae40a05fa1915d113344b539518d1641a819f627fcf7746c7369719e1791930eb5ea559c2850648b8ac80861c4c71f1a1a10916eff251a2e779bc873023b06d77b41d229e69b4c741c13a6a138f1c6942e707a55924148a1645222c45c0b6f5f16142476de24f1678f972478029400108a00cb880c6708f4365acc0b41808d335f879453a3a7a042d113bb6e7b5f7105589cd8c065093d4d882699d796665db02d17126f73196743e46f72265541bee1f6804531b87a94d4735997879f898bc58131857035b31777ffee4cb2934c4e0d758c75cd7b80373bd45357e8d8198ebef618ac5ecbbe7c5fa1151889aba00379ddd9d1dc2989b1467bd8d85f7ebf7e083dd3b58b18bb0eeeef6e8839dbf113c9722780dbf9d045a8c7d02191a3b48c2ee4b62b06661caebf7fc79630f0667c5139a05afdff3e73d22c21a28ee04a902617b15c8e0a4bdfbf573944382f9bbfb7bbe648808ca69c60b9b9e417b601a7ffd1b3ee0e4d880d6a80c853c652663587337acdd50044c8b790ce1ab1e84aff2e7f253ec19ac640c6b314eb6d40a42c055c95635974073c7f7b2205e670fbe4cb3de7d8cd556104d8bc1c931a34c4e4142cffb47cac50a6cd3283b7ac44822318dba3410ac7de6c90aa32fafc4101114b70c3720bcea7ae5724f340b53dc498c6bf1c518939c463b38275d181244db97f272a2a3b3fbf3c7cccccc9c392f6d3de0e4871042f8aff478b004999cc1e601c6ccccddbd8ebbd7b97b9db9db77e3c608a360294c3fd74419afdf830f767cbe6193f302d6dd1a83bf479cac32fb1abf63d5de0826bfebeece56659663ec67448c4d42475595a5094c1fbedd1ed696486683b525d2dddd4b647b4a3b647bf4692cec59225de5e4d8d7d26e8411427f858e3fc5d2aa5e96aa4a36aaea5996f5a41cc9fe37aae17fbd0d55eb2d592ac9973f83b5eef15e4544395e5766433f6e31b391a584307d9dfafd02a834aaaa663e58eb1dac45e9d922f05b1deccc331d2d59609b0add70291bdabdddfcbd0203e75cf78e94eeee0f2ecb88c693b0d71f6cd75d42b573ce41e8205ce71e3a084fb09d73fd9e7b19cafd7b7ed1f1ce38e33d84ce41f7dcbdbb25bd406cbe2ef55477f7dff6e7de95600183b96285a5ab990ac1367ac04197c33937849c910ec59a0ed3c4f8d6c1cf6199a3c3555b7e4508e1cbec848270468c314a1d7e8d68b8846e79749f21a584988eb6fab78ebe33f60c2847343cba8dd6d1513df7482fefbd6ee8ba1b967e3ad70dddebe79c73eea4c8589285c9b41ad8defd1cce98d5d10e1c64deb0c97d8a515530ff4d0b01434063b46bf7209c4b30d9f735d7fe5e5dd3b217d6629c2efd8aec4c54877aef596f35c26a4403be3895b9e4dceebe760f3b704d20fad7c8f5298e9594efd9893594945e954634aaca23af389837aad16f392a7ef5562c954e712687bb4bc8f022f5a8068a6922cc50a7c928ae20c3767087df33379bdcbf0c87693ce30c4786d305ecc47f61158f9460694c40ddb8682edac60005d85545bf7e0f3ed81da7a0a0dd06bafbf337c39d80e955554546de17b5e13190610996830b62da0a4642069827fd6bdae904a439130e4370bd03054cf5af61ac397f2a92cc28606e27059c4ea7d3e974caa1323910718299519bbe809a0a1db41196bc7ecf9f7764d40d4980de7bfe9ca4fa5aa3833953ec42843a383848a4229c85333f6e899d9d203b5660c1ee6ed3d06c9a768108c484859a8914371c61db62f8b6f4a66abd9560baccb25bf55e611a46627cf32746830ee183cfc130835876e102b6ad0fff4cf113a39d4e33393a6e8e080d3d39d24414a7d3e9743a9d7252806dec73f26102867f9e3869a593ca9c26f39ed486e7d545c0bc446facbdfbf5731ef1c032e1c764b28ac482e8803b53d5f8da69820f112245889060dbf69c7a86c0a498462f1f2208050535219a1b6a6e1a4ad3b4d7b409401930c371261b54806d0c04c4049e5c0d94d12ce4031b4a2d2d9d6117c768afdff32122283c7e18e9e1f9fa3d7fdefe8a520522fd5cc96cb896ab840035f15725abf480d6d4c4eeb4794096d80373bd6532c63dd107c5348e089122fde4891327575cd1a40993b9699aa6695a0e36b9cfc1010b28a6713e37f8c1ebf79c6558ae3bc4aef184212228afda2f16403cf5b5289e7714495acadc5c5cf80626d8b63f3fcc404344507c5a7b01c9a016211b50bbeb2bcbf16a27227cae3b7b374cf32858ea520936cb5db0b43909a67ff3e0e6e387250622e59584c89c7a55e2ccca162e3b65f08135a1671a850de123826f3be57818ae19bd62a3920e9d3662a808c771fc3d0e0ee78793e1e028451cac392f6055c90de1c877590faca11c6ac6cce30724574604d30a2d85bff7f62d33ef430821fcb74bc4ddfbb9f773efb7ebfebabd776494214bb1845dae08330df7ab7dc7be355a262f06afee41f74f4387f00d114159c93670c7e8842a314b3848151ee22176e278dc107ea2b1141e622a6ec70999ee9f778cc638d818c9a4e289a955d66750356d8c646e3ce42cf0214e986e7bd1b8a8b95002dbbc4811a6790232136ac33e38e889d188f0a1280a17334cd35595654130c5a9d48c869026bed48c979af152d97b103e1acf66a466c05497092ea6d1d601cbdc1f690c6c87e395b9414c8e23d5f6d703936377cb6bf481c9afdad563f2bbaede7d4c75314d1341f99906a51a8cd6905093c78fb9af3a73df470560a647f6c8ea3e715a263f1bb442eba465587b9d2c1729988cf5611f159c6620e9b5d3ebeab6ac6c3b36838197ebe2466dfcc6010e40296a4335401900821e2a93c3110c6fd4c67b0a3e2722448a14e190cc806ddbd333e4f4989999b50984da04055058292ee22286c251e8e400036c632226e22729286e681540b8430522e810bea1202127344e34cd3d01db5648a8899290a48bbb7b555553f66b20e9d40080ad061b38e0520310bbab23c6a7043a84ef46131a7aa2314dccced41dfc41414242397c3a9d4e454551a250494d1e3973377b895d0574081f7c7e5d2e2e2f2f303537dc198a351bae88d1b58fcfe3e0189203a55272a8ecce0a41ddbc6abc82890bd3fd86000dc374bf2da0a43516180d816d27d4699f3323703398579baaa66da3a0408a2387ce0e95713cd4e478a2b041a6734d9ac09c50674e601bbb2017238675cf885b8e20c2c47d69bd04846387b183bebf0efaee3e04feaea43073615363320bd853e1832bccedc47cc4d21f9410d242480b283f509cdbe0101fbb0b86e3cb8be68af5dc624fe7fe456fe791ff65f3b930f02b1f5896aab2bad730ccddbcce9fdd7719863575cead7bfbbc9d332bb373ccbcda0e33332bb36bc15a67d700d8c63666deb25619552765dde3f3d8afbb338deedac58e0d9f7784cfabe817d67bafa5e9570783edebcd5a6520f4ded8cfb5eb6fa7704af9fb98cbac9bb461af99661f3412218470480adae7d160058e073310f99122b60842c2c303173f4158010f5b502942944491dcc1922e9a54d1c5130c3006aca74ed10591c9075d3cd186740125092225446a300322b438a245920dc8dce732401865f7b231b8549300d574360603d4c4598c262f99cca2433cd28177e7f2af940256aa02ebe74b4d2f231c56a24861e90f48489912450b213f54621005c67b0d3a8194a94127e862eacb6a6c357ddae8500485a5038c11f74cddfe0fcb296408172ec32a4c09218410c22020844f4addc27237fb9dafbbba2eb391f79c70c2df69d7775f73777f1d3358da74bc533f3fa90cbfe7ce637c8d84b5c8708d85e53a87cab876ce0531e9288e080b735e776b672eb2d6ceb96f6e02e327aa6ae0fbdf85fff103e27929e60a6cfbf8de1be2ac192f01ebdfddcdfcbb5b477392d7ef515fa639b42efc8659103d3d6329b8a7d52c805c6825444586869c14a181326099b0c412459ae05004b6ad90d0154588526af386869ca4b630379da2331dd217b06d8584ae48a9cd9b4452284263ad3252c2779aec1f137a0b163688bbc2ce921aba055878a719f705f7f71c421dc23f7952852a40010a4c60021290d080b9690dd0344dd3340568cd05d4a4b50d3b9a8f108478788444c1644811137b647f36898e0ecd622a16a652406b58244ba653844da11bd514217c9d18e1f2c408e52e0f0fcff22c4f559d2047336c11e5d3622c2dc6d262acaad262dc568eab7e107655599f62ab6a29a5bcaeb6b690a51baf2606520ca5d186aaaad0ec1b737fc75f2e8b18049ea1a5a8c1080a8542a152286772989017f02e84259e6ced0e0d64fab315a55ef2922069a84b357eec355447d5b875db31f78a7db590114c6af3def3e7fd1bea0028140a8532c0d2686035f7a80ca8cc4a91496db897f939e66d752b9b951cd0c95b6b8f394d406de09394308592b4484280d668cee4da5d8c1849d329801c61f2db080a0a7a95d912065e7a6ce0c6f0a236ec2f4a524db57d8ff1e5a53be3f1638bb83406662f31103f5615b73f8f511b864668fbb2022f6ada1ca4c9e3c7644c4dcbeb8ef0d81d82110f0d63bf0dc86c87fb63eef36a0c765e6776eb56de4329af070584fe5a922c55cdfa8dd1923700f568c4157b0a96b8e0744ab91a949af8af203e3ee48e838367480e873dc43804db3468eaa032ff026cd321140e1dd446a5081eeae66d2378dda81bd40deac6279883a8b48d9b0731ab118004100053160000180c060483e180703c502451f914800f638a3e745234140844418e03298ae228c618420030041963084146a1a14107e32dc3d1252b309dd7fa2bc0260079069572cfdd25f0912778bff438acfd9fc4976d00892659e065144eb16a5cc5703f884abc36ec39236ac9c68d22f6e8dfa9c3a2e0689183a8b9b2e418026f1413a23a74a79d2f4fc72542a96905712f3deb2e4531df19e7c0e10e287f6d526f88756e52b727ec51c9fede104e2c3f650d0d090a14c493773849f2a8d8e49a7722596df977ff473ddd6bf929325352d82d02c772d2f10a1ba7c21bc6f6669c807882078fcaa7754a09af00aca0de2b5c291d08c12261090a0f716772ee185e9a3f5fd7820abd449c3d71a03acfbabca4a9aa8ead156be86d6c98de91fbfda227167382dec6ba64cac7a1273009f2c48c16793ba387921bf88293cc582e19e2e058fbd918441e4d030758968975a6bc2a24ca886e9161b84f4f619fe4cba73e775f2985728c8d7cb80f84baef87cc221d1fdffdc0e8b36261c39ef596fb889522843127c9f02c5bf723e22d9c77103d53a8b3b467cbcd70d441bd9043c416a5b9894e80a28c580a7aad37a643bc85460e67af37ace7611ce4517666e2d1737b287fefbf53b37ddab7920e087a6ada1b2dc7a9222179c8fb465cd2c27124757e6476fb7317b7b070896f2b2a5725683da7ebd52634c1d0d732d25de98b488423cb0a07be5e440b8c3f7173c48ab255a8d4f08920620a0b0262ae3858d66f92bfc215a462f4ab9ec066a74c32c4411c77cbf2f2af5bd97ca2a0b8a4f34ace709d8862ad4f5c15a5f074acb7bc720397ea0e50f9b45f845b7fca5925fe750af5e3c4e039c9be96becf7affca4afd7315cb64d276a998bf5f7aefb6d64f70c4e0497604780aefba6bb9580ed4470635a02ce349bf79a2c87fbf87fb17def7793ec92667a06c322b5a46ccf986e89e52c221293b95c33d2dcf5cc8f9fea810e38a4c257d615f359989c57ed79212a9e20f04fd1e045dcb9619677c2353b809c48787f9c1f4c119a01b0aad3a8006a23623b173de268a308dd1f286643599a366c277fafcc6b304e80fd667c9474b61fd6c82a1d998cf082bb8c6dcf29da4957f3286bcb93f6e6d497669a4a4084e54222410d0d1c543030fd0e326c4c6903bbe3d7731db407a623a41a3d7f5b6943a2bb6fe63d2621e6eafc9cbd408e214fada0c4f7c6b7003b9e22c9ac29d851bda1be502af396da6567eef582742cbf80bdc1756e1974d332d2a90507934be2ddb9916c97c88fae15befd0b1acaa06cf0ae97b65ec5104b13e2b729a5e9c117c6503c78c55cdf8e2fb3bc1ae62becfed8a0aa76989c8e6e84e9bd4a66d308826a604d0a170d1dc5d37e88f9a1937630bed3ffd4ca3899990f005511c1d3999bf833e5ad7136548c3977e96c1f9fa6a22fb7f72e12fbbf9534f8bf7960de7e0b86081a2dc883200ef0046000d7b5424c4bdc443b374118dda2a3008007b99b625e52eda4b3d2180355a9591ca6dae395db38bd5e66ee8ed179ce12c97a46bbb4aff9a361f481487b12943c8e7bb34c577a180145288b34353dc659e999fe6142f6002ea0437a472dcc4d053ade531d56f2303a59c22a6ac0f213e5cc2bdfe40bda80bb0f41c4d73525fb8451d0c2506db1cc6a1c0948b986c243423d52ad65169166cafb029e770de7a25c6100aa75cbd122a5a29d5d2b23f92104ab1bc0d8569282a7dcbffeb66251c490c005c6f1c40ffcf6771f2230b80abf8468a92bcd8da023a0fc26269c3d574e66c9943d7c28614119e4e0a7b71b54a4a2b486bbfa9b8e6c996fb8cec6248db170f020a560765ae9df90214c5b1623cb33588f87c01effd151e26bf3b45650d0e2b18b0b1cd23b04842df3e687fa28de7aaca8436702c7a6e166569adf90da6e9aa1c0fd0c65f0b64812a2d1dc6a63924f3d3e2d13d648d97380ea56509c53526b002511e933543b46cb09463ad4bc5c3e4b121bf622f015742bf76835417224e5ebfc63bceb42200f80579eec1fe471a30905bdcad33ec7e36a33b3436a2617246af64f9543a422f53369a5a6e75eacf6b57307d8410b81e239cb9c130d45d143500ece27e0253baaf3f1095eb97acda7891c5755725c9452e5eaaec3e5fc740e707854b62185f74d26b756a599c8d82bb8126b28f0f4943d59a1c5d415d5597bc2cc0f643055500b84a455a768da16ee34a57696cfed198c89b6c509e0e2635c585229404e76f7147c5b6cc73d03a083a78e2c7ee96fff9aba14e925e458618540e171d3c929a094f3c396483ff9632e1c5ad8bfbb611097a85ea23fdfc13214ef0ee3dd06b67d805306a588029dd59873a9a84882ad100b799bbff483b44a54cf4af18961fe9422b20d9a2bfb20955115995842279365c97d77b145dc2d39b4431b753ab06bd573bad8a76b55d65a779238ae2c748150473a9bf3ce516a3283b03092d42f17d0af4109d54cbb324fe109a8998e40bc4341bb7fb2724ed875bfc04aa78cdf12fad6e0d73bb8370f3cd5dea055906a1472cf2df66e9f801ca39d174db1de9da875f416b7b1951d55a7d0a4681efbd7843f769ca8a55a7535f6dcb1df52df8ad8e82773acf407d7228d28b92851c60e2df939a7e5143b1a3eb0e8048ae7cc8a415bc276c6996d73f790f9f08ff254c4b7403481d0902e40acb349ec5f48407b05d5768d9fbfa0e5cbb88be68dbdc59740bf05ceb10d6785981635a8a871956b99dd85d25bbd2e8f5e5b3c44a83e1edc5b9efbfcb9cf902dac53f413e8a43b0942f521e2d6ab2ba237705f6bbd5d8d797e3f788b6d03d87360c5abaa967e74ab22d93fd226f5ca5b9a19875511abc0d883fdb7f060e520ede22391ba4e86ee181bcc5a1347a63ba29968ffcd16cd8c972382dcec61cc72083962a8ce45efad0611d940195a9b97e0b8a162d6e601507107a40efd12b09f455ff7953a34a57137a9cb7e7d95b8dac2068197c3786d8a8a11ba05213fe306bc99c070670be8a0926ef2a4dcebe3d09ac76ac206317c7c4acf6547dd51313e7f979092027c7951daaf7b24298a116736cfc1a2bcf90a91682571c325595548ae8fda47a62d8eea2670e7dd1a5d91c7abb4b299436ef807641cf19572ecc6fa4662979e1bd145efec0c26e39678c4d6984f45e143130c0071ae448a36758acbcf521560913c2600012135c879bb8dfd75afe6f568c571899c22744fdff38d2fcf8d72b90a691be4dcd0dc71d32953a37d0ae133ec1607304c42cc103e2092858d5301cdc7bdcc2c3d4090109071fa09e20142cd062af13d09f1da11e77a0711b97d0df886c21df7d4f0f23f5faf5021eeaf942bbb853d487048766646b481242ebc5e74d5ed648985208dc9c6a61943e7962a625c2d705fabbf840893302d6346f729c087c7f9d34ff97cb9ae3db3b49a215be9951a728558fcc2564dba3cece45a34799c181b5e2c6f7b03f98984262853fcc2269c93857a68ddd8a01c8a065737585202aa172cbaedd78228cd8f54d2fbcd24774d906e64fd8530170dbff84127f5ffec779c7e2a6a57dac009c357b00c9d63a8cc0c30ea20b6dd2b8916eae0f07db64199a8eecf0cdaa0113a8412866cb1336877f0c3d8a8800487888e03e0b0190ab936dae58cbbf610e0ab9e835963fc570b902d867781327c76d5d876268ff6642042c59415be738c04c48f1d46aeafcb171b9a898ea95bb7d2a142e4816ecfb96e5e67eae12f7965312111560fecd486b761bcb42054664986ac5a6781ce7f85070a611eea49fcda12684238fc73f90dc2d9c46ab11fddd48fd7e86d3d8f119ac2936bf1ceeb6b1424bbba8f0ebb8d1dcd02442fb92aaafd9eb9c028526a29d5fd1d34b5ed9c150cf915b81de6034343ff5e5ec45f1cb8cb82e2a0085772a728112c88b88f98caca38228e0479d8d2c4431a44dd7a4b43f513b037ed8a1167124230bb62434bf47786cad4b5ca2809ffd7af6a6c700b042d3c1d74e0590193f4197f65fb37885d86ff8a710cbc451f3cefb04bb7e13a538db8cf9cea37423d8ac44e016d8cf84c44b9f63f01b170b0a8abd869d824a399c46240043293b24c567b36b5a1df40a3119d4d17b5b2f21c70fbae7aa256159a113e1813f0cd272c8fa1edc833da0ab3910523c2577b1dfaff6cc93c5ac8f5ac33ef680c14786c6fd600f2dc180b9e96f63c896e35e43e13a60b05224f8cf11fed8d85b859e75d0341235f73e60b8d385efe0db1ca614babf3fdd073b7ac3eb03e5e8cf34db305188031b1899e9f30148f444074691256d7fdfcde7e7251e08697175d25b1a2cfc6a69e63eaa08a3bd3e925513b10c766c502eb616e5e2a34545682e7f4b12caf7d6a41c81197553d3185bf0fdaf187ca54fe7cc1af5c6463a41ca786c4d60e76a2cf71294fb0ecdb9da3e2765f679e3157bc48c7b684116d5e76a13a0474db2bae834e93e1408944a3377a0ee3e48e066e1a4b6d164a1f0dad493a8bcda73c8832d256375f7c8bbc3beecc3523ab3ff53c4b59bd5d7aa76a2d617df64d87743d5601cf306f1edcf67a742b2462e652fdecd1fadd5ff5a188847c090cedbe91b07f11be7a0c5ead55724fde695667b2748da1dd4b4ccc16f3ac9d1d12e238451e02a6e568299bbc482a695d7907131f1ff76e69876570e9fbc590547d0a578af61018046788c64f6a36c1c683f55c665e5c597f3f3c04149894547d691cd93a6fd3ead19c602d385a55ae653847f812c071ca623b58cb4aeef98861a58432e499e991c33d61004b102443283018bb8ff7420a1e0a0bfd955e79fdaf57c438b6687a7c382a93a16f78147fb2087a20066d0cc3645e4a6c0a7b6fc3428af50826441e7bccbb84c92567935caca940479dbd78b569d9dae0d693d0c83ae597223bfe23024292c36d1e3bd9d755fb4d7d59db78e75d9ab5c06af0de5582791b223606790bbaa6169cf38fa2caa82a1e46d549938987a9ec3de7df22b693c620f3b885c37d2a604e78dc26796016ef5b81a3274cddff09cdf898d84f9e1e990026ffce732c37cb35fcbf8f8272c3613b9a2b7312caa9b7556e16bd7e8ca811c85d8d59d3e358c1b6f44fa9449687a01a8da8878ccd2b73697f225667ecbbfcd166958d2185041ce9b03b0908070f44e9827440c7aa87653f354fbdd4589ff4ddcb27cffe4a0755aa4b1fd51602dfb3c842dc1c0ab78b748c7afd08d1d5418d874921bd0da8b447839aee22371fa4c2ca4a182ad249704d86db449ed91422fda4311018c7da08645f46d7ed8eb207349a1cedf2e4bed7449c5cf5615c16b887f186aa44e69e0b52cace5e8948b190b19ddab7a7fa22a7778589d3e7e1964ad2fbc142063e0adde6750c238a40d39bd93e3c8087822c869c4cd8e12c6d075ed958fc7e06a0172d99bc4328fcc952c60069e14c70427a91c43d8bd5c650cc874836224f6157563aa8af9ab1e1536d1e6967a2d8409959ff1e03b6ec44f46ae70b6d48d1ce93c1b3d38e1466300a3ec8980e9551ecfc90299124b957dd5062cdf648786c70d8c6596e8b57d287eaa87aaa1116e406df1708a2a03396be27ef79338e1498a8e82c747b099b2a791827cf97bb4b70ee24d612db8f6862a8bdc9553d831114be17ba9a1223d6f919e952174802a755753337a7a7ad63e7a2d9e3a5d723a2d530bb703838b20567f34101fd1c40ba07e60ed212f1bb6b0c89f963bb1133d59dfac88f2eab5c7fc146f51d321f830de09760ed9d8afa6c4e50bc02bc560f6d2f026065cc14107471d3b40b4ca80b76a9697266e6a58b239236ee06ecc25cb4bc5e353bee892da74527310810cc6eb7ac3f1f40c3ed7cba8ddf8e270ed0c53966b98797d06c6e75d7b55014c315617ae4560af12e639aebf60232b7f852ec9d479010b3f328affea318075f5731d1f68f4066060977428b1cc935a8a9681b4b144c394f8719e80db9d01f23a18952387d513181ad082e87264ed2f6ed18a4dc1533d0a35997712718bedb972b873eee40a2a2df8de6739aa9e322bdbc4fb71601e03a5a5c05da08f179dd5c2a1eb3076383d052ba2790b19d992f1001eaa70f61f17adf456b6bccdf0aae90dabb80cb0bf303e5fc1ee1fd129f74198490dd877e7eebf9a2df0e6032432bd0699f0229803debc0da4e58ada9f4979551a51f5a9cac181db016d4dcf17090910000abfb5d7c89f35f803f2af27259ea6f8c3e8c2849e2332697a89d5020f46ef07cd770bf1540fea48ab0fd336905e9c347d3379fe41f248a388bc004a050d072f381513f64f7843802f865ff3fd9805a10e93685d59403122f68d2bd8871d5157867e9b7635842690f6532d70b4cbb22b930548640b9cdccbd0ff1e6928f70d2305472d6a13cd4513f6b46745ff86f54318eef2eb8a023ae06211e0f6923df53f7c36574664c1987ed1df5c5adc085e3e110ce91cb791d1fcfd9903b38d3d6615d3e90e75ef81b27de481e9b454dbc9b4994da8b4ce704616499645e019c21d90d0a3daa60760832019816978fda118ba67f67ea0f930fda00e275eef1c1b17d7973726cecd9b75308f9631508f0ec34f606cb92a5d1ec3dc747e44b22fa88827059ae67f32d7beb422ed85948e4bcfa0b9ae1b7ed355a3792101e67619c9f2cba9c440d7f1c51adf1c21acbd7c46982c09467c4463fad0175f16ad4b1130b9ab20671d412274338313d0cba4caf1202674d2144a9501991d4585c8ac48d00cbc0828bfb15149c7311e1210949f74beb65e81a49403401d6a669621b52ce362a8911f04a60ec415980b78fcfebff641e093465091403aa4206bdfc7767b97cc0c8e6da82e97226ffd6fd40644aefec499a666268996d2ee7f27f50c066d97d2118d118b5f754c83632a791765e500002121ba23234c49e17310ad04be4592f31221ea5faf4ccb80af8c14befea77e4d3e1f1e056111a19ba29bbf688736fcfd4a1f04cecbb170dcf51c542ff6a3df4610e173c2d79d0708f976a75f7e1e6d8476294a62af717c92afe490d6221f5d1cdff520681516f47161480fe89c043257c60ac8635a3c57db15fba1d3cd8d71b5af4b121fa3c30e27fd2562d953e22343c816317e90fcb18784bebf259a8a3705fb249c4a74f0e178190bbe24cd352775911b53f09467fbdb3de80c9424a1f73b15e82895886920ee8147b3d1498a4a123d3191e680cc0aaf7a3295bbc64eda3f32fc3aa08764c2492ec5d59ebf4a002b2f2f1d42a6308a00fda7fef23b5c892317b24b00abd135a58ce5701c5d868642238cf32969f264e28803ac71f5483212c94b66bb0b58a719a181025d17f1cc381b9cd7c3c877d236ab68be2e5ca94cb8a3847b7da3a55af18b0dd460742730c790bc19130f33f6ea11019865b1ec1937ebb98378a26a4085c04b00b21d1a62b6b8e116242c5c303bd0e5971eece4e54230ba9ca99cee962b2e89319da81566eaf50dde148327073f93e9a275a665e938342bfa3843675048d0ef8183ab4bc015775e11722490a02c399a29ba427133a25b618f164b496807784f5d8a5f6ac49349d467320e740c9eb6ea08604874f07b906293e8a71a855298917b7060a845e40401ab40ed74082a31a97080605ae7626e256d4e8ea3bf68987c2f0a7b5c653e6431261a0b3266307673cc330c8fc84441abca6d1a95707d621a2aaaee762b0e01cfc89bbad795fc71b5bfef138ed619cffef71b9d05e2c8d394954c5c21a07be021318035bd3f2dd890eedcdff3d024740c9441a4c228651ee48144a1610886d52f07d52f80046620d17dbf58830fa30d1e086cbdde0e8798379629693a9b107643fd51430f5c20e85f5f16084b0a5f5971dcdabe0696c0fe41e1de64eab245d2a2ccb4811b2b2a5de54b57357a9a80a6bca9a79e1193dd1a9a6098ecf8162ea7b2c26a21484f08d32daa41b3b1a1d9826e575bafb7eb5d74b28dd46ab98f57d2d3712fb55a5bd55b955b518b0fc68ac7b9878e61a3c69c629916a6e6aa084e4fc17d47f48e3f843c7e8010eb6c8a88d184400f1bc4198322c1aaf55863fddb0021d3b3c0cf4fee7b2c685617f49e64884b3014e836c0e02a6c59516affa8446bb119c87ffff6b2aaa2fe3cde9a54204eacc1e38d49f3dfd656dda91b5cafe586f5655de7e4658625b600c1dac54176c7b10e81a2dd75f2a25c9743ec394c8347e10a225c2e0e5c0c26a6015e6aef8bb078fa888329d40b450766d639c041d3fddc1048f99e847b0031f09ff9acd3d97455760c55cee5709ad1a40339f879af2509cd68290c6809d3d0182c4cbbd8f611a40eb62ec1c72557edefdcecdda00e3134b5b8e41fec0a003dd2c3506b4bb2dbcf588398ee49040bab7460f1bf2af4cd0d061dc7126e54988505e64f596be25fc828a0c9f1b62cb4f1c5973f0cf9ed439a4dbe68414db052caeefe64008b2ea722b080ff3d0fad241241999f9468ac3bacc28c2776644250268b7c3a4b3906b4b3350a8d141023797459f6c02ede1b74b98db06bf44e30822aca84daebd80d82c32291cce91ac304a80b4869dfebb7151493169da0c419e2d0edab5813aa2febddd1dcdde643e97ebc2ae881074762fceb1dd4ccf9a57a03ac831397509fb913f02663d6ed03c2e03a41aa387fbbc62ef16d214744bf9d96df55f78392a4d25ccc34c0aaa7ca7c4cf3dd47f508391df3e839db57f7b93838c3dddeb73e8e75cdb8898178c868e325028a86eeb271394e8ca2e9b12fb2f172405d6322ab678a404b091aa52cd18978cd32230fa2e4e6f04ebc34faf5bab8f35e4d11d6ebb2640094da629fae19de32868d75ed825f8864108873194c39ebdf25417c4020d0f721ecc4ef525f3d36e4dd63d62a0b878a91a1662315b440f3b1c0d04140012744991c2f3585dd68bf73bfdc804e394ce17c9801074e8b270c616d0160dfb2e0e6435d3020f45de64f1fd55e1b93dd17ba2e6e4a2592351b44598d732114a764c45c895546af728cc919bc4013ec5dc48094aebc1b477d396c4356aa4cd233dc3dcd322e69295e6e246af8bdbf50b410d86136e0e38c5d2ca1b887f07a99d610e5f6b8c4ce27793c1358ab34ddcf63f2b64b65c6ed06207810576fb591543600107e80e49ca619c14936c4c029fa50a4cc2034bc398da83ab6968452f451c833f45b3558207004dc060e77e437e06a17f2c93d4405316ec26c3066b6c828abeeb80ca85c52c88b8ceeb1012513dd2e298c5625a4edabbd1e8b2b495337085df65eb7f05d12e1d03e1234721541f85546141d233326dafdb85a93ab615864e96c46042f700e6b4e43bc65d454b7798df39359e2681b8b6fcdb223d2723449626f106fb9187e043e9aaea82be91247708ef1111f31a46db2788234ffe29800553e2d3a7e73ec9fdf41e6e95ed35c97a4e28513baadb0ddd952cddfa0b91002c46a7a82d802dc73f66522ad3639f886d7aa52f32bac2f1564b819049fd87d7960cba44dcbaa5453c9144df9d684a708056a596309922cae60b8c123b88fd951b0859816791cc1f7232d01ae4fd5d3ab3bc7e15dab9224dc799c934e89e06d231f5bc475a685d4053a9f3991434416fc83b103490cf4e1d882e64fccb6d4aa3d4d19cee4f91a11002ef25288c2c0085cf053b4e7a81fbd1df14ae177f66382c271f78c0f605635d6b53569ffe09b339fa90e3ba63c08a25eafa97f5c554f65373ad254c359e79ae98404b5570793d20f9a92a323aed1aab908769de5c0dfeb13a808b4a58b832f4c6e2e9d0c890e8aad48e063c1b82e04c83e67e34f67bbe963404148bdd8cf6cc0797ae998c84068f3bb3dfbc592af6268bb39cda9b9bc55513933ef62ea18d2b69b4bb5f9ae515f718a77fed7363393fa1097a126847bbc556df26932a3797968dfd63bc928f7c73575c72fcfcf38e70a93e80f9823096bf7b920c2e2cf69be33486e7c90f78c01a75a0770ff0ea613452340755cd0c83696f19beefa7988f5a0772eca56b3db6b450d829854867462465d88a5b278bdc5b75a2dab07cf2a47b4bfff10bce98576ba64bc72cdc12fbd7cf0e675ff41d002582fdcc5ff80d4bc476130ac8fab9729e746a2c9a23782bb57ee515d01f4036042df31593935ede2305d54eb2bfa4cb64aa752a952f60aca5fc8d4fac98175cd697206c7e0961674fcb1031c28b2c9ae9b2ba8a8e861a009cac598eca6a72c26541f219f2a074cc6b88040008d5234f704bb44fa1a28f8b1a4e0aba961da23eb93ec452e8b1ce6d450c01de456eacbedf8845c0ef1ec507aa684d572a6c6d510fa3569066482f40cbccb6d0046227e45264a74139d3a31106d13764b6562f82fe0673351facb4ffffbdfcfc85a30b99b3f2ceb38a6858a7d5006283ce8d2f9526f7eb942a8c886736df899e7c8c6388c0600b4479ff9751013380d0c640ef9445c2e8a9c8d86e75f4ab0a045b12cdaf1fd0d81171dc4396f2b88149f1d6a714e58857f04b954be2d48cd198cf244150c8431abfa02a551d16db246199ac36327142dfb42e57dd990aead33bf41764b8161274801148d3fd506baed5aa18a3c39ca078d08f8b1823c1e0ebf83af0c5a5697407857d580e32573f717dc8a0325c46a8782e7f10af319c230c422a5c6d4f23bcc18e016b09fe90e1a0ee05072fa07e799abc40b7ee9ebc2ce6d5d835b05c7e17db5c1c813d05ef2d3b9aca1d3388293edc512f0624b95a8ca8a15012abf54b67348462b4efa5ddfb56491804236ead4d9d2ec0c1dfbe85ba3a47ed59b12c675e19308b515132b2601bd62b2a10fdba37550f9451e2bf5c01bcf6e45fb66439bd2239f2773c38739eb5c119d26c9a38d6c7475726dc8f6db7acb1bb48d5cd055a7bbf87b22d3bbaab0937d306a408b289155f748d63683d464341118270e86b631c2cc6448e34abf3904d6b27104f80e8762c398e39db084559ae88c3309a52a999bc40be1c09b8ed0c9bc027fd753935ed3cfe4875ec0a2354aee2fab065e1dc10b9f428621cec5c23e4b9058701c4efcb1d81fa68f5a6cf5b87415cd1fea63d5e8b74bcbbc94593373e9aa8552754554da69cce451d28770459d622b7eaa0e057f5a5b2174ce039c40fde8eedee81339981f5116e4b27de56d4d42d1235897cce28375aa95508b14cccaad574859e1084d0b9d581a637c14aa8b132a66c03d1eadac01a2fae834b083c7f606d911c5fd496059bcab9ff82c12da259eaa2d115f395c2a91f91ed154e22e4aa1a8e05e4527c4a19943cfd1c42accd41525586a01fff8e66fa06c876b2e80be2c22f1aa9c2a57860e18f78704999a83257e5686b197feb97f9ebe47494586c34ff91b587b68ce05700b9b72b94f28f1522f0f08cd2a1be22a83fc826daaa20552baf3c9a9abfa88d7313d5c99767482814d9146cc431edaf7849c12ac1a6f7828737acded992383d8622847980e30d116b00e70eec3043c839259601e7378fab8cd9ccda075ba245fb6a1f0b62cbfa3a69b8c1ae753c677f517c8e52aa5e8f861e54264a2c43e524a8e36d2fbbb8b949adcc2ced9dcfd09e3d82f3fd703acc4077cb8292f5ab61cd830c6d43be9855461ab7e2da48f233d53df31c37a058045c76ea4c7a2046f572f54873bcd2db17e78bd488e4af2002949bd5f3969f7de5f7053dc844a28702de30b8b104411c2f8d6bd527ab3321efe83947ef3318732bcfcc6a74e43800240fb0f028e53de04540b80b7ec1bf15605707140b0c57eafc8efaff8182bffefa5627551462394cd8ff91c3f3e1e87fcedd3cb504cc49a93cded15f9e2eb0f0c27732a2cf846c6cc77da5b3383d3c2a50d9d87b4ed94472ea7b55ba2f2ec0fac5b98fba936aab4bea63e791b40bb270775d8497c078d046d3069d28dbda02aa3e6ae0663e83bd102dceb4464e80d0b8d4bd19f33568075f3d508c1bf060ef9bb8a4caa0d15aa6ea98a120a1b6a6ebf599ab1b74d526edf7c9e00851465f26843bac5df8a0a3f604aa6cca8b088ff5859141a66dc6c506be24347086309fb9f161cc2b035475a3424c0ef3eb0f70bce43c280698aeb960f58fc608e662f4cc046af85219b88f460786c5b78f97aa0fc8f3e34537ebf86a2c0b4f5618d042b5c99c428068c3e79a9c7997d35fbd35c0f93439e48f022875a432755d5cec569a6904097a1a99fc1348413bba428fd672553a40c595da1e721838e0a547b6ab1a1f1a08e8d4265f2f9273258f60379ac15bd15077b8656239310758b422ac3667d1b14f4c91c88f6075e742a98b5ce08d1ce57e2643333ce97e42a76083f8f2cbdc484a152ddf43f9b26368cea0cc58987dbc690527a80ea8cce61cd8db7774a6563a3cc92983ad3564bb0e83be9b52bdbd32aef41193da50cf1e1a4d755997a5ede7288e4d48f43876080bedd047309cf83d2f651054a42719f599343978dd444bc6d0f3ec99f30ea4c0cd2de5e2f642784efe7aa4d595e3d6e3c528d2c3cb1f184132500605e51c9e2642a5db48e116f650905e49ed81373d1e2ed86726604938149e5f33a7293a013e9bd0d43d29f594ca0536166e33987dc61db4e0917329cc93c2833391f398e6a4c0383fe4a1fa1f709d9176bdb92536ab646d751e59dd02db2d7a9e0108409e7170277d5f858dac385bd40bce6e0886a7b4b33bf4924bc1f2423e3cc5698ef272edaa02bca8cb9000e4120fe35b43e7e9c801c0cc5da4a93013dcfb665d05f5a954266e20a9705b8799582f8f8cbf0291e681740caadb6cbcdae5eddd814e49c080bce664cab1d8ba040713f0d3849e898d7e36ac411919fb56018b80d403dfe62d603ad6745769b60d0b6046e9d1b4a9c896ece6ce644c2f6b922273abf60e825b639a8034238450f93f822f4d96630af25b68db1b8be58a8316f2313c5f09c6ac8ececbfff4081a9a938da4e1efe57ab3cfe69115911afa49897f0eff48f450c962168644b1ce7796058c260570ba229b8df9f7e85286cf1d38d5ce78b4a9f82d4b448a0124b1dec0f47e0473a5d8d07d9c13f42734ffcd29c450538c8f6c640d56919fc7f230fdbc387130c9fc8eaf02ed16cb74cf7a4eb3816371ab3192f6d82ce8472e42653d22a5fae0ab296d935e647c5a1c5d9909ec6528cf83cf7a4d00b0f74b0e4355900ed127e64baf6836cb8c18985a0bb48b28e2cddd0c83c05ecf088f45fdc814fd915e9207075dd8303281660411f70733f50a661d57d2dfd5a2a296313ba43c7571ee5c46e464826ccbe1a6d8726b67d0a4e56fc2ee76bf0b85c36dff559c1ecf33d4a32b97f81ac0fb3a1ef5fc8db98723f4cc5d2aff315ce2e0a4010112fb97e9b527f41bd67b2a66d43dd3586207f1d654948151cd44ca321e756c5cba9d44b9e8f724344c4e94814d82314394c28733b4877013d1e6cc237e4d74101c84f49ba9a7d00dcdb668b3c3b507e900887d9115aa991fc797609217b6a3de8f7bcb078659a668ec21166c538fe894eb143b3df42cafec0b8a583182ba2c8ae314afcdff3b215ccc60a21dc18b730d59d0d47ec0245c60a65e5440acc76c0dafc93ae373f8b6f56274e079d995ea79ab561fb4c15a5c307b19a617510b3c20c215e3da04ae61b9036685c3b105026d02ab1278da66627f11990970f347d22e47170894f02c2769f5aa0d5f6db46cf3ee996c68ede1dea816bf49158eb9a3a68ea6f613d4c4edd46a9b295637a4c6b2c8c3fe690d61090ad3aa6ea10321e6b559d6a453f716bf19860ca76fe01e8d8e794643beaf0bf322a0771494a2645a95016531daaf5c7a21b4db01c558749e3e159d952b02652d5f81a59fccaf8ae3f280e727c965e1d3fe67ee427e1fe64f1aab09032d7a09f5aaee06c2d4a34a897c1134d9a220e8e0c9b7fb935ca4401aeb6942732c99b8401ff2aca2398249c590f0cfa0d5639e22ecf1aacee33491a5a4e2d78882659dc06d3f0186b09ff8f4bbec1b6e161d164c9a8e3cafa1ed2278a2c8b283a85988884acd6484b618395181c9ba38abe4f7c57aef001fff311fb36504a8b11e9ae46272f810111dcbb5a62478691b84eb9ecdbb514a69646b4ed801f3e8b78e913b89de9cd902244d9cddf12244db728227795977a1814d065e8752d885edf1b4d65d427ac0550bba5417281eb56c05753720dc997c56a08c44a1332f4b46569e13c0568187d8cd7ac97a158c5720167da760119e015bdf1db7d893e0e4a5e40bf0254db2ec91155413d9cfd912a0242e6d27bb974a01444d99436296944c4dee492cbc1a086f250db52ccd47cf478b501332138f086fd367ba991573affd6bc0382975ff0bd10eeba529c6d9dfc12d987ebc49b7954053fecd8d1725f77d75704fcb258c836faf56b7d1a04702903b5a0a7c4c217feaa9b2dcdda821d7eada0da9766cb4996ebeff3fcb283897ae568417b4bda3e3c4dc729a39ec3e4274184316e842adf06e6a528231d82d3c2ddbf9fefbd06f749602089161601e4093f67c4cfd1e8aca4167104346836752cce382ba0868226f0d9428dfce013ed1da11469c158678cd9c8fbed77cde8287702e8bcb3a5f0b12caeb141701747caf5a6d9e694dbb6c24295241a1aed81a7d7e49e22fc0e4a22756e4548e1a222f3da9d318a387db1ab0d8e54a62bc9d2d48a50e98d7db21c18322e3032dfa2d5df302015bcb687910ac3a23708fb32b3128b03f653dae55d9b7370de05305bf6d5e54ee9488f2e426ce857a5fa9b33c3863b5810a1dd8664aac0b703439ca0d038c18b2c63fa24ce3575b5c4f42430088b140e7fbbf1420712f8bf92043c01bc522774d50a33da91a2f22e476bc05dc5d8a308a9829357e1b15477e171c960dec328cd521dcc3c8330ccdf52910b11223069e672ef024fe761a618befd11814951b2015da51ed54193c22d1d6836a6d706f4a42af4ab275ddb9f6ded2339722c9f3fd029b5304c30b07b0fbc0dd092a386b502f802aafd202f554242caa1059f85c7e41fc1d1b866b4349facaba4255b0f849b225b10a513d496c6ce70ba6cbdf166992fb329cf1f5ceed998770d56174de6f724881cdac946eb34d1c506f8ba86cc69b97d90c8b2a0e4400b54debe16e69c6654d2b9f1d36b34f80272169cbcfeed61c5a22f146da42a9b3b9411cd790b2a8b569c7554557e55e555d10b0d0474d75ce281cd59fa3701545170407dc76070bc03e81f6367175d54da3f9dc3d10534fd8869fea87afb0c7f4c527066fb025c2ebed3f5d8bbae1082a2cb5acb1b7bc659ec2ce0f95ff10d8ba2cb1a3ffe56fcd2e4eac18b5216cad870c9b81f4c29acdf211990dff82d7ad422d52e7f8d8b097358a596dd3f19dad9315fb5c51f1f9ed9a0095a396d7ee36c07d1320aaaa3df05d120362dd8490ce469a55b89b0b1bed5fe317edfa1c0e19106046396ab0934af72be7be909835f651a153c2bf208269ee4270067ee0c6dc0e66a52c7340c6d86da2e5dda9dd30d8f7a34e931b7625a30efb196ea2b8b1a1fac2d3250791c79f3ac92a92684107218119ef83d5374e4d78538b497f7ccc8425ea5cd1b7512f0ee1ba2bd1c5981b9f7741164743808341b91cafa456038417a15c049f2169a44e87cc50fb549a62cdaf0cbe85a4bba4af70adb5d479dca07fc654ad1105120b877c4d66f979e6149a7ceb655888f4b51d4d4f5e6b3a82e00bd423b23d334856ed1997562cca85921dfcc512cd01bffa23b5b88053c7d279720057a0933029fdbc049f8ceef897411a919a624198bea23c89b3211c6c84fd77712c9de5b38cf2cd73423ed15de063ead87a817cb85bbbd8e58ac46a202ce112babb60f648eab22a3e9a71e702d8a5e01192136bc1ef4a619d4d0310ac9c14e8d0e80ff9dcaf887dce2795a48416f4e64ae0188a67cb9d1abc3f1e85157bf3aeb6dbd3f6ae40257f95ec3005d530def59031e6360f6468c30a4cd34514240e08a8cc08961bdbaab75590a5692043c17b8cc3d6780d955c4f1792b781a93f45612b932e221210f17429b00b4b35008145482f218abab9870c30e191d0871727ae944c6ed1571978477328fd986509f1276fa6adad31fc978fc5e96db5b7404db0e7086d5b373be097a1390c8a92e617be7848e0856f5a5e53d4376f12c3967fddfe04ff868cd428131ef2444b573d0e7e7899b2a5a869a2ed19da1b2b70453b883e74394feb55f53ccb510e5e0965e2828492022a8a7f88c6f31c5bc6d72f41dca8744a54c562b22401a5dc368d5ae15980f0da0554a0ec133d8bcc023abb40646344e6d17f486dde77ebb0fe3e49ce0acb6afb6e11cf70d874de6c7c2b484e57efde757ac154c4d53ac149bc8b77f414808b2a8b71a3091a81c3710e2a9d36de5747066a9b54cca337b10ea947944079de1791066e959b3192d15ea7959bc1bb39663dd9cef5dbd1f36f20d6fe7b0949c37b7ae135ef57e70de21042b54266ebe7a743089a783920aa537d88f09208d8b392a4fe77d24793fd3484bf6de780b65b2b3fcdcecaffc48000d252076a2d44fde6c283ac595e0f99f8f389780271ec6852f3f63608b47183942aa757f07421b718522199772b50f07580b7e4e98b848e6d5f6d1d784e7b8b8994d0f2b66bd1ea465724f6ab50fc4157215ed830be3e792b0c908a61ae01875698cef994dcf63b8447532893ef274cc25b7a8230840d1d442bc30f3eccce9485472ab1b3ae7a20a7d40089e7d7287048de485c6ade633549feb12cfec21f13e9b93877f32302dc77ebf0bd6f20325c6590236c9c0eaf156777b282724361d3099eb116948601a6f41f5abf30834ed32338f169e4002b3d026231a48a70bef6d6b72cf3df05dfba0b03e4a357ca678b885f3f542c8fc195b52b4ae1e34df456fc42d28021c8ee01c3cc9ec2b6612d17044c5ad99431c9dd7c65155c85b8e0e8760b300f60e4e41e141743820c73b011acbfeb514662c53121e0e8d5e752eb1e8c639dae88638e4ef0334e50374d508e8b31a74acd35801724e8be6f70cd5f0b3c67854283912626aa3d8f2d861a2813752bb55059354e09025da3184235f983808bc11dd8371668c39d49360a4e81ce740d03cf7a4926726b908468029eb280c08f6ab1f32fde9b9beab2ad2fd2217c8c0946e928e49d8e6f901478ab904994568bd4bcca65a92ea0ac847efaf359dcce8517cca9e42c62506ccd0e8732256e1b0250603d41223da7346b75962b6129b46665962307d9563b9813049b0c4d099ee2b31e19544cd33a34392f9a1db0ccd83c7c7ed9598f484247a2d591f0851bd728d13607891ad099345082e1592e7de18e938315063c05cb5cfda080aa52b2ef8e12134c8015dd4287f456aec4b3a842b858e4e7fe6ac348cb0c17360cc32910022ce688921394672bfa1017c757c0cbb871bd85206f64d3c4530c1c6eee1435f61b0356813951920a5626a4a1ba512eb232940c80ba154db09a4782075e87c5cd399bdf80f8a09358b22c0dc5a65f3af50db313913b5bfc6b70d93104f4ea4707ec5c6413e96a849bb9ce589060b725689d5b4ebba4b5306c0d795eb505495ff90675b2dbbf023381e3271c5631ce9be67ef2d838684d3178625ed2d5be716e72fc965c690602f36295f64baaa5507984d45e3250971d9c0f97211b6e67e44a431bf3e7c09566de3f59fd7289ca0dbe7dcaccc49b6e68d8ea5fe81d2b9f793848c2b35196ee684d15d026e802013ca54a2798ada5b29e4d5fba9398ebcd5ab3db9baad6ac6b702375e8d3dffbf6ed5ec7794ec8b28b88b5a5edf578858ec65deb1b6c4c2ee4bde05c8325f9d3ff17a72db12083265590da06ab3f875b0735971ebe7a0048d4767e330647796454572423045047011cfc903a82dde9bbd4338319785fd38c34e19014a2bd6a920f6480c39aa17de8790334c562cfb61494a90dc14a50a95f01a2a82185e63d9190f0caa75a3daeb79f8025848e472da38c13036ce84eb48a79d0b0d1267a7c5ad0733d0bb050603142edeb4a06044d17427b781b27039abe21b17024b3f273903c4a7401957c90709df19a4451e404221ab48f05ac0914e0556086e15447f7e90309edd83a96a7f9ec3bdd37c87e8970f10043441363f3f769bdc48e299662b229b781c53f7d00a7d39bd8eb771662932fddc90d40a2becfec6c0206fff2a6a8ba62f933111591fefb4367cbab6bcc840d8bdd7e052925a878bc1d1369f88629f75c4729e2c7036f652dea484396fd48124f4187d144429aa57e9106d214e7dc212db4365ac8f4e0b3d24134113d1250b3afd606ad1bf7a141affefcd99530f6640e9dde85108b04474ebd1ab8bd673b51ce328dd4e3cdb314f0c8f962e3106b68589df552e11e7903f87527778c3fef9f4fcef42f7822c7ba86ec3e69ab27316a8f96ee6b9ccca6ac3ab346815144563d07b9be14af51fc21cce51dca2aec0910a5d29a8703331df63198139f9004459a0d90638ef7534c9028ed620e2faca92fcecd3aa57cbaadecbc7a0a9159fefd2ee89f793007820d3418991d907e61baa1fc668ee4f8ddff5c77cf488059b9a9b0f5a277b28842588e75270c1e77fe0d9263e940809a7bf95224078a6804448f2c4ea20521cc7b2171de2603585228bbc91a398df4dd927f690972a8ad928167649d34b116bdb020574c04370ed6da5e7f928bfdf263cf1591a4fb63ec147d880d0555e40c022092959e0e7e9f86014334f7afc874940e1f9d15d61db7c8d45ad7698db4b495d238494df84f7074352761afc8f4dc48da95174cd84f9fc02c0f3da4d53e685159ebab0c2a6e2a7af29fb8cd5b555f091e9d20ac014d8e7a32e34b3a030ff6937ca54596ffa865199e49c4e5257b6a5a843ae49bb2c8a58789c4639fd6e769b086f174d0b1f96a187a41b0f5656b8b80237aa8066169d12eb690944835ef2f9160212ec62378a09d43153c9ef9fef570953dabec02a51439575079e680d23b5a8bfe370020ff32a5909aa828f3b5bdb6fcc8af6d75b9f8ae112eefe561f51602a97225b3580264d105ec6fba8dc052d6f9e2000be6ee3c077f6020aee3d52ab39069c4015d8d9355386e7566dbf237d00b0f9326de98cae0fe1c44c4bcf047ccde6a4baf9a7f8b8d533bab5f2dcbea68193da8289a5d068c162002e93047de2d35e4f07cc52b6a65cbf66f8b536b1aba664e41fa7f67e7823b70894dd5937649a7c0a199526f9928bdf722e8aa444c01c8e31e8882a80d5d7ecedffde4637795161d06d9be4960d40ee103a3ca9aba9ae5e4d9beaa8c18b415d7e790c966213f8dd29d4b54ee55cd580d06b99427ead75fed4d1b1ce42a4ba29a9f41039f542d130eb2a331c41ca853941393e9bd1f7596d963e66506c4e06371334b65b79cfb677d61b6bfce635894c06f9ae1d6b5e9144d0c10622a57a934cbca5f6df39240ddba8073ce6fdc8454b74e43fb229e9f7b5450aa98ba4b85c0b14415cdd4433a0ac0ce309c52d7ad733d845952cc7246bdb406ac822d95096e90d814baa71821cb4e39ec432163f6f305c4fb76c6561cb2923dde27c17e5339145b47fb9bfe450c052d893b99e4f7302364433932e4a5e26b0184387bdb403c6e26e646dcf3ea58269f083ee1911b759e9af8d141c0a6cd24a1473194c228bf562426ae37667ea055cd4fd8b7f6c91b77040e239189a79607acbfa7f1038e3187ac3f9f4eec9bf1ce4f04b67a4aeafff2ed629e6ee031d7679774afa270bbb784175cb0a7644fffacbf40438177bb03ee10ee53346403c1a284430bfdd0de66af05430fe8413a26d6bd791c7bf4ebd1acf637098711efa7eae8a6e73aff547a421587cbf508c9b5271e8a5057d136dff739a1fc05f45ef44d553e2b95ff1eccce39b7c5a8447ae1b78a4a6bdb4a09505a7d8f25280ce2a7cde2f6268062a07c8c3985a0136001bbdb602d5713543e8389debcac84df0f156f61dcf2c3e3e462e49b39d3f16c009f8cecfeab0ee65b2a5ef168a9f21ae25e4061a1c2df63a409ab85a4506a2eee736139612ff79044f62bab0a4750e56dde97248eaf9d745346f5a4d6173849ccc8d5f02c2bd55886162c8a795332c2673c7c857f5d689fd37cba093c0ee588926a1f7f5a071ea3452340d41597682663f39de020bcc3b60b0f2c2a3890a4c893d41f26c7097e679a1bc798c3d4bae849079048866861b2305b8f7e9a4a111607c9ba33fa594b7922cbb620de516aa172e6b0d41c033f890110644963578e61e6eaca32cd2e57caf1825fb93bc6f09705f06dbb88160168ac5fa76296042e17b5cccd9c3720032882ff476ff7000ed4dad02d3186dc25bf0b41b02fc53f5a292fe25a4fac97afe7965a377a9fd09779d3e5eed756bc10b6b61bcdd2d354b43ae5cbdc75a2bdeb3a18b6736ffb671d5d1ba2fd8ed4430fca2edd450aad22c253f88805e2bfb44dc8d4a617f37f49bfeadc8b8a97b69f40ed43003ca5166785b30ce424a07026d0563d3624b9e585045b675d6f88af2f2650b0a863ac375b916ef185a765479f0392e82b1fc2c59c513b167383d6704629dbda1392dda331023d02c5379d0573c7b3edcb730b3f3cff125217befbde59652ca246584074d07580746320713d115f6efcb79c4d8398c0a696811fb8f161b4ac638dc9672bb94c7de85d409ebb17320a5443207fb66451e29d163b74618e76c8d1efb8ff65890635e038243192e13aa3d09276358e289e88a8b89ea10c456bfd76b074de4f9b5d62ded9980c3d13770f4e72e98bdf41c30fbbef454a8e1a763b7d66f10cb8473497966a6492e0b6369e462ff0eea3dadad991765cfdf2457f233f7d6124e32dbe06da5c2c9189688a5114ac3d4c5ac13cf3a33c59dd1692fbdd3da7c9a8a9b96b3fd1de89cc9134f3ba7a5d576fe80ca2b355666f369aa8ca394a336b7a320bdd5e6ebc9c9e916576e9bb3d3c0dc3d719cbc76823033a5a1c8aa524ad90e020eed6e5b314867edeead0739daa7e368bd4a5a6d7733ac6d574a99d17a86a33da59436b576d4a7b596a3b46a1d05b1ecada3603d2339ed4caeb5d64a67d6d7959d2caed5522f5c4a29a574aa524a596badb5562ff294b56a980911194ac9504a260d194ac9504aa64c1a329492212aa30d7112c76a3808f57e64d8478fa60c671680016b43397f3e85644efb0b9e0b2db6c7b4cdb6e0b3b47155051438ceebd19cf34e68c9c26915f734d055630d6431a95c6834b4032171b43f7d0b21731ac914125a80c7a389ebaac469d13d9f4353680aa986c8546b8d6a427c35e31acf4f8b9e4bc920e9124fcf52fca4ac1c4824f1a33c82f59c488c9933e69c530236c4d0303ddd508689d6a444492899dfc4738e538ad1112c871fe7d2e4b2f9386363a6ecf264c9044a82708202d49a94499432b36676b3835c2e170fd31b520288899a49b09fde4b930b2945e68cd9657ece897482a04963ce3969d83c0d973f3c0da741430a134fc367783d34da490d92124dbec6b3d75393b3a7bc9e9c8a42c6a71ce5f5a45028f7bc1e54f6a204f15ee79cd7d3d56c79ce2513cff9e6f5709b4fc9b3d56836afa95ef3aa6945535e4b0204afcde035c75e8ff601d2d0e31b24b47cf655e1ba520550bebcc5625c3aa0043de5e95cf434262a86a1dc7b7dd8c205cd099cd0e204d6bad488bf4d90f99bfa7befbd4fb0ecf82d96780bf298fd090e7ffde27ba99080bbf7de7b594eae08c20cab595e13a323db164e5d132c48b426ae89971c300b6584b6262c38920135925ec91f71ee02043008426a1206d0cbc94f08582043e4c48720169a849c9400a8014c8c9c2001c4c216474e9e48c2020c8c6aceb09ab55a926d5e221991428430b088b20598dcbd443232832422224cf9090a80999c7a89f433f4c50f124ffc40e12c2a9c8ca1095119436528a1b12e7d0447d4aade9609218b3c4e8b2717dbc4e4d3bf2ef26c59b98133d6a2f530296b3e8ba40b94c773eb228bf254b756ecaaff2856cefa9defbdd8859e0be93e50d5a2f5e0c81224436d2d097eccf116477ceb2d5abff19166aef8c287232e578b3a8fc67d5db65b89a399ba09123298ca70e5600229685c689ac660da62362c571966dba25a9dcee974ceef7a67860d25b78f5c0d25198bdc9e5d467ad0d57458ab614f81984d6dbd6066a90e101062b94b5d91658b1d7e39fefbc27de1beb8900497f558fa419f66901018114c245dcff51ed02dcd39bb55c9e35ca22658bf49a1ca505b94d8dbfba99f30cef99b42736922993ba6d2c4d14adf48be5b1cee7a08390dd1272257d1cb08cc8fd4c83775b5d84a734787d163cc2a6aa3986c435a5a4899e8ca4b6e400daf81ef837c4f81c8344fe5ac431910276338c2c818466328b9b8202aa2a34498248941248653bf60cc9827a630929e8ee88f112e308ac0782ae3e933901f080864a466a5d3cf34c9a7e9edb125a963a3c2f17a86a8b5d62aa7d3d945e8104dd3d4dedf5376eaa686758cba6a0f5121fab2e16d1acda514a3445a795b25a34396070934d44343f410ee21cad44df488aeda312c2f1db986a8100da2afef1b1a1a72ad00c263a74cfd31313149a61a86b0d323fa7d97c7d3a1efa3b6bab223629eb9d6229e007044ca5b8b268f19a988230178eb9bd763712de18857099400838594297489985a6bbd7992479694357ec479eb7289b75e7126810adefab7e4ad7bef48a4129c1173344009a4a126ae2d4b6468112788626b0e2290c04494f1d5b3a326967873f563f7b58aaf4844dc60f4d56db64ddd0f752888afd8fab0be1889b1394ba7c77810be7eb92ba3648a122c3708c5944c716448496c042a00b1a4a40b20158496104999ae8c5e896d1a2a6fede4412a8900f6d6dbf5226491062644b691921d0905c5c893525a6b6d254a7d299ca199acd43b0d6b476186f9f5254df333f46b91390d93396d43961842501ef1df681c74fb0cfde3acf9d622736a97e9332db616c9a342af7a8c18305eacf826244c4628500dbb89f369516cba847de0640c4a9c80f102230999ba1565fbd2b7a2dc33295545b65e613926501f75524f1b4ed89c3c351967f2d48425f41c75b6a24c7f86da0364be01648e0d9fa3ab38796daba595565aab05a9137db24ecf3c51273379a430aa449774cc64eb29bf6ec1ceb90c95023d3f9239d4fdb90b6af2a88aa437a1fa388fde8b9f312fd87c7a2798900a1b70bdf819e382f1c7a3fa68a4443cf50e1ca90d638b9982872dc4f450bb9ae480c482a7def1a74e943a5198cca14e614e79a44ed4a99337757aea488410947b86dbde0d7db2734939e8ca488b64e486f5518bd4c71c96671ed1d65affb99ea7516647cd534debd6b954388b64ce9469349350c2f3ba0c0c4d7120ab03459c1651a107bfb528c5a8c529472ddaec5b53ae97bb20933cc11b2d5acf204eaf2c0b9438accf10640e94b7d979b48e0cb717e4c92473cd79e1640c565e62573aa350f2f376ceafe79cf34eebd65a2c28161c754ed6306b9a38a4903a66986d51253b76c5624bb228e72862619c33a543928aa2fbf7e55cf47d2e242a8cf260b7b021dade03ba859faed3d3cde92967b41919519eed888a1f679435c13823a7ad4ace9ae60eae0989630ba9837b42e650a7463267883c72464f7d33a23c0d83e5cf45f1c7cd6886238bba165444e22029112bc5eb2079b420ba72cd1d163671507769413e04c128b5520cd156bdce84a44a4891ba85cd1dd9d3c4419dba164479e813623cb530091be229bd22f494a67ecc96640ef5322dd2cc296b6a91d2316b921486f3e74d14e98970ab2bf2a6183386081afaaa62a4e90a11a22b36c03a06548a10ba62435191e6f055ede029f68b4e9eda524b8d24f258b52c5b142f8f88cb4a749f9883be0d67f3957105ebbf3aaf87528ea31dfe6810875112e9a56be0248f386585ebb42ee348c963ad49c04bf77e988180a41ab13c561a366080687fb8bbbf5fbd33b106ddc7820d3a8c73630fcce4117fddcbe604b9060f073b747043c2d286854a67c991f2583de8a42aab168ff0d966e70be797f5e086d27ce5072c9d7f0902019e6c96f2511e2b88036673251cc019d939620949494a4a5c66d69522a961ad1f783dd5034a716a98002080a7dc45041b2fdd032f64e3d54baf60371ba857edaa7cbb1a68b028c256de13364410800e00bc748e881bf39512411e7108f57573268fb547cf11bf9e50109b624c4fe711ab7d7a3ddc00c258c794478c03e7fc7deea2c89a2fdc83bff4400679c40408ea845445373627050802238fd58397ce8dd065d747c0f325c4f79da1fea42474b5836e5eeee0965db493457d960e0fba9a35a6aa481e717e52a73c6ea3ca54990dcdbd837fcb22675511271c47bbc3584addde7b25186bedbcf25e6b6dadaa5aa59492524a697777eb349d61ed30febeee9e362fa90447a9e5e5ea65d5ba9f86e30cdf614d15f93e8d410c2c92dfe418c460bc6169cf51fc35fb5a29a55d6fd42c7bea17dbea9cd7d3dd466bf52c08e69c93ca7b9db22e9d5d6bad1a94c9e0386fe0b0c2a6b47691a758bbc8bc5dd5a2740dcae4e9b8c5fc72529ab95a25adf5de7b6ba594d6ec95b9f6da5a6bd55cdab95d8b6515d265aaefd3b67a2de6bc32da56b39a6badf55acc755e190f4b9e35f33a6bd5b86dd3327bc95cbfb6d6cc6b9d30d8b56450489cc471ae69ad813797a20b558a9a735a87bb70a4e135d5f7691d963ac2b983a250e36bea8e976da28f4f18176a21e6b84d7bade332ceb3365ece53e2a01fe86eab92af732994ddb814d8e3b3010cb28794a7739b95a2a65d97a207e6a62d4d58a5ce759dd763dd9b3b54ee58e917d549a9a3276c09d6e459dab33a795af0c70a7a2ef584101c1753923c28805053acf84c8eb179db658c278ce0a919a454f514c7a9e32a9e7aa6fe35d1c55377249e22393186d1d3536f297982de6e915f473c15a47ed8a1b3e20955840f1c97208f092642b07561eab0e8a0f9a880897be12d4bdbd22babb8cb8bae2413311f7c52006302063f3419b97af0b1c2c8a8071e7cae10322a010f3e312123293bf87081195d09f2d9f2649368c0c5899c7a896483183a10c94174caf825121120a62042048e75f9d571c660a8a4d7e1f74d27b80a562f05196cd76e3f417e8741ec6fa0abcfcbf4585a2f05d902cf1b94cf49aeadd7c33927bf10c1010636e09f1d7e248fcf3df2f8fe2087782d244a86915d24151f19327f21b9a03c124c2ff50e75ecf61ed12e7287b467465be6b1cb1ef398cb637bc563dfbae49a9caab42937e7061b80372d7c426e1ed0adeb1cf806fe8542a60e2598e2490e5a1414b49e68524b94d74f4bc890ee86d613940fc95460a5f544f331075d217b89969c280f8bae700aa6d43b35eea36db2461f1ead11c67e9fc84c3d63dbaae48cebaa64ef477f295bd5ebf1b9a17e9d83b0c6e80a7b06de50df20ac4c4d7485bd82c92f649fe82d7a7c891edfa1c7d7f5f8be1edfa0c76e63168be4c9fce6b1db29bd73d43b358edd26491ded1f76492d938d8faa1fad91063e5a2a1ebb7d519e2cb446f87fb42fb03ac99c6a46aeb06357961ac6e3ba4457d89f62c9a37db5973cd6a5c7d8ab1592875597a48e49231c3f3259cc58bd7c3351235532b74774855d7a12b86e8de80a3b8fa64979a444449cd68d9eda22d3f0b19f28914d980ac74c54c3f1c35b953c5aa3948fd5db3b98e11b0df0468bd81bbc115bc42e9f01a9b0fac6254fecfd243eb5881bcd63dfb6e4544d78420eab13c83d57ad95c730db84d43c1af2680f92c85928e4490b995f0f85c8319e285af282c9d5ca42ecae9634616e3fbd94134d049916b5883d1552a21631f64b947d70f619e00cf37f481d9b63f7a13ccd4457d8714e61e750a0fc0d94df4cd825b6fdf4f98de6b9d449af3095d22bdc44e66007e50e7a84c48167cf6d4bb6b676addfdced582cd11a69e196ecdf1b68034d9489e9e808069bb1212712a71165fa8d62fc7dce8302a13fcee7b6701cc2f43ee61005b14f30887dab5129bd6ac72115cafe45dbd712722c91bb27f2f4ab9467488d90c917d47ec8d2c49205f038ba99d685deb1dffe02d0b7cf97440193058c7c177d3b0cbd93999424795b6951ef8cd40895d23bd4c9b77737bd14ea1b6997af42fef68e873c9d47d3a3ad67fcc0c918b87079bd5e499cecbd768bead5722057f0c909f2250cf2acc19bad1952f08547af107f5459a194822d4a90b6cc0029055470607008b01553fcde9b44149a8e3a73ac00fa5a3b89134014e32c35e1a46f7e578a7c2c588a319d398382f602054f38bc709072c3d367003f9014b5010542568ee0f2d2ca112fa0f19b19194315463f473724f919ea19d8ae497ec65b6fa30204215d41f2d665116f2d75ba02e5c72e7aebba42c58f3de5ad9d6d9bacf0f58ebdd6553b640fca4f90c67333e90529c8a0797b7611dedfc673d36341f3ee6aafbcdced5a0be19194a6e0f670d8e099107c830078db610f9b947d04e9951dc149cebc1b49061a7d967111d2a29d209216a36fb083f71d72032579eb1f2c6951e2c7b9f401985cbd4738416925e8a5b74292d46225cb4b6f85ab85b20048567cca793427bd9e9437889de835b0bb3661fc04717ce6e998c9de09f4a3b802835f7e823bcf496e6eb2ceaa859f731bd24f5083e7384e7a0270411e7da83e9b1883a3086b7ae9e998495af2d33914cc043f4104fcf474ccc078a8f1bd42167a6d3d2d4b969fa0cd679e0928ef10b7e63ce8cd747933e705718338bc3d7f6bae037163253f41033cc7693ec1d9d1e95d97cf35d6b4c7a1750c66ae81a38f769486a588c14f90009f8155e6d010843eaa03471f5fd8655d9662ca4fb0009f819dccf911a47e56c1f80902e039e9ad90b578a73441f319de4cef97f3a078f47143eb13ec6e28b5b002cac151a565cb4f90f519a881a30534d087eafb06eaa8052f85ccfd2f289194927c8633e585207e82e14b6f85cc259682c84ff07b892d853d75bfa0449a22f617df5c9748515cffe0550f84f31b02998f0a479b4779d722762e93b351362080f01287a30fd5639fe1383c77935ef0d24b013b0daf711e3dbb098af017f452b8a10ccea3db6380e633ac1ecab9eb61efc0f92660e7c0f91c9e586250b68892dfddb44ba4242b2f91b018f113bce990019acb1645781b3fbdae459473d43be1b62009a416bc94621291b493a246218f8465e92798e3bb934851bc7e821fbcf452981b9a414db24703a2b1209ffed2d7c4f7216617cc300df0a6ebba5c3b1478a36929f046736d626d0678c339e79b731c5803e2c61c28df03b78e4e303ec1b994042848f593531e9b60687cf1aa1db2e73304c14249e6f56991bc0d225a1332c7e1a87aec5cf7dc8979b4e4d1b863b11853ac29e6147bdad1f2e29238a80d1b8f026de85c81478c375c446346b1a3182cb684029d9317dc41e6e040cd97ab45eb63b55e8af0e2c48b142f53bc24e96879519239f2b9bbfae9ad1e05ae1ed7a6a6a332f248e56533fcd16203c55c73fe0cd1bc0ba2a7855c665353513657d03c3b613e9a3e4a41f389a4458d869f6e83fc741cf167467dd84c14d8403a5e720fd11f5d6c8ca39e29682ea4458dca167dd864e7d10d94f35cd609c9951cc244b9e833922bed0a2800f05acef3681498f318942a787a097b89d465cb5ff1fa0f5e227591f29cd669e06c0a9bba145104d1b4ce659d06cea7a32b623bb449d64dd488af8e99acb92c0a421fb502ca37405f86d94def84ce35ce51e31606a13f9d73ebf54c07e14a96e102d3fd65389d66538b5665258f3e6cc2116716678fa45fb12c5bd4326e03c7efb7e975285be6e8e94cf753bb94d0b491dea12678eb5369168591517e7d7a5a389f269a89e425c99c4eca8c9b4b8f85e94e7a678e38484b5497d50ba2c29135def0e969e1db67e6bd834275a89a02c709e4538836f1b4931b37a68b3635925e59b7363d28cff7a1c2cc7edf2f160a3d2a6c003fca2d131bb7804af5d35b6cd15a950d68a4c84cd37a32db87b72ee50863f3f0d6fb8824c13a0ec71f8fa7d69a57c3006a3a923a2e3d821e71c32ecd91d4a185fdd2e9925d0852c1ce688777ce39a7ca6f03e970c9b8f2a0181cc37b7de2b468e7b401279a5e596fcda7b7de320686d2f374cb7a27e91eb3e9ad6b7367be756d86b66d87dd3d7dc020dd42d5622e7030c08035d1c2d0a2b52fc0d0a2d5c01c39509895434bbece594785e3073e47f3cf557ee3c60be78652bede8937faa5853e3e8e428b3a54e47b820a3e2dda51e543fccce96d816a5909bda3eda0dcde70b4c05f9f5c4d2da0455b82064e47813e7eb468bd877c9518294f01f58e9d28b7fea3772447bd9e6906956ac187ea512ffc0cc7394e478152f3f1f3c17a1cba4079a63c62593bc1348f8d4fb7311bf035f6ab815ab41bfc04259252166f2d90ccb17e25bf28400515578a145d314274a548100541ca6597edb89c2ca1c6a57fde0898e1d2bb1a0464976e933397dfdc9185aaf98df91167ee58491c7646e8a1386d0d88e92a4bbab2b2f581972cadcfa2deb961c01c737c367f1cef4b691e969559e6371bb54ddbfcef78b9ab6930f40ee79b11a9a3bab5ae0f2ffcca34c47963b264075b953c30d0150c94a731ced7fbfbfa0556c03b08a88786fdea320fe319b628c5021c8a88690103072db67001c5481364c8c08829a264b1c1c9ac4c492c0bc5124058e9258ba6af8edd71b22c82b8588a135e9d91c5ebeb972f6fad95ec3858108de0d3996441c4054b5d50b9610cfa848932342c8ce044d1b26035ab0373c60757181c7c180a134410511813bcc244e941284cd21108588185b102041038620af365070854390ab3050f548ec28c910304a8307d354b61352bca8f1850641a2f917ed26071430f4d727d897403098c18d1e58627696e90f2c31d01ca4285e3acfc7047b63280b250e1b828595c2e233680968881c2868d0fc9880d20d7123150d8b00145cc754ea65259a5d539314c9a74680919c215910255a69794e9ced6789d3dafabf1e6161d949ca0256488a6248a2459e4eebcc65acf7377eebc1a8bf20e47cf6f4620ed27b40ca3e75c8327c8cf35ed1c38c2f039e49c07f57cc2c42c91f97bcdfcbdb79f832a79ec180338bf01e3faf5eb9cedbae92fd3df8ec9ce42f2d0eb5507c943c306d3abebb5c7f8f0372cf3d7c72ac46da1d650c8e4c23485a92564c846054a4b0317326446085ad587cc2f925a4f524ebd86d6061b803588aeae6b0056a126e13a05ebab48f63ce5637dd5787b69bf28cf34fcfb824638662eb0163f4ed88b0bc7d48c70acae26e1067dcd8bf2b417b9ba391c5bcc5fe75ef5f51de34290abd39ee1f811c9f56738ba9099b40488574bc8900d8a0fad27d387cc145cd17a9272ffa8e4b1632160c1b9b763134657d72f1ef353afc92e6174c6955e082687b94e5dbde3f9756aa4773abfde31a963fbeb0d46eab87fc3f1eb2ffee6e6e2efd85ab080f29b145232749e72947b9ed2209c4abdea6eead407369816af7b6037b578bd03fbcced588bb7c77419b09d5abcd733a516efd2bd1e02963aa4c2827389b1c1b9c46c6564e1b632b2282063b744327669e7c4d22b9808902035408310960e175abcb5928ab7d60f80c3ddd039af013318c4aa5a5ce13a0e715ab4dfa7815fd6b44e2a2053df52b9e67a0d28e50afc361c9c3983d8e7401c15c7398fc6a07dae3b2e03f33b9f5e0fc63d1e8a0b551d289f66dbd488641a9e401dd72fa13bb9a32345b9430089a50e96ccb1ae52b1b20b6bae098ec894295b1954a8e08cd475b5495bd21fab4d8b99579f4b9e04a84fd8e80e4a1ded46fc0cfd33f45b2d896cadcd42ae660244965afc2f43418380b250e1a86099b2f9b724872046bea68750648ae937edf480861afc1cbda0a14dca921d9c704438224b2ce162441811652b632b830922b62a990a9d9146738a06e40fa9c3c65b0f2275c8ef495e328738e21979b528640119968c028f164340ca4b4b34bcd1ac07c92fa985eb47ac85ebabedb14e85c821189169add685fe9ac81ab6529cf81a560a2583415ab4aedc2f5d79cb001089c37a0d7fc81ceb1c9c208ffe35ab2e843e43e41fd631ce4ed65a4b438bd67f380d3fc21fd4696302280b150e0cb7440c1436c2d8b8a19ebd06fbdc504f79f69b94d7f80cf73e2a2160e11a8950074dc941720688025d59af01adb5b65abfd5390faad6bab5d67a2a64512c99456dcedfe7f54bf6f10305f6a02b6b9d26e5ead662c0ba0fa9a3bf71fe7c9664787f41ef258ebd55b060fbe284c8921e63230c14ce35440c60c6bc9a7208ba612c81a523de709c4f6593f9d8f5681fcdc3d2116f388e6c1a6dbae9e9e441f274109d403c6da78e01c993e94aeae09eba0fa9a37ef3279341bd83b2b0837c64ce03e48a3a8bae288bf53d2da8d32b8aa3e4116f3fe69f8eb73272ff985fdab6211199f66811ca8f1e48799447d80c182eef4aac0d883c666fcf9e045269d2a449d3aa2d64978b490d6d5a3b22fbc8829247dcdd587032862f498a94c6a83f5e8620f535ef3a07727da401871ba8f116a494524a432099b7822c3d0c643e760e879e0aa31602b93ee2e93ed7418c7f8621902c6442bf7012a79b2a2ddc5c7aee398f6ea9bc0b8b741de6b6ceafd7aef39f1a5a5df893726f0b81648e352628cfc0142acca173da85120cdbb9e7cdeadd9c5e89d0ac67f7a4938657c88bb195d01b88b73072b0bc9e59adb5f3a66a5eb34c63d54bfdc3517553de58a73818cf6ffab8f1d3592de668517a074359ceb00653a6dead754dd7a0647052aee44803f5ea3fd64720c058c38fd22509928451c88fb2a6907f86e38f9febd47f6ca5610e5feb877aed1bfe50ff09c71f6f4306d4fc0ca524806cd51426fee9429e4caf610ea96d812438e2f086d9e29c7399b3b9901a5a5bbb0c41ea03c97cd47c03f4997460f3bb85456a70b58a081992a64a165d80310619695a9bffd4d0dac29fceb9300492b9740b32d9864cd7d150d0cb08111b5a45649ad6ab466b0b73d8fc670b2f38fe781b6e60e4f0b57e36e79c8639e0d60fa62be943648c888c1f2e63ba94f1a4f5f3a47d0ba9cb1b1287d21e8e887ecc4743eea4fd907d74169625260fec4157ed1d8893701044a6432d8a7d5d903af0b7bf30a50ecebb8af1dc4490a7cdb6853917b528851aa1c92315a2453b45429489cc69bf61ca54c85f680474deee0215ea9d9132f9f6a644dfde4e2813a9c33bf0463b42f57dde8114a8574d5f1f6c91c7a654322dea1d3a4a399dfab0348221db4969b13da66971a38ad3b7d6a12cd6b7731ca5aea72727a7a69cf4a8348d1656410b99220800000100c314000028100c08c5429148200fe464df0314000b85903a72549bcb836910a3200a32c0184208008000000800801087aaa60800c804c07847778a62af42045c3eefb1395633e29e034b5cdf2546325fbc9c514f576782761573019d19b546730653acc2cc978365d94273680b7ab0d6ba402a841a45626b2a7826cc0148433ad8c14b87bdb74671d85c2fc8e1cd5df4eff07fc2f3af20fe69ede617637829b6f1ac2acea81d7b731e23d615868225d8d30e4a2fd73a9ee00fdb3bb0379f2c623787d6ff0e60bde4e2429651c41054f1b8e5eb177d93487f1a85c27fffeb02c5ee47001de08d4d4e3da1a43b78d0cd36823dfcfd63ab1559e12971e51e16ad1d9aac40d1b8a1d5c36535fd152cdc1dd743a6d1e713dd022c657f81aa1b1ddbb6ffcae518ad2be036bceaf2921aa2705002f800c7774a43b0ce527d08b6496bed3e58b2761805597beb0c54416617d55f1b56af38d66223fbd0a748bfc1abad4d37eb1cb6c53d2572046012dbc8ad1bb71d254c3a3cb7c2e3dafb5f5a980f5c0bcf895fafada1b01c51473e5f878fa12557bf4d03819c452e6f12e2930139c8d49e428e443fa2428e924dc486bc24fa881878a5eb7e4cac4db4c33e138c559a01664d15c9de976c24c17b5706157cccb889fe8b74a68fdc3dc5d1e105186a265886585229a734336ddc1636a4af7ccaaca6ddc215b168432cb21f5cc0beff97ed10668377117741ea4e0f3bd6094655b7986652629084ecb289add5684280d798e06fe7c76f2dd87b0b80c6d881dede7223cf0df9aabd8beb883623568337f4eabd45978e1e8874268ec27d758f2532d17ba33b412f940e2c3aa0f4ddacc85d4d89299fdb772c99897e86f4d6737bd32d3df748eec647ee3edc273419315e634e5a6c633eb08a285b4d240cd05aab26cd03c1faa733e8e2ddf046250cc643a48e2f4b14500262a0095c55191d8b7e95851ca50daefed250b591c12e1f27cea70f3844a68eaa475d344ce97aef58357f5a422136c8d1fe0da71686bef4fecd995dd063a9faef2bda19214a5b53976c43765fec464fd51921955d1cb6be50b676617246b8478ca199345ed4fe1a90006ac71abdd7e67b3b1780ddfa7087d73d18fc125f72379742ea53aa3087501ed05505f77d03e141d17fdca58a194ae6d0c3040397839e50c2346a68db07d05c2dfa9d7f84c1cf3973f67a0f7a382238e01e438756988ad898382dec85da89415f9d93bad46179f09ec8ef6e037a8ac7145f753d6528ebfe58bb482feed01572e5f6413e4f7eb147d9e7570bc205523acbbe61a3cb0387a5b206768dc06055259084c297f7d579bb1dc111afb5defbbf1d15ee906a5e81033acca506cb4a6e9468ed0563273e2d88d221c33e56bdb1c7160e48a876ce33926373eaf2aa82588be50b23e6366c5704446ae843ca862cdfec095680aab539b47e3f445bdf47b92032a3487f5b76d1e2202c3282bc794d2f216d7b16bf4593d654868899018f277eae3f8faba52e7c31aeaae28fdd02f09d4032d3c3b7b30e9411460b3cab5d78d182d63484b6cb23b69315305a9d9a496b9899c4defdcca32ab49c06efa833c6e110e30e0e8acc1ba2ea4561595cef8a4bb8bc6a5520ef01a4ff440c6bfd79a6d175045316452ce210924922315e8d66b83a70966a8f2962788b7730a48439c0c57b50a3163d96adcaf00e8c58928bf5e783eee9f7b9533683224120d8c6fbce093b5cf4e17bd49cd8ec0dc92480420e5ea6fa84c455798b19cccaf848dce911cba782654134648acd0024ba76416ea60957934c9b2586926a42e08bcc6a3915093a968ed2eaf9561160d5b5a798c41377d03a62ebd9ea92c56e65eaa1e918bb67ad93c4d8c0b7e91209736a37da56e81c540a097037024cb036586efa74a4262ca2d657bba891bea062f9026b07ffcb72aaee241b57c1d563027cdfcda26064625d9adcb55825ca7cd8ab0de4595c62f72e236d5595f588094b439dd8b73177fafc8b8518787ffe52df8a56d030a1819e651fcdea99b504db92a225d1202c6c09f9a13c11231a443ff9b99efa069efd92979f4f702dc291b11f316fdc25965832d7605c31193a2947ff72dc8dd500b70802f66daea38bda1eb816f1d2eb0a28fc2411f77a7d5445cd181120e65663e9ec3d12b9b088f9a365f215c0ced12e609c478f0f8bd0fa58d2f5dfecff35d7e121921b90809b93198aa87ba951c80be1f90036629d5b15c67cebf3d496f120e9df73c60bb3b831906e7825194d91319d9ba2b65720eec3e7e3292168157621f2796903e8cb0cd5ad2b5f85f5a1fdcaa42772ee681b7c3a0209ed17218475fa3928b0401e2bad58841677bc944569126e3f1d148abec182d675042ec67a6f1be04dc4716bf184b398c903621717c280132166226e5f549901c16d963e80b4d295543bcb52fbd3e57f548ddfa59c460ddc5a1efce7908f3af2647087f57258507ea2661a66c937bb28509516795888da0695c34e116d433e305ec12cf9d6912e881c2f8a76b1e2e4ecf4742f3118656312552ce3da13a8f37b4e846503f51520ce9d76b10f2419ee8d88b5e4715bad2ddaf1ad1ae58436353cf49b1a719577186b4bd0c48403564039398b9bd6054b8bf13732349794951afcc2adc2265cc215a8e7c17a2516c9a325940469611fc5ea0aed01c03d8d035ddf7fde924dd6b32b8866256f35fca8886f5a4153c445366f61c0bb452a2ff470b0c4f1ed6dd9bd229998be2910743241261156fbb9feda06160c6201a02782ca826232e4f799922bec0f2794a16588fa930952dc4b3cde1ac6a174df06ff9eaf4e46c7caa56040a429ce1a79f15828970cf01a5c806fd636ead0e9df1953890bde4c8fa4639c05db3e0fc9bc0078c67e52683aba9d5528ec84864fbf4e42879d1ac22c28f0977d2c5cc565455db7c7a7fb5160ce50a67a58ad1e6ef4eae88de23df15a9043b9b5297179c238abd060b40798bca8731db0557b35b51c8304a9d7fb63017622a05e2b0ef967f29d1dda46d5b740d2b9f72b4b4f3ec7829eef860d9217b54045dce60b23d4bc8921e5f33caf44bbbe4c05b55fd24d3c00a7f2fff1bfc02e6c2ccd077a2cd8fffbdf1d54245da179e3a1269fee5b49c77123fea85bba483f55a507b8d50f512c4566c2787a59514b02af6c508fcb0082fed5dd2a26b356ba7ef692c9dd18b6e59500b7524c2cb0722b25d082593da662e301b0d6c39769bfaea7f3ab1244e95c9fce51f95c75b11e383cc210e55194832dc2c4b5bc9a977ff0e205beccea0a7bb4ebd82d1bc3a9fd819da5cd48f959ce2c7298ca44bfa02023e4de912c2ba61389de414c837c8ce7bd587017ae64da958b875084bb77c306b8a0c8520dfddce7e072cc0eed5e62364336c02a841efe26f0bfbc79ca321c8eb8ac8935678c599cc85c898673ca03fa93d052917a94364561bd32d3cefd43de8c5656b538d2e151e9bf018f170ebf7f2a8b5a31a79bf049a3fbc9b00e45dce6d81b8d9b4f45e4053b6c741a8a3e1bb364ec15b7718d488fc3c59449992aaf97a1e24d108166cc200a653607ba74325e1b8f0ec9cb25a87c15b4188aa8fd4673e0d68c56067ed2ea874065176af3ed165484b2fc8b0d2f78c80d7aba42ef9d66e3619d3da9d10bb6562604d5adc509b5dad6be2c0205cc915c8f099118c4d0b0bdec8dfda7d266da81ae89f8a6eb30c3e515b8bd846d985c463fad024185eef147a81aaa23b4b761955b027711d49655df8854ec2ccb62206a45f8763634ff0374b5e02737791f02bb47edfb037296d897de719e91d50b92b27ec7d44debbb2776cef3d762408c1466ccdd991d906cb0586f0947fab9987aba1ed5b7beb899f20401f53a79c084d4abeacd3777a60c137220248aeec16e8db96666627e3c4f1b8a5edaaac1535925a8b17ab9595336c800c368f078dd7c497526858b2f330c953a18d1f199550d5b13b709f66255c42c90748c4d884dd8934a82d7c832674990e2c530523a1d9b70f7dad0da3190be9e8486735baab7af0c6a39cbf1f98f67fecfd9fcd81dc4d9d396b7d5862fb0effae3052bb49f27c3c06e46b2bb92977d4be43a32d513c89ef0644447d1697a3ead73d57eaba307b29d884289744de4937cb65864650997a207f7ac5d11c3553e3ca3005e683cb74e79be7b787c31d6bc2bc6fe1025d57cb72623819ae8eca69232971d645c381841b72de279ee4e7bb7f543841c264b85501bc492424bf1ab2184b7d2f4fe1d48450bac1e91512e1e6e117069f66298d3debb42b8999a261bc135446fa74d27219a5fbe8f45696577c87f6f7096c059546712b1fe7156b03d4428cb242d2b56290caccd8927aa18ab3f7db81bd0a6efc765266b76b4090cb6420f68aab7c5db9a54d8c326602dc6a0c27aa7689e159a148f494829b82bd2ae450757536e95c89b6b4da68e897fa20d4e737aa559493b19ad5675a451d27c785f034d5f3151cba0305840c72ea9cf0c6c3369a9abc998f035696f40605549c942c8c26df2591d8eeaf429fffe619f4f9afb78a10e57cf8e12b4298b459565b6f2ab3f7d5c13cb07ca53ebecf431881471fd49b0a6e0371c7266f4bd1529b546855c5406713d2373ae3a294c82ec1034012c956c95889f36afe71b77ff65bb7c0b05071e5f398d55c13537015f0b2f2f4d3c72bdb337addd708a41eaed5b626d09b49ce479e5be7676bd72ecbaf154a11d448ce1571091657c91cf73dd66c58160b61510851c17d90a9ccd9ffc435888c1f364dba51b11f65404ec8bbcdc945cf7b3d3ca1e758a130681879002bd0332d1149fd31bc9d1f2611754cc59d788b2b4bb40215abd0ca033e579292ea907f6b0f30dcc8540fd0ac55475698fe9fc28286208ab0d0fa1bbf09493889ee0ab19d4f7902815334182359be35cce0ba28918a8dba75202a470349f3cf4a8d00b9b765ec78bbca9679179bc57008ad28b77119b8ae8f0889d582318fa6ac6df0924af10399ca8f6c8047a88b651e3892d22d76e0166603796fd69522a3cb88c59ce36312830d16a88e35a43aae866d58d4c78b4c6f116de4fde286a8d9f2d8eef7d70cf9b28633e3760f046d331d15cb4aaaa766d4ee566fd6e0b1c15b29a870f6c335b3de247e267cd5020fb3c365dab1309af01107d8d4d69ef6824ef12d4b3bc5aac83bef9c636a27113e5bd09531089f5e514c09cbe9d80f8544dcf7bb2fa0f4f65548c2a827812dd36c1f49f2a85ba0337b1e278c26358b274335a3f9e632a2f75accb2bff78f45d8f3c142d34746abde7fb592668e568db0224aa35522c4fe6291670f0e0c7ec1b4e829bda17edc6cca97df589ff45b0aea3e9a6f70bd088d6c0959741423f87f3ec210c7fb93f79e393337d8e59f68ac40b652ef684322aeaea11affcc733676310be2860d8aa0183562eb00b198ec628ecca18edc841e533fa1a31b7a98dae68d094a3396b319997e289ef389d09c251f23296332eb3750fbe3c0bc9ca3b30c10aac1af8a89140feb4560004e758844d71fc26693e5020a5d35da4648213283be0862223c3bacca945e27a0dbb90939ce7d91d5e2ce1e6d2097a28c9a3237d32580e3183187961661ff6771d74aecd538be3c0891d26f0bbd9693dabdb1ce18135f2d7db42833d7a390166c48968397abf9c058ce251b279ee9ffc4c7709ccb077c0d21d4c915ad87c9143300c3c36b277eac6c2929d903beb9d1ca6251aeee830fed76d63844d7b73805b8846ddc7be2d377dd8883064528f0d05219905a70b370113a01227cc8fd4463360be2c62f5093bd1f7881d51fa7b71c69fb1bb1f6ebf00504dd41594d168ee702d81b9c6441d31f97d587ed7abc3b70c382eda4e9d3f64e86010196b8613090d3ea64e7036fb9ab876d5390daada4c74c0dc4fe89e2ec0735d3cedc3754d7b922a6d40410e51a47022c6477f036564555ba430978216ea1681fcc3ea06efa7d32ce98ab73f4823771e2c9f022cc84143aa1bc3913f9b1867c4fd3a27c6eb1d9290cfd457907655d44addcd3ad2726dd351e98a0c2bc831745e3ddff79b9d228ad638ce92273290bdaf47eb54b2d457c6df2226981c5b41cddb291e14d48058de736720fa7151ea79a6bbdb90f3779159bbd577b011680c7a1fe3db4fb5bbcb2ca2b7008ea0d1c22970fd6bae591564070006488eb5cd0bdd12f3a477125293a1fc7515329b1c1be54f53999e874f522b4633c6276e5d27e02bcad0b75c2f4bca4529a44b8ad2231b608b280bb7d9d1771d1baffedc9dcd906edaa43dbf43bac2652cc1c78586e520da1574b3161e043e755680380a511748d8d6c7078d2cc0bb699d6b2ed7e2e4d6a2508536462894188f06f570c60f182a92882b643350c22b85eae96ec9cf1300cf7acb00e1aa2e29a3872975be0ad3f104a126e5d37e640e53bfe7aaae15e74393cbd9984c99b293e9ea4636423e811dcbc585e6a6be97db4f9e187887568ebe04ba8e47375044fe5fac39b7446dbb1b0b196530896fc00c0b012fdbe40d13e5f2172b2dc3af6cfbdd62acd0b13174e13cc126ca5b44c341cc43d9ffda0d0cafd4b8a54ca125724f653e4864d51a797b63037f48c261745311bf8d27c410b60228c13afb6c184965469f25ce033550bbb03ce70d5cf28a2f219f7a30ad2cd72ac3ee52dc9b56a0fb3e5d2bcd8120c1260649621968891b1f1de6a626e14d7bb2f8389faa8a9e97838cc2a67c23bdeb028f6862f1fde349915ac1e69d419e5740f5971f559a4be3a2da91338831fa8dda71568e160f06021dd75dcc772b6802c70aaa5588087ff01d5795baedace066395bf32e13dc7477559200d799c4c717f28a21b82945a1a4c4a077e89a149b94d891d2a377a66d6eeb3aa92dc1ecb576ed1241a96814a46da8ccafbcb081b5736d4e90a666c3368ba6792bef17b6e02e5840dafbbaef7df9df9d84fd950d4f0ab930e0d735d93dbc923f4f19341e447b2ce22fb27ceaeccc67dd6faba4550c78b61925cd7d2fd789e7e41fe6bdc7e451032a442f547a4e64ff4dcea090dfbe5eb2ed04f5427df0d8808ae5bb2e9fa79626f4e2c2a6ec2485e54f251424747308560e11a24343e02eff07ec4961221f799614b8713908596d8a400bf91e5d741db91bf1890c3856a7bba7876e1f6d3501850eaf899c0eb76b50fa51b65876989b8634215a663cc54439d5d27a4999d4fe8270a00a2b30046aec809dddd8368885324c129f3a3ecb8cec18db0f046de988643a34a533b52b5dd140b9df3897c8c0aea401f30ff0892df608b26c5e7eb3b17913026dbfe757a3e7fd88bef645700ce3947c6868e369e8e60eeaf4e746c6e3f409cd6fbb0beb404188aaa6f4c5eae5e8ff89312f6a7d8063c3e008fc34cd74f97e52431d42857dc5f7bf86e214f78389e2464658786b5889d33c91ab278d2add0122ec450ce51ec529e307fb46aa7f96e98f925119ef3bc7d836346338d18bf4859e9d67f907748675be65321a46f3312877a34ffeb3ae7500429de8bc423ea758a55279402155d106a446566f0d684d50b77cc97b4034ecfd11a059e4e91e5cd40a7a78276330ead47a3a32c7886b4e3ab94d2978ca64adde5453ebd46df2f766582a6ae39377da521439479d4e70f03bb70b101a21f8eaa988843762525236d4212d1d31785f3b7f16911f897a0b0d6c4acb63fe03feffdfbe821c0d908bdb83813558b9db6253abec82205adcfe71e401e0897f30f3fd192e534f26736359ab24a959baaee44c446eca96a1729e4b900624533c710c1a95314fb106f15729dd62b74b7345dc242ac46ad0114bff663fc011cc2c16dd093413758ad1246c435c796d434123d5798baff894a6c60626b60750c505840776e32000f44d18748ec3b38dc2a4ef87e1ee4f130e2768b38d45eee75eee9d7faa68889bf22295914c9b43eb80ca335ef4a09e841cb00b2ac34573b342d4722eaf837091d6b4220e1263290f006c2500534ca478a29504aecda8bc7e1090fd63d81a9815af311f338f49bffae5f206385e2bd2ab9d6ccea77fb1b1263ecb428dda194dd756b5fb13ce1ae19532d41669cca28711eb44c86718bfae91a382a1e8ca4e1e502e8686533c04aa4734cc189df5c12594c96eee18d21706858c350a3129a873f7c936755e8ab373a35d90b2ef271673359fba688050318a9644a66737e2b66603097e4005e783656d71eab1b5c79c7dd21453cecaf9e417534333e3c4953dec02c44da472495a0086e29a017e0c5fbdfaee1948d5e4ead29c78c52eaf14fafdf09ec09c7a53a6a8773cae0be90d2b75c3be34dc3c36b6df59dc148c0163f3480746fa027a32c953d04996d1c8beaa2fed6c835c9a56675d1f2a24e731d8cdfc809f20744cc0c56fb7e0139769bd057ae106d0487954bbe51579cc42b303df1f07e42ca58a41714cac5d669e43457c7c387d3fc83975088198b395fd3b4965c42c7b0d20e2d5b231d03ac30961d6b9fa1d12e73b7078799f8412ca3021b4045e3a6b34c976b281e327155f67e5d76489d4243eba0150d3fc9af300926debb7d394b6b121adcbc2a2351465ad06b497059430d46f970d0475cf3c8d605c16094316cdb2c6f7f6a6141f4d5ae01ea3f476502bb9fa2bc04fef37625d89614c480a3732cb6643b984549bf623d33b5ba9fd50a77e8f86edbbc83316810b0ce86f4cd2170aeb9d611ec3d8c718a83c7a20b79dde15dadb7530ded64d4b5dcf38fce242582329f9d6b2d70e7f78add99abfe700b3c0741d6ccd0cb5a8a5e913724fc3a89ba16547d2b3ee0c48e36c1a5dda14e740b8955e03debb344bfccb25c1888524de63c6804b546c419fecb95ea41ae7324275ace92a99695afd356abf5c32d432ae104654a61ceae1cb409c5ec0c6e13a513ea6e67de8101046365b749c25d29e4fb1e193c224dc03ec48d9aad7d9687478395062766f1964e7f75243c98db05269174f145149f816113382d8bf337d028b2ed03b98449e48f1c28f3dd3669d9c17a8a27fcd18d28abdbd3a5057f054a8746ee9fa172a9467a1e7941aefa9f38790b2878f2b85c4ac240b06d9019761ce37d8782db30fc6a6ed3094223a3b96efd290ccd85702a4ecb8dfe069aeee384281f705c1a1710fe9060439b05df3c3f7673f38b24fbb9cccb87ad2e5dc84a75dc77f2bb03a4bb700ffa82a017d09b48e6e1373015469c09357e0894aa22d3a0eb5ecfb078acb222d004e8e1b3f46652b419bedf149815b8cd143402f70220a76046c1c3cdfc8b28ed911a6ce754c718c20fed989007d07389648f2e4f85aad282f0ec26cfd5afdd8478320cdea21884a44e618d514eded2d18e5f51b7c7cbbbeac558751ea844d937d7f1c7664dd10408aa2b23bf87dc57f8f4cb64f04bb2108e6899ef2f7b2e449f3e429669dbd69c5ff705ec8d616781a512a602469f1880c6ac596237d602ce35fdc4d9021269a97fb8074c890fd117dbc587d5ac360906cc07f799d3f17abdd6496cc8a9ebaf5b8c2231097ceef6409e63f2a25cbe306f79b00ea9bab38bfaa9bae97b2a14f6a50fb2156c9331c6dfd2950524d1c0108dcf44aba1002ea6a1ea530eab216c731131b482f52ea75415fe8eabf4f11ada7036dd8081523a17b6010b5ea8ef0795bddeac1e7f42d710371f00d881eecc0d7e8e0900f02927764ca33070ebd12a841d6d3495b380a2fbc346423cc4b11a2fa1021bf567c3a8e3e9ac87f02cc16098bea6ede1074c527dadc29a80ed57ce3eb684659039462ceef603a9ec1dfcbc26832f5e3a27c3b7b8e02b297e8881d9a528a02d01fed9b3c1f8792a5f0570c8e261a5a29713bda9bc0fa4c9b5af332d0bc0988a883f9f1dd4b1e0238b7aa34aaee15f23a90849e7be6af8919888128abd00afa80e839852b88bfa0298f878e0a62a39af6140ea1cda4d37c8baabaf07498239a04deeeb59b8b7f8e43fac96c7e3e11ac7d6fb613d26643189e8c068cba9ac075ffbfd3ffefa4c8f26eb34bc914384b07aba06eb957a21a02402ec0b4ca122d3d00de6c16bd64b04d98ada1ca2c71bf3fda34c0cb603558337619a531f4e786224777da08fe0e54e083ea0f6a48c019fcce2edc4732b16aa203c2dc77eb7ff6b7047b524dd9bdb88b01fab841b4c3cad0ce22e7c8870d31bce93967f09583e635de14fbb8a10fe95aa6101dbc5c4c3b1a7a2722cee3b0e94bdf39ee8dd9608e6e22e92c6e2262907f72f0a5a2b7af8908ea9aa924e41d13cbba7cf1ed1841aec16051a2faed66cf96713f9b05e502dcc5722485ac11dbc3889a08112c5f6b8e72dd57905ecf8891d98ff4fa615b4fca0fe93f56a21e7831d0a3eb363d5ea20b2120942b434bf4c3586b3924d375442c59411d8f91790a16ba8f2bf8322b8631628b3cb201b08dc02eb2f54fc6721cb2beb47ff5f62d01ad019177dcf94bde373d58991cad74b0b37db45e97f03765eb7f06e8641305519801e46dd422aff8c65dd78ce8dc2ae80552e658876613ae24e1e082943fc6ec1f53ebf60bf8acde3e5f9ccba39f2e443c190286bb4b526e5fc38c69be2abd470f15e3505e9f9446dab80712ef019abbfdc16a3d5b818225313471c0b45314fd9706126bc85c3bae0b9f7aecfc80c7ab898fb4456797e9a748e94dd08ceff58f18441e8bd41df47a7022239ffacad6a79325f9421d08f4c81c621fdee3bd9185c78f1768aae42f9d6e74c5d4b33fe454a754ccbd5feae7efb687004e569d79af6cc538eda04341dcdc4c140e22ac17b5270cd37b712b6195f3c0c6c299984e78aaf3be5d8dbd785e18b0b58de4ec46038d545227615983a5ba217ab7a0d400b1624d9fa254b7d8e092f06646c9ea557de72b8dcecda8267603759dc58a7ee097566048cfa4580f790b0e9c2a8de1fc05b48ffaa20600a10388a77d44b4829569202fbe10e6e54dc0d4c39642b6b0f3253ef7dc764c896d897a0ac5ce077d7f2aecb22416ec44e3fcae0cf99f77ecf9e3aa85eb7a50a1bc5a279c40a45cd0732a99fa967fc870cde5cd9865a89362bd5af786349e737b70fe5c54f52bfdf90f829a78072007880b2df682906e611ddbab939104299c0bb78c182642f8909a93e58d3d56002a9242259e0a06a674af7854203f2ff8c55b8f3c911a140630ba6832e7ced3799fa6e2e4f7cb6b2efc26327e028aeb52865b8328bf2745145bd868b7b5fe7bb1c03bda037be8b13faaa9c9da5db6fcf055e7f0fd59ddf66006a1e2b33a815bfc0d1ee7df62545f359ae0a07e001ac56b59a019674b2e0edaf2567a35abb0b40c612f65c21af620c1a8783a09cda88c01c908f688c893cc0e90f70bafdb3a80c7dbfcc574e0a0eefa05637be9d9cfc8856527a829b0207d17ad486c881e0c8074e8e807734523d403e0c4e7dc0a0bb3eb84aaf01df34d2c00069a8d3646224a7f4bce96804ca74d02069812c538bc4772b01b63d1895fb7e7a0771ca22e6efa73d2516251da333310f095608b9b9c9a0f8d8b808268e3d710218289a1293df318ba543521561fb57ccd98eef73e7565c362319d07b57a5c67df699719a4ab70e51235c18d59dcc014df08b1d75653369dc16b7102f4cb534fa828efaef19eec0df9f10389cbb233c06631ff5eb4e25547b21bc5d25e09aefc978e98a5af91dd5a748431d774b0a1d38423d271da3a9e1731f858cfbb6286c152241a667b723024f135d2a9436a035c5633b420f38175f6ecffe8ad869a56c14c78f89712fd35c5357f71bfbdac3104642b5a62f134e7fc8e9f047dd925b4d0e9a709172ca6cd3c344dccf0b5d17f7f882e2b5c81c570bf855cda92d1d97bfe2f103ee264332660fa57807a58f9ab6530c7b6b984bbc3a7daacce621642513e85f37dbcd421e6adf5ae764b9b64c2fdc47187192c68f16f615ca7edf987d117739dc7920de9d2b7f917034b37444f488161133bcb609a6b586cb32d41817cb502f42c96ef625ef6ba7045cb4c8d1c10f8cee86805e63056848534e04b01889d5e7614c1c1d87f9194044906db2bbbcc96d88cbcac930bb02a56de121a107b51eb67c86d66db7b59879ed79faf887449a34573f4bd38525acd4a355852e0e6328aeff41343a7bd285a9515cccf7d27636e3a0f0e755eee1bf5d4c8a716e9212a7d36bd7250d6255d7868f75ceb2510d8a36a14d2abf7abc0bcf2e33585cc2bff3784b890f78fea584dbd84d886ceb93d8a1cf6829147f22daa19d85d5a006e01753f484a99461619a567f1729f286b51e8d3d4205be768d22ca07375905853e5cb8c5a4b3532625f0bb4a1563df02ad233fe0f1e3e88a791ec4e0c07e1d621a9f052c963aa6dde02df6722649340088c26c83ebeb53c49473d0822443767526aa422afce8852fac6080dd90ed81b0290d3f1d8b8e141ac258c26dc469736855636f63e0f251fa4184678bb039ce0aaf5247a5aec47d93d17344b0d701a1168df0926e7551c847111ea00f37ec79d0ab674b5c01a2762198df803b0998561ff956a8114ddeac273b53c808c5774c632e26a1344608e71cd720d901db9334eb6e36e4c27773d9cd0a40de072cf3bd71afef9b1bff0d01737a8dab3ef9dc8cfc89956dd50e5eea663b7e266ff042b22ce2add235ea5628469ad7b50eb988cf020d80fddf977df93f1031f74994cdb4d88c3310197406d0fc1379e3b91421596465040bd500e275b5ef1f77b4bb105afc2c4684e0c6d6abf6227f48e8057c184bf7313933141581e95489902f6f0ea86c643d9ba018febc98694093300f851d8ff52817f69c805c1fe3535bd9ddbbfc400e3c217543950a5c3bc99838e114fd55ea9200b228bac4e9d20d87c15564f00d143d1e00a45954053da15242bfdef7ca2cd69ff60fea8896ff9a4616d6ae84d9408ab8733a87a5e3d642e6bc92e16ef57243a359e83c51d5adf6cf5f51636c9fa8ecac699b2ce78669db8ebe04f43f38bb53adfece5ef666251bd4795bd689716086815ef76c0d8d228343c097efdd4632555de08c69e98e2f91738776ef88d203adf3552b503fd4381385f9f7e7340bc8381da712fb952709199bf570b45069903c77dcb608526e8882724dd1a459affae4d999a1267be00a1f2042ddd899a028be5331860f9b39d001ff509e243d9e636346ad4189c4fd9351d06d4ba128c1c4e91df7f96fd71859cd07c8d378927d0d1040a31184a141722cf8e96d72cadbec660c178944a7106cc3a1b97c824c741f33d137503c1e55575d538db5032717969c7fdcf89d5faa692fe9016f1c7b7477185a06ec6ed03a660b36ec115825628b22f8621fe895c04e36d6f97fb523295d70a2eb1ec2a1c5f8ec2fe9dcad70094b63b890b05bcd173407f092dc8373f25c1420ccde3bc5084c70d5eaf2b2b1995745e4cfec75fa6b882fd09c31e5d36e2a57056efa4d475bc18f007891f83337fd5a9cf262daefb57e56d830562622265b29067fcc42e64867fc71501cfad996841e60f10244b41ee01622fbdbc5615050b9be2b08b4fd7e4f6c06227f720a8d02c410595ffe8cc4aed707f022ecf917284a8d3e871c50cef30328f69c5c526138b14298070215994634fef1cc2213713e90bcda24767b84172513f066652b9074f34a0499a6cd0365769c1a3ff5fa10b3380c970adead64306c51cbfc2d4c44c8c6f2776028b49f11718466c3861e40cfc49a1860067629f36d52fcf1cb222958c8c65269a947067662bf24082adf5d0f3fd9d34938be4e22e1ecb8453df2dfdd0f81c3c52ef28270c1b08feada6f9ec6ddc177b2f24b515021dd74df595bcc35a7aa154bf6e770161d2969a54bee6c3dca42d7cc444372da1542581e840c4c729259e51fb6e753fea441b6c4197cc6d4d57d3b491d68e8e4ad2a8f4d10f99b930a28695aa80c109982d1540289cca45f7267699ac0ccdf594598d370b185a117ac3afa9764d9525b550d12dc6f2143d41137b21f1ed0b31fc430d934ea2a938fdd60ee85c742645f24c6770e3448b59e7344b6a561afc7a76fc1a2efa8da16fd3601b9a6843fc83990156f6746e81836fda5fadef4c52eb54da10792a8e6048592d0b5f7cabe31963ef48b4f0c68b0c4b6a729de7030fc0596904f1b0e25419a225f407768e8253a989fd4306fd71001408f80beb6d3e7bc30755cf9ed10f0a515cb8dac5d4a0939ae50fd5e7fbf71e0fff878bc88e040a46f7c47439f9dc6e9e8430572da2d4efebdbcb59a7df4473e9c6015a84b742c519a8b5b9933a58135dab4d0feea5f0efa413d57737b3dba0de981dfdf23aa51e08db8aa24a7d68ad2caa1283134045f444ce97473271c76572c90aff2b4f10d006149818047cf2a900ef0ef34a6406e1bb9f5e438040a6c27e2ec197c2357c8cec32e231abb48b20407994403688940c4b8d6a0f8470ad695d24aa00c083992857a00d5889d73265894a0307bbc546ba01bf17feb60e2dcf4123a721da5d59f5a47e0d08d949d19d20c5ac7d694b6417916501c89fb0518b0c5ab0be8b3d8f60f15eaadb2a65511273255853cce06e7616db6dc48e79c06e63492d5243690d52ec79a66a0f1ac65d5ea2737b324c0a8a1c72a12768e406d54f8fb5419387a270c6627bab9f3f1cb00774e639333d696ebf6666699a574a283270d17d05d68c8e2eca900eb5e72a7d0e6dbb5a7b23d4c2e99ed7808131b5b04cfa281fc0d38cc5677e55fa3eac13ba2b325f887698d59029138dd3afcec9f950c6578e573973e04e6302a33c7a0a0d10544ea43d5f7eb5b5badbeaf63d4fc2d636e19aecc54ec6a12048226a878b66ff7e35c114c438c0423982df94ada9f80506e3aafa4f90eee5220030d15b746d0ab45b37ed63bda7c0e7241a84a46d79a6c5bbffac788e9119845b83364aabd5e2c1fbb8909f23ffa56001db509ffa3ca57da35c7ccb14d1ad21f89c1735a016407d60518c5b6bbce4e79dadd126ae0f883d80723f9e2e8ccd75990b80529c04ff568059b527b5c82cbb2c14851e7b252464cde9531eddd886ba096c8a904b516ea01f06f80e682234a1071722cffcff7dfafefaa73b2431b31e88271bb7cb93a596e48470ef32d4bfa7486d904fdd7babd89e1756b850ba6318c68f05ef45c6b92ceb47833f2f81950a19ff39c0f1042be5a442ba6fdb738580713b0058618b4685e081a32c8208eed57d50fd0bf271d721eb5a0bec1e753066be82714a0b93aecbb729d0df8ea368741ebec11781c067a6d48821822b370186b2e1e6c20e7b2c4b0e55e7d6e1211a3fd6fa81adfd3a7fd114cee79fd75c7aea2dd2e2aa87632840b2c75d13a4631a87d73c7fed14ebc9f4f604d22c1408ff7d27504fe68e4d0e8860daff1b9cfcecb29a425a63e004b454deceaf5f6e94b49fc775400f1ae021c6b582774c18d280c7cf6732e455798c1f719a3c42e6adf011716a38deb6ca293a57a1d4a51f9412851db92d054f22edb91dfd62e6b52be3a64f92521238d6393565a46cf0fac72f38a3cdc2b60a6ebb3eff89b1b5db544941b46cf64d68ff555c8653cb86ba6753b3e3e87bfcf62aa140be722bc452cec56fc8647d8b01edc7071ba5a04aeb598a2eb5067dc4c375cc81f553a11d586f9bed470db9916d6431708ef4f3f162549b0d8192f36159f20235cad552e822d6f27fb9ca14b1bab94dee7cf81be67aaf913eff0e512b7904afe179466adf109bd74bc0c105251b91557f050b13ed4185b69292a9131b03298da5dfeffdd8be5dda7f65f838d0b6fec298c8c445dfe6421afffc3ab88207974af4deac3c9805243cbe41a85cecba93f8ab6d2ee0a7cf30b091fc739fb7850d190a7289a0c297207f87b159a0561d55400fc6de42bc18db19b0673d45d1fde93d6ad557011883186c0c99f01439e51a213fea6958e784b5d7a89b74a64910c758a38425b9b9e6891c32665313b825909ce7c5cc3498b061edb562b8da00b0d94e3112051023c63848087e41cb9e9d5ef30e6dc1c772c730e92c36a00fb5a76f3184113b3aa680459b4e4ecb6575644e27144f46c93e9c2bb359428dd05fac819963db353defac9ba605f6506ebdadf953895a60f23cf55c2ada02c20c16f4887385383602b23aa16a6b75940d19b404bbd5a2058905cc34b70dadc14519a119bbcabd533f47de6f4756944966a96aac379460aa55231692cbbbba35606d037561a4769f19962d01dd2de5ff4b17dfbc4a04cb20ec7e3ebcb0d8326e0d77e9bfb3ec0b17dec52dc2de60b47c1ec360e0162ddfc82018b50962a7a7efb97cc038347646b2ef453c6af4a9d3b7c9e0df8a5d9018dafeafae65d8b6022d0400ea512bb80f3a3dff9dbddd909c792d289fffd196ffcc3fdc3ed4597e182b1cacc9eeb6de434545066cbdb5fcfdf0b28205170da8032a104199b5451e6a65ea15c6898e681531dd096741dc3546f6fcac4cce11c429574065544d085c2bbc02807993f386b6060ee0b56caafba312fed026f524ceadfc55490d8f41160fb4ed3d702359b320c0ca62944a833e344a8d24079ade725b853b3ac979023504cda981810cfcd6b230540da59783ee0cd4cdd773c16b1aa68f52bccf825fc6bf701c73b73522efe067ae4762dfbaf4b6823c6cd7d522a9b84b597283cfe5ba748c42c1cf9227d8a28d37be1843c54e91e2a7fc5cbce2f689a1b79a4d88898f8afd619ee6883752eee43233b12570db76eb9fb8791bee3ff20c7b5e77fde9c64bd8e8c3146850cf0bb81d74f72ac26303a866811da35388113158a891cc2ed6746a5581aa163ba9c736031e37b54bfb6ff00b424e50102554109567aa16b814cb4ee905800c820287c1512bb241da79cb4f5eb276579a83746181a071f6215b3bcdc90cd057e17fb06e43e19d92ed01df4101d43306b60b4aabaf073e4d10a7844c0d47ccaa06b30fd437488921521de8d354da6d2af9ff5873e4ab190cdc9445e4d629b5bd56d866a7d292256d51804be07f35c336a2a53910aeb12ee3ca68221e00d3909a87a0cf76a55d4140d9c5701eaeb7d05c2475610268207ad0d674f56db3eb62d8e89d800655150174b8a4cf6c747124dc15c9798e5ba0dd62a04e8e67b82f7c950dbf8af6aa9197382704a911aff2e458ab934fd7fda4a5620dc02185e972c93cb2b8a8a8317fdef0a9e7ee03f1babaca7760a941114f43c96ea71bc00f532a6dd63fb497aa8a4421c63eac8ca722f636a27cbbf94ac3a81f000ceb3b97864acd43c2f07fcd2b55c30507b2ef1481f6387c33b5902175de5b8baca2be6f900af631c26ae1c93ad9404691d337188555b4aaf9a70820e53ee9caf1b2f284f62908287bc1abdb7d55357006326a6177890a79642b0a270e94cd629d65e0a1eed0e8d340e901c3d720bdc3a28f01a1876e26db30e0d0323111c7f8b0bfdda65e88a6da16b9af2938582a1a16e9f8eb054a01a50ecf05b2dc5a7619aa6249d865974dfa1f2c69c5524029eb3a05ff382b2573c30077485397975a9e6bfa649a4a8072b7ec42349991b30328d330c6e111aac471c664210ca01ca57db6ceb3b01d9c3196140459b42f70df7bd24d1bac7a753c014e3a407df854e157e11b833c19e613ab7e7da017e16c1ac9bb1df67236ab56c013f48504c3b2dd19931384cc055fddbe83351d074af7da1119538b4332be2ba8406ac94c7cbb2c7e98cb68f08265d17f94244c0dd88c957bc58cecd1313bc1e0f1e3b2361381f1b58920d6618890860db7a321c8724c41d826facc5c56f9705b41d3efda5fbe0f7ca3bdb93099250e495136d2f8211b88202684749f86f3d26820c6c7b0c367a1ca3dbfb31f80986b1c3389b4872593d31a84e6a25b131091974764e01452fb5270637e41bfcc5aaba6632e164e68a2811ec5d17f6b94cd79128a9512b3f2f7dff1f18132dc540b36f1f2ce798daff58ef873e1209e6e2ff9821f26101241e8f47ba58c52875d6ca85dcbd1b72238b540bc0a9c4c41ff33868aa0822c70a8870b08cb5d80914ef160a3378da4c388d1a7335363806bbaf1b0e9cd689327dddfdb327b894b091457748d3dc30e605d63abb2ab27606473b94801791e3d9d0692782546ec582337c490fe09943d3e99e46371c1f788810f32b94acdda5936304a585b3d8ebe64092d1b8f6249989785cf13c6ea70bbd94eba8f281e38bd796c424dbda62fd2f9801690e16b6ccd9b3ff9ae9897b245294ccb71f25effbe4f0256d3f514e532794e792e0fcd5b017a4a02e68b86b0d22a169a1d42b7e20da054ba4f8c4d65fb330f729231de9a258a44895c2f926e4b9b4d16befbb2ff1ae9147cafebbe32523788f504bdc1f95b9f0a5e87423867d9e2e3ee6a44211021d46db7cececa656ddf6338ea0c77719217f192881d7cec3f3a26eef04cf2b1937900bb38f79f45bc95777bbc11636232d8e198a9330be3ec4b4677afd2342b98a22d6285279b49b2f6372f259f84fc38536f3900a4d63351744894b4ae9825da6ef650e1dbde149230482f64abc1250aa1c51c4c304db7323de36c3e6a78de0a5fa496246ecefb657edd048221be03d15baec12e72e34a1727947a66acd961c35368361717ecf9177ea0f15bf4eeb7c60aa1a5baf6e1b4a2445842cc3f8b2078b62df2424e59ac2773075a749661cf085119f8a0d997c8ba1efd3b84aa090b4aeddc42c730500d295c1462e993c7c63b338402843a307e16b2409a6b4bab98505dcd4e53ce1b339ae071f938aacf44853dd9b66c17d5a85e81baead308543ee1e8a1b64badc316b1a0b250d36b5a8c992c45379fda70f3dac5382b687f470b91640b2aebc55cd681f9f24df4b9da3303a61fdb1fbfb1efb93f38193c6d08b48d0946a2a7190202d08b7f65eb7fdd1567da5541540bec7314a250e10a1b2d4b476cccb05764acd82858c857392f532b8eb1de95a82c9df013709342ece1a1371c210791ac5b45b70e0c3f0cd0ba6048e413dc458b593450e32c63edce10ac9ef21ad1b4dc825c1510c4aa3278bea3e8bf7b9893dc9fcdeb4a510b65679f608db85940bad7071b7f83bfcb61d08b211e05b255e8b022ee90812189a69873934cf8e94f469cfc7a7488d51132ab8ae55d90bff0d5f9370ea6dd847c4f2d4d54c8fb6825004dbc32d869a056e9cf3cf3d1e0967579520e3eac734eb2621576dd5ab1ceb9fbaedcb225468fd3df2b473abc7578dcc33786773c1da1ebfe2d2108018ae2f93d957f72016bd2fa30eb64c9b3b9df3046edb284a9e86523040beca619afbfa1fe873c786c0f4d1afbc71928f58fbd1bd3ee2f1ee929a5181c64fc24a351d58b5567b4cfbf0228ec373f0a15834f66191bb7ec622ba406f58ae6ce5637e79d7bc83c632350845dcb8fa14241ea1988f0c41ac20ed17d3ab20e5b721737417532e98891c164717f0ffa66b1f8473f8bb8a6d5d2933cf26b46420f794acdd8e904e22fa4cdb530d8c43be850c85ec143b21c3a6faa21bd41078a8e509e9fa09fc48d5747f37652790bc6f29b60adc9f0faebe7ccc21a69670c58023355c4da98852a2f9ed027db7ca2012bc6021ca6b6fea003423405a3c3330803e7f0debfdd0e7a568ea4e9ae51e930dda296f42ff213df04fd1a5420e567125d4f27dbe5adfc67f4e8544f78a1fc3c78507e5292bb970d1be158484d394607000656bcb5f27a830ff7cc4fdce386ab7ad67147cbc66fd57d56a909c3e850a4adbe8192bd1567310673579364780f5d7da7a17dd2ad247b35580961ee44d288d14f72c84167204f455490aa47a39f955b56be55068365f87bf1307b3fab238801fb59ca41283ad2c16f85ced17dec48590f925ee84396ce2ad70ac400381ca959ff1f4129840a932daab92112788bff75cbcf97abe50844bc4166fbe97b7922617b5dd81a91b9c0a8f46823cb168a75b5eb9aaac37e6260fc40fa3afaefaa5bce19e43d1575e4a0f9d332a29f123aab1a590182d7dc1c5624de05d4508acee1bf9ee36d6a33f3385f347f4a885be7b31e0d4a6bc8249a1641eb43f1c1e6994a2ea3b16eebdec04acde26a27d6aa9d7d0339968e7c8721729d85537e38118519fae8a41c965b23c0b7f5c65a38ef79dbef49cd8f975dc7f87759560ecbcc4026540d90a142e953e571f25927fffde1891b22e3c07c1861cf1f37759230586b499a8f4fe2fa31f5645eedf1841ba6527a492c42d6484b25b7e0c2dc4bea2a70e13459c34274e33def0535ff417a851539ee782ac5db3621073d814cb7acaadf1834c73bb36801f802b4b96acbfa4c5004fe59d2bd32a24576cda0f33b41fdce3ab5eeac4c47c8419b501e22c94532c1899f50743a4adb682ceced311479af705b43f718e08946112c486232f6de6ec16569d1da3ed57174bf973035b52fd4d780b3262a732c1001c35faedf21a1d691354bc3662ee43d9453e5d393070d32b7c62b9f14e0ffb845a6d0e1eb2618d2e1b91d20c36d601620d4c64fff067b9c54891e30824e5f77c1944e8eee66d624d69e5465d667c326a9a8ba9a2a3919ca3bcf9afa0d70300337a9a109c3b77e52e07420b161cb735f8a55dc7c695ac5cb8a2bcea0365560c74f8160b10e879693db13a131f9cb6094a16b4076ba397c4bdcbc011189a6dd73dc7e9483a42ddea5ac11a7b3c0aa9655150eba55a3ba56aba086b51585b9037283c6b65955417cc8a6b514fbce1da452ed2aa0a83fc019c85ce05de6900191589ed772623ad73283a92fbb9b07299c89dca21d452b6537a695a55b286fdfa2e37eb4dda913c7cdc96d8042659533c9707a72ddf0bdd652722739402817887bb96cd8ea77fedf9da12e51aeb58e837b41002d4b7f563ae1f07f355dca70179e36cc41e55f0fde64a823708ae088a69694d4cb5db5f756a26f21b99cd56fa04ed050dde746f06162869244a8605d766074d590e3b5d350f06b68d731be29532bd24979080d78aac31c207fe53614a9e74cb36e5f8b2e5a41bbfbe0783004c77a0a649e1ccdab450f1544688da6ddb016d10a819c2793158997924ecef636c34c36427c29f0648423a59aa0ba9860594c1629f69a5039731c898fda902e17f5ce62e541b56a84c6917cacf72cc67a137ff34927c58ff84b6c8584c68141b6d3fd3a7ab403ecbdd08dc1ca63d9ddb8c2de8e7a9fb6f625140dd123707110a326227ccbc7842bfd5aa9ed027fc155a2ef8da030a6386b3312270f548eefc6c88eb20f5c46663795dd9631dbfe45575d5fa9035af6546b47d679b02036cc01881feaaf032c5b23241b675b41801d8b37be5a8961849b9f56c79e0bda89d700f529926fa3eab15f7351ab8d67a5fae2f6a71630667ced0ad0f9d5a754ef5ec8045c8dc438aefe8c0795557b2db4a93ae0b410281acd793a37b0d66697e15c1ad2f244d537a554bf36d53e160aa8ee9a77eb2053a3ea6ef5d435bcaa77ea8c29e9a373862d17038b3f41b7e342cee7b9e180ce21ee0dbd5838e7e989153353138d77d4903631410ffb91777db7f5b9bebd5d66aec6594aae2ef16fbadf63cd0ed9af01dc57b02ed7f489ab03d5e2eba8caf5d04d511840dce48355be0da1c8c5f65caeb8149a6acb1b59b50a4d8903868729d148e8d4274d7e91cb16b59e82ab10c8c3d714f4f54926c3ac55eb97b72d4ba78b72815187fcebef017dbeeed57b062df85b7df8f28348760806fea24f8c1600b6e5c9ad7e17e32cff29d42cfcaec2457f856ab7f30cd1ae01d321914ab0733a8921566407954b9fba629c0448a6ccf3187fa62750b70c07d89db7702be336f1001b33360acc7a05f2cc374a4f70ad15ff34c52ded921bb5599e5b86ebbb1e41b3117c465946caabcd25666fd1d7edf75c0706e2ab513775f8c8a238c49eb3b0e9c6ae3aba42f60094b7c7cc8ec07834d272dd997f7f44183bab7f6f5178e20479b7216729750a18ebf5677e9a1319e0b0a1c22d9fe55adc259ffaaba8f34f3b131e006b8ffbf47e82d244a6804293a9139147953c8171e7b0be689c35ff633e54ac95e6719ea7ea5cfeaad03f31c63a8dfbe06578c1abc8569ce1f9a00d1ef61d165e0d3e3b347a0ce03d44f4335074d86ad6e6a40f692d9415a01dc76a88ebc9030d41118f33be2ac3146de9345858c0d06ae288f2a7b7ac75eef79d35c84d1a3fd42b5cb7faf16a2a9eab9dcb33059ab867ac513bca348e091af38ee6ed54de90dcac28c86168038b5f599e713240bda2c40402984ace1410fd9c8d4e8fc31570865c574290a98f5768761ed68258807aae184709bfa986db86fa690129e9305a74e884df6c5cd5b45bfdbcca3aa3dea13b9b8e982e1805d21044a969a370889cced618bb32ca38ee18efeab32246355104e21a51ade50290b3206c8cb419c844ea497bff09863dc0adb8f5c3a025dec5e51ea3eec5f3ab824a5955cf590db9a875f9dbb52e735b634d8cfe5f28615474c354f346a0bbc22ab5dae87da94742ea3e7bd28a3aa8f7b0cb9cba91401092aa77b608d3518b2b04a7f1e93e065be6ae931a0e7a6eceb93ea78e57788bfb59d3b2b45ddb286a513fd0a48103ce416d08dd331f771526473435079441ca0a060e40f393b4b3fe77e16b8d6bc1c94b0800756e852bc0ddffa73e8c897dfd3c332ba21c507aed811da3d0be39ff6597a0fe50135a74eddcb7c4f68c863cb1837e4a208af34d561e0d84a7a45fe854b961d0fb1492db9862ac80f5ee5eccac19583283524049dc578b0b8284daec6181e1d4b308b1500584e5ec0504563a1de8fc00ef25a685101547a2aff6ab62886ab5811f4fb00e2005dffd74a21dfbb8debd16188c701514c472d12ca0a93d5e1f575d02cbb6520077f997465f797a2527d4c6dd28f1d1e98419c45d76b78f2af5df5ed4ba5de31127e4f1056f075a72f716d77e2a8d3201b1dacb59d22a61ece1ffd3e3d8e1b87e08fc7890b850fadf1334853acd03b1feb2f824fbc771c050494fee381c7542730b597b5eca41af19d4f9b43c22cee22cb27ed6ae5e0606f772ff849918b7de30b08a57bf21a1b89dcaf66c999507d032610e45d5094d913e842a98144744ccfaca195bcf61b96f9a5b751ca651d0a4af77e835c3e09e0efaf6665efda482573842bfe235b0ea00437d914d93277f0d76c0c97a5fed00b014560b27cbaf94e8e2b343d8869d771c9baaa7963a4f769ec11ae617228b5fedf936d75b7fb9cfda1fd0228a066cddcfde670fef4749f7f3b5ca0133893db5839bb2f69c9cc42d0d9854259c05e179f466ca8f15d9ac52a0401c5d4dbfd1606268457b7af377bdef8524079ed339e267ae1a65478999821828a6fa57108ca3a780a382a55b0558c3136e3bd3fe451731eed7e855d713aab78e9ebec43e6310ea79c7a10ccd187c86d35e9deb96c5adb4826783d21840e3c36048b3f2f96ccef819d125ab0ea24d9807d0535f1c855753cfa7def4a2863a8be3d3d40cac6055abf27b738bdfdba734f729ddb8f894429c05ee3efe25bd1d030a8d5a8e78e0edabb5d667b9d2c5331727cf167c6d5058747388a59844f0157ca85c4730251dfe464324010691bf767e287b21337a01133f1230cfbbac12d43bb40967f3775306b13e6e4fd7097b8694287d46f1a8671fa20cf33d51665f89de15cd6b8d4362574c690ba57187839447c95f69f5120e51e4d95efb0760bfd2c4c8991e1a4a8c0b79d1d614ca444c928a56675ecdaa31aa90931451a4ea464984691d322041bd05ebc8635900f4447809e503a112068034374f2dd364e16a95b85ce610786446efa473d9a0e350be60208cce974a09bb991a6406227c92711c01c1106ceec16c7ef1b218d0285bfb34af34d7b8253083eecc5360a820daa4237e441f071fc464fefd4b27edb0f480ef192ae587fa99a805045574ad5a0dc5149808549b6bb7d73852040dce7cbf0ed5f0721bbdc5824f56bf3875bd756c258d104e1f36f94b1364aa26a2e49ce0a11c1fb9d5e8dcb886abe4f0f9d1f27c0716ed71b1342b3348a599e422f56858ed5a87093683a82fe03022a95e4aad209fcd48eb1059091fa0744853cd044e69ba6c29f460e9f4d8af0a10b7fafb4f5c9c70aaae6f688925618bbc27d09cad29bbd93c37dcf6a3c9ceb309efdd0dd21e25a83645fbbfb2b87a7e93407e31264f89f6af4ed3e4022f4640961cf9d098b56fcb129cf4971dc3b9b8fc70eccca2ef332252979afac760e20af6cf26c6d09b54b42290f17ff4a9eec01dab5aa5d463b5c55573b9c7290ee33b953cc781fe99c81154fbe121e5e66f1750d326827c263ea3a9681e5dc0256d2a52975d400fece0314c8335a32e59980537ba80cf416b433dd6b9df0314bebb17446d0646c629235c5c6393d1b0d157094a1cee84bb1aa393074dbe4f16852fbae8a09db910af7a3bd2adccfeaf013d98587c51c78e96653539b62d4c636398491758c1746ff273716d7e0d78e63aea748bf9a48237a589d9baffe2fb64244480a4014e341c969d8fe6077cff35c998149cea5bd1706c6cfc2de6531ad1aa84ecddc68ae1c37360b95ce355293b8295b9c804f72c761f3b57ef2471fc2e183e90d14afdc70c9ef4e98e0b46643596e995263606c3867f45daee83114e74e50c3149d69fc933c0ed990789aa0b71f54b1c5a01a8afca063e29f4152054595a21e101d395a491ee8c6d9bf1d91eae4913a09d90910ac0513b10b0fc6513e17f96584c7d9cfa6bc86ab1ed366df18e1b4abc30d0efe08f1d3680096bf9b376ff81c43d04dd4cc802601329b3b26b435d2ec04c12fe278c66d20979941b91ab1ac55ace047b55014c0ede4ee29d0d99086c2821556f4a91179d56cd5ec1feaf0054a9fc0ce0af4ec6c917f156a639d91629985b13542d569483751a3cc134e22d614e84dd2049aab3ec31297a79acfdd5b2e77fc3b287baccee5157fcdaa1a2cdd6614f9db583b6abd06e90d50885a23489e7a9857472fefc7a5c475352a3aa08ad5d2d18845d8f0cb2f8859f2d758f0f67af6116888b3c5ade1e2ee78ced3e5f0bfe25940940b99e7bf1e45fb87f843462f3fbb7dfd8eda26c8af81b4b4ac7160a2e7f887edaef3a81912b5cd859c81e11946c72f6ffd0168437131f03a5340e714066e67f66b2ab2f55ed676e84fb27ba9428905ced0a5f7a119ace3a840a9c9af0d903e399ca912505c12cc4f3e6cdaba7e11bbb3135b4b7e36677fd6859830084d8c4b0e74ccd02d03157c021dfd255ff3537e405e3f1d0fe836c2f1cb4deebb392cc02e6d4c05c2dd910d0ffd18e42f674647bf1440f5166266f5d52be6fd9539fc52d487b82459e73b5e0ca251216d76c69e0fb4c4019e9b4a501ccb53158a967955c81eeece059a5b0207fc28eda87de2470627f8af7185a818b9893a24c81f3920a25e196d668589492cc434bde85ba8926c5fcb23c57f3d301937d59731a0a3d2008c28a5324098b2bbd263c0f9a1074fdabe4a85121b72a74d0b1314385c757be1a86b50537998f3efb14999ab9c69afd4fad3badc61eed9499025a69283ac0bb55bdea481d09ad2569bde73c6f6927e76035ee3a7f80c49cde86b0cff1bdd4cd575391b271755e2eb54e6191c58bfec1961029f178aa74497bfdb42aa6e59bc16d58c95073ac95842b62b46fdcbc309d9ee4b424dcaa48bb17c5dc6240e5b6a2ef2c3dba724a79f4ef1490a8797fad94073897f428eb08cc76b86a6bfd465a694e3d7c33fd9efd540e9c72d2ca80743774e246d1c8d47e55be6853f5ae0029283a522f46f93fdac991513ff38b7471c802023738a479ebd9d38e638da04d557dfa8cd4573f8a3b2a13c19e6baf482e8d9bd0885a58cfbe4dc53c107ac456ecd7a087b3bea4276d52a1479441a00505c66377d6fe693682738494b3af989a84b6252287b2d78fccb585b410c90d849cfe568103712239c1704148bbda4b342fe07a46d7b0cec8046a04fa23ff468c009ca41c4565a93b432fe50b46ac56cfc0f4e1840c4894bd3357726ca7c36ac2625d48104b707e126dbac7bd7b49f8958d6af26797c3037fa6c6cc0346872f0c4ee63a8c76faf67cc06f3dc6800e99bd53c3bd80ab522e700c78b0b1e052220427042ab99e8d5920d0ba4a0789afab4ff41b19c0d004ede52fd750e36730b063c54713c06f4eefc3c7a3c396b6910303b55651867664b102a26208e9c012ec964a317dce1fb2c3685af643016533fbd608ac93ef43cd92bd09917b4b99524a018508f708df0842b10b283f09d80a7026ca1f6dd7c69db8c65251523dd29d47d943e3f628b548130a0df5cead0d2e413be2a33efa78247feaf4d5d928234d182baf073e507e2ab859430d7270a60d30747f770d48adeed3292b7b2c958783e3717b685c822653efb304b304b46ecfc9ee45001ea15f7a3cf855de4a51a5b75bca745945c0e3f13649f070910081b74942ebdcd3517785c242d5b059ceb033d592b5e373a64d15b43f643dff7cff1d9fa3faf8fd9ccaebaaeac358551feb78a3c171c1eb5ab8c9056b7596719c4179d75f1d44acf1679051bebfa68b4625dbcfa06d9a8cf125ef96338ceabe6bc7866dba76f917bb5ddd7c0119be7f9bbe84a9fd1d83c12a5f408698c43bcaca2085294354c608856d9763d1b58ed569669943c1e3510f87358ba2acedb185862e1e8623619eb03d98ba6a079d9373b04b3d974a3df34c116997b7b0b33b3fb118f34ef348275baf7cd8fc77cd196936b2f97d2e6a215ca8aed9a51b165073dd125ae01170707890e3f77fb6c7bcf1011097c054780447e22fdf3175d571aa70659bc756783539292635cfd6b8eb8a2fbd6e5df1d928321fc95a167fd398411d2ca9e5c0f11894443c90e190ede01c600237bcdf17ff8d07848448112335ad5a0384f11013edece710a09d1b1e37b5028e54dfd1ce06a3da72ac05b46b57b833f0c6e37df1db78abfdca95c7a0f6d5782ceacfab292f060d77e640abd6f6c531891e69da9176a41db5f66d47234790340fcfded2d89c9ae5ff3411ef91cfdf4d3be397489123acfa987635d4eab837f9cb27106b37d55022c94f2bf2973b41c39648d59be7a979f871b035a1fdcdbd65993bc0cbeadf1851485299b9b977c025008c9ab8b32c97eff29392bf5ddccccc1f9ddbdca4ca42757021703b46f624c68b538cc98a12121528474c1475868c19d4986804b7bbbbbbbbbbbb628290e0b3b31d37f1c8e9b0c8fa1387002f16e06aea6e29a39400309c6c3163e8b5acf164c46dddc8ed07caa072c3531151173750bac1e886a4309cfaece8b19391b9eba279970b2e0422caef33b2affb141b63d4589aa67dfbde55dfe2b66d31fa5744f66ee8d2dd37c61853bdec3451882863163251926127d34f9ca5b9dcd98948501c1276b056aecb65f4d9f136668ccdae038e893613edba4f868cc2704c945f3291f74800d8907af94154bf3d173fa005536c50f9db0f69514aed555f90f6b5e2fbf8cb83e4d78adf0882c8d7be2097bf3c09b87d12d027f555e9d00534e587bf9ab08419d22e873242640bf59fc925987224c3ca4e8c3136e9581500329a54f9a908c61893d0004510485f286d6962058c47c42f0c1369ac18c71655fe54c30555fe46b184991a0863c450e5cbb83748428818638cd10331c610c3a5c6d805e7504950e26a982c66d5c8a096a48005944c8c998c0d1d8ec814dd1023f38215349119a2c80260c82cc124ca0c2616706a22c3e5862f51646650a444068c1b88c898b1c102543c8911030e16f87284130401c058c2e40585872c4ba0b858da07644e4d929a8c692f64311b906c20c306a2346cf0c42436864912d4f2482d6a1e5f933146c9bc05b7756577a4fa737b715d712291cc51b8fd40184e2b706202bba1090c3064326a46d54c8ddf0bdb997aa46b0b488e7aa3c62f4c81f6c9ad9bf632ba103f5617e2e344fab1564434aca9fd610df46de07098a1494abb3f5a23bd758de1f60363c42c812d61d2637ca006514a9c94c8606d300e1cd0fd30d6b8ad4292daea1a6afc3589732fa979ae72724137c62825ff7e2dff50a0a9fd6d6533a4fc5ce88f31da3e8a7493730e628c51070be87ed27d77e516a594a1ac45bad8b030ae0eebf8816effec662835d3b959fb26d77df9752791dc5a382b92b342a3b78203da68a38d36826ccce58713638c5e39d2a7f281c7f766f6707804fed87275687751ca9e1da3267bf8a0b6ab1177706298b56be3152e80baa9fe3c9a67f7a34ea8bbbb7bb84cd9ddd7290046d058c3ae76cbfc3a64386e1fb79f31ca9f3f5b93114afafaf3c7285beef4246ec2f776c13f8747e09f3bf6c5dfa4b5bb7ffb87ee66f160f6ff68bbda8b3c61a41b54ddbbe3ae9432eeee8e4be3393f8489839cda6fcf693cc250ff9652fe87c03b48000c60445c84616262064668a0c90e34389343163b98e2454049f913ef24452bd0e287a41df84003154f8a4fc2d6eeee6167ecd27425873262ae8851014622ea01c64c51c516269808a20897c1961a30c1c1f184aaa0faf7f6e8da3121ae80820c8c95b07240f96ba5c0fe49aa2bb50bb4c0a28595ca7f02901061988490c2534480a2fddaf342c5ff218292b0bf886b5f0b3d2415db2e4998a95df73739b93a21fef869178ba084b6526817be166bc7c7441ab573e4b7eb5d3cdac5eb84a5490dd74989863e4d34b9be38e38aeedeb5cb9d992977c739a7cfe9cfc349a1daefcae572bc82c0bfc06da852deceeaf84f6efa967aeee9b19e3f8efbfdb39eb5e38fb48b3ea1fee1ec7cf8330bdc49c5384e0a0dbbba404fb3a62fbcd4dd528699caaabba50c3195ab91ff020afcdafb884976482843631bd53bc6ed07bc80925153a25423538dae3dafa426b58f5a8c5fa87d2e685c94518b316ada27bf7dc12b7f8575b1e3eaede12cd5df54fe4d6a1abf4ca5bcd5e9dac57146d6b6692b5e1dde38068573c518638c316ad23deeaed4b64ddb18ddbdbbbbb5f8b1bbffa8abd32cd69ccc3fdab5b4bb37d6c6d327b46b3f046ef7a04b4d4ac571aa94a66d8cabc9d9dc944a95da1c57f456673fceb66d4bb172ea4a7777df388c7126d95dc6a86952326fdba64929b79a252dae5edd7ca6acb15dedda326874cf9152935195d32eafc6e75d1fdecdae9bbda5e3563c68341937d50a38c6892915192783855e09796215d7b9bb0fe936329b18db2aa678a4562c16a5fe43687cb058354dc494a879bc86de44d58daaae8a53a53457a9542ae7544e44cf709b23e78ef628b56d5b75504043625c6e95d3a57efce5e3a3c2ea476a05e4bda254bb9a48232202f235eeab7e56af38a75d3f9b8771dac5dbca472af08fa69641798fea5543ed045381add68243bed6146e41d94588004155debc6264a4d2a2a6b9e611082866fdf191ca63236e83c2b33585e91e0f92a4b49556ea587be94d6b2828a82abfc734cf92a9f2f90c17b54bee88d1fed83ba8e03687cb21a3b3ca3258122c06c5240595bafdf6feb1d991aa9b31b5f1a91bd05aa183a13585e6190c62908bf852bb9b53e551cee11f23cb1145c366637b18ccbe787ba8b13c1d667b3aca5f1c420c34aace9fac0fbb29ab27ef612ff6c54fa64ce577352aff93f70ccabfb065bebd87937aefaf7e1a4ff5b1886b7afe2e5bf273dcf5d3190ab5b35379d67ef29eee992b6366820fd00f7ff1bc12e3591028a076f17fd0792cf3177f121495839bbb6b9aa6757c0f716aaf2ae020c7fd148750a2822d757e3fb51857c845705445fac5ef1fab7f44188a05f70a4490534a54881a6e540b322068b850a9c631228b585d1dd4c5e26949cb512c06ab61d80a29507e2d5f30ae82b4a6b0c0f717abc0055b53b45f9065441935dc99bb76a4e7e3af2b43b5178885ba92c52e3b10b4cb818a29a104ae288b448a18d515eb68294a110c3c298a13356428bb9483931a466e77a10122f01ef91de0bf998081e1ac6bc4909e86b1a268f547c2b0a2a51f70a8fe4466ad2f701b2ea31449f9236fc87e78cff4214d3274c5d03fb8e9a57e67474b7d3796d0709b90782472d73aaa8ad66906a2eee9fad5bfaa256e8041c3598dcc663bdb59ca7be6f7e41eeeb7c7c63daad4eaf403790ff7a9f8d5dffcd2bef0c6497ee107b53fb6b7363882c4c89c4dadbdccd45c3a6132543ba85ffd48bc8784f66242ec9e552d41bbf697e3c47b4eb0affe18a692271c90ac0e0fe12f88a328d155f79777797957d3de59ba74e9d2fd71982e6cc27c0cd61ad6aee62149049dc5269a18d2344dd38e905490180263831a59a4903dc1a4c687a218c8a198e44a524a29a5c6b47d318368484a29935c79028b35a6502286882e9c698a303da8be94012545b524a7abcbb483ac0cd72df93442b04147101d0307b34359ff392e1e06604206008b1504c8328500aaa05add25a62e71dd054b4ef0a07ddd25a62975abbbc474e44d96fa0a099ce1e405982b76f8618cff2b5b0f9a51d1126954839438bb03254ee567668900a4d0ad2e130f3708a3868d8728d5290ed30e666aaa2e130f5cd490fe5cc2b7d490f500335fa0aefcb0035292ebf0af7445ffd36c41f77763661cd89318f3182d95dd573e689e7bd54b6edd66bdd77d230e272326ef3d52aa714a793bb1976d979d9aa7bf9bff7d766a9efd7ed058a27fd050da11f6b9a95decd42eae5c436eaa1c26a9dbd51ecafbd347351f3d1b7e72eeb86df536de115f7d8d27a45d71e58366cacdf7cac7ea93acec0d47357925e811627737efcb18a5bbdc26f6d23c2958d38b81daba1f3fb1d2c7499c5457c5ad8c454e9abcb84daae3525d978ace3aeac14ac7a58a624d4c36f1db091bf911151db02751b5417e2e95e4b1cacf58f8dba9297e69721813466a29ed62362ac2b1030d39eae663682855ee1b8fc911af4ffc73fbc2f9313f0d214284610cf5ad80aabe85e6514d6905b9f1fbb552d829ad203537bf5f6b63128eea9e7801f2976cc98ebbc48393ea41e51e1ba39913a3213889872d356c2a957bd7c9b35476a2863c2602aea3024ab43d36080530dcfc0d0d409ce7e728efc1f982664802a200e7614801ced3c020ce1704821fc09b1b5f1008a2802b213734006f40d615fafc9579da5c000605fb2a0e28d4c5507352eecf63a80f7ca0fba9d4f37a5b395ef5735c149ff11a66039d1f721217e73782197dcad971a620c314685a6afbfdf678ac34cf7e0c2526da5c00f6648408d582862b2352a47b3e0097a3b8547e3ed3b05dda010a6e871f87057787a356879f9f78f093cbd5319bce4983f382168c17322881422c45d1f442b4040a0c1a018b2d305088c13c394206c65640abb7d0c0cc0cecd14314483118c845efe834b16558230b224e76ee05ad0da03043060a390d4ead15be209e728319a240a1550d33d03f4619b460e6040aad5e8004fac72f8865e0a48918284473c611e8df6eabb112276494c0f616148adc38f4182bc0785a2e509834711919506902395f42c31846a0d026a31b460a18249093ee04ca4a1328d49d39e268dab08596797483153230888198c129060af90e47a076460a1033b06938228a285003a306982890eb68342d6b1c29f10ed6698b2632e712f58453ff10454313c8f96ed114451228d400323d84891a0c6a98411328b4d383981844275c684004725ad79e27110326723003b9ad6b2f686540d4c00914ea34aa84818019c0cc402e654506284ea0d00e94334dc09c56407b4108c0818c245068aba0828015283102b9ae6b0f0135d8900472abaebd03b4414311c8d174ed711618cc10052e93922e512047bbf6dc8719c8000672365d7b41ae0519658a40a1950e4f14dd74ed79d10f349881dc8daebd20f985071a66a0d03a1501469caebd78650b2d92400e47d79e63a146124720373dd0d13550e982040a31bb1358cc38710472dcb5d76a982992815b85194dc840cebbf63887299e20818cc31145c8c0a03ea3062398b604092b92402166bec107eb7bfa98dc75cf751dcd8faf7e3e7b21d8597561f5f397b43871dead9ea5303333f3b6d1d4d43c57b3d56c5b951f4b69573f8dec35e856b93528576e0d2a2b173bd51a5a975aa1a0aadbef340f8b45a993eb3ff0e19b67fbfa1989f2cf9cb88455ad41b515ffae363ea2195237b903a49582fc14f091ea4377e276f2399dbc67c1e94a1ee522384143f752bbdfbdc8afb5fd5657bf54f28e6d338d9e3ef8f0114be1595dfdacabd5b3e72b2f7e77146bd84523e0da0a838645e2d6004abfbf95be161fb5cb632967d0bf12553525ba8572636957c76a33c5ca1ed7560ae189324ddf708184adc7713268e0fa4bbed7efde1159b1a0618b69a7586d294c5868377d1dfbacd0fdb063304683e3a464667ed9d139c4bfb065a21605f8b55f41fe7e30eccbd73e18aa04d967180cf2f96b2cabe3bffef2e7dcb3820ec94f0890cd0d686a5ffeae239c5e66476a0e7ad05251430e780085b62cd0007fb4a630ac700128b45d010350bef605ad164d4e4a80429b1210a0fcb61510f2a8fc49407f7fad207f7ba92465c497d60a9f267ffbb4179a33dc40e70e166d0591d383f6092e567801063cec2fe5e73c1454fbd9cd5782d5c310ef7922eeda1e5e0b44b6ede5a1fd7aa14fd55ee09e9db5d0fc0d51a29188d742bbfaa714dba30544fbf8da47262a667599a880aa05a8cb4485953a9b87a97e5da62f522a173b5f49b949606b94c0566d86b2b216b5fed82cab06a46b7f093c1026a9fd496a7f3f0da4df05ed350f4800aafcb65d75733c32e198be38a9367599bec0bedd1853af799b53a5b7af79aa8f5e00ea265dc6df3eae50e89f807cd9d5cda95acec6af43a22e4c76256e66228d33c47df3040d5766831ab342458a07b6c7cff668ee9f2d1e83a7d82f8f45fc34ff682e445aca5ad6b226d24da4894c2dd409112832994cf603886a319b1cbac848e7b0cd3245462277195f70cf0356cbf67cfbe2af41ab600b1d587186146210000a0454f61e95dd0b5e4f29a985763191fe3eb023f87f42768c2d4d2e040d295454932225d174515c03494a9467686790ea0f6dc30119cda4ddcc8e934f4c16ee0c8b152ad508926d9a91718f4b4c14ef88f11943c61828471e738fcd8cf88b998d301bd16464239a9c363a90329ba5543355145c1a45508c60b1b2b9fcc53db7d6a28be23aee3eea280dc4aa8b7240c39d4d1ab9e7169ba3a0b151236ca53a9bcd664a9369622370699f1e4858b8a3847009fc710c438cb00c5a40b9913468d8b00ebba836a9bc89bc67f51edf699a703cde11ba8eedf3dee6711e9fe20d2e768cf8f8f8e4482aa28b895ec0eb4eaed1ced9a60eb149678a494a95834f556a23cee1a870dd8d0edd0a071a32d18a0627079a9a9b266ae80d26166a08b5c942e30e269b9b3528efcd0d336ee044a191c331e273860f0e19d0f8fc383aefe5222226f282fafbac8f07396e80918395c5647573a3abe33f844be0cf6647b8f1c54cc756417dce9821c30a13319940820924101111f9fcf08f65e5d0f16388dbe112f8a70008b0d9016b87fba6053d77543bce83c71b5de2dc51ed481c208af8949a130d37c6f270a763b2e3a345dec0a0e12209194af59f1d1f7f2d2e0e1d380e070dcabbbbdddd3631a0fc5a7c5985f24edffdb40f8165357edddd0d44eceeeed8dd317677f4c254f566efd8da1641f9e3abee8dbadb366b65709b13c68e9ddca6cdeac4df1882d4be6c44b0994046d9695b472937d9cd8a6f33048d326a51eedd44d149bbfa7520456ece2dceda1efc5df9bb591d790304d555fbea7d495509ba3d761dcbdf5d6af365949299e76b9d9c40ac470da58ddae51f1849d13185c6a32e4654b99aee612727560b8d1144598c9115437efae019c939c9830f5929c7c7c8454382e2508d2f24d620699ed6c718a353c4e98f1f7fb6d8a02def9b7e1869887ff0852c1d5f485939be90e5c117521c319c2f9c4621f59b161b34ec66ee365f386521ad29f21dde3a79cb521f51f50ffac610aec99afb2c5839ff0eff863e55b00d4766662766e06ea2061f93962dae2899a8419558a65489455665134330f183ecc9a104462e4dfeb38625a6b8e2a5ba6a36c1344e57643a7fce7905cb15236a76708ff8f30aac4625246afcd6ca47a4341011e58b0e9e58d0448728b248d1344dfb20f5a44445893035be8e958f488bf83259719f60cae149e205498c5102a96a4890a9dae758f9d05c55601113638cd183950f197657e454eda3f6338caa3d3d628caa3dcb4a1355fb474287236ea8dae358f9d05c581c01e6468c31e270d951441a463451841155fb1b2b1f1aa54bc49002eae8090dba28e150182104b793ce8ede6c971a76496324c5a0c6b759f9883963681874e5438b677471b50668da965043126a6842d59629842a59aab021832664f005112d20a28baafd5639aadad3346591a166a90a518df157ed84937c4dd3344d8bb17972545966aea6254a142f4a4a645c3465c9c2c251e4da1e4ee5892acc1237a8a416638c93a67eb99c06d490c7135a96ac3040d57ed218aaf62ced9f49d5de25c41455fb9d1554ed7d8670aadaffb0a06a4b31b8220b6e4372a2c6d7b4170c41668d1fd6b8f2e505dec5883238b9f221674e036a5c42ea414c8d1fe94e943151d4700415596600e35c9460c0006003d452e3c777aa695786543896a65099223c99da26b7edc809903094acec6003a8288c80c8949682f8c208880d8a5831a4a52c456c6abc92e84ddda52c2f4882eea8bb9445869913b3073821bb517709872527742883f67b6fef2e37af7cf65ee8caa05dc39f6550aee1772d5710e83a3dafda1534741de99e4e721e924907d89ebef9f83bdbc3e6e9872b5b2f56b387f86b6adae86071f7ae0ebf8dd749de53a45ff1e36b5ff1a39918d3a871e38259286a7cd5174bc5b88076aa317e38239176bd14347424588def48716e7c1f6a16941a9dd438654c20f0c154606b05185ab45d314aeaaa3102404643972b27d2744a8b18a4356541d6ae363eefbbfcb5fc85eb64820f945251513c8a694d4949fc1c0d1b51c16691e324ef6125fca4f247a548c44944f10b3916e78cb18bab18e5aa536ddf7393316a1a5be917d7481cda3d8d6ba7fb58feab5fb0fb9045e4323281e8a7f37ef82b16c41be22f21575443463256412d05015b828192f61a0c90ea9a517749ca957f0a43bd1899c0af02f7778c8cdcc87dce55d1eaf0774882f00f274569cc7bf84914218ae0278e19ad0e3bed60b1b4546a4e6e7ee4542b1fa9e7150af3b7d7e616a3f69af69aa63113738e07e1bef3f9e13e97efa83e745556815540a010fe9091d888a1b831565081db17d16c38aa53b62f88c1d60adbcf9f1f0cf3b7af06d97efe377bfe825b7703e12f2e0b9847c130e03d2278012606b6806cbb0aa27dad0952d54fef705610ede517d49ab2e3affe7eed7b97bf3af60fa02b50fef953d4a10b19454c45f3e733c1b468991d390c894a0f35eca893665d44651cac0b0065a2f88b37f2392a95f21ae6af281c2ccfaefc56b24df4042b4b37015086527e9ab2d509829fae18b183862573445136b6879f6417557ed8f116dbc3515c832adb68abf69325a1629d7cf7f7afe352352b1fbefdc79523f1fc6ececa3554cb5a42495977547f61cf729e15030d218a2e5964e02165e84aaee44a4a3e49d876bee7a4dd51fb926f6482f4915be891595bfd496a557dd8505256fe9c50ac1c28c74aded34ff6253f4a1155be92944e6db43a12ca09a71ad20a15c4a0ea7f3809003464074d367e8c9fe302628107e530ed35951f877754aef1fd9f66a73efc9bac3e2028eddf811ea9d1a4055328b78ceeee21cc7c0758aabbbbcbb8d345530c607bf47b7f5d506e57bb583a50ce3bd512b4e917a309f1f311e10a6522a8b7093f3eed62614157c77b806145b1c58747102933dcb66a620201fafddc0e2fa85d60031318b8c30b0a126223643cc4c02af377bc083aa040b8974254ee93d4766a9e30cda3e359bff9c66a0f7ad6b0380fa85d1bf7add607df4c1e4ebb5aad0f85ae2dae6edf5468533bb598ed7b4cf77094eddb8a53dd24b87deb03ee08168668336a9eb0a554212a109ff1c2c40994ad35bb99460d0ef7d1766d34eaf01a69fbe875d20e19286c7b96c74349dbb3d3f639be90a56a1c8ff385cfbaf185ac8d05866e543620a5e6a1f9ed1b4bf3a8d0db37d3f6d07e93cf79e1576ec8eaa777f3d10b8b2c540739d2757e3fde4fa14fbd01e16d3cd5afbcd67caef341f05a292198d8526b1a69076b7b6e87b75d10310403819a67fe4685053210a680f03b3ca0e6f9eeb7e7be1049e57e7a21923a9fdd0be777c441f85af31764a15fdb775f6b7eacf313c2c445cd99f3fb8f80fcd5d378ad14668802a85d714f65e5f168d79684d4aeed456883ce57a179e647a45ddbeff0b81ccc62e0ae2ab06df377dab5bd099e4fbbb64fe5405fc906757e612bd52d6ca4bafdb46b73b6cf0587511568dd1fb076a054287f1bbfbd233ea5fb58e8178f61a24e89d95e917d4261bfdfbd10ec537561bf91585f682b050f859eb457a576f77bce20ede2ef3d3e94ca3ba5fbcd2bb24fb55d8b71a7985591e45af4199130fd19699e0d970cba83052fa85ffcdb218942fb1b22a45d5f147de2518195449178096a62183f50f3c8a3caaf42f3b0a050fe1fdc2d7911537b3581fd0e6218128e88902726e2e13a21baacccccac04c6520c5566662ade4cad1d12b444f146f9f8ec33d7354a7032071774ab5c41802b03d16a2b85cf058d9bdb24abbf0a5400474b5ebc8ce1a63db369cbd462ed3021a6ba6e897a73fc58bb1878ea2e49115565f4a8e6e100fd10a10dca83846ddad9d99e6caf71e1769ca191080b438eb4a66d5d1847f45059fb8aac530a5d18473cf199554e56c2bf9d26e8d6d8cf0e25bc20edeaa75cc352146264f4d6d7d6b25e987d90419a877f9be1e989334a0051c39e2d085150ee57cfadbc86f217b39ed0d404bb75da50298772198c363bcc7bbaafe53df39eedb74a46cbbff9dd07c30743151852afe27ece397ffe0043270aaa1224154ed91eb67d42f361f8d4b79cea22f53d09c181ea43156c1f02559db27d3020f1d747c45faa2e54dffa2bf53191bf70c044f3abe2dfbdea4356179d8fbf1884293445f35bcdb7622b7e53bad80c8a6ac84620704137075dc6a00106c230bf05443ff54940fa0555f1dfbe06a95d9cdac2cbb1652876aa4d5e08183014ab468a30602856a7c020b4c9c0d46f9fda425b260216da5fd84f7593810ba63ed82603b7afc5338ef217d450c38e70f217ffa6c9dfbe4e1ad3444c5a16726da6767592bfa648bad2a4863d6b275dc4db5a9750ba87953419542a379da91cbdc3e6d6b61766a6957ffb884af3eccf28109cbf12b4d5bfe0a6b630abfab0f40416232c2d20db02c2ad9fa2fa84546095fe24759ff793d424b50544abfdb552bfadedabac1b695af27342bf260155e3ee744ee8cece67478436e886446d6666d5582bc149521db3fc6c36cf458c9663a9e90cb7d8ab7f10edd931455f869cd849141f691d817e0489c2e15504ba87edd1411b336307513c81421cc59725c02afda0062435811f4ea369fd3fedfdf0819ef00389f70075511cd43024ebf403c9eab88deae7871bd5b56b7bdea889a30534649db0f2a1fdaea226a36c7d0ed777252809ed72c7a9db05091e6ac84426f8d4241dabbae05a40fd49407e76fe1610f3b7fe62b0fb19ec8fbef6fe24e0802e9fa04203b58be3d57e394ee809f6e5ef2f85913e3e8a3a8a95c1450551fb439b96c02a02ea360d9d51696da212e539417168a1336e67bc8b1815e18ef0c38ebf711ff990d632b1d2f7d43d4def8186ac1424a9306385cb11182454c3c3141780f4855a32a411036b825a01fe351e9256803390d404e47327c5867f0b5989abef121458aa4735cc4bf56f22265090a9feeda47b968b24180c16c32e4d61a63ae8cf6d6c85612dd7e2af6a8e1d1224341c551aa2ddb3d2d675bef2d1d1acc47450fcb44350f304bdabc29dca4afcd3dda7f23ba11060a15382ae334cb56a354143b6c25492c4a86ea00cc54fedf2ff9ad0a54fabe36fa3023a47c0f5e90857b9339f676ca679564b104c75fe74549a4154fde9a0e6d96fa2213fc57797a2fab399edb1b5bb9f6284220a29b5bf9c6f9783536cb49fb6f16a94bcd4137eac50212cc4232ebfbbe3f7d7fa515598693c01390e5de718e2a3d0709f7866c84c5c8e01161242991583630c50804eeddf1edb7136b553b59fb74788105f7d47646aeaa8f36dea4cd5f9d3c6f358db0b0ee0df55737bf7053c046739fd82a05c4f5804833e3319e23d3f29eb818474169403dcc37dfc211bd81ea98ff353de065627fe72805ff1e38c7b465c356a8c435627c6c8c31237cca0e14ecafa77ad53051888029302abd31598abc57eb1646a8e9ae372fa6643b5046d9edd1e110c6003fbf20f67eaeb6f00db83045c9989b9690af1971764684531c6d2594c7fe9981ce3588f0bac0e06f8e5afa37e21463885e4b0802ec736a6e5c0a996a04cc6b22064a1a87c44d096dde430146e98392965b11c025b7dceee2c88b4d0aed412f42b83f277165886b828d13c6100b8abe9ed129959939b9c526eb29b638c31ae4777d75ec02a6f5d3235b70c547fee8eac52b1b721d3344d8bd107ae6776a8a3ae4df476775f01e1182a4ebf2825c3a293d9bb70817243ebcfb5e3f3365f3379720ddbf0cd17b2ea46433a3f74edf8a4bc3db9bddbb67c21847803a75ffbad1415f46342d070ce36a2e669178f76c9ee62f7a87e555e0bed496de7495bfe9fde96507e2d885b80431950606090d08241620656892f5423464b1358a5bf0532644370a2a88b47f7a89e559a27ff0b1d0214a479b69701a6a8a8a42ef64435756646000000008314000020100a860462b1482c1a1236691e14000c7b9a427c56990a84519203310a21630c01000000000008608066c4120025949beb342a08da99c14bd40aed6e5ae059c18bece3e31c32031202481e301737c55a5b07996a4ef450ab8c2c9344c846b0b4ed9c39f2e3e8455d22c6257bcaade8167c40387c9bf12f38617fdf5e8a2ea2c7bab614bd54564dd3a17670f89de9fe9a112a22d00169d178fb531749f72dd5877d220f48ff91ab95c5b8268c8f25c178a7404d68694635be7478476851009fafed2039e96ab767e9e83ac46814d3d4ca2b497301e6338da96dea5e2729b3d48a6dc9a6d87dbc21cfb8d6f41ed4a8735b32a40039082e76c6040c40bc1a446ba2c5f59ca560a3c5451c72bd6bfb325a370f2c9d559ff0d6f893ab4fd1f12021e520069a0b14516b6e0305a22945032da57ff829965ede15325b2ab9320edd598e99f7b3e45d98eb05bf6f44c1bec7a7860ed09932b335007cc3d6f09de42cb657c656d9edf2369de1cd2eb564b865d9fe0321ce12d8312c8509375c4f2a62f0cdeeadbcbcc761d82f2f5b80e6ca90018b2a9e704d2c94fcd6a170ad44d4ffa4faf775322fa1579b46436aa18636555991c2c3a06467aa2d27554715e24a3d38b2aa1d61f45d7398236e5d57b35a4d3011bd71c6ca5d175f0d215d1be0ea7eb60eabdea5ebdff72ed14081ac7342bb16eb2a84f3643b2707906d419451504e2ab2460bd8267ba6abe7e26782a7d0550e3d1b826450ce28e53d3d325785889bb5f17648aaa8e011338940c4c03c2be6fa9427e5ada20d6ada7118b4f507f07d0990ce6c835a1d9ae8d8ad303a4cc1a7975b6ce2a9b378a1cab18bef6e11bd7db76dbecc43830b5d3737dee3e5f06013567dcda06c34d1438de4423fe6f985ea5ba9bcea516c58a060cd26846d2b9abe4bc1c98b08d69255f68221b9a75c9018be647c0e5de9f24a5ba6844ed731fba7e47ac6fdb12cd543dedbe1c3b10ff92d4219b935d7485d0b8bfc446c94d299a4f87b9af0a838b0978f3481cab9933c6cc36f4cb86d52f3c61d63d307c9b28680d1f364b9b1c8dab150383a7222ac62492a5c686a9835a0d513a08dda2463c144a490ed1dcd06dc77f16e3c9d0b94dcde994b0f13d5069433403a4834ae2c03f76ac35318415d3f208e3748e1ee9ff10a859fab119769aa2a63ba9a7823d9c72f4b1ef903730a7a46d9d2cdbd77ff8367e2e871594a6e75dd028f7b9086bad736b6247c859011fa399f401c43b8a742cd9ae6cdcac91ac54a1c9e7cd241bea75782ae3bc9e53f9b7ca7c8f0ad8edc40a297681b2d32f574b0ddd1eae80e0eedbdd61d69e3390e359ea48a393985c96d8397d570f74d7e6e9ed7872bdd8f299815a6d5721d05c17845597d8bf9e64c8ebc5d33126c9c35b5d7011ee389bb5e19b8808cfe9055e606eed1795297ca82966dc8ba3532dba779e7f91f14c0f25e8460c9a4ee4f238247a07f5d624d25548d8f0d15d6dfaa3bf4afced947f0ddad0d1d9ac8b9254dc21f769e44db7bba0f96354d7b45a7e0d89523a666da797c33b17572127faf0ff3ac99d0088b7e966765ee61590731521e813f07311b2251a5959c858f9601aa44d11f53fa49a8e62fa06a5efff21a07a2647b5bd85e31a465ec3b2be336f94ed4db158173fe8719450e01f99cdb3486540eb93edb6036baa5853fc5f8d312491a6cad9896d0271892f12dfa7b8f0388017beb01c3faaadfd32a4b189b72ab2daed67499490dd426de2a40d1f7ec5d56271ae571b680d48d341c3269ee8a96442280579123815fd02e97bddeabd35bb7257ea4ec1af879df3f74ae0c01c6f56c7076fe3ef7e05bf0d552ccd2c733c7a0cbe9546053c312a870ee00db5f3ee538a969fca0ce58e672824025a679ce5216e6eeacf3f505820175ed3be7a5bcba5460dbf1ebd60ef02d610f3c3f90cba9e7648b4187455f724c5f9c9f7aec75da96555116ecf3f3fdc218649260d406174def4f2e2fd267e81cfc8d7881d1c92dbf9ea3f5f154e81832cc46cf1cb0344b7962e31fbd5471f728928e93e69d692670a3c84259f220c66045f1cf5b75baaa7cb0e7ecd804c5afcdb5bfc7c4f8c8ed88d8eed88eda0a6629be786b8048da6d3a2a29f2aa7d965a1550cac60aa52bbbd72c665f54ca6a5d5b2a96f02790f000dea89a31fdf32a9091c6cff0c4eb1f6fc6781d349876f8e1ac2b23bdd70d43348a09c35e3b22bba9ac6486b4de48388afd0203ed374b0346ef63650a0c0dc33fbcd6e3d1f550526c252c313beee7f8815c809c0a0aa431bf552228eabf8864a558b2c1139bec6d35a49e2b7d9d3289cb40cb3832960e6ae2a903bbface4bb5a99fd5b1a8bb24e897e62cdaa3518a0424df95d2146611e29742899449e0927ee966cd051005f36cf42e0bbaf5ffab32b0e7b5e892a665351b2023232ae6f5e8a59da1ce9b54d67b4c6ff3b1810cae5b9a154514266a98cf040645efb3dfb63aa5270217648623142fec434f1bc7e85c4dff0c03f4375fc9cec82941e533bd73a889b932f829ce7b343e6be751fa1fc5b7e5f5bb99af0842199218465909134e78404490113e247b12328fd15b10e40b4a9e70ba961ca33ab1a8098923d29288f1ad6ae190497d2538c52748d3ca7c63276961871ed89e44d2d781b8272a8b89eb47f24007038b2fdac94fb49cd859854ac757cd192a8c4346a01c6f3f0e55f0bfaa33cc79463166a2e2d1fe7b623b52ef03e2560e813f1ee2f13c9f125a1f49f259abf95b4add92ed9b2ad2ec303c41789bb1cfb3c9cff949653f6f967f462b7175d4919e65f021189b2c40db4cb6012c6db9462b70690208ef7dd6070191f7d000dbb022c630093cd3257f81b2d2c2e9d2bac7194ea88f4228766636ca966cd997ec13b20c7fa672291090e7a370e3b5dbe826dbb9f546e4fbca1d20597c57c7ba254d4a3b5c6f625ec79561012156c77d581878412c89f76401ecf2e684804aaab91525138b7d42255a4069cdb5d2c4e54234282ea76b05d96b6772ce236068cef19c5b0438ac8c37895c8352cc0fda0bc3add46852950dc1164546f8b853f1099b9fa9ddd38cd922481aa35c12ea5e1bdca18b577f1567d391fc64b7d5d04361bfdaa5c6536772f6e5db87efc213fdca1b38f20bf81d2a7918c74a5e0c6d3fdd4dee3c6db28791d667823316ac4e01b4e407aaac34edc907d790cc1353c67accaa7380c9a937fb58ef3630662d90e7b39b38c0101b606183eb5d5da9267fcd6801bc20d79528a571e3cce59b3b2e8da18fdf55806a3cb16fea53e3728b07eebc2979e108f8d2d9b532423d231e506c0546b2a7793284d9a07aaea5d03ecd9446d6a27f8b551dc233b1d869e85f654fe929477169c237d13542ad3810ef416c39e0e4dec26d093682873d6d909dd8cee81c07f72add41da08950eb414712285b63d1fa6a0a99b1d2a80bcf4dcbd8f407f3492cb86b4901141ade321c9b7147c20ec43306cf6d8d72b2fb3c962a2e1b547d18e239f4b9c43d65fb58a181e09a0e43ecef88bf95cc7858a6ddc8c0e8c8a8d3b3fb407e37d2a17cc05832d064a4779f1d3f346b74ff69f0a5e88cc5b7a56d6113aa55ac246f96bae29a6cbd62131b56973b7bae3bf81fa0ccafbde42e1996212a081d6d47b6807ed3a5d9186d4afe6d31535b3a71dd6fc41cb83f1906ef84eb21afbc487d95f16dcbf9301f03dc10cd19ab81ad9be7bd9a68b4a4e5d70d8d7d77789302ac51db2bf43f53ca3647b4bf3064c51ec0f5f5dcd40dfdd28bc2d94d132cc829d13349c9e6a0571e3848b2b0228222491c2ac0d7b31e56c942f5ed68bea2724deac0271b0c2dd8677f707d4b0636408e0eb619c5e89def5ccec3304378e622ca32ea91fbbdeb97849da203b157a8951e1ea4bda398ff50195f042fa29a464c76126be7191fe2365e7bef6f5adc696f30c5ab448edc05019b47533efd2d1a8e9a98fe2d2f4f71da215cb33d6b5527132174d202ce7f46eed2682782c472200bc296330a43d6164f4a4c0e8841e907f3bae7b61a9f00deeda2385a7ee777a3bf1f67c65f55c81ccbec3e582f22f2897432d2dbd6e071d5bd68678bb4562d645c79950edd76dc7ce9a26712c3b788875f6b7daf8dee73f79737f314c2623a24acbdbb193958ffe8c3f834c659dad64901b437b736e823824d066221f9f296c4b07c961e8431238518c86e4a20ad19a8e14bd1b772234e0568b6c690bd31935380b6cc9f50b5ef9eed3af09fb477359a3f226e6087793d5e49ba0343cc5e6b8e41534c1a6015eeda31e3871306aa5075471542bd9b324417e10f09789e663bae8f6464847544fb56fde9f8810ece2aa1af6df348888352d3d2dcfc0c19efe4445b65721baa97730f665fe1e3dfdd9cae5c7281e553868b48c43cc863540a8eec6422cb36620147aa6a07e8df4865b62bf0336f71d99058aa51aa302fb2e3f26aa795233cce1adc34ea64029ba611fb03070bf8f5c3a619cf28f282efe9deed6ef22142bd61d9e15c360e4df2b90b3e25e590b9ba564e2944a4966e7e334c64c71ec10163ed9c9e45ebe08a3940066b96bb75cab2c3020cade315faa88964168ae278fe6ffece6898f92a4e4820a84fc04da65026092b58d522dda43c703d746f61f198300635f47dd5640f3d07c15de9c05c05bf87e463e11867141e6949cc905cd84de3175e64b8918fb9a60297b654e8828a4e5dc978c3208b52a14f34a234086da23ae5427fb012be53d3d5a260e45755659458c9624ad84813ef999b24c3c05a4f1ac88e0580b437f4f001f7df7c344cc226f07dd43cb0e8832b6a330c0d5b89160d968c8795b246065516f958c9faad7f64532ef0c20c72d393ca7a0a63afd936835c53836e3c4949708cca9bd452f24d2df384ad2fba0636872482b2c72ca73a8272d5dca3081bb49022b312925c717be76cd4b20ba54d6fde0ba3a34ec9f520e42634a59f83ece07341c16e490bd7cb0ee488705fce61b00ba28282f14fe49dada049e96d3ea7c5ee08bb984c8896d16a83aee3aeb7255296f21b01bc43a2dd4eb06ba5e208fc5c2b343b691b96aba210c06ce91439f09ad60ecdbb596d885999b6758b9ecdbf8f96e966fc3af177a1efd6fd9197f55050c0a93ae7f5a4a1c38e855b462930a7282f7008aeb9d694f6e4134dc359085fda9ca36c205db9fd3aebe8bbbd5bec90cdb3b005c809d8af3aa4c1024a2afaf5bddab8f94a50ae23b4b5bd00fb9edb99bcf1fb0f3deeb6cf16ee2cb310f4b679d9470be66b7b4fa11a845eb2acde4687d1e0b19128fdc2d8815c222041cf6282cc4f7e2dfeb3a3aa13890fe4c19e223788914aa52082578214a027dcd12ac9558d59bce8bb718ba3592a6df05eabe9e83256bcfe2a00c114f513ffa81d1064684b0c9f8d488f913d518c08a9634f09e6a0c8776bd68e7868808832a1300ed1db4ab2719cdc4c6e4f8e2bc55702d411c351c29d2643738ee67ed12ce5da1d099d57590eabf96ecea7ed375516973bc9234af21d3c0c197d0b45a662133912d18caafcb44a09a5a3cfff99748e07dd590c5fce43d50631be3ab47936ee1c4e43f171f2b865fab372a60d956cc4025d6c93bb8afc259439ed9cf38f7a7c95fa2d7f7e3afb63e43b904d6f2cd12090ba11dd3ab90d08eee8d49b8fee235238d8d3275a8ff44962c4d00eb1cd41d2dc54ab0a41d7d86d60c93152cfbef5c0ca3f803831d0f1c14100c64a9a260ed6fe43e2dd41788795cab5e373e1ade5f354f858ec509d2b6e59736cb4d25f37749703ce7f6664f47ba82c21f2386b55aa9b427218d1195ca79f8072e7f0dee8d09f0cf7b868d14061a2cfaeea8d67791bb916f7e8d2a87b05c112730a4b0a9c064ab8134c6cafebabf20156ca9840a38e7e53975f061c76e8335c021ef521f7554e8eadb7bc726ff20a4c4297221642e3a7c4923b854f6888a9985fe6e3f1ca48296385683c5b9372c609068569459c9d9ece3d452826ef245008c09814804fc097aa40a7e0db1dca621041bd08207ca708c3c03537af66d52f4608fbf49c0a56e5d87c734e39a451c7c975a07d5a65aaf63005548b6b82b9645db2d6e4133b46cec7067eb8bac33f2dcee9e22479ed878b866b68fc75e51439457c42aec219b0401884a6f8db72bcd0a662f4e275c520f663565a10610135f5f4924e9d54a0a03d2ffde49039c684b95741e0f7b35c173c8f915aff228137912fba39a768cd4575b788bff724102b929155b91d74001393cbe84af7d46552fb7a9c52b92d86500fd41e4d4cf7465372c8bad7d15db2043fc9b7ca2df98cbaa90d1df75b504a7b82a3705423a17d22acc5c8de3444edef7c41e47995091bbf47fa343924752e6e3b51e210f1248a5960a79c3590933410efcd04a52001a73ea87c9a461d9fb476859a4d64a28c651056b2c93ce99e7f422751d21738ad857212e2baea08158efd8f47ce8b74460d6c928136aba46bc3674d853601026ad5fdfb2d485780be8da5b0d82c98714b6cde9d72a2a747fe6eb29389c40fa75c127812ad3afd5a15622fb55022f8dd0bf5fdfb79adbf08ae2de79131d476b7216818d133fecb35f3bb4c5eab12efed4e125daf8f0ac0a48e6cec99c4225ae4d913c5730186a081236616611d094d3b7fea045756a779eeade5c2c38cf99e5555de212e3506614343492f7f178072b698d77c124eb990fa12f80143ea745a46ed73e8b667d6a9e072e3d5c99a821918b3615c7932c85874236e8590a7814a3b9d8f6fb2c018c5ed045f9c37885b5beab2e5b8cbb05c4af5965bbd6e21ffd0ee71dde62559804070273904a0699d07634d6f373743836c68a5c0d27fb5c9e87ee55f6408aac5ea7aecf5dd543218acee9ca3183041a7993b42da88179c1a657f3a43bb3ff16241b519e8aeee937e80d4b00f892ea09bdeb76f908cb0d81e3984e42e8dcc0df59bde0448e5e5bba48437c1cca8aa4cd69bfc138a9ae8ff0b3c352ed1a489dd720555deb55f05ad0bc8a62a5cde8a82746aca9da02342d18aafaa63fde1c98e7ac68a3f832adaa9ebc594f70aeafaac5157d6d9584ce11b5b130cc2dd872dbfff93d0977f91bebfab4ed7f92c13c8ea7b2bb6425a6e7a501c819b76864eb305b27e004786d71a2e9b0b85a64b2a2326c55e60a586adefaeb15aa6d9357b542755b45cb27431f53206ce52c11f46ee6f22641001d83e28b801e4bad813e7e83c6bbe1a6ab456384f19723c140319846d7353b6e4ca01ffb7e5aa88ce57bfb4af38781b2d388b03864df8bef400bff1517482428067bd2f285bf43ac8593f54788b404370c757647705a15ba1e5d940cdae74c7623905d6b5d3cb356ac38badeff3e899a34deb7dce17f9cfc173613dcc9b62b1cdb0672d64a954dd90370d22c6d65e89c77c72b462014370f9bd57b49938dd17e6c4464aea2d0e63124ab89c6ffe10468ce7b23c828bc51a3ba7cc28d1457fd50bddec21b738340b0b5e6b9c8ab8416d01f89e36b76a667160d9a0039dfd885d35ad84264c9525aa531ae66dcef1822f703e3e909141e29f26325a0830c7b4972be562788ac5e50af9dfb99749cb544e0b21c3ae1cb1cfb44254cb8b162973bc17279c94215d5f4479d86f94f8910b633d6797d8a16adeeff1b126d6707c99d073ce089ddf3c475fc2a328917169d7c614d649c541a659155f523e44858fac6a804a31bd1252950804e844a430f64e0a203890bab444fd84425209496c5fa43d5f5452064482029c6c6c018f069d30577d018ba006fb44e4a0635422efbca3586892c0e01d0b35bed3abf1d02d9e32d02c907e9e4f9dbed8e5bf60a28c955dbd51dafad029bbfb829da53a655c8c3673ded35b7901c98c56a904b8bc557acbc264f38771909f90b2a20f79d8f24e37e41fa898068686235cae85ba9fc32e243e234fc3995bfa3d27ff8520bb491c0f356193557716b353a0f65e5b46acbf4e4a14b7f341b9dcd6122f9aba0e6aea55d3db4def82787882021ed08665181b35084f519f86b4e720e159d04c9c9915281e135c3f2ac4a1a790a67f591b01779214bf9b7e6f9f2c0b6ed88c1c907eda0e9cf552c6448692ed9829c2dafa593b32e2f7f20f94c41ca6c8098ab43537adcb9637d4d2e3afbd12931e875882aabe67a1c034211b575c5725fbbe98e4ae1aaec5aa44f1a9bcdbae868366c43968525459198fe89d368482d8d6154c0314849580abc34bfed219f171d08173991342b2b9dd3ce1e298e3dd5c87f634ea72ac6ac5253062cc4418b5c03af73adf7319068415775135d19ca4dfaafcc4a1c5c81382f689458b144d2d5c857a4fe9a012ce69b6398cbb8979b4c177f6f4478bb92e09f033f032c1b817d99e0968ef9f95b8a3b600ff4850c4830c8f226cdb4ac9b8192a138a12d32453786e4be7eb4abe343063e6a6a64454aea7632ec613dc872b3858fa4b2d29b1de3b111d8c1f1121540970999c73821623ba927afb9abd090c65daf437e1760de8977cda703e043f7016a128c2b17c2fb5ae246408ab280827dbf5525565507bc02ca56089384d007cb9dc4e649c9a4f4870137dea2611fe7dc54f09e2d0e2e83b67ec873c64dfd44f7bd25bceb52d08662389cedba8f174db93c24b7a6721170eb804d6953da084a6454c61780a8c4cc9a0ec816d26c19f0cfd5edf20086e037fb3905d490114c107aa59a08a3e09642f8f8197b7a5f121a6c61a3f3d1a3aa7e1fb7dc2974485f021eb0064da66515b41b77e0b4503d6c6351629f55f7be94859f4620c6aef4d34e2730255574883f33b4de348e09fd3089be315e1e775506b75455c5acd7517256aceb4a9650d1a6990f713b44512526d3cfdb064df0b0ce8788ddb2a433a265c67a6bf5a5fac5a5ee633008cc4271d044ca40e994420cb1412dff0b2c65b479dd20679a6ea5268e8a0d33b48530ad1b822111b4f312a4322ae2e4b7b00c2b8c4f1094fa7dc55798f01141d6eed89f88089cdb288315db50f1477f64b48c6fa1819c38e03e52e5eb563ad94a729dcdb8916a4794fb78dfa9c28d182a0ea12e5a560bfd34be1c3c413f13c52106e604b814f23f18ad3826a0109fb14c1adce0345831d39700d6f4f9ef22e8ceb46855c3609b0cc5c34a5fc546a97b74c5f3bd84ebede1e798de74ad54d963625cc00fb45e7652481dceacbd7280c30786067bd7b7228a0cd0c3427087aad96ab11684e8b59865373102a628605471a476023c9e79ca1096d1ef01f9fbc1519c55e19286e4909ec13dc20318f434c047a6ed0887a1a973835ff0ac7b757ccadbf24febc63a53cf5add643da960a5ad78b12623637959940ca7185a5b314e5ca1a9e7d777cd5002f682e05d3fc21e1624774086d5451b9420ad8b3d042e38b90079d78565be5185a68c4b113da0abdf7a758c35cc636ee7dddd53e078d77b8cc54701536b979491600bab02832332def1bcd73b34a441b525ae77aae318aa2786532684dc5ee13f7fc52a0cade15b086cb861edbf21f0907b2e73f50fdd3509799bb117522d3a5cc643cff9d5b07ae50f188441bee420d8a2efc3bb013f582b22439a5d0464919944fd8816edd694d827541a5c8eaa3aa7bbadc27391b82d8f442b5487f7100a186818d67031ea3103db375899b1eba23b3c0f6d41fee2eb18cc795a8c2723a26145beb918792112cdc5f5dcb93e11f748f18cc525a3459f474c7744bd5ea5b1a251ca33f199bbc87ccef9c0a3a43e2b82dedd3102ab17f0dec9d281fe0969c5f175eb90dbeaf92e061b667cf5c3819e1e3d5a91eef7b6a4d5466fdf2091eed321ceb78bea8e395573066d8df8b650215771b9d3f2e4fcc354a4fda1bf4d7fe5901903be8bc7696fe436a72e2e8cdc1205e754af016edf187a8f2cd2a2da3b335ddb47bd83949000d9743dc04ad0621d42471455f2322331e744ce7246dcca279a5f9f30bfc72d4e7de8a02d24930240280457b0cf212f465b7ae951c7d7ba1a54bbd28aa579375d3f3161a89c3df9f1740356e578b1b364aba992121962e36f2b96d4178dc1142d1d4cc5174e9b11030f6fef82541da218bdc49b6b115f967ed929fb019f2df5f51ea9c0a59fc6beb7ea99c30d08093d31f49ea9856bdb17eb1a73fb953ce362203eb7d3cde1b8d1a55f81233dff4a54ca3f60bb856793a855571004363099abf71a092bf3ea3fbf7f5b7241379a3b1347c603b0489995f60fd279c973f6b9f9d8cace18f2b7991591bef90fdf5283c21e65ad024fabac0de9a61af0fb69f268e8d69b5b5e0ae4001907a8715eb5c7f5c2d8d95830bc02ac5bbf059791c0fd28d1d2e2f6ceae94de1767058dc86d82e7bad218af6ba59fb1dd951974c274a43de9236d7f5c60a4394104c85b024e99140ec6a5baaede28bfaa256336b4274ab905c6e8bee5e92c55e8dc917c966015c6315f07c1d8c822d5dd036180a5dd2db3f6508ed8ee32a6a0efb0a4d1b8c59f26981f70b811ed26818c7004fe0ccf44e758c8c28ef019448be469422514de3fe4603f0cbb8dcd405029c7a63ae74a886e4ed392f374f742d4cdaab4d326160d47d559b8ec21f725d1cb9827bbec15599ab7191ffd09b514b246b7b50404b68ae576ccf2fb6198ff0ea591a7f15355e43a11af4c61625ea302a805b39d7fd8fcd97ca915b53c989655b2ca666bce5d2fc384096bc3a55d4e0d9d79607e829a708e888f7960bd6ee06c2e4e11948c2d35afc04b88cd50ce4c06ebbd07bf54999018bf9b2fa092d1e9436b4c1f0947123b27e5137858ab55b1a9f8913f3667d1c588d362065cdeb656cb0dfc1b1fea4b7e2eefa2e38ef09126c9a39926a40db466bedf2df53dd0201e00c914bb4cefe797353039141de0269ab7719d9d477b184ff63bb7634274af6fc5169f78fc65d1aba9b8216107bbb67bdaa3bcda654722add7f2b32582e798f8d074c838ae7b8c34017e30c87f5764d82bd020bd72d544a4fb2170061c19ce40d10fe8d11d5892a4db3331f055a757e6b3a42ea973efdf1e1ea6436755027ee967a0499b762cba390a1923da2978706703f28e3fb01456f3d5352c18644f16ec55e38a3907a13f2f63ca41b75383a855119e5d22fd60bdf1809d7e30f4b62beb9cad5d2717d97d700fa14480cbb8d6053ed86b2b618c5b3f680a00518dfc4ad6236c9a4128c8187c71bfab5c0c7568b51b2aac54d62b9fd604dba152c7c2b27606c3f8091367de8d04b703d3c3a2800cbb8b3b1b0cc50561fe09059ab97233a827577a435192fe00e8f1917008723551ac688068fe5c75081e33c62a23786cfed5a5c10b1e9b763cf4b7f384e8a9f382882aaef4263e8d4ca32106eddab2f7c87f7f15ef114577e0f1e0e3d5aea118158415f930e6b0f3135a77016c65f3dc810d64d161a9bef1611b7308fa516d96747743f8bf9a60decb6dd4e9f7ad9df420c25084dad62257f963e49288ee0dd309c854f73cc34d4a53edf54d654c49047b23a59c6f13f65cedb3902a226b61bff68b3b6e64a6f8c5330da8e636e0b819c0e76fd426132dbb12436a4cd280afedaed90e772f3c5c46c76cdb6fe6e7b770a521270dd6eb1510e59713fedbaf874434bd78f297bdcc42cb4c2f4da9fa018003cbcdddfab79b432e77cb8972a130a8b4c689d23017dd1753f7dad7c48de8d85e3933f5c9d718897119891c38d4a3a93e7454abb7808c8e4fc4fa230ef4925d0339e92d1ada7b5cc5a482df4a054fd1603548234a7443bc9a2d7b664967c3b5b84b6d9825e27f0fe8f3626ac41985f54807237a95cb2a9af87b01a7462cf43556b902a33b335cd17153b08780528d56871402c5a3107b1f40f1d62548b38b53346b7434ac301d5218b2a5b1a02078a1dfd4d75a27499398d1584dae3a7627d2e53de5c3b6bbb8222ad23a904863d68982a4646b027e21f6a8d5574398bd6f30a6c913027faee3b880a4f5c038a0eb7d3af133f96273c64c408a4001870a93384474678001a88789795ef91a3f214823ddfe8b833f64f61344e9ef3c8923fa1d2ea35927e1030b0a70921842374fe8cfbe1379c0d23c06024954fd4b4b77c98c8ef93beb96c6b165aa1669d32a0897eb705a9031f9636fc9ed8e98610a24ea878b8349198945285005435485833e707b3a91bf6907506f119aa4da0503c5dc7baee6e855ba98c7cc33abae0e23dae57cb8654e1e90fcbaa1ca35697a25061d225ba2b626699b4802515fdf271f324a3af9dac3fceeab0339bfbbf00934912ab1c05f80cfbfae102cd39c978437c61b17d391788a0b0613b86b51d8f5079a32f8498224025e835077f57ff1f055af2846245427d4cb4fd97e6c526a9d7c417c0f6e051b2e1d3520a45b6865bf53b05dfacaddc4a39ab1ac06657ae2fa80ffd19003e2448de43cef866a44d59b0e211264338bca0bc480308fee2ff9950660a2813029a85836ebc758abecd548fc96c020eefd2ae1ef93435cd692b26b72e3ec803eefaa7b8a908bcfa74e939ba82f50a49a1c4e45ef0b6b610f37911f8d28d01f455cf17588d9db97c47780cefd4d32be7c9a4309d31fa2710d4d0bb3c847e8b43cbae682a2974c01ad8fc5563954ec75094bf3f4e55e28728bc425160f4e9621a3a41486dc4c641c08ba3bf170b715d486e355d5059277007297c7a8dda904aab666a411a92a8a88e7314819cda8089653c38ac789fa7e1dfa0bcae9a9aa3eacf03368ca0f59a47e94af0b09a564602734b3e768e48e6b05eaf7be03586a5fa03c73121085295397903994b3c0238fad942ffa17bca3a774abdddc278de187079b60e00a4c775409e3b61dbf5a59b02df8b77ed1fb2ed8041dbf88f9028e36c78b20037a5d2bf60b1d8f58638e3c743289260a5bfcae7946951cd640be6cb80612796f746cdd76977522c4126101d3f0bb0b1f94bcbc9728cab904397719205ed21ee1ac9409052ed54e3e46cf391009956f1884a89c7f0c2b4dce98a5419cb78067dd7a1822b46777dd07945d68d4d7d34698a1a3f7ca2c4998835212e4ad403fdae85a02c45e226b8318abcff5b791182b64c1c90b118d9c29d91dc147d5818b3681154d32d3b33ff8e56ac2192d4052527cf7e8566b489135c78286ae6ac87a295df5d6561a9eeb3038836b58f8a563643afa1680440925d5398883f10508741be320ac6c58d230f8eeaf63140d6c60ac900d24f5c6b5b60f6d813edb94c991f5676157320f3db6fd33e2a8ab87e92f7f7c7718fb507399a85adafe3942f8267216486910f6787ad1b455993c05fb075a0fb6f048d01a502d8b9da96b3752356a944339ceded31007ea022e0d4796471fe2661040587232758e46d6714f81fa53883bcf0ea5590a9ec9e7c4b94a6ec399aad3b81f60e8a2a07f924efc52701ac53eb8e25718c301e16a373b10cd997f7a2ecce6f270ec04dd4c27a2c5636b6b20889e4e6911108764a8e48c2739d83a4e8e66f95a9121899625efaba6cd348aba1fe5b60ec1d6bd2803cb5eac75efab6472a9bddfb6453bc98dadc6c246a1152484c31352cd7785baa371a8785cf59f64fb0e840acc2dd9de1a64d17903e5cc707fc986271dbdba632caf479829eeaa6708945d4003ced1017b4769ff7038f83f32d21b67a3ee778fa1a5d2ebd33776dd796da8466ee68d07f35ee4107b61f00bed982f94d8069451b9b042e78e5ac365fc4816c2c9923ed63c483c7d639aa16e7ad4fbcb8354f748bc3bb67a5bafe70cb48ac175ddd9f9c6d86d58e0911e9bb5f7e9d598012f3135733a0c8a9106358d429ad64603bbf6c57c5d0a3a391c591dda9168bd1feae8de23640a41f1d7d9cee48798e21d0fb4a8d7cd8d7652d991a246b6d57d338d7f965d7e4683f0ae48eccecf0a475245f9f33bad5ec4c688943553a0f94b496de195b5eea86adad1adc0afc65dda9f70e43d853a184df9eed774a322a97d8d1b82304263067b17a5d50928baa384fae2bc007929c22c05bb74e3c7435330aec68df4a40e9622462881a9fa160a6e9493aaf00fc665c2baf1eca494086ee05b59cc74e4cfd91dd0818fcea0f53dbb50b75965b402dbd1e82664a592af2089980799ac343ec0b70c36754b9f7c82d237008b46994e8be4c6a00b545bd1186e370dad3e398e61abf20f980dd4eedf53003f9d3307b30ea7105590a29d2fe55e02388339be7d0cb8a243fd572bb4197e5fc5f5b912a69f09b6a5294848bdd19a652da4fd5c638d9d7879c04bdd441cca7a25a80da135373b5febeaec2f36b2fa4a33d143679e4eac89f15e2cf2ef34550c590c40426a854a627bd9c5ffa2c85cd2e43423ae40c1764a6b07acfe6be5040cc3477250b9fa725f4382e57e0ee2babe440e36434ab19b0ec2f001cbcf821788c2a02e8b2a23b525a7a7f01cfced901640d87c3a48bac72169d92d2c178fbc4881c1d8e2a9f1f411bbb578950b6c5b34af6013cc16af496938044b983981a97ef8031371ae8f028eef7d649f4a0560e70153ff6ccbb72c807b1f39e473de24b6d27d7b6808376b7553223fdc63ed22eaaa595c26e9f3ecb30fc6c9e1586a14773bdeaf2da5c6003e783ec65dfc25bcf354afb64368e9c538d3708e5dc635e2b62ba7a9f62344331510b7652f60e04fa399fe5723935d44d6cb78be7ebe96b57e7dd384d61e2b3cbfcd739dd13ee46959a537091d60b373acef04a3889286789287b060ed76d189fee6708c5f8f1af9119ca241f6b934f0b9a78d487f72912d943ee74fdf7267f2670e6ecc8bee4c2ace3dc3d6f61e3de2e92d993450d10cd5e29ac7e91c0f47c4e6b142258e075356271a64b122348e072762ada3f11899d5b71c8c0680a6b61aa142de67025b57640e35c577d937953d1791e1d34ec1529e7ec01535b875f87417cf4194e36843f664d5c7f72aa31765900be30d760fdad5245d4696d35a6cc05ea853173e50fac202a4e2d903b3d876b34f6ba09ce6eda810b6e5e0d47a664072d09967c18f71f37917aeb9e47cd1c5d02f5da2c0618aeba171719a03ceac7f94aeac2f10363f09a5cb8a3240703d1b6afe0d3ea0d00a72bee9acff19cbac28be12012a37d55b79fba3705763b75d16aac8209bd164771411b3ec96c0e7bfaaf604074e71336e1b60d8bb53801ae258c2d9659d9780514b9319fb8eef09bae972ceed2bfe91f7ce41c861b3c186be4119306f94d9b0a183ab0e0a0c20dd5e217cdecac65113f8a7c320e177544e6eee8b3ff4eb76ca1196eec3b722bd0a16ebefe705ef777ff4e86ed751c6a7f80562f1419b9e7dd01f7f9c0f7b919407316d824964ee2c14663b3c3b0602565dae13059336dd9afcfd9042c4af74abf25b5ad9e4bd49795ef3e01371a5e84084494823b8f93191d14a1c2b9d059be8169866222ab532b233f7a0aaf1613517cada50f71057d6880e118bdca0d8f6265ba1f8582faf97b1d04d8d85e26b1b22f3a501ab930aca7129a05aa7e8f4ab2d68110d0f9684cdac3af5332c753213deaa98a7f079eefce267cb69f7c67634c74eada140b30b3ed83a0d2f8ccf65e73d3259942d1c3dd5de409608612869a198e21c51598cb5b07d674daceb0df4a58640716e2e139fcfedf43da2de3a64a4ce6e7a8f672f3590a1a2cda3c2c4edcb1a885b09343439a15c62b11b26b44f4ad05935722fb4c98cdae873c9be541c207437ffbe2b7a80227b064572552f878e39f838734fde73ba21d8466f8d62c83321d354a10c3a6dbc81ae53c57bf03dee816b7de38c4144b98983e71d9d05fec4c08c53622a70f80c9b268928b659aa6cfd95fe6fa451d021789a9630a71f7c2b1d20e73624d3e7892591d9cae0654052e5caa40aa689c66b111f6e70ef4d98382a0e3123192eedaeb23de64a2f22e68081e533cb840c42db55de4a3e5646c9e0d7b3fb842f7a640dca6fee3abcbedb40f6ab311494961ee2e823189bf6d8aa91f46c333d9f384d3cd43ffbabfadefdf7a7e9a6f7baebf73a483ce68c775af34e846ae009318e83646edb920a31ecd4ff3bb0eaa9badfe6a1d26c66b86ee69e23c67abe4eef8638a6abac625605dda1229853cfc2c31fcc562179a6078db6a82a38e22c3cab143ecf1866d9fc41d5ce8cfbf1cef0eb67d989dc40836246e5e88331b479959983f4ec91d24061c0c88cb7f03c78a1b76e4f9303a2690f0308542bef979959e06f78a4152eca5f9899424b086320ad320025b893310c4c36aa0be34532d9f025a88b5e11e2b4b986b90c4e0511aa0e2c65d06de49d85bd1d7823cc819504f80b6327913e2b8b5aaa491829ec66d9b75ba5ddf5a6bb6ababc950d1046004fb04ffca53a79d8673d695468bb6cee9eed83d101e0cabce36add74ba5730ed7f04876e862fd07a2998c14a0bfe2c9d6d25bc8bb8873707e97197eaf78390a080744790bd61d5dece8360f156da94a3a61d225f68c92e2b0da4a59817bf7f81ddcd16f3a20d649f82cbd38e18637da7306f8968aca7de2c16072626fed7cfff047607001a7bdafcdb52cda29897aea74c59402564d11e9ea0edae44872ee60a8011867cc53fa38401594a3d38a32e060d84c0329b53e1d9a8190a9358f4863544efc945a1d737cd77f5c6e598605956b7c9211e4d8f880b2d8053511601d6289cef348e1f9727598a21a57f99e830d030a2db74ca7e0d1cfc621fc81a05087dedc0b7a3f4a302b63e5799e50d3d9de36534afd1d5e209d6756923916010f94fa44f079fd8f6e6f27645bb0abecf1eec911e1204f529db990528d435293524904cc5362a533d278ca267085ddd44ffcc2a0a023425dd41764b719cbe5b5c8989b40def1417caf775738ae660db10b4982064272a70aadeb92dc7720ca6dddf8625ab312058ced1922592bacd722d17566d4d8ef5212210a86c8c5910aaa06b43de1ea53aa011450c12e84bc58a1acf17b2a014a92eee2b8505288b880fcf8ddbfe7da413467a0a29635d3a6e4a9141bffafa63cdfb21d124df06b2aa82585ee1f178722d4de0931e88aeb9b0823ef4a4ad0bc04c97d02358404c8075ccb9bb71364352ac4a9223a55e009f548037d98f8c6f0d53715938b3fc607983a7990531605ff47322a511a7b7507eecf4889acbe8f2957dd5a2a53137b4ef3e920a2769834e23931f45f70321dc7a425b6b131a229a8c4d7c1087699c1814d751c7c806eb22fa120e07a3f7a59fdbf51c00494d1d9cc947b14ef3103aacfd51a0268469c76a628182e6b170dd8ce36f52b329f89543d85cf0d0c5b87ea24384f2242bcc35317d53e0a91c15d1c921657c05e1098611d2fda349760ac815b7c61d2da030e67aacd20358f54c255add543f22e5afee28bbeb969a661934590ecc1c3d8cd66633ed6207c7d8605a2c9d72bbf8fd6cb6652f37b51120e634c0a438938dfd68ddc941519e8545fea41fbcb2e65e77158f7bbce6c561a951b4ee6bbab3cdbb36ff2ff8e1048422aad5099f9617438ede1979361032887ff2ce2c2f28aa01f692116994dc05450f572ab435778731277000f48ae60288cb01609ca1ca60dbb48e35c1a85824934c9a7b8eede5408e3714b88fead02c511c162502bc4d0c8f33da4166c16acc1884336e123675460d171c33f20955c894bf05737a68e3081b5882f7441a2625daeef79ec96b244423a4f992628be30db48815a9dae2eecdab262f1f9c54df3603a419ed5fd8dd7e0353181c3035dfda84a06270e3db5c4e2a3ee695b2bf5391bb2008094dc4bf417547e0785c17ea56c1e7abbf5cafb40f9c428f8cb35370ab5092b38bd2b33cc88dbe85f7cfaff06771e4fe664cd89bda8f8a9e4695f4fbaa5b0bf148c6373dc77cebc0d8009c05031722303e7ff6e4f49660202ec20d6d6924d8b716364cc8ff60c837ad1086f484fcf64cf71f747bd0a72ab48b7c8ff61fc403adde3253fe6b6f8a726b20c9ab2b8efe03e6a22dcfe30f004b2cf5219f79fb881931be2dd5a5236b3bb3413e02548b28ad729b6e263c444c41d42d4162cf4e3c83e1c6985e84939a8c4c9f5995613fb353ea5f49acb808fb872f2f5d2382f451541b6b0eaa46057fae52c9d7ad15b2d2034ca8f82444098cd644d68c4d5003622b41cff80eaba989a54f1d9ee1be8515cd41caa27c85bd30875ef752e2e27d934f9f74d2d4eb94637968ef5a420a63676c83c942fe03f1dbd1b2aa608faa1a1093d9fde94e3e835ae1c40e29251a85e4ed40c2f5b7a2fcf62768650af7634c8153203024dd6d63c3d085c0a8ab720b66f022d1b9674dfc98e0ea5fdd854d48270b796c935102cc32ed0a389e79cb3fe3261537e741cfcb5209c5d4725161d2f4cb199737debea6b7497d5883567c00c967fa432b1fd511822a907ab5019e2d0289ce79d25561c95064a3e73bd54470bae829825cf1278dee01931314fb3ab93e573807ed7df3f655050ad51597e4d494aa55c08775be61c6c62ac7336454bad1bc8b9dcef3f6b8a88107f8c87dd0ac3c0d5372513f5887335905743d39cd66f20abde60bc0b96623dfec7c1d6c1962fa06495d9723946b1318609cbb7c87c1e952fe8f61beb352ab1f0b4b6cb14e970f48a730b69ff7be4afd9000572c6a83bf45168cb9948bebece7098e2b02e89b0e00141ca8e9e7517c3804c3f1d2233509100b5fb01138f8e50cde1414cb5d33fdaff786acd6f64854ff0d635bacff49aee8a62db2afdd05736e4ccd26ead9b99086bb046250c0de2a53b12a29e81b8f8241c33f737b506823c7259e9eb37ce7c50783ec24d858cf5503b8b044c962af1be529c79f2e1de78d64a728d02ec4e9360d7d69d5bbdd9a13e2a865e9cb6f9bf2c09a5bf98e7345747df37c335dea394143d2fec9091d3d3d237fdabbb4d21429ac60a0b83fa9db382dda7f0d444ecd5ac1009aec380b5a7b6264b9c4ad50b67575dac4a28e96686004f2371267be1cac849ac5b900cc51370d5d6aa11aab3d73dc102d91f329a5e94a60cca5740a9e02531d35e7231558b28fc6455d11b6b3e88dd9b8a59160dd646746125cdae8f19ccb41a47d33083e93073e67ab23d4a04f5c2f76d4b02340183720f52439083195a2964f8b661b7fd05146c1c6f783896e3b5cbf64a24aca407f3791dcf76103bf47343720029dbe239d897bb2d66d766a8ab1710ce4a1ca111d11e17ab3f29168f315d41338845bbe3ed30f5c75a0406c1d3755155a9860511bb05e8b5410dbd6836f0429687d10ae29b2171d0c2b8be71c06322ddee6cad1b6b6b3593a77a724184499a59a00198b83e1ea944ae574f00753d64191bb00d69db13db487946f03d97a9aee4b3ec6e105a0c771062349ee4d32acb30cadac76d5c68b00b26e956528f2d474a188a03489aa88744fd630f0c889b512bc993046919a97480e96ba2dd6525bfd567f95c5588e14d5c2c540393d7e11c9438e07e00141700610229138e94f92980321ba75df15de13b17d10e4d5947122e217d488590ba802142d1069ba847f060bfd6de1bcf10cce23a22515b86c6ca7f037aae88cecb0e252c5d76c113b07eb5a463ca95028861a0890bc3bd228cb77efd30b68fc8b134492b42b5a16b0b292a3ba2247d457844ba775fcefb05bdd7771fb79f40bb60f48de308a953c093a5b3986c06e0f0ca3785e0060295a59529e69007c7a688d815deb50230adfc90c3ed1df4a9f65e27c05a7272ba58f5a9165cfdf07119fcc228e0f5a32eda758fc690ef6a65b7b887b584bfbf54e7fb559202c7a6fc628c82cba006ea866cf288c7d4990c4ad44b8242bbf80f3d13658f4f1f8397349b61ae4dd616dc24d0fb3fd0df0e267c2409806439d14807a0ac4ebfb84803c3cd0d92b0ba2495fa4f8489ce8057625be66fd40cc99539c98f7849193aab82767fd2b4badcdf70d57dd86b0f670d89c24a97da7eaf510b6a73a95592b287824eb7cf21563f0f8630f767e80c3b04c202ee36580fee19fa80abc78dd28017a134093722db8b5aa122173ed4d1eb0765605bda2e82347487c78e065a20c52862cab7d059dff69c5f01ee140330163df3fbc36f989f9dfb39868fa608cd0dcf17b9e3bbe44b4bb31b790bfdee40d8f62d1c6a9e8674ab6d0bf11c151d4dc4808ced08fc03094f0edf5f73123f877967921b0529148db8f49180049836fed2ff509ab28b58de46010ecd1603680f0a687626ff97a5ffc01f63081d6063f69385b5c8b40c0c68c1efd500410a4292106afc743ae5b054936ccab21c7946f899678d57c8ea324d84228c2ca0b24b7fbf3354bc8d5697571ce0c47744492a2e906aaefa9bd2ac2084e205dd58e25d5427ae63f65c905c2b7c3c6abe82e8380334204dbdb66c32722f645b8d0e054d27abcd5773280234feeb8c7fd907c568dedd247c2bed67d547dd6fc58faa6486f50f4da612bbd9cd68c4d0b24ce2df10a2eecd4183753131eb191859768ecef0c32a5f9e26c8e85b068c9a922bfa9e216304d37c2c49984d16450e0d2584440f91edaa6ae7e6e7abb661f436758f067a9f0f43ca9b82c4edb5583aef9da02a6d0f520c3655db6916cfd8a3d8de73cb6618ae56ffdc2e16de5e4b3c86881d48934b73db6b47f144e2d42659034479b1b398fa2ad90ecf6bdf22c923de9e304070793b4d32ba7f4f05535aeb3a0af4fe3c90442323b331a5416dcbea2edc590ea359fbaa8982e73e28485e86401edc51b72560afee7c4d79f87d7e6e1703d360707fc01f17f8a23bc24e98d19d9f37fe1d19ddc2754123e878ba26547402e9367359d40ef916bc289f36a5b63bdf336a63f4e23f211a3cd0afa6763ce09237066874ccc77ddcdde214e406275aec9eecc7c85ecd666d54ff08a508af33c958cb1086f0b63ad85815eab4617d34a71f486d835602504ab82ba172b559755e6a200fe09e25d55b12d49ab4db4f4f5d134688b22e755a7f75d6316f4edcd88f87dcb8a6bcc9eaddd31d9508289271d64c956743aff3f6666f80b7774d44f5d6b35f10c31f47f6a420450c0ba0f235c80f6b5078dc5ea1abce0cb0144cf60f5634e6c24cdea5f4140b581448b6c70950995fbb5d96499dec3c12dd1260f26c81e98ce7a6dcc40398b9d1efc62c46908768c6ce60b04be82c2510f5411ac9898bcc04f469e842ab79b2abfc0aa369e9d993f37457a2adaedfcd84f52a127b854b038b174e6fabd668ec33cd8540aff6c73381732d54b5bcb6ceb2ef10336390e0747247032da9db6b275f196bf65f80d0d1eb6d06713bbab48bcef4cf78011abaca99c0df1f237ddf34a5e09a75fe1563e09425e2e4291d50bfcf6b85dd25fdd9d0174f9e654440e991296c8c395ac6bdf03d54827a437cdf8e61b7995adb6f1d5cacfe2faeaa368be7d37036b4d40fd91900ad7ea668fc0e614417bb16e62111cf5be8ccfd95a17d877a6c900fae797fa3b6162824adedf38dd59cfa949e797cf8b8bc5369bd3ab017d2cbb95fd6637117c08582a0cbc17ffefb5307a512ac322d9888ba36bf4d8928241cf69e0f866146accac95aa59a7e06115f5db1c732e7198d5ee86186709000915a5c78030966bd95c6e53e079ebff20f65f7bdadae2348880900c1229433ec0c5ce1f22c7302a32cbe860549dbb89890f917ac4141fdcb2900eb0f840f1bc1d8e083e5d9dbd3b38a12338345581e53068a07be6a1bc605617b87986124952ced9c7d199b4eaccb366dd4338b70248db90936bada8a417ae357cf54bd41e70fdce12f05eb40b47bb8bb066a3e7b3bafe7aa82e1c32f14ec3876d9f5e0b54e66fc603929bafeaa0799547265e800859f28a0671ab0c8588b015dfc2a4149d09dc8ed197e74a58fbd597ce28b43d56d8a126e508db508dc28b8da17a6880368dd884c0d1276cb9fdfaa549ba4096d30b58c41ad1338387b3479b791627ffcd9c542a65ba49232d7f94ef54d18c3fc60c29d0187e228387598f15337b64bef3f3133e8bc367a01df3e65efd14c8373c245195380ac2cb2b8760b8278470c4b1bfb75fcd9e523f5a69d9d962b4695b5f659afe6d24dbb26a0b67b857083f7f0f718a0110eb879fff0c79a80e5f72c13b64e8e206a4b36259d14fa31cf01a80617e5deb4ef092503ee1a01ad90e7115ef89ba7a92c55196d0509b6c6c15918d738fc1326da169974f6b0da7b77da8ca462181b27d75157b757490457220a7c7e93d31862e1230c578f8f11538a54148ac72338db3c11c6e494345f7bbd3ba0a486f151bb72402f641e321251c08b82a2bccfbd4d8d93204752583dddc8fb36dea1a80fda6693d38b3489ac43440f55b8337d5b3d8412ea35480cab494dd2cc3a8bfd913492750a35ea78a25bae3221590a8d5a8e054e53c9e926120c63daed779ca354b86dbdd5d09ed5fcabdfce30c9a53329e8517715ffb89d3a89a331d37ce70673158cc04e5b75f43be68d78ddf9e63b8b35435bc3812844648c36b4a836601cbad9bd754cc0f875158a627e48f6f316538fb309532b7fbc1a3051b2bc7c9bad1889397415ebdf556be1c587513ad8b04aa66e5dccfb36bc57e03853f52e73bf1b4aeedafadf7756fe07ce66fcc8a14cd8af03069a0e5e6135045f0f8830b3cd3e2e5f23b91378af1b2a22c5b4340fac8db4456a1b165d76975caf3d4c263abeff92b6ee9dd9cc379e8321258ce4322bee5b7bee0ec05357568db17dbf4df684a239ad0b678c40147e73faf5ddf2440e92142682ba82b54297ff48f1a8839dc62cd524a62e932e39d0eb2cc7db4254f210a70f80640df0aabcea7beedc496baa5f87d2a3be20124f456a1262990e93d1e850e0568ed0354f4128424504d600334278146ffc052846424e6ebb233bdb1c561709b182ec7a67e371eaefc4a09c650d7785c588455d3e5c6f2f0cf70799278a685419eda16fa0adc03883ecb9d71f53d3a353038b29fb48fd6c4c804b09e8be4de9f0749e18fc80065694ec1662cf44de97f2fe4fa9fd0cc32b04279c3b1a3bbb18924c98b524a55a7c5f4053bb65713d07dbcf74182e8fa8a7cb1291209a58deb47997b5fb5f281f6028515af21904215cab456e90c004aceac3e2a1757b8a8963d9a271a80e1856533c3844253679cc8f495dda6addbc98d8d8f0b1903b797e6f8d6736902e535e5d7084de8b98a98a265d60f091a0ff8f0415c654877ded29c9eeb95b9aa26abb6c7a21076ac8db903f358e94ce38b0adbe7f61714fa8c028340bcf92b80126181f30a082d88ac0a07c42fa4435ee0dee6fc6917dc0a0ad0db573c024d1ab868d72e876b0e428624964c952ef79fef55f85355821db4fe1e1136dacd26825abeef7971ec42509da3370ea5ff31971d5590e03de35096ccad299602a55047c8ab3485d41563196c5b4d3880ab605c73eb5eb8165ff5ee5473820e93e46d42cc71f700982a32ecf64ff1a565e58ae1a4649dfab17096718e6ff5df6dc059e9c65c4538072336a6ee1ab29d9138d017da1971335e8a71a433f6d13781bea4c80279d2656c0fae2b011198cfa13b0989e228fe2a8c29061d85155d69b51814235b03427276ee228c79a9ff8a3005e3260a942392a9b62ac89181456c40e9665a906e6ea42ea99d4a632c67f6c7958b10662b01541fa53b2e865f3641515d2a685d408db977496d8a610f8977abfbdd27d941c2add24edb67d0d86a7304cdb9637b75f1a054d228c600ff5466a4df03cf5a2b53c367050e8e7179be9e76e8946b9c3917d146c43a78af0756011af07245aa3d74c84752f9b0d08024c532b077221ee8e695d66e572a66042e65d47a505dca98578c8adb02f7d1c2a94c83f459d58f5ed936a9274805dc4ec5d29788edb8b6818d3108f0b73f733a623b08c15e176e8a40cc179e8371f04a55418fc081e5a9e9d2dec90b69cfb0ede5be668284e337c7a6cbc3209698fa366c5c413e3bc0b199c31409c729abef337ee7073b9b1e529d3ae0418054371e9da6979973be3c56b36e12c85374fe238865cfcb55e5f30ba80ef00d2bfc2c79022c8fcc31cb137c6a878f9969921d604ae98899e9d519c75d95171073f12adf00a2ae17d0940407ba4cc0cee2bc82ef564a87d5b0701df35e417cb9a113ced67f0af049aa5953c3aaa3bd40a5a8d56c8280c0b12d4f0a3ffaafae3503637c889eda78e7faebe83bb2b7c353ac04ad8f36c3d4e587c5ec1e0d63c2484b0aab8b297ccddaa1987d96be1209facf100166d11a7d17203d6950cf1cb3bef2ef2d4039120464f7f828f3f7bb99bf82d170ffc320f5f5bda400fc6a7f300e446033bc5f7ebbd4217d853f379fe1b197a3e1b3e02166f4d54394fd3a014b1ef6f3eeaed1cf8a538051b192df38f161a1afda14768b792c609e0b612c2826995414413f65cd5c6f827e922e8b8d5cdf5016dc62db320a212aac3d02a4c3cbd86b6a4381d27be74b4b718502e89886aa485784f20d6eb07a878886e2ced02a9f34d42016b8114d55eb0ce2efe413d6f0336ad4294fa6683acfcdb79520688fd7a2db9fcb9f3e430a24b3ef359467b858b3ac4aae79ec836c62184c2d666d2b1dc3ccc21db6dfcf566689f87e5872e7938524312c563545c8c7c187124f3dd1d4bbcccb0cb654d32efe785d4afa96f5a6ff9879ad1f93bea579049e46edab020ab9e30a9e8e9be68f76e2bc02c51b24328eb2a6d0a3bcce9e4625b0a9bab52b203b50fceeb063d898cf3184f400447443eda016f1874631edd5311134d726608a4523e091b7b8b7eedb22d9826274cf4700aef3f177196bfc1f92e1042be2439c77b4bbc1dee8e5ca28a69448a9014a1a9fd2d5d14b3103bd7cee397fa9d4e34f097f3491a49d50a5fe66e00e92942740839994ff9a88db1c00b8e28102f61d2e0a37e228424a33e8cb64024d34a8de220cfd2c51ba0091c2a227dc84714a60f0f9e4a0fc04cc852542216c5a756e33c914e18e3a297556ac19ad2d796e633de97daa53ed0d66ebb8153f8cd2fd01b197f9c8f3bf89805c694edf192fd134eb942f37820971df1bf096eddbe609563e7637891ef1ffd8c1f557afbaadc7de1cf4545647ce41e51444591b072431c4ec6cf094c2716c365ed2498a81a44f4f10314b15927dfea8c012dc089549f11800b76680a4dd946003ade35ac309a4c420a7517cb1399d5f8cfd91ce8e8d92dcd680b66e6dde5902a4f86df016007d489413689c72c2d4088f5535aadd953295be45b2edf2282593b14f9e77cdd4af04c2d7473b1d3b02ddf76169695ba6320271a1e0d33a00f50c024622c7f6010ece88e03e0e238af667af9d72582dd973e84bc5477593c5611cde8d79285b41c7096313bdb1431520acdae2c1368a2fa5fc7e2012f1d23d355c2bbd319471b77a22844af1566f668bd54b52b4500bd8887a037a843b747bd46dc97689bb32b00fdc518add482328b30461954db7edd1b158a3a2d97caa11059a1bf397ff81b971bd24e8ad96067edffbfa3093d7e9f34639eb5551f3d6e9bc1743d8d6f98a66c9311a14a86f761171f931200ce45d62c34341ff4f677a24b563309b3441b799c2d66b755ed84c571ac1a1747a8e2d460ae239814c142b6ef7d0f1328e11b620b956b5c5996372b9305d001ff4bbf6197fbe40f62d19396234191ad09cd27dcfa0a1410ece3a7355b4e59a6ec71da467360f0a80b9a47fb212dda2a3b2823822511289a69f3a48f6b913207d55a2735a07333f283287b4177c55d1a309be5287b23b94b4f30597ae7a843797dfc04d9d32f3826138f772116631bcdc1f0f252214c0be07a269d9f92e09071221d12e2f4df8e846d6265f78dcf95a07a6a66b2e054cb1f401ad7abdf81bc410532741465850b06d00a282c81561daac2a7945894d2e9ddb80ec7e84dab9c9444ec9a48aeb0bee07eecfb87b305762aefe45e613a62b2e1b5cd2125b945bf346aedb4f1dcb01ff20a70b9d55d445464330a0365ca78ef64c280c25e0f2dfd1f90e9b3dfb8255db412b2793cf91a914d66ee574d8c5481275f2ffc0bd20907ed232e40cf572f650f3b9b20df3c1da05cc511eb1cafc01f3460520d59540608d83bee506c9d49c5dae91f91cd4bf6af4e7ba7ca1913e0e3d22f3fed6f9fba77096a4bfcd09b47f26f1ff64785634666c682e0a2113af42955a70d68095e9567d8d65a7af37ef9d705ad91428222b88276c01a82e425c044cceb2e4d950d3109d1d1f0497bfa5308782de55e93648329c06aa7c4d0216142a4371d0ca5e10e08afc4eda47d343c37b63dd056e5202e3d397c0d212e66a5f1d50406d8c0eb047a8727d7aa0a6f5c24f27e3be894a262862960318f1e3b8ba8a44433f13932945e0108d3ff43439f5743501b415c76d031acc351ca63299f8987ed37e74d7b5365d9057d7fc72a5f8baaaf0b107b854887b09cf2a1c25a684211017a66506fdcacdf4067505129a0c93cdf33143f0cc6615644217bf6c9e21138174877b948dfba3fbef1ab9e99d5a1fdd4493b98040f186aedfef02e1be02e6ee091b958ce8cb0165eaf629331622a5c9705475faced7e17483e220dfa71cf2ae6b04bcf9ba76ad7f2fd8a07d23a8dac6a99ba8bfc929b403f86c64991af57d8c68d8c820645189038691069634454286100704555d647db9828d0007fe4409c9c35e1cb0fa283519dbd3bed45025b60a8e83c487025fca91d8e3e3d828d9a182ff331ebf1bf5a7244fe1edbbe172ea0af4ffa4445a91d168896e42b9f108de91866970c31b6d4d496e00d6f321c933781981aab303bddd364c6ee14978f6dfb448cb4882843516417bbcb6abb0d800d812d2bbdea9d3863af8d9e6af982d0f4e95a1d53ea25534bf23080f6077ca0a2579a5a810dd8615d6ed065d0981a13d10e0215410049c4f949dfc343736edaad8a42d443d0b0ef5b0a342564da2959dcae5d02014c8de1b16f15c0c4db6129e6bc1c1ae74efc756d3d74aa2eadf1236ba95ac256450dcbd78111bc9e38c0aa0c989f6c0d11169ce76ad9f1603eda75de1a7994705748b07e2fe33f707576c80983a3ab82d9235e63dcee29eace753bd8d2e766cb8966be84d9350bc082cb4f71db2e50c1912acf8766193de087a5cb51ae7897a423429b350e314fb57d0fdf3b7419d72c257df3587c919b6dec98ef14278a6c0eecdaebf830875e591f4aa98d9b15c99aff07071b1a98938496a230159b9bcb091b064d44276ac168268246ec19bd67d56453181b12e453ce92666d5ec0b18008d0f925f8188354108fa508834cfec3f84fb836fd40b3b6ca1d7915beb26c88ea44fc9c7de840d8d65cb149ac6bc1655923cb48c449a36e94c256ab6baad13070991588e4e61aba968630df9cf5783a7a0991a8593f0fc7f9fa01769feebb28e8dcdcc77b10a795b0650b956a94597120825018daaab3023cf6091bf7a9cb98ac96078f008731512a3caacf61e5f764d8a57acf0ee61661527361ead048be13be85ad95733129ab05e4d521a1da81f08107e2bfe8bd92533c9d8a756ecdf5b6b9619430b0fb5e4684d0260048bba35ef5daf623938731956b3eacf37ddcd8e268e3bc5351aa6b85367d7da0c0cf37fef0cac309182b6571fdb08775d887e75489865b6380c599551204a0a21e51e7cec8299a31180f4158b573c5cb6d3a8a3bd61f668c36f7337443d231cc60778b249ed011f3c5ac38142cb3e46fc604d40d33084180a7e4b128d0b5c89e25c439179318c088359b3777d2c4443159057c6c5043471bdde99710b04c10999538670a290935cbabc7a2129441b426c046d33421a35e9168b8d4b1b1c6ced3adaecf038539611e5a92bb4657cb937e775325d2e0b5953b448e7b9cbe586af04d7c1aa4442e8813ee399c5fa3a9f58ba7fdc6865bd27df63692294d041a158343aa41f4686de6c3e92630fd906b4c3b16cceb174e2ea7e58ebd7447d3a3d11dc5cff66820d6a0c7364092aa0be5bf0f8d6923bab94fe830956799e47c1c430c9bb6b6dfc9a649ec90baa78b2b359b288af9334259a3bd8c3437ed7c884fb94a0e5eb7ac86471d57f865326cb15618681198e001ac8822833ea83b2095fea41c8af99be51ad2c97308e88facd973845286a98a592e86f5b93f2a6bbcb72ccbe03c7c21009ed392c86cb1f97b056bad56aede49fe6489ad9dff61d489f59571c88fb64a6d7c2019ae3e980265cb43a44e8da2111a8258140c31e93c5d14503913a705a1c1e81edfa348e06d9c28b4dda02d98185868145cc43d72187082c388ace7e19ee9e2b30f4f18f25c9545830235b46ed5838ed0b2b8193b8058cc0bf5abe9b3733a735e2e61ad6a11e435ea32f77530455aa65c06be6d1c74b9e9d385d4be1e41856f62df0cff507ef5f99717c8c98ee151f72a828472a17e73b66c303054cab0965aca620beb0a43996cd91427081d585376c4f04f58e5f74387aca95daaeb2130dbc08acb4d9b81a4f98eeeed61c050bbc0e59d0a18f9927bc5170e403da59663b295e98a1797e5434df7d8cc39f785b7492fb31a65b4ef8b791f26acc252111837b448fef81b9bf0e6d24b9fd718abc11dc1bbb103a5643d45d504b467364907c7186df40c585352abf72519e8c73abc055a8e44e4d0681cd89dcad907254e84ce83b718a1b643a6b564004b9eb36b7340e834a08ad0caecb1f2576c400be6034647b248d2bd3f8a7c6b037034edf8fbeff6c3bd04f3e952fa3d0bd93673bc332b1abaa31b2ac2792fb742fbfc6d477c02204423ea1dd55eb3a0e9acedbed6f182dc80747f488a4d9cccd280f6355226e6e0473f3805489e77c3ac8b832401011f97b6283996d347e97f98d8d639e1142626365a3f186eca5da5a39712ac1ff366e63f74e8f5c4e13f40915f3e1fee2add369bb880ea668564c2d1748035a13c14f42f65c88732a26859a8fcae53f0d84c593fc8d9754df6639d539cb612b90e9ebcb9b0909caf258aa70fccddcefdfb7e12bbe4d9662936922e31bb927817be35362efaafff252f493f97580725810213b3af048ed0b1089729f1705dc2c1572a9060e1314454b57bad6f3505cbbea99fd98490fb88dbaa56ff2221f6faee10835f6e4584e8152668d1715677ec9c70bac658c2dbae6a9a8f01e6ba59b85df1cc63c0c893e04faf53af156a76bf77c707478cc177b71e5f90f9cb422f1428467b6a7c812ac44e81a38c9cdf192c85cb74c727bb016f9e40385c5114e99bc48d5cc0fa09cd763a9d80b5647ba643b0d85d1e288954c35328f1647bae4a7915db438f2f8e3f6d2aa88718ffcaa62af6cad51d435dff35f2d98c61ed1ee68399e8afdbb60aec14c7baa63ef8c38b03110d8fb44b1c5e9b293cd10f5bb0c7c3973f7ca907fdbd72ff83ff35011d0d1029a63110d1702f9d7e61827adddc5b1068d4a3879c252e494d88c7c0e1a77a7df12934e405e00f4e2145990404c44e284bed917c65a083982e55463d73ee058051ed2b5260c501812236ed8137268cfddf25e5ec40b74cacb8c2684c0df7a19fdd7df1346813994b61fb52800d5cdc878571b448da9320894a8269813e96b19fd3ae5a76a4990fa817caff7b9521724243f889eaac25cc377d6df64c9e501d8d44b30f71aba685f85b1ae9857a23d133cb47196b807d4ebb8766936c28bd58d5acad1ceb31cdb59a6826f25cf866d430c3b34af96d174df7a6dfc252cbb0dcf5fc15946a3b3253380be242e3e40079cd26d11c9e730ed48043047f81bca6523027f88a364ecd66b2c083a3dc3139907225dc66263da3513e49bef5ae844c9e4ba4abd3a2ef82f0c31bbbdff516f9d879bba8a8ce59133464d5043e1b1101a552c9f29661e6cc3031b478bd4e1bf7fac6b79e6c09125ce59988a7d1c1b7c278ac13b79f275fe53c8675a7509e6bd4ab87674206cedde3e9af682da4c76bc8b33292013c4ec41e5cf0e71b5becfb528190b58832fdcdee718987cb6ab679bff3d31809e48628a7269abf99e4ec376cdba18aadb642c990f12d3a017e0d26c617db836d2d0296a36cd4fc189050713c7bd7963750bf1059b73b1614aafae80bd0c6e2552abbfb352a34f2639499d226177b22adfa30429c14dd992354800c4b451a374f55490bd6d02d03663f3f0da91fb52c671634ceafb7b7f3e7499c3699c130f75afedc1ddee42e5940f73ce5463f96b857f0073f31476dcc42929adeeacf672f9325ba53c189fe2ebc162efc1a8478fdcd805796455cedf8ed31e87b932b751b006a33246cb2beb94ebe44a9257a5983fd1a9d9f317d3dd10bda11438323283335bc746ed4bb74f4e3bb98d559d000d490ec843cfbdd7587557d462f09ed0620f52c5c6fff7c54e4d593c3341f68405791c1e9b01ca233f029263ce65ea386b180f1e7a9cc5eee69154479bd017484f03269e4cd7f60226e2801227012f713d6abea192cfa6089383c856b4ccbc3c3e3845a507a5051dd9be7c885138d7a1dee1dd6afda4d84aec3b7fbc31c6232af11234b3a0c95c4e4a4e5cad9b56b8b325d69c257a037f38d293feb77a59c1102504f5040612f6a96cd6bc5ea000a626249aa85d0b0fd854c68217e9db8b6e0b1fe833d13f5fbbd6b0788e64961dca7c6a9a362aaa58d508313cc5fb368dbb57f48d9921633a92256bf49a13e4c00369f7dc4566a66f46732945a49baa75198abc50e170373ce3a381cb3bad4b040285654572e6c8213e2d2b8b4407160d4c17695a5d5d9d653b16a12e39ba490ef49ca9c075f2e01c701f725a5198095803ef4601ec5432026c8ed9d33dc38ba021458c24323bfc2345e006b5b029b0434f28e04280ed1443058f53228aaa314cfd1918c2abe80c4aae3f5582bef8576e4ca7a78912fdf9be2ac9d690852cd504e07e59c8faf35bca2a785417339674292cb5a19659a8a98ce9641c48c35dc831bf61d24b09c78c2b67abfade50ef405c288b036d6e4cac9c1271411af959235f602dc3e4e7e882406110f4e47555b44d04e788956e33439d81f33e5c318e1c156e148c10e003d6008641c26d0593cee1282e5acf7dcc4eccee4ed4b36b48ab3ed1b274046a83a5f102f808aa17062dec3324a1e1e6d415ee1e2257f3f93400cb5a6efc1bcfc517024fef04e0a95b3da94d40aab810f24943854a10dfb89b9cf9d0d10fb68a61085800ba14581100af53c3c326fb103c3c9ff012d91db2d44c31579f29f522175a470c89e8adc62bc1a57113839711395c94188003edf748e477550387d636b3f04debf1d65c1974b39c75202235b2ce9e5c5743148224632df292b88bae45c77ccde542c9ca16864ebfae169e04da5a365b1714c72460d4d4203e9385ac86be04c99b090bd2a071e28570b2f71a360324c0357adbd7fcebc8d65c1693c74956ea85bf6e7d823c0268c84ce6a57c7b48e23205e15bd2a68f43606ef4e8d3fc2a1fce594df950c95717af73c00c845b10e3e2fd1d168bedd362759654d113902de61c25859292b152529cfa200dfdaf6161dd96585be5a6dd9ec6e5decfcbbc5f78b99b18b9481fc8db9327b1a384991109d01a804f38756aba10baa0324c6487e339fe2b0db6acdc1096c1051cb303af0c8278869ad1439702bca63293634ddac1856f0147b082fe6332ecad08829f34a72cbcf969c5a24cb1dca073bde74e0cfd183631dbdb1870c0ffaa61d82f80de0c6c4d616a3117574a82809c06a4883ca63e0314342550efc395355540dd8bb8e49c844818784274bfd86465350a8987586625e41bcff984f6edbb440f1ceb42edf8c759aa184f3ee9fd0f06fa6769f98062978f7438d36e72e55d9ab190ef09b27037e477193157ae7b8cf9e808c3bfab668ec1acaf224cf21a41ae7068a34c2bc22a518129f7794f966369dbaf0984c3b4f88d070ca32d4b988c3a18d7bef087233d6ec03cf20d768d41c5e1eca012e434aa1412d08026a5de46c24cc370f8d387030657ef46d4e6744f342070c77771be046210bda3e075d27a4626f16e6a87afb45c95c524aa536f00ae2782aa0915cf5e3bb038159038a58ba5d1824cb04a237d7a94a842d3bd075a37770c7edf0dd11b20a17fb12c05a8223278d57788109a721b8052b0d3cbed9eac8c48e4ffc83e97bff6a9b184118e2c09169e79e16881127df462fbb4f581099a6031e7d8380b9f5499640dbb1f1a445d02d3e9450f96f9c8a5b43eb4e3205527da9fbc799eb5597bd8c761808add79c2e59d0a2161e02f60bcb96d2013f78d97a519f5be94a7827ddae8f6af915671ae50716b64098a2d9b0e309d7158b269248dafcf3666c47cb08d52854803e6e20ad4173e1d7a683d63b9b487204bce22a2f18a2ab87f0a44fc846a3e4c728c2691caa226263b1fcd6a821e279269d0e38ffd30d852947008a180bd1ab7e431a8fc83585c8d8968ee6fb2d6daa3f695837eb558064ce81d3e4f23362c7a30b8def0e1cad6b527ada0df038ed347461d956e7fcd60a7c98bd36b3021513a403c8148f181c03d8fc3f04cdd2b8f8e4124121c408f9f8cc945ef9169c0c8bf072c6826823242e077f2325d6dd0f5de852dd2af1fcfe123cefd014de7237839ef4ae393752bfc43bc6ef06a2339046561c81a5026660293186a703631fe24a76b13323b1509dcd7be0ae1cf6c6922f8013756e8d71108f02ac42473f21f80d70e710e3953f5f1690e22bbb3b8594837409e3eb7519699ede13d5ca6b0aa33765c198d4c0180b2cfbd743afc36777a346276e9fd27c0fc58e10dbdadf3cd45540204d718cd52f8d87313d44fe9b6e9993e519127981878a32203437695f21a5d421d01a8a5c68de7c23042494758ef5d344ca8c3d063d4cb9a570e736ec3e824f575e5d59093de70b271359c5491b5997d2825a5af0112b89a96897d93dccb15ecfeb0488b83d0f731cda2e920e388728daea48b29a32b1adf517079f3345fb296e47365df70aa9598a735e317e060f8a9eeb5c2bdaa2372c3bf5bb2fec4059703a62a0185f0cfa94cf734cd26ed49c06f7a2149d0e7eaa628f588e5d5afc74815b24c04d3e0d306e0ee0a4954f2290d94e7163d5722252b18561f1f5c1476d6b1d75d000c0cf8de0aef25ba24d976d7a7d128d4e2f9b1b41e6d54d5d5bf9397ceef6c311ae8b3a08fad0dcb013a1424a22a65bf7de311aa487bbdd42e7d12797fb46016fd41de6effc87ea790049b86cd186e31c56c448b8554c416482c96d423bf7e449250ff8f1f35d52e07b3188ea7379df6ec42bf30129fdbe06e08eee3481d8888e43ee481bca4363aa6800f53f4877e2ead080625b3e0bd0c71f16640996830d79e5340e91ca80a94e565d94bf4c8134504c581b878cf64f3e87f979ebf9c6a8858786e028568580422ba525584c8b24ea4711b217034fbbe97b3aa73824ddb74df7baaea1fb54b7cc6659acd7c2df81ee5b88c1e7e6b93c82fb04af84b5d583e3e4cb2ebb9a32191397f13b648140cb165fd0803256269bfe1d4020e0c01cdab84f30c5d2b11d43a57c7c3ef5747814508607ec20ca4645a1a0a3e2445d96ef6dc5f7ec5c7c73a9caf42252370b34617b92974017f9924b66764b4e4ecd2fb5814afb2dd413a8a9b12183ec19fed07b5d3839a2643229051139805de8821b962e301161321b1823398e7c3ad40cf155bbbe254af605d610aa6b9f2635b5c065c443d515fd49594053ac6d54ef8875631679637d65f12cbd9542812b57722090da030df62683e30095887a65d7f4e28219f3ed27bfefdab21a31b1665fe59101154698ba9327ee232453eabc5cf943d7e14e33ef703e4b00f891b0e62579567ea8df6063588001fc8edadb34c76cc8f49fbbdaf152e5f60e11285d13358869156283211fb0dd002962200707290494e6a6d07fd22cf3888e40b44d7f8222df9eef51ad11a94117a706c241cbf573f44f6fd502fc345b68cd12ed202b8b89d1072dcd76855a410c889923400d1f68ab640c0ce55e27d8f1e5dee76480523b45abf216863e158ace23579ac4fdbf069fab8476a3afc580859d170050d2d39086df7be33bda9d9881e7a788de0dcf7bea40905a6bca36bc2d04dc81d692a5e11a7cc3a6a7777731c40651a2b5da7acbcd00680c5d476a74321c9fff4ff3085b7eb36103cb0bff1e9b38d0e398e1f9d67c22288c1257ceb910d0f5942b26aa0e31b08bff4c1aad1cf151a952ad91ac6889b2a79e076ac00df04183578208305475e7453d6abf66c03ede7bd3a9bcbe386ec8b3e56e5b3643d97164a06a4e1e04ea7c6b945071fd73c4e12275384f4fbee39271612add7343edcd19048e3dbc72cf78b90bb2927d989274122064d39691326317055439506bf101c5c763070f87fc5cd8233b1c229fdd02a64bbf2792b86d864eccddd7c21a133846ad737deacbba40342702d3ffc1e8b83dd2c273e0cd7bd2cc7691dd290118a64c3965e1615df8bda12fb1218f3b5a0c9f0f79153f4e4590436f6311a08ee66044bd8b8a534c674385771d0d513fcef14b43098489126497774c0ff59ddd5de28df632ccb1a353b40ffa1709a40a073b303ddb557532422aa850e31949a768b4e7603cde96d075c87e492d291418bed49b96cf69e3c651f33247a5e9400c698bfb665b3d5a4addecee71c0057c84847a65a4b0af2f42660d02ce4a8a8c907484038ac40e43092aa4d050bddcb00afbbd70b253bac8233b753990da2223c17f2f03354f4778c8fd6d8d8b1f43c2b7c02e809c0ad65822e5b1688c1f70f0685d0c31742c2886793bb122ab3cc047594d387930b10adfb88ce10d84703ddb75f393dfaa24bc903643b889611c7b03d9b88faf7060b5c43ff8464a42514c8237f1d8fb11bc6c44ddc952f56954350945a02a4fd537c9caad27f120bc8e3278f91bf2b68b998991b1955f374dfadf87a978aacf0fa253743b735678f36bf52b1ff1783f92589e1394568e5c7ea924048eb632facbb07aff3456fa96ce301c4904173ec82c728007a2e7b9402e3ae81b1fe51e980cd41b5b83e079181389507cff283a610c1d4b0aa06641344933bcadf5f6248acdba454bb054066aa3aac8f40a4701a20ad8594a6fdd14a6db03b961347f407058cce7c18cdb50661b842a081ccd7fdc0de7a0f4428b37fb09ff1da08e912d083ab20f3e53fbaad162aed0f8bbff263bcfd34978d2a78e08efca093ac15d8011337bd7617c001d4b5c45253fd83edf9151f59b9b280dd531b3a560b21d2aa37c77c9d25b83a343b993740bc5e568e49f22546a8599d622874e276aae802166b2583a5b5503cef5ae8f1af4c6e5e1314be8ffdaa512705cf7008f849a45cb4c473585e6f892c06f620954f76a077b3c92cc62ca368f2de1847a8b96d7eee47ec584bd8514a43f8ffab21bc07b41104daeb82995c30ccba9a0e251d2a102168e36481dce2c0d163f09bf18284e080c6488505424ba5c256ad2ca04381c9334548b60574387598b96fd7f1d76954156f4dfc445d8b88ed585ffc2104c7858e5cabde0b8da691496b7bb735b9a594322519d9095c094609453b64fad3484b9e93274b261268594af776caad6e49f5bc77ded7bfec7c4dcb4442510f0da81f5e8061ca0b9313198511605831005330a165cc172699116392e4f900f0e1872ac44801922d26808225082856386fd4336008c0194c9c0126d7c7e027063450810214966e505a413a0d71c39c73ce1e6f68ae7274ace8c24486972a5670c40a9acab0a18c33c4389921e50c1472fd1ede50ad2408a28647ec124446d1d413793e8f8e183c5480058a29a35c7f07930a78c091eb5f20450aa6b0c01baa1b971c1480494111b97eec36f9114956cc39e704bda109801e39cc914222d725505084822272fd1d6fa8ea3821b50206b9be0e6fa85a219ca0c97133986fc7c832c60a79be8e37346f5217343806500031a41003863c1fbf5e4c4a00c316585280c20a098510355b2b7e07bb5b8c10d4e4f99f373473c894084396411846e4fa9f4385eea28ee45adf460d91ebdff0866a17f50421b07202a65cdf863754b7271d308ec895e96b784375b3628a1f2576f80835608a57a2162f9074783145982861b6f862875cbf07e88b2e72fd1f4082e4784375bb225a10ee00a36403cc152610c204b55c1fe60d551d1474395dd472858a92ebbfc010e5fa34b0121266b821072eb40c219f20620515ce1417f8708145174170a143aeffa242b48af7755dafa09ba6e2eb17242e50f8c224d7b7f186aa1135d4787122cf7719d9e450e143835ca3960aab61b8d428c68a14acb0a28b5cffbeac3023d7ff2b9ac8f5412c72c8f57bb01893eb03d5b8e4fa3fb21022d707b294450a725dd2e2895c1f842d74c8f52d90812d98727d225e6878bd6aad482899894133edcaf34a9729baa45053830e1722b0105197b8c872ad5aaae821572c5e86115dbcfbbaae976b56c18214797ef73a620bee36518124cf5f7d3184956b9b9e92c8138bce9309b9d65a6badb5d65a6badb566b92087ae297c9862cb0979fedd52459e4f5f3f905133a3531679ce295158b5d65aeb4e9e45042db1f7058ba2498597164734a2c832bbe1128563b7486293b76d73bdb098820595d78d422a494d97292b6437d784992b8b231b1750a0a9c2a866861566b67072452992a935d14c79e1621135c3840b2d2e453258acc0d262ea66f922ca66191343b785118b89122e8b266f04ab2b689d9621544f9c653a62b718ad66d32ef1586cf2c4633187f7554bb2802a9cc25861015a142d69197364015434a9b0002b8ea4a02149920287a2055cf1248514240be082f4399a4c6dfdb295564aab8e272e8d1c9762b872e472392ec5c0c50a3184912403d113198a7ab85549d365d174b020430e3218b1e446278ea0e59d2519a05892a1cb7dffc273bd9c80b224d39f5132998c05195680a48b4ed470845ad0c2ad39c18223eeab091597660af874594a607155392e359122ff52932b4ca83a4930189d48622ad31c645cbb45cce8da1c979a7851c495c971a9c91832382162e2ae725c7292828e14eecc71c9c9121b392ecd30460fa782eef60ebcd0c20d2fc8eeeeeeee186eb6736d312846cfb12f6b73fe25c494835105827e3ac3c9184252b6a726b8ddb382843d665cebb13e2342dd07ab5f2b16c27a0e17693deb8b70dfbd11211ae74cfeb45e7ebc2dfce2aecc7bcf61ef693ecad8ccdf6dd24209667e437ab2bf63a97463c60d6d035ac8fb8ef5716db749eb240eb7928c612bb7f3be48eb65dec345baafc1600d4c9c47642d066daf3c49c8128757929125966164f9f3732207392ca5c615d785a5918cc999bf1f8258cc602040637ce0032d5c448a90d6771f65ec65f007baaeebbec320f88005eeb7f0c5edf4dd87f1098b16b64464cc7bcff3dec320f80057996e7fe8442d2ca50616f7bd0fbb080bcfc718dc772c25077fc95509799f0ad75f853530a5d3978bc3885b6bb88e8d3c87e0affaf9e1dce0aade5ff5dc6c99a3acfdc8300ee563c70e9dad8ba1edec6b2a064510f42efa916c21f9124a3a5296b827cfa2e8d2684a1c76b307190b5142f16f0ba7466beed3126ace6613ffc148618ca498299b4573891f492319258732cad1e752554297e240f553314b563df7f50bc17f72d063f2064b407dfa1c547f0e749e2a2712e33e2a1c420d679e218ce0b92ec0e248bf10fc371a427d7ffbd14b802a048b00bc7909880e4aa9fac54c9b62507de95f8b3c3f93c8634bc41f9985962b57644d8350d67cec78bd646454d39f1479d8978831f95934451ee9a59f6a2a2b8e43764327f269244a9e1ff3c4b52831b2d6535ee38c58ff1870f800e4a5c28d1ebee1fe7020b2d25aa794524a29a594725229250754f5616cfaad1be2c2ac94db263729e595745220b449bc575c07e2df06af136ef430941b46a60dbaa393d2de6c383d95f3757c839e5a5cfb405cdaeda3106c73f78a9baacb7be50c0264851b9970dd95ef6d822c4109b61792bace1b6a2d0e7abb3bf548bd9dce0c8c9cb41c4d1a35256530dbb918db5dab238f1affc3c01e5f7212931d6a7af48064d2069bff74cddbe994d14a14145d249ddd1435e53eb2a1c254713159bef5861c07894eb20271b3d70f241079e4ab96b81209cba317686e28f9e3a2f93c8aca55ad7c48f58ae72742a41f1a41c24074caa111a41cc2fce6def5c1fc6dde8810cd07f3a22330999f79966c72c1904fb27cf9c47d4287923469e4a054d17db9c47dbc972f84e65d2ff33580e65db2fcf120dfb12bf9fd9c8a741a6e3515717dcdfb27858585b8de0617b179d7d77c456a9ee6af1897bebf74288fa202459372b0dae028635d83a3941bcd93311eb974d93d329c51a8bfd0a964f93964dc501ee121aa57492877a92807a5fb181c675cff469247625c9d12ae7f9d5df9b11eb9f263457271686404528aecb8a37040b961877941b9f2b54c18910c4b40e71fc8c854ca96c1acb8321ca1e6f72f00cdcb775acf644c65853e31e2b8012021414145351d4509239dc8a14b1d4d41426a922f9f38b9d374b8f2639634ea94ce9671b21286caac84f2c88a54b7d42487b28b4c924c5dee510cdab23c8a729fbcf84f902c2e618162924eb24ba52d619dd454a46b4992f2a89564902ea16a209157dca71fe93aa883918ecff151e76b212ec77795acbd5729c7176b2c71e5bbcf741a89ce21c7e74daee40298b4d3f94c68a1300ecea8e9f704b7a1ba269b24b9928d25f746de799d0f811e082e4ebcc0c504a83184e6cbaf6b2e80f939e6ebf86291ceefe0225284e4f81cb888fc4d8a101daff345e4e7f8fb51fee8e022fe3a709431d6b33ec71765ecfe90b1f96ef3fae834609f63ff7e278536bce12b13c1f11dfed5f7aaff1b4f3f4aafc6b79e88fc71190e1cda0f87afc7e1dfc021986de01a3804ca3fe48f7b7197cdcfc1a187c9f36138b434f00b872e26cfbfc1e1cb06879228cfafc1e1bb300d0ec1192c83c39efc6ae110c89540599d5f2beee1121163f3ab402d912796dc4991677e9ddee4e0fc39abf48e945575be28e4440e4eaf1263f39700b1e449c5865d8989eb8e83882b8dde5949465d6ef8b1ff2a03c04028f7a7179e73832f8932461bc1eebb1122d87d47c4ebb82279f52dc580b2524e282eea6c51c6de5e4e1d392bddbe5b750772b95b712b1528bd0d883ba3cbfd2d3d9244942a35262644a13f29729f965522134d2abb90ea48460a555fc6c9eede906398fc2f7706024b0e5545d51bf24ee6606fde90e3d8e28676ca4e5931d6be5ecf7943931e1539d81d11d20d3b5957f41a81aabeaf2bea64f4ad980fcaf673fb8ec83b99fb548fe2b1d9b9937545ee13ab4a36e52086bae12dba45ee538418395415d12197dbff70ba750d1c2e4d8e5bb68c206ba0fd48ca7268a537646be470433f72cae176d4e3847b73e5aafeb574433b7593c4a54734949472d8c972ffd1a447b9df33923f5b918cf51fe5de68b93ff490727b49b99f4611eb4b0e3d2bb228748b84b8e1565494fbb7221bb7a2dc5fa162cc63ac50381c52a1324c7e14f2280e937f24e6fe94e4e1bae34e8a314a96c450b6ed3b6a2a777f03a6d086ed94dc8eecd496d4b84706e972016b2f2d6ba5a824d418e6452fd418d64d2e112b640a3586f513b95063291c44107463c08893a21cc470e1c315412c49117534a3a2d4b049c0095210484b556c01430b162b284e0006892701133932753ed0b8187f36b757c6a8c521fd4ae9eb65ed638b438c514afa93cef8515c8cd14b8eef4a3551b76337998a5a26b65de692f27cf9752698fd5eb38836ec47fa21e0d0beea6df521752d8675509f6e1d1ac9f439efbc0ebed0089df282b813417ff9e188608f18f3ffc99e43c38716f24608f3733831ac83cff53fd74975b044e83179e27f9593ab56d56e5ceb762c4f86c635f3381fbd211a35df3eab5d754e62065ef4688841d5d5e3a36bdd97f9f845dc87f5f17be623cdc7ef27add44c34c2d6f28adf556206eda57bd87cd84b3ff149e0c2cf0cc949f48f42d481e8df474070086c890ae81efea01733d8d13dfc77d830176af310503b281a384d5e5e37b89b6c70e85e831baa9110e0b9c6042e9a9e4599cd198f95813e12f552e6b6820c6179386c31de6db647013d3dcd75d4d7611c7429f7b73874a2ecbfe1b0c5647f2f76646fa8c8e30ffb0eaa280639ecbbbb63b7fc09c31d06aa4a4375e1331c9791eaf999423019ea8e0f2304514970a92d2185822c4d542c2b54289c4890274427011347d4b6265078188e7208eee8f172648261492da1d229636cc5e860fc5983129b5015a24253889b1dad1bcb0acc4686f5aacd93a492c478fc8ebaf225c7ee22c76f2f3108a78d9cdae829c6be32a5ca94a51cbf9562108d8f4528e49824472639badebf9fc87cd84744eda489688841351f9f7af6145bd64443c808139f480ceafe87178f31c6da1621518aece4b129c420c7171283bcf701a44190dc9175a2df9cebcaaf0ee7a3dfece2099c4fe626314a5d3c71c3062c49eaca97dcef44b98bdc8f63e3833dec6dfc8d1b30182ceac6d719e7eb6c03ca0b8d8f7ef3937949889c1a9fd3942a35a4a7a51a9f8dcdc33ee7b9ce1bcab1b1b1b992f375a6f175863129d1f862c6e9dc45ce247751e4a9f968cd376b3e995b0437af2f03dd4e723f95dc35af4fe665dee66f6e6464648e6ebece355f679b36ea2691a7ff8bd945a7ccb532c098a1f9641f74d110329a06d5b35a19341f8bc5adbca119160bcfc88821e27d747a9fcc718ad6931721518adc2fd55bb2773f554b019dbd272191a7df931ff50264a7ded413000d72a62a4f0146b2ca474f4b47d6513f2ff7fb8edcefad93376fa8d6ce5de4e98f3987e8f63bd9e818153b2bad95ee94e0cae4c8d40253d1f572646a61881c2a6801e97e0b32b8a0baafeb7a41c9335a700188abca91a98523605a20daa1dd9a239392339894444d2f48321633a53b3f5c1a53e8d002450e9fcab53932295122ef38a14409dc40491253922472f83d3d4877f98fcb63fd4aaab9b13c57c751a970707ab23eefeb56d64655b3727134b6bb92eb64951b9098251ec113d063c68c19d31eebb65a57a6a37165a51c9d3ca95743bd9a1ad652afa6c6b40102410bf6006d30326e38694c98bcd1572f8f4e2b99ed7dfda42c20dc6c09c620dfe13e1debc8838100727f3880ec1d1b87e6ffc69cb37538295dca28a594aa18a5a4966e73d6bad139638c314629e5cab9961de99cb3fb557fce3a65f458364c5c8cf12565534a698cf1467635b8bc26add1c53bf2dc2e8a09e46084d9703d3796db6fc3ed0cb3e1469b29433a5d730c4ae30ced628c118740e594b1975295ce6c49c97594524aa99452ca158beb28a59452cab1ba19b59aad6e46a9ec94895bcf50cab9bae8451c4ae0ca6ec76f88ce53eaea62e49194524a29a594524a6d7c8cbe2ddc7ef9b4bb439d1c256aa5b5ba1cc442d4248fe058667904efa93c82e777106f5b0e558ed5089ee387f5e72aabb6ca7539e2d75a6bac35c6aeb6fa7632b5d63a536ba5b17155cea69b33da5a9fbb795557df7412a0945219a3d71fa2e30dcdc773ce9b5965f64c719a4637a51ae6e4258fe0596a86f4e5770f05e7e33c75725e37382e53029d9bc5e95577f7a455ca2e5772b9b252dc45b3abb8928a2b7174b0b3a1a1abf1c2d0b470984564ae6cda9a6290dd2d251829c1483926090d5232f13061e84c1266d231b015b88a3b779e71b13dc33a885fe414814ead9bc579e5f8bde2b8ef5b71ddcb7d54acf8ed31c8fecafb4ef515c144ba2d534d42e4f1b7dd4d718f74d11204c82c71cfa43062510f37ad9b5e03a4d00a379216aeb3e96a3c8fe5b2400d04164d6bc6a99b76901ef03a9879cadc1cbf08526e26d6cacdd0b4726a60d5d874d14b6edc9187bacd0d2701073f887315243ae174919b9a9a542a2f9c4b94d3ac553d7d57c96d82e5745064efa6d844ec2cd97d065a4e8dce02c10a883c37861a366cdc90403d3d3df267c571dfcabfb8f118470caa407afac7693247cca0a3ba87bf183b56756b50345f254d164af4a7adc53baa33e825620f7f2f5d8c7237214613f7e1d078dde0ccc701135bc1cba8f20512313098925049929464ca28989220fd1b4d773a414b122c2fe2a27325994cb66405160a52b49294548832836486d73243c76ab574726b47abd5f25a1f8fc8ad9f2d26246372eb6f95dc6afdaf00456e3d8824b7be87052772eb8156c8ad99536e254912a3cf67cff33c4f0531d93be2c4a4c2112ecb9373652e27733f392e8527994ba18bcc7939ca036042a10935b273fb28c656837664ea23533a001aaf6090c3a8a50bcd804ad126f2c9611fc9b49768403f66c994bebd24a420d37f2165fa1f0231327db0042032fd9e12aec8f481984a1893e903214acaf48310a520d3ff000428993299e045a69f01fa44729089c08ba21e32653ae14ba64c011f975799f33af9a9b2cd5cab93df96b9dbc9af669ab9998e867375359d8d8b66a6e686151ac9b00e6e3a37f68091d92ea6a38e7bccb9e1c69f2a748c1d63eceed8dd91c6ee27dca42f99f8f4affc22209bd33d467713b8f84831469d3927954e1d989c2955b6fb72e5538a6d94f5ba125b1d816a256537b74fd2f9d58f66d5eab33182f4adfcb939f5bd5a6abf036bdf1b52596c75b0364c4509d9c4001de3733b5f1402bf22345000e3884c68e7b92e020267e14b91d0055f040400e0e2284a28f616c8b80fd991675ec6bf70479699f91fee9fcccc476f68c6a6c647cf068d4fc7fff0170a3766fcd6b5dd0be686b2c88fcc8e5a375afff26f73ffa68efc6cae09c1fdf79ca9c30d5f5eebe2e0d3d40d250ddf7cbbbe717c7b43aee75c2e3a66cc182c3947a5aea5399af33b1ff70dfe0e88fb776cb8709f06ced30f07e77f382725c5e108343ffd6cd89367ce174aa770484f479de04a263188fecb071d72c5a1acc1667b4614f9c4084bda279b3868cdb8345ff3f6551f4a1a77b3a972a85e7a43aa8ae36b8e987138def960f279fccecf0f0653bdfc0dfc602adc001ebff330893de081779ee7c1876dd8039e079f0783df12fcac8c71bf5357cfad6256e178ca89f023cf77bdcb254254ca12873ff2c421b85ec7ff70ff5cafc3e70f77bc4d11b6ed13c100746772b223e24ad294f6297d42975c7243b90ffdde41c096a964b1581deb6547955a0afcb61b9c100115b42652fe944e93d2734fda6c311996fb6553948a8a4c4952b925131130e4502a5dc9dd3fa52b2146892b5f02259a6c9c4d173d0e74bcebfd73fd8effe1396f9f03df88d0dc79f085ec3c08f3e78932362516b20373ac0121e0f3e0223fb003a2101019eb0fb283ed86a5c03e0af1f0ce87bcf20f5fcd6ff5f60b3fabb0857d21d4bff987b45f97f375dfe3e3b103fbf09837d46117eb69bca1d5bbbca1ee5d1feb7574cf7a6ef5dd2865afcd9a94a2ae92d39383c0fe90f31ba1f3dc70e0f78f7b9a83c3435e19c7db1c3d1cffc3c31c78484fc6f1f83f2917e7693c87f3859db717a5b1610cf4641d1adfde100dd8f7309d8fded01724e7619653b9761e076f3938b4301cde1756816190fcfaf971a0f3f7fd0f7f7da10e6e3d8daff307e5869396fb713c1ed293bf9f1dfe6cfe3e188eef93dfde3f27bbefc3f1850d73dc0135caf4bf8f59ca9c9e6a505153535150b5272737f223a424a5a626a524243f8a9a729917f98ce6349f4d452935393dd5a0a06a4f4e4d4a3d6b5a1bf511525212521fb551d37a166b112a2a6c595117b56c2aaa07282af5e0f1f100b953e6fe87b7de88833c3c7a7c3c3e1e108bb0e5b033067a3287c320f987e3577d18d85e7a43f8fffb42780cebc0cbfe5ef6778c819e4c7108ad973df95b2663fd4684769e87061c108544e0de0151087c1eb8481110708fdf791e1fde1e9fe411b66ce7a3d08e0f63144f186b418840bc835fd25a7eadf7aff53c1f2cc88e8771d8015168078e3226b4f3300f5a68e75b6807f74cc65a166b29b5fec6e79f94eb5fc36dd4a851a3468d1a356a7c9d6dd8f8ba461b19b9769ea3f9c2ceafb7f9866e705e62b007e855bf3dad6559a353df5ce539387cc1f04603d315b698c3385f3842cd37ef72798d95351f63cd4b0cf6b83e04ba71bd7f353fbff0e6dd1baab979aebb1b8c3d8cc76a5c51f2d1915f71b05fe67ede34d4c29ee47ddde746df852b4b9afc89427d5434739fb02ff87c890ba0df82afa75c00fdb12fba00e4e7448d50f392dcdfb289b4b966881b4db511c881bff33e80b2a4d5ecef3407dbc886eb4d6a9fdcf9bc04c42758c88edd6886f2481a6530cb270787b8b20a491483a2fc96e1953e2e254dd27c892bb32ca72021d9194c49c6fa618e3bc98ac4342d4edd4a2d3b9a328a6a1a54cf6a71aae73df6c41028dc3ae8595681d093e50f395718033d5985c320d27654023c3066cc18a7bce1213de0ce178566974b8b415dee974631a8c34cf9f7909e3ca427cfef28077b4ae9facff61026ee2929cfe17243497bb2f3f40bb0b3f30dfefc064123f7a118e66fb37fcfef89a11c543d216950547ac8b2cbeca79def0989bdd41cd4a1a5fcc7c378acbfa71c3bd40ac9fd9e42eb3092d6500ef63794910e9f8018545219e5766f6dec6926e647fc3b3c947712dd34504a95a69cd035ed0195e74feb2428e28c07a294522a71f28422cf773a060633c6488113279c7084a48288a93a43539e566a92ea4b511493932f96cc4878400b3424478c9824c961f60025494c3308a1e40149a03a147a3607bfdc558e4b4a4e782d2d51a2134ea5ec7ee3c5f599776f68e667e48cfc5519575a4e46e6391919ee7ee190b7ffc3657dcb71975e7ae9a5dc8febe387b688f8e3c231c65898091a6c25bbccf159cfd18ef51ce775ed7160bf22c929e57c6eca29e5949999796e662674654a714711417a1c788e52463e6c2e711f9783bd4d23ff79e43e51fce72c923f52d692fcc9bbc830eee332647feb0d61e9bc8aab57e6b8c8bd27399555197766d673fe0aba00c8d2822c71228e3f4e8b5f0c8ab97b80a4bfc45362a6a46c6aca5a6855c6ad1a90b28fa48f309e6864f23a80cd23da7d2865f3894c5a7bedfde849a4c8e3f208c71137743146fe75e6412ae54a2499342391ac375d262af5d1d7aa54ef17be72eb0b6d9e42ba1269db5e25b1f538f0fc23f2f8d323f789b2b5979e3295dda5cb29739f302acd22f70925929c9262e4530e27d13c92fdeb064c4d9eb237921e20b047de97b419fcc0b2a7c9ad1543a14940ac9227f6e13d34df061e73202986a43aef9da424e33a50dc6071c3e8247ffec445a450db42f2e9c4b097833b40f901f184b8501123424032d61d6b4e515f28c150c5dd9d521a630c928daf74f9caf366712b0e87b8f0063fb3e31fab14eee7f0a504be72f74b8544189d6adb10d50417471814700722bb39a7063a87ffeee3ca9f41087bfe3669ca91a949d2528e4c3248e56792c14bae3f9da4bcf1722703e2529ef89b4febcfa7923b32790cd8de4a0fa828e01404073fd672e22b7b7f54124d94a69c90bd8833b2632b59e847dbf445fbff8cecafe39fe3378cdb0f5edfc8c8b4bf2ed78d6e5b75291b241f5e3c76d92af5188a5290fd75bc658c42e215c52aa1d65aeb96335a485070220c192238b2811ba0c6a0a054c2962db63cbde045f17483122c6242d081394185e7cc781d95540ebfc722795b69ad7495040d546011a3c313a229b6b43013c5161c9a98f7755df7c6315a48b1f0c2d30c4e2c4df1658b628a3050ee65e264c3840b2624a0b92e262652324e8e4c4c96e43075d2ba59d58aeb58565e4111003916c522abba325f6c2266895c7e8b1cff00d18bfebb86a5e3a4322a6b9b71cd0a04fe903f351f7ffb2800426dc8c416a0ab4d8c4c36400ad16022644c7d2ec47577bfeebc8e2e57adcb39e9e0ae643890b96d0ab7a7cc8c4a2929a986a456cb1b627dcbe3c0b3ea672c919679e71ff88f17452872fc05b8064282282006a9b647723b9b4569b55adf2d3cbd157655dd6a48b547b2b328dbaccea2047902c2cd7433ded0f6331e079eebd7d86c1988535ea441d105397eacd66a5ddbac962405f84f276d76f65ed4b575c6acb596da95da14990fa88773b95c3536b55be5b420cad9500f4c1b1b9b9bd7ecc95ed4b5eee17f5f2f3abda8e633236a2de946fecc7cf8caf672f78b4232ab4c693fab85284ebfcc95f9eca521f18412391a2d9623b1fc6bb1d6760a0009285cbe2c51b3a20769c50f38d81e2b434c996925994c76245ec9f406a554504a63971fa464fa3f4801b104105053aabc10c409327d20415ec8f43f10022aceef07223b65afed0be48354cd0d510aa4207e90c2f85025d79ff186aa0f5b7c209190e75f6f68ced083095a35381080872ab2f42025d767d9d04981c20309b9bee70dd59a274c5290d0a8c9218a19b9d6e7bca16a8520aa5959e014254b14151130b02c8f284620c5084ac8f3b74b850b5b6badb56576108369075a9e4fef9c53096fce39e7bcb88acfa18f5cc15cebd10db91e89c9f51f9c814987292865e40a14047777a0687941060c0bbe9ed490d84cc9987df2850a355a9c6c9e4c417125d560c181862317682470410897a32892ccb658418896a3308a8e987020e30425990bd6056e8b5703926ddbb62a68b40aa3cbc311551551dc2b872232a5db539ae569cb52dbb2406d59a2b62c531edbc026b668a7bd82a6ea525d2f2b64326673f86205da258a902e53b61cca6032a5a02205d385b26426048b1294274a8438d5a008a144c8144d509a8e087932fa9e90b4525a2d78e1d21c979a5ce1056dc9c9931811b7cb71c9490f3b29383102cc618b135c1a26a42d3a90f0e1dae4b8e4c48a0b922c39215a7232c61126883c168fd825b7c9eb89c72213513c167308c68e80628cb991e3120d496a947d4efeb42f3fa39634c924c6ecab24cdeca2ea644e30fb8b946c5f1eb98c1283aa945f3b7ded25f2d8a75fd71cac12833afbdbb73548a61dd94a22b2f5215b1db27d570e97ba8e6e2869921655ef82726592fb6c336f5f1a49c9e1502a65fb5151dcaa5379181e1585fa03e2ceef1cca999cd99ff6673e6924690edadfbedc9837498b3cf6372eaea3107d5648a28f6a87f6993cb40fd744f4a150449feeedbb0939b81231a8b3fd767ab7eff5972cf1a7919a3a83bed26664db28c8b6bfc816894bb6d6beffd05092c49f0cb808da3e12d97eec22b2d521cfc0c47ef55c4f6e3f573d06d477e170ade522875deba4b559becc7be00f0b222303f3e02dcd5f07ed47a1d7a5c130efa310cd47a10bf360e6657e06c33c0ca5e6a390eb6530ac6228331f856476d4a18a4130dbaff97a64ccbeeb039231fb349f53b5efbd878138686393ccf7818cd9bfd5daaf6f0107ed4365fb8d74a53390b3ee61df43ba32abb556ce3bdac9aea37226674d29fdd6b4b3283db3cf51caf2b2773e27d79a3d25f79cf4eaacd6fca6b56fbf8f74068dd43da2676d876d1711b4bffa7a8b085a29eb1ef66d7b1163f64390eddb318c5a397497451e6b27b6ff61b92b4b439a6f71c8765af943b61c9e13e987a7dd502737533fe9eeee4e8105df9698d88beb3fbb890d377a584a13f7799913dc59420e6bf2fceb8a41dbcfe96097a6fe58002e72ecc902a4e2b1bc96c8516210e33fd83f7d45c7fc6b5051397ed857f48f3ba970c8c3e250878d1cf65383919d8bee6de4d067339f55e069979b504ca7f9e43ee184623a4d2db3cb84c2a7c824242f90b4f09991cf7cd61598451e7f3148536492ac85f19f98524c420b2d4d579cb2cba7ecd24b76da4e336fba22bbf8ac27a7450e25174dd28718f39759f4500162f9255e55b23b11f24be4e95a3fe1d87a67350e7f83fe69619914f2c861010ac0fa2ede4b4b77e3150ed491436fca8e7978412412f672c3396b2d1e94a0d2c4b484d64158ac8779e081f71b863540f5dcdb8779c0c1a4c530676158c51e744f571836b1072a98c450ba0dc31c43517d14da3ebef7592b1de3eb25f8515122bb0612c8c106fa2181787ceaea73082eea7034a600f7f11d3948ac9229b5d4e577c45de51824a3d71e02bc2c710da4d537fd0f885bbf33dd24f716e3d55bfc41883807e3aa704ffe0fe346196b9a8392ca98162eead4af8fe867420bb551c4c07f03e346198b42dcd32e6da3c8e3bf857139b74fffe949e23e52616b44b123893cfed1480a8beb53fe3886dbd88bdb180a7dbd7c4274dd1f470b37eca7a7277f9232e9754a8c31c87c70c782e848efc84d73374d36b1ed3845e48686eb665cffb069e0911b85a2500824611207110a6b8ac8589b715be68ec3042476c05e2491d2332497888346dce80de1143a02f57b9c6d447051e795dc28e3d634e36f3fed4f4e2623e2e07cee05c38d1edee186718a88aa7fc619e777b9b218b42306adf2acfba99247befba28cad6cb2dcf856c61f1b0011f20108ee038bf88bdb0f10199b1f8292a88b7e7090fac89c54c62ed9670e88423f646cc65509d1845b69fd721c9c8d03c98d320b064ce2da1c999e3ce5f056c182e40913201597e6c8f424891a25d01daed3ea3ae5d65deb477fabdfbd75779d34470259b9d19c6a5f39decf6ce395e3edd0696d9ea55c51fadaaebd76a3af1c6f874e6b524bbb4b819b52caa77f03f7e8f89c73ce493ff6f4a89aa136ede89cb3937246c5182395b56ba594527a2da5946e1b1254c61869ddac8a524a6b8c91d6788c2f393b2be5fcea54553a372e0a183060c0cc31130c183060c08099748699632818304761cee0a24ed8b5235b172d99cd365b8ba88848a9e46aca71f2f1a4211670d07f840c38e81fb59a12e03e90dd1054d77943abef9e257b8040ff97f781fe4a5468f2a7993ce6b6afb41114775292060e42791deba35252126773582a57917163ed86317078c891c9e315a6274732b54adc1085c6ebd4c2930d275ca61b54d0a5eaac40083a850d4972e4fa53e74a0d5276e4fa968a5ceb2da2883bbf062436d01ca851134b84bcd06484059329a3291784b880549bd87ccc923282b833487408716768b8e0890f31302e54d9e1b6725c72018a1d2ff7e6b8e4c21535725c9aa184292a318733c69c79811873ea2f7d6e34b640e44972fb093339790f3d8b41396250cc114714b3f944b325f46d8c71c39ecd9ad6b2af8b965c99d0d3f7f1f229a5b321b87e1a36b636b926bb66b24cbe99db3af9b532cbcb5de6b84e06a5d8a8248df745392ad19400000001004315000028100c088402814828cd835db10f14800c7292427c5c369708b324c661144286180288018400038801305354e3007682e79eee7040c7e23542471cab3e8a5010746afc592d0001442be9be613fda755f3bff06fc67ae8bc9ca997de96f1d991bfee11f78851670b5b4b95668efef23aa28d84cff32f9e73389ff847f590a00db04c296af65c491abe3239626006d70d79e1b50b2a8e531e915c542949b08c52a423b61c2fda524c05831051a9755895e1a838004331485d7366eea40ea1053646b76a6b3398ffa7bdf548259fbb75d0a2641fe4b53720ccb00a8e2a12e7265874ac9fe1c9598a0014341e72b10ad74c411e5592742c7d0d6560d6e2bbf532d6e3b93e5cf47dc076b5e091e66f6ebc986754743fd6af4aef6ee3a306529b3e65cb17656e46bdba337a7172f212c7dc14a71782b98036879eeceedf35b87e932858369f19ae72d42f10e70a33e4b73084ebd9d2541774ed8808b5d2434c37bda30b660efdfadf39df20e4b9134759d5195464f7fb9e90642fa50bcfeadd576406b94d4941d98490e3b8a18b89e601e2ed493990b7f6bffedf0b768360bc28de26d84ef4c2c1b9172118272f62bf53f842898cdd30bf1538cacebbc17da9c752b2af19a22e18b48ae093c0f38d82df097090c7ee2c06ebb421126d1c4266c9b584e3c7f902d10fe7e96eaa9665ede7e0556184ef9038e40695d5a9bee647059f5c304ffb41a0a73d6cc0dab4cf8e8360a629cfb0b172ee198e0fd13b5ec739f7986cba0f06980554f3cc2a3e4ce8d29a624ccef6d7e09c0c5ebbbe055339800b6ce564edabd1fe63ccffb6a773d2d6812470681e7aa4a32e34cbc41a0e63582ace32718f8bf1771e47685e7db024b98c5a8beaf54ac4ac370b5782cf42ce8c0c190ae653f34ad15f5188a6168b383498461a09652f82a8a1aa1f530afbd18776ff8850cc83b11315bd5bb196e62e3475875ea6f3047d5822bc946de632dd8cd6fca0e287071e47bd136ad41b10b8496a6ae3876c5d8e381bd6dbb1a94ced56d323cf21353bf045b32e525b0e326964b11344d8aeaf6cd8ef23836171e31b7f7181a9ba0ec6525215513c42d561f3f0827d887c190d0652c7d110d751f2ed6c67f011590d1e39c94531a937ed345eba0b992ce21a48d4ebb9d0de364478d859e0dbda75473f819729f61af4905bd86cabb1f27206f3be97e06d9d1a73187a168b7bd811eadd8953b27b00c5eaee05d885db156a8ef6c4a801525026cbac229c472aef2aaafce5ff7a8e79abd1b6ac827e7e458f481e0b791cdb5c7f5f950f5398db7a4ac5ee6ecd3bf9a2e4992188fc8ba142ef93b5a0a7c7e357dea81219fc92fad06e6605d5d4d80d86ad2b506e218a887b4fa9197525f82253494a9a1008b27403dd97f83a2a960c9c57308bcec5825f0250c6b865ee6cc2648aa84018432a72efcd72da302166301ea4f56e9f2fc80f0006b6507f724242c13f630a8cb1277843665c572e9161bda2a4a65e24c5e3a7ad532b5a171a3e3d87cbe23531d81d99a4173d6b28ff6c42bf6eac3fb72bb9e3f0af4d2a42c1e84a9a0d1d271a7a0a2820c7211ea675bb18b356681e60a28be3fcb0dd198b81c2c7e2e1a9237deb891f2eedf971cade6de3593c824b140b528b206f9efcba4738f2be492128ee5401d147f2401103782899c198323182f9deffd9181ebed7962c5bd5e3f9f34fbf6646fcde6b006d3cb342c8618f122078e02c77d27119ff066b4918c31fd57e2130e7400fee29e9010d144ddb296c9d0665c9e3de67ccd0f513362b26ab5abbdcfc31ce12a2a91e98b3314671fc95f9df63b3690ea1759b18b9beb2b84fac899206d6a80ff66d73863c79ea7f39021fc9c8496fc360d121079a2fedbe07955b2751aa6e1516b55bd982b283d37eb7f959c43c9ce3d7b94f494eec30ab16d60d0efbc2cffa157921afe127cfe10a146fa8484968cfa0eefe665107151aad7b6bff957f9f0ec3779c898698d4fca929b73074ea4faf5c8a6a7171bd3263cf97a169e1908adeac7b3d95cfe1802fa74270a3034694de89423dc548ff858e8f22388b915badc1b04d8457c32b64b9051960a5d4ad684b31c2d8543afa5672e85b633537b399a97485d4468f74985ad28b5b624dbe2c011bc1599cb1b98061bd21eec7b63b66924c3254f187d00232fafeee16f3ca502a79ae6c4fbb6e25592463f7ee7eb145dbaa4d7d32ebe4b4548db2520e3682c79a7ee34470ecbdfb4b6755443935f664bb4907bf10c5275e485cd70559b01953e147c477f91bd24db739898e47a03d6e9afa1cd9355437f3a3d2b083c3a44addb3f8613544a052804c2e75674fdbcf836c4d3755fcedc7f2488e7a9e99fe1cc82de48ad6ac17a0a9a2ad38b03d2c57bceb38484b1678aa278d574415bd3e6d355d12f02dca12677f77775506d0f844e3b3a0f4c3cce3ad081b09c3e394db22955bbe392310cc33bc5e76903c2c46541748969f6655e1986febd04cf474b1ea758dec9030fcecc1dc1a3e4d967545d7a00ef220e1a127b326d585dba2f30da5da00b4181abc7c1e3f67a9a30b085434fcca8d848267ba21a7b1a5b7634663975ce777d0892c1cd96672a6b1a646c7ab01d863c832c9b47fc7478a0b3f07bd39e11d8da08cbb2787c4e2e6f903d2721e5ef59a2811fa7065e19d64e69e1f9a1440147a8ee0650083dc729a4b0d3609e47f7f829fa1a9b59b9b06fb672f996d43780981c3e3e300d247841f76494956d40f9529c791dfac074537bf78efd8e017a8e05b986019ea70b521a24024e68b525b16986d6d2f8379c909608178633e390fea3617618e8ce5b833088c111f6d03156e9f1f4d3059f58598669a8eb0aa278eb07425e9348a17472bbe5c27f5a8051a853dc0ccfd5c3f685f13ccd4c7efd7c0d1bd3f30cfcd2c234fc5f4a29d24b5f0de189f6d67d5a7f0a77c48e1b9ca785d1c02c3ac557cd2538f11ac84f54fdc5c9644c2dd0dcec2dd598292c602d2549c45282c581096f4b4a00d08cdf503b70b33e37b54fbdaf4b987b72da3501da27b5656979f7f0420c11b354d47d34f86cb3aebf5aa992819b0fae6e8298e599d5534b776bb1fb69b56639ddd2f2aefee859ef4c5b631a772e7e416d168dd080816f4fd409a194df7faed7765a51b58218501608f5029f9714501e090a281889343c139186a783736bc48c699aa58307a278acbb86e5b1f66a05c6510922d111a0f480a0693a2251a2d0b98b75edf55113c98962c939d21cd9dd11df61ad460643519705930224aa3114acfee91f2d9232e9c8889f12ac2aee97fe262a7299b7c18236e7650740e0ae8889418646be6239c3650c8de14c6d503763dfffb5a45c0222af22c67151eb9627e2686df42edb06e6ed1ccd30c81ab17f10b428b7dc6eb066b5548744dbc33f10c037b9857b166eee6ed0cd9098c744cbb8398241b38253d37584776b7a5f479db366551e4b586f720b7d00ead598bb921c4548f49d9368273500ad01c914a8456dd005a4346cdb2057ce80cec123fbe5bb3d48520c69b367b8e6d864ab81193fa1555ccc08780686a0370be40cf22acc428360e24f018b180e2a27e9598631272b0bff68a6b02531fb6da3a61530c5aa804575c7573c8709557022f7456c2b33629a26c076c98a98a8452242558c13416ad78b4adb2bd2b972a4d3358abf44f088dbf9111c7ec6fc9212c729bc88767724d99ccae0891bde99eae80c2589ee2b3a033d301d63b22c5e209172ea6b02edfd11af599309227ee974f38d9b77ce70a4bd642ef160ffd76e853d9c1fc7a27858ccacb653af36aa478d9a6f9cbf904d437de6771b70b761da17701c138f5f1a1d2dd0930c9e5c7a4e0356f1bd0e429b2144dcc345511955bb471514771f22a53bb5ca1a178b9229cec2760c5cfe549c60fc54b0ddb8f484553cf035eafe0c508b0837671ee314c11f1dbef267fe402024b97b32b40b954080a3e7323f2eb8e58cb71652bab0c1b246bb912190ee56422e3831b6f3a6698d4051b311192485e565aa53f2eb292848669909823d1852be4142869a19752359346daf96e7e49f85f2e152cadd4259339af1658d6e457bf0fd7cced16c8e686434a2fc5c0f89c62d589da28a96b452660a78d87aa8711faae79d2732592b2bd0b3a560cad54c9a9b086b9046972729f17d53d04f9d32a9fbe5d9d0d908c1782bba71f3f0f9749abda0edb1edce33b191de79aeeeb9ab4f6b7c7dbbf1932d9ccca879c11ede04d96dfcc4994ac73d4d8f5e5701ed1038133feef02f4c01a2c8d3d5b4e767a9b512c33c16dd7dd359f04802fa2e9903f2486ce933a53a9b84348997508e10fa63c50dc57d0accadc6004dd37791cffe308d2ec05ce42c60dc60b4484ece62c7a24c495346cc8ddd71984877f02bf4c338d4ef0a2d4ce2746bfc5a212aea38545c1937008cd03e20b4e81887a20b2d0a6a8a96870bdef54f5e943587d33c9eeca1511962ec300aabdd9167a2c59048797045a4b002a626c624e4da50a2635de6b619b2af32e3e0351635b254d4870607f183290cecd3271a8835433d7d10d129b194928d6a5c06bd2e94e589e4ca8ae13c03d9183a63394e3cb220787b8289aad9c6236d35b496023a27e85ed60a14c75328b3ddb69c7cd0ee4c4def6f1b7289089795a029bafe62f7ce585bd3cd1814cfd090cb0c560f7d370d91184594cd06fbea69afccbf1f1c20c31e15c1783c4b3c144da257695487eb793d0041d88b8f60186bfc9b607f2a2446999673216bd2cc7060ac49b079ca05bbe83f83a41dce6fce9afc7b24af4d55481f59de189fff0535159aad329a44a05626ebdbf1d94d18b2657037dec74412c9c000fa5ade20e3e01f49376b6fda09b8b5de67fdf22bc3e040e1efca4a25ebf80f3d8e03da89ea107709c74a49082e39f7944784c2a29479a83c742be426d4c95779174ed814c5b3a66b151ae1a52ddb3355aa2a22cdbff22ae7695c30090c2ffb9b46e33a0b1bd500d69169831b46cb5b9673d57c01c481fef575d16dd22ff5b558de252620cd732e68421f18fbd2c2e9a3624a981e56fb684f0e296392b0ef94ef77a13cc5b4a2eac92d805d51f3011019024b492496b40ea0a2a8b318473fb9a6221970200b4311c39cdffe6832770f85569c58da0be34673e2388156f074aa42edd75f3e468273306993682db5767d3711a0d14ba75655a4c64b9061bb831b6e5c2d0c58872fdab87baed90ef512c184250ebafeae305c60f020fb802c6a169db66651ab9d677a2c971c9b71531809c1c1d0bb6b59a74c33f30e2b32200b2328513dba98e30a73f4d20c3c18193dc02cb46e52684d3f209ae0d801de4b70167bc98f5ddf0885aa2e28ab142cfbc01c99be142caf3b3bd8e8b5c202df8b1ed4fe24aeaefd6b0420ce74816c33135f48233dc4a011ab8dc4cc13fdf762bbedf24257875e2e2d15818a38cb6617283417a8aa6c6a6c36642298cc7f1a17b65ce4902eb64bf247cf84abe02942ec4cbeed68e0b9cdecb995a32ec72683c4029fe4b2d82e946d458721ffa229c887196ddddcce7e20f42f24fb7f012c40c24526581348d93aa66c09d96d1e908df8643270af9e9fd8fd613e256b8b7ff340eef5bdc6597eeaf034621d3137ce4684aeb817bc76a051dcebf2691a296732082ccb6120336e69948ee703f4c50062ec8392a5adb464c004ec477fb56f457a5ae661068608d04ecbb556b577da687a7e67c73627c4c47aae74210fea689cbca5da2f031b5e06d625c1430ac87df6533504dc9f0c5370b383172d82d93511b9d759c7bf935d5230c213962a52a49bcf748044e59be91433e2ee556ff6685ab8fe3b938be2d57db7285684e1e0eae17fb4a2d6ad272da5b729a7c2517ab1e0f550c977ae955c16cb20bbbbbc5a26a2798f5e302ead4183265370dfbeb229a4ecf2398fcdac89e9634f4c60978681e5712a064bb8e2b07a43ccacf7ee08b4af294d8f986bcb304ff82a3204a0a487b72a8f45bd8aba3f8a2333137420ecc71c2d1692e24dee4c63ffef09b67e1dfbcb222422bebb4e1009cb8e2d1463a353529bcdd97bcebd6da2f11c86089e3f20c4a63341ce245ce4ab3de841b081865ded615e8354b45a77bbd8704905a06d50c1c17bf0bb072fa79fa07617bdb52f5d68ae777e033d8ae8be6829de3a20183ee72485f73b921cd56b5a6a3ba036a1e1c4c89355856c4d4b69133ada1997ee7915b0e6e73677a1b48bbefb015595566054de1ea727e2d6513f74c7d52107fd901691a4019422bf1809938bfcf6ec19ab951431acb030ab3dc52a071498643d027a1d41adfdb31dec49acefd200401d66f30435fd0a757f15e7340f89aeee55f573e065bbffc9b3d275ee4ed7862f610d47edc0997caa88ef533a590fc52f082a472957c27cdc09651811966a5ae676a1250a11f912080ede317449c7c84ddfe6659d5d5933c22814764fce43e42cae02fdf3340f4d0a601843d8cae7219a9acf4314d4a5cb93c2eddba4f9bbc9bbcc7effb373a88f34a5bf444435f11eb76f3934a4f404add9282378e3dc15b06c008f766fed68d1e15bcb27a976bd9fa54ea0201d986bb5317f40c31cade6f4b4c203aa967d2871214ee5765e88c8b4b6401faba2b4b2302c0e529385d90b4430c657b15531b57f4c8e3373b01c225065c016998af985014e5c5e8fe99893b300b96ed219b6b5468e4ef9a1f0b960456e299e138695be55514f1e6ae3bc8ef5f525b9884aed0674da427341e50163e1da6e72a2a2674d8678cd01634473defc80520b38ba14dca438d0617ae9642afba9ad3834305f7fd74cacc9ebafac2f4c74d3bd68ee6e827179f52d9725e6599d2f0d79b5668c53cc76a502e1223c729f20893b2cbc85ed5c02ace565c9c6e2d7161e9c96a6eb1ecdb01c9f549f260cd4884599906444059193ea5b27495be531a64c60a375d1caf782b9c6e87deee7a2be5754434dfe11dd10ad41374608285e23ed834a44e2442f4e804e885b556e7a41a3684600e6bd80bc9a21aba2f51024dd1f6491dde1fa6f198ccd8f5ce8fc56d8af9c6fc798adcb52e96572e90a051e1bf94dd36e4bf40d851920eb87be3ed26ac19d32effd0d51947f1956cbfeb13b0fea0e74630915d1f79bb786363b3637f9f3709f894cc5513391db01cce56e827f34738c26fbb06043b3cfc08152ba04e20bc4bafcb94bcc791e7560f054820f3226c58715957015472b5aeaa6bd0fe0a0be6b328e14e95a1ec28928d1d3dea3d85b07d9f2a685333acfd0f4e4f9800a911d2b83accca03ee80bade8909cafa71bc4a738b05d56839f7681b365070707c19a06900a71851ab10e5454f3ca17c793b41cb38e1a70bd96a2c38d9d84c3172032436bf3e81030d26d35c2b1de54350d7f18f24fc4c70898a8ffebb14d378a256defdfec1fbfba3e5bc54472d344fc1ef7b392fda337fb208aa4acba81da0b9e03f9d71871cc1142e40f09f57a2afe442b077d05dd98921afa5289630a4f699afb81ac776a99dcdbb919728241c8b28701b5eddb2aef8e245b88407bea83b0ea893442c61370021a03e5048ea818f2e90f81ed46c7cd0e25827071b00f4a01223fa40171e1642ac94c184e212486ce299b2988515b187ca3ad1c9f45d85bea3fd4705443952de219720fac7e540a77ca02d466980e21dd140d2927d38979029180f10de1c8fed5d368ccc6f401978475de5e12091c62d0149f64c615150fd58f30407cc2bb3923d8da06da6a8b5f52d1529eb9929b32843ea11c5c8baeec819c8afc5848763657f84f6dfa63fbf050b3fd82485e49c30e29d917c29f5946fff9e6b80897fce88e0688ca7e2b878ca1e44fb4b4905502d0458041ce4cd42f948099266302ece1ed26d686d49881806021ce9e148021c6b21d0293424136d047df2ab35930927f1f41c73f42e9bea61765b54af114dde4b3e26234ebf42bd1108696765729a8927443bb53311537c043102fa466c854a4a4955ae5800c7d479abab134b365e59e9c72e83c8c8ab35a728248464794c4858ad14339ce8d4cc57b142dda3497775dca2c1ba3412840d623bf28f6448b4c2ea5d1f283948cb54924b2d423df8761c4f5782c3b12487582925c3a10153ea7bd91708e9196f57115ee4bb4f19708bd23d391a472b981285e486766209210e2c518ca341884bc22cc84267e004cdfe15cac0d7a1062ced52d1d44e1117ca4230a6fb57bc33300f36e9906b476c724b6c13c72ffaa5528c0b5b45764c747b46715688ca847a7153213de367f2b0c7eba016e299455b69d31e82bade1a677fbc3b702fae29a5689e2c90351cd1ce808b12257a0a81e02d7aa7e65f88953bf44ab5bdb6dff66251830ca3f25bf540dbac8dc7b78cdd7e29378071b040de96448ca954ebfe545842dc40a712887f35eb0cd32fdb6489e6cfe715c0e903e8b212b7ef048abd9d66053fe02d60f5ae16d1233a2cd5519050a92992ce98aefb72c163e35ba664a259f6c17ff66fe19891cfcf0254ce9853a8f6884c068031b553d9eb990ec723dc06ceb9419689a14aef187d74ef71675614fb9d753af8feec9d97c89abab3ef8f450f55e13ac0e480edd2f0fc0e53e256809d011cf6b854a84eed9c432a33f348725593419df15eb381a2e730121a881abf7b29e600d6419a008340565bd7c866e6e10a1ccb6a9049ee9a94d1fa35c5c19720b6e25f54c61821879078a03ceeaedc6362bcd0898557b693f587a70971581fd0518dd6a83d2bf9cf1f05f2b30a5f0ba290f83849587a3b795c2d1e350b25e273b5953d8ae5c8858743b5a09d0a2e9d5f772ec8f1226703dd47ef160622d3fd2b5787f17963393184a243ee6fe0861e8d48cd1f97505d6701512ef41da07bb277bf79bb973027fefc9c8a34ce27aa50a99e207e54bb40970cf63c88c8104068a675d515910d24b777619dac0b09b022b7f8e6caf8ef2c2d7c91f476f89de58c90d4c611143a7f772ca2955eb543ff0d7ef5bec1d40cd2b6d4ef97926ae1b98d709ae9a819389e26445727323fc7664bdb1c8f5bad2ef90f1dc82ce6a4682b7a9effaf169adbcc3db015b357fbfa8f9e773061c9d42eb22cd795192f4da985833de501e775f14cd58af70cd2a2c1e71849602cf265dc8a5c654411f73d1dce6997c1c6a02633edfb26da42c6a7006728ab1068f80141fa8d5f9630084026b2524186b34ccf9e3fea705dde4606a797e1c3435a1fbfe8764e4e5ee211e55cd368926eca0437fe0040fe3bf12861dc21bc736891b23112631099456512cba94706e56f50e175c1153d36ed93c85ce8ba194161efb5f9269bdf86ffa35e626f65655e0e1bbd9becbf1853d6bed5685f3fe1b2a08c3be820f2853b5c9e83c66f28530d135641343e3b06498fde7e68de70837ca49b801d8a91e9c8164e23f964979e99766431b86ab2fb4785dac439876afc34491ae029c7548401ee818d7bdda9e00a795c303b679c9ff49ea7d00f5587c8faa59e6744926d643ad35d84e2f8a67ade9c476dcc8c19d56ad6ed1456ae4f0284815427c49f2277f59fb1faa29c6500f1baeefe9cd0df345e1a66bacd6adfec55cf2a2d2383816b48b6efa53ba673f121e915511ebf3ce246deea71578581a904a5ebe1692a8b71ea8fd9635ac82084625148c9fe6698191dd9556b745f8214b257c41a3962df35058ee57b6dac70ad887e76b6dbb35515cdb62c320d3b8f284cf94ba2e53c8f8a6cb4ce4a49b607125112ae54105d918f8b80670e8ac050a41562c3c73436c8a928bba6694c99dd5d1bad666879203eaadd31cb33292999a40d16d26befc6fdb6bc931622ea8fa0333843a397118a10892309441a83b82f84ae0fca6987d56972dd553229fe2ae1e48a80b6370d030bb131fbdfdc1f63437bca62fd35c4790ed5ddf0f1b3079231a9926c135072c58740cefedf2ad536922e3012d7482fc2ed5ae6a401c11aebcb0dd28a0149095ca1f27a8c13722c782698351ae6f7819686799733555a5b22029891d732a365be636474ae59cd31dad8c2972e42c6ab1a04b0a43a9bdcf1e6f1edc8420f10b7c2b189e148c60f05ca77d8ab184e0ae977fc8ebca2c4ccd8d7d03e8f46a4f7797ade07877c1459732a0898f62f1dc0476fbbe4421c6e7dafd02c2b577a4753979b583072ac9739b79ee952aaa965e31bc34a7ce2441f7605457739c0ecf54e8cb11c9604679b01faa0191869a98bd942d11ab993285655dbe54e2e920ed5038c202d18cb7a9baba3dbe66e2dd025ec5685e2738e3c10001ee0f0f548a71e4b47890280b675a028de6f66a8082cdf4a539cbdcf2959c1a2dc148efd55218fce24d410d401b64e5a43a2bb4bb8876c75828931a1b86da756dc2ecae9c4c4d22396129a76274c92f0df2e88753bef5b4ab8fa9a8ced53e2352c16b9b005ee3b5dc14855520f2f59fe210e0b82351c0517c1d8d16927c681b2c7bd1d42a6962093373ae9294fb7cf176cc3eefca7eb2ada239f4609bbae5bbab837ef2156d40c830203efcd2f0e289aab0a138ff68540dc0712551149b4586f631ba07d6f85004ea84b28c1cda635af7c3b231733798faee680fb909cf1c6f9322da48d17072fe433b64c60b256950312dd51b932087bea40ff0ae20ea468cad25d79fc0ed37b3b9305f7864b111ba58cc4ff7d23fbab72170baa458590fde830231d98caf2be60abacfa9150243bb86c0db1504281c9fb9679f33eb505ed5204a9fb7e5c8b56b3dd3788d328e2a9d15b445f9ff60a133f4c6c319fc1dbf886d6c98a2097041870447cdd7738b7c81424106fa275153a68206ee7f21d92da4153cd83c944f827cb786538db0334224640a096050075a01044200eb135ea712472cf3fbf96a67d9180f6ef247f0f89939e90b159fe1f3786adfdd6aeca0e092c224b8006b17751584ee068b5f5695e2966b684aef5b11aeb8a8fd2044275161210d9acfc8998ee926e7ca16cf38eee05303f39a2bca2f8e081954032bb78bcc3ab956a5b2060648fff8b5177eabbb704f1b5555b5de8b80ab10947cf52c1909c7784b8239e60d25455f35de3fd4ad6f6614d6fad5e596c59cf48c98787491785c7725a7fae719da7e6bcf43499db4b14a3120bb794bd99f31404a139f93b8e9ffe6e401b7e200b3e4c9f6d32310473021aeb814e8916c3ada7a5e82ce39246b052eb9219e1ff4d5a00e37140b96dd9d18fde32874334ae6ce2a22db0e31318acf98f351ab5b0e4637282483576f3906ac7c90a40d2a7fac82d52e208be812eb4e05fbc939496122560a7be667e82763b7d60d6150c7f09e10c14ac9c62cf551c45ce322e62fade950371aef2f37cebcb8e1ff09d302d90e9b85ea974e892d929f383ade43a8d7738129fb5388e388ce8a429783d6602f1c1c6cdb7128cb14afaac30283d87f5745aae4a74e5a72feb93da962033d6f4f49400fbe0ecd5bb40adce3f8fe41abec029d5eedae924d0da2df33912e07e09f40d9f998872bb06deca410f84f025f99f02af712e86d3e5e800f7a80da30889e99d62f5887d0410931c807f70ae12790ff941721df1fc85f92a6917c4653c41dc00c7af993400209ecc4d0e8e3c3d743aa198220e45935903411f05e4a033024f88724ea8178fba0eda71999c2c00f2bf73c888875bf17f96c824604cc1688c602e13361145d9ddf11cd5f1edb9e71f5ee2def31510c1578593b75c86c6585b6ae3f2e89d822507a1f7af736133c8d1ff6c9f89d3a09cd2e1ad8dd53a91b9970430699a8da912a6efc3c19d82fa8091e28d703c37d8d2293a3d6baea2445212e8cb1dde0ca857e13257ea888071729b78a170b6b6a805335edc1fe09946f59fd029ef046003dcdf9b02b2272d82721cbecd40d0a8ac8ae0b4e9f4c71c63076a3faa191aafbf096b33ea760bc74d5d78f0826b7b03657aab5b67d9f2636ccfcfcea5d9fe9b312e155a8d0971a78288f78b6a0bbd1384f40366383733f59657cd3151592a4d7a6132cd704c3f025224170cb3172fd66f49efce417cac2ea4f2882d651d3824f9a0320b493df7b410a38ad2722032a9921d06c655cc0c2b5bc8a6476b27e132c237d22d5880275f6a48444be5584281c0d76afacbd2820a766209cdadbb523838535cc5f7f69be0f34e59ebc9bb6ef9327a24e79b60b0105382b5b7ba673ffe1251c2995cbef1ee3d6efe8251ca5e5deafb2c9df5904a401033dbd0f3fb4d09a24b9d5f28b29ae396b4fd7100b6eb4d50e62d1af29f018930e0f5809082b0abd17f1eb8baaa0211f2bbf6cf1e2a51528b71bcac670d6df28e3618c8419dc63d4d5bda899e4bd4c004f13e7bc6f6b88e287de654a32417a8e273a90c588404653687716d675302d3f58513fb6b293e9a9d0c6c868a99a129e3627bcf25cb531d7a79d5aab4413871c8a9ea543d5bd16dd3b811b6886772a65c3bebc39d4e59378f1d63a2f67d48c6dea1f6cfa455dd8fbc38e939e2406d4e7130095f732b307d98aa190a4a32a92510592198e817211b43bf640b8b795b262a9a6274227d840f7605b33181316faf3b49fab71ea06b846f07f4a95baa2ea6e24a28c514dfa730b88da2c698641005672ee338672213a88f17441710a9858c9c64d6c7755689596291866eaf2d7c09c76433cadc46400fc522429928a004ad3f748042adfd9513c207c0208048295808ce7992216693e00b57b8c4bd24614389fb2c86feeb60dff532bfdedb74da93f268e4dbffbe3c0db41aa8ac39399b7d3b0b60d8879bebff4edf4cbc52e84ff592521a3dfc3b01e98d42e1cb57d1fd6de43e9f77534d6b2922a8ca937a0a6bcfa4c70be0000da13c8dabd6777bcdc073e0e08b95f3a3efec6cef025145dd1fa7493930c8561e121457a653ff46c225f1a56edc767b0e61e236c3529f19beba65e2362ccd2715117aab698829e1af7ca222bc91bc101aa9b0a5012ad6c9d4a35dbb130eeadaab51978c5cd5b04f0a4494fc432b1e485fe3c4b8c0d42060d5018f79b3642e4bb017fa604a00a25016e62f72ad335a8ca08516f516201b7d116571092a6413bfd03e3b1714d83eb6f2aceff06eea44745634c4d44826fbef2f7f372da0ee6bbc34cea1033ff82d7fa2a831f0067ed02f0f1a0d79cb26b4d0521da7259f234b25b8b5532c12659de38f719861703dc9b4cd3083324e0478259e42483a61d6a44e84fc3285bfbeb65c2c8e4960dd0feb02655f7418d81ccc99288abf0486318c8fa04c0d82cdb46adc20600e841e4168baa44f5bdfe615c66395e141f76d45e31599ccd9189afb234564a1a787c5e1395ec8cdf4a3a863cd134f55e49409e6f4446d3c18058fafd1295b2e19d0666b4fa2a7268932d307ef6a9cc86e1ad21da32942b1593c2c3462f9d1ffb36b2d88683ba6d7bc918b34cd5f1d412cddcc2f8228b34e1d96933c6c39bea970472655c53f977348e1412f7d0e124b478cecbd58f4a5063ce2059e9536ea47b0e43172c2a7c76a8e7bfb3d84327cd7ca671c957cb5c8b8d14530791061faa95277e8410eb3bcbc6f38daf0d97399e6467bddaa5c55dbd309a32e6fce3ad2e8940f2360dfc8f77b194d2381e55a9a4823a2ea3238041a4238e8bba966123f548a933c8501b10bc6d8639be1053a756c20e46310cbd5bb056ac50edf1ec389f8ec1c041074590c964a2122ade63a3c7857efae06971a23a26a0b18a4cd9c2e94cb146434a928b73a7aa6f71001ae4f61d0884ab43299117bb1626ba9b503f81a302380eb1c9275de3002749e05f112d29f5d476e25b9f97b905787cb7c70ea14f39909099a739c17f7b532216e5dda5a7156e2d907722b66c54ab23a69e0506039a33a18a3abaf859e19e1609d7b3115a4a4bcb6e690e459150b416a587202d30bb5395677059bfda741626babedf072474110c7b8a843a17a216ac57a9925142b179f89e537d0cd57f3739f9ffd50a6f4c02ac5d8443fddc2aaf53bd9f0fdb018c53db11a97405ea87b44b59c85014fdf0a398caf91c670cdb0805bcdf1e741ec4d36ab77e48531bd6afffbb96e6218f79d358a84e313c7bf4805660c38f76e0aeb106fee5197976f2f9cb0e6dc90e1c35fd09f9c68e5f8e3f45d8115ec6f8fff05f772c8dbb5c0d44f4d90ce03336d579bd68d22101ef72a9805e9c7b152bfaac0ea15c8f3ac21b1420c820d9dd580163fdd743b0ebe9a26bd0d6d2197296225e7b18f3e55d8d26538c5728edc98dd85567797aef9298e00f13c8e9bb80a2d956c944b42de90731553906f8f41be77af8ad36e7cc4a58f165b262e9b30466e1f817175d3a1d9dfc36ff68a0da3121ebb820ef72888c0331d0f06b1326d76979d477ad0438ee97710cc1c86a03a21ec778c4bb7b18fa9e7c854df32ed9658c0734da9507b8e628f4f3aed61101d928b65bdcc84cd9c70ed896190f58bfb1be0d489a4b52dc003987a0b4689642758041032aa394fa7b8e04b3011a01c11d020944b2cdab16776f0de19e3fe91e0df5af3b067f760e97245383f4d217e44a815cac4131dc5fedd83e89302d9d38c2997ed03f868d997f3a0083ba568c8ec8cd91082f34175f74c9c9b37df2627262079eed831048b345613f15c2ba1b20bbe40549874805d1635716205574248f2001195a9bd87dde611de01cccf96932a3f51475930903145ed5a5c481416fce8ae0eeda25e966034254c8624db7a5376772e735476cbb0ae36264e044f4ff53f3870f21a7199cd1764918a0c628b054e915e41fdf34d9aec399197827917b2bba39c0471ee85ad4c56d1aad9f2aabf35eecf0240817f197d0ca5844acee815619c6afc4df5c393e8c306f5f26aad7528f80321577e391950e62d8679be6e43120e186df31a8091719dd077c32d82a2f7a90a2ee14eeacd131aa6dec47637b1ea59f52ec76e63a9cc1200f666325d107dcb41239f72f0ced12982d2b6cea4f16bb6f4fbd6aed01f661c877a80610ee1007f5e1cbc83c9507725a9068bd3f8136bfaef15da0f27966aa17cf9afe610771f678c5f70f79d0bb5bfb4b693b9ffd0928c0297ed92f60f06563a4fe924757622554f32d673493135cf3a9ca668e7ec46e23ab4b01735857d18a6bf06981958d7ce7829fe0c12c34c646732fa1ce96141a65e5b99ec6ac4b8c131eb053b459b91ab9b09cd1a223bd789ae6ed3e1cf17e0b2c76b90d34f1cbcd5a002d8354ef18c01cc9dec8dce67e989119841dc514f427ec2d505453fe1d4a932f46bb1f8e7d9a65cb9fecc307362a5d7eafc3e4605e7b2364673ba95043a78cce173ac38c710b2620e06c5370120c15869299bc88b48eefe98278a83d35c46168fbdea497c34c613b31d4e9cdedd4628c4639bf2526fe1121a1277b03dff90605746b735eaeaef73fe08ef8e7e333bdc8e9c00c245840db48aa3beadf5538b3ae43f098870d05125cea03bd43f6cd4c055eb8547bc74ca32d6378bd0af58a846ac0965870a49fc74a7ca9f4369cfad9c21870220d76c42fd4eab9ac029dd336c0b6afec4f3a48c58551d04c6d15b9bf18fb9c2c2890ada1f046b88fec1391e17c60ff0bb9bedc271d6fac69610f17e8ea37fe74a176c82702f4cbb1c1ed44071421a6e296a1945363ceff1ebdb3943e72452c35ccf7765f378c4103cca698204c23884cb6048f4576c7cfd9de75667441d29740018de41d9e68004915b9a715dcb142480469f44b298bcad5d7f50a110472c98efa412de53ca818e04066d57415b8ca81013cdb59a006c157ea511efbdfcdc58d86cd3b2d4b96b209a53591cc17e3cb00762d5f62255618be056110092945a09310fe890d789b2c106b8b6c8abd6b54066246732f7e5575628795bf7d4e503358f93505bf1c30f136a43740e99322ce49bf9fd6ab4181a0f1fbb2755d8d993d3827107d6d6f8cdfd7691de9d8ee7f96212cc5df2af3bf437a2663719d788c39bb36a1d9a59a7b5d7ed4428581c248863bc71c33f3f8a9064a504f41af47904373c583e04b971b4996907e236289a21265b375a329fd217223815c2182639a4c54c03f038f01f2cf302b8c1df57710c4e41e58a8dbc444ee2cdc05aa4c168ff056099f7d1a9626c762e9992b459eb28c3f7db9ef9d4b63160422801b711f0413e0bb555a08336aaccb2aeedd111bb913ca118319b0061ca2bde5d6bb509adcceeaf11d2ed69fc6874cf7fd4021d8d7c8d7da0c3db868499997379cb22c41b8a7a408e7e5c1c5450835fa9e10d3cfd2fc9740245f260d4a0d2bd85cf0292cc27e7fe4858cddebd82137957308175dc04811a349e7f85f2c1ca58dfb9e04cc843b66042e0b40443f01f505360e79d0c0d0d7493ce4b4e7c2e00192cfd717b0b39e5ac2e940012072d8772106cfa90d9bc180a1c6dbb3ee876a19952000e51dc6450c02caa5ca0a11a12014e4239947749683af4f55352056abb144395d9df4d8bf67d4526925451039bdda0d1c744da5e4575145989108407320efdfe34a1165aa3c4608c0bd1a333bc68ea071363b64f8e228a9734727b43eb87df8410c0c1b05ea3eaddbcd7f53bd179817454c7ad158667143be6f7c025410386475aa646816fe4d30e6510abe49c585a5e9fc61e6262a17ce2b153c192ed05ac637a34db89f52a7180f9e0022be561ff29bd901111b5f22a98fd0ffb698270bdc19718833fa847c615f253be815c2570c5c9f87fb061cc63cc50741a52160eb2f6154dd41a74658a480420a1ddcd214acfa5f54f2dc0f108c4753a11d7a9b8641e7c9fe3744ed3d183a6b888fda6417d1f27c53cac96ec2cbf62c6268b240234e4283f07323b1a7617f726151f2e16c668a78cfc22507b1a164f81e2b0746cdce2d0db3c63f04f82456189b9469d6768dd409c370526c6d3fbcaf1af7d8dc7675b6bd63e05444eea6d5e41628db2035349dc171e3af02be77f81a6822d85c29bd5dea00b6d9e45f2afcd18f0622fd179c0f05209da3435174bb789ccebe4a627f11a95692eda3d858310b7a036d4a0eef435c74a5c9fa344a9cb37983b97343b4d2f50a5a42bb88854055d14828fd99e672694f309aa168d63b7a1e5b8441d5f799ae4fafb4c7945d8cca24d40999fd8981889073c7dc3bbca9b060a0ca2ed5c130920b3b0c03893bb8fbd3b5b9a79d92bd31cff9a8fe53deca91d0f3f5c3f7deb1221d58a894e0ff93eeabe670a146b2f18ac119a02b2c74edce75900eb87b2573e091034eb1c8daf9c7a131de8429320e6d3e9116e789e4a53fc619cea1e670b48305bcfb27d8dc05452e1f851c3978d076bcee70dfb8620318b1fe7c9fe3ac3305d737f7f51a738a500a392e7c8e0f2fd2dd7930e5392daaae1ff131cdc3800f1bbd40978907523751d460f1df51c0e8c8b51d9a0dab61b7f580489eeeac852ff6e8ffc6f468e8f9d780a6a650e9ebc3abe5d797e021a0f7597f2c6346252b7e8861070c1159adf9955993bd1eeb7d0ac155864f4094f9ac658dc85f2f32cd9eea110fb58512ceb393c8ae90aa9e31012d08ece5927312938ad0669c0a1230408bf6bafc395ff6d965c0aa9b0687a66639093cba3c5e8251e5f1fce2502da951447c43d5d1acf9985260f7a092b7a5b3e929375e635bd6a8829a1a1ceb4768a912f6249f6ec062a0ba9c0334b4b0017de97017fd2a7c2de60ae95f3be46460010ee4bce35e1f7ef15fbc0ba6970f8dc202fc1c3937fa6771c8185e65fa062b7e1b7f9a2ba3e7cb829abe5f5c3a527f60fecd62d271dddf1b7ea1c4ff19dc0dce809f26476b9f37b55b8f74012f2352683abb408053415a89e912a48b399f0b7df5a15cfb7436c94ccbe6d7ea3c94e2464e51a7e438a5ddba6f93ad9369884be0034d000a401ecd5b43a68ae644e7761f535b081cc420ec76b50165d94ae85a8e6da6892538117e41ed23cd75272cd98b6ac35925b03a17492c23875231abfc0f16f7b90c3b58f4f92d8b361d31696d704bf459abd1cbbc3aea66baab105f7b49b8bc79e7193aa07e696442f3a327c5d097f0bfb2f6cec225cd137807a8f87cba1b9fedcc816e1a0bdd6e2948f07e2b6fb0b71e3cb8886f37e0bea940119a8208ebb4d06106338d04081291cdd753b1eecdaa2270c16b2093c71d53aeff1494867d0c4bd4606785e24b7ac400301901f4209c54a911466b494b4fb183f347178c0747ff1dc6432d6b1093dbb36e292cae9e35fdae32ca9486c3c0b15441ccc27a90c062c03766fd07d77ae2ba283ea85f829e46c0231099d836da229458d5c700d3aa092d510b2b4ceba1ec11650b985ba1709604ca8804c3d3c18a4811fc433980e7d99df65515c5790f10410b8052080ef208b145ab7ea40214168461ea56d93d45a8426c30cc2699e812ec620d71cddbf31eb3974c35025e7acde5dc0ef5ac054a6e30841c805986cc1a42d1a501c0abb8e6934e1b56a40d70d0499de386a66287de65e84962c86645ecad5379ea2882449035d512649a6f6c88a91085de595f4c3b2ebab803d7323aa00c0a7358a47d83f2bb9094955494fe6853495c4e52133840988ab8a72b948578afcb2b209c126a344d56cfe56b2ae33b5b89f96c7d6aa35b788bd06ef573fb71a06075868e4744af71a4aa0b665a4fe79576219e41f2ddb8c77c498ad2167f11aca8c9376a0a199cffa877ba059bf4f461a1373a93c450293c3300103d08642af2b8c1c1ccfbe3fe4442194786d3ce3ca22015bc59259a5ff83908dcbaf623efd2c0a908191fff58aa355d48b44b1a82dce31b36a3abf6837882daf5047d4f5d546677f894b956242799e3a525b7ec3e825f4e003055567cbcee91b265e040d82581a7c19f7952547b41f157e5752f88bf60c5fe62ba56d0de6f45d130007c4de68ffc66af75ea5e89235a5f4b90a00647e79a3acbc9a0e33691e4cb1f18d0a7810442b49bfba1eb77450bc27fca89d6c3f2e631e3c2d040cca1f5579891fe1752f493555ccfb29e675fb7d5ada7e02c6d026d049a3534b342196b075e135ddc6078dcfdf4422e62400d5830233ab6b24105ab164512913578343620f3713d31a6f014b4850811dbeaf506122feb2041250a77720f52e746a6002ff074c9ee00829ad2ddcfcb8abe4b1d6d9832f9427673e9b8bc63de25ceab83be3aae9778918a1b82d6549683187c900bbe43571c4dc3673c480a3de7c9a8fff20bb320c84f2d20ae2bd98dc9cbd220330e27a78bcb1ba279b35e39ee18806cdfe7d7f6e3af5bb03401362a78d994227fd90ae65a5d63996280d857a8999b02e215da8d623fa12f3444ac98a94831d38b07e90f2cef0ec21ef7edb0e575a855ebb0a5aa777093bcf435601b82bca2b22949166a751e5ff79b046b76d730b000dcc8d75f56e7380e810bee5775d244a05f5b8c9568504f39dedff95b53ce24e68fa1e98af8cb77e5631e6dbff8f3deb62620b04ae1561aec71c8df9ce27bb7b7d64bc0b8de3c2d8525778fcfa4252e51ee6f1f366889365494a0853aac874dcde127c9941911b8606f46a9cd6a85dc027b7bc9cda337d5bfb04fa7bc193d2da402cb1e9c3f3d053b6301360a3806692be13812076fb9bb56f519ce815cca7aad43300992772fbabc34a7c2a30dcb375080de4403dc5a109d0ecc2f8de6f19c8b263398b839708952cd354ce68dcf2c6561110235908d65ef5eb404f77f97fb948b8c82108e41d4337ca53dfbe15f31db3ad487386435507d773e59f90a5e5a1a3651cd45b02773d8f71e4f8064efa3a902e815e1a0fd98446a1491f84d8df6c8e384115fa6d1ffa3265a184d3b98ccb21e885831ecddd4ec90663367c0620ab7810a0dcb19f101baeec58e171e4713603e69ef69c946a041c7cc1ecf088ea2956b55c8a9ff8b619d52cd08da70aed2f47a20fdafcb5d4f8912fdb914b328670ad64da9b0d335190d3399de7d611b9c6256b3bb128914c3ce18b34abefb09db252f2107fcd82efc9bc30190853c512156166a03f52a45922e9892f7c017e58bb3e8a7f1f538840317ab8bd931dd20cf69c64b4ab6af0cc35c56e786ead61cc8ccc8bb3c3c6bca3a0808ac03b8822f4747141704e0a783b94aaf7b16327a9787226d4f90786a832c5f2ef2e6b64111d9015073f4c8d67687a45db95651307ee1df2974ca5ace5481e1f1ea2bd6e4e41cd4036b4097fc8ca3467e030e785833f87654bb53570c28f61d52ec5aa93e37dc9f72ad8a2a30058319f8abeee5846d454267c231c982bd9a760a09dfb7824dd7ffec872384b01efb78653f9d84baa3e492c22a741d1b90fce50e79a877a28b80eb23061ef2c3b623cfcc0cb8a13c819cfe049b0b0d376bd0ddc1b8d2df6f249735a5754b6d16b42607981549d74e0538d751a716192777bd23357bf88fc3141d404bae76ddf7fbea845d6c0ab2472301c1a6d12274f3d662d91f3cb94222f8cc10e161063d204f11cc2152ae5a6116fc294cf47811e202a8296874577f8aa65c7d838c4cd9169052fe26a6382fa9f4ce52e92a659d41b6312bd1b50d2860b1c7dee8375c43ec40900223d9e35bdae127c9b459ed75dbe4f9caba443d2926514b6440a11362a04ee0cf73e407280e6d607f3e5c0646148228296f92815124cfd761bf584c90ee09ba17ae0d0cb94f414a65808a15360559c408fd0cc8bbfce46765eaaed9a4e726de66914211222d901d6fe45ce9b8162f1f60d9a90b31dd0d49972673afb202fed1ef043630776898c49ef67adab577e509a0a1a6f28f7e3d12288332ed022898c6716206176d8c9a61024a9e442adc7adb34ec8046447b1d0af89e0853c9491e871012940af7d30558190e150fba91bafea662a298bc726de678e109046cdf7ca2beaa64534234650af2c3b3f5e39580f8c8288adc95090e0a7bcfb26a7f1d3cc0c815e96e2a1c24bb699ab000e8a08753cbb53d45367116130238a4daea5d766deab0cf1b75376b3e3790f21a524a274bd517334da91689bf73e3cdda52854175b0e0d9058bc99101266105f0a24f29c8ac91420af246a264fb2ba2e0ca20e361414424f9a4529d1b34ddb9ce7af76f6c977df90edf25fe1517815c1e021de8c90025f64a97a0722d9afad4ab8766758d2f4461bfd50c9a91f8ceb27952a466a7f01bf4aebb3ee739ec2fd7e2d0f1eab3336d22b5a8dbdbe802362b14496f6a6c747ac0ae1dd13c1d084eb33d7fc1acfb77623477a5acb3db14f36572abe60141fbd4b56ac608bd45462f44d28fbf432647e2bb105443529eeacc5fec45b665f5009d400c76dd6d24a99a7a4ff2fff533bf9e931085aa92fc5c9ed7875032bb3d523bf8851b154cd57fcedfe464e908c1e984fafeff246af3f8082094a92b8d10302f001af04abaea3892e90d9085a04ce7c686dc64c9bae933ebadf1c556abf84f6d6b9fbeed692fa12c6e906264ffce27a9be730150c76ac24735756313f79fd5693f399bf50c823d5fdd8d7230d389afa31f829183f079f5e68822f1d0e891fc03b0bc384550475718205b5f5b1ca2d4b5c15acb571dd3d99de1e346147958693b1945054ede4800f78c357974c4168ce71a78703d63ec9996fdd4e911cc6cf43a941c14e66f2fbd3c0433497b45546e9e244fe17ba133022791eac05f9ae3d27fbbac14e401ef94321f6e0129fd0b936f4f125978873d5d6931e6fb576a4cba0c7834c7a451d8baaa5eb7149cf7d86c43fd55e3a9d311ea3b297f54193d741dfa7b5d024499735d23e4ed0a810018b7437735935ab0d0162850f01b1be88f6f6124892448377a29ce8e4149cace7186409e9cf99d90090b3d007d3a83ff4b652280772fb9f3f4eb0ac5888c06fe1d7745769d312fdb9ed0186a13b03b13d2e241a1e91ff2371de143e982ca0b4d99b74296e47b9671e3b7d90bf9858be611a5f1d433e0fbb5be5000c03405ca82d8034ad2afe97b6ab6421cb6877e2a8000308f393007a7bf13403087068176e16a16ad61a4b5cd0546a601961a263f922adfda6950131b301632e0ea696c1921c32e654b658285a2c267501907930620641a2a028dac050e0d103627a2a6a154304de401721c3443c231988cecf63b6b853ab5ad4c32b4a1086b4c8b1e013ba52f26c96c8254ac201f85eb73d53a56eda2e4b2caf05ca4c90a9ba417e111b6d17c84082370b7e0a0951594cdc1e211d33c11eefd09653f4028ce32b8697b8837bd56e216e73d71669328718c2499b453ee458745c462c386b8f3f129382e3a4c54217bb2156cdc131286d9b68bcc7af32731a8f03d4bf602a4fbf9e14869f0aac5dbd281de986bcd23c2e00a91599f302ff39f6947aab6a329011f584191a58637c7a44f105d6b39b41947686418dd33a36df60e621c91e2ea9e0558073f7a20385eb359ee016a9c697a17ecb78ca760da0b1330a7de30694c6518311058a8502e3862e507779d14149c4409ae8b10be32d70ba038201102ba80e295da21db101ad32a508796f5e0597a49c03c04f33f9c108268c861275bbb0e12a9d57425832dc42b682d64e3c9af3e157aa4408cc48d4f16a59a7f4860950172077d2b4693275be446499064f113846ef55aa755b06cac23d2179e3a951b528866f2872a4b5b599367e0c9625babf1919b3891836321e74c30a7701b2cc68608ba2ffbd986139883a47e937ab0972719d0813605bde34bd62e2106baafa345c03d6a07b8c8181ec883bdea7d6e5aac78414ae6402e608477a9f3d5d2f190f817d8be1a80f78d2a304e01ec631400b92f577d585967ac41f03511ffc78bb54a33cfc21317011a46c1b076f84cc4f06b42fae1fcfaa409ad5b93e5f6be0f0a60b02ea5024162856e3c263758b91287a95b1ff7d6ef443b6df104560a3fcd454f76fa30a5bbf6aec7afed8950e5e899ceb586b5f7e4f112f5abb49705059d4f2339a709d226c4e8ecf57e300d060b4e753e7571a0a71d7262c974dbd14d16f4995241fa9b9f8abc9305b81022f8747fd0ebfd7d49c6700535909afa36bcdee8cf1f8f5e757f4fe51c4d37b2d23a58abe1027bf70c445d4fe540143f0dac1bdd65477e96679efb045119353e2d8bbb87ebef4ccba05460030dec2f105e4ec8cbdb6237badf6c66c009d4d9c048aad6cf8274b5789c809dc8bf4c5b48141010977d55ed8a5c1c6b1cef254fe8837b1d0617638e48474d70cf38724d6d5e7442e4cb0185415aa02b463002d052bd4ee3f92b7641c1a33e609262611486e3caa3a7671e275e84e803b408c408f922470b1129c58a563941e8d4eb1784a614805c5a56e547b2637062a1cda4e737ccc41f1487874e0309c8c081dabffb52347eef0de26a73abb341f976f82ea82297612051a4f8240150ea5fa0b07c6521572b4cb827e4657c57e9e8e08362e68612514b75efc86b805af9cff34cf78d74f7f808d471506be09d0488297c168917044de93c68c2776e578ca6b1108d79429a8ff8c721a520192bfd560ceca52390b16d06c8b649aca0bd9e9f3cce24d2bf1fc6353aca13f95b1a555740685bf7fd9400615e76b6f64221419a31d5f762466068051630aa8fb144f2f15024c36d239a2efba2522a7d8ab84d563805cb19ddcd91c716b476c0f1538cbd034ad0fa73f29ea898652f712d060cbc2717a50dee8a4f2fbb86a350796e8676ae1da0737a83736090944627c91775baffeeed78a33f8478fa68e3cb496e129e41da07bd6fb2c41f17c7c24f803372c542a0ad7e46a7e0d0c753d95e60e8913ce694113eec95d280ad4b55488db6b71350fc6de4d477aee70885b8a22964602ac0351fd7aaae7c3a0f414239d150aecb50c07754bdb8c7e566470a58f38ae4f6b4eab52ea4ee2b6cdf970e2ee609179338d5ff506d46a52621fd44f5cb1550d507443dd1044e7f22bfade45b97690906d19f6788ce2a4383b64a791dbf395d0b1cf9076b1cda679908ee4a7ddc45a14b725101b386b17e493b0ccfdf647a01f5f115273c2eb9eb9aff8e20c7c2708091262211eddc41434516ff09dbc28c36fc11b19c6e44181070af03b30f8bf4fe41e69b27bc186871d360d27b5775f93b7fe8721b28ac5d814875eade81f8b656c69e00ed7eb1819534d1248ab61d52f84f1e004a72caf5c8df0846beaa4e8e599823d78eb383bdaf47f70ff92ba6b4fe16b352a57b370777ddc87ae43f2b9f7b3a8f5c39a96f3ef7bc82ba51620f89561aeeb936e806e0f3b426a4ef0626813dcc52aaafa76fc47a5c2b943ae8594d04366b1672b3486073d7e298e802078e9464a56fe77548afa6d78f996245e623568888b98e57b1a016f3f15d1cb17a2bef10cc2089fbe5a2203f6c505886104be9b8c6c5e7b3c3b18404d5ee37d254851d495a77ab348f0ff4755e0a38081f1fb738cb5864735903ab0523cfe065236f1e6a206dd86effeb3c58565744e24d26f813aad283c6dca20283303724143ef81dbd25b625c47fb7cb478cbdbf53d6391e4a6d6df4cea26900b91062025a850b70b0feb918ff7d43dcd06e9ac72b0e7bcb43f5db7e8be0200c2df9156ef91f7aae0f97fb8463080cadce9d4507be5cad2ab54adc68bc22e64fb3e3ae2ba58f99e8639fad36371b889e7f899d214da0418dbc3c4470b87552d8508a14b7f7201090f522b13a64b765e2c42ba472b5e0395681c8fa2b272207738c4439377e2832a87bb14b6fbe7d90342e9dc69a3d8051c6fdd602dd5d1f197f862a4023426643e8f0b3361c5ec2fb76cf0e47838fed41ff25114f917fe9d96bcbee1dcb3ec976a9e2e55e8ab01abb7afb39eba50a9506845e5ae168b13b2f55f0d9347482ed87540484d3856f17b81f7bf0f1edbc3eef5b7723b06311d84617dd769000aff6c2305767740a4a44260d4408d5ee0fe6d867ac9080b378cf41c80f1cd3aaf18fe8fcd5b15f203da2df112c103d3c4083f9f184b6ad1f22425b1f188cdccc215f8e0311a8fa202ecef048fa9f97471db630271fa81ced5d8f4788d350c0f1d85832c9e7611a63ba30e31db103e2140cc352ee720e4d6201c01a96173a37e3478a59d6a046d5afa24b3473f368af8bf0dac0ed40cc1f7ce3c1647ae998804bb353ac7f65881feb846f681650c856ae1a2c6cbe71190bbe14c0a948ae24765407b168ed6f25d85c22b7f10051d8aead42d4d915b70599c57662a8d5fba727034a91af6d705d432a5c688a0389ff69164787251c00f9217ef9fa4cf00c2536ceb1c039db86ca2ba39f1b63921b941e1d42c7e98f0e290db381b42d7ada32fb6f4b07127ddcc992e2f178a610d5acc487934e0578d07d1b0a5d9bffb75972851df09b982e8340f18705f0af40143c3db451d0821600952638b05900c965a2ad665938d625123f9ac3673cdedc791d7704c3f318a08aed37e99f7eb27bafc2bc49f93446b28ebf749c27dd644f8fe245a0d21ad06b16b3f185ccabd017d1bfee04c4ca16d92a3c2f8fefe16819161cbdbbbde145c0dfd72491f31d25c6b9fc661a5904589eb84a1c48aff52a360ddb3ce0dcc9cf64740d1ea822a08b2145aae2a0119063906e4ecc3706571d595ebb63ea13034012f21fddadc68769823642b863f9d33bc241523b221ad37dd29893b8c5161422e46ffdbf0e78800eadb9da845c5d5321bf1534213fd2430dee2aaa31910b88427e2c9a42eba7ebc52dab082a09142ae109f9f5bc392a22e4574a880323c0132228988b0dcd98faf8eb8698bb6160aaf3c5322c010bf6e1688bca65d5f791f5236bc930cf7abe3dd00096f3efbcbf8ffab1f34b365ec1dabd2b1f859c7c9f062b49ed5ec2009bf90f50ea4db1f983d5fec538dc358a2ce630a96d0040a8af12bbc73b971fafe33d98a6985f5def9b6c8c9cff80b847f6e144d0ccc5f0073f57bec3432e0bc4d7de3c05290edbdec0777bd31e7a83466405a3dec93b3ca3f9a7ced23c5961574568d370293ffe64c7bf559302ea6bd0fc16c7f59d372fca6ef20e24d26a65efbf96721e4889cb4e610520543158643f43b18598499627fa4acc8632bf9e5317994a99918f8c654d4bec62411b76f68680ade5500215d49acb8331cf63018d51fee0f665d01bc3cec895d05bb6cf8f0603014c2a1eca7afa0a2e282b7fdc63b094f293a1b6ca80026526290d98197b521e2b5175ad6723ec71cece2e6f650dbeac74610154c7c1fce1f0dcc713a6a426afe73692097042f0c1b9b7bc4c4e61bbefe2818a979ceb0404fe60a730e8b8df770815465b5d7ec234ef8aeec66d14ad2f25102170c8efacccff3a93946f7350143368afa0bc65de027db0db117978c073317d4838c9a5b140abfd68d9f1b136937a1edd86604f095f1cb094709e6b6ff66212ff29c0ac106d161bccb4aa66f383263bc66c924793909b3ec7e6fca29f9fcbbb7ebedd1cd0d5102f4146359f1cc362905d0faf32a863bcfc016927c1f6486b31193dd5ca7f3de37561ed4e18f14f39343c148c978b5be431c4349b511fd220e726eee022de4cdddff171d956914f188382e5c83b2317ca21266bc761aae7648b2d7a3243c29d96e3df7bcfcee559dea2f52f31654561fbfccab1390e4c2c6a436a66f9a453f8c2af2e647b577788ee386ae6984d68ebb3227edc559781751ea0320e1626b75aff587289ce04a6a1fe04acd230ac85f9ab58b9994a8732bc22c2e25434e69f88b34f68cd3e09a383c228647930c903f88fa2ca6b8fc7f5ae2cf46b99da9e6efd7c3855406ecd3ab83abdf8edcfda9283ccd95d17b4e500ede81967fcd7798c2dc9759894ed46ae3ac9c1dbf228ad8f15742c2b2efbfdb287e574822c51140eae7983d9958e40ca8f689de5361685c20eff44f0cf0923d8f5b6de6d28c2e0239e15dc55ecd392dc68f1063679df00b8178502d7943780674dcf5dcfcabbcbb2853615c4130b5706aec20b074d2a6058565c0b7a12cd0e6880fcc221f9d6c184ad336ae21d71f187bb0014d600f2f7ae93a764722d60ed8c96e12105a8acc09ca0daa53269ab0da10827c5d8ba322b2943953bad198cbd714bd93c06f0e017104a2c08a56cf5121755040e68aafc11ca5d2e8308d62a4d761ff257a5119a63a485624ca89162a01abf7f0227d8ca07dac6e54fc06b236e672345f07a03828daf27705a77964ba15db181912376b9765ddf9a2a0f62ec03df15083d9931fed94972de4bbaf6ca45018aaeb49502ae6ab41461c6b50e512be7bb36d590068a55045c1753e0c41d92d11e096329a5642b273ea91f7042794928ba2d92398d9365aca43b40c99ce0b2868983fdbb5e3e4091bc516848fb6fa85f6f2700a287d93874069b8600d101529681fa52890e537f331a79ac32b1930a41f18503666875a87be7b3ffb93266f78c182cebde965b2b114b67b21ee8bfdc3486d1cf380d17c52788ddb06dce4c298834dd14a032f791edf2b096f83022fc0d5420b69ebb9d739239f424090556cc8139082419ac90fc030f91dcfef2df8cefae62c01c9631f1a7706681e1139c7d25a2e6541cad2507ceb3c088487e8de6a0d60124f07f32d518c14e9f35c322793b89c74d60b6ac7988061044eff19ab982d0070f5134d2e1d5176be0e189470c26bb94e465eaa37f5564fdd6f2b75fa8449b964adbf95efda31a0786ddbb54e6337fe6a6a0a8cd8b5ad81cdc83b9e42899eda552da5c0fbe43b7bd40430ca4db196904acd59fc9dd6d2d9a1fcd896416c28677ba6b741f49e36cce89abb946c8b658402d3e71673f0deda41bc7fb6b6487bf446e944f04d7cdd1fe35d8c11f1136e2238274e338fdb982477072d596b83f0a4c56bc1a294ab6c403d4e2c93beb83bb2afa51d50f603704486579fca1a56c6823972cf7886c78bd09b99251d1dfe0684bbc1415c309ff09a348ea73477cd24f1fd2a87a78f2cb1003fde4e3b9786600eb4976e066c3f6418e25dc6ee42f9e587de0d233524e872570d07162edfa3770a0049c1c792098b8807e3f23b04569b2e5383648e10fb93b0c8d790e1a0b5bd85a7ac29d15fc5d77ed867f1310cc37fe6e37ca687a518c88dd5535345fb8b4719f20719615a0db182cb74f9576b3b8f1f8a439c04172a293f0c4eb34759cf7cb302e6ad0ad0527e05891b90c3b5122cf06957b37846e12038225f01bf6dfbe94e31b675c36c9b0c65c9db294c0154cad324a36b72413ee68af50c5e8f7076af3628b2230fd72bf44a721bf4cf9cddf65c027d642a18db7dddc0dbf1212ee4327657149c8b0189933b3a83a5da495d9fd6cb0a1c2f4520419ba8a26d79528d715f10335a0d4101d4f6da509a3c2e3cd32c0efef852114644575a7ed150c66813c0b7511e9463182517aa608fbdcab48d152bb9ddcb0494171f5253c86f8ff85cb1c905e4780910708422172cacc0962edd2f9d7b08e56756ef9d0965228d5d7c128c278b456c403c5756467cf3764a5987e0fd0693cf5cc8e75bca9ad130782410cab4d51691bf04f3b4130cb514f4de8b4e1acfb85ef18c3e689b2bbc07cbdd64ccb88fab44ba4cb9d7d7312ae0d4ebf8015fa4f9842c83268f2444bac6cb057717048eda2907d68174555cfc1c5bdd6069ec460da69962490f44e20041f43c23ac04964388e08d1c2db030734c10e6e84b17ad86bda100f678967dc6a9c8523469f1f362c7c1def807f5914d1397b254a21ca9d98dab989fadad6fb853412dfc3f63168e9e253ba61b39b2e3ce0165c5fe2de1730d6028030b144c3b3db810f773ce4f98dcb3ee5084a62ad2d076a12515d790197eee0cb0dadcd55cad4614d7d13468234b78b4d29625faab6768dc806c139011752dd36d096c57a4d9e4086915bb9d019673286ae37b02506283f13f2e272a31dad8dfb9eec4e2b48541ab683441fd5854d498757c5e0b0d2b3e31dd9b58a129671243444a72bb75e92fe7f32779320b042e8e1a908fea768a3c292e09320b415865054d17da8168015f0721de685faae1564ee66900744c59ed23d2a839c6cbf582341f8eb41120661b5363188a5ee9431a8bc29caac697344b6abe260a5d304be621a9a00cf171446d6d1de576bbb97864c7792b05161c7c34f455397eba9c509cac949bf71399bdb9ea97fb38277204bd22964ed90a0c0612060c56f7106af32da9f387ffe107d9d099771e509554c1fde9c36a4b4ecd4358bcebae2e29ed99f953e910b5891b7269cc9067f2c06a842afd18d157f86eac1be24a29c9c436ded017fa41a3a4adf75d5bb1cd61b35b2545070086dfb96b246bee7424b3323f379e3e398a527611ea627fa2423ee559034f58e01c67c1573971e42675fb637bbd0f9490d8d8b0a05275651c73d0f6ef35656b58fcfc8a09ba244e2a820769e1316ef82788093076d1e342e595f2b663217e4d2f3260cc6b1c74d844440bba0e4feb2798c03a0a0d045af4a16f65038739c54ee840df2fb9561d32cc8ff709213191d6631e313b95fff0de876c058e23f9d1eba5ffc141640f445c520680c96e8bf7431062921c5a5eb5f402efa7db7efb7998497e7c0caabbc6bfa51e7311ef277185fec4d8e91e0c8d8438e9c651be58019da1fdcbe97d1a046b9c0114488dd928618f71202672c33234fce51cb15f4a6b2733eedc8590044a72aa83fe8a3f79eb1e1bbbdbeb00bab7b2282e801957a693a4f4f0e5954cf88326ca59fb19c002656c12b5dd22e710cc14e72a947050735a09f168582d782d459c5424944948151263cf65b3ae5271bf377e11f235e82966524b1e9aeed22dc3ae3544ae660aae370792182f07d7767b4cc861193b79adf7c8716ae767b1760f958be5290b6fd1b48b0a12bc79be3a6e2330038bf7268d478f83cfefa3baef16f279a6717e616d586e693846fc49462ee5936632e0aa65b05834ea6fa78e6d4a9384bb905a1fe1b5d4fb6788a109113d9be875cdb7e33350e15d038d44b485c55f637f4cf50b8b6ac9769e400d78ff8f8a3ae6c7eba2a4be0763fc70e6350c6a1a2a64a18fb9c64cdfbd52428255502e0858de1a04ee3297136e9c5e588b50cda1cdac118452315f3282082e86b0c4be17073913718bb8346dd8756a47e59845d9901718d53a7b58ded58f442d2a6f1fb1d35b07fdb0cc2cddc5f0e64eebfa18b0b5e6ce5fe687cf1f030bf7c5146dd74c83b1ab7e104cf47ec0bf01947660ca532a778c02772a1cf4d23827d2b9dd3c79e0b9409d0d91cadfaca5056eca7204ef42ed44eb1b1e9443d81d17aee5f0fcc95723961d6d5f6389e86a1ca0b1ee7a30f463e39954207a5049280d2beffa264e30192325a59caeb9573eae34741cc635bdd2322b11cf113ad00461732dfa3458d5bf81200a66f9f79779c844e20845b8a8b7b67a0e64edafa20858c3567d3fe433a7954287fee03240d983a7cb93b46c5fc80ea253a622866a994c01d4bdfc3e235e551392b064b4a434e40d45950006c38b448ab40ae852f9b52c45604235dec6145f6a66bcd709c71396c903a196258591d6cd9bcb7b7bf70b0240106b47b5949f02490c1450002bbf9e312d0fcda6ce5b27e8a6a82192700054155ccf1a118a0d84c91603ba9075492bc4d380275e43651228582481a1a7ea22c35f20be435a52b664b1bbfaa8bbc86861e077f48c84fc8df7b8eee2f522c5b0e756df61474d3e13a95c6fc96e92aaf5da158307abecace3ca29e5b7066b2db049290491380a641f3bf5a87f12ac448a27990676571ffc4a4d4d1446a7bc8f25f5c5bd609ddd79ac0120dfd0520f4f3382e28dfeab834166f4bb27af544399c117df06450565394917c364a517a7a10c310d18f3bf7ebec3605b704ca42242be2532b00166d6a3534d74dbf16085d493c6852eff9a4b002f6098923ea8707857e1f08087b0e69cab39090569ab59df8e5f1035e4e4d21673831592b446a05eba947988535ba06b3ba45bccd54845d26d36a4a8acf5b6782e12a55d90e55fede0fe5f443b57e811114263c668e91806da972a6f6d3b8e2c66237699a1c5a1ca4396a91b1531d458adee2592247d21fa938709647b1b428d7ec9e783b807ae1f835a8327aefc9f4b359364f9d6fcbcb1c4870c0034c53b7dbd09452e449bd224f1854843f7d54000a7d288613225a6c66381050261fa72a2f9d7912240201ac735ebf77e5210e5befb703ca4a6300d7c0424639ed8d09e06c41ea3a21a87b3950392a2f783faac210726220b492b38f90b45b46e671223379a6e606cbe98bf66488c5223c665151290838b8850524ef971ab8d918c70948a62be09aadea955b105ae642224c6762c520cfc44a86db2908536e7ac4782fd7d2f4c4a151aee54982a1b4485cf6b84f88ccc5ad420dba073aa574da294dcc89232b951f5cbcd89abb9eee2a80c5a3ed2114c21e24537f47c7e52b0555d7db24b300e5acb4375ed269673149d42a5e1e64bd778792ebe96445ed33c946f00b0b1182dfe1aa888605d893c398d48892d0fe6016fa3de9665a7422503d10995676f1b4991112141d832422f23ce245c08198b451c7bf9d64b9989e4b5575dc3f70d5b95746a1b8fb26c7ca9490670908d724c3708996af3b9d664b044f7e4950210a2896d13e54a8c1f7d0e6e72c37b53195b11f30d1a620462c02c65470738455bddff7ffb1cd289338ee9cf3bffd169a7650f9ee481544605c7ce19063a87777f180eb77f908f230738a822bfc675d2a4ec8efe0d24fe7fb14a0ea168af7e41bdc822cd1cb72f73e212f3f3d8cffa62197a2795d2857d0aad8af5771079c68935f2727d1930466652cce0dd947aac90e430b2a3bfcce0326298cd480b9a03f2f721eeb4506f5df67403669ae6c7688576cb9dc4aaa0f7f05a11db6c48af522f74a69b506f740c4ff115f2cea0c5d2a93d8d591175445daab3c24e17e0da5b6dda585683494afb3c7423874a235a89de6ea4dacc0a3c48d6e06d83687faff2fcea016a2822cc25d578c75ac9f9b68ebb531307f8ba97f3645361c921369f25656f9b38e547bae0685105b34d0d0e8cd3879973c2ee6f1d99e63a669534d9fa7baadbc97ff89fa9f161ae23dab1cf8e388d355433c816fd73404dcc6901c10c6cef13b9b5bd7fbd03dfab0c3100e6e3dd88187d78b3922eaa803e0ecbe3e6c374cb85dac51f0233f075337912e82e1f884e4018ae22005352d4d3d159d81bd40a5ae914f07f26926a2af720f373be19c36239f2e9b50cbda9dfc7533208a04eae2cc68431bfa0418a0b976d3a204f71c2820c4ce2db4dfa5f66d2e966114c59d143841bc7e82ce9a2347ba002a6d5777fa5f8588185503bd70362e15a845d103230c240cddcb4d0edada2be960d095aff43f20d68f34271cc69cb4d40260c368066823d03b837e0f44c57a602ca1b70703dfdc05bf56f0ef9acd46bad10b58aaae821f91b114da682e1ad3556f4c28875c2e5a3bee0378c12624825c4cc305d20b162ccf76502e3450747f0795210099606813a90863cea75133814bae7767698a76c016faa3ab21b61c5498633d55466f2d990760c00fe48e7d64417e3a431358598fc88f0b78fa200165b40e5ebd40e35decaee6fc696ba546c929c72813384f43923a4138742a443c24c5dbaaf6184b6badb6dd32450eb05ae99032bc5b1fd2204ee4f1d149f836b084573b97cfbba9bdf45b42f81f25223bd427fa528e3718e6baca056bc083b6f89994c838f910eb9b2ef60e2c3f59f9f05b1c6442df0844cb906aa7aff0291659954c1dd510d9dc99bcb9576e21e297124d771adeab31478adee2e34f27e5352d8a43e8499e2f3b07c51babeb5ae7491b4183a66bd84a1b6b4d124e87456ea612a4da147ad49b5fde21ac34ce49c8ac1e364fccf0bb099a75af914e1f60166008fdef134e3999488461f9bb227825df4ad75ca08ec27ed200cd0419131ba6335bbeb9bd623b4d9ee96dcb4126f427e8a458567b7d36f13487dc140bb2587fca48162adc67cd0769bcc09d309aa6d09b23650adcfe97652e00d1e24f1d8d2105e0523ce3deba68e3eab113c5aa633637cf7b1ee413dc288de57f0588ed27cf8ba0732e5a90bcfef8419f1acf73f6318f7610ab07902dddbd5bf84a4068f5249e6d5f6fb9fce44c661759ea3ccc76c031e814453d05cb71461b84aeb028b6143ea3e0dd4231e614650d452a423b5a601db29685d05370e369f4939aaef07c8c1f73f17a51b5e82296cd0299904a098a7c97025eee55d14b29519e75f12f5dc09f4baf55bb8e58c4e061c80dcee40b8cbdcb662a596e9daea7bb60c8ac2a08053ea37f2584c6a1ef5e2e02133d8abe25848e94e61af5381192c627f54a121934a703ca941684582056b24a07f26597e77e2fbd1946fd6760593e9d316307dd13c1cc5cd5030680fd9fbbe537813459e0f28c39d16d1f823a43ee45b6a9ce15d5c0480fb04a5f9178aeb1adec2563e0008b56d2882464efbdb7945bca94640a5b08c60743081ce4f6734b7e06e248c2862e84f373dbd21591dbec59f971fca3e6421e61422e452bdd18390e23bba217535a0907ee2d6e8112ba4e646253fc3b3ed9a64c791225174c1d9138454e59698ea223ae742e713764a6302ea9b9f1a8874b32b211cb9187e32db9f52475d1e32ff6f1e12e1ab1cb3f1d2c6b05298505a41591c202ca5e7e2133b9118c9175771e7e88a6cb11e7684493021bc62746401af40634583445baccf58f5a6a8818298b28ee91cb754cce90c4cbb821af31847361c368148d5ac8cb183e2c09643e3afed286a28ff69b0d3c1ae25127c05ffb0858edab6133faed63a282cff63dfda33de649d93e123009f2d15e7a524640030e358c407b157c827cb4eff12d763ea48c407b29db47821afa4be0c3dd8ff6d1107dbc411c7f09619deab2b1cdd67991b91c5efbacd142b14bc7a13a2939be3d1c1e7bebf4598eff7bc45f375eba14563e51617fc3a38149f439c206907fe3dba321faf41f21295d59fdfca8e58b928ba568f4c5a42f1e6151f2d45ccd7c03b13457ab29af944fa43c41698d2bbfe5af66e6929116a52ba59371e5473594ba7858572f041bc6a335a6b02c1fc6a75fcaaf76257db61928bffa9939e77cb2f20060836d8761793a9ebb16747c7b355ef4d1e1235e2935afc36379dad778ec53738477a6cc873a2e657b2b106ff968ec816032cf1e83fdcc175aec6bea65bc50478c97144384f1629037642615f6a9172f05ca97f259be7c50e25c2925273f8787c36b216fa1bc76d2445de3f5136fc9674d91bf153971e5ef68b234f453895d20d05f03096e3c0ed1e7c627a58c5db279e5c735983a87bbf27b4a3bd12f0008f5122d574a19a5a419364390cef1252197622e3197984bac8bcc6426a5c8921ca9df94321da386e79814cbe15c5bf7896126a0946294821527d6ba5a514a674b4ab9c8cc94524add23bf5ff756c3a5cce73c654f2ffce60c1bf39f93e38db367bd724e2b95e0eeee320773777777c7648c4ba639991963c6d8c3306c323327ed650c0929e7a44fe56cda22e5e7be45d62cab5ce4dc82512e1bc7338b8f25e900c93927bb2acd2a9d53c75f2f74ab29c0376d01be2264bac7cde71727e63b1a0431df3cbbaa7508114782b5f94a31ba84ad52003fd8d465242cb818c3dacb485850017e81050d70c02209ab821dfac1b7018b28f216635800d104c3304c054b2ea840cb009ec06207af08e78a0757b2ca6abff21855bca06b34acd8c1698d1b97a051458aada2835395266b650bee06537448582cc9ace4dcb0e6aa9e7170552a555293ab4ada82b7204aa56a6eea6d6eea53a92b7eb8a92bb0dcd40d82e801a8e57e0123515f918070516fad407151cf483c705284aa904191aae474485600a1564a6b2e0dc0a5b40a1f5c6a45511534e01e84418333785812f2c1aa38d21281d0c419480c61791c39515153ac649538f2e5eae80834260c1555ee2cc09d54a471670db09ce69c734e71842b6bffc089271ac663871459dcf9727e8d72e7dbd51457eefc0771a494524a1b3421a59472dae008cf0656a42062fe9cd3872f9cd8d003305a70848b31e79c938389020c1c512461f5824d8b5a6b553d11860f9688a2972788d0030a0a96d4134d442726cb830f9d1051504634e1a29c9218628342d4219d407b22c479e1244b41940d0a317aa84318695014e107fa6429b341070c668582a21ac50e3c4c178448a25140c941c2ac86bcc5350a24807061082d5140e1c4851428457185095c2062298a1cb8f0c4c82ba2310304cfb03097ab141145006a14e1c40e4554013a29420758952280e0a40726b4748b992a135dacd0ca8526869e0041145104af6856b1b10d10b12b82cd0013e19db8a2497d31c41bc6a42c66d56312bb20c03e31bfc5206ef99be0faf340c4bf566bc31587ffba212f6d552c0c6dc41bbb1e3f319f51c1b9acdb4b09e2e8a834f6d3b3bfb8b69f2aa5a1bef03b893e33fd98e4d2db3310fcae54e6d3a25beeed1ef81e84f8e27acc47c45b3ee3840d75e8e09d7ea2973e19b1c5dfee1a18c6d3088ecb1db8ae354ad17ee35a6b0ff6b1ead724d8ad0df382284563217e7dea15c9324ae9ccbe30d2c7aa473f529b6ed9e37b5c62536c395060b19f397eb0fcac060cc0a0728512582831f9c497b10138f2ebc962fd5b7d5f911217507ef09dd8b7d773a77f8fdb2db8ee188350843aa5ff832ffdb02f94f383b0aaed60bfb9234e82d54b3df6d9e2a89b882f1a88a3f3d957d4a9e3e637bfe370b4f9126befda59c72cd6e834de19b99b2f1fabe37f968b3407da4d2ccc6524a3265a2db0db6524a3237880646485910e6a58ae09891a24a108260840440d8a3080218bb5d16a52032d60036ab0342f23f5604526c5d32a0c16e2ad9dc9c308e01300b0e38fc9bef10a41ba57b4303100f35e6830e29c80009efbb12b7edff82c9b8bbf79949977ce9106e30bcd205906c18285b1f4910e8e4dc3505046df12843484b0d20eb604218d20323a16383b7078ecc03140eb1aa051a8020b50d244af95ab594dd178de110eb361aad1b204be846c327a1a42b00ca94228c01d19315b380de756ab93d0a3b16d31cae8b1b52933efda36d4dddd1b7377d7399239b20747c75fd8ecee7677f7d8e5deb1bbb7d34c0d5863c0c72efe2ea6408169107b49b1ae07f65e986d15fb9ec6b288bd0cd2458be130c7d2c9132a08bbf87980dc18262d8679956a84cc74b78de3ce89329f64661bba7f5766cccc9699ebe4103ca1583ff5a6645469c1a114aa4255e8052e7e55a494b40dda066dc3c6c7e8d3261b9f37ea853acc1fdfa56b524a29a594d99c2fa59452babb3b7f3dd2203fe52ed863d8d4a2820df2cca47429a5942ea59492c1adde60e3b39452b2942e8fe0a72da69c5272ec32659729bbc8c984c904c90425cc39e53bc628bb4da13ad8f82e6990a997ecc8c86808a329514d4d72367199b2a949ce262e534e2967939499724a2e7236bd787c9fcc2e19e717b99c3c2544e614fb28eb9cdfe3d393b3004e6cbc731ac166e34dfbacca4c6699967dd56483dbf7f4f6d17e9edf13fc8d7a717e766adadcb6d7e6c7e9fde0bb3df5f8c6d82c03468d0d573d9be296ffc74e0c7bbb1f7fe727d303ebcf752bf2965d8fbdf97d27869d9f7db6413965fd2f321fa6d3b25ff96fb3004e3c09bddac70273d3cdb0d9036773c28a0f4337895fcb567e58c18dac0323a594527ea1119c989898ee7e791a2ffcf9617eb5aa55af52a9bed008cecf4f4c4c0ceb5931ac2fe6865dad98c8bc3c8dca0b8de0e0dca059794c66647e62626038bbe2602a0eb7aa41b5adf2d26112192691292dd5b5bf2249e4f5b76ebdf0486b1d0a853a9d78e774ea3adee93a7669cff18ee4b4df78476e9ba6f18ea66519bbb4fad2abecd29ef2cee9b59fbcc33ba9d75ef24ecef61ec63c18df68df9ff23076f59f3cca3c926ffab5774fb24b7be94d76699f3ac9937fca637679cbe7387bfacedf7e117f2939a97a4ec5a99ef30fc9e993d244f5aa6fe6552a99998ff9f92b7fc1fcacdf9e6b2108baf6b9af5e9124f2da47755f68bf7e6ec69389f12c8cb73a72398ee338958ae35e5eec498a8d6deab680edd95b42a53e0eef43c5e3e4ec943f9fb9c8a30c9b7d6cba217d4d03927d614552e443fa49d9a40cbdcff695808a7c2acdaa57de8af960ed7d8006dd47639b5f04491874432925a0529fcab645ee7ab40ffaee7fd2b447c27d32d05cfb1aa7d99e6e6603c8741880dbf27b1c8dc3636e75fbf3553d9fbea755a7af764e11ecb3eeadfdf6c278bbdf5e3e36ddd46f1b6fcdc9ab39e7f9957d65fd4217279f7947fbd3b7abbf507b1b54a47941454d57e39a93af79bd3d12fae10cb79381e6729f7aee1e502f12fa2128a27df745b4efad3f24f44329f39bfc90d0f65cd62bf3fdf621e13e294d645ee6e96f350e78ee7e707c4fcb78d55b13c717840da07a99afab42ca7ccdcfaffeaac121f3f337d5b7b7a341557fdfa00ae6653c1c3532ab97f998d43ccd3391f91baa9f7926aabff1d65f333fbfc6b3dc52fdca5f377ebe8ca7fa1e87f1c2005cd557a47bbea87ff14223ac74431074bbdf842eb32bf585ddd7bf3d5feb6dcfbae1d1784c561f0dda0a8c98811041508183283e3cf3d9adfe0d1e68d8fadb56fd45c3126ea3c947e66f883e43d48749908fcca67dbff4b46f85fafd80faf2bd5b81ded63efec9930dbe62bee3623cebad292718563e8b85af97952ee60019e2cd3ed60af5d23742af0c7eabd2152e98ecfcdeb6ea890436f617951824109b7cba3de6965797df0af5fac73e415d82d82d3ce5d842561ffbfa19e6dc7caed7bbc9427dac3275006d6a6aea72b125d46fc1a873642b5df9d987f5a994a1f7d1be12b4920f957eacf3d15fdb9b9f451d77730ef3fafb01fd45b0fa2d18e9b863a0e2df6c1d4485c7d073e707f57cf71c9fe3f6e750419eece9f69f7430337318743d36ddeed9f568f693b3fcf9b2b080db31b05658c0ed5f0037dd3032bbead1c468cda6c635d87111d6294fb27315e8b8f543a2e3d69f3eec4512996e8d4b1c79e29491a79550ac6ea1fc0ef2e137ec5898efdc39a0629f8fed6bc7c2fc2abd96e26e5e91ecf96a5e91ec63617ddd0b5d855b3f242adc1a65bb3c85a0af0cd87d8a7d2cd82eb6f86c5314b1b4165d6a72f02407368842ce03d2164b785ec7288f36c6529c174497df89cbafbafc85245801d9380496b1ec807451dc391e6ebf6d918b6ddcf95d3ba7b2e06d08ddb0044aee6cbadb77b92f8caf6ddf378c9fbd772c047953d3edf19edee16a1e17a1cfb77a45e8a3629016fb045b3fdbcc96ed2dd333bd6a55c3744cdb4ef54bc746f5a9bba6f9d67a6bae6bd3c63a76bcf1fd95c586a561fec81ff957917f26c67ce10ae60b2dbfea88e872eab35fb862b141842516a13e2e62ed86c74ad1ebc7589c915fc6d58ddc754f0bd51f6859776466e62fbef65d2337333773a41f63a4941e493243e7546c6a571ac4d260fc8c1081e185af71aea051609b99397baf34f8168b62ccb237d2491dbb3b7667f1e5b6fdb66ddb16330f89c77844e784418f02db4410613771fd91ba09de6116e294ed88066583ee99d751fa8829ae431147252d0baef30be79c7372546c3f7fa1ac517af4b420ba31f7eb12b26460e33b958d19bb825d91f259baf7732fb179bb7bccbbbbe3f01a77f71befac777799777787797777777777777777f71fe2eeeeee38eeeed57d453383a326d3b04a3794e54e5d4a05f3728325a3847583663523a324060c18d58b92ee74ea524a2cead4714a68ad7453a26595e6f84e89ccd1a111d939e4155b70f4cc112d34976a15ba3e31724c3774a62e10601fd7c23e397450d9857dcbef3a439d4bdd85bbfb7b8f9618f6ad79365b11b91ec52676e4e3dec2cf7077af2291d8f86347b99b73dd845fb6871bb95c356a3e8c6b5cd6cfcbc894f8f2acfad4c7eae476dc5ba6d6128bbe88568b78426dcfcf21022e53c746452ef126bef64cde8a356b27f1e6f9c75f9e14562391c7cd8845456e9dc8976826262277125b49a9a8b97051f21797c8d35ac49bf85a70616a2791a7978837d1ad75a2d84eb468a9f9c215eb0bbfc64931ed84da3acebea852b3fb81dd1b344966acbea4994fe67333623a1b7c4b1a75094530315b542fee728975a225525cc7c4c4e44e2ceaa4c4a553e294b86c182dd2a2a8480bed732dcec4a5c1a8466f914b6e8cbec68d436057fcb05d0bbba2141cdfd960f943d7d2ccccccfdee9fbb96226c8cfc6125aae9192628fe4a713aa7c38814bbfe1b31c8db3caefcdd3d41b1f4b18fcffda6715bf65aad5ddb3bebd495479388669986f5b66958779fcd8dfbc2eefbe91cbed147c3beb0d2d45b54ca9e1e75d2ba8ab179f577e93289bd4831c62aabd58a777a48b768582b76d9558d0d965ab1974fc32f03649ad6f508b2e21dd495cf383f1f9006654c0f36642c38b458f98eb1a0c4963ddbb24cc5230a214123a5947ed635f6fe713e730dd2efe94a64bdf31174315a44f7a675cbe03124b1ec924f3d1aafa65bf235cfa65bf2376fff56ec929b7b2cff90afd86028aba1c807fdc21a8a2ef5519ffea84f7fe3066b8cb49212b726c1aefab1eafb576badf463cb4ddb5b6bc79ae6f0dad12d6eb5f8c64fa0975ff4f945bf1dddf2c7be70bb61bdf38b4166a928bd1d382028b9e8fdfd2bedea9d8d4de6784dc5309a2465e87d28f6a5d0a04c815d724a4c0576c95700dfc8df648f14748c6cc8615c39a473c2958e01704de45100dfe4e01d156e5060f0a5c32ef9f3637c92d1afaf7d3603a25f310cab7269cae0d421a961c69578d4f9e062d1d6f9d8ae4f28ba3f82f5892311b5eb9e4f1fa134189f28b1db0c341c894e78a76ff7f18412b2dda35efb6c72bc63bbbc46c0adce7e339c86387d7dfab1566befe9749ac15f40521f0ca8cf4883324a2cf758f730af7ad99dbeb3dffd0cfe02bdd57d4d755b87717d7aa95e4e24e097eaebbe1b724e9f0eb77b1450b7bae77ff18274ab63c1ef0a7cd371dc6f282f88f5f8caedfef49da058ec27e8af15b8d53d19b7ebba0f756ef7ec5de7eabea79fd8fef89143eef6e985ffe40569b0fb976e736ee66562663e2b1353ebdfeebbfb5902d3bdeafbf156f7b103d260f72727acfcd87d4c2452fda97b98d3cbab4e1f0bc54259d4dbd3a34ef6843afdca5bdd9f76b0a7eec3d5ed7e86ced1713becbbeed9a6fb8ac4a2d9a726b82d08cb0daa61c615ad8b51b4eb9f7591c84776fd6b17857cd4eb12099273b7813962a3bf69596c162bef64cca33d6fd66b40b7e847af278506290fbb280a7c43a906d84561f016cd00dfd06ae90b7449763d2aa5f4638c315a22fea2f1894486b0f6535f14fa58402fac98b7cf3e2f1fe423559f7ad5c7024ad1c09280ea6154413ed9cbcbfc4b4d7d2f1f7b2b4e919bac1cc795b7e8c378ff42e7d00869907e7b400d6adf4f8354c6041de01d19ec0698c79f3e911778277bfa30748ea66d377adacb08d9909788f00eeae96780794e4f5f039cd33dfd0e700ef7f48b70cef6f441c02f1d6fd19fd1e1ccaefa272ff24dfdce7306eb731e65b0fee66d0cd6d7f1178a932e7df6fada47fbeb117dbff42fdfd0fae9b08bfe098adddc7ba1418abaf523c22e4a9ffd6fb59f7ad587e32dfa2f40f653f56365f5b38f95696fa495b4ac6a35ab0f7a8bfe69079b42e760b9d90734a5c1faf1eb740e4d143af97067add507ea9e7cb0b171b0f10ec7925f5cb71ca65b1e39de9827e31b7faec1d6b2d8adb9ca2ed724d75ff4954817684a833cc5c636dd200873a3f6608b5f37cb7ad8254176a11a1004e3c0cee96f4757ae87777a08107f116100ef58805d3c14e01bf9d2524ecb62b51b72d6f9f00360b1f236734efdf60360b1f5fde7e9bbed39a0d367bb49e9c7a2d91bd9329a51a6239dd3577e922c56de21424e4416fb285bf7eb9efbd67efbee7b0e48fbad3f963b7d23ade44ddb9b8f72ecc43a23421ab1c929de6ae951640fb51091d785c9859c49cb1253176ff563515af2566b51aa4a995151d2512735166ff5917614c588e3a4d745de92d2eb28a827442dd44344714888a94b149ae1486bf196901f18881c252d49e9e9e000e9a95bcc13194e7a9168860501f601bde5ffdef228e4adf68e784b7a44bc253d21de92de4f0d273d203838e9f5e4e0a4a7a383931e8eb72427bdff38e9adbc253d6bc349af7a4b7a2c80959dc14a8f0501f6a9de72d90918f216c8498f05243f18bcd51fe4b371ebb4590103e946302e237df1837cc55c19666e7c18dee98f30ec8a32d4db57cb627d75e58d4fead7803d7d09843dfd3e1616560d45379c6fa495e6d785d2f53fed6081a68438389d1373c4079106fd67f7d3e0ec6910c7877f83cd4bb6f0c6652d872318bff0bbf1f9dbbfb0ef3718afea200aa101668c31c6c8aef81e5bf16f7c8c4a4a428ef4e0064764ecd84c8012853ef56cda61981c815235246717135309619c77c5894f66b0f20810b656cac4c474e4c81115143664a623a4e61df9354b42a6c1e460436682b2bd98c15c8cf47041709d92ee74c56e27948a092818daaa6cb02133f15175e94920423a8bd44b1362528aeb1893224f04d231e945b506962337f222214474705216757a51ca89b261d5ae62d738fdc2f9be756c5ec13d874d32738c07f0c2c2740eb78a1dc6b0c63f295629b4610392152cb0e189248c80c40dabd665a4329eb8a7cb483cc8610d185bd1e83064b208c21658ace08c2cbef0342a30450926306083871900c194c4134af8b0050d2c43284388223ce1c5510f16a5140013765eb69203130428b2f5b2951c90a04866108159c961e86e97ad3461a3d7702d6104932266a0841437306a1fb41449741835a805549a8cb6a8428910d478c2a70a2a866842858c13302142900c94a0820aa32fcc6062061a407409c3084e4c21c4141d7079820344d0c0062e6010c590920fc188043734f1448726286109679cc14df082cb900cb890b16404dfe25a18074c8c8104222ba46c9101ced72c965042b542c4e4486594016385480757bb6c85080b256af31bb513467d74e5092b188a201714c1a8054760b1c610429e250442f0392aa3890a9a60804614a38612436432a4022f24242424b4c589932daf4587036891afc396e82de4d19b9c6c1c5fd873830d13bfc8c3374c126f94427fbb51250ebd0f76e304966f84e996cb1b3e08aff2eb7e649c445edf912de994dd4a3d61e3cf2fda25fcf894131b8db055ece5639827035d210addfeab55e6d56fbb014e56b5c7b4ac22f19034d03863256cf62b599379d4c3784828babbbdddbbbbe79c18f6cec4564694324eb0c619647421c50bc808828c1b968ca0ba8cb434c6cc65a4a5a4251a2c4959ea610c36c640c21867c85c461ae34ad218518cf182315430c6500e4968a265088b09c4c8811855c49881182e10e30731726023e632d20fb460f9c11596062eb8302c38228816fc608805b76b821117264b142c2bc0a2888b824205b7a302e5724610714f3f5c19228b10f7e53252184a9edc4e8d2b29c88282ab9d11c44d8101c44565f1c3f51b5cf1214b0f17ab82e8a68a78b8f40527b8d5881d6e0784934b2f23f960071dae652387db3135b95996a1bb8561828bf10087bbe180c99d575c5992e5869b4971c5862c4aee160329743b175c69cad2c69546b071ab1057d6c8d2846b2f23f5a0c915266459c2dddab8d2258b126e97c695247041421635ee16c6e730c3fa11ae5f4602030c30ae706a051ea51777f2004540480bbe04549f521a238db45aa01f20ef6d9a4cc005e6f1e71d5a7fae1a9c5f6570b0212bfdb4108109580003fcd2a94a3ab5cef97702fdb06b2ae18029ac341f5c71346b28c5408ad534987d2c1ae96fb4522c64d51f33d6844666c4e6a45fa433624f2316e7b4737e2c6c3ef6b1300c9b139bd954025cd5ac583435362be660cc38b2e2332a3a02408d0d8b66b5a259ad6c914dcdaf56384c4c61f31d0489c5528079f855bf21f4931f0e24f586b4101d6cb8ba14c0b1d915c22effd313db92597eac39b1297f4a39a5bf5c213e46619198babbab7588191860977c29dc775ab86f488331604f7f089117302f89bd1660579d31e98b47f128c3c17182e3afa885192bf94f6ad147b153e2508348d90c6c4c3aba72a59d81893c1790e11d0cb08b89020cde0879626bad4cb1c8a4e9c0daa0209ffea98951b3ac9ed0f276340fb0f22bc3aa579dc60acb3ef533d495f5ce2484628c1c86b591235e926e1561977c0ff08d740ebc61977c29db73de06ee68d088c300faeb87d6c73a1fddfd61970c13ab02bf56de927f02efcc977fc32f06230ff6f209b023f2642fbf00bc739379ae1df6d26f1579b0afc53749270800c3b00108615c6a0a51877fe8b44efc8142ff40c17da0905d9f73fa9c523be9d0d9d9c8ee0737d8fdf8f958f7c39b92310c8b18d6da09094a763f5cca1fddfdf0ee47164baf96c56675326d6cc3c1649bc7d91b564d567bad734194514a83505c4249c02ef923881995523fab948619581d1d22f3669d8feee228b294526efab1d7e3587772c262f3d3316fd8d42b6a566b96d55a9bb677cd6ad0715cd771b33d1edbaf6aa66d9f0e8ef6e9e0b827b3a4db9e28875d591b1362dbb8b94d8fe0abc5add9fecdbf524952cfa32e62b14b3e0ddf14619eac12bb5babf5c3191b1c21754bbe94ec352fab300d629c7bf18838c58b4b744b7e0b8cc06a8c825df2e3137ce351a278378c464652fb19fcc57df1894fbe51f7b410e9c355dcd0fa0d1e6558aee2b27bad350bc08d4bd5ca0b53f10bfb6cb8cda673e263fcc2dea625a57c7e9e99e117b317bf5bf2d3b055e9a773429d2b5f2fa49342e7c897cf2e798212df0ba5d58a5d2ef4db2b1f8877fcca1701bf70b8255fca9608e057c8d3b15f401d060dbf70bcc53b4da441f92f6ec97fe1c89ffad4bb55e7006159527ab9e1eaa66eb7425fec5babdd0a7db5f7a858edb3a206811a940f34a48508b04bbe8f1f2a4a1e15cb376a58b20d468679b19734d2ce86108d146157e5f100dfd49ae5f0e82e07a7c1fa59e61969100625360ee1a0842c8f06eb6799bcfd5e03d8557f93c9c1cae01749bdfda83fbd4f57bff690c2699dd6f568b2213788a37b97a74e2bc2afd3179d74ab7efd803882f635e1d6df28277383fd10e7662feeebc7a1531c7a20feb2df0a25843d7ddfea22a95d8f9f25a8afbb38d460fd9313566bc2adfa462c0961939e685eb5ded250d93357351dd8556313bea99cb7e4861208f970acec51570af771675f1c8252638dde12ee71781feea3a269bf753d32da0509ba5aac7555ad91f9b1b28f393ef9e2a2938e43cd796c0486ce89482292be8dfad3671f89bacf501fe8adfaa78cfb2efb581a4bd37ed3b22dd3b2f756cdbefa30b06ecdadb1e885093892b20f549f94b08dc958794cff64171bdb586b51d7b24beee8168f6ea598a7a5e29d137ce686f64acb3b280cfb0dbba134bf08f65549c106a5ddd1a0b49db303a6b31b3b20a306ab538e51c29678c7256c138505ddd3a3b03f7d2c20fb27d59f9e7dec07f954d4a7b0477d2c20d4a73ef5d150c30bea837ca27d98b7dfc37c8ca1de3eca4e89ac18a3576f4d4c27059dce99f1e8d3c6e8a96e276f47b7e6470f64d7041d4ba1c1d9e3adf9123b256193db61d32d8f6fe6a901dd9acf7f9a73887d0cf08e0cf602cce33f5fc810dea13fbb1e18a555767d213abcd3fd7c149887fbf93cac02338073b297101d1b73dee63983d86b1e6510fbccdb18c45e88bf5013fbfeaa576de67c9fdf29609f0ebba6ce9d18b6f2d6c4308c6215a3d869074b3f7c1bfa014d9961a60e8abc314ab13232d94894813b40558a6f60bac6dadbcf2cff7eb4f6406fe1780b356def4aaded1c2d126141bc96659a963d517dd243dc72a795ba67350832ab35cbaa740fc889069f68f5b34dab9fd1da5d295b966173238e1b639aa6a27ca06e7fab2817a7ad88b26939750bbaa7362ccca79efd25e3431f27a394f60edaa53c3ab90da6e63198ba55ced2e7b1e3045a6586629847867a67784786d5b5abae5d8f0dcbbebb8a79dbad35dda230dd52a5f8867efd2cbbe8cbd86035201de407e41dfbf45bcc7302e7dc20e0456774548b3a751c10c60246ad2bede8f73418843a901d0e82dd0ad86b0ff3aa1798afaa52ffb2bd9156da364e92b9f483d0d73c5ee2a688b2aac926d53e97a27dd65e0c49e4bffc9e35bc722fa2b6a8546d58743de910d100000020004315000020100c884302a1503420c8b2de1b14000e7398486654170aa491280762140541c6186000200018628c31c814155501adb64ad121e68267de05dc66414b0192b2c99904d78b62811c63441cefdd086fa45d23c4ea2dfddcce1af0e3e2b9ddea3d2e65bba93f07f1c59389cdc74e88304acf3892d9bd7ed743c5fea13601808dedfdb5ee3a724f371604e9c3c41cdd767795a0debb90827bb90a030e1cc1dfd6227666abe0f2476168b85d92c8105e546b2f625141bc561002aaaf419ad79bce52906ea36db067f465719d7696e225044a49a1944f2cdfe99b65d335ff143e31df13fd157505f6c8d55161dfe7a963941dcf6f1784367056b8ac3af7f5e79d56a67d2f31b99adab1b456bcbb4c903d55af6a1b08d975cc1839f970c4cb19b8540775c70c497c8614df17d3c1c6d92d3c4d6a393e768969844f72dd9378672fe1767d6bacf8ad3187b109d62936792a4714f90f447c46d1e2d714ab200cd582025ecea6c41c44b72991bca4827d5eec13d91c86d14333279be7f2495e5cfe611bd1395a911d89829d4138382b157cd759061644b49a4e62f79a67d563ed009d4439372611ab04b89027da8ae8fc1ff9e26894452a0dbbe52e816fa154a9015b680808e3231f1967445fa0b6cade0e21f5af7a2a63c6f04da34723774ec0661b00eae40fa42b09e00adc0c738033ff5aa9da41b66d1628c3473c368cb861967c284e6ed6536b5c83e56f0afa8ef4f7cddc1a78472bb7cafdc0cd1ea4be39889d426dfec678ad6e36fe4714fb0764e72713109a3682a42799ba8702b186ad25858f05f5a92721cae4265b49c34890713baeea52c19ed0afc09751e5e6ef9a776299d9a453d27c28b6d2355b92920b120a556c08a7573e95b04b50e1768a299818d10d1b0c6c9094a98d6413960a1f7d66434bbaebd38f3365d2a4e280a540453a1f4223920964ead75e64f80f9c31489363b51bec9705cc492215a5821e42ff28aa40794a95ec20868a655ee4de7e1ef6aa196f3308396955d43125fe9c24ee23ccea8dc5fb48a9b02aa67ae73fc284c2609569dd126f259e793a6f130c229054df76fe23a5da284618ad2a2dff2317080f55553d41afaeaa8f31e88530de31d809fa84afaa4060733b7d1bbf0812ed7d9a4f721a0b43615996bf1a0bcc6c2c9be85ea43187100ad1db7a5cd23cf6d3b7237ea15d8920f8b6e0e4e44545daeab9aaa6a6ed7c4376879ae63d2a103dd7c5635ee02e00a1ba53d33c3261721a8cbb66166c7dabed3e7250b2a2a6ef6d24b56610f6ce4092ec34e08ea0665aa101b9c74981c65fd001986baac1f9dbe5571fee8f6c82a53d14e384038a60c7307d62d8b99de4e9fd119b6f92533eb4f10b70844a04b294dccdb7e86271bc4248c5c9bdc46104c2ad0a53bc49089f8ed65274b951adbe23bca97a17d3c55b2a3bfe96cea8ff886e2bdee6f48d59ac02cb337148f8447e94a314117c400081ef43b57446fd8ef046e1073101741cf7fbaa0f1a426cf4d4f6d3ce32478e9123d9a85d65b97488fb2b10ff4fb208463a19fef524dc6d95a7f517e1247a350b3c63ca17cc9225685af1777215bb009538521557088e156155fde15749a2fb1fda51ca913828e380c705dd90b1e1dfc973a3131b1fb72a9273e09dd2385222a2e495d4d3a53572a27eaa1238cd2ff51cac5ffcc2c02dee07d88becb4ad30ba7c4c04939deefa2503d75407a767ec217e38393a0d2ecf49581f5c84d31163c62fd866c581093f60b2fae4815d615ccc74ee1cab7492e25400f5556ac244f267636c9c4c5a21172f42a8d6d8eec256c4133059d5c53e682798c6086212a1c7d55774db521a0de1cbf3f0492386efe7d1829b3be6d50b1989f68af2264c77d78fb6a38d6a9c27006900141c17f7879b60a74e64ee56fdce02cd8d5c25722d2792c9aa1029170c2cc372bced2357f48696905c5b53955c7a659029067ae3531fc7d7743cfc82d23c48c4f99892ea7d184ebd733a70c2ddc33752b2931059f67e41a0075b4a3d17bd2ff0d11279a121c0060dfc30b1a449f612e33d5811f7c63a03f81b633dad03f9c8c43144ba623cf0f60698c9ffc860781e4b6f04f7cdb33ef6831aa06f4f6824798609e339957358a4ed42f6d777928c205fa88959fe9dec7f8532db336203713f4f21d6f6122f9eb016717b46bb9d8860ae03f2d212f52006a300f31944876959f48a5ee16d91baab620d705407c31ceb3a6b42f00c255d2f3e828625d3895b49675f695e8b66fb98b5d05afd4ad7ffb9888231160f42447bf7eff30819e938f6a52fb9c04363bf97f4c5414a13b208bacdc17ead0456093b7fb2369a244f849654e9cfc340c471993d1e3ab3f4915eb311a22d0cece2fa919be8a2597a4427954bb9076a0ae69b0469c6f4ba1c2e318db7fe076d243429592c90a360e2dc889d999d7816b9a63b122e48508aa84dddbc4d8426aeeca2bee81e187d8e2ec0b9b82e9a2c9180968426a6bfdc06cee6d17abac1dde03a9d6af0e0874d72e753e993263b2599500d08d944ab5564051a10a0e536a7a935297c985d2d09317920b5ea574b1c6c8bec7e6d7fdb98a68581de0e38630036c1409bae8df108a9707885b5cf0841d69cd09abf9996b5e475633d494e1796c700dcbd74d64ce9220c66c32b3318a4b1d38094d572a390f65320c24d4888c4645a8f34dc7b6a50fee352779e8d90e9d5cdac20e0515835cb03a819cd61724debd0383ec328abe6512ca6ca1d684b5b019cce4d17ce46e18a5a67051498bf3c42745579427c8cbd19fca8535393bf3c0c30d98f04bb8a1c99dc07da1bd04d135b11285200767ed4373f87768a660c91e51fdb1a3351b43537b613c1fb9897b4daf87bf6fa10f414709dd2eb20176c1b52428f071c987b563edd726f7db9bee413acee6ab92da64de4e204dcc74dee9719701c95c6e4a7dcfd5af771184a209a9e80673226882b136a71e609ded70c7293ad46c8ff9c02da7c12cb3a4bc537060ac28867731a823c30cb62de804a186026e937c80e8d8880ba0be97ba24805564deccf2c3189b4708e543a4c873322aff11800904e9f33992cb4e7d1ab04dd12cd9fdbedcad5d25ba2a1880fd567ea77c9c6fb1f2bd2f8e183f6e3661f61095d5e898811624806b71b7404e5dc74b1721e353576697b85e24374b78019f0682b25c779f32b527ad3ab93c88f129b3e91629438eb288db08a33af8d489679378eec838c52b407733003a8d84b998d7aefb39b1c8ff18a370f083f6387460aa09c7450bc0f9ccffad59784491efb640622727de772c5c53b83bfeee59482dfd0fe0fbd8df278a45fe95bbd3494825c2edcab25571d2ff195b756811c65ebd0ee80dd600de274489d0478a5be74fd39a8710e7f8883209e926300270effe1fec05672780d41612633cb6420debb15c6145af01e1cf8c6086f233dff5925dc97cd2a89d2737e74b0da8c0486db67243089c587de1bee42ec1977d1d457f9b75e6d988f6918108c7903495e191c21938a52ca0c9a9206bc47c4eba798be72f9126bea561470f21dcf76cae091812455de57f9ae30636ed4780e78c80b3a0b51ac20e4ff375b4ccc4461e33a8f9757b5442becc0ae8e6b5b44f0baa99036063b7a01449fc12f37561d1b4abffaf89c7c334fdb47ec4c8583da031303907e0c0ebf2822dd5a76606d278a1290fb30e62c38198f6223336d8d446d4a84f0bbd96ea93fc0d692e291744ef4a5abe72ecf1c701ae35ea15f7ce98282df4617edc6792a00342b698b3cdc68cc68811bb8c9834dbe9e0e80d0cd163845b9deb2f766c63b035027ca22d3f1f1dcfadadb8f41baf53035988f851911e8388cdf2e14ecd1836bc77cb2d42ec19e2fb8b618f8c753a78ba803d726d770eaed0d2abec200ae69c04841c15acf60477b213035f72181ab0df77b1950886d7508753c4fdb715f84de16f81a75e871e4c8eef68aa3afcbca306f854f66fea2058670914c556f22e4a605436e3c7711a6f46d5689f0857025c46f40d7dcb1690d86eb0ed6ef3fbe5e0f2e675455277bfe33b09faea9cdbd144a8ecc79184ae29cf48108699f0b4546079e4ae536a8ba24518ebd7590fff3d5acab0ee3fd99e4785c854bbc5fe7da3f5e51385d2e4cc359dc6c2b5c6007cfbf530e4f6badedda96bac0a8dc5e65acc9060fcd1e884af99d102840b2889fc592780161a9fc48c9ec92d1135bebaeab1e128bb0e2f5a2754031f01f99813b925df2c51916e2e5916f54534ad849b34878346003e9e8ccf0862e789ad4622e0f787d25ddaa0d32b567dccc81cebf28d4d01ceb64e80ff753e06a17c926f695e315c8eeb067b0aac0b1c17e279a274126463c1405028fd83ae65490e83d07acb41b2bba5c77791d0e30cac1fce9593115b6c3ab017f630febcbdeb4ab1c8f6d07a11303b00a2588377ca35d5e3249d05b7df5b3a7f4e9480bca93a37c5f3fedb1c13033cc30977e65451a3fe467460782ea48f33c064d1c205bea5ffa8c8062a29a2e099410c688dfa9a8bc4f5a2351bf3bef231a131067799eb8701bc28dc269b4d1ed39974520347759ec502f5f64da015297d3d44a061e3980b1382529b252f07a8a5250cea15aa665fb39567afdb3d572caf7f4dcaf12c9544ae24cfd6e58c1665ac9e7e1c2ad8d443dbc5bb11c29cccaeee8ac42ead27cad770c705b238b0194f77d1224f4ff98a3f5be76679f43e25fa36fac564ba76f7031e90c2e784569c8108926ff075cb8cf2724ae60015922a80725d5ca5e1643a09baad146efb7067e9775a7280d3018a3669f97a2df154d33d91e2ff7915a4a843e9101efc20aaa1a6aea475f8218eb02e890ad1c1cb35d3128ec83d9fc7bd90fe5a986c6357f2f5c8ca05a42dbfa326ab7abd56140040ee4b61880aeb680b262f26d892d77bb8201cb17fd3ea5f8856001f3fbc730d37871d2ce69bb586c81023668967136e0855989f92d9fe7e00fc4115af2a153ff6fc50dda5ef69cb5ca4449242229cdd65bea42e6b98b1fe19b5fd97e2cca219806b17e256a29dd21abf2c5a85aceca816ff5a0c047b9a61ac37eafb0745fed28bddf11e121364a1cb3bbbc25dbde661380e0e1b560fe8ac69bab1dfe152fd34065008520c9b786783b260a71f5cc0c2e2ea9181258ac2ed1a304f87482e43e6bbe10be1f62f503d9bbd6555b413880ac444e5950c55824043303e24b9a14e88e01f039367aa38829dc22721d41e9087c55132be348e63d79760ed90aea7594db9092374ff8bdffc17dfe3f900f918b50be49a3baeca61c67aa5ee82a44e4a4b03eaa10d417c9352f79377dc556e52fdab60fb7b7f6633243665d9cbc2a5241b08105b2cb54003d221bae9b613dc03494c0766d500442ded0e59de875a6c587ee0a74f2aa900d62133981d7054a017527e1f0f2a06c24b0d09d98d9974d8d5509d26f0d3c5afae09ac6de5252536db18b420ccc31649a76401ba124e338af97d71bed34ac4c443d3a945ce163c4a5626061cdccea9b9bc5257d612abdf0c8eee23157458c013cb859286cad6913651d62ac3d81248287f84b32b7467f315b964d1334658a30c4f14179e5026ec2c30e555529436899953c9042b8a196a30443e8928434c31bb0efa6f29c2f8be7108532c9c2b2b46c512afe309045c5c359a7787ef4fdc5a47e10f4a8b7cb995cef769a4287ea4bcc0cbf885d16451964d9a120287346f660b332662cc7b009228c2e8ea9f60038f4fe8609930924b6b08bd84ec56ef9cfc564d605c0cc00e265e7cfe14cae009cb6834f16d93eccdeacdac6e7badadff84e6c80581f20f8e006e0920d64bed46d7fae47a365d89ad71888580282957c19cc55afc347edefecd2522ea26e9562f84e8a0efec7f898c7f05b82743f8330556be75c0a83a32f0a3606e18b3ff9f21b94ee15ae92f9909564b4a664fa72d8ae413223ee34f5d5b3235e659f265839a27af614ffa38a62d4b7908e526cd4e302122709f68f32f74c44a55ba2c2801b66cb199a2515823dcc7436d4ffd033d5e1fa0cf433b2288df9a147dc9467920a29652fcdb29c7d717dc576943ea90ae9b5a393d6b8586d1fc1477b670ff67fd09b5be4022b7abac859474bd8f8f3d572cabb9051ca547247b43489d339d63655bc6d135e4da61ebabc149f9e78da150e02c0babf5038f0984c13ac07120f20ee09c6ddd03f0793a6e622558c994e7350bb00e8c2b5289e6e87320ffcfb3a3651291984486dca9ec8c2026bfbcb4d17507c28e255828ff01373c36d96c97cedecc0f218558981e52cc73e11745d12c98d7911f1c725cce10339321080c68a01c3e2e169e025ac61e52c0dd4e40ed7c9ebe735ef9c229d8ea7602e75bb6746c2147b735a8f6dab5a5cce634f36a737250853531be9023fd4d6412b2ea58c16d055735928677d97ef181c51d170a405c87bdbd0303e102542a9d9cc2bcf71b7ad1510d99a6e46652c3b8814a911bd16ed4a7d54e68bf20e3139ada9727a4e9a9c38266f73f25e37616b53baa7d3700fa4b8be61852ea87ac2866267e524a5f6ee3bb7850cf10c827bafc0e323f1eb2172333df3fac7db189cf11534610db4d6878f9488975c1f807ca7f89490b55688ee625de50dd8f723baa37d137d2cb7308955aef02a312aab1c241566c843bf93971b4a0c204b0368881ca0f66b5b9315e03b1c21b1b9099cc267dbae4c880810c1b6b327dcf250596c7ee5b245b8f5413cc7ec90a515690fb5ed3d481a4ae6f921227349940c5eab5055d3ee976c2df0f28f139d11f79e009ac4356c279cf2cea91f97cb5259350bf1d01e415490060ea217f56038b5eb9720beac1f0a1580333275cc8321a48f3f56cf51a4cde698c6ee246ea9720e33741ce4896eea7e1064be8e5f1af395463b9f5f6c897e46a50beffc65d9de65f9a2089e92ab7563fe32447a76dfdc327447f73c7768904d29ea15a8fb875b887ea37cd48faedccec84b239330201baba091952963db90c6b647284ff145022c78827f368bcfa805afcbd21dad9bb5038dd18a525d890d66dadc241f6f5f9db575cb72d7aadf9226ab13f21ebb5db2531b4ecd5c5128f533a23b6a0f316bcb43b1c1c9cc7f9942b4ca940591cb9e30593dba7c433808c4f8be55c8ae40d9d65665e5e62aad402a16067347e61199a6ea79cc66469f814d222d5ff2bb7c9b8c57abe74cfc6b28ec2ff16e5f25f651950b8030be98a3471a2526937a7861824678cf2ce12598826f90418662e0bc08f16b98634d494152d84ac034164541dbe4fb9cadbe4fee6b7cc78068379dc47ea9c254c405103b6043ec3fead90926710f3308ff84f1136f67c325046fb54929860fef5f78814810def2b736ecf4905408dfa1edd3b5de549bf3f1a4386ed588914572373e39f97624c928c7d70ce2163b877e832e42e37d95662462f1a151f92456c6b1376575d94a073e27e88926e56164c9e080932201e22870aec4f0c82d17db623ebae21e5737c31a6b9d2e8f7100f88439071749e7fe15ae9019e9ae42a0f1fde6566a86c70c401fa9ff88bc6c56ce0d1291dd59ca6af157694989109ab1135f55a0bf4a48848a84f530fd807566601257b5c69e65a6a604c1629fd592047719a9ad3a62a03329ed2cc44008eded2255f5788fe8b5aaee082198d2b9a1adeabb68c2653b8f97e223ff0504209cfcc68b282f391956f3119447462097f8f0ba3f829baa8a2e753b3998e04b19eb69768d834492eb6ab304eebc55f81fbbcb106dc30f6c73a519118cc5c727402f623799cddace960f82405c571733987fd652151a3e2dc672a6c82c8cf00fb5b90d120ea5bc07bf90052ded24bc664dc2ab48b011b8342029f269b27c25ad618cacd1bedbb6b08d15389380628fcca383855077c9f05240b84a5fe97bb1953970691b0e8d2390119a1fb73ea4e2a7ee2fc69a47de3be80ed10cd7bfdbdb14595a4812c1b73cb5d5a5ba1c22541ff01ddcc21637b4c16039b5ded8fc5898f0fb2a61f8d2f8d4e7997da2c1a87d294411c4841c895063d1b18d2bc9b0dc7622c8eb38e4b6eaf2ef320c2212517cb0355b225730078d29efd51fa19501929e8404d46243906e2e2130820ee00bd1d849608dbca4c3a9e8af6c44e94aa48cf5654545e9ad7dec1694481feb72680fdbdbf95c7b5ada979c17bafb3627669d6b9fd619aabf60454228a10a99049276403b5ef266d8eb8c6d72d4ec508fa46480706267d42db00390a997cb11ce81dbfc0f933c6269fce244e40d07b56c8f0044ec12f8a0b04f841f5bd44e0d78ca9ee0f15fbfe1f46c4afc03960b83c37d2f39cb0d72e32b1f8f4bf484e0860a68f0d8b3c41ddf909ac4d5b3d7c4efc8b43203dcf66091b18d51d5571c1cc96ad2d143b0af68bfab3e5d4ee37684807983a730fc53308f6defe48bb53c323e088b6577647305e24fef966301e90a8069f6baaa0b8f8dfc62ca6e549205fa498f73f5a072a63fb44b9a65c2c9671d4e2449d69e6152e8a55413f93f1d45fbe3807240fa2aeb6f1dd3b012952a8cdea136be7f017723423763dd2e089475f1cc07836e2257cfe8d6d87ecb14bd347803ad37dc04bd4b2373e530869e3987d28e0b90da2b6e8d11e6cd13e2624a0435dc5918eed3963801269036d9865a6fcf642e7879d00c41e3acd8c1eb2aec691f6f09175953d90e4ea56c95e12cf98f2223f98da000b884322cb57388bde3d7d37f2fbdb1546cc2a629a8fb19cba1c6e8096f901986ef2c866c542a7d55a36a6bc989714cebf4e431d9d4e22ff30446d7fbc0309b5031b1c02f9d325d223f547373c4ca349e14e85d4a1b3088d1c495b2f512eb8cd15c2b4ea598736625782d600b2ae7656bd9342e3daa030e11f6b920538960f0433605e6d4c429c58e5b5ed7bfb49d1eeeb626c5adb2a3bd190add64b6f9dc3ae55e2967fb870b36e23b7c15a9ef35868843a7040a0d0fe2c8c274b532400074cd3e7589ca6efe4e1090bb5e41edef21799337a627531afbcdf552abeac42877bbb8dc9d4676f5168d0bfaad782f8cfd708ac2ead7ad138cb686279f1fdf58c2adee4230e4922ff48ab7bc062f5c6918f6a5c6de614613e9845d2f0077fa24f9d1216161a06adcaff4a07ec9fe0bf3a2c5fedf35b591b95d4d828f73c0e7f59cb747b99dd3433e5e7ae32e0ade2052abe4e2f48e1b05c76d2499cfcef01427b00d67698b8c733dc2d75cd9d3409a95111c306b748692c9fc74350d6799be551adaa09f1ad6dcda31f5e460f67e06d768e40982e7c212b64c78ce98542022fd9d591bd1baf2af3a3509b3535fe31ec1534b6aed8d163f98515298ab2701dc47f1f8e09304514bca92271f6bc8602f2344424b6b410bb69a5d25195bb6d069b038e72e7dffe79f1a0291759d5af51869fbba2e0cb6557f1cfae22d5c38aed8f33da45e3d6bcd4fcd23789dd5f9f932c35b64723ef8a8190638c965735fcb400bbad5954a73a8c234c889bb84260ab13d138fc0263bedbbc479fc2d424ead3a735ec5b0801198ddb80f317dfbe84185ef5f797d5d72b4f71128e641cab789ee105cfb0926a4e1a3f9baaf81ef7204ac1ab00b46fbc9842e6bdd997b8a37df849ed4f629028c36dab6c4a0e27780273135f619abf468d8ea3a25814a8018c095d7dfc0a0172efb7c644cbbb14db5f8333d0a7bbd70a52ad448f0384cd77a3f94c31ec258a0ce37115eeaa6b62fed5c8123453809a1c9fc806db38a1541d04aecbd593c6b1e741868764c5a2be5aa9cced303dc9beeb68e7fc96a24806f4d2f280266d6441fc0224b8013194bd13672fc08ace2cb7e9d8980e600b48298cd5a82536396eeb9763cedf26a5ed9196bd43d8256c3b3137ce8115dc16aac3b3da26bc2ab9bda444d3e8dca5723d26fe0cfff259dfce3ed4e52f89d622c00da53bec616d0ebab2a57679a4a8b308dbaa51107220787df9fc594871e687d0bb91b84a09f0f852e130c5298ceeab1d2296b741008acc500d2860ce0915269fd47af8f2426765090f88f0aa002935b708a26a24d69c34cd679875cf46442ae65fb0b6b7e0c85f7784c7bb4f5a60ab94e4a36b8ec67b913e3d663357bc76370accd64011443fa9075a9ca02e057292f0212b3ca46c4623f62b16dae1310cca836227de1f3f0ba40ebb4eb0a4ab6602343222920b151e9c4011810a15a07891df8552bf72e6f46af2dfd45d79c959d1c436dc6b9462fb146636125915027582c380ebf73ff2b8b15c4b21f5b99c20f88ddb81a26f132129d80aa81fc55bf1f29a6f0e4e5c65bf4a1d4a0fa451f7ef43b950a357809a10c1ed1501fb92e447ab44f63d4280ad6a45825d233d36c5c670900d0800ae3e0a84454517d6bc1f0b675e01689ffc156ec75ddfda8eb165d41ec411210803abe492e5762f10b9a44d2105d304dbab22281535e5ff570c6d6c398506803328abbc52fbf5767e510813d60c0bd1d8880f6a88b0fe38c8435d6314374378d2ea937b345e32dec1a0cfb0c64638ff8ec2e832bc99eadfc949ec1a97b43bb51da987e301e73ffda8b2ecfb60ff0470255263069a9afefd7c75e269c8d53f0dd46d6ccbb46069ba65888e6badaac8978a31f6724276962195df087fc6bc8469a4ae1790419e2309b486b830f7fe6c287e366db1c398976a79c60c23b7c94ed66e92f54bb61a1a0ebf6a041d011c6cab0994fd80df84d6118e080f072715ee03402bdc9398f078c494144629728edfbc61e26d6c7beea2568c0fccba7712839197799683e359db3710b91ed5f2cbc9e562fc4e1a2d9bf82e69c065b3b29ac139f03ad191fabdb544755e606c59807b6ce4ad4051decf223d840e4f63b7d0a1a32a79de8f01280c02a9217855d88b7bfdcb0f8416432b3539da890547adf85fc5cd367d9db1189e6c79a1bddb73b701b47c89d971c8749d7ddfc708959b6991af53cf421c9d88a5ff1a8c041cc7e94b926f561a859dd417c8d7ce169ccd495447322793499b97668088d899696344ad964cd94c471f80f97ea5642249afbae784d8003a07ee8f588253271f1eed3bd520722eb4fa2eea55059ea65e34572c60fa52489a4e31c029a0f034a1d449eb87d3284abd77858bf7910ea8d0cc0b7827a976b3908cda0a84d632c7ee4a5397c9856ee019f8f93fc0dbf3f374b2434789796b181d99859d3e78045f0898bbabeac292c9a545a931df70e40d3cc108ae2ea732bde581ec67f91cd860cf94218ed63941b1bbbdc4fd443a5b62dee7ebbf1f00a35d1737488a096ca649d118a1b4d3b9498adf1a3d0a72afaa62821639eb7643eccb5934e8354cde6328608204703abd6c8d5c2d26e460381bfacfdea6ca2abd188b4f1a98a5e8431f228e4e82658cb6f2909c49ae72832e8ebd09c1c2a0a588bf490349614ba270fdb94388ce49c8ccca1e2b500804140f54409267342c105bea3c6c787eead27322e8ca09703b7413cdcf6296ffb438c6ae1a6c4620cefe2dd0b16dd6d017ddce8457134e2f89d58c06608bbe299be9dd91f77c028813b6cd53f6debf885c5d45685a42d035744bf77cb128c4b0fb85749000dd0ca4671b26a9648fd3c8fbfafc1bfab8be9ebaee200227bc4810dd6aacd9fb6609ca331e54b5303b58838f39784477c80e04aeb1a1fd63e7fa6568a434d675854cfc190e922ff5583d0e4cf56d8a50700a5b1f67300b8f8fef2acf72005a950f12153f194cbaaea80d5385e9236b0fc53260aa582e69b4c3f9f5b0e6a47e28e780b556eb6774b1f75697f0c45228a60bd608a3285395b9f5c9770e8a5c11c8afbf79196a854a92a0f3e007ca8bf14964495ec65eebdfe97c592c509f57fab9624a345ee8168e76a430992fc7925a4275aac61493bf12f7ced7cd3a14aa7c8dc29266086ad7aa7ab69713dd9fa418fd0e5a43525fb1f68ee103df4f52a6d29277d51feeeea86fa830366bf0b8c6fa5571d881a7e82d4b7f487a654152f6d6029d504dcd60514232d84fc790a245899364d944207c8cfad8b9481c41508e11432bbb4052abc4912e8c4fa20d487d1964bec0be49224d60508e5afc2ac903e6389431276c6cf4656be7cd5b53049747e5bfa2b47d50cf5b4756c87dc9af30459dab09fa931fc9c6362339ad3c729ae13189ad538c8097f53267fa52eea1cd7e6b8dc4427c36849da699955b4abd18d3b10ec3623922d495487fd38e540871a69719a77935618f66795442194fede38075d3600e6df797f38ff491e753bd31af6c0f3cacea884c0b62d2e308534f9baa53cf4f3e5230a1dc6ab00acf66bdc6a44760ee75fee62a1767184c852534d5f02e11faca1429800d2da226c42b82e5fac89218067bee9365942ca8f9693285aaa98290a7ccd55451b4b16d0fc17a9f6376cacda42282939115732d20dfdb177005c20e7852a4b161d6e606ae2f88288556c41dd19c2656b546a0e1af15f5368f63290e3f7530684c80f0a0d5cca8c20d0c64a6723f8375283355aecd0230444f6f2a3a5b156c0707b2635d948ffe2859410fcb05738007aa0e41cd060ff20c26e70688999e52c7b1dba716ccce36dea3d70b3e32e43a5cf047de3b057cdc1870e24f15294408c659de16cbd937e9fd7e20de08df9f7b9efa7f44dd9472a8b6a1fb9a3ad6e63eb28fa6d7a82af61245a5d9cc21d594eb670bc70a733c7edf70a37de7d861b0943417285ad6b0f7c87899235a892bcf25a37fbc12012fe0d24d2c019271832ab12589d0d0de81914ef9d89d32619862bd7cbc405bd0881e34267976cb18c62d7f6fe6a72609ee8c08d197f801e1b472b1b870ca0515125e4bc0247be43bb959925b41055db1eaa204d6529dcb4368c5d004ccf7a58a350d93a81a91b4f866489865e0e490cef54ffbecafd5e6ed8d3bcacc32a2c7d08c9bc9f98ce0ebc6b93518c87e66046b0290723c5047bc773cd7751259e35ece5c880b04f85d4ce873736e648efe11bc1b823a07a0ff8c6cbf15387b4dfa0fd0cd4568450cf468d17d01dd01b417d705b3ceb5d86f4809e7e944382e532d99288ebe023480bdc17ba8673146075ff3031ec93ee9516c2a5800de32ec7604bd311ff959320b7dd88de83bc20d850d3425eda31286533a02cd628228c8e90348bf749ec40b5d1716c7e6d63e3a0be77621d86bc3b3a3d3ac1620065ef4e25130215a49ea8312c4681a2c548d8d9bd093366d3c923ceb86320b56ef84d29fb1691ea673a311a883e16bab6cbc5d5005e5311fff76d61b68bb4a32bddd3f9fba7c509e04fba8aee29d02ba40ffbd2c4266443877cba9dc18c1a6324baca6a6b54cc41b42d11647d8917a260fdb911d5872dab819f72ae6f3576c00e3169ba0b7a2dd06765bb67df0ada8a1eb412527444d27d3f13f384249ed547b85a70d8e6f00196d3e19b44dce1f5b152463de577952cb1238023a973ea392ed895683a8ff364ca5ac37c06fe436ec91c7e91363dc4b0fa194012ccf166557804f9c12774e1dcb6907d2f262a4b0dbacb21e3d984bb725b3e48298103c97ead4dc958e5925bef578d69df4cc37b97869977947d3f49ba071cacc65159dc11837e50daf12e5969b9a52ea613769a65137ab11bc0d15eccc7d7eb98c71173f5cd548f06eb0e8507ae04d7993b34407d389b0a046f24e192c012306b9ab5b4c9497985185430ba511aada683db869352a37e93948b2a7d75a2bc72588d54f13ec7b886a558258208e4005e7a0317a721a57fd6d2c4ac6bae4f1ecb93fd1dfd72debb8f7da4b986d26aa055d2d9e9a1b15c89c62097166f2cca5b3a133abcdd152d69d1f13ca0b4708c264c5d7a298aa5cce299e281ec9c95755b33a1c3d7a046d78c7eaa14df33458b642a6636cdfc0f925561f68c1cc7e868d2d31b1e5d318e419e28d73ec1a206cd694ce0cd4637956caeb778630bee52691252958def97a88f4c3aad9b29133b3502c73de0622b8cfd9e6539decdb3f356040796bd8c9de8ce0360969877b531636694a48b063b8f350a503e60104c598f2d42d2b6ffefed4f83e4086a7fa7690b095f867784e99130efc98a39f8cd322714603e072f41cc2b49570722375e631a8691cb32fac31c84d7a81b490326246e5f6ee53ff2cd0cc62ccee594191a781e3d5336d7e62414c70fdce8119c04507bc741436c56eed8131be3bf04142815e29f05287bf5d88dd0c1ff041428adf0500f7145225d5811a6a8be261118c32af7549e53f4b10727da65702dc41aef0918cbc8d75e20ce7e46c2fd7e7145e2efba17a3fd5e865964119a2785102c1a4fba30e0cb8ba128ef9c7a3c105fac94e51d1a8c47c90c093a298eec3b4afea3144832ea58b6392476b2121a8ffa50ca8d8bb0670db8b8bf39f59d71c67b6c1908f997201f05d1dd5320ef3fa889c07809a87005eacd497a6beb9e40754d45411cece865cba7ce6cab1d0f5c2bcc6dd14e7cc8d0fbcda8f446893948924e8e4ada01d87f81fe586edeab3fc61c06a3fe889ed179c01d18d5d18d2ca2ebf4de4266f1487235dcbec04c382e67b7b96ef263cbcd0f5b8f5eee165a5e307294f35ca0b6a470ecb238880379efcd3e57479b64b06c7436450b6dd38353709ed7addc9e61dca4a69349350ec90158c7056d52fdcf2165e2cd8ea06c8159dc20fb1aab5358f186f8496864aa2fb7f005b0e68e5ea7d58ad596be96ddb6ffa8d03e968b2c599ff49bff40ffe4a100dab2e0a9af88c8853470c0b0be4eb3e44d03decaf0c2b3e77f3586658099743074546596ddaa0f76fda9c4576f061cfcaba691d20d34551d89838c9f287169bdf7a4bf6bab4d845f295b1759ddf06cb72e0349af25df4e61f83407bf273295f84e712fdcfad67d4700ee6cfba8da7c724e0280c0e8b0bd5e9d94ea5ebe3e17a537ba9c0e50d67bb3fb4e4c16f50ea8b649a343cbe72b77dbd59ec588bf915790250202dd10f3b3f5a50972bfce46145a844fce93e7fc3a39bb8f5f264b404e0ce551ca7db9000ac3d471939e1d78d29affeb6fccac41e45d7d96559b1735cae8cf7c7e32787ac64f1ee0e073c0db24b015ab3b2a040c13fb4192420401a03bd87f85ab8ccbf80c0f825328c1db39c105b8d2c7ddbefa6b69cd34d5e75f828f543c76043dab30ea62387e6e257fb20e6dcc2e0c8590fd3bc6422653a582abae4dd1377c1cd6bf4a2fbf9833b99e91e1780bda61e32a47450ddf17b06d347aff92d1002e27f177b97751520827491b639036dbb483a0260741e109786e48b8c1938126b895de91ee94f073103aac2a3a58a092880cb739c1c63990bbb160442a3ea3c62822c92e1bbcf5ca26b114a958abc8eb6545beb3e93e13811d69f52c6a3098a336a024881eda852369d6f069c73edb6142c63e1d67607007b6d37ba494603064488c2daea01d2b542de8252cc39514caad8a98241538c42d959de569c1d2043992ca71ed59de90b91da4276a68a9517336254b041d202d4f04a74d3667d95d91e101d0cacdd3355fe86abbe8f112a05b31216225b114f63ebc9204d051d2d54657c80e661124a008d546542d2876804db8da86114099b19ad230811972a196bbc76e1bff4075832ac084ce91367c1114663bdd207c10dcd7173d7a546cb41df09517639272d6252f3816b5df0fca75b5c4f20c91f11d27b70b9d6685b97b46dea073b67f373cef11aee84a07dfcfa75c162efce3b33b576f50e490717440fc336493572651ed9e11b947e382ba64f94f246d31b51f82b02feaa8bc90c19b6e19edf9263c972dca1a4cce2958cc9eb47ac4e5b1d3955b8509952b50c7206e1c58b711a3b1b681f110d3e6d5afe66c8b57ec6d4bdb581cb1e2d6a07081b6d004bc7562e5ae4ebd8b034407c6818a6f6dccf49d99c77e7b2e19908c76bcf3ec12b3376af1385165842115ca7d4162644fc0db3354e80124b5c291206763fa8c50a3a19a0ff051e0d46afdc5e8ce608ab25032d760a1582320769b439a3f958fa294c79177a4f57c85992e6f3436e23cb9017e935732528fa7661829afa83372d51a7c63e1e946cf4c9e62260743436ea102941ae82cba04a9ea739b550998cc385506db268a9455c911e0a1099f755b167af95ca0b468a8913e7f6bd90809aa429c7d68c7104577883f1c22743ef09ec7395f5b533a8095be91b7c9ab6884769131081dfbf48ef8bbc4076e824f3ec51b460b1911a8cf3eb1dbf14b696738203d5ce2bba17f45fe1b4f14d02061486500a292c46becc7a688168df040c9ab1ee8fc6d4030c3e6f06e851cc54c881c14a82fea782b1315b22c7e3f0a1da2e5db0942dd642a7c7c9940b4f47485651d9a6fbc6cb86ed4f5f1d8d99ff922828c2ea50270ee3ea4c864cecdda00a894bdebdcce2c3901ed0dc601830aa276ea90198204146accfa749a74ac40d5b82a7b648d8c36d9abfa31146cdb284a60288bfcaababadf938f67b1bb223a1bfa81f0fbcbebd24daf4ba41751fdb6e4265c5a50b07ce69c4f2f3d96c35863d4445e188acf83e5cf76bc841596f86ae16f47e8f8cb1f57579c38b976e7a7386e8e6850cf778585e553a3945e61e54050923ad7eea7639ef52f26ce0d6766887c1f62202a1b850ca3c3b44a77cb04563b30ba57dbaf2e29ff450ebf5d60f83379c5af0e7e4ac01de828db26c4c2f5c11d7591706c756f2b6d1098b852de236e555494090d706fc11a3f4f9a2d115702c72bf211b3cbd8f052044e28f4f2f71c27711916715ad96cf035d1ed588efd2d17b20b02ced01a87272c05cfc99b9fdae190b3b92357cd5e4051d5b2bec459428e4cb0ede41e848a0a31aa809208bb531e032aa797e4824596da07f99972ff6d6c1b72c911655b01530221bb10c2b953e7a1c9bc808e751fafb34db23cb349ca12bb9f2833ab1ab5b1a51c2da99cf600917e6264060e10d2db35099904f932622ed48360ac334c7788c3528e668b152e9262d802cacf413df119443727ec3effea4468662fa6c6994d02159159f0220b73496906732ec3c4ca4c9bac873dc90c479ace8f52b71ccb7e549494666658251ccf8556c7adfcfdb9655ee704514a262c1076cd70929dce25c836573b069cc26986c3c855824339d761e4357d482f5640c956a733fe214b6c0357bc247fd8e9246eb9a256a20d5a10bf7b0deab4b241a92618c793f688a1f36b0c2160ea2b2d41f66678881af4dd91058ef52f662ad0a49d75da4073814c7c601c230ea59d524fb8f911e0a6546af7d6c7288c3da58aeac4e1abfb894811e212a2f9f541864d249c4332429e0c0667e463279474f277789105b5ba03273f0f319c7185205b18b4f073144a1ed081c9162ef18d71c652f74befe7313d130941952cf4312338f095163075c91785af6c8229f2c609c6909e6bbf117892f9794b67514f2c8254fdfa5786d5bf042b9922ce2ede45dfb6bcc6e440e2aeed12788c349eaadbd22cefc184c663b1b99ede3681cc52ae0d81cb51eaa92636f966ccca8c7177fb0a21faf331ec03e29eb4c3edcdd5bc2e7db0f29f2972668872cddeb0ddc7983c7f851cb6636bc52acf62d283689e1e66284e079ee6d3604f18f8125ff92b0dad007677a43ae102dc6c21b4cc31599e26d03c5b9ca1f1596a631cc2d92c13d014259da4ad2e027bdd2cf25b72dd0dab9e41995cef1085c2e8e1846f3154a4943ce33f92ba6d1a07ce238606c1c4d415fa78a046d7e0df504df6fd099d112f6c0c1f19b231d8c628f450668f1e31a7f2c61646b6c63c3fdc39cd49cd21853a22d40c66a6e5378e82de2ab0b477d1bf8d50f0081e4bf415caf82e04b895aa342b9e4c0cdec3ebefca5da545faa162628888333b7e32c0bdde0c1343f62ec2f9a2fc23a456eea48de8cb3a9a2a4085821f2c7f749c27d2144afc426878d64829245fc75edaefaa2dd0ffb94809272fcb776b2568c433069df6a5c64d440f06516539e2630cd50282cfbd9151695da13950349541d22b29fbf936778ca390a27727165319701ea0b8b8418f0b4184b4725cd3eedafeaea12984cca61a44b77dde4195dfc8a2b3a9ab1133bc251204e8e5a24da39f82f2c64ec3ba41a7e5ec7bf828440be64073bfd17448438419f0b6399282f2a2d4f335f9ed7d9f748b379c2faf344abcbd42e3a08c648a4205fdb037ea9dc48708675685c539e9c2b48b9dbd38ddb1d9033fc41311c35b9ade22a86dfb93415cada5646b6c67c3aa017ab5942c744be4a33edc089d7bc163c224b0452f3b7dd495a10eb1d480b7428d6dbdca88a504d368f6125ac15350d6440b60f3a0a10450858ca80f93b44da4c27a10973cd323ef86199932585250a6a3e3ab408c035814190e76cf93da062b5f087c8bfc22fb91edddf66ead5450e2ef9130068d78f485c86be9802553e8e34a70ad2545eca03885633881b85507c65bca2ab747b2992bd03624b6a5933bde7fab0482b16282171c76f6fd013b2c3daa3e7d5174bf8726d2c3f402cae5f240f5be729d8b024a95fb49ae6245f6f02dd49fa511b5de45ea6b4f0d8ff5d402193912b3f96999b4a7fcbed572147e0b02f1d3a78f1cfdd6300f93b9c4b18a64efc1b649cdc0cf873692ab249306c87e27d39c136c828b94163fbed501f392989ddd99c817503616ba9c453b3b8e4326516407572dece4e4c04dbcfe9bd76face522671f1318415ad5cd38fc7c1b1972199ab7e90fdd6bab8d63d3613af408f6acc76b054415c06050726e684ada91064409aa081d145761ec27d891df81e195b0e08e8640b68e126acc23247508b79e7e9cadc730a3f451abd483f01cb6e938758216c0f6b1d6e7b67d8a99acd8c1ef1dab92d2d14c977ecec03b872a74f7632a5e1e318749d923b9f8ddf0c0ecee5b9f0b1774b7b81e91b5fb1d59fcc13c0f16993cdb78a2457e657edd9541476670d447538be79fefe945f0fedfaf24b79b030ebb1a16536bd66b87b23a3f4d763b8291db1cdbe4ba366ccaeeb324d91b78a2f533bed512a63ce35b5b9320b643214a6f2c69c45535c7fd1a3bd0b3495a59e1ae8b2f85bf3a39f3efac8e3503fe01ace8c5fd565ff5cf207620c2a1899148d9ed0816bcdbb2ab8574fd54c84d97cd4442c0ee14e3dffa83e65df4b17607ef01dddcbcac52c88363913dcc2efa57b03a6637ec44a8c1e252214e728d915c0a7184b632a3e4f1a92b520c34488a45b57a27c2e98c991743e0fd8f916ca874364abaee5744c61f4024966ccc69482af7d817c4cdd00369285a7debf869fe646fd0903753b1440995a3bab952f91393f1401d6392fc643e213146ba6cc79940cdbdb35310d4d85382a2c1205e528bb24263e850170540fc1d071c4223b441f1eb8d783220681329d1c25b35480311bfad007d8e9b85425956c67b19e2bb4e62b0f6ec495595b37a96aa053d0b7dc54a2ae2223e1227e8fc3a16229e4f9a904103e2376898c56a6f7a032fb17cce48c298faaf103b433e7edb7f4222a671a47800d25d0fe2f39f7db4aa0119b7166a9cbcf1abe35869c52f801b803a759266018ae3c349ca05756310a64203f48905146a81f8a3d9cd0091a8fa7cf378525e350aef1ee3876289b166c10187b9126d8af1ce95b482fc95d47b29b11bf70efaebc56ae3378d2f9b3160907c76f80ed08d5ae32628efbe6f216cab582a0f693e8bbed4236c06aea5d886c3d4f7195033546dfc22d731aa95b038fdcedc79f827b4567e0de2d7f86c79e4fd0d491165bef6b031f83e2078847b52d21fa6fa6975f37cd0e50f3e25e893f704b6f06cba4e4846580f8e70590cc30660c596bd291935361fa23f00803277514e5f585709a71b88be0d5a2b051cc41a5135402416e307d5081959e85af76050b453227611f6d9ddd028dad8805a3c286405eb92f010a8654a7c03183c1bee9173a43bafc2b0cd34f7018360fbec1d0bd46f215fff365159de3047f461830b66b6527eea31baa317a4b27b24ab6013d434467cef156bd3416aea227b5386c11a18768f51f45ef9fb511cfb92dc127a24336c6045d3788951c6fb2c2ea14a73206b7c69d03a2991f563a9eec1fae7d42f793b61295862a7028bafee1b9d5546ff60d8350e56ba071b7bec964b30f4e0008ec1849bd088e1cb28b17366a110a6b39796408140386ffb8d4603830dd32cb4117f70da94ddce89ff8cc0fe4ae0b3355fb90b1c620fa6d85c3b8416e8cad051825ba064cf27979bed061b0132ada91b9f19259f83e5f419918b2f05695e690b64343c382988ebc69bffae31fa57d1b713111465030aacea73174d4f0873094cbc96817ef66cd4eb81c1eae442ebe8792b388a68062c039d16071cf244c4f191a07aeb5a30019c2c8d6d40facb5538438d0c749e5dbfe78487138767b40b47f499790075b8a5dff57c70bd67f03dc5f73a18871de36aecfd292bcb0a89b6c0e33ddbe35e6a541904c0b8eea67145f9cdb4fa0a79d7a0ce50d812bf579259679360cff1398fce1dc06c4599a00332959a472a319a46141cd07805c010c58555e054c56ea14b6087e11af74c0f6b99c82e26cf88199b95e03e0cfb2cbb193dd27233fff25e5bbfe64f9863c1118beb13239fdbe3500f77e4e02606e9c7d3191b6cfef270462df39dedf9430cd093d0f85ed3d0f22b457186870c036cdf239b5d177d05d79dd4a761af2ea46b3a50409e56da306422f4b0162347a7e79e9468c192c3ef773fe6412629dd9b6b0ebb48335deeaccf31a17bbc45df14f123bbb6deb3dbf854a3fb0037f6c88f7c3482bf8b4da1b802ff1b8322f64cf455f2d0b10f9ed120f1c93dac20d0838de107eb9bd1318a61f2dfd8936104efbac5f6ffb673562329227240b2ad50f65441ee15ec930dc74b087552f254753f9ecebe2d63bd77ce6ba9a6cababa926796bdfd5652976455dc1da7ad0ff873349ef3dd10a1a5e0b92b3a6014df06d80dfa692cc8407d163b5c0c9a8ace50d1bcf164636ba49b92b7d49931405be577114347cf1c4d85c2609126025ce2080783f01737879249de7aeaad6f33e091d54feb4c45d2830f88efa01fb32f0fcf67dc66ba98b2060acf504d0ea5df09eecc5a501ca7ec9901871d044145cceec8277a8728ea34483fc480f6cca223019c9c7261427a4b418e1d0ac40dfa170603193598cea1359b3291ce8085318a7cb67548aa1f1243db75719da8f17135064b984b9e5fa96ff056f638837e169bc103dc360a284c06d3e68e79a91ee5dc14ca97ba4242d3b7e09ce531fef4582ef2383131ff5803a36ba05e260984eab61be17d5607d49ce5f6db11c50e90df29def9b44e9e48227c5a6c8a1dd37b5cd59da7f40644fbfe795e5712064042e259063b3b834aa169f3cecdbc2c2fe16ed9d588942a664f6d17a9869f6532680f3b09fb420a875c225fff1fa2e26754b84fb4cc1ae54e4101457dce3d4b2c9177c78f5caba7b5498756bd6345b18f2d3b9a9a7e56289a368f4893b362ea98799b4a76a89cf3261cc1909ba5470d3e078872f1b7287a36c986d2113a29a50964d5c80bf87f3ca481cd6748a91c31e94e8ee8cfda615ab9249eff787bc615e014a11e75cae53f9f9573e8604bff5b880ef8499703463db5a71b093adf60981bfbfd00fbaa8c6690d0fc27b2aa75d58570bf3c1fd77912aba09dbff32274e73c575865d5bc73f9f1cdce8066f925ccd31049c777be01dad7452349408566f4c2810c13b95805792513d0236b4ba7e142e5c8c7dc26e0a628396c45861b614c5f8d627587adb6574d276e68b0f25bf0e7f10e439e80db2c8370813b4d2b4a03c6a5817767d5b328d1092ce45033748f570d0ca32b19d25fb6ac12c6f8b484a8e2ea509f4a11b7996c9e6d3abfe2f1d89ce3a2705f2a1adfeef2ec141f1f894bd5d5fb9b605bec011c4753b25c4f2d4c5d35cecd409ca6d290eaa52e416cb0d601918098a9a9dfea46cbe6e4ef61e67094b313f04448a5e0706d49ecccaf23d083dc8f4c8eaaffb8451746ce85851894960e7df687bd24babc959bb71084e93a0b575db7897628675b9139ab6a9c31bddd58c014a860a64103053ed333214a4e8f928459e521ba4bc1f1936e7e2c0ad3a4f35f5545288345ed1072a302aaa4bb422d041ae5518deaf10d869d939ae43d6bde03b25085483e13311288a27ab6e211079d9141ef89664ce6289cb2dc18097b444a5268fc230a81bbf4e01aedd925ce2d1e73a809f5c1bc6a33e91b28059632d1c7feac77df453a38b5357cdffa0d234d2a86ed0fc34d019e7ded9d87cd6dbd537b474d448af3cb74aa45ec1c6bd7c59a7078820f11e8bd9e8e8edeacb24372e4dde9451ae6649bf4abd605cd6730703437a2f36916deb13b8e8d183bb4d7c32da9720e9f1c23850663bda3d8c268471de84d892fbc28e8ee22e3369c196fbda6124cfab2c54e15440c4e27e4429bce9c4297287f8ebe939dea95324eeb981a14897bece61e35701393eb3ab659a3159ecb19e6d09dc0d2a630a58bdbbac1e0c536f2b6e604eea740054e425bf34f8895719a7be3a4117580571087f49088652af6450ec20fd209020693e31fe58b18eacda49b3b245dfb0ae0aeb0d1c0f036de53c8096582b813db096f3df7aa38da5803fe591c14166bee99118e227b2c81149322489a56a696ae3e550e23f88224c8db8ca0f048d246c9507ce33f901b859235a225e26bd0f35a74a6a7d85d95745ff6d45ea5aae51ba42f5fafea5d64f7201f905155e743d561cab3f0dc8d03398852b8e24a1e27558266a7a868de044c72d3d1d064775bc9de806a8eb7d2d735d70e37f34c33e4c1a08828f333130106f69c60f5648d4f4419bd6cccd57188417521dbd1ea4f0ae2257032749fe196667ffecbb3497ed10ad35873c44db037ff18123195fe916afebf28a5fb90d91c91c702b4c4f23e4e9fc6badbf1b678fe76ebc11333a6db4340e9da126dc84c2ca815ebeda2f7ecb7654df3d1958a7778ee2678bf64578ccb93cca228d770494f0d59ec89f1b2800756690429060430e77c0b88fd7c2214c559d44474b39fe269ae6fee2ef6a35d70b219cea7f04bcd11706e2cd1f56fa8ba8d399c0cce55b18a3e995882733ff0c63c5cf184f3a38a91a902b433dd3c2d3a0dc4c50e30d5f3f9bfd7f60519ffeea0ac96d4ab84c8b8f329161f4622656f01f7252006f24ca1f23d8ce243c4d070a30183f5e4f1779703a65a48da90553b6e410738c84db270eb129c82a62c26f812d9628a15f45a8427f6f0c14100e398c488633d2ac86151388697db61992fc4948da136040ecbc0eb76aa7aa07908b72cb2bcb719a54414c3755229bed338306bd4b5491fa29ee3a16525db5c1007033db451d5cf0da302052fbb766de86f3569af6e46ff0945858f9dd52f2a72b2966d7c54ca7131aeab8b67266b94d3871c513d52e3b532b00feac4bc93fc6399237d0ee6d69665b3af154bfa646a3a578b9f98f7d858893507adf1221b073f2e4c50b274ab280e1b26a169886e2bcd4893c15548f11c7cd830505306779061add04b1e5734bc67df84980acd74574cc205543f991f118d7284ccb4c4d9f54d1871a00c6268a48f9b59dd4ddd4bf8f861a3cbbcb5748660a8182c6cb3e1d6bdecb54f56c6933817ba12afd74b8e8ff631203fa294aa2091f40e4914355da6c1f0bac085388d17345766808ae5abfa59218950dd369c6e256870bfb5789f5e1d4a97340f9a962e4937aeeb0defd980e20598bdd68425518b018d37067d1c365bbc247730db31695240b2e2883c58a8edf1d9e37108b7c5f576fa2ac269b26883f541a7dffb25146bbae4fe3900301fcadcb8bc0a00b0fa316ff8a04a6c55209fb127da406f9674009c2e20db614114fc5743383c2122c52f27daa70ead2d5e00ed2a50de4b67d8542b08675fb39539adaee2338e8fe19846d9a18e6d9682122c8a0a622a5b835db000d3d8318a87a76d769d872a8ccf01072f5f71058770d1b1dd5cfc5a5af457e62cd01da88297319668c5a8645c102158c5e7dfad2932711789a44979ad7e7337a69b7dcc29c8b1279b4704172adcadf81ae682647fc684351ae5da0ecf6ee9d900cf8d7d29e7ceda7385b98b2c05f069991061bfb00b1c7dc668886954bffb0ec5a198982e4ec77869fc79da7d95b92cd901d8c36f6771a64ed81afc8fd0c0d8d5f79d4f919ecb4506badedaf8fda5e3a9de7f8dea577def88c1e0f1b0e14e08a47ff85c2a928f95663d6e2f5d1e9dcff9f001c1306db2243ab0c34ceb26432c877597602bb2e8bf463d1f9afa8e39de67b3b43f54e80bae59896dfc7569b043319cb53e379215b0c8c8b8a7c67f2e6698d72a343f303e715db24d77ad2acfe83d3fa1a65d1f94d719c8d4516b021ae0a488cbc52b862b54cc3b2a858be86a9437856a978eeecc851c8b0c0845cb597ce6b093dcebe0c05b375a5c823aa03e0449c9772e35bcc145c1bd7e095afdec8396017017895285b31782509314209e3c79ad3a9d80b0f7f54390d54ba0699d23e53fc0eb266fa7d777fa244a7dd9dfd7d9313f8cd3980076e5ab1e62c6aefd049aecb887ddf2323fe90df38b2bf76a36f305ee2b430ceb0b2879c63800ca9dca94a5b0dd6ff768d516224ee7d9fbe04d42ae16f8967182d55155f0125526c8cbb1f651ff2867c8f301621f65dc3334fe96b5ef49f9d913c71420533ac86d769173c2e9401ab74c243696de8db47068b7e0de56b63be10cab5a6f09f7740021fb9be99a60307a90676a65c5578f9963a4cc256829811fb2b45cd13085e33c2390f21f9cce78ace23cf2788527024e1041bdc1a430f6cdc57f36b617c1a45267990addfc14380ddb3fff321b0eb61bae24af2ad33de982b0901cf760ec0bdc59cf2a1859e9f0e4269c3e82c06c354ac7552230d26f955da5df27b1957e9570eb687b724da2ea8a377bd068ab26ca9cad6d50abd1d87be51c5d15186b4e0c898c7db513b0bdaf74df89b18fc88770e32e0263df00adfa8afab90f160e7f96c33e89d3533fbe0dfbf6e1d1e0799638472e75e01a2dadec7ff6d3dabd5fa79f3d5d3d74f1ae2ed72793031290757dbbc7136a782db59ff8c85370b2c08025dfb768a8d487797c1fb3b0568af71ac2cf2ce91822fcbebae6b7b154747d6cc2855262fc201294547a187f5db21858930fc157c57f9721204018aeef0bd79ec5d93271325891eb61f06fb70e6d4618abc0efceae62c20310a9063371e5cac44185c78c67254f531638d9c61ee5fa2a4f02dd8b8327c40b271ecc475f411375ed945f2bce50803d41f84bba76bb3e48fa814d610b83136a0589682a57d852f601a757fc627000fbc4d2e2e9035492d7aef38f07aa0b2ab0e8d444d6ca9f2c5b1f92253de07ffb0a17c7990a05665d1cd97f57c089001d3e627598132e8b9e963589234398950076b0876b316eec96c8148b8a2ff3fabccc76c488cdd72a928f6a53ed3a95296833ab8fec8b77be7f10bb1c14ee36c17ebf3108468039b7fdb468bf2f0c6982bfa45e09d88356659a47e95f2fb4952ad29dbc046ad40ea9aed96027fd6243fa4b0d4e2927b06ef2cb77bbf1ca0d66055146aadf8c967858499ee6c85ec3cddf1155c4970cdad5eaedf0afd1c9e7425871e5a1f0d03aed0f042a96b81bb19772dff69f012dbe5d5ef90d2c5052b493fd9c2d0f032d9ebfbc648d5c887cf90b1fd5c93647b4628a8da8d0bfabfc0cb568faf2926d741670a2bb384b7884da13df42a20b91ba8da822951014bda239c58d4e16a4e7d046c7082e741a1510b98c376c596d71227b03550f4cc767bf3c414bc2219c7e2a1cf049bd21cab406de78576d9be0c7aa08b2c24dbc34c752a6466671dd05bb9d0ee2a35b3d5b5828f3e0ce7b48de3f4021657c997e98db2004a823b5e7f98e5fa7d010d6422a023d7850f982512a0248837c6eff2322fb7b77b6862b712b3d094eb835559d72a14689b014ad943bcd3d978942bd1c5059ead3f763339184e10a9303b6b117e2de1eb3003d7064ebabfe52136cb6c999065ecee0380588c7743c848580967695f6c8e67a51a1286565e2a01fb69b104d74be196c30e4270505ef4491e8c8d0103116093c84414cd8697484bb6357465350c180a4eefc6a8399ef384bb39ab2e52f8ac410f13741d8c52f0d467a5462e4852c208ec49f65635abc2251f3a9d7975774e076aaf7971790b78aa394f1587b377075ef14d6ef3acd7c03a7b805f3151bb09847b4045c0a2921ce4e448fc1e0dc2c7833bc5cd4b76de9187654f65e78de35e0c3db36a464fff70f295b04c0fa2c4b03f660a5e875f1acafe4333cc07706290abc7fbae88ae1fa8cd454ee1150b6833e437fc7cf59c28dcf99d07123860aebf1e36f1d25f5c7f4dc3fc578f615dc25340ec59fc27b0c0a5d2b229a3bc7446e313534d9156ccf28a2dd7366ce439da125c124aa8cce12f62f49b57b190c0efedee7d9f49ec8f91013f863474e2df0cd8782d978db791f84b9027bc804916bea310002f7ec933a7f00d10af21d1176aef1a78249ff41ca4603b67ded5bb46ecb6c3e084b465e733cba989d90a92c839bedf72b51108d7bba586494b95c5d41906474ca12e87a9f88e102f0ec7d9ccb23027534bd71218b224a5fa1f21b21679bbaae9094339aa79ca68e9ce5e4cfe626d7cc98484f35db7ba5bc1409044b5e3f6888dce2e8709efd2283327ea95f037c4cfb2082e526e713fb8ebda8417eeab48e9d8c78fbfe74e0bc08129ddc3895533a2926798009eb2f19c884e2256983882467d876d1f6ac0bbf5027e24ef0dacc9967895c09a803e858799a4ad6e5a458b7cc3f73d68527f36ef3f2659d96c945ab4137241ae4280559092d502d739c30917b3263e7321b53bc2e15fe5fa9065ddc2b81d2bc11822fff8ff012c384fdbef667b7a3e8aa276126810f2237fa9a7b1c96c32b20937963729eb527ea326982b5017234ce330a12ebd1c93d05fc06decd5abc694d046e0a517950fe005ef86dc66238610be6716b67e54d9f427c845c9965a3146553794fc236b2b050d7aecc440a00c3d46c8a5149dbd902256a1b4b8073bf68521e6bc186c392b6864311d21bbbd72684415e21f0428891655e96eeae276a1958c8fa7ee031e2bba2d75ab525ae2d3100a20741823747d892c46835803c1dd38ef419474d9d0573d303239b10fd6c104891cb98554459d2ba41382241d86db4049638b1e9eef17edc3332e9038f51b15123f95134c575d2ac7e8921a48d38f7e5a9ad6d25c0793498c25347c80cc92f6de08be9c5092506dbc9a1c9a1e1646cbf7d21f9bba110741ecf26d9ede1aba247b7fb02fb8e80f773c6af5dd4807de848e57fa0fef3595cdf71010152e67c0b4988a28d431faf8a0ba01dd423e3e3d1030f29643fe17bb72f29a1dd80324a5f92ae414d907655e1d97f688b0140738afa5d1ea610b5a8d7348a508cfaeaf2bb8924e462b6516cf15a5f882b1df813803c4cbc0f1b821ff974364e3e312ee21c2a6d3fd0854d8e2e086979292a2dbf43d3ae08a016cfc384680f66cb64c97e8fef67917d2d3e34228ee539f47ce0595825361a264187504b2920c1182013784d06d418b89dafc1469d6f4fab5d8386e940ef11e46fe685cce87337929155f15006687de0d1b1bfd9f9487c4e77d623bbcb9c96da745ffe477dd362e7f6bbb7f37690c388529cab9ccbc577ef96cd0d32ec9981730cf47b2753fcc26407e27aa5b32a229ca3613c02806859097b75cc96c46655fbfa208aa902232821c2402e780cf933608b98f0f3c52f79b15ec196283f481d006bd43fce075e5618be101a717c810b2e073914c711549fe22c39bcf25f1d0b4c636ef92c2fc97651011b59f99c6a05157c7f7aab0fa1645cba9e7395dff61e13e6552bfbb2ef415f46d5ae71f0f7cfe65f3c5b39b5d34db04c5f166702b2fdce5f04c487ed3028accbf3da510c05cf104bcf5633ab4a44ea32df7814acbd72fefaa3f1da9fff62974d833c0b732ba563634168a75cbabd4b4785c762393ca75efeff7bf5150478509df5f8709fd330df110ca2198b5e4ce92fc5c613e4d41c21e79029de18c01ba130972557153d76b21c04b50d6e1a013158f5c47e8a1c77b0aa5ed18ecedc4a523a5d6b0aee7ea0a75572a0b54ad2cf96489577c86edf4e906b1913dea529cd7e9dda1310019c6dc51b4641a038ffd7924b80b9af6a12dc7f8ab09770612152004efca8a65395dd248c96aa4845452b884ae48ac74d7eca51b40e9bbca282e14a265938706d6b101dd7f5337d4b725c61e1a79e46a29303b78d512adfefeb0b1e7540df32f7e513b51def3dc7ce29176722ebd991ab5acf9d7617403c6985cef3601ee5496c7582ae4ec98593a5b746586fde02aac4a2065be599adbe20edb135e9d8ab13ac00add4fa50eb592d5630bfc4c2d325598bb97ee38956233d1eda5602601bdeeaaa8d2904764322021c3a64a144cdc2e8b76ca3b9825144e9ef8a5b9cb2ad281db08caad09dc51d19670b975128d5832d4a44ecab2dd4e392396eadfd322f9479c637e4fc10b55b2afc66e6de7ea30ddfb56bfed8d3e05e0a4d2751f49310c4419262269bc9a2ebf4f8bba0bb94d452fc60a2ed6bb0e578e4916ebbe0ff95ce265a115d4be44b6bf650b5a4eb94fa77341969b3c0ebcb07bb0c7821bc0709a93b4f2478454b741087a35cd07a13a685675ccc2eabc25d8ad3c7f5af234e7241f623b133afa9489f8453227b0a7a259d43721100714d2857f9615cb846ba363ef789bdee76bd438c40c7fea960a8bd55513033348699b7961cad63f6f42d66cc431e660e48bfc1adf43aa0dccd01e4b1ef20decfb4d5cd47021fdceea9e16328a0de9e33187ba04011bf12683b2ff85e8602730da0eeb96e5e1f2ce64112bcf9258ca1ec60093077f690daf1f0b4e95233a4d87c092d8bb758a856be895afdedce82e5148085f7e1aa71e279b8609cc1f89c8f2dfe7201868e754b57eca92a074b05ec878cd5db88ef975ce4906255b3199ffd16caf0fdd4c9cab70ae3872cd807b8bf4a364babb7acd6a989c89eeec35172fc47e398a85490a272b8feb983e69ba725f66a4d10556db21599d82805f6b186bec99ae06d8c068dcca64cd4d34b339b5e9076ab1a4fbd8d93d0a378cece535fb0b6fa581f66561d14b99b374f18c63063e1e7b7d1bb02ce395772621fda74625592a1509bfb7476f1089809039b7c2a26c2d64c320a5252f91e4414ba9061109f2860c2df52966398af4e4c210b6b269869702a909a19ba3482e066d50f8860673122de6304c1e9f974482078faffbbd3a24d040a2498cde2b5b943e20e430a95b7f307517543042edd7efaf93c99abbf5337fa85bcb7c53361c8bcf45bd1df499767c06e6b73ca5a7d4e06d6ccd0628eb48813e87d6a4cf519c1022b4dc4c5bb33bd6f5ca7d45509dffd2e6e76fcc88325810881ff9d11147fbc6a2a1b97fcfb9b5426003eae371b2992d95c0190ebc3bd6ef09e792f93e061a6ea994b32d401febadd90de2a5a6af4658c78c6298fb31bdcce8148b972d98b7cf0d3731e6d91f840062b4afac78bfb64b56e65b2de22231bbb313dfa49704fbf7cb077598514e25e43409fbc3b4e65db3b0c0ce5b835402e66bb09dd55a86ea57be1e582366d67ea58d365943c4cca29101ce70af911d6cc3cc204eb2fbcd07079a098c6fb99d632db6ed099b602002ae7000c4231c70832c87bf8b916facb0016104069e0297a228e4f24b1ffc9e37e8e604403ce491e4469cd0592bce9a5fcd2c1a960d2a96176952e6e6c7c53c950e2cc3efe17f4f8c0215f7f4c9d81c615e7c112cf37234325a9c6a041abee4f2e1483525ed7ec6f864c25985ac92e2b098f6ae42ee429a451305383627096bcf95018cb22cd9a1140ec78319de29c8b5211874042a1a2b6b2005f714d3c5688ad364ebf96c59216c32d325bd66b4149e8acf9817483c9f7264dee0ee6c17252fab5f1afb4e41f5d513ba4cd18c818c557174f6142668932111ae8886235d28355e8ffca886470becade505fa99472c03cdb64e9c76d20e2fcf77d002c69b653060cc13920b703afa67b36190193abfae2edea2b75ba65a8a94f147cb10c388915d2e620c813591476b40526bb0487d18e28d882d588be8c1b6c01b8c92b4423a80279b31e14408086d27f1795adaaee62273c4a2c09674948107482445230d40c2d51200613e4a7801e2d09158503d1c63e1913580a22056a8523239fd8b5eb7ad4d6688b65dd946c63c5e0343e9e207b744fe28390bebe69ab19bce1671db644f5c4d706294bfcf23b1045c0dae24a1b548f832d704c1171d668e046a29055765f64cd122c83349280207f713ec6d7e3362320df75136a5305a49103ba72b27f5820cc524a0783fcc92540a636b56512542300ffde127ca48755ca33eaeb4a3fcad21b18b0a6c2379d0b877c96e7020070fc623abe006cc2c593f76252b6698f12d8255385b3bfd9974e5664117d49c8de9b6cb9a59429c914a5072608a0075147ff084d17cc60fd434a95744317dfa1ba4da541305656914d0db60632d8fed8b42a74adb921786955adb4caab6ed3afb7386cf5c686d996964d76f5943d656b3626b253d844a20f1a8cb25bf64b6db5fa6d255772255752ca8f88a505d7552ec431eea87bee91b6ef65b41db17e7ba46e7941a26cc8532ecf3661d3d8909f7eeb51b2e1dfe84d0d4697c2cac299764c0505b9504389c5a729a62b2f08cce0cb7f8a89415f6a3042b9fd852745cd08fb5d7563172737642a9f6bacff1642080db28ff94db5213e710038c4184fa85f0485a4bb7aeaf54fda0c6020d611c6ebd3f3b95556bd12b3f14fe85437723e47b1bbbbbbbbd65a8f1c8b952ae254413535e54d0daa9973e7fa57ab34cd397343ae9a33e6c63f8a4b110a0b8ff598d8138cd8eb62c0b08e68e958869283e5862e0575e39c7136cd836f9443e7862e0525e78d508cc5f735cdc3d930778cd1ca78dd3b07c1e7cf8f0760c6cacfe353f3a8f5b6b1215715b1d61381244b57d8ec271b3b35c8c15b8a032d754d70f76fd67529068acfba633da64de9857c879f7a46664f0492d0a9725d2a2e71749a6efca3168a11ca56e5912d2dc50bf0e0cebe6106ee3c2a21def9448e14462b979fbb1d57b08f69d085441063fc1a638c532ed56044622b2c53c9e8a166fcb1f62012740130c813b99c6c1524a61b72951179b9cf6c56242bc29572d42ff71e0d36acd9d31d2bb0f46764ee699023a458f71d21c5584158314887f41212e359ffa21f9f6ebf75de11ffa3d677cff25e42fc488c7b7aa4f5befa55fcd949a0e76dacdcd3a220dd778414eb7ea394fb20acef28c6aac32207e97edba851358af1acefa8f531e867b46d528f04fc59efe560c31a3bef48f7f6e30a859e6fcdcf0bdfc5747ef835fb59c429cdb49f45fcd2af48ac5f18b38c9f64025a5e52cf97a5df7e475adfbd7722b4be9751f7bd84b0de9f553b8ff5ac2772cab1e91971cecac609e89eebd35823042e223e25d80b7024d4d0831bbff5b343aa1cbd19bdd67b566c5867b6630536fbf8f463adcd7dfb4e57146375a4cbfacc7b01b14fff957df721d2cd4ca81fae3e3ef55e402a53ddd667b1fbb1fafac9ee595ef79af44224ae5ab95c3dd0faa61835b0c165282aae5031755b9f3da07b1a56f6da15458f487f186fd6fd583deb930dfa0784fb2cf3340ef403fceb7b25b02dd66bdd1722c9d80de95fc9d0e76637a3f4b3c9f28a6419fd2c760ff0cb755e9178b9af886f5fe85e96d198d4d967c46fbf5c791ce8d73e1328fd5e3fb2ef05a4be7f28bb1fdc60483fd2be6d3912f4110cc443ce1e0dcaafe39c2cb93010f7fb833d5b4ae9359b93cae539e753c96c3ca8536c910a7318b558f0d6a128050a150af9cd11055888d983f8c523ba5ce9e5f613c1e011285879310822bedcfe1f18944188b90d24defa452706923f2c61bd58a575c569ea7664ea9aaa24314c5a2614eedc6e40ebfa73d26d8fdedefe8d0245d29a52b1796cee98bd6d904e4a4106ca420a6d308f301eea98655946a3dc3e6e7960b0ee7e10e0fa34ccdd12b1be8492a684ceb9a3259973ceb92599f354c164712749994e09d5a6821455ca93357162369973cec9056dd2e4882964d07ab0a658a1e7888642082396cc07ffe75da87ea54049ec3084154ed04045455e6102059dc354032b3141f650dcb007d4144e6ec84f5041526fc258a3a429a19452eaa50a2a6a157652a8e20e8d32e79c93ce4922abd0228a0a3ee78a2e0d0b090834349440872aaeb0e2c9ffbc79f39455b14605695dd6c75a3071bca0f64d1a1c59bd0cf5a6cc0dedcb293e9b72ce3748ace077ce39c12ede68916f787012f98d0d4b2cc8374a5254ddd0be1072e4c869aefad81aaed8a4e887941bebf0c840459fc6a0c76e16f16def039d75d7009f1d9dd545fc4a59f4fa31affc79e52743eeba0fa25e451ded2c078a28a5947215853806350f8dbff0bbfc610ed7c7618ccb40714d0458a8c793c043f139c64e36f3478e1cb24ce33ea7fb941d7cffaa6a610ed787c1ed8ed1234ecd28986fb33a76374f9613d162aea05b30a160cc2c2920e924e0d0691ea2d0c18e17477899410c1776a40e52bc10a5704297b0cc610799e509a5945236db9299a539e79c5bb42496ce6001536689cc218a3c38bab80e021531a490c50b7660708a414561270a298c608edededebc00792363e08a715737ba96ed0410f884c8df3c98b71a3dc6e81ee8910ad12a1f4941fbd51fadbe97907aa4fdeab35d100d29cb8b525b836050a305954be3b21a2d92ee2ae9fa1663e762a0d690b02110b96e8df8ae27567e3abc8ce66fd33b92c27cfa47d9d3ef25847efdfa1dc93efb65e1587741e6571d5614841dd6e06dfe6929a557ab11af3134e2d78b8df1ce90b261e50924ebd95aedd71e840e8df8cdbe703aa5596c31db7caef40b91aee6bd7bfdd4cbe873d461f4fba83639cce46fad785d57fb8d3fb0d9d3cf3edba4c746b0f68528d4a75e18847ff5fcbd7ec4d79efbedbbedbd82dbb7f7fc85aeeab18725dc50fe10d7d5e86fabee39efc8eb47f6abd5e7dacb8c662f1becbc7e5f7df4b28f9da669da735e1d12224529dfdcec7fd0cf68a5af7df54ca0affd466b5d7daf9708fedd1bf5733f02c7942cf15fd2cf7decf50be97f202265b4289b6a98628c5c966dcd5572488332f3a10648a2c776231e1b729c127c931cc75b59997dd7de40a615bc3be7ee3e65dcb05e4966862eab276a758f05451837c6b8c48a4be365f3e07169bcab2ba2e1fa0bba22d7054b66b817bc055dd10516fc8caec88219cf65ee8cf7e98a66f8bc8caec8a70032a264e0b83264bc1d7365442941726524a9f2fc262a05237ccca9f3863560d77d6d73bb28244144252d0d453279b258cde76a3f57d3c0b8e26a618401834c0e28305c30c01765044085c31ad7816ef8737de8ba7781e7ba175dae7fd142175baeef7822e9e9d28832521084d405cf972f7440a2a44649621142c4192584b83367694af6f0f1adab8b2b61b8cda52fe9db00ac10b9aa252e7d59b9b8e1d26f592323ae9ca9a109942e26903c68b182e32ba59492c55dea83c10d6ba4d962882a2855415cfaac05144f292d028719435c5ca5d1428d16488ae48f1a353c74c31a2b9872a91aa815dc61228b2f97feaa2ba23f7628163c0061a1e6d2d7ba22ea0202aa8b3ca410183c58e0e1e14a2d5f5ab42b9a63ae78e2ca9f49288f2b942e6582cea5575cb9d44529a55d0d3b6899e109a8289cc81d82684329a551892b5fca6a0410ad035cd9a4c7d2a89366050bc2dc71d59942698618a70bead059c167aaca823a5eac20038cd30c3b4e96629471f2b1d342cf1a19766e7062a798a69a62d82c515a524f3d45b0c022f364c57462a85a76d840e1ca24b1ecc459dac254e9ecdc5995e1ec78a18599daa044a960a856539c645254da0d4fe8d454f58185d9942121472853459960c14798da32a558a14700c324a1acc013471853e577902cb198a5762d599f79394a69e986f63fa62a9d3063c4065466c2c0943063260c336f02800233564099c90206eec1ff018518d3303ec3e6cd0842d0f17721fb50679e38d9accbfab8a82e33c1b441155b5f0077b03d337050c6763ec048605746dc60b2aea81e2c8dcb5067daa839a3050726b0f632141a261c0461e56528343074b0049a18b8a0a90216292555283478aebc1cc504889a467ec040524af9335e4687c085667cd149c6e7b4f919b9c5c9bfa393537b216850dadb5e062cd8beaf7e19fff28ff148b157ff8c7ff97794c5fd339a71e413a570623346082f8208f326d6de14368c4edcd5b2854bf3882f1166bc8c1732e37bcd8f21e435bf2016bccf736c8637c5f09a5ef367fc8bbecf7c19ffa2f463cc789fef288606868d5ba2d30caf4e4f460c3b592d5bc442710aa7f0001cc56418cd16da5aadebbb19a760a16efa81857aca9d78a8e7ed6f5fc86fb80d0b0d198e3ac00da35310cc73f226060aa3d395641836e5423e45471b100d19eb4d55ea80815efdc2a7e14dd2c730907ce9f235505ae2d132a9f565c78ac54ce632475d3e40fd3008e65da6c15a7da21030900bc52c0c237ac3fa421d6d40344f66fb42171f400b0a75c0408f7e215755d9f8452c0cd4ffa6898d71a83450a2ba336b20e221edca0fe10703a434c240f2a50c6a50d24062c3a0fb2fc45588104519a5121a141465f5a15f0d5783f25d4f49e23031c5263c24b770b95a8c04312221c85410128884800d150fc9205808749893161ee2d897332e3251472465494957f8d2cf3e99cd7eea713569529b94b4c4431dbf081ceefcfe0a033592fe72fb6392152b6e686fff2775a1a2a27092ee002e434901c3dd5afc2359eeeeeeee524af9bc926337aebb197d38fd6ea8a8a44b68b0692029212669a064e9ef7822e9f6c73b3db8ef8f639a47f7fd318a7d90109f044025e9c647bafd118a87b4efef383da85a055f6e57cf9eabb56ea1e5cbf52f32d1225abb4da5795471a1d677670083657deb8dcc4b89da87442974f3c85483a51f910f095bff82276c1899989a877c252c12b21ecd636e3884c7c8cee306394f3210fdc85402fd76b99828c779803ffd6a784149f0af3f6bade4b1f2c75c60bb3c86f5bdfc39e641c3fae5d743d362ff06d1e686b1ca8e226c18745b4f36fed956726cebb21cdf1144801e02906c226080201ed22efd1e3c945d4a298d94ee6890067957580b368fb075e9d3a78fa379bc44982fdf68befc5e6e24ffe534c8ebe16058f0fb06a9f7848dcf2d1ef2a74fdfe5424eb15d8bd78fecf267998f23ff1b82af20b1cb135c945f12ebb098041737ecf1ce64c3ef6e23b6b8758d06df07b38e0b6e5cc1b11cfbf0501d29b788237f4c6a9fd78fbeb7430f5caf21bb600561c35aabec8af43512af4767f9137511bffef93050af56b4bfc6cbe95397b3bbb3ec9d66efd9f7aaaf5a332dabd98a66b4468334fb5c0c445d48946905875da8d6af66f139ca961d65ec2867d3bebe84c423d947a4d8ecd7be5f46dad14b483752cc575fbfc8c45ffdfce339992753c97d822596b0239528a5948af912a6c91c33658b3995d426d9145a9d25b78261c527fa7cf4c1ee75d655cd42326c3ef5a6cf902bbbd3b4164d8a1224a14a73ce3971e0acc89aa024c86850a299d214154c4f64c8d8d089939da1d366632e5aa89374450c5f5889f23fadcb5074cad0792242894f09cbb97a5c574baa25852403fbf23753f431bf768add020ff9d7276020f95576fe0555ac43d9f935d34ba766f26abd9db8a71c2669d59572cb93ed0fa55383528bbbbbbb3bcd6a56b39a65533a15f13c72cb4c63a58cc997f225a5d3952fe593749a5912175bcfc8ec2ebf38c34b66f6cf04feea00bcd8500ea08b0debe54827ba942edb3f966ea4bb7b411a74b11c638f066316fbf10586e5675d6139cbc62a39dde8aa11b5029b1b81e460f969ac60f9392e2c3f8d26cbcf6961f95759587ece0acbbfb962b0fff1fb698d952fbf23f225443b32ebcbef6524e94bffa691cf77ed031d16654357c631d72ae9e9c95aaa6ecf9e3cd2aae8c3bf6ea25df050fc3a06a9c36dd9e4591303655318d6df2c75f0ac04d3091bcaa62a4a45a9eacd92a20fff4ec2427d03c3fc5b0505ddb8a18106afd70d69950c1937a455d90978c83f1b32a7e8237e5d259eccb2afc618a43583e5a0fb65d7bf12c2c84005eb3f4370ffa0c12f96a5571692672453ca9c65f2e7c7ccccccb17f91735e13e6471dfe8573ce497d4e77671f43bf3f32f3836c57aeeceb17cea79ff48d29fff44c4aeab37e6c3e98b579d0d9834ab13dc3fa4dfef931d07c39db063bb3e7ccb3f7ccdd9d4ee7a4b75fa553122e9862fde51070052a3fd9a046e99c93524a69651b73596599ad2bfbfa8594d2f7d64aa394e9277f8d95ae7243db1c1c1b3fb4769b2fa5d632217b7bf9f20db3eff5c35efa56725b99e1b2496da756b3596687317fdfd1b9fb0423d90581afc6401c65fdd8ddac3e0b8a3f4ebf6d153ffa8842d661433e30069b7d17f13c79eefc000085ed9e7b18a5dfd4697baf9e3b23f1fec8600525a8103e2019aca084822bb39a7943828e6ef6e4759ed39698c47dafd5b31e2976f45a716fd4fd5196d567c4fd113bac23f24ceb07540e04095a7536a66672c505b9da576ff34222770baa755bfde6199137669ccc3167556b500fb0d6ef2a653a72b7a05b3b22dfbc217eb72d7ba2ae73ca5b2f145d8146e1c6f899c07a9e3c4c3d3f645580e3b2428aa156d13452cb45a105153e3b1df87abe0adc215fec351b27a06a14a87d6b833de4a7860fb6bdea853528e0dae28324c5dee1c6d42b77a2a0246c2a777efce2d2276a10dc3afdab3744bbd37f2efd420adc213f57360f198ba2b4dca960cbe001f0fc2e1a6c28a5dc7e0078ff1978367078ee0ee53ad3f5a5eb9ffca8e5d29f1edc3bd32bc2527e7d27a7ef1efd1e4abf5b2cfa1db73dffdd322e7695b6d25418a8df29d392cfa5c914e91a4ad5604f6c2a3ce4b7eb58b9dc2da0bbf30e03f5bf1abae6f627e08654eaf6bf7ec40fe79ddb318e8cef5a2d8871e89ae843aa79b00b517bc5e9125df2f61933aef4e61b0094b131261719a582944be372940a4e57724f13cac6383ca4cddbf3126183e7e085800febe46f4a80beefe037f080707836dec3f130cffb0e827cde14d1ffcf87fce4c6c67fff46dfff3ced5efe7d363ea39feffb8838ac8170bcc77770bcf737bc0e1ec8037a22df9aa311c777248b3f8ef77e03ef7bd0bbf1409e8defc0fbe7c0037a98d7c17bde919f07df3da39fcf3e0eef083b6c83204a6c7c47b218d978d81fa9ff1db1f1c19ea877b8c1f13fdfbfc38d8dff0fedcbdfc8c6e378a3ff9fcfba977f22e0781b6f64e38d7ebeff1d58d8ef6f3c070ffbf083e6f7e1f81b9ff73bb0b040dfc16ff0e0051f4409d077f03fde911bfff33872bc87c3fb1c38be1b379e633f39fe7ba31bfff3396e7c471e8eef891c47920601781168e0e1d0e003f044fd9a36bca58ed9f8f794304140ccc62f75ec7f7a416cbcf446e0988d774f88c7fe6f784772fc8d07f28eb218e9f80ebe23254c121003faa518ebe0757c2fff7f8e1d2dc518d0bfdc3b92c528c7f7f2cff11cbbf1475efebd44d0f138de28c7df781ddf5116a3ff1cdf5107ff7f04f4369e63389ec8813c8ebd67c36387edc0638b7018d703485787d7081cc3f147fc1413c18d17018ee7588c85910948098eefc637af743061de5cf22607de065e00bc977c8efd7cf18af7925facf27dec30cfe398e6c5022eaf25468cbf315c3786651692317e2e7dffe4c243f6dbc7e743ebf3af2f74f97cf8e0f599532e7480eaae9fe2823a6e28a76e5be0c509b91bd2a513a40c2fdc71c3b914ce23b8c1cc7e1e3050f6d26b58f63b82b2677d2178b3ef6453b67d2bd9247d4269b02753f6cd2522ac9cefcde33d8e6d36bc23b6a28a229262287448a05012cb223f86b2c98697c1bf07802506eaa5a8a464632ff150d75ab2f1ff05a87e8a4f36bc230e63878ba6180a1d12306462598808804ad27d01f1f94929cdc3bf5794ba52f70ca5734d1c7fd31fa9629517e3c8407a75065a9c4e4ae716d65e8e6a52e786d6095ba700f3d849b54113343770b074095818bb41c1e21811c3096ec860b5cb514d7668e2060ea6d8ec7254132a514d5450a1d1ac71185882a8264b3026b65e8e6a920456452d2dddb0c5883d3fa0952c00781d830e9391d3b48ca39ccb67b2b495c7ae39f8f3355c96ebef18db0220ba6ece07ce4fad2e17e8deeede2e996bdb6e29a59452cae972d5eadeeedeedeeee1e4677daddddddddddddddddddddcc1e6b5633dbdd168c891cbf7ee2cb96d3a7f469a5f4e8eeee3eddbdcacccd63dbb81827c7e1abde9e3be7b43d5c10e7838dcfb1e9996105671f9d9eb4eecc2d2959b29bdc26b4d2404f4ae9eeeeb23a33334797524aca75c58172cb28375db2b816e703c7c6f7766666890597c5c697cccc2c8362581968dce5486d4a275a9b34b35c6b955a46679eace59e676a92524a3549bfd612cb0d4a2d8fbb9c4efb0b7c95ae71ee56f6d0430f31e41472dce568529335a3938ed46a46670f313030e2c96378d0608f1e2c7b7a68cca0b2517a936696de217aa259cbcdb362b78a110c8a42b109cf2acb326e3e07a475b32b5b4a6c7ca2bea2803b1bb7e23a6d55a90780411804ae7b0ccee7ba1ff2d20910b9b856d6b2140ef89e8bc613cfa6553775537f37b5f74c4bd497c8870ab7327cba46833e3e5dd17c9f0e8478b7ef91351109e103063491e8e3b2eb2a8081180f118765868c18b56948d597b2baaafd2a41f974b716c72f906881a10446125c1e2a3d75f2741191b461a590c38c242e2851a25e71dac2c275289f0f5d6acc8d5467dec42dc6a51b5f32c9a6282638dc5056915602bf3557b533ae1aff036a727eae5ae7ac9e03f31712b9fcee69316ace9e11d97dabf5acdf9cc7cf5baf46c36495a42b5c4cda925445ca690b978e49639a4774aae1b6969854030f456b5f0cd419e5be90b3cd43d3be6d83fe5af36885ace7063b20ddb79e763f58dffac2ee877faf1fff99c07d7d13581ff7b1731edfcd0bebbdb776f6e372fd5416c45663cb6f296b6b5ab665f57385f2fac88fb7b0fd527619374565839d7d3472552e06a795317e1f2748ea10db25730d1777f4b136f2701e91fb6958fbc795a53733478dc50c245faae988d4d1842de4fa823556b0ab973cf4bacc1139ac351937cdbb526a14b8f5d3a17b2217b2600e192f49b1fe10788abb27c81bdaebc3403e633608d1076d14f2164720982fe70bf22708f65a2f7b0c9ad81882526280c5ceefad2b821297ec09f2b6d2f471811c59f6072d4eb23069c70d665d7da275355ab027c8195d51f3990f78a559502de058210a777b9f79675cfeee4090cf711fe76d2bad66744abf1bc771ab8de32c6bd3a17ba1eeb3b55ad75f30eb16602f064d408860ac50b9fe12c808ee7c39878c97a41b9f8ee04a2963dd04bf61816a4900b458d762fde517065961c0c4fafbac33ae3ee7e0baea75f66f48d06dd939a0748d6ec6bba29ef7aec8468dfb0171ece8211bbcdb13750cbff5930de82be3eb0848323821c1020625b2a071136386ad7ef3647c2b2f94778be1c5cd0b8feecb7ecfc70ddacf6708775b9f0d3595c16deb3ce95b08384f3e6fab2f3cbad525b38e63441a6563fa723e9dd3887b992165432458fce49c1f90ee8847bb62e3e253c5f6f21b48bf34322f064454adc72552c788878a14173684b1248822b0a041bac10b2a52f0c49aa617a03883a51ef1a5890b4b5bd4489971e20e9427636a70a18a34f38212197c50228a175e52e044132c984c0933450a253985a5bb301126490e5cac5461d102945c420e1a178620c10ada784953c606364ddacc3047094c1d32588a50f183152956cc14d1650c92152469b2f06108252b5188711c783042c30209dc2041c39a3261d88b28787e386229890c45c43c0a9326f06899414e5211601070c50a2a2559e84c8142aa480f767218020571b248e14b12121f78c0f24445c78d12333411461b20f0a8417243145216195878d374c4164c501af00186285190c0f3660a05484c48be809344c40b4c5978d12189282827a0a0c9d243122e28beb800c60b4b9078e1e9d5449326a29ea2c890844422ce1b26a0f2cc80e4c9982e3d749b33499871a24c0a787e807204961f5334c8f004551518d0482e0ee7688a9492921b92ec70c5882fb248c1c1ce114f9c48228bb02c3081a50c1e20bcd040440d5257527053039425e4c82db379b0001c12acd0c59c1380791a824a082849ba8c31c2890c42fccc2084933a2e8019834706b1c110456c21e70b0c4b3c915a6201e29c3b1828488cfd183dba7497d2047fcbf9b894f2db06e0c532736c61e39fed7fdbd1e08423e55a96a282caf5e8cd0a8faef605c99ecfb4af861b1b1a91d7ff33af2bb259966599b31cab7d4fd8ec897ca57d6683953fe78330f61101d6317b0cdd98414d82c3e6134008cb178479ab77b780ecb320acfab357f6b5c197f6f433ff97f63103655fa5fac2cc5282534018b22ccb3249b92b9f5969a35936e79c349392f9d9a564965688bc0cfb6ab55a497777e9e2ea5dc732b3e5724193f53959f21429a59430068a43eee04f8d70ce39259d35f32624b394cccc524a29a5945232b313ac2ab6c15d00fed81579db62a27f5a16fb63fdceb2efe88f8ef891621c97fa6d2cfb5ef30ba284484c7298fc1c6e2cc7b4cf92e52542f6ab37ca7e95511965af7d47447e81a7ec439ee25864a21aeadb58babeb1bde56ba706e57b66ac7f3f295929656520fe4660398a853b37690496a35a48ba1a0f55285151b4cc1fb0a85158f82259e79c73ce6f86a72c2988422a4e9dd804e2ce29ef9c73ce282876aeac320aedb3d5afe25ca0bbbb2b9a9991797d6a9a94ac56967d8b2f708d4c6f0f071b5dabff5c1f1176f55eb4aa736a9aa6d1baad3c49572bc642c677df3801514c4e709e3ace5add7cf7b1ddd9fc699817ff6a6dfe8b962459b65e45f5301c193b4607a37750501838e4f72ef97e411359d34829074d3a77528e41ae17eefc1a3be6fc1a656c92940bc5e8c7675895b65d9f552297d218437214644727e8e4b084ba5952479ea15382254f986e08e2887af2e4facf1326782403eaeac78316cab6678ea8041ea25f2debf983feae42ba27ee7cae85826a341577c670fd2368d3f0100b2399f0e1fa00b4a13b9fd9cb9d5db24b3f01309003a8940278c0f5e79e3ba9f3263b20ad1f0a6c2baa553a23730deb276aae824be0a1eab12bd2ec4ac4bc91835d71e1c1f6e02786e2a9356ce2dc59410855f47294933437fca02dcc45d5103e709102869730f2045b7871030c29b0810c1236fc8025c825c50a2edc2c718309a27832473acd3373a20cf15b94133237e427285e739dd9506597a39c28f1840b0c6a2995a9e4b201e76e6c86c0c00e65bab860064e17282658f26632b1444e0d486aa05343d2172820020b1f9e9023656e804a933356a058b303179985c9d28a09262d38993f9f6813a529b436dbea7294132e576cccee85ae382465fff42bd2baac8fb54feca65d8e7202844cc1c7449b945ad5b41ab34863f4e862ea8dee4aaec7a406659426868ac2f247270957be7b174cb1fc52724bf68e6eb60c54b0fc910121307f80d4b1973cc39841068a3f35a4e5cf66d44bb03def60201b622cf3d768707a4b2943ee5a8e6777fc9658304ba794ba9c9e0e4456b2c75ab2f27988cf8df23959e98c69985d0ce4534379e7bd7bd8f8e7b3cc595096654fc199516a5d73d6ca94259d328b9369fc9a3d675ea0eda8044e974435e9d0d4000000404000e314000020100a07042281301c1ed2e55dee14000d7992466e5c9ccc635190c32808216388210410000801060898a9a1210e00686d76025bc4ea29c66fddb6a2625ef32650a17d51929d62e56f757534b5b9e7b69a46ae0504606dd7041902b0ed88c9166c7edae2f917205454e88abc5ac38df461653ceac3153de72f6d48708df4bdc8f5494b6e6f0e589b8d05e1a32f599c0eb4703af805f5e7ba19063c3213553b890b1e3413f8104878e29d927f86f3302866c12b308e9971a5f0e562c1f5372fe18aaf8e2e40d0dbf772add0bb868d736f6fa555237dfae0d3b0d09a62de1bf91eb8496d23c5ea087cc370f1123ee8c4dd3f680660bf4b8ff0ee53012625341a6c7d3a262ef00e23d8b42b83ae8125ec0359d869361d26e8998536ce15c471e4ef808d8917825f6f8af2787bd0dea78a4cc528c7fa1ccb668f297dba1a2587c1d45e900e0e1a3020ba5062eee10cb5761b9ebed3d6a6d0672b8cf10a10ad077cb63340cf968c0f51a2c04565806f62d25b8e8db508a6099e4a810b9d053c74d86a66cec8875fddde71ff2ec76a5123d2803790b430886eec3d83b57e1dd3db0386ad98029a74eef4ad1fd92abbf0e77a4fb6aa8ac22bc1126c21b4315717640458ab3492e4d0679644ed8d371a52efccc24715b1d5a908ab0ba4bd7c94ae6032833ea11ccf7efa2c004ba4fe30ee2a1138e6c6868bb86721ae3d088a38a0e8c49e63b24a275a614fa83877dba3a46d7bc2abe565ac528244b10bee5e192a90248b978777b897ca8af3bc37313b80edf8696418bf91832ddad7aa5055822c6eacdf814f3f9f9f701fc49ee5b94b70642832cb9b883f994f2a0050fd767cb1d6a7618dd35b7f49a74726bc5303fdda29631a435b091c33f32810c0cdf439f1d1c71e520e206f798ef2713ae4502fd133463345999c6d44b54c9753a4b943d2c1c56e8cd6e248e01d70bb887a2bb224da2517a133f668b32dddd59f4438ffe030bc036072a3eba9ab97fe5a621d31ff371be3ff8f3cda7b6446c797ae9ff1a3c0dd77c0202b91eb36f6048e8b42033d1e9ec14d211a7d430a44e1c9956731f50156ea6d7b70d1140bd850de7c9e4e5409e4519fcfad5aa90cf606e1bd68e2c8f49a2db2e94a3cd7ed0a1e52790bca95181efaab69a04bc593b91ce64548bd3343ebb108e882c57cf4be98d06fcdce6aba45422417e8d4c8a8cd9fd09dc95ac0e273bbf35c6a92830b3daf1e96bd7448a6a9691c5377bf6d35ab30331450c9926b19468c9792ef4b554f52349020eb109f6ea8c8540457cf80334706f511ab5419a891c01b27f28bac0e8878f851d49142885f64ffbe325bffc7080865f3c8d1ae5391de18526bce8b43d3422a994182d73656537c21ed39cf4a578c435568bd8dcd4d1d0d4b8aa0c8cab19efc2c352b5157125c28f771c5eb7b36f7ac89eb8ae6fd25f3b9b8feb4ce8c9861a7ca6a4a6121de7eb91ffc4aceb45df6e58e185c55316ff4898059a018caed30ba1dff6e5e5d2bc7489a09d323a2cee41a363990e621e5ae9236bf1e9d04c747c1cf713ba3a6c59d23ccee2e929b1fcefdb7445e8f12382c344a3bc2bb8fd904dc6bf3d66c6924d4c746884e0300298393c069081b51d59ed0efa673ae13ac788742757d91846fffe312d22b0b064a9269be70c4f592c0c6c0de52d8ced45ef73c43a5beb5b2ea119b026b36a1effb37f5a12692677ca2c5541421b230b03791102770890cdc7c70a662f1e82aaadd35591c2b36d7013a371e49e4de7d03423f2dae50a3b90e9179cef4ccac2c355af7b1e8bda115ff7f73a32137f52a4f59eb8c6910e1a1193ef2084d82c9d356319c934e1fdc96888d0be44f164cb03cc48e12cf2cb060b0a25a9e54b91e06353967ab779355891c3dc4d5630fd838d8540f72d9e2e0a637784e68821893b91e26c441476473e3c5608e067d75ecc04043e73b800a7dec75044d97c73f0e933ce1b83430b4a851104e11e1a6b2069e4a1c1a262103ca332bca7b0e252e9c66c4dfdf2ae04954229363bea1f88c183b70ecc34a2b2302d3ba0bb9a111c06ec5a2dca278802217fb0855a3ec8070b1176240771efdec29d0b4880d4371392a170cdf200faa9804599f96c90c7dc90260072255633517ff0fb5095a771c7c1dc51dc867956f830a24485d39f37050231e5fcb01e0449c1a2c49b1e4c41481a94a700c177649de02bde910e27fa12161399dfc84bcac462f9142f5b20ac5b29a50cfa442a04ba756843f75e50becbe095baebe4b075a1e2dedb473f07352a268f32e322013f4ac1caec9e9796843fbf014747798d3180c661dd459b583abc0c72ffa2403f583034288187c83ee4262581cfb45a1e6eff1c18333dc76a1b123518cfc9219879c90dc6413baf3b142cd965ecc06d11508dbb941cdb956c18dc67141a85ae572d5e9e7e78289497fb28733aa74b38115f72453178729f537c645adc677481290f448b568a5e7734bc7e30ed2fdb19473908e08ef89f0e4f7324e7f1c9dbb86166edac1bf0225e2ce3801a1ff61ed21c025793a38f267ec88b38328e908ff60cdcb51542c8d424ef440efdbd7eab13d3dd9c8ee930882250051dc5fdeeb70b2ac9eb2ef950bd21cbd697f544aa562c5bdc5e1dba3a89c20da006ab3577c6090257b5ab837564a2eb0faf6063c6d3c8dc238054fabd9e346ee26029deb5232d786cbcb997eb9f237288aab003a534f990912bad043603937329654630adb6c17aa52727e42b9e776e716128f077f3333990953b6d51d344adff581943dceb93a3455d0b577294bc41bec01d431489a681f505c165eef26738811023a4f2a564dd7112fdff8d491e168d15e8f3501af80b5d1e19542ba325cbacb755d2b8af9717eca17680e11b6e5bbbc04bd11a8ab82dda34442adbcf5f4268d1d0adccebda03c2d016e87eb329efce80ba8b5c80addcc018a646550acf33944ce4a8c0a3a99038ccccaa050f73980bb598976e5920d47792da8ba2a7cd5e94a0c0a583de184e8f67ebe25b5d8c4c9c1daa61cc4dd4a2529296f5dcf99226e21b98ab904fe9253dc4fe610150284f374145d57f6d9334f104b7a92c2783915f6d230b1f5a9a1bed31536ed668cea09d414b6979e37ad5864ea0e712b725889622f2d616fe61611e03ea358e44e46af0981ccf3b25a462220d52f354dedcecd5d3f03a2169f892820944e343bb2c9455ae24665a05401e39b6f7867f7a178a544c27a1c51888c447f7710021309b1bfd87fcac3a9ce0babc7d84fde08063f1a8fec58de1666fe375287476adf88e8d03a83309f524de2674618c0ed83842607ac8ddd1bdc5326ee942db97e00be6c4d95042884d9363ab6d3128274de8e879d587779b72e62bb96127844eaa1f1eabfd65292cdf32cd2ab58d76b040ed1b39d738a4f154fdd0ff34a2dc6a1cc05a078814b075386993addf53cb2284628838aead645c15605ada73443d9b002004d930980570187ca6d7f22dcdac215541e626cb6888e985c4e0487d8e2286a221a3dba8987177bbb57b52163203ce118a1ba0a546844fa000b36aace6391cc654bbc9427b45ab9a484d59b48a9a16ceb9f3a1f81479393486371a2e6703064575dce91a832a90fdd323954a60820eca2fa9b24497b94651915cdc36438809cec30630136b7514f7169a381f181c4f93c2dd854806273e69b96b74da636c1af04d7539f3d9206487aba6d63eb9d8ae9f70d8499cba1750f5d4e032e3b8593eb412a36e382715c64f066c874f6970150148bf9182abd3ad5ae846b1ed687ccda5cd9b96800ab09087052ed6927c1c444a5fbf287ace9521cf27cd561892629188c58425f26258a2ef57351e5cb1955a56500e640e0e2c6df48a5dcd24d071482bef17d1899fd9f5630b3c036568aec8d0f6169bab3b695ee320130083f9f82d6ef2201de83da92fff4a66424813cdb2031f3da18ae33a7dae28d83102a9a3f3ee06c46f1d44f83b7824e2201297183c742391ef22a5b2de7472c4a7d4c6f47b809f68723112eea94036ea46fa763d8dcde494fc82f2e0606f80e583749591a2012c1d5a04f4b26d545a89ef91098c5cc8c3ca2dc80be25d28405725a46c9d1aca405028b1073ef6d2f94ce8a7368451c9490afecad731e617e682989c002f0d1d848f63340ee072ceb5767fb370596f4c182cf6a836d63757370a8320ee74cddd8ad0cc46927e8f9e64fa6c1c4587df416f96d9798aae74089c3ec5f0d5075c18c3763dd516627cfbc9b2e8df870996cc7a99d3e5631b9c90b64589ed12f1cae303f8d52ca09da7c26865a5a6f66fff17bd45245a315f5df46e7aea209535e5e30643bbf9388dd477456aa2a00ad4951520f4d115801f0be5e62670a9ae6fbece34b9f6cf672ee60ee0ead44152a26a3fae43e9f4588daa9010d97c7e7b0bf3c0551b55029443d6c9c00135f0d7830a266522780fa1cb1b79ae7ea0e8b14f0b3592de045824b857558e5cff771bfc9ed31ffa6b159fdee0964a6b7a534b6b0b159793b1d0c9a9c9c24bce3e66eb8cfc4bce036ecec3fa88b40706299a6698f4bcda375b51ad53854a0fa537ef266b37e9f2a718818cb075aa6d7703b5cbd5458bb027c58d54ef05f187c1799177ba2f7ad38c5de65bb4ab96a161f6e46dba55fa8d2908fdecb5a622ec76821400a23fd771735cee9dbae0df327f24846f426b84857d027f7b4ce3b6d9994f29466127cf1f4eb852f1509791d416975cc5576f68b2eb0813cc6cf025f77fca11b365f6688aee19c7e856055fa5202cd01dd05447bd22088f42d0d06e808588bd35394526e023e74e1eaea32b282610c7bf4fa4fde324a374b4cde461b7709ce3560abd8592aee09afd0d3fb4efb25bd9ed5a9e2a1fbf9fdba4bef0af6d5f74db0c0dad119684f9471bb57e5bc0df284472af5bffdb98623d08c45fb29d7b905ec28e353409cd8fbfc3c5e4fede3a8f2af098c073ffe6156539dd7c65fcf1e12c3410a7b69a5a24bb20af60b6c707b49b1d4bbb89c50eaa91c077ebf3c5b5db1ddfeb78d4e993807f1a34f62e6d9a8f2e9a5307703304f2787700504a8f61b467cafe2e9c80e10e9e351242c40da681931aa1736100cb7196ee7802ed58efcdb7dd41c76a54f01c2d94b38880324dd9686a0e087aa8efc647adfb53b21cbe24ea0a625ee140d4762277ba9f4affdadb938622f6a288f3206ed7b1c76ca34e9a1bd000a2dd53a424b96558974dd1eb75991e051dcccbfa83e81a22bc40a3bbd472c7f623d0d60b2bac20c48e9aa52720def41c406505c0cd064784d4ad74339ecb926ae4736ac33d5bb5c0b0bf833f0eb4feed4409b5793b879ac4f9da3938101215fc84739c4a78e81adb26dcd612959e08f19fa68928b55d36a715156f08e084733ce8c686fef5281e8d9f97fd146aa81a5a45aee9a72c426c479f4820db4dfb9a3122447dd75a4c7f69679991247902e80793237fb42853bad956f94c7ae5f2edb4638fd7b510c6b962026e3567eb26543b35f9789aaa5002ae5f311cb1a5798d42ce1f1e9d7a4d77da7705bcced0fb37af384891a470d2a63b2886cafd7d0930148e77e1200c207264232e931cc4935afe50e61bc5f8d38c09d1d85ef1994803de4517fd0601e40dfbf91030bc49e8fb818036d03ea29bd0135d20b90ca8d39077286dc7f90851a79b598cd5d5dc711f95496864ba1418173c3f0eb74a8cc7e130896e7b99b03f705f4ff8b4fdeb26c31f59806cdcc4f09d3084044554d1b052c0691e9ba0c6c1b4bfef820ce8d3f4e81411cf9d89e47575a8247302fde9bd864c628585df5918f07b270d202a938c58d1c4f70591d6887ed0c4b07916c66c71019e86dfd08831d08593892a41db5499d17345dde2f39aacf439213882ae02dbef297dc7fbb94ccda52a80ad4d5ddccf2dc73997545b6c39b1f29a105e5e10cccc06d5ff79954632ac034cc395db74b5271e0f1184209474cb4155c88a8d409082df84124cb5ce45af10656ef70b8cd49a67c5ce923ad14983ac3ab530a710638c66434738e03299f25be424011d4e2dfa94a71b63270f9bc0db460325e402953f1517ab84eb0ab628451eb0a474d4bd8d755298d670e14699611b4ae1454a0485728536c6ee46aae4bdc678e7c2f59e23fed3a6c86f87874045d571135e438fc35bbe4a12504ac96ed4309d64ddc2d021b80eca9b2c676bf9de37164d61ed2ff9ba205fb23d285d59ad5d0632b8e0c45942c17430e20559ffa7d8d69a9a5a46ab4506d0ac0147fb1e53431788e8dbbb44e9df60394c6e47c624a28ed0c1da21f97549357b16d4c998457170f0e82628b4f1733696143322f196f7d3f978825f3a0b14478e1c94de9cd0d961e82478223a705421a3a5d47ed49a0e8a57025e6c765bbd8d2cb193acd09492e6042cde10aaa1b6024e226123908891b69ac0f24205934c1bcac72d011103744640462dac81c4ab7404de9170d629d1edd2a95801a962c4d7934f10c127abcd8c21c91217ecf666a99f4f8b9bc421d4a8832618a7a1b503557f67add8e4edb6f8155d174ae0848c8800d4e411d847ab6b084f4b49daffeb5f9c7af769a887245ea40b55bb2e3be2c643771686fc5f6ca27f4a5af6b7a091f7890d36c0ac8761e8bc047864dd09cc59abf21c638f29ee78088d75de7979b759d3800363d57fd7a01a939974a912d71d2834f8c9ca82b942d0fca5d849fa3e9cf23c12611a355fa22ff3397a44a9e86b1ca6d5281d6a67f4a7bd706a7939c17cfa753910e99b730ea713ae976c5701ecabcf3323d01c0a6fc1e85e60fc82fcd4d9b4779eb4464e9f33914289d3dc5521d1f331025d7f3d1ab5f88c2ee2ec72b274583d01fa3dadbf73d4fcbd3beb3e38fa7b962748761c07d104a4a62ac9fb2442fb691d83ada38153f65164de908146378ac870ed904ab8aa0f6f6608293dea14f68a10288766f70accaaa3d66ad4ec19ff266b4427a240bca7d17b27e74f9bbd272930f9ac40aa9ddf0ad088fe16fc3f41196c10d4563c53991a27453b4aea9d1eeb37d52fe06ac2328e6382daf5c60610638b0032159d5caec79adf47e861cb439d60c8231cf0ea05edc48382dc62a836c49c24b34a285cc5783613501101f630d5b545d679918955cec7aa40cafccf208044306029cbf03bbeb2c9b7dfe2aa3102a9dde11859c45a5daea4f2c6cfc506b9e63319cb90c4c90c58de303071196b7d363f9bc82e6c4c5882bc661b472fce6c836c3cb6952192a3fe74aa3f0e516aa24655c579e052b9b45dc74da903df117e411ce32b2c94cfcfb11249990ba9e4312fd84f8e114ba6c184ac91ea88b692d09c01401802ced0c6b2718d83c95ca4f38fd4f00c08f8b8e30f019c2799df2b953995fd32a314d6ae03395ddbc732c60c7d66f428297e5d802dec21a26d4e9881016d34ab9b28ed0012e3302c10a9902f2d70d0e6b4ff4ff8beaace306931d093f5cad0cd0a1657a5331484e71fda0bf5d5287b0424f25187a7b0514ecdf6699ec5f85147475e4993eadf79ffb8016c9e1ef16362041c063dd373590d14caf8fb191410e62c23bc4b6cb04f3105d4c70858b2a41cfa0128b3c5899822cb79d047b1687834dd053a242d1687d2854f8e9273f8ced39e82d10d9c0d10399ff4d7aaa75d8f08a38c108a80731d31b792108ac21e09396d5dcf1f9606bff035113687fcbc695ba3d19110d3e9fe9307600126e0e0000e166ce053633df90058e0337d882c84ce184cfc8b9b1c6385556258e1de4031c653609f20949136ec5d639b1da4d100bb5c48d4f42b17be11bd3559aa1146c16cd2e160d42354eb84c150c1839708c646b064e06e04d085e3261e23c9fd105ac363000bc60441416a299970701609e3b541b6e5045b107b3619c4f496a725b6112e0cd5c4d6ea1fd55f9585981df823acf7939d974c98097d9608abefff5d29fe1c88519b20c652780b632683313624f1f36ef5575cdf6dab54cf6afa1b1301d5c0f8c0bc2196142b04c115748494c20d2c702d84c80d51c4560d0aa5f7b1bb10deba8833a06540f547582209eb3643c356a914661d811667cf2aef048b29ddb45a09a8eb2e6e12968b9bcf40d8bae67d89df437d41a0d063d912dafd639fb7c393fbb0bbcaf1720c09821c9316f36a36d0f0a663bb546dd98e51dd4ed65cbf240064737662d252742b994fcef1bac11060e9b1c290b8570915c623ffd5506cbf02b1e8671323ee0d7b0764d906442a240c7e6bbc147dde0c391e2147dc9cc9a916006d545daa04109458fd3e39803a85ef6ecb8b98982f7d3cd52cb34b0aafea94c562ce26e04b53006a7ad2f0c1937eb7d065eef0fcad34ff6ae80dce7c0e72cf471d69565fe5eebe158a1e95d8d6160abc4782328e5e9ca8438aaf43843dd70690b44f02e5bc894cecdcf8c4dcaffd72dc6287285aba75e03adccd9ccfea2782c302f64be619a09af731410f72614b85407f9bc9f1ac5728f8a38c8fa30bc1f33bc05476c1d4355e235f9b198760f0174120c32e400ea5a9bae1044194e79f42bc9dc6282d2e8e503f4afff1d222d027d2e80386c9521a4737d9f19d5857926cd8f45f99b84e3d5ac10883664f674bfd5d3bed864f302e2e503f3b038ae0974c2707c42fef15a8ac56aaef21b2cbcfa5658b2aef3ba27c27df53e9742cefbc741f42f3c644318201bae7540184792af7e95e87f6b27a94631543330ffb8cc2a062a366a7ade77c166490ae2e0206073933d7ec2a9dbf4e15be9e5a5a9ccab5d2c6cccfdc984f89970ed49577a9669247fde50920b31aaea972d25a7849b893cf16a0ef41b7c8977a2aa18e7588bb5242327db9008d12f5a2a593e14616c0a634fe6731741c04ace10c3754acdc06faa11d3a61381fcfdca365e41eaa37f516550589e91fb1fa34a7f591fc42270d1f85d3e69f297d5a76795436ecf6e9a8968ec686b575e703e3ea8fe10164f7bf587dc96c6f50a4a71efc3c9e070b0e98ef6a8c512edc3071ebe8d16fd89781ae45e010cf24eee2958dc61fa5c30699af775235f552f849676978bfbee5c752242596185527380f1cd40e18c5bef5e599aa335ee04d6c77ddaec1fdc1217c5c127374b3b69c139fa19b83c7998edb182dda062e757e5be1a2b6cb6d36b44968cf1dca232a3658fd299216342724067762365cff57e14bc4d6470e5cc75ec7a09fbaa49be4dd76f3f9626dd92b3755a2d11eae9089a8733630ab481a9e3d461943bbf1bcb5dc750292c1b5ec0f6113bfc9baa8d258a6c03bd29536f62691fa4a200ca6e24767e9dd9b1ad5aca15a251c57f7d615d0ebc2a8943d256095a28835d16ad3d6e60f26411beadd4ce208558abdd529f4c7ef29ac36f8e0726e5050ab329d6b041ee84db56cd83b9b95191aeaeb2a3592edf681721d5c45f001295e8a89e9446e78ddbe23f5f15bfbfdf1df1bf63dbeeb431c906279563457126b2a7481c6b7ba0fb4f8d8e30ebeb88e82f2b350d47f1bd243cb4bbe416b786ef6d193922cd16c5548f5b3b2d5acb3245019201f383d3047974cbc8f3160fa7e107f0cd0177ea67a79ffffd0e25e42138a03e0924702976104cca260c79f78c17a53800b2ca0c6603f2841649a411ceb6adabd228ea97622a2c3ad195cf9e8c029d218e3f3731bd741c018807db42b59a44885b40772b0488cfb0f3712803bec5f1afee58f6237aa9904c4abfdd09d24c22889d924754512b6e435b4ec22753ac1fbcbc8692001bbf386268436ba12afc57710584f4420e4dbc04a2716c5adf49a14a1dbe201107b737c88a07b91b0ceabdcdd53676d5104c3203a71eb93ba402e26a8764bb4bf069d957c795c62751614a91dd2003fc8f5b0af86174049e2867867c0ac114f1e40ade705abcdfc8e4e1458719d1e199c2f184527313cb0da23ec01afa53a2006f37ccaa3d50acfd8e8416aa705d49e058b2b82da0d7bcb3e0420c824d8136306e6b0301e501f00987c21c1b9173ab946e931638c9d56a794cc0b3351da7a604ef8d4b9bc3e8c2a1b004cbd60f58f72b0163e43b16a9b00e6eef7b3c3fd2b886ace554ee41ed11e31fb3816d1ab1a07165855057baaf5aa13f38d629f88b5d0901eca002e4d80357814a437357e0468924dae70ac5293bc734790a18c7910041f99a8efd39f673198574bed10744bfbc0ea5471f8c1efffb4556db3fb618d91d041eeb057f1d436456205315eb75469365dcfe1fdc22009c15d8515306c4fa61db008c9ffd70aebe56504c6c38c8414f00ff9a0084ae8f928824609b259beda0bad48d33b408adc1a10a51175919fbcbbe057ce4041c7be2c0bcaf9d772ae05b48523a962cb06070210d94a0b2822f3fb030e21a08226702a2870a9cc637f8c2b26660cb699dec04f55ae4b8814a27825f01dd3e8134d082f07371799664842184afea0442f00927eb038ad4018e760c4f0b746e19d21ef70a8400d8fc89230e151207de4e0c7d5b32fee2f775c3063238dc205b52c208a860d296bbcc2d67d03eab7e588b25f271c87e37a6bea86e05026acee08d694655c62f6c043209a868c632602271383ea3e6952b08c0ce28ec010f2d62ea4de050004451e460cd4c4121cc09caf28ecffb41d74ff0bd3ee48ea25d71748504112df0bc35a1d6e9d9382c069e63c60d88289e837638596f57fa22da3e7ce906fb85202efef24356e0ad964f6b0be5b2452678af4e6fea45d28b4f8e138c77d1f4321ede96b3bd0f21223a1520d7b74b1ac60c4d1cdde05d054e47f8e01d2e44d0e8ce86ae1bcc030af4d554eafca9cb325fb2af676aa3d125db7b4c5571daf742bc7f80be3b8ca1474324332a4db35cbf6ccc7bfef3a1a7c51a38bd02f494230ff4baf7a6693f9aedb5bd1ea6f80d9ce8a17b35f51b079bff1e76291297ac81bd95aa39a708940fd4ec86f2afbb97b4dcb4baecb008dfeabf9ca52f1b4dda758fe388ac68f6589f2b6dad34554db2b74860ec5526bed67b95dbdf3e8ff87a953c324a24efa7870cf3d73025020270207bf7c4be9bc8a09e3130473def0563ea4353304c5d4c3383458a0100fddca114f74e378fa06145f5faec7c772e9fb9c1e78cef7cfff22e844b02ae7457b07691fa49cd106e03b1b42956ffb91929e3d0f00f2e2dd6ed5db63974e18ae3cd459d9268d4e20bc9edfa95124920783bb1d06c5d35c8191e6d455ff0a3a90d309c7c25c9af449d5ba745fa10609621a9817393973bf4b4ea75958faa39eb0715c35a2a3de6d6976a82a841ef45afdc1f24b06a6003e0b3a7da9e5c1b87f054aa09efbad578037dbdfdd7d2133abe925be59f9e7fd683be7362ca46e37923f00bc8768110ad85c387c76ecebd8875e97ec47421e6093631d09ba734fecceb8af3614fe20eb778d06cf6bf4ec1bc1f02dd391ce80fa96cd7877c0f501bdccfe8487ebc87788acc1445bd3204323f871f3fe0a28f130fdce254200b3f1a36d71a1b6147143f4a430517688f82f7f4e40fbe7bd29f864964b3017ffed1bf2f69766afc7aa372dea1c021aacb624dc06b12227e0cf1600802573ffa4e113e27fc16569bce7616da9da64681e4acf896d45eab051c55cb3de88c2cb8e9d6282e5dee3fd25c88caf75eaaed1ffe75a7682d4b3558aad1a1520d7dea7ec1c1b879e099c0e5a185628abdc28035ff34867a855011efba09fe7ec1e080aa2ffd5d9f194a16552e7c57a9605076d50b79975130283755b4f25df301368536e93dd48669b2aa2e80a990952cf42a7ac1366c946a5bdd02671a495eda21a8dd1bd54c67042028a4ffdd5ec2e3a2d943d0cc8cf2cda0d608090898c939a828457a5ca01774cb12a1963c265f8777caa62b39042c8176de6d63ca2903c0b6982f45375a44f85fd942077aaa7564584259b29ace640b53609d67e0306e757699d01b30ddc2cbb6323427db79cd07c9dd2fa0ec56820e00d1012a2953ebaa1e751ddd44e378c2ce97cc6281162e64a8be58d6c59ff8bb73e862a6be3165103c5c1a845825b93f1921ab432ab021d3e095307f8b836ae77ad62e8d090134a48f2b3edb08f891c2c8ac3ce18492006d6e84cb725f1164b5827fd6caace9b0150f1462d25939f0e075439df17b71f53bb5036e0ff77547a3ac59f3da65ebfb673b8662e88a2722511fbcca8e191896f5804562b1b7e383952858b71ade6407b42ff952f9d7cc09411e84b1f2ab9a3c634cfd258366146ef8a883208307b6091aa6de65c7f5607b780093bb429429694e34ee6fb8bf64b1ab45274a1bd1b5a11615b20a4b3c175cdec3a376f4c04bdd7213b9ebfd14192fa8cdcbd7a3ea0d67c706d650558684fbc0284ad0d5566a75b2b315ade236f36c82709b561e59ffa63f6808137d157fa0ae4754ad479947fd47510a4972bbc5523b73c47b8f505d24eb84e77856cade1702aef08b9d115b99f1246d271f1a2abcfc6c0af38b51a7779c500d74353352c98e825e3c710b8c5ec26940e8ed90de734da7abe28fd5b132874c107198c9decee9626a57083a30d822218f9b781c3395761b903be931b19132748c4372b1ccdc487d56629fd775baa481a7aed10005d5b1f63a023854295e80dbeee46a99df02216fb6a22142622d363292953843f5c5eaca138205bd5a4654c37bac8865ba2584b4b79ecbcf456b6132120425d71650c2324532b982a988e5361c95e114dbf0a8897a312296fbdbd10762e466e0c8f45623356ee903c45420ba12d390ab8780f76c14b08c64839bdf1f9caea41756170bc82022834ffff51cb1fda1e00942a8dce1ecabfb7bfb4785795e5c05f4f1d1a9a144e404683d85653b758200b984a151aa9339324d48b5c3dd1588bab30e335e9cc0bddc6f27f226ec17aa8e9b92cf9ce5999b46ed016f40124f8ae8aef2b85f93ba8cccefb7b1e7742967abc134e5774fff2477b997b570d74571037b490ccdd4d1110ff28401731b2ffb680d28f80a0606ad8aee68c7d3c60ae79a015d5a1aae5df92c74f45211a1fc8070411e78df07ed0e9c27f4e98f9833df9d906d7f2f150391639cef8a3994f0e968096395d9f00e1a2f4f358bbe7ed22d657ea8fd9da9c5edf21440480b606840ded9be0b0d5a4db98dd9d650381c2f1147fffda3d51e13c124e4dae72d5b59c9f4456148e026157f4516f95dc1261488878fabfe755a65c644f11b78018bbbc429fe1a79d861051fe5aa38fd7fec7e653e1728cf6ce847b8643c770853f19741fca90de277315a9a23ea1843d6c44aba2b46d67de4fa5ec1b037fe37d59586305b1835ec0414f4f1c867778154831f8311806b2b0d29923e96a77d19284fbf5518976084b19f54ffde40508fc8a4ea41f27d6817defe93125e9d3035ad687f8f460b5b9dd0616885a4eb540f02792f93b2cf547646315633c8427b9b80401b807da220a671fe0aa22da59bac88d90a7e8a513c9f809e80c84338faeb66b52ea111be9daea01168c49af7fd67691055f1ab15c384d0104715430f32ba7ce080f5bfa05509a95d0328738b1dfd9dc6771515c19a3f4db04f9a24911081e722198753a510133a3c05ce5e30ba983ea7585905940c8202938bc0c6835127306b394f6fcf1e5836b1d9d6eba112badd6f08db63c7a706932098e312f4f200695bad3751011e8b5dfe0053fae8ff2d5c354f62ddae0bfe2158813dfbd4029c2f5fceb959824e2c7ab18f513e7f293a023c162119761e2cc1520075b2f7be552faaa70c1bd6318f3bd6cb6fa64224671ed6acdcb2e996661373b44f065350d0a2e56fea12bc6cfe326ccc1b64513f117fa47e0ca8c583eb25bac76dd47801cafa753f195dd99408ea4dc582564b228fc505ad6cadb22bd22fb5ad412163039aaafebbf07b00c8798034aa81159635892c737653b082b9d2a723dcd4b5260f0d43c41df65bef89c507b2fc3764eaae98e0bf6000cb36f503c8a584d84b7d8c42eff3d05d8331a3126846b390f198d249139448f86f07e50dba15ca7ea3a86622f4408d191cf0dc4f854fd1b52c50c5128c41ee071e37f714eddd1450a64ff8fc6988dbc3b848c4a8684d02b12d87e0d0d3185344e053c89f974a4694c47d54d76cc352d74f86502ff871974f3bbfc27cfc3aa303161ee9a84880953b275a7a759cb7ec9155ede82be28c3f182dd9cdbdf315969d52b6eeefa3a27336c255951e4ebf8556a04b4322eb431a3afc0ccd49508551c24d43a7e1f3895aa0d8e9d72c15da0f8103dfa1f9fb1bedf88d1bc1d7d3f34bcda9f41e26d44b864cc0c2895525f362cd7257d11aae8680d33f56cc98adc548b47bba82430f7d01bd0a50ad9bc9d586eee2ef3620f95f3f37d7e8cac4ccdc17f7e91486dd5018ad3f2257dc475594eb5e8dce5c5408195d052b4d92fae966fafd851e458143ba19bc4e5d9cd3ccd3fc89894b62d96e0de96b8000b760de82d7733c8bfa25f4f5ae0373cf9f8eb2fea7903be204931727066744d0f933e2b24a5e48c262367379d49000b65c968560233a851a4208bf2f89a2d7e11a37478f588c26a1072a446c2d7f46e6700aed9cb9a358505b60d88963dced832ef99dd989eb43cb4a5a69b00b49e02e00a618c63349140041a62acdea08c18c214f9dce10243d69850e5eacefeeca4e4f87396b6aac652b748d72064faa23c275d668c018a685b44e12637cc7f90c2ad8f3236c70ade26fc4310d4435d7514bc708e1e54757325d7d8aca953c0b6a7b2eac1e91152480bd3d3943dd95f636483302483fa22c4e691279907af25fcc833e1688731da7a85a8bcf2c7090a236b755cbb7085f446214e6b36c02d1b9a228769ec69b85f69f8a525e90a7f9b94aeb73d7834b2cf0f84fcc801078361d228138ac043d59e54c240c269d16584950f60a08ff46ccbb1b128cce20305d8a334d2a4a65b3e6d6e646a90c0d4c4abd7f5516f0c0f5fbd7caf1435063799d972904554fff0f5c7aa9ea8127fae8be4b3c112bd8a34ffb2547310d00466f706e10f1cc4b050260d320d1bda3f8124149a0a96243261a6cf78196aab0f7b08b536c16eb65ae7c7f97ada7c938aedd41a0d52b06224b348f505606e53179a1dd9bc540fecff4032f7d7102ec0fe345bcd7229611654587ed28f09bbb5dc032aa1526347af6eae3d9aeac9835213132ea3e3e71b86100aab66b817c7cf7b921560b354514aab3ebed138d8180fc004557a97be9015ac00ed49c3437f9019daef2e95cdc99133519b845ddaec5a02cc0d6e994ad5b33d50d393f0fe505808140852e8a6af9ebfd1db2d8f074ad29180305f87083f9e3fb77ededa663ae9531ce44074bea3e3bdf23d635a0102a5df6415caf4e1d780e3f2f2c0c1ff3494a1102458aa9c58979b554c3a71471f4d0d80c408468633d02c7f645716aa5d1959f1aaa51fbd377584f0daa34f2c6415bae4c2ed42a748b78f5ebc08a905a8a6ea5d1d7d791f236dd456cf52c8d30bd136435afa94df4689094ba1663622d6f737c1ac5202b315143175a7b26ba1dcdbb276246d10659255848206d10a3d87664fb557821a1515b368efe5b2263500d8895464677d7864e4d51b0d3a26821d214f94fb027aad69b1b35159600de727f7846f7a2ff3993d9386b56fa38a8cda76d909d45686216d8535e544bb8d3e6e9597d12ea42db5481b8b925e322359f1717dd83a2d6cebe47a94ad52e3ad9d322c69487e8b444020eb8f3a96e1daddef17b5ae478711141b3120c994371518ac72f9dd302e1395c5ee75b641eb05ec81b84e74d0829c202616050fe320d6f31d53b0fc9969912cc9c205a17fbb0c502e1245f91bfa91d0b0a1b0b042a2049240c6f0356f88fdf56efd83f4b32155d66943f92a8f5d1383a159f3ee1482232f1da96e83c77deec7e7947fa771f9596029c118dde3f9f76b3fe12ff52c5adca917cd59a9b95577e3e132750644803baa609ed0cc25122a20c14531d5cae26ff8c9c0b2a0772401563d5218725924e8b27a081814dc80bbc88a414d2546cd768fc937e857224e3b7d00fb22d82c788bd8998b8f79877c225ee130bcd6fd231fe95bdb48c883332a4284ae83483b2f83299de1780da1abcf41c8884d21667371b046eb4e98109b23eaf2715fbc2d206ff444fa664609340c28131627d622484d4f2824d9eb0534f63706043f1f03f66969121c524c269ee5c3f1970a86f61cec1e69d561c6add3182a252d333924c06ed692e22988e9adc9253e19f8ccc2ac2416c1f0fc947435083d20bb44f3572388405a1335403245a03249bf73b5419100278f563b92674a81462ee0251e942f60671bcf733733064733606070cd9b69ce6f35b785c480c341841251c33e2379a75c6e4fda1d91964c53fb1a1b68bddccee1f266e2c5394385d9c4baf082a281d304863410c3ef16cdcd45f20a6479df3f1bac6024e42a10316e1e58f14fdf56c274fb99fc20f0c1129d9a8670534cb271cadfc0fef4a7ee4eb1fe46e50ff4730050d689249336a46572156d09922a23b5fc30d2f1469d9dd9d33080455665b330d9a9f71bc7a7eb21bb191aa3f3eefd0537754dcde2d0ec4a33fdc75cbefedac2190d502db251bdcdc9b73dcbf9e76a54fe4cbfb707691c92bb2681fd5ef0c22899335eff3d41b0c0463f25dbd0faadfc31258de222b7d70570bc73bf828aee036cb2ac783ad29076daeabfe694ae8812aaa522db506ce3da6debb5b78ddcf357cb72ac488b47b517edb7acc80f8cce616d4c4c25b125e6a21ee741d2ade25fe7837e475a32669b30400c560c3f0cc412a78052f5f82d1d1eb1bdf5eda234ab55f26c7ec586f93c95154039e4eb248f0d8e3b72e1cf5ba208d01a4305c929d8b6b420a9a64542a39442cbb2990856e9e8b61669bf196eb528f9cb8ceb1a7d17021dd34eb15b9541e290609c44e436b99faa512b6cf606f6225e6ffab94db941818f03106f200d426ded1512a1d6b8d814337e8c636ed341e813e5507de6fb8e7508c9e2f8290de405734b3af784298853d9b07cbf098f32f153c70db2a14464c4854aadc45641c744f322d0a4d02bf608d7469fc88e0585acd1b3691f2887cc02840f8217016e9cc87bc205c113a689cbc641b8ce797c32153de0ec7ad75a0e89f251fbd415fcebf7e88a39166ed834e141c34dc861e06c3447484f8f51fbe67cf46a80a6e73a4301504aa7872ab32139d9536a9c1f8e0c584121b2e200f30ea6739813f008904035976f26bbad8af4e090258cdc7aefdf712b207ad71de30c07d127bdc8dd9cf713ef009834628a1c57d510209122f0e2f41199e01a6543e58d6416e8c49f768ffe8223ec834de81bfab71db97c01922025bcb7e1cd3fea60e752d25b4879cbbbe5a6dfa00e32adbc043da5c62a6c1e810f7072d7b42877a81e354b6b4bf13e65ecc804e507b41eacfce632feab55774216b9bdd40445eaf151c457a924e5449daa02640dcff2699c13bf9a96901c0c84c82e1224976475619999179a253f823a06ca6b8fe11f9445d4c995a7e82bede49db3422cbe2bf9c7e81154f07767b193a41908f079432d409f8ccfc964a49d9fda188de07244aa9627807332a6b99bce9e95f700553fbabb053a09f11ab3c1d073c6ae4f4b2da197e6a1754d0381bd9a5d4d4fe45b3d555d4012089aeb442fcf05b978c65f8aeddc6d82a879cc2cd24a7d5aa45742f635be976874fcfb62aa4ede08b7a27b75a3b1768bca69e8fcb1683fb083e069a48e369840199025b737408d365c45645e40f6bf8a3f39d09c758743ba5dc4916431758d7b468d451686402d9a81a6a602c18911732069ab8b2f22d0988bf0ba9a33f728cf3112abff76aea06e2ec4b744bed12d1e50b099583c89aa5941c38c4203a54ff10903cec4f18aa84884402f82cdb473283f91bc5e60f7bd319db11ffc74c4bd3e31b6149be4d12309420e967814f4fb3f5daab019a58ecf89244bbdc20b131c2468e7dfa2a33f7436eff65a8ca751bd3dad736ff04ad0e9fd8ca6dcce3d46c9ac185d7cf8cfb397c4888f043f2cc48260a190bf1e5a6d75ecf5b3f5a31e5b67a34fde1297a8ea7a3a9644d79b98ba856e2d9a9f3e9f62a1f144b022f995242a243cea676e0c1e20553c7af32bc847d0a4376811d688a8effd19f15deef6473dad972884460fab3a1454f4bc82305e80261fb92752baf4971dd19b49de6082e32602183902ad2975d63885a2a40010114a8c999fcc934724e7ec68117872c18c60e60d9e3a15911537a47b5520ea6a1915d0f2b73583ec805bbff55a38422c85d2c1f70951cb793b76412a8390f1478c98e24ee1861bbe9a48907143972a0a85f48e6ce0bb729f4647429869ff33003f649638e700988613419a95e5f48f4e553e8b811e79001c0bbc73c2d291beef22c48a3ba271c604d55a5874fc1b98ebc42f029c04f3d6b6a7223bd2a50bcbb470c6c7541f50c69de850413ba77a7fcd6cf539371277d38b4ef6374aac44e46658e3aca566394ff7e2733e6b6656cb025b71bd694d236a0516e7b3dc12dcde00696df3eca092fbbe3123b9747c93eb179d925bf536ae37c009f6e021281fb2303a168d2115686ee366004201ed70e2405af6d00ee2b0c1065118684297e24119130ae6df79671a71e5e101264114c407e157f3a7e30bfe2a89b4ae619ca3e10de20c843316111df289d508400d1d550fcaf2501da33bb43adce25c7154c4a843c582c29b0dbb9a863c35a94651acb822edd59b1f8bd09ed5778880374cf56999150e204ad46f7f4ee903d6f548dbe00177afbaacb8255664bfcf4c9d12148bbe14fd65070bfde24b1f1434facc9fae1593faccf316e5d3f75693fe51620a249db1f6e191ef0eef924980e94fecbc6a32f9f66f3cf510d66d8b0e531876799eda5d40eca97e9858e2637d258805de1e43b69f0ada8d46a29eccf0da07e517624705d085cc35da3a92d6d489d04af97e2d748793a19fead36dc40124763b8469520b36e9c6d8fd9186d24e6acc52bb791c25e8be971989ba84408437b0a2f0577fd27ecfdb38af4e67f9144e27e6f9aff7f9e282f0b5188014976934a655a1e2307b2edc3d81e74a8fcfe617279e6bb0a3e8526856270efbf2a781b0dd96e3a1d471bc4519c60109e5d216250dbf9905e7537eaf9acf8f71afa86535e08e511179f3ba0024e2305921d855d9295dd3df55ea384f678a55da3779a00d31d262434f2f4b682a33d0c319472dbf56e9abebfedcffb06233e689f9d0f014f0e885bb767a2b8536fb5d85a92b924e01c2cfca852212bd0bddb1ae3528b3c4175a02c590e484bbc8467e500cf6051a0bcbb315f0902cca3f4942a33e2a026a522234fbe3ca064d539d617da5ff1f1a59f15ea18a386159ea1d4668c93d58f45a4185b57ac191adea4ccd4f4378516276c84ecc59395461e0b740e6fb4325384a04a50f48fcd15dd1f69e0244851dba66db4120e14a3b3089281b13433f57abe60137e0c9fc6c52043f9bd76b90086aeb4dbe7b850b750b6bb53ba8e1e71a052b7f08da5b816b91d9122a9abbf2da561d5f3e6209f26915f60c1190af367478b2fe33735775bace397878f302c3742ace2dcee927901cb09d1123cb7372f419ba7970809ea25c6979607d37d402e64485843c47cbf5c952185f33be943d0701a9d7a0a1b3a665bf9570d3d4c7d372f5bc9443714dec33ecc847a1da94dd54bfb9ca4be2c1f195122d0cc042de68739e9fcfe2d045770800829006da2e1041b09f8fb11f577f10297a6bf55584f48dc777ce34a415d1d01f0e264499a0880634f5b1aee639208017038c4765b6a0da523c217d4a6b3bc59fb99818dc5d73f576583fe6fd6300af76371115c07fe007808732198eab5e66190cfcebb22340e64d1bcb555bb2ad0305a2e6f97535d1b7bc2149895044d2332714a09289a698849876ef651b181bd3a42b7b20c7a4c72fb65a8b933458822c85b141234d1f591489a4a7ab9e3c0b4312257ac29eec498ced58894ed8b4e11fabfd5339dfd9e952de5e7de42234654438f3a2830ae8a3029db266f50420b654a228e1c4bbf38fe863bc6d98402760eda3f949441387086947b15b05aecd7a24c33bf2d24e57e5db58bf89c1820ba54cd73919c28fa28246ce977178f818903f31c585e8490f09c483fcb962dd9c4c7203472f7664b1e6892e3b12b7ee06cc9bb18e0b53ad2ece96bbb9eab356cb3dfcf8ac35aed3b0dad5ade14a34c80c53218ccffb769bae236dd8d1199c7ec7511a493615f3e6b4d68f9aea112b5f97e437714dc555f03b084070c744a9b8316e3f2c1d5615de61a7f219559cc7fe17d58206ed80ea9b57340be35960c2c50b25d6ac1c35f579fb2b506c91a8acf130b7db23626b4c2a3ef959e7d691d5c9eba7de5ba65bf4f0cac19165365ea57e921e6ac30fb1fa381667f2d526a38667cdc94155b1f994fdc6662b2a4f3e11b1a4a4e256e819e3afa069824df70a003e161ea426e868b7f49fff155ef02531b13d39907bc000dbc72806374a8ee4e6838baeb39f4aeab34645e838cbef0189c5f97c6f68305b59f2dc7f4e126141ee36b9eee6da581df7a7f840b92b9c171c6e11747f8f2ec31799debd884c75856865c5ef7f75cfd9eb31ec7af0df66a1857eaea47f80973c025c4e456a0845bb004e87370bd76dc530b8de7dd85c862bccd7e730aa94ee87f5c825e93faf2b1d9a4e1dda35079a6b6a763748735cfa19057d94b664c46438d3c5899520d9c6d3e17734840724376ec7809e4270fe686803ea53b143789f9f21fe6aada26aaa3e136c23167a7027a04a9daafc6e7b62a1b07d2bc08d12781e5f83f6cda491f5aa31c23dba13f6273a97b6f780955dcc43d8dc6d23279c337d3562bd250d61b168969b1ecd1f2f90add949a96345c541bcdf1ed967a3b381dcbdfbc336d8dd35f14aed206300f5eb50e65be2a77c1c38b791e6e0839452ba0805231b5df19166df0b99e2e5f8ed5857429e433864ddb549f1725e9110ab1e0737a62ab476d153bf3174d3d2505f1567d2b5b0ea20dfa332893dec5881655ebe236b199de5ab4f412f92d192b0e352a4c211b0f23c2a17a8300c947fb2404fd65db5fd68173b43c99077bfe45bed7c7736b3943017a314369fe560322ec9210c1ee710fb3f8bc597b6819a64858b49f503174eb37065ee96a684ab58dcb7ce4562c80d2c20c78ec256431fa245c30981911ffeb78d659312172e73d17f4397c0a83167f1ee526cd5d12d2d48ffde9d94803956597a386eac8d66c4389797f7f93c53f5d9f4c7c71dc2693aba15d5b95d0ad4816e4304f1c6439b59cadca720fa365ca897bd73a4e133653d93332a110ca1d68b58dc98d26561b362c11dbcec605de270e6889f95b4efa159b8b1edf8982c2661eb11c77a2d1a0380a85fe761d053c8a894028374fe60094cd18a869d87f9903b81824596ef0f920871bb9e1a60c7ee860beb741a1d36b189dadc644bd0a0f95cde7c45e51023580f81e4245cbfc28b23968da0937696ba007e8cf7e404173e6882d027af461ce72092301dc90e71123dc5c70d24299de0cf7b26d3b38a36c6adf0a165f0653bea90f8ceb27e02a43cd541ca43371cf6308bc1b9d8bcc0b2f7889aabeaf138a610a6833a8245fdffdd656418d0327f5c322a3425b6a5c2e1fe1a8189e90e3f02ebf9a77f348b03a6235eef5443e8942df8d3f63ea33afc37d2784167613abea4fb1465e8aaab51f71c51d35cd726a5d5117784c314695aebccf967bd185747154ce9557b457969f490deca1a805117365c47f75a3e4cab87bd1b00064ce7c2d975f9768a545bf77691152c4aa4bb6bfee0eddd98e790d7ba913738e99726cf2f09b8495e3531cded6d96d5119f5da971579f4dda5102c27e563d174d0f12a098139f7c469a135f4b790f350c4036fb67e6224cdcccfb2e9ff1a41f939cc68dc4c0b24070c92a7b8c42a2ab216107c94e621a2c74d3059e3409403ca2c737acbb0d29fefc69c9ff99e06a12ad9888928e4a2a353855d4e171f0b3816973812edbc6f7cceb6ec2a63628dcb2409d5fc385c40e9e01dd1ba6b10935222e416665957baa754260ded401f2c0e11b69cd174799a2c5090358413fdc14ddc0bac60219001fa97322f5b1bbad5a7e1caa8220fec2b222bcf1aefd143e52ed8b353152322891fd153c8d8b6bc8d7e45e49dc89f0df7de23110fcd8b99b28f4edd1497b5d679c769f0fa8bd3b8cb92a035d6788d1dc8ff7f72454812fa6219192a4818b7420e2361e0caf4493aa2bc1b751847d818a08e3f59b29940cbba99a4e6fce60252c70bed88724dee30da6ebd9c4795076d17fc94e8f61a62d300867ef4a76cfe433c1087e66a4a1bbb81d1ad209b579c442db285c11a1e89c66604012f0f6e7e142b5942a5b40909b38805d1b3c762327012a89444cfd78000201753625c86f108f5d180218827ccc1e6bdd402fa96e80a32bfb2b720b85ddb168384d38b36bdb31044fc9119dda0482bf2a575878d8989e3f4a1dc93b693c754cc5b27e065bdca5deb7c254f83b21fcbe680f0e817bd9a800c7c150603f59acbf7aa5c76a3c7f0053cc7889868dc4aa5d54fa216067671fefc11fa04511ab513d65f2db00605248b974250806022f163d1e3bf385a80ed5d2c60a8f2814662a1300adf85ca39dfbba1771ab2b3e534fee7e51129b90cf92fce697219cb7e20a06371a09d1f64e21f54c2537a3c5a0f3ed5398429db3d1591f8c7a66a4a2502bf4d0702a24db41e9df2deeda38fc0101d5eb3654f75c474afab54e8296b60087598967c1bb8a0db4056163b4403ce6dfb68e721f6db933e606d9effba251e7d056513117031906aa79307857c029ccd87c719b32a80afe7c8e2ecf5e2f60a729f28e230cd75a377a641bacbc2221f397c9fb62b9632806e7e33db7a5e5e3ef7d96ba2b3980dca335ff4a50cf36b51c8c90ab8208ae8359d18a71e7bb190ea7fb40594252ffc1c2b1d6b86840725eeaef5da16ec0cc337888ec628e9bf8f1f5c5b381bf2a267c11bf6c634f1be8115b8d6be52ca1618e5906942fbb6a682e4171d849af3c973d414a26ea3d87c23999bb80619bed6c4d5a2906b80fbe8a41af1127b2034ca403e109b7453c73ab467fc7e472f2f6aed293c96f3f1b32fd713b298e060781d8b564541be9c5c79d1475835006174b448490bd65fd0fb1ae60d0ae4633d5cd17a4d0d44d5492394478b9bc9c9650cf4698cf795f64fd40f035d1b72d30762f245c1c8be85f76981758001a1b160a56a812d5df257a297b7e12d084440a8f111bdfbd081a1e320a2e3f0a9f099d032304f77f58d13991b8c6b8ff822977b20c66910606412b7c5d5e8978b2d5b75f7295524db7679b4331c5827f0f81692cb710e20c7f3e20e754b34118defe53fbea9c3a0a65134387bf3cc9810a6ac7b3b3b15f1c8e88e1d068cbcba076324d732c9f6ecfdedf19214f41f85a78039f63da2447e1a9e35a0d8cbd2819cb6ac6b46ad11fe87aeb2641e9814386dde74ee5667180c26371843b569c1103c13b119397a269318f1502761a5ae0c3fc89299a914d3faae9fc95c38e1a5b7ce9083a4fa5bffb963b2770b900e90d587096a9d33981b179291c9af15036b1f9d385c6c85980cf9580c7b898534d88c16ecf5ea245ca0587959c2fc791376a38abb575a8b9b05cc155ce4572d474d35e323942ed289403210f45396309878a57b54065877f3aaa59758924373b47ee929b0830ab75c6fd1fd855e3a4321afbce4b84d15e2f71f01ec0c1610b53b7b3460988a6d9534da8c385188d57c88973db4d5eb8e2007b00f53ca35ecf0ad19cf7bdad3b2aa68cd03fe58737a6cfd2c8b370a70e9d4b433953ad1040def61a9caf1367f02698c694b840df28e078a0a4f581886121f860fbef28995036edc9e4b9cb8ea3f83811f9c8ec5a28252cb80d7e3cced9c82389e410c36002ecf25842cc523a58f7d046c2092c63006d8205537e3aeda22ecf2f4c479226cadd47aadd4985ebcc11e1129845d53fa3023800b358ce38257275ab3c8095ad61f6aefd20ce441560804247c8a7d73591cb1412b22952a29bd18485a0058b9742f6feccfb86de074f1d19f498a16b65ed6cba8210787ca0b3a9201cf8de5541bd599a62d519a33c32c0f7fbf329578322c6ecbde3b92d1be8241e2146d691d29e970832189746f968ac3bd79ce43a5c5558b3a42a743f3127a329c90389fcfc22d60777210f3ab6be73ef8cdfc5ee4b8a90f026b4d2b462446ab5dcf9aa05e37f1e24b47aecb77b63541228f78115283a615d40c8ac4a9d51dcfb567945cb12bd9ce47d1a0e05781d9d351ce863065d12f7096ba11d5133a0f119f930d5f15ed14fbc152a24f5e9b10ec10a1165e3b747c7ff5de5c21603d24676c3f9ffed4048ebd002e5f5427f5e40912f5799587348ca28512a2e4ab012dc66d7ab9221a55a8aa12c977ede77e96f7c0a01aa21c0c9c937e13d8e4466adccc35b4525ff01ac17669a98e31cecf9df6bc825b68b86a27c922104f28c0cbf1a6fd312577ba7f27aa1e1ed80621cfae46ec385706a295bde402cdcb8c0c6247b6f81b1d4cf222908218cf8fa275b4ce29dce03e956052bd1b3a4ae1061095015079d2fbb5c8cbc3be96041e4b60a5a019c8d71a2402301e86d0f5c67190bbd013ea90d94dc4e297211be06015864ba75001be8cfbe78bdc2f73a12279798fd56f9ee918e2f3e65b8cf84208160cd3c113446f2a6894c5136dc033353e9a87b38fd160392cf724a5b591ce8818824415d6bc50b40643d2f61541ecdff9e62e46085683614d9849c3aa6b02ead05f8b7c001b409d1da3ca7954f1acb8281bf3f53f62e7e2ea324247941a97063a2588578ede81f46ce3f59e7e44ce42ee6dad846c8216d000ba5c0a711828bad9fd60ed399fd703c8e9f2f1cf7380807fa462187080b71658cd17c653b152c22cc1bda870e98d68996194894d0867fc7f57a9abcd496cd36b19fe0dd1c54300a55d5451d5b67ea969735de4fd6fc3f06ad07a307ece0074b0d80bcbfafb30a8656a9965ab4c2d8f911430b11472ee5ef1b3a2c053e48db3e82f1d4139aff10d56d5a8d46de6f3e960a806d7202375a272ee99114935c15bbf286a875a28eff62b3f3bec83b765d03e5c8d01eaf1853a380226b842dd18c453ee0720c9a70b1b53a681200b232bea0d2d4db5a1ffe3c65ab15200c8d6bd963422bca72f4541397575b8d61c291b2e4ea95c3784e75c663ecbc073720efd1e8556112c3a9ab2871c408d1df32ed653cd58411034800b897666ab7dc16846defaa9026eae1811785025a33f9d3b33b6b0181335194a82b99a6bc4dfbe0fa3bc6788df9cf8e9c7d1f8098d14c5f365ce355561bb4e0820aa3cd2d786cc44688d226a715456ddbf4e483a555b1683af231b32662d7a1061ad6ef1bd4a8e9024522d9534eb88ae5863afc33461eb6f0696a74762e4b8fb742536ea0a1e01d1ebdf94165c2547d8342d67bd8ca87191f7f071ea412153557fbf0580a723b420121e101b24cedfe97b6d897b9be8de8779c482538a8f4cb047b5fcca13aff715880e1211e7119d2457d3484440eaf0830b6f845363f5e63fe4abd50b301bd0bf746eec90ea03a36ae540b6cc5c125732c9b7daa7878a72ffb84113aa157cae408c9e9928600db33d91108e465e146488c95636715048b86a9155a5dc4328a990c6cadd4d01bd30427de3b4deaf3ae5f659689b5240209b61523210d0576b4362804a554ba98c9f31b39bdc19e56e8cf1611006df99f7edc9ca8e94c54c40e235710a42bc200249d377c3b183f596c60fc03ddbdae6bb4af1480eae36c59c0d11b3d6e5a8e5ae7506ccbb7f884eb6b1e72d84f9695e96d5b481a5d9a4991ea5976db9a926527121e96ad9afd23210350286a5b32cecb68870ff1b9d56a2acf6d8a71c610234ffae297d84125b182a4809f7c6ca9b00821d46ec0a2d6a59a1a805b9aaf57b72544f40d54cfaf36ab7d55d295bef11044015d675abcb831a4046d00d29e109405c01527528f0f5bc821b0722875e8664976d7deaee2ba2b58d6b70f8320895593a5673944e3fbd7e4040b5811df184e069928ff23e309d149381e5b25440902429a8ce5ea394eec643549fc0512a8477ba48df6d564af9ada1fea8155507ce1e45d30be966853cc2b0806c5a76ec92d7e710ba1c0b9f855a59cc094699788e2a747a0971d2ed9a321fc63a22ff5dd6c29bbe21901344665628c55001f998806dab99d9efa692bdd340de9e19c814f4577ae7745bf441344d92320a4dcf8494061470d25e1e26612336677018e4e439c870022214ab11d9e71a9b1491ac1d0b376f0e30218c7e48d497af6c62cf4e7722e56030c11d26b1ca51434e4fe8003cba95407ce1c6438a6b2c99cd33bfde34b2682b3d4c2f8e60dd11e6881b8414182ec2bc9bbd06e579158f0ffcafef3610ecf0d59d4ada78161b2d5fc99548d3a236436b2e38d47f21fe34dd9d240ac85a9ec54d1c4890ced77124d27ae3555cf47cd5dfb1f0a4b83984cfe51d53a56f2a6210620ac6f9c9923950f70bc4c5738d2e4e9767a37a91714d969ad66b7bcf52c9012f15dc602ca95798c59159cba69cd29b6a4232cabeafc8c87be24dd1f02842204cc1332b8afa3f9e09e6cd160704e1ca7d1332b890a2151a94387b675564c52bf662bd6119952be8150c8c4a245ccb22e6db18a05fa121b2b577a94c71b08d7d021a5e64d54438cefc5d2ac4867c10868d68d6d0a4cb48a43084a06f19fdcde7472a84d23abe8bcdd37980eeba6b9ec6a356949159187d4b2b9d8a874312b3b9983514787b102add531e6a640e8b8fd240484b80f3d1ccc95b649b6c2750f4a6397d33c59367685e5cd2530310aa9bbec18ecab748f7545c5ed8c746b34b0102780954347e5650c6c1bfc23e66d7d9f438c32d4903e8326b927d26088b1317c2e3c1aa6db9f1673bab5fd7fbc9d45425b9e413f24b886105edbd1d23ae511b45384614a5ee7710e7b784205456d861425a6382390a0f73fe10770c4f3cf23f8206b410e9639784e632d79fe439db98e46193784ff8ba338d434ced3382bbd790a85ea122dbb1dfc9d474a566b455f2be3fd6d2f9bc6d3d6432eb437accbb4db6dd83271f07828e0857b9fdc64eb88cdf2faccb7dc00be0a17353eb1b0bb6c0d7695e6037abdfca41b88004fd3ad06f7f07ee9c8a1b602c7d306d4e9cb8f75fbbc24c9da340f406ef9b1647524a88c356a14e78072535bcabca2006086b852c903032639bc6a285982625f0b36d3440798101c8d4c531be35db8e9311c43509c378999a52c6415d666c69ca58f6ad912ccf5d91a80a9dbd3c92988222965ae047f8982a412fa9f5aab07407419dd5d0d4fe961da0b013b9fd9f08eea991e4fca2edcca85d4f43bbdf6c3fdbd478cdc2e7f9f9f177569d536f94b7f36dcae2b9a7e59164259bb533f59cc22f97604e83cc41da5c4ec16e15cc6940177352f08fb5345e4da6ac2e2f695bbe694587aed814e117d3737154e07f59bbae3080afc2ff6beac6988862d3700b57b5bce295d8d46816cda8808c4855c1da25c99a205d0a9cc344b69165b1fa50c2d32803cffb399f0b0e94806dedc32864bf1b10178c014815f553a6be84a2953e57de36fbdd9989fec704ec9c0895c4472f90b501d35e95092af866019481205e60e4cddfb82880e5448b9afe51e9d2dd4099be398fb7fda61ddae8610542c683fb0d3750e97e37c055e629b47e4d4e1f268a5700f133d81aec8acd014280bada48a6371d198b3a5920a1b5b2b88ebdb9d4e0f1b972045c21d34e1b542a1923c0a2102d3321ecafee4494e0960c4d798602ae3af5ed1c681fb8e85d4a26fb8119acf774214e61517eabc537fbec3ddf5613264e5ea3f8413ba8d24f8482b94c0109c662e50f42da86da41a2ac34e38bb7991bd6b0038180802e4f9b709feab5b9c444d09473d458e5ac30a6f9bb0cc4b00c24d32f79910ab8e9b36826b73102aa83a331f7d1c0ca8433602ebf935c217dfd29aca8f8e9ef1e3e9f5acf9bc7fa003cb1fe4548b54ace0a4b619b2c3f207c7428ab1803ca56613098d859c885a93dbb01d7a222607e27e71a15a34a7d1d1efe0655b818f044ddd4ec4c91ab49ab18d58a7970021f0c92e99c3a95705a797a99a8b536baa6f6d31d2a74c4c3db08217a00bf8026555a6f869285d79d8a7f8db9a67db608dc994a3d21e07bb7b333c2f423a5b31471a39506bc823b6310441d9f6448c9c7c8aae1bc391fff94419da3e5a6b4c5edd87df9c3fa25b8d4a7da86c44c2406099bbe6e80789c942450d325a5c147f1961ca74b7e688be7ff322616cf4706a9149c9fa114dc80d704df620c8a9b4ce483085fdf63d3154c533b645729c3783c6f4a95537a230c492068e61091298b3cc70e90431e26b516ac8ae634910e1c17f9ee734165f88f366a0552890a246f7abefbbe6438d93b8896be71db18e964ae0f49e9adfbc1e1b987f212148ee5e2ea746909d8f857a17580237b41957ddb2c0036b0db4741cebe30e42b5983f4ffb452ffa9797b00d55fd33f52b3100a274c909c30c60c0907a2ae864b145ca41031eed91fe779141baf7d871b6bf351d82c415f00c7c848f937b98eef20768782670cc962bc4f51bea2c23e3539c6377464acd12eeb6ebbc623713a3b4a22cdaf4c312dc9b3c90d4c1603d54f738e6f889b445d8001709ab1db64c24f057a4350e9de7772d559638ce3456a3852f8bd5f94d6cef422c1f2c40c6892cddff12edbd983d4475fa59bae4dc9b9b1ab9d6b6c3fed4046559700c194b81bd7ddfbb16e7ca2b620a2541c1a7e946a6b6c86036862804e4596db4e4fa6619def02764163512278680fbf40bd6f44485f771cac100d379773d58d2a4d2f21f2eafbd306f26ad52ef2a8abc0ba39fbb9b985f4d9415ce937181466a0a24070956e562bb9d00549fff52aeaa4f4c6cab434d017588775cd0737b551d7b7979ee32c12361a7512a60083907a5e6b97f061a2920a210a89b35750fcbe355fd58d7211e986633101849b9ebf53912cab3d50a61069fd3f58aa08d9f9c5ed69e3ba31563316d25d7ee6c62b7b629cd0fcac976e9bb0dcef4f69eda5992f81ecc1d90af0b5189bb99072377c2239560b8a9df3ac9f9b36f86fd2dc9e7c7b2f2ba82b671d29f11f15777b30544fadb41b1620f2787975a02f0e64d208d2bd3f9e54a00dcff19ba6fd77963227c654c7e358042a7ab72c24c1db6842ab300091f3cd7b37611b94d732b2bca6703fa096d23540b5b1a80f80cf3a2af3b8530e85e388093464a98c3e30fccd48a5fec468d683dea74504f645446f0ce029575e879d855acc68cba782af074fd4ea41169c71cc488d861d31ff20918374900346222eb083da3251ba1ae7cac6d26ca1fdb3d0ad3595f4acb0679855f6535f8d0d954e898eaed73f65b62367857daa729c5d315f223cc7502bc90fd1ef70f73df55cdc04875b5f8e387f2c1efa73f090e4320f31d8fb62833c5a052b2405ae5886314b98d6330309c621862634a6879816a73d67302b6a1a57f10c2f3e627b5f1790dc3f8b9f4025aeb03fdc30744dfdc8c979985b3a6096abd32cde191087125e03b9878f431e0be8e353a1804bbeebaa6723c9832fd53ae5ae10cecf0356a1c8d0ab70e720a280e69bbdfae01b3c7cea347ae04cafbac9e9b3cd707c8c099db3526c06803063b427eb46e4116df13d33c5e5b7df524dd1cc5bba20036f02bae5968a1d00223444030a4c06795c308c4a4e07ebde33e77b6cb6e745b30d681b967e9329debee0e531201ed67bba102646712e7e606ab87e4441a758046cea5487196e5741601cfe3abe995ee48e7f0323a400aabed1d0da27f5fa5fe013c84b437bef2de5de524a99648107790758079c16a78b2a85b0f3c8e5cf38ca2f7f06da5b083b77dbb87da305b88dcbc8a8ac0a5917f7e5df3e201db41657dbebffbe22a128d15db509dd44af71dfe2083df76a5b6de30664e7f56bedb5cd3dfe4dd4e2aa4389a8d7dfbdd6bf3312d14f645656b9fdff1117f7ab1cb0a4b24e54517295d359c1c5aa11f7b54bfdc27dad155685dcc57d0ed8c9ca70dffda4a437e6ef5125395ddf5330ff97c7215403221ceead444037f7f641b8f17762a9f735da9bce155815a2cfbd0c532028a80c51c392132f5078808b65220e15158cac0eb54aeb1b870c93710a0e51462459776b3dccca00be6bb50fde71eeaa268a289568ca2043e9044c7630aa72c3ab7a400a6a0b0bb3872311966a40410a3166ec127084c8709ac4c0e9616b72ffde264b708a6e1370cf599631683a24db15b20fa626094e042e2099b1d6daad5a6badb5731583765d7d5a6badfbd0a696035f83b6f427cec974f1fbad368b4319e816ad2fede27764cb0798810e43ab83e723c4ee2da7d4bed16f92506a69a594d25aeba6744b736da873debb8f6cee63d4086142d209a6086302154bdb96ac80e3c25695d4eee33d6729a4a0046f06b950a7547fc8fa4cb3b9c0dee5d68bb6e2eec6070469427f34a7fc6e361d757e90afd91e382be34a0a5338144ba8084848418924610ab25d6a319ef7939294d2df58bb8f783badd537a5d4d234de0c0ab95615f10cb5487e145a47657150b9f8a8ac2898fc69d3a10a0524f404184e609182e504242758151698105984329e0866a0c061db4184378345c90e3fc867cfd995dad67bceaed076f9addbe4cd2077771daba78d49c5785f46e57cb9daa8ffc97914ea518f42bda638b647a13e9c17d8478d2c3aa0bbf2c7fb9cf113cb0af6b8f77239f851a811bf833be8bfcec9f970dac8c911eb8362f7d543594a5a1232c7b536c77dfe8d02ad9cb9f74422211cc0078feda3e7a1bca172d2bcd74151169871460fbcf780fbcd1bcb567671bb973a3cfe39cf23aa763e14e83df765ab14c29c88f23c07776c5fb6bccf62d9da1985aae1799e8ee7fdac42a9f7428a73c0ef537af4b1cccfe5926c096d9abbce3fbbe7ef44cf9efd3f686d6fed2ebb73b873efbed6aecb9ca69e145df7572cf5f61e7ff77df5ca233beba0ddb443ecee9ede21768805a1fe160b945f1ed9de885fecaed3e9c65201a58f7d7f47cc93e59f12bd8ca78dd6f6cfdb7bd4c7b2b5bbf7442c96be87f6edf0f85d52dcf7eaec1bdebf5fe78bed63a980dd3d1ecb17bb0c3bdf41b737e269c3bfe5e9d04a74d93a7bceb064b1f9f1db2be2cf6287e9d74477ffc1d0eebec478d30f81288e0f8676ee511ed95dfe8ea81b4bdf2b6f042102dc1fd9734f8ae37ee7fd043b2f7fedf2571022b0d2c037f7aded818fdfc6dbe650acc59b41d7ea11989a78282929d19413b4ee50a95695c4de9d1aee96f2b2713470b809e71d1472973aa1f376da789f0a4a8de15ad55dea91ecd35a6bad8558d1a5cea1b5d69a47cd51a291e8d1aed11aad512154d786425dd34f18292d618b794c25944e3da1b98d1b794c24362567d2e1b123839214159817dcafafb5d6dcf64c2237ea0a88d8b4b6422f09b1bf94a209d5e05dc0021e67d3771e4f56a1eef36fba89498f2e94e8efc1e71e7feec6559d5f470ede1357f37356b31b577304d2fd4804cc1c8178d77bfcf9c16f9c235e79231ea70e71c5adca32a2f46ef14e4812d3d528436b0d7df1f07ff93fd1878e203527a774070999a6e1121065f99626d910c9a3d5c444294d559175a94ee5a42555268fd28406793c86643e688e011d7f4c0bea73a38e334c581d4ce872d264474d827ca1d168349a75ef8046a3d160f8da2062f48deb94db6a8e011d7fbc0478db797344db82bfd0b8cd5ad5082dcaf2a12f5e130a4aa275afa3e6ea893e74705059ee44e3acdd39a20c1502a29ee45bba7c411ae2369ad39ce65b10ee3dc80c528530a69c2b15d3e5c442640d12547a6cca7854a19a0f893e5e95cea882873a1fb0d3940db496506daacb4d95b99baacac6a9d1e5d090fa55001d0cd19a001c03cbd1cd2fde412229cbbfa85abaf9cd2938960e0d6dcc6d37af5cf0b056edd85a6bb5b539ebcf596da57a1363551fa86a65532b1b7777fbc6f278a0aa950ab55aadbb6d7776956c7a7baaa93d675962b14dc986440f499cc95e9c6c4f9b52957befbd4b565051955c41432ec10d97b3e72ccb122c66294e94ba74c512ccea50abb4ae5966594698a5304496a5166dbd211363062b495274dd73666506194cac1c69d174cf99953056b47c332b55665644d8df9eb32a5434616940228592cb4847820c461449153ac45c46e69922a6334ad018d1118aaa78b9280e3218417379db5745fa324a62e48a4384acc8e5e1af8a5e53f2a6e69a66a0bc70e49aa1612a7279de57c5a12e5d602eeffb2a1123b336c399970bcc2a83fbfef78f6c22463e22a4bc5c608c7c415489b9c05491087d45b1c2e4226284f322cb4d60ae490990f355b1e55290400a25971114938ba404407d55f41f8c2863e49a52e0f08393cbd349d1265f684c2e3752692da2d29f9545691554da747111f13756b481e332429b2081ebfaa82ce79264cb152e231e1043dbd048e4c2c0260531b88818018f704d4a00235b9926ae23faf572557105a4522a684f4cae3af2c0d92908168ea8d72cad793ffc0961387fb8ede2546a47ab5cf5f1f418d90955433c61cf900ff5f1ab52a97878787a7a7a7c7cc8300c7f7e7e58b0f0e6d461dcddddbda7c72728fcf9f1f171afeed5abbb7f2b5fcdfaf3e3eeeed5ddddab7b75afeeeeeeeeee5eddbdba7b75777777afeed5bdba577777afeed5bdba57f7ea5edd3f16fad361dc5bad5640aa162e5c00bd78f17a6f8aa238864463059ffbd00d5518a4220ad23060c4881123045285402a55a8dd9d060d1a34aead9486cf1f202090a885aa45a802c2d385d3161f510b17ef22c8d2f0c58b202272877beeeeeea5915dc7175f75afeed5ff5f14c571ac00dce1a001e80763c6a0313cc6ada0023003bb71108c1ad467850123460c13741d65c890419224eb63b166cca001127d90bf0d90c823d23a8cc78821838824594d44feac1b435313a9767cee2e43860c70877b24a88106f60542f0489205ee98f139050dc07aaf5e4a396c67ad7a33543342151d67801af82e8f78343e1d46d7a861e3d361b407000058f059e0b5565b1ed1416b7c356cd898b5d6fadd7bef68810ab444d7b1fc2cb0a0d50a400004f0b93b6500166f660e7fafa7503771b890677f8229b29543984f341fde5459f47ba2f0d74b6cc0e7a0748608c1ba298bfe8092f43bf0b40e4a27b5a3fa3014150f17180c96278b3a9329e44dee4293afd0e42838acc98f9a7a1e1084e6c3000e9aa8177ede6b308339f3260e03e283a99bacdc8180f766479f362e3562594c1e3e9f0f48843f04ba3836ae088802a9a031ac49112c870687866cfe0949f029d96afd7879ebfce416b4d431720702721890e7e2d36174e9b04d5f7c1fc5e10e03896aeac54e8a851a6db1c65edb1eea032a17a4d8710383f765091e304ca0dc69fbb73d136cefadf758079dc55830fbc2011833177c8863a685daaeb5ed3997466b6f81661b102bc10f3398d1326506850d67542d8011807d6dad6a5b6badfdbcf4b0b7b75b0b5af63685fdba98f12245972b33b60d2a427f1097372c8badb5d642e00a9b1fc8a0a04413319765b32d0d4cb605b7b5d65a1e2ab6657078c2d21d2c1026b3ade58264db143e0a13ac5b4f5b1888bb1ca7a5cc931f2d4c2b2d67aa84492cb4b489f93cfdac80e449cf9657b80294253c1a2835f9ac80258a2a4bad6705189cec24f18c8192d2f2a4224267470450b42a5401565973b6c2d3955814a31e578e909e662724151183cdb68c800ac7719c15d9374978928d9a8c683a0a941e28247179d223cbd1079cf4b0525466498f2ab42094f44821294d093db6182561420f2cb4190b5158e9b102d3cc032454e9a1a5287c41abc228ed527f8c1b748bb6ae93302a5d62c0f4cf9e332a60643ce99e3d6754ccb06054d4e87f2209a8c3708a538f2935fbafa9b899f23df941a414a51e5298286b4609fa1b018906f79cc59eecf2ff63574cc83ad4aab047512c07bde365c60934169344e33232aed0dc120d11f477264691d64acc2881b652b068d076cf59ec0d0d2ab11390446164333054d4b0e2031812642976d0b7948ef5edb5e38a7a083cfa57a4b4eead1ce9d7bab2550b92962d974b7ed15a90c8bea5f6eface307dfdefeaa7c071bceecb2b563fe1ded9ca356fd4e52b2cb9615cb1a45cf2abc2c6b183da8bf71c97fb9e0af2217d8afa8fbaa66289a7ab922dbbe46cbf1533ceaa840dba8af0dda69fb07e5f83a6b503a526ecf1e7c28da75fc1e47d125de5bbc567d774cb75b3b3e25e78341344d8ee20ff4898b34321ab52719ada98b15b4bb01dccb297b3bd60d1b59a35aa62a55d9b441ff0160740794a4a2149a8cae5f6270532af49e6002aecb7e1551dbc50aba3ed2a6235cd7e746d7b1ac62362d67354252b3983a2255a34b0be26a9a5fface1914a4edbbd47b622d82d1f769e6fe0eb97b08e5fe7e1db978701f73f17e13571b16b9747fc5cde954d11757746cdda0acfa198abea3f883067b80e01e95687f6a995021216be7914db4da7e22816117fefc9c08c32e0e7f16930cb9b8bbda461fdccf0d44bb70b9fd2a836e6fa305501b8b738a46b4bf6f969d214b0b33a8acb2cd3daed3fb184a5c6d5a4727f53535a6bec8ac20155d525965794f521cde7bdeac792c3fe86077bf4d173dd8368f8ea56ff781c49f925ee78d1d45a1b4fe3a16015344f54f3425b7c75f71fd0d2afd6a77c6983e904a47226030ad78abb58ea8ed270af5aa71857fe7552b20faf10864e755757b4fa5bf52fa459efa984c4ab45e478b7efd22740c2264d2666d7374e5fddc65def26800bab3d7a2c4609bfbec5b6d0aea8d1ae0711b29121f4850afc1efc421746fafc5557efdd3851257795cfda88ffa0df4d3478df48ba86812c87d8e48912829345ddcf66da8df60e77f679c95f58d1ba08ae8df54e26ad33c4f44f528a1e9caafebeac77dd42fc03ecfeba0358f3c63113dae7ed457fd4a35aeb6a7f4f5d3d7aa5f6d23eaf5afc447893b44c00051bd1e8910019380e94252040c109ed73e5f84877bfde1ab463faa2cee9fe7555ffa9112e1e845af373ea31efd6f8c3b5f448f2efbd28d94e019551c87f281a24828c93d4adc3eab258a46a2421388990a6b36f7f3a94c61082ecf8ccaecb27ed9dc6fff65af29c198ae3421e18c36f72ba751927beead134fec922e39b14b8a4604b235bba46b36d7c42e699bcd716d7e3a5192e3e85115a2322aa301ac292481356ad4a041b3b4942307498ae2cf8f8e4ecebb3651b2be57b10826b7a8b188116ac4930bcc7d9e31b210f5f55fe7f5a833fea847b2b2eadb2094bcf2c54214bc2ad3022bd22b94b4f6099daf104ab5a314d54a631b17c58a2e6952514c6f493c288eaac63e07f382fcf6194071786f3ffb0a98187c6fed27828077fe3eca1431d91215eab9625fe9cbb67eb4ad97d93607fc666390b66d6d4b8bbc310a25adee9cf407d3c6486563280ed302fb58a462a60dfb3489929ead688f8d3ea6d1db584e37442440c59a2362bf0405169c5ca8c713d079fb7a5ec0bd5d0179d44864b5414fd24b8f458cd027925ea8df19573bf8f572a5c6490980f5d7d4eb4f8d9f7ad4a7c62761db79c5b6938a6d8b448f4d1bd60a5dd2188d6d238dd531a6472221bc5e2e8d1a894840e9ebebe5d2634fd2ebf5727914ec803783c017a818b9ea1f71856f74fd20eb33463086852663a490418c511263f6449ec9e10972891c9a72600aeb9a5aaf67d4bd2426c98e729abb2426c9ae6e1a773767eec720d22fbe24263bbf2426c9eeba777edd2f8949aeb31f750ca85be0bac6fdf246d0b3f6c67dfe1af7c6102b070d1af7ea7b6fd4a8d1ba31c4dafe9406ae5af537b4bb6edbb75ddfdbdc1bf723508c8d7b8da449920b8a111463cb01b7a018338ef03c4a291d29c514079d94529fb5d66ae9acb5d64a29a553b4d6d25a6bad94529aad78d85acedacd5a6b6dadd6564c71688a81b51587b556bfd65a7b496b2dbec1db2e75ce568a73cf58abae14d56590a8d2c7b456d4fb9cf306aa06fd26a5d6526b29ad1445524aa9a6d4da9be7eb18dce1b8c3718763afbd1b9c6b3738ee70e824b79fdbfb06b67de72ab7d56b647b1b277beddde05cbbc1b1d7ed7577270be78b3783ca5a349d668a2f77b76ddb360e5f8e26f99005ee84dcc959f4e79eef50388efa255d12b2d0a95dbad3c59f76af7b3e750caee52cb89f1d03ee7a1b77fa28491fc438dbcf74733a42a716751aa24eee64371da12c74faeea7efbb7dcffc29fbbee918383939dd29a84a958b76d80ce99625f56b991b1a74dcd480044de726072414103ee47ce3061876fe9c6f78da59cc6c671c9eec8c030876fe560e5e86f4dc81e382366739ae862336670396cdd9a0c4e666376ca9810a8ebc51f30cf7c3be4928ec7b3f6c61df59d2153430792fc4c84c21d3b219b57e20cdae3448997de0f5b29d3d53c133d46070c0aa66681234c311a784b540481c60010509e90ad212dbb2b132c8b6d5019b5073c0a187292430d44ba749939cbfaf634cc0129a5031acf03169ca89e18b134f098cc106269d2a6452f4cd4ac830981743104b7009455d0c4d4eb825b21c83131dd8f2f8c304d92c8627273f9c182529e18712a6274b7e3429aa5204f362c20f136a33193eb0e4078c89862f84474d1586155186d8fa67519678c2e950ab42a638af598d8928706a90d2c40629566238d1a83d67525c8891468a0d240b5278505531d3ba33cfb7879a73ce7b875c107cd3af7f64eba074ee0952588ba3dbddb62be309adb3e70c863755681f353386d03c3ccc4881fe6ea061a4c13036c070820a309040bed1e0130d3ae584084767bc1cf900e648461e25bda0b4cb6f591454c7b9f33e300744695027b5a38259dae313feb05801b570e1e2c58b6305412f60c49041b266d0a86103002ffcf9eb59a4d37c727ad51cf6e44a5e3accab05ad00086000376edc20000ea19616d779a13e2f7f1d06bd5287404f7a2890e8e6b80e41206d732caaea8fe2708da4718a32a13f4a5677b7d65a6badb5d65a6bb3b515d7ca430577ef56ebc5b5561e2ab4bfb5d6da5ab3b5d65a8bc9b7d65e5aa9b5d65a7b456badb5d65a6b3d6bedb7b364684fafcb5e9775a8ebfa4368fffb41d0aef5a2eabdf782f7de7befe675f7d68c3bee5e226cadde7be79c2048543f08c869bdb7de5a6bad76c35d9e5d9e9823efbd97db26b74d8a290e71daa8b372b7dedfeebd3af587ca9c73cea04aaf7fd0aedb7b76ab7bb31f04590fdf6badddbe8fdb3008125dfbd9fba6cd41699dd40eea9b14476a47c5d393da51599e7b6f8f4f55f1f4f46c9e9d3d3fb5afcfadd5faf60189eefb7cb527fc6ab5f75a8b83c577efd517e8de163e55bc66399cbb0704614363f2e14ad5d6cefb7418c0c109b0b64934101f600eaa2a1929c551ca41699d8fc90829d0493d06a6902b391425264a2328cd5a5532528aa394da5165150f16cab55aad2664b2e867803a95f089762578c6d9743472a7692384c27b7cd8b051a9787eec1552850c096148085a488e1a9bb0874d0f143d60abd5f3c3a27ab9f3bacc429c17bc601ab88ac87fd808a9d56a405688650394f2daaa039ebbbb7b0577d8bb836ece49575d5544f6c694bd70baaefcd448b5f6de6de3a6e83ababbb5eeeeeeeebe7118679c73477d568cf3e79e73eebacef3be1a443be89c9808ccc9b19bee54965277cff33ecfc999f6fb9ca26a543b83b46a72dbe501411d46a374ddf56da523ce41a1505a6b9d21a21c3b7cdf0853a9d40e49546307dd2110dd2a1e90082889ae63a9bdd65a6badb5d65a6badb5d65a6bad7574f7fc5d1e8968c085abd6eed5bdbabb57f7eaaea3a3934aa5767676542a1511518f4f1895554e1e4a1d746010310407b90811471fde7f4e854dfa449f6a94299bc640a21ac3a2c1112f81c928c42177538de85315aa5b6a14c7a0569916d047a2f5cba68fc2941acb5386e90b3124697dfea49422a184e6508ae4068922d66ceab02f94525a4413344529a54eec1d4a2b0e4478e0043e74608511580e6cfaaaaeebba9f212165776fbbaeeb2a120f605ea0bd5cf9f1e440a2bce9edc3ed081cf6f6b38aed618448a20ad9cb8531fe376208a618bbac6184525396a61a0864455071c66e7f99d43cc984465f950f46cc0050c43d7387b8730aa625fbbebd976969df2150d8778811ec4b44d2bedf3af364df1fd27106cdbecf030d07f645d3b46f1a12f64dd3b46f115e5eba0a5da6cb2484d3ae2f5ebabc340dc6defd68632c44d3bb80440a2e4079c900b36d70e802b6865440a4400041800922ceb68fdbdcb80104961940f0b06d8e7d69afd76bcacd21892b650389364bc6daa06d6bb0586d34e55463ade5b66ff9d93f6ccb83990e98e101cbb64ff200c5b6df1a62b2edeb506261dbe721dbf67d70a01467db0fd203d3b6df8107b69df960c5b61fc20f4c2f2db0a23073b634c3b63320901cddb9b570a40993315a509064042c2cd828a4783c4ba66bdb33d8be3b3051e605ca832e75ebc60e4580a006aa103ec6586bad7fcc9a272d2c86e2e9c598374d80546113b226949a6072118e8953b452d3444d143557d46c69f1b22725b050856e62405816bbf23302a3950ce94aa8e689858ca9c86789a61f198d4acf12b05076022a3c163bb1e4041a27d4f8844e4e5025e1d44326cbbdf75e36489435c59459fac1060d0f48c491648e8a52aa906644eb423ba2acb9552119d9131dda0e19214cd03fa0e89222a326ca1346984ccd494e125d34193831d06210348180a6759862429b241d567852c44b87189ca461d241c624044cf88726b8a933a20967c491114959ebd044d1e606ebf006ca9dd8e2354a3fd630717a87244eac2a8c628a1f51584159739758ef1025ca089c76e80213c10e47454d453bc8a614a114bef036806025a9e0c3942f58b4dd7336850c8c351aef399b3233010833539a76a99f86294db08af4ef399b52c5192a55c2300446e0cffc16e4a19c4b3ff3d72f73e89f96b2c0df11756c624bac6298432bea5c50ce337bea7c39d3f89773884e447d8ef8bd8b3954ec6ce888e564431ff42270a744d47ba2fe1cd17b1d31e7679b0d72306d80ef2dd9d068a858ce1e76a9cb09c410bbd47ba6d99a875c3b3aa2426a9ee7c9947833a8cc479b7b8ffb12dc9c36f7a56a03c1dd9a6cee37cf0a4a72db6b73af83b2d8a5dd774baa4275ac5b52cd3f9f6e4dd306f7155ca14b2e4efe42716cb5fbdc7369e615b93493c5855c9c79c1b46eaa0836c7856073f768ab2579e8ddc7cf81fd5c57f46f5265713f5dd8c73d72e12d147719b11125b91f63dac77293825ff7a80ae1e7c27ba4b967818db82ed8c7d78fce09bd7db9955b93d1e67eab71b5cd4dee5dd034978f9ce2ce6883ef91b5770a4121d0f2a042add6745971ba3c178f0e9a73ce39e755b5bf01f9f4ed58040c10f2e9577b7f0392fc4b7e9109ae7ed8afbf01f9f48afef68fb8aab8ca9ff30884becd39e7d7599c5b67189651127c2ae2a2ca0261c4788d9594d0df971c6c73471c8c039f2ba238ba073f3f75af0d76b00d96dd1a8a83e7c1cf4e1487eac1cf6d288e9d073fd7288ed4839fc1cf4854686baa2c30a8829e9f3497d96036aa4239ccb1581592f1e0734719e87111e61e7c0c23638ce573628ee12d542897a12cf08fd0a08131967329682c679959a682b1cc91e3c68d5d724fe44ff0c7aae7ef1f71ad7a80d4bf2311303d4f64044289904fff882bc7385865818fc7b1c45a1c4b1dfe58862fc6f2398e5659e0932ec6926c31963fae46163fe1586a9fb1958f36f89e7f3c222e8395f01266c26828093ef9d365c5551e2fe043c6a7385815c2f8e9712dc44e1ad3b00c912345fcaa2cf0afc81d7134b0e48eb8a3a31cd59554361a734c2cf118d3250e017e55a1eda8b2c0bf9ffa9e3127bf6e4ca1679b0cb41da58e8e36f8d3458ad3c523e63594045f2566274a82bf23e636b94649f075c43c0525c1e76855686be26038d35e18a68b3608eb621b7cd8069f3baa421989b2c0cf6a9ad0fc52584667311b3cda60ce98c71fd4254397d1dc13f75431e69e9e36f89efd5a059817cc0d3e90fcd2d9bfecdeeb44164c3ae7c11e3963d206df836ec494fcde52f27b1dd43352ceb10450d71d73acb2c0ef9cd0b3ccb1a6a6a6a60d7eab0a3960b2c007a3d8e0e7b8a0e94ec494ec5e8b9692dda3c41c31487e308319043b2316748506fc493e08165901195b3f8b606e95df77fe551ea7cbe6d873a2714adbe8a82c1087f7f33d2cd6dfc0f74c4524603cbfe713ecc6361c5416f818fc2c967a83ba07fd3d8ce679b40dbe3375e07b97fc20cd20cd20cd60fef2c17798831a0cc1f73e1104c1ff3eaf29e1cda0922bbaad2ae4faf7a2269f2d5ed0dd779e534ed90f77d1a78f7a1df451e07ba223117d54163864dbdfe8304a823f4170aa9ad0df7bcfbd40eeb5c1df68756eb40dfe26ab186fb20dbe0b9a2e72a3ca025d1ca1ed57071feb2ab43df8215985eea3c632c49b2be262568d7eeb256cfa7acd765a43700cfc0625c329f404260620e00d42d8c107f3821bf49dd20c5c844d472edbdb0ea60dbf42039feca8c48c9a6962e9d08c00004010002316002020100a87c442911c87f24c14dd0314800d65823e665c401f88435192c34008628c21c4004280310418620c62caaa0ce82a27d81a1ac6c4d01061b5d9edcef91f453135597b57896b298b5c3fed1607cb8201197f8f5f0b436057a8d99f0bf9df4b0982c9d8a91fb0b66e869a4f43fa1ddd75ec69db3caf7c264881990dc6a65fcde8b6eb2fd58728b77d373a70d78b6ab8db30c1258e45eaa8e9bf1378e20dd0f39bce6dc57d9ee720f1fd0bf4ec72b18683abeb89f8ecbbde6857bfd033626cd43430c9911a89c73558d1ade1cc59ecade19f68984a28de704b00c86e1033c924462667a000997ad9e6656fff6e3aa9e4c3221c21951df47598f989bfb3a747f91fdfadc6fb8a6910d74f90fdc7cb2040a317413834047e19b9d105e45e54c75b7a7c8de76a4a3cb8b2b970206acbcca66c79930d6e481c8615bfce2dec4810865abf6c19f6a0c1c2a98c2f23c3bacbca2dcf7f44cf630c6ec94b5026011c8f8f599dfcac8e5c73b2d5dd7c505ab97fb19c9ee6409494fa22491750fde0fd9467f535c948f53a394e0a3fb5a51439a474a43ffe0da2f0fb6225beeae4d2bf425289b3d7d24d1411cd89049d38e1905cb5c1f58c5a7282a2aebb4b01a7f3cfcf1e91334257e1c78ba5ef2af4f6591ce6e3da3c315dd24bef1d6226f74ba6169fd7c5f7107c2e78bd0bcdf326af270dc269fa5692034440a399b139884ea4386b2e6f1818c921af102f9a4d4776d6e1dacb78121feb114e2da06156ece0b1294d364a6f9a945644790f06d10b189394897602912627164d109876a7d104b94a02d963b04c98adfe403829338635d40289c7bf7ec49b1885578e65310819420c4340b0223899f025f244a068b79d492266c9fcff60100068c9cf3b66322c9a0e59f6c641e7810e1f95b821c769ddc56ee1acf5984945cb94453feeafde9aceda418d5068c6ddd8431a9b2431330886538fb0b5f29e2cfb904739c92981f50ba5a8a09900e1cbf7ce45b53a3412d2199c79f3d73279ce7bf8c76555c28536b3115e28e1cf42d426884fdfa5657aa83f4506f8f2c20a335085c96c98e8c841e481cdc5f8e789bce4f5fed95e47bb40a8f0d98c2bb2805ee504f04ea7f02d6119965c5fda975c2f4f89a25a6b61b4201ea3aaa0a9a9f0ebb8dcf3e12b9c10b0b18ed82b3cbd7e99bd98d85ffcc22551f2a8ddb51e579d1c2839cee2b99994c7ff68af798c5550d55ebe53f2fd9c7cd7fafe95ef8b372883c2fd75f738a45ae9d2835979ec2e8472f86e2e9dc91b3027eb487aaf74b275c9bb45fd7fb93b914fc20c0163e2556e6820d3e87f737c6b4167f9950cd460687eaeefb159345275223f0d26ebc0348d4feccdb3c955e74251d4956a1198835f2549e842e1725db120c2984255cb18ae10a0ab8cacc0ecdedfc96e82e6954497942952d6861f9b4d9927fbbd06b62bae756af2d71b39b2891f5676abde8076e883d075b9e6df8bcb3856d6e558bfae510b77bbe76aba13ebbe1c8ea3c63af59f36c84d316917d238164dedec159226a108c8ba947c9c62a0c03014eb9c8d509ffa5bc05e41607c6c1dd535b1a1aefd55f6db9f96d7a3b4266d2eb39f69bc491c6705170635417c76f54703283816e6819c50faf07ce909da0355d728e3cee04d1a8a8918516c52d4e508ab22291c139e499378066fa2098da96ecea3b20d632bee696da3f856c7114bc00c7c795d6c50aea4a5f77c8e8668e51dd5269c2ef02b1c48dd6419f6cbbce964b8875173e27282c87f2e351353154d91a39bf7f803d6d3d51d912d98309669d2010b52e5839308a2069b02be7d3627223b48889c6cb50d6cfe56f7bba647e6faeee75aa747dae0c3b80d82a1b20897a22569a1aee7de6dec850fe285470d4650fd7ed5bff265cf56e3e0b7aa584af25fbca77bc576d8f8e320a50a595325a07b4a7cc9fe66bbf2e1dfc3683c6a8f769f2645cf49f1961276f04f5a9bc0cb9380747d1075107a28e7a5b6d071242cab1cb1e0ea9e3f70a6e4bbeba8fce40b75d97b52356f987ab24fa187d438aa92fc23803271ffe4d658dd7f9d4b4fdc4ccb8e39ef21b13ac9f116ea22bf08059a1029821fbea8ce91694f634fe3e20670d28b1805ac5bad5c52de942e18e92dd1950384b594cf9ee9db5fda8399a017dc3204bd8ca37c8f9393edebcd9fd887d35a00d06b25c7481116d85c65c45232f6495ad569fb53fd2efa112bea7534b0cb2d399b4caee03b8d601c6618d213f97632f991829a2d23a0e988a01184ada7b7d6a20ab748f24b75b7acc32c90d74eae950073858dab1ae2a47d95f83d02dd2e42ba19c89c073e8394857ff25a567ae5be5d255ebee0367125100a3d1895a5c4794c0679555eaf33c0f7fc85480836be9d8a913631f4c1fcd008e5d0d9df2ebc2d77959d0417b9eb31165b5bea1430194084028e88c6f2955dc9b9bf641c3a6a718d1c0ae442a5378d747440564a055d57a5e6b40dbd02471728212291f7bcaf2a6ccce8b9c4c47b03330427cdf29f9263bdc8cf8021c8cce00b28bdbf0af40bd2d25e6b0476e5eb662ee0ef846570cfd469589934a62d5cb8eb8ead95dd475c37147410608873b81452f58247c2bf07c80f241789faf967072df51961e9fc4b31927fa71ce272e5c8be516e761f8754f05d74b9bc8074ff31ac4eaf56e8985bdbd9640b994fef6feba9e81defd161185527321d3ea19f3cf19275ea7135a42da5955200b2a990c4d2e6625f8cb513ba220f1d67fd3fe90b28eda00bdba9fa161133374b3d69d9663794b9d60e423bb4f92222e3c011f1367d71055c4f9e49f007cd48ef0c6b59517c8d34f9cc2895bae29adca51953b4d75035c7e7b437a5619d2e369b310823f197b2411afae924ac4111d40eeab670594b05482e41417a181c89128e3595a6d09c0db69a6f9d5693a3fa789ae4c1265e64768fb4ba30d5f41b7e896c5857a57722bb24c2dc61bd42f36f78b5d972703c07644f72c2b0486cdbf9042177938bd4194fc2564851ce6342ad4d864df71fcc4d3c6755480c9a1ae1eff08b461d3404ab9a28d109975dd48eb47b23a73dc73228d20e0d6981777c23b0be501a1084d4f5cca8b54773e8c85fd612003940f499e88862c0e7e0b4bcf235a960fc5e925701b59e568474ba462a9ba301524dfe5bfea76bf83959878a77611885cb2265578cefea64cc9a2456ff9436bb136ffc38633df479f5325a5bd0587342436c0a3ac35da5f019d2f536810e36076dc04219cfb3ba8fd1fb3987d7c4782e7440bdb1b8eff106a67e52948bba04147c8cf4191cee1aec8d2df88cefc3ff8cbf5b353e4566453b35be6d199fbe7d073f90b35a7833bebb682133595a7c04631904f560f184f8b8240c30dd5a0e449e2db9cb5127c5a7ac0f1a70f57da79237af81cd81088e977d097e61138949481a1137b4de92edf28df8bb787c9102ce7befce19e382e5b761ad2d03d3b71b0887c05029fc4f7497ee6aef1cfb1ec7172feb4fa09eff82041ef92c32ec5de519defc68c2736e9c3edeee16bd2f794eebb42bf65dea99b110037f29017aafdcd7d91d036569bf32e00d8b7da8925ad6fe4eb49316fed23372c42983cae806b82a4f7cc58597a8f01fa84db9aefd1e48f975b892764ad8b1c3a7a9fcc9a27aece21e5883c40bbe52821d8c02b40981d6b65fefd7922ab78966632ebbfa852d3d724b27093a6b47824b0a6b0d88d2fadff1f5901abc0959139faf3fe39d1873be45e5fcb7ed134990172dedcad8e998158b2bbb5327e5a404d95953e1f51ecac2561767e18e45e2d0aae56a8c471d7aa7140f8086112591d9cce551e38f0ebd2fcee6ca0cf604986e4d1e98527302a132d103a27077a31d525c88986c67cf4a82ebf0bec430cc8f1ffe8f92a88c69672cc364ea2b5ae017ba9d74188f0c75acf22dea894fc5a1cdbcfd916491f296fa19c755201e98a3a190cc5ea987afaf44006da82152eac9ca23e503729f814944a78a604891d0a8d0729dcfafb6d58e98603c8bc8eed584fe24c37ad1b7c09658f982fb7cc585f4f8e7e637b3be7c20dc89702920252588efbbbb8bc3537f892a44958ce2719a15f1a5ee67e5b42068d8efbc16bebe6dc98000aeeb5cc297310fb464df60bbd49aa5265cc59c4134a009487c1c97aa94f53c318170df2f91110acbc96d6a289bf75376ea0ec600c3e52c5cab8c0743e878335034ec53ea79c790e617ed5ca5c6066885f1ec090f3255dee96b256f02e8f7d36fb9c1a476cdbfc23e3d68d2a31ab99fd207e6c60e5a74b6cadfcee3072e9a551c4118f3e28faf0be5bdebae8015ca5a829732ad95e599a9348ed881a992c356381a5b5f0df5aab843e029e0f24ef7e5b5861a0291b8d5a124feabd0d0c8591c19ccf10578d813cd4809e55b18f625f879221052f514ea5629ae02e7e80ff21f9e0f680cfd0aff1d1b821be69b73d9a34fc08cf43260813c0ff7a646541136e4394c5c584ee40feb07ec8f1def6592c9b624f73e47dd7542ead4b43b22b47c40ec1b8243ce508114229a823c2d9a8c8679a0c7133a05be73b83765153decb64c12d25895ede4ff315904d593edac9a0bfe41d5d958ca9c999c775e619e2af9d6acb34d2c7f521e11ebef4dcae296c28e304d14410664e6cc9ae8edd0bc5eb6d2c68114745428a0c29a69a5f0546b2d3d2caff559c304e074f7e5c6727ec59edd35216adbd139e32bb199d5837ed303550a2b5cb739ba2f977aeb92a304c20481c9b834e5a2bfc25bb7d9e547f640821ba1a82c5526abb8014898b0b5d051d9cb5f28a445e19b3a2dd950c01204f1c7669ae2131694420c44ed42cbfae04978fa5c57548acc773861715ab9c7f047d72420fa8adde633d1156d5497a5329dd6da7c2b340897cc030917c67c974e145fe28b04d0fa082f6270fcd7443a1dc948c9c297f66b40da218fa736c8e30d469189d5fc6675bfc5f9b5aeb027c3dfbe5df94c11f74226ba670c628ede39fc2e76321503e2a61a02915b098f6ed2584d053360e58a7a6a9c24cf8b7704ed642d943cbc97d07a992a1f5ecaebc20401e7a0203729716ba6b31ce7ebe75c618f998644fd9a890b581586d596240c7626a19eaf11c116e401875c37f5c7d880b10f92237f941e269710c77c07a5acc91de1e4728c5c4d89f07a235634e62f219f18efb3616102006a12c9f03cfadba5514d5a52ee8ef3e3bbde7c2c413a2146ce87e934659d4504f71b763f423ae176e6af2a606a6170f834f82346d632448cda60f28f874541cce70ef4136548a07498c113394e64a250902b398edb23d3a2aa588830149328f483ac398405be16f72fa02b0c214c2d2564c4f7338899174d7277ab2e2e81cb2aabb7624ba23bcdccb7adf73836718044eb753229d835c634dd9e04f1ed9c1623006d9359f2159fcffe3e4f5acbfe2e0f4a815a080ad0735a752a6b2d7b378c6ebbb439450d7d475eaba29ee3a90314475edc8248f814f88aa0701cfda12108a323942e5ffc5446ab97457776a384d1e7073b85d9e8717a91b9ddc1298f4221817d378a7f2f0ab05d3dfafdcacf15462448839c82a2020495d44204bc49a8fbff7f19000d8393c670b92f5e28f051a6fd336f00651d094e25b23325c330a803c32d013db104b79129954d725bc94b807d5cac913c61b84696ce5574615d9d5f6b5ee87ef037c3ae921a7477cfffb77e9e76986055f79479bf16e1c572bc58ae94842233149a8845a279ba6c67a8e6fd357c1cc47baa6257fbf55bea7c025873bdc1ab3374a3ec4df449837d3e841ce9a90055f78f5c05e9f5217e1437c09c05135d35e3da575ea1c2ff3f90c11cd0816a01ef9790c159b19dad415457589181ae8b42bc4c9235981e0d61ef29e3ddd9747826c5f97aecb0063e132857176e11030b302c1d3ffde49776f3082c3d30f3ebb62ffa322861e5a88b238c78d965621ce69d1dbfb1a5462893067f99452f2cebb646401be4550fca1e268fac4efdc31b1f9876d55bb6f6dbfdeefa8428cd0bf3a71e3ec12ca2d341395aff4e314650fe31dae67184ed130bf2417a7a1cd577cd71b3c796e75a0fd6ce479565d2f24cc1ce97aa276b2b9381644c5bbba4168c7f81147e60d5f0874ad8409424d7db856373147d036c6b2eba5c3a042d146cfacb30ec4663701fd2a437f2f8d155f3b8cac7947f0b226d3e25891cc7a5f2f7815e9ee68245c23ab9ad76683f27532dfef21efeb63b4288e29d8365011a4335e2385aebb80d558e598400ab5be7e43cd49d1be351aff492523477b50c6fcfedc8092456d50e2aa89245a932feef1c48415f849ec4259165bcb2e7e2b3ad2782b9998ad1a8f4eb0e1f2a959a2634d643e5ab4b0bf48136b810d205173c7799a3ce7b4319821926feb0c756d78f8486ffc99c46ee5756e4fdbbf15c894814c98a1d5346e95875166b22bfc010c74936f74dee4a177b3fe1402f60540d13c36700a8b44f7fab7e4106601b1f9e70a03c7b262ad83a9f9516a01e7c7f9b4ea9b7871970f8c35e35f08387770dc88e32bd6ec2ba924b91af8830fd36a33d0837be85db1113766167f79bbe241969656f848299f88218757d2509141648a25db98bab04bfe79e5543ffaa492f823fdf4adff6369b37bb93f6a607d73e534dea327536dbd957edbf298da5d7d9afedd156f54f96fe52eea7e407536053cf7129c1e982b8eab2538206b3e0ee40f81582cd970efd5644fad9cf72a46be60c6f537f10a6f688ff4a152227c333d81bda8244522ed828692b3362e1e1489da72b95e14c402478922307856c981ce5cca51772a8712f661d441d5126f9d7cc9b5d821395c7cccc1774633c06a7874c1227bbe284b8a0695582c685817749f7219364335e07068e9264512fb0ea7156be482e076aab1ff85b059a16a99732343d0c3f01b56b381724dbde96093f716025f098329b9dd7449f02d00a1cbaf7582f49e89a2c6347477834cc210947bc3bf93adf364c0e52b95a1c2fad7d8d46345a87b2d83e0b0c2ac7e6817bf6befb6f4552c4356f3ce9bab94e91c835c372da1abdb896fd86e1587567f86506b25ff1cc8137402724630d1a6e6795f0b0b5fd4c94b9a31a81a40913a5d5251b0a82f77c0dbf6f4375eed7e34d7499c9d65fc75fcdd5f839678c6918b015882cb57c4c74d030fd5e06c60661c23b3cb3e30aaee8bb3c66948f776a705da9db7dc6c89728a0716a7058c7ae127dee1c63d95b75324ab28f68ade867531222cbfbbbc7426fa6b245e29c0ce08c05cac466e8d6067f216604e72bc4d0319c93c69282a8325ee3973d5a56ff521dc0a62e33379ed36108e069c38247f2b42b3a1edae6fc9bb45f2648a3557e4280ff6b9209ab9c60821233e81312b5f372fe9de589d205a773c8043d33c16f8ef38462ed9c434624c213aacda13eaf674c20d75d712b4e992fbb2b3188ec9776a319147de8dae1b8140c1f7dad7d16869105dad409a0dd0ca53244bcccb90a397302fd208d6fc4decf8a9c147a7fa33800f5de467238164ea008916557b9a8f8a19c00bddf4aea3cb139f5f0743a83641b764917b8fa07d047d83609ebcbcc9ccdd4f3a4677b7e7acc37467afab81def59a09b8960694d199d0b62debb85613acab89ee5859b41e6f6eb891fce67b290289c0b33419841a70978da9e711a023a9da6941d5122660c84844ebb2b087c8eef95eb0e0ce5a8a1b9494f16e57d05c1e3a0fe13730f9c9fd6e026d1e4e5b68870ad0f832f5b8d42a691bc36db8f459f36c84bcbade7d19d89fecda504c6e64ca2ab9c710456130918899ea439ffdfd4728e2593fc80354cac56a517c6d0010ac764dd6818380ced161d6a89fee0ed9afe545dd749b6006ca89220c69f8f39daf7bde019eac750a129e1612de1c5b8b6da9e438898d667645789e12cfffb0dc5a1759c98fc5b02b387f833aa559310f062db5adc550a04de4ad05e7ebe46c7544d822dafdfb8782273c8a95c0a707babe9438f76fe5ab46335979328f0bf3cae9b4757b7f3ae364efcfd06a83122f0e81bd71a0cc22ed62e9614124125af12cefaef02cd343550b8860c38fb8ad0cd2d5f7029c1180c00007cec855a43d54abddb4ca207660846fcbf12cc9834c97b2318e2706fbb8e75c9efd4a124f2608bce7b5172e7dee2b8dd24d9cf1717a505051eb659b7ac3b0ec6990e77e3411ec89a86d7c106b21373d4f5ec6aa034e24f29f9b40a1575932636256fe1c2007a5a95db515d54f2351db807713e3ab3e81f29a723348b6e0a1cbd158d36a676f3c5da9f85ba3094f7bd70dbc2f824f4640bf6f4b210923f64c061ff99447daa9fab2d10cc8eac73d8cb2a398670bda1eee8234f747aa1650d2657da29ff8ddbb3ab44ca2bf9097bcfa1138391a68482f49b86cf19f345f81916a819ca3b504ecf853840ae4d56599edd4cfe61095c9bd03b69c75921c6f4009d04da38357c226212467928af5885c8aae60b3ee956e7f96046165fcc7196b2667d276980c4f8c51c143685fac6243829f00e7c90e63a59fd993261a1ded0b202e58c4c076be600272a364d229c1c9139b3426271fa5d889eb6522142d589a0420323c1e53559b27e161d9fe67d28ca80855b3debc04c6c906072584ff6c051f6c4e997c74838fb9d157e725d55ef61ab258aac8a18d1042a5cce74833b4e1151d3ecb39d9aa6db8a0dea13bda5854d8196b41c2b61cb0ab2c1d673d280615e84e2311557d97e542a09b359beabbfc494d45b810e82761c3c049487cfc648b7a9bc27c42e68fe89f2e23f3e28e4958e3083356b54fc04d1e5d70fbf034e0db313192aa688bf60defaa81a0889793e68daad33179b924af7ede6fb6f97d6a51b018e3af64dd9aff4879816b728e800f3acd2f2ef99feb4fa6f9860be97b95e9b9efed7ca5d0d3594ff73ae50f64bff6036a92be0c1097e08c24d1525bb1f1047e60946366840bc0c559c0c47716a2c79dfd095fbb35f54a57d0f2822777bc5a6e6783981ce89a55999814a31b83a05ef1f54f99910a78fdb2202088a369fecf5209531f46acb16648c03232b24b9e0245266e3d9d08268a59c394f58acc75b5a47fb5088b5f8b3d5a9602b4d6040a999a29864a860a4cdf84b6c3d26499c6ed6e2f71a74d1a7370a1057a65d25fa7bd34120cce69e954d4f7ed3b510d6c3f6e99fd4d24509bc9ab57929b742609acf035507574b0f619a431dba47ab9b9a61cb8b09f79c694c392ea1bc3271042e2c835aaf211dc2798c58ebbe15fc4b66be58859b631949c78cb0c22b42aba140d28a50dc5c55a8fa56705028dc1c044576a9546c45ecae16666dd4a4c8d28937d66d89d54a5fe386d07c18e924ddf712dd59f4563434b14bc406def999c7e9358ffdba647b9420f4e3191ca553327665ee7f68b0f6525774899f1fc840a49f5de96a5775990a1f9c7caca4e6c55e4af053c36b95a597f5c6a262c44ba364a551f21dc6d275ec05cf7a7b95d24ee44a02627283ed6e3e28202bb5c70d86bc83a7d8f32e2d15be65d53926eb1ac2031b0ff2c860f0ed23dce8ef2dbdcc3d588fade57de8514b067c257800fd300fd1e51f3ad58691b4d0bcbb549600970cdddda5acbbecdcc277e8f01bdf693869a4234b9a6cd216812172d705c8b69a73dc97c31ba0635f8d0811ba7b118b9aa855dc0584a02d2e5941ec1b749750255b3a9bd418ac5117c0c2f4e3b52f4092dc915ecf99af53d2aaddb94ba2d41b3bacf405d47bf7886fd3b5b0c5cc0582700453fb0a906e214cd35247840a12f344939f78e7ca5adc366d90088c01223f66be5110460cbe00f1e84a9128436c299f045500ecfee680d69fe14b27cd3418b273001b43cb65b4daac709bd4b4aea000e3ca8aaf95e8f60fae327734d716aa8d80930aec558bcc424bd2030cbfbd3707de433ba43c6122c014d63e052c08d0118f14980b576591afbc204ba19b80dc5d047fa7f7a3ff92e8df5da75460d85d2282bc0840f9b9f78f043fffcd317206558920c8f5295d520d88779f75436c0171eab692af86fa0ddc7744fe435522cca42fbfd081fea78fd7f6b984222219da97851bcec481d7845ce2f55f1c33bf6ed7517d949e698221c74a21d3728b3748a1f7ef0a3bb1cc917e656b37b2f55bf3061983a21d25889e4ec34a8784f6af12ae3d909eec0d58f730c2ce008d5d5f72eb9427eebd4423c0c8b61a725bc0f9a121b5b3a73c551f71bc72fa0cc90d2a21713052277e2eba3b5c253a8eda809aa671964a7426afdcad8e76005b37d918ab051f27dd13faf625cc0686923c385d7fc1408aec4fb0edd4a9db18c3d1b1023c818597fe145a0be2fc2d1e77be16de7bdd51e1ec88d2b0d859f3a9a1b85d51cbeb90c3961dfdba70952ba444ffaec9c233604976c382678fdadae4a0bae5b971fd85f5c6c6f38a1d56db282ebe3ef698c7e44b47212b48159a00c102e4165aca1e4ba0f781068753f56835cf8eeb97c578a92d0c020c2df5d9d29c532814b7e66c02d5d5ed9201c07843c4d8df519ccebcf0fa05b091b7b60a1884bebc103bae7a71251632517c153f982862cd855b904f70108efc6951b7cac2855c2bdf3a0658552a3f4336f473ca5bd12088fa3becbc8f05c1189d01012faafe0471c262f1af7382ed398134c41ea6a16958a786677efeddc8b77240862b30d07af6ade53dad1adb8d01fac102ae29db97e1d6357926311ee8a712f5b7f23d4353790a988357e13dcc119c5d9eae3d6696cf81ae24748401fa270dc3d6c5280c4403eec3d35125e8a8d3154f84a04d6751e00788a62af714ce12244a61ae16e8779fb231a014c71005fb58df9fb37cd0a916f49fae00ba77641461967b2b493babbf9a9d544d66dea97c3015f2cf04cc93c8dc239557d8503a3824878c788da8f7196781894b402fd8e2d89d30dee8a13eea62bc8bc843193e519dd70627b56a10a09372ca7a609d589c31e755900b0e11033a624200deacd23d58d5a261dcd289b5fec7714cc15907b986fffc9f48ffdc041b6707fe507a3b91d9cd15d74176e6963e2caaf777e0555c0f22c0747b275eadcc03560548278bb0d6ffdf9ed91624299f8f070246267dfc85eb161c05f6cbd9445c2fe0e17e2bf3425050ef35ba3cc0d74a55fe8d73b52feac3fe2106c18b0a8bbf02b207170cc541b61b829e8b958897ec7caab7dc79a17a886b5c2d0b27f69c29f458c6c64e24fc87ed1eeddee1b0ad699ae2fb6f79c664fbd7fa7bc9fe62077c1b5a20289650fb9fb9427790cb786720234980d00a435a097d1b7709e40f0e33b6180f2c56fd3eee3c7ab131772f57afc39e9d735edef9b9ee0d1ab9519fff1e7f6b24f685f8ed0ac01f199585590588babfcf077a6dd39c7b37ed85da08c9a4d42ec6112877cd25e793a398b763075bd44cd6aeb8a6d040612a5c7197da9221a1fad69d837c84b8d08fffecd18799f13383375fad267803dccdc461f53c2f255b9bfa28227f182224abdb06c128548006d3ccb0442d88ee898f13fa88fff7edbced3f26c8ccd568afdb6e481c9af5b21e4e11f79abe4da39c4ccb8961891f5492e5fbf0fb81f8df23f9fb3709714e4fe55b218cd035e6cd0371a5344e4ceba07f3404e20f6a0dfcfd232202345a7bc185cda2331b4e6f38f18db2b1ee94514234d9a4a412911a383a3149400793b7d64e4d511ceec5899d5b7d483185f47ffab1f1ccc61f70165c0f68e842e60eef4014fcb0bcaf6870bb9258804b4a2179b8f372818e90ea2cc5dd208031583ae89fe3e012c818ccc361b26e7a6a67643d47d7a06788f04383c665a7d71e424d22f206a7ddbe0e06169dd0e9c900ac2edb6fd67da36ace681ad3dedaa02b6f7306816e22891715ef946ca29b12a6a2f6a43b1684697740a0ca56261073dc15941cf8cefcd7760ed44d676eb9f6590d52a7a6ee80814fc43593a492d39cc9e6e80e9f85fba34751ab426703d33dc85ec262c751ca69326e6d4104192fd9280a1982f32af1bc0f43201a773374788be3f39d12ec031e11b7008733521a20f15f38e87b0be1ce2394f568bac2b3aef576a8b641267ac81f41506af7bdadb5a75ff18dd1c801b84128f733284e4ab2b7db5601eeb191b98ce5a23107cd0c7a01500f194df7c2627fc52a2b73fc2d955d66e14e6560e88447587054b8ba6dcea50fd3ecfd184388f7c6a960569cd2a4da0be53568e41a72a6e7645e1e6c435ff92804e925857e9f06622170510743db2afde1a329dd0953f27f5344afcde33181b6f010adc49c8710961b92dfa7cd2f55f2b0781808638e209c4c5cf5aaf94166fb1824bdb3c956444aa32379b79743b438972941aa811a527378863190c31bad24304fa9e4059e8afc284b980587683a8f14628b0711709aba9cc5af570870e22e45d3e84beddbcfc382d864324d9eb0409558bd437ca2219ade60228ba6b4b4ad13bc4de71568e492f8ee78f4b8ed1e2228bfb20e17920d8924446efa5e1a4f5c343ea31e9c0db8870fad37e4a1dc86f1618aac9a91a103d4f17bf8baf70a1ee61672b3faca04535b49093a155e38361cf78521dac54b70575ea75915667da5acf9e7ed2a453b015125aad9a8679137e696c4c947beaef0e6e84fab616ef04d8b0549e835aac8ebd9c76dd01dcf1024f2fe320a171f318be32efbda1c7582048bff3ec7196799b5b76d8f97df001e7cf9ab8bd33bd45bcfb73e6ec840bff3abb3979e4d2012ee291e3bca107030f2ad2043f0601ab1ff1be9c7654bca225b14730f6792d315542c73aead0c78bbe4433758363a00dbb3418007195bc1707b0bbb00861909a70cf1d5f3e51ae57bd428e793b071c1bb1240a7e3999ca9e9f57eec805de1c6d99dccf0a345e8c1e26780f32be0f849a3390619a89222be8219617044219a5e20f6c99c2024ace24db1f5b1c10a597fe6583de0d2657ad9024e0c14a2757f0ccda2329a077313a60aa4707da658e6abaed10fe94b078c625cac3c0e75b1be553179c424866f6e8a86e924f6c365abb22f4bd80d0b52d774ad757c231fe5e461f46312f310a37534873ba75162f6febf89aeb0ee1304bcd2f63284d9ed04417df1f9b5785b39b317c565096a7318abb0da0e264e77f2a28b41093569cea1a7b6001e557a9f540a3f26b094a110d0b94687924fd1dda3ab59a3c0138beb014a2ce2a39e3a88d69d98ac567cf394e31205bbfd3a7b406a928adcc6a604f19a30c1b06dc464d514f68029c2c7296a10ad178e9caa7e3202f2b58e3488a643e67b505017df4d11b81647b126653963c701e645cc112bf9015789b4ad71e6eb450e30a957ca0bc1cc935682503a6d51c79a933fc8c11ba37a1ee0941236093081394ad414e3bac384846cb7a72cf9b92e4e102b6fcaa288de03ca052c1aba40282f219f41b43b95d643d2cf0c2c5cfdb704b3208408f3fa3b50332e06d16ef4444bda02edb258ccf0cb8577824c6537389b8c4d0db14aec3299b84ba2165ff4bc4a6b98efe2ea6a38bc84b31f6b3194816b6bf93fa543a2711504cfc41b1c165641e77a718f310967289107bc231444d3c921540aee9b7ac8e3e9aca9b72ec18c266812a69834141bb9c70b4c99d911961e7ad05e7f28085839026f4f780f808fa168b9e236638584209aaecdb0eda555debbf4253825fb0bc3cc63502dc854cbf4341b9fcde7f2faf67070ea4b4b017ff10a8f45052e50d416b58a5303fc6c74556936682952093f31f119ae24bc07e7ddf3a6c2d591a85d96a0fee331cf5ab0ee6e9a30abbd45814d9790cca5fda88f586fa09568205a7ce078d04c46356dc96865a1a15c3cc279794593521988d615582e0c3940a9dc9f37927f0fcdd1aaae0960d36df722dfcfb02ee7ae0485231ae9af86801ce4a7e35f3d3c6b668218668eea9419d2581f1e52a3bab9aa42680e4b8ce2f06a98d7cd92b42ab46410e58d2443064c60e81200b38016aac53148acfc9ba6a9272c8fec5d3e852d0af0eacd018582a007be737edf271901d1ae642f903e319af0ac8eaab754e55de24a56b338ef2fea6a20af7e808ada3c78eff9ab3193f11a4a138078c66531b0053b691b7c939357bf3bd7373e9bb316934e6f00d13a0e488ff57a4a3f68da70e302ac956ab45028908d716a3b98bde9e9444f4a13241afdc42a457cfaf7e4f00be92e45b5864d6785102881d525fcef67c7d7348eb8a0da8e415f6f22cfa9e398cff8780d12af751a08ea5ffb8dfe8e747f2d6b883347f8fc3c7a29b2d9b66a59f3d3a8cb3649456bc206d5e301c822b7be430de4ae6a7ff214d937cef4718477e252fe6bab45aac5d1872ba20a0aa53f171158852e3e1959f2ad3bc7ba5b63cb3622a8f996dc66b5b71cac32dff3c363e1c8ec309d586e5e6e8144894325ea63836d56882ba1553a1c13e153ada13b49d0adc49accecaa10370d5bb43357c8bfb8045d6c4d105b339f76746d297d07ad8f177dad9f23596a6ec75b74f6fd692bc95a29fb3139bb200c550b553a8e371f7cd1b06b33f5e308729eee3f417dd08d3129c9bfab52d32af84e57d117aac2d839422a813d7f68471cd87d322d69c729aeab06aa3f56512d8e11d093bb84ac765670b94740e85e656168a3a6717d3aa9d2d818bcab86abc2f3869f0c563b12b048054d8369f16a08ead9d75ca920c769cc6a7315bbb4094ec817622497ff1865c86224f04f6646f899a13e8da9717133ee6c6bf830c3ab4fa90f328f55d8faf5f445620a9ec07261c74b05d34e11b4ba631f5cc14d5b1d5def056d978ac35a7500be44962c22d25fb878d333f6c13c3c9c5c811c220b83fe976fb2deaa8d980535cbdb565aadcc907ace4f6121fd6075cacbe7ced5bcac5c7cdf1b6f1230ad67771ff749bb994dd91c1f19eec37d33355ecf6ca51b49ca0baa9906de2c4f2dab6704f74d9e1965f9aac5ac4659a5792b1cf6ca60e818e8d23de1a43e95e88c41a995c90ecc0ec22a676548ca6ee6230a153217bda9dabc71c1ccdf6aa967b62d7a8753cb0dc78c835b3df663ce7d700be6d90b55f4037eee03ea2a1a910f2e511c1da3342727af92d5fd7ad83b86fb6d5119d2eb6c9358ac765528273b18d831c370f8c3e626966a13679469b41d4ae427b930049649565a17186a368ea679e37c0fd74c5f55eac3641ca1f2a0c125c37cdd22f12dd3639b7674cca3d0edcef440ee648690a69e96c9018d3c41f19ee6e6ef68dd1fa71c7fa7fd6a99cd04e8e66172e282bcf9245f4a6a8c0d5840b26c57bb6e243b95e9395c79be7bbb75c47571bb83f8fec0de3e0b37c33db2188838ab67a4d388893d58b061aba9f0721ec167f248cb02873e77d59752682733eb3436343aff1a76ae51487accb15b2c6220a157b3538bf8048c8b1834e9ed50a6415ef771dfeca20241815efb838331e59f63fbd2efc3dcf618755b0e8b6d4d0d6441c5dbfd969bc3adf3e1b8bfbf5fd4e212fefb093474ae278e7e9e46eee3231394815e0e962ddc696417a9503d8399310c459f1b1eda2485717544a50418b3174954d5b3c0c30284bd8ec86055be15949821fa22fdf7bf1db0f24ab324ff74b4c9e92b623f8f7ccf1759a534ed63a88a8595530909456e571d3bd3d7c6505b28203ed41b341a5ac67fc4fd82c184ad88bf903cc33adc8d0339008c1f2b8121d908195d2569e4a81f601a0d123f1b215905a717c9d23cb568716b0b7761acb2603fc95d4eb45543e14b4c295836cb48ec97b15f2411d5e1e285dd057e3636def3bc225ad757c23362aeceae3f7e819172297f8073457bf8c4996e52790c0ae5e361b6d563b74efb22c153de1cf75cc075856c86c40ce71bdc9a3211d19654fb457f06c64bd34d5e0cf30ce0b224a22328d74505ef2590199fa8e894d3b285f81384428932b30a644411ee61626db9ca111369c172c6fb3cfd09372b6c884a8e8cdf6c3c8b6c80800a9c42cfd4600bc0817079b61f72312d5cb32ab465a5dd8df1e6f13e8455eea31992ea86041188507e92cc6e7ac90383a25c88741d19fee86760804340439e724b4736e90879cc26e3117b1b96bc78a91e9ee562a0c48012c6f38f0950f8f2797ae61acd7cadbfbda4dd651b4fcf2ba87fab9b751353ad772027847e67d033c4aaaccc05daf4f8a736776bd890c8e123ef3905d055d072426127a717829ea04fd0b62204f25e12d48c57b13a8e7463309c83cc3c6d620413e95dae059ce24692a98291891b102fe314f1352e0fd91499aa48f2e8b444edcdaf736277db2d09f80c98adae794d534de325101097ba05d739bd7cd2899583b67977487a7a6f3e0f13e25682760dfba50295dd0fc82ad3095a76292056a66a8c6cfc802019a73819e0407164aa60b27603e3653a45c93407c691a982c9da0d40902a3d0f683523fa6cffe235e56b3f2f92d4c7f77860437d64e30a09f490a5d7c70f39db4b23c8c5a7f37ad5935b9913e2e755e8dc18477a03ffdb931bea03535e136922b1e514bb0bc97bcc948a79c063e274a61cdd7cf4dc9bc1431de207f08d7cf75c938148886c47e9e988c100232ada72d6928c3530ffd85eb6eb0aeadf4a9d7be23039a88d5eb8aa6707c88b0f5a9067ce15cc62905875cf42859b1ba74c2ddb36dad5c8622c229ddd1e4174f239031037e9a7a4e0a3947d359292c092387804039cad6321936c9a8cdc17d993634817d95c1a5e4bea3bb728e60da7da5ad46f1f1f5986542c84eb55328971250cbd649408bfe9896314693c84d021bc7da0666b257ca0b1cfa811478e5948dfd1703746c30cf117f848b40f01682bbf7f3fc7b5c5c16919a57aeea9e124a45418e148ed721ba7c582ca6d14d560a9c7c71ef32f8e4f9c9b7bc9fb99d9174c45bb238a879c75bbc79fc76704ceba7258af664177211ebfd66e11c8dfd2953b87e01181a17658ed3576d572ae5b42b7df366755637108d4b431dafba62aacddb46efa2bf82d4c48559c9347305823cd5b8324c56c923b13bca18b6e1ba1ad6347ae221035b9a83a5f27b9bc1ba2c65b8aca4d36d644d22381c4baaa8d0c462439652df63e96449b96923e70c15e43254aa89baa549e784beaab4e9061671b648d24efe7b3b5ea49d21ef40eb5e5059612b820e0b89730db20ac5825057eefb25b86cd2792eb3275ccce960f8b10b0bec0020dcc93610bcba5a0acb5e135c67f3978570fdd739a0fa6343b5a43a14d26e4ebbae0ad93553dc7a6695cade9f034fa3b15ebc838b587f78217897327d8124da372bad0143fc0f063d3a02f4548343df13c5240ad4f0e0a6ccf2875f2b7a18a4c90c333aade36b3596af339b287e90f6ec749941718019b0b6dc4ae7ac0b968741d6783ecafec30b8a87e042a6ea9f53630606a1a3c4d6a9bee5aaf1d4a979f41f68c40efb236b0fcb172ba10fde5289ddff7ed50b763c35e5075ecf6449414a15bd606d616024ddbab3454f940a64b958acd85684b40cda3b6c5245b38422d7a3a54a08083271120e65fcacf4b782415c2d21ff034011a120db5783589acc177dc67694864bed5713e4516db58264657a600d2bea9fecce391912439e7b2fc3c3161094e8639b46214edfac6aa178b49550007611456b0a17bee73c1b55cdb1ef155fb7b5d9ed605964c543d286719ca4438f2071480450bc0c1cd44bad920313ecd3f70f6f8d78f2a1c5b7bf2c4b23bffcc05828a5489ea0a4b66e8190eec9120e7381d824cefdf9792495bf7fe1a6ebeef23c6af070b56b5e54cde44b7e507115a8e4f8d1d4197fd2ab7b8795bf701831f1bf0df21b3beddd2f470765a529a23aa8c4a755cb3d898d39031065f6e2bdc9c60894f12d1b79e4f13d23e79502775abfd15d54a42c59c48306ef79fec4ba76b191f4f43733fca00514d6267367a057c42e5578bffff741ffcd0c79201cbc0ce3045baa9f4241fd14809d5f32bc178bc709b6c7949394efce4b93448408059bd4204ed5cd48608d7287e692e5e8488765302fa504ccd65269f541c01086142f4a715b6a1f2fb1bbac3366d91a6900b3750e681f015256201e1173a2e6dce48bb83c114cef2bfff8c564b3836dbb0b59148274c24a041edbbfc4ce336e9eb51391ab30afa1e62d47dc11db58aca8e872fa126d13eba3729681e5cbc4b10e5faf32a7e84b8362d155a7a16f27af5750e5d223e4a004870409f02d4255973875c533f4a78b0795e813334319a7a3a4f36880a86d46f99b76b1814803f3279343814d8f980e5513038ea0f0461d9ad718afa5dfe8554e34711a00621ea67414797fc1898a3979297a8bf04e502e119fcc73353deacb4c6e910ef8ae2826767fdac30b9f4fb26fe93cc1512a0c1c0f055335f1d1026ab425e80edbe137c42759755ccde3a703fa7f8fcffe0eb0f9219bb9d2d571f9219b23711231eb48d6421e3e6cd57e0b1fb99473c415df767618026d58a73cf1f90054c681c31988ba9d4a013e1a1cf8eb9015a32c62d117db16f3e3c7424b52d8f75bd54b99d9ffe024772c10125658e814bd67e8db2075b7f663cbeca7b8b96573eec67ecc241c2c5c7f3c1e837c2685b5e733cb21201712ed0d9be67702c5f56371444fb792f10d8df6cf3b28b55b950f98123055f97c039883102a48760b25efe6c70c94ec7a020b252fe3ef5e5ceed9877973f1d7d17ae1e301dcd44822c72128ebcf149f57163901081c0e5a47a23556d88ebf51fec757f38b87fe6f68cc34bd29bb36fdc77c19e01feb061c8437bba8ff1c1c9cb8fe77d6b0bf9dd933064f2804c4635e6c7f36afa1aad138523e0e8c15d5bdeabcd68ece9fe4c06a4c98df6918dfd657e030838eca2e080443832b0703d44e454e09d0acf0d0df27e43e3f9df0a003f05d7b49102d63849de00803d2527b285d1f4f9f87d2000647663846af838b513824ab2576ec9d8e7100f1745eb3ed4fb4796142245105818d008429ce040c2376fada931a490f225c7297c58f0076a00f88a2f8c422eab67fa75cbfc4ca40be363d91df1f92d49b7bef20114aab86d3fb4c91852e833d73e64c489e44fd2bbe810f1d534e20bd7cf9fb276d0a896f5f3ae506a84d5f2831ed0c9de277079d310440f9cc564430e97a541df9188fe49b9abd8a203561e77022b6dd1ff96ed6aa883aee6f3ee00e132fdbc8c4c4ed23b5e7a1f052a803b2f052106d87bcd8940e7e687cbef6e19a72005b30762d84e37c17fe121a8846dd9325ee8df5e60bd5f30c0d3225306ff6227f06d84218debe3ee16fee70f24a9d0097a4913ac309f619832ded87858522f90ae60a84e01f592455ca38ae13ffcedcf3f14b3868fed29e7c405264d143e6704477c6824aeb2cee72d7cb8a22f56c9922b4552777833fe331cd917635534929f51768dd055ba8ab053eaff83c8265dcae994cf52b99478b471e97ebafad6f13ee52f677925e28c1742cd36baf0e61d85658e080cf5c26b86363a5002a4d535da0407a29ac5b5f8b4d730632f76e0c8bdaaa4873602bf275606813f53a82552aa44eac4bfd1a17d4639dfd3b8189643f464803781640a224b54bb3f0cecc8acca6c7196be52cd47bba012201af5a42e40d19e6f014617aa3f6306d8673ffcf12ecfc3146676103c33d41775b420e199f9a5ccd9e69f56532ad56d2f09da9dea36f59206d65e1277618b9f978836342374875ff278439766893276c298271612655093bf8df19fa8f5bea42e94337b39792813be84f84f6d91a7c22f52a86eb9605871f3d1a975ea41f42895826568bc51c4233e059792c3e9daabd2439c0e343543bef3afec0dada24458e63e369c6e42d14784b4d3d0f1a801d9c1f4d8d9d0f1ef112d1e96f5a188f9be74c55f00b8cc31466bbc07405d72e5f0907bf6f05a29c7c60e0f497f7ab2342f9d14dd82bfd0a7ac63767b27d7cada2f29aa7c7afd3e4a0745bc14863c4e973e39679bb1d016d8fabe7fcc47665810663c078ba646cd126b2824b5f2448f194d765f22bf6116073980a984ee51966eca29ddebdeb045b9d09198af0f4b427180960e4429870cbff7054e9ac0118d663e7d352393243698f55b613d8e00965438fafb4e9f2756e80caf90243a48d2b064ad4d9c443c77e05024cf84a68bbbf69ef0241d7f04fa566cb719ec6c99005a9ee89032eaa23d93f105b0c1f4ef96d83d85998efe6918bec970e158f387015843fcbe7d9fea1e748ed5ca08c06868f6649363d86100074b6865f508af811c94247fd87462fca1b5c35763e21410c705c4b1e07cb081d632943e7c1e3d4f2edc22d698e85ac112592740e03c469a7fa8875be4d92dfdd008549ff60408f9866ec4c6e9128ba031c18285686c5ef6fe31f3ae5dba8b746b7d0ad7b2f7c629fa0312155759620ef37f4bc6c349c6b46087f91fa31a4fe51583f1138c7908a344b937d0c900f058aac9b2dcc12c4e90c093b5630f462fd1a8ba4cd5251a9d91f5982a24bb3f9e8b42a6f1d01401625535d7afd9938fd84ad2701a86c81ad463c49b869012f2c9655a7a22d8a786a2c1748dd295fc289ca0ab2fc0f80e16956048c51de8d5e1720a6b4c2ed0a824b55c7f1bdae0cf278d490f0abdb350c848b88992a9e456793c853bec5d0b3623d693f65e9d12d0af4fc2519e90b5e978d3c9ef0a28649c16f6609169ddcc044bc877e50e9102a04c0570080bc99533890e0c2fee72fc7bd9f1d2ab90d079dec7fb12b43a4a0579c7cd26be8cc968659dec653c3132d026391bd139d9f9efb71baeda2200c77539cacafd6dbc9d3ff142a6940b87212ed03900e936498010f77580b62b52e6e22a2b74dbdf09535fc7e1b2c33d454b61f7fbbe9254b46b58924427e0e36464f0985de1b153adb9ecaf06b007a8dc6c5ab86e7d76c266a428b10ca65faaa1127bfbbb8566f29a697fc3178a3a9938d202ae7480f78dc79fd8eea2d261a56c17d609baf4fd5195443db72a9902abd4f488354b660eb722ceee9cd58ad881485ede5314cf48955a82d44392d8b511dcd5d7a6726b4bcae7825808c704e8a880c892f607543711c43a360c747b486899a22a731940e582c34c6746e476873e5d6ff2463a1ad7a4d266b7bc4a63843701e25d4ba789ec91073fc655f2e08ecbb91f32aeb746a37e1eefa5347ec160912223fcedeee3175728a220d08b2c314cf54569da8cf3f39032f5eb6b275c849b066f0e0ca120c41da2907fdaef2ef627e0d12834cd2756422aff55bd4bb116f5f0dca276489db87550b1e38ed98bdafecd72869bfab0eaf7d114eef5e337bd7c10b9f2c69d6d764bc7906e421edb6ed9c9ff06f58e34bc004901784489a71e00ec1d90b72819d3b55f47922a8ccd7d4c9d4f7f36fa2a196cae1998c99a9d123743b57110d0e3fb0c744bf894287a627230b00a5cce0c0db8add0b690d1862101b07e219f1e4ea5eaf4ce0cc89f65f88f078c974926819cd36804291b8cf62b69056c365ddf717f0864b46f3c7ed7e614af9f295c9fc7fe0eb3e2101412cd38fd9ebf95a7750c7ad60683a26f876e69e33a03ee7887e4e2e6e54dd5d048244aba73607e410c7ec899cd2b7b50d51f75672e90c143c63f575e644b1d6913df6930e21529778b35ec01743d95c099c76f14ed0ee6ba80b127a25697f5fc885cf6f260d299884730fdbe5e7f4b51c270b9699b5dc5f4f8eedbc5fc85d33cd343d455c3daf0300a6dced162ca522b157b3d90c82354a9778ba9445d89ded62704278e52beef9f95e7ddd25aba3afc7e47f3ee878de91ee837acab605771f4f5b72c1afa40b76bb8635310d56a2bb07a99820bf686a34e5c8ccc230c8b4a9b91e46a096dd85e16ecc40e97e19f0959846e621a4b72e5dd38104012da037c10d8c2993a94960c2d60b3bc8d8a09de0ea1e7d2b40b26c2f1446e7bfe41aaaa1878e1da14fee86d9fe51b35c719e1de5215d78868229575f5fa3b849c6fea86a2a04f9acfe106ef670e0459d044fb510424b82d3fa3ed586a052cba5356949103e945cb4da21c0ccb16b6542c2c382ef5a2935a1e9fda1451ad5907f10486f4060afca2aa4aa0aff201a49b403454d7cb4d29802884986baa3ab96280878763b51e197ca4c659d704327dd8a966c10e969ce02af81411f5968e58490de59e730ef83dab89a3a123fec9cfc8af360e27a961345dbcba8f78e322cee41079ea15628b1c382e9f765e17aade169694353d0956d87b0387bf9494f6c3881d58ca9b9558f0bbb18082d4539b747675368823213d496807fcce060c3efaa8b5b5f6abd204ff9edff6c6a1c0584908cf483165a98865e785fd0a96556c27d708bda67b057929d6d4d6913e9d64e90838b92a86addd526a9fc0135d4c162f216c22c0779bf7b0e0086e014258185b8c2ca2ddbc29af6c7bb2f3f39fc5e77680c6e0604467256477245307b8367ea480bec0c23b86a35c937717f30d3dee87a46283e75f5f3f78c74071ea2764f53f66b893f123412be41b12f8efcbfab0f48dbbbdfbba5f899d0a27ba656ab3d4f16532372806f5197b9ee60dc89b69711f88997c8c745e08fc4d9eccef93a7cc31576ea9cd0670e2e017ac84a8b2aff23b3b240e54e27046cd469a25cd2128838e8bb54a218ab7a09e0aa409576d8de1d68c30228151b86ae33a215674cd059defcde2abd498c71fb1a9a2a1319776391cbe97163fa15232e47d7c6508756b25eb81563c12b17a3477eed186bba073571c1e766e54bc79ec58a23b7a7f8d611ff75b081659b59a4486ff4518f571a8123c6545d17b200b75618a7948a703b46f375837b33f3c810b53758ccb5a76b2859709bc23da25cac032c4153fe1f3e1643018b41e733f285adfd6c392ae62d444f98100b6d22b8c3240d21ace2d1f71870eaad62cc139208548d7739920055a703de7bb49818faac984b8874c9dad3e61bd2414f95ea476e43a4f2236a65511b430345e4eefca155816af2bc16229f41cef905677a5e786d64a790bd1b2ed61698b4f92a040fcd5a896a2de9349739d5b6ad2595e028ab0ea570b21ca1c754ce95a6dab084b4a70bf18f2c1953d698770708b1c42def9becdcca4c06f3357205460bfcd901a1a0979c4dab3bebed5bdf4bfb2b7dbc968adfa08879f3cbfc96a285521c4d8fc553eb31dd84afdf382b4f5a86dcb40db0adf644affa4387b996601d657647be5d384801ef5d841da65c3fc065cec94b333d5ad8a03b89db3ec1c4c21232bfa899cc079ff59df4fd78d7216386b88e462fa40c8ba7b4a30e61f0f0fc8cc4b73cca3fa4eda9a84de6444ec58e6e338ec1747fb15ec40afe8643d325e3fd6073017f0855ab0d994ff728fe242112aff087133da467a437f29c96d0f5a9c7d5f3e158a7c9384d10f4900f015dfb1096500586da61f10183b49188971c27e24aa620a564c2128c828653981fc5e9f034e52d9cfe6c6e222d50c0cde28d2206d19f723a42f88524e7b98b9832c45bc4194ee070df3d9081639f5b91a6811e9cd52025d2d259720de8c4529f311111d911e2c0a6bf4d5294e2841b819e5d11b043d4b0844f5d6d4708901197b5804f4756917588b02d97a18616f43e1f88658cfd8a863a01ec49bc3c8c833290876039da4b4f7d48d68bfa6140a6e4c22f6503d5b15a03bde980d07cf2cf6a4c6cff391abbd24d494e7201feac8c3c4668fcf9565a18622682be0884ad162589b06f95c288ed290569cc527e5425e31d2a5bec58b53cd7ef507840a4162e3be4184a3f496f75049f55a3491cf8b1a1ca017699fa910125cf93eeba2bddde17604152f9d368bd8cc334130f94bbc70f88b8ef9178332b48f8a4c38f60c21778c08efaafc4850e0b819e68b84106e6b1fb2c99cecf181dad6aadc3db7ee7a84efe5d0c9d42dfcd1696a2b7d0eab3ebb258768ed961395576d5e84c958ca2112623875b7d998185b5844901b6a1609297410753f91e9a94361cb1617ed19b9f8e272710a1ed0981102a04540872ce7765c9db290be76af87d0efb2b0aa928e85a54be50626b6181b9ba7526a7d45e4ae8aa828d91dd917ad32c319e5a0850abff1c4c8f2d690a0fa46d98c299e91131279ad55fefd96a6031b728589c8df4c3e097568eddf76c40a6cad256d3447942e4b0218958d2363b3c38ce5b6020df4ccfa1ce2fae3892b343aa84251d1ac480f4d218dc481b3d23c49036408269c428eb5c399b8696bc079662b0c143bc562840b41829860738d3ecaa3092aa46b772cbebcf497101da277be91c042174423a6a7538368083044c27453a7469c5b2ca7c35001de8b6ad60d2abc0b3015a1a2aea46b71af2f0509e61201830f9aad4919e75c0e9aebefbf58ebb7605e942c6b210cf9129b434b999f4074a66249975cab3c23e9dfb30db820d7e629bd8d14adfb0c32bb144d2d2fa066ba613c699c469b90ace8e6d38040815ea69e575b1bc4de766023fe3b2b842a1a7f77532539d992f880725cf03767168c21ce1f4698e0cf5888c255a297731c9756795c5cdea96f97e6f28a741e310dd0e841eeef5debeabc71081ae5cc119b4484b5ac841bf61cb3da27b553f1db4968ef64379c46149ae56159243910f9c30b14c9cd1e7196a4d5bf18950333e8c7622a82d9293bb4e615aed431b40d7cb30132fcc8b4fbc68b361599fdadd228fd701ca3f44a8538487150b9a543190867d0c3b91bceedfb0156f4b020500a02125aa8a7766662a41a32fcdd3bb21a58d94fb69470f37f7e43585b0bb5450c0c9da0b88e40df21e3a12e1b11f17e223ef98829854290c5eb9fe265f860c3ec79ebf78a6921dd743daebbaf151922babb4d43fb80164937be03651dad2692c996fb81bd8ef6cc7b697d9dc47b3282a07dccc95014ebd3749bc4fbdeef468de8a5e9653a27464b4fdade2cf8daa1acf0df840266549205902376a65f295018b5aab2637a5362076aff10cedfd0d788b503bd09413b83b476e06510b422d825c62c3870dfa33aa514e4e791f3b64e293f3451900e98c5807c9f844cd57607d89eebbe71a70b85197b206ee6c68f243809488281bd00ee03758334d8d1736c74e7de7c46c1227f22c4273ab16a1f4423211550f8e96b7265af7359478ac550937c86cd2ef4f3cc2cd6618451f9c5e8c01a879223118ff7e499711cdd1f0731b70fdd7b5739cd9897498f716738b038863a50b25f5c6150c27c5536615ebf828578a29d23adcf516f836e14772f93ee6bc694f69a845f91b3bd81333c97c907c8c885822518e06fd035ac09e05a7a3706d62aad3466c923578a5746c79041029f45f99f04d4fbd003bb6805de9a8576002867b3d4d4a6af0197734baadd2ea3e903aa492167acc9259f227f84116e45d529279031316f794cd529db374cb3e59917da750ead0f3b8e9b68de0436595ec1a214ff3d37dafcf1789c65998c049722c5a4d95db7065f97b69a7e5dd57ad1e6ab7f8af21d4d5f5dc042aee323187c7e4f1d82ce11472fbdb1e2f978027dbd16940dbdff59a1eca22147fc79cf57a163a8e877d8723423e27e6c6eb13fd7f68a5ecd9902e38d0b9e6866cb01c3bf9ab89bf3160ad2dff4e9bace58f929a8676e509a2bdaf6ff9fc27a8b78971447af602f70e669ac636e62d715b541017ae194964ae5c2dd7157a8572f2417c9b89af50bb9370e87986bdf96b52e2ad0469dd3db86bea32df576404da3f3c1f5a085f7be512b24e0045e11393be74f98d2660cd5c94589360f152af1cd8a46e4320d1e885204200994515879d601eaaac3fe8c518bbad5b893b481fa43261a3cf89e14b9e3b8a928055b8b53ceb6b5e86bbb20c76ae3d9cdd002078952ecd4b89acfc3465320d6c75a7cf815dde49c3cab279fe8624f8253f98cee7519998059f6d514bac27adba9fa7b99ea998a6ee41b3631b906c532d7be0fac9f2ed29c31c99e9f30edd132f8df2650e7aaa162d21e2c1e4e2e9ea81c6c085394f7b9d706adbf5d3f99a0147e9ac7e4070624bc6add5b9f21eea422ecc48ae550b27ff94322d174b4e4ece165ba3c02b75775a746ebd046b870be555d1b636acf23b9ba4a5d7fcc7c297fb6c17356de085ba43c28208d1aef0af1488cbf65337715646a7f23381f54024f8bb9bfe387c422362ce92af0e2e2de5ac1a84f75137cd7c66ea4052981ce51a173afa843b8e7dae610622f50c51756e567645d189fdf3a5fe0fafe1d2c56b11d531d62679f10c61df024814dfd95524789ccb24469ce1280760a9bd26fc0754a477b498fee0a81c53ff1cb26ecb5f33ddc867d0fd12a41c787a5c5919e0f25cbf221d67be6a12a4b05adf4dd869befa1ea453723a4dde1e3796f2aeb3418d33208b9e8923bbd6a925ae94504a98c48b7d6127a84cc4141a5161136bc54d0dc5069af5416d383a8d12783016990d706720707f85e8b4aa178a726328e9d5b003468caf3b8cce020dae4c7196cea937cd1170f3a4049db8906257e6808ea8e0cf16051474aa6d5db0df643121c991cbfa7c0291f5591b572f3e8fe36df0a72cb4b6d9e69db7f32a1b5fa21f30a99e7742c2ccb4a1c850e19cea7e11c394c178d5055d15c0b490cd15980b97082a1026438dfdf962f14b49edc5654f3ab2a972dbbd0125e324691b711cc37b9de1445ae644f02e4a618a70248b3232afbb332b58508b7068734f523b6cf7a49211e2be59f1cf3bb2ca8b77f891d76fd3fdd820ff9193a7a5bf067e39d55814119920b19a3a8fdf5434fcd901b8565257e60e404ee02617f3d3565bfcd90f0f16a3538d20c002a7ca78580f645b75514ea4bb5076de34a1a8e05657a89830f40db422bf921d38aa287489a3250d922ec8e620ab0414d48cafa09e28f54a24ba82ba30d37174ef53ab0249f3759cf608797cc14c1ff88be8ab0580b8639eb8fccbe8d4eb298cdd5c6bb4d6473024af389685110280d6b3a39d3395132b120482b223f1040bef91ba9a51f9fa287a49ba5a13fd591f6bd88d3743d68a7b9300a984e0790aba471a66ffec552c23ca2fa118d6a5b987910706cbdd69a486b7bef9da44c291e06fc063b06313186d8f7c3c418d42cc5c499c730f39eb731a9598a7df8b11e91490d8d09bc813071e63dec391613675ecff33c94f0610fbbd94c883d6ce6f5d82eb4338fc77e3e3fdf6d5e4e9d836a937312a15ce0d7796b42a75befe89cec797cd338f43c7e3d3c1ff2bc4b64eaacef1199d42cf57c90c8d43dbdc5d455624f55cf5550f3fc27edecdbce46a82f7fe622942acefc9bc6d7ff0c0afd7e2ace5c54caf23df84da38b09161969e8c811f5f36a967c9e3ea55f7f897ecd51a7599b4f4599a37f934e3427f4d3c9c70728eba7be8f8f9028b77ca65396a8a5c71f91871db25ece66f5f4fc4d6e053d8f413e7f9b4eb29757b336e98ea74e198c424bf5652293ecd52c057d159964a20cba62da216ae95148ac0a9fc7f06fb24d28cebc6f1a6515cff3c823ce3c51290b4379393983d223fd1ee9f37cd0579149cd6c845a79ceaf6d96ea07894c136ad6dae480ac07e5e5666dbe8fe5599b50558c21776380bd3883127bd8c744263533a5fa24f0584528de5698383ef85085c33139c9317c2cf561e26c89be12cfc77efe9134421ee7135c94d8628b34c418b05411479f8a33a5faf4617f9b57777a5ea278ac17b35ed4accdf7aceefc9b0775a78735e57856334bd2e64472f3363c6b722a7cc970b3a6356b73fe043cdea69607d4e452480bca44dff305439995eefa7759647fdf161b0c7b4e203ae1ad714ddabf67ffd30fdcb3b945ee98bbb9bbe7c82f94f6b21074a3adf0e94d2ef2e9799ed771e9ba07ddbdfb29eeaed5f99c1eceb1621bd778e6bc5c18267415f8caddf89c2012286bfce0d75b1d7716ecba06251465a2972ebf2392329162cfe3b96e0c24047ad51b912bff25b6d0f7babeeff55d650c14bfd7ab07cc049e87895866507a847d8f30d1a98e3cefe46316d86b01c76623dfc76c8f12b6e4238fa885069e11f672f45eeffa91d9c8f779ef1788e27e1df0142d6575326d3623231aedffc5535cd77b52dcaf0396a222287a9ec8ffe01b0aca2370ceb682f2211441d99c70b6d4bd7f27fe84e2d177bd7bcfe36ce9fbee3fb1136750bed777e26ca96302bffb9ec7506acddad6ace1a6d3cc9a35cb238fa0e5f1850b5fce3557de239226614888898a614191b34979141027e02c9146e825411b13fe98f902ce0e428408792e4fc1c9f2469d2ac8d00a3206196974e9fe66f7c7f31596a5e5237b240a595a0d4ac1acae7a66387966e5b1362734ab2bf975df045dcaf3bcc5988d62939a6058c3d8e7dff74d30ac611873a344886be8c8a51131024274a402b18542624262f2f2e87a8c6c7f934878a40271a344488c687e3c5fa50dbdfcbe3b12abc2fa0eecb67d31900868439a529425b3d6555859ddf7816f5b625465456330269ecb23ea9ec2604cbaeffbe6ac9d6d09cdf57d0814ab955e61dda615c6e4fbe073a254ad8dbec21ac6c21a86b1ef0b6b18c65c9fb4739c9ddd765fbbbb7bceecce53745dd775b3737767263289a6cfe95ee7eeecd2dd6b6766767707634176a89c6efb6e1d13cda75fdfc9cc36afbbdfbbe77ddff7cd2c14fa73b9ac9cd057adeefef96466ee84be3dcf33a27d9e47f43d337722d3917ddeed664fe8e3c3fc7d9e6ce3fbf9ece705d5cfab554be8d03c2be4791e1da5363e77f7aeebdc3bae1a92a9999e85c215d84c347b78320fbdea6ee544ea935851bcd9f9fc1f9d4fcff33c22666666cff33c8fdff3c0392de3d073cff33c9b91e7defff03acf3dac28d2ea63a2d9c373cf9bdecf6fafba572d71b7799eb7867d8ee7795ef53c4fc9ddf0bcce465d22129d7be7eedea921474ee75e97eb72eee5d49023a7f39c1a72e47877c7ebbcceddddbdf3ee4cfa72a7b6ad7aec2a3933cac509b5867b97f32ea7061b723af7ba5c97732fa7061b723acfa9c1861cefeef8634de66e29bdf33ed0f5a2151663201893ee8576b88e3aa52831295a563c55575655c5b2b4d0482c8915e4f323036b4ce94a46c124546df3925268e41ab906284868c7acc7078ba1c059ce63636267cd28a50965a5d34be2244e268331f15e36c4c34b7229f06f422e3f90ee8582e72098211e5d7dd14a5f3c916a1f4d79db73b7aa575de5790ece32b14e43d2e4c7608d85c5a4f301c164566214b22614d4cccd35726b4b6e493bb8de9a503cb69f62ed641b06eba86e2385a6d7f41a1e3c66104827336a424da85966464da8324ab3165454b7fcc5a5eda6bae53797f65475cb5e5cda54d52d63e0d2aea2bae52e2eedaaea96ad2e6d2baa5be6e2d2bea2bae52d2e6d2caa5bd6e2d2cea2bae52c2e6d2daa5bc6e2d2dea2bae52b2e6d2eacb8b4adaa5baebab4bba86eb98a4b1b03d52d535dda5e54b73c7569bfa96ed9cda5fd4575cb545cda6054b77c814b3b8cea96a7b8b4afaa5b96bab4c5a86e33b8b4c7a86e31b8b4c9a86e2fb8b4e154b74a2eed32aadb2497b619d56dd2a57d46756bc1a58d55ddda2eed0c54b74897361ad5edd1a5ad81ea16c9a59d46757be4d2de40755bc1a5cd81ea96824b5b8dead6c8a59d55ddc62eed35aa5bd8a5cd46c7e90ed4366ad835f937effa34d28fa113dbd857fe38f2a3714d5862608241022f5c89800b10b0f28016aa5071000b2ba830e5968294063060010a4800020e8082010a70020106608200a204000a00964a20610425114200e183271e74e08483264c36d060bc4b32c0e0022549922cb0211d2139520105468cbabbbbf316a28798b28d36ba8d968ab9bfd79d06f6647ef77d84950662281379defcbee979a02cc2b48e91684bb848ca2ff2dde9b74adfb93fb826ff06263523c9bc2542cf13c3252c9b4598d6305b9df0f6dd298e725ab594cdb2a54b9fddec9cbae1b9d7759f473f57d7d53bdfc117ece592b4a63d5c84f2e7cd8a48dd6354bee47c5a292c84d5a758aca7613c62b33b9f863c3ddf13fa589f9e1ff1c7072802a09f20a2202021219a50d08e1db51d42329904b21d43434386643c7810e131349b15cd78f4e831418f990f1f457cf480000223087cfcf861e40704ff14fc0f6b2bb02f8a47441b410448221089888e88220002040908519020b620408408b14048101a2d8926a4564b52a349208112096a43865c304402cfc31e17ea2194ef44ba6ebe5b90c890a20c8a883c4d2075e70445628a4c51640228a30b1815f912ca77235d47c59d6f04340a75a0c0df29303255010554472aa802c991aa232456201d5d6143c2c2025b164916689124690b2549b8b8408915061774910106185892811777c99bf17ea1c108c6061a84c16483ab264cc4e0a0c9184e3820a30327703ce8a08c271e98f1c1933340f8002b041032204208682889a0811194d22061840d94400207964a5003004b595000b04600a0b0112500710410458e0902e8c0004c6883000370e304026815e084370c5000385030c09c03a0400701078823010890430109f0c00214b0c58005ccd10006d491d2003a529062e796c207a6dcea50610adf5941051c0b2bc8e0001666a0e2001aaa50c9d242951a1ed082162b0fd802012b4e2e40c08608b870c395087079e14a1709bce0050609e0c004430e31306189e1cb9d4f9bb0e8806bda41061c981964e0818619c264a1414c0d59a0b4d4d0c3162d639cb690b1c1a9cc0d36f8c0e506335db8fce0a54b140e5e80c80187209e7210e2cbd3103a7c39b3830e4480d9010d0f6072617828424c1823a0c41cd103549a313da82133660265c820e1439935667c48e207331488faa1024044b1090208258408c2024308b1c49921da107186093444349143e34411b9278c28028a238c88224de50976e823604de6953db8b0af144770cb59344111232314547004c91192cd82a4244a2ec020832577d46003264d3870d281074f3e00210411944620a18425004009401401983000029c500003a07000042440010b604003a4a4709ba2c20a2c38804a95161e6005022e44e0ca0b12808129062c4d8d6b197a86a6a1b3740dada5b7b453dbd0373497eed25e1a87cea19ffa4bebd03b3498e6a1c3b49886ea1e7a4c93e932ed439be91f3aaa81e8205a881ea2cf34118da6735d441bd147749a58db400748e5f3db7e5df03ba5ff7bf01381aed755a8dc29ddbbc0fefe16865ba1fc39e9eb6fe1ed34d7fbee4b6cc1656f54ae07de1c67b3d96c369b4d4a29a59457fe7dd3a960a482d17c158c5430f2e20ca83be4f294196c5ccae35364bfdedfacb838658a3f089dc856faca96b31bec9fe24dca7ee3ae053333b333b3333333773a5871d74d766766eebae7ae73f7ced9d9b99b13092396396d8eff9c57d8b123478e159e2a030e381f04255127250fa919d58f35c5c645b174834428aba494a449299e73cea9a933da14c599a2443690352997b8a8c2284a92f644d82f9334d9a0d1cb967064514654196ba8c018e2f6b84cf585d4a53c332597f7f425fffb66dfd366cd639a3f452699d734be8d041ea9c87493f9df6cddc86307fe1229edecfb28b3e67dd3e8b2b34f6c81041e95442627de38e37f5d89b3251c55c3429b20fff54bf3ebf357f92f91a9aac55907dff77f4fc599d2eb674bae7fbdabc78631f95cbf444a291ceed266ee1903c5f073d92bb3e671d67cba220c2eb25c0f4a1e71185b9491483d4e0e430b2fcd9def69bdfc4a61dfd4afc6c5a57e98c8c4d474a52a8a94ad2b27597c05fb259e87f53ccf7be21253d087df34de786be61f7b1ea59db9381b413eeca1f407f7d6746e3fe801d9858aa83454fcd0c595623b95e924ae1cc2ef6f2bb6e02e1839a693cc4ef2b054124d029f3c37d9fd6e61d79bb027733a7d2394ba231df71ae7b3ace5f4cefbc029394059baba653d20112e92df31618532d1cda9cbf61dea896aa8eb346957234e727d4f100c4399ec1f8b6fe7248df62f9365792e97cb45eb64e2ca2a9db0b0b0b01a8b7e7d02c13094b9aa8bbe948934f4f2a97c1e6b2f264549ffc65b208c8a4d66ee36f5584fd3794a79c8befc59a59bef6f9c45c5f924aba4d4d02f99435686269c35d9535de22cf62c4536e2a409e34002512130e8a78707f6469c247b17dd41bbbf4929100c4399ec9f8a4de49589b3cdcb29ec2c4993bf43149508c127e992469c249b8986aae20a893723d7f5af9732834420d187b3c450bcc9c498787b176751795d4fad1117b9c4998b612eb1c9bc3723f7c65946f6d64d5cf9df7472dd1b67dd8c48ad6bc4d7ec90426d384141444e6611071c4f23953858a08ed448679d39b8913a9809e046da51255181a7917a5a4f1880a30b2a46262c3ed6185922c122d4441d4ae046ea22838e0dcc31d217951719a81a29f522aa0aa79156aa3590801a29ac8a306f9c461a0be3ccd59b3284c88d3484b2c269a43d555a5983971bda8cd4a74a2b6f0033d566a43f55daf6a2c50a6aa440555a2639068e54165b231316a6630c183a46352278995822c1c27696787a625433457653278bab910a0505350b0d6141868badb1a9b1b49c5274de2a7ffe94190265a2e99dc8d414b03343d588e5cb1a418944ca0b8437f082b3366b73fe1499769041c3184ea38bb44e62ea08ac91ce9601ea8ca7715a1e5b76179f349ff2146fde688db4e1ecf0343a9189026a6434b49863cdc85ea8c1658db4ab9de53034f084d548bdda5926b6620b17592396971458d3ca664b1dcf160b6c8d1dd8d9231ba6781afdfb0ec8a00b041f9c48af07c16f4e127611f9a078035d6e6f4d13c6840a757710743d88f4025dfcb2409dc4254e9d51c575f166b3048d3450c1e1264c8479b1dcef13dfd288e6cb3a21e850d0f1191599e01675443ca1cb433e095c354a8309e1711ba4c72d901fb7444111ec1087ecec7dfcf80181f511410f2033213c6a6e8786b89515b9dd51c4ad9091ea3688e5d1e74a96cc9a5ba0fb592512897c7aa4bafd71eb73ddf6b80def74cb93d434a9b36c01172555b754092f91ed16bc605a2592e6d6ddce25d52de0a20d24136fe2512d0e40f75707b5031813ff396b375b22bcd96c4be4b741a844d6a407b127e107defd810f08402108892053e2e1a30704ffc3462012050122a4469380c890a22213185160a40224478e6c481624495282c10519dc25e3061a30e1a089130f3a7802c207212889304209242c410140000410c504020ce0040314a08b1214aaad8b94038049c182ae262500cc6d0164a634a08c0af24b8187156a2b619ab48069720aacc9df166d6156a86efb884b59a86edb884b1d50dd76119752a96e3b776995eab6d15cda4275db445cfa80eab6cf5c6aa5baed212e854075db425cea4275db415c1a81eab681b8f44a75db5197be50ddf60f974aa0ba6d3397c250ddb60f973255b75de6d218aadb26732996eab6c75cda54dd760f9736aeba6da84b5b86eab6c55cda3354b71de6d2a6a1ba6d1e2eed2cd56d83b9b46ba86e7b874b5b4b75db3a5cda5baadbfe72693b55b7fd7469db50dd760e97f60dd56de3706973a96edbcba5dda5baed2e97b697eab6b95cda3854b77dc3a59d4375db365cda4fd56d3b5dda5faadbde7269eb50ddb6964b7b87eab66bb8b4c154b79de5d2e6a1ba6d1a2eed30d56dcf70698ba96e5b864b1baaba6ddca5dd4375cb772eed31d52dd7716993a96ef903977699ea96ed5cda3e54b74cc7a56da6bae53a97f60fd52dcf71694755b7bc75690351ddb2072eed20aa5b96e3d216a2bae5382eed21aa5ba673699fa96e79cea54d4475cb705cda68aa5b7ee3d2ce55b7ac75691751ddb21b97b611d52db771691f51dd72072eed34d52dcbb9b4d554b71ce7d29e4075cb6c5cda4854b7bcc6a5bda6bae5ac4b3b89ea96d5b8b42950dd32072eed0a54b7bc814b9b4d75cb695cda4a54b7ac814bdb02d52da371692f51dd72062eed36d52d635dda4c54b77cc6a5dd4475cb665cda4e54b75cc6a5fd4475cb702e6d28aa5b26e3d28ea2bae5312e6d29aa5b16e3d296aa6ef9ead29ea2bae5302eed0b54b70cc6a51d51964a18810a19618a4c0385301207ccc1085c94c025acac586a49c90acb8c0fe8a787ddfaf77137fbfddc7d7252c7493ccfef7152ecf93f4e82d5245a39097c7e588c87cebff99ddd9dde9ddf9dae3befbcd13b937c7ebe9c9c14fefc0ae3a4eee7f78909bfefc702f9d820a11db2211eb31e3e20f8c145f6878be48716c8060971917c98ddc145f2ab95d9211e5c24df6567b6870f2e82808b7e70513847e8a325046f70ef7c6e3e746df5d33365403f3d56640f40a1191158055cffd9e38621c82008be3b7844d63e6ce3e9b072b281067707ef6024215c936f346b42180909c968225da6f20682408420d8b91e04c5255cc27ea3b9c112ab1c6067c8807e7abeef6fb0ef6f3c975fc9f79c844e9077f0e922f2db0235e9a16fc810280c7f5ad18822b04cb907082491be9f3d74a875228f1c4ff4ef867bcfa7dff7cdceeb3a6f4eb71fd0f7c942300462d9114a248df6830c30743a5dd7d3764f2783a083a1ac7560083e4f36e19ca1ec3c91e6855606e8b5e7303dd91e967a267c61044366c92c999925b364e6efe9f1f1f9f9010262212166e6f7616666c92c9965b62b0b7213e4e60aefce095f96b649abc08856011f3d61da74226936224fe4d195bec2e16433f7a4311294ae4d6f9c6d2d1c15a564b9137410fcc0cfddfd7ae715d40ae378c3f7e5ba9e573d8f525707e5d2939dfce4d7e5acec48a4be0265cf798393389136f4ec2b27b0cec2ae14abb4b4aa566b85a083134b6ba2f181adf9665e4d2bf06fd3ca65acca772b086a69b95c1de875567ee12227a943c3d045dac0a38cc2418eb9f3a5d60eac500d23358e6bf3d34469219bbaa184e2ee88229c5ef0d1f7f367183c31506792c1488d9bb54e72a58b4c99a881099b6df9ba6ed7755d57ab7ce224d95ab62f5b29054b279836ff4a27e9ac2e327f5291c4c69dbce5e562d157f24a369cc6126d17e1f7b7bee24ec6401cce09c411e1fe65681cae03c12f8cf5f490c964412ebfd86f8f6060da1ce2a32b9236e9874a12063e4ac2b4f936499b4fc5bebaca5aea22f33bf1939fccc9237927903b5f5ec934241288c5664a4581bbb2ea4e09e7ce29dddcf99fd7754f9ba58c755d8fd2446aad247cd44eb491b469158b6959595ddd97e5d13949def93fd2ba6e8f9ab07b29a5c4698562ccb0e6b66d20b8288876828fca740f4c9bdf4e5dc79d6dc4937438a14c82651a4e5ac349f226739cd473e7576935919eb8487e895ae2f670e7d49a35d87d59394eaab0b55aab2b623271678e8be6b3010317c544ea37e600e74f2d31c29bcce5e60cf3ebb84eebce032ef4c8c3febeaf6777cb29a79c5e85334ff7e979288fe6f7ed5726692e0508ca99c691c031c8e5a90e7820ce9bb0839801e68d335371d87ce1431d5f48b065482e6808301b000347ca0d38fe812b26cd89ef14bc9812238e1c3b5e9cb1f3459d26a82b78b89ac30cac2b92703a6850307903079c115b561c78c2ebd47e38a18a34e83c3103163f40f91cb20f813a1abaaeebba2ba400e38501e99b31a8cc5c5dd7652a33767290800b194d703953070b0d0c31856f8121c7240386902cd8042142881c5f1c088c1a0f12c82185a03d5138b2b429734689ab37ee8120564c2068524b1a566feea06163ab8c91d11b2a36535084e0652a3650c83ca1cb546cd0b02923ed0ce94172d18137f241f9619b3754312e6f5c5bc891f2b6bc19837e3d2e538959e2d2cb5462d208d1c1853b45a46933831923d6c861f485c75185c5962f6e8ce12285865167bbec4ebb42cb04be2d5f842133a9c4784165460dd7130d5da6d222cc7d5da6d202897b7ba32e0ca3967d6c847e792a8e5518278b2f0edbcae16a035dd6b0628d71d28983c5957d5371a6be08c3e9727dd3f5bee9d20f7ceec1e3846f078931263227c021ecdb5dec56e13bb7fb5825e2c19e4c97074d2d806fe5bb9ee73df1264d37d9c47bffe941d3ad12abdd7f6114c19dc738893b30dbdd93bedb2332a4e63fad4c3e99dfc4e7ac83babcce05ba3b0cfcacf5afeb5ca0f884ef5cff2a3ca5bb4feb60cba9efbf8f69a44d63bf27ce963cef9bc6177fb5209bf0065e998f2542975113e1105c1c2da5a858d162bab245e3d0927ac24a85b3a54fc9c927ce48e0d1d634468d4c3d766ed6feb6992e428544514d59dd7e6e28cf93603a2bd75abdb50667e6de9a4e63cdb937994576612c293d89bcac89227a0a2594952c2325a3726fa454d59a36b989c4e3d8df663a89ac52a68bf4f3982ed233694796813374fb659544aa61f5d41c3797cdcdb1e6c471a9141d32edb3c7749996c2658143ceed3702967ca8040c1d28a95388460000002011400083160000200c0a070422911c04e248177d0714000f65783a64523215082391381848510c84530c432110c22086102619320ca2191b5000fe119b2f79ec20e98583a718e11691fa39be5f84663d1e7d5568c5c2e90c46c61fc91f537bfc407132d6e8571cb37ea078d81a85a8fe08d532b182b5998d69013d9c71789771c5122b9c17527f21b526f5b4e6b465ac4ddf395a537c3198b9296b86b57126b6c05b3df56f9fd74c84817f9900714e928259802658f5a4ff6f9541ad2b84f154e13037523a695e9f2754a5deec90a13f335a901a3be75bd0f54a724f810c69536d4d04212658b9171312f131e35024d4432e96ee8ad01704a843c4b94703ab55e0588fa1e8a38756627c2b9605bb7c5a310961b7cc6c6e78494891b7c06226f1df093469c07ccec0b16e634ce30f42f44c659e1ddcde70e4e3946f8b495d6ca37752f85236b222e838da1db0239b2390acde82ba8f431446d84acb558c7d2889d50fa64209075818a604537fd45d2ebd4fb748845fe54049309847ada679d79a6003ddfe933419ef93dae1aa41667381343252e3fb92ce76e1491e6b7bacc8b7393f999f6259c70412ffe03d4f3515225a586a51d6feedcf6c36189bee722c5839181998b5a1102b6e411e603f7f0e7af2cdada2827c62e90268448bf44887012665473272323d353ad65e207044bff3b76984da6a5584a625ec990dad613f477212189e01be8c37d06c80662b6d2ae4e82c990bd296c850da2f6ebd3ee6b3229660410843389ce78222091d201fcbce0ae0a4e77d2e330c15cbf9babef1e4c62485c2818e097c08ae0b90b40c5a02d552c262f557b55b37ad1f6289b77dcc81ec3d8c4dabfe0870de6b9757b0a7cf1ad492d8001ea9ce71494afe960758a8ec0a4d180334b21db9e049b1be4dac63e01bac7ad1c96e2154ca89d4d63d0775dad7ea88266ddd9921ff03235860c870cf35dc2553f666e9f5a1a359c544021f178fb55e4fb2af9a357fb5049bfd32d771df33cf334f0fed50f2e765404037058271620e6312754c01d375a736cb9e2d193ea3fb8e1776a82a5f2f9bc248828593022cb73661060810d2b1ed123fe001af3340a59303ba729ce3c9ca6ae97c74964e9d87fbcdeedaf805999ba5a38d6a3507d9a0d81f747e7a67b1cb4309e6bd7709f4b39176940d24ef7bb9c43cc8c240fcab1be31190c652caecbd84b3ef5f8c071c7f4338422e878481383f05dc987019d0232ac0856628df9a39c756dfa50b696367b4da19a9c14636c2d64af06ef86828383b3810c10601af19e6e9e9a52e632edd6502a897339fe73e4ff7f794598e517d9a8980d3bbf67959b26054563f2c0a90fd2e6caba6eeac97811db36c4df002c306c30c50868753b90adf445a62d87c71b5df90056e6506f3fa22b9642237c7920c04f2ad66a1dd70e87efc0e440b2b37748938e61686ebcf81f62ec716c6a449420785d09109fcab8211d1932523a47fb19207ff60e606f1f0ab329a71b13a23da45c5472f9a860dc1320e71daa154cae3e1ed0c26f0cb99c080e91e746fa3f99c4781d35c713a4b8270a16b712e3424e1e4ac969caf2951c96bac26d3189f5db3db9b95d41d4b0d9c465954e5a5a369035256e239b3153a57238a030dd19885b3501f60d45fa02b4cf21e2c4079808dbb9515fd47f4894987c633d1ad70c06862f42decc13071533f2c63a1c685854cfd38493f6bc9e5349b871eedb7e815f5e1520a3f31f6edce2280bc18f5922a8a170b152a5624664c55f1bd2a8c718590d154b03d03371f2f34837e1a926e954fb68cdeddc55ad52aea3d67062c13655f44d7978a1483347967030a30163b86a010ec9a4da939b3b4cf66d5ccd293de65caf3be3f1e2343a37fe7fdf05125815cac8b55ce2a431633010d481c5df059d2be3de0718ec276d215b6321612428f87c7c4617cf6b56222519974dfbee97734e52b1a3890401833e45f2f9fe06046cf8b81ddf2be892545fb65119d7887e95a257dc4ea2640d781c59ffa767c1e10be6fe4da8f33798c84612ffcfe3548d501d4bf576bbdefdbf43fe1f77dd4c718c8f755b895c6365ed1dbad8381a20db3ce8325f4787dae2bd8298b08eeeb50c4b25ad4e44e2d97f771f81680b6ff3c468f26eb627d0fba81bf34dff129a0facfccf1b71d8bf35acc5a75775c4a98692999208fb8f27e6ac4337016a171b90f9466e4f8e9ff72e6f9e3907eb2f29c5b817f8c01a5e11bfd238ffda9aaf282e86f70e87f6e79fb036d285b5403785e019e3e9f019ece2a6c0e59f8bb2016025be3ee1e29940560bead25b3b381d3919cdd91103a32f03a6a3546e8bcd9115f960cbfe89ac14a099df3f3e7f5f3a005f4808a3d26d2c1cdb5f0fe3a6122687a55a1293b2d72b80ba563dd0b9bbd481d32572cff070b09c5adb6e4aa242db23c49688e19cf0c895b090a37c160276410ed5723ad5afa835b9f3d1eb8a3df8691871fe4795b9968dbbb4decd698d8726e398ddb84634e415354c3b88d2d3459d23bf03de538685e1807535aaf341b2fb70919a90adfd36d82e1a88a089da0996d824e814511a080054bb6b2d4ac78215e90654433bbb1b1fe4ee9c2064ade943968e0d4cb630296560a2c4fca83eab3560828230277eac1d61224ac55eee676f298149e6772b53266bf0d6b47359bd004df6a2c534fccafec66bb18a65988d6ac84f566e2a40121162c11310c0cdfdd645f69d3c7701947a82116e12a8a94ffb363bf0e9b30fef63c3acdd4615e567fc8c6939fd8e3950f0fcb341080306317c8d97d6a716da352a5de4ff3794eaac4a0343a42fd5db26c349bcd80bd0124260d57952ddad545980fea828cc28f0265267960d8f89ae3bcde8151e1812e8c5510a2c4fd21aecd6e6dd6234482f83a5c1c0cd7ba0f6d7a506b8a4ce3a609b4628c9e8a2383943995df99fef146ce42a104db1061e4677dcda006328ee7bc2771d29ece5f8971c56300fdf1d3242868c845974b551ff2151a8cb280f0cc2075b8dc2d8a5806c370d115a20a9c546f3e9d998b7c90fe3306f558c6454d207ae51b0a514e579f05553c259a83217f068c3997434c6a307195a4ec17f5dcf04f137ce10297df7114f4bfbea3baf0b8f25042b84670a94d90bce6dafec1318c481d7d25e4b155181af2aab2b8ffb1f4551544d5fa5e16c8141d30cb5fb5a9dd00fab5f9bb37bc658009b62bdc7b0bfaf34b4a4e8fe239ead0968fab92a73050ab64d521b0e0a4656ec944bb35138a1a3e33f369f70980feb26c69a124c407824ebc035965d114ef6258d81175d3ba17756db5d69e5a0b427f039c7640f8541b8e12123d012837a8ad7e6955bc2bc56bc5826a4538bdbeb66114e104cdb134b3e0e15d7f58978b85f5c5d556515b54b7f52c67b338e3ae502e93c3257c0babe0b1ddd8e2bf86fe995403c4d8de3a876b92a8821afe47f812138cf3252ec9be34b3e0a6e07f48f6d1b881316c97b81dab215dacac75b80e40552c586dd1f2ef997adccf62932415d645de4ae27ee25beddad8a34e707de5948c2d559fd4b974a0644da4b64b54da45f6288aac52d9704c38cd364e5a46a25252cba012a1c8c4fae605165e241df31d11be60f7f0d71e3c981f2298dde1ae60495fb5b2165a7580d8f3b32f35a587e4034709081a38ce88be45f34b31dd85232b2b3059b10b22f9c2c50468882d0ae025615cc473d4a14a61076380000701b35e8285fa602dea80850502bef6ae6535645d258bae7d3e269941d8126a813bd24bac76d018d041a6b7258413a49f6b8a6c3611da3c7547a575a2825942539373cf188d96a5e0852e9db4acf01b52c2c29ced8f9f71bd360416c4fdfa9d4b3c152d0fb220e2b12f1bc376a197a4093fd558d2adc3e839fd6050f637276a83d7758df9a3b784cf2492ed6b2eea56d769ecf287db1c2e9a0f453c65c746985501fd6ce3498275c2bd57d2aa3ed12517af954593c6169b794c812c29515866aa7465b4569bdf04a80da439d6e9f6ba7a21edaf63a7130b0474ad4a822d850f8d1553549038f049aec7ad9245b456e8abf8c9eed2c716794a016348594e0fd48f3bbfcfe84bf82f65e1503cecb947d8cc1654278ffa7237b67d4aa97853d04bc9a736629e693d794f6e43711d02220c05c2dcd5fafa8640181540f2f49453ce9419b8dee6cfa512466834ee9e7bb937d3102a504ea62803561aa2084f4ce4ce52010c1a6ccca3b54f5015773f530ea1537f5060370584242c1d845e41a20c709e905e87bde3b6935e3b32c46a951ae533ec7a01a1e2873ab8550d5801471ed35795dced4aaa8c1c312a83251d4a84f276d6a94b9aae182e0b7ea72b184eddd286488663154d12e1697b806e8b8a90ea150c392164183e32afc05733ed8292420c87173ce394774ecd0b032405bdb7abe30529106f868e3a146836661cf7615241912fd2b9f5bfa5c1852cb3aaee8b87023f1555d2832d6db526a460982282923be9d598603f25cf6acb03a71b0d9387f1d655a223b3c47261a677d1808fff37cfb8ff39e7734d73ac7a073a90060cf4447920822c10f4e11f196de8277df9d4e20e761508747cf8d63e62461a431135d9fc4f2770f54674b64a7b003c8bab13812d367173c5d96d8395aab241f10efa96109f9d3cf674dc3474556c461dac57f00cdce6678bc99d842ceea7fb5dc89a35e9a0570ff5d9c91d7b64213f6dc672c1656100a7b3223516add60839287c3e6946b40535ae799aaf0df1a5d88dd611a8e4f5684458be52b6e15917d98e8cd1eccc47ba0cba9f1972d348dfb07c5fe0646e88685b68868bfbf3dc36b261c560ff497e5ddc71ba757a1d060dd0d3e9d8b3c9bbfbf354c460cb8c33d1571b47f97c1de8a959e53f3410a8836d7de7b6d3ddc6aee4d57c1df53fac4ba00de3bbd88ebdd9909f36636db9ec74b7f90abb9935e7296dc51834185b663a0cedf69ad03d0540a951c639bff9f224b25f876a4cb6a7964acab129a6853a2b54eb72897925b5844f589a897fafa40238d3708ab591f467fa2b952bc0e56f31b07efa71851e80e2b47ba2ad49a63265faf344b014756878b8f5fc3c91c0a09954687a2d7896c4eef4bd18951a43253d6cd1dc3ddc8a0c764afa8b65a00087fe415b70926a3a995b113ade89b8a59aaa42fbab28302538926a648caaafff58e306434092d04c233c7ebe0e789a0f1d6d9eae46dd8a6325f383d36e48ef4a6131a4fa204ccefaab8b87d7bb1c91ea08ae86229f364a39b68a58729e0ff2d352393412a547b192f4a14cb6668ae8f380e12d19325182a15add83bab2099e09f3e2dd5275f26c8bda0baa5a28b81721463728ef7cf2c90101d540b12c276948413f0474905d5e7206a8215c8d8e559865a082bbadf0d2bb8500fcba31f8d5139548bff03045de92c42236892de493e813caccb82980b031dfde3ae48ef6bc49c5c6332468f4b2105689c55132e645c9c88ea186d809a54b5742d11c32ae7fc6a0d3038ed887ab7be650817ca585c100af89aa50e25fdd3b7765e7cdde70ff32ed3c32330504e7cf6f983f369018d24fe3f7013cef17c845e5d8dacfa9ba812a72b5d9a0fbe3d9a5d47bcbf67b43d2065587e33508108bd4c53a00046b63ba709e6ecaa32afe5b6c87e03aec259f716a45406bfe56adc8f5e329f1a4b80701c4e9f6ac2a881d6dc126891634a0b77ddc18f214b347d6c692916b5dfbc8414e7c64399628e65162618b1e88169e013c8815d6ba21f9790854513cbe568f00ae14ec9bfa2442ceca8c6ad5c8824ace7f569bdcf5f2abe63745ab65149bc3fbe6c0b5785c116ef208ef017d02f5aff1636cecaa05e09e5bb522e0dbe0d1ed924dcbd121262bc9ad2a6a8748165ba15c542cbea0d5fe81d826c4d536879d1f5fe8584108012736c235bdf7927a704d5bb62d9d947a81058701ae58433890a51cf8e217fc312e5b169ded94efe6b6481aae83cd820cba68c406d27f90f8891cf5c13594748a56c67e26d5fde3d11f37910e98890daa9d4306f84d6c0e983429a0a2f5819c677b2e65b0c472a7778341b25f7f42443d38e0736f00be78c809c5e7c8ef3de915ad25268be88e178ae67bb5da915005260b9c24daaa28b84b517ca3a55d0cba7e0b3c546451c25141474f0aecfc3d10c0b37087c20f050ef77ea44268241aefcbbed82ed3d95871d01df3ac4ac58122415b720ca3115305c31c708ee8df707fa8422a20e721eb102814b232586ed9b606b307e9beb259e7f1aee62038e737fe2b99697c188aa42a7620bbe2fb6c04501a51b233698b5e5f515f191774ec0e9c028ccf5562ee5c4a4fa0f64fa85c186e9867beadd179e8124f63f7b85212459b236622de6454180ac475b59109057721a0826b8d51adaf8866fc028aff5616480418e4ed45694462b7eb5e963666ca921b3e5418354a3bd6ea488282b83b5826c9bdd8e2848176d789afd4f7e62b9fd17e2375203e3fd26366b70fca43bacac9b7d0560c57d5fb5fa7314162be7fb4ec70386974d8e037d22f7fa70e2a7df0b885d1e9a9233508e3012c0d230dae0d2baceaa3be857f4576969d40e57daf6240f54d9f37bdbab66bed2300e6574b711c8b703c6856961c039da44a855cddd5c8e8a7bffc5deddf1c7523400ecca6bfb749aa214f524530011cc91e4422f1e8584c75967d9ef5b8f416ef16af03942ada14299c3d4d70a59bcb40b460a92ad0b239513e7d48f4ebd974a4e5479d1eeb4178d41ee417c1d011242c4e17eacd8249955a4aa9f7540bc5b8e430fa8d8974bd850cd48171e1f5024996bebff4f338f0a024d13262ab3fe56807bfa9d4d5979a5f0b42ab3b04d4f19c474e80cf1fb2e8d9e612723babb57e66d6a326a0db6a6e18b48351d6fa394a71ca174aab190c94ae5d589f04c8f2d9a12b18f53150f2899e89ccb27ff91ddce3345afded292707144e392b581e4849ed43bfc051babf6463d175bc994434edaf9c6789fb9850028b8d74f431a033c8e6763268b4027f1c9108f6f218414ae1e1b1b762841fb748891b8adb421803f11c2bdb12c6a0d42e5d5de84396f6cf6d407092c3bf5c406b1bc1cecd31dd990da0f075eca171d3f0ada99d16145efc1e7da565301de1ed10a915d77e2ac5efe45430a240046b0145f58b75ccb4d98960109ad9263cfbdb598f08694c1011ac47e0fe1290b203faef9bab3fe1f9f82f6461ae1304f4bdaf6e6f949130f250fa0123b910dee0e62d0d3e0dc91a2cbae33d0efcf4c053d16d1233bc2091764ca7b85f29e8e14d2747d8564829f99310d28009c8836e39e3af0ce757ce1bf85c7238f822f29137dbcc37d3f0342207516ba79c607eb919e97a013257772e171eb84da9212a364dd1bf54d7d4e106c0c0be94eed395d5aa682a1f8b8b3421c6b59bfbf11b8bc8536d05bd80da44fd002548db3d605608e0bd664241cf19744d676dc5125effb00810ef332c47d0cb9484fd46715e40f9eb6817686a3084a3a40070ef204b109a11b326294815931e4c86c9ffaf6d0a314788d92c93b3bfca1a1e1733270a194cd274a336678e8833e0bc51b77e792cf4c0e9dc5cf643f2f5d03a93b9dc4025ef80f9443763b77f8ca09c5c5d2380f3f05d29c8c0df2d5a6e49ca33613865edc8527eaed125f0af8cd92d5c0556385ee6ee189796afb64d8190dc57d4f4b8b5f93dfe4d7c07231967daf15e21827c85cac6a977058356ecce0d1cd92f60b0700bac30df9b560702215826dd6d86f07270bdccfd475a912f6e0ec02a5935dfefb1138efd8a745008b223b1886fc8015d5718d48b860db05955b1074d92bafe5823d51344650853d86f4ff773c46aab645fae90714743de5361d050e21d11f9c4a5509ef60eabb162a204d1fb1243bfabd86b7ea3a1da566e56ae370b83acbed768aed29b3b18a45d1a87e23fcf0c550dbd663f581f34b0fd8c8c6e296c0ead83186c62840143cfb76bf7d3f2a70b7cf35b61311760ca9bd6311befacd0281ea17d8a0a6aedccf4d9255c4f529685987d37918fb2e5504d174c4ab5ed72a75d7e4a2d60677214b58f653f960525addcd7a4eb9603793e4305f5cbf7bf91e8b57e498a70834cfde331112142f6e9e51101fae817b11ae2584a8f8aa06213fb39b55027257e7c801c87fee64ba66701d1cef47e7840ece7d085265252be04958ecefe3dce6e3c29c0147d778536f985b43f85f163575d5e2b783386b5e0aea373ad7d5e83c5202e9562ef0a4de9107aa50f164eacba26e784292ea16198d270a14ed60506af0566f77b3cbb48a4f75689ec8861823376d0f7873af70016988a536dec84c6533fa55ef49e277c01b5982332113856c41fb8cba8b2817ea879a3d2f34aafcf73255dcac858771cd324fe60458d3e60f29dccdac5c9a0753a2f7a60238f6bd422853cb438690c929191a91a85529bf1992bf750c630517988ef7400ab85d6d35119be8259496369008aaa0a8af84db509b1c38abdc6d813bfba97c3f62237dc27d85030c719cce17a9fe97eea3e0a3824c159450c19dab45b7a648ebafc68328c6ac55149e703da4d0bad530f4ba28ce731296b5182553000642a9bac31709a7e39f3580e94fecafee734d05d04e2982173771a08f99ddd85506839e176ab380135759333bd4fdbb9ecccfcb4a15d5aca3ded4ee24cc6d3362e39ff3a6d688796644ebb9238a3e0b48dcbceb04d1bda2d2366d3964d99b4f4e4ed42e0554a73bb584008c9ec0a8610080903287e10718edbe0c30e7e045be3af47a372784dfba8f18ac8f5ab2491b254386530108451d5902513953d47bdc9bbed179cc8e0f0b93fef2a6ff01183a8691e15096efba1fd99295c12ef159145b4420cad3f90943d4b5aa76a425496a63f05041667c8d9a59e89d50b24ccd53d85d4c6fbc03fe4575584f00aef5b9ca2362312ef0a3858e5a32d9d23bc5971596cdf3864e1c5fcd43d2fdff24eb2b3b63fe861cc75f2240de001c5ca56e1321ca4e467f3c2197100ab2592dcdb213e71cb2a0d177842ff98c70ad309d398573abf47994866c8007b79d2fbc1dd24da88b7d24ce411dc6a516bf169c8103d6429ebe638fd4cd24905bae87a8c3d743dc87d753ec43e5d1e635f1d1f739faec7b04ff783b85fc763eca1e321f7aafb31f6e9f210fb75fb98fb743d847dba1fe2de3a1e631f1d8fb9a7ae8fb14797c7d8afeb63eea1e321ecd1f918f7eab21f232b13018c0ff4d0eb1bd5488fd614f64951d4babec83d3f222295dd63765f9518d14ec01105716d40ace3659687e27f6b6aea84a772aac0727bec47100faecf492ea65d926ed5d5d34ed6a6db3c7d1035a16591f08f09cedf0abad4fcaf5086b546781f4dbd3278d4fcde8f531596b3b992efc5a7113a7369a14bdb26a8f1080b1e13a7e803776389eb96edb7a8d40177bc3be0f28bf2a64bb690ae190fba85b10021f482bfbb1c7d1e955c6cd6ee17caeb2e8b397c6e45c76cf8528db8f74cbde4d551cb164c9c1eba3d480f7058fc597998bdcc36f5b5f1f04a179d2a70f1edae5dcf3dd3bea8c0868f66d3e7be57de0ac46f77715ef7cd07d4c5a3c988b398862534689d06408389a270b6369a66a5378745b9f2aa86fcb9c6db3b3eed580dc6552f875da3ed11740af332fffbe3fe7098a4f219cb45c93561d1fea806100b8dcc67ba65413e3b905b9899ffa7e3eb495d46ce782831787da586aa8c3b5cc5bf58b77ddff7d039818060df2198e9858cd961199b31124b978dcf4f11589bf4a3888301a14e342ca4bbd5e6b89611b5c353753fc7ae0c413d416477c31805bc64138fb9247c8502644c0224b99c80fdea37828e1253b892658b9591fe010fed1f42e8a28b7ac14301b45b7d7e246cf046cd92c6084827360b411402a22547a78ea63a80962b2fc60c69c3391d7c0b2429b534e48206b9f2581d805544da231ba2a4d9f702a23627c70210f08e0af8dc9214ae9063c5afa96249cd14f94fcbe6325049f256da42e24c9220fb7be600219ee5c06c7ee57f59463b657e9bd50cb1cfdab457ce9eb3cc12995a887d5980bc65c1ac51f99810909ce80ae88e04da59d6476b8a644bab221aa502734575203b12c05e791f6d33322dac0ac1140b981b5581ec8800fbe5f5e00dc869655544575a682e7405304702ed2deb83352045ab7513ad7201b9d04ba01d0b69b7bc8ed600292dd795e80a85e48056817e50a8bdf23a9a2624b6b86a82291422075a07da4321f6cacac19a23b3e5ba88a6a490dc2835d0e3802d6459650171444955fa3a7d1615d18eac89c0f4447185740ade65d4e17a8c4c2ff015ca101846e8a6819660be959e65af6038f59e7f1b01ac2ce956fdd8ea06471127a6a88b80edcdda7bbef0027f71ebb0925024c4dbaad36cba3b58df4f1dcb06cb06c2b15063d00a0e8403cb450380767aebe024c6ab4735241ed1e28059b191ef07077961cc4c64c9f79389b8103023b0e6fbcb212b08981735f3fd71909503990bac5c7e58c49623662246fe7f7964a50033b131f71f0e9942cca8a895eb0f87bc34664860c8f5cb455c123122b273fde5212b88988bdaf97e701095838c05a67cfe78648b11732256fe9f3cb2420823b135f71f839452c8acd8caf78781b830642432e4f5cf445e0a1811b373fde79095440c8b9af97e18888aa3cc45562ebf49b42937212762e4ff9747560a30131b73ffe19029c48c8a5ab9fe70c84b638604865cbf5cc4251123223bd75f1eb28288f9132767dd46b245882d72780e6efd77aad265eedb4b84e8dda55723b69d2280c62ef2fab0e90942f5fa2557039b7e24a8c62d71f158ec2d0274dcd2ab8f854e2040eb2ebc7258e83501b5de920b876d6f13a2e12cb89e58761221747ec9cbc34e4f11aae9975c2c58f42b813ab384abc566bf08d131165e5c2c7a81107a73e9d560a1af09a8774bae1c167b35b1349fe414cff74d81cc5ed8c9b94cd2f00a3a4efecc826e73f4b7dcd74007c30dc37521bb78fc429fa85e2f633221b82062d220578d080a74e9963b505627145cd330a94ac03ae50f81a5bb055c3cc7f0b5c399ad7d31c3518ea81363acd0ed1e6b43403afc3caae17c382a1f4ea7127f8c7c2a6de48e7f54e9e96341feebf8a60ea3573054867aa4f0838be382b17bbaa49258a856e19b386677426b161529f0c858dd04997d417a6d471caf11dc094e0e72d8f18dc4f5e67a3449ecfbf89cc04939b7397ae1565534992f018fa675bacaa3d3d2102954e6eeee8a6f6b21f9a4738aee0c796ce9bf98727e73ffc772758621db8d34bc8db42bbda0567ace869a44dc142b048c7ee9a8bb6834880fc80796165d6822176e2acc124224701275b491160458d4917ba6ff1137ef0cab2a2bc6d013b84e7e1e29c3163c961da8921edd5784d84a0f9efa0432db155379f1266fbadb50cb7e0e083f878aed2f9b14cc17c782585087ad4d33457711e1729f6084f1887d42fd4644d6a7ec756a363654d63c8ecd94676ce0e4bd1067169fa6fab643d27d224cdef44a908f2715c3ebbe35adc470a532f021433382ccf21f2968b384f46825ef81aaa80c2d3339b5efbf6b44ccb07a596eb14670e09adfe230569e9b5a8d16b1b8357d966c8ca6dfdf7baaab977e4397553c99d4c5650c0c6f1ba113e1723ec0853073d33113d9e97cae7e9b6e11d093bf854f3f6391702bb13ed261bd4bb968eb0a9b736cfc69b186dd11f2d60d349226e25112ec0d278f5bdcebfedc39492420eb69c221d67ae5c5bc2ec8de209df94549534b745f9531d56441e1926e3709cf2e87dd61356f6a6027539e119df563d19f63a28ba7af9e9ec79e3ecda97d00bb694adaa66a9f55ec31705632f84ca7c7d07dc6075e7754df867dafb4e8b095372e3c6db295cab859bda9df10201f79f6120107b8adc98be26969ffeb21611dfd7e7794a884ba5c9fc7fa3c75734a6104cfb910fe75c748e1fb1e59569a5267d9e96f054bb3cbab20b38882825bde1df1f4f62d6f61710444c18716487014c517a058f70fefe79e0be48034f5524de6989b7d50b7b65457bde92ff58026f60c18a9cd8c3a11390f64eaca5db8f721d5e137403cd7adb664bf5833941d4e73023ec694802400d29c5603c065d92ba5176ec2eceeca28c5c4265f84007eac27c14eb80a1d4acf4bca5e6cb240b16510cec5a468e64b4f7b7cbca46b7656195eb83658d7b25f7f179e6122c3fcf88ded36270086c9e67c47caf7c66c96e84799d51f25747716da79bb600eef7539d6c8fc9bcf4ea7c8d40c7a06b40d20390144f8f8f54b65f1306b07341e2b935b88592092318267b14509eebb573b5bb439a0cff63a25feae606a0096c4761b67a0cc28dc5d77043b944e5aa279045c217a39c17f93e0b6eb2481ca89c2e5c2b9794690fe9a8e821ed412200eb7684de10e8fc44de51706fdfd8a75b326ba1b32c53e083709700719baf45b5f30a653bfb002002cf8b21310db1773bc429442bee053a8a2dcf438768002021315fe01d43c7b50f26d4ada50a2e5d066ba12e27712f076df2f15685955586f194189b8c005c5a22e0eaf5a114677c1052c12b9c9d752666d3b99418d5ed2972214a32eb4991308974b6e11085db7873389a8598a6bb5d72bd447fee13dceeaa09a8a96a51326abb4d9e9c608217e9e9b8e2c6915c2445f4df18f6b43452faa6b3d77596e34caa3cf92ae04fbfc0227c91e9c087611612e5541b93943b1d3cac13819d66aa9b0130b8a7c55b2bced8f63134cb6076c2443a918f5dbd52f16b1958410250f56b4a0639b2e7ff484ebbf8f41d76058c20f76d70a1469d3165c51da10067bcf77c485ced737cb06d4f94d8eac408d2235c2ad2cdd3f4526fb7cf44d6a0090ed7bfca0319af4b2144ba13806a764a4e1fe907bb8193a4b4e1cfb13e0c5b85013683ddc08bac6748345c19d04a1d515e11d55e39d4b6f0b79e6077f73823bd2a506e076b73a60728409d622d6b775db016fbfb6fbe56ea58e550a825323d8b552dce3e70657add147d15b1fd00159a2967242fefa37d8022d17a0112e373a3201fc1dd32cca4edd3dd9dd8eb20037d12545596dc1818bbf09b676abcc273ca9b8a08d7761a8515add6582f9c7249f8a3145a461ec83d8ea4aec5adbb7035d8e66e8e619e20b379d7ae071d4838234b7362f981f389dc18afb8299f4ccd6b4c5f308149f2fcd89e888f64d6bd44adad62f3c7a5eac3ace4a7521248976edcb06a44083741105aff0f7c2a6fa71f3dea9287372a81f17e29991e3bfde777668a19ec16648c4fb21c21853aca3c045f3ede91acf102aaabcfff8c6a8ad62c1e612f9e741c102f8ebf1c02ad66fc5af60902877f279094e312954adc70c1ef7d12b855c2333d14c68732ab73a8c93e00c94565b1e755ccf4113c88e8d47c432f6006c326d8359a3093b6ebeb9512414233e72f400a1934174c2d82b1cbb4df7cc3315e84a12b50de71b60605ba6ff780859cd2fe063eb05afcb6e383537566752976787a58db6be0eb88d1884912e51950740c1bdc948cdc01bc7da5a585398132b9dbccc96ecad097c85656481f4042b28b8cbb2013d07064bc1199c3f74968034c15a14781f8086f0df43389122ab3ece51ae1b83874d59c99802d05b34d4fd06e9ad58cbf487a4e78d23d6ed0b4427e1ff0f47c2247cb90eca4688ccc5629b10dc830dc255a72dd65c0f40a508b24deb0e5008183dc1061ca6d06408b30208cc00e0bb5978aa5ef7adb862434e8f2d5fe71aa0a57063f4333408ec128244d7c05d253c78de38249c12bec676f1ed2aa71e350178565646338fbe1e155fe52679d282a68e05be44483e463701174012fcce3af8e56e49b4697ffa7df43feb06ee718a7d6e1615169634f3b109599fd70fe49dc9d55215585d637297b086ca92faebf359e77dea47703bbecb70a607f44332795f64e5c267f33db1e44420ec4703d661692d9b6e88d0e4f4ab55d9c0631885b1f44f36a2be002ffc01c89524ce544486aa82e15ac9884a7ef6ce2e5640d3bd334eac12905426b2b771418a99d924c55859504643e828a40d69de91d013a2d62c61fd5a958ee518aba792ffe79813305fab11fe67f9cd2250efaeddd98264b3064ab3db1873b13a0d124fb6a171b504f9b624cac3c6082ade878d6632a4f07950cd4ba95366c1bd42bf96ffd2d1b56b5483c140c0187a37edb84df314d538f61d0b4433a04b174b8390f5b641b25aa159e81835f8832ab19d3e679fc4914938ab897158bc2e2b309112ce6f69a542c36bf861df3c06ebdc296a5189fc2dc2eb5748110a7e749a4622e8018020d22aeac517a42912d56471d784d669a7c4386cf52d072fe5113700806d77b59cb2089786d9439212740acc868e6f7a02fe30c8f6ebe06bf52fa5328503819945d970a0f970e4355dbd9464a31f90a689e7cb3c7c3435ab31656d667077cc1060f53a6aae3a6d2a767cd050bd367b101b0041ad2fe2bb23f76944051e6df5a0c49a329167b4728f7ac7ab2f0386e825b3381c4ac68048d0410f03c65b004cd2b8395cc66472fd3513a05913f8e3ad944c8d45fc5ab1d12fc6b94c6ee8234cb56ea083fd6e7142793418a253a032874bc0a2efbd0753e6345a4b53d95403c4d40a9ac3c034d539e661ed2ff06688789e6ac1b8e76a4a3af3b940cde3c5fcc5f41526002e6975a8123c3647b0e1475a5390bbfa7c6899056bccb9ea0695e38366003dc56a62c658b1b4d990cd95554f7b33c472aed4437dc505eda3487b4e57dc61f3d7e9dfac66715275fbb41a68f965a002f3c221442f3864d53cf10a1bf5318344b1decb3999b39e1c06a8738a439429f5d83f966573582f050754ca212366ddb19e5784ab41ce278c187a92bb2a24b2bef50dfd582f2624fa81ec1206bc7fa2cebe5f5b1b66b1a3c424dc7228d77ed211341f90d6a7932a5c5f0b1330eec3d2f1ebdc1d78e9f3f69c31f0b268bc545794a4a76b19df37ef42d987c74a19752915a78aa64fc5c016a683ae0ca14d1a4e6582ba079f862e9df0d5635283e04e7c839350b4c1c60978050ae78f13b08fdf37e155b36fc2cf5c7e5d72eaecf2c7573e56ada22f4fa491fcf05ec4f820ceb571cb817193dbc07ca75871439caf688fc846a10be9cc03620a1525b233e2282f2598dc616370626d4b537b7e9d43de831acecff895768e953f037fe2432ac3b1bfdfd55d2d476ee491ec11d6b6c7b8e9e5276b0cc7df4758636ec743d2abeeb797a8b46682a7a4c9a1d8e9393c1185e84ed1686f9e385ce4c0ed0ff7cb518917906229432f61dd48563cf3e3b94cad4487eb08b1758e3660e914b6f351479e57199c33b1bfe4b56248e89240455c245eaae2554a54de941bb958890d4af51c8986951950fcd12254d4ba78b5ee41c400327ec2eb37701c591987d91333942a980f327a70055954f220f4b826cc79c099550309816de8e3c814557d16a1a73f0f8e8139a2135f69f187f25cd5bdc971a912ed28d0af229bd207792310f7bc3a9732f70c187d0cc55ce5545ca726d53fc00a9f3aec619bfba0ec4b4852776d589cd315b43e07c13311258379308c11b975a438e38cc50a814edb18ecec3f8f33119ec7c0bcce20074ea60cc3750b05683cea2f486ee98eb26d17515525c3ed37cbb10a12411dd1566cf6eb8f3681a0e351b388dd8303ac60f8931fb3ca71f561e695113b976156e7e9dc11311c9c118734e5b92341199d10957bf496ad371c10fa947cb48fe2d6436828a5581dca2c7668a4afe42c3c1bb23fcb258ae3ec47ebb44ef07a73a6f4f24cb9289648162ea6b51bd85329006461e921428c310cde6125d914906222753416601d453c686078cc6a5237b78736811659927c43a2b245e5374f0a0788eb5d63541644471a390e4e62b27812b3e2b84f2f05b049b358b0546aa1c30d68854227a2343ee3293d8deaa9019047d9f5d50b172545d6e8000cbc29a6d1cde162c1f4b91429e394314a4a3f54fd87744ee82304bcdd1d55e0310c830602c74fc92e094a8b27fdea2b286b8b09ddd78a89012d66c01c6a84545727629ebd5cd96c950c8548ed4fa73981c7039064185e4f28f72fb60e115b93835df6938d8040194dc447dd971a76f5d1a85ccbfb05b2d60a38a8fb47f22d95821d1b53d82c79402b872db5984a55352a181ab3a5e38e45e8091fb015db034184637304b5eac0b1e9c0386be3cad265ca6c67d25209915c6bb0dddc0893cdddad9027c4178527ba705b8abe9a95a4138a84a7487e95651db272e7b8dc344966e58530362580f8061088174a6ebafb67646227f9a89ad0f82ab479fd8afcf063536e726451460561b635465adc3835a05cf612ea1e97e9f91fbf8be34d6b264de119ea7d1dafa4b3a76062c1643e22ec7ea17bb374a24a893d0018b73c1a0d42aa9721b42912532ec8fca885b59014935543f0149716dc72f86014e2bb74badceaa228d5537bf72b538a27e52bed4351a8a06ea638797869c25b065924344010b811eec5cd9f60455e81943189e473542b34d9cb2ffb05c5097dc2cca887be639ed42a2f8634f496d784d2020eca218bf249298b9a82f3e69355ca54a44b0ad3f4cae5eab74e4900ff7c8261c38be5d2d3e7a44b79512daf3acd599e7779522c0087711be71959aaa4ef741648cf059c60800bdb9b96001bca50c8d385a9b02cd46485c3958f5bc850d3491d61927b524424114be8660ee055559ee32c4a6e488c37272409b062c52059cfec17f6356ecdc22eed08112cd85772c0e469c1683b673d84eff8c8a82db8c3faec2e5c6b62726044c6a8deb90dae917dde4d0b93311a13ee12bd904f179c601b49e826b0070dde35039fbfce2d4976b4018a876641e1f348c50ced53db4216c4895dbc05ffbbb54cc74222b11514a533676ed04b2b50315eb5fb75c139c84e5301a0aa9f21666ced5b425a0008fbfe21a3b60957fb1779e379e8d00ee2a85a8dbc45422ab71def9b92a992bc638932ee17a85f0f425f3ffdebeb9fd607501f3d35233f9e40f1c6fea8c56491babf5a2a0f8c08d2f54a98eaeff37dd8fc574a9a60212bb74ee8c4650a9e8b7e14d2040becdddf171b23c5ba7189aebb14ea0d7bdc5496e316b2c74a9dbc235d0706bd92a2e0ea0bdf48c5e33ea053a9e283e4c67f94722cc757b392ae2123124f03c88a52a8dab26b38cbac4347e1f70a5ff08e892f397159e57cbdd15d86cdb918dae75630d32c6724bf9c2af8563dae3b8325dd138534984194867173e7d9e8b906ac9e97667cefea387ae4a997dda43f1afb857c220128fdd3f12bbaaeb7fe0d1bad7ceb4b532f1456ccd5d7af3b8d66a74706cfebe986c183f3ee77fc7e3730f487a51cd8770d4ce115ef8ab2156baa07912d3ea1822d6ba0c5be7777bbff161ec792de29f2940e10ca0ca162708978450a12f97c1c5f5a631841d3e05991e4dedb4eec91a8aa641ced9f0d01ca8c4f991b117541c19fb4e8c141d9c30f37c0378f03f10a26adb849eeb21a16e2c6576954490c41ab7a6363461e7396a5439d208e1b099c1f9850383a402f010eb8a8fa566df863af97f8301e8359466eafb6df57c36a0e3ff90f9b214b6d4ff2879eda8058e7b6b3ed7c88c636c7e588b0a72948fe10a84f23923d37f1c172e21f138f3d23e34391fe85bf90bd22742bcb86a7d20767d161acc8db632593d707745b7e38be07e745ddcd3ebcc1563b587f208930f5bfa0e641ca8939052eb71e6fd74be343818e68a311b680a058c6bc4af003fe3a21d5082b94a5b6bf4edcd6eef6de7ca35cdbc4bd7f88da7bd1efcac9c223e6ebaf08d17d883cff270c85c95b6060c6e66cc99f99e2f92ae58f5a9e798a8e6f3eeafb0c6a06f9f86c7c009d53c6585e1e8e9de8edcd4efd4128a7f046077e353afe90489ff612808368f4e0200f6334b8f38309d2a4df7541515a9d65e727e8acbfb85b02942e15c2578bfbccf676820298cb6901b4b7e03b8322fc15ab49a73f19d7361d84ae9a9947637aea0dd511ad61c1040d2297b8b0a9ea71c7803adecd828267e2ea43451b878d47578444b3faf416521de3205af9788d1bf64bc580e0b9c16c165608bc544e9b16e6a13225b4d8ea58f2425772497ccb8f8a8871de6d405fd746fc0b15af0dd4bf8042b8b07bc8c5e1a656456bd1646676fe0c560a6e067c5413a9459a88919765fd2dd0f58b50a48e958381130d881243bb127fceabea4fa5a112aa0fc5ed6bbfd638da931efb6a48e439191f010489cd32b0b31e6b4553b5a41471a85a90ed1355afa5b41c2dbd525bf1ff945dbfc3b205467a2b602fb65ebf4c09d254b2eb2ea694e2f3b5b239550db193ea969d62659e1a015f258e648189217544a0af6422b02f0e5db2a1af4aaef5c0287f9cca3ae03ddb46d3f402c52f36a4ad3675affcfa6bfff69b7689f181aca4acbb683e19facc9fee7ceb50e32536e9db29495a41c93e668ad33101dbf7cca098506f49db7c2e4f928212cef2ac534c7bf489166ba002ba738997489e770c76dc963667b8e5524e33039abb17a6bb6980a6948faa82fd97bf5cb25cd1004f3525ccefd5cb046f2396e3557c24e83e7e1c664042d3762bbfc3fb39778568fc67363696be9c8a01b580b35cb6be6b46ab4ac055f09de5ceaad881403b310c8d3d7104356d3cb109e1e141c1f75de703225d15801fa85784251b785996ca76f9884c5d5602ecb3568499fa1ce9b54dc7ec49f8482d41755fc20d89175a1b06d362265af4b2c3d07ea82c5657c6da0b660b37a66ebe6536dcd66297cc53f8d3fa6bff62fdadcbb21be3c3e63654eaa9a0e2b8977b80bdbcfe23cdf57ab952d2cb61635e5e445ebe31c9efbd1b6ae534ce7ed0e5043b13d9b6d1a09e8e81080624ba036aa12e8657205816a703f9819b4295991c26757055b82fe96e34a8125773129a97aae8feecfc88f8292ae25cdefbe214013b2845fc8cebfcfdeea32ed63c334d3907afbb01306e789d8d7d99b24cbe15ff2bbfeddd6d6fb9a54c29c922086e0865082017ca6ae58c133178d272c44b5dedfbda311848f6b6b9e8125d97df43cf35227288f41bf8da1645475da04ffddbd8e28c81c7d54298955b468e2e548531dcee55c49121da6ac86cf9a967385538ddf13744fac97bfe86742f3f9def71ba0325b889def3a61722f15e14f6d7bd28947e9aad71b26eab3e1d3827f4f20bc2395f28fa49863cd024647660284412ea54daea36385220eaba3d65a06f9bb5a512eb8e0c2473d49caee99205c9648e92393eb329cf992e3f7533500ef3d93fc99d2e3a1dd4665ab7ac0134c61d1b48ca88707bf410c5ff52a97dac3505e99c6dcbc2ca58266d247f75570d1b48025d1cdf8080569427e59658e0b4180d9b3c67cf6666e7d9546b588b81c3375ad82fa6f1d7f193180c44a5c59101004c654db7f5b7b8591c1e1ef73baa9b099259fd97959033a328ac0fec77646f2628b9dc023e71109c059e71e743105272cf6d5828ec868db01899359b713f577ef2d04f959f9c59fc04abafc819ff8ebb7f60c7ee59875325671c0b3b36ecfaab9fbfe3286baab46f335a0c3ef9cb8eb25a8d9e4dae61ad86ac51000e173e15a6f1279bb66d75ab9ad69a0264cd08a4e8353e388a59de023ef97b9be13280f5ccdc09ee197425e48cff085c49cfef1cff10d8391d14f65ce7409390d073cf21098544fce47f822acb09f17777fbc08259cc624d5517c0bf87a0ea1a1ffd4f9a7d66c82c1f66b399aac539552db6142ca9e73d3df349a93837666656a3a767f5faeb703269d1574eae4f958e97f5c6d2413d9be9c857b779dce3038f0f9d5729c4853aaf65daa9e3083c422b5ba7aaaa565df3829c4e2693d99a52266b56e17d1e15b57893999987300a08d6b13f1208f6c9019fbc99d5203fe1f2fdf82e87cc52c352a7325953256d4ed77fae1ce5393e5bf7f975706135cdfd4315d871aaa6ca513d5389c4b0e3545def3b551368aaa66aaaa6aa67ce6da31bb349811030856f0b4b58774ee6fa6fe0f51bd747d77face17a3f7b8ab9953842278f23ece839d5bac123f449b0e9d0e8f4ecc63147b58f4f0c34f5079a5af4dda110d193422426ed4dfd2ea24762d24c1d9a82c43ce987c0c03c2914220a212ea19531df27c91975ab5b2d92625e5efbef9afadaf3e81aefb570f469d15bda57d1ad1730941ee2f8c94d3a2f6fd2417b17d29b86700ee9d954811717d2cbfc8d1f42fad18f42215ec2286a68caa17ef8fda4d01444e66fc8fc0dd390d29bbef4a6973931a149fb984f920313923ee685903e26441285f7484cdb9be8c39042d37c93f6a497425e3ee6634224a62d3c22c4e5edeb2073621ee687c43c4c28441442460ff342d810498fd7109851a803f3129a667844079963d2483fc4e54921920f63432c29dc2149ce2881483ff6c851bf7f7c1c2f735ec2284c397491ae2b187a1238fa17b0e56340978701edf367d291791d648ecc0fd1f1a5979f497b2dd4b9f144748451f49b1e3469323a748451d452e961625e8852e803cccbbc8ea7610c1da9949f489fe36352314ffa0ac07c8ed0a443223de7bcbc3ce7bc3c4ecb8032c7049ab4cff149726e80262d34e9c8bce97384486ebc298c22f4329f244706bc11de78ebf242dc08b9c7becc9b9e86314ca1f493cce871b8fce82b601f47f89cd3d2f29cd3f2387d03e49c12c8399a4f8b3e5068ab35764a04b64fa7564f7c7c644099d34f80a200d150ca74b956ab2d86543d9897ce051c81cfa305b4626f5d3d11d82993cbf694b15351ba46037275cd4be698f875790a50bf1cc6b039e9cb6f6e73e3d015767e3868e12849a861b87f90a7d9da7713460b6b9bfbed473b3f7485cd71a917b344429d16a77dd5f4cf470d400a3712a5e327be3182056d237cc32d1d1fdc299bc0ac27ecc3eaf7a4117e59f97cc8a7d35ba485f245ca481716d3cc9f22c847c822a4b803d37012f2347f9271e78f3d1ee0ce25eeec959c9993888ef7ccea03e4e0ceaa317d7af9f3a1b3d385d2756ee897433f9d6c61097bea17bdefee169658ed76ef3d0c98384211d6c8d9e9de7b9c762f7485e52b67a3be97ec83c7117647e97f5c61fb8e7f04071df7fb0d748267cbf9befff3c01e72fee6be7121ffe6fcdb3770bbf2f3b0449234fd149469112cc2f38aebea4427794c665b62666ecf9ba51668a9856935d6261565a0a1659a69bb49e9943c7b761bc8540bafe4824ffe2bbed144e7825132f0c99fae98891517ec6ae6e94dea69301918f5834f387ef2e7e71f16c73217923022d2a6df9f0212c55b704e7cbfc3944bc4515f58e78f8460bf173dbb80ac0bc552fae431d72808b67e5faddf8878bbd435dbc4a1022bafbf1372060792c69f03d9899c59b1941d587eb1ab45b0087694414138ea4706f19c6c92a1243a57ddfec0b130af3b719557349860a7044836ed47190433b9baf910c1ecc82eaf0bbb4edc6a8f90a6813a57ce0401ca1901c10601393b87a378b99968326bd7684f1f8a7f09641037461c91156ce415ec46a97ad4b585dd4b0bb6c4795de53927c79e0ca2f785447d4690489035da1741daf0bfdc201cf585bcf213dfd090a3ac3e1b561ca579c5e148a3c8c031af021c05ff6eca633013a4e8cf51f846879de0c5758e12057649200e9233fe0b0082c32e094479355d937aa6eb32744d498afe23e82251a23cf977d334a78b5f1cb4c353a4b08cb3308ccbb8feb39999081fcfeb28a5f46f13e19b10c81f8c92fce272fd475748189d1f84a36cc84d644b18fa7f3fd7bfaf8bdfe56217bbd8c5ae3943eda3075a3061c5cc54f479feb651f6fd4bd226f4fe37a4cdf6fe38a44d871a8938d8e1f99c96fa2210880d087be97b20101d18430f3f75600c9a46e4b26844e48d9bdaf61cd76d60e8a927fe10c029009e486f0b08d3b6c5dace748a42e16f5b0edb739b6df916b0879f5a207ecd012116fc8af9916bc2d5d1adbe725fad56950833f4959cf1245656fae48f659b59ec0e55729d2e99939d15faaa45f00b3b83e6939e554c2833ca9472a9346ba5ea993b9fa584e5ba159d1d3b76ece0e131c82f06bfcbb5a5a48445c2bebcab40def0c034cea335e5206f8b47e70bd8c20bec894b811debeb4ea9b0ae196b8a47977074165c7f1b8ead44e32ee4c95f148ec2d191f08523b331ba8baf3c9fc1556a25c562b158ab98d02530c0207a278fc11a8bd5181bb24530df43dc2b4080c2ef7243d85a46d78c9a6ac7f5ea4a0339e33f5facd335db0e5d511fcaa230a02ea067d02b940daaa24be40c738b4271d8dce9e9a427939e4b945cd8b65381da448df18d16ce17d3f8aa36c1286f62f60c8b15292f17eb89931d9e949c093a03c69b985de79bc9869c50b88217d765333bd2d89c6958b4202d0b4c8bf1c8ae6b53d0903447980212aebf26a5fb32b42854a05c9fd2555c2368506c21699e7869ab96e6735de3b9ae29b9fed40945ec481b2b8d6a257d2a83ca681579d343481a9736fc4e1c713d4b2fc1727dd450705f9e3f1fa2679791fd3c1a8b89c0c6f202f6cc0ae87ac26a273c2939e33d5de32a12f4895f66c1b2934509ffe84abc65ee0d83bdfa497743e928a95e754ff3901e86926667812618e562f009039589baf2267cc531ee420c867115f6829df00a8fa031a9318e8ba9982891418679475f854db0a34f1e7fcf022ba36bb66f959f8266cfa56ec50b7b498b211556b6d84b666490e1f341bf82a14bc123950b6d95ebc2b179683e1f737acc614e65fa18de041a8e9cc5570e7398c31cf672a2b19ea8a2ac9c256de83777c48b515e78043bb24f87a3c68489b4610e7d481cb7558d4ea64e288b52262d1ee0009f8fc91d61433ffa6ade2e015e48861df9d53b3cf3b7679f57cd91fa24e02c65748ac935808d4dad5cb7558e7d6e6cb0e1f3d1cf60e83678849f04dba55ac8307eb53845cef85356829ca1f1974a47da009133fea1efbbafe1aef13e0583c1d8a04f5a6451271e48574c542d32fbb89c30100d704f550d527ca3853a4c235f2e2951666c763161976f6695e9c574c2f5b7a3d077b2fbae3f1fdef77cbde62be8e52beaba14e8d21e47558daa6e89f278c979781a7e871c666147a157ddc27aa5c4dd2d5fc7fda4c6449e0ffbb0cf086c292dbaa2c819568b4e562219aba6344aa74ffc332c32cd8a56c67509a3726da0311a1b698c871dd887ad982f15d3b0158caa66f049759965be1f5f35c363aa9894ec302fac1599a73ca3adaf1665420f43c684529ea6640f71abb9ea15afa68cb957afd7063a00368103e0ed517d98c6a7aebc89ce039eccf181d1993741653e7c43af5032a81a3b88d1181176ac31719e214ffe57aecbe810b329a3311aa3311ae338cef27a7158633e6ef8814303aa1a5418d000a64ce88aaae6773978dc11b6bf7b80c7da75bec2af51aa0deb5fd018743fa9938f862b592e8d491b1ac81b5781f32b083fe0178d4925211b96569127ff3358577c281badeb7fe3a331baa24ca84aced02554e52df77196af441d27ac5ee97c10f87c841e02524640488e1ca51b5336653077984781bbc9f5ff42dcf6a4d23a6b5795c7311a9bb18eb1b792334e5a6445e91a1f2c77a4b11834a0eb515cca48633cd2980d3406d3112224021190c01026d2a9dee12cda0426c051e056c023810e9b389ccae7c3615b583956f61013581ad53c74e77a96399b5168e3fa0d6341d2f8944dd99465e954ef4cd994f5aa9d28d19e684e3426da121aa3313a2de09140a7d2a73e8d5ec6f5d74020b64b4326c8197f0f43d290402718748247cc810981d880b0b75f03df8f8fa76400039f8fee31f0ddb0018ffd4a8df5180e8b8df575bdc586e52b55aa8c5f2e98273dcfd7ab6b48307436d3d9dc5635caa0e4b6aad10d6c60871d38e03d619a1a30aa82c1277fd235382f83f5761ff018e4eb32391306182f47f912a220850f460002860e63c18e1bcff5a70cf3ce2fef48205865ac52ae7fcdb8ed5c4f8244d529d2865bd5e77edafbe58f866167bd4348328fd33e4c41609ef442609e04c3313f6d57c381a53b20cf40e9a798977921312f43fa98f015f348603e26e4d776dd95c819ff900d4b5fe6c0901e66474ad79048a153d0838298af098b79524cccf277463ce92812cc8f3a607e748143be9937240260ba2ff423bfbecfebfb422b612a1cc4b3dee1d8e78542a1d00dc58032679e2167a0c034565a9cf5c412d4a27720df18b0731a7422e649608967cb89d1d1d35f82b5e854a695299b58ee94855d8c5336d615533932cb9036f35b546034467ec2f577987b8cbbce578b77d553e627ff6db3b6549a29e01ac92214182599f674cd9436678c1a112b285122ac15661ad7a76cca2efdd219b6ca3629d3989cf1a755349e23ddad29e9896d9bb53446631cce57d7cc0f78fd810f7c3ebaff0008b63bd61583b5ae384bd7b077936a4cfab6d2a8988ff99898d077b2602983ca94981f3b8acc8f0de5c7aea2144a5f72653cc6619ef4386dfa422a2c87ac228135e6ef1cbf58d5ea9ab1c6ba46dac4aa8c8eded47aae2fb9ae299133313b8a77a43123357684e51fc7cf47f70cf62df2dd50c4ab31695345a2b88b578d711581c7af66d2356c480e43b6206da412d71b09cdab73951d6458fa9d5281b45102f341ec63054b96eb3b17e554a6a07ca5a451358a85c25ec51226d743273cb4f892332e73c6ba3a5dfe422a2c83da75f7771813541aa5b1ac8cdaeafa87bae60b8d05b70b79a3a940d2bca4cdca511e863cf97b95cdcfb0ee57aeaf1c267ac1fac52f7ef14b5ba2f5809a1239c32f7eb1e6d335a394dd2a5efe74898c56719d3eb94e9d30b91e8edb168ed6964ae1f8e27c98077bf849bcb1f58cbaaf28a3933ca476e58b573edf78299924470ea1bf0d39f1c8b2a7743e4ee3a07dd3d9abee884d5dfed20a6c897e0ed96db914a56346b340234643166854817aa92ca44003caf4b59ef4e7d26f4f874f87d0735fa2ff8e12620b815be8eb735ad564619c115bc28b007a72cf7a8d6ec198c67f0d46f50b5a72bb7e5ec0285e83595bc88d615a14e1ba37aed4a28858aa0a3da0d73369d3fd836eadd1335e836f7a0bd93ef056b7357dfe7068118ce7dc601c8644f54f0bd6b29dc8db31c5f2fd5ad2a1fcc896b562abd5f37d3bb2f48c3e9d1d20477d2f7afe0ffcbee58788de8abee5452fc4e8bf9739dd8e2c9665bfc586a6feef47a3377528e4fb914988e85d5ef42eef1f1250f67d53421381f502dbf2f6e76b2d282dba11972ca4729065f4489b225ad883f66ec4ebb612d2a6732cb7815aa444b032685821a2d05e88ef5de67743cb14e47b9717f2bd4b28fa51c8834c14dbe2359d2eda5e04f6cc82b3949cf1300d5b05599386b4b902ab5544af5a37c802a87bbaa7c56ab15a2c16abd25ab5aa51510bfd91f7e295a4b063b77ab688b69e9033cc05df74e7a0597d65d69f8f1fb88dbba3063c76ec96dc9e55351cdefc6ef88122081895180cc6400ea4c308931968c643b8fe332698cf476b74bb188fc1d1ed969cf1d98807505c219bceeb7ab7ba55c33d55cd62188bc1e2a1e26ddbea562577b3b85b0d04ee1e69b3d5c7e91fee14ec15109ab48089eb9d7cb990009231c38e3d6b713ff6ec093264d81b6652ecd82d192b36f41df5b6ba79ceef1f04fa869e65ac60719ac17ab7cab354dd52a92d354b5134ecd8d3f3b41edbefd0c28e3debe16184e597a965a5164b5857de915d2ef04fcb823bb0520b1400ddb17be413aa80a15185185c91c62c0cfd58ed8edde380f9a67e4a04cb2e25ac2bfc1ce0e0c777013812a8d25ab5aad11d3cbc17c460144bc127e7b055cd7a0106183e1f4e7c36f5fdbd9bda0146f5ef868f8bf09c3de72401bd4070923b9fc386d2270ffdd8be5ad8add08f1d9f79ce2a8a9e288aa11fcbac198fc18f492ff9e9663e853fa1fd58d3ed5b9aa2ea9e16bbc759b552cd6d9b556301847e66251dd81e703c27ed66f0669861003bccf218740bc851a29fc29ff0264e5d57a4fdd0431f6286c4aa67c794596805c85d9a6f6bd1bb87fc6c8b53c219084083c720b37eece4ae8659dda2fe85507ce11276ec1fc80e82acf1810c2e1612d53d7e6af10dab68705d1ae1fa11d28b9022b73c099574150d96b8feceac59f7a8ba877e17a56f6e5bd54416acc106cf0862b00bd6b317fc4967712cdee34e3c359b990bc04a7413492345b7a07b4a4cd8b17b1a4b8fae449485f60fcf6aba35a78d0d37ddd35724d713fab17ec7eeb99f0fde69d19f49cfd89028265d17bd121689eb3e32024b9b204775cb51cd6275688a95e1c28eacba11b116728a79b6b023a7e61dbbe7867eec773f1aae4c19836312d5e5b05946a4ecb64f11be100a1b2eb1b31f79f2174217b36ecdba35eb59cf7a26fa3c26020b3ac943898f3bc2f695339bc82bde829f90db4adab470b7d9b6711777fb99e26e1c645bdc9e4118d16b71631e39b33dc8861d79b56d630d77abd94a77fbcd13a174cdc8473090a3a436f211d2a6eff63ad28657726607470591286e224fdb6fa328fe7669a8847b98270b368a104a88f9de6f510899efbd10f4bfefb1bdd8b1778fd4d46db0de6e52272d6e4fc1f9dd90e476340c7b2ba8a5aefcb8c7e96a5ff44346159ca9db51cf3f5310d1b7bc10d1b788420f85881ec9e845db57b053d7c74fdb083485defbd07f4f2916ebdf813ca5c50dc84f9beb075310fadf5760bef740d8a62267b66ddbde51dfaac4f2d3c6ab16472e0bc4767ff8812ffdf938ed59c9630b8b74ac4ba2abf527581fecbb759ad77d921c1387eefe48bad1e6749bc71d6119a4b7d3bc0ddcee9cb7e3a8e6b579e336916cb1d6e77a61a0b0f46768da3c3485deb4bdbff726eefd33857ed382ccef7e0385ccef42d3f6dd8f9e0385f87ba12948f7a3376da1902eecbcff5ef4a370fb91fd9d963762cbe9de9a3621ecbf84435c46ef12f6f0537d99d37d3ff0252267ea87be203307c74ff5394e081cc562fb6b0f03dcfadf016e7d2f01b77e07ce204ff5bb8fd19033f537905e6945ced4dfea16f2acc7ad0f7e613bc525994db36cb381b828a52efaaa556693728a5344e80c01c2375ae801a619c2a81e7a60141bc127244b64194b649650a74d168d603c70fe188e3c2ecc8f31301004b8b29329996acd41b59eb5ced704a3ebac569133dd61bd22692a19ddcdfa457d7d95bd9a70f7578b3ea5c50a83e88458adfca38913153ed58c1a0b408d5533d86755cd60546da2731f39d344cef8aa09f729a4374ffdd1573d66a852a4c8eaac56a932bea957244d25430d2e083003d50463be6898af09868a7d26188c622b9a99d90ae62a92b9260a3ca078e01b2ddc767adbe19bfe5965dbb92f03f0d03656f8f020a170fdbb1b7e3118b4c6180c465533eacb06ea041a03e3c5d5a736a9aceb7c53a7a837a0c20b16cdae691e3ab52f477ea1c346702af472f118a42f35764375428dd526eaca058f5f3870119caa818a01f38bf9622bd8278619fa01105080d73140cbc1ab2b087835562adda82bd3fc15f00df73b3ca61ce8f37328ef568382aecc342cece8ab1b8fe3475f1d69aab3b3c353430e0ce9635ee6774a6fc4d672e5e72af015308defdcb1797aa7ee58122467585602f92567f097e12401466e8294338c3c46e9d3a173605ee60b327360428ee1c0696cbea84aa632d5ef4c5e0eda646575683ab0cf310e9236f20acb292642cef8f3b4c3fce46d04c16032495f3eda89e89dee22a88d2b4197675c85c388d9b1c48ef5a519616b65b1c871b26d8992a674a76738b1a88650ad27a7d888909f18c128ae019f425a830a86a4f1e7d3c8fb42cc4f5aa34104fde08cb0e394c9ac2cc112648481b094167de4d55abf90370a7d558a0bdef49955cc2753c9ec993c2e782eb80045cef877313b5b09247a882a9a54859a8a6735a4c08ebe72e2acda64861cca7c144a94ebaf49b9fe74caf59f54ae3f3fe153fc78f3256dba90289ec267be7cec8ab26c8b673eee4dc63aab41be92994f9f41981f6f9002cd8af9005680966fced9f2cd1781324703e54d329bdc80697ef46a0d3b726c7a9f17e220bb90354d24123128354dd3b417cfda29f31fc9b1561704cbdd915bab21f00d18d5293fadf8462c2dc17a5184920fec0e3855468b31366c6ae58a327250e81322fa165260b9cf851f3b3207f14de8fd17206d385104248ab330031802b286539eead405e2a798eb0bb30089b95cb1de36b07b408514bd7bb87fd08276c92460b1ead70e4f6a95e2d20784526d631ad157fac28c574cd3346db52af57da1a93c69979306ea29edd350ba74c36b1c8dc3d3869033fefefd4835c159f886959044a4ba2e08b6cec08e9d1a6bf87e882834f177434c1ceae488582b53bfe84dfcdef726ee5ec8f748a2e0f09174ff0185dcead5f51a1a8003878ef21c85833cf9ab80ac56da6ab5ea55af7ab581dceda4b6da9ac80a3aefade94f10887ae5bb12dfaa26411a825b928d598cae6bee2b5be48ceceeb691524ae914083ca4db946529a73085124be1eeeed259d0f168517a73523a5dca3927d542295896c297668b7336a1352de81514d4461b1bc7416183823627f51707390703776f7ab3062efca6014cf3af1467111dd53b6c481aef56725bbc020705695b6d1804050505f115244a6706a5256c12b7bb881a9d3a8ac2fae63d68b70abe9fa2605f73e3e88b9d080a92335e586e5f6d35c00112ae48238b531a965e99451a4069b45cceec20290976f4eef5cbec2c6ab123971718528c4ce9060e530e1d4e9d3a75da820b0f863b78bc000018c453000410c38c0c343d661800010840001a0a50538029e058a0aee1ff12cbd2a88ae5a17d3eaae631ee75faf4a6f7c3b4ed98eba5522db164395e776e9bd329f55992a5f652e9fea88a9d2df29c1e02b960f657540186cb3ba70004ab8424765cb67405c8c25cb1c11ded43a1c42500645141b1f5ca2cae2821801558d29559ccb2fcc006f998cd9e30843bafcc62b6e2e1413453b18d84f4765c39df0339e5a720fc14b33cc5101205c44fd2467bd92c2e1cbb899c2133909f805c40409bc370470682e18ed2757b8a3b843c39c75c3e72c683662d863c48693ea8da7cbe99a10d4c23830d4b48a147e6d3cd559b3229ccf09946c65260d40fed7b34ad292067e6ff8fc7a1b3bfc5b0a39c89dc125b2fdc125b2d51e41b8e314d174186c859bca6c05e8e6225e4c9bfc9f5af1a9dda8e97956bedda2c999bf358c6329ea99c30162bcdd34c38884e4da353abb5d6aac1180ce697f6fe42b6d0c21f39e392335d7bda5639308084f00d6f2199e594e6275866999fa858af07d6c42f734cdc3304ebfe1c964a1206b20556dc1948eee11bae82892ae48c5bd2466b438a5790a2f3c05d84ecc1c24d8265adbac00e445af487f5c81997c9aa4c26639946a7cffb5081b35ccc1823fdedfa04d1f0c0eda79e3592e3f617a1e198e3f673620c321cfb678edb438e5b84fb51738c392e573b346cff37c71c957eec815b84fe46bb508f8ee98eb6695be5667353db26a5734ef6baca6bfc748e5230bff3b6e91d0d694d84bc9fa0474790e4b24ff6c071bb73765c881d8aba6d200e9cba53dbd8351f5b05c769396e2b21ca7130fd83eb01e616e1e877dd2247e9e4be4e2e1c616e11e66ab8bd12db3f964a258fe3429a86c4a6bd168e20c96d0e9caef1164a19c3fc19a2da6fcf6d1bf5aa51285da8fbdf363727d7bdfde0c3f5bed2ed470edf9b55b0fdf6e78fde9bda08641bb7e547ee47a1f6ddc0859e0da9e8bb341cf9ebe7c3d6c295e511f9beef3322de5ac11ec4aebdee46b1866a35f1f620de16517d7912fdf72ddf712e2010f5ba70b3d62de4e2f238ce85b4d9e32a47f2c7df1ada14d87e17d3a86954f3b4f9385dc11be4cb4b696302222fcc0f09b8f56b38822417e6c7fa9b3743a12d7c6f9cb743a18f646248a4972191b649c281e36ffca8fd86e36571bcf613c717d1b690391ce1d6a27ded06285bb45f02678bf66540af45fb31a06dd13e098469d13e0c7843c660ff05c42167ecbb8039a468df04be3cd93b0a4b72c6fe078ee2b5e13712b945be1789be2e044405e5d4b45a1fc7ab374bfd31999365287466cdb7ca3c9d8990962307ad959b93a3b4bb2bad5b87638e4b7bc871399ee198e3ceafcf6d2d8eea9e8e39c2ea8190b675b4f98bd4ef2ced7e4ab939394a9fd66deba17427b7fde55c58027d4f9b97f26079b845ea87359cda15d80bb9c06e4f9dfbfe0a8a5db3859b87251c7fed37edfdb5d7e86b4184ec7f2ccb6d93deee4e6bad557bd7b4f75a04a2c2890bc356e3d1a8d0f7e9c51e4fe4008a99b1c68a7ed7021a36d40ae3fa8f1d3709a280ca7db9520b1410e176ecd51848d44b1bd2a54eff8926977ee743c8438bf4c31e5827240ac74fb46b76685e42d67440320c24cad61f590797527f7b433c7440d6701252a4cf3cb4fbcd93903552d2e71ea72cc5fab47eedf1a894751e929b03aaecd60f8542a15028140a8542a1901129bb45ea6f1f0ad5e7ed4bb11a7a9ee7799ef71d0e1ad0b045425fc48894dd51f4f53d6feb72e87efb31f4a328d4ea9cd5d6fa3538aabf3e10593726ecb6ddf0fd180a4729bba1914d41ec8f7e88fd91e887b4bc281c43ef3d8e87a311ed3d2058bedbcb9003badf3c50fb7e6cf543a1ef2ae87de759db6243ef51d0dfe285a31129bb5d38562e046ecff9d85aae7492c7e851b8270301511a82bdeaaeba542cb93917171c857211046b11fa11f56496140c73314dbfc0056b6182143016b77beaca0b1825d987909d0617c172cf546a5404172957a5b56a55a3dcd4298bc1cac10c542e18524f868911330315b74bd9e1e8c92ad30c54936a52a47a443c29508af5973255b03258ac8c1a56260d2bb386959fcb75f1662335048f8c10ecd8ad1d50b07c7e72c0aa3c907881b504005708828b284be041446bc62d2fe9a0c2cadbf24f856d9d524604c1375aad35a238a8540dd65639eb140d0800000008a3140000200c088703e2e18040a20782a83b14800b7c8c407462381608932887619451c6186310218400010118819991ea00be231d829503017ee523f858000fe934e6bd14408e7c2f48bddc293628b7668cfbd82a34b80ff58d0de48ee977fe6222a58955f1a59754c38eebc5b7c60c4662a7a14ad62fdf93d6308ca6c658645293f2e1525337a4beddceaa9adb35a60284b6dc3cd149aea25d23877de2a0634845399c9e97f975dc4b0745e765e4350209449ec12970fff12ca61edf472a8dec65c563683a5663114f67f615ea3374324360f9b59f8b8f8e925bc3c78bb47c48c70514cb1321624a297052b30ab69e8bf9920771daf3e3d61741ddc2d6721ec3530dd113343ddcdc17c4418420bbc44d145f2d1019cdca430c21da2ec409670f01d9bc8f5e1862ff62a1efcde85604ccf36a60e41eefb1c51bd54c743670b1d093a14bab127131ae1be6bfe2fbe49c2faeb40d62dc18686b29292347407b046573618310d36dbf6379cc9a889a3e08101d814272c68e62035168fbea7086647677a35f461cc0120f0aaa2516b38a08a94216a1af50e83a53f990341392ac1eeb63824c8f11cb85e3a110dd3b03e72638ff878994f9a18c49c83dbd9980321eb172f4b02603f81b42c38679fd8607d6870fe780c11b9d4f18fc5b767d9abc51d2150cb39f0e3c8242a02517254c14a400a37b0614c03a2fb04499aff770a17c9e0235fc2a6df0f0525d6e2803c56774c8da1946e0a8bfe47899b04b441d37e92841480725543b64421e69531b6530a15ff2762652e0cb33294d41af9495978a48187c25bdc248fb47135de44f3435d4c227b7496bfb3b49f8b91b530f7f94fa60080fa9f42be88c1634fda7345939f0c5e0d08937a205b297654355e34881e49438c8c72b0fed9ab6c76bbb98742a8f925367419bd2a3db73d43cf59b8e80988191b498d19aa73082fbe54b07f2f9e0e32917a77b9631facc2bb9a24b39108ec8df7f477e994587acb973b682fa487aecc635b9b891811f41140f41867221a81c41df8aa24bb1c86e2ba8055bfdcc9293845829ffba1d3fecc63b2256d91b01e1abb6f6483807e418ec476c25f1b2571906044e2e19d8f54dba583a52724682627cb320d59133c676da2aa6b3a56eb81983768bdcb47e34a4f07f9fc972a2cf1906dd924583587851246d84704f0e746604fb1ad19ea2f9db8a4bf2dd6689d9b0ad1320be5b037fe0e4bbc09d7a659b83cdf2c05c770c60ffe2f8509e77cdef3e6564aad574f657199f75f8f4d06bb80f0f7d322f778591aff05e6fa7cae2503f6b22a3a0e979cd644c86443a9e245a7946d7f13731c82a53c04771922436f8a7fb7e591329f7d456afe2dc3d73b92412e8c614fbde13a0a3ad0a82a6b2733b21132876a7effe14027633bb54b183c822787f1eff91a9628b3c1faf5ab44a14c909145a0ed886ea90d8e2d7e89577a32bd6c2b3dcd543749d839a898fc15cd9b29f1c933a0a48f5b354977e01ff0d5d021e76991e152b354721490af95754fba1039494ce715b34f82e2bf47ca3fafbf11f202f481d7dd457547616e1d373975cdc5a018ba09234bc86dc91d1da138f0752cdc0e5bf83d1397266496b774657556d41b2362e86ead366b7783911866bb7c7f441133891c063be15ca86d977bf421fdb963426a2d7bc8179ce6a77eaa924c3ccef1ca2dce60a6d93915a0ed72ef1efddd737d6e1ff3527a644940c341db1af6fa9ab93676ec97a9f2038332ab555336888815155d9dba9a1b8e03f3dc4e68fa81003aea8afb2fdb9608e48f8454736078d0debbc0be16a9e7101c7e6559e1e2f6b9793794426c564a0ac2612d2842ab453974678475b2391e0b8d9aaa26e1cd46f96de73e3a94bc92ae5d0f17d7e18528db4d444f2837f33d93a3f75e2e7796ed4b8031ee25d3c8f84bdc11a8298c821630fec94e76e46821cb1f975c3351414a4f9c48379ae87d2088ebe8b9116f9bc358cf770e9d8b61a4248a16d2909acad65c2b08a6c0dc60d53d09c59e838c4f0f1c716aab44e1dfd114f2294f033cb843edc6006664473d262991c1be3f261817066fab854d64570e23cdb817da47be1f9dac4394f8aa231d200dfacfd960e0f5cdcf96ae9efb0b07f703ca50b2d3b8b3d3cc0cdf194e84c676f3eca501150378d63684fcf4613bb32837a106310c9ef63e227e5c33872ca6ac421cb9e7c7d864352192a90a146e0314e5bd9f84ef527678a609848592d523c7d2c612ed23509920303599cd4a0ed794b0a5064d734c39e33bcebf80baa1be98636d1787e1b678d532253523036f182587722876f240745b41c385634f575ba32efc70fc162a1ef248ae23f403f5aa0a443145a76b9bdeaea3f92279f9c57af7c0d39d169f7a40b7159b1f0fb7108cee3fde500e851f6e57f394bcf7520c3da842c7755b8fa9b7d9b31eb787d7a2f5867d0b87433608c164c8ae5edf1c00de6db67c984ee2c6ff6c1a894f2230f96bc71ff709924c2d2ad0fd001e2c9ccdc735697168cb56063b188b193380ff0411ec4b4d77385895e7253b2371e3e4bb17667bdc9f226647bf9d0a59b1ea296860d9fe57fd70f696998460a8220749f2b08d28599825099efc44266468207b8df5eb5591dd4a00ec167e27ae536ae8e337931542554fac246b6f5120e0fc359051c998840edc109fdd81e56ce4ebbf9eacdfab1947f453bc034b3d2c27dc0928bd417a07cf5c79e225639a2f792a028de6885f75fccf8f71f70cc0b46c5f1d539a30d5e812f34afd09510aaff8fa4dddc0822549cab8e74126dea9a89aaeefb4f0307a083617586a0e2c508ddcd706162e22daf2ab62b13ce3d99d1fcd678e058821f5ac770d208c5078fefed67aabb894323331d3fbeb9b07ecba64aeea4e47918f6afd2256903897277bb1dc319c9cbc8e2ff6b700a2f99a7a9e10e4664525d6a821b690f3b41afca6d4b504aef3129a3e020a499760829b0086651e0fc8262e80ad8d7e8bd501481a2599666bc9a91f7e84f5184216793e54f10c1ac7e7214092632e603a6fa6c75c77fca21451731fcea82a5eacf26a0e2a06731e8f74cbdb7a77ba73a716ccdbd220cee1d266657f9aa96d9d1b9ab6b9a63ca7e56faacd8a19900d2edaec3fd32bab0fdc2280a4613f46d66ee7fe8523be24189fc9a1569d4d3db08e9d673e4eec35e862586773f2126ed00cfa240926df00a4e6471533fce91396e116474c3c0106f85291e72f4dc2f28163a115bac0c8f82dac4d72b96ef6141bc6c84a0271cc8ff009c38bb6affb12ae4df714c8d16bd880ff41bdb235e4f4f3679ed375198ca3c417d03bca9d68a9dcc8721a004479092519428c9942a80cf2d1fea4bf1359a0abcce9bc687105667e614333327cdc79f46d82a75f2b106c65700bc7c2fe13e1b3db829f677692fe3bb93ec59d188c96b38ab8cb27be0098ae03ba17e35966e73ff587cb7872717d8423254ed537054f98497aec21238c0916e15568d0a4bb2d348f4bebff37cd0254a121ba9dc099f059d1e26e829567f7ba94892b2e6bec56ed354a7c435f4dda696e12322fe789508ff8d59abae962ddfdc1f6d07a0a792b6c2498e492f0fc2a9c0e071981da593743964466e5aa8519997f355ff202c5b53f53c0df38af0b94cbf1f5f1c0a490c4e1e82d56c69188a612045c218c8ffbaa50dec70d26672b44ffe742e19a7f6fdd2663c58256965e953d367e1b1feec4362c335c1f6f5be3f2e2678df5accab67144e5f1d8c7316aa5637ef939915997b923018a2dafbf67e4c00f1ee5b0052d57454b90c204eea5b349de4f8658b1529760d362f51cbbac81f275aa24258a1f6119395a41b059d16923bb865a86be5d9b31bab77e9b0319eed1e88ee80cad6f9eccec4e0450aab8881e3b932d2cec9bab0c695fb0ddba3a238df61978543e1bd7e8555dbc85163f63d42f58e2762dcf7d05fc1e48984130eb09c6df7aa6f8547901942b079802dd79efc0763aa40d4a1290a9061effaa39b6b0db3d38ba62b4ed2e24677e4b5995d588bdaa417a52dba137a069e954c0bf9d893e5887a87478da10a3e5b2d238343c8975932ef3149c139ce0e52d704ed288279dd4108868e0c5fd4d408541643466f25bc4307535ed3bd21224e36b2f0a9c44563e81765c487668acf99733ca5eeafbd6ec2f03f4a4ab0da67e70c0404928832843367f7dd8dfa3e798cc300f067e646e4cb039be3f0b9ee84e2253a2689b4b233434dc537e5285d1385b58062363ae6ec809231eff1f258cf4839f6488b753c6b796ba50da9ee60ea084c936764dca09493d382871997036073667513e39f7cf84daa19e440b18cec99959a85441435bfd08fdf62e51b895b0c17d91247681ca79ede288f1590a41a93f991ad39882360c5ae27cdba6aaf167260429c5793cd5ccd9b1adef5016e64d981bbc6e58f84290e594de303dd5c45f7fe94c4ec6308e1ebc34decc3c5bc0fb7047d05e465b311ead1066bef6f532394d299dbb8d7171522e1b5cb450bfc9b4f0b8cf4afe402cf115c2c3d56f9f7dcb4428341ef1760e68a058af0e807bf42a18adef36a6c8dd3f63957416e08f4fba4f40351eaf5a009c1f3d50d7f9ed3e95fbae627b30ee21495855a4e512850cd9360bdd803bdf8449ddb9ec73db4f2c283f2ca602cb662438f6a1e227c3c135bcda7d698db6360d4bce879e81e189dcbdbd124c04d95ca03d5dc274f0f5011c68a5791f7a6c817d95d2bef506274f3b788c44a69464f2cb0000f3d7139b9ade9ef87659cf299f977d66c2ff1009b7178054ca3fa7db27ab62178c73cb38639ad5ac4b4f9c90c09897a5626750823fa3cc05a2537fd78f294db47a1f3a4a3f0df45231440ddcda4061a57807b421ec3c018ed87cd8fe48770cffb8825350f0ad8861e7a64cfcad27a32ea9370d26e0182db8f2db2c6582717a315adb60f48c00d2a966becb3e52383ca89c8012698d30286eae20cc8eea74f4011bb78e021da309366e49d047d1418eb580ae9b41bf260f1017d2f3f22b04061a0890d9da9683f7cf16a23336ad5010d61563857c890b1e267f11647ab93899b5f62151757564adcbc5202809efb6230cd1d19947b9fe6c38af442929805a357f3c9a3603cfbb996721101a13c5b2617512b416e7dec41999db32e8088502681933ed74ca29c75fe1f1352b4b94ff99731e1e4e79938ecda428228f29f160430a8aafdd90692c71b0bb976d944d1653f3c5dc8e567154bd8b756253e0e7580c0e6fed26b12003a7a8a13ac5ad76f45f67413fa26671ed93c0385c6df61b39bd6faaa109a0829e07143604f4801e5720458188e52959a5d7d6baca94f853d4c7065bb0bc0a789b60892ef01a8d6a6651be0a5803cbb28b5f3c3891b107ee99424b8d6069313d2ebbbc35e62c5805f0c60992c5e090ba1c32fd17e4d684ba47b851809d60113180c84adafaa24b7ec3bf08299051f3980117945db3ce2fd775d91574b1e4b3cb607a488eadcb387fc028e347cedd67e5163b6a34ab912383e3726ee01b85dd017e3ff17a06040f0357e474958ff9aa76a7c45c289b5ff245589b4f06287be8aaec2ca4b1c9c8f18a68c1954cb195a915f8b4cd8edde3fec5a9ec30fbb5496450144f8e2479b68e1e1ddc156405f45e28d0581eb1779d25c272096a1e856c190bc399c0e0c0d4d1bab1388b8fcc69e1997b1397ee0080afac909e7c045b2b181e6c6591e4ad92ca8fb6ca2e1602ba3d5881090048e7cf52791a5acf6ef22e49d18cca55ed2041ce1844555aeedc6334ab92b724d2f8962b5c264750a3c11f111af0fa4befeca44aa2c441726bdd51de875962ee9d673d212577e0494b893dc9f1f2076ea2b3af4555c2499f13d27d358884d030984c59edb302313af9b802246fc50a9664702315ed2c0bed99864461bae88b3e40d254a131a20a7e6ec4cb8081b6282a7be3a6e64c3ad1a0134be3d5dbed2c6932403b629b448fb656fc6562b68bd51bb069e162e09cd03dab434501b48d40481cdfcb9daabd62da73ce2a8a3bf20fc615f166713a7fc8be1ff082195134ae95c96247a4ba918f19fac2c26c4d5523ac3691aa5a5c132a133ad894579c03b3da7628cb296ffbc0f1c02b26d2fc7944e67915601c2d5184b79ab07f6119d595eea475d0968bb2046fa9fd0275a0c9cea1a1ca459abd0e309f723f2d4fcda9fcbf35c0287cd846c27b58bfb2df41090a7cdda70a508c473e33e733ede6fe8faaf627228bffe8085ed2f40a6936244ab85aef85c5bca5d02e0240350d4b564f65d6f27d3b9097fdd8260179d0eb1fbf33905a9dd9b693abe278436450cd299399907fe14032b97261eeffdbb20785798ad16774c7499abc7565588beeab1d9b62c6090d89c31a437633e82b1c861d126619c0d2537069f7f30edc2a2b3f89c9c0ef202035d7fda3d44ed9e65cb9b50b349766205ea63e24fc764c26ee49702dc836b427413b6eab33a95f77e0eba02375da3544ec7ec50997d46808267f42baa67ab44ed7d28aed67c781f8849c86f18abe4ef4c52612525d225ad2aa9335e7a7b6da33585bf1d6f4b592e853ea40d67ac1eb14af71ddc3b482026049b7146318c5b95051158bdcbe53a46329280273f235588625a9e726ff33c793ee859d082bd3151694f0f89028945fed400869570905306138e668650bff26a7f1b02a89654f0db86780258bce146720f3a14a80fc0cc503cfd3265ed272291cba4651cbdc0e4c3d54233b67a994ec94071e17b10fc9ad959c6863de13a3509103ece71197154f810b28aac71b810b982c5f69555cb0561bb3ea3b50ab5ddde324b7c4c19d2fb1972fa2a3b74e4a10354df1de5d49e35a79a8291ebf9b2fee7f6c67fa79058ee19ce729c060131d2af4fd861b510db865088016e153b3050a407f3d8754ac56616cd460a130f98c50babc8e9d575658cbed9d1d221d8b00651cc208058afa0b8310e85cc80a7ddb6eefaffc2e24f7a9bb7d886f1ab89ea8cd108b770f5b4ae139cacdff4a5753a2cb2a986ecd8d1a5672ae03184621fe4e08e210fdd2d3253ed57408c8d9f8a1727bf22ff4810deba87d44c8ebb40a3d7b1bb00640567909b401aeef254306c34412933e4a83fa0f614c494617c85cbd19061f9e8f1cc778453a8f749ef7c45c2ed2a362a8a9333c781fde05dcffc47a7280b957c8e75dec96b78878f9f2eafd40486538f0dc8a9e39944c785d1369c52172e5804b865177c45ed0e44dccb5bf2f1a5a6ab004e73e5fea79926bd0cea3a79e3a93618e933c9c954fa3a37395753930b1fafda91895b12c721f475c1efdab64105c874c130a5755b2daa8d82f4e39a095d9fa488902783718ef9ad1bb24e65ad1c63d9567cce61e87f6b5065a3e2929eb8d70e20aafa251934ecdde984a68e2f1955c91ea19b4e72a7d97c4eb0f85d5cf6bedbd8b12760395cc5b1f55fd90b9dc34f130ea2afad88ab917a9e66a61aaeffbb2946c79c6b23b161440637c1e1b19d7a79d489f52d347dad34711c3dcbc2a220054907b8b7999ef97cd1fc1b298a2a2864974c11208252cfa11882f9bb40d678d9d05956cf293512ee9c19b81fd6469576f48e8d1ef735fa2adda5059227711937d7cd7fd1b4f82fbc2b83b198759043ed751758359cd6da4fe4951974b66fa6156f5200164607de9b8bfb0fcbb561625f65e683200c49793afb5cd2f7ae0a9d5b3e1aea03873e66b1d68336534a935e6574c593d589b44017bd5c1442d51c1377a1cf7a020e84c220038eb037822945d2066bf8bf94383348cafd4ec43f15f965eba005dfa38c5f94d79dd0173418361c2652c4ca4014c6d0cda75a790a6c3b6c77e75d0c172c28e944d97def7b9bf023278373652ac0cd40851f94470874ec892d80e4205840504dd402b76863e2303983f840333d48bfc470146927868b43caa0f9fbe56af036f96627567684cf90fb8bc94437a27313e898aaf71de55d93720a664894be15e04e6b0692eaf21fa7ed279f8ecee91c9fd8f467f3010c54e58b8c3ee3cf83588622120379851b4cd506724796c55f7c137466dd480ac5c23d954a913415362dc572455ae454a2fa6092439b9c6a5632d84ad71aa96dc8574cd10d4143870757fda0eaa43bb958222f8d4f7e664e680ee7214a31420da84cb1fb486fae544dce469705c01619d9c14777395b892979c8276f4e656f3ff1923bf5f7da8e5dc75a76dd63ed1027b3b2512661992528744afbcb8384dc81d9c1addc6d13ca3b4ac70f33e04fb5c09c61d7fd5514c3109453fba7f7376747d22356e95a6c70e4c2d30e43d4f8e52825e83f163451592793fb1c708164ec42cd069b40ef372faab79aeb5d08e02aba14c8f89772c5c113ad6d4c635d718db58157d815a4a540b357f6686b29886a4aea35b1704d1423e1ea514af9eff46b03f7e40c146befa2e4a082743a62e493c56a59388c72c3b78637e7bbff6969afd01bf16b2fd8b8e0759dd5281b2d690b5445a2600ba3bb5515997af3c8d70a40faa3c1b6087b63274a8b953c88c22225637f931d6317e1d340abfe51dce3c8dd119ce2ef6c68a957288fa3e424ecdeaf16857b9189727cf047ea352b7e922e6735381b1a3e739024cfd994f4354e97e3df4227f08e61f1badbf04485f10658a42871c8f3df3f360f35c3990876a23d97f40af8fcad37b82ed38419ad85d44b05b3792b69bbf0a538118210d56c9d4333fa67820e086f4e56ad466e606c84f4cc9462702153efcefb9738e769f499ead904a0a266ec8e39eae699386d3d1a7e69c01345e61371e855938d850fdc6fedb294ae41bac012c82413f3f2f3488bda9195b8929b8c64d9e603e042d3824816f551d775c02bd3e042014622d81ea1455d3d8680b9020ef09b143345d436b4465d050acaa6bdd20dce09b342c0965c976d7e9c25811478f4935897280789c09e052abb292513246b7292ef97b1517837dbf7584d7826355e0c4c82ef4bd88cbedad2485170a98539c7c72774d0b2540bc64188c89bd4c05e44be5e66bfae8ca6d1844ac0aa6cfafcdb2c7ea527bb247fb12b663922516994b220407241a8c15df45c6b444b39f42ed0caa334e947ecea01e0e01b4999fc8495211cd2f03f5342e3dfdb1f314381cdca1b12f74ff7cb04bc79ed7b1a33347745c388eb07c760114d84ca408d1c2c6eeeb920ddd7f4b89be9c9c319e6a46769846ad9ba88646391849671d1224732c42b43c5fb2675a15156caf76d5d332685408b87f92245e70db3c999e57f8ee08e4cb6b836568e291631cb5a634c018363113cd8b3cfb6bdb6e542bc74b74e574cbcda6fea681b1b81da88f32a6412873c804b49388c3e136da31179c529214288ebf626180cbb29ada0c0e92697a3809ee35d5750a928a5a01ea0ac8e4f3cb615b002bca4909acedda0f333409b6a8ed4e3245f99bef2a4fc43a73bef2fb7e0e3d0e7347db10beec567b7f6d1a0d95bd9ca1bec8a6c67c091fb30685e80d6f9df290de3f41b8bbb3a4fec9566cea4745b2db16939930ce3bd515f85d5b054d88475c0bda4162217b89aeb02acd805495fae0b5827ca969cdcf76bd89480d51970bf68df2eea4a53d0febb1f76761c0d640586cc66e62c8f85144b571096a6c834dd44bbf2ecbc343ceee97e0570b77cbd8194313241a8d6f814684919f26ca4f9db682b84a6d591f1627ef86871d79e8a3d83d84f62c0c77325bb241a1b45e6580df458b80d45efcf3ea95b322aa279a250de6927100e51c66d354e259170e4afd76033d9977cdc7e34b2846d1e69aa3ba28c12c3b476e67b7b868fa73353a5cb2b228ed5ef9e33f85869e1186e487e5fe96b16a9b5a272eeee82fbc525cd913baa6052c51345e9e75a87570bb2189594c749b3f56d395d3c95392e2f4efd9452f95615b5ba1cb1786d18f655fb0ce96ea9525d003f817b37329f8c345bf4eddef37d1cf1bd719028622cfc2b325821db19f7ba70ca00ae07fb9e6ff0df889c884134f86796a680473bc76ad9a9b8b79ee7ce6b782d652615554de6fed04bc06d4c5f3bce8afa8171c83943ccd4938b296410aa9cd437b9fe052d0d9d2c078716da054ad7b430e9c20ed62cc48f3b0b12857949afb94e68e2d7779447ce82038a4fa8b21276704162160b5ae71a9a9836070e28496a2df502a8c404f5a13b6880185794ac46de8f1e1450c069cef43213e6d7d067b47545ed0a2bfaff20f11d798c13cb7c0ea1a29814602aca5d6a7d1116f97fb6bb8928afe32fc443b7e03448dcf4874939d2fb9dc54ec2be43f11509cc9bfc49e80cf4552cee392d321a1e4529a54b904ac7a88097663d64d949d90f9df9482f866cd027d9394816b029639427f392b781a70971c310ae20e3591e59c76dec0c768c1d3184d78891321861333118b865d15b14f08487316573211b30fe70bd8cf11715acd5278cf10a3bea32221d5269fefbda5878ebf5e45ddf2f580b1e0a1f19e3bceae34a3ca7159d3fbb125fee3b28405df5d8e0ae8d9c9cb25ea15711ee33faed09b2d18771b047053ee9db5f54c8451d475a20e5a17b14d88d1c8301264099a13358d0639bf96f600c542a812424348cb027f0497f9fe7b13e5732f99a389429bc1f11394787aa361f293dab8b93b52a80a819256b34183c7a99e35c1017c6de0eeb1da011d07809e703394b3ec37148ca8fea80e8692c19ee376cb7c6ed665c16753b526e43c32ef1f1bd40ace941cb2aaa1e66452427fea2ba8a75fab091612876a1e38d134474b6dc3ad6b9fd9b3f94809387112a9dd0ef897e794285fb66e4ac3eb393087579e873d8b645ca62407f0ab9d8095e7d9cbf75008d63c3906ddd2f69b194ad87279b308c445cc5a508f8a2c0227180d483053a945664be07935c3d10bb00bf16f8b34444fb3566a0bf04d0df57bae4bdaedb39497190f3556c51d19fd7353f41971cd0b7ecf145af628b364c841c6d99ded3d8eeaf3d12bedc1288585c112809dad72c3a1141ef622ca7cdcec4ad074305f961212c497d5e3da196ea4fa4b87e00ba79012d0914e225461a172ebf1c89cb49baccb27c31383893d463c2ebecbc5d4e4a2c3b54d0e38ac21b90c5e8a7dfd820d074d70f2dbc5bb1c3cf01c3d8153fcfe8e7768d801dc8904f5f2a77d0b8c91dea52209aec560413126372ae7adc81f0b6ae0d884be4298a59524ff80cadf0ed8b13804f9ee9a5376cc241d093b5d04bb43d01633c1c95082b975a7bd22f27878793d7bb7ab12a665cfc727bb4b30bceff3740683a3d17ab1a07db3e5aafece1ade5cbd1c6506e4cadcbf36a82c44c8714899b699bf1a88637b66f2c9a529b2c3875bf03b4d20e451bb4411dabaca8a03dc5cf031028da62da85bc971de37975f9f4d787cf24de9f236f7fa543f59c33b8da97a6c43227be219de69717263d78605fd8a262ef3bd56c1264c03834d1dff8cfd00fa49b9a18e46bb76633bb89227e609978cd67678572dcfab9405093fc8f92fa9fe8ea643ea110080bd1bb65c107c6d1516b5d8ec820abfb7948d8a91bccedd2219c6f6cb123ae369adf1cdaf44512d425acb64e3229acd232da5259af0681efec2a944ffefeb1cf80774e41f96e488f2f4e3d9e5252364cbefc358e8a3603e4eab29bb9362d37c3a0d0da615934e2d83c51fcd725a16454ca4389ea59efefb34277ab9ec678acd1c8bb2bd39db618a5816098fa94d3fd1207fd74b547ba1db55ae113048074599f588083968a4b4b85c0c84248c2454c3560c5910b455b2a3c58afd21f71767247e475082ab953b7e45f5524e9582465da05eebfbd6997e4f992b2f4f5a4db881c06c0a4d7d67bc1f89ea57781af84669ae280a0b005cb07592305274b13c07cd274cf89909791e90d7ba5677cbc703b3ac5b6a2bbc0458d9430b0f4e52040706b70b28e0a3d51edd8c334f4659e53ce3ea64c5818b52cf7a23b852634ae66282fed75974027c7a2fde03d72f33a0405017752da647fcc6586db16759d20ccd138705eb10887dc2b1bb9f8dde688f2e05b4cb06bf1f171dbcbd80c00711a3b064f5078878e0587d35072b00350cc416c71bffc577ba38cfe9e967af0126ee13f70d83e033a285c5d09334bca6c27ba71d4e8e328cd8a3824c3210daa9f7d9a57878468ee0bca12ef01190798b1a3ffc4228f214d449333111590d5e9085957caec71d2b76522de183abce98e283929bb0c3c32741a628e284cf4c42337dd292702d28e9a063b65193aa756b9b1eda37e107a249721fa66e0f566e236b042477cc24498bb8e115ecfca982a118f28991640a8502217a6d9af9a44964c5451f78eacb86ed401f0986db768170609ef253a239de19d036e78063c364888868eb4f10c2f1111fc102250502d7c51fe605aa6002b85e909bc48b02bcf7759d2e4d8c4208ae03b9c850bfd5234c496ca985aaeb6efd7a03c2916e8861f80379887ace64a8e5f198dbcc28c6d44d9c4ce7e529bc0ca89ee8dd376702f2b4a8b5c505884c3894d7888b7a620bd351be945398c0a85df348a5c5e1e617938bd8799b804ffcfcfb56b9466af445b4c140b745c255f69a6c79349a4c32a1cfd44aca486ff290fa160413f9fd7e16088e5304ff2428d209c36ddd3b0f651d636f423e24e3320371b8b03a12f390054ccade002aae2422e84fda2f007b3e7f929ceb911de67daabeded28e3995939401ab1ccfed6cb211e1fb8e9c187518c63524fef89127360f20a76c851bd7c98df9980922f9671015a120dd825ce4a4e670314d0d5923d733f64cbb2ae0954e47e2e5c9cdcb05f3f7a0d2b91111e5c1d641c654b3f12f543fbef36f58b32b80270d27e48eeb3d44c9987368972cd5938890eee583a4528daa243cb9d2ade01318176a1ec83400551d746c05257377d79dafe0e0d5e1dd9e0445983c947223a40ab65dcef9f373b469a651da47e74725e73d077ec8e6965439cee220736b156d504384abb47256734552ea69b7e3c8c6aefdfd39f684beedfd5addfe6a32c8563234d1c9b611a3e59682de7efc3f9cc9523a56e9d1beae5aacf5140103989c2fc295ad7a234caef29f09fb5e6c8108683702b27debefec2078b7bd8f30d63b3dbbe5a09ecc83aeafad2f8a7bca3eb97811049ac0b7062fe9bcfa42ee160a889461a9d8480ec0e816c7beda7fd032f960be7adeab3b9c8caec1322938b0c99fb87ac18e95726d3589a2bd914ee15614c8e2878ced75a91ccc2b267971f6206033d6c40842659c1870f6f464d0c4966ca4eafb8389cc5c979f83f208c3a8d5ea6a5ecf2902a3fdbdbe7d55b5e16bcd1e2d120c58a4eed2ff44c5a23af32a21eafe5274ae675bd202b1781b7b977bc458f31341cfd3a5fcdcfe4d16fcb1bbc71d36bb536bc0b41860ddba15cd57ac95a4ee5a80c2ac4628206b616561048263bf22c5ff297ba0589ea6596df46e90b8098d1606bbc0d8cb5c1374f38568380e479d9d93ec01835b218b3ed3b6017d4f146672bb16272407914d98677cbb03d85075738d915435a98ee133ada2bb344c77f03229762d1a06247fe9559683c493fbb80a63901fd4f30c9ced26ef9665f39a0cb7827a2303ac124d2cab1ad8efc8815588641a00990e4063113cc3ce6e48e056335808fff5bb61c61091b26e2409cc591941127f89df7548fd6e0a8fdbe87e72265b03bff47b848ce41d279cb386505bcb86e1512a82682e08f039accaaa3f1d821f8b8ea833e666c2bedb0bb1f41ce8b1f41b25704321c5dc7982e73cbee39979d9c8b07a7c1ad72f2455de5e6079621ff4397c95624d4c4e44f563a56fb85ce99d6693187bfcdbc7ef206e312f597f6ff9b4fd2ff8f234c3c873d66afc7ed2093f3fa336ca02f5d93830634cf50cec09e5dd3433f6de986aeab6a67dc425b6d72fd445846b305e31347b76ec7c9e7d26b0228e50bc1cadbb018cc32c211f40973cdf5a195e6c31f891c26d75cca0749b4b01b4d02309819702979098e58cca06f06ab16a7ed5b09ad5a57cb942e6bcc010428c38bce727881b00cfeeee52de91335e62b9b31c3c7e1ecf528d05502ba54c4e92d902da8bf41db79a83351565998c25dbedd2dd05c068fb2d08325c480095ba9da00320370fe0cdbbd9672169adeb1c5c013deb0c6f7824e796a75f927ba961c7da8c82de7a2a60552eb63cc2e7273ae33caa3e8c15def0aa967dab2c1235d47204ef60e8ac14ec39c36dc66272a09d368693d64e1a7d8548b501b9d64436f578cc0a91d7fa1ebcc0711a8a396a00f098dfc105b8fc4450ffc77fc1bcbfba6881b0130f04f78fa6199ca3e2ade1e464b2d54c3d9d36f3eb5f6c814e08db0ccc8a76b8fe0aeb85e22cc28c6b76c48ebc9e4c936c8cbfde80d50067932a70488eda1cc5418fdf0c1161aaa3389b47e3568e02efc6a8476a7a1c95edaf4cebc1693e46c57d739fe01def2e988f36c9a2d86089a8205a8f66a017ecd373c5fcf5c9a7677e5bb2564ef4187fd61ef230d5bedb92d1f3376296d6a1f738f8037c5425e256e2d54a054a277593a3698de56f2b439364c9a8ff736e74e993a741df893fd7fe589cb2097de9d4892cd0fcc27df14b77d7d6e2a4e32781d442a1e5c3ff5a487038e8bfe5c3aa06a8b44c53c4e3fea58e58c9d6d59e43f9c455e633b397bf3547d611b6e0fe3263b90fd73e503155d8135139d6ea9bceb09dfcbee6b68d518ef4833102308f05bdd9bf5e872d11da13a5cea7090c6215cac0eab1a07306d73731c952939fe242c1b2898b4ddc289d37ffe24a95ae4157389dc9d49c8b8f6244b2b55150a2b45e149a04005988b148c8820015d5117284e9fb3b9a927bfdbf90611f5345564eac866f1a22c833b2af3582bdbd6aaab9d683f4e45240ff7aa142329b53ecbe889501b987441c07c799f30585fe236fa0e10660b4c4bb9bc5daef4d047f0d4b9e69de3d5198d97343f303090520fd3832df14cf52cb583e3a6ae78a3e313981ecd011150c5f52546ab710b1d5064abd2d6e1a250b1e325e8dfd948e9b4635af7f821fb0f95e8ae8cdfdedee00b85f71430b41905ce800b2fd544a4f19afa98c9ba5019845343640308146e5ba8ddbe8db12775e516ccfaeef95d2ca8eb6b4b7c1fac90cb34a6f0fe87649fc06acc9d45139c4b2d0f4447a9589fca8468efd3465d36e01ae6c6a556df1417ce4da61366f3b4359e082c43f7832b46c64f52da8cd249d3c30163eb948199532daba5c35013f8ff5c0f246adba9cf79a03f0ef80038feb71211ee59bab93d725d11efff58b77a708bca225c6b40eb87bd468051d9028d183025c67c6b9b8e7c1907b8ed204326cc6ec953a385a8b13b532a145d74d78808b17360117c68bef7be55f3d8f9f1910d46be16b86c3f8d810235cee45e0945e111541407b8f3898c8ba21d73806765582bb4928b726323381f05acceeb205f0f65fe50fc70b016cdee488d459c4f6215b24f8e0f5e5dd2c0bab0a1b2adadddafbf6e10a9ff2ecb0478da5d73ee4691874bbf53af8dad139cfb2a97cc9f3b5be8edaf5602ae0f6b76fb911353420ad352d76693d5b827e4604f7d6ccc57556f35a9dd8a62b4676f062a3f9e7f764ca50b4bf98e15c5fed57208ec61e6aa52410365a32a65e31a3e93dede1b81ac0b921612618fcaf5137f0316924cb2ba91afbc3f088b3412f0ee0ea5c0f05c771a6c00f637c2ff4dae3abb303c073a4860960403e720c204e7f1fad39709caa27d337b3d8f572b435cdfee518661a1fab8e64c7aac1586b56135d92d04ee9f4d63bd516c0b8373841cfbc5d25c7e808eab1e4561e0a7a93ef7c731dabbdf6405a5f8b1bdd92a736942ca38c1eb349ee051910a27e10fa7b91ca5c36b8823aabef1560e81e719523f575bd954e8e7339e6bf5a9e8898e34a5044c255519bda83282a5114250c62d9ee4ddffa2f527888c3ac010c8901da01f4464842662a07192298eb8ceac5f06aacd9ec8c095d83b6fc4750ef763061710191f7f4c928bf0213922e17cfd6536e7b33fff955b81169749f1a24fb6bf3cfdf9b97156092585e526e78c3784ac03e78a6972122ec1f555c61ad2cff36ba604390421589e045a8d8b84c40507adf6880059aa38f437afa672929e0c3aab215180009ec6aac1beee8a69193f681eba3c8191780fbf1e20b8c2f1f62a7a115b98eb1e41499b8844d362555183c6e3eb8d203c692595ddf8712006d00f7be4fdc6b42c90b9d761a1a391f2804a517fbd31befa9c2c66238b965d8aaa705457bf331492926aa7fa412072f2a55d43b667c3572a716943480992cfbab67cba785ec4599643d24691e11e90ef8a5fe71226254ed1578e01342370d100a0b908d55a319bd39f03b5e4f8ef37e08f08132b861af9d7cfa2472173224eb4b1c38bccc3248538457c8053a530a8ae51adb598f00526cbfccc4c04a98af0306c82a11749d0aabbd1b6b792271985770eca1f4997a4ab0c6d8d5338515f50a40d8c8459d845a8b275a4ef9042e753d4f5db404563f4b071639572520742d9187634997d189dbfa0de42ef91b8763fbbb5b317d6ae28543b9d112125506f9e16be829ca297f0c1b3df5090a63763051609219350dfcf5fa94678abcd5be8ca8339671a17cd1c03ff374ae99f51bd1603a2fd9322b3e8e4cec10fa41f4cc756bc46f3a1ec5d0f301ed8ec67339f317579e66fb081581c28588ae07f415013c91c18fba3b17708d838dd92f789e061d1356d24ac02c0dd0d8d3ba44f89d5c8068e967ceb94889e3e9e87064450dbffd478324cf4270e13f38285755964f537853fc403042b298e68b8368f58259cd3b0ce9bb468b55bbcde7aa59f90b3b4629364c128dd11106a82541c474d47441defa9a7f9938c439b8a2997211d64c9642a607c27432e8c6ad1ddf089a30ec1f4509bc0c26b922cb9fd16c68cfa4cb6db18fe540352eaead0413a5469fbe9a65cda02c7ee85ee788a1fce28727b3a4e2eb0d320e1b568b040b8c4bb85aec536a096d12d0c2ad7adb391190e3a986aad53d1cc8640f6f42e1375e6a17673b6af547e90ec9d18f0c7e447f8109d111f3446b3c72ffbd4e54d4e95daf9d7b1e57a0a8dd50c5395789b2d50e58a0598d51e682997d6239a6235c1e3c1d0415cd0226df024a5d5a4c27010ab4488784b62d83eb2d66ff073183d34b028a101ff8c223eac5731199bdbb907c02cb845d3abd65008a26b44b5ac8195b12931140e5271483179949ecdbc3599abef333c0305bd4f7005d9bf6366428191c05f3399ce9027f82b5f5812e71a14ed8a77c4d0cd7a8b9084e4b95c997e3b4041a64a454ff9410ceba45f069ec5ac8f434dc2172e147c05c3c255a094cd68609be864c7187b95aa7c5ea772e295581975fdc8624c10bbb847b41986a1a18f6385930a229bda2ed9acf82e043662dbfb357c6bdc288f4f2f1dd9f77ca03ff6f0466b223fdd86e13ab702b56faa9a1d2124363f76b2e59dd73d62910cf27ca79b55bcd0c179e0a1eaaee09b3109ccfa525ba8aeb28c5c329b5aa16eb2b00aed95bf1f3d53ea74f7af1c0e152137d7690c7e59a8a7442e6505a20e556a8c6d0361215711ab69d2ecf02ca479a40cf4479b4d47c8e082d13e4a78a1fb250f3a00e2a7b03470a6e790f266847048d73c388ebbe0576a69a21867b50a56d3240b84eeb1050ce30f51df03facf240fc2737b75f20f8c1f0bfc00d985ab818a44c9c3b43c214af8684d774b88823520d9986bdc0030b48be3261e90744ad06447b62123fca6aea5c073ab635d7de7d15104668d5471c72af988a05676de3d14e2ff8b2dc947626ce4987912034bb469ec6d324f24d2e0ee99d784db08fe7cecf5b1a85aa0558531a752b115c0f2902828ced5ae075662a5715beb97eccaeccc5e2204c011cfcd0ff0a73b3f79512a78d65da5f2c448e41ef030c2b793a33b53233d8ca4caaed376b14f1439904c84c6a534eae9459c69948654fbe1094bcd09848c0bacbcc65289c9ad8b5acb7253c9e63d59047c13374ef98a28495f3dda9c917b0174726125df9f9242e258eb46ea183b467b38bd29773a06ae66edc46e5d3ebb7c30b5c5ba2d6980648505cb5769e37af64ecffc9c9fba4c3e1236ca722b7740ceb09f8f65017f45029694eb6ee9366a50715d36e507b1462eee151948956beb9ab25824611e58059e24bb288efb57bc0ae052fc7184c732962b75c3f599088e0b2145dee4b46cd260e50296eb3c4334b7da4757d30da6ee8a3dc04250ca2ec6158c4b39c02e002a30545b021a93569152362e60a4fce5e2b0adc835fb1fbc91e79c5d32e3bc4491944abefef218349fefe0615bb64ec6086ed398ab56074b98787878650a5d84ad8688c1a4815f5be54c546513d381a6e744de59086391621abc8062f4523f4a3ffe4e9355cbc8ba6455ca6044d64691c215b44bef2ef281d8a2074969fc411ce749b3f8de30346694764432f467d152053262610252f89f67f47204e3152dffa2b00d39b122377c1906cc63ad40f38e4bc1dc49a3606060517cb5453d0434545ea8e02870b859d2797991933fa02d40022fb9f4ec487b812a6b2c551656b6162d6ab1759e212cf2d42872a43e0e3ec372d24f37d0f7a0e6daf41a95c80bbff0e0bebe6488af7971bdee534414e3251f37d3ac799860e7f5fd4b5a093b152940a8b4bfd6a2994eeefb0eba935904531738fa657f40969208c7f7f2994a5ea02f7a8ec9d0dbc9c4cc21d7bdef28a753fb66440fd19f869495b0254b3fb5a76310280719d06aa424d6c656db36ca44013692b63648166333396415c843628f94abe19067717ddea9b9b8f4ec0f5173844da8ddb29f5b633229295c741d50b64d846a6919f798061e383158e3dcb2dc83e171a1a0db89c16000c92fb1e903789fbd75ee15ffdf1e465c35fff4364afbfaf0d82d4a5c1b64c31369c2a72b2f484b7b5c773234f066760f09a3b486519ec1804b14f5ff005528f40c6c140be2b27d7a87ad1cefee41d59701c540c5fe38cb3f408a28ae60fb1f489c352821e5f3eec3b983c7e84f070902cf36a5f0329dea20ce823e7d52cdd341faf293e4d3fca3b6b4f59a01d18d4fb3f0c8ecfbf3645782d48937c02f98d68dc1790220554f573186f890bc1748a4597574a351d09bbe7cbbb13eea38a36032b7520b900d31dfa05b628ef1c880c30eede5673e8ef87be4c7e1eb3e55b866e0a79dadd40aabd51e921593fb3eabad598bcd788bf04b2c9ab31c7e75d4e10c224a11e9cd4ba65f08e3cc21b01dc220f0f6f5ed04294f40b74bd946c45b5dcd841042204f87a0c9717aed7ab3d2a7e0cebbb3119690915dc9aca0012a7ef3130b7536e2e6ee58f192d5b0ac29fc0b596748b9540714395b971c789a720136ea2fa8be33155595b6d6d6772a90b580180f8ce6841253249b30222e85683a470d91e0ab8f4b70bc75d09e98134fd31aea8ec283a8f09de0a7ee68f366764003e7f76ae6ad0b9d7cd06599c9ef408b1dc97c52c82833c5317b09a78b3d65a375e56e4778f4b56e9b52f5b88efcba089e1a16d9a76bde1d1a2b4cbd5482a26a1aad7a7c7943776cf268f8881cfb7c09d80e120685fea9d962354b9b78278d0108992b8e7119a2e11882358e84b6a0ca60e7f372ea5a4288b6d51fb7484338e9e5e9c055cb4eb64f71eb6fa4a97ac46df0ef065bfd2fbc7191135d0314496d5d6f6d0d4ea9d15c51499160462b4659f6f67cb5066df22d47c1472e245c4bd146381a3aa6a735dc80d6eb96ee6631cd488ef3e049664085221c92cc74329a41edc4081a040d95204220ac4b61e7d42aa30e2a0e28860eaada6da05f6e0679e86e822c548782637334fabbe432b30e7dc3e7d8b0a66d3f8fb007fb6a188bf7533a02dd17b61429582066fa45ea122d13a8f2f60397ee7b184d750926b9fc28f8c4a6d27e452aa3b6a52f239f446ba9eb5507c1f3ab679afd9efd684f1845eed7a9da8cfdbba0bb96f3a2cac22efcad9e5dbf43cd628cc9c14b5ca529d572770bdd9507b3de01fc7ce78bd3b4d4fb9ca8ef177eb6d58ddd0812329467668e28bf966fb571cf5643c7faea3059afbc912ced37bbf97b964c2784b009f2928709a1825dcaf8ec062162887248ccd996342aec5b84102d5a6808ab3063cc6f35572a62e1d60575b5670401b140f2f797353a151969c240abcb8a20228c53ecca4065eb49f49c471e4955b3b000f70e0a4be99a45fd90938b70e66deacf1b407386deaf54dc40afcda5a6fc1ef220c125caa2b68bf22ebd66b1a2b164d048e9b91f043cd90b1eed4d2ef84b7a24dd553fc67acccf4008e53f6b294901957b12f5931b6ac2b113317a808dd0198c10ac51bce247a1bc793b8d6e96222823580bcf9d5e2f028cf6116b88784fa1477a281d1478ca9f35bcc636c3f7c5a281d85d147496f99f93eb35db15e8cc61b4d7efc7d2b79128fb569443dc1a64dc61fff640d6f6c766e0d12215657bfc6f62c4750b4505f78ac4e10a64a32fe686b7615b69c5c0ae92c0a22700f846c218276444d40cbb17ee03299ae07c477e25768117b896aad7714be3377a34a3d45c14dcb5d9f716d6a977369d902cbfce970841ffc4f5af51501f3884f3e731b1a35867322e79ccf47e886d3b51a4a280833144b7a5474a04d89b0964cd6901b48ca40eb973c2bf394752dc574425f982846bd873182f02d91024493f710e6c14c9b46c4a1c08594c76df400da0f2e01a8857bfb5a898582d00ae02214b6e6ef2c3454378e5c21489225b05db4ca9352e449d2f2eee4e5bdda8cc85296e8ac50286eae97a543630f61db0d222c49d757cc95473c494f063da26680a3b9513ee5830a644aad338cc6feaa84f8582bed672ae3304b0598e44f3afd3aeafce0814fcebc18e38b4355e87072329138114b99cbc3556467c8619dc2c96843a39cf5423fe3c5f823b705ca74b8e12b46a32ad5bb82ab18456b044bd7f221badef67c1bfa5b8b8486a96f32ee7604e8136b2d3b1463569c224fd5081938db3842338759a1e62d8975b51cb035efb896ecd57b988c9f98db4b8094598883b5774d6e1dee0ee66a529f4138a8b523d5f4f2ba775c60f15d6eae4df50db6d3903c251f32f6e456bfd982dc5d33e1a89a497b79b9dd5ab2b5bd0f546a63e5fd02c775124834fc825ca88a773ed052fb968884c1f2d86d71db0831c29fb86fc8f1f562babc4a55819fdc726bb5025497ff15f070fa2081c07cc1041d733fc712687c483b40499f62515e8b23b4cd6136430e1f8114dcf68937942d3cd88069bf34d093fd49edb71ae438b505b0db13f7fa85fc3c3f90a70a73c1eca04b893d396d5ce548a608b56aac3f461f3e1883c6b6145afade6c7d47486aff547ffa1e309464cd51cc8abe60b32dee670bf29f10c5d6c2f7b35403ccd563006149c698f491129f04805ab7acbd67fab2c9f2bbfe74ad21faa31db21bd92974f4ce58600d52c957c734a6dcfc42d1043c1063cac03d47808d022b21bcae03ecc905fab4299a5ea87db60ca89c75f3d0d2d6df536c884a9d4de24943b1010180183ff4cdecfdd7ff9241fd9a3f61ec02c3eec0649c7ef1927276019a35cc76d40bbae92d2ad134498f5c8291206df0f47f7da3b3add1c53e52f71b8b45a48358c0baf7b80207945d21a5a14b029f29c6c70cd2d0a7bdd7701ae00ec42ed4f9ca8876dbd0f5a4a714e3329225f7429d8bef3b3c99d5804b695a2a18d2241c265321802d0ca5994d49fd05fbc192997f3035d7f355d2f03040518916a39b8fff20832233f1f635301b10479b9428b107196bd671ad3c79b062bfbda63a66242763aea0c90d8f195331cf40e55788661425ecec129d767ab7dc62db50a9c6a84f3822163ddd55f8aaa6f9070b4fe71b9257f7fe4279840e06a57414ff1fd6f14506cb6ae73d3e82b04d2cf3a883b16f3c95d1a56046b6b242048c0990072ed398f2284a98ede53d156c3e4e8476ff4f68ec85cde5dca377c65af6f9b4c88d5821179841d8db743c4295d300d020c28541c4b5389179e5c20b3989843763fcc75f9d857f5e82e163c75dfe661eb532b4e5578532764f12ea9714994ca643d5d1029edc9baaf7000df0d511dac74852796846c1140f720ba2c0b81615470467bc1fbf81af2b421fa74f0a7b3097fa2f612a1c85999e5e965986774b936cebb08a1615d8f89af748212b572a633b99807a0de10d7eed93b476685f9203cf8e17dee0cb5b3badecc917645e8fe4fa6bfb31a59689760ed39378155441df9f182902d7939b78b6dc8a6cd193829dd2599a328ae56e931a69485f2420d909b73d9da4d84d927462421efa566b53b5228a0107d2fced97c73bee3e59fa9b712dd61f4a8197531e9224f95e5559bad5b270a3965b34fdf71ee4cb7b007dddc6e84e02a8610ddba6924b7a9ea6912e07d494c8523827b1894ba6bdb50e17f14c40cf92aa43dbd6a21fd9b01eb48157b87166b72d1c59ca7d6eb591d4bdb5a9bf96433a7e90c7bd05156dbb39fe5f73f3cea106fa4e084904765faae98450b1e28076b2fe166cc501fc13e348389cc00953170fb69d8d17ba463bd930a6b2fac7a720f214b0c0800f6303326f030a2d08d7d76b8d8702a916a057510362d2b2dd0484e9762b938f4b76836b77c4f7883706d9d1414ecff5c94e9cb8cf875985b3e26fa47e7ca633fcd83da2d6d97df527ce3ebc575aa9aef2890aa645f1ee506b4524da4930761a56a4536f3090473a97474df0f1ad17b5b879d385ba251ac5f9a5c64b7d929518719896832d579e0052db554ab831fff57550b9c2cbb03ab063933e05b1c0293f43044f9dbb623825ac23e12b1fc4e674f0098f1acfab472b1594b522515a416917106e990b8ce03c3c34091291c4e8a813ae3685f0ef1a98500f94308a1d756dae7cb2a5f6e658950c04e4c85a4be542a479914dc310bf9975cf4f322f5b52379eb11037ed306ebfa3655f630ce5fcc01e9f9c98166db4f5fe5e623db08419def7fa089d080841320d70aefef0bf2a4509736d4381f81925b79554291f956d85ec7962c30373e2240361958d5fedfd12f05eff5a5d06742e4678de72102b8c0549b00b2d21fe392bad65083ecc6012a327240bbb1ea6a11677274c30acb7e1e5af74a3b836b195388c735e1b1f97a0a6962a55d1d9f5c813896a7c0b1d1625af4bad9f9cd41c8c43d778dd373bf0f9e755d83c2a86a444a63c997ad0447c40f54632fd0670905deb60dba56a574d4649c51f7a314de94b19759f6c873753408ae49f71aa7b714329dc36bc202a6e976d140ff3529492b5c87246bd7fdf145c56e8408bde28f1f9d5e9feda39d0c5b952a073dff8627b7ea805f9d57b96121966ae616751879a27bc0cd9e7671d894aeb0148c3f6c6b94d53e09bc35b44753a9e4d775fb5eec89dd14b66cce6f65938355d55c0c5a3ed5d2d6ba02bf1defc8d29f330871c5200e9ff49c4ab5f0ff17159cc96ecfbca8520672cf8c917a4b5f3c632e862609f695b774ef219ff7bda407a08f15b0f75b81247dba1dc1dec5cb247d8dfebc17f17520556ada65aa06d490ad59fb8e6c49a7d9f7736fa4af0afe222686b141fa0158aea41ad1e8706f3fc1692bc32e2157c2a4e200350c06651d176e9f0fa304d0cc72a4931a550b6587f31c7c54ed61ce0e010efa047a58c50b2a57fe8b70e481b387e37da9976ac9850bf24c901ffce85072cf5865dac2a908f8b882047795b8bb78919c2607f8dc3c5a718887f70d08520dec343fb623efcb9f448c1550f07c1ba075b910c8f916b65450faaec150dc6c2d3cfa2b56776082c831e885cdcb5b69235d4497e65fab67bf4ff8839ed58004fdae901f09aa887e147bdfbf5d65c65b1999084ba0f27058139ea1e60c0d37cd68948194b7c0e8be9f585c64bc6c5100ce9b7a840c1b06714eb6f7f4e2cacf54326a33a49d6480b3742d5e3554f47ff171e187c51eb6aa0976ed496cdd87ed71aa3598c8c8691360e181bb2247e3dc863f5cad877b7a5eb97135b53dd7176d7dd3006d41744b58b861f889eb4468750ee73278ef2b21ca1d5950073c1e926da58250b7aa7d6478c4c784f0bed51d158f0ebe6fe26330b56c7d07eb6809de72265587175e8cb17e3ce22a05ba4500e543f3fce8e5685017928857d4793454fc5166c922f2cc327b8faa5825437b6d4addab460aee7116537f3cc418d47155f41b21132578edc403ea7f62d4e58567f007ff4c61ab1cb90d0cfaa09a8277a97e45a8b6e78b162a717221af6fb13d515431aaa4a5f1ae85296165a6ac3363d61740b3ab36e1edc34e8e73a022d724bdcd3a980ffab6e1de5e3e8e1ffa931f263d5af061e1e56a83d503791bfaf1c2711d1ee8280d94c371afa5baff85b174653d6c875dc2a9e609f4484b3cb16bf99bf630eea650ee31b31e9bac271579a3eca32efc7bf5eb044ea4589c080ec20ee8aadb75cb7bceb7cc6890a3647ab4a28236f08a7d7fd1d82f40b82a5c231143f6261e17307c6095423d57bbca23bcbc7c95d3a72d2c35b76f67af91ebff4e8dba568c277e13978078944ba48ed8b8e1758a17236f7c5dc809936a37e564f8c8d9d732019dc49802a5ac3cc039dc8c795acf1523141dce6eb6e9aa19fb4f72852806595ad795a88ecb557e5e7d3ec0b85d692981b1d577d4c1668b822efd5c3fadd2f4e82fa47e701e65100a11d776c292f27656edecd981e89245be302bfa8e5e81f18bdd1cba40a5ec19656b02e502fc18be709cc1699f1afc16ca9353829eadd98066242e4f88382bb879148e9da0dbe338fb7d083b6fbe66f3c816a3b20ff266889bec9a43932dc638867f85e655f1384c923de503f4c1af2afc68a0a1fbc2297da0d7ea3587929a9d2838c8e09a535aa4b76d6a054e267e3d9132bbcc60447606306dd0ced5c386721e0403edb3d8dc2b64d09f9a239d512b04e0d6b3814196502292a118021364eced07821573f4cf3da7d6ba94ac75a1087c6e7a78dcfe2f8b87741cf156de9d449a58e552df95b6cef5716428b37354f98f62023887376ab4824a55d27b8852181028d24bb517f0fcf60695aa465575b7d16adc11954f650496f52d24cdc67593ac11ca4291676f48fd23a342dd4b685da3332ab5f4991be8a6c6e89c9df6ca31467d49139b90cce8c4d4c06ea3b35ce854f143f1c03bb7596f7462b25f9c8262d49ad7dec663ab49a4ae0fb0991d0a35b6dccb1f2e7dd5be6ab4a33f302331fc2b2798cdde9b71596d2319478362bf842471b7ff025bed5e28ee6c509cf363e15bc4a846241b62a212c39e895aa38478592d1516ee7b5b95c1be73ec1f5a1285a9a09879cfb76eada1f960f23f59eafc89555a69a21babf1e5978eec61b5c6a626d0c22865e14d30c6b72ccfb049de909c0e5893bad84ba0b090495ebb2e3f0ab28a5ca4d2ca62b40030a16c9dffa9136916adea9c9d1ceae7038e2ad8d831a2734b87f0d7b2f6f4358785e9723027b308fb39385b8e4763e6e67fc9b5c8ab22928039684407cc98a5e7d37803e6f4c1acfed4bb1c37e5cc1963d3bf4c743c06633c68fc7dec89c9e05e61073b5a5d4e9398ddc8c7e0bddd5f35923c931b3d44372de55da5b9506a1ce657a24645f93b58010077e1688aa9cd85404b7c4c61971ca5ed1277364535c2de422f01e082a74240677c8e6289f66e51350561b86de8f2aa2441913567d00c7aefb18b124e53ebf057edda8d7f188b653e06ca313c8ad9d723b8fb32f1a27bedf5cbfb6e1186c367a54d0fd303d7bf0f4898164f49652ac71e0b38f0052b202d93218592a76a96372ce426b1b60e2959267c59fb08dacb26e24d24627e63b7bcf632f6d3d75a08efa32707fb9a2bced37b22e81c2d9d97434ce3a6a7b0ba434fcfd6017949e25e117db16365681a49d5cc4fd7363f520380e6a37518eed6759fa062b811c833dda6528674c0ab7c2be5159a62e8c2abede6270149c965c03a01456f581803ae4773547491a52c8b295504bf3492ffe5bb220a43e1cb6001fbc75948f49ce875b3e055c1fa2d496914d7f49e98e7a1678b2351aba528ff14a2252a6c753bffb0a044f76e813b84b0538fa1060144887e98976c9eebc5f89bd03a3b4e04b68088230889623d728f9096b1b29f97066974153456f15f5fe6aa040ad4df7ea73fe6d737acb29cd20e0b87b158d053836670a23b31c6fb27fc13df39f906dc7a7289fba17c1e3b5d0de847b97058c6799d409d78a93f441be76eb8d0719639cbe3bedabba07ed830c55704aa1054e6c08b7b26be44bf6345fd8bac2094d383bb1863e625c538050df18cdc375352f06e6665313d6789299d5692393fd1d468ffa8889a866fe5a6f9aab8288b89f68889896e146a7427c5037697a0eda944a3f4cdd0d98c308fa41d397ca52fbc7d653a02f5b2ea7f08e00831546719dd025a379c9fb87fa5f1eada8d64c1c3c8e43ff861a331588160a9cbf19027d095e9d4d98f8aa358f23ddd8b598aa89ae982d8431b853aaeb7ef0d859cc9d62c7e864e4f771efb18144d34e1df75abc740e2f84820a387f1042f21e3985d25cf354388f0b6632d949cb9caac901631b9318d1e92e7f5caa3842bb485ad398d724f584b910b4b985c743128953147c49ed9329acf4cecbc7ce2673590d9263be40a25f640c19d3df2d27d04cf07aa2b37eeb8a6cc08ca470b6385b85ba5be7a19702608ea502acddcd01486ffbfdfcd5de33045c3656954c9ba1be85f4f2060fc9faf57042bebbb7633e167f2eb34e47e293714a51a3341ad46252a6f6cd9ef476d3de991030cee72ddffcbc953b68367e287373fcdd7cbf2ac1b5a22d8847abe6ff29a7577d09f8bb8c0becb6fbb2220726fdf21c3deafb67ff65fe7ce1f4e3867c8a6a818eadc00965592df155e94bac654d4c3acc5cc779abc3dfac21c553dab8f72515a061bfde0dc546817b04189831efcb38affc2f17725eb6c234247ef9672a1e89c83791321e0852e196ae8094afa8fa1f5152949fa0f8bf1f9f465ca3a994d33f7b8c825872c8dd086d3b38f8d1f7874ed0166668a903049ff5ae76fd66343d5d093fbeea8e8036d7688ea6cac5bef200826943caed7f0811df30412c11fbca212782af7d19c8e913e45b6b7b46888e1997ba42e8592dc03d6e03f03078b42aedffd213bfcb1f592e4dc31725c33e68b7bf48057fea93548e7bc4f65debc9936efbbdd883e40e025bf7441c6e357b4669b008202f61ded44344c7d0c8b5e9c9068beb6537a4c2b2c3a7b49ecaed3414fc8d9a9609b38f623b0d14f1533e27264ffacff7aeea69e0339abde8a9b82e4564bcb9d90a9251a35217dce79afea1f3f4a311340915afa33a7d4a5bcf6d2feb0c2437cfe7d4a5f0d938dc078240d38a3d9b7fed5124ccac30d29f4b09ee7c0ce7ca317a3e1cc91623755f112d9fb510669083a3f20341fa0ea2ba4857485f7ebc7069755aa734ca69c14360906d9164d84c5fa62029db6416cf029f0e7862ad9d025cfb6d1f3f11bdda828310ce5ac863dadcb615b3b931a364b0d34d101d9134bababbbb8273b3e7f2b9d411f2702aa5b6208df89b9317e2d9541c42e2ee339f4988a49fd10310c3b3562775608f19145b66cb5c34046054878edecde7831cf2d8ba59f37467a525620f6c728434484dbbb32cca6cca8fafcda45078c2d3f2b3f27bba120fecf4dc575610d4229a0acc7f9498cca52a28105ceb7817f358f7db4582d9aaccddd98f5638ec5b1e45cbc7a86ba177178e9ac0d22d536df35e5d983e29fd24e654a1e196ccb60ea7c46efc720b94b39d0f86a0fe526a68b8cfb085d8c81f5f8dca0aa9efadc06858e3460e8e18cfd92f6dd29037017899a9b7cbe2a88f623383910d92d5c36579180ebdc884f95ca153a81e0e27eebfdb6315bf4bd2e9fe78202ef21cd02d1de1283fa8b8f07b76dd4012f09cf85d141978e4c8b0ca5c1ed57f5097f1e580b71a9ebd63bc907d847a4f233f60ec5aebfb7d059840d6d6ce2ecda2975ae2459ad4765098b69d0040c23db036a54ec11ff7cd04a06d50dfa6fe90dba93b3a37e47d1dc3feaa34047122dc329d769ecd06878ae8c1e912f0b9773e1fd2138ae19c70d3dcae23265eb67005e042d415aac143326cdb63c3e510632e5ad72a23fc46b8806810b02c1ae90c6054cd09b4521e61b7a606ce456d8ecea77d1064835b88ae6eeba25c81c943a21b365d98dcf830b070d590f74d5c20098ad43b984220bba0196bdfb4bc9beeaa1088ba4d307ce6675b8f1f3b96d9b940b82cab9f7f592673bcd4b6b4291ad1269c710d9b8b0abc45ec7ac7a85a2560a8f3494432a8bcec00a73fd92046ec9f35b22df7c065b2843bd1a0c3284c47b7c1d47437a70136e0f2040bfcb761f6f148eca114cc925b37fac8ec002a44d804f0c470fa8498ade2733158ab1b913c037c87cfb4b92292f790281b4609bdadee8b8c8fc23ebc390870bc157e6cf8e17303148b2a60591679d76f096378cba85a15f07bf357a085b825fae9737804adf64e537426242861c1eafc08e3eac8380ea3898d9d0a553f53374270a4be45a7fdc088d3f8923ff4f1779cdd4c2ee98926b855985b13d1fbcbfa42eacbfefcbdefcc464e88f56aefafd74314526e408b3b23a434c741706dbd60926e0db4f99b7d8b4b8b5e2e2e2757c9bef8567be60500096dd4ddd53b0be674f347f5d224f569a8cfeccaf9e80dc5125931b737873682cfc43a62704b91964ed8096c527255283c3fd0a6b8f6bcb963380a91e2b085e8dacb81e6577e54288f8be899b6b1f0c2a21672ff176dd1574d6bdc98ecc1848b122994e7a6edfc8d29d62d160c796fd3e5fe91772476be0d676724f5a27007862e0764ef792c8e8598a79ff4157d57e80756e57e29a5cd64ddc68ebd43255e6f8c3cdf60f548f9f98c719bd7d8cace6fb37df13903322e538eb19a8ce3e30a37e7341e7e8c511a27ef302814aff8ff40aa7f1e8d96f10cb4991815fa9ab3bcf7cfda0c03adb5e878ecd99f3f40d31812b34e8034a5bdb6dddee350734a9f6ca88648f63aea30722ba410a8cac1f7a58ba319b1c3b04887e23a85c2ac01397ccab62f1102c705ee10bb878d86b66198d5550233ff8f2ed7f3e22d48bd800cbb600a97fd1bc4adc162050330f7b1942b6f0b499d8e423d03e8faba442407dd8ee0872cdcbfc15e2733dbdbcfcf033160c6c9f6384915ddbc2284d5aa34f025ec1658b221e9efb94240b3ab170e633ad0eae39ee9ea6cc1898fc0ddc662319c322ef2d0cd714b8131420ea6c8cbb1c94a49cc877d88913a9a084567aec4fdc0138b601a8a7db0d8190964b867da451ad845da51ff99fa41493aa927000cd5132b0ab93bad88a3e68cb42430c0e3d5ec93bd91f43b260f983936f65cd1a9b4fb1a87ec8ba3b1ca85166cb9e1a4ed2a5d15690d94e798e1ed24f433dcae76d72e5537f6563046e38ce709ee8b2f600e89583feb492df6f42aa17bc3cebb99cf97832c72602813a5067296e434506ae063fd5fb0b731de2e367a8d130615c9ae86f884fc60e89ec8c8191740f861cbe56dcdf5c21256106951f651ecf6f1f34b57ff1171b763c1ad4ce936068752926c5a4845f048c35a9ed64adce76fb7a34ecb35d5a12c05987cb24718576a6f9b78268f6090c4e1a85c954b504bef34f75227f67b8b06801201887692945936b93851fae203fd54d1e11f7c24334c5c50e5f79cbc3d0dfe7a8be447031e4d00526ee6903409191faaf0e4cabe006547bb64060fb846a21a120d926fe4744941e8034d318e46087b91129173cd765d36917878797c0d15837fbda0fe2206d8ec71b265ddbe4bce9bbbe9387475ec2f2ef1302f060c48db264394fa80ef270c2c09da2d995d8a00a4a500051ecfd06d6ee59f16cf4c4308562021ba1091b63506e4d2496cb0f8274e49b96b4d120e815bd508efe975fc988638e3717a514a238e8b95ec5d6ccf0abeaaac310015763c49e49f7b1886b81f46e1534fbc28ca796b233e820b4fc0e6724bd8614055de6ec312dcd1760164ba2690ff3b61785755c603f7e8b15ccc2d7a5585124895786f69755437f6aa29c3eac0666c1699376c0b467a1e1ebf175c07b3ba2a2cd3f78910b9bb6a564d84c1e2782c73a5422c980b68d1e6dc016f0ef1156bcb41782453e4ca77b2ad3bc2bcc1596fd3ed4b0d6f16243c14186b4ca764df572edf2c3115afb12f730666ca0071549d07a2ca3ad2c20a1fbcccfdd1a6641edb2d294d819b292a5e40404a11af20b65ef638eefe150b9bf5bcc73317a6d60e410417724113a629a94f231d15aff1caf940f023b08456a3b4e3f780d08d93bdc58144dd3a3afc3df3f96adaa68d6a4a0005c1ccdf1163fceed06346eb3e622bf2d62388561442d825d597af6ae31726c156a8121ac9dd171d390e74603481852042909bf596f7187b76cf6666da36c899ba466965e802722751f6997754bc2f372a032a09985ad2428bbaaf6d81ee99c9bef3019c6582974926721629d4321dd289fa89383fca693a5d4bdc60555225f11566392ee259c6546bef9ac1e26b8241ad9846efcda391e160b99160b5157407f2e396e97bd163c9baf30a45c887cd6a4e1735a675374dcfd364c1ad5eb078d511a9bf493f7d54089d36915d74a6bbeda99c742e9f034b5e5340b4f5e791b6c987368b5ca6c019733258df6f9f3159d2a298a5c1f76e51f152558c1e5da90deaa4de403f20ee8f1ca479ad61394fc810817d97f9cf52eff06c2e037e3cf147f783ca5a20399350d48e50ed349958706f4bebe6b9db4175efa1016997b88b4b7f7628c6a42c62377d4ebe88c84f9401474afd9b490b760096ecd5d7fe9038a3eaebe08913395cad829669cfe05120df11339d3ffa978aed2e7ef211f05e2d26e420b6ca07f01322c7052a441b5aa24f1223c9a8def535b541e61b3f160ad3494afcd2f092de90a929eb9bab47a078009c9a53da0a2bd6527e73d000cc5431020f15bdd85ad5f5d2129ced3eb02d40804faa7fe885720d8dda80369d5edb5b5d45d553aaab4b34857914e13829bf64654c9261966fd63dbb691494363e6017c3f98d1b15e5edeece10d8040b4c6778c09a43c6e7e1fa7aaf3628aceed347072e5ff219fbbdb6eb9a59432a514520879087c087eea9ce0a6a445c1f6adb646981096deed8f665b8584a6f16f42d5c27611b2c216bab86d04f903a6f17fa24a61a908314125b1e812854419e1524b1eeca0422f5819fe42dc702e41d29420694f6cc892c6b54ad2685325699226699246a9a449daf4991e52b22e30d8d662adb6baaaab6d1549b5aeeacae3f16c9e0d06f33cdf30b7f1ab43678b1677f7f73c1b0cb66d2fe29a2f01a102583a37ae7a3e9dd753708dff9848645c86f4b819bddcb8eaf974de83de4396fd96224c11c66d2d13c6a50dd63a302947f877115edf3dabd94aa705defbafc0fb929c9070c9f361618844d28dccdec85a4bc39ab061c3fca3542ad7612b58d36030180834ea713344448406079124e9d30e188c460d56b163e4c8dd1d6bbfdad5adae358abcdbad180a75b1eb6a5723778cddeeeddddeddd55ae337e21a04704d02c09273e7849db9e1c80bda3a49b86646847b708c3f68731eaef3628c317a3778191ff24e172758c162dc7edfaaac51322b4c74efdf5418879947f7856cc5ed77cf732d2e75ad2e4d66e0df4bbe3086cbc46d3c149740385c86351249b09d4885091eb3f6cecfc8097b03267e2d73900ad3f803a15e71e71a7f990c060624ea71e3a00f0789d0502409d79c741c1cc956359e2063c7c8919bdb4837ec155dd1155d5b6f5bdc6273fb8e91232a2837d880ea27351d39778cd0aeebbaaeebba91c7e3f178b66ddb647c61258dab854bdbfb47572475ef1f5f481cece872c51797e8f58f57b024e152bf0cf3d35cb3b9a060d9c4262cae7b32f8ddbe245ce31f4534ae112e11e13ddce0bad7180cd40ea9ee1fe216a554a107a771b1ca42e5613c798d0ab1f5459683f20b232b028169fca7a822ba8ca476767676768cec189131992756d7bf8b1c63c766ee1be6888a27af79cd6bb4298d3436b7cf9b799363e40608f8602f1836e98d83a20c6d05d7f8b37f77715d8beb322e39b3d74d298d1ea5f1f361c2865e9bdfb2ce433d2c8049fe8ab02b768aeb2fb6c277380be622ca261e06cc654e664544b93e856bfc5532559b1ec60e5f1897d81f423737b55a4be9cd4d942b1959a462c7c8919b6541aaa591d28d6e3146598c41e246dbb62348dc464712f7bfa1530da54f5bfd12e0b569c252080a2023043861fb23c035fe096892049ac63f2626f46cdbb66dfd59c28a98b01bd3606ef464700ae4806b74b0b0bdd1205ce347e2916d6b7074c453b64ac739d8bde5e06f44b02d16f58a25c44a2592a12896bd8f8a0de5f57fc5183932f39c71048140111465deea6696f10517da8936ce628db19014f6891a0fae472aaec71d5c7fa70293dce5059760de7594f418a62937de755037a61b0f836ab2f12124ec0daedb8087507c018b715d72b1509c146b4770c913ad78ace2510947177089bebf6bd13b3c0b2ecd8f43c47c1881b81e6770ddb770ddade05db8ac068daf26f385f5da982fb432be6abfb0ba565416492d93c9643299cc23eaaed55c2a95ab93459290982c268bc9e88b40a2be5d8ba423227655aaa642f5e3944512334f8f3fd0c1ee641de8eab8fe11159b38a97a17772e1e995cef40d1e7274e0ff42010687aa0771b50bc71e32b358dff87f6c37ae383e223c2f5f7a468015ce3ae831376def0e6d33961411fde88e6ed3e9f8e54804471072deb9aace6aa2e14eabaeafdb82e8b3c445a27eae1b2b26871e102e3bafb8c2d01d11ea66c4597c4e80ed7fdc3d053d09c5630cdcca24e2eba2f9c604c19d7b84c76c5f0316ebb7a4b1458b82d8bb55a3b57aa4872ad56b550c85a97aa5659fc0212c51d8822155ce36f43f4825445528c4552e401d3f8fbcb5452ace213abf88395cb7cc7bd10f90b2e85eb1f12312b20f4285c7f1b9ec5f597ae488a3d341598c61f89d00d6c105f1012030b4078e0b0b0f2146b2e03c92c16f1e43b91343defef5dfc0ac699a0f7772bb8e4bdbf37a171b870a973a7fde876f7bc9bb255e34857f3983299ecce952c9298a5cb5ee9f23dd0099826922ec034fdcc85aac4f60c9a50b5e84700d3e8d802f37e25ca0f3d97305f38616044de4724eb47048e6630c10e07db656e13c618ae749ec700f7f1c3217ec4c1f3ce3870384cc1c1f3f1dbc46e33677ab673eff992f873df4cbf2989e7e337d3df8d0ea6d932a1feacc5c19f7b8f28b6e787c4f73cfdbcfe84e8bc203ee7118db866bec80b2b67cd01ba27830eb74e2bec0cf1bbc7803ff73fba796edbf462bf0621edcdccd03df743bae7be2453b64f12bffbb66f48f749fcbbfeda0bae9913fc4449b0f2c38f1f2b9d5e417e118b11c6cccc393723b0935fd8f62f5ac618ed17b20893524ae78c94ce58a3284e4ae383925239279d944e49279d91718a413f4797e3e278afdeff289fd2b05e19075d7a3f64cbfa23ca3869d41fffe3231cf4c3f81f3f5aa07f04618eebf30bdf47a5df0d78e76de639a2dfccfce49df7521d599df6ec4bb7377d79530e02731888d1859629877e620b2b35d394fe20b46ea279bcac38d8d14fc77d3892405ef711b5c79670a471fc8340f3f8b07c68cbba4262e66a24a46be63595758544fdbad63ce23b124dea4d6e5bddaa9452d64e344d94f2bb691e1df7e99eb0db13f673fd76338a10c491f02f1d9d73e33e54e4ad22b8a6ce561e0e085f5a1f99f191c6e9562f41eb86c25a1de9a6c56677773a2708052466abd5aa6e72dbea5665e5a4a7fb745252295b5cd33070358e5361239ae8ee09dbdd2f36ec58fdd04f9d0bf11c36b0f2b7207caba856d8a6545ee148ec0bf300c24a2965f7f1401f2ae26e75a249bd45d3cc20d038fd650b246efcb8ea2f34004dac5fd07b272f3fb8c2085b708107ea09d1017da148131f86289c564ed8b0b31076166ef4d30d0f3c73c2e9e674bae9d837ce830eeab43ac9205c43e794f5930506b0894dde924dfc1fef47e5368e9bdcdc56ab6debb62ae20b12b1e9745af9670389dce48992f4922378809972682780f083cbe4bd114d0a1203a05bb17d4d206f2b92dc89477151e1aaa8135127073f2cb6d5dddd5d02a6f130da861162300e8ee6e18fa3c7781fdedf112c4edb5fc138f1f4a526340ec7a5fa56d5ee9faead8a8d53a479f8cf4e32ff22dea73fedf1ccd37d8ee3681c1dccc37b1cf7f3f7e339734d7b4baea91f19e3c74353b85d889c71ce3a6b8c31566baddbb6515a2bf56a6dea0109bdc0355d802fd4c5ce90f7fe5c158d22c9fb66601a7fcf62b409a92159d56ab55a0d0912d0ac999bc66ddbe846bbd25ab7ba511a29cb91ec1163947d7a60437f5db7c1e23630b7992f63acb53d5c953b2cf921e435aeb919f5b8d9116385abc562514ae98e118ee3385624b1228d1cc755aed46bdc7024bf27d890778cd8e047dc66bee7312e51fdb07212259627386d8a5415ab2b602f07a5d36f918dcb5e047c87209114a35061d5eae08dcc11b13833839b629e394cc0e502b00da54c30e81fe2b83ce399bf654cb04d2bc1b4e536cd9af14516ad3355eb9c73ce39e79cb3d2a9044bb94b348dcf280cfafb84c2ce26521e45b3454534b138a8258c1dfdd01a2362138d11b949cbf5a781f58471e6a6985f718ca84d51c4b7e3e68d11b1a9ebbacf526b4c5f4f65c399e2b2d5ba6d33156933254f0e42e192d499a82e369c29192a6eb7613bd89952c2b2f763a6666a862849dc828242cd9443ecc1cb04c31022e86d94810fa8dbac3a6d0dd1ad6198a908fa60449115599755ad0dd56835b06e54b061b35256dcc609931a9592af48aaf1345054505166111316036b2ad11525b27c60c39992f9195fe39b9941e667fc4c7f43647ec6cc901a1ffa1a1f7adaece74b13c988362eac77e04c7f8dee97097d8d2f898d0f7d1200fc8cd7c144433403fa175e07538c6806d4d9108db62b581a2ff322206c979a9981c6c73c0664bec6338e1a49313ff318a8f1386afcccd7781c684c1c1f80c7c0cc0be0c3c1c6cf7c003e1ca60cb1f1338f03001e47674394d3e30a36e66bfcccf63a98a82bd208d1a1b0176d459a0304615de69dc6e733f38ee3dd0380884d315f030038be991a2aba7d3313a8bfbd0e37462aa22219dfa21bfccaf8220d9a074c970bf3531416b9309f13ad28aa60bef8648698bff14362fe068d2fe6852f89ccc739d113cd10b1292462538c884d34ba4fc83fb1a794dbd0bcc45a9c39e82e51110cae710114d7b7309b8856e825b8cf0d67ab8b619022184cd3c91b9b0a60cc142db2680e7adcf9c2ced4e748e31aff2e78c76bf1e435afc51d445664c52cd189e8839899a2551c74eaa22d185e60e387d479e0128dc6cac2c1808511f31d644b58a99adb8491b5a2d1bca4669309e54b644d9f59668a6be60ff38a0c08312ab028b10897933ba33839588b446898a9ae61a68a843cf3d08ce7be980f41b7f14d554ccb66b4ac4616685441e6eb2d66cc142d66be68b3fbccd46471892fbde16c4d29b11559363eb40c7470816052a4451a6f9cc8126388248e60d03fb23c92a612ce510c26aebfcf17d7b87baa65911669916524922218318c9d1d76967891365373ced93595a231900ed663f0325fccddd2255b9265ebd6db16b7d8deea768264b15eac178b158233078d98e233b789dff216d7fa28e30b1bca4e4996cb6da4152e6ddf754ad45056556ca122595226a7b82eafc89914b11536942c09939fef1e1472ff96b0de9a7ddff711c99688e5921549202859208b6e3af849bb9ab41acb8a8291d4bd7f1a003bb0d3e2d545f4642092011aa41467c9c0dac2b66ab74593ec275b9dcf7c06f35d0085b2e059afc2071cc00de34b1ee086f1d53188e8729b3006115bad2a31462b3706c125d6d7856507a713969382d6ba6ddbb67d88648049f1ca4bf67ac5dc30be5876854932ca8bf5928105856be2a64567b9fe339e39a145b9e2379e20595c23a3482b5255a0a638edc82bb8c6d5b1497f506cc8b4193edf3d0628066ca481c27e8e965198c65f52b92e81705d5eb9ee4956a758ed4a75aa53f3c5f9278339e7fc7c3e1f4ae9a4f3f3f97c4694d239e7ece1a4fe7c54bbd13c1ff77d2fc4396984b1c562b0d7bffe35e7dce6165f91f4953a6ccb84f29a5aaecf2ad79fce6df3bef9482a116167fdaad5da5048b6c00b03171b53602b08cbde6784f55fd5d5b6dabef0b7554c4d1516b799b05904977670295eff199bb348ea3e04044ba5ce28dcdc895ed8ed89839b152cf7d2003eb0fe7c398a2e5c6edb4c96db843175533eb5d84272d30da7943bc60d638b4b7de327aa41c77d463f380f88df6d9b1f3d2022bf51e55dc04d311505dbf160d914bf7faedc26fecbd5625d8f22d912c9c086b235a16cb096dbc46f7be2a02792810db727dbebfa6f5ad8fe30a656a9a852a55253958ae08d2c95c72a5315695cc776cb411f4f1c74c6e2e9e964aa3a6f015c5322029ddee7864bf1e30f31753363aa4a3855b2255b32bb4d617bf27d7860a76a968a3207a74a0a9646beb6205d30933298491acca4103369c42c0bd7bfb1c0a54d62c134fe52b8fef10bab85eb63cc18bcfcfb0726c9163321f55442515a45b43ad58598daa61853750256019b80d4af2879c0530906e36a3261c4759eaf364210aea726cc55515485a52fd9ba79d5dc6e5dffbe3245ac8a9815b1d754bda66aaaa6aad61d23071dec566db1cd0710a5b3810c0b103b302b56acc41839b2a7eb4fcf6284c42e0a892410308dff16d73fecb8e6b8c8c58e8989d93eb5562bf7484ecdea2aad9344d55250fe95db6a48786b8ee3aac771713aa83f346a95c2073a34a4c43123140242103233ac9db36eb14306084a2b5be48832bc1ff17bda281f3737a36ddb1a4581202f24923acfbbbf17b93d90cc2259d56a35e7768c7adcf8e01a121b4ab89b993bb2562a5b69531a69ec6ddbb656b54a35a9bb8d0eb791e2a43afb32bdcc39e7a451b66d71dba20cafb25d6bed6d1b218049120c491bdd91478475976b676727ba7622084a54241941a15ed2a6132e3d719b3058d2e4f6a11f782e993b4c9611ff18bf21dc9784fbeee3fe685c03055b7feb264dd6228df770e9c541ff3837340f5f010b1a87abbf936af73e2f22918424d2344e4ef390bdbc20c9f97c5efdf9ee4f0dc9e713424237d16914922f36d08a741922631ef5c8018670442dbad3f5e8d445b1d026940965512b5445a15c7f19455068c1429fd0551435a1729b761b896a169394db44978c0f36152342511445538d436920002736a42852acd5da900f360036a0a9bafa01846f7f32e5a05314d7f8e7bdbfd1c38b75d74db90dcb1bb24c004278661e4fcd43f3ec7864d7ff46b760352600950d23ab968a39d8b558cc16c164c5c513a594527631bbdd2dd7bbbb259df3935d8ff1c186a35b639ea73dd03be84b9283a9c240a02f097d2501fde73b8f6846f4cdf4c3b4e787783c0ff32561df9e7b1d4c3a7414f5c2151b6759708d47591436ac308a1a82dbc022699afc6bcc6d7a78b15dbb2145f96f35b7691a4579fd627ab0ed9a32a47bef4bd2d5ee874d53c21ed7e455589483ce84a6fa0b47280d8091c635f091c68d60c31fc51b273676fdab169817fb1702312708174ba5891bd22154081136e458ade2fa6701a3281445511445515477b2f1454a29fd7a924b112ee1b8fe466c9032923a402bc235fe9f13a4eccd49acc52d77cdbc702b553ce65dfcc51b07c1d2206ce855dc4626817a13ccfc2458c42e65e35227650ae34423e020ecd410d7047d4dd0b7e33c34f85e60e9875e60528ed320e0728ebf935a44a790d0a42291c697e8aecfbd39952291e44b348d37e1f3854e8450fd4287c29bb88d045fa2b71697a870a9fa1242446887a2bd096fb9b7ea87a2703b91edf5b75a9ed06f7b68ba66eb7113631e16e8c8c7a787a5dc9467e592dba8597fbe7ce504c4650f24b3fe39ce0c84d58a15e3220506e5b5a56585ae56d5bd7635eec9c27237fcee6bad5f3f97bc15144d226244af1a87da20d48496218108a126ec12fe7586374d7bd5ae48530433c7107694ebcf79b7f2e0fa3f5769d9f951fa021bb26c031cd000976ef00126f5e949f3f0f76853f801130c869a4707e1c67558f33084ebb18fad0ed6533fa127da4f6606b80083880cb65aad56abd5a8c7ca52e166a64c9bfb65737222a94a493b0f1555aef9175a96d69430c963c34858d029cf97e3a371b88f86e6e1fffcf2726898afd7e49253abf461060cfad3d0384780602b705919610818d7662c2b66f3dde3e68185a5b55a6ddbba3098f993827262f22122ab44b8f35c91b922658e48294084694d7a1b69dc00d37dc05988d0b972743dcb105ea24966334f3794b957ec8d1bb24cd6cfe7f3f9eae0e73381e8f92244081b322dbc61de22fd34f847e446caf20d43a0474185d828fecc32c4ebe9d787610a1b72145f50b9a22996fbeebbb9f9978076846bfc6d11d6238a5ef480f483b040c041e7bbcd7c60e10d13029e28fc206ed3d94c520cb20797e4fb1549001def36954b5bc771de8fcd0697526069304e7f29a856864b9e77cf15eb5d7bdd779fcfc7fbe1f916c944e172f6084bbb1f1a8d46a31df1bc1d0e8e1cece1e08d833e722010c4c16e193e88a1dc85a5091396955635143a1a7532274f9cf8cb10979c8e4236c0208dfafb236bbdf4a768042d9271ce398af3e5acdf10a7e98cb07ec31010d6e6c086f6ba7c8fef38fe7cc51084d840dfbdb0262463e380c1216c689c3e1de1022f5a57282143316d368aa25028140ab56d3b463d3a06338399ebe82736d4d0a76ec234fe12a3f333ccbbfba5773d3cb0a31b3f9e15e1d9ac8894348cb98a2d66d7218bb1d825bea2966d4606ef3ed7f87ff7c726f3b76ddbb61a5aafadc6c5a5261ac7f3fd963eccc370c586b1359f348ee7065c62e2243fbd22c953339aacc99d4892f1fe13758a246fd249999c359ba44bb6e42aca28cc87f2142a854aa15cb3fae578626722107ffe8caf03150eb5d08a40709a4e49711b260e63058b5a82e212f3c7631173f6d5da50282e81b9f10718d10cb771116f5785f58f556e186bddb6214245d87645527479a660ebfdcfe1a4c8c4458c1397300c5c8a33f8dcf0bdcb7db515bae185b0d8b88469fc7fa84e228a6bfc0a1bc6566c6d1f7a1f4ed38f058b6bf12d22601c04308f7e5983112e4d0528a0fbc25a3d5f6cc556addba8c7cd5cd950d2ac6256ad184abf9eb4165655a9ba9b9b1bbad55a6b7d52f7616dff4251b061e84a5291a8c4f5ef2118076c33df85e052ff4da4691ccf97a479f88d77439e7d5f6e92d4f79bfa85355f6e68641e709266c25f51dc2074872269014ce37fa35255954aa552a938ae0a19e260ff27369bcd763ce54fbc7054173f7913dae46a4339399184a4f6e068462c51a06c9f56713991e43f1a816008d6b39e71e9b3a104dbb7ebba6e3ebd61ab26f390e17ddfcfcb077d731e902888839e811d52a20474615b3509543525e9314cec31f48f4056a6e9ff421b2af7db047a04bd6d40e87b6c60d8ddb8efeeba0f924e61b1b5630efabb604abf0ecc2b895d389950e0f6e57432a14226985702533b9d4e261d1d2502a05513cc2b71c02c6482791d253cc6eb64820168827918dc98604c30cfbd6c900f0b9d05d3cc9eb0ef1a62d6a63b3d836189ddbe9909f408e6f7ac023341d0c2ed1cffe00e6f5a8f59382a41dfee5974d9f006c4041bf6cc51b3d96c369b51ea33cf65b67065d65167a08f500eeaddb6b1043b9271123b5713e7873bc21cd7a9cff67ecc2f6caddb3665a432da3ccd79d2d15698344b67d55552934fa78e0c82b147a3ffda5f47c4dddddd3d09e80bd8ca94c388c90f5ca629fd21c36230fcc0facf990830974b9fb95cea3a7dca9175ea0607a9128be408f5e00006232ceda0b0f23671d053ad1c2acb7c3a9d4ea73ef58952a7755268ec34b7711dfec5dfbdb8bbec642097fc9238ee03eb573b8f16d6efec9e5e3708d391449fa6c37d7d5a2455ee0b400dac90d98d1e5e2ccfaea36ed846c0208495c16934da9c3b68758c8e99274f99acc7b03559173f317a778a42987843d75b75606a24b5153bd7df690d6cd8ad4869b2586beb38a9487f09781e611fa0f72f82821b9c04fa680d0025712793ebfcc53b2b1e366458ea156b1efe138b834da4b0adfa9ed8119020ec1d9c808df81371555c9062080c3ad83c8a500029322292a06a8566383ed40aa5a4a452527630d26ad156abd5ad6e756bdb463d5a33a4b85caf1d2f586656645ec0b65497ab7dfb500f48e7e1b863c3762921c68a3cd142609c2456669f54deccfbf629926efa497ca2a2a208dd4a4214ed84b80385b5723da9f2ba5cd5e572b95c9c886fbb869cbc2e4e2754cb0a1396ceaa4aeac9e90482b20623c95a2e798de35dfffac562c1e5367d1b50b047e889f66f4fe9f6fdfddbf71ba91ff24e7df0648db06d65db28a5b6e3a55e05180a77fb3ed5ea606f2fbd1f5b1525c1bd0d6ea4af9123f2044b7d4ed70dc0a300bc8502c0c6a9410938416aa05d6c100c02c1a07f0d8d83a479f8105d584a382276da69d970c4c869e774629d4ea7d3e964b1405f1cc7c5da49b264151df9454a296badb5e6c076dfc1a049978508dafdcc5924e6863cabaf5a3b092781b5849429490e323b28d3e773e8b8b032791f6b50dc660726758d4b325c9a21272c7f58721d06256c18dbd5318fc13f0dfaa6312fd61cac5dc023c39b6133d948e30f33051b46d749a2b8a71c05b2dd30db33dff3f15c1679a28f8863acd185edbeb0b7442ef10a26512298c67fca941ef4e086edb272c376f911b1567bc2054ec80df4d6e73e569cc6fbebedaf5d4caccb89d30d252ad6ac58ff6e2bc27d72885061b91bf2dca813d7db65a579b812d207066740ad4820e42a0a39c42bd624ab5dd48adfd4502b4822a96b55d45c35b9721bae166bb1166b1e0fc735421c1c4284be6c00c1ccb4b1d056df4412dfb0870a0b739d524aa9bb870a6b61c842686665b15a739cac5c00b3b826a6c541eeebd4ed20ae7f6c988f9c9b90671fe010b007901841e33414be02140ac54385a5fc04834180328486122b024bc6daa4d06655615d094d11aa227473c38ab1582cd6b6c154890921726242c310a6e9f08cbd906ebd05724c08970470fd89c0000a283d19a6582a73847dfba9967e433ad50d79f6b2b6880d2152b013f0dcfe402e794c888cc562b198903977787c60e6a992826a321b89b551eff40cc1728c49705f9f37fa7f798aafe2adc899354a7b38cde69b1662778cb17687c57a37ac4dba35c6186387c5c65e71e9bbee8574b0729b9614a61ff6eae670e5d50e3329a3bf6f6d6795811d503db0d36bf0067430dbbd6ff5e7ffe5571539ab55575fb93c7592a97f90684929e58e9119dcc43dc3803e1eee330203f27ca4b0ec604ebfbe70f46166f678a011e89b3ba31d5be5975365fde837bf1d233b3827288b52564bd6a54aac61cda55d946bb53614fad128ca6ab5f61f0447a366f58b4b7c7d764faf1fc6a5fe1fa6b84634a10b76b3a351a86d121c3721063d58c294433c220c93e7bf54ca32d7d8c993274f8ff60cfdbef4abe7a39f1107dddfeebcad4bd8203b18b1b65a6b592c101073ceba4dea24c65837fff13f291d758a76c157b0204290e80ca100eff0175917589066120d1a39187683cfc042b854e3bae806e192288bdb42827cda415110075d6efd520bcc11fc4990ab7ad2c38b95715e95759356791031ec7c820dc1eba22dd81094f2fb85041171b121087acb240c0683c182ccb9c3888e8fd8100e7f99c383cc9e93ba8e3adbe9597be9d8a45cf274308d3febe8f1ea973c5dff26820d2e729a5ffdc9d51722ac97cb59a8e8b2e58637314358ce4205961bde64a982c9cd424595cb598ef0e2e2f6eb543f1e1ffad55bc4729dbda4384bac4a9f9acb7c65893de186bdf369653942ec86ed456e348c56a415aef7f0aa3199ae80df5aad0d85fe417074634508eb1e1020ec20783ae108c5d3cb4ece628425d8bc765eafd7ebf5dab6978582e338e68e7c24e9288119638c2e9c4c32af2383fb9890679f1f8c2304300ed83cdc13b14946c4261939ce711cc7fd370e17f27e8608f41f519bac884d9c2889cfc788941c9111c910292972fa3cd853dc225cffb000d73f2236f5dd3e326e9a216a1367454a723899bcef603ec7258ce805bb9e08072433433eefbdf7f97038d271a2d6e260f7f2e19182c5e16a975feed362a5b07cbd9f7201192118a3c56f00e8fd6788580121cef5070bc0a53700f3601b4a039530e8df34d063b8eeee340ee42ddce71f49344ce38f802b15641a0eee73d41d5686877624e420d708f1b8c5c2e3f1b0876745aac7e3f1783ea311087ebe1a699cd890675abc97306157b9eede00506fb9a410278512383f9847949b273a52ba05e5ca78fb31f67432b90c5ba4eb72201084020e1ab1e1c80d48224d7f441ee794d45ab972ed20b88542455253a9b5d6eac9c0846b9c874a7bd23a6e93059372228d8eeb6134185c0069d9c0862db3f12f12699630e908e7c3c3033bcaf3f5ab50a47893277de7dcaa7c00036193152690538324f4175360d05fcdc3fb0a4c03c5f5530db8d0b296a3aab85a2d6ba18e5454ff509d340a2593c96432d9a8879021a998d5687453c4061f3db8cb4b0b6d853a236c7f884bdea871280fd73fe4031b8eee0cb9949147233a1a8d46a3d1b66db5be55881c63c766668e0fc3e50771649a077fbfa00a1760a61c1820c41096609acf1f120117f1174435780cccd26336123bc03cfcdd3fac2132820d6faed74abf9b1b29c1ea25c618afb07d638c3146902cd65a6bad52f610620e7d90213970dcf80c001036e4fd33925e003f2f2b43d7fd8642a15088527a84945246dbfaadeb6eb08a31c65879d63ff817e19201a4c93ecc69f3d8a8a24b72e474bad1830ad606f0869f3f810e97fb7e90c83ad8fd9d534f06f91fbbddf2f35d07deeed37d1495a0c7f8bce78a9d119f8b9df48000e10bfaf682f0057d33de8f803ee8bdaf0718a0a75f1f06e663be1a693ceed99bc17ecc10fb319ff7cd44a0043dc609e483bee71aef0b6d002fa7c3fd70a28f8c7ad9ebb82f6c6c62fd7360439ef12039e06bf57ca1e8f6733f2bc27156c435d25899108ee71a219e837630a71466446415a6d66a63ce39a70e9348245598da9da304db452f3a78716fcf8b2ad61491b8754422e97dbc17aee99085ebefa8482272d2c18103a777a8748db805356534020000000033150000200c0a860322e17848260a728d0f14800c70884082583e1a48b32088611ca4903106210300060404400433ac14053a62ebe1d49c3cda53fe7eae98e3e643a94226bf60417b85a91e3e638bffe5089ea7e763537b32c3cd1b038c3ed906023efb9e63f336e064a8a5567cffe1dbc0c094a9331d30f3b69c1fa8766c68dcdec5dbf79c271ee0c74b7ffa91677432bea0e7760a7cc1827b9935fc8bf91b5072e98c86924426c89c405bcd9ec76dbe2abf8b798d697cbdc896c50bd10f35b60d3eeee2e3a9e6833d8c3fdc9099ab586de87e245e3dd53f5a77d96d350a46ee4a20ec638faca8d75b58722f4c9d5bf121a79728d4dbff51812d25abf793941e0c2a3e16ace0527081eb1399986398e835311a9d843c292e10d193638c719ef7cfd7f6db872fb15176b6af55a7c41259774b743e7789008dcd33fdb5aeee4c0821d8133f78bf84d086cce763fd2975d25249e98900256f18c87ec3ec65ba3796c66396f3d40cb3e4c9235fbd9c386e20e511a6494adf8484fec0ac303cc9fd5ce9de955f3b8c3d890d84fc9f49851918390692050b930aa0579e9ad95d4f67abac99a2156e8e93dccbeff8726d19dbe1f738d5e6ed7c81f89e00e560d99582d3511fd4eae2c29a878ccd80c50717ab9af4827868365075a7a67f4f981646cae036e7d550443e5b091d11a8dbb68935740a15ed5daab9b4b9c564bedf47b8c41e2b26eb9f4fab1ebcac8d42f858421a2789cb6abeafa8603cd6dc3ae1df90ee6c4ac2ee834dd2688f12d65da7c2fb57c0b5720d571f17e5b89b64333557a8403c519ee0153a81296d23cb115ec9cc8faeced45f44fabdbb023c37503c1bda0f244dd058a0871e98bbd4e82b4b63b49ad2739856ea36ca901473eb0b125b68361cadce9e8fb4c36654b25830f9d53498c9060d377291e29da82124d2f2f2b081a8f4863e004b0616448ec2caf20752c8ec48e23aab98bd5aa73d6e853ece07c8aa7c44562ff00118bb279768075bf267ac182846526963eace6824eb4f159bc13b95b78e24f09182615f1f6c8da9cedecf86c906d856644d1778143198c830b1d57beb955188d86a275864d4f5fa80a4c72e30164540087cc42ff9727c32262ea5fc500adfbdc04c1a29e92a9f226a78adad762c357c9c06f4635c001d00ce22e486d8041ce8b8063ddf6937531de8bfaac2e6631107123727a9dd72003ae25618b18b7d7895e442553a98a6bd0b626aaf43ebaf5713efdde821caccb1fc37d15039cdd5081133f990cdbd4572ba8199fae0fd85cb60836e247223e06cd98b1a2fa39f743322774f5ba60978abce004c9f467bb2bcd1b1d1f16bc0c0b1bfe4380594a60e8c292610bca3f87e4b333474b25b02f8c15541743895ff0d42318117dd915595f6cdc4236582b62383e04de142221d853474510a33e68c682aba033e40b58ebc76bbc6290c30773b02cf76fcd1178b3a2ee7f8e8f0fc7390ab6e209eaf8cda14e00a8d0df50fb9901c367b8d55d2671cb402de4f72c2e7e2287ffdb07044884a22902a1b17269804e76d75db1de3187ae9943411682352fa2fa673fb0b818702808f2048681c5785b1afda62f312e6b7b34085bd8e3327cd5a3516ea969af67a2efb5fa660a86b841d56073fecdc848b0ab4820cf1ba6a0518546fddc3aa69f5022edec05965ae0c3dc47e8a6e85fc73ac1b4e09da48ed3d53a56bc3688a032d9ee5c8a52c55e44998a3c187ff1d5957efcbe0dd644cfb9adfe76930432b7015d19ac16f64b416e9a0b4d35431c27fa157c4d6546b94469717208a9ef5e444089ea933e9cc3c016aa0d2d87cbb90bd340d5d291e694d283f019bfc4f428959d736f5b64ec3e26bd24ca3c5f47394d134b17a824e181534f7c4c7016c5975e862d77f7c17203f98155dc4bc3dde2706189ca8a0f30cdb6ff0b7ac52c187aacf776aca195613a013c29d387817a57895e7df921f24384c56e9bc980f61534848c2bf683912224128644ca8599eb24a5afe099b80a72846470d30e9667628615161a4e182ca3ecc012a5f463a792f424412cb371d5289c610963613d2c89bf147ab590b596ad168d09f959ef77dcd554a927e8557c038723f857f9a6fdf08cc80a557b738a86981da9b15998db868ef528d12ae19edf7e79192f43b0030a00dd35ae6422ac970c657fc6f05d16c62d285b56ae153ba5ebf61882126f6416070e6d6caafc044f1410cafcbec35560083280eb41ae14101b312a1f906d0803adeca8d56e3b55c8b7d59082f6f0967ca9c3013ee94d68236909f3592ba01c801f3066d9e8f53db3ee9917d641c7afabdcef143a35918ff1e0b3ee042966632845372cb10a3d3d7165f0781b38fc300e1a6855ff14a54d9e0b85da30c8e917554f58acb08ce62b3b4d0f0cc431fe514351b0dcfa6ee4a38048fa8e95ccc4ac6a6bf7187a708329cbbfb45a20400f766dc5fbe3f8bd6c01c11363d1d5403eb63a2e564ca500c7c5ab9e57b63b842a4ca541f88c27623994bd1223e82a53e4ffd467bf353255ec48c98d1304d65a0bc0bdafa55e8ddf9fa060cf497c2745543ad5855612761105814e8c8dfe28965282f1121502ac17f9e23fd242b5509870abb0cfc3cea02d28700aae84c53c47ee4bf9f48611b8c30af99f36881d16a91b2626e9fa5b2fe34126128a51b518ff14868931744b9cefdf7d89295be661474a494f2305e25a17568dc11611278160b9ea49ac7aa0c4f9d1ae59aebd032ee838a6fe11299727b97cb6f0f3891bbd69cb48535d2f68b8e28d7a7e27b8cf0b8d8e20ffa142478c06d8a0bc22ac0d7ba8b3afc491980c809b0eceec3fd899fc402108979470025ebe8a8a222f98194fb3e9316e16ef9effd147df3da0eba96289ae5ca9e362b9fedc74695958a965df4dad6b72fff8d05d4a4be04e5dcb923077c745526bae38d9d6774e4e7e63afd4922fb947b0572807c84f889a32f90fb771a682a9bdf5155a31e0986d469b0e69181d19624d0ae384985681c49ffcb2e141ea8440f30b0d206d9815d7ce97d04067f1001a7f9aa4cea198e2bc91f6310bbd35cb4658d06a1e560182dce36c20e3ce539d4aae733fbc4a2edb6a1cbf13e18d3c52332047fd2f87bb177025aaecd21a3d78b3f859ad953338f7d710c9944a348921805137c51cee1ebf3b7abd2bd2eff48bc6c2f9f3bdd0b32de2ab613313936e325cbe3749ffa7a6377a01532bf8303a282701f102eb21647f7cd7fe6703cf7f5a0f1fbb0a0ecce432b02d948eb1bb99f850eca8db8a04a2a42df4ae6d8640c26f51a6926fbbd67a5c6f408c0d2229665949e909f22b189f71e1b69207e6e162b7c085dc9b1f84c419878845a611d6c7e1500a61d132064056bb5329615241cd71aa62ee67df9700cf1ef5725fad2508f59f41c94cb7ab6362247b7d9b9914c94602e602c81e1e69b2746dfbcfa05eecd43af1277f83745bf2a2bcc8105fbea6e82656bc44a25427fafd91311c34049fbd38599b17536390b654c662572d6180f070a5e1a5eefbaee7b186f2a51d0f94eb86423f7049c09867a6ba0e8e831c963a0895b087d0a72382505fd857e22f100695c6b2db6d500b563e0d93810de15bc602be8ef3e3e953f6f5071a3cca42145469abf001a1d644e6470733ab6f9153268eb85fcedc950150ede8e01fb480db574c85a9a2f49e5df326c5bedc8ad6e28ef29d8fcf5f0188892809f680888a9dc93c51f3f66d98713b45e19ce33b05c4f11d7bb1fb015bdaf0629ffc8633b1a0fa3aa3800fa003a7f72e4a60588ec8743207b6f21cf9f96b0fe20cb33d3e5361989f0cfa05ee8ac497760cb21e4e253876ea6c690fc812946908b79e8a354dd2665f3415cfa23420fee8b62d1743447aab966c99df93050992cda4ba6125f713a111191a8220b177f575b71426f3c25aea18ea52b6e24be13f90de47ccec53fc53d328697ffa8601d6f804022c3cb68e97f047cc83abf819d3218d36ec4d9951ade708a04561c7eba07edc995ed404c7c1b7f174246a2509c5aaf325d3ab9e467377de354d7d1802a79804da159f2fbe2af88adc376df0046f15a7c6bd109475608da1dabd3043aaa2d9fd190f62e131fabe00eb1487c39431aff841da124546423ce668fe09ee5500fd5abaacf527d06e5dcc128e77c0f530ccaed2e9c9d4b0461c8cf5af3bbefff458dc6aa2eefefc4f69e522bff8463d0343815021966c9f5aee60e025d943f37baeca3e0421500081742a80c14d9ee95d07f88d88e62e88640803219da46a860f1d8aa905b12356aa842f2d8e4bd536e625d3488720018d8663186c8cad165e9f3e44f6b6ca5da5329850b9332056d0ecc5c1cc7eb691c14d2aa9344cae648988be775ed2a5a3767493f9ceebfb7e54268d5c133addbd8c83e93504f758d452df4d7387168061150bf8d1c2e562078c4ceb31eb453672d4e18c45a5c7d50b3d76f27351715f300aa0507b2293d88489abbce4edea2a30b1295927f983f4e9fce7b547a49b1f2dfa38a0bb207e1a3a12d89eac800d15deb523fcc44cedc16afbc04e160e26662b2da8f05d238d6b81fb8abbc5c40e5c7ab175242d54e20fa0f839d54b32ec3ba87ef63e407746998e6536fa169dfd074af8a30a42996c6bc6f155698dfcc3ae14b76c556f0c2e790ac3ed65101d3f746a62e45d12556afc0841c038a7f9226ac88bff4c9e13eaa2ced3719a7a6cdf72771d3d1420cf79af1945e6299988d9630305f06c1643bc4a42d090e97a460602988dd09d55a0f3338ddb4f6898290dc1bb69701dd35b1457e1a0170b56380b06e82a7e86aa2ad2dae523308089e49c7b499da12a5826b44556984b4ce04b44412848a638881b2275c58d00ccce7200eeb5771b9895f0ed4d841fda00b28ca8cfc8f9a3249ecdfc74398fcabc720b8d458b3557ed9fc641f213abf5a7856f49394d2cb8b1a3196f55fba01a9691fe8d5c96701cb0d89f20245c76fc4ca49b6467f2a233142aad5ea75b0b4599ee4a86bf48f546db9f59396f73a836c176ea6544b4a640d6c5cb5135c29dc7ca942ef01f1e9c93e1520c961a1333f1625e04057ffdd71386530417f1a5d7a7b6c91ca2c870f23eb8cce3d44f66492c44a8f353f836afba51fc3d64c372c2e8b473a721254bcf0bfceff3867966954f999a814c4a5d0b0b154b6a3aa5f83770e14de14c8e9c390d8410ff361d5f0838dfe306959254f62e48adc6112ae26527bb0062c12ba5efc99a6fa8fd341bca9730ca51a730e8086ddc4bfda344f91bda9685e9867f16964af46d2da38c4fa40aa814aa99d13749bf4ee6d155d113be71763d80333ccbe9ad85090b228552db7b5bc3851cca82e80705b740df45251a2b3321d7c5ddb0fc8e064c802ad6907067bfb6d3ea8bef3726c87bdbef9f5cca7f30d644a1f5919df5ca9f58d02dc53007b2c001b05008efd46bff4948e11197283f84526e333ea5949f592a79c9a487ee2deaf5aa02d976a25db0710717faf8b196f6df9939ea8f409defac02ec7e97fad3500286567a1fb4178d785b85619f23ef685ce551434af46516459e00fb1c5a1b88c1ed57300fec042a57f70f91aed1093ecc1f4af0d1e34f494adc503a0a40739769cc95808f2f1e88520ae5b3f5940995ba243a12576a917d1beee263657207e7ad94cd2b06b3068c011c60fb1ff9dfd2c07386d0aa3c7f461d382f9ebb705aa3db04f03b651ff004c518e63f7c65baeb517b0845d83523f57feb52cd31a0dc803ebcc521b34e3c58081033b77650efa62e9187d2291b314604b21df488e64f4990c8990a6f36a9367bc838d6719d2ac2d1a2d7d667b56511b37271a94e6ca3caac8e3a1d013a16037807ebcdfe18bfb9e0f6521cb87e4fe087283a3875e9e60b550a0ddaf9eb2b672bc3d3a708b915eb573e4eb36cd2b7774b21f8a3504794586673342f0e8b13d13e46855f2aae9493fb020bdb826bdef0cb42a5aba683fbe60c48146a1a77da9187a73b833550c8bb91060d970ac3d2ec5757b6353eac66c81f4a8e488fe6eb69e88086ca3633b5b3655a07c8dd2ce7cbbb26589b4b8f21d43d723f80f0c7cd8535217eae639934e30caa982bfab4a6c3b38fdf4e163110414168ae9a527a6605160885dc7199f8cc3d9afdf7a369f26742ea7df8486badc6bd765a2fccd717800d46c36962271b06b00dbcfe576984c58b5e7deebc5b0d24608a87799298632ef373118d225bed47cd647e298643587c0a430b01ca837c52e0788df4030c6b52b278bf8b5847f8498a9496aac1b54815cfb50c33e5f0581ddda77c7d95c38ef4d9ff0268aecdd02d7b164bdd7267b66e91f0cb5533ba3cd65840ae0303838d390668185f0f8b6235ce490134eaa7e76ee062c82999e666f30ad251cde26fa4aa5746ea2acbbc55ea1dd785d357099c6b0ee1686d9d4942aa96081d29bab3545acbbd2430500971ed86b1f0b2482530ed2ebf14f7bd4ad3ff94eca7831fb70062ce6731ed80454ba3bdf7475c4b919f13c491ccdf5669416d170e0c5793df76518e18c3772e91b5ef1c6095fd9d4bfe0d973b3666042df85b6e02f1973eceead3aa3502498508f7c082d764a1926122900a2c20f4366592563d1b55f972488987214d63853a6036339fc387248f9da2ef7214e7991c23a16b04431468d795e3c7b2da99257f678d231d8b89cc96a26a68c4b8d82a451fd8ad2c79546402bfa365c58f8bc31137fafdd03be7cef9c594a4b7f1ab17fbe233a665c1f071e5a3073c1cfea76b9b1252409bfb16ace86d006880d08f0a035f26e689f27a77e7430d41b77785b56d31c307d2c0b4b1a33d8ad0dcb042a2330947513140aae52d98ca629b717ece28ebc04fcd85c549ec66f63ed8dd952f1aeffc971f5469330250336e774f057afb15b224c07716bc2b2b33d1c490a67ac82f84147c54181cccd0fa76f5d3cac128c8dd781158ffb581ad87c8e0abda6771283d6995cc2e27a78bc28f9dcb6ab478a95f01047a55321a3ba8b0eda1db2d9e8eeef611a0e0501a83cd3b17567dd1888a1f0aa7b72c48fcdde05c13e5c909eed65b6fdaf0ffe305445d988476f4d518566aa03dde53ea7c45cd1d63cc700457b20e7999b1b04eec640c4e03c0250477b4c24b34513fbd7a58eae9ca9daa3edc905d52069886e01351a9d344404b52337532253dc27b3685bd71cc26f8626e9b1b1f8f0d51fcc83cabc8592099d3241cb7af063e03d71f688938becf2efc9160494a2ac78d5074e928382d6412719339a6e7b07234699d5147c1b8a985495bed6f49926844b2300a0c9f7aab8445de6796253f54205e446f2cd0113a8c3ebb7d3e57361f03cd71a21c2319efa7da6c0f3c417ae916fbacef331ca8196f8d104e594b55be7f9a9a11cea92feb65579f2faeb5d2d2895044ca2979969e3b65d790a0d0765aeeaf254e1370465677b6a2366602d1328664f23693de008ff39441030f1150733fadfeb6c38cff28519b02a145a4c6364d05a756b1e78670fbddedc947e13b6ae175fefd15a10564252275495f6b34c996bcfbb743644cbf89a69b252966f9f4047f5f21011a90f85bab824dd2974a5125f8bc6ad6b4a80090105e6f8342649fcd4da0d86ecd4984e72783f6d1e657e3da8b9cb43194c59fc429709e4e5023e8bb5c50e864d27a11a6d3742b5acadc4f23ae21456b9020ee694b7de5663a7a86670f34d5217f72564eae30bc40f510c2c60b081230adc5c2aacafd1e66f9568b5703f6446c504756ee69733fad92e9aa689823d0a23b97d46b99612182c95bafc9819ff1067cb41922db2dbd3ad5bfe33b48767a688aa33f393abf4933263989a7a564345d7c83d0548487b0e09649f112201c3ed470109dec0296820e1505b76575d44d4befff5dfd01c1fc49b010593470761108bde3a441c7c4fdaafb190b564170de9ab9df051e7bb0adcd9908e391dbad9f0ebf0477675ecdf311799e69b02ee8318bbd2a025c3c0edaac4a3592b79f62a4837314c11201a84f31e9e6d958e57866040a5f2bb06982cce859b06162e7a040a5b33f2be5623200756880a328c9a19ae924fb7c896f5496a60340abc3125eec47590fb92dd03ca44299e8716bea1d047d06acc421483b6e41fc46a4a745e158aca19797068758b833d51a5179d53d42138f6381735cbbb0c2b54426754a62afaa6fa98d0cb4625e0b03ca0a3d371cd2e6dd1ad6da6d2bdb297f5f13f597d5b7077ad97108d0e98ef8ad3321cb4370e8bf374dc67e3e00030171ee1601d2f95086414f8c70209dc1716a50219e35992df51d221d1fdc9a18a45e8b29ac81e5ca93d30d428216c45440b8cd926add5519b0054bb86a61b307c22323015401c2eb728da840ef047d4c2abf2996a60944aae607e0e052139c6d12df55f4e28505a16c0e7ac134af49afc298e4f7493270bfa502282bf2bd621c1aa9383e0a92d5858523b198cc46a14067c1e00d8de0b32ed1c531761786b9e130d373eb9fccc6eeb2eda5d8f75785b906bc1c05906e0097234487c6f9c1bce308eafa1615256edc6b6b87a2e70d81c86576634bba14ef1b2daf37a283a5f4cb18ae78e59b20362d53afe4710bcf560be1b0bd378dc7d95accdcd00d0c880634eeca564b4796895b914c7c506238ae3c395a68137a2195e000451995588909204a746379ac80f54b7755f9c430f7dac534b39ad28c28b99e8015638307e72450b8cb6d56c68b791582e86757a7cb90d014c783178b14b8bfe53fb1abf2a9111591961bd0131261c82275d1628a06024cd36e44e0e4fd5204894b6e17086a8c8ba36d0aeab3737556bc35642082d351adfa2270bc1faf0ba09d661ab6cf443caca1997093d787a97413cae68482221254ca6ad099580274db1646fc195b4b105cfed9fba2d3ccabff992b2e05a6aebb5d6ebc7ae40ddde56c86763007485ae72adbc1375be6925b889e3e02259bf24b1aa12b76f9ad65306490fd405938953c69b71f50d702e82e4c451a537200e3cc349f342daf3a4c3c7ccb0f15e616383124ba5de8ac5d60764c040ff56c415220aea6222300c018f7ca2ea3dc57d508c677b779fdfb326d77d64b7cc573d08f2d8d48e79cecd62f080f04b96580534a807432fe4e4f0433472e7030652a9a146b463f83bc36b46c41ffb48b1325c11a3538a845639d98c970e1445658a013a8d9a72ba36aedb034b621b89d757e460e32d771eb95782d6fc4a726c9accb93774b04d23430bc9e39b9c2181878fe1ceb632adbbf2df4fb39fc55a73fe78cf0a50a40791b2f94d84e756b7c29e6f7f8f0f4c44a87775423b21980066231bd3cb306d03a86ebfc70a38aeecec325b7f9e8c4ac1494ee261f2a283ad8a2970450d2cd5254dbefa371d98ba1b27bfa0284bfb1e4d069735b724225ad6d93912466c1a74f07cd9843d4c411fc05540c4777283635a8ba7322b10910fba436c84809e0c1cb773f53860351cace24da7f26f1aa05ae4c0a1a1cfca141c8d28c05a16ac2cb83404860d2702cccb54f37aa462a600e70546cd3e426f7ced4f257c8a47f406b1a0696ba3af516dd6e02e3d55df110ee1ad2539d3d21f7fdda85ead65d32d69791e0ab6b3be2b2a3f158a38b2d8a25ca8ceae350e29a573a3e1c28310a92bfd4cccaa5ca4602788c2936196e7b694c31001029e7e9131abc4e57be60c8d60d90dcb73f084ff7735d9b6b40e357f528f934cc700c8d873c4018b91b15293e280528bc1abbc95f361e5aa23a6df9f4c3ff3a5c6be857892e384b0889b5c844f5f0c63a698c04bff8571655ba39c0d6d98bb355d39b4eb75e43715665d93f7d840aac0539d367298834087915c53105f9282082bf20629b93072355d70ab1772d0069349560a1901db72e359a81848280cbeaaa41a6255212fc3c9d2c035f185fb6c3f0a95daac8bbc6b7baeb449199ca6e671f1116ff046fc4d2fe77d654cba12b157f2f217f4c9d24de8e021b95fc4bfea89bda26b27e2dff3956947cd72fe88c34e647c24f80a64f157eb3537a44f4560089e81ce39f4814c7c921a1fa0c5e85666555bdb78b272bde309a023bb70ec66ca3005144d6de55edea7ec7394d1b2eed9087eff331eb5bc5f92efde73c5b1449dfb5f8d822094a12624a6de71d2efba4699d16baf0571eeab785a885669f7a64c1b5cd34d43a2e27d12e5a726b993525c633796837224003c7b3602469c8e509304f21905f743b88340ed228bb1ec4ad54bbeb07e8216d08e31b295c69dc52ef205e8ebf038bed90be5f70c60bcda7782207f3db2650d2154d7d774ee622bc31ae36232a1563d1067b4eb0e46aac2fb174a39c0b066de4ad7ff88b6625f7d7db6823b3c919769223f5971942dd7d3c9ccfa912f38f6096ada99838387799883324dbd265f55e9f8b5d34c3cdc699464517ac47a02a9939a82aa997f3a37696f4fbd9555e4189f787f9f71d3d290864aac4a1cb046a33875f5b32e9096f6969c4948b49ef670d0c8add2dd394568bd98985bc772598d18c3eed9327befa440387134671d89c27a04348f8210988b37304870ae817268ea4b03bdab17d757423f12a239eab66631fbc9b8ab917fb0d2dc0b89ea17114f80e01006014f1608233a4fb1f11c9d1b76e4ec072518d98848fb2373b034060fe6b635dd7af23d1188d3873b11a5eefbc847ab0c8308b41eee2d3bcc5fb3cfffef4a800eeb7a48b65d6991fb630aa53788d49e3abe8faa94d78b2c4f9bbb2769d9501e8c5303be66cb20046b5d82acbe1496501670b2e05439b70e57ac25fb3f018d4d37cb45bf7c42f62aa5e1e221c6b9b28407e6e79f9cf6d1b358d8461b9ffa992b0d40fbb18c5de6aab2aa76e3808fac00e780342c661ad75270afe59adf06d299d15461ef5e081cd4e32aff98ba8f7e200124bb361f1b0116e66e6595819e6eafba9af907db765da91d61dda9a00156948626da81e037589f371aab04fdbe959b4f1fe0d267b311dc59689b8ced5e175a1906f194b21712770f3b31c4ef87a826ae9515df000e88dac119c99004266fc18303621595fb33b04b0b8f6306103924ff5d26540b087aa20df6d04adbb9c265cfb6d23e1de3c43204f48533b6d6a5e7df1827e8f8152c75f7bad60c0c4d3deb6b3de5392798ba377cf6824017190849b04e2e14d774ad42480797d80817db91e36a5f26841809fc418d6695c867d0464db2d4390f4260b4f1bbdde97802cae777bb192f8b093b3ebba2a865458d262b1a1ee4d826bed829bfb52369e4a13307990bffc22269b41963c938b646935bf28b90ab1cb32fb8f5ea03229c8ec4610733b4b09eb5997aecad42b68e119bff37536d9c6fc0e240892d488c8f4e26757f640d94d07fd7a4b49c7042f7aef009a8495af1b9aabaa32b69bfb611c4bb6f53958ee3812cf95c091b0f55220cd3c6d8539e3682de051b36c348243bb6f5636ccb51d3bb4ffdf525e36ca342d7c45c5e112ac40b02af57da520e2ff87c06fd575aa8bd9a2128b9902fe131d9c39a9c7fbf496b50aed58cea85b516ecd60996ff961d073b42bb01ea4b14b6b8608ed3901a7f29302bd825fb74f5054bd1660ecc654e76eaffa4cd88fa28caf6256ec32ceb2c60bc50c1544c0fad34d0a51effc2ed614630e777d126164013d729701fffa0cebd3f6ae55644dc6a0798e2a068fae33840a03f6468b5b22b60db75c6faf8c97e0f069fcbf8fdb60c9cc5879357f0188b68376da4d6a0cdeca36e729a63e0ce5f59de37e3017deb575791c57e9620fb2de96a7d8f21be0a7301939ec61819b8ae52abc17fac40ad022fb06304f71a40d20d7784408a4cfb10636de17817ff6fd862d7ac0bb7c2b640b5751078c4885f4fc28b803ce31b20267443e1251f6f248e0607299a9d912e73a18dda523911c36f840c7fd7064a245478505d30994d090c2291627f44552ce0bbc8a37008de2a018a38a4c55ec085301b5dd89d51ef0981523d72f9697d8158d3d030a48ca9aae08ac397545e443675925f75cf51cda139d3cf07e73397fb8dc520884214c292b43d343258314c471f4068682a463877f12c8581681ae56116198752e6735176004d5934c99e2f84f9072ee8d9d153a340fe80906c95d55a8c9cecc6d502255823e95d971ded6d2d6a1cea2f0496c37e609195b0d0e500bc15228be89f59c4d5599a4c5bc19e816d4f6067aff643c5edafa203832bb50cb18980704e560ad70f57546496b34f2d535c779a2a51596e8c7855c05c0e7cd9a8b916782abc2430131ffc773837f1e26bb650ebb846377daa39324f298956c08ff5cf0fbdf2a3307ce0213b375696e2be34ded5835a6a2d73825f79c92e74add5ab94393066d3821526a7c720e83f80a44abf231a3222f0dc7a4538d2f9141f7d5ca2be517827943108279dbb22385046c5ee71ddf7ea41e16b077016028f0a18f000f821cd83ed20afb1638be0296efb9865c816e6292d4416e165d8f4974c87aa9a1f9a49620494694ffb742a5ebe486c5777243355ccc4d942ee5aa512d80452427b384eb5f8dbfb2a53814d19ac1bb310b8e47e243e615d68d9bb18f33b6c59e6ea4cc0bc61a2a24672f23d9b1430d8953f7be0dba7805714cd2984c13f8a7f55932ecfb1ba008738515170f305854a408f2f25b2780a915575309959024221d8d7d3501a3538b604d2472a73282228eb7ab5f1f23ace15cd85aa28581173b847650731cad92b488f04a9cf1bddb329051e1c411aaca34467f5d1c6105c5cfc059d78f33ea865c8f3787b531c0603bb1c6374ef72adc8106690f86de4806481b7a199ffff55106eadf8e6a2349d3025e9c4d6bacad5b6c569f519f2e85771d601c0014c28c570c17659b9ea605010700789703e086fa669d91ca726bab52998ec221c9bc9bdb144304adff22d3738dd7328b4c19f76c16be3f76833fab29afaa5b0c0b15135d7c3a80421a150c48fa4f65792d9ecd9e14e7cbf07530f0122f58a1159e1bebabdc375861850d77449bbeb5ecdcad20f2b6b6619bb9e57520d187f8eee371a0f01c784d1491746686ce3415520922ff10b49cc4aeabb185a9f41c449129b99c51658acd5ee7418027c7feb40ba420ebbfa2e759895257576d9adae4119ca5949d7ca113a18a1e7aaa4a0f5574b0128fda51ac7f91453424e4550ec0b7f3643fc4f7ac1900803085384deb4e016f21bccc1007cebd66d52ecd217adfcc785d857f162877d41016f197b4e2f916689bc7d4b205625c4d348165d917dbb981816180bc59afeca4184c842973693b6568f36e477a801a8b4e6d6c81710167cffd5929afb761c2101acbf754f7a08583f33dde4978af1af3510046f0db6756684ebe3e1401d81c00e20f3185a863e216efba675453b202d28c62e3491d6eb63c7d37aa10035c85a7517795a330b154c7cb2f6083c136e9fbb25c36e6fb192d8fd5d55c7d99f40dcc6518a419cbe4e4bc143aa301705733a263402ace984fb039ceae69907c022f7219788edf9c91d30281ea6026149c39e2c208d6337e76289c62d7febb1dd5101338d03c489b6adc2a94708a556c6b3558e49ac548eb29cb428c0b584cdbb554f3473f478d86a071e67cb2857ac6331c1a99c89537623d4ade22a5b8bb3f58c066cdd5a78245b54d1b46c5508e9aa5ca480d6c0197c30ef4ec078d86c7c72b9b43f53201f6bcd62b5ab8d7b67b01078e005fb58ddf52c091c11fc63a04db4d4f5ba97598ba81cf668ca917e6bf1b021779ac9acc81b2ff42fb06b2cefd491adb7d616bac7d314212a8966e5c79f95bedf02304968fbb040c76d423533b96402fe7407535ae831ae5cc893342eaa58a7052658c43ccdf677c5b9a0a729ce66716d1e0e7138fd5713c02d3d3aaeaaf2430543c12610caceb5e667c3eefb43e9c4dde55220ecaf3df2d3e55fc660fdfd6d8f4a399394c1fad386e3a30945c406ddb95cce3faa541aa011b4fceddf4f4fa5eb8491ad0598cb7f35dab27f8f5d0179ee5c3ff96d970c92dbd67826be8b92a1012207fd80ccf0df4f7663abe11e21717fb20a130a4b7cc3f2a4ad0909836f3255c844d43e9eec2e91a5683962f89c0b8c5c7818e376b0dad205bdcce83f18f207c2e7fc4ea3f106ce02ae803ceefd8611c8b708b335f8c2fd637200cb678123037135e0f237e6f9f18b04e30947bc0f0ca60895407b3e65dbb7720ab08f8fa46332dfd0e26a4dece89b67229f4a44ce1228e404b179442133e9a6cf90af982dc87c8495ed2f55e69c3641533bb5c644f8dcb161f8373a87f8d90984853151252daf616c94e0576a0df0d7d8c8ac2c6df90eb70af87ba0ce497d35a1b0b9941beaa5f6f17d3866a3bf3eba92347c56e7195483a6b66dc4d504da7b010674f4197340b44bb4cdd871c20502fd1caea9b820fcd47c828afa01021027e538b49239b9a990ef86730c82a4a9372e956c04a92b2ad355c1b423d487c3cbc112a990e72f755b721057880a4690a8d1f92099757ba174d7d887f1d9db36756d051ededd04c9b5b5020f15eeda6e02d5bea38696fc277177cca7dafadfb0184ed207ad0f840e0d27bfa626e48ade790a78f6f0cd5a127e283a93ada77af832706bc1a66d87f73f7f70b26859093f05ca7baa3432cafd5a42981ace66c4bcec1404856bef94b15c0af397bada8e30fed61f41681050cf98c9bafc5f6b94d8e9adc8551a31608eb43ff9754dc6d389eb6c7eec194c6234735aaeb5ed3740c5712e0a2c4d550bbbd3820d8353c12a80089856d01d1f559bb8c3d85d141e571c7351aa84001f802004f06e06e0ee4f9418253194a80cae1f68f3ea80186a35893eac6c32be4dfe0731b4322014d8b666a58081ba67c209afea223cf0750746672e268094cb4b1464c4b47a6b3c00476b645250105717a8ed7b42809e35ba86b264e7fac5d704493bde4bcbea7f7852649929b2eb3f1633f1f9fbf8d7a208dbc9b3ab3dfb9b2449c00afcd03ba03660811f9b6bda0d92003dcef87b32e1856e5d1cabe9568beb8ca647d0d32f9e2b040f0c30ae8ba71c02914658d6b47e31d620e7e85d4c6e32acaa125dce72acaf3c3af1770bf1ec9a2c88cf20ab6be60c9a44b0a092432f54e4d31abd701d5515cad6f30f9a62f20a5b983f39fbc3aa7997b9e60fae514f7ca3a18312f396ea699b7362b25e2132c34c1657adbeed960afbb0aac991742f204aaccd7f80432ad058ac4062b68d5d16e8ff8953dc32adc059dc572baa1b7495a0cab58ea3eb14e0103ad282a56b91ed5d43d6e0f18f175c98e02eda980b864ce32368c60381a9912a4494a376d874de059734e878e1fe264c61451161c4d303c98339c530d2a5329245ee2f081964883cfe76601fecbd0e9cb22033e74cadb3dc5857a37c64b5e01c1d6f809f810563b38d000c7557613475297eafb8e2587192fb9cb048869ac64a48737d4971363c80a054806df784cc5ef30e083e9ac6eb8c1701b08495a832340270a4b604a9f2deedeadbf50a16f12aa03dfb2d8009028f3e9baf68732fc24a95b65feb3fd0c25e7b52b021a50c85c02830f7dd0729bfcb440fa7a471cd5ed864487bd068a0a766b5e15305934d2ab6caa449ebee6c51869a064dcdd418306763257289ca32e0a7be9a93e866f388cbd85fadb7d68f55e346b6f7fd5837b3a71ac6e245b39c1f1bf48b0937dcc0045d32a2c8f9ce0f4a29deccffb65d35af6994a24b743137efda26c028afc0482ca9034ef0005a8ba5ec7c450f486b84c65deab89d00acbcaffd3191026b01017b56bad2969c35f79fab7d7e6e555015ea9872557a551cf7636fa579cbaa67e1867991c93db45ec52ed559819a3701d6daf9c104dff2647451dcbdd51ce1f837a86dce8a7e6708abce62b85fd8d8130c8834c7446c571139b3df6a375b97a55556f7199c74787303703de9ceafc1880179a61a075f80761836b98ae5502c1dc9d860a244328d1a33229c3fd7b61fea92b8bac334cd36b971071f38aa87670dd178417e4ef29bab34e4a22685f57e1cb12c899605bef84494cb058b1458fd683b86517dfc6e142c603e48c6cdfe3813a65b8c8fec9fe86a0a80a20deeca116178e5ae71cbf4226aadb7098aa9a87265437d62583528de7ffb1d26c887e855de6befacfa6ad6ba55b26dd1dd27c238589144a2080d8a1dfb3979aa1c8e0e1691ad3911ba66b1ff705856e0d451f8720d2b05964fc6a11509ca76b3060107cadcb45fa3785c418348321caf7470dd5e52f756d89ef4642e241e06cc8626af2d0db5da0d76ba074c59a0f8496f45a1d55b61dc9abe52e250a34f6ec79cf353998e712c62e9e7782c24b68676813113269263bd5a36801f88c39dde6f7dfef6ea4c3972c4958fb0fe63b268dff39f7b3df2481ea4acf1bb5db76fba5c4981b7eab77db2c23de2b11e2ead7db601cd1b6f5e86b5e6c37418763555bc8458eff7e802b18335c72ce12fff68ee581e3e230af6a56bc0c9dc99921ebcbd1674bee1b902237f9ab70edb6b655e82cd7cba88b8e2849e2f15dd206721ad142984f65ef13f3f143e5d95c25e37c4672f3efd4393c0f26b5280dc330e74a704fd6421505f65a923a4b21c40668102f7b15dcd0f82938fad13c4747c591a3ee84725634186877219dbdebbbcb83b98118a4640fe93305b571e7e912112e63e1aab96867e438c612ff5b32b828b09dddc147db76cffe1dc8b6542d388948222c1956118a203456e37ba6178300fbf711de38c758531b004a227f1fdab53a826adf3e6329dbc6cc72f3ae57e566c502fce9aaaf7d8bd42dcbbcded253e6478429bf846db7c0185e590d391a1da5eadfcadfec21aabbbe9c8e97706c6eced6f8b1960f41e6086baa9c2f4385195b124df5825dab2e57afce2ce59628ef6df2f5c644de08071db5a1ffb3d061c3ba083c4c874bd9d040c87dce6d7039550afd7ba98da24f7fd96a07f44040639452d1e8407272a137c28233a650706cd554d37c5f44466dde0777c801ca7af1b3e29d38e2b32321cdf8f26d269a8c1dac3f5e95f0393d2fd0c1911bfcce84da0112d556df7b06748c14c66b1b2910840991a75184b669b0a83bbd65a4267d8cd65400e4adc7ec4c706a478ed4d5a7923e8bf68941a6f397b914aa2137daaf543c73f8415329a5277aa90f6a632d1bb41ba4a9da1bf741b9e640dea902c906f510af80843fb59f17dc8dee8f27c4b255c0338ae75918311eab7100518f536fc3f1927bb56e3b186a9526748c61f7cd768fa1ad1e4376c6dd8ae91391a8a7671ec8b446b47d8d92a27ae5aca8d20368870a731aa411a2ae493fd133ddfb2a2ddcee5a8b6ac3665a6fee0c64124f07339b24d4ae9ddcf0f46ec28c636ac7fa209fa4c8cfac5c4c0171df05e4d8c69b8516816b0df2b05862471f65a7612da344c479270a78cbbb54d6f496a2333024b818b0d21910658e6aad69ab2c816a8d9af76b2576b1447f613039a5757d131b2bfc37351642e3ae6a2b8b262d4160225f937190bc7bd8b774edd8eb2d12108597a1e5cb05213fdb0cf0e1458f16c74c71105ad80a14ed1680dad776e6213c2f7a65cfdca20cb7517802c7d30d25ced7df83a0dbce5212f427989db4edad7622bd731d9d74b62a318858e0586547395d772f5f7d0ab376bd6839b9c15be9c111181d0d5c3cd460a0d1273bbb818d07bb0ed2a9dba9cb59b0e839bc040bed2809aca1b069a83dfe6da1b1b800bce62747dd66a3d270991335d0a80f1fc555a6c52b458369db029071183296ed98990f888ae7240bf8ba8609111b01f64dd4c7f9df805b81a58abb8ef49be421820093b54efa715f42dafa6e71a205c7252caf80b72d2ae51c0c017d40218932150304b2bd548e4bb009aa5a968d182f9d4702f4141208695d230ae6f4c8f7cc28698d3b0672acf0df2c36e57500429b460cc89eec2846e14ccbb2c2d569121dbd8fac0f8f291c02e152e0f51889b02e88b8469ef16e37354f3f63fd038696aeb8ae5574334845ee23ab2ec48cd066aabfee7ba85ce307242abf794d2128608510bc60a4774da2c9074ca44623460ed6da1c958aa10a88deba50b1df4d783618d2a30186845edb4d44a24cf3aee719f0db34cba41ad30430db935c2f30d276f11e35b47b6315e82534d07f8b1696cfba72581e5fa3cba2e7befa8c8350d68315c7f758d87da0ca0bae484b910babc2f0153255da145dee6546d8431fe487998facc1e8a18b4b2017c0f8a993b08a4f853d645cfdd7f12294f6e65057717656971d2bdaf8846350e6a7b4db5565bb91a6f13eb8421f1bcdaba61f5da99fef77b9b8b6ab5dfdb0a1fcbd2624146e6c54efe99002a7d1ccef3a6a049aa79b3ac7a082c9a9f6702df0e8f1fd1c3e0907d13ca2d5c9943be4b5465b0a5922f69f1514bea4cee8368afdb09264d20a92b4b0d54a917b8554efaf87062f2e7c1441b8a6a44eea9fb61fa5d28b2dd1fd20e88281f1ff52d248786693efa499ebd7464b3738aced1f499222af7caebdc4e21c5893461621afffc7edb52f2f928747875a10593dc114836349b219122553d8acb0eccbb105349265538ff196a8cfe7293dfa81ba58ca7b7979ee3300d90c5393ac4a82c6276c57f930346deb7721b993b1bae09e47cd6272867e5b236920ac3efb9b9d6b41e29ec033a586071e5e53cb11e1a8293e1b49673624225597c49a63755c2f53f95e7c5d0857afb31ad163362c4f8535e2b2df837d64c0b6adaf017851baa167877f63733b6715993e3105d6b1a8f6202388fd5d6ce021ed6bd30f7c2145977e786dbbe968e032ee5929e65bb5f1f17ecbba8a587c29d69fe430780d30434db3f205e13022675a75dbb582c0ec8bbf00b37e157934cd16864af98080ce4789b24c40dde2d45692e51726f42e5f5c406deaf27b33c2e268dc7ae03f8d466f5a6043b814fe0a23cc85b7f6e69b6750e3b9352b34e78471f628ddee680be61a2cb8b371d324451181e54b00468eb1edd149feb6bd8b65621b6908b9d90dd1d76da5d212bb6afd6094a2398b2c54f41650082aa65511cbc89f8dd6b8ba81a187e288d75f120fc6581583d6502bb0b22bfa99cd1ee45bf31a3b9039587081cf28a91496152901d9fc965c954cc71dba5c4dafee67f7c0b2b62a1d7616999b27bcea58bcbbd55d7956ba41793854e4d2c59b51d6a9114142edcbe9a6e95cd0a1733b4c2ef80dfbe56c5e92d516edc68646f89ef271cd3667e0c3278c8f968e8020bbe7f51b65fe82cba0ba3104c48716d03d3975ff1046e6644b528c89f10624c1f99b4cc2936f268cb7defa05d49f15d5a82946c98a634d9004a315d50364ad980915a758b35ce3e91962e96199ede91d2cddd212f2f3b1efa465e0ed0626a5fea7652b4fd2aaec1183db649ee9504d5cc415a0f92aeac9a4afc1288341d8cf74a711984beb31a82fb605cbe77599fac0c08ab17929c6367ac54c9c547385518ce3a20ac03f700d97b38167f15e8011d92526a9d6391a57e297222d6a00a9be584d59ee91449bea91c52a80087942c66ef1ac5bbe7f38a3060b444552cc58a6b92fe4dc679542f84061ab27264ede6aafbfcbfc1d506da2b43970db032dafc7056419c003e7614b154482077ca1ddc54a0a446a8116a70f0c6c241ce2b43567bd8416fd29471544ae581740ce95a52983ec0963aa073755b73f0e5c12d796b05c6a7542d9e4e4d092fb8ee0dc89a8f5465a43e0a92c927bb32d16bd6055d2e2abe860eb4bbd652ef090dfbc52ec9fb3e86a069ea63fe3390ffba165177cb3768e7bc5e45d0b86b0aac27d6660426af993da77b80794d1532c1e20d8c52e89fdc370f8f1b087ab689df216285ad668f1a39985fdb8dae8e226e54ad723ac96f8fc4a731e0c4116c93fb97c2857ee985a8f60bfadbd82bf2b1d97387c0cfaba64beac91e72d525beff87935115581ab9afd1812f1e61b46d99ab2e9016caa2b6b11a6959d1212e80aadc5c1210bfca8c00d8ed767012d4cf8101a2b4654e10cac7342fee7fd869e8fc83ab7ae0caf55189511f266375b4aa66227e9f3b1f0fd37cf9ec2cd64d27e4c4eab78490d0cbd03ff96237872959b6b64e2893c80512470794b1855e63b1077d40bf835c93b11675d24ad6396a5327321c19543d11bec71cbb802aabfc55b9d09c7d83fca03ee3af414fda88a4eeaccbb3f4717e9aa7d07a0d19f38bfb3ef445c3f6a5d0c1d9d89a2caf7812fc36d1b1e10bc5a3a5435af6e2087c34e770c1d73fabc8f0fa908aa4e487a8d2f11887952008622ab8b8d9db8d981829c54e1a5de3eb3766d0f5955ab74aa2486067cc75f11b74d400329c23c07210bb1bbc39204f83c445de9875f9dcf94e0415271b5925eb32813035122555500f78f0c4a108d7e0a1b119c278920e1f3ba344c1fe250e56a577710377b4d08dbd7dc3491f4c3414bb9fc995fcbd34d7b408f36f3b10d8af513d3a85a62428f949586babd70c15b0630afe1db956d05ec8fd7ee4d4250df08ddf254e1c27ca192accafad9bf4c9301b62b7fab614d0667351334c92a6bf8d2db855b9d2031d371059e1569b4e3df408b4e5613642a6ed26e65c7aec3c2a86cb2533e29e80c08a3d03d0e653c927032393602a02797119d62c678870a7a13b6420bda1889804c73e930f65179fe8023f3fb1661e64d9e5479e247f7c123e9666c19e89b634c322d4d1b3fcc45c61c4b4574822967601096a83d870ac9ef98e82cec79c008de2e0b9c7afc95e86b226dfa81932ba175268f0d7e390951e40af0b057beeeb756f7983496ddf18228bef4b3a8549f943e88946ba49381da02aa1bd67d0d737194cf1dfbb93e36b5ed54887dbf908a353b65092afdda2d4021b575f5ea1d4f9cdd6e968a96ac3dd6a442343d90b1a28ba19d955bcfe338f40f59e53812f5be502e2a082e501c3a356200b47e7f0575bea4bdae77a9e385ea9c2b71817fb942971cd659b2c3b0f80b0342c7c3bba06adc8d75caa5d44209a8bfc55000053d43868041d71065ca61939017c00d16f9398704c052eba7e4869d72369334f29fc432587c5608ddb47bf5e6c08c3ce58bb1257b12973a89b3640110accf4f26a7fd0ae57ac5db02b7709a27f811117bb33669f1139e2043ac9d1286f288cae9696a75522e8f4177ff0b7456f6170892c7865a92ad49af6a85d11ad4bf6cee3d3e80c7388eb3666ebf4997812fe14e795f7515979f5f1ba3de1592c79288408862985bfe5c33a360dc963397a809ebdb21c20d9b681dec0590f8d98ed8d959ef9fc95de6adf19ba01d9049128f3f9f0e087f4c68d201c36b8aea07a3c7577aab068f79f64cd709551beb2165a277d2a2dbf89e653e512f0f4d905c6a5ec25242e220cf838f5382262481b6b298e32438221effd31cf0a49688e574e65e8c081fbf6a680fef754cd9135a16a4f932d23b9524dcb4831b9fc01e52c454a451759a82e776faea3a6f81cfb8373859bef442ed3dea358bafea3501b6b72a0b2886ab83a210b5378bd9563a7908d4d33622dc3e026d79e58f16a22bb7d46adc2a982cede2fcf5ce5ce7c5ab567c4b60d646a762345473296141bf0833e797eefe35713af32669b1cf9617f75c785300e2a092645bb62f23887c13089796a40954bae6426e1898b3c683bf623cc6ccc138445c5c52d1166cec802b0b6b51d52163e6d0c55989cda9f8d68270f9924a52e197cd38c0bba515d2a69a2002162dafbb5258a61581e9cda65163040cf55f7aff0117d6c2587ff5b3fa9660d53cd50b8880ee0ed3153ae91c591e6cc5d16243c1e06d45e827b377e988ad5d617af7f3f2686acf249ec0e5790615d0079dec7f9339106d3fc44463cac394108fc86c83fba3b85fa4c78aadf5b4d9ac8eb78d0c7291239e65a784091e77eaae8ba450a0a17daebee902daf1d1e460c992f8e5624583c8c651a130c14c188c585cdde36c00442dc29f50e5d3e0e88f6729336366920956986592ffc25e42d4fdd76fa9ef2b8475ec56471226869ee8be0e6bb0624db6c7808647cc9bd88740bfa19d9cd43c061ba4139afb3e22d814d546f9e82d338119bb9875a19294c034e881ab51a072e2888fe8f7a911a0f5dcd830bf4d6edec76b0ae66524519092b7ebda85eb38b432247f3874bfc0023f6c0876dd1476d29ea17c71804a813e80544fd0a3755dc2e37e8d951c8cf95d65192fc80bd21441814bfc249bc4d8802e361d36b86184da2697f3856c3dc4f0b86f1fcb8a33ba285d0a3acef6a23b7074aed1ed15a967d94339086ecdf896a7318f7fb32b864631b18d5761e2fdb6c79a1fb5c043d70de28d5f0e69dd533afac3b3d2d9cfbcdebb309b46388d649d0ed32d30d3dfc918eda088fa39f84c5694b88b6655ece8980b654ec3d47c0a0ec7b7797c0614011f7cf57f9c46cd8d2e90d9125404f92b9dbb36bd3d9f6a3abe9edd2a62e0e595a2fee7a2f67a344090120056b3023ae6a6991d7d20ffb0f38d673498f4cc5cf022c7565a716845f054fbf1573a6a733f54c324376112fc507c329feb837c31f671bd5ef970c28519b66b9fe09e83a5110bc14c031cdfbd4e39ff38a552342f369220e9cdd623f4143b3a4caa2b2a87c499f3fee9744d5d2cc85171825d988dc5bac0b26d8e7f43c25378d8827053bd88b5f06efa3854b530963439d108d92d87088a853e0a63b9743b495fa2d4ef797dd9f2cc3e6c1641d40f112b2d020c13da59676a122afbf6b6d50355ba901ed3384d743202178c557deca94dd20aa16223155b93e7a59eaa0416b6dc4ac8cacb3d39d1c28bb6b73638628266a42753149e3c30ab7f33b01daecd2bbf566f8aaf5a35892c3c9de83f20b90284115ad4f46b9913d9985f1e76757abd9e9c288cf1e45364a6d2623c0a04d031ed2017393f9f34923d5e79a5714ad7cb7404e3fa0a90ac881ee08f39967e6a5da7866d6b2075c293eebe7f4fcfc80c22599d12bc15f574955d7f49e75234d2dfc0536849a19d6f419eafd336789679cb96fb57ac9a6698539694b197593403daae72c162baad2a1988df8343b146402ae6e2a5f30e544cf5511fcf075ae834741acf68cd298ab3a07e7d8b53f040c677542af1221e261de84cd62d6c0956db0d92ef7830ccdc7928124ed20488644300a99f7f0cbf41513a84178b710afd5b47102ca794a3b267a01623308ae01d60f124d88c8af3695bbe8637250fe46f53ebf359ab6e501aaea643d3d597babc437b243ad1bed7c096d2b6e2f0308708c0dead206f7d6092974f0c23d10966b4dc501a03dfe0e842dd0d2418d35dfa0b7237ec43698110e8329a6b2777711535eb53dc2677383c901724df564a01939bdd9456b93aca7c0eb6117341533a9335a10e2d587c11d9916d7c4890c1537a439fbee851b0824b1f523bbff93b580126677f6ce02a9a5034fc9341b8fdc790f9ca2a24820ecffdd7f6036e4cb06bc0301a0f264d46e84621649ad65c7f24917f6dfead5d9385362a829d54c14829e16d559547f288d5a59081f4c9c6f511af5e94804f10d1fa4690ace573b82f10726297953db7c7d38523658c9d4546548acea36cc8d11238a88d8501462131b9d0010cbe488048b0f83d203107bad3d777c587608b6173c8c1d58dc88dcd2069ecd981f87cd9498196c40940ddb85601c467ddda3cd0ff014c7f779cdea0f039f28d37821ce4db6669195cf5f28741341cfdd84718b4d573dc633c42030ff1e7b8d11837ef70b52fa62d2b3c6a8df64e0cbddd91e5ca87173c32a531eb6a6215f9ac61d48bfda398750ff039ce903561f62ee75e1410b8c884f889bfd4ead3621b1dca902637cf3b4d54800eb58155c8869b7a5e1ac823c3ac40a191c894253979d0a7140ecde4f6c412ae707fdf858ca6c6de95db80205d1946460242d8887b9219c6eb1d67d94589dae015e2cf4f4709b5c0317b9513044a389110d22f70840aca7c8c6fbc81846e2a486b27304e1504033e9805bc41194f1b04a56cddaac691f9d612a6c95ab3d2b7dc4f25df31f5aa9349fae70ce2226f121556872a1c1c6333ce81a47de02dc5adf68e2973399991810d14dbdc2fbc6c022921383df5c03ad371fbd00b9bbb7e1ca6c63bb9205aef492813f654c2fe128c1e98c3c2843400218efcd34adce90f64614e752556d77eca1c9232ed2d7cadb928e330c1ed0f88ed9834ad62de3bd00eadc32e5b79aae44e1331b15ac08356eedf0e8e6132e820b28990b837246cb03f54e073feeda6a8ec4f9ecdbdae07921c6f0b06edb399f77895b33c843d434a86d538e0e8e37700c88110fd54dc2fc82c686866cd1c29ed427050751b2966acb056818da3e79b84c4e37b1779da3a101f812af231f792285c789f087941431225bfaa2512af3f4121791b987a83c5c46d2cea321ae16813056e4ffb6c9c6c346d79f5e9b9a7fa70368af5a7ce4f6b3feafa57d1e61684634e07221012e4a03787c7f8f3da6d3107bcf03eeaa47fa8d6b09b2e7549644bad5974b374dcc1da11221f81c22f9343cd58e43f229a3b188163606d4a0fe1826f6eb66d1b6d8a3ecb8e3c434d419af53681c842521ef7f29a3660489543b66d80a1370600e2ad5316f7d3278d376ed39d377a076e01b9e95d65da55c2cadd3a2b6a3ff7d40a3cb14545d08550b967e48da7a903fa632b90b5667ce5048ecf42957ab53b8b712f0dd68004351e2c5d89bbfc2261bbf76795d1388b034514ad4a335a4dfbc17b9f736232a470d5d6b9957d9f7abafc020523c74bdafa9b82f2d48554d56db16a04d0b26938d2d3d13e34cc3a1dcd9a0f138f7471973e050bbe26f6813ae1fed03ef91daea1cab3490a37d432f1af4dfa4304d1131ff4f30cacf53153142f348f7b51a2fc83e58279f0e8603393321d86b97ffa7cf21c8a1d198bb9a8ee3b51c65e4a4e0ab69b62f084410f38d9039920556751fe3391bf6560e2acb3151700311331b02fa54e44bfd4ba103afe621cf8fd1fa83d8c30184a15f6ad48fa9c29059867fbd5caf143a40282e1d819bf3c371bf31f62df1d80d9fbbc5cfb4a18db255c51b6d55a912bb0465a175c291ab13f838789ba549214caca259192ea38bffe43c84a87e4cd5d6498ae94ee3a0f99656d3a825b7172e9bb28150ccedecb77aca715c441b45772fe2ce4e7635ee4c8258274f3eaa94c482d097232a66b7611c8efbd80e5fa315218ab857ff5ae03ff33976c4ff92f1c4cebe43e993f7a5a9b8b2a132e1fda7dec957b3b9e98d36911829909a284ed6ef1240e02171b323e922212c78cf84ccd2453b092e928c9a04c22f7825300f5345c3dce8e5967856b084b851921a6257a68d933b101b9cd135fb12a159c190dd0a212960e72c8a0fe64bbe958025dd845d296a509a760702692575aad8f0bcdcd9eeb7c83c5557b134d999cf01ad47c7c1cd1ac7406af0a44d5fbdb9eda198aef9c41e77061506445e4813239e41dee6cadccaa221fc66f6ad87cb421821fbc9172d04793c0c2e044ec1be5d81e006e2016ce7339022b7d52b37b36e1e648a02f4400902e9d8ca229565d9d3ac04c1dd6a8d4368f302b789915aef24b76d1859947b9a551be34ef9b4386ecd5cba2712d3f4a9afb50bd2f2575a8d98e75b065c8e29328d89a1fc74f2aa6174ddb527eeed80e5fe83475d9a78fa525901e582a05e88f8ffb558438e32d68a341de6e95e6944d10ff97abf89cc5c4d4abb6a55898b09a9c46810b54548556ba7296aa31a80e5c4e2cec5dc58b0c8edfef3e41541b79bb844ca925901533c7c0ab303f9ca371879cb68ed6fb9b900476dfc175be1803f14848ebd88d6af69bfe28be8d0da3896e07d8b8d89f40f348b5ebf010698e92d44d4c2976962d9b576fe6f9bff1ba382a4a3da6a85c7f087df812800e3939c26b4c44de3a2bb4d0d5e5e6c5c09d76524318c2116037afd338985499b26049e7eab7853d6a773c3ebea01f73d010159351c9a303e11c1d5bb9970f18c4ff54d4b690a7107664d73770cf628b8606d13ebd6e4fe80549fd336e5c6808033a8e60d693f5dd3a339fb38a39654188a539d984cbee76662ed9d5d28a5f1a9a28120487f888d825f7c3541851a6220a2eb06c7380cb6733978ea5e41f0ce2abed93029a376c66fa7eeefa056d6f2fa00f025be678e5b4a25ecbbbef826495f01101205991592a7fb231f5e0726c00a4b618206d9a14843f4c30473b4e091d92f41f05ecc1269559fe17fa2bd7b881791513551312b058be92b229f7454c153776162f92579b10ca2b35ef37dc95e504b6d92a235462325f312a95953174a3c4bf8a9599ed4f44f49dafb0fbc49a3b2330b5db7588a792ec00d68677addcebf77680831cc74302c40f2b0268144b5f1970ab4839cf8d466ab3ab00547a45e1eb272519ce0a613866863b69f3e5d9f7b79eb9313dc04a09462c354c2845fad52e5406cc412b77a32c071caac2c8032840a4dac3fd01a6b4133c16e438ecf28812432b51ba02a939892bc84c087b54be99705e77a344021f2b70964c2dd445120323c85b4a2a16334c682fcb0869ac0503cced5b51fbc5abba6b528ab0bf139685f9b09f313c0e9db38cb3955d49eb5d72885d694d8ddd9e42531c6f11297e1f15ee92be370a967092a09eac04759681d0eae33e5043cdf7d2d4129f9c9fbb227270c4ad69db3b3e119002a3726783f273bc1abbcd469278ada4c7ff8acbc5a839aa52a33a8223b3a03bc80eda648a6584a220e504d435a9c014f2ec135faf78cbc7f488b532a868baa0f4536bd4323e7e27c1951e72c65d2b8563c311b676f7c2f7c6eeefa176085a6f08b62e03a23c131739e87bb9dac05c0448093a22dcb491b8389fe4c6c962ff8843ad0e76baa23a6ad6aaf29048e3174d3f71b5e1109852cce9b92a8c0b34a065d5b761f9baa13b335e95e8927483c95c7ae2baf97f59dd50d397c6714e0a5f1e3cfe4544057ede6ee94bd4e21b483ea50917d1aee469701008d0baade1b55e707eab23e929cce9f730ec14831c9be24dc7e071839714ea095568881caa5dadd42a370032a289fc0094731ba4946d479788ecfaaee55b4cd3466a5b4b2446b04a30d5a5503e8523d958afbd1ed1fc70f487a579d11d0816073daeaeddcf85cdb54b54f177e1bf3ddec7075ca8ef32f9735cf82658a145586bcda4a06ae26d61b84a59a0baec5ac897dc5663c81b3b54976e73c92d07f26fc98c78c733eabafb9559e843598ad76cbea68336c70786cdd7e7e0d680e7d9e73caaabc8b4f8d35dc60a19dff7373017d59b4106c569a2839c7b7b274308711c58e658dfc9ca9756cdff2c1716010df4eca49e2d1c2c7fa17ea110a979d7d046788ff86f57ee164fa2491787dd64a5e13a745eb60638c20017e013e04f3572fcd247b0d71abbea295dc36134b4c238b963a08b00e1a690a8374bb12be81ab11026a27aaeb533d117223a35d4ae0467a7dbbd0d5830d6917290a5870e6dc46f9d9398b5173b4dfecaef3bf7ec096ac4253961925d592ed75d9579b574a129bd1025492c08aa835ae96cbb461271215ad48cf5b208816e7cd0c847473feecf1d419cb6bba5d77fa4225a20288f5274846ae94f5987bdced38d756bd8c396fb245a9e2b4c1326cce16a120d8c30d3b086a2e8e6245a358ffdee715c32baff63fa01a32659414a0e24f2db0e2afaec89365f7c45e22b662f31240e7969723e008876479cada45ae650743cb4411d80eb74ade9202275304e624eb0fb12fa93f1586f671633907e4b713d4c05b5f2b1e70454916e45d71ac2cd380764527240489901ee3a00cea6088faa2c5c9a799ba93adc666e41ca3ab4839a53321a33195854748128a1596b4982f223b41f48d514b6a484eb9bfa92d5af769bed16671f625442b5b0f289d39e8449cbd8bf01226429c5f4407af4a07e75618135067374d6e9a2b37e064ed786e6c7dc20cd45bb73b95b794250689e8a557168277439e4bba35c842c1ede78a1f7944d74f4ff9ae151b6051612891b53a3d9f224672f8744d6d09f81d5308fb7ff59654588084f44158214eca054eedab94d6930b15f1452c727c5868f14e02c9d9945ac048439c393af89ab61a422ba67e4d8c07326c463a2c5712ed5caf34420296a3749ebc694fbff47f69753195e2489d98261da76dab7831f5e660aa1e21961104954140165593b5b0b6ec9525a337ac3d0722d0252cb0b1c9b958a0edff3471ef0d509ecb6ad68d5e602a17b00ea99fc3057236d18676cbb8bc5b6988a8d213d1d31634f94a952510fb1ee1b66ea3117cf65463fa42b44b96d303829acfb26b791b30780b87c1c7cbca3afcb97f18adf3176b79f003933d671d5cd48d57f049d8759b5475a5805373d53b003bd004790bb1a6403ac35a6fde7e9aa250ac2dcbb0f9872181ca2f6f1c11070a53a37cb88e2c4ceef08b0dcead10ed26f90dc3594b7d2b1089c4caad77fb6c505c9a141dabac72e3ca17234e5567c08f86138fd871a104dcc34b0bbae329470851487f16acba4b6da1f4f19e239a6d68dd7c7819706a2aa67e77a778db72d911a77801c443b404e6e7a1e91664bbfce3ea8562cc8ffce7700244308983df19e007043dd66d86cebf257cba8227f0993ae355c4c6a1e0dbd5552350495524e5bf594fa653073ca5a38b4ad6e20d5e1f4a4b44d54b95582d74da101c06625259ffc2020662b1738dde95a6c67301f8d887c32793101f74408a92a83c199492de55223dfca478067406586faf309c2ac2a15c450fb60265b27fb83df96ced792663d61f8eebfbf827e939bd0031499083c8e5274b13e6ace4991cf85dbd35621fe46219477bc996b6dd7163c85cd84c8f05ae9c7604e19a8b2cda641afe1a20db79a5b2d2c937c9f31686694670805cbd7f4251f99568fc1b9afbc6ca0fa401221a5e3bab4365240d75b84f64be0195a44da6d2e0ffb1cfa8416ddd994e15307f86225c777d8257ae54bde5e6c5ff8b89ef5e225f4b6c7f4c3bb50851cda62154d0081d34b91344ac33d72b0faeb2605e8bcb69a9135cafade1a1c540ac9e10193e95de6d58591a202918c7044bbf0bc59186736553289cb3397189df8a1b09dcab9378de24dd0434d46b2261e76f2173112026dde1483186ef7268294e8b91e649b36e83f3c92570b334e662233c272b9284a6c9a0da34bb670ab02f519863a3b50f874eb95648b740397870bcdce75b0d66e7d9ab763b04ef06cf5434c1b02c54c51e83a41d7cb7a9d1d3c516cff1fd371737211128e46a2a865fe1ed3aac76d689d9cf22711e879b8d76f1b889f61aa1df3b03dae0c35d49ce3027182ed32ff993444c8f63e4f4a8065c91ab8b0944e24e74cd1f1a244a05068b5a43b1497df6ebcc3a59956cd10b903fa5620b949dae2a184dd589154ac609f1aa9ef3fd0c3e687556e6c943a1975df244d8786240c59a2e490d50aa785bb0eb33545ad00aef11fc1db7b1a674874b5f89dda6e307a6eaac1cbe471c6dc279745dba6e8a982ad94f5e26ee00542ac96cded164bbc9b3539e4343606f84d19a41f76a3ad681a343eb32f7a67978a496b15f4914625ae662a173d536ee6f8a7ba9347d1131d7b96a3caa44533f5038ea67e9993a874bae31966bfbc794a1f35b9efc249a6076e639bee04c1ae6234f66cc53342b9c5983ec68e165797054adb636ae71c6ab6973b63997012747b504ee426a46230710528245f271b0b4b120e46e731cb9f978abf4be721391d0915c46a302ddf7b61c23fe34e0b7988840c8b06b22ae586443a2623be6a79cc08940bf004ef733a5b7a559fc8128986c71170c5e461334b812d87dc9ef3c46bc0bd667a144bc0dd7c26522db94ff204d942a10408f3107b8d3478f51018d0d7afad1116121361260f785beb5185b3a4a91fccd66e982ca89b438865f0d60913eeeb9df77b36e3e79738ea1517c888b6dd26d8fdecd1153dc527187c9a98d984552102752e695bc2c5ae18fe5ce73dc0f050e0d2db7c69fcc8c30b878821a3d9df2a4214869075ad75c1f6e5e0ba980e07f91e53ce10a5e8c18fba754a722f28a369765561456974c0dca108bb1f420f833acf526301b018564d33e1aede0335b000757192324e7df4f80e4c11e5b8d806e2040425b926c26f9be6b1e0ef5dfc70bd993339bd39033abca0237dbe6b2073d7327f314883b052f3831052c2626ed92561d50b1be74c5c423ea7df697540c6ed4e6f2737870ad91d6c80554a17ad37ab9727dbdb821662bfd23a2f8c3e8687e299c8a7125a75db559d9b4518a2b1003186fcd9838303260186ab2b0e44b35de5c8354a68ea69d174b7c33c10f3879709c5fee8c1dc22f6b0c07527011b0cbf6c4352b391f162e8c7424be55e855b27d3ff613db56e85525af830dd0233d1312a63e52be6f8946afc365f9e6903b23bee323535c3321f4124a404496318f6a1f94230d8452456d53ed43f322d04928d02ac565e5d5481699de8b7efa0ff5d9718a89e64d14f05716e3819b67ff4bc5548cd8d8ea9a94c313cd733c803f15c1bd8ae962af3a4d5c491f590f2b3b0ba26c925e48b1b9bcf8e7ca281091b0f5d9ad2f45b655a2e5ae9739edb51658c7a7cc788fda2c833646811f9fcb2792b89de42156a3c34c07a808879bf0abbd9b08343b8e08adee2a3795d5f24196e719dee7b220dca49eac557a741b2bf9512b46e665ae7061f0c9f4421f8ef5d9ffd215ccbd837fa15ac05195b6ee4699740a5d835912aa1d8ca58246b465e9462ba9de0f9fd555288f8e61863697560a366fb0d694f1a03c78ec79130a2bed12f4deb851f40d4eb27fd5c69b885484e8243138a5279c4547084ca1d9f1c6b56ca0d4e4e3c0b616485bd8fe6e5b8c237f8676ece24102ae885238456e04d8cf72437ec20acc47b707dd0d1c8338ddccce6a67f1e135eacb57e4515deac7cb2705b1352a16bbc9de10c2a059f1ac7a2156ae841cb41f6f0ed1af73cd166daba0c5d5fa8382a8fc877b746478b2564aae4ce2a998b097bc527b44b00ee4bbfa0a14c7b807adb4d31f070594c494fe1404537c644290abf3174fae852800e6502c6c04a0d00cdecb84e291c225fa40ddffd53f28bbf2bde3015acc0a03b4e6636ade4f4ed88909ec9f51b2c1028dc74fe40664a989f69b774ed56388b6cf2d7a3f5b657dc816b08766bc3c21724284400931a7a1d8fce0fb947cb0935985338ae90ba30e407b2bd5f5e85751f6be259a01b3fe64bfac0f2f96e92898496c51af5580638e45890a8b17957278a474adaf90835c9bbca65c39d6a5677425eebcbbf7d32c5eecc1c495440f1abaf550ab0347431c9b24ddf0f50ce24a2af3d964039cf782a7bb6ab822b85d6587576596251728953d92f6843d14c207528386942df47109f9d0a405a392e427f1f34b93d83490048db38bb81766e35cdccf2fb903cef71e4c9228559dd1aac0de7fd7658409169c468d382eeb512f87d7bd8e50015f74f207b1044e89644b87db3e0097a7ce2436cf90de5f043157bc942683d0f66c535623ff162b3ce598af3943f27d1114f9e67a466c5a08bea0d97c2cda582aee94e3f8a7f7c4802b7df4fa798b0da5b8378b03cb1927acd376467d1e78b860096683a713e7d3cd8416fda734ece30b6dd376644f1ce3f4dc0f77eba11b913ffcd52335d61891c368ce76e63454937007b63a96ac57e330687ddd2ae8a9fcaa6014849e70e9c0f981447e28d08eebac4c3b5838af4a321a535911f6c539b109705c06b051edcf5c4203a3c628891e741ec7285163099f07f471b47588adf898aba0b7189b80f525841d9f3e1093824b4372fbe738316a43342829b7b1e7374978610bba6d961b3ba4a74d5a504284693bc9d1ebb69854370d0b3a8771bc71dc02077982b8bb03338f600a3d2ebd98a57bf3d46ee7225c618f7ca9071c6a958740202610ea0282252df5d2c62a6651640c83fa8e37691165b359a786ec6858480678b62b8ab6f3431fe743cb023d9b1b8223acd86b169a164e2a62e3626b780cefe4a4088432df4a244be431ae422f1d586faa7bd221433e88866ce7aa7b42a71cb263301a120691d4b5ffcdfb6b8e3209762b3edff6286823aebc5451670792aaff5247b313378042c427aa37816efa2a06a0424f27254ac85ce13a04cc641403b8da44ae5ffabd82e7dcc195fc8ca23cb085482957119db777e14c787d28f82d83b243518e0dbe28ffa5eef745405e37b4973bb43c563124854b494ef58af000c2c182fd76f58a8743bd360478432d4b0ee97ce1c581aa660ff111e65f390cdee20420d8fa4d8d692076167e6b2ef772695fde4c3fd420b94ec04761bb129598ee707397da0518c054ce178a646ce0e9db289fc301f2b432acf1e1cc9ef5eb56448f598c72586fbf008c08e07fb047bb70de34e85f5dfa9706bdace478a0be5a82420becef94a4f1d94263de507bd21dac40648c4a681dbf628f8707d2d2c6dd401904ee01680ebe823304928cb0239193076fcb5efca1cdeb12fb1e109e8491e5207322adf4a044037f4a02818fe52abd813bb931c2509640afb1aa2cbbf218b08f98472092cf7fe0842444e10df19061e34523bfb51038ef29cf29ed18fac3f290d2f211e228a219e0ac384d3ee01cb628b6e210a684ec13b15ee16ff5c9e7c51e1d83657f30d74951381a2cf0f8e4bc458290ba4b340d7e659294937c05ab75c5ae3aa4a38f4a4a0fe0dd2497877a83702c83724a24de7152d459239ef472afe5ea9ef091b66012237412cca772d7b391d89aa8512c21ae54d3dd91f2bcb46691dd9d955ab171df60d861860137a5a5dfdd6fb4780b2ded6017d0b663bd29a4010cab8fd38eec114a37d01aff11406605486fb8ebd155f275015e2e3d893ad5220178f97addcfe06dad81826225888604530d0a16571cceb45635fb0c69f60ede6ff091074722a3d749cc99eaf08c522433b2a75c12532437de9053666968d66dbbfa4d54683e1635404e12b975997243d9012bf523ae51be174232da9065ed45c4886165434e87a136943bd4ccfb9c5e62aa2bb41ec270296fb4a2eb76f1091f79a2af8729b675caea7774babeef5a5b1dcbd7b88c31d83847edadc280a4e02942a1419c4f8511fff75e4ea13d3d40737ed88ba62cde622fab19725a98e018b5d51281eae636a7b547ea4941c26a8b7b5916fb648e274040c6ae13c35fdddd2f49874ddacda922a0ea56eaccaf1a2170c18847246c8feece050b4a1511da13d151f30e331880db300d0d58cef821c90d02df1e817e68b9bad148242f4e016169eedcf00ed03054e24275de95f013ff591ceafbc27a9beadda8c0f1adc4106a00e37b9f8c1d2334f097b9ef6602cfcb10f8c1347717a563c92f2ccc44654df86a0a25d84d364da7ec0d2008f677fdce7e454211244da0baedf597cd5a641a82bab1e959d71eee131a0b05682917d427db195b42fe63b504cd1fa98b8011218f17891852788d1763abc05c7980a2d88f1102a42db1d38804c9d72d7bf4f909c1ff1dd4cb3d2aa543927385e0c89624aaa6c6399ac10f597c568b01612dcc3a981d10989c4042141611b5fa3c7fa67b70b2e8e8016c41abe720ab3eef1ce57c2723de3a48133ab87f047f894f2cec8b5e4c5ad1d0dca54a8315f4e82f1f2d0d7c0c5f60f5c51ebd70bc013ff448b596848de3c2bbe96a42941b86466d643978dfc82667b19df25b491ac04907d66cb73498242f88c54871da6d1c592eb8b9d6d7b3f83276cc4df65ea541ce82d7b84cf2bd3e6a62a8631e3c9c48488183639399ff3e7a980ca4a0fa9f41b6ab69923725f33603a0d8bde144a4a5cd10b8ac21344be6d7045e58b680fa5fe684f3199361f5dec7ac2a331327f4fca6615ec591818d8a8af43a8c92436c72d8a884f11b9e414298e191192cfb6641ba2cf6f1a5f494e5207e14137e16e6d1f78a2c38e6d1cb44e36a214e0587323816448068b04e22538c1f68e712c42311904426712dc5c50e89908ca93707a60126b6e79cc09ece4992cab2288e226656ee3c83530971ea6196aab06bf41e4ba08ab1423559e22b009c4238335a44f64e6f68281d98762c296c8f5c0813904243140db2a3db11d191432cefd034dfd335ece71dde2303a9446d9339ac36e59e4a31af53722e19a8ea36dc0c34dee2912a276f1658ad823e42aab286e1273a11d804aaaa3c4be2dc36e8e5fc2378c64d1edd9f26d245bb1c0f7d9e3413eabdd20e0b171275eb208c94307a5320a9604b32af250a38ed1c1c7d103ab0051d1c0e781888440b77d1af5d99c32ae8ff3418677b3dc7ce24327f9a934f8c02843181090123229bf2c463f75bfba55dbad63689cba2df98a203017c8a03da1e8a6e5920da15a903d9abd27f458ea7769d7c31cc9d5666cf13012eb38c2e4c5d4817d6c9680af1660be02e4427ed4b600ce01cd600702076e890f040c95d081e7d3bf0c3b271a7f28ca15b4b4222a8027941dc6def2df79652a69402d206c606e40623d80c9ff1a2a4c1f519df2f6962824dedc334da87b3ff89c940c791b8153e3e05916bc120a89953cbb7a09e6680a26779fb32c0202da8a7960641508752097452ac766fa01f10380871b9efa47077e53d6fcd65480a19af7d3cf849c67becf96466762dd44df0c5869e665c0e5c79ef3ab9a28cf244ca64109d4376219bc005394a981692149200512e71fd59240d98013d246cf4b22ed64a47739f59f8f33ea3588b5d064856404f829dadc9f290460c262b7c6245d7d171978e8e8e8eebb8cee7222a78d286bbf58a80cd521bb2da9881804bfb47a24e6e57e089629ea8999aadc93387cc23e66a2a993ab3c8f567c9cc2559ab944f230a2b6a2f47d0abdbf990f1392f36a26f7911588410a719dff2334432b04922e1924abed8885a5ef4a3a9e049c97ae6e34d230adbaf434a6004bdba3db4a6bd4c617d786eeb5e0629e78712b0f839c958dd9ea823660be965c6b7bc10a7fa5dc04f31bc88f46223fad18fc09cd18b5e889337832451a4171b979ff12ea0444998ef67bccb8f482f3632fce8650047e0cbf72ee0cb07eee0a7183e0650aa402dbeaf3526634cac48592c2f56fba25d79d65c8245a14260ec86fe7850b7012d131b70bf1f31d109a0d440766a29419354e28f188f9458f16f0bed6f037de8d5953ab012a808c5668809569ca86e990c2543c9503294f6a22b64133551ddd665c4609c73b240f30769fae91af3266886283373ce39c70bf8a987840192127203c60f128689cd2143eee0c8144c4345e7a84cb3bd8c0178200b143b234d4088f18425cc9546bba7e3eaf63d5bf50dc8f340b16dd53d9de366b58a75cf0dc8f34031ae9331f2c701f246de0887e7755cdd34dab205700956b67a70fdb9d3a4681c5ce7182acc47f86ebbb7043e5066451ec23210827f304d739288fbd2f4da4cb60a010f9c59955158bf75369bcd4a386a6c6a7b3ee0669e3c7bc6672882952fbeb681de337366d4629dacaaaaca65bcc2ea936aa44875d59eca9acdcc524ad7bb58ba4206b7608204ec94f36385253b7dffa3a508d7a90ba7e0aa629a15d3f068095724565c7f1a46ac58955c7f8fe723c2ca97cbc31e3f1a94b0defb007def49972b7485ae303564479d02099575fd4d9508c35415c7f8fb083d28641a661a174161e57bccdc1dc182eefc5e76083de879f0fefbd16b94a88d22286c836255d2a2bf4d3925d80357afd65ac11e82a017879b99b99b6a929341d0db83f6cdddddac854421c0fecaea09b2bb3b6ba129d65b591eb6a42bc4a7461df1fe03736a4769d2885362eb0adc99a982179a68e11a4f7ceac0235e71a4492e61187f9974a9aaaabe5c66565555555555555555516aa393cd5062eceeeeeeeed6f89b58e107a8536c14628930c1211858d0dbcf612134b9623ce5fc5881bdd7e81c7c7f4e3188abf56f93f492c3143bf6583640840cecce2311f1ea71fa00baa28b2802907f38a658973bc198115eb8fee5e072ebd7e47ae86dec5b30476af1841e273be37802fdcafb57635f23d9db24d6a20848ee46e26b493d4a18d44702033b03c2bb01283361cc4c99b97182157bdcb6d0460947e720293839729c1c512c2547ac82a8e48e4c713299176b99ccca703c742c2cd3f8f4004e8f3aa43be372de6b37a42947ee69a0426a82650f6d3cacf2c2c94cb0672f9cf721ef43a00fd07f209f38506c1f4e0621c994370ab115e4389415e528479709e226ef1df40a446f973f0938b39d1ed6670f3be5212705ecc2b287b54acb98a60ad370d30939cae44892a3cf26784029bc4e89b1e29b92a6940c076d6a9694dfbd1ca24dede27ed324101c348c1ca261bc03454985acc17599c4f5e7da88ebae2a71a049204cb3bd8c42d6402661c428a1f0d6624c09639b9aa6699a8d871330409bb86bdc03304cb3b111b817390b1a5101e3c8710d23a76a46cf8a56b5f95a47032b8eae7f7f5add3628e69cf5980e12616549ca4d1bd5c0e66c369b513aa56bdba9b5568ed5e7176ddc7e1a2014d45bc11078c8347c2587ce128707d3c80bfad58e0ef49fa67d0fea3ad07fdd7b3c80fe034b2e60f0f1bdb7f2a1d7b4eaf2848f2336a15f791f1ece4bf7f7d2200f3cac84409046c312b603bd9f76d48034baf1e7750ffa1c30c7035d442f6cdb01345260020e6ffa060bbc600710217ba53886022c2b712dc9706432994c86c3712b51586245a68a8c47c8d76b072baca7dfe1322f9d16e395e08b7cbef385f8a10b560c7b8734596b297592cbf526055d602d8bc5816dd1c76d5bc48ae10da5290786f187b2e347cef9e3bd58dddddddddd1a7b5d8963dc9320b650ec77c55207c57a572c953a87a39690da154ba551693492a65269541a953a28d6eb4ab26989c545370fdac443c2748eaac48b0a2f287891058d2456641f70fc72a84f3f1de697c3b6b5ab3492168a5de176668b039bf5846b48eba458012feb3ad0c7f7476c3e90c197e9e3653e9f3a1a9858ffce8ea4cdc20633f3a862c16ed788d1bf09863bd6a5f88ffb78e83eac924fc9673e33b39dfea261fcfb5be7755ee7e7013257ecd8f6bd063e17ae30852f2440ab44f791b9a2ab1e7045dfe19058d9627948c308cb2c27e22a1f1f1f1f1f1ff7711f4a6d4891125aef689675234ddc7332c90bdd3a311cfaded8a0092638874ddff0da840a7ce246e65070a64acfbc9182cc27e8262716ab321addc4b46dfb949873f2e439e79c737a366a74873d86f4a9128bb2a3720c6c62e53fc7bc5eafd78e275c2e53b581654c8561fc64fa77b519b6b05f415ec781c03ae76c1b7c474cc704c3f7d0e50b567ee7c0ea70b95c2e970e4aeb1394520aaba1d2b44d6a018ef1f7c14d1501579ab6efda4198ca6d928d4d0678e81bfe1b49233961abc84710d989059886effb0e1ce3308e03d65ca06fd8d0bef1b0524d2bdda0d6ca951bd6b18e553b44126fc4f51ddf683d0449221c7e2ccb1c33478d7eece29a63099f1d2178bda28462b59b03e9a43ca5e372d2446b088b476584fbf9f9690ee4714c41946a744a3baf683d10685d292edab4c30b870215ae37ca9398be8567e1499c0aeb38a85e83ebaf5d61454fc16029580af6ad48b1e00fc8c30e8aedeed63fb27fec0f140bbaa2ad3d52a0fee43df7d607568914de37cc0391c026e714c7b1384f718caf9048adb8221e4ed95cf4aa6157a86c20e7ac54ba87e826ae607d0304a5e01c70e863150ce33f04ac75450f58a9fe7151e9d98fa760ab15acbbbb57a9558a4ba552a91df8467f123ce5291c351c637323831593b55ab162517660465e4f5a3c158562d91512984a07ba4e225defe2fa92eb9d27a58c59e9ee4ce34b347163b8ac8512ad4b3f20fa5623ddd717a7914bf295af6444f9bada532b5c7f2986bb46a3781595693a762cbc07bee21831d8e453ace46bc54dae285f2bf95ac9d7eab5922ff95ac917378bccd554ada68a065484693628388764b24295a039c6ef064a9ed56baa54413c0832e64260182f5e1edb9cc0e4ab5153c5327649596513922402c748270ee3bc85273df5b56af1fe0b710faa3e05c738e854348c7fa0e85c5c7f0f149b0572c370a0e85b340c2849c334283a0fdab7a002e63275c534dd73a01b218b24207f5fc95713962fa6e1a0742191c079b5d65a3f0f9c71988ee11bfd376a709027c31352c662b5645294c47660515cee84a65229562ae52c16d3905a002f4bbf45f9e4fab33c6c79d87ec475e2933e85c19a34119dd5800684218924830c2c2e1343abc2ca9f48b0288be52c96a649901315a534caf86476840f95580c0683c118c635d2d4fd5fea1fd9339773f68c695cab2995c23006a3c104eb75a100ae07be50d0f75ee841341861c546813c1fdc7befa37bd088abb31aefb9f701faee6342b0f48680e831ee4b9022bce7be08d0772fc4f51c071cc3c2728e7020112f35dd5f804f1df8e5d0af69df0d25d0c097991a5e72a0e2cbe6328515676ca0c252c043972aacc8aff9e9606f835270df2869b2a222b1a8158c4a4d0b6d947c73fb82f6b17b44b1941cb10aa2da49496696b254b2121c8114c0915c9f0234763e722b608f122c7dad6218e92cf447a91ab97188d82ad625aed1d5ae518ee338fae823a5212d1c368e23eb937294b526b19386173616a341c48aeca2410496b5d6da0edc073dfa4ec3a0513b49ac5fb1562c7058859dcee1afd1b7f01d18a094b8cb437f6f314d7771794a8c0a1c240a2bfe0522040926c6518bc562b1586cdb3e2ade88840ee69c7394b2f267a9d65a6b85511a6cdbc61bd76aed68c4b6246bb576349261e871746adbb66ddbf64d60bd84c3a07e7ad997b5f6e572d2398cd008b8b3c72f9670fd6b04b1628ffd55607b50cc61db8090638cb1c9a003db9f433f10728c31a64b5932bd2c2c10d26bb05d1c9345dff0b79e85f5cb333995ed970b4c59970b4c76066b2da53c76e0ece0d14ce03009ad262499210a2bfdc331eebed2beab2e97cbe572b96a0d39c6c56264345d78508494ac9e73cab64cf30dd1a6b053a206334918c169bf7de55eba7b0725a4d324edfad73f728cff04c230cc800d06d75f93c1047bfc306261457e694e58314442b550c21da3fc5ae7f49e98218e391acdf9df7279083db0e28ae17c71e4ee4d50a9ab80cbaf99948a85bd21f4c51c51ea9a0b344dd364386de29ee7f34fa93d3f0b81c9cbe7f423e485cf890bfb86bf0c0654775a9a0d8648c2ad9f6ea00d35e4b421e461406986366861e89a0c67db4036d05ad358638fc5ae7c2c76f43e3e432c7b48a5f4402bf60b5970a5863e10e775dad4b8bad53ac339f7858e8eead553c4e52d77e22bdf9931f7c9e33293089b5a8700d7df554ce357c63b274df64587d1a0c28aae13facb2eaacb50a699b22575244a0a6153cb078d04c380c050b6c9681e92249d837bef76fffcb5a3d572994f6b47abf59ab5c637411554bf1aad1608342b28b45102b9cb16bc6913d464f496d3371c023e30d23afdf07666a7ae93d137ea26b51a7434806fbce66888c6d1c98139559b54677c05418d3a3339335f135e33a3191d978e8e8e8e8e4e8dcc93f2ffebd7afd555841498b0a95500609af6de821558e50425d07859b15dcde3aa6258da4bbade76e530720414a0508da7feee9fbd916c38a6c64350b4a06c5718566cd503c18aed5251c0c3510f8ef177cd5c3eee72b98b028d637bed29f8737e3e33357da8cc586c4ad122d31fa4053327d1a3eaa9e5f9991f80aa27d18bdc1589fa7b5e791d4e830afb329faf769a3e3486478931c72933442d2d2f12b55811c9cef8185e943f35325e87c7a0b049fec8dbf2f2478c1faf63a377f99834b98c4a2ea492e85b5e9425ede98f07913f31d0ad21c00d3d2af46a446143ef695cb062c76231f9c3f2f67fa68c22653226757cfa0649362161a18fa7482c6154521cc6bf3562ad5432406fa9b10473c728188bc5c299b2ce82c5c62ed7eba78a131f1e1915d893e972b19cbd8df64cd65a0150afb02f3bf8a9e7edd7130b4a62e9e778ffe4e9974b1cf5cbc562695f353027477bef2b98537fe33ee783e11f0eccd940ff32419b8fbe8fe7fec5c67b1f9587dcbe971db4dfde4603e76bcf270ada54f0067c9960cd69eeb4abd52c579425ede258bb38d62e8eb58b631c6b17c75c1c3332d359348c7f17302e9a6ce18a6d00ca288cd194d18d6bb2d060cea94d6dd643f6e8a17953aea1693dfc9b2013baa78734795f396f7605fb89c350c07b7c13ecd8f53138c6ff658c9bf3a20f35c6e8e798264c2ae8f9cb53430d38703ccf66cfdc0d4f9a617e369bcdb6ad34da18a8b657cfd66a6d206f03792330ece5d3886f9de252ba09d7bf91f413b197741324987a4818d8900b6040032c532a75c9927a48538d8419d239980e81d5004b804bd3488ee6aa860a27c45aadd3adadd699d010b899678ca5bcb8675afbb57e53fa4cf3b9e8c0b287b7bf1b660d13895aad9da174d5755dd76a750e6460f7ce816a6534912c79691ed8ad195d8189bb89d40b266559135a2a9f222b554b65237beac669df01fe0007543d5a35fba902a98fc044e0d0f6105d9715d184b1a5ea3a15f75dabd59ab1999921f96db53c2fb451c251637313c4689b9b3948376b5ead6fc009c3d22bfe126c0f4bb84c831081dc9e2bcfea8bec73e3a107403058917d4413338f1e48600296916f5d70fdf3e1d90d0d7a3ac56efe36ee15146be07a6afd826cdbb681402216bb12fa40228fa5b3dc4a0d7d54d3346ddbb66d037d9625b4c20256cbad84be0ee46d9bd771356439c161ccb0d878c4939e9f202d6b2923d1ed92d87abb24768eba2476e4a387fe4305c463c58685ba4ad7c934130e5552878763b8ea412a18561c09c089ed7fd1a18ddcee1b49c10a366c94a6aebed8bc67bab9f4a582363f9a1246aca3c1081721d896151b562a51d8f852c31246f6c460ad82e5400a969955b0a947bb8ab83d706db3cd25af381a81110557e708a5a9abe09a15a18511d63ec2465bedd980c49088f580cd601b0c06b37198a60283e1a8117501d3509a864ab57886b08e5829d1d15028ed09b11a56a56106564a6dc60cd804d130c47ad81c48c52e20d4a4848dda3698f7844c715c5754adda0c2ce781225c6f2de8a80247e17ec181a4d62899ca63e9152b2af460b3fe8b1ce143e4ec2ae75447b35c34daf6038b6528160ac51282041344505c2c168bc5623729ce2d9526a289182580c39134351305d0a1936f7eaac05f7d05418d3a23e76c1a680bd8cb07f0e33e5ec563b399599624ab540a435913aad4101c1e33b8420aa8d38f298428a7232f3a740fae33abd5dad1c86b50bba669db65e7256150723d8c22bae8557cbabb3f3d42830e9bf564fb555f355eff9476b7bf49e7eb8ad7742758b00e7b85a26f996911bd7cc5341bd3d02b7a57182d87c2f22e5e1c9fba24f67b1dee45bcfe1436318df73c3b5e8d5c77306c42dff22ca496b724d13749868ccf01fd8c0fbd88b4f220d207e67c2ffa1cd0b71cb111bd8cf7d102ce781f22d0876d7087d0b7fc4de85bc09c2336325ef4392c3f03bc59791198635f460b89e54524fb73b290f83449dd8b3caea8bd0fa3db4998d10d1de83f1c159702865771186fc2add611fd0ae23e6da45ff7670ac3f8bbcecfebe7355ffef297bf5e2f4a619dbb38d15ad358e3e6315f5b2101d1b70438b4461a898063fc7de0186718ff2a5be0c1857dd19e6f09da6df01d2e235fbb3c62d7885d231d34287169db569dd8b68d37d6d1d9b0e083c372c79126eebad23182c5e101019a3044804d38339d23878930298e157c28ce26936dda56ab27f3dcc7758a389594942e9b996bb52ad5ab54b2566bb5eb54ba4a9afce42ad548baaa8e8690a1320abfbeccff022e13c40557ecd4a80512d515bb894de4281ca5e7f3930d1031ab0047b9f3c9ede7516b10ef4917061e848007490bd5e3aeb7bbcc640b516eb77f9352cd063318e18b91e3d8fac6711cc78dd2b46975d3ea07e27134053b63c269391648b6eeebcc04b94edb368eab756686eb487cbbd5753536375cf0388ee338862c47ecfc9186a156ad473dd28bd78139317401060f4e393fbe24a723f47ff40f923875dfbdf47b1ffa482ffd20b05bd50097ca94202b021359ebf3beeb041d232684cc9692d075d09282f3465b5f0c2bc060b7baeb5958b1a90ed977a3608f1d845220429060626cb55aad568be588bd92e79c94523ac1be9225537a75b80f0971ccd1684ed115ec775166bb5361a43f2158ff2aa511b3d9b8da09a2d2318262aac39fd020866d596c8918aa7e384cb1f28a55d37a36c36d7626ae5b212e45d899f67db9569487de1c116c8361d858adf2ab42c7cc35a3b3d96c369b699a3785d6fec31a0fd8a8e1ea2971bfe64282b1251afe5326d8fffbb55afbbf6d4e34959479aa092925ca9c5276777f1886f839e5fc98a1c4ec14fa1f9c173af6a52c0c36f528933e4c334720a314639453aeff94b210cf9c734e1114566c253bdf0e7d0abd46aa170c825ed10442dceebdf73ee47954649f293b1db8a78f4347677f3754903ddc260f13366945c85432954c25234919c704e118bf230f1ddcb1752453c9502e336532994ccaa48c521baa101566e6f97acd58c591a639271039ba48008462a76d28dc0588331ab94cd7754d0242ad10002c7c0d52781d417bdaae57932d3a47659acd077c75bb374fdfa82feff55ce7813ccff32a28b4d1d203d555c0652db450e57a2b9452daff9765e877f7d73c48e2ce27acbf46a2b4524d2b694a24a574d229c7719423cb15e5a85d51b21cb1e28f9e102bf68b254b96a2ab821b720c33b934338a5b02bfe24b77d0093b8ee3467f0325d0af51272f0d3e3703db301211ecf793d2eec8d6db4637ca1a1de0824eeeb4bb9bfea6c3f348d20abbc35d94d00ed4d11be86803450f78a0f73c1f40d77b1d1e690711ebbdc82e1bc01d38a858fade83409482a8b7c47a339ebd1e0741c3191b58fa6209071c331dd75f6d14b62389f5fa50e36a4fa5699e4a330c050c0683c16a361b25263caec1cd5c7b7e9cf8548945a9d5ebea0652a954dae488a86cf7ebadf5946cd2d4e329b1f28acf63b52bfef5ba4f88edeaa655971efbc2bd1097a3a105db2abe53f5e2513529b26a7d31b0948617581f47fa7d74efbd8f9707da8040ae46ecd82caa28d2b19b580f7f504f471949f97e19f1d281deca75001af66262d943ee786c93b4ef6feb1cfcc8a5dfa3722c2d4a6538ac97794a29a5da87615d00b022c452f0653e0b71bbd520cf65b48edb36baa2a0495ff30f87bebda2a4ed7a3f60130e2887f7aec97432dcff0c21081d8676fbab8f53a7a0a26f386b27d55af56a4541d106e0a35960c20bb60409cfc85a511e826046ca04a081384cacd8abd65e003f92c1b2700e467abb4e8ef19c458939e7a4829d1e7ee1c4b6cdd9715b2761484ab05c00f017b06d9536cbb65adb9b42a552ad60fec4c8ab88ab87a544a51a8d46230e65a92459a552188e46fc725273c3329834f95879d0b6f9b0ef3912b7c2bef73cac3c2805969910197299896aa70897993c3185eb29573292a61a2961ae699ad63f05abc4271a66e02a8e718d44a43ead907a95d349f002aa75fa315b4a749195ab52aaed4557a9422f690a7244abbff2dbb63dfd2ca001b9f31bec41fbf99ed7a058128d443fdfba4cd7812f3bd8173d0f2bcf028a56de87e86d079244dc8f6ef7deacd665e66b2ba4976949deaf3c0fa067017dd807fd0ae8c3c7ca7bcf27fe74a0afad9034ed7b04a3db81228fcb813e8c6ec3be9df4388ccb4d6e727b52c3c3fb95d0adedf3335bad34a67195f3381157f5ca55bd72550d35e0c06163a55ab9cb086aa55aa95e2a954ae594da50712e0315b8d979641e7329fef21ead824fac8fcbd09722f4a28c22633b5ab7c33457700ea984964e4bc75b5d41d933f668d1df0183892540d6006d8709f4df7b9236f5f03a4792040ad71fe481a27331db62e65968ee45928a846936ff828b2d3c0b6fe9788b894ec78a2988709cb7bcd571a18d128e9f6f8ac7de0aa594524a295da976f480d087422f0ac0d42d8771d7102e9f28a5945a92e85948f657487d0a919eb8fe4e5c06f837164d84437f569f4224543d8942274bb2608e039c70c13aadbc10276f85c4e30709b342e2615f447a11813f802ffd166c70073f853e04f62861502e43b94c119c63fa4801430ade5728740ea6f95e7419cf2079ac7c917d388ee338707379a8846392f40d241cd3e2987602dff07f3510f8868bdd9a7139c61b089b7634bfa783d7921a74d139ba7796b72f826edf986003692101a2eba8648eb87456b2110100000001131500001808060583e190583c2e9c27d51e14000c61804084643c1a09a4410cc350c820638c610600620000000c0c91a8005bc4b75540a5a7c72b1deac50145f8dc4928dd364c074088a269ee6dfe486a1253dfae4a91eb83683c7be2abe30ad51f973c7c268c732e8ff712c14d2b6159b51439e66fbb7210af987b982a00826bcbf7e28eb27efa429df022e047c4271939921063dd42feddf0f2719645a76fc4a962a073a7af70baa8efb54d55a27d93719c641f8c33029f90d5e58d35719c62d7755351f582d2e269d3d628a6a751ed31a34f8240329c070e9648c5802a045db863d9cf40445b0c278df7d0fdf26edd62eb90caf0a8a0a1d69d84882dfb62ffad8faa54d3307de15dd3a4cade71fc11a4c9fe03c21e02fac399b2997cc8a7581f30db8a441934184df0eba87bfd4c734b0a3500e2cabe6d7e2587073cea4c712c021a900e439abe8a36df876321bd5e1dd189f53fdb99d07b13dd5be66c12e3107a6e1fb13d2e9aaaa80a707b64848797408927c17d4278565c732853861a9b5652083c50acb269abb8d77f5325231858e3bc3604e0cc020993c41205628091b626e07eb718fa43c37cb4e2d484f935d82269a29d0c2698a6213032ec325c24380ceeee5160a8f8307a02a297966fc2f63df573a80452eec54da143bfcdeeaee51ec6f06c1c04c9b27c675ce3ce96b1300961209fab8aae6c6968d96a2ef24ed067633fb5349caf52c366b82f5361003c7d5dc4f209b46aec54ceae2bc361ea71d77f6779559c9b30548c205520b84d0c6c00b953d273ccface5a88ff49f5320dc4cf82f7e966b2d97d82ea59e90de3eb2284f69e12fd4aa8b8f3b63a290a242f139a2bb71e7fdaa2dde223820750fbce770f88ce7ffa4963bbdccd1923811cb1a3310872841489428b57cd37e8bca7095840ec583dc08bcfe2a9259390a06bf59c1972f6db1ef75c123888d24db519f82c9223ec9311750ca9039825b68a01cd4888bd54a844d70eb0c32e7366dc4dc32fa794fe4ffcb78259a2e16d2c1397a38ba90c9bf56a2f4060c24048bfb2304d1612f8b233594f77fc494f62a29c14623721828a1e4b5264090272e3efb2145450631bba11192cc5da390cab66c8041590d50a9bb19390378271977000321aa13ec11b9c6b5ef849f56990610c2891094cc57ed207da0b58ff2630cd3030a2343b72fbaf7863d5c02ef0fcce269c574fb398d5f37922212a71037e680942eaeabafaca5f8d22e00672ccf690b3efb177ede1c8e0241ea5f6cce84181921802f9e88ac7db43133771e6ffe87c5078b9880fab08ef7f586d6c305f763c9753bdc537bc200d22a1b3552a06463d1c1ecbc7b1418fef7470020eca9b44e47dd34f56179f4d3979cfc99ae87c3d50a586b4e45b9f38162737f84268c7f031aeef6deac37571ab87ffec269b6622c4b8ebe1bd9bcca9ecfc587f01d74ae4205fa715b832ff7a18ccd52caa79ffe0bf8ba31fa94da79c386099d32273c4b492d095d0dfad08c5dda8ec265996925e12f3dc56165fde30179e24c75ebb34ad4d869ea5e7d04d122e6278686dd34d01192f098e5f11178f25da24ebc534943b0c54da911116a0225c41a7bc2b1ddc80969369c512d042a0983f92fc084a8ba29eeb386d4fb08afa396330a5009b14ab80775aea993744276dcfe0824b6c90a3c105ed2385304f95525c3c7316f5e508b3e4d48265a737cc2b2427e2d809572cf6190871215ddb9b526e68599f27ca8c7e9acf43f30f9180aee798c64a33295cd17b24a643b62ea6ae59d78f71369b67de7a77c0fe88eeba008439a19e32ef7b88b2095c2187ed8a5bba01de81222a89bf6167a78184d1e358988615b41bf88485c56ab78871c2f25a76443eb9efef0bcbaf66041011f07b8b71797d5d82c0d326c4edb90a633f4dae21cfe522bce3b749ae2a19ea31fe08ea8cce9ca9d4c17157ee00b22931e61f68d277b4ef9a623f4cd7ec43c2958fa53a43b50fd4a3fc47bfe7a05a39e5be281b6f58110248ac2606f7c1e7848cb2b5a0e3ed9875d048c9e8e69af173ea71ff6910b7d4dcf9fadbf1661c5db3115cb9cb1aed85079706255abdc40c69d0d2810a2a1ab821f4dc5d2a141f3c4a74ffb7dc1fa3337a8e53db2777235f1bd902d7165f482f30ef08313bc4d8e827f8abf1df92fb57482ad4f85ccb67ff6254990853988f8e34c1ff7c7c2cc1a8553ceb6f714dced694df8c12b789fc51c671a3d56e358cc4ed05c3c08be80b4322d7fbb964c450acd41f24390420649b61876ca9e84b4dae0b44ec88662bcbd6627f1f9cb43fb4406e7a4231e6686635002426c5c5cbdc10157e2e0d03896c2b2a85d91c7ea3a2388270c6de417c82f0296b6ee7ad0a33c5175fe4bcf5c0488d85c42924c9f66caaa4b7cb5c3c486ce6c1e4f736f27c42c10810ea7e52b86ca09826317c1f4e791ffa7f970d753bde5b1d8890e2110aae3e960ae47bb62b05c7dce68304d4478e444d0b0021a16dc5c09e560d108b55f9a83fe4ca4263c25bfd1ba7a8cac88314099149e8b8a3302525ad21e6e171c4857023c0d22425e956613687fb84caf0e1085f04a5dcebd05aa14149bc9000f40ae7d0a979eac196b3ea77cc1e900472d196d9dc10322a43a4b63824b1a73cfcef9544ad41a9f541500cdd601ccdcb55239616fe2cf20805af10064c9a2c103eab785fb5afcaa0288287402fb8881a11ce68d4acd689fc170f0dd4df4605deb214bf82b78db0c5dab8a95077a0c43e074b61820de2d5b9a3d319331124705fb27d4dcb192e0403cc241667f512595d84bd36f1fffa60f0455f9ac022e3402552c38da214794ab8f1f925952569403a2769c7dcc4290de6d76a71702b2d5b8c26662793abadf79337f21efd7293cb1d57f27c43e1643278f7014493a6eadebec9eadca9c8c8337e69fa88b81601253b10fad222b24f717d93155181b51cca11ba3a945fb686b5e81fd95feb5aae1d18036c9d58c781f997df3afd81d4c32b8fd02498cbaaf172d0ee81b76bd4d4e8d6f2edfdabf747de81b5f0a0ee42841b7f50dd2e6ca5f682e69211372f3bd5ed3101ad0c3415cf89f4fa0b43f7049528721df18c30e6bee31e41b7dbd12ab574ed3128f7bf85a1e0603cc1611690fa946e93cd34c0464626e8f1875a506b2264317cb3a1d54fbadbd5b0b2117bb166dacf872c6f910da40765a58474ec680cd3a095990db68c06cce2be4490b31c7c2e5c63a1a61a95f9a026ffed3669fd2ff1cd5b14cd2c97b97c05d12208576bbb3aaab596342dd5653ba3892f73943305d99ed76676ab49765defd56c6444a20d36059d73a3467917a7462baaf8c7f5864bcc84a436553d58061691eb013920b0ff338f4af469f67cd8fc77dc77b15a3cc7abf038185113104ba82059b8b47076ee02b7c3a8a02af80e0917107c765d0a9a602cb280d28b8a1f1e37e678b1d6d1dffd1347b2433322eb500ac5c669a3f595cefd6e21c7fb2cec274ae8e60854636543912d940011857b03cb3c9e40071656d41a7ffa2b2c27ba5e45e73e406acb1722fd0611a45d56117ab5793c0b41d7b8e0cf58a768ba160db3ae9670ad342aac065a4e48b12a8648c3b2b0c52498e3aeff2091cb30f630f9f360f533113f92ddc8d77f4c7184a3950b9960661865dc046af7fd502e451a2c1ef5d02ee69f95a2810789004dd525e386936d41afd48aefc038aad78adb8fd03b3b28580a296c543deb10f79af3a1cb06670df1cd083434d9e1baaa7105aa1b1d3c737064b21ba962fbdea89fbb4af8d4d1ca8045700b6411cda7b616d1b7a5269eea20001de27a1d306c87eeef553e34df3cd071d078ea81ef12f9b34ada1905a9bef9e589e60554abb265bb1315d20c238760207929536f6e2ba2769cba4c786d75f9f3475e1b14ea60bb0ff131d7599c205b8dbfd92acb7325ccc4b069d0563f30ab50c54812c66f0a7b0eb7555c3105677d10c2d6fa795b06c9a2b6e73c23be86dbb3ba01fa702162eaeb905cba71a18041a138899b7efff68b82245aa071c00a3f2af2cf5a4f01fafb2a3b6ae0202394cf177e37d1c739a6998d2d3ec9e60706782ff3ff22d4de1f2133e0b22c6c04eb6085217a07002426f234f7207ca3bb5afcb0cfd66c61c8770aa7ccc0608c53c43e753cb300cbe0d3a1c2319b96fb9c510afea4040e9285c320bab877d9d0af1580c3498e6a16fb58566dbe542cd0fa7d945d8194518520194d2f26278ba18b76b31a4a74088c9c3c35060c85d9007a4707b6ee0bed053c580d2efcf5911213e2300b0b510058f2d040c9ddc2ce13f8e26063b40f862245ad3920a320fa5012ae625435b878fe65e367265e5e35bee89b3f612ba3fa1465a463c141c99e6da0250c96a9c2ff6ea8871bcdb4ae49448a0f033d9ed696bf24981cf9efe568edfda34912c0e3ffa3df8fffda3b699910c4d5c183d98d05bd8129e1f3d1e0fe7fc0d5c813375dcd1b51e0f4f8ef2ce5234a35051cc4c0c93ee922e7645c121ac28e2c7728129633860f383211aeb1c8311f3b9633dc0e506ec87ab243dc6acc4f05ed9f2da9423f54791deb92ee12e698f3857b4b0f7afb5e33307655cee87cf1c5757db9c25cce8c40aa0a45331790844606dc4d40a696465c4bf79c7d10dcab3f7b8425cf62826480cf5e108686b6f85d1f626e12a8107026d1d48d56b2407b22b34d4807efe83e83782bc1468dd24ca44f1aaa2856140929b19a24885d06161de4802df2f01c2f932fe726068cbf3dad0baf78b193bd59c0923acc2590b53ae4f915fc0a22d3657d085139dd3d52c45feb687c9a03392cf530bf92095889bc942763396398baf7a754b0e2539a16db4cc63adcf983900d2f5cf077192ea85ce7c3f94b202f32648055b102a69b500501ba4cf025ba411714cfe7fda756d812543b31f7920efd20968bc0911faa7c23a24ad8ee628eeee220337093e246d31362a87d746bdadba15ea2625f769d93753775056fa3cbdd5bc36b1d4198e8984b67a31d8727720d3f1351916ec303c58efe0a0e0bf235296b3c8fda15d69f8270263d26de3ce55556cb61a3c2829c4241695c6874c0e6700334c567df1a25fd379579364e3d563ce9375ea227f13f027e6332152580f80a6dcdce6bc34e17331ad311e9d76f43a1dd64e1bf8402b6066e5f9bc2ff61cc4e27e1469e01f9c3cba70112ee9a4fbaf73a47445e9913963d3c6846c16d3307af07dfb56d9d416324d3e933bc4644b67a61d4a1e4e9dc3d66a002207672bc3b66b8cdcb0e09af3d3b97ca754019f934b61541cbe73d7c109f3f9535cb97a5f75d5c086fc2b4ea0cbcea4bd2284b609d4a5718201e069000ddcec080ca97f8f7fee1dbd005cc8527250d8a4bc1179ac1d74aac425093c162aff0f22605059b7994783f3232f4dd96c5bb1fb3fd522d2da87bc2a28d94e5ae2ddb2a6c6004f2a51dd7c0c3e79640de48147ef7a0f70c3272916d86e3939c8d83e88328bd11127d56b487089a977093b4e2b676cad27f44dc3f4d28139a4d6d81f5a91342974a1ae1b8634396d64c634708e28e48d39df050ec23d228cdb2d45ba753eae3f81b3282838d9c1ebea26a5d07deaf56f2fdef6d014b4d7621d7c2797c1532378df8d1815ef922536e52b1305979a9a64bd3544a486f45c39afe07a8d4201407dc16d2407cc31b247babe960952c56986ee75c5c1ba142facd46615372e9e88a360f0165fbe01e8ea572300c86cbf3e8fd1499d999163cd8f620e21433a520b80fd80e1557214b7a644f09f897091a362adaba63c5071896bebd27e1c9268f425239540699e6807a2c23136c082aafc4c29502628f4bfdf396e852a62522538c0780d812d3fcff8991528c5ecc10800607df981879205463d8e9e2bcd776c3db3ea0bbfae2b431f324fa3d1636b51e554f219273ece699f8f8088bd9757390e4dcf481fd97414486aecc9ced42c6db6c2d68303366e8d51194e7c443f5a8d62783c4b7b21db259b9b52b777db591cca8e2bab424ef7abea063244517d78ac424b1466e8cabacdae2d665388a6f811e69abd342e1e9c55516ffb7cc5924670db1c26c9932a7316a88a4b515fd8e2ffbc378d2465811f892c80d743e5c6206b1c0c5b6cc23f0bc020d695d64582781fec69c308f352e89d3fc132cf19f0951766d7875d656172dfded480a15532c3c5c741836c39f99ab258fc55774a35042a3dc04b5c1d89fda1f0da289f1296ed453795248dd9542bb27281048bcd6aa8f192f41bb39c9ed3fcfcb65a3c28b4f09ea79940efb07de19203573248c986c7746c95a1574a9eec60078c0f6dbe5a4b7611390671d38d5f3181facbe02a09e7a3f2bf1c276aa60be6d1fba3124c272ca8c67701db7168dfe3f3f5f7faca407b43cfdca7d442b390eb595404e51726c8733da3fe70154f7a671bfa23d03c286b8aba37efb31977c4d3a4d6405463e2b504a896cfd76ba0f754f3e801bfe5a18633422fd50ba93c24302eda7f820584210c54ee037d37db88b24cc733511d1711e5530b02101f3fe70cc297d1f2977103e3cc33f7f50c7b830bfd2e485d5645ef17fa1e98d5dd430a7c3fcab0f546238c6c6003c2c48af161ceab17973e96f58e256782d8deee55172021c039455d71af78f6f4a9b93e73d8e665b94e993710bb710854c20aebdd021e6964d1909a875df30fc3755fa9b61e29443157998c09c1fa8546cf1202d8c01520d3e9a63a1c538de76203d87ad2f673fe4d0ad34d90d04fe7d4ccb14759e339d1e886cbe9c59c05f73c478dc5d6d05d822fd26e9b137b42a97756507811d77b89daebf9b18e7c6bcf0e0f08e1c4152bbe7860af7515db364fc86436f315ec87215318ad64aa9751d0ed9286bb5a9f1ba25b42cfdc8d62a58655d82f200e27269f6a43c8eed9a7483e24ab1c62b45ca509e9cb0b0f84819cd8c7556ed2b8ec83faaac3949d56c3a2a197c9f53970f2c6c37af0272c8839b2017316b4d38ace2151e9f3f47893dee1c6d61c31d24449c1bbe9a5d667f0f70ac9159b9171bf2e221524b470c24ea40bc16341e0fc172b6d3172488ff911e9b5875a0b2626029b8419d76aca1031f60e323054b5eb0e0c350642731f88030ad1faf64a6a4076f1f40e9ced772881da70437f16168fa5c3fe4c36cb3245a04e56b0d195c6b8760308052cf8931f62fe34ff28210bb876abad9210731bcb1e40f6e126a82ef504461f14788de7870662695b53b2b0f75b58e7c20f6ffdc1fe66e78fcbf6390f5c76d10b89b7fff00d43cc1e0ca9167c82522cb7a5e1c3b0b052235d5e96fc6c87f58310fd0873bd2e551b8a5e198cb2a27600f5057b94a474fda69a6dd8f10837b6ba578f561e94f135aa25d0b508a58bf7afd30cfdbdfa5544192f6c7adc0055db1bbc95499e227fd40b1ce63c443fa438ea2b72d7ba454c6f6120de5f48a79049f4a4426c5253981a4e0d382a4a965d3d0fb769ef5a5d6d3d6bdd28577a3499ae518c3f3dcfafb4ae3b2011dfda41d042bdc10b643f1b231d11659e6d8cc8ac395bcd948932e81da32772c416ad932f0db8c4ee1aec87cb988c880a31167c56777e7851648a451d7690089a01ae253995c2511792f4adf9ed67b40e60bc179c7b8f2bd51afc0c80f7631628cd6a126fa6c5d1c66d568db8d32f7f6e39da066f5c62b9c49389d1c9ce6a70c6a41293dafeb7d726cf24bf0d9240b0231d628690802d50238fb5d01a1958150c601a9821fcce1c9aa2593fc3f5ec53ec007c1cf1c7bf2fee13bf71a34e04a4ded34dee000cdcc9f7b51166e4a9a640c97ebd2d4b287525fc226a5e1959851825639227f7948b6e1cf3cdbea9398373f86c8cae16429426c462331cbbef3a338a57375479af28b6078e1aea62909b4dc30fc2bbc50843dec6f7f28942283bce03c0ea1e7992ee34982e713d0c6b11ffd5e5ca23296f27e97ad7714c8fa414dd3bae9c205fab2d077e6349da1e40f620ac725f145565b231df862fc9405c58b2d507a2b699935287b860ec7da0150b3bb93092925c232729ccc1251a0737b6a272b82a21a65cdee1f15974490e8eb826a6a9b57f0940895824659a0fd44fe73be6101b7ca86aa235dd3984a65b72cdb6d801499f1f4e14fbeb22827f4eaaa5327e2af6d472faf5ebd9c9d672d221c251d89fe619951cded6cec9788680e5ea9b617f5e667670adc80ebc230f4bb9b1aa1e26a73fa787c3699b244dffc88053cae09c51622d0954d96ca725a7f1f196d7e7c5af4da93afb134c6336dc4d471fe287701d580986d5e2f9e7ab6413380dba9aaa6b469e4f86bc82affec94353ef1344244873851ef8ff96aa537a6cda05c3f9dc0faadd387c0dfe1c07fc775a0f32dde5c4bdbec0c6e454a172de29f596ca138aa27f77549b24a193707289be392fb54524394f158abfccbf0920ae379963689ef1a3819932ece9c585de74d697bf0b5757cce238c27f5aa7c015500fc27b751339eac4655158f8029a87ee2dd524fb7cf48371d437d7507a664e835ea064c0183bf90ed0d504f11952aeb149d4405f6bb08594b491c4b891adc52251c800a6631b2f458bd3e2fffb06ad5b7ab5739deeaf65f9ca59d8fe8a1d5ce5c62a378036d148ccea23351231951cbb37e52f153997274782f33a1bff0120698706e508348484cd6ade31e378ee644040cf4b1e94a062cd992696a782938ac494395e597356e521cdee2aefe7b2915e2168411d38dd3ac38c72f4aec8651b5faa50c5d0ea5417b75fc5a042a38c5b3306c7ca817f50fadf12602d0cc1b904e747fe73ee15ca4f31d0ab20911ee0fbd66c972844050aadb1ed586fb23b6beeaeb80353f31dbe00c9f654189568ab15beb542b692d4c60d17a8c01b1a4df9440e41abcd118d3c27b627b78192eb976effc09f3f911fd4d55c4e0296fdd250d4dde80ded3991d61704414efc5de9a403852b6c4c42c6f015c8e75a6fdf09bc45070e70d189d42eda5b20aa431a3dd036e894e60523c20091c0696aa367fde0fe6e8011381c49b84a0a050c3c200f997b00248f1ee8dbb7c26d7c0a0928128f3aa103d40d548bb5bddd4b873a1a799e942d4e4d4fe9080804574149f75ec757915984aa798b75522da221fff0e37269123ced35d12b78ca6782db74e630458df7f4942fa7b910ebacb38ab4f45b572cb43322d9305d409c737b6929d0cd6fc84469134c3f432dc1f420e092a2b1e613a948f502a5165f781d7cb447d97c82a9ef0a94e57d827e051ead65655265a167b8eb4ba198fa2372913af11f12b88db454dee48893c4be33b40347ad01a57e2b2345dd483e1871a34e6c57d36af4b4dd15651891bfb71450902334c1fcf33e97c749e07cde87cd8adfe233f02b5ca7cf9746660fce1b5c0f09653979fec34b3bdbb86e56df7663cceb82c65b75bc31233df99d32ab520f4ac9154a587ea42f7fadc824479061fc095476e384c73722962c88c394968af26b1189abf3724bc4bfa296f4245741f68ecb71ec5325171fcaf44cb1fff56ba063234ad465741a2a11e6e5ea1818898bdb7d37517f6f16da61623641a4e26bd1d5588ab6a9c6129d5ca3a4259b943d295427dfa0b9baf551f6a42618c906118511675b49378daedf12abd77de1975a091e6a8ce98a170a3205b749710175d879859b457ec988bd89f2100eed0a9c02e95a0ce7bea68d5a18dc223b08d0fcbdc4eea3438e9cb310f72464992be3f5fd45ec6b3cbcc3a282cb0455f91d6e350169ea9045aae6e0b8c5dc190511845507f105caecd0092e6430c512dcdad69d622a6af8373511dc027a65af7e8f3d3f52f6ed97416f8717938837aad82dc000b797f739bdbfefc1e0c740a49b2e53df5339af86aba1bf116d34094ee24db37a0cb16eb06f6edbd451c7a5122d1688702a56c18352d5ea017838d9e8df9f33d8e51e9c80e7cd84253991de7f31b5990e44c210199b5066ec89b61d12af912333a253f76615b7eb899b29c8df7b591740f7c9162b20038a6342796fe99a66ec397519e602f352dad809548bb3a70403101c069c3cce76b40a202af446ef9145de16e0d33c646e9e36258f9a4ffdde85c3155603e59c6a07296a3b93e0003e552f96a6734f6e42742cf55cc1d425ca2de539ec2c4c9c7e123a77e40791534e0697e3d4482716dbb5963a051ee7e6e381d443ef62e132092387350e3cff79c22e028cd4dbd70b150cceaa2cf7449caa20c679576d15135eb22bed8a36f893c6c1848d96b89fada8850c29640b639fe6368975bfcf863c5d27b59422b181ab0ac18e04c57a1bdb2a98a9653d16f6e651853137c7a1b546088cf06530e1f0e17c0fa75bcb8f2f4ab6c29fc3dd81e5dea0a4492573e6c0a29f8c090ee7cb10eee41c28c7f79dfac954d95b8e7ba9302d7fa57f2698b0a6c496c77b483767e423778200c2fbb5615ea53b751e20daaf9964bd37bc787f61374cc20dd36482840401de24097d9610e2cebc2273eb99c866134a25ad804b24152d751a8596914c2c01c5dd76bd388121d2171205614445f8166625ff79ed11b425bb4f50de399f3fbc3011c78da96439c0a85db5ead0dbc568fad5763c127c3be6b2a8c2f0ca463acb8969fe8480f289acac064b8ce5c024abe6abe476391f258a051486d35ca14ed4988a94ad0285dfb60772d62a2611a811fd30002cc86a296752d1dbb7bb4c6a88cdd9a5c90e99d02d6cbfc97c64dbcba749028e38fa7fc33030f7f6e21ae7e680600cf4b2a765fcc4b740ab6229776e1561eaf196adb281bedc3f8d10efd1df336468af4f04438d94987b88993cbdf7f143143dc6636a7efdcc838e65213a1dd11adaaa4fe2810ea161a1d2cc154c3c5c28d8898a7b8b70344edcb82ba762bc9c8b35cde31d785c59a2122860b117b3078339018d3c176b6f07c8a5b2b41fbda86c63a48f74cafc3d0d99819debd46a724e17656abfb21d20355cd0572a5f85612ec6f2b58486c5ba80811a90b9c69ccc90cd754844a53e49a3f507ccd51bd2d8952de78ca474df3543913422312024012c670e880450cb97a18033a464d5356d57d2f51f2bfcda01e5368d8db7c89916c773d4341f75c51a447d420089b5e6d23bd5eff3f439e7471418c0d8b68deedaded2d4ba2dda0d0c995856196d7766ac0a1473f5cfd9ef411ae7ddffe3fb2e76633c5d17d9eef36a420a236e6ab7159dc573b5156b91d4ac6687978703fe02861ab9b808f7aac5c5bbdb137e334e4a046f370c4ff51088ace0bac5c232a250bdd29ac26e8d7db9fa65f93a12203996726986cd1c0a1458aa42b16e43ac1321a4a31a3964e5e0486aace2bdf936f2bda43402b080b083eb1200b0894a64820c37b312138815e59abd319d121456276f22973290f406c61c489fa920d75dc084cfd5c1aaf7660f238f0329f8132369431b5e48eb67c612a3e336cec069afd52994048045ac69c7065c410d6278171e99e096a04e2bd23213532b970656c5a06b75906b73b52f51c277d5d63e70c7a2e8ecaf58c89ddaeaad68e40855ddf76e04d3f1edbc88e4093d84b3cd0fe67e88b983fe444bbcd88ccef64c2930e1b7aaa5be2cc90122816d0cbf48f0fae630ff71d621fda69d62042e006ad96a3bbd0588a19e65d1c8712ac8b587909209bbf5f009fbcd2e0935628a4e999635aa1deac96b4abf7272905cca58102d8aa9a8f80943d735ac52faf6b816aa35e868306b9d1fa2b56249fd644a23f49c73f10cd6f089bffaa859ebd3a1694b8b21cb5b0ae9f9adc17c23db222d00e04afeb2b980db309fa90700bea810a590d23f809b03eff7d30962bda5fc942057595f79e2879df7eba0df55ca77453e84a7115b1dc9427e9c6dcf20c99dec66e6cc34962dfb81a60498a40b84aa7b3cc04b973cd63b323ed1a16121d826c572db5bf4e7cd8a8dc5748741eda69685280d8c3c416cc87a983181a29a292b201e32f40ac3aa6d035c82543e2955ba01d0f1cd355b3035a18b72bcaa9a3ef6544de4a3729542ab11aa9590ff1d2f1895159cd3d46c008c608b0c940abc6f6492ae342c7e8c19072e896e93516d749b646de09cc355385753472a71310e4e5627d4bcd9455855c3ce353cc0efeefc5f318af3f731d0d26f7dfa9f308f0fa942c003c2fa7217dde59b746926d44e33031d1ecff9574d0b9e7f2e13537b0d167e903fd5dbe050e1da40618e7382a709666bff057b39b5373a96c1d6aff5a10546234cf6eb08289cbe8606cf75eb41eeca09184bbbbe27c334ee08f0401505812346546cb03b08fb6affb5378624228be5211f1904987d4c6376b8191e58634fe8294ae97ac9091dbc67018bbe695492668c02dfc01819d98a25e3b26be8c11891250211e19d80764f69c64d241c49a7f5b5700efb0e0218dbeae2ab0d41c6959ec1f7eddafb5c19221e0e57944a892b6580b8de6eda32c49fae0e944e8d7adf8bd841afa058aa9c69b1a44988ca0aa1f2e2a4804334c581c96274917e99f3098ec0cc8ec29ab4709aac9ea68a7e9a5cace3f1725010ff310979cb3cbfd06e3de92119c9f3831fae082e3879909c50c02c71dfb2e83233e1b6f43c0cadb38f62cee202963846ceaf67fb202cb1e9674b19abb2ee12a57e757bd8707f150350b4a52988de0ff06ecbd83561cac68454511ee69cad9b752aaa851cd63787db35e6ea54122436a4c3564c2058640dafdb67a0202c522dc68d26233e1f0702e8150cf955c7fc1557cc1e90fe3bc52a5750d85ca24f9f83a0576994a75a300aec370eb29877b107c8e656df2e7b990534b0c1292b3c4f5037f08d9a7e265fabb8c0296534b59b1b364f8e6203173d67ac9805d1011661750192f3907cd633db59a5673125d4f7c323590187c9b0e0f043a307e83fb9b22722334912263c16177a9bb3dcdedfed2bcdf53c5d4c32c8522d75f76d74208fac5f80259d1300bb2730c86b93a029db926ea0f0ce83f45347923ba25ff2727ae66971c9c2941ad5dc4025428db6afc1e7b72d7befcc6db16976584a2c66ae1ad676f6245334bf4712074155800dc048cd340c0dfdce0585128d1d53e0fae1d68229c6d70542413b1f4e5a4b9815be942c537ebf901d98158dc970a00ef63e77de541172f7da53678bda02c0aecf2c121a40bb9a91bde2e84442177e397264971689c0240adbdeb4e83aa18eb0b9cf6b3986c8a39865e6949231710c6e720cd05a5d7dcba28e2ed5f934069ee0b70bbdde8c7fce7d2ea26825bd40b5b3a04d3b62c8708898637e613a22e4e351377c95e0851ea25d85b9276850852e80b2c69ace512cd2463493a0eb80b91f2532cbfd3f910c1b2357a468dba2e7dac6414ce295f36b5bce993ea757d450f78950c1040ae921e1838a881fa99ddb27b8de1647364cb323cc77febac0c4d9d400313027c21d205b02820e85a0942e9e4047d65b9597dc26e14b95e3ea1586740e113e218772e28c21cb8042bb68fad17caa1536f8c7f54d816a02ece6e7b0bd09d1c487f32e55d7c200e0d036b473f45ace576a1de4007b639f3fe2d7e8d09e63e6c04a0bb682cb80ca0918b71ff16ae4e94bd4b2aa600975ba444477c55798d321a177506d5861c9617458a73d153b67857c541f84aafe698cced7e09c6600205f03ee14646461c63527d70843c690844eb7178b591122f849cf1a5fec5bc0f07275d5d102a440e5912f2f75b8ac5c11e65328970e9fd410e55cbf4ee0850daa696581b943869be35e431a472951d7c145b5d6df832c61e5f1fb95428764f2f1f1b13582570f19f7f16b1c275a33a04b370ff5e202e7755483fa0db6bc41b142a431a7ce87cd09d41154022c7e36581e3cdcfb81cc9c20aa2f6a6e362c766f4aead5719f2c3fa95ef972bd1fe7a500acce6c360455cc1535692a8d34f030aabd67b52ce855d67e3457b2089e8d12f3468c05403f414f580b41a0e302c1b8894a695b939564858c5c7b7b7425e1f8eab981acd58be6d2094cc98668ceba21168e6b6bc94ff870de594f041490ac3cc09e5fc80c818c95563361e5fc42656ec689cf182e15e4ce3fd8dee70c89afde7c072f5dce544652145e04c2c9f1e31c02299390224b68c4b44c3a1bb5342cb491900d0983b70594c132176b9dcaa69271799715a01ccfd72b9e058d9c991578fa81aa50f541a55db386a327fa6af400548ce9c2fbf495b9ba520b5df6b4fcb5369862a0d4f43464379c3fad7a6802f75782c5bcc9863c5354536f0a4c090951511415cb22d7d20e8152576efd513260582c2a1d8754d61515fd25b0d4e9ed72380b9e2683e62a3a92c5efe570b8995fcc9d620c46f2c1578f3c7bb5ca4805b3fb7984e87ebd06062b5b5a68fa3ab60a0dae27c1b5851e337d0f9e01a1f2d2eee0d74e092ab282bd7ebfe2bdbf0d419a8e61c615fb8a819ec759d4c0d114f17306584c9ef46b16f7761a1f0ad8973714897e3ff8632595af32aa4839d6d7901e103a5f1ea6a5153602d2d416128e927fd978eec93a40d438e5bf6bfd535dd8a13673f5b880a9f5ca5734526a4118b2b53fe96f8779ee452b36d3b98b08cddc18e2ff0fa1366003ea20ad894b4bfc2d6d3c73f7d4ecf32850085c4b6a0d97e17ec7e2fbe12269bb3de3d8c8d0d135de8e9f97a5ba007f91399e1b30457beb9e1ed01d2c9dd9903ca645f022d28b1c29c4c80bf65b404c16531f3ef30cf5297c8cc81e1907bbf29c463fbd4b997fce4a5c21aea9c78e732ec67c3f551734bcd1388ddf7a5a7bb1c10878359805e605fb4fa3bb28aac12ebf267927fa7e776656d325f74ba29da5c2bf3e97413234efabcdb51d309c362bae4cb685612ba04ee50e7e9fac94df9445490117765325d9d9cc2536f9576f79e0b607495e3e5a75285a0adee504b1a78e65edca0862f2852d1fafa4888d098df513802120c26800c93d2d729a0722ac8b1d43bbdc9c61b8b0d61551c6b3ba6d9c76844f1da1399d459cad53f660a5b08f37bd7b5c60f60a85597d7f9bca4a7928ffe83e1faed5b40776eb974403f42477c91c664ab12a5b8625cd0605719f1cc82b9d18a2f100b279901ab9f66eca211f23d9c51155bb0d67bf434fb9a5e52bdb13c148a4b4f5068124bbb1fe18d3efb69612cc1b021d7e7859da69f8af25084406f289bee11e19bce8fcda2beaa94e02b552dc890cbd8068f04d6d759d916c1048bdef77a5fc3f3c1df687a8592b0246f0b476c820d7a01546fe5a2f0fe1d450eb551a492b65eba8840174351856bc4b4b1af8062263f8685cb81ecfcf3adc661ca2f34dc6089d3d173092c634266b9df1a8650e8912545984129363f4db3caebdc130a758209dc38777ad5e759d4039ac2e68a3149b1fedf082deb1b436284a6c557a6ea1b0a3ac7a10206e4d477efeccbfc3004ae888dc3eaff9ee62b8248c68e66b66701c0da40151b2c41fb02b3a19dd0c40b30199bb13db63e566fd6af3e3812f469ed23341e3c0972cc6e1ac0868ce0043cee79f89922b14d4b9d40a7b4c5ce184f5850d4a91de0e6b0497598062604245d60b27a37a27eb37faf6e801a04e89b12b080f74925705cc9107f29085bae3a8c5d368ac4e1a76bffd6169eb1f314100acfc4923303f72a1425e9643ffba82c3da63b90ed822eb405711f21ae1915f8816051ea837bd335bc10c6a3aee24e0fa4c1323cbfee3062e1b00516f60e50ea2758f725fef3a27ad16fbf95d7958cdc20b5e81a44aec8935f4d17116ab8f59a24c74a55746075848b991017651d3d1a6b9e6185e591c0bc206856988f2f522d132cf7428c2b45b678aaa38ca5d2e99a80303b95381b8d51450ee7d40c4f18cf6428a07aebaf22533ff86f8c43c192df3e659320c71826e4060e945c449d008113db777a856e9c89050adc8a96765a74fb13ff8853936d1d60d98d764d2d07c0dab6f795719c8283d048424295effe0711e12214489a970351b55644541beffaefc7da8c1a86fdde7deb735ca5fe3123ee03034be9bccd4ff3df460a9e8f31be0dfb49116a5c3dde10d9c955479994d3fbbdf7051aa94da6ab5c7391dcfd2903cdb350e40a2bde5710bf582eeef32128e3dbedd2b20204bccfdf0b57754169b31d72825dcfe0493f17206e6b06103c3091bcf693e0868183eb34f80f00763e06f04e2e60abf92f5f8103e288ab431266ec594e0d80c68dba450c6ecbdf7a0a84d4dcbb9bc5b21866364e0cbb6426993cfe5dc03a5e61a6d62805d8c2d8cc44f04b4e07fe2b97f0f608bf43d47b1678e0ba9f5095e5340d492a795a0d9de9c398a79a9caf105763ad6f0c56d492989fa088729fc1d62b2a31070f3a3e7c7926761884194ef95076370ec4ed8bb2abe0fffd4d1e8fdc7bbff8a912e552d42e44e91bc82fdf8878098f5eaf1bdd0d8e3f4684e13a778d0c5fc33ed7f048ca2f9a82e9460767949f6c0ee7fef146590ecc664b28e28c21e309a0b004974d44447fd1ff477ed9403e08f27864e4d354aafe3bd8152a89610276fb71342a507220493e94da9266d2e0ee186e0a492695111dd17793594b90709daecf592826fa480300293785fab87ad630c430163dd2871eeb1ca218d6d84d52d58e8ddd4a6d510444b90db32b562d4a5e76a84c281630cd80a3c41468757100ddde4d28bbcf1282038984171203c268c697741218ba0bcabdcd5f82fd377a1eac1e09f9524ff0d72a8d43e5265767cb79ee7b25946abde2ce090bd38f907ab823cbb3ae3255e9c1bd27c8f4173cfcd6ce1136e908828d1199cd6553dcbaa45cdbbddfae2ae13270a5e15b11dcfd28c9f040b76e1d189879138b807a116b00abbd1850f56db94a9b5cdfa1c294ac24811992e510c463a31d8dba010494d8b77700b186677e94fae402bb9f69ce396320c1412f65641314b73280580872468d9671b0c546ddb8b585ff76210e0e028a98132a098d1127b4bfd7b3488a129bf3111144fe1ad63e085f9e63c6b6022b35a557a34cbe8f8d82c58d6f015ad29d46d4eb607fcb4bc102e3cc726d153d3c0657d4171dfc39f3ad31962629769fb432aa010e43a5c30b4e71c0d8ba124355c5b2a111280037569a63eac424322c432758f7a007f0962720ecf5915ccfe1c01eaeaa75969ec2fe588e808da49ce8135f7d4c51d58ccf37a9b8538576afe6218bc020731ffbbbfd76160d546f570d58bb2a9131090dd2c31967d66a471c1ff64f939e38dc7b4df8150bf4be18b58721ad96a177f577c3b5eae7a398e30dc8297b476aca74ca8234b076a5afdf339381200b8279e92e98bdb4cf4c0aa48af634ed738210497e6d000eb99cbf1ae704071a6c431c9abc8997c23749070d53fe4fd53838acb06aaaa79aa46af9c564305718235a758add978bc65d315bc0d01e5a2511b4bb03c3715973a4666b9cea3cd9b783e86ac224443eb948c2baec8c99d18300410cc1713b92202ca86f88a2f877128a81abd3b08057c7c25ea57f2a1f8d9bb974ac05c7ab0aa100df66600ffbd3330ce099be1dcc11f846ba69ec08b0c96b6649ad5fd96e11634260ddfccbffc49665abc458943444d0b339c4c445e16e114a3b6ac5c498107ffca4cf541b30c684729c16adaa0dc2460b8aea248bf7e19843503b9fec3bf7f1c6284944f6fc63016e6890f07fe630427eb0cb471f191e238a43f194941738a0d19930e8da9a553d3c681673a832e013911cf85407f0c8dea55c91b47c01179f94cafa55f19480ca240c7b5b1f372b6e405bae9495d32e36b698dc2ecc60353d114f89ccf724414c2d28fe8cc36308a5e6d4116da7f59aed50d38ba103fad4bbdd6f183ebef044732439fe0963305ea5485022c199eb4241ebc6f89dfce44bcae8f927d67ee80467dd20e4ce5955bd51f62dddcb617d399c43ce46621f6e5dd0a94c8c1ad655b532a43bd8cfbfb783eeba32618e662ab00c0b80952688a45423558401a1bf07c8f67ccba199db609c3c1d31db6879fb919e477ed6e940020665062a84ccada77f96af76587e404ef4bf562ffeafc45a14dc8b18a4129dbdde07655c940c27cdbfac313d04e80b032574a434c66bf19f9017e715220ee28f478f748c72190e61638fd5e763b5fef74318115a0d85c9264f6708e002a50fb158144a4cfc23658848771d8fcebc522942405345dfe00f9b6e8c19f387b13470d6ee6907606300300008382d4686ef3de62517c93837f2f60fa9347d211f5b166fe4d7e531c05c57210b88b29e56c1405cd6c9b153e3f1bb5c5703329037b2b254be23223e68746dab50bc1f25ea87020f2d95c818f21b63f386f41c0fb0dd8254155b81dee18cad39426ca90e4a535cb9e76a27a089b2c3ae6cf104e69ab3a99e937f4ae18940efae603b8817676e4d600c46d4e427dccbfc714dd5589f65b645eb9ff71c70f9c0374304ab21d76e386d628081d98f0b3ba608ede102340af0e9b9ef8226a8a72730635b0921a25b3a0578a813cd9db37dfae130e929edbd48b44d6453c29f2bea4c53dc358e8ec9a1fab04808ac103f8cb8fd59aa9992f8c241449b5efd1852f7bf1c9eaa003074815f9bba43fbef249973c30fcd29e5d0de69a4f6b6ec6cc12950ebc85d7ace1d0ce29d7095a3ca01db64cf02123814f82fc5bcd5dec1eb06582886a34249bfca144872ce269aaf50ea4b7e71ece906d7ebafa2a2bf81646f4b66a203e1c8b44f7dc48f2b90f5e7f728b3ae5fb89b6b4bfe90c0d3fac21ce81545abfc3f0d7bbe37e209b71310c9f53bbf4236ca4acc586a8a70341397f12344cc97bcd25f6c3ceee685eb9a35c0fad93837ca52af8ea92d03221df9eed6ef563b23941f87a4fcdca2dbba87a8c2b9cbd05a106cbbca20e16f93d3c26415e84d970bc54ffe830c866ab4549a12c23ff84645817219f91fdc101048256ece05958c0ef86eb38eca2b55a28e397904a0f8e0b9f94d39d49c689367610fed215e50d17a051fc26f14b07743aba98a218a1434f2ef82cbc168d72bd658550cb8c2acd8f3fb02ef70a9367ab085d91a51e8e5c4f2424ab56f0f25382d4c11903956da35da763f89e4da7bf91a9cdfc7a54ba3cda529b7c04ad2bd23410288075ef88b13a9cc07e5c8ff4cdd79d476666c049753515aea4c54fbd2bbf28020b6935e6b3b93a106d310cf407818ef8aa6b31df4d6d190dc20094924513cf488e1610899310ae60cf9946737833a074822250cde9288cbacaba54b3e124c00da42ec163955197ddb58e9375c0907220c93d4113ee729e8d98f6a34ef8c6713609972666b4f84e10c7f575742e1f6f8d641d99524446eba86e9b3e9ccc180ff0ec3cb40c8f2d8ef8e520e180c289d3c4ef298d6f86e257cfecadd1e4638fd72f611339af20bcc7b98b2d71b55a6c2c468412487946907f476278c10500512a6bd8386e0ef261d9b9d3f4010487bc45823b1f36ea63f71a06bb1a772f14a09d9b8c40d250b6b8ebe87d0c7292b8890da226011b96addfd8a75a04621a851d368a0a1879e22436408d11e66e984338db2662e48130ed18ab9670d824c80b189bbb74fe25b90258a03ac816cd00a59566ed2e7149ddd341bed866deb076fa4d0ddc32c0952815a633eada34deda23925d3949eefd2c296d2b2b801d502933445214d79399fc2df95b7d6a311add180500d0eb98a965cdad919edcfb2b456c832560370607c0ea605330a6a25c63a700760279cc1f650df6bc8b7f85776dc6ecdce55b4bc62eeca940663ec638412300d0fea94ca33064cb8f4e022a5fc8ff9b1a71f15227ebfd770a511be21f2aed815b9d27814f88f35cd51a197ee29273714c2ac5ad8f75a898bb44cd8571aab8f69cb2af4db6676db7b2f530e362b1c3bd0fb67caa48cf5df5607e56f78083e7137aa97bd7da13e3824bd3c5e292c81d26f7d1078a29935e05f7503659aa56271c5262391d6c8e2a08fff86c22f6bab3f3c1efebbfe42cebd00433f555e517ed822fdbd34de4240b79b5c2dff9aac290c3857463180f21591ac197e218244125fde54dfe404420bae25f8d781e28c9ea74a8c2f746d428ceb70243577eb03f8321ff9abd41e8e6e7a8ba6b4a3bdee9168d2ccfa499303d53df271a15245f58e0ba28b94377ad046138e287479d9ff58721587043af7c89b03210cc1e8249f3dc5f45cec07861b08d7f8ae5bc094b0a52c2dff86cb1bcce9644477114ee9b257e4446ec63de1b488b155a8bd9955425f6be3699c5d2be7ee10a4ad426d2a693d7e0ffc0e38d2623cab55f28f05c5557c1da59896b38c07f89f141a4336c304cee5c289e9a05581b0ef813fa1cab7026978b74413d261b75f6114aa1d5124e8cea8cc4dacf091128675bad07d6f2b6b55ba182d57cb5eaecec5a39c6479873e0e37587fccb6d07bb0544437e2aa8f2e89a548e87d3329626438df0f8f8becba8e0c41f45a1e4fb5a5444eadab86d8156b6319654cd1feebb1289a09664828afeb4adf9a5839d01c85a238ee5be9a24569d7146f5c5a9678908c3c4941c72b6666e42a3e6f4db434198645b9686ecafe52a100316169ef4fed55e036ccdc099afcf298d73bea505df608abc60225504566aae2b83d2a221de7657579748efccb0a7c70ba4a6d0837cab4d58e46f9a48a07756b53a1a5ea0f7635516b907bb64881c5731e8fff60097fe4335b275e56df9d80d97a7739606a23efe4fa7fb2dcde63ea992acb3491851c1f94bcb67a42ef5f7844d293388b315223cf205409cb9680103af2b223dcded5b2fadbd0d339c75bf63b19042aa77869f1ce4d54932f4e9b7a864a1ad8fe889077cc0c885ab105d6b4ea87eadc70793f21aea5e5418915951d3e7f59783e9a48007b1dd3ef033c6140a6b41ec4c64f715c6343507d05b88110e024350867ce3f6ed080e90e6d7f9a480556a5a70c5d17ae4ffef9011f769c763cb0941087965d3ce4b4cecfa81dc37f6359e4dd370bbebdb1cc895dd53a3b36fec10320f4b16bb39f2204210ab0ed1d5fc01cd489023914de7aca27c8891dd700cf07bd28c9278511f3fc2b9da833aef11cfc46f4b3cfa147b2977b99930c98f359eab14c71b750b8efeef8265c868c0d15d5175e1f77dd22ec47efb08c44930ce860d12497ea7b9ee5b6354be91e6d0cd85ac4110cf0d84e60855c5cf5c3a60bf42c1034209c8508d516a49e360014f62be407adb5165fe7e6d215f9a21ca0f1957a92d3bf40bc6687b37e8b90347d3e854fc3d06146acc42bc6e512bbcc7b90fa3fb627d5e5a70a3048dad8ac721f5f3e11112cbdcee5c1afd6d1a31d0a81c91ac1d6a50eeefb966737e3ba4728275dd0a300573c37a22470f859ff5db3a31ba6fdf2e5522b143ed2283e5e8d29ae429c6b6b81b843118e67509bdb87739b1527c63491df3cd2916c69c5f7e09c7a7e6cca493eea08c77144706ab7e5f3e1aa8525d3cbcf2ee1111ceef895f16818b06901c588cb4f71680c411c0a6e7a811ee6e71b0f5096304e6b2469cb131c4ab174fbfdb82d0d9755612428bdb0f78f85fb7dc4565580a61ab107fd4cc0b9ac381a859a8dac21d182c00efc11d842834289f2c3f487b761447d55fc6732427612a66dcfc6af7321812de5277a3c681f7200016c0574a107f1c133dd68d1ce966ca0a5d99fe1ea1a77e61b0aecccfa1273a46036a1563639926abd89d323eac27de61ce714c9b840849937535767dbc79925dece58c574bf4244707e91aa2fb9f26c01bad21602bdc064682a30e3f5e5a48c978d2c0faccef90cb904fd1e7ff7b13b67adcd221e6fa6ba7aa63d05190598dcc8e0a1b60cf58ba335aeda1d983b6ebedf045d4e77d11c2270ebb158c2655ef7fce3cc45f39c0a83f3d0e4104d0c88b71cc45dc235aabfcc9cfa71c115a9161fa08a8f946c2d205c023740cf063e0041d98a91684f080c5fdb00cf4defd83cf92a813f93ea01e1e91e33d8fc60769c8274b254dca39fbc8c2f2aaea89d0225eecad34d1886bde1d9066959e853474a71ac6fbb44714026c3b5af802c9b977aeb83338d5479c9d7ec01c86bd357d04c80e4eeed0564ede4b2444a99d9121b8a823934c270906b48ba2d1b35e0057ec2608338c70db90b3c77e5548a002d88fca8b68d07136a647de9d2b94a0dd8f2323c60ff2185237ba07efd8d49b4bd809bcd0b22a864ab8a279cef7cbdb22e0ecacd8cddeb6fc8fe24d94f2788d91eae144874a53abca9e1c019351654663f5ecf40e1b256d13a3012fff19af4c119a5d0a3d256c87e05dff323f3d89af38b8b6975abc69d3a5cfd70a78bda5488d353a97bb20805262c3884eec00c098c9472a9c17661c273e05d409c1d7dd569a25d0f8e85859751609e6805a8079169517dbb8cf82671034c8ccdaf73f8107d191a524d23bf0853acc1ebc400305425371bddbc230e35675770b0c5c27e0d59e294db4032b0fac21c83bb971c4a57242b40c14d088f9b0b3f6fd422a951da18b66a38752273df0419a74579e1d18a82bb8d21628ae1dfc1744deef83163062281099fba4840177acb4f68e4bc97c249f4a98c1e778eb63309528785e6ab1bbf9f48f1f155b0fa44a76e764e2b678c4e3282e9d386ee6d5e844981fc0d068ce827e092c66d7016f28260cae6966b6f343a2721489a3113fc9cb5a7b18044a2b3c883c4a076c01cdc6853036d4eec04ad9737bd327a350df1fbda99a86da8c321057903c15f54c82eb040daf29d310666013fc8c473b3242f8643d1fea179e1a3539d4bbbeec7579da5f2d0b4a0057639d682b1cfb7fca089c02b9ff52a68242802b71292926a7c2206572a1903acb4f705704de9f6be532ceac4a4b30014691d0a94c12096d7d4659a86061fb91228c073ea4b0ac2364351573000dd3ae49964dbbd915341d3c548430916af268b05883b15a0cc851bb7e755a0325a0b12fbe7b2c725484e50ee3087ff80c2ab5730b91b5e5e2bea2d2823a40a68745e75d0810cc1a5e9bb5b35c1c13af83ed9a1dd43984659e4acb80bac93f55b2efd6721635a0b3713048288f2a61c5082af585a61c621046c37133df6877d84e79f9d1fac2490de14c798440701c6d41a00ea2996e1288467cf08a7825e88ae82358fe05293ae71f91ab4ec498c3a157c01910a31cc017e27af57f926f1b4a11273342924f7a0d08a48b707560adb1ce9ae81fe0cbca97900b771bb5619a88b133157fb585ebcf264f26e0246a29126c18cb7692456abdfb48c1a102aa54ebfd5ba3ad107faac1bbe07c8ac8c4fd217e29bf142ae7d386cb0ab4e560a9db9bbc25effbcbfb8f35df51ab5e76fb81790176a6b84331800015895b09e8d8caaf5a2fcdfc518a2bf8a31ab50ca30fd6a28ab11f96ffd8a1be471c34613e29ec673cbe802e17373373d5b069f4d8a8531066ce2687f0c32e0056c1c5fdebdf3d163b32da327c74d45f97d1cb31e73596d0f18a3a0622914df5d2716788b3831cdc3bc4459bb9943150ef090bfdd1742ac89b472388cf746ce7c6e6115e31964d4d22849c1e50edc4c42fe91cc4d7f2ef3f0f66898183514e2b07b72b73ef9a63313466878209f0b51901ed12442924d60512bd10562b500965b18e8487c73564df6a393e1ee64099c13b880d3004e31cd1d3bf0043a9d2bc52f6875d7bb22efb00e01bd10256817ee8c7d02935cdfb4925868c2e556ff0a0252c6f80b41e3ae221a845b2e0d07188a6b1f22f533a02f2f13bf2fb06b317577764c40f057808b3138e21f7b430cb79f800f16f899372296268643d6c8aa884beef196ac118b2355181c687341856628a9fbda1222b327c99420912bf62b838599195d1763c1f7be40aec21581be1a12b2d7e267811a8dc4e0cc3242973a15308c24732306a05da8320926255fec276050ae29782f8eb2aa510f1d55d30682edef929e6ccb5d0342f4411187407021b5b11b61a4922ef93e94ad1abda2fe548c4a81b79c3ff4dca1db0984345ee5a064d070d09c51b7a89a423e97e198c75d99d0363112df24cc35c051bd577e3e6f1c4dd0b8d66a5ced9ab0648c8dc6ee700d445c3c3e5f0368168ffcb0a13e21202bfd678a94891c7be25fbc7f8b9645fe645c8e2de4060a0e496f2e1627673064fcef19ce19e313c1fb3384740fe77dd6155804e46fac78b905d906ea28b7771ddfe2cfaba204eb48d65c1c33e2c93830940ae44e5abf50e0c50e67c7b6500dc981f7831cc7d421a9cad4119787c1bee501df5b3e419006a21ab5cfaec44c6b28bf082ac31865784d732ad407974a6b9cb3fc788702400bb233c8250dd636e4cfa559e73dfdff531f7d151aba75b13c93d007702759d4a0d5c7053d3c1f84a46dfeab5f1782e766fcf738eaeb2bce8edee1a08431aa0a2c5743ed412a0d68208398d503d0104080c86bc3cb4901448a1224f9d4a57e49cc5b06a2357ca97f13fc3067a72dbb559af7f347983854a28597e0640619c0faefe4e306aca40c6c836b92aa3935154a7d2387994c9eb371374eaa0f64c651ca76eed4d2683286ec2a6c648e9e4b1f69aa91fac2c083824a1381c583f369b8069c1df44e799f0ea0af8fafadcc2c0804db33a7fcb5e324d55f4271a63d70ba7ad9ddbc59e440316f1ea85ec74e62d1d5a9200d965145f1480d93791ab88bdc71cffb508d587e6636dbd7c447f5416f087196abf41e65823eb988cb9341a9c84549ce4856d4d35545fb1936c8003f2a637751f408daef9dca9bc63121197e57c1133520e770a5c19795d9019c9668a1546f7a318b4ce74a0c49e0f6363ea597e60c77db1c54e20c758c58ca1e30c27a22e4304be0207c5d18d246253521d122c008222248bdae69217f5bed647a6b6df7b43ed3740c8f2cb7d8239e0b0f2fd82253956b92ef29f123b034040c8c841b013f1cd23f3bb0b2e595d5862b560d52fc24342b656393e7fd5ec1220d0b97ace46011d61c31b2cbdc87626858552362dead4bb43feef46d346d8133c72f9f09786356bef5354ae1d1720be9e0e382cccb9215f514d52783d63749d5bcaf56d9287cd287230339627380ea20a0850b5cdc3c4cb413dbfb493f34c827670404e281f5d34b80e1cc129f2e0c99c6f3bcd475a38c10e261d22460f6c68e59c50c328ebc3e069d1bc1860c69d3bc3f7682eae08874ce7f8a7022e130158eb8488c5af85590e35a53dd5fdf54d716e3e7713a331151b25386de025b0eba5362f4b8297d66ad5e6f152e9808c7e921145ec445cc0712379bc324936161c6f5cc4cd0c353c12683aa6a424a502f3d78c42cad6a5f4b7db57d0c364e32b3e8d8cad4d01c6472a33f002257cfd7585af45a41084f932c2dfe8acb5b8822b79861729ee055ba89b96fb025d7a6de8bd9e6297dd89fd206e4fcc209c29ea187d8e496333d2789a8a6dc5e20ad045363805188fd2d43aaaf9aa99d066429916e81e1227977e065a6ae7f3b85ca5069a59dba92d96ab00fd17203a110f5b9892251fa731b8a8494c2818734315e364e8366f66f69170672ed93283a445207fc617244e11ce338ebd22d5d7bbc5b4138e2b9075055f91b5a28741a71c22d857e07865504197f63c5c58d9b4556aa6b742fd0109fef09250c4c4cbd037a2cf9a96dd8f731ac821c863a5130bf5e0f86f498f5d22554bf1552200f753ec72111150b412f80e2f55aacb764a9234525c2410545c445efff46ce2f2fba6d58c3fec874e694563b8350820d4af9fbbe6ba6d14c983b3b6716b8f26cf73457fc576ed54206e8eba3ba6b25b9cecf347d9a06edd2a3dae8ce97035fc246050206568ec815dede61d503cff6e9b7753604cc70bcf80404082eaed037f629d3fbbec371e7a5a077ca0d9b50c9d38ba11bb4ae6f28032af931d3e183e4421c39c91acf01a99d6236755b914b8655d29fb86c0e543569ffa995eefe52d2342bd951147cb1a4bcb4cd4d69d9d080aa7bd07b57e15a0e0ffd8f96dddb49381bd0ed88defc964af041207569e67777a449d6934dbbd359bbc45a4b8d218a8e9e6729fcb26169f74204145ab9e2e67f9ac7569aa9e70471d0c367925e9932c564828a2ea5c91f7aab2415285edd2d224671d34e54961a5b1200a25754824a3fb596b699cf108b538040bf2f00d43e8e158f36ce6a77079cf78978d94735332300a5c663e441ca07c7415f7582d52cc565fe08186c0939a0a53e596b62a424cc454683919280a218f8e21d8fca2e0a0b609bb035413167006be16a1cb9eae2f76893444efe8fa26e0bde54e54cf682a6f91a0d2faf7c4f31d9567972467452d2524982c6706b242d35989285902e7503c14fabe5aa442fef51a200eb495490e6a397dbfc4d594ab7fae388774a3a775a3da383e650521fb5936b40b0beb12c1b87bc088328785b80981f3510da3114697643df3d280fb6608b84ef69a7a5904d489334ef150a74ceae6ee6d704ee2e9baa7f4281fcb16f4bf315774f49ebca369678186912d871e9cd21f8dcf28eed11284eec6be42140e551dd3ec59e9344758468dde9e9ad00caef1f03e4827aec27e46586a51de2d26fc2e17c470ea67463130669f0c6d34224acc32458e2747edb44ed167f33fc2984ae8d0e75019673cce3130e6d4f549055ae941a0c6c464519221f73c728bf376bf0ee4e680da75e910f0f618b0a3c599f425f67acf2c31f6fdb95b38206ff8eab309ef755f3c2c31965131f290fa771851352125e7a795c08159c6dce4894d046aa1b6a2af7c19d8e8431c5ebf29997fcc22b7841d2039e5ea8c01195103574b2800ea16fd15388780eb1bfb2bb052062be6adf61884f043f871f9179681b40f2edfb9ae04d4f09fc1df56daf2a5c651f6c1655666822177b1f6c27b3a2bc5d5bb46476eac65e2be24e6095e162c138b6d46112c54c97c18cddd5069b836aadd18c1112eafc6ba8a044e07fec0b57f67cdcdd9cfa478890ddf61ac7b04c5abb47bb8525cdac4776185a100e74d6e95b92c1e8872adb07a329c9a7442f5ce4895dd1cfede15d6898a54b063d8d87274fe88b3149274b56f75f5f6bf5a6ca9999611bfb2e114742f96c52600b11a4a4491b09f046f5dd3a3e546f39d177f561aa35d884e49af9c0e2edf900849213edba22e252d5767ce0404f8bcc4f6c8d974214efd982463a94e769bc95ca00bdc88a5f62f5257e62f2f5f9f5d2faf8af85073b11a176cadae0c413e57cfdd23803405502679c8ce929c3fa88cdc3e0db896d470480b1cb1b4b47be284049fb79e04ed2d7396e927fd383716a55d6a9a1443fad25ae320a87ae51c06ff44a683636ef354d841ce451b79e8b5e896684c22ac6e54bbe6f73a01c8613cc80d52437bace8c0ff8b7576550ad2043223bf59fa7a000983d56c3a9eaa428b73e498832606a1cb8f3f7fc3825548527004fa224cd934c06a8d82ab3809418f8adaa2892395419acd0338386b6d1f99754625ce03630924dc74575cb37ea3b4151951fa9b6c1d3ae4bf689489fed48e86078b3827d37421ae1d146c4e9569bb0274419b72661c39b43376b9814ca79f2fa7b28ed9e04a3d498529413af3b51373c16d0ce9450d6b74137e12043045b530713d5106fabc45b8767b30c11ec4c51aa224aa6baaebd9950afe32c82ea597765fc020b567f681c7247275e748ef440dcc1c3463105d35b2ff94c83e1a73d2ce7d1fa0246bd20bbe58a97835d2830278bb9406767c80d8db5d849a0fa73d95e06636009b583e7a712e7627a64cfe5866efe2176890413af4637fa870f6340b501b0393a8c5e4a71c4e71f22a594f6eff9c74f26393e865cde5313fcb432bc99f000f1c1bc69b32aaa99c3fe0f1113f83ecd5a65915bc41db9799eb6b1b03d0fad3c6be77919ab232e9b6947d02ca6e3350f57b46f4d0068c0e319de44b7823b5cafc24385408f55a95057109fe411d2d77421af97c490198168677da6e59d8c469ec699bf93040b851a50d8f57923a2b36b25c5024b84f7225fd6a9c4c76f389dca07c64fc1987d8ced0694e106d58ebe39a001e471f1552b831b5f050da9dc19fb44171265e8f0e1696166579104b09bbd4ae41efde8ce8cf49f4f0a987f9120962c6123d25008387c6383b252b2d89435dc002017675f5efc51b486213c6608dc104da9ba66fb31cc044229fe9ac5b3db9efd0de219e511893b408d3798bb57215137641565d5ffd5095841b7a3ba6c4502fcea53910159284c2ff910612f2fdd2802d1441b242a0fab9d74a3fce6137ba649677933de48ab67d9ac2ccd62a31844db8db2016fe6753f2708e37237d8a9beb85c71e2d981327c33823a8d4207df0b0121a4c38badcc72f8f650ad8a3f1c964d61410a553c7fd5350e43063a438adfff5fc929b5e6e1d0215fb11cda94fd962a69645bfb0397f2dfd9c1a2267a56cf6c14234184b7365e5b880c9ee6fbd5f5e300502344afef7ff8add0203ca4c7e021f0901f5b79f2c99745496a16dffaf962d4bfe02aa3b4fbde7af311ada018384652168fea2579738ba3daec9a2a614fc1bc6bf633c5e4b5e9c4c29437885f927690cc55887aff87bfb58dfbe18f087a0d0789d4a454be9c37e733f6a39b1a14ddf0acc8c2d4c1db96b3a42511a01b3ab80dc2c50265493cbeab9534c8fd878902b7846b72cfb4434a63b67bc3ceba0ff42bb8c552572a560c04e9c8b76cbda952e23031faa0c39db5b158833d958451a9a6e320729f0b40c69dac259afac3ecfc852d05dc22c71425d543951a6f592337fa9c4d51b0306ecda6d23c7a6086fb5d96564dc0f717cac82b7679fda27bedb4d79c118f66cf64873e374023a55e29389186a0e1b8f64e5baa7f0894646129fe468ea9e0b39ca15be2343988e5a85344edd1fab58be7d8add79eb726a5d0544c836029d6374dbc85f93636cdd0fe783485e42d708c13344d208a9eb57a0a2bf368f9f1978efb3fd57501e5818debc2d3281d176a029379b0ef90b29adeeda8249654530ab16f489d43a6f8b04fca74d48d42d44752e2206c64e5185b3f8ef31667ab347b0f4908c8e963fe49d5cb246904ec6bdd574d016e0034701acc1c75c86c1c0435b9306473cf8a413794fb46840fa559adf12dcd7c3e09f86b7b17c912afeb705c395eddf457d125bc23d745674d3b7be5ba4ce3b9464b93095ca11bdc6a65e3e3470f2c834caf76ebb588c3fc654af43cfde26a0e0ff79626de3cd93f5f8f3ba5fc462c6a9928914ab0f2ba7ccd50d14d4e567e2e9b4e1e50f60c8151d11f962ec175609bab069b70f93b158618d416064138b4eae630bade7ca82bbbff0f54180b654ccde3528a950eb43bcd0cd30d5bc488fd5123ddb8b5dec0f395d73e67ea618a501f0a3d9a31d04332b92e9af355373c1913dd5a98013cf8f2637beff06462c9c5ccbec42e3237bdd7347374d9b3b16940be20ff8ca6119368087edb207bdcd6b00cd245d9447f9e92e19548e9fcf72ea2a8a6e1ecfce44af6864a9758b5eccae389e33e742f235ca10007ac79942f4cfd1e200320af1c6c6938516268d3823bb20f53b8a0282c6ef2d9a5dc3f0c9926685c3ec93d6a488a6de61b2b57271f7b05a997c20a87b080fdd8c8980c4e600c37845a4db55de43b023e646074816fb68e431660bdb72db70c661cee0888a9db65fe98b8a1d574b7950b5e9e51b24146007a2afb9a1a7c8352a10edd1fed170c93753848c0f70e708c21b552fbd9255ce646b2ea69e1fed9110a015633d987ca8fb6b99fec50402a5dc40d8a349a4800272867b9ae501fe9a94fcec2e7e86a209210d9bc2aab338720f9e28612ba1ecddd5cefb91c7dd5902f24937365028a879e11c17a227b2b38cbc6d2f79794ad52eab5d1a98ef13aaa3e22eab5db0c753e1ed856349e3a07ce6389722be9fb40077ecae38bc3b619617e61450d276646e5879c70c1d9ee90206960e470fe5da9fe925e8adb5672f3a7526665807f031d960c749d6cbc73a0ee040252e87ffaf36703e1bd29e1d540a824631f2418e7ffc8f7dd35c9327d0fe1373d08755ba1f290771fb65ea28bfa1d1ce24551743ea2f5f9e4f2aae934d870afae0dd1273bef0c2d6e85de10d9b59b601d7e3d0b1933de33ae204da73b335c366dc9b5188eeb059f7a5c6eb2228d41903c0859b0ead3d901868274fa4068e3c813d00033741ea73c915dc548822d584e368c0afb9c8fff7d4650ae2c2051dc60ee3eacb4e37afa2d870529cadb159d725835f05dcd2e56e16c2a14855a8ab285d8cc94af527e35605952cb29ed045ce53ce96d3012d2b87cb5dc9c5940688b2ca999417e43f13d350ccf33f13a8c24599a177032b13fb0a9828bef842959b645bb4be9843ce7bacfd24360f686807e329afd84902dcc15c93da620e0216276e80ad7e32891c526510d878fb53da51ef66f25e457e971c56158744e306d30ded17ba8cc9703f94bc2c663ca5456c99988df30cba8a9cee778d8051fa62589b34b0a5bfed80a7ac5e9f8b8067c0676581436a7046a87e95aa70532b8a9e64899a4a1d79d4598b914dad5df0276cbf5a0c0e6bf643dd2fb9ef53ea8e356417cbe71d05b1b81a51ae3ddb78995c1490f492ead7046b19c5fa0ce2c89175d9010c59bf2695a926bcf96ae79d347fbbb8319e9c2adb28752a6b9c1fcadad91d8d95b286b34326808aa6903dbd806fe6e7f1c1e899571b0bb4e22296cfd7412783b127b1c84d762e11ce0ff1d7a75752e198641454cba8d94192cda93586a6988c8ef74d97f6d083ff746376eaeb14ec5a1d6dda6ef4f261a08acc46032a2bf9b970e7aa8f6a6c4d7857d1fd2290477ee669400ed6f746a3e5572b849367d029632653ab6106db9fd2e639531b9e1098218a4f53473e2bb3d165b365b04f09cfcff0ca8b42ad7dbc66e93900b90020d984e76b5260a7658d0826120c8087850a135f7fe5fd8a30b2ae7b8c3d9fbcff4558b267097dc8eba576a2270ce990030638b75b7d462729830b5b57fa46638c6c00d6dcab8381c768a80131ec9b059b39beb66a77973a406c9339b06c4f376df92b49ef6904e741bbd04417a93b388909ab029f1c5db8a19525995b328138b99748ea71456e7a413241b33241d030ac8dd5d5c4014163707b1cd0e9bc5f1b25d7fbb7c3b7ccc80b57226f47da9cd3aa090ce9aed9b97c99f4b96b177130434f5f044b6c3c5a68bca0835bcf0b8df831fbb12c7e6b41f4dc96f61b6636cd2d67b4a5f804ff482464b9b244922654a29bf05cb05c2056384cf83cb4f3f94103246cb4b5d903f84b0582e44878a344992244992d5e52095e2a2bc4ea7d3898ad39f2845013eddcb896047740d85c169fcd3f79c565229a8405788ce7890f1adf0991b0aa3846bf6fa243ae6f35be130df666a2714bf82f37a2b5e892b89a6b1bfe4a7782856f0a753105bc4901fac7be61d03d608a5ba1d90130cc33014eade7befbda11008a6bcde542873280cc3300cebb1d1aaa27d62fed80fe5f90976a821b06359bcf7db7e58adddba17bb376443947da7c5d5c2d9b4f628ccab04d134bbd429ddb44dbb30f6bc1cba3f346ddab4bb5bebeead9b366ddaddadddebcffa4157b0caa7e3b615acf2e938ae082c8a6cbeb24c4921c6b53056f988e83be8de2c5f9cdb40a390f320366299ea609867bfee7eddb59b356a35ab59cd6a5ab679b125d42efd9ae6477d352d13d9bc5cd1ddb4e9fdffd13b2a84ebbaaed369752ff5bc9c413014baaeebba7c34cdd7ce78c576180dc57ea2e9780ff1835df5a0c0758566d14f7ea233a09675cd3753500582601af4e4613860f04070030e5f5ae6b305b5744c0aafe0eb743a9dacfd0f183a6763a6f9add6b71690795afe7c26d3667295b7bee5262c302569134d9224fdd60a0fa6be050c3de33770d3fb81ca0b5306372440cbf400402652355e04920973349fb80933796e3299a6699ae6bd366e4c9527582854ea89142567cf2a8a8a8b8a429521efba2e123c9dd05ff6b9f8d59b0f9a977edbe72821b0d85c847f9120830d5b506e830a19a03df4aa4fa1de7c93d237e99b660875a25028140a55832bad8ef58c45e061be011df33561cbd0ee017bae39d3ec206ce489f59145318e23a9ec1142ea9c929488d47154a94a6e855dacb550f073b57a900b374de747b221af72d2b1dddc60bfe1bad15123d040086b0e1e3a22d034ae23819e711e1ae673d7f9a85a6185518676e27ed241142fc2ebb9ab1fe4018badd27185d7573f198ce3388ee3385e570d1528a59229c584086aa754723a7215e148bc896badf59215c287b8f9f1136ec4061a7a16fb8b8ed263033e680002dbaf827e72afdb0e158197c475a7c6832ac0a107ff93826b16c130003d2a5da552a9542a595b23f4b182529369753b462514ef755d2c2ca15333b78897e4f2b067b25c696a0b1fa92762a75eb2104ff53cb8036aea181e5c3f2f91c254c634454d2693a99e40d44a92e5ebb85bb783cb5ade9ecea853601d554ca1d28131de386d0a3c23145b955facc0627ec9c2c6831d8e500a8ba1971a60f1caf0b2048b311c80d6af069a3d50433b096faeb986fb7da09d84f55bc15361d1eb3e22f067838733c24c099c4307f4491e29a295003f797af0d64b02f2b390b72b14c81b9ddc2852a6e83c89e61ee7799224499224795d35700f1126241126b2bbb3ceb22ccbb24c0a07ba6bc2e17b9b841d8e8cd20f94153e9ee79ea184a29b5ab2acd600411908d10297f375bde0fe8944958a2442d14d36b250c846e0ea50c500080eb836290cdb68e0b1ca5bfe1da41b6e735296d910bd6063ae80715374a4d48ea4b247888e3dd924b66489849fa91d59cdda56f762ec7939836028f494d2cb0bd15014fdf0421e0b13d833cbcb711f98a8cc510e904d896ad4ef420e908da734e2c9329090115594463c58f644e3687f9891bd241adb22ec108b72c933f2f7a8568764c707ed4841375c21e602cae1d9b1b292735cf2ca97b3925b720e4b9651bd3acda1147ace096b761790e6dcf6e9724e0ecf0e973ce3cb99915d3e1a38f728c725d3c026954fcce1f4c901d57da20dc78386f64da0472ea01ecd9872b30cd4061ad6cf0606b8645f0be8078519594dd33444f40c2dcb233a0b7a648950a435605d526849672e130df3f9f244d1636b60c78eb9453aa67e57eb6299acd0eebc35d99114beafd81213987e383f6c18586cf3e6871d473a8ee368473b86f5a654bec5c465cce639cebe8481c57e02e4ad00e28811977dd019f046017bc6403cb8669b5da39a96fd014a53f46819759a8d701f298fb68f86e71e61370f9af77bd22cc0f75e1f3548911162f5125493f07307de65027f7ed087b756268c18b65ae5ce977b2f4bafe15c86e3388ee39274ccbb736e39950e24e2bcc6008d002e947580451f3fa1180a49ce43d770dce751b4cce71b8ea4c1ef3e97a26368a87d5e96b4c440f9ef5d89810b748d972e43c3e5cf16f09a2d7fa6a169b416ab9de01a2e53eab8da0758995d1e1db76929cfeeffae95e3f8abf1d3e273d9e6bb5ce3931d3bfed4f87c3a6ed340b563ee83dc83e30a40d45a315b310b002af08cfdec9b409b66c8b699e301d05371aeab063686b5f6b2172e71e05e620232c4b2e7a74df3ee8e5d1d0e232b39fe23f41bc21a7a156ab889562d485ce9f02af40097452c671983720b9feeea64c0fc13b90e33500ee1e59f58f3f6ddf061be40349c7b98cf5912d8e66be19b45d1a783a11fccb24face177307c7819592982455178896c3c0c58233472411882e3382ee7cf4745c64ade01c8387ac99d5d724bd6998157be729917508f3619a097fc0179cba7e3e35df8cc92678058be1ef28887cbbef21a11b4cc679719f9472f2b36bfad805eb20c900a885f5436caadf4c8c572d957befa924701957000298c4acee7c301c85683114f96731875c7ccf8d1c67d7806b843140f057a84734b969157f2966f68596d2e777b6e9101bab7e5e215efa2c16081d24858ad3c2e832151ce9d8d037444e066078fea590574298c73ff29dd2772b350bc95c9dff01f7a8696a4a6e9c65155852af543dc756ef62cd2abddb19a200ca267e8fbd03768e5618817f40dc2a6f1ec45150d856ed131d46c9a6adaf19a9ac4445b49764c74a6a292784d6de2325d9324e19fa087a84dde888ac52f51533fd22fefada82615559b5cd7a97bc61d46afd174681ace344410f4d4343f6ed1338fe4838ca7919ec60424000441a01452f8f154d69e9aaaa8d1666c4ad3383d62ecf3c7d17fc450972601a24b744c915a83f0082a3c8d271dc6204174580149891105a110405c25741883a800054200319ec6244e2f40d2848d0ba66a661cb78e11bd70aaa083b516afb0788deb0842a967169ec01f09bc0061d82a40c2046038c2e03ab88ceb8c54e772d2785d1876eb652d86a9986853ead4bbbbbb3f4702ee8cfbc3cf0a8f7cd499d6ca82ec89946cb56a619c1287cef4c595d65aff71b390c2a2af2ed18d274ca9b121464bd42d9e26e76a85d918f4f0d0d3c3c3e8299c9e6ef5e0ac56ab9567c3f3562b4ddbc14385094f7cd05a6b075e7c20c1627e5a6b39841449962b2749cc23aa26282524e9799ee7d119aab3e309c93478244a0025144956b4472b27d414249df1d6553eab758d17293c57d4ac2a3ad3de2354499e4ca5195a928eab91349246d2485a95ab7255ae4ad34fd2248f409e48484574828c438a189d8c41c62145ac56abd5494de7399a3facc6d509c6d8349d3a72498ebc7704d8cb28f8ca66a8cea7d0353448cbd0d01d466ff452892c91259205631ae487d2905211a5911c499324c91a42a10c8436e083070d3d42a150284492a927caeb254ae45484f49ef968d7f38f86f9ac7d62163e97826635ab594dd334da1d734ba552e95dc1cab2fbddeef77341462095600123e89a175c4648d35c3d3c55ae3b991052a74898f530cc285f28c153e04678145fc251a46b4631cd9b8186f92c468f934422e517722691346d070f179c4a77777777f7aede1ca5949abc887690144c24f426ed0799fbf943b52ccbb22caf4b8b5ad6b2a2ce28b5274935ab939aaa4c52a9542a954aa572ee546aa351c4094a52a32fe2c4893412c1100b5f60b1969563a10a7cb5b004ae496a595b50026399c6f5ff6421dcd6a2ad90b5907a98cf57fb52a48ff42454288a42b196f553dfe2add042287eaafc944d7d2af5292f371312944ca3ebab9ed37c7d34bad68f8528b85d86e690f3c341f4aba74e0b267c7d622d3b500e38a168f30d34cc58ca06a7b0c32e7767013c8ee3986b78d5fa7d8afb5ec9c37cd674c01555cb0759080277e832b496652dcbb2ac652d65144192e5ca4912f388aa094a094692368796e18ed13e5c75d0198c1f80379017587452c454872ac791cb95640c2d43438004e1cffca030ef425ce603ed76bb9f9bf513310c046088816878ae76c7e7a6af78d4ef668a1b5409b502149193c934dda04c138599a6699aa679af8d1b53860e7ad5cda3c70668e8a6d775b917b829752170a61fcd1887563ce86ab55aad78d47aba529038d01ff61867173dd0a104477eb0d65a104c65daf54e5afeb30603b30985301b1d5c8a7a201f6d1c8805284001c228078866c5c84d9e032a48ce491afe7d3e2f290c0cf7f9690c8c41e1ccf12948be388d72806c4c588dfa5d28401023ee5df80ce47918c268bb3ee0365d7cb5b68f9447da47c39a2c86ad5adb9349a8ce94a73bdb7d9bdfb66ddbb6bcdda6792c14efed981c83f79d7b3d3f315a5a71ba7165813fbb3cc1a2ef34cd969f07fc7da287bfe5a7b67d94480fa639e557f48d2cfa86155e6891c2c165ada67d626bd9d5b0ec6adfaf5ce673876a012d4183bfa24318117eeba4a413cfb2e8e797bfdabe5c6ddfafb45c7e6bcbb9d5e556abdad0228b89f9b39853df2a0974bed9b42ca3f289f77e3e11e3eee33e31b75a1c07d6e898908d9b8ed9c1e3069cfaf9171f4c96144b88c565064b904abd7492f4141c71c4e98b68bef32e84e5092c9fd862884159348bfd84e513fb0cc72c7379cb2fb0e4cf083080ca27e3dcd2322d9fd8397896ccf26d94a394858585858525b3b8cc7099f1c98d9f252443c60aa8c683a18eb121dac183860771721e7427722ae2a907959832be33a2d66aabbd31cd9badffa7c09e6f9ae69fc98366c7d4d4fc0c5a86e6b57833090ad5738305169dc6cdd987606b6cd3b80adc0c30962fe87234b016b4125e1f17e13379f0632c0ec2cfbf45cba85208038b7ebeea513553b106d488f5390163d96607c9086db6f92b963021e2a6693eabd7da0e862bd3c00f7e06b984b7090f839d5074d62545665d4e9e60dfd9d1988096b33c03368388c27c773842e7953dcfbccec3bc83713ae974d2e9a4d349a7934e279d7daef499fdc8e4fb890f8f74b9bb63185823e482c4464130c46303363616134ada41d0dddd6ff0d07bf0114f8e0ffdeb4f131004c15a59250a957a2245c9d9b38aa2e2426b8cbff4b07bf66299a6699af61d0e2c7f56bbcc65b15bee99cacd8cf33a508f34500db31e7da77d8ecaa080b32f14ca26ca35bfd84d09bb37a29f348d4816ba41a15028146adb68183b1b60ac29d87996251d3a9aa63b555452809df4b913bf441be91bb507e572a6046dd206ba59f4f30ec1dd24539c43601d55879f5ace3e1a375aa641811e693466c83ce20c00b0e234d241013aa91110da8339caf2075a4223d9870348619ef6608eb6ac65d1f3c126fac66e6c422ba1c15f3dc10b25241fc10ea7ea4b4c4cd5a952a2a4c7799ed8799ee779de7b73aed06064b15aa723534c52be54e4c717c2627dba4fd77517cbaac0a2b35827d64a7edc7d3eb9eb3e9f87f38ae7173d29fd0cf9e17c92c56ab1582d91889e8e3a4f2ae564b59a8613f2319a46628d45cf9375b2ce2d735974161774c5e4c1a74d5c8687929e22ab7f1485d1314d2a53ca4404f53bf74fceea4c2c16cbc462b1582cd655a38edbaf149165fe2b58e5d371db0a56f9749c870516a909cb70b295560aadeeee6b93650765d9b5f9db319fafc96432994c9a76a2678f55e7b8accd19e833c34d7df4394ef843d163a1891e9f50f44e9c448e1a7ae17b87492c7a5ae81e9d494015de0cbc2d3cd149abf644777787e82a1402c19cdbfd5d6afb1158f4540a0473f6ae86e323cd5d9645279908d08d1e0b23984b8c48b14003dc581c0109967b7a1eb4f91b7d39b67d46ba8fcb9a566d1c908d38fbbcc44ce025f0767531d40bac7181dfc5d00fd21c4225a1b761437ed5cf895d951281205523b50245adb56bfb05fab073dc0812518c2320da1045463c9e37202ea8afcb36713a91669323aa22292eca9e7b3a7912272fc36ac7dd8ed3a8f0b8ae0b39bf3714047a825fd0a61c271dfcf9860ff866b7375eb361b96e9f13d1beee98eb52b400bb0ccb1758ee220446084ae8c6bca75396ed18adb5d65a8b596b2db6c1808a2a701676a1e88516eb70703178775805d8731294babf2021438c9aa6691a160aa5f22766cffb7080bb58a5a0ca543e1df7659fdb5d16168c3d2f6710bcda6adb6a846c885eb0710087459bedde2d9ceeacd2dddd38c0526ec011515102a8e85bd7759f8822c5eade7bfb2f765d1fe63baff3c511a81110059cb45062b4b9d0e2240fd2329f7750e94174538ecbbc7773c4eedbb13bf7ee6022d1cd329ed389349b1c5115497151f69c4e2b1b9b3055390cc36c9aa63ba92801b2d1d3eb7ef423d2ab4c0394427802cbf7c222a27f813c9d5ab4568e0a6b4dd3344dd334e94c2804821e78c19013312851a31c200ad0190269c4e3194808053838473c5a1645d67e8f553162cff1bd9c7212e6eeee5cb6a00fb492d0039eb9bc7508702f412215f61224ca5026ec25488ce193e4c665ff9c84611223b9ce45b6c8d8ba2772022c3ee9650042dba3cb2d1fa8613cbb69e24fc5cd2fecbedc315e2a7925f16710be8b5baed7855d6e5a1b1bebeeeeee9fb5d7755dd7957178c92e19072fcfc88fe401a4977170c92f198719d9cba4fd8e86e7195eb24b9ec1cb335e5cf2f5edf0f20cda61cfe4cd57758c0a26f6e5931be80554432b096dcf0f7cb8c16867809e04b9cc7814280c7bc44bf2c12dba00038a49924f3ec992f1019062b3eeee16ffaabb6d60791865fd70d68f1387fe1d8e6a2ba537887af402ce0f51005a851911dd116a02d0ea23882602167fa2a1070fd66a87b53f4c2c168bc5fa11b2f1e1a1564abfd5067cd46bafa555cb5b7655bb691976dd5aedadf6baf7da22863ce6ee8e5dcc8235b831fcffde7b5d49ca2ccbb2b274f7a157d7d5ba4a4f728328fa0676c3d6ec701922f80f2e0dbab0fb6291893c51c64cc6fa86a57d03cb9e9b66a84a7778749e0daf2357b0539e1efef59fcb4d5c64714583249779d702085307a1cfb235e2943c4cd96a95996e19966559869ab68347c912c4da16ceea455d6932f03ea30116166d1b823ddb8cd3575f7d5191c5b0ac0a1004310631884bd6087a4813d137b24c81f01f7c6479211c08acc9215d7f307a66253fdda125238b0f454616df0832726733686615460d819587fc900a9272313e2b327056c99fccb29245af594dcea8edf0b18a505dcb5a11f075752c2cdb2643fb8cc8f872808cbabc8525217564393b060c7c84f0b38a11c2cf1f28210dd2cfa23b40216475cc93a592e82caf94eabe3f39f102d327e7833db0582c168b755d66ad6fadbd027fbc2458ec966a478808870b85df753c5b48bbce092c86b4f0e66f5c81b3f01ec1622806ec490cfebd3871d3c239b55aad560b27f582eeae5d5b2ab5f4a2f4ba905094124a92a5cd46391b6b6da8c03603010720ec254a9284b69b408f74ae6cb3d8ad1728e0ed72047001fe2e5aa1a396ece0e43d95dba8a0ff1f23def702fc9f93f02181c56ed510a769bc898ea9a23405141d128ca309383bea055224a2244992f43728176bca759ef8576e93fa5c8434112962e3505e08bd2e6e09ced12a702a7c65d246d3b4ce33d6c960f34b16d8412fa105d56e0277dd6bad09ca186a6e7930ec0e077e50088c57f0bc9bb13cc3fdb04c02338e89c88ba05bad6e75abd53af2048f154eeb3ccfd3da8eb91e93acadd553700267d75ed9fd687099bf9db59496e06cb55aad160e86854129a553f0f6ff5448ad6507b81a241403096fadfd2ec77605c6e1f770db82f3dcaec09e4bdcc39bf252447e9ee779f6b8ae2c0aa6b5d65a6badb5ed3838f7629c7316e1d0999c5bb9955bb9955bb975b3ad954195123aa71c209c12e48887cb40424490608d78b454a9d4342dd664537466465e59242bdb4412d65e3bd6644ba59bada6597b5df6ba2856b14aaf6bdbecc57da41691eb889ea12814132a1e1b2459b52c8acedc286090c242c5c406255bea9824d7674976b4452c59b2da05f9fdecc97e3ee29456ddaa944e415114652d2a8bac9106515e439f582788405116c5836dc1684b2b9bea9826e7ca96e8ca96ac2dd9922dd9922dd91286995e13ce28986952cfaf188f42dd7b31db11d1fd4cf4f32c014785f0ff47f03c8ce1192108660e1411423734b4dc619685e84cf9284eb4ccbce9cb0b9ffda8042e3b5ce9900000000001c31500001808088442a17048342295b7b23b14000d537442825e3e1b4a849124c9d114a30c328621028801408000c150892c2ddcd8ab999bd4543d9452a28be475855e6441efdf869a9c7eed04c8b24feead4d784bb5e98b0eb34645472f2c0aa6a5cb1e45c9525fbb94b0cb7f78143c9c394150da8d0fb07dd83214b014cbdbc732b77ac29132d9d931009fcd1a086ecfd58179be1a213dc83a3c45775a5bed482df17138484987626691f016730dcdd1047c6aaf9a6b54421a328c5e79562cbae5dd98150d7d18fea05a2d2282bd9da99e3b53515262afbb684310daadc2e33b5bc08160d9c1027a6e280a71d70e120f70c43c48f97d58de2761878cdd226f85980c9a9e93cd6f7ac287d8fa75c023f002e713c5b76ffd48b5ffd85062a200daaaf1e0711042b3a8126bc2c5640cf13778af074eaa29f81fd4e241a49165a50e9f539d751e5c906d684d935cf592db0d35020ed627c712ad834431c04ac64a13f7a72aebb6809974d250527c7c41f7f427597b5cab489f7c2631f34e88cf3ae4f42baf87af76ece5576c5bf0b49233e4484a6fcbc4d814942b2a9c99f313cd00d97a5482901a68aba5dcd569425b9da6c8512bda8a04f531448d64143806f621a0b7ea653ebb21fd1296bf63551cb58412662de6bc64232558eab703222b9166339d28217a538498d8c55f451fd8d1af76ed151e5d86b2ff932ffe56c4c59b4e0610aa12840804debdf5af4577aec948eb8fa239b7f47fc6f92f4f9f5753b41d7a0a1d653f6c4625c01e609eb15af69727ed58b9a14b2cf2b0ef42e4cfc1312b79cc6a86359867f18b96bee4cca441a7ba31888c4c3ca6784cd22a999a54e6700f6238f43715d7c03c7c9345e2eda3e80047325f9ecb2c8e083dc48427a206966e39142e75cc1cc5a3263331e9ca51639ae5996b82cc04e58f19ea44f3245cccf0ed4bf25b31ba3103ad980316cb264fe8bcc08934ceac860f64eefba4d5969aa5c18c1d7948e8424d9199def6ab0d9a1152cc7d11fc038e70364293e7f79e80d20d7d2f823710d1549f325459250e8157debd11b931e82c9662e4fd850176a1b6be3b6c7d3bdd29ac108fd47ef07dbf639aa9fb076792061d28b6dd82aa88d417f2c7bfd63498e3a1a9818731ccbb2b967def42f8bdac8fdd6c19703c113ab6ce9e2e2236205166a3a77f4f01d4239b635650920adc89154b0d635a23c631fe10436de96ce08af2ca3065bfa1789a704e79d6c8cde9cd1a1923d65fa01e373bf629356c1448bb36329877ad885d023c5b4dcac57a10659764699d3e2f7c2c3b128553b6913ac4f039d903de3a342227c0212916db8893c904313ff3b5a92336f6b21242afb37af5c72bfe908db124646186a3fb597694b6930f12dff4a139fbeebd09a57638f42aa3d5c33258f8565a29fa0915866561aa8e2da28c3761ad2884407cef8e13bbf490ecd17d5fb1be6bb9366b8fcf79418540bb930dae190e431c90dc9d81a97ff9f5328331a95eef4b243f6efc1e13c8af4d453ac47c1cad32f35a98afbdb4f5303d54434b73a45991f9ea0be154d7fe48a9634fc5f5104fba3e8209aa723ade8f9ca96ca9c2bb4515f1128312160e910cb9249ee7b93b105d91834d27c25afd9ec80ca7d056b381a4309e6d30d258f92cf6059eefefa98d76324424840a08b839113193246ca59d296b6819e6765a3480dd053fa0bfa54a876a85bb1bbe512c7c105c9499d51b7c7bffb46680060d06dfc18a9dd4b735a6366b41a07c84124ea9415ba8dbfc6fd31553f857b0423e67d215f3b52a113bf8348f4c407199978f5ceb1a625dfbbd65e570d5485b6eaf5a58597b2103a2b9092ac094289327835c09a18acdb282964aad6e6e65be42ab39e767c9349b9fc89f96a4599b857c6ccb6f70d3b8f260017f8b9bc50ffaaaf705bec76082e2d52cf42ce46832193772ed946c0d51f87d935ec9517a2a1dd450760926bd0cf24167426b79cab2e456a44583a3a89de85bada3cb5f48f567504b26db24e66c26e00e01529e90cb19c29375688e776d1f7397e4739dbe1ca563ad4db46de9be29db682dc5068e5d8b1f1c944094d5fbfc52d08e9886a38b2983efb848a4ba6dd9e4ab1a3c6b5bbb713cc4bb07a21f13f378c9546447b40b9d5c3764295d85368a96637e3cab8bdab6fbe0f8bec7eda54bc205bc2d75953d535cf8a21d3b812a47eb8c76f51bfea362c45f6c87640d79d2bdbe392530fd0d22233d802a058b8040a129f1b7f229457e0ed2aee6a385afdf7cb0fe457e1d320bb0a90068c6b9f39cddd8cb0c7740ce68d61e094cd3358ed99fd5293a661d13250147381caf90ac58f5bb87ef6cbb67aab2e7156537593eab8f1db81a2d33f5d87d3d1046075ac7858f336c9e4f54b03065585d57298ea3d6a206b5db8de10eaf2ea6159684182c1bd447c407aa4024149f67dc69612c511fe0413d63cafbe347419a88e88922a901567780a0174a07ed577dd760886414ad95eab06d2f1b01adc55728ba22793c45c155e69bd6a40fdec8068107cb9e9e97518ffaec2545a5c3074c384f3bf7e14c80a691c69f272ff146ad861432a21a8cda43a420e1f26bd1357e29e1bd50630cda39d6c6d58ae035c4ad6120430a39c3d2d08b882463e094073fab43fb2905a94cf4a50e6eca31c4b0493857b59174a84afff3f07af2e9371857c2c53c523a93d180eb59cfc3e1b56f35a5ae0e377840be332dcf11f26041cd29ca979a20e84c27b2ed612a9d4bc3f00f5c4fdd6e8fd801bfa3c0ebf2f7e28c4f406a5f472558a880c2568dc266fe86f51c9b9e22e62df55e1b0ed003f5ab3533641e6f0d7842bd1b0776a6cdd7d63b21185e4f96ef84ccff8611ee89aca01daff09e941101c89de345514507acf9635494e88e928b08ee93bd14951b00416b84d0eb61b10c1d2f73087724c45d8cdb374b85bb35c2dc3fecd85a230860b1093419bfd64dbf293284571adfb27bf951c70ac945e9a5f6b54e7917e025f16b1204595ea3fedae44f196ee001af608028511be53e9f613e9c844dda4741ef94333cdbef91f0f16ba810baa132ae82c70597166c28dcfe3f0d9f3bc01f78f62ea20a0d6358d00e15e32783a88277903fd06f672cfbd3acecf5d7f29bab88ea85b3498e12827d984e489f360583fbdaaecfa6a6714aa7d21c8f23334f281ba678af6733400fe9d11e5e25d0d1ad1497716a3587a749076624f81178015bf47cf0106e6818b828ba65a2a287ab12398b605e17f4e97576db31c709d88e8a5585b6a1bb512e628e73731fa8d754677cf6ccd9e9950874337f880ad364826df075a46ee444ec74cea06b678132e273041644d8966a8f6368390a121b8528966239f2ec58e798a9fa2b0a8b5e04d9488fce37bce285e27b803237a5c45d382ffafbb3a0be967a2b126917e219cb090a06de48a5adfd84f623b681742e860197dabc1f004109972f51651c495bb32c0c4a9b93eb99a331cfe8cfdcd4df2eb753ba9c34715886ea2f07814b2a7e633e0bf1fd49e6585e0e07e68dfd1051ac0d928acc8a21dfc91331f8472792227d6d9102c800f8f1fa35dacf9cf3a57122b5ccfb86489c049f2c6d27908adbdc1d3aaa36f1d4157d311ff0003f7c2baa78e4f4864161eef5ca101c97eee9caebae37532cbc6b034f320ee1780e3c626cab2df674bc978ddeb801cf5232133ff18bd52044ccd9ff0912177a1cb610924c1ba15a2d31a35b428223c89d55a2227e2dbcc2afe5c4f835d8e658dd92403a8730d87b9f851d70f8a6668efb1aa091b3ae5b9cb75b99173432730ce5e2e6ed66c972bd3085200b8bcfc257aa77cc964f483df88c311b89b2d19606fa6dc3b7953ecfdca90afcb81924870dfba42edeabda6b7898530583e060c6f27164cb67b094a2e3f60838c84afd8e95a5af628822719b919b1f0eca7a68046ae16bc17940aba218da2401ef4ae03d0ccd018e2808d15a6b5a0718b24e6d4014e303f3826ced880eff2f206ebfbaabfaf8588c3c2e623f2f51bf1cea41e7e4c7a13974921a5f42b346b01759346773c6e704e313999fc3e198dabd2bfc8935e5307991f1253156dc2a2c9c516f3b0d8e8871bac88740b3f477356c2160cdfa29863121b5d6dcb917415dd70bf50691224a16809f09b104b0ffaa0e0652c28484274c198be2d0e36a948f873f36c03133caf074bab48916619934d17319b20367ace9ced6e2505ceee5aacaba9241ac2fa4d5a228ce9a7192fa2b7b2de3581f858b11ee84746dc640b9d31403ccbc34194c1d59d2360cd937da21bf0e4267503cdb2e8872c9554f209116b6c60c7d0b15f0e2c2f854088734cf646a874709f241d82ca9c0e6c1ec9d90be5d7cfba4761d64520719a94ead7fed8f17e085c0dd17115862d334067be0b9b8e4242daf953f16b8c9cf1108a940b2788d7138ce0e281b20a4353b231744df56a1cb8c97af32eece387a3340096528af1d160b6bc4d2a1354adc74e520ccf3cac1841c0662c122b12ae26ace04351ca9657dffd66dbc59bc0434e5e4ea63b1a0a49fc7bc3034b8c780fe46a50c7f8c90dd1794df8c3233b61750f470c31005a899268ac887146ad8c11e16df532592c70cab5459eb768ac26a80808ad4d0e3711c2acfdb21a80982b5ee83354b2846450aa51cf16c798327094c5ffa98a28266571992ee3929c06432fe51fa14ef291452e77c8c32e18de83a195d1cedcb151281454849890fb730ab7642ea139bcd3c9a329a11fc002aef36f923ecb0c664112d8a557dfda6eb513d98fb5b6e2254d1364a788197055c422ca8fc1fc917d038c170dd88d7a7bd2d9a1909e1dff0695f854635fc37cf9f03a066a6f8674b48ec80253b1b33eee4cdbc32e8cf5c7663b06942f0103495ef4181f79c00b83240b5d7fb635d31a19db5ad1533d78ad5eb7f5031e779ffbfd13b46aa49088b8004b6d7ae3a15b8644931ba55e535998fc83cc252b1d1ae62eb713e6f94c492a2d9fd6103a2b9d77e78fa7588ca6c6b4c91b26a3ec974954fcdbd4d362ec644b2bd68a7bd4ac95343f73d898f26ee3c1c358fa5bc574b24ba47094304be79cb4da72e345b8f33678a04129f6652e3971ff22047e77706ed21b38e44113a1f8a347582dbc64bc4dcdab25067118a38c438ed952a323a9f83a3c375d5c5d75544f0432c7d416fa6d0919403ca69756272566d0fe9052e013dbc2b5287add033ac768518eeeadcb7faf3ec4b9abeb7538e2b836f5530a2fe436a1314ba486c41bbebebc2fe6ac95e0f6d7e9ad1065c1793100f255eafbc923186230c96ba9f2afe0f8555935a54a542bc54c457755002bd6c969ae0006003a0f2b73782b98681d45941b3c5d1f512126f253e1a1a03f249e0a9b59d8c169ba8ce5caf66cb16c74fec5e74c46097c3baf86302acaf298b6434926e9d337b30e5247364e981e2250fc4d6f2a4e5af3a59ebc51bd6382e86f35831ad7ca7ba4b6a95c1cfe746325ab5569f2561fe53156622ebf91d08a3c377fb6425bd6b710613937c0436c4ec796cc1c23c032cb9fd4d8ceaf6122abc18169e34e098331872e81a7caddb273a6af965b56efa1bed756313a8f8ee8647f24909599c4a2196542a0f96685b840e6daf1848b4b1fd9393409d76229d402340a325f6b1ce00297cc4680b79fbdad7c8bbd341cae7c95b92c1024ec2eb6f4976f669099f0a4177828589554a1e6dc8d9f47b9164aff79661a1e11fdd1e310d2933b224bb08ff5a86250b04c7f6653a0ab6ac9b1e93b9fea3ed7b3ac9c76112fc240ca3b78319fc140efdf24770d3f4306d6a33b1a49bba76db61c478be52fa4cf948fc51a6a587b9a27288a5ce44d857c3f047db005c13235bb79019d1fc894db43914083c9c90a2cbe950491fc22a5fee2805ca40da169e83fc1e6b82f5e459abb1dd35e1d6600c3b4d00f973c77325b9405b270ff2f55ebf047aacd26df7c2139698ec5bebb31c9037c183f299d70e35e8340072f4256dfa56d93a14b9c851f40e9b94b89790533a4a808beae3c3b83d08d105656073183c34f863b8f48768bebe057e55cb5be67a04f0e2d7ea5a99725de18ccb1477a74fc87ccba2915a16a593d49cc52687254b5ada3b15f61f9cb721bf1cf4bad678c2cc2b343893f9f0cdfb6566b0a14623f601185b809f957855b7a6f7a6a6544aa9456d3e85da043b47521f0b60e7505b0c335a73b5fe20d9301c668a083dcc442d65dedcd77386ef481be2c844d4ae8cda706627c9598a6854c3c77a6e04647e320dd7e887a1b8466f188c363fc31f9fc94170fe55b210d6b80741c1fc746c954dd99d836399066470e0fd938428a7e1780e1f4be40426d1f529d7626584d6934be930bb74393fe6801716ea34919cd327c5849e2da71ee143b1b134cfbf12ba1cb60ad5a49a9d80e72c4de474537bb21d27e0612532e872e90de18a426b08ead20b64bb0086325754a02e74b95cb9a45dba963f10b630d5a15a6e4c09581100aa30b33093c4098fa8214362fb676e06ea4cff1310b4d694b05b760519e5570fb1e522ff088cd2aa1cb3731797f6df1503a4bc0a7857f9086747f6de797278dc203b5f463c6b8133217dde9d1fa50e6cb98ae2b6902e727512fa332c0735b6dcefb3e6144c3756e2c69835c79b29fc05a15140271d86d973cbbd5005eaee72d9724048ca0b05bf6839488fa1e5002d715a943d2820a65f1a58e2352b55fb99f9b5f4e492a12e4c3b0ba06648350b0f240e0ac455abecb750644ec9064f7ce55073192d2ece691d217c3507705d6c7c61524719e9ca3222f1b8475912e4ee159ceb3b798abe032330945a539458135c6ab713e67c7184f0260702683d2a8e4c0b13ec223d61b9bd01823603a25724e2d174eb60681b8bde11de521de241c8dff470f782bf55abf5128ee0d3902cb002c74c5cae10e76271d6d81fd0475ecf48e551628ac9485143393589e85878c1374c6f24e2247285330b599a56670381222b54e57d3c1f38eb00e8dbe5e7046f808477978bf0ada4a7bb7be62b1c9aae02bb7934731c3be8c964805f3287b46a2ee216e66d914b916b130d063616a6eb1fa44d004bdabbe70860859c9675f89fd5978a6a7d601a8a0c6c1fae93419903aa3839e24ed43ecd44a80df420364e7ebe333a295f95328ae6f9a3fcd908345ad595069bd72be9058b281f33e45bf519c5f1714fd39b82e473a66ae4098982c6b7cbbfb7b30d949e94624affc426e449b924d16cb8827a640bc76e18b170fa8cb6548eba488d5fcd4de90ebf8437427ecb2906e65b061d303a29e66dc3fb44902e69ab1836a86207e35841309926d3dcf3a3e6b5908c1f1521f4fb9c34bcdc203d796a8b0be8a385b92addd0a36b247beeb210c0eb8ebc2f48770fd299055864fcc5fed0f37d0b0c8992b6c29d2c3af021779503ed1aa89ed2c3095c43e2860e50491439c463b290a7138035f51df94be4576dfed226544362c6aba12f8b37837952208bb260300e83525abbcba683344af6f7c164c19a472ebe85f68c54264fa30114a9f12395e88ef9966dd6f0ab5d1d3bfea1e8f8e36196769a885f841a27980dd7f47c0ccadf58434f82ca748728cb91a525f466290c34993462d9a71e7a47f0e5bc3b2303be19588d96e1b89b8ce24a114da4334ece8466647034c093855ac8539f141cfd72e89a0de523cd72f2cc65bc1ce9997dcb7291c4424ac7c4e6c2e1a5982d36c6279fc3cf437dd149b5971db863aa414fcd673c84b159828a1e9fc66412c90a70036c181425d6697b84515b11df40700f9d857f1026f711c71ee06e94e86c476ce1cc6cd1b81cb8c69ff05a416933e06d6d3cd18761cee8060dac5cbdde9d9f067b4d622f39e8d3f4c221f343f8c1ecaf2c6d5b9287ce4dd85e43a99148aadbcba7ad20fcbca2908c27a0742b821aa2fa84ba5df63795b17ab7a7598b87fb9b728bac19576f6d9a940b44b964924d82e765fa33ad33924e0db10393f5c19985a36f3c0faa42717072f9e2d9a34e048004167c9dd0ed4f679cc12158d91466e08be8b694994c934acd82e7d452a9cc2b4408b084341a74443863cb6713d2d378627414eeac776fbb3cd793d76ab71abfefdfca86eda324276ce779afd6bb02eb8a6a8f232c0452273f73bad5729877460ebdf9a663ccc4f27e7f559375d252d37a0519b1fff036b77863480d83f0a2b529506d045422c94951b18b221a269be1d92846dc607d0da70589fcbe88f9785c6e60c62f37a940f889d7875266054b07a596cab30915957ea5f201e44ac5595279d2cc7e1565572481f395b24b2bb880e722e3d738a166a4f213a06ba7dd128e1580a5893eab2614b3fbf6d6db963071358cd20bb2547aadf04537d228fc6d1a72baaf4ec38c905c04a770f0b236433df01584455731321862ca75d60ee4b5fd7f5ac9ed4380ac7d113bfda8f65de0d5918109bcf810a3b401340879b4f4e9571b2f5c1c9fb1a71fec99ee75747f6b6efe9a9460c001ecdd2a5ea819ac85a6e5dd8886937de53d55d81ee982bb5d682b3c0e921d8ddc94d7122499950407d112d03d8c9310e6cb3f58e7d31a90e47a6d7a729fbc425871ab26da749446f36b70c8838088a34c3dbf02748996daf5b45adcf85e57a88049c85b9e71f2147ca5dc3a8d2e6a1f84b68bea956fa149ecbd97b822d50913f41f6902d4c58823d9e3ffd03a911d04ea493690bd0d4065028fddd9c3088edd487b5887f8588cbf8b70d88a160cb90aa62536b6334c5dc5001b3e4485dfd4b8de407b1537299bcf6c36492d7b03762e3122d59d2fa0a610e8d4067a0bcb816417b52b34ef41807b65fbe7545ad51f11a82b4a395f498bd87b90915b19d7e1ecba217de937d3f68f85b0e23ca03438a623811b25505499286c3d6bce49b3bb4fc701d5f45d9cbe229b17ec6b901214eea6c5bc2b28e8695a111e9c8e25360b9dd536b29193ee9683ec8101c3d02276b85d7bedc25022a0ee8a5feebe209e568e868b66ab75febe44352b82e8b6155517c89c4442f265a25c38466a0fcba093ca29c4779b4a8886083a39ce39c6d6c836476ca3d497a43c7fc9621e5eee377f5b295b9aa40c0592f460388ba129eb18683a861b447f8085f42df80a4e58f068ed7df32af9078671d953e839581a456cca4d034aa923db9bd42b0723c46198a237c0d18cd6a6481e08edab6fa438b4743cb2474486e4b8035ad6fcc4530c03068da1c04d62c472b823f2f11d7a5e6007c84b2a93c9699e6696c29ae5d0a4937b17d0f4344053f481183087c2bab8fdc5b87fa62873cd53811d911e598ef194408376707b8946030aaf60689f790ee014b8de67dee1ddccfd149e469a9644a4a996b2f8a7a8d8ac4437209bc8df37ecf054c9fdcf2eb5c7cf87d2e477a9a540286e6d821e898f2ff22d5bf63bdf879db340f9dab9ddcf984a0d53f1bdae520ce76cbd19d5efcdf1ed36dfc1259aa6c202494bc4e02f60db1e8cac542162ad04efc1b77b4db45a73604514e3519e65224ac4ca4fcc4156680bc0a7804ba455c8f673070a4c3fe00b02d5ebf47598a635a18099c18da48e3c5fea6421b6711e9d211b437ac2ad60ada11d3968a7e729f6850e60a06c13cf2db1d17cbb4ffe5905a41f877bf454a24909238447cb2ff6f60497aec953c884b35fa82c4496896c765fecfce8a2e94f8d4fa7160d356a1538a0d87e80538f9f0185c38342c8e0b5a7553432ef4185b06dbde434887f6b2f9dee6d0e8bc59a458d226ed979d86a342b2b331133b72d4e89dcc152da2a6952ca3f67ca0b678d34b77dfca91654d5b45e3177b9f2888c8760ad99e99bf2a21a05dec8a82e574b1cb8aec136f8d9c58e1d921cbed385fd5ca04cd98b20440415916e30f2d38c211a2d4bb96e82191f7085c495f65fc599f30628ed0426078bb8fc2a81eb485213d6b9b8e3ef5ebe8d3dd2d2575a11597e50e26f7d32a4c50a9fc762a95f55d07b189f4039af12e4f3afb5d1bf642568007b70d96e9e93433b306dbf826f8340fe257444366729197fca98c1422e64943d46fa4f36db1e819c3096b59e9b24ac4e0e4940943f6599fabeefebdc6d14eb9165edb05f330ac5b6c0e27d17fa953c96a2fe53908b8d88c4cf76a18a8fac1641b9172768f611a7d3c904c7d0dd0a0f7f732dcf78983d1f7debefca4b0695f1c3955a3ea6bd61561a01286eaa89249ce4329ddcdb6bc11835d5b09b4072d14d2b1f75addb4a908b39fc3ba179c6c23d89688d4a374e8fc4a44bd846400cd0e57fe0ac52c3da024e82b94c3cdb084685e028256418cd8d57430617c9c14884bf877dd871c74ae9498bcbfb9173d38722f84602a18575e2f4185f5bdf44c44ae305019471ea44061d52c08564f9259d3d73d328a636ebe6132ccb7e8d6b6841c1aa00c93f177c4a270b52e3579bacb8c37d4badaaabfaa7758afdc594a2d09cdc02f66d89f03c780e3a001b8847aaa2831d97c6cffdefaff8eecde42659dce336870af544a12a0b1d58af12e5886ea048a275e4a25cc01c98d8b66f2fa2b5c774aa18528259788466897d323c54ad109d97f05763c8e2e1c2ccb7bf7fc0ce200cf596c6b8e28ba55666e0eab2e3fdfb6d5ad00dbd1ea6773fc56cea76d806a02ee0d8cdb632039137e56347d69e18b46715a5f4809f6200ebeeb8dff6f6838acfeb260657f58ecc0e0c5e6a6884bba4126c68485ba31f37343ac04e89e863c893f129f3e92a14acb844976f6175c01472b24c56d270116c7a72306c070b99b18822656006d40253b1a7428fc5b990b717c568bd05cc7b11f1c13b9c2907ce224bd32ec89aa9c891999dd2526d9addf0734c78465186b42accea6778b5f06dbc507b27886c515172a13e991a915e7c7f95d181b30d20a864f7b7c3d8614c6dda8e1475be097025355c8a49d671d9b46723200eb0a9658b803676ffd2d575841afd2336ebfc1f53ef211d4043bcb34fe88c06aa0c446706b8b3b2283671eb71f9568e39e1215ff4372088d534564fd3322fd25554710dc387b47f44bc4fab8e1b6c707e9980d9ff268ea72f0e2afb2f0bb70090cd1b0c94b3d61031d64e7d23110b6a846b26cec0b5560eba1d02c03e54320f19aecf6aac4aac952585b8601800acb40271a67fe84c7c317b3214d8ab36dc2728a8135a780278e48de7475467bb39e9b7343ec8f8d08ae9532e8800400c6089778d57e0a12a7cacd0a7dc5b5f880e7854073ff2109118c9dc04445ac2f0e7ff78b4a559008c6ee1b841a88c8d906b00ee5acf97466ec723495241af1ecaa9fe5b87a7427bb9356bf05137f80345092d3fc339b623a826e6d2f28213c83dd1b95032fe22bc7f2687b2c88380ac3ec67ef491b7dc6013b1ed877371b72028ab0a506ee4d36250438335c1f7f3cef52435f7f7ec7495431a1644f5863e4d1a4250d60aa2b5536cfaf7de04c51abd4de05ab98d7869d64df4242174ccda7d22ac7322379e4d19305697244e5124495030241406573cd425cc4ee9bd4d57c033f277ffaae35fa1096f3d96e4eaf36dd302bbcb80f64ea5c4a7978dcb8de06b1824c9cef5a7c72d741f6609c0c572b2a65953f24f71cd42bfc7d29a019767b7b5885e1485bc31a8ae30148020032cf944c79cfd3b100d76ff7c99ad6641b161f3432ca1d70c5db91064f52f86f977e71b5b5f489167e5ec635081fe9584180a5dd21855e35b95e53ad4f8d6b372ab4a9a39a6abfa482a2d19256d5f63b837c4f3188bad3785f4514d00a699850f37fcf53708d2a0c06915286d87adf1f5bf8d355063c7d605860e73e537f15f4806dc6bc08d1093b77a6025beb459624ed64e539ed31e95e8ca5ca440c5c5193455a43012b7885cad713249cb47ce2846c0283a0c490e6880c57beaeecdd5103e99851790f4011afa5f325e235877a5a0bfa96d38f535810f688019782d41a36f358e4265466a0468721eecd40f8193352c4f6aa20432475984255634430b6663022534997844970e1ef01bc69f4f63f2814369d2f745cd6dbb6089657f6eec3d081850cd36827b73b8fc911951ad2b761d81dad57462aedb1102365ad864f5b7559b924e0c086b05d425cf2bf8d36d99cb56add289899f8a7153e27c6a266128fbb88abbb8ee24c4f8f455645177cb1c9fafb5a549f95cfe69970c3f9771e732adce75ec10b16acb8b716e0f22481889b92b846525942a72ba408a11c751b59aa81c7504123e09a4162c554dfc0911ba01c62e4434b542c78fbc35f576520ec9d2115e4e4d5a7369eb72326276a29a5856c96ce2a86635025b70f0ae0d84f92d5a8442bd4d8c797b71d9f2df80ec33f2f8f36823d38a39bf37b77ad434e09704aab409291dfda2a8c01775820ab32aa38020b453c68d7bf2b9dca0401d51182d56bae2b78bfb411ec771ef526ed0323de1777690c47c9a20566f74d9e86f03b33a4613e80c45f1e0c341fa127e0e68d36d3a2846c5b4e944a2da195f784658acea1e9134be42dbea2206f4555bf142020aa876930b272764283095da7fe21ff07c27489516d9dd280c12ab7184c3c55df743977c669580e6703b0f98bf269730b4036ffd27bb32df59e6c8f996f539c046f880a99f85b2602917da7059fe718875de643ffce75a9400f7363574b9cbd3223d7d718cb7d2ec3f3e9fd3582dc4180ea186ede1831c07fe295a4c5288239fbae18ed1d11deab78d40c1217e6bb663fb742dbfd620eb36950a025045a1296f1c8cc58d69f6e99aa882101be12048017edc87860a4a9e53dfab05ab783ae6ea58b237d364f0e1e9c5871484c94ec394d6cbf4da1d54ddf72c08ddfb9f4f0ff393c5ad830534777bb1d4f4cde55f3659a8837f459e5d729ae3184105352b3e69d8fe44131ce64e0cec3808685f7ac4fcd40684646025b97caefd5464f295e8f4e682a74202773e3b15dc9c2cd7eee918a94fc7c454ec9b789291769d0d32ead9070df8284ecf81f0cf91b56f2e79871bb100839c550e1cae97a346dd6c62afdf4edd7e810bd910e48f717c159f6765a5bd2f3d601cf63427009544f0fa6abed4c316d45f10e50afdd501d33afa9e105a6e4a01e5d6c9dcdf274e015b7f9f008a259974f18b9e4d54aab50c16ad33831590e00c96f9d6af6e3c016f14a652b44a327787dbe7d3ac986e84da957cb4aff994b940a922e81ba2eac0b8f4be962e3ba8d554dff58e8b897ec40d0c8f4a680cb11a228bf3105e32617b096bf67b319b64cac1d4c2f74445371329a21c6d403f65529de61419a800c9b0247fcd3cd8f96dbbdcafd730657e651519359cc2bb5b08d98f819158ca983629e2239cc82d13e46631162cfbe46262b1c7dada16f2c36d9b0c520ba9059e92a496226f9736c94c39d266c98530f51b2ae9cc0f3a633011e2d730a6a22563b272306dc398d5e52ac580310883bb690595d3957542b4ed3a711dd780585f5982df60fa42d3aece90db5fadd375f12e988d65ee80b90083614da7298a8bc39ff1d9d918a65f16ddfe890ee74db420f8964ac240c120e59d942b0eff6f9271b56b23cba943efd4d439fc41da041294b8f263f3ecfc14298cce3b6b22fc6365067143721287443c2b33ecc42ac551b30a3f5f00a75410a94698a6f9a87829e8de2d9c6b2564e118ada4c223d8f44b0cbf3f934942d02a4d4d0511a99c530618cf69b5c9359f826644addd9270d0675e06fc5d1ec8bc2e480762caf181a47ab0dc13a95462d5cfb93c3150bce59c9231b7ed5034e647ef9d4b21b960d0d6a898bc528551ce0fc3a027ffbc48bb6de3167f2a0a4e9b34498774b8f03c032f46591f1e5b8e8900d1bc41b404b1b756afce3c3136dfda82d4cc1a5cd7b319a2b314c33790d4b9ac47c22083743da1428b803910891909bc200536fc5ce2b45df325e0facb0864356634716975bbaa784408a06219900948d87d54e7208ae128e29792e4ea8fd560097330308a7e8ef08225629fd84ca6f0a92e96c2178e5184aba21dcf7669ea49a2722e93145ea34e90816ca4306fbc759641a882854e50d0a4173a06cb1d40bbb4af82e278933a5030d220bbb944100b3824a05507ad6dae71861c8d38cd42d68c62c7ec37be50611861950bf32661865fba32df5c787b951a86f8d775ca81cf594534503a4d36b76e495251a8855801b0d734579b0f8bf4725a16508503bd16f46afbbfd8a31fad25ee017ce43b32651569f5b26d3bd9e4db1354910b8cf51e40040709681115b9a05d8c582ada63aa48b8ad53f186f716852eddd94ca08a40aaceefb10a4515d9930277a5f10a19cadad1eb3e9f5930cbcb2879ce29c67a6e08fffc77968e454646ceac21dfa2704590027552259af8edfbf5c4bba74853c5abea7f7b512872d2c338a0bed9bd85de5a6b982f51d10903e204afdd090ac23e4391db273bfa630750ce748308c5428873b92858a481e771b90ace55c5de1c6ecd36def60e73e43491c9f4c787889a619d90a82298e3b0969a88a03ac0cedc5344acbfae1d3d1b2313084a2da8f87ba1d6f82dc2de50c66cb43c0291c5c8cc0e7ec135534e3e26ed040915622d22c39f7dcc0a26f137b6daf65acb96aa95ff2b50a6c7ffe7f2ab0954ed6e923f1809f872857f44e54c264ed7732d28376d9d4a1a055007fa10fb923b28d259eafc47a89e9756ed4357708067e0f6de7c969dd6200818def3bf5c421dab786992512a3939f467494737d48d0c0d9c79a78b7af3c2252418475ee41573d6fc47c9a1801ec50ca36862b1fe984b313fc083b0d0f9be92476e760416115bbd4bd87da197e1cd83874cda87c0797136c5e0a578e55d7e71d8aab97ffaf2286a834db791270e217c0d9fd5f706db36e8f24d481e821f17ef33dd99797e3674fa560b28a1f38af89d1312020f17f146a36c1d23dad75c23992c613a9124d3e5545cfbd3051b91e55db54b1b40bd43a3f8fd9493dd1e3d5f6e48ed5a29bf429a614671f9bdf410479363bf5e4a7ffeb86d3e013628fcf8baba271c94e1bf6e1f16da3d772b39d75304b746201a950052c08f9e7d04828e86847d186aa1335601ad0204e836bd51adb0345e22508484b6cd3cdc9c0950916e318c33b0e74e7f0de57678c8e712b1c49ba01e7238a15686ecc70963a38636c5631ec7df0b59b85d593f2817ecd8a432bf6cc6caf9a9729d7ec93af35ea781b68ae94b550133c35b7da0c8e0f8f4b0b2ea4eea7dba7d39568f2acbf999916ef77f3d9de4ed73c38969bd3675a10c04c9c8902210671f88711973785f216f81b6be9043ca3144faa6767232edb5f6bfa7963ca3d1c9f3d5d81b28ae4d5cef5e22275433f5b4ad917a7f211bd0cb08b0ca7663a2153329423139edd4443597871300b00019ab45225fcc210cff1104660cf48339124969758af975e8a5e7cda4d37e4603270c6985d5facb53334a215b520a61802926db529552da0df2c0a1d2463628b36857e1e1d94813e212d98c984013b66246fff8bb094a59fe4bab6ae159bdc900ad4268d2cc7465d74fa49cbe73cf8261180d1c5b71f0ed938ae74eeba71cdc19a4a5dc0028ec16553fba0bedba34897f73a25aac2a8b7c79fe2a7e287395812d434c3bdbab6b0a81441d6495ff76dfbb55319af7c93cb4b6e9257b095d2c008844dacb0825f6022fde172b29b9cafb5e2b7e622c789d22eb73eb78461043a173e2b933e28fa50ab224578da1dbd6fba8d219dcb0fc7d15a2c1acefdc32a07dae30f0177b15265ec84bf153da268cc814640642cf9224341849e9cdae095a95328ca43743242924f9c1c61e924c9b38e117ad34c2d78e90c5fef223119947ffae2460a98ffda68add68482ef2a462689a320894dc1ca21505b993ede407aa849cda1d073298c966dcbfd935170382c4d38eb7a72f533645919c6a71a48628a91a831da57ec9c9b76225a00a47dd99ff7b93046e2f6240cddbba497e997091b103c0e144aa447641422b238adde70758c94311ed098c55ea92b407569cb365a8563e40474d7fa959dab10cf06a34392bab28821b43e00614fed85356c3a1aaa55bcf61260f3bb43e93d97394845a885e55a0be27988c2c48ae0737fd175fb38598c3463c62018ea43c5272098b73249ef7c4befe1448e86384c893accd48514f76da6e391d2a91e7cf669eeed8971b22cf808d0e20213299cff0f6f5860071ef14a431ca3c91dcbd96acf168933efaceee2b411c3a05215e3f23725e70a63ca02945f908726579ca97a85d468d75945e883d9106a53dd93265564431b5747abe0e48d0cf96782189dd99e06ae73e4fd26982f595c6ecb4dbf3507304c1a5511a345be9049f10fe5390c22dd52d2aacef0235a87d1c5cd7a8da2825b12d2e9bc54b91b6dd1857f9c7963d8b58f6c18e05c073a3224ca1d117885691f9458d3f292e54dc6fbe542aa982b9746f1788e8e0a318e7b0e24c458a7d2389646df1fa468e8a4f0c275e5949e5535237c57a8ba7699316ceb1d407f199ac0893679398916ef827ee280f3b6e8c027bc8029ae130d35de8f4ea640802cbb44baf51c8c9ad262720902dd02424ff5abf0ae738b83fc0df9eb46d4e17ea60b3b887a273aab899a30a0827ca0f79e0b8bc65829edfe9119cc504a7c2a691d102e74efc41000a039c7ffd13d6267262f82b4c85cd8986f274dee642e56e0009e97e887ca37137e0213be7c5c6d16f771ecd40f15f69e95ecfa5234cfee5a426aae4cacba826f1fd7bff17633cd826251768fb6b06f0dd44f50c599497a60015dee1f1ba9c3c16d5e3b7cdbed23ad6d7bac65004cf4f9613977563f6aca206135003781de7617aa53590432a14a841c10fa17d3b7009dd31b0150058d569b0d735430ca428c4a00e98d7fcda98d9afad85c07966b9d0374b0c38453bbc5c36c331a3337a0bbf23730783a31f860fd166eeec27db2eb80834f5b84625e345207427fc3d84633141c661b4489054fe0969d61408c6162f6f513c8160516b1aea89ed26516407d5605e6ecf9d77044c1da1e63ba72d49aa19b49f1e4844c70e0e212ff9467ea48b4a5d0760521d03af1081b83fb61ff05c664034c70e4a230ef8236b00ae89c80e62a7051c911d088c2abd88ec8033522a68fb87db47dbb1d03ea16cf1c1c9273d99ee14c80ed27da91d1d341b0c78d00e1e36723b956a09d4ee7f2eaf586707aa9b7e44ba615d7763ddc85fb3dea804fda11ffe45b0703aae2efd40b27e5fd01f7096f3dd9575d2325397c3e0e09bfc5308ac41b21ed448b10a32823f2614ec147dd003f97857224a761bfd7c87fc01271dbefd6b8b63831746132883b6c432b878881d462f8c64b907fe23e234ae8611747aac8dbed0dad83849d648f99cd6b6ea21ce5483bfa8c6a4504021b72de308264694001579647cb42e6a66a07277a56b8ee3a4c50e6cd4a65961d14a67a7b84fe0c7313e5fa2c4c43d738380d8ae5e23938b361a3eeb4d770fe657e9a198e0c4a623c17e337d63e9961024365682ca5634ae910e8e0d2d495300ef162c670b78368f3027a87398e2d36f36948c8313008f4e062b85f7bdc560c9a66e1c91ca9de004f7b1cb38ac571f9a81c587204b47bda16e60b7d644c7fee79f66f2eab53edcd93b3bcfeae6db6dbbb63c21704fb048c8723129d2c40539d862e6f37db337df49d1412695eaf3ed515111b2a4aa0fec3dd429d127b449eb60a25a9100f1466ddf30d9c76611a89e3672b56fffc605839890635541cfa5362da2ea532a34eba9b10c9629fa14ef1bc0be4dada92d00906f811ae3028297e1fd156f84fe4f0303bc77217be53897629b1142cd96c9601f57e6725472c0b3716c2110e1b20824cdce5ec02640fe2bedafaad274dad4bf6aa8d662b45ac47cacd2d74ba53d8bfcf92957966010e825a1826df349107d95feec3cff04a335d96639134e7dba1606dde82c2ae0dd9b81d1651c37ff369601587a6c53fd5cc8c38adadfa2ba43c934c129e653658372f6ddb0df3e33544caf26268642839cec97119ea1bf82efad609ffc42b8dd46d474136a7986eae95973f7acc1acb8195a70d0b3596d931c24857839abe40c452e390bcf33e975a4e4255d487f5f173e56bc05db85fb98a7c8069d4bae97a950ad51fa6cdd788a20e4d2afa788191c2303977955ba1939e5c8594670690a369f956d10cd266b1409f0ef1b30592a01fa2a6b777e25149d24de86446102c11584e61e26f9d37b09f735eeaf68607831bbb16075211d896c5d27cdba5f99482edd822f44042ae570fa249e19f50563e056e41cb4f019935c04ce1f478ad260569b721afe497856c61af92923c2edf16028cf65477fce56724b13ffc3d30991e89b29f101831d715e00ef4a304bcfa1a5277a94de16cddf5eae3c5fd9ff562312777e04f4ed4ea5243179fe5f44fa3e64f26df2d1df59d4f73bf14b82f2cde5bb9c5109faf9eef0893137224a2066a6f9aea3358f664f383d1b8b8cf3856f80334d3958283f2d7bd74902b28f1c888b74948448ca1be95efef9a2ad0a18c57fc9759c6fb907c41c89d2672f12fc26b90b376562288e5549e95aa1ca447a245d842f19fcd5197fc771bef00cdb6c4a56854b006f6448cddc84be01382325e85930ceb74c852ffe206421747bc45ec856dee7e67c952a78c255b9e6a0f2bf3248e97935a65cabbeda869884bc533a94c7dfad72406aa24c37c80c23419fee28364b0837c7d2156c3a50b051e2d1d49cef66b9d7152509b126bbc7d7801b27c515311175deaa8384a0ec2e3e715aaaac39ed95d7de7ad128ff1273bee0d7cb3fcee70f3819dbdaa1339b41e995efaeeb5a6a65e7015e6ba1becf7bdff67973b606b5c936b6245d26d0af47fc746b53a9b4fb3b30ee36fe48970febe382db3d734ece3f7d43d0c59299686871cec6cf7e09401317d21087284f384c9d9ce30092db0c5fff2bed078e0343c35b3011fd138de43dd35567822f7a422476a558a381c231bed794f72028b43eafe664ae5394d4db411e5203c5da6a32658a444b4fc0ef4f3c6f661e73f1cb09048b1557c8fe6fd5885c8b7122b8ab494329495597b1e504df7e399e50ab21de2ff8e0f6b6c60d792816273432c029b431e8b17a111a7a3c1e759903671cb8e1ab0d924d294be502495e23b5efc941ca720e1e0bbf2f56352ef8262f0a62f6ed87d4629e4fe09893b676221de295afca56bc3247eef64e262c9377cceafec2ca688dd873c4be3db2abdc246a28c7cba2de78675d7361d76c840a651eecc0a2db9ab4e2244c53ed942c3e2a3ac6910a710b39cddca4ea2e5c52b2bc660c2568a3c7583060b5a38bf4c7f3ea190ba8dbfd33db917b13c1bb45217b54019f53085be7e1de821d500a90c64173c43782a49adf5f5181c80dbb7f41c1f9c9e13468140635d8cad9f0486bd0a10b1ff21bbdae377caa8d3e764b8b2439b38708578e672382de17b17a20b7a7fbae90e62080a143073a48098fe0e26727a84741ecfc5bb473b59c71fdb84f3caf7b6d1ccd13c68f599f8fe40efe26871f641fd3c2ebda29736131809387d833420d65c2360541096242876668f6344351d469ddd307640f6d2f58d48586c90cfc69340d7758d3ce9a0d9f2775e0835a6781b6806871a5ab7151f2190168919d21b95ef3ae8bdfb28251d50d6650cb079bc6e248c427e34a7366e71cf048584a90a9f7d185180076d3325f4f2c30c1be23a48385e12c2b3af8196e47a183e085eb1f02237317a52f875576d062d2f0e9b0603350312819bb6bf690ceb789319728b7c045ceac783437796f38b75bda27b9a3d581a34c3c5d6f41b982b017a991b3147c019656e02af822f1b05843ba721b4ab531ac04c383c3385c0a76160c84901a579db7bf4604a20da31ba8ef88886a123fdcf95048fc3a184234c59a17da25ede799bfaf9fa3b7e6687dcd87048d5e92ff39a229655c0d55aac3bcc07fa3684e2a357854d0e4bb529d7af08128fab5def92e858b01e2dfe27552b3cfd03c25cff80c77490782cb0d657141537ce0da170c5315a42671bad21b472d2c6ecebf0deca0596b2af5111695b398143c4c717b1bebb7ecdffdb65c62a81d5af918cf6f0d58cbd655da5c82effdb54960ff63364658aace409f922f2671e66358953347a58481570d3c0edd9a9e0a52174873424555dc29ddf8b9a5594e1751004d016ca8570be745feb7ec6b0a47cb2cc79e63af02d06865af1ffe14576f562e536f7437cb70010b23ffe718649584d0507b4f63714be9047529e808aafb75de0441980c5035a980530c3e38886fe823c110e0b10f54a8cccbae674f1126abe51666a91d97814a6970a32d39ad5f76d83a3319d1865bd14fa2fae908edff19564b2f6d5632d9de0e7c0cd10471a0196629a5d61e99b09c4664f07a8320faba24164460e1fb4e263a4d2c8cc356f51a08189f95e2589db59ce086e884e30f5dd3e3902ef04d632fc530cc4479f8f166a3b8d6634c17c6684b2b1ee3a6c5b7eea07082fa4c2199d5279de8567161f48145f7de3af54e94f47ebf4c066ad4a57abcac1730896e23cad79aa8b5f8b57e4aca258fe438d6495649024f0e308b376744b5c662a6d1ee95c2bfdfff2197d486988608a2aba9efd1733342e704ec0c42c80ca78f4b6a4572106ead8947bcc8c01735d465d4c4b77ed1974cff9f3e28a04137b0144b52a7efbcd81192855b8c5f3b9ad29f195de871c4926d4a1aea5c176ebb4abc54bcbaf0563bdcc26bba15a58f9adf699d4e5d53c500d6f55aaff687dc70b78e0e3f762e240385cdd1521f13bf5c2d2ebcce37baf8b5118e905d15180be43f3d831c7c91ba7674ead6eaef5a18be999e09bd51f94156683f40afe48e87b1d1a77b02247268374ceef84bd1cbb8313c75d3819655e66fc6f8d522b94a518cf068e4c9f76d72ecd5910c8354f7d71bb194007723d16be13bf81c37961c4f67cfe9b853ca7d350806a6f1c758c47573fac9da52d46d6675e361f698d66588260358a4d918f59edc01488f30e8ac6ed7b5af8951aaa062771fd4dea41deca000c2eade0a404df79f19a2427905b1cde9fc72b0ada361dd9147a168e2fa1aa46304affa74a4e6cb3a68d3b9db3d248189a350a6735677869ca4783bd1ac7663545ce792b1dca963bca87db8578b52405837412154a84929027719f8f84c62a2933f452f0ea85131a970369b24df26211f74cbcf37f263ac32a22e128d80981151c435e4a24bda86b105873e3d8e660a8cc23e1698e8a146a5005f2054e9db911faa763fcbbc0205c86ddd23e47e91a76895bdf3f63192ce712ecf12c6d714cba02ad6b7c3810066ceac7da83c1ca30b34386e61e18797161fc774163e909b6252152b557bf6bc036f319dd7537f88b4358a241754aade0d866bf689c6749151dd9ebef34216107da9ae61a0ddeca5d3eba7ae18e4c9653d14865a645973e5546bb530b430039486bf4b420373355953677a5f0500acc856c816a874d831f3ed858e9c1e7d3e2a00f06b475f363a053e6b846945b6b53e8f0de71eb37a10d49252acf174a529d4298db602619e057e041044331ad64caadc7b737898a93849bcd1ddd96e9f81bc6d207dca8e6f1f187a0437101c8d9175d6ad37057a3fe6a7346500126370d06b8c1ec92aa651ea3b7cd17ec37912f542132d448c3024f53e8d6d47ea321e98914b0a7c394be700125276e155e7256f3c532c0fd5d1a10ac14401137a776be45ab08127d4b805f9c83506636f4bd64732f9875ff0bf5a69e0fd42935c062cbbb816107e9ec3ec6bd5688e4a9b414ce28fb04b31f0be7b5fc48b80d194ed333673a6b9a938a8260dbda2567726e1664fdf581fa5c24a0ac561909f5f2f03d204133b2a38548f56e1d750368dbd4dc1cf3dbe246384de6122e22e2e022202fc7b060ab5494bdd6a67f48383b21941f18d376fb4099998f7139dd6aa271573919b47466c16d0cad8580d00cbfbe55fcbe13a0efef82ecf1bec2592070cd366d9f721db3d9b48315ec9205aa262e35f2868c4e33b21a0f1dd016ca436160d6690116086294f17ac4c9e3f402dc04c940d509be40d54e10893d8e96a77eccd6ffe7a6b268d4a1a22a34d1d939fd67f525cb21b8d4c60a09a22470112fedfcaf8deed7e963597e885c8b099843db0a8bcb386dda43c9f9961da5e68b1582d985830e6bb5fc285a07c92c27c4a4c537983cf5d6e10c1dec23d657b5d612ef17650efa05711ef6c6f124bf152e856edd78b2a0f1f0a43c88f71e1f15f40102d9bde3a49a798fc85eb214e8a0f72cbd3b256d3b5180a832cd500359f97edc354cdb90466611ac6418ecc6e40c4f40df5eb56f5052e000346f7505e3fce4d5d18ee27e30114a96b17d02b1dac569fb06f4cb33b85f8352438b93d9d4116ac376a3d92d93ea66c69c5440945f74a43ee38cbc3a5c2dc35f2632d6ec78ef6479916d47f768b0fdf3715e402be01bb07b85e62bb1d8b466868a89e6a83a8f1bfaa8a2775453530e4122c9f16df2ef24f77b7a986ec2975ddda3678406e06d0ba7c525d6610e773c83d623979fe9cd23b00d67f9ce87f1cf2dabe327792bd225d9b3ee225fe9de7427f6c3651910958ffd13cde55eb05d90af8af11211745872a595e0ad787b6837055ff53b5881ffba8833a332ae57b13721815a47540549e03a9d38b6b89aa6a0071439a390c3414399cc49d0a27e33259696df06ad09fecf329718eae36f58219823d8f816a0764efde7542999aed9ece46319b876ec8219c4bc8a79750b6e87d02623e293a63b038c50656abf90098de7d1667f0569a36a5cb19f7f2baa481ea8f53570bb0d744bf5ec6716f07c0a00ee32d835a428ef30e3071c9b278fc19c4089c1fd1abda374c939dab7ef38083315b8ad9676fc40765a22c727640fb24635b86da0e5b9ee8faeaa1811c5152154195d1808354ad9da2a5ef80348786b0c482c7aa44d81f39781ac2e9e87616504ab229033de9bbd63ae5ae105b9f282ac1e82a3251e03b2955191efed4060db9455710c618f92f4f57f512e5eb8aed02d74d71bcb11c0ec2e9c4e299a8ca72c600954829c32ea7f67f7bd1e1e1f9a9844d9614cd8f5b78da4bc455638707a299050ccdab95ad4d2a91bd9f81288f09225871a358af4ae09ddee54edb7801bd51ee01350924555abd312590b106755314e86b4c6e4daf14fef4d4cc60e7f21827bb45d5612456206a2bc5059010ab52ba3ab568fe9a8e1ff303c4ad6a6ab38de4887af7107b9cd2408d83e154295cf57da5500f0beea88d89854d180389837902504e11ecb1f53270de9d5811ac4224c074463702917194b2c822b8149a4293c1f02c82a688b49a1eb55594927b0d051c06893720d4749e07a417403918c18c2becad3010a9badc01605b18c1e5e37b22c4f508af01ab719701eabc0a7489c5bdd0ad4e9e55a01bdfd295ad85128f288092325df61963020247e7816491842902553f9e4b05668a367639fc7cef98cc6afd0164891bdea9c9c25d386c084dfd13651c7ea16022db893f873e5072e599b55d627f513aa95bd8b1bea372c32893199219199753993a484ef18e8af7930c893b91c5206f254dbd28514a14ff14064365eb1449bfd23165e47cc87593a6a84134292963c87a0bbc8f3e0c12a3ea649bf499b87ec60845c44adea44009bdb273fc21d236ab7e01e0ddc267def96c6474096a8afe1a49f55a2da00a0f30a3fee65047db0d4da64ab1153a01b7c9f7943c0b8e30df564b149b7685a617388d5e2fe1564ab3733c899ab642e91da185d424122d84bf410f2e9e79064e70497869ef50fb9b9d8f463114c403296c93cdcef97415ce4c0a0c5a635d388547f47043c623f3abff46f9c14ab9b5f1c3357198c456d2019a69f448e52f532d8ed2dc474a96522ca1656f0a770f6626abb5384c437ee3857b212b3b88aad59e6b565d8d47b6b79543ad7e0716d3cab2907f512b9069d5c7de3785e068ce7b312d1cdab4765348d658fa6f539a5d8a7306517c8d37113868461bdad4b1f32a7919ad25eec686b1f8f84cea836856a94a07456a51ab56fc96a91be026c15dc5d9c07521a50582f99244e8750f9b6d575e83373dfc4e2605ab5812c920b9a643133ebb282cea0334f71e5437d98da1eddc0f50f36ee7cc78966c6aed662be312c8b1ea311f8ecbd349051c66e0229b54b499a77402f6a3a3f4149e629eb977c58ef0c0ff87f14c8a904ba219f297141efddad5617102021c61a5053ebae4d9c9e40123ca730744ca68a357114b0661da17092f2491aae12c8b9c8aa72d3327723554fa5f75c7a5be608e7a5674ff534e9e82607355abfed7b234eda1a6d6ab9ad3c35814553acb070dc85d0ceb806dcb0f69f71ceb8ce3878d708cc6b848e8319275b90b83a1a72790c117f7ccca65503c5ca178121a89eb47cbf575a7b7388b6143e8d551cf57c085590ad40f9ef56f84945457140875b882166d4a9d22e92a5779fc880595cdb47265f5ed1241bed76eeda69e00eb5d61b272bf792ca393bf73cf5b3a51646851fd5af9f3937981de9abfc75a7aee959e9e2eaebabb5dcd0316e0a7b13ec0a24e67e94cac4375acfccc01cc7496e80738c5d8f5fdc40381d05b8bda1e770541f9630ba1907cce783756c9803efd43f977c8903f59cddec74cd972bea9de860aa5c18db6e4b47e591b265d91ec7d476950bb3fdfd64c8022746c7366a060b451c0b502c9a9e3df5d74035bccfc9d16058f2ef1ec2f5c15c3f966850826cd7aef05814aa51a7918285702938a996dd0c643647b384d4f9bbfb85f70209d4bc1cb9a7b4100c31d220203b88d597acc1d6060e7b986c1b111f9d22a211640b5b8c0e4bb40482e09242ad3c80d6435d2445decffc44d03d8053028147f33ceab88dcab9b78592cf697893df46af231629a8c8b6436090e95ef88b66167b75c7b6fe7ee52edfe56fe2c97e78aaf11c57d9dde7d9c52627399313d27427df2885488164dbbd231b56761df171e81648c14f158de379333e83f9b7e1501e67ae5bdc9330f4d46fe320cb49d2b055d2a18ebbc4dcb008b9e41f1a72d54027cba7687e4419b658e4049b5726eb240e060cf599b58bc06966d5941c8ba904ff337ae8fbfe85fa838065b99e0a049a0b38d3bc5086095ee0d5a298d364eeddb1cd93fc380ae6764d2e8503201393b6284ad90ab557367dae61bd08b604d2bcf81979fd4a643b68764d4c387022799fc7be21083eedc7134f643312578f9fb2d2e6fb1598eca608ff3cf9a49ffd41ed1e525077a9b4f889692d4310515a4cf2c3b2379fd533fb12cd74ed248703ebd74bd544da6b8bfb83f97f39236b916760f5df44423479df36bc6c53c5d4f03c054ac0a85d9076444e85026ef7c4888c483b2c46c010350563f5381bc58b5af76f2698b0f52702a805f1a3cb9a7c5e423186f93532c686d54d9f39c0122c357dc5b01cf2b88b2eb8bc7e18576a30d46308c46075267bf043ad3aa4606f8ecdb15f860c69d4133c03e42c1775ee5ce4c748640e859a20502e97df08009733f5f1e93a8a8a959bacf20cd4b46ce47768cfb268248e00873d8e7e0ec1380948c087419910717c2a385fbe93b5e206d8106729c2b7e6e74896a472c953f9a2f80c82f2f285d1725c272ea735aa949244fc9f7005d2a43d23faac8e788dbe840436f21df1a5c0e11e16159ffeeeb6ecd8850d21ea0187c41cf27aec5783b2c379a04cb65a6c209337ecfcc5433d6df6ee600156b2043b2536eaf00aceb28ccd3a6b86ac0f73e8c9f407950221f1f77768339590766c967c617fc651e0ba92e2dd006d0818e1cd3df05a29d5cb5a5e5da07c69a3617f4bd035874718f4e7b437e20ed2904258405d2d3b1addd8f0c3e92507970281cff5b1618db7ad3a6405a868fa739c2969ea9864d8f1081d203b6628d50f6ceb2c26422fd5920974cb92dc8153227eb4e482bf12802fc626fba616b9cb30cf3f087ebec37f8033ab4bce2cd1e0c5e5886cbdf6fbff038784f9179ac2800608f0175a8ff5745361ba60d3835273e0d1eee2d1437e2f7f8fff57be2baf823d78bf19be36b1a485b84031c8075097e19a71e8ae80882dd92b507b67152011f2d3c737bda76db5d1d316f01efaf9e0ed0fa11738f5e8808e08adeca6ef235aad17838e0bd7cbf4b15c53ec793286025f6fff9fe46ec95637e1e095e5dbb8f8a115ca14baa15e373c5816d386c00acc88dd1a3e472e4692b1960d082ad5e8a062a953ae6d220529e4ca67f17df9bc69cc2903db8e41ee9d7ae41d63d56bc0dfe076790456f18bb160b7fce44df852d73d6067791eae69d6e9b06c0d8739433d988f0fc769f31e2c298b9ad0db0dfc0aff0a22f6b3c008d09c906b081c7131a886c5941c2d9e79c7db5303ec6e504c7b7e3cf24d28bfec4aa12247af47f3d5171d5d9dac0fec0387de709201f235138832da48a3432ed961ea72a140420a48a820c8676a9449b62e2cad85f63f64e779ae341898cb722d6b5242a1cbc49b43c43022c895d864a772881ac87423db7a94fff7df2d763957156e1b0dc2431f6545b9603ae4ab2838e0a5d7e42780ed6121e4548ff02959e8ac6581eb102060fc1bf5738c7321d4e5477cc73ca51388e1603e00fd6580b4064561c004a4796ce7c0f0d561e416a1a48ba26a5542456b4f46a38490c4cc5dc4f826fef82c8eeab2aee892b78dc7de1363c86b019d5d4817f067eb7a8722575ded07e5397b727081a1c00e90388dc9ba09c21fd16b23ff72a1fdaddb012e174579ea4290d4709511fe0572de5df6f1beba6054d3e8dddd03342e817ca82384b9c487db5ca88a4e1b73fdf500dcde5d47a419419212a025ed51b6e64cb18310994d6c36a95b7b8e1cdcfa4a27d917ac8f10247e40a03f56497894bc86cb3345863c7ff0dacc3f602c0275e46e4c58d126a59e171544622cdf587776645ce89df8007a086d9f1b786b7c50040e4bceab847c2d9fac05bea40a6848f6ffebee3fd053e30aa6803d0e9136b22639d1bca08785ae8fda927a81959ea3b13c84b7081afe73e1215c97bfee0458580d59453c853ccdab3447149d260a9f93f960aca9e5949f7a2ca6f7aeec4c8769a2f4738d79c632bcc2ea737ef4482cffd457f0658d4ca8874a7140ff6e76d9dc05616ca4ce82bd05b7402c3e18103f7e21b1db179a121a823043777d7ac698a8fda28be778df475629d4e64572b4ca5061656040ba3ba79cc3299c14aabe35baa2b457232c12d150a4e9e0d3b7a4fefea3e3815fe300a090e6c6be04e8fa511f7d9a65112d3a48e35fb97a03077500ca39f10dc249dfaa7ce67edd05501135aaf30cd2935eb06de6f204149e60d41099dc619a236b7d20634209c318138f3216a69a1c9770e4c3bc0999b33f23c2be1ac6cc98e1f48b4da194ca9106f257c8449dcbbdc525b7023261ba145fc4e616b1f0094559b7942176c7824f3969be8c09eba3fe4310ff76dd5ef39b018990db75217b3ed7577815e1b96ec5c271f1bb8d6ff53d859ea0c980335217e24324d4ac1fd5581b6483f199990c6dff014f0a6c7b46ffd0fdc1d035ab33d595930dad53607e5c8dc585e72aa45b8863fd60f0e78af06e43efed81b58131b6b27d4b4fbc55899a3031d56ec65d89c91a1060a27c1604690d718705a8e83f9457e7f9e6c1d0803e0fe7bf2e049fe9f8fd2464b9b249152ca94646205860574053e45755a29469c4cbd47d662f9567a29a59ebd3806eb9ab8f7d251278aab64431b924e4c8200b14aac6889946559966559965f7ef9e5975f3ecd975f7ef9e5975fbe17923165d08636b440acf811c9164869436bc3907c7d98ef67aa4aaee4743a9d4e65792a4fe5a93c95211b96655896362c4b1af35f9665f9e597effd7f59e2adafa0c39e96b021b640ca29eee28fe1644bb2a17b5d156c68c37074981a86a10d6d68c397ab34030c2349154e766fce7064dba8497cfed7a84d4eca904206bb94fdc514a394d20bf3b277af4ab9d9a3fb18dbae05c7fcf0eb303fdcd590922dfe8b81b2cf2ac8bf0b5444d3ba4e52fb730139ae6c67cf9bab0a5384385d40eb77398b4c8395bc3ce62ddc2bf71572104eee63c67ac4324692363732aca6871e3ae9c4c483003129119d084729c7711cb771383f080cfcd1673961ba143e7fd6e74a702cda86ff681adc4731c5077a0ffc89b6d1393c98a61a8073f5351318cd976841b37eb40d9f4247ac29dfe3f09e3db9f3e03931beb66aa7b40d1ad2144c01838ebc851df3cefd4ee5b40d6fa552ad9c0b7b8bd56a1b1ca6651bf66d4a350d7fae33a59c06d399985eb7ba065ef87c4c42071eb23c4aa93dd8ac1437e23ab57d380ab930f470348249c3075238bda0280f3dec74b84dcbb0e63167f4f0416524b9234a29a594524a299d18e7fcf9fc87a80ae35528346766df20d8a30898a04b8f7895af4d05640a2a910f2158562511f742784c2d51e76f673b140d8ad22d681628ea0485eae137fcf862b22ccb2e1a97c025a2a64b494b391d3a0c8d6a5d0b0fdadfd741cb287d8c7eefc0ec077af8eb073ebdd8fe3862c6d829a0e60a35572857425128cce650da36f8b9cea6c53eadb97ef02b07ca3f1c4551144551144551144551d4bda19110c2d0462770220bd4a3380c3a800043eebf66cb081530c678bb19085317f1da22d9f5393add17395618c3b21a343964e4755dd73499ae1d95da1db9acd4e693bfbd817df695ce15ad533401840483115a26922a884832917600294b20330048e43967dc073d938934994c26934966fb81c1efee340a72fab6ab967620d6e1f9f953dad54c8d408de3691545892a0999836a728de3d829cf7d918c580a96923d675b1ea198353899550c804963219988ef68d2c4a2f965d883e1513c67005a64fa9d8f233e4670a6625471f538f2e0466c15c2e73bda862b691ade4614610a57620425aa2528811c57230e57e24a708cd738625ca372857d52366181a1cab1a9944629f574321319ec548c4aff1472a5c9b142ad344dd33c1d4f87b1bdae9661d85e375585154d581bb76d1cdeb86de3f0c66d1b87b7546adb36ef25b4790c1bd0cb5ace89dc14881e33f84fc76b0ba2584a3d218212726a624a8245b1568cf1171983583948a886babc94fcda637d755d0dc5ba0ed234b80e420a1264073a08d28320a828c49448ae3872ce5967d5461a83c5b410af61ced42fb848824344c6a1f8ca552a8ce17f956a6555f831ecf157ac93a12efecd64013e84593d0a93cf875059b6e322759d7380ab542a95b52fab8d8806843585aa6e0200838cdd8729ba6e7713864c43eee59c73d79b5a2f16dcebe14904217e3deddbddb79b826677d3a7d76317c83eb6f4de7befe6850c7eea67738cfd3f14b04891bd2f640a81ac83c5f5ae8b8b32e79cd66698bd40c8321b17727d50a60273e5f3b7995d4c61b6ecf0813cec1235c3de5a6b6d6685ec9915727337441081ee8af495d1227fdb9b9a1f993e58e307ac0f9c2b1afcf3214992bcf7254496aebeae797154a4722453cdf615b95ab739b8901ebc5f6b9cf93db6eceb532f5d7fbb52069adb83738b1c201ea20e39508b38ec8b21438e5c6ee370d4dc9426ea9b3d67164eaaf03172a2078d691199fbcbe15199938cfa9426fea02e8c658a1013dc6863e17ae374fc9024cbb4d30fee614964ee734b877bfe3fb8e7bf83a344d091e70fc2f3f9c10683e32ce0f9dcd22a2a313a1ff957de07d7e5ce07d739c863f437b017f58ac6ac742ddac7985ad72201ee57de022a1fa30bc2a3d279ae9ba20e688a3ca029ea96c3b4683fc4a7817f611886611a913a52977aea247d726fac25ed63b8df3aedb5ecbb3522d5544b602d655ced50cb6853ac9a6a69eb8a700fd6d2a804aa715243b4711c4712d7d295611a892d79d552b6d512564b9ab6615ab66d9bb665dbb665188661d80f2e9381190c06ff055367b2a9c5bc625ad1812ec568b46dde8bbbd490c18126071ca81986d4596bd79e329ece26e3b99bcdba2b32fdb985429a0efed3fbb7d66b1c845a6bad1d155e593aacdd42097b10dc6a6152eb6412932426afebbaae2773c64a53355902164d7247c3224f56aba80da60daec89ce93cd7f512260d876927d3f3f7c6a2499324258720ddbb45b65ae410414c1b4d360de736ad5bd98f16d96a915ce7d2c1c241692b8c1285c17a7466bcd036b6566bed5cabd4225b64a9d52a795a2d4f89eb783cad96c78343c7699082b5d68a22c639ff6b1fcd8de7a8a190e7e5bef7da720b916ba9c1307cc21fc6e00cb75dc015007cce8217e459679d2dfdf88710cdcf067bfc36d8e3164c9ba29aadc51275c1c0206a218af7be84481d23cc39efbc5bb4966d91e99555f22c45d0319e0c2928c20b9fff006cd04ff17ded9b86c5b6cec7111f5b37b51d884ba6a0b4b71c8ef97518688b4ceb4f6e87ecc33bf4e31bb82ba239e77d5eeeb82ead7c42a7d3e9e42cc34a0c337c5e064b0c510534452b20950c6a510cdd8f23d8b3fc87a5e333c8685106b98805f42023842812e3697c72376a10f9597e05d4b2a2f247f011ed8710cd009aa24f86719b8e180ed01a484706c845193445db16e33d5088ca5017acab9d9597e163f818cf926198eb1ccc029a2dd218191eb33c986374169dc7d1438dcd8d0f397ed001844aee782787ec743a9dd99d4ea7d3c99f19c3097905bf4a1325a81edab74c974c6d24c98925460c31805838cee3a1c901877b3dd4d8b877d33fe800c2bdc65ea094524a29a594d29e349c93e5edd7183f6bf8a60058e0a35a648a669195b7b1a295ce8710a2958f010ab1688a3416509f20758396c89770210c51a4f9114c9c208a348a8223986089b47a04a9092d91664f80d30529d2ee14486a8012691793205c4125d230145054220d2b021454a148cb84ec201469dac602ea2751842b91b66d2ca036c2881ba4441ab7b180ba0826544889b44eee84c0f3d5074d26478c1889b4012cc94f54bee24c1bfea4690871184c9c3397e7b36a25cb790b3b4f2912d3e355b5c49c73deaeb6e68c2fe68c279f4a075622a52ac4fd49dba8389346eec0ca8433e173fb2bee7d6c2892413b6ab9401a4259d1987ca3380c062fcb8e62bc2562ce99edc5061a3a7634b16ca29e8ec807da519de29eab3a1d8b4bf2d90e94d9280d69d8aa3046476d10af87e1741ad2b08661188634b4f6e596d955be18eff5ec23ce0ec018a6d9bdb53692318f5266cccffe721ce013350b8f34d4c54e549da84a1b06398274a152428ebb5add253587aa0b25b0c6d5683bc085942f56f77a09d5f03c29c158ad30cef93fc7531ca190e7652e2efe8f13f185141c0018e48f98c15ead1ca673accd716f6e3169f48a0aab1386e9c8844194e3c175e05f57dd884bbe21efb460ce894321defa8155b9bac77814dfb0cd5952664c06006cafac7316f690738693e9e2bff2e273af24968f72fe7c7c544953e54bc8988b9e9488d9b91467b9776d6dc14d0dc3bf303d475123a68bbf14244aa22a70161593b88f95847125a9703e82290916c5b6d65a6e7bae7beae2e07f18ee66c2b611ad1e6dc3a604d75eab65afaa3db40e773219c6183f6e1b3a7a4c7c84c76ed6a6a6ea7a11258aa2288aa11a353659d45ae76ceca2fa824c5f730fab9cd18b0cce32026ee4ebef20d41b5399c1df8ee3388ee3bda1910bafebbae6e4ba0f253f9e4fae992e2e8009865c43155ef3e347f6d8ffd87ad42175116f6ac81a92478fa68bff689251c85f230ab98507017cfdb4fb40ff7f5dcb0eff5d8fb300041924d1852d82a08552047687795c2928a145175f37a7838c530627596373e303d7d1e2aa21a37531e7acb356ecf3c7d35a7b85464411a191a99a53b5aa610d925347505a1508ebaa54dddd1da2636fdb5328582ba20926910f219a481729d191d9796780e763541ea4513e5a2ad1122d8d4a9a66b79e344e04db69d756b56dabda76752ad59c69fab967ebfcf643883815ee8710691d100da94b8b4dfecee78e862d990a25659fb3ced3d11235512a326847d9db133dd9d169644fa3d3a87b85655bd6cd3963b37fb9fb58afb88d82f4823aaaac7553d5c861ee4a4557aa95ea5af14156a9ee6d194f5cd7cde5836a03d7755d97733756e4f999f63df86fa0ff5cef85643006d1e4e01e8e1e40a522c66661f39bd5d92ecf59af8b5bde139ff4095bdec4a1987ecac950fc496a625fd2232715e0d3beb62dd01b8dba4755f491f8d51a02716f840251263455494dc345da8d843811235ae7237519cd1944883ef2d168f4987a4569d6a2c874a6711e001dd3316811fc64568b22bb1645ee0ffc7c9f6cfbcc19ef743a38e2e7b3849a2a52f173823cb96e063d3a4b02eb01d7389c0e2bc8af850cce164e0cdac59190728044132d22c960e8a3223645418aa2288a62ad9d1f4c1ba0c8f33b5a9669dfb1d66e3ee6143e602214f9b85e080f293a72fb7998f6825a76b83f5da5bad7407ee0c7ba2258868b4075f48218d4757e1d9b0ef795abe97c77b8ac6e71f30495fdd168341addfb121a6952e8a9892909a694528a67eba3df4adc05479c2871a2c497cf3452d20ca5826b65328daa9c24a4911494131393c9f484c94d3e254ac95af156cbd432511c984c485a2d8bb24b9a4c319551587346e5e20a5df8dc3e685354455d4ca696a9656ab54c36c8df72fdfcaed4d5c42f158d59b99e78a916148fe9b28a19e3a4542a954a792e219fd4674fd4c55b4e52d99469cea04adc85e2c0549a4a53d932b5544b98584c399b4ca4c96432994cd70a0c20b40199265babaad86d0cc3b02539866f080df15f7f6d13a9a1e44dc6f2f939038446413748327d709236ee79dfc74fe96dadc66168cc0f3537b4cb51ae7a24ee28cbb2eceeeed6b29a39a736b5d63410f7f5c7075bde344dd3b4a7716d730fc7d542d74a69571965c6d7b5d61e31abfdda3d8d5f208caf8b3a6a9cac3975ec1089f0002214d262e9506121d77b6f8dc145fe0ffcd0f4efc075bf7ecdbd5173df723740e045188d829e73ceee401503584b336248dc7b2fbeeebdf7d2eb567bafb5b8de8de534978c4c4a46ec88030799d2c8ac7d77c39165a426c284c9e19ef5c04c5d92df3bfa7e942cfe74b807f381e9f9f370ef0324989e5b92ed20006814a0a8708232423a31b15fe9052253831ee10371f83a1a2c0f6f71407f1ef26bd9c1437efd1ef2ebaebf1607f4d7b2c34f2490beeeecf7a41225914824128944ba3744ea187177afdd3d8634a626c9c964c4f675f12b810411a2dd6ab28cb465abbf6f4eeedf98e47104e2030022b93ef83a34c78300be3a031872f6f87176cf823a3e903e109faf3e8d8fa036b9974b2cd94734943503b5d4faf9d86cdf22feb40de4b5d85246c6fbe914db2abb2bc5ad467bb04b353fb00efbabcbba161bfc6204feebd1bd5a06711797d265d865d865d865d865f8a2a2827d9354f6aac3322ccbb22c4b6b5fca184ca04e27146b0a9395125594d493d309632ce5ca329f33442efc099281c4b4f8449c928373c8669c7d76b91b7d5a32636a6af83e594c27a793a8042b419d5a6082214af04dd2de93b34e18630cabb141dd40ca1778d049e183dab60ddbb0ee31fe1504e6fe911a615b66b047d5da17952f68d6da69a7c51d1b5bd3384204492484d664145d3f2f900f7e4377b70f5b6be0e694c1900acc21b7f75e5c20bd7365ec7018eb27db699526398d27c8f56ac2ca8a528a1208655fc103d58884095648018491342a41c2c1109c71f45e42b4c385765d2fb9862d642b10d7556bbd735a4f167d5517c624d684d32a8a1255123207d5e48ea3aa474a2bbd51a96846158db9c944594959b8062bf2a42e2fc83a8fba786800a50cf6489be8aa010e64e0a655f6e80a3d5aad3b8ee3385e17e785cbc0b6dee762528fc6e76f593e9a61b08690b135c3bec4f45cc084a906a37164afeb3611482881d3a3ab464db9412142134f89d290bbd3a0b134e744067b341aed20f7e0e19edf7882f60ba533d4d3c57638c820a6251d6cdbb6b57a6cdbb66d1dcffbcf4a6470b63e70b2b2fd56aba58282eadce4a03a76c86053065b01711c4d0e387aa8b1b9f121c70f9ece6a73ec8566ad9d765ed8cf8bf1cda670041654221f42d0a0c24a1465ef43081a56c84288487b2166167250240aa2ad6fb801c4e1f3cfe14f83835540a72b420efd10419d3316490f69daadea97698c942fb81ba150180a3b0c851348c04d08019309d38a9950088970088e1234eebd84b073a75a6f46a50b46f7de1fe4fc00ca144d9053349102711f3781918c93e8fa16283214597c6837eeeea57a363e3428a24b850fc0f8c0a9f201111d50aedbc950971c2e0d122a3e70b27a3055d5da9266e4de712ccb1d9f4ff9293f234ea271eebdf7ca9052d3552c91b14506272902df8164361473890c2394423bc4aa6d35ddddf1a9187bf083e149624badb5f45a0ce49f18a3753cdcc5ffab3b505fcdb99da3c0052600030d683b546050426b5c8d754790107c11da71af2b546305061a8b00df9c12c5dd2f56b7680c0de70c28c6d7a45a0330258bd9635bafebba2e989c6f5c1de8e3bb78f8879f86ee91420a84843b0a7d5c3a4ce91e48451cdbcf4e87fbad0b627b6e7bae1b421404f79d0c743e3f7d95ce67117e5af2803a201ca2f6da6f8f3d2b88edb5b7400bee2ca0fdd65151744ca7d0162d411a8496301948e4e11fedeffa9a6538e6873b1ede3474984ab32830bfe811cd4a352bc5eae80f2125d13d275e16de161e0ed6c562b1582c16c65e8e62db5a6a69cb58eb323273a63ffc7692572783657e166152978f0a191742768cfd6554f6de1a342a5582d184c5643585848275a94bd376bd2297ba346738b144c74fcba7a2fd8794431f7dca074d29b207f1cdaec8a539d3ff83bbb80fee6203039386ff8c06e60c77ee060ff7ae07bb74030cec9b9c0e334aa67d373bae86325513bb8175ad2a92c12ea14aa55229c707f740aa9b39c33327d5cea6811f43a1b2ac860c0e3452604c92d9c74c4336b96d5c6419b68d66ce601f27e348c6e29250f03fee36f206c69d4291ddc6fd9cdd3daebfd129cd094992645ed180a13a94a8c5c0d62776d0597d630c04cfd71d0e3232d78533a4858316929648e2e93c264a85802924896173860c0ea4e6311821b5bb975d204e51d92d2832385b94bed8da897da23f421f6d3320cd39ebac5a5649b4da11574b29a55893b06fd29ce990c6ef0fac94c126913e0f88b130580eb30a50c06c8a0893c6942839547cce240559cbb08d524924128944b2f6a563857befbc531445ea7934a48d628be258924183eca1b73c9bf8b1ff74596eac2124113853b7033ff5ebae676022b78c26641f0d046708a70cb7484b5ad2520cdd7075a077832dcb39430cb2add124947a61e2a16c5032039939ef17e2de776df45b88d77879b1c1061a3eb16c510740684d42b69f13294d454585eb171d667ef4c39cc8ff953cc0f96cb0fff8b3b7d1402dfdddca402dddd574495dfc4b20ccdba1bbea5e0b71af4527f93ff1092a7ca08fa6286f5996652903671cc7d32a8a1255123207d5641ce99c18ff77163295a9e99691e971cefcf56559d6cf910cf6785d374a669fcaa8840e7a64c968040008405000831500001800068442a16040302094cdc37a14000d577a3e805e401c48644916c46008a2903186190288018000019299425500c62fcfd202af25a3cede476926b63f2a54c0fc9206d818fc040159cf0a4f6aefc8f07baa91e60ed749d43d2b2075d1742bcd3bb80fa7945c6b03d62f00a582e2d7e383ff660afdfb19659cc6015385c8a50af437bcd7837524f7e494b6b04eac50e64b6d2bfa44c81c8133c6b6b6e73ced05c1a6c24ed55d67c4e793a6a61b9a093193f59769678b8a9688a82c81b1de893b294755e589a146de29e630e9526c64172833fec060d2124d7fa25cf39990a45895a744f0967917d7c338afacc4d92b9f99e2f1427963fb6d07bbcace34e5bdacc0d7fa2d28aa0865e6ecdeb93f5f49cd395c52ea42f19c6a8e88b23f8427b54f294d5953230599815eff254d31dc54b89aec8180f0f077c75465fd946cec4cf1462a94c23935156412b78c13bc3b0c5dceab9ada90ac8ab60db5aefa194ea07c7797fe8537843952fd2c2fcfa626275d37e31da9e4ccf61327d44dabe18340429de298001def786b431a7ce3484191777928e9314ced8158c3ea8e836b83a3ac51886c4e8d470523b04b12c0e8dc2eeecde12a9cc60f7b6ebff74d1b80f0527e7c2677df060728f3455f7a41ec225d68c8fd8579603fa9d283b0b1c37a7e5568a19c1b3586e2830b848d02e99c4dddaca53fc844f1a25075c611859fc4e19206d7511e6e3c024201e88d02ca61076ff5210056b04017c2c22a71ee401e494bd4f7f985fefd0eaa0e459f1b64a5c723c562ea632c58793adabfded5ef5542a57da5716045e64bec5801589356b54dd105eae52abd44ac0d454f65a70cfcdc7553f8844295d833232334965459bcb2bb59dc359b221cc1e6541b33ba8bdd2994ec0e1b34f1aef368c5baf83d472599f6467bc44adc24ae290a6d0b8a8ac76bf3d42a0fc9f668b18bc958f481124b044e07f00618073b1925e58c15c162a9b830bd28f0e61c41b2629abac63472c652f087b0676e3b2d85f830ffd62ac5e22d87846f368b9a357c2249ce95e10fb9080af3a98176a217ff4d3d00a6e4bbc6cb81877c78e0356ccfb560260c3eac23fa2d9664fd88a11e08fdedd7cb6ec92ccd8b522a1d41008cc3c232bb64545679de519890597285d53b2671ffdbcb0dea41897c74d49caf1341b38640217cdb816821984cd168191389176c94cfeb9e17e068949687149e541f57a3f69ca67466f159c142a3af050b1549b8978345f8029501125fd07cc395a18741557f54496a191439f69def98e4ebd420bd64219404b58f3c91af362336cffa5c9eb0e42640bec22829b772c9eede40fd4903d2869df7778f7ecedd5774f4b100e746776a2fdde95ca10a3feb74a75ed01bccbd4516c332926999679fa8951155178a92b9c692a0ff1e3bb012689f538ffd11e70539114f90a13b0da116f6e3c87c8860ea453c91c4518a98921d5c6162d4972b74900ae45fcd8753cc61c5370c9d9e11ded5ad755fe3d0732817759f78221afb637ec7c57e3993e30fd67becbf4ae2db726811d0ddede8ec02734a435f2b192e4c8217c0ec681c956644890b25453d4b6bb720b1302676c43213334d24ad2b627409b327d1bec1ee3f06fcf0cf2811089ca9eb4326473277dc27d2523161c5617e954bca0df6c2a9654d1c3c4ac6719087917a382dc680231256c87b822c5a9e22c8c20d0035e542af3649632d6a533983ce8842a5e13b6482689a6641792c004890c1f19842737664dcdc43b4dc79fe6452dad542b3c48a7ddbf4fd26ec1b3b4be258c89f982de888620b28d7aefb09c469da0dda58e9282b58092d48f7e06fb59753701b436c87777b513159b43ce6aa785751a5b7a13fda5703f485c159a418ef0c1cb98b569e23eb32ad5bd151c5a4f75409c3e966de62d24c66c405825a5caf780d4868827bbb2efe6ef83e3543fada09ee8acd91b92a9b0a426799ed9a2b40bab7a9abc3c5db38cf3fe312c2424237a6ed5cd106d07b71e64f297a4626c30f4afbe78e2325258c0087b639dc9683ef76edc4dde00a1a83b73fcea0e18651830b82d60940785535eb302f5506faa736f3df332b86579a9a462e8880934aefc18270b8aa317628cbd245e60eadf848b366b064eceb7d199d023bf3f16100a7b4247af47512ca48b29a84359047501d7014628363ab2d0c0942d7f851b8cfc5375d3a63dad817a3b8aea4733952284530a9b16fab7827aa40cb3a437d74f5925b64dc5c9ea6ebba9a5c47bc79474b1cbaac42e3d8e96c8eac84225f706d16e113eed66a1b265f84ee753c6521a063f4b09ca749bb03cf7caa3a76c7f69e8b1f1fcfb99dcedbb67f5d3ad68d2390b39759ea25be59ba325308a46b3023a49c08630957c34f3ae33cb446b0b5c9b5e9526978bf55862dcc9afef56b072f2ead393efa23f8971793aca9c6213419acf51fad0cb2b6c17993ac0a27d94827d9e38e3a8725b527595ca7014cc4496c734fe964eddba4eec1b5bc25595712997d68405793ecb08b2cbaaf88c396dc02201d1baad3daa4ceaf59775714fa3cbd06802c95878d0f3431633bf964fe213e9cf56e1cbea79aff2313b56781c3b8dbf7e13aaa0849bfba71b82643208a9f129918a03d868e86987b589aa90fcfec0b7c3c4e4bb58691cb1b64593e2d5d923d4ad3175c5cc0f5173beb928e59f8bb6840abb9f8eafc30ed483b8fdd9d245afff0c2ae7cefa90b6d35de87e1e1ee29d862cd3885499143fb27d970e85722c5c52cbb3ebf78ad6d0e58a9cc5f238cb86d06ddc69ee718b9286c15cb7108c70aba2b3e70a0a23568e62a0943c745060c106e314443933b9a5e18adcfb1ad909891b11eb136071dc8c5507267982866d613a0ad7388ab6c2bc329d5ed826cfc012a76f19c9a3988218e8294067e7c5dbed236ceeb30619a490a95906d44546fb7fd59222729df91a7a510eebbdd71ac0972b124012f970c39b4159582f8f0850e028d6fb234f1244a00d7d4bc171e21ce81ca925d2e2afbd23d4a56eb6a5023d7193fb1546da43da033abc5fb0f2f2bc3f54290fa6d20c8bf6047381407fad8c58c7a92af6d5b0514ae1e97e74670e750a1d0d976830681fcc6e292f87305515ba69d0a87b0f01dd8436d5a65b0d7dddd1c1a4353ac00f33e38446398d9f5744f1deaeeb4e2e164462740eb0d351137a8b3e5d63c9362a538e21928fed3076071d05c96d070ceac4705076eed0a1f266c64a22826c8cd4769cee50b3a53303fc4a5bbd4e210c0d2645487d0fcbd224d63d8a5eb6be5ea590c1893c18a1f5a3c2c68d287343f743bd6d904ea00c0804893ecf34395237e5372b59889ba0c9d1fa2e302d13c2e94a021aa63df3faa3e80bf1eba8f27120e25845ae8358c62998a84a232c5b43d11a4f275bf5aea2ec8c1bab9842ea2614ec2ee030530a52ba9101eab409698f939c7e1206d1aa3199288e860906bf12451fb6a486c9a6c6bbf9bd75fc25246bb6136bbcf4719ebd4212226e7b97ec531c84c31f66904e631171ed4d55c2d0380c682eb5f7b8a837f3b0ccc8dca8ee36381b7f5cc9267ddc4051203afcde2315f9d4f4612538025cee70a924a19ca6620035decde70fbf2ccede068b1ddb27b9edb09e66d03ed5aece4938ac49a8edee848260b224bfd6e7962d97ee60f805d230c8d1dd279911c49c01ba0f7a87b04075784ed76de05730e121c45a9043a483b9a235152f0964b3a2878527cb45ebce69eac7dbb2ad2021d8d3e9178189e9f73a0b671b28a2384421a7aaad69ed778b30e8e077e63a7173e756f25240fbe40ad1e69156ef56843d75efa0742a11efea63ae4d076295fbe6fa481a0417f63b7b1e5eed1b5628f40e3c78686b2a31587c1be4a591bfa28626150f583d0bde714e8ab8c753552a4f6ab55f77010a542cba9d000980deb78a603befa003f74ba29b677d53a72da7eff92310c0428693d5ee7fbe99b32f5d4b6fe01c78a5563afef4336d82e1e2214e2db9389226a958270abe5b7264cf60315f5925058e87bef5637502b2313265f7a7d268c1bb5ada9008fd6c01da67c69980ee4761022d52020a4e2eea3a905fc41783f12068291f85803b834847ab62adc49313ed6f52c87ae37eb8f5204fdb9699cbcd6887dba04511866690ac16199ab8cc8c7b0827dd10608367e664cd177b3758d2d1d3a2bb8e7ae8e62175da57d71b007a244438b459b49ebd2aa139882d373b52acb6852b1bf4fe480a6e351d3a6847ecc255e889ab30701322702ac1f31cbfaa16fdf60526665a88cd1ba9414600720906d97969b2ec9cc8dbea16bb005cdd17ffaa4b2eca181e279fabd82e3734013b7c862760ce382162675d95269bc90612924f1fab6e883ac02697a89a58d4e89f567a4373ebd3bd0bbd279d5d20bde63ff24dc10cb2fe235f7a5c3735f09c3529571ca80b6c9dd9b078fa726bf31555992765c38c5c6480a32da180196656575e9bc1044b9758bce811411fcf7d2f79b2632ba4bb46e4f9c1a949e19f07684770459bc3546a743fc4daed6874e254238b7ec78120514ad4e40858343c960ac18ba84ba168b793da5daa2748615689ed5e1f8d6b3a669b61bb2c0755e472892b60e97f16e0e08557544c43e12ebeff854cdf4a867343e8c32c570b8549879b5f3e323d5efce881b44d5298a23f8d1ad7e026c2df0c58fdbad2a1d1f57286163fd8faf0414fbd9f38be581b686316684b50c2a5fa6a54e2b41462f21a7d503f3aac04aac67c142060662c32aa1273d0dc0f8fb4499947ea15c2d629b125694007e3aabaaad95c1c475def01edc2243c1529e16f86c6bee1cbbcb233b38414f8f800c2ed772f81ea9a89a67405e8db86dce67ac1e266818a31a1dae67ae4f0053697d01f3190302d1de03d40a878cd722ffaaa693647f36581f2b4427c7070a17948e194be98e3eee1b62663f5c03944cd88462c01711e99d943610a93f2351147d0e02f2633a9074efd3295c7f0fb9b3c6c1994c911950e2c2e8170e98f0c85656dcbf80bcda19af36be7951e1d634b7c154a5861e3df779586ad4ac54c34c42e00323b66e1c3f6ce7ae6688a4b7aea13d433bbc017a036ec43ceb13e264e04141a93ddb719f9b3c41cce03eccdadfb9e8ba4ab95dfbd3712054ee8c846cab5f936aab94c202c0a2949f90c9bbb9937ddca230fbdfb10a35fd5b6082487f18eadaa173926a8324017ccf81574eaad92f8ddc70d4585b75e34e82c643d70dae5a300e26ab120bebb5a83c12917324849b9b8f1c5bdf154af4ac99435cb445eb02128417149b91c116081bda59aaf18140ef2bc55207ae028835fa038ca06210bbfb11ac43c50a7657c687a24508b74ed3a693f1a174261006d245692616a5fa3618ef21138a308bc0b946efdd1d8fda64d15e9256b637ad0ef58f0bd7e7a0982f7ef138555a04b57c695924fbc242f4c9acb15afa8416d5284f21424517312199d119dde35c73bd8625e7a54d7d266eeedbadf38b837c9fee88fda35690429069b43fea9282e84d47117a465160878ac87ac67df5bbfa8345a3f86fee172bfff0c0a2184e9914a09b02dc3d5e5f9aca70c70c4db170e68cfa030975008de8a28e2e9b124792d0606fcce338d5e11dc1121afcb516011f4167a9f56430654665c78ce470fa902e53ae49807a477eb9e6fb4e03beea5c03eaa5423744e0ac17d1abc548ce8989854726e764ac4b1c109c490064d77c6ab5d3f9302370a9143a13824f53e13288f9d5b1da91cef3031d3074afd2dbb3cf4a6d7e9c13bae16a0e108297d07e69e248354e6d527f76d9a617475cb14085ac564085c80276dc57d4b6a41031a3feab429dc45e61b3d19abc7582622174e7f81d6337aa97ca672a64d5562679c80d9dccfcb8abb6397cd7f8527c7fbeb5af40ae8e64b4a278b23dfcd0bbb6030ae95940ed9d30f21281fb21abed9ee79ef14c78dceda006d7b6100514d9da46ca952809d4d42f8ae722c9dcbec8f9365eaf22820ac3b907dd7ccff00b164bb74fc3fb8e26ce70e49d8fbd5289cbc4e3198515d2e80ec75eb1f728a6cee7a557ac117f0cd8f189c55134ffe12cd95da9976b9dc750c1b48cbb805f6e6a0e8e928d4cb407eb2da462aa9deb1c0ea86a0a5fbdf9528b5af05241d8519c745ebf80d00271df256a2969e7c6a0be9c8ee8b8bba52ae4f9b12b2c1f7e1f6c6cce1241beec981d945d15d3557a61078c11cf3f229e81f670ab09f7aad3bbc055fb5d2d3d16912d8ab7013d1db96cfcea00e5d10f5c5201aea9dd38d4823f563839eb0e60c862e46d7f228f75acde2270c01685e8994854fe83ee3fbfe42350f4bec63245bdddc83de2640a841670c1e67965d181850347ce1118bc8e69f630e7036f374b6f382e7a6f5d065098bc505d3bf725c4128a4ef7e2a06facb1878d8d2db970ba5d807040a4a4e7d5ebfaf993092dc170719ebfe63cea2f025a64dba471b110192343732696ddef6f38e5fa7e8eac7115758fb971745998cf4e0736073a719a537b3e16d89e999ee0f0f244052adff6fdf2cb6a84b37b71ced93c9915bcaa99f23b01f674cea1600da4f57ed62664d4f120e8da4a1843612b108688b5938086ef15b7e460a15bbc0978e60c7a087725c212cdbb0a062b5d93e5d12792641c4f3bbcc128c8e6ed5ccc0851778ba5861aa36a35bc8fbc4caf891a2fbba244a7d76994c7e7fd1f2cf449613c71b72f16ba0029fb0191e93b45261e64a74832ae3c1ffd1982e5ecb7157024bafd75c1e5812ddc30591b09fd2e72e95fcf45b060c5a2ce914234cb65d1acf974629f98f274ed0903602a73a6391f4f6bf702b8cbd9a18a4ac92b075260403941e4fd136500848f702375294b3b47ef6e78e0499b33e4deb9cb658714d2219cbb93f422d78fa87f17553eba3224871548bf275b646056b6514009fb8a6032fc0e9409750f177df7f303987bdd75cccbab3789b56c794ec7f85e6fb232c4de00a4cd6e03d8ea308b25dc3dee58fa5762708185c82bf88278b96d8e305c9453c5b830103dcee14fd87d1f76da413cb7a8cd30f2e878fa02eae90bac34967f7120a7379d0e0f528db0382a694bfeec64f772e546346ff0170137718ec7d2a33bb4acf155a03f18eac426612784d1ef466f4a258081482589ad54204e5997f3e92c2c1b08190ac78fd80aff472a37ba9731cf50f6c9f56afe1109ad1af84d10bf5ce63ff9c609860912d3b6c06ee193d855de9ef3b41049b757f5ab942cdc0df858e16139267780795e100e04503cf320a1fe06e3a451719a441e40cb9d0e78dad4095f62094a27b7b437232895bc338f5ba0b4938f4cdc406942722940c851e5cfdfc4b93691bd718fd4eaddb71d050fb1c37cce18dc3617782509c7175bd3625fd4b0d9e89606eb7fb2b00a0ed54839804260142eb72a4477fa1c331734e13798affb63729d209edbf4a50c4ca900a98bb4a0f358291ab785632966ba14b57b92ad73e04fcb49e963d07fa266dd8a050cb1d6a95eaa7c18a8c225502e5cf8d4bcccf453f5998aa177e5a5a53130ebfce7c7d7e76ee4c6668412a52a044d6fc987dcc133df96880185254081c738184688e7dfa69fade9376b03d5929143d3e480eb9d3d79c42865384e3f24eca2294d5a8dd6947442cd3ee8c2400cf18127261846273c3362956c65b11a2ad147e658006d611c68fd564d557e73e988ed819ad84c9b5454455adb63458d31bb7d121ecbde3e5e98476d89485bf05aea48ac27112c4c6e1dc68435c69467617ef027f76b7f65ea0cbb34b5c44ca0d8096aeae18832b541f515cb59bb64f6a08f7d8b69314824368fd2f081db48c7ef890c3b7799afa9d2c18201ab1b2202f2e42dc8f672e13f0c9a2be755776c3772181490b98ebe09baaca3d6f4b3a64e93c74803dd2084e34369709fb7f5949c739727af81c1707ff4cdb8dadfc8c00814ae121090c48a9af2bc882e73eb046c1e26b042c46dc3143ad20a78110a0704374546e3884b8640a5b06b57ae60e9600b574d4838281571522703b89a54526930c68a26c3c23f53946535c1d93cb7dc2e21bc772fdb4ab067aa14da4b2719be6416b92c707a27622c55e204b34085a743f77148d6a88972d6c7c3d7b4ba2600a1f59f2b959927ced9917331e3b8e5cf5fb76bf1b2cb3108bee4ff0d0a7d01a882d87e30140c61af2d3212c971f7ce678084e8eef547e0bc65ca54431a0534cff6a4826982847f2b6a8662669b9e48064c6a7e7ae0cc52ae6655518ba5765a7d2c57f4a7c3850dbdb2e5b8f657820f72dc215e34dd75d49c42454afa42b118ba87e195d562e56c346d57d4c2da3f5b094fd492da6b96b41737a2e632500f8d0f67f53414132b3bbf1f17b861de0dbad2b0f1299f402e538ac08b200b901d054222e08f4828b6e9062094cb11f3578de02b4a15db227bd9a111416762043725c2d7a533a518608a0a00ed3608a1cef5b5b0cada9dd7f4474236a6561362710bf3350565a1d7dcef49c17775e161c3a720fdadeb4f45696ab530aad3fd4e190227d0cab95f0ef8553b98f6875b514696f119c0a18c7083336aa63912d980b351313ecc3f66c27a4e61462ee23a0a7303be455d81a6b5140fafcc1a71acb192b30a80b3dbb29d737b67a2238e9c7d8fe553028ebc9726f6391c81a88f33a83dc9e372dae6cfd756ac34336d1cec50485f887843086f90d11b187d6fe9e68062a59a86e14f646e01156911e652cf54cce7ec15d98bcb68b0402c29ef3f8b2ae24669698192896d29aea2beaed1fff9c63aab186ebd93b4557db92ba813bdc8edddcbec07112583da2333f62e6bb075bc12c88f7c87366230b7ee84a3351ffe7952f8c03a0affd90a56745a28dd8f3cabd53a33b7d0e5029fc6af0a8f8168b1fd4364b1c5e2ab7fc0a94a2b4bcc7904e3bb3f58d139c2dc8c5e92a334222ec27a10a1cee06c18a2bdefbde33645c916d10ecef3e38994ec8a8494c2aabe934a70cda2b8f31e1649082252aebc457394d42cbfd0983ff21625ae9a7004aea66aa6c3b6b70817d1bffa9e810ea5599461276c1afc51694fb9357da37fd556fd96de313731a7e92ae2446715380694f9f8f3a652b8f5cda16db33442f575c54c7f42ef0da60bbc456c8a5323a8c410d10b8a134a49954a0c1a6d7cbebe91463756f0a526aa7b369438a8a6f9c8110a68433f6c35097176ffa01fb59786f85ef4b2694518afa5813668c5222faffad826b97ec92c8b0739989096e970b366a2b2fefde632f3e199cf492833be9a54411d9d78c536022ce502ac1deeabbd86a49682275b51ba0e797c18fe92708457230ce35cac66d4701dd7ffcdeb316fe4522f27a3e099653808749ad7d87c7518fa02fee83c10e1067057961f5a4725127e11a210ff99d0f9065629632f4fc0e82ca94107886a2f40a37731c45cf22c160962f3d59129142d642263f280a9571fd1127679786cfc51b9ff4b9f25f0d3da7b2294c84f3024376cc0932069de82f3141823447403e555fa6e04d6511272cf0d2228c98a2749be9ccd2c0292d060e4f2012278271f012e2c32b652360bf6e4701fe2b84adce4a3078dcafc8cb8474215346293afe42439bf02cc3d0da7d92979283d6c8f1370c68f286441858c61fb266776eac6f8dcf2c36755a16f7801bdbc9bac2d4492e6ebf76efe88df38f0d3b87e8c4a3999236902fe525b2db33e36e666635380697508fb3aea391b8978c6aa6585a3b3201beed8a582167e97a90ac9dbc75a6513902f9cc9c80216ab400eaf1e7433c306129641faec246b2826fb711a2b0f5a0d7da4bf0a249e5c8ebdec98ba026563e0b81ee30ba846e42d01be1374026089fa1e58a7f3900c1d04e11d15307900b4c784a3fda73cc3bd8ea1eb81bb8b232613c6fe6344627336b5431e11041e9b5dcfca3955c858101333642a04fb71b99172035d8070b5315a918c639043305f214c083388abd9900203e62c52c9c8c3382c276492b971746b4427865414beca5f1e2610ed5a27e9aa4b453083c5652ef161cc04f1d49c3b6ff5f40444af49ebc71e15de033589e68c5b982e1beab107b85793864a084eb38182c94d0ca268cb4d59c54d93be162f6fa4025baf51eca72dade4d00684ccb84fdee4d5d6ee3e7c85cdf13bc1ec699dfa0018a6e3bced9868757a34e860bb38475586ccb3177fda5b6fdc859b5600580b531706db503f2bf55a7416289fed1c5e877baa0f37871be7c09c0d0ef7b7ceb1301920bd49902f834f0ffabaa2611f08230fb4d2e7fe6e075c0a1765e753b899b6bb6a5c5a779754369f7e4ed93ba8bb5bd4cf7194b9580d8ddb60ce85da4e0bfb63faccb5d4778750f2eedb84bf80c3616f1f8542bea8fdbc22eb978e4a030819d8d705c0c4aaf12ba3f1abe4ea7316bb8a3f94d0e694fce11cf6c935530474e099db288d3fe924b72e99f64aefc04314e637a57454e930b43060d314e992b58148d3fea48cf94663b07f71ab7f1255924d261158c472bf0dcc1630a642afbd931ab7d19d340c9344aa8f4daa86be9bb031a1ba52f730573028c50dd95fe8e7447d39832e3cc2d5aa740196573a802b4b9b70313b173f0d503bad24f0fbd689e06a401fb7771ef79d80d88dc4bd36bd243a4dc198646261176094746ad89fa779a74dbfd518c23d95335cc6b5299e0d6467788452cd09106a3bf62e537faca88892f228851266b1c6343248853388634e9a733005a145c6d46ce45f1921a120864c79a9e5c4ff0a26603e681341c3bb25bbc16fcce7fd242a904b81271a8b07c4c75da1fc39925d9baaaa8575286035fe5d24571ae28c0af6abb6c96171e6bfd9c4ab18527bb2d018c6efac2764855d70044cc800b9ede4b6b7ec38ef28738a21933bcaa25d105dedc687e8b8189650eec4f5d1db1dd32ff0ecaaabcd16caf445bf47d88261c2f869394c525e10a758e30999ea6eae752dc4917c6d1deec71265482f4626aff6845f47be3527caceb76c1153b235776be5e6d2a4a4ae63c21000d750c548477260fe0891eb1b9e9f1bff3bd9de9ad952f1b4fc3cb47d12d2dd4da2e585fa095755e56e2c9b6aa40573bfbbc6f1a0a34871e358454c5c9faa2908d4b50bb10f403f1e381b35c9ebb39d00aaf58374b4917498563dd488e341b7c0aea43de8f160de4cb5c2a2883998f6668a48e9e21d96d04f8f07311f34efa84c37040e92591d9feb84c23f32a7d7ec04f35872b51a8438a9753ce82171e73e0000f2d7beb6f236f2ccac0f548b1b06b4a254df3508dbf3e37aad35604118eb601c0f4a2296ed132268a9b805be5a5851cb76970ee3385bcffde98f07bd7a968552abe772ed5dcbd45946dcc5f6cd302ed6b8570a31f7d356e323f12f77e24141f7fe9c76d60dbaac3683013e17af1318282172ea45f93eda84fb338432f4cc5d488ccfaac24c6eb676c8be18f9a2ddad0c7c02bd7f118d7d49c669fad6fae566fca49f9d9adb279d4b1e6333ff0610e1e253d632e83437d359ecec21d3de502f2fe217923327761663d93a7c80ca6c2ce5993b71088ad7652650605e8d982beb012c9159c317839d61d64a9be669f10e85d98d582c2c36af66b5bdcadf66ea4de334682d559be1555348dfe01e8ca584793797476d32eba446061bc900a050bbd3676c3974934f974a10d3dafe8630fec658cb812e00bf7b81586474b0e25f3309bb501781506e676da8086f2924888cbb7ffbbbb9df2333ae069acbc92f069e56e916ba901b6e9d82067a262d6700f085dc5a802dbe0e99697723cccc2e77526c26f15b34efd455aa20374acc9b6114c6a64e36809af56de7f30e72e3c1b27f92f1ffa46ab97bd003f0ea39ee3e75263c072aa9b567572814d68a0671fe698c82ec6003f6dee5943232214b76d49e39151f3069e13c8a378baf3b4d1d08e0fcd63e09b8c333703669bb65250270f60642dcc63b0eb78bec38955cac4db336834fdda5a419e9352c371372cec4f59928b60ee5fa87080c7458df60f82162acd52c531970f0e3a8394d1fd6ad860397afc96c67c400ca3f73f869f892af4b1a046ee5ab321e1f65b650a76e2ab4ef6ad459b23600150b76b800d6d88d3b36ef11dbb710024165580b33fbc125f073b4e6462a4d2b1a58f4ac0315a3b5709f7624e63a3dd1ba468739dc167c3887b3a9ddbc456e12853aaf91cfc9e2a2259cacf0c5794b6ed9f3962840f5418c6b772cf0724b6d6e3c3da2ac764d404d5e308313cd56ee1f455c5f79738b885cd5da77ad4a3e8e2b33995aef7c456d0a7f5792f19c840e0fd9b67810e2f52d7f503870003c3cf4d7508d50e2b6c55e5b93849ec62b24bc929aa822e139c9b57a6df12b4818a15622cf075f32e9eb4952e6bb9510c109e4608ff6931025cd02bd96d534175587fc4d3e281ec845f68dc2aa060e94465c30bac79c2f8bab7cd2b096e3b2ba66c8f9b18ea34813e2e172bd55d47ef937194cef571262da86eb9ab5b32d1a317e196499a093181a8ffea317296b928df806c3f6b39d64ad6fb8fc8a78b16b1da3275f280f95b13021ea4740c91bc8dad97ad2fbb7b6b2787745aa5a46106c7edae4f062cf1254e8435b535f477dc9f150e5488c20d88f0473199146c83405bc8b5507814d8811dd049c4430bdffba1dc7727fd5a25d5cc547b4b36d52a4b4cb2354dce4100ccad5b59eeb53617671a9c6bedb62c6ebd901743dd9061a73286ca38b830deffa0166562a5b61fccbba29db1056722ee328bc16b77912e85b3d807c41b0d4956d295c6698e070645823240216abce102d212b3ce13a24ba87c5ddd6ccbcf6238743943edcecfd462828d7c2ebd3be4af91e738c60686a334beae5266079160d4aaea3cf278132342a6909cec0090eac12b2eb41bace7b602b7f4e23a7eef8ced7ce0996b3b9891bb51ad6f799985d6850c0225452228c159eec8056261ef2ad00d85bd12ae2b9c5e2642bb2606f092c714ee3c9cf04ae1e4af59d0e0213975ff9c2de2281884701a08c2806e3e876851db50256794bbe278afce23a36d95b7474a3dc80a0f7b6f64ba8d3e82f15fe2ed04432f147e9180561ee837ffec21a1a13bf5e304d39bfbcc6b0c4aaa71b315a4a0f7cd437b090cc280ec7d860fc7e6ee35315ebb906138ec05457f98b36530933e1d391c424943dec3701995ac57221b8a2fcc90063d8a8d12402561abfe833083b300c6bef70d9d53d1b79eef8ad7a91b95e7f1d810071fc6dc24d44f18e345bd4882f30d4d186a09aa3cc945293c33cdd60448b93432aaf85651dade7d436ba4f0e77a8bd038eddbabe862f7818be49d9816b3316eb08e47aaa6ad756fcda54138663c37475ef6184ac38125863b4066babe67e3759b7523fd551df3073cdaa10e6aac83c075303947d746e0bbe7c0d73ff5844b7d15a45e834601395651ff71c1fc2da244af3131809795685e3d11297b38f7b5ccb9b5663cc2aeea5664959d8d65bf9daddb1b2ddcdd87b2895b241a80e22b326d3563c80e56a769087346bbb94a9fd96c989f158fbe28377ea9f6b55684b4fbabbd525399ee01a086e08ff5483f33ecd062dc32b54220315d99098901ca28b3403e0f89a07a893d67278a02bd865366f915f1dda5a7b2c71227c5314a1bc59e8dfb2aafcf87e07433d25c54e9b95a526067e95fc40b6a6fe4bdfeb78f4606c5ac4ce2d1becc716dc2c987fb919d7cdc25d395f2b73d54c4cb6fae1351b928086be2694bbb87e244bfbfe15362386db0016d7ab02864a3ca2aacce0e98015cbefeb3a773e7ffeacc296df5c579b3d0f07052a1b9f48aee5f12ebda73f79b95031bd48f223c4b638f8d4eee87fb1ff9cfb295f2977cf8f269f9f93109573ff27ce7e99b8bb31d79d9ed602181829515d135df8744601e8179f043a86fd6cc057b06fef9af34ba30168e46e553c5b3e0fcdf9921db8a1a419f6c7babd0edad46408b9b270e863a32cf896b3a1ac7f25992fdde4579bf7e95ee1cb161aeaff8af849c32ef86009005829537f0a521ca54ca16886c3fba55476b23068d7a24bff95c6660dacea458e1006fcc4197d6b8bbd20b77f6b14bdfa37c5c495a6c5b0374f8bab7622b56039fdab072030eda1ce03d6a1330e0d33e0046d4a70667c273a5c61af4f0b193b896c5fb99a68b26f0a7b442001ab48c9e5fc04464eeb386d3947db3e14a2c38c7a25186e75330c5a816c210b3c65b43a6001f358bc0c6d8406832c32ad5a3002fc53b65f49ba405240e31b314f5aa1fa4dc9ee366fa7e0c4b4c5680672a4b709da37d2c9448ac1b8a317428590dc47429ae841e25d718682497dadc2044cdc57540a0c67e6c0f96033ad7ed71b48bc524a129c0a4e0735a397cf0e51e5765f56279c2a72d0dd1d59247f8abc64ca5d0519a691b710c8d5516f27b1cc6930e99243c1971c65d27414a9c34579269eb13b7ebc4e999ee7cdc39dcd3ba037af9eda42ddbcd9d882d2e816f85b6e7bea388e452b75b7841dd859f25219d08f479dde74cec802e86c81624cc7229137d0979b3ee3342fb7f949b9ae9fe467489b7b756bed08764e7221da1abec729c513bd42693470f36f84c8205b9e793515d70b8618bd0398b6ec7599ecd8c4995e481770d848d81b6a1d1384646521155f28833fba10cef0e603ab91a2ee3e0e0116b3a7685f474d41122cc947b33199d6262ad28b06047715a68ef2c94b329b38ce8d9e428e894c0197cc66e05ef783eaece0221a2437bf60084749f82e3fccf9e779e129c49f8ec1624134fae6852f8bebb967691ab790385e6c3aa4a35b88b9fb1c6d167b26a43e6d10c3ead4a79a5eaf8fb68903bfc5165311637b894a16a9c34eb438170f69ca781d6885d6a09bbf8bf0dab88a38a34f75d0f427d33b3d2a796156bc3929b30efbc1c1e06e226cebf61795a52104f5f95d470534bd0bc28bd19457a79a6649c7625820554d2ab695f80ca0c708f0cfd0e7d59515b81bfd9b0758fbd2b9d7bc1b59e764466416c7ac1cfa87320eafb891d73ac579d984a3a6d41d82e671ffff9221b7c57edcf2187bb0b2db16e0e445722a1dca40c78154dbcf7536cb2527d87f4d3b56604b63640b6b3978bd10af4ce70d0608c4c66c8d6260c8c802e26c4df85291528d6a0c5097424aa31a680df5ec9a41070144f342d30c5d8a4148283cfe5fd74a4a5be354baba5e725078d7d563fb4939b36681cba48a2ff5ba201ae93347547f696b5820b66e436c0c2731d895458878eda0d6d6e440d60ae1dd3a5d34e824b9cfa96dcd69e8329237df73cc6d8be2becee34b5d7edc53c9503a44138031a9be24f935b8e3defe95a8abb6540499a91385d55545dee69321fc1272d08e8ff0b8fe0a17e653f0254aa1f7fa368c6d4d5de4f29dfa8ff4eb976df7b8f554a571b34aa371be258ac0bd21078f044ab5154c9aa10ed56668627ce492905a42feedbcf234443820730740fb1ca9993869643364dbf4b4c6ff4d92bee72e44fcbeccb7f731f85d23195fe8015f0b5816b8290fb37505751b8868c5365152005b97705289c65e7315b5b69f76193c4f6dbb69fec77be5f305bf426917c0249982589d14a620c0685b22191c4a6365d17b8f92b7d3ca72c35f42ce9af50f627b0029ade625b103523a367a696014f20ded1862fe0e33ccb19092868b48ebb4ad43969951db46390c6940d8c8e80d74367915a7780044ac04df259b3db4c7dbeba6e6f56dc61edc215b69b283639aa5981de21d14b8a62599a0e59538442442b2235b090a1c5989d4687fcc93b9eb0c22ce8207fb6a0389ba0870738319a125d6326332756fe54bfd33f9973c0c35e91490469c317536ad3d848c878711dc1e52952ee89b32f939596fadcbd27145407313520dbb32e13c5229e35f9b857cd3d4604a8bbe63cd4cfe9838774a5d3a72063812d1b66e9fbf1591ea449964d4fd451e92f5daa1a5fd6e6e48a21a4814247ee3ed4e763511169e06f9c197fe00ab271b234f27f993c0c8011d1401b3c3db4754ce7a3ec6be141f91efc5a2de8ddbba7804f99987ac9cc8da33607dc479de58405843f2ae6ebc8533efd97282c32de3de3e835afa38d634d1aa87d76a9ebb0801e421959959cc34efa54b3cca73927fcf653b56de764a20564f4feea9a7a7c2603fb351a5934b33c0a278a49cf94a7822a52b35c4d70899e6d1a0e7b384553f4416387808b62bf3766ce0eedf077fa364347b031024044b52034989abc811bf4126830bd443ef833e5d92f47b1ba082a6232e46cd3ed6dde994c71ab76827e18550cee24921d0f157683a711cd58ef8afd3b27bca1f97afdec1eeb2ee009d895ea9dcbbc99c3657c7328ddb9258c1a347edf58200739425ab30d9c1ae1ff84bf291e616f5b16198da5f74ef9c1aaf881ab447b549e7ab1486cbefe1ca097e79ef0b868d7a501c64c178a7756f7547286aa0dedad17b2bf1c4d8608588dea2b765f6074d57e54dc95030e1a8601650ac749c9395b254de1a79833ee153339322bf006b01e7a43a4b15441857e808593d775c7a85587b56937194434a089469f741a236258cb04a3d5dfea4079e9fcb2711a1dfe68c3bc22d150fb499aaf431056d65079029454728b086999864e2e7fd1f968f72d37ce77601a727dd182715de56e0b79fbc661e66c289b0ad74037a5e536958fc95eaf2c36ea6504917550966164b5ebe2e668ce265135ac4b5c949f9fccaddee62f3d9e5905a91abf536bcc029b51dbd38f8e01cdd3c414975086a2124e23ccbb19818680b2d896a750999a9b34b826e45b95977c74981cd0fa9a64bda3fcd5ca96042f914493b8c8172f4e6558594275025dbc90509b84c5580809d22cbc017b9f923bd1c71b845a928bd91ebe31a50af6e3a9f38d24086e0f3d00688c88cf8b4ad315b6202ee7cb25674e5823ee5f269c7c281bcb207ac90438a56ec79a5cdbce21861819b798c98d326da3c06644a4870ed890150fa72fc24067cc40d02d856ce689b315b63d1c92a1619fbd9022bee6a94e88c25209fe68446a40b229005f70a4e45b509bc78fcabf4b0995ccd9e46d9058ee6e0acf26349a9dc36324903bfc6001880736541f402c5d40350d0e331b822d84ecf43295e28bc7ccebd75d1542a6b137bee79bf053816251ce6ce89883b1918e6f879f1a51324801c53b3edfeb759a41730f928ce53d70eb3a01608931723075b9030503eb38297d38f3b20a444952e7c4054a0971ba4cf911ccc8f3abfa0dff23fd170e0cf0ddab25f5ce308178be9678a6f7aaee4b1ff45bbffd4e1fd85d9fe92cbebb576bf28851cff3924e5ebff27c1fd184c37d64fe767f71f492abaaf7fc96ac23cb479ad16c5bdb4019f4c84fdf17fd2ef546278f4c27f937a7c1bf053772a53be0e7886572a930ba9a9cc55be9190a61a23b51486c4e6c901dc17a6eb4e7626fa93a7051e22163f8d34ff8cb2f151fe5879fc8ec3f09fffd50510b52cdad8cad8f5d317614a36048f3391ce800c49bd09bb91e90f47091f96fd59c1a5a7dbf47ef884e14bcc756f59815555016fa8cc0dc3fdf9de32af5f64ee14ff57aee0a26cefd51abfea360c8984add837d82b450da5cb2cea355f7fe47b63fb196b8bfec78be2756ce9eec2cae5802662eca7f98303388efdb864fd1640f6273d59cf1f32e1d81fcdfeb30e66b9cd800f5a33eb1f75849a41fe574d2c5f0d4e0cfbbbd35186c238f2a7f064367c10594038be2e51cc942037a7c5f2b1a6866239920277ba1a65e7a60c4a23912883c430c41a09d8e62aa53795cea2cfb61624c836fb879a9574ac9ef68bb7fd00b5f3ca7f94766e9c9a7e225502fab91b99e0b8ed631c1ceb84ddb012b8e93836fbf77fbd56001b1f8b18b61a43872df64e6a94bb90c13e32541878b96ffcad4a8ea41924ef319c0a65e6e068b7317be246a2d1744682bed142da46dfcf04fc1656829a733f092bec40124f0027eea838a5409fb4e1fc1b8e1fc245973fa6c53fb9a9661b0c0f9e1ff7536a92488d09ffa3c1b5a1f0a4cc7fe88ab0960d04b17b4d8f79bde38f0b2b24f1ddf30a3fd75b853651b4e73da1c840a1f0fee536fbabb213292ae7b1ed40dad312024338167b60528609f15457ae7b6941e8ab01302c72f6db288ce77f7161eb35dcf89bc84d70d36ee6cdd0ebf1c6ed077bf5caba58ea497aff1d9b3bc4cc9802a7b4ccf2f125482d6e0d1c6c4cd9b43fcae01f82c0562a8fc3be3fc2e534e939fcf2aeaf4aff5b0cb7aaea70a5c1480de76e00c2b35b3a205851ff1265490d126f0df0614250de7dd3bee49b0b39e673f2e4a62cbd0dea0e1f0421cc345f63afc80dbc51d1901135342f71aa1d2d5862118c869d050726d2ee09fef3e1384078a916accdbdc7fac1bc9e4042fa26fb9fd80551a75c67bc8b28c6db6072d070f54dbbfcf7bc826fd172fb42ca356cb4c4a9761107e0214e95cc482d756a342d66b4b6618d976349b25b263d2ec79b53c7d439cb1bbf8a68d0bb0ad484246f111a908c2be6adf43360fc50e093c311deeaec42e0c5818aafccc6c815d3244d88999daa4257510c34b43eeebb2a608b674e2b3bc4c38ccf2659fd923c0ebda89cb2d767b7eb9dc727a4899a605b7f5722bb8882b280ae252dfe0a925dd002ea5db098bdc349b2d9a57d21210eb44c4f9961c7f89d136fb095f9e0cf685788fed0e2202cf27780eeac7aedecbd2568d63627683ace9f5b6c69d8391a6a2b61d18c967a9c050ebc93950dbff161e59cd6f843de895d14f302161659c0018c49e36cb7bd39bb81a8eeabbc06540c5933db06a557a904a3f9c56d9d0786e4118e1b4b53eca846ae0212f930d863b8738af580067a3ee5b6c6ef1a64c1c27215324817ae8ae9edd62d0af33f031de707c06431e6e2de6f00edd47958444e5e7e7de3da0349f4670a6aa1f38ed2300ac7aa606d111080b2695042cc0f434870bd3bf710cb5255fb557f71ac6957f04684ad49ac63ae19987e04fd0981f4930dfafa236c39610edfc18040e8fbd7afe72dfe70a04597c197fb41d998c8449dfef34e650562586b6d09a1cf9610559311b157de674834e0b051f0faa74b196d14074e5d78b5ac6fb2286efa45a9a1d1189ed4573fec3323dd514af5d0f5de4e4affc147d2af527a69fce7b5376a90a4d86678ca62e56f5cb250fe3796fe02ae90c4865677b9874c40ace6bc288daf987dea8c841964cc37dabac6b5885e52234433dd6b00525f3dfea2beebc068bff9f6fba915231bc603b87d7a227dece951b89e2c2057babef5c57559efa59b2f7677e183d53e718e978ca1df9137daf4359a884937b40596392364a48fa217255d2180817fea2d3eee3352533e66b8c561aac40a0fb33c0044280c7059dc11cd783222a481a0cc3cd85d985b13ac204b790a357a845fd1502e9ff07ad4c1b51885e1a2a374748c00af687576219f57b49e8521f7e46055b6d1ca53111570d217560686c50c58615b78c04c1e217d69cf5d5966d6a6fc20a0b5ad2d5c541750704a9a072628af962b36de467b563026438d740341e4d359759609402c1935ad60eaf6f99d06b02f8781af16b0cd381e06f58745309c882490170d5f546aa9221348173440a0a8f755f335ddfb2680a1b6975d95fee57ae0990c4714abcf53f2bf79bd03e8a8e8b09489dc474a958b10e62c9992caa0a5e96853d7f2c490eff718273224720119c453ae3cf13bb8b151dae622a1406ec6d2b00913c26423f6d3ad5f16ea1f1940363aaae7c989742b8530d7ebfc07c6cbee8dfb1d7911c91f1b18e570dec5182dde7846af6ffd200231b46629dc0b791cc674c0568c9e4ea6b8f86fe1ec81ef7ce5dfd505b78d36764fbd5d17cc2e357f95e38e885c71d3c954f17dd6209ca7d83975b9fbeed37dfc13966708922ff9e13ceed8baf7881c543790cb87f5d2f80643703c35c84351cec52905dffe9d645f08bede8eac7b3377119700dcbecfe8850b0d07b1a710a90190b66965916bace8ed1d34855886ae2e0b0ec88e0f50f61730a87d8a062fcd810156c26a3611373454756110286255659fe2515023ac282c454b70f80fe0629c2a5353b1802d35e33e619e4e4c06ff81420ba10bb08d31ba1ba20e252ee90ae239cbeed027d4af5f06cb7482ccf30bc254b19ebcacad8b580c44206ed0ececa6682e8f7ad65e77a43c663103b3dc23cfdcda54e48de000473fe9d6cecec9bf20dcd05e7b32b3ac8b71d522029809e537f61b796e185dc8d63e700f03c07e997dda89672a6e4f6eaf9058f0fb884ad6a6d3b8bef13f6fb76da64c2c90233b4455769bb06b6f3b7a91dfd753c986ff6157f6a7e653d96fd135e32b39aadab067499268eb8650fec8dc0261412928500763aaab80224dd009827262106a2c900a6273845a42bd3d07c5cf9f21138a82eb3b7b2be3b7c264ce752edaa35d423eb374da021dc2b617f189aaec1a368202bf297fbb6011ff5c0157e65cda8750b7e3adf2536720b8cb94419bca4a4ac02de87fc9cc95c1f64df8f02ea3a05e8fc19f85d63cfda3c9257231064f9979553bf202ed6582d1dcf75d8ab168ecaa3860cbc08f1f188a72fc24754cbd51fb5fac4f5bda78107b25d034480fa370074ce14c2f98e31a6340372b6a85b2dd7d1c10b65872871a8c89bb5b6d6291f1ee8c83118fcf4c6876d2cf246139e3c829f00c64132065c272dd26f5ac72cece4568586c93564d70375fc7d510ddb0e60f86a1f0adf11ff636dc17f622a87faa8226278d1085022421aaee77122a234588227dc00dbbe8c6066c38cee9bda5a9e93c8593b917ca05a114fc24e03e5775ec6095f9036aadd1e386bb00e4272617657d12658342e3426bed1b2278c556ded8d22ac052084371a8558bc01933caa1188ad95843ae00892f1479aca1f8e0c1f94654fe6a8e966fb04ad38c19ae7398c83f0b65d8d28d656215ab5ec576a3b6082e9125ca956472edcd6a12502db97b0bba51b225cbfc2e490961f3f3fed7880fa08b59aa3175cb616083a360c89279efbf5166a5c151d8d7993aa16adc259eaaef8c919bb1881ac1a2c0896bcaa74d5b72bb06706a6ecabaf198a65b58b72209d2378b9bea7a78b779289c2ec7254d75c3544a945e6d79cb32f42bb8a4f0b00434151163a3a4d593cbbc6426d369380a067151dc55c3c44113fe7b1e8da9612806087161ec3c75d206ea41feefa987d925e7de71b401fa6bbade669e311a6bbcef828393d468030486ad9d006611fa4aabdbb8abe9ba4aaeb9847216303cda8076fbd851cb33055574b202204a0c3c2c487b8dcd06b37c294674a88fc99b45c6469ab17d8585d2d106407b08eda019babdd79f360aab765d93f407d2f46c367854779e40c571bb6e430fe9930201960b2bfc186d20616637db5bc8be8be444fb7c952140884398ca66b1e00319a88c4f6b36451b2c899e79d3f4a8c5d5eafd4156c28b2ea970200342957ad083f86c10fb690b3b74464bbdfee0b301fbe685ddd420971c29401349a7a5e61a7472f3e2e6d676ef9ebbe86ee1cb50fde19a33ea892cc59b4222cad73022a4a917b128a7bd6b60622979bb85b2556ad77a382574afbcf15e4265c35577811ee9148a5989c3870370ef610b364f21914145a5122ddeb6f87950dd87e3c3ab69dc869e68481ae43b4fd0b36868e06a391770e823e6ba442b8b8453d80d2c7088bd6aa039971a91da348582c4e51fa7eaaa558a09d72d0cb232b291826038f626a44e4d0463751c787c021f6f0ad2dae04ca9cd1aa4d2466026c8622499ef7346d7fccbe7490f2d9df3442051dac959c7d784330b081fd3736317d2cb69ccfaa5f43e554e348a07c7a095eb4101278aae78b6991289f71e3a2357e21c2fc2e0e793a206011848c528fb79c64f53f219d9a6c5b81a1650908ffaa9dc7e9d7ac07c647aa10b15a8fd9c31be18ce4aca22f00499341521cf195c24a79cd18a2afb1a332650ab7802d8d1fe5baf5f42471019d50091b5ff0592954c789db462c19ac469a16ee40393b308b58e6f35104913ac56f5bf340e7a4d0dbae945c56cb7e0e1d9566febe127029447e204513d76f5fdc4a470be7917d9e972f0e2afd2430052b0a9f4a2dd4669f739f8edf1917bcea9c4ed0079fc420fc25f83879e54cfe90781411004cc33676ef018a598ccd600acbe051a3e1093ea39ee4d1fef0718bf9540164a5be16a1d48fb887b09f902676f91636cc4d9ee83a4ce38032c88dfa74628a7b1251732d4ccee2cd4ccb1ccdd36111bcee342406ea787c79ce397508e5fb2503af2b040a824356724e30a502179bb8c6a336c3371d4fc658935d4181f5c1a793c8f89eb10cf53ba5385894efc739781f1cc15a12f910dd0cb5fe0430a9ad4ad9e4569ac71aef71079484e34082b2df19e760f2a821145f0f7048d6f4146aa939984803a547d77255fac21e1c2ac6665f153400335f81a60630f396225f3e2c7465cfd14e6760b20e9d6edaf1bc6705f4cc8cf575e2c511dcfb48c8297d7e946c67bdbdffa73a0207c97c0ab0e262cb5a7c0b733a4bd8f326439cd44a81de72989a25a3d10853f3fbfdd1650b725039e2c57eac1650f9b10cc4473dc3a04ae8f4c4dfbb087ea1c1308b2e0fd02625aa1b514395d709b3aecae19c0c31c5d4c0283126e959efa474bac198aec8a1a6c53052b04857520e8bdbce80c7f34beab607e660bca8bb97ff423e8d1dc7537b1ffb2fe2a115d15c5bdd556cb94fe4475856c94c9e296ec990a240a0d331fd0536d0722cebe172aac5afaf47f25e366b9718e26bf79987f9202a9c829c20745c35d6c2e852c984ab202a6d556dcbc04de62c74ea6ab2212241bafdce3345136362a721c1c5c2019b6718b0a7e736e5acc00333d014d7ad6a56d040afde47d361f19c93947b37c97e24d47655da3327a9f594e46c9f43c58991718232130a546c03cc0c81ac3b12e2361222ec87ae6b40b951a126ee409e849cf57c5d66555399ebe38e0b95e8b2020b79363c5fbdf73e0ddf2d0f86165ed56acad1c83ffb6a1192ce5941e28d3270113c5100fa50a1ce683430c4301531c6e7b1de339a102f4b0e6b6db4f23b16b3fae68f4426b151783312d294e22f330dcedba593582e6af622e1fa298f97de5d2f9be1593b7786e33ecdc2a6ea96b036d7e882fd37e39681fef2b7c0ca90f464bff5e2a1933be6d8547859e972b94957afd0d0695ed206fe0b75fab9f04cc21ec242fb26c8c09024949feba9f5daf4012e25b6deeb4f25ef105f223b0c7d71fbfca6d43f5663f25c5ccf86c15670b0434c33bd77cf31563bbc19106981a6557d2e29088d67c468b07edd397aa99b1a6424687092c6ae79045722b4215280d7ac5f07cb1180fa0c91404b1ffd0c0dfcf0b41d1dfbdb1dc6e2575ad61f401f1f71827f541f9cac859a20b6e699450c1813eea20903b01bb8c29c003e2a4fee05091ca637f9e1fad0ed0227512a2a36bd7e63aa3f66b388bb52611d2e246c9cb591142f0373b06edf2c9fd78b7978f6783a1a7f090bf18bc5fad13bccfdc237bbf7cf2512431cc7c5430f313f0fd7d4884598b9cb2f8632099dfa4836a4a99296f0b8a347754e78c3e08ca9216a0423366b4e3067e76da5298f5aca1827f1d4bad6f03fa2db26c1c2349f32c0ba210bbcf835823c8c420880a0c8ab1b4824158d21b1cccb65b6167bd85c21dda6e3e50bdbccd0888b10144041e8674e3036d6a178b31d18ec327ee9a943954383479c93fc21a8fb543065b21654a118c13cc0f2c34c7889a633f4904931d5ae0d2405b973605be9cd9fadd91536d990f5974d7970239065addd0db7ac20f966b58c0cdc9f814d94e6ad31580f27424b7189e1d3b7976e03d52238ff767396e1c4f92966747657b04611fd9d63fe2e0811485ce948c095fa66ae95417786cb8a647c6074dde996e4f96d5200fa995f495de60bbd321bfd2ae379cda28fc82506666f15e7672fcf57298bd7c4b90f62d4ff8c2c8ba5ff6d285714264c6a6821324f29d794f9c40a74abb0bad2045c327a35cac25c12a6f60f64e035be2b0b92fa2ba60f9e53e183229f99d0b399168174bee85f673526349e43440e568724b72eb475515c35feecc644627f181723da4c27aea1a9d9eac2474b71bd45675dc744506bfab468cf8a0dca0b2b01c517c37ccef474b744b3baf5cc8658444a1c87c849f550b08a2612abaacbfee646a1bfb6a41759b1a0b44c0de52b38311ac897d613c277d5efcd100404515274294d4e93d9c1da388c9def1ba4b5fb66c18451f11eff5a5e4b25f62257a84f12aca9a08de493aadde18408532f7646c6bc75071a17773b2963e8863a8ad04f10f1cbb87670858c9773ef6195aadc4062e7f996f310e0f8a4d4aec1a157e4fba89a2646b5ecd292a75c1106a549dd58f3835089d5b00e074a822f49b00a10753b5bfff2b12d574d4e31daa9faf8a9c348e65b610a61d3f22769962ab6d4a853f17c532a59a29e36f93ad5c9db2ecae4ecb64e59a838b57e599d04719246a41ccf12cd3995bc7766229488946189c972b15ce6cc5924798dd989f05db20731dd2d2cd1f20a94c1063bb75b9e4832d7e84d6a1a4de7573e5673f217bab3320e470ad88db461ef4a5d85e6f00d782b8db003be0c420cccb532a782b8608a898f451c96fb7b92f3129d7a458547f8a928e373db2134f70a36f491a42164b162ccf162496645d1d8d9c4158ff59da588825aede81c172a59ac534ddf4a873793b168c1aecc059d8b5dabe17a2b865b1d32d0dd6ff646195ce9e5f07f684ff2394ce5f6e7f449b19574bf66df92abb02ace7520c1a4444789ee12abb63d8116024c62c7abb87e640e79ef8f18d96462fb8dd631a940e300c2ca9ade500ba0aae6152183d96bb6403537878613454272ed7867e19cf02678358373aecf4b66571b0f83f850b5a7c652e674ec8269e56348e02be54ccb12db18cd69b23767e7269993eeb8d8c5279645ca11ccccd3d2fd1da18d5afcb718d96750f6bc590184d1854c8ed27ae2798207ab7c2a3ae29907e1eff4235f7ebe8040cb990a7051891757bbc9593d08699c76d50ea016a7aaad68870448f34eefd45660204fa03d463e1c6409860edebcc3a27cb4b508ae45cf2d2acfec0d91f51c4fbee0c1bfa261c1c47d152ea3a7aff3a8f065a3e6b192892faf85a03efc5b5d6336941fdf590607ef8acec096c22f39fba11a00f6672fb08ddd888788025d740039f11758817a72c055ec1889daaadd3e73baf9df1625a0891e750604b302069619fc0036ed6c53ee25bacf4ca31e1fadd68c77ae542f70dab8826b00ed10106a541f99b5f54a5fbabebd0d991ffaa927fa78d560add28fe08fbd66145349c2e2591ee4aeb1dc256fd4ab7352f44310240b89fd3bfdb9d289fc9d437379c36058bf256ac3a63483b4e20bef9252c8484766c03d68ae5acc436ba65662a80b8ccb08de04fdb449cae5ce49c4319e60f5ce8fb4972ab858ad2e602af963f92054348b38249379e1ffabb2df5b9c220cb702f92a7062177efe5bcab725c5e53943dfba69f310f71d429d497d96ce300ff1bb5f551f305fb3492fca17de04273f2bac0194baeb838bb000d2556aebe7d40c31255ac6536af19b34a8a3a040718dc58f167d3d16551c87a017f87647a27562ef4830a5c0430b673508dd90c55260248731939ebc4bdd7abbfe6e5e081fe0009e79371a580ddf7e0717cb251225f42b267a87b675ddad92aaa3753463bfd230b8ca0421a22e61f57bc33b0d0fa1a2524643ea262cf51251f016a331f29ddb3d8749606f57fb6584b77cf6b90f742b7c7b4bd0c0161db3b173bc0ba93a8f554e5f09caef6ac7e5139112a92528ea04e0be8099047c3e640f55db57bd8969c0b7b5a99feefff4d60fb8fad07eb142c21c519129a669825994fcd0acec5602c9d0bd3b59470f8b43d8c0b730eb4b5dfdef619b2c01729bf2d596bc9125db0774e08a742d1a6cbc53f436bc485b1ba429198ba7cb428f9bb216e6efd17b20676218157afef3f81ed0734e471f72d90250c17a8dd75614ed910756f64d01f59be57060ab23c484d8f196327c48346d1929ad0cea5c8ef4e918a2584c625ad33ceff46f8952f4d7607562ffeeeee633de6d7a2fa421ef27a980da427cff4d45f5962c1a70d6b89b8ad58852aab47af230092d01cd60c98321b4aeddf0027da7273549306515d914fbc4ce5cd25c43ccb5ed24379381801557f19e1d7d2e14ee0e256d89ab0a422915727d16cde6dd128d9d03d11b51d5c69d7135534ca6aeed8e5b05881fadbeadafa458af811c91757f29e1cff2a0c5e12ee76c5f5ad9e80c4d7798fea8bee7b8a3dca2e66e4ae3980c203378dcd52172416d9855a0d0038099755298a1ae0f0b86acc3392c5eaf17c71bde76a3e32f3963e470ae263c00438661afe371977a22442d9b837b99251b354b08784bfbc3e21cfc5134a2c68a94bfc1b8027819581db89aa3763b775864d27c978fdc8746b1ea82581d063026a2ca293561a8b1c0decf1076a18aa4fcbe73837f959b7ca8216874b03103fe1a43ec22340878ea697d5d770bb87ecabf7d56bace48c11aff6a9b08309cb341b7539e3c707fde42f72c2166a044824360e24638c31065ad1b82692c0521a8871ed51ac032dcce313f6510e0a4160ce97a3deaa8e68eb900d87b7c2726ef86962376a7e36186fb9175607a6aacd97b7c406c367e85bf8f9a6355da3e167b538bec7bd541f191b1d2a0176dc2c4832138bd293b54c93dededf8410af2f72e0e37c4e033f3f8980617a9e2f4341798a5ab15a798d1660d4028dee75adee4d0af74649470a228571e9d94b594981a70a84916add37da6026c110e9bfda93072314f35a18f956a49698bb5fd8d12cb481e51ae5dbef7b5e53222c570be1f9955b4fc03d825b4061e195b5ffb07b9eeb3a750f40e848b817fdf24694282d0e17fd0fb860811cbc7565c19fb4592545b8fd971aaaa25aa548479b6a5babdaab57ac836201179f129a1333f085388eb3986fefd370dc56b88265e303ed6c0c0c53fdf443d9502d28835768ec33cb110f7d9dd9ad3f01c39d6585f8cf0b5d2aafd279c421edad415dc82ca599c8ac29caf5d6435e8cc7f9a52ed4cf5ec25372c543812469789f28fd8d8d96072da1262fc9b892ca149acd93dc58f18fdcfdf45728238554a64821c356a1ec56461efa9926f4a5e509b7715c341f4eb15237c60eb4c7e2b41340a0450b4d1c0173e7dec2bf4c0a7248206192cc37916e860be965b151587766cdd785fa005d0b20b2357bf7717ef8e240c619e8aa01c627b16de979b3d56cb8191983cdddac1893911d6b4dddee1591f6a01126d65a492b11b074415e2c07590dc3f015c1182b92323d2ef456a1abe24fdcbc12cc4f17b7734be358be389844cb19537b0527a42cc7a685c4bef8e4fa2991f0bebb45d38ba4f3f44cb51b3cead60c764fb02760c51dee1e32a3bb671f67585322e23e86173630c0413383e7335d77df0a96d0ad6a6161aa14a4d4815a3d7f6cb74c4337da781761fa92a77e06934966e735eee1f9340410151d5de5f0247908ac95e60d67ecd78b9e0794afda2c47ccf319ba1389eddd974eb73c23cc763b884de374cefb9060dfa5ffd031ecee0b1119600ce1d2d069986bb1f099c0b036b1962c54a5ab693fe44675bd83850079da9fab5f3a073af9cbc2154779793ab22e7225a272f7dc599386f61ea1a2c148142d37150ace6786b2ce67caf2fb90ff46b99a616fe544088d99d43549b818211980f5e374d0aadc7b38ba7162aea7e785548cbda4ae85fffdbf7afbbba4384c634bdf9f97d2f1f5bd540689501364980a0e47b9e9985154f91ff9407dc2437901b9ae648fa30021fc638ec2af60846bf3123027f2bd07e508da449c3a90701a4536e0781313defe21cb11482933251afbc50b9164c01b75f9327e7ba850d458f7c5c27632f825c9c4585885ba9f735b8ba78c2b56740d6abd9460069c5d02aa71814c5e4010424e079c48509fbd9d87254ec77dc26891177202d14e2a3955c91b46bf28b6354fcc4f99f5ea70983f81cd73b51d11e662407fa4dede007b5248697cfc3e000c01690dfe98597fe37b1b3714289b67ac7dc86f7b77db724b99929429b704f104a80477766dec707148a66463078f7b677776efbd3317a14c9d973017b219ac07180960383c2d2ae2869033e9d3592ae188e082f2c4154c18d8d431ddba278a3c9564aa2ae6d9e83bf2672af98ee9c7dab1c88b8a6e2fbae8e26c36b3b163cb8cc717fa640bed2189fab0042d82c160303aa444049986408623f758866c48b52dd2e7ad20b1e5615e248aa2488b6614082ba5052a5a24d222f145175d74d1451769a92864a2289b55516294a468ca501351f48fc5542a95ab602caed2297acc251689f090cc61be63c23277e46c478e335a98f9656eb1b9154571c8771e3063642fcb2de2d0c762624c8c4df1b38781cdd0bfc83136c4846886bea585c5863445c533994c9c899fcca0cc4cac391363be3391c4c498288aa29128c3c58484a702134a2975efee9eb5d65a6b952d1a5488ff7f57ffcc1f49a3aa2bc4fa081b0c8e87367c17a539b42d2f586bdbb61582a769e34c6c7928d03b27744ff70a3f9a2a5dc75f69cbdaa75572b6eb68784ab8ee5adf29ae11030c8c90439c40ee20ab136c249bcc1df9d32f2cb37665eb8f9b95c8de9de04116a315118ac9623c82c631a8729c4c867f51fc9ab5be93a5a4f87f7f1434e0ac22ee50074ef0f6f696e0cd04df91220a5246ad76fb82c2ad0341e4d7a13e35a48c897bba19a48f7529a791283d294ad143a186784513bc6bed6b04293d51fcc21c9b0a10701cc7b54c06050f1ebe834307997ee5e13bd68e76a45f47aa82ab64085f69c4b2d609a5b4d24a7311f1f0f0601ef9f1d42aab9492561a004a29a53cdd0326b96bd624ed56332cb72ddf0e494e8e0b735a9f0b3826332546b20ad1134e26eb3b6b36c8646fefd4913b3566f774cd09b87ae2ca2cfdaae47e7e5923fa407804d594a83c0de2314390134ab2224231598c870cea710cca525a60c4590b8ad6de60b797b5d676b693373b4d34786a08811ea0943efd1e57e62a3105d2fd218b92d2ffff27219aa064d908c857a8f29ee5d7e56971dc45e9b04baa586d569bcd66b3d96c369a1a96bc49cfa3fc87abc96469562b666a561af8f1bcf6214d9d37ddf6530d431d3434185ce9a8c1290f955aa1c306a3b00dcb632b3e8cc23658e8c3365f0aa750df89357540d64b524cc3bd3c13cde42c57ebdb8fde99424cb57dffcb9891dbade5184b7932cb32c7fc566b6db5542adab79a41a81431f43385101942fce1808f98ec47ce600508db8f47a94f4a653fa5f724b11050f020f4336f3a1f23ab905c1784c8f94ad22709e9c3e15aa4c6aa8ff69161a45cda409eb8d29ab5dd51a5846f79625aa4a2294dd38fb5f7ce94e40d759bb7a1a10a0299705da247f754fc61be4bcb98d84bc798a69fd7bcbd8cb1623725a48c892b8845ca903f73e63b3f676f4428d631a69096f144f20ce336d80de6dc57a5acfa7f251ffa2e3ea890ffdbed769bb79c6ab3b1c1c3906e45c62f5fab1175cd03305d92268d0680182e872b8f2d1fc4b1a4945f721314aad631a495ae8d58c669abd97258695d6925b55cb45aad56abd57a28c116a19f0c988e1065fe020c068b113d293214246b2294e46130a2144e5c180e3da59340c81e2409ba2f1216ac1949605316a1425c6bffe9ffd75abf4787bd300c433725d9bbeab24dee2a56fd731c676bc51c0854bbd51213ee31752c36b1660fdf913f9d65e1e8b85c978d2f9c0dcae1ce22011934f0be1328e528e59842a5505c9696c6a5a571694ca152282e4b634d0a75023f8f86e6d6d0d0ccb05e1ac8174b9c425dd673014f2770e9f34ee0d2d2d228e5f875266b39eec32fb3349868e84629471a6818979696c6a551ca51ca71695c1a53a8148acbd2d29842a5505c96c6142a85e2b234a650291497a531854aa1b82c8d5746fb2914ed5228da9530b3657574e9c7142a855a1a1dd3cc9f203dddfde481cd719eed4cde079e64ee0c4d0dea53a9e04085940d7e1256b9111752a9542a582c52ccdb54e23a2bab54e645dc572b10a8c32b160b9b3b02949cb9f3e2967164b1484d2c361b0b162c6e5aa06646d3e8c511a9765373a52865b1c614681fc78202064ee81e19cc005be31cede346264c1daf7b4c4832d913a427376496127b0002cdac58b0bebf333317df197c71cd6a0e8140175c32651f8805fc64bc930dfc6c349b5846486ee44648b22732d993f9a4a8a8850b9a24e24c9e96a32f5e80408fa977b893f867d8b13a09674448e845786791196bd10204aad8c50743465e94aa03b9924996ed6028896c68064dd88b172fc2ee9e53b646463264b676db368b5c33cc0dc607e01750e9839c6fcb38e6c4f87c25b1cc70da6208c08454abc9c4298aa128abd0aa003819df0c10acab6b309e369231dec4681f37cae91e0efb282b5a2289aa11677d947d93f1998cb229b359abd59a0102673b292e91424c868666cc980142081505a51e4ab2888c8100020821d0a0b63361894b450821844063004ff3311c36680cc05fc9279938138b339c414e3880afbb71efeaecec804016ef002db15baea44fdfd626423718ebe880401dd6010f309be7abe1c9dd417e98e3cc3b753adcb58f8fb64da69b4df6954c9c297d1ec98bff0f7b0ef8f89db4f371a0e2effc8162ffda7bed94d96030189030317c12872d2e32cc185923e50e294676982c5b0ac9a59a8d1c6292a584c1602afc4813c84c79132365bf66912ca51c132583f8a70e8f0e08b3ece15e62c29a3a17e46e8988dc81e92a625ef85a9029b661bc84f1c0b54982041b488211207b2d7bdb9eb20ccaae8a8d62d692373f4431029bfcf8bd10f48061c10d6eb8410c90c01811213f9e3539263ffea993b22c14a16acdd60c144262160a304240226c322aacf049999d5a8879451481320c31898e636d20d2d13c2676ca6ce2346de2f494dac4e9fcba2a13b0400465488d881f375800a302e48614252b416644b622455b84dc18628c727362c80d22642a03c06e298068cd56b843162a4a64b1f242873b933b4a96248cc8d2c48b24ae4cd08b1bdc0bc307395c9a1b865c9413183029306e23123e8c4938398c4e44318a995d530b3831dc0fc90c1fb8dd8a830b19ee69042c48fac105946b734749a221d55e80b99f3b4a12521049361843dc2f7794a42c2ec4242fb0580e6aeac8562a779425885a4bb557acc8d1e10f733f2b28ae3ff247822b747c0a8e2dfe562958560a8f6b4dc04b695ae155c5dc63ffa16354faf3f3d8621d8f2d4b87e31af3a79f748c8a6b4caeca99d18aca95b439dd87f45e36b7784c3127574aa10a787dc34287fb0aadb53fdc0659426e63990f4ecab55163864bdc76f7fb7d79369e9826a960f1be6e60b8af36aae1ee3e6407dbf4e1f50d0b22aec4ed3f6e83aca1dbb8c674406a8c104c62c07de34d164bc96d2ca7c441e99dd6ce1e8e54a526b9a2f55818cf6a59ed58c2ccb087013e4bdca3d216441b524a2965ad392d5aa7848b2eca66559424299a32d464766777766777164700c8159f26122560820387d8d0223ffeec0e47bcd793c58911a70991217f8b204fe495ad159f6007140c65b9c188177e2af5916badb5d65aeb10b533042830a29fbfa3180c29c84e72e068f2eb60554858d48f2d41669828aaf9e1e57a31a2f92166888c6d26489153134a170621b00922991b867cb4930e413c116c126412bf22221d0e2f364f052d58946ac871da8816645758883511c3d145e9080814457028156d40c85864051c369821c1d104ed28880788e0606244e11544a31caad150b8d9b25feb2c094c98e4aa88b1706d6cb872051e82d89e1283a231bb700205930683c18e44d1a202203da50363a6307eb860ee285aa49440cb0b5c50b95cee285a6090e40a102d4ad0aee78ea265091b19b4b828cd224c6389e90ac79de30548dcb152e852d0414787254ee1c76311e6be46c8d415e4ad706cf10a67e998d8aed0f1987ad8b9cab231438a9d5563b278b82f55b70a5f61a68e5f2d17a5dcd13da92c318feee964171e93b58345140fa878078bc96dec49a1e93d4154eb110cf4e8b0ffb77c3c010d645abf8ae90a02f36b4fffc71d6b8489232d0f0eadf4516b25eaf6705f570292a7016d93ba8bdce188dc12db2c16af6fa4cf8ce57eefb4923f3468cc90051357e220b7cbafcf7d5af54f25e276585610d79faf62ae9a2a06575cfd5692c94a12cdb062d34a369961ed1d8e533b9b7cb2c4e7cf4be6563672a1262e4dee23159a6489714c706b721fa9600308ee4cee2315845ad0eecd7da4028c862bd325044209181db81f0c421e6e97fbc8470b4224d73b7231c68710889323372a37dc0f071832a8618ae8c3f5721ff958c104b7e63ef211a44596cbe53ef201a3b9a37499511bb8a74295c245a3e570d16834174d872b878b46a3f9d72e1a2d878b46a3b9683abe3f4551dfd5c7db88aa48d009aeb9e3a2d1683453cb90728a0aca08e1c4613b2146feb8bd132de68477174d872b872b29c9f522486fe58438e10c1180c879e9fd2d9ba391ed235bc68cc9d45d8a31ae5f07a8073febb2b56b1b756e34da08a12bfc2b8e42152b57925cef2e9a0e570e575be92b9df4d2c33027c4e9300c3f10fc4cb7939886735c2fbdd58a31001f12bfa40b395c345a0e178d4673d174b872b868341a0d575351f46decc499aa2f9c2a3c2fec7a2fbd7be68781a9232fd05d5e49ac42a680219f913c94deddfdffff9d524a29a50fb8930419ffd34b7777772ce54bf9ff4effab1e67b2de75d6c479edeeeff56b777fa7a07cc09ffeffc6656c58ab52d15a2b5d514a4998f7b3dc8dbbbb4b4ce9af7000e4631266f84e030306954c316d3c95f3a8fceaee6e4b6baabf97af1cbeb057ba12bfbefca29452fa2ffbcaa189dc8ca59452eaf8e5b793c194524b29a59452ea81df077a4aaf97e9a8dbeeb7335fb9ee6a739a46536b28eabdc8eefbfa724f2f956259777ac78425ab9b6567284daba69452fa1ed8e0e1779f3add3dfd294f6d7116f5594ae9d3cad98e524aebed0429d64c1d8952eae269e5a03ecbd9f99da9ffd27f7de03bd269becc75758cc63cad0f3cc9dc194a29d8dd327786a60675bf199aff477dcdff7f8f4acd7c3b5d4decdda2391da331a594524a2b4d0d8a529a9ad286e2d777b516d72a3357ffdf4bf1fd972b255dbd5ab955df2b85faff5e2bebaee28a1072ba7b6581edb66ece4963ce3973e6f4e9d3e7df64efeeeeeec972ef6a55f2bc39a7e339fd954657ed7dce9f52cee67467bde69c73ce39a75fd895787e375783faffff9cf596ad7f73fa4c25c5e0e7513c29ab398eda315f393a865677ec4febe931f8d9dbe3563a660cc5e17dead0ee6581ec983eeddaf5ed9873bc9646e7bd3e98d8c3691f1242fbf4c6f3f0347926fbada6d723db9b2debbb9084d0860b870e04341696bda9b9412c83db08374f4b75c3803bab9532bae72439cb8194b21e012d63e21bd646a57d35dbf8367aeac84a95bca3bf338060a6cad10c3c7490bf07c9165b9e34501d2141fa37412808216a803283211b55e194ffffc3c712875454ca01c8145b8aa4859b2e2f9e40e7883f715552c5c95083235fc28f5a95e106a2039951f10521b38420e28b1c3822dbbe8d1995109bdd2391680890a932c00c63d783aa735a29ad1fc470054a7e61a32b5492ae8c30aa5c8162063e821a1331519d1626ae787de3b5c91b00305c4e04dcea9e1499c33cddd399c31cc7610018b935b738dc608bbb2cf0e3e66648cb6a750cae95396bf220777409c575b524667f28e7ab1a8ec3e15fcd9793c3129ce1309e3a26dce18a2f873d0f3768b2d85911086fa898a24409b2ad768616638badef38b6f8face636bb15d7140826bb1c53756646c599cfde1b0b5788696e369cd90c3ab2188ec0e5ead153f9ee1062270936db7188fb0a0851147a0c0d8e182b98f8ef03074bfdc4747720899dccf7d7464098e0bae29f7d1111a5ccce0080b2e5e7069c2e080e0aab0d4702dd20b1a2ee782704603c2881035301244d1f5dc47465210c28c14d9c8600829bd69fa899a4f2557396a6f3a4085bee3d66788ec58eeffff2d27f32f4ff3ffffffffddddbfcf1de3ffffffdf29a51eedee6e53edb8eeb6ffffefee9cbbc9762050e37777f76aa25e77ffd7dd0efa9ffeffdf72efee52e6ffbfbbbbfddd5be604de53072e78b2bbbbbbbbbbbbdb57afeeb2ad34cf39a7bbcf5ebd26276d87e7ffeac5a2cf9a33ecd56b26b7cb4e8b27bbddddfd3bb7dddddd4d83a3ddeeee4ebbdf7ac773ff5957a652777759a9bbbbf707fa40ebfca4ac5fdf7274e94afcd57274cc3fada6af6368fda1e72a1dc3d9aecafaf5e95793f7b4f6f7e0385a9fe3f073958e01673f06fe7e4f9ececffda9d71e88b912d3ff80f5ffb49a68db5775d8a1b5d60567bbdad3e451295d5c29c1126696ac3779ef792b5f1779deac6eb735752bef0329066fc870ac578ffc2c1a13ca4df8815352d938373ff89dba6350fa2c21acb5f7fe0dc2ca580b42ae500d342ee46082792f147102222345e6c8080c34b2966bf04f1d1f138d23188e589c2c13832c834f2090cc494b11f80211ca046c054e6ae311413c61e445952317828e8488744cf95b1c872d132d20c95c01321d8283231686402ff2a53c62416c2d31edac90447eccc18d122b1ce5aaca15ab00816a132b5635638523f9710a10e8392b5768e48a6d40a07a332b12829d5c71aad66a73ad15dffa43ae18d72752a6a88043814095e21a2247e4a7921fd38040bf82232235cc1459f98b45e6a60643029938a8d4ec5de88e1136400013170000180c0886c3218118071339cebb0f14000f4c783e5c5c2ea48bc3a1481003290a63200662180620c410630c610632a8ea08003bb571b1b5d28e6ab92538f456f188d0a93bc2db4e411b91dfb7b4856b84d6e480282c8c87b213b860ecba34d473f9b735bbfbba91cb070e27583551eb3da938d2af070a9dea78e31d45ae15bdbc4b472bb0e231cad3c4b1669cb35b5de25156c939cf179ee56638e10ccfecb6a85f71ca3d35163060397332c2b945e7be8b899d412132c255c1f44537de41fec2f8aa318cf66b4d8990abdc9bc6ec35b76dde7ac80fdc36a53e9d587abf2f40e01e3970815631636c60ab0a1156e1d4f90f3bb46b4f06b1e5cd616eefa50cb268764122a6fa3d35d117befc737f84ea8b99db2c2b047e2f1df8604004e03229edcb7c70a7f661750314339dbff564fcc0e9c9a593a0df6f6882278635bc7ad7f242e6bc7fc8b650b4fe4b03d8bbb5a6a20cca8cfb705ff2d320d58f8250d4e15356a630b331527f21d78f0b09784f078618b31f88b506c5d3532bf9d045cc023420f54490e05374bdc7cf38d30032a38b8a40b693497c3e92b0198836f00edbe47f10128bc16a040f0755a466aa3360164d6d76a313e9451b5ccb67e7a33897cb09c30f710a3d73ad16548444cb207e87903444ee736dbef78077cd2f9adc6e9d6d3ed530e1a06a5634c4d4df1e24d7ea482951f4d033d2e2eddd15e64dead564dc9fccfab1fb39bc2e9c18b095510f64e817100070a66a3277ae0f11241a121fcb9dae7e9388d17891a97f1a180b4c38bd1126224e5b5f1888cf60c1c7c3f6c8dc5c77bfd82b912b1b53194cbb4a049af1ed9f20d4ed3bb4663028c538739f5839151ced87d66d92105c67058f6ac9c9f2c29ff0bdaeac1800e4455c7948ca6556a6abf7ff36e1a195a095bbf4daa9614e0733785d569c6900a7456c9b1520e50b3485d0470d4c4ffc09715281b1227c1a850f98634100e43b1cb279f1ef91e2a0a7458833734d31443a01cf2af6409bd49ccda11d47271ae8d5ea0e2b1df7e0d5b9e358ec19838921dcbd474a9b967b9494b4fd6918deead420956a79bbc567fa0f0f1736d0a0c00ab61bdd61fb4dae7c1c1dd7fc32c05bbc67e5e4884425336fc30b1fd3be75c93ed4f1222ea65960f253ca2846d2c0f4646142dee13f2dd0a4587877bb31b64186d40d1983b70503c40c61f9fc363133d3bacfb6e5a6d6eb386ba0c6d09ed88f1daf1222abdd5a1fc352bcd6c50ff4dc9dd38c2c09b5f1fe02413f3673684382a1dd44ecf827e7fcc956669913b75a0ae217bc764f8efb0a243667170f49b01417109bedef089cab7c89c627a9f7a7cd11493dd61ad6b4764e46300cc5ac8eb08f3b448148dd44a212f5437073bf71efff21b519d91fcd1729f4fc7684d2824d965102782691e37a21667a4a99c44e0950d4c7229be3e1faf9d1031a03029070b454a7e32543bef3069c915bac9b164dfd320658d64a7573dd8875b1376feb28e972e1266fee6bc5c75bfb808279c340a64e7c16e01d88954b8a5e38e6411fe04adcb71e5b9b20d20001d2c3b3489378b067930d9d171d2166ce7b6059692dbc6b14fdcd90ab2ada6cbdb63c0177a94aa6ddeb62a3139d49bfbca036d955ca5d145d29616274e6074620e8250891af575cbf83f6842ecb19b97a1e8c4e684d010dde9685cccdc21916e103fb0570e37e309a892693f9af27a698227b21cf152c854629ca0b34cc9a11a5b82278cab805ef2a2fb4aef2a3de8f203ab31848683d4360d87393e574bdef9e349ee0a42f7c1e13f0e620d005ee4364af3269467a237b46bda1e525da1d3df2495a4cea457a4b00acf0597da411502a56c7e9d3cc79a3f1d3096b2ee52a95aee1640880977a17450e8546d5ea6f0808b0c8f95c8815b50ab034236e2203c299503373d5acb9164df38d7cf932b14842ef4986b174a9b2e4b87f125d2a8ffd96d5517aecdc05c5158177925257489e9d90bc2cd2d0a534717bd0b2e8f6d4444f5464642f883294c9468ce820cab0c1d66012be6bdc7e47b871e7ee9bd53748e9f69a75a1895a1ead7aaf550686e75ceb529576945f464141c587a4cb3204891a18e16122232c485208b71c93edd5ec52daa140c96dd4f0b30dc7845f615e34a5a515e7cdbca068b8b1767bd0a3711205ba86ee9d3466d51622022cf2c34f7df617bf6e2ff800f9fe85e708b8417cc4e8943b75ab91e2f0117867ceb8c38142b34a2ceb90167101b8de441a66b77fd6e96f6f826d3006654bd33989cc79ed8582d574a764fff390e2e84a2aaecc89409f8dd638dbc1c3b482b9ef0f0c0505acaa620c53473345c9b22df075a3130245c7c962263f3b1911645aacfab545a1874317034359eccc515a07d2021d340647c1d7cb00555ec111cf988773c93042087c75c7cc67b354c5d2b3325c5cc260cf05280f81744499cb1bbf2556b89e3866e94f2fdcd9a112cffc07d51feccca81b3640ba1224109303432c4625229f5e8d87c620118894ce78261509d979c5a7b1285229d5c3cef4e688978c40558fd2b6350d0c15359c7e58d328a8326c6eca1802fce969b51ac6fea73488359b3c192ff8a0ad9862a79f2cc9463e5957bc0008058878fb6f122f4dbf4b649f0e61cfa6520f9df547ed9cece0d4cdfc2ca233347ec2337e9d0bb503c31b3c221c7080c828dd2b5439409105a0ebf1bfce1bc31032d85921c6443319c7b75c39cbd2729c9d510ddd074fcf1c422cd6adf9c80a7c3f4b091a699d1e5bec8e3bfab5201c630048b68043c31f3b66df91beae21889687aed006a7d745e82c50943cdbdeac4f3ebbb56b81c14922723d8f1e163ab24604b350a78632168fc3e30a1ff41d0073f52684cb9f089405181267286c475c016b33f306eda46ac0481d4fb529ed3c348860a38e373818b30e32f97b8bb124a366264276c5c5dd345b9b1fbede1ee200dcac8e02e2fee8dc9537f0c9e715fd6f6554e15ff007cbcee52bc1cc1e088283179a5adc121dc5e240b593518c8defdc1a41a12ab8ca33f19833ad77a39d0d80c2326f993ad123838a40757650782cd4660d175a2ec5195c84a008d94d24a6c4280f6eaee278807d2de44401291366e1ffb067dce109852b637b10695bd1226258241652a93be4959341cb5131f29e57c7ff4410f8551044860963b1130428e467c38a12858bd45a9504565d01f6ba0c901a2b90acd357d4e0094e1bc5e08ee580141088ea9d41bfd321ee81d8b055e759ea417e1ef71aa9f3eaa2a9d730b028a003cb997062e0cce01c6842ca64c94693d1a10ef3d0ced97afc87991acf5cff9fc785f2a13632caa1976a054221de0d73272465f2ca3eaf7f2be160f23a6a61b8a24b5b6b2283ea1ea3bf40f20c75d8de09753543dc80730bb09e6df2515280c52e3a4d0861da1ccf871f04f18a15cd8b00ed03fa5da1a527bf617a5d1e071bcb33e5f66559a8dd31801770a34e0acd35cc1882befbfb885f41bac8ce8e1bb42b18c16b5d28ac308b12d90a1c20f01c405c8e8f5c38a8e3b09bb614d92bfa3cd07b5f55ee47073e6601423ae0d1cf349a2eb1a6c3bd4043e8b156d764571a3b1eba08519a686bc0ba4ec4af54ade220078b463b1cfa3f5051854869fce246e841b834ec0683c589fb5bec071f3642bb2188dae89042bb2cae7c6f76cd42d1054316ae824c36750f883dd77d3341c56f9837ffa0c86500de8a297ab5621bc4162ac042093f2349de0dbabe6e93cc8a00978113b173012111db42b857dd4c08cadcf8a9f89443f87e9a8e5f46a5a5ea26e805729e8b8fd2053a2b84de1f9794b99dbc9c076c166d61b321b68ac2ebae1343e839d7436e9b27873c26988b3b56eed102b978223ede406d382c081d5458164edaab6c6da1407a417eb380dee5721459c8e641d68a30319bd2ab71c5487b42b07d183e763c98001f6d913e2f1a2aa269bbf0a462c8c10ac7825ded2b39c74d3e2f344cb2922bc3f61506bff128bbbab4e6a70257b0a5b5cfe7ca82c838bcdd291a078233da2638400e2b1a1a1e592ca09c584dcda74a0fd30ed18dc5121838921208750de5ebb6442a13e42127b3881d997329bc0223bcdfa30a29430c59b430f5731578275dc7a8b4e50e2e77d0838cf293122a9a680197c2e03cb49b35d70cccfc043cb5876e4176adb22f4905d8bdc2cef495263851d456c36deeb236c50b70cf770b28380ec20c2f6b9f9e4108759127261bf535613e42e2a3767e140bbbfc703f919427277591623c6141a4f50583eefd812f158edd7841af0d01316d1bc3e8631a501883b7328caf40244e9f005bf83c6a7512a4f51ee94de344602d96f05140fd769a3c351ed80d9bd7030dce6b9c0e913988a4f0b2db1dcc8feed0dd28253b91e57d19330738ee82f9cc5890617a47ad0bd3d229c701749d200d2eb8063867707fb3466ae9a72e7bec7df4e2baf69bf5f6ea0de4072a8c3280e008afe7617cf388fded2bc9411892cf9532963c4d4f0a888f743a98f44ecb7fffba61941e9989f0002fc604a06a4c00562c8462ae47d433040f0d1c90891ce0491294c4c5c985bca61919ba3127adfa366db86b6ef2433397c862ede49572217db7d3b21596af8996ab62446dd79eee7239097707ae3610d7deb6435c9bff8e6d565eb652904d46badbc1f11e393ad78ecd64033bb1594921301dc629dc529fa815df6601c1eb1fcd4439dd9a409a59c8bfba4990940a014137357311a94a5e73ee3ff18abe813048a9e0d483926f9bd68e3d9e5490b35140b0bb5e31ceba77ff8396bca321f38b385b902f493c3a5b5bf7e9afcbce86fa1e7cc3e297270f6a68952e9fabd6377b0e946901ad59895ef6c34dc4aa56a899aedf3fdc0d14d616fb08a5ecc89d2b333764c38a07faa87460d064f557512717902b37029eba00d92a44844a117b8374d5153c65346b3e22ebdfd1c20bcdf030ae244c1770ddeea3a86995bebcc219107f897339b5269ec8e7cd46c53fc1b0521b36f3d7c94db3e2675aa9ff644b4d8ec76b75d84f9f4ac72cde4a7c4e034b2107a7081866484a7da76656423629593a7ca81c1e9481d0d41967e3cfe00b44451936e111ce5ac1c2c64bffe56c05688cbd1a1be7a025db666b0d717ceac7e7f6dd15347ba1ea26f8d3221e68764904a5d1ac3792eec48b8d652a0e3fa21a4eb727717638d83d3043b3e7f4f32560439dd21b809d36ec81af37ed0b55d1b89a757f790d1a397846d84d5b46696a3bd6a6d12bd68ff7851486b0db7e628759e7e297239afcd881d99ed3234e4a13de9a74441b28c7691985d35ef3d06ea8b388adcfb0ceec20831ada9eb4e3f7e7e6dd1af5c29b9897ae57dba4e4714a3a5d391bb49c7672602934d403e6b483f49099910ee98983ab6eb252ba4267ebe742cd6cbde53de7b04cce4cfbb227c4dfe72f2d82c7d65eae4a7e3ec8cbe9afd0f97d1a2f56a1130635012ae1222b38d216204882ca83aa80131625dff0abc2feef24b82846646a9ea9ab2af577da7a720dec7a8afc11a70798c9fcd702503e217f133a1557a6aeaad46f3f2ddab59050e300515f8e4c9bb06a4644579f066dff9407293003862807bcb2caa5d618349f7bd81299fdfde77c1043f7777e7fd5a0ced70cadf7cbce5588020a266fc1fd8f6c8ecd0f50d3d056eced0e95c5e6c00d8b4dd0448adacaf0e69b5c067607177c61748bb37c4ad6f02eb6560146352417555edb29d5d1616a799ee5c9fd5c092ef97f9bc92646a9ccae6bd7324de5812d5d5ab96a29b07aaf86a66ff0b7e4dee6fc8d26a1e92e2e317991507a1bd22201fd552177d1e7addfcac2d686330095d4be520e15e94cd0903b5b9b1bbfa03edd82dc1871c9d12e8ca1d6a4e6fd337b3a64db7fe896f2bd2a525899bbd62b6770816c21cf0389502280194cd9f91c39083da68306f8567e6f70adfe37e4097225d8a57b8edafce75be8cc88dfb0c4118ddc5df05c32d406edc8777dd0e459e4995d5d1a5020e38fcc2c3047f134023aed8328e7293dd1658bee72a3771b40f9036cf06c8c644b5e5334dea15f3e991bcf92ed441ed4081e5a1c1fb9396dc9da999c6caa5bf237ad0468633090dadcd9b6eb53a635d3d92e8f572c9cedf4076e34601040c3797fd1b619c115b48003698612dd274322a0ba5e6385c499cdcd04d7d96aa41bcc5d958f03677d18e9e0c858c7925b4147a189c9909f24b62f1855db48278587d726238e474971208fd2f23102564415f1621c31b5ae7020799b151f659769a7e71f01baaed61567404a65dd1c2ea7adc9550aeffff50292fc1161f0b104cb161ea6a704aad0f460b244c109e476304daa3c2b3898c466e574bb6b08a5308e64c2e194f3d050d85b81ab4219fb074758b2bfb847047d8dd29fa7ae882752bc52570e8990732144fc371222dc249cc9acbcd1613cdba85355700d5243166ae8c798de09341d44041bfc8e87e8087af7ba75aa7d51eafb78e09bd1740d58138b81f0242c2cb2db38fa7da17951fa94523bdada8a641129395ab908721a50721ef5482c106fae070630c3e84126f2e7026e2cac916608e2c9cde19bd584076d4e58ebaf2fa0e416a30058348322c48336798a719bb8ef477d4d5a8106d437baca5adcfb22ef9486b7ed95a21fd11f5de0d52d56ef777b0f86faff9bd024772878758fe0ecd4b4906ba855af06d2cf03f74d715a82999b355edca070660d5f4ffc81d5cab272f1f3f803edb52dc222d207bef40e0751e28314f23996faf2f444c7ae53d6193bb016f9082fe5f724f903710fef65f740aebd792c13a7a45a1fa03e6da6f334c5258f63d4cc66950ff56003518be09529c1f3f94197486ea0ff39e67c4cac99904fc4d0baab43c29e2546c9c013d50cae6f2c49f67a528cf309083eb0c44671c5fe34541f08090dfbbc6544a650d1c8db8d30eca03e0eae5f27e2cacd570311733551af20eb8701fb885d50e9838f154f1daae4482deaa00119cfd741fa0953135681e38c633b0c35c4b419830863958d5e895f8f14616d5c7565d4fb6404727bc2606092825728b4865a8bbc548f55954552e1aa0a4f03930a48f64f8bbf242355c8c5f7a288b81d4ca01020f1799afa9cbd4f76d8d52651e10956d0aa1720528924bcad9e97446a1dcc2ecccd12493477e6994fcd031647126bcba93b6d09f80ef115d1b8f5652778a10ac4639454dfabc77057ac09f3d69b3e2998a63d0b5baa4fd16cb1d8cfb8f9f8fc496ed431aa7a5748605d6f74eb7c71aa5eac94662cd0a61da51a3eb3240a173821c467965f81154a185713cc1d94427a9284467750a918345fd158c7e1b30910b84dc5b65d2f8053252b006cc75785abd70b4263d62a80233ab8922ae393216a9d8a5674c12ba44111b3ccddf2a7b02e22f6ca7e5ac943ad686f0a4cd76c5a826fe61fc45a676c129b8383021bc4396917a802602b4271f407ab4978046c92c1ff39692fc6bee0e29c46987074c60de4820260195e75091838e8dec05f427593b0dac948a1d76c265f0489d4f68ceea602d18ce71506212d51f864e60c1ce9f77eda39433b7bbf1a0b2f280cd9d524392b4ff0222fe8a62acdd6b36c2c2a64c07b946ad2567cc2ced2d0d9e1e3d6a56184c10712988631762b92f137d3fced2037807fe6d21ec72827813d073f828a6c3b5bc573dd7a60d265e64dfb7476ccc961827600a891e58027bb7b8f044dc299d4bb0435902ef03b31fe65845581c192be887c17cbaf7e9ca897c250127141ba4c5f76c0a3c422999b39b7b52a923d0870078d55058891277ca6b3504582dafc146418a77c22c0dd1cf99741164506a70169f1855135760b3494832766a53947b33bb40d7f656b8caa6138c4e628ed40169c036c4e3e2386ee31e0ae55852b5a769c78a6e2a9f7dbb6141765b01f0502955608a78a7098ae16e75b19590c20a47359c6a144545628fc390d1c8596b8eef54ebf11ec2ed84b0fcf34e784a65d72f7a592c16cc4b693e6e14f0868417130192a5cf2323aae6c51d7cfe710678f8c87cd61d4e2be5c98af5311809b45be29f67eb5e30d6c9c6a02c5b5b6f09395b5510454600f6b673a21b2a9c50f177cafc02a20c63e23d06e898f8fab8fe0a693c8d3eeb800d390efedeed045a871110b5cdd158c8f0a0b82adc973fbf98096d0ac01dd8183ac7572251fbdf2f2c71a8306ecccba9b430027e74ad1f5eb755152d5bfbfd4ca1aed3e11d7b10e117f51da6662015f828b08a8c16f1168356d79a7a3d2611fe5bc0f0876129fa873f06c5dd54c6776ce2060883d41e41ca63c08141eae13a884e2059ff6a73ad63e01c127a0da3a4587e5b54ba8ba97465b216b997b6dc1bba316196c69d9352b10bdd0f94684bb6e95670ccaf6d5002d915ec185c8a6def1dd1194dbb7cf828ab5f0ac445b23a6a9e87e407069a6a5a8b06eb13084c5ba02448556f6680497be2e7fcac45743a4c492b69bf053a5aea7e28ca77b1cbb7a63c40ff29d5230f1470f22c315df38e99a6556ae4e8b6e8c39c366a8a96fb8bb7a8a264ba12d7e3d0bd689b4d5c3e2267adf52fed6ac145b50640ea6ce06b3e63245a4fe4cdba64454b0e182a5e640d50f39cfbe1e064a5e0d24fb759b7741350c8eedf6a4f64bfa7007bcec918f2b203c8dd2c7f4285f9a37b3e49a604ae703eaff2c1c59de380583e815a07585faa489448890bca0092ca8ac6fd9f9b93a9ce647b864f8213447ae2d045cef5568e5fe7c0229e33a56bff2d42bb75d1fd3acd9cf60ea464fa8aeacbcac788d094219caa9bdaa04904b148401fe2f010c0b5698a438ce50c7861b0096fca4aa3f871702dfae180c8249f25abacb8655612d1449efb10dc98bc1665c2fe33ee7f92500c2ddc59418083a4343a996525c044ee8edc0be298bd13f28be8561fa401dd0ad150530afd1108238889e9f5fa64f25a71bdb08e8d108e82888683d8ff8baf82b4f1709109e927612d95719e402c2ba0b1b9fcd1fc5bd9fd9cdb18600a626bbce6dcd9653a84b08eec2191420524f6678dcba412d422656b898eeedea9d2f2748d57c88b655cc3c31ed054a4dace0a71d7b4fd51bae7752b81311d65d42b7ede5e3c477845f7a06bffbca4b41e4305ae1e4c428ab7341e03af9a0b6ce8ee438b39ce09edf48025528c338b8c807b737080003a0884764b972dbac5fd8a1d196b480f6d5f1d9356a206a25ef7dd5504b46973418090c0265c30072b3ee3344a6f18d3d19cab8df175db8b3890d88d12cf097ea8e2f1543c1a0c06946f556b3c23781df1af9ac3fe2dbf781e09c614a10697c1096444f7e0da43015471de4d2bad04eae4493ca8a56f6cb8a4cb78c10fa4b138451702ef10394d19d2319a05412bb045efbe89a76e72b7dd9a4c0de56367ed8b2fd1605c9e5a4b7b5291a8cc992d2e2810246c8f747b0de7f7992395cf66dc180ea9e23cc14f5a55945c28e822cf1b58e4c7240b78b22e4e0973cd5726ce60fe5d20eba9fb791a036c29aeb8dfb13e22ea42418b06fbd28566c9746891fa8b388e8791c48c000b868181b06d06376ab1824ab776fc08c60a26c537d49d1f65d2d4f6830af29ef5b1672d0ef57c33f3fe19588efa84b41b71ebcdc6c474af7db3907f989235fe93297d39a50c528769dca711ea614a4f773951a3d3f6e1040ee7df39d6e922ccde0e0a8ca9aa25e29e03e8601a4956f4eb518588b727f21bae53405946812e8c89a32e8abbf7452a6697e78132994a47d69f6901f05dca76e278121f5dc4ab2f9228fa2865b47e93564094e39546ba2dcfa9920b84e2c40ce523f8f5dcfd91b25729b4fdae63ea89c7b241e1c596999fcfc03575cbd39fb0695d55d8e5152a58a1597eb72981311e4b9dfa368b8ca33c78977b54d9d3fb7525162866409962bd7ea34e201131f8f6c21e9a518e98fa4668f1dbe156cca4771886439bb43d4567ddfb34cb9dc318f3731c24f82df80305d1be941461806a6670859c3fbd984d197d53f253b32045021f40091dda44c0175408d80b235f82b40d708bf5420b6e1612c43bbc614c394a8056e818b82a0fb0645e94fc71411c6067aba2cb208c3b420e1cad140f112112a978947b8202353855158fca35bb8a2c201fa1ad233f64e8891cf0cdcf6a73b12da81546f188eab38bc602850d241a538514f839d01ca624d5a7b05496136a5a4a80fbf105a3303ded484e83f2e380605eb4162826e800fba03e24ee8082a33e003748787b10d426d980a89f21af41144c7d85cdb6af8d15a60a5819d31658226128875c0aed86a401109c64a37565b310a4306ba89614ad0d895cc682a52dbc0663ee9845e66e46c5ee09542140406e70a9c80b40bc92905961153b742fd27cc0358f9d22044411da85bc8dc264052b8d13e3be0bb9fff5be8dac7006c4165f30f349692ce70d6723161e0a716c2bdd6c29cfeac2468d3c8e81bec420dbc1f82849155b98a6500cb1db90c2a13fb8237578424368653da4bcde9f95bbae556858a874b739ae2ad6631a0955ca9247614b59e083784a6d777e5f555f9b1a0490f623a95dc651ff8f81ad093c29d208a832dd5def508bc040bb41d2775fc1bb80365efcc7c14911094c1865c6b9ebb71b58c02552149d21db10dc93f09043d9186996c0af98f3ad3a06713c45bc3abab2590e318aee9e0db39a807566fab1a0e17d8a0062d935d08d59882462120599e6eb945e714a131af453fa70f40584ae71d703197676c62b09afc5bec59786a72db2657265298f89d7f3d5d029461480913ed70caf81d9f31cae1495513ba0b39451a8591dfa0ceadb989d6036971f24c2f75d36ace8b8f298413dc459b9a7c74ec1f372833b21cf0f88500d4d61d930d0de98472abf6859082f9beab109e7b9aa24f80f67e2d273ff3660ac2ac265a037409cf83bb723840be1dd83a47b5946a12a07a3128448876cd0d456554e4a2c6d211009aceb63e86c0137ce21cbccc84c23d59131265004aade2336d2fedc26a1c2a9cc5aa272fe0cf5f33d27a067eba590e0b719e646292547faab164bd0c24d5a85bbeeae1d4ad72155f40cd2c72248bb0a10bcc284379c751a96a5acb54b027bdadb032b2ca0a54aca352b6b0921913fa0e7a0618c5602ebd6ccae333feb8fdc343f2d42f3aba3f411d69d26c3c5004bf99b3bf535877cb58f101d43d93daa296a94bbc8820a61569671d65e61979eac9f2a86a2ddaeb501fab41dd543c742da0e4527f04000a1c48feb4d10bccc079c4b36344416ea0948dd4bd7f0fdbd682094910ff468616913c01b2b07850e81eb873128683e889d785589c3c0dafbe61b2661f1415db244c18cfac9e30e86774b2b19e0508f3d687b4ca86613442315c42f1244817a7eb07aebadb654b3433e886534dd3839a6da9a58dda0e68b37dce520c9ee66b8fcb8ce6361c8bdf81d950821ff46cc7b40f97ea5d537e578859378357420f722a3420676ad204a8d619948677d93085936cf314a8f5bf93fe690c4740af777268f6ec35f0427b45f3cfa6da2374ca57a03ccf6df6b1103388cddb623884acdc4de051515d958878757e1125044ef03c9ae121ed95b353b00efcad93c6dde70bf12a077e7e227f01e2b5ebc00d0e0bb59617434cd40503490e9ac80f32265cfd297efbe64712357d5027a021f05a6c62081a890012bdad8a8b123b1a38ff706c0caaa50290c5bab95294c4c5c1731831ce932ee11a59f9ef712a080343d08c527263286cbc0975a4965e23d37c4b519cc4a05873e0376443fbfdb0e5d645fab842e6dfd6950389cd7cafb99818b709ba10eab352f3946bdfbf381cfa0e644df698a535ebbb98124d42d12d2b86dc702eb21902186cc0ff88688cbe46f1be0263ca1762029b85414ded2ff7010dfad105920f77cab0d7f4cda2c2630fda6d469ec3b904dd1ac5de0e623cf6a8cb46c709016d0dac2e5bebf3c0e458bd6203578dd9ee112a57b4915149866d828525105ba01ceb9f21da4d9c828f28eb8e0f5f877eb362c0bda0c6d117727344c2367b10ee3951aac6dd98d2759328f0ca6a99e7ac7d20f361fca569d8982d4044a028dce1d28bcf18d548cbccd50900298914726dd6b47200331cb7cc8b519061206db8b39a0ff8b8e6a261f9134f23cdd44adfa5e30b2c7fff9da6e6c6bcf86996a6535df054f3b58029f557e624d0d08bb1a8ddc1f195a23f4ec51c69cd7ff81d4257ee3d52abd59dbf4e6233611d14d001dd26cd1b077ee3c5f24f278b34e8fe201b830da21be340fcfa2d17f39924e569830e31f6d2239f7a09e3b9ee2990148656787baab78b42418742346653ee38dae9da3e2d363052ff6d704c7f667852f25862069e583fadbb1690df6e4be619764b1ae7202e9b583683ddec99b2fa1395e3271676b68df627c67d35d2774a0cbd91f65a021fa06aa6aa0059e3d9407e79ec8fa2941acaa6cf0ff4af99226a0dec8ebf9f3cdd36b099eebc434b9b235a519367d2d220fc2b96e065d4a348fea9e6cd2523c3a56c2721aaf8f51bc270345bae13c2da86b3838b8e2c7d708473d1f2729958c95aa65143a597c9d2e4b74eb82a6ed184147de7dc465c2e2a8a84ff5a0003d1e1fdf17d1458932a442ad009a2719a2bb145b3baf82c39858b5ec5ce8674599fff3828a48a3ceab0df7e9d86ff04d6fa7cb6e6bc031c2aca0b7310d090d812372faad95c09b55f28162f13835933c3b46a998600e87588ede951b513b77a1f43af5ffb1eef6801f4b93aa00e362310a02ba26d2dced2754a19156d7d7c246cbc939a6aca78ace58983c035390836758af77463f8f9fc349cfcc27964549dac7778a658361d84137ffa7d46c4518caa2dfbe738f7629abf505936121feb49babd1ac44ce3275a63fcba3c4c6b0d3fa99a5baa69dea226ee5b6d4fc5a92af6a5e52e97b135e54dc89b7cbb690a93b5dfe92df775bcceec12e88bd322e63e7200d213a0a9f3c4e92d950d63051b65ed3f42132fba7ccb62d39d5800f28158c2eed121b3a17fa7d1326584537390aeaae48f5d97cf95d3f584fa274acfbbae2a5ecfaf0ba6748afcfbedefc419b62b55e2efb49dbaa3770d25ff99d2aa31c67ddc7d5ecd8f38f42369502c3874e2d7beddc255163b98374571302598979ad8b2ff1524934dcd8f226717a37680e0765d434671d730f546be0b3f91a17a78e6f5c7c707297950a768ab7014d39b0986a31bb9feb2d403567f2fe252cfb926c1d6a20eb6f6e74ff8c11c7a06dc871c0fbae4c48bf59aab81557ea515661f236a4e8dbb07b38f824ffd068b786666002b7fb9b477d457a063a059f9a0e0110363897dac74d98adc34b3fd1d8621b28f992117bf00979eb09749c50e2774db1efe390935726f1a32da030e7f717da7b12b3c375490a6741e9edb81045d994a0a677bb87afaa66c4446bf55d68a1a11fa55249e671dcf97cd664ae851cef4552332eb1c76c1af25d796f495ea10cd49ca740a95dcc4e6e3b49e899053d542bc8ffb8441d335228fa17ed3b5251a8fc3590ce1fbdbe02135e831b30becb6438614d5fda70e74e730777331f43efc9a63348eb8fd7662b0d97386d002d8e06cac2354ce136bb82b5619ccc9f85df17f630c783912085bd1e259c69657249d72835db4005ce694427d4f0d5be59491c8ec4cf0112acdbb5b8b60da1ebafdb377af7097684703747f45f63db5f62e0c5403b497349d457a7dfc1f1d72dca54b40d38fc69f86c24f35f13891fee5b466f6821b4a0472dee3333a8c5d5ca5d1f0c18ade7cdb6932d9c75eb56ba342911760349dbcaf84a98dff196a9ddafc2b8ad6d784da52b1461198fcb2e318f71580bd217b628e8a119fba34b2219504d3b0ae0d39a41da448a85f2474f35462498f85321aee754e204d2e05bbc1e0d4fb167c85dd27583ac18f7ef81268bd220bf2b682b4e41fab524c55a78048aafc06934f9c83d641afa8c4e42ce80445762e5b3db843cc681229fe5b7330b50797105598237cac3a4235c397d81e0b6d22f4cb7c9c9cb341c1e7b494b9cd48a263b27f1cf1945ef0b44f4fc8c95ebe4fa0e6dbcf663b1693d8cb636dc7d2c4ad5ebef3c35b861db3d257c945e2ae536ac32de15ecd5369b8f1a254ebc5f18df7026cd7913706734a377ccf79617bf81b85f0e36776db11cb956dba76101a033d42e33945ad70fb7f1c6be0b9af58444c7f1943c54b3d485ea095feec24b208885e10dc260a5050c889fb21b68280aa67910063523b8ef200ffb1397296039e22b5c9b46cf1f48a88ee9c9200de0e1b98b8733922c72f7dc615f62d6456927a7517836f64c98143990bba0905980acd21df42b0658eef1e6b7c8602fb0ae65f72397c0b0b47f8474aa43fb564808c64df422d25e81d93bea0e04b646d644ff8161af25227172ae54ae674649a25f30a5ff42a92536c7c678a1df5e1ecc35700fda37599f7ac4afe7c2b81062b6a375f4e224d3764e1cb93812caf44508c5b4a2eb3eff02245f3803acc018e2ea94da9d17afe89b067f96b5195943c3392b8816f3b08e9e1c0b7ae7669409062000491bd344303f805133b314417c91969a181a339cda45a721404d16ded7dec0ae7e9bd9cd9ea9810c70a2ca6267fd602c27e4119779102d1e3cf98c9188111f8f60b4a85c14be980a14f31873567713f6821f3c5d52c5c9779472fbc0cf6374a3a5bc44d91f8ca6c85e8d6d7afb5f9bf7d65302460a01790d50f644db96bb809cac62b42952e0d7917983166df5396d739d6e871045e369403c518413586d8b27c3d7f350b24a83100c70dc3859455671e3679a5c9c092e15cba6012882170131a7022fcc1b739cd1051c018e1975274a46664e82583d57798dcd4183ee2fa6080dcc7de6f987979db612c6ff4cf1d89b76100b7abafa91a42d19151886ce07ffc557a49f9dc49229a889725da0bd10d60dd5246977b581e74772a33774b06ab9dc8e941efe114ed7581c616b66f0ba3a4dc05ddf48aa372d0cbb53c1c44596bcb14637f5e1121a48b14b569e6b86bca70bd01fb4a3a7277eeec368450d309fbe98ca9ff87b2c237d25d5a4149de38a5be1bb17e7fe666a068ff156d7b5b0cad2fef355fc14fce04e2496317f82db4727209120c0f3a2fd8be93e0775cc3c6a6761b8b75e37f99d4be23496e5708b92267735723443da89f0f96ce450360b9d31d1dfddf48055efd76941f07f9413e86d71c5d1df2e1324cd89dfe84028ff0c4be4a36d4b16eeb364f7efc2af51be8d1d7eb080bbe41c20a01be827d098f00f0bd1c52add0b82dee12b137753a71baaf8c30fd14ab81f33402690723e747e894b0f4a3c1d3b3050ef9f669c3d1da11215de238dcdc12381d8c65b2eddca014bea6d73dedbf0c232ec5e8ef5e4e786cb44a6d4631a99322d9ffe0c3495c196a35df44d56506448f1f1739d5dca53b8fccb3819e7f17f7673368c8b8048ecd4e3a6daa1072d75ed32d8bebc5c49199163c3394d926e24feb7106fdcfcea91599fefdef18d524e90337a92d9ad1586545c1dae1c3df6e040dce7f55a9f488e0011a36fe9460f8528fbb3e177127de898afd0beb6927c13fbb4dfaa78617b8ea234574b7b905bdb0f974feacf00be10cc7e0cc0f803c0e1e94031d30b37793cf72027675505ce053c3d537ce5b74c9b55aa9a2fade9faefd027c2c60d293d13ef0d7728680a70fc8f419041c9470b558396ecd2ceb572221bb3a7598339d2abcf1001bb59edba032a8446954d5f42db8fb5485f68c626c60feeb555da8df251182fd708ea05b834582f7de84012ceb40dd15bddf0ad3f86fe4de17585fdc5d952832b995316fc396fbaea639da06c606029311fd9251797a4ae870d137d329d2ee8f33bc59fa6c755f87be7e3382409c479c7889bbbb6760040889297f1a065469ebd96313e988aea3372293bd89b626fdd375a25b1fceecae324cdcd6a708b53ec4574252eda425044b255c78d5e21a3a1c29daeb68605c8be2fa4dee0a026d1f80ee24e01bda2eded0d8e1c07c0a3ee8e80784dc22b846d7be67f81b618a4ce7e186beb9f5fa6c8daefe87c2fdf58691319f0850ba2df97e62411f732b3e714a85c46dda776c869ac65caf1a74d493f55c7b1d8ee15f0be33e784fe7174b34ed3beeafd92026445b84eb7979050c0b2d16733cab7a2e6202459d61f9053746283a111294e593fa454c66749a7254c150bf507565f0ebea91417d6d20187ec42f5fb7ccd77290f46294acbfc67564d0242569c3090c90deb09200ec87d86ae83b83830960f96b5248e930bf753a9d5c19f59a135b686b42df5bcbe7ee9a9ef299b086c72ac82829e8ebbf5620d777324a5f94cb1cd57afcdb4fa9e3c3c4a1432aa1a3d826d92a3671cec4262104b8d070bb7e7a515061383133f906604a072141fc965bf98de1ff341069658013977dff786461b3b13621f27d4b181adde0072d163b1572d8ea08d1529612230a3b9d39a8c5d83ffafc6e30576b7796377da74a31a985dae10b0c7b4dc9954b3f7ec3313325c60ac4920dc5d19d93e5798189f7575938d7a4f2635e635e7220fbedf9f0356e053f4bb4866636ddab29d13b2eb767d71fa6115cc85b492ef5d7452ec4399c670f00f9b453dfac690d1ec00080b69305949de57acc2a1326e66a0078191fa68ea255b76a1357456f5a125187b6831afda8209ca31716a06d782e4ad4f3988e0dd2202ac6e31c3916edb999be0a17fd97a38e4e61c7348bdae9dc7102de2b788bbcaa36ba7221d3bf5a2d1afef867a39a1f64d59218628dcf9f8048c942aea766a318d86c902d31226baf33cfeef09248a0a4998054edb0e98c3a98b87d8f3abeac04db5751e6d1cb6ec93d4d36d65553700c782fbe25bf44f9dbfea94ac227ce586521aba6ade6c4a9c9be4b4f467ecd151f997c7c6255b09abb95098f0fc4f56233168f9a833ecfe0c7faa7aabfbd7573ca46f6c23d2d11dc0c51c61b56557cfbc10501d05e19c860f34f810ed214e774dc3c2be45e1a44f2c7e9538a622f821861c4c92fdf5281a7d0377a7e139d3cb9584b921176745999a5aa9a9e465fddec800fbc3e54c417d6402f525e9e157cfcdb6e864f83c8e0e96dbe54efb2ee0434485c96aa3d5fa1420cf0ad78ecd2943af11bf5f72269e5a64c879bd9c4f005dd5465c47dc4d13c66428042f9dfcfff61865a926a5032501cc4853f90b9a23ac75a23a943e7d85c51330a2585b43077489602a60a3fa82bd0edf6f46811adb5ae829102279f4b526ea99bf0796b48bc5902521da50b95aa89dd91b9288d9e00dfd2b804f6cdc9b08394273464b033bb8b2013b6cb7e47f8807f129bde855d2b7242c147e66dba936f735c1123a79efc6c44c03a0470310fc0497e2e17f857cc340802095bb10fb857ff1057868c6239c054f1d058889b50cee6fdcca11aae53a6f454c2d15ab7a16039702a2e43539b60ca68e562c13d4ecb565d95fae5c29e3e669565dfaea37b7c5b4b84de44580c17af18278e7a6d9b564c406857746e4d42560c0a1102d2eb069cdc01d2ef5330a2bed9bff252b9e887d284d57c89b52b6633969511723e48d627ecdf26279c93d6cf263bbe7370aad25d10db31d38beecc824152bfe11d6a713da9df7eab0f1b4c452e8d0e8a320efaa6d49947fd82dc34b389601d0f8aca9db686f6c8ac376f4445b705330038ab7efe3b3a9714812b6b242b2d6a22eb23bfe6e3a613f33d4ce3f3be2ee00e02662aad313031e3f790969fa4276d7329de32355f77a9ca9b50061391b232cf0782c946aa684b99c7170e367e1a72a3284ac33751d8c95f28a4081fee4c86a0d97a094c73947a6e435ccc81e3ec3d0e3498ea5ae586069f8193e63aeed1282f523b0bd5f0c93c7fe16aeb05f70100d4789a3fb2b036ed7f7f602abb478e5d63bd3381b612b61ef7fc1bfb3589f88bdf40dcbcc6bc95aafba8ed05296e40080ba6b79d4664630a21b62934cebac5fc73a197b0eab7b3976cc8d5674d457b5109bd70c71089cdbed50f0490628bd5c47bb45effb36341cb17bb19b7ba3986e3966b0207411479311b02189672e33c4148e5bb06593f37659d196eaf9b2142ca8dd5bba539dc77131e50dc29426f873db21e1f0e15c8e35fa2eefea8f6dc979c0e09cd9d7272c8ae678b90411c97e9d6b004e12a37adf668e349f52189c83554c356ba386542ab7ab81651ca45e6afb8c3453819b252625e595c5e317f2aeca3133081de114e370c3322baa1fed60877745f37d21382fa7a0f95641d60570eaf0f9930cd14ae03840c1caa08d3a07b4cf0a9a0d34770daa393c75bd769a76580d35d2e68fddc7404d4b65c3dda54a7b7e3854c1fa3fb9aa0b7eb14f9979e8aa4d0f7c1a16b1d6cff434f2c52382ba795289d40f41c3c2f31b977e86fae8310e71fb58672871e92bad782850aa25dbed375eebc901dd0bd9e9273c087d2de24a51434bdcc92629f2ae19aa69d0bce0fbdae98c03ee5e261950b4afd1c7215b119783a68bad0fd1dee23391f8dcb35394899a352832d2754275e99746677801ee5c2cf3329fe89f3123193ce52efd31056cb003d2743615bf3e7515b3013cef3f388569f47bbd5a0b6862bd3530ec34707f4fa14360337feae21899e100355deb735777fad88089ba3b921627a53d82ab89795e033ed0ef0e879598a3163d4229eb86145e8dba7db6b9e4e05069928cebc7cc49d2b02f4b2793d0a6da10c5945366e2aede6a1639678e3ed7f2a4ec1da5b8c26a78124960b70b461f82419ab75ec0703ac0286f14549c844e937920c94347d20e4582e45057d2b3e5baa3a51c9a8fc05407ebc2c5d420ff8823ee160f294323bd0005052d25ac938e05f4ec0eed7d3b4c9e087c834c02433fbb084f2531c414bac8920b45ea75f52a61698ce6e747bd2e018a7d3c1cd1b42f96f3dece717295958d84b46d8d75a4952ea4a176ee9a87696e369ecc53d7465e4d3c759aed2957f5303b0db25675c0d4b889ccfc4f9150b5d96eb16b49f61794aa2a97dfb397fd3b6662766a209ac593ed08b00d88eb0131bc97179612d911ef00fa49e646b6152a13ef69f3fdb0d424df27496b44cbc3dedc3ccc7e25c96d8aa710bdf2bc45e85182e202c5b0d23dd65820dd6a65506376152d389a5e282c8b898527b317256bdaa3be98da98527c510afd369920d9b616058b2377b9e361af7dd85bca3d35331dc9d373fce60bff7e704678fa1e3737c79383e8c19f8760554dbf43148110a6068817e9c8ac81272e3fd2acf50bc1e0320018bdecf258d48852e9ecad58cc3faee4befc393bbfa76b18d66c07621dadcc78002ef3a9180594bc2267664547444fbbceaf8a019d7c22080086c4748b5a020d90209b3acc2686b1fe14c1b02518bf891fec37823f95658a15cd83ccc8a023c71c5e8fb9021d9a3cce4aa48622bf9793f56e2b3f7f9a6dd006e803ff2d534bddefcc3b24b60f90a508330be8390941d8cac27a1ff39bdddb2dca07253eb8ff74d3db78837cc02e3d3faa41e01012153f2b15df2423cb48332bd4c7625ed2436a61d91f1a0389906dd6a74a40193f4d051fa7a61472c5e709eef74f16f307e42fae827197d22412ad6fb9c9a40d09a9c7d49495b5ce2836ae9f82ff8dd452bd35b004e658a2818f1c32e2bbc345f80ebc54afd32aeb33708444fd8fc6c9b43f312991b83a8c4c7d1189bbc83fa131559bbcb69c8415099c3b2a14737596183e465433ee60189fc42c772a1175990792aaf00c7cad7ce604002ecadc5f73c650910570eaa49a7d602c6ab799a87a256888310fa8ae2ece0fbe026a68c2ac6fb40bdeec8a921ed3dcd3d9af03528a866778a11da18b06f8f85a2596f2170f572a6f939559cadd9e7ed33c225c55e4ba9aef80072a550f0d85931ac021f68703657f73e8925f81cd779fdf4184bb4cccdf667bf03cf4eeea24c54f08f4d158faa2574ddbb9edee98cea195ab7460685eec506f6df97d14d5e5989c054ef428c2b462661433cc2c7be41a546b09c3636792310c300da66e4776a4848b3a3ea6ffa0ac9896aa4d0f77bcfc15eff57bbe5ea37a0981d1d07131ef6c333740e199a41d0e12ff0c49f0f7117b31c49ac91ee9d33b161bba92904026b665d63083c07905ee95d9ffb49b1df8e4f7ee3b92280ed13a3084bd324e3dc161787a73ad8b36eea42d90f332cd30c8d10c34e65790d59095946f013a3f630b81ada24c075bc00b0837c9c54bb275ccf57a2ec947789d0b1c6595bb7f79399f1e9dc68d804a486c19be164a450b431381d82331abccbcd0cafa1934206e502f76478904c492be848b6559c3e8b2fe65f947c2edd8f245bd6115afd0974873134f6730b0cc74f359f06047671ffc500f65432ead4bea9f1fa6ebd86604267d1e5b5766e3d77d3c526e3cc2ec9c21425b53390a0398d49fb9a3775f4b65039932a8a6d3673ac46ebe94706b4c004eadf4bc158309a55493ab94997836fa9ad91e698ae1372c7228d282db805765cd2d761d81debd680de4b9ec23d23381fc5fe2f6017c49a453a361590a574c1ef05349642d9d65a687eb19753a73634726d6c6b9f10b21bd984ecbdb70c2d0cfd0bff0b3f6032f4832df2bc5c22ca9f792d6543552d8bb86a6d1131a33c3f7f1dc5196c49f4381bac5505fb655f2f419221d6ca13758d257b20ab394964a711b8081c64bf7894e7446113c6a978ec29d3b19ba2299ae26d748d797a244489f2a42d5a94679c731e80c7bd899e2623571368557cd441d7a0af8936284ed163294cbb32201e8e38a45cafdd20e92c8955927b49ee222d6f56eafdcc7643b416d073f373d3c40dd0cd913893dafe9ba01b2b1a47e92d858f5047a12877ad75b38158c2c0b65c2b55f5a9db49557d2604fb5a0f6bf58c72dfe3c9df25942b568fd1a071c05c8fd5a0716cafd86b3d35d2ab520684a650d028aa0b88c419fb7ada13674eafa74bc41994d7d37aca449c49793dfd8933a8d7d3261a87ca1571a519712d1b34f683c69ee819eef518111a23922b17afc75a72f5f22c0b8c091de2d124b25167d80e723da6b31d4da07924ceacbcebb118cf27933ca317752d4b94eba7075a264d9dc6c1f2fa0b49ae3256fd9514672ea1ebe7899fd3a07b814dc1374790eb6554d4ca95e8df0594ebb54efdef01f38b85dcc3fcf73017f2a7606eaafb7d0e2b75531d4e0d913acc6f52877977188c63a4c33730bf18e71ee6387fcac2dcd4bdb7e56f4bd5a5c373f9c815cceb67d2ced52357a9771db6a96ba54a3eae5aee928a4172d5826512963128855baaea4d295526a91c72404a8734e381e9ba2c48aaea3defaf35283b3aca3cefef3a6b934a317e7e7e689665493229294b4a4a2a259592ea4fa9a816957e4a3fa59f52515151517dc62357f54866414212a6eb8e783cefef2c4f2614399b954f9d23d95913922bad2555f55b140d9d904d91ab1572ddae1081c6a3b53423882bd792ab3aa426916bab12b1db85b91615e5fa7ad4bd5e13925bc0745dd7e10f3a212857a05c3b5bbf6942b9a6d8934c922b6d8bc6d1597ac1d28de56a465da36e3d5255b52cb4aa5ec80b3a50f793ebe6936b85c2e681a7a1d322570d0bcec684371e93a954043724106f411b904c6a1cdbd1651a7a26d1383a5756b5495a4faeda0c727df58255c97832dbc18c27d7dbbb3511bdfad3dd80a2578f72b723770b8a5e7dcadd9044af1e7537a1e8d5abdc2d49d7a8cf82e44a631255f543b2242c916e893311942b52bd8bab194955fdcbd58e90e4aafe48557d96594b91727de947ae341ea9aaef4ea59f5c9f25c915e72355f54036a8133a51fdcae596e091ab0d8b56d5d77a14fc65f5167fd603b98e20d713fe626496bb1d758daa823fee27d7734cc499ce75abe79a88ab529254d5734b74492c92a423b9bee3366ec95614bd7a0d73ade8d59f56ae2f25d52ecbb623d376546f326d474f6c59fc9492eaa611895ed57aa257bf618d277af52f4ab09fc693ebb72ce20ca713673eee4ce2ccc9ebb7a3380373fd8614678eb6d8a2d892e2ccb77121d7d7ed9f6694eb2bd251d5c9b51ee57a4d6e2cb7ef8d24b9aaaf3ff0465ed15b1d912d25e55aa170dfd29d6ccb1fd3552e54d235e051f0291726814217228141f0db91cc326bbbeef73c996d3bcaf5a5a25a2acaf5a62b03af1387dcb8c18a7d7048a6b883121fe80452de8e51146c7300948323792b8294b7225b446f834582608ebe59165b5f6c31814724c9f0ddf83b62a7a79094b767f77bfe6011a0e86dd656275a52b5bdef44125d230bf6834ef01849e2d99ce0c9db9dd0525a0e84f2f6147cdcc95f4e90a64dc1246f5f53a19337888b00e56d4950be4e04925c4123494418c915f6ed441cc915fd56d2a610ca17fe9a8a9dbc61970823a9da0e8d24c915114852b57d7b864eece40d7f3d4552de9cd0c91bc5441c49d57623db59833b20cadbbf9ec2c8897fd9edbf0e3601245792b51d36112457a9d3210b07b250be1d1a218a33a56f4694e4ed42793bcab552b5fd74b72b5bfcd9bc39d1ca1beea2b71d1a6999f0971519e5ed2da191a3bcbd6336ec033a81a49de8641a59a963293c91a604c237c2f0235ee396586e0a0bf3b557ed55833c1fe409ca54889ef6348e93d3d31750251a473d7d71faac7519c995fca4cf6ed02b2d336233204091b32430394b22ce9c4e9ff1c41994d36744e24ccae9b39e6c8938833a55397dc644e358393db5226657d82cba2df2b6c3247a43323d6dc995d6d276d094073d633a3dfd41af5a4e4f85e4cac5e929915cbd9c9ec2a0bba90d323d9624579c516986e5dd3791ec0ed5f18aded1cf34f4449a498da33b3d768421c5192c08f3912bec545382cf5fd0a5c08ea0b3cb039916b58830a1be0ef31ed8535848ead853c72e04e6d79b958a4f3d8775dd54bcb98e3d1ec39ec238a963c781f9f51cd6062455dbafa0bc3d75b7c36cd7eabace0ec643afa41b34be974c5199a6be09c10ef3ed02362c8f5a7336da6ba2f6c286e5baf066458457645b1692730086533cb06387390cbeb9708ac7f5eba9a7369b882fb072202758019295fa0f9d5b7295d5d20e4cd7791e1090d692ab52c9f3feaeab317c7c7c7cb22c3b3a7a719b554292484825a4125245aa48b59515c995d6aa3ed5a7b68a327d6947ae342359020a92463b3b3b9ef7779db519cb679291dc82e46a23922aca41d12afa188513b82932e5ac68b2ed6c44db5644e4026f45529564bbe58ce4aa32d18670465a129c1167945ddcd45fae4b0b22b532bd6674fa2d081555f4305d672dfea0138c720065fa43a3db82327da32e391bed84324464a5b62c1a47e76c2bda582ed7ddada86b703c5245b72a5a451fe401fda7f3c994ebc95453421aae458745a65b1632e5764a2520d8526dcd23997240dc4fa6af8913a971d04f2d7a55ea6b080624579155da7822fe361764faea851717b24a3b59666d0793e33f98fc957632fde9724c448f1ee5723fd1a34fb95c1397038a1e3dea7247a247af72b9a0e8d1af5c0e49d7a02f01c9d5b64554d16f4cb22136898ec8b9254a4672b51d19657a17772b922afa97bb191dc9959665d61e65faea2357db4ed6551f94ea93e94b48f42d5bd419dd5b2921657a969b237af4db8e5c955a454fe92dfe32fad3b6f375cf317277b3ae4157e80b10673ad31f20ae2a9254d1d3d3e4e8aea0d47b6735ee8aca1d61ce287af4970afb71464831227596957a95654f6c3ed4e5f45b12d1a33cd1a3af78db891efd8b12ecb693e9b738637a8933dc29bd8d33df29fe3828ba38f37156c8f4b4fedb8a32bd762457f49a915cb55c6a49996a4699debe3790e48a266a3e72055b156fe415bdd511d98a94a912ee5bba93f5c974e542251712a1842012940b83e0912cb3b6eba4cc3e9a5c5b729565d6766d65fad295b9f9891e7c0a17e1728d883a436edc913756a44adfb8ff88adaf93bec8f3f2bd04630b26f9a0501e42b22e5c845461f9cb4af7061f58a49964753a9e7afaab8958ffeef3ae234a72fd1556d46f22d57fad848672d77e51aa1d20a7eeb4aa9ea7a7d24a3fe34fd7a89f9c022a4ed56f18d65bba76bfeb80c9254e074cae18967047a1954f7450628e1f151376cb3d04b404871f9be51e025242f412af99bd64d90b98652fb21798bd642fb2972c7b41b199bd64d98b245a96d1f6608430d2a04993096343d4b3ac258c11c6a15894936453f41941932200810a2ec880154190239011723c2ace7017908e8ece0e58f15d5c75cb1700e42f86c68daf93c896596cd14cb27c7f7088849192892920921bf9834994c025566401990c49a20a199790b1892311494cf2452553c85f5ce26345969734e0c97288065ac8f2afc110b2bc375483a3dc4336384296af714196078207135ac8f20c18628249962f020a59de88a11b504107073ad8c10119788a14a1c7a788157e868a8021cba11f21211ea2560e9214191d21dd20cbc3241da441e7063b3b60034f1347e85122cb1e3282f4e30411509010114f963dc483a32c1f731667be4d28cbc34d28b7d014497913da8c907dc82dce40a20cb7242cb825c9cdd9c4d2155d37e4a36404435f68f101419e9fddec253ea8424a9e73ce39b119f97a2791628a198c60ce20075d0a2613544d6610021990010c43621c010c2be8cce0838ef95aa8eb1c1d637a65a52ac295f686456e4455bc8c19ac4cd7d10091e55b75bd74bb7c1591e5bdabaf0d64e95d38124b298cb6157d4cb4020c482527d80069044d8fb410c1bdc71ccfa41fcc16d0132da0f1424b0648f9eba22cdf56c877f2323159ee2119b4e8921944accd3da4441bb2b027b987944022a2b50f52061209d1d09841a38b4ee491349a9877727970b62f8aca646ed0d050e5c8724ead7236d8c466092bcd8e863703001e958d9a7925bb2eecba2edaa815b9494faa68eebc24362f99f3a4f39a5794534e39a5fc94726612ce89ea1848e3ec0bc503adbbe17c9c53e2ec0abfb0e7f40ff62cf19ac8c3cd715baa240f7c81b9b806b9d3ed9cd23f8c2c78c9d95ca39b86e250a877af22ea5a09439e9724f6933c197ef630e529a89f6e6f0afe687c3437b9beca7a6f78cbfd6ec03f68e496dfdbfbdde4fb12f7433ce18f473edddecf3ee5f56a29d7e68616f615e67a697fc8a0ee77bacb4b9c8dc51d3d18bcfde28e5e0bee6ba50d5fd421d75d43dd0f66ad391d50e59eeef77cd2ecfda1b37dca9b4b4067abf2cbdaffd0d9e21b5a5815fcd5bb1c723aea5b6eba2aaf9129d74f56b939b906c1320a63d086cb6b644dcca2075d60df6398eb05e622c22c05c71f8b4ff01dccaced3aea792cff6458fed1d44896bbf2973b391b95a7ee4bf4506f7977bf9cdcb1e49614fce5e494dbfb5d057f30ab1cd67acf9391913c9247aae0abca57aea9ace0c0aafcebb2ca6ba2e491f0baaed35df0e9d8e926fc7557777b597e6a39ca9b1302f5165c6fe7eea2ce72bf6794157bb8726d4565e5d637a75281e8ac828360d93e7241c03469d26447ccf51980b9e21fa8af5c9827b74326a75ce5167f5008eafc24f920f51c411e2323a12322a496fdd745349efd2793abfd47a3723fd34f6f6e07e4c9292af88b4056b9bd9f4cb6aff77b9675d7fa1aa972835c59e5cdd9a86020327f30a3700564720afe78d87b7abd28879c0dca4fb723109d2d0e7205d181264d9ab4b209ff90c9278f3b72b5b5aa1ec6e4faac56fc6df8078ddec997e7933c196f71d5aca933800891e8d1ba71487b6534cb7ace47c80361e7c060c4e090d135e2bf1b06e0411bf1dff38c4cf4228acbcd43015d63e60d442f7f32594787862a80479228a3a5101262e180d8409c8155682f62a199340a3212ba060c401b2cbc7681c007a00d5647d7f824ccf276864d58e8c3920f9093e1a14fcb962c0c90abb74adecba48c5d7d5d04d334e4e7f5a169c09ad2101861bc380c6491bbade8259d64cbba3b2667de0b68b210ca0c4050a28b88a8c602b2bc5ac5e50b0359decba65c49d69793eb9039148221b9331c02c18ea9e5fdc6e98ed3f22a6a8e38c8954ddaf12100ca2fb987424064fefa25244bce46bab80c41cadf3cda8288792ec10940c81f3542434b6e2e2e93682a89392ac87e14c800f2a674c43cbf247a51069616d1d60c269e3f716662d1aaf8a905518e474747aaa0e17137d6c5c54546a665c4564427c3eeeaa0582d5707c5822b6fce86e535918888517ec94d44e40845355607ca53de33af93cde97dc234283b02731345afe584cab18b7abd29775ead6be5a812cab5e6b0cb9b8118c4f591a2c750ee970198a27807cae5738d440902e445c6404ea6c03cccb31586951c3434f9c80704a6264d9a346175d7b0ad7427d09d4dcc9fe8c5cec7757aac00353f727af2751ff39d2f98fb28a78460e5698b0201d1a0d40e7a79fa2eebb2dc51a0959be5a02a0e3081d8586ee7959b659cda51b37ccd12a78290f312a778dcb4a8d103666b12b576928841d18b4838ed2d1807e3f0d8812c97df3c858720e103cb05a7e40e64a5a4cb7ba4eee272212d4fe11e2e7ff90b16d2dd05a724e6f17297964b2002f448f42e6db95c5a44e5b4fd2146b9a7907b68c808326d512671c6f4f82c1b621f4fab50c9f1b4886a91291639d22ce47829ff6c892691bce48f59447d81fd2890c4418c381267604bc21389bff664689e9d1e3b6dc9a352504e6f59a76d207996154c5530968251f00957fc7979074dde2890c9740a34b31c2dae004dc6f00784e9a6933b91442f4a1ce40a424893264d88323689b6162acbbfe4eddf34ca4e232f4faa745c789e6e9f1fc53f68b2f60dcfa049247a1113faf901029a447312116597372bf566c9899d3ebbe6d2b9d6d27579777a7bb43c75ec0ed1f2141662e4c6e5dd85a4de22790c4961173c892065f9d91afa409119904a1b9d5402b59800922bdaa29802490a84bd33a53ea4c4a91d9ffd494c7fa80f6542ca679c8da42d7903135b391e43527739057249e13b7bee1c123df9895dacc17e3979ee64996d55bce46c36708420fc60c8a5933ab21f6d115d7174b493450f529cf926912de2cc377f72fce6901c5b4b620f2ed48a38f3fdf801eaf191ab26b2f3f95da7adc6e1e3c215b82e9168651a85c6d157e44f6e91618b1a5d17863c9047082ef77161d85272e991a872b9cd921253a02a02daa2401f355a82f6f4c096cb5b6e4abe59ddb1a3388321c955cbe3b124aa4377e4cac5e3290fedd1828d3ab9b38b7be94855fccb9d4952159fba13e9ce23a98a7771646dee2110a4217ff3c8e5c2ee9690000422f3088910bacbeea624a62dba641ec5792404ce884b0c3a310afbc122b8127df041073b20db927be80936e427bcf044d14f4b99a3dcba054e2a3430d04498c92e92ba89acf8c88a184708aba50a3b02464c0a24403188e1f3a29c72ce396717a44ca459a994d1186126e79c534a1b39e9906c49447461d817ae2fcc78c10848ac0470de0c6d2e51d367ad22a3226dbbce5a795df68ca7dcd825239c48d7135910b66801d0132420b2001e9f2764d08382a427828082999d4048aa3a7b228a152c00053a3a80b0a311bb30ec8261041b2377145d10438c28788105387cc1b2e48ec20b4e14c10b51743d48b62e22cecc13cef4dba355d76d77215f4ac0130e6e1a84105a6b6d2dc1ec1f4d861d764cabb2cfd4d8ededd10768b3961ac34abf6e91d28fcd2eb863348d3078cdeb9a9e91adb9878e78e10a360bf9fbbc47433b524516062928967be8881443ae9f9c88a717b439693721bd539e2c90a3ac79e2c800cdd81ba50239c6d32cdf37058165898174938c6118e711391ecec72c6e51d7755d5729f7d01126e4cf7ee640eb188de330d5fe936b13b91ee59a45f42a508c9f628b5c638c3046182352aeef187f72358254df874690ea8d8f317e12212455f1f144141101e39d58bb76ec12512455da8910922b228e2e2b9e42286b4652a5d5973eb803a1acfdeb298ab276680429b3b68d2065edb0891fb98a2c4d3bdc01529ce922ed2db5778cc61323cd82968a5a9cf893ff41e24b9992513e428886ec59ae509f90c77e599e2f8a6ce7963458e9237bb06338a2feb514ada20ca5100a6aa01f9f9d3c44d1bd018a1ef4bab33cbe6b39cae52b7749a9682a97e5a8fb37be69c2450a1d54d20a5286c75f4be1c828432914ada0e46c22c65e2335ecf60d2df02704e8f04280ae8bb3b988446fb6cb8d30d83eca31c60bd2a3e84d9a459eb185099bfde0cc277a970a3b1f77ae9e222bdfff681010504f0aeb89de757474a4f21ea9428332dd30d91367a60cf2bcf4996ad0e9c1ce90680434d48321ac83f29c8941974b0cf58027cf5324712615e7e74ff23cecb1fd8ede8ec9d9348ef826287a2a1772f488521a74a9fc3aba7cf2853f382b6f79c461b9cba3cbcf4fea86e5a9b33ce5027de42a75d31df5eea86bdd6de95a30cf9d4279c68e3a73fe221267523e3f8be40a7578048d60512b7fb2a7855a9ff168ca783f0a75a1905431498a1e44f28e1e3475324fe2ef47ec9c1cdf728593c3ea4caa7eba46aa8fbd71aa69d668d30873f744c513eea993933ae79c73ce39e7fc49e5b84f8e3b0fb99a5a51a61742a4492f26d19bd717a06a7e9221cfa9863c4faedd0c64393e1edd0b8bae31df42996af205b2a767c87eb2470ae105b5679d73ca25c823a0b460e1b116b6445bc1c2a2b460338de62963906da52c3be1cf5a3c4dcfb2ad44b169858db95e42d1ab6e1983691b0796a79c22cfd951fc93373092a704c2fcaccf354543d14e9ae998e9167f59663793e9d7e46caa50d64a375561adb5e22f55aa37dd1f4c2653ad37d592092b99ea4b2b7d714a3b764dbb66d26ebda95ec3b2c725639e0c8f7a8f05746f298058de52106917666ddb36ce86fe6272ed3a6a1c58f40a8b42abe667e93acaf39ad46e56deb9a0def21edd5db09095776fc1428cd40b5139eacdeade2a1d16d2bdfbca85a0aef2d4102a47fd46e5a8577cb3f2ee3571720a6816cb4d5514eadd6f50ef5edf756fc1382bef8ea302391b1e2c3db98347b10465f815a10caf72a5206299577951854df140bdf3d7513092e208a7490194e18b6047c1285f4771267efecae2428a3312338a1e93c696604558ab8fe2104552055f449cf95a8a534ebba6dd5a51b8130c046257748d79b1d6c5965cac08b3e2c87e368af81167e8e7a4415235b9cfedf3725e6e1167b417f5b4788876bea983b5bac6445dc594325d339d6ebad8d7a9d33b0b01caa6db37a7c3def49a78ba3f98ac35996e4d266b712a6551ac111a8a351d25eaa06b74b6a65b5392d5546852ca8961589bd2de9673ce0bc34e4bd74412e38c104278b718e3c4d98cd7fdb2aef15d2288914a794929e3c4536230cee092f18a18931ec4b0c65f863b5ed7bc660e8573083b646a88fb983300ff86cf9be1af8d72f6989b828f390ebfe1a68688390e4f41dc23e638a47adc7018bfe1301e00690a6cf6d835b128893d429484a0241c654882dc43486802094b202189dc4354d8dc43541cc9dcfd66e64e4caf91abd4498c13ec611cef2a5c93c9958753a51838558a8153252144259309008f817164ee6537ca30e336fe9d9c7236275807b762e3a6e065bc5933ae77d57bc4b80c2c44c663c8c0292e47c815d0ac4b55ec72581e4ec19b19f70eefd5780c8ce3bdc671627c460a4b46885291540ade06be078dabaec238399a164fbc9be262dc1477ee9e7715c6b171ef3800788cd410337ad4788af3aeba9018329ec3eaa1bacc65b0100f5fe0467519188706d748553c1052156be29b65037f4d44031b816f1600b0e44ede31b2779b756760bf4e7a76f29314134e0d112386eade853c06eee10951e14b95097bb8a5ea6ba19cce1a0e17c6ad81fb74b38cba7d1d9e0809477213292122520224747294fbc18c029fe12c86848a868ec024cbe41e12fac94976b60d951ae21e873be01e079c72003e8c18f77e13e31ec63172729c141703a7e05537321ee331304e8ac3a92166dc7b8f19f7b0102337315ee342bccfc03d6a7cc6676021315e03374b069671151662e466c655172203f7505dc6656021dc55b8a5ea44939c0da732e3a64c4fa5a49868524c29de4a0af7d44a8a938153284f7137329e4aa1c898b1825354b65b141977064ea53cb57273192bf886c655dc4da5527a7087d67ec6950144e66a514ad69eb0acfc2b87f98ba7522e9ef297a79e4ab9dc9452397794afd0a4c0bcc09f8c8b14cff47217930aa7b8d368c12915942fc8cc283fa9bc26e664538a3bb9a9d4cab9a71cfee4326e6a050f41e3aaa738dc8306567dc6653c95827bccb88ccfc0324e375d2032a79c64dc96aad355b7a5ca05d2dda4499336649413cae995b34139eafe885ebc4518c1c28cf21f7e1cc2c37b82bf994d87af917106e5b18838739a97717892c15dd51c96891ec763c82f55dc077540c8e16f66eed7e1551d3e3b39bceac2132c13bdb73d577ac3ebba258883cc2026c7711cc75d1cfe70327778af77b7ccf418387572d5735826ece1d4094e0de13dc66fbcc7f8c963601c2327f826c65518e75275f21827b89fc3aa91f0367781d0264d9a30d921b3c680dbac184064def007b38681c8128e000eb759306eb35ea260bf1f5d84b3ec628fb9cdbae136ab46cedb196313b78ecd81892fa630e79cf2f04a29a5d426ca35ed1aca0f9a0c3921b0b6fde446cee6243e27cbfb3d4720c39fee372f67da6e6f4e8637c8b55ddeef46deb8fb6d599b3ae8350c51d77e733e85c59cf3f31e0d19334fe25544eb984d1e420861a4810d3112610bc96b0def4723c39b60609b551f5f83c87c437e850afb6d25d4077f75ce68640cbf9382fd3cf983468e9797c9e2cc87cab285a609e0ef11ec0735001c28cfc3b8d6dc8e79183fb9dfc9eb8366a4e4ec7508c04bb502086c9251ac853fd6da1b376e683a04e03afc6ace2687d7c4d5ccea5773363ba04ea893a6556b6db53f9c6c3c8a1eacb54a1e0cbb2857351b7c92c706361ee5ebd5daa4dcde949b6e54123d28f385630bc79947f8d3456022ef50842345189277f80e45207245cee11fcca72da0489273780e876289c66dd8c9c9c93cb9e289cc41871c0e374e07cc395cb3a75faf185669d0e9302c2c29296f8e8525e584c36e6861535236cbd9d4aae3f4eeb5c26a391da7eff0eb62ab9f6ee7541030ef70987738ccfd72f20edf2108017c00ef68accc8e9765f6b7efe674c01ff071bc26621be5ea75b8343434a75c3dca8534a7f9e9d809e6155fd7f59caf9ee535b2068919f52d25057f39574acad53511b25cabbf2e7b5d36d6b68c8a5a45464618923b77468216b2c88d043128b1b9912006241ba783dec5bbeb020a26906c81040a22499664fa08c5904c1fa13852842d327d11aec8f4ddcda27797d45de28c193366cc10d23366cc98819343e35c0aa72cea299f05a203e6ec33628d15eb5cb81cc31f8d8cc538eb23516b0591282a6971f6cab37036b57e72db9bd38e72533a520ee328efcc72181e5eca711e5e13b59ae73c95c2373cbc06e3f4701e8ee3c3733c87d5dc3d0f37957a7eb372dc54aaa62663213d9c87d7f4e003e4ee9b7b711eee759d2e9657cc741a4f0162937d06d681e3dd4df7b3f93fdd171708ccdd258763ec4edffd59fddf1d1e77f45e70b76121e8bbcb7b81c03c00fc65c771940b738c9d0a6773e10f275f3511757a146743af410e08cc28f745cab19b8284c728d357242208657a8c08addc435464d1e51ea2624946a293e9afe48911830346a9e6c3cdf11e2e0f7f716146b930972ecc382ef67b61fc8147cf72dc87332007f6e10c38bd876bcd5d3cecadf7874cb6f774c19a68aff77ae5250f0e31317e612ef0310c8ea84b4e08fa8a534160c791a88b44b65a8b5142501f6e0e0bbb9d5fdcce289defeddcc3cd61e1fbe5644a2477eeac71a86b184cb9306f712665666b32bd394a4da68bcba2579f72ba66ef1724460e01f51988d9877dc7fdd0ddbe539e723acca89bb2efe13039ce728be4c029fb1ce7e1ddfd307cc2457ac0298b83d8e1399c073cc4880e01780e3cc4c8ea489c656426b56387e7f01d9ec335952d09b98790f864c8518cba30290b36559f9383d8e102b800b00fd4777867980a2287d757ec03f51cde7905f3950e872ff8fb75b82e8e934f98b529b76731d9145b537016bddabde2d3399b0be6a77b17f7833987a37678773f9cbc3abd1f4ed621871b24e61c0e7383c4bc03e4ecb31baf34cfe24c3dcd2f997ce3f6ded0c2d2fcb3f906cd339a67375ee284a04d9a3469922bcea277e3cde9a0c137be9ad921e8c6953a5c49240057e670e51237b01ffcc99a83c9723dfd84bf99ed3526ecb79ddeb9721c67632d863f1ccc9b433dce543c3b16da75428d03e61bb096aea32c375a8efd861baedf7043938b93afb7d88b0178fa0933a5e4a4d4c81b302cbbc7c0313860f853adc51fcc57095bfffff3bedb5e3140aa2aabd61b99a80651db80af7129b4c4ca4f39fe753cfcb331efb8f2e1313e6058d403e601b754c9e0962ad5ed8cba9d61b4beae0ffbd34fa7afac5cc3b69595affe74235ff57e1727db5f29c71abb3f6e4ec10180364eaf91315f077361c6a48fcab1a75c4d6e30299ccd75dd0f02d95ee56ad7c500cc11c8f55f677a733627db25194bb92b1a2a25db265945454545e55f17d9fb5d08e4facb9aae1f379b7000a00decf67e3a646c0543a213ee681a07ecf0977df52cf8eb360065fc05fe3299bbc0aadfc62127848d9fbc466600dc0f66188745366ee3326c5c067fd606fe7ed0c83030004ee3aad74800dcce32ee97a9eed7e51af79347c8f0312ef793f8078d3c4fe3defd6e320da824d2b81dbdfe2582fd32ad844f5698c8f525ab72ad7f78390bcb3597bb84fde2d191f77cdd775c1d2673c1b227c76500c49e823fef84bfabe0afc38a60dc895e123d70080b49f9e61ec2c292ecb3454fa6711817dac9306e3fb3761921a9878c4044fa40266c66b6ec277af51867637116bd8a82ae016fed0a13ec2751903f591467ba43974395c34b14340ed36947bc9049103077f466ae4fb95ffde99033611df51626058939eaa864e070cfb396d6fbf066e5780fefc1c37dc0427c380fef010b3172e3c379b8901e9ee3cd8ab906b7a813756024459d24265107e64f0dd1c373bc470f38a6870f98077c1ef9e3917fc8641818dcd13b86f119340e23c62d0ccfbbaad398110307d59bb39171a1920b7188f1c3e06f66986b37f9f47a0de35a6eba761e9ec3aa16a786c8711e7e93e33c601c23f638f526c779380ff039700ecbe2efd69eee07f329e5d5d6cd743f1ab962d36ba4e9dea7dccf7b7342c0b88df9e9de70c983533b6246ddc56b24ccc5e1302e048231f7cbf2fd2f0e312e447299d4e8a35e666badf5516f2d13ad96256a1129c100ccf65048e8542f54ffc12024156340668b81402660abc2d60b201344ad97291c11b95ca2be1e5efa1ce5b0208c98b75cc1bce52ae532390ebb28d79f52ea4d3030f04793eb636230c69e973fc92393e30e19e1486e79e9da4ffe82c48cba8b766df3af18ee9cae35077d603e1df6f02ff3e19fcdf65f97ed3f2fdb5fae78b87dcd6da972711f6e0fb7e50f997cfaaf9d2ef409900bf330fd29817a0c63c8266c0893b040c35aa3721ffa68783519da099aca18ae0bbbae09afa0417a45a88529b33c23af2967f462cf75daae9479e9631e3bdc6e68c1a68248f1318fca9d3b7f13a776a0327654c660cf77ce1d67e4f624547ca09480b00d4dc02e7a51ecca4209dc381ad6de9c19c811a3f89a1cc8f130cec8432886ace5a5564d9c6530c3160d34b863769baeac35ccdd3967420dccc8b25d2a6345d60c4347168c11b200e7ea406bb96261d89583102c56ce3c0246b80520682c0d6e57abe6c90296456fb3b0db2c0d76bc6eb396d0b03967841ed0e68457072694110e41c37266a075a9bd920660074e3ae7829009373c10d4829d12a020091f104187e107f69a584e11248c0cdda00966e831ba90a022092d4ef043a63bc3fe6e83fc793468aa60227f2dd44986aaa8414f14296862086a1882c492b28972cf1b720f1535c9a6dc434548f688d6cf826d2e421e493e45d306441b0eb48ef920cfbcaeebca2ad69bede6dcf29cef0b4462b7c8764d6e14723371a66aa5f78138409c89327af2317a387048f476a8dd400b11fbc9e46db2c06e7126420ce9135ac764119569e24a9b8176552a22ee207f4d09c87798d422077104b4b09f0c16f693c9376b15bde806f2754920aa906692269d60e155b4b0f0f29b110b85748dbe0a182c3c8c314231681d53935b080c36fe9a71c618048b54d0b41c67e39c0c71ebe8d93d67f7ecd9b3bbc140318c6254ca8961584b2963f2bca83c867b46b694f328e8c7a6402b652f3dbbc41fbc32eca39f5d83bd59edf89af304cbf097fda2b77ebb27d72ef7c9c38c524c7f7439fba4d8857f1421ef8f2ec74b25d06fdae18d2da4e672230c9b768880f8fe341c44faa83888ec472d621ffdc1893fd87de1ce4060ee5f37b5e37afdf5fad227f6d19fcfae5d3aebb33b2b9e1b86951e5f7a8d2c5d98afc38abfaa5dfaed66af77be5ed330bd2d61fc75af8f2032777fe384886fec235ed334fc553c4bf7a15dc336cf4db5cd73977ec22032633e801c41af8f2012bb3ee23b5f1708fce4769c726ae21df1fdb8a3cb5fd32a4a7d9230768650681d330fa1fc25040b1f2f2bb0e53819e9a8699a8f2247c8b08cfcba20b88edd4706642ec281eb9d254e0eeb7ae471e1ebf04e213a595e0f8194980611ade116b90f9108a069dd0ddf57b0dd4162ec9eda15ec372584337a57b0d9a78410c2ac218470369411e9ef8285ffbaecf22f71e66edddd36ebeedeb2190498bd0f2184597e9ba8f932e7dc8106000fc06bb0514353386ae5aeb1d316bd993b0a3028c95df41aff784772fccb148260458e570840a0d1488185ef4d87488370caee6e8bd45300e5eeee9e32c6c4c4995496b7eeee9630b08d7d3492fc804681902a78db51d84290b2a09021514da7e4869c0228ab846b5860371067e2e171e20c4df4e05519e64f468706f302da8364d9342746310cf380c019bdc982655858f089d82b04bb5261ad15ab87174837418a335be0479ef9b94d21996cc43c4bf70d7f7f07b18d9d8795e422205b44f4e6514e60e790a833758032c59148e4c178628c31c6083725b496f029f2018fbd11208d0862fb4e6cc27e91076a10a7b2c76738882d0e49d1cad8e3cc1cce66346275a2146c7cfd23ce00cc1f95d8b586339e60df6e77967f453a1669640552d0c91057405a0106223a39620dbb189cf9ab3ee283cc8c1dc38c88de3c8d94c2d94a944c1d29298c3a3b3c30893c672a08cc47846fae07cb3b8c8343852776585d77712012419041100bc7e5382fc7717178533b4acfb961b99188bb474e47841c8f96d36b5a844f5d23f4dbdd3175c4eb031ede5410a5c797bea3744af107e3212704c70096a7ae414e01cd6a79730e60790ae31c216f4094c44abd7b0660ce5e23e32d3dbba597ba8b0341c03385100bc7053ea2a7904410cb883cce0b3ee286198258296ca474177c04968424969193b7b038c1ce97ae4c5db973c89d3bd12b510cd57d6641e6cc6cef446f422b8c66963ac212267e8aec767b7a9a379623715647ab112caba8e8cd883f1a18362961bf78abc3be747abf0c578046f6615fbabd56baf1f4f06218a5cfb0434b33faef26c30ad0c8df4df69e9f3a53c7b4448c31e66843b1c89361fbed1661f9d54165b97de441a1c1da4f163c87b07c0e89de1cd29de52831c218233c1dde53778dc0b35c24ce32f2d2b19c8bdc351695b7ca63562a7dbb5f4ede340cfb0f9db1e76478bfe7084c085f57de5c94a5d39b93e30d72e5151c0068e3462ebd466eb7a3b7650ee51c8eaf9147dd6fe6146c44f47ed0c8273c77a6ce144a7189fad947457cde99608b4e9e7df6dcc4990b70df80c8d8558185cfb2d935127e592ec272ecfea0c93106b119fb01224d9cc93e9fa3c6932b16dcd1cb6663a16b58a16dcc6b175ad135e6e98557942e3cca227a33ab6d141fe329463f9a4c6113585932eca92c8bf391888b900e3e4b92f2bc4f134079c6a03ce77c4667a6ac429e3a7205ad88aaf979052c8ade8482fda68ecffc9ccd3242cb9167b24e0dd041823e72c5806cde8839fff2010bff02027b09a5441b628071f61704cb7d7923ebb79d114e4a31ce112cfa2b23555146aa6472c7c3932a1739b0f0510a1ce4f8971cb0e2e5fce7498f860b27d44352d0c9b1832c2329f0e4af5b9c8e148a90bfb6425eb38b332f71669281c6cb08905808641e73ef319fa20530b862152e256cc8c109ae147477cbeeee8edd0db1abb1eb6aec9a92045a1c4084534a29a794524a6d421bb023b431e5001ada90d25a137607703d8bd08694529a8080958680ad602963ec18e337f3978ad0463fe6993b69638ccc119ff2d76519638e97dd0def17f3c92c718cf29ab223c45840712e8c5ed725e31111bb9a482831d8a4dbc69cb31fe1bcb00bfb81c5ae26f3921884524278c2a08497d863298310c3221614e3a6e85637567adfa3efcee240b32ba6db867ca984bf23f2d5860cc32efafe840328c5b671c1d8309e408c4e194f5e34db221c006cfa2a4b598c56c26a29533cc4c2d7c489d9b8a49098e7b5d2f6755b561bc7d4b87b6a95fc76a53c59205fb133407326af439a4ba73b2c964b1848d79047e44b7275bb22044e402206186c29f7901236d0ec25bbae086722f4e0fb82d73c84104208af39bd6c622f110ee03ac5b2972c6799e72c4d6cd2db7962d9cb8bab94c15206e11bb3557cce2cc9946491bf4e52a4a40a29e418e42b265f57138e902f254ae4ebc615745032f97bc9c1841e26846109567ceacc107476094755c8df96278aa80479526188880b4b90420c2588c00a4ab8c2da2fcbb009f644470be2764a5089f800cfb682522b49ab415675404b4f604e10ca86884c70cdcc4e91822ea080080f04996147676768880d44e814a5d44026cac490813de58e420c6c802164b5dc518ce104558ce1055f18c30c66f4d82c771463c88137033ba99079ee28c850c5fcc001e2cc67a3a60040a0ae6ba27e20c08413af28818040a402851f9ca025a60d489aa8ec0401c8058c249028a11205105c40052888a09288a0029311b2eeeef6011172f7690ac0a33f1b675266314fb0f0fd3883e7212b7a727fbe5c97854b50824496f88978868e872965747663d735a38c538832c2d2a905c20b4613b412b029ffcd39273c651f6d62fd9a98d1eb98bcaed933a31836e7ecee19e48ad3057d2161e1e375cd6be294c472c4a2db66d9b265777777675052011e368700982057a4b4616268c30b754ddc3171e144cd84104a3803f003fb6519a8a58456286163bc1a9b33b330c88e3dc3b28cca2ca318c33d6397638c1d73288c4dc4d87dec7336f6c67e659916331a23ce6a60d1314a6cbbc61170ce6641291cc9f663c71c081f20e535e735fb03ddfd5386724af99a78c1fce564f9a923c2a3c81d68b2bb5bcaeeeeee8e51cad9b108730c5d843093ff72329c17964062d89c58b7a9d492177627463367dfae715d18c5e6293ebc2e2f6bd8d735fb7a4d2c7537be2044b141264f39a544a26577c309610732ece779fd82311a6918bb21ceb07c733912c227c42cce5ca62ccec0381ba722ee5cca16e83296d1f79db3c212cce095e1a71c81d4eaadf5b262b54a292fe7155a8c51da07d5a880200806913065f6f929bb67e4bff92cc64b39e79c73ceb89aac289b905168cef63c19191a1aa12c7dc83e64d3eeabbb9f050dbeb24bce970cd3c83db3d2bf790c675225e72f0c772b20a96ca8a46d15753d8d3a4533224000008314002030140c8804a3d1682ccc13416e3e14000e839a4878569cc9b32c88614a19630c30400400404000406466da00c0c436764055e2e393cb7f7b1169696b44dc3d53a14306f060f1015863a2d2a8e5843b9ffb1ffa8beda4fb023d1822bb81e7dbad20d60725548b59eca7d708e201e3b7810fef2df099633a29698dbd2f1ced3defba012367169b549e4d97c89dfa747367248fa832b38c5672abb70631dd62fb7c83312f247bb3001a6313212385d857242d53ca162676cccbb2253d5c5e75268c23f738cc0ed896aff9c3a49f7b4211fea5dd17ebcc4092a25af46e284d62ad80824a16614b6585ce602bf3971b0c8d51f74d0ae44034f3aea9ecf41d62ad7d8108397e3277bb31d0a1a1c8771c6c2e2776520bd4328b64ca5ef0d7aa8ffc40782d4e095f26371739baa153e4cfb722c8e14744f4cdca76cbb26eb4068f060f03935dc6e692010fe6aeb8bd090278383d7c603c47eaefa506271e63afa1838ed54ae28f32a6b9064e12c10b0b4488bc7adead5c92720472bc2420b75c37d19f2ffee648bed2445cf523c8b70c463f768135a76089b243a87348f645980f14e377a9cea930fc076a81638f0d0d90086e1d54713a8721f80ec81b7ef4c39bda043b160e3243110d5424069a3480936c7a4d954cd512e0642e15543b42337a6cbdbd8b8d281116e2510fd134f9c9f57848ecf632a45b81b40d5be89fc85854397205ee5d25fad1ae49424ceda8258c431a9d380e047c84615b261da9fd3d61af153243bf0aaeacf3c6b18225e8c690dcbe4416ffce18c230257e1d8cb20e0c3e1fe18fcd421986c2399fa401b3eca1ef13d0293c667ddf4869e7858bd67ef8bded878b01c03731c2ff9be456fff7694ddff5f69e73e8a185cc91e73b8fe6a044ca8a7ca55efd7ea8f8961e72e36431c4611d3fada9655befdc3426b1c299cb0487195a16511d77fe3b83ac56a9502e95d8a00ccdb971c23c1dd65823ac470d3418a6fba0523b28c099062a9e98893b143d5a8e1e9b0f2478495e81b84ac1722cf92302aa9269b36de546705a4bd228249def296ff7fad39edfa4317049cde183030ff9e1a30389330ed683ca5bf18a1f1c5d1c51b32529375d164e4ff80208ecc4ac8f2b3bb7c4420fe3904d931f937293c371d5466e0aa0e34b00d6acfb4e0c58f7f476a28e13f9884e0cdee4b5eb55c5564bcfcb3edad5367a6a5797caf0956978b41005a2bba3275d275c5d210e7f919a800383ae15989db5bb8c2f32433a440bc8ecf88a61e53b75b7ed8a01c2e4df9c4062b1c2b6fc48f701d70a8a87e34aa2014adbc13e7245a93bb0c2a6f41108779272ec56c733252b48e88ecfc0c57e7b989db3a5ccef273b165d5394961537143bd85c1bef0447647df647a9ddb3eccdf51f021bd69a5c4814db14a2df1b3986e44abc4dd8c2e9efc10bad6e0edc875bbff776c47ef0070354dec4310362cd23194b7037e939e3c6dcfda97aec58aeadeccc3afdc97f380e9a284383367c04da44e6f14a2f6f6046bf80c9598f35331f1a0e5113ba8603c2f863028743239e6fb643fff67e8948545b549626777658a5a50cc544b4e1639e4888859c598a9279f210a81dcaa528ceb010399ad36a9dcb4dbe86e2dfb9b1ba3c9169a3ca4ff1c4d288165573338e200b8e6140be97975c0a4a01d11b881ba1b0688513c657481b947a58c256a855e657015c8437fe54329753b0f14ca5dc2466031d3003d348ac380c969c5bb6fb579cc23582874b87202068b76b906831396ed9352a33632cb81dccef939a2960823a5a8714fcf13ef3f4521445ad7fa4d496d3dc4efb1c7837a2f9ce1089147a7b683113975e80d235164f2c02992f69340182eee9aea434f4c5b3f53f804bc98b2c1e60c6d0c98471c064e711a9cfcb5111fe990de1f10265f67d28cd21a74a4a6384d49e53e43b191a7e781f1d301129f523d964b37957d8e146ac0e673fcd126e69718fd540016b8314dee79fbe432bc35daa3f3078388aad066ec691bd0709840ed1d930e67738a2b355b4cfda2a494a9cfd26c6463e17aafcb5815b5b48ac8445e24d5f1cfda0b9ef3777c0f24a2f58cab0e4e63fade0cb9d2cb593caa285d84c4125bd80a1c6aa230d2c06e34a2c78fd74dac4733a1287122223f3ce7351cdb88d925b0edf02711ad3fa38f264d2f039299df95d04dcc0b17dcd11f1149f079538689e70eee6257ba8ba85532a9afb879e765bf9c64522a7f5aec4cbd9d8bfb1689f41dea405ad318c868aa5391754a78f8d35b47db53e8ecf87fec02ea293550fef24dc6e0cfc11e93b502d626c3afb2e1600409277155fd28a69bfbf18f2a09f67cdf609f61f8a3c890673951edb4f23d78920cdb9e08b157b1c168f45ad2f8cd73931a232c2b62d5c4cc108516dce83a4bb3260006e8848136064b63b69c5e8fcb9e3afbd61d9fc4623bd2487b5ab41c337246d643af851320faeae845109fdc4600f216fe429290eddaf6aeb6a8648754188c8b3ecd379e96ded7132c80288aec874cd785721725ca353b4903ba297d656f76292b5fa5f4f9be561c4de639a6aa0054f89af41cb7b338048ac33e7d8ba371515f93c4badaaa7d9222ab3d6dd2906da792a2769d17164a3ee090c88c545a3694bceecf4ab1e484acc4d232c63fcee98c29cb492f40352662cc0147d07b2580a19392e91a0dbe45ca52a0252f0d1fe4c976749a875e91a9824a4ad32df1acb490a2958b2b571c5061bb57b32665108f383c4a7d463fe69469322de535b6436ec0d31348b52283e2ac868bebdecc72e517c662ea1420e2ea0be47f8df233a9dac41e771df965e986cd3557f8ccfc73defadacb0c835c53fdd2f0fd4b802e585a67f29d292d08f49912853080132ccac600496561df98a1fdb943f1a53bbf403c907e392a3125bcba002b1581853775d46ad8a84d89b9d0c527f885ff7a1bed4a09bbae225512e8eaf4bac07d6eaf3db807dfa8edbe84036738d189bbb8e0b83da99150ec914475556ce094223ddfa764366ea61d81ccf668c9a81564ad7c843654ef76b2ed5bd8407d61621d86023f2d32b1d5b38784d4179f1db676e9f6f96f97882a70cd33e84d51da044ce43897a29e8010191dd76e3020d94cd3bd912696a4b2cbf873a099050f5050e1ed2443331784cd4da9d0dd116b5681b20de4ce509416829d191ad8f178d8b6a168a1a68f7a0a9d509b59d6cd9d732b57451ba7d59caa22d1c723552f4246b63fd06a5267695481dfad90f4a1646f68917cb45692155f4558c59cb9e6f1612902f88593d046c10915ce832aa5a74a0cf22c141fd5a75c91e608260d02db438b5ae0c2d453b39ad74716da369e58239773c59fabd7cd27ba89d90a52ffa0c3b4624ae55a9d99aa145307dc6f5675e79e3558f1f8cb09086e9e61f976c3f7f9de8331061585d02b49e0961dc181c4d8e048c0529375f4f8395255be020b88ef7e248a757fbddd76ee806ed79c8533fb2f1f8b00f0b42b8e62e22726a128239fbe2fe73ffad1d0478714e044a519fdaf4e4706b801dc16332d2184bc824e9041b6a6734ce7585cf52fdeaaa9498aa3fa449102819f0005b45b9f6667f4fdc400bdeb3c8d4f96a8144484ac9b8192bc7c2446cc3f370c42559c58fd2304f901cbc2cccd89bd5c4db98ccf407e05db64ab4c2176dc67ff2738b667f9706aaf55578e3980512f3c94e1c83fdfc096ca7b00ada08c4b9a7f2981dd8c3f3f323baa278672bc14f3d8d8db265e2b4c21815a78f7d19a500fc55762e262930f4b3128e2a7327b23dfb944c190fd25da2a7a6c2f28809f1e7390bb4a77614e8d9dd4d11a9c4412a6b06f6829beb8d73898743c877264b60ec764d5fda11561eb330f0fa6fc9e29f3c61444838a18f3b7b598987bf87f83cf8a0c707d900fdb815b7b5302e2e0a0eeb5c011cfe26a6475f8608b5ece79af3ea01222d31b9b3853f20b4569878eb9b01778bb180136b8917c6130551a2ff655fc189b32c56875b63e8277465b8259b2dc8a724bfdcb9645eecdb04da3c7ff11aabfe6a5f52026c4cf1978ef96c9f31a56c230882b9a2feeca8b39a187e28e1294e2f79aae36e05dd3865255b32214d4ec03e3f435724c100194febe1452bdf276310313add860c512a98e9de30fcd174571a482cbf73c78e5c08b4f9c49db2bd84bae61967dc7b7e7e807d9ebcf9af37e5c1af22459ebc14c8b2108d54b7fc92f0e415b25d293d9c98c921088d0726366e4762d5c16b62a72d3e058764ec60809aa67c49937576038955367aac259476368557b563c9b418ccb7055523bb40ce828e8ce4ae77d8b8e9acaf4cca4f081361b3b084f4f935099791bfd4b1751eec0aa0d1a604816ccb54923ba5798186c0690090ad100ccab762315eb4d0ee2fa776a9d09933c891169770441c83ec869ed7745f9ddb91d665069cce12176d12dbcca8b1048224a5119d499cd0082e0b24f74a6d6cc4640ef820b6ab6836cd6d374099f74421d39336dd554ac6c59a6328d5c4ebfddf7e5a1aa4db59b44912b25d2b1637e8a185ac0fbbb455a2cc51c3d06059d4d3bc9b72b419c3527e85cfd0ea43c1963f023d279efb094fa4a962a75a8812a24bf50fd36234b848566b364e135b53b81e8ac88f11e3898cccc3ce692e2bad8a36dde9b7d61a6aee17a0ad9ee49284a283424f4d9130023ed4858813e0347fb41d54f96f7986dfbf09875b67ab3ed2a2c0b4e485391f37334b7280ca971cc8638e572d60e46b5fe4ba501c40d5b777741f316c6fd5c56704405ea6bc294616ff95d3ebfbed3fdcb256d56946b6c04e058d732bdcbbf14c4b2b5465581d70a26202dc5d50b49f321dbb18bdfa69a0c0de8227873ee9b67647f8a3a7bf381295f219414cf7021d7b633b7f95b04f43857ce6998beab7ed66e02ce435f378ca9afc2d9c97267c9289dc9ab53f92b4fe7b2b506ce9f49b073d8bde4c82496ea32c16c043674bd820f5acb061b56b0edb407cfc93423bd161eebcc7be83b4127e1fd4609651aef10343abe834f1f164a3c949275cbd854ab9e294e231946bf9d5bf170a3ee72466d9a6562d7656d0525d3209ec8d839fa8834a3af685010b97a062ff3ae0c1a80a111424f8828620829497fef4953551fe52ee21f884a3fd53dee6fa17644471f1c59f7f7e01f228baeba95416b67d24cc975394a85685ec21e576c7446fbe1a4a701dea87f7381a10a929fe6a751e80f5d7a679b56dcd61db6553bd133de15a585c1cfc32babc38e429df36b9cb1b3e93c10911a0ac7665edbb2de33cfa3b101312a0c8b11e14b129197ff0eb9f121a617c44a805c17b44943f5cbcf4c288a5aa32ee2a5080ff8b943be8d6e0a68ab835a78f68c279e43712e350c34c502ccadbb24419da5c3bf22bad29696e0f3854772d50f7968b9ca069a2728d3905fa00329552a684e4e9e5f82d55121c1604db0ee006f1e0de516ab1d70bc0ea68472480b58334b11f2ad7a5ccb5a4032ba260cb212eaef0be6ddf4c95e11c346772740989e9faadc8f8c11876f861e512b894d47a39921ab4c6460c6e8bebc09354fc804cf9b59bdad3cb94ce9085005eac5cf49533945096844f8a1113b7b4af69ee9e75f7d0099d6e75bbdd39c240ebbd0213f73a4df4cbee7052df8dc2cc845f49324955886d000e51c89c8bd9a78c043fd1e48941fa253a1d8fdf06e00d28860ba268e24d049d96a813972283575024338d4c151e95c2a4510653434596a7d1d305581dfb7be6c5115b9a93152c70f647244f735a9298faddff763b70bbfa4d5901bea34bef9f4219f634fa35a93533000336dd0d99116e4d3a23880084464052a2ec469d321ece80db6bab330ab185b32a876637306004978b49bb01081d3a58ec5c3b2f9b3b5db17a1a4d2e9c9236631b8624cd2862fa1c324217ef737e0e46cd79c08f67b5f39db24580b8716bfb096bc790fe1b02ac658c8a3c33cf064d2ccb50905963826c6850e3a4117db24ee9b2bcd77171dab1bb4aaf63ca9847a0e70cdfdf3baeebb801c617eb6e7dee919eeebb194b238fc8fb64705eaf05ca0e8fc4f56510939759682aa7d5277b968c89e172ec3aeb851a16f05bc67c4a6715cd10a8db04dc5deb3457c757a65c8a3c667c5e3ba3c81ceb5036cc17bae2a9324c055af9e7234664c3a306f5450f3162a074bc5048b6fd7663501951c48015fc300a7a97dd7ceb05a8045ad549414d086536d6ba4fb877585d07e99f7106a36e009c422fb77d27df07a1e93137268b1bc8a23be72272f76ffb3a32bac5248c7ff8fc7ad3d146e27950700e3c2c59aa1f5c4a0d083d68187521a83e36b473cd5005f19b35e6edc23ccf897a4c3fb93f859dfcd1f0f26cc45a40859488c8c4a11ed9aad3d6cac4c0c156f8332cd24c01b1bde76017e945da6bd1e13db405e8aa020319a69b4826017a36a4afa4a00d51b3705cd4f3022e88353c8a5d0dd1fff6dfe234ac3abf1e9156411e34def8a982a74b9911909931e5338cedc5342f31e5e251b9739911c2954798b5140edcdbc72c2db3d9802807395a921921326292c6bbef0c4572a97bb5d7cdd5049a2294788648820a1fc9bf143429fb3379fd88c2ca5be114100b2711ae057f598cf75df8ef1c38865325177b55648a1d15b7a5252d04c2edfb49079b9a3b4046b1e1f87ce526545a018a071aeee55747c87919e11237180b5ec6e02e41a2e5f77de1d43155514b9942ebbbcb2ed59a8105e89fafcd38858f8ae336a7afce90c14e8eaa874e40b64c03afca87c1cdedb4261c9ee486e88f09527e4c24feb0924caaa40831326cb457a85856eb55d282780f5b939baf9264cedbcc435326cbbc3b661ee33848592d6acd4d983c34e06a7b5bbdb4af61f1caf746cca12648d9da68786846bbd6e645615496d98242df15a44904dbc2e4721600338967605d57fa23233cd0d4af629f0ec9e6ab90fd4a7412817a33467402b8be0161a62288bb95c135cd23402a11918fd250f3d6d0904f1569af6a54cbe389600542e2fe0b3707eba68556a9c522e8fdfbb31b6c46212d765977a0286045fafb3918d38c38267b0e3a972d811264401cdf3586844982dd59af7e0058031992c77450b0250bd7e5764320797d48a0f3f36030e45c1a21e8be1cf44814d080a7b15e267236f4740a927d028e4ed0c97bfecee88272bd827341c703b1d4c857cca9fbd3d10a96b3e6f9fb1421e1eeec91d1cb30cca8506c120dc7a6c616e370138a02f2ded6940db5c1e92941c416c2f2af7d50ae5381fa970c8a0a44edc0fabaafba29f38c902a34f9f523050d944688ac0dc001df3ae96a435fa621cd81a601965f2985b798ad3243c70765eb68f7f67e1584400b5d9e2729315e714ac75b0a0b32c194a75a3e9c0f0ee1acbdaedd40214324409db721cbff86ca6887b90b800e7860b9ada5e3cc499096a954134f4e765f6a58b33abc4dfa1e0221b46fdcd08adfe3953393f16a8a2a117cc52bf1d0f428209ba78b10481c55803e95cd3ec79099771ed3507e7b5f6551fad4b49f0f7a32c71a052187ee35c347df512ebc40bb7ae94a2389137dc510b694233b731af2a00bd1507e07c441d9c2f219b02e923ed5e5b7a1bd0b544e226474274ec8f35a85130468320076bc9ce0c26dd033b154356d9bb33d46c556988a5ec27b97de18f4915751cd18aba8dee4a73eb172742819871c40f9495d1121f4becdffbdb6617d1abc3152254489d6236309efce375519506ab2fb31c2448b9b32d91efeaf93b80251915292cfbd573dd5ad75726989e843cf9e3934eab3bbd7adc7087be75d787647cd70a5b69ca3337b327dfc7ad5569e23663ca5ee844b7480054b31fe72b96387aca843c0b59334d599441dd664685b56bb9f4db46997cd731d2da3b0089ff13c11b09f98e135666da5306a248611337788444c59ac0aa5c614035ff50f8272cf339acba389309006f8c477867590bf080bddf6bfa523027ab143c68498b11728afac88f684ccd8e04806c91d9312fe8411b6a4937d8102715a0bc838a49f1d0e0b9e27c047a1cbda06fefe380392fa1fbfedc21a0651d93b20c81a1111a7ed0c44cbcb8247cb703e575646a5241a663350e5ee0191bb9167a60cb71321cd7211ea634b735db159bb5b420b343d4073a333091046c703e45a5e43fb48b98fbfba7761d74205c68f733cc25bd16db1a4d633a9cc89d0cbcf3058f4b58e231da5fe8de53672689e03de0ea79afc6538a62d36d729c33c58ae66712493a44c9799d4016ca6e249e2dded55ee119f8d43ad1be5920f725668c8f96465db3a1c9ae67468b8fa006363d6d2fee85ed9eef9a113896a08d35bcf3783d121589757eee278b94e71e4e94e41bac9c0e7bb844740e3abcb211ad2060947948086da401d702af6dbdbbb18bee1375ac4b2f088909309677193aa468fc3125f3d6170a1afd7fb1e551afbbfde8f61d0b71d00ac1fe932c08d62382c1f97af39417f7cea6981b125f2f3b5f7369060f67adf4dfabd5ef3da7d1eaffc64e3178c0df4958c132b673b7e24dcff797937f72567034de94f3318ba0f66c241df9003c2f09f9f71b71f7974fcfb6e2459dafe8f2135090fc1cc1468b449dfc2e5b67d926c0af2be9a478da0005d53f3fe8fa8356d2470d01185b0e45067293aa82fae2ce8445117af79631f84375d7b4f9f4e7855a9457d3181c3d0a6c13dab0a1a41c8aac32bf5c41864cb436c97e7c739d749b124941dd404c9af42f60f4da322fa05dc5aac283f9fa612567b80a664763787b9f1761fa06ba811147deac1207d0c6ef943c33d29e9a031bc144b10b32e30cef6b7d5c7ee4a1762825590e05f6b3afd1212f95458bbc1a545cd66ba2adc3b9a55a2aa3557770c610c6d051239da83d277001dbbf035b77c7ce6da840f04d43030a3f065eb5f74a0f70b78f2bdc7bc9785c5b1369c9115d790cbbd8cad3d8a23d1efc6db23b8faeeec31c15489bee096ec7381f8729e6886be1837658b80a135c4ee7ffa6d06c6fa304994bf8ba9c4e7edc8f50836ce1e1c3d386a1c6edfb9ee95e5b1bb52dafa58f7be9ce5b0e882195125a53c4e33e795eefb3352da1c20872bec804997f96659f111ad109435b95773eb88bea1c124c4516ee559e7c581d9b1143ec5ee10f32c519e3227882d82c4e74250d429c23b044616e0a70cdfb63b616562f6231c37bb53a4e4206c21ff4d9d1c431a65963fdd765f068daaa97d009778b37dbe92fb13bb0e3ebab4821769b56b7347a32d1f0631672ecdadb753719d6a17949fade60ec0f1d942bf35eb5fdef580bbda9218bdde66519d8fadae2c4f36ff389b5e8bc79e67846aa4faaad24854e95a55e654cc4c4da3720feec8dca98950ba6598e9ccacaa09a404d52945f55248e082d366abd210594afaf5101cbda6b4ea4abf6e3efbece722946f79c2de62772118514a3e5dbff9af6a22b5ddd7b152eec6709d74341278a99637a94e297d4cfb40c8e954064fc3810ab0ea6e5a2b8d77f6fafbadcbf05385f82396fd07a0441b857f752c696a469414dc1f63fc4589df6a018d5cb6a54bdb3c59219ffb3570172b02fdd65bddba677814fb1dd29ae243a7f060bcb25b7725286bb3745eb7dd95be95bd7c47c1aac839153f2e480b0b1ff021e8f4a4cf3b34e5978093a48691fc83150f2e875c0cecbb37f6381de797e0ef00dc8b883d25f9dc24a2a034e05e45682bdd7fb3c865a501f63e52af077a3ba2501270af23b49502945e25c95c44e2a72753a338155f4aa2825b35ff71efd112e499212e3b7a81d00693c768c07a13e736bff9b4e35dc5accc57a344bdbb776326d4fc8e870aba582bb7f1ce91ec7993d40247a9e4491a9ef95a5853dd4551a860f1c5b5611e2d6adbc2aa5c6d152e5a546a87f6a3e5a5e0221b658e30cfdcd11b9d873690b833c345182c771b0a14f357ec53a0d611649b93af066f6ee3a460943cdb21f3724219771f1977b5f3846750f6424112a885cb10b10f48ae93756d470ee3ed54ed81ce8cae6c3877db6641b0643689973367125ef406f696eb58c67dac6f1c1767d4eaa82d0f4d35236c998b3c0bbec8301ef4871eac90dd66739b2c73936dd6eec169479eff6d8632302955ef3e01e20193c49115aa31773c625a1eab0d69c01fdbddcf56b6f6a5a799d34d86438af30624bee30d6aa1498514d436a73927182302d2d4fa426634492cdff758548690817c85aedda916b44073f881d0e1ce6aab2e7aeda4cbf24cc324c61569186dad962ec6de7bd095ff7b0c8d52f1b64c9686f70c73a2e3c6e0e29c283811b93e76f4579878bf19a1a65cac5cbf1dfbd53ce1621dcac6b4bf1b5143a90661d14ea2287de3c97d1066a4d405e76c20e8d9ce6a622ac9ee42d232c60d2ea1f672a24414458366e6e12abcdd3918c01d7e70aaf05ae487994a5b84fb97becabc2e6a564d1c0638516c384cb27f148d497f208142b358366adeaaaf229e360b28779999395849d5fafcd6d89d96b05267ada1ed91f9bfac8985d396d38f464407d46017dcb4867d7c5d1ec491d235a0d1c65233840b9f8bdbd237097aae9ada2618c883db74dffb7b2f6d0adb19c76492332b6e8c80d8f3aa6c582595464cc0ec84dbab790bfb1de86cb54bbe24e0ad25b19ea57cac39504a65daa8791c17b2d3479dd373ea8e9f0b7ae0388da35b29953c9d46cf08c263d24e66a91195dc748a67ab3ddc1d1b565acd2a55109b199c553d040501ec9c3c101470bcf49e45f9b40088dd22a0a40c2895f8e28d58871779e145562689e9ba8893931faad1ef3b13fbe728d11f0dbdab3d898ca387ad000b817166914a4dc89800c81c1b526ffc2901db2bfc615a645ea6d5d49ef7728900978c9ea1a02ff31545255ee28100e4ec7053a2631e946ea050df4559fdda37ba1b8ede8ceb446ee7e24ef41a7d0c7caf81b65b67262f7842ac705fe36f803b11032c3f4eabd191de8bbddecc64624c49bb0c098b73095799d5c69b0394f3f884c4be4bad81d892de9e56b8562be2ef4d20ef440d028a9b13a11cd8fa6fb9661524895dd322739c762bc7c13ab66d7912566709ab1f522c406dc6e3b54549b86a7b29fea9bc4604bde5d1b4330817d0d7647be40bfd5030cc6601df002854190fd186ec7ceb90976503dc76238653577055a4ad1034198b32d30302fc9bb6560af0705596ca06b4b6107c5003c225bccd708edb6cfb9e84defdef90357990be679485cb9f112cbbad0c9513d166aa8184818749807b2665e0a08234a81b8d6ae0558c251e684d3c0897d532f09a40b1eedf699639a1558f6c75e3761350465114369edecdb9e04f2304527a68183230327280e61d2ca7b0619efa07e98b133fa0b860a735a04619e2002e9e9a71732bc51bde299beb151d4d5b692711a827bbddd1e43592bfe844ef243065056e7f40ab7760504c6adb9d899c2a601eb2e44dbd635c1b051a8ffbfb1dd8e9029c9d15a4fea5b74c905cd615ad9f8643a9af046d59dd90121092022c0aabacf2a7dbcc82250f451e008646d51f0c655a6c86796016fd266ff7b2ce86f13fe6154a4665c7333fb049d6f7f9198ebfc7936e74340d74c3e745e6149eb31988b046f38e522b2422a6bd154ae0fe6e8beb7aa4c816bd11436a59956eb134426ec501ea9e631aca105dc86c151c0aefac881e0009149a496091be97713360f935337768040b217d9c407828882dd48091779884a834ca2999c031ff13681a1884d3a90819ede08e4b07c8fc5747144c598106e02d7064fe5207613864ab49276eb27a370b197348bd556d9881174626102866a2f862d6d6c10004f2ca0887d7f17f17343074c401d78ac8fd3f47ab55cb19f757a18b1e33b324dc8894f0307fae4575108d37cd7a08aa9c157df624a116a225a1aebb0fa04b38009a9cecdb28d20df2629a4346f2b48fca22bb65573bd28652d02e4709b663d7923586ff586f131a1c22f29f5303db8e5828011dab99bfeab744e816123123b66bc96e9e64e758f805bb202dfda12395c902566c5f506626f0e2cb038a9c7676817dc72e1bf196942893112c3468e306cfe1c0ed432497c921c6b7db911b823b7ccf7becaf0d1dfa618accec50ac6fcbf48b2e01bd3d781a7a8d21bdbe54c612ed8c8e433ca4b7f8a5328d1dd9098ccc63801d171a0f131db61dc46878a0923693c19f599dbb418fc85c7c73b7e04e3ccf5786dce0e06e084541d9e1db27adddf10bade7a791511e0481bcbf048da90aa09bc7bd019eeb51748644e9e6a923a363e1bf5c6b537775d5e722058fe6402a725e61fcc89a6ff6e379864cafcac0c03a844438d9aab5a97792ef193d7d5023d706b9a5b96c865dd4873b350e3322886d3a512bab9f27c9fa91b3b870e26647d52626ba26b332c17a22be9d4add760e3375d028fd10e7a6df2c416119c56830b8e0fc91c6235d39a162a047cb692e56eddf63d7138b0e26aa3fc693d7562be780c432dd54fdd8e18a57146bda6c30623b846f82737d685b2f42141c03edea0f4924e55a45bbbb07031c9778047e62463849dc3a2d25f5666a2b502a1d3593ff07f04bed875ce1105562492876d59193e79ca4135c387d005cc5e758faf234c8128f740c35ddb4903e5cbd69150b9e5e145019b7448c24b12aa0b60c1c0461fd0db1ba51eb4a1b81aced55864b569fe754dc6418de827663bffe0b38aab8cb0b75ffba296d84bb6ee7a90d16ff3ec936fe547877db655672288790f5b9290ed4f9285947453ee85522da2e033a6f5ccd52c828803d458c84647e09ca9e66c564d46b10c85666900bc5149bc5c6468341b3f737987aa0b7dddd2804e9f5d1f0318d910a14dfc9202141cfd79a95aed2fbe82631584c8a44af2584d8ffb2a56c90fc9f1d97abd66fb02bbe384d339fb7baa271dd04cd23f7ffb3faaa7bc34843f0f4353f21d3748eb401ce0e795831e0db60024f47884def0f5cc6054fd99906ef4c46fb872ddf55d3043f4e6c23b3ad847f8232f7555efc0fe5232d7dc7c5b3dac37be54f9781e030b2d2cc1f41ca1674b116127a7ddc50febe8eb00f1d14298791c65381eba2fb49474523e2ec5307a31bc65171ce3f098087a030a79a70cb844e88d1213cfa58e54904d86d2e0222075148152a7d1a2efa27041789aa4f8eabfcb5a43f2f905a6b01200967a61d15f0ab11cc558d388fd40ed4206662ee52f41930ecf87a0680223f8ee0b1f197e21e1666d19f6f4d5b871c07a92ffb8b8e90e30c16213d6a8d2a4367b5d207fbcebec1842e5dd7ce59a9a3f330ace7e29428cb660a06b3436b169ec66b92bff32f7368608a25a3ed7f16ec4ae410111a226c0194e7aa3a98e7fcd3ac238f6f34f5fa5711c37b6081281de0ab688ee667448fdad44ee54da9cf6a0f6f94cd0ed114f019b5bf2f9e6ed21622f3699a4437136159275c437736875adc7654931be3bb41d2e753407be0984e923fca1b4b086a5912c24b104e922c7ab58fbc374a12d75be1bd2a48285b00365bc3692314369eb1d71b85c42f09d747ee0054c0d9eece12f11596e86443013058c81843033b96f2031e8c2c39611d1f6fb4d384109fed2869b4f5313da0b9800b41ec60907fc9894170486fcdeb5835960ee31fe63a0d21e3f8b1539e938f775b1fed43ca09b0d6f9056384ce40d992008ab0bc44696cdcb99d24ab7bc031b40ef7ad59c7bc93c47bbdfcee8f5643c0504dfa659d794fe8f8643a0bc2f3ac9bb99091af3b0ae2784e23489f71d22f42a09e46b4de865befa2a346de0bc882642231c1f76cbf6094c23002be31429d12222de7450de501cd8b2af9202750593bd38b6c35e2cd25fe67819bd523ca87e5c24b7e517b746a23016f645ec988393a89f1122b62fe4862bceb1dea888c825bd5960d4029831af2b9aa1075c647291690fba5f82dfaf332ed2540c9bf3471b8744ff79882d22b5ae7d318d70192b5a12c4460d46cf1af641a69975b7ff07d41ef2a65ff1e51f36512af357c16c414055e5b3eef7b5268ad64650e4fd8c19f4f2fa3a842d1f2cdf0a49005f5d0350141f9490a5e2061dd03eda013346af81468b1855f1b9718020d0d158973a41b38b270e3ced6b50c4ff42bb78b4a3361b121ccfef2472d4c32592a2a3f4155dc202a8e784b661a1a28d224714853636900f10c37a31deb824732a3d56aa2daf861ed337d7cc89e54b648a78666279d903d475f7d0520561a4a2e1c25ca1a11ee59543d90ee4b80aefbd9d776187edf7bc35b3df1efd2f0c8410d5bca2536c75bbf08f976efdbebdef84cbcdc40916b9d38f1a13f32ce1f679fddd7c24f5f926c1a2c0a5ff8f7a657eb0bd21f1a10a0adf82e62abd75d057b0960a08773cdd04168a40ea6b9c8cb1bce3f101b1fd1fffa4dfcb610905f8279b9d2ab87f2db75757ec60c93937bba28a78d31753e536f04f9e84d6c7411adf693c26ded1a4451d08afb978bbbb2bc12591f520a5d2bbf1aefba606285c479ebe5acecba1f008bc9b4afb3503739bf1e6bec461d55dfda827fb3195dab61702d48c505b88925e2cea0fd0bc86eb7bafd2f480562bfed37fbc92b2e2e3904b07100f8da40494087a968425c85bc743e011b137129fedd7752a66f1dcbbd975e6fb14a047ebad528168f98d9491fa140717bc20dbeeda0c0aa4ee2d0498b2e017e21fcc06c5a8dd63dd56c7d93ac939832189bb20e7816c0e0b3d93e7dd3cd51c79cc27397565fc31cdb4ee34ef9365c2812c341c70e4d312e0076d0154dcee2682a1ae2aa9db1881acd181954a2e1743661cb7a3f524a855c6f05e4629578c357612aad2b23bf8b8e9151b14a008d4cdf7d5574d330706ef158b04f941249c139d7b2aea6e5bfa16d649055a7706e8b883c0d64a3331d08a65e56dac6fd367ea257f84ae464b3fcef0555aacfddd6f6fe9dc5a2c28f2b0382069deaf7cae4c14dde2a4af2540967ab8fed78d2d377d70084c2266d135b61410e1463f212360c290d1c7c4792f721b046033d6ed7acf193405b973818af0ec7e026c80d0a28b9fdc09ea73bfb0d0fecdc347d193d107513115d520c0739fd34ae7525dd795d0a4d6a8bd189042303c3944dc2c3bb913d40a918722ad5f08777bfa45e90125afe3160a9cbc067918fff1847147324879610249f643f88086295a6d7ef1b1ba0c6999f4fe41f2ff9b88d54445b94f58b066d56d405b452b3b38c8f51fea981f4a21da56426c1cbd71cba4dcf362e432481e4bcd317b5bd0a67db67b0a559dcc252b459ea1847dced7c8c8b153e3abe034f70e2b29f708783ac9c8dc073240f49a7609662801c9f89c48d55772ba037be420b47a473329e21e033021ae4da13729695a7bb563f6c277ced9bc63e305ea3bc2b1f9c0e7227099230376bbad2091e51edec9e4dbf0d2854f8e75aaadf13a5adb51a95bc19cf53f1d13716084390d1e765f3d4b296079700bfd7647086e9e01d4e35223a303b9a10fc061962bb29667f31261e7d6d00ab8b044ca292b91ddedfbe6877d260096660e23c6b8c41e883763b636c71d787c019b3eee8665c401178af18a6403dcba1c8b6fdbeec3a0fee6b2878bc03f30a82ea62d2cd60c46524e445205c68cce4a49f145e2aea07be1e7e38e41ccf7e091cddc2ab0f708b9d210c8bfc2af7b4d9b7ab67205b53667c8e7f9274e2eaa3c5e4cff1b6d637b802f189ab81b1528f912e0f398a6e9b6dcfbf2cf968822d95a69b1bb98f6bec02f8b2d4dd4e5ea78dd4648d4e9835c2c5d6ea6e57a633033fef44eb478a056a485c7523348583bfc549673c39768c9e0c51649f6792be29a213655f54ee2df44610776c42223ebf6eccc4fa6eb7f122fab17d82b85941859e556ec947e5af5ed293382485a5dd2e8fe0e34edb3e6a0804e92f23a4bf9892102abd65310a2d65b1fb7147d5a29155deae468fb8e470296c6a9c55f04b2dc6d42ebf8d65e6ceafbb42cdb526f7b1af844b7185234b687b6fdb64a530c5d63b60b062db1a9b2445b6d424f563f784e0ec58c70ccfc6c6ce04c70d2d7e48767ce7ac6989679918446171e50791dcaa69a6125c580e5dcc4ca1bce6eda84dc3539c95f658c37075b16f6843b3f5d306cbeacd5ee5c14906f58afd327cde5c43d5cbf7530ac0d510b206ff85da670d5ccd1b4c40365287bd48fd8ea07d33241f7da4b68fb0b4a4c55f481f24118143c7a52c5ffb219cfdab489303321cd0dda1232eea3ad777538a4a7ceefe8b01e7e10681d51bbb283ced841ed0d0df633d40fb174de1397c9b193c0e638828e82e2c9a20f59329ca4287d61feddd39fc3e476e4d5800cf9fe1f2ec61ee4313409af24d344c38c57c74b3ca3c0d0dab0ae4e8b91c5266605797626ebad7ab3238d4a7411d554c2c233d6c96f0cd15fff9c548cb6d38a7633359294a4c2d203297e0fe8dbb2392385632e915ae5d230d9eaa57ae5ab014b4519fa4aaa953795f9385ff9459b05b7f88f98cce6821e3532f27edf388d9d98fceb5b92c3276c4cd6567375a29dabb52a75da242b7a314809d267959b263df1e6f373b9c77d8861c95569c9e9797aa02b7883e0027637e1051995b20aa1e33db0bfa6a0c679088da269cde95258d64874164f02fe2a716bc6805bb2b5ae2e85f77508f28d09e5a493fc7c4b4ff811d0b205399221532c678a4fa0b95df74ac3c736a31654306a9dcb4e9465a15adc26c57442449a8c83319efdecf6d74592421c0860b68aaf60728912ece1ab8daa507e32caa501484fed83c9a5689c2de2ec0af973caf23d907b403a64b6b1f3c21c2e12fb07afb69b89aa6b81718dd20eb03e086e3cd27a9b64ae9b033b693fc4e1592387b1aaf070269e2f716cc694246582a7a7056c2ff46985a397770620a0b33c81ca8c908f889cefde81ab7830d7162800443649c13961076ba66bf3ceb306c45cd5848f8b6bcb6495f7013fd3e84baad8350d369f53c105739851238a9f0c3bf7e03cde451595445093100e63c376aff60a216a5d51c43ec80bf1fce647237a7f22bf19c83199388be072c3982af0259066fe01afaff41a285f6d990bf365f5546d455ef7ee8aa0c71fc95033068d0fb35e36278a00701525151b92729f20716ebca2a2a91aa73b8b3d7cf1095874df4b74ecbbc19523818c603877fec492228aaabcb49cb0c5f8256267b76647002d288a0d23591f620067e987c241bcd08ce276a073e21b3acf61331edbec6bd129c2e7ea3df45be5808109dde59d1baa6456912390779193ef5177f0ff4e977552ad603758a1373dd6b5094a626b55de0f336a058e390abd183a49cca8a965afc770cd8279be614b1ab9d1e3cf458490030411c2e1f2f04edaf1ff1a33d3e08961988b2ce49c2b0636a076cec93e6b2c90520cdedac14c4a87443ac5cdb189a42223ee196d21483486df2a64820c35ebd7b680b1b7f6a3d5dbb5756a7ae7d79a82ff82ff440b4ca9afcc1ed44dab63bffc93a3ec0d87697d2290374b472749fc19a42ea1db1dabfc41cd085fe6d52a1a557e8ba08e9d01c7db50515c2a32e3c5891a7ddae9223edb98edaedaa01915330d2b84e2e27a2b90ab1098c149a81b60b522e094923e158d9fee0f9b099a281d6ab3e9445ef4f4fd062624736cc9e620d256743c62c0d387c348c2fb60453d74f1a8a6164cc68b81077cb0d29d4d2846ae974afff20a6eb3df9e464f66c47b4bea18301c2eac14adcd9bc56eb6a12c7b6bf45c0acffab7a7b1fe70d69f6ae5093143d7dd900ca021ae1c97700ffd1b16617b151e8fcb7ed40af480580c9cb4a4c92c69b383cb88edf652feebd84bcf52b30df8dc58f485d0e74a8b2fc908bb69f2271a9412c9077331f537765b857be7d6ff48e0601b03755eaf7e5d2434077215131d55aa3a361ced31dd9f7d055e6cd589a5a48a2d43b0121f94b6a8bcb8353526b8c16ad83f7501a2df9bb237f25fd05bd8449b295775fbb8b6abe34f0ea66038f385c2451ea912db50134c77dd904a41ab0c54c1173434742c690ed384ad073ddc05144870c08ce31fdfb5b86ac5fee2f9e49a276613da33d682a862fa272236ec96c36015446e356d7adf5fdc197d757b7d7258eac95d90481973785b6471ac802f726e17dc824a5860dfd60f98f253aa6bc8376c71bbf88e14fcff5f1b0f473a03229308410fec36fd004946a8b6808219e95c75fc36e9dea6cc242d4d2f076e3c17bb0e7602a8012afe9450778acec6ecbe39d865b8e73ae85f9f3c448e0d7d5ca2bba4fadaed67cba29b985e22a2442daad690380f751dadc931ac9285a6054fd05e01a8ed801b0db90749644673383fb808a8adbdda51000bb08c522fc5768e8566b4280b0408c36d9a6d12ef4bc0a31095babf53c64793a3b794eac375708c3769ca46786bcc2d90d8b9e2f8dfe18453cd24f1a495fab30be5130fa2e4a5b1d22256f43176c4a58028ff12eaeec070eb35abc8cbb5beea7a0d3e390864b13ccf18d74debecb0f12e4db6b1b2608da8bcf3c4a6c85eef8db244b691d5732cf20f0d8aab1f5b8aedbf4777b4f4801a491f40ac7f5735ad60c42689138cb2d648ca60fd67548933c869d5f640aa29fc533c928911e99d375fd8a7ebe28309376500c48970cd28c020c7f3a338fe889e1766b276c6d175e090263edb794ac371a1a22b9adda656a7d3e3712cf20dd1413999b0ac37f0ffd3227c40d136a78c74592ad3d91e89becd842ab08a3d0b16d0ea230c5994ac0bc829477fc6e8891778006c50533b83a68542d30c36aa914408f9190ee72b73379b1ee6cdb9d8db26cb50233137ab3cbaacd0736f5ac3995804fe4949bc0dad0e1108f96f39c07e1b63081908ebbf9576235bfddb489460bfb94217d09d176947e8d50dbf4dca0fff975254a3b275b297b6930b2d04d9115e6865208cf1097a0cd1c41df0f8ab22434398a976fe8872c37d4171202fdd969cd66959ff487001c41dc62dbabef24b40a14e733f824bd4bc401cf0961aee662ce2c08e7e71dde9da5e7fcb0464ecd7c7b652403d712bb9200dc6c4b9dc80308b91101febeeb9d0ee049af0670d0205307a09d386e1016ff3e0247e563addfe562bf8f1b589a605cc713459c887111c1a6bded18b0a33eb3db58dec051796312261028fd66af69e9dfb822439c9a0274162d61a1a4c72cf0b2208404114838055e53ff0fdb86856d3c6e4a28365056c9d28b19ae8fcdbad74e631bfe053aad406170f9692583f09452b3f298dc7cf2158b4429cedcf38619e620dd60723a056213103904f39505fe4b956ac0e7e4b4ce3735d2db55e2400d27d5201fd2bc24cf45d2bd8878a0838383450e6d2826565c180a2cfc76cce131ca61a05bbb11fbdcb8bbd67e40d668ad90fd0a11ac827a5d07cfa0555d2b59b8f2f2b4f90e833ff119da56c87108581c2d9b5a7a03c70c39e1005f18f72845be647e7cb63c6bdd8d58bced3ed7df99f355492a32ebe824ffe90b33e8bf8eea189c64038f7870ef9bd1aa9fee79a43dfb806d11e55e24610dd3e0a635a2d039fc55e449d1b62f22c1662f619891f0ab0ea2ff76653c17985fd1b25881be516a66449f1832ee84562b63bf104f8d327b1e56bab6d6e14ee347a64b5bdcb23cf1695d7c85ea181e875c570ab971cdf2c80a15fb49758caf03da703063f19abdb6ce6e4356fd1c131efc9b2cf1d3dedf968608391ced7b96ee6d071c6a8e38ae76cafc922c3dd99b7eb5c25da32c8892435d119e8533fc3bf65ebd02015b7013b82996ec98d18793a6c43625f0ff004762ab4df9c04e83b4768ba6df27a8b407f5149907819b5344f0e4a76c433ef88880293866ff353ff4f4b5f35789694ea52008808e815026fa225f0cd3636f65846fed129935f410b495045dedc4e2a706cdc292203e0e4188a27690bea5f86f22b7b5a857cee935cafb9dd50ab51b32c6ab96a104271a12afacbd795976718602098b257abaed5d77d1eef63650f2db3e2f815b83a359f31a74486571335467c4640d5257d731515bb0ded783e589fb8559e3d7f17f455115860d679930fd5984411c39c2df51e320acfb8c2b8249f0f80501558bc9d97e4edc268de6f98b60905f169ead6517061521999f68d1e38c251ebca319e222f8e48f084eacc6ce695f847925a83d2db6b75ac221d6e092e7338a703e66f5ec3a3a4930e1ec40a7805b167b1256af10622fcf44b11dd191f4f145ec0ac5fec7c99845beed924138ae664b56fe112ca3ff0a2212a043651ad8b579f32710de1f11856dfc12c6be0f817fc81a0e9e22ebc75806103faf3bc26ab1854291ba3ac0649a8aadb04452e18456af98617a3fd3538f315e060fd875bef2fd0f58f8c2bc0d134e14f71637cf880c955edad8e8b611258b9d0895cf2304e46380b28b1c598043e82dd57e44ad06bc617f78ad02b205c9a0d292e0ccb903a834989365267713e54690647ea678a0e30f8545f43420a700b965204ef96330855644eee547351a9153ddae1175c5c3a49615261002a89a3c7d28061d6870254bbfcf8c4d3f232c3b458bbdf452b4ee34f22b3848515cbb8621ae96977019660a2a566dd107b2e69d8f88b8b63a3a3db4c384833cb5dec81772ed77ad64ba5d2f574c2cd0f344976ed58deba32a0351fd15b151547da3ee939f239e01c7243e77da80e84b77ec27a9ade9e837aece030d51662288da86781348da2cf9e4d91fb1cf94d77fa9d82927fd506624718b8cb4168d64f1be9d785abca0fae1fe967e8bece4a59e416fe8fcb06e2a6bf560d9025878d4ec80b8a1a000f3460ca67d4d0f389276b674c70b4222c1c22c5bfbde3e51faf73e1d4e1457d17d911bd02e2e9d96571a739353a1a39d22247711eb86f688155a2726035b1db07adfebdec94af9755e8a48273d18c61d81e6f03afe27cf96adcada8b8e2422475af05eacefd52e17a3dda8c655a41d9b8300f1e6f94d59a743ec7ba5245af0517981f8d9e6f8baf8b86a0c82fbff35b84fc993861b390bdae49f3c4c73768f4106be0437f20d00e5b95a3cf611821aafbafa8c73e48c18a7012c4d20f6cbe87e71f5c6fab6e44556e79f75b643915068073f7a1d94604f7226ad60633ed37bd4ab16e4415a7b80d7903b448f9031760807a180d55a7f1854579b5272b11957aaf1612d02750c1b0ed4a59f6a0260da01bdc294e39416814845ccda753801cba9e02968325471d550cc2b2ef8c1fa0fc8700126355ab49b98e12c2b160ad383e59a00692599dc3a35e8b9b4467ad4f78591a0b8fae470077f55f1cc14fa24a682c61762b12d9f60de3c89110c2408f3a83c4a2a9c87d0f3f111a71d5a3c35fcd49da62b08414d6d34a28878cb2b1a625dae9d88dbb12b6385b1ce48e25584b561963cc230d98d1c2b6e31505279a1f0bf4dd9f44a5b50108abce5b4903110cb5afcd61c89c91f2ac4be423f7de443819a9b38cef2b501ad2a42123a943856ca3e66f327b4030242bd9639c4901443a99f54c2c08d768bd4bf755ebeca478b2aeae56c936ac081d81c97b3081a4a48d49978870f31f7f774a63f0102348cd06621a8465bc756d11f7edafaee277d33e7f2c35ac543f025e1db35138430a298f796cb30bf208294ef18e18ed8286233e477bd1e21438a8088e5b88fc664bdfefad96158f5f8db305babc207f361aa787711edcd0040de5d2b60d38652981e2f1346fcb1af0412113ce3b1340489010c7f373f1b4218e146858c017ebd9305f3524de2fcaeccdaa814c7dc88b7202fcdf6aa417745bc1ed88fe3e2bbbffdfeae08fc3196641dbc86d76238e96d608cc0444359d7de34765505d899cc0d538bf10ebcdeb8d945abe5a1ce710b676394190b80a76b7f6aa56ba74f2a7c86a6157ce8890a5dfbe0a62ae28ae7347f0bd44d3b67461b7d85a6739215ee65c9f304f1d64d34baed53a40b135f1080c599e30fd504f462631e69f743e766f7a8514ded78feabb6ca781789b2eb83ebe056286d212175c9e08c0a5f1991b14825c501f3e7b8b59e242a11768ce43902711722b383c7e61e150d4eb1dc3d7b99dd14b1565b2f98f16e59e3f6d3506d1ae3c1e4f6b310da49462744bf307a6b1d361ab45835316aa96ba83fe8e2e5d5e2d0b836b396ea62de9b5f83e1f76cc1fb7adc9c30983ee0fed148a50489cbd1435bb0d69d2d8ee98754690fdec4ad02506ebc30f255bb96c281e1dad74ed2a0ac3a477f597d961b2c7bd0627a42fc11d00d04e5b59c950ca94d00b64bc0945fb3dc5e58241b4c3c13b846b61424c6c86c751c55749fbc48a2545b6375a0b09f5ad8927359de381f0c4b1294a50d8cc73ba487915775640afe2c3cc45a7f060cb1ae44b2931ac78a2e5539b3ba79184d775a5fae5df3d433e32c312df49171bb3c7ff1206a515562446b7a5a0b399d22247ef327a5487927fccb78aee4d40fa26f7c0757bccb233d8e2af61d588da5a51f5a00f32ca59af193844e11787f86d1fc968412c1af469bc8cccbfd775580dd646523d2fb63ba43e608908e372216bac21f61bf3a359da80ccc9fb3689d5fb058f793d2bbde05e6f1339677b48a606868818db2ed04fea2c00fc730f03586e116eba4642c404df12f6a09879ecd5d620c7af27ddd8e5b72a3790c48239ab4155e147d0c26085b1068686cf17b89e8f62156670e25f1d1b0234a2c8c2dd532606c3f2b8940066deadf66f56a922b8c9cf60da212194af4316cd4536a0de98198bfc9388cc1b7c5cbdeda337e1ba003451175a7b390a11d9e4040894ad34eadf3117b6b3861d7ce0de1be0a2404a40f7de0cad44410e0f892868a392ec0090cfa482ffe87de1dba5402550fa24fb045ed2efbc9461e05b52ae48b35f65185464cf6b462e063f4f2c6063dffeb4848b50b52a0a14105f89f0aad3dac2c35466ca7af93e8723ffdf0271dfc73506cdfdd8320a28137221b7fcd608d32ac486c2909c1ac125b05ae3f11281019adde1c1329cb3df59849f502c88f1bf3b9dbe218cad2299ee2b5cdb14715068dd52809b001e98740673798fcbb4569e6b557a9f6daf4cd90b4b55f6e151cc2fd4b9646e97136b108fde318263696076ee49074466211ef7c49c13ae075e9d83845ead3cc005304d81fb57cdfe489f2594235f94478622c8bf8383ff6074a8daa64024811500fc192f8c1a925a5db63022e2821d52dc8c53e41c5134433693b7f67e0429f6d0a93422a684846bfe3e797c3dab31cbd0e6dbe76f6788711d335c3bdc070c8ad0cb4ab20c22102d82989e9baed187e4e21df4b23ad043d91383869311668ebba68721586a097fa504823f94abf8bee2c60787b4e58ed29486b2290f005ea5b32ab0df41b648a4b8a214457f8d006b68db587d26a474d55acfe7bc8bb9a795e2c101aa468001dccf1271148b7818fd7728108d3e94ab8d42ce7ea4e38e1e8429e985c08a5b5e2112f514f35ba2f6efcbc52b672a1a6a1d8bb31a3353bbd89267469059d30207dde74a498105c20be18992bc0304053b1c45aecb1e82d652fd88a57e6d875f16ef84f3e627ec84d1179fa9cf46e212ecb9728c4938b398fa82b1517b33f1940bb31e13ee69adc6d9b1ac1c4de8c40caeb788364dc5bdef3805cb6e2ceef695627b21b443c45613a8bb2f2ed5a0f2efbfc2aa0d428179a308318c8c7e5daabbcc30e7849bfcc11c8db56ea1583f0de3d322cba806c9249c5542b2c9877ede25c23475ab5727d3543e7cb7a2037b9356ac95ce4e7ba518385a5bb1a752e9abc41d73b3f36286c2f0599d3e9f23895ad4c5c4b442811bb430c470e634d0751a5c5eb0b4eba30f482f35b67acb748c7a7df179c043f661a2a1531e30fa6a7ce50f9c2162097b0b3d168abbec56b36d2889ab4a8af033097c9aa15182f3f3baf6508bb669e14dc0fc26055f4699522b318cb6fed27025105446dd3c9e62d3d796934d0fca083ef3e54123992cd5be88cb6aa4acb41ef6c20e394e220d368258ebcac7cde12b2a3231441db814d82110e9a078cd9c3afa8e7cb06bbecff84ddaba5e21d0a20f66938dbe31edf9d6f0b28b8bb04167eca3b2ffe703a230e8ddbd4bcbe576903cb061a907e2d01feb3220be336bf9640965e64a2d71ab29ed35b0bbd0db739a0c3a686f44b2d3c4538836f9bd32c4921b8867036bf9e0df731b46458c0b24100ab9daba55026c39d8ea78eab8951f7248d77b119191f91efa5d7b698de7e209089d1b452f538762a8b6936a7da646460e43d49d78424a3848790bb9c2b9bf641b1dfcdd9ab695301333bec634e610165af8860cff21e80f301cc0053cb26351fb4160aca6e99fc9c1717d635396a8052fa97029219c6570642c519ca55d40883f1a49b1b94e42d44da967bd7983474dae43a0d9c106ab6957f7bd7e98f919cd87e29f46b4394ae40724246f783dde38bf5b286fdaeca31981fc047567e7f6e353833e548ba1ee51ecb2cc6f575737568f203bbd412c8642d36a14c10347b3843afe9079b67adf8bfbd888633c531ce524aa8c423e4c106a287a733c2dc49892493e29a394ca38c289136c34fc730d6400d66e56f5befb615a3cf83910b1a35f704e7aa114a843f0bcd23fad81276504b8f31298046e11e91332dca33890239c82d924439f258e00e3f4e621b6ea4a645fa1c451a0383913cf7b9f070861a92b4b935b6a513803dd7e65ccb44ae88611f7af66ec68ce1afc575f9241afb39943550afd4e8cb67ca4e6c5cecb86de427b290579a207cc7d8252b27062dffab384ba338d4cd6fc2573f02d4a0421a51ed218b19f2f0fe3d81eb819f81b4c6b735a31edbc59ebe5eba4b081cd151ea943052f72e036b76b07a70455ea383f291b6904f1f9eab6c793b645c73635108abf378a9571fa6a505de763ec58f9195c4264ba895c09c3be0bee4644b65b029d48f3c48c16859f04251fd2bc24d5d83fede56fb05d8ea311abfb620e56b13fba59fef5f22c98189d3a94cbed22b0bf73396d9ed71a986d07bb75e56f6eb4955ac218b1243c205ddc5b6085da66fec5fbc41953b27b7b151e7317c3e6f9fa9006f0f2f89cb53092486577244abfa99065e705a39e4ae5e2f9182626c9c67fb0c3c17870e6a3a9514a789e90dfdf977865d22ce65218a910214de41a7894766e9d41b80aed92573c0c12e1da0fd1592a78bcf60f9f4fb0c5fa1cdfbc792f78f9714ce6cdd692c76e05684158b827c2fa26f03a9dcace322b61a76180d8e91063a8807731bf46b5a4f05db19373b88d85e5ca06893277c57bf2c1d781aadafe2256b599f2103d19a9ebc7c75c42aba47e25e31883f138f7e5b214a3bd32ac84c41ea1c60dd43cf2aa7402729e7f6cd68bbd47e26426a839c8aac6faef6552267d0d7894bf5e95618a6d7bdffe5c5ede31515e1b4ed325f5f1550f8886138ca405ef357bdc79bc0115462404d615762d78913c8f9292bc007c9f9abde4ff78a6cb6f0d2640ad4407602afd3825f50af05fa65db34e8c8ebe1460f776ec01e512e4b64f62b81b3367c1572b7489416368859071baafbce97d325940304d6f58029294e354f9e03f4d03a42e4cd0e30b88c61d9399fa25f076f9bf896f4733a986d1d9a63d17e26705f1a681a36bf5b9452383fa018bc48d01a3f9d8344204c989ad191c1b6ab65993d57ffca0e9e931f34883821825e8378a74e824e48ac94607602f5f2659d35d41682c275c861afa189b898101bec6e1470a82a16ace3c95a03e878d8b76c6dad9578668fea2d0952778403f082a683c5df519c0a2580eb9dde228e0284cf84c2936d3777395d49a13dc50d38bacacdf6cf2003d309196c92c17983a179777451abc874331d342e02c115ea9147a565968cd52749918105793517e9ca35af49a18626843b6f1a63fb087e3b4133f55cf2ff79ad6b113f40e04420aa1cdc447c6b3700b6041acbc2a17fa81712445a07f070db66f4edb4408a8e499fa7cd821908bab367cdbe5f235e3023c588d872f3f415ffe0c461d2faec735a339303c63e74f57088cb89775dee600eaf9a53e6c9608dc9a8467d8bb5b4eebd69c2ed440927c45337184dbc906963f627a13f13185898faf4443dd558ba30fdcdc4a6144333206c945d7db4d5d1460596c31b2c00e435d6326e95e239394fd8e9bf411cdadcf6f9899195b0898ae37cc25720ad74042788885fd66da309c6b13a0d73bc0678f0afc6b0a71fd7414e880da125f5158d23fb53567db4a42141a121e7b2326c50d3ab3ba34bcd2032ebd41237c056575d26c85c426a8ffb575054ea3173f1c6833fe8582a10a269bbaca3d082ca1a52e5553c871f0f82522deb356c416e88538dad8e1b4b912c176413573507db238d1a725533db557c2285d9304e2b7d19ff4c4d24fdb48cbd9b6f19d9194ce5c3424431c0fdd583ab25305ae6cf6b25811edf0f01f7224df67873b293930dade3e3672055224dd0f9ed455cfdaf041268c8f49a7beb51de0aff27365b215a241d71f41174257eeaafa0a68beab5a9ddab171a9db0721ac17aaa90242b6943c394feda96730f8f85312d12c27a7a88e8049486f162b3ce5b02111db0fe360ca416d27796f25d9d2e78ed1e5ebbc0a20b4083120865fe96daf9125256bcc530a291541fe87c72eab57e15c9643d2a61e4db5cebaca8f52f9a85a0c78a40ecafd1526a927f18039af82075cf1bc0bba98f7a00402c1b803f4c574f6d40db76cee91506d0da21402298b2bef6187aee3b85f7e153386580b82914a4b3c69cecdbf1a108c70174d933c9db0f54bdf8f546b514fafbf0512de6d20d780a002bda80a5726055bf8a28603219d862fd92910297e4ed0227a3437e42ae3f78e8e6e22345dcd87ea404d0e29f6818bc82dd1da4109e030465210fca758bf462a474af16fab51128c5cdd66808b9e038eed7cc7ef9956a00bb082b1c4685bd314aa0ba2492cbd7d10bea7099d18d1d6a377bbf1ea4db13656f600611c12a84cbbed41d571aeb9934036ed40536fcf813125b2ebd953e25bbd2a45cb7a24ee5f6622e8ab37465619bd178175df9b7c95ac64367378c3cd0140069f1a771f53c1aee04e1f055038c566b91b95c7ca22655c860f06904f68b7e18261c129e2dc1066f3675361e66e7d2ab46b72a4c2a377378da04199aff21e80ab70b35db6770582ba46656d2137222a01a683821b72e589c8aa7f49217a74c628dd2017a48bdf83b2f3462487b2606f2f47b1eadd7abaa23616f5adebf77a39d3d323d14938ee88ceaeae23e1b4c9b3e3ce1f472632f5b5f39d76c946a67980a135458b3ccb1c9509dad37b522981d514b41b4c131642b50067fcc118d524317d9e9bb442ce396a86e5b428b6a40b0e6e9e857065be7c103a77ddd2532889c1354383e1f9c160afe5590bf756109964587b7c9f3d92666eb9783551511848096b8d8fd30f17c9642c58cee5c0d6de55c522906e7c45d0939454bb44fbb47348db92fb76d51b2e1d7b32abbbea0d179d49ee3913f8165dd8456d2ba61a5000ca6596b3b9f3723523e0a7d82bfd26912cdcd8870dafa7bdf5b480290a8a735d157012a965415686d529745418d21602241402b778291d72f7fc183896d77ee248d3fb8b61648c9c9a8e19492e106ae93b0cfc4306970a53bdf9059f151b3001eea9ef41e1eb894adaaf54a13bc734fa0e6f6112685be9f080717db0122d99faa1fe97e05fc1c57e6cc110280d9ab0dbd8ca86243f9fbcd6dc56ad9e77625902dcbfd7b5a172cda669cae253cbb95c3c97c18ec3584f70c4491f6d34a2c55bca6d25d7c99801f94e7661c0a03320031466d9e9adba4e1981b7261dd844d109016712db57388c1d35d1b999928634f84d5ca611cac5e53d2e2039b2d2c38a0d3ca438a9502964a21ed1ea5ed8c044b3e022a4aa02858f29d365559f77471675ef0755b25c5bf04c2f9f3b1f39d920e1b498a3f92020833c2755565baf15e96b2b266a118d40b39653c63033d31ba1db30d33b5f2e9237b2156a46bbb2c74619f06619a53ea93bd56ff1fe58669fe7511c7a990ecc1a5ed2bc9e6d45f87e5196d40abc44772e37ff7f101988e8a9ebb9ad25949d694f124c02153cc6970290d864c9fc72aeff8e522298c4cd489ce25e2789c1921c1422806bb805c617c8981f9af5fe0e4e9b95e6e60fa69beb19c5fca08a0bea187eed957d3893e77b59a91fc97d7290f6c23b4e17520f9430e3a898c7259cf76caa23d06ccf16d36e0c7900dcf3f1c0c6916d425e20f7bba9b45a1d09dd87e58bb85a621ba8d74406a767ab59f97334fb7bcfe113e966f57ea87b90f6ee7bf4ab3660f2436c764f87e32bd6a4a5f978fc6e9ca350260a4c1d4d1dcbcc7c059ff8de670c2a5faf08f5cea1d9ad4f931ad45fceaab8ca38719bf8ae152e302a7e03c453bb3338d02c226c1776fbe8e696def33cc9a8c9000c6b680a5d20055aeb5f01948480f2206be62f668247ddf0c7424ca4916f324aeea057e551b0629edc70cbb75059d714fa3f5d707c4602e27bae0361062da7c6e503e62ff3c30b0d6ea39534d6c23d2f89dc49d6488392305f499865b2e861f20775641888c38bac092a1338f422c4e159c1e1473ff24bd214cd639a5fbf5823bbf3537745755101814a4cde63c0db6511d4390fce353cfa12700aeef529f39da47d3d78b77d7ffc08871e5061d166580596586a33022568e0a95cf7736f611b39b315357615d4a3f6f86211409cb0cc64ffb35760123cffa28be2fd724596afd020fd0606d159ef9d5720fefe5273afe00cc8b7ed5865a5d488980d67ef2092d5bf331f2fe24071b68d51254e57c1881dbef8b4bcddfb4553081196965fffc1b05b39562a191b41e6ad1551f9afb575c21efbe8616e516d9092005a6084a09459996d80416e9ceed1877dec58fb910045aa73407dda5e58104366d3712aa903d766c352b84e78676f30cbfd99547d47ca08c2c8728e5f2ae3df8a8312186375af03aa99dba2af73c21f87ad675dd8798a7c997a65a3ae958d01b217cc4b98bb08a3abbcb77237092036d9bc43092367d4d0aaa905cc39e28e11a72a5045123d211ca54d98e669240dff4883fa2de45daf38c752977fa811c1b55609c8309b3550a367079c1f6fc8ed12bbe2dc4c9553470a59d54d323420762f84022764669e2bc16e67d0bfbf723c270ade8cbae829256ae252aae31387d096eeb18989067ff40ecf6d9be0342960df95780ef81a84951b907fcdbd61bb4d88492c590411462e5e1639a5fa251c03212ab94e4778ebcd48ad5df206b2fb4fc4033833b9034bd6ead0673578d551691020256fac43fb95aa6ae497134ca08e7d3518057f3261387cbddbc1bbffe402c25564a7f2af46154124ac783318364a6f22f293e3a82528bf3c284babc3d064f142758ea9d291de32f62ce0008ade2de0a150a61c86b011430a144588b7ddee8ac29f9fc767a89615593c7cef9a3e02db71efb96210ffc816057bdfab5fd9edfed807ffb3cd84a99ed564ecd5acbf159a7662b9e83f401c9489b65f88d28c97b3be308c3119a80eb23877cf281eb11a6be5e765cefa15ac4bcc8dacf47d8e483877609393d19f95c9a51932adea06a1ae4cb3c0ca4867dcd8dc487acf0cc0296852ec641da9d7c90adc81ef02981c82e33da89718949f34877cbe0787bd80b8a88a08b0d6c8e100a911a79aabc524e257890db7cb927f4305ab2683ec9d378019880afa570ae15f6a52e8e170567c9a91092fbc294a7bf71f5278c5f99b46b78713fcf3b9810f421acdb2b014059e9cc5627e96660bb8ceb2dd172f412e4f6c614bbded915c71ce29d7d6eea3da7cd166caf4ce413a5f66002edf6b1d496bde3823c26117940b9bf5f4949d6ec4df19310a57bc43a6ca03b020a28f1bebdeb251d164edd02169a57247af1122ca37431883469aa901e50aee0b69745fa977c45a9477c1f45acba96be8bf42a68e4a48ac6ea275dc87d5647e2e4bb616ba6f4586aa3a8fb3deaa7b8f082cc90e104a1611654166541f14826d253082135ead870a659606b596b8adf85934b0b079d1e7a348209359f8d168b15d0ef918bb3dafe822f91a9131008bf9bc199f3a422af1388d5291f23219001a3f3d5c5e109fc8d4ad62a06c63db3ec3d1f1c59bd61ca52b8e4f8c4840570a2e9e26665b3d8541ce32b34a5b12d9c425857fa1bec75d78721537a4ef452ddc253b79c087a28c5bf338978dc89be8f76cd64a252ef0e7d46944b1d89efa1889ca5c81d67d660f50610fbe71ad304d29f313fdfd898071a4e0562cc3b3a65c72a4341a59bfd92fe192d481032e53482eff4d94565562c8f75638a1ba60594a8156699ae300377c18afbfb4f302ea6b3963704a0410b565f7c971d249255883432743e5001523f663eed6660591eaf966200fc61e1b87911d4c7522d50ec5ec51c15578cf50f497b113e1da18444d50dc7e723f0363c352ed6ca3406f959e0f064827e72f9c80f923804a3555826927e11609a0eb522669404843073b098ae0603a2041c38a2f4214289217cef2c63ae05beaa00c4b77f7620750c01f1003331775706336f448ad32d03974ea0ed155c22e2c0b50284ac48930d58be043c9628b8faf8661d782ea9437cf7dc50820019faa5d8c33e845ac65b064d007cdf0aef93ccf25126d3827b7c60497cbfaccd4530e981976044008879c184324b2281f396bea0b15df5aef0a80ab4a74398e3110515c98a8de0caba4a43eb33607c9a1bd0776134292c039741b8e986de6cb8a66aac1ae7bb9472fd97d61c6d8b84070b1ff7096d4fdc52e66d17ea857ec064a1b77821eaba5c7cf25dfe8dd9188c7a70f327a4386953898c50515c5e07b0c8d6e20e0c40e749b414ca0dc4d78f302122a0d60d41c0d7fca4c5f851b06e55b183f78e629b3fd93c1677a707fec77251ee896bc52d6e13432c95e54ac7eedab5a6d4f4bf009343e6a283a80128dc03b60d32c47e69ffbe15c3cf1539a46079ebd954b9b07ca34564f3051b25957b3fd9c146a43835ec85a59bdbece417978db00e2d86ecd767eb9db491a3f74740fa159f871790ca84094bfe32f747885cd831b693d2e63e75e963254a313fd01e86d5d29436397bf7d69a7825a8084a5d17d7fa6f130b642e0e61f86b346e15fcbdc868f4409d046959b16662eb78ad806784ba3bd0f467acc909bef00a7acd467eadebe651ced6543c8d98538731381fd0a23cf0ee97170830e471309294933439d001954d21c72ce443210703b0805e073ab3bba107a02a78b87e77fc3c2384222163c35a4c46582b2448b3c808c48c862f1a2b4ccec42968704a2a951d320da2c846537b0fa1b5d854da779109d7dff4dc555ae775322a750112438bc976a3628945153369e17c1264478f227c0020a773c6437c7999e6764911aa5d8527567ff4cbdf0143e23842f5ad3c06050cd6c4de29450a9f2e0c9d248992872fcc9ed18dff47fc32527d0b6fe889e000d54e71ce5cee2188dbf37fc98f2e3d34873d09982e4011564bac12adb29f0e5790b47d2812800808aeb89821365525dcbf8bab4ff7737a89f634a93bc80acda41e358e611a024f02399520cdcd11d4486c0908e134c2f3c7c18f93a5bda9d6c5e54118598f93131d9bf4631b0cc424b11f043741ed6204a8c51808115b9e4b4dee2d50a31608f1414c3fc4de83a5c7f1687cea2eba3b92782603bfa73a54bc78fdcc752537c60b0f5f1fbfb4e05140588636e9918dad3f48d75ce931a02f39d4fe0b39a7d0eaa3e224ef825cb7d94d25380e1fb87fc36c760f3f6e6b62e2d52d4d1682f8e6684f49df629902262fd646ba2a011e70d14f3ac26fd3767c10709ee101153500b44e422a69cf21b5381c6f4b15f724317092d9fd7af23e6e97258ee7220020fde3f01c465d26bd888663966848540372608437fcb966a8d309a7d75858d131e72f173b6b85f1a27c80a88328a1577b68d201f1a77bd0dc113b061d544978f06dfb4fe840c2d8981148d625c85dd069744b55b65908bc32a4211568bbb20af3525d106d0ac97a0ec3f7f27349087e89dc5ed7d33f3bbfb5711904b085b6cb3e3346e0c901a641874a6de568564285939a19e317c7b4aef66b744d7ab0163911b1e911d7bd2f5ccefd9fb12a298240ef6a837f32714e3709c6e08ca9216c981c9b1a582984dcf0059310c09c55cda92ef77a329a8e86fcf0f744767a6c4616726bab748c5a6fc32f1a05e45a1d84bcd20c2f0275849aa97ad4d3f0451d0ce73a04a45016f760d36643afb9eba6d021ecb6e53b912cbef11d1f590bac099b3e98e8fecf332bc009c7ce15185796d3d2f8acdcbff84a3b11cc89eec9296df8fed6a231a8f41651a673f14540c1190baf6382354de66ea56ab686d7361281573ff327442f7a085429844b800364a662ebf996a6a3b21769b9cfcbaf41806d7cadc138b57b44a2abdc60a731d9c54a6c8323fcf279c2fa13c5c8e8b97896cf3c54be90ed130ec753757511ae6917b772dafe94f521461d63cedba8242e1105f28a261632a30db863d7e29e7835f3b71168cc8405ae199b538d9e3a5d89d16fcb1a87dada4f29fbb52b717efa0930a636924b530aa4958d20941a66ef0564d55ac8f667807bee96e083e2d6bdeff0d73807fb2e08a6df6154fdc05ebf09dd2603a9a387f5d6f0d146676f5274409f10e064f8d88f54a6cfffe2e69b994d5d69757d57c921983d16a7d0806cbef21ebad0b1f139466ad8b0c0cb8a54e53c3080c616b026149cf39c3acc29e759d0fd6b54dd428ccd182614944a9b71adcdfbd3e8a95944f79b1337050feed6ce72dcd4fc6b36bac51a05083d62406d0d4e83041632a9e84d3d0ac4ff393059f7224bf0ad5a4cfcffba0ae9660ae3f350354522aa3394da4ce1383faa6723b8063c93262a593a590d76d00f38c149e69925b9ce9ab15c3b5e88f94a3980050ba82eea1d90ff8530787e72564125ad5d6c9a2347d4d1b1bf6fb712fb96bcf2c20116c762b69449118546829be48b8d980e1e04712d4c90136832f2fc03bb9ace910446bde292d37e0ba2e228b80ab527406c460004db2e108a23374d737147b1cbc22532b8cac84b1bfa9a55ad8cf29fb11e04b18f9834763279aacc000b88650c893678a5d1f4ffd844f7ae79e04a33a1db7b89e7a4cded60883703d4c430ba44c7990f59b5e0e1537afb6caadc2d649b18b19c4b236314192e3ab8c5c62f007341ce35a86d809756859fa14afa6184296f65a4c262b14b9bfef9795860986eee85d52d25ce6ad99cd48be425b91d3b6a18236b5a6b327532a26d2c2d8387a338169fb882cf145b7e8434bcb7d829f22f5e954caa9e86a1d7bf1240feced94f80e00eef078feb402f008ff05ccb20eb1a74bd61089d92b519064ff5c2176cf5be544a2bfa705ba62bafda1c7b1551cb4460bf0cc233e2570b2068cad2dbcf92b112521ee8eec2d9703da3b49fc33fab217ffbe97573271b867b7e6649fcfeab4d359ac0e8b3d37b2eb1bdf4fc38e5a8e7734bc6e282b34fd6d292528a9d92740468988f9a2be8e47a838d538a0cb08b914e8dacb50c9e996e91cd9e63aa1a8df0533a60289ac23c577340e6bb490676a1f1c23fc8ca6f918049f05425f7d0d240f1562f8e05ab891bff85ce6fe67b7f265e1903d2e44d3c9a030cf1b61d62ac6cb6ea29283319b65cd3d1ed99ee9b00fa0bcef7f83076494857a1aabb73d8c8dab2bc97452e2a10a1d158fec1bd1babf8a836dbc0924759b67a1168ddea7ad06b73e05390e8c92b79703e6def143f28365b62e3c1e0416281da2b8f3c9b3adcc2398b0a7a4a75d9617a8fa5a27a5e9123056b1adf2fee36342c98329970c01b0e9271b4012653d4355ee0552f0809c288fc352b8b71bb2b2adfb224fe3c3fee3f6c9c236781fa8f452c1a3da1aa50fd8072aec2af1fc479ead2505224c0e7cdb00cf9712e282d964a6202fb6732c77bc425620b1a36a5f60bdcab24b99a5a453c5882783a1fd8aeaab85865842dd03ee87b3b4b7713b105ea4618ac3563234e2c2d2a249f7bc63e75ab93663b324ebca3775ce4bd3c11e40754d45fa24be8fecf73240d0208a446324c7f5e86c2a00a735a9c8e21dacfa4acc9673f33bdab6cd97cae66492989a4cd1f0a2431e58098515a1068a1bb703539672337e9b7b25ec0747737630c6e2b259967efd4b77d44d2a92621a25865b3900657cab83bd399557bbe6711c0fb0acadeb930f7a42feefb9e21efded7a2329fb1a00fb0288e463824882efc21cbcf93f671aa687e990f23191ce25f9a4d0d5f7eee8292bcd4fc031a1b9cbb0774cd2b8a792bb8d81fb55485873b875183e5f91ebfe9f1de1d8da605fa615e3b372b6d5dda3e70451c252c39d4605fed1b0f3bcd993005a2c398c6d0efb2ffb421cc4424563dfb530c7aef1292627ee24226c6bdfd1ace879a94f379bce15e2bc50071bda35c0beed005b269478aa892aa8f605c5556668fa685f7212ba0523881d261005b245b6301cbb132a45b636bc5c8d50876738073908aef2e92dbe881853e0b9785630679571d900bce84b8fb6459efe8c6d6227a2f5be9b8a0e274e6a57b143f3e52a8a3a1b36916575a00c0a35e238e3ff4bedd51fc1a80c7fb01e11d04d4898ca07fa9d641695cbfc0b7bb94595b05fe9fec182ab259ed771f4027a3a892bef38142c92eff302e5b4a3ac9c1a95d339ccc253766ebb29b6595d32d1190e23cebc5338cf91b4f4dd9c92bcc677d392690519712713cac72f3df2161f4e22e4c153ec8dee26b4918bf30b507460a93cc3d6cad0c40eadf6808ba37e80d93e31904f568edde2130984a123d19d58b59f02915942a54364e8540560fa6d9aa95025d3adcad447d05a09b665f02d8c2e319ed1f66f8b2e2b11392574e8d83405cff9a54b74fd2da9ed6e0497a530078d8d7832c59e37c813f6f4da8da2cabe89ed55647de9f912af9cad9100f4f1321cb856191e57051196aa9b54c0061c834be101a4d700ec16528155c16388913860ded7c62500b6098be0589ae1ceae8de53123dae708cbd9c8a88e96472e009737a1ec6464fcc28fe1983d8abc58f5fdd9353065bdc79ff750beee71b2cd45fdd3251b223a95403726ce4635c5fd20764ebee9e2b7e725afa457513bddae2d8468a907ebe1cddf204c831422bcb07c36d046cc0512e1861adfe67af02477513763bc8bb4de4f2ae3e04a201c9af37c4b8bed3e8200297795510a0f166b4bf301b8020615120a8ea242ee1da6ab60bc6373cb32da51fb3ce051013a7e20c519cf821b7d1c36ca1a0f9854d52f799b0454b5da006bf3b83c0853995887f6443a7e545b4c29495ee0ba8bb50f403ca75b35f61bfc0948c9e033afba4edbeb576322d49035e4cb60d113b40370cc87a35328e1f26f8dc18cd70cfbbe0abf6a81f1b0ec1760e683c2bdfcfa11bd40c2eca814f4b4195819a985203b41fbc98377f1a11fe498e21485ef200af052d2d8b7c40c87c8784e0799bbbdeab5de79328f8803f8d942a24e83e190ea1a0912fc950220ec17daf0d5928d2e8e2de07f2e076f778b1ff6a955f2d1602aa5cc1dfd30c3b70d0a7f74d2826c6792a6e211535bf946a4f50c684ffa188600acf10f00886014d881610f3c6b3df2c8a5a265dd2c45a7ff7482790ad62542af58fb8b339b82fdc171b28269acb4108e886842f7121ae47cc9fc472f4536506478e182201dc3e246496513c05920a3cdf6a8c3b47d794420671e592b93cc258b73e9a5d6901607523af77118d2b7e2294e2d6ab989cc80754359177bff28ea4341483e3b57779c4b80f5a041ff07ce5fcd9bb9a2ae1b624f6aa544e11c5c55f7dd8aaf1321fd9aa92e7640038545b24f0240d7257de4f2f4b45a08cb26eac54fbe52e2a0267256c1be98f6e7a6eaa3eaa5794d21e19b3c6341bab8871dcbaa5ad1b32916f152f7d5baea0c0a9feb50745a268ddd70a3c1f73e4bd7ff1362d55147c2a3926f92a4815db9c9eb1610fd357702d3e74cfa28889a6f3f38caa7962f5670a3fecaf725d0048036cc1b83b6f84aaaf44d5e9cf6ca300d393b03c517c4c08aef9924e26e7d02e9386fcef14c4ae018c91982f1944a7e5127acb6681bf4401d975fc666314642efbb7793b047d45b3dace5cbae92cf46b1e97428653ec23c06f208f1a4ef13cf8aab69e3bbd257e4e27dba671f5e128a4b47c1e876d192209c353f721ce48a55374b98a697036105457ce1e8ff732d5d74b10d1e68235647a86ad785262ecbeac5342b20025229dc4e2735552f37da064f36a573e0913993afbb98938dd4c7473cf71b0384a630c3d4ec85d486b69525095815f4797375f97a045fdefb535c683e5ad08cfe3c47a0b75792b35b6ad427f71e0106e2229a3d83aa9f22ae155bd75f84151f5e24a0c2352dab48a3d577cf0bdeda6d7cb8712e48a1d3e25b677ea3742f15953e39ab5621f869ea1896f6536c472d79b2369e70f98ceff2065cec129d1b389417b2deb536bfb04dcd088d04189d9765182e11e4354930af9a80361d397c6cf92f6d7427f2bff27f8ba94accfaa5e84384e45954d02ddec3a6504ef07a1c45f4268660d2645b258d2734d885acde6e204a8412f7d7fe25a0c3ae57bcaa747e6c8aa9225a71fecde5da8cb98ae17192df28ae7a6db30ba2bb1fa92bc051a1995e8d1c3d98c474af51a6ef010fc9ec1956400d1b6908a5c5dcd5c408d9b3e20807497ca37247f17e1cfdcb7d8e44556b32a898eb0d2782bfbb90d68e41147ba2170536329f531930daaecd543012cc48752177aaec84110dad6809c969a0ab6968e323e658b64538ff43c5af121e4f66399092acf200a25d932ff59a43dcd2da420c5ee72d4f52ec56e1904afdd7a87efe2431635a7ebd7d81ec5cb836eff8d4b22634ebf11d1438f91bee07fb1c30031d8da24c10b1f6b66d48c638d48e67af9887f7e59985698c2c1bfe48ef0281115a153d84c6e21bcff24231662374f7202c00ac6bff57307432222bcde5d86c73597deb83cdfd73bf2f3961308d2f4eb625b46d16b88ab83264a2861f3291a4929636885286d79bf9f45395462169ff6bd53e898f54509b38cf24570c6dd89601a5028cbe528a85639bf8ff0a53995cdbf18bb8fb9d9c9abf96699cdb4e7b067f82b8e34fbb7f095c98ffca8afd10c29b1b217a6d91e8417c1f5fba6725bbe3895db33948763429a69526454262779520cf05ffd78a07fa8a8997174a8d7b2fc34d6b39c6cafed1a6032546b2f1de727b0385932851a133acbf694ec0fc4ec9565958f480b698c81431924924c8075e56a52d00327c80ec2e2193cabcc2810b7bbf0b46e5bf39b1ddf653195a5aefa3ee4081a218e582349bec13c5b46ad910a7d036f0d2c608d9ac8feab9b1ce781818404bfba61f81394d4367bc8bc61157d70ec7d878e63982905418ab0692e4c5f7b3ab76fd588231ed030e1003665fce7c9c2440c421d28381098730341048d10df3dabf079eaa304eace1e3fe012408a8f29a844371f2546350d8787b435ccd2d014aca1c78bc6cbad243829972abe2acdcea7b9a88b50a90f0c9204ca3a3f9b54305e15251d2f4e10ee00105140cae798342b731c20e6cc34bb3318e4204cdc37d1219c52bc53e32e6bf45c1d6ef7bbe8e72ef9c5aedf5f175380a7e0a7b76ee2daf2633ffb1fb5d61a2184904df6de7b932de50e8b09f50879093a76447829c34abad4df71a1b6bdfbee7c3ce553cb49a485c4d6ac23a5dd58e7d27295dfa695d2c26b52db287b55a0c24b4ba11dd5d3c70a1cc147820fcf43e0a5edcdf3f8a95d5611f2d05a92c2c753e9b1d4e381b492f4159e02b9b582f156cddd0ade4a83cd3945c05f9af5a7813e699f4b2d3ed3a8c33c092df3e47aa6b33aad33061fe11a186637994da6042b5ad57ec587a7dae4c7f311ae41a789aad475d94d66b2b48d89dcf86815c12f55871612d5e5f391119c3a0fe91647e95ca93918ef7602eebeb614c00ba73b8fa8575a3ec233e28e9eb1dae384946766867c0821bcbbce92df380d65b80bb84e8b340839c2853cf2e31ae03a2dd2904492a4ddaa77fd94ff68521ef41ed2a4bc89963d1569b187b4ec65d8652d8d8692acb29b0cfcd46828c9aa8ddb0c15a9cd4458c729d650e41ee03a2dd600c59d9556dae2df774cd354a7572c4a7e529cbeea58ad187dadf49556a230abd66318f68a5ea66f6573bc5bbfe396aa53f7fcd6b7ec2c036f33e8cc269c52b1d38b321057a71bcbb4c7caf4ad761bc1cccdac4cdfead22c3be9938267503b49bb89b2963ca8b53a6b8297e9da5bdb6dd2b6acab8defecc79cc8f7d24a3974dc7877658e77a5fc7437b66f46c7d5c48dd79f0faeb1a9e05171b7fee38a7dd837c5ddfa327d9fcdf16e05dee1906dd3759795e9cb402e7cce909c1ed7c791325c2347a8b8f559d7d5707349d769a1869a4bc475b6aa26b95f5a084228d3577e93e9cb3a7c63bfbec9f46415518a76abe227c8ca742389ff1c141fd2b2a712edc4ac4cdfea17fc68eea97832bb595606deca6e3355738d845a276d899f145c637ad73bd98dff7ae2a0be67a18c7d302f2d3ce941b6bdbcbc88ee494d569a0e8fa8eec6777b52f00c2992e12c2cc2dde9687821bd6071fb431851069a961868a8b91de35e2e5b4f0a9e01df96b48e67c4ce69125b13908267f061ded4076ea82dc136431863a4674ae5bb80e82b5031c59cbe7022909997dff42d93d12a2290c9ee00dc0cd856c6ab38d3fa2e0878ed0f190c048ce816f8800b8580b77e93b9eb2e743193e670e640518afde1e6707f78404c11ea6762a445448e4a55ee0cf9012f2f0a50c0010e308001c48861125304f8aa995c3145a0947efa1543692da0ee021953044ae9d4640ca515614a51b9d6cfdc3a2b40a51501b7e4e670b2a29fa1b9d2222287b3b9d4258199cbf67240f44dec6572e9756bfdd3b6573f5dde1a44ad94b224105d19d17d175e5a674d60fa344331c4c1b932af00557eb25d4d32c249dba619c82a98a6699ace9adc80fbc31f62f8128a6086c69a8991f605326603ee0f31fc1c55f0f1509d132287fb0333f363d4f813ac9ad42298a1b9cc7f98b9932581bc3cf19df5f6304d30e7348114dce95369c9b315cfa14e878756470d2d24c12666fa4c8c8c26b1bb5bd6701241f79ecbf4cce384702217c4cb229b9ac47d4575ce654ed932f574df6ff52ad05da7e6e9f5e445b7b0186eaebb035527a33a2250b00ffec13554d00f3815fa1938157a47b121bb56e85486acef3c2fa106d9a5e71f4e08be94b570e9c656b8945a0c844b7dcc39abae4d75cb38c508e1d48c641068157a4a9b5ec80f1f4da2d7c09079c005419dceb52e4ccfd1b44960bfee62fa7152f641698f26d14fed0c593cd38be890517aec331f41cb8e20fc657514015a0d5cc7ac8ecbea704f65ce79aa59f3d59a16a5d6034d7a344ec79a34dd12a71dd1cb534a27ba750ffa48e50fe7b4b07bdcd114fb08d5c9d8200edf11f56a1e47e34c6a524da2f4f5d9da55280d1910da40b67fcb6cd3d849e99be6d1679405f254e8619a690cfd43424fb18bf89029b9747b492e3d1cf2bc30d22dd4693dbd4e4d4e29f54c5804de5c3ae9bbb9f4959a4f85fe69d953a1879ae8a9d09b6893ce3a7f69f31f8fe65e367f63de8b59866c13510ca3306ad679012fe478417331152cb954dd668a1c7ba5300c135d27041704355f313bb56da6c8a5aa3bab88eb54a5aeb345c465ad4d245ae9167a4ba3a7d194de3d7af7281de2d2b7109c10f323ea1555a94af5e9b3e7f24e2f7a2ef0f47f2ef1f44e06b5258863d5dc210f4a2ec24316830484281a31b5a007d388424c13f2a185ea64e4dc290bb91eefd655ef66f77d7242c0fb4ec47bef04702e0e36e03e06dc17c47dcfe43e00dc67d94d64a4b216c6bdf36f9959f408e77c56e0684632f89c17979f2bc3e5e7905cbf1e5f9048111ed2e35ed60ed1c2903dcb86121d7041bc1787bcf7624e6deb89ba392e989979729fd84ddccccc3c396747fdda3dd78e63cd9d5cbb5caf2ebbb3a364a2995a109a967d4b4269c85ae7e160d527cc364e9c7a629ce959b4dbeca94367c85c8c8b111dc62b08974018df45f4e36b8c81a3edb586302699a6254d4c44da857d94c18b57d32d930a215674a4792e289f40d773c13e659f4ea6e863127dbe9160684ca0d893394db2ccc2e920ede3d1324ccbd8c7ebee49e9e3b6acb6a69ad8497ac7b6ce4e770f19dff10238d954dd69c954d9b7d397b6a72aa38c30d65c33b3331dc08b9664d32184104238279c304208219c7114a58c324231c421977a9d46a0915330890b823adc91a7e4cea55eaca9e91ecf051a792ad4df42ec64390423b46f48b432a091e7f26c0bb13513031d136d87ce90bd9f5f0cf509c873a999a7ac8881c5a50e874cf7e1034845f5782a14f55aeb057074700c768e611733a9ed15b9d4331f59e50ae31ecf856b322ac9dd5e1697fa16e3526f0875f7843c15eaa3e995ad4ebde7ac5665555cc08943aa1d911db2fe4549447ee4f832ed443b32329ae071340e9c5ef7112adfc9d82e0136c943f63ee7cbdee31ed5c99833cb442492dd92b8eeeebe1cf05b13d79500c68ddf5c892fa59452da1cf1d0be491619b2bbdd5996adb339e0379e9fe0f9f33a37d9e688b68a79685d57ce4d2a6a0c1940844e15709de8ba3bcb9672d46f73babb140bde75ccc4bc4f1a0ade567dfe5944c8cbbff7ce6326e6591ef1d2da1cf5f3b4d2dcad135a27de794c3c9dc77be76179ccc41db0a110133f644c34f15c98a0a1a1a18979114e27b92625e14cfa906dcee630ce750faa33f143b6b91dd73aba855d66bfbfc88405f8466bd287ccc5af34c93d3a19d3734316930bd1104e854f9dc91eb2cdd9189102c3f770ced7a811b1125ff39f4d7d1238566eb4a51c2578b78a79772716c8eb9889919f1a0a24b449b0c0051c439d92f23c6662a4e5414f599bc31d6aeeba24aa90a7d23a213fe57c2a6e9e873c0fcbc3b91dd30cec6906dcb35b9a5b022679c8a66f434c5f70823a0f4a5178eaee53c678ea4eccf39053ca3f1727ea79cc57ea939eb24e886278501e4578a74e9d1664a8b9eea4151ad75d67053920e16e435cf739499fece49e6bd2fbbddbb19c991a3a4146172f8b5b6badd5a2281af593e607e3ffcb06e09515c02d0c158089dd4401b01f0b00eb9e8ac766189db46bd7ae15acb0768b6aadd5568b1ebe52971b393639363936d5fa5551d70d9753b1b0bc1f2c34301ae5480d75cb54bd4a2dbb5375486aad20842548d1ee18a7b690d8fa130d2bfebaec46e34ea7d1f2cacaca8a5cc12c16cac2ac4c7737cbb402a5bc68c83f4b5978c67c979597646141b2524f69b362e9ee66e996eea9592696ee696261a1c162b195841f4e55732be6ee943912102b9f78c6f4e97d0a22247ab5a294ba8bd2aa02d1ead4c5c235e8998256f56a2e33d50d1942082194333a569e15a5ab57b8465f3e2a5e0d0bd790f45bf56a6efc3b535b5919aa31335fd75ba33159d0b2406dc3a151978f26a14f771faef07b7cb565e1191264a93de1834ea8ddd8e2d6361ad5b23c9c5b2baaaa2aeab22c2bcfd2eaa726312d7b1af5e0a4b3e724bd748ba7b2ac58c896524ae9a5692c5ca3521c1a0e8d998f43a301594633099128cb58b06fd395daadbee1d0ee75eb553b66a75544f5d2559d890987c6c2352a16295fcda558409c5285ac8644f594361dd36af66aa5babb4bcf61dd1485d123161234ce4718db96a8bfc768ab943951e6449913658d3ddd58cf3955c6c6be31927a49cd292fd337cef909e7fc46cd39678cf36c23f5b6a32cc6c738e3678c51d6cc2a227bb4a5f83e96d58f8da00fe89ad70a8e6a8abf2a05e37d9abb549c2ea5c5f864acaf4eb5eb53939f346a8a320b75cbe4b9982d8651ec5b758f700de9b196aae766ca96e267e5395f238561169bf198465f2fe1bcccaaf5ebba6c7579dd3a52bd562ad218638c54468871c80f92758a846e57d2d2ba3e4a2995e9a795b049e97ba04b3ab1293d10fb7cda0f175981507ea319258910c9a244dd234261212917a136acc88d9752f2b425b8527d2845dbce0a60f26c41d28eb0538f171149e9067aa4de4da9ea7c85c9b7950bb2156832993604a2eac330cdddf71e469908fc68d63d1a251bc61378720506af8c5eb172e14bb5ad5c4a29ccf215977abc62e5621486612b37567cc5153ca3c8159f06d9fc6024cea2cfce9a7557df9cf3692549cdf9de8cefdd94e731921bef1a47d5f3ea9ed11161dc79cc3d5807c6c94868b41b23b91047157b8a71be15aaa3cc8bc798df8fe71e71ce0dd25b9667de5aa93a1ef4899e950944d98f87d2fe71eb93ef69dddcc33ad43ad3e8439ac9611e47abedf989ca62769b17cbdb52dffae4272c8eeace3ead4c7b3a97d0273ff1e8e1ff04d79887559502c5a58fd6ca86a3baf3950a14179e9fe02778063cbd9b2c222823b9b1bbbb9b9f704ea5415ed47e40b8755ce6f279153f6ba55af5eaacc167d7ab95ce39674b47cd4967654a62d562947e7b8fdf7bd396e8e51f333b66c7dc4a4ab33bc64e7237c8dd3af190b6e74d6f9a3d379eb302d2f617fde8a7cdae6e9e33b38aa87f9885446631bb351fe33a59cc6e1311f101b9954af9de7befc9091ff7ab43ed7d4e69f110be5bfabaaff4ade273d17359992bcf4e2cbb2d5755256de97d1e5a7093794d6de93d9ecf5c8138cdd9afaab375c49c56e6e153aa533844d36d954de66d0dc5859fd4a51193dda69b792a1211752e8557c62be77465b453934ec2c046ea145267855aa5966559f3b22c4ad9baae43eb0cfa654153c8e51a78e3704056dddabebbbbbb9b7ebea76a62a25b9e8c333afa6eeaf3247237cf77dc86efd04dd38ae6e79942e014d28e7294f51ebfc7eff17bfc1ebfc7efb1c5a798628a233333b3075a5df3de0512cd996522d14f72d3c6d6ac7704d7e8b365091172e555ea7ba07cd7bc3f51762be75c1ecdf6b242bac86e22510d8c5ff6358f40d5428206008f26867d34a314bb89ee0d18769ba2074fec76131bb2b05f1ef72fcac8ad1446dadd3d3d0cfb2c0ff3207f7336cfe5a4b81c0bb3a869514a29a59452b6045fa78d4c4c9c764d9b9999395e1a2b3373f3cacacb8b356796894494d23b1a798675782db9754d4ecd9b2e5a727be7e565658544fa45229b1a7ac578ad57913a7422086fe6cc3291e82791acc9a2afec4be419f1f24eca29e4f24b615b040f4f1a89f3c96c2871a35b26750fd5e6a9d4e2c3acf79e2c83f51e175c43deb2ae495e58cfca64175ce3559627f3489c0b99f93db6cf96ac1765195e5bf2467221bd90d68bf4d1923837de4d69a5945276216f2417f23d89433fa7e71df3c82e7806851e2a2f4b4a0fac26cdad966559f559d985e42274bdd2ea5bfbc81b97853b6597142e0b7c4ce329a595d6bb801b0e87830387c3d970f0bb56b4acec42be3e8ae14ed3651738ac57d5d25092c9bccbd6e329ae5bdc8d8a419b2e08cb6e382afb8feca25ad985bca1288973a9eca24172e8864b2abcb9151b028563a577ef9d53cf7f5a7d6b111412bd6b464723e9c3bc2e59eec15ffd0b5eb7b80696754f697d5ef0e6c250bbb08ff7deb39856dfeb7e481a7a51c2bee3c2aef53d245ce39d21172d6d0936f6076fe235e1cd8defeeee8df6d5dfe8db625c7e2a2d9e73378cf3b9b1270fbf7ddc9cd9f4ae5b7fdf3e13b0e0af3f6d93168376649d1fbf02f35c9800d96d9e844cde7b87dc5262c72c24aafbdefc09d9130bb26d652008e5256937b62c24aa176f621f5bd0c776651141e1925b2fcbd2dcbdaecbc2de2bccf368d4533494c3d044cf34ebafdee83757bac5b554a77ee34dd2c248278293c4aacbebba2e6c9bbfbe4d7b7d5155d7b7fa78c77cbd32b3a8522b032fd49933cbaeeb882a0f7f695beb5c6923136f92dbf578fa94cbcac06bbdfad63a5993ae8b50b46915515fb2ecc6d242a2bab555ae4fd1c774fd621f978cef2fb846a56c7fc133e2a9bb14de5c9652cafe62023500d45063bdfb55dd4d0821105d920de19ca0fcfc24c3b76d094aadebb3db34a3e3c94f0f08a6df711ff5b7756f93a16ca94f29d8ef51946b50b24fa98d3c039ee7a7f7fedee127aef13e515611d79fa557e419f3c15fd78476bb7cafb1e3a59abbad3ded96364f69f5308fe12b16ea96e90ece73ad9555bdb37cbc0c12d2469e5155761423d7909ff8f23719bed4e9ab0c4f94ddf84579fa39e37defef4d37421b79c6342bb676706e75e51b0890cb80ebb4c8c9b93936b7eadc8a91ba05bbdb2a4dc675cfe5130f92daa354422a0ff3a44cb756aadf21733c87b4b2a5ea155ea67abdce5bdf7a2bd6b9f0cc966fe5d2062d8d3301a4096ed7a195ff3c5db9efce3585bdedb6322d66335b6a8b9d5af6b273a26c891e7bc4a2b3327dafd3697339aeaad44ed465c1486f455885444d621d76d25c0ecc8370e6e2ea08960d25982eb163b343a90d256c2c6103f3a866d95882f9d948b6a1848d259ecddbb96c6309e78212ddce8d9bcb8930220d455bc2c689c634547d137a31e13c2efa862fb4f1369488dbb38194cd8d87d82314dd0b8675228b6233fb44300eba732e29f7c08f34f75234e754606822cd3914cd0a7dece6965c07e5e3355919081d7fbad1561978a91c78e797e01ad596a8c74b59c953b6c46c97e01953daedd95cf825780684f444e3188e71391386a6a16827f555279a8b715611f2c6a47ab495c4c99cf37f11908f274368517b79efbd373fedb3598287801ab486eaaa63c69899e185e4b2484c2df2138ca45b26f64bc3ecf604126624d331f313dd62c53be62835772d4dfebd62f05570c9a4e4ed7412bed142cb489a04374612cf4822c0976f51f17d6e59b6da9c4b76cbdae2c7ca2c1a4a32ca6e38aa63f4932ca2343f59eaeebab3d8af63f05ab6749d9a0065f9090b09689f885cd578a8c51810c2d09d8f318220744bf1f4f58134cfabb35be8734b93af22c300e89e3f90e6aa568920ed5611743275a94d6d0bddea52543c11dc7f8c849f58b13abbc29c3a538b43f8c89c9375260fe123131fe1217c44c431d6737933097c8475f888cc3c5acde465f62eb3bfc73a7c8412429f01c9fd755a980188b49b8e2b0fb5f8c8333339de9544c8770e6691982c22a2140fe63d8b88eb9876317f72374277e11f64cd4d47d4fbaa3c41111101e1291bd23806f4f164d6eb890fb2d23cbcef482e8cebb458c3cdad5db17504bc59b74c0babd0ab96c6a734ca5611f49174a389365b251e4513b54afc8926d2d88a003d4c8bb4ec28dae7279ae7261ae8ceb2d131a1c3f46348737385c6cb1059bf5d43cdc23c07e3676e9cecadae2d783ee29bc23361159655c73ec7b0eb309d61d9371010184d4e5044175f8472628279387461d7f99e0c7b523c299e1493a7e5116c460708f41110100602496c4405c8032b9383b42dbb333a3010f611100c34b2c204948140d64d4037b138039d848e594f9126d831cc9a3c115e9e5899be26761bc10cc86eecf9c763b70cb33e0a320a320a320a22bb90145a369bd1615d24fa645d348964f33bae1545a2ec5366d15b728a2815616589b42dbb333a325196890ef344d63d5ae9ba1361229147748fe5c13cbf2eca3c22eb338540d84766a27fa6d004fa605384817272d2cacb8591797e896c36a14c2793c9149a40d3679a98dd5ea8cb5f5ae419565529bbc5d339e374c2940b90f0e1f20caed3420d3ad4e62a7b7daa7f3e9f4ff5cfa7faa7b4e3560e7e3e55755dd54c223f339b62ad5ebdcfe792f968db435ac9fa609f4ff5d0e7a1ea0a1da631cbfac7e43af60df4ab7a8acc747d3e17945f36f4ead7517e7255199cbe5d329a5899be21bb8d6086b37b32bb615745f908cfa86c35817aa9dde0a71df5e564a54d77b7a7adf3118fa7c23cd83dec394c57fca9be65af144eef1a7af560a7af0f81348b89d48f87eb2dcb6e15d3f3113ec247f888a7631fde7bef82d2c4e4105af09649e419cfc48462138a4d2836a1d8e430cf84faa595aa4965627299fca2aeea323953186326c7ce33a35ec913c72106f1873d9c31c6159f5cafb71bf35d87401fbe2c1b6334615a85b6b9fdde7befa395765cceb24358c157d91c0539f53ecfaa2aabaaec302fa3fec9ac2ca3fec9fea1b8faf0abe715f594f8f9565596c2aa8fdd463093d9cdb215351f84675074daedd94cc2a036ec6eabab99032dcbb2ce3956b52cceb12ccbb2a6c9aaa6eb569d769379599c1b13c9344cb3a65b293ae3115166b504c37b5d4d3a51e9289da6191dce4d229452eaa615ce393ae77fcad2fe7f68c54e3c16c824bb2cfaf19c602093cca2a28732d18927f33cfbc75e009fd089282546004e3240010820630056d3ec05100000c418a5c010a19c9884401f4f865d96a2368ab2b72506ebbdf8de93efddb27bce49ca7e9772644589f608c89b2494721bbda845f958db6e4cad572debfddad7dbc29e61575b95a5941565184b25d5c9d8e4902b1fe37c71aa26a7e88c804c37cd382335aa99736e9bb555dddab4d3dd2625575e29491a4717e9faf74edb6e2949a6c72f489051107931c403849275a0942d25948f75ec1631ebd5f58fe6c91c13b12a2b33ddf8884c131fe1237c848fbc19aca118aa93e15c7c6e92da96f5ad264739237411c6cb4ff67db20bba6137ecd449e6a4ddac539b8fed198a1180ca6e2f77fb6d945025cf360fe65c7913bbb1911bb21b34c23817e7cad0419f76e301dcde39171937b9732e0438ca9d7329c061dc3917037c74e75c4807c09d7351b900b4ab68cea99034e7540ca039a752008d009a0c6d009a732a34d66af6767b11800d80dd56006063d88d34b229763b8c13bb65d26e32f186401fdbf3a2a124c36ca52c2b13af7ca5a56a29ea4dd196f897d9271b79c6cbd1baea4839c2a9463574e74eafee46b4cd0df5a699eaa9d4e254e465afaa5274aa8f8054232b4a9647a6e88c80700d792aee163aac3c9e438b3d20bb356466666666b615f4bc5b551c5951bd149d1190917cd53b8f82c05797e90ac05752db463537ba2ce6cc3291e834f2a50504692a7a0fad5292409af7f2b2b24222fd2251c35190471fa722293a232055b5c518d55c8ad1ed2585741439650c1b2ab194524a91ddfef8c44a28e508e7d6f7deabec28c808c8950c318ddea35d5bd36c538a4e671195d018d12d0e833459641195641acf4b69a594a320ef103eca51104ea9ee24f940e33215792ad3776e74725e8cace0dce91b691a05c1d145ba089592e4522c95f0744c8329f25c5e5e565648a4ffbd225de4cac72925c98d1f05e11ad528488a0e458d6a2e9552ca51109e11290c16c01ce86f301a2826d7096b17a5924a2aa9a4dba36207a554bee9bb3e293e94e2c6243f7b554aacda9275aa4abb4d333ae4e994a7534e17a573cae953b7eb74bea24c594a299595a5205ba51d51af9f522291234fa594a1cffaeba297b624dfb4d69005514aa90455b55237b75a1425a594525217a6657f9ae89386726872aacd7fb4ea308fe935a5e0cb96e4add75f405c29b8c6f478b2e702efb1f3b624e9abb4e4ab86843feb67c5d54528da89963d15eb269a7b139b8f508a7aebd53ba7fa641151d15bab9452fe52534e33577f70d059d41fb7625d31106fad9ab5a664a1d40a83a2a09cd45b2214eba2fa030546b5aab3a5faaea1ae3e205cfbe4dbfbf17edc780ba556ebd937bde94daf288e5267c16a5d1e26fbfcb81b8c1f0c639232ba07868d012346c5f5c634b2392976cb60882c4bd3b585ee81a45d920042870ff7e528e124348ce4e86c6ec7ed0c41e3c25c8065a016641e22783e63f880a8f02613a010e80f211316989c7caa38f9c4a3bc8080bc6051018145cb20d231ace8101513a572cf855ff88557483b4c8456906d2f1df8cb4530be906d6e679b2d5b63314563d144da8aa6e2c19051c6944f18b2cded4c266c5e919d9d9d1d6793813354d72d20e09a5b724dca0c10226d0c847b308d119c1b9b259d835f6c08c1a02c6c707ffa30a9f74932a4c87381e767c43c827a99b21092f38a02dc8c68ffe04351649b33f2e8f38820c988b647b819efcfbe7c608d2fa09b1797ac5578850ad214fce31a3152396803ca03a47f16869bf1a89d3da2c27d771f9d6c7337324dd818c1373733f4630a79136fe0cd0d0d7838a10d372419cc822ce00c8921babce3bdd7ec5ebcb6995b430b49b08999e11ddcac04fe64c1c60b1b0e831736d188112336e69c73259b73ce25d9e68c349c18e6b9c8bf1bf15ce2d4a47849e106373827cab83b50b58103b5d908597643348d72908958d0e366a11930b89febb4888318ee969dfd40732bd6f39660222db9a19384910a2fe0bcd8438f1d3b9c010b254062e00fee0a10ee26a2e26ea41fdcedc5077773373db89b335273210fe690e40863f0811a7e78010c7007406c01270a353c74e2202466c60a19c4600d72c8c210a2c08798feca737140164cb8828a9c1a306108317d98e7e2b2480213867421061af0200e31fd249e4b0370e690072c6001061e57c4f46f3c9713242982199450410728d82066016668c10ac0608517841c89699a1ab0a1f298020b7d40430de620071b0401ea8006150f78140951c105e6023f5c4970411772a0c8208410562c8021b0e10311f480033a08d98153bc81055317d04007287ee7c590374494ebb61892852147ac232c794ec417fc0b23a0ebb470c313575eb7c50ad0206326c0096e3e8639b8a49e89a20f3588c3139840042531dd5ce4478c3ba02e60430b824c3d842e4c95138e0061f0890f52204245c1092c48a18ae9c4c4872d64b006eaf270dfdb62055fd09b2d64a0c5c5aedb420660b8db23139c1093c7f3aafb1e1a7e6be59efddea3a6a99d13629a64047ed93a17c47b6bbf273778af708a3312018f609a3bc5799eb2ceca591096005238e9a4df22c7c8fc4967e4182b8cd2cd2b34855ae184fc4acfb175ee21a122a5749a788a16ceaef4add14fd3ca95f255ad1ee35df50d58d6bd95a90bd051d43c45e79dfb085ce8357c6fea8f60e6be379d9a93faa43ea94f869714457d4ed4e690f023988980a31cab505db760539fa51342462744844e08f89c10af9d101d3fd996e2dfe361bad497af5193324608a7e93d7e87dd7756e92398b96da72907b67b4208fbd49b7a85da564d337bf6ebdbbbccfede2fbd57e122c699e57869638c18dba955e62d6db38aa8eeb42e68bdad0850b768ada89de27c73be4f32c6279af46467154c4f359e29cd2eb5f2360601949055eeef2eda8f217bef34de9ce83451ca73b6caf44b736fabcf2c3b3569a23e69f212c6e8966a3759750b94d24ef2512b8114627ea377b3275875eca2ce3cb4e009363195b7d0831076626a7b010b3cecc4d477e30220f888a953d59a13030e919c980a6f728cc4d4a8e343484c95556bfc041688a013536790214284c4545ab5c641a6f0e326a65278d0c3196a626abd830c68626af5052700e126a65a794092b3862038e811533124b44127a6665a0cb1424e4cf564810b3631f553b5e6bcb8998226a6829c165f6c01276687b3f9820b45980d40d440c36f48034d4d4c3da95ae32d7440849d988a52b5e69a015b75793603f85554b5e6eca0879d9a980a0346ca1e84a0849a981dcc6f8b2f906c2949dc9ce634cda9e15ffca4c1c7865372dbd81c86c1e54c935d420c48e6bc1b433127cb4f0069244f0a32f01132b4ce9c93aad7ab463dad3e0b4cd32f7b91ea75bd08774bd7e3294b630ed9e888ebf157a5286de6520d87e8f69f83da4ce5ce030d29e504a6cf1731cddca76def0ddfe3cc7d7de13403998d70b4e69c73e64a6d939fe4ccb52c3ca30ff366203318a4e4ee9b514d93e4a8a649324e31c2a74c2949ea08488ace2808cf98a0803e608f2689609a373759f328c84dd60f2699baba4b5578aa82b7e0f3d139345e562aad52578081c015229c627c36a1132f25949406a9c944f808e49c0b2f2584d7263dd122eb4ffc28a1d40236dc82d4c403d224f98034493eca4ea2fae9956c244f8aa86dccfda2ad502756b0818140b7307c40ac90f5b739ab2cc2806ff0049b98429a24af9c429ac472c736032b1c3bdd542ada6d42f17c5e2bebceb998fcc2ee5c16f26837dee46c5e5e565648a45f14e5e434e6076f6c9a34591a4a3297a3c3c2c833a669c7754d3ad922eb6f2ee746073c70f5c443ce0322815427c34db74e2554f284099d9311c94af5b07aa5dea2d349ba05b6924afe5d1a914664196495a253ed280864db5e344e1b699cf6c2088744312e83d4469450a7510d16190e918c6f1612c5b80cd228c8888a0b3b67cb7a07270bdd8b6e89d148b2ce190599e01458c029eefb2848fdd64fe1ad73ee1b05494972273bc99caa868a846d86847aca66b148b68944a225504de8609ac82df154f6f7eecea8e9188b2615b1e1226cd32d946d4e90c09f70daf8848cde15814bc838068cd38a1bb94811510c1263d1840e915bc233a285d0081769e281de3645951136f2399f8b3cebe122f7c5ccf29b78d9465a42e592385d5a6861861e1701b7623b3144acd396641bcab22454d5215b4a9b4dc88cb879cf72f78aac240fef46ba150908ea43740b5579c0e9e0a828bee421314417c5385320090dd850e2c69350272718418124346043891b57647c8da040121ab0a1c40dd2151941011b3148403c7743e2233ce4c8345d73e2234ca4d6a16e40dc6a52cc7242f0090f323e5b19275d46b790ba25f2890fea542dc39f40218b775d9dd4647cdf7b7ffc77f73ec4fbdf65bc9055ada4ec66f654445374d2da39391bda11093f587eb0fc60f9c192fd6081d60f4a29b590702b8ee507d532c7f2c3ad38961f593896898bba0d7199c66516c9c233e02bbe86442864a2e2d5f4147ca75d99f6fdb83c979fd8677dcfe9a39068cec3d0685c886252cd53ebf3232d0586283bc9e2c287404d1dfc74ab2dd17a59c712cf52ab65b12cb52508a939f9d637ba0846ca484bd1606432226522f8dca3bb65dcd8dda218f7bdf7bae70f961f2c3f587eb0fc6031695018429f8f941f2ba5b41b12d30eac91b2a1e828a69dd0a5168a62da9976604d4781c1f816822de4ce77145d73e79d9bd36e32b0c77bc6ce06c87e3c8f6e240cb66082348f0fbd6452d2504c3b4d82dbb46355d5b403806f5900be4d013cda8dfa27c6e33633d03d02b0ae55b2bf56e918768e2c8c771a31bb85328c3ee512dd3997d14f465a8ae61c0ccd39156a1d907b75cdbd6e62b7ac85dc4b64e35d6d431fd1bdac876828c940f29ecb5eff7c3cf71105d7b82edb51cc40bb73a3e019f1991619098470aebcf08c2078b9a568b32a605437a25ba205e214689715d22f12c5ea155622238ad0b8c7c76e248f15b1c52eadded2a8b3cdebaaaad8f5155fe306c4b672e38b6dd3366dd336f3d166a20e35e93d74abb6b91baa19b96feedc28764e1a060ce0a4653370c22e5dce74cc22e1727c480d2ae16e748b3ccc8e69999439ef6e9a7ea35ba08f2b35c7d3b4d24b4cb6d25f58ad3393929d243ad38d0eae11a8a42d98643dc9d4880000800001011315000030100c07c482e18838d2242da40f1480108ca24c6e5219e86192e49032c6184200c00000008000a0499800d2c3730312345a41b15206bdacb84e2b1f029f9d8e502d16e8f9e56813233a05d7e6f9c8d82ba6c3e017cb304a39141e336b391e3ddd9753d16ac99b135d53d35460e5c9d4d87b49be59f3cf10ed73be8c02e28ca0009158e6ca59f306c35e552500df3a93e1c977f46aa097b1a5348ae8a5e15e4a59949d7195efba0dd2cb502fe35239603ae269b8975ed2594ab2ef2d008884cd6708c21b7349537e83bd1c95ea4ef8508c587121c40d06bda614777b35ece57c29eb573b2c5e39897c65f0be5698e40cf11f4ba729757a4f707b57fe60cc5f42ac8155b70a1bcd4f752e784364612e9dbd2ee8d85b85346723e0c502221ecd1a6545dcc4740916040c75e432cc1fa2a228b470daca363049e2b5eda6fe879a78d3d6a3fbc1f772b2e3fb96440008a3a1e05798919dfc836ec0dbf9099c5f7304ccd142332e09710fbe421ee299b6b2a787e60fe0b62745cd66ded1553cf41f83ead56b0085dc86fccbe95a63f976d03e45f5507290051ea7a0d17d6f510b7137c179850bcf9744392e16f609f4f113e7141b2825be0f6e829c0e67d2815a1fb71f4c3ee57ef1363fec21af9ee9d7a7b6d93a30e144dd6a10435c921a13e479a70816f50b96d948fbfdd0cad29629c63fbb808a064ddbdf5f285252857c0103cf4bee02e3e05b67950c87a2b964cfebb9679fe16ae67807ac185d35eb265023b73ee520530cd95a3631b4bd85714d8561ca9b12607418649635443037306056badf3fc80a1d5e3c3c0078ba0e3d15fd830d039a2fcbbf9e7d065dc1c5f025fbb4045110b3b2e64faf077a995daa30a2f52860410493caf1dcf1a4b24c0f2fd148402567e9d823f9f392cb5db961b7ba388d1db9ca20289d2acb5e88b4ca0a881d2991ae4af7049156f359edf613591c5a28c7bec4d3b288a4d61a952cf63fda3c8104abe4893c2ceb4fc686df7cb84eca5422aee0c8fa1c05d50f2fceab26e40f8b6377951a2cb9e685760ffde5b4acf2fe30e3b506f89273b1dfdb8b2cfc03ce8bca3411943aba21d2045e22d41b7e5aa0722241431ca49a19f0a838fe78f4e4f0bf36fcacb4c416a46d7dfde6f65da9eeda75dceb65207ac21f8f8bfa61fe3b77e0a7493eb84190b88db1c52d340a04e0e25ac283a97f2c1b7da38d628323c35b96800c3baebe1380615ee94a0fc25f33e4b11332a53a01cb821c571da192c18775f599c7ac55dc161352378efe0869e2e10cffa43352cd755a583d4dd4188519a1f0495b647eed4bc186d94c371dfa188a1eb1a270d48c9c74efb4d43c21abfd81c3e981af146730ad2f8cb3bed16782e0f6608bc291bf6e8a58f35953fff4a149032b1aabd3688b405e0e5c3fa4bf3eb96b82f11fdef11189bc4e1331c4feef2b1ef1f467307b6fe095ae9b2755e5699579148032accc1eace3054df08fb56ec6b645ca7969382e45667f8d4372954896f0597ea72e32fe50145e8e18510798543664576976520555ccf44354df9154622a337427fa80c5e87a45350a18341ab2c459d9ddc171639e7d8f68c5d270a4a383210ea2eba3920708022f04a45507e3ce61341110aa9f30bb3707b0574b77bd27ac4c45ebd2c8b61a3a38b619e9b341faf3aaf399c3a9f212d899f9b7e2f534d9add4469b2d4397f9fcc2df99f8d799f5fac7bf69492a66143b80fb9616bd314cba11f48afed0edba3232e68e8d2654e0df4516a7563708b9ebbd10d82c95c8c6fb9498461f3d2788b6c8ad74f0991dece840e90c8ca46a2b7ff8d68d1d677ba7b2a66d72254b86cb1a32085e0569978c770c00683ea85c39c16c55ee0a3515d9205d1235f2e3ba75020a4da072d90618259b4d7a1e890e422efb3347f49c5e8e8239361b2477cd28fdb5f587d166b5266edd8212a32799f223b098268f14b9b7200cb1a85daa48ac8a3a71b9a75bcdd01f440feb3e3782f5ce66033418ff16bac6d312e97451f4747c996334c916b367156090431c4b8c2b2fb195f87f1e6cdc62e9c61122cecea36d525a24918ca0b7343756ba003ed99fafb9fe3cbefef7e92577c8e7a8c85d4733942deaee85588154a31fab1ab585f3c21115af0370eccc6c6fc6e146ebfb2b1f9a5e4737741cf39488af3744a43f4195bd5f81751919f4f66e505253d3e04db13c7ff0d63c163f94ba0517bbb3e38763a508fb4cadb14016e28a617072b3ae29b6a9bccb0f60f24370d93b3a0b6ee37ccbc89e3cafe9d451b4c7e7605930f74fbadc11f612e7d37f5a0db49dce6412738859d31f05f8209661263e7e54025df2030ecee406a058066334f82125c5802d9205d7354af750c9c109bfa4dc64e19751268b37bf527bc1906bfe9bc3d06bf3d8b14bf785554b3e46d68062de684c403f4adb71fe0bd69a27507af91a600a1ec38dccfae80295a8f3bfe4f707e300e7885c37ece307b775ce7f38d6a42e998411f4422ab2b2bd8c39ea86368432b41ef1e6c3b0aa2a6442b76a8afea7ff39ec1c3cb3f81e24c3ee05b46c55850828a1a8e269139d5fcf2d0ee87eadb8cbcaa3e9c67218c33bd048a66c2a3cb318ad97befab4cc05f10ee07f709c54c9f8782aafe31c65ebc5e590c4437cedec9a31d59afcec40f02ccd9f9e29125d747ed2cb669d3d5ba2414e19583b4854aaed797e51e6a543ab7fc63cb80897c30c4961ff42ca5df99d769fb69106937cf32018654bb3d24a3a8e2671fa130fa8d3ea44ee706f2f85d1319f93a5f075510c5d11793b9c88596802c48532cccc51abc5db62f942d97a18f20428fb7dd7fc9b44f85f3e026beae560246c11571579f0cb7da5dece975c8bd20fba98bd6cea45ab3e31a6abf5fd59a4f3fbae4108c0720d99b3e6b399e42d3ac95e2e52eff661b8905a17c150ba6e1f359edc0b2b2ef6e98ce0b7808329e33797f4562ba2cb85f3424afa7a1be43903d96a54700df5880870ccd76b970c9b06ae137ad74e4156fd40b53aae42cf87e9b7b1365d510b5a0b735d151e08ec28ab9c30378235f119eb2437b09e932d4e016ab76908d933da0aec442c204e436a8d48ff3521fa9a2bf3021ca717e0b019ec7fad46b3207bffa64cee6d83064633ebd431ab94adf8b14717dc2086faaf831bb0e6f25c05c811e53d1087f85c97bf250422572a9efe9fae2a2af9b709db20eec9f498f344422a77bb7f2fd0595e966f64609892e1880d675f0a0ba59006d55eb64600bdae4e503ae2a45ca9a196806256def0213130050c4d15e5a8823b9b5bc4509cef0f39c2e74cdfb5a953038f3bff437e456f8119ac619d45af52abfd48fbfc27497c1c2d73feb9c7edba0173ab8e2b47cd9665cda557b0dfacebbc4ea7032f2bf57f69694c91d64d787dfd0991956ee2be1a645e5f0b4b30148b3b01bc406b8551784a18c2efb3dc86af943fa3b40f78e046a6cecf5e0355a4ebfb90fa7b14244316d28f43c3b3ba143123281a61f822a484f79769dcbd1bd6c76e1ac8f4b5d8e2d439528d707f395c1ecc1a7715a6b431a0f6fd89b8aa0285801ac875a99989f3e2e7630c6914e8b082b4233a3636039949dc5c6cb574d3c5847f4590c821897d207a116f7e4f5c14846aa8ff8f186e47bd9a49c3d44c43515eb3f34ddbfa99bf462ed95f17a3496673bd525367fa8b70bf479ebe4b8847c4af3c799001a4304c0eb826607120e46d61bc52f1275161e2cc84fe91f48bb50ee3dee807373d20fa3a6c5a5a4b755fcf8f031d34c5af19905c962426b0f99072ac3fcaffd9b390335e242adac4fe9f60981daa5f3921fb0c8761169d05782e3343b4ab290f4289ea918a6f512f9b020e804f3a64b13b4c64db384924f591d52b773d28ac046a4b81df9d24d3ca3a23cb7499db77b4e339174cbfc628b6b46ece7eda2b026adaae926031f03d30dd64b475fa820c29cebf8f4c75bb0d94a73b0c96f8c40d15247f7491a7be9162c0aade681b0f2675951e43f1e496cca6e8ce236e1eeb535a58fc1075a399e692e36eea60eed8880765871c2b78697a233f7aa17a975df36dcb7c5bab1e905893c32a41dad72b8f09152d65e20bab56584448164c2478015161904c0191d7cced300c98b438b6f5c857c2207e066a3f5fc04fa07aff49c18e77e59248211845faaccea9263f58edfefaa4b511f98d2ac3e502315d39d0d6d67f72ccbca371c88e5f429ca63ec39548e20325d98951df6eae992fa9adc8037347c20c2aecf5dfd0f06a1f653a3fa82e318e7760eef521a67816050212cc15852623b4964d531e1318c4d4411d2468aedea76da63ed7f4a1f7dcd03028917654f9edc46e39a778ebae52991488043cfff9b716e5ae7b0a0218a41fc1d4329bc5f32af06dd2ce15ec3f1c3e6f541139986693891042874d6ad25bf9525e41d2273b1006e84514238141b1074775a74b98abdc71490c0c96f0ce7e8a938a355502dc7c29f206ee7461b7c847a87e4b5b77d77d88d1e0828f312522104725f0f311b662f0c176668966fd7c82d3ac63cb53f82902e16d3a3195e8fe41337e651e58b285ba66ad358dc4ed77a68e2ce34a06b09875abb4be97c3ef01b0e5536b8648f90d6d0081614c4328a988772313eb8ff5612170f15480b0fd826f0723aa141902173dde1128721aa7cf33453a5cfbc6ed941cd8f0b5c5d7410d76ee17a8d5112bd03bef2bb716f59bf27374d171192df29fdc65ae70874c5df4adeb7fa87f625f569c996b84e04faee6c017b8e0d927b7bf127c728aa2a6060755cb5d0e930e4350dc0fc21408da19b7130075f2a5419b86dcebdd1b1382d89b4a943bd46f0640e8d17ab7d0168b277a3d3aaa453fc3aa47c7e31a92de240718a50670e3fb01824386c019a4a04684f448b01de0901da054a557027f3851d17924a53b73edc52ad55493505e017320f83e6e57eb514b2a72cdf470d0e8ee173875194acd0ed91d65770e2172c6312a4cd7d15b20a3eab600bd928077d55aaf086caca49a0dd68a6d5b2d564aa0c38dbdb28815c3402e313f96ac8f40316b28e9d428aad7c7e44807800269b2435f3da5ec446610ae5eb39051838018fb9a7e8ec0a74bb6381171fd99de482c8e8bb5e3edb65d8f157c01e2f2965cd70c164611664a6d01d43afd9b4a1b5469c90645ea739e132281ceb3308508b039ed6e2db4f9e7e97bbd36599010c313f36c18a1bd3518489fe8ae1e28b16e980a8f786260ea37dc95c889fb59f58230482a16d738cd50b009efe79ab0b5fc2212502000e8bad67b3fcdd3926a04ada27444ae69dbc4debeff9758405f25504dc685f2a69b28b6e0050b62e6667c0c114a18acd5e0d830f3493f35ea05fccb27b7cd8332be79d87868df6fe6d6ac5f255f3a69107ccb28143ec2f2213025fb087a74412df4024e4c2fe0901f01148d6d84d96a9d4b03fd8654db33bdb15d77236ebd135b254aa86f529fc3372977d447afaf24377da1134b8a58d8a6a52c69b20f7c2c2182fba4414587e05a7cd21cd26518a018a9f7ea2a78d789abdad06c7b9bcb2a845863b23bf39b82d4ffaf616022fc3ed961f46d62ff0a575f32a915ed9f7705c22b5227ced444d5d1758c7c52f870cb6e65dee78d959aa5aa40d87bb33abf9cbdf601aad9a8336b7e922e0c3dfad325ce257842ccba8f26dab31312ea462abb91594b73595ca7bca129d9958710a7ac3f3fd121a9cb53f69c913c4876fb3f2fe92f17addbe494252198f2fdf09767feb85353ebccb79f67ad39910fc6fbe93e0c7cb73ee5500a3c81db5f208430f95512820f053ac814f2cab2cf7aae262a89db970da58d807d0d5cf5a287cabc1d4b7f3769383c4cddb6a314b69ecc2be9f35d7d305a4bd94b887dfdd121abaa897616f7e5a537461ef11b979c2823349b10db0361c86c0d0ac92ea4489b2ccc7c1522dd42463179151fbeb184ad8263283acebecc87c8f58833c1ede47913be93583a57755fd16edc611a4075f16d029b7af9e4af2550022b33236145d0ecd5aaddd409a5097626aad88a2133bf1f3ef6272979fda28c3ceb9a14919e78d9028b79e444bf07b3f615309a7d4cac2406ca7daacb996461a54f06f959d153cc60c25e46d3d09170276baf4b39d37d9a18f092b8c1ab2f99cfe9dae28c4534d36eb1b953354bc992ef2c4b501ad0512c372a0b89d3382340043118f43e502913c247a3cf02982b2ad82d1876d31edcd3fa09f9905b283119a088f0ac4ba4a5b5fe53bfefc108693d132be8234385feebff6ed6bda88ef6d1a4a4710fb9a6798240d3e33f1b385c20a221ecd9edae3e407547d2181987e22587dc4e1f6a8a4d7cef68dcaf04b0b697fd8a1068bbe667c56d10e9141ee5974215e6a08c1083546377ba5f7228f6b5121e54b7862f9af677897cd92ba8de74677a39f26b6cc83a8479e39f644258a1f808fb8ba7aad54dc90eeaaf9cc9213cd13a6c09f443e83c19bc43e82c197443e83c191c331d30524d2bf03efb7937f4afea8216701096a422c5f4cd105e286bf982bfaf0a454f1755f80653392c82371c7ba7c163418f4616ca4fd7c8d2b5e0b8d5cfab4e83d27389b3651acb6766d21b20530166f91033bf316e71779152568b555c6c4a4d0a6aa75c9cd7dcc8bde3a8c09949d0b95c2849b6c13ed9a560c7f62e04456389952d5576bd5e11ed6609a8bd8f3ebf647e19f1fce29182be444f0dec69fcfa249a98338194d9f2d5f14aa70d6df21b9d74d01a3b1e11c71368282949ac53352cd82d2a43dd679e8694edd19b5b1b77940b9c9d0bb321b8ecc01a3768b365d814848465def84783350d6ec3a1073279a687e3a01a63e6933206489a3bfb9ec7326d28dbe684ada197e82d452a984ae58ca7eae62510f0508735743adafe18d4077144cfd6957f9176ff49d8023fcdfc2b28b14cede2c456cb06787f55ec16d74c3876cac91c13bab853545e13781d1fa4130e689905739b0becc649365f083e5cbe9bd255bc3ed4b005b17ff569d6ba7dada8bd8cc6ba005737e80f5df14c4a282d7757648a3b07e40af85c732cf4b3ab5b4a2ee2d5c46c74e06bddb50dfcf5be7de0133dc7e565b21d68e8e23560f59ceed05acec5735a5950adc4ecdfabb0b2b1d1f468aed0427ce672d3e0a5c6ae31c1329ca09e4e4e6b0c50ca2135a1f30e2d5083b96c20d74d77755e20cdb04b62fdc48ae20cd2bf7307ed21f7bdf0e9602ded2cc6899dcf376b526c40e159f60ad2109f435256876f564766bb38ac6dc0446e6fc5cf83e2ff6918286461c95aa67e7dabf5ba979ddfd96338af4e739c29a2be051a7b05991e25049c9c7d155318e9c421fcec1fac42d5f971767e8f6cc39fcbe5b00620136ef80f94511af7751eb228d2e00202856f4d2913df71c2abf2b02cbabaebbe4108a4da91f1ac506d9728768ff47c0a59a735c1afab2b85b4d6ffeb4d96eb6de8349c4848a83ee7a0eeda83c12f9a0e51a6d5955295552e0688c3e5655502162c54bcb685f156777a7f095f5b0ed9a5aba2e4966ec49b6ed9329c7dd1bd33adefc20ebb6d174d9ed86126aca5162e8861ea7f21b43a53cd64c12935286c1a6d3a0edf584b278e91165c2af27c4d5cfe13247cef088fa19ebb9b50ed04c225fddc2bafa4012e7a1e30530cd95ca51d52f6513d54ee1838a5069839b2625d84043f5649f3a6af7d8def5442e3f22a6f41c1d776ce8f259e206ae69d5327c537abd30eb971eb7479f3e754f42b0b31c2710e382856b6af1c9987e3caa24a75a08d8af8799ad2882e0931abde72cd90bf8f0d4eddb138e6ca4c0de55121e1da4eae8bb616ae1ad94dc3a00de1989e995d837d5d67b839b6a0bc13384460bd7c06b0a3eaa00030fa81d8d27b4d2b617068aa1291a557f7750b6e626459caec50c25ba8b9c96f5beecc4a30bd570029621e0bb26f52245b513efc320b1ede1ac634f2ae668b9a175cde54ebbe1209ddffe70ff52d1f9cb14290b645f21799ade1c7a018eebf2e330562a6726949a215d2a532bbf5b918b52ab648cce52337f403998e82438cd2040e92dc85f363d977e8dcf2c958b263bee7dd0fe6b3cd9728c423e79388aa498be4665a7256f2bb8323143ff55532a6e9073900daea1255a6ec75a12a3a072d8bb05b311dfccb10859eda8be5868e18340c3c3846dc6cce73215a131b95602d3c3a1a7fcdd0d9129a8a396657908bff311b4d940e36e603b18eae4733e61430aff35ce431e5000456d0da32073dbd3a5c5b9a402b9a515d99291b1843edc6cc49c9e8e6cd232b8242361dd65002f0941254297863bd19a8cf32e2035559444a3dd0838f8c6585f31eb0f67fd5cc9c0f7bdc0f264bda06ffa52e00c5345309fbc5065e26b3fe190a74f106886807dca3963c0d84da793e31d630c16cdbf49b53495f996dd68e5e5d11f2027b73ae3dc8cdd33b191786b9dd4b84e4471eb4fcae60b347a0ad9ff1ba45e3e02ba8301a445724e772d51e7d5459d2538650ae414561f0a6d1f8e5eaf03ef2bad5ab0d8566e45287f81fcd9fc78c065a27a8a05000224e2dea7515e42cd053402c2fa2ed74762cc48e02b2568f24ac15ab297f4a0f5fbb2580507678b903f571e2315b450626fe4f28795c2143739e7c61a54cdc714a9a82d5f4187ff3154612a522ca7bbcd2649d92723d1141d2ab115ea2f4492ac6210e05100d510c05a18507f983ffa91c8bbd63e898d70e87d5f7f97f90c44a345339f319d56df9611eb4e7e915c893a88af4295817fe6db3c8d33f4c327dacc2c08b8ec1b9cbd3c8ed079cf6388aa815d269dc144770194a4ac0f91a2eeb59ae1caad7033151ce527bc4ac6a28834283ed82dd8fcbcc656121bbcb6e1b5aeb92188ca9adb400ca769b23485c65545ed95a55bb81561d316c1eb9f6cc5315e935defe7ae12dc33fa1495d6c01cd52bfd77c78fc681b67613be429ce6d53116922bc3d0234328b792563a74b2a614fb2421f43f3c5ffa356963199ef295415cbdf9a2811d60e93c105ed23dd070420e4b6e667409a3184532b622d27ac28498080e418835a30b5479b8fb0de499b1fc80e3f42e4b54d0bf3fd1851ec1fe92ead42d50c6ab54e781de310568ed41399df2d478643c5aa11249371596d77a86b31124ac6c944dd0a9145cd2b67ec0cfdf8e3b69a7bfdcc66512ddb7e657df1683888331d44e65ab17fddafaf5cfbb3e0362cce8bc66dfbcab67eba0536e88d5a0abdb266d2347b029cfc273afb498cf8954bd3f210acbedaf9a21ebdf72103b52a7b7102504d52dd0d20cf7944082bca52a74ff4b55907c48927b79e1e5f38d70b9de48bf0181ec28ac1b95139a3cd13dc57faaacd3c18eb45598612ae8e36153699b1b528ac8c5665b3e397c84137afbf5b479dc22d6ae84770f1b67c6c3262596178cb709cc26667f1d2caac1608772b6930cd23c3e800e084f019ddbb0d8de12532b1bc08406077a32bc451f22406e59fb055cf7dc0a16861d9dc680db19d93ac1ac0d1c74272821a1a6a53cad80c6c8e223f45b2f1e09ea79bc24b2ba4b56c8a7ea8213e341884445b15c6bfcf7e8166684a94f80b0ebdb59dae8faa5222aa6812877da3c7f4790375901c5ed0d1b152f42422e581ee58ca20c0ffddcb60c7b3795d5c989e6c647983237cddfca4fbd6f5c94fe79ac2633d15c057018713766cc4e3dc64048fe79894a30b3c0221f92d95477994c32ab724a9e1c66fa68e73433a2a89e1d08c712d56da68ff1512b5e5a36dd0c2414dbb39750751c7ab3b59902dfdde7664544d0fdb8d02d002d4917b4c39a0ff0301b600f2e0d501ec6111a7be9a7ec61bfbd5af0b8a9e9e28ac8c3d508d174a21b74f80f24708a9ce52aa104caa2e4fa38827c09908589d607e2928d8716b9675f6ff2efa00eeb1ce04719a4e7d834dd37edc8709f84aecdfe374250a82d3b488273a633f390a38a0c51f1e7d5d515b04d8095d866b8ef06e96691cd690a6825ae92dcfdda05cd5354d49f2a01c6ea20e0f28dd50b4c118fb1cf2957e00608d7ea0839ba8c27fef304fb29f36ca40695c98defae48e8ae7467566a2233fab24ad5df834bae363d6bce1b4639b3abe7e984a9a687dcc63a7987ac4d0506a516f41b2cd84bbe7c66d942f650e936242f191d79ba72cab6c7e970524c73d63ee116fad4d8b1f8658c3c48871bbbaec31d1e4059be678d3f4c8b40ef9d0e3e17776068636b7a39dbec899ced2a8773a28e91210b236b4f2be6c5368a730c016eb8cf649561b694aa72bf5f58ae1c63c798027938e3dab525819e7f44c609e1c5fe0a32f6069ce9982c973e5224f921f25388760d4ca0d8ce0386266351ca0e3c4222db7e72ea55638ad22c74a57d53ce6ba3a5f5e1a46005932785d0851ea1a6f1e537d145eaa609d0ca03530981532fd0a04a6b4309b85281156c6e700e536682fb7daa26e40533394de5fb501527aa918671ce1e3a1227b975064bf41ee59a31bcdbc37082012d470e5ebf79d9586f6a2274acf5bf8b24ce15e78395740a66f87024500d82048beeb7e720f354cbcf8941be0cc2c609d510e0ad3157120ca590ebaa437218e8dc088c4a431911caf014226ed4ee2ae1836329a9c0905cf736a96e72532009a9e612dea2f3a501a9c2064d6e1ed02464a7382bea98ad208a8193a2360aa30fadb820fc416fcff7f4782dca64ce1ea177968d1c20d0276ee573dd98f8e2528b313efb26a0c05f7df49b4229ce9dca97766166abbc567ca1ff8c73a75c54dcc3f231ca8eb99049fedb3a21a9d00ca4dc0fac401105520cf6c3364a5e223e46f7343e8e27f8f8016607185a54d8e957af3a3732c018026fb0522dbd01913cef0f0c9cd6e0a53fb63e2417420d97210f664d205e212860b38e7c8d58a8bda31226d781812307822ed8546bdb7870333d9c2383a7d624b704f3ebac798e364dd5cfed960d770fd34088628fef15362ed32bfe6dbf53d62052300c9ab35b1d73fd6aa19888d212f6aa1012eef5624e31c69c387c6be3154fe218c5903586961eeaa296f170b295e23317a1503d4adfcf20348f2317c32469765476976644e05e2a9b63be363aeda872c73205c90b7a8d312dd8fa2439ad0c4c04c77239dfbbb5fa6f311327c6d241dbbd428bc707c72e41f77ad2907c2351fd02f47029f6eba31e737d182f8f46fee73aa2f7db0e6d130745d3dcc2576e6c8bd9c37eff853a285e36d41ec7dc7f7964311f80cebcb5266db0a48a9bf2e7632aecdc86e6a04124e890d03647c9b029fb3cf4a5fcffd2e4fd379a34bcc5e2cc52f392ea8a7620acd8f4c548ed52e5d9bbf4ab4794117c60c38a30022fe932861f62a3cc432d0cea07f8c5e2524a84d37a232e870a81cc3522f29df0b94de6c83547a34f7182069419fcc6e8c5b0718dba8d8e1eecba729f4b29b58d198854d8db401cfea8905fb30883efab5863cd17430d61c3807cf4e03cd75f5cc39705436e1139a0509401349ef7e2a3560a585d8e0fde8733fc16dd98163e834cf2b4e34f151dad2f09e25fcff4439469ae678e730f2c6402762e4d184804b07c88d7df017e47957423064aef54ce6a30d2577917cca79c116997f237c82e34c1c91f152cb25e6cd8dc38fed27922ef985ac604065c5ae5bd2dbbb0513ae2b6c80f001d71ab882d24e294f55abd4b4c4fd31c8960b97cf4226d1531baa897cc01584aabbb7a85c5ec55703922db3fdd7f5dae9480501d8b66ddf6c81bb8e81be69387c1c85d61221685b82db91822d8e8532b2c77bb4bad02760bca09a9ea341e8699b22c8d1986bf1813ee90c1029613c5bc3462e42a47102a7ebe14f4100c7815f4aa70290e683a88d9f4c1040ce706487ffdfa86210373d926256ccb3cc506d44b9bfaf77d0a81ec20f87c744691588ee7ef1c42f3bd44c755d4661369b6c45f7c2cf8812bca8e69aa0d7d3f817efe793e07bf904109af190f093bfffeaba66b6f0f3b51867ed1af86f649b7eec5fd7861e1acb9660b422d91bdad796afeb441837f9d7182c8b670c4b546031591220706ca905003592156d8afa63f8c1dc1fac676ba257bd55bd3212643bb95a1162f9143c9c436be3701ab6d8bc496e0fbb9dc94828c5885e8d045aeb3884cd08a1ef822224387d02a8960d33eb872668ff53e340d99ba243ebf8db5726dfa52f3b9521d1451daeaa2cef858c1a27b5c092c2f22ef74091d58ec1ffa1968b316d5b597cf909e5a5c9b1d9eab2aaae491ad87cbeabbabe42eeaef7a48a3762abe8f13d63867405ae6ff25d6f6ef0dd729be01391a8b52f16cdacd4a5d24e9f798a5db48eae76435583b5d7e44bee70b68eaa78631fb0cdf66e21e510721bf7918d8b85c944c4746b6b4688cda0da913201f1302df90623809ec4d2e64fd50d71124f4a04e420da47b44a02ed2e1d723c0f9c7a960426cbe15662b15219f88ef463b5359f0394580d1ad666442bcc4c69d3fad2939eae3bd6ec245157118d53c6c845caac80d328412355e1c33bb8f66e4b7dfb549773cb76a260f36505222af4d58cb58b3ac47295096a29d644526b538c819a148cf6464c5d3d54e2209e5b4b688f62a8fb3a0b3922e1d3b945a5eedb3369d76d731f536107d0402f9d5f60d11ae067bd0a6df04ed9f8f8d216cd290a7b23f43e2d6524f4d17e94f7bedf803c2497fa202ddfd76b2271e9ef3b791ba4a014330465f6d9be68b70560a3c4a5c7a19d45f8c2782d61a5cdfd738eb4fa202ef37737d0c1ec1ac51afbe203940c978aba6dec1832918074ac7289bec5573ab937697f42af6cf25065fc532d983f61034dcdc45b5543cf1345f472090e212ba49f2e82ffc54f442d2629f02eef88b44c48bf18702dd69fbc61621fea404615f888584f6f007afda443d564245029d55c8ec31f9409fad5a27c2c54e8666bf571209f3f532becabe16b7ca0fb34f48ef9c36ab7d5ca3a1ef4eb5abdbf41da1351d1b39b7d3f16807995a501eda3bf6b110a5bec5739615df0078663d3e53a73efec50a1519f015b315be005f9ae2474be305a4bb1d2eef89c83c4280d5b7aa87eff4c36459684c37a1bd4a66659a5da343dd9cd9dda9507b917c3b11ed9c4411d299675e9b27134570a92b9b68ebac8c3fa0f8f166dc68193da21f3780464fd6267b634982ca6da1513fe03021b4c6eb1237235489af974a600430d3706a45c3b0e34199961d209e5442c85ac62802b516d9734571ac34c5d6afc40db8a7b88f156c329647d4d0ea01ad1d4b166cdbc04fb8de59e43e4f25577c05b6fbd28cefcaef16dab5645fe08ac572f7068ad2359fc9c3bb51886b640782344e7921bad8f9922001cf0a751fe15f7caa441948b870e2ce9fd6d6c803ff7892bffabb0c0f48854287916cbb0ae71890aceb0d73cf0b1a03af6ce96363e9ec46959a53869cb954d2fc5d068a90359cfd7fdc86c278b1d574657e0ca4685661fa81d58744301358e60c3a228626c4ac8894569892d2a8f1c71cde57a94b2a8a88cb9a2c06822539e1aad35d062096f816e3384b92b5f5433af902a2bfd7f97b02c7733c8f8f8af1e5f8d773326fc20d775ce34e9047c4915a725607157aff08f514d1184ce6928abb6b0d39fb6b569d1391fb68c83ecab34a7ebb76438db59d2ff6e567a48af888f60c27b70c4067b20f75bc7a07b5cbd1655a768de92ee5b2f5476bdd815a9736158c7c757bf4618d1d18d2efc32fda7f3d083354ccef84b7a4d1c457e9c5e9ff01fc0c2e7cab690277c5cc5e72d4ba03bae321472a89d2c54ae3cda7195a51b6168718c23ee689d4cef1d765eae266bf5426386a6af8486e9ed0b79bd66577de2b69bcf3a6b7ba12fd672773a6b6986d7fed0724e3862ce34c2ff2239903a2498ebe2e652b172a2e8a6314608ab7d5ea9137fa3223efbe7c04a4f23f0cd36a6f0529952c25951c458a83ba0b9de9f4b178258286bca029108e013362bf2312ecaa5b12a5a5c3b6a1f1f774d3dd4e54072f2f65d0e5945ff792e05173e191624b230da7a20aaaa514f8e0df5c213766980d38ed5102568916a877417dd5041c950fc5c88ee898473a10a0a342f74fa4e832326caa2a8f16a200dabd671098a36a9091d5f269ea23c23cd26702cfeab76dd86335730682f54d146dfde1c2ae92cd4863e4301edc51afa9e8d7190e5fbca1580053e714542b7248f632153d4954d3aa1c597c923b4252aa841bc45d124dc4c8d54bb48e89f3d48c649d31a1a988146fa72f4cbd7253f081336d624b14fc2b62b9cde8696bf36d480e1cf62a30424946c06aa51d93d0953aaaf543db15766020e4888014e6aed9eb87084cf80596cd3aeaaa910baf60ff55cb086c1a1a5ee91961627cc45889c3990fa842c6c08c76dcb2b9115bf0dd4c34f420ef6d927f971df43817d7dfaa2a75679756ab9a990e22f7054d1915b6ace483c12ae09b4e09964cb7724e92c928c988ceb063b1c6da6ca599e3aa1c064a6f00d6c3f39d6837e5b787400ed8fae4067552115681e3ed505d4c7fec01b0cc4aac186c962d010ba1a5c972e7c57430a228a4487497f6759152bcac87570e4644657bb559898d272fb04c9cd314c8700ad6c4917290cb58d34ba989896b5164291dba324f5af2339d7d9b825f4748446e6680f0db4e3a7d83f3175ee6a077c94801272c3452e7c679b2bc9e28a87560ed884f349675409aec70d5c53fec3f980bba6dc8e681d8d05b8c42ccae51a204e7b7cd8cf38542abe43fcd733cbc172a0473edf11aebf7be106b8c0f9e5bf52f14ff80f83a164a677c2cfb016ac30ae28eb4fc8a7a5b2401a50ac882b223f300e0b22f5fed4d99b887805e581814265d7e730bf1e57f41d3865bf842b4ae80632e004b1b8a21bde42c776efe64be3ac68d5aa043283e1390b21aa759d57d89e817e7454f95ed76bebbad9426114963eb162c92344b3041bf729bdbdcacac07e714d16b775d8839cb432bd9541e6873ee7236e47ee27bf6dd0c43cd01b40703b0b03bb243323aa34df40d213a0c3fa8caf5fbb6d3d04bec6e5ed2f1b2754e5be961b5e98c658fdea053561f7358a5367fc9f73f93161d0d49807b20100dca7da6ddde64a9e0f15e92b523f687a8745b8f11ece51d37dbd11a094008ef235c8326c7bf09e262759ce755f7936878ed6b82fa57367a53e243977509a0602b59b14786bd0f2ee2b1c909cd7bbf1f597e9fd55a8d5b6d87f07b8602006c55dc2cb3c3c03c1ada255117dadf5064f43401ece3c8c2eb69684aaf34914793ee5dbe7bf3a5875349daa6264b55f9e3f88ccfe567b68d3220942e3e3d54327fbc9329de1bde43c4f4fda2f5045f51ae7528ea79fc7be871c32a01da368e929bf31b7756ca174b7203fffbdc1cc0fa5eb7ff71f066530c5b8154af1960e66db0514d0cc0734bc5e7ff0d94ce19b97f4441d793e9eb496f480c4b29d7d2e4efb862c5ef4d877e6795e77cc39e6314a42e160ef51d3f16e90f75a9648c8a4e075135e78eb9de38db2fbff5cd203ffd4ae2304fd3777117fd4154d618579dfd2955f5c88beae3f5c26b892f6bdfd811c974c7210b4dc83260a5d4fea4566b5d6371b1fc2fea5a15c801a16fdf275e17014ba65113c77d79f7ca0ec170507da136006220a24dbc23949bd644c8a1b7d26fd40c507a0a328d8454b55596046ed3305bfc22f81ed4bc3cd88fe8b0c00265f7d2d83979bba4f33be987f2af6e6d2201521a06d3e805e1c7147237b309d9eb7ac0e9e93f0b833146d6798f3c8f2c5ec742b43a2413de78b9051f8aa22b1608f247d979663670a3e2ca54d187ece2734413d79c79e2e74bdaa9dfedfe2b9304bf46d5d3830a99edb9f9ef76a086cb06816d66abbe79bf711dff5bdb72d2fd4dd7dc2de0fe88b06dcbfb614d035904d50cc3ad799f7ddde0a75c7e081c8165c29b608d816840a5ff49e66aeb68a6f482b429358a8e00ead8364a78a441074e3183a9da92466674210cf93093c7ae62a175530abe7337c0cc21b152b199ff471071dbc1fe31d150089ff7338206fd9954711fcdfaeffdb37242fa757fbb192430e1559069b8f39dcab3945eb75b5cafc46c88fdb865366efe5fa516581bdf7301817f3f55e68c47ffa60a4ab336aa57e114bfb83d2e6fbddc5a1d9dc9e67f16a2fa66886cfb6a1b5ebb5ae8abd8a1e6824b08b3a98f9e9f8f2c2c20c8264f0a24334cf3380909082e4e9df7df4f387f4137cad1aefa28da7f78bcfe3964005225450bc522464aa646cb295ec863c1b06645cf590de5b344612639d96b73c30250e6ad691eb214b07b5859f253456c9fe757bc9f38b6d63a7d5341774dffc1d004359e88987ece05989ee9ec2b33dbd732a7ad35a267fecec16de07d321e16241ded0ba67d34b99a11bcafc577cb49ecc3d241f57eff58ee8059e57afeecfd766232a2c18cb06cf22d5323ad3708c77bc7fcabaeaf94a2ab3ef4f58d7626dd513931f69379479b74f081543ffbed38db4a61bca6c9c1cab30e5e6c07833f8067b3c92e216c23318d7cf677662da646b1ec91e9459d7367c03a59ca7f5abf317cdb67840fbb78ae39ea6920d49aa93449568a431686c9c8028ce29489ef977c7b90fd78fc58f09b7403f4c0430075998a41b7aa7d18dd335c3ca0a47d74f109bb9524aab6a57cc019ccc563bdbdc5d00ca097cda449f2e92f0459271939484aee83fd1d51f6a7519fc03945818aaf835a1ab726faed994b6b223a8341ef1d09085513872a2055b86d2eb9d20e986cc9a2724309838967271942bc78c68c6fcdb55953fd9da2e6c97d3791d3c1942967fd82424b6f3fd4a8db88c4f948f385fcc69934d1a195091a518b0c7e8fb35c60ced3fcc545edc9929c1a8ca165f59cfffd509ca85ee81d6aa3ecfda056b69b9defd0a19795dfd83c0498831763c29c8316912a55e45495cfd1d2118a2b6efc117b744ee1f29711a1c754660e570429c1829a5f8869344634a9d6b05b6c9644b61aa8e94985ea5ef59ef5a67b54f60a4c450b850a1b22391df37626ba5c8e2c0f2e9ffa14055513b47266fcbc83b94153b366864662646f9068d32575fdd73bf699ae90a7b20723672180492b2ea3525861ca0e6014cf98a8c9040a470aece911cf3192737d042ec851c9665b7f8bcd830040162c594b354234f1a1604ddf8c3105712f4703310f4bf5efed90af739c85e4bb8f55328f4cef5f9fc3bd2c11b6a751bdc96705af171f228c473e2bed244103ebc02682c633b3188389cf8b8896f0c847b2e3e460dc3aa4ca80f52812517fd9375231178dc73c78a44ba8873b038d030154c9faaecebd50f1da10f1eb7f69729554025e88dfe9c1cbd137ca9e1ea4aba164863ae4b34dce136292abb78c072ad94a57dc5beed0dd8d6401f7b527722b7175316165f47b75c0b072f16ccd768e90c8c45278e3a91000192abcffb4d21e8189852f8a3f949282b618cb34ad5253d7342799692c3daa5ad070bc006508fe1af4b74ae7bdeb856141f93e514cabc3c834c5ca1b8755e0b7d5ce1c6ad6b07df4c2a0e8982fe91c5c2a67643c3c3dd2549a573bc14c6b22f87631dc01d4d3a049bb68de86f5a04339d5a7c9b8ccd68c7013629a2f850cecfeecdda5f4ea36449785c0c2a5bf95b437e873a5ee7fe6eba6436eca1ae50c1b03f47d49325d2dc40efe8592cddb7b3e1b45e3026aed7aaf5e48da524b0ecc9ae23bdaf29edb767f5a26a331bbff04ddb492f8a64e2a83836d9ce136910aa436c85b94161d95e43e1658323f45ee2229960d7e7dbcdb3c42ca206850f93c62a62295a7c638900b107c37de0811c8840042eb0a412ebf9ddcd37589a3303a31c373e45d79aea6dc1f39aa54dea88066f3fc6dd96fdbfb359b780441d6cc55042f562ca2bfae51fc854271595351e4b8f33ff5676e362a6125ae6948933698b9b1a720353fa6efb4b1b62b3edf9da12240a9a401b47de63744d0c05f070c83f0246c98b92ba58facce6bdc873f1e23817988da1232887cb26115f65fdf03f32a551faae98d1beb2bcff64390b57d82b671887820715b043cf87728889ea8040e0cbc17f8e1f03d6c2eac5d8c1de7ef8d98e645d383be6c835f03bb300d58f102d629c47a85f8777999ce2c34da9fbeefd3659993b93fca6d8013323d4b37ef0934e7a53bed3251e78b36af1fc4d48fe3b09f6747c1cb5d60336e998aa2fc593a0a9041593b79d419f394d8eaeb4cc6981c2c7d80660134120b7091decfa710bc3ba99cb92b8f0a090e39aedfbdfd6ddef42ac24c5027b50a1c4a8c2720a8e51ef90518832ed4de913b6375c3306b80a7c065e1844f8faecb5ea5f55ff1e08977741f0e9e2fde7be3c0108e5afa93a3c70ad72163d4ea97027c3ff2aea2f120837f41f616b48473b79df8cdd0eb8892adc2f1b17f37980e326e1ddd21085693bde25b9a3ddedc0c0551af368b0b88f98893468708738500c1b15f5ace015796b38cba948e7f533bf719c3ae414f95c39a3841e5430f93524c3e38f2996fbe93ade8dee9fefe89b8e1e2576d135137f33e5d669ee9a56b9c61e05e97e61da3db18ae7bdf239fa96c8b4e114b97f02f430d02f87ff370f51fb3890ce9ffc9a58242d990a93e9584d2e98e2d9a473ea70b91fe43818552c6c2cc5c1a2b02ea354b27cf2f990ab36b0da8b51076e7c3b1639c7e81852c255b705b96df329941dc49ab8fd475f2549125ed27d70f306edd9a4eff8b79109506d9ba3709bea94fd03fa4b7924dfe3399aa09bc7342eae1109cbb5bbe4002ce11e2b38fad73f2abc6c86a7c521a1ee8cea929789d39ef76624e300ccbf422325fe99b69efb38c3fd420881c02eb3e4d43d3ccd6814721fbfdcd914a02f2de106320e7ada640e9668a54b5e32480f1a9da13411272aafcf202d6f3c96578c111cd4acc894319b27b94ae244d70ac21740f12432251071ffde208ba19e4986e01066f70ea7f67d8c8cc78c34f0378884362379fd124ce5d979152fc22b2cf4aa630d5bec3020204574dc975f7a1df3fbd243b0e934c6e535231434304ac4a291f5a2f4e725b0648b40ba8d9843cbd87c4f471980dcd35b18bf4e09c9e21852be7de289303183e7575f38444f058ecc4cc6a491cbb3bc0c2b875da9c8c05d66ddb09e4c2d8b5cf6efb1dd3dfef0768ebed33a9c4bc832b2239d0f10062cda84041b8882da16a23d1237cd4f70288638cd85330e97aaa90800b84f05eda706f012cc442f3dc3beaf8088b1f3145ab54c3f9a9558b23bd2f1ea4883bb64e8ee5f38cd25f566ff570988b7fedcf25c69701b1f48c7d96c2518754996f0fee2ea6f9971363fcf3cb7378add722801b5fb731e0a197e04203720dff274eb5ae75a8b91be936d5c2e0d1d78acc6c16dca9174097a85ec3108a03f1034f1aa45e0022651d678d0cd18a04a84d43d068ee2566f452325662d274c2e67900a85ecc0476f1f052f9fb2913a393f3f2c0a6118939084c9b9936134881acf77de67f3cc4b41210d4c984aaa7c0b5384c8ed97d9e61be5ad2df0e889c072f84ce64a5b875e32a7ee0403d843dd2f56613555f9919d3078cbad61f059770655b5644dfe3c05bbaab56b21c2a20bd15dc85fa56e6517bd343097f6bef4bf8e710adeeef020051eeeba7c10983d8e82d474774b052cc0484504a1a831a1a9888c3f7128251b95363f4b4c07d5a7e89ee555e1bf2b1c4e9c9b030715e849369e8c3956d9c654779ac06f07c1ec3879fc76a401a7f8aead108521dea63a252fc0a453387c2b2e8a5d9c267c538ace8fb66dfa9b21c6dcfce0f9cad69f2f56c2e15c35ada0937078fa86d689bf132f599c3ca34e4256875ce3745cc8bc7434581f9e66683e999369f5c75930980e367d99dc0340184c6771f4d0e5a3f9c7fa2d382716d2298c3e6029208c50b3e73b32dd4a0af8dc71ae9e85fa03c8ec81c56cf369eeb60faf7f9c04f8a053511dc943332018372729fd368840b925e73777a7f177018ae09ca095ac19eb84cc52153fb4a61384cc11059f62a2728a827c5ce390b280cce72ced14df6eb3bf2edd9892b42a384dab92a9e47f84ada74a2588d73a0e0d645885a81a08881bc6da7b89c48a16f042e18b07c12069ac97be7bd49740a5e61e918843d7ccdb41b41073670b798a61e7ec5126b60bf443b768dec77cea7f0ee89f921f4e20807f58037d0a9bacda27c10c385889bac0a0838a6ee245f69be64b956a368963d4ee034bae244579a5023b7f34a1c9eb63416204ea38a52d240a0d89d6e9ecfe38661209181a88109daf73e3e0d1107921c9f235e0c34f12db0134e090bb0b7c3f579e3e05caf931f31d3ce2b905ecb39131737f3facf5b26f4fe7e6b8c04c84a014864cac690f9189fcfd25db6f85b133717b483263b4d36e59f34b3a3d9103922c5cea5349db183e881c3ba97d0e5091b27a9eea32c62ff8bde0d526c633fd08dfe2a581d9f764ea896c77acdf4e0790d942b9aa8993ddb8e923e2d6f5c4109361aa00579472ddada7bb82390c0b8eb651ff5b1bd2604fdfe5acb6c4767ed8e057c01f34ae6772e8e8b23fab3cfc661474624fe477f4960cfeb884fa353983725b2b2b4e94af1a2c939f8f24ab8ee7ff36adcaf33b46eaeb29427c57c49ee3431a027b635433ee893538a2054db5641063858146a0f7f1ef039f70c0ecff47e31ffc53da337a302aec793ec52a2c510cbd57f2521645fb87a4fde6ec2af2958d13d462747a9a5a86fa9fa0b99fef00f829820fb3a38f2a23c4e38a468be9683ea87429b9abadc38c4427cbeff6b5ce6db791fb633f888a2ce1501360d121a3529f1e5e54070a837c9ca44efdf4667d6a7f2f235001345ca372c6eae26857ebe89973aff7b17edaa29d05af280ef3bc3bcd378f90d5cf7ada6955dc5ea75070af06b4b788d39f9b05668e3413db5eb15073d03f8afbf43871f6e0214f9b525d374b9f72c3bdeb6266c88c5c92779b2a27397014158985ccaa84069a064347df1c1846fac4d170318f5be1e5eb56cc61985bf5243c3885e1cc1449ab69f31c9c261a61dd8a2f0559c7bcf9fad0cb5ec185b3c944aeba2a9dd3b8cd83d838d3ac641599a72c89034ed58d113348ab64adb0d26554cfdd40986c7bf7e1f8ba2db85d7d5d3cdc49ac9e2f989ed13136a3f01a7ae460786347a082a347a9454141c718000c458a4c5febaa45a8ee4bc9eac82f31602a47c8c13d94be9e9cd3e741c71a6e2a90687082ff2ab3fbb5cfb09d95ed954fc2b02dbfe41ea90b06cac6a69dc218ce6c6193eabe4af365c0271ebde7746d47474c385541a698baff630a7e91322a6e425241a5686d6f0a21333fe0a7e87e1dcae29fac9545859b7e91c23ad042df0ffec48b03290a4f78f080e59e3011f13c1325dcb1af514eb19cf4159741b4befe620486377f5f8b0994d19991199a327825d5ffa319f852522c45e21683e32bae8e1304b46964562f0944755d64b45b8e8ada1ae86993fc95d9aa179bef28e74c30e2983628a85a36f4796315f514894bc0fa33971c25bc3fdef25ce4c605d72fce41993991b62f35b74d2c6c9d91c3463a35b3f384a57b6468f05c63e06b711cb34ce621d7e989ed212026c83a9d71d8fe6a211d7205f428ec50ae048e60651cb8b776b75a160b2ec371b34fca664348b5151b1b52a8fcb352f4dd2c264aa13c0f973c62a28d39e031ca875c1f0413cb27369a495baf622b519082946dd111f3256a5a4b316768a9d8da1b9e6606e279436b4db6781f676e2e51f0035944b83b796c4f4150cbd94664161873cfe8a8be4d6443f0aa7e048a08ce2f736e23df47550245c63c91bf0c590b3832c9acab6dae5ee7bf3f210175e4c9461f75c4ab93d425fc63387697c7d70ee5d9a38abbe986a676bed2fe4d2750fec5607418ebd0cd82e36d93108b9a8e55c6ae0fe34dc6bda482e31799fc2b095e332279b7dae068c8af9b01069cc3d1bcd928c1e3c815fe98e4077aefb810e66aa40c471f68375e60f8a496ba81edb5692b8575d7abc8c0b5a955dbe6ed69c7781343a891a59671f48287f466ec02e3bfdf9527290d00b46bdd9f84dbf3b703e55b97b9f72bde3b1dae0f419fa40c0468fb727e090600ed503a0162b711f23daab2d8027c75801e12355686594cb943441b088dce43c582f53177a4f119e97c02e9a3e6155204199c80832fef00b046fafc8b1bf4f56088ee6077f6e73981ad5a235adc22d79ca70b5bb6c3918956e8d27aa39cfa1876680302a58584627516896c36a2666db867f12074276f985ba1267a696603ab7aa25b2920b204c621ae740d77a143b0a95a6d264aacc87014a0610c1e92a695daedc44192b7c893b4c16aacbbb9e12c9c3f3e722d617f70f4a5af6df88003aa8f037645b70c9ad9802e788223153dbc3b115ef5ebc6eb1f2536bc8c9fc190fd565150ef4b2f46b79c4ff7aa256d8943c626631a738fa1eb6e7327a281b7a4bbb53fc340ffd8ab47298b52547a04aaee5d1106c81a9e9fb1b8ffc16d8a40d0bc83650e42485daeccdf108f363a983322a55c7a5133483af8609d56860e1ef5c78fb75e05636a90a3641366dfa081d836a88e440416cef8875504a3c5147ce98cbe05528a1e8e229b611e3bb1a28341e47a196931ae1fbdbba9a92ceca542caf4db7ad4a7dc99a58ab8aa0d24bc1429162bccdeb9cb30530b9e5cd3ad4d1618cb29fe3ac0726c71ffd4b19cbf61e1c8e3d0867340e6ba57340502207589c7dc237a0409bf1b1fc6520b372e4e6965bd5def18342a8e8c907d2abffe08e5860ba01de9a2edfed52cf2432975a3eff07bf3679b34e872138993a4e5310a2b1fe5bd48bdcc481a8bb4143389bd73a7e7816290b33b9b609301ea29380fe44ce498a671954ee342e55ddaddf8d2cb15c4a3ed82ba5c5165854dac4f7fc82f82c37cd77022438a517fd01628f515e4c872ae25d8af1d07310ae848c36651d834fe4d1fb8f68fdcd2acc2bb2fdc9bead38713e9668b520900f531cdfd272ccbbb1b72e6e677aa66bd538c4f82eb3a1d5d349febd312dc577458ff8d397c3d4883e21c8f6a30cc0918db0f457900cd49523b067973d599de240ec2cc72c0b7ecf83c1ec4e1daeae01773b839fbfa7a38d521d75e18d08799eb645917a6aa95e548e0d19e0fc516b1f962da368b9dc33585f4c6243334d721d31ec8e8066cc924152fb52297aa3099da70d27aea79f482559c47d273d07673e0be0178d90800bd57e979095688ce5380d063713cbdbcc91afd044aa169e49483034fa0b83effb162071b9575b18da5aa792310210cacd5f06b5431d8392813aaeff41b4b52c0d1b8a0fea18b84e0ef542aee8dcb2272744eb6d44849e0a1724120f39c9ce3f5e92ce72f78cd9ded2eb0368dd331e215a619e55ba41ba651779ad324bd55b3eec020df8b3c5bb073854edc765d2cb7b8626506cdec7ceaa61338df51e137d69575a91e9d3f9a3a1bec997cea751080ca195b19483ebe8ed1ecfc2c7af3ac0b33693f105a12def795cfe64f0adaf2c2a8cc04194d6c88351fe077f0d10c8af5cbe57d912203632bdbce89cba418dfe6a2876b287bc5fa8dbb21ea4bc167b19c92973eca612694972d2f9321ab8b50269c3ebef7b3310204942ab828a2087744b445c042eb69383b451454232fca11d333426ee9d234af604529e7899048f6a2490ed8a78faae832d20ce3f3b31cd1c08eb816c8654822aa6a7e53bae05db4ce2894667a07189ece585c684b0d66ea7f3ee24a565012eb132af3686c9cd777fc9deae96c5347e292e239509e9a0943ee7e2a677d0bede172fa9ecc2f940e8038c7e04583c1887021f6a8910b9bd40bc557f6c729dc6df6b170075f0e2df1a62225ee0ea55fcad3aefe7c68a56f73408fc718f0d5a28aa3ddb8252e50fbd6986788d8015e365dedb90db8873317fc472f6530aac84d869974a0cc69be77959a3529836ab2cd5ad13be0b7b99949ccdbc320cc3b07df37f784c5e57f4a1fe5f38e3758cde0743e07c7e419d97a26c9f2d0503eb98aba575e723ab0c22f97c7320c950f9f2ca8af1208814f391d1beb29463fa68e3e5fbfd035b745477ec012c51cc8269d638689392a2ba7e658bc84c5bc8d0a0841843e79795bd323fbf68259b0fdda0c5c952736c6f6ac133f5ae4d5472c9e3893ff81473ba53904506a34640e3f6b5cf32719923eb8200607aacbb821d97fc6bc2abe0956773d1f6c6713ae40cc0a806360e838d5bd9d515ad6424a6ff3fd04618fe36bbe06ff8ea461fa7589abaff81ee8d4507eb1abbaeb771043d2989acebad62c6be310eba069e57f0269b5dae895e26498b4b14bae39fab4625f31739611f7b6024a43316c223fb158db6b13b87b21166b7d5e6f7606c61a433d7d64a173c696a39ee79f5b3b777e20ee67e9f01fd73e2ce587bf0f6d52c77e4ec22b69481134d07dbd952217d3e835dd220d595cfa7c24768dd74c8a79e63391d055e1aa9e8ef04d0291fc594d8cbaa596df39fceb4bed1ea6a474f4550e15e377f9c21a6eb31a97c682377ecaa5882fee86de1b854dca73b0d9233923796d3364fc3c743ff62d94201cf0f30c5cd2370a7454d73489db1c13c76c5c0ba8d18984cc1515e005d13aa83f23a670fb62650a576f4c9c298357085dd8d321660a689aaef300024440ed6206be6c034dd1b63f2bfd5e192e592d1793308e2d64096bc57736e9b4433424fcea1120f7b0f90cc1c30d507e6215a81913301e22eb1cea93af7fee4621a287a00c2588208ec024d5196ad82f34a8317457292fda110b7fa235a7525e758365f56738fd180d5e1b48a784203e0554f30521a834e837f2bbf44a7db6b275be2e2d185d9dc7ed7cde7478ea523ec1b987edacea7de36a9728a8642729adaa35c093f22ea7aee4427e7afaefdbc27487d06fb836ca7c7944ee6d0041cfbfc9634ab6e2ade2c13a823cf00d603569a3d0c8d8b251688860e9e225e300ee3b5699d0fc69afd6e6a95292304ac4152311359429853824dc5703824eb29309d5781aba626ed73337dbe189629248a99f28482f4157be885429c89292ba5ab7b718c098b47bbd57928c8c0a1261602ea3e866d3113354b00127b2d4d123dba66bbc728f7902440a3d89e9c4a0ce39b2aadf6df50dca410d5ef42e3c19fba01650c2b1c3be5c5ada3aacaa78f06c4f06d6b4bb887818dba24ef35e48df618b74d70cd5f32216b07d68eb88c621a148fca0df027b9234fd6a8dfdd5c31ad44d622353730af211761c518058c379ee52fe5e48250e885ae5d70175d768f9b13e40e4067bd75632c2ac3dded4587d9575b98bca7506061b4d270827ac7fd859cb75b3ace21890ba35a4d0a69e4738beb255d10192f64bc13ca2042393fba8398b28c59ef80245036699da8fb95228e29f682fa549c4637b8f2276250e923faa944e10f947d7a4a821be89943fb9f1911fdc38b430a3f3077a4bbf5a8617e34f2299886a9441466cb2af85765f404c837861ddacaca14c32e31e5e5bdcaefa9186fd774bba26863fb2b8becf7253ab7ba019541520c81b154fc9fadbda940f61cf42169cb27ce500d9f0edb6f3dd8217e0af71b662e4426d2b3b0d7f37e935a94d17b8a4186ed544d6879ed22edabf946bc3a5f4535807f7670c007d52bd4f466468d947138ed1a847187adeec84108a1ece2b01e184840a2a24b07fe60cb0de83dc76a4cf99e3f4fa5b4a34b340fe9312bf305363bb8d97ed4973302585c773d97e4374d0e0d5fbf208d716a77d44d31497f5e80893fc2d044d48f91821264968f2d9f6f19a2a3be8421f4acaf6459b455e5d76610e443ca66f9fcc659b4304133d5414a8bb875452984ebbc5e703328864bea78478a5ed3897a296b0037d90d151db3f79c19c64f14adfe8539a5463e2548236085075002cfdc6a543719dcab52f9c71a67015aa0a0a295e5838965a109e79b2975158e2567c0cfd11e51fa9d3ae9aadf2e1f9bfc76a0d4968ae2ca1a2f90fcea9689578b8db443086fc17ac09e411eb0c25aca4ea9c58fbe81ae7c62763d02026f1c53048fae4135e7c500b5ebee3e599e29a735abcf5e8ae43be4878be066f9c37f96c727c65f7d525ddd273555e0abdaad4a84dde0f3c27ef099b872421b1dd1dc53879270dadb699adf06ad37c533aba059963a73dba4ac9517ee9e526a47282ce14e9de4fb2195fcdbafd5cd7be8fc008c198ea2f2c02536cbfa0c7d9a80280ac6fdf6743a28956477bdd874a1ab2ddc4a6b850ae75d5eb9a10a5a891562f62eb6d888b38cfe879a45efce135cf1397d004fc36a1a1809255c85c55140a610900de2a9898dc490df6e01c189d5dcf7338e2851455dbab25d94fe88def9aabef3334ee4a1abef1bd011b49c733da7bf1cb1111038889c82039cacea16ce858002eeed57ab682055cb78a514846c29a8387c863e49e31bc7c0aa4e1b83e52e1e40de5e3eb52fa43d61756022e82b26091c2c5171746ff28b227f119080392384a55764e35f50f1518384cdde3844d90da4b99eb4541679e6d063eebd9b7d75885fa6d73442a94be876ff3c8c45e67a70e9947993a756a445589a46a3e06bad3434a89519f71d3ea63c8694c583420b4d498e899637102b137e0e30f6c2ee779584a9655a31bbfb3838ed5f0184bcc8e1d74282245bf4eeca888ed708ecd35316a74bb29979996000f90a6e2e68d6bc75d689453be5b9d13ee6d87bc647f52fe98c51c1950dbbc015e70be8b4f80bcc82da7d5bd0dd9d29f4726a793bd6735c9e56c7f3b233e490325c5ee23a66441abc2bf3968f4e712f115a154576090055a53250d52f0d16536cedeb34e389112a25b1a0cdd0cde0eb101a37dec22546157704f411a7ed00175555aead7f0da932532adf7e8512a3dd3f5bd911eec74a1a2acf8fc699c43cbe7c54490b88d3ec4f4cfd08759d58c27b380d05f28b6d215afdc7f79a745dbe3553145e300bb017dd2f0a1d8bca99b2a939229c68cae867de88d8a17dc0d26ffd6440fb183516b384fcfdbacf27e2d0e6fd05822c886f872764ede37ac1005405f918b590dcc916089fbdbfe3266f7d0e8c453fb74308a736ca14e46e04e1645ada74309ea90cdf9e45fcc2b6b80fc9e420dc8cb90ab4f4f83655345fee478cd8b020f500be19eaa8ee3e741fadc57a06f81caa08204d5f98356eb94bfe786eec711683f89a047d04a6ea3217566f3f25efc9ffc39362e0dd5e37260139f13dceb699153cb33a16bb246ae99c2cc2ff6223b309ce204384bb946a06ff82c4ecdcf5b5ab0e8cef268af51d8d4b9a1a46565785c5095071e48fafa3875e2c297a01b04b6b8e2bb56a7bde31d60462e1e4e28e4dd360b762b694e17c96ea150339b417c6dc70166bca55cc2639a71105366537b56d93a81d1594215b46fd0d13c12b099813f5a176849660b686f5464995050581da69763eae0871cee2dd0576b62808af46b46120d2d572d049c2cc2cb90cad13860adc57c5160bf3c1265ec28f1741a5631ade49c354cb05fb8bcc2bda74e625c93a4e5a35d52f48de12b4475bd4da1e824207d520942d1a684f4ed9e6ae958faac307fcc16013c9075bdc351d7daab10d728efef4dd1675b94c4d0aab920afcf7b86461da940a8856be0485a6dc2ea3d33e246d8003948d2f0a5d439b7a8727988d3471fe89616c69bdd9ed831eb19ed9a95be1d2d77d6970ced3557cc0ccb5a0def14171f84d2e50cbd4d6c0f36782089041fbe7af430408d1579ee94a443aed3cc782a96dd1e8e6c6460b261026f4d41bba4f93a14a18af6ffe9830f2cfc341330626685c17b10c592e7e4df47027db918bfbfebbbfacccce51177392acc77ff964e1698a88166408d18600a6220f3b47a97665e2d451a2e6a65eefb6826fe9fe88d7585177357aa4cc9e27d7791bd1763f26ca6db1fb0d1b837a3304ab5965ecb647f706d71f6272977bcb7ceb8e2354db9ed5bdde25f2ae531848fd707f8f0ba7adf7f099c7a454dee7f856ea1c31b06d606ff97d6e2454cd366cd78bf04c6f22c8b29e440f9017c2d9181253d76ad9017c0c7c9f233f404c97081e913b8310e937c9eafa57d53b85d07447a81866fb17855c8ab2b8781466df59733d46a4cdb4472d77cc675bd2f074d9dc80d7b06319f14f4462c48cb6ae27dc625ea8f2e8ec99e9fa95c14a7b0619f82a1da97092b2b1527b77aaa7bf222358e4012f0cf65def2a460ce3213e77513c405587614418f68e66e824874803311df3f999ab7f404f03c9230cbb6773e8c8c4cac9ca0c603ad1c453633f902161170f784ba0f3313e311d89620cfd1fe5c73967012b81de8d1860266bc739139895e911f38ad04664f7ced96d4394630d4d28d23e40a6acfad442ef3044ea219504bf1380b1202b044e85027ba2f150b832165e90958ca59940ccd552aa7a092cc85eedf8077c908482494faa30d0da37ba05d8f12d69a0e0d1da25a451dd4720f0d25ac863a3d486be908a1d5a17e35a8aa0c89a0903af060da1efa8ec99c4065d726a22766620feebe9e9e74d7fb2ff917a279ab3e45ba2f5906f8d55d518fc00370f112a370edbf3f8713fae71fb2b2621786c335d14a49f338e75b99d6e514616244ec244a66d9e25dc3794f4b5bb52f92e42a309af3aab04ee66987a8391e746df02c286aa1005ffc7c1371e1cafc120eff56494869567b6502166b0293b089f3066603b042679cf92fffae2ac317fe16bf4b87841b23a1767558bc9ccc70c8d050ec6ddded14b9683fd28a783198366175befd99302c25a4e886b5f2de63122df975a704e1bfbf5b97696f6905cdd1b2b2035814663c59039b23ad60a8788b6816a7891e68bf12f2e299deb4b401d151425bbb06b4005d4f8c6d187399adf053163131fbd744851aa9deb41bf1b199cb9db9051d13350d9f75daf35e52bcd8a79fe505fafc960f09e54f554b3c2c55f68eeee845e9c28506c543c7fdbd95211e55300f1aa41c2f45dfbf337b2167c36313e879aa8ba21ea14e38da60284fb37e68eb58fd5b57a2ac205976344fb91e0bcf15d446bb32d85f4f70dbb6df52428289a5172134c28f8cadebe84984a4a6c82017f34a780611531e3e1a491fea603c54cac59924069ef6cfb800d201266c45eccd40e93adca972449c8a6672337b2c254f6e1e1adb857845b4d3322c6376406da41e0c174a002a13a5f0738123ea8218a1ca670d87f1658d0ac263764c48e23c3065d564619e05a68632052688c552a6f15e77cfc9f0652cd214f9c0044463460fd4b562031d842da2e40f58584614e896d0cedec8a69f4021aab53b942b259b39e8c42ee3782165f4dc43acb1332fc580fa39df89b55ea0f931238a557c5b6d88ec93291ef0eb721ac7d7bb33c74443644606699822e74b32c1de3fdf2530cf44cf1d12cdf76460be0cf8877a50c075648b7303eb9d911e959511b026ef40f213b1cf5f56fb3bdc44b66a49e4d627ce35e7e52704db1a19fac9350fdaf64c6acfde2c9b0633e8ae8b6be2ff36b27ac43cac1cb2515220e5a87738972f325824fd22e6b7f8885c69e3042b7191d61b004364d89ed3ea65a1ae340944628a3e6f90c4aa194bb33730354d7711b16ac8cd7fc94c1b6589b151e3e0d00d25346654c9103b9de31166288828e105e0ee7b56dd7121542ec7c0345e9014309f9e5f86dcf932b7892805d856171551ab8840559f3e7f0d24c63d900163cce1f65a5873bdcc6546902a1957a68bc1407b7e52e90cf120ca87e05d41831339834858ef80db09fac0f7ac672761bc048554d8f1f9d4a03020d27b1af27e3b2b2f981af778ed674edc8165745239177d9613f72d6e5d65875ad5085121fa920b6769a6824485b8a73352f602ebb4ba36c452beb37c52f0f6a8d9fef3778f531d8ca712100bd19d36bf71ba14dcf2c63060af9c3d73e58dd67da15029d97a6cbbc746a84e8600913b1320f4c86dcbf278dde947f9052c4a865af96426c0ab3629ea190de96cddf59552e86e151a200c5ce043535fe10adaee7a537a9cb3ebfeefcd0befee356719664f43f6fce44de17865fa44b430739caea90d49129b841e6b9840faaf7bf319c6b0ae631c28764c719196f3454a2619ff5db129616ccd6ea684491e83e2ecaf26d1a0c00203de21af9cef4509b79961b0c3836889ffaaa7eba7fe8588d691276461612240477e115add24f036438bbfa9d2aea987000cde69bb6a72fb2dadf4aa66ebe3892cd88c34840f1e11f6d92a55d90306dc44d5f2fa5810f5f76c848d2d07ad793b66137a1a9026e338aa0c29d87e1efa095773fc58f4324c506bcad8c3401bffab14a606f67dc5bcebe14af28a5d94c2cc2a112a4ff156a2a901394fed2889d0b1f2474792900dc0ce619cb9fc681d01065484a8e21402476a1cbb48ba9e7ff611d969f839b9259aad21b3cf08f6522a2a72fe6e246a9c41bb2c6a20f28341712ac448b9ff69a352bf0331921932c4be73e90d063f543644a96f76724abb6fc1c6ccb2a141a03b506fcb8ac4cf929ed51c084ef662903b9704100540b55a488356dc7f082e1f0975556658e008b1b15f165c7619778e5c9d8b582468fbedf8b4473b4b4f175aa128ac47f7ed8b81530b81123820f09c5524d56b03ac7826fa3df532f89b110c4fd818382b1dc74dfb5421dc351e03231a3ae21aa500a68583a769dc4c29e6254e13616fbb41c9315f02c13eaece9710cef714357ae4ae8ea14a27adb6332847ac19a906a97fe12f91d367911a4c470d6c7ab1e4778b3d915a93454a7e4e59bedee0e5e5a14dfff5a3cb6208798fe22ae54a128867ac81f9a4f452808111459881a02789f266b838cc1fe7be3ce8569d2d8385fa19449c9884342249dc2d6985199a74dfeded4ba3ad0724308d5bded47e74ddf8cb78c1d4ef631c1fb143cc168d1df724e697bb7da3b8bd3afd3dceaa3cb29d1c2aeaaea024ae9b9f529628010f8afc4130f8e3287688b8d9f2a823af8a18cdb4819bff816ed0fa95048cb5c6ea13fe848b468883c41a09f17e37cd1d018437d1df080f507c0c9822cec49734568e78bdd684bef6b8df10cde6d803aa8e45593e8bec39a1e80127a239975046f1b19c6c6702a0e8070eae103cfbd5515faca38f097102d22b09525c7a8aff0054f77eebf5fce9ba00a226cff44384d256d2cbf646760b0a421ecc25aa96d1647649435ce747df8fe1933272f44139f2d2f37cc721e013015885add011d0208d0795c3607bba3fe1127e3a603539c272d62708d52d2f37d880e3726b0ac55b7406c1aca5038d450eaffcb31c88b71e91104bfe90190e03c50dc485131f160bcf75811115f3f57a3c12c12fbcad9a25685004f81b3e4e422a6101282afc7c3028b86197dd7dea1a61b5cf1575e243dca003a50930cbc517e7d8538606233c32ba37f7d8928aeeb5d7320c237255054409c105c2f75c3c60a36d7cb7a1632b3353cfdab53d43d73773e9c36c5efc847e0a819f2a340fc9e49907797d535e31c8a651ba480af015c98600346e04a2a97aaddcc2aa47af70d8f828b86f11e1cd4712dc4c46edaada701d62dd9924d7bccdef6d304be2a85656e12e175b76a6f353d8f4c6b0a941f2351f619c5e5740e5d9aa1dccf99a2b68c5d50f3ba8d167840e3faaf3b1dc4d0ff979d8e2b090ec916845229d18725b06f597b2f2359fd06593c9057f1e8a8cc475c0e4a3fe745919b721ccca99b888e444423c3a1494559e9345a2a901ed7096db378b5127447a31c518acb7fccb20ea53b613575a3b078b9b5eaf24b045f4ab110d996eeda9ec15d1b7bf89f4d548347849549ef480ef0ce0bf7ccab20411531220a20d3d77d876e105e02cebf2f129e906106912fa1be7f020c99e30d35fcba4a8b5ad342a228531d257ee47b67bc45398437b6826913abd58048399c490e033b07a917075800359e6f8949060e180a35b7d82574152a1e9e7998391f6ddb5b157003f17ad50c40d580ae3fd5a320b30da6a9d5213ecae2fe8215f7d0a26acb85f0c772d5603438a9d0edc7cf420c67bdac978d75ae132a08e518c77315800db2fa4c52871450a2f44eb94d70eb3eba47c7a28e910077db28a2ce404a29a9130c61bdd24cfa06ab6df2d688d7bf5a5a8859098313a4f057165e8dfb73fb9abe802cc8690a9141a6e5dffb2f508720ba006915f471ec2acdce5aaac040438f094b0ac947786e4360e5135cd229fe811520863d45128bccb41413d17d32f97689a1189964a009e0caedd78e264a2341b0481e5ac9414665501bc386e657c79b371a736205b770a67ef25174176893a0c9f2709e14363252a92ec2e10918a8bdda5dd8db433e0241a3cf1062f064b534a8c2668dd5b7dd4b2c1878a9f9c510b55d2c990f819d43e438d8e03c75ad39ddab498ca308bada6d51537826ca755870444b49bda83c51b4edf6283850d3465a46b7a5918f296c0b5040934dd76874f376a83ac3bb74860ba07ac7a00b267edf2638dbb85cf293a281fe73d058af830aef9d87c0170fa790c7bec95eee2595f64471993171aba06dc0180a89a2e29ddb6674d1054186e82d20d4f8164e164b078ed49772b78080cd5b955ae49f7bcdae20e45320a4be59874e7f591a37b46303563e99274777271316c9006d44737d20811ad68ff462d7cab94318d33e936fa4cb07d1a5c3d490c72e7e411e18f0647400decf95bc540b27702313aaa60c21c28c608930ebf40d629b7608a4fdd962fe78017f0fb0bd367ff2b258af13bc4d1e0b7d625c90d0213cfeaa9331ca72a1bc70c910deb150c0f5574d10324b6c5b09156eca40582bec81027ca271e4b21d1af2a4585b337042a3ca8b8cc7da9d808051db1ee8aa1c938894e54e2d9e2563d4a59d467e2a8a357293884617148c947c58a7464ccc57470228e7deef8e70146530069aa49fa23975f9055aa6b735a27ce4bdb0783afa24a4adfa493e1543ce6a567aff2226ff3b6541860ca091171a33b7ca9f4df459e8903ff845a9fa4d87243810d57d7ffada6bb8f24d247cbb36db877df7bda28ead8721b3ddfe147185140a8e9980631625bcfd8e55893d103a32e0adbc6877e28a3aac50bc0e86c65378cd44e0d240f4b78566d92f83eb0992e984cbc4f286aaacca4d6dfae22dfbc0f543425fbc903478dbab3343fdff9a94336f154c06127f43db2c87d3b0aa52e3b20639132b56bf01d871ef98df05966afe3cb60c99d0b4c785c53480e5c6d5bd3ed563525cab0e6ef924a158f2eff7a27323fe8def7763762a41d9b92c8f5eece23cb596efb5683a44bd490cabeed49e87ffac64e245a3e310e50547fa21a2922e67ea2dcbb9df44828a9af319771338945b16816bc0cc70a2d8ebc166d4ede75ed2028af56841e943da9e4d0d5dbc7520bae4c0de828773fb53cfa4ac80d920e1ef9db2216fbe7dfd15777833e24c35e9f8a8d13bf9e11e8427e5d2c791662e4c930a6f22f54e68538cb310bf2f85de14b76f7d2072a4af3fb17c5f4d2184ab904ffaf6353615e06ecf0c4c83efa8fbf4bcc6e40328abaf1403c7885a5dab800f2083f4e7a0d415bcb8101e82050a2f3329c82a3ea96e98888330d5e9dad3926189c03e8cea2f5283ef6a17c8dd2e40c8b328a12981b867596143b095abb6203141e85c623d48a67ca8107ddb7b3278d4aa0d9e2618f7d9edcfebc594d5c8045e8e49668895f1b1f54feb519a6d3d2cccc4e8238bdc354c92db4078995de9c5ce02d49f608516c7c4b4537a2986db55369f12b72b4a5114139829fcbf10f08b777b494fbe7b5fc6c8c11f4758cec291b9905d25ed3863514fc80470f0f82f129bfe88c28fcec4333686526f389f26cea3a525fe887cea7cd5d4a70d44d076711067639138539097b5af5161f67cde416e75ce085285474e485d1d59e2d003bd3c778a78b023c7b1387097dfdbcfd6625f16e435748c521ea2a4f452de0dcc18bde63533bb837fd213265c38f953ff8fdee911abbbfdaef2df8f5de6bd9f76f56fd72f1f2398b9aecac4e32e2f79541ffdef3105fd5b296705b925af93376a68972bd882ee81e3f4ffbf60c1aeca6743664f0ee5133fb38472a6c39f6317835e28ace65e7474e99465bc0c235819ab61ae6445fffd0159f2bf11da187ed9c9cc87fe10b67319fa6bfdf630602860985ad4784a18c9a36ff3f52a61ae28b21c9feb6a81e05085eee3d8c6cb9cf893636e5d1fc44664b55f1005f35b4eb9ec3ab93c4a6c01926c85899d17a437b7c7bd745fa2d36775c80597d43845884747f52720bbd4d9ab2129b120fb5d222908af7c17c68453bef0544646bc9b52492b6d7851837ddb1982d42c6a8a0a735b8a6ad9ee4e346bec0143619dfc1194860ad9861a8d5a477558952781b84481085e6be1765160240e26137f9fc3ab7fa7f745cbd8e9f97561953a9c02f9fc2d6a3c264e2096479106d956a69088c54d11655246cd93e12d4b810c33d930d9524926309874200a913b1c2755119f3461b39fdafd86d726887a8b0218c84cd78b716a55e10ff5764990d0870a58df4a8ac99ce847e9d9b5a61d1eace785f2280f460481a9501aff644c28660c685ed057d65f7465504273d4eaa5b3864439f23b1cad7489e5dff0cd567244eb0e49c23141047e6336dd1046bef4ff17e949168a21df1453a62a2101b179e2fa1acbd5474548fab837d4704d8328ac181b4b37f4e491b9d6c6821578fab7807c8d78642eb09535ddf778dbf7e3c1a8174a7fd3c86f90547da5004f371dc794a1f6cbae88effca0a6dc590d4c3f4b31c245e40e4d401aeb20609b41528bb8af929ba5bc4003972594b65571548e264a9af1f6eeb5c8722654d6d81b4a61aca5af2c42d5db597b8126a1276dc860342662f8228c1a0cd2f13c0709c6b8ec68e46f894ef28c775ee049d4daa253d8c78c2c2f77adaa869f68bf5c2b83826c181ae59b73954f131f4c3651d7ed0bb09d51f5893565574741a1c9e64394510d9c233b5128a06a76382daca3e18b778f8be8b2febfe94ed782f5baf445c41acb8a73a65ea98d8820823beac0a50afd1252a8641fcb8800c2e0694e185069929b9f8366450920591506b440b95ff6d931b818f6c8539c125a3032e493343c194fd5a506e7de9717fe8b43888d657d63bdfcee3bd19c3d021238c803899fa60d24e33b5c8263c434c44336a63a5b11713e9d748be5db1de927c7603757e127e912abd8fd42d3f9b6ddcb4ad2327462d1964cca8bbaa28ebc296a5de7746bdf516e82bf3065dc0c165457d3845258a6d819350495b2d3e3c303961eb8ff2460d9794845e5494f731c49940134a64fd5dd0a92958eb91ca13814d0c4ed78ec0004a581231438b66aa117291228abe2f585ccbf634e63516db435671f803ac3157005de9908284cf932fe373d18a8f0e0277312d5c806b4ab5f665153399fa7e1278f3d5a8eea451ed8c5d7d85fd4da9d70af5762b763caf15a1c0e7dae15779c285ceda55081391b4e35e224862593d219ec2d1835022a713958b709ccebbef3bd89e9d6111c6fe3a2e5c8d04e9485227396fe34fbacc317cb738451562f292a12ae42b106f7cccc1369990b95e5a84dad998e1c953177a40076759502d224128c2b9ea43c984ed1dfc09b6693dc2b4f1b8e399cb3dc1ceac3dbfad642a883023cea85913966618ae1648a35d4f4b292833e63449e42401fde88ccfb66100d462c9cee7ff211da2c09d26e453b499919936ee3493627ad430b2d9667016a6e94a65cc4c46a7d90235cf4a585bd552abcc841925b3b8b30b46e3cb7649c738f38228efd0747e7ebac029992b97c52654b26fd0cccae9e896c936025c1ccb9c4005ad5213ad1afcb13e97a60681bc50c0519bcf70a1c1dd77590a2b86788459a47f16a97909f78dc73770ae5de15a8364bcbc3e35b84a64e5e26d857daf71dbdfb5a55985579027e7a793593e393a33e22aa6959641305eb1227f43937b592886c52abd4eb294dfd22475b98e3a5b431ac678a2bb10f9967a02625250431087e5d13e4333f6c00add16799dd0f2943a1a68b032e7804d4ba4cdca1c2df58d927e122987c62091c633e33f5b133dda892b25ce8bfbfbb7520e77b4b6205a5cb0e79342f928f65420329477b4eb85c1d876368726573bd84d7d16c08351f793f935eb6fcc615d6d4472e7f6285f9d1727c6ef889301f3839c179a7da1159cf1ad7d3d4d576215e8152ec6fba631113dc69c03dcb95e9f8a06ea97f596dc9ce39658803a35eeda7880deda2eca4d81560dc0b80f943842fa54e81d78b2eb8285ee472368c3908e6c29a822421d6e434c8dac773a62e8237d5a99d2dff0fb1a805a08bc23bf9c737d53199a02e7f9ab6728433720fdefef05a79659f9798e8436fc634998a6bf77a0921268dec01a29e0ad821600066705cda322b0942b6a5fde571a61e0678d80ebd0908230757f6142129ac318509f59d04041249c505055e7d62698c7006d610f7572f6cb1a025124cff694152481f4a062a7435eb76759c4c8c60b0906bb8230e1addc5c45fa645e1b0543e1711ffbbec4468b207ac1ab1a20b54ff0e79a9412044469ed2fb5e1179d92e24be3dca770398f794f6f5ede03d79a7108771b1d886fa7a997b41cfddf52237610ebe1c029a8f89bb211554a488387298a3c21fb0d40c96909558920b2d0339e9ba249019ae390ffeb99b7cdab6d33a392de967400f7f024888ecbf1115b3033c948c52663a79e840bcb3cfbb1d20d631fd4ff50db0f3a745bf8a98c6edf330c72eae6f61b93ac97b93d6f55359ddaf76d3eda3906ae0e6ce2465a0818cb55ef155389602bc8e4757075e18b7269a264b321c00ef58f274f599a77127041754133c5891c58ca66c9e671e7f465a4bf7c027dd1f34182d9fb153816570f6f40e602cb2250039e5999424c738698471cde073df46a8173569c6db0076323ee137b5164db77c1a7acc560d77c9177401520fafa9413bac9e23154b274c6023399fcb301cf901099bfe0dda78f8fc8db75765d3a857e4bfc5cb79295cb13a7a8c654250f9d6cdbc7a326bc416eca002cbceb30f4529d9dc36d7ca90788174e5ff26af9a45764c0a9655f8d8dc8458c4b2af0a92d75f33a511554ad465cb3ecdfbd45eb2a5e91a2b74f51cd9983bf92bc2d32b3d4e034bab0479304e559e908305fa7add04b986404372cb39033d9a164695ddcabc884f72108b6d30239d4f8434e53527a7aa8a0c6536b86029bffb9228473df97462d41de9d5838d02f364d2240f2ff5046d4c611860c8bffecc35ba2550a2c447d14185bc09ae28bd59f2cd1969ae4d8ae010feab22a71f5dc4d3cdc5d424ef1393799ed5ba1f23e33cc93cece99bea6fb93f950b377abee6c80505f0cfd23acbaf5117fd54e5634e5bfa0a1f1208eee8dcd096706ac748005d7c64ae940dc7d72fcb849f142ca3fe936e72c84d4d521aea97dcbba3a74a099a8a8cffe0f45b52b09391413883eb161da7c38efe86989974d059c9f842a7b144f0e168349fc571ec2553d549f0de50ff478fe5ea0a24a0f106700e8f56ce22daeed89574087f7fa6b634673eb54980022c68aed03209c423a4cb53a81baec9e4b44f91bdae3006fcf61c9d661e4d9b342cb5a614a10a2d8fefd473c3ad56da07179083be44aca65bd15d999173aa933c1daa50258991f1cab2efa04cbaaa273c5584b6113f6016b8bdf79b8ce3e62fd28d540fcee28bdc640725b7ca1448d8fcb7571e30e91f5a6ba0b23fdded28858400f370178722f5fb70538ccf202e6beb26d81293227c3b651650ba9d8f2f3d61acade1fea2463b38971bae63298a6cb2ef4e84d9c8f21b2680d495cf2dae338cfcebcb3150ca238e2fa34173d5c6409bacf91eae00dc8c4a906e06f86a41ba571961e79634d0ddf4887f9f96d291e3da9aeac649266762213a347c88db941d3519249f661915bc24e82fcb0faabb0bb5ac30f83ca7f1e826e9828e7cc1bf480e710d66cdaf124e1bf8a214556ee67518131e4e7bd54527d50c3e1535cfc449e8f5a45768845093c60e9b9628c233c92bfc01747eb181a64d165776d0b22ee13418fbb1a172986b3a20a24dbe1165a637ad3e990f32a50ca8818493c062e1eb4c40a562ace5e5657cbe31424ec248815330ea675cc0a291ccdca40fcb5831a5b7fe5f2af6a52bad90ec2113d19569fdcc3ab1f045427462610610cc211b83e8cc62a3274dda7c07faecfe5a94a41686afca89513f265207470495e741b9aeae82690ea82c3c57d854766f0b825037652e87902eb676a0cff77cce229bbf0f5f9fed23aad95b55c13677ded6cf1be087137880e06bd0cf71b8ed39ea289a2329a98f48bc4c44d4c54061da9f21dbcb5776ae3bb7a4b7c0baae9ec94ece7ec8a7a8b80a19afea7da117b641964c94b9667717175754ac80d26002f3736e1482db3fc840a5da348cffe13308c22a43f4a11dcb9d0800af0b7c20d4640ec0b6cc2342af6490cf3017a6e518a198f1f2290b11ec50db7512961467c6fa9ec01e841a5b84fd1ae0abfa5e6992eff181899e142a1662e529f7a4b96c2aa121b97540aba5ef9cdd14346ad3c166c7bd18946a2fd8a2e6feea7e5238a2821dc0059b6078120b2f8cc6dfb65de819224593a4dd40cfa811a210f8073836ae4e1da694e72e342718e7dd355256323c11ba386bacc59ca502e401e3257f9c2e060ae047dcc4ab0325a6088708a6433a7fc00e2f0beb063b7578b381001a8a711a98a05dd2b591ecb05b464a073482db1fa11b49d2c68cf71825668a3ad87e2521673128cb306a815548d5e1ecbe66c0f74e426c55e1ecb0e12b48ad5e5b1a44b4280f10aea59d290e27059595e92088cd523fa2db62275624fff44773993fcd116cbde923274c58b0ab45e32c1546c034a158e6a5b30145ea503da7996d0e7ad34be5c6d5bb8cb595a4b27a6ce13003b19ce0bd01fe258834645ce122ae22511c5161d628bdcf0f801e33eb7bbde4ff37afda35e0ad72ffe8f269670e1bee17d834a7ffce22d3bc8de64cbbda54c29a5cd0683068306346421ef166a21168a21c68f6b70842ab696953f9439628c514a295329a47260be0ec373c498715877c55239b8fa67a91cecbf37dbdc5870dc8cb9c17f3e5755d908abfdb6b586bca687e8526ef69c150041507e45a66d69c2f9f29fdc04bed56ab558469eb59b80f06b3748e56b3fd3600ac71c2a9a48434b46488db33538f252fbe79459651a354e3885a8475388adc110f08f5262526ea80f3b15b4dfde533b50bf7d9cdae19f6ac76c55ffd9aafefd981fff07aa92406307744a6e668750ca47fde6eebb6973325bb1ea550c72a8e1004328575709872f0870021cb2f0828315e011c5ea2ae1d0831516ae6931238cc023acfc35e933c3a44fac4a9f137b29a7c4a4f499c9a86931238cc02b50c6cb74333191b24c37f33134326464cfbf71291c8ce2994c866518f3fa67c2180e4de09fd96233d8a20eafd96e2fc60c931ebb3119dbdd1d6bd9b1bbbdbb1ba63bef86114497c8ed5162324ab92b252633e93353aec6a1c2f82ea5e4dc93acb56024c7c86566d8cc30cf231789612d774a96f24377f795bbcdf5203d62188661f10a0cc364c4aec062160cc33029b12f24e65fb29c207ac430b98aa9ebc36b5ac76b5631468c433df699e3ac0ef73634d98732aa8e4dfe7ecaeb788b07c4f3693048bbc03630b02768ed058bfc30293b66e6f828c6e9be7df8f00192c2e19f0f12d02984da3239ce2eb2931455c8b0218a183728e151d49095e090430d798897542e7ac9e0f0030350a81728dde0440d99b5a2640316a6a2c5450e95f9aad790c3912d9d53dd38eb423ba523575653829c4471648601c81083f8d188961aa7a89186276a0cdaa1a3e1ec2a73d5abc94436654a1c894804612938e245415eb34a35b4acdcf080238c948ce0e086071021a186b0ea55cd902a90f88113c8c30fb6a042b3ba493ff052032b66c8c28a1c9470c08245d27ca6a25543a6a255b51857a8ff8186f3851c5c23d001ae1c1274d21a657cda3851a671c08fdfa790a08cfa55fd313f7bd487ab1b9c4a617ef6d9e4388e7bcfe9e7487051ae598eece79c9aa6693eab99fdcc661735962dd89666052e622b9b73c3c9d628616b74cd987b394aedd63e7b02c2809a00cdd7be0423ba757e3f4d7ae6633f7f9a842a28a002592cf505ffe2a5fac854f91a90f50522a06e7ed38b64585307f303bbc5c2d23ef38f88e6a9fce5664212babdf64a4680e021b2ad2781f918ca53f9d7333f95fcf9cbc23e55f62c60d9639f4a3ef6cb6221fbf9f3fbc1c01030f0e243abc5ca3e955c96f6bdd7f8d70196401419b1921c3c3d0a68c8454c1e1134ec22958bee927f0c8df3d3386110f971510a09aac373baaee1efd81917a7d59d3776c7766e5eed0760c188d3eddedcedd47d77fb6777f7c605c70884b8bbb179cec8cc3122f18845cfc89c25c6e8ede8e87883ab78459432fa17af702eb6a5a9dba0968d3916a4ec97de8e8ed7fc40c71b5cc5ff188966dcd238f1080c74a87119290284225b106088224e45b21079d2220376a40b1aaca06c60ca860021635ad18e6e51036ab5c56be3820a27451732c0be90a2b58ae08ea4b80a2353617e4f5061607e12a930325ca9300f238503a62b402af51bced4144d4d7d4c7d11414da5beb86a4aa9e5434dfdae004449b16d3475b3a9dbc7eda79724eaf67448ddbedbbc58aadb838db3495f0c31c56e4e0c153930e20b220b787531c4c58bae4ccd9ea8d9cf8c0b126c7183c5f12e1543610b2432cb22801a54fff954c593106d26af0ac39c161796640386618cdd4036d920a59472092c5a920829a1c54b1750aca0b224d960050d590d65862c0d01ac494fad17528d3f6fc4f5828b11473380410e929c205a22882d3ecf2cae1ae4e83a0d72185feaac68e8344eccbe307e3a4cb5205269542f6848690c61894ee188af56ab55e428c9155c38f1e2cac11169440a58b0558c5695115de934ceb2fc38c3801ad2744982290b23598a6accc2881a93862003e84ee6933927457129129465d1b8e0500336b9f81024776c119471118286a804e362c411d762cb0e2dba48cae504397486147772692207def1d4e4353bb9b84068c7109c3a224e4a337c914a32801758d440802050545da519ba0ce0896e759566d0a244b5ba4a3334e598814969062230f0efcf565d6f6babaea7daefbad5c398e83ed7908584d846ccaf8c671e388732f3f22fe77c42f40b79a96e128c2fe4275c2ca5eaf093da8c04887d3c8277016d905543a061ece91ccec2d33898098e3cb1836e6d79c2b21fa76c19f6d987e9c4784171c207c21996a19ef63bbd2bd49199b7f21ac6da6582262ac60082d228652c21b1262645998c22d98c61034f4e46f2d46234d1260a45050d5d028409456d44201ddd8004885ed24c9c4a07b93fb953739566299aaf30169723b99117c98d932104c4d0122a538e9870a9182ba80161f63c0df2c8fefb917d3c7aaef037ab071acefa537ff007e9d8c91a769e4a61fe8e6facc5afa381b6a22520e2487b47b96b8ff25ef02f3a2f1470a0d03930dfbfc5878109b30f39f662e488607edbfcd9f3ce87931f11a5edccac82f13ffcb587e1af3dc752f3b28f44f40b7a16cc47157fdd4cd7f46b2ba09d1419541745426713db3eaf4725102f3e225ea1eb6d6d6173767936085a4c4822b3f79af92808ec6f0efe52330cc3b22fdcefc06f7ce9c110eb8ed8bf2c22d4af07a3baa7ea9fb5bf393d100790e62b4b284ab08c208a96111c69ea8fd9dfb93144897bf8de434b69410d57df030dbb97122d980d70659a8ef10f7908480fb53044c92eaf0024862f08d78486eb14841fc837d72de58361c762a93868832cd4eaba2a46432ca12449c9fd8341dad81bfe06ec092ff8172f3bba55e5d780bde1da5e07ba4a4f05fff2e5cb97da5f03168c2ab8cb4f676f646dd6a921585986d2b0d3b0659f61588c40acd1b1280463be61e9004850595789071c146009edba4a3cdca0c30250578987293c18d5f07b8ba416282dedb3e4ea4a1e1ed7d5de4041c36deae17352da750f32ce32b8cfcfcf21013a122833aaf23659a0c7d688cf41a8dd3132a7a47b60d82cb4649bf99918638c3ed1fda36ec4b634616caaf2b7ce3e9084adc19f01fa518bd2b2d758d3b44c7bceb0d8621b581c32c5e2f45377b1a07c192f624af582f2851a477bf911091071282e699cf9dd854ccbf862c47c5e44bf229814d71f7b0b68b8efad103d1d373c14741a94361290e0cc1cf584f9974dcd999cc9005b835f3ec83632b7c1179996b49961a856a2cdec51dacc50dacc309436330cb54ccb0ff3a9df1ec59307745d4f4f4f4f4f4f4f99cc27e38bf18594c27ca1cdc77ddb3741d000314954326d8d661b315a557e7cb23596ee8dcb9f93d2ae7bf94bd953f9924a83724a95ccbb9bcdcfbc179c911a1c82ae8dec896d645f346a2eb6067ff1c9de7c91696fe44b25745a098c14c6bd029aa0303fe3e5c72316274eb1a07c39e38b465acc17dad0afd5cd2902a500e8ba19f12bcc179960d080c6f7efcf8be44fda715e94050909090909098983268a6addfc4c3ec641937e554013348c46ad2a3f1a21b9965e4c325b63a590b115e4394258f2a390e74496fca8240e7d2c8a4d362858edc7dffd5412908f3d0b8e7d404d7a16c85f7eaafd88b25f96fc653dfb11ed9837a605195e0bfa91f7145846c0c2753ab51ba10723f480668b369a9d95820842d0d09f78dcc96b4ad8010dbb656f1844a914e60ed8c5485cb4af255751d1f217c3021abad04e7b933b6571269dd76aa99b406f2667a55ec8012b21d085804cda096d8dd49c9476dd3fd8af55bffa1583d7c407e235d1998aa6a061bfac6c4e347a6d0dfe4e0ba5c2688a1a7f33f21c9a45a3ae24ac3cc799bc26fe9c9476dd3f08ae564c948a100410cfe9d7cbc90926323de938adb2803abfea406aa30353e3529eb3d8a1a01da0159b6c036324f6263e17f109ce4a7ddc44ddc87368c450a1fe5da917a35517fbe411f5efcf3e9992c0b252bf291eee1909fb908b7060518a28d3aa5cb492de6bf7b51d0150824d6e1ba7fa63f4f7e132fe7cb84ac21094fbc17ddcc17d5c887dfc2020e5730a07c7d40a42eac6defc622a05d48ed3e9612e770648ccc004b57db8da334002864bf5a76385d7c2b362df2f5f074f8fd4549ae6c3473e906725bc447e10b94d44aa0c2212878e704b57ba90daccbc165a537056290456aafc1b8db33ea8f25dd8c1098b949ec2812282c68a83bd0c8e2ca502f6330d764464fe3958056eb509c12a69c815133190f27584a046d91648849478108251015d25212521185816ba94e2b81e68ff2a0931a9d8af6f18c6428da3d2fe3114e771d8dca67c2e224a3db37e98c5bd4c0ac7fc28edbec33e1b50b56a50feb2745637b06f6713e1075a7fa0d8739dcf2d240d2a69500a3528352bf46bc8462b603a66a6d0551212aad86fbcc92859888d3c466a50be00aa501551ea97c53dcae3e9d1625640e3b1044d793dbc463ee7f98842c36daa12ca12757e412410277655f9233f062168b84ffb1d7d7808fb9529283666a6d99b1eb21f7d4e4add6d36a45bb338dcddbf0f40b32bd95a93d2676262626abd8c3c92e80ae1fcffff9f3c74f0c0e44f791a696664907037604c1d9fe051860e51c60892cd33e3d91926a349a49991e1646a68e641c9b0e2bb86a780523495d93c681f3a7662f849229c4f413334b403ddb523c4483bce42317ef3056f194e345c97cf8ecbe572b9a490cf8da741973b0d6e41c3e9e2a01df6666f76d9fc4960760d13dbb922ac18f4536d1f4f8ca7458f025ac40202e35371d1def4c3c0dc97d0033ae39391c105c5d88a3e1e0eaa28d58e50fbed53ede0df9e5f48155277baeba672506fcbc2c9c582f196a5793cd4200a6c834bb027f433d1fcb888461ba59a4341d3aa54f854fecc04658287ca5f68233343433a8310ff1ef9df4f93fd7afce3f7f32384a948912d32d4a7715a1c0edebdc0daa06117b64610ae2964ebc2deecd3b08d0bec09fbfb855bc57ef1eb582816317fa78f7eaacf1f3e5ba54a75ff1117ebae52e5a8ca67cfbe3b85a3b32cee0fecb97e347aae8ee8578e4a49daf863588f9f3f9583ab0f4c642a476318b61206dd1b8975330deeeeeeee763b4de7d90467409b06411bcf46468ee338ce9b7783a1eae158f2632592df0f0858d46b38994a01e3662a076bac699a37350fb5715b2a7aa738b5198cf0054fe3540ecea3810395ca81550e47f584ea0ed55d9f7dbdbe70a91c5db9bd49e5f0caed8d4771c0487130606052746fd893c1d1e58895c66c31a8989898182d26262666c6c46431588c8c311ed3311c7302bfd8c50cf38446514336e2a1d496cd07206088edb3623cab8028a80ba7a7713819266e18b8f2689c6f4229527f884151a4d07c94204f9e2821c5b69cb9a0092b4840b97b70c5513061b27df49db3bbce93a06d65a3fb1d25879928534cf041fb2c15666ef6d2ddfbab06757a1ba73b7e389d7a7737b66f9ba0057dbd48e04e9d3a75ead4977a0f9c301a5a52d42038a345431eda6716d72747c4a7c9556471c2a589cb15c58997902926f880fa2c158ef254c3a74bd35788c0fbeb4a297d77e3faee7e3dfb294a09ba27ed966977386b0e58e065e7c60c1d68b82e1d3a74789d0e28ee2e82b8bbbbbb5f5ca675ce3f7ae156d9dd940b3c4629dbc4141d9d0f596989a61a3e85120528dddddd1d73772cce06fdf7c6bb2c8d954d83fbdd206f3bb3733132d6e43b8e8a7083b92ba0fc73ce39a70735f161cb8a4667db2fbf95ced3689075a46c273c8912585c477614bfbb45a8abe4a40b126c9c34dc3e3831738f06f97d70f5f1e17e0f1f3e5cd0d91b7eae09ede183db6edab4695d274772024ce5e70f003f9290cd86ab2c450a17b91e9888a0438cbcb8ffc18ccf04e59e8d836dd7e5f4fef7864a7bd8a250d73d46667e0c8b8286ab1d5f89c2737431f624185058517612d0fdd5f2036b4728818222e2729520690853e4c95a8aa281aeb866239818824a08945c69024915282aa65eb3cfde8c42775775f7bb068ba06b43164e51fc553d9fe09947d2753c14c5a1788009e7c46c2b3498f1a8c094ca39ee72a171f6092447955f5d7a340eadccad2a4f20d95243bea18519ba204de1691c6e41e5dff53929f51b0d12cd5fc5b430c405f2573ea2418dd3534087a2108edc5d623724e6ee2ee5638ca57030f67247e28e64ce22a8ff7f838d6152a83f867dc488e66373ce1857f06a7a31755d47b4f1f4c47f148ac79c3c3c5e3d0db644794124cfc67ddb1666af6def1e797ad9164476332fc398962512ebe1c2d7e8f9e8ae9bf3eb71a3c5850665d6c37d9cab2138d3a0fc3ace5eca28d4ff553bfcb3cf9a48fbe8691a57d983cb85246bec065b92b610996982020a8dd31f3d09e5e6666460cd38a9410d6d666823115aad5641336040436e794f31826469fe6ab55aedcdaaae68cc0f5ce980ab07e90c82814fc4cb781a3df6b71f44dd403f05ada599a56e0a0dbb89441123468e8a1c0d79e9ffd93ec8d6e0efe7c0720ed0a2096d203e9e0f8fc70edbe8a0da3bd9cf148e2835ef9be1ec695a8a086ac842362d90866090bd41816d70604fe86f4d0b7a544316a2c12d579cdc9091274f505f3c61c26408131f5e0d2ecc0be8d72a3289155ee136c5cdc1beae03f766569bc69993c77baffb81c61ccf1e11f68bbd7fcd7a57b520e857285f549957a50d7612342b92e4430d509200a19ad0cb01326308fd979ca67d7326314b46d33c63b5d7467be34e360b1115435328b122d4519efc3d07e6b51ffddb6bbfed0dba7dabd0bf793d1af427a18924a954654b65a52a5dbad544b0df9e7a305ebb521e6aa2e69ca86ddb8c3a339a935277327ad0c948a7ce18427b66764e7376dd3f08ae56298f480af56d3427a55df7afad3414aa7bd25ce9289bb398b795ea0d45f57602c6f64e1fc6c873603c877eeb35309e4e08528db37d1e6e40088c944783609bf6dded2814aa67f6bd4429c95dbaa687e74c963b35e8de46d597696ffadd488605fdfcdb8c21df0f9056cb63f3c72bbc6373de6b74f0146b65bef6bc537fec07c16ac45e9b3888b26d93d8172e5663cc767fdf3518d03ae3ea5cd1073dcca3c10e2af47ba401bf0f08743ffcb8bfa908ec77e353edf874b8c2d54cbc22290906f47f65a48c0da5734257980ebf12164b4cc2201ec64fa741ef874c413ea603f641bc4af9d9476fbf5583197ff9f2e54b10af1c0601f9e964afe3ada47f343a67fe8d95ce8e4605c90674ca84fcdd395b4d7ff71678b6c68ceacf23dc205477ff95e76c1f2b995f94b287c7c7f341a1fd27a8d28b51a5b74b489654e96d7553bb37b2ab52feacf263ca5ddce3c6dcf3afb6c68ccaafb33524144e705f0cdbb75e83fa580814a2a4fee8f7d98c6be6bf59669dab95484a438ab817b2f5fc0889939b3b78da41465da52737d84149294992ec411ba4f7424370b9719669869716297a9c148df3ae55f73abc867f7d76097c2574a7174e15fc4b5d50872ff9cb2964ca2ed5ddddf1812f35fc69d5fdb80abae8d75daa1493d1578809932f5f5adedddddd9ff6a8466d956e8f8d5a4dfbeeb92f1eca300c23e6a373c2501a44560d45436b9e0685a0daf7a980a23e5c0939aa3fb42851da2729d79e5909b51feb2fd4deb5cdb25578f36db291826a2d8b14b8fa2f14285d52529ea4a43cd12289249ee8d2c4d2d258c9501aee2bba0e349c759bb33f206ba571c23929dd710ffc81f21361cfafcad1771f6c20e857a41454f9cde840c30eabc293199c407a6bcca8b1f6f311b5e75c7d50fa7df40616e7c7829da385ac038a66a95dcd7c41ff8e42b9621f0cf10b7d364b8dcf9a4fa4b1eb86b74383d0b75016640213c8c8ec1eb19732945232b38c531e716c7759d37380809a00654f81eca380fcaa3032fae7de70f4ecb47f2b30b30068a0fb11e8df0f05546a0876529c862a55765f3c7834b83c1adce769707757fe569b06b7f31aafd9d75e40bfbf705d2b2846825acccc2c9955df2da9115d59798286da5f1bb1f7d0f323a4e74748ef7e1c04ef0a614dec8b086145212cd5673df1e5f7133beff66e2a5c5b2af69bc4e54a9dbfbf836b2583515ad92589b667ec53fdf601a558d83ef5a90f889b5fa722807d60830cce5feda7170aa9a1f6a86deaeaeeee0b3f2108e67ae033f13fb29fff237b08ec6826557e33a9f2db3ef3683a677b17ba66f6d042c37dcd6c57270e7a784a32425d2514e4b0bcdb02ca7533b804e599066bd720af409edf1f00196cc006a7084f34039bd3c2fad81a5cf931b038d10651962455dea6ba5d2a7f0536c747f658d095e734c19e2bf77eac2d53d897419ca05ca5122ace2dd719f639b22f7bc9adfcdc23f27cba867b40df091535d4910197989d6de1bdd2c44e287f051a87824ddc6ecb8f908406a8fda1571b209f90f168b0f7b5e2b1e113da3d1a0dead8b08d04ec09fd08c09e5731a0a83a27a5fc3d8f5461bfac9edec67e56ece3b9c1b59253583e494a8202a5698b1042114350ba326504fdbbbbaca40b13264aaa50a7d6feb033298defef1f90d7ac8a05195f7ecbda9517d2ee419d9d16dd8dceb4caa2a32d550175a13852523718fe905570a4a4822e5e18cc0b6630fe19ce9959b675f78f20723d7cecb7e6b01f52ca7010ab7f22848c0f50647c80a2a4944489a5244bfe5c8f80c2748416a10a0ae84f038b653f9ffd1f3e8ba5fafbecfba781c552fb53450985727f74f78695800285092682824aa8abb484117fc1822d4f9858efa44b19dd37c77f7fd0cdd05ed6c90cea1ae999f891e5ade195df8ddce539f15f04959f1da9b22fb514d82f5569d506d45daa61a8ee60e4853f7b8b880921a0404dfa81e2cf0f9bc90b1afafcecbde51ee439254041819af4641f1f0828eb57f64c2a16a26abf9f263dd8c7e79ef8fdfc3451f1c49fc0b2a2b601c58f5bcf3df60179150bfef1e30784bdefa77e59d293f1531fd0fc38bf75a075e2b2f1901050931eedb1073202d23e36f21a1ed201ea63232ed8a7da4f4581f9d8f764af7d3f4df6b517c2eaaf67fea878522f84c57d13150f50931ed473a5c04f10b3b09f8159d803a154fbc1d0335ffb7e7a780dff0496e5e3e535ec427db84da9cfc96bb2af5b5e93cd07dabe16c21ea5f1872d5419fb80fce7a7daeccb1e7b1632ec81e4671f0b1890d7ecd74b610fa528e0ef4abc865b3ee42da45ef29ceea55e1687def21a969605fd1a7aab72f7521f35c84b6dd420ff26a49b0a4d71bd76f77f47c78d0697bf30e2a8fa57c6f622e8462d647e75ec34b87fa5f2f7f2224992224746a4808179b98290b06dc9d6d86609d6060a3bbf79a8a75e5f714531c462c2a47154dc6fddfe432ec291eacd9be12f64241aff16c2c9698855a4c4941a32d2875cc4b193cd0940d3efa618bf947ecca7da3ee637a0dba8440ff6243154340300000aa314000020100c064422b150389e29d2247b14000a73864c805234944643591224318c428818420c308400600c101a1ada2600cfb45151019851a3f4ca9642e3cbc9e9e230c410bddca5922151eb6290133dc8040710390dfa9d5f2ee64468b80d7bbbf9b50c6a5f8db9db7bc1bcdb5b99a8ab1417d21efb8839dd7eef530afce31d0b0d48aba7fd31c98afc201dd4ad87cf68586ae87da0b73c0051f036bce1c9f10bb9d9ca75d0a548cfc2f04b21e143bb30cbabc158702965b9984b789d054b0fe099a6802f25f61e62abbcaea765bbdc3e6cbcc26b135de1d173f2bf57e8fc0fd30dfe28c902297d465cd2c6071cc1711a58b54da2af8dcd35eff98c8983a40eba74339db4b835795e46ed6de5bf7680a20161c46f356cd46ec6ace0e307005ee094ef35939c959e05e7502ed346a00d948eb664b84ea0fd76602d2c4df7c0c0e85432409a2e4ff07d3efac79862deae4ae6633d0f8980bedf9e1d6d4f3f185b9bec6e75b0a20ac2292149b94301ff592d1152560019f0896466a9bc403f9a3a599624420d5414ebcbb4642be8297680a4394efa251dbb909bfb856a465519e302c1d58c73c26f1cd072508acb8e4f6c0de5976b654bc022c0bf059154ec50115f6a43a7fe5d5c351cc885a1c8a568492fc6688f56678ff62d926b0cd78a9db13e0437b320f81ae1533d5d9f233600ba074ebb18800db48727f031e718641a090c8c3051827e9e649f29567b897d1d03dbb5b1bda355624c9becb19a793a07af76844b36b903f3eed7d13bc65189837eb28f5a6ff06a5ae00af533206d9a0b10e636db6ea0df0585da3706480490c8821838ff5b65cac4af862f75ab69fb17df951268f7ace8eca935978cab224883c18b96d60c48f77c51cbd60b0d162becee62040fe7e8f281813cd569aee986fa4a2abdcd66322ea8891ed04b10f5ace5e7bdad198ddafd2634dac9fcbd12c25e01de377a1280ad60b6d78717668c492518025b6363f5666e28537bc547ce8f0b61814684452c48ad1b77e60a60f81578d9710730185fcd868486b2977a5b38bab058db322207ff6c23a064ce0472356f9fb72e5d0769ce2d49b3690636f2adb9e9937e03fbeef0207049a13003c28153d0762e5eb25e380af784f8966daf63e9870d52d2787c02fac858b4c47d6105379ae144801d963e187d1cddfca6730e8bdc4a816e28752552286f7d182c57429cfdc376ef3d673a9e8e43080a275dba9512541f6c2c8bdb9bc08e030479e366609660917d9dc516f60c1694faba7be3b49eed38f89322139d6bbf14204b46ed3c8c0477000be3d3e3a8529c1983019e5b87b7362baee849a4e7bc9e5798c2979f3909288113db26d78ad91258c30585b7fd390efa1a9ae0ca5af938e648e1a06021a961ea51a796e5c29a0eda16a72c8b298aad7af5badfb3dba5fd94f5da8ee00ea751e8675a6180e552e03d9f01cd087876a7bcaa3266fc40f652ff305dc5cb87b22b7f6f32c7d0a69ed5d56baf6924935c72733ec42e7a5342072a5e0c7977645060151b4ebf484ef608807c17baae0cce493f20bd65f20f3d00cd5857f5584a05863760897937d0b04f8ea5d85cf263fe26341e0d0896fcd69d58dbb6ff00514d4cb0ad7cdeaa005bde134efe0862e7ae15dfab73e63d81905206e6363ba1a3475e15bc721bc90e92568481d88b641266488435d6d3dde216eed4fdf199359e04c5ae0dbdf5c3a50de72f2f452cba382b003b584abaab53659757b51f6ccebe345a9ee57ac148f806c0f7a4b386e32a765651a45d14b7405708a8d8c7114db2b1e13cca7e9ed21d465256c6c1a6d0143bbdb3600287fac95b9d13b4cc504ab3e4e5d97d5fd028ae8a92557592f2012afe7990db1b9ee1bca96ede4d3c4bbdec8c979ce08569112729833c93d108d91b7e0212ff9f2e484f03d297edf234fbb4f1833a5628b9a1add0ce938bf5b50b1399b8341c17ef00520c6a10bcdb878826bac303a286e00be168961dcc83115f5d1eff3521c85569ea590cb4da5eff5c2ccff80546487f15e8d6dedf2a81c60aa8e103466dc75bf52c691f731dbbeb9044ffe12da857b36d23007136e7b24f1d3db28db579c0470ca200692ac2a3bbb79abf16aa53d97c3d4b2dcff097bf68563d9dd21be546e641da3c60689db8417fa5fc6aead8d4484fea1c55ab3a2b24b851752fcd4e74ce7473d036ef03a3df503c2947de0983e82648e7c9685b37f8d246ec786157689df0362f91b6887737efb7f4acea4707cae198add6e9e176aae46c956fd416bdf44e8d4ef0c1415bd8e305e8c3b52603186061dac74f078d86f0cefad81b6524dc9e89cde71bcf73ad0db24c805349958a17922452adefce8cdddaa0eb1f553b006e6d8825a41760241c8f45e6805cf5aebb45000187fc99f2c91879aedafd7a8eaeab9db19ae57a65fb9ace508b8253a279d96fdd3dfdcc9ddfa85e0a7fb57112417d9e741625f337ce2a6569362e8f87e159952ccf799783c830d5199443e66c30e0fc95acb1b431a30ae451a56c94e98039899d9f2ea24598cc7b3f6d363a5b6248425be3c213165574a24b8c867a3dc4b43643410a826568a0e1891131b22f42006136368ad666e5d429a28e46473f157e98374559d29223b4a5314c4072e1a8b0080c1c1073a484f09504e8f30264ab5c5c1edc05d8e2f9fbcd4b86be5b6774dbf3bf3c10c51705cde0ccfe45f19c056288801e5768da31fb4d112fb4a0a827164d33526bad5c4a38d966731f36d792f3636c50267a092c5f52150b665896f1941f2d2a6aea15033805ea2465fafdfa94d2450d4acfb45e6c91bc48c2807b50da164cca7d848e8211656ab39d15febff3678c214ae11201b4beb9ae8b5f075b4e17a0b08f1ecef2dc53624743e20eaf33da156f216532f64a4dfda7e2f98be5241c96426e9a4dfc500dce2f202e276d59ed82002528555407d9a8b216d7db0e4c79621cc76579e6cc7ac2e8ddbdf2c1aaf8f263f0002b0410a50cdda8eaee3fe413c1d5ff6c8b55666e6de9947c21b952d650f112abb16af715c9b3525a59415724ac4fd808fdef64de05d91f43a10a36da60d2e847e3694f82686c174a00c84de0207f9ba02d20b1e73dd60fd645372245041c0be35f37a46cc77b83240f882950a6912e1be9de86b355c99d87d880cbeea3426c06ec937927c65f4edf5ed52dc224a3d280714ecb67490569c92db668bb78c7ce42e41e16f28089611362e5ae459d1f105d11947dee239fcc91350de0e42946e0f202308a80445749decebaa4a7c13f634acf318b3b0a74327ef2f7890a40162969c29ed09152804966498ada0e7cc6c2d3c6345647c6f8ec41093c7e30b0022ae3111e80efeae238fd5eb426c3c2cf35f81fcf370fb93155754fe8763d7d32880ce330beefeacc904feb219491e1845e96a950e2523a9e6f5229aa90132d071b8b1e9cba1e29d56fdaca15c0b940731a31176ffcf6f9c3ace50877b5f3e2eead764a2b447442af776799df1092c83f7bf9b561783321e18ff9c82fa043b7067a06a1a7cbeb3a534bf9424a8cd89ebfd375ddab26c0a932800dd0000d53d4ccafc649d8ebcce4a54fad78fbed1f4616ca5b16100def77b8107b14ab05fcfac65f23d81abd3e9a2ce180e3c10bf60352d6b1b460857c52c3210baee9715b94b39ecd3c97e95f5e3dd2d7babb1e6c4eec7fed7c6fa55d5c8b5d615314ef9e36c6c34427e5a7e8fafbc99343030489ba20af84c7c3fae2cff247a234afa1f9592ded8203578b1f80eff5c3a07be6d3c0a94131fb4c10571c8379d0d422eb743a993848abd36accd3ed0629e262279b3950f20061cf3d99d8afb7c0918cf1a045cbb793b1608205c52ab32cb6a89361319f3bbd6b1fb330595552ae19954b7fca5433bc9e413ef398e41ac00e08c220f8b58eea709e4b90f8829b4287a2c770dedff1278a6ec97d1900e3c3e84e35db91841e11c3693f44e867f51c2188968a1a79d24aa8e41ea4ec0b9e6c9080922e1b75d8d3f00dd21cb468cc23204180659cfe07812fa7b171800367e00fae60590120ffba117dbaff94556f5f7dbdf5fd126e3cb1fef00c8893c4f60138f6863fc4278f089e247add297f86dcf4aa5fb0156196639901e183074eda75efe1c40b5a87f289f2e6a21c6213a8083369a1f835b74d6c8741eff024e7f1852c0ed554881327236c7b04e18d495a8e99cb22289207518d8d36e1b0f98522f4b376f0b30cdb1bb326f77ebd949bf354d052a1e52dabb3d0846d2f12ec85e44b32afdc50b8ef43e4661ee4f8189009517b98033f8fba3e58155fa77605fef0a0a04d5ae9224236d0c5228c695a48a67eb09d0c5a998bd281da0115c50d438f19331b42e51210506418e64064d11d10700c5bf87aed1e270e2b65052e98ddb6caa101c20b1a9f57c880435a6cf0a3ffea5c83694f2bdf6caf32e183a2b344ed92d9a54fbbc2d605a78edbc3bfa02b57da010fd9725b69f3863d0a28fa9f17ae66acfe5240d072e48bef1426f375c6d6875d0ce3c044eec0daf61dcab54aa04cd44f97fba40d4d88f30ccd08f26d24db57050c640687a9a199ccb1c2fe058325a4b9b43b3860c6abb49a6f85d4f6751f25fde8eef2241c31844c4bc36a2fa1f3e5867929f37684c1d14abf247ff53b1edc8507c0ff76464b68db3c12c3b5889fcfec2a47d0a35185a9615f8c8be63c95a7aff06f8f3318e5a2141746d8d41363251c386f09f4daa7a391f2e4c1a82583f4cbc73e9d5de5271786e73b2e277bb63d4a27633c902c7810eca6c2db134463b2ef38b21749c708e2d949157d97a645b792db50f23e0969064bba67dcb2ceb7d5aae4dbd60aa3097d979d361341146a7d343b42e51dbbebaa4eadbc21ce0f2815097cb0508c9e3ed6d8fb692d466bfc1edf1b2758e11f00acb397041d9f910aa401c476738145d8e2da15821c87acc3d95589fd293beca5bb9fcd1eea4cbec0db7de335e5fb5249b89a1688d9c40dd884bda315a1e55bfcc5bfb9370a17476765596d25dda2f889b8bc66868d209fc8638e415f0cada750ea232c6a793aa64e38c0d49a7412fe9948f31f867a0e98c026ba719eecd328b73c1f954f0b63085971cc1823be1ef9148c41c38777fa21b1ed374064aac6778a8a9e3dab4755928af5025237ff98b89661366bd702e2f70be7d67a4608828d86154271f9349b02751e0030a4711abdb4799d80bc9b34527baa6a58de8a1bff5e7fcbb71c505560c6e70bcf5c58642045794b7e85fccf924caf469f2e83dbd4dce90588ead3606c12afd46c1b6587ac8aecef66136370eaac6fbf9142d344448c9fa7f29a44320d8f3655847c06feef6d65a2a26bdc25cb2081eb7f98bb3f9feab89c6bee2c457c315e5d4b0c80c5e94b94ce295a31990d6a18b9b4fa550c82f1490a437ce3c90f3ddaca51e892411f7a4eb3648cd332b2f81a68b7a60a87879997d748887d5dd3a6a6578a98e8bc3eeb78b9e40544a4dfbbe2903ec7bc879f51b00e33e5b59defa5adc3aa39e668aadfc08b42daa5209dab4b9dba5d98c6a3f12e00a3c5b455d1506e110050a15f135e06790fb49cc5259a9eb616e600c4d27a279d6ec272f4b73b5a4f8165c2350523a1e0aa69ffff57e54147216c7ee780a0072513c430a29a1ec5ca7cf4ebb8e4595162380a0f77861316828ca47374cd61e3393b36e40f7c9e5111ac710ccf0bd6b0815061820adee301768752f0a759cb219343416fabc042357fd143dda81774a865dba41904349eda712121c5ded7ef5fcd131952c3d016775cb5b61593625aba23d041568d31cc7d4132f8fd644770a4a5fc9deb28d073f225a8ee7534701ba3d240c7a6605cbfd8155b27fc6799a4e1239e29cf110db55ce5e20b38c33b3adb49987dac8a5b877f7d067cbf721d7073879347270a528733971e87ef318d3386de7a77a87a212648f2fd6778de2988508bd9f4f2d99ef9a3f461229aa1d3aeb46581ec0f2ffe2ceb08878dd9c599a5b25fd13a57e698c332135f3707b5b9d64a0da08eae159bc9d605b683f6802bcfef7fab771cad584cae63fdbf9521db0e066432d8e7f17685e5f6fe92abe10fdde2151d0d2321c2a8148d10ff4138f63fb9b1c862c5f2afa6221472d14db06f9770028c9007c426e18658c24f6f4cbd06fbd1a5182c920c6cc4a2a2631fa4ddabd21e786ccb3215e87f24dc2c3159c693afca6416cb3168fb200029b728a23549635c8a8943b69ea89a113f57a448ac548c1dfbc747c8b75d888451a660a47e1c9278fbc5dbab6e843918d788c66ec634af895925d9522e9a060463197d5d80613841827bcc22d714c8643ddd12991da40d766314511d420743b59187815e1b06e63caacb6f673a944b781ae9d95cce0a684611d70e86428c421fa6134e00efab882b4e6e468b2018f6d59be986a198225ad01d20ae21a42d6006f5cd7ae06f67edd443201c274e99e0099bbc47c022ba01c6d8969872a5183c58397558e008ada6ee4ea3e4b80d9285eb390111fbd2a773d5e487d08a0748b5848240edae13de2db1dd3f0a7d01d8a962217a92003db2046e706ad27962f9f8fbc141aef1caaad7de40778b8b757bca3c4d340befdd50aed43b7ac2e1613bb337982884b47f75ba9ea8fdcb1968f939b2ab90f8ddedbbe17d3f345dca2921695425eba9d61b48702f17395e32d24263299ea9a35de8c49fd6796ce5342f1a2d26300d90dc789e3679f4109e7d0fa9963be5cb048786ff5559c5596e040290f1eb6ad1f4df3f93a01dbcf4855514919c2cfb174cc4f03a1aa3dc2a1dede485b16ac3a9749b9420761dd2d70ceaf04f92daff4be6f03834cc0eff37b38177a171804000d5573e2b4ce5998fb296070879f9f4571e61c2ed78598559c5b217c3c7866ce52e5998655c679f43ecfbcd055ac48a81af2ba7d151d295eeb933caa58c9f508c9f86ed675ba20b5cc9e7352208f939a103ca2f164e658dd01fcca7524e5e963ec9ae2517be1f69ba3ccbe8df1f65e565197ff9552f1428b0ee1445f01177f28af03ca6f3434c120f04154990a0f91f1090e9a323f0bae498b91dca3f14eeca78f210d96303343ef4235037418a9f7fdd72cccfe850a6e4c47ca23e4eeb9b6cee1f83647781cce9277b4f41dc6b7793e2b27c4c8c186688da8d4ee1f81f44245a10577440406ed496edfb96ca23c636f75489334a81ea58c438270d60dc8b61b5fa2f0dc756fe9593e623bfcb8ccd5516c906f9672c51cd31cc452606ac662ac1fe2d65c18798f40895d1016b98bad17745099d8645c16016153c11673d1cc2d05d083b08061e66a5a25ca6c07bf782200aa82f36d6e282f4d6bb036938e819df0247060f27a08e80b0fb048c7aea39ef25874af96b7cca606731d2315ef4ffae78280016124b092bf0e00c6d94c98ffc9a9ee11b02bfa403c2a9896375a501464eefe342d519541cb33d807b0ac8036d5f6ccf85f09cd8f79ade7570b7bce9641edd35654c38c3cec3b67022c40152d6d00e8d340025f95bd64b0132ab2925c7929cc692b5fec458a2fdc3817b24876958cfcac8cd4d3afcf79de99652ae9719763e3c4ca6947711581a25062f8bab47dfc0b20c171db507f67df9a22f28ac5ef24552535891fe7fbea4ef15242b2c4ef31df3494dcd720c088bd2cca466198a44f03c2f4dcd9a782b3434cb044e5aaa0f7512ef3dd32d9537e098b55dd33c18de4f24f73f7af927bba3c7915e1fc93a38a5b9f9e993c1c9a17b257b86bf3077a547bdc40fc9474384b0b50d912523add5ecb54ab1fc1e48b7154a381f5203b700616d3a34796103d0c49eaa2864ec9e4b7203bb0a3a100bba733d18e1155619b09619ce756db14e6384d23c2b4764a401285b2cf1bfb32888a1077d0de9f047a9cad26f6fdda22c65b947c521f247367586ba46ce3a5c1ca6091c677e56a5e0b9af919179a6cf9366722dc779ad4458d5adf40f6f0671a3f78d54a2631a6abaa489f9603a82f4b31bf70738f1e64bace59ceae66640f46b6e3f25dc051177594cddb84063883d6c6e3ab2aa3e2f5bddb894a16b25c95970a630e6f2fe42b5c1f11901273876310693ca6e19ed77602a3842f1b2069c01bcb13b16ed80d0342bf3de0b05662dcd10da1f5092a38c5894639a0db6a0e62058b00c53abd56811f7cecf3927fbaac1804853a478ee0d1b52121110e52ab8f2ba79771c864de8a3526f5430ba4ee8decdb81d60bdea0181b56d91a6ffbbb4bf0442f1926b3e8e1e15e71005e8f1a20e27e35ea76cc162497604dfd9d0ed3b542ba8ead33dcd7b830e3ce1b12fd1f0353f1f45eea6d62a423502b9b364ffa6b92dc05bfd3ab1e0deaa79a2e42ecffc9d57647f1f3e01ff4a3a7499f8d863ec7a0593a0ea4d94efdfc5fbc38c996df201f174f0b32ff6e8af22446eb9c2002d4a7b30ac8c6c5d77052bba7ca078af639a39f8a470870e57bacdf51018363cff4233633e5490930d98ae9471750e915d0a456e7f60a3f6c646933333c5eec11aa345457c8517bbd13acadd4bc2773c03ec7652e80425c68e8ab97a357ee35312ed0474bdb5a1539cb22d8e10a87c70eefd8b9f26994e8b3a14fadf39450459e4ad2136e58c5bdd44a861db1d8892c10e1523774f8321dca50264f6fb5fc4578418546975e68332dc42d884e5965be52bc45272020dde40fa65a0f67add422dd70d25d3ed54a14c5618b7ad30beaeaf6d54b9cc6631ba176a3e7ae4c6d4f70a8e92f2b40493da63b7c37a98861d90f29a462a30f5e2c4a7ab9699b0d104b1aa8279e87831e51ee3bfafc1d3358cb9ea7e3694269954ec3e8705d42703d664722059c47c1bf837894ed15e247e23dd745e3d8397cb728204482bdb5c614de4692adef7110af2958dde7144e0d5fa696fed12354895d931765794e2e4c16eb89b5b2265444bd152914f62cea7c9330dcb37f9a0e32aa0a9ff651562924fee39bc24eef1e5189f7bf3efb3fa6ff27c0009890f2db33c298a22c0cf419e576a058afefc0a8fb3f9078cf344c58f8b063a683f833d2d2fbb057f0982b88a87d2b3155cc32223d1893dda8a207f46777e33bae1f58b6973325fb613fe57e8001bff08f219cdb20a9314de5bbea6225b1e781aaf796cef47c3e826c414ca71f0f85da08b7fdce7fcd7613823103f6a17f4a38a87e144163fd8aa5bfb6230ee684210bd70f3867ee4154505b74f00c62aba397d81e42bb492dbbe697de556dd1d33df7de63ad51e02ef888121854a850f93d9a222629591e19ffe309bdb1b9b9fd3eb25dc1df0e8491c19cd6ff40f2b2da332896c368eaaf581c83e8bffbe8416c86c6a6c34de30966865e927895d4b46b931f826cc11b94828737435dafa4f9558dd97b6fb0dbdeb7b71397c106de0b04edc022cf3dafde623e9f5481a6b7502697268fd49fbaa4fdce873991b553d36616614ad0a0ed37805522e4af6a952c397cb54294f4480fd3a06655c15d88f1f8c2612bc696f874a128f4ff25468d726ece0a80468e08a2bb16f9ec681365690b7461db8ffb4445adddfe973b19bad28fef28d812069e3c9a07c36698ac665c9a4f77c57fb7670f9279a8b6fb030739b53fadd4622d9323fe0254f06bb74c23982b0e2525db8b8d0a370cbb062570a2aa64c15210ba0bf5a19052dcb86ec6cd2936e927a013a45fb6914a3318c59041ef1d5ac6aa08b39c0d5bfb5cbc28ac0e7da844b0ccf03db957900f1952c60ccb413f53d801c15bec5d7678fc9f496bbf36cd20cd8068dba111307d93428b1ce410b3e2cdfdf5493d43a0b3a6881b0ee411a0b16e3a4e65350cdbbec972903ef25d18c8470b3b594044766593d887b211aebff1cfe9e2f022a56ec4fd6573a04938c9cc554569f3e8e8d391a8defc44bb3098a63dcc9595f43d73da9e0f41554fc9d7ddc23f1ca6bdfc60cd411ba7d8bc2c4ad05beb66e6eac530df71d59287b7ab247e52d74d27a61ce98a4aed40ade9eaafdc282a8e967e24043f299b5ef2705b5ca5c1b34707c75976d2b0a18b785b3388f8b6ca340a7d3d8a5f7ca1e40e7a370d48066eb8a0a36ec4fd854aabe6bdc8f7431bf22cf31fdc79192a4003ef0bf40c2c3f4bea46609293d7b0e3eb8065a5d3cef62c4dcf053f12c1a857e65bd9458605bbaf99a93b9d987accd003841a32cb60351735979967cfade3c2a8a69aea7e9a0788b2e779272ce4e359d2e5359086f79aa56417b722f375b3477af4d2fa2df811a15ca84e2a2bb0445a452e70df980a8746d658a442415ade9985ee34159d30fbe95817cd58b9b27c87a077b011e5e8b68602533556d20e4d9d057a46a697d79882911c99949fb1d659f521dc3263bcf4453dab3ff94c7d8b12bd81d17e22a144ddfa6b250d6efd11fc906b511fb5541083bff7c71929575ecd4d830a8eebf03a136ece40351c6b89c2ad14dd6413031c7ec8c5407604f4c3821b032b280914ee9be88a57ca953ba77e3c83884e912425ad2d8a15a73b8307a72e696634bfc6b74c8da6498bc9153e8aa15383694d045f325b0765c1ab48ee88558366e18d46010a3bfd5d7595f9944e2e0f39b2e02a2190069f3396bc04d66ea8ee1aa719047a175048150cc4ed6b8745d147f1b6dd266e896c5ea1e93b357087837864f3e88fdc98d4ece64b14a19736483e664f945cb0e38eaf0cb50af122620833064ff7fb307b6f06a494eb6056b4ab3ab84dd8698672deb25c390b0171d19ee6623e4f47b0a9e64b8e36fb3b1abc2210fdba6f3c94138f66720a85be5e025eeacfcb80d75eaa9918bbebd914f998b9c3ef581b465445cec8541b96ec4d9299156730a0bca221fb38421ad31a90bdee4b4a215fe8171f531900830a7209a245b3a6b8d02854410b06289263718fc6d08a492ee5b6a506985ed8138b7e00000c6f4b7ded35e6fcea4f614b8916161a588355a063061c242c0a5cac779cf927a3f88ded44efd55f142eb8be316649e3b7fdb029a23d638abb0f68358877423c6caed110e57a76a051c287f51b87000acad0344270991de63991ab3ade4738ecdf6bbf48eac914086397b7032f61f483d83f535ec19a4e4a5eb3bf6dcd1efc4ac50de291e9a8d2d3f99f6b812b855ef641ad9ac86a9ac6336929de869c9ca0b6e2bbccd1204dbcdbc8dfa470f4742d783a92513f4a0cfd22731ef3b0afd14fbf05b5329be949ee4e3fd5d76c59b5804127cc1ef2c7ddeba106b33858f2f62305dc5b75951c395f3bf0f1cd4d54251db70a39fc6b631065f70d83454f6b5517ea49055b491cb0ca561b16d0ad241057f02007426e8148d9d4669cc47f38e1948816e8457152d3b59a74b315772f5a11ae842ecd978b150c20f336f6226bf0367513ac98201ba2465b9e4745740f3e46a920a75cc052b69d0d41a42c46695c76210d748f57e889dd77c0706ccb7d629a946a12c7c21d560b273618333411114ac4e933bae43c063e86672463710b8b57b155f96b2a8fe0b7614e0f6cc8bc9d76a268328a94b5afdfd134d42c35e6e35c506cf7fecd518e311063c929cee93f701844925b80503fd58513058e7308ba981b98fe5e23ded26918d81137923b5dda76430e5eb6e7cbf53161a160770f28601873fc0c73dd7b843142ca56ea388d8673a8ce160f4908c60ef0b49d2489f225c84f3c208c0a8e719e201801c19205156fbcf6926a8d60cf69d1bd28f65f80893a0535b4023c25d713ce9be17e4c839505320c18c2835237fdb6cc5e124c766e659a17637c6dad1bac49ad6919c8b7a1318c687513f876386da7e77a99c001be4499642d9922ebd3a302cbaee4cf7f5a9bd322e5c5ac58ab0fd250d4ce4987bb213ab5c3e75f07fe0d540b6ebaa8b0f92bc6d84770def2ce2c1c905e4e545118fd15383b8c7ace311869db2e6d3ea488509ac157328547c8cd2e50c88fccc0340c00948899be0306ee576999e31c0607ce173cea4853fb765f444a0d753e47f63d7ea6be648c1bfcc60308abe7d862c68a1a90a87bec1b8b15ebe16128d346b6dfb03a1a7ba90477a3d81a3bf434a4790a5137e1c7ed64304d75e026467be2423121e955a87dfe82fedce59aa263baa83a1da5f83c2dcb51292e5fad1cf24a93472c58160964f139e3242db3f75ffe4eda4825c1bde6ebd7aa2b503bbaad18072ef1aee2932cb7c7d854b2f4499ecac1a921e40c8ffb8e6fff2450d2e3430eb9018f8c69f2dc707513cf522d5734d2acb91f150a9ea542e5daed3449809f8e76723154e3d0c76ee4a656d07df3bb452bfd872a270db53e5439706fb6ff2dd88c435bb113ca57f2ffe70d9fdcfdc41340ad89803bbaec959dfe9a32d8138f176793b6040cd4c54aea0a34525f18efc6523faa6b3ec7e4b39eb491441f6ca53e69613d3418002d92f9ece907b50f1a8e3fe1c77368d05887293770bc692b02640d9e6f36d47c00217f4d199fadf75066f2fac4bd3a836de016d9b669cbb31c41d05a5c8a00b9d69dd8d1c94ac1092b891b7aaaf62c2e7d22766841a3bebdf390fa669791f24559dedff72c32fcc9c97bb9b8b13445a2d4f76c6971daa55c2284c41851629b88fb113f138a6a5ff521b34a845eed5b2904e8f5ee2a2fffa07007ad637fa7519ca5a315b536c909aced28a720d6f2ad62ea4c7b48399d27a32a20bce288e8ea3228ce97967043e7e1e06295159ab85d7d805e01e86f312834bbdffa02a5ff2601a7b2459ea9f030eff49df6d3962580917863f2c89e32c66d68b940c424f894113aeebb6d3e857c71540ae5c9a0dd96c491c43295946ad467902f196103d54a33523529cbabb8b82c04a875546749a7a975320d0c201ee704dcb30e91890573fe6348a0d0f37f79badebef3cd1306fc4dbb7db1744aa045eceb66fa66bb2e171efacb4e1f0758fc77b6aadc819b1a7d94d389600384b3eb4946870b5f9680c28e6734d8111ab4dcab7e56511d375aec6001fa7dcea48c3236dbbb80836ab9b6b5b75f4ec4eebe3d2e8d64c5ead9e6c0e6d98c89f911de90ff118bb6e2039f0dbb55bc820fbf9b436071ae80ae64edf5d8cb85fdb7b7a0e21f02d24df941da8c5010b5a27db34681e704391d3b4d771d7ccb471a303a258955cf4cca0146459c903d21854a91373d06101419ee3b52976496710448db0ed5f4b557f670ed2cf5a763b790e18ee88a8e2aa86e03a07d564a394ca4d8c98bcb2f2c48b9903d9344c4432a9dca485a15277155bd38a0aa3dd34f3b5ec24580b8cea5c45b2469ab8ba6cfefe3e7e639082aec95ca0ecd616ab9774b83a099c0632b4fdc2a23210486dd1b6889c4825e3285e0672f88f93a3481ed4e0545b85fc4ca31a2c584a2b4b0fc27dafdc337ef604c5dccebf69acfd0763c6d1b26c665c0616dee4dee5d2d7b80cd332cf84586382824072d7c3a9a71cf02f3406b7f11cc2004822701108c4636aebf80d2f07e55b38331b4e496fc05eb00ae9370d33720da213a1dde48dc65c94eab29da2268a1eee83c5d6801fbf24152fec6033c4fc55bd14b56223d5c48da0e78559c6f6bb31bd2fed400bfd6da3311e96839a62a3527a9e1f616124751451bf8e77561693f0da1f0bd30dc005aadeec77d4070778f469ea4ba277d38c871a2b8312f4100bdc2dd7831bff46ad639741fb1eeb69bd2130c3a20afc0ea4867f04c80d112909834799749b25333a238a434fe1b9a947110d260bcbd01e80b69dfd408101180aa984e0a5f99f2f6e240e3e8584ab53766170d13100b7c95960d0cd37ba3c85cf48055414c57ff8f94df64caa0d624d36eac6d059f11ef76cff51755531ae9dc235fe0b3a55e3a852e73481159dfb1471ef6ea06f97cdbf859045ff4dd7a89c2014b4d4925b96179739f0724fcb445bc3296afdb13024f9eb54be0712671d94acbddd88a4d27850b90c1ac76a4c255a2aa611ca2d16c58a383f73f86c62b61ef2be35864f9bcaf1a08b9b89a7605b93e6edd011875e7ca6587ef6cd2305199a5c4030c6341e559ff7d4cc87a87085e69dc70c230ca46327e718189dcdee089f7f72581b61e314c3b376c3072d8e75de464420a86f7d64d8245916e3d71fa7b1021f1bb1815baf4f4ceb1eb87a8dc136f17a8130228f559f4637a94295e9d951f1e3ac48c1a9cdd9c26bd407b6ca7efb78206285b7b76eaf6518d2decee7d6895b3d28e6e2873a896b0bdb5b18aa822e68aa9d91f1f5a70c489d5e3b7dd6815adb6482a3e8861cfc9610c1569f967607e0b4685861e6cab162d4ccc78072c72fc86e6325a66168442461eb5dabe01979f07bfaf80db00b0109274506f0476588f7c150d3c6a377c2aff2e08a7b316efc89ddde2b8bbdf26ed0876f478fe224aaf9ff74e094834deb59bcd3bea1e64c7bffd439d85ba11a930c5d53843701821a772254eca30a85cd43052341e72d19c7ce1a6fb91ca23fd3001ab526c1b9c0ed518857d18bbc1e2caa0b0b3a35e6a7853ef283f2ae9a2572d1ceca57b3169313aab34cd6a8d920c2625458cac4d57df04e48bfc16f8b974e178909535e26b965ae2ca251c881a995c6fb6206cee30a49dfb6e91ef822780527430c5ade57c0572e37f43d6a86d6a62211061c1b27cfd824320963cea0bfdc0e6123ef780afe27f4c0b5a3472cae09a9d1bfd5855ca6c6b0e214f2a2dd0104b027b518356adfb12dbfda7772f26ce6f9c98f0e9d8ef19fa4ff34677a3d233c41c0fa424bfa7e34261c39156d7b7328100db7fd1f3242db62172e3371b2735ca7d4e67d2f3b98e04a0fd74b9fc3f555e3ccbd3d024bb482758fd9acf44c8d88bf73105031e162bdb9ef7c2f9c08ae7f771c32bd945a0550e17c00f8c66835e9d7970f9850eafd31107efc955143254bcc9a67ba36013176080185504228c705d0a4e6c7c8f2c1792c79163206efa5deab2ab97b2986c4d8248e0b1368ef585981b11045edb837d014d0110c64d9eb5d79cde7192ad70d86c1460063f80abbd69c944ec1ac1758e2bd239bc49429300054f4bb0368d0cb30213b36d14c4386892fe4d10f19d30e075074f12a507714a853bbadf4f633ee90dfe85fd7939185afc14ce7621cc87e178ac6b9622e61fc09d29554bb5186f23447d8a35e9d36f64b2a0390381677a4dabc577b6dfcd942772eec29d51e1ff351e4cb792a234694da0a8fa4804d0892f4ef24cfadcb93c767484cb52b912a7d4eb8417e19406fa99414cdb410836a53841fe26ef0fc68933af2a73170c7fed35108f01157df71dd1156ccaf6f7540613e8af39196dda838705d60f1ed1a1bd8ad7178cdfe6e9ddbafc2e61650aaec18fcec6193ef860ba31bc6af99761f5dcde3972e45ac3efb17952a1d7be1792b06dc11928f60b54d856538f41ddb6d57e24f53c19983b8518c97e321720cbacda5702f6cb194e83d0a5a3d2342d262767bb78535b96134196546d064c927bb91b50e7e708f01948ba39158744333d441b68cbcb14416788596ce5a2307638d3b4ee925298dfac9d96475727a8e26a7b950ca53242e88bef7dc34be4461c703953cc3c2fece071e8d8bd5a74ede4b271735394725c28f45a3671dfc136fbbd19b37e834663a272b8421fbfc15fbcef3d18a2cc8bb4e9d9e8cad0f9cc75450148377a698bb949a43fa5067270e6c3f55a5f79da08b0c4a9d0fe4445f82b37497837ead2dece261244d4535e765c6f9ba336fdecbd48c75966d0ebd207289dd62fbd4f0e531fa24bfc4791d8ed5174367a2e16a43840c6aad41e3b93e09691f9923283d1115a2b451efe8a527e8f89b1401dde5161eb0a87f676894ba5145f7bc8b2a2a08e69b940d3b9ddafa9659ff272874f1562a2619bd04f93fde11c9648779c592dea291be88ae8da90c6cba989d911d0ab05250118e56e56395f634c2e4d45bd78d936dbd70100252e56966554caa6e51021d605bd76edffd8f61fcd03606f2318f9a408d03a2605117e2591a326f317dff9872b30104391a0a4ad3f54cfa769f4974168cba0f82ba17041a8135633708a68b665e357e0ad0cd7b1caf45e84ab0f1736336d08a7808109210fc5ad9814c1796bd82db351a3e704b5f123fe9f8f9d30d5685d16f01ea9ad21ca79c214d0bccc97cd9556e2e8d94295bee1131524afcd01346b482f17e3be3be5a9d02c7d4dd9b9fbfd6edd86f65da6905c3f45425b8582ae89049297d46363b7f0a544f80f14905fb306bc7fee6ac69ae40e1b24d9811f9661cec821e7df5ef5110f88d312ac5339a50a1c4e35251d94e0befb8967fa2b906744b832845ece13c1f8e630e1c281789b60ef09e9c5fcdef4e543f042d3c058b5a066772b240b5d1506d45adb3e8293d7b247ef76b1372fcb1ae35a16e24884204d62882f78d7bff054301027a5c6bf0cede50987f0b8e7b1fd76fcf72a8055e76bc52bc814310eb2c9f999e1aedb3766178cb05e81ca38dec64576cc80228e00f1422688ae1e7f56c4f6718692f1d7309f99cb2215b997012bed94b2f08d6a9f4b47e77452d50f50557b6e63d4f524439d35f7a054332857595f77b1ef290b42d4c31c1d2d039c69592b8c671bf57275e5bd72fdb56129954bd5fffedbd64402d9cf0e23060656f70b0d0a31e8ffaed2aa6c2a1482662c8f46b11d682a1835811d0095f85b0f3d30bac7b6784c122056d0058ef4406852339d8141a44eb7a01e4b50240b0b16812758d7e2ebf621afd8508bf8f427f4deb467574e1e9faea1cfb38ad45f19932eea0d97cfe312ba4295a37efdd62152ac47677754d0fd71ca9f355274246f366a1e88cbca7970d74e65d9b7ab5c1aa2ee4e950486a4f71ef0f992546bd326236bd1ef11a692f364788936bbd5a039b8e11df8b082a80737d102d88a92b79a8a151c155a0ca04d2baecdc0102699bb10e6b6d978714ad7d07e092e3a5768598071c76e04e187e285c6fc08caf8563864e91b4cfc884e532966049efb50323ebc8984d8a1170f37ff312af579f545ead37cc8eef25a52796100ada781da94c7b4d5a0a8bfdc1d6f7d0abebf4af09c70a1f9e22c4953a05fcfde3b16e7394e2a0ceca462ced744272ebf47d16d6063e87f730a673312fe1f10412eeb2a74f33410c6302bb4c1b86c2ff362fec2ba113538167cef340bf120a35ca65f74bb98ccc92f6e60efaaadd78fd5ac41432769266e7d7591269e4da2aec889a4a51cfb69ed5f66dab4d38ce39d8b380f7d09d17aaf2d36f0b264225e733581848528bd415474cf066a03503ce3e72bdacd10bb94a5ca304b455c1f0317c3fdb9ef75d7857cc4797b1b37cb05a32d05b4a75bf579ff595025390933fba4df0c9c4e6f3e9aaa423a7233167d7d7037cdab0f6a872c4cca02e35669bb252485dc0fc066d5d13b038ea9a4eab91cc5cd1db21615d00ff7f742a6b8a0df1712a3a74eeb38da4dea4a9104f95f3fe8291e355ac9f4355d4426fa57ac340ec7860043d027ce62a152d54d94e5ed143590344f664f1c0dbdc7b9ad532c277d76fabd94ff85b7f5feecfc3ff9fbcc84c294b073903ed3d098c2cdff1b0f2ee17f474af20bbca5084ddfe2301f0b7152df30e6ed3d7f2bd1c49bb320050c40f7c4edf9666fa385b458bf0a6a55b34d45f03fef9f72c2dfba39890428084c51a675c2507dbd760ac82d69119bda9fb7a35d321ee3bf12c589ec01ed324e05f8fb22f0271660972e363313a355f469b380e495de7dd510fc099ad40ebb169dc1a2112be88a0693cf86dbf47e9b81d2444dd845ebcd236e35e9833c08ea364c4c96456e44108264cba85fed342afc882eec2a273e9b83c65c97a8bba1f51ee2f4aef6f07a96c79933ca373ad08592b8f3f8fae7dc427b363bd8974e287cfc41c8aa62f666bb993d92da350fc1a0284524364fec375f98b9a757959ec0934a34bcedac2181d2ce390f79635b6fe0d3a2f65e471f65e1a99a702a5572b3ed3bb71cccea078c5aa020ca489d505e833c853dd155989f9dbd5619f621c579412d7641ebee51c2c548886a8a9ad18a9b4d4eb102e780effe4f8c28ee094a08a60fe4b35be16eb9c17dddfa30c24e98124429bdc06a4ed71919ffecdec856cba0ab2130739d9ae3267be008f4189a38a1d5370496e39c27d8ba01e975b55e9cc7518654c1867193d074a24b9416df456504780acafaf9a033ead10aa18bbc673d4a562199d32c08f0932fec0c81b941d8dbca840abe1babc68698ca2456953b45abbf823aa18a85a35e66eb7307c9cd4f29c29c4ca55f7f9588a885b963415adb22d0f033fb980398171b15afd2c52a8aab933c2805e32837eef01dba94012a18901e9ab0bdcca10eec7da348bd220fdafd8f35ff27f4a41deb57d04cf8b7fc12b4492f401b96724808ac44f263c40bdd650e945862a2ee90f556bfdfc540c4d43e49581460b48ec146ee9676156feda2a23390a03ec5afc990409e48bde24d00e1167e04f8a2c204211599d378c41cf0dcab8364ea75885520ab190cff3b324c7deaf16b367c17862338a1a4955f87da0a1b5da4ec1cb545d060458dd60fce281012f13e719dbc4dac4ca7f052881123ad6d4136d22e2a4225d85bb8333e9d1d124eb6b15870f57f63664961bb2ec86cc7967940f8c92d04191f2f9ea3ec4f963038bb4c36245e089815874517aa53cb20c050d6cbe57380a65d74b3ed83778602f3e53190c7b21819cb1f2168aaf869a5c57a997d9d7cb2b4df98b2cdba73d1d7742bcecb5beb234b00b6a79415ac596378e61a5953c801c87f467669b355b31bd347aadd7f4f1984ddf9d1de95bab69f2393dc4526db9d8feb911a9dfa805b462cdd825a8eb15864bed323e516fa2581880094463fca5e5f43467cbf4102874c0f228bcd3009dd05c77860c34767e7b038b35034bd705967ea781efa816e9aa97c7e93b4bc933ae9d418ceae0cf2cf42e9ca82b4a8b87045d11dd2c244d9eac00ad4897b9b83cbffaf46f91ee00092e0e6cc7cb533ff384374e05609e9192176f7665f938007fefa9750ecc4ec186d6fbb355a974dda7d9d97d1e9d7ae666b5020951f2d56f9885b9cdffb2f1b59d39743aca9ca6cf1988369e5aa6a0f065b1b7b9a1a81e4f0b77c88a87d709f94af9f15ee23b1c25a28cfc944f4b3dbab1f336883d86380bceef246126f0228e6ea8aa1be83992b91d59e2188a3c0ff1ea6a68b0b12480bd52720e6f2e681cae1ad9b98fcd03eb5d2eeac5888d50a71aee9f0387e09b1467ac24b69ff0364f3ee7e531d0f846cf37a4e36d98e1a7b608feaadce873cb6ef24852cb88043934c3bcf28c6488edee100de2c38af1f8b795f207a57fc5be0f8f81bb0326b8c3f738bed181126f103f20dc8c0f4a7a796fe89858e1da184e7676a2a38f639eddfb9b628e8cca1eb313b10946cd78b4c102aeef53e503119cd142093924846a51e32adf82e51019d4b27598c28b9fad71e743d7d2356dc984a544a7e32a79f4664a7a367933fcc25e818369f672734d7083dc68c3ce667afc8f179fc3876d49948142ce28a279a32a48241ee59ef957c14ddb92d327130f14c315945690c21021db1b571411cd8ae6ae61b042c046020301e89080f673ee0eaf721152d4386ec1de5f08ff8893f0b6bf3a665050703231bbbb9e516f057b56040e1e68e139d38c7c00321b648ee97ea8cd62c2263cef0f4da533140f399631fe3b8b081252414d8a7af9ee09989d1845f4c719c0f031a18f96ec086c1b0383b143e7336a910ca8183b112cca18f2f0e35da8d8382f89ef22a47070cf204c7a981b4288f697d4be99e7e6658652ea6ce4d0f91b2827e0ef18fa6531fb66ed5991218f0fae7fc27967abef1868c4aba354227d4cd408b1fa01921f6650983ea133ebe354c19e694450ae93f9b2ca7297bb4e736d8fc3b50868eaef9bd86ee2ef58ccee62917978b3d4d00dda33a215dfba9328c029b31f5f4e8df522ad3cc211e7e0a988c803ffbc812ea5f0173a2a4f907742e408498191b5a6c767cb377870df4755281d5a3ff99ecac9841baf88b09b45ed6ce5c976e4ef7c7f56c162b9098620b922a20c464d1cbe20d34e637f8624deb2269184e9bcd345b88c40dfa5918a5e4798206cb82739e0b1c4e9cb4106039e3b1017129288eb15a09aa50cc0375c12e31fe941cb6918121a2a1fa188ee45caa997066e256c02c333b3edaaac90b8199ce11c06dc62f18a3a51042957146f2cdeb179c8171f73f41a2694d6e65a79c41a2333e3686486137b1906099395164c3d845c06a30d2d3ea12eff1a6da440f7dd34ff895753bccf3130122cfd22873a4daeeb90732cb568ef8771f321d1c6086049765efe875f10bb7839d43b7dc974a4e3ea6b063d171a9128e4e983633dd20f04e2cd64584654b45e938d7ea7f08e527e2bf0f0a5b462db45d89adb7fc61c553ace880d2ca49fd869164402117ac28dc87c5868bab71fc203b84f8210798e72de57ea7de9585ee7f910f4e9ad053bde203cf43640496f3c73443fe4a8915d2196053ac175500e940f8e8dd487e84b5bd9e107b8ab4ad30ef8d69632cdf039e433032622b0c835fefafa45d4f0c41b2233e474107d20b50485da81339fbd20911d59cc7d9dd4bc2e0b47f3b1b3ad42374c0ad08d40626c076a1ab66491377d3627a17b984ac04d13aeab1bd4e43040060903d391c4fef60a458438d7c2fe63ae70b38fb8840c2329f2fa43aa03e562df0e1629abe281f63cc7c2d8a5ac7b075a3c2f162aeb086d9e54b0c5ef6134a5b4db468dc77fc1103a92570c44e648ed4131cc630c4f03e4b2f9b7511420cb4c00a0689964a070dfde5cf9a82357cd17fd6f574e4a26db6d8d8a78142fd45b5575a21447925d7a6256415202a0ccec04bac5a174a843b202522cbaf25dd125310a691bf6c04f83ae6c306b34e697b8337761eb6eff4347c62e9d035e1aa30be9b033ba311bf85cb1494fb70c6f23fe780f72db9c6c8b4f8c8093c516fb6ccb982db2eef75239e5579868dd55643c2ebe773d84b4c3b53feecd86b8ae70ed562cd9e7b4339fda497f2dd35cc8c30014654864a1f5c7568bb31f1c20ed38c947d63a1b34706d01dca23543b96eecd5038aec014d893afdb0179a73e5d7ce0287213013b71d6a2b2d3d3329482c9903bfb397f17d6b511895c0311840c6a055b445ea38c1154184fc64d8596a1db590d56b5b888fb9ddff83c083977a57560821c66414ec6de11f15a98a0b3d853df49d1c8a56026226e7e1e9bb3c3b716dfb864773c06cbd39c5009d5abaf5509955651b9f246d50f64f422887de398f92aa79f5428d4be9006bca81856210b7781ae9bc5ac9cf2de3a22b4197e57a2fe3619569283b606b446cf0422d314058f148917e9dad43265a326e590a0451a8a35dd9443269d858a0666f34150da644c64d8e4745211d013009fe4f04dfecfd7447e695f12454ef5b7c9f3350ff8ed0761ad73cb4871e2d22e6e749ceedd5321ce6012cb316ebaa05c9dbf9d5234a75315ed5df280c1ca916e121cdc236871361097716fa3b35a660fd5729bedbd139ce34db8ab29c177fc656fcff5af6d21c7c4995cbe4e99c75a567332bd842c339971b63faca21959943f7d2ce2e374286bb8d06a5582151850c71fcb441b50bbd256f34a23fa2283909e6f34559605293d39015f52c82df23ed39f8c37e6c15c94554f3a5e7d4c593e806aea1ebbcdd266584dd9148645c521261dd70a108b051d936a9207bb082cad684bcf30d9f36355c13169c3c38a279e2e257dce65d7f8c60ea11daedbbec5ef894adf4044260f15a2a9e4a725805d866121f171f384e666ac0169d0e0d5afa616e58f48c693263559aabfe6ab7dcd9632b481c40770b702346b6148b74b6324cad32b362398b7dafbdbc8633364186bdc20d0881ad338f73b1644a663a7f3c290a1f93f7e0adfeaa1ffb3efcc3c20006b3a4b31cd1f5c3c01db6d2ad1f418e6e78a400cc1e7b2cca06f8a391b1dd1d9bd35de692a06888d1dfab601c37808991d79a6cc5e87bf936bfc090865ec7afd4dd335e745523f8803abb9623d54a3a666d00d9b0353910b30f8afcb836ff7c5ec5247425984460b62f64caa234755d85970bed17782fc7c089a19a544a880b1eebc14c363ee50a9114844eb83cbc19fa662c39d58ef1bd82f1ac7ec5b29feaa1ccd3fcf197647048c8c9791f38d39c58d454cd7b694fe84eb67c3357336af946ea23b48f154c5512c21b227e63bcfeaaa04ae87e6027a380a5b706a22cf853a51cee12fe72e7ca7ec50de09676fd36926998d16d33f539dc1fb59e43a824411497b2ceecd1aef0615a597ccf57709ed27e7ae885e49a9ae992c9b5aa05165db4b1315bb997b4906519bddf1c8976ecfa6af7c9b7895272c2c0972203eb4d96247568634aaa85fa93e304e375341dad0e19d94eba0e1774f4095f3fd82a187049ed8a52ab1ad9422dbf3d9b6a70ff0dc527b19dce49b7ec49a0b0ccdee5d529a4062de19ee1bbd980e6adf3122d599344ee2371b4d7f15435a527b09d4ea3a1f8ac5d5b2df9a01390486a5df421a7405f806e4de1de8f46cc9e137a1b0b7c83ac53e4205ce2e7206cc2bdf39b2164c3734dadcbf2b669fc5b15bdcf4ddba31871963fe457857ddbb8ff884d7b0399ef0c604df356decc4cd68717bc29fb78fc66a11b367c1efdbca11f4bae259a1da674f5be6580650ed7b5455aa49f3ff325f9b370acd31bd359165fdbeaa13f31a64340ae631efcad27eda700bb479aada4654d2a88d7884547ed4fdb5849e2e5974fd7a768d6a740140efb3b52115675ed730c5f8110a6ddb6b884370eff78261cf9ff8ea048b47e2765d21126777f38f549c67b10e06cd870b4475aa9394c50913435a7bbde803d26413ba908cec5550b3f0e1d341e7bc88c15eb715925e66b3840b8d2dfa65045699c60b7c4265fcfe59e7868e6958174d90177620f6e7429744d95f4310320332dde683bc5576d9917c72a4441db07827895c9a80f83b5c9bcdddf1529cad3d7b6d385da6ced6ca54c41058c45fd624ca45acea8d4e8d397a9a743ec6b38943659431c2204d7d180684136a4a3d61b446400d9de162ad632f6cd01b48ee56e0e3b8043fc1a37f38115268ec2e3712c7a263e27853b6bcf28006b4b2f05d8e945242de1f9ac69dc96b5ecb6a9b6cce2cb9e61f4647f9065079f8eeb26b1842f4f26ff31aec8c9d601e2f3d42346b2e39ea550ac438a2292fc4b852f1ae43c67b25e30f101f8baf9b6004e0604e3dd88f067c0fc3f2f39d960f1f20ed173674409b6597102a9f6e14363f963d7ba694e21e574b397d501c89d7cdc1f17017300a9605e662759916c8c513243f786bdc6c5204578db07ca09f7a6e7ddb5418a5838a4d07a3f3b98d9a127cb75865edced017030ccf23633533455c63ca68b6dc2b046797c117199f4e8bcb78bf24c970e1d95f25bb29436478e14b1d6f353603f5a1c09302dc040ec7051fd5d2b67b6cd6a2ce3404f7c8cbb61b1a4a4fb27722638ac0de2f52f20839cd40b3de6c194cfda7bd0927c9e03beaf13d8912f0ebd7323831102f814646ae0df83f72b5f92ca8b0a7d021975bba41e1a6d6aed817e976c5c7f1409fff3f569fd91be85c415e1233be46844f648ade9e60a445b7c6d9819d9272ba4547a88299e87fc0ca23f61050bfabc553cab042091b71d813459328534bdea20cf55de19499be78a1febd20ca5220d90ba80724205c2c79ed0648b31e4cb357f3083fa2d40baba705261d07e24f0bd49c01862379b867cab68c5e68189aca39acaa01de8a864375e2b4d643b1dd73f92a55d94232e36b7525cd921f989896137314a742c402658590ca1f0a0e8233745b5e90ce628aa07c5a839a1ea6cf5dcc0191129022a06384e9852b5d289094baad6e35af4bc7a79237e26b34095e11491c9dae86468baaeb2026433efa38bb1f884db536bfb337e3fb9c42bbf53653fd3fabb9a3a1192f0ddfb08c1dc13283062428c3a3acd7921d1bfe0b6f3680bbd727feea3a06b1d00fbfd81df8670de150805510ccee254adaddb9eeec820375158e95d349fcdb57d77e37e4bf8f220cc7789e8532f33a24195e72af28cb4dfcdc7f96ae906317410faa957a77335271f67a11f06c64853ae19f0eeffd284e11a3f514a12ba681028b22825913816cdf8eb59b488ef06bcb118444a79a668ec6d396171eb9ef03adc121b9cd3436c92c4334e4d811fa59e2882eee1264f48c86a20db05fe693b61a38a0f410cef60bee734978f2bf38972c021f04e14b240ac9fde60399aa406a1245f39b5ebdf993fb3bb137f2c3941327e7b91b54d9ed6b1c59c95eb0d8dbb473b1442af4afa8689b7c958dc4579ecb2cf36253635fac682f28801f37312373147c8e7779b52d9f63bbb70ba58372cbe43411137285a3e783f150ed9b68ad4ecca9123cdb5c1e59551637ef484616d76ddc85151c5a912a0bbfe7e7c98e98b0293f86c5b4709fd1db455d6afd9aaf89c8b1fee09ccfea4daa25df9816a3a06ce1f80a24bfd1eb9f039112771467575622a342819afa2460a1b0d6f80d6ec42ca711d76cefea5fedef7727f85ccc09486a58b542f52e823ac2f34922bcb3135cae97bf69cda06853935ad638d55046894c0dad57c8f44807ea144a074e2fea4a0b8c8cc2dd3ebaf9f4fb863c1de4cb0dd7203adfab18cda6e65c0da14d9b9d0e1b0e6b7e483c19aae9ebdc9caced6937cebd1e2e512479c062a11acf774e7bb8a379535eb9505f4b5e16e2ada1ce92a2e8927067448e958180bc76652dcd4975ed48bb37d425d0811d8f41b25fa6ea00c45dddd20db14b7cf3c0d10b38a6ffcfa429bde32419a6bb69fbcb70fe5f800ed529241314479b428dd0549fafdf3a55ddce521ec03939955a18cb8cda2f3550b4f3d9b53d8e8a9c22150335973beb48fd8d26a367290cabee0094be097d3410a44fb28c9a21b164bad8ebfc8045bd09eec244d3ca8e49cf60f0d8a9e8d05811d07b2cffd7533e5e0e8f2c4631802dd9cdcee2736a142c7198337cb6e919eaf5b8337efc1449a1266d3369992436fb8ab8225c76594dd5e5ebdb0b5962bd9ef4f9e3718ecc1cdeb9f53b494b5497188efd2aadd2dd8fb192bc5bf14b1de22dc4341ad54312fb0eed0f902fce205d55055fefa45cdff123d7e2bade04c8853db7f4195c8c10804eb691d704b96dc6cb93259a23ae23e1c5f1a76ae9fa9de69996040c94646b9c9579f414b935d84d6bc0f70ff037780dbf987c23faa0535b898b4642d5145ae1b472ed5b9f57ed04ad3bdbceb2665a8a3fe6bd527d34913685aa5640361f52e00364de62be7c500b1adcaa92310b880306f5c376d7ae82f1b050342f28a2b54e28e491142604a43c98ab360ff070e6f457e51a37de4537e097b0e3ea2ac501a63f8cf97f3d6db198f673c10e29d21c688c2898b4488903c6f5ac3c4737b1d5004d137a86bb15c0fcd090ba5e42219d026f8d8f2848d31d0f90dac423cb75ba7ecd3700d6065c7b81d237c277d8062459df83d1eab50ba865b15c117983f8e9c40212d6de1e76b189b0ee108691d763d20229c0051247c30dddbdb3a14e4d69e09f914384fd5550b55de90a502db8f81df3d0bf33e8148e0a265373f93b1af792de3b69363f11912d170b07665bc849aba574dafeaed60a471255597ea544f6d5172aa43d3333d9f1bb68f9d8eedad3b741a2bd21f0ce6ab06e6c947b91a090b85c764231a898e7c315a9e1768b9eca83827b92aae43a771b7d1d66c8e844d36540d67adb50e19a89c9184ce7c7ecf955e7caf1f454d75044957d54d498daeeb0dcaa3ab0a5e6f103f6027b0c8e21e30b7279f06c19cc071b98746b8d2c0e149461372b430b0d3286262156154ed6916f0c8a4820dc8562399e971d4a12e89690dab4c8d28b30be170f9c8a534cd91d4277492fa45996083b9333e9078508c3ccb690c3fd423d7fe2bf594c8489e44fc929831bc199a219406885ed5f00b362aa15cc7604a17a38e85dc3dfd092c68038516f208fc452fb4fa88c9176d2af8557dd242c7682faa8da1e730168afdfcce77760de5780615726b025590d92da3051193d013699a5203571153e4be4ef16717c6367b61fba8014a301210526258d14ed6b73d60a365c5338bc60486c92a44ce103f55260c9896a069dcbfeb0e3834e41e494cc4870481e025d732e05faf0936884af1e69632bc16adc7ab70a7f955f7f82adccdf3e84e4249cb3559a1ea8dab4f732a3ead954279720de66a152dd7e6ac718381f84c28b42e64625a040b6de681fa543084d732d64a774653e33588cc9c8e92788725bc928bf4a90d122c4fda2046def513f6aed96d24c363b31deeaa2cd0bbc2167315301aea8a320778ac59a6a9a866a0638f6b2c8192d2d19a5d957bf8cf51036553ac474634416e14bd66324d9e6db380c7b057ba5813acc65b48ce8abcbff74ad0e233938cfcab854af10e0873a8710a5924ba4bcd07b5f1b90a7f8608608215062251e48cdd1f3eddcbb7280ee1bbb873418a12688109dd342dca4a07739e5999988e8883d13f5fc8d3b1dd2203092a449ba500cb1e25a554558d81b0393fc46d260452ee645fdd18eb3c7b0c68dbb87034c38feb61d85b762e9f87362208afec3748291114f64d3fe7a06b7608a0668c95249a704ce32d88c3886c76b025e4e3b11aafc2c40fabe81eeaa142cbfc79506bf292848d26c5e3b8c034e9af8980fe60761cba49c8d53c8c5f3d691a35178c40929a58c0d32889ad674f36d3234154e44f33907290c90e67076c4e8ca7e7dd1250c9e2e1eb010840bd276c6ee25e3da6eae7d8e1910a4ac7a42841ac1545b3d5c144488e3277f99fc201f129bd3aae73bd3c85c32a327f013e3b6ce40ade0819eb831147800b5745047a04b2cb07ea5e461e95d7dae88c499aed0488a1e563fab5da784f92fb4751da51349df5320a9b459125efa30e5ebe0efa55e7152583ab3dfee44685da5a8cc04cb91cb817cf3ee65e22762dbb152cf4dac949c56d4857616d74877b790f203f576995f85e31eb8c94910113758fb0bf671002fff176e06c4a3e2a58bcadb903932b727ab31e3482a386a774d6af6b778b552e07c6d9660d3b416e1c9134f9441b6e1b43a30ccf40a611b868808bb9c5447720081ed9a43df1485b5182e9071175e05cd65352936e2eb53ed2267e83b7e964d6eabeeec399c6432f30104b1e09bfee48ca514cb383164318ef063c8b881b35921c0135a310ca4f22284b9d0aeae16b1c9ee16094eb5e5e827442296f7c533e65235b1e511b31dda7646a4fd2cc1d1b67aa716c32bd896979f4d1fac53426d34d45a756ca25c1b518fda847a511fc3c9ecb3a9b42e2eadacc210eb8599e19f4f7b71e05d21e576a73ab0fdb4b829ea4a3853ccbcaa897b277cb0bca209963dd77517e430be48aaf020228cbc70d6c42e926bc850bd313ba0f424619ec67d990cb5442a7a206aabad19580e64ae12a219e0ca366872c991e41b408d628ef858c208d6796989061538f736ea05dd600ef095cb8007e170176a6e3edb78b7df27f077cfaf778e219d27712610e2151542d873817cf377b25d266fd765de3b05891f83808c194bad871ff5d45514c4fa0db8cc5d87d5c6402e4c2c163e2709475783a8056e3182549847f899c41c6ff5f3d442a536488b12f76e84bfcd1ec95405c08a73c7183189d17a1f125915473b3a9998480d7256d0298de110854bdc8a498258f9a03fba0c802ca2ba1e8e3501c871c4a4a950314006d923ab12a39c5178bc049f02283dd1035382e900d8adc81581cfbe1b4dfb75853c010a30c3257eae070c0979180961061806d0918cf31643ec2d77a29679caaef3ea9854ea33fd34426e8abde615240e4cc01a3e8d4800837f290f31def003304643b8642447915c5a66adc13263b727f2a8b82eb516d0a4f4d16cc305648d9cdaba7e3a5a1d3e9a96e5bad58275cd3bf81d1f91f8c35bc1813e8a94531dfb3aef671bc5624212697c1d25ead2da6e586968fcbf5b9d7fb3337a9cee9400a5732ab2e353e42438e4a44e818b7c48891aad87199c76e57b38213e1739a0d46ee5987504810d96eb5a42a589e717cf832e2a675aa776471aa3e4d6625c12affdb37eeb0711babcceef5a72e38f4848e6b650e7eb20dbe6197bd18eb2def3e16d0ceeb0bdca7cefe194f6884e907172b2f1eac18ac62970b06f62d56a2e169cf025c13ca1c27dbbd109cb2f0862b9394a981ea112c46d054a8ac48587dd59b92c145da9d122203ee412871598332254ce4300794d219981f13db341aef8c36167da1fb862583088ed70d89e03c38e4e65fb44ebd12046ba4e5df539b5c887801f22c47e611b477d61c55b2ddd94557ec9fc865137c2b0fa1959d6e2ad0cfe4213932858a6cc4941509f2f20193f7a940768f59e400f4231657471762e4217a1253a47bb7ba45e0859c082553995305ea5937d36047e1040e5be54c21c578315b553fb39729932c57ec87abb44a538dd2e9a14229a96f0c866eba8a520e0c84ed6cfa1e57e8f81a8db4a70b4bdaa495dfa1c976e8b5c92f2e59ed30d0bcf266809abc7857e795951c06d80deec776d4067d1553fbb6a4c696e46ef9b54b54bde4e9bf90bb68a952c89d99cc7cfcb00b050be0c9a476993c2c49e4ae5a54505ac4c3104e6fb995cdaa591658c4ce02b5921d121247224a14591598328279848a05daefc1a5290e0741df15efeca455b86bd5e615f233006b94086960c088b3c70bd6f972c89f3bc2e79fb93368a3e7579bb074880d0b51cba2a4658cf83c6a6fdd6d3fd54974297738b2d58be18c9e120174661d656a45fc2ef974def5c93a7b2db5e24481fb46771bdb1b8c1e1d858b84bbd984ac13cab19dd8846c14c643a83721093c112dacbaf87a74ab723909423cbff584dc07fbad16f981dd60d81178bd76d23566bbf766e77518cdd350a0199ebb6bc32dfc695e379843f0dd301d3d046e35a51fd7ed9ba4fe1e9b9bb5689899ebff137120af01a2d1dc68e08bf4e179c9418883ab82112a553cd506e339a1b48782c8891071796755dc713eea0c20a3855a69a2cd55aeb93ef5d442b139f8e69dd1f2dc78a4ceee2b0c9cf0df3ae1e9bb61ec21ffaa567fcb8d50dc174e343eb0ca3718312bf16888edd414a3ddb1d644087ed395d10767031ed792e0cd591a55028828a257d4abc7656ca2fe3ea0dc6ac609176ceb92fd629c28ee0179723ab8d174c821cda48a91a6c317e178626e53f16fb20b2742b8dd73800df074756eadda83af3009abcc19eff36b80488a527ae8bf5719a12c3b0bd4e5a98b5dfc04ccc31c23df32888ec1fc197d26f2e484c5519114dee59529425b481906e0cf3239858a986c0d9e93cbf28773a4e1f133444ca12b9b934ba6515239551f4dca89652285c2993d0f4360547b30f20e64548a4ebf2ca44dc0b481faeb59ea5a6111907b4ed2d1fe84148189c8b1a8d84a4cee667e06b1e2d2ff677de30e70923ef554f175e9011a373be6e99d8a8299c13db923e3f2f234d8477eacd8ac47a9c56b97478431c0f7993065177a1a1522cfb1c88c1e47b9a0d8b4eea8b9a75d35432957c5fc4f9a5b15d6b68d0af483c50d3e97809f782f9bdfc025c575ad7759546656dd39357d050de280852c6a4d3f57e037866f9838ae595f637d4bb5fed90a38fee1c4fc12baf6db24e83f6b11756a203904db334b6df046451ff267de3bf2138b61bdc5269b538a7e9895e59411a84bf147ae9e98cfd0cd221f1c40e9fa7383372a6f3ca5f88d43eba34290879dc7a9f69cfe3cc2459b819b61c627243bf58aa5c6a7267ac78cf042f46853d9b06c963057574fc01a8db60a3dd886596705cd280d543274e3b8f8e96623c1ac5c866ee533667f6497c312ad9ea725ab6f53478579fd871235a96181e1193637de84e80dbfc105e7572cee58b0188d7f1f6ef51f24e8dbcacc0232833af1324339273ae3eec10f8b0cde8cdeffc02c572ec49010786319d463ef2513f320146103ba3b82745c308eba9a0ea58266fa7dc08955740b5c605360b57c5f66d484a679b773ec9080c49501be08c87ebe00cd3cc310ec0ae14caa4340134e3a5eef9b4c21c36e17e94fcf1adce9e476981795bd72a191e668c416d2f6eeb6e59652a62465070740072e07dc0bd93a300572e1e675c7820a9d5bdeadd42b485ba43cdd82ee47b89784eea7d7fdf723755048f7938988119095cc0947cf74aae15538971cd6420eeb7b2794d3ef9813d40cf8a98b8a5cd9d6a71df5baec791c28a43eed7674617361f251c3c6b2a4c54e9a2e29b0ea59df48848afc5bb693c3dac9c9a9072ff221284f5072e64bba27a8b09d5af4204a1d095585448c88a8fed5bd9cadbe71f8ea42229a9733ecb416815aac34208f1f0e7b5195eaf10761fd2afc593deb71c8fa9fd56af5abb0c8ccaf1e874054cf0a8918adc21f11bf2a0c419553b0e3708ad11ef49d195d56a864e2985505c6de6254ff2510cb8d64b26e92a6d3ac67b319d5e32d79227dface5c25c03b64c6913bf325173c36d2152cc1489f1476c8dc60e44e3f09f43da99346bb81bda5b33ff6960fec2d43e099bd1f7b4be88386df8e077c3b663e410acc7ad58f8dd43497a8dd36afbf11bc67f2d175a9cf7e273bcff3b13d15f2fd043b157a4beef7b8e46e2cb9bf91c03413ad7f0a5d216f2473cea80b55d4471794b885d39e9528da09a8ad74522389c136a42e22f0d8484923914ab57aeba386af1f8d444b809b16cea816bbdffe8065142bc98f51921f230f4722d60799628c688236fb2912b29e1516f99945b4223faa67fdcccf204da4a22b27122ba4edc17cfcab1f659415385ce118831d366b4d4b93c98fbcc621817d24d67e8eb65a92a4cff6d652912d14965a1acea4a31a064f30004699fd14c8811df772468374cf853f463ff73b4ecefc7e2a1cef0d2507ba30806176286228b8be9cd95a6e6ad16e16c98643e05196152061f6adb5d6edbfdc9398f80226db2ddb9f5062748fcd91fdb9c5beb4d6328151629dca23fb938afd293f13e48e0d9f6a9db485912dd26802bfe8b3dec88c035fd47bfa9ef7ac9008ebbdf0e7fbd41399f93ef53faa13f2ffb4de7b16f8d33debbb1939e03a0626a2f91fef67be051a01e99ef53cbc87267cf92b9933a3d77d2034effdf673d6fe3fadeffe8608f09ee681784ff3fe3f461e02e9bef53fded3fc8fd116026985f77f68ae7d1a5ab017befc573ff3ddccea675e4030eba9ea59e05773813a2ad44c1fc7182c6746f4f3fee52110eebdd4e3f881c493c91cd61cf8833a288158f6a7e028ba9864b2679e91ad2b145d4b29e58b0a9e7276695a7eb100a7e020a5e38c1aea9e9f4535e35c629ab59e13706ebeca27305db8508a23539dce01ff7ff53466cc7819353443381c314e38ba38d77fe128aac21a8e327f854953fa1196118eef5f138e622b1c63b5a69921d6100e41f07e27794f083812e64f461613bb7c275d1b7a0e0040d9b4ec34da2686aebb4d7bb282c060b87bf794ee02edee6edadd9476373872b9a7cf321f8539d9d32d09dc6b4b8f05842be12f5dadeeb59dbadcab7d4aeb04ed579a6f485d2d23309db2c55a3f2d787e8e9672a72f0d3fe70d2cc051eb53aef56f0872ad4a3674c9f5ab7cc09b7190476f8a1a92100293e96fb8a3a8354a84926ca5b243aade106292e9d35aab8e1836685143982a5c706156eb0fb9d62f5799ab0cb629d36f117ed46a24268628a2706109125866b516e54ac549ae55860f4383951c198431e7c40cb6e97aa93db208da9466004388f67042b24c5a70a62a044393a002212a65c9dcbe6cf9c2a54eab4403112f6c36c460c06022d7748928c05e9659b278829100e36499258ba83c62f10ba727b4e5cab20516368f3fa5dca19fa3ed14789c436456b42642f6bceabb4f2993974c2514322d273c00859fe0cca950f6e7a3e33ef5db4b0fb73c9ceaa1bba2543887b660224666aa4fed806b1e9f0b7fa8c2c0e31ca20d557082a13a4818ad4d010593964c8fc8f4471911f4e7049a4095fe03ba47cab7639280c9c2e9697ecef5ad222929bb97dd6f985ce9095276e9495b949e38653adbc79c20384cf2a0603b09da4cbe6829207bf27c7f14b05fcfc969d1715accd101d71f71b2879fd3a23f8665ff6d0478944e3eba7e8c752e830a5dfea20c4a64f7d9b783c69716dbdb3e2549292500b2a5d6565ba96f1b121dc8a38b07198490ed1e065078c4221358059afb45e802dcefcfe19fe2c8602f13ebedaf17bba1a45f0fbad1c740fba04438ff948729d057e1e62977eaf75824fbb70d652cf4b721d1fd5a6b7783a0088f3733d92a53a6d054c184a60a2636d890840d33ccf032a74ed1841f1434814e2174ce06cc16a5218ec5feb5041e63fff37380346cfe30014ea5607c4808f12121440baa0d50903aca1160a2d86dabb556bbd99e5270dd68583efd1b06aee1ab7fe60e63e10d392738f64474cace19043583dc45e089007beae7e7108147093f80cc94edbad40c21070f1083ad0dc4116d282be18710f865503850ae6c99504a41c19f9b5639a7a4f425f09dcb90c8becde958770cbb2b05430d47fc426db7dd22d28be12f29ed6ed99422e1eea8975d38b4c56e2a84a951151b266fc3e4cbbe9b531a7ad98244f75e4b3d69690949949f63ca29a7cc3165eb88f6e30d30bfeed4dda5b4a981c17db395baa49c51c3454e96b5c67a6d6d12d34b73299de15adea52cdcd195a7faa8089f8a8ae0addcdd6ba5b5d64a3b9c929293f24ab939b53555069d73f21081ce10bd6d72a6f794524a29a5f2f502e1480d1b192175e10eb9dcefdee1f8fa1173987c569eb456bbdd71ec57e5afcb0e4af9d4e5e3ebe6da79524a29274d4a19a502a594524aa9d7b5524a29a5d4fb542b3cf37d18a86cac5c18a75836683c0ef42eee928b044770aca19452997f83b97524a594524aed6d5fdde01aacd68c67696ad4dc8a5975061c5399db6a64cc70d9bc6478121c0130c3456dbaecdfdd6c90fd3b9c9ef18d5073879edc49659f8181cb868deb06a76ce4fe8e86c7b97eec1feb3bed9ae9775d336db029a5e1b5b9a95123fb77e1cdcdab064e8b8667737f0700ef29695455cd8df355708ac320a536b36bbe8865595d4d4777772ef07c1fa59452ca9aeab5e54949b3497a299da192b2384aa9ace102bb7baae54eb5dbbdabcdd6ceabb4aabcefb374deacc32465e7eea56ef348c9b1ee8c47371a8f4a4a6dabd64cf95eb3943b94565aa90c8f4aea140a6af4dae5dde6a975ba3ba6b9bb53bfb91100986aab9d9133f65f5f0f6c29ad332d1a302d9a992ff6267405405691810c6a6bdd2a4e8d960694564ab3b0f645ed97d60be31b3b43ab9db9c27ea9f7d28e56cb62b1baae7bd985b24c9932b6be36d059bd6e7476543796565ba68cad3602b8e1d9dc8875ee9303ae034a847670894c892385060e0f2c0d1c0f78aaad0183f3e2e9a1d5bfbe2c0daf96563b64da4a3ff09922c5cde3a4fb767476a7d56e38346eb27c7a656d819c84d940a062cefaf007c3968c97326a70c277587f4d173c7ffc57e80a4000bcdb354d19c8c820834f47fd1a35d4abb5bfd91a2d0dea8b34d4bab442e3d3e12ab0228c7e58b45e18c33cd8a7637b1b1b78392d977f5eac4c242c66789b6c6021a12934a9e0a821f349cd04d54766470644f4be173f1daa176ddcc0deeabf1e7d801956d779988ad7f94af50d994fbcce0415c8c88070a9af0d7456af1b9d1dd58d8ea7a323bb4957d66e98306f34ce104e269b4835cfa46945c804a1c9ad5e304436b9e4709c5b643602b8e1d9dc8875310e3a0eb80e3af0a4f4b9dd003c297d068063436a1107081a383cb034703ce0a9b6c609e7c5e3d1ec49ad25266ff5822ac5faea9161b9483497c93e1df46545b2cb200cd9b639adb488a58f9037fa93903bf5034f4a1f1f4f4a9f248c5d5eeb8dcfeb66a63978824e0eb23f75f798f489c91d9f14088cf1c54e0bb3c316960d3c44d1f4d0d492e202950f543a1fb2b84b5d23aa218fddd460f21cfb293799281fb8c8aca7cd63e38a79f5d28725eeeb28f900743be83a294de48e0c2e410f2f5c2994789062edd74f8725c00e284852da61092d1b614daf88be3a8871b5e8a04408997e875d2efa2e7a058728a40ce145174e66f4c5e6a9405cb152e562af66091e4516f20eb976453a28c1c9f4ef3645173599fe96938229327d6a37a5211e32a55fb9e4e420e50465327ddafda083031513e4e042a6ef395ea26891691352e0f18a3a50d8cc8cc04d578c1607585b68a0c4e0021e9a8122f4c28a5e160f4c544d2c612a4e3e033411b5520af29c545328e94690f44141810ba9273c2859b8c07550bab80618c1500a4a18306c475c1420254c51624862002786a2387161a916258a0b062041541422036ca145a1c2845e2e3480134b51987819c53aa6650b316cb8803924b028304cc1ad2cb33cf9a08114d826cb2c4f48884cf08c2cb33c49914197272d32e0e2290ae604af90d4a8823f2736ca60d6908d21f08a080d76c038a9c613be4c3570c03335510b5e6599258c164c61c0a8410553174416b08d013606e69e9c00df285b4809e307518c510d1bb097651631546a1c65119374a41613b3c5d6a493392924302bcbaf5f18065f292fe82370bf510a5aa048ee14787bfbeaf7d519039d4722790b715a746f5197c5c4609fc205c4eda2058a5db885f108c214b1618c714be90616dcf09447fcb48ad32d98eb965d0577376f5c33601fa9c733a6e011a7bebf9b9e6cd93e02cdfea927f8738e965212a9beb08884a285312420a168610c5af6fb94e3387ffad465a8c54240e58e87f52be55e8242dc3bd9bd48909ffa1da9972ab8a42f4122cf41dfee0d456858e7f841ca0f600b12794f411cdde37d7f2ce6b21c6070151ef2289b7498b8269d2b0afcae16a58bc0c3257379cffd9bb70db4a0dc3102cb5047eec8afdd5feacd5d0098b08ef4997912f1a72111fa5eb885dfe22482df3f4ad5a9c019e5b04afb81c4b1c3eae3f7503aacfaff784f7f23a13ff5fd2922aaf0e77bff2dcc6971ae5ef58f55ac577d10d6ab56ff839f151209b2fa51fd785618fb9574588ed81830f4ab53a039a32c14a1e14f051c169ee0b0eafd98d7f6347c6d21fd1ffc1ed2573da534a4d1b0c8cf1c72587dffafbe9c85627b30df7b2b68ad8f630c7d7f2449e87b9fc4ffa3ef3d10ffef5b416daaf0b5d5d716fec0db130caec0090536cd61f53f1003e5fa9c18dc42f5c330b8fe6c9a4935e9db42da6227bfaefb0de43e9532d21f391b7881c7f76a10264196c9aa26ecca4d690946269d4128773f16c99386eb8144e40b8018439b11790551153902055ea2663fdeab422233cb0e3800cd8a7020050c41b38e6a0ffae5ac88e442862a42332fec0f9433d79125a425ba24c70b4573b2d2626f791d61e9deed515e774322ef91bcc76ba4f99130f34c016d246107da88c2374273e98ffb7602c3a979c6761abdc8a19a67742427538b1a3b6a49f38ccd25b7182ec678ffd5c3bff63bd27b91910374a11f8563913cbfa1644079826d942f28c3d85244cb0e1a41e28129cae40df49745cd33bfc7b778911fcdcde57228177605d1bc47ce80f2903bbd8ed80277c2118b39798f87c31ffc1f0e5ff3677e5e53f541583346e16bb258af7a396b16e84e0ee5b0ef3d509643d603112322ac377afdf03e0172a6fa9aa74335e0091f58019e54cdab7e944d56d0680eebd5ff1801c1ef7dff83c339e4b0a079e4b0315ff89a0b50bdf740beaf098918f9f41088eabdf766dec88c45f35f8df747e6ccfb1f9a703239ac5f334ce187745802e4cc0487f51fa9be6936318da999504d5fcdffb0bcc7615007d15ef3ab79ef83d4d484aff935ac67853fdf7be16b862f04785ff341bcaf097f8c80cc7ccdffbcfc695eb23e48cd4cf872ef83cc3ccd4cf8f29ff19ee669bcd6d3d474d00254bea505ba934371636067f2a5169de64beee454d4475d94db432ceed44950ee1464f5a3fad53b943b2d60155220a73477f2aa38aacd3ea7e9984b290f94339af43ad2d1dc21e9eebed7e5925e28a5f5012dbabbb7839fc3eea639e48efc98ecc529bb3bec965da48d7877347436c7032692e6a18f3f2665b247ce205ad417292515593e0a0e90208490a70e52ac40210cd5202ba58aec3fa5587fea604576b17e92303ec78657c8b54c762bddddaa6e7882dca92fbde46a240716f8a3c97348183dfac0989412e63547f697b0fa29b0eb8fecdd7fc20b914db7392f40261ab4e840c68b306ea8412cb14d7aa82f7ece5b31b48527bc58b550c33cc1b48277d1c2e54038c3ad6d58704ba48262b663b3d25a690d16f097a59210458c21c4942d4244b13428e91c2658760068be27440bb93eed5ae9bf8e00aa1845193d512962d43f2f9411982deef9a51941e132bb7928eddcfd7a97a500168a64f76e760a159986f386e3ddc2117b8bbea82c042656b64d22348f540a6229cf20bce45492d29424733ad7c4a9e3155869bd7a3dfabfb3877bf94f2499c8ad9e5ee656cfd4864cee06b2c78625348c3e0d65c3c601f08c2040691ecf80a4995640fa804e7d053a54c20f1e4bc6abc210155aa443c20a04812f7d2f2e2a32a514082fd1a73fc42954ade17e377b7bf9e9f0d1dbc66ba7bd96c6451e71be933623df9f136886938684472fdfd4bd9f0af1a4f590835d54ffd6d0fefd8e227de41d7bc98ffdc40b7b68389f72bd3fadf4fd09a57f7669f55d5cb66071519953eea7eed3200273299003abc0f38fa84dbe3f6fdfa121fcf23bf768173cb7344f33d5ecbd6f5fcb3ce21291efbd6f8ff09422af0b04d8887b276dfbbbddedd29f9f2c5f51facc21b973ffbe0264cf8442c29898b46891573ef8401441d0c686c5baf77edf7b7f9cb4fcd7730d6dddb5928142fa90c3a40fcd15cc60aeb5d65a9fbb628bc0435ee7dd6ae55ef0e762c04745cde3ea82bd86a5fe0643f64c2aa496fa4c4c5ab4c82b1f7c208a20686353b93c674deed4af453e44471f9252e4ceec41faf4d76642dea85f8b724861ca9d8aa536538bf53beca5166b95d4932e0bba1916a64c64cf0974932dcd5b30ff205b876cbb06dc2b8041a579f05bd6db6f26b2c785ba903ddee430e943b34dd9af35acfe3e0b9c605ab48f417b05db22b037794f7dfb2e54e59d496a81d2619b17712f7031e0f9d43c76c4f387a25412b875381572c73a90f441923e7e8473c081b43890088236360e2404044e026f368923913bf6534dc224789c4dfdf3de24202bdb5a3b9de40e7d9aef4f02a3e030fbf762ecae1702ead30f529f86488064b3995772672d18c038bfc8f6bbc8f683d0a709914c27c9817d25773613a2e0d65a2640b98588b2fdb63fc14481ddc99b869ab2c5d247faf474923bf6b3ece91a562061f69998b468b922af7cf0812882a08d0d8b85b36dfb6058fb36083c7ad9be0365fbd6eb99a00cae0041d320041843d90801f6c068f0846590400c836fa8d0f0430f5e7e1012bbf80106b10abc7a0116633420c236592a159d40dc01d764a954c484175434d001b3b2542ae2c1865091932745405ac4d8a0c953161867a944d4e5044426d8801605080606930649b863a286126c97304289e8034c4440d8d082b72c958878f0614eff6eab57473a6520696459abc9f293e5947f6c88dcc98084f573b1828c269cbdae7379333a9547af87adfd6ca5ab16f67ce5ad4a2ddeb6b0042379ba2a75f9f43a4ecea7a33e6bce1c8b43591c48a704e1d69aa0a6bc413d29592d55cd274ef73c2a6578d2a3299b1befbe365b43c16d9b7f73f391306987636978423a413940381d9d8e3f59432303261e0600e87e8f36a98c192e9b9b215907fe8694fef0b1e6914a4a185dc9743f980274c515474041c152a58244169dd36ab7f196b0b91893f993c78e7443cafbde5bc311d370e6de5bfc28b7f42479073ef83043972fb490c169e61ea81285cdeeee2d031e316e9c7657f3c8dcb31ebabd79db7bd2f3878c6a356d4e9f41d3c1f1a64079f8903d43648f321b903d3c96e691251a1275df605765e50196d0b0f9dc93710ee540bda039e49ec354c90c85f69caf21dcca5269862a799c41b98416271641997beefdab7fb7c999021d227de66637ba6ddb6fdbc681258077ca56cda0a01fd913812119909e0464cf1087bd181ce571b3d611ab40f9b5825c0a747a9dfb5bc2b7433e51f70d689ed8090d6891c70c2b37067fc63a416207d528d149efe92b5f94116a7532bbbabb7177775f8735a6d9eb6eefeeeef65abcdeddded6058da4694de48efc9512980cd49353d214ea204a29add45b4a995c1aa294e21a9e2f7f8eb329cba7b44b8bf273b4982b78caaf1183e5d734a5d49b524a699d492df250f711e0299b5221f49fe60ddb2f3b245264e645ba6750e041b55aedffbf56abd5fe3f6989e9ffbf56abfdff77d004a189f4b964ba073b3d413575116e72393d54510bcdf0ffff1f4c172e5b269625115ca97708171dfd44ba40ddd3b55aade8a8a8a88b7e740d045dab0975a935d56ab55aad56abd56a35ef367501438518649eaaa8d56ab55aad56ab79d01fe9ec745aed76b9546767f267e77d95e33e15bd57b5f26da3ab5837f32cd9e4a2d39680986a28788f283a2c0578662061f55960fd16f22ad21b8a0cc8f6eb513beccda709824b9ffbf26717f9134b6c224d2bcdc3bdfc7ea6a79b9ac9752fc62ed72ff912153ca548ce225a4fa12cb97f9c4eb2fcc9448230b1cc1bfd92e674c57c856aaadb50ba85baa4165241518193502fa95a28053912d63d0b94123697963a550bf92a945d03f2fd2976affa7a7c4e85aacf87ab64119e3fd4505aeceea1bb86b6e22aea1bb078ae610bf50bf5ea1b81fa1337fc277b0f9617f39e6d06b56a0cb0102247510b5ce00596284f3851c4154e30e960adb5b6061f5cf08219c22ca104083ac9cb096e1212a37ac55092263b34f1a4cb17274e6c5189b842cabdc208870eb2b8e00b185b64698111f5083741e1c15eb1c24cd312375ce1845382c3115a94184af08014461d02045c962748ee155090d45a6b2d81184b0606ebe58a1a85972c668a349822040690c00287114c11a6c9163329a7796e9e33c9262182cad01412601ebae01b9e10a203415d82a3329f3d8284e65176c90e45a61ae571d226d05c32859c8825398fd34af6a67214953d004d8628ba72e5ca0c2cccfc633c4e9072e4055f4039e1441033ff19348b3cc9aa8e58248bad73e0459dd8d6850a76d8c24b91951cb4f8a18bebc08154134a6e70128515485e70e1160890baf9f385807bbbe97dbf79de77537f5f08d86c98fa1b522e05e688b5d31008f75dd87e432e1c63a1c3a6ffcf7dfbc3fd06e4be0d8170bf85f8b3c02c937f8ce8ffd84ffd6cdfbd7cff5e08b09f7a20f653a10864fb2e7c8771e06bfe1b995d30f5f7132067ae90080e2be46cfbf153b65333fbdbd726a6a47207676fc0097247be6784e7c75a942f71748fe01987dc915fbfc0217da8b4b96a0007e562381a70824c862079b6cfb593390c662061dcadc9a59e2c3dc81c09997ad0352a1a19302053ee3db04648a63f45ee7b0399faf07cbfbf03784e4d231407acc608ee4d11ab570f87f4a13e3bd596872cb616ac68e10bfd23a4ca6502b422b43801921034e952a72c81258621ea0b2e822f5c1430d45a6b350192db4215a0285a40e22f7c0ece52e9a8288f1fbb5e0800093cbefccf1175ac064af866a97434248e81b72c958e6a68131c31b9329924b11c1df132c6568778ca5d964a4378916189b5d6da301f688d59d244eb8b314b527004e83b526bad958b0e1c145c8078a116610598302ab8428c099284f1c3064518474a66b8ae219686b052a543681e2ee575aa6f05f290b0fb52ca5b94650f7658d3af3209bb72c9089228e3910273ae091cf852bc2fbf1ed4f3f6f8debb59db63ae902b5117b61ad65a74a28eccfed441c47aa7bf43e6eda6783856ddbece3cae3ccf48f6c231a65219c92a8cf11bc9d88570bf92592564f51cf72bafae563be4abbe7d16a8d330fb1514b9d48b62f35831d63cdc5bee6e7fef9c77daaeed169dfd252592f288979440ca7666bd68cac9f62f879f7e3a2c70819953bf43666ebbb5077f5c0a9c92b2147024a9a3af7a5538aa8c641a8e3ade774672178eb1affbcf48fe7aacc6ef57a191bc0a475927a4be4a0a517d55a55eb5a382ae56ef90d95efbd95a6b3710079939ef9d3766a1aab413cf5a67752ab3ba2783a58023b516a50bc7f2c764217c9db34e20e89c73fad359ebac4dab774e3178fc1ca7230e32b5d6d818f27ee02bc834ba3fdf2bad94d254caa6524f53a930265bede05d1f62caa72395024900755cf04542cd1ee851fb77bbee479c5a4cfdbdf47e4e419939b0befcb617f5177d9a690c31269c20bc796013d162313451327b05f929229d88528299117d2f7c059929303935cde67b2ee6a6eb240a542a0bdccaf2bbf995febf101aa02fa8b46df844b5643403000010042315000028100c87442271402498e8c22a7b14000b778e3e76582c1a089324c7511404418c318618400820001162184254441c0020516c50695ce0c5a843790fb716cc6ea340f88e8727bec67ff44a591a7d4a03d016515bb9882701a944cf604088a9ac816dfba7aebf6559b1951f11acd87833f99578132e8b05894b0396bc3c14fda0d81c251ae4becb11b87d6df5b3b22abcb0321eda997ccf0553aecfa2271e5203fbbb049c307d2ddd77ffdd1754039b8b1d2194851b34e639d94b16d7181c34417a142328fc0043766f574ed165c011b932275e5aa5044c2139dae091585a8e578a2d1336986dcef41d8a343a2909289e66c6fb733806b46ba64a4123c90e2d87cd49fc6e03bb9a0322f89e04b7144b6a4664b0e235b373cc9dbcc5d5e9d5fdb4c8ed074022da12347a37f75f2637d809ff5aaa49c1207e4dc0ff36febd6f733da9323a466254a02390fdc1ab8de241832d2227f81dc8894000d566a6afda11e08d31ac4a34960b097c22b653a056ec1c9659af2a2c92d5f952168d6d5986da6a861728ed695f7c6c98bcca5dc585b20d0518e6d7556507c6ccb4a8aa2567ab12d6b5f718f3e062835b4ed068940d137ff25650e8471bd865f927fa139ddf988d7bf7af073e58f322439d70bf6c7bf81e675361d6008a9efe03c9fedd952df8c548de5ae9927cff1c0ede0e57e01cc7b5c0299081af12e7d99a239d46af049179cd0789f2245cb38d45bf2015786f07f8e1a444d1c025635f92dbaa09a50a1de1fdeaf02ea3ad1850150a7045f6e050e9a2af0c47432503e80f843ffd477f8bce44b7a47a9ec4daced89ff4d5e0b1d45d5fc0c678f68eacf4b061d677700538d4a1cf10b87e09d69bf802f9217aecc4edf23d0ace0ceaaac108501a41eb2d5ddc178b53990499e3bd7b804cf315ddc373618138c396eb1b57f2049a2483ff4f90f0322dd830fd256ff00c58a483c6fca47dc0dd3bae6e76f484a42bfe1f7a8d396f725d1c9a65527795a846f3d77abd5516e4b1c1648c87d555170d21316494f28c21cc8b32eba08697c5b3e01b72b4be9a80c61682a1861b4266e0a026a09947b0c78eed58bd7b674232d34f3ee7f09b099ec69e57efc7cd1c6a0907d1d0b3299df18240ac9ed79905926e52d70c2b08e8326c3dfcc503b8f21d7e589eb1260be009d1fce33223a67972f8dac80c55953eb04bfdd6b53088b008f77a3471df79b12c164b189474cf3efd82833d3db6ed2baccfd79e45b364011f2ad740de9a8407745e2a8d7de6218527b48a82ad9e5979aadf0ad542059db4e4eb07f06d3b5a0f0311f719d8e85d7f1080d21523f8b68c310950d4300d1814d8d7b32127e42d4b5553eac0874ad205acbfd234958e4a54458423f57cba213eea0326806bfd7a5852a4fbbad310b100e89ac16834a27c0019f7406e91b94b91acd056b1e086f0326a5e1fb51a64bbeaa56bafaed72b597588ecc825c9f9f1d18b473276ebdea571e43284a417ffad8d58c788d207cdded3c885732d27e8ba41f2c5a5d03f1765913064f5e9dbd73d28da823dbdf12dc756b1106e6793d369c239adfb7dd33f786536e4189798ec9f9ca3cc61a638e4a5ca04083d6548ef7bb3e1bab03d208abb117d6662c2e16ed826661bd35e3aae3426342d13bb8e7c569d7d719931ec6d173374fb16bcecf9b05870168fe1508c6eda0ae5867260ed3cbeaa26c4815302c657c4915a5af1328cdcde240804e073031cba83425810c6b8013a379c53b30b17e3527a427b35937213b184d4132e9948f5438601f71afc250e2c874f691eafb9a1a88252063870888bc49ccd9e60c85ba3651afc0b875d9e0f69f5910d01be54d96a5bc92d9dbe7c20450de810987c49b1ec4a39c71e988f3981762514d65a19446c0ed02a648abf4b43e7910c4ee677dba24382c020ccfe9f8c90c81c12fb4c9a675a66fe31cda40caf4f4698734d3d8d1041ab89e910b5c700634d049f665aeb12d6fe1c771995dd38518b11a01f0fa0a3ebd4437b8733e2628aff07aca3fa00d3b69c9fbcd178bae094903a04265bbc15b8b67701065c920096c17a21aa166da90b79884decf2d5da173f54201b7919997b71bb9ab38089fdfebe4cf5c88ad97802c2ca559d966c49b1caa2065e3bd18cc909659a7da867a78ab4566445521db5e200f5698186f40151831a1404ba072a1cdf1ba4319e93458fa4db89c9679a2b4bbd895ba381cf36372b3dc9bb3500e6dd863033007b6874ab93f0e2d795e650d0d768e9eb827b654bc44a5cd6b404ad7d646d3ca8917eb37868c8174fff3eb4372d147d9d1ff188e24667fa9ee0968a7366c10e2c4cbef1adc26085982e28947bbb1c907cbf8363e5a3c4a5daddcf20f8e96edd4c5060ce4b4d5102f4dbe2cfdc6b523f559a4b7b4df7592db24bb0fe1038d4813adcaa8b42055120979520b5ca66ce2dfb229f4be442cd08941a1591ad89622a79db854716fe4a94c5faf8e129cf94de03152c15338c9c68a0790bde558927e4fb6f28b26fd68c55a61a1a8906bd6302deedc17b2b996b196186e54a13cd3d7c2c407a9e284ec8dc8add9b6fd1049892eaa0451f25be43898ef8d567697653862cc6bc1628fa5672906520457d2bc90a4df8c8ba0d041aad13904562a33b797b1bf06b63a33bb5d9602cab57fa57b4c9c729ec3691d09be1f09415dad8cf364190135a7898d270a88c1c200671b8e1c767971b892a02dca774301a69c277232c08c7739d1a0ea04b05e44f67a84c08d1900afa40c3b704c9b44bbef45b57eec3fe374b339c86451ce7300f0d58c0c92cfc22850e9c33d3806099e4dea1b02589b7d8a2713799084703ca7f4e5ac476d70fdc7a6979f264d810747871fca9237808392b5bb4ca721f4c99a7a82070c65e7794c4f35bc4bd1aa66fe42e76a6de91544f5e6a63d7d77c9e190d0f5c00c91a5bfe856dd3770c5e8b44cc5a6b145eb348b578e9e2c74c6b9abf4e4c67c473c8e91b57f595d54aa527c8cf69020f5c195dc4f3cb351a8ff6c5aa761bc46aa706c7a7d915363791a760476223edf30a6ce021b82aa8a7c3aa20603d009d37640a3d91e0e26b0c0eac4d5165ac679aa2884cec34761f38dbdd1dd2ec38dc3a31b4fb88812546426777cd3f8d24dd4840b301bd1f0f7945cdc8efbe6fbaf30fcb3f6033def57b0520255f1126e4605fb55d2544a1d3df0f77c64eef6a49dc46d89373c6989547b44d06925186815edd9cfb0197be2352f67b36f602d480af3c620ed5a206320a36b1529ce2d9b5038061bf04e5de27b0eda846f6623c4a221fd76652cd83572c9f2391b4600e92d9e3729e0c005af50d79013f851d7c98144ec7d9733a329861b046ae76e4d228129d8d46fcfeeea1580586971509264008285f2bf81c6dff8e9b3efba76dfd97bfa8fa2400e1b00a77a75e2278d42852a52d3037e8897d437e1afad513ba13b24821b0f604640821f003706f93e6f1936c0802995ec54d54ed25b18e5a9e4877c72b7e3f9b2e61f8041f3cdbdb019f7f89a0e1af3fbbf76140ce2bd03b89e16b36e0d52b1704d020a129bb7339b01ac4398c614162c0be8b55792b8cba87f93bb602ef2ae0f499b9191132c82edaf336c43a81e142947d037e217cbd06c9256fb496e75d4cb0a48cedd2cd3ce05691a418996e99ef4734b59626ff43a8677616ff544f830985dc9ff8e4668a5ccda95efe497fce8b890172f6ed0d254119ae31f69affe67fe8251055c84bcc7c1cefdfc6a007dc9049518ae28c4e559c733f96acb5e5ce305cff268f0e234be81045a5e9ec4530f2ebd060c813ccc892bf88d395782fcbbea4ecf2f9e95f91d090f47b2c9d1ca48058de4fdcf60ce8ade0916b2cfe757f6feb9d2bbd9f2f66990ff89d46eb16f31827665765909e4c5d2dc21d2706cc273b9e69fca00db21273ed7c06effd3a3de8ebe8506f5c5626f40927f7b3061172ea62c176c449fcbd5c9f41e30c4f8818017b9ef54531cf731415c7e875f283e601d9d3234f4fec46018ab325e6e9fb0dd8bd58f741a7f7832f8bdb81eff1b8e30bb2d955ac7c7643ea6d1a99779963d6a4d811629cc1975b20f9af9eac32c67eb9b67ee3e18d48ba7c6d8cf7ffdfeee7fba579caf0ccaa756587dd14e6aa8e49e8d79062aefa82e723228a2aac0da806056f60346e1502c376fbc55e1c7223245e9af5b09312f819981dacfbc1105996e88f27b6d2a260f2b6ab12d9b3386f80d78b0951874f8cf63133dc63bb2ebbcd76fc2e8805d8592c5685ca52edbd920df6a4b53718ed2d791f9005e00d1ef679edcd3ebf1719f808bc9c700ec06ace9efbc31d32fe3d033560c3a88cef583e0e2ca13b574c13fb9b297b9d74effd5a4640313abc947bcb48235e330b49da7720cf9fcccd159616af63ab41f5bd0478a8a602d9bc9345c76a1f2c768d0070bc2df3b9762e401c97e8bb2f1d1c6d19058897f729c16fc1cd33a788d110cef8c3e94e29b425e7496b49b33c89321d1774aed8e672925c966d7903d63a11b64323fd284267a03a1d5fa3925e58e785e51b5fd4f6fdad90eb72e7815b31d30f266b8c2f81da0d6983ab8c9e22618eadae426de7031deda7ca17804ef99e46544911d6a769f6ac7b23e441bc96319da1c8323726704782029267afd29635a373c2f97ff12136392efd0528bd44f635503d2b6a7aa3dcb7de011973d9547c7339209d46e40af776701ef86499c7628ba36146d1e12433b552fe630ac42bbe30669a6e871ef3d47e93023ee883ff5300c865707eec9ee9a041f818cd4188046798c741828b83c06ba351ac3a22b6c5d6e905d38ae6e37c40f0eab05871c69e0fcd2a190eb4311cc53219b458caef1b33cf7a71a8e80fba422cc9bf201ee431f7a0b6e77f411ca25b0cb22cdec8c9042bf84d26ef4de0256ec37278ac8722e894c203f5feb8ca2797a02f9d3845afbc130b3d9ec75f427cdd27f7d0449b07dd2a95f73c79ce9038fb03d2af94f4745114b63693ed184d4443adc1c26e7d5f5dc574b4ad1ef626146834229e5c9ed63185b7ee020c950d1ca715589b0a95cb61a2637b8bda10120a0335f616e821e4c2882b6a2bd8fa25bfa88924d14d7e2292e54eb4f7233ace3da17fa6f32ecdf3bf3a4794438f3b9f832e73723d0d393d5596eb48501bfcd3280e11c7b70a73b08927a43c4f28ae722771f07780a46e16bab5c3c937e23d8b55397514cb3a5cce89459f55b72b4088b91caab967ce8fb007747b3f38a3377c599d1fbfda1d4a7ae65002face8fdaca2b083553e87de3f5896e02805156e0d71ee5944fcc450d3938016013a8d0d709d97fa9948ef9207cf2e620eb3f2bedb7529e3901a75719cc88517f5102c4614d6253d2bd9d7f3ef1a96687742503a0360a71dc85094c76be8cda0590c17f0f5856b6c8e9c04d9816f8fa53b6c8394a6c2933e093dfc453042707d7f22f6aa79bc4e0d6fe2cdb17eef233ebdeb8570bbf30a952a69d90df002c1a0480005cb8a6a12713e7de42142ff5848b3fb7ca268e3e2a3b68876b6393064d894b7326f428824b0042c324be77ff4145704f90978a4a6277695ee7ac18805e2ed13220491286fd1039bf11f3b1610a83beb79f175b014af4ffe380c305f36abd6dad40e8e3339f4190bad78145bff7d0ab424c4e3724da2d3e20512c82eeb3075a5f26fed40f12974b1644aafa62b1e97558fc072255533367b0746defebbaf63cd091f31e5b1f0a80e5c22c4c34e1ad68eb0da9b6e09e9df2e4b0e440f8f10812b210395bc14e20474f06d54d54ad78ff27565432b1ac53dd1d64e3d7e9ab7f50875a3c330e759e76a60d351619070e8a1437715316f07157f0333351c899deb20820ea568cb675b71c669325247b479216461feabc23f2797848c42651ab3f4976d9de701e71d5df03180524ae6cb38a5c900f8924638d9271e1d4aee733217a2220062ef1ac5a6bbc00a09b7bcef4cb41146fd734fe0021f35c9287fadb76cde9ecbf00a4f8639982f1e20e837f12e970b55d1111850b10aa575306cc6c88937e41ec789a0f81da85df593a2c7ee0d7185a37efc0d726ef8a8daaa73a9f1b88eda2da7a2624a503349499692a1686c99990affad100801a63f31e2500cb32ed3edd8d85746625b5d94d4225971d6647cdbe4bd87d8bee7d788c2e16218e7a432810dcd67bc6a09266c149f3522282231aa885afc0dc742dbf2a0a645f40e10621d709016b6dc03aa38218ff47f8449dac78300e3abdc899cc161122be8f82e7d86395571c8db99ddfd8a92c2efcd3c0e9c084420cc8b2aa7d4846800d08002259b3ac371590cf50b80d50abae4dadc004c6fc387daf60d0f73e51a4ebbdebd725381e06c13d8afaad461d4bdac6dfb347e95fed32dd84707d07f32ca6244b938a2df10461f6158af9b71d3a2c966fd4e73dea272950b851a9846c02d36ca02d0b4c621e92e8d7300e05c198977b57e34aa1f504a2cca1f7609b776c55828c90c35a604d1f1af9e1e7815fd1dc88852026cd817d3d94a7aae6155ff30f6903703fc50f9ff9eb9f32fe84aae19c40179be7c0e0d927fcceee2312394cab7500696461cd20543469c71922e4c15f3a2840d5407946cb9c13cca6667b9bba34ef5563a2fd26b26adcee9eaad40c506deb068c87c28a25ce4ac7193fb1981c7e03c56ccd12bf8caafe96371002092766ba901f13c105ddae20b71281e304e62c78ef5ff360869eb60b84e979baf76ded16721e89990fef9757c959cf270fdc9007a92d0fe5860f744b21dede2b55f9f9facbed30acb4f27b4623add5a6a1bd44b4074fd531a3e8219129ecc41d81e185dc3b0467fb2b3193cbf141a8f30baccbd51f155ca065344ce171ace7d4a80585a11a6c073a9e3282a338de6fd909d7a9acd2c9685806d11f6b7a7ec843690d08de4758ca4c016eba7dc9475158dd36f00990189bd5b07a54f0256b2b59f1f41a361447127df7aa29af93fbbb1ca757b2b221c4ff0b4c464656ba560250a491a1fddf9344815e6eb19820539aeb0d3aa57ad05424a6be231b5624c67f5a3d28fc18442dd08512d909714dc8874a1c284a204882677679e184128b933d9e96c3a196bc83fc902ed313405ad257de8eaf0c1ef734314afa34d22b70319cdc419cebc698275ad5a7e0f7d0e3ea6634d65714e8183f24c12e8eed247cc5246843acba50287daac8dca394037d93e7e675890364fade86dcf4d2f603b7ed025608e698181ade8fe74a183a460f203a03301d1b6d687b5cd9af18f1266299f29bbebd3d393958679948eff17f5d158dd1457c15827d625e69fb0d0994f255bec67998c9ed902f3154110514b6f2984cf68f5cb7832b6808238148056ba51412fb86afb38d65140d111bf3e1a0d6b5febb1cf09dee7ff98f06b893b65b05a6871d696b1708848668c8621a0858a0aea2316c3a20e1e9346114f45f2c7ab3ba6529b167b7dcbb1f478316de045103127f2830ecd763d92e2372dc5be9d1daba5e0b5dbec18763e8256adcd88c32cf1623a6f54e8bcb99bad1a52ad75c164fdde84c114f6257022b386c038193941422130a3cfdfeb95c416c53b25ebe54777de417e7a79865af4a207329b3f84f366d0a115a3919490b917ef14941df2c6f27cd04f0aa8106e628e92c76b386abf9a35580c325f239808d13633d2359c95f935634f16862110ff3dbb64eda2e78e8b3408b41df9a142ab8e066798910c42a2e2c86f778a7c4c39fe0536876d398fb901f780516ee6f9ef91c001a22391747873aa6dd654e70d66690494da4be7e5a218b911f7c3464ffff6ab5c1ab906105991e9bedc147b01a25e2358f3e3eaa9f7e2cc1d9d06870495f6b00a236a19e17dc205baece553bc2f94b87286bdb113a4d907e11fb802b7fe0dc3748c6ec3ad468e4b3ed691b59714e2f8d78834f7b986706e245d9c5cb9c345d7bb1f6a1137232585459feff0a33b540bbe0f55bb801b70a63ee3423c03db16171fb19821ea326dfe2b6b058de401699069f75d3126a97a81111305bd5b336deb48c95a6c1d60130176523c1a6c1cc658912e047c54912e0da4c0514ba39f9e0cc0d06cb97dd6c367b37d6e4b20fd160b7ab8bf4bdd8fd4ac9920bc6742307d7814a0d64e44f9b0270d492111e8cfc43606b7f509b96ef2e9b1afea5e5b380a4cdbac54b35c5d77e0a53649601884b4e9dcc7265c8e498f944bd43268364c9c57306939b6bc52edec8054d740555c151c6b366a55014353356c4151efe858e441b2afb29f095e4aeeca0a9ac9c91e50b5616c75e515fb0bf67b528f3938124b02d10729b82cb0f88bec6836f3e3548a278eaaf5e6adce52bb018bc2404dcdbe27e4ffbebb16e9d44cbeeddea399ee4662674a28a2ed795576d3dcabc62044de2618792823058d130f0f1509dba8dc43108731970f7dbd05fa5ff17b93b6ce01e5141f63c36b34d11cc29939564804b9cf98fa249f296e16bbef9e1a791500b98c12d3713c4ac9a4cc2366519616a1fa155cd1146e33d8d35c111406c1e900cdfeda1e2bda1602fdb3479910d1254cce02482bdac45fe2188feecdf23492ab460f7765237debf4fa8f7440004671866716f439c3939aee079ae57790828fa349bb9d263a6fc53de1aa65f7c8343b6522fc59a19dea1efc54fd92e99485c3eaa8bc392d5adbe0d2c742cc64d08d007bdd0274dfbd7e4e16846dd8fbc017e984929526eda1fe97c23e71d8381d45648df37bf40dc991d9685a022fcb2430f73a8d1b755e18dc7a335c21f25552c127023ff180623d74fb8dd498c7c0c53986b17384ca4e0b1a67b96217030aa7a24d6c39dc41cb2103491bc06e0cbf638e0967e4d8961d5c5371bf354eb9a6a7529db08ae3ecd5bf60a94215cac55d92a107263f92e2aa3d2f82b120e70737471560f7b132f5d6195b82ceb476ec9a9f86afcdde12742402a5c9b227e582e934e83f8e04cc31d5072c195b491a00ec454738a82b297e36e1b334539516d6f86356708a0ad91bd17a3e2861af30ca145f18c7c98b646ad40de9478993f9918c256e14240ec424ff2200c2c2e9ee77c057e3cedbc9139e2b92032df6e4764cfc00cb65aae672077e2949452beb6341536ef657f79690eaac86aa564a4ee53279d7d0d1bed754bcbc7a245f24a7af98b0d3441b6a1916bf360ebf583cb3968289a564e3bee2055ff209388f81058b852e52243c1c49d7bb9553d1a7b649ce25e6c4a9c335932c39808f37026ea32410be16d7f918bf9d65221268f8996c39bfde4374b02342db7ac237feef2f9ce33994f69f633361f40cd70a9e6550e6c6c6336706df0950c26c55525a9f44bab34ea4a8afd1217c9e9d685badc00a47e5b009e6b15417f3c4591815b1640ce00f2774ba163c94449cef009505adc4ba48b53c434ba1431f1f818916286484b6ccdeb4f76c9259676a760d0f2e9c4695fecdbcd208b0e41e48ed125ea7a28d9be7c7e9565f5e8c921f2bce7c1fd8b03702d2831c039864015159fe7ea8654fde8f5fbce48815bf8955762b616da05f0e0887f2bb9f543611ccf635d94c3a751da38183f71316d079c35b84d7a9e628466c05a04bdc88d3979fdc3fd67c08e578541e6bade0e3161a7a8670a9d294c18f38dff319506a02f9bc8ad80b10d6cce1fb2e68f083b03d83cc02ed8640bd439808fa0e902e9b1bdb51c9db84a41534b246d4e5c3ca981264787129b1164a8d6456cd57d4187f155311b785b7f2de477dd1147a6c114e445221a6ce380101048e1501d44f5898136505f6841ce181730c0e68bda980f9804f9de6be6d8c04235a470124c35c3275b6b78017d61b3f6b8d436161f09091c9ae7db61feec1a27c02bbef8b141dfb41c8e1e268841a225e1fdc6237f6fc61461c18aa4161d06920ffe6744c87bb25d75a6308343d378e13eb44ee71f45f81b16a12a7f16725efc8cb585028e814547180d708f0a2c98d102e0f5a93cd99ac08949ecfc28accfe672a8b9dc0b44fcbad8fd1d461c76a4785d57f3b04ad5f994c4f8a3f60829484df216ec4399961eedee8111bb59b041e9232cb2e5993bd508f2dd3f02caedecf428c0189316ba87f4fa5819c0a9da6a8396dcf9874c9bc4455af3a5263274cea2697faf1f10784c703a3a8079ccc0f26eba12497080e1e73aeb9a8f995a63cfe5a93ead4e1d7ed9921c0c21b78af7ed9d9d516ac8d00726dc9d5c7922829ff2654d64ac3df22dc3e9f27f78b38f3f6a80d63e1ddfeeb59c42768c893168edc5435f85e6a135ca7173c5eb892df138fe7099c7c8829fe128ef49ab75ebc582d3fec530e87b688670d2b3f51a59b437289555cd744da04f10a7100f8080d8d13915d5029a6771b63da6340bc2c2b6cf1e7d87b1df146480e60fd6c53be531c02d7f31a4c50820e5319c61b4931c81b57f89064d8a1f5351c85386f431286892426ea9e95c099215f68c23817d38315e580e39936975036569855f2024f0f339652b4c51e85c75740ae3d9fcb882437dcec022489ba24b4e6993678fd4c85cb6493e69b4ac192fd4f77afdd3e09c51749d9b93a75d90c5ece0c6bd9eaa3a37a5612c74ba301f3eff94add6417bd2204c220f90dfd1c927d606604f87ca2dea73556214ddea13d917414d96099315a11c3d302b2b70961aa432ee50dc165bbba49cf4481907e71258e3e5a0734d19c2f008206abe48c8ba99067c19fadaae9a0899c96abd2ba3ab69162c91a215d5da5881e614f751c69a74c5d8ec0541a44321a03bf1bd64ee391915cd5038d361e37ab2792ac553080363067d2aca54e2722fc544d63423332d8bf308cc08e3d08c33f425ce8e346d51f7e7d94c3cc1d1b7fa20bc3b0b457824f69f89454ab1e1384c73e0e64fd7801919cef697dd5c24df6385aa71062d7e3a04952949e71af3804bd0b3d400cd8cf99a4d4460f3a05c4942d15477e4143f86439ee2be46286be9d90229b1d7881036e0aa80270a7e47164d5b8fea73ffa9d7657d06ce2b364414c833e08604ae54943f49c1c4f7c89eb0bad3a643f4a7bbbd4814726d02b7bc29f6731ed9445a2060d0938178914b0beae8f7e099838356247ea574a3f9f003858424155aa5ec5f318ac715bd218531d2626e1eae94eb99eeb1660916db862ed52f8fbcb132f1c00e3c1376729e78b6e1d22f022392c56cb67974b5fb413a1cb33ca5dad651c35be0ac24951fbaf1c6a8b0e93eb9714428b36af1a3c5f6995523ff86ca18ada0e1b6cff976b0cbbdae3162c284d0474c76219d3142c86f1a0f83351ba91fc487d863d747aaf3b0da2d72cec230043712e2913822629c2d91c62a5338c173fd7228d4769866bf789bc1e85307bc81fa78b8c26c24921b415ef83f2ab4875353c289c387a9e613003ceca0ce96edc0430c8f7db88a81a27067ae909a2b2cd2c8b292459df237653802698c8097552afb80ca8ce6c782812248c4bba259aed93063395176eba28cc17072f5e74c42e4ea9081ee104b169026353b9b976470f4c72e6fddd8f1e26616b5677e805d3d2ac71d51f895544efb86f0793ff0c8b699ea4626fbb2336039e2fc34ca11fbf1aea1ff22461fd60bf7d86bcd6f8f9ae30059de7583b8205e240f05a6f43b61d1a23545c3d531801e60f5c0794345072ea829a7f5084003caa1404b90c9ed84876b9c9a7499308a3e0241a4715f779a33cd5f302756b22df7357fb1a26bab7bf02d2aeea1db427a784fe1a9d878806a85b1fa13fb44416ebdd04746a4245ac7b06423ad9b21e193d5bb0ebd73db5fe8db522014398110173f5bb90341e0d9d38ac4f0990d553be5215d6da88cc15fb5145562bd38466a40dc320aa757493a26e4bd8cc12c5200f4c601ec99795c9210c916bb8941452bfca5eab46cfa7f1f174f4044db3d25c38b3349c5675e004c4d29afccbaf0a363e9610678b55dff82947071db794d94d186aedb80f6ae695c2194f3c7cae24d635670899d996977f0b4e3d4ef54190f2e9eabecb611d8e7836551dbc35009415cbba97e50b5a864094a55b5f20bb49b14d7e15f64cdb496bd219f8267b205aa024299fc8a5df6a7f3f7e94a12bb25854e64b6e281aa943854dc3c31a41a6450c3e1207acf8af9259f792fa99350769d58b6df6dbd1522e7596b6677b66a3f76be6f030e06acecf5f68d8a40c61d20a2bb01c22d8006f467573cc7b600f9bf859aadcf1efbaf2efe1590c3541f316428d05647046be359a52d6328fecd3f17c93f8f6847b2314e552497b4d47dfa1d5c91966a806d1636ae7df95be10bb5dc3d0c5b3aa1157770cd26017947593dd2910fbee83996c68d2ae693c959c7a732c8f3d5e9298123b66665c4cc6e24cf14022179dca80aa8292124653762758480566c448f9079840b2df1d0b4a2b50f644264db1291326804cd95baa7bd0cfa24e649777d366b895cafd16a72b0208812025fa3752c8e5bc6818f5c8ebda7fdd367d7bcf3558e3c7e05de9430f1e5672e579c507279ade88a4b255c3e593f410732bdacd11f0d6ee8464b71c0023ef79ea2a7a406dd5e1be32e97c074e302f73b5d99661b0c36efeb5c721d8297b8b55f92ffe75640d633ccd3a44a2c29f639a6361e462db691cf6b32444d6fbb6c88dc21d63b8db2cda2f012b7b46bbe2b253f4a63e8d0f5c2f637fad48f901167ef8aed89c56b4e7bd5f515c605b809ff0f41fa93b1eb003b85f02ae24deb775aff054ae1a8ef034aded829c7a1f7ccb718d58ff522da9f9393dbd1f130bb1dd5cdb6ee61ac3822826f9f61a678d6f207f3edf9538b5b9014848674b5071b07307b715b5b6f93c871ff56b835457f63366df690e1f806609675b9b6db0af3571b0f04420ca7bff53cb0fb062a0c609fc715ebcad55fcd9497e4e898693c690d7153509f252df84b7e9df9533c8d022876f93c39e017aaf07be45c268241c50b613a830b550dd77e7c9180ee43bdaa5916cd223c1566e96044b96e8053556bcdbe2a5c3b903eea33055eb817d7c36aae87d795f168247f29f19c195617b2e40cd6179ec4bbc577f86cd74d46fc1dea01a4f31f0cf07a7851a87fe99d472bf65330d376ea7cd3a0fe1071380d19a87dfe20e16e7399a10ef84f1560b71b9edc08d4e2d22a6f78a89e383b1edec4140b84191317eeb39055ebe31782fec44a591466b45583cec81642b3a89a6c32881be04c743bd5483b7d9757e0cbc73c3eea1d1155f35bef6af62a5a15b48e58589cf7502dc6432c0da755dbb264a596e9ff0ea95e14e0b0562b6367b27442107acd36a6b2ada732efbb8346349a3cc2b148f8e5894cddd2dd0fa54087e800e8bcb067228008870660f41e9614fc98335cbaf334c659e3fc72b442c32f9f9e23840b860f11fa5bdc2bc99beeecb108f33be8fc359cfb807a56b5b661fa08b1cfd2ad182b3eb3d0f32cf68dc0ba23bff5e92fbcb72b133c1497be6a76886014ef1fdd09dcdd1d58094a7ca599d2c0fd99e351d9695d3dc6462e97fce7b823665d7c94140b893c51efc054de99e2fbd28fdd14b80515a91c98254199a9724b94c61a4d07c3cb19c8ebabe8e3d1fe79488b4652dfe9c0a491f67094c6b0602ac27d3e8e59a6bae0f71147c1d6da1bdbf6cb046d5f7d33d07d225e9cde1d3c43c479ec8428192e2563bee28ff769ab87badaea5a9cb2478527cca698fae8bbf59ac49fb63b13e712d1bc2591f2ab0abdaa9d3e11d44521deb07cd5705d453de9339dc37128e7ed3a52a8e8f8ae270ff56b3e9aa27b596aad85546b9f393cd7e05cf82d89f46848855f0d46a375b08c35fa8b37023f32bb39202bc853543d2b26be7e00dc077438d16d4adb5ca5d8cc13ebc0edabedeaab6fb9229a9cb80e6a4ca9af6df21142f93cd4773821d122a916530cc504f05a0e789dc6b5c4d45c0c3c587944c4178e3685b2c496e4f523d2499a8ae5741501b48646f4bc853c7d6ea48356c717311763a3b16e1e8119f78e78835681cfcc4a1d88308e58659ced19cdcaf467c6383ed4f522cd1292b6d920e4a3e8890df8d0a0beb3729cb54ff02580747172ed7e1a816d35123e32e336c26cb58e608705a78c6346d54f105dd93b8849f1a13da4733d8a46a65b54d2af3399994de7fdd3a6f05446c6a8ddc0e9908c15dc15cacfe11061ac56d0501bd066dd121a212dbc5dc880b641977c325fdfc57cc020f8ca1555e45de688208b5d3533425e706c52bed8dc4a47d5dd7c4bdab9348f25d3cc78f00d0dd9dcc4eba2c731b3f2a7f822858930eb8f8a5b24c8ae664f3b736fa65a9d7cb44e9d7f287492555acfd877544a9b8e83dfd1c2c53a7eb1f48fdb7adf90333a803b247492f5212cb69552b7c4e185fe58c45a12eeb2975309a4cebbad6fdcbe1b25d57aefe23ac25cf7464591e8abb495b4a31bb4ba73d06e290a70055cbda55a452ad41dd15add8b1ce9f8b1158bbf5a5d183bd5280393ccd0e3a42d1779d5113d01cb1539dad3aa061b9e30093babc42a2127fe57d97063f7798610b747f02c495b55f1f4a03a38c706466cdaf789eebf82cca093071bea8499e16fa2c9b57c964415419fa240267b33a50031f358b83de58360e209e0bfb43fc69979a47a799f8a5ba91165129a9f4f59e2d30d61fd6787640ae1cead847455f7d5bc35eb7f29ca7441ad3964ea300e5ef3ebba2bac00ad87d1886c8cc178bf4a13493b29332a54fd01f60405551afc63ec6f9fdff2b4a75c95d80d8d12210192192c6ba568b2d93069b8a8956c10b867d5e442e179426a2b88b213bba8cc037687aa499e0aece384522a29388718309f40d5f82942e2a0f337296f3328f75551eb46ae97225411cba07b7e4892d3331b9a570f154ee6cd99293d4e7d8525187915f47af3c6b395a6270a009b0b258fe00cb0dad6f668d1b71a335a551ec3530321c3ab8a04085574af56c120880866cb76a6587993ef5350a9642e810d2f4f32cd927575943b6834ea1785424080958a12a918d49d2a6be5c1a84d987a5fb3aaf54d8c51aa439b29aeb7d901c9d0107f42bfd1833a87713aaa22570a4f5f96103419c0771f667c33e5c4d444c38406c5562f7b2c145ef83ed86bf8913f5f070f3e0d047c5fd9106249524af2ce9f384926023a33fdf978a4c6df9a03533b524166c5c83334bf89bf4937ff87a76981b750070583c7919014c5835284a422a9cb317cc2fd629746718f3726df44b1c9198542d805a578327e6f1fd548ec43063922866ab83303c1ff9275319f10729824c1fc63a4a9d9b0c89040c238079202a7d0ffb2b69250761152e280c439a2248a70ea2cf3a5a03139a7509e1e3f7f59b602039686a616b4f5f9205e13084a0715fc243b0b8f36239c78bcb3f5fa7867569fc3fdf250e8246dfd16d9601105480788348641dbda7250e6dd392e8bace96ae0ec21c21ed4497eea5da02408dc13a9481db8e0f166cf55f9f01bdea053be32c85d1fa2fe860ee6aa0b9eebb58d8812cff4b624c8a3c1dd43d41e7dae38e94ff6df8298a7fc060102b0fc378415461f26c79721b8faa27f2bffc059f603501228034c3430c865ca3def301c5462462777aaaecb279418d7a7b7d688cc9574cc3ca50094ce3e4393100c51ba8d026eb1888cc988c6d6cf36d056b4e181798d92b630d43b25cf3f02195aa0d4883828a997c9616601e8cceecbff89af12881a496fce5ca5c218c76cd7511add428eba094f587f0fd928198e559c1583e3cff159b1f98af8d2851ee7191ed3bcbabf45348451c26d16910c2932fed577670a07b4308c53ea14ccda7d415d5498c8ace6a6460bad15ac5dcf1967edd2f14610e57630a089742f8a9b13e549ede8ec60dcc72d5360c1dcb25d3ebca23e3467b8830cb08ebfdc9d125fa27c2e588d08f20d2a06632d94224626f56c524e9368bac3a26b52c9a6855d47d72bf04ec0044f52131a431468949f7251b3589fc7549a89b931e9df73ca42375802ee658763eeb567e0698187ebe5f7c15843b7ef6c2f9b375341ef43cf77afd621ba74be86555d996fe834d8c11c1c753d0789ba37669a0996e68b59e35ddf31edf2b04c571cf2433c5727a0b32b1d159f77c60e29e556bb81bdfa3b6b3346f88a0d55a532364d5fecba74da018e715d91c9128acae6558a70269fc85e1d904b20b0aef87fb5b1566f1123053c9dbe3db0d4933a239d53170007ee0119558b90cf63d961f5c15c6992ef22c95b022134317106c7d0c56a1036ac8e167b40d2a44a4c62cdadd5d3c77b74a513b7401f98c682b8b020215be21cd5111ef0ae699946c75f7bd5577ae152761ad8ba03fc9f2f097e265a379b954e2fca6575a2e60391e2e84fd160447724e743ba2571b85126a1c76fe514a1c3de9a781b2da681954eb25662ac8715f2e5069ee3752e78185ef019f9cdd6da7c7dc8a6723ec04ff8f543f441d2661d9a808dd914b8cf7530e1d6de2d74c6a9de31cc5036250388ee29851f0e85112066f01a04fe1ee405592049484b0793105390ab6a5dc7168210d89269914eca8fbfea500c077f28a69ce1a366a38fad2093d0cb276f721ffe4c59c1ba6126fa8ee071af515b3de22bc96b8dcf6a4ade847a01d7e4135292718a2d2e0add0384d7a79d3e56cfd25428d885e55ffae1f5556d0ee2f5587346c220e8003bf1a7916dc3ca915a4c8e71b2bc18b5edb5e934e8625bd7c7c29189d842c36baca0a7c06b4c1ba8f1a3579306f4caf559f016df8a5c5ae96a902a7c040f91e3c5fb9848df1702bf09e365c663061b02fd199f3d162d24164533affc4c8f6f8bd8bdf3b6c901e2178e43a27301a1c5902a1eaf1fb4f85e50cb5a2c85b4c02d20a4a5bb47b118072765f140e32963e40f5557b9222666ce8641b8bba8e5c592efdf3d38c801c5aaeba71183ee74c05d48de3da28b21bf531eac910008b6a7c3baba9e60560f036c32c16e6bfd7deb9d16896f5369d2579dea22aa2186a3869be52b3f636f34ce97c0de6d9601f8bc195c59288260b8228852f382aee59d7b668a1b763ddc3134f99113ba7afaeeefaf674025afb1db183dce84c915782a9228ca94834e4cc1f265b5fedb66504aa60d1cd82d548a830e8dea02aca52ec47fed7cd5d296e693c9f7ecfc67a80cb721aa7419db0ecdc2c08d6ec1401428972bdccc593043a5aac9bf824b70ca074d7f63aacf9ba6e72c9a248f78753b509971f0aaf4e79f386e7c1811e8699d46217d87a31d101becc98f8f8c757e510dcf7826f05b43f969dc401b814051845c36cff8c00fd1331ef31efb95bfa324c0cca391282cf75258585e3346cd91b21fe5da95ab2caf814a6784396b7469f4acbcb3603d5af5bcfb15c84f3bf4c6754d404feae786d2f176fe7783effc714a0f7910cba6f00bf6bc4ca2da359ed3734c5f28aa257b84e3a0776281ce2d7902a65c96457c311c89041abf41fc61ecf7647f0712bed908daa0021b0700b1b0c68bd720bef443c118f0baf120fe7c5fa213b5d23c017cff4692b9dd95df857295628c29cdad5c08cba7ce5ba53514834d74a4b660f9f1fbccca1e83e10e89ec0578c0959668fb436e51189053dc331d42a1607c679e4090c6017192103872cfb02f300d47343d7ea10117fea905532dd6b61630d189c501325e06b64d53602cad5aa31e1fbc82c2c8682fa16dc798b0c73216b402a849b564a524e27e620e0855102f3b91be8434700030511c12e84a21d75dd77f95f2f05e1de40da2578d4875106cc53a0864cd573bdac96f65b36950b00ab1cb04bb2e6edeb1f6c4ba90ee0cf09bc347dd955379566a24c742f3ce626edf6fb38ef185be73ece603ca1d3466676a39d692c74f04436c495d269b866c09281724f914bfcf6d0d5be93159e8abc18a72206403c0b50e5f271d7d66a047584f1039157516e222941ec506620025c6981f4a6585424d87751a7eb762dce3a2ea1207608e229a3c72abdfeb845a2660b28ad6393c294f5bdd3ba0527022580a06847c71c7d98f16454a88799e90a43006d69ddcbafdc6a5eca74524bff1002a2ca910682fcf5d991e3173ef3fe1fdbbdde64fe6b2a70ceacfccfde4aae81e146ae88426669d7a754ef29fb52315b25c48e2d84d65d9bc3109bf3c852af1a87189891c9e3260989ce8920dec1fcc835c959d088fa1b5483dec5a5fb397cd273d77942f7389a656c00fec3284ca0fddedb3ad337e8476c8b0eddcef591187079ba00a7a14e1405ffce9f50580e6aae652b7f12c1fc4d514c239bf2a95559663cc0fc85be6c58b023bac754404064b778205e52d894c70d6b31377ad734e5081d885060aca23e4dccd3403c9d535b8557279fed82c690aa12818c3edbb64742ad8f092b6046be6148ccaf2e0363c7d85330eee4afa7237802a6820221863e51e3fd6f9f5c3989db6ebebad5017e3490e1b5a9cba19b40dc91b49f9e914a4ee728e6a3213f33e1c94be3eaa61781603e32ba41ad8a4c5afd5cb9a9f3579e4eccb60e4f8a7739b1432a81a63fc3b5258f0e791d65c7cc97c94bbece688256fe0bfcca66596bc717d75338d89925da126fc4b43e28b0c02d6999af0c197f7ad3d15c4738aba0092ae1952f60891813147fc45c8589f74aecbba9a0827c72bf14fc06253b1971f276c75eb4c1536cc634eb220177d8035cca3cab29519b34cb2d55100f7498bc1f79da8533100e7cb7e9a87b35b5099955e5b47300d6a5fbc880aa020b7096a66bfd34ec19ac5467fdb43552b501ff4a4de5548a9385aa40b3edbcddd0c6fe3702c6137d40cc2dc28974692ea53048e60ba53e777e7ef3f4ab010c57b996431925994d498b6d43c15960162296a1ccd0a4499506644b98dec60f25e7e3ddf029ff067f1b499d4537ee4627537e3b3adc02a3f46cb0588341f116ab5d428cfcd860aa4c1e0d8ab668e0e01471d9597e7e0c7674e242dff7bf02a161d79303d0267d6ffe279dae4e8d0a9be9d9e6c057c8678b79b8afa40e9ea24387e2a217f6c2c4a1e94933567a43242d3a9856c3eb5a175d445abc9a8d09a7b5c586b22d8d0f535a88b6c64b46b4c2f7206ae474fe843962056d41c58a70fa86dbc83c875a6ec6f9cd9a879084ed19a3999aff795a996c9c084b75b33e88f5f374fa92e1e03193c6e45a02ffac065d5ae1bd7d62232c4dca862914b2aeb724f14f498f11c528cdb68b16bc46482c5e5b3d13634d3ec8e67c3e552b4664fbc3cfc123082dab5e560c1aff0aebcb9f4b6ec8dcf5b1b231baa59c32b5578ccc54b109e43e7160ea7207805dd334c63cf758e04362c69d093c1b68c6ac2de3bd707cfcd8da8066765ea5bdea941139c0db092dfffea54808cef8a232694177ab17d7f5c97289389347b83cb33dc72ef157b298766295c4111f8ff25f8f1b38ba00af6074eaee86b17e192db2932c3165eddaa1ee46d355232ee844456497ba8e103a5f6e611e7fe72f900798b31747a88eb6800d09bbf09d34bad628dc7a1b00e1f81ad874619b5f1a8852aa0aef05fbb5b5990e672ab6332d7a0fa638c14a0a0ee024549459dad99a125d15af537c977802974e4944d1000633b8fb48cff0945154ee69047c5c1050e0aac8a581f89c3447aa458d114fb42239cfbd132abdffb87b08e1e15f3354e4944f12f4e85037394897ca3d0a512183ffca8cbf1092e98d3abcff28f118a3a72a0c4f6ee83db674a0647e513684846c39db1c6d4f061eb3b6a147be006d31a3eae30bd86082595a0f136d6993e0cc071c81c9b9112528383107648ce3aaddb6e7be075ba1f4012c601a514368008dd85b2ea506d8719b805112227ff171b2a6c4aeb290d42437939b2442592cdfe67a48ea5227388026a0b21283a8f870281d2fe97973738efb8c499aaf3ded77fb2819a82efade8eba95330e93e2405fb3b11cb012bbbe3a2197220eb55d9de7d63af674dbc1064eb63ea6facc21cb214a1fdf9d8e96f05c7946005ff67294a825168fc654335ba99bb381fcc4216b76da9ecf19989e7097df11dd7e6a4915717d7f8b2d924c35506e02318ee603c74061c9fcc813ca58c1af09d21c4ba754f020bc4609b728edcf2d7c2e4a3248dd5405719c9e98df80bbb63c40c8e8bb99c233688a6dda36e873189e228c9574d37099dbe2dbb8f318b86af32aae615e67e46d290b256fd26a398d4fca71691f8ca390b9c7af221f079b2e02e1072bea6b05be4bcf656704ff2a980bef27498ebccfe51e289a86fb44a365419be647ebd08c180e2d256aadab69c3080a499f9030dd9b96f0779117d964c0c8d0936c88f132cfeee15365592e46daa6f1f5c884264af51f876c169010a69dae6fbc3382f048de9bf61338b73f4ab5f02d2ca32f84f91c45a7ef25a34a9d4daf98d36a26a1592cfc366999369f75d1fda2262695fff49a071a14afa7bbbe72385e9be60a9b21df9e09f435e88c09b615185c564375cb2d1433dda29d0a9cd5eca59376dbe4ee6b4f467e2296a18aa4f81ac02213296990b283a1d36c579e8dfb8f846e4330d139c598fd94cfa777f3081f2bfca1cda029e63da1b946f213f9da70cc6e29afe479444673a73ba945a1802fd87f7f299165abd1499c79da2684f26c901677ea1e453cf7a39e774b128905eee31cc996dbcc17c040c9359a9f1bac8dd063436c783b225e722f0312bf93730a1f5cfe16de425ed030b6065e85f8fdd6d7734ec1981d52205fffe8daf6d5224b8137bc05981ea3109d4fca1be0c810e99307cfb8a4e131f1ab357b785ae0425e8868d71724b79f5a71cfe61b8cfdd9327de5a1431b1bc77fdc804339982b96e026d4ce8ad1f53cc98001121e2f5225b4fb84a22469deab4fd2780d8e76b3703d467a465634aa08f79f01e34000bcf3f011b009c28b42c75a45111ca824441c147a89a7cdde1b242cac3e6443f8c2dbe8a20151f4962d6f62364420d65fff06d5f034d2da4b2746818b286b09ea7bed83fc13b256318e407c81384a7063382ca1f8bf6bc4e48517fe08c6297b06d0cbd6019414690a29fd30cd9c4342854e4b67f347c861beedf4b02b3d424cc0ac0a4b78410952cf11b4f2166f90f053cbf38fd104af7161ee364fefd5211423bbab5449cc5e7f035f2a2b1bbb313e110d0ee4d1b6b58d44c1c3de90c0fd0beb54ad1dc064b6b787c8ecce420137a3ed3417039de9765813397e3cd8bdb4d0ff0a2b7d9a6ca0031f1950fd96f9012f35f4973175c656443217d9fc8dabcbb458d9f112ec68a7987a40ca812b17fc5bd67f6ca70d89df2fd88d6cbe9f44f2f924f355860541ae6f936a94a6fc65b636c0516b10c5029573b44d9a529926d1ee84abaf68daacb8d4af046de4cb1747fb7a8b9d23052118b534a81f5bf6078bac85b7666cdd3e3f5294c198a43cf0e9edc3279d288bbafca0caa7ba44c50d432ffa00869c0d3e1a859c73bef7c9142888e02a6188e5aef0f762b117949480a72aef49fd642f2a7a2bcccd4a20a0080b249aa1608392fdcb81c6249d32d21e83c2d04c65f5542243e052756976bf7243471efcbace1a614f4ea7ed4de64a7ccf7a5900212a51db03b34be2f41b563ddd989fd079a732d01569dbf802727ab65aec2fcac2df7a1b7cfd05749930a77644d4d55c136fe5aa47a2dee5d9004a373c0136c9e2385c666fd415dc1a4e4d3f0061d313db364f6294f60f2f0ee974bb3662bf87ed336634c4531f8b3d051a9fce19790eb924b833ebe8c045ad312ee7825df5b41d45516708d2f9ed2831f2ca1739cdccffa17a318e66b94011c975c1721d8b63593596450b23d0e9b6cfcbb9ea1c1285abb561b1cfbb96417c8b4f922f6ab7f2b0ebd44b9c3ea59178d252b384387f44bbe22e05b00ca713dc5e67367f98fb8cea641c7e803d7493737022d1ac1da6b41f48839342eabb19687401211ce6f77d45871e878f9d2c18b1b837d4611640dba82e3fcb827ea1bbc432541546c2bda81a01879bb2fcff748711895c0ce8d47338c150faec01c85fd8f7b8707f37707474b9fc378479f3f5b93e593102acce44de8a84fb2c62c350716904f1ebe596ff6874ce5cc800a16f6af99dde74494af689128fa7b4b0ea2d2b986504cdc4f64ee8a74462ac3b51824b59751aa3191bbb0e70af3a16a865604df4356e8b3157aa03b55f192b45f72d95fc552c96e033a827c6f5937da44d6b16c78c5d43824603f495ce7d309ac420e9061dab768eafa78c0f7bc4cf3e57ef391bfdbf7e74aaeabaa204e8152e0061a6c2a71be0e8aefa0fc6aade9e994bb5439b7ba8de53456d61a84d1d59c867e50ea969df1cebd011d801e9a3139ed3bc2d0524f99c2234461e22545a408d539650b381defaa4f9a35106aaf89c038acce3623a0e9d0eeb3d3471246e03720a60b6c748f3368de922502319eb716bd260d74cd03a364c97f76bcb97c70018dfe9e26f6982703eb3e3ce831deb0a2dacc92b5d8576e358da850fb49e084a202fdf5d97f57dd4d145e48068c85a0fa46ab1836e560827aa32802c61544700fb0e0a97206ee0dfbd650645e99c0a686fc7b5f2d9fd95f693ff61dd34ff208cd4f3c26eed4a6267dbf574c3f60e4aa680e741271bdc522d21eaa5ac46fdce781b6c9a380766e3ec3372c75f2c57014a42735d20ebdc007bc41dcbcc3a808ddbdd24f99af727c1e42b24d9d9505b3973164e6068ffdd8d172f8b1204a558a34d38a743e5cb88797a53bc4de440feec468754caf0e2c341679e6840dcb677103d87f343db996a5996a20e60af6355c6392af3b5c90ab0fb180c9a20960d0e07efe1df80c242382306ef787514f4c186bdb9a514f76cb587dd556e793f36878c9c6e725db870ec5e7c5d06244cc3e4e6d1c64bde84779756cddd44f66391dd7077618d51985ca45cb8010681b75d7112f4e423a217bbc403c3e4432225afb5356ab59f57109fb3ebdcc919dba08b8e073123a789de7669b91a50dbdbc5b866b23c783198f4ba3b555c71f2ec457c5974f358a687724f111532c3d9ecf3c947ba5c1476720ff0fcea5ee031a0f282ef3e319450b873705f0a12d108442166e4494f4d82f2d89c3288c7b7ac5177037682bf9d0e0f404071f1f2dd1848fc8df11f88a1f5be4b4fc098161b2233d17c92acb861e5e78b83d2add5c58dfe591193fcf52413ef7bc7fc3102feb4c87d8f0ccdf64177626125f357470ee2766f9f806e8ca4bd79e2311136ccc045bad10ab2d691de1cc6399433e4b1f77b2eb47f3f39d32a98f2c96a93b0303c96fd8a2d8ac0a37825f07da91a8d6a7a270f0d43717402620713050032c60267abfd294e53a0a9150eb65445455e32e6ed4dbbae7858be4d8841e28ae254101843e3f887e369e7668ac383c38924ea08a1eea05051ac7d754e87324c60e805bc12766e826a6c7082d65a9e4be20c7ebb4511f6a642345129e55156d5d57442846aa5c83387d82cbcd64664cb737a171cb1b6ce97b174e37166aaded1ef2358695be1c94ccdb91495f6692e1114ea21bc920e6380fd997057788767c30cfbef53b81a9f2289eca0a4804b048f7150b844cc93e49c3911e6257f9a3ac2ffbd669a9064ff28556c04215c11ee35d110c7b555b0ddddf12078f2ec44d6d95e3d0686098a6848f3c6a78b55845443ba6b16ddfeef16f8c6015328ed8d5c29ec088ecca5dea9d0cd9b68fa4200671e553c2955620e651ed6fb4efef1846a70878639ca22622116f5fb8b7617b348dd54b60fe3901027444abc18337eec0936a3a880b01a74cd97ee2be11d8972df3af7b2f2d384b22fc6ff64264f698a3d0ead5aa4e63d913c93f13bedb0ba8a5f7af021e9db01fc76c5e4fe944ab6b7964baeebdb6dd3c056e20b70615296a5061126ee1c6f4947ce69e08c4b99c4aeaed95e91b99aa0336c2ead865cd1e507aba16d2f88fc695a47ddf946fe0c19f511602d98ffe48b51ffd11f3353dcd139bedd76969ce674cc201d681c2ebb7fc5ca21e4bc53864de4ef0f96c8c1513cd01e26982aea0bec34c8791f0677bad5ef617643321e164506ee4c16cea4a2070bf64b42002c34866ab19360ddc3557576f9add31d6feff031b49eb6e490922dca732e7a368e6b131910092f574b5573760ec0cf788a16a40dcb9bcb91e2e1bf455e3e310563d0fc8997bf67b76f03b109e46e862bc27da116018ca32887896bda74a05ea5c111daee99100b11665670fa740cddd8278ca3f9eaa110385b60f11db22479dc7c6cc85421681278dd2200b23cbfa8fe618d275137688d335111b2823082cb66c37f76104be175c918f9fdcd86cfc34cd8065524ddc8e741195f7a9785b8b791d996dde34e1ca109887ee18def52ecfb4df4b759483442f4d0112a357e35321dd1f325e5e30eb3c97d8230d1e84c5ac2295f54f12608c78391ae7a69f18c647cafbc20832684d3c06f12e8443291b8072c4c99e1b8cfc1720bd5e19beaff324ae5f874cd0af84ce694d00dd84bf894122bf6a53199d2ea8057db37e14e9a057be7a7dc82a36a8b50bb9243e9c61b9843cb8867c50ef273781088640da142595f80997db8b485275eae9ca772ded5f9fd685ff701d2ed3314de89634274ffb921639ae2b3b2e6dffd395ba0d77c0722060bd2d94ca323b937e2b8056ac6dcf2afa5ff00be75fb102e9e7c947066001e80c7a519c3503a727a3df72ca339f8db338a7a6e2b974d68b5e4a2f75cd283571ed90916b2c619b09e3c9f4102e74fc9b479171f05676c35ee8b2dcac462c8c223fa371d7b3c1fdbe4b04d20ec20075bf13ce68a7d1fe73526ea8cd829f32d79e0b1eea0ec7b7afa64fc135f9c8cc002d2e2a90351102231ec9a2962fb96d8d8f4891e2246ff9130197047cad5adca757c74437d21bc9c805a837a4b62636b5149e15c02e26ebf6ac42383ca48cb489a5c08820deb1bf9a309e0111b3d42ea3cdf09b581346ea12c69a80cf47e7b427fe0d5c641ed1e23538b315cd23d267931875064a8f537965db436f47269a60d6b6e1464596d9179e3c82ef4851c9738c34062613ded7d4cb8298c60c9f7a96f4b109bd75dbb9e2620a1a9e8954c3c68034ff67fedb3adcea225a5d96a5e03dbc81934f53406b997f454af585772711a7b3cc9d3535f66c80d2c12cc3e3d1813975321fde4493058b784769d137e9ce2a4a66e511f2c327e93334e40cc3ea1fc6f03c2aea330ceb0dc3e340073948f5d991cbdb0d2715dde35a11949f013bbf3d4d90749db3b2849907eb584edf1cbd951787be304de7989053b9e384ef79278f749267ab4c62c946a260eb9e66fa223992b44e564ec5c45edc0355c3c0370a9524e4429ed68c90cc30d123bf40994bd14c25a754510940cf2ee9c14630de2b52efb7a3b8c7781f2ec77c2193a8a0941e37eca682b2fd337c74a4bb12a0f1477758d9759f3f07424488568e1ae9c706c6b08ba1b8b868a9146b9667f85e41b9b7664fb433d04d67ff32bcc966df0d2044ffe35ef26bc05ce25917c1b492822e9d4de0064696d9df6bd8b280e1dfb7610715349ed3ff83da7639edb2896ddb0ac3be364a6369e6449a0ec7fa322a11b6d66a83c205378391d746bf7e12bbb1c7aa988fe8c08be9a0d793ef08a8f3ba56f32589d7a2303d665c4568616a7e477a2975a1fecc8ae785e61ef148056da1dcbaae4f2bdb96d37bbe87894292dde0eec39c426a342190168a17698ca7ad319cee9ee5f26a0c174174588ba1b61959206ed371367f505759d6481572db2376a285d09ad2f92378797e10881072dc8463f4a2e00d85778c46642a0895f65b071b1803995420fbc871ad2a698552a936d99e7ee844b306c450cb021fd073532159225a4ce29179c47c506b9429cb28dca138eed324291084cbabc2c4b3c724d1977d463dcfdd439435132eff08401140358b5e7abcf00decccd5df4012ac2530a2990c6455664eb910c589bd951c91a8c30c7eeddd785993d835d1000f87fa1f046c26fe7a034b14f9beb1b1e89d68662092e629c149e423a359f7659696f27457496e3e2289c1d84321e371dcad42e5ea4cef279ab217b78dd2df8eaa6586f08b01a9ac77b200206e079f6ce107464f3568d586e9375f2d383a182132f99ac15ed8c2d15b75cc4e6c8c3148e6e3699c12251a83eea859cbb988ae03ddadcbce8b49d04372a319a446eefa6ee2da51c6342519363918aa982b18bc5700a4a5f8a5c1a77f38bdfc67fdb089df5380de99ad73fd168234569e22957c80e214b52f698cc912a0fa817c4ac45cf732251e4ac8180c015ddf2728f271a0bb63b40ede29c23487e5dd5d7f3b75836693ac86ce070aa6def697a6b13062f5e6d6a0822a5143a14b28e60ad60ac30236ef71e96dcde405d68b3071941b79c0f70cae1905a78e7ef9c73c4c167743621afff5112adb2488ab866e35af5acbd6bb9e0fd2f8174feb0e3e3dca055ba9429705d6c668d8ed95c88561c50b84e8fe5c69720d7eed3a83237963f461e8627a0b1ff92d492842afb7a14eb436686ce71e43df5b7fbe7c4948ceefd6dda4509c836d46d619f25690c2b8cc1f2a69a20456607c8da030ae6f9508d85508c22eebec9e79505504022674a2a9ffa6bb1e54724326aa91d41883c53648b077be2e9e5864a4d5b5bf29bbb283db8a055020c877aa42b08a957206b8b547f34b5b692aeed043340a3332b229d6824575088f84851072afbb648f8a0ed3ab031f3134107ca614880eab57acb8ff718a2d23b6b9b0354f76a1479fe00fc676a4659bbb04e03bebc62c60ad9f56b6d4b5e4c959e430947d35a1f75638c200c71644cb4485941e2756dc00f9df4ea80f8ab3f86af5a7eba9481116bca9b7d24b74cebae16955fd8ec94c1d3a2d957684188824674aada8da4f87a5d10683a8860a50e1e9834c6ff5544dae0ab7be4280a713ace8cf28b405f8ec4269767a74649e12ef07b5a19050d218c530d6c29ea1b18d2133cdab7f0fa43661851b6da737116e2dbc401d208b2453be61488c657218efc64e520466f1c21892710ebad3dd9b8983ae22c92ab7004ecfbd06933ad5d31d3094b0131825b10499aac357cf4908b374efd10c81b5fc939b6c5a3107a2ad3b5f4084ba4cdc24d13e43c57e2ede44e60a3bab70e71f709c4c0a8dabd805adc12c73a9b01e9ba9f4aebbfb478677b6f6460bb135297dce487435f47bea5e4532e3182be4d2ddd3f2f06440a93005e4abd6009eaf521242c6fdcd1f2eebb3d5dc853f591835907b414ad9b16287f084e8fb174dc7a696d70fcf195d4a511c921dc79e00659595b6550edff40083121a4c8ab8ab7875be3813e1c34e63877092b656882cc976f995a34cf117cc30e30844707e4164eb91bdff4c166e262b7dc128b2e87620fe26d3fca32a9bad41a95d1df3ce2881bf832416b5d6692a128c3dfa0a1b31136f669b4dee3ecd1a7363749172cffe0787cb6870a47e700f2259d43515406836181ad5776a8ff3cf5915910f586ad919cf875cfc3d35a3f814e21c9f4d921956a29a1ecbcb9033b54a817eb0725fb3931db55a027a8fadd840dd7e0e0c691a67e4c1adb61efacca5ce07ea28bfcfe5112d0e6c896e5687b236ae1f0c949f0c89ed968f07fe49297b2680fe27428de7411d3932f50a040d2dd4bd89d172d49ff3fbf993857eb30d0e19763662ee4f6add3b37e273ce65ca70b2693b88e508b53031ac7e5290d114abdd77432c2f8a7636ea85a2061337365374d623ce9812dea8d0fafc9a660aed715a386562846ed94a55f3868e0a151c186032f68fe65d038118f1b94e1f8fc3ce80f73af3ea65834d8556f459e9929086e65b19a44cd97081fd46071e82c841a3dcf2d76c1b23160f54ab5cd0912bfa3a23af989069580d7429db033ccbd7d5a3f4fb66f258b8dfabf16d52cbf992472b16cb8a193456e3c322d5f736984f7daa2fd5b0e63d3c191796954326321b8e94766e9644a5f79b3ca394bc1801a8abbf45241e6a3b0c36dc59030af7e385591bff1a54e6b622f888d034051a56c7c3d650ae8431f405021ab8949c8e750bfcfed5b3f1bf58e174e9e43f295198121a2fafe3eef746207baa44d77e3eac97d47a901e7cdb769e9144942a282cb7682351a744af2b8ed865d21ac51a3c331b63ea5ea00b35610ca36a66e9cfaa87dcb546d6e31684bf197221d16141a81455d64e628a8677efd0479cc0c47ba845def81d7e79d86864652099ac6b02ea28c40d10aa366aec23a5326b71b5e1fd66d78b813324008538efe2fff289e51c461a7cb218a7c80f1ba0869f89334564c112251219d7d849c944d57614ae5bb442268e24c9bc069058122b911e703b2c24c3e24dd65bc8bb350eaa8aadfeada52920d779f13e11b2687d73ab80044965e09268789a6c090969da3b0cada66c38bbf1995368660b35a83d7ae06a7b843ce2bece287ff51a02abdc6b378b9d65199ef0a72d20219495fad061224d241165a544a7d7df22322786e74f59199b682bb014a98c170b1a39e52e9ee645184637613838b0bcee7f792d0e03dbf492390f1790c107a2174864ccc009e34995b029074e0da5cb58f52282adbb03e9028c0abd48f6d5b7c9f01f98617ada1e93d0d45a79a2718f3630cb1e3c51846d0612e8c08560b304fcf4031ae96694572056b24a07159d088bb37ab4d38d0835da51aa8c37e30f5a145d4c1f95f6dc3df41cdc7f89ef4d0f2b5a280998d1ba9eb8412d2862063671b8617458bcf0b2f0f1ec2812f7f2761e7dfffffffd4ff6265bee2d654a29052f0909092809ce0c050a43f9ccf8d1c44ddcf4352971539307e1f0516742ac3d6a5dbd42724b7dd9fc3ca8fbeffba17383d983d887bd51c70d75eab85023759a5d17e2f0e1e0416ce54aa7ea1eb041aa30871d390e0a6a3f053d67ffcb2891992425e1cde6264dd3b4ef4971d4cb90a3529b73e0d8337e4ca0107165e241dd771772538bad43863772541f018ddcc568d62eb1ee9ba29a9aa2a2fa888df6a14bd045f40aa494b2e3af518110203cedf9c9987f64f899305e3d5445e1ad90c4a9cb4c9849163281ea611f1c877084a96cd42d1ac2b6d57449ed1f4965186973936ef5373b912d6cd01622691af600e23853bc71998a780884890e15deb8501cf3e9130927c0a30f5b1c72802465044a9850b932f11828bc02ff0e063434a82b266aefa0f6e43250672797b75f9ed05f000b51a52376c09efe3eea3e2266ef9fb94e0cade383c7beb277cfe33a23d5afff0e9d11eb80e13430e71b9c4c46dc83ad48791ba31fbd1a43e37c37bffbe272409de73136a845b01d436aa2d322d890b29d76f2203e8a2151a49c6e9edff9ca2a223b5fa444f3f0ecfc0fcfb42cdbf99dffd1debefa1d2794f27213c2d884decb6f0731d3afbff7dc04695e32135be11edc85d9c94a759b1f3d1e48ddc426ec02d6bc0b1b74816df917e9d80eac2316557301998f79ffde52cb473794783c3ff2110f0fe223a46ec23d3ac63aba466a529d06dc15c720d6b907af8e7ef758cb239a221ec4aabdfd349ff0003ecf67df4478be48892cdc164130ccbec33103b59f871ef96ad7d91d3ee1e19fc7860f79c2a29eff4109d80b75e67b106aa05977f5fbde2a88664f5bf43e87178a586887e71b7cc2f319f8c4033c3c3c3cdfe11310f0b0f33c08ad6a084f58d4423b3fb3aa31e11111c3802fe048a3e24153537c446b67db59366512de8a65124edf18b1947e32cc69f2be7e75de56bf9cfd59fb6ff8851e73ee5e1385c73e4516575cb982a972303844fce910c77149781c87f38437e79cf3bd07daf371e1ae8be33ec5fdc671e17844acdc770dc4f5d4719d3490882b8d3571dded5f778f66d6b1dddd9e725f9d205efbbd7638f650a9171cd7dd3dbbbbbbbb7fdcdd6def6e6fdeedac05dc7577777757ce4ca3a9996974cb34ba750bd26dbec875edb7c1d1aca7af48a5964d2a25d5b2d9867b1bee6db8b7118458d6d3e7ba66f681d9febb1b11397f58477f90ae1b4d6faba3b5acb8f087cae933ebfef17136862e071151e1bbe1e9f413e9d40bb4ce6696cd9612bc5977c90a2c5450c192153c5802133db0024a18fe194b4e9bee54ae7a93744529cfa49487aee88af2d015a53c5c4ab2945366736bee91fdbae473727e952bf0ef6b85ea95ab0ca0ca5f06d23ced3dee91bd7c0d7cb1454dd334eda56a487b5074e2699ffd1845f65ef5e4d4fe47b714c3355442fa7f34ab8064ffeb929f8121eb604de1f5e414eb90bf2579628b5286485e5213545ac1e447e218c21be9d10976000589082780810410d60b2a595260858923a27cb1031e1891a9209531690172d5a985b15a9af2059309aad49e9e7c8a55952750a8f2fbc91be9aaab02085594f09c78fc19674870ce3a42c2ca61d3ce5e526609ca30c489976551781e645dfefead0d2a00b7fc671886ef0faa3f0c3cfe1838d411c22f9f439db9350b695a74cf62e08d2c030c91610daf9f25a5f8f9fea19c3635ad8a25740828a92a8c84d08670da88c888d0429b9060c6b42a6210845400115332294700e19222796b992081028a60f2a2601a9c24555125081a58f0e4ada50ff3c34fffb444e07575ab40614104d4173e8881020ea0b4a0700694127c48c38725218c98c263eed147abf6be1d3e2cd981ded7a2949ffd942c9d78f2b746e1c9ae6357bbe31eeb7d2357395d7ede1a9ef410c90c8e81100294cabf2f50d58f2ac74ea344a5cb4a6e97fe3a92fde55355551f1d5413eb9c90a830421dc594142a3fd4714775679f3ae006f81085038fdd3d03def2e7a4e598f66662726a402ca5c494b4af557b47b5796ef1cbbbcccbbbee2f594a29a53fbbcbe94bf8944b48ba03954ae876ce5156ded1d7dd7d6ed7ed673ea1a2f639e62d89f1111bcd1a780cfeb64422146a9af1f33da97ec8321f7461fc90c4053318021b48b0f1c4162ef025234820fc208d0dc30a2c54502e62204608360c2a94e820a2a80111413461c51629e613543cc992bc20aac9921c3861841c2009c9d45da2e2045418415997ec52aeaffb0fbbfbe24ccdc9a5e694bed3478b3b7d939967ef99d30f86393316acf4f9b548a9d47c6a99bb53f7fc05e27f546366667a84d37eaa25993d70f70b7082f0f8a1b668f16056ee53cdd9c185bd9d7c7f22d520c1db976afcd775b787fc662224b23e89e92c8085e6b3d00c8f3aa1f93a4160aa3f4cf5704c529f7c6086d45beeee04b1723034cbb22ca39452174cf1831a34955fa3495514c10a57f9b31774107e90aafc13092a5e9040c2a0326f7142e5a5f27753073989caef7d4a4cb85229156b4e9516540e42f570fc9a9fd54032955fb008c112a1a5e2a3eed2882bdd3d0c222485a12eb8a89d85d3c3f1abac22bcad445cc765ea2534163ff6c5aafc3204d9170aebe2e7df262a767fc0b053f97128eaa0258201c870aa2cb20462142af38e0dc76d5a29a1b0af7d16703b65f33c4de129970bed7aa8bd7358973fcbe51ea52c5072994b1135449f2975dc2ce28e9fce615ffcfe1c730846731e86d7bf54c695daaa20371e185eff4ef94d4260d2fd532e9722597cab8ff53378fde1f91cfe04d90d77f8ec87496818d2a2ff0c59a101dc86212d36ebb4dca1c8779ae8209ea45dca12abeeee51aaf7cb65225c8a649101240da4e3d3891cd471b354c7411d774bf528ef70e4a3f61f4a0668a0558281d2376178232321212121f506161529215f7e92da034c93d0f65c4759ddacdab1fa4383194461243662a7f6b3d20a414a93d52285c6af528e924003a1f954cb32b044a7a713b51f072ee30ac99be1c849d38974007054e0c9dfd1416c44b776ecf0f161b1562b24a4f1e767859eb61a74eb679b81f7c3492218346852e43b25fa026fa49e5786a7d33a5078d5078f1778238fca38108166566c70813ea7d2c7f3993eddd215e73b28579cd3bb5d91f967bef2ce8afcddb6f85795c70fc2aabf6ae367086e7f59e342559875f9b30e73577f1fa52ed5ff874850007649698b231dc5a4388247f5e75e985e411d3b987ded7bdf60e99ccbcc54db750791cc5dba31f306aeb7e6fc49837431b8658d3f9472f85e1adeaf385948f0ba21b4940fb2094b5a1c5551a3cb4c428225253021e1042dda50c284c2092d2fe20c1b6981b0c61426a2a8c0093880811a501c21460649218f5220b865ddb4c06b4e22c47df645a9df3e891038c45beea4d39fd2e94f7d12a1a26d2bcab62442dbe72d29bc19c6accbe927e4de7b19702a9e0c9d0af94984f8a5b7fc930811f156f69fb7fc95b0818b94b0617e1616cd90c32cd4b1613eff0c89784b4847fefc2a3cf0c75b2ea512549f413f713390992a85711b55d5811ca8937d12213ff256160e31ea286fd14f228484236909a698d00a29c14f7f8557c286feedc322ef3bea578876a863c3f6fd423a94071dbd6a8067ba4c7e5405a5a19199c42d7370e3c3dfeff6d53bdfbcaf9d97cf6a480fd3d81e9cc43ae4b7e49dffd13796ec8093665b37b887f6f2c66fa0b7e28d1b52c78d1bcf205df1c68760b7e28d1b3f321255bebcf1db3be8db167e6f7f23e4a31691963012d3be364a299ddbb67d2f1fe6c79eeafe3d4339ccaf6b1f78836907eccaf2c6cbf0e5f7dc06eebce83fc6e0f871ab39defd7380384016c89f55bef1db86cfe0b8559877d5d08d23620dbf55423c90c5194c2085cf71770394350c290cd34ef4f1ce55439de893fd8f097ec4e1286dce29a17ce328979a1af33b6683c6872aa79cd27307bddef847b51d1e77779e2b4c13e4df9161864fa337f0805ba90c2e77c04feb181cc51df0d33cb06b71d5373031adfe7b8e763facba626228bf116e9449fc49c2d37e14abfc61256cd00c0bc5c43d6e60daca5032dcc03d90b00ef9854c4c5ac73c2a21abffe8af7e5534d0ff543330c8460cf5c37a16615ee4466b78e34679686407362ada818d36b063207701398a8bfc22483c1b703ceb7f3497c4f484373253124331ed0b2ccaf13afcac67a1acbb01f67c08fe6f20f845907096653e335343f33b7f464ce13371fc80be1fbfc7ce8fac9aa5006edbf2df0177758caeeaffb5aa8b200aa0d370870682edd0ff5441e8f3f71f1ba578c0aeeef473be2807eb7960d1ff582c168bc562e560e5c0f139fc7b5efe4bf0250f0c4ec334790dfcf9687a402f07b8dee209e98a86b09d704c5257211bb528657e5d270cc7233caa7f4ec838e11716edd0a227026f6428661e3e70f0f8f1791f8e276fe48e7117495ff33c1c373734a43720f736e098a3662111d780e357679e061cbfce844c0c9594c450ab4ff4f91199f5e327036f6c26947a1eeba9b34050a6004419b2775d53aa5c2c90b048da97d6473eb4230dcf816c6c5f3eb89192edc147ac43be6cb922d3d03de297c2470e76c7806ce5a4265a9453fee769cf494ef695bdfc1e9c6f633ceba16a39384cbf37fb1f9e038e3d95be0d387e5b48c4ffa37b3ea9276435a7a38537a5140f62b54f95b3d2df2143d1fd354d63f99016ddb5a11d2dca9faeb9bb873f5be53bd287513d3a3b365fd26cd3380c9a492dd39848c76ab93e4052feffac06daf9d5cbf769209d97dbd39ac7f9554991b23ae44f1b2d6782f46dc0ec73b80de4ca03eee2e01c58ad9353d37eaa86e6c3681c27b32c530dd1988c66dc38e0c8aa33f593079cbfaa4dc3c18356dd62a9fd9864f5dca986769e53694f44fe88e3959f06e7573c5b0f76621d47583943783684445a944f1f07dc791e50e7370e6c8b9df8e504ae727670f1564fe5ea6f562b1cd8487e28b32ccbb44cd3f833d6f8358d5552ee93012a0fa932540efd351b671bed1d84edaa866cfe47f37378e4a7fa3bc80fa312e2cf6f03f2fb88dfc762411d83693f323bf1b8789a114e72a285fc1ba83dceaf6a933a60afc0de01fba88d5a94eb922fd286ff9ee7a438e1f4c0e3183bedcb5f7e0fd8f32cb474c594aa42dda5154851b9addb01717e05faeb8039dc64555936e7bbf365b2cf62f3733ef54c56cee7fce855999c9f116bced36cdbca79991cd8cd4b1b4e4543f60cdafcaa82646ff33f5a03190a47d1ba09a640afad0292fab9deca09c7ef8bc0d7e12f4a22d4f33cf43c0b656fd3036adf420c0ad13e87172a7fcf73cf3bf8c4033d3d3d3dcf604f58d442e0ab6af8079f9be0290d84f3f2198a9ced2585e12a6f8f6d75f07b9f037e20b860e76d215d1787e31766fc37515a942f6d42761272528bf23d203cd6d802aa1afe8bacec9798bf87bfba813776ac911a481ef991d956edc11ab0e61d1c8754c93d0d38f654999f01c7afca844e440c038e34f5a563dc3b0c3f5a943fa481fc191c7f5b4d49234be5a74758425653d230a332cb9a00af0784e28d0dc55b3f2dca90478b92478bf25755030bf5bc149ddea1c435cf6c0cdef632b353daa66c383938211b754e185bc22276c596ca2a205d659ea1b8ff088e58aafeebb4c9cc99f7fe6ba06d669b993321cf99f7dfb6779006fc544333cfcd8032328fa45dc667405957b56d1f64fef632a091e9acc463a175550ca9f79e9b41e6b7e75a45c3cccb84d1a5cafccc732fdd107dc9fe12a4f9560991f9992a622ca937ea56f1a24b65150d313133e10e560dd53cf7a3e5fcf6b481669e478b3099df3126a93ed4f0c69f6a0426138e49aacc1bf991097db4e8dcecb6f7a09c9cf047ba8536e07c4ef8ab67214ed6e0502465e35024bbe4c436b0e655aa20335ff332a08f167f1a0816c3ccd73ce772e7737e2392b3b393f3ae1ada5435c83af3379f6364e66b5e82453aaf63130a89f91c5ea8db1725115a3d0fab3066cbf92de765c0271ec8c9c9c9f94d27272cc2791d24b26e7f13fe34d04c081b3949a68aa126dcf1f38337728c97c87cccc77ccdc7bc3fd79c7fdf8facbaad827e7ce647afdbd374d00f6ff96fbf3d6f0f1c5887ff168e5e8bdfe69f8dcd8dccff6822fe3fef8f03ccfbab54bfc950c65f158587f3dc736c5f32ef3ddf0703859301b932946d731970ebb675db16ea6c32bf2dfe688ec96ca11099f0090f398ff31b119c2f52629bf99c9c997094f96d7b9c4d666395ccacab4200ceefb66270de655ee61b88261cb91ad95edac8af79ce8dd8fcf7d90e329ffd28f3313fee506dfe03479bcf6a6ade553170ddb6c604a9091db0dec27919d026ac0147991f699d792eeb6a40f93423d79b99979151e2c1e3e74746e6c7af9bcc8f5fdddcabf9ec6dfe47df803a78bd9101475a657edbfe876b5566bf81317f038e3bd49ab789a9b951d1c01ced6ac0ad34b44e70e498ffe13bd418702b0ee88123ad5be8fd0f9fdac3c4469a3a3214aa316d3101de7a8f92670403f2bf6eb8aa21bbad9543926d1a94825b16b7a5a0042f539c52cffb3ef932e54ab7e422b74899e5eb0ca5d3944e9eb73a54cd19c6d055a98e1fe52d8b7d71d9987642e08d1b535454038d2f53b234d0f86969a07163aa3d76571a287b62f251c4a819aa8de8981326df82249bd857ffec0bcac95bfd947adef7fd8fe2d6a5c5dea2dadbd868a1eef3e8a0d4c684d5d1bf5bd4133ce9d44057f665d4405b7770f124d4cf23f7a02dca5bfd0fe54bf2984c244a3deffbfe9d6a3fb7eafe76344de8b180fc37c5bf0522bc19760eb620024f470759b587c1544d8ba241be16eac0d57584f0ccc0a548b8ca306aebe2a481b693500dd42e7582c8ea2fab87230f955f9e6c946852a71195e792cad3a8b2ec52f925970ef22950b349f5291ea4925d5cc988698937d59e46ccde5289246d4a9bbcd5b4c9839e9a9a68136daa4dab872a25dad48637f2539353652b4e3c51a88164f7d41bdf206da5b3ca999fa90a4a5b825b964ef6fe498424a89365ae436937dfb351aaf245529d3fb27cd449b5ec4a9d2f756ae8976f43bf0c8b9490df9f8536f06f61913f7fd315ae5ff66c9dce699cfefaa66dbaa6bb1ba669beb3de3ad55c77adea97e6df51dba7368bbf797e67e6fb69ea841dc30947efe36791b986959861f8a90bc78f0bc74f85a398312b51de58d2ea8423ddcf09470f271cbffd2f1cff261cc51fa7ba5f53d4e2fe0ca8e44f1ee4635b1b138e9e172ac184235585e3b79f0a9fb670e45179763c6969387e9b71d4e7ad56d5aed439e182b4a7bf7150aa53bfaa2156b7b4dfc06596377abd55b603c1f9737dc415c5d868a4557214b7bb9ee4634be14029a17400bc99a4c2913a73a92c95a5b2549651aa75777777a7f8658adbb66da3f9946af324ea4d5a8c62448b1eb6931671a078631f31a9de40485bab9e38e19653e0ad15509c547f19c340befa56d92d10e13943e1b6527fee2e4005b8adce5e554f5587ee45d60eaac2b4aa838caa7b8191b158e5cd6181e7deb5db17112b551db2ca441cabb0172ad3e0c52a7f17846395b35851c6165354e134c594de6f75ccf046f1f3544e58562d761005a255baf2c21bc5da3cc43a859452765048291bc8a594325c4e825b393993383314fe2c41b946b8ed9b167838bb5c6a531651a0c8236994e549090a6fe24ca2b858699212a597f491962c549438899b7489aaeb5477fcf0f30e6d28a9d4556a719f1966c7cfba606ab4f0ba37088e1a2dbcfda902c2e14ae9d239ced65b9bfcb232548bfb315ab89549b2499728272b4d4871245fb2450b1315279dd4b12e579ca634c1463012d491cd42061c33bc6d91856386275b5cf6177e0e351970db6266f9d2259581d7ff5503b9bae4cf26540cbefa28a954964aa354aa4fbc9c107851bc7eaef25bdc178cab873eab7e1fe22a5fd65042cda50b78231542142a8b5027242317eafabd1ed491a6f60f03e1858bc39d91b4e2806cdd17d755128208f56b71eb2a095184fac1248cdba83bd7c5ebe290430242d65d1a820f940d982d57aa4a8bd1db252d4cf503de2e699952f7bdee929626aa6a5ffb75a3b1e2cfebeeee761590cca0d2dedd2e8174b8b29a823e5a641e73abd7f3b36d7bf6766f21799bb144b62a866c6edbf6ccdffdd9d63c5ae4267125b81567055e566f3450fbd75715e466e4d4151ce3e12d5ecac3a5cf9e3b3f6564c183673b319596c9679095131b4827487728d6fd4f12fa78a27d87eff3f2c9d127a43d4df7ff4085b4e7ae8f635d387a14d6c7d18f90f6fce00e407b6625d57d1819fde009bdb000e077615be0ffe11352c2c31f2db6774b2e8389b68294272ddf500401aaf2b3b80ca4ca3e95df3d26f73625b8e94ae4b2ac331f16173ed78c460b3d18161b48cc5695b3a7bf0dd45ef7aa81565e85611fd5a3f9eaaeb81f9f25d6293baa1ae232eeb79c4c35a48519a53506ae0dd31da65823c193a31f5cc8074d48891c64edd8910f42b481bcbbb329a56caedcdd9bfdc0c1ecacb265f6655916ee77077252ba37e7a3d56ce53ebbfdb33a5d35b47582dad66db3f74bcf54190fa6bed4ee70eb56372584558b528aedc3ca2ab7fb6ae7491b48ae9cd23d7bd726336bd9d7dd8d18522ed7ace1f1eec32491695cf71b854152a5675a8aeb568479f985d9a1c2bcbc84635195a9aeadeac027319c16e92fa970d795fa1457e9ceaa2031af814f62429a6eb1af74b986cfcc27959abb160f975da35a66c8eb9a5f7d672699596acb9ae653669a9639e179443eb9be2b3aa774399dd022ad780c6e2baa72c8b60ef0949ad4b215674a07b0579c9916b2104443f2533b32cd69449f9fde8d61607efb1770577c81d9647d79f9ed25942d6ee1762ad949e66457aebb751aa4924abd279ec6592853d02e8986fce7d3ba99f4a9fbacee4e93bdee2fdbff684d0b97abd3cee2f187da04b9669dacfb72594e4a29cd6896895e5716b981e68622abefa3ccda4b3976ab59e972911b5a24227be0655cb783ebc6d5cb8f3430d91371a7f09f488b383fc8324ddb3939aacd15a57ce75f51be0682caef330bbb06da90dae0fab90685c00ced48c36306c2758802631ca1e145e5f746fab4051043489a4ab324e5fbf134ead9ccb299c2104f4d68e1d2930b6a963dcdb3b2503c42cc5b9b83079e5677e989c9138fd65d7a427a3afaa8189c94babf340426754324fdb520ad90a1c4b63e4aeb05174d57f895eacb65471db70b0b7564231e3275e4265a38195577271b388d01060bc05063290c2a6218218695165c811a03690c294b6364c182022c3ec0f224d9f8f934279c0f599665d90a7c985d202105d2873356f0124402b6d8c1144b68d962898dd9832ed030a2ebe20c1904f1a403277cc185ca954bc8c1199feb8b20787c51459defb3088d104fa4c106133c28411421c9832526b2a480f036005ec0a0cef9d40ba73abd061a797ecbc593359c40a50d17b0e08a286e708457c45bfced130b31d3acbaf081a6caa75e0aaafcaf8b23bcb6041e1f7fd44a0e9614a1c9833cf6ac5071842c82948c045e84a869e58a10bd058a9c568850e7e4a57286940851bab54d50283c2a5f166fa55b5b47b16a44b8d2add57858e16202056c69e298f8f85341044870e2815753b70a12889043081e4ddd2a4868238527783275ab4015b1c5f3ea56814a4205243c9bba55a09c48c251168e36d0ee4eb99276f777f713ac245169beb06e953482f018143985952080becaf9ca853ab27cc61d3ebc1b9b65b700403a2f6123b46052fd084b5a3061c907598c60698b2b517680c552d3117c588ad20265898b2e4b3f687a028436765f9303759f7a0e7e021bae00c2084f0841064ba060022107180801064f28d9c11231777767ef27ea589cb999b9a04e46537706132a68a209f729f5f424babb3bbf3b3716777777ee31a4bbbbbb73636131199e497777777632ba63edeeeece3f72952419eeeeeecefd947999bbbbbbb337167777e777e726a3c570f666777777eedaa18e907ef9cd8e458ed14c6e021f00d14c58b25041d1b4250b2c47685a38220b348ed020d1a8e623e6880a0c8ef8b842fd8a91f6d405c9080b946c09131b40c1b263c6c90766ecb822fbcacfcf2b9c94317184169c2406d44684932ba0b04829a744c2fc058371449447c42f852e66e0c591ef8a966dcb931b68c142fd4877a4f3b94182cb123e57322fbb728575e8a93924e12da8c00041451954904c0ef2138cb043243d91f4184c2f4da62481090b1289448ef15333832e68f073659d382e74c0649db0f0a05e83c838b1a2081e481c898fb16259414412ab7d67e993f3baf63cc2922112e62b417e0ae9e124dc9ff3eeb7ba5b802b929e01d7aa96cf2a24bebe2a863252eaee5ab7ad92322cafc1254a149410c75d837c4512672409ed165cb4408a10e74a9a100613a11e609a8a580444f08249a8879ea4c98458194a423d30b310dc72309d8163513582a4ab94a10e5d8d64e1760db8658dac04a9f6732fa04fb73e160aab56fd4b0f1cabe3a543d104de56191aa13c84b755d0b852fd99634cb1260fe21897e618972eefda208e3df9a0b168f4732c8d6d6d9555352cd30f269efc6d823128a02835d2902a20502b14e1414dfe4c6e4a8e9a5889518ca981baef29b6f34fe6a7b1ca1c638a516951c95127c18561e49852ed576a208d4a03f9c7a05a037d939c83f3e52526a67350a3a99c0de7600ac7c14ec74155652d1ccbd262c782b0313354313cf1782f61b8dd8aea493918199b1c1e8e3d79100cdc77dd4f57a7027df625bf5f876b7b541874e8f87170fbf96129030c04e851a7eca01fdeea1ce4aafb9299191a9a2536526c6c6e6e8e7096c0c1c9c901627583d5cece8e6a28e5cf4d9e4c535982fcf3532ee5d6e01f0487b891831b2c160b05265010455185292a743e5a3ca8475d1d66703e9d8f6a48ea00e2ef9896da9307937442878e9f9f12b0a003163a1654433eb1206c10c7a0bcf5145bc22e618376ebb65a50d2826a8895ac88f0465612eb3a72ec8963593a8895c4941cc5b6d46625310f3aaafd635d4d04c1108e5d1d57c92b22a80b520d718c4b8b354835432a1c532f3ff32d16c531a8a6e71897ee59462584fbee635441e677e1287f863a466648b7452da8a35ab64ab1a8065a185aecef5440becad0a6053eba3ae3820bfbdaea23e48dab34e4070c36c4a0fa09a87d71c7a55e542f324625c47fbe2743ca4a90b8f6d105c7ffb5b8a4c509f3b79e9c224afdbae0bc1b5329ed53dfa9972afa23f7dbcf7129493f05d2163b74da1d1a91df8554be67dbcb37229fa6a47cfa0deaa0d5961ae552dd214db19462b917eea6b6712a9898999aa6a01ab685648716611f0e4131fce392bf2135ec36e5e8403118a98eeb86e873373743eccbbb2fc78a1ac87f6a6fa4a808c9b64313c996524a0f2606a42faa0e4c894016c80631605b41439c01433c08b62d1cc6f0a0dc45a08c064e4c7184ca7fa31ae2a5eeb9a8a3e361705ab75a655a7c299e9d8eeb7878523ed85628a26763aafece5907ad76b4c52692d57f2553d9fdbf00814603c1182e641686345fccafbb71e3c68b239dd4e958ac1c2b48da71e0d869121bec720c29c263ff1cacc5c3e15540629cbc71a1c623ab9a3d677cb4c41f899452ce0e0520bc8eb1f68cc4466ccc190d278aaaa199f14b0ec7917f59cb40e92ffd0325bb8208542891dee7d1789e2b498c2580a18329a260d22cc836c0860b90886289255af083904b41a35377498b1234b8c815aca0026ffcea2fec0bf6420cef3f807dc10600c3fbbbb02f980b42efef635f301f3fdebfc7be603d06f0feaf7dc15e3ede5f00fb8209e005f4f283f6050b5a97ff8ef707da170c685dfe2cbc7f00f6050bc0bafc7fde7fc7be603bd6e5bfc20bd682cffbb3b02f180b29bc3f00f6050380389ff5f375ec0ba6635dfee1fbafb02fd80aebf2fff777ed0be65a973fcffbfbec0be6b32effd5fbabb02f980aebf2cf79ff14f6054b615dfedffbb7f6056bedbab49779ed51d8170c8575f97bef9f635fb01cebf27f797f1cfb82e158977ff7feac7dc158ebfa1bfb82dd58973f7dff705fb0705dfed9fb83fb8281ebf2eff7ff7dc19e58e02bf016086770fc0c7af9821101e97dc18864f8ecf705230af2f4f705232ac0a77e7bc188847cf7dcfb67fb821119e05f7e5f30a219de7b98178c88e6b77dc1880ef0365ff3fea97dc18868f8ef6f5e30a2219ff338efdfed0b4654c3af5e07468480e7f99df77fd9178c2801ffdff3fe30fb8211d9f0e183efefed0b4694e3597fe3fd63f6052352c08b8fc2fbcbec0b46b4804fe15bef3fb32f181103dee755787f9a7dc18888fc0aef7aff9a7dc1881af03fafe3fd6df6052372c0b3f00078ff9b7dc1881ef03bbe0518118f07fa00bc3fcebe604437fceb05f0fe39fb821141e07d7c8ff7d7d9178c28023f8077e1fd57fb821149e07ffc0befbfb32f18519117fafafe3cfb82114de0617802bc7fcfbe604414f8187ee861bf2e7ea20a843f2800f6ac8b7f0220cfbaf88b803bebe29700180150675dfc100073d6c57f0388b32efe078037ebe2770068b32efe068035ebe22702d2ac8b9f01e0ccbaf81700caac8b5f0160ccbaf86d0061d6c59f00f0655dfc080055ebe2af011c0272ebe2a7014cad8bff00e0b62efe19406d5dfc0600b375f10b010bf041405f17bf0c60af8b1f08180319957fe885ca0f0396ca4f8001547ea1312a7f75a1f2ffb852f95f18801895df851e95df471895bfc7abf2bfc0a8fc021040e5e701eeba824220a7ca1f0070d7b5036c21a8f2b3e045e5070050e5ffe9a2f2eb00775d2b80bb2e17b8ebf2015500f953f841e56fb150f9452e2a3f0ae0ae2b07b8ebc2118e9eeb06b8eb0ac15d1708be0f2a7f0fc8cfa345e5df59e9e46051f971c0ef8aca7f9342e5b7b1a2f2d7b4765d33618cb7c4ff52a5f2ab72ecba381c1b48a9a8fcda8dca9f4dc1df2b0558d1c8525b485ba95c79090d2a756bff04fae5e98c2c6a4edda53396a8bce1d45709e9a91c7e8ff3e4350b96089113cb51f2a4d440dafbacfe924a3765e164e5e94af54d4b6d0f37264a3def7b9ffe3e4aa78731f4fcaf4b675ca9feefc4240a977538aa746a71eba26d51393b24f8be2d6a6e514d9452598ea234715c9840d13d1941c9625f502f46e0d07fa1b28b67f4ed6f514c4c4c4c4c01a3daa884cdf66441ec10d1080000004100c3140020200c0a084422a16028186ac2b07d14000b869e4876589889c32088519431061942080284106008010081a99a1a00f9eefc6488e4606f630c03b257007b312e2ae872714fe80b28e7b11eb8ee7de9fb51e6850079288c27a492b5b8cc4e0ca9bb0b089bf9989a40cb37aedd12c2b831f3c51c25944241c49080b87349c3b3196f2756f4dcb3d198e2ca92cdcb0142ec2e6d9a85122e32f3b3add43d7208e66a346218d43b459036438066a326c6bac0197ec1a6520c2e8177586b9871c38a360c139b33ce1da8864f61120b5b3b2150ee3873c08193f2949d54853ac1095ba41d9b08498ec8f5c1727ac9b53ed393922d0d19372d94e093d1a003614dee8426b5a2ee7a151f27bbd18d3abe4f18e3775fdab7e6c3dc5ab68d610427098dfca8290f6bf87df1fcb8c19f3f5470c50ab5092b3b91e26fa60887da360a52be693d1415a9a9b4578876e59f20d6152da1bb68e17b847b02ea233e1e05fc33147ad4899561697016d0ae5421736d6450c4788038dbb671df1df7d0d45864706ea12ba9637766a89a6a626c072680ae5847f7d2207541c40690e54b32c3cc0b56d824ac5852bb0c6dd0736b673ff1903c942b182afb33ac6d1d7227518eaec7d4f2b6ab2111680e17dbaf76c521d58dc44ed52898414bc34162d8b886d5c17be1d1913a10845f76c69d02de0d37eaddc0b966968e51ece602eb2b6e5dbff1c89d1626cbc02416219ac459e80dff86eeb4ea4f6f02732c4450bb31ad8aa620a833d6f18fc5c2978f15c36c5d7352b7a285ca6c824933d01bdbc28bafe835b5eaee12535a7d930c75ec6812c69bad96de61e4e0e1b876a6dc03d3df6eca4951ba959ebc6004dbdd9e79529d42d151f3ea4da13185da3ededf38b31a153b2c55a8484415f44dea024194b51e77933876df34bd09b06f0266e5f9cd888db8d8d18c699f7cef8b5f12a243ab83e1628118d96012cbac3b7782f5f40b66e9ce58a3252305acb9042a757bac77fcb6d75c5614eebb15e9259538483d592525bd64ca04f641689c43cfe2d1bf972a108a94a555e3fce690c34696ead7ea767bcd61f08ca4236318919832d3828f02b1e4949dfab3a0afaff772198230a1883356505b8c45802ffe1a54d5ec1362a726f07c98c54585572940f8c54fec247823cea4e16379e0e25f19ac40c7eb86c431d1572332914274e5def29ca91e70cbfd20513ca6683ad34c2ab64ada6bc3620a8784f67158cb47fc9a3f3aecc7bfdb47d4d2c5644e5225f7222dedf70bae09f1c2fc1432f051c83cc8c3dfad0f45f5387988768efa311aa88f10c9fc5a2ef726851c20bc7d2e4405d39d057c3428967017d04d98284bb7992f1d85ca071555b65112092d52c7015e4ea5d53aa95f27498b5e20bc9be0d88f82633bbe908aab84ec82d91f496ca4ab16bcd68b6f144e1c46158a7a2d738b6dd9b98489e7b655454444b1f9b044b46977e7d1ad5f477e067e20dd9a6c3d0e364303e33c65a092e9b6f6ebec860a55f0f9de0ce7ad3c54d0f9513bbc754574b23e9f157b04780733720161060a77eef886c4380d5a3f33540ecc2dec2015f41035ce00ceef0460bacb043f6b0f7c27b84403513f0761dab0b0231a8b42dc3889983274a0b525c4c89a73b6982dc600d5be0f354aee607f9493b4f784887eae02474533726db297d12ea77290102158c9c490613faf2a861e75309ed024bbe9b9c708a79da204ef695ff0df6a74b70c0871ffd65672ac0b7eb68920e9c031a3f461bb613aba7b1a2fa8b41d178002e5b3ccedca14dd865f281f9078b7f50a41029688ea389303ba059932d58a0663e663b275a8e13b1df3f734a14bb94ba7c33799d75beaca92a1d490897e38b2cfd64b4c0b788475362577926fe0921033a0949b9a22bd037894db162657dae80072abbe851d93bfc3e781b07cbc2a50271165bfc27848db09f3f223c43227d07a412d214578360a672b0f9b31c2d5ff4f74c88baa2ab6f141d1521525f4e08e9bbfa201c89bbcdaf1495af381bf3069f145271671a8191b17422b12292a9a961087402df67bf614660199c9896d6f5775e7c567213d4c8ae730cc42221055ff0f402c902a7e6c875859e566414a9f56b5a1745a81122f0dc94265205b401c0ad2b8ecbe818f4854ef8ab42a0b53f3cdb4a84f755b75126cad72242d20e5c6337928e939902c24af9872da09704a499f320f751f0b2fbd832427214c3cc2118de8bc28425fe1f2d1690077c6a9a7f59ca0a2bf3172667840627f53dfea699579ff59f7928f3cb946c22cea9afbb0032008baa6b79ff8a00c3667ccbd3fa1c8282c180202ae064f858e3156109c5c79c3778102f78806289ae434d4485e0b5f18a4f82bfb40c60191411f01d0631d3dc190ece090fae56035c442ead1f6edd0803ee7b953be3077b134dd02c65c163b7cfabe11b0569d68b37dcfe6a7e7699d667d91793561df202c12728d18db0161e2bcf0e4dbdc8ad74542cd6c3f0811f2b1a6f2a40ff9c8073ef5299ff98cf1b1b468aa088745c1f8ce38da69915dd4134eefef453338ede853eef7f2941f48609b454c5cfbef3eaf2ea21db2db879d8f3c39f2b4c2cde497c9b1e84f28535a747aad42ebb249aa31c0cf213141734a510a31e002355e1d03902fae08c22ec0859df5a90c8073145ce05762c85fc8c7cde1aa96b76eb26fd81eb773abd2523acb6dfce923ec8f9bd96a727931cdb5b724e6266825226795d48c2d19d543270c2ce777f91524644dc32433de2f6153f60fcde82e95c01c379056fd61f34ca02cffa4a67381aa0f09d244b816e08168f404fc930f2c1f9783abf0d22a948bdeb9b8376189f24e6331993837c1b232eb140589a9429fa5e4928e627a964c2c97ef85d6d8a3dba18156d06b42cf0345ee8e120062bbe6888c552adce5bd291efc0618875532201727df082ca2faa11dd46262c9a2070ee5de89189b5fa9a6d55bdad20d79369f620b1da05ccb366b3a37aa27dedf55a4b4c6b25305015b29b0323438fa08b07666eff1ce21d398e0e83e00cd09196ccd0ee46de74a5f1cb01059ac03a3c9a4a082b65a3c8988b9eda1108ec9403b9c5de265d8efcf13a387866484fac43407a92385077f9b643d9a386149b17b7a477750ba42c7c2a9a77b3e4b79e61569902ed7d4173cdfe2b54a31dd6b819805667954e5a4751965ac08bf9e713af8aa85f2d6f8ac1b78bb0b1ba07cd5bf08527f1a4d294733337e618f0bb259f96bd7f2ee4a32a4e4295146287d4664c82e39a09588b403a7178b9aa8b91e14ba3511707660c2c703e306c29d199f0d9d953e5ff625c53fc03eda24dc81d0b30d537178b1908e52b171b9009f3652a26dafc8a5cad5ef8a7f1be640c8037b92294ce3a51dee1eba66188313b5ce45d8c357697b607e24e153a4d91788afda8fcbc09c134498c4e589928bec3770c5a6ecdfdb99bc73cdf0a911f4ecb0e75e5338b93557f2f16b1987ac160e414943238aafd7bfd936b1792adb65449ad6ac6bac2bd8fd289a0211dd0b4ad78ce3258a55d24560973b48cf55e5051b32432411b9d0d4b0e67e4ed9f9937aff6861c7d2d0352edd2cdd760ccabce0e70198442eca8df812948d8481bc978264c5aa9f26f5c01f5aac7d9cc563c09d4e1b70bce7c3b1dd66e1c26163b609890aa0f800ee322bc22f85875be551597deb6eb7094ec0a4eeb1945136be237bd1e009d463c8ee43488352c73cdd96773a7dac0f8bea55cba42d0794b64a73d2e980816cfbaed781c6aa06b51f659dae1c81ed85d36e153b57da9885a525ee9120489b23c276bea0e895b697f72c31ed88575475858a9e42afa2afa1d7d053e8d442676106e7c8ce4888eeb66149ed97d5def5d62b09c077714ab6dfacad816f1a1af166b6b2014583d5bd93f5339524035472d1d75871a9e68b6a83a67598284b1a2d176ccd7d9f5456268e12e4a6adc6789792ca3b01f29e3dc857202a600d6d55de5c07273a86788e8ee1edf582cb9fbde07caa546e9005ac7f1c9138edd6cde523194bb7fe258c6fd7f4a6ea0d409ddf62d6a53ff0b4c264b89dd520ebb6dcb16594959b863d38ae118b060b7cf4c1e62949e437cfc78e46c6ef89393f728459fa8ae08e4acbe62651f2c31c513b49db553df2185d9f12e23d6127341507a0a3dfe6e74889c072681955c5cbd2e4b0ea8e0430ba57e159d157ed6a156f3ea0d2edbbb2ad3181b4dcdbaddc3870acb39f0cd9c57024c4870ce01f883ca9b2315b980860476fd4dd4cdc5284bec02e60e95ac48fbd56ba5e71c16b9364bab8ff688f16d61625e28b77db4f7f17612f7b76e3798f95c03e7fdb8a4cad466a36e2f0a3f52cd2251e51432f6a69521846afa678f18f0b8e9b6923b4bb1188d79885401e82fa8090cd7d53d2a6144687172a51e6572f10cd5d1b0a13c8b9e2be11aed72f650471983eba2f1535dc0b3d3841947673bebc42caf1a57723072c3f0a6fb4e8ca8ead49941efa7f452b5128c1d8a4bd58be2174a2349c0b0d4811bc5c51b1898b932ec9d5e91f43e6be20130cdd2a1a675084e75c10e0ab6b8c2c1f6320bed5bc0ca2ae320472303e345f7d0639511a2df7f264cbe7d245bcbc99f16ba304817cb57daeaf0d8508a4e8f431f7b351465902aa68fa5c8c919d158e6a1410bcc3cc8ff50773182a89cf92e5dad4bd7ef09b1b875d11bd1dcff16f2c3b5907dc0918b1a86e341d3cbd05ee0ada485d2cae7e3a288b879e77c1db3cd04210bb2c88f55864b6304f44879f79473446bfcbdb62ecf68020e12d51b7e2398502e8f3f006454534f913778b33cfb280ff6af11da4a0190a74ff92c27e8a93da8379e5c5c9a0b9c617aa27d7590946cef564bffffd3df3daf8689e29810456a6243810e2f2421cf3c708223711eb42710fbdad89b38cf2ebdc78dc3d188c833fe8b4626611c9c373a4b3c08bbc0b51e2bc13a76047a8f26bee0332f72d3889a34d1969809c242d351106c2b97b1b23cc816693fbff5e52b49ddbe2abc5793a96f369318ac083a04b383fa7148e83e71a0fe972a368d910649f01642683b900a7d4abb50013c72ac97b46d3611471f6a87d3ed7b7204c68729557d883469f62a96e911e89752c798a0f2853ea596b982b4ae8dd6d92cf90ad3cfc796d05965b52a91f2e65a1167079043ea34ee9ddd40a19d7e7dfdef008e5a3b0745bbe6439af15b14b07872aed34d68bad17eb2c3f4fe27223222870c5f48a7f0066cbf9136387d53d7f6ce32b8afe50e4fad9a09c812ab3f0770e1e01fd465b212956ad9325abdcf3278a81633b8fdef3841db66daa5d6eb4a26a418697355f3cc448bbe0bb88c6d37b2c31f0aeae963eb307395c065d7321c459dc167b20b9882f47e5c56b8b84f6a8659c993b13b5a5c0bf7cf641ebdfe3a0fafbbc07bc76636ea1c0579078bb17eec81cf79402e009d68279e7506d4d13dc0556c98dfb0ccf3c855cf16ac55626658f47055e77125a488234a347f862ac785f936de0c2b22459cf21cb8b26d8da80ab62aebbaeffa78a291f15562c46bcf813fd75ec18f7a7db6044e4f70b4eb3707de20844f64408e98b385adb9a131800987ecda89bcecaa57ce810c9e6aad1575fc8296bab0e82e36b5ce272f8a36288a8b36603e951befa3c4578390ba6dedc086f83a2fd7181c8c3b3f17384b050b49a0ee78f606c83280616e6a0e7d01e8ed0d48312662f6a6933e2ff3bf89b2d7fb194ee0f1c374ab69334e65ef795d9d483ae21a57115084892259cfaa7a995e7ce72d8869667ca217b9399047bee81d797f5f5b4d657372620d8ea2f08460e0c1f57c4c78c182b517e6ac1fca82f6f5e736feae807622129a7f861df57a09c9b5bcd35a866bf7ba81f2080f8db1456d397d0ece0d0cd29825a5c344ca7ae9436abd65e2f0c290580ffde2ac9a7339faef3a930fa94703e1df9b91f4708eb086f1e44fdef492ea567e4f360596f88547a110caf8be7614b347c9f11a1b503794bb29ba52cf13e4094075ab0609e0922a7a35540baf0fc774517278eb801e7616336123d375bc427dca20a6673344a030dad468aae6260323a6ba75065b192d897c8049da355bae2c6e72a98c6d6a637c26009113c63a9390fd61cec4f583d2141a9654b2f176a6c257885d3a3d0044db333eb8f8a5f60f680fdce7e72e702ac750ce9b221047c73de797910218ca882e3a21c5b78fdbf7254d489a291d089a001e171d6e1fbc2897b33f193e3463ccbfef423bbc8cc2156d37e6fc46577a1132a1bc2147c55d8b320fb3c9093a92af11a55370945e2168345c6173c748868676e4f359c901e68214bb43c883d94cd7e793cabf9469c024c5ce585c0085f6734adc0eac894f07303377bf4a1fb731db83346691a8e100eaae3ff91a7862f5805ec70a94f268f6c4b4b942e933ecbf6406988f7acb525320cbb83f08dd27df781403de7095d192e9833f77f49ef347b4e57d79862a600c790a5ee528edb0dcd28be1b930e9aa98a96e3527adf0afbcaf029dd8d9909650ffff17a6ed426861c53d0901ca0f56fd995fc04008a7eb5a290c5e235a4de9f5cc5a7767743eaebc0b36cec8ccb52c71ef68625c43253dfd6cd2a12170cca252f22b3ea541cb23bddf0db0065a3fbf3527d97e009a4d3f4693b3e5afeca161e0b8dca863691ac7364ff9bdbef4820134278bdb1287da0da96987f3028e000bd93145b071a3582fb980d2d11b57b5a7f063523dc6dc418188916a990f023b01ec20d1ab4d1a31d2be8c791276c027eebaebfc6e8c83154e4e34618e63e71bf851bd8091b8e20f6aacbf9031069ea5ba6e9fb3574ab74a3c760d72340d2004040516f60e1c4fe96b7b61dc0a665013c2259e69954130e24a600814d502fccf0656d7d697d818afd17ab08558deeb858a242f58851649c3644b12144d55cd3cbec6a5cf4a6098aefcdbcd0bb6df5252a9a5e5faf3e9695d7cebf410327438924e63f3670631bdd0e432434e0959037c2f8a085447e563b3bc141bfc14b3d95f66cc0cf1ac0f1d7658426e743742504d482462496d63d8c567a5ca3473b1ae488ac243d002540ca44d1c8817fd53b41231731fbd8f1c36eca96727aa78b6c256dc5182638b420e7787dbc626d2b03493a9d27ff280718d0f376bbf4ac6e576101885938487dd9a3be01d7b92e1559aa9e2c2d53f02f91cbfa691a0009618a2297326c819b2c350636d6938ef01404a53c90b4a13d05a548a402c87d8b4d4aeed19f0405989cadf35f52538fb08f31e29f30a1f2e17a226628b4c57a0fcece4bb0caca823db2ea24306f25a54cb41831502f5d94c510dfb8c1313ba0063b85282bc10edbb50401ddc3b3a3b78799412f20d3af55ff7e300c06d37a8469944c8c79f4187411f24dae4ea3736ce0ce41049476a877264d28e853add467063c518011a45e89baa3d09ebb847059b9a0c68148071253fd7a0844b3bc830f27b3a937e3ac187cd8e8f3d9b3fc559d6105d540dacfd4c69bdce8180a8ef7211e3c2731cf0a6ea0d29f3df8ce2089551465a32a1fec8e9724caf9f6b5960f9729b56d51c26c0e3e64321589bd428e8c7338b346e4ac22bb360fcb9b89cca0be226ba870e6c32146304991433a06a222bba8e49882772dfe9541997fcfaff30468faadef75533e3845f7cdc7970e458855567e68bbd289b5349ab79cd19567fc0baa93894b2d18c4188e445c66d1d323de802cb1a11a251acd0d9498e2dd446fc4d0c3032b15ff8c1301fa474c37f7a646c9fb12883b3450af86a2132bc6de7f53fb7c01f90797ee618ec40642e1386c2a33ae710d2ed616c613b77393be485e45a6bd065a55ba4b0bd7febacafa65477fafc0855ce0cb18124666cae0b747b40243f07f6c4b6b26e82563d7528a60fb4158d90d82cb7f13fa94e5c4c7a7f3fbf7ee5ca1bb43f4aefae641446f00a160fe3a54671802e122e632010f52208b5529271b60780c328eaa1e11dcba426f9abef358139aadad7193bcbaf2216e9a8679489d75c6ccb5766e7355a5144c388eeb661d3a1b138623075a7ee66800709e73bbcb430d2ba48830cd610c225f002e4bba40621c78b67dbce47bb925eb7d9258d548bb593c4a2cb1d0d1034e53d4e79f0979b90b795defe507eaf8a6c6f9347a97a86c1fa5035042ac1abacba4341db92d34b8b80cd509ad49feb9c9bd6e4bab7db0086288d704be611d42ec8b1db752cb0b84fcb012f0080f68cbd8b8fd6c008c7aaab58195b592725b2bdf0faa4fec9a63ba0bd26d171deac847696d21ebfab550aca17c8c3b5f64982c980c5cb230a0ffa18bd6e545e63dbe763b1f8a748a9676e61565b1e0870538a29d9a8bfa545879e246441969c4b8935e6614b1bdbf9b2d45e388413cfaea829489c4b3c8b83462c5c2e2b6d4d35aa9e8bc5baa7436fa5e8a9e88637e4022c06f06bc5106e48834a5f8b07400c5ebbec14d4817d86add15679174dc482a01463ad6387765bc3b23d5dac8b73578e6f88714638316d26c6c540c8eee972d4a22acd04483ed7d9ece3c62fb5b26596004fda624c29ea860bb85ebc04677dd4b37b3e9befb1c8bad691081b349b2695c2d6ccf483824651b259604bebfebaef1b5710226c97dcf9e0803fcec037709fd757545eef2ebc770698043b3bb7fb08b120876bb88e4cd6d678dafb77e31f606a0dec0b4d71635500deb1650f55eca1baac3961b0a85cb556d80466369c2952ab1d9af45676986df38c404ab0304d4950f0e7469758ea3f4ecae494704e07f55ee1c0e5f74ed884d1a6020efddf5ada633448315490f69801958faffd25bc17e1d8e00702d1909e01b6e929a79626b5e0f25d62b3b4a41ecaace592cd78b07d17c818d903e64d50ff820dcf75528e8773b7f77c27c2feb6fd4937be255664ed8e79cdc1e4d50de3ad3a456356b6b550d66c17bf9a01a5cc836b08dd355efd7914c136a56b960754f56021268a710b282c072a260f5cc2c506b3d223465acaf051036f1962ca6ecd3b769c40233ea1388c8e27e6fbff44390389a7af60c7c71f252f993f11aab8b1e74818753eff1c0966c55a6fcf94ac237d3469258e753b08f6de14361d6ce50e58d27634ed840f71496cea0793b80a128414c4fbbfdde27ffa2897c94d63edf2fff9d097c4cb3bddeb5ef7bccff66824fd3b6d7fbe6ff34d9f83038ed2b5c8e81d8156c1687d78e18db2f055087f08043287082b4b061b5bd211d224a1d4eedd93f867b88c25fff88dccf87bdc74cc690d08fa162d9b86ab650cd8a6ae295561a750aa2a13070de131b7a55913ec3dff0f4188bf5748954bea5c7e4eacf81bc17a6a184ac632bea9420901a2d108d1a380d6cbfae33d0342e108d1928512e9f7469a0695c20180df06dcf58d73d1080327a3e3291d77ed418a963466fabe6b6d758370e63f6fe3dec28ad829ebac279bd8c6d838de95e91f98e020ecee5719a5246ba97b8f7572dae58fd0b6993affcdd6a43e983f20c54533237d132dc12ee4f8c8838502e70080346b24d697d433ef0b55d11624f2e4cb651eab2c1fc2896ef6ec0010ba55a8e820383439e3c5f42c6a43dd4362c87a3cf1e50bd6bcb3e9194e6f164c1642129d684a497584ad71ba09d1dd0635a7260ee724078c2b558bc5be82d2b4fbd86175717a993dd06fd12db08085ed7467c0bcd872cdadab301ff6e27b9101f2a6ff4d8ba8704312e2095f504569e87822476261e61e809778629cdb5dc55219a2946ea6374db0376b3f2904cc5dd2b501d07ab80087841334518dfc75f985c4a834ba4dd6e863e4345e3ed98341e5fa42f858b3294dcb80bad954b8aa8c0fe13c8c8f704239f5d4df293ea6c28240240bc637b9070d9bc2d7d02e32cd697d1d8521d3d2e34ce46e7003c6ab88e7a6af2a4021ee0c984c62ed3b927ffbfb26e8c8d6971193d2f1c5c01adc17e631cb359c73015735a3eae6f2c519198ad9f42f9a1217a964898cf6f7ce9c45534497db944d5e99358580c2d5b566e2cec050a3c48bbb12c1a8ca3e9235e13d1444a80031b9d362b287a3143e6c86eeff4eebf207160a949dfbf273bb6a794cc701077326da9c3e2fbc208d63348daea79e08a8a040b10d38cc7da4ac492b6e893325bc6b0d534dcc3a4a6188c6eb580b53f505fdd798893acffd0b1deab8767c53cda342641ebf82986382ab531eeb875fac03c1e8f4418d170eeaa7df3247727f04c7c3dfb3598a3c33e50ded0bfeeda9c0b0f87a0bf7d06ef03c474ba2048c0a2353b7c85b00ea7937bfc48d442c7b85f3e65037fcf2415c7d38d447983fde0323c115b45020b2800c2f1b14df26f9f93c250f0fee7c367c9fa13984509933d653f685a040d3ebbd8f0f91e6ab89c003b579254c758270fb2e7984c0d1c0239059a8e74dfadef16ef2864d250b6d7e33c902b11491d77ca0ae35b33e0a07762ad06037a1244103429a3c19c023c42c5c0412101076d6988d04ae24d6129fc7301cb7021fda98d6733f9d6d968d4ef1c53554ed63959b5c0dbbc4f4430a9a1a288bfd6a98319824ea338414b42bebb84a2a42dedba3793234f828336d83c8c991d386084a60598a4ef671d412e5343367a5daadbf539272c0c67f323e6aab78d833b378144ccce6264d4ba0f5f65343588ad626f51f6360087e93cc8e82986456f20173ab4fe75cfe6ee70c9178ed24c21561a7415c89cecbf6e3d16a51063a45aef0cf534dc7ce32ac8de356cc528a5aa0fb6ee16542842adb549669c141a50ab2d4008777827f1cb8a4c106e3e4a37eac07689cc40192e910d71d5d96b911abb75033b6b91a8ad824ce7c6331af35805076d3fb0aeb20da0432deaa61779b3fa0d5eada633e1ab410bc4e8ac05a809e4e4b90f3005ac1fc6e0d34e9154ebb6bfa300022b28ed9be72e3105bc3640b19595810076f3aab0314817979526feb6eebf7868aca4ad49b1649a85f228a1a7804f22551fe9da5510095888600f7d6abc7303420398f1682242ce20f2b7dfbffd5eb6c215bafa7ba553f71866b0eb4e47d257a1810166960450917f769c6582443126c0241185f7372e1cdb77ee061c6c2ac43a0344b65f6387e83084712137e20fc2a233360ddb18b0cfd10acbcd5ad41080985d9b5cab7965c2ab27a3f0529d9dd6d07c1d6bf0d0be707b5d69c683b6eeb6e6a9e8515e42a6efb75a62fab2361f235fde9080fc7d07895a7d65edf77d9f1fe52ce12c9de1a0570ad7716088da90b9fe7a8527999325dc9bfaa9078197efa95af252bd39ef924e96d1ca8e0fe7cff1d12270d396d09d1b160e9560181973cc70561c65eb6cf8ffe3ad236ee7061c4f02cc90089e10906300fe4c173cca890436a8ae6899cb0eeaa6c99a0fab25716e367ea814df9f2ab2ca5abadf652d70cc591416564165e9860da457d370fbe2f69d219ba14f6d74e2c4cb42892c0853e1dbb028cb49694f4f633eebfc65a14fd4374d5a34733e288e3ab314e922e33104f9bce6d137edf119de8ad5fe4bfcc22b3dbe2d78f515b9f098a768d2c032bb5041026579e46e422dd6640d27d802f2b92bc125b9df5b2bb9fa36a640290f1d4caf2fac484cc06236dd14edc48ae1c028f7a0513cb8c233a0b153021607eaca4c94290d816884008f728fa85c6a321b595b8b4fee7701465e0a887b34ce401ba93d93866cd9ba41c12c99b1ad32d7919df403ee2cd9e4d40491a4c55eefaf5108299b0ccba5c24e16eff6cd88c545eb8166c8374c73588d8104aef40806354900dbbf26c25ab866665f424995a0fd5cc1b83ea7ed4b9c24426875f90e66e8763a71a01e15c60261a70648579dc3bc55228bc8c31903350a34c1fb31e389824bdde9e6fd45e876931f54e988aadb434e8553a6fec51d1d14df848f5b39525bbffeb45ff408293a6fe090607702248804fb3b8400f2ea2ca89af7211fcae41bf2676c57a254770fc9db0cfdb4551fd382cc2628738f898aca16996fbcc76380bfa17438fc9bbe21123c81d5e0fef80c11398cf91c94905d6a8a2a1ded801294f22383fff60e2556c549845dd1848b96539a212f87941a02d6e15c16f6ebf063f91ba840c4372152220b171057ca18ac6e01805915087965ec6c621196193a9a1e285012e516de7da1591166c516f4d79e720d1fb2578fcf25941304843cec79647009dfafdcd3be4d29b1b2ea47c4b8181d8a641f01297f052c66c56f9dc7a0ae8a8547dd9efbf6ad1c9f356986143b7ed6504582b7010423805e114f7142bf1351a616184518425104a67591dc9a5b04de73d42d29021b34230f542f38442d10a1fd4b8b9c080290bb801f7a4689d90cd6d851aebd95457c39e807a13caf6ded826a85a815ddbec8100fc9228e3ee8588126510eaf84bad0015083c719c3956636e2d921b4f2df56f48b029de0b48ac5b236228fe083e031fe0f925b77bd26c6a6053d0077be8c6658728823a3c2b925ad82a628f658fa5f44b3ef8b478b8d51c85152c9e2587ac7f9a7be4b101a953900cbe45b246b0e34cb082187843a752793cb002710253fb2f7aa45e8da27edcb3ddb313f7b094c9f54055d2b61f501351a5ab2aadb0b61cc80ef314ff4ab783a0ae16ec626265eadf8c5d71f89a7b2a936209dd8a3e5779903480b159f6ab0451580983fd2e747f12d1c00943483d09296da2690cd5bc545f152fbcc022861901a4da55b5c74bf25bb09feea946bb3bf94c7b7a96c5e554963638e40e088649163bd41b05988490131af951b80ac287f9aec86ca7a8faaaf0e88ee6ae090972ab5d8b3134aed521416ff4fd5d64871494439f1009b53c67e7f9181dbd68d047b05e64c0b5a89446eb7471a00abc6c6db9c0cd4f8c10b8d1fe93c5da0d2f0c9a59ce4f5bacb0a6840fa2a669ce1425d6b7f3577b6fdbdf667a98581e89de94c9cac8139eda2ef1d769421fc202d869e6edfbe74a0629158034f404437731a2144f4e03293122851c5941a96f9da3a57e59aaada82cf5ae4e13266b0db0b2704ce674a03cc99b0829bd35d88577db9ffa83b2d461955140624d8cee3723c44ec857eafce821e9d5585f6c414d0f90103cd1aac26e5b0b08c69d0068e5b59df96efb50fe27f9ea3aaf8ef4366f8c884d573b9e7dc819239dafc88a3911cd3294966490a404cfea21b803953fa5398b60796bbd86a070dbf95b2b32b75d43485c8c0b8e7cc67cc4d6ba6cc4ff5989701e00d5968a4856bcac825b8b51e9533d53f583990c3ebdbc70f0404a5c05fc604ab73b245eefc092023d94a49998255c4c165313985e2651f1d8cd9789b77cd9ec772ff2e047c46aa9819c5ab461cb052f1e04f5e8764b08dc882b70d2c90241527f53de8520f1b451df0a5de2b1ac3fc70d58d9de121a887ed0be9f4181363c6ed3d346a863cc3faec6b19e476d29e134b7485430c7bbec1949173d0dc1500c2c8df96c029a073ffbc2e9654b9785beb439d203f0b96abd44a63c551a581acc088cf3d18d8e010e72c18a0a8a238a865b74fd47940afeb524338775c8ad9f0bc41c15d5fc473d6331dc94462db9443c20eada804fa2dc046ab03c1d8a7472e72574f924b621099fcd72407464f46100c1aaf40ef39ae23eb92ce237d2ec071ae441c2c7f43e8cf9dc4cdfa1b16336c57a53d474cffb718e75941df08670953fbeb05dddf38fcc6432f2dff5ac80f736077746014b75f77f1188a117bc5c42a69d59b33f76761e6bd636ac5467b723eaddae97917767a9b4764afd5ae239f9d65e9a5ce982e6357564dc40fae15b92764b568aca946ec6685957f99140b098d067c8149877d329fed61511c444c684c67664ff362fa85b76b99fa97cd155e3e94e0ebcf8b22805e28cbdd8b3c8da2d47106e2cda75a6fae759f71e6b6c33dbae16a6f3c947123311dfa6717883eb2e38f24867825917cc6aab32c8a65881e22c628a0d51d2f6aa66897b70e4afa400389ad59c31c530f7153e5a69a6fe296117d8d9d60775020f4234d1be5f5fb11ad42fd944ada0be201e1c1f9d32d42c4c9c496f5d3e10afc540f02913590433038fff6f909f74ef61af49719e4e273a0cfed51127490d9c8831cf2e251ae621c9b3cc53e8192f969047d187e9225b439058ef584b3ec937d948644022d099179b862fcacdbf88cb4c385baec8d5470ae8a43e8b2d04f843cb829c759f153ff2f2962ce4b48d0ca6f8c3187436daaac1f8e763b3acb93c5d36b28ed82571a1dc23e18d28a75fbbd163c5ae51e9ab74736fede2b11c86ea792d4b8efef8d806ed0723ba9b9471a527a34ab574044a22ae4e5e23d36a213f221ae8f0f7c3993b5c3999c2b7b859e0386e4065195d9a1816ad0905950254be89e7b597604c60b385e1d40832e8b90d5782d6bd600de17050476f1492aaad58c33d5c34686b9fc297aef57d8d7a29affcd6801e8539980ab61dfc8cd6474e18ac0123c6794459210dc763d8d939564974fdc4e1e48de9e6a72059717bfdf76939bc52c2ce104d519e4be91691e8377f29ef45c7d4ffe8edd2d9673cb86eb25619bbc9e40df2d2dd678427283b4e69b4c43ac9c8905b8ba26113031ff381999cd07eef14b27e39cdb7c614bb39a7e1e2a7e831f4af16da5dd432424bf35aad0a28da8de7039738c735a39c71f0d3a02a754b3501563311d701d5224692fb40fd43466b81d53748a6f55ae58cd1165ae2016523d31a5a7242e8ed5a11f162ae0ebd0f94940eee4a909620d4154121c507b01d38a9093cbfc3d1324407c5eb5927f4b349ace513f906fa2af708d88f9dff4ce2e78e4fbd4a9d7ec3e2510e63eaa1227e220f6b9ecf112188bd95d213b5e19e6d9fdf6be208c62a989a31dce9c06cfe66e2db644597fca6763d424ee4896f7d69772295ce5413d14525c0b9929e7f8c39b3c742f8d2bc5796080dce4187661fbe67215c35a0a610fb03da8374b0ee051102b9a3b72ec66798750f3c7bd6c874da0eeb750085235baf4a645800c479a2347ac2db015a27e236485496027de882f1a8b8a32150bcc60280231d6522e1d20651d591f0d4cc1c0ce23bb0194a414f4186120d5bc6de6d8140c1e2a8b297624fe124c18056b02c346988291be19e089615a37e2a1b21b1ca01e6c1694345f49196f3dde3785cfd9876f8bd8b5566bf0dc185cd26a5b1faaa41d8e248cf76aa3ab737bd5d1d83182d7456b747cbead6c4494aaaf93848c4d7a80695d5ef46ce766a58fc5ab40b44f5ed5d46d83a280a243f1a93bd0938acb7c0a5b2d1e26770c65bfd2e2a149c30e4fed0ae649fc5fbe306a77fb243e2b01a399639eaa23922e23ea13990daa8922522c6c3e94ac880d8707606971fccdd7aff6bc30fec947b141df86c838c9c2392b68d30ac6d8d3bb8e0a86b319d0d2502ba6832efcc8a5600cd1586d31bcba10ee66080482abd4fb46ea4d3678279c018a7399db99f81233c39e5f0723cd8897ccf467c89260c326b9c41e77136af26a3b972cc61874bf8c4fa6cf02c11df424022a0f00776400c49c1b0ff8c630361b9e3067239549e250dad5be14361bc20242bc0c7886190eddaf7e3e3a9d63ce6734457035d02ca859beb04cd0841361c4b39a754a9d33376287d90647e625951b1eac1c5e64b520509b9dac34e5fd657322142fa119fe1e7eaa9dac1a44e9daf93cfee98e6398c1f4366f82c4899f97c3d4f1b8b627244bcf13c62399a1387a8bdb82c350258edcb5509b7db00ef35082bc9c1cebfcba4d5aa3ef6c79442b4ba809915b16bd346708c16a02a95b220dea6abd819ef265d0e83cef9ecfca8e5b6a6467c9b167d18524e2bbd37ae05fb700aeb5a7f74f5d9bda2f1d686b3a54a254926db7b17427f85de99e442425fd723a0d5f8359d604533f0bbf99a0762767fd89941ddd922539469150aa4753110acd9bd52c35672de04d291c1dd17a0c4d01086ac0afd947be46c289eb8480c0f9ce12f16b9ee07b50d135fb404270aab9db50cb3a73bbbc1df7f4b29db6b432dcb6e3c1b1d261f091e5eb83ad5fa6703565567b9143a2d0fcb79a1835801ca29764144a769da946381d69d642d555e26c064ece57ecd1d6c11f049a8215fe35545e453868bfb9158f693c03f737840bda1e72d66fbbababfc4a3b0280634c47279a531fed08030e50186f01b0d37590781ac41eedf8c50ed160954606e26334f8bb34f8afb3e953390b213b55e6c436f557667f7e87d0e53f09d0a51a2efe167df0b362f9ecf0b4587864b594739c805c61428054fc40127b4a89661979808036c7a8794548fcf7ba76c9d6dea1990fcc159177ce34ce0fa9ec2b6598cfbb2c8325a2c49321714be67ace643539e70efa3998992c2f235c4de5c6e6be17dc873345f5ab9f75d6891de7dea3e3c7d89dd2d13d6d03ddf6ee1fd6763cb8a4014623ca256af7e7f9dcaecaa487e82cf163191d5d54322ac0673119351ca4380ea6d909dcdc5e6953246b0bcdae7a38a2ccb5ecba7521fc310656e6359d155cd6d976689036dad860a40a57f2be2c6e1928871182acb859667c3edc337d3a1a25ffb5f5f0a732c4a2f0adeded62abe871816273726c26d89226a3fe4c612ba633c329a9f491ca92faacfbe3d5a5d399a7e0dbc03af97d0408725fe74458e2a2db05d05c96b03e33d064ee358ba03e3df399962640acb927cfdeffe993716d7e125269755ab1bf500ae9624dd1c881bd8b74bf0b0c49959460af1adc7da2206d70dd8dfdac74415e3fd1011d9dae8b89ff0e04ff1344b303aad860aa2d248b78749548d467b78509e4f92c3e61a2480a36486c41fc9c3491cf10418d6fc9e82c97ea2e9819bd63c79b0614c82a4c1ff79746ceb4b65fea2baa6f7686a7b9389dd0ec2647332a50de73ac11c459aea451b949579cef6fe9af50ff37c36fc8f61ab484d6907c34fb994e3f918e54264e03064761fa1e317abd33d2b74684a310fa876e97630a6667f9e23ea211e4b1c6a494d5a74aee9a7bba607d657d74f96b735e3b0f5e21f920bbffe78866833c079e538ed7f96624d4bf98b94a9c9b549321ebf313e745555bb4d53b4e7cb48fee313b72e64b6acd240d2277fa2e695b538e76ce6cfbcddd61ffec232977ccbabe80df1c229ce2189856afee5741cd37dc6b6df01599f1ae6fb789f14e9ea2b3871271f96141b69cf7aa061dcc0d2b46eb76eb46e71980f9f0d53484a1fe71f77dbc9a09476abec4e3ca089196f10850298cb63502ae7878718503bb50bac7ce8586a7ce6e712e63f225a52cb6e6012da9a3aa43b4c30ad819c30d789abcb9177a902bc9a40c9c079ce4db71c2dc429f3196468a3a7f13570ac5b7e7d63cb1992d602c7822ce274d23c17de3656245b06c6e4733f265ed5f361a9431d276f45f8dfa79953be544377398733a8b3e8f2904452a6b159cc54082a3907786326d1eb864b0bc618e5ba79ea8df84e37a42ca9aaad10ee11249b1a759817247da7351d3c9c7b640560c619f23b30ff08e68b9b18ae2d94053f5e9309478f00a222c973ec9279ab53c7bdc16eecb89f53e3aa9cb38444403ef52d35c69be5a828417190d2fc315e30299b6991193736754659db25f16763ed5e8c37b0c0ceff456c5570a2a578741565b825d17486694306f2b6848069c7f3c19ffae0ae87664bb0bc9c22c23257741b33c5b8186fa8a03125861777e034ddb2e8b2424f73cc9725b118dcfb0065ab64f47061b7d7f45323b8d55aaf60e193cce5c104249b33513480b0f65bb87803a4131f839917c738295de37cfa3b1548e584f4198c055ecf23b2c837321d9c861d1ec439d0a0b9b85d8a268251248f1c329dccbc1c2f0272abc01f145e784d1ac05c1461688a5e2ca8a14ae494fbd187f847c62446fa52a20c97be9415c4beaa69a7b8d17f145fceb25db941fa401173bf618cfa47366b00249b193202eab9f1e80339d2159859333634f1c1a667265a6653b75356c8cb6f4400a6b884e60ec1dafb7aaa79f09320b3053e8d3559b0d68e7cc4d785febb8c0cf02eb979aac9e07f12566fcc2a9cc8423c1bb77f95c4af60bb1fe945222d1207a64a44c07bf8561c7122faf80dc0c12d1dcd9d99beb7b2cf80dce237424df2f13e63f1671430d224003877c21cc7b4e25e5501c36fa1d11056b5b866a958f3b9e20b43f03d5047c11547958962f6e96447e5d1308d720fa774ff67314f8cf7c973667e5c673fa08f6960d3f243d74f777ad2036253e2d492ade5322851df4407180a00189368b1b858417c7207a728befefdae6bed13226e41506232d28e1bfceeefa4949b4dadb720cc85424259ad5e858d8f267c43c23c660fdfe4566745b6addb15be8c03240ccf353dcf0fabf7b7947200e9a52dd02fec5a454a152f00b10e24aa89810ea6707ee057d41c883a5893dc933a704e1134cfdb2768f3131234582a2a0975357c0e1a5c058a6114e0ae5b06ae7c1267ec5da3da86ff8ac861ccc7791ae7cd5fba1fea287c2137456a51030fd60f62db750358c84bb708195afb69116ed09ae13f963bccda18a2dbc52403ee6fd48f4f3403619a30305de273ee783dcf033d69b5d2daa58fb56832d3f28ac9d869357489602e0e52dc5804bc5dc684abd43efd682e71759858a0020c05c8b5188538817cba910d721867af7dde666e9a1b6209a54fb5d8c3f4656a96c96ae3830ef2a2c41b8da17e75e60a8745f52bc5934eeff30685508be9ac185c7d9c4979de4b33cfb266993eb579f21eaa0018725473368a422f0272d8f090c677c8099493c16915ab8f45e7f88370afa20bc714c21b6d028e51b34b3607ebbb00005c5d4157a7b1f4908a9487937e2de75b33805eeb6817b144d4c3b9626f313cacff3f7971a439a41df490b29e3ecb84461b3b8fed352aef0880cb1b16cafd3d618d5e63fa4f09e7cab2f5399cdabadaee92e29e144bb0b62416b958cc0ac4dfa72dfcac18c97cbb75069d0f38149f81a7be1a98a713ab792b4eaacdbc7d6ecfd19d6f722386246f1e08d84b97fe53724865afd5408f2248b721480a9663fa88a55418b77df3e1cf26d4f9cc126fbc937992084745797e5a44d326d393dae0e221bb09a19c4aa23cf8a30a8d30b54e4fdf678cb82df834773af527fdeaa13df84fa2308fd1196985623f755911e1e0853ea2d9abd7d469d82a8cb63836592715f5d8520755b9832430a508fb476aca2d9da8c1bb0c30fa3981b39e7834a456e13de0f19d37c91a241125f35a34077f4b328638098294d7303092435529df441595886c160ff2f558b5e0c342b514f32736a6079c11eaec23e986f50ec9ac4d70f91a71c5587758089e17ca857412b5832f899641d6aedf3209cba44b3082a7ed574b3a5dfbf5369ba002554fdcdffc5b1f1507e686fe3d06d6271f780781a386f472400fdd5abbe9ba9747e9322c562b18333406326806f2dd31636407a19246d2f219e56435c7a72bd52d4ce3aef8002f82e000e5ba7694a17744ace5c107913036adfe8237d697354714d5519b848ef6e39b13a9886f333fc674d44ca5f130d57c2798dd83a30c5f3336d995cfeb58a4af7570a735fea9d66e846693ea2c75a102b63a0ae7b953c51cadb05fcabdf84d4520b692a74b840d10b099f327fd283559775c30cdb82fa93f2762f95ab04584ed72f7d829971a3cb955de0a06046020828b9d32d6de7130ba5068031eb9f055ecb91fee8c58eed8e9f9cb88356450c620ae8eda543c53666c267d839389c32b7caf1e9dff661cd3b842b6e8b64af819de752954e643369b669861a0d29bc5d8242a9caa64d9ffe1bea94687fb2169b6d3a82219d95652f8e78f95ac2b41a57b2c9a5512e7322b526640345b9200ee8f54ea0a91278ab849e943ebb390e30b95f1c6ab3f0c3a9eb7df16b3d6d570e1f99c7e10ec65fb2edf08a97cfe6acfdf911ce92127d0eb97d7050100cbc2a853b91281f56da2a38b8ea14f4f9fc3439ede7d2c1dfec4cc9e5320c4ef15aa351b02bdca1831248d2042bff9656cd2dcd4e619fd11a66586a88d9fc3447f437094efaba76577e1eab4e17c4a56860c9f989925b8d6e052f294a2c8e4a0d4bb535b82aaebd60e5f55279a1d314ad4b18d9b10387bc3694eba406d60f2085258c387575991f709dfba278ec8db674d2e9ea12fc2bd0c30d742977bfbee9920c24e452f4d45169ad7996779d2b2a956a3caa932b94452f3894a59da1c15c3bcf77ec6353ca6e3381eb121492cabe9156b8586d8b5582b33bc53b03c8c6821626f0185873fbc58bbdfb31072a9e4f410fe1dd8d542f735eadd3981eaf29d6ac3dd1323010c1b94e258d27c215c088933f81352bac5decdbafeb3abb243100f18c609e140d776682e93b57efc5db13c1df64417fe89c6b7704b08fb51eeaaf66830c823e02938845456ec538a1c0ebf8f91e6fb31596c57c49140c6370d7c153563b69f935d05ac705b60ffac7108d45f33ba436f4beb6f436563d80341fcfc3cac5e266cf8d564b9a1d1c897947115b09968a23d4e30b707d0f195c3060d6b71d4b367bfb7656258a767185f9f7f69d5bd825384d7132cb5b87fbfe28bffae397fe5c4ef46051e00dd07a9b72cd06f573dacc0b426b42981049a287dd7bd0b1d41988cc027699e6ab550e8fcb445759a09890773e4fc8541236f1bdeedcc7932b8944e19235aa3c65956fbcc78ec85c6b0fafc92d61fab74eb0b2d2ddce333f4289e91791a55070cb537a64b55f53cd558af5dcce24ae30b68b73e6429842836f4b35f9eec1f0df48beeb6fdf5ff79508e830683c8007c230a51dcbce0ce2c22291080850ae5a9a1349436b0ee3ecfea689bd1b3c16993fafe59a850a2c034f02a9a71f66873529b74ca318c8834986125f6795bff498e8a91e726fff3ef0aa3ef3b5fd028d23ccbf3e07a125f8537812b694e828edfd95878b0606db73339a4979422b5d64f42a01a68e1b8a16641203f01443b33fbac2a062a9f0b65270cb3e106c84f502b823cbaec2dc26ed0a8b698afb39c685ecea60e51c35794af2092ef085c1df74ccc3cfe188c4466b7a37e0f1d310e1b438e10b2c7eac248594d3a8adf9c1a68796375ce682d452d32f7091dbdad105a5581c8364013d542e0b3a0e528987c3c8d1d8fdb4693f216eb4ffd5db84e4be09f79b95f1d6bf87b529fbeb3d1327ee4560823ff7fa6c4b1202ba1e4e4794ec472e7bac40a610c451814a77b2d950146816617410804133272a09902bdc75aca7378202e100fe28eedcf19d1d13477206a85dec5cb72b95bea22189ce04608a39f02a6b45dfd3e68b8532076d7e7f6a90de6fbb242999b890612b057398dc0271a9d0be9c0ef2c88129d0fdfaecaa383f197bd731ba37ea5738ced3abe820f9e804b1b588368dbed309e1a62c0a5a3c71af07cc2039b7f183fe40351d6d82a0785e085ad31593ef885a53358b990c4cc2c650462a05a3e963a63ff2b5decd164022e07046205611a485f3704ad821078410afa13af1205963767094a23233c585473f1002b79e8c1f22d12f241c48078826c36a492b0438a81c414541668aabc1c3066eb36da4a77bf7234336dddc5402a03bf0fe3503de1faacbbedc358ce719212712d5dae67c294508a4cf7092648e738be41a9c4a48eda2e4f386c187c851255390630029e21a92caad7d54c9cf048138355a38408d1397e6eb4e35e259c95e271b815d682e7f2afe741052d1b70c0876809277e067c62211a0f3a2921b2312e4bcf4f7aa44d0b57ec3e1c8fdad372e13a94aa9366747c52554bf7427f72c586b8191145480a7cbafa2c47862783a41a0fb2ccb1f0036d0e9faf5e5631a35668beab75c36fd5d6e827f05a6a1e5c714f3c493870194d35993218a55790d5af757520d484c97bd9640bd7422bb1d42cb2bbace5c6f5fda69840d95b4d4e097e657fcded22a9d297fd92507d8c0ec7cd9371b704133acd1db8bc6c3e1ab79f15e985a12349ed035bd02ea14fb539fe51233565156a9bd6df98626cedfbc934b9257554472cb75e6b35c986a20b89d41e4039d0f2615345015fd335187337d48d43a2b32094087f2466356aa54b0099104f0a95f92591cae96be5761dacc8c28909ea7b0296ee4dd651d9557177c6fbac2b549f9904303f652d8e808b85051b3078e68c524dcb64f4b61805f697948f24076667af59af26696e0e4cb59581497f9828fe61a48e547c60f782a4fc67e84fc9ac9784d42beb0881345060d544fe5fc3cb0d2846e732b3240caffcc60ea17429cfc9277e6515170afc4b9ac4b4f9c93f81b8e556af197bd5bb16186b690bc9f1cba7d2a683ea5deab15d10ba4d09c66fbea91a03c2242a4e1a0733c2e029695a8fd10b18ae74ef255c72a1a9ee9fd161d3677f3bc4a2ce9afb6233e0b9f9aa8b98325776be92498eebf9b2e4e443bca3af48c4c57ec280a68872e52edc9e76caffbc2d55b7033161617880cebb31241eeac80211643066b0cc04e1724e48dbb60d545db168a55e778f902971ec6de93db1eb56600532ac31f28fe4133ee902d2a88c2947907d036080d4da0d35ec811ea84148a98645f7ff9716672dae14c0c0561d895b5c17f13ca97eb0120987c4c9dc08d0e10bc86194f71e7ff8b5df8b82253d08de99f58bde72b765908d4ba5cf493dc15783cc46e435e4bd14a02de3bb1425861674c903a3e884e8d703294f68ed54e7e3ea95038b2cde679e91d7b25c33007a41acaf75ea2f0dd5f75ff8393fdfde6bbe300d66a95618ee036f975ac47e8c9f4313544e0f3d2a4287b82b1473368304d21d1914f5a92e2a94a3382fb9d29008950c7f70a5106f36c910d7d2acda31404a656d92f8186f27fea35910a43e22fb2377254bef888ad473091d7b51150ac99efba4b1b6c0c7889c86c27c9cead524481344710a20e5beb252cc380479677690bd9d633141a8dcb3b9285304f43d390b62e37018900e3b512871303f60e8d9cd1fb6c054b902417934e01f3660b0cc8886ceeba4d13cd7b6bc7951553ae520068b110bc1cce3711aeb01750c8c1f31de6ac7280320135f0fb5197c40f603a46648e7325d45e3df7e21cfda9d63226d3d5cc2d810556150fcd89d32747e1913d0237eafa57ab7646a15e1d0f7e928f19cdabaf10c089226bf661f41da524553c29379b763949f8585561392d604de93a0194701a7ad4824779aab316b452c3d61245832b28f67b7116d684d5ee495a49b4676028abb3cd18a0d1a44946aa0764ab0d592f2967c8090f4c7b3ea737598e7771a59d6fb5527eede2bbccab8231304b8eece0870966648aae87450c12a457ca021e76a7c0419551e9a6da4b6cb07fa53cebfab1d381b577207849e829fc85b26aef139d3f02ff6a55276b53ff6826125340c15fddf869757b7585e72b217e43af610c81729876da5020a81615e65d4b392e02228d2d498c301d7be3350919efc6283a51ec580136abd7373844676835c2acb575b4de25ea6cbc245bb31f394a93e39cb53c4cb9312431f58c4b12cf24b3e399844f02935c1b5cf0747ef74256084628427e5dacd6a29898b1d746b20bdb7e6cbd32a00d5f02139d3b679195c22f74a63dae4774b4def34752af12978848b33aa92f47632375db472f05732adfbb2ae4520e6246081dc2c79275daee00d32d8bb1176d7ddff20700b2ce913e8df552e4c8610ccaf03bcc734c04bc3888264fddad1985a9cf389d56df72e3335e08b265773dbb273742697d6ca04f3c1e7411aebe8d56b428ef06e3324026ef17147e11dc17f68635cde19596806996c501f4b60b3f6fcdf898db9086ccbcc8e37d87040534d3ce3276956c96184a04f2026327c26ac9b083ad4fa37749affce8c434f65de20c83ef62926750628674c9c2d8d32fc49e3bb0461f0486580582636ca890fa84f202e097926584d4e496a7f3da8f5a34dfa565df0236b5a8aaa117f773d2dc3899d9382e9bf5cf07c429431fc1392ff3460ea28353cf8b53ae210911b72b0f70ae623fcfda213b4d5ec7de833dd1b9c8af60948a422b7a9c1ced2a3151c3a4ecf657f91ecf41bf5a825611c50f691d1ad49444dc6a8496de21b2568062f6dc6f7dc9662eb9d3302d316282b22483012d595be867f40f69feb582b32339b2b97590a509722cee918e409e8daa367d5851d12e23264d3f6d073d81c25bca2c2f94993046b571b3a636ca93cee0db21a27a90836831d103b22f772a06ff5b61e4e074f00c0cfb8fb15f108585960cff591eb750f91365622f60a6788b460b253117e2a5055228272f06c436e470d56ba961789dbd2ace13c127e3ab9e600728ed4acc51c0e04ac2dce67312a7cc8903b5c0eb3da16b12a7f1dc40d53f6242491a940f0990cf035cf4113c12edeaa9533426627fa8207a78af9751f09e4c9c6fd8bb039bff2382a027b890b7ca3dae0ed1755c202088e88e1ce8384a66e6a9a4b46261e0de6f02b91e7e38a373aad6ec7dc9b08cd31b0bca184963eccfdc0f5f321c9a6760b933a73cc00cd59c16ef1b35506899fe830f02f00a2894948c36f934d6a7ce046558d0fc992182c9ef8322cf96d38da0e2cb9561306a7b8c48ac0f55a54d48aca0ef32a8dfbde94b278398bc645e2379dd76ac6981b2a4dcbb994c313d167d6468a27a8aec462031529d543d7be62aac314d036c2dc252b905344a8e278ee4ee181f3097f7edc64d289b148e67b3e3593ba59eb2adb7ed728d464a9cd06de9a410c2d2fc9bf31dd4897ebc9ead42ae4a8d201cbb50d207a7429acfa30347616bca4f497715a2523bbe2f1cc1ec0631885ee1352fd4e5885a511f85674a423598de4df2edc6b9e956f1ed77426b83825d444e461ec128b58d25176648b07f82e6269aade0b346da6781fde324a19c029e543fdc5382f8557046ec60be8e9fce64b36d20f9180f1ffb07bd2878f522628e6fe996ebd7cae35a713813d0b2a6e49b4b0d2fada5f9a6645342ac876b2aca5ea064fd13d9652b007a365370ff13f0bbbe7dd010fd7b294d091ef0de48e30193faf6d0c55e54d3b9adc1310ca826e64261755e4d5034e56a1f0d1aa13fe38c5af61105430fb29a09188e6bd23ab2b66819a5c20155f7d1fa011de8d6240e76808a8888ceac6e5bd67035e9711a26a117d67c5c14762f3f94c1e0ded36adadf680b3a5b28325331c9ffde477699d0f03dfb813176b8c4e7627959d1d877e474a738993f46137ba1a297b2534e75e4f908256ceb87792aff2a1f11ffe99ec0eff0e2aa0e4262715de4b79833d3eaf5226abc5846bd6cf061bfd9583b216cf248abaf1fd6070913a3f707ebb26ab86cb0b130871b2f616e8f5141378eaee594765b8cf649f80af3fab7be17aeeb753aa64ef8ec49cd32fc20dd5d1dd96c27d3cdbb581712da6fce8f987986f0deb441f3ee751339d439cfefff96560b11842f861d5354ef28079106adf7eb8088560e4a6b22074a849a588d5c75042203972e32b2da77bac9585fb23d7db17d42a57bc60b7d08054d953e64554de39a04b9c24d7945ef22d7cad9ddb5856b03aa400feed5aabf09d1bd60d06f089516383c311f7f78ac4c6acdb68c419e039a6150bec2e09046c2e4995f5bce65dd36209c3f4e2cc8054292f9f009e5f2288da554c560d8d024430d22d8b762a16d92cc457243442adf25d219a0fc89b78a293fdefeccc77ebd918629a947aceb601de8258fd35ac83b5fe228a0f328dad21b3e75efaade1081ec3b5a89e615f2489d72261356f43faeb0c0e02484623fae1d293cf55210752c0ddc40265ec0bf90613c142577b0c4e5faac573d715287bd2c21454401ceecb45722f1c144530374ea460de1e6fe0c3c3f2b3c9af1ecae2f2cbc3f5ecc10060a080a26bbeb23e1aed0ddea806df98cfb8fca05e9257e1139cbe7210e427d6af98b5ff0494bd4edffa01ce8d848efc1e34498b8a9e38e8c0d05e87fdecd368ef87d9845d2b0b34706cbd67c2331d22548aa410e88f2a7596d4e2df9d59d165b2bf4fa973386d8a6a7d15144cccc38a436554e0399a74a854e375d2faae9c987d99cb436e7da3ba0bba64821e390098571d8c2fa65af64681e570432bcee2003ca1c0b2d0115e87648948fd30b6ec6edc1c05bceb6d4154fca13da8b4df9208be4c1f33933efb2556f88715ff7facd00044defd8cb25a65c1abbeeb4656146fab35f054ce56ec50f1ba0c512c52aa02e5688c5069e12fd97d802895d459cdfa4c06c71552f75c1fef78f1b2e5911ec819a004ce2ad3c5548b49d52e09e9dba0907351bc4ba62911eff30bd56a95358cd3fa08f3e9201e85069855657eaf5834d5d52544248e08d387d6f75c58d4c02e2c021044fdacbe06eb5750c4fa1d18631af2d6ed49d79098774d4f6ce2a6e44d3d27709e05331b8de89c57a76cc1c0336b35a70cc4ccf58047d204dc7a456bfba0041eaa8e35992ece03f2982f97fd0c5d640d1c550c4f8b8ca071dab771364b32407a967741b9ad744972005361f29fad3d36a36e7b7f476bb8b6a6db48e02beba9fd4b2415b71216ce091ec825515da0251456ca44f4a2cfbc00edd6507a2f44890ddfa91d96c234117f87014388b171ada3bbec39e8109a96cb0a1bbd6c9e2bb6223b64762e0be0961b7e1805e82993ff9dd181f5d13e5ecf52d67a763cea9859603f27b97bc54ce62abdd9d1e18a107147be07c8b53ef2488c208ba23abd504c51ee8796b24ccda856bcdc24cd2a82524bf032d74b502d14588dd3b4ff3b7e1bba334393ed6984406714eb0a67192dbee2ba5783f6b2c20ccc7f4d0f6651059c548f99e65287da537d0a7ef056d5be81031ccc2229290053dd106232a517d40013143ed9c40a191dd8dcacdbcd7a85c0ff0342ec32f8e2f65a8ad45ae53bbe4500cb9ff783cee8bef7060d1c202354082fc8ef0e8fda0b09f29144210ab08386d30d85872c603442a19fcea13d58b25af1abc43e1440c2c1671eaee9c9b8fdfcfe78e896a0d3a08917826db2d815eaf9751caee6230466f25fe00ec3f7aa2ea033698f68b830e702c170068b51c3b80270ed68831eafe352a5bc4d5f0608808bf2300d4517f620976234e846852c629bf5c1f0e836280f836d336e0e674de2d8e953ca58ec744fa4ce0f6166a6519d8881c37d3ad4babda1f1f06dc81746a0ba281d85ff7815c1d35d417fc7a2997f8120d1df0ef67dd5ad22ad6668aec5ad5a2d240254a74faf79d9f90ddec61d0f426bb1828868af387edf13a3e87e0409092e6564f80370591b80a078a2479aa6024a7223417141dfe94ac6b0ba87632773762db9676db70b390f0a2df2f63501c39195c3ac463f1aaeb0e3c05111217505fe3d4077b33904d06fdf348aabef16d160fe25bec9db8b6501ae4cd50fc9d038904b932fba76e68eac89a9e675226da5bf35e16426a86ba2398cdb5df39730c6697d5b0062cc12f256bd78e83b295be22181ec09280621f256113204f8e34c49acb713f850b5649eeb55d9580aca660db1ce28743ca196f7b26afc5b7b6b6a67ed140e4713c37b3197010166d05a53d27f5ad16b86491753d3e2f391a48359c400a1fb9a7403735323392e539aaca2e5cebb545a5a32017433245e5de1248a1d6955e09edd3114a9a459482f356280aa2bb98fae329aa57f85ab969fc55c11798291d3cf59b994dc555557b5795c9b32ca1b9442d99c19210aeec001daa39d94893d6a1e29a831f34a613c8da73600255999931d4c7390e64ad457f9b602e980400572e39cd1b88e5c9f052fd930db7b13e5016718384b370cf7edd755ea75a73c736436a19d03215fca359f663c7af0f16818115b9033cecb7072ea4560f21de6d44a0ec1abd6d9f3af81404a78d6bb666b5912105d921fc65febcc9f272db2d30002b616d477eb18e714866b6e41890aee1f3b4061dfe4b3d23905a67a98a882833acf6261ae426d0b4344795e1748248bc3e89ad48af708e13caba08cdee3d3b94dbdface923b309005973861868e7c9295c34521e0906ad854e2a1419a4e5b10278da3345fe65ee728c81a61dd348e2e3604680285d2402085b52009a98e1d4e61f75e17eef53aaf407bff9bed5b07b891a7030d7eec01a3c3289d749a6adec6f78b930b702fbd3a8bb0c081cb2e11cefa88c0c0c3e7bf036aaa59cfe626990dfd3e85ecff37a0786e25060b91933a266451ca90d03c73b174c7f00641e2999a7034344cee08fa7d44043dc7db3676a00a5dd586c4e6fddaaef302db3694e511aaf3302ab443525587ead793bf474c7d3b6e4a3192351e83275319fa6cd042f962cf82aadfded240c0aa2dd9da2a41d60c8ae75441575a7d1e638a797aa512c20ec2acd380c3e77d321660ef8eacfa32a4514a28fb601e8f87f357eb7d82cd167c45c4969a3af2a0b6055d046d866c5d9ad8920eb90e64d7a2d4734713c0915f12444d9782e768aadebabe43373fc139fbd605c4dcc64ed934c2e1130b2ee25bba24fcdb1d1f5559a55543bec4c60fa68ca079ddc53e9dd7e4f447052dcefc61ac20f8c0da7de6dffd62328740522d9e5e7e8cfb119f86009c0a0c72dec3a757ca64c64495d9d16a1550a3d74bfac7aab8221bfb6163d45b0a53009fba81f259d09fcff4a9ee3f30b4303afd87a23789bf1a63ecaae08578d09190ba5f68de5efe8c920a922c40373c139ad601506b2f4bc60e2129b78826b17ac5d954de1ac14f899a603bcfa2ca18742b3d84a66e23da145b4a3c42ff5c773a62226c8d6a8caeb50a208908222b2f601ef3c37599a48fd505bab6b90436c5cb2189641dc542603de80c1bee938e68a0c28b48e6857c5daa05ce25c4c84b589b3137b280c1df8adb5f4417a497947f05217dc888f684f6873793d12caf19dbed38cc967e408b900d239694a982ec62b7abccaefeed39feeab13aff90550e70e6e5b97c2f664e8b09d49df95b3729ac67ae5086f03261c99ce9893e7b7c1972822bb50a06395a4319405ec18a632b53ba958dc67ede821bb0ab7d7b36afc78a9722eb3cf92ce6619701f55333e845b22969d4a896b8fc739ff61ab78b66242f3041400e8045eabfbc66ef5c4082b2c0990b31073c1332480aad092714d30d082c5ca6a74b9128c413c873726cd010ca3ad4dccd442fb8551afc2fae2ddb82a5622677245814dd6c37342af69c895da809f94c4db3ebe4232ee2198ff218b17b8fb9cfb36747aafddcc32a02d91ce5baac5fec92107d7f2a891baa738867a0987a92f8c2407c8f54d74a05e5310a2da05643be465f374e90f8324638d86e7aad04f8e78e58532eb0b1baa849183849482f33c2660a9de2f5e50b245b417ba2ea706a8ce83c55543fecfaa8816301d98f187670088c9d9566cacc2941a490b470867085ee2161786d1177d07f673e2c0a8651d868a4ba2b976e0f2f8dd06f2281c2291b6a92844ac498f929687965d36d896adb294ce9c643d0435c05979d56d159ada4a441f6101b69b8bdbc114102f63bac7ab7ae99d62f3237d4fc4fefa46e8e44b0f64c847cde1ccd209f135b8c391c4a441bd2c2813888f62b903e46e6d6ea1f835818cbef94196323d420f5a89ba871b54982115415735db870d14297cf51d844e6c059e8fe85d6edeb533217611da6b19487b3aa91d41a006447654a4eac0a0f286982eaa3f11d1ef4c4309ab532bdd7202a4fe76543a2c481d450055fceb279621e445a4324b4a30cb73bdc817d6e160dcd84ce914226b992d106e291267e4824eee77c0a9d465bcc8caddcacc50b1b2642c8448e5a8a0f826ee8300e2e0890ba7c84500c22a0380a81c759852983bc24683ab87be4814ef38e2ed55a3e00400c3b505dd96238d178af20e18b40a622bf6974d7a3a95c01157dff989f8d2e2c9e75312064cbb8963ef7f34ecc400730d482c3e4a9aea1ba95ac8dfd0a264c43694b03542e04e9034f85736c5a14a6741115453c244c33d87d9829f102fd9f2db3546e166b3fdb1ac0d4e7f49e0e7cdc3e97e9b0a9d2b968ebc04edb20bfba1da7b56e33d8a51345864119e91be84653069dd51201cf312abf895271e2a37311dfb3a9d16df93f669992c6dc4b208a62254e84a5b65377632bf89ff7083db0ff0c332e9064eaa61b39512645774d0e229333b48aa845b4f4eb8f93b8e69b360db8639100db2b50ea1d4b0c76b8b33c4b620cb528162665dbb716581c7759d6c9d2cd8d631b594a474a9e21bc65c048c9f6716cd4b9aaae4472329f49a7b9bf8c5b7dabb606cf6e47930f0df472f604a2a62e03118d07ddc31746b9aed06d79387de62e286343f00bb5fd56bb02373d25c62eb8b9cd082689b50bcd40aae1b9782406b789695b073c271d4ee38b94de5c7d178b8ffeff91d46eea483089603f37804bf83962e656543c2324689a71d63423f88fc2dca29e319045b90c6050f19b74da2b9845723bbf2924208d8b65874a13aab6805db757a398497e9a31ce5f011d3076e49c6a271ae7dd147418b061602753ca51e6a2f7f4e366c2ff4b992719d174bf3c21dc1b286800a6724b51b1097d1912d3ca829da55ea9850e2fc19aab11bd571fb743fed9285790b98402741110da9e3899321f0647c243d5cc2c7605286a4475108446b0d46c2aa985409b083117aaec0fad22d080401735358cd4554decc2d09f7640b8ca7a719db6b129f72d298b6381d24f5ec83e4e9e861300fb2d6c00f34fd4d78b619383106c3aa5093274bebdfc230b8255bf44c49c6952e1414594f66af4361cf24e1734a3e36553297e296496af7d8db57c5af27243b959d51f20c7eb4654a0ea2593852adb900b5c90c17d03aa8bd8e6b9954ed47653882157d694853eea42c8cf59535e1a81f7c0567c45441d5a9b4fdd7bcc2ffee32106a88bf1921d66970313ee34652c7b2db0c41fdeae14d3c0b93832d497bb572bc004cbccbbbe704d10f2c7018fbc2aa4b7ff151e6eebd78d87c010c88c11c2e799e9273ecebcd3b2b4d7eb3be350bc15a8ee8d648cc49115a99c775ad63349caca8e261316b716a94b99dcc9b00a72dcb9cd8448e470cf5d90e9c5a6883390eaa4bdd84baa353b949145c98e6ec8b979ae700ebb7c70b0a2f9a86cd3d94bc6188fe4719afcd7394afae850b8b8a57c1ed6ae883a520084b317667408b69296bf68a92c51e0b00910f11c1a066361ab7cc40d01d83cdcfbad8efa6400c7b2b7df9cb84b6049808a74d860d18fa8f347274977067aac57790114c6ab32bc74e8039b06ef158cc5ab529e8b57b9147575f9885846d0b365ebd0e54b3d60171f0bfe7ffc2445f5c610b8d00cbce00e62b43e14dc55d2a32d63f795746c0d9c84a09d19b0720b6522e490447cb42b4619c3fc12d125e0484836180364b16ef6dea3403c74d9f92d93028102134dbfecc0f17c46a4d6317c5cfa69ff395aa84bef9d4ca8e4a81fe5b9e387e8663083d098f69e666294620842b804e0b10d7e5f0fdb2c30e7b19c5d7ce9b0bd54f5f5c4ef25fd8e83d22dd6201cf5008a0957ef1610e8868eff4b34382ffd76a5ad167718a0970451fba13a108728a21e040e8ddb9159e2a4d66990f6de00e9f4a49da729bc994f32fd21ad4379c602b13edc5ece29cd88c161fb53e7b5bf221fbfd1544b408cdfb9b69738c0f444c38af947f04eef7224ae440e85ce81132ed20fd6a08b7e7c7a22d5d731145bc1af3cba061f15d7ae04e76aa435bbf6d5a20cd051450cb00d054926f86e832992c800f82a449879454bf11d111e40907e146d6827956a0d0b635c054e844b43b8b8155af0a47b7c1b92f9c177f588afa6a15e7f2c318bea4268bbc11af70604ec9be9f6165d827ad68d513cfdeb919beebfa6f2b92d8692d4abd2c7e1c109e6c2513504cbbb6cadb2dd4f45504b77f8fa26581d6a7f5d18250f8289d705dc1d8bc2d46c8c8b56a3b6ec5ba60bfe37b8cfdab5e744706f4fadc4814ebcca050dce2a784d8fcc6c994d4854f977394212aaa46f915d9825b331f921ec956e6e5a679d0b0e4a4a8e3b80ba385d93016fcb835f415f4ae21fa8685c8df21ccd8c4ad0d20dc4cebad442240f415d6839c597d10f8117d059e0533017288bd86258f90a03710924b1558970333af678dce4bf782cddc68e6c849612a8cf84d7ee199579676266a383fa729b69c1bb3fcc99e42506f55293193accfea1ca602199655b13b20cea5849144a48da0d2bb7bee180f0043134c7496603a65a4863ee49c0d77b724096229b309eb6101274202e2accda05f0c0c3b034b533330c6ce3b3149bec663e2ff0ae78e2264739ed3f79bd4c06cfc0f54cd25f655cc5b857b92be8cc846f037fc398a656843de93789529a672d354ef5053ca722c71a628119cdbc89539adbc011256f4ef4013751bf94622331b18755b188e32079e0059158dbf2fca29fa918ef5096a93e27ea1f9e7b9586fb0db0e4ce103fa71927c5a30523a4a1b557ba611d5181049f87b5d3578efeb15ed62555c45c95e739dc48d5d89b1a19346eee9649720ce20be71ddf7cecec471e3790bac553678cbb848f0a2321d57063ac65bad11fd0fea13d44d12222989482291bd690760084e07b0073203ca3f72503621eac4f840c0f5af379d7e867b4ecfa8437fe6d613874d1a6b4a8fcc7d55fa56383125ac63d7755dd5d0dbaf32f63663d6630880bbbaf247949a2e65159675adb56fb19bdc7f89497bcdbe0848763d92f65fe7eeb57addae6bd332eb177fd8d192f0b2d30b76e207f8e362e211f4b1711fe311d1043bc62b95abdcd33c8d5ee2499df89c0d2c184a1d4b7b9e3427b566fea491a86661463b9256423ac16234e0e803a683e455310fd8ea0f9fa828ca96ef590587e2cfeaa3ac2af0a5a2c71b04499ae9ef859d197961cbaf681bb1b634e92f866dfa6d03024aa53e6f67db7fbfb36ddb7e1b4a42b76cda4c992933fdd7ed4c675baddb5002bcaa3c0b226902acb7d62f86bdb5d6643299be6867d96fdbdffc75fb9a724f19f8b587d956b4b79c846e0dfb2cdffc25d998cdd56399fe35bf7e90846e5d61e79c55aa6acecf3dbab027dc39d20eb955b9cf97347b8e30aebbb95ba77c2eb54a1527f6c42e480487e4c79f28a0860c410d77ce96b342183d9797c72eacdcf4da2aab8fd10cb3f7fbea150e81e14f5da137037d78402f7e0761849ed72a6bf4aa2a75304429c03ef62d60df02b744ca92eb97d4c72e0da50e108c5eaa8a1504160a61bb7d485eeae89fba62c04203e0eda6674c8c7388e34044fa56ab351d0825c4e8b3aa755befd65b5e1c41ce96357abfb3054bfd59f875cb4737d473c884518694f51656cbb2f4071f47f4e1d5e73edcc1e8b6657dc51b96bb68647d9dd6b2acafea4c22ddc15495e538ea43fdc1b774a53f77aaa30a28a594d2ea734faf023a84d0e974a702858218ba6c8f12dfa330c07a9475fa8c925616ad547644e05aa5cec49565d518e3ccb9d785510cc32686490cc3302c6298631886c11a42955583449905b99910d72a8d6adad4344d46d7340de20bc3988d0eaf0b5bb7d6420cb3f6664259a669dbb64d6bc7971046cf9ac3abf9b6c19979c41056964bd5d0b48728c8cf77fc32b05d6f189b4c27df3dddcc8c27b42324ee1cc8d309e108899b833cf06b788c31c618b5bbfc19a7749f4b61be54b992f4c481982fb0d003949a77ce2b39135ed9baeb6c6c6c6c6ca2436d03521fbb00906a3acddc1e39a7ac8935355e032fed83e1749a99e1381a9a1a35f0c6aa689de130e73457484d07d40175b1734a4353a3464d4dd779363637372854aa67e762685574deb821a35f35375046ef6cf00dbe12dab0216dd88836bcda6442379910148ee3407983729c6843dec09104ae40638ef9835be24ef5ecd41c570e1682bd317f292171dbab23f3349d6d07e6c163c29b96f15c1e8b5d3c3cd5e27158a37d45fb5e871107077338703e5b08b5ce9183f32204f403c210421041870ecfe33a211d6ec70e1e3bdbcece8eb6b3b3b3b363632313ba2184c3a3f615ddb89109898f234772dfc91ca1fc5c177de7e0703964f41c392aed33a7391c73721cd29cf5a633a11c5cce0de17a16e48a0075441dae83e66428cc9d5474284f803d4f4767c70e1e3b3f42266421beaa954a553435654c792a958226ed2bd2e1e9e87411da910989bb0708f2b83c78ec509e29a3f3c0aa7d4b3c2f13e256277ed43a3b78ecf08c20a343c8b343661d3dc6e831422b7ff59bd0dda7363f222866f3d8b163270bc22313a23c3221d72828f8bb0e631129401581e107459e11a4ec4164fbc748d4790a0001c884049009c11eefa9da5734c2087284e8b0070a85d4beb1a3fe260081262c8c6e44c6996c77aa69a414d248e98c926a0de91473ce49941373a0110906d260a7f657043f3772a31d03f6f73d938804c03be0f8dfc78813638c313a86151cb2ed1923b7a5ac01f5e855abf5b562dc75305715dd441ec444d8092184dc7dea5cdcaa28a5d4a97b2c62d239e5c3af43f2ab6ac99773da9b1e2f289d93cea9ef3c83fec420c5bf0ef94f2d44df7277cb5d4f49abdc3306ab307f5214a8be728931a4958ba4f690fc1bebc72b095b0f777c5a7754e2db7591d4aef4d07c9737d88ffe0d135809dd951ea25aeaa1f83ec3d2972caa940942861bb24c1720e1e1080e2e3e40022f5d0081cb126362161d30e388279acc3082262418244a62e0a5035998a84103369c71045a31a241135f9e30592209337e904598264884e942c90c2906121b4c0cd185164590994195320a10850c252652c2fcd0c5bc922305195e6c20081f34d1032ae6480f61c0600b285ad0031371cb912d3e948004484835f8820c19908104298d1ea028230c2c494954073598628a256a80d2039931908821a3054300010aa32f35d030530922c8a032c4132c64446024c34042811684e940131fc22c9d012684230f0021cb18198ac082e6e0c8ac02c9092ef0618b3159a0e10130a81847ca78312687107421811552fcc0831eca50d9e28b333e3066098914400105151a7ca1e18b1c03893efa000f9618d3449829611cb1028e61860e70b86203165ba8902408c3064e0091a481249070010e8a8408c1126472c032441857c0e8c10f860801164258018510f308119668a207625809410c6e9853e091a9a50b078a30438a1f68918418f40bdc12850cb4b84203661c81c313934a1a5794a0063878f24412497c8104822993c609c68ce0cb1667502fb48031c313204ce0050751ba4092c2165d6cd081862852ac50a5235420819409242d30b1850a25c48069a265a6801a01c41622e822461844d01b1c992f80687c6982e54910aabc400772043f88c00654b4c00a0e5ba2e002890c63c268228827be58c2872d907c51c20834c290c18a3368a05e60900ca2f040822bccc0e1055a403252762042074fdca0c50cb309241698e2041fd042891b82b0c2060f38e38805629630a3075e680d669214e3e3012caa4841175e84d1046a11812aaca46183204ee00507ba1c296201106980b105193a5cc981d200491454ac30da010f1ae08013740631d00004305ca0000733a2805e661648341bcc20450b30a688810697b802314a3ce921cc0d42d02d47240f31474c3027619eecf81188f8e323c6075a01e365c7773137a72c29fa7895e84379b0e3fb951c36ddc1beb82cd378043525c09f2bf952f46ef08071fc3ff635f237778d370229c6ecbbebe6ed7e6642366f6176f3a7fca5f6e96f7296bf157676cadd679928b5b38f9910f69f27d5f8a4d3d1c912f87327950cb8c6bf67416abec607c90aa3973d8ef74eff383e6234faab1be37e467f7e0c247f6ee898e1682875362db5335dbddf6c31a569ab54e9d93769ff883ab08a8d118b91d43f5ec7bf7127db7abfb9827dc79bea3f586503f9d20fff127d28fd1ca3edc41dc7cc97c2c09f4cf912944792ab9faa554b4a86061a68f0bfe63412e44646bea05de92fc85a7de7db9e31188707f8835a64f6e3ba6ac5b8ebfe33b8af8d999023f1439628367d0c6a195221f14396a44d6b75749d4201eae931e14dcb2e10166310474675fb58f153138cdb6b6fb529b0efeddefb4375fb6aefb5d785b3bd32cc6a1b5c98fe946127d3fd7ab7ec3bc3aa1635acfd479fa86947f7de7befbc990d2e2c135a68616484e52f67635fa3cf497f7557bc4df0f49346e3199a65cf68d9333371e6bf222af42b38a33fece929fb36e5df387f38f6fd4dcb593ee2a9093108adb5461b5fa6ea4e297dabb2acaa48ce105007faa81fdfc6c6042f6c19718e990026ed6cdbf3e3c76967d3b2aba4b28ab01859b08108b344f73697aac9356832cc33d955057d4929a592beeb9ee8590f923a99f0965d8b51ea4facfcf564dde34fa24f7d8933b5397fecd9137f8252330aa5d78362a27814291d8a01dad388cbf9fe010155203cf9032552f4a9a46c229bc826724acf8532f341d18b3ef2639229e22cd0d81f8dae2a897434f6855536d4017fd5dbc7feabd087c7587f75d39aceb47fb00aa55a89dcf46da5844aecf38954cece0edeb4eca6a20d49db6e94e6bb1b98053f2adf649bef724d9e791aeeb5c76ff115b072fac32793ddeae55996657fbb1aefbad310d6e81aba66dbe38f3f61968d31bc7dbf313621faf8e3a4fdf2a7fef7fb7b47bad8b24a4846f5dd49f4e2e32ff873244792187c17945c3fd951cc4cbc5d2cccb9aeeba29452eb9a3e57a67f5dd7755d9665ed58182db263557fe5b96bbe95b66103feeac4138a65f1a665106e1d1d1cdda6bf0eebecf8f6b2177677f6ad738b9d1ccfc39b96edd8186d88db9b18d9d37f75c67f382fc1f4f4b993def8b7b75b166d599f3fddb608ecb2bfa2287b28fb216b6b96bdbdaeebd22eec56edf433f1badfeb2f7bfb417263d7ea0f76b96e3fbcdf4bf06d6b6233b7aca75996595d62972e996bf9da7a5d6da30b2875826203287655a16f3f888691b5828454a7acb08e38dbb9ed9d9bde5d7f443b1bff7d7b73c5827d63ec462f76545bfbd6d3fc153931e9aa318d556ce6b4693965d921eb33df9b56e464574af5e6dc78fde7f4f70259a0ce71883afe34436aed3b86d94c04bb14814a50c9a1d276a0ee081503fcb90f108d1d7d9cd817e4cf49fe489de851fa0d903f3fe857ab8ad3afc3315be2aad6f710faf5c3080c922a92dadf0ad8dbafd25fead2f067ef5bce74fe746cacba2e4d94b3aba34d8ba436d54377a691fd4d5dde4e4eb5d65afb35887dec83e2951fab3999f6d95c4e4311100c981e860c98be060762d24b1c88e9fd6bc41bccde94ddca4bdb2d7fddf79f8e1dbf7e50bc377b98ddec7abfd7fb755defba48ce1eb2bad3385a89dc4355d71b3d9ca26558014365432b631ced6e432b6178b095c43d4497ecfa18e68e25895b49c5f2f5156696c6dfc470ccc61ec3bece8de9fa7e6928a7d4fa44a20fce1ce74ddbb7b6be045e637c35a6ab43d7f9f0671ff5634d4718c40509680fa5cecf20cb940122e5f31fe265caec21a222ae89fca1c0102475744aeac82aedf7dc00266d7a85942c530648a6c542f7a3eba856bde97b0f8aa345c5b3735b6b71b6cdc9d976d3b2ebcd222cfa0e8a78d82da59f43297efa3350fe70dc9f3ecb50d9cf3c0472d24b5099462960db6666def4309cf457f73dfdf60d98d1a687d9111666fea497c02a75502b68da3b10fca8161c087e07f2612d053564c8fed5307a1d04a2d5e8ddaff9734ef4b0d4a9aaf937ce237eecf40001459d1f55918df10808298c5fa94fe833dfbfca775da55f5c8ae042c49e1f87442e457079b267915b5d8e31613552814dbfd60af557814df557813d047fd2eb3fa81dd312de2118e052041727732b997beaa8efe5520a5ba38f125a2bfdfdf6b1d0a36f037d6af4a27dfa1df4713d7d6bb3fc18f6f13be8436eecafdc411e0c67571fa1577dfc0cea602c75aabf740779aaf9357fdcd657578da315ee64dbcddbf53fe2d88ef9176dd75f7d0a03ecfb8ab8225225e697ce96f988efcf8fe60f196387434d9dcda638e83bfc0972273b48fed4a50fe370ff00d4891f97e24ffd751b092edd80392a9f1f595d58e8357f3dbbdebb2fb72fcebe39fbeeecfbd04b80dee36a9b4d99c6efd94353c61868918d3108ea55911d2058d29e32fbaa7ddc5cfb8b3dcdfe16524a7736f6f52bac6aec87b2afb6b705c06f59c11e33219f655b5dcf8454ffed6c0f8abe594b29a5d4dddd35f6d907492dfba699fe7636fdc135fee0bfd4c24cc80ef601d285ca9a73caf8517a9c310a71c2aededddd7166c09f57e9cf4a0404747a82458716649f69ee37cbde5ffe9c2a9abfd4dea6eb6ffbfb35b9060dd6327ab5ab65d909706b7a9e4cb46e5a6541e4cf24d5964f935465ca94a95ba45160d93ef6f7d29b7d633e5950fd97fa56d8b362d8c2ae6a55f267554d19b140daf32bfbf153a832feb9422985a8953f98a4e5e200891fb69450e290b976717dd79f7cfa36cbd7d72cf74df6dadb2b13d22eedf2781dd92ef616dbaedd58f6f5c8d5b2b77f3d561fbb6c9642d1ce78e2ae1f5f72ee9c007ec88203cd2ba41894b6f555cf18eceed42184103e9c4257fe9ec89db8932ddd0996f9d39a724a6e4ba93f4b3ef6d8755ddcf5f2edcc84a4f4cafa2a3e5137777443767029824b94524cd70594caf17672e4ad79a6653ae5ae6fe5c5393b2998049403276e3bef964d6ffaed31beb2148ab6696b307ad7d34a7bc8331fee9f3fafbd30fa571577974f63956d0352749154b1d839abdcaeb1733bcb72db78b1b86fb2bfd7da6bb32cfbbe70df6cafefb7d033a1cb923f6fb8bb7c1a7386e05029e38b0a0afd59299d4bd38bd8bcdc9f385d010da3870202fee6290575fc973adc45cb6e129654df924f89e694ef50cca5c3132cf214e914f1e33b3440922d9fce4dbf4ef74ca88a2f33d1dd3152ea35fac49f918a20ec0ef4240584b7c3e3e04c20993157aa16dc450a47f33635ffe10db91f1f38ffbd0f8eff727c6efce7f9d8f0f15f6a93f01fd01f798f12508ff2d13dd41f4dbe79541ec0771967d764283e5fc3fee4018c1166cb975c0daacb10cbb6e1aedb6578c5e608cc4a972115b562dc75ff51ee141809fcd5db6558a52607e049c841de4716f2353200fe270b3d4e4efd08f9c6f7641c2f808cfa2e432936978f709fa4ee1e6f6fe4111e47ee795416001553173813a2b12e244319244779030000a1548690077a3d2cea863702576f97e10b6480058d11f8abf8d264eebb0ca3e09ee66d6cb8e7381bee761926e54cc1dfecb20178e872016cba7499009c179adbe51fdb5e81bfd9e5126ac5b8ebbedf9e974a755efc6420df651fbb264339c0d3fc7c8d03bc09f9e7ed4f36e10f90a3bc0700d2d5f0b0cb24d828e3f4f183628c31c6184f32292949fb1a244934c4f8f8a2143d2f38d9bb401e2d5227cb95ec4e64a70279b4d871e6e3cc7f914b0277f9a72607d4edb20f764597078071d1e59eec5f5023f3fc4fde8f93771e95773c8eacf35d16c0b6c947509fa46e1e6f3d5e2e43086b32843b43b8c3c3eab143271f8190c7480c465be7adbc34b9a6e66dd4bc083535b3cb01202133f1307100f149f857c2dfec3200382df876394526ea24e16feadbe51e9c14396e974718d3651e202575e37cd0c3e8e1b88142039b508f7a9ae7dea9a03ebebd910971a8f7501c0ad3649bdce51d9312feaa08b7cb3c1e6254f6c9f931972eefb071464dd6f135b2f73f39e8bbacb3ad4d969b8b3128bba703d3641b32b5cbdeded2c58bd29718268e893a34206335c1dfdc71bbac236b419745e87208d6043cb7cb395b086a72085f23e7fc4f2ec0e3e41c7f23e74765111e47d60f9409f05dceb1b90cc5000678287f88483fc238e9e8e82893f025641f06c84c7ebe8607f2a3a5c02772f44bd857a91c513942bdcd8b40f3dc5b2f13b2d129146d08211a9506f60821e7e402641873e89c6d641f99840ca30e01725cea94701712f28ff7914b781bf9df06d5657d9552b7cbb9ea80bf0a80dbe5d7b2a58b8f4c4206ca2ec665b07bc40f923f36cca872254bd4898f83237b97e879c9aed4651c1b33a2cb37503de8b95db6b19fcb507c7c7e7072cf002e0e6a7b54ced91598f3b95d46d964a75293bd4af42a19e06f76f96673198acfd7644fca5e253b8d8fa692e2fec6e6f43701f93354e213aff245208e4c2327b3e35f2f351379141fcb1f66d4a926f2285be2e003d296a7a29ea8821166694ce1a5ac17766528d80f134e889ec49e524a31aa6d76c01fc678d04257b042ca0b51ca082c44ba1c5ea864a4e3f0822c2ebc07c2d09903936de32f5982e1143938b1bfbba39414fa70337607b168f8ef5f1915c4fef5715f9f659402f0df4761ba05540bdbbfb5305b80c6850cae040474420a2c044d89492bafcca4262fa015b000da3ffe05f9037f85e853450cfa1416b629d1c7faf8275c81572ad00f79cd8b5ed575bd705d4a1ba752de77152e2d0002712a4878bbf4bd9db0c5f8180f49f71ee194c14058941fa720825dd522123dba2fe56cc72943e528c658f557d5917429ef9d4424104fa6e094c147181416bacf0b6b8555b6a29765458b69d9a11069d179a5624aa6a495722c4e2eb6edb2b0edb3b7d9b6592793c5f055ad4bd334ad5a18ce9f3fc9b4ecca6a666536bb19ceb62ccbb2ccd2d6c3fa4b6dfe24ea443762c7d732a49457bbced2d5aa1615d2487fbe67fec6dbf64b6dfb0169f64d883f2e7cd5277bfb37b20d1c8ee66d7297617d5b6766ac9a1a341cde5c309994f0df87d643ed743a65990e7aabeb8e968e8e96e2d1921b19c9997884d5ac565c6b05dab5d628d68575524a5f51ae2cde188f8ee2516675448a4947b5bad157603086611886bab1e930a3a5a4384d3e20a57af294d5ac2e549fbfddc88d6a8862845305555512f6769c28874a49a717c9d94352d7eb2e830d4af287685403e6a287f990b2f43fbc292a7fc6915ffefcd31e3b7a07218ebf6f3f3b8e6cca1e7573e2a287bab9c16167ef4a5fc6ec48b323102612c986a92a33cd859d6938af945856e997bc6a8555b6cd43f2e7e7568c16b6ac9a1f7af531a62f3d87c86da94091c4c0202c9fbe3771a320729bc0ba419c49b5a6c41f0752ab13d1a3bec5b7446f8a11d047dc4044ef6807ccd5ffb0172f4b4a5e941cc90b4e5735aa732324952b55934ac2a95143662643af8c3f85d4a0017620f3514ea267e56ac99c713a207a2c78730758e6d0e03ed4d0e2f5442fce1d60a951190b90e7caf6db4490092bd4f0823fa8e4829c3ef4ff5fce6a42f4cac0df4bcf8464cc9965e01863ec2612d6d34bb8fe8b615bab3fab8a12bc8f5c9f046fe83dbef70b277afed8855595aed14b5082f711ff24d84bf0aff9bd878c1173c1f2adafe40c5275c35bd66412d630849694f2f3f0756537d72a3a05014e10f0e747f15f70ab7a3742637f750355f94a4a5dba402db54a596b4de29bbe53aa5128e8214b13d144d1f34ff544cfdf03d890f9c5b0e5c3f0dd0f03177861cbff70f67cf9f2cadff5f5ad4f6eebb3b6f51f8e6519b176d543d6e70f495165af28f0bd37a386dcecefc38dfd25effd7bfd7529662de6ee1935c4fffa4b0f5dae3fd410b92f6d7da67fd56b7d51b9ed3f5fc2e8535541719b3b16e176d17477f23bc659ab1860df9dd481690499b3aa26c8137fa681e15bb9304cd74b88dcf5bfd63a2bc9559dc6950a70bc4c948e3ccf842b4c9ec0401bfb947b54b06f1b23d0864040de8ed6dfff07bb482995e613f80baf9eb63f99b2c72eec8d0a78e5aa441e992d5fd62c5a556cd2c2209b69ff75d8572630aa7ed1ae4bb0ab2a1c24ed6f2b2f1d117affabfaea11df56ad2c9d84eeaad2a82173573f77a551d517ed4a9aa8c636f08728eac80f6242068e91df754bd004a5c90ae2334e8d023ff6d2eaa21731fb1447c88d5e9cd782f46de5740aa13f67177db069adfe7ca3866c77ecad22387b08c782546afa714e3b5fc87ca1ca55747ab155f96bfcd7c33b495984bac936b9abc10eb05d7f5516d837edb1e7d7d05f0f0e68467fdda95613ceaec5a84bd9337f3ec6b5fde7799ef6f171601830b6d01e422bb6a6e1145ad52c4dd31e4aed09007f807e401ffe9aa601c91f1f03ea646000fde748408e84bbee3d0f2905b4c3401e1b4db0fc48136d0c7270c54a1949fb53fa7120622cd5d002fff5d6d78f5fef95b1871b7ec4aca1f9f3ba3c436168be1cf26c5af353520790528efef75fb73b1b9a69b4faad0aecbbb24c58cbaec5ae8a54c99f52dbb6ec2d8b94dbb66d407b4b6d58fef89498923f15f0b2457eb0e2aed3df7b29280668cb16ec5db4681460ff20e9ef4a385e680cf9c79f6f59b7aa1c8582fc0f921615e3c4b6d9d08a182d56651109817b88481691f38a9842d42e54a99e05384c7221b53dd79e9f702935addf58ef4f5d000442c48958fda0777d1b46ee6e79162fc1dd5d471b46d4a9ae286d1245208417e12cf01e368cac1feac3a969fa23c2b099da1ff6433a1b811afb548a2815c4faaaf5148498fe52d0339dec9fb2b7699e55d70ad999a960952953a68b3a7115020ba10f9a65fb4b27b6bf10b7f24892fa4aac4f808c53d704500d210fa5b9fa2b53c15f3e66c4dad588b52f25c3cb7677f9f43d53c13315dc359e720bf74be812eb2d4705630f3314b09dc1c03d0462ca309c9e72798994164e6ffad44e459f4ca36af4a2ebfa033b03957ddf042e34157d3ea20b81cc3c0ca6e79e9e7ee6e90ba9fe7a9aaf8710d2ba29b5374c600ceb88ecac8361060629dbc3c0bd897bd3b3c069d36f9a85199d653bef299bae7d135054a72e30f884a01c3a60cc5941c41bebadeabcbc04140ab24c19e9310cd6706e8771155777568fd07b58d1670583af84e0b437e2eed6ebbaae58aff40266072f607630da514341b1b0952923b7f5371b71abadb9bd4e6d0c2c25be5bbf7080ba14e7e1bf1dbeac2afb002a239611e99e87c059b763d1726256915f6613e93edfca34c3cc1a328dc8c801998618dba4628819620b104cb144c548ee6049054488913421bc4288a5a32962f4306f589268c0a008b7d86cbde3b0fde368e5555565966559395525bbeaa165d5b7189df5a77f16a4c26e5996cc558e87392bc3ac5b68699f910f87cb84dc5dad2bf3b72324a4da15f2f8756596f541715a9635bfaa2a2d334f4fc86642e49e9047e6af0add29f3778570853c75b31bb66ddb766ddbb6d56db3b66aa373935bdc7ceba12589515828a79c724a09a3124cacc0ee417624ac2884b0a3049a907ad47a208f9d99901f417c4ba9a1f65279d4244621843da7860a42bfb9e1a441023838314e4a679269c54208218410429f16662d66ede4ec09fd5a95fb5b1e71a2ac2aab56abaa2a286755559555ab5511e14c5ab9af555b4eab5955ab56abf20a7ea5616559b55e17d600cfbb989d93e60f0bd19cd13c2177ed866ddbd44429eba729ee13ce39a7432ef037b53cb21ea594525ed100cf23d275d67e73cf39bdf2ceb2e6acaa5a41f841b128494cbab88b5d183666f81ca574469c1c58777777d71046f85083b156021b67430821c4d9fe9002a6d40fd8e3eda43e1d5c08db2bae77c7d66cb14ce796dbdbe9e142a8a263621633995278f372204ffd4c77367794dbca9b6977c7dfdeacbdf6f7b34c639a96f9be7342c8536145afc82b433420a89431b32102a4e2a8f44e164435656648002000000043140000200c08868442c168381c1266557714800b7e8a4280661fc99328885118a48c31c6184200000000006064a6888300b99eb317ce9b79d38f19cad04833b1c8ff42131b0f32e2999964f16c2d046d4001a38a3cc705b6129d10be10723156099b1938d7682629f39bb180e7a4e2d2c460f8679ab9eb21c7e75aa9be2a3f29444e4abc12c4357b7388ca019d6f0536016a4a6b6a2df0c952d5a04727c83bca2fd4a46791d9583fc0bef459748f5aad60cbd08f630ecd1bf3e78deb71fdb9b88a488d7e56a2c31190e808912d857b89b2e5e7d88d13b607671d0055a047d0112ab65471546478f726fce304b44115771db376c2d161300de24a17c947635f8a08372eea69f7d63874235ba36f11928c8102e2d83a0b319f9605352a11ff330506663565ea2b0683e34b8480c03cc22410f2d752dffdc6a6e75f2e6f0acaa709710f641e7ac302d9b58d64e7809009f169525f499a0da11d9b2ebb1ed9804287635a3f1c7fd481be2d9f546e5f1eeed2beb031d6df1e17c534c4397884520f796cefa41de72489ac8634ded1b5de383636fb96fdb0e943039b398741ec865e9bab63873014c78a6c603fdca883ac74969aa6a9d9aa8ecc840eb18fc64f4cf1ca82469952bc2518cb80b12c5570c69b39e85843c18d4e585596eed365c90fe498579799d9993620c0be9121b5367b38a0d0c3cb5acd341f42a2ac1ce50681c68b60529cea113c53de7244aa31abf5f90f9768e2fee773a0046135783a1854059838adfb0205d481ef69461b784496b4b469c571a4a847451729075da4a38519b77ef07792f65f062481d79fcd0e6703ef3f37c09551b96d0539db6e832cb1a875d2e681078783515811b70ad77d8d1c89cf7c22937c662279d179915282dfd73d2a3b7cf2202943bbb2cb2d4b13f2c5c28ea5780ec033a514246f39863c5c889e1318a98d2dafadfde32a76da48dc84f0bcc13cb4493feb4847a22f2efba30b6cc1a396d9d191460f314011d98a13f76eeff5602776c6f393fe2e448e64b5a75daf31342dc435ad5cf831aedcc2d644f924464787187b896bf375023fc83fde3f1ac645db2f41ffbf09793fb3cfa13b42e9ab0613846b824c7fe87e11b8dc976e4e808d1f310cb5cc9e4bd099dc2dd6c06940dc3c1135fe64fd42cc3d414af1711f4c34796904ed35fbdcfa61bfc93bfea10bfce4db3894c6441230c26656b60c249f3234bae73084f2544aa2db933c51a9e337513258dfa1885f5b5bed36ffbbc3a1a7e7016dcbddc085219ad490efb794af754e34e9ad505975b89d5fac7fdd887ab1b4f8c858c755db5901678ca35840a0b4119e2298d90d5393773172fbb46fd5a961e0d688d0cec930db2eca6a999b6e071aff0beb04c9ee8c0c95f3c12e13583607e022c73030038396d0e61a889e38bd82727b4a2abf82ac1534fa47d426f855a50ec8e4e2f12a1be4f82ce814035c0d9b00139bb218e4da797e182b160f42a3f7fb8c20078fba474de297aace375ceb374e32a84ac996179336d731afd90a967f98c835f1e143de1950dd86014171af2ede66eca02362cc1ea2846a090c39b9403360a76b24283074e9a305f78815c42882e18fe63cd3603647af0bcc5c1e49ce8af8cf5e9cc29fb524a9fb6e77d5ad42601d58caeee6a8a032ce33af7fc2af9e3ff3aad83fbeec2a253ae6dd00c5a444f39b125891f9f81be0596e0ca29a5e9e95435912a4390fc43289bf5c651cc768ebcac84453f2e569ce56dca979aa0c914f794878f3a62793b09037c8f0658015a81bdbd83e4578e37efbf969f1f71b770750dbb8f8b92c23d1226c15ce474955c8a92c7717c31513180820ecd1ec0d0ab7877d414dea391fa0aa6571ddb028d2725bdbc5958877aa48b31983b30f7d51d3057e84db622004103688962f0acebc063ea55b806c152345e113a8fd096f542a52b1960f9c9f573a08aa1d73c2656778a9c46df9ef4924c5241a6f9575a5929fa0c09dce942cc689dd8712d33a3dcecd4a6294e70a0b718872d5d055c893ace9e74008cc0ed83dfb78020b32bec9ef40f9312bc424b0cd0772891755d5a5f789cfa31d18a39210624270c0857d85b2d561c35eced9e04186a19499dca5c2f7cee1b4ce2c1aa34269bb43f8872143c18010635beec9acb090f30051de30754d3f0e70bda9b70e090dbf10d02a1c7f87089890d07b41056c2e479760010c25e9d9e62725b9cbd0dce946b56b63a577e44c0c64f7206fdc814cea47594d266ab37e84946bf68585542299070162c63e4509677e0c98a657df302f302e024c32be17f36483ff47a3c53f933550398108b945cc74b75abaa3cbf72c5a99a5a29c1d57a49246667e07d462b05820099f7d848fa7df70dfb4ef5a8f4a5a3dfdeeb72041b424d620f94a2146e79f187527959348c3c09d41d7521253eb3ccdf8b1a7fd8197393eb51e5d3ac04c6f35a66a584b3a33edb06bb365e1d271761049e28a04105ba24588ee70c2501b55507d45dd9361ed1cd26738746092115dbc9e52783e22590543ef18ab2b15ba1038bb0da8329f99b7f505a6ca8c99f0774f1ed00ad4a120a05295abd4408bab8c22a04956d845c58de8fcb3e42d46657d10a96026dee86c581157c78ce6fe1af30ccc2c4209d449ca8ed025b0b2a8828a945d3b900da61609c544e00af4a0bc2fd827862831347c7983c0b6acb92d0a1a6badf0ed93b7fb35a9a54c916bac9767a5bb71088229472fef590d51dcf300154363d3e6f33a833838d8c0c5e05866571441ba315915654d5a51374998c18d2ded47066e692b6ec8aa9c6226f6b9effe8709b402ef81f9251552bc7f7d59498d17df6c8642fa5422ef28569e8909afc4484b1ee3c83149ffc643cf156f24e1662a998da5dbd83b63a15ff86e8dae5e0785f67c39c0daf205cbcbdab1ee3ad37ae23eb915083bcb36e4ab4add7f87fbd8b71946f600a6d4c15037a06bbf43c00b6813888eaf5af34d9372e6578394ba6a64ea6bef3f9acabd656a7ca6f238775e2895965412c414f7b75fc8790ab7a9f3ed5a2c8945523b866653396dcec6bced98392eedef3e3cd05bdec2b655577d1c431e6061be6cf25fa43591aed0a4db74db8fce72deeb19d48a7d12eaf49db8f5f983d23369a8fa23a3231fd638dc21f2d2fe3ec882b6a660085d3bf9810e5573212b061a03d0dd510882336cf1c57474cb50d0b29e912cf67edf09817affeba1cf1349cd885e5bebea78c52b8fabdb94173621213cd04802a45466a12d5c880f21365115fa914a46d68ecbe500aa537b818d82ba52567b1693ff9af1e5e152645a3131b42bbe5bb9eddba2dbcb67c62df50caa35210c1946c439ba983a79b2a947d5481b646ac4e25ffbdf552f94a9a67d78d9012e7c2489e89d861a81362ce9832a7315f1c17cc2154816d4c606d58538f178d0f528d3f70c47c9654b586895304d42c6d77a800d2e0734b0b9d184b89328d207056a4aa52828b35f72b626fe6e626331b166665148dcea3e85f6721e9206d083212db6773841ca4fb34a442cae70e4927d3053187a3761842005ece952c91b224f74399dc3895b2e7cc32079b63a73d256b987a4001df2606a269115481a06b8286947efe218a7940cf896e017c2c8eca100f805dbe6afb6962205fbc03ed798c61f8a42f0d423bf2145f8c70f32b3bae9e46735e195a4327cfd05712b6221138d9cf86a76bae042b4a5960e389bede0d80d5341f6c129fc9318b13308a012c77fc6ad0f0d95853e0a77b262c970e43631ba11465658b176e0e6450d70a08754bbd8b4c17396aa1f0d25ec1fa5d591a37d72fd9eca151a8d4b8801c78ac45912942d8c9e5a4b46f030f055fde15f343d7d1e6fe103e639254b342ecba69e5048ab0d605b88f7d32df24ef3869de4cb0268a0e4fc9b544e651846934d8703235f9d604e4cdfd15e6b4552e15f44265cf4229449ca9430523fd125da087ebbacfce07959863acb9a9f946c4f5b91106167cb1f4aed74661a4d46c48d5da407058b6b02e64568fe1cc45433f51a7a74615a83ca4ff58e4f070796cb4ce4c553b88a59205c1517d1c72e607e47e09a4d009ad335a5bb846d843e41ea5c4f0cb9f1c36de3dad3c67b9fc335a37e9a711222997e0d20c6e13b19f04a08718b8bb8c77da05e25088d777661c588beebaa10ebbf6e49807b008215a6aee5031f8613a160f99f6310140c9b29794b6b8ce91b8d4cff4ade7a970193267470b55853777f6fdd953d74ecff7d8a001f26bca7761041c83f35ce36697f865811ac6b5aca776b035307a75432d9a3b72a17fafe854d44aba7fd5243203faea55ad071e492b27403a554d725bcf3aaa2905dcb9d1bef3cf64685d80fce310de97b92be5bc2c3d55a51d4973fd60e5e9c3600f6cae970c2f58830511fd5ceb40ba50a0e270286e6067a65d21e7638e193afe5b3a99b59f42a9546245ae2dc99773b291b7d85fc537bdc3ccb8c9ce9825ad4f8a18c2eccf386f476d9702e623ba28083251ef712fa083ed3598b4c90182d71612d14465182f8b0fd49048a27a5e091747e47682cd8e6197fe0366e25378ff345d531e34ea3b761aa39b545cab93d7d040c2a1e254e1d9e3a68906140dc237eca1aeba020c6ab7ead9295ba8509164571b5ffb10c1fb6e3aa333140f870f647125b07c5d17a18e801c8aa3d6044ebe3718f2c41daed1568968ca540a3d78733678246244a2b841949669e0d2751322adda0e6a0ab14785bbccd4151e89dc61111695fbd254b1d5a33b146c9742f811e2e30ae5e63b370ac12a2f51aea517b17c23adfd0b20c0bf6911b6dfc23b3709d2f3a59c59f1b6dbf40920d05432013844032d413c07543425033e30ee4551583b62f611913e2e1de56c4cf1f6ed9f5aef7bfe7bbdfff3d3b25de134d3e1bf7e79df8433831ca605975b997e295c38692004043d0c0fda08e9269abd52aad7ae03256eb66053fee125cfabb04131bc56aa00ebe350d23629807ab4e9e93e5afcb42fcb72f5c0c545e2935ef17d650ddca3b1719cc9118fcfd62dfac7664cdfb0dfcf43403b69de86320102014c4fb0391134d6463c8045df4c8f118411a308a2fb4fcb97190a35d6a83c199195ee22dfa342ba317817db3e4c6595828065c4c700e9e3d37fcfc888d7ad85a30108e776347a70386a9281160f82c1faaecabd40d6c02a81b2345c122dc7644cadbc3cf8a23114216312da2e0a3a3274f0d942a73e553cef868a0dab8a4cb1795d54ad5aeea151b603f65af270c2ce2dfba6725056b667f2e2745661415e6a5a26b69037f9527bf9e5da708e688c5be8a6cece7e94ae555095ea2dad49c0f30628ff1c6edc840b1c5a4b3f857b8a573e35de19717efc83796a945825b099d376eac2cb9dcf76a4c687396ad4fe18d260db60a976ab88ef6080b33941433723fb3d94971571a61804115efae603ae575b619e005e72f04722f589b30c64777dcf4d79763fe9c06500464a26aedefd2075f0c007e64fd8b3200c634b369cd22a3dc916e7f2ee4791474a33106c0c8fca1b4d70fa81e42ff989e520362f7b0ea08bc9a0ac1f420e0ed93f06b2a99efaee2893e45452fdb27d1c8df09ae6daf40936a894bd4880d933b9648ddc14084b5a4b3c3845c1b9381b831329ef1445e1b13bf46df20888263197b88666f2c0443e08aa31ef3289e3764efa6ffd91ccfb9abb5e4ceac0a71ff0637368a71e6d315c7260f435dead76b200ee3db34d02c05a8c01a231d4833b4800748787a364938383374a19c602ebffe0e4c8d497e682708440d0c114aa444f93c83e03b2a3ddb3d387319482fc62a35bb93242726aea90882c53aebf01fe8191d93280eb6ca556aa07b20e088b78a880c24bafede68da0b3f9da7e5e91b32f19b32d839d2036f1373512b90e7d20384f1300c8d23835382d70ab7a1b49b048f96c62fce14e5caba34df71d8591580454c4931acc9e1019b6f512848ba03fac040fa15eb0fb372369828e71b5e5763dc6921b32904060ae63f7c2032d783465e2c1ce4d013c10d5c4ca996033c374527560dc0981bf81a2688abda9421d62e30c371a99871544cd46fbb822c2c444b5f10a8096e92f74d12bc56c28eec9fa581c4e97062e0de451bd807edfd68f5b234f880a90b46841b08fc2cc4924204e8eeb1015df476961d99cc607b1cb1271f2c55f1c3d22208306cf99f3bbbdf8c29cf04e3c57cb047346c9273645e07e726be99d0f7206bca2a0ba926f227573628e295b2dbb725d939932429412b5d9868c73579eb1f24e3da71b8a47baa6cff968723b0c330ec95019cf7c1268c658d2975af238653650ca84f80dc5c86969ce1fce802c5f84e63158ce087ac4053b31d11374fccdbcfc6b0a4693293693908683faf26ee7f000ba79551fdcb208adb2be718f16ea802b1845462cadc7be00c8905a4cbdf8b07f2a8b900e0b9807660e760b25fbaf890b772cca97150b3b518616ccb0d02361d86341226fc53a51787348aa1d75771a7de437cca05c38f75608b73c71f1112d81e90b457a3b61544d7a928310091871ba15ce31815084dd0e590bd1d192b1a29bc187623768576eac95178635e9ebe757c92518a368fe143d9bb6920278dc3360568a352359977ccd59112072c3a91857464c4952a885f30ad5cae12a4100173cb1600b552a63dc0883f5422e26cd4ac6bd044d1e63b1ca9c6d1471c60bc40040771e74fb11a4b23f2d4988069c9bd276e7773b437f4f69fdeefae14a5a9924e28a063d97b42fd843e81ae8c34557a988ec6a210719cf8ffdb5a9f9e918d6694e64fccdb915fcf98b62af8b40b79c6cbc2bdaaa6e4fb1c8a957c0efc09c61ba19093b07a1516a96e858fba65a05e7cf16a70f117cb6487704a865c6bf6edd606cb5eb6344be7f72a2ccfe9fae24ec93b1b3e5810146af75e0653b343b2a8897fd398041c9eb9f2f537a96e27446ce9efe98e1bd72439299cfc46d5da6bd520aecb4bbae01212b0c7672a600ea6295f4db58a5b9c363d3ff7349a0317f5986a70e9bc52f0edfadc2a0f61c0596d3c50413f50f56c4e4c51849ef403496a1f22c474d0812a73b586921840095c093351bfd29d4bdaaa840c94052670729b7b40064a28609ce26f3362c4b603d6360be79ec1b67847b69510db70378f9230d845b106f5550753ec23b87c8591766494d8fb0552a5e4a437bc62cea06ad937d72aed82a790a9d03a589ced49829fa30dab9a35e96e6ad6fa1e9f8f9f021fc7ccb921ea6ee43700092038f24a1e337dba370d90b869ff2ae9d307f92d1fb7be35ba88efb60f0bc7b32986fae188ae5c990c747092ac447850645950ff40acf7b93f4210d0090f33b9e946821a1d5c3fa50f20d2e443d8933ba42b6be7d9b8a6d3858467c05e47570f207ffe1546bb581f7fb3e790d838adca144639bbed26a8e629ba878ced081b9aca610b25a2f001626ebc0eef8823620ef356405cb6ef2dda04dcbb9968dfde6a8d82d50ae1b6d153c28bd3d4580ee532e802c3ff26f42013bad61da5c54ab3ba5a6d42ca5e482813c52473c6913d35f3e376ec87015947394d749c4b9c952ec50a00d13668b62656a7cf703b2a5ed0e628dc196e80e35b317e8d1b07edf7cc1f17f6403319c6115b3a28e01c6757dd4509495c6d60b61d83fe3e921ba351caf987b34bbdc5eddadc457f8dc06de9f2a19f8971bb7517f2459166947dcc8d516e5897956f5bb0b02d13686d46bc44459cd770a20d7e7c120ef074212e6a1b6d41d62af94aeb428f4f4659960c88ecabe4ce91cc9744b8caa596e6808e89c9264577dedc235dfc2e66b8ded6c14aac8e166e0a37ecfd4b9b83b12a52256a4322b04359070c7b87ec62bbf0153ae810ff32777f3e6fc552af48fe6da06b8ab2ec8370322dcd5bfced48497122a8462e0d97c7d401267cc8fa43df07e3c35bd7d7db9afe734bae24029ee0f27a69cc7dd5b41dd0904c73dd33898f49ce93ce0985b73cc1db77e66f112ac2db6c3cca9b49dbeb188fbf2c66ce01665839d984c87793e28c1199f2ce136b82f3af6e4de777e1e3600ee06ef1b56497220ecd0edf2ee279fc5bf24ec9dc835c4d55adbac878ad7485e442dc44776bb797ad79ff97aa0b763a72cd57f766af9ff69ebff227ef453661417b9d33c53f39f7265c8db667a99ce3c5bfb46e3df1fc0bf70b88cf2fdb59950ecd9eb1b7d0043be8a7d956d175e6116aed345f9ed2ca638f9d27bf2558aa7d8a7b840ad65f87d4d5c0481553dc9f81ba16ed72fbd7d053a7b5897a6f46d1f94e27a675be71ebd5635f1962c2a6027a97c44fc68488a1c14dc378bd1cc3d1b43757f4ae40b2c9bb5853f0a0efacb75b13c8aa0400672655c46f05012db3faeebfd2e088db1b738af8d0bf1fffeb628fc38a694b0fcb91cc544b7c32a4f60e024dc5540bbfb81024ef48f48392ad571e17da4eb69201c47cf5c37b87df77f9865eabdcb2d44c70049b4faf25d220b5eb80ec617041750e855f4e6687628bcfbb9e2d741d68b61ec99b0101206dbaac3821073092c0e1e00ddc3457048335214471d16b07da708ccd965efd74ae15c5135d172c4d36f9b1be0c47b9698abfe904614cf3a451c92d1ae2b0d5a405c8a86f2ffc54b82689b2b0bc7f06df474055da040295a87235304419a86fc42f93c77d8fa26f1913df8d8885a5ccda66f2a322958d80fea2a472f6a333c8ab66761b5e7d776527224c0185361e0ff97d92a8c89641190a0d6e2bf100e1d733154fefc33f8e0ceb84d06c8d5388c831a512508c03593a29fc360c5332222cb47d3f76395a54c96585b46014d66816d6302c31f03888f08e6fef60e99c347083d686c53e66014b2d7f3aa6a40faa7066cb0941e9fb0fc1e45105d6661cff1df877487dd0405b62e07650c704de170a2c4350780848148270b78d4ee5dbaccfb8a3239655fc7b1f97c5e4d79314af7156063ed6abd8eb567db3d7bc64e0efecb94ee6bd6a8da21c8824d6a8199b6c3d3043b27e8322ccf665126cf77e56be0f99b3e618cc69211fe20144a515b5f5aa9f992b7ffb08b36fd95e7aa3948024d7d424042529062b4fa592370b355b3a743591f9916b06aab3c03760093de1c6d4aa12e17cbded4f4ad9969d1eac0f06d09fb04e4c3868323b3d3b247a144d795a936f5a9fd5ef8e0cdce71d4b68891fe4ae046f192f3a241aaaf4ac6a8c16624679701a439ccd1c58a24fb31045d3f84914bc5aed122851dc2ec9874e6d3c0f0ce4a1a4bc05a9b532161dffb13fe440406735406507259db663d2d1fbed42a564a445eacf62a7cbebe791c0723e718177c0eba5d032e1e7a681ef1293f6f9f9f9fb9e74bbbc24eb88a70576d1a05913d5606564a3214d94198050946bb09dfb8657a872c2274d67b2b9446c74a6e9932d4f91a1ae45108d83d450ee37037e0413f360a6660bfb34fbc11f74b4cb2c08007cb5f64975889cdd83df3af1c046ca98eeee5a6d61757272902b513c50c2660b4522956a22c7ca859b1f2f19563ebcca604d34ca5a279fe6fb14338d2cebaa445b95c36a16132f750a5b38c7119fa504d877dc61f228076e56deb965cd3c1e0a119211cd4046c72f6467375d0697a549ca60617b29d60f5f5492e6d24b1dfc7659ac44e24f69f49758507994ee1cd5c3203c2ef441026c3e04a4e54c4bd04c8b18c8f294d41142f20a1372e27e07c48897ec7b87c69ad28608c46eab274141075611c9d38140c4c60f4567a4a086028eb46c3780ae886344e649a076edea083c1ea3f66a7a9bdb8e21aaeba4947bd3cbc2dde0764b28a744d622873cda3f88b65a454288633935a1addb590eda51d04548d0cfe976f2a8bab00c66f16775034b13ed9e9aa509a3ab4524c5fb58b305782cd345a4eaebd31076fb6fdace4916229def1c7cb8029da8eb939d63f4fad2000152b351edf71249f0b2d17eaa263f137a89ad82c5a94a9a388441bbf20f4f2a40694d54b500853cfb16d1982e2c94ba1e7b2e1c0365cf71c696b4d6c848b5e3240fb7a071f52a9ed934c56d2479fe0abd077eacd60bd4dbf16d451b6842411c9fdd3374184ea5215e4ad16c20da9cff2a005f3036617eeb33b50718bbccb46648ba5e2f6a16914d6f9ad2867f612b81d50cb9e5d63acc04891900a0e311ca91a9ea5d606838622777f96688a41a97c7fd095d36030ea05c5840a13ac48c9a6d45dcea7a1af0349f8981ac4b6e17c616e70d19f8a5e73454c56f498c8f2a29552c826c0066228a5008ad43bcf2395da2980ab23dcd8b49732ffbc7773004e1466c8b189040c3e7f24bcdf8ab8452df50699f1457bd4988ce941aa1e09d2cc8d30c99d8cf6ac6554f2a53f00842131021c14ba6ad314ddd5c912f12d7173401e16f04c0580875b3e2f45851d03f7296ca22ac7a4aefe6e919b5ae5c49f7c0d394b66b17f73ef2ca732b269e4c17324fad68ce4e6d4cf5339f92c9ec33a61afc76a573073b9c661845a32b661e746474c17fa574f194da3a76733f944acb529b7d0255ca7ae62d01b9b7405cf08e6d87f48277b60830e4fc70f893a247a080211ebe3f12e8e3465f4970f8a25adfd4531368b847d81a59200693ab4bf6bba2157ea81f2fa4a4aa8b672282035ff49e7511867a59b150da0cdd51e9ff4f52d4efbfd24b85086ef4994d6f0a495c1493bf36e8bc3a1a818f0cfdee61b623c248884acbfdf6c4500c074121a98e1682b0f7a1b366a72e5ba45101bbe0175e32e8b134bc5ed847435a7d06ce14023d7dcd0bbf2cb716b88404a286476b8419e888ee6beb0df853519b89ca7ff6c0366936caf064b1c5f1b2f8c09457d92ae1a8146142001bc60dfc05023f9127aba0f7a2b8dfe5e64ee53f29c31e3893a12ada442423e190911514cd040f2493563837a0aba541012651739564d2cc57b86a75661aac4a202eda38991f8e126cf74099a4222ed67d22b3e8a06baaac3b294d91379deb6fb5ec77c179b72631cd62460902ac8f34eaff4c40c199c0f6e75e68638724bded50f8aecd1183f4ff6d92455a4009857d2b41778faea65b53a570489eecf07d94b68013c3b296a59ccc39d12e420e02668a4873157e32de3645bfcd77663916e80bb8c92e8751f4a03a071f14efa11c01e42332838c8bd8d3c3aa7f10fa705f2ed537867f7101c6f82257a0b7552da28b4473ce54408706adc18fbbf7049ca5b4b511f4d1bd43438c1d6abb963db66cc378caad73431b1e2de595eda73bcaf5e2ff872fa6313c2bf673621b85ea366e315ca38520d318445b05b7c4585f40dd4beda6ff7847d50b6cc0b575d391bf8f083c291d288568bbccc0da89a001d5038900a4ca35f3978b4dfd4e064cc0945b2532c3e799ce52e36b46ee4cbd6f36b1a1a6da315d56a3b8948e87b6340333117ba62e80db55389517e5eff846210f3b77159c687b3f68bceb817181695fd23fe7470d719566c0c62e5cd69141c97c4f538d56420bda604900f48525179c7d4cd514206515d5722697c14c80674af95f37f6acffa2317ac376533e787db1009c49bb48f6acee3ac2370b2780630aa56862cec984b12d12abff05637521266f7da6ff97781e1c869f7298e320c31c30b296c064aeb5356b6d7d5ffec855bf7eea41e4bf1936b5de845e2975dd16a0dbfd44d16e8354ec76341f35ed6d7f795e4739b9eb7c8a8864d390286deedb7ebf8f97fa790713599f9890a2034044a77dbed0aa84b375589e644650632983e7a4e5d035c56ff29f2ce79181ef32bb67c888c23d85c9c360328cf5126c309aaa97f041f04bdd4c3be3efce04eba510afd55f7e9c2293300cbcf32d1d082dfbb7953b114517b3d2d16fb6f3a4b31295a96fef3606c1468a376fe36ec12cd93771112feb1a81588e040faced0ce8f08f3b3931211b40556d5e4b279831a0c0db6d3f612cb82e252c0487c6a115aa42983a06b41c2e8d05c44b5943163e80b2cfe5e70fbba3aa8633cdf268bc435cc422be60673819c46d75897712e1bf83ae2fe7c282eabc82e3a3588ff984eb3ee7afd448e789758a6f6776c4a27eb335c529d164741b4cd6a2a4c669fb64b7be6a7b83684be167ce376914fbeccbcc322577810241b0aae424b64580d86e1632be5d05d3a6b7ddecf61649b06b3ca4e44b0126fd6d1b87882acc47b88a7f95748efa7480c80c0a2f11a03d19d5e34b9d4aa29e482164df30021f1e272f7972b44aadaf7b9297af02f96a4eaf303a743829c675c4b71d777f6f0f4632914e31edef7a3226fc32af38816dd7e971f5c4a32170517f4941d766176a1014161807efc3a8a23e5e42a1d6eb8b1714835eed4de404eeca1349e3f077e09da66f80edfa9d8b82b15acc897ca200ec1d68948387bfbdc01e44b4e13a768dbb7e3102b7aaf6369f5f280a2d45e14bc35638d994c44c7c71b0006ab9097a1db7ce419150debfe9798ba93364c2dcfb0f909f71855f232360e6be38bc597790cda887e00278f5792fb53cc61773040782e728d0be49021ff68ba4b69f574b9d7d4c6184564d08850dc2788a90d14755538412278b9fc8b340a11ca64ab3edb14d95701c206ae1ee36d5d621e744d4394a99eb0002bc498d67e81faed6dc91c69f8f5089a68c8b01551edd80acf0437a7e2b6936387179b38e1fcc1d4ea94515abe0d3fded7b171ba5a72fea1fe1a1201947383f525cd41639ef787c5bcd22bff0982949d6ea7c152a990b94ba60c4fa0ea054102d1ebcae1d0da2af52b94424e229e2a69ccef43a11ce71ce7f8b19e05eeb1e03438afff407d122c93d430d8d15a5dc3c8d269fe104ffa09d0c7d6a1640ffd7aadcba9e5897d966f91efd580ee2a3757ea53955a5509b3d65bae22210e0c0bf9520f11295a53a4f573197218f5cb167b5cbc71b8ea2a3c3e3103dea21c81f6c0237150b7b75db481321fa39c116886ef957f7b328ae5f74bbddca7b6f5344fd09b1db1c69f820d503f49e6a7cb8810ea6ad87ff290511670a4738b6a0203b9eecdb116d4d80646f747c8d97ba311c0764581fdf98ac28c76210d4593d81b3d23be4a37ddbddee67e5470bacff2761551f00ef5d47aca184cbcecf6296137e0e37df8b38bb9187a6345336572a1cc34096771ae6a8698a68e5d18ae3d965cb178f347f606939b260e2acede0644bbee10189ef89abe5b0d7fca0d4020ee4dc8338d09b4d6e17ed2aa611683e6f5420f1da711cc5f5c9c86423e28253eda9832dd8db6233bd6e4e7c468f6cd7c1c03f8d53889e816f44629373553a1cdab5fdc48851c0d0b5c778784fa7e5c85a3e7e72d91815f6ae51423bb985ba596b1dd28ea32d9a9d344300bf54e75bd76bcc1d3762b57bd9ea6b90afc445764abd8fa4f3adeac6a892ccc91b7ae8b56ef599b0df8bb4e384470be7ca10f866194b55b90d12451406af1d447b14c8762f36b13e5ed937500a9463462e16ee2be628df16d1ff7855a8274010fc8e577c65a7e8cd6f2b66a4a2c848b5b6ef99ca7967ad81ebbb46ac16fffb7b235b008b1f89e20e396955e050c60086021833c3fce2d4e36b95a4d8faa574d76c6fd147a1e04702d3da269938fbc92185de559ee53dd0db0fb96eac950f01b39454317cf454fbc75ebd1d7190f5bdb394bf997386fe177aabb6f60b4d9a5b99d464f8c8b10fadd7fd029483e1562dd22da7874694d392103f92dd7ddbd4d89e804e6133495d48b0a240c97b9978362fcdb18ceeb8f39c64d4bfe4c58e6f7c7b69d73ca406102966e160743cd8be15649d6408e4aef42162eac6fffc0de44425cf5debe7de5534a3154e50e3a6a789d965242217c8b464480a4ac41050e96404913441f9b13611192773423fafdbaf6885252aa3b019bf35f325d3a5ee30295fa524e52ceb100b5df4bdc087e6ee439b2b2186581e0c7049abbd701e89fe00bda5c7c2795d30026b24a464b5e654bdb507e9c9c482a6654e759c998c8206b71f0a9627f59b0344f532792b55cb50f4f796c2b345b33940be328a27e1bccd1298d3ea569848652542a314acd926cc951e4229d1ddc4b2f728c58248f6258b153986ab68417fb008fd6d3a58e3a8d47249d5b055cac118afd30adba1a9e04107d04b5534c5cb77565ae04958ad52a709af8af0cff5ee8b715ce97c08f821627b17537d6008684f31de2a6c09c05655ed87d315a36394669f10dfb07d09f1008fd87721af0aaec924d82c8d246d20ef3f158aea12687d5c5f4d6cc1e4ecfab57b42e610cf870a9433781468f94fe50171b35da3fa1cf93e5a3ab3e8faebddfb1d566becaa841e0980581981fa32c9f8e6db4dd169d66052cb9d4925373727953a7fb8f19b14d0bbe703c93588e3ff35054398b27302b087d03fae6d92bcd29596840ce942a8edab1fcf4be58f3e3db8b52d91b243a8620390ae971908242bf7fbd5360faeb19c70baca3e96f3a8497acc08cf6df4992888edc6c67244a065169c118df4dcc6be1d44068c39e26693fb64f8df61f56c6c079e551f4ba93fe60842be5f61e015a21e43884f6caee37f962d3540c8d0bba43112d012f6d567f98d9fccdf602d123a249270ce2f59eae36b2a1f6bfcfbca9d9f7f44b13f70612366125fd13472b1a63708cd7477e3eea693d7e4eab06f3b971e2e519b2220189c406680668f9e4be10558ff819b58bc65fdc7587de86440f1623c3bc5baee3efed42207283b67a2e2f0f4f705de34ffdcff4fc5903d9aa358c3af4ded11743fbe071db7bd98bb9ce40f9d18300be2af0bc2341437e4b240728bfc42d66fea9e9ca77024c2bb671dd142047885293192ff78f48fa582763f3428cab6d6fe9770e90833b1480c914f88d0d77ed73aa3cabd85e101a9d2e8f109ef134893b4c840c15c06298c97d590831b28be4cf20710f89d3bbb012ac1459616d50b2b4c221b7f363283ddaecc9690e66a419c52fd5e84c228681300844ba7925655bf98d010006feb986361864ac1cbedc63a4139ad3a031f8181a0981e6a182dc7ab6cff79496b8aa4aaecb2b209f2ade21197413dbe7c7fb9a5eff83807657af64416300935704677b44408a6415d523fd027dcdee8a19bf969c0cebb8a776348c7bb627022a84d873bda054c5a23c1ecf3224ab2719fbb9740e6fda9626cf229d4ee323809d66c30e1fc9a3bb683c0494ef7f95fb2b060258a7218056d589be600bd8fe28a151581751915c782674e6ffb623294023b30bfe1d5f9ed3ea15c0d94fe2527148a4b1fc5458522480363b82d0aa9334a239eaebffa5eee83c0175c09fadf69180ebbda91cfb6d3c67effd3a7db74d6f53fac42699e8ed4024b1e85e3ec528ce53c772d9e483b249c64757696a3a389ab7562028a671ec60ec8329305ec4a2456d9eed294f0146aece6a59b79713c3db73ea49bcf0722ff33eb034147ffc5a175387e5032a4451d9947ed8652fc01f44e7f967de58c7c94f525712e92286d57397a9b19f63419089c836b68ae9e0004c2eda55dd4c82c4859d3ece3e6ade01a78b614ac3fa4ce12fefb52a0f552221b57a08184f021f71456ce7cb3371d6d94cb69e04f2ade483c7091360807caeff8e204a482e4181d2011410c78ebbeff126703844f5ad32e4aeca8b4eb4e3f305b8106bda8bc873773b81e7941168834604a42571596ea245852fab2bb293819247f52353bd9ab2450510f710eb540cfee3b50952eac8f0ccd3ded4089d4e2603691e8658832932946beb7c4a02d4db161cb8752858a2ef236c78f807a5cebc38b05435b6b3c5616304469a9c9fd245612bdd066308232e09a94485708497472ef2498e0e875d6707a68e35ab7f4a22633cd4b8f69f1128ffdca5c0624375e6e8003c0191e48d08f0cb659b0d08f7ab0950aa1e20c897c561a85630a3e2b18f8d37bb1f2a756ff8c927198d2d24584099fbbdaabfaac20274306087bc629269af20666dd7f3524c1bdfb8a1e49541d1df30b53459ce64e6cff92050837fd4fd93cab47c80e3b431b941267eb4cec6720534528d3628a6360f53144610fc4aed0fe1f931a7ecc9a39c2cb7ca603651d14c1c2aca9bf0c61069486d708ae2882f931f39ce619a5e70cc87e6368cf38ed97131c1145c8492ddef5676bd56a0cf659716638c7e7e8df1f1ea5047a45f8e5bb1b6045085504f13f518d410c7892e1a36909c9034fbaf63db1e0896337418c11c686d71d142bf4b445ceea2daaa91fc95fa69610f6a1950f02ac5db4582b9e472fc78f5eb896880b4a74e762342b0145927d6b5f9a6e43c6683226958b02e0fbbec4f0f3c96353da961fcfd1c39f9c368c2b0cc4797cca794037f9bc06da7db7bac34d203a9c06788649db16d1bff2077ecc6220bca84de9b2c183159f0ee2b5280ba525bfff3df986157965284d1890b3287ddbc664736a5ca7eb25e2afee50a940fb7e3a882586f6677914fa48ed865154753c7c524d63b785d374d4f53bac436b9c1760f55723be6d3f9d46604cdf32bf76160427b338587d8c0f46e06d367530f2ab4bf0b2e69b446f177bc75da7185d47f5dcf6ddee7350275e65fe0d8470bc5623a3ad6a0d0902bc21b06dd8e2a3abf905cd410de32925bc716a3d86267c4c04115a21a0d20dbaa7d3e96df04d8c7598f0e8fa15d2b742657e1b6fcd4fe14d2fc39f9aa6219ded46e4d3fd876e02e4aa7e41f148cb7181b6168123b44401b1188c7ebe7271d44ecf7c42c8b6eb529970e9557c202b4a553411367ddc656f85a06cb8214a4b20f7e934128af6dba4fcb8c23dd2a5bb15385262aee30602d13390e5f7af58d0bdb01366b3675cdde503164b9a75975b1d96addf1bf02bf2be712c259b7f3c498e77cf011663fe9e65bc98eed0a458490d2e621fe1cd87008a190b09c2f50e68e5167de078331734608aa5a3efe994d0b832370f99bf3e3a67a894707f98a2faec0dd8cd48a24d98f059a23774f793bd33bb9f79972a0059bb340a6489185a0db6c74e76dfc0e08e0621e56143b0e0f1a6c2298c1d76a3114510528f2e08d901b68d07772067170a05718fde10d2476743bc06d8e34a317ab2f4bfa927a1507e1ea18ac89de1e33d2c3f6da054d2c3ffe0291dda8851f1019d864128ced7bada9239d9f0afcd9b753e2c442eca4bd223bdcc490b42dee88b6f63edf4c94a4505bf8da54c080402746b217832c831e3d587e0256be3a82a3a9c2906f22298e1e395d5cb4131c5ba91657135918464e1e18386f3cac20591d5f9b8d1f0e89904fd163f3fcb631a1cd77bc082e6599a3f2a4f4c8e26954c7600a3c504fc05f7d167290b1774faaf86c05394b553805b7c4d70865c09230a43aafecfb1272fd4284419521b5319b03a598629a005e26c8fae3a9eeea510c3d761b0f53cfe1b4061d42431f48c474b70475b4abff4b99820163596fb38a4d8af197ba6832b606c1c939608cf1db118e3cea5670e4d1e0fb2471cdae58a626f4038a684686c68ae64a9f2bd74759f0f6f91ae0add030864421f0afc8def56f58f3131ebb02e99bd18327ceb0c3e7ca22c08296a5318ba31a0868a3e80bd4ef9ef8f5a63f2951efc71bce656b1f891b766f0adf619698aff31d5d11f02ce4e93022296c7abba33da3e83aecd24d791d24642ce0f88fb6e8c8286ffb887d22452b4fc262d5a6e915c72cd94c424cc98771194ba74587d8984f42dd0f5d2cc1a7e4b32d7ab0893de63ba3a20e54b266352b800dc2de80fbdf33598a68cb54ab9f38722c78ae0b425ea6ff031fd51e7fd0b27dab233ebe41e1b892f606ceabe49ba1cbc6edc9d2fb98f7b57e78297d3d6e086af4466bedacb4ca09776a12ec2b6949265a49bd4b6ddce37d9bb20aaf44b7e1284f9c4f5276ff8aacb3453fc2d2ba51c9a8e0c50e4fdc278ea5d839140e527de30726e8089e3c0c5c9f7d4b3887ecc5618b53da00b078a5e2bcb0c50884356eeb5594a1c595c8f74fcce6632446fab8dbd306c6d2aa59f99a01a35126919bcae6cd7a825a2cda0b3c2e542f41155747998f13e54c38f75d9e281205c4796e2ed31a08154c99653bafaf9d7502c69dbd0d3c3a740f3ddcd3b3e5f2786ce0c42f10d8e951c2d6e255c02157a94093cf4c4619b6651c44f3046cd0a2bd31ce0bd40f8cb2151dab3e13ff7acf1d2c3b82198abfbb861422461d3066cafbfe77cc489a52d3981f62a5c766ff6c2e29f3349c48c1c0ce748e2255e85d70c4161cad2c8acd3528889158ca05248b5e30d933badb774cc585c0a187a4b5424c9b15a322e0c5a6320ddd445eab8881fc7e34a7d751bf20a0a4f8fa6c05b55ac0e7eba2cfea298f5b3939383c24ead1a2c83bb5346958a5fce57c0e299b4fa4fa5601c06b96cba86185dd0bc115e215c7c7c9fc1d78d242f5cba099283619e7cf8aff8ee5f82ddcbbca6d33b869528fa4e3e598ca5d14b6251ae4ee0dfdab080d825abd6d08b91c28cc34cad010a3e6867c87c0cd14d00f2546d75e027765f88ac9e3dbde2858920ec6c6575ea103006f19955c3ac3cb5a8d41d33b8475bf17f05853d1b8c09a1b25784a66307fe7289388ab84bc438a46262f1aece39e3ca57bb1b2abf550d063334a1546812ea9d6c8a205e0e45b6ed2f6e115819a419c9578b1f2d1afe888c4b79b666937ec56d2b3144bd26adf100e41172bdaa1eedb2df12ad8006a66659574c006c344702e24b54fe09a3fd7d2a8a87ee3c55161467c80e26554455d2a0f0e4a4910c4bdecca2485707658734a6fd64483de1a901caa2984be1d108d66a9b5d04f6c2326b6cf8511ffd7a049959e3383604068a3e375f950850e356f5d2573e5f77a4ba2c04d04ea1b20eda36b88ea044f3706abbd37840b5f3787ef14094179129b54c283393fac80db3fd317a2b8f553f6459a7419d8b158589f04c5c70260255a1425be6a2de5dacd88961fd5ce8f49f278627b4831eeb3a6d735688b7a34124f935e03c60714db3ae3fcfdad8c8dc6ba4ac3cb206fbb7c4cd9310bfc0786f1b5e815b5360c49ee7e8d26f933823f05675343d7b9d53e90602b2aac9034a94c55c2e3210a6a9b6344e6d2ee78dc77e31fe38ce639f862654488cf0bb548eb32fc4be9631583c6440edc3327843cd80c96d27cca7cce4094cb60e3a701f845b02e9c91fd189dd9ea29c9e1e832d15084b59fb9bb9b213a02b70cd81c4328748b9a8893cd0bb1f845845ee25b9c1854f0166512cad266bf874687e298b8f42785c2ea1014d25e3b6565a9116941be22b8cf919327970bd956cdcd3dfd226c80e573079116ca1b223adca1264ab3962ddb96d0693942c0f33dedb2c9a662b9e3d0577c2f5b220892cf439be7c91b9fc89c270bd4f781bda9c1b97a8d5e650afa681abd0bbbd8c4c49641c0912a1a558a51243ff8a13fd7a1e7d0090ecca7eca19726b082de1cbf65a747b14a77889e1e998d769fa068f6341dfc433f85a41a086898f67007a9579ba3c29b5a899892221078faae6fcea923cf067a7d4484c1e2a412fd7ca898cb1eac519c3e00cbd8721b4ebb99dee40857a36c4db9ca470f830d13b5893788356ad0da70be61b0cee21197c6e74555fb5dfda90320a4284ab13daf28b1517552f50773421aaea469b2233b54bb2e7a78f526cfcc56c4d6040394b9ff8fdd1640d3b2bdfa797e66c22d2b29a155a2c25037ca0961dde875ee5f547602c66c609e8f6e53138cf986d80c78d0a5e8117ac743617797dd99970920daf81ab550f6e0a4f1caa0fbe1ac3f52733169a8c3a4bb8d684958a8c5029be4e821443a8f4145a04993ac4e20948642a2f35b6bd54d477f704a8ff3c7417b142b2b2e8591a6c0cec5c2de996ccb2c042d81e137e5b8c945e4cc536e678b11cd5c32be80e86d2f1ef04c897dca657ab54e4e4defdc425742c0dfef9383cb34c07b4ee36c5bdbeea8b84b0d25f58a3593b61d9a80eac846d89e50d9518ceec09ebfe11ac102547149125790c512d72ad759a36de382e7843f62758ea980317358daccb191184678632b5b5f1bac28e31ba1ac35605bdecbd9940ef1c7f252e73736057c43dc3cf543f284a4252f5fec770561443c5da8fe24e2481dca7fd0d38770cdc8299ea63a3066033626d8ad96b5393541a5b873aab6b5d53f8e911d02469ffa36e8c19df658716d0bf2500f3513811b7613eeb49488d02bb490858e2e017b17c4156be46083bb2b4e77d57cd1a716d0c249cc14ee368cc830c5698020ba66d958a59d0c1ace285f2072852158d12a856e9f31df085ba114c9a721e81447a0e2b93744e94117352b29283f8b541a1bd92f38221b339b3cefe0855819315b104d4cce7cc5881ba176991c26f5b3fb845416b30e7a3b2bdc139da18904c0161c42ee339cddaed9e88933bbf4c101925da4cad0ff333ee312e0decd57242d5f2e6655aa4b6afbe36b2ac9914e7bec2dbe0f8cc107113b5576f3c8a4ff7431110e59b26352e81c378118fbd9f70c1da569b62c64c8c1fe47da2deb45d10192a234d5089fc44406ad98596d915e87864573f2ff3f8484888c8fc5bda034b998a2b71450891b70c2257aff1a81f5ccc61aed5099702ec7b60f12985228763492c18322534ab3afb11105febb0a3f394a5b1e19540ec401127a76b16707273e39e80888231e373ec35a71c711626beb21d2bb07e413926454ad3d4cd8dc9381fff122a73fdb05d54b0b02e4aacf22321e2ff19a7e6bb9015f6293708a953d7c16b245a55e17c946cf08476fe4a0899f6dab3c14f7a6b284cea90375b9f3b7724dc781498297a398cbe274eebb29077002e863af2cba0c82e31562ac05ad5dc2c86331251ed000a1c830984e3c777df04c6dc178ad935e7683a4ad43294d01f6edb9e1797fa15c9191f88f6185992dec6ba7ff961be0c6400014790b17d50850406bcb019b0d0eb628f40a3b4366d68d7fb4d0cc4aa706bfc3c8f4030915b645af3957f1b469189de2cecb86f5236e225db0696937b7d3d56216548358530a994fa7fdf2276ecbebdcc42493b4fcd2fa5c71d9d6f166aa0b225b37e8031f67da4fc222164e2ba52deba42aba3566fe4a6105214376d70d1751f284ca131d213cd879792fd7ff1f2e9b1aa9f896b8924ef7a8e45dcd8fd97b9e92032850cbc3ff76d330dc40b45d46bb3543bf69f051c598e303b568a0e1e53a2c5542e8ca32f4336011e36a9805ea5fd446744a4e5ea5f55bb7a773899e43ea13aaedc9b53bc1a1073c3fa01dd01da8830228726146c8de594f386a4c920009602807bce027bbab820c95335269406bf82e703fac04638089b38fb446ea734d1d178fde7f1c4e940d2b52a8e68145445cc38fd55968e9fa3d8feb9b676d080750fa7b698947971088f12aad99ddcf41f1a5c93ab9ac679edfe8850c6ea425ac39c72ce2b5b814bb3adfb2f347c3e54fe9adccb4411f083338eddfde64ba2012d45a5058ff1255163dd71e4fa4ae4da46faaba14297ea9efd2e6baa41a1edb9508e3b67e3bc6e8975c0a29a2fb28fba630cad14773154d2b1abba50cbf7ea2108d8e0a8ed1593cb19c506dcb42ee260114e6bdf8821146933cf7f5e3272b13dafda40c0169a98218d85eb79672bb1b2e1cd0853262e944ebd01d21602514a05e342248d4b08e22eb43f8546b4dc430cf8a8450393ecb1a31b9d0dd8a2a61da570f94897f5beba7a462c9f5367805e043f235782e7e537c70eb4531f3d3c3f1a39be4fdaed6ead26b1ed761e539c2230655b14eebc7541b234a14370994e7d6c78af4d41ba467807ce2218c4d82fc1087a332d35d99b1176ef3696835e2e40cb4c6b7ebe60edb414cc331e8be095a3ded7e6bf81c5d0dcedda76d04c2aa7a1a0688db4fa6aa98f3c45d61210d974c0046fcb5d11904094c6700b73d1c48cc20eef16ca8dbbc0181e88d532d18ce1a2f74b5296c1c58ce32491535290fe453d41413d9449d0cc5fcaa79b88d8423569cccfbafa3f502d44b056b689911612580699422ccc95022fd7ab5919c285284bfecbff3a04e50b7614d971b542b4c4dff36a5de5de251090899c8a99b8b2ae10121dc2224a5ae9e0e84338545802c060681e0ce5893465ee1d2f38a883f85865d4b43c2a240dde6857ed476803c60dbf578d8dd1fd2f32172a9d02b3f94c95ae5f2cdf2b065452615b8d343d91a915516b930730a58147428dfa48064e4a99fe91d550bdd03812a92333e4b3a78649d1f69b484fcca9a5371c6367682e864194a4ca01d90eb79ceb9a35e34e9a58adbc528268d2e1580fc197a89b6b03086588fd187cc073898f437bbc669cb128e7b7cdfff8544cf517f2b833cbe1233a9aedc78a7b9f446d3530d1decceb6e3c1e8f5d6419cf025d289d75badd0f3e18987558f50b526cafbaf35a6d913aea65b16db180a0cde4c0ba6b463730267efd591483f515730fdd8c224950870f1049d3a0e0f1a4c4d2efc3f125466c1b318c394d0f134d919c289fb21801438fb85ee4eac5a561d20cddb2c176733a4b502370d1ea802202948f44349da347a7280f466c96c5242756b710a85d2e88f0251ef70031e3e85342a0a960badb1a693dedda6da76e02d2c0993235a9367a14e38902903663f6b3bc7033108de9c170f68418c40a3f3b9c90254f7f4372858715776d3905f7e2cc4db9dc83f2c9242f4d73e5cca68273ea8a5e9048cfb50091944f7987974b0d8d9a89fa6c8e81456c4f5e1240e889f36a26f526b753ff549a7b483c486407d719eee4855cf8e3d6da9b7c4ad92e2cac7612ef1ff41f5264711a8a797d6d424921319638aa1652a77a4c109a607182f9a0b22b2baff5bde75c20b990697cd60068bedf5463181ca2f820fba50599e12eaa09f97cea7f7768f1526c4dd39c5158167182c1fe9b82373865a29ae35e1d76956ba68ee21428da18121c268ddc9d38a2364d695a13250d139ac47f5619fb8afcf04fda0ad034cd5971e1d06038e18f431e4c07c3953f792d40e26c10cdfa523be712bbb263cf089c353df708a60d1e13ca5ad964ddc07e979b15b208471676a0bccce2238b2d5e6710a175d31eec4688a80c66056ff03fcd10fc64244551556fc2059a7c5058d4124fd39a2cb493a13309f1e49d3200f6e0436e0fca00fab147443b7fde8158bff75a24eb7c0acfb4cd047acbab1735b61a0eb6d61fd81f9e606a0cbf94a624f30649adacf1c742fafc048c25c8e1c08543b105bd7f576b6d38834673d34abd69007f76c311c4e902bf80464f914ae5acea4a4fdf20c1a67a41e6153e3c87ce422ad85c3595663c108bbac3323f791d275387bcfab21666c357078436d9a9f9ed2208320f0cb6322c32125ea1673579a3eaa76a8f0e726f9bf2c0bea7abf25aeef491fe22e674ecbce9a7831453771a1614cb67187d419a1ecbf8ffb5c108b085d29867d7e9846d4eb6653d4d99dffe46352533ea85e793fd39386e312ce8fb5cfda468903db37e52c3f3281d889acd0f2df8afcd19bc29b4d8aec5f156941432d679804700c38d9c07941fd17d5b43139f192effe95247eba61647b77caac4fa21584ef124b0bc24dbcac1adb18bbbe264dabaf339650dfd09f5c0f647915c15be98b165a8007e345274b0ae5a72883141e56722762750415c47d6f36ce0b0da24ceb7274ca6e1be5ce260322b2a698ba737bf7050fbebabf344a09f1cbdce722e7f566af567dccb030fcd51c9f667ce744f900b22ff84fbd3e87dac73e0035517eb2668a14726758cbd9710c944b9d51502442be1db150591dbe1b078e3d7dc020030b32feeb27c281600c928b3a36fb04ed2d077d4dab56e97e9c2707a14553b158d1692640ee8345c3fb2fea77d25e4a10ff8c2cf10df133e888d6e9f23f14d0aaa2bb7731cd578bc5bf541232736022dd9d0c7530b13059465556cdd0e5a4fcbf2bb92021e21ce9f4da86dc3a93f3eaca39dec791e11a7e6ca4f37594f36b1619221e90b7df84ddc509b920a610b61095788677c95f09b4867f988e7e3b10405f19ec6f21aa120c26cba98f05faa090a50d14288d5c6542fedc914db46f093dd0df6c61bdcfc5dea94ec72e647af7951e8e884701024296417074d71daf1f269bb7f6f077f76a3bd82f272c6849719cf20ef8b0bf9bbf56f7ce92ca2c17f0a217082387668bcfb0cf272312155d8a28a45d3fc9edd1e733fdfe1168a96657cce44b4f891be357f75dc98f826b4d4590925efaac589ded1edde8a25c4021928b612cbfd7c1bf46a7ac8f8813818112eee90017cd8a2d46436b6ff39e169a8869f69773f653ecc2032f57f0725745c9b986ee452a7f064284628f32599d3a88c429bd34edd4a489b5334f1f4dcfea84badfdf5311ed0cfb48d2bf4a9535b3c4a5b648e8085896783845bcd0f53bb3eee625082e383883a4d0ea9b3912c088eebde91def124e4cc76f808787156a62fd041cccd82c79c2d1ba2401661d22787125542e19655782b635b1a6f99cf2503d7f5fda5ea6e2a73b6bd5d291fd5e916418aa11fa78c2c68cb6036805224e562df0ac5ea32d3972450f5bddde9f68b7087328f846fb8b6f81b2ffc41fa3e1ff6e3e52a2ba4fc56eb8904b832a4dd51a31799e4aed56acde29f080e65f6388dafc04e0929ef018234fe9183c41b0d41694070ee547e18e77758a574b98518e1b5fc037d11a307ffc2af06c03dd84d68cafd7129c1dc855b0d829fc163d15bfa82fb13a929e3223e52b0a8b46e29e0a57618064d62d5bb301258ff190132728490fbb4aa5a45e741eead75352ad2bce7dcce531e20b9e5cd2c3390f1d2517139fff5d6b422e383ce9457571c8a1732fc160e02aa97816230adae105acac57345bb50959dc3f1635194c467068c4c05d4aee649594727181f8d8492e73862f5cd477f5e1823532ea548c301e85d372a72c968620546494e902e25d7922ba2029b7177cb8296b58e8dfcd16662e0d878c5b33c769df460822a6dc6e9a63612c2d439aee9bcb48bd332f99d1df7314a01f25e35cde0d85e89150e2039b0ad69fda9642c5095cc09f7d8ed98bd01caaac4bdbdb18e4c8deb0ad1b632198053546057cacdeee19b503e455d424c111852c65b1cd87dc726b9c5e6218ce5340d446150fd3e2317479120f978ee9bf60420033c040dc3d667e92885c6ddf33763c4dd15164c7803158194ce2194e88e94be3548225ddc1bf29625cc0eaf498d97ea822cf1dd7e31e67ba4d51a94dc327d85c4cfbf17340b8d99b73248d3c3cdc82442b338271a0fa01897b4489cc5a2defc5b8bcc00842c9cb252a60a03588e1294016b3da55fe866df3509cc94dccf0a410ce99e40afa883971bef29944c619217975196ac4a78166343d899bc0e76f87842695320178999c0ebd8a241f39fefafc583f2255973ce4c27776c36dd1bd7d78d1105686950561e9dca5d172ef96908c6ad7ec6f563fe314eead35309ccc54f983a0b06aa39f68ff588a982c3e1b23b03d4a7732a8324e2ee50ff43f98b3158373e9b470794c9b9bc1704340c05ed490fd03050e0a1f91a2d4831d4db04ae16db5b0117f6e5d81a463f52230ff8bf52e6797281089b4015a381fcdf019783c291b55965fee711f1437089a28d338722e070e8e3e71909b1196ace829e45fad7365dd8270f4d4e6a327725a1635b33b8c4a6734da08322cd7397cc6749342883c7ff136f0932286e41a5120fb12c2d1d18b23500c608bda0029e41bc0d6a038452a12d34641c28c6d42193ba12bebea464eb1fc5a00ba6df921f415ada821f2c65d87a254dda1f4a0fb3c9448e3e8fc4fa2e8f39ad8d20a91f7bb65f7c4ef62417feb761913fd68860b20978da8e4133a3ceb95e2388ad1a5bd10388a795d9ae8ed3cb979763f42b912a411cbaf22842607bd27417fb7a200e9f61133d1b4402183267f99bc0369f42580d9e90819d762e5d5b5ad0182cc1acd4f243f5fa1de20bcb921d5845667c453ad3649be0316e54d17adc98bea1bd4fc46a303c2ad18d1a81f721512be64fbadd6ae367c52d6c91f663cbc41ff79aef786ca552ba820e01280617a83059db9430f2cf0e9550dbc890f0af1309cead5e0f209b0a329b5f315a0c8df0408780d40f1c876dd6d7d973ad67818a0734da102ac34aea624e3bd6a54815e895cfe6f32a9f460d07b186c59cbe7b0ffa7b91aad31ba977733b4f2fc3bdb758fcb9c31eda20f69c346368bf1713100dad1932d0cbea5cd8ec141e68b5dd5e8c5d1205eb830dc4a78fabc0b80ab1698ce5fbc7e96abbb2a629b74db6488ff4559050c79b54da564e5305e21b10f799cf54b5e347b432ac60021ba63e53d0ada9e384dc2cac71afc203e2f180773c5a510030765143a7319a2bcd86513238227c10ab23ad11fa8f5628e27a555d548309017f61a685ba17ea7f5add012607a1e9ba3ce8f4999a6f12526628f7ae8ce0e709867e7c550d026ba9728a9d71a31cc110cc202d9dd4a61f091cb7852730fcb24b491e079198a4913b26c2b0f32deba055cbaba8b184b8f4f5a90900e8942e464ae1e00b52ac29f3e40b7704d9d074ed8dc368e08d46954a53ce64e0f3d99daeecf4af88acbfccb9a7441958494d18cd0e3f3f2bb704815b1c595937852a7a26c058665297d195f41b1cf3fa17a3e7f66213741450116e043025b6a56c1fb837c641b00cfc259b17f47b367be1fb6949582a0a0f8631f2bb1fa63f3b6f089b3fae8acf289451ec8bc2f7c832b5c29e415efe905d3aa3527b5761b4473b480e00e244d990100e3e88632491ee5d7bbdfcd69adc481a15098e42cf36694f5efe0882b005c5a41903f0f7f905296c1d3787e00282f164c2aaab966a788770263f095adfb748fc416d63043e45fe3aebf159352364de4c8e6275039b1d6a2e124280a03f5e6d26529d855f7aec10f8ce40afa49054606028dc465c9af853b3145dd61d875a0958a521a18a55f6d286e60d160b83f2cdc27b4235e650714ad9c7d00e0c347e8071a1927b880596e745354b28e82ffacfffc3a59a006e236586cbc74f1fe1d3cbece560e7ed8fd1fbf98f0313d3c1c0d9d34115e38d07e831a8f87f01b695cfa39ec8c3ca4908de6abad71b9e3b9c9c24e05358589708d413934ab1bac654ed9e05c96f3ee89b8070c022d6d61a6f7c55592e8c7565323c1bb0fc3e30781b485090d0757decc9ac2b3d09a1894aa213b74d18b588cf2f935528e8053a94f940fed7a03042a109eb702513fd9b57d467c1a27f27d9586d09c337e34fff214cd8cdc6449dc220d581ae25e2990d8e407a66026338b64a1842d57c292d466e305ecbb87580584136b28b3b8b95f573e723498e2a4b4313e0fc3754fc9b31bdbe7e1237f30aba8b6c72b66f3cd4bcbe2e01ed602c88bf20da00410fee28c37def869f5d06563cb232bd8d245f06db57deed11d1f59849f6b2ec8cb075e4b9c76c48b8ed47a1818884d306f2734aa8389c5cd28fa0dde97c13cec3c18cfad30cccb11ed9e213c79b95124ff1297b308cde9ab4ca097a0fad72e480b95d06784e572752ec1d31e0297326dad5725a8c0de32ccb4ba5d6f2af6f0e0aad26e0df52a25eb01b054c8e62e68081bb606633821f73d84c2d02df1dbbe98a22d453113448873f34e875d329e502a743acdd0e6f3f492f5874dab25d3c9a13b9237ffcaafbed1e071443c3b9eaee983586663964538018b2e12053a147584fb5fdbf5e51bea540c912f75cc332acb2884c1a3cd192b5026abe19e01724d8c9429b6450c14f4dbe59556c33d4e324345c14af86cf84c17bc26d1971752687f1f535065cdca38b7ca3a9b9ff6986b4f4335f0d1bdcaf6bba96482bb56384b727f5ea82b01ab96f86ce556012c03f06e4fac0e551a417dd43e9488869d55929df97404c29da98ac4c1d4952b0b25294a3ad89faad5438026399620ce36103497885a595b3bd4a0d526a04480dddcb233f07464ccd0b1e362a01365516deecde5ee9e28b329e2179504515b1930b6107a437a3d65a866f206708c879d88682c74b673f2f1dd99d24908c68604ffd81056a82a2b315b8abdc828a735b3e5a6e3dfe66e1917a628017a0f2856fe3002574b3c5d46d5f5b48a971a9063f1ae592d4f456f7411544c103d578b3d82be3a40c90fabead4c326b2e359ef86d07bcb7fa31ffa43b6bc96dc750aa05c4e7b3b4a4448457d806f9c8df1a5b0f53833425ae384e48bf565ebb45d119414456350dc8bf033dcc31663b2e0fdf523d8406bafa2010b2002b82823f15af74a3ad7c4c3e87fa4e03b63ba54465d5af2ee74988cbb32ee29365ff2083e21d85da37efc84024e6b51b541f8103a3f4eab92172f527d12360c74691acd3e77f1106849b51ff7f19a932900b531201875b719acc448b6b792741020ab055262581e78213a028d13c3d901123aa2c8ab098271374096ce0cd3ce108ee55f5e145c1f2a60157851359b7e5308ef576f08e3e9227233e868220ccfdf6ede96b6344f3541a141d1044791d8f08c30773a50df496cd3c4ef93e014870475091b88e36373e984e89994c5641430ba57bd7553582bc42ab21b88e179354b733c2e6ccac454b283ef2f350e02b0cd887fcb8bd9e8cdc70ccbe8024598fc03ead9a6019e866a41590211ea6a1efedbb79433e6020d83d6f690624e7ed401d6e90384a1e85d993cea9f83c199e16e462be0f383150186cb789dbe08f7999985d028b0b328708fb49bac9309f99266a1977b450abf80e5e59990f0690ebe02916cb5b2817982e40bb403afa0cba3a58f81ec7827f61484fdcf27d1169e597f2252c2188ddb984c8f4f0e8514f1fd90aff02e878401efe45b92dfb6ad4a317454776067fba0ed215a01e9238790280f33bc782272746de898dcdd85436a30814318ea9410618f35cc4877c7cad245548eee207d8225919594a024b8afba397e6d689bf4c15e87501e923e810d234799b415bc869a13fe4b1fa4deb99234ac7f561894783a90e447654df6faf1d98a9877f61a148aa1b2ba37d2624babcc86cd63497393681e70c67bcc959407ccc0600dc1f20f8770953a4f87a539206941ca30f24cb7e9d6062f8ebac08d840688818dc5ca9f98054a25af61310817e05765198eeb76e7c86487bca056f582fd1a39671f70657f1f59ba5622000e5443f6a3c442eb4315b6787451321f0e5efca5d19bdf9d0897b2e71d4c2a7095c2a13bd7591e1f55e7614d9deda69170ed7d1111f2c6216d41e8ed050bee96c7cfdac1318086c2915b955bec2821441269a2ed0cb5b114a82a416a5e61e2a3aac6a16af8a4854c242cde70c27ac553f4e939288e5e85c7a1ff319e5ad040750a69ecd917cc87418fea409359f757f46a28160fa2b457e99fdfdfa8ea7070adfac3eb5edd733c55e929ac8e364470da2ce336b8db6f55f3276d9da172c8a1f397bf59640307c2679060b7a183a6c1f4f1a5ecd19bc74c82cb7883ac9baad7a4c11cae903186766c33632bd780681f34bc6991c0233310b9e8ef70f6a946bd1186ad330b1668bebdd19cf5861fb06f4078f5fdc8df7d4893f208ed8be7a0050a5fd52f76337581ad61b9a967105e5cea8336efb52cdb3127e0d3152d4d1df52a94cf12a23076da06c5f63c9e725a5c23fdbced24fc4c8af680a56196dac59d8f2095124c0ee825b54ffc3348b5587e075a6fd6aad72b19a40aee821deb17be888fb4c0b3eedc467dc76481e01a506422bc1f2d977a98160923d3a75300934132e2184b80ba5d93b93fa5fac96db9eb4e43057bed1654203c2f3f2baf095fd335e970772e52e69418d363974d82829674ba0a3c064fa407695fcf82f1e691b6297df41d5312b8e67adbf4b2404e9b66e747cd3aa25abb9243111ee1f82fe560dbb4bbce29d9689f5f6da7e59f96dc046a7f7d85633b7210e684a767bfadc5ccd200f5649d0985229296971d5acad8fffdc2c1afe5b8627a8437e16f754dcda395d1f49ab7189988e3bd9fd26618659a50ac43b1bae60fc304e0c02ae1868cf7834bdaee9a5c7fd0cc6096eea06d17e85ec997dab35352eb77af98236451ca72018a5aab42ae11b02fe99ed0bab03222c82022e1bc0b2b0267f7572e3f8c4f6038e82642b023feafb046d7fd3c116be8fd7da587881c0ffc5ee8796613ddf4f82df91398587a629961ffd65da28963ed2eb25fe33962bb4b86b97849f434d415a21b473e6f0c9257ece9bd99ccc9cbc2c04da63a093a1cd6840617407ee5ad5a84dd4296d09ed2c52ac376e4de4303a4e3bc4aed34aa1aab836386f7a6658c3494b9f2dced9d9a96186b89c889d20a985266bb8aeed13de92a03e903cf33d54597003e49fb427172f988ce4aefff334f84f213eb76a2ca398b8c1135fa2d70f74f5e52a695f9d974c3dd69f8c473f5094dad7fc92e092cb212374182639e71cf99ad2fd4c9b97dacfe3e367ac6c5438de6169c7cd4dec5551bfb1e06be18cfa45b146b08bd3f47a6f105e54496f5301cc358aebb03d6ea5f7919a293cc41fa6c4305a75379eeee9080c2b402cfdeded5802e1c7d0e3dae484279350e1fadb6206e12b8c2be0041666ccfa3a927bc0267371cc849cacefc73b5ad89295708113ec15cf83efc0e76ee14d252ce68b2072b25b8814c2533c9b387e344dbaa3aed98fa6e886061ba3afaa42c566ea84e61d7ee0a9e1c3dc5eb74a5d818ac0356675715d48b9492ea2f7f18be5317227d7e0bd8163eb2cd7cf018379712ba3e23d47afb61c270c7c4cd25321da3669df270526dcff1a489fe1db9f313f8abf0e3dec5e7ebf5b01693499b011fa95eee93e3b471506597d4f4ff37e0d2bc9f1dcfe9b259baacc350f4d6ed4bf7b891f6d5500441bb3aa3a9fe7c8ba49faece7be1489c463664797ce8e279a4464162c22706888345828fe810774e4f6b38885810a1146e84e845bdac08a3eb5f35fb9916c73b0b1ef4e3e4f4948d3272169977558048fd795f11c6519b8b63e2d05d354d24fed29f7be3fe302cda5e24c7a52ea7d27b4f7988375d48b66d4dd0fa5da3efc27b562ac95d895ce51ffa7715566af1cd69f7336572ba4a7c5280a15f3373635aae65f5201b6b676c86dc124d85f164dc759da2d5fa4efbbbc5dd1c4f91413854a15876e3f748d2715f98068581421b25f8ae29ad78c69c4416895bdca8c46e03aca97be88e2ae480449c2640df963708162b07b6200297c07c29f051de67d817d3988db1a879ac6167ddadc78b4e61b831095e4e309a7082784d6afff3ea16ae3f35cfd7ff1288067ebfcb284b4f07c61e6feb52750c79a069a27398f4759838e6117290eb37fdce596cce00814bdf6fbdcce3af8466de3fb9da66b9d2cf9e09bc2983c0071f28ce3c67513f8ce561ac395ae40a1d7740c6545c667f94ab2301b361dd6925445b4c558d02a2b7503d6b185a5026001f7ea6fcde53a88e6f9be701b5280b5fa57a98abbc6578cbbbbfe9183ffa71d3de546698ff0a3dd8c85e19de7ec210443d5e84c0f774fb699c25ee8c20210c51c28f001037289f6d27f312f5f45fea8006b110f44c100fed60034a299e4b9b35f216d905418041a35b26140e196fd72a06c77f61cfce704164598b54f27b9959723aba4b8b2205a086141877b223f1cdbaf25f172565ff9b7cf077cad9163b10a1b68809ffa3fde074fe796546bdd39fd8138c5c8ebb503d69595cd968995113450f87beac4c6fe2b66251029a43adc74ccb5d5fe21993315e3434ed13f2d8e81ed7dd60e76fe52f347feb704f036470c720c28b965fc656808c2393c0b6a1b517977d54d17dadf34befb4fa25ea6c7cc0a12a4cc00aae7a174b443e483ea686f414049286f627a3aca5589e935fee235c3518f8f0c7c20d2319e8667dabdac051178d4a4cbfe77ae67cd2ab45aee7dd200ea90920efd30717f8bc367b01ffaa0a6bd3c0d8b8bf8550dd81ecd80ab3d1f4a248d2a527386223317471fca78cf6b619ce7e36f052882ec1dcd68b212727de8dbf39bc9c3ed0226a910bbc6ffe5097b28a229220a6382841d653707fbe69e03f6bbf280dc353d887b88a093c403629fbebf695022c720887bfa65772233ba09f782c8942a90cb1dfbd635c59390297111e4e646334a669c5e573f2d1ea120eceadc5da012b584acd9237927a702ea2f97d172d4f8fd0f900e6a1874800bed1ddfd771d644e99ee54c58c5e32b500b8787e6fde59eca28b8c114d14f834c74a1f8546fc3d3bc745ab9b423b369353d0b73a2b9c9c1ba3839b3b91a557fa7173fd8ee029698c59f0de3bfdd4fe0f70714aad4d743b6657f1ef7fe7f1e794d2b33d068a89a04d8051c858f90b14f647c5b1214e25ee8b68ebc1ca8edb6e386dd18ce9b45fae10e1111317114aebf4051acc57b53fb8812c68a8d00deb63f130f05896aaa38bbcf6f563aae9004a3eacf21874f74a4120aee8380ad2e459c28a3ada04ca520391d832e2d83e5de943b110679f3a096407dd3de0ddf802b0d98d3c1d82ddfbff4e7f3dfaf23a888e3b917ec717028395ce7372a8f05382201ba57d1fdc37126be791e2f22feb0777d4663a940561a2c457d575fa57cd5596c99abb3825d187dcc7e42cce4e7f2f644b6636cefb479f7f9012b7f0622ce5fc033c7fe846f076808a11918afb3e4355d223abb820c741f03375303b94613a16f926beb660ff832f46db3677982ac8967af136b65a8f5dad46a506485e3cf24ba69791961704161eed3413c376975940a190d2152e5b7f3cc7660bd0667adb8063a90d72da36ea8017647547bf07ae857f5b36d55e23656493ce74b4059a409a71b054a8bed97acc3966334cac1f98706240204f14b433e3d261a7b1d08a6d73309bc9fae4a3451991395ba6e565816f8ce4e752c7072d440a41fe121295398de1760865b178e10515e3cc3608139eb711df2fc9543a2287791342a4ee66ce97d4967d1b200f549fdb3a43479195541896cb4bf5aee709e8116c93d40eb0ff3b53519bdd9818bdecf69e7ce0013a4115715ea341b746a4c0260d3faedca6b0b89a3a55c7080054764d4e7147c5193e900b7400735e040c24e0f2023266b9f33d4d9d4a037b742a07ed223ff4640d512125c2484caf71e5439097ec2555377a3780e788f266d9058c33919017843aec43710e98f204fceafa42f6c1d528df1e2ce2084958060c6f078a4db164b9a4eb7c3e2ca2bb35ffdf49b0df7740f21f0c80e5ec59367c2d524524d09f32529eb4d3af8dc0f08e65b2277c3f2fabe23f3d347d8e3598a110d1c66860b2147e1e851a07270c4b052460c32cf4a3381f13e79b39ffe91962da126af94c9bff80196f0fef0a525eb4ec9f2b63b76bc2ebd3df1e4a3467fc88c12cdb62a24ff01b80eed8303564a02463c0989b52f669f70c37045823d470b8602fad1a40681e7b23effc6241df80b61be2369bf1580f260a414b5c87343b26e8c16fd510187904bf8fad3f5b1bc2ce905416e0aee0f64988f6269250882cf9dc832ef83b4cd21e035f7ed89c11d351c9a2971f5517c515e88eefa5aa76d65b90c5752550bf9bdfd0e3ba634129c3c5af487f11fcd480d82db932caf0fe43defa070a5a4a099ee2ee033054002cf5a4cfa7c97ee8eb9afddfc0dcbefe3a8f56a9ca3027669060524e79a6ba13c3eedc87cfb051346b8146fda85329ff6765a983fe848ddeae3048aa8911f322580e5a7dc36fec224d47f15a324e43fd4a091c691c28f79828c20461c14040ad48a9059305816a503f2460d49daa6f7c11696a91b2ace1fdec5ca0e4acd0c517debf1827a4a560c79d233ee01188823eb83f6e31fbf2120e805054e012fb5b9acd453c1ec3e0a9eb42c2fd3a71e37b3d6882464efbdb7dc524a99520a8b082f0924096ce3f2fc31b04de9b7ff0e70f90bc03f7adcd45c8699a91835a697edb1593aed47abe99f085a208c03472317d2f3921b3f15395a4d0f84505115284d8458b2fabc6b6a22c4922ba4be4b57eada51f1539ef76f32d5486986d5ee5372499fe3ee84d25a4da67fcf4947619af8a4e824eb2d7f01763cc4ee6b14dcf859eb8b17b232277cd1aca2254a04ab387b6444a21b185525ba0437c227c2aa91c58151d3464ebb1fb7286732a83097871899263e864512f5e3160fdd4d24953f3b69647b0bb0ca83a898c528dcf848fa870f3407c6f1c18403e3c88f5f01b6a11fff48ffd070ae8f6f3da83a0263e81f9aa5ed237299fdb88b25da48025e82bdf8f448fc4863afe0466ec59f338051a7d5712fc608551bd3f931c79d71fef5eccd39d342340c596760589504a2f8474ffb4892823dda477c2c7ef4e6523c299aeaab3577592d05553d3c444444695c807dc48f11034c13ffe32624619a780315d5488f7bf157c840cd3ec5d33f7ada477c9389bbe809e34699e44d5a4e44a41425da673dd9fb50b9381ecdcacfdeb9202de9921d4c7630e91d4baabfe60e37e8313a6c814629c76fe6523691e88d3e3f66dac7052ea55fdac1a4b27b2ef6f3b9f4b5b8757d7dc7924a5feba6ef14c228e3c31bede773c568dd7a2f97872e9699a664bf24977eff88c1ab24fbd5113733837b300693bbf7fd70778037da1c6d54fe90cc5c223272965984069fe5535aab49723ef25316f56b2b577e3de54a2b526c2f91d85bfaeda5cbcbef2aa5ce887bf2b7aea7bb0148922c27b9674e015c6c270de49efc1b3ba8d98fbe81b6e81e67d93dadc76db8bff131ff5118d5cb5fcc46476d2fb76deb71373bbfeb46b40d712eb9d0d8f60ff9b1c42a0a51f1a9cdbb17fbfd42ec42edc267c906e055fff0cbb4bb18e5d1f81ee338d3f4e7a1d806e2a061fe75d94fba8feb2fdb3ea4c52105a5683cc331fe57af9cec45469960a49561e662988d8e9a636818e39c6aa8cf3d03483ffafed6ba5652259d6a7079ee8bc0fce874aac1e5b9e781f9d1f3fdb970dbece95443e9b9e789f9d1dbc02b2641949efb20627ed45c90530da577f922257bdabe74d28ac4d8d3668b909494eabf3c4ffd17eb43457b1f97afaf591e5289b91d262ed6870a8fcb97de06ae2fcf3e80eb5dbef4fe3563b8d8afdf6342e8676fe4eb217e3d3414961196e632073d7a7818bd26f486a2853806b7213dbfce0c6e537a664e7d8c35f22fcf8d9cd47594daf5144a6bcdf8923aee8ede2c7bbea2b615292addec575df8650d5cc76dfae9477b3ad5b03df745467f03b3ff6e341eb7f93ed3b4ec6f80c0cfe93182047e0faeba2114037c1d0dcb201c753f2bee7f5640dc8395079eed39bbd9c02b1db8d91c1f007c5e610e2d1595dda3d67fba64cca5131a1fa065646ea497c43ecbaeebea2e4a4d1ea40db1c3184e5b2adbf90c2fef27949ea394de5c8a7d89d48d3a4dcbb22cf34dcbbe3b2dc3fec22c4b8f269899f9e740154a6b894f4eea4e3e7a9ed1e85fac0fcc8fdee7e549ffb3d26a77f25277727f97fa31d607e6ebfbbc7ce98847edec8e1849a24e08388126a1ee94591e785e1ee679b081573cd8007f87ec6de095cbc3581f9f9f950e1456cd66f918cb2b98b7a71d983f65d6065ec1d8d4cac57ed7e7c08924759dbe5f3ebdd4f1aa76bc2219711b6ae3cf41c85e45738a452a85d16b4af786c20a2b806c5a86d16b426f247c8c1bcbc8fc78771037373032333d6a6a328c5e53baf715db651b8625faa70ca334cba80ccc81af33037c1ef833f0bd0ca3b445c506ee132efccdaf0c5719d79ff20cd72c62422b92a71d86fea477f9d38ecb9f6ce855e94f455625fb3fab51fd6a3fd2e8e75e2fb3a4d27f7e8c310c7b86b0ca0863f04415517c981bcb9847be6919f642ae2d5bb6b461c31f39055c9ba9d248230e3448c37d1b3532a1c243df1d601bac2bf935eea8fd66bebfac0bed3c11f981757ba0173e7f462e7cc242725e14cb8000c9b0ebc7bdc1000244757343afd93df03c39af99197a4d197fdc3b8ad643d492c633d767d9c32c756fe0f82ccb328f8bd13c26bdc6dce8bb23d92fb5731996f83f6ff4fdf57af8a774a07f8fc949cdcda8f99db94392bab687a8eb2b0ba1761bb98ca2d55c9e7fa9a82a7bff1e32c398dd8b3fc81fe87dc557493f320e7c5a03d9356b3e1b172e818728855dfd7ab8d1e7c69883da3f82bf74bbaedf5ca575596cfb23a8d35205f81a1ae754524ba7bb41a51fd4b1419dc1a57db40f7f26ea217aa21d9d19dca6a18ef6e1ef704bce0d4ced238afafdf7a0dec0f3c1ee3bbb8da8c76d3ec67283c9543dc6de41c9ad37c4a836fe31b84d9b4c2c1d45d4af9a58cbb2c630da5996cdcf4e32649f39d76599f6cc651af69d61df18f69de6f33dc1e73bbf7f7e6f9bd6652719605b1ffc9da35b859164c4fe90c48e59ea4efba56e64c991be1b6354fc517c2ec6974c13396b993ac22b47fa909b3b8e2285a8332ea4d18778e10ffe23bd8928d643ac228a262e57a106d1355dae420da1abcd8d8aad52b799736a327e47db9f034f50a532cb94aa3019a6653d1cd146a4116544ac1e5f613a355373a660ed69222d510888a2101951374ac9d0b40c4208bfd4d58e348241240bf58854fffa7e97eeeb9503b9209ac6715cf771cc6d9ab669ad75d3ab75f7f5fb302f0e3ecc8bc3f51a65ce24e3dee6d20b8eefdb2503b45f920b3fd2adc298e6d53db7e95cbfd60da7c5410608c60584fa994c8f6ddd551847f56bf412aa9af6d95fdda7b19643ea6acfdca5659ed1ab65dd977d0ea98b437c987539a4e86557517825a8661442088f5c11218cdf18c42086c53859e8daaea750c5fdf526a822fd75913a18ca6d5e8d4bd858857d9c5807e328f818db4a837a59ca2b2c5169d462a4f3e1351f9e88f80cfca2664f214c72a1fde80ce0d7f06f866118b6759122b9f39b5252f4895009245ae7cba9e5f2155f984194032713b58c101a11bdce06f5bbb9fe566aaabbbb7b8ada2374990bf2566ebc1ed7ad855951112c9229698b8c7c84ddd9a0fa5ba9fe1f17b5cdf7503c0f591f640955a1d5242474bf2b0b64614ea06ab2202a42b9110b9a72b5645e7c980b6efcefcab2a36981a80883fa4d4dcb8d3f599355194ed68dbf234ac52edfc089d01d627141f108dd3ca8a87ff9d2c0f7cfcb57d608e2c6e7314585cf438a28acd13dd078e693555a1746eea018d48f85fa892a04b1b4206c412bb55fc0838aca97af44410dac9abcd48dac0285a75c58c19310b0d0a47a603e6149994058e124f017ac2c454d60c284279a587914ae7f8de222faa9824514222d2d289cb8c2440635c0019117b3ce14c040e3410aaa002209fa53646967a235c512ba5e6e4d31c6c52eb7a638632ee1964401cae2074638e2889567e00b368a5a555650831c1011456bb68c52469413aabcdc12a2410b51849c702688105b422a9861e880460f5cc084222ba2285d6e0941112a020241ce979fb4d5e553f9358b269c70a5fc198f16492a09130d88e81a0c31c485156674b9e58328acc009723425891478a1c5d009b24c2088b450e2074b10f14bb2c052ad27435c78b9f5a404330373085f620330890c942e7c53ca035888f0f324063e40620c278e085263b25670834b0bf9de9c5860190209126230c61a5b8c20011663e0e00acb77b0022e8eb82e27818b9607d0b8dae5561223b8dfa7a4179ab713ef543a33ec2469263ced7ce3369f0a08dc72bf4dc53852de7a250a625c1cb77a97ddee76e8eb4c033f36a1ea406666da1d8bb40bcb5ef38dcab95ddedc15aa5cb4bf6ee6d2c138ea9ad736394d9bdc77525cdea53341d4f59adc3431aa76dd8cb445bddc26fbeb395bddbbae87a155e15ff1da6a37ca344d7bcda52bfda8ab245bca3ea3ddd7d787783f1dae666d0ff1cacf58e8d22b73c9a28114d757dcc69d57d362db42125d8fb7ba0d13d10008d7b3d0d80b26975b5d10e072ab0b2b3709f44117f785cb2d1f44b9def6d98cc63b2e8344bd5e931886595ae5c605e1614acba8f32737445eec35312a256dc184228e0b82618df110ef11243dbc9ef934db360c8b379bd0b351f6b48cfad55c49ad8431044693fbe3728b4807d7c3b2e36661e53211164358586935717d5c6ef580755b5f64b9dcd222a8a585932bc513222b54b4b0b4a6bc6cda18350ca1fbc2e556184eee4d182c30b4c46ced3736a216308a89a2a0445554a22b626eefb8deed002304444caec96db8d583347a6006902cba3352ccffa2890b80cbad2f987891c665e172cb0b2d2ebb4d0a5b771fc52e04eec428655a7344cd6e6efa0271ef86c76fb0ed65b820482ec65650d3e5bacb7a7c3b172b3d89e3b867ae318c7bf7b8cff127759fcc2565309051b1efdf3ccfbd1e34602f93511257847d003e54faa998d83d17b82018666a1c9c69078b5aff8b97f437489f034725faa4cced4d7f4f8f7b2cc3dd998c2646cd300c4686de7284edb84d66638744cd7ec4da9e3ef6c17041280b580f0cc7eef180e168c85e875b3d381a6e68b820a4ef3b391ab2b7372f7041483fbb9fcb6e43b296723464efbd4cfb7976db60ed788897c3be2dcf65a6c1be2fb533f0509da7dcc51e062c286b815ddc90ecdb520cc37e623c37dc10eceed434e99e59f36c2b526e3c2da37e3757cec76c534a7fa326f784f808a1769895916f4fa0d27bfd8672f98bcb2d2eaab804b8182764be96795e4d0dd6a3e6eb713d0ec37e731b18c69118751bd2f76f15a62717a473903ad2b7979e392f35fd18a71f439b922aa97e8d89e962ca9db9dce24204976296ed377aca29a0ca50a737eb4edcf315261405d1ec4906ece963f6c4bd3d8d9eef0e90f41a471fa3db69f4a3cf3a24a30eb39799863ed67d29ecfb3199d49d76c6bd1bdde584c56245b1cababe64308acda069d42dd63031f9268ce2abc3c51346f1fd523a5c848069f83601aed61aa715f5b071d7a02f5fab8fd9cfbb18d0165d5c9acbad212d9752a1e532110d9e98d6b032a58563ebcea75926c72c05d2217194fcee8eb8279f0764c02b81189151b8f275a070bfed7e3c04733f1ef28e6ef176908cfe00714fbee7d5d4f4a87949d57817e769cdcdefb6c0b443e2cdecc634da0f89b7ed28fbfeaf9b1bb245f7346d76231b8d44b20f8485961c0dfdec5e4c74d3cc8ccc0ccccf984c2d34c60d696cbbb98921062031b8e4c47013b99b1e82ddccc29045fd642ec66dabca588692b80db786547025bf9becf4e830ce842ac85e3ee16adaa6a3c3c3f333f04851fb4bcfdc901dcbe39eff484ec7bdc805e9b1359765432c3704dee6283b8e4c4c83bd10ee694cf7c120e9993d86eddcfee6b9d9f76b5927432404e532d11466dc7a99688a1f5cc62244e6621b0c523037a4da9feb0f611827db8128f9dd3b5095044ec93458866118cfed1bae7342faa74c872475373bd37ddb4bf7f5f880dcbc97bac9d1d951f18ab254cc758ee55e8d769ecb9739a2cadcdeb62d320efb371df9b8a75ddb75514a63865858a8c609d13ef3ba947bf27574a0b6b4a9aede6064324ee9fd4d2c73b7d7816930f5291629a5305968a42f94d2178a75497cba6b56a963a6d9dc2ba27e48646ea5dab6e1c802d4dcce713a9c2c09e2b849c905e976db4cdc93929503ca57596e531b4afb111d90b88d3b71c20549c283ea0f01b7d97a6e30e2dbb68dbe63b8197e6ee7b8cde8e5c7e03693dbb01386b59452a68a6c6580e8ca211fb7e116104eae7c20a8dcaf9bf8fac9958fb5c7dbf2c1930bc3e5561845578b1c91fad4a9b3cbb847b226a6c9b2ecb70e89877141bacb76ae7fc605f1a75dbc3cd63b24a9cb29ba318e0fc72a258c922fdb7e321c1723ff8b49e29efcce89ea2f547207cc40cb1642b707781b0890107045a5b2986480b52a314e015a6700310d544df93e3ff0184e3061ade2fbfc302d15f83e3fec006a22c54a5a2afe3f9c90e0240aab68a9f4ff00bba7bbbbbb9bbb25d3305b33d0284263e85ebe8246153097afa041e52a216a30714a64326e702ff6304dfc1e8269e23ccd06729b18372b56b86a004f3aca57a144acb287b8f163931bb5dc6fbb3d04e3cc797ba02ad54050c580f9f282ed610c685517d1a8f8b451f1dbfbacd5a1c3e57e0de4d240d9fd98a853b8299864ab6a1c151f4866858b8a1df5e9b86dc3eed9fbd11437451d5a82151d2c97c8029b3aba74a3971df71adc5c84b809aabc99fe3143fb881fdd541fa6bc51c9ce10493656fbf9e5712fba1ced304d7c6e8afaf2a4d7a24296444a4d6a924f44462f474f6464a52881f997973d2fef43455a986abfd14bfb1d692ec8e8e50b67bf929c82c0ca932f6af6f1055a7c0d08e3909e5f875f98e8729c139e7bf0fb53504260c6174330e2076aace07f12f5a331a298c62f147d9900d195f642dcaca85ccfa50845bdfea3345c598681a88078afeb3dcb32bfae8797e501cbb21f825c9d0f7e31c62c8c9b7b1d8ca3b668cd6235fb88b3df79c8fc877b79f7d34b86a88078331ffc660c337b2f8e645cc805d9a1b708f99f7b7549ee9675356754aa79ffdcee7a80dde44b72e35f5434fefe1d30a8dcddcfedab76221dff41a02a56b447f85b33619c7e93cc9bd6e034cc30fda076c15ad498f821c0aa9fe123ee39048c98a0f6efc06afc68e0be76b5fbe8b23a308d3c03890b8d647152effc9ca2a09f73e67c966b8822212834212673ce391909ac1b4ca9c22ac210583810ac882d253cb04407a49452b2e042621221a594920821823099fc1586c604a65d6e2da16289152d084153de4c1ce932839bab73d30e4302872e5fc07ae372134b5b7ad069b55a2d1aa79cf26504f5e316141d776fb54c5e8eec3c881a39a1b5d3cbe436476f416186f0d84eafe8b4af1ea259d0dec1851f04a1402743bd03cd6196805e58e1f5c743bd1399a81f0f0545c9d181aaeb31acbbb9baa11eeaa1dee954efd01d541fdf990981cc936ec26468c883a8388b70b0019310a310810842601d6c00160d018628832068af92f04fd22ff767ff588beb115a71dd9db280b852807e35ad8ca3dcb4c5c4ee43d0428b085fe8dbbf75ed4c4878c1002ffc0a9a8fb84b957697339c5e466e1eb033840bffc3b94270e749c5d2156239c2de11a8a233720eaa2a1ed94280050b96200e64a1290e409287c1e9bb3c2149bbcb091c1a01bd2f6aa47fb4b310ee13d158a224d1f2b11653d5125f9878423e8d523fd6f24510be0c909a9afbb1961b8ce4403a618cd02e1731b61e333230148608cce5089cc9d832f68b0e3e2ebaf0a111f814c6ce2463cbd84d90b15fa8162ddc6be92088824f9b35a596244ed4efdf29a5f0af7f9d2450e55df65dac549ac4b3e2a5303d519bd0a2458b162d579a802549171742f8b109beac8d10cd43a935fd27a07f6054a856415a523fd306eaaa53b7673810b921f4d239a590decd795d7f79b2ebe109cd34a99fc908f5335d77db6148d4bf9f77eb90c4620713a66a6194f34a82d218e5bc28966914ab584629866159a66d1bc78d46a49ac154227db58d1bf568b4916238eeb71189546ba9e4e2f2f2020313c3059111a2a670828a0387c9044d2693a9965c60626e4478c36fdce81ba5928bcbcb0b0c4ccc8d1b26538e6b4aea5293ea1b3f986e4c381c070c4c4ccc8d1b3870984c3972e8d0b163c78d1c384c394c39749cfa9443c74966470a9d5d6106002cb4e0a15ce0d1a3c7d643cbb01e3d7a6c3aea0623733ac9ec484185ffaeb3768515666600c0020b2db4e07928940b2ef020f1e0c163c483070f1e3c78c8ec4861c78e1454f8ef3a0bbdad9d2315989dd9f9bfeb989979054e86a8020b2d78330480112c1851ddb6d082e7a1502ef0e841e303a3970f1f53fa88d07db40f1f3e5868c1f35028175c7081078f1eb2478f165af03c940b3c7ad0a0e49cd2615c30419927d5ad0b1c91be3d54b0525fa83406a18931c2b8bd20534475eb23150001c0d030d0d0d0f464a1a57604d0c44e53ea2f4e48b576f0f3217c1d05b043085d4c103d14fbb8ac89c7b46cda0183eaf282faf1d0ced0d0d05016cda1472882ee7403bc4cd47ccf347acdb75150cc8d989f4153b02a49146c23ffc6cbcf1903f3da283ee98b8c1e3e09be125237e1dec9dfad40d4dcf120889a2e7467cfefc4a8cdf1475f04fe8b55427af823ab840a0fc9d257c2308524ea07d52fbd074155a985588539d1f532ae1a9182b05041198c93cd3bdfb340d5280860f0c0abf028a8b7c0b55c9aa983e3a70e5425390255386c433171769b1ed3311c754d0451f349dd7cd9589ce5ded472a73371d4f4a02677ce6c0605050505ddb933fa2015044d79a9228a4934499fe32f5d5771b1429a329af34b9dd441fd3cc8a3b8cd97bae236e1361f5fa9122528ca6a742364481097c6c3863e83e80d44314ae97f24034c21dcdda1b7684d027ff83b3d146015cc04d826fe02d440b1d70fb91a78355f8b3fa80b80377e0ff6a633b88ce8232957bcaccfcfea212a7632399dcc41a5514606be3ff48f1f87a40fe66b0f5f4a7dcd1d0c5631f7847f869771e79c139226e9739c3422b19063c1e17090a036fc18db4ec76243afea9b60ec72523b5055faf93f02aa71554f9973d6e0ab6a5b0b44cdce594f1c08aabcca8c325f9b92cd06a276e55560155877ea8477be5b619c22a88a6fe241c5825671a7c6927d24a53404898afcacba055156b8379f347fd4cd975bd4cfabdc6e5d2b6ef3e1b8f3e7d756dcf9f3bd8a3fb7c324cec7c13393bad684c65544612da6ba4d216610f793b935f08a096ac70b2adff95feac6eb239f986f7c24442e50a2b8aa003db46125b2da38c2fdda89e6644b116df3c16c3d4666701b6825063789fbc9780aaa4ca6f89aa9b6a03244c123d0623fffea4e6e79307bf213cff51462d863cf2b2a4fa8ee04edce1188e215c686ecc4a81db12098213333d727b5bb9b075a4e0c53d2bf28d54107a8468d393ab80d5f2918041c85d99c2c4e90a81bbdd839295e161887e03e10830b4c8c1f18218740c500aa720835a057d890c17db97c850d297774f90a1b4a18418b9bec4c321d17449d7115fd7adcf8f165fe8377dac89e64f624c3bb83cf9d94ba0d845235339e4926422168d7f5542c503108e96bbfbd4eab590db400d6d095fa555335fdbbfdbc5a83c6f0bb9b7b98377b0cc3be31cf7e0713f54b5d8ca35c71ef9cf5f961c57d8e675d0f06395035ceea78a0726bda689d00d1d9d318994da5eb73520b600da5628c99fde0cbd3c8fa6ccdd1906d4fe9e8475d6454fc4c067a3f9f9b3d5b0fc69a5e4a0b0002e6e3a108bbcc651513ae818529a7f90f4fcb8fefe3a7d89dda9e6a80ef5f24beb44aac1225fed002271afa29e9a1dd81a8f927cdbb93664f0c900f5fd3ac0f0bfe0ff195a4a43d69f37f569b3d6996071ef9dbfc9fd5cf8acad0cb27fd977a5e916c0e0e2719fae989483ffdb6bc92160d2a27227de9f7a5f644447bfad4e2405fb35709159ef8a45772d2de2a89f674fd4923d9d365315b44c6f7374285f6c045d7536b011f08e143fbc3e8e147ab0344cd6738e1977d0ff05e492ef673aa21f28c3ebeb4cd8a3edba44ada096cdb411035e7d7a021834ef4e5a9066a79e49f78e25fb6ab448e75bf168aa27e9095e33a6a8450b975f97e70680a54f52a0668c40e75997250bfd8795abc15523cb1c216bb16b819cc6cb16301005bec00a095a07e94852d7633a4245ad862b74266a57e3176b6f542282b958b6e527f1b680713ea4763a782f6248a4381414d1cd58437c1638bdd8e2b3b29177d618b9d8c1604510d3482fa511f5bec4edd35907b4fdc6b92da62a7e3ceae9d74fdc45103a0976d2088f214aeb4295cc805811c6f95d413d032cd4308614321681042c834d43d18a78410324fe9ef90f414fecd5950598a00ca08a10cf54233cac0fae95fe1cd1ea39cd7d636878e085c18c39ecc21810bbf3df93206870bbf2b506fec16a31c07cc3108eb978fd3598860ea7672351216da404848a1ad0d40b91f674983d7b8bddd0b060927eef5f17a0ac4bd9c105da6a93a228b4d76a3e988a10bb3d48f7d84509531704be2800563a8e1a40652ca78c14a422c57c600ca957fd9082f74d03ae204373ebd62239ab1e2c65cf9b4827165cb881604910f85145bdbcc98429314dcf8728bfad53bd345577e943b50a144171f8001129240c51cc2054f5c90c78d50dc184d26d642064a6c37625f98da848171c42cc2093542965611683c2122cb101ae04887208a39d064060d40d1c0086628424803538c98818829401b116f6ad305e594725e39c38b39e7dc1202477114da84a3e496275cba90e58c222967248104356bc2fd6a3555982b50d0407d31a668c1779b1391f9e3c49dcf949a482e9f753a5c10051fcb16edf7e180848beecbe700affc972e076c2bba97d5b1a4c2fbc5e81c14a8327dcc8dff2a0e1badd501deecbf1b1c94dea875c441b9f061daa81f5774e18f9ec3c238d9c3e7cc701b9787cf157164b88d6ba5aebe465f7244b08ff2230dd84986f8d8c7c7e1e5a37cb12719220ef24b2f5febad748a5648e9e5cba7f44b5db51f0feea8e3a12f673fbad9af9ab6a2cb5a6f10420cc67252380a72562e8c10890a3f76f386b6606e6ae5b2501ac08c4a9d97e7cb37995e4e8c820f63fc669c1e1a4aa9fe1fe58860d76d756bea72300d7cd9e948d598dca66e9c05f5fbd44d0e8cd2f6d0d9f5cf201bb186eec052fd53eeeeee5f5cf525821604868a6a6aa3eae0bfa30815a3d79411fa476b819fab83ef284265efafd4abc0a1bf01f601ff45d32e3f64557740882ea264546a5ffe6b630cbb54aa948c6114fc43328e0a17c20ba1055282cbead800023f16b2018a0f3fe38244185a2be300c13832e3867037da8c4a85973929d9cbeb7229d910fa57f7f98df2e3d55bb48b034c03bfb92119957a5d08c4ae99465a23eec5ef537c5e65ef2ca86e84280650b2930cf179e2cf3dfd4cc3256a0ee318b98171e2c36f00ff507259f500154ca6528961b43e4c036b9ca480b88d3b61038cbae3dfe3957d270b1620687606b59f4f1e1f5a254a564aa215b2e255bf9351bb655ba8e3a2b2ad1d79a87340c6500ef291c02d5baeb4523cb2582b0821dcb265e55bb6a8608d1ca4c86732ae14fe359aebe118e0459ac96032c84083e4af792785452507ffa692833fbb3d11e9ebfdec3fdd16883aa51b5f23c9e719165c22e5b4532d7d7ada81d60451713eb5a79db9a24fbf57d4e12ba1d683a808df2f7b0351d1e1f717f16739e40eab10c10c6a53aa5258f2990196158b48cb8af6546415fbfbfbe10f90f084751b0a1ad30cd1a8db44d88d80160eb2dc1c97afb0c1ba4360528426975b4320e372d1e5161945b74504a13b4b77be469acf3ebda2fed12dad19a36234da229ee7d8bb5dd2abeca36522c40156d803f52a7b13f6d967ba41035a1d889ad9d78c7105d9a11f29fc48bf5f89126a7b6ac66022fafe45a875ab258916fad7f350eb4385c7bfffb2ed478022d03750901decb52c5a9f5e65dc8e0645a3cfd9688b5809a201f58abe6619a278a0f56122c40156f4817a757db44b7a757d0d1995fef5f1a137478fa31ddabbd5a27fd9d30eed55967daf32eb43c5065e3151424549665b6360581a0b44c167ff22bd62615da06e50e7e5161144440c0115911842689d9999bd2111124a184550462a0b8d67be54b4ff10de80c6348a20d283a296106d04418408c804d41aa24882c0d426178470c6d0d51982e8679ec0c48d975b432cb9dfa7588fb385c6333836f84acbae8ecbb0e6d0f12e36bbbd020f6a578710be91d9f5ecb8c745468ada4a670f59da9caf75bde209ffe32e43adf3558a21b7a062d6870a8ff6d9fb68f2a177d2c8e415e43c2baa7dbfd8d14752b677ae48af36ebf3c34ab3b36b2b3d05a2e0639d110aa9b568d4af592c0a37eb2c6d0438b6ff622ebffc41ff9b5c4b9bbd69cde2308dffe7c275fe991856997ef20fb9b10d86b33d7f896db4cc740056298027c09effd55120870ab00f7f78861ad79f70dbfb180ad75f07721bd75b88eb708b3567d4ecd93d6e48f6dab5c50ede03f00eaeeb34ca3921432e4ec80e8d456e988d8614512087f6e17f791a5a9e70fd75988debef5bb2e06b00ce0895c2fdeca400d115f7b2930254571ceb466b290bea47a9e6970d349ee12e8fa3e27677e20a4854f8adc58f85e4e7b88c3d76c73d2e6af721349ef99ca734b3ec295da59b7094f7138ef215a8807d785b7fbe5c109bebdbe569e116f5f3a12952aef721efc26da277e143dd41f01f541e5a87fee14a149181eebef19477468eb837c56de077fcfe243a40d5cc525d4d5a0a520a1da6aab8d7c409fecf8516f62a767de7f714f7fa79056d80ab695700aa2d5d8ad1ec2b01b48a95c8821b072251e5f54fa42083fab5909523f0fd752000a7e00d3318eef90d91bed31eb9611a2f2280d70057974d419415586ab75a0868470e2a7f771087304608234c615829478e48c1a85f4f99d21e1dbe13582ea0ae7b8eeb5fe93a659c78994bdcd59c91011e3a08104ce3ef4c7383f3fd9b4c371815f5bbbb3bfe8fec2b0dcd538c43baf33dc639ed29cae799ab537c25d7f34b5b43ff7c1e2554a2e5e1bf5e49ffbc94cc1d58d47e36991c06acb5e646a0ea9a454a8c1cb9809309b08a7610377e43a14d8b2660e4889306e2099742f1208a7d84d11c88da99445ad48f8b8cb8cd478f40c06dba280235bfe61bc7385ff64def97fd67d967178bf1bd0dc6a4c5987dcc3629a1d45ed3ec69085fcdd69c51a9f63f37a319ed7a80f7b25d8415282cb8b204145ba4b18a9e8a25d0e0ddfa87774b54e9efb90725848e43fc1901bca07aeec1a4b5a834ae9574410c15d1000000004314000020100a86c421b1602c1e95cbc2de0114000a8ba0447e529849a32008529842c610600c2080803100032033325b05e52e2b6ac05478bf273b1cc876a547901d81ba146452eef7bc0a948c6ed2dea62f4c9784d52eed030910924114fa59e0de1db03f7be46813a14b0483308478a33da0abdd88f57483bd0929767d894aa9d6a74997a1d1efdf20adb8322ff4b9f2db32957ea26f2727881446ec4a0366981f409e52a16cba27c834a97f45c2e8d2266ddfe35961756aafff73427e714d0be498bc34156c2712b34a8c19376272f61ccbb04846d1069abe353cffe41950d37bd97b971f906e73df893077e4af3278a08a15d69543ee8a40d25557e39c1447ae6f27d6fa8623ded381c5db8e2645ed5362123465a0da19a66d735cd0901b33250d21d3a40c601e869e3a9b7e400274800cf624a71aa248b5af2ddda3104e8262d332a7d4534f1d695510b5435dfecd623eec499ca60a19a64b673854e7e8b8351a8c1ecb49f3ec57b9cfcf6218b3142dc652067ee3018ee3f99e4bef8a6bd3149fd698085d9b26191563177429c4204ba735ae485996d66c534f02d8bea5a5f99ebf4df263bed1a7ea3db6ddcb779e5035886f68f9ad01e7e6a411591c11a285e9d386e9b95fe3849b3b131296689b60c7047fd6af67300e35afd4afdc8b1af0cc57d479aaa5bcc3b0495a6ebb1a4dee9fbba915a5e6b9a7b7a83e7ca942115f6a4c7636452c6cce9b2d3121d0873852869c1acea97138132297234ad9f71a7036ed93dce4a3d06876107bdddea8a2497b7a103032e60360b535ed44e2f434a3f286c8db3040a7724415e7855431fd9d2550691537de0b6b9ac00cf6060fe1dd1914837aff900d09e14cbf9b34ce17ad80f44f4cc1c7bfecd97eb4ccf1e734863287b6749e0a597b3217d375eea163f35075453223b0ad0463ef194305c5e3ae9d5d9bfb431a0b34fc277c567413af7ad0516f20f7526b81b41c14f0285746e9244f91818bdf417f1fd299aaa46dce45ec861de8b6ecc22c1f9bb604676a3429910131b10bb7fb15b3b27f806bcdd4dc9918ab7e5c3bcc653d35625686ca8a00dae6407f0e3abf8444079703b0dc69b0aebec2138ed0e46f218f23ed8fe9337ccc1d3174d3f22d25120b373ebfb55aaa7927d9e457a984be67937bd4400b158c16a7582a3285187737220644d1674744542e2491a3f12e3e2c208b0c3e6a31cdfdb8db67d6d421b0db3c3561a3deaa9b3300382a6375739025324c9f99e14007fa8255f8a775a6cd7b7ba1ed1430fd3e025ffae9914960d95dc21732ccbe6af91a7d969d7422103823d92b876a54989366723d29155b1b652a4d0713868417e99da2230a84b62b9ffa95df646bf920c6f5a5a209df264518f62e82fd04b877ec21c699d05a8fedd529a529a45777e211905de71d67dad532308ec29c450f9704cfef7205cc1d5b01fabdb8235f93c6b229c1b0ecd0316a9a37c2b468962deb93a00266bd779b973fb622e9455b014f10e126fde906222f62ed50787f916944f8b3c326ebb16f97c21ef00c3a881b683c196137cd1dd2181a9b61d00cade4d67256ab6c36811520dc8e8f598eac6b96bfa24807d0a526715456ad9dd5392e45d278fa52db44729ca53d27f8c746fdd628e7680b1ac639c0b4479ea1832dddffe9310f7cafcde26b1ce83ac0aba9fefde4f20bdd4a697e5ad76b4658292129e9aa3cf3a3c5e64808cd35d65d1ff1831043dfbf4e30da8f62e853e320e2ebaf6b4074bdadb306c5de1c6450baf472f59d1f579acf9cb60c940737e52f9529d9d54980e634f15b3732c56dc00204014a399d294e26a748e514f939650c25c9e4833f71bd3294c0602053a530d90abefd540f85f5e707211f476e865f51315c05ff6706376ac6b493cfbca1cd613bb8704c1c043003fc100f0b0c98d6ebde89a300ac9ff3cb4d9e179da4e307e3564725bfa5470c94d164049b5828ff385689b94f3f892c0f8a4de70594968634ea62f95b603392e10f934458131cc2829fb71b91d5ab7cc5ba8fdb090bcd6462d6d6b7112c209022d00c703b61ca3f70769d25dd7ec93c91aa1a3aa08b1fc2618dcf4bfcc7614dd6968549b6da939444e589d17260dca0d9defb984bd573771752bbdcbdeb245fa129d25aabf4e755a6d2ffe4a6070ead45120df5eb2ecee3b1116a37fd61d0bde355392efaed4f917cc8029f80aca4a44bab6f04824e7de31648a6a7f1bf33665d0fc8547a8d0cd6d5ce61e82974da717122f615cef89cda9cd04100a3f4fa2fc88e870b2d10d7a4ae535e15adaf48b6941a754789547fa6a793c16fcf49acc54866c396dd637c756f06d458caaf97b08192b89a957564ace6fff982c7a17a2e31c0e4393eaa2db87b007be308595948fcc286d4990731ef073d9cb7a3785dc08ee79f883b352a2f98d000978449cf4f78fd59966e09315f00af3fc7f9b408bbfd74fc03ae091c8e1ac83859f9efa1d1f78f1e10db9accba489de6a94231165a627439d5e81455eb98cfd404f86fe605e103c650133029087cbd2aef653dce4d82f41264787f5bc94d02006231e10dfb31802e9d52fb3d96277c0add8219480b9ec5f6b7a21d674f51b46d88d0354a802d758d6d430bdc4d9050da6b49b7f4b30ac0b552c13985651d3339aa0c309281050b4ac8cfea394236cc0da609e7e904b2159195e69959fe560b265d7682d212ed3065409136028c1381a0a6296e0b1c7c812f55d46b07b0fea6a3c346c3c451c03b24b260f8fef63aa97325c0246fd5ef1209533ef63d32cfcb5606d063cbed33632929786041d91eb23c1b6ce4f8538f013c0cd045ac3a21e793ff965ec171bd0a0537e42b9642b34c221238dac2a9bb72073ef45cf5f82a757abd14608ea9922b491ad78ba3bb9fc7405c6077e5c40193d65d9d5ff7e750ee19d2e361655c4e5dcdc4e5f09357e400891556e5ecb61c11823acb329a7ae7e864e5c6c8c23d75f74b9b5ab47844fd31d2aa0307b2dbee95a1d4c53cd2df7de0159c3283ed866c9232018ee686577dd0adadac3724742074a9a5f37a41fab171400a989cfd0c878270414f62927f78d20875e2028ce5acd0a66db899a666ba5aa361de96b9da3f26c5baa4cecb276a4e5b79978f005b3c25b44f3cb2edaca9b53fb08fc9e1084eab2d037ceca42956fec090a18a154383bed00d6a7d03ccde11317107e09c2351e99af5eea103af304365ec0f94b65a30f7d228c5bf72a36afd4c24c1efc99419f3a0c0fe5db33dd2358f60f78a87c52402324e1dac9722d94b5dc62f7543d5a09e03dc74cb3ddabe0228c297a720f54a740a663202a68d0e04d1367602598a391c92ff047c8d34f4446f95dc3bb60e0e5b86a49ce615ff5df72bb576a04f11d13b706aac6f23f4c81e540d31e5c0cbed861ed42e1b2a948a2111fe46a8535d1f168f9de235ca0c1807d8cc2dd870b0705520a0fc511a2fe5d442972e5d88a1ff98e7965ac31ca22d3ad1ab59b99b12703389017d6c1ccfe2df1abfa3da2c94e9e106b9bd9b64d112e9a703b389c8b3c9170ffd7ba022d5255ba58b040b37b066a79a934e97b8c14e99cc02c64d5de8051d715edc14114f4821039c4a7103750fab7d994471a32b9934018ed0c73b1637cbdbfa5ac8276e26179f5a8826018534ef1b93aaf04b00ea485c9d93accbeb24b6854fa6703077bfafdc655c67cc7565c94a39ce94922c5d64e5ba933bbdc1727df3818fda7d1cca32445f04abcf1be64a10f3d70a47026c02201fa35cec8c5692b88ea2ebcb178a51568d406e51b3a418c592a246fad460028ac0519be451405a592bdd065125d37818f459c1679adefb20963759cda88b6f52afdc628c9f1bdda3471d5b02344d492da4a48a83befa10ef1b8944782701b3c16360662ebbdaa6564130847ac848941251c5b41301c42a43d12e0c6f3defb328aaf8287fc051846758c866c5420f50325ce3a81b8c038780f6f81f11b83250b1ed750f0d2732f332abab1f854f780c34ec4af3db42b048ad3377828785e680d43e4da611d579e97de4b8b2c7c7f8e4caefea89e321b5fe868c617e68dc3ad178bacdac9c04a71a9f4f6e0005d1755752d04bb84a239b5ff2cd50865596899ca1cf3e5fe2238ad0d87e60f9316e150c8d873b08f7a08cc86576b2c79d5acc09b8adb4009c6528a54041815ad12a0042d1a7f5e3dda474554576152339e9ad1faf866494b9dadb37ced17c7c4d0ed7c8ada71d3202246697637015ce7c71d3bd6f1325abf119eaf2fef79e2c15c9fa7325a1a638fbd5028136684fc7e76092aa31c3f3550255fc22c9f1c89817236b743bfb573262098a462b22a4596de23e096f79956d744e9026aecdd18fe05af37ca753c6136534ab3de739f19054798923a4bedc1984f5e92e18a090ade04e7c8e8d41171e32d38e41514000bf47d3b57a52a07405eda8a9ae0bb5f2f329467fb89c10164176e2df92ac84c578c384f9bed282e0b07e3e5a46505e2d0f8e6ef0afda3d054e7e86908b1be913253285f1115b4e9931aa0f7c11823936d2cac2d05857e0b4bc0f558f2523a93f02dca963fb21a3eac080c54cda404946b72f727cc8fc6990d624b86f2d92ebc50d73eff95e6f3d6b9774a943e9c42f027669b6198a80fac4bcbb12b6df2fa333fa408dcbaef4016c3860b18274903728e23079b04d026968e5a3a07282190b052f0fcfc130e0d433a8eb485c318bf20e6d88ab6205521991bbe3adb22fe3c67eb88676c5fad8b9b3eb4a4fd405bdeade12df9fe364edc2114449d2620257ae4c46d534e2a41e5d09707e814995c550c7038d47c7249db13c408c308f782bf85f55d97f897e37a61e813f28e73824720d7517e9a17cd5320f8edb31ed5980ce787d3bc4c490b1067caf6c20448176731f37f323cc8a1457c04f7f26043a3f9d1b8d814e3c62b7c454b36d158da504addd16639b109602714fa48cbd1059970e4b7a2d4cd4e07ea37dfdf7373e1a18fc6a9d5b31497adb38cdf641a8837ae31fa397d79a303731f4ead154cc48aca45f0bffcd6d64684dd7dbaf5c7b87b6a04aaf049400d48c1a297bdf2621252653aa9e6638a24d39c06fbcf75ec2f87d72b15614142d422db44cfeeb8a15c73890a5b597664220c451cb0524579543bff334149951337c918623c4bd5a4b4f2327843ce48cfd78e24b716f18e2dea98361256ef990714f5db2f767dc5343ffb88ebe8f7861d07c9bbcdb843b66bc696174c421a27bb6e98b1b114fb515bec98854ac57d9040de57e57130816135731b67295d3923796e16aad075132da0947464dd314b4b2646d4e2ff48089b4fea90f0461d6b4cbfd8100e64523a02773ce0a3fab64e6955b2dc76ed4eb52e7231cb328c714bbe4ffc9caec215593343167eca95d80de326b113b0f1352add7b31d634103b7100469be7264a03e31a9e18a44eb0432510b6ebc53bad80323f531a8108c7accc67da2c573fce609ee4bd78cce03f17c6e24a31f37f2594b5d1ba0d4b573b1a4341635da765363f2840c748850c44c5abe535e0cafafa284cdd83facf9ea77ab4312dcfbe7079430c314c669efc11fa9730d3cafdb5a700bf5c1dc58443b76d2f1ef700f9935a57321f382e44e6b92c3b700ccfa43e6b9c4caafe48b19672062a126e26f3092168983a888cceb61799fdcf46b207075d13fdce31ac66a0905b6bc7fe3626d19a51b461b3a6887be4c55ef37f25a431daaedbdba49a0b14e9bc0ce4314c2dde4a2f7e8db506d6655a851ee2f2c350a78a5046b25dbfc326643802370ece24a4e445d7deb158c71a9bcbb02e6e180c00fb9eb3859c676d9bfb5e6415e93505454adaed082eb2810f364a619b37a69daad3431e0cc185d5fd28294b4e01ceda81d819f527f032771e97324cc8c94387aa6b67617ad4cfacf611eca215866d6355b14a6013cb2dea0c203d685bb19a6ee84517863e21c9c4ffbe2eada1ddad0430cffc8b49a6f118ebc3cf73bf846dca495129d3be32f45be73d2b126c500fd6e4fac65e5f9c7ae21d922970e43fdb2c32d507c99e60b2d67a5efcc1125302bf2dfc6217123bfe65005d9055ca9659c0fe162ac480015502b28d1fa26fa605e6a1c409096c70e0027cd9d214c8d5c82b8ebcb06cc80c05ad7e8e18cef5974e9f07032f889f8656e7a8187146f73911058b549f4d673f378e07be6dcf456db0f6168fd51fb2916d6081703634833e860f601737315c6ba830b14a3212b7e30d3b7d9b7dd7ab340037b2a9a60715e175acd103c6067dce1cb6e65127357410a9f6bc4dab2980c3bb30a911623d193d1be1162243e4447ad47455db0e3eb7e036f1ee08bc825ff2f43deed1791a4c9484525e3f4feeb73cf85a3cab289544c588f452e74beb10ab51bde1d8cdf162cbe246fd0a4958dd75713cccb3ccf6027f71144df0263c46fd652f25a15c81fec0739798a593dcdddd56db573a0aebc1556b45da8f826de5bcbf40b3760f6c8ae86c870c034fd72dc27333da0c62e04b858c1b0bc2e04896a7edb1bc9716f065f681e97724d7b653f8952b3ef9dab0fcdc95709064aa66041c19153ddb10183132ef5700c1068ec7669da674b56f3ee2111fb33e8d02ee44f8fa49353011cb0ecb865e52d0116480b9673d063f9ca93d33cc429259b33dc6ceac1d09e8bd113e80957871b327b9da37c9f1a6eb120b46ff416836669400c587eb62c3762c35744c22494c6c97c0447ca67b6e60c7ce7aa7444046e40849b290bc8b37208c395251a26e843977eb2fdf64a9cff1aad991be60d5a17d1f74248813c77863f37420065dc4886ff338bd69cdb46346e2d5107b014f6ab990e7169760cb9b9aa42a19f6428ec8fc640a8978b1564427dfae96f63602dd2646a14efb2b1bc191d364bb0c8ef68f1301de8e2edd98260a2a98034a2606dc383fb36e208524e19d799c18a913ab05ddce6e89915e29a26df4be867b87379d22af9b0b60d54a7799782ca38a1f390f5da577087def7a70b9b501729ca66a08e7770fa695aa0e0789415d7e6dc901ca6b75d769a52a86bb42d8de711fb3b83c4931f07638bccbc113c2b5de1126a45d0ef455427a22d79c5c22876cec885b88e80e40f6dbe3cb852057e0610bee421d5b02ff0f956a1a027ce331d581190187118a7988e594c16429df372c76704bd81951bcbc5e109fc56336b71d4caf960808092b06d3bf7301b4839954998e16f5924996e79cb5290c81b96a34d6e067f894cadafcbd2e2ca672cf96352e6064ab72cd3c7be6aaf35a9896b76ce81ec1afa927a30c2562bf7982aae4fde3ee893ae1b137d9c01cf19621788ceda0073592ea93b9dbcd4eb9f8697bd6716c6b3a9cb9efe994f1924f8306c34680f71042b0804f4dc0f600721efdea709803520bb08bea44d79e0aae25534d2e58ab3e564ed356b5a7c4bae03fb3e8683e0e507b37f7e9f1008ff0384017ea1ff45f2d46b725c10ac1b457e8c0dbc245d3b630eeaa26e7177c92c0d3b02c7a364e3ff33bb0db938f51de7e8e5933ad8ccb64613246d12899651599b2d35dda96e99a179bb4b15acb1a3508d1f7b556408691c5f169123abd9c66237443d96f9d4d148bf5c199cf283b5db45b0e7183e884c43865c7f3567d2f7e937198ef87a6266697e3daee88a68e2d374eb7b4636acc77b9e7a8f3cdb7a42fb43b1d531b2011d8aab383c5ed3def1059c01245a681efb10486e94570191376bdc20d663188519153dd063dfab66bbd551373c0672a3d9487d029634cabc5876413b4c7785c51e8ae70dc11f050e3b1a64d02a683094124bf966628b8e068390a815f7f79358729a59ce44697a9c11d2a43a5cf1572287d3835614391608cbefedccfcc730b17c489ce03c5d9954f2f7a24a11236a346feb1d3f3250204fa0eed4e8e8f45bc8e8ba9b10aaf9cb8c086137a75589328d3657bcd94055b85de9971b430508f1f824e7b8fd1e969caa6acc22aa23fb2e904044239d1779a0bccc0f6525acb21e760b624ffe5a645388865b7c5db599d6096723f1e0a11a8109a734ee1f21e84058bce70a939426bee69a1026111c802593595c890746b195c18cd42acf43986405f82389520df7eb85605b81c3d5b4516d771f22a6a89824fb651eba0a60b624c77ccd50a1249b6ad692183f25917c96e447cb60122b5b911c774692130347d8deddf74d9281a088b929047ffd07fddc94f6f59c8a249a7d0137f038bc0c7ddb825a6351636ab377a54217c148ea145fc2545f779b4181b8403fa22a92389685c9a8542682534c26bf4a1847962bfddd31fabba703c94619ad48e4c982d5e71643cef7e1259a7109d9e5a7d281a0ebc4347120a4244192985f3a5ca245e0f8ae86700ed946f00a02599e9bdd1d5dd3be72c471b0f3074a15461616ad8f8048508354b727220b2959104d84b80185b159302858c7a139d2b17d0a32c9594f4207ade5b51ca0e30eb67400cc8e85b50a5f28feef9b5be983bb277e40c09fa40520957205cbd7f3e993383b518d2eac89c107aa03299086ab82c94002590122d423526b08d0e6fdae4b4b2c7ed2b83da3428fcd8984030eb5bfa2a093a9203b6954ea25cc151d73c182564d38022c89a43f236e86995f82b923dc5ebdb0402826c969d42fca782b95fe01ed3379a50239446922317e67563af0033351bf28416815746cebc787ba436d66ba23a3d0308a2346ee384b94bb48ba865127928b9d220b48c97efdca93fae75e1433df6f9802bcd50becb760302e69177285054448c61ee5f76b9d570f9066220534a11d86deeb35f7ee18b9d82a82e06721c1dff1fe039cf0faad120edb8aeddaae051777bf11aa57be06d74615d0772cd1783234d8fdadb3743057f6422e6ad6125ed2292834d149219f63e05f7cf258f0c8f5e5148409116ab83d7f731cf02a553f81887b3903eb4fab00aeebeca8cd29366ecb3c91580ea15adb83aa02e02e07184df61ef5f15985e1ac6db9d976616ac85673fb471c2eab814a2d23c4f83549c1ce7c0d8dfbe67789fa2b0b09ddbb01ec673e709059306244e9db32a7cd0f4f9f2cd4658c68a1fdd8e6010e1d026bce66ab451045e18f861c0735c3a60d6f2bb220111e90223f5125166e66282b72db65c4f4f581eaaa7f2b79fa23e0a33bfb27f561346031fb0812a2a1c6155af99e406a80c33baaff903441c69d96f0a3904c56c7e35ea3901fe440d13c6716121fa25d7e49448a88157eba6f650d50e02ad89d0e8dc02ab765488e049a1e701afb1061de9b4390aa12428020f21bd9a0a23f1b73bcd8de32a4d076c4ed6d0edc60c52fbc1cb0c4cc7850f1d10755faed489edc0b925868d0981540afbe5ba5487bc28d6ded347b0d0a0137d4732e5cf996161685018e3c7d2b900330f2667e9a82bbd074b4ce64bb85e6f2f3e28785e6b0e2b99a4785c3bf403eca880bc787965b5079e63f5025e4c4b3cb3ca4d7dd53b4c30c0322b1370ce06fcf188aa477bdda064c09cd63ccca775558d45c9d127542f3b63134b80f82a53b75091b826dd0d446feaf9e97eafbdff7b7eabd33366b5310103c652141d439f577892f5d0eb154eecc3ade4eb8ad9c26eb314d8ea205e12d17077b752e21292bd69a9e3a5ba9b2a786956328eac0d35432dcab5b009d03d35f7a34fe39c42fa9e49d5763a734e8b0d3f9539d1bd0c3288bd6c26c4d6a7c202286ccf43145d921d206c7206234557603067703d32c5257bef912083ccfbaac09bb3846417adb8dd823a047e19bb001633d6591ff6565caea25db2121e10ab7a7b25ce6b28b05b821eb41b5d1ad819fe036fa53e8817df628b4b7681810fa8dd9f2eeb6197ee37f12dabe44791c2063ce607c874e79683fd441243d54144fd2a40475b772154dbb2be2330df1fff6db04ffb82c39c8bce50ccead992c56c6f09dc36197221b7fef7ea2bea65b7206237d32430578188a4ca203f7b1ba94b012d16bcb1485d754912f53749c8d760aecc70fd7f8a35b346a6efb530aca2d1a7bfafc3404170650429b07f09f72140ae078798680edd8071c8995d0cb9796a691907975b6ba8168d7e9973c287397dfa401e8ad47bade0eeab76fa47a102e707c147d960aa870f99f494c4143e571ccbdf32f79cf6134187b3170f5618ce44d5be290d31d9a19738319a00e66d451987c1acb5bf2445fb02b5771193aecdcb086e1281e6c15c8c13c7f8b9bddf936d8ba73c41335b3807f493a4e581e8b7f8b066c6405e0562d0f9737014be10faf2b3f36dd0fac95ff0022083e00425cb96bef9b6477dca946fe1466c367662aac4e83c2708b9ebc283e4de28d92316d62d335ea005fd531c96dc34d10e36af8e8b3d8b6a7912a30377c8ef8d0b7fa914b95ade0855ef8457a313ad35bac31ad48059abda2ffb4895598d1ebc2bf805627f193a4b2bc61f2280fb75aa655b0e427a14a0e79abd5ff2344c24478885eedfffd461216c5627c268089e8b5a16a0e5a41027543e0fa31480c212ec46cda9dd4797edad82c9fbf385825214d9331362824742a45970dfe90b3a21d9ea124ec478f86011689493cfec428f9e78477c42340c207ccfd9088861f9897815185850117b8fa94e9ca4b8684746816948d47485dd40708bba1c218e0da74801c05b190e0412c20eded8216d4871d6a4b869e65a1ae2462f1ff983a71b757c8c42a8dd375130c6a32121d22bda395dceadca9d2bbbf0677efb43fa0ded84bb0f40aba920317f0436b35f96c0d76ac9be17bb87886d4c745cfd416c4e1b10aa370ee30174a760ea796a2d265af60eae5f6a6245bf28a8c3be279d4a1781e7e1893158f133ebf1d0d7460817f465765a0e545c8d61b0d2547cc023e62ad2ee741798cdaa218bd753b6595c6076c8d1f0da89589ec7b07f3671d098a0479b70040fbf3437cbbb81b60433434f2795328a0e15f58aa42a61654e29332cfe10d6f25efc1b5d40c0520b94344d9e463a501b9393e6cec1fef15a55807dc181bd0b75faf9d0324c9c216ff41813fd617de51c93583ec0123982bf6c1749db84152357d2ea545087d48b89a033f4f54a8372d4ab56ce3b1ac450d81f69053ef372d379a1c7a0540ac7a24114711e08f85ead3870bd92d92db746df7cd7ba24bc6ac19eb8e1a08caffdeca19502f4c90337077113f211db1055bc0dde8fbd8d5be02aed29fc06962e91aac9dd3d8858374f40dc06db90f9a9e588e6f6fd90e20b9063e57183cb544e726ec5a94a02839308859f2cc3a609639647c80e81b0ce86376bb8b1085080f705295010f1d3e3912634f6b603d158acba7abf7a9f815e919988e16aa9020e927da69bd4adeea9afc705d5735e81e5adbf47643e88f9629a46b3971299bedbf2e05d1c0ec7d4e7c3219b256500df450d9042c2b2db7fbcaca9fbf282514d673b03514b03c706504d281c1fb9af02419aae94e1dae558341596867ad56a2e0d3a98169499b61e9c095be1ca01b09be153adc014935b2ad29b40ccb45f52c2aa089d9288f5409038acb2094078045267b96bc6596e498ff730a34f6a4fa6e2c3828de795c649a2866029e009382fec8a0d92e90da0cda6c2b84992ff75dc7be6b8e5f39be7c1ad5c4fd4ac5704bde5a2a0ed77edb3cc47779770ff44ac4f7be53e739bd0799a420f46217d1e6a9fcf876111c796708b2a508b37088aaa3975ca087a05ae7c259fcec9d83c3153d1f8f5eea9f17a8d6412950c45c04e91c8738793e8e21832d7a2a2b52755452dd17b53b1520d6868a7119f94b56ec3bf9ec27754e1f8ec30ee56eca4bdc4009a60f05d3618ebec78d266e4da293bbec28b05999fa9cbc8abf8c2a95634d1996c1745d8654e0580d41d48765eef94679790861f22f428872c64180283038dc0c741573e28686907140df7d5e299e6c5b8a2ef05ac1ffd1950ec858bf4780ccca2bdf06b42c20c51df415976a08fdb1dedb0fd7d021e3e1c4b2dcc5d5b5d77a24e671260a4237b29981e0a5c43558a1b56e636f97ff5169966a2ef2282a1ee075b6e00431a936e130e9a820a8d9ae34191240e7102eb753c8263f83afcfe1624b4065d3cdebf60c476c1b852155f6f8fcc3b2bafc64d9382d1cbd11d6e59c36f1272adc52ce743aa3c3d9c96b99be2f071e2704b32291bd74a80c2ca9ea9a26e25475cd658f864678efffe1ff456d9276370dc58315333e6096c1f3ffc30a1c4159a64c8591a4b997c24c3a32ce7c1bdfe84eaec3a07d0b7b6fbb8e374864cb49bfc3ade10d9babc5fafe88279ba5ddb805325d88d7bda0e4f229cf694e2a2187f894df0fe1465bfa997d8e10c66a35ccac58dedf0d3a0010b24e6a2c12867e83edcad13cf7663ce119bb8d48c0c977e3319fce73a6d3fb6e9b10a7fd9893e3d5d236c9cb03b0b0ed3680404075212903fbb664189ab5b9d3c5489349dd64a81dba502dffb2f9ffe2e171b36f98eabe52934f7d58d4a893da075e9cfb61d7f80d53668f764bbdbb2a4b0822df2df6beb583810601d36bc0c180e3683e3cbee52cb4ded0e4587c4e86d9237709c9e2c93c93e133136c4e3188bf25b8665368e489bd87c0e407faf38ed0f8f526d25d073aee60fd01071defb843bc933eaa99d4c1a5bff5a650ec5143ecd050a360f9e75cddb7f711fbba8ffbd2df4e509f0d2614d0ef85602262ee633725de6d102101ef6d8080c0fcbea500ea1fee0f981b53ffd6f77b735b2a5522b212d201f8a7fbd79081ce748a4f95779db0aef77eedc3afe6fba12fe1a6414c2f0ee5133726827bd1dd2b788f759cddf5b215878a2cb3aad2d420a6574352d733d389c02d19788e4bb9958bde5cb86dfcad33c24d195d56728b15614ea4a4553e290606bc8f0103ff3eef8b7dabbf1cdeb0db1273f37de761261bd966393628017534dccc4f3204588bfbc37760dc4a263944fc2b35a787acc4f6c6e912c458a08ce238ca2e104b6f8d7e7d43dc03bf26e3ace8ea7170e18a55256cd694ab3f32a3d838351ec47add1443284f26f0f13656efdeb73ebb211cb726b28c5f1c6519193212b8dbc8042873525d136da06596a3f4cfed931766ee502e6a1968b5ad5fae3e23f41aa4eb59ddbdaf57b75606eac5d2d8cc14adc5be57b75738d49248e2e611e8e5d88034b2aff775df9f7d36f132dc2cd385089ac4e61c331d30d995144211ff08eb37b85f8aba618c6d3102971f07c0dda6646de24e3f92fa323a029611f0b4f9a5ccd6e14c1429e4e10948eeae99f1b40e460a17e29cad00a11d88f1a4fe7055e0131a7b2091cebd4d2cf1e0c08264fe128cfa2f95a2910b37d4ac2c86ea313e45e02bd49226c3ca7c0e8e3573b788deb087f94337b62c134b12a3291de3d35848974f950ec4d77822c8d0fbacfb3f814104fae5c3c46bdf0907617a07deb27bafb0cf0e82f2a103c0daa6897e9e017d988b103842dd3c258b4b5e7a99f77ce3da4985507132212e56558983ea53e23c52ddb4fb149daf24cdee054df3e3f3863e40450f2ff1d892306f934d98e029fa929fbc799010883e7f5968d932bae83613f008043a3abee220b67315e0bb8c6a89941de6d4b90e1664d88cbbf4c9a78eabe9ee1809fcd8b87cd61e8caa50b663e6b308921fe509f29bde6deeabf577cff906883ec57eddca4d29d5bc4ac010ac5ce2a14ece0fb834e5e18b7b26ac6bd475119cf9f0421625071817b146c5a0e751b952fac022192f78050370ea198513055291067c37b37dc3b51042815482f41dd007a5b7c80fff57c941e1d7b1f01f0b196257e536541f3a29a0723e718a19a188aabe6a20d630a466cac365d1f95be9484035d100b48b29e141afa16df78d8e3f0f1c9e534489be19d2ee6389811e86207e601fe5afa1824d730b7665c0ba55958d68f7af6a7eab9ed802cbc32163a2418fef7276d2eed8210729d0829d63b1e5a5222f55dd2b392a8da58b34754f3606cc2b723a5a3ece8f6389baf21c95d91d4743e1666643ccc15969fd5141714b30a9a73d647f7dd4b4b6ea2380b9b2a33cfe202e2ce94f615782dfe4f4a61c0868be12d6debaf37318f32d3c59812542576e32ce58b4aab773f2d24ea282acd03fed7e727bb407b2da611f8c1f59ab4ac8cb515a1cd96a39871e5bf0cd7237665f2b6e7e3a805839e3bbf2aac6808e8b0434eea516d7255366e13754cd3cb6439b6dd03a5d1f62aec269ac1cf670d4bfcbc297d4418e823e8d63f2a256a142a31c327f3e6ee40368f465e07255484f8068a1a46f5401c78a17b8a4c0c39402de3cbd71cb294f91c5a3d578fbed0207a363d35a8bf7fd5ebee5f7f3f5737adcd4c7d390e04a24b87137b36a74dd91ec62d197570a677cb4bf4b626056b51ac28632302761c27f6e8f4814ba7f88ff15fabd97bb8a4f9872cacc1e970f12b118f235aabd43fa8d5a5fcd509abb03d46c26b300ad24488c9291aa43a086e0180947840f0f5775e8c9b4e5af33e4897857875e22c35de80b0d249c0faa929f73ab4541fffa518549d436de8caf339b1f3a6456b35eb199cb6a05c82fa9e96b09a68f930e635e204ee9038e280094a783348feba6158fd8b41e3f8c66ea32e071e066c0b9d048caeccf5be097367484985fc5a843d5b0f001cec072df05745d47957a68a1121b0a7f94ff3d732f865b7789400641ff9a545f2cff2a750601a287c880f6b7276af38c4c7582acf5f41461df3fd5d2295e864e97a1533bd725654f94291737eeab7629ba4750f9118e8b131fd0682a253097fd78fe4267b9662cd7acbeebc82318abb7886688a835b18fc85b4ec91ab0207a4afa13f6ad192c09808fa5ceda217b17b0c8d5e7e20ccc7088ded78678f66300a2a982ea80f0534290694b16ca53e9b8e389480e7c2e2d733dc9e8a2378b7a3157a0bae8233885003ad7ebbd501fc96aa7cfdc08fe688c06dd261dac840704b43542b9e5389422846d5d27d1950c6a40e800f03b07900fd9c6564cd312480bc880d428ba8fe4812475a41c3f301b4af8f19ac43d371687a11cda8fdc87d4868f43c9991e94a9b53c51fd67b82d559c24451933eb22e9dede2e0946d9c61cebf2bfe09d94eee6f6e410c595d2e770fab529b2a2f9607b9b2280e320d7d6b5ca1cd6dcf9850aa300c5714f4dfcb6320332552bb45667b9aaad4118faad946cb3bfa1c3daee34b99756c7eb681d116e418fc697e0eda2d872b2962701caba7942467c097f3bba2068860181e3f935d04292c19816a4d46bb1dbeccede72b1b00474b1de160731dbb3029763efaf1dc8c5c6a2ea43e15118896686563705eb7c364d9d800912f1447dbe0a5ffd5fd92234eeb1e312132d7f1c7cbc80065252137e7f842598d870d800fe2c540285449058c09ed2f1b7f5171e8ad80d18000caf9840ed78eef4e07914efc0b3829fac53f21ce40d17bbd8c2ffc9ace978909d5dc244d55fe03bdc9e1844696b4f4d801ae426b0ce8a86ad3e16ad7fb324039be10bd8162d933b09ae8d9520be283a500dc9353fe8898b70bde56d449fc091389ae4cbe96d2c5ca8aa791f838d40dcce19b56ef6c92d88d79b0c9e7ae87de26d47cb540487b80ec513c7793d904038891068f0cf1698819376fa8b352ce56adbec09293568bdb4e099a58c716ae617b69c78c731a7038166a43fca05c2eaef19caa4c425053f957a47826175372a125220fdd58ab6f0d02b12da8b56926e04ec4a8edfe9fed0321aadbdd501a94019c2bb96fdea3ba2645d1197841b2add2fc5d5c837b3334714ebfdd60ea9b6d021fe7fd468cf58edd8c6ed1a90222c9874f3de69a26b74d322bbc8942abdd1fa622934526a0746b69b2483032bc7a789fb33bc94d826ae63769222b50a0a67be7f804ed819eb556fb35bd260cb2857a50cef32164bc6c414a78a1504bf3bd08059109cf57e9611f55934e1179387266f1ec47477506e326f7dab99261d568f659379fbee0cf38ae81c0cf988e250466cd033a70de8dcfbefa0c58c2ef707448cf1aef0b7be9415b08e1df101fcbe2b49d1ba5e1c22db2983ed9aae7a6502af143a479cad1c3221750fe03ebc24057c111f856b36dcc3b01f127bc283c69a54ea4b423767d06d7a535f4c51d648eaaf655343005c03e4713733f39715467c129436c627b526306527dc8d965421ee4921310441ebcf07dcee60d92a87e0f3c11f1d436681e176f8e81a56f78b20e07608a114653800c8241194c14217bedb270d0a85e61eb993a29576cf77cdaa2c79a4c9b5026e1172d0a30a9c57bd3236a770190846d0be519b60d058300a18481c07cc0d342691179e2ca010aead2ea8dabbb103a2a14b3fc8aea0f9944f75cc2bc48cecbcd0d254f89fd473f2f19ba1c40cd0e2d26a1b6d98fc92e3d585aab68d2b38ff988f7a16c9064f9bc968137ed1c73452c009a228b8ac0d224352ca052617734cd24c610cb1492250854adcf81469a4701f8d0d02efc3e944dff76e86efeedb3d8c8e9124b42fe6c81a946841abc3490341ba4ddebc39dde83466e3918398f393d66f256664bd89cb8b57eb423e425035bcbb8486a8715445c80fcd6c8dbd335465fd4530abb41f6105525ab9d83cfe30324fece74f59123eee3fef4f25fb25a9189124b55256fabe9365ef435e16b48bb78e1b75fa6a0ee1e4c383514446b4d72e95c2e36555a3cad7001210e4fe7a67c36cd58cde12a657b1402ead77dcc32db34c1dd34a3628c9b518e5c2695c437d9d3e60792960a7ed673411feaa3cd07209dde7e6c0156283eef6dda6bb9aad4c57dd05c1792c6b607825ab0b47e4a9e0c267748328477920056abde478941f12b0a8a38fe9bb5f695b84b82e80f24a74f13e96b1a8e0b255022170b2eafe70d3ce53f65c9afc4fb8348b90598a9617b66591634b525657e3e24b08710c06c5969bfc6ce0d24ade764b4c2d857ef0db3e50b5196f1c5f1a827ceb5c7cfbce58ab28d37ab1dc9ef3a0c8ecf252b806c684fd4f75b18279bf812e51e4350b4069c9e2ae22dd8f181cad06258a8c4543d88f35bcb540c95e13347d560a0ab67a5a1c135ccc23c490c7fa3c3e934020ab6b3652fb1c6b5c819c514b582ad5a66c4194ee2a5f4edde7d824fb48e8371ca629667b84219cc30b0fff6759aac367349d83ef960ab6865acb24a6a7cc7eca1e8452012f01dd20d2b467ad84f033c7881c262fbdf4e60c071c112a9962102c8002d3aee0bf2378ca56f129da8bbc9c049330010936b92dc63e9f5a21b998630eb5882e2b8bd01517c0daaea8af9aa2b2e54dfb53ad7852c95c3b32a3a46cfb7cae9fd5340afb0171f63ae53744da451796bc30f3aba41f2bb99bcca0099fad28ab7a813f54677c639b4bc962b5a9b77b24e0852a29e4a0b720a926bae75202b9f2a44d811e7c823b77d8219fab97015e10d8e549aec085aa7927df32b558c02fc4db9c5c7b81f5f3d9a29663c3cdaac81f0f1c167405ef2e8e2abba60a463ddf80d1337c55d133038429ab6217a81908f5b9b9b23517b964a37fe090ef1efec62ea9bf2a14fc7e18ef3165dedd63d3f1bb2c64b925b6bbf51fadd04de5836e74ceb5cd57e2cc8a741b6d6e24145e159d1a852235366816b938492f085d98bedfa8a955d58c7cd73d9ae548a7e5ce6d0c8b7291548280e3263408800231299049872da1e42116a7bfb7afe14097301e2d0926116728eef9db938d209d888921b8aaa12993f6570e728081aeb50bda2c6ffb5fe9e9d98e33bc7df9d952a43e140d6a3b06e76220b769733d9b81bc6c2c7b74e81c33fa1b31e0dede5df22b7c189e1848f704dc7051eb83334187b2112a43bc0628f802356719b0186d1caf84238b2af6cf719e61a27f640b3ecb06d33611f929f6145050c43ef8718cb099ee9ce4ce008bdfaeac68b59ebf36024393e5cf679ed5271f8d53539eb40c316ede5009962324cb1fd7a8beefc1ed507d97174cd7e7cc5a839f133dc90d0fb171a8200c3fb463651dd033dab012b34415ed13a3afcfc25ddaa462d1420c6a86410be35776359938bf3ba99cd195eaaef4709754cd6000c7f0e0ab62f49a92117f2ba9c603ea143f24ddf45069b263db0fdfa2f09697ecdcb2da448f9d53e0c029e068e539b8c2dfaf76d382f9ea8d95304f57c5f0171224e85262052032ea7c2f1a0083bc4822480eb55e2e09b504a5a8bff361e3378c38fe8c20151cc774a8af9558cfcde866f858e6459fd3608b1e13db70ad36cce5298769b27a8bbece2c99dc99f91089a506e36e42d6baa71d396735f3cb2abb0b3d6250b83c0528c2b412ba00be3c95ce0c83e50ef944f05dcb454bbf7c60ab8ecc2ae452d0beb89693385bda25e7f87e19a0cade78ebc2ccc1416076092a42edc1b52ad8f1bfeea5ffc8f91a245cfba7bc607184bdfee0033361d118fe7371b061395de9c712bcc649b4a95dbec8a29ed3339f83d5418484730916d243f7a3237476e588dc8a5f6c72e2d4aa88b29159d5fef08291ef07444d588221a63af7992fac83ca35409e09c5bc23a72ad641221eaaa098c90cf6383dd0219c6fa8a6efc24cead13fe4999642619c61f7858b484264fa17ae8da2ad05e86f1a50b537f1fc47ed14579342f1db00b1b85d2cb9cb5b53590a0878cd7423866961ad5b776c6828fb819d78f5a2ff62792a53e8ce6d2ecc94a3a1cfa635b8bf9a5a3fb9e61888299662ae648047151c46e303264c517b5447c3a095479edcb64c65b76cc7c5d98b6d191797d9e9605b5b4606f379761fc669ecb196aa76c9165a8e80e2c021b36605729d9243c2b6f0419fb9ee1a905cf82adee5bf298d297156f1e6d943dc70a3fc40c0e07c59e9a9866f6b3119c7549b05bfc0c1bd3e652f4751b8d56cdd52060976b4db19fa827e895efb7698ecabbd038c7456e29de81e1e061f65075f4c8244b8d4c004943b266402deab8b2921e55c63c5fd751782cd0201c78070b68a5ae6a2b21d9fcfe3ec6ed896f4f3386c4365fefde1b6de95fd9c185238b206ef6e10ea6dbeb4e1f9ec4686d6c5270dbcdef08ed73bd33053e2f84df5ce6bbe5d4678c07df817f9e0f9a9baf629483209dfda20577bc5192c09c89238e8c98437e22a4b46692f003028a86f8fbd06f251d6ffc0e9207daa01de51c1563843888744a73d8b61a505fc576d26265e4c5b5d232b2308676189173f20d26127276e98d40b9a3ede0050b2d7c381b7c157555bb16cb36a9498630a6aea33564bb99bee57596f0a92de5f3da3f8801801161375ac220c96acd41537540fc6397098681da7fc45d7b1cb940959f229320d70ac517ead4c59077e46e9abc8acb386008f2474e8adfc9162cce5f7375c3e001e84f69498b4c7b67b64e62996e033e9ab04d7b439a04e8ede2510431b08729ece639c07ec3c9e5d7dc4ec82668e08e1b7f5d6489bd065e8fba125f505301951b0a985c7f66cff44cb49c7b62346c0301fad1a61c6db6f5b09ef16ecabb60a83a24398412f43843ca833f1f363f847155d5daa59c17728f8146b315d12ca8c238eed165b0fb01141b9bd65f0e6730a32b3b6aef82aeb4be9f14b993e7041b327fe9f6142ac7fd3654c271005873a6a41d4a404bfbe05b77c3d94abb1003e7a9daa5f6e52bbdba3eadc26aaa4fa43be755a6634d5cbbc6bcc87668b4245d8076af5defee1593d2e71e0ae0b269b18ce6051a6758669b4eb5a6276d280d7f870245c3b185c92166d43d6f026050714332d109fac8ffd3ea3ca511740b6d1d0e4d47ca048761a79946aaf5177c8335c3a622133500d192f412973a8160e464548a00e3261c8ab390fb2474ea18dd716bd9b062261e7e8f6e99249642e9aa618289a3fd9f6d1b925067b0580ef27587ef3fb9f23db0bd91b3c166d1be5e962b9320261cdb07a8f9df29fc698078a46b9c200ad9cfa94077537d8b1aba332f3c361bd0f8fed83762cec4838dfb2d861f48f1dc41636994641975336f0a5e667964951c40aa070a4812bf1fdbb01d8d0b73b37c3886837a06522a61d8f396e4509eeb0d238bfacf05ee7afec6b713a22be268a8944d7f0f0a44e3480e922bc0dadf40caa44542c5a2759e4331a975ab10f9094f1b56bf23cbf8d15386ff9d0c5c47c6e0cae4ffe5046c4da0ed542ebd3f8869fc872b1b71a866add2f050d7eab7d293eca5a86b072db2db7283091c218eb969220fd5a3f3d18253a23c0852fba7520593d00fbb7b03a783bbfacfea0b50c41d4549a5c3582f0299f0d5bfd0f8724f06fee71eaacc90724271b09b482a3ac4ab3f14b5c4b356a0b87bb83c55bb812130ac77fff131eed246bbb5575c355c241787232eb2c85c09079d01d20f1dc3f04f58df90becabe2ae8e4dcbb450b3e207d64be969a4fd136a2f8a051240f38a6f65210a14e3ce9eb575fb0774b838a8341fe90959a29a80a892f5e86a819bcc41120162f2248a13da26ef90785f240bd398039063e7d748fd124885cd07ba3536280ba47baff810a84a828e146b5219bfd08dd867ac61d67a3a85ae181f3be78e15d4eb74737bf7f79c168cd836de2c8bfb2e21d4fcb049bfcc594607c425de3ac4bb1459e07dc1e28f1e02e547007825fa1328034e7c2e18d03be8b713b4f21a24cd132e14ed7cce02537fcfe7696eb7b95b8ae96c0b6a8e003c9948517fb617be3ea21f5678336078c62c68b460a88541e235890a90b7282ebeac89c44b700d3f1513da84e1c9b8fa5819f5e6dc916acca2723db81fb1557659c8ccbe2b9e35b997347c4b5dd168d0ef4113a60c1e1216114d7d60585ad20f651c76abaf908d6da96c20017f26f6702ae133d2cc70bc65c43bf1e65f697d2e916007cc0c68de275055c71b0b4a35dfd21fb7d64633a04db23f49d20bc194fa4f72c4a5e08be701d0f325a0b1340e3ace5c979606571cfe042cffa80ca1eb922d362d1b968f8750f1b95d200c45e9ddf396b9f5563d7b47a1f1fe7db27d31bb6f415b9de9f32950ff0b443e35bb8d974d1fbe2029657c4334db586e8c440021b1119a8c87a3430e6e8d05978a21ffbb56347c176834fb1eec0da0a6807743e814e575f3a1173d6e11409af62e7ed7d160ea76499b3849dd551ba9ebab0a1e4f7e89da279e443c112569c9ef0848e9b88ff4b72ffef58c9eebbf0073fa80e4f38892c6fddf1e9ed68a44ec631e06fbc12cc8106d7ce3c89172f5fc37e65608550fbee699a7e788cb95e0fae5c8a36ac4f47a6207fd557fdaf3cf465ddf79da157652a67562f66683668990200c75138a4a210e439aa8a249e804423807e69890ac91c33febcbb36a947dd85a2106f22361e9d93bdf7a259a9c8f9b4e0818e432ebf4ff1290b31e282237ccf5cb17f42b8cc57b9e2d73db46c8d8175dcac384283f3d841c802af8ab43051b9c9ffac2148e8da667ca6a2b10ba1ff67ee4062bfc5fa66ae5d7a542dc197bd3bac25055683d719bbec07689aaa7099c3c00558745e7c1cfa938e05ceba3d5ee9cf2e0b7048750045011b9133d8d63ab2fcc076c41fa26bce3caabfcb5949af058951d9ac53c22c838d68a4221c28b3180d38f1c8b46f3cd4d9f3eac2b941dc718d40916f8311380703518ed721c1f042d7d18268f6e2239166001ad003339fbea7c3fe9c9a203a50e3433c8fe633879918975a8cc8a5774411bd959f69d573b791a2360bdb8c1ec23f7d5e0427b7e6780199c81530dc8d21c12f14a024f03e220a49b4204c88012c48397306736c16394d090c096fc0d75f5f1347e128077abdc0002016900d5fd6ff70e360b6ffcc1f2bf3c03b6d5e1496cc58e8ad0d6e162847c1eb282b5909620187e6800a636038096b4d4cd9f097b8d2903e531bf674e9fb159e34b1b1050521639f9d50f083fa5128c1ea11cff78f022f2b9c6729a1732e93258cab6f88502f6d7a222ca9ff41cb81703726a7f250193ce455ca70f3f918388562e19ae35e374d6aebd4763d226caa9bb28915577eafecde1ba5f28b47caeff0fa4ac42608066a6ecf646ea4231001d0386743d6462769f81f71967deba1117282c90958d42bc335215a451f3fda094e5c6a927a10c10312188944cf17a4706c05ed4e344c37d3f3e0b50e6f3abf3138f3b5e2401c84e10de31f09754988793241dbc580e656837604d156e3d5abd96dbd549e9d31c04b4ec33de7418904545294910ed2f60c70d3aaf74325f38e853b9a4beb41ee442ba49525fee31c2d4e4f2acd9913a7d584a13e6d793818b80202ed4571a626e7bc3e4e9fdcd3f44521049f63bad551b6c733f37545abb4dc07668a2f51e99f35d4338de4638588c64d0a17619c96f6920884441bf7d563c744681108fa96223862ae7bdc4dbf46eb1c4adaa31a40ec489ebed66921e3d51de1a31dc6d9aab594dcf94eedee1ec71ca4196edc8a7674ec33129bd56a616339bb502113e16c4e888b5ea4aba1c5520051dba1b438f691deacf598c3c9151c28df5961fb637feea239dd576582a441787b5f364c864fca9c4aa1679fef747ae3f488fc032921a30be9ec244585a2564eb7065fc6150d1b621b4026013e5abf5d057b53190f08bf10723373487a637355cecb13dfb6e29313157f248c62d3dc2308daf3f254f06f3f70238e671e7d9440d80e6a8da5ab67db447d51c519121fa412fa23d011afff5f8c5c0be0fab2b2da751d0d16adaa32cad97e7b753a7ba0111ce340114b38145c66af14b10f34d600a5782e15790f72b7abe4b214d040f843e9472679fdc9df40679c7da2177a6b9cbd087dac25433e9e7e549df68eda3221fea3f30d081914b7c9a204986c846db6ee2941661e60874f8b752afa147b6440c53965c1aa75d3dd457c9afd7dc687829e85c384efce6a6b0988789320372eddad3e957c20b7c03ea4e777219d0b636442f2477f060dd6385946d2df75bb47cf4f7afaeecf1e57b74ed179e9aa025a29e435fe1e7f7b7c3e8aad1e88c6d608bbb49bf0c07457f9820d8c9efde01ef6e8e72a8441f6f4b98e02c9a527ba270bb0f922788b11bfdf93fae5645dac0a6352bf39ef36729244a0245acf3ebedaa4089bef9a624ab56d18f03e602f773d8e6280ba3635f55db76e6a00fe2bf95e564bd954f1403dd3be0cd9ec81f185298d9ca704de1ea97f7694a973b1f5899bd67f12efe210fdc0a670cd4689cd43ed8e56383b01b84074205f22f47b8933f5954018ac8d98e61c6573372978f177938c1e446a80284e0c28d23103adfaff4b27d24329d1d685ad791b957dbfb0fdce9d208adeb927d9874aef367c0004c13ae0a86fc3fa18497c5fd8ef2d92efc176feb21ddddb02510e9888070ae8a517ddbc7742bee4b3a1a03a03caffdea17fb3c886de783adbd0750e388d207babb1e7de82bd80ce0bed9e949fae977dabbfe6614a4e881d0a51289672a5ead9a75dd32238660e11b59c81e48df6a45407582d593f8b3f34f79fe26aa040c5ce6849287c31384326c94edbb2744472929b3412b4b084b996b987afcb18c24b8ae4e0550b5c0eda1110111a011ea270c18852e70fbc38b29722e43fb0db79bd7736b1f0fc519e3ce6da2acb6f297bf610f9a7bdc60bc318ca762af290043af03e799962b1018f497b132476b87515dbebb7eb14cb1c28e587c58cbce3d33d66e9c90ed6b768795c83542308246582d4333971824364a40ec7c3990e73bc0b9f5d4ae59145dec0809fadc3e0160475703aeca8a9088530bb7c95d4aff4b6ed6c9d421240b7b815248c823f03397c85e2a41c29a94eb3eae0232027c3d82c9265dc480afa612fd78159eff068e81da3acd5a3aecc341431e9d86a610f2bb8e056707cb3b9f044aea4178e78bc7f0d062b51a77b41e6932f133d11075b4861ec78b9c2ed8994e2921597a07a488468ce35fa15ee4a5115a5197f62a66329a7aed8962d0e4fca1e4e3e7f92286fb70cc29f96fe414f43975e9f1379323f2cdabd545c5e6b6391afd1f2b30ea39c5affb62ee9f89ef15277f7dfa2ad35921f0b3d2b7b87df6922f8dc7c4524ed7bf955a554bd5ab27b7e761078daed1dc56bff2b50643c44ee6a54dfb705469fcc90078438e330365e89996268fa47d9792841c7b09f9df2ec14074af02cb26b708c85943cd94364167e06231e4ba6c31e46213787ccb67e6fe47cc5e3b6bdae95d6741660898ca966bfdc8b434b673385ee702dc321c93e04563cf5cf29e95e190411f7f1519b0a3ce6339759d0753b56028f702dd83be8c7db0893ab19dd503967060607b7425838271edb2cd2dc74dc530760483186771ffd0c9bec7bb086c4f420bed030b5424c1f781f3f9582156f54cd54a8eea0371bf2a39fbb647a9be623314c706390d2f1cf636b883c543c08ac7ba16aa77ad9a4667b93cbd15769a0cea2c7e92d600cce287f8b73c11497ae1f04b54a482e9a7573cf022c468a2a8b5b02a714cff14ac4c7bbf127c0fcfd47f23f367226806faaf7682b73b178e13429154edb5e544c88fe63f4305ff4baf5e7b95d0e53c4885053be32053ae00db3f4f052d4a30b05486a1dba7c1ae2aab3bf215078dc9a632442f09d8b08e9b7f5106eb72db85f72d7c493624505abd09c32e6cd49689cd0273510ff736057bb1d80471be62c7fc1507c11ffe57ee1147eb8d3e2cc2dfb8a3b6006d9fdf8bf67270535d3ea01fbc055c7c3640ffdce3a9cf878b0b057517d7fa73ce7f752a731676979663b6321f93e6c32cb615ef3c3b2892e16a83b1dc250f8902d28ff9163647a1b2531d843be84368ddd9ae942aa1e1a912462172cca85e91c173703a90ac2d0a65266a1828639a47313bc239eabe79412b44e96743a004299cc418c7a733d5c1d5940475d0898c96274632f5811cdcefdf03ec5b1b852b89a350eee2b0eba0ba749e872bcee48b8991781ed4cc5cdc4c5c7942ff8429f3cb818307b3e9e359cd3dadcc8dbcb3e0de0dc05b0bb814d065ceda06f2d07149510131107202279bdc7e3bd2677d51211f5865da3c003ba4948e5160db9404d879ef3d19f6b420c1613f10a2dd068d0424b18b7dcc36535aebf215aba4ec8246fb4c833dce17c83a06d4a1d00960bb1c51af5d1c57e5fe237a8d665197b2e42c956f8a1b262871ca2e791d1a93918ac3450284f2b143197a36e303a9bf624a6d445384457a4cd8bcbecc86e83af04a31bc05723f1e6cc7728360f2905e2586fe02952dc6489e33859abb869df56b6d6a46ed518a7bc9d221e58ee918444c0168e74ba8ea96415cd1390a56837a2b41a6a975af0c830bfca70a4f25c644a5c83016fc3b945f40b67c40d4328fba9aa4e6b26acfb27bc3c9ebcd8efa820d133cfb260101ea5b83ae634200bec90366eab172bb63cf01838b26d2004aee5829c1fcc1269863bf9ac7836d1f74f9e9ec631a9513d2012752c610043966be300618ed5aaae82b05e2ab33c1e0297fec247169bce5b62ffac3614721f4818abd7943eb6e6064fe2eddaf09c2f31572e26ae93bb008b7ae3518346548de1db3676bacd119f3c2d2c9d35e3794bc0d95d788b785627f69534b48c82b073d39ec9621821f0221cfb998d3fd1329e0586fa1909553da42c83af706365a35db5cffaad4e830bb5c71f77ce9920a4c7029a406f6bc022fd1a3faf7784e6987b8927d4e5e382254cb38320fc2b8f2a7491b29a5f33eca1e84fc073936b6b53356a808934396c738ba00034b8786e6a8868512d614efe9b33c8682ae3e56efc4832a1b4cdc178b0f865e3eba0e0721ce80209ee56f076ea7b622c39981a0243e5cd7e378d8832637f13891a76ab7fe9d8a5774e5bae530dc96bb60eb81ca340177e51a562a3657b6d51286eaf050dffa9845889ec3a9bed78fae9c0dca40e52b31e04cd752aa97c6779f0990afc40e679fa3d10ede239c3203bf17464b0366172fcc7647c72ac7097f79a822194c0e52fec351e9664096ab892be59ef697786a5d9e1d91c768037d13b4a289767bd53f241c499a13f13baed4b7a09f44ae5b8f2d4c74f1e259f9fcfb987e33772e1f0424e9d0470486df547e10617e8a08f1fdc641a83eb4aedbff2a2f9f4aadf9f6d1503719b862958c42b4f1218674b47929b136aaf84cff9de88cd10954e2ef9481715f941bc38a8db262635971e359a3b1acdd6856d148d63696151bcdaa1bc39a8d67e546b0cac659531b07b152091549c06cac0213b78e07aef383d4f5e0753e409d1e5cd783d6f9c07572d0ba1fbc0e7cc05be707af7bf8ecd479e30e1231514c69cada36dbfd902c8f19a6143d8bea66a014fd10a01debdd63e222e0f2b33930c137ba7be763440d00c7b83d756c89cc02a6060450c696804245d50ea43794cf10b03db03f21ea3c7a1b122e464cadf9ff1400fdbcdf1c9e4f7f043b4da142a898c56af9454af79d857782f7636e58b4c8b5d41536c325002a5be2f4f864bfccf4ca293b152b5c1a72cc1344e64996d167ff1cd4d774134e2176a01ea4f9f678f3e5ccbd83e3343782d9f1c87dd193828aca06ad874e712f65d9629f929ca9d8fbb16ef6b4d4cf2b12e84e91ba20281cc94adfe7db2bbd21509e6e482e004853fbe03f5a0e422173e95133eaca3d979fa0336f0477d478651a9a9011f48aa8b053e1a00a1cb6cade011b03225cee9cc0227675e31d3e16e0fa102c2568f577d0a1613cc4643c24d0417a7e0b52f1d038a361f0722c10271326eebcde6236144bd1f5518c3dea46eedc3869e90942de91ca9bc2ed7ff2428c6e79380a3b1abfeae5dc9a358887f90cb10188e164e4148d8b97594a01b85f6815e2a483f19577335e4d91bf6f784e9e0ed0d97dad7bcd7f0ea53afb6be6717f8e79b6e86161245a8be2811a339dfcac83db3d8e4720baff9e0d5936b79c75713ce2c68b5ef389cfe8fd9d683dc112fbfeb2fa5415c74e265e4e9fee8663093eb4b48bd4f681800a6d6191d17351c52af7ffc8d2164001a9c4b20d9be4781ccfcb0a8ef2a54725d3b9e00568bc4a818cd38dd5005abf2ddc3ea2b79378aa0f48424ad9e0e7a09c8064d2a20c111afa95d31ba7113aaccc9067e6c8549d640dd307d79c53727e6e1ba9b854a3204901ec76db04eee47e17540c092c4eb500ad60e228b8a0c6d5c8c0ee9b59526b2128e1c33253624181c06b51f0a09fad74efdba1c2121eea0239cc40e9130028bedc45ec61d975ec288fd0e7d2eb5af9274a0d6f91227d9b1ccab9d0154845b639b524ee5b5a91b2eaedfba29c82af83a616b9d31e6d8efc9dfa4d2513e8ded4da8b17248e2e08687ba6189f3a96ad847a26de7f3659b89c77fb972868e1fb547e080edb9c42edbbec1f422f7d46eab45d1ab5713b516ad95a22875906ee8c2857806c4ee33823fefcd92df0cb8658ebd950de6b58be2a1cf4fe9aa827f06622307924c1bb4b1b3b59e7b8d5ddfec7322387820884cfd860174813567db4bd4027a91a49a7801504782f37806426f0e19f74a4f59c2004627c1df0ee635b2fa6b20f3a82dd761ebad1272e58cb1d82f7965c320a0306b3d767ece37077d5cbba293078d32f357f0eb0b78ed9cdcd921b5346ed56fbc43e18918e8c772875ac17d3b30658a5c90c0621b22d2e4ae82b8648c146bcf836673f5eea9ba5896051416d84268f7c3a5f941d615f3107d763a26d914c1b6f04e59fd8b3e88967ad9f94fd6f455cb8b571ef473f5ac9a1e44b760e4f1ff15ad2ade6ee0104a53a98af813119d4aac13624fd6a466195bf6505f6941f25d7730bcc2e08bd0c15343665c2fb2fbcefedeb848994b6b0a927819873583a27c61a18cf353aadd06d2426825469a1b4c5fa3851e66d82aaa6783efd75b88738e2ca7156e209f8d75355572d5a65dcc617122f112493ed94f263033ca4e908ae38046c3d8f2bcce0ff1ae2c1b26db62f4db4e2dfe08cfde841dfd2ded1a96b6c15ba2ddd00ec5f49d6ae60496bb8b36806469ed07a2391d2d692bf3bafe08aaa1b2a5053eddd499dcdc9b323ce93997db5123135a8bde95285883f3e3d0c5ed3856a52fb79ef0cdd06914d52fc9c1c00bfc16a9e9248b2ee7f85ef7a1c1775a02010043139f828239b722178474a97876c431302dd573bc85f357eedbe6d71a89c2d07ff155c62688582d18cd591e351b3faa04b17b142f16ade4a93bc6c1f01c2b542baa8d92b59e11a88d34d0b6f47a7665e9174f7bce57e62ef90f7c939f2a87266f01b400788defaffd5aa46631ef391bcfa234ed8179a9a4b8a74d19bfbfb87a122abee14fc047e65700e2a3c0c3c3185edcbe20c65318278bc1b90824ab1de9a32e1f805afb2c00c0a156c5fed2dc98601534e145903518c896071d02e65a9f7e9453e39d1914a78d11dc2ff8390272e0225a32f75dfc239f938ca5ca29e568e99ab18a301071436ed07f672e29ea893fc1aa78368964a99b69a4820fa1ecd2b5e983c4b1685dce83d01b301c3a146ba672a763dcaacc96bc7be4ee0d54dbb35bb0c4a23f5be655dbc8e61ab15f9cce82e750673df208d78ac87fe3a149802951309f5e7b247ac1eae45d263e8905bba61a51ab771d43c2d23ffc00bb0a4fe9a8b39083b31dae12da68c408b61a00328687fa847cd8afb8eacd465fbf054acb2c674c2c244ad50b4af5afc9c1f666eb836545297a7060bfa7da8bb58300e76e4d31e5224361e4b5e45ea3b3ea172586f46a95901462ee15ba77bbcaa867aebb39b656523f6ddeaa9b6a9dcff8c12433380fa617996c1774ee499986c7e6b141e37f1d095f2e102cbdc4b06b655d5e03de7990250d3f478879ed20eaf74d9285be921c1f3472befd87120eb9a968e5c6c3bb772ec329a369376795351a89aa0d294f16eebf4bb50418448a7156dc385a5d5f5f8abdbea74f13ab2681ad49d4a7bb4d4d406c0f2fc3f0993b498cefd1991b1f75c0858f96c2e49d0124aa0e2ddbfd5b0c5995db19e23d02a222db6978cd045304c266c623fb5e4311df671f70dbacc03fa16ba58007701f6374fce0ca1d8772065779e287442a30ee2bad4e088c503d85d2a4e71d2d666d170dfeaa851a38188c399344ff04fc168f902e9bb9237e159d3b48b5c5f609f147826967f05526b550f06d5c2cdb200126a350ea6b54c84bb185876399602e85fe9a3124f04dedc454e2e8ffdac819505c6d02ac0949ef4f739ddc36733a6f7520846d4dd4aa8e618977a2343b2253d0f7e617bddc79f12b0404b6f6ff839bfcb00498ba49afc0b3f7c4001da0e7556418c0bb4d5cb3771c88573a6a4c8457abfeccec09215eea152c4831c1a024f20897660fad3ea0d2f678e9dd897f73fe8ce398babf8a87a00650a148de1e07dd59ca1433c15ce95cc18e65dd55a26f0776e51225ab8584abf80752f44ee6e67c046a10e37360e196fede0fe1038b5d2b6d1243d8a5a6a5b8168deeb0204172cd553ff26861d5187dd0b1848a192f7efc1e73453ba348e7f788f76449696fba56f674bf9d59818f58251325a4c5f10aa2812544c2fc806a14b370b9c446abf86adc2c98dd83ece888a78a8ec7407895cdd33d8cea1b9cae92e7f9f4e8fec81232064bb7ad8c37f6258d6dfd0a760ea4f4a695d734fb841b760789b1ad3c8e7934e5b36cca5bdd53dfacd927a3abad36fd7122722d0c614e4d282b711281a1ea444275157265c6b7714f6276876c5d34c8ff3ee5243f800eef3a1132cd46067b39caffaea48409536fdf4b09acc148472185d6ce77904be2a7f25e24f500527b43beb0f0efa1a4bfacc3c92bfc63a8b3f9c06759b2fca2297b65984dfd4ea47c62db8a08395a99193686375e2f4ab81257828e1069e95f4fa12261ae6dfe55896bf1c4321eb401a6945c98e234b312fddbc527e367ec353fa9facefaa4af46d8d4eb4376dcb97dbb74f90efdac7656938555eb49b47ee78eb81456501ca11d6df07c29f19acf71c75d9868d3f6a346642aee4d1701963eefec5ca446370d11d7eccdc098220779e359418cde4527a0115c1b0b9d3d22800da87bca60184d04a21ca40015b01a21813115420377200e3284805600befbbf0506e61c9408ecac7873d75c36d222463fed21a0935694e159c31293b6adac5e28375b6e8d8f8ddfdd317467251da698b8429c019584e7e1e3c7b62b590d1ef9396658ea5ce3080697b2d0352da18366454938e8e7b6967489d75e70628bf7e02cc134956693a4e575156549ba2d2607108bb34fe78186fe4723941d11132a799e7b6a6e3042dbd6070a80ee9d048e7ddaf19ac4bccc37984909e4801772b7cd1491def5bbbe905e4424bd8e6bcc19a2d7e7cedb793d75efdf173b0b17ab4944fd7cd7143fb786b67a3d1cf298177c0cb21e82c95fd0ed039786c5240eb9f2b504e5262eac612f9872fd781eab41004a72efe87ea9dd45fdc17085569afdae8fa4fdd1cecb5e8685b544831171aa89cdfe478ecf517f6e464fde1987cb019fa3ef4e83f6a9909f7f879723085aea16a374a2f0ffac47c5980711b12f8676fea2e752ae33252f626df3419e7e358a62054335d447329bc43860836af0f8b7154d2c5dc0ad05d2af46092d0959208cdb6d5bd24554fb94bc2a9dc4960d70bdd673d6e03d6d1d4964735fec078158822b7cfe44e99ff5e8249854b11a047c18971a49cce6b061069b02de621dd49060e9d8ef2749dfbc1a3d10ae775dc7d9ace45612950fff995d29b26de8c6bf1196d4bb52e9126201d4878155131f45d15c2d58207387d6fa28ab1772a5aed866f4bb46bc1f16894589191d423379b27b37a5ab4f389a49a72f728264c92eefd8c04afa85e8d1ceca4eebd7a1664b48ba68ae99cf01ad4b861cc533b83bf6d4d679bf2e113030fb2be0079832fcd115a8324bd1a7fc6a9b45723520371f3595b21c95c067040465a69e7387bf96265d710d6cdb4e089e35add86284b09d46b6f3a5d4f7c7aca86ec6913dac00b17b00975ad8b34c07a2deba83bd630542f280b22d768e9fcf7c2a4a3a8b86da35da3e3836a0b71281fb687de0e6abb54440b77b599e58f4eb163433985b5543dc9dd784d428a667a5787a3f0a11c931011e137c607dc3fa9f04ad95f6279ba63957cd576add37bb27cc52c0c127e25f7ad67ea4db2bd43fa52ac3582e159d726c0d432201ddee8e36eee44a53ffac797a95d683a9b42488f2c1896a773af7aed21c287b678045a99f16f724b3dcbbed1ea075726b40069a31a8bccf178e12cdb11dc3aa47f494ec00015095e3b0c586b79c46f0ad7ce1dc362f518d8ec4c628270417c565b6d3f996acb7fbfad6f81739681280f7d3f2e1ab93fad83c74e070dc4e099c6db654e9a0f7811ffbac96e853990481c833092b788cc624b0ecc0557a529b771573f664216a4462a5af7e7fe9f904ea42112e75f4fbab958a2439ae0b624141d1eb5a4e5cee7a38934fc4b9483c8a1adbe2bb51933a21adc42b1b0245cb42825fb5c6000574f7d0f86d41695d05aec3f6308fbd3b3de47f17e23446f2914cb83c462437a373c8522be3422a96532bb254e3fb80dc74cfa5f5af7075db85e8868f85b72e5a9c114e8bcc5af3d9680992312a0cb90fef5e29c0faa58fc72c973c56e8e24169171399cee3359587e7ca71a225d0932131587174f4419ad995d433e24abb8b156eeb38de9515448ca6a820aa9beca337b53cd666b6503e5b01b08925dae3d91ed9f878895ec6097885c2e795acb11913d93a80ad2ac4e2fa2a3ca209197db4887a4909664feddd79f97454ed22c4a02d9ce0181c4d466acb828b794773819e80f5837f6fb957202ab6410559a32a97643e40b927981b7578af8f4799f19178353ed0a3b41067743e46ce76ce3befb6bee86c7d80f32595c76e7ea33d4ec87d8b19c6fa9b1a2d3f69858db9490e2400ccb71d7fa66cff7c1e669711abb1bdfde74c14fac9d04a126caf9bb5ab5a84110d5ae16007d46b15fcdb46c0046a1cd50048dbed5effe502b5858fce71fb8d55686e6e59ae21f0477f687e6f33a4df7e6805a1e4176049bc7f297544ea8aa4915b5a6730e8b2c3b2d200ff0b2054baa6d26cb46cf87ddae15aaab8cadd5c1e2d835adc7c0fcd81e42a6a72698d10d889395e4d6deba38a0bc77ff9442122033e0e247bfdb225b825f2e1891dc57b958e57a83b61083da7d1829d235e5983cdb253ae86b32bc4998c9b00a664e471163d80cd352c8066528ff34c1943a4a07322bc8176b3a9946988d3f494fe5769b9d97ebb447a4b5f60ca80073ddb6e324b8fc73c959131f1b5823bfbb85c27c4ec7a25e4f846b7e09afc532386c77ef36a5ec108e9a999c0c768acb0b653be3451f989dc2b83e0dff812bb86fcdd4050be19bf70b5ea9553e10b134f642f1b6ef2fc1ef0aae8dd16782ccec29588fb37b4fcc3c58b6ca06a948dcd88198ded0ad67cb45410e06554f9856e51880f1911bf3abe0c4c27663c7f8480754587990f23e01ff25920c8996177d652a2b8900a8c28d61f7ec89caadbd843d1ba7792b878a7dd119ecf37845ec9267988c0f3947af2ff6e118355ec3aa62bf6878c5bb41015b3491e361c151fe2bd4ba6162b93872f4cbacc439507509a418f1fd05a9aa2e77d5fecab00554874638061349471f2a0b9bf4058dadf5fdc8aa236e62641a90c2e5ee315c377f9f52d83811a97721bda5ea6a326d397cdbd17be3a81e4371ab8e59dcadbbc9ac94da1c50e4b605d5431338ce34ed9aefe851cf15f8e029c702dc1ba355f5deb0068dba91eb2c3054b42ded2ac2ba15bdb3baa42259ee1cc50844f020022deb1c72c3e7233dae06c19eb4dc0623c3b9fc9b05e6c4cb1f4cdaa33bfd033fd46904275c5bb873064f6cee7a422bc8569699f39adb506341c6f0fee328f35d9a966e9320e4ecdecb3045ad2ae246714b103951e55031a610bd89be241bd8921cec4307d29da4e6e03e89087a28d7af8a104ec1c0795db59ed657c459ad73a005ef9759977a213b95ccf20959895148d969c988825a75e0da30e990573797d1a16adbc6372ba1ea149dd10078a1cf817db91b32639e9b7e4e0d3d8080818c1dc44ac37572aa5308ebbbb9f804c85136290c71bbb7c17123bd4e8c8922294f4a664ab15241ca9dbc1fb0e06850c7d4208f48d1b3b3baef8bc2a6cab91ee60a7a8e9424968532d1af40e6c872afcc7ab75fc08e27784f13dd4f729502566a138a723137cf026ccf275cbd4c05bd7f5ec14e24ea7fc0679e5acaa1d7b01a783325b9b1c5cdc12e30045a1c4a8b5328376b252d93c700544bf93c224d4a29661e074e05b80c997ccc3f869b27287a575ff300205abf720dda05442179b15cc62f4c1e7099b664b095743ac5d4e0953298ff7526950c1a3c728750fb93a77abf94e66defd6d214ae0bdb7a0fd7085bfb850bee5407f3c9dc1a973cd8a0607edc5e042939c59c49009bd84e2e5886427c7b26864e1e51e53b7a2be111be3855370a067c22f7a65af10c5c7307db899944755676ea0fc52a3511c8c302bf6c2342369bef5b265b11fec35b850233673cd70a0491b91a777a3ef3d634db61532158695964da4958c9b01b66e08b0dd44eb26c744535939c9865445c80b2f6421efe5d75299afd016c8bc60aa1783bfc0a356ac17477b2a85da4622d3cbbd496feb5089a7ec7b81db171f97277dc24ba4fd3fc464d603c1f49ec032d08e742accd7542671d5914c163af8208d1aabc614d264623ee8ca4c44359921a77f9ba92928d607815b8c6461f983db426d1d0616b5b333ae1dcd392977ecf181d5258d5550d4b6b39bfdf14596b19bb7b6d77283fcae385eb5f1e2363cd66987f2774e6a3daa64230328d1a8ad6a8accc6095110dc05d4a52b4cf7d8d6aa7e849c51ba043e0a823f57295f66caf769555c3e24df49850f388912941b94648012d5be687e576d3f5bb586c56654f748b48b26195e3f52f18088fb0beb82affb87d7aa0df53fa49ca086edaa16b4614b543d3d6652bfec05f654725d0b9124c03d278efbee4e6ecb80875e511a58ecdfd7128ce61744963c6e8f07ac783cea320dadf749066dbcc391fac9a9bdb651ff26838c9747b7cd4876ee717610ee7865ffb4f6cc853bbb5f6db5fc7f0b89d4af15500f2db06518de6939a10af925da60cd3b235f128bd1aab5e070d1b7643cc7e2b885be673d3c39ef359c6bac70c3104a3b5f03f3121537c78fd7a8aa6a4cb3fa77af704301b0c87f2fa5224f87711772e62e2e9747e52b7edbed1d8849e03facdde91592090ba9216032cb8aa12f76621d24bcbe42f40247e91562ede15ca60e0e73cf36e34eeec7d0b972db486382914e00c06b540a96a4a1a15754213f8e9aeafbf78ae17fca807ffbad584ce691ddd116db74608a126b4b2bb7b07280925093009878f64c89faf29e986c70870a36f874d293c75f83515bdc9e1e9000e7f0570780e85c363130e2fc4e4f0480270f82543874761c9e189821c8270d8a1cf811c5e861f87aff201d209494a9492f0806489c861cf7d1c7af097971e58f038fcfce8613db487df083f720880f31c3ea6913f5fbe3efc76aee3d0bbcee12597095485c69c1c5ecbd20978890471e4a60568a5c75a784e6e5280bec94d50b4d2634f7e23372140b372d301b4d2632a7c959b0aa0b9dc64a4951e23facd4d457a2be0262ce040501e04010f3ac08914e0468c7e24173d96b50d4977412731403bb9f4177b082edc85ddc48501e80fc8052231fd0de0db85105cb811222e0429e0420b88b5a0939c72dc8fc845d82100fdedc75010daa1a9e0a024a7adbf1d990206a03f146e08f73bbef7d69f0917da424f7242417fae8b1082fe727e11427892d30efded0bc004fd091d85c75c3872895c2341416e420b408f692a5c0b26c472dc480b381ed482b621c9e94f720aa23f13de447fb10fe024402181059d9400edd4d2dffd072c18394c53b92c10799213131602a03f120ec402d01116989c080b1f0480c883b0a0bf20076ae1421040c2139de484a33fd881682af73bf2c308d049582204c87fe0273f9ee404a43f58d6c2d07f682a9789fe7edc25b0030101e92fe84234150e48c8939c86f4d7fa124d05ffd0df8d7fa0a9e01f1f007d48c8979c04168280e80ff600682af7c87d0284c89320fa0b3a094f8290f0242798fe700ee4c9cd8d3c810179921311fd053913fd91f0007cc80186aca0930ca09db0feeec7e33e3495bbc29127399db082fe863c4853e1562012e463051efae37e64051e4a8e9cc80afa23f2a027ff29c010157492938dfe4ef85ed74712fd057d08921f1f9a0a7ee9cfc77b682af8f524a720fd21c99ef4d0542ea7bf1e370992070505fd04fde027e9a1bfd579f4e0f1242724fa0b7a929f23f990158c9c707d5c252a10d19f910f5181c89027399da03f9b2b51c187fe587fa9e0e3f524a723fa23f213f437e44a5e82510929e8240268274d7ff73a9acadd4d295c9e148ca460444b21058d3ba2a96429e8efc88da8f022994ab69b8a4a20d29fbe0e4de5f2e828c11629c2a3a960ad3f9eebd054b036a2bf9aa97075ae692ad7d61bd1543823468a1829f224271d3afabbb73a749ee454f567e4b5c8ed4bd054b2dd9402bd3c97a3c21dd11f7d099a0a77a4842739a9f4a73a47c4a33fee5a53c1db8624271efd24a7a9bf239ffa2be1f442b3c90004800293000c2d394149122426e4111ecb247c67a00b65213f9247780999841bc9402f92859c481ee14332095af2205ff0411948c3ff64215a02215ff041f2085a2ac917bc0879e72164efb00c0290ec13634efe23effc83ecdd47ee79650f64ccc97be49df3c8de79320074ce5566cc894e72ba863bb47422efe826a2eb648f4877398ebcdd9553cfc9d97172f7d46fe48c95b7e39cfa2a6757e50ecba6a5969466cab4dc225fd066bb021ab648b36bce48a0c4022936cd330b0d5034d833db1d408006061269861b2b0390fa059b1095741312923f14683bec0bbab7d6c3251f30952f68067fd08c9671eade9b5f73f6744d3f037fd04c14970b6321f9032bb53686242fc8577dde00a65f50845d1152cceaba6378010afc052dc52520848290148989813c3546f8bdb0a504a955fe1092af0d52f943deec801ffb87760ff5845b9b457bcacc9eca1e94e5a0fa2053b21d81b9a98439828881bfd4a68f6bb6fc96af6ce71e10d1ec18e26e9ac0b65dca6ddb9036ddb659bfd42715e035e814358928d6180a5f767d9124b33f986606f8bb512aa828a5690618459f484a67a500d296b3d62921851b9452eb5c00471d0586cfb2419ecf428966d494d97996edc44bc9a4e4de235fe9710f7a1ceeec42d2831ee7b81e3d1ef34e8f2cdbe9d123fb0ef723eea8c175e7316731c6b8f3e57ce736eeec1c764a72ce390046edcf4a972d25e731ea2931c61863ddf9dc3bfaab42b9d89d9093f3c5ed8212235f59b7f32c6f3b99ae54c897dca94b70c8ce65a6eb17f9c2227f2a14cf79e83800bcebe438941cbeea3bba3e411f8946be7ee0b2736c789e9d9d9d9d9d9d9dd73452e0fdd5331b03816f7cdffa3bfeb9f60ede3bf9dfdd3bfad3baaa4103f757d15435f2e78cfc992ad04e908a334e789eebb8771c5dec7b2ef00936fa3b9975a83594af9defe84a867cc119a94a3fc121fa3baf3c348ffe5c00d03af487f74a477f7773ab1c38f42757aae3ad755dca75eb82bf8894f445fe7c53693fc91ff8e40509b67c85923fdff2144afee0b83c15237f762e4fa326508fcb53a50924cbdcdcb8f987f78d9b9bdfc854e97a87fe5cad7f5309c7639764e7b0a34ad04747850257285d01b0843f59e6c975f91e19caec3b5996c1b98efe2ece3dfd619ce728f345de8bb1cbf5bf5e30d817b423ec7068fde5dcba32dc391fc5c91fbd915a608837b2120c9407d7a78f93576518625797b8fdd5a5a56f9a994a53c96acda30bfea81255923f4b6ab63c851b045ac86c18bb376960798bc42375aec02f36ba2ecd6a96d5d71398db10ca026178d8c00221d8df6f012b78883ca899b7c0123d7c07353c3019ac70c8384f63e424a464245314d04d0c7e8b2d180c16020c0683c160416021c0603098bc71ef4ddd7befbd5ca4f04e209b9818f8a548908bb1fcf9512395d3470a31126675030c03fd91baf7a6eebdf75eeea6eebdf7b2388ec25b44ead01d1e3e71fe7407f2f011a2037de25d96c3ab584b1583e7689c18771004304c1783d3255f31062790168313281583f7d4c01d776f90345832cd73624ccdb4ffba344e182c082c04181c026130d85dd96b56579140aceaa81d63d448afb592dea803d35257e75e1daf6b5ecccad5f8aaf47256522ee3a0bea01fb0204499e783abf38ce7d2ecf5c5f3ea51af65b97368b7f66a2909fba0c72bfbd537bbbeb4529adddfab8704cdd4eba5c15ef5be226dddea7a65daccd125a95aa5b5d2d77dbd347d656e9787069db2fe7eb384d43f1d7c09947d2645ed6618a31986d938a29860a8992a023c85fd9285a065032d6e9cc0dfcd1004db4b93e1533ed205914bc041261ba1b9c88aa399881accd13c643765d764d37da6f458762d27ddf058a6951ebbba6a164733097bc7061996c803f4d1b265cb1c76803ef2ad2a7006caf121ff08c1e1c6bc82ec1c8ee61176d0c01fcd22d01c420e8e66187ef2389a4168858066203b8dcf179b067f540747f30f56991d1ccd1f442e5aced1ece30648981182b504d30d24da28022bd910a1b45a228a85c40d2a2598f0129d1250ab2d565880e34246b505aa025b5354b7050515d09ad0705bc4e021c504d4b685064b978bb6a58d1e889a404a82071fca1041c46529892474205ac24c1250e84094849a24ae50524a02044a444c984922cc0f444d5c72d0783f2c1de3964b2703d2019e293c2df98a3aa49981926ad75a5b2e18b561176994f100e9062f2c69d6e841039cddd0a305b8ab800722c02c0cf808036b4df4e0a00a2270b621183442e0031a5bd88059649090907e40a38c1eec5cdab08b36c6ecd71768a4410234d668c9c10e5128a17936c4062184104208218410420821841042324284f1a201df15f830836dc07c8044163d5650851558db100a8926a640c28331f0dd100a09227ac4e06c43282480f0629352ca29a58c5766526ee84329fda69c73d25aa994724a29ad94126fb9240a4a4965a694ca2ba794320535958b0a0c2184104269ed9c41a6a5f605a370a296ffece79cf39bb79452fa694b27a573ce7fd99cb37ed63ae7dc2a47d3e06c2aa7def47b0d01ebfee69c733e421ff8cd39a79caf5965a5130758d21aebbdf495c4a5524e6ae9cd6c5d73e9a51ad2bbe6d24b335bcfd09bd9bac6d6ec525bb333d5da9aada1d7d635975e7ae60cbd6b2ebd34b3f50cbd99ad6b6e5cc9ae94935abc532f6c5a6d16630d5b461abbd45a6d4699905e88823489b42012e88b16a01fb01e286f8f84e5095165d2c2c46491b0543da318242c99ec329753a9a65f28e87649ee6f778284d9ddeea2180245a6881441905ad3bd1913d2182631910b8d484abc540ba1cfbc4d9a8b71b4914b47648184d2768408761602117e80f6c7ed6a64d7236e30220c1641881140ec29718c1016f4579669b892704a08e12319f8c988a495b5d6c2664f7cace2880d5ce18123a8382266add2b6d65a6b6d50d06b7f2da62c76e02f36e5590113103d6bf0878384d9456c20db6a8004f3bb94527a420c3050031a41d040882e4a88c97410812f6c43c51a5bdbf2fc8837ec2f4811f8655b11ec0f8eb19ab59dfd6177aca5d6c62d2dd8918a6db394d9568b6d7fb1eb2f180cb67d90d01222226eb0ed9f403962db4779c1896d2f0306ae40d262859219db42282268c0a4650c9722b874b9b22d84ca0017f6622f62b6b50fb2d65a6b7b64e1c0bd9d076012a679f1210363260566071ba6cbf562881a0629c352460e148bc572831fe6089e6a96a40bc826504f7488206213305db238819b8c008d7429fd8075288049d8186a18e1011835baa8a106163d30c0aa0dc1a8d1850f23f0b6211835c078f980ef8660d4308303141217c8d9100c1b5b94b0fd6bc5bb6950a2440d0b54755cc76198109109a2cc182595627e3b929b3eca5e77b6b4cdf96d9bd56e75d65a6bfdab6e72665de04fa7428922034483f47a990934ed0f50505faa8e1ff0847042a8a5c45077f698c2b4f6846b179a98ce16a4b2473e35c67ce268ac8184a54b9d99cd7296d15c245f2ca4745c8a4af0009882aa81e16397c472306315a0062ffa090cb56f179217f38040ad0c7fec06c47d7e3e19c357e5bed12f587be27927962e775838252ff504f89387eae2cb9697f67572437461c134a286e002239a2070902a03268d96c833bb21b9ab974a5d0e490b16c102a1845862df6cb92447d8f2d5a60ea14fe5eeb32cd24aebebddb093ef5e9044c70f986e4be40fd556432d44f312263045a26b75419f3a035c796409f3f5d556faeb512d1443227bea5363e827b00b0ef194c46d2b19ae0e6838d82e1b0eb5d65a71b85d34e104c5e102693629a5843e74d2092f6574c176e50f84b0a0bf30b51267905286b1d18927d0937618a38f59529b669a3e92f747f38b503bc618e352f67b6917a3d0de69aa539a462c942e118d80116ac9eca187b14d6608a554c9cec84a4ce84911282fd4280c0485444949f7d99388b6db0c6348287dead2141b78098cc92529ef0b6212c6e6375967ada78f59c4e244c6a6ebb387c296b4826c7d1ae36a4d2a8416586ed8794eece78de636bfcd6f9e91fc7699394f49dc5edc72c3edc566f6e2535e6ccb5ebcd985a49682cd9eec3113ddec37cb32884642b1411e2fc2a889ea679452a289346c90e766cf3aedc5ec475ed41e91ea29c098eadd8faa38191d6557fdc8a9283beabaa77495edddab7057dd4a0cc660ec1ec6b6d82591303c8049dc148624f644a90bc978b3176b517ee23951a9b25ffb5de9a3ecde8bda48fbf58c52d9bbd76d4bcdab5c5a50c6068e422505c1a691524a29ad144a144a29a5dacca44501a5748b0d0689b4a1b673d8c01149860914af1ad883da73729f7ad17d4a577182af72e474a43a3db2bf87faa83ef5953e9a3b5be0d5aba84e6f57ba4ad59e13d5e9bd4d17a9b4b73dd1d7a752afaa2b94fad41ebc7609460536f8f0840e19f822765f349f7dce5bed44b347fb4b0d342640230c24ccd02296a9741558ff647595a96151474e45a9cf1fd17b2d2a0663993e4a69cf89fdbd077591fd7d51fdfc07d33ebb14622c3b9c324e95071f4351f6d4bbc77db3077fe4b4fd28f5ec474e9d2ea2bf3fca9efacc1ed41ea73df84d17ddd31f51ed412d03d2ecd17428616ff336ed39493dbb51ea993efa117dea463706b5a6ec4252bbd7525a5cb249d4ad84edda29942812804bb5ca5a291492b5d6babdd65a5fb3a7ea084d69eb6b985a19207f3eefcbf142945a6bbd078740b873c021f465e8195bd40287c82a1026f100a39658041c120f213c03a11816ece18bc0a86311d0876660533172cc025f458cb1650c228b0cb6bc05e08b7e0661c6968f715f1d97a00fb5400db67c84102ecdb02712d22c6385495a3a2595b27e61938254920542e3820637a8ae6c61c5997a450d2ee98cae0733d6e4d0829a43adb5561dd81c542c11a8ba484d81820c18d134031bd460072796a05e10517be0806a731b4205f1441a323d5833704825692b488091c314293893431850c82eaeb85c9c01020b986146cd813e08a61fa99b1d90b698316306f26c6e674ebeb07cb9cac03c32e6e00d4d087da2a69312d9aefc81504a2965bdf9fbb561841d84a2505c00cdea05a00b019248427290fcf9a07c8970f856ecf0ae7d781da1c3bf051e067c012f177e2800f6c307d97148857cc18770f822120eff04e8f02c08393c94270c48c0a144913f5f130b4f1d3621e0d961910f3924f2a0c320ff6152618b017e806f874d2b3c75f83515e0d9e157a484c34732f2e723a2e5927cc11b397cfc42fe7c418a1c3611bd0bb596912f49867cd517409f7a9d31f017a3caecfa28a64c7d952f1813c9c817c021364731d58ab59672d9edb35b2dc3feee69d7823e57c7a5ae49a6a5dcdb674357dbfbec6aef664deceff1afc6f28573fce14f71ed7b3b2538f8114b37021438fb1797602a95c588c56adae52e6ec121705b6db16427e4ec387f566f5b8ae3ba221ccfa1ab38d9c3fd45d9df1457a577ca989a65bc21fb794d4a8fe178130e3d0395426892147243adec35529ccd53cde56c9e69e4eb862ad51b7942654185c6d96dfbc23d51a8ce091597d516ccc4d26283e50610ada51b1e709472925c9ccd1509c71a39d4786974bcec50a1a3cb0e343f93cd6832005086e70b1e647a8cf120ea25a6078b0f9ca05c7e9cf1e16c9e6640d802630a418b085846e06ca64004e16c9e68e4ebe69b10cee6f9245f37f7d10dd93cfb06c4470f10204080bc80dcb46ed8701b10203e3e5aea0201726f06044896d90a8290114408418810988f8f8f90880195a6bfc9ad9c314b6391af8a5bae1db18c9005c3583d527d646aed2cbb37cb62a09bde52aca97c6dd9cd0f80adadfa223457f4aac8d55b6a7596c5b8713b460d73b4e02f46c52f7ef922ab8f59764fafbecf3ebba1d68e5f84dd907d2a73406b47fd4dd9574f69ed98e9f845fe7cb41b8a5fa895765a0a63a6fa64d0da3bfad1fd4531bbe6c8f214d8bef1cb06a486fa4a757d9623d1ce6e65c36e5edd85eae805880f187f3a9773bd76762ecb2df9aacf9104cefe71bb56224bb2f80241807b491623803e707f5b48de7b513856ca6eefe918357beaeb925e8a51f255731481618cd5323beacfb5a18f1af88b513baa4645b5a40757789c00771b42618145191f5c808a170a7085c203344d6460c134051630b0580a8389ef2d9376564a2b9d954a2979a49411afe6953f334a29a994f272369093569b5ff313c279b3219c509b32c29b26ab66361a279a29291a0821a438986dc468a6d2362a3d234633319aa9b48d4acfa06a6cb322118295ceee55ede479e6b287e59bbb2648d7b8eebd37621a35b95811850ffb834f104a0c0a9ca298d00006d03cc1c36cf292e68a2536231ac8ac5146cd0d67ac985cae5043857ca0a905300853060c282618b7032ce056adbde280167b75ba5a750087bdbaaa4bb282647457acb1bb735d920e7eb9820a1d5a3069a82b7088612079e1e588e8851559d02ff78c0d60993744696ddc0d285dc08b316b54c181304e40c5ec54625b0e6cb16d6a5b6badad628b12363de5a1034f154d6c7b2d151cb0d65a6be9611535dcab04864958e67401cac53e09319d18a14622c91eb8bf8ba948c1929323a27898c1131531d062861717164a2a8049188f1164a0870bae1b82d98193193b90c247961d6851811d98000736c2e031c271411457ccac59139b37aab7d393a79e8c41d86403c71c5faf18c5e002671b424df18507434031850a5c504a0821a432d628bb9871ce99d93a29a5349b76d639699c73427875d8a69c137eca0c3999e79748e78c311ecacb28c116e99c93b26084c1e61ad8a54c770a241ec86092228d1cc6589a820725319ea600a20230589ac2081ec6809ac2e90632a26a193268c0e4400d17186f08260d1df6771cd2c08018692cb1031ce016b918bb18a59c52c6edf5028128683e79f5f888446e76fd5dd3734a2112ba604bf92412612182208fef16391a6f2bc7c2b3a8c50d053b5c601965193c378c0c782106195e908f686478216628398a9297d4a7f6b844b36060cfc33875f001dfc727e910e2834f332249b821d004726965308d724e55d376d919c9d8762dc7a739b9cc1c9880f69cb71b45b24408004a48d769bba9018ea773de9c118b04c9d79c954a092f9c3d70aad9828a405953546ed5d4da2a9b3ec218dbb2d67cec7a043cb6ba2b48fe84005fa99ccaf52134c15be60440583369a780ba014e6d0825451a3e8690e28c1460ec6e432829a2aeb665f4a9d4a65d0c8120dc5259769ba57eb56b7a48ee2f6e6fbb67543f9f42d5d333aaa74fa16a7b6f964d2b76862967187b6b96e1cc663c5b4afc7876566bfd3785a7d6ecf6fe8b99f6ae1e82f2f54de1d936d6db5cab151ba70e5bab556374b58838216a2bf5dda44024983e9e9eba4610a3f6b25bed65ba08ce9e59290f4f4f4f7018f18293524a338dab9e93ca182319b576a952d62e55c71438be4a69350c744f3d05b6e52c82ca1b1b3480e1c3e0cb426b0d1c2fa7f0ec18e3149e3dcf13a714f16903f7e9be60eb42afb843b463522960e9b63293598aabf976d92929678c12069b3adff13cf2e7b6648fdc1c2e93590d60f9ec35f37cc600d77f98eeda35a148481084792a9b64b2ce6d730432ae7602d011bee6ab6c4277f6fbabb1968f99eeb867e4991ab73c805fd76d60082505d37e9a3fd58b33a4e8210db659e3c504c6673e4872c1fefec396367cd0805b5b6489c2cb52144cecef4544e103de36848a62a9e5a262a622a5f44a09e79c73ce39e79494d26cd23969add44e29e5945266f2be6094c6ef7b5dae9b35b0f8e037ac5f26ad36bba9596bb5f225ef8f311827ca7fb5555fabdef5663745b53937eebe6ed00666760c7233b17bea267bfaddd45aebeb9ba7347f5987ba5258bbd93247a98b7694d65af5a5db96c6255f50c5adba24f05072f3aa99bfc9755be0c8d9dca8ad16a594bb9956df0843d549c9cd0be16d3229dc340a21b4787230c0f1355575f637e7cce8cdaebdf207de4a7180a5a473ceade3b0d8b009f8e5527265e008e19cab2cd35524105b21b1c6524b2b0dd4d635965a5a69a0b6ae5943abad960c5bd7d0325450c69a32ce9401633c53c6cc4119136a4d171c37395a5bb5569bdda036752e417e7435501d2c9ab05bb2cbe2a214a4c496ff766cf81d48246e8920c821104875a82ec378263b60e7248ba57494d9338613714b4c8a4848a94deb5210c5002865b48debd2f2a203c5a170bb255b077b71af1712a15be7352d15bb244d97e7aa150eee82b9588129dcb0eebee068c375e1c2850b17c8850b9c40383155c76d5a54266f78c117632c9960192424a41b9aa0816b820ce73406eb092b5450a4e0c6179dc4b277ec8fdbdd6119bb3bedba64815b74832203504f8800a7f9b89db556e044164e423441868b04504d54e00699a14698113c098162424c1c038a1928818118bc98c0024b21934503496819620359d81c3c80a4451f3a5cd26c7afabb04984d8f97b0c1a63c6c8051118c0ecfa64d6b36e5e2b4e95d9fafcd8383314a9c804c53139b5eea005320586bd3245ab029941248b82e165603aee86a0527581981034427040e2f749cb1c9a085458e0dba2daea441a3b855dc242db57ad84a80a30dac04ce96ae0d17746d58e1eac1b526cbb6869a6e0d3170769093a56bc30cbb83141b2758ad41828b46b5668bba0136d4746bba986bd4c099a00d1b562658b3858574c5f502098acc0b1acae03594e8aee83ac688d41536c4d8aea051c65e69b9726c08e5010f4499800607f07737f528de7e105949795c3b50603829fe04d20e537fb2e93fd134edddbfdea5bdab6f2c51460aae6042aae2a4e9abedb38bb9632e45027be8efdd6c96dd669aa76a29b019b5411e3c81644c27661ef9caced26480716c18e80e627d09910bbf68d3de3dbd8c3ff29c5c6de419d9cf6b8fba48fb165347927b11eb9cb6124be922ed5d113ed71dffeaa26df39cb05e5fc47ad5de2dd2eeddec292df0b32cfbcd32d6b58d9b167792ce98bd5eead888754d7b29dd2435fe91bdda357d548fb5778db6b3b41f79f746dab973fa086bef6a8f88c5fa1136d2479c3ed2ced2de2d621deb2a4ef7ea22acd266cabbe7aef69cb058f89c3eda8eb7eddebd11fe9177b551f7edddb7145665a20c65f664d7f20bac55f62ecb328c6941b3278b58e42b68674b4c536a4d1be4d956abcbaf3a1b1b9bd56ae5c47b77239823078e635d25e7385ec57b8e5b8949d8c91f79efa0777ca39cab728e737ed4ddf3debdc87b77a98b72367c2bab17b5be7afceac6b1ae62f31bafd2bd752b31a9d25558c7afb2baca4a0c77c72fea8e2f8f6daed255726ef32adebb5b896115cc717c231c57e9a39ce3c8398e1f79cf7118935d124f7b91ebaa17e9b46ef491cd6f9ca5bbb7eea55e74f3d58bd4c70c7a4ca27dbcbf70cb6faae7dc087f471fe91c3f47e7f847aeab72b2c77de730e6658fd39e13d7736ea4f31d9de7e8fc4875d7a63d4d3b715d7503b8b4a7dd4aec06d0d19e86bde39cbbeed2559c8a5cf77415a873e81ced11b9726238349e3d32277b9a2b7b5afcedbf6718cb89da8b5bd7dd888573d63b9cb37e74e369ddcdbb1be1b08ef38e759c1fad7e93d24e6ebeba016eb497ba95d50d80a359bfd15560dd9c1e91cdbda2ee30c624de7eebb2973a8cd9642f05e4053d4ef51f8c2a923d267adb2cc53ebf90a1e46c639927ba70810e9e32ed31a1b7b75a4ad534c6b2a2c9951a2222e882572f3902cf675789dcf544f2e7a2205ff0dbf69b67fe22929ddbf6cd6e5151db3fd8e6dab66ddb52598bb0bd7d9e955d13c8357bb61c047decef9c73cb36752d9522ca02a77e9ba4e007a1b8772b9b1cb77803db1cdf86e3baaee362ace3ce3a8c6d94734d208eebba23d6b9c318c775b7d968c7499b89595fddc8c6e62b7de4347fb4ba11be8d66d91cff88f5d5eaac17adcefad445f8361792b24b01c66e646f7e35e75cadceba951b87bf916d329c3dacbca93a4e952f9e3d54c70af0fcf743bfbbc10e4ac2a8e9a106259a6a30c40e492d08030653982fa830636fb03a2d68c286304a6394400aab0318558c1b5610b5c54c0d586ae982dbc2cb952bb6060f2805f50086085090051a25184115b11578e0863ddf72cdc3a0b2508305582e13e6e2ea722faf27a8a079844e912ff839e18c9762ec72fd305850107d3a3229a5b4a5c33329a57386286f1adbd4b822f88b4bccc7bcbe809171e67e3a1066308319dc6aadb5d65a2d7de40e63174e7f3344cd3066b395da0f8aa182c6d7b64f368535b395521d635652334869b3c43441676064bcbef818971809effa8179a0b4caa0517391b09274c5a5a02818547c8a5d2297c804d1ecbab4e76193d8658628a6e8c40c63b643910f0a92d0ce9066cf25f055efc248544c970be309e565cf29e1ebc99e8f51863d9fb62836388120661d679bbc719f6bab609324ffa26cd5b56c73b86995feb46df3450922e7375025e0b259cff1b2332a809be7bce626dc59df62d62eb3bd0e13e09bef759800cfdb6b9afeac703902d31b8ad175d80920eaefe611a05b4a8edbfbcb62e521eddc6db04d86d0475bb1ec6a65594aa46c36e3d8617baa57aced3677dc37eedd6137b45dcbdf0c43da59f66a18ae24d25c71336c284aa4ac0eb7262db4812f6c7889a4bfbb39dd1d4b24782eaf20d02449ebddeaac434fc926a5dedac36f96bd4dc45ad4f4070314a41d03dd10b0f138373404ac705e8e81f5ce1b92c27dbbd552585f5d062dcb2d7f43aac3a8bf21d576a1b9cdb0a36c2599d35f448236f90171d35f44da753b772119f12a07cd9774c1107713d5211c426f1f5065775f6596a6b79a9eb03a2b4b4969ebc5572fde3ea5691886b6738f7a6875d6ed3f2b48dbea08d06df5d097b6d552bac32ee7782a47a4cdda3547a42de5e639cfa199b09e73d90ddd3ca789eada6dd6f2eab06372f39c9c9c7d31d055f68658bf39eb25c002c505991d332e764d6e76d4526c0452dde0ec7532de2a8abfc16e88fbea36774d589f8da68756d71a2b472fb3dee5cff5c9b0216641c882c75c406d94bbc9ac1bfd459b3f6ce1d6c9707299953f2b4838ee23d2bc4b37c9defde6f84ede6e471638daa7e2eebac76fac5fad752931d0ddfa77e3f0f6563f40eeb8d29fdcab6f5cfea06c2e8a2a43f1b27795cd0b5bbbbd97a1fc91e190b5ba71564b47206e9ae48b9bdbdc856412ee5df6ecf190ea9e3debabcf72df70b657e5d50ae7eeaa5577782e7fac7faa73d7b2c7d24338f7549e6a75f89caf3cd5f16d6eb377e3ddedb79c7c739cdc3ac769f9e639593bce8da6a9f4d0cd3d8ebbea387b9c1e627d75789ce377d95bbd3bf7cd266fc7695d95bfb8556f1d276fbfb1d19f95bd1d6e57e5ef861eb2b9b7fa567a8875acfad665acca71af6e337e97ed6d32ebb01b6261fd5959e91828d7e5edb06312c28e31c429735b1d01b9777400efba430b1c0fe7032c7c96e385663774b3e373e40e33738bdcfe5a0f889b76f837e32ea5303563fc1253293d3f9d9de97ba189e92facdcec4ed8ae6961da94ac54a7dd085d707c2a03c2f12c098eacc304584a03b27fc39de9b8458b7c55fa6d76f84d38eeb5c7b93d0ece715c38872e1d97628775131c1db1e4fc669c692109378e2cc9903d395a96e1e814570bfa581d976aa10ea894ec98a972182a44230000000000d315002018100c09450271300e26b2aae90e14000e7796406c5099cc634990c4300a82206300208600630820840064942aa20301946a70aa9e4126bc9c963159da9b7e6a1782514880c1f32f774369bf5052c700b13e82cb399fa52af277b9f0d0be18b0876bd0d1747de2522fd6b50b1a412694da9fa8f606ba690ea60c289d1859eb9757a711b4702dc182bd32a8bf5bd29f58d408607197163c02efa19be661da82597e4de90c15f52dd204b4dccbb155ba44abc55220889d5250d440e75fd6a27da565ef7305e2b31502c2c19f7d8e10781857526cbfaac8d603e3898e6577258a12a014bf51ec1e95ca69561c00a6ddc5a608be431d301860c8ed0e9787ed2406c2825bd58d10a623630f58985a1801676ad2893ee0aa3da594a1d5b4b63cb02e826708cd6cc5497ba94dd1aca2a9dd5dc3dd3ebba452329d7322174473a262d4a6f510c7818802106581ca7f830da512e18d059778e5591a15aaba37ea057c195b8ab37314dfd9e7993adb4ab593cd51fa3c57d402a5b839d5d9a2492cc4404247883acc4420601484f3c3e32509d7f4d2c15b8db31be32fde779d62291ad12f7c08e24d5c84601ac8688684c6819f30fa0b6392a7619a5db3e8078d97710c0c3f517aaa1510eb405bf551329e288bc2ff9056fa88ca11fb7dab724506fdd8fe764cae53dbd5447e6898da82e3324e87191b76b3fc09ad65d42f271fab29f0b484c439502f24c003fc647a0bcd03911591df42bd02406a38b8924d50c05a00f190840ea00711de30518268952e84556ec3413eb6469482923289a966e568a7dc7aeeefdf159baad3f4b8d5e50bde370db8674310eb77e59379d9a708bb8de482ce0fe90e0eb41e9f366bb18a15b55730320d591aada7180f4708bae3668f6e0678307549de09f455d8c0b6cf12b9aef5a36b5c8e461425e7cbc1261836083b22a328e9ccd70fe11aa5da44b57014ebf535d503372bfe085f122aa26f168e462e048cae005da3d801e6ecdc0fb443e894dad8b204ee41b52f491bf611459364f61598c5ed6f56646f12232159dd37a3f51e093754c77ff125fb62f8a9a21c86073ed636a8306528554bca2a25db1278d603e83e8a7aec0f352028a6792710d15705a2807129821033548d75ec52cbbf0c7ccb466dd056e40db05d28c9066c9710da27c56ab21b26747771cb5fd6ec1d4afd5824a310322a27c516079ad0322ba7f33d30d6d017b8c47e0c635085a7904eef619e2333c063995a1f648d4af8bfed8937aadd61cfc061820cffc6434bb763c34a9469f74464d9b11014c0d82b88f6c3275a18af3fa2b053eccbc81f34f059cf05e5fa10286f6b4eb64fc53d442c55ac87a72a648b153e835fb66d132b61ad1adae757066402db534052afde3751c88106a10cadcb854350d5eb2d0a31d335feffcc303e46f324740124ff816f40f4aa2d945578ddda388c22a4bde6fe2942a09285cfac04877883264e0de93c8f505097a28be9c0ff28e7fc4ac6c8de4b23292e46474a3888090fa88f0a431e962ca8fc4829dff3d90755f0dba504acb0ff256e4643d7c8a090b63591f5d81e4bfd6b04b0041be29c93317113bbcfbb55815384933a3291b2f08297ed41297e88123c8732329ddbfc545915e56a7550fe97e658ced39b28d4099f935b00b0f5e443b20b0a2cc3f68fb1b4e3f42936b35be4836c0877ab9486a0f5081f347c59d72f0940ec17b1deb8107098a7392149972c8dacab1655b33e7f99a6adb094374b432c0d9298a45e82d9906443b380d9063ac1eb9a80363b5814adcbca7232f47d5cc19b98fc726f8ae8f807244683456a59b43994f645e2bd28e0f4ad7926517f646f4998c70d332c7a56300922c1a5e3797e0729fb9d1f8aff46695713a835c773c1451123e07a2a04b04f943c88c9ee0b687c1d6aeef18e71e4a3338505bf8109201099ad46bbf7c2837dda08a6ab02508bf0b3d5a1ae930a8ef2c208a23340ed3ad630e46967a73cb17c45e148f5c1c9a9cf4065f9ec912ac3f9ee6d11830f61eef026e2365957a02f820415dafe6ae30738c188b31204a109a36d95b03b2215fab322183f36f5934ba754137d1b0ef3c4afa1758f90fde37819d434b84c74011bf0d6f5883406a5742946e507d3c0e5bf204f09860bd9d2f2ce7326f982be1383b0d8438443745c9eaf514c7ac95db1d521151327b6c8d50063ba8a5882fc7372186824a45a886b788000e2db420d801a31c8fa1d76bdf45f84ed82478a347920e89bf93e1e4fd84ae03e90aa43c32a7c4da37295fdb19269b69b50c0fd179dfbb1f403f33e297b3bb1df44318c470ba591bb154592d76eef54e5b91e54aca063f1505188fefac26c045bd6d53a82b32c5d48e6995950a0a5161dc71ae261c9772400637b1f6bd1d3a8c2c58516d98523e787c62e072c7f421e206552147ba2a8286f120fc768f8504ddd869b109e1929ce61a2c7315011398dc6b07cf5f9f80647806d0d56fc90fcbd6967940dee1e67a841c703847d26dfc27babc58244805092230346d4b6c81c9086625d49577f793a64fda86db5fabc144d5e47de5513b60eeccdee0159c5786939a7224193acaebdcdb915b3ee50f2c71720b3158c7fd940343018f99931b24577086451b9856323104308868701b0ea87945cba099cb6e789a1fe861cb7a2336cef279581d2e13c1b889714b908074e2028857e04be0ba074e77f7edfc0d4022476e3c2034199af049d206ea9da807521d3741495685d4d77ce4dea8ce1854cfa64592bbce8da72af921d075f4fae73a15beb57742f29773af7c84e298ce4496ff7ad524ad47b96d3d46057b4db5d3de2bdcfae76123ad6456fe4feff92161ec38e7d4374d572448732fdc518acc302e148ce9b98951f47d092036369e59f3264b60ee3423cdba8bd1c846425ba95cbeea80b7e3e296b0badd524bb8dc4152fc7fa8e9f95434e31c54367177a2960930e2259e490e9b3b03298787912c0303d3b43585bd119e2686ab6899081d3b6bfcf5bd4d23a10d760879f7d574848956e50e411c831b2e5e698828b9b6171471dbbcc1f9f03ab5c84d9d5f7760b13797d82e5755b71f7017e7d60118f914b1926e8ef11c59286b602ac20fadb597cd132fc4c40c4594d3b7acc7e809402db5066463e7e5d48c3d8bc678e8519807a0a8bce6ad127a1e24d452d4ca096b373390265b2954b8ccc8d4b974fc361668f8b47580d2ea00c879dc83e700c917592d8b91167bb59981a29817f69b9ee6a806ff97ca8c821180d09468c0106221275b05042773577f7eb8fef0f3fbf7d489b68645de51e027a1a9b1c9ccf2616f613e654ecc0082bc844467a52a42f2c00c1ca99479ca6fc297dc4a235140cf204ed843676cac14cc7e770000d6fdc65404f03142fc37d715e945950341ad1c755fbbbff3702e311cb9450f23aef20dcedde5e52e6f02e4bd2fbe7fb0c6882ae633e0626e0f7838fee265aba10dae4817d1eb425c0d77b1a57846c2cb6c15bc93833a7cc250ee94debe58db9a8c0a6d83a014bac2555eef64801b548eb46f7ba60d2993688d86fa79cabecaa0721be4eb4f521df625cf7fc864c188afe2baafcd7e99e3e17730515795485e99253cb35f7b10075c878086ea5b58931bc33e6260535fcf1cea904697353ee7a66969854768685b721c4c67e97f950891cf6631f34016c8004ffa45ff2561350747fec1604d219e3b26e26de4a9882b55494af9706cad4a88f73e1f9574cfcb85db1e183137ccafa14d378701222c9be12f621639bc9480cdea0707a13208a91ac6b208c9d1f20981a3163c3da617b87db3a526af0625acd3331a73e0960c6110ac8fa81d80baa82b6b5480ca1353523f79f5f314ea0a8334e2814371860230a1ed33d3a98efb10b4a7211e67aa1c5b9908ba1ca54c57c877c42d66992ca5e601bb3ad8c34c9d8305b6554552cf3de2f6f15a1ade069c88f83355cf4a839a04d83c11e395888b6275f71518ff1a0679768233cf483ddb2bb3d3e326e6166defd884c6c16b208ed2eeb989469529213ce01b111826905af023fb7fd6146df1277b309dc28e102203045ce2080d9cd81677fe30b1731997effb4fe988f0b34f96c8920c117e18485cd32eaaa74c1c1f84dbeea2cf5096d66ab9cfde0e65cb24c82554e9165476e9d4b935570cb0adf7fe7c15951bc03c26e12295d8da204220424782647902575b6560e172535669f034f5664e8d9389e9d93f58bfeb290c4b5d39084f924bdb2456a75b7dd6a568be9531f0a69d372011842c20567a6f1ac496c63ae4f9b48bb1d285a412afc67284a350d5bfe18eb049f43268b998e6980155e8f231c49a5bd4e8d4e8aea4b2ea83d252c156b08c973bffc8b4498bc9ab33bd5b0fdd317e160b031bc5753b6950367b082867da2d11a5d34c5355dd642993c54b5feacc3b1448cc72f0ad4df1159ea12335115e7bafb71bb0d76af44c9bedc2070fcbbfb36fcb88ab86684929e1053925736ad2e88011c731b293e0fa73c202fab60adec36dae4cbf0252d6c46ea76cbf8d2f59122ba5c01057595955ae2d9acc87ab821c423460fb5be1034fe9aeec9561adc80d95387d82b0e924c6ac4af58f31a20541537ea762b2fab57422b684ca95204188d823d2f7c9c900e0ea445863343847e43520938bab887d81538e8eb936bc4aae9b8a9be5f18cec1ad2cf1a767b2030d5f1d85bdcfce8bb6d21806c9e7f0b9f77b86be6f633d91ae2ece2e826ac4c4e31b875e25e476b2ea89894b6805d349bb99d41c1108cc84af010bea728dc5768a9dc8a3d8f977299e0f76b3ee3bb3d4bd727c569fbd855e410b93475dab51d61e42a40753011e3190d58b53e4f93b6d64fc384e2ecd106f16fc0afcdfdd70849d953a037b60e9eafaed1768446fb468287d11f4bb42601f8e3e41841ac58826c6189acda3fd0a70c9d7792f368791418c8e39e4e26eefadf0ed092784ceef4799be02370b075032c3179aa66558db0a2397d5c8a87b50ac49661d149e32ebeda5b0edc419afa4c63ba19e6f71eb3a1c2d2b41d5a4e357eca4c3974bf00fd300ff66722210ebaa53fc0bbb239ebc3b778e603c36337ef5deba04ca0c9d388a917907ef7539abd1cc32f9cc4f364449ba01e3b7c11dec29590ead6254c078bc35cc368b88a9074775575c4b084718b99af999e57599e1c1f619a740200aa6caadff73508544da8729054cda08f6c93ec723ef55e7a737fc8d35c10d6d8ba7930755c01014d1a930faf638b151c41a4d93a982645264c9c6d1a5dbe08b4d937bb900e7acb4ed2560731fe6bda0f946cadcbf4708fc2e0a2b097b0f259ccbe97460416baf5dae28bce08d04050bfa6cf886cd577e3604a42af3c3507587519e0ba5dc98eae3c1e5e95925a5de75320d65ddf7163de8d203d00d12cb197c4e1093638564893641620fb3bffdefc88119cb59b7a4790a5cfaa07e76f1d71716da62bd8c250e082ea9470df3d462259532917fc00b5fa8d9f9eefc917de2483cdf2fb5f6a34c0fcc3bd0030b7fb0661486fb452f6c508376b55ea3c0512aabe3cd9e37676dad4f1119aa4cb12574eaddb5ab31a4f0caa7bb268493cbdaa9e4ccca2e1082565e55b6a3d7cb33282a6a14e64b95a7c4272c5a6e43b1213e360e6ed236d447082d97eafa04a58645371a6990a78a2fda40f12ad972e1e5d6aa9f0cd3498b93096ac25c89594dd6f262e006f185e3a0e811ce53433dc0cdfb887b3856044e7f3e105b0f046f564fb1c9cd8820c381dc287e714e0cf0e14289fa7db38944ac40bb9a15b285aff73643e5a83edceb5cbeda46b44503638bd7fdced406e75a00f1012f6742c4395dbb78d0b62073c117525475de6c41a9979a659ff6062790c6cc20101e43f59026320631cc26855fdd56bcfb43692f9e85e9390b9e10d720b74c523304a049f6d5a25595cbf820c52a41c5b683af0081a627181712e3597b6d002a123d6a054bfa532eaddaa1f9c2fa43297d1db09da529ba77277c7fec848d095121bcc1fe0ea2e4d71b7804deeb1194f65b3a3defbdeb6ba0e8d7be27454f985105883d9f19c009c25ed2f558c8552055d4fc4e7beb8673e5feb4354df06238859f31b5cd5ca2c1985e9d7dfd6ea5e4cc78b32783196a30052813c453d402707c9dd481d22d66a6c454cd65959d106b3961c1fc04838219b66f3a9c0d745895f849a2d68e8bdd2768ab90099289e4ff875b174dd9d0aab3844220aed91f4ce8c8165a5cdf54cdd40ff0b4b683708b18ea4264ec849a159cf2500a62320c49be9d0908afc423d8ccf40f6f16927c7c9e029ca1e27ca8963c20a8f15eb27cdc542102bde92dc18bcab89fd3cd6689f698741cb659681d73beef9a44d50cabcd6ebdb92a88f164b6352d93b05d236848089ee5f1a07598f33279907e2c1b9fc55bb73df30945321dad79a141916b23296d07b9d2170dddccf9fa146534333a9985739dd620fb3bb3c88c2822f7bf4133e9fcb6cb960605fcc06cde5e5b94186d045bdccd248845f7298358e49832b4a3ce2c0238747aca0e883ae39af6f4963ab029ef3d78814e6dff8220f5ba41be765470fecd3209ba44955d00eccd9fb924ac175967c264c6a445f8fc5ef76651d571fc7e4ff731f0a564b408835144d87b883d7aa3f24750404c06d78c9f065b57a7033331883a26fa1d3de69aa65f453f6f732b623b23a21d6cf1fd9a35ed77b144ccc2cb0ae39a7b744f8eb4856303818e5c82d2018bf22a3cf078e359753930dea267f48e4e9d3db1d5002706c724e494e462ad471f7eccc734b7dd02a384c4f402b662ff33c35be93c06889897d57657e7977dfce32a0f6d02ce3adc77b34af9f22263ab404f88d1d9573e6a1f1b1607927e81fd79a4d1e8ddbf3a2c8e1a92f8141a22c1a22554bc88ba5be93af5a54ab608d1fd8cf2ef04404b5f8aff50c82779c3e0e68150535ce4e04bd97aa28541bee0e214e14a204708356bc113cda25fee0b1e50b9c00645de6cf6ae8c113225a3274a6303b34aa7408b8e5c5e35c5cc80f10f24cdaa6c87f18b4774405a24e301b712408287d9e48a72daa726d863790fb46dc3543c8b7cb7a3a1394b70afe84867141a6594d2d36040908abb65d6eb539929de81e27217e49450411fba87c8a175df0beb3f81c64559f45532bae17626adfa837056872bc8f51628726817274d0c17b6703aa0c559397133aed1a7b12bb1d6029112b79cf5a26529069098aef22f0331c814159d4bae0cc91db603c5bb0e3481b479f951c67a06f075937d19a4e2d4cfc7fc2e604f59c383a88f1775fbae93a4c22c529a4581a0ceec498a782cd6bc6991f4c08133f7b14a866a7b13861ca448d738ab9a8e60a2015dbc2db192097bb1d87df887f85cfc2d462cb6e1c54ca4090374bb12e91d331c83da119cc96c8dabdd859546c3c69be55a6dd44924ddc6941e219f2880595749d9d70f9ead23a6838d09a5c48cfb800aefa1d9978105d344d13eab180a0e578018e103145a6445950fa86728cdc7c59ad19a7030f34b25f27f7fa0651f8b667db49140dc1596c6fb969a6fd2b99d1ca04e3b380172b77aa7262d428127937d0d8dee9f40ca82450a11a197bf7c4b5db0b1bac56f7f28d629cde4694893d7ae88b29a01bd2a2944a8afd05fbc0adcd3c32e58047433ac6449c48f360bd922d28f180b8c88629b92a95bb77501769136a98b3f2a12eeee8e53804cd74351a6b2264c2444a240b25c9330580d820ef0cbd726f6e1749f6faf279e37d1663976312a4b485f1e18abe117d2ecdf162a1d116b18975bc27b6e806df24cbc0618574485beab41b8dabcfa9e0530fe93d20147ea837038e13072a496be7c23ef60a074574888d979b6a690a4cdd5b0f67921f20177ad9b782d86ac95c2b4acd297fd34d84e81be44750f0503ae406e6d909e9d0f46a9009484942124af377600b4a3037f68948f14240406a38f501aa1257f4936d3a0736288938b80a2110c6a2ad1ffe29445959a62ccfa95f719235e4cd94c83a863f75098cfee97cf6c7d5b0dad3759b35db5ed8128cd0322919e2fab0b7d08ecc302708b99df12232d1c824344fe3670a0c21534f89e0bdd2f7740dc2ec794de2728657cf141e0456cbe33026be29b8e544f6a4e655bc6506cf9e9540d80a3b92bfc78729e5523c8f06d4a942d72fb1bf7cd3451a01c0904e7980ee45956d23cfae32144941d336cd397e37d509e6124216742ec8548210b9a6c9cece18671639c6d704f63bb6565f7690f7678721a690af9fc2987d0548595fe30dcecabf6192d83c78f8189260caee6bb691d9e5e1c388a1a4089baf06861450ecbcfb02efdf7cbf0a8ceca30eb394388959f2f2375cf46470a9dd6671e306563d697e89c7b427bfa7897273292062565a4b0a3f85fe76a015cf2ed6e4e33417aec98284e41dd54f517c69324cd5892b9cf478a8440b876e37d2b6bdf2d6833e91cc56071aff6dd82038f0a88bc643f7a64bc074518f8e200719813b7a096f4e435f632ac612c6657a4ebe74d23aadc5b5134dd160a474a1a0034352b1daedddd909917d152ceb277cfa11a74973f15c2803abc6d71fe45cf1a879adc8824246f99a8c3e00db7b29c5e1b3058b7878fa9db805fe1da0f2aab3e50f687b5aac7023f69c779d41110cdfb54beaad3db9b7e139be161eb7b381debd203c96d3b71dd5820e229067e67bc44cee6f5416fe2043f70a46a9ce7a88a4a41b037f501374cb9968144aa8606ab69096c65d3b6a6d6d9f9bc845e2d09011a0961b64011f35a737b46b4679b68599013b56e54b5e277eaa384b819ffdebce902a64b8d002c094124e91e1f17d4b2bc8c20d23510b6c9817e45ba782a94284683fd4dc18e474dbb636981adead3251354106dfa25c1ce77da0c0130ad39f1977d592629e1125846d6a56336a105d8fb58d1ceb641ae013772cd3ecaf0b48acbc4bd3ff9b70565709f5989960397a587c4812e8cee5d01e27ece34f5b16bc86c37312ef8cd7307aef4e6b6b66ba1fed527dfd5ae2d9c77f3ae61c6378422f422e9ccc85541904d391dc4fb7a0153baf24700fb94965adf575e1596c694a3d3a381c6d2db755f630c490017e9a4ddbc505b0bae695a6d42dd2a492782cb59c5d5bf5922af9b5f3202120c53b3eea8c9f2021b0dcc3c943e1ddd0e843a7d9a8ee0ced8dc87a668f6deebfa164db991e1b032fb462585933b6751032b651960d0f2d026eaf07565ffa40cb8b25ed15fe84544923eb111e29d2efaffdbabfee193a5dcbcc5c30def70bd7f585d37cadf6f2a8897b0a5ace33f8302b64b284f547a59ea976592065a23280b35f2780231becc3198b035913e200e6e78f38a3ef9ae646877463bb09c326537937639be1320652dbbc72cf2b645055171bc9601cbdcbaa70eadbf64c54fde5f78c42f17d03351f529b2f3cc55b1bb9466889013c6074210694ba8c3944ddfa8722929e668ab163625f7cf03d3a61078fa779879d71879d9f35c1829c26e55e83adf9cb255ff4bb0ff7fa237899ddbeb2b71fdf93fe59de5f6418793464477730cf1faeff90781cd1ae17df7ed625dc71e3b4256547fd8586a819b0f26dc7f49adbe7f09fd7260259d5845b50c43071a2b2346410b054df91ca0b1ef371adba99a9d556be71a95ad49980d26185a8c6ae7808a999a34bb9375b423557648d6ec92d1a68e8aeb3152257fd3573477ef62471562ccc4474707c056d612e2f1863fffd44a0685990876b1a7c1a6c68fe27a1127102413b432700ea794187c6f042718ff6f6c0d5a532514fb5c83aeae80856d64f0d5c169e4119e56cff1e2f878855bf37df1a5fc22cc0c6c98a933efb111054a7538bc1c32976eaff63040876a34e60eb67e442f4c126442abdbc764ca1673e9a8a45a766e22c30224a2c41bc9a86b10d6d741837914eadb275ada129bf185b0b9c46eb35afaa91f133d5428d1fc0d251ea7e8e1e9db869301e22078810d9a459a8974cb822724f588298a4b0b25c69fd6888c80b94df1086714c726af358b756f1fc6360b469571aea4556fd16e8a2760a2507d92a05d2eb7f6e35e9ff1ded52175d7b163de694a05307c59ab70071fc791c81f0b8ff979e164e3d0e1bd3fae6d44677e4024273174119f23bc87abf1189d5a34ce780d754415e295301f711fdb51c80dac45cbb17f40095220a97a113c50aea2e979e044aa17161b2ae9289f408cea251fc0a36f84174c70dac9d7d4aadc4c3ed722e7c2ab3362e7e02751cc9f7145af7821fa032d5b3aba9e7fc3fa4e358501eda77ef6138ae9ff0e0d1cf1bfa184a350a85b5fe1e75293357819acdd3afd62137e1ff59077f0301d32eb45ce0f98d46a4d46727b091a64f9a798093b97b92eab9c249b00ad3674e842c58fa9329f1378297a8a75f1f41961ba3d9ebaa2ac65354d92af723edb1140eeb2b557117f04bff12fee3807f9545998edb4cd24ae37ffddda6a15eb750be48630bf0147a7c0f44ed909d96bddf04658fe009600344284124d14327f0724732cd16d26d9c4271653faf0646318bd4c9198fd8a7014341a0a8bb02aca93268aac4b99da82c7e5587e2cd51c45214cb7229cf9b43ebd20c72bfb14f73978d1a42c23e4a19434d1052cfadd44c3b45122ae519d2023d8bc19f61fed2f1cae851b94ee122aed9c09884b283524967ef9e0f80fda203827de0562e198c2e1f0936a1d0dc14c53e4a64e2de08e5cd384e32caf568e4954579c32a2fc83b6aad4adaa702b0e8d64ff2ba18973ee6d49a35f3c805393b71a1c8a3429d2ffdb623b166801409c82f00374482053462dd309db084a50b73f5ee42de462c1d4faa12d3fac9ea167dcc4b7803e2315bcca7a75154f5fe1802291b5eeb94647c580b297d485e8cf17d47b74dc55632fe149cc098dafa4a8c577c0d713db689610726ef5bb95f3e4ab1efff63516dd143f51ae6766f91c826df31f9a80e75b5ee97bea1e04d80677ea5ab55e08fe736d5854746185d1adc0ad50f3867058a3439d6d38618d85018251831d37007818056263c73993f21dad787ed20e203e3673dff800071902fdcc2089529557c86a5b842e7ef5c7f84022a1149122a8e17a059c551802f07cc64bfb93b69b116c247f26b3e6bc5107af75af33383b87313d3ebe6753b6d0682f12a4ad0b79b4e23a8760a8bf7d03fb4cc6e65c954fd578471313115ce7e2d22898892ea363828f9f1a5980509bc78ce5d32e048a0d86533c881edb05847e2e922fb12283e738f69c17d79229f848e923062247ab94b8d17ed6012d258695917cbcdd64a1913aace0b10a61c6c947798f42ec81d84f2860390d48ae9bcc269998f2c79e66ec5bea917a849283c3c705851b90043dc9c784b8e5762c99b03c4696258c194991e64ea43ae20dc996f1f86f9db8e670e56054d126a679c15eb911fc048700299b6ef609c9719e946381850d8129079a058ceb6252aa2322b2dc797415e196663f7cd40c5edb42583769dd8a547a4a6cf0a1d7f169796506bb6cc9eb1e6ab4badeb91d4335e6a9f680cdc4060b9e2fbdb87fe950ccb19928ca1742f99aacbb63c2c0a64d7698f654745d34155ccfa44c18c3a841390b6d38c97c0c2cd0510582ecc6bc3ad17b26ca34a2fb215b1a6e112ed79508be134e09ecf97a84ea5fd9eea2c610e4aa977da1cd9011207630af99bc0ebfb13da475e51a8b92bd80ca80017c1adb0b53d66d009cf97e8598139cc072c423441a75501f6f45ad4c1afdc6b7c18ede322a5adecca571632a7ca23e259d0b3aa7f207305f23a17e45d4a20deb6e684c9dde60ed83b6e121ce158c704ac6ee642644befb05667a4b5fc4c01dd37e4d476adfb6ebaa138e93648f17f90c963a989aae13e793d29e1488050d44ad528692c5ef8444193c4687d984ea17efedf49cd452264ff6916c5c33a5306323adc1f4859fc30df9c4a09dff6eae7240a7ed4d6885fcb63ee56e975eb90915dbe883501308aa36b0eaefed2da7d5f5e7f10e37c544df6f643a7611c9947346b59d71cc60c46a1d5d6c41d450dbb410cbf79f17e202c43788d89b275d838352962450fad12b2348404315c347c994688b95544b9f85be0cb3ebea3d961b99681a5b9bd13c329aa23d0a6d2ca80551323394e1fc76ef27787d0eb4b89606161cdb68e5f538a41c7528bd9b525624db2efa1dd18be7f14e7e858cab45b2306e726789d1776b30347fe90d023495b923b74d674739702c9b9901cbcb9a4e7d3fbde930d6ef6c1775597df630baaf859171dc51d1d93fad28e8a452ff234801a9ed7d2cc2b1684453bb99527263b9a8c11d654ebaa5c4d098099e49332a29dfb6113293932dc50d822bd19af295c1fa8265262dfa0a5973e750e33ecd037f8c76538ad373ad9f63b9e40e4dff4b85e67f79660e27ed1e45d10d710ff4b92e0e0a887bc72c915e5fdbe6a5f2648ac95f163a8edd061bad1be3234b27308c611b91043d99acb0f64ef6f6ad04d56bbc4e779248775e1bc128fe0118e33d3d6a44c0efa759e7d857a77b3436f6f03d455a29286c20e6d9162cab9a5a0d24d1c31fc7db8964be9f4fb6d7435a0f2d165ba0f50010af81e0527a6170e738ac0d3f3c1ffc77b2cd750627a768cd1200c86597fc8d7671a41f1fe151ea7437534874612c25cbe5a29bf7192c5670b807de735659a91990d10938e0cb80e9a3ec6d5f53401f3670b9f3c2cc31ed0e6cc1311a0eea6989f92c9e8ce89ae97073a0e84194827076ed2e7e2e540485e5c5cd3c68ba16aab9b28346116a97cf101c815257cb0012250658c798a256a8e8144d3eb0da89423721cb0a25132217968207a076f8e0d40dc0607358a35963e6e74ca502187012cd8405efe5070064d9f403f79cf56f66a0c0225a7c7a1fa97d0ef6563bb6a42564e43539fc60006a717829a05547a3b5af02c43cfbd40f196ccd5c9e4f037042c46439e805dbeffaa7ad0b5d3018f2f3dc72ec8521e1c5fb974b90503d5c76b1a80404695f710028f69e1f87d34deda7b7b28058a70ad53a5f74eb429c44a52f046500f5fc9eba780256c6961d14835dfaab413b21fde04a2800dfe89096bbc878648bb6880680d9047479687fceb201b1d57c0fba429aa967c96bef8e6f0ad7136670656078e3ee3fbb4d65cb0af1d4f2a0a1b85fe8e0862267f748390d8d45eb1d16e0fa3631fbe0933de0894d3c9697e5ada267cb8c050bf80906ee0cf5fafe4517cb801a1951b17088ec2512afa0f4be5ace3ae61c343c627f78e354bb97cd0629d1a6e924571008a63440fad7d5f69cb6378a2f3f456d2ec4f617f5e8ba8504dd783b650c988e5b49585048602970a1b5642a00089405d71ca4f7b40f617bbf36185784fe04ec4d9b5a83ec0fc96e1c76868e8ffae0e7532d6b7402e5864dc77eadbe9275e403bbea8ada205f2550c50880e9b0342851c5c53007b7d8520847fa489c60b3541aa4c35a7856af6496392486cb14bf83a271a3b693f742bf102c2635b03c601cd23efa33646f5fd9b46fa5e44204727ccbb221934309c111ff8758719693b6ba2e89d12a3ab6dbc7b243f4bf6c8cbbb5d257d00e40f3e1fa92a9b8e9e46493256de71d1c9163922ab0b2a7d0a9f5cc9a8ae335d000918099ca029229a66de2aa651815c8428bfa1b94782f799057549f606fb8ad3d58cf1a37065da603094eea4653bc593ba402fbd7b20a3366c246981378dd291aa7b44442e78bfc2084ffdf38ee82b8d612c7f020babeac14ce5e3ba1fb707dc997652417564188382db5c25be645a6d41b16e817488c1a5120fd866885048788d075226599c81c1df626c59c5add1e5fc1962baf239237f6430c0fcbf97ca33111ab99144d798e18e664d5f8e852eb48d85396620b0f19cbfd12e0d867aec51d53cb01a81382e184fe12b8e90f7ba568dde8721f7abfc30ff322cf097b48b8eb1ebfd004fa5aa9600184ba35118a8cad1620626be68c7bb9592970a2785759d6e86b80a0d39ba9ae6823d4c810a286bc913df00452d023c6e0eb2f04453fd514b413683505c662c005bfdd9be192be185add7af4524807145492000d2ba8426b0bfd8f5de09834e0ea1f36cc59e79388f0c9b695fc273858ae101d3bfd2464bb53854f9a82418dc01f374dbdfbc5655b938c3da8dd6a0a0ef05a0bdf496a7d24a919181709c5ea58313e4f5aa1c1cdfcc38d146b4d784bc92e95c47a132b7023b613795646873fdbce42803e05d0cd01a9456a3144b18020e76e185d232c6edac45aa67b135c050526101ef051fbfca73b21c0a02cc5a9516dd0e04ea10ba00e3fa2e674c251450122b9f63a24cd1f262344f9350647719dd8fa4c24eccbaef7c4768a043cae22a5bf7a659198f14ad7cb7533818cac48bb6fab60a25723aae79572e14d3ce7bb56f96c38ccd7074a86c5743ee6ce79036e66c7dcf7e119d5cb789e85bb10dcab84b35900e4d40d66c1c34c4f2f821968b3923fd314bdeb55be17aca04245380daf4d6624866311133b40e4993951ab5630f9940e8ae0502f8d62678ed5278773c3faf6fdf7721aa02af6bd7a7f8cf2f71adf29087aa8b6b77715765826b9f7cfa73934d1c16c50e8f9101de132a073a7b3fdb97ba216314bef4a53bb8dd17bb72050d5e4a7951a176a57d6a9b4eb16bf4aa854210b7496ab3a461c29ca6bed739e81f59e071b53447c40be7ac99bb6d166de1f99e073f392a193bdaa7eba1ef3f7f5bbe1c9e92e063a8a816e8ea3f3ff2e70ae67c4dedde7c332d6b01fa00b3e8c7b168791d82b8be9a15d6118cee92786ac1743914515100db77d168a298b3034ea20e4a4d5d024a470048598a83695829585f56d995eaf536ee0dd0089fb9c28184563dc83f1436837b83d7cc3729c3e9e10f26fab415d501835712bc23eda7a28c49a63a5d07d202aa2f6fdb1da32dc38bd24110eb63c8102e11d5ed1280b7ec3f741b7da61e7eddd1b044a973750132aee7b96c2d839e0d3d41d506bc4e2cac0d001170cfab6e2aa593b166b19b9140a4f472f02a51d0b38e31165294dc7a274ea029eec584048b2332d551be042a60c59cc91149f59b46162de580a2dd2e2e7d225f60a215f7b96b89968390c1496c075d1015fd598842b8612607d07212a77294fcb0a3ff122eff95e06a5a208bfd4a6a91622d34518b43f5c869335d82009033422fa0383c9a47c21a2f00a80dd96aa3545e1928fc0e47935e2305da3e61e48ca4d281fbb197c0b91c43cc1696ad70d51197847dad972cf9238f10ae36552ea649d176f99f987554654a6b24eeb74515a2e909a2718594f4a3b47b385aadc00ea8c6ea49e2c2f67233f66e14014592f92cb2851836052b393df3df7690ff668a34144b2fe60917fd8ac04d177c3669d12cff0be796aec9e94ccac0f0e024803def499f42c8edcca491055984141164ad60c442a9e344b62ed9d559095745ba141602fad45d1b0562d185e6bb8421ea34106620010d574da18ccfae55d55f6a596afff2b474b8ad57793be1d888fe579e4eb6047f7356e68ee30c442403bba346bcb94d66620b8d24ab0382e818fc5de51b503c6f8485959aef311a511dccb861f546a9f074dc0ed2028b02a44805efd094051c4a1e01d221b7bbac524a60cac58667888073bf6c4803e037cacbd4736bb1759db6fe3ba8d48298e8adee76750e04a2a4cc2c3bb2292f3122468a9b4774e4e91d088fb524ebfd01d29e094cde2009fc7ad0ea755dc1048f53dedb49bcb3d8a128f61c8f6ee56d8ec2355bc5af5e0672a9a0f41349dc3a8abea87c0d9deb8693180736adcdd35e07e75c98941db24d6fcf50bfa9a76055313fd48a8b6f5e2ff3312b9c492c18ce7a02f6cae990e7705512441fd1ce32c511127be3b5c6041b76d3bdb196affeb68a85dd6ae813cfc7ffdd8488ca98227914a2bfd21edd4e80e091bc8c3258540f3a9055edc0321476f940732778d9108ed69971c0c79a603cc46a5530199d638aacf3edd41c8d3714e6d4489f31aaabf52fa0a0d64743e87ee2e5d8f9b1b56365dda61731c39f528f477c2d232d62c25ea7bca7eff94333e9cc22a9c127b9d5e46fda01fe421fe41d7fa742c5a847bd351e9681830a2cfc31ef7dfda2d8583bfa70189164754ed51e284893471c2c27277a98016d5a330930fa3247ceddd90474f273a09f331eb38d09bd3152b1c44f5fba780c90be1682f4315836691597fb9f43e8b3b35240b80bee139034eaf42672b3b19014954e1f04ce9552d915dff0a9492a37f6f5337d4df34fe112d808bdf25ebc349c6f39ecc1cb74af5d09c193d11b3c798f07254258fa60231e6994b609c0a8688d707ed82bbc1f2c86ccfe380785bab3864d9de6eecb703f2119491ac75ece3b445323b0eb24c81b6890d5212664a8eebf31639d2250ba96994685805762541f83e458e8f92d603c08ca8c17ed410f4807b06a68ca6243052dba3d3e6a6e4d7ac1160f9e83a6c4a9653e718fd8a0d5d0200db4f5695ed3ce9fe5d74f104b509e917a9f537191e36ed33b7b42100bf2eea794d8289967b95e7e37d79be102ad82ae718160f1afa88c76ae2b6a617f7e93bc7db88e16793d2ae0b400860a8fc6eda1952c21931799da4d7475828ac4e29daef9c261d69c4e04aecb4340959db8705445ec0069dec6130f1d18a097102dab338325abc82800d14818e9924d351f44f95773d04f1646dbffc889a88bcd28467d6eff739443b8823d9a4cfee693a96fdc15da5a0c17566ff6dd7400ef44e48088d52ffca79ce68e2e23d73458f65bf9467956c74bd504377b207b4e605bbda5c8e6750d7b5d7dff79339e0895e314a3b7bf063d8aed54dbac394b6efdaf477768559e5241082529a7a1208e2fd5ba55d85e992ce90d1c5e44a2b47a97ee4887b4ca778d408fe721d0f9354bd53cf17c969f398a76e721a2f92e0b68e4fb10891dcf060102b484532149295345eefbd4759c0db7d8cf3dc88c6fc34e8193e93abba1622f89840f2d21f72ba4ca447e0b6f73222a32e56b5193ae4f2d2771120d0228072d7218ce77ee8197fc01397ed084346eac458ede2413e411fbc95b91e78da772091de8592d8b4356d43e81d1aa8651e463c219a120e154abab04ae4568b463f104b57296a9bfb39100d49c28cf1c719806fe0d79ee3d5e32619b9fb41c510fe83ec729f218884e7bd181eea68f415fa688c4bb37316878ac5c4c0d666a5d8135a3d6c827a00e2b9433325ceb6d7c0696d8feb1c950fb150ed54a0dda88a563a84df2b5e37ea0b678cab460c84411f1538d0dd65a3ec5de4cf0c3ca8b487da1445c6e1a02b97982a719828c486b34c59ec0c16f140e44813e2c38e1da7f54dcc7c878281ab1492167021ffe40a9b5c56715f051bf525becc89bd97e543a5120f916aed56d69019d8c1d703d1a19ae38f1791c4da632f9d0632d000ebae327488bf46391d0abc3c1e98131e95823c083dae1a30860761062cd2d3cb9c60463cbcb67953f019077f52419e385f410119636220194d6d1c48cdbb04d933a1e591ee8f3df70d33a92841a57383610298606a421e121aec7e35f1222585328614fadf7674414e635b36459bac721c5660a70d9293f4fd64d9ab6265e7a4de49c205353c8f20327dfc910d531231da5a562a7e28ae1192545238bfad2e616d6892532dbc09034984b5905956b2f2794bbeedfc3678764ec104f8507feffc79df90c00d614914a394954f45575f67fe4d6a2f823bd476d5ce8d252b55175f698d725bada9424d08b47413abf077ec37c03dcb1da1303b8d97e9f1a1c227a51101f6d75921294074fd37370a0d65541fb9fa4977de1d542b98d94742d08211b97a483ff63e4976a36f51ddd197a4e865ec7c3bece84d88fb3659873e5a2cb75a18288204276614bf7b516fefc2300761dbf29007e1dd84db6a51d5d02dd5ffe26010f2ccf569eef88e331d4d6e9b1b6d3be695215ab6540b5335691721decb8bc1301c0e4eb547d008dfb916c49debf5e3d5c77a5b97cf756459c5bcde33af75a1c9f5b432ee3a79e77d36bc140971d256e6a211d5fa17644a06cbd00e7ab791aeb804920d8410b5b79b4e90cb72a6b29aca3cef5576e268f812d75bc973f604e2b6b4e5327194ce84fb7b284d560589a67898df7d06f950f1300de90fd34dc1d247abc374610ae63c971482350d986ce88f111571a4165661b91800b72be620aad9fefc82310034053811a08f0f0aed56a4d8f9206f11514d1f0ac16db842515c04fc15be54b047ff9dcad7be144dc765ff566908f0044293ae69f991733cbe38652dc66fcc9413af71f4924e36add19a907756c38b3b5b801a561953994a3befbc082d13941a26ccd8e97b8fbe9d9edc7f1e827b0ae786657d97e9fd106d8f91e6897ce945c81932437b85ad516698b6b1447bd8a5298cb87cbe8616e69017a2314f53ba2af6c6ecc00fcf9a0da605a1dd220f483d14593ff533bb2e92cee4746b4a833330c9cd3bc648982e176d41d0e965ec1318ea6809e1c64c63417f87983714d25950d26e0e56fdbb8550baade8448fa485cb2d804b12016be1d9af962fc6f715af9cd141fc0b58f3912f599ee1f8de42053688b0274cfdc207c413296a3a768088e1f4e0bf6e45a89133932e978dbf5e3a42f71781a845be8b484f40114be285a63da57b6e9815bfb9062dd707f8e1716f7b527550b807bf6285b2707e9df9de14134906eaa0e7409bf4043f20671926abfc3d6b417d748df1cc1be5c688fcc91349a224196e9421aaeea1bc67845c58843e1a92e37fc903ef76e52f1b59018f1e4b1178ca28170a622bad08664350191cf3f90500244dd754327407f8ed1b3629dc3e8c7a2ef525899a8ce551972cb290ce4b0006b9d45e44375039b0c194d1ae29381500e3e8c51d3fa971bf48daf18c8e8b9d0763b8f141961e80aa2f1ca4ea20fafba97909c669fa4fa9db27cece60092e3ac5c7349985d925baf86db82c86295973d183cea0611eb57b4f9170d6a4a8f066230dbbe15ef97d14f22308496b62b16423af6c4e3d32d97539c01735a25548d0ddb49119a6989196bc81f372e2ede51187d0f1d1c7789f744de4a87de6d0112206268057e7fb982681acdeb6511bf2da70ec3a391f1542228c3a23b9025c158b83e00f03da1bb9e3ec6c2fe0989c519a9e9e21b4f0d8e07d2367d1d5aedafb38e87be4f808da21ff13417f8be908181af67ab3b51c8c121e8174e13af04fe85b93d4651f541f6db745b3718883726a25cf9a0d9283d1e1e8d598d121859e8b509da3e64aa798ad086e29028806c2de4438d3775a060943d0af375bab9284e3c3a131d981c8cc3785faeae24622e5f87b06fc83119f3f728400fc7d790e70572dfe909d11013218ec3e5ba52884bed9803797f11496292f73d795ee0206ed23225b5af1a9e8c722cb081cbc97a68a450d455e382fd2755aff05da5bab498c90abba44b3f03fe9257a4cbbee1291429d986f105d3d8aa736bdc624444740e3c744576994fbdbc7a1bf949ba99eb113cb3d90ecada91d6b758fbf07fe79c56ced06962a3d51181d8e47ff0a66bea5d4fa7f3d4d9a7681b99b497e57af3c437def6360a44f3ab9c8b7d3ba0f21d73eb093336846324a120e47f6537f532a8e7a328f53a9a8be269318f31ceaa25304a0c4db6e0ceda2d2dbc504cd174ba6f864510e628557f13643afd593c2e382339ce3c6fa24c00221250cf1a9e441a35af53ea6de4d014e37a7cf5484ac45156453a4c5565e74369115fd505e2ff7fe00a296712612d3e9714a6a38dc4bef3c308f0a62f5095588d2957f362fe5f440d820d94040e628174b28450f781771ab234b03a282a0b93b325fb5ab4456a32e071ed83b539efc35413e5734fb527a73e4e094407fb8e1dfe2cb5d7e6e1e4335291f5fce22afd1661185211a5fb0f6497f331f7ae3b061313dc274763de4ed1e7b9feee9adc1d319fb2874ac162d99cb4ca56852017cdff765021a388689ce8ac35b771b5acedc6f12e8d377a3fb10c126e1277a503af594d6976c6d010158ab9d8023ad011742b7e035bf9c29d113912b514ac01eef3d90a27845a4094ccf63c8cce7c3abd5aea13aa45a18bdc4aeb6fb72e489b2285cb736f556e712a6e1088f977bd27c953385a4f1db7cb1a9dfe14774f3b4dde20bae242d388c90b4ea63fd515330ada29a4b0968873d14cd98d0cc88d20a67f0444dd8e9486b0a957cad909885705deb791089b40e5e8dc4f21d830e9aac746c093d2f1923db53ee725f22da3ed90e0121ccd0a42e169586e0fca06c3e4e9fe7dea2c232496c625f6011b3b6bf8e21d2bb61aaf28f7fa3afc03b7e15e5bc40f858d723d1367e65871d8b25b8b917d8009084e69032c0a618e7b5b218928b01027b4cc8786dd2f4d6a846cd8e22f585d985e0b8300710396beeaad4cf252a27576f1459bbc3e4eb7c5f48cc44c1d5c1adc721c538c18a432293a310e9eaea5a1d0736bef4bb64c6dec56fcc2a0d75ea141722fd2f8f9ef1e15b70ba4d678fdb167356613e4e264181fef776b60a605a39629ba7c8675c456fcb211203b34ea72deba70129d399bc749c0411c7722cffaf43359c113454c07c5dbfe846660e374bdf84f022919ac66f9b73c3defb6db5849235548dbd857cea9e3005a31a13ec81c7a08e14398ee23f54beff73d8a8eaa0d7a658ea1142b219033070b373bfab879f5e5ee9a9dbdee9060d68f5e04742a65365c23685db4d5ada6785ccf70c96c185eaa177e407236e27bb4d29b011a76be2f323601103524a6678d4f88df32e5855b3fbe59f0b24ec285e85576fc6538a7e9cc75534e70816766729c05198fddb6e9d6c248d4cbb5492f10f8df502029027823f808dbe6b42df07953648d789633607a5f1d9eb5e97c54abb1c8f74f1fc14279eedf9fa96a2090083751e866af3c9ee394fbc464bb5a5922e65722ab1523502c0e88077f7700861148da0410fabbaf00ada3c88eec1de7667cf5be321f77838ba587be1414e9ff540ee4b1829d38d06930369fe3b3cb206157aacdbc26e446bc21a14d49034627bb476c9b620091431ea590dda99280725277e81532655e7f1e910470eb26b33373e3ea08d0c76b4424ecb0d79d78734df5934b548574b5944310697556b7bac23071ad838482192c44f7cf829c1a449d4d4661ca3a6862350ba1078e63619c5198378cc83fc1df7168f3d4b775a923c1287dccb4b8dd6280c5294ba3e59dc04adb14f461c1e5b7de9ea9aecd4550b85b681b66c6b637d8a607142f2d69ed1c25d6ae0c5525b48a63a13ad597303b451be63ba66eedc75ccccd40765fd9fa853a81e63e83292c2de8c6d6f415d8e6bb7d6c4bc070eb9a456a2cb5a6f59c532aab890a2f9dba868a1125817231eaff7e59268376eb51a2dc920d182d176897d5847b49c217f0322e22c0aa6ce63d67110bd3944a982125e1216505da7bd61cf036a548197034a7cc58612ea387a6a9dc9fbe14908908f40c06775da13e384ac2ad72e404435b387fe52237082e8e708d87f29def0e6f44f89e9eb85f3560d5661139281699a8f970408881320c1402bff78c3569c9484e13398030a464d8d764f6f70d137c341d5fd111397a9cc0deb4fff2a253a6148fe01ee9676ff1ad3db52c073537e72b869c4d247ee53461987b433d50c176b1b26bf032b8384944290d5fd8f6c7e9c1e90b91b1c15bda0eaec5dd6aa08b9ec32852c60ad47c4a7c846d6954e88da3586bcc26f1271e90888266f54b54e3bfe31c02a88a50aecdca378cd81c02189c8d46436ebb4cc9695347d33536bacb80d6666b0cef3f3a3a16a4b631d197ba1212aa92164d61aaf3bfafa3bf7e691dc2363f121db81e95c49c8f84ba8e26f54ebb543bc7fcc78bfd3e83d550b30b7e55f90b1864aea21c4babebeb3a09013c37a02b193d15697104f8bc56fe431f655eff81170712f0f269812600215c68cc1ea446548186c3c86c43504cbec79a8a53aece6c9d683f5de3c5fd588ae52096e3d9c1efab578fa0aa902257546d73420bf6b84954d7b8d9997e9f204279dca44742bdbe9e2ec542e69ea41927bea276dc091dc6409ce3dbcc514944961ca08e5be73918f6431acbedbbe3ee1443345a5142eac0cd457fa51671150092f3f9137f83866abe5e8a28079b36e8cf042c122d723f56d7808ecf26b1e2c3ac53a12c3247b13a13d5c0790baaa883e8e696b799b23ba58ead308890676b35c466106442e3201418618589c805b7fc2fc2a6b023d8471dcd6c083c3627c90964ef5313771e414cf48cf4afe6b6508a99202bc95eae717769e52ed24452c267b6a789329e351aade4756027b14949a248aedc23aebc17afea089924e913c6830cceff3a670460ba0fd0d1cc6b96e0cb33ea64ceb5b3ade67303547b7330c0d9ceeb6b2f71e6d60cdb2081982483567da1a1a7f0a413b4c6e720035550eaf902f34594addac78826b09b7196952eb72d924782296790d6734e0e3c29b414224564ecf307fbc8466b70ff638238977e6b2b1bd0b60dd0f1949adbaedce51d778e3a92296f96cbc047007c457de78093d0c6072d4e61430cc89167fc073d2dec81923acec6e5a896ece6307b7c4a62c542b4995f3e16a81c6fcc70b58c596c3e1879fed05c1e3639ae61504c48ecaea18e2f255ff09ce0568f1ad9ed298022b27980fa9da7b5d2a84e4be56dbc6f3813bb443892bfb698da902a9d7b3f85a86b9e6f34172e6be038704ad825e8636cf341e9a5b1c34d53b7636be62e2cc3168fdcf99c6d84544fc280918134a5a6b35fc3b817da4dd28711ccba44864cc4f74445278c841be91d4f8f8a6594a5104199e97575938dfe086bfa4616b742409f06f7df7da00ca54b986fbd20bed4bc881050d7b5c62a9633a778d15ff959b2832aa5c5565a55c5d456193ff6befde5f7be58d0054210fd9579ca64ab835cb1784ef6028df3a65c236c56ab5e960e1d8b1c7f2503f5cc5ac4b25e58ad55b452917a9f62d98e94d1fd58e2eb2a52f9269c14826cfd2e983657f83c1536b25cffe5e5c753aaf6a6ceea7c94302949ed7fdaee5feacc2f5d4a5659dd673d9fb45b948e8c36a65b1aa1f2b90c94a973938bc185a87d8563f9a9b3beceef98d3b4013b31f4fd9c92c6cb61bc65617aaecd4f5ad3dbd49bf93520a77cd506e41c28226aa6dc16f4c25bacd16bc889161b9ec39446b1e88e9d65b1dec8d575c568511d748d1b2e3e977b3fc91f2fc1190f0473601628284d2814dd681197b9a5244f379ef286722203997978cf5f830391ad1acaca6b910b28a3a894f807d02b30bdbc8a2bfa649b1a0b10956c055311facdd3d5ade6339834c085543ca553d7008103f43b58cc96a4211f35f0de33033d0e73d8c490e83334d4e7c8b0a8dd8bf686f4cd26e79d191a6675ed85f72c52b4a86ebda19cd6ed01692fa9263ae453462e331961b2a862ddb0b79a3341cc727fa27d4202a0bc98cbbe698848b58a0ea195ebcbcd2b58df7e628b69615ccde5aaa14f8eb9869769614c64e55355e7a64fccd48cedfa2076831a0c0420d736ad59ed31a65e55e15255729e2eacf88ec62f0cb69476ecec16a814f705452c5ad6902f72f8e3559a838fafb4acf35d5edb502b9e5f2c5c1517b616a1eb76c0f9d6240b76bdc5ebb0ea3b40145eae000fa9d42412fd3788d9e7817185b930a6eef4895c61c37fe7cc2e3f8c71e5575cd44662e08381f314e3251e35a24a680b97af984818d3eb8871967ff84c0409c89a1c70511bc3455379204aa6f2764eef76652335ad9a6d1632d782d3b955081dc72f9e2e0a8bdc2a42c95954d9f7935d0598f104aac431b09d73b58cac47e2a36e4e207fab3773390cd83e50fd37c0bcfb587ac99b6f30375e980a840260b203b79a7afa81823cf02287609d4fcfa146e6ac642b4158149f7a4cca0557a69d3ff5f733edf3e06849a0845742e0e11432d494b6bd65f3689b2a67d531ab7c5e82fb70ad2340e30f2c49a264496d0759224323f63a1895a43fa3c44c83073d943648fd414b3f74f10e46f770adb19f5470b7da00abf5ced32b191e3decac62d7a97463ca5244b40f915955df7fb20f662431676038156331062ee94e1347c5ca0f181a65049218917bc12405e33cd2fe3ee1f468e0726b0c409af029bf214d61dc0c0cdc26955765397c77a12336224854e61959e735b0be7b501e4271f0f19b2d6b570a8cfed4093062293ec7c2c18e22c7cadfb4e2fd62f5179b32e60a376cab08319b6a348108d2a9deabbfc07b9526d75a32ad05085d9c3a9b9f47e6e0410948793a11c96bfad2820c6dbf540a60620bc0efc338dc9522be3e8574c422f7b0f4bc128fcd30dc95fda6a4a73a5c2887620e5192a54339703d79b369117d603c9e1a83d924215c7aa3eb12bed2a3bab202ec14067168507376f9fa2dc4cd435fe57559a934d0d50e8574f5b811ea881ab579de04b4d46a55539326847741eb13dc4b400b5cdc55367a0dbd46060ace696a280f638c7956480fda1a8bd91dc95607f2bc00c2247b9a5eaf478eca8d1d8bbb312f9de172b5b6f120ff914c48eb7de349a497f4461a02f9b5e5eb43457bd2240ba523ca04570121b817d007aeeba11c17e756ddf3364bff5da359806c705dbdf8cee8b7e48dd417426de632d27fd140a53aeaa62293c5dccc2d8b6f4a9ac9b0734216294d209a58a788fa18c75ebfdee7a92268f108b12f4c1b83c9d5be8fa4e346877075b41d51a7c6c5e9c548b83740995a87fca255ee89ace12a6493c9486eb7dccc9b02d67f53b846bb05afc56b1a064d86080258d6175492516995a19a634c37707721209125d6362cdd600adfea90bad40b0eb3ec7f8d9eae2488f5a86915a29d3447e7597478530a2f086ceb2d4f582cd676467804fe98e280c381dcb2932889578ce6a8990d57710ad256a6c8398ee412649c14fc7fdd1a0f3a848d482d2e3b770e00d5eed8f26a12f044b8e57578687d4c93bfb88df125fc104de081b3a5b801e26e2edfb4da4e3257fd7d10515a2ef164ff78c627ba5380f704c3bc0c093d9b32cf024487e491f4d2beb110b8aeb8ce70db4006c240088f4d1a60e414a84d851501418b21eb542bee74d24fd3a6b7524b19491ca8724ecee9ba9b96b3f26d3e4776ffb93148c45d493a789d83de6bebee24461de41e017039d1980b67eca5c8489f14a4a75d2221bdb6d479b6bc9814e5b312d2aa485700190a83e0df213fafff6e158e50cb05c570f29026ad700ab9508df1c1c10abd505952ca0e6c0c6bfe4ae3b9ad7d5110f11325650a41cc050c28e90ce815c0ef7fb4b1e327a5cc2fe24afdf46e4db41517350372b77381c69b8d00a98b4ccc451814feefa516035f0b2a91bde09db77ca8057d4266b2951775955dd0541cb34d74a4db39d35de5f9291f4655c29a7babac2dacdad8cd37033b9cae554f05da38a321b205ec0a0805b85edf40966b9acf362ebb585c58bf467e4d5025f30dc1d52b997acaf80358711f41c4ee3da5c7df582e4d87029229471c5d06ac69cb5f138558ad1a91b2c7e5d5308856642827eb4799233ac8a58048dd2ee3a254a5a13250328a35ad75c505aa6ed342d53d0ae11b2e91b32f97aab90b0318a6be6742987eae3fda663b44812b8c924db6c539a63e74c1573b22b4b52dbb1d3a6e64c2a07248e0af5c7f446470615049e9f6cfadb2989eca66c4bd6877cdc4286f4d0eee92ec82a8185af5203ee8368730dbb0b9257afeca0a34aeed888672ca74b2900ff8204b8cad939d07df4c215732b11d4fddbf0fcecd2f41bc3028674bad903e4b0e2e362dbfbcbe471771de895363937a63cb5086678e3594e7c09cc29ae4bfaf2137e086d5d4c28a20aff0e24777f5c5aa0587a5a3e82fc009a9a873a1f3a06fba77772716228f1c7c5abd2a1cf38de455ae8bc2c5b08e9fd2e469c4ed41bc27de3caa3f82ce00d8f2d76089ba66d4f00d13d0c5c57287aa6b9fa1f758f8b76caa09b662964fbd719488ef500197caff04c751384a5a83e4efc7693bc727862c04231f9d1ae15b38e70cc710ce6d4521bf6e4e7b89c0b369aeb0c8c6afe240b22d814b5738936b6495f33b5f5cd4327e264a1acc9f7d2080038694af34dfeb4a69146e8b872917c6b5ad755e4338b3a191fd6ab53f465e571d18f5e8cc22f4633afb92d01a8408247e5449f3a41eb66c959bbb9ef416a0a790503f534801a95e41dcf8284b2ed1d91f2a4bf174d264a33ac7d82b188006c8510b0e394fe60b84f352c8f67029bdd15829bb65413f0a650ce13170c7a52d61ce26cb36679ddcecbfd34ca9a934bc5a801ffaa8b62f78a2add22ce17015ad01b06cfbd1c5c1c00ee4b7129ed0a142394784535ebb24f2028d9632d3743c63429e5aa5ec2f0bf5939632a569cc31faec9c0f962678c64615f7eede107911a7feec98884f94160e2527541c728e620f708b647d54d96e3c4e55e85807783df46bf7b5a9640f71c8ca1b7f35d369817625ec824efb6f64aaeb98a11f3d5197ceaf3f2129720536761d3ef4e0aa6e9cd6201b5393e55ea6570849cef792461ea65303edd41e202d279555107e2a8a81e135be8acef324c421d19335d1f63f7b612623b46a182a1d38e9f8e0475800e010f586ced1dc8dbb49751023ba70c27e4a7ac8c6cfc045b65fe54d7842b9eec6768d024670413c49eda57d780e3a745bf8fed7490fe974737a943cdbb9a1c7e309fc31148f1bd0aab135e5adf6e856af3383e1d7113491751638caf37f2ea9de3917e680c77fd255c9d938a0080bc9560bd889ffdecaaaffe432e4bc8adf53b0f99015756ce4c5e435094c5742e751526a59c212e9042967502a53cac0e541ac50fef424e7b19bdfde79c4d13d53e467892c91edf86c69446a5940ce242a0bdb726ca17945544e3979aa8591a090c27534cffe9a5aae4bf50b39ce66468bdfc7601e2ef9d47ea32f63e3a91c22fa4960332565401c977c98674d675c281ad91842aade990dd7325360aba6a77b60ccdf76beea285c15a22233906ce039b524d730af213b3c5139355e8572ec045d2392c05f0a1c82440b3df97b515d912de448646f8bc63a710dd7c05481eae14e120a25ce9490f7000d134fa9cf3e5904c7203137e5c6b7999d073950166a931c06454014f521c3c5280395cdf99786d0877f51f800b92774853c0f30059c8423771ea84e06bfba1610257ca8c989aa7ecd5e73def82b538d2986bacb6b0f458c067934cd29ad551ea422742c3e12924a7fbcf1165beaaa039cb5fbf55eb05c819ce95f3f4ec4b11d2c3655b856b53e106607f9e9c8cdefdcc88cece54b01c5f81b46146cd146b7826c7d9ad93fb9c186cb46bb7e258e5e83a905348171b0b0824e7aa121c53384844e542dbca334b7310436c0fe4d1365a2301e78f79c900881e124fe20130319dbce3d4c68b73290677495eedba5caa53f8ce246a400412f5f1f4be8b6f13f6e575c1571d5ff6c274a8064591eec224ca81146ad772d3603b10db186d22c6839597d89e6436fb28c4d8677010e7458a65651f928567d4dad0d28e1c4728f056dcdf30cea688d929f439986247e173c685394b07bf4b8d26e916874af3e0a6e650a39e2a3fe4a9816016d5aef84e3cd99ce6c1e93d7d56238f53ad78dc0f4c386a5c21d45b243902607a9cf730e4183518bd2d968ac6ca82b2fb67dfb2f46bde8478e5c10a5cd7167221e392f8478ffac9787408f35c64a3009e904186b1179b94d571a5e01d790dc684eec09828914f9c05facc660b003d1b98b9301d722f6198d040377f0b3e8000081676ccc0add35383e5b2adf3d3f8a461109e678e97697666712eac072090eb94511f3f97e905186cd32eaae0e6f196db5c1123437884480b2ccdbb95694bdc0feaf1f2391f020c9457b02d1939c83b386759c47316d2cdbe8c765f2e25df05995f8a3eae348d3c5609642417015d966aa991974ea66ff3176d08d00849d88808a21028a8b6ea396daa24d99330cd1d3f390db7ca46c0b810be0420ac4957a0e14a453225320501ca580345cb71b3b567b3e487cd764cf441fec6246e89b129004182b374ea4df53bdd15f4227dc88ae00c8216c184d75ea8aa4a5d604b0c4403ef80f42c7152bff9ba9a22d30e34487bf0033c21a29d6407154ad64e87e13cd4e2c0c6d0ee0d59421f678252840cc19723ab54803f1adb66eb9aa56465c079887d09101e62b23c4e513242dba9994dbeb241ca16fa8888617b6cac2258d507a4bbd8e428dbb8ec71d341c186257cc8c0a657392a239c9e2c15771d14e83fb8818757704d07e07887e106b37587cdf9dc269a007a31d5fab398ccd4a09549ab9718156a892aa82a26327347da40a065ea5838f4b208fcaa462f29f718d981253f769071ded21064702f49697ecee70965466d56f6c4e961b2317f379f657ef561a6d86b05535726e79cb89a6582ba228f29402913164dea1580003ea5c17e0bcf44a26d21dc189f1b5996351ebe9a9ba9eb08675d167208e1f7b8f3c655b05a67468ea6c7d8612ba0b7a7cdbcecf735568394b404c0266f480d1800e7935342272d346405ed2077d4b16529302cf50be2c4150a112bf770068914ed6ee2cb73b4ce68c80e087a31dcf01f53857ee3f04bd3d6385bb3fe4eb47aa682a9633f299ab45504fa30bba25a20181ea50365fee81889443650d29450a0329046b6fd2852c97fd42a8d6288a74733fb5daa8a5734901a1e8b0e40602a430d721efe6d486786ce9b85da93d80e8c0c12dcc87f72017ed466d1482344eebdb72444ee2da54c3205c10a8a091d0a1308cba1b64dc68eb0d714614bf5ab5e0e1d6dc5835a870e09fab0e97dfbee9de3d9e45bc132489ce3d1114e7ff1e30f3cbf6f228fe7348ed55471ba8b0716decca75e101c250c8f1d613e9bf0c40b7a03968518443cc142842221b0603efca00584161fb21bb020818926c876c08204ce5dd4576ee07e61b1015bdcaf971280d00de1d323134b99ea51a7582855d0475d3ae9bb4164e7c60e2ca3b1ffe43ff91f6a471dbd02d4e69c6d4347af002f4ee3a04038effb86fee6e104b1aa94ac815299e4df8c73f6c0b1c8e284216459c214ed20adcaa6ac3d928512848a7670430dbe60507c5005113ae860adb5292f70b6b8b1e8a2c5931f9e40825112580f561c618c0c0c1d72d87a10460cb8f8c1890d37f01026c9078717d20b0b24271886611804a6c082071f2051f485123e8412b0c801ab825a11461068b0c10b0f7c40a58b2864c000dbc1042fa31f54f811f358688ac30eb04ad3a04783fb25c50e1842083d5ea14165ce89655224ad70bfa4d062d24aa552c954a158cf81af570babfc8395d3b66d474cc22affe2b61991b84b8da37fdb5880f13b8f368ebe24b0fd7a15d5c0fd7a49c14f8750bd94e2c704710047588afb2545ab251936e84816fc19c5129cec203d4631466683fb15450fa2c892443d5284a31d18010544c85206abb528acb8200b31b4a0610c1aa81811a910e560ce395357c06c81c208cbb75108a9748840c83be79499084dc909657b944c53652050d1875e06fbe1959e11986300810ae718700f7c7989a832c5aec1fbf4522b36abf2e71bd65885e3c5994e5e3a70b53419a3d795a42cda0ca0d71357b61cbd9ef882014df11b29425ec54aabd5e1e4e81568e9538161bb10c202a0e8af4087e025136ec0b1044b107e94525a81a7115172f4e6681cf355c0b3051c6dfa663ee7cd1c3f5234d5e444707cf8cd99b95e20220752be1062092928c0747882f900290618866158bca1e70d3ef002888d0745d002872776488253ce2909a0c3133e5a580cf7cb892710c087971361d897135f946007a9c54910aa1356387105899a52428c51ba7faa7eea2e31eca252cf11720501fdc4169824a4a32d46af22a221215710d08fb7c024211d6d317a15110d09b982807e602b499118601812a407cf0ecef72a4be30d6d9f2224c51525080ad0931f9fd90a03e64b9217a42e475cb66831caf2ba526485a8cad0142129ae284150809ec81fe9235b6130982f495e90ba1ce1c8c5bf2d5a8cb2bcae442b18be4cac2273641cf20f36a055a9e7c0c7b056a95494cec7eed481339940972b68d950f6370ea861d80fb3a8ab2ab61fd288f147e3801826bf3bdfc02adbdd0dbd3b5651a9e774e350bbe9dd6982edf78657409fc6d1bf42c2c6306cfc9294f26569963efb5aea52e931997d2c956ec81c52874ddff44b69a5c280b319638c47266e2fc678638c5147e3d0c2c647c2aeaad8f89d819f4ffdc78170673103b2f1e96331fbf9547ed693bd7c2a367751cc30ece6ecbd2ccbaebb32170aea9bbe34c677ea39100ad1385429f78edd1da571c0d9ded0c334170cdfc6dbf48dce417333f5af1b626424f12fb61552cae8d0217c6c4305dc10e78831c6d88ee35f3f0d42ad34424f7637f41a6608a3c396d808c4f16626d82f74e853d8c611e3f412c4be144208a97cf83146195d89fa594a8fa671cc19259533f60d7a851b8d634e2d621fb10845504d1822125eb1ddf276479af50eeef9a7083d6b658c56222cd5186354417728638cd325ab0eee170f5e30f5dec6bdf8589884e49f8dbb5f292a65b15008ba7ed502d4bef49ce784fb2ecbb86f4c9534617141b8cb7de699ae474d5ce9b94c268bf52007f227d8ff86e14ffcc77390fc5d2e21fef96b5e10ff6acd1238ceb3d38b306dfd80f1561218a4a4a38c3e7dfaddf46b2398443d4aa9c948c42068235cbab9c91026d246f46256544158f82ecd4d3ba8f33bde28638c32668865308319cc604e49cc331235ffba6191c7de4e187032e45f3b0cfe3915eb5ce94df533cee6f937717f1edba1e4246e9e38afbc99480a639e39a9fd6da3b54b395b7677cbee38e38c33ce39e7752fa2e085ad31d41b05a41217f221d991e5ae78033f85969d0f1f7ef11c5984096a7002777f1912496929ff2004821204e4aef81e04545d5268201272f9073f432ad65d10097ae9241c5da631e2cca49765524a8a69190644624d85b9749ffdc5a750662944c77cc8d938e263790458becbc611fd739904991cdc638cd15d42f7ee6e82c148c559d3e590869e02f78b065ce0eaf0ab77a418ca17f2414b4a8fbdbc13a09fbd054a8ffd122a42b02ffd92ece92fb9b01597d09ba16083758c29814ce800c40f2bfb2aa9bfcf9f71f698b3747bbc9218af0a089b2110108540f1e6aa58603304a28fbdd35f82fd5c42450866fa55fc25f4aee21582bde9b1a75fba4bec53ec210a4eb858a5a75fa262601727de380b99a784092c3a4a428845456585092c42c207894505aa9480503c080bbd4277bd826b627705e75dc1f916c03e7b0bcca793e54a661006146fbc95c4bfde02817a2b02b56861f8ace935cb814c4bb877b7673de7a440d8ecda60118a63d21b8391a3fe61de0e367f37b20da689db8e4c857d8ac2ed471388336c8ada36d555f686e794aae9bbe3f4ad058861961dd91eb44ac76d9af7e8b0e96df60d91fdd1f7cc945d29318d086dc77322c690a27cdf7c4079537e63a39277020725580d840314d6b6040e9660d5504103ab9e208955e32809563f4ed4a971031a585dc50962c0eae7893a359a3062d598020c568d2935b0fa87449d1a47f8b0da280545ac0a861358fd45a2ce0c0e5ab07a0c560f01074fb03a0b568b210410c2b0fae11735b01a0e5181c2ea87425147c715376cc16aec0a56aad503ab1f16451d069c008a17ac6e2e4760351080c06ae824071eb4b01640469820b0ba817004563f4c8a3adb1090b8c2ea8760a2ce0c2bac2d095b7c60f5fb4fd4b1f185d50cd0a18b1eb0bab9d081d5efaea8d34f6869620b963ff1a18b18b01a5e8185cb15ac7e278a3afdc4d018276075562507ac7e7f459d28041a7c7e585dc760758d82d5375a2005ab511083d5ef495127e78814b0fa1d4cd4a1b185d5363becf06383430c58fd3128ea94acb04e5a94f8c26acd05464060592b395ec1dd6ad5d062e598c5b672d412434551abd58223b072ec82f918b5688062c4cad1cb5cc2a8d5e202042bc72f9468a8d54a4207568e61729c50d46ab5845859fa401a865a3e43706165f964fad06ab55e50042b4b2825318c5a2d22a22cc1a8d5ba8207569652a018b2610b2c7260653905cb51ab25e50656965588306ab55e70042b4b2b520747add6105958595e916206ac2cb35001d46a4d798295a5162cad566b4a1656965c8060d46a4d9982956517295cadd6942fac2cbd4cd751ab65840f2bcb2f9507ad56ab0824585986d14ad06ab582d0032b4f9f1db45aad2d64b0f27c5284abd532e28795271428865a2d2a62b0f28c82671449ad16951e58794ac15d049ce794c8c59b121105064ab8683084137e68a1e086ee07a024474a10a3862dc2d0a2450e48c440e1435f09e20b134e98f8011440604f4a0003ac8a124cb181e27e31618299c54998cc09866118564312a4644e5c590d4a603e3870e2653a6902f37182c5886c87ac86221f319ca8c009941a56e083854fd0124e843003ccc70a4c8ae924cb747202d30ab61ab0d87c98e0848b520d5e309f273024d42838273d30dd60d2a20626947cbe2825ed207d8c40392182fa184d2758780d48d2490d309f32680d5a322735d48004adc2e704196a092c2b4a90c29ad86ea0e2414703550fba329ea072123334c1dd2ae6e177f7f5bbdb737aedebed89790ecb486c68da69d358d0fe64e266688257a8efb1df3dc779317772777eccf7982e1288ebd7fa266f65bfbe49061f9a0fee63bed63aa36e35dee4c1168ed15a78f3f29199af41e36df733347e06024d5adb68fccccc8c47bf763450dbf7a3be7e4dea6b6f3dba4a205dcc57cd43691705adcd3694d679784b79a798467d95b79bdc6bfdf579220daf717b9ef5aa97a1501268bcd2ea6bf5b91b33540e50a8dafde9f439a6c66bd7decb4570e5acbdb948cd6ff6e65ae365319f9d22865dd73dcab35ce5b6cf9eabd63eea3b7b5731319d8dd96e8fc6797b24367077572894e5663c279379ab1e8d536f3fe6372f23b181eb5d71cfd53ae3ae667cf6b5a3a152a95434ee6a46ad3277d575d90ad5dce937191999e7644ed9766580486c60ae7bae7b8ecb2bd423b1814fbfea90d8c0a9bbdab2df32eeb355f7dc67dff6ae50b747e355ea7b621ef5a998477d4ccca3503116d5bfdd1e8d4fd77e67bf7b6bbfb3dde59ef372132b57546ee308198d1da9ccb22c9b32fbeac9995189bdc96b20d208c43e661018f612a9c2cd07166594df110806047b1963bcd9b1863bf8c1f027180c1f8b3ee6bb40b38b4120d35b09e978e3f21e8138462410c326d4593ff328eef0b67137f372924c71a642666e8d6c7bcdeb18176e5ea279a8d7bc7e2de6ceb83083aa713bde7030cb31cca83f83665e04647ec6631e90eeb7afd0ab368a4a6fa3c8bfd69b9b208998fee6ad7cdc9e52c43d343e2b7de5beeb7adbb29ff1ead665decc0cdd4cf79907b9ef381fdd73d9e28c898ffa5a7df8dbc60477db37ae5f2fc775377fb769db6b703b3dfc158faec77ee32311c2c6a1799ce7318ec5dc9c3df7ad3dfc2c4391163e126b66840946b1e038f38c408ccab8ef47fd8c77243e5cf9b83ddc779fdd1eedb7245d9793601822f5aebb5948d73d4fecbc0c5bd87ef5320cb82d77336cd9af50832dcbd16c07ea65f5f2aba2f5ecb79b5da8f1db6f31a713fccdd3eefc2ce2d376a7e6f59c2e1288b35fa14ede0af5d99fbe5e197c74cf3deafad85efb2cbb4822ceec0fce6e0ffc7ed8c21a677b647eab3f63860b333366cc98e1d1cf4af565bed2e7b22cbb19e2da5e861477ab9ff92cfb1a5e6f40b6ea75f576bf09e12ae4bcecfb67b84a5da8b1c9788d6be94b5fc38b1b10fadc975ca871b99bc84526b2373d8f73dcc3af35e67b8e445ce37af5b2ee6b78b546eddf8a6054cc968f64db8eed396f957d97f1787685d4d8be765ec6715cee3ea37efbeaad3ad40af5fd32df7d7dce5b718ffa98cfbcd5e9ed675f65bcd4b6552ff5325e4dd5cc6ddb738ff2561beafb538f7a6b7ffb1ae3699ff2627ee5cda43c2d06e5c9004f57c8ea57ddd7f899cfdd1552e36736eb75bff2666a78339f79dd5b2feb4d488deee6263337dbb4ed884724e2ec22716c4309ac09eb0fdf48f64fb1d62a4ae501bff4b5e4d54e48295b21f6d27853aa9f6d6ff2b8bb7d7ccdeb29bde94f57156f4cda735ef6dc9b5cb0b7238d3726edf6946af7b5a4752e9cdebee6753727c10f79e2f6d27ee769dfdd476d092e7d73df516e2e9c6ec79b8ebb99892dddedf4ad952a8f92e7037ec97a27ea82fd936f3c4a3f6b12acbdbda5ef2fd19c04e7fe23910eb14bb0212a0ff826887de6955694529e689ade95b4a7268ee39ef332cd4df0f63909debc9ef9be65bf651a7dd373f44d4f4ddac5b6db33bf6eb908cef47b66dbb671a026d99163c0d8e72218bb997e73df83bde9b96bfa8e01675fbf1d7bb941a0095e75a6ef4ccf5d53fbb7dd9b99e04bc4e9a58e3d5d7afb21967510f7b6a374794cf8990ff8f3963c8857a65ff9b8d8c49e2d51b7a75f79401ff4e13b2e79f9c31a863f4d25ada4693717f91efaf135edb5dbfed5eea10fff74fa7af2b20a9fe8d7a89dbc06d2bdfd9277bab9082e55d5e312f7f2e310acbdfdb8eda8df7d8c77ba9989bd72f3e19876d7f431a51ae371f5e2ca83be0ff854f3aac903a27dcd9007fd69fa7ab3e91e91a5928f1e4cf001302feeeedeb50df6ad7910620d279401e2f852cacffec88633d99836bf62a68961d83542b33b03f63360d894917a0c43a876dd104a2803c4fef1b13fb2612cd29f5e9c7f8d6417890772130c679833cc4772c2f3ce00ffc826af1107c3c33ef6d682ec913e9f05bf0d53c8472046e23fbd8629e0e8829a24a043f718a59c13c3324a4bfe512ad15dde4ca394d11d4aad524a391ffacc4d5a18c2f70f2787841f3ddec0813b50d4b699c1ca145c7ab9054a165cfad209c3557c8927865b32188768290e15c5a1a11987e250f63568b08da314fffca97fdefed92d72cb9d9fe116eca85780d86112eec691a11658bef4f6a48704627a73fba77dd98d4798946a8306b604553318d2b6adca703fd5fc53f506e5b7bb175092a8e76834bd83fb5337b04fffc3c154985723088bfdbc99f6883a4d55f87495746f6f4b2e36f76b02bd820c1070fc20bd02f522bea3e0c5f2f817ff42b1f187449d8e415e9166c8e67e0d7905c1b4927743a757a08ec10d48f6f2d634c1e62638cb6872608564d96d9b1e4651d5793bb17ae9e75f5954c1a67f6c2ad53719cd943e9f4aa58770276e3b7947339db49d9d5ec171e95f88c173e2973e01ad43e34de96d13a552a9542addf0aff4d0abf793a7496347ed665aa2115e161e9bea4308e3f6a3093679d2bf4ea202de6067bc7400d4ada3d34d5c83de0f65613f3d21de6261df5e66821b89c4f3573ea00b4f29c4b2b039e52502c3bc8e37f1a64b8c3addf166d88addb171b401ac804f98e8934e13db13bf33ee221d8630c49006dcb73d24b2850ef767f92b1f262caf912b300ece5d06ceb1210bacabc0426323b00cdc6d44766f2c50ed8533cd6b074838ab6854dac9f60a2aff3aa8de48c10e58c01becc45db443165f149c35ec7d053eb1f0e59d510bc0c58d1e0a40040bbfb5da36392bc231c26d488e4038027928f0f78490d2c60f16c3cfaa9fa803595a8df30a8e39d8c679fecca9ddf9439b5a193f3e1d22a9a492528993c373b2a7b452fa254c353b7fe68fcd8ab227f03d258000d0c5ca79058531ec7c41f8f13dc720a04f02277e4ae5396129009ed8f70fca1f2fc31ffb36b039fec420cff1279e13dfdf15853c67e58de1ff60f8ffe339fefd8a51081a57d81efdaf69349988afa5accd9b70b08fbb0847881a7c7181e784dde9d18f43a343759fc63fcf47bc4a1370de91b2fd5f08ebb9ecbcaf9ac2e66f35bf1ff03b6e780eec07ecdf63becb2fdaf9f58d63c9e2c252088c8b0984e5f4a1819d3f77b600f0c4629fa7f7640a24dc2f2c493970bfb02881bd28ea74f424cff9e239d8536aad4af59f7dc99330c6f82c60eed1344deef14449e341b192c6e873ba43b1be333d27fbb983f30c4df24ee6e520b2133774d8c05624e227bd817d9c77200b6cb6d8230f2a9511a4607f9a96299bf96d039bba0afe190faa3f4d0a435e09c8a6704bf1830d70c0f862849f1d0ca8c7173b3a3a0b3684fd69686cbadbc6a87245a11f8233b0425f7441cc2dc1660c0828eac496c4801a03c2802612306582c568c0fed15f13a971fc7847d88c8393031ba24540571c1afa420bf64f55e9621176e872211f7215fcbd1b6c76210b7d0862e853a80c611f5a41c83f77a1298dc387a20f6c8e4371c8e3908d2245684a169b5bb168b6aa0f95f9363c0bfbb611b28408c75fc5c07a44fc359fc8c23b44deffc1c691cd2aa3e789371907e70b2bd83f878a46472aeae0dcffc1661cac81f7c17616341ae8151a0f11e239f05a213635a968a9c0b8b0bf121ff9363c8b3e0621ccd8c4300cc3ee4ce22a804912755a9c183653087443f5b1e16790abe02fbde97215fceb0d6c76232ec95aac1575e43bd21637b253c8735e5f68f11c0f8a32c5b93061734cc2fed5b760376a1cfe52e6f862e1169cdd08c98de4c55aa61cd86c53d95114b66dca46f3a1225f95020c6cee97e5c2661ba5b0d94ee1fd1042082184dd72f6ecd9b33b1e51c28521e7c3a3961747d83f55e408f491574629a38c52ce38b18a6118065d49112fc93dd247b08beb7ceba936212da24256db7047b0340cce30c8837e9e508d0642894285f5f716ce306805bfbf0a2f84b03fcf8e0bfbebf0e207b3dc85c0b9afc8ff6c3a1fd82d7ed34714c926a97a00e65b7ed33c55fcf80d069c80f19c24f8e0238e6790b874c2400d40b5f2c40a352712c28d31d0b2e2397d8597271870f2c573561870f225eaa0be7bd95ff2eb8eac8b78a20e8e6f363552d8fc7f6505406c38be6f3e6a188c79c4255fb00e143086cd4712c2fd42e27271bf9088f0c72094e765e9674b64e1e72560b07fe45183b1db8d63debc2409e725449888bfe21005fc3a0d540034c2a25aa9c23305431d104ac110429d4fde07f0d44461db891b965094420bb500c718a54c410a9e7640eba85a075509f46bb0bc9988bf70bcb4461296c61b252c8da276d47126e8c0e5d55852f033c452d2647c91c31616930d08b6a0f809623181b003b56d4cc0840c8e581546814511ab8d78a084114b053c60e16209c065049722569d5ddc40882156c54cb04412ab662ae04110ab9b8002c688d542b0c16788d55f04e1082256ad5af4025084a41b92584a04d022a208442c260bf029c20fabb1f0c20623d6f7050a767f1bcfa1c1fed45ff3af2d27e5552202564361042d7e586de3d1871a1920060f492c252a5811c6118b09aab5c5ae8448251e049630462c29fd8bb025bc42861118575094167c253d7a83a10130a4b67130411404ce1667095f46081d32a1b60d2d515a8247248636efe0ece8f01f9e03b1a79023855ec133d3f7a75cb31ff4e4767568b7fdc6e10374f88d27a015d0381e4feb04ec65af5083fbb71da58f1efd92e7a3e1675007bc3ba723acebf09b1d1a50b025d4b6f96edadd6472ffee99be898ffb73e933897dfaac61449f4253e9a6fc939a855f189ba9dfc08fa988fdaa1a47b4c266faddf0bfb468e2a07ae907ce340e7fb8b1007deb4ef9d7051715f42256b547d7a9ef697ed3ad00c8650c9c1d6741f8b0858ba3219c298e9716a17295ab5ce54fe3153aec57173dc0ac975b64caed11f6039d8a4aa148e44352e89314abe1353affef31f9ec43ef51a9f9f69c9a87b0a606974a5a95534c2e29bd42728a7f9e9442fec1cb43c2d2974a39353fdf8f681d2ba7749caa79a5506e304afc9b9fc4bf195f06c1d02b34d6817d53f3b34b566a3ea7bce6b367993e64e3fd08147eba3fbd816d024f14bae0599ace05db76d8a8f16abeb71d36fed53cf4e4973c16e04b1ef03188829721aeb936fed53c8fd7ac6116b96e1c352fb51a221f721b5658d3ffc0ade636163ff38691c34ed6c6dbf879e239415127c99c70e298b88753382821b5ee39350fe1eae1b683e6874005603fdf850c748ee9e767ea7db618d5f0516ff2627ff0318f0686ff1d84355e8e2f74d212c6e3317daac18d855a9a621a1212f2a12a442f37f22d6ec5b3b8162f9a9e93b10fe2dfec998209492121cc83b8cbc67536de8643b75274c58750f81a358ff6d8781920aef9209e93e10fcfa1475a2ad745f1cf6a5306c9201b33b0335ef5331f5f0a094921cfc15ec7fbf9b268d69f2fab78ce7605cfcf320b9e443d1febac149242728a1c62c1b124a2c9a7f1d987e8cb8b04d2fbc33f1b354f1040838d5d3cc7c6cf8f4851e73f3718e9aa79d37ccf931b0268b05906f504f16ffe11887ff4c8284151c75d320837f42af3a14729b1e9e62518fb98e4dffcf7e217bf99d9e7f6dcf322cfadf837df5d13859b29ae71570d2f435ce3b18d4786b11bc348b1f0572ff333de87b46cf19c159793ed52cfe334980fcda743f2ae543ff3b9c1dc0008d702f73e047f760b1c1288e5cdda97bed478c653d1f088f89fe1c9789e0c30c6cb5ae643783eca93211b1267d723eafc9ddca659f8df377de502c8416d1b79b2fe4de95311b083cd90c8debe90c8b30f3de939ab7879c4b76f79649915a845eb582f30169180319965df798d136fb0a7168b5f33af6b11cdf4321eec9bf8292fc683561a75ca2ea47a94274f39822296619909c7bfcce413630cb55608f405679969ca2eaf2c528be422bb74af2b451c07adbd48208fcc73b02dba544aab86f2a015ffb0c77995ca62367c605ffbcc7f8eecc582fc27eac43cf60ec573a2788efcec841d798e7dd4634018f3208c611fe36547d4b60c83b3f7417a82b39f5070f676fbd104a3ecad5de7446cf0ca08c4a74722717c231d8ea887017591407cba19da7bc3bf18fcc39e03fe61db860027367b286ce5348c728d655f3b9e7c3c27d3bab6297af705ffb0a71a3d71dfafa32f3843220989b85849ca6d74c57330ac888655cc84758fa5fc9b10a848d4517552c47060c0bef4190da1b044cbaaba5ff58924f19cf8f57dd4afb5d65ac3e0fa4d1fd6d7d115dc5e70bfb2d8822bdce6170c3f460d7a595ec1b008e248875863187d2638bb32a9be464fa9571657f0fc1dcf51f1788e7ffd1e5555359aaa5458efe15fedbe81945ef93d6afdb6a9b707b6b0252e300327607051c51554848912813862055bbad8628b11c48041104198c11170f02148881daaf051c1ccc28814203184215cc9d283186300318f3065052a3f4f4822cc183208c2420339ace04b0f2508eaa10a96021370f92268e04cb1e822bfd287d269d432460361c4a8ff0c4db089b5d05af0bcab161cf3386e8241cf72e79c73ce3c21c5f36146fbaee4cb246cc00afb3766d89c73ce09348e7e991c6c8c3d5b7ad1b7146d79d11d5e5b68c1bd4516dc2d01937c93d79b04a8cc62fc58a2b494655966929b04fc8bab52896634e062bcaa69b6b5afdad7faa79b93606fab45f0f6f6330ff59d1773aa7faa9f699bb6755e6e125304f7ccd3e87e86c6cf68cf3df184c6411a35be964a5f4fbf51ede6ed2bd7695fbdda6d0fe16b5f3da871a7aafd8909e65ef37c701f03bfbb99d6705caddc37d6b6ef3cce9ba1098ef99819dcc77c6f5a0ff68dbf4298bfc678272fd73fc9a03a542a95abed6eae1f83656ca5b1b57e198665d8845de68427e88261199c451815a51a9e0076d12ab53f60aa2382e1cdf32b0438ede38cf7d4335ed75d8d4b693210aa64361a3366d7fd065f06de4c24055393d31ece7ad2b4874422707af8dc77deeaf4f5bb6fd7b4d5e9725fbf3e470477b0a630f7f0747bb6ef52b8bb4452d86e3eb6db2a1a57ce9832a914d4b40e7615c22e057f42a85d6ec26e42082184282f13c1288a632c8e89f91a73baa70ff51c17f3a76fcad9989f93a6f0bc99480ae56522a98efb69396ea6344efbe9715caac3f3bbc97199480a6b29f88021449a0834006207a926020d80a08208462919341428385ec16928500e10a1409161a0a86cae58e9bfa3809e9ed669321675b279a1fbc8c5a44cf6a22e774f73ce79bbdae1a8a2a15aea53aa97b1aa4f69dfbda6c99c7ed6ee352436b47a35cd7efdd879b9ebba17509db742fde93b4a354d5ba16efdd39fbe3ef7272fbff0029e9afdaebb446cb8db23a379446cba4bc4065b1999542ad5a53aad6adfa53aadaafe55afa2e9ba17e87ff5476783e21ef539aa5da5ba99ce90b9a993bdf99ffb19af370834e11ede6c83b99b89d8745df7bd751de7e5176c1e431a0c6930f7f6db7354d86bff50a9d4ccc39f79eba1663e26e6a2ec678feaba1770ccc7c8e7bc8c7acdcb2f74977be8698ff24e27cfb166d3a3bd041a732fd8f080386b3df51df7d6a3b14d2662038548f53b48e0e265b383042ea4a0368bcddff78551a1e70fdba1cc44d052e7b7077fe51f83707c611eaf3202e1f6a3f8ddb862db8e9ed3ddfdca7ee9d0fd219c4d6210c4ad1939b09926486e2d10a778800a683ca1d7dc10ac651b9a54d6f0b77f98b3f67f088637737f02623f88d6533ffec7496562a6bdfd55894ff1e5576dc710863fc385e1cb7cad70d6c80186cd45f0f63602333ef5aab740e055f5d4b75f3dece373430061ee51588668e1f9337cc4fcecde7e05f290831ce42007b99f0fb98ff172fd917aed2377737cb1f36bb511e87ebef6a7b7945abbeaae8fd3d56e1e82e773f5b31e10a7b2fd989be38be5b8db937aedf6a09e7b1eafdef4b2bd443e9c7a5444cd78f8dccd7d258d7b2462998733785cc6cb4cf0b4d23f0e73af453c2390ebdbe7aef4efe625986adbb6c3f44022de2260aff48ffb7ab59be11f893be66b0fad7fde839dd087faa64fc2404c587beb0dc1311fb71da6b6c11c4d4e61eedb5f7a2af5b6dbe9db3d4f4c7d90edff69a0642490d46dffe03545c0f4d56463eccb08944c9ec5e202171ce1fa36428ff3f210cc7d47934c8cea3feaccd030c9a86e7eccd1b83350375b999b8ab9f99f279e3c2410c73ce765d467eff6ebd7efcd879579ce47cabe7c6e0886da43eff4315ef78e4f5e465d5a737dedf49c37c487c675cf133b2f33c1f56a57fe87b7979b29fee4264343b0840800220b128edf48208e3e64acb0f99f86d34082111536f70bfe8e0e1e8feeaf84a3ce6df1bbd83d957aebedae92be4d58a62bb59b8fc4a01804c4bb441e2250e4e19b51a21882168c833b8a214809426afb90d5316f9f55a800ee4d5b3ee2700b6c6bc184bbede7c642f7f4e186e439d86f2fb71d5b103679361fe79c318495dc4ff9dbb7a4bf3ddc1e6e011500f1b67ddc766c4d7694baae7b16fcbbcdabcf794db0c435cb3cacfa54a27e663fc76664b1b4c78f209e331fbe0e16f59aeb344b7a2688e49f8ccf04671e12962859706309a20b5e006e2c41bc009f1e7af9886f9edc76188165e0550bf1edb310dfcaac04fb5b8ffbdebc0976ef8863eec22d600a5a15ac3d4fdcfc88ac127fde8629cc9743fe491f700b9882fcb943b5b63d932acdbe09ae52cb3e635a9db89a30186b0eff6edc10e0868773d338fc336f003005ff8a6d3c20748f374e4f320cfb52c55a0bf1b3cf10630fe3bde11f8df76b1c3c7c45b388aa695aadb53ed5a6cc5efb1a3dcd93d9c758327daeaf51fab546198e64b8745be832307d235d06ceb2c46e8271082c3070cdd77cb09f0f1f43081bba3c1b9e05855e76c8c6095df1ca30f11109b1d80c5d2f03c42e1d58f80cf879d67cfff7b781d3e61bba54345909be242fd8ff864dd9e4584db12f9b2d24427e5e6343a737a85654060e4e5216ec9f2388902e3aea9bde2524e47209b9a440178ecbe58a49415651d8243c858868ad120c12f2cf8d8ca04ba607d005849424c465250616167e4c7a127bbc7afc78c0d10fd5878af419b5ee1e416c606310608f30ba60ff1f3f640f56f3cfa6781e8025684a1ff578ede8c83255a881cd36017de34f593b8dc38da4c80115acc900230e6ca62d486db05118f44d5f4d1e863ffefdfc98624d0aac114fbc811f05fb13ec57608741a28e6d1fb48ee91d628161d185ad7ec1332c77c9a8a5fa968c116cc1a4cc08368ca01bd9435711ccb3e159548a24b1f1163902049b3ba9086c9e8f3aa55799262cd18bdd8e37f9db51a4099a8b0a55183e54a3a13bfe2561338d5b6cf7079f8c2a5d38fb910c024202fdd01f22ea47447e9421911ff99117ecef5ddc0b9574c88f2891fcf123223ff2a3272da5ab8bfa72c7e3c934efb8c7d71be1fc38f39ab08ab2ec2ae97893a3500c6ac28a40334dd878e54b62c1ae2c8a3744576cde21a23f06e5080494654747961865099b77e27b32409c77faeb5ec2365204f2276ca6389a3472c7be4082a14a10215d62908b0ceb4f630516c628d115831ac74c1376826142e3f0998414dd592e5bf18a85374b2224dc464141f3c8bf56bffc7bfcc05a3d5430ae42eb09d6679eb0f0711cccab83b0bfc4c971251e6d61f3e73f5bd5874abf0dcf2abde69f7b3c5e0f256ca6168a221838c00323f6b1deff82fc117cb0bf0dcf03b4dc20826a7ea38ab7fba879e2d314451f30700073ec042d4ac148c2fe5acb070a1582dda14f6c862df8b303ed1476b6eabbe969bd41a2e9be406fbb7fd65f94f0cc9dbb53bbb0721a1164084c0f2ed8ff07929730ad658b43a65716a424239b61bcc2e608b47df7a7ec43d14571910a84bd077424ec434343eefae2d50822253dc6c1acb77e1cc883a0f52050bc29e2b697ab8bcd146b911221e43f2fa129561c0a7c1b9e657a77a1a12c363798f8039bfd487beeb3bfb68fe1155f18c78289ade8f0667be1960c07c38144b6e1e6258937fe9a67823ff1860b9b29a65a12f58c09306058e9a29ce8f57062b7c4de1277b06048699982fec91dd8577545f6937039d0904b4a15a3e8452b6c7697bba45004fac1591205cd90416404042a8c3081135b8001f4431345c020c51006fb13f9b2a323862265d84cb514582f5d5aa6a0f4b420db02a42d43bcbcb0ff0b54a3e932c43f57c11f853036d3a8c5669ad48497216fad228c9708ec9b35b10d88ee8e2a15fc197498e7f3b90afe363c8b4a868fe3dfd71e4e7ffefae2dcc0f95c857e1eafd16b6bbd0461ff94a4f24694c1c1e6ef7e9fc5e68ff6f7e9b8b9af7cd8480b0fca947ec1fe5a9047f129290bbff2d830f66a1cfe364a80b9a20e46146fdca3640f92ed4921a2a82387a4101196539e0c2baf0c722914848786a43c9248f4775446b082fd69bea688a894ad7585452d11d10c0000003314000020140c094422c168241a536565fb14000c93a248745418894990c32084900100100208000400000088c8dccc00bcbdf5763e6c71bb01c0cee3087158de58af47fafc668d52d7008b50c11dcace6ef541765362c9b20775709e16b79d1e2c8ca5fd113d585aec72495f95a9b562d016689e6e6d922c58f60627a7e5170064bc88a92bc017595dde71fa0805c6b5fb5338e888b1ce8242a363b4dae59a0cfe25656837fa81860b6dd383116b0544ae81af24ce2a5bafdc4a8308f5741483cc46319365a504c6be0e5bc65d12d3daf2fef471af7f1d91f59de9ef45639e56ab34d38417e9494cb508995c9744f9449a6a3909af86e09a94982a0690e6e8c28aecbde3bebd5e8a9f54cf5e83dd01c61ec3403c0ffb3dd6b1f750f5ee416ece557ddd8b94243f0ce583c9acad7be96f3249ed9cde7e4707c222cec723d0bb89738586c6904206add0bd82ba833c64027d57388382da5eaa8340c150adb0b3b07b53699df40684ea7d299eabbe986c4f7d87a0c0d05cfd008b8dfd5d85f6025c6f6ceec522e21b48633100332bdbe1040cc59c693db8a589a7dde0c756affd0bc1a6fe293560497f05b6f2870427302c2298b9c9dff58a90c1563f85691b21146af1fb6b6c2f7efd8b5ffc321fdbef62171035ff6cc51c694ac1af619b8a96c0c9d84000fe920376504d4acee79413ba024f8d9cb34fbcf2da288c33154ff8e28d18176376dd4c91a0c3e4969c85ceb9476e2574164263d7395f3a9fa3a7686eb5a79cada1dfdb82fffc84143d670e47677262eaff857ff6532badcede4f8f3ebc0f8aff899d6f4b46d11c8a819d0b96ed2a7898ad8b98a6f6965b06fe988f04efaa17a1520d020ef7cd69d983ee35621a4ae0d0f997742e366d3ec9c6179193ee51c54aff24ad0479472076105a47773dce41488748fb927472b8eeb82ceb988796236ab76dd152ce500cf95dd060a41556d6e03ff9323729cca2f563c671790b4c4335e868ba18d520c042d23214509e5718dd5e9a6a6b566be52c1852624b4e9a721df7cf094106ed7919aff9f1f071f06bc886188a6388081bdeec4f752ebeb2879cff93dc511e1fc92ad38a3511a483f971918cde9ce732846bc7cfb927631b868a5571f61539dbb0e13f71470ae4ccf9b0769b06edf3311b561bd0f337c9982c39a77680f6fd7483b10eaf55a387369f519d6ea5769b0fa9a5b6b0216d737e565404e89a7a8625a9343e25f753b87941f2bf64f38148b0bc512cafd94440e136ff4c6901e8a8ee9e1fa149503e02fb40d9b1476046497a64fea740bf5c9bffb48fa9756d1e32328ccf4af0be8cae66c24fe16e3ec865fc28df801d155298633e82815a526e2f779ffa687a0ee25a9f2f15c9ac3c0e7a65266f3467a6292e638f1ed0745895ba109b088a6074749acffad589c30ac95b854add954e3dd31e384c874ce359f09ea159a19feac963e71c660c392a1873510eea586f5504ccbb0b53fa2c2ab43d3cfd85a7aaeeab5785cf7715c6534b1c23af9b221e53910aa53b475a07810bb32476b1e6001b5817cb5c9b17c719a6736118962623323c10e32a486a24fb9cffd444c3397fca3a52cd70f0bea66e38cd39b1fbb6e22acfabcc9ba169e7e45f97be0e9711d0337f89ef6340cd72ab1e21e29e81a0feeda5064feb16dfb9bd6b128c405ac79fa01c6955df03f79cfea36f52f7079ee5b4d1ec9d50f149f1c0f6dd86658f832d60e088d234e3860a66423b3b9be9cb60d3d3a2451371c8bcbd32bde333731b619d906a8d341f009362dab908ec4980607b93d3a94001305c7d0bd5822e959bbcd2868fb30ff3b0dc88b74dc559a8f328fec37a1f39bbd06da88a34b31fb030b1e5cffe26e22af74b5d1e45a689c9811d4c58aec15de935c61d40463f52c8bf9b1330fd0eac0cdcb096a69c0766b745a810a546f79e91cb3ec8fcdebc8985e2f7bd05cfd34ffa1b62dd312df30fdaebddefe3d495d032dfc4496398498b61ae80bc23e89e1fcb58aee1052ad20d5aa1cbd0993abb70fb21fbb735605ade496bc58b80d1963aa41803105e44a49e782b48602fb8a26697874799b3c4a71bcd2988d8187643241c341a6166f07156f5b41303f713d488e69abe29b2d413bf940df1e7f56c14a7959dacb56df8c9b85dfd5e4b14cee8cf8a565d3f9fa11d3bb9457a3a782f12227d3ecec7b87b706c9135bad7415c47a48bfc682326992b439349301585ac1023033cbaf14586a7dffa9d1d17bdb975cd7517aa8c37410cde23c208f28783a0972997a22f00fea8051609f5ad58e1d8b47ad8ddfe1c64ecd3a6a119e75e2a1004b24f15a613991bf90411eb3461a34ee642dd4a4b9ed255e4ba05c64ae9c8368c2ab8c2c39bc6c44bca8aade4cce4235c2391de27b35db5d113c27251a382e4336fe64804d8ad5b13f161233d29a034d2257cea0ea8664e0d4b7662530a2d4a582a7de90239e8486faf5dd8453ef9911ba36eaad8631a5131319769c4b0473e57bb92252b6490c1cac908bb23adc8a9e830e91abf3a24576d7ebe5d8213d8ff942ac1c5c074a675942782b2ddaa1cd08605b3952af210c2069706d119893499d79bcdf9018dc59d72754d0e398e86f3015909e70d13423543ef2782ac923817c9059df621323f33ea39e7e5b3c4c1f614dceb990ca6ba30722ad6a7e183dff41f72ada08cb49426c0531f8c37a211b13d1b5ce424212267ab333a4fd7c9819b40bcda9d15516624df0a46ad4ce98e0e5eb5c8dc636fd0d2d58ca86c3ed73cae0d307d72b8d703922c848148d41040c6e4e7a1f48192f34f5ae45f79215c30df061682a07c3fe089f2b3b6754cf4635c5d0cc6c70501098278e8de5d29c8f0c6381827bdca12bbf22d84217eff3f7404ba81ae8afdcb818c64d9bcdcadd521281e284adbf2cfd9904696189b8e11a370e9c2fcdec7b08287d4a26ebb8f0b349121f2b9165ab0602a2a0ddd4bea3ba918c5381042bbba75d0cab849d66b1851677f2d80ec9ddb9d3ea4980451ca5baf77e003fb8a0177d2a4505e439f5cafd53ff5dd2073e1150bbf84366d79bde689588fee9ac52ce67a67446aadf09bc379cb106d1ad60b902956fbcdf3537e9309dab6be41b135e86034955f0a108bc753fdb584fbe2dc2fda70029cada1a8dea300b986ab35f26a03ca6853829c84cc864ca023c3d26752c1a2db0ad89a52ad9e3932a3f0a7bcfb5faf5c7b9a7772b708dbc86b2f6a66e626975a836d85f676f12e4c2227ed68311f29d812191224f0517526739bbd30cbef7de5570093dded20bfa76daef648e194abccde1b8f0eeb6ca48371f62f25929023190d0981f7dfa946767405ac0cd840bef0dff841cbfec4fbee1913b65405b751e6c103957595c9b2024437de8685632aefd073825c6127d4bfabebb3fd24dd3337302a9e1d7e48a7968fefa6b015bea15f37216868eccb46c37faa5b790bcad875b6c98d3dbd9fea891a711a50f77f2399c0d73ec528490bc1894b0eef3582d5f9f12c76f80e10ace64b4fb189b09d1f3f8b397d0ba0dcdad2f84c257ebe2313d3b7e9b9d2e98cb80ab06d8a60738f2ac258a52747b5fd0050167f5efb745223e512fa089e6d2736205c34939f8592a478e94feeddae146de36c96159713b85f8fe6eb75611f3a376884071362c27414d16bce21c536c7b2d54e2cb5d5c94401091f497d088d88202bf9409590b3f67545145e7816932e7e7afe262efa42c874d4c136b94fd46ec2d565ca8acea621ed3843e4d59bc831c4d1d554254fcd417ea6d0224f7ae40fc02d4fa587f37bfc605eccd990f8d1f80e95a4f83a15f4d466f92c87b1372befcf7ff9bc278038f2aa00aef575bd28045660ed57fa98d93bada5a14562397179cbbb048769f7bf888d8a8b7fc6266551d8c396b35b82a09c8514edb8854076b8b7c82f29a46b59812da87560ea2b934f9cb062b0390be579df51869a11aed68f06cf40fefb0d1e9db875a9edf413178213c8e717a8967780e5fad8be9c27926ad3438e85bf8d98780fbdb35937a0b571d0b755f010c483c97a16fa67730cff49e55b38bd615560da418c0c73fde0cd06657b1b78e2b86ac45efe18ac892bfe01d0b2829039f75b80f66c172ed913235c221dd7b2ce73546a9adbad89667b466603168f529acb08d2b6b12bef714af6437797072a58277e402ea2c9eb7a64c4ae5912053ecde75f5497957eb98f90a0fde91251e4dcc12f089d4366610ceb8178baa6ed38ee129f83955949ff37ea7dae43456ae2d875a9b26a37ee2f69a4c97c3fd42873c5ab026d9dac78af3337a77ea0b9a094913b0ebb9c840379795e061cd88b5ad0229745c40b9cd824967801d4b75f53b1c6574b57fddee2d9209c9646391f643c939b48fc4c2ed03753fae15da873183239cc943d60dd7e61e1446f4b66b653b7cff29f481b0398b82c434e919aab04f2fd21f67c097f71fc3651b5cf772545614f3860ef8251b76da66e435a68c1a913b511d39a9c2f141d4c9f44dade3c4f0cfd2c4bba2443248d45bf242836440dace00c92c106c5597cede299364e5248cda8a02c2179dfe03365ece914bb80090b7e3367a80c1b9452014cf7dc5a1171cc0af17df33f618557aeb01ef14a7d529cf190da28fddd1270d7f1bc4773cfe665b1f1d9020c4a35e32c55df1d934b98c8879ac9d51943632a67b0b84c77791f813833f906deabee4894c1793655dda5fdb72ff2778aa0828ffad5c6b2d03eab0210fc18f385bd319d0b8a4df5d6af1907a2030e497c9e104110740d0423410c5c126983222aee559ca093b5063894843abf799b46d943b2018431edf113a6e6b9d47c30aab91e2760ef7be455040c9596e9637a9d061dc1bef7ac06a4bf5c0c05155a5bc5532863557ba890bdd350f3b81406a042de67f8b320cfd50d4a1f6e1c478ec2ddfe7e48f3e1f222d4ca2313b09b39b4cc57256056d8e760bee7cab961b9f4ca7fcb20148cd7dc0e39a87e6169ccfab6b10537702ecd7e60c503edaf671d5796319db236c3b9432b92b621053912bf51fae699800d6045c8a5c0569c5664c6959a02cd172dc55e5e0a7c29fcdb450d10658528b28e9089972695584dee4c89f1602616bc765c57aae40662de3f6a216e8218678c9c6f90718cbfb40625b2f377c262e862a71af1afb431405ffee10f17d790b14c686fde64bca4455eb755a360353a81c1c3f3700b7c40bcea213a0a2aadad071fe16a37ad5e8aa28295b4aeaac4891f26fb2d1f2266da1f73181df6ad8cd76c2d89512e12cd2a498dfb0539f8146072f58376138006177472f901a3a5e885b5da22c7798e0f82b50fc3ecf53486e84f3a499d3444211220ddf027e64418f8b1d603227ab351e564a486f436cfc6e5f4ca98c73670dacea3042df1a217343a6d97d8693a6b115b9a7810af3b4432b8b167804a005135895d1bbace946ba8bbc199f6b0dc2c333ecd2e054db3b4bccb5f0a380a776c05ca1dfd8bcf55984a9785581c2bfd0ea79666325038ad436b5a627dac2281defd6a352ea7bf1848033b59653e9986eeb4b173e72edaf015fe94d1406bed8af9b3185a865c34005460c67463b42ba4143181da0d584219a0fb6d0c2b44d3a469deb7e3e45793663c837ab29514723eed0c88d734ed1f3960a04536a3b023b5021db3f3b05acc76b3194242a157414c37920258153cc9aa3cb1a9209224ae19f7d222015d37313c76662a8ded9bbfcdd564ded0c693d2b7f637ec601481ab1bd8a30158782528c3ee190db110d5244089ea11d5b34e3fb196a644ec97acf61598ff7e57dbae3ad27c79be08f85e0b4add6e2d845863c3a4e5e361ce03f3f60b32d08d97d19efb6f12aa4b6608aa2bde3353591ea0e855429ce829d50d9fe7dd02c5d4544472976b9db3b89073241d899236e1c7f021f2a31f5b1bb927d6097b502c62ddbe9f438f32302f572774ecdcf25a5e62bb7f72762781c8e28f32d056642de8fe2168be17e77b96c26bb44da5088487794177ec8fd1e3fb03ae797705db034ddaa6c421e090be3f82f805f5a721520e54922cfc4584c2da675761f0b26c800dce8528ef89ab60d8e6c962edc2e8ad3b455051ac350795f55b49e896243569540e38620f58ae9318c82513425a4e5a0b00c880c8df865bdcdabec578c65d31a7766013a319adcd5e08a7c393744c04124ec55506e093e8a10bc81a1022c500660c2b6e11372a852aaa75eec54f694ca101136df74a591fd3076d63eb7068186711e3fa62100f8cd583044dff7717b4382a46fa60ec84ebd6de0e5ab58759a5dae8e6dd82be47991b72e33195b76a49ce5549be04329ce0a5edb4f4a441f481a1fb6630befcddb44646bc7ceeb49e0c1051c04fbf9d1afa108e789b22ab61284549c6d3b43fc8abdb64d5ebdfd64b1e2239513ffbe136deea5e41cb046ce5bcceb09d949d60cb224562673e000e60f28f86ff0f64a4eddcce9b1a63e58a0d2dcb24f89df13202f5baadf62964d6fa3a6bd092b9b516efc9c2361d403355beb7a7e7606420f968f991fb279cf536cdab4310324fe01685f5cddf0882e10a6b5d463668a61ab986bf8d5cd9876a11f6f290dea7f2980ce94485ab250dabe997c02a473e399000cfabe4ae5eff783f6d281e4f57e909983c250cd3d1722b0d6c915d326b4ad57744ab6fa07adb81b578de5fb8d3cbaefaa4525a8e84a9900f2ee7a4ac2f2fea36137964de3ef2a524a30aea830f4680ac29618ec860567873e82c30bb54cda869e676758b27a360bfc99bba472b505dacae2a0c2b882413f8d737fc882b57f46e09950adec77578cbfc8c4a9facc280644d60b5d49be8ffe70479d610f50422f813aff62050c56b53f3dd860dc6ee6d37aca382b673f09aacb9a195ff80ccf6fabf93b6ea79212a0dc37c4c7db5cf95978d429977b152adf80ca9956d07cf80fd6fd3a69762ae4fd39d382e540e837d5d019ce8299178db971b7dbadf2b5a39fa342607eee133fd1ed9dd0414efe3c47acadee50ba6205f69ef82524c4cbe793cd73bfc6628b010a9344d626f85076b62e41afa88b7811101f6bc02d89171c79145ed83bc87328ae8886ef772a31fa4270d487c97f6290dce582056b4e3da0b60e666a9f9251ae843591c79cf7f4d519e76baf6b7171a90326ded473e6465785bb40eaec41834773adfc90ce8ae00d76ab1da3f16433b84799c26d1b9090e65ec19bc7dfae3d403b1a8f3592b02196102399580887b4117cf8a324fcc894e4fa4adbb83737a66e5157d0e130254fc2c0d673bc48b34fe46af4cfcd35f8ea6f8981a7afc32f3c592dd2a37cb879a89a1b5a54d112b29fa26e5ebc95d10bf443a691e3c1d91040fb3e5477f8a8559acd64c14d970bb17bf02b04184d426a068df17a5e31605307bdd85f1542510732b20ff8dcf39427563d9bd147c5b2e0acb2e8535a6ffeb11680671d62126e5baf9784993ad0f50d23f98aa56755585898e16d2623230f584acee1b1ce551b3e32e007a2124c4afca4970c48f646725aa770f7976c07d0a817a2d7d778fc4f5261374483b5cb6eb46ce340b68cbed4ad9ad300a86d5ae59da4132340a3514911f7deb56f4fcbfc88c940eae3893ddac04d0990c616f49d262b7bee75c2269121d9ff9bed49af63703d6238d4263f59163e2fdac8cf598e628f80b320517ed7ef93036e27b3526daa65530063bb707cb7eb5a018f28151b090f053804486272ac002764ca34e2a907dd091755b4823136a525b5c78e90f95b171cf8890524394912bcf068447ad868f0d4444d1474e4e3bcae03968dbd503c16797d0542aafcbdfc5201e26465560c2bdcebd33d0eff34536670327786342855c6c64e7d224eb00091a5fe8f9631c4f575754657f51eb2a4ef2ee780a0c2ffb13400af9484d3b86642649d00740a26fd0c0ad8d9c26f56f9b83bafa44f4fb7e0f6d93bd4b7d034f95636ca01566e60fd584c4fd6676afb592e8cc86f6c016c4b6d2c31ebb4d99a524dd063a7791de64f796854799a825ebeb537c10c91b49023c77594625d2d36d3e06365977e2863f29f8f1e6254879550f54a9b90a64650716bebadc514d06e3384380c9003b5290e42989d4f28ccb4bd733a0e5dd88e08edaf1aabb02aa09ad3ac24835b8043bc7c580bb9d061971e4fb334402ac542b17b54a641c47696988f3ebcf9c33980bb39c73d005b3b78619454ff35e1ca763443958441a1153820de26035086c0c012e787e797f40351738703850a22f1cc198a37ecb3a07e2b310ae52b57db03ee03fb27602d6530dcc1955b696b2ff0c27646af7e21fdcc14589c2f3caadfc98b273c9574b1931991549cff4dc5b657a51419e2b19135b07caba82a574c092313188015574fe605c3e05a275603f3ad7a0d50955e3e29df7ad8d91a28372297fa11a1f45249fd7e173d467f7fed9aa699621c1509875e2096c5ba815507c32d33a99ed598f5a14830fb4d4ab9fc1392cf97043e6ee82adc5a8eaa91af973b7dd19381a2fa81957b4f921144d9ddd6bd61cc4f18f432e932fff65d4088f85827cb1c5e2d34ba0267f52593aba9deb026229772971d928ca843aa407f027137dff4b13eba0ebedd147e23ba8a02c03eb65a3dada1bb84205450a7a9aa25d92ef5854d54e783fc2fc445dbd04c1fcb8bc73c85579c12bb271be3861fe027c39fe5b1cf080a4d79cc7c5eac2e896aca823fd8eb09e6b0704c1a086b54d9f463540e0191e000b74442d695a1799a4e3b955ebabca6b3034eca7b6c2b3bec138022a5b25c8011c6f5646c81c3fa844b9891597dc6539987ee13ba184238f0f2857348218eb79455fbdd88e500d5ddd53a9ba62c881504abe4cae3303daf32d9113bdd196a02077b24c919c2015beaab82960ec3b37578aa4de809a62bf3e8f7c63ba898bf0c9f3cc26914bfa0479830785cd17578ddf70306b4422bdc151047fa90386e8089386adb24b6b8d15572c7fbf01026c5af2de7eb0c2b5adb36c25b9c74dcdbb061b131e38e0bef894f90c3a36634e02851212fbbbf8ec840a06893e00d5fecd986cb7f6fd24c5c473fe7f5f66fe582c52e2b7f23935f19bd81dd0961016839db5f21991a58d70f893517121404f8a5622d4c0a074c7bf6f150d3b373626a8c88f27d5308e6d48b7594147a1f16f88f3c8f173a5c8e4390190d6cbeadac1e8cacf980becd5e9ba39d3568cd8381cecfab96dfa91b10b7b58bc89da82659afc7990b1e3e97f1e035e92449c97c5f6e7acbde920b652f89f785207e0b56b92c8db69d80064817bf5ba94a32227b959755ed526e7e05e98581bf23b9e3a8368b03508ca034e49005c802cd4490aeffa99db98a33bb15fbe8f836ef5752b14eb7078a1c43225c3d2fff9882b6895c4a3bab235804e6cde26d6bc41bcc6bf0cd72d33aabf709645b3829428d9a39512b1e76570af0822f8d8f878203b8346174a61638943c54c4a2ead1b40e4320211ab1d5bf0a0868346d582dac8986ca3b9011243c7f85a851a72cefc6d019c5a82893f5b84222c512f5763c73473ff3577a9274791eb64f101b1953e0c22ee1e30b918d815690c3836e779d011d1bce269ba653c4985b5b3e2ef479d2418b081064647d19b84aae48b889e39c943316fbfcf47ae349b2417dcc27eefc2be900cdf9719d693be3695664a53771fef8654bdcea05cc41004c378fa12a2cddf50ac98f43265e3e29328cd8a2222dedd8cb2d0a96563ba029b66468c7b8a56c1f02579af90519a0d2ba936922a254094ca8edd7ca4a7ea9f0807a074ad8dd9aa03098d469fb5df074d6da54fb92968572c024cb923006c87a22fd81fa59c499a3d0e773b4e68bebcc2455756380fe895e78b014f0545d3af69778e40ae15ddd4532fd3460808262ac9d30ea1994c422785cca3e629e9867ecc1855d80d799056ec73326366399e8656c693811c8ce653f1c07756c8ef1097d3b9496f1196e0a00eb7924743b2b8152a4953d11e42ad9abfd39499f4be64a833dd70daddab9d88e0ea1d46c50d83da768497574e74563eca8bf5777be0ac635f584cd65b9e9cdd064778df888e18d70f92a1574a904ba7209a61ec8e9ea2db350b2b764bbb78114e8c4c40d7828399a097ceda9615b2177c239981cc333a1eb5e75b14eee591eb486f87e41559658fbc9f3a83840a72f79dc19c5f30544c80212c36cd7c633c6064d527e3144907f87083da6c76fef47c49d85837bf7872fb5a3fb68ffce50649e11ff3632f2130de24fcd2029101c40a517b8de2ad59ac9dc7b5e85b514e8b47b281ee310278a5a4085633c5b16905ddea50fdbc8009913260e8065655320de44257f6bb86c6424acbbac0fe8839895d4fafaac62234d33125b83082864c40749d805fe81e70d4e9f6b67c1574e82d0ae40a8a829adfbe0100cf122ab347a56239a0052a5d1c1734141c1667fe9ec8817d2f6d7c62db5e3b1726eb44de3624e9d25907093d8080c56b1dc81858b6a36d8fe49cbba0291713b03cf99ee70030e00f3468eb2a1231d51f18abea950ded7b3a59c28714476b38e7f8507281315cced5d2130ed5ce20b5bd82004b3836f98b6e3172d971ffc5ea0b3276e6ca3bf135c9dcf49da0fa7336b68ba399708c3bb89836b104fb69aab8a9e3c431f21453c013b37a21d25ed2f3aade04b71e1133b36184bafe1d4c119642d280d81677c5867ac4d418fe2358ba8296ee4a8f0f2cfc9e9226823c2a5b36b1558fa19723d5f3723dd3cb2a57ac4cf185e93a7d91bfc4d4833c9b0cef64222965c9577134b3f5399c2e4bf5a6d3b74143bb20a04530371fa8612cc16e2c26a279553eb061504b860e838d85807a4fe685fb0b8ebf112014bee891d7786eed29ae0d6ce127cda968be7faab74a86761d727fbbbfe483630b54e1771a59fcfe7076762c00f27475f9d9bccd1456e65f64c9f7952f3b8cf5a61025961ab9c2f28f9322e95daf5de131c27f026755bee9f136230595c0e9302a85d1115ac6a8e785b4db2cc06b5e55282b81c720ccd3a5c6a4aa2a7c3661de3428b94782e36cf5e2dfee4b58777f38496da8ffa05483c22cc126d4c74af752cb35d22981a3147767685e515267b275320877ab5d69c66c8240274233c04771b59d4a9faf952ffd8b48a894e59087d8609285a738dd3faa752c2ac53a195dccddc5e6d4c9089507e2a2bcce59310c2b7ca15aad2e35b2f302936a0207ccf1da58143e4705601ee50c6919e430d6b039d4d79805614ee1db5ac9f2e5e0efc603a3e9b17fe90b60f11a781038b1eba91949c04bd49e4ac62d60a8fd374d287c649abe7780482399d05147a29113b7fe23bd5d3f9723fcd6adf25d5771480b111e7cdc95e4f538d7d6b66a95fd994256aa795bdd59e7f3d59990a36392c19baa6b0f77280f13c45eb0d73dcf1da213e1883256b7c71a00e8480605c0241f5e8065ce15200203d55bd87f360ad5b640e84aa34120614a0a5e456fa942aadab667848337d5cc9a117f10897d8cff7164300889796776de4267586dc7f647be27308af6aaa74147b31955c9ed5d6d528db089511ee643ee422b8c68ba8daa06c463f175a3639439fea3f1eb5a9422d6da4430b93bc9ccfd78c1868216c745cc6a865a148185d86a13040cf8f4029e99fe8511e69d52d974ced5d8737f24b5410529c3de2ef4221231098eb8fd4f42dc92ab85b027a300803647f805bd1838a707658a76f8955a7300dcd55539970dac5466345b9b697397dfe73dd8a9c33b6ddc8d7c5a0e74c6964fd7bfca9ecf1da92efeae0fc0c31f741c1ee1771a9ffd78f0c66d009e1e0e7260029fad0b242cb3417ddb607f20b204b8b8396f25249e18c6839e4e9a76d36fecbc7f813ade0ac4b68074a615c0c2d47d98bf12b1824e6187dd0a27bef1b0ba43e257624aed64c7ebb66f6539246cde441526fbcea52afa6485049cdb7901db46aabb57e6862af9bc93c51a4e104384a9e8f41c5b49dd40afbdda07daf023f8fd1dcc294bbbb709b8a42dfb07e46a6696e7f6cfcbff343aa54e471e2ebbac8a2144f74fc6b1e3cc56a71f2f34c48193d6c9fdb8d6b1f21c27ca74adfb502a81c0916ac79861090cf068b4a77cc07dafd7b65d18973d3b414b658d644a66ceeb914b1502de40dbc9e8d4fec7f214c11a8bc0c940fb79fb2b3bec90e0bd644d0b0510df7e7c26e759e8e867d657bdddc42a5b3b713154b64b94e0037a05f18e46afc587760a6c5bdd26b50d74253749ca59afbaf37c90bb74a37bf2ea46b37a9b0418f7bdead514d04fef50eb1159b99dc9e881869e071350f4fc21ab915a0da775ae5f61d4c7e2c5b3bd94cd5d39678b511b3c546af275cbf2691c692c7d48fc04ea632842c16549e632d3f907199d84f9caaad40bca46b809f3482870ffbea262fdac543f19d0fb176238dd863018a7a4ba2bb6b025eda03a8561e7b3f54ef0520ab683e32ed13c4e085d00845bc5580747a032c29db7e65d5e65dee425cbfa1a170810b5d1b0bd6b21ec90bb9f60c6b2e1653dc07df8b1205419eb7d174dc09d8b0a76111db1d50996385d3605e85885d6464ce1a0136eab729452b5adebae5addf0e1aa1a8bd61388ebf57a69f3a1d59fd4a2305cef3d814beb1dfc705656c003a0fba8b0ccb9e49e5bc2ce10dd2c0289b9509bc925e431229628ac0f26e2a95f59aaa7e188a2ab64b8a4114a8212c8caa7032220c07f5ea13a4e4870f92f2f35083443bacc68ee1c94b5f9f0ce784b5b2b6673be54a913ef889be364e1e2313464cab6738f7a797714894c3f48a1374212714a6ba2cef2251871ddae1abc1196146178b10a6eeb7fcd5a0186cd9475960133ec2e0e235c485ccf1672f6b4d0c9869979a50bd02574c77e36da2eb2c85cce22f3a4cd6eb00d1191c93b3c1c0af0be5965158e153c873ff8086b5a05a2d06a3a59502a2b98b7f7f12025e72fe85cc511f0396fef2053f9875a4752e429c7a11097edb0c56e0b0cf8eeca88ca796bb13311fc6cdf3a198697e5aabe7192e1f666cc86c08cfdaec702a506d8c46e03ee9f3513c9a8002f3786cb5b4fa83234bf2671c32b65745007c99c02a8827da804deaabc33ff29c39a25aaf6e7ce24510e7c0522cf222ecf793d018e19b4aa639e930b9df07879c1b9891306daf1eacd6b0eb9164df44d6e2e033de5e70173815c310626679f2b20bba7f03d0f1830856d970acc922a04f4147456881ca0116703379429663df745db1ec3c4bf370390bb00a9d8df5317f1f0dbf458629d0ae7ebd3c1632f39fde2ac412eda147d296acbf6f681de73b78c47ccd037090f51511208eb6d026e499af8196f6467ab17c0ede3c3f50b8e01f1d626d49e354c5e4d8ff9ee839061b92079a77b9d6f0372be558dc652c0f9e6a788591f64aa93abb59cbc014459c423250e06835f90e624b73f5f5d1a1e84c37852edb3c26c1f4abd664981f2d5852c9e377039bf01de7918687c8d901eb3a1ce1984db8f968303ee066b6a912c51c046fa7832df2b660ab588bba9737dd33d6c15a9bb5da75246c86520a8a0490148e078ce25f25b674cc05fdd1514ba073408252f414eb4d3c791845aec4424770b5a4513cac9d416a67be8e075b3dc5925ce50d663e6108c4eba29144adc2b08eba0becd5a3d2f5e36683e7aac4f453150863669c5072472bd230db33d06bcbe0e6a8eb602257c4f10cfe03e3f712c238125c587ab72bcab0cba1f21a2ece27431d3eb12c760eeddca04f41bc741179c3f211619fccd0ae50379275b4cbfc4ba6570262bdd8ba3face03a6e65d2fc6bd9c1fc4856a279de8a0c5e2ea58304b7af1b95a5ba6c2098a0debe67315acf99def2bd16adf079cc9532fe3f50be04123445ad2cf72e855eb36ee3ee3af5c958ea6d4436f397bf106727bb8dacaa0a0a044cb31f456a3eaa7e2c96800a3c59b6c3255ee8326545d956c9119c2a0a697168e06acf621abc8f4b81b49d40f25a1df010bbb1c423a37e0425cad5b2012ca5d4e3a0a33809535c4a730221ab583f478be52edc5f740adda94558c02a05cb0b56c88690e729bc549b94abcd6c2c8625ad95f31bcb22ecbcc88543bec00ab75aa7dcb1060bbb0577317bd8cdfd0a967d2654543e5eaa9f33aae3e901b2e4f1a97f7d374918f86ed083bd42c5a4b996d2739b7b038bdb46bcacaaf79bec7578ab39d64ad25f73f65773e859e2256f225d23fd74ee4ba350e12455b79330890dca9b7f42e8415bcf418991255b2df27434257b5f79831b7f2c19d930405c1f06b5e75a503e0e68398b1b0023e21f1923b34a75a1abc787cab4f2f71cef7d63915d4f5603d508159e12379ec711291f68b16399504dd2b1467a97397febe16770c0f2be84e1070bd7fe1e8e1e5ed91ad8d01ff3d9fda9380a4549d1ff03ee8e7199adaa900de48ba7e9716dbc5037dfe3dfcdf8287e58d41ba0b0974b342d201c991870db7aa1025987f13d1684998c2349877e37d0870fe0afc7187a0cfd23a91313c4e80ca78d029708da936e8b639735f06ff3dfa3dd4c0b88b836295347dbfe18db44f88f48083edcb1bf8d9b6e631c8c024199ec4a742276a570b12d7f6d9817a641605708c51d556f44f2410425b799f17c9d0e5598c89c75c86465e1a7c2fa137e45e7eead8a23058afcbe34ae1faa1797524ea3d5ba8cff0d807ddd6186280c5d452a60ba9286dafaac4d5462a6ce0accb9c7f2d9a2dff6f8e655d24213e46593ae40cbf59ba930203b0494d8590ceb027611ec73cb4af2da26f59d3158d51b921011e0513376b951babe06657128980117b3236b8a07e1b6339518c4bcf802621f2935e2541ea1510604ed5568a79db93029ed547322b7f491d814cc97b3dd98e957eb595f210c2a6291a01afd7f7322ca4547445fdbf605986ebdf5c1737dac4e0a070ba15efcddb901c24df73ee1cabaec7fdabbea8dbb7a21962ba70d042eb7eaec4d3d6a4b2c4ee8f17a12b7ae6057ae1d8b7f5bc74eec9501c25193300e1281f59df0099b98d44595c0d89e54c7ca284127fd416fabe89988564e4b669135d83f9ffc5c8b83049e2f0e8bbc813640e5953558df94f3dc627e1da26a1230f6caa37681bf48c951748b61e686c7fe2737a89680c6356ea4ab9fe392bcebd71d6439764467b3cb33758bdb38ed7e5dcd9d5ec9826a81febe0f5160696ee01bd105f2d693e5b3607a3e60b370fc8213065ff51080ab42c1bde746d3be6cf51ff385a9cc6b6a5ebc25eb4f82265ad6debfa04af85cc0433e6bd81dc9b841c618844604312de794c2c35a40b00d44f295f7fefc42d6245c853c9700dfccabaa12921e732e3bdc3316ae6d2288b5229ab0994ae45208d8106fc81be67429b16e51b58c2526a201e87b0c3b592b449df3c577d71f8a04539b6db1bfadd365e8e1ea1f857b310a7269f8ffca013ce0040e0a94402d721d2dcb7144842230d8ae5ba6096a0ef0e385442a06cc3a4fc189cb29ba7c73f01bcbe4578f2220fa8e9d8eaf2b3b30e44bd345fec1f4465cd4174d36df8286691f7de610c68036a1da277f4f4b6f0dbb9af096e28143741fab33a66dd42986d0325d7db58f8f3f1aec37532b5fe98f4f6712b05132edbd7a267ed2b61497fc5326fba562c135c5e3a7cf6e69b5c79d986fdeb5b5cc4dbd2f2feb682ffaf7b3db72c769a910c38a84479df4e6f44c7d965313fdfec9d38de5ab865afc6bc2f54d61391c793408189158dfa73e1326506638c269a3432c8a6a5e419617a7703406c8ecfd28090fa106b6fcd4aa0b694e2fbd71660f3c158f1ed1a71dc6308ce5e20e554460b5b6aa556be16f32fe045605dad9f9284a2bed96c0b38a5d4e3dbfe65669253d5ab09fe7c3f11499a8fa8946ecd4a614920d903daf0240cc858b286627860772bca0fe65cebf983a77b7e30b505f9f441a956f3a6dcdcff57d8185c2a02b625ac154d9a0c6bff61ae24d1e922c4356b5ee11a261034ab120f1c0d57b0e0ff17637de91264cbe05dd5bfba08aa52305499d3d066024c992e30ead93c16a2518dfa4021fb0642e0840e5b278d7b83130246640a831e05218332971a8f3a54f534eb50d421b7d53b055a6bd8278b46dfe7e5815cdeb6246eed85dd5fd7f696b988dd8e956345175dd20d87a952fa740653813bf5d6b61fe75d4f22cf02fe8b6f174f40e3fc6f5bffb2c390a69b8d8d4c2b57eceb9ff0cd485ef96ba1806728d714ecfaea3ae799b20a9005f3097fc71a5a35e225b8ac8732a79ccf701472f36e3f4e4eea47516d88cc64c1392908e19247c44fb364024b8d423a8f38c2fe59c47dfa238253a0e6d79301d9d776320b921936acfba6ca962596d9dd78b0988a802556666f5433b3e2f6eac21af4ca7913798aca6e28fc208aa14527d3073a77d12624deb7b20815ab75e1813c3519c1c5c37520a20b6aca1725d068671cb5ad166759d49905853a92e6c2490f2178feb5e8e7a215abdd4c4bffc239804416b105be934b40e1541934858416ea4cdddfa126f4038608496fe05cc14880c9686f4cd2e5c720282f44893c8b1ce3d65a78dc57b40bdaaaa452873b9cfdf65f872bc031a2c0c70191469a362e29b856639a90f17f131393ffeaed7f7d5e60bc82c8f7b1aee3fb798511eabb05e370cace1a987e43fda50243f5f5e7af26244a28e05f3d530fe25feb942c82d3d846b453a21f02f8003ea688a09eb4cda85f5db4a84b92a203adb2b91fcc1e838beeda0a61bd634bb4d7f3e07d81e70ae5ec0bc7e19f46d14da93fc3aac6270c213b42de1dfcc2214826946a151df09f67e61465d77b315040663a254059eaa2b2f7442217898c44e916ff6930ae8632d576d72680681391edc4aa1070d28a6984ffdb07fae5fe720a84e655682fccca14b3b95fe45e3534d2dfc9a8c33af0f85e9efbeea970d7ea6cd67615b3cbd1ae6fa268b3e944a1b257542308ecb256693482c425a72058f851080a90de600d140c17db9529c5f1ebf25624bcc4fdf21632702463ad80557089eb12bb31f885231372294c6b89be502b9fb67970b0e1342eec0f2e79a978e3ece3da45e3710375d4574a7d37b7ea180012f55c028287d3f196292b8f15cc730148c01b945c159079253081b0469f3f173969d10373e8fa3e1551be375b6c7130257dd0f0c64454faae33038fc283da2728131ddf19092ed4cca205bf64b58efde89a5f2588c80813b57c5eb62935436ab33317d14326d6d1a8ea568fdc2b6ed08ac46ac01a7c6947b5bf12d025d82b0d09a51d51b3706b8029b2db32204e8c14c5419f7277aa22177da15b9548ef6a1a1021c546c0a7ac5a55624793ba578bd21dc25a704f71dfbb7e00f0bd3a4d66f2052810cc522e9e6e2fd1fba9b82b60aee239697fb49c3c5d70552d7f5d19bea8bfabc0fc2b0ddfc9af698f200d9eb9c36c24743ffa456f09b23e9e5ed53aad1d7fd0580d173ec03e6ffc819e846c1b48e11ab6f5affda2359252d08c1d6e85e3b1ad4d54b987a3972609006169090f3a2ecd76e36025e1a63e586f9e7e3ac7056dee318a69c14d43dc3e8d10b3979b0a1ea2d5d295e1158f018553ae51bfdc0dce2773b4b5b40dccc626ecfa4315d5ceff2a04fc9bc5cd17155260dbbfb6aa4d8464d7290cc72a8b6dcbedff33af42ced74343d79ea8b0ccceb708278046f390b0e4de0174542c8b348f49e974dcc7fd668a3b60584681de095c4bb2459a0b4137e64a3c14254d453902b02a538db34632bfe1b01ce2aa901669e666a5a729a3418750525523195c1a880397813006ad911821c16b8a8036775d650b3b4171eeab7e2bb31cab766b05f649c881a2ed1e7c75c14f31b14128c4612a88809e212672cebd6e33722be749fc0802a2327d7cc5754ca6c4ae7da09a891c1f0fe58f9b454bd4e0a51af5e2ecef75f772b497423c4c649c12251070f9ca16cfa0738aa38968f569de57e21d23ca41e19f2575b819719b570ba228fd43bdef991de75303c8ba5293209598e9ff6b0f526ef6fa43060f12746dc9a07c8b2a5280506d2d499218a00488ce0e065ad8863dfed034bec077bfafc3161ae11163f774c9ec139cb2d5645518a76590f1bce60bbdac2d1cc4fda17e58ddb79968b07490841ff343cedf3ac1a30457350b99fdbc68e947c76eb6f11f1a6e0a5b5ae848e1bffe2dd4089d334657116ca14ffe2eb60d124ad10c009a9bb07005b3bf789ff688a22326909cefa80538a43198460683a9e4471b1a53bbd8866e349b387703335543677d9a4a54b02491062ca06643ad4a2f7f24b43dff7cf10b6143ad67cb1b94057efa53d0176317d56de1f6e281d8f69de842ac03dabc5548a7c980f8c8a21f6b3078814fee14aefba6045c5be1901993d17487fc98217755fbb946c6c411329b65a23f1beaa30c486814c43435bb9c359369b3f51b18f1a93f5dda68888037f42830fad7cd6950c6a76caa3eb0aa1553ec0154a844d5500273429f0f5099cf157084e086401591b370bbb965798bbedcf959deb54a07e91ed9a85943f7859369dedd6d94f3d6544386eebbd81189de3d5d7e804ecb2743fc9e2b1f55d390ecbf8923ef407b8891b187cad86a4859bbad945a5bd79d6a2c3dc5ea744dfea26af63501160e57cdb707b757c2b6f7aacad8fa76e36279837d4b118865a31125bf98e4772b2bbb74a397d1cd0ff53edfd81498facdab178efc1abe1d446f8d81074f6ed8e0e4011c36de9f15ea607049fe08e1a3d5e9edc7474ad2407c1d239ff4b0bc3678446e9122070ce4adeac8f87b5959189db3099b04c8eae02728c87591119273e30f7a02d5666093552ff32f35887b66daf533ab82c2988d4c1c6a5beea8a25a66d08982e842b471793713aa60b48e69c0a2a0e8e3d844e287e928afad1e0182897b98cd191355c60525231682082aec10d9edbf30243d076d89a3a9b10b9852f603b6d3bf65bddfc6dc7a9759f11715cca376b9d9d5e6fcc3b32da6b6e8e06ee65eca0102b82ff39f059c6891caab193a428729acd9d4bfc23b7c215c7358ca0f72994cad2a3636f244bd555399c8fc738cd689127fc465ae80fb0c14a23cffe05e490e4b2a671236d64b7f8bff6dc556664665174097238b61ba621725dfd58a6fa1c5b75df1d7ae3a10174c558603399f5c3ae3c0f5bf15a307d5c0cda1fee964db22b8f89768254c97efc8142d324820a856b1a00ab6aa4cea9bf90a75c5fd2f4e896c198a65b3b502722d5f5f333c066deeaf55da4814831026585a4bc27922c3f4b2b3c3ebad9fee018d5c454a7f491b09093edc15283e58d3f1f864030d00374f745f38ddfe4c28dca9943c9f5b9c3c368f8cfefd411a186a12862e4cc28f79b594c44b30f5b3dc42bfda773ee74147176acf1a2b1589a03243cda07a9508a02c9fa7d1b3b9b79c96b5c9f2d3f5ca7f88896a7f1f652eb43708d58a65b5b9db09456f17639dda832f0996f46c15450a8c404dad293f53a5b51b5201d72fd818bb8cd000d4da8c724eedbe4c84fa14f136676a679252d945d2c7e3f3349f7a94f9beea5afb334d341079e415b691620f70f04e0dd33a974800f6a81b68fec66b060fb7b80f2a6be16d0c484d78c2c88a129cb2caa3d7324d82d42a70328bf8262d72271a79dbdca2c1827292b4bf4c19cac9dd90b150d1179c4299c13a65226bf94e2a25cb5e0a44f2739f49c932539721a425806efac7923b20e85931eb52ebe109ff57261b7e026e0ed79eb54fba47504a94089da386ee0975c206cd5335497ded0352ee7b17aabefd6c059277c127397212d0a2619d6a3ac15409bf529a1bd7708ac51218b7ba7a4385c83bef7c2e2c74c0d823e220c930507d243a2b9a5c2bb944ce17ea5a0bac841d96af901769472de5ceb2dcea0230b7cdb19d76207f06815eaf8a6e9cb98ff004b23bd05db6ccebb87bcce47052c18104589be143e2523e7c3231874ab8ebe862ef4aa39405b87d512e4a113588aa315e72a3ca3ff692951a38e7532738c4f7de9f903bcf4b49ec12115caa2bbc765a706b32d9c3a7fda899315ac053a1b9f0882519ae659be9325bcbdcb0a5e8f781b88442d7cf0515fd7350887d2243e28e51352adac496b8d3f80a11d5de5f029b6c847d5929c985d20ba849ec6b29ff50af08b7f8936806281d2142383a45c1193efeb21f025b3581d5c3e2c2a025cb32e862812469bbe785cc8f4c9f5f33e0bf19827380f193fa5b4149fd2cd0c3da989d7130f4090fdb16813f460681c915ddec339f0ba91b75e50556fec47a8f3ff98d20132a371b23747458c783c76b3cbb991d5b0d65124b21711f927a5ad4ec04092a709b53c5057dc5d2996b50ec75a134e08ea11372f79ff577607c973cc4f8ae6a659d700b81d517a2e4c8ae86ae58e270cfd8730007945a36e2a961a9ad62e910a2f49302f72e37b5da2b96509ed24e34108def9699c60d764a048f4b15bd8c6dc5929832bbc255281f0d1b565b3437d8d5f79c38daa64e202f1e2486b6a225d84b6fc8cd10c6a1cfda57615c56b56493c513528e1db1f698336880c37dc6f28f003734293ec044363692ae938aacb95063b6e836df1a150ae99e4b620500ffc6f02d3c2c5ab76eca4e4f5f49be4b62b9d8429f7b119dbbe8f5eaf32378c6c974d08dfc816a3adebea48ef26c87f6a5a31ef4baa4ad2c5711bd842fc4f35b5b6bef928bc034772e29d7d12fd3b3b61cc39288ce02fa2aac3f5384d7795d09e161981bdd706ac5975169f5564fcde8730c8e9e333aac2f6b34c82a265896aa1f0cffa2c3922661c287dc70d38775a6102b0362580cf630bead344a7151c89a78b38b32e14e69e7e9225651d4d5e029c1e3ca3dcf644d57ba33a68bfeaa1a0774624230c57310576a5763e20461afe83d0aa7633b40abe05035e1227f91f23e20b2d2fb35738ee96a9b25cc1f2b4d5fe9e0b721c03c2658362cced80d794c73fe4987831793fe54fd9e8ceec69cc7f42d1ce401f9ff3ffc46a6b4a3def35dde3169f11afe2f511bd129bd3b98cb50a044d759978ccf24a5b2c36735f4a8d1ee949d3a7d2b2e6fa6e04c773cd9a9095188c777ba2faf365212be1a127917c8890e51b967bffb5d795ea2530742b83ebcaef03f0583b5a4b25dacaa5b0ec576bc8c8d130cb625a00fbdb9280863228a6175d163175dc7bfdad5c8e0f0d3bbda73b69623b29362c63783b428f3d7922a5199dde691332522cc5aef067f2f08f5a50081e761c4f2913036f95efd24994adbcbcfcc7b67f2e954c773e824d3d95f8aca04cf5e52bc671387778f56ceb445f671dd88f421e7c434b4977ab40ee5f5608f9b7ea08b23f2d870dc6266c3605194e09a9d6b9501d488008e1395ded04f2c8d0d744e23bfa87052621fedd6fbaaaca5ae1b386dcd2261468c7f76b2c289ea6e72743e3fb43f8019dbdf0b4504878d7b69528f44718ae4cc75d8038a4c2327acdeb3c6a6c59245c59c82f2120b46149093f23a21709d6c33e8c9df6fd25c110e7e4e248f7e96b1f914a6a53c431b0aee78db8ee06874dd6367473594005f6165249a87e8d5393cc1e1ea229a97170b82a5690513ded5b63712d0ce5d205d0024198f17a228d8f22bcec62a047a6fcbda1963d5d21832ed074457f62a30bd8f21aa1aad4284515117ff9607538eb51d51cc531d0fcf88ca9900bd28a07ef11551a114a2e691d76822e62a6c6274c8a57a33513f4f08d815fa6d6c4e69cb8c0fab17a8f8ddfe12c19a92aa13223213437ab2c1a8d60f4445a4017f2939598dd3a66ec29b695e89d79a5856594134095eca45f19823022a4c667b5791d28359c317c523090091f4f931db7c78421370ea8a7db22efe62cf1d571faad191899379ede566f02ca7d0c66215a9af4e8d8eaee682e4107545233283eb5fce62ce0149a554abaaf182ce649a4127df77911efb59e15b5ae5a98f481bb0ef1c99a95952def60ed376b73996903c459aaccae7f91011a30d6f7012136000dab754e0069367732339d2facc095341c57c88862ad0834fe7ac044dadd64adac246ff99569a120d11ade2f87666c8f801278075d0789038a7f70966c77d3e2eca2ddfb93524ba5b7b7fcfe082e6f3d6886b3b42137225509d7239e61520e98c0231562b6c1a48bfaa595b97eae369d9fb867b9d2b518b5ad89d95010aa1603afb5131a64f2c77524ef052659fb2d28398a995494ced22d049a22e76ea742391895acdd52ad57392c0b5295ea0f3188329614e748262de426f468b782a8be5fb653de30e49a986083863d93a004bce83179b28551d2d637f9ac90c28eeb637138b582491f55ebae4caf8fb76fc4614bfa141f62bb84b3e2ebef9aa8cd8b3479500e4b8c90ee94c49e8568d48f70a97f810b3f92a302e475bd530d5287bed51551d208922e93ae7dff3826546786d01ed0187a239ec9b8ad563622c4d849a7e5f74b7e1f2d1d13eecc1f5eab5ec6931d34ae1cf0649b4cf3086e85c48e6af65d3a2c97ce4b90c21c1048dbd9f76d47f3d77303a7d4d4228ed7329207655185cbd133a401dd27d6dd8936dae049bc6fb4a43a661700d94cd8ff9fd2a72a5a1037d5388662543abbf6855c2bcce3ff48a120208fa26491d12899d0c65882f715ee6e93f28bed2679a5aa7559b3ff172a82a96b1282f642d40fe5ed612e91680c93aad62000baf15bde39069cea3150173aef8acdd9c97784c6adcf93b5dfff574021eea110f85e88b446ac170a5293dd211ab2b12ed2aaec3114f309c2b4c13e883d91327ff45e8421ea22af943ddb9c214dc95a94a7624dc40faa378d1ec3c755e25c30329de3398ebe0327ee892ceff16b5adee16afc3806f8b6bd25739f23907521c06e90320bee22d3f754551f31555653323a9220070efa5242b183fb3365a7d8fdfbf8a95a3138cc71673b2432bb2b54ff07c12656c1bc10066a6a0120fd437e91ded602f30e13510dc192d657d4e6244189caeba892edda033d490732f8fd562c1f85dbe9310cd7df7b12cec8c21cdd4c9995de536055cabe1913b4d1d991e154b977a19512cdd7427b68c9d53bb59be4fca63a8b4d4fe9ea2714ca07cebbe733bf3550b1a9f62105ed08e7e1b329fb0a9c4cb7ff26a9470a29cbfb283543aef326aae261555569bc0efac348f9677f67df59455ac5755720b9d611d1b571db4b1086d59b8ca443fd1eda4197616bc8657e180ac710c3d8f21ba56cbecdbdfc4336755fc215f552fd1693e37cced71a8f242d3328456d0ad64f2ced4c33dcb08fe1dfb71fac591a99c151c8fc9017b175a21a04aea05ce380dcf14aab1ecf42e4487a57329b73f176c9b87ec17108fecd0480ede99343ca5caad7826f11d8903cf63b10a1b5c2b917ce04001eee1a7fe8aea78e1c32d0cee443b16e6d785111ea8813960469687e1eddc4d8b979be86295df22e6918045cf3e533e9f93a100ac582e3bab559e8bec866405f9d55369ed8768448232a148336d1253f6d3debffa81da1069eb1de59a8d955f8e00ea8168ea8658c16fd9ed04892e7af2f523cefff25de209e56e9c3b4c448e7e32afb5718778d306a9bcf61e6677aadfd604f15d433a567671d63f43019e94204a550f4b356273e5133f0aa468623bd8944236aab1c5e19c449aeee9c620bd057218a9c6cab1e1bf80d0e8a6ebe48a0eb35204d2e951bb9c752c71acc3259d5567c8f82babdeff387965e59caf39c86c53ef1f400480067edb79b70f388417f09ee989d306f35f8297908629f13459d2094d4c45f252fe6eb9212cb8d77960191d71191c03fdd7aad5d5aa7afa696e89c99d120179732aa8a3e6ac933abfae0606fa0e078f761c1f09f6b0cc64f0f8e167545bb22232e773fd268bf9695837f8ff3fc90e0741b4fa71a50cff68d9e460e851089ca1cf66640038782fd8c80738ec6dc1855f74d0cccabb4d84e5f2af83a98a44f2075759b5c23745d0e33b730cebb73ab9ed8c8c6e0bdeb4996982b13694d66d6bd341074e5b7e56a1466e559807ba23c1a2e2373b6540f3facc01aba690805205a8f11bfc74d4b2701688baabf495d5b87938f0baa45fbac3736f024880386b85009edba18285e07a8b46d7355d246a1bf846934a464a611e3a230512fff853999127e3ef347f88b0c563627502fc3de9b11a4a80e01e14fa6f1673c824eefc0b7d3f1aa1a8549365a63a95ce8bf166b8c8ad282a1d9a5b06f0132059517569a5a8a2f7b534f822c7315536b0a88a930a3137892a6a06c86d2522e013a4eac9832fa08ac24e462417e40a005208eb86b691d58f4c8bc82b1b75fc6d07b48f56854db1fe061ea5617dc1cdf3417179056abf928eef12563a60360d563c9daf969cb9737a525cd4d974592f16594a4375480c2eb41978b41e03fb48a88c0f2daecb9376ef900d4d47a7dbf7d1e3f7e917f889a3b2b4710e595d386b1e1a0680de56021a9487e3f9c613075d839febf48c5c3744ec44fe6552b11a6fd6fc485290145bd831a9497570557013b7067486df6ac1862cdca39fef15d500b7e28c2595923735bcf4e3ab95509bd45e52a4bc3b9ffc38d25d3e27854a3ca8e4e26a4866eed9c6e3604fba0db65a6c91029707e0067163fb52f72abf86deda90c7de18237fd17660ba92fc427806ed598d4a3359729750abf70645f8d3cdc5634d60eb338bc2281bd3710fbccab1247cd3eb7963c9c46820da15cf66e1fd5cefd9ff7f562f4a72aaa15abda7be8dfda8435662052e36ce1d1f4fef6c1111650267f682402ef3f9b97737c5af86db8c0c246ac0fc99c43995eb8cb5b4353079463af2a90b865c2041a111916ce0830b195f0a00874912f3b771f39631836bd29057b5ad4dc71ab517c396cad82e9d31d86a691e82afabab71340dfdab0b2677ae437ce823723902a3015b00edd6639b1d36f669c67934554ed04e7beacdb6372fcccea641ca77779ebebdd308ef4c764ab61f71b27caf467fdcadb2d36bee49f103c4df75571b4257c2cea459c15a2bd52f2c2bd7df629ae20c9ab853f25890be9dae831087d2161291cccc48d6bfd4143d19bad2922a20ac0316f29b277dac55017002327b050ba836131257ab0db76aad90d2a17183901b325c6cab9861d6b593aa447dd3ca07ad9e4658af4c51d782308b1f83e892b04c749ecfae959d68f12150cf309f90e8247eaa7234b68025fa40728422af00ce7d27ae78c646ddb0083eff09f72450b0cafac998427f618cd1715ff8c18ce3205cc47652c2c993568a40345f5eb72779b0004fd86eb2c1764525afe3d44d1e5ef3add97eb3ace00e9347afae4cd5f9511c33f807362e6f855098d3aa38bd3d26128dbd8df44bd80fd95ebc643f328a62e1a2f2421642a0c95a7eac4e85ebc41002fa5cfd2f7b40de7aac2c749dc7b65400625748c463add68c337ea116fe396e13ee1ab6ca65108bc8427dc1ee73983942f75538651245b4b6ec931413bb4c13a6bd514943fd65dbd27171183f6aa2127c8449369d30b6f87c8142f7be3fbcf1ead38ddf0c6157b0df3f852c1172663c8de5823db9b351429a6b176687428203e0315bfde28dec6596b15ed03be9c8b32fa4616dc8424a332aa680f9b53a1502df5ddb13bee822c83ae4fcc2f332e1d3a57fd18a5892b6d5b8639bb5f1813560d19240c3b4810faa1d8bcabeae02916d83b83474920a261d51bb09a8b871359647d173a61cecc81336c525edb5dc713e8d37046da3dc58687a93811c13bc4c0ebaedc5868ae89f85b59da4b02d751af3e7986556a75119e41753d10ba14a1116a127c62bb841a668642b763eaf92c03386436e1be91e3d10e6f83106a446e96a338e2ea698738bf6b8b34349e56c0dbd9d4994f4633ba2962f0cafa978140ad480dd24c8a521004c373f3797faeb90e1e9ca4b3e4b5c470a39ceff250c59248714e0d0d6e8381986fce4c510c3008725e6c1e6b04fa9dfcf572e5f1a120721472dc512c74d2ae082a4942fcb8dca9d6b3ee1764a0f3c4c212a8e30260f3d098575b04fae84a67f97e5fe89ea784a0279fe26f784848a2e4212b492bd6e24bd4cd5c14d0cc2a3616202fadbc1622e94bdd5d7c85f9b88c9a0e8ad273f0591c67ed80e626bb1e9135e137a0a49ecf77ff6cd310973c6500ff9bad5b5787b15e49179987fa88f077fd12e32e9c04cc217b587fbec7ec833d02a131e705868ae52eaa64d83aab95dd497c681b0d2c7e502d371e776cb27038fa7b094286daafbadb96429dea74f4d8e8f8e3df30e72dc6120509f172383d77d235c20373565d566be43eb966f8121fe23cfa3824e1ced9bb1091631be68f79f7a0670446479b462817c02eb3c32172d057557dc3172b12dd868099eebafcea2e6e738d213108a4faa0a7c30d39f5e9552e42477fc0704b9314b14cfdfdbfd6854d00c2d4b08cf33c52caea7c264433c68c41c6e80c51911c70e93f9c6d338f50d19965bbbb6e6a37167c0d70f2669d5d2feb413f7245f90ab387378d8bee0dc4a55fcb5760f235d0687b51b9e2a66cdb9a2b76520dadc3b58b101da6092bdefd940ede63791edcdd93bc71e82ba79c579e631e0760cba65f32417d977cd339660b4ad3698cba07bf385b0235fe192739f05167bc26f23b33cb891db00ab20dda7aae49d8eafb080829705033f324db6eeed74e327b98c736e8d91ee9c98ada6c686ac3e406771b287264a3d0e53095b835c723a4dade439d8e51682676ff8860b94499e1407dc1d523e8640b7751b2a0fd376cc50730cdb65551bd5f64dec28f7179538337346787c68e0ee50a240d563758714b06ec483e2486885d943d65ed65292434b69ad965271caf1f97d62380345602075bc3a41419cfebd0d88a681c4342b9b7753a3634fc19d561ec449598621227c032591e40ca818edcc5dfb3aa86450c4416c3c92a20f48bfd1421a9b208f56ca8cf8e58278c6077a3f10ba37413eb63783aaa089b217e755692f1b0d1e6d7bc4455153b60d3e8bb420968181183967b4b942fe0db86b1de6a7c1ac63dc9c4227a5ce1001950b6bd1c65cc8ad1e67e3d664a06bdbcf820e3b473cb09567a9d6f93bd24be0880165ecfbe206fcc5fa3e2affa5b682337a7c16c377e9c65500a7119f621220c68d46d55e57975a25b5ed45184d7c6e1cd95432db77667cf31cc57f6f2600494c1fe51be80184222db904ed2861a59e36fb1ace1ae02c068250ef9f4e51c2c235fb2f94b3b00f7f1802f962578723dcede3ef035980bccb6d38133c7936b2e3eb49ce18c9f2c0965ea153260e59136ab627c154b84c9403d45cc58e882e1cddc92add480a26ce86b8372848c3ac8aaa2e1c320220b52a8eb08eae20ae68aac994e2a88824a5eb791afe1447d443447c15852a16dac0f102f6ec6cc8832924983f4070cd22551e7ebee86646707b7e3b1d7fb08b816a7f77a68d9e28ff43d3264efe0497057676198446de33f15baecce729ccf3cbc77415a5d64af8d3a0dfac6d0c042ec1c811d7ec4e973049dc6a0eb9e0b04c91ad08fc7e0f4c08df76e33986c8da0c7a97eabb891fd996ff3f02e104b5dbdf9b7831c0326b8c48956b200d8b95ea7fb30d3f345a0447c39a3a5db56a664420fbd6e4896346f4888653f7d72e813cf8174b385feb65679c62f0f95d0fd3f88c4601165cc71f47d43f3b801a4ead5876e08903b2f523ac97733e25e5f5e74b757406165397a0045ee3b3eccf8513ad704a08c732c921aa775c7e36c469d20e97947026abd1778493790367dae22b9d70e04cc6ccb76b5faca3e5d6844e0bc50b7c2c910c53cc872cc5c010874379875a83cf0abfc92e2c4a57b644fa0ab02ee43c7e8cc328c8ef1cdc04bba8a29c0ead76d08963d5d089c0dc31aa4968d634f6dc803b7f7ca42fe10656ecbfef6f7f98faf7024a6d5f5049d3d16a97e23f366243806c58119d2b20188d0a9a209512ecde8fb4d6a1cc3b3c12ea4980a19eb3ccb7b53ad52209856cb5ccf01b73e420ec7e213f24e47bd9198f4509f9548ed496250749f49dad347e4043f6dce0b9735826d4a554fc043be7dd4d722b61aa64c7ecb3fca0eb59da5ebec9664b5c3dc4755d2647e4d41aaa9aebe9753804e38b5028096f964fed9a5c86c68c642a3b04a10df45d79cdc1a4641170e84b07d7067b07efd713c11c3c08bf401cb185ac46b4d4f50d69c5426f180f5ea342d7cc38e214d112c21a5b2affcaab8970480308cd3289c468b83a4f5b8ed822e2c2bdadb072aa5e8e3c1f399ca90d1de3f4cd9a00d474ac42e05295c43e51cf4320db89f78a8a59caef7f688d9a20f42d20065d9a19d3db3c9555da4680ae3562a7e2b612b7d7df47d5e447ba40e166d365a9a4b61cadbf99f16c3b75793b43751df1fd5547fe2fe44ee3824e85a54f30d0fc0f0fbaabd656a87a197625e1724737c18017109960a68f042148cf554615361fb46909140dd9ba5e0165279211b909a8d0eae444f9a099751d0cd60cdfe4b604c26a05850d247587165af630eb2688a9bb0e86a23aa7370408c4fe739bae2f1a63ac329eb81d076a8ea3f57c124d6355b44f35a2875cf390cf4f62a8bd1ddc14b825e4bb18a4fa236a949cd991efbd6651885f9b0caccf96c8e504be8cd53c722ab890870816744bd8ba37b25b59503a92ea64c13b296168c9d25de7104601d726e6559e9eb466d1777e1f79945b63eabf6ea511694d22d915b4313b9fdcb34ad9c2d9d5d450fcccbf82d1431bd991f9dfc05cd95d4fd877473e8b5614859ece1d79584ee30ded1e5676c76802679b69fd6cf9c9413feeeec8fff29c5b3dda9972977bd7c096cfc83ea76475790f04a3de4657ae229b7c225facc13ff83a6599aac01e25a8697d487febba0df9bff4339440462996045cff0245aa5c7d69b0dd04af41d0e238f92be64bfb4624970907077d2029330b864ea99315d9811858d58ab58a404bbf5485161a1612bc47e6a0676e451fbd209cfd024146d456f39139b50cb5f7df726be05da8b039bb246307542316fdea80fd80ceea02d0657ce2028021626f9abe05757bc020cd009a7a764ae70e82a917ad45cd24a193cb048c8d14be9445b63b7dd5f2fc084f309d1bd9b19d093e7b09b5cc927894594ab1a683e0d2ff510e7c414e30036c95d308c5a04f88036606ade109e013e48019612b30097e425c282368b5db048b031f201798095b0193f00be24099410b7002fe0638206686e358edf16bec1afd8d4ee3b7f16df4373a8dddc66ee3dfd869f48dbe46af1adb0dfa763665178128689b75b0dde187220af485a14dd085f09939e78cc17a9c3c7e66ce21635a4e93d39722d798395b0f13e3e7c89d330d967372fa9cb863c6d1324f18be47ee9861b60c13e677e2983246cb71d2fc19b8c6ccd162e6e4287d2e73206724d8f2a17f52f89a38c6ccd1629e18fee1e0486e091b4853770f65f1f97be0ce8c9375aa895121d573ef05cd9e76df3799ba14a82dfc5053b97d38009eacdad67af1c8c53e1d92883a5ffad380b420cbf229f56115f427e85bc3d2c87f7d1f293b507bbccd5667587ee23b807c1d60933f0325954c18f2ef11e725acc54a522370da8206f7435630a88d778e2b60aab653263f866d7211eb82031e018b8231098cdc8962bf5d82a539c7ddaf7494f2972a24d1c04eb3012101a0f3043e2959d42764987ee2f5d1e11afa4ed1dece78d48118c83b8991810f822692371e713fe66b6b2912e3e04a4fadeb90544590d3e7d18581f773e1e56f61641acc0573de12d6af6a0d78a59f4037767d25e33606496cdad5a83d215993fc27270dd6d22ba7927499afe0f3aa77af935be6fa7680965757c7cbc57c42cee0ae6c7833cc01d6b72eaaae5180aa3e852b02c973a85c017094b6d0f05bea797a0247fefa4cc50a5dce370471b0d9f6d465b6d002af78089e6aaba1dd5c56279b79c0357350f8a2eee06d2084140c4b1d805eb670f6d3bc43f1e54d8b391898aae102146e2d10e5e1cea833c895fff409fab2b184d9802e5cee2483544d2aa06975b483180a0f4fcb2928ee5ab0c1c775182f8ba15a882bed8c18f54483fac31f9bfd65d67c7d3c56f49194a88e742f9a92bb641c829a6608558975106ddbb6dda87a00ac68a70d12e5e061d2ee0674031d836b29db95eaf35676312dae63eab2f5d48c9ab1840e06969e395d80ab766927e3475ee314249585a80749d980c51c2e0f1d74d63b9e6835859f47ede59bb2c80814173d427d94d84741945cbb4f7d1a2a3bfd01fa4b512d40e1e36dd8c0c05554378ca76f78865f39458902a6f3071bb5fdbe7cd5f86e2511f5b77b2efe85eff72f5f65ac020091516dd31002f052b539222fa2f84127c6ad675d7b505b1621173952d648942805f969d7e4a3f8bd9cdf338b5f42f5f39a80e50345f3dc5504707b78ba7328379374be3c4344101f7b24e78ca78f6002977546776606c0e46db31b84757f95e10959184830e28ce5ab43250401e3b022f7340dda7db83bbd4d4e81456f6f82d1240da1cb35d077cc185d141b1c7ceb204581407cc6a52c813b618a04c7f806caa8df3d09b04a1d1ba57154867fe95318446e12aafb1b0d77cdc0236af234f7724a65454ab80c144f3775c92cc15006975936124b1c7cd40cd6c4ee3013195452dfa1341cb50228cfae44bd87812e15a0e514f5ca09eaef397dbcb84a35220d686f59b634be8810cc306c03170223c95054b99b8f33c1b14b20233cb3df430a868cb390c1ec34d3644fbd6bdfaa555697d7cb5cb24726dee2f5ada3c4da530ee15a693ed31aea1bf3a5e937adcfb1d68ee301ef9e76773c0674bad3d45a00adafc360214523581fecdf8464a40481cb22d541abd2e40b8ef0c6b2c64f2bd60ac3953522a148321ec7ff786a5cbdda9b39a791e09dce1fdcefbc08bd787c5aaaa79ca9602cd6e0f78b959163bef69198ac99e8cd7f9a8db0ac46df5df67960829408e2fd72ab370d786214cd223374d57cddaa898df01f7dcab64767004b9bc907a697cf090f10447c46b41506910d18ba78b4920dc3924dceff96b412bf23688150348a0d51cffd1d413b1aca1f142394e3afcb46b02a8682fdc88b15ac753d5b6f2163a9e5b1c492861478723700fab7aef7f9c3c7859627491a888feef4943067df6c42040a9ba8c93acdbde4bd03921b6278d39fa69e13dad6ecba0b7464c6b70ec34b93451b05f4c4600098e8b600b4681109a2c7340ebed12e62b301a936349fb7dbf7b0e4e7cd1ba284a71797a4dec830c731b37cb65adfc25e8fe8b27221b9debbc7084b026a1d9f53b0cdfa5fbc066d5d2d97311b27c8ae715085a4cd42080cee17c742163be37a9c00495978f9124df0075a2d8d77b3b575c96568a64a454c20c733bdaa3108d771c809e8d4a577d566f6e071f2e68ce086978f01af5eb88f80c990bd1154f98c607d6944bf6ca5984a8f4298491569d55942a62734836f07b17303723ceb98880cd87f3e0a82094cab9e73f7973ce217cff33510839860ea318856af9d58f16ad101c2a22d722e456f77c5b5dd27c5e22230a384496832e095297502587130b11645065f821443d336a74f310b4add2b779c18afe515f513abb14f1aef373d4f20532f612bce97183c56da0fddb77ece6313092d8fa1fc45bed5fdef6f0dd3b68b82024aaf760625dcff38a9a49c8c74d0cf8e1e574ec4e614a12a01905863b599398275b16f12e3d997c5049550196a893bb4a2ec5f5b6eff2bdea83b986d1e7a94145cc256979b5f956bcd9767498fa281c368248c8ec512a762d76aaef4ce388c4527d7c7b7fef2f5ca4de219be1f66189ec17409a0818efec1e3b1dd77c15a05bc63d0031041439487a46bd48e9d05a352734f3ba923d262d0af8a69ba2b4c1cc58055932742516311aba55c8b8196837e8622e1fabcf89002496e234019ed7deb3440a910209f8917e2ed152aaa86ed67618de87795fbfa3cc32e15aef20c1c46cd599cff0b030dabd2daef1df37ad186a75e2ce2addce2ae876876c3c0b823b30515f434243ce88baa3b9b881a669ab4a75630a774227a03a866ecd04f178be1122d2e99b5e00d5e0d343620347ad070e341209ea49d52914ce47111c809b802aacb43b88bfbb9a568211df44629f642ab2401623ba348d3ca60ed8e619005e1b9290b08e04b2ac7213aa7440a30e903847d6eecd39f7580eda3ee9dce0ec220c05fb2c51dd34725acc8ce054226c6426e942f7f53f24efc8b1a4605fb4e6b86cb4d6adcaa8bf53a68512b1814773b77557d8f6f32caa4370198910a00f99e9382967342e214d981edbdd03d83b91513fdf576152a2e7e70500c63f1ee95962dfded85fa3047526fd03398c5abcf3a3e1f02dcee7dd60d1d2d0884dd6631de69037414374dc5667e1180790e46449e10b637610e78e4600cfcfd6c7e1207300b0e1dd01c7a1923269269c866f8b6f075f3d5ac56904516f9ee9c5937a2002e66a0ff7c9c4c18bc9950c9a489974581e781d2779628435592d6f4b6b998259e910b67df459514a19f9445601155232449e94d723a6f9b92ab9d6727430c99c6282bc79944a43f2aa476efabe79332e9baff489d26676a4ef05f07964008361b60cfb10114efd31a910886d93d72ec3605762f722b55f26eb894e91db206d060ee4ea8e9238b974c465cea1c14a6fb06543629ee1165a34ce39bb1ebb617e96d80219105c3d35e3ddd246e2357a1c893e4f660cea2398632d567c28883d2e87d99baba04af7bf04542c24b2058ce767cc05232b48ab9d61eece5868b05533401dc2ec8bd7682de66ab711e89848bad04f56732803b2c3fd76080f299e7224e05b0a9fad8fc250c608b29be452ed8d8b6306556acf9f389a1270836a04413609a1d1ab6e1dd5d9d235057235390fad6faf8e21fa6fa9fabaff3fe86ae8e16ede74fbbaa32f73ad664d9c495ac98eea0e279ae266b9be1be102fd47c6f91570a91938ee7d3b926e76de3f3d7ac2ace80d4cb2d12a735769c2113368f28b44ffce9649faedaa6b98b7469a2fd3b5664608d66d6154ce85e4330bbcf3abf9d513c40d3d9da9801482019abb7fcef4e749ca616b0978a982d3e9c866bc8dd77f93f510422bbf4a5d47502761436d0d6e951822e396c3ea2f8e02b8c2b14d00bbae9f9cfb97c9d4492166458be0e3ca2e31a663b048bafc4cab50015039f67ffc3ebab48fd1ad68fc7106b4f36dd6f69e6b75264fc11c5b0c791de3ce77f9ba2b7239feaf4a87f7afc0bcced94842ff29d82192cf7daf49dcb93c2028b56ca886177fd293591807e18384558ddb900316b04ec3d623754250071fb8b6af2a4d87d18fedfdffb807cd408cc80f380030762e57a05d375b465a73f5fc798c68f54b4e809a4cc519618f7af1a468e126ba73d1a07906addcbfac2296f0d03de079189249d13c7377b9968a38ee2e0a2f6413a0c8df57327b2ba920f7595cafdb8fa79b58ec9a0ab4f5604d95ad1f697ff02e33c5f76e615fe588546fe0645ba6d1af5b6ca803184912f778b0db503c8e9b44ab8f98089b7294ef0ac01a60a4f1187a34fe4262909f561fde3d3ed1e6a5283c15b9964b198444d106e50998092d80a547d492e12f41a0c023fdc80ee111c946dc629081a3cda10a3b1e85c424ad107a4f6dcb99cd9811e6b225721990f41add00ea8b35b85387e09132f7e05e3a0ac49c9362a2a56b63d10f7c511b4c20250e9cde6d876114608e5d05eef5fef43caa203487dc81f6b805f97c80a0569e57b159e399600614a1ecb84c013930e49a7f4a9294f5c0b51e682cdb43676ce46f299b344ff2a8ec0b8dea68df35a3b10d5a5498a561a9f73f8679c7ad9fbf213321144d11ce971fdb9001581a8274a7003bb88259a7079903779b33dfc77dd5e2c4e247448261498158c4a6e2cb858a29cffdf2449b5f66584447d9a0381680f490accacac57bcf6c0ea0815357ef47bf7427a59d0de069675a8c7772d4ca9b6f6489b1efec5849c33ab7fc125cb50656e7ebecf740ba9ce542581c42a27bb853517d283e0160e445096a44577905f4b75056f4f86ee24e71e7a26fcd67309053f7a27cfd25d5e25cb7e152b2593e52db08390b96e867ab683cb6bdd64de1fe0f3d226486d5260e21c98a3c1fdecc460aa4589c9c7e2fe5c773c307d0a126c0ac11a4eb59e36214f7d9cf588b5da94fb963e3ac5e1690bfb9e794fa207e2a37bb1c92da3e0c611e02c73c9800c8abb6e3e96e9914e449d4ef6a1f531cb9c04b8bb0254dc6b99b33726e85d243dbe6afe96bddbb7364db8f90ddc2c55c56b7a8787cec8f99c2fb3016637cce6cd02d4c8edc45dab90e92470757516ab626ba967a81fd753c543e85630b70d7af61653abef4c880877201e7686d60c24773bc15c9a8fd5b4a2539bf3570ed1794069d31a044eaf39408a99bed0502aa6de4356809527399936eb60edc68b1be4921f5bbbd45c5ed17540886310de573e9024bcd290724386bf95602391dbf5c2a3b3c232d9d38a67422c80dd2a46f04b2baacd4b89c52986a90a5bb3a5e41740331f57e28f4acf81b60769c1e4302319c427dd1856f10704a8457ea09a50d7dad41ea1b7cbfbd428f3c7c2baffe07804800bcf8aa33785bcc42c8c910bb16217f466c237eeb8c9650d606240dd641329c2c4a641807a5cf4af5c070b261e4e3b3db17af687e2f946db01cef665837df1cb5ae982815b0e072ccc5eb6ea4820a553460be7344177a439c41d4eff30d84d8c69b360cf1dcc3c571b4b37b685ad6af7e7f30a7cdd4bedbac884bbd3b2d6eb4dc4d68779abbd387eba064596bba0c1765171b336d0e6c69a4be90223d0bb6f287b517b76b7e1ab65690b6e9cb6c2a71722ee3be095a78b3d4187e205c97e13b2f9212549ad7ff06a72ff5d0c7755feefe737097489ec6006b38a3414c8a8993f9f10bf5a5a0e9904edfc24584a06992e6de2c6738370cce0ee463df57aa4b77a59aff5ac9ed54fe137e5c6bd996ee8e6546481d5baf16fce1b74e3bdb6f7ee656f0aba21de6b1c017d88b85367f14f656a5b0bffda996dd180b98fd1d16854290a47847a04939bc1f9f6081828da7351104faf120eb2338274de14f873d7a7a969857b31957f1e323c82184b50b6abafc0b446889e2712db8579f42dbc752eec51e7a1bec5de6a4b7a27f678dceb3128d7b4a897e49e75b84352cf4e3d9374aba3be893def7c9f20fddde25ec7a05edd92f6b385b3eee562c585c2df2cdf2f41fed0d2abe7c2e2fab2e297e15907cbc50aa3f4e3d27d89c21f593eae1bf0acf657c5ab7a710dc14a736771e5cdaf90c060a5ce37dc5c518c66dec5952957538a577ba599cfd2cc4bb43206654af16aacd442b19a798b2bb9a8a614efed95373ea3999768259a942dc5bbb1727b60cd0e45375756db89b7f24a54fcbff2e8e8028d9f433f905edb8b0363fd5fffb90dddfe47466656dcf036f171a95ec15a377913e0e325eb8a5517b4dfeb54330b3c9e039aa3aa855b3052aa9a74ea14ae59b46ba470d6de23dbb6e9045638eb56919daa4b16edde55cadd2669b5a2c429a0621a8c1a841fc2100400529608e28a8c5c5e1ea20781e27fafd7eef55e12c6bc4fe8b1caae37b3b6d612a2d636217b6fb977e508e4071a08d3d5c93063a6f70428304134ade323c3ab90c4aa726154ac90fd26ee31c4ca02051e68c07c56a746a353a650a1d2f05a5792fd581347f922b613726c3134193e653493647590c28e0c9f2309f6abadec35b9d845c49f179b712d57aab7c34b21c37755ccc8d0c464f8ed03351226c3a334f003a10e0a199e7b7951c67824938ee24ffc9155bef803039213421d9ce10f6764bf0afbf1726e86af49d8afaa60049d3c8112447342863745f9a123c3bbd6b0e172d4ab65b75981ab11486402f6e8d33bd12cdbddc0cad938f1c795e1332e70b82a71e28fd59125fe1870f3848f8ee943391ec77aa932f48c92c68606f6822a00e00a226fb644596baae596ba6a3031352bc4d4acd082878367430cac5981082d980183e3f52b9b34934930482f47469c0d334a22986143110e36dc60030c830866942237ff2cacd79ea4b36389b2560abb59aa5a666a1f1d4c6c73de4c2980f6726d75777777b79431a3b3bb71f79d73c6ee8ec51e514a306c7c3f4208514db8bdadd37bbc72856d430c55518ecfa109413e44987ca802473ccaf111c9a7a5813a56ee5e72779883dcbdeb7e1022773f80e1451047b0c1858f4f8cdb5879dba8e46dbb0d236f3ea4206f3e4d70d00417b090b5c336d0c828ed912965824fa64cb041a66d84e00a0ebc9a1fb478811442190abde4243cc9f2564a964b4862038d2ce5490a1940b104a38d04d5b26125890c18174c36583093e784a922724ae8000e9450865d2204f2473a401cea92e59328813ab03f293e225ff211952f0db046fe88c421007cc93560f2d540470d24a5b5ad96ecb70e2c0cac819747f2104e2c7b0b14d8efc740a687e99b4803b38940e4696d8f1e666eb6daa3ad4ce7040206eac82f85dcba17038170b1c7bc95f1e6c09a590f03759460a58c929bff4e799e7042ab65ed5773e2ec0e869929868135b1f4ec26f172b64d579c2c5cd77ca90d3b7f8900b9d820cc566126550b2c0e7d13719ba065467b1ece0c4f3ce95c99613fd86bb5b6d592b03c3529365894bf24b0cab550a7b521ad8a7603ad8a5625571f6d48aba255f1628311484720df92675865fed9dbfb0f8349b0b6d24674a8854025a5437428d3a436a24374482bc106fb86cea6d995f436195ac928d7734a49a59452ca7e46339a6536349b369d4d2af31349da858c8c12d205ec66dfd992ba9c4497a3f6baeb4f292b97cd3eec322a3749adc8ee6e39a7fcbc72ba9e645795bcd3f2ba1c7541a6eb8926a59cb3bd98156d104688124883d50f5e6dfd633769ab924e94ecd47c581626d8278cd504ec8985524694230e803a992764e8c29ae8cd63aa0242424242a236dc0013e20398902a3f5202a56f909090909022ac616541ac0c3c071c5720f08aa214a1a13caf0cb1c168a9a252b56aba9d97e23830fd8a515830b231a2318ab245571e08a2427a91e50d07884068769a8e3d22cd545f1401348f4304d234d3b2c841ab2a14d4920cbc52d5aa66da81fda051a402eacca01ca240b1816ed92dbb65b76c198dae1a72c2e0c0e0ab6f61366258179113904498bf885d432ebd7bde943833d6dd46466b7777d3ae5bb7d464679dcdaa6514d367df289d31993541d5ea49fcd25efc8095313c0476ce395bad5ae984dddcc056fbe6dd314653de24d7a5bc4d4e526ec21a9ae54de3b2ca69336f25aecabc99b8d2c699386eeb60e84f53a96ea7086be067d10661a6076b20aedac4301bcdfe699aa6497a654728259bb3c8890decc0892644232072e287a8893432972191134300b52a154750031809694058102106900d8608030968074700418a15238080046486116140e9170c324018525a5fb4b42108815404243c1a08f10321bc60d2da322412628bb90358cb22592003322db0801416f0895181dd32240a228d9717b6cb902888a3208c0e932bb89665190f8ca094d23190fa05c748aa3efd823ffd82325158ae06444dc0c0082b55e4cb5b020bae341124234413506cab89272e2840c05a9adc618433a0384a810704bbca70e88c19c83439a307af32ce08e3c76e190e9d518433d238425249ea17f4f9017232001b3ca12f1f9200449524606087164970923d20609905329a65b406233489126589295714418a200769e4e00a1499cd3ab0840c5c800552e4600b1870f144e3828b2856605944404de45386444045e4cf7e8600a21184d08f1292f82162090330f1092288688827800c1044510928853434308411c4f8c072190ea521254a1a53764c61b30c87d2c8c11a6914e5c0b41ae3bd980426d7971a6918e1215c202221a55f5068a84a919101c038a23979280d339a20c1450f9080381a52034a9e191201c144fe9e63d6c46b85268d6a9a0788d08630541a011199107caa0a8aa05682b42b8698568a321a1021855045be604dd2c18f5012183f0308824f52108418c0109eb4981cb1321c4ae3072220903a0ed4564ece6896d11d32b02c19121921450b234600c508275e51ac29432223909899c2d60c898c18228527b6942191113e438a48aa7d038980e0402bc321352c906986444c9068b083ec8b21542a08c328096900d10408d90b8260d37082524a29f5a10645449c0f42d0807641240411911055e45586444240c9c0061be76bd958637422e67863582d43a21f1f3c614b39466984221aa29f2d887e84b2cd90e82708099d26c020065460618231b6b002762101318020c6154a88220650aae813642ea0598d28d410418449b135c321353a60a5e529cc73d639e9a42d3bc3f0cacf4b7b764398bb7be291a452ca86f204310a6a6db568b4c9df12aa840f980fa9048da22961e3db4a53ebc369b610c6186b8510c28cd65a82b07ea74bf7eb2157ac69a734f6a8a7771a654a9f4d082b841aaeb7f488187494db89510c3a3a12231e5589c10e9c32d4ca964a14231e9dec91ccc596b5167f47e8e930ebd262eded6157e4845b495aadc3c0e9dd5bdd3bddfe74b256d5a24552743b9db6abeec7ad4edf4eaadbedf6ba52e75028570a0f3992ed37fc1dc991db56aa956d657bc2495a79d5b276b3276b397bb2f69e77ef301175d90531ad5e254e8bea36a53aac3dfad445fd645d4e27a96ab5be242d7b249fbe42a14ef4747a94cac4fae9aeceba4356ad2399a6ae32e1242d4a8f504a69abd59242c3913c7f4f4669e960043a20410ebe324116ade4562283f84bc2e5c943f6194f5e6997b48361e2af475e6d6e938bf2ae3e6f5744c5624d6b97b4ecbcfac7d2e4ed3798b5c7169c211e8b1aebdca7655d6a3bcad5e567afaaf638777ade263a224a86441e18ca5ff421f2405046d939ef1019a1c6d98b8a1135c48b11e3af872c53af29db4b7872fd112526539b5a08cc8d75e0c94b622e9d967209efe4d4c07ef028d325ddf23e7d9276a1e5f44794f0b0b9b0d8b05fcd137a347449cb9123fb459fe8d2e28297b476a54a9c167c8408fb4558e4431bdb108158ad830748d954aaa1d6fa392b2cbd5edbd0b0255c85fd603089e3daaee5489caae10a68e7b473d8b51d265fa68a2319b0c6749369337da7ab15ebe1b767fae5c242261cd668d8e9d4116ce9dbe72b761160bedeb56123f334ccbb4c97788a02c0189379790ee7c897e93058637a16b604435ac24876d305c2f4cd74984dcf3293e9f14a1384d2643242c34ddba0098394601f83ba54a511cf8305b5621739d6b05f0705b9b8b05f0cb22252d12e98b029c3a69214b2aec0d22089bf28f412d4d2500029cd32186986a70eb209547b2624871cd8c6a15a39270b8311c2881f10a3917cc9d2e3c53da55f42edc94fb902cba47fe44b66b87ddab30200f892bf425a6e47ca1848338c0e2437f87a81a82fc5580f733d84b53edeac529afd19968f6cd8cf47218993235ff252ca8db2b0618d5a48ee0c657767a8a83d9823cb1af68b463627e7cb89460be897bcec97b4f25808b6d7b0972b38326219adc07eb625676715daa8264cc861613f2e77e9f51c9c0a308a9beab36f56577d536587dc94df917d6b3abdda7b755557a6306c0f6733645f222f12ed11ef40b3499b37562b1c93a53a0baa57f2f3f26ed9252b6a3ad4bcc34b86404fd62e85c0ace1782a6fbab2934175d3a5c49bfdbc29752d85e54d513b2755df698be52546a2ba09236149bc446615ebf5dd11c95818ce4b16d4ea73b58a744523356997a7ab29f134e16fd219e349c618a3f693fcbc277b59f1d7d3b7f6a68baacd5a5d1586556ada94f3b0a39485e55feaf5a6b7ee57b36bda670c167f3d64fb1bf47e4a3255f19055acc3c669b1f0973afdeaadab24abf08704667b61961d0c31b3e01d2646328964d7aeb14e57afa7f67e3c6493bce9daab6eaa3e753b6b37c99bba260cafd99fbe25d2528c7a4569d9e3915ccfdd8f875c67d681874c7db2e9d9e9ac37ddd2dd81568c645ec21bbd7a02deb8c2de6897da391b141081c01ca3ea281c2916d1135572e9b36bd7ab7a765195e5d46ae138b1a42a4b6618c95ccd194a574d24594eaedf2267fa2667e3c0588643eb829c2e0fbb19703cdedc48bd85e5b55e53ffc63c0ed527fe9278a8cf241eea15856754c9d093f56c62d7eaf328d5b3ba425d7595eaa2eab74e6567611b346c868ba48ec2486ebc9378291c6f1029ddc02d38585866df49eb1b3559e6e3a90b72fa9ca8393f3360513f1d09d667f774d6955d90d33728a5f4b2d56a7559575d5befa26ae16869c1d1ab54b7565917ee7476d865a5a350cf6cc8cdc27acbadc471b93cb1d81676d9e17ea96f6f8c2423523a0a6543669d85bf245e5f55cd70afaeaa73bd5f0a73a89f90d4a3bad6faac7ad945827ae7c6daed4e579851f7f452a705d990b598a9e6d26737a41a3520a7744bae971cf339ee4b75f984e4093d221460828a10a0e4d96f400e8e6ef96122216501e3d1e4c05e2cab8f40caf4ce6d6b1df6e7e4e453a491461a69a43021763ba158261c2d2d2d1c374c2cac154a754ae570ddd01423aba5595690f59de6b8b3dc387545b673f61c8d34d24823a5f6f4598e7e3b6d1cc77d5ea69c7d8a3e753f2179bbbd9f907cb2dc763f7a14b641c352ccb19c4b92f3519c2427af72b8961ba7e75a386e64f6593764ce701a2267e04ef614638ca79310976ef58d863de72454876c9556d1234eddbea45870b4b80ea5cb057fb505e3b8815930cbf330ea2f3939a7d3e92424c7c60606a3a1f13c1898974c5329fcb5562f2f17b645a55eae4afd2585ba769b3a9d8464d555f84b92934f9fa7f77e51084d5d6b734e54488ea739a79c534ea6393d14010911410e0c511221c892e505dd7769d71064e8eeb6e96e9a39a5940d9bedcdf92d838778d2b477f9c93cbbbba59415ce43082184b0b1b0f15edf05c3cc9d638edddd52ca150864a073ce49e3e1acad496e42fc13b249a79c3383c910692279341952da40e69473ce09e19c734ee9cd39672c452a71b43e73ce6932ad86a8539bb46693d60c0b4d8cacd6ac0d3a6bd6c69c342b514a33adce4cab6d64339b5ae50e4b39d59229a514cf9969b50d9ad579941dd92c7bbc966571ceda4696c11e95873933b9711c47b336b6d66446a306bd5a67d775259ab55192198d1995117758946a7d9c8f5aa4996cc3e462b29964367358b0df4c6c3dda68464a6fd9c41efdee121482d58ce4c4d2cc5a3768d846ce49b312a534d36a96d1c09bec309842f7ade3b49c1cac65da2cc5eea62cd8a314a7a6695d106d562a618fd852cbb21964ce88c879ad0b5232c98de3b8ada78cad96a6698f734ec945d3a4997c74c969c51efdd3a90b92fd3425ee6460e3b78823ac291d962e21ac29cd5caa9ac45940df007bf47510d0b2a1c8b4997c5c6c6cd94b86bff0a646afff132b371b6736246a398116db0c7e3a289c0c32ece0940c5b9e6d6db0f863b9521619ce3bb3ec5ad66d935ca9656545412c323c4b140875e0155d90599a32058532540520c3774d562e4c6929b2df64e9b490210bbc01c4494b0cfb554d27c7aaed08800c9fc30cfbd5b354605d707060c07593e1cb0a554c3fc86153e558c0bef999f4c7cca6f853653287f98e0bbb212fece1af872cf3ff5b22730a7fcc85d94b017f3d5906c73ce6dec3dc2f49fe51b81f0ff99f401267c624fe4e6f18dfcb7b7859f2c539173604fb4da01374c49e03c809bdaec7ae4b629a14a357fad52f2161a7933b86cdfe4da01a76dec265574300ee32e2c2f333e6891828238a50cc85f7c476e4cb85b9850b63aedb933f28d4ba9f975db838e251cb40c3e2a359dd8fa5fab4271348ca513cfab8fcc5a389c50368fe68527f60ca03a894dffa7ad2480b6d5c402816007c172e8cb57061acb5b0df4b3e19a2d003d703a45f58e2b90f757ae4fcfd55582925cc95dc3832347d03899c90c102bcf92a111368e43e0b5e041998729321511257f2869af09008102726c199675c02c4c185fd9a8ceda6331d8d3c3d2aed75dffee263589c21911256e4cdf428c4952ec5e50a4bcf4deed18af6e6e9277b6dd0b0d9b9974ea58e08fd40760de17014eac9aaabcd60061f108aad2e8cd13c69c8d917d608eaf4c82854de2e045ff35ba954aa37a7944da1de57ec65b5e74a61183aa7acf4913f4aacf0a489932c63f3a97b031068dc1b7ca4dc1b7cac189aae2954faa6bbe89b2c0bdbbdd59948891f64d8d1c0e43a3460fbaa734176dee83edf4e248eca9a7092770343c982f1178b2e5ff47da13cb5d0679b08590e91e74e9f2ebbec27eec6a1f66646e9bbcacc0a2091124ff2cc3f4646454748502091123e793e5e699c199b2c325857a816167633c8dc425c5724fa904df1ca7663924b8cafbf7081f5fa2f096c521c457fd13730cf2efa665af1a34a1256a41c8a55da9b309b88058c859cf626dea13312fbeef371a86fece72395be41f53773f7ee1bdc9a333d93c25dee54a8bd69cf5dfb93b53826a16ebcd2836c833a3023319dfbec21cfdce1ac339dbe9ba1c3a58b8496cec52f2ea1444edd3324f45c9472b3675be4b8c89db647a510d93e01a3bc3d0bb97604b0c66422c385c0a13cdfa44a864419a0424786444d8272ed23230b54f90132f1404a224a1bcdd9423e453f5434d013560bf58d8b0623edcd33c9f3f0eb22f2bc227f2ff98b4938cc17cd204b398b57fac5a47f7a3b71d95dd4549a79bead50c9f33d7718f9f8f8f8008990b022e94a9e9ffcc9f3891a59e44f2a416f2a6531a96fbe5894e75d70664279beae31b90d76a54b848abec97a32ddaced8264575dd9d1771d91e893296e9f7825265161a54fdf449f3c332b4d46dfc8f94eea9b2c0679beaf40ae0b925123f8ea8250a23272868d1660cac1a8e298c48124141fa9c2da2e86ce3ac4256425b0665e754b5ff0f0d0de844935f47970a804c10ba1580f2a2d84388a4d91447b16a970451f1f2aa21441e30fa7ba5b1a484231d8d1eff4eac258a4a26f287629311b294f3aa393f6e6a3933cfb531e8711c2f089b5d1eb0914d4c9269d51e66b03e3d1c06ce8945067c6676159edc5576d6641676c1b9d53ceee081f4307598cd0dded75c37477b794db0fdb766d64f2e7f23e578d5b6c9b9492b694b3bb2184d0e64e09d31ebc9452ce9b29e74d7777378b1933c6d8ddd9c60454d671db35a5a8a9b4954cdbc695b614544d2ab1a026163060177a3108c8b22cbb1a8dc0d4b030418ffef480d2cd4c3b64f938e98dd09b593725b69c34d36ac984ca3a6ecbc1d40d39caa6a8a9b4954cdbc699784add90996ab5d412947153c1958eb449b5504a0f4367162fa66cd8700ef98a93cd9291c5524bd544e965e9565abb212518fb5431a52c5a76ba32bdeede012b7f704a6b0651b4a1812a489841132bb17eeddb1f7253a2223090e5a7a4d99338ad28899092270df294b80cfbb5e2df3712a7c2e25b0bdb928d484404144ae1b4b48b4c7f8fa8064e64d99c19064174812c88886022cf4b0ba02053fa6eb528a5525079decce844885ad9204c865289f8eef21b0a36b0dde3eb86c21636c3b300e35ea49cf234f9949d7e3a9d8ef4cd119a4ef58f85b3faaabbb0bdee50da0c7f47724881adffea4dff2250ed2b9111488c3f59266529e31131b4296fb0a87d72761c72b0a1872f720f4639fb7a28ca59b7d3ed451fedddd50195d39d7d3be12fbba6e57acd27d767328ad4eb5d40e3a4b425fd2acd9c83c499b1aaa484848d4eee18361e0730723f8d1f2107e1d45bd256354dabb5d67af85a6bad75d62adf46d0d7d80a81e54037c9357badf3d5f45a7acd6a56b39a55ad6ab5e5663a91984b47b0116eb29eca6aba34d55a6bbdec82546bbaa9be845bedd563262c4ccdc0d0b22c05b046fb27a59c00f6d00e1b6a34b0466b6523ed69dfe917d92f067511500766ed3d05d499d71ead0415c52fa25133c9da95aca19135326ab536d33ad6ab430c30974e24e61aa98841b046bbe90856e62f0611a9efbeae7e4fee21f55ddf93af4bf3b2a6bd6a1046c3483a886eb141984f46d3112c15ed49813a11eab4f27c4ba9711e28fe54b15f0775901179be89140ee4393b0479ce22604d0e1bd8f8af6310ac99e768e267add606750e1227fecc39e747137fe6eb5733fc593a23eea22b869d8f41918ac681735a213527991af66bb5e47560fb72f6105bced6b8cb69ca696fe65285cdcc46515f7e1ac9043ca49f33c2181ff19c734a29613361d30eafd596f059649b854b1a47083d7d292332893fdae96dc0a16ff2b6c939bb66edb65cd23b3932c86e4913994c91357cdf896ac27e1d254e429327a641c80de050922349bcf6280d0f4f90c6dfe4c951f69671a5ef745f54910cc7d64aaf9f37498b7cf759fa2cdadd8dbf1b3fd21569ccca6296eab3342e229f1599d73eb34f9361f5794de2c8c5db1fcf0ca8f75177a1f010ab952c863cb95fe5b56f99bc0adba061255ed2a60c23493deb159e384bb57cd62d9bd5b1e3e4ba23ab2fb9ee2c4efcc943daada4edbb7fa4af927e92f6e83b3db26b181b84b19fcc9f39334a5bbe4f46c8cb1b833cfdb431c3fe30899bd5b2b5c4818e4a803929aab0cedbb9ca93ce3927b637d0c70b0ff207f3923f1a14963841a66f7d8c4c899cb8608923583c60bf7a030d00654a89342094e9bb05b1408a52831f2b404145d91414f06056cbe5d01c019b1865589eb9c670c6457c10d30ea14f64d84e296c1499cfcf1623955fcd0da5644d395bc258fb2666b03dd93489ad5619bbb9da77e609a314aadf44ba34e37433510961686c5e28945f6cf4924a29a59cb2b1946163d19523c8404198ad8b36f1944a7db04e75ab92f1b46948d8fa79170cac2aa652e7696197a7700bf3a45218d2ae95da5c6e4fac757b622e41628ed7aab575f26a0efc822d8cb52e8c9d7e52dd0f16a9345cbd7e49a011068a98f205ebdc65bdf6cde95bbc2c1dfe7aa06caad4bd9cfa25cedf72ed0ac75d375c5c386e6017015a7f790d2ecf812bd07297bfe00a4c61a4e52eaf80ebade85dbe4e77b956be4e6fb956be526fddd6679363927853a943a39cc22cf5f67eb08b7c8268a4bed35beaaa3ebb20aa14fe6656d9afaeebf4d5a395eb3adde52f6f5dd7e9ade7b8ebba4e775dc75baeeb8461e051866df9098f462f9f393e1fab481c1d9fad4f97d5a7eab3e5fafc093a72bcdc54aae59eee725bf7e3c9dde95d27bb225a0ad7f6ba4fd995ce42dde20f1a65fb6d83028bf2e9f5d6bbbc27d6faea56e2b854980626aeb7bc75971b69a9cee3c2aeee3d312117ccd3825ddd7c4f2c5691afd9c29607106c8ebffc84f3205ff3e799c288cb5d7739cf144674bc75285f2d98670a232d3f01f3b4dc8579a630e27a0be639825d304f118f10f99ad7818f84fec12328a70bb6f2650f632d2acde578e9c9b09c041659d4d5eae95b6cb53cde8a0c893cf0247f3143220f00e59e58cbbcebb6bcf59e584fcce5f6c4b6e65cb727d6727b622e37c65a2db885e3c9f4ee96be23bfea1a1291588dc3044c78518426a18c13e209e50d7cae0fd7cac9adcbe43241800f251f175a38fc76f9eac462219cbc850b633344171197eb30bb9eb9767cde35249b3962279c4a29a594524a4c23941c3972e4b8cc711d394eaf0e281ddf6989c216f69b4a344eec32c05c73d8ed38cd611784c7bb570c8cca9c1e7917c9eb3043188cb91f4da6df646ee797fb45a21f2b59c7656afa08befab5f6511fa150a878b4d91c7641fe83dbd1d65a9965be513222520c71f3ae87e311ac690e8af63428da13584393b2271914f40ad499aad3a45bbb6dcf41c57eda2aa35264bd7bf8f3be09e4e32426665d0c32cb7ca35b0e1bd8d3e351f7f2ed41b01f4d0282afbecd5b2d2929a4d0accbdc06aa3505e1514ac66ea35c03f50d8c3906b79356a2bd0934a5f44d6645df08411deffd7905d489691c9f4050a7f51f38fe1d078e7f15c7eb61c4a142f2d527cfe5e3f92ddc85ebf2811f50c3ce33761969c1c7835c88cb078e22860be02f3cbf2796b1eb0139a74236f807feeab1900bc3bdab09c19a7eccd5ae006279e1c2f018ee0b177235ef76197605e8580c587bf2c2d5a0c070352903fbd52a4faa6d5581817d92dbc763de9f494420548dd803ea7c22017500a09d51b54308849e71d8d5b07397911682e0d9c27b62d06524ff85bf8079048067c62ecf054304c0231b30108a452658098af5c4a6d88f266dde8579fb71e58f1f3e6e43711bca1be7eb5a46284ffda4f336b95f16eee30279ccd5b97763a645126702c9d71911084da26a4420da13ed45b4e88cdcdf00c0c2ed27b0a60fe43614edf575f2044aea6b4f2290ee40ecd11a4d6a0ec09a06c085f9476fa081daeb9e026b1abbbce7bb30f3a556f40afd88a9942dec179de0098499b03782e0a4f0afa270085bf986cbbf13f28df714a813f3be975957e15f0bb30efb887cf559ef22fac6c7bdb7cdfb50e7c76ffed9dc7d857f35775d8f7f9d4f873918763504e02e1862f69ecfb34d198a65ecc289793e0f2c0c1c14c5f25d784fcce64ed81eec5bbe3cf18735520014eba19e119be2df7e7d7c8b3f5a602294f3a54b10e113f37161daa38771fda211c80d5e017f35f7c03117079c041c913834fda277a187c1f4f42908d15f8c8f54c05fabf56da3dc0b7f31bfa4e7c93cc7635744c765300c33af8bc47bcc3bb286e63cbcdb93775c9867ee47e3c275d510801674f02314e371e13db06640149b42e29c980b329f919efcc743963bbc1999dfefb4f43c997fe6c2bce3fe97b95f4fbebfdf2287a5bc0f791e314efa464a1e972a7c02f9b8845d7eda5ce2efc72790c4d389777aefe27bcf3b4cde07cde58ecbd3ae08bd5764e63318c21a19f997779a5f7632d47ce6927aafe3d79534a7f7bcd72576edb8bccce975edf8cce90e3c44e6339f91c1de77a4773f99a3e0d57ca729cdf13d8ca4e6331809ecde775ae62e91d9bbbc5f0d7ec1fe7747e4de7bec86c03054012f91d9c7e93dfcd97c3ff01299799cfae0c1437a7f7d7b5def255f3352e6de5f3ce469cca5f45e9799c11f95afe9c48977893f27f2530999c71c097aa737862686493e8f6fb18361067f3d7966077ee11d352c9acfdc14bcef348ffbd5fc7afdf5172eec155ecf778a9ac3ae480d7efd15f37b1849cc656264be2369ee4e7ba7a7f9eb13eaecf8ebf599d35cd8deccebf51e3dfef1f83ff31d77c29a99d7ef7d7b5d24b07b19995318fe7ac8b0abf099fbd93c739afb29c934df71d83731f7b1037f4bf28ebf5e7321ac79e18f27bf7ce04f490c0ff975d8373f6e73087562eec3067f3ce41daff98f0bdbabc19f92fcf2813f9ebc037f3c4ef39ac75c086b6af0b724bff0c7e333df9134f757a1e630c37ae01d62e621e36124f432f732f432ff8efbc55c739afbf19067bccf5c9ad7dc1d34f7df71fb93b907bb22afcfdcbb1f0fd9fbeb4258e37de6e55d99ef38cc01a13ba8846d979435eb149140000000000315000020100a064462a140249808bbae7614000e7a9c466a509c0bc44112c328ca20640c308410020800c6001821a21901b02068fa44c7b603af965f4e0776b3e92439e6ee0df3f9043d0ba2916888a315e6157695466e2f4a16c7177c6a1cc7c508d3950d20e02481be892c2660f0d4ef419fdcc044cbfececdc2511a38f95a2efe2825a2e2ed5e42266650b9a123184c883a5985dc84eabce2305f065d9498a27bbbecd7322f50b349a2c92f2e591998dae67ebbb9fb5a98498346369818f17daa06af4ea3b9e2e3ee3d1b2f9240adec3664146f8bd6d5695ba63485d14ca530f552cb4c6fd15505d5be01c3bc03cfcb9d94e9788e729dc460dc8fa174560f28a237f4f44b96f716055e376589a1544f00059fc8abca1c349aba8ee5ec02610197c6f217aa1735a350b00aaec080e719a366ddabdaa7ae547d03a1419b5dbbe2c0c01dbe09070e1549ecf5f3cb5f819cfee76ae6d199e2757e38286671c5e0f6022810b3f32a86e6c3fc6a4e144ec95162ec4bb6bf2f85ef10518b454c17488d565d4221ff5f65c52cacfc016970e7b3140ee3e25f4865c7613e5397cdeb5c4db0ada37f9bc309934cb518f52ed3ff0146e557ae35776bd7e3deb7018a63c34eb7b4c1ac7d263d249e85fd60c762948d33bbcb8ce8946607312a51685b29f83b344c73b300da18ef91478956b4fd6377205d8c0a72dfca52cdc7fe47a5e80415542a9c802c4f94d679a9716dc78ae3f102c1653b2adea0e183061f3752ed685a1d88e2bc25dcdd565f0f2a1b81964c5adaeb4266dbe492c2269614ed8c80b0fae7642654bd006fefae302f749c2c96a2d873b9cc1786fc2e482c6326fd0c2e36e18618dda6b4aca0597a408a3f78815d2e9a00590a481de70484cb6f8d6ba4d0023d6b0c22a14d5876ee92f73c7b703c28d6ba62c4a9beeee1ec4a3e06d529de1c31e19c342dd879ccda2ff965a389b5c5ca79d834cb5fff56df25967095f1ff54a70d53b8983f5dd55de7cadaa146e8916d286eb4833f1d1a123bf24e39e5db4936262573fc28fad5369a46bc26dec02a0621ee02b1e13f7fa5896ecf3609c4c8ca5fa93695324f7e2d138916cf3f7d57d070ff0a99d71ada9e80ac8b9cb366cc399dbf0b8ceab1f55c7befb385a686eec46363e992967f1ea8416963410bf4f09ca5601112f1a0fa208e779d3f869a4493b048be70835bd1f6d6480cdbf0f75a27dfd58dc3f32ada23a9bd1c5b1ef15fa442cf400bedf203dd468bcb41c911a33837d941f71a418a2a96d3daa7e235fd29e71ed169f77381e86b1b2a5cd05fdc0896966360e1c621ea85e291ed2810410e98916b25f054327e2d7a5ed50d5fe8f5f3b5a93e52d6a6ae004b79c272918756ecef181daeb60355d748f975a7bad227bcb252f700e5cfaf9ecdb6d44b17144c155e2ef25f6eefd6fa166ab5fbec9900b17b4a9ac4313f0ef70c1dbe26a7469db161806c76f41e4f67550a101151da22ceac33fdfacc80fa9db3da808ad6479a05af102851e1e1b840c572ebee8f06eae4a6fc0eb4ac65f7c93144d98fec669b216d1b140b1f974ab36eac82fd0e1247b03bf2492b7237e6b7412b64f566d79c3ea4ddcd0e41013ee3a51d10af92f9a2771245d1d39a4c4a1b8858dbd38402f77427f1fdaeec5575d6dd6c5fca8f9a092f760863dd6e2b4af47e5f8e6050396de8972cd01d24d2bf9aaf82f4b1f9b9df5f217a21f4a22244b7f4f0745a3d5250e36ba70c5a592832e39960c15793debebce9dbd577826d60755aad603239865e8e4cf58851cc09ba5c8e6a2581ae77dfc27739f94d0cefd8e5c96bc4e10c3b253c4881138c0f4baf9f441e5c16a46130a90125618ecbb923494a6c27ad25e0d7c683706c3b9fb186370fb4c813e580ab3da85243d3b67d57b34da8f1b473b214806209dd225ecb78840b1c15cc7bffc78cdaaa108af8ed90e48e9fd1c7e6184bd1d328bc8153ca03bdbf3c2de2c50b60b5d06ec36080295bdf9c1c01fba71ffc84c06f67102f109780e0aa37f88b7c4e105db150b328c4fb0daaad6fad8368cc0b38a96dec46ec059bafcf0c56faf5d683a41226754a5ea094c6e05dd889b59bbd0a39aa8f870af1af6c19575a83f98d013097e00f1dd8c1745a6588e4b5c8fe8cda2b5d1e092a7923c62308bb7b14e8c8de31908216d89caf7a5524a030100143944549212aef0b9a0dfbbd7503f78003bde88f82d2ab8b55c555989d7efbf6b3ffbeeb98e8cb23644c0b5b9bcd2baddf06f1798d07b484cc6a6ea7f38099f162393b6867c91dc844cf7ab68e200dda96fa22b835aee20a135a349fc91522e4939b0a2248fff9b982f7720d0c4a1ba5a004eafa3c4f48776cb6027ad64715d879259c02660d2cc768cc4157d78e33a7c460b270cc8e5f372459b094dd6f9e36dd99101d1a3ad0370806813ea517d860dbd89995a2ef3aabafcaebcb0b56e293ced9d4aeddeec9d84856c52779acc58e39ee0d223debf7141e440ea059713e20bccb8e292bc0f0b51252ec61ca8663581f1280f81c245ec1cc7977418b4216fc57e90596fd21f8ea9c76d72921bf110a570a7d5aeeca8678de1425c72a6fefc3c7d526a557aec32801de6effab5d326c49950e16a56915182b2bb77ae37a2995ba27d7a5e7dc4cebf07804ca590e512e6f0497cbc8bddf992f8351c5013befde93cf3ce1ec75530b77946a453ba6882e26f13fd280c6d2c2a9b941e8ece17a4150f12d0a7861796e4461283eb77ceccfff0c3e4119dedc9785e19bb5b232c3f750967d825c20509f3090bd2b8d5592d298de1bcaba35a86998f366f484e85c9f790aa64e1161b65e314c551255ee1cc975f91cce014173bf4f6393e67b681cfee8571c3c82fbe7287665489203ede7879c856ac0ff738e169c68c47ae597811fc1a97fdc31c8ecbe470604d8c944003875809ad1f07001d117d4bf41857a92e9257b229b5f0e3318f1ec45b2ac903f3a4572d2136d37d2e0ba93bed7949f719b95d5fc55e45163092bd1fb729e78a9157299862fccb460d12b24c9edd78512a725aaafaeac63dec0cb282bd0e7fac500d7a9d9357058482800c6f5de20d0034bcb860b7e178165ecd39d90e1765bb39e3f82694a29ea30589469ee19a8a3af6d06a1edcdb574b8238b7d1b60a1ee3b0b02a37db4bd5f068d36cb2bc070cba265c377010af6c8669addee3d1d16a836e666a2a1e439307d036be7ed505033db9dbb60918a99360b296529bf9fff472ff87dd20283145cb3452e47d540833cb244291429029c3e71855ee3cbce409e16553e1baed26a875957b7270910a90ec1e021015e6b8836af1e01bcf6db986d23800d2c0b5edd2f9467b2e5fee8fbfc4393f15b763e8d2d9b0d1c5d9d9223cbdf06ca6dfaba08dfae1b9bda5d35eef451dd920383a6190570c17d03d64a1362ade067b8e975165d3825983264cca8a17eaba7dddc8a3b0b63bf1cd0cfd4e28f2b275a0ca6650d1f999e4bd822715c4148cbec4da862377a0679ecc77bb38b5ca3082c4446d5af019ad54a74526c3de0b0d1d637f3ea900bd0f1e776e66b57f97a80a3eff39d57c45393bd6a2ced250544e518cf086299b6b5f296d4130c9d9e80b9ec55a27cbc8680cf4eb0b55512489908ad637368b41bcf4ca700ee22c84696a5b73ab419913e9b3b64d6ec5603fa793f7fb4d24cbc5ada6f707b029f8e87f8f574cd1590b0880296d27446cbd45a38f532e4abb51621b40aefe864adc4ca0be1ec84581f2b0aaaa036e86ca1876bba5a30556e50981cc483375721f0149ca674291607e98ef668094e200ac068fa3bad6e6d046d1ea357ff37f743fbd16408c3e68d238162ad19d0b4273742a2c842520815cc3954bb9b2e0eee5ec8ff8a2eb86463dff758d25182754e764cbac00124b950667135b4e9a5f1b66eccf693dc8e3cc8ba6709a8ff8c5668af6bd15ad455dfb8b1cdcd295c4ca9937db9b1215cc07faf0660a4d0e9ab56fc481a6482e17492508167ca0041b812cd62b4eb015061120f2f775a1fca969af74d37da81a5bae7b8a318a6260329924495bc617cab2942392823ef4bfecb5de493a6162049225133400d3c10c815a0824a8b937de7fe7e836878d9a13bc5cce7a15dd2228e04148aae1b38382cfd7fe21a922fbf24e79515d01fedc6d677c81caf82c76976e8a55cdc40054dec6a240019cb5180393b207e357076f05cde25057d3fffb7181a273dc280cbc4260b7cc1356b76e249b759197ef93f1e3638f7488af8b46adf11bcc19887e8fddf322673de1c7bec2eedf12f31b9ad0a3003836452c572b3202562c8e82cde5071ce422396c10ebb4a6168742ac850a430f8affab0182c4130484d95e7d199a629c8c8cd73062c2661c4d143bd218c32e2cfe99f07923d6adced813c0afb41d836ef6d6e6acc09d375a63a1607f0340feaee97def24ed69acacfecde6dedae6eaed93b15677143ae87da89da0792582d9437a7c23be25ffdcaab5985ae65e26fdc262683c3c0fb892519ac27549a32d5271919e4676313224ac2755b6132e280956ccf6c6d6a6ebf2072cde802a3b5b0008372c4b6edd349408e74106f5b222d6136f5d6add4cab9350698107c1e026f8ffba6c4e75f070fa84cdc2ddb1bfcefe156932bddcda48abb79370f3b92079d35c9c4fa50d24a344705b227ff5a50d2e83c26088526e5f36745e30d1200a407a0147b889d7f05cf5825fcfb53c4c60e3388327a471fe42f91dc4441349dbe205e76e9c993feffc43864c591212b019558eac49692fb46949e6242d63bc8cd57b5e14d0170c27c569abd1cf025bf65a62b86f5a6340b4f03e265fe14ea937e51715f4314ab830239220c504c15615a643da015781eee3cea4a943c7f57b05a409245b93a1985ba856c0b50ec4779fb3e55425d4fa3defd0c5bddac73977a50543c5913b672ee039fc54e3dea3524caf3b7373dd9a4aabb657a603293b800b6e87d7b38dfabb00eeff5f6b28af5907621c89e5d82b6401403aef75a7de21a7538cd39dbb3c69b81cd452e8bb1e510025d04e7358296edad4e258137a2146c36a58731a9a9bfffaa915b4cf3cd06329012428317524330812978d47acfdc3359eb2400a5898668ce2807413fc3ee2570359c260d4c487debdd38bf9f07174f7206f49e23dbf6fd5a8490f8bb90ebb97af23ba7373ca85e8fe1ef1c3ea5b5df1b44952b850b67d9f68b83fbfc760b40357cd3eec28f634b25ab5c358176170cbb1c00bf74d4bd3f7f3e1ddee2417c77bcb0267d55db500e398c3cf6a05407ef0d03c9b04243cae235c4d319a76cb26e007aa40bf2122abe7e70c1a1d3c308a2c523a7fb4d1a351664e9106cc2674255f7df56e8256ce2dfb03e8079dd127b5c9c36502a9562f163dc76840d594785978433128b9b3eb835c2992650d156763670139fcc2768e9c2cc7056c7cdc09d8928f7b26ce7904024cd3a34c558df59368c563f7c917235182931c448d523f278dda44f0a3712a8a5ddb87809d2d72478bf5d1b466ae39f83c648959e191173d04e6f42705d6a281a6bfaba156598f58034359d6746261e1439e3865130eb07452cf218b39cc5334857ceb5bbd33c2e35e7090149de3bfa9c5340b9b646e817ed0c5bcd0f36a3cf1650b2490ffe9ac2ff05cacd54208e80ad08e9dec17358093dc7eed1192173e62b593286525b865a646b09266633336c06bcc7ca48504094d935cbb6290e1436ef075fa26db2902265f3d46b92937d2c7e1474cc8a4b5771279cc7a595153baf967ea9a8b50c84b2eb41e30e1dc1d4d99da4a5a8ad4c1caaebcd144ccb6b3846a04079b56e73e02e8b15d7d334d8f40a4aceade42e3333d32de4a2310b14f1a3b91cc6007b42f46b25a11f4839db46db88075cf5c01dae1af370580c601577ba06bdca77e43b2059a15269ab2c48f70d7d137361cdfaa5fa53c8e6292006722b63822fa0ca3b6be7dadf4b1d3600c70a77fbd24867202940b735c04813dbee21bb1b94d696eff9d7b9bf0168938580eabd7b2ad8911de8949511bd92071db1d0dd4c80f95b09e78f637f06d112a11ec9fae82a42150d667b55a4c03d8d61cca31ec1899993ae5a66bde19ed685e210fbdadfabde97ff5e01991a81661a227ba94641bf9da21095867aa1579a468f3fc187dc2c7023a7074548ac090f68cd1926d27aaad2ba709d2ecaf13d015787526ca3e474016e9aae3c5950c2d581805a1202cf5ff54e16a601d422065c4baba8ded193d825f3e9ed84230d7643e5b94505a732336ca7756ad47549f7cd06185dbf17312c0a285cc36d290c8e545dd9f63d9a7ee87b7f3d761b781cdb58abae61ddc99c5202d804353ba22c53cfb79066e2b4efa603fa5edce43bb5f4beafd3f0469f996ba161a78c893b2c28f5da804f84c0fd615941028714f3133c6692220fef1436ce0fce5d98a6aa38c16cc18a850f2680326aee68ba3f8d9d53b6cbd69167b98b7bd35e992d88e27813980f32218a3a66a0ecbcbc959db9661bb004c98596bfb91a61358d87a099b3aed0c4551a5cb42041149f101aa2e471818f793feef44f1174d7555b69179b7571af375ab28ac8f8f1a33e31c41335a8a18d8cfee8373c7845d5cdd3975e7350c6224ccebd87c3e70a81ddcc184c8ad6114159be29ce518285bdeb5808fde507751b31807335727d4fe4cd95cdfc384f6a4d002ec60278dd209405be92d4259a7e38229f3d50980c40e8e9141d44e2592c092d11d63f0360fc83232b6c074b7fafa265ea498d337ae6ff9349807e688668b5399d690292931c86fa51e33ca3202a9a77d68a9cb326945c967376a97c3d3938ddeb0952a492eb3b16652d2d473af01a4b0027858bcf837a424f989081634bbf7a11b2aac11f86eb03d31526ffac2b82dce9352e8b5ba1f26aa6d477b641a59ae62d7af011feb251191a627382a4c3e2ae2d8c05c1279439ec1793e4081db183ff46569500a9157f3970f642a37dc79bef4ad142533d43a0a24d75ebf7fca1d8ea65b1438dac006a74e6d7a8b238c64e000e846e63f7bd08eeb1f1d3e4ed812c710205c106fbf6a7e2d29d03ee7cbfbbbf11415fa5430fcddc99a95bb947153dfb373db0c0723c299d7cb3673fc7db6e4ce7a3d0076f7044b6c39667bf435dc5cc346d94bcbb05960045cb5ecd32d12a29dc4f984ab8c199ab97e51f77203b22bec7795071792321d3603af129574be29994e96186e7df2f5c57c9cf9d4cafa34e47054261e2bcc6069f094e5c1e1b1e2cde17e780ba90d113b305d8d9bb5913f7f3e489c0ba1d9868f3b816b835bf242ae9be4818099c2c8e6dc6b17e31235949ca12b9242b1a05323da125b2816710255280f0cfa6b768463bc4017b82305bb6815095324ccf5750c2d829552d0c38527c00874bbb6d6a6a4ddc8a65392d3bfb1eb6418be3c7ef0b464bb3d42bd982cefba7cbef003e4cb4c0e1ee01cdda7a0298854012a826b4c9b61cd1d4d0351a0415a8d810ac275c3e365563d8004ee64455b13e044e1c8951f8b3d8f37b0fd057da759ecfbac0a724e6abe63d01c662df91d42b8d1ca2bebc0b9b601fbc0194f49a7ad2fe5fe1a9136b0abc104bf2a2056b15ac89bfa2ad91e0ea53b116dadc4b4467d3b6bfbeca01e9172618f2654c615f4c61523f7e0a7ef9ac5b0430cec126d63c7af3ef0e0173d0c333437380175f68f096ede2f42b81672d3fe4156ee26a6c8dd4f241eae5b2f2a2f7cd8e544cde8813cf45fb96031970ce7632bd0820f2bc77deb00477a490eb47e2701d83456b2a08c4cf3158c455c8e217301ad11101fca9c80a1ae0da80ce68928b70d70a6caf878bdad5736d36a9793d0c8f605101aa4818caeb61806dc6ccae7bfef71affe90c02583e6873a1e1ea05319952a6561f8631750ba03c55f83698dce94ae5a8c7cadc299fac95d54e738e22b0e88ce40a330619c44a720c1ba2a844a07d6655dbe62e5f40fac450ec50d9f22404c3b624275373288065db41ee54921d27e4be57e1c0d8bb01471d7149a452a8d0d5a731ad11eaa1c37652203c88d2bb84f393f3f59898e48518f0ef98d9d6e260fcc29323efacd3cd5c17d137939c20b20040f1328dd98e33579f201d04339ff4017c95d243df3aef5e566f25224311cc6cb073d92f584e4ed94d0265b53a1e0208a6a4c996a3b8fea42134c42a6bbe74b4545c31142fc9ff13a08b0c0535486eb5928b4681b92482015c14ee9ff7d10791b55cb8725a024ae8c38d0f449205d38a198195d0fe63bd72f6f408b626126c93dc5353752e1a3c5c591e9efcd33efc937b4f128f48f9228457932695c3cd7f27004fab7508d0b2d3d539684e268f4f5906eb96337fb7aed9a89eb2c1537f7471053b9bc4a0bfcbe488d6f75fea79d30dd1793b00480d8e8c3da3e32acb1269a1c0f7930e89043dff22ce348f45a3d783b239c8e22967a544f998fc0b06275fdd92a0ea1a06e1f21bcdb68dd1f2f782f8db3df79af89214f2aaf20ac040f0f7cd3389357ed953df2767015479284956ff6900536feb77524a738ff418aea4c04cab2bd0d7ee14d356ebbc9ec07f2a7dd0a7e1ab7a65bc64d1bb2b0863835b08420669ea0407c0e3d37ce99d460e0030475e0ba4a470a07903d82981787ed991c565652e4c4ed6c156852f305a749e46c241e848fcde89608f68b823d08013dfb81342acf7ddba874dcc144c10d8257d24a3bcc8d2a936d7978bf89f6efc4db972e6321b9060a8a59c671348d3d9a2fb6b1a6a7da31c5a58bf38e01245e297108d0db23e060e19538eaeafdaaf8cee62f1acde682e041baad57d55b2f4f10773af3bc098c0772e45f4e15f8f4aa3a7714cc5c6b3ab22fb5bbaa1b16c11549421803cbd486009e5bc2734f3059c39479b6e7a9cee54d42fa56b67af70513dc55fd823c2b29111ac96247c4b531478a82d7102555114ad92185ac0b1cced4b084bcb20c5cb8f27bf85738d7d99ec2da8f5596cb5f5c5d250148adbc8634a17e68fef426b25f9797a85c2c6d9e175e96258af56656bff9e820110769060218a383f7f02a9cb56a5200b0b11893dac17a4d84dd2de42143ed0807201cc328d97c1645d4a0c6ea9ab7b8f7e9c09d3f13b00ea9c812a860af5fa1641b171e032aff3c8755b956a38a8f074a79a3d3571a1dcf10450f997c4c3a46c4485dfc52ab59b1216b5428e5322824b24012066cf2c857f044744156537ee79ed3fc9a2f94a8b18b1e160bab61e43df306368d30594f25867e4b220ea4640e11a64fad3fc3f94b537fd1dfc1ce268d1ebc5be5762a03de9a10058c137f913ace1121e1d11de1c1139693aff947e1fe9d734244b0459dfa2efc19e1fdae752d6c53c2fbb945cee06b882210c5db15c132d2f1f619bab4a734b56319aaaa1ff10bc0821d6634c969b0b29fa06432f2422d1dfd82edb74e482df624d0721a88b846d28d9296402eab19d0ad6f0a9ddedba7221dad00c84c33f1d16715f9842b007c8dd7231cf75310cabd2ce5d3dfda69daa7ffa79ca9f23c1a64eb1b66c070b6f1c7b9a909d4cb830c9582c6169dc100b188f1fbae69450c80a319533d4f2efa2e1803250bfbc6ffa61216af8dda157763ea727ef9b7a5173e8a884f39ed250ffb64153316130021725882b3752a37169df64b16c88ffa1b9cf1b784302eff90f006f02beb80e0bbd760fca8a2cd6a235d85d9dae54e384484f1220ae12851b8514e69d9e6bf1a475a8adca139b8fd7443aeec2dfab20ff7c1aee889e572817ded39fdc9229e66cddd4fa8cff1c7bdc5e58c43f37f9b6b7ccc79831451c53b3e7a17a810a42d1b8322da0bd8df1aa3e12ef8f305b1dbf8fc5a4c54c7b360b3de04785e97f1e9202b361949b3c8aefb3f1da0ed894a45e254bf20d0fe716ad321ef782fd02bdbebf3f25b27b1a66fcaae62683f6c8cb951222c03274dfa0736ed5a7e117b321e3fe16a034c40517aa4386d4e1898d8b725f298a99cc4eac79074bc99c1ec31a638c52eeb8ac6f48ccfb245831c612e81b733585c6c0cca2be6507d9f05e52e7087c64e004a065c845a7d724e786a5f7528abd56808b91b04f644d54aa09e1e5970ccbc44a36616af429b902b2c0e3e461a6cd492c69f0367446c9da40c3b09370b00f9c72048bae54d2b5300c5753c87389a2b65f81d636b5f9836751b7e34be400bc29df146bf35e8f78be6ef5219699d05886a315acd0c828cc63de998ca30c6fcdb285d05322c03e7d7f7a2a33ac2d7f53d2f063ab9a360511e721d47aec7e6ecd3ca3ffe28ed76bc7ddb89e0a64d3be9073b22654564acd9f9505e75c2728533a9671a127113cc9256b1557a26b7173d614d5144f6f66157bc2ec287672b10b1d3122f1f028a79bf6d4e74c7d426d2dca4e086de05401b0a9c8ad7f15202132b1761682ec6ab4a0264ad49e5c74146d40d4b92880173d9576243f578c641d63289eeeb22eb9e12eea32552d3ec026e0e7d89035155b8d5e47d7b607eb2fb2bfca7fea2c81ec52f1182cb1421d057a9079a420ea274a93c7b6fafdd1d7dcc886e4c8c3479515faf485a07082d5780992f524a3a674ff01523ac6010c453978eeb06417df9c72f486cfe88e826ff24ba2738c39d218b395ee884b772e14aef9bd615bee5cc1e9e8cfa7b4b627367733e755c44ed69e2321107fb97c68b39bbd40e54bced37b16655271c9a6d575e433156ab80f9e2be2d1d1f55ad959d87dd4bbb1c33140088f927382363dfa681710517d850a1171589da3b5df4b691404f733070420c7e9bce730cedbec68f032d352d868a815ed7ac43ee5b26e0189f62aa67989423d8c0bb8240398fef46469b9d016e3642f6a976ebacc63a2b6de541638d8ee5ee16777f78a7b62ceee5e71277752faf85a78c82f9b38a0274678964e9f056eb843e9bd5b06c50c8236c71f19ee0dccb07b2cd311affb8b83b2e4bd9a8ca6385a1494726986aaa51c2959e791f1720263a2e3e1fa9b26be238ebcd524fef1443a5874a3bf36369b7dca90f3969d70b869a6bd68374844880e5fb849704073d16c0877b35539f94e6cde74b351828d87eb4ad8dcda85cb84bdf3d8623fc6f8ec2a623c2df5025c666ef5c637f70b8ea471bad36d909d54676cfe99e56e500bd3c984463082ce1c58dc80aa7e577ec9ca3f9c73a0582f13ab5a86cef34fadd02fe0935667515a2a17bd6146f0fe878faf163c0172ff17f46eecc87192d23e961fb4b711da473a2166ffb21d49cb647a42bcb09d98cad42f628f4bddd12d09fc8731322b9f45c7700ca2897761e81f98d03823e4344bae104143574ca3ecef34f0d048f48ccf5889e0898df0527f9032b0a4d62c094f23fe38c78c9d0b9ff6a31c6a73cbb06f09e16d81b28e34703d869cd234532a4f90a91968560edf633de1ef7edfdc02157632debf0c71108713b30e143dac30d5ba72a55027b9721b7b04818d46dbacd8c6c9b2c53e3ab73f1fb2232118f5e5e35353bcf59647de5e41347aa9da68356a6df0ea20a50f236bbafc3eeb0421e2832e6de699a4287569517aab185b5dfa1502a5ddf6324e3428c90e97cc5c69f8ada10252c49671b277a116af7666f6c649570e6933f58211d8d7f50b91fbcdc36b0b84c89f42a7e47d68d612920c454053d4b48bea5f94481fe43476c61add25f8ea9ac634726360dba16e49f002fa64b70db2232dff0e020c940c441e659b9eaca5478fbe3345b2afc961b1418f01f84c61c24fff478abc556c8cf9182e7421ed7d5ba3e353770f60fe5d15c4839f671ed2c816194d64a588a24871a59e6a168a314b9d714db9310d6f99573a27c7a17be654ac317ecc9fd6ef11cf828a71c7b4e7f62d3398a759ea00f9dd82d94928da72cb5fab68da11c2d21e64d23f96d1834e0d0ce8f97aea3f24cd735903bbeae0cbc36d66e3ea96e7f028d066361c306aa914119015c316ab6617c0f932d881a518cc22c708cdb77157b96dea6e8935dd7243c271045fbee044ff88336d1059092067ea51342678ba9ad0c285de1165632fda90d50e1b7209c69d3c275404d3ba2177f6eb63e3fd5daa5fee0a7bc8ee9a1a54ba4d7eb2358a2048f8019c16bcd7578bbfa48488b59b8b87a188a0ced4358bcec8d86599d973899c02c7e16357dcee2d54f7aeeedef295685a3d2aa2bdd8dbb4eb3293755c3f02b6f76166dfa9d94157874a8c48243785c590303d3334a8841abb29f287841b2fa7cbc9ba280de3883246a91ddedd47c67b9d013424a055c0c24f28cfe6cdcaeccd56c5c744b812cc1ca50a6c27086ca610830def270da47468cbfb4c4d1f4260f208d36324787cdbf27a666009f760f4d36de1d9466dfec6052f80508e3fd17981a349b40e06ef05400250df235f6a9134a257937875bc11bd55f52dea1ccd927c51489ed4e8948ca5b86a8315e913a95b2492e725a029d69a13cb646b044006c93233672c9a586a803109b6d0d208cbf8def49d2897b03ed20428c4e2b803fa0cfa37d435d31aecb57737e76e4bb76679e4c7435d2302f77eac817650f735f069730d6cf2cc64c052ece401cd22eb4e24f2f122a6f0c16f56b95802d516a68a017cfe9d50d00fe396885d529006ccb32864d1a8b3041c149d3514a1800609f430caa7e3775e45f629b8a8b882a0a627b9a791ddd3376b499bc5edd20c2f73b3963876665434767afa375e1535699096bd9b63b31e43a846fccc5a99446c9259ae26cd2ab6d046f1b40f136ad4c9085c20323959c775c575ff12a1a7f3ea338f991e65822458955ac38f9bba68e34235e02db4c68dc73a70ccf4cff33636b02f34ac21025084bf42048332e1641a4c6b4b11caf4187808d510174c997261fdd06a5a8a0443051edcfa0428f94c32db3e7f89fb140aaaec66e2d2af45d4f7ef224d488cc488d17a16615d986c76682840f6b08bc44e66c60e8b0c75e4710ac75354783c554631a8a3e0607e090157e3a476069d920ce3440351e175b960f5d542362d11cc6f10be071d1b4ea4de1bc554402dd9c9af70e7265479ddee7aa94c8b97b7c424e666ce5235b519c478fb450819519c2f1f1054351eaf23edb19092dc5b96b674fe8ea22622481730f3d955183b5b66d663753c19d3182b72cb8344e63ddf0d4da719d3cbe513d6f13ca766982648ac48b669072157753abd61ff11ccaca0922c0fb28790db39378143cde2983d44479fb9c2fe50e87e83f11078ddd623f0694bd13708784bbfbfc46b46c17e0b5fe87c210bf3034dc3d1a9439ce9004019c138be7d6f8ec5bc010b4aa7cf01ecadcde2477964810d87c52c609a25bcc1001c0af10525f9654138dbdaf25b24a58a22219bcb2640f26cc4a507aa8024ab04de3a07f34032307e1b8d11224080724be7bf4cf855ae2a95e5a6701ad534149e920bff64a31043b415cbfa7470091a8105091065a4af8c7dcbe489b2de24858aaa9245d8b287309ef17f86b1e570cdcfb01c7ec82a2f61767d1c94e47c5f0bb2c7510063b63298a1e8457e5ac23ba3726d85015ae83ecbd60b1415e9c94dc46db990325e022cca6de063ea9215a053389d7b49b35276a49325269d0f94e96862821ca4c655977251fcd7fdf6ba895842c782c2c89addc2f89a5026bdbb9f48e23d942273621210fc2d5046a07c156120e6b04926d83b593e53469c61cb736e6259f95fc6576fc78722a05a1382c7be2df1fbc8bbf7f7237d0c775af540046d53ec7247c0de200c70150f7e7fd5127390442da085bf0367af3ee2f89142a561ea05d4775c4463d821d92ddcf3bdbe60cc776d97339e25dee77bbed116f4da22da6a26e30bdfaf36318b528263d71a2d07f1db31e18421c5d6a1f02c6ae4c4ee723489cee042e21ceae0c3b1da6acf9b2d3298ddb0335d2f75bd9593006f22131c6fcd30f57411c00f4fb95a6665edf27979c6e911387be4322767c88844b431f1120b5fcc595fcff81e5c92a3d1f8a1a64df3e82722c2530a8f817d481d231590ffacd4420607df6ca37fad9196846ea5b51d62f8a9637c40ad852b5eabc1aaabe12a23988161c0ff30bb25851089d8641f11efd7e8e7146030800a750badb7e4ae18910b9c796c1039d656c806fb38b0a3477da4940519c800c473b1dabf5a9957cf236f97eee1fdc002700779783379cb577c7cb3b030cb9e56dc00fb7e7c265317d7e96533ba78c01b4544c1e7171b9758e2a465950559ac6e902574571b553e8c0033933e7750bab6e785ad66290e0699cecd744f86cc1e3a28a2a3f6e6abb557cec7613cc49c33ff13a82b127e2865005563a44dce0a2a114d523f24f03f4b372ca2c40f5203f5858a9d3bc6e97b31600ec94898bd3bbc39d76dc8ee513aa7588ee3b152790822963a341285685c9c8cd095020c74ff3d1378ff9ec6e52d80947f723b438012d01590c2d897dfd5cb778ee793dc5ca7d7e6e44da4c0d9eb2f5a5d6f376081cff9ba45175677dab13255a6c0498c3320b53353ad4c9a4e18972ab2831e9ad7b4608657b1add140a8aafa3d4c923c04558e1e30e362219faa771a5592e01c68b0aa4149cd86873a8805a38f69008f1747e6ca3b6589d5ca524ae4ab40971520ad293c96e01ef592188f2ab977f674cd1917d30e911411ef7aeddd3732fe53c5bc49b72d90cbc09f3fd116e11438e061500c0abf121c5926f3b7f209d86f4dea6553c5692fa0b903074b9829b1ba0d1261ce7a7bd9fbcfe69521f1208d3d313130b21bb95cd5965dbbf549d39079973a237cc393218359cac66a14eb71c414d47587a4d8ee6930bdad911a3a2e72beba01293ae3781f4fa442d440edaf36a75999aa8373e565d65488e42e2f536031caf9e90a05aff63a621e4ae4e7c41aa42561db13cc50b353bc08d8b1625d33e46e6325b3da2058a3b98eee5e0566518a793842f2665cac31f32e523728a45c62cd19012c11b8877d84a0a6cdb0e74dafb450ae330a2317d174bfccfeae03fd09aa4e264f31df64d28dda21578ca331bf8738682faea06b7d45fc36bad03f6d4f0dbfd44d0697933e5577d1ba6e4537a7e6b7a89979c155b01bd8dce5867e287c4c988ea7d748e11df60c9c40e5bca48794f546268fa8ed0b068b197badd674c75db7d335694736869e557a6591859039cf1bcdb35a2f3239c29c3818457d8308a623260e56370861c5d1f91b7cffb07648ed4b3b5d51c3115e4771725986c33512f16735a5d6da85a040cacb2e84147c8af0e3c800fc722864991ad49259432d75e0d729a9587a681ef98d242b4db20bd95df1f3c079d4c98c05958f809cb3fe9d9e487c68edece4c489b6f2aa1887976fade781c44236e1d71de5cf59a64bc8f483384264f9ca1697dc5454c027811d29ae28618af915b62b2ac280d8ae5705d879103afbf99e0a86bf24f0ce7798d61d7efe5ec7a26cc95d0264e6f24300c491c89cbb52b144d873072fa34270c1aa25d3d7b4e2c4520587d9ed06126198fbe09fc6d32ff3f786ad29a4094c015270f10fb78cd4c4646cf851f903162e8608b69dad535a02040e933b1676c64d675c64f21e70428cbe12c06be37838bca025caf060850b217c46efc0f86c9a8478af759f71cd7ec9d5b51060d755c63e8e1761fc1feeff4ef3c93108d3666900ca89856d0c8170045850dda988b1122a50e50e5dd94cf39a98d28dc133457a230414d86c91824a4423498512fa1e9dbfb1dd1467eafcee2078e5189c4c2c131d3c7046eb1777f574ca7660a84a895625edcd856bf7adc61de7ee5806d28a399d54310f33a8acfd6ffcaa719fe13949f2b2af71f2df36043920a3abe327558193ae4022f196e26dd89ba84ca78d7b29e2487bed123cf01f32e9b392731cd0af8bc7735e481f670ee52c263362a687f60ec00dcf561020bc3fccb073de519e08e5c297242d6bf7f9ee3fa434ed0269f7ebab64e0a69c72e693e48204ff1f3e55ad59cacaa88f7cbd226fe11796217ea9eeceb06205cce67c2d12bb05ca7c6eb97d46c743d4a6bb44e4b891c5c32144610dff67612ead7ba95aa5e9f25d5b32b3c55197abb85bae026bf2a252ac28b79eecfe6096b08acf263def8fae9e39902b6412e5bcb3d5026d78d6c28a9823fea6f3b4c30f3b862c6c9ff50686b06c23d6f72f17a17d3735a0a602976856b086f53ca34353ae9e4bb0cc4e4a153e318ff55ca27c7219f7ac44db6f2906d88f3fdc382f4711c6f40dcf25a4e4dac4d6f6b1c809546c234352bd805bd5cb222e031824e07913aadccfb5737630d89c8c78d0ff87abdca010fa12ef0bfb431563d8f582bcb6c3a809af0d6f8a492e84056787288dc7f5f1c3e50b0ed1847cb7ae2ac003027e04d88047720076c46f32e11259137838f68bfc98c55637617a6f6b4aa570c0f182852f01be59242f525a1eb54f922e38695a99fef75a40c7f1140b6eb9665d30c3dff2a948882ac700c83669d8c2cfea21fda7fc2582829ebe923a810a8627b7cc7962eacb7048f2051344e2e656e357d4992d095e60ea0a52e7b4039e535bece44a7b42830b45468752db46c0ffc7250a71b8c26581bc3c89f9f4ede2b8843847a8e64e504352f117f026a5561340765a7901a71221d02bb3fd4c64a65c6200c408ae71e751b924bb92d5482375264e8ef531ae536cffc09c4d2fb3bdd1a2d727e8aa5c428ff47f69eeeb22b65820d1980ea8acf7b479e20747cb207c2a3b64ee49982346200461b8280b5bd1b0f9510214348582ad93b821f6dd69451c41356c323e9b75ea2b891071e1a858beb05e6e7658101b743013801297888ff787758c0adf91a5c3b4767ae292d5673c75a418c07ad272457b4f4740e103e9778bd15bd741511526fe62e28f6e0965b50a829bda3d69a6d4bdf318987a8fa66c76cd228d2e540dd5c4741a095d5b02a417fb22d7ca4292f544243fea08c42567772c28485b514a16c2077a4b9c81df7a8c57497e01cc5291fe29e05f2a640a03262920b5f2870432dde660c4d99f10a2f94b640c972c8c5cf27d4053062884958f496638de088513398203964fe12b51a08ead32600d9e02e4f50b177073b25152e53c876add683812f6ba6cc44c87807939f048baa221d200735c02acdd02c58579a01e5e3bef97306c4b79bc58eab3cb12b513791822ef92b84211318c1566f553f41fd7108b495d87a9eac0817ab9a34f3fd7f1bf94f14ee0a834d0c01340af85086367af9b47d4f0b0c8a784edf9dd8de2e697a7363d4d925402b43ad4ab08a619f6317e666d504fe06a2622f3fe449cb6cff387e49f59c641d7aae56cd5025246d896958951e4ff1ac80e83f35623d37ed0c517612be47d96f1005b21e4b680a6d6dd17492587add0c634f0e67488e10c5edb30abf2eb7015daa3fbccb94d8d81973174ff86760dd78b47bfc3a957977c3a7b39ce558e2353183a50bf9eb72802f3de1c06a7504608a4cdc8f8bedc3291ec6152cbb71603d0155684e29a02d218925086c97ea100e5c280458e9193dd949230f5d4d081fac95e9d0d62a7916e8e271dca11e41485e6aead38da1b65766d3d312fdceeb28c6fcda1142a1f7025697f436fe71919bc47b9e6e91723b6096b4833510431143cabbbc579500ac15ac664ab0d6cf3b43ca2a8d4c086d50b9ac8ba65b5caa0300aa23851c8498471038afa8eed0f78acc1dcae0ff851e530ae22cd32580d46c1d26a3e09deb6ec72aef402e643f63fe0e6054d0c8f413c9ba1a209ce7cac41a742244785f58f30f40edc3180cd98d8136d3e816aa4209d2d46d9edaf44736f1bee359fc8ad2f47913fa4c76dbe689f5d301211b528c0e8b364a790039c228828df3eb460a982b5093f1f47e5f4090a0dd1150440a2f26b63562c8da0f0dfaf186c8b7f7868189726584eb56f0171e1d4556d209b84360e97fd7240caf04885e2746215c7a09b157a0cc8cee307bd4db08ddb649dc1e8f27e3b81bf5d42865ff07fe31f1f4230c11f4d1bd065f1f9f2de940594cebae04412500906897d400312b36f1145f372e23175646693bfede6891f9816c10c2cc122219fa35877b28bf9b92db5f4d77f1639cd12fe69dab78f3b7c52b83b6131daddbf204978b220001590833aa0744408b3b1092c12aec207b35fb49b5dd9eab616093d709a4280510b3bd5b3eb02d8b5d5d75507a33d981cee236ded3df00a67e0bb2b56512b5e0e8b848a81e34547675a0bc88bde54bacd38062981339358f5d3b43e02a5e6bc004813b2c5baedf4449b2f7ccaa56c40889d1656f214b62d79feb3638b936315857dc31aeb6f794a93765dbe470179e4f1e17f0f452540dff1538b0409bdfa08197c51a92418c91c86c097f50b3b4981e293893b82a8fcd4c1e3a693c057f480f40ebbb8e0e5bbe72e07cf8d70d1966672560c79b9362312ac623b859f7c7e6fc3c680808d344c82946e1390c3f94d234451a936954db84095cad206d7a781a89c8fd6b49dba45fc32e09d5705717a8d1970f22cf3b772f4e87bcf483af6f25200a0b6e6bb250ccff9fc4c03aba70351910fd5b42dd293e999c0d3d514a698b27fbd689a62411a68d10255931f0bf940fbb62746ff45ee238a300d02fb11e2b85266122237119c0c4b37f18021f015eb2d6fbd6e58cd3de1ad5d8156c4692fd01132389a3df001b6200eee64b43dadb077fc8b0f8ce3d714cc26a764f8b7a3d8c30ea04bef18ab8f6189687f6e589ecab22b9f0349a61ff695555571a8de1eb2cc07ee3729e393f83c4171030d7321b65e80ea3cf71a1c7740b20400ba51c1ad3b3f122a4f83cbb1021d1846ca5c059477d89feb81d55dc74d389b4b7abd76d56cae32a9506c1198a8fe2a168ce27069d16a0105b19b226404ca39010dfa5da48e4b2ee27545c288d96ed643118a6b0916e459889d14a2105bd6ee75c65dc7c80b072f989e913ee159625eb230b2b30bdcf153092654f06693475ff7bb51d5906b3f9ecb74cc67f46047f98baa8b44a7542ddf776988762a6d66abfbc8dbeff6c3314eaeb9ac9d12971d92a689d58e8fdb0f93b395008433ffc876fa4ab638b874e8d404867da41955b068b59f990f265bfce2466899584d147bb16b96aa568e3ff2efec3ca73a8d27db14142d66577e4a0cf79dd634281bdbc9199e303b374ec928beae8d162c9c81ee96ddd9e5ac6ab35f88c46fb0753e7658b17253ad62039c6f843b0dad17eccc6b7bdbe6e7b8e5ed9a717b9c8c673e9370d21903f4921b85e982afe49396e5bc35686eb499d99cedb4becbfab9442baa14d200f6e8e7977a6db458b86eb143a55772a9a13856371a73b8fc846622ddd61d4cb4fb18d4546665692c91697b9719f22e9ee7d08119f76dd3267b8b9337e836e31894a07bc41ff9a84739a00f8cf91b51504ce4d6e538a214566a8ff2dfd355433fb7f1732c358a4409d01441dca68362e22a257ba5d723f7cd04669c09b47633c8b25ba49d3bb5f0b8f233ebb875999759aa1908368a33d068415394fa2e3481119c00506dd26e8b4e1c6d3177e4c3e75e14193bf933887d798a02219676bd71429bc1d10e176765f71cadd8e0fa549daab12193139a263aba7f24beb642d7b1002c13a759c30840978c4ba097c62331d61b281db82f2eeb29b92c0992cab28bfa9be1d52ce87ec06ef848f6943b6d2b7f872d19753adfbbbe95037d2d4d057872be6993884a612d24eac7fd854ea7e87548e05b595085942e0281cde4194c39d88d7f681dab247acf6d412885346f4707ccf2c5dcdf7bedf5dd002c03001d3bb957538db3ea7fbc793ba2cd37482bbbc3466aaf1344226fa3799515777dc0225e1fbb54e22d921ec33654102ac1e8a2db9b7bb4f745c317ea8bb296889d75e19692f86b76ee90bb266d89a2c09fb02520a1c581378226878b1474f50660f78c3eeaf3c260a4b3f30c2c23f38a1ae8a8ac7ca9222b417a796a021582890c817ef97ab63fbe5466255a0c3f1b684f8fccc7e6eb3c47a435fb8d1cc817421557bd5331a2862134aa7dabdf5e72c4f595aaba36d8447ba617722ec28f9484738ce3f3ff8c3543c9df0120c37f9d303181550530b6d36cd7ba0b719dc0eb47ad0c4c9f2cbfc1aef7b96e80d215f4426b34f6b2ffe33bc2a1ec878d5cedbca4060e06db58c129fb3798ca7ee07dee4914667374f93813afff3f772b6249bd649b781d2941f049832e3771fda8c5fe1d0c891c991778f63a7747cb88b3e05b8b884828885440f2cb7ab021d330bc4d4d0d96ba6e470bf7318e6e6157e1b3addeb2eab7e6b2299743fad1cfcf03fdf026213ce48fac7f91693d229e64f103702f3b1b62a033a274b08c765d8c3791dd4db8f04022f28f57ef4cd9bc71616db93d1d07d7207d3cfa3c83a873491095ba3acbe5da1a05d3cc29f254508ceb6476a4a1bdceeff6ae9f9f31450afa97f31667e1004329f9a9666ede9e806caa4ca1efb2351f7a1af6b378ba37507111c56c2e4d76b5aedcf944b9b6bb6fb793aeb088513836d475a20fc2ef361a1280e1079d5747027a5efc5d623941d4fd8d3579cb17dc42f13e538f5645608cf85ee5895c195325e247e5824e31ec5f83fd6d5b868036bcbf4f2c24be4100cde8178eaa66a034a357c9a1920bc1d70b238ed62e33cf0e37f42a49b8b2f9752433711d94a3e6ecf9dcea9b7c8d4ddb7a7c0058ae04bc61bfbf7a05bc7acfc1823d559be4dd06428cae8cd392eacb5896f2a6a4100b3012a8ae9f4f400cd1c88a02c68eb916021891d882afe5e623dc27fe1abd55d1c30f901e40019d65bc9325c10ada4dfddf4b59dc19731f2a0aae1385ac302c8f5f65aa73bf7a4582881b3d27b467d367ca2530b40864f471b57d13809a7dbeee4461033bc99819a926a084b735c70369f9e61dd347c97a931fa5167c3d51700d7bd79ad25681cf152ab9668dc982309076927a1f85e08e6f551a3f89c10add3602bb2f5e19a12321a0f204f7bb8d8ac5a3be4eabaf01cee1f687a29b7fe33e84436926005e4dc311ab8a44e581cef9c88638d5fb11274d8b1ef1e6e9064f409ac0b992d31e99d324031e09a1cd0f82855921d2d2881f0f5a4207748d10a403a04d465c008856251c91b0f3af112b6afba0332f621c6d25934a273234057948019dad33947af6d962e226857644bba37e0c03151cd3aa38293aa3ace9e2a9bdd16469e5634a4fd987f30612ae45d6a067edf7e51e56f10016a4752208096722e8fe6dcac78f34a975250869359c87ca8eca7d230b01e659769afe11d4fcbe4c964269734cd7da3598e3fa6f42303b19c450f983d1b973ecf774aff043864df2a2d2b31d160b93824443e4907bd8ca5c3f07c7912101510bf13b734a58d86887ea95b5b9baa27c38c487151b07d3381db4c56fe65b49abc9cc76db119fca588c8ece62a6aa28b00273867918bffb8796bcf3f0884287d8d7830e4c3662ac45c1823f34c4e5bfe6a98184e378fa9153b1665be9e9f0fbc610e34754aaf5c86a9f03798e2487d0c61a53413e0dc849a0080dce9ec665a04b63152adf34737d9d3e6d4c582d593dfe9c2d3ace9634555f339d3ee17ae4e295224c6f1643bd99d04eb33ac7416d041e9a496491c241ea668a806534e925a33f199a5cbf83392f4deffa37239ca8e07c7f171358415640ea4683fb80cfde05c6f30f3e8f9d2f15b8c3a0e0ca3b73f95bda3705858bcb4947686fc51c2fae87bee5726642c8b1a8154990961250b6cee87f9eb1a32b3657cb5ceff8048d9bfdd0a502ca5271c0c56e3b237a782496ba620809eb15efa34e0afe524a1a0b56db7c58d066974df6c1dd24a23b4a0a48bff6a10592abc339644316129611fe9c49c0f751c44f88195fff0353d69731c0a444e4aa0f7ee350e1fa18f12e575978e25beee04c2b042a0ea206ef51fbf0cc707e40a904a83e2a19c8904d3d8bea774934f138f720bd88871ced7fe8a1811a68d8df742a608849aab7fd90b95eb8a6970698430a3c720e05a2fd3b174cb6345ccbff282d7da3356d364ea437f859c959b9d10639221c6133c94dd357d45080d9645b1e804ac5fc24e751e624dd233eafd9a4bd46e45faac314e108ec77b940bca00be9caad4671107c91883dc79078ed9d242214bd5696484d8786fa7bb4b303263b8953a8686a2c1f07f9e306d844a4afe6c245fac954f8d667a09b1026cd8d67af985fbc8f969efa19f0b666fca97553a335a06e12df3b583f21b8583a9a79e8372e1a40c805c70fa402573beb132faae42ca492d80fc1257952c24ff524025716a9dd5461c89af73c9bbcec7ecdb7b990a85489c9d9960c7bccd8e745b880cc709e7687027cbf02702dab7260000df6df86ca3314693964a7147de667560bb3db2656ccb2cd1b7493220a9488305617330ccad252db6eb094b581c2a69c06cd773c3c611a33ce5bfbda061378a657d1f6c31a2bd1f6092ee403156ffbb3334cd49a67e47a5ac9b8b751f2592ebe512c3da705d99840d246dae7bce7d8154c2f35f9483d0e0939d1b4ced99b6a47afbd99a0d3c4a6036e7af1de05b4f4d2d1b32cb7c45d198aad21d3fc484e2c0307b89b8628dee366ba2e96b0c2a1f580a38255c17192792aba80badf7e978e30e42b346e8cc93474df35e2d0319199c0200e2f0c168ad9803aa83408ffd9c458802d226956c41913c17490fc5003e00e67925de798b0cda8d5eed7431f7535d0f1d0f052c5841f13396998c49a48a37dd00a4dd03a4d29561f9b311f436fa1e5740ec6ba106f1631d0e652278cac462e6afe78082a2d18d0109b926633c410153e54eb9beb1a1ad23cf6f1e577ac8f1b3546bea0c6d0d01cdb98cde4e356ca1cc2d0d0f897978c6d1a91fd319ed55d914169d32c343406945bf228ba2ffe2c0c7ca5f888b19f9742435946d01731c45944dc6d2c665625f0d6587ad2de39716aa244fccb73369412bcad4ccec3237ad9d0aa63009eee0c9b5a0f334eb31d73e415be6455d70917d28b6dd632fa973a1a9c63df2d45ecf2c4a921ffbd9b4235e441a568c8abe79927889bcf7e7ee417b2f23d3c98eaf770ea9cae755ab006cfada004af056026f401e18ab5b5937f0034641c92b417f59c627ccf2539f2bf5c4064767ab39ed6c01dcdf8594085856064481b430f8b2cc1c3d167ec61f22cbba8890d5f17baf63bcbdc505776797c635953dfb126624cc7a9ddffd784963de0f4f3b300ae5728085a8e8f2c2ef9618598eeb1b8daf5b31e6eb67eaa46f1521e250257b1e3188758b78a21f7f1144943b7e4aee978bfb1dc28a95ea03b6321a0a0e6b5f53cd1d10527d4176cf93525085e97427478cb5ee6abb5d6238462df3a2041b9aec01d99f20219a8f00b011409ca22a7909f239b1da1580b6edf8571af32ef8874c0a3780302b83c41f7ed57e30c5828bcf4bd04b8d9b293a31cbdf05516803134eecbc0df37d697382baaf8c66f9cb06621c23da3ccd14edd41428af45302d0a0ca353fd16f30f8f318c0fce43a6bdfee2c330f8ff0a80b91f3273e37b6484baf67a1a3945a394b944f3d165931ea5a4858c560800af2eedd82e9c3812977377c6c6339bad9351e3877cb5864743b44befb2a1a98708016247f8174b1413d82efe76f887630f660ac3039f4101458cd25b5c6a05e7bf31024c78d36f3ca53b5c91f672a964956960c775e80228c81e73171e5ca34c2cfb15d98f78b270a93b82191a0ac5482d882884c4eab21ee7f5a3f3158fcf6df191a93ce10bab165b5cd33eef7b3675ff2b2262ac5f2fc9dfaa9f5846e0a1c8eb23ffda8e459860426f1ee2c49a5f4d14b62d80d48d6400e6a42fef3c77862148924258fdb7fa4b0be1dffcf25cc8ba475c7d3a78f051c20350c4be802608ca1d4fe8afa390bf45e03bb94846786e42e03ccc7b7f842cca21545df417eba1e7469273ae38c4e2117f81f772d1acb24c4acf0c707a600654b21d96f9ee1cd82577d650c46b2fc243fc8a92e9c4377f25c8256b981afc17281a2d977578fd6e7e5ad2d5567b2464996508dea7544578541efe250c95fddbf958a373b80c57eb1d04fcb13b14cfb3b5b8bdf76e83713d1a4c320406af41fff1ee23e906deaa6c0041f1d9adf5fb1bb56237f607866b7345c6557bc119774f7dddd676aa0b1efa4a5ba8f10d7b6741c67311991d239e2318db93651af27e587d3f5604f5a0675d24b71e931b531c7245fe27d248d5d5ae8a9a64d62b341b039f90ad395a99f7f014a3daf22f6b0d24c1a38686a055dcf94b2d8572cf6d382444cd31ea8af32c1bb011a98b8b69df8cd243511310e481bfdffdf59dc9b4013ce32b72a29497f527c0be9b2393813e7b55cc0f5a1d3f9037950fbc53af8b0c185c411f5f880e0deaa51287a719fa18b82bfec6be3c1af461ac16f981cfe7ca86c60f8110e3ea286fc0da5dcf294058f73e5e3620cf552eb074a766e2b054493d12341c299afac1e4037213af44a8eb785b2b4bcf3e081c49b9685d3e7e0c0b7d9f4bb50861e4e97a83216cd1ead54f12f894667136d05abcaf09dbab318ff0904bc0368e3805ac82d7df57b57a76ad048f8a5c11c11c23048cb810c0515f63d8e3b960a64fc4d09f8a491301f0eab4cfcc505a86df794c892c095af8148d84b48d4635bf7405dd5ae9f35fba1de27fc18209f8b924e0d9be53e172ae94aca3bb26dc07f650c12614c479b1163dda2d08625c1881272be2a36c6e63b6412a67d85d9452e5a126c1f46d699b1b1753ea684cdc30a4777a4e520ff0610cfc484ff24f4d8c29219f33f660737182befa6e623963890ff53ca1e3689e9a0509311ac7ad383a17f95a6343234573f5c62970c9c3fb948fea27f995fffc4e162b55896ed0526e9fe392c077da2082c68fef00e433168ae3ae8fb6109908f0ac9b6fcd409d415e45375f0e6213e16533d626911fb7b5360c10e328c04423a2ab6c106f94ae59e35bea3903e235a29f4f10cd642ba33b09bc7a88e4903ab6157cac5b255e161c43f252ea6d26453128a18f4cfdf3b68e8e7cd61abba085f7c9d99c20f4b9116992f3eb36331f599f6845c4a8825e3d8002c33164799075bc1f9f3373ff49771179ab89fc7aa56cc60af0d474390761114c38d71870fed3fb9462bd63c736966c78b45263d7d26e17981df2e1baaba9dda2b24d2bd5591fcf2892f9984d128084fca1475467d2d1da996848948c52f8e7de949b9e03cc46612ed03d1dae1c704d17998771ce7af4ef8fd1188882497323dae7136732c6332217bea2bf2481ea3b4e8a7cc0596333c5a5a274e3f9e29096d3ab72b3fa231d8415b2d65cf362ea20bf43e3141e62f7c3312b119d79d365a93cde83494ef914afe180b7178864592e3fb6dfbeec3fac5b8bf680ab6edf411335a55e939597d0692c0bee89402384896516361ed06470a3e325e9d9a4279a1cfaf0652067a2d413f0471c4220c5cf8e8291fe8369f6ec633c9f7d9477b07aa6a7a10bd1da89565df506f25718716080a2a123678bd00e1557fce34d72793f8d1885c05f00ced1cea309cc8f0c69e8003675230a0ff32a552f8270cf5dc81afbb35ecb5274655790030677e8dc96474a0ba38e6661b573a873f4b0d7d4f75b9cca52cf29005222cc8211e194710899691afbc33017e9d5948c1cad5592f100ef1c603d7aab6c80e486f191795922c5cd3ba61dfe254adb74e76fff95c03f6d2e423a9c62298941ba8c1a2a42a1411005f574783983b6fb1ec8758318f35588fa83e8dcb112ad1558838ee0693c06bcbb1a7ee5461f882c292bc6f415ef068a64a889c1802f195fb3db1526494436142a5d3d894e606c4c33917721928001a5ec743fd55714cd366a71774b0df8211fde296c86eb9d174b1347cd51ed7fa73309e72edd4a93e3502cf31b1bd00a71e6a0af65dc8e4030e7d4a6ee469a0c67a9bcb2e24873e972ddbea021d65ee11a67cd296a4e365a0fd13e1ded90f219181ad20ea7d3ee2b6e773fee709439beefd52efff063776bf24e57f1cb9e92edb54a8bf904b46f70efacf844c4415ecc7413bf5711a41c1e12f9044b6a882f900facd184f0abae86dfd84de4169f4c508162492d16d858ffee2df7f7c4bc6ab0b45874c700c4636e706a283db902128d610e55e1dd3c41c694bdc988cdbb592f6445495c64cff52e3e37235c3de85204f6775c82a0810f39b0968993e4b7b70480754cccf08ce92770d60062cd4fd68c0dda4472beaf89fb2cbae928892146d4643b8fa367f503ccbc0301a83c190aca98f5c878fac41bd25c9149e0cb4c2b6247dcf99f859d307ed7bd2af9f3988b1079b751967558c42ef424eeeee43e5439a00d16aa98f9a727c699f033a7cb2618be02738c043186eea545597050865c060f5b0a154e494974daaaf0daa8ac4c2cfe1dda1074565c41412902e8546d9e752d4c352a0b4b5d1f4ac3292b687153ad9fcc93b2c3035532c26a96b2a7e69c5f982dbfdc2655cb3cd1f889a2a08bbf883980fc41b454b33572fdbd6d9ef24cb080a03f8bb92a150f9a53c12f4b393051e0516cf233cb9583abe0d26385acbd5bc1e9c67755112a739b9534f4c00c9a55c6aef0621824767528b11e3c066c9adb185efe676fb347c2a860ebe8e58af54d300a9b304d17b38fa486ba36bb7b61cdd655091451bf55d2143e1ec897cb62a5d4a3456361824e428517f5af97a77f69e580dfe6b82842fb89d0c49bd3c5f36922ea7c890c55de4faf2e7c02282c159d5734bdc4c45a84f292c721f2f36461b9d5ef6c107663cf30426d4d1f3160d3f86e101dc7f1642b7b092e4c2ad91c7b8408935282197bf974c975d201e95b79634e5c33864eb958273f781cbc5c6a11fccbd9a306e048c6d425338965f9728ef992a969720747325af16b48884f3a87aaf8d438ac9bd906e97b82954ace7e2ba378db35179f949bb09854e7705fa1fda630ce122e880b457c68670172050ea4ca02f2be8743135c77d9e740bd26194ea78885476dd6ee42125b30eea87102aeb10c3841073436a7d1402c69206f4f0802f9baa8bd67f39d0c27de9db3a5089818a9d2fddb2c348a2232dbf7daab43bf5cfe4d2f73f32a2ed5c5fd29e4a36a25c8eb6d07421c6de2a7f485b98f9baa861050cd7ccbb541368f73668e93208312138a528bb2336d5671a429747517c712e452e0cac4c18d9503b5718c8d97d6eb3a5b8e5923ba106039bf4dde3ebf2a01879ee3b668d9e11ca3851ae06bbcc74802c2393c0c7996907342b4ef20606cb52a39cf591b3fe72d67a34fb3f681de528ed701f8a40e0359db72be58f12c337f8bc7135ad3f3f15385a6148d2255d7a1e5af3c5387d494c6b97772bc9a7b974c46576719db46d5e8ba12e6c15fb3e4294428170d9e4866331eca07f2872ae69ecccc33456ac6855200a7173fb0aa4fa828066652ac1827ea4810e6d5daa94aa30e134cb9c841e6d85d961c2d86c38019dac91487936f77e5658c3234b54addab6502b8870e91df3b38a00b3e41417f55f49b9a956ebc265b5722dd684eeadb3ba279d0222b78964d9e685846ea4a80ef7d10f575c8ce21b51a29e81aed1a935341ad84cf4ea799281e035fb2f6e81ea599950509d224b8ff372d1997cef86eae563bb0a3d887a737045930675553f9200e48c9dac89d21c1a154bbbf54cb9703f29899032d6516e9dcaaca79c75d2dc761816fbb6875ab6c22fb79b9917451dd6bf9ebf0ca8ed822489056a3befa110cd8f5c95225395cbf0157bead8683939f48131e5e76ee3528a20d8323ae619288b8d77dea8b9964b1c1b92d2c49c459614ad50190616339b8281391a5b6bee82f2f8d098c9bbbcd9947caf711366e345c126f6a2ed9852eea45175ce0c0c07d089e70b7d8ccd3fba09f3363311ed55904aeeddef3f00dc3352bd18e24c433ec658dbd02f3a013893885adf7b96b4e1d899bcc91e0806790dd4978d8c59db5d075f40e5744f576255bd7816d8217cfb58c35e4a16f4b1018d528fb3468b5f4163969c78f45a0d2d5a32a281fe9c492870eea5cbc3d9d5f151380e46a383add0c5cf1ea679fd026ee68f42dbbc53d973cdcd530cb381aaa910daee7822e423564a6a757f0160745033b92854ed4133523be8d1683d369b6d980fc590606b3ec3a3b201e6fa64b4b1e02d3d2097291e0f54b4a059519a7b1a4347a3cb4e288f8125d357dae726638b95d4136e5222ed1cfd3fd5c62329f48a0fea5a1553a2b14cba21f55dc969ad86f1d28246d2945c719acf5c590412df332ee4e4fa1dbf6075c46b058f1cd5d873c67333c12c91662062fe879d6aa0e4fe330ef1ed06c92e7a9ed6f74d8d704b79fd37f9343e76962267ce9b014d51604557c3cf429fb43690f4891801fbb738a2901c86a64fbbb08825c7986052786786dd5d414c32861f2ae4ec2e0d0438c6a146764632a3dd25e4e3791f9b3d1e35ae7f4ad6cd17f0e004475781549a9c971954390f3ef0b7d310ded069e6896381ee5dd8183f9d69adce7f222380d918fd70435e5088df77150ac73d820c7d353b6a9f9a8d2a53793063242643a17385555e0eb9163b147fc9be7e208fbed7406b155c4336afc7dcc7a671a89b7daebb6a8574a95e3af8d73b7ddd688df10d0c284a0afe6a64299ffca6beda704ccbd12b24b6176eb888cecf2219c7b0c04b495b334cbb7fb2d82648229856f71419bc62f494d51b6dc9937caa690394fe598f3182d11fbb527e12b1a8017aa004f64ad4dd2f9721d88a2493abf35f94b65d025107d0d1c7a466a08e90ceac13eabee18337a8869507fd1146680ae5c66162aaf64134957c7592c485eec09a15fb31a56a6ce48c54daaf542b2878383b7687b2e63a7f411eb805d1700bfc56d072c9a651448073615590c70eaacdfe158067512cc79e6e3261eb13d55663b82b65b18282c8508dadc1b36776b196890e47cda38bd521ef61fbcaf201f432218df35bbdca3be2280dca04fbc575b5fec3bfb4e45505e6f4e03dc5a2295f2cfdd929619c3a80a92d3ca9e799cdcc6ddc2bba412c43c568aefe0909857824b6176f8728627b898e99b1683b124c65c82bed4cf8e5f177a87ad263861a0ace1efa56d171ec5c5bab5a7d70b4b1e609e70733ff1a72eaf1516202ebbb2517c1ca2a5c44f9f7e72282127882a977cf64faec89a235592698c73bf6dd6afda36cc6f5a9f31220518e956b3d3ced3f35157854c4925006d22bb394e17ac656609acf1f69b0e8c6ca12d63b580cf1336f733e34ce992a713ecfa3184e506c80be221e500c383112008750e509a883d70e0875aefb2029db9689a9048ab9bd9a1cf5955682669f85e164ece85ea1b3296e62edd4f22654fb26bb4758339b66f242faf49621dbb644b5a6399ca344099d9fa48e17aa0bbabc6f9f2c6dd1147dbfc777f53df961eed49ddf126583585efe9023743f14232b1347a183dd76f3a45c5b0afd89896bb12d5b702ebd31061572c10f894cde200b654171027b610c193ebd95e82565b5538bc00f69769f409b40709f969a6530d984e5ea22e0f689912b09cd6f796954e15e4e91fe09a862fb2d35fc38a34dbaa978511e74d0a26011b121c07ff2dc1172a23ddbb6303233f2ab7cd7749f54ff2723673d173225d4daaa092af0b4158830397061b2b27565e1ea7fe2dd456994f32f5690afee26390ebdf8dd67776d0b53b4634bf81e2fe734580efaf1b8e96d5dfcfc51862e12273270167a2c6a8ddf808f4f8459eeb8c0dabf70f39e347a38a943e073375aa435a8b6e69bfbee85320a82952e32d3839f56d31d647bde2c52422dbbcafd0d416c3a80956b1a946dee704085f4266b0a1ba43d043a1a1f9a5a305c15351d9981a263f758ddd46dd4f5f7dad545b35ba6d52a844a7eed36a439734fa8ac5fbd02c2071e99211c00030b60ca0084d1bdee9c330ad85c00f4001e6d6bf884261f632ffa08f661bf35db96969a889051237bcbbd777309f508dd0914239d623b3a15fde41487a563d4bbbd77637b775defcdfd60c3180cb3d664fa51287aaf77d7bb1b932fd5c5342a7805bf53d74d267b8750f94ad783e8dbe5556f5dfeba3eba4eba56377bbb6b92e3e17615c3ac35997e14eabadbbbed9aecee8750c117c7a651c138f6e7d5d464abf295df2136bc826d64fb9b7cddb03b664366d1adf683e817ee21fa867b8c7ebdbb3e62f2752ac2a347f96b4423a625d2e57997bd2eefca7e5d5e67afeb9862ec2a81bdbe1bdac8df65c3dc1f3f44895e5f595c9c9edecaf920f27e9663ef2af8266fdfede554e075f1a86511fd3159723ea8264ab668717c1aec42e52d4ec3f2abb46039b6d1295a5bb438cdca555ae0d8a92156b00a8ee91475717f69bcbe2a6f7ae9d977baa961ee53b2c6a12a571c62217682435b42e17bbded1db9fbb7b82c5fb9f82ad7050f5c02eacaf18750b98b3777cab1727c1a95bb68f17b9a16bf2dee7f4fc3f216bf1f82a5850b7c67963ec48902c22e30feca695c541bfe62081bf00b5c6f8bdfe4ebe2aa1cdf95c764adebb29925d75d931c0faa99f9af7c0896ab5c72a71cffca698e574ec382af72161617c72c180fe102636c3b557f9323e7ddff342dce72f1f1102d70c4bfc9337366aea8dcc7c8d0468ec72ca52da9948f3e28d38629603fd891ac795506512a67c823396229bbf664a98a41a9a44962cbc21695d4c28ecc17f9932f4b620a841ab139383ed6c7e463c24c39312726873227f644ccc7fed4253144512cc2d86861478290c8217246fd15521b65117246edc6b2882145f4758f75aa5fdc920c6c1879ec44a15e8c883076a6e2651147f0a1961563376e1cfab8f9aa17e56a5cafdd5267bef4adeb32311cf3b22ade262a66091c8da1666248bba34b84b13145fb6e188e9457cf7c8142cea0aff5f2c93ee8a504ebbf7e4171f9cc970b27d72b93e0e8d4fcb513031b4a9e70e664b90509836921ca5c4a228c4a7ece0de4190653155450c8533221cfcf9c1a8f1d003aa6145812c8224c66b060c8510b39c6cb0d48184c6a000231d65c3df345093e3972b561244f8ee461e1c99949266a5e20b64c0c3420a6e6656a0a24301561e506e6bc0c19b173c74513368463a7a475b16da2304f368526b5276b9e4dbdac96b4256d29a93c6273e1505a7d35166874935c6f0f36543860202f8bc82132070859e3001ea6c6b14d727cfc04b2a625130bb547faf03411190a9b086661d60ed9b2e843968caa92216710892a18a88e3d2e35b0d1464b53c5228df1891d72bc8b0c1adabc0559289040598749fbd8a64c122986c35196d8ba381eb24ce32dae064663a634d6fbe2a519d85035b34d548c14065bc91c311f1a04ad5eabd7b5ee9e3cf29440a84b72d7b48f8d3eefe8e3a3ca99528b3e78a6009d74e80f5d45ff4c54979460c3b884dcc74153f3c3290575c91591bb7265b3edb4ab8b4c14bdbcb1f796bb2524d8f0cafdf71160e3b2648dfa0dcf5ba169337368d533a07553c919d9eb89e4199fc81816976057765d5886165556bd33131533511876591aa31ad81e206a7834cc77611bf3a5071c9fbdb65374eef0d2325ce24be8b3ba648c7b56ef4dce64c81821cdbae488645765a2228d5844cd5bf333da8ae1409bf8bbd7b78b75915c37ec93899cf1913e1fd2d19641d2bff9e678c832094f2cd4209d7b64428166e9922e0ac644d1e310e110f349c0cca523368c3d618c0d1c34350ee01181eddee9b7399a30a589053943e659ba71a6b267573681bc11e95abce88626f0d68899043e720ba8f524d2eb4938147dfb453a463a7612965c0244b71fdd2f8b6eb2f5b66fdc76356d6241a289f96419a536537b943342df5ef1862d19437be56af4e9294743db4626194389ac6a9a96b56b59bd1addb6ed35e837bc4d99b357ad46f6d0bdc9968c512d2a35496f1039ecd3c890af44af9b8659c7cea4f36e44f6b0d570e863b368bc62be2293f9729d0b5dd355f944d5dce1512ed1c0d277cc0e13e67b3daa5e1527cc37fb56c819327296e87eefac6bd866e4bbe876e02b26422126f23562f6f0c9be07760f63b7b7b8b83d729c8f2d6f585a2163843eaff8a25e28f039e964cddb7028a42787dbad87b887369abb6fe7ba6fddf6cad5e87e3d05e85444ce88b9eacc977a9f1cba538a89a2efeef49928ea44a697c3514262c319c47ddbeef6942be6cbfcf614a0f912f3f6530ae40c99b7538ec66671b766d775af31dfede4add3e6c6d5d8cedd9bec63eee40c9f522063580fdd30e58a6c552443e89e2c57b161ac6a6dde2bc6236f8fa21bfec81b0ebd87a4b6d96fdebf7ffde1d0a6e6efdeb4d8fbbc57aec6e71d7be7c8193163de2947c3ebc9db75514dd4f579fbd25b6fec920dacf53a7f5d47c06579de64e27d2ef1de24f0bc7b21fbd0354dfbbeef35e6bfdf84cf1ef7cea6660e6fa11a44262e59f34298bc4acea00fa9f6cc85661a96216364af9d3bcb4e391a19932caf092b31f19c3d7b942167c89cb964a793f220afbd86ccb2d06916bac936335f58868c41e911edba76dd38b1df647ac3f3c8b15e77562c63a63b4ed98436fb864172bce24d8eb74920634c26b9df37979439a031a714b0886152461f9b2c420e298249e3585da04523489c884a62f013951811224f6c3373054cd9c694b3bc917202bf028d2db438b5162da24c8b162c2c518685656525caacaca8a844191595d329ca9c4e2929512625050525caa0a0984c51c6643a3989322727a55294299548a42843228d4623108c32202812451991e8fb3e6ba38cb59e17653cafeba24cd7715c94e1b8d8b26db165ca685a9645992c0b85a24c28145b30ecbaa2cc75595694b1ac5aa34cad94764799ee39a3cc9cb145467e5ede486909c104e8a70a287c789e48b2736489247472884821b26c92c515414b9848f12325ce1563d12bd30341a91461bce28a44b04215991e063d55ec9e2aadd78609c33353071a4329c5e982166d3062c3f853310cc37eb09f8e91e590962c8324905c226be426251356e630027b2387c8481f1313132fb52ceb9a40b5704c471965bcf53246a5ca22d7371614f3d496b1ce6add1c88b0b5946d620e43582343b055080707e41a56a1cf4c17d4bf19b1218dd9b558b3cc14c7999219111acd71c26c745249295529820d9f290a063da57f31bd2813e7fc8dd6f5a17b93fbdad48c61193246fdbc8dcd46422ddaaa181e949cd5a2966559169667a439d0faf29a942b298388af51e6ccce224f6b725f0338808359b81034777a16d19158c43c859846e44924cf2b7c4cd49c3c988e649922c655cb25661892a3e4287e01e3666640cec9040b47004a120435ec4451038b0832d87440822c18c18a220b491822054cb010638cb2bb37dad87c99b13b7637a594524a29a5ddb17b069452ee1b1762f63eceabf21095efbd76ef09bd61e9cd25a0fb09c342144d0e4bf701239f4e3b03418ce9ef3d39dd996949890a9ec8f5f4c5fbe4f4c43be9520b96d2574a573979a9f4caed78711baed9303f14141b504c9fe92b5d7b71633295dc0fd4647aca0d6f72cab5de5ea484348b5cc0604fdebdc9f4dad47cc232640cd3eb0d5db2e9338d82e3448d6ebadf090e61e412e9c689d2bed1b649ee878d3b0fa1dc9de3be594b0303857af1827a1e087ae1e87b41a905bd172f6e9c29cf761a0d8c9b0dd7d36b2fb237fcf0c5855c476fefa581a59f3032f7d0bd4b031bfa80912b07235399b78d865b16343747727cc46e2002d01460dc4004202964f93b6d3802f7892c49c581a3ac54ca48230b8a3e8d52cef328776f87ca4f1aa651c19ee6554e3bc7d1c44e698f9daa9a17d6ea795ead9ee9a4e4795da779ef3c4eeb3c1c59f4d1e93c3a55ff712a1de90453b92715fbbaee386155a72a27e26ab5a2d368441ad9fa5315590e3c5591e5b8d355c141a3824fafb59eab1d47e34315444db3548612c98aa84a011d38ca4e4e4a99157db5d6d1886447b556558761d69a4c3f0ab579efea3f6b2b0571d0cdf36a15e1eddfbfaf6a15f7b0b5b31da992ba3a1a8d46231044a12794aabe4ff483eaeea24f54323d364c4a8aa9d4892cc5a12a77e0c88a4ea3118ac99e946c6647d64a1c22eb6d9b8a7b941908c2714284aef34cf5dc8e2ef33653ceb28c4412812392c7ed40b9a97298060577f7c07b60e76d998769baaeeb700ff01e281a91482452d7913acfb3342654d7ed309d8b3b4c184b795e77af13bd870dcda277dde8ddb59ee79148249287c3eff624d2f72e7e64459fe9a298ce75dbbbebdd5e2e9ecb36d3fd4183824d9c0da5d96a48f32b83165f70a2ce3ee3911b87d9bdec72cf3e10147d190e4dd144b1567bab918636b0db87ad4ba4087e66a6b86061b32cfb68a3413c5b20e5a1e5ded9ad9769efd7d6b2eeee5dcbb2cc46f4ce3dd3a8956937d432dc63c4d1daa36c949548e006825a9659dc0dea43c85d7b86411f344cbdacaf69a73c3051df1eb8043c466c27e7dd4d33ddb3a311062d16711d531c87439baa51aa510f8737dcbd1ab0dcdb02325b9f5a9665f778e4d6de97bbb5a91aa59a46b9db542eb4a959c371cba1909ce1117978641b7a808b92c06176a2b089e298f0de81af4cd64e940ef41e078410e00d6d3278d1fbd8d75bee3a3777cab2efa60b66a2ce70e33851deb6813eb64d2385b4921dcd9ef49d466f9525adb7eed4022d32053cef74020fe2eddfd6ed7d8fe0d69d4de313f813f8ed21963f7c02b18d981dcc0e3ec3e8f7ed8636fd6dddf67e05718fd2777d6cb97bdfd062afd495e8bb114923913a52e77d218fd49fd733f4ee3b9d2fa47fdf356aff99b87bdb4874b2ef814dddb9ab8936d2a5fdafffbdf187c1cdebbc5028cb361105414a3abde1e85696652f1da4b7fb4e0f7e7be9f6f840cabdbba29344371c611f5bceb230fbbe7df47636d93b78698f0ed35b1f9ba5f7e8bd631f9b8fad1d00848d9cc1143370828f2d5b40ce608a194c71b0061a8eac659444414ae9290ebb6f9556db3d76c39470889146f560b5f5ab187b2aaf44aa28f444d64fbb77dabee3f495ef60b94a47e9874f1b5ec1342cf766da0bb7c74feb28d52a3e59ed9eecbf6b9dc9844f169f729cbe729a130d8eace1d0541fee2152a9448f128552cff3a84769ad59adb56ef68638b2cd40100c5119040f5ef35431df907f1cf81e7d2822d2e9f41da313dea182a3cf36adb7793872d7fd2405e524238128267b52b299c892ac5da17693f1047e74d2cabfbb912ecde923f0e65059b122ec43a5cdaddb56aeca4f9765bb3954f0cabd4bc382e35576e52aa759c12a98e684bdae533e545127c7f88855953c8f69a284e0bb2190fc715c08a4e32cb5f6d64e6d3be9edb449a7a5531c8a9a36f77a8ee34eda28e57ef8fe59ee5e73f6961702c99cd55eefa9c3373e0d73d7b8ca719d394cb9136cd35c49b471b7d72c672d77bb71da6739daf566efbacce4c9effb6e770bea903d72a14dc665dfd7894c9dac36fc914db7131c3de68ee3344dcbce5dd2b37b83c4ddeb6e3df07e3814619bf6ba73b7bb373ecc7157f36c9ac33e7e009196069ab76d17512cdbcff4774578b30dfe7b87b189a216f7681bd2bc61edde64f086417253d228c3dad4186b1c961e47f4deadb2ebd63fd87ff8068edea3f5bebd36959c0fdb1b9fb4f749c3372cb5d76ebbcff2287e05efe11ee06ddf90721ee79d50d268e4f5d6dbd6ed7d1bf5d6bd511c1beb9e7dc320b96d08247b07ad873b7a20f9fb4c77789b280f7cf70f6313e5dd7bf779d47adf4bd2edfb3cba7db4f33c9499eebede28d5b6cdebee9dd2eddaefdeb0946a1dd5a6b6d13e958f2d1f6fc1d0df39cd0b92696d8be93bea7d97dcbd5b9a17cf8536f3f346948eecebedb52e37ca9d62fa10f743fda9fb0e949fbe43e529b7f76e48b377eaf0a7f23d05d7efd486dde3f70bb4debd4b6b101fbc537cea4ecfbdd60e9f72a0fc741a144ca375df431a3dd605c9b5b35564b75b7ac38aed462da5d45afa7978d451da7d1ea51ca59bd5a4773311a5d7d2c7a3ece88f6ed6729f9974fbe928df7142d9918225f8a8a23693bb386b6d7c7d77b96bf6ca0e7ff5745b7fba33fd598cbbaddeb54fedc41dfcc87e23909bd67ea6439b69efccc2bba1c69dba7fb728f7f41a84e68463a3fcbbddcd91627f7a6bd7c65d8dcbc20c64edf494d39c3c1a149c929e6ac319b50182591a24bf344fa8d0dcd5b0de369bfd13b5bd5eee9e69db7ec8b09da82d7236d43b098d28c64a1ae91193747bf60377efd96bd753876f70d8c391defb7743eef16ddf1eaeb7eaf71f3ed4537cb2a7f7bedd933df72db3f806777a79db876da8e5e11ea25bdca3f4ed3393bb9496b651a4a40a8259b6d18df39e7d0343d9768fc361dd6a287ba8de1f39d4358cc966fa0478b8628ab9eef7ae7613b5a139f49885425b88f36e2fc7891eefdd74b14e79f8a10dcd75bbc6dd1ee063ce32108719c8e049f76e77431eb9b321eea6100e79944e4a38fc91edbf874c3814dd8b273547ebb32c3bf8ed8a0ede1ba21fd9e3eed9789b6d34ecb7cbfdb8c16d59e761198e1385c30cd02c7328c3a1cf4c798ae3478e8f41fcb0a4f949f1d988c9d7f7e9d58ae5f0d73729babeebf2baaeeb3c7c5dc44d527a123d985db71dd2932befdde34a7ea661bccbdb6818ee339d92ef6e4da77870d8466c628a4159ca08b4e19015b6626ca27866628c266dd22ba48dd094c5b044aed6afc6a185758e8480340c860384a988cde9db68982cd8503ee1c08ee1207285fd66c523ca707986308f4ecd03e9d4bc69e60cb1c252175fb0615cd2d6112bc051a2042272e844d949a58ff53a64be504bbb8f9462be08315fb29989f326a6200b64976098b526535c92b5deb488fd0822c2480f84423f3a4513c0c5a01f5186cbf4fa44a04e94b1995e2a1165ae402f8970090399864c1fca3164fa5076215329852ca74842a65c963ac814479fa892a9f5b09deb126fd7bc3f36943a2af43b3586c416fafb043b846297f499424829268afee8a09a3cc844a9a53a211d580b63132575e4a9951b1882944c21043948304308da27b70fd044054d54c7051b4aa06eaeaf4929e62402a353856c222ccd3e5186abc2a2204e27664e9461c9fdd93d6530719d3913d55fc9819539081334dcb507d98e8368b72692b9c444293175e6763bae260f620b1aacfcb952d83851f28a896a30581994fb73620924a1882d7d202020a03ec6e44661dbe78a4109a494b25b36edee96e10e748724ace1880db15c84d6311912369446480dc417cb23c8f4024654c7c49656c2368e4e755eb9810d634c0d13638d189638606b20a22e53080af632c1cae7d0215afc0e98e4013be325cfcf2000888619000160dca0ba005a1ee3383cf5500eb18e43df7ed8f8882d3387088a066c3873a65c71df68e623ca649f8fc9e1f34088ac8cb758394bca63343d4610dfc5637f8fb1616450a772b827ed3a3cae64dc93864f3964dce53a72b80e39dc25870bb1e1321e5777795c692ff029870e77b90e1deee2722101b80e38764ac33a5caec375c0425077390521b3f65006659f993208c705c6f8c696f95f20266a3e07193776aa050e51319d9a2c388c91658c1cca1d142a0587bf099770682d883fdce1cc0396c31b96475c1061b55010190efb584e177ca8b0c72bb30a071c7050a954383cae04704f9dbaa7c6a71ca994000420800bb1e1a947ec12464b4b0b0cd461a00ee34202f096c7d50df7249fc3e32ac63d497cca11e332aee386e7808500e0375c0616c2040d007ec385d8f0183711a0c88c1d260a35800be3dae8d4fc0d57755b6e8c8bc3cd38dcb852ddb88a71e3ea861b572d37ae60dcb8ba6ecc03a83153b7621bc6c400f4c53200ae0dd7c5ec7a9eb042cf135638810f0c0cc330152a8cb1d1c34f8c12248001a358ec819d748a9e3040a6101335491e2801b69a31cadcc0fb94353632c6943f0d037e8a5aca020cf96a7b289bc8f31207d200db470fe5111964c5555d49ae069781c4969969c17af77bbd46c05bd7c8106c057ebb427440b5fa3ec4aec003a1848a9f6c3373c5272c6f39c87ca9a71cdbed4fa1930e4ff4791e0d730a611ddbedb7db875227f450e684b045c2d67b8b74f7547fb3e2eea9e209d07057756abec34150d8e3805cc38a09eb71b53d443d5b6df71e9aac0d441932e4c981f842447cc9260b268e9c6123a708220c378fc8b3a661e28a1447b2c995a27bade65db9d3a91bc496f9eb4a2457267165114964a2e63c42c3b29133e4084a105b72e8ca5c8361d95a12a2c2528cc3c6445d2558f9309b1a0c8a80c4bbf6ffdc4a7c7a1082247890a80286945236866158cba6dd2d553ba07aa28ffccf44c5989999989923e4e4d0443125f24420030e7549b17a58d775cd5f3c72c7eb560f375986cfd20b368c3e34354772297354a2842a72d330810d314a4513ba20a2020c83a80917801f20428250980daaa0e9a805d49452411c74c5c1c1c189a886b1d13895cab91a89c086126789d9b4e79c162a478e20c6e80b4b1c598279491c216cf8bf982d67cf77b71665a62414d8a9c4199ee059459410d8b9842a46a0b3924300421474565189108ec0596975a3777ae0034728b2babaf0269f6e5839cc40c630264a7e07497a70566d25923f471ffa5b3fc1d573f55cb5d65a6bad97cfce5cd26dcd25b5a7f6486176b7a58a735e2eb55eb556975a65b89c40c7d2c9eda3840db71c755a4746ebb44ef5913b5902c9242a974deec82676e4ce04ca5d8449c4c2049a4016106850ae3ad3886d64a241330434880659249e39ada83ca8a1cd933ba886a7791ed03ccd9306ec48c67ea8851db17ab6755380381ab3bbad6e7aeb5eef3bdb6a2b05a84522c8735a279d2b03d9c2575b97b6e0a433516dd5478aa38c98b47a685c92650e8fb8e4013c2a0f2be877af29b6cc6b328a177cc0a7872592f8acb4ee001337f05969549ec00849f05969b5e7a70547569ae5042c663064a55d5bcf112772561ab60149c1b3d2b0adde134d0d129940e4089e55454d948cd911988004243b44622044d5348a61d8abc51e0a65c65e6f45f7fb41cb189edb7ca83ececc8a6d990b43f7369bd1639ac5a4155c8dcad19076648a17ddce46928c5d912790b442c6980f61184603bbee0eac0402ca92abd41a4111180cc13ccc091802e8ad6953656b94a996dd588f6d974e1486d5502814ba7693af4b2e4e1476b2b01d65d942a0efeef3406fccd7e79b9614b8ac895dd69453f6b02c7cbaf1d97362cbb22c8c7aa19a2fd3b22ccb0aaf8ba27f81cd2dcfda018cca4a9bd2534a69a5b23695b9be6fadb5ce1ad82badb4d25a2b0cb6a2d9a78956276dd95d67d3ee39273eddb8c2be2c8cfe4d9b36a530daaa1f9bca6682165f8419a5216d02413fe9a7920833833a456fd109058c760eae4b42b752a0dc9f411d6d6d7a051b5e38b92b9389eaba24b6583d9dea3fd7ead3934319106c587d8e4c597daa143f5950cb8699ab89335f84c064bd62a6ba02a9c0ca26dd6448ee954ca9414d727f0275aa59a2b0a16c02947b7234b02c9bcc972c70e68bfcb5a467db208727d8303619126402992ff4dd6f988a598093a392152891c3d81395a8e08a091357f3a1b5d1b9c646c300d17303096c189bf467a6cc01d23038ab6e15489181f851d3ef1f551d42a8923a3c35353535353535353a74e8d0515353535353a343870e1d3a746056a5d9d1a91361d69af00e150cc334b15335db6aad12c73376fb7d1688873db1a7fba14caa4e81a3930eb2a87ce5a39b43e58461262ae4a715dee4ec212b6485db0f1277214da35aadb5fe264b59bf6119f2862ed946c3ad6eb20cccd95e10dd90268baec53967ca34cddb9626839fe91f7b4513a5eb94bf5e1a581819c60c7dc0f0ace63616154a555cb0bc45fccc2469dcb56ea41c05ff403ab7e3bb218cfc7ddff77ddc51fe5d7b7139eb9e4e180573971edf1e46b724fadda2f42b2cd7acedbb28f8a44bce87ef28f8548f727bd23dd57b2761584a4a4acaa9621a1bfe22936e714893519e82439a9c72edc5a9742747a3f4931394d3afd3c9c9bbaee34eb89393128e13757f724d076fc82393aec9cd0613a613755fdcb89ae97b4f9484618c4e718fd12deeb18272c38a7dc0f856e88a0b96d7d3b75061c1a1cc2a57b1d7ac8ab5a00f16ab60dc8df644a09d74eb0257fd66e572c36962fc86cbdf80c3553808ea383c48001ee3372b19f7242fe337ab1c6e8e1bae3acd0d575d5ea5ba0c1c0400573d880dbfe13738a860c4b8a1c58a2013c523771afb25777cb082dc30bfcafb53672a315fb61e1934711ae6f44e61d189325c6619819d79f495539ffebd61b0563daade84ca4c54131657465ae982275dd19da09b8e8e4e5ca5c4d549461d2327df3c651fddf4947bcab08ed14d271d2b4ff9ca532eef094bee58197c7a2ec98e53ee12ecfc699fb9a46156c209c40903e28a470d5e7918814ee4e36af430fed8187de561ecc98d250f228c5e4294913bb12507730711e6e43d97903170d00521c288c087272b57f248cec8c953ae3c5d693252fae476581c9c21fd8e28ebbf574c599749b4f46918d23b943ff2d3a7979033641216b7b85e4c5967b12e77a24c8bd8622d911b473c7ba6ce9da73b713ad54fb9132574d3a87f72a576a513dd133b05fad0c82e87b49cd149e4feaa74e38a74e36a74e30abc713564a56558eec49639244485c5b0c4012277e2e6dd78d343a2c51761c5e9e6e9d69945bc775c983af3257a98c5a2a9105b0a39c345c628437c41437ce9236223117b07b17d109f105f342a2219e48cedfd1e4394e1de2d3a0bd974c58b1332cb3b0849e40c1a19830511a6a65344fa501841471379896cc402b1a53fd7106180c89cb9634413816147c4549f67e6d24397f4eb82b7aee8d45ef2ecec441996fce5fe7c22a50925a223cad01314f9294293d026624b9feea89cbb31b69cde82e5ca23220c7d424c519f4e5135649ac54579764dff9158fcfc5891a3613ab9d65ba649148c541a5deb596777727a826e42a49b944824d98488dc44a0373a6dc22b92441699454091682659913c512672356acfd979a73b0d439774aa0a3983662163ace02574872ed9c9134f21f7357be71222aaffdd898488f291319634cc8c22a65a362137cd625291db9b40b1a53f9f0841c1ca1e9eef4b1a66a3594cda853c0aa91ae47da88fcc51395d79651228a66b335140666a1ee7e7e4fa285d1ca4db0316728604628cd9303444be9025164d64938804223662cb3c7861c8ef456c51cdd4fc8c99491a8bd8d2e76e8f21b6f4b7db6490319e1051fdec3615b1a910532e72464bf1e284dcef20c4543f0ba62b4c468022a67710db07b19188a8be75fb8888eaeb6822d3c8199d8405d146ce6822b7409f202ff1851cce1d34c4598688eab7b8f3e72ec9730d3175c41772bf81763ad56f2372bf89c8fd9d9db91345c5595983145738d13384123d4284a1064e044d9c23e008e28c789f997693b10a9ad812438018bb042b63da0c7194fbbac194410b42ad71b307c7449d64287778b7a7e9fe5dfef3b0e9662531695d550c8aa14924cf1c2c3bc2863268c917ecc4456822163f57f830a9a24891bec4b00c8aab89716856c8162a08c68499420499a4496654c3c8d5270e0cbc3083b4287a767777239948827690c8254c41f2874221c2483004fd04fdfc502c836415f45dfb72466155da54042919423e620a9f385f26206f83e509d8d82c7d105bfa920731c684913b9bcd0f28acf56a447bc368414131413287f1c746187d76421782854e8f248b7c801c95d820284b4e869e5d8876ec4242cf4e7368c7ae23f4ec9403bb761dd835eda1ec210d9be46dc8277cc427b2fc4c1256eed022cb5f3631280605d13c658eb56a1017689d97143a4a7c8ee8d66182c57efdc77cc18c881a4684636a9a0830b84294a1292da74882fc81d40167a36144efc7663981053fba10d149affff9a9a9694084912d883f3a37b042981042bae8f5a7799ad1c1d3902e7ae44e13e718611007618206bce83a48d8060e0262535c31a1cdcc17d90f0390e56d8833a40a628c3e6647a4cbbf8832f4f2a4fec804da9823d8981d6acc6067cd34733eb4c8da8fb78ef4a4adb15d5e9e6e5ce217b1e593f235629638a4f9b352fbd4dedd6f363314ea50167746091595914e8e99b19161a973d56031ac424dd4d502454d1d6f762fc8c15982176ee0830023da6d4c549f6a60c3c86363c665c26857990608f20307a771e41ef33e54f9d2b02f582c54354723b47d08eeb153563643eb4395352c6b59166ca4481c59e28989ead9d34f744ff360938707db393251488e44196ed607f1aa77337faad66bc534df411ce47b909b5588cad67462727227b6f4e3ec79e4889c3187e496526b91687411e95e0983251c4464799774633ad5a3eb7913d38c46d6da8b623ef06a7434127d640a32ba27bf5b9ff634e02df80f07b1074728d14d28d3e88ab0195baae4ce13d4fd79c57ce18932a1dcda73b6b68d63aa8285827c820aa89f9bb9e9c3710f4598c9d3a966429e3b1665b673aaccf950a5f05899b54f80f61139033322cf6f9916ac7422893cbf5db3b6d8309e05bfef8237766f573e617907af63748b858c22f81dbc8eef2016c2040de9e085c44e7d3ac07fffb01012963dab4943ca9f3cb17c42c3fa137d9f083cf7378c45babd90d2c15ba4dbd3906e71e920b60d630fdea29cc803ef8db090efa35b17f54b570eb10dde3b0de85d748969be8fb010fb9177d169bc8b70ec14f8efa11c228b8097c6298777f034de413c318dfd085f744ff3403ad51360405c7d380810f866f5b8b28f40a25be3ddcad8192957ce876885acb5d44f9e7a2ae4fe09a70f0ff3dcb7abcd356459a3bb7c8dce872a6fdbb34b6dd2d0b013b7cb5f903b3b4d828096fcf8c81596f1e4892d54d81096488e4c54b73c92fb579ddb4ce9014e963b71623151f44d66d0bc42c76727f3c1ce8e8f4f94b93f55e820622193cc9c91800a9c30740089945276cba6ddddd285ba58392af14952440335c6ce52e4f0b109108ddd48f639e79c73ce89a53c92e7e90d6dae4c7ffdba82f0ba2f735e71e412ec2573a80ce51902a28930f597e71ab2c441c835e41313d8d02501d488f6992977e6cbd430dd7246392a392f68864fb28fd91158ab4a9e248c01a358b52eed9a9846bb8957d42bb6a0601cb1e13699ac0d7d62d544c584aaec32511d635355a1abc246faaa186ddb3ab3b6b7296742b034dfd0acd6758955666aab1709565e073c2d833e7264a29640f54347e20c2901349b0911466aa18166048a2d68b0611fc97d7aa49768249d2476f7916f8fab1cddb7ebe8be5d88761d9c90ed9926638c38fc68ecec9eb28796cb30131ba23cd61128a04082fe749289a2bd83a491ec20a1d612b1859a7e620b7d88084b7f58320dbd4c2189f4ed2c456ca1a71963622d1c62cd23c40ca080a20a3aa0a2044248a1831c244890f8f4b060618dd0914148924c89058e13990faccca14492f9c0ce1c4a2448649034431259be590645999bfb210f6820f73128260a934155d8cbe201425225164d8276ac8462a2ba39196465108c9e257a7a7a7a321250613be7f280165f348f5944eacc588247a684953953c26648d896417c8999a8b89a1151fdda84c7090b6c189b0c6131c1ac41c87a6b96481aeb03e9543f74b1192d83886aab890ad5c407045fa062b0d288c61796437070e490a6e8a8740d97b4450b158d0000000083140000180c0a874342b170409ce7e2201f14000d8ba64a7e4c18c9a32087510819440c21860000000019001860b48d00628607bdae1afb40ffb997e327352707c5c08a31b0c6489061496de9c085263ce1ee420974353c2c846dddaa2970138d3b18366d0fbc58a44e6f2755c2a99ec67717316a3968f5c2933e9d4a07a9509afff7b4d8c0072a1b06556ee68ca99b16357f77514715f4bfcec3981594865e7c74ef684e03124c027f2215aada7ad1c71c1d48158e77b03cd4fc2c5f47fca94f3a3b503a6f068dc3a14d770f61384a75e19e8350a5c96202734b3c1e23cd2563cc4858b4615c368ac40e99074db6073aff19db91e526668a4a7072c223bd6307be64872dc40879a83d1ef899ce4bfb6b618c07a9be7434516b19268ca6a26f768c7541a9fac9b1395b16992975874c8297f399d93b2bc8546075c5dbb88b3094877b48c6ce5ebea85d175ac86f32b60086657cb4dcd95ba2c0381608813623b9345baa6bd30eb626d18c83cbb42ec939fb0fc90d535306a2a58205c5c511a1a5091ca9ab75e852704458f038fae6d9bf35f610fee3ae4ea846008f79f9c09d96d92200c10fc09486385854f58036b3fb8e650b2e1ed5cad70fd2614b82dbc82c225195a5c037425d75298ec532e34b3dac180fb07c17d37d9907ced04282a239fb43ab9e2727a1fd3026154f51b048ac9ef95c035f76782d4c597402d3a9d849ae540749b0f83a42a8d6d90c57e6d61464600f2a1f95574651e19dcef569e2995595c1efee95463d23adb4e1dc57d1a2de6e3917041a4677841e0d66b28ba49bc5175b51c8543c4b9f85fe166ed878dea8ab12655e8557bcb1afac9ea702558d69e64262721add1b9c861405efbf33ff7f7cd13d4cfeb2c795ac646914ad58a8ccfb8d15c581c7a7d40f073cfec2318cba85327ba13f730c068da5b202a9aaedfd3268591242487227c417d30b10c7591f0ac52c27b329bf3c217d124c148ae91b67433e3bc51c1ed8744d2289ab68817f53249a9e0e9ac40a71fd9266f51df2eee8a13697d6c8a226179a17e5640b9319b345329d930945838988a6d2b1c567ec232cec72f6ac0c7b9341589517e5ae624c635207b53de01dee30683cff6824f9fd8cb44cc91d345bed990a0c8dc0dd2545aece2162ac058f5990a5906c64d92d397ac8303eb0409117a34282b1c09f12035caa5027c31cfcc875681038f0e955ea9954fa93db312722d52529df8292fbf3ffac215e7c5615f635306ccabe11c97c65045162946e63c9057a3898b4e1b6782efa7c93a4ec95be695438ab73b740398d623f8d223a59820819336974526c021c28877a57bd7797f39353d1fff4350d8e1cae2164946f6acf09304d23b83869f0d0486503b7be90e812aecb82af5ca668363cd0a569900275cfce2ab009d49336b63c6a101e24932b70b82e239e16bfb2795cf7261e54dc7b53e49cced8d0e171a252640fcef3768e04be57755ddf9627e0143dcf5d536e1254271380209a0b18a602e8cf3f9c4d1efe12272acd42c954ed60825dca15a191e53d4a3a7385ba96ff26766a173d0794cc660df0fe26156fc029fab5ccb1262bedae6bde670e071cc74896c7f38fac9d5682547ace9172d764fed0504e834d9629ca27d0201d5c920e42c882c6485eba7d9224c6940e44220959dabb4a007a6eafea42f4b7b83cbf27bfc8705919388248198133531ffadcdcbcd56f9abc1822647196122029a7be49bc44c70f27d12df41546118e97af53939bf11e1cebe8340b1782d65e3b5875633abf152357bd10787073e6740d6ec3974ff5b4e4dc5fbab4a08ad7b963ee6b44f8f9e2fab6e2a120ac1ee66b8079d50d8bbeecea329d7f8f4cb4abcc816d77d9a3a407f0c786f636303394212d41cb90aab2a242cbebac4f6fb8b68f862c64f8995485fb4d4c566c09c6c928cbf6f4f4f775522e4f0671122003f418dfadeb5849d0ce0d775d16b852b500ceea511325f59d408aac02c3eaa75fdcd034ee001821539ffc9d73c79918ed6f981176616bd19494521bdc5834977e4466b16a8493e390eb3dfc251ff5ba78d2f1fed70467b114f50dfeb7969c8561238d03f4af733406d58a33b385cdba9062060e76790c65d4e4a652e92536c0249c3bbdf6cf9bcfbeb8a2317257cf0f0bf72bf651548189d29d586198623adbdb8329db4b4102da44fc92c9101fa8d40734f338817a3873031052d18e4e31cc6ed614822e4ee1fd7330e85259e1f6bbc3a49d534e4ab8b95882641385884e24e4cbf0f57a1551935c6c313910e4d24d957e54cd17522035c54238519f022732cf265b1fdbba166561e8f75912cc26d2389e762fc476b5eba6aaefb2cc6d87fb7742ab16807a8024882462a0b81f5f003e853a8af3760165530ca366954ac30601bb138321b6091c7ab1771b82736818a41113157ac72e446cdc877a21884d9e049dcf0a1c8ec227d75518a4baf78bfe9c60c8a51da32312b2bd00247b101c3a0c83b8677373f1b5cb4c527dce9df333139d690971ba4d263c570d21d7e14e22f70ad95a12d3710215d0c4ee0bcd3f475c2aa0550934d93feafd5052ab294d3885f34e0a33c1aff9f8442fcde8b8288da1144f2dc59cbd148f0ec47199e49684cc99fbf0f9d34cb47e86d0b42124188a2a9608fc1412ea4dfdcaad5a170a62428036542499f762a7368ea7b4ef9929636000e27ab13cde83b4c686b35f60d700d9b92b8c81684a068730b64dc00007587f7651d9316341b6707be0d78ca87c031bc46f60ff0a6a3feb8fb1b15d0a8b3024a0c20cfa818ffd458f5f386dd84e71df6b8561f86bfa9162384394a0fa5191dad7fa402ceb7eaee813d7e708a166012100651a13c761f4fa95355414d5b00e99116730ed2ec688d398b195c8691212905c3604e4b3fc171aa9098f86281bb1f2fcc8b79bbeb33a876a292bd1615ffe92f6875e6bd4e92e30350bdb859a6a6c30ed5ba14932b74540e29e3393735ca1805634b22e24f749396948c614e402f7221eb8341db72904d642b8e90f9d81b4c19e7dc8c6b9044f37755eaf65836cb796b7d5b2f7a57e79e95bbf458694503e43010ecb7e15194e2e5e8abc69d410e9bdeb47da050e3cfebed7bb28d6a0eda9dab852211e3f1c8aa61d4a6b87ecc2c0cc3d96200e086cb06a057355030262593a3aa3aaf06dcc05c091e4115da068d87211e456e48e8670f61537cffda54792aaa078bdb8a58f804948875811c155f0c5c6bf021162b14b78f017ac709d942374e81675084201990bc04711fa700eae9bb4139622beb02511e0368077aeed34718113eebaf255c0cd9e84897a0216f8f0d80439c42ce08b641eb4609550c1a8dc839c583a72b46c259c364135cbf95519ba2e6c07bb1126cdebd02c29e35c413bc09f3d2ab7192bd49dd8322d8c41e4246273c63db828c8c4a871590115d0c47233742654423fc0565dd3f745f59ad2800020eba82f74049a69fbd2ae31f4719b8744594a360c7053e621819262fee971119ecaee6417c5a2e40c984f8ba0511ce55283841a48ffdadde6034f69a0481da5356d607128adf1239e86c8060ae2ff24a230a3cf6869e0c7a1dacf47a5d7b523542d17349c9f5512b4ed8d9273d23684e6c953651c35a4aec9ad9d232fb29ec33f26213e242950131f49b21870cf5d13e943f498db245856e3e20631ef9e438027d07f35b8f216871591b2498410ff4cc5c2a49fd6cf44928ecd20a970d1716e4e060066dbe4465c5475512cac110eaa9ccaee36bcce727f7571b18b702ffcbf630e2d839008d363dbbb8c04113077513cfb21d616b9e8a57975615e217b90194a05f6e20959125dcc43562ac889ef8f15d87f61d35acbce8484a008f1b3addbec08bafcf7c5252c390ea0102a4d8ad9286f1c33b23890c77628a1f1ede59e88e41df705e8c7800435e2a33026f0c481eb8fe318112365407d4361c47c2fbc6afc9ff2a0eab5312528a3a090eb894341c9e9a56c496620f617703bd1dcd92d05b2eafc65caabc42ce362a121fd3c8fbe59c3263abf8dd979c0a25ae772e1bfb55a153566093f4dede7ecceeb347b07d90ffa5c990a1226ef0398666faea318efe3bed2be149e85db476684276718c4247e04f7970af61adecefbf5d971c639af891e5e079eb98e8e3eb27a8d1f540e37fb815a27205661236db8425d2c749e1a7fb1edad2cd9a9ce07ef05633e0be4c8018196f2f738e1f1db99e8a3f21655858e9309016c50007f9f38593bb32b81286d4ee4771a64bbc1b59e259b467a845383acd5d51a285d68e63e56ad0c58c1819459aca1af798c53cf05bf7bbea429fb01759ecf44521394c9f69b1c1edab754c8f752eb80942976c1f2ab15ba9f956d347d036d9fe6b1674dadfdf48c127bb2807d227612c209d8c370eb07c5297abfb93d671c7969004f132f4c01a378483b45fbe74bc9dede740bbc3a04be43c19e2f6baa03f662661e28360803f26592a234abb6c8852f19dbb10db288c75f89ced5494a66da9eb9e44d4bfde85244d64702f1182a9b867d22b0cd8690b37ecc1ea0532650ffcc3a03556e49cb0ebd03d5851b10fa199244231dff624088c4a9f6d8f622b14646a672e4c6fba9ea28bb27e6734922b49398f15d37ec9b6ef7f94916abb4df63e5c802d0ef67289a5d157aab46832c5f281bebba26b1228b94baf9bf8f8240c3088c86c6f089da104c7106f78a3d13aa6918e5811cc74508c82ee0b472d6e55421d64ac22350535c4465ee37cb3d1d062ad3bd5e960ccfd89905b62684f9f063c239536c8f370964f866e7715ad2dde7f03729dfb203a4849d3320dfe406bada3363f2fe0e9de3d9a1928d06932471682d598db0abd0e28bff96ffa41b966c8b162e335c1ffafe241bedbdcaceee3f695c9bfe9381f35cac15b0c38a578a0da2760fbf93f4eabd2d1ed969caa04709f57dc274de1b35120e30cc68825660a31e2a5134f749b2c25fa4273d21a51c7a8aec8987a9780c1a4c69440a37745bf360220bd646fd06a710c3f9be977b793ed9a10a89372cfcbca3175001505b4f2dfdb4dd6c00fdfc623299202b95e1492935e4eef5241c75919cbe1d62ef7d49dcd22beda0da22c26db1a98c3250207254f40e2ca469788abe28e60541c6219357e7cb6058521b352756f68d28c883f70310fd94e01178ca2925848cc952f7b55fe3f5b2e00592b4a3e900032d46d1bbd40262c05a78ba3619e26aed24bc4b681ff97e0515e909e26fbb8270a4c9d97829df39d22f1036834d31a7b06a9a398f6018cca5a04df981a30ebc332ee016527a101bf9f537ac8ef50cb5bdf83d409fb1a96d72608da24b9993687560c33879029266a53b397bd6621856e510280ae19da8dffb1da775a51857979aee9c1b2812f7287873e8fe6b8fb3f95dbb15d233a1f3ed0a818ff671c3a395ffd9f954674800c3af45f2d94b8443dcc1f3104766083b71082aeef22f40e8abab5208af1f79ebcc19fb841b0f3c0ebe5ca636a3edf111e79404ff15cdb62c9cfc5a3d39d76d5f8297a13fc11d8da2e40ddacdb6f4ec0b52bcf098d0b544405b1b243131c23c0d48e9fc7e7c5febaa2a2875a8e5c421045d6dadf5b904944468f5a0fa93a71b5c41dad3fe19fda92723bb4dbd86e4f7f3429e66fb326470daa4d047c3eb23c1abdeb3bf91a16b67e0fe3ec023f76fc99c329c2ba05d75812b313250beb0c05a049c04dd74f9222db0bc29c14a2fddf76a60012114f913498285289cf7f96149c1e66bf8ec66201a80ba7b479d478ac6fe18aeaeb96a692bf36547c78ba32e97f59eb52fdcdc7f1cbfcd9677cb896fac05b718cdd3492336468dac9da2705b63d08ebb5cc8a9e52983358621c5c3ab6701d80e7480fb10966b6a3624d1a1bf94f7862477df7abf62af5965f984398216d8f4930e3c6160c0b9c9bbb771c5306be56a5a10b07973207dbb42afdca03c045241077a37151104085067f980f54cc70544b16187d45231a718018fc8131182f3c429908e426e7b8ead2439d1d4761b4d516dc89a71b6cee25ca89b3083944b19268bb4b51a2c3d49c020d9b1d1592297553a38cca8644e2231983fc526bd7f9a15cb36888f5f08a6a8e21df4c959b1f6b6784d8dbf7479821c47c27917dd804b79cfc96986ec1df61b8bd04d7637ab6b39469a773acce47ac4d34f072c54466c978692a1e3c85a7975f90fbba33cc38b257638f1069cca3b47ca15406fba6af7e8ed127de7f3074331f232fdb18f12a86e29e7f86ed692c710c6ff74e07ee390268572264eb0df42e504801827821136c2c20b0cd670e9e92b5fb730ecf6c6abe183253916875f1c37c40f035befa1050afda8adb3fc28458aa113810f2922229e229f08c299d4a8a8ce0ce1866d1a0dcfe7bb27f21acf24c43033d295ea8fa097b94c83f3816eb19045e6b1b39ea906d251993d53d55480db661563b1ec6b0cb36d03e55a016cdb89292da34e08e179e2eb28818d95ef5b881cd035c3dc559934b9d8bafe4bde69b865cbd9be2ad4ca7fb234274d95a2b7325618d106e54d8b4ee736132f0e0854727c15be2794b37c0414a54d89e60cda5c7189f213a28025646c555f2640759771a6526538db1c2a41c3f95e6e231ec59e6e0c64a3f9d590bd703af29e7546f8c8a4d680e8bc4a756e0557bd719aa0a885a4583d9b1653f6e01fc9e65b225c12333a3709809e431f8327cca9efb862253c64ca2683773eb78eaacfc1c07bbf07cb0b987f0bd82f2ae3ed7ccd3493b4151d7748fb640b8998478008edc8ea9c746ffa54703f235349a9f040e6866b1989113d064d0bafea15a88625ea7d09c923a8940b74944c73bbbdc9ca18a3ce4ca53102436e24a8bdbcbf7e2b9ebe3a9db201bacfee7a7fc532af8e73a1d7f5917f63b52bf9c36cb5da2c5f4c8d90218b390ad98579f7f661c2afc818015eefd707c9386629e8f4147ceef50e4a4a9de77a574980947bd891146c0f88fb6636c1025c3491ad48395fc1104b4f819124354c26347a5b03230cf0c0eaf54269bfbfbc7cf3ec23480a6a9909c1727db3c0392993c332c9efef8565624840dfcdfef80681c5d803009cd2abc50e0d33a5c120ef1b93ae73756945ecc203967f25598a90c845c2049f5fb8da3bb952187946c820faaa7bdd84f24bcf7d6b3cb630d2302aaf334651025875b762363f3f0d896c0fa43bca885c1725411b32619c47927d7cbecd6e16e4bc216bb0c6e8fd85fcbd9786abfa4fad9f0803ec11dada9a971a319c17e5f4266ac96251ee9a30beb28ce8d6cdfbf7c25596a48ed12112499580306b3425989eb52ac3439b96916467af337b6d6831de854af6e81884c80e187b9975ae6ad9cb9779fb310ffe17c9b2efdca7a835cc3532883e36c444b9909910108a65e033abbb7b050431f5ce27049108b97f4a3d8b5efe5e2fdaaf340429b8e4c548468f1bb312b12832c1b8dae39db1a438b51bb4ef492594664aee7e694c278b8a7b77de51fabd9fd938265dd22289ad49c5d340939f9e950a7e0d14473c7448eb6bc8ad2131223cdce37689592259ed4e6dd0e32e8a6344f88928ff66a1f5136c9c4b980693be0de0af7fa5cb0919523d55078a17eb8efe8f6b5b1965d35f5ef237eb8e46e2336cd437c57c892393259f241705b7bee0dca7b7a80cd073ae8aaea5d8a6dc89dde5109be94a2ed11b35b4d8cda6f8d61c055492e92517aaaec534ed527c0fefb482c8a2288de0330d6ea5094ad2838aede9c554859fa0c22623d18fd120166e49ecf970e63a1357ef70a7c830a8ca64cba4009d94639b29e6c95e3925f522bcac6b91b4d5ca75ed383f4dfb5063cb3bef9ca551454c044d29f618100a7ec1a78ab22582ee6472829230fbfde79cd54fbe284d893b5e5e2742b60f43b91528f91a1da137d64a4a9585be30b30e8f77eb1be799d8097a8990c6ec04c9d6f7f1f99e8678cc4f21a26f519aa3c3a16091fb9470f882a95957ef828c85c6415925cdb80b4b08b706e25dfacac1748d2cf3d77f96db445be5aacfd4ca5e755606e2601f042c45787794452f91d7a91bb4f4c5e42861d542b75ec6a10017436a247dcaaffaf751cc0389bbe19a40991c3c33ae166f0617d0ee1a013c49d5c4159f1f068f1456acbf894413f8330b027bc5845d93c84a5666e2943cd39b0f4dca5e87e38cbc53aa5e904d6ef70bb21f0b6b2b3244161b990d808a66826d04eb60f50da8d071d7f47e4a5c3fd2d9f7b4e1e0d8c0bf8a96dd7bf4b0d6c10ec1fd0aa18f524d97939b2749e4be3edd4a603afbd8be4312467102d1c5ef0f2107d514bfe681f5d5b64702bf0d4aa00643d1e13b30512ab8e61400949639ce12063e6dd402666e3ec437a24aae3e864835d4fc6fb38542625f60d5ab26bda6d1806e4b70f85b872819906c3a43f5a23c084962958adfdc1eb9a84ac70d4ad690306ac0c33ca56a76d1bad5bae88feec72256ce13578550e1a6b895ad964073ea0c2d571aced296e48c92e1fb358af3f810656a0da77c47a681eabe1c36069b4c7b0299b0faef33ff828eb1761f7f75e2a9cfe921d882ee73d262050d383335e924e0588ec6265f1b380b7b3957ef0c9ba0af2a51729a0d16484c9263d7c2a8e960707416ace6a1f31450c8263c3531beb2ceade9f05737180cc2723685368ae1302cd054111ad9e82bfb0ac0054c1946e81c720c8f83345d749d29562debdc520d595dff094aecaa3b71307017e3665c50f5044747a4116feeb6af777ac391d60735d04739d6b6ffe42fe970f01aedf0d308d965cd4bcae1083d6b1fe7428e8fe6e6003702571e299da69044f30f6547cc7565be556c517e9680ad87fbd339b48bdd93afa66873ca2f0e3355801d64d7659a050b7704fe37acbb43a237bafa8f573facad95cd93aeb74e850ef383f866db75ea8672014e23d2a1f217e984d2e8c1ccf85dfcd9837ad3143d3931867fd56d91f118d5888c04267a79cc408890d8739bafd490bc3021d5738497ebb59952fb28d18627692b51125a36858dd7f121ba7b8e2533fc70e9959c0188057b24c853c5597cbc484fcc741d16d30d35a69abf55471ec44cef6a7c28e04134105488fa66a503d20253310828d89aedd00137d1c24a6b311603104696dfce1ab0d69729d676acc8613b8c2e718e601debcec66092ee4727784e94355b6701d42de6e0605d90a983c6635a74dd9027bc5f57b969863efc1f6328fbbb1c8f2f1237382bdf13c01b1a68729fddb928b73a23e579b9d60d9f6f6a13348218f7bd20819e9a9293ff7c2489e4a69f98f77b12eea61bb57df466960334794f41589fc96261f28e7cb3c807808f18adf09c4fd3e6dde7500590712400b0d3cdf905a24b1c5bcf9a63134aeb1827d71debd7cc6dab84187c7e3c379847de41a796567144b00893d94b39df68efb733746b0f8028fa83640f158ed8b40bda609cb7f53436cd55f9499a6daf7662cdcad645e721b0813e3f4bdcb57fbeee427ae10ad2e9ac1e852588cc261603e8b984f5e4e94f3e97f9d8b4f6bb44d2b0ff7e9f93468a8612a24f0da13b94874f1a0096eb23763961a99116ce80cd3cd1ffcefc8ca8b33100cdca2f4b0091a4c05800a4a60914debae28d5707451154a325447d4cdfd6eebb9488225ec69cdc962f93b7e1f952ef5c6ae8f8326df8e362d6c1ea2efffd7579681734c23b637824883943b588a757eef5de1181081c07495c7c3ac01092c0baf4d648a54adf7ae347ff9c6359c213b49a81a795f64a14400b5a8fbdca5333b80122195d96709f8c9e3042c2063c8587e3ce9fcd1c3ad0527144b2d99c95cb1966b8c2648c9c11c1a30ad7c5bc46dd168aca9d4527466b2862991613f10fc96b8b225b8bf5a0c941e0a7a47ac3b19fa0f02b888ae93c6afb0729b52c1a3bb038a8daa25b73695d8ca4a960331a65267c0c13ccfa0f234134ae8696bc7846e37c744859718411a7658fecebf6c371d1ba114dc6d7e19e92e2bc772bbcf076fefa78ed5bf916c7431490e0c49801d2eeacf3d8e9f6bbbd2673c7836c4db574eaa25fa0626b82085ebeb8721e98dada40716b49bc47c77ceb46a40c6ed8e575e75c3f96c3fc186e328050d687e57fea9586bdefb1f29b32fba775fd5f7b2bff3f5f109d243b5f45516ea67de051c45719003d14b7940c4f5ae016faeaa00e7d9cf900309370f48f76973a6d41817cee190c4377c18e37639673936e865ca289b498a64f1457e8ad064151e87ddbea0268786fb15624ff1b5027fcedb9a1c7919856653a2fcd76f5286965c6b5c284691e94edbc98fd19cb81d92b1f11f9202464084cd3a645814e0d0568396e02ca72e64709c0fe891678b2a280d78ec435b5e65cf80faf27b67b31ac960098bc9f1539f5ddd76829adc8c84f529230d97a601ff8831dadf23a927826b169b4fa450678ae6876a81b05168381c21ead4a65a8475b657e12fed4eb040ce8e4358621e61cbfd79762478f948c4c98d90867d244b10dd3dd917c844f111f683a39922a2ed0c5665b1f40c17c21c4f5e34d4ec1202892fd68cdc390336fc7635f067121eeff5894d696bb22615b9ba04b08aaae208e70056b5cfb3c7f037176951f0b42719a1871ae12635ec3faa2740cdf1022a6c7597d0d09f243a19b2320c4e4d94d6e1bde4af0e310ec4796b178af415b80ba4780f045db65a3b8484f65556d88be28624466a675d6ed55844afc335a9b6a35c14d5218257b5bdb2bb28f481e5055307a4792da78ebfa5e5bb1b8aa77c55aeb27c515ce57739dd690ed3b77348150467808aeb64dc2252d8a8cd83222b599a173363ae9f40c1a48ec84e93c0009f2acad20fbf9295d03cc597e28ccb27022f861b8e0d895b28d597ead89ad120a269cfcf71567f984c1e29071bd6e4a46f00bf793a16770f899f2c5080287495df92945972b825897c96237b2d42beffe64621e9068ea3d83d6b75930d57d40ea0fc88dd22e5d0571ad8c181ebc05d0ffd8c7aaea29eadd323c57b405ca2c5045ff53aa3b12870b42ad5af5c69376ed4c128213ae66dbd9ec3d4444fad85a7075513bd444e0f8a7250dc0a3741f2b4af687ca3251e9acea3a5687e4748f00a06e4e490e088e40ce20b732a3e7ebeafec9a6711db1fcec9333660171aab313aabd25696dbdc6443e820a5f1ae43a3a9f411b321e0ce47ed072aed688ba36cc6052fc231427974a93e83d91d25ae8d9322abfbec95a875df97fcf56bc679ac7f062584d3f244d6e28bfaaecdd78c8674e88b55c2c97730906c3ed27d1cb97a1566b9f0f00ebdaec9019df914719de311e1a98010012b04db445539417487928dda744d5a72daa1986921f625d0282e94fb8e671d8bcb76380a8d475df23d3d60e7bc83e2361d860be83427592e15864c454eafee81397d5a601ec49a5fb5e79ba18722bdfd556f7978c0afc7bd15d0b01b2c0bea72af2383dafdb9f2729b2d976b71f28e8ba86b8c6e0bac7febf26b6e2550aa2810f2efb39781981ce990dcae355caef5f80f62824cf324f901c8011b404c708997a1ae19864e3f11e050959ec6b15aca471b17fd62a46ea7625f4e1b01cbe02b3a511a185960a715306f3b6658e3fb2fec74a1da54ae2a8132977bcf40759617e3094457ad059f36a2446e1f4745ca015cec3ad6f1af4c7c899d258af398b07c484b9040e249e0ec07d0b7cc1a4a405824a1e440cec3814da7af763aa0c42854161e3979b5d74de5a4449886ff8c36e22859072351901993ca87422fe7ffe4193164b185346dd82b89a2f7ccf60237c3bc52241cf475fa2c073fd639869f07a89406f75bf97f1d6a2726f1f6e065e382f700c4b13977b02e26542bf9608668310175ed9f69ef3004ab41a3994319a9fceb397b3eded87c3942df923a102e913e92d9ade92d6e39c7a95b3de92eb6192d5d3e2a9e81df30a9b807a51a4ee4e89afef2438bd146341b5b0c21b863b821b0a6ef4c36d104ea08f9d294e93059400bc9e9fa5387ec31f9f1b81a21e331fa0b96054e9c554f867dc40223af5cba662018930334467196fa3ff599e0dab1522c9beacf43387b0cc8b34f033627e81439d3f8fa5ee3729c3f57ff6e937d0952150ef75c728b3e6d67bd9843482cf48f9aa1d004c24977b56aa024f7518ec7d283e83ba8f7e511f9c0f50baf1368d791f805f52e6b8822f98d9bf91bee8832ec104233cc007700b6cd8d8852a9efcc9ff5bc8fef8d4041207a7795af176472fb0da6aa9a85f8502b70b41c2fb70ea286a1424e1c84537da386753c3c2a96666c521defd8be2082768e90f1109e677c4c836819cc88d2ae85cf5431a0872ecba05c421d0c3ab473c0e2c0f7c63a9fc55839a70b0b93e8e51c25432ef1032f8eac30e43e3aeb7df872e8a87feb378637f7104746ba3b77e1a6b3304ce5aa88ba42d8532bf064a911589b8eb8436b988a4bcf4174a2066e714d105a6cc8ece993df3a6d91551d858603c853e88fe1ad59cb64540e0ff0cf53da6258c87e5862b8d8edd13fc1e4713fd16f2aba3bbd356af62574d3c0139fc55559a6066d908551a34bbc576e5cb688cbff6acd00cf49cbe1adb5c4397335d40b0071e247c903eacc0e4a68ca3afb62e4c4a650d775fd5e4a4dab5ee27883ddaab2a1c0b5dd7d1998270e37ffed63f220eb1c6d9915a78c5cd2b3954c3bf0bfae030874e8fa17b5bb59c141046e4c724c2e20daef4c82e1306484923990bf7185a4bb8c2ec5595accc2e99605b865ba8dd15e0891b57da8264a71ab3fc267e1a32d2054d1817993d6b596656c65eb635a8cd0c2bd255b381dd6ca1d73753f20b59178cd1bda4d6a612fa15fa6f62419f5ca5947a5f666d32d790e8d60cd064373654ad147a47f401cc37d800311dbbd2ae7dcfddd1f2f8d95de384d6efdefc55e3dfff87a111195be01a1b59166d8016ac40695e8b50e30e34f1ab02338901ae5f101f011bea97f90386dffbef87a6de33b64b6a6db42cdb134d1505cea1aba117bff7927b6d32c4d5720c8182f699c1d3cd073a68e6ceeac122c96d1eff180ece1303b0ed8c254501da78a1c2f1abebf8e9124fe81bfb08c84469c200b715eef0d7ffc7871ef4ead83eaeb961d9602769c104f6d37284f4a7a5a9cf13596bb34fde37151e8df0f21970a3be913f52fcf9fffed0943e4fc59275d2291a87cbf3e71738b359d5b7230754b68a4ce85709ebcdc98f4e6f52e66a02f01daaf1d512a416a375982d138696206c89f31568c8fad663078d804bd53f657de6c7287effff7de84aa063fbb8e646cb808da45d12384e6b84d0adef87febcf70c9891195d08d4e6bf58d1a21ae983a01ff8a0c4b1a59970aa892722f4976139dd51304fd15b291e05178d4a63ac3655845f23bcce9e1ed9eec8026ec74da1fc3cc12fdf59df342160e1829fe90286226758fdf284e4d6e286b1201e2161c1a7534bfee7885062c1960d390de077d0ac3d5895d8b43af33c4287b34e5fa7841ba73722972d566db4d9fd28276ae28c3bef3236fcb4c6c7466057fcbb73ab9b2ff250df546101e259bef6b91b9bf6fe354b45660c5b05c36bf9e78c7a9c7269cd1ffc7648b285d729240b1c183bfe36c9dd348def7b44bceaff1f1b43ba1e94878722fd1744e24097a2bd67a760ecb8f31b1d506085028f63562b716581ccce300ed36f9b87d78498bce9cc63ad17c58cc6da32bda4a63132ea3cec0de5534c770c4facdccfb73f97245a2700929129aa3e774656b18ce47f44f5ab4a89ca086265493e1f45abdce1cb727cbdfe9dcf90da5484788a7b330c3d97d01c7bb8d4e051b6a87159079599b4f04b15e7f08ca4bc7cb2128d018c2ef5ddf4517ae5f35f05bfee049eff138a9bb8a717997c62ceb77d91cb9b1f517c0a5aee084af9f7e22b586e210597d33a5d392217f97e95cbf30337dba09ff5555631668a87ece94740ed49f475b82647209a400fba51baa9e12efa475ad5a456359be11f2f9e7180a61dc9943d5ca236b5f9b4cbf7fae8be445cb1d189c310ae8275e6b24696d1ba5a9b2c12ea22bb10d80438bc79af361c4066492b3d4ee12ae3b1d03dad55f78803a395e30573ccaa678c54cc6aa92cceb5d90d5a90d4063f7794f3d111c8a413178bbe4bdec5937e668268f2b3dc8fd5065313795b6a25167e3aec9de7c9bd1d1b10b817bc2c34c375fc71f479d3051bf6b562e013f9b0f99c2f67a2278e4cb75a7d2be0c2075fc8b5f33857a54ff423922f4acd89f8ce79e3ba4fdceed7719ab91fc361b647b79dd671175dac08b2e50f0968619bac5659c591e35c8f6d3e9a935b8ab8d5d82e0f57bdf9fef91b9fd6c0ed88a20571c619c9e5eaeb955db03e94eb2b1a435876d38f8f767c4d0366ffe8963e8154ab042fc94f4001a5397150b12e169d154930ca42980c41945b31025369fc40b839c275517cf4268e84104cbb83a5e85478d22181537209e56d0d37fe4af4d162ae371aaf6f63991f8233d77dd2a9d2d66c9391cf7f2af6f972b82237780303ff0acd31be0bd5eb1f37711868d430f000d4a338330d917ddbc8f059abdb0581005e13fb645f1af29088ddb3457d2259898c1fc54c83b94bf2faeec7ccb34f40df3511c88549703c0b361a7b0c3261dadd19f960e093f01abc235983152a9e14ca00e06d9cd19220170d90b8fad7e87b68a6b64611dce2ecf05d54ad871dd4684cb57330e5c0a8b0350e1610fbc593125a338fe97949ab427fb3ae97f33d08e12e4d3b74302481691c06fdd3ef1ea631b3cd7ad9ea1dc2689081e4f609959359d9c5c4ce268ec23f967cb2ef53af6f9d04029f05f7fda2d4a22646cc26cdafe5c0892012fa0a6753407fd671c1a59d73890f6481300c04121e5ab354448bb34d9870c1dc3d69c75abe124010becf4b456b444d0921ce64463ab12e933c971a930ccfa1678c86b9f7ed2eec857214f463b43989008a5c165b0c24cdfeb7e1860990b7135fb8319a7ccfd9a1104ff3ad6b5d1aa3ad51a734781d1e33dfa5cdd0bdbee9b26999a3f776fac94cd39ae19914e0aaa0744eb422e9b98d84ad5fc7b47da148c88926c916331a1d51c616352cd4111ba3f3d77949347b844432210af0d53efeb1ffa78d30b3820a08ec9913c171207c56a86a4e3ff364a4e8774fcb1c30c85fcab65f4ee540469e1e85b4f03a167940cb6099cbcd566e6197dd6f7110bf087ba164479c5fc203fd6c4661836b4dec8808a7597d8ccd43dc086d3c0fc87885546b21b1c58df4b1409267f82b4c196d93924b4bc5885fcd17d97b2b751791fe64fa21c1044c9b905737769917efcaf510a2b257a2ed5d197f536a009bebfd82c5a3c754fa5f365daf2a075875d92c9df63dad3f2c8dd977803a46c7363eb45dffeeedd59bd2fab8dea7a949aec59f3fcdb58610d1ea35957e249b3ffcf39491e4cf50b9fcb75815cfa3a6d7b6eebfc61e8b226b054f1347bff07d3c681fda6709ebb1f54b5a2eaa087c7a48ae7bacdd8a6012b8e866ad8e25b5d271d11a369462582a686938b79162ca33b8581242c40f8e623d274b6b030808daddf556787808c28148457a3f4195d5bdd4c60d8ab9d2add4fb49c1d5ee8630813ddd8d4ccac2bcb5d479db2f8d0756acac1e4b84db705d3fc54d0f78c16d343ea84d8e4f6dbfe64bd6ef6982dc2814e19dea1e8b442784e86bccf37118343ae302e95a26005381d43b4846e5873e1c815c18f456270f1c5ea4fb9601d01bffe4718745561397f3e132f14df213d42bae99ed8b13c4bea4fdbe026ab82c345d19429aaae2c00f78dafa8a9799486c4faf4f487c91b1f4f0de324b9f580989d0631f4f74cf6d63f9dd727a94a94db30029a06770f11be353ff5e71d3635d47592fdfb1bed6d1cb940eebca73c01c0ae17d7b9b803c7f9ed8b1e333aa6deb53a72b804843a31d10fb74375d378843ede6186da44ee83be5e8de78db31e58a1180f0f7957d4e21e9c7e5ef046f6e2bb291205ca674ea22915aa36ce9faf73a856b63e010be1d8e9f5924f6668d2f144c3f3f35614dc808101c0793877b3f53a7f8b96c29d2c7281f45450c27a3b6f62ff30bc51d3667f1aaccd18179dabeff0746117c47247a905a4b564c4bd5d1e5cfd2b9667fe657498026ac77dfc22df02398351d82f4982f7741a6fc9fc790bd3116b210c020d7941f7404a4733e950453dd04a8e9bbe48f3c2eb3ed7def4be2f172fc69eb551431ab0e9b530c4eefa7b5d5e42f7f26c7973f3eee7dd5c65becfe7387d9b2ee3f6fa907d19c2df3ec01401debfecb1d266eec8283ab30401d17d462a667df3e920ffe9af30e2259ed8967a65921988c96850e4786da3f083449086d50c02b9593441bb72ba7c1b66455dfeecaa1c79f021e3ec84c1b580d928a2e3901fcc05c2f43eca28fbc12c3c8a33a0ab94ea3e53f52e270c024494b9010c2fc945c5e11b3641c7112420be99459fdc2cc9fdb7336bd5e13882d6c2954819a04a05861710c4eef55992a978acbef0e040a0408c836dbdbe4061a366c36fc6068e8bfd9c7ec889af00e72a81109b0482ccf257b660ee7b5d20f85fa8abc3333ad29e1a70f8d2221f6989fed0a4951e3d663374e388ce0dff6fe171507e53cf8a8fd3c85db761ed251fb599d7d7515a657a0e2400f20497fe7f51fbd8ff9b1b8c67c145738ca58c57b2cf38506a243370a3c71fa9d5637a337855a8b78cf610613c94c419447526ffe84d9226c102d2f29daba57a780f1831d10264baac003bf984b3f47f18e37c37cd455781d7b3a6de687af9dcf18ba8086f1228f9c49dd73818793ff97715ba3dc67693fde399a8c6f0ba498ba420cf2420135214cfdd89011f710fdd80af0f288f4061b62dd24175967fc9045203f01e7ef298092ee97d64b8db07fbc335acde38ea0e201f83185a3efa0d87aece6dc907365070ee5dc5e0e7c5034e9fea5a804ef7eece687d6f088e7adee2275b2b888bcf0d510b3ca98d550fcfb7811306d8c9bacdbe6109c66355965bcf819a678996009a8e8a10b41176a1e368bbef54ff0a128b2e8c2d4a52b31243704101d71beac519ac3f10aead0e9ec6a5e4550fad8c54151f35e5e4b5bba53fcb20abc57c413a2607db266d6b5be8511dd1ee54bf7c83313acfa2c606305f0c701fafd50b58188ffbe11c71f50875c5e9201f520aa7f0b6a9dada402fbf6dc3548b15317fed91cbea69c8fa3a1f426e206c5bbb3f7506002824bbc2b834f28bbbc6689fedb14d6e14ecafa532d10b8f328a1cecb28402affe93b5664f5302a76bd085d124d656b6ccab8b85d1ba21c287d68a1284777de096be4ca8f89e98e4ed65404f5293f966d179300708f97c7c2ab961d5e1202afb389445add2318ae66b554b8d91332a782e4e49e6a473fb79e8466b34e16d8f3e4864a31e53b98840a65cb50c01c8030c7952966618d8a530cfbeb29764056f2c168efc00774512338169e582347617ba91bebdf60ac71b8371066e1b3f5573d3c5df5641f25b57fcd4b572f8a5a9c9b5fcc57ba58778653173d5fdf71aa87e8a8e7e8a4baa0002246c176efbc41d425ba12db094b8e64c706865931268c3330fad6effc93ae9e662d2b99ce5b51c1fa265005f25f15346528ba902fd30a0e3066b24608d677a850a14373b904e1c38412ac7fe332266b245e166301a26db75baff4a675aacfea15b9c64cb9add13b2ceb5b588cb3e7732a8546d53321002a384013319da24ae808d02b761033a6ee1fc57d430c54b4a0f8689c344e9ee9e5a383a832c54ae5e65dab3a16fc56f200970aef5687587c055e35a41d4af49dca9425950c7b03fa04eb8a665c34c1802e257ef3df5b002ee01a709584ce6e7c230ddade4691b50622da9d0f65cb4bcaba29b902a2da60eae7a467a9e71ce51632795416d279872e267b26bbe98396b9ea6760459fe23eb1af443f024f4b47fa5a0b4ec2766ba9e5ac8701ab033b536335917aa21024947d324c04057eb812753f8b77fcb0b0d490cbdd5c487ff0c1b916fb1620e4d6353873bc7775b755626f827352dd3dfcca7057d05e3db356f8519708239e63ed51035742498451878dc444eccd3ab5775b6450cda8742de5f380548f5e769fdab294ab3b4356fd91fbf0a8bc10c2e7ad3e2e55c92156d046af0571d9da3b28d460074e35478c0df8d7902e12012f8c61454b8399232538206deefedf20b0ef564a4eb504bc379ff4fbf80e38d5fd03f37a8cfc3b853f7546c879aaa6d21c99c9962555dddca49cb2715cc86f28fc868ee023dfc724bc89f075d7af1d81ee88bf4fada419e2e6c016fcc5073369c186f4427417e150e2879c86112c13f61bcb9a97368832af2a9e06f182ad3556bd93b434f03e8bc507dd231d7d56de8a0de4474318f9b7a613ddb4357ee4b37be0333d0f2c8d747ec185dfe7f69b9084d5b6b53910b01a74caedcddf79bffd3f035cb1d900a38739d7f45b29e9f7418fa07353f7a3ff07025d3fbdba2621b6c7fa0569e0a74292fa6d792af2b25f804176651387b73b181ef324aae94168a8f4f81d318f06f9ffd617de22821bd28d1b9017c6e7bdf0ececfbf18fdfd7bcffaf2bbadb522714c3d67e7aa12bd20299c20507abc7e2867ca6b39d0c253ab94ad8ccc02688fc7a887f802fe5f5a8c284a3be640362fa8af2c55a9ab38e923c4246603c39521a751d04930b043ad947b5022617dd64ffd8982c3dc153abce2937d5c544994cd69a2d54302e69e97a99e9f42ee2003dee33c67545c6b4e57b6284fff0344c7dea9bad1f014b335d7d39e2a240b6183e17dc9a0db7c17ba5cdfce15eb1f0733d059251e390353211db06071e909907af6ffec9e52e02eba2def6e40e340cdd5cf4dbdb1aa9c3c257cb22930936376b16ef0507963a54570e8f02c7e0525e83430889f33ecfb024ac44cca9937a9bb746a8ccf279e5d7f748d8558a774aa7e3f2803f86b0715d49345fb36724d06de20f8b0606a18451c662bd35601dce441b86ef1907022ae11a7c87341f62d063e5dfd6be5dd1225101f5412d340003248945c9fb9df7fa265e84679e600a1abb15c0dfbf45c050331f43675d8ba296ff00530754fe991a46137c216298df5706588bfa80fb779ba20a3f1f94c14f5832e6ee1afe190a1708c773796878f9cfd4b5a6f6ef531a31ce784f5d531c9ae1cee7c6b611d17f328e69953199c48cf902f5a206865b5ebff55090c66710084e8f0372f80c51419a77d12b388c00cc840428809a6b83050a2a660e3e5367badc8572db4ee333beeedb0139d72e103c9d9a03ed7bbdca9d9a06f9a17d4eb714cee7ed62df778772e8a81896454a1cc8f58c34e3fb74a60b19691bbfa238691c624279862ce434b8ac9e786734e2fcdd8acfc085860cf992eef37b5def6085accdf6f284a824984cfa1600ea710114949735afee0951e9fde04f09761c5a8a6e9064ab8f69bbeebecc5ceb070af858e9837c0747e4511542e21cdfc99556051ac94fa44ec01cb2e14b5641e5e33a229dc858246dce5339f74ec46d44a5e1dc8b45cb5b51e075c6b40d395126c5883bb87a4c62cb13c2b7d9a25a47afd0de4122c6d15cb4578539fa51a43f1cf6721c770f8fa4e41db134df9fd1af59a4b74cadc5c3420aadf2e02024780ddd1e320a1af632b002aac3635e75000feaf41b5758cf62eba81783bb7a3dae55af842964ef46d1747390d3f1c80e774e42f53fc760e7da7f097fc231942e06a91deaa8b8d18316e7d610a1aa27a73a58cd04381e30222c033c3205769b4be4de8545371a9fc4b2626b4b165fa08a7d0262ef024a1724e06077f1b54c6c4ee5dd9dc6f3e6a22b17df717326f689cd014fbf1e14800f14e1d4b04225288265d303ad3a167951599b43ae2d0b0a111cbebefd654926a4774eec0d3b97288167f6bb27e0b96a0f82faa8b2e13d0d4b8301b16aada9478cb8539d3da5797ecd60206f6295dac05a809b266ee39965116d346fc8aa4e2ea0964d18336412a14d1fefc763590d972acfd70e39900602942324b7ffb2142a1abc461dd50717125d06b953373f88bb64c6f72c4d5ba14a740d68733a11c9a22f386979ceeed2653c5d5b13b61b8fb3c67560a0a187b443e4fe5aadf7dd9dac47390c4437569894e779df2b5a7f57d6a4ba449ffd8ac9438e11fd3c88bd327378b4dc410fd3c2358455f1a9bec3241dae84b3ad5b6e847c370b47fe2d82feb88387887595ed99bbc1ce21dea7b189e98eee403315b48fdc1dbbeae94f72c9cce4d8a6bcf420c57bcc13265fdc9ab4954f52aedab343de063298f3ae6d2cdfb5ddba3ea6e27dfbc07cd807f2f2b817e6cea1f9be30dc53ed779730973c903925c69df96ee75ad45c9e01bebddd27b72995eaf50f70be3c6eaa85dfaa490ec1aed418531475c64231c2eb5fbb9b070da55a27191703502c632eff3fb6e8fdea0fbd0866568105b609b9806587af021b0e1f2f827354369587d78b5e7a7db95e2146887f4d4ce1022c670f269a2abead3b17199fbaf0ceed703e06d6e56891d6ab642782e7708917fc252d0aa663a8c3e63d8b5deb91f1b548e56c0aa7192e82826544aba0bc698e10cb4e9d1c0eaf9bbdea40ceef248bf896f25b40c7fa8b762d7572d197f4ee686e018bbd064f29f2dbffa32ed90726d183e05cfd02a84767336a1fd7c23663815093f2ead7620cb68f0760f0c2d736c9fa79322dd66f5068770fd7eaff4da3a96c887ab32f2d00454efd616e464a4f3518491fc0c24d2dbe0acca9aed5fdec2becd6910dde915dd61956685374929a46573a2dc0beeb81cf90f799a1adcd1324f547d5650978a904bc17270f8dfe584f6d3e8c67a959eb2ab7b952bab14511e45922a05547d128d1ec686145c53c3ddeea28a36bf219e18a95800f6231f833e113c0c832e21bd480e6a906eae3cfa060f8831e3856be25caacc7f8e24f9d68a18cc001d85722fa9463ac83eb58945d778c474bc1713df6a0b7a00877ca660ce3842b31f2540288ecd9f263d6d4c5d7739a968edb4aa81f7b945079434c09050ccfb2052492239d0e509590c731dc1ed1596780c94cbce128954f3ed4b5f2f16a280d1c5a0ed3ef01dd9ca46cd0d9930076c4a36f95d15e4734f7473e9918935b63e9529d79562f4fd9876127d95bb6c3400fa97d2b756856e6ca37a645c25da54aa1d785afad3c78f5763e4af2e1b038c02e3e62dfa549d230206f6db499367703bd96645701b1b82f2dfbb5fedad839168767f3ccde49841f8d901be51c2369ffc9b86a7b57b58ea47aae7996c80ac16beb51ec75a8fbe390a5f1d566c03947540a3ab66f7c0578ab3c5cb3d1b599cdc572d6e425665c1122b3cb52f82073c679d6f8ba987c3c56c3aa3d9258e10eb988f595fadaf7a8fb714486cc032fb53824922ec3d72f13613dec139e19c86297f5b1b80066a3e7c6b3d875ce6bdd0a5e4f59512ae4fb5a830971e8ac84609f2ebb0a9ac0c1dfd7d7e475497d42c11a7040ecb5b9e4f9087bcdf643fadce4ed61e6c17aca59ea785bd7f4f6f9dc3eb675bda3269cbffc212d62b0920a4a947815ba31b22a7068032609208024fd1420b86fe928dcfc91cdd7b306f70a6c75397da793aa2eb240ac0499aaba8ed640acf6ca08ba70110fda1c68839b2796014ca340555d0ea0bff51a555d06b2453cecfd1c3fe040457be16ebc0d967fcd158437846a4e39a6d094294b018a2e3ac7d59e105ce86035f05c938f245ee1d730298cdbd17c55a4213fa39ea93efaaf8e0212750ebd525b97b5eb24dbaee862cc7de73d9020c85bc78cacd74e6aaf500ccbb657a9aeeee9819824ef74f6558fb850570dba49f38a33510ed58d40eb5ed181a347e8815abf4e7caf5707b9257554d49f0f950c76b047664e448687285da13d0eb8850b5fde767208872ab4b66770be68a5594929f497c64a65d2658f007b70c17ad8e28ca3949400a8b369b57b595bd78a5a56e12c58fe22ab7b8a15bfbc2cb33c4fc03beb67cf206bd86809d09b7d98e669c3eb26ba485f8d5f794d0046fc360aa4bfab02a50c129b7b390b56414eea376526659e01b9f1b923965c25b1404c98bec2b654576aa70b30214b4c6070acb71ff7e8dbf145b0a912a829940c6ce1cb2e8da003060d50118ba6de5946039661342c445000c91f4bdaa7a343befae0498e685e8c4be658eea3c939322d3177452eb0c1c2b39a13045d07f336216b33435c79aba815469009191840ffb406c6fb583434b3643d5d921c4fc5408de8e16430590a4e6681532cc1690ca642ddfc79104b053e859b4645a9f9a1fe4b27c7f07532d8588b512e0124326405eecd1c5377b22619c6e24f29877f6f36a0fcac1a7a3f8eca614067a55ef421497d32506443d5b1052f479eb164e908172796206286dd576049671a6ace25607663c262a0a2c06cc0246b4a1853f26e53f00c5ee4e3cbf2940560545c9ffdfb9c60f2daa22d0b2ac7977676e6ce159411b00147a51a42c7f7b4c9e12cb0109921cac212b9ccabb2e7b10e17f570e5f2b2d95a9e511a246461eea60dd847b70645d1eb8536d01bc0816dfbe3317f74785ced5409624fbd81c53c49520c4f0ac13555498f97c38c1eeea51ebd20fb2c126875911478662a22565d50071a8b85e560781b0b50a49783212c8c65af3650693129bafbab400a3790e71d65a16d62a00cb3e64cc0b36f22d665a666f73e28f8accc080cb490b026795f3ac4a49995f6f364a2c1c8e4d2ab97c1d2faf12f4ab92d8e6b6e35e2512f70c67ea236a05c55a67ce2e34ba708f324a21ef50609567d910cf2d0079b6418a758138f755a1e848d198b9c1a763f244436aa7305272fc5a708df17fd982ea707dc08045b9a2cbbf2a8a9f3adad3bae3046e42ac0654b468366e2e88943cf33a363f29e51c17639f5cfecd9916623415abfc58928428978cfcdffac16600477796885a2e3eb75ede17cbd1ee4d2923df5be3e94dea46e47261ff987827c74050368e6acd15aeaf17b8301f60905ecbf50274d27dcfd40cec0fd189d9ec6fbc1c61d593ca8701bc32de304eea365189ad683195fb42b7c62562c4391b33eb960df11aacd957d11955671a741af1fcd1834ba832506110a51453387c7d68f049905550120684db6093fc2e376ab92726e944272f92c54ed9f108de09ee8b8fc8762bb926c04e459b476d88fb4b9304216b5c2f738def7589368849250026f14ba30e2073c2b83d8f75f702318a42ee8aff18fc6306c6f73e39f63671bdba214045c8c508936db83d8ef349b7b12aff222a08295618f1222b40a2f30b3626fb3930ec2c8445f902d689761d7b71bf445006e3c723c1a40a505198e6285807898478a5d12763015ba061bea3341ab38faf0fabc9b8b1232c2ac4c5a37da047491907e910ff3d82c66e3320283ade7b87a0fe48aca078952b4af222fa48e8a59577c7a9d49e0a814ea0b82d1db792e2a218c9a7dd92f69d556eac2a221d430443c53dfb9e766a07821edd4bb66400be005511986dc2ded50aa9fa4f5a4c466ed5ab0ae34a944f40a6c06d4862b848df4b893deabd4920a8f9a0dd99345a665923bf33790b35ce07b64b2e8f3f6d2aba5f7e75345fec4630164643cdc5f41fdbfeef62384947023cc67375febb2cc75e5c83c21424a5fe24a15b511f98f2981306628ee197baf77d7830c298e7df2744913a80056b9d040aff9af96896fdaa437b7bb82f64e0ce61aa06c4b6634c180a6b0da2d68caf2c1e633ee9cf704655f48721eaf962494d7cae0e7fe1cb00eb4d298c873bc6d761568e661fee4fa06638fc86d5cf25a607ed1d53a40109cfad398325e70afdc48ce9bbb6f093890f3967cc2f6d9b111b4689651dc209879d00e4e68538da47a9f067d0a11f1a0ea353cb93a4d182e363d673b0d3a59ef13ec08ab6c4898f8496a8a09f49e9cb728284b1844df4629c47d09c90991d20069dedf75f889fba4704c0963e27c7f9a4f6a7d06162dc6ac10cbc07f4fd0e0200f34cf26bc24d782d0a026e0e30e8445e9390da239ff4f00486f162c45b86da762ba40fa36896bd03b190a50a06b04d0a05ecc379014f4937e6c98429938084b45ef3bd2efe03d19879148692429ee40dc9bb4e476511231e930bf054e5474032daef52d61adde609f9a84973890783277862188dbb69a8613969f551f6228e2714fdc8df000ec92a988d1882d865c8ccf1ec69b034d2cef704ec510890e845d099d999060f0e96b5a10e88d556060745c338533eb7d0b838116e555818cf009735b56cbaa26ee2cf2b4a897de93cbdb0d1453af75f5abd20e2619e15369dee064b555ea1113a1e28c6e516ef465823c781c9b073600539376c9db1a829b1b0e4db98fea241a1d81857194f9f0e6e52a1b9b09c49f46c8b08623865ac8042a58eb62ad996032f86fa38c95a5951aaa39674f83f82cdae16468429e2c3282c63ddac98c38666b076d704dcb4bb72bbdaed64356a8c56000b5453fc80ad84021dea26fbc9483b0bbb0f993408918de67402877a453224672508dd36972c00a78adb2004df56ecef3139d80ea9c2c4099563738dc152b7eede3413f938d53a7c22acd1db359070d4795498c8cb2330a5ae21e56ec9d6d66dc590ff49c0441879826cdf5d3f5d89c8e8710f178e135bc1ac8cb24de7fd23d350c2eda84b01a6214a746efcf0dae462686219c3805ff66494ec4d69cfeb7e608d87619f9f3d5ccbf019e5d228c6a1d9a15d902b60dd4a6d72ebd7a0c0eb6710e5ab013f0613faa090afa930898c25cc09528b44030d2af60de0c8834a51370f849cfded966dbaa61bf04d1b1d4fe485f1122b3e6252513bddbbc5a3b5264c759a49941f7d7e214586ca401cc9d4d4dd1bfaaf32350fc6486bb0d22d0e8851afa22dcd0dba8f3d0aa60190169b0980216f34252187a5bad08b7b19e2a2f8e59233f69cdbe925a2e8bbfa635071575e30fffd6d2ad0dfc864ee86d9bb14513cc8202aaad7f82120d525b00626a906b7d852c41a6df1c17bf1073e109232e30f3db957d7e7566b7de694d0c673754298e8765a9d97976f44cb0773e0566740a1e0489d405219a1ba19fc5cc9f8abef8b7423862d2779b8e9abb19b109dec7b5079a253609afbbaba7879a03da8612221f0c8a2c4c3d204cc7ab3a8f1965374388a9f7a94c1f2cf022b3fb5cff8c0a35d7ff0a51d2f902a295714621d819aa7fa60c1ed787dea6ccb3961541a26769039be9e9ee6b7c0f1469ed8030f9d64f63094c4ef0502b20f1fa9c513e65e66829b1e7936b18e9eb9f7c180c80b025c96d49c22feae016a16650a5b099f2c0ce4db4bf5b86c97467cd37345403060825cd333909978d93e57a066ad6899acd09c5c517565202812342cda186889f80391cd41a0799d657388749462d9ce25138161b505fee4938ba17770d04c3b689eecd51159dc0c02d0962a71e14e0426f04eef5ff5b22d4d1cb5546b1a58b26c0e2c2b5bfdbe0992c51cbd7767600fbad66e6c5dd182681fe9c613d68f31fc00bb00b39c681e411b2396d125b852c2e0adc29c04541354499feedd46329229b3ffd412ebc37646075fae66368eba2e237ba646c61e11b8e14e1fcf9abb1495bb614d1d45390ae4414ccc2c991e6b0a1c6fc20eb4e2c3dce8db1998ceeb607321183e4ddab3ec49bd2e94de4965f43aa0de3b6eb7632f8ec3d8ccfeaa33a502f7a015d0b79681ca9caaba5ad6d182d27101bf14b5b0786cca9a3ba025ef9e5ff4df7598d0462ed92cd46a0bab22793148cca27b85b1ccb470b05261f0c3a39c4406de6fef7f6386116ff5b0b5b56b752fc8ef18194ec4d799fcd0c0c6f61ba22ebb374a01783dee533df1095cb6e91d775bf1442c8ad7ac447768f39eafd2058ab8e91e2604e0bf82e4405b386e92bff7bbc11f1dcfeccb2ba3470ee5fb8b9ef2f550dec104b991c325075686ae4284589c69a07ff948f9d9e4ca544022a7f6279ccb2ed352ad4bae9e6e65e9bf34e6de1941209e93bd3507acfdab285d961d473b7b16d9d76fa0a5adb04373962582c83098551d512a6baddfd23ea4181e401647b799c38dbb9604072d617edb9601cd7bfb6dc912f96f8f19de6a01e4f61806d986737a7983be290e3b417a45dbabc81dd355f38084812ee0e2fb66159fa67aa63efbbf157044ea181062b9a85d29d26de6301ede0d1c096a9ac16141850e834bc6905c6ce17e12c0096a28514463fc576be070ac037da5e30454d24369620b5dbd3b4ed6829d3060bd02b5a9fbe10b18e49eb926d1f2f6cbc714cf49c2a0046c6da30713d62f44dd7d71e53bd98a6a86e1f446c325bcbe1290d598476aa1a3426b62a7e248559bfa573d03268f9d2a8fda8138efdf7b7276ec25a51ed1567265414af8631dca963e38631e93df9e6a5be373a672760ac6cc6d5f30662dd7de7dc7a75784fc02bb9fb157bc3b76d9027bdd5e195905163149d795bbe36538c00287f67406191a462c6db3b3d62740d181fb6a7697ddce33c50bbe574df0c624085791db6a8fc3a482f651fb613f562eae59d23f3dab0dd2cca8ceae4cdfead67e6bd9b05a2c82b832e1bfd75c1cad825e7d2550b274276740889b18e5819529a98047cf3e73cced1232151afda058c6cfe5bdbcd6a230c3ec0a63b332769a30124806b1ab73b7945091b308a3c8c7c452a6ee2526d2d1d043518282cd1dece5b1a778d978ae3a1eab2b96352fb124704ff7a6e20f3da9a1a6123f4e984d33e9517949eb17da46825c5a83e4215446dae239a015512a7851632a3a202f0ea8f8b186f4356c6bfbdd05a87e0e67e78f713334998990ddedf2000d18093088372202e96f22ce482ac68c11d239c5076952be9548dfe642824905b24ceb58d49995ae0214b655630b44473c0db8b6f4f15160d936cb6179b43ddf380acaef00875f0de106c747a3c88d419fba00135e3dd579a71346f4ce33cc2d7a9dc1f1c88d2d585d74a98a63945003fc6f9ff471db7ca582098327f2217c099843bf4ad2f82e12542fd4b2900289a3aadf1e81c745db4f50aea27a196057d68c0e7d450f4ff72a460a512ca60a06bfbb4ebfd1d844c5a9c1c5180177d86d40efd6ffdb47310cd96230babd22ba62e5fcb879e39193a1abfafbab906f6b0c14fe2ed9f2f15e76bd84d2e078da2b61cb13023e941725d6ca63446eb370ef99098d3ebc9c6b2e5605a3aa24434ba202be22bfc9c10ab01f110e0c7dec7ee0673a087703cda24e68b33df6592c62b3aa55013a49306423b269b8db14ea2ed051c839ac3c5b57bd01d717cc04aadee10dd53e30040728e723185b9bc9c9b813fca7b9d8f0e06b665d1add0390f369e31f0034e6b7f93746407abddd81e54e05c8167e6ee2c21a5751680d6d64ce0925647a50ae00c9434c80bc34224273bd40722b3680fe26ed73a990f20bdfadd20612340a0f8dec82262bdd910175c53c457f4534a14351a4134c213965c8c5fbe9e776fc1abb9c9072af9616d2e934928cc6b4d46ce86f6bfeeeaf059e3c6c67b726f3ec960a74dea364ef2e607e276bc10512b6add49da43e47de84f6a1686cae93442a740109a251e70463d79bff20f6a8eb31238b7ad0fcb0f425a1bcbe6a83e1afd11173e365ab8b9191c1b70eb3fb979fe8a4f0c959993c44e68789a89f52b2bd3cf4d5a19bf85575a9c97f5f66adcdd24416185ee936c340738f68e3b4cf3b06ffa69a74a4d62b966b18387311fb786d6361b53a2d4ecb7e759011afa0b6ee86103548f48017d5d3028df992e21bd264b033edbb0a1f2f70f6323eca4a85fc15e5fac1d3d047fc5e762490255ebd8c0683976a6f2e6451325d6e08bbb8340a3cb871b26693c6796e268d926e5d54fa1a8ed277989e1fd86b892e152a106b2d6417e4566540040ab1ed099818015dcee788d7ffa55610d63a31507e66397adddcc35d9f9a82ba9ba4b5b1e0731d63fb1126b521fa7d758c329226f029ef6df9ea5ef787b25ffd3843cebec7b7111714e06501f4740d5ca2e61a24ea7f7b42d9ea8b1b38a8e001d706326a6015e3dd6f77604c0a19c532661406c4905a5cd5d616c483c7d1363dcbc505463f97652e08ba17326a1e807e3055e7b2d1ec775d54f668799a9523cdf8d2590f9bfeea4de6c2cf8c3a17334da1891d57a9c42d8185e425343f41a0ba3b98ab9a4d003e5d0a4f0109c5f04c6dc48a289cd85078539ef429297428f5061c65325528335a7b20c867f0ff38dc455b583bf1f4d83815e2a96eb34c966af1677e36d9bd6dc5ffa9314df9a1c9e5963ff85f431f4734ce22d3a8be55e9ac810016573ac31dee82810be687d8c0d45d439ff32c7eee9c34a55f6f20d116701b08a3d48aa2b99feb410a90abcfb328ee655b6f6f2de5335e95069c10613c8bdd392d08e77f629cf6d068b19e4146e5d443b48153285d633f9d1f1484c2dc75e62c4923b74f392afe401f215d81d285dc9aa5b8bfb6f43eb2633613a4539e1e5dccab933bd410f11c14853c60d2f73537a9821d230d7ea0c52f75ec64ce11d51cd4f19a5509c462d18f153d5a56a8f9d867827d80eceb02e7d439177a5d3071049762228f7d6d6af60f1ed7b4829a2d053e1e683cb2ba12ab57227f894ead70204618ebe08d246c52d11cb70f15e698302ef7b87ce31c5aad0a9090aad6589160caaceb93e8299f27e5715459967f98e577a20052c2bc96f5e5a4df606b05dbe961d90a68a71a55c31d8903d489d7078f8a9c683ed70d4352430478fc095c57ef58fb322560a1501ad71b809046a763b8c70a43cb983ff2a7a1415c6b07e991cbbc1a7445e0798aa6859735428ef049363016f4d988105cd61e3c9908c4676badc587b6bd1a26105057fdbd1e699b9a9b1f2b12c526c2ee279ca7dacaa990049e7ad63f0c53a9914b010164382c2f9687fef7b7af337077beb9d640a1c9d9cade1a972b1ab129efcb9fc912a52078d03f98cf330c7afb64cadfab2b93477788b52f3184fdb6bb6f712f86982c7c1eb2803a4362f3cd218e2a7ee80c7633ddb64b087092351ae700b38b5465a057e2e8efdddfd47a2e367af903a313d9a8df4dfe203169e7c0df1f881c4ad6ffe63d2a1610ab2b147b47bb9af58d3deac4771d64a9eb6e89e0b2cf20e683958653a18e49dcd69c16b36cd39034700d28eae67b15f05356c3f3d701ba69e71bbd9ce66395f1915f92039c0135790dcd492719bde78cfaecde9c6a93803e4116c52c67bae250e605796c7b9cb141cdbb8c85aea434e8388f9a84357182de00297eb62e88917fe1b4b5bab71fd839918ef48d6022a5cecbb706b92b56e1c9fbe49b9405a88a2584340c594bdc492908ba41e7479c479df6d3f6b75ff88a4b86ea6976868d66378587596ce93faa25c66a837795c27511c4cb3f6fde6091614f4cd567bab274c4993448de70f80044f008c76ce7174d4f85625fb4dea6f5b45a28eee96122f2d3fcc4ab02663687036bc3d9608e9ccbdb0773dade8cf4e911884d9f1572870cb913caa319ab42c45804a468414f3d40113d1bb04950bbe93bf4ad809274c76ca502b3c0e5db2172b3e172e239b5d163c60f578dd934100b62fbf009bc3d78bb31b4673d7213a46da687271ecac2d04d388653094dad14c81c147f4a49ee060f38b5173b4128b3474eb3a81bfb1f5846c48801ac068f0e3225a38184c1cba33f91a0a09c61160974ed849622312589a5c7da85de78be4768e1363ed1fe5218a26ba0905f6dd7fc7b111f050e9408adf80bd00f8deafae2bac60f19ea04b664fdfd9a24b9ffeadb0ce78db4a043b8f05a8f178adb10ba2232e30bd5d7cd80999be2cdb89ae1b18450a5a40a13373c2f232e3774c9166dca8eeff625504c8d4d5458d2da0db043a5cb190829cf5d090b3a5262a181075bd071610f1a3febc70b38e6858714508a25c712b8ca430709e191e5605c79c82abe3f5dcd156b99dfd7c53c0da886e979373e141cf8977ef2fd280df6c977a83838d08558e78546c3d0a93c57338aeac138c06c26172fc4fe4b38497eec5f834caa65fe4d5145b3048fec4f812c0f66b37ea11d201dd0029783b900fb21c0327cdd166a1e1c307c84364d012ee39fe04059112dde1ddf05454a7871238e45e69645dd649bdc921f060a2de7b68a8d7015350333450a3e5cd57187e5228cc9b7de5ca5d428bd61c9fe64abcf1b43bc9e335a76f3885a0ad906476904b1dac781fa9db05a3d1391be2daaad449cabdc483a79350e909847990eca042c538c4c83b74ea1063a47112b6af25ecc3add7cc6dfa563adbbadfc63e619d9b063a8098a819cdc45194f70bfbdf7901696fc0daf5e407784c158506d370564b1a814a4a7182e2509ea139beac8e44beaccfbde739b6cb3ecc6019af8ac6d115095cb56f039bc2202868f644755abc9c21e60cf48b503e853d535f417c9171757d716ebfc30520cb4648437f9ca3963b04c7da77e5fbf4f7b63ebe14bd113afee73dad00557547482ba7a5e0c46efd5af86e44ddc5332bb1403684b537f1ad9d7da06d00ce803b1934548378d1d06cec777b0befe9257e14d327260ff3201fafdc7444f5b15c546e9d55363a75302ef6b184358c678c7f6afea592d017abff6a67ae1df8083e75667a423cf2033b83678f2051d98edd63f56d3c5953351871486e5bca5df552b7f888f53e2d087ef1457f5813ee8529386587a762adf2c802f0767cda402916ed9586f5f8bceee5f433adfe7ac1119f656aa0ccc4cb046a18348c61471f7986b2e84af3353c60215037d5b1386b6cd95c5ab37d215353bdd321fda85c6637626f86294838f21b3bcd6bab439a592784fb83247345c51894b8fc83f39b5db053107132ee40090799cdc41900f577d627062243fb0f6ac2fdd021a828b48402c46ecf090dd94978fe11bde4d40d0f00405340280df5cde43f1ed2a025657a744e71c22d25e04bc201ddd20beb8eaf32b30f690d6fa9a1a02cc626704e8b32138ddbda631c9e9620d70a3a3e83280dfb1487bcb16c3ce81a810e1611dbe675f6555d123ae87217a27095f18b47b8cb58dd44e551eb4b54623c4282a69b88d54fc6e04e0a13d51dc9461a938f931f7fde18031bef785aa16e3278e13cf3dcb712a447432570cd78707ac98661033c6fb22fd44e6adce7fe30a1e308020506c5c73d25e0fe75814dd831155cd7dde542a6a4f1287dbf9e1ea79ea3dea1e78fc8d4f20c79ba0747d187d5a132024535fe86334aa388f762eca0e4ce498d978576f0307419e74c45998ae3e73ddefb84de251dd37bcfebb579b95ce8664d7056260f5bca0e3768d01b607c11321a2cf2187c2ee4f318c7ccf4dcde900b3c659727747d2ec528f3c9c13e76208f4020778d0a3c0c51e16dbe653346ec71d8cee71630ed69b4097b9f78cbabd0bb05581b8956dc853cb9a3707edc49227656060c34fe3f60b994f6e487e1b1047f105487e8898b30f3ea1cc2bc8e78922f2ae82126b7d7af5596316c4180f1e0a1bedcda9be7e82dec06e8db6f148030b832239c6b5c629afa53225ef50b4f02cd8bc965270fc814b67f08b76dccac8d8f2650da286d6845e603a608a455455ce66a02919faa01612c9f7535ba7fef713309fad078f2e157740116d02f17f14be0f4677fc5931c5d9de49b251811fa03ddcc4c5165136e41bae56a1d3ffe83bfc800a263dc6082fe2ecfe15ef5353567a824dc3703a6afa96dd0fa395758603a064dcfc72d8c4149eded1841efe314a01e9914faec47084272cade2bcefb0ef7b4d30ea927be12b1551d2849b4664d955a381db40757e530d7b4a3f6f86d33073ba4333bdb907fa2a3a63568b217bcd641b8fe6ced657055b31fb9f037f706094d1df567f60f39850a936d0ca4c81aa4532e987c72d30f9e59ba57406f7dcd1b84017affe2f9af56ceeb8fd45a3d9bbc98545a65bbe48712be9fe960c63a25ab6a6999b54bbac29e8f1f6315dc302541a060a3715bb2f91a949cb510e48ea8bcf4cc1c21768bd3d2a12d40d01489c910140c869bb9897594601820ed3f3feccd57b7b3c503c0d930f1182b1b1a38aefe13ac206cf910035ee7e4df6755cd09454b5b642d3a21a09040d6a0c42cb6721cd91a8c894b148c232ac224a41f697940ecb4aa220f73b001efddeacbb642d4f56053e67b2e4c560e7377047809fb85ac622c794c212b85f435ad6c4884e132f22c474768b73fa5ca69d763c78ae74192ab88f7c8f67f8df8ae381ced2769d67e23f6ed00d86473c003609de6d07a3d884a32f9ccf8684652eabb3dc08e0b99f7e7f5b31b83d2aadc707f536eb9e97f682e46509262337d1e5e17c627f8ded0148b15a6764708b00601e4615de3027212e4321abfa73c622d85317c88e5d9584614f4cf23680892c1c506c11a0908e7e6b793ca49191ac00038a7775e6ef0cd5f3e3c7314ebb16ea6698dba5e947cc808b48b300d7462956f6a3aa1a49db126d2a612b3022592ca24638010569d2e69d43843621e8ab09f4cbbb66b8734fe2d8df62807f20e17b21502f9052a101e44c87e5263a40021faf533bfbe313974b7661dc2598055c55c0c83a660f9b1417aeafd372fed6a202344e15e4c8e87fb0cca3869165ef0f36ad7170f57c09cfe824b671be17166e360dd040317fa65b6bbba50083b3d782d8eb48692d646700f431793e29d38ae8df0238a4f5625113e79b98e672130b4b8a0196464627e1edfda6146ec714f906083b54bfdecccd65223ccbd07fd86870cb4656acb541007021a7bea23027c6be2f6e0d4f91d98766f042fef26998b50f52a2674205d22396a59eaf2b8c71649a8e056189c0deebcc5f405355333ae3419941c74c0561774b0b14007ba9079ff9f40c4df4d788a094bac77f5fadb24aab7074d76622e47f28370887aea4c6d1dc87d523b365252d9efb86fc16fa321900616046ad41515e26c6f49d3dd3f560ccf85aefc5c48a04320019bb596fb3c901c766bfc86757f4725176382b301b2d360d9d120c16180ec60b07436b371757f4e824d53132653417600e15775de6f1c8e6f36dd5f1e21f71d0f50dd116a059edb803cb789d03c7b431c0b003ee483bcaaeb971b072382b703b2d761d9d331c1d67a3577078b18789a7b940f17a643429b7192d3c649b89951286f1275db9475d55cd21a6796a68ecb0fb5776e0e1eaf9f2a76d808360ef6b3c51f76bf1d768c7e56df7d677018c76ff10aaec414d58fd882dda02c230ea249ae1e0d1b886cf1cd93d5318c4d7607b577164c8b95a819e4487e99da2763e6a6d55f4159f66f186092d80f93a8dd4e9d8022f075cc01b56d424518bf05fed964fd8423cae7dc25478968f5ae8448bf6134b1d89731a69c3ba730d505987a8fae1ee1759272f6c5fd112b33a4ebca4e122ff67475f659e562a66c84ef9a6de37436a4acd1aeb5725f8c6831416cabcf8c4ca3aaf2814fb5a7a10e6f94a77789d01d2b71fc9396b3b6d38003be261f7290be1ca2c1018f432c0951a0f1f2a9e1208558e4563227be3c332da12d26bdac7ce02edc29c34da7344600448b04c3ba059dddacf0be424eab0ef2cab9e4800eddd247571cb18ff6d07d1d4a2c01363d6a8d19872d748e29cf9f26b3800310769dda6ac68319c5263ab0711c9a0fbba4d271334baa9739e4ce4450e38b4517d572498585b54613a9b5ff94ddc35c1ce11d13f3eb60332962022318de3141c39d000b03d84039d9c00ba60b676d997d99223eaeb3fda3f2ff36f3e0df25c58e4976536b8b3771073a59215f4aba9c2e3e1713bf6a659c9a06ae6220794de25fde6555341e00aadbc806e442260faac721d9bacdd710e887402d99dde28284ac60dc583f0d36f1fb2aa43b8dba23581ddcda33e26dc5ae09ad356da98940681fe519bcec25aef6132e54087f437ab9d941695d9c92adf9d8d904e15246bf2d9cca2146a29b7738f71bbbaf0f9a44aa8564d0e717afefe421fae29a841dd9b1b7f6baf1c1e688d7ed23f47c0c8ce5189acdef3422ce7c58b01177494da04dd31ce7ad3ccb9f78ee1e125d2db4414f5d15c7f09ef036ef9f2e94a0f80c402f0264d44fd9226a56f229ca6c576e2ff71cb86441f1c689b06b8ceb0dd7caf85b82e3a9efbafacf34591dd73d689fec463a1f11f2a85349c7294bc99a6585c1032db728cc08691d99dc5888f081601243ef02484e938af4ed38bbd6bf0d349e425800a2e424b602088d67c8a9970b158dadbbd2779026fe1e877e036eeef79ed82f258ad63cfe5006174ec9da4fe9d22c0883be069b9d20eb127308d31a363b7844aa5c3c3e405bfa6ac2dc5ffa74be0c58ed62f1650208b0cde362cb9708578904628c80315bb0d521f79dcbafd177527cc690c702d9a16a193d0b655f8c3ab92bb22843650dc74167205404713f6ea64fa2d0b70e8a0098af23d6ede4dbe26fd38a02189654ed8c523fb3d12bcd3665345a071c6159594a84044726ca62012d9547fd9c5be115c85b1b8984ce0713415773c0a51b8e135aa4dfa95568959eddaa3266dacbd5fd106935e285a894a9d7d0a9e5ac28d63aa3a4ab7470b29b6e1cadb0a3111127f96ded162420edb795c13991dcbd3971ada597a0a4f0e895dfe518776e1e9b61939fddb0123ba5d75501ba1983b5711894ad4a4ed17edcf5fa9a442930c2630f23d80068ef6645b7724e9c3b0f1318a0ae8bc3a7c02672281edc9b16d630fd4b1d84de422a783b7ffcd758d0b04c684b4854a09b2f0a39470f2555c858ad088f6d57cd03a9e676896510c15cbb44f2aed2ac5138c3522b54cba4dd5a53bd5a02f34d40d61f254723b05557b0e291faae9aa6fc056b6473601f8a882a261567979df3b08421dffb10ca6296e84fea9f1c5bd3ec6d011bbb6f4924a73f39c3ad1cdb011847ae3e9b9842aa654f4edcdff8a63e17280597e2b2aa5ffc7a0d8ba7b45c68dff41ecc6fd99c4a75f56b616d9109c7b86978b11ac9d8c1a4e4ae280b9712024aabab8b22f5f56304ea6ab78e6a3b029d5c0a2e61234463fe89506b44a4af04fa3874c86f6ebd0ca96fabb25ad8fa0311906e00fdc65c0785d9e9a3bd053c9952e5a34ffcc5d298a35cec3cb9562123a026e8f1e4e0c3839cb042afd09d35ecfb25c61f332521359aa7f6c9240f9cf03e82d731f9f5f487c6352aac0314d3021adc555f5d30522a9b0ef1e28426524ca8ab663d160dba674e3ce9ae2b07ef3299aa251505685066cfd96aac86c2e88aebf742986220d1c260a817bd5a040af1300a49a1878ac3a36ee8c6b98ea1cfd6ec45363dc2c3881006697befbdf79629a514f205ec05b305bcc1aa6f321d9658d2df45ec244bd23699d3b4134d6c46b5ed88924d4868a64454341372f992679b6c33da8e6c421b0c969c6626d995bf139e127a45f589e44e9cc9694e482152cb9d546772a6ea3f7da6cc6936b02bff15c3f3292bb72935e47b20a7acfa2f973306ed4f6304a095181512d28c9413261f274c4cfe32605e5c5a586241b11382fc6867fca9e850f00d7f15d19fd819234b125144900aaa3f92ea7f1a8d6a5852044be2b02bff203e1dc6ae8010fa41142f96e462497f1fee7994a652ff2c9a5ab650a5a9e536096911c36215344755152ad86847a5763763a2ad7e63d0d94d6b9f20744b4edcbd930e688082c61c6b7287059e1d9511d485c9c3d3625848335f3e561f17cc916e8ed067128428c8d177baf9afb95bdce11102ed6f74706c1e4f3c7ade77ba59c0d25800696767fc4b7e17d072a1b8ff8e1dde9722eed0817a17e6a7fc875ae1e32aef8cb747d5c3538b5bed10d4b7f1503c3d288ebb33de7fe20b5c5346158d0951a211954f51e93fb15869a7917d5040594dfae7657ff81e3e2bfc920c59dd0a47f73c4a6b18ee0afcf04330f47ed206cbc349b2bc479c26fd6f9af45edaf116bad5dedddd9ca404a9cafcd73e511f9c73776fef2697d58ac024454535a31e5a8c2c94b6265ca9d53c218618328218b80bde82c80b3943414008f1d9284dd6090e2d6029bd89113257152aa8cf76581097299c4d66a276c9486c1f765de735c88a60f275953b13a9b00d3336628193daeda4b6f3a6b03577cae4c632c54293debec6a063f7919055a8a0fdfb251b19ed380dbf7896aacc7369ce1d168e2a3f4fb7542a03d134bc03ad856e6dd211231fbe823f5ad2911ad42621a63a7b5ddcca335fb10de81ed5ad658afff58d41474fa401dd2f3d5a0c18f5ccdcf4fd295dd0929b18caacc9229f752b759310e8696fbeff6e44108340552b8c0c60dfe6994273543e4785a69df1dfc6f042cef33cefdbd7f721d8034e15f0555a54a15248814853535393d89432a2787ae2a94997265331bf339f128e5fa94923b1318d59bd99a8de43781f48d44bda08e56d14f6964975eeb899c29c4fb8ac03dda4a127d5b9981e4dce9f58932a1650fe79b58ca4d2f78b240ecdefdffa8790eacfea968f9e4769ec4b560a9e47e79093ba4944c84ea89b34645439e6349e47692af54fd24a52c7d6f4cf3f000fa9738917069dd58529764cf56b117b8ee4bc8c2edfd3f2348a501dd7e1f710ec979f2c3465748a1a9f54c00a1d17957548175dbe4c81638f60046d7997f7173ba6ba89fdfcf21268f99751022ebfbeea98eacbdbb467e4e56d34cdcb4f16d2ab493a6be46efc948b4aa37e98e73c75ddc19973ce39e79c5d4bd7c9a5630a99f3873961384ea3e226a7a2f2a8ef49993eb5e5e72e94da32ba5646fe11631c63ffc6719a14d88523c37666e280f3e7fc9df96541b7295a4165a6d71c0d7474c7409949e8349acd8ececef8cb0826b0d32c74eb7bcfa18367a88e584644cd41d5391acdc66667fcc313db6c0daa04c28279192fe3431aa2ea05044710550fa8f1345ef532f2a8f134543cfe47f8df9d5919550b68797e7e525fe8ba81ae726056fe960752b728288bbd33a597e2400a1be0cad230ff29fa80c9e543661a7f5ffe357e5ffd32aa1ef05fe379fcd7a8f13d22fcd7f8ff1f7b667c8dadf1e2f3f81fc71ed5ff8b63cf911cd5ffd813f335c67df1c85166c840cb55a25e8aa6c02e28aa964b5bf958f8de4029632c4fd1b2352c5ff2104e78e28c0354dd405751dfa807bba0a7ca309f82498daa1552fff22aa4c66d52060ccc7bfff240eaf730e2e732f234b9b38526b75a001c3bd37f125d402cc460e0892181681d4ebaffd00474541ae2be4610f71523ee4bf579b5db978e163398a63fe1cab8b8870c031428309cbeffb3719ae7fe24daf8f046da7b11d2041b279430734300374455f8ac07f21280a80a6f7c8e00fec6877fe3c6b36ea0f040588fc2e7b01e85f1c71199ff71c2b364c61cd6a3f0288c3fc86701b9210001bc0922eb4f106f3c29a2f0366666666cbc09e30ff26dfc8f137ee68198f0404af89cd597f0fe2500e04d187f98f000f81f25fcea81bcc22e4124c09b200ee04f10713c2916e00b20aa38a44d0735e941429b6c9b55ffada85ba51fcdba45f4bd1f756bcaa6d256444b9739cc5d4602ba7de92815168a0afb44856dd2fb243b653dc6e6e019ec0d7f0fdfbfa3fcb04b363be34db6a66b939d014717ab9e8630b963d89533d9b08e01f1a7a880d5324505cc886398f7d33086a1612a1eafc2f97124e75ba972def3bcb1c7cb540dc0717df58d2e5f398f8b391e4beea1fa566bb8bdcb33a5eca9dcc8403be33f846e23ce78d3e4d65b1647449894c44ec0036cc9107c9061a526fe61d09295941aa502d7fd2a003c9abc05ac96bc05ac763e36af79881562613db4ccc51b610d1bb94993fecb83eacff15619a5c29c356c14f79d08564f754c0bb5ec09a526bd95fa951c1c42a78f4749c29559d7d1a36b7be609db85fe685ee84f6ca157fe9c98819df1f7440cec0d1783765defa679c2eff9fef43de02f9367e4ebf7449d1ebdf2f0f4dff784a7ff9e47f8fde9797c7f3a7d189efa1576037a5c5512b45c261e4ea77b50605b0758d27fd4b133fe9b8b72bd35e91f9c1c96252519aaaeba494900a102cb8e81205210c3d49834e121d03b2684fbc42488ea34c94c63e11a93a6a1cf29292a2a2caf32aafa577e5f3d2e9f32aabae579a47ccbcaab7c4fcba7742ce3ae8cb4855d46d95c79069ffdb949b7fc9be55bbee507cb188295f108f840640da482a3a3de1f48ff27d3e4c3ac44600625163908d5e2096419d3f1855ef17b3bbe62cec72783944f797e1ce620670939978ce4a4b20e8bc5c2c961d96091a806a3bc4e932c43b8bea27aa139ae7d7aa9c7acf2ebec740ba6f2c340e303d68c80a23207618450e5e7e1b6f6a1f2f3ac80b29af4b1355bb97befbd6edc1718841a89fdabfb7e1b4dd37127f6e895b7d3640fd761b58d6e7d651f652eb8011353122d60814cc935a51a55e2d7f62f5f4197669064ca508ffc31095ab23c2f06ceccecceeeeeccccececec3f4d9ef8c1120c8911c90d03bcd9d9bddb1b8b203e3423f18a27962cf919829f109205121a9ccc40d4440a2366109a2205480cad00c80a211560b14b8ea2348164288a228de44807283baa7852e4480bb890a17a90c44fcd071b92340144d4440e4ba408c202206a53d8c086fe6a30248aa1708a2234253f0005315bf2e467c8cf2c92439325419014418208dd6700ad0ea9adf833ede73add15ad6eec93d965504f3407da64044db5251d74380ac2c2892896242931028a2555bc000914330025d54cb08276e2892f7a7bb777bbb7677bf7328e6ea5e65094a41cfcd0474562dbdbbbbddbbdddddbbbbdbbb35318a6c3e0b850c507a77b4df6cbfa3cd26de6c37dddac6f279e38dc51bb6d971c3db363b37f34df3f6f6762729a9010fa1fc2bc3738c7065ca19e379a9909190883890ff78fe333dd2c53db72ef315f820f8a7ef091fd503fe49f56d2dda6256dd8af1c7798977b12b7f1e5487e24cdc97ce44c55951b36e0d6d6b386762695a881d52ab25252d13063000498ae28c192b2bfec38d2de33e3128d7909a74ee3bd4f729323561df09c249b757211cc1ef1b4bf0bdd1d5a40df8a50b649b19edefe19272cf7395bf837ade7bef5ef7fffda2bb54f7dee55a66eeef7b15b67e63f879cfdf89464eefd234a7e79ebacb5bd79237c30a8ca458b2e1081b64d58b8a8a8a3847c7b89bba514ea44612edbe8845ff6920224affd91a21b666bb695f9b118d8b84d0b29b98748be35105ee69547d5fe2201d107eb79db8686b9886add815c91dbb2b716d0c42b92fb9569980231379230d328fe6b1c744b4d3149a327e9616626139da1a2eb6e5c5b638116cebabc1c8b652dedf89587f626b54dedfa1e01cf4fdbd880dc261dd8773e4185547a2820a040f41aa7f87d151fa07953573691167fc27a69e13617e135f9e672d24d4b3ad49a9eecf21a12b5f6e34baf2e55caa48ec846f30f9ca7ffaf00d26a698524c89c608ad168b3565b2297317b896dbb4c9362396d191c21431e651a2ea19899db0f44c8414a1991251d14c28e64b9ecd52336488f0cec4d433522966f2e11bfe2c4c3ea713cf58624eb3c9b4d81c9b11cb268bc5e60c605e5e3ad641ddf4662e2d2cbf4b61be9a49aaff36c36e22d84d86b186a5d964be82718ecd886ff8a302375f10a8d731d833099529e6e8ecd6a07e64e2d6a854ffee616bb83fd6e4b83bd3429a746ff6a91ca549e6906833b1c307f2f2e7ffbe0fc71fdd7f3fc0f776290a98042cf749d79f9f213fddf7ec13efc1b11b7f80db0528dbe709a862f04fcf237cd4d8e385370c68d7bebc574ee7ec8c3f4369928beaf705dd4616e99afdd5e0ce35aeb112d7d6a64316d25ce3a52e7f546ee4d919ff79946a22a209e7740bf8fe5493ffdc6ca265ea26f5a38afbd2b5286e544057766d0d379ebe24ba8d3adea6d3f188a3d3e9743a4de634e93839364dbab7747af8f42ec8118de3d435a2a0dd77834968d7349bad112b5d9f5f14fc83782f7f478d3a76e6fb8276638a8e59501f62b6851cc303cafd4682ef32ddeacacd61836ff89f3e9c4d22c0726d8db73cd82e7c7d5d8532154b20d57b70c7b9abea6f8373acf646006ad7502453314dcae040b9ed8659d8db5077aed2d5bc28540a503420f1135b33674a093e12c40e218bdfebe8967faab7a6605229a430bbcbc36c9ee3f2309ff3f2a95f9787791e2e0f33aa1ae665bc7c6a54f5ef4b064c932c1fb67c384fa78d85d296959516951696efb422d2d08517683d3d7f8f2728941304ec8c3f8b07d81b2fe28dab6b580f964e53fc0441c50e3f4d3c7952cbf4459d7329e5f5c8d3e4e4f131392e683fe9abb94caf81e5d91a5ae7b7b035f3a7d75d6f403dbf471e2ca83fe934fb9aaedaa3aa7ef5b301e5c61f996c3184a157feac49a2a29e3957f967673ac811184d5b88f301d1b32e6a1943018202e463c856bed334bfb8891d08317bbd66b32e6a23f07fb6860b4f10083f3c8da2e72b70f4888a7ad69dc0cee76ce65238773559d433577fd9b36de49f9db9b1a9cd3c31361c20f8295ed0a288ac269bec221a6d224d21f307e6d3640a3893b6046d9395dea4f96a095a4ed884398d7bde37c392aa3fe581bc548d5219fba76ca31a34977a3661d2644d9cb48954fd5fcc1535bbe8710a9593c6cfbf094d19bf50b7d8f368bb029f859a94cda029e38d43a5c0db269b42e537e9a22f35e94bdd2a276c0a75ab74da1c72a739ad5b4bb37103fda269a949f70eb717e107da2f739a09e3718eb359396130d50ae1cf9f4d544e581b9934186dc2605ddd7fc07ca1c6d904035afa12adbad3684bb425da52f5e762b4745a1735e9bf421335d9335a1bd168b4179a2bb139788cbde17f7a7f37da1a5e62094eb21d25cace84e015d33b5117b05998000a190cdaed395bc35da38c9c9e51356c666666661e4b162ac1064110fcd0719af410f652056511fc0ec3adb9707442b25b5d99b9bb9b6178a1f3b156734078cae9953f07e27c238b6cb21b6348b7626b38eecb0e22e18ca3d3834611fabdf7fdd327f4f91ec461f38bc27c4f0ce2bdbc07c520decb27c8bf7c82d01b042867eafcdd0294ab98ebe58de2aee602c6948ff499d0d92dc5e41ca8baefeebe49cb5630123ca33d763847dbe0c2f6dd871cc74d1f581141dcdcedc4af9ab3dddd45207377779fcd9b37770af72278639962eab123b2d0a47f221d83065a0c4124690a307c50aa0205330cc1233aeb3e0f5fdd75980ff90816b63531b1ad99df0b09e14043aa7b91128e6d0dd7fd37c28c72dd976a1b550ff01e7c1ede83e1e63d0f301c55db6824e5f7857af067b5cd27e6b0263b51b571ddf6df7be1d8037aafdac69c6f546d1f760f82dda8da461cd49f3ce63029e8fcd261dd7ffd4593ee305fc5be316837313549035af64c24a2a5c3981cd6ad6d8288a0ea5dfd55a40d87910e837143ecca1f89fe61a2a9899734a9c32322adaf4e25082ac776c67f08e5d1c685e49afdc1a07bb7fbe60d04b76d034fa80de4f0b481fc1bd8830a4f1f828f7a1ee0a3c653f827149fb6499511dd9ef3d8fbf80b41af1b59a4cb0b9237293e1cdf0a9acc24740393d0fe44564fd9944da139d4db9753c6c44253c6513e1aa8ff2e69d26bfe7dcfa32379edfb5ab7befdbe5df9f721287aad919a5ce2481f7f1c92d71ca95bb56e95dbac2ee99697be4b886aea085aa66ec881fa6fdd7ea8032c00aaccac270d9b51032d1bd6b186354109429747fe1f304292c5308ed6675d9e688114a5f881996b35a41d983908a9492d46c3a0297ada2f29cecfdd5d13d0fd263a8201121c363b6e6ca80eb45c9a0dcb85c4ee76b1a9269ba363aac95e124ed0316c32202de1e8baaeeb7e7b9bf6bc6e75dec6799bd76ddce62c1da5efaf29d5f28774ccccbfd3e4bfd840cb55e57fb18167078784176c8d8778f8197a9be69a060e9479773218364d34238872111045b3a259110c4533a2d98ca8685624039a01cd8488848a8c8478a1fc30d18edab9e4ff264926654eef6f83ecd6d25411ba35cd02e367cf903fb5710cf364a2fbada496293545f4f62be02a2a189ffcd80e0aca485d32d2f6cc48ed791cdb5533d2116db78924c4b4a36ef5126666e6187b50d8083f0943b48f5033d0466ada8f44747fc4f184d6401032d2cea800a41af1cfd95e3efe08c1cbe5aba01951cf84d0b2675dd4b396b5d00d347eb2240041f702e1ab66f469aa49268e8a08b978d3e4eeee6e775721039224e1323bb7cf8deb3ce5846e935bf3e6ed9f8668ca555325fc50c20f5984ac9202ea620141f73dcf474f8905eaa72eebbd238e4e4d87524a757056cca9f5eb34b7e1e8f8ceea0f4b70bbbb2c8fba76b89c481d22bf9c39915f8baaf9208a154504a55c43d4c7225e607676677766676676afb150e369a106456c8a2390581f77a03d7a23120b78109e7f1e8f3c9367a7c9de259eff7f1e5fda6181679b3b7ec33bcdd3c26c6b1ff3a9d41441d496b400fa82871f9808a5bea281f2c840b3f907887fd88128a514e88789fc004d77ff719afe2939c63ecc3eb13f516f7e6143bb45a98bb5a36f5650a9dcbbf8a09df84d6e2e624c933d95e09fbf7371b04df7418fdece30b703fdcbf4b82bc474ebd4a44d7b0cc29529d746b7b6da58afd5bf2fd336627ab5a586a0278f8996d45bd24fb57ca1ceb9a47f7b759630d4fe90674bddfee6474656b7df19cd48a86eaebad18c92d4cd6773fb6fefd2adf9dbafcc06841b76288285926b89134860f1ecb25ed014093950fe0ee830ab22072fe020ddddcccdcceb62c540ad20e2c4122d6214394110314c6f6b31e0d06464440f458ab868a2429644142a9210516406126858f213daa0ee7b1c6b1e19c6318eb14f73cd6db1e6d9692a22abc74393a7d960fe556c51cb57826e1ee7799dd771dbb6b9bb4fe6cda793208313473b14b5348184431bbe4e23d34bee73415dac19ebc00721eeeeccce1b4f66f776253da91d014612417c3cd07dcf4ba57e64f0191181388c37784ec42b8a9408519188a00454f480c81731081a628914454a569820091a9e7c5794a0464407198d99d99d7dbabb7378c40a21b57caf1ee1dbf438cfebbc8edbb66d7edfdc260f68b948373898210521995008a1b933cfc9734e9f93594da8b8412d7fb21b75ed535a9951f76f7cd53a9aec6e1860e4d3344ecbe9d636418488466d76f473bd719a6daf69a71a8711ef594f4bc5a880f21351e0088d2882848e5e30272087a39a0d6666f73ccf73f6e9ee9b3405915aa650e09344b4248908a8d5b56c752d5b2dce61b444cc0aa35aa66642c4c2f6be267a33bdfbbbd605922ddc87de6cc9bd8f364d36c77936dd363b96f3fe9bee5f990e4258eb13ed0889d6b414122d6c9e328aa0f375b68679bb14de4057b98b421c3d7030b033fe2a4ae8f6b3396cf20183073eb035cba1c0564bcaf3a8bcd3dffa79bf4223639b9a7cc8f89e948c6779fe1e984f7d8f8c7f7916984f7d0ecc987a1e735ed8c5260533aef7b7fc0b035bd39e4baa49199514d1d5e46260c579a2a48d20d06d3261b174f7245c19274c4b5b33756bbde7b8aedbbca02ed70a3a4d2ccc1d1a45481a8dc6756291d3eccbc325a7f17cc543cb223cedd74bbdb494fa9c7bdd9d4be19e6b7323f3cfcec0bc1bb613ed0889464b21d174b6c6bddfddb1fb1c705c5fa57ce5cb5b4a1982da8ddce6b51729c272eef771db6cd431f55b3be543433ea55c4019b56c847bda743f25e81c3d1fb706749f3584ced5a4ab28a1deced20f1ffefe3b3c5b73aafe18d89aad66becbd4251c4d93d35b4d3f2a45a69629d269fee43852622d656a7f7f9abca8142e3745b9746846020000d3140000180c06058321915840281c2d7a7c14000d77984c724c98c9c37112e3484a29638821860000000000002a008188010df3fbc11416fc68e1939ba6d428385c039439c70c73e9987a22a438b8b27cbd3f0ec0bd2f1f22d8870c8ec036211322c125d046ffcdb2a9682ecfd7848c359941eb62eef31ea41a0e9b0f22ecc16e6be837be6b49fb4794791d7be959424e05a7ff7975cca5eda5be88d77d379a1029b74aa3b986c2e6ab6052ce5e6d389b260c3852d523674af5e37e735140f685ce331425c46ef50458db95b84efe0fd76345e183d6e036bb9725c2e9d85b0a114569fb336e135508a236340e3d4f9a84c68fbdd4043d39cba62339f6697f30b53d7440adc6af45b42a9ef1c7c420053c20e4b87035ae92f842a1980ca84a01b4146da423fa5704e52259f53c57affb6d941f5ca24f58482a051301dc8b753579501af73808271fa9c0c101de25bb1341a86e21ba0df56907a5f7602ee0ca6f61158c2e1171b889bbbf67d5decbf929e574fd363e6f34f6f033bbe9d0ce6ae59df543f4b5ad0f5cd38eacd8882e9400b8ca96134338d0060ed4598b28d11be4b90407759965c2031640a104adb086765c590900c830007eaff2eb4dc9aeebf581a4dffdbfdee4d2df10eac3c7f2dfd6ff44e8faa60729746b02a1964910da4b533abb72d90939ff7afb5f288cbc1bb2c8744846e69967ea7750d08d87093ea4586b234ac576fb484b9a2b67f5000dc66bcd8d11629fbb0868d2861964711ed1c7088495f57397de8d4a309629a1d60c37c9111f46aaa07df2f60a789a99d601eb52b2b87eaf928d464531cc7e616f056b63c4425681df837349e18ffe0665ae9fb253da2fd7f7ebedaa7053793adc4433f30d94d30558bcc756befd89445c16753f59b64fa10fed62d23c4cbcca728eb1fe505c37a1dc8238ce0b6547026e71310fe90e73d0c730e421eabe4ca58f290e85bfc7207c840ef50cf28491560f6d9d8b4b19799f6d4d1cbf5296b7227dec1b4af47bf42b52f3b52852821db91d977c65dbb011e82fbb65e80d1b6b753668d8185d961abc42463504faf64a8ccd40d40da7a643e0f953cf77738fae3b8450dc3f88744e504222eaa0c036d13661f55d427d44c2b46cbcc2279e9fe2d9e92494a6bbe651ccd1d5af1906c83f25a542fbbecf013867219334dfd11a35417be63b4c2bf85e68ec4c21eacfdefc7902456f12be23e185e177779c3f78d9a0803cfbd8dcace043d3c748d10c6cd1fd9f34b0f55003326deb343fe8dae37e632b0803713f5b0473dcfe09f2d421053924c2eb654f1ef0897079a1909bec637fee543fec201de7c8caa3ee2046af66d309015204a001c4ea0f83865337b95722c98f149e227d09154853184e3657e2f15662ebeb3477047081690e22c62b91c12207ffbd4acc6a948974686815598c5a787fb28dac43ee0d361bbb0d3e945ac7c283e46ad40634900dc1350ff6f9fac9c285b9595ad7f4c281831772578369256fdbae12c7c01f0a86554d72faead2dc2d584589a82fd6b3739ae140685a093398e39ab6da2d3aef3bdac73a219bce352b3ed8a71a64741e8d500f011dd18ea3b2a211737d0d6b9ceeec07f7f58ab2a8bfd17484cf7484d9f9edc399df64d1d8329e6ee816adedde4596aa0ed2ce129ee8ddda18fd757f64023078535d1c109bf4eace3aba2b11e824656104e1bbbc0ad1c7206f3255041dcac6317b8cb230a81ad5ad58c196afa96b160506dc04acec05d3350029c34483d9b8990c9d32aa309adc08f7b491adcf3e47a45ad764f6024d1ec84dc78dfcf17038cd800167c74bd155f586ee9a626db3ec90c8a89cc5695601388dc52059cdb936c6c1281642042e301eb352ddb5d6a7af88ca9bd6fe37567816108c9eab58f80525ddec093759e7e9ed9d5f14954a5b9ff4cb93ecea763485445cd3ea8ca0ae5853ba7ef3fef741627d1fde17d3ebdcbf5f48bf031a2b2f27bd360a4b536e4efe4ddae0ee84e3b314c11f98ae123c429195f4fdede52e9e6a7f7c0d867ae158eeda51c704bb242a5548f92743812c2124edcae58c59b47cb3a6eb33ae1aa8f309153c7bfdc3c07f5f9129f16ecca4e9616d0ae86298611aaf0939b63e3e16cc5811e971dd290654c5506ea473c3c5a1a21434f1e1dd52a42951a9923228637f30edce85fb6e42209967c60848484b5e8db2a00c53ab31863cece850edf8ca16109583c72b0dab416c0f9bda585e091ca3829c40c0b9432203828fc4b3c3db08ff4093bc61e8b02a22a75de6e7e51fe8a4ebc88b6217ca056c90ba61ac70fa9372bfad57a9b937d3aa3fd8088df73cc272f4bd6b96fc5aaa1dbe4a61720dc781225d90b673814afa0ce53140d51ef58cdf15e7533dac44f8d8388fa25a89b94f235a44935f5e75ab342908252fe5c5c0ce5700a8009cb8061de7a99d0d6a56df6dc767e725ef29978d1e62294b940a6fe088b1b9f29f3097127957996fdc654a89a6e4bfbcd04593f2ff565b9e794030d3d9d95ed54703e7a0e4762f4a05f10174f4889fe5126645622b1e311eeef268d62ed3555e1c38529359b29cde014619720c59c0d2e8f7a17493a140ac916335708ba24eaaddf103ad728f2e7a10b77b5c9a737b76026b17acfe11e6f6779852dbf661d56762db623cbf2b8050ffa974d53dad7aaf9566b919b7774c0efb0212a41dfa65e84c27bd2b5ca9ae31ad02a34c7a00db57b5b2d81f113350a10b5fbe747f8a51f74e02ab8b3a6238e47a8b877c9c63a66219e6b0e440e35ef9379467501042493ab185098993d052fb57bc502c01e9f2110c4618c72c4678947ffaf2022cc0d86cc64634f68bbefd1b0fe8a74ebadeb1013db6b19a730a24f0409a85c80e5759d0f8d225288e0430afb41c6f48c8d9bcc1d6acee1673388346128589a3eb01e6be135d13eb4c042ad5b1fee5fc7b8407ad6a8ac11778a785105e0da8ccf8b86155908791f15f11c93e4dfc2c9617e4a0b246a3cdaf7d7c8e31800d588090eaaabc54490705670e4f726ad1caacb41120cf8062cbf3e00433aa2855a9cd4c72aaaaf9d85c6f015d7001194346ce6a9411a74fc147068890c2715d6fcd06e5d36cdb157f6e206062edce0499fc2468dad53d73bce1016f06a5d534cb4b9b227ef86445db74de10271c7a88a014c344e6911240ee78b4c1621e1b1f7a87964034a6f5bb9a7b5e8d3a7931a1f53a748209e099b771964db42dfa653315290639f3503b06347def8d35d579c9e0a6fbb0978615f728d1eeae36e6da27f945cc05c8b29d2b0752b68c04880232cef1f964e66818f6d1a2a044030d23928af467772af42de8023e2f503a37bc2972e2ebbe8cbabb05f130a2f4db22b6f4452ba201ea3c16d9737a898de8413056b292d1e93e18b393624f1b6af4982db65dc955ed442d92fb2d4c44295480a50f4a6763c960f656ab6d82737c5b4764317905846e2967fd1fa144fe84e828ef904740b2d3509a15568fce0733469a0af51749040467c9d8abdf3ec86678da489a86c51ccdf20d9bba9c9a10a21493acefb89f4124a01ae9b5c36f2a21321b192b18c7910a134bbcb8a326d3a6ce1a65a4385aaf1b0b5c1a8130b925c92fdf42c98eacce4d8930dfd0459bac17a15c2275d1f0e214bb9edd2b674fb403bc41598286f41ec868fb086ebf84341617d1c820cd5301caa7eaa34906278e4d4f4011aff783948690480b6e30b670a58955b24a82f8e8a940a2cb59dd548066e16a04ba71cb36f78394bfcd1453f49415dc2855ecfdb4f42d28849ed44c9f51e23a53a25ee003a6d933cd0781ef91d7573db0f63ccf87cf0ca26be8495eb1e06d104eea5a87fab87cd974c1e626a745ef4685de7261b4496e5a49462d3b67568ca821d8f2f65c9577f6fd594ef10a414bcfaab2d96d556a4f8fffd00cf0b3fe662de8b9d178203e0c6d861d95f4c70e8913a75b38f60ab00e3abb0aaf7b5de3629a8d448297982a21e20a019fac1db5e10a33ba3c00cc301a40af6cc506deaccb982b981ffb64f97a769b91b4de11acc8ffd8694e505899da04900f87f65d37986004c1222a81ef06be634aef422a700bf783c7b3500b2398d3ebd6024a70cd67e3ed21f1c2c27668981d293d9b0ad3c9b59fa1a2b38b32c5e18ce0e5b9ea9491807c5caf9b7405f5e6a292ab53334be0f03f8d750f7f82848de80519b34628e62fbdd9585c772fc459802705df60fe0831dc399ac70db73c6a3e542e320c9fcbb80dc4a3e29a010ba6b5bf5f4cebccc06ac54f1c33407e39a45507e2cfb3bd5dc2e451e36d7e1b5950cc3640840e89bc45c0174c59fe31a22486cf2b62dec2fe1a9ac65ee04c82ae9c3946d6ddac1a8053ec01a19a5cfab8afca6f3e0a7f9bfdc03c568a5f12d166e503659993e619d3b145f11c7254b8497961848cc62c47714d43cf7f7b84e643dd1c2c363b73383393b10324bad722fc410b60c6d38b44f009ac425500636acf2d180b34acb4c72b3b0a31f2be2eada2c785284742f21b81938ca1a52f7e3dd27412434d316947f9c2457ba69b9c5ac75cbb81ced1dbd0fa701a19c76d3942fa5e381f2c0c4f274cec4458c4987c3d8903458e47314903657956fa29af88a22cc49cc08ac33a9678c5f318d4074689d12b56c90daa79cb65e40025946822e6103e18c8e9b8910e7a59fd788c31fc30d51a9734eab0f25e9090e6a26eecbe8da317d7d8a8b99a60e2dbf698ef4bb406d391976a69f7478027dd3d5db0ca93dfcddee1b8892a4ec8eb8ec66fcfee2593efa570ee13fa7d26b7d7b6145899bb7c0ee822460ab2aeda0b219cbe3d678f6e3de043e3d76d1fd1d233497dabeaf31cc47d48591677c3033e7b0fde8047fcfd1d12415b3dee8d6f06199ec55c04904dee665234f66f59ebf7ba840220fe5cbab3637f4ffa9e8a9c59da675b25001136619807f7a5acd1319ff2ce814bcbb01c3928bbc425c40caa166eafb78278b116879c98756018b8bc8a94922f4a8eeddcc4c5c9c3de741deb995dcd50da11232bf78e242916cba97ea1fcb7ef479d55209e6c48913ad83c8f110446f7a1166c44bf180f1bbaaa104158ebbad2ea2a4236f44e658f9821884ff8f90cfee783d4a161c9a0d12386d3e27fd98a27448748ec9612ba99a66258300558863ae49d1ee342b9be15b06d4a4de1246d8adebb57ab312861647ed5ba9034fd49c4971346cddb1b47501dca38fd799ac04c0cf62a765cf339815d62915197eba18fc5959dba18e731419e55eb081613cb29db10f4769ed8c796a4fa5a2f6df7708aa7ad6b831efadc91b9013ad3c4c976ac2c3894096a117aba338b63d1c61ab4e049606b054989c751eb89c4bbff00a5aff4339b6c29e421e330014253f3358bc34926ada2b54bd483e12a9800e9297e91bff59abe75cf90fa2c53779cb012fd061b25e5ce26370ea529c42a5ee0bb88961a61f4d3dfeefee4c03480c6870bcc18e42830b5bf7028eaca7d9f6c3d50378d47c0f7b354ddd9a260e9ea118e411b644082e9a92994e94e665bca2d0670db440672c70b0027a8ac42ac6437f400cd99dccb6c74089f382f314dc486a2f5e3af36915360642600f87070e3bba4c06c6840b2179d1a1003ee9842fecaabe753511835db8df110998418100b370cb6921e37949d9771b3111c2dc74a86fe276d68619a752f332201ecbf315138f377eef90c7761d78c6e268ac2b2b9a9299ca316065f6a1b75defab79ff4aaa3d1722a406b0fae6686b6ba8ec1a621f58644ac702182bf5d9b274b863c83c7c608df188a437eb6823b2049135837851a590c1c728e5b745788eb0f4b5808975b31701f4941b40ec6e8133b80ee02a69e14e7bfc1149fdfd10a62a70fed82cce1e707e32ac6a11f59ba9ded4e25796024b1c729b0e9eb1a9e5f28f7e29ea2a7f2c37474df742470a44ddf75e180414b8a10290cfde0d97237c67395e942d233be269c6410554e2dcc5ab313af66b608c2aeb861556b383ae4629ce308778595f389b974b24f5dfb9cb8ac9019650f80ed15f94c67dd048a858f6ebbfd8551b393bbfd275495eaf6c4229bdc9bc885bad1a13312a8c6e73cfb003b18d924c02af490f4e6c86d3b8cc83b8f493a2f89a22380a5d9dea0e47a68d950b697ea7bb8f1a69878b6d870f14b99c446cc67265c2c78152dd674e4ad9ecb4c76b62b77c0d0f58655eb1b1a0144150a7926bad4a49c39b6ef8af0218c05affa96b0e31ef895f9ea0a684c072c18cdb7c3a6d833230eb9a91712fee75167242de473aa6e5b35e6782982ab068a5984aa944c7e9c85bb1ed8cbb05e5d140e0736fe3b4b774fb72428d714a7dadcc9b5779e9b7ac2c22d0e35471af7d2a3ec91bc49b5b58ed3c85e415407df96cf8fb67634b8b35901f299d421f8d7da1041f09580b2babb23fb6e8c93b16c1ce6eae2c60720211843db7a5f857c7b1ac0a4a00515a138934bbf88774314ade4b2beb1ed3f14dbbe19792a026dadd2c3451881689e228cd57800950e378647520f372b9d0b866d953312e1b8e9248cc12951b4cfe3e32940a3911efe1773dc191f220a154fef99848803cb8784cb8d8c882c8bc7c15244251553d13167e196446b554053df7da43d06042e5ccb7a30b12a6e9eaa6a988c2f6748dadea5c6ee8313657fe6ec84d5567cce4b24ccd760fe080f4f465aad8147d8fe1a317803c6d2aaa841f9e8e85983d9dd9ef13ea14b104248559a6d4a790ec178f57fbbeb14601983daeaef49c06b9658d34599b596b548d7d161482392eec030769aea81d1efc9b2a77fe0ce54394775e9b9fd4aafbd44db4cc79e8787c862e9e8b3c794e9200521abc41c4952d3ce651949685340ffa65219bfdb6d9801c26f58c9eb53353e5d4d085720e2d445b17d90551572648b6941544171101571e88216b74b077384a465568801c5e6ff331bdb352f2e97a7539c446949560dd1516b092caa3603a946218a40b8d9fe36957196de6869564abf3f47aa19e833ef78e90ab46e1590a113ee67c415576651f3e91e76481e1919c35b12d2a56755b8e5a44127162cc2abddb09e09052529cc6cdeb3e58cc03cd0e14fab33d0afa33a93e170c6a0466fa51114a1f5f3768c1f23e4815cec692d8662a0bcf136a623067a60f1c9093d6ef8cec51d4b3f65217b9a9b6f8a5ae6598004943279803464c95fcad3a3a000fc1f9fbf002f3d26873b4406d6224c261085d7fc4c1ba9088031e5b0e5f1f483d7a1a4ea80da9083a8d0bd1e397ac591886f855b2de7c375f0703b5398e0b0a3440b19f3cbfae6e09f492f4328067205035224b6a4fbc44233d78f18cb43a13970949f5834c4f40b1495b8e548efa78bfaf29955bdf5746e76aca1ac9f4b15e4184ecff0a35fede4c1649c6993bb28136b88929f0dc17260419ded7ec09d8d1e19b4037e48f57d2de6ca730eef147de7ee11336b3594356a2ab2657f2b1e720e69663d3fe36882c5ce9daa39a988880b0d1ad0fb73f660e15f6ee0e6cabbf6624669964f2d1207bbca2bedc8997697472588cfb9bf14f993adec19cd118909c29f31416200e89b843e74d79d0df5c610ac3c5f6ee48889c7e1fb01490cb1a8256ff117f837ed28fc761e81049eea7c45f6d130b29c52a065645100c4b000bd424249490a1d37b6443dd88601288d22f39b894501d4c1c8b64495c2df3d39d7efb21540f646cd9be060fc7c389e023752955d64bf53cf5da8b7fdf5552194bde3925aec7d23d1b46b814bd0ab50e82da3e777a1d6b278b71ae994a0ab2e140e14b0659979a56c2d45149dc04231f9b1dfd1942fc36a95864d4fd4da580e187c3b36e400fa8a70f193fdf727ffad0d95248bc1e2ce03ec11d5d8d3e1e32f331c047cbcf737da00bc35460330e473ef7166fbfc75dbcf387be533b00e9615d86a430fba7663af55690463d2f715a8715333b1ef6aa2e0170c4d2e3e912df3221e861128efbc10eea160079bd593ec5bf4af22eb648c8b45bee1b2da6796d50512177a8bf1025f1a4da4a144a5d8341bc40d4d8837327f36fed53eb1eb9e690396539f699cf9ac483cecec692175d619371b974e3ae5de58e2dc082219fcf1f033664986f15bfdf4941b48686c549143012e42f6123f4d8be8c19c510f3b506a1b68c1eb5437dba20277c11d94bd94a671b2f0f071c5b43c3c1dc2005007022e4c1b5c45564005f5bbf67b8a09eb06ef25b36b38945730f1d903fa8445295c71b9a706212cd02cc04c9a7ba73025dfcab49faa1776fd64cc2419501071d20d37c7b001b5b0a4b21955be26d966eb4a731ad37a0b9c8cddc0cc27def34a06a00cc8edc2d982c4eed931cc894cbb3f4cff3933b7151b71dd7f132a195ab05f9e420f3f6ee5a6bd2a189231887b9bd3b46f3a7b8864df31537c50ca7500f13692fd427ddb1453dbf872dc35c36a7f88bb69668d8672b9b48c3bef9e99e9a5e1e4d2101ae32bd079e144039325070725f0609aef3c552d03e5fa17041e3825db33711064a59eb06c2f9b7e36834bc715a4dde151641832f11efee1219db1419c1000fd6f221b4ba9f31cbad579905c6ea7dbf142c9da8950065a69819f99dc4862146bd2ce2d48c2a83d5ce0b1ba64d7baac7fc7a87254fd1ee0f762e0aa608338393eec2424f6fc169a6a6969a6a682f07a08006a03c797c0ec2392813cf6e75fda81bf6c3c93e844b61747a1c5d72b3bcc9982ef8af861e5299fca2ad589ec227ca8b1030b63a8e83e4bfdecc7abcd1864be4b3b45267731dcbe4e8d9752413689f2e1c50f009f438fb9ceb0e39296c816c124d44caa09bd2161db0f8911a094c1b487471575d552dbe1272e996a0660d2daa49fb03dff9b1a1252ecaa70971217b4533a0e7e59d8f07871c69f8ffc3399cd04af434440a8c30ffade43a287eb3117bc9116626f4c1005652d28f5468ffbf4f4a3f17290e27b06c1a0f4a37760122d4c283be28dcb9febd4b063c1a5343f134177e152e99bfda3fa150d25c15b6a18c69b8a46c953026731f4721853fceac236415266e898b105db0a3121b4d682d90f44770eee7901096b539863155e3e28b8fe235e5d474b4b0529f0611f060379a3e4f37d8e0eb397c932a01ed678d9018242fe6eb4b5d36be482b957c0d11e7ac0cd842261417289430d12c5d081227d207facc291cf02d22a75087ec0d9cbce192600c32fc62c5ed331a8fc5e463dba0d4a0711a1545b4a50acb31a43f95991d9b746d162c099f7fd8f1221224a8c416fb84a589056def4002369655ae7708909b982b4e7487b6d6089d9d0a9f7bf113468af5364ff749831a6c407cf782f2f99927150d93ca91b8587ef3223f2693fcba2074ac93fcdcadb7e89ac564c4d16a83975526e813790aef8851ac4b6adff1e1c41f84ba7bdfafa026da3e291802810610d78c34329825710724dd9a5367515d5813ed59b5c8959392ed4c462537da136ee3695e2495a737e5ebb0508d569ef820045d693719f549346aa410975f73a1b2f062bdb21da90df7b420a55a0b5948b63133ca6bc586d5c4117d8f1877f3f13832b0ed040127ab0873a05d20fc06e857fd5d43400a8e6623e5253e0a8ef4d85087bde4fd177122448e6abcf6803832ea731aa1c95fdc574f60328eee7304754f625f27681a37264897c84df8cdcdff3eccaeb95051646187ef0b716d64b5043189928a3710ff9e2763b91841ce42b10489414382b319a65ceb0a66571213ca6d2f546df4fe7d8668188389035e5b435d5d719a83d8c44079dfabd196f61fc60c26fddf345c7214cb6cdc80050a7a1212e9cf3032be97285421af45af006c4a28bf674e487d13653d141eaec1b729fc085faf2d6c6f3dd8769078bbe3aa86fb8b4258f6a344a2cec17070dfc698413a5db57ab9ed6ae9850e6b8e460b6d6c08853d287950af928d296fe4bdc7ef793f811a326f74a23eee7166fdb83971e174f140f2d243c9f98a33cb8edbc18faf98fe2637196898be92a3c94aed3de3b5a30d233ff5b4a22600a5b91174c649e5e956cf66becf2f9b741132ee7f8391ca5ef43d3f45f96f64a00b50b9e087589e5c15c2f42d57e0a95e1ccfb449a032b6f75a196323eaddcc37f24cb75a1060dffbec358a00b0fcecea97d04d09a50a578f14c92c71ae60ea816c4b8daa0e43a61c9ecb19c2b557b2637445416e91b1ac0d7c6bedf4cf8298e1d57dbbeb25a25e3321c2af5803a4d1a14e3921e4dd681ce948d9ac27b82a43f6f23119b0de4ba31108cf02ec93c53038a7163f2539588f0262eaee92e66ed030cf299fa03dd74a3f0571c66b5e76f95ba204de9d3b955354f53bc7509dad7f8e7169de94c534c0cbc3bca43e853482439b71d5c67a01bd0d036d65d76e5fe1854d0e923985cc151abed7e4ae614cb4f20aa988924f0b0bd7ec597629640001583632bd388419db9e27947ba6e93427fc3082de2dd24a2546cf3b7024f9eed1b42df7a56b588c888e88816a1dcd43df2b0793856693bcf97abd227ed703653ece68694f8d2c07495ec490c722d02331b960d59c9c0fc7ae2b55c4da7ed976918924dbef442795c2cc922f031c995bdc79173a291c0e13663f964288efb98d839289a615f8ca700101a22c7ba52934bec4f54fac46b11f955469bc14bed644e40184d12a353945c624a4c04f2f8ec36ba18b1024f4b01698c1e5f644983216ce2e3af9a85e2206a5c683c013c1e9c06ef95eff5ae4180e3c9c923eaeb40e7b57180702cefb6d368de9b81f2f6cea761bc7a4f55fa60eacb19229e5026e68e0892a7dff4183db1ba46ce0bcc19e0fb994accd11e87915984c1a508e2c3487fcf240e721b0cf35b1439a51757118b1c1481c0014e1c9d78d7303623d9ee86291ff9589d9a82c7e2d28b14955a7e526b096a8b96df6bd71e7dfdbe6a0968bd9d283bc7b347c39854eff2d18407e77e6d4365230ae1fe5e3e35ed5f49ee1e0b8465004dc2d0a3a559615b511ecb0c0d88487908ad1b29f9969cbe2935093c030dfebaca2454ee37322224fde714d23e4262e08f87f0e063a0bc9fb98fc9e2aae10812e2202da38311851983a9d9c901408159b6ddd435b87e4d29698496557762d9e16bef2b71260ea3d507a08db74187c1e8270dcda32bd8a543a47b1705df594dc0d1cff4e0e3146a0fc3b0778d912e4c8e34b56ce63e880558e2fe6263a4f5a80e963e5b0ddabd64c5bb73a6cdfac1bc260447d243aeada03a64d8dd5f094a2636bf62421f8c25d20b4b26d913508c9591420143211f55929e5b539882047029dd47d7fecd7809fc434b1c3fb8a4a940e9115d2081023f38bc76faf1dcfa48b46b1e20e6915c18ebf4328ca5f54858fc2f4816df280569903e81e6f68db31796c12d24223487aa2a178297a14e4ea390fe483bb8660f24e962dcf05687402390042d30a2c35818fa9de9316b3c79627400688daccb7d23d96e6fff42d7f63dc560675861cc35f5260f6236c922dad351e69ccc0756f8ad67b6e323a13faed0408200a0fbc0dc3780b22e7677cd3b232237aac64f862f33f64084ae221348546aa298c789a0d17c1caf968d3a262a08ee9bdd7653747dcdedddc3dfbd79be29c625f919f786811dd8ea280a060b0631b69941e50b0085f1dedd87ba954b097f5cf7304b2cf63160351c78fa4ad3396b4e58de021136df7c042e51440bf92556677cd630be115e14070e272fa0ae6a6f9fec24bca37cda4a40ddc37c9c204abfa6af193787b6718eaff64c0ebe362171dceaaddc6cdb3b88d92613cecb7c832ab7ce503aee2a0a64ded8c213b91ef35556b84f542792f9b5321002197392f180ee9a78cd21cd98333c305eb1f53372745721af6deb742a1aa622e7dc1964e377dbb9d319ce33e94d11a5d211964bfccaa969003e1d533537ea2e52d46f98bf8917f57247a4fea49c91f6711361f04bcbb320dcc97ccc81c84eccf956111de122cf40fed5e855841a1b4546af842fcf0a4d1645c3fb9505e887ccaa61389e4825dbb23ba77335633c895b41279e46279e02b18e96f566753825a7fe98c2c62fb9527edde51e43ae85263a1ba44096ee8135f2030c7efaf90720e8a7cf44a58953d3d6903d7390dd5dabd20ee242877b680de21fb206a75bf68834759d4a87d31dc2719b513ef806da4519b6271411bfe9afc5b8f7b1b6a0cb0ff02895ef908066ef2609a9730be939a87ce91cbb273e7c442f724593e24b06a9ed26d1ab4a009e67cb218ca5798f35c26ca5b71a2b065c7f52070d5f5f6ce7389f3654b49e888bc081df70334bf20e2c7b2276c07dfc2dd78e0fe6f260f2205c60f882f14180ff33400f5b9badac39d18f13ae12cb7654028ba0d88209d560d02dece6ca161f31249b7282b149b84d973de88b45044d236c23861fd6bc3884989f8a667dd7226a65b7419a1be57b9f50d40c73006ea32745945deaa4a7cd2cbbcddfe101ca46c72129a295db4c91f21895fd63dd4bb9cdaabecd500475633b252144fc64b93ccd6815bbfa47e73833511ac69fd38559a9a08fe6bf67e6197f2fffe35d51b0dca43059e8e54d206b7ff5fed050e71462428f233764f9cd883067ff7477984140b0acdcabc8ed97b6c001a6bb506571f5989e750145cd7182f8217b1e786469987e171b8dfb6007d39873a0c2088b278a1957d20d06073cf723b09c01008c52f118d5886749f0625dd612e5681f8ffe209a0124e6d0df07a9aca39029feb3de5840e3e61754e319a7628628ecc7312b19ab64cba12aa665c25348be574315432a751907eee057ccb1655b97718795713db1a76ae05bbeccb77395c9318e8fb539bc6790098a7190775ebfeec78ad4fca06954db4a18275fda419023f85bfd0a2fa705d335333ade35d661170fc0898a8f0759d3b2bd097a2080119db3d99493a626e3d0c5e23e797225824442b5935c28e2223020639764006eef088eb8f57a9d129859025f2e1d5cf16ecb8a6bc6141866192d813765ec3c215f2cad6c0de3cf190d94da301551af1847db2e0796aad32049ac830c25a10fe15fabe4fc74af39f9285fc19d6be8a76e55b190609581c8d070d0f708b8230bd269ea6c1f2d7e9681009511c0c9d4394a90b6548963b35399633bbb9b054a6b36c44f28cbb1860b1fcf65998b991c981b528b27147e7000001ef66a784360cf301a1bea9274bd7ca70baa9ecd245bf84fc825de6e579c629aee8f7ec0e867b30f742cdc2e45a9edc8c890b5075a8bb4e7c2df6c7e4a4159c989d941d59451bb67695a00dc60474134656936efaeaff697739af4a57f9272a9a6aeb2507b4b536be4643ec2189681bde910fe99dca4479c6681937a77e4f1432261a064efdde8819d950b5b00a4bcb1a4eb5b1a696ea43aca006ae917eb0ff5922430eb1f772f6b97ca30a578b4ec97585ba188025fa12ada180e563e3387a2e2d875e6db425555f31b79dccb1475a24fed96d52316aaf6a5cd932f85cb107c7f9824835859b396ce9302b104951c9c736befb0704ce8f5947894f4bd204065d4faa47f212dedf21149a602d261954d7cc52a896e81d7b578b381f9c0bd7694ccc7bbe1806537cd04354c002680976308d4f637af2d4d988beee962611a905c8319e3d360445f3741e7977eda96c0678465a7e08f367be9924f75bfbdafcf078b89221532a0bf145e6b5b9454498d4b35c2f993a346b39bfe603c57969a17083a49d724b09b6e2b92adf050c6e40aa8f90d38b5bfe7a3385764c05096cec3adb24e6e29fed5199b2966911bb70dfd6801ba3288ad9728f00ebc8aa431ee24fcde9f0457780e88a57a7872734befd65c282c5385c13cf1b7e20dea9f92ea843e015f2553c27a8697e0de286f011d7de2e5f185bba452553ee65bf820a04309bd75dcfd79eae5366060f09a9836a0723c4e9beaafd9f59ef4c2adcafaf8d0446cb7f84699a7efff1d32411d807e771e2e36b7156613f36246b20cc94ab67013d613b6b38cc7799445b435f1f8c0ad3e8d2a051575de7f44e10f00f320e664aeff8d9fc5f4ef2eac3b64a6db536b18d5e135d0dcd30746fd57fbbfd3c5c26fd1062e76e9594cb2dcc424cc8145025b79eff727ddfc86b24e3bce3161eb5a3471a1a60a12333c7c49496ea072ebee09659f78f7ad3dcc29bb7dabd0b1a4233f623519f32b6604c9feb7391181b2f6d304024dab983b833dccafd795e83f1817a87d00618d12008456ff77f440214ba82fc88143e5ff403c07a82a3fb39ddc2544d7d45ed2ffc267034b3a60291be53480a266152a8a30c16789d4f90217a19f7e5b4fcbbed992bc6b064bf494888e01b2b91d04d362478b4f6a653f4b7bba916655cbbebc8bac47745b9a16e34e4c709b8b05ecc39eadb585c847a02bcc593d964e63c423e414ead3113cada04b6fd6db9fec86f951f3a7b51193ffd63c8f81dc27106535c05c26c0efa52303114f43ac43a16f757a8a9e67f4d85f1f5cddbc190dc4f58f6f6333e0fc847ae275addf512e75c638f9bbef4461a0be4eece9566d315e9aca6265c8ae288fdfcf474c3e4fe3023cf1d4fb04e1222aabb1920cc118b7f489da1a1994649d644482b00764d04064ba7d23f054957b666d8012eae22e413485bc2a8d3ee75fe0a8bb80582f50ddd3f5d6751d44c63f76344f6d220aa133df4de18d86b5df2fe00c6a2fdd0233b1849b5a644dd3fd3b3d137f1a4597402c6faa9fac072a9f9c55106cfa8f4a36d3be334585445be77da1e8c072b20d8da1d464ec5053cd0b562f183cc8d8f1a1363aac40acef5dac70985f2842507870bf9dda91dced1a5c2c9a74eb0be67ae46e4ef49945208933da97b2a177ba5525859524f4c394295cf25fde937e1b4c02441d128a4345dfc4eab765004af4a97e88343dfd3e93521815972bacb25ca5b7226feaa98986cb2a1d4fa6634f04232adfc92cebb9f66b7bd5373c4a37c323f3301fd88708c52a01201ea30e6194ff2109128b318dae1af48bf420f4afa8e5b18236acd8717fbb265a7a72bfe1c631906c4b794e707b5ba2f94d243709a599da6e28724bf5a080b193315f9bb3f268f3f0a2980aadeceb715d091d83b5f048556b8c65adebea09ce19c6a526c00f44f18cd92a7ddb288bde3c681a513c099031b9af5da8f94388e36d31097f1dd3c128b3a88d941841c5afeb9cd295752766d3e85353ca02e3fada6f6c785d63f715bbc51cf480b4861a3feb90280b8b1276aab953290e8c30654f6fd3d11a6dcd1891dc51e6a51e5859a49c7e1fff56aa0fe80d9eb4d97cfca6490ba583f388702a3a41b23787d1b4d6aa81546e6eff9cb013a3fcbcb115e3fe7e068d8b21c420dd364283e4f1a36bc68e9139476546dfda5982cf3aa138181aaeb04f7d2b2c49276e90ed4d438214bd8d471df05f61c706ceae72ce548023919a98f7fa3169d0d467f982154a0f714c1f7eb547a58e98b57f43c4c5f33f6ca381024499232988d1b58b565ab2435e6cb9c35f25b1209669968f2f587d4a73aad5eb9144dcce87c224f6e78cc906b9525cb50775048c6a36263452d3af46260b8f72a15036431a1e6830fec2f8393935609f21f119798baa429fc4a201fd789dbc597fdc1691e8114849d2451cf3772b9178fe2712b59c9745ea4b8cb103924d3b7db400e3d9ca1bfb912daa478008c21c2345ef9aface9f66000388bfadf97ac2bdd523d7da6f48f88e828bd30a5bf9e5751a096e5de821a194d30a48c430c34da648eaa5bcd34ea197d40854be0e927de87d5e3a93a8519f5b56e7a978271d207eca08f90e0197bff84adb1db5c48c1c899540f2066795462ab2d86a01870eebea1f67cf280b03ba5ba14b0ef7cc42ee7083aa1f373386abc9160c50262d10f93556daca5a180bd1213564d6bac95973d0c9519c0d8559b38a2b334645a554f2d563069e7b72e3d3bb89a89ddda15b31c30c07c373e39b5e674863b72ee4e1fdb42265febc6c16a9df5f2f8819fbf7666f967007effc78408f46fe50f619187349984d3f59c7ed3b46f27bce1d9711ce83e43a6f657d05a47f3f5f4ca39da20a6a947ebeae524270ef6fa44fa952dce8f527eb6bc8818319e73b05a063723d673108e2820b30285d8f83eb689e2eccf6ebcf2ddd63e313bcfb3e9497f814993a251420e79efef3004cd65f44cd16ee16c4777dd44abd9eb8301342b3d53ff223973471d2ec5d2720810eb5e1da51ff9d583e1deac8d97aa84da47e315f31942480d10b8e3f7aecbd0b160a11084df7b12259a0d99ddb6e606f1439551202b33c5bec2016815ed70de6b3009901855c7a3e90c515a8662bdf4827a25ee665ca178c151ab0ef90f464e42b9cfc00fe404e2629087cdb0b57210d77238fbba36f7d53bf5d302580c34805f77a32ffaaa06df248fcd36edf08567e5c99286b0ada10138bb8db9ec1e7917e5303eac1d3b7165aac99d97095718f43d1e1e21f961c8604360639657878cb8d2a988cc6fe14b4838471e93d9b5f4000d6bd16096b047656f0a3c4a6fca24b4ecfc37a1e300fa3b13b437eb3db4112ae99913a82eb91ef2dcebc719aaefc0e0301a1d18c76b31ac25d20d8837c504df01834f6a7a03d248e4bef6deb4e409a82fdbf2cc780073f4c748503d9a881cec4005eac9951d46c53b792d3c6c732168496899510370765aaa9ab611210a35bd7bfe0938d4a685ed62e1ac8a09346973a91a5a8c939eb7243388edb0db00673a351b1733beaef82776b4b567812666fe0114ee0617e2340b0d4f0d4358080c7a09a961f25cfdcc539ef61621ad90036cc9e15c009aeb6b252452f3e5d3f5d3094ef5c23e793a22fe130e3898f7632c0278ad9cf005ac99510a95d48a03afdc33be9ff03cc32c40af01b2832ea1f51d181c14b186f5d155afefd093e31f94be008be92bc005260751aa4d83009b20357749053ec65d5aaf98f595c1f185807ea0ef0ae10572e683284cf7f4dbd88b4def0249eb4f946a578153bd4866ff0ffd085d7d6768c37c65732b9355e67356034f17a9079541f243c462b031951cae285944be967cb6148781a62b03fa70a7d4dbc7a1a755c2b4d1e90b0d47d97c14c3272af5edfa3a6fa87f3c0a80879883c2a4cdc96c406b3e461c7281a6c26a1ea040428e70d2e74caa11eebbac4639f8decc0edcb82ce7b68c590a9b4dda57744bde3d7cdc9e3c274ec5b73db9642d10e3f47cccb4614017154daaa39991191e2f27352180cb10faf3cceab4dcfb10e5234038d9f61c4e06106ec690af1cec6921170f0c623e4e04337a7be0ef7a0ee1230da2734211c5da6a171237cee84686c0ce14fc6553531cacd0e40bf13ab6a12841f6833d39481fde961702522b84588f44be52b874fe68e5fbb461d716d7a9b49a923f02208ab0f55920afcede5c53f3ee39e083c9e01b997d9a6e0f04021e8678dc8f6a3648f8783e7734c6cdc9b8d7dd10a4e79435b3fe402733bdbd137db27667a7266538da8747be52d6c9909721f9b9a13bbeae1c7b014423a14f18913403b0b1bbd2ec02f1485641a9286ebafd37018471432318e0871b29d78683042d07c6bac0a083c06014dee087606b92c2e9219759d7bf85673c7a398e72682dcf681f29cb410db429081be670285e91eb4ac28c70d5a9504eb9a502f6382b9f6f77c250808b97d1f5071b1015d55002acf2f9f3a09681050c58f9b70deefb7addfc0d6b02057f60426e21f6236cedf3d9f54eba4a743f5e29d1139ea8ab6681c02bc728848c03e290bbca49dd959b82ad7defebb7c9d789696dc215215cd533684f108c8d24b2016cf0f39e0636645258475393cad28f784371b83c5c3f7772a4a9f7dab0d241694cafde3a086230b668b27391220a6af86bd93c4264d9a1b676c320626ab14187f51caf9a374a9961ad5064400834365d01f6f43405128d12353200506d6d3c3bcbc184c7defe8346c67f45a8fb83b1b679cec59a613f762c992583b577be8082fca01b3b230b4546301e02e3b389326ca9eac9abcec968451540b68171a2ec2b6751f8218e2063df99b88ef45c76447dc1c6a6bdc886ee0fb7322578d10acb78ed89f2a9a06890b80ad54e0b48a6dc8ac41c04c35019f06a57bda24a7866e461ccf5055832ca556880bd87a418065eabbd111f1f6006dce20aa55efcda4387ba4ab633d31679eed833ea693f82c6c6fb7a9b826fea1b3d3d1655b442cc4fc08eed39567688d5a10178a766d115b21a1f2e59dd32e0f7c6a0d7c4f06de1659aa683b09962e31a863dc582b2946a1de2fbf5c5aa4d01df379e4a7b78ada380cdd52ada2fd737e043546c941d1407e156953a217cf7f7711aee380bf4a8c6bdd176efec9010a614074131beec2b52718696f623212a6c01d18ed0e23c6b8bbd3fd63e531999baf804e971e1de1e4dbb439446c59f9da1b097a2a95ee30bd0b2c7bde9a7d03c46d8e6ab01d7a59e5267704fe8d4668b5c82fac7192d91a60d547b123dc8a8255ab43672412f15b0f1db95f0479fc2297017f4336fcbd9faf54bc5495a22de5b0d359c6c33e872ce0fac226d0a1fd042d0c6448282955b92cbefe9844d7cca12b4a19167461109b20552387d25d3a7ade56ebb79441d50497b923651be1551c5eac20100e88492a54bbcf211861b0e41204c5336671b5123d2317e45489b45abc79fe55e0b5b9972d21f0199994d979e8111caf33ae07d13a15853f88aee26a0460522a3f9615954e35aa7a3d9fd064951b960a9a15914e2965286d56639043f5d4dd29a364dd73414d0643a9550dbdc940e351ede8c5bb304db3158c28dec2445cb016896227985d7588fb225afad8707771f0d78967d2eb09f6efe21c8c648cc8867914ce68e5590d396c74bfdfd20fe8aa53cffa8b14b3091d466139a97c82b6caefc0ff078818a18f8ec208c20c84fb214b2fae3a80d2ca189b181d6f2a9850b5d79920069086930a0298df90a71ab052d52fb2507164f3c1b7518c71fbaf4ed5e12d4dfdbee5b0199f4b7d0cf1e237352891439c9aab3852ddd268f52ce00464f03b6443aa40e4fef5838f2f5e90f3dc05d52d8a9e2d7d279615e5eb321260fd19105023763005ca06728d2bbb5620abdd05a5cf0cc8c0c24d8ff86d5ad0223b3b243bb50ca3fab25bcdc39760c04d8f2a0bf814f19c68119b5c8f0fdaa0bf7aa75274cfc30b1672c1810a7c42a0f9ecb7c8db58786ee6a34b91f792ae2f7a19085890aa653f182c844819aeb445b387341bfeb21129731307fafed2e5177b3664611e9b805e242207a2090498f8d3b41f7b4f03083e21dcb35713fc0759fa9ff6f67319c2f6377a7a95895e2fa699737cc390bf02d15be435e2ef4eb4678aec76e69264b880090eb241c04488c94ee9beaaa43e3f6c40d6a8d637a048477170818d598394c88540d07c155a3b1baed30fd878edf0828862e5dfc9147aebb178beaedc8e3919e2b33e5493cfb79f8c39267c8a8ed757a6bbdf93bd0d2ba6e643d076fe1fbb5746bdbe755cb2a665cdfad65fc150f7058cb38e7190095fa859f9d51abddc858cd2c86330b585d53f80d717af937454e00f735ecadfc0ceff89c2a0d9e196ce794d25e8d72a55f18bdde7fc8ce1943b08721bf63789929805d48a3ab52276c320006f6ac1e0db86b8703ba61986ee8950a2ed1be38a44defb2db55393de7aa1486a1e02cfacec5d1465a63007fffc92c5af01fab2c09451eee72fdea689806842475a2a518a982df3aa8fe691cab893add33330d7e8a17e1f3223a4b0c414303b7415d90fcad51693565eb4b3fe26812a25befeef5f5b73fbdfe182f12bd73c77061949c82517d30cc1e22a49dc6198c4211af38838ea5b622e5515568b297eb1eeb2639e09582b1a3704b2d636335d85e916e08a53b1f8f175155a098cc6330e6307c7466f8e790833be31ad2bda23d935b7fa5f68d74062a8f1a18c9fb7b0f80392ad1f78cb5b40f61a7220fb79caf4b80eee42b0f6c17df375e0b4fc9a7100af0c9c976cd33a37189c9ae39d853e4e9603537be8c69a6eeff1522f4238208c0beb20ff3d474a47196b0433ff0bbe60ec47e1157bda2b753de0dd8e0784761f146a80949dd0bbf526b87879ad9320635b3bd9df280018b3dc1c65f7c848dde78b2eb20e7981a9ae7ff7a91f2a8f5ba347680408afa6b659749bb26c9c48f72665d097445d2e7e4dff201c0ff010f48fdc207837713eea25af7f158f73eb610e53462024c2c9fbe75ccea90d13bc7f1e448c4b36cfdea86c11641f7e1922d00f2f9e1d4864c91d40607c97ac8e2d8031ebc7f5ba288f5e1337dfcfeaf0d3d5a12019c0fe4c76371811ca54069801aba595d32d7538ae73707f3612655ff26c88d4cb4d8831d5afc0833563d4ac2039d2ea97363434801717a8769013c11ac0ed6ae54b034d4eb059503c60106a7e21fc60c36b599fc90ef24603d84dafd0c4176e38c3ce392f7fc0060960cda47953c2990e57ae671ee8378368294ee13b3d59439897668c106385d9decae81d59278fad64d85ca62f08228ceecdb38bdb70f3f1197f809e9a9a30a6234447e6526f07bb7d535471cc348e7012623b308df1ac4816a19b529cc81bbba2c438872b0da2afca4d3496a304c56ad403a6070ffbad1d02dca4c3b05d90f54f1d82f20c09a1e05c7bb094bbe282e06ff6fb91bd5801229eaba3c1d71676d787b026f485785e9c17993d7ee3a27d53736e86c814a754cd144ffe2283c1359bdbb09422b45c6d29f592cb46da5285927d6546b4554da13cc65ee1fa22740161bdfedea00c7466b2643f0c3005eaa968856b49ad193fdd639bfa7d1e3674c91c92b9aac2bb01bb26b0a6225e6da9ee4f4ba6dd4d75d593ce761abf0998d6bd6cd2d9d54eaced69b7423d572862cb204970875912faaf2350f53e0a0d36e159c77a6f91603a4902b2519dd0d93b259571604dbf3d55765335dc752fc94b23599b08b050c2ae0b5448f0f97dba61425e89fe7d4f692c3e57d0838fca8777e9b514e9af1ea5b4e804608a616539000d469c7d6b33ae34f0f729c360f204f912944639c82ad312c4f31eedbfee0e00b7834297eed56e23388fe37b328d8ae927b4a8efe216611d8d97d8b835619f899c9581c2e427ec6915b2d2a4ebcfeeb0bfe9f9801f4c0275d10144548d8bd19c13e712d93349e3141c881a715d76259441ce4a2195ef70f99707f86991fe35b0a36561d9b11ca3fc497f68c613581ac83b8cdcc3dd9d1bc6eeffe13dddf0f6b95c135c8fbae10b46ba6fd8b91b427cbf79dce92b382b5142dc4469d28a65ec97e83913bea81ead74abf065ad254eb6d3bebeef60a89f5a25cf5d00bbd09ad583fdd17e7a6f186f8179df9d91919469b5c82e9045c8dcc59aca244253ace5f99963aa04ec1b91588041aa28566a89a220a3da54eeaeb5f889d87c1d75796bd00f5437b901c80da7cceefa3254802e8e2d62050984cf78fad896c8178049d84be4b4e1d6a1960795bec6df3e9be32b2c7ae6d80041627b398600d9eb6b7d3a25f718d40b5f18f5384d27782b4de90bc418b5c232b1614e4520b9fa6b47f5700806530b95a170921e1a53c4fdcba30f9aad59ac8b28710208d5860a529e1a1ae1208dac52ccc0dacdba22230d71b63b32c857fb1ebfe939d5fad572ae0828836a8d9fbfee7b880cd3a4278e5a3463b394849e731c6ddc8387ab43e443f6beac6971730dc49f700632d089cdc7e00deb0593caf0ec76d05df7b192f73f23184d42b4f7ecff851adc419d75c077eeac863014de8a58f9fb325ca546421fbad6c62aeebb8959f51e7a1ac9d29b9910ada085c85d5f1837e1b57ae24b4b93f5df9997cc73e04aa36d6a07e0d550ae270cdb3be37da01e4af63c3931343b912cb7debbabadb7b522d5541c335ef0a5d0c712d1e5d38de53f40df5585322467df7845f810c68c76dda9da9fb5a6b7d92fd5b00e9019dd88f5579c552ff95aa491df6dd2fc09cad9ee085f17ec652a4a385ee91732c0557620bde0b8b1a71d7e6103a8e90330d7f82aa9872d9202a630e76ae9c1737d1b658b124a124bb19e866afe9ccc25ee99b0e00a8214fc37ddd4600b27354bb58b332c9d6d78e690052955f4abb8cd7492714c59e1f1a848db44fc601834a812196c18189c36c1fa60299c464e1b246264be61436c4dbe08be8f782c1d1383b32b84e6fdcb922d348c2f6273c4c8ee0524989d1646388fabdbdc56fda910ed365a5cc8694d6bb314956ae4b39dbf999381cd7623de15b9becbede367f99c1db1d373769d30db38909b162e4339603e18e8fbb08d1b416463454ba419d9045a350749ce859abc5cfa94364b688a6939f88b325fb96301319cdaf405354b9cd62f8dd2c2fb9b90189a60bf9ab11f3108cdb9bf0fb5d7537f67aeead166028662ca07aac57fb86704e0d83217d64bd7fad12acf24dfecd197e25ac6bf9cbf0cc65c9bf148ec0cf7fbfd71dd6ab89358a7510a4f014879568fbc7aa6c6391c19f026879578348a59c7d35786fccac19f0bcc5a7d790080459e0b63d50606a551bce726cbcae2c01949aa3d35d47a5aa461d5301fd0b5069c03439c8304e5e76258283ffaa5b01f32505105f740db868ff561dbb0a45b2e2b72362250481d05083b10862da728fb926bb0b8ddc35de8cf87d4169562febd1a524e9b533ad75b41e59e1c8a7c1cba1c9fdcd16eada51cd5d86769b9073a9c442e7acff625a7dd10ade5c05fd17f1079c86334459d97ef4ba29007a5b4a64d804ce84b38bdcd8e02c61d1f793d50c60c7578cdcef582ae4dcca74625ab6eba6a2828937c2f9e6ea9a56ada503ae4d900b0d4ff84f7b01c51972891285f8676c659227ae3861b0efac63ca07668524c3264ca2d3ae502a0a342f60856bdc840d4a0d81f4018378b36c0cf7805f6b52405f25a29b75417622f25eff1ea2337f286acbae0bfe368d0c4580b941eb97328ad4623d743de60613f6cc48f248905479e7467abc63b1d0d232c3cbdd3cc0b05a7d93b49cb47e1bc20b05e4be2c807df9dcc11ce60e8f85c6d8b2cad1e9fd424b5e65078c778c41de4972bb1136dd8acd57fcc7f5f62d57585eb39d43d807705ddf6e0b25bbbf0ad35e417c713aaa90d1e75a3e9e411b583137f019b9453bf00d948ec7e9b88bf6e193e0dd3f8286bec8f32ef8ac2c8588858bab1c984295b7a44a0f43be0a3bdd44cca25f2c6e344304047e94ca94bef7d4567f71e1cf7660284223276374c01ac173b543217e095f77ed05cacff9b88752b540c300f6ab1612f8c8009e470f8e2b7f3853244fe9342640b5705949f58279d57d5a41e2b73ed2942981cbd13e6aaedcb0cb56dfdac427700bfabe36698f757af72b8ad9ba5a25eed143c1bfddaf0f0a1ad613d3857212e6a152a0bd13934a7f9fd567a5230516468f2deb78fac82a90909ce81073f2ec935291c3f09b3279609283f490fc758819e09d92de5f3ebce545a35d564e576f67f45158da1d4646f8701bca2063db25cb02a087817da464a2a48102e3877e02aeea535fd4da1651a1dd565618dbda9c8de4c4fd77c41818a9050688b2578ccfd1b4e23c17de61ac40ed2d61167afcbae548a807fb67c010eb26b9fbb4ce3791f228e70e06b48e8bb44086dc5f4072352bdcd1456582f206bb3c078d09787ab047ef547eb688f07e30c7dc30870156bbe07a2cb6f716758274632606f0089d4ecf3d73658cb392a0528a80f19779e538d600323ecab97a0e33e9a25d1817902306e131759c315b6857453287c0c46930b4e80f0a7fe4ee082359b21a222a1bc1d4566762e9dec8f5dae24eec8ce4ae9b51d492a20e4f923aeec1b74660d4b5afa370eeadd288e63943c6c25806f60a77137546ffba9091b0ccaf2093b6ea353abbb5cd03c56ca06b08a4743232cebe01817d8f75aba377972b0c7386814b35c940b5b1243e75dbe67d8f434ec204223092f4630adc1ebce4f276c2604c0b9785106138d63df3fe69681d790d1a054d1c18d4ecc2583cf7fcb087d5311404e5a8a51613f932174ff93e1bce5580b80abc9a025281091d46340a90c63a0ae926344f531487a6dcd78a6df4dd7fb8b419a6c9d2ca3031aa8c28b5d30ccba9a0ec5922617dcc1ebb501475a21a6bc70491b98de1303dedf668a235f05dc25451a34654439ef38be72d3c3d1e6cea9a2f3a7aa3040a48afb45665745254483e64dd3bf0f21a021ef8af767beef6fd2dade7b6fb9b794324919670867084508159ce241391f8a15cce82573d99e4721215b0e69f32116492f872d7b82b1e7130e23ec1d8a6c5761cfa71cc4d0c721fdf555917376396f38e47c05a91b08220404bba6b0ebcd3b8edbf88d0d39a72b67a753994e4d3913131393eaad16325f2b538dd5e9baf47495313131c9549ccd76739bdf820809827f88030df156557df97d9021df97f3f6e921425c3349fa97ced70f5299aa4e22e71f80ccd720383f3ecda87134b665dbbfdb6c1accd7f82017e69036af43dedc5811799099f7b75a8c968b5aa53a3950c5189732ba36d197ffcca9413a362f833ada88be74727490ea2269527551261a5d8e32bac4b54a91677454ce5fd6589783973dc3ecf944a508ce133991d3245a8489ced3292f60a638207b782bcc2115e044b3ba847e7e5aadff71ccb97e2ee70d290781726f8abb6a1512fa11125acaf4b9a319cf01e3e75a2232d7653ae21163cc43a61b7f0d32027feaefbcc7737e9f66e1b0ff8d7bead2e777d8b1483211bfc683c4af9f45b2c75d78f44e63f14642f0e78526267470ba56561a0511b267ed7ee62df339dd6920d3b6e72cf2965bcd04ffc45fe441d8b1952157ad80f1ea3ce6398e35dc98c70935720005f53eb814972e5251f2a42f4f33c98125792b47d3189d51fd64cf9f547bcbee40d2d122cd9921cb38214f5697e9aa22fe39ef794e0aeef0f01dcd2ae2f99de7016f686e6e6e685c2c0ae1b5f3532ce2f91d6dc4d22c9adf791e2328453ccf7a239da74639cfa3270d196408c9d344a6bb88e76368232834cf13e38f5e34ba88f53bbfa3ffe8455af03b3f027ded3c110bf45797b3a6ce4011eb6368a30de60b5797ebdc6258f05acfea22f5cfbfe6d14444a014fdf3b09ec86b9e8fa17d54d78e26b2f33c12d8d19345b2a69e3734f3e666ded0cc247b3e157948223188fc5bc062c578124a510ccda32d2089b0b4479f8764d17c8c9faf1d9245a3591aec3ceb79f07c0ccdd2af2745f67cfa379466521a1a4a3b3a6f665ee88d66d2190d79c81d92488c67e9fcce4b20e7799ec863901a5717cdb348223abfa369e88d2e799e88cec77831e7795e7422a6b5e753932e36bdd5a0185fb67462b030d68941d2a51f37dd79fa19c88393a44b3436714c2239cffa1d92f544747ee7a994148ed40d95da8941ea3c0f99f3536469a0f33bcf43e777348f9c67fda4d134a665c89c8369ce5325a7b11c927271973f5905cce2d0db796fb5bc2585e3230da494c65458d08fd14af3294c1b3594066b01be699ea563ee3987687e262fd190b5c8f4a15f436dd414260dfa744697350434d3a7359cc77ffab331975c75d618095c69a8a555afb5d65a6bad4cc250e7ea129fc5037cf1411edf8f19c4efb94ed0ddedc67a02196f501a9235dc55b1cba02293d60aa91a214170e74136220265e6dd841944542f3083beba6acab905de4e65a02eac204e2f5e359ab56386e6c397919973cefcf44b9a65e67c99cf794e994c43ffc7ff0f22c4c8c78cd498a5b254760e09aa770c31761e78cf3d11d5ab5eb45b728e4934876ab63f03e690b7c73999f0a5cd73439e7bbe549ab5237c9f2a529470c4fc11e44021777b4fc1a713739cc8fd383a753a6e6f15f520f248407ec99c95166a6cde62e17f244b0310d4b3ba42fdbdf72c1edf7bffe9210ca82ec7f97ecfe25ef5472f4fb32e0f56f7e37bd5177dafc29d2e12fffb4f1b893a08a82f77b923a77dc31d44e5ac95dcd37d1fecc8f287d57d24abd33c42cdc21f3eab7bf18f5e1e8b8766618d9fd5e9a2f0456d04a57ba30ed447af8f7ef0b3af2e77f4e81de09da20ed6dcf66a16fd72d6e8d482fecfa169abd17e708cb9734f054a5d8f53e720f27c23a55b7da54c1f865cce5a8f1e07b0f9b2c70e4bb78dced555675ceefb9cc39f9a6c2197bf417f7fa10c727e19199907694d4d4d4dcd1fed1a7286863cdaf4ee194d004a23d778aba7ccf8f14f31973d1867f0bd05eaef896a06d2336d8fbbfcfd26230579bed338e7654ed81caa2d6ff5a81c9e400298600c24597cd5ff84b5a6227790020075eba706f9f8eabcfdfcfcd4e3fc580522c6d179297871dc79a794cd49085b4af0e18a06e8a65fedfb73d0cd2f69be317dfc9843d5fd68535dd20c4c1acd8714752a66d55bf365a871a0f11de87bef6b70e06e0f480edc8d835f4562ee31891f879c791539f3204896cf7a500764913a640e39a74b66becdd3901eccb77970d2d8d0d0d0dc7d43f33536244d8d98f34a8be2ab4abcc3f99ffe911cc99ed68c1a42b46cab8c29b91cc739c739de382e019c3f7b621ea3b573a649ce9ded4fbb4c707eaffb3c4f5928b6dd976ad26cc81cf222ef3feff1732c0dc0ff5e02a09680a785b86f85dce99f96c692e7972d1c5626cb0586a6249854d75a16955cfefc30115fd4f993a2b69ad0aa7c9a55b40d0ed9355c7df82a92a5c1eac3950e593c444a021977ad1f8c1bd4e58e0d64dc5de7e9ec2debdb3748c351fcbeea12c8b824cf5de6397acbdb75531aa30375da03efe9db0781fcd8ded34969800f9640c6fd7df71fdda08f93d2a8ef7df7def4967f4c545fe341aaef6ab8cbe61a845ff6c7834106152239bd14402e5d6c5ba06a332c539c644f9230c59468d294182f37292e5136da6ccf396d92284e2f66d25470d3443c6923793e408a0c14e61bc9656b0aa1e5219583214186ccdb10212121a1214184dc820819626b90213ecac965bb73e9600a5187c900ce9eee098b11d410eae004f9870f4c6f4a18e7dc026343cd98261f64c4f063e70e358c7bbed39eb40c310224ea8fa48f15b9be9d35deca795e1921c8f4e73741f10ceafe665fe5eaa2df91a3eb31d32c3cc9db3be77c3badb5f6ae6ec77d3727be42ba7d6fc79153dc2f338932c05bb5ce5ae7acf34e3be79c13055b6bcdd95b638db7eaf8f3738d07e15725725d9b75d639c588961662a081d6724d87f1e8f3bb3927ee640c81a95bd1b39221b79eb4e2c7176cc894882cc3c8c4fe018de145a64bb116688c2679e09772d9836589d326632b4c1a2cd86c537559186c069b59d8ccc26616366b22d3a99bb764907053ed404377bd12b99cb01ed56dfb615b9876a5f6a9f745f7056e194d7269c19e2419e791abd5a095e6a654535c4d9ce26ae11457fba6b8da9411d989da2e51b9be6572a0259b85b1a5551f32dd5cbc55bb484d9fca79315dd56a6e0a964beb54dbdcd4ae5c6d3b3950978a85ab511bb689d2a8514c1b1d501af5837e385057a5e610f7775251a718b7e428efc2843113a5b3aa5845f705fedc4cc06d39240e26666afad8bc55bf036ae37a3169d4af5bdc755740c5ae17cce865d7e7b22b4db25daaab3ebdddacd3cd3addacd3cd3add5ce64cd68969774c9cf8476fad2e52a95e0cc53ff2de282c0a8f9ea8c2c96892cb7a67170bd4add52099b7b65ddf3a554bab41aa9fd9da76a95b0dfabeb6987e698ce524298c731ec77f0ec95bb5e438981279979cffd1e66033651e3919b5b134673427cb7a756ddcb57132991179cee6d75dd65b9d554e565df53137e33e094e46c1e92d3b9bb1e8ab9e65ad18beea45ad7a2222c521cbfb30b47615eaf9b29a45f59c3ef44b9f85abaff9c51aab41add68f63ceadd6ff38ca623333dae803afef3fbae5ce2e964b232f945f6bcba49591d689b4b4a66d6dbd55579daa4cada58f8d49bb3e0ce39cc7f16b9d7948642a65f3960c156c5cadf39514f58605164eb3d96c369b952e639a61ecb719bdcde8cd6b22d74a4295aa37f0e7f392e99e52f5bb881b8cb01a3366cc181ef665b5d1075eb53e91db29de72a95d7acd69d5e518e73c8e4bbee42191ad936df296bf0c156c939d551a387b26b561bf14aa957e65a25f659b665b6bd3a60f54ad2e511a54575d0a3975ea692157cfa272d6d65a39ae723adb8c33ce18678ec6808ceb679cbdf5b80616ca00d32c0d6f418328eae1ada794524a73b05f605c6606549c39cb71d776ff5383ba9f5151c85f98c277b2eb2182a00782df78f366f8148681873bfa8f45b6df3d7d20de83e39ceb541ffe286c485211a50e7e08f93c1c9233dcf579b88b6cfd430e7c70e01fd698607ad8aebbbb5f5b0f9bb5e500ce1e5b6f489caf64f717e4549d4af5e148a9be8eeb4221e28d215ef27df1c3f70fe242fcc12eab9e95f3ea698c6e0cb627f1f126c47f08927f5ea998e03ce87ddd6c776fb5c01dfcea5578ab5ea5cb957ebd77e28dcee66b74996b3eab9e136fa85636dd156f745e87f3385ff3e5940b7a5651ceeb60b104ccec959375f6911cc9eff90d828f7d71c89f19def2bf194c1f7f2297e55b3b20eef27a83780b4847d627aa371e791dd7dae5c774f95b5bf30c360fda58a82120f84bb9fb297aff1b74a321b025d9932948494aa0f55fcadc7758b2f7436ebee36ede717e14ed88f3f6e7fc71fb569348320e79430e7120f0e983460e3491863890ccd33772a0fcf4296cf5d4ea00bfe671805fa3774de99f679ee68866c65b2b585efd78ab355d572586de83159c02998c86d050197d42a750249a4495c0ea59b0bdcf14e73c8e38eff842c89e0d91db904614667443da10551b4cf66c8e9c1a3284088aef518daf9cc0492bb520644a0477bb373724ce0be15485c1781b57c8be3343e2663dd7e49d913aab1625a34dac69a9537aef0d0a83cb65655df5bfbdb07bcaaa7f854f5f2b4bf3d5caeb05b32390ea8bfeca3aad766ddf03b5c5813820de739ef5dccee37c0f9d1f9fc82f4b039ccf791ee3b334119d1f3f47138152a4f3a376223b8fa367d5e58ebd7a276985cda119384f593c5fe218ef896f693528869ea00ec9b279d61fbd7648968d6669b0f3395fb4f3396f63a38b749ea58d705ee78dc6dff9a3978d4c4d6263e3394c55a66b4f4a28bd0c8dcd8ddd25072a2f9102aed24d427fd6d97c5961369a01f4d26a9095c9b0954d1ffa443e3ece4f7107d2f8384f6b8ca624b0d826270762fdd4a035879f0c84223082e390d7e62f17072aadcc81eca67fa11c683e1d9164d3bf3507cae9726f5436fd3b75c5781012387b3ac2894d9f43f2200b6637ca81e653d4d4a67f6d35887bd1a7b1b19c549303952ec6c9814abb64e640e58dd1689c9403b176143d7120aeb669cd61534e0b962807e28ad8f471988ca903eaf6bf4b3568d4383fab0b07e779d02f548680e16818cee3e80fb09e48901fb50f0dc57ece8f5f4e2928464a694eb39ae7894c7ffc9c37c27919231aab2efaf9f3e3905df2ac2e1afd95ca1c9279fa76c981689efe172b6f2c77311b2edea22fc5066fac065d8cc7f1ffc67c767963565f30a60fd78593f2168da64f38313b2b12c86772c76d2ca7ef359ce791f3e3f3c0799636ba800c45886961d444a014e16896e76896eb1d398ff3b24f72c891ac49d5451f87f4c0c8b6c9ca2a8c3e7943f6a79756832617382449a18b2d7ca952993e14ca8322df9a7e21e72faf8d78634647fed5e3c8bffaa36d146e9ab7214bba6d7e454e9fa2dbd4c8bc2569c8b267cb4cd7add9ba4f9f1317d0da20186dd583dd8ab55a55195285d5a4ca549ba6d42ab5484db2b2dea9de82adda5161d5b54556ddfc7d7b4356a4ebb72679ab223d0e79539d36fd90bc01c91268979706e4d1c7974683705d6115e6f75eec15565dd32515c67947362e002720931927f1cf880f6230dea6cc271bc46cfbf84bbaa452894856eae9862a9b520c367dea54836848e0cd061836c5917a9a12934da1af12718c2fd972308740a03bffe775b853ad38ac9736fe6d2d7e2297799aa791c9e48aecd103f45820f87d0ffa3528cda6bfc34707f48178902a03fa440da00f7e5ec5f11b8421db3e91ab2cd8f38efe434bb69a49d2bf66de897c86ac71ab500d2b43de0c6a58fde32d2a02ede77afda9d57fc22f403c5b38f006c1687b886badb5d65a23300218a72a3e052cc24c728f1d38640ce8272b70a9a21e357aece851eb9173ce3d7614d57614f5b876470f216fd11d0ed48383a639a3d4a9d42541869111b8121a62d304304c774ab3830e5ee825a4160cc8c129a54e67b0369b0d2ac80bb974a6ed4c381f26a4b0810a909cb0c194131352174b4b92a49e9cb04198272b389cb04117b35188bbe7930964bbcc2f83821fff01402dcfecf954430e4872def3a9862700ec90ed9e4f3530d16252c30930b89271f67caa610a0cb8c8377b3ed5f005064932b7e753939809983e882376d9da4118f1f3d4e4896df77c0a22cb2effc72211c2fe8d6e416f357e684de6533f326a432693c96432994c269b426cfb348a6ec102de2aeab8dc17725c48e61a2e871c076ad61742d1eebd31bac51cba975cedcff3eea55b68b227f7094b8a9555d9cf8fec3ec634aaa3356a1b41d4dfafb26bf316a63525b9fcd9f6e7caa2b2f7fe6595714f3c8846d9264ed31ad6b449466bd40ba5516b5fac179096a907f8c1efc41cf8efddab2c32f7fed88696cb9ffd8910d0811ffc4f977401f8131d007e1962f2febda4ebf89e7b1c9fd6e57d0ed08d3f141fff10f086d8032f28d6f01f070aeb1377d9fd5383ec4bacb24f844c65a0906f8da38597546dcfd21a9555998ccb94d04daac3b4265bf27e6e8cb36c2cab7d8c7f7e644b41e66d6aea66696dde682d28e7f5bd87dfc3182fc59498c61d5a1bdf5bf6b7a53177d55fc232d90f77d5bfe1f903ab3f4e5e1559a65ee89615d4cae742d16899e63d7eec698ae57b0c7edf7bdf73ef69968efd7d9a45e49f8f92ec3dfe3cefaff7f83d7ddfbb98c5d95a17f97e496b58bc55033ffa6546a35e6acd05067df095a0bbdc4235c87b8abb77a1246a63ca31c65dcedc385eed49d3870a2565406dcca7424d55897d1afad21cca40d51cc7dd0feac05df62decde3fda97e671a636a6aeb6193d6748dc7f1f57bf7a5939be520412fc05e5fe07f7ce9996dabea44f92cc913c4f644ee7b195efbd77ca5bd6ae7e7a6f6b49dd9747ae7e5457fd624e306bbdc7d883e9a0df7d9df2d6b4b86e6135551bfeb2da7c78ec8703d9c718df171f3f381590c59fa28efb62102bd47c78cb86399bb6e9be242bc7fdeeaf76240c2359de6dbc7b64c873a685a9fe7b5f6dd8615ee891d5b38fbfa55c4e29cf61db83d5a095d6512191579fdfbfac369b03ddb78e64ab5230aca914b539aca43fdcc81ca298f3481c7acb72da6132b070da5e68f394a907f7bfefc41cf7f183dfd1ac412b30c4a2f844fe7da28ec9e56e100c6d9e72a981a3fdbd0881fbf8a9a8e33ed6e2026e0e6ecbbca72120fef7a07843f51e693748c57b498f043db2a45946836086035446fc4b8a1f79ffc7c6ef91e5b83d2aaab4e7912009641499e417c9efc03359b7b8cb3ede3e6a107e85d5e642f6245d4596b97924b8b915595f1ea9da5ecbc95671b54d1ffb9f44c67fdf6e9b44436de88420b5648bcd1f35e847a806e159fe1ab3dfb5843cfbfd2f91ab6ddb47807d960eeef1c330fe74b5551bd374d9c738e7d1be85652772e96050cecb3e2659dfe3cb912cee3bf10698b3bdc061298b124eb25011c5cbcea72a4fb6ad71e40f3773577d1aae65dbe8f597dc559f09cb72d3e8447ad9f57b68c81ea334be8d1d40c5ecea91de86c1e61006495ec4811c68d33fda251583e9944c88c3ede202e866e5c07fff6a0a06a4625e3dd645f9655e461be53f7aad7e54d74c4bd39ee20d16d644afd5e7bf1ac80cf3020a609b95c36efc7663cdd241a996dabe02fb2939932b301e2b8703e87b9a95833bdaa0b8017dfdd08dddc8f4194012d89edebada6394467d0b033d00571ce6489e54679ee4451ce6b0fb778b035d2ae5405cbcd6a25d5a752ad77f3a356d2dda6ad1a9d61cb25ce89649a5f6dc6293e026f0290d0acbcd974c706ebeefb857fdd7618efbef31c73ddd9c7deebf27a737ad0762fc16bff79da8c33ed656b3ec7bbf7aef891c7b7777f0c3813e6ff5f777d4a05c5d36e5b4edfb7bda68511a57e61be040f9ef9713887dff670ef9ecfb42431910cda1f0a7456dcca8d1337cd34c99e11bffcccbc8e819a51fa6192b34cd4aa1cdc29739343708923e4a72f8f8c507c3f0c110fc50b374ec30fc4007a859e0afb417053e7e9508be07fe07824f37087a9e66853f37793189c36421fde3adabbbc8dc7f24ad79ab7b16dd5ed73dc61df54269d06f927151497775e8fa56037463fae007a0d8179f7b20d6f34992bd0faa4f926c27deb9e3c8f34466d9b7f9a317cedb7c11cedb682328f68d6c6eb4d1cd8fac1cb583a37df7557220d5dff1ef5359cddf2f2993cd25aa2e990761dae821c481c4eeef1b7d39b9d818ecfb3fe690b53113640e797f7f86f418a5e1e37ed09c5b5cc5c53dd277d7819c7843fc2b36e0886e8fa43ba434fa3e6a1c94f4874d5f6a5fbf403cb12f9d4d1fea4630893fe78f5e2c5c84f339361ae7b12e1a6ff4f8472f1cfd63247fd8903e4a32cf13f95fadeabadd83fa4afefbde5ba129b92626eae0bec43177900f381fdeb24fa5541e328194feec1e634d044a91c703130175d6ef1d49ceeab2d406f63ecffbc00982a27e0f5561a87aac33febe7b173ff171f7de73f841a29f5951f75e0b3f58ce19f73d66e9a2efb1ce2208740c8bfbf74816a73350e4fde769efe70b7b9df6e93345d007d0079606dd7bcff2c7f8e3f19e03651d8a1070cdcae16fdf6e513adaac1d451cf4b01ab841a78f156ff8f4b15fc5397dacb621fd942c9313cda93ab5e122581da6ab5aa71ea65c72b53974041735c5944b677272a6ebe4f489495cc935683aae39bcb7de516881426f81d3a77ed9ed59bfbc78bbb73440434f674b52f04aee3607e806627549b707f641a863c64c4ac3f334b65ef747bbd3737bdace21ea85d38ec95032379dd2b0356ec67536fc219716466f30ff0a951f67cd894bf7062edddb126c093687a610484df5a32a54857258ccdf22b92d525d04a424d8f4a9e1835f86f75683426d911cd4585bd86d5b98087259a796a6e6d017d446ddc22d714b4a21207912ae23389dd93974c31e11e492dee88dda006361b0593dfc341310cc5d7299bc75932c29792b367d02fe2ab29b2ddffe73d2f007a56a10385dfe17cc8266fbe3f8dd89053092bbf7bfa26692f42f03fcfd02a0904b260678efbda73a039656010510720e4a5597006c752a80ab0a352b87dde1db1dea92c89ef2d6fd21d2c5b8ebfee7449dbc75df0024ade2aefbb8bbe0ad063d2875bf8252fbeeaa2b92b7eed7f70fc37a54c369094b407f70b5e78d1a5bcc9e9e16a5e0ea468d2db2f59608d62099caa4b6cba0613a08e298f3fd0390867bcea8135c89392ed8ab8a63ce39e7cc60cf05503de7a474ce39ab1017b470410b75bb2e8d4400674f896f168c07e125eef2b762ac1824a4224536bd4a5769d3db2eeb33b164cf2b7b3ed9a43678410bb509492e31d4f6c7500e549b1c33d5251f6e870ab515e32d5f926d937bc923b67a1e554a4df8be7e5fbfafa0934cbf76b5ab5df7714fff821af205dfbbaeebba0f042b58a93bfeeebdeefbeec16f00bb7bf0ebc00ffcbefff48c06e85abf0026f4743c20d9b2c17724501b7c47e26583efcf3de824d39f18e76c671096c9d9b30024e4fb17d490edf7e0b76a1087f3c61bfc48b2bcdec23fb14c8fb7bee79eee398740a0377fd101fee0ddf4afce7101cde10f3e48410a6a0ed00dea06685b0013aaeee1ee131c0c91448230823852041058182145f45b1533850eb31d5e68f2058a2ec9085937ed0e6f29ed700205296870d2430f598410a22b2106ef488f274640a2c5114d767842c2b64f63bac0965841142f4628cd89050c2b4788e08624685c3a101851041fb1e285d203591246872637dca0e48a0ea966457ea56cff61c8fe5697970b058630010f5d58a01dd1a41ba126e5dd0bc05050124b0285a8147422d46c58cbec06a428aec49a88a10ba196053c62830d2928a288305844d0a183d576a0a1476a0f15a69890984a32c6c68da94de198e4a815ac0842052b5d48490aa31685b3b956259a8070fbe2851794623a7814b518564990a00b1451b4c46ee0c0f0e891da0bb7c9044249102980e1e94b1556c4d490d85a922a27a93e4e8044851121d030821b56501be132a9553bc50aaec4906a21c4f0851630d65afb46f28eb1ba8ce45aa9d0ca83521213145844b0a289175abe702e7b1b8a5411831329a418a946b4508195b4446103432e47ccc930048e0d159640291155c44a186e4acbecbb4942a0a549971f6eb089208c161cbe7b24532d33d0e4249e444b0ba1529416116c8ae06265e566518497ceb3057ab86551c11547c2e8418a1364594060c20aa618020bd74511537858977e6b0a9e84cd051b0951bc38c20ba78222b6747ea4c90b51b531379cb083162338304520e1719574627b62427241072e382e94ace005081f76f832858a6fe96e96215172052d0460a2102326054530e52bf32506206e43d41053b205e74b59e856d17a1223c4174d285061845b1125a86ef55202272a2588218c089c1663b02b390b3cf4c0c50909214449ca62e64802bef80085890995159c705fa400b9903282929426b65ca14316599210a960295964b952801e9414e1f4830a9880e15464a1225ac942823c44eb10173c48d990c20f27c8e2258b920c9309446df1c4c6b43404150e6a0c9e4018148288d5848872e3ae80020372d34a1434ae64950081ec3df244834c0aa72b2a4ca9370c111ea1800e26e410a50a132454b1026aca4a890424a258c941845b9410705da0986aee110a85e403b27e641a51220cd314314f5e64a9022ac67147fede7baf9523271494e0e10729da123a380aa0eebd7763d764cdf4f1b7b73c87ae8bf92c3f846003942861603b7054101106c621635cab942423584871e10a264b8480438117d78fd401200166892c4e8cc0040e5c88d862ccd3c7c6841a5875a18b2b218922060726aaf8a2045db829a0f08e14e96207134819610a14332998e8c00c332c524ed084165d4411757d3a81029715580821ea85239c1411b4d06a21e20a771d8718931980b08202a530382888a8c2390d242c9488e24452d416dc13449400ac4cb630c97828e10712e08965b6f260d10483ad8a2a49ccc09d40866dddda975f212289c68a063688a0092648c022b6026e0b11307ca9fb8c0bf188148d5b6a654631d101d97be413620431b0c005942c60a858640939772b26e894acb5d6e71347381183a2e9052a489c280178a402575491c2298523312e4d2c11876841c458487001092e464040e3981822d6b9d2bdf37ebe3fdeff5b45b02f09dfbdf7823268c073885e24c0ccb09e5c266b7d66a1457d7757cade840acfe7e47499afb676de5c75b02a7cc102172938114105596c4fe0287984791de167937a6489ceda24e53273bf4302b660e2842b5d84601ab344ad6b22e2e7a8290c122eac70c10a09342580105725c83e5f2da8d5cc5aa11a84adb5b26de8c49624820f25f060c5cb72496cfbd4c3b65fc381eeb6ba0432330bb9c4e37629e0cc1864e1436b86002c11802f0090426b842727d70bd99312002926188491710c00a892710b4764156a7882b9a2a4208a36050094c8aa3d9f68494cd17ab8e04bb6d9f38956c3055eb2b7e7138d89b62484ecaab0f29f3dd9b7df696387d2a0efd5bebfbfcf397d7be9e307d874cef93bd4460694067d9ae9fc0ce8d379c3891c4fdd8e727eabb575fe4cbc13a3d571f7bd960e28dee8eecd1cd97117efe018db6d0f95ecf70904573dac7a08c1cfc3d539bfe00ff7feb8add53bceddbde3bcd65abdd66a2bb5ee6ea79d6228aa9c521a1c9deeee95d269c15a6badd6ef9c40f54bbc7184506a963cdddd67786b3e56d27577da8eebf0c59ef5be0a86d37edf773f50bc4f2c8ded8fc505d8aa050c7113f3fbf9de5d3a5b5dc6a1f50f745eede52e77b9ce76b8ce983fc36e5a71f8f8b10801bb3d2ff41e04c3190ee4aeba4419e41aa48090da30402b4a559d70b57abb7aa562ae4106982e7f4fa62b69585d6b9986331ca8c39d9d1ef550208e16dfde67163fa420b3fdc71f9670cec7ec72e5cb9e60268df98c12c1fd73eab4e79411255d3869f20313a957d41662d3a84d8308c116df3a90d0161f146f882a104cdae18be2e73065faa26a47fc4fbcb152893a0376871ac89cdaa01e91f87e9635fbbb32667fdf13104aec0f0b94fd3d166f7ce3f6be136f7806c0f80a958da5367e4ebc8127d00092c00cf083086a96d96c5b5cf36dafa8b7432b0a12c25851e40606edd2d0848f4000c60e2e3c4d75310b02091fb6503a6bcab3f594a504b1fbc41bdd986d1f639ff2230863892b6ea0e104501c314495afc3d16996945cfeb7ae053b3ac4b6fd6c9f7c90b22dce6073dce7a80d04051412dca0786284941767c4e6fec781b8afc1bd02e6e68e0ccddd75dba78722db5a2156d7ca0c3be9e8e704166d267c2a98855114f1ba9802b338018371fac29410ab12c6065cdc02a103a8942df6553d0b11d90800000023150000201008054462a160349846caae7614000c7f9e487c54968ac324c861148490310410420801000000034668688a036d8f710c200871e6e8a3c2fd99197feb07902cce524cafefd92f2272bcd65401e29fd44a851cbb690287d35105a34ced90c269de87d3e0be909d91bb5fa76f679e3117fbba73089e70420eb9c6e748c104ff861eb8ca2b4f51f3746c329ef600837f76a8b554fa6763110b7f4471eb75b4a8843c73b2372d6fdd865942867896a2d7345abc89abcce20d52a29b6128bcd81db8c86442338ec234cbcb83b6365398fedf854fbe56c2e36f1919cbee65ed49651a84b2d2021950d80245c84933fb059f494e33b854c2b17733857119ffdc11fbc0a82805171c16ef79e67f9e508bcab21dab9bd39fdd34e89117ceffcda36a55d60a51af47ead2570ff91d6970548457d049f538d49fe984f55a7448217630bbba23ee863dafa605f916929aa8c9556831477fd2d67fdf87a78a645e24d71f83fecb1ac1c4fba479b8a441b2ac63d25686ba86877030a0368225201cc865e5de05230a1a5068326f85ae1db86da83a1b84dd5ac66478224ce7c46b31354232fc493dd4a20283fbc2611d6b827ceac5992aaec1f014a6f3c4fac1a279c44aa76864b811808fe53ba7669060cb5a591fdaa5d1593f25a4afaa50e077acc90d1386c54e71e81d4c865a2e3e2766196e6a177c2e6d2aaf5f70402d7349561510db41f2151cf10e2a10b00f33e25c212184dd76d121bf4b8884e12590f16df0aefe8a6304f006d9693b9f6a73f5e80ae38a013717e144eefb77d3705dc29d984dc27a6cec0ee48796056bfceaedec36043ca5fb045d17d472061b98c4feb21138944c98a089c2cbababfa11c4827a16999e6a3b28b1b1342e0f87680392c503abef768825b451e818765757622b81ab5e217f49e4da8db92182f14121fd66bca5ac8726f3ef89b8b271097bb930e24d9eef729921c195378342661f36e92d849eecd130e7c7bc327653beaeae6f6845db752730f65a72a78fc49768ce0112f6225a100578128f489e79d1885b9f7bb00c1b0dd565cc633936e97440197f426c243ffb76c482e9826d733c0e3646f34fc6c67222cd93bfca368aac23def846c5728ec3b129774bec3e2e4d0b649c49368dda9644eb1b9b48bab135c1de477c18ee7221620335ab8a53350ca6867735764d98a5c238056f043fb133de1817074fa4834fa1297161212ad602aa122bd622b67531cce05f4db869de9249d103eff160a216b67be296351a73c72c2a1db5fd7291fd89b5a4b3db780b28a22dd354c8dd48c5db792cd964413b400e6d521bedb08fa8aaa006b988ed2eeb0ce3d13c48f88270cdf57794d4a4a5b0d35288332432c4e6de9dd37e9323f52637ea01d43b4b4eb213b224211288d915d5192c82341d15eb23c94591665a3b6007d41f901e3af918fd4be2e548e833b5c30ab5d456b8b90d58a1b11e01e170cbf870024d1e774975734acc1670c4f50496653161b7488c5c799244005f420b4e0585e7466a0e51aa980f59e468eb3f42eb36006b50aa93726482f5569b95312cd5489087d340e3b00d339839f8f88fdad053e7ea13d8c9031b388dc5256f34874541a37b04672d2944331b4eb190e0f665ffe24aba897b774ab1170dd37eb49c12b83ebee61887d905bca2fee42a7b5dd869d9725bc5f8cb96d3af5258c4e73e9759806f80bfeb254a4e61cac8551876fba88335ab9cac5c2ff0bf3d0910cb132763f313fe432b2d06c76b958cc9398e9a4694c911cb32c6168567e2874c415115d534da8e3d836bf9cfd92f7521fbe09297a3dea4465612352a76cbabc51d4e78925eda8a98a86291416b539ad401209a7864744a5a3013003c65e2ff738b6528d7b8c404ff0637ab0bb45f3b8a9e4cc9f0593981256370007c416025a8ca3f3a329754d015ea4ddc946922488b0ad28efa94a8f453a8f6657241b86640952350a862d412d01c35369833306f0889643f44a46a468659483a5d84ef7f2aa297eb72a8aa46c5f7b63215e395ed2948e3d05cd5e3f2a5ef6bc28208d2186b29fe9fd4dc77494f40476dfa33314401025e02da5111fe31baf7155acc08219680ddb1a54409f1b2510cc6ee9d202e3322841bf706824106fd43652eb3dcb47c204d3ec8b905d7b1b2f2031b13439eb7f887acf40d21a70fb149fa59c93cdf295a897643186806b32be00f7cf31ce2bc369c9d4f35356bb187f51c289e4b6474b5e2062c6114f8d95e69919c89b352f09514d282bc828404c2ca8e753cc7b705dc7b2471c693b62e9420a6b981402c1aba62eaf8b115d1f53d468bfafb6d517d1d9d6a2501e571d509a3eff881e11de5b7463775a0002d7093b55b24ca71acdc3be4b8f474a6fa64bd24b98a5aaf99027aec3e0ef6c4b7833c81ab30b7edea79ecc071c2ea37038870a228971b9311b9ea2ee7910f26f5bcf29655e4b85e89031b5f81381fb279dcaf80242a0bee71db5506125d5285062f242725ef1e2c30c78abdd8e7ee8b2d374a91e0243d59c7acd54f46d2be44c0b7b27cdf1ac684158ab40c58e6df6a2a3ead8469e3163be4178ecfbb53a6a43b7a4c895246d9109f0e0e58dbb7e596217ab5804742e86c292728964f7de04dacde186db7f09d2522751f7e021596fb0d4f8da8e5df75449f170178618fb34b60a24c43586d6217183e275d9325f509aa248e94c9cdfd6c1c2bd2cd3ed692ff0da7f923146fdb2b39ed7a835aa9d174a672adec0d8936cd7222935e0cf19db4465f0c99efc30418fe5b15f8eaee5f9021154af5e8e330ee0c2983fb1c0d811c7a2bf6f744ba0d54b9108a13630dac02bab1af867a87a5d53402da985c12b9dc7c25b8eb96024a864b3988bd2f87b917d19737f8f2a2ef98a492d7085fcb34a90e9e09657061d901522b85e4c391ed0c8f083c7774841bf96ef6b9fbd4aa248e42c3b4cc206d5d55040124989029dfa364c8432141044e462f9f74329e06031bc263f792c4d88203bb0841ddbe2c6815fe09d01c2ddc6de133771c4a536bb97abc1efe29c7b970dbeb30e7e6d6a9d2a88b8419afa7c9d33be8092e32fbe897cb11a2308360a465724d24b35cb3e7cc167193fd0383b9144214092eb5964e8ab220d5c4385d6a15620befc840c3f1b6035aba29d32d11e33a14a7dff61bb931429c3931f29a994b444999a4e25a0114ae58f5e406f947acf1e494796846c1c816dda64d6729874de08856120e43a2db6120af832ead6bc970323e358fa5358b5cd50e8f194f9e7af149fa7be620ed466a74607c66621292f0adfe88aa038abad0086538bc9038ac66c41fd1fa31be8901988b219a5bbe18615a36fd6940097221161d7a7175c87f99b85e48026aa096c8454404da8972d8bacc0ca388c5d64e5ebfc9d16b448c68be0e1d2ba9ba2e3080a23f41623edfe3af551659af70b9524380efdce306a16f46e3c409195f195e23ed018fada946f30f6e871461c0b732d16f356898f072022ead6b864ec4b721944d809c6ad3728e469e0f375b4ebe0d1ac2e1f8b82f02de4f023fb37ca2466ebb0a0c5c7da2603bb893e566dfd6dfdc1d79dc6d2f4e07e197703dc5501e476e68ce57bf2a323faf2f08a6f86933d638ade6fd8b8b7e6e78b81f78169ed0ce8a3578a6676e518006674c169481a8b30be9c541519ae1972961e2bd8b729810dcafab18b580cccde253f6bf5386fc86c44e95071b47f632f19fda19f2e805858b8973bb426539dabc414195394169b693549c9c2074ac903f0245e0da0ccbe553bf07821208bd1f823940bb08e175d42a68ab2e4faa8588291332694c31cd210ee6e3a96ddd2c4d5ab7384009e6ad5a6c93ac577593037626fa88e7cf92672d52d324c5d6c96088c37a6f5fb5bf00b9543cbcced90e4f84904da2de0d46f60ed4415fcd54b8fdcd0435a8d16d5d4010808fafaf9cccbb657014bdfd651e5adc7c5cd75d514c1ef36529c6844ae9b31ccf9a7c4b6efb3670e11f2b53307f0923963de024dd8fb8180deead008069c27b10d299a84d9ed1be6d545e560681688a0bfa8a030a1995f838ad76724519024bb1c4a5f9af344f037cc6f48859c9c2f44d5925e65f0d48aadede6812dd5f3b77e6f90d6a38c5fd0cf474da55c7b768a6b0673454eb32731f17f4f8140ca2846a640c6d2855876d4fcf9b4df27197b167e251837b7e175aeb37d945980bcbcfbb3ab07b350ee533fa234d0c4bb22ba140fd9458b1fb63029c5ea1f7a37416250e553d6b7eac8f53d4c79154134c91088c5c19ea7276df25e519326bcbcff9c10e93a2a7ae5d11b3d7a2bfed0ab03cbd130a10391f87b1e8562519ef4f5a46be5e5f2ec1af93ecd8ec786104230bf4db371927ab3b0323c7cc853adbdd7bdc025f08887230ad5144f855e8afa121cf0a919b6ed61a1ca4d66ee6ad03a9374ae0b46a0d7932b700191a815394efcebea5e5ab7b783845ed7a72fd88c9d95d35b9fecb0080d36a5f92c8102a6182c0380aa1d61f3d956818174caf9f764c8d22b1e5039bf27844b9d4ae4d8a808e08a984a384dd3c99ab9a21b4bb9ca29dbd2fb3aefaf0ed3e4117221cb8d9a42c72da1640e6a6667c0195d92b54d14b802c961e4f31e7b3138245b85f9872d36624cc29f254d617616ffb5dc348850184f162305622343c7b1061ca69db8baaba312471aa376df5d78327dc650704987df93577c0fe832e32dd2a6cd5b60208433173a7d41bfc98947a727c0c726d2d70278b9899ca3be51271d4a9eea132d2b7be91693bf8d17347ca2a34ce481958b9c06589e7329b3192160634a420ae91a14d62b01c01622994da47ac829b4159762d5a45c456d9f5984fe08070a2136323d20831b3f76c492176975158148989f1064b25d2e6c660a7f8d6fc1c8d2cf8986483a2963671cf3b628752e265acbd42669eecbb6217dc5acc344920aeae4ebcaf63252835efca55c6054d883699aab499087d42c14f46c96f0f94825e0519e47aa38d7816db244346fd4cff28e9ddf5fcdd81cb525e7457deb5f4de4af15bf410d113111ac6b87b493ea43948c2422d4417143680a5f56073cec1b177d12aef3381a84079785f35c8b02da768ef2673233a18baf719a00f6b6049e897a377604f01b510a3d668df8e7ee6952464396a451223ca0429eaf7efa3d25e267ca04e83dcf350563cac94046e59a8e3679fab9f16b02813ae0d4e98d5f65743389611621f6051afdec4f697f6fae3367c7fdce313fa19100c6d596c33937d06ad2a875b2a60d3bcc90433ee06e44162ac5ac41c02f6f96e2ca1e12958893104793125b10c0d891577ff0c18c6c4a21573dc159cb788c67d8862f73604cb5d50e75421a14a4147d62f6e2f0f92bf7051aa4721a58e208e31c49037979472d1557514b7483534a21550498bcc95d88d3f813d5127d82d786263e77939904e550d76f72e8b74aab3607942d9eed3e7317b1e98e5ab5cc178e1f1d01757f49bc3c20cb1ebc546d66004f303a981333ab505889b7320a029cc63fc3a337750a876c65bac26e82976778493a943deec4bc73c1ebec2c284e57a6ac0cddfa6c3c5ea55ac1c7dfcce9b7a5780e535be8331b323dd7286d8ee090ba4c6c9cde7d2996a38a0698cfe9813f3cab6610d4f974c964e41d85233a6c8b8c05b673fe67eb04fc31f19fb36f503d51f3679933c137fa0d0290590c18b32620155b1023c388808b9de48df91d1dd87780b6c70426da691d7834550a2016158e3256354f58a082b0afa78671a0423808744059d32990555fa91b6b6cb80be5d9a7b599c764450ac90a4608ab145f80308809adf3dbec8c2d6146286d9c7dcbaf1b07da8e11384f603eba4859326d156ff6ece92da8c6e406bd3456bfb315eeb386146708298679951248c8caa31dc0370559407c32e899673b52922a7dce31d52d7d35f0291768461d326722c9074c9b64618b4f26f1c57f17bc0a5607e0294850ce26dad78e7820a866f2973e5852f79bf3cc5d1449a39beba67408b5a42c50c91d054e8ac9df40b01e2d20e212a97d4259f0a88808cd0defa879ef11c3e4ec28e9acdf3667fdb86658a08cd11da9e81610f86ec02125e81c733a6b3491dbc0eae111b6050e594da4dc28310bc0875902ec4e1b692ad73e984bbe83a601910115b6cb3a4ac1dcd080d3951b941d55914e9293b33aa02730b2c232694da072e341105a699575cf523ab14044927cc8c714c0e0c26da6352e627c76670cb35b876cdb04c24ab7584d07cf6041830cd0578f380df3d9e0e0586d2fa8a1c029703d6429317e6cf0dd08ebba8cf243c9646fb3b6969c97a233816686b72088732ab2044209b1550a309dce7722289f3bf8aef8a248de09c7fbacc20e7fb775ec535b86bc58565426e5df5ecefd09d29564aa41a5f707f28be0d1531fa17c41e87dbe76a74b2ce9627eb190a5a8a62f6bc2c3aedb778378f18b5593477a45942b6da568eb2615c47f417cd0a954b3bcd884f680e96754dd20e0b0577dbdf039682875c0c6ebe801c5821a776197d26f9f5337b4a9c217155afe6708ac80d55f9c155915b68ab8730983025824f3d712fb8d787780d6b4f7219c21b77e2e5e32579e63026066d29e68d3baf8e919e54e1800edbcd8e104662f69437f217d768c58e7d09e35a09c23748251392646e1b56183cedf2b3a44ed73d74c160e6f473f2f002757b0d4a79527641cf89fac7b4202e9c9b4ef1f1e0cfc59b3ce9d87cb3fddb6c8be1ee52c5b2166d063fa64b8913abe39e4e27288cece83d183dd9fc8a1c6150dc0045f2eef76a206b60c154580e39f4c5ae5bed93c282a60795905a2aab3c9fad55e4960a30db96ab82d4d91c3f93a36168d8b5c703c2abfc37c69a455de463d2c31596cdbd768bab16d060810665c9df288da236319d763910b5cbc549f4a88e04ba39041d55be33d1490bec296e9bd74f3d27f90f8c8a7e595693367a8ab7cd19bafa03a9e0b1b1072df9014ff2d91874516160e8035439da747e4e3a1de180509e3660d077dba87dfc185a2a0d8d4b01d84ffc279123ee7fd619cd553baeb6348341f391c6431515367cbfba8a4f9abfe1d345a283880dd26cc29bfb4e76e2fa68c3eca4518f1d4594a1e6d2edd7c8245ca90735dfac80dbacf99f58d063a23f884913f47516a157106a122cc9de5d63460ecf0f6565380e9610da6117378590ac4ae726d276a69beaf2fde81b8b0c2326492769a6fa57af0a62ab5105bdbe3a765b8dfdde93f5f89c762a933463b412497cb0c4659c673050e12bbea44ddc5e50432b690c37801dba76c973f096484723726ea2c1d3e1730f2ce7a06631a4b0f7083a5261888fc691d3843f29f631781f6d4e475490e8111fa08a3c28963a52ced91484441c16a5a5c1e1abd0a7254b621fc7c7c847e975947de3318d52667add960d7c1a659e347eddcf6d36a3c27edc22a853d734311198959d471b20d6ac00148c48828c90ea6cd7142b83a520e522fb7f72c89722581cc1de63af0b058ff6dcb86ee09f0237fffd486cdbd1e4ee818f93c1f8c99b44632beb11f998a9d16e79930cba43d691729bee0379dbaa8058d4dbcf45eb7500e267dd3e88bc696bbfb1e2feb6c60a022a1d7826ed24ea77de0038578e82809238578e88b5a93720dca22235a9134919e0ea949a0f3db03283b7b0bd468f07b143f56f1e0073ea7fdba5d85088ba210db497ab80fd7c68dbe409c4c1853467d0acbb5cc21a442d49cfd634d1118a251884becd7c27f63b002b979b1939cd9ca0937248ca431020190e2fe7b3d10090ec1a88b3117c7a1b0ea28bb3a391fd641c980ca5d0fe5c4c90ad02a7915a9758d28d42538e3fccbe117adc740e3acad8caeba6a4a6d3414a39850c749b4d2703bc6b244b5d830c4ac647e4b6ad74d7a785acc28ae0d5d85e1e90d589e64755ba6b9c6ede53ab4a89b84b358cb82a794bc2f8faed871e0a7685e059ecf1618de400afa4526b43ed5de41d767c6b827cca2792d3112f5b5481cf86527919ff69b064935feede3e023882b2361c2548e8a9c66ba34e0975bbdf2a5285366762239fc725335b4cbc2412d1018e336c0a484834e7598d1eb147cd7c46257178b34012aa80f1a9c306c6d63d4ab613357f47090fd42844e596a824340b3680489457bf366085cae375733d30b61e4c64f4ec6d833d1050e120badd0259df9f1154de9865955b733738c8996aaa1cbe091966e68cbf13be8d9ca71ce17606a9738bffebd651c38ba6b23410425d1bb2b8eb91db957a96409c33729636345d9281c1b320651f8427e4a8a97282cdc9d57a2d5ad34e1e579622e9e48504aced3c1bb9c804e5d513cceebf69c67b6e6efb63c9ab0bde412b46a6e884a53f98cc2d817215302c7f5e804bbb9d7434f551f287c419635faec5497de2bb7fd319265ea31cf8bcac5f4b9fc52d5c4c60dbac2cc244045cad802b64d0310881ae4b811c7d70e70a167a31275e12185fb901f18dd254b79863a352ed85dc4a1dada9c7143b189710bf845c32f5685f872cc34921775abfb74f17a32f85fa7ad99a78e0d9f502dc5b479043d531d84060c454ee33697f3fb87785225091e6460b48997a89bde974e202e57caf7a6e4d84b555de206508655deaecb02956bc52a50b9dbd102b73a2818ce8d5b24fe28e77c10969539505d5c5a9571139882a2d2b9697496914335120c26fc917c3ccbdb1d4fa08a19f9755be20e44409fd7fa6a699198aae6314c2be68c8d17bfcd156043347c335804541a288e0ed573177f9fe336755b2394de9bd9a6046ad8fffc825de8b6507774001fa222dc46ca404ee0f9ee05b032bd900183d587158c4aa81c80c5be47099911e36dd6e1ff21e6cb36c258895fd616744426461f9c441e03bc8920a1df2034eff5de2a8f6b554c61a437f4cdb552a04d6f62b2672f23dfec82c71e57c5c2ac5d60812d353b92df92b41877bc289423a6485b3cfdad755210248ab7324ecffae6f0a30feb9b26d0a4f1749dbe10464de05c20fe8e58861585010b50c6ef69d84438265a80005aa960ea610d425c433f4fe86dafa281860e27b5921346eef35596eed0a3372324e98a3620e684096b49b32cfddddd03423b14ec50847c12480153070b4923fedebe2e9e74e29af29fab189b643ddd67b3af9915286c13e69bd5c8da852425db90ef02af3eff628d3513b8b90203de9c28b7e4a2f191b4bfafc65efa0db26fe78dc60ed5a95403f28f231fb05006b75ceac5f1cdde6d47c73058480bb8dd6ea5d0527854994cb35d99842b634249b01e9360aad971fb54123f267a2cb78c90bc9394f516543d0fe79d5bcb447dee6d13ceee72470dff088ec66e67981b40b50bd90f499452480d9be1220c53d80eae6d5b1c362f4bb8e3efcfecedb5fd621671e7c32dadc987abc3a51eeeff0b86c8097eedc06210d4177a959c6aabaf0ade1332c7abc33e267d1f9cdf0af150d87347b4804e15fee3bde26bb4760f58f5efd6733b47b7e21b781886eda52f2d20914c871bc9aed6842218a47ecaad0932cdc6f4a71a44166db01f873b5db97ec511448ae13d7a18bea9c14ba4a004400d9b9f4bcd59110ed1b8ce6c0ecd195dab4ec9fa98da84adc0c37dcd0ed9cf2f82625d90449a78d34d09bc7346f579e981ba7d21c29aa748f7815a5aa8c7ff6a16fd90ea4169237690982119dffdc63bc501fa4095650e8f6590e6c34fdded79eabf797beaa983ad183515369235cae4312ca01e3920db2a510c17c298fee689afb94f7f54fd79f05a8a46f8ff3364549f1a21af4224c9bad5faee050c4fbcccb16b4c832243f7a493e8a71c5c26e910bfe08dd322572a85ea26b5efa2d630fcabc8b54bbe817a72fc6981e0ba285f55524ba2113cb19a009b68905cfbcd6757fb75f569f14f297cb6ead4c7402c301262282a644081a97b35e0305febc42eb70e132c9c7e2c6e9fe1db2865f796e8c4fab8062c16a3e8ac69a2749359f68c465a1a42dd3de2de2af20e473c0d85f6df729b69cda455f6784f4eeda12e1f8008b436405e5877b2d2b2cc2f71e1822049cd117660f86032783d892b040bc52111dee68b8fd05950adb710b8dacf4b0f3a732622a22d18eb841f0df15a70365c4242142dd0670cf257db043cd878cfb6898d3a49bc27fe859db052d47840f8e4f406bd7ecc599a93533928910dbcd45f871475cf839fc643b470fc8b1c89805e226b100ac2fa986710e1720266d7148d2a55bdc0e70f8a2f10c19320f05a9ece04d13631599749bd954b9813ab11960385a9e55e04090c57b4a4a0e4318aed26a947aeee94e360a72ed4eecee06e7b04b24722d5c4808e647bc6e8e1dbeb3d5c5b7a4d842c4b7c2e0ec7ebe99137fb0c238238fd34aee8b21c45a280055da9204c670a5df10901c8fb228b38ec71427dabc1e54c8b2601ca0d9e4fb6198b08385011c97e4f74d6f9811b17237cf0d2db135a188c99ad81736fbcfffccfefe6771f2a60e98ee426292b92543abb636b1a2a8244106f2ad7721fcd19ca66171c6ed70322bf162a705ce8eb7a95a2d0494881ce5907cc328df0a155cfc453c0057f405c10240ed393e898e8e507e2e443f663fb4508a9679e70984d6dc598c4f1d603b1e5e8605d70a05849059e6dd6caeedcd5dbb051008a81fac4513f74891331e5bb94150bd0dc55a87e6d5f33f7740e051604109564b8b49c4b9c518b726aa45f460c9aa33efc2335ac8573edeb3ddceab85a4a604684ea15248d4ac273359b83f45032c7ebfc7d0d568557d261ac2ff01a42f0f3467583837a6011a62634323b59b107957daf7f68fe3fe947fa2ce34e591dd3491890c1ee75aebb9ba3d90c1674814bf8a5af169e4bf4e00f1e732a78c9b0cbe05bd01b8a704d76777bee7c267219ecc7b5affe90b02bc7e09dac7f4d1975c9a8e7f6e66351112cd80bf16e44ba13011d2a0f5d011badb03789366574cc8f0e60d29ad93cc40f58360eaaf619adb6df42c4d4e0e6639b429647c9f0c4672a5ddcf7c7204dbb1b0d568e08ca0dee4b5acf3ad746a0a13f6f1600d64216c34e69a920616891de81e515d884b80f49f9f44aca97859e79b42d3535a1f2007e83cb70d247be91eb3aab89ca9b357b0ee84f90c9fe3717480185bfb7da6c27256c1fef6607b6346257df31eb705155d27bd341647421e26667dc98482ecaa88ec4c50c84451a0e4952df50c7c540a09bb490ed13b0fd6686bc1e92833c1806c67007f1fba87f10c49a20604eb996cf479a8543bb003111baf87b8c4316620f360d492dc26f11c4101cc274653dc155cb045f9f9bb90f17b9bdc54aa84ede78f9837e4816ba654c213634e5c55b8e5a0538baa4a51584f98b4b14186a70563ae4965a812a6d290fe2d6b02b933d012e6e06d53a259777a86a5c8d40c496c8b6851db7a7be7505d74193d4e86cfde7b5b4b147634eacbd96f0ecf8373e702119357b6dbe10e43a51887a52d75007cf1ac203c162f802f77465d6ff9257339231577b7e4388390b6a2bcd42aa2a7b27d1462df3dffd00f92cb372ee8592cde9fc4db6ab044308a4709dd38d85bf319b090fb53011ddea6dd4d703322f8014f7dfd7c9e42de6a98490dc36919be84b09625ff99593a81607a21ff99d80ac5de1086828d5e10a45d500f5de27d91c041e03dd348c7adeb04aa11efa968eebae1012c79cea5ea6de996c4f8b2038dd579f4afc71873c4fd9dc4302854a26e5557b5e2cbc0047f9249f269fdfae83388af2e5909f79b1b2df13b2f104f998009dd206140519f8267f284c80fb535634f289f81a81d49e7c2713afdbbc5ecee769c2e26919a5142cee8669879c467d6fee0089960d84af82489f0878742b61a9ecf9de3201609bf453f482a6171d10d7516f0e4c93aa75d629435b71a352f47e9f08e2d6c29838e9e23ffcae87cd3b70886581210558242de111134bb7584a2ab3f9d168f18af167059eb2a2af2a461f300c7518b33c56159d0d978a51a1a096389fda3df65dfaf59488c862d2be5762e1a34bbd652ef43a7355b50fa7e47f5b306c6c6816d98fe1b0f0c6f2e12f981850268419ed92ae5ef29308c7377ebb7d1c9ccc2cac28edd3cebed585c3fdffaad14f085b43e78ec5b1274e910dd72e11720a054e4c117d8c4954d120703aab5f42ec2c8fa5ae455e170f61d2e59e288d21721822ba9a6d0684f015e36a60cdf80ec50d28b6dde4d572dcc6263cdf015e40d50374952f8f8adcb41e8760b9de7213825123db2978483f9d22c1106894c9e850c4759a8dd4d592cf8e3d2b964b1ae1a2caf9571701cc2eac65e1130bea9d9dabec0c4498f4300c721491b3aaf95f3d54cd2a2342750c8beb5c35c289d96e61ecdf84d482237a4c233eb4c9bdc5005b92afd41fe9d2bbf6fafbf3b38cce7250f592fbc52c90adfedd5fc4a2ad5d734c3858a0a06d553a0de4f8586b8655321dd980652a9bfb8f1586fa61d3332d3948e1b5738ec7a2a73542aeb36efd8ce806025c2a29fc21db88138ab40109526f1514ff1ab0d35d9f323265f4bb09d59769acaa6288da21b3f188e0c62f4c7307ab466a6daa1518cf375a9b037de6743700142c23d9f1f11a01a78ed735104ce10a45f78ded08cee6d4e0cfe7d5d8af32e258b43c82de6ec22cb43aaf3ec33ba4b19752d4f8e5c04245623e415e17b55149b63f5db00889d8a451e158ba3e867290d6816f33ea5f10d54bbfdce0a6cef939db5c694646f85b0752aa6428a2d05d2ed8395e3d3d476692809a7b044f0d4fc417fa741c5f837c609934722c0bdf518599a7509ca75b4ab4539c6cafe484a9e9610bcaee2d9c17fb872aa0c23d26b1fce5c043a0d174a5c90c33e3e50fcbc34438f15fe7c3b95b637922d2cd4448680ba54a3407d3827765301f7a00b8b15280a3a4fe5327a6579c31b81c737b192c0d0eed19486bbdf532d4adc63d10e6e79683d98583213522c0c08c1528b88f51dc9529eaa5fc2e1959a28385a5d6a48d25e891d8b033f378695c0eb15523f02267cdd728b3fd08bb42fa3d8f07df9cb5aefceca2cdd1a106ec25185b75891e00761a78160dddbe03e88d7597fe0613a6e3486c341343f789d68f7f2e372aa7688e9bae12136183f6f7a92bdeb67e881bcc38582ae26fac4f8a9329fd751e93331d5fec6c7ac725098341daaf34b37480f8e9cec1f59c9095d9fd1354461578aaccf2843249973c4c9ddc9bafb33ce3e0c4d098bd079ea8861a7f15a019aebe7c62b0d4e242679592eafc949b84f04d7e16b89b3da40c83d607c218d2c374e74023a7f44bea6810e14126926710e5929b84f8cac7fd6b59fe2c936915d7f633cab9d400fe0cb3d2a1b57a5b34e332395453172e9e05708d5e5b5bcc39c1321df7f071236380fde011dd26270500aa519b2731c398a384696c81d06a3022a6293b5f7587d5ef3112ecd748bb950237f4937be3b3eb206aff085485e5dba38446925ef4ba835a196b0cc0126f1aabd4b4c07823a12ce845452d334582f6bfcb6a1ba5ba612b9175990eef9606d6f47ae4661e97868a89b4c845f1bf950d4f98fbb51b1ce3305a5f893dffb315c4cf95900a0c2567a19913708405c0042e8dff254dfa600fd76c4a242dbdfd08cc06eee27fc3a2f10048996755b18b67ff419c1fdc04abe3c3341c06922e83da7997058857f7e79cc50b16018f309056fb7118caf53fa5061bffd8bfb35e9dc2ee93e73f235c1ed48742350619c3e99fe321c4f96731288234f801a076e7ad65a7944519f70dd3d4314b5a5c41c1113889da04f23db4c87915e0124e066ff9ece35104f7217cd2ae131abd9d335ee583b9abd66b0b56719e0bfc242c98ce6cb8ec78bbea43f6a8667b20ec84037fc787dfadc9c0d986f8d9c4d4339d0ed4bf86fe7e543297569c08a71e928705bee168d2412a016dad6c2b5345a51df60f9288080b07b844ffd9cd41998a9c7c7ee05d5e44d0807880acab4b39cfaa44157fac4a0e6ed29938a01fc586b3cb3a32de53117aaf55b650441a8b4bc2eb179fcc87e1f50105c177cacd33b9751a96315a4869ea779257f4cc9a4865b53ae60249db0242ac9d83e408940675e22347357426d14a74b68d1a18c788e30f35c0706a322bda97f2069b571e51f28e3c3c59588d3dd24a8de4c11af3a939e9a0e60248eb9b323fd064f08e88cec5647d2b14dbf99473c914cf2952982388d60b3dec2124b78d2119210d326fae7e2e4988ee40ea7f138925e3e3ae884849fa290f00b62965ae31db3c2bf13d05daf792a7f51302da1e2719613bee873331678720367c6ef152b7b34243a406848074f16ec6a3eb40a7588a6c8e1f4de399b899eb807402fcda1fff38b1580eed0f5fc0b47da9d526e8a4031939f4801d10a3ae242a67e66c24116065849233e116d74c9d8033354908aae13a73cf137c6ec0b40fe464836345c17628dd7f586bdb45a4d6c2c86e67286234e548e46628595d2e87ea12ede97cf2c652e54d99da85b20c150309211c38717f31e3517a0ed4f294d9055bd3459e755e2bd1bbc1cc462fe1198a287a27165ba3c6c10a59050f91b319454f80e72f39cde84183979a6102333f9c403b698f9d3fc01c2449bc8a46d3a469210553b227898ae1036e5ce814b4cc73c8a0d6b412a79d769b31f7a1fc869570f75c4212e5dbb6be6b4dee99c5b4b656376f925d079fcb70605cb936096d0beb93639e115ee4810b271ca59cd55edd4737e761eaa7ed90a1118621542011abbd9e36ff4a9964eee76ce62b6b17f7b1dff4eec31a7bbcb40c497ec175a81dce25e7008932facbd713030ce1f75030ddf17db37a56af37c0ff28ca8c1503f526384ee4a2e04fb1081efa6fa34d1586de26c6d6f43a99b08e0737523f9d47a0bbe20070cb23cf04eff3294f8c88df955c18ea2cc91b263a9fc7026c568a586b4c1ae78e6482ee154d1d10c95170db63f19dae95cf08871d959dc56f2837cfa66a9c8b1d61f012480822d831165960c4ff9b9681c566472bc60f5fa520199de14d2bdedb624cca0a9c8d24ea21c26832b9353a48729affc205c5fe039b9b1ef52f305038bd97bd4b13cd7b3a247be40148927f1ed4c194408965c0927b7a2966d17a4a178459b16c01ebfc4fd4ae043cf7754bba6a131c080c9a2151799a1870da1b79da09a2583f4b3147255ee66c6e23ff9454b20ea6726b97f6614c139a376a49cb8fee32ed2441c0b4154da16ad885eeb4e2631b5da6c40d44ad475baeac7f2ace1e7901f2c465957f8fd929f2df134c6ec3b0b7e953e5d0ca45a1a2a43480033b8811c3bee2f2b29f62ba5f36f4c293299c9bedf24fde4b11b4ea9e83001976ddf4b760a07f4097bebe0a562f666018eecc31bf67823dce1c02d7ac28096283a0d6342a8f4a237a3e154bc52d98c381082ebe1fc17aa6169ebe3af05d0aa35b2cfb4efbc6a88c006471847dd72bc4c3057498b1c79f5da2db6386586ea2451bcd500983385aa7233440fb126f352068b06e1b50b477579e7e81743a56f2398a65494fbfe15409f4bdb829b69e255c26102675c3ddb8c3d1cf0805ce6f86251602252c366eb4d3397767b4a0a2279118b4a1e9b34336dd679666ae3ad9f98ce959b3ee4180904ee9a40c511b808025b18f71c55d43457b8340358e3e8aeaf2a28093633020d404780d89b28eb12bb999098cd4091f2cc2dca0bbc39e5bb07be190c7bb2889264dba7bd013601dca77ed76debcb6039a9be329c2257d709fa004095c8666ba67aafc0e63f8e0f0efea3718ce2a1c7255f20f8e4bbb7459f480e4b1c9e7e910f99625da242c64180a8a3c153519882b1b836ba19244e1170eb94c5b3145507bbef996412b10a206c648b716e1fe9c085d9f7e660f2e21b1d1fb58ecdf8721cc9bb318067f9468a91efaeed88462584c340ccfbd9608ff46fe2ce7f6b39030bb6ab2f1ed2dd3fb61794d48131a17e09b664753e3f60bfe441597d0125773e75ebcde6f526b98c0e0ec3d59a406d51da3d1222ddb3940f674c98bece6fc8d38531f23f876e598b65307b66eb3fc1d23f21eaf406b6467dedc07dfd10ff36d0849b4262dbb95a172c39251b2c00ba2d109e0e603eb0addaa1a21dcbec670d05917ebb2ac95195d35d0008fffbbc0047fdf8cec334e9df575d3ce6cda5ead503da56e489d90255e0e872d8b0db0635adec800fbcef58138c39c353144012007337fc63f38dfe4e82723ed0b09287ea2ba6bedbe05ddd4257429aa3304fd764c3b47282fe00bdcadf3f37b730193c343da06c80b9015536be084d3e341ce2b225e037c9a0bea2ddb76ef97d57cec9d7f2601802571fcf5b4143aa35183cab80c6b7ce62fa25c557e61009e72ce9fa5898c6578572fbbeb6591cdf5de9bbbd48ce89f1d0389d5ae22d465b6759d17258453577aeaafb9c6750bc155a29f144b6cb68d014c39ec1e4099d201726271e95ef2e811eb047bb971c1688fa4e8d6eae1350c50dd60c3e296c1a2287e4aa14e3de4c5de840284a9638806f3d0206871856b14f8bd48747bf1ebb7cd5283b09c94208b37a71c4cd38144230744ff9d15054e5484f795c05c7d25774bcc89fee821d095070db7592ddf18a84aded3b59169696daf60c997a2a8ad2847cf72bbdbd0232f73882f8ccabe44aa102d2fc61efda093fd21e7b57180796032d3c35e0d684fa1b1e0bf83fe84eab8d3f865ca56b54128c8328f77cc936bcb65cef8ae279fa62085d34877578e0c5837b374dd106fbfa35f955c40bb5a5f87ac93d3c1ede77b8ed85368e9c14ada827a898b2b900ccb1c7876884e3d3f5186c5f31ddb8d2e9393e277034bff8a0f91dccdb00047d80aa1feacccf1e1749e5940c5c99f85eb4ac1c0256abbfb4264d07a77bb1f7f3d7ec6a6883b937fdee1f027cbaeb4220d814e06b66373768c580f988796132ff93b7e9f2f318133d286e78773f058d0a8d5b46fab4ce94720adc581fde2927fe0ce443577c50a7aedf8fe75e21d98eeed93567833bf8c878b5b088131128a7456b593c21c7f0b8c80dd0012cd65dbbcf6d3f132e097081f1cb04c9385e1444cdd836e2f019702fc856a3ed1ad0188af8637f830320670f0b331342fead9091f1edc33c5128bd259d9a8ba89b95afd984e6411f7a2ff19440657edf17c8fd071a119901de9faccd44b07bc9197b5e8d72bf587a583cbcfc096054f0f78f0f43686ff9e7e95457fded4ae03359cf0db5a98844e7a5b563096f92f0c96c463209a51cde59c5ad4e3180628b7a2912e50362801433629dea3d3a9e41438301fecdb9150bed31b03751d5861f604f33697378c396867988f925953a2994b93cad955f42f8f07048a81f58d47f3ed931cdccd80ae8ad54d063ea0a0c5d3d19edfc72d777276cb652d90b307f2d1021edf9212542e54b1dd7c3d2efd90f81831d8801b68850b3471536298481492ed68d2940a140f8a2b4796cf91d151eef277e2eb0302c64212eba6fa5c0b38a1570f309a2f79e4372d09dff4ca193339f9686c635a7cac4929764d1fbd1a1d09b29416b72a7d3bd0e82567842730562cdc7e6ea759d79eed85b0ef9591761af20ca076700e276985e25fb12ec662f0db86c6e801504857202d6b50facb149a6f445449ce14f62480ae455eafac6bd218873b10daaf77fedf387a71751418bd7a9df80501829ebf41b4edbac835a7a602b1cb334798d0bc7f30eff00acd3502fbf153a91bd66e37a372ed3e58ba073d2a06dd1fb539cffa98ba07cfeee901e1ec28e121069922a5152793c5cf9fc180804a0e726a8a308167989cc9654b3805762c194a693acf66c3c0e34e7dbad11850f1bb6fe1d408364c4cb10f4f3760b686adabb460bf486844bd2e67eb69a10eeed5599f1bfe41e11a59110451a1e914d6dd4e0832ccada086a36bf620eecec50993f63be23304cf04a0cbe725a35afd39abc971d049b65e0c4fb18f285d382256260eccfe056b839b8fdd70b2237f7b060b32932aecfa2600452425b2009bcff687be3814dfee877b012bafcd10b851a9e44fc1fb135408e9b1f9f3a910bd0c1dd88c9496b45fad4dfdd97bc51e86265d863cbc328b8ed8c18e511264f894d4c668d178b5d86811e6bb726677296265f5e494fa9a15fe058628c06f87eaf3bf88e0c2036eac63e0c91ef34f50b0595bbf4072f7224f83664695867ae1bb678015bab4035e2e109b949ecc6d66a341839dc7a25d8fc84bd3c739b90a1be9e3ff519571d624952e46bafff3ef676debc25ecaf377055694cacbfe9acfa7703c946da7a0383ea2eea88cef330c855f7dd8a107b31e45619a4a9c5b15cdadfdd01a5891c289b70372b6837a44517e56066bfaa205928739260e66cd3fa8e6925b1b1fbce097d3e0aa33eab0b7c5ea4c07e422449081da0cf1771b30515bc63cdbe82635e3025713ffc0f56d6d1900748c3244887d7c68d9b94da5cea781599621673046ff43f8ecbb14e53ea300921b16e5b03deec222193f52b3b325dd9ad631c6e933cb147d83cbb355ced395242260f66e87059cb03568922dd0c4d4b20ddd90f3594e823af705800bc4b4ca4b46ed4544e9f3ec5dd01fcf74e28426042237b53c373fb7ab2ba08d7784b2ee8dd6548c3a4f1234ae6b8e3d630b52e61ab4f8b031c349f124caea74ad91ba16e1ce341bda6abde7b1ddddcbad2762666762cca329da72292876967ee170366736cda2a40e9e3c2b3cb523d29a7c4bdab1b8e130d5b493ec3d7dea6a920dbbeb0e51a5c55a6605b7125c8d0ce45db87ed89582803e338efff9c748e30ba310384fcfe1af1f7268c5fc5f7d3cc280d6c903cfeed9d66110a02af41a2661128815f02498b1d48b0d8a9eea79a54ddea152b552b4e566889b5003f6069e273e49ae9437ae5d59ec71ec0cb865a135693202bc8c646a88256a0cf712a8b1d3bd57345b148cd4e53171af122dcda5d0af1529621a0edb20baad2accbde7b45ed1f02433195802c0af09bd866e6fa8fdf9e2ad2690dac35c5dda13f24f37e2b91a3b890bb53814b40e32a963771978f5264235dc7f63fa82be5645ec084dfd211f57c5693ddd32ef309ce0526e2d226a56f498962c4b7bbc054e8170d8f8855dcff4ac4245d64811a2e580dd662575bb33a2d72973736a41bf55830ad71148335787eec433525e068093e8cfb2bafc5b9691c3ce2397dec4b1402682a8103b420be13475ad71ba9f8a9aed8d94f7f7f5061dcf987eb2d3c592253378fc14d0a3aa0d10ca2cbc59fc1345ede6b5e8b9aec74194974712a33a46c853cdb52d61c6343c784ac5e6f77be5263de5ee219e9bc670346edf8677756635c386ab22913393c9fc2ee7916b130897a55e2b9d322a1185212556c16b92e45ca748e0eb0b0422e27503822524a7c9d23db635a49ddca2b07864f2ab00c0cc0c97e212e2d35db65102fb97fbfbf135a6cd391078f7fb81c85cf2b7f7c9e36b0d39161ce7f4b5ec9a63392126d2f3b1085a10500c2cc860c8b24ab5c14b996467aa3c1a45cb8f48e3d9935ec51df6973fbbeae3ff0bd2bd1f2d42386d183c2ae4d966796a6773e4da07b73b34881c627ecac67a7bf3ed229ed927582558d1c75fbe91588ab00b2c160de0a456df45981f73dc98e95be68899d8d13c7acf6017f02802fcb034e168560d1404c5ff9eec40adcd935180b9c33ffce7eadbf6e571d741dfd3c0e92403a109ff6ec9b7f70535c6df2c1e60537fb3fafe6c99ca6c8b0acd6c2b6756b410630d3b060b3a58d0937a9b042b6daace8bc39a0f87d122c38209d96553ca6c745d74c6ea889b7dfc20508033b81c9aa083a0c93874038905878fbd84463427651a32acda86d16cd58f92e2bdd0d97a46866ebd28cf428accd2463049144cff229dbf3368a6867c3c2b90efd2dd5199fa10f547bbbe1e50e0951c1bfd086d5947976d04c2fe4977fe644319cfea2112622176327f4e129714d5dbe33cf8bcb95237a7e510e1668fb7f5c51023ae3db2b8ba49ddf9f0eca1efec402cda3c2d054fd682187693578281022468b3a6a604adfcb66b6a718344fd0d5a1ae03173449ab78a1cc829980e0f6dc5d91f8a07d1ea32c31b1ad785205e776638523f788c314347c11957bbbd80b374ef397e5d3a31a105daf920493b9bb3beb701ac738bf906b4fd03ccdbe4b09f923e5551f7241f43381cb5a27602af570624c14db3774dbf9747fd800249c192e67e9e63aac9cac9e48057fdf155a577fc35bf9134317099b64b85a8a5744ba9b440872ef8e6d7abf35acbd04861c2d59b53496632119b0007a89312cfb97cff6d4f57b2fe35793677261abe12b459edeb996f74c30650a0792119ef9700adf973c00b3537aaafe3c9b448707c100b30a24d3f012d414f36cc291b6af2ff94cdb0f76cb6ea6b129f466b702ccad0fd6d8b99835ed8e6cb3b481426c2bb11e57e117fe590091c4229924c34ef030c1144dacab32ef4fa807afda534eafe958be8892b6e843f0deeba39d2f711851a4aa693feea33fc437744459e105712a2160bc5eb054c35c35b424fe6c8760c4bc8f925ff452b7913b4e416749ee48386d36e3e6e9af29d9feafe3ccf9cf42194b2d24774e9da182a59e14268bc9eb81cc2dc0cfc232d5362afd0da1862d12b0264a62c0a9ccce61648967759e0359ca7f0ddc99e25682de13876c551f23cf4336f5d88716008f9526af3f14a99e1514872ca14569d290c9d9f36f655040820b1255fa0e97b3f80e17417959aac383f8ff197638a47624a058f704e1439c13fdab94e900836fa3911a13328a2347507b0cb4ff63c686a122e23b8ba0ee8cc21966aa22db1b92d58c913aa876ca8a9dd60853a4dcfb760ccc9e50e710b4339eb01bdd9344f2a30cefa95317b017965bf447100b703a07451085657c8df483679788121d675c2820ae1070248b46284036925edb7a06132ce2a943e111b585123be604d8ecc0b1777af93a540bc5f9e1e7cdc5799c65d7423fc518a862fdc698c5be32bac20fbb1d0f2dc41d011d034319fe02c2154e064cbd204fc0860cfed60aae55a8ba9230780f31f8a9631c9acf94ee20730242f7b695fca20b002086a4e04a300166947cb6d7812f754e5a83d5c76faff07a0072b7500c7b125e2e2ef15f6bf2498f4b7812d2e83149ea24a50185e09ffe5078f9da944a60a391365c0ffa96e6055388fdcda03eba851ac03e5d736651ade4aa54529d0612cbadc5c4351d28a5b7dcdd890d0dcaeb5ad2d8b048e1cc8a4466c149b577aaed0a357792bb1c26419fc750c681e2b5024b1392ddbbc84b1c501fa5c622ca68a4ecbd62740e5f96c65387846bee10b14382e177af167c12bbd6756a15e64826bcd3df531406022af9e179ac496343dbedd3cb7532cd23c6f3f429a5ed1d0400f42eafddcd34a6776372416486b5009cba5ff45a70c3104aa7eb6a54d66ac272bb83c786e4e3364e1e786f57083013d89721fa7e3a192e1b8633c40cdde621615498ff3c5f1793b704a27bc6f36580e6a2a5c1799ecf3b2fb1c6a516ac4a12c45c7ef87f19bfc750cfffea3b64c34e9d7fa75c8b7219b70d92c6cab6c13311850d18eefceae8df35d00642f3a7f79a8de06605c93fc23a9ac45a419e4308a89382675c9e79aa7833d9f52f895b26b0309938a7af24520e061c984d3276a136b5d094532d36d452537325942e7e7ff4c6ceaafa25ffebdcfe1b5b0cd7e1fa084ab3d18713cc8295edb83c64abe890725728f5459c01f9d46595013c41fa752db2c6aa6653bdcd0552384ccf118ab95d9d232c06df5ef249132161791026c1500853755faf0fa6caaf099d90526e7859863de08dcca5a5139b1ee568ae9d9e7f0093971d883122b9900b382b612094d05ab9746b332212b5aa40d02003f66694b1a77f52af97420a43f2df22bb29a4111a4912bac8fc8bbc4251de925f96561df87e38875e6b9fe69c1053c7c3019993f1ee0ee11504cc0f19810b0a43ab4fc2b76e71bd1fac498cec8a12509e856d6011e9881304cb23cbae4f84b5aab03de24141a7fea7da0389e61e37497a878addc42b5c8dfff061727bf557c580472b3351ace57e162b1d731f1125c5ffef82e93bf7bf6b7b4de5491f39cf1de74aae13d6e1f1fe30235a49080046aef913925db858efa3012f9244bac90106393713e26cb871b5f27171c1dea719074323faa6a5d900409e28500b0f057a3b576d045196118a634446f23802d29acb8dba93922543e846f905a3e5b1470d30c9d95b808c913a6ae0178d0cf0d8481e2cc478250ca4b44e333e3d6c0dfd7bb8cdbf49be5833198708041afa67e2bab4ab2779fe9c34c4b5f5f3be759a5eb9d9ba8d50671bb81231317858daf82ded9663245d1dfcb4164ce8d738f02ac146cc7d008c1fdb7f54c0f4803a0dbe5f8c3e605f2c547b89fe3339864ed1b2da0bd991ced317c40376c5f2a354e4627881315db417eb20a85c08d1bd5c4c6955273084307a62b9e42af0f55262e54c5981a6e110df34e8e1fa0b9904628adecf83e4fbb2866f491c85f8106aa1030aa96146296543ec21e02945003fba134e46f6e25f393dd0a6612aa102cc58965e9550e48c389c1c04a326470cebedb427e442f8b848df82a8c5d55eed0d1446c6e5d6e0ccdfa2d219019ad3d400fde350d690cf04d083844adefe5069c7066b0ea24854ef3f5853328d7bb2ea735b370eccec5f0f777a467c98f040c423fc4c3df237ee8b1e9cee6c859c860c0297667809ffc0b73af9815b01384834fcb56d3e782ce11ac9124345a3d34fc22ff7e0c3149d66f7bc8feb9743bb54405ea88902011cfd4ed07ee19c6b8e6de6dcb08e2e86427e5e675dff052652af14d7af936f032bbbe508c54fd4dfbd16921e7b1c1471e96f3a521b911e32a5888b5a4083be97a3ab843a1dacc19e60f161fd84c10fe94c9952829da6979a83ff25faea14df6f6d5c876f3103a8a93deb51a3834c9da185355a8b189585f7c71ca082295ba29e326e60b9d08ec5386deca533a03b903541380cdf616135e348c6b09a5cd4a074d7c2e3d794e44a0c4ab8537f3f53b12ad2c2a996c5ec0a1b6d9a25d920a782bbf2120262a71f816a66ffdfedf5e5dbb868d37517607e880d697720a0119077b19df4e9e8709a177bd25508b00c6c79d4b1c1ca7f607aa7b18e6f93e40c8ac9d8705bd9deec9626cd4aa06491416430f1e204177b62d1bd18e9fefa0da4d6e032f785851dcfdd3e2a2058b1811aaa976c30a067d9accbe4504abb7c2a39492889fe518dc529cfb481320dc05a785cdac3fd225936716b149620ce252d079b0f14a947bd7efa17868935b6483516f7653fe108e5c1da91db5a58a3aaf920ecfacdaec9cb997d80860e966fbc4c2cdc52f41fd3dcf8bfd70fc4a28f99128b1b5e0d412c91fd59a9deea1d9b5170e92e1ba12064847d72f9ed9a58874ce906252c6679851b735cdd5f3f17e35e0ec6117e83b9718e85bcfb608428bf03df02e2950a07e80844403f21b8b0e6ce349c5ca2c260aaa780949050d2d9fb90f890e0d33b4a8263b0f3ff72d06ad34072f3567c85a6e301d5b374d9a78a94e5a3523735e3f9c2db0734aa7c7c5787879687b485f1fce35966c742b896aee8f0361d98ff221b58f0746a0bb4ff1443b1f21eb26e1503187f52903a842a278d3cccffe3328876cada9acb5f109f1679265c39ff58a2f5f63162117f63556389d00b7ddb9cfe0346c62344ac0c9f130294d0178425920cf8c5623e09bd387d8f1d1bc4a7df18f8f38f20680ce2734505fccf2d7e13a0fa68ae5f7f949346bce6896c992cff605197740913534fc38ab50d679c92bdd04e6682ffb8581c3de476b13bed9e72cc7807e6ae6a0f0fca5cd9a03086c1a5971991a7e0a074c128727a4fb8e63240df8d4e87ba2077b50ca0c1de5ca6b4a09fd2d16022aad1342b0dfcdf706d13d433db3f592bb467f911fdd09969d1fcb4fbe4ac156a4227dfba8999642bbb7239cc6bc02d4e1cc5ffa47642171b9807bc2e62027befda4cf5d0b994e36b110580f40ae0b8505bac989738219bc3deefb4fb85037482687f8f196ceed78ef556907987d952b042016046c57e36e47e9d5e4c71d5fb7bd2327809e4663d75a402c9b6d89857d2d07375273e3142dbfbd1c4c658fc64e2436ad5d8a714d9a626b8c6a26c3ca5cd8b7bc75162e5ea63a1644e4365ed6b16107b8462e7186586b0b2c43f526f0656c544b70f5e59e809462b1e00853cae8bb57e39c55abe0e2435c53bf68c0dd39e3bcf6da3250f4ad1d47f1e9813b22b109c9f2ca6b06b362f8a0c11774cd90321561ea5633ae94828698a5b87a338a0a1ee58aa9e3759a68ebce835ab5bffab1cb564a704c220e8f451d02ca511d3a61071e59d7a638c6eba218fd186a9abe7144e21dc862d71cc14fbe19898710ec7321917cb26df10994c2d4980eae0c9c512cfaa4068fed52d0c57c9dc3fe75b51f333210839a37ff90e8508bbe215d7fd50cc4c90daa00e10194fb660fb38b9a85be80afd5232edba10ab7c98dd8e5ca4eaa6fb7828b529711682b92e5c980bc877a83b9d7f0b848181110e370bee7ad434463f90c954705032d56c0b86305b83f61642daec190941c8b687f46d70c0c0cb4d627c0af930844189d3df40c1ac2c5e0f19e598bea0d6405cbb41e35fd850f26bec7aae8873c883ba0fe69c268839681796ad11c090e891f6cedb944fb4118092d8b4397dce87fb4755b1ac74217d12597496c41370c4653df24c3df308c2c9605efcaa3e8fb55250eef97ba85bac046d6aa82d249fdd348bae7b1f6e658b8072a2aae3dc22920e419e656810cf8f80398bd4dbabc4df89b4b46328524ca43edc03677156dc05a215d2c504db8b28e876a1797ae0b57de85f634151a8bde6002948ae1f540fe4d5f53186fd979992d4bd95c2fffdaa0d02596e17b439ab392ae0f50e6bec0af1ca9668890801d5dd1c9fa69c06ef9fb04b9b0260e8279bf93f1fb0de27d60b0c0d07329f986aa78e6660a8d0785903e7bd71fa74fa1ad87a952a22ca0c215d035817ef038624b5445a326d84ca96a7486058add15801596906b1d78381b9092f504db0008628d32fbca6471359d445a248c0019ff3b8f6157b7b45c14c76ef673a927ac5e425c0a2cb71cabda5b2f323aa037543ffe1c84c1833ac61c016bbcda50b219bca8b2d210914e2e6aaf5483c943fd4e7c68228b26bf751cb466667a66529cc4c19812a8306a25776f5ceb24a601baca327cbaf470a61955474f3d9ae0aa8fedef83fd62d1d1e843a835c3b993a00f09faeadf015cb28d01b01fe5ca49864b667ba0cf0ceea8439d39cb2a9ce71f7b3a5a9b2774141bfd68baacae775d48ba40cfdeb1d1018f77ec090a44ca5f4672f8a1b6110421edbf5014dac6469e980d3e207f43e25353c1a7d4d010974e145a7cb8a5e05d7d79c7f4eeaacd410dab582adff9b9ef8ffadda3682651dbd8ee69d60464070908bec020c622cc00cee15aed981698ca7d413b83b78fc1a2e6c85bc11787374342b243093d26301476c94e671d7008c7f076dfc7d5ea5d765cc4e82b0fc85ffc8c6637909142f4f3fc943170002ee6e4ffb8a3807970aad74a2aa0faa7a3bdfe4c925757497aee9bd3c1ed174e3aa9ef62d6c8df963d0272545e3a19aca824d7ebb9e22d1818f67e98d5dc3e1b3c45fbbaf8a5e053db3fa77af51d975a22ab70321bfc40af2e3828542f4236adb4755552c9437ce84d39332588d33eae8fe5119eccca99d74962a3c9f09ee67eea15f24e58fdeb5536db348f335c67ddcf19dfe848cb1bf5f2be87f5641032816376f9dd3a983822463898a9ac4fa1c88294747715251561bed2933c08be3c006f05a7b3fce71c179e9fc1bb1de53392ba96d0080179c058c441b9e370714526fe5a01dd1ceb43fdbd75efa6324d799955a4cf9441f50c25e456c6436f95fb183851782552a4e3ba51d2597225b8f2b84dbd01ab4bebb18a49712ea84e86297c4be4e394a3d88646bc8da23d09373a2534fdec05540253bb9435ce0f9c1ee444de9557f880d3bc81ae16dee47085dec4022057b0d3cba15569f542939f58609cdacadf9d5055a9b123be4fdde7985a8a4eee0d37032769527587fa1e09cc7122c8323d1c7ca23a98dd63e970031d8545288d6224c9cc034e60731a9212a89fc6704e67f4ff3638366168abe7c5300fa1dc6b95f56e079cc9b97595750031d8d346244a5619031e924c5eaa8ab82200032737621f4e5684fa19c40de1ac18cc6d416ca42dc59e52b07e1a69f3dca3ce63ba55a4c1a3cc761148f83808c62b840b9252ec3fbb2060b9e7c75f6d94891722056d24771bdc8806d3c5124153b2e07ccbb16b02987c0e0ad70a29b48ddba6a3201ed15f4c055ce2b9e3feb5a7ff082215587c9d891c9c02c2eae1c5efd8625c6535ec57be13465f5d4b5acff1945f0a7a11b0e41546b1e8f3a3ae85fff4ec1c89c2dd44398d04e7689464ce2c631929b4652c9e7645684a0857d6c2cbbe2363496c88a5d05050fc76d488ba7f953959cccb5ce8e062c203df001a542cd5e3ec4fe97eb15619a9149022c9ad71b9de36a3a5d257a0d3ccca684666a703ded560df69a53ba546dbade63093831c306d04bf0ab91ef00f96e22edc0e9745ee374693ea38e7c9a66f766d395be678036af3520d65e817f05c7a9e4b5ee15decfdd2c40e6a75789c112a2cd02da7e7aaa27b57b50626a8f989630b47550d39efbdf17d08da5e066039e2be80c51a0221853d0d840fea4e5d0a818de468b455d1a99b98b69ba89c118ff0bdcb250fbfaef760e3eb3098b92512e0cbf2cd32052281727ce17a1d55824fe953029190dab087eb179c5981c1e785a2400bcacfb5fd63025983d3841b2123e21b529bcae8f9162068db42e1280053dae8de583b3b713f2f78738df67188b7a01f40f0918fa1e51ddf85757ce251e903bb996d87ae4c845a420e6333e0a3c292521190d1318d4840446166d1241f4a837b47179ea554e144991857037225cbcb44e782a9c08d58f166da0e7dddd3b05ff2f7cb5ca53e445a3e6c06a1e3e03c7912619a8418606b4b5a6d8df274ce2aad313d5ac4305024352e9219046a0ec8ff68f6d7ce075490b9bd41fc9940f8a73fadf6577a4742f08d0524f72500d944e950ec1a7b45d2bdd46df4bedae251dbfd53c97a4d3c746de80214c46675522ca77fcbb13500597ad629d5625f9c2ea4c38d1334225b00ec20b9127dc4a15d65f129f4f95668768b51b02341df16c8430c163878ea13ed24b683b993732cc35b987c78e2bd25f1fc6ee4e152bd489baebe997a247076d380c092acecb79c91213a29ab370776cd2c3b3871204cd156e2ce3ff091fd31b65cfb824f14f400f6d9bea7894b8e2744e60b68cc0c05084c024e1cf64867a132f7429c9411fd0c9e225f4828bd9f523bdf3f5a8c21b09dbb8f4770bde3a488928ebdf0ff3d2c300bf6f4eccc94c5b1e4551ef25d144cdf78fbab3fbf37f01160ecaf91b153e6a9322575c082c4eefcfcb41f606eacbc87e9ad7022a0916ca782ba8a2bf4f408d135a1605c85ba1e211a78747ffbd1ba7c36589bb7cd801166efcf55a8e2e9e24bc6cbcfbf1ede191a777fb4a6a67a39bb92498b8d06b287882c859d668a7757716572334aa19f4624fbd7dfb3cd42a34f6f12da60c850a21a2e9998bf4256b961200e57621dff71ff89860ab8f1388b889e6836fd22f120d9f42e08f4999454c0523038e056404d989a3dbee88cb59a57efd7c5bd3827bc85a0bc8c02953ed0d7dcaa108c115a48ebe00c19ac6a0b7daac1b1468d6d8252e93f061bc381523fbea0f248d4de1945c704fd01510d54aceebfe791ff1a44852613dbeddecf2e7fdff1e981cfad3c615f65ec413546c787497f2dadcb2022a6a2aa463bdef07ce1f303b06c8413357261f8d662a71d344737504ba69b8be1ea8252e6838ca8936dcd7041dfd6a646d1cc0b12436f3c89a7feaf8f57fcd34f860017798b1cfb22395d2f2836dd02988ac37178f82e5c543a98b8982e59a0729168c866526ce53173da0b9300e6c17ce85a74032f052a82142b290911a0edb93bade04baa6e061678107143823fbfdbf3c06c0dd0de671ab6567cc3ee3a657e7ecf6d10acfe4ecb89f9698307623643468e0ecb86d9d9b18db9ebd3272d674b43d42784367f2454b3759ca6bfdb30a9001d8b0ec48ed1378d40a33524c3a81de9f331833c8a3e9cee970f651dbfce88b26ed8f6a4240fa2907529e11a44d4b35483310d25e3c006f2d7d51432acc216d43a4de48db275262f925ac1669a7e6e9317fac48f5a8c60620767062e78bc4d93c3fbd5dd47a332512d2fda7447f76a7badc77f862f290ee4b89804280f591a6badc290d3e128c46b681e69d71c3ed2a3b6cc4c530e0479309d6287b692e08529de31d830126afb4a4652a8aeb3303d3547d00b93b661edc1b226d6d79a7761fd1e43264a090a0d8e9cca3454ff74b8257714e299ed3c6f5b6648b9c26bc78ff09121b9a485282e4c5c2b240129ac0819c8532b0b9cca4472c180836f7285d09386581375779cb546d4c8ac6043b3255ab1035c1d6e07647f453130bbe2aaf04e5b7bf49d0cd6474c25a75c67901a23870ae77c32e61af3ffea207f8749100649c3cc054c36691d4127bfc7ad18e1f018ed75f0609001dd83e41d6e2ac66964c248eccb2802c378c9252146c8674ac23b81fc13b0089add0152a1141a6472370df94ae57e54ab1c3a26c55f24a1dd0ccd360e794f0a63987a08d50ffb84a76d3689ac1211ea5d8917dedd6abf81476dd0ac1c7017aee6abc9ee1a41e6f5edd0b89b9d99983857dc32914abc1e20c29c2c2882c21923c947c980e43393500716ba97ec56ce33fd4d01240988636baaf5aca649f87fe606606fecb888b6a7c62dc99688774d648982ce1cbaaa3282467050963b31d95c29f55d427d4166b142f767a695ee5be116bfc9455f405c12a1c3e7dbb14564f94c73dc0a7af0aba2b89c172b2fe5f9c059c0a51fa792481ba1fb6f97faed51fa1e1857eafa818290875f34d876e2a17e5adcc7bebc7b01b941b9934c2e4942e9064d836f976444ebff2dd6c52f44fd9132f4a4a0ab659f052f5b80e6696d217568dea33b7a90302d011384961f7809b0238ed7e26a28492676e4368ee5548a3f5be6b7c3f4342ade03db48e82bb247dc95b52638fc38802881d0c2691f2801d0be3a66700ca06e22070d3987ff4d44db8f952832f0a74e43f77b7874bb68fa67435fffb3a5d98b034ae7766999e8c9563366a70d86110deff01dc7cfae0391be8ebad2fe2d816a06daa82ad16587e43c91dcad74b672541420d12c50f82f737de005ad7235436f404e87ae5b3ac732a92a34bfa3c20f267fd4c7ab07947232044ed716b52a0eee6bbd8388d7e4d15c904a4ac17bc8a7c57032a9e8e8a5c30ceba4a078e335cf0db5a938e3233d9e16a503d741755da53cb2b848c86c25199e1245000f93043ed0b8ca80200a4d1cd64118869789d5e90f2b393c38faf9492f91051c29563e737b8d7c5c704dd199696ecbdf796524a299394015b080f081b083dddfbbb0e92e54cac171d082fc24500c43d3192e533573280171d8acfb0e33fc3cc2ba48f17e79700bc38bdd079718ab17b78f8e26c5abd38b570f0e2740200fef30889f313080d369831481685caef5a062fcea0a9e47b518a91645422baa2362f4ad98e17e5171e456408b2077fcd8b52a900dbc50e7436cd8b9f6366bba86dff1cc9aa4168760028e60680a22e0104c59bad251901c5cc0050d40b004700c51b8d480450cc0a00455dc4a5116dfa2180e2cdce50208062fe0014b50730a84d3f01a078b3f3880050cc4440516f10146f68363a0028660380a22e402b1b6dfa4240f106cf7c4031f780a226c06bb6e9077961261628661e50d41d30316dfa406694857f80621e00286a01f88cfe8ecf70cb0728e60080a2d631c2ad4dbf87d175ba30baa20f0050cc1c80a25e81a2ca69d30fc7bc012866ad0128ea1c5054c1367d1c50bcb92ebaa26f3300c51b15088a2afd81a2a6ff44f90614b3e6018a7a0798b46d40f16657998cb26c0d28dea87480a24ae70045adaa59e300e9cd162d28de6c51dbe055ba1615499d99a40227a67136b3e19bd31a5112b09766d4d42475269426457369ad198cc2742a8f5dc49c9868100d923ad9680924d86ba63edf5c37cb3564a1ecca2d2525a9c314862ce6e4464652c777b7e1177e022761240c0b1386c32d5c05136121ec72fa7c6f4e6540c96257264c8a3109690ae5ebba3f5ca32b044605a9336363665032398620757cdfcf375791ac601a93449c2d933a54db5c446c2fbccc10238707fbf9ae9f6f4d4657f445215949636df91d8d4d8fa40eb67c20b2c73f67b1ba7b752315f46e7bce0eda5fb6a9267b2d49007b326dad71b66912d4a63ff67c516362e747142005d8b283098a40f6fc1f76fec654c294404ca94bd3d6db9f7ef69c2a5a7a983d147776a84a2582ccf6f7917252ea5cabe420875486568656665b9e200fb1b0b267ce13a6e7d3393fb9bb3d6d0e91aef9405461026da74892ec49bd6c31f7b03939e80955ab5beab9d30f7e53eae1299cc4113ba16def4bf92738cb47bf50d65aa5fa705321fa725aedc559dbb8ce4bc9e899ba6bd2e66cd0dcc081812a878e1a9b1d3c6efec3ff9bcba0cbc02b92b327160f2d0e4e475f3467ad73cc3407e3ae38a741e7f3714c74e57405e6a17d24586ca645cec5b99c27c8593e6f61b287522ec63ddd7baf067f2f0777f5f7de7bc3bf37c8fdbff7de7bf3f7b22ef8f7de7bbfbf97e7dafcbdf7de9abfb783cbe3efbdf7eef87b815cd5df7befc5e0effd7175fcbdf7de1c7fef002ecddf7befb5f1f70ae0e2f87befbd37fede9d5be3efbdf76e7faf8ffbf3cabd7706e0de7befd5b95368e6affe7befbd3deebdf75eeeeffd1000f7de8b736feaefbdf73e8de7c1cd206783202c9e0e80fc188000767c0440a7070034c0d10087831b2b1dc20d34c8c1c9c0c6df70cfdd703540efdbb8edb79b8d9bb1e96ab66db3d96ab64df3e0766cdb66b3d56c9b8c6ac360db3ea523874c25c340e6d1641be3138d8d0e870e372e8d8d17a7c376d452b9361db455c964797399ccc7a7f18e3e6ee08ca6b5f6cbe59a1dd49f6f0ee394cdeedd14f7b6e7eddf896526f9d898eca1201c398bd26a85d62b58b687f3de572ad2a702ad5eac55b6f7b548f684ef7d6d39ab6697e76d6086f4a9473c2f5234b677f35e7dbd9e5e605e514ecec3a30ae953953a78915ad1247b3e2467d5714c1ac791e98af37c11d2a73e0179911a01933d35ef3d1d725695c968d191f754c87974bcf7148af4b1403f5ea452b6b703ca59d6356b79ef609cc7e6bd772fa48f3d7ad1c1d81e064bceb2af57d3cbcbeb69741e1c4e481fab248017fd0925d993e3e52c3b8eb011c953bdf7ae83f4b14f3b2f3a0fdbb3f19e9579900f156d2fbff7538cf4b940d3879d63c2c89e1b31675d974be68272cdb6477385f4b94701f0debbafd7547a2dbd9ab647dffb7984f4b94ad387d5797126b1bd1aeffd2472d61dc76934bebc991e2fce29dbbb4fb23000785186b1bdcf80f4c140f3bddf80f36cefbd0ab207bb64ae984882b3b0cbfb22d2071f4d1ff6a5922702d9de6b04e7d1defb10644f7def43e749bd062f6eb0bd773c7ae08b747b9f237db0128eec71161ee9cabee7fd943ef869fab0ef79af81e78197b4ab8bb2b8eaca711d4d1fce0acf1165715cd5ae97d2f4e15e1d286dbbc964a36c94d5a7e9c30101a94fdb7e9551d6368e553656d9586540d3677bfa01e4a2acedf572bd5c2fd7d1f4d9940670f4a2accdf57abd94a6cf762400a56d35996c948db2a7e9b301edd8a76ddfca284b1b472b1bad6cb432a0e9a33df900725196f67ab95eae97eb68fa684a01387a5196e67abd5e4ad3473bd2511a292bcb64a36c948ddbfe7d9a3e1a508ffbb4ed5f1965e571bcb2f1cac62b039a3ef90900402ecacaaf97ebe57ab98ea64f56e2e0e84559d9e57abdb2ebb5ed63a5e9938fa6064a948573d65886c76d1fe7e39e9ea64f069a3e641508c8b7889ff653d9531995ad40fb3494d98083715634c801713200410ec659d9f6736e523c38ee75f3dc6bdbc7f9bc97714037321e321907b4ed6750d3e9189f6a461de3f8b4ed833b389beda554b3bd746c2fa56dffc360c3b1b9b6230c36178ecde56daeed68dbff1c9a4ab6016120c321f3641bd0b67f6323e7f1c9c6e88d797cdaf679dcc034da4ba96ba954faa5b4edefb8976aae23cda5b934d7d1b66f53c3ce649906d4b564b22cd380b6fd1a239dc7a73c8ee3530d9ed68b872fa5acc197d2b6cf693a2a9d4cbeb3ebc8b52d78b4eda738a11c3e95641968fa6c2328aa545a63d9ce40db2ae98f7b926de3f4e1be481ff66d4bc5c5b4903a761bb77d95f7816f1930a78aae9b6cbb93f3881369c79c47ecfcf3ef590478fb53698e7329ecaa8baeecbb5c1f3817fbb8274ee6a22c0e8cafec87d996737151dbbab898d4b13ba78a1637d9b6bfc99c07cc06c5c168ce5a4b0ec90d50b995742e666dd6aae7643e2ac3e2dc323f559a0c78019c1054903deeeeeebec3f3c0531f0a1e62145cd91c6c990505aecde974f373ceba757fcf885377b73216dbf5f3a79d6ea79b1e50fb1944e1c62b32c116e6b6e005a48cba165fb88eb65402e8aa3fc5acd0b10279c57568e31ab4f15331d2474a1fdbe3a761644ff7acc9346663c7130a8cea098bb429f519ff6cd2a09851e0e1d2fbe0c88f15ea7419e5219612fcd1cd8fee681af7daa6713bb6d7669ba79b1fcd5aab7e0cf7dfdc600acae7e119a9ef3b3c112a131393b7349d4c934903b9209d134dcc06e3c73b4c745535d734cdb70f3450771ee6fc6108c4cbc60fa429c706cd01e92efe1adffcaa5d8105bd2b2075f05340fac05545592af80abf93e8318fd9e0094e210cbc22149c4473c83d83f7834c1fb9e3e1bcdbf4a9efb258de728c93e1f08a5cb07a78856cb3baedfa2777cd5da76d910034dcf950f594898eae83c81eb977360d695efc1b3830f091db878ebec2ab1b9a46e5ab95d672784634217494d19ebe9812e9c8b4f1774f71784552600b73cb8062eabbd75ee519d1de035b981ef3108b1e63fad1ea400decb4ea7d0b75e74722b3f3dfe764073e7d8e7e3a609a82f0f45e58464270b8f3a1ca5b9476ef19a1413ce4412226991327041584ae38cd30464a2e0b8235d1592daae17970f347e319c9e01c0245ff4964348de88bf7690b48c82b72535c900e7ff8af52693de60ca1ef4feeb989bf7b7102b16b7846f2fbcf18394f77f49af144c8294febf09b4393a8a3e338e6ef5e14f2c022fe4da26f0ee550c18f82d4c1eeae4e88b350a02bac04b524674d58ca1fc0182d4e214b97a40e7efad151882f31fc9f23c7cc0c550167d7b450b73f1299edf9b387ce231ae1c04ef4d745b6f7231c38893238877c0a4d21222e7bceb30905ffa43c6a26a78c02a5ed15f1dec116e607f953ef9e7f4e7d2ccced81297846dd0764fbc40eb60bf18cbc4f9c401c89418c266c638c9fc39db79c07c85d1a7822e4f7bf224773d65a2ce4be14e877812d878aa64b7404e188068f70e0053cc4cfc2dc1b98c28f17f0a3e03cdb63cca47ddcf6e579c5c6184856d24baa9f4c94557de5df650d631ce2e778ecd3af7e8d4f888c64eefc3b94e54fbec20f66e3a8240fb33106daf88336fe1f4fb4e87f57a8fee4f8bd22146c61ee0c6ab0fb68cd15c6390b37701247ac42bbfefd797fbed350d09eef5836fd1c22cd45a9e09df781ddf9a5f529377c737cd022654d222faa26bd9484ecc9811e217dbce88acf5c70e127e8338a44dff31630ffda22f3535e91f917949bc23aafc8b49bca7bc11e1e52b08e30412959142657f591b4a45de98823750059813ee2ffbb3ae82fe93fc41dcc41e8191235729ea35a93f66d39cc75643ed2f695fbfaee84ece9be7e37df03c5e9b2bd790b98bf7945e63b9483f1996c86690c6afa741a8bf5a0459fedd8b3524f531f7df21585499a7a5f39d59aaae87f28f34b1ff19887f55998dbbd7828ba93c3809c4774a75dab4bf6c8fa4960c6a830bbfe0f18b4e84eeee43c1e73e9d291a8bb23d125aa44b160f19c4daf7858fdb9499f3cac150884e915c8c34a6352a7be10fa087d16a62355094495c89eedb9dfb6bf9fc881e274d9e6b0e8ef3207e361fd9c1e747d975587c252a9087675905e79ba3f63333663dda657a897c7b111298cc2fcc859da8d5ce25abffead4857a44ea558bc68fad023a48ffa957af926d592a64a49489dfaa2e7b0eb733f5c5aa43198f388f4efd72fb2c78f50ff90c8587fa7a1a23ddf7d3ea5e01cba9da8556eae503757bd4e596d8a7a719e1e3416be4ad39f4f9f648fa493562ed5f99e9e8775856a77f6d01271a0ed3b149c3c48ee0fd164687c04c9058f50f07ead427f86eab306ab874c3879438b9656cb3af3429cc77e0c170ce11b2104e7a9b1e007b66efb41926d2b0d61841c9f5c51f76ad4ae4f6badb2d65a6b7561df2c575cd9a21ce5df0fc179ee8fa0a239abaefc6ab0fe21839e2d0fa5644a638271cf33342e4a29e5e74c8f3e2769473f107ce30fa2895cad0e4a991227845215826c0d04d96208fb88fc4e0c617bd769da431b5fe34f903df9ad9e2f93f26eec39ffbe8d1a9fd42627f3a53e51a5f70557989f0a415436dc092dd0b267befdd944f638cf7dfb53c8ce2b2205c50f6c79c54e20e731c1431baa54f2e7bb6690ec9992fbb7765224e9c3bea863b59c55f3d321849e20d878686d9289f4f22d9d394fed0312a5f3d39f4f73e6e867bf15ac7d77495fcd7befbdf7de9ad4791ff8bef7de7b6ff579e9bda9fb61ce5a7b185e9bc569698ba1d427482badb4f2f39772b6b4f33895f1192dd55a6babadb5d61c1974adb5d65aadad44b22db35811dbf6a5d7df62be9fdcd509cb96599cae6cf7a6b8ed59634f1d53f64c4daeecdb6cc73c45db97dbbf8ad98413ee0ff10bde90e9743138b9e5146999c50a2b5b66b1e26873dea9bce8a6a524d1a7a391d9bc907c2041640f1a6364687fe44abcb1e9933da93742d40ccac3a54d43038af5b52bd0ae11db63173127262c5b6b50a4443bca630a4316734aa540d199f6eb8924249895dd75a0e8aeed5406942cf6b461528c4948dbc6e1d89819942c67242b98c6a42b9bb625dad93b5b4be36850dba62f725bbe4f9e3ee8ac51482964382b1572456b8c8c8a85d64d4f0149f6bd56a98e7bd22dbd9a4445b28238e0c5da43035eac4294f0623d8204aff1fed40cc942c1c88bf50506bc589b2ce0c51ac3086ea30ac99a2e115ea42950c08b748b222f521684e0ba08c99a30105ea422f8e0456a020f5ea45912f02285512892359d10f0229d81c88b94ca7e91f630c4535e4896b70ef0a28b618017dd8c02bc485f1022e38464f9cbe745d7d2f3a2574180173d05415ef42725193209c7599208be3a68d0f225b883657b9c33d775dc10cfa2de19c43f846e1e601ad504a7f6947e62b735aa499f6e339d765ad5d1e049aba6b1d91857156caad56854a1a6654da3d99a06567b6f9d9fa837e6b4ac69793e0627c6396bda764268a9bdd652fa73ce3927a53f2db5d75a6bafb5d7da69a7b5d65ad05f033bfb48926ed68871d2d68a55e8c40e23a163bad79b820e4ed061462bd44de3e10d2d7a6aa973bf86ff2a95be5ef40929489d8b8287f77384d0a2def763f7fe910d50d6101530207d24307ddcbf7f5fcf21b13dafd8f367128b9b4ff82183963d74df9f462fd0dfee6f1c0625dd91308d9fa6e0f7526b41fc9c6704532a653b9b42e5e94634d28b1bdb8dcfc627e5782754eeb49c2ad5dc4013385054a924b881db089a90672c386b8cb2868c4e20fd46e0e40f710a66106824db390f96c9f9358953923d9e13b22725f3a91428cefaf2b8fa929d95ef7747cd987bb31b38db665af5219ded189d51564542ca39d248cee37bfe48c791ce2315e6e1b45291ea8bce26fe5918a82807b3e7ff8041d797f36cf4079a1fe22c1af071e813f5329b3c9c4b37b468fa1cfd56a8938629da602df270cea4ce3c729f85b96945aa583aea3cb282a92c903d5eeabdedbd979f98023d707b2a6e4f917eeefb5cf7fdc61487c1b2ed3737fe56f02d526a86ec997b3e3d63b26a8caee6cb6098d5c99e55863d5bf8452057f34db0e767d9136a4f2e629dedf99c5791a4cefc9c299a22c5f6fcf91628569f8a64aeb9fde63c582e6da24e1e4eca654fba14c659f3e9cc79f03f8eca662676f3e992cfa7239d383634b469be3365cf033ac58abf89e59b41334916c6de2966e447328ac8cac67fb7ed3e12996d04ff055b98437cfb8ed0df6610c8c29c51b2a788b3985a8ae9a455fe10a775d3bf20dd860ce930eec08b69e8af02de2dd01e73ccaef4936e7ff6f0d07bbafdfc193567d307c8f4d1e56c247828e5061bbf9633b246eee654fd9c01458d6303c59f3efa3529545f8e5c6dafa596bd3d069f137b7b1c05d8dbdfe8607b379201c59c02b78de6dbecdbf87e6c3ccd097b1349d81b68e3e5a64988d8b06103c4913a1b48f372d31f9a772109119ab79686e6b7ef7aa0b5294d0992880d319c2158c1eefb8979cb9ff4ab88b3e55b210090b187b0fce9729e2b7fb65c30611b412a09d9b549ea95c8a46472a8294b6b194957a9d4cb4c29864bcf820553f5c019811f28394ad1b4ebcb3ae794b5f370669a5b3fda39f6c22581edfa72f7d8f291d81739b03d10891cb37fb0f162fdfca1f31cf13ef450a652a9542a053ee8396947322b985f4ad943aca037f39904afbef74368fdf29e013d1089f6168362b69e0732bf713232325aa60192c8e55effc63da765b4d6325afb8ef33eb1f380842593c88dbff1338f648ed95ab6f748e698a5ed3da62fa33f2d23da2da3c1f7d0f33c24f6c5d4b740f70f1dc0f172d71a8fe3fb99791792903003fe2469251909b9806078a145e4c7850f8c59420561948186983144665e49122233a0129abf910291d8f7b81f66de7e343640570e22336093999f81ed0cce7c3333df05e8ca312801a9e3afe399408b322684fd99b742ea27765063066ca222a264e66b804d32112599aee42b513203bab055d1e1081b6658220a23221f904e2f314a9a9eaa4843082292aee42ba9f14a667e03945503d47425df41155dc997719926ae8a2a3bdd25655df79a4384ae4fc2f431926082ac596bf7604a1d9f2931498d8273ed39b779f9dccd6bda8df69b767301674d28bac23f299eb3e6757c8e573d063c387edea0791b357ece6821431210c20824384f9680b3fee7a7e03c746aee19d1344d48065050380370c240700a7da0e6f1353cbea6be6351b4555b66c1026873b5dbf1dd3cfdb4ffeffbfafdf0f6ef979ffbee771ff7ded77dead3fa6bd4781a9ac7f1dd507d187c8e4ff53abe1cef9f8eaff9fc6d3eb96f7e884f580db6d9f1e1afde0778dffc64a2fff8e70ed0a6061473ed6abe9b9f3b87082d4ea809f5a263d9f8455762e3175d48083fb7e39b9b47cddb7cf58cf0f89aa73cbee639ef7688c0e36b7e88efa8f95a987bc7cfa8300e14f443fcc6774dfd0bc8953f940cc5d9b2e1f1edf8b2873b3ebb6d3e0fb4afc1afe39b613cc49fe3f39687f8559f0379881f83cf5d9f077988ffc6e79fdbf89cc88b3ed79fcb7cfec2ef7d0ef32b8e743fc792e44a1e8efff9927b53f61975721eed77fdaeee5aed27b5cd1e11623577affed683ec5aa767a4babbdb26772ffee4b328143c6d99a5cb95dde5685b6b6f68d1d803c1e737a3b0bfcf3c0cc63b34e7bc71536e5cbf9a9c358c71ee7276d0339d87136a688a21595a9ab131c638632fc8e59e833b7586fb09e53cf9797c267bccf846b0a0c433aa89640322d84c318de5f01d408420a58a27050900e931e29e160eb0852440f2e0dfbc6eeff0fb9187dc73dcbddeb4b7f7b6efb64f4db1b2e4bd8cf6a7d3fbd4adb53a6ded3ded53e3ce4fff62b132b494df6508b33508abe831d163a2c75a942ed5d73b897414a2ae1e3836236da21e1803c18c93e331ee7307e278453658d5caa55efcce73ca4459381ed3590d6d882a1ca03f81220d0a6f809906f428edab0ef41507e617f3c679de7beab74d6fdb96da5e664b6daa6ddb3ec8b66ddbb66ddb96e319d9ba6ddb64b6ef3aaf03c189e319d9c0143cc45ffe21ce851dfdb66f6e0dcc86e99c4c9db75c4dbed7a14a148baf9cbe90ee47a9481ff8c8431c16491dfcda4789fcc96555360633141415a39449f6d0ad514da323dd9af653fad071fac0afbd967a24323bf5be395013b2a54880edfd8e8cf7da93f1bcd17b21cec3bde7b3bd2780f43e88ece9def33ccf7bcff3641ef48c78329ee7698fcd3c967951bfcc8d5764060cad56eb094d4113aebce5091213bef6de7b2f045c509a26fbb95694ac3c011440464b3c26d65a6b9178f295041365021261804c482208ef689a1a3f4b96905ba8c0b0d87105587111490912ca4c7ea680090a98c8518a1f7cedbdf7de8b63df7befd52266b6efbd59b4e82c29a8416ef1824836114608cacb154ecc96f0e0f262975c5faacca62934686826f304370dc99e5cb9f7de19165f49a5a526ed01332f610d1dc0884571c117504d88dc1df67dfd65df961833049530c43873d7f5a2a8cac507345cd5015c185da24a45174552b80294305151f282285230a94c38456152b493f1c516638ccf10a22b99f3cf0d32dae414443023308808dab6018c600c125f9c44b0022074706d3238e9321be221bee00756748318980f485ca1a105dc1623c238d9a10902a8254304c929c080c26310e957d1146ce402261915d43003bef6de7baf962186f6bdf706cd24b0d8333a202bc5ce40492042a889097624c3740415453a218b714eb0b04e8860ad566b0a26da320a588bc849511dfaf071feef903bd18c4411bb4e6890c517638c3186a2096a639cc31014add2345adf5087cea801430e5880d1058a9f31c65011c20816dc07ec300baaa207275c15474f3efc4802821611484cbc10b1c5cf84c2618c1fba128ee0a18624ae1841144ae6932e320419618c711090af649091a021adea7e5250c49210c8926a80a24395a11b4034f9f071fea160cce42e859f31c61896e42b393239d905c062a110205c3c18e1258b2b58680f5d8af0ba94c0a5559a46eba5314362965c308c31660213b431eea1cb82461951b41f262e6ac03180382f78428b2d404c61e50b26921859c0822c323198145d2c90c08c20ca920ce30e328da32516482d58985c18635ce5c3c7f9b7415aba5cb10d6d0da0220811784d00e921a78d68cc9596eb0b580e38074b74022a122a87555e2b4843a9e80730309528f785289632e00a59138ca5d8bb64ee501c7c1e91341fe7c3c7f9af42450bed89b5d63e31f2957c6598465225fd183ee009d34e102fb90aa127202a85dd42d3b8269a2685144d4dbeb811205e55a986a46ee9873a8009d7bdf732f1d68e945454996d8cb355420b0d8c1d722ca850c2894b8a6108638c9d6839c00917187ff2f4848a1374618124c61833f9f0713ea84b0a0aa32219c688fa82a50c5c44654c92a6699af653e42b7964e50a16a5127e9670184230f1e104569c0cc1840c430a1745d65abb84155fc92b198b56522dbd91259ac29d2ad689fe9a74efbd5a64f0b2ef3dc2044a206901c58c9f228c83982c9a946c0892df5a6bc388f94acaa08a84316bb98a4009e12064c87eb44ad3688d6fd0966d015ddc34848238e8d1c245d40ba2a2c002862e36fc28a1699aa66521e42b4964f482212519c9624ceac249ebe2cb931fe20e5cc64c7162cc099e98627471e24a54f5408c28ad20664c5c60a96734a518d074efbdef23048d9a664ad012941d6ed0678c3176f94a0a11d9225b18fd1845c8b0a488a84b1337bc649210d22a4da3f505bf20995165a8082943d8862dc61a59b640810e5f77982e8b2f166ab55a31608c31aeb131c618bbb6dc2283d0c6d8b593c060a225017df83817e3ba25cb0d395ca37ad444ca3d62ce643269c8702706891d8b2fc618638c316e32c2cac6d80a4d0ba4705ca0c829622fbc6028081fc4bc8892b840c92243030e5aa4f03065054a44f105d7d02445152f0a0d5cb23c63a4186717bcc08698983fc6788c2669204dbc61fb846b63998d31187a581b20c62593f029484c1c146f26c5698b202fb27851a362c0d7de7bef55c0102d4dd3d2c3089a3811c2e8c7059806241f2a52102e269911cc7849914c235dc96cbb9c819dcc2636bc144394b404132a4ba4ac00cb11627cc1642597a525268c317e828bf6908d1248100514315aa0d81002a3218c5a60b92c1947284a10dba161009391d5b20570400206298a20818bc98aa19264c91353d3981fbe98155130b6502e387cf838ff4d59c420858849c489549c1882660b4b2d529c5ab70a30568c2a3b425b6a88a2822f4870c49319943c29f1a24a9221588b0a0e60ca408a61280bce0b4d0372460f49d0e0c44a165e10c153367e5db497e8a00417b2041e8ceebd178acc86dc321446b68778b652f45333a8035e18b5ec4d4fb49c8a1f2daec75c98e84ae6265aa569b4963286a6699a56e42b7964e50a16a5a5052cd19414dbf7feb8039a8062b2538788f94a0e21cb4340e921662a225a4f842bf42efb4ef184114245f98bcc842f505a0b0b70d0863e7c9c7f210fdf7bef76b7eb5234dd1054a150b282a12a6ff1c518638ca950a2b531c63570d1fd6895a6d11a1301652230553af8c08f1645428c71c4c513938c222b5eb884a317141c143f0049e1d2c36c630c055f7b535130ba925bbb4b2d8021480a1aed892c964cd932cbe2880722a2789112810a2334ae931ff287f7091a829aa2e81283c18910d5034baf2d34887105891db00c398c51daa116162be2e1c3c7f9bf181812a30727c0b852051522b7a6b12f0e44eec5185b0d4fa182102c696202511469e143154fb4cc10021aa6e4f0650fedc841e34806335e512ef8c20823820033b524b19480a21ec000d3da320b8356d1524516a9a0d1e28b855aad56138c31c63936c618e32d474c1be36cc7259a949d291cb64f348d8a29464f90f8ac598c313ec3e52b299489f403ce3052bd6be609eed2e2a29950303ea93c542e4442331d232ccab862044c284144858b0e2e4e8a1aa880e2046bb55a4fae6482b2e2c485c9101eea92a4699a06f395444a1a999c620f1843e62bb9c52049617297178a6820818c66354dd3b4fb9ab7354dd3b42366be922d971091d16d00122f8ac3fdd162888240f518638c957c2597624dbe9256ab2110415aa569b4c68106dc84039525fa6b0c3a1830851394fd7b31de72451a1bdf8dc128c4c4ec96518a14a37ad444886dcb134948f91d7c826af1c594a9d56a51c118638c319eb22d9bc288da188b2562d8b62d4cd0821ae38b26a204e1189ad208639bc64499819e41b110143f685439da4952e4840f3f28e170839527a6174c70d102a858abd58a6225153324410521021fa298587c31c61863df18638cb36c1ac18f56691a4d0375103e4041b1a40b204d9f08f239676cadcdf9adb5d6da28a85c42d4ace51222ca259061a47d9a267fce5a105cd62a1a560933d8943a672e64bc76cef96d9ca7dbf91f27eb336625a0d17a2c72b6399909d56ab5a4dc2bb534d1b4ef6c37b9768d2c6440fd7815a3195e4489bd60c51437c81dc408e386fb2f7b780e60c6921b6078418b491573563486071b3235b2e4a22b99996427185ba9513c11e48442b9f77eb7afdf7b6b2d4307269e68594195344a40e46229fb76a1b20315306b5dc539bfdbb8fe9481914559ed7b1cbff2c5108d0e3f186f50687124b3c86634bc38bf7d7db981bf2ffe8ca1f8e2858d9f461546c9c66fe3aca0c5a864cc184a7b9aa1a90100000000c3160000380c0a064442599ea6499cf5f614800e6596526c50369d86b224485214c510a3000000000000208800829073b4a3001789589a955a9b67b75d52f2f90cb9b4157bcdd2c16ce3e00a990a31292226c739bbbd9835c20adcc6afa81012339a8037382fdd96c7f08b065c974661e27fbe4b6b2600cee951ed6d1c1c96205aba8556c83643ff8511e10c604d7a368661f77c56de01b4617dc87331da1eb9a09829d58134a28a237f74e4ca32aae8855cf3505a7cdce072f6b98838a5d6a375f2bcd63d2c995f60756cdabdf3d30f62ae806482f2e47fd2d0745d633cb442a43114e0ec4bcc81e0860d4f474b978ff8c71f480d3364e88b3bd2e3bab5e32faf69da18b2cec5c44bcde9d49c5e95099b68a5a701e4e6baa1272bbd14fd1e7692dd4b3bc99b8fba5a9c3c8ef5ec7a01abc045be1013800ec5369816f763a72e882d5cfc32bef807ff526d70d39601b99b926465357582a9a4acccd80c1d7c0992d1a68bfeae692239d12300edae46f6ab7ca0bb8d1ee94cc9925dc131abc92cd9dec37e20944b2766e17a123882e69f99e7e34aec1c67275835854084012c2b051d2f3360ef867c98be957e8668364aea0a8a4054494ac64b56fa0f73e6a633ffda7d13a6344c142b028d257aab535d8cb8d4842da4457587a87159ef481344ed96963c519c67e13addafd15397b633a8a08a34b8f0ee2193806ba69aa9f7974550bfd34c867623b14251abaf7165845690ee8d4aafecc1a9c3d4cd2b5efef7c82a8e9adb37d6935613b7d97c1ec4c8d7f0a486aca200fb12e3a03bdf243d0cdf6b57e274dfbed096f8dcbef9150e58292f0d7dbc28fd5854239db3400c10d241b4e16e47881bf28b7815201a36e38c706f89ee09557e7df2edca497860a1da903fe008c478d59a620c59a6ba01f05cdb7e18c0433643bd3feed737260631c9e07d006b5bf63808adbc7b45b7ef99aa0e5083a693e85183120e430db11e3e40907ea4618c467097b8074336ebf41d69d1874b8927312a54df0b10ad126090f2f62175a70ecad31d6ac2c30535e3e16a8ddc8948978012eaa875417b41318f703dd749607b67573e026a9e06665b282c4fcf8e299fcdc5c45993af3108d9f7046db141719d84240fcfdd6f09f3a1a9efe7603bb0744354e9c20019d4e9f5e7bbd596b9fc885e9e5e36f9fc8a9ab77be6c6a20d05a692df0b1253154b6dd6d245696b2afc72accb89cbd07d6ff1f4797a35be9f69ef5debc61b848a4c37341738aa729631219d35f0b839edb3b0d2e07523ea99bcfbd208cffd0ea2b9bbc9596451aa0dfebd42dc888495b4cf1db9a9ad6a0e38963ac28bc74e88861bd68a7ff8fdefaa4de013b5af3d5f2a02ba7e43255b2022065fa05515dbfce106f54db4f28bb7552e39881a4cd65865cf8fb0c5a1b38338633248e159842ba32e12268214f8a898b87fe3865ee5ce60ae80503f560080caf3abfc94ba346cc56d73435b2a66abcc7a44d1a1cead361b9838097d26b0ae0d8c8740f7ee85655c34356d631faccad21663422484ed4a4f5fde07582a2273f61178228dcaaca68055963f8e756ca37296d4b64cd06f6f05d172a7ed8de872a3081d11de94aa91798c980f1db1d6cada53246d0cfb194a6f62a0241bc0ed903f193288c7ca587c734b5be99d57619649cbe0f4c3333ece7b34b58c9b4e27f03f5b974ab95e9c89218d10b1ec4939bd4884953396d851d368ddfb7f13f340bf227dfd1741bb8fe5af4f3ff34e5bc49884ab6eabc5254af84399184029c3e5f463426b2c7c695e5542f3876a1597d79e0ffdff15a0a423cfd07b6eaa2756eb4bff2e7696b6fcd01092b9f5ecfa9e43cfed87ea1b0cacaf0b63910f719b5b5424e60ef026281698ffadf90c95bd336d7509676ee0250887152e543fdf4d0ce9b2835c2d8cd087e5b6e6f6c4aec81e1da1d356a58ede46d3f906226a0e856db6ae22c73f686e3f4bef09d476619aa291edf112a6db9eee92f22dd96cbfd36e35c315f161f0b46d324b0f879bcf98adfd8eafdeedccf22c164249ad8ac917983e181f9f3915ee441bfb6ad680c9b45468589bec364fc56c0eba51a864602bb0cc7161784c4224ae9089b7914eac4ac63d78e58190f1d462b80638e0bda7f4b5021af785c2d7805526776e6f60c979ce3d52ab4432dd815e35e4a0c4f94a25c16741d6911b6896fa3168042e74e8193560fbee3803a5716ef90d40c94fa423e994f9704d4563e9c484af9c5410bd89c4d94e3b1fb3f4e0493d562533e9b8a33f7032a42b646c56afb7b85d5e98267d4435a07e53b1f84a77321bca95925b94017437c005b39a486aa3415ab3194553288c478e017678604c213751d05da19359bc4f7ace368bbda50b60392e5b505e63b35aa39bd0d7fb811f2ee6a0c232cabdff2f178969e5c7fb3bcc63d0a5266cc2af47b925b1fb997088f34e21bfdd7ad8fbe7c0d518a52d68a54158b8e57d8d853c9d7b41062a71f60a10af8796db6cf4f0a7be6c8f36d4c199cdead3c01f747b2f2b830947dd087a8940e63b820f4ce65f5fc8b2e2d0bee4bdf30ab550cee9425a937136018405d5ff245d7ad8c077f4018cddfffab03b49e78750b570a49f50eafc38f9a12d526bcc88b46059a4c234ad28fdc13291b2e6cc106f6b4f5aaab4a50201a6842fb2bb6ded57dd6ee06b5567a8dcec04917816a71c1be608e44bc836ebe3958ce016c3f9857777952765a778f18874585ff4b64e6cd430e319939db0887f30c11ff5528158b08e1792ea4cca196046a792fddd925973b6012e7c3441355007fdabc21890579fc706e18737901ff960b62c22d05b65ccaba1d3d969ead8a4af0cb4bf12d6815562c66d6f3e13f2c54fb4b6ed0eb74c3e5a099abf3f3833b79d176563c9530bb040f2cc04db2c351e6f998d0e69b4396c79639d498a5b2dd351e306d8dc62280b9547fb6615e3cd399f5be9f2f5b9f7dacaa9699a7068a0361a48466a4c4cb17476da2f5f47ea44a089e4849c94e3d0d3597d7afce5193179f4ebbbb45296fb3e1e059b747c7340866b8e310673e1d5fb4ba63b2911115dbfdc595574b2fe99dd5e26680acdceb8e799a95c836ab0d95ad4b2b908c03427d96985045c49a4cab93d0f609765b4210d4aac2933e935c8d6fdefbff8a8f753c03b457ea60173cf748bafc448e1b4ebc7fd64d839dd7e7d5143dd05c1b32ee9e004b6c6aa99d00dab977667a1bbfb1ea856257b7c49ebf00824f1aaaabdf5c31433ff30f503f9635521c3bb9f506bac5431512b0520d378e0aaf4d90fd8022f72d806b13fb283189e59191a3e6c091c75543cd6cc87466b42842c45c1c1e8d0e71e93536a3fdb4d1b8b998dba6df603ce243cf117eeb6817d587d830e262b6721cd541856c8a0347fd4edc940436c308b33634fb4e99ac15d2d5f6931fd3c1c26026f0389838850355c373e04e41188ef2f9f49cbb6951ebca41ed52f8cccb902750158c937424795facc0867281b3268eea29f3f3addad0ebbc8840512141a9eeead73452003ab6759f4d68225221076ec90ec1668c69650907f7fd8ee3af136ebf9e2b6681b7dc42191c1b33dc6e669eaff2a3ed0897a3808ae34541d35f771594ed5b8ee6fb618a21cd6c3ea4006d6783b9f9a58fe76802bc45df94f7f5b16767e193a3a9af43cd6f0968d75e8a554bc605cb00399ff6365158bfead1b489b280ebcd5ed20869684e1de3128354aea070461f9c7c2c9634ce70001e97bce1f355eaf9948ac0330701751cbe76efb18859098b1b79ed39d974a016a58dde34f0e0c39ddf12b8ecbf8d7b40d7697e1a5424b94d7f062f46e184889e7d4be6b0fcf064bceb37c45909a679d3826a0cb8936ac8b26a0e45bfb0a4d22689f3ba237fdc72e41bfa32b6346ee399f6e9bc1350b6a9ab827f80fd3cb6f2004a13f6e02d567ced5c13344306bbdc43686c24447436d390603ebdac40a0a44c8f375e1f30a5d02be287f97b02fddc2aaf64b879c398ed31eac6f900ba8ab2528816cc480fb4799602d3fdd8caad6684898ecd63211cbee13733b51ce5fa9c21b97bcbf4ab59b7497cc855e666d904deab169136a81c1b34a455218a8a74388eaa106eb05050d23cd599c0b1b91e4e39464527a120f8fe91a612f693794c90321131a6bd42e4f0c08955e7f4d9a2db9112b83faa8317cd39fac2cb94910b282f74cb23dce4143925841abad3ca5f1dfc098e2165c3b5abd46d07339e8b1737cd4f7ab84d3250058e04982db947a6231d2ba7698b342020aac7fae284ddbffd43d9e816a87a3de6dcd0aff6ce0db0546392b5fa8b0e9542b6e5fd3c944ef5eb794f6aaa5a3589b13d059de1d2c71b6ac6cf0c0c5d72449d795f81883f62dd32c97bd8ee704a7d4367a014b589bed95bab6110b91a8cf39d23635d994c7c8780437ffbd4de259f678358f3e0e2738adeb753e3396dafd4ae5a3b16f2f30894027dff081cbe640300bf93240ec925b0bf86a6efac326311762adb8167f316cc358293a39821bfe952bbd81b4b6c2dbb51619cd0d6106261c7323a41ca0dd5667c1d9c7f535caab2da27f06787de384f4ecfba96b6fdf15f4ab0e08eac7fa3f6cc713b323c7361bac2e96280bec301cea58518c880e4db1cb61e9ab1488abf04ca7c0920c8aa3bd7984ce1587e6660f2b0dfa42bb0c5fb48eb78037d22bf0aece3dbd3f15c8e073605e04e427f4de09dea961ea93b2a98966c2607436d0a554619bc09bd9de262669ced06e1a2b0cc061ee782dd2e0f851c74df6467246127020c73703a1ea980fef963cbb9048867fd464892f49cec9a1f5ff783117af870030c73c4c37f5d99daae862c3cfa322734c74e760c8ee0c65b4ea7fa5e74934166754317c63ea0746c6d434b585eebf15dc4697ed53f178dbdc65a672853f4a6ac12e7745b94e53429f3bfae444d989a450304f4a46ebc9c491fe7c812dd6c941342b3196dc54fdcc9385e2939da9fd842a90cdd0fbd9348eece2503f9592ab468a8f864f6d41ef735e0380ac00e84e11d9ffc7ccbb16b35a83b6cf030a042873c97dea4a22394a1ea657ccff9d20b2515e133c75741df6091256d2c206b8510ac14ede483a066513e26213128eb84d4a4ca4d9442134063bdfeb22e64e774ed8f77b9be1d70e9bf37454037eeb53ad91c6bc1df58342348ce0ded737d76df5a3976edf231b2df75e38dd26e397f2634bd9acec8cad7dfdb84fe4ba971430111f13619fcc9344e55fcd7619dcfb4b427a1da24e10cab86da1785560eb19d8fee95ea9766f3e71c0b87c4b20ecc1f8e572973cb560d5d3920b951670218f17694b82b30506aa73890abcc8b7300384bf6b258932d1f9205c1494e44d981b7922db02a1742397cd3d8f66379bca28a990a2f9e387fb25e16daebe96fed5a79e729851c9eec0ddb79f4b673b6a9f4ad2239a17ffb39897185b4c266d39778f7bfd4d4ca694d5c2c3b4fc2dad44465aea4297525e633446c6a57e5f781983ee1021776ae690926950c4d6095ab7620b78ebc894d4f8a1339f109a79956ea64caa05acfddfd52c5b2af1488ecdcb0b9982297219c97846e9c14f7c6b2d561059da05febffc313935ab09b6e3d3e6fa69bfd06cb36ca4add2df7e66cd8d5bcb0a6f1801d39b5eae9b1bb218558193518f763a38c698a702df231f8ead8bc9d6ac55cb495488b18386c6ae39b58433ab9114de31ff5d1f4b7a727aa9979e19c75de1cd68f2e01e3e4f3f3fcc104e5361fb654c0c2c0aae4c202f308f370da17e580750c075e1c2cbcaf86101a7eeb99380aca3a15c0465b9375b20b912bf46c4ce9dc691f34cb057d28f458be7b3b25956b8b0669d234d2497ed1122ec3cb84aca600a4111ddd0d13073647b901ffa5becb4a8e354435a900668c127176ca3d40301cfbef411a268e7218f7cf23e50af1e0e4e3a2b2c323d3a4312265cae7a124d426a09dfc6099eb50a25099703c7da69b261dea18cbb6455e2a6cb76d283ddaab5328afa244588b52ad98df7ae3200e1558b00b1cc95fe3fd5ea8b94ac8b1f98ddc17581c780394bd45db415e705914c22efecce660f107bdb2ddbe618168781ddd0142890026c3ce6f55278c0c5911d3e6f9a371e5332f3c5d8c707217a9529d742266b51f8cf069f3471b78feb66cd8cd56ad1e78a873e44ebacae533e0ebbdd7e118d785a67286133ca6023264953a19ad6a215bb961b7b1350058e43a35e6b22619893a16e45c8e0575ecc3cdc1aff031900e37c9b8fca8b1265fe15bddfdeaca365ace8cf540189f30de1ac62cf4688007a52a0ca3199eb76b6180ec718053d6cf00b116c20b9203de1ad8cec04d0b8dffd63b28aa32f945bf6703b0140272cf50939a40f93d342d2cf143c3c1d38d573d0599cf963fafafbe1672d7587ab6cd9b7ee641f038dd12cb9b93f94cf7541290d1ddb0258159ec7105697facfea65202212d3f4c6549731712c6186ddd32888562aae5a3aabc151fdb64ca73b84b9180b1a753e9da5ae2eec31adc1440d814ac76b75d0510f7251a633234d0801f812dfb00b73bf1608c6471e0a127edb5fd6cc403d0c30cccb13f2a7918a6428428e11f40c90b3bd7121d717a5032e5791c714fa966e258c99c8c529489ff5606aa729cbd7f865836680a8edcd0ff887766e65ebaf81f215009b466ca1295afd3626468d7bddff5af49e8840da54e7901691308b91e6619afbac60f621595c6c7ac3ec7ddb00343c1e4824653b9c6aab38b30c052236b0a39910517957c391bc801fbbd8505b9294fc0c9682c53537112b056537dbf7bf2e6d752d349a679f0a021585863183dc88d5f02faed84cfeeda1fcbbe2899e2cb38098b5d815d408bb03d3b8b5a6052841f0eac59b9914e783ad42ca8577caed35c3eb485be09b41c43d08fe53acc92b763a1fed1f8a453de2556860c3b521adf769252364068ed6b730789fa7a0a66fdc475a61e1a93c0b8046e4b6bd11aa54726faa50ad02f00936eea76063a81c2a22e2350dea6d6d595fa30916d7f1e80ab7fe82c21336cbc289e3c5e08a6c3f2e0d7426ab58e2b6b953040a60bd8376710b639f5408dcbcdd1ecdb3501a3564ff3beee3a5d7ba0a6758bca2dc81231e0233217306e6283071a93875903b49955ecead296480b233d18fd9e90dc1c06dc621c1e1e37c8f6d7bea717f5a48cebeec09a7cf974ad3eebdf259f9731ffcfc732855afe4217b9190d7473185ec11bcb2e56a42cf4d57932b7a5fbe4fe0900e4e1c0afd7ddd3886e4564d801d9bc023ac63181ee9f913a0c6e013c317474340e8fce98d6e10837a197e636f782fed760c1d21bcbc473f8a18e12243a15aa9bc69124080c97095f74544a121e55fcf38c4de25e97d7f9ac047f3927910d770dcf00d0df614e5d3aa0537dc4b695525805eb702025228c0cadd5f7bec55e0aa4b6b64d4ab14c6e41ed9771f07b6fa135764ab2585988bcee7ff00f6d4451ff7f4ea4578244f5a848d496cd4ee1dc9d14d4b6109be34551b7d781d92058c43b8f700ce53611df569fcc1f615479b6b31dac4a62899897dbd3d066095a32aefaea928bbd9ba5e367406dd8359b9ed0c1a8c15ace934c229b0e8f0281671b316184261549211124506650e851c03036b1ea8c858c428de10ae205115a1cee0a593d4e37ef14eef1582caf7a8a96c52291772ba28a51b5df6ed897527aa45ee514a6641fd3afeb29a390247d9ff9a3b79599ec9b38301b50ca505f8e5bc9db5a69397e36a663db6524bfe555e9ee726bffce6352a8877ee90a17ed44da4f4028892ddb5b9ac9073635a79c7a04a62ba8d3a196654e07ee37fecd2d94a4bcc10c81923b420d8bb30aa0cab28f384d2e4433214219d123c72474c1e8cf8203d9ae303a22a5d0c7c8a29cccd9e357e8711dce9d01628d59a5f1019a24c518c02865ae754996d5877c66ee5b9b8128314485706ce39a8a59f62198088ff674c9db5304feadefddc6a53ff5753855e1743124bae4215dc1df5786896e80330cd8205ba3921a1f7800b16049dd546ba937b7b1b06e0d92f4e496c2e5cfe16eb2de57032cdaa0c92840d04e98c5c58a05821d4d29b6370000bc76c7bc9a303d4aa336b6798b0dd11480d6916f8c164502c38a598bec0a4560a6b7ed4dd5fe37343951320dced3e2af9c44665ab358d9dd4466d9abc90e06b32119e3ef818a82536be931c2c1bee502b4ec8d0293c78c5e78707d370b2495f52c60149a5de65c05897e1db3aa52fae888a47bea337539644803957c5ecc7969473270fe7ca7022678b75e6447e7753b296166e8c0c0288aa5dcbae9718ef1003f5b850bc0e09af26ace3b98d6c94e5e6c3b31724f87091d8f9ff2d1892afdcf6f28f44b0a4fb1cb96e8f9671c840c20a00eabf249c1b2e4719f03d3ff9216616e617eee4eeea27dad466666b181fbd87a26a0b9acfb943b803a80ca30125506721cabfe288e8defa16688228a8e977273afbeb419d400ce206614c5052b257976b522809d31aad04771a44ef1235c3eb4022135ba7393fb944ee74f767f899b392605a93a7b2c1d6ed32ab85699afa22194e6c840b680acd09170a72357f7716cd9ea73dfe565a6146de9384834c24d60a176df7ff0e7b1fd2a2d7272a7bbb47936f108cf377e0c157eb8ae1a27278d613ae701dca901e1a567bbf152845744d0c5df4e370013e3dff3c91aa8d91088d25a3adff19d341e0423d33117bacd8ac2d3b7c5ec8576c8e6008227ca1bc9c645bfb190970d69b54b11993041dfefeaaba69ef6f988e71f4512502626b07201d364c84d56c90fc3f619a9b24b00f2d19eef175fb6c7c9db3703019e000c10c5ea0be93c3699fc5b94a283ba89e13f2b5a8afb2a3af5853d5070ffe843a1e9caabd8f2e3509da9316711ce0d398dcc19c3ad271ad5cc3475b5c11107f739ef604550391d957d662fe4a50e34726a73fbe47120a1e247b545e8def59e39976046985750e79abf9b4c5477cd508286e1011c309539850ba6c9520bc109b265b6302a0b170a0bc35f4f59ac0244cd82370abafd888d4ec14f56e502d53ef24c03f30793c010daaada864dc5aa204bd4a30b7b243ff1152533a945bc4836f89cf813e10fef31c07b5b7d0dacd43ccbea61880c8bc35c5f0b4f5e59630a8f1aeb822febd9320285119d72f15ffbaeb11a4324331e2ce0a9347af1e9101209f680e00fabaa0b4645a4e04ceab5e373e54acaff45d57717d929c69cc0c14fa55abbbe0312b563e829e464aaf3d6635ca1cd3b2d2de4a39e79efe3f55b847fd947c36a13447afd162213f1edc32e61caed08ba90a20b5e6da074b74a2621a0eb29e6541db16ab48534b772cad16738898bdfd3e842b890a6369c79b399f730a6507b4087d1c5eaffa76078fa59623b9aa813db321c290032deab6718561743f2eeb1853ed0315344a00ab4a423c8cecd8d146b1535ed25c0a9ac2d15cc31142e7ed4cb4151a6dee7a89c36cdc3dda901a771b5d23654f2fb767d2819d72d20c37795e493fbef4f9add9c6aaaf9103a6c179633a0c10d7b176b265ad039a53e0e4cc37057a2aec74f6061e2b3ec963f18aa9b0c74327c9c80a1afbf5cc133c2e890ade8ec574fe1001752d77b49e83ae1982e88f0516961b742e0d5044a4aa62db5eee43b47da06be9839efbb397d0bdd8a174e4ddd364e5a42d54b15dab354199c7058f06817686f30a907385fbb0ae55d72e44564ae994bf3d9782d1483d2180633772d4bc2d742488b5eaaa7fba0027c3460ad5b487be964988395d633cc33a7f3f94eeefb22db89413618f0502b875abe51a563b8950aefb3f57c4bd270ef316d762b7d7ab329e93a19a5dfd506ec63d200ae5a63d6e02d985611481c64ba2613238deddfe28f1eb0b2956934395b06c5a61c1bdfeb857736a3fefab01756d581a4cd30b136f763f6cdf2569456f4e04439f20fe90c1485031132a9552fcad82a3899445923a5edee0307867e96ec2e7541d3b5897b0de141b01703a312e0c2a543d9e50df7434fde4857117dd54a436845d57a7ead81f89e54ed113903277db3bdc2a233deda93ad255f118748b2ff4c9b8e190aeec224a0b0ec9c8e3aca01beca1f3435584fcb9dc421c808410f17601c3c4622cc40e481dcbedd3a18f464794159791bfd98168fcf5e7c9a2fe2a88cd955f853d0a690e4b53095c1b83fe1f72d918431b218f9f94f900bc624bf65f6e2a5180f95bc259802aabbf352c83a2a2cf586b3d4cd2a5282cbc2c96aa5f402954d7db7224c611a442429c39d33b0b6c4f57f077afb388b46d666d8a9290cddf9df890856b65c71ef967e03d316ba7301edeb98276a745b202e45e1eecc6ca12e68804626431a4de55328eae00dc317887a8d28a234beb74a7cd19650fd06da3ae4d006c0e850f09eec6d2d5549e46f0180a21e6ce0c8d997e327787723caf2e3e346b23a9d309f0f16e370ff47e43580429d0888ba247ea44a034f1a7a9588982170130c0c03edc250f928f669734fb7c1e90bebab943662ee26c2344357632e7aacd0cfa5d47c512edbdfb70cdd173aac1e70427c81f74970889e79110b91d80c3d6440f49bae8b98afa20a27ca8fefa46baa837ff42a94f952f15342798e8ff3f9fc773899dc29a00336144d71bf7854ba10142ee4060303af38e77a3e5d3b0832ee792228e2c707b03edb572382678c336979426834a5d1b55298f91296e9cdec3e6cefecd6c0f43d9308b54ab9c5719fc0d9062eca721844d4f6eb6fea7e959d720a06d48100c4620a3274615178b1b4a4d7b53d713f0139b51a02cb46c1488b587051cbcd70cad1674047382e9234828ff8b460d5e15e194498abe6e51143f6516eb11859e6c8e8e8dbee3c976ec28e408d81c9a5d390a75e5a5e2b06fbfee77d4d380ece5015ff9e0d4b9154d3160f627fe2bd847fd1298ec42eefb6d029e91cd7f2060f08270a56b6c04bcac807ea68e3537b2c4d0f1d4ae41d0370089554c840b586e59cadc9594dfe893119d0acaf1bfc1f65b83392ccdf8f1b847644c04b4d6ad7fd5d4ce17989dac28253b6e2f3232883f2d8ccc30581a68a5c21c1b10bb1c93a8936a55fbea790fb8aa7a1b8a5f31c1e39e040d2c5b457b67c3bf7e4870f4677f4a19c58fdd3a6c82da9929b4ad5484d34d8219db0c9a4cbf5a606232c40a137fb50e0c2c85488b1430c5b39f72674c12276e356a270cdfbec9fa705fa874653a611ed8e245c7ccf69f9d1454faa1c3dd1d6a94397de4c79d2f0902f3a5262a16274edffcc87b631b3948cd4cc2947542cae2104d86ac5f896115c782a00ee4a89177da304ea6ee50ba0aad21c80c5c34f787ca23e1003fba884d2625b3a4bee767a0f10b2747ca10dc81777d797312079718ec227476d6550449152b3daa840d252f6f5b10e82f25bc9c17ed61e1bce605eb46ce8758abe9f0dd3911b66b212f1390d0506d2c11857bab27525b5272a5a4b95a0357ae5f0d2c4a8350b2e8042d740519fbc09de395c010ee6b4e22755e694ad5df71b09b033b765aa3b22d527eecec48cfb47e39097770a09141e36e45d12736344e32ad630361134448d3b126e990337d485f2279f0fca461c0fad128f530fc15f8b923ce006407785fdfe2a06d5f4194e016d6f3c57cd5ac661ec10a9a6a8f7ed9f69272d136376b182fdf60abc24f497116f4d572a1b636f9eb7e026ef184484e35a07eab254173a83fbe147312c6106a646d9ece9d448e440045e1def6f90b74466721029b409240a849e98ae9646342e3ad809641e74ff6c92eab02023255043d1f0e0e67168acb766272c1506bc14e2164b4ca7101ca09d33b6c3361ce6c2592526bd50990ee93235853096f573040e6daa11fe543f02558e453fee17af8c669890778e7b2c887b3ca9af3f419347050bdd07088063703ba12c100239ed2e9d56c1ee7d00a832a0b5713e5c0f2f45fd227757aafdd15938fde3bc5b417880b7e49c89eeb35a46a68d94468f7249077eb55d1b9a39db78d86de774aed3e3e7b574e61363daffb5bbfcc7dbb1f049b3aa9e1949dd566bf9d31c9ebfecf8b1c2fd5a66253f62d554c874cddf04a4e879f30f0428b6b9990b36d68f1314dbf3d1070c1ea71745330e60fbeb19ce7f9583de72b7cbb76d79b00991072505162a4c4eb6d70444ca030b88416de653057f9f1c6e1df800e13ef3d456c6ce9eaf825faaad51d7031f2a06f51d23f475e89a6621ed9c2d6a9c38998ac10d1fe344e90a106e41843d1176883a62d35dd119102fc7428eb9baccfe4a786dc0364c5920501a97e176d7316969f876f6739c4cbfab26e1251cf021cb1dc48bfaebc7c2a1661e27221c05315d1f41220f8c2835dd4bcc4b78567bea10c8ba4090021bc650db5232ac295337e7d40d43896e5d733f5fd8d95117c707a8d5c662681f4f3bb9bfc143d06a2d248108fef9506536d4cd3980881ca12035c8604f2f32161027ce85c4f7507f0873e2e7ae53bef9467828504d4e3675b22909502c79bc87394d48cb7bb209d60dfa89234ebd8eb3772631f80f01c85790db234ef58808313f398ae307c6f045a3f1ebab74bc9d09339e7770b569e744937bc2763cc4d6636d3bc682573ef2b996d96fd699dc7cda1c7c6ee590c6366666636cca774c5156e6813420e5816906a7a41701fa0dca1419b42d543b2695d649ff95f67ba61807f370200635939b47dd3c23f4ce54b1e5c2e155e9496418e240f31c7b2462efbb7579e897432e4a1664e357374a246216ab4426f97827616306dff2c7153692b3a8f433e40dd47ac317836e98ab48158b0daf241273c5b1998eff9c2728ba6575a8de0d4e5506ed77fa35c0cb61e4f899c3888eb247b615ef28ad104820a97474f8d5b2c430c2560f68b9e27b8411fe0fb85496d3c5b508c7828d3c3a536412607372ee4ff630671c31047b250640e72ed825d012aacabc7950998774a54156f78dafe42781fb9a8ba666fb8c4ae8a4f6de0e43508321dbb0e0a243669528246c8cfe34f41c9e1f11295c46ed444e1825b8a076b0ef8376b6d321ae0bae95d83ee51f95336304fe008fd93f82b58b36cbd5f822dff0c78d63cf1eb2d40e9d4aa7f365aa688f6072bb28f2643fa25816012b3d328a1f24e638e4c016f43e31856744d88586d60ab2c6072f897fdc64f9f3bb1850bf400f4a378dc6bc46833dc661b1e586a0a869c826b54d5dfb1d7c2c9f2a21bdc44792ae0dbfd8ac8095f69b919d4f58060a2e88f988ab14871c0cd63f0efd42403d717cdac51f26443a69e1933386744ec296d79562831dddbcfa541b5726ce8eb1b64cf3ad9b35cce735e90ee36865fd75c97e4abe6aec350fd9e043835ab4b026857d392d8b09cb41d5a4cc9a35346f52abd1d989a9bb0d62829e6cc4ae43ad77095de51a73fdb46afae55203c86bc182cf1e1f22981e93851ee4e88e1b9a7ca2ca8d1faf6a9576bf6547b6e63d40f188b1c52c48ac51d7c215ba3fc77c5070790c41a409a2541979493c9320b851a44767df89a11912e661a85acc95f226283ae522b6ae63592fb868b6b74a622d80e80e96d4d9663eb78ed2a40b2d824a4acd9540db9a81c3831b0a06aaef679994d5f2e10c4d71264b7d41e3d9c67035789034f91c479d5410df09ac25deab0238987124a67770e26ce4a54e42352334fb10c9ccf3a7d0b00c34f3a2e75fcf1e00e52678fc3210c6228fb8e510a0318075a6c0082fa8b8ce72ed2a54fd8904734f199c77ec18d217a34d1a225c2e8c682a60599f0aa0af2649974306992da04e251559a4e7382f1fd3d8a4ac6370e46c20abaf6099d2d84016e9f2e7ad6b5cff332fbf7abeed81798cfefdf44f2cc700e6f62ccd5a2006922ff8461dd0d892630f3709aae03f3b36daa95e53b4b196ceb21ad33f81ef200c2e57b83664e44279bc9f4fdfeb5e6fc92f408298ebfc144551ef4304019ba7b583111f9c30ec66082de8bc081b90ac28d273857f285e61aa311ee13b676c4961a28d3f102b41be004d188ebbd6ff37feafa90bec272ceb2e1ec33a0099770c744416d9e5341c1ccfc1a588b6a8ed54fa65e78466ec6995df8db07a0d1e44a801adf1a85fb14b19d30befa0cd0bda14357ce81536c29445604f3775831e43504717de0ae52972832c8820d74611b1a2bf0ea6483dcad463f2d0adb56d6a45b6c4281e32c715e9709edbfa7089629dc2dcf70b4949632ef32dd77f1245a60c9c48b161fad4c855ede1c7ac0604324da651c362a776bde28fabcdbd30c5108855420f776d40b10c9d5dae25c1ba3889f8cdcd54dac626df8064bdf56e765519d82faf2bd9124f804a58b2fb0fec80bda3bc0ec798848dae2621a1339a1bc2802a628f5259cf00ac225f6ab086b6786278532562970502f380d49b3954da170581d96b3bd1f79d34afe3886eb49d9ff2287f1b5f94691089d46fa9587799e8718ee1789515ea918f42f8b11bbed6a10451ebda1d12be87cc40b3582971c9596df7827b1a32fd45f3d479967ddc34d3370f8e12b0c43f5e6d49a87b2596c20a6f47a4612bee31ac43da2f96ec073ac1da3bc56b731fd774d3f822008ed3969df670b48d7223108c3451d193ff1dfc9de0cb000642a8f188b5550118ea17c95b21f68507e705d6d125a078929fbccef65f63d16e3516e1cbc4e80aa894dd00c2afd388199ab9de1d1e7352d889c8b6d3a637453d0ed844ee3a01ba52fa16a147783217a2029cbe4680d14fa0b5ed0d8058073c18ae9851a603d9fd02b8912ccee92526e75f0c42d89309de25c7a3dcf6c3801de6550e908c58537a9e41864310661c61e94672375efcee09389dcdc5f66717c86c744ee8d50940f54c85f2fc9a8ee2824d22b362c507ca4e9b7dbaeb0c80864c58a0d3358310b769153eb580d47913127db0287d75c1e84dda7f1e8adb3c457e010231e23be631a2b806724ca20ed9581920fd857b32ecaeb0a1006774e1816b983bbb29cd0351c6d1373aa10e8923ae9fc94ed108a52fabc4b0c5f1544c59c2e0d4447e50e5db7d8d07024d204d597e0e7ff9a4fc7a9e5b64c171939c21005819fd344ed80529addbcd97083213b8783a0d35328a51954c085bd1fb7416e21e4ec2218a2423634bdcd969cf5a593beb36ce4cb7b0354c6c42d0e4a870332fb0a4b6da29f5fc4bb5ff2a6c355ba6825addfeb60bc0c287e97e6c951cb19be1371a419e5bac0b9bfc3f28690afc62aefd0e9f18b04dfad528291efee35fb8b4c66fb9bf954b9118f2ce1e1276385855ae0a2267e9f8e665b2c6cee23a5d7b4b9a7d82409c1b1cd2fb1af8f0ca03442e0761c8f4e3ea25e86a46174ee1ebbe84613534f50e4e1ad2b01645d9122082cf84d589198c717c228fff447c95be7b5e558a85e52a2e94c55eea20ead7fb64e565d32b50b7c110bf4b8505bc2f1e07d76b363472db05391359fb902cb97dc265c6cd52727704e6134297bcd346f58d4218dda55f9da213796d347867a569c5105a40ec6f7ccf046aee230a361082874e115264bc25a270cfde7a198052a3c24a6e8e4f40cdfa66e9cc0abc9410fe492b4cf60af66437d5897896d8e5592e86cfc651ba058f8c87d90d4cbf6270f1e46aeeb1c4a3e77847b169b27c339a87b91d66507156f3e87a6331ad5e4e22bc89e3b6416739dff4813680a8d0fd9bf9ff9b52191dd985754f271d4cd9f21becb512408d8531304d380c80da54ab02291326d27c6defc6aa9ed1cbdc51c05cce30238fe112e2f3cc326877d6aeb472b3bea01b9909edf4fc2c16a54d9bde746590652bc643185dc07f3f15347bb2acda8dfe1cae8b9c914d4a3c8e767840c7ffbf0381df9c55ede8ab9298aa18428d5feb46410f388b846ab63da3c946309471f5b0a982686a5988502d6e8f1dc74c08e7ff1bd320bb46e1780776109c2208eb1417dffb95a487597740497726180bbda7dbb831979ea8a51b44d70800022a8b4223fc942327e7c5754739388a7f76d4c397805dd72887c8738756ad7ee74939376a8d49d7cacf858bf0a08a5aa64ca0caf348114b309066c67a6868255465d31554efb2bc42748dd9e69484573a032d9a0c880a90e3e12ad2dcef8ad970d8b04e3eb8bd48e347a658a892c73dca1c9748488ad029173fb7d58b753ba7dc33005f4e8d3e5f3e9e442da81be733bebc9733b95d55ece240f71e891c79a64bb23a8647f0778a845858ff85ea61aa4a926cd5bbbb9d924e76017843c0e35186f0aeb8ae12098c4caf06b2a5331d83006b47d39f6bb84f5add049e7b46c744ead670ffd58a2ccf22a618671783c387bb810662f1957d4e079e2de68f1c421a6ee779019008a2e39814b144a6578d579e121819799201ae4679499b01213c686037a6c28c35d5a1b686f1a623c89191a19358ec71bef62f6a04c4fb52b9e1399aeca00d993a5677e3d460200cc2372ef0dfb911543e44165453f1f23f6b787dd77772c9734a80443aee6a300da711e271c9d70960df3ba5f5817321da3d434d6c1313365f709340946ebd6b458a061b6eb73eb91459b00ffdc300bcbca89a16068d22d3e910624543c395fa9b28416518f4acb060ecf8cda51427c5f15b8a63f1c0262cee1e8a7ed95d7264b5f43170ada9ef062fa6a3f3bbe428ac9b42bab5bb8c6fdff66310611422fb262f6c5e1c9bea9a2b3a45c6b1dd8ebaab36ccca1a99d75f82fe1d1bce7c1c283ccc3db84842ccaa34245d895d7f67fb379cfd6613226ee557a368cd37974f8eed68e8be29321ec737d5b524c1e73a25f64f1589679b2ad1318d6dd2d825d27e5abf1dfb89b8587bc288d5015315f5a54e28041e73749d522a92db017d75e1475979b91da947d821b21723f3db9295671fae647e0309611ebea3620bb31d7f0aebd4655df43ac39515daa521dc14f926a59a5177e338b6f2a38a4459581af11b73f58b9cb2e7b05797343ed813bab7d56b4576d41f3bde6e27f6b9ebcbad079f650b66dda0518f4b2c86db180d81e8b5eaac61da76509846def8b79d8b64b12a4e735c16af521cec3ad27b15cf44e82c46012ead88bf4485c5b60fa94110f14b54f12c21c3ad3e7114782a589e79025562d2ef3a5d33cdaf40ced8c094d900824663e18ae84263c07840536c220a17d5c43a6e85fd2cc1462bf1bc690f48c273bb01b4505a8da50464523e594c34734edd07e31e8f93163955c7ca056a1f223d174272c59725bf2a6ca33084abbb765355a19f9839c3d6d87f7ff2acc55a79552b52211d1acc5017429875516336307e3fa8fbbe2cee7a817f2c1aae411eeef09ac0a4ef27b4ffa4cf3a58ddf9037fe984af5d78ada657a779e3eb3ee20aa910e1ab38681b03e1ec76e7613af7ef9ba47350265158fb494687f2cc55bb6a2d67e0b53a334bf585243e13af5c2452c5e0d2eb6aea38cd91c4275ac567f8ac729d47c61335cdb6bbc4317e9679436e5bba709654e49d96763063ffc58282a4fe1883e4a7046913bd508344e2e4a2bf6f6b62f6ab44c099d8948988d3c214179389f9bf2d43a42c5ac20ebba0d56865a292f1ff3c93d178df2ad6b311021af1c2d871aeaf2ea40b1768c0ff3cab3da038b08a45e1019ff77699096470c044a48d727bcb7182193401574558fe58b61b56c7ba3585a3f4400c4720206ab3b0570e881637a0ecb05885458c5cfd7929e13ca5a14951b18982e92945e1579a6ae36884547c38e0ce353195ec03e374e07f65dbde85b2e8f174a0603a579e2132496c52466d1125b30e68afe2d96b546135cfedb8adf98e8e882f2d5245939ca446df3985394ef080dc324ab9eb061d30873734f572e922d3abb2faa370f4fa27d59b1362fe09a9494d1c1f015120e0219a610c8a582b160178c142eda3d49e2f3e44eda428ffdb546b7af3b13aae2e80822c3752c041a3932aa0bf2e94492939a6cf95961328a6450beae94aa1bf0b5effb754c98cc1cf492edca4859c5f4b26fea5cabc9692c8dab6308091691426542c378737db809e28da8cc34a699b75427a76e2f2e6493c1958a35fb75665b9efa57297b107468c987c12031b34fd6b47084b59f553e9833bd42c957faa1125fd1981e644fb2e738d30dad10c473949dcbfe1d3855c3f0a32a2268b990c7443ce0c3817ffe1d0df8c74428ef154f0f904af5799954bcf60d85137fccf076e31124610f80cfc1131cf42225945f469c878573509c0702920cbd0e1ee56ea69a7d490580e0aee677ef78eb05020282e97c7b2548856e825c67f0c35fb6185c23da3777acc48636401519f28c93c93b875e8c09ca28d092315189624c1deebe190746df0fe88082551b308c6303a3417ed682c9f1716b94adc72b80787f0697a4062e6f891b0701a7a1ea5e5bd66c9baeac354de33ac65e0d995e98d91fc98d79c350e29659dd93c7c1ffaa83c0dd5e70312da0137d228abc1f074fdc606caea84071a0d1cb0869d705137135746e68c05260471ee501b31bdb490e6cb7d6a170a87274cf30003d1262642c2372fde55b048486d2dcacaecf8cb280fcb530a3074bfba8b8e823ba41600428cf91605641879aa3d1622b5be95b98751166a27011f6debb0803dda2305dc5c3120986da95839ed014d2fbbbc92466991ab207ffea54f17231cb2fac40d9682c2e1df2f48417e11feb2679f20465b474202cba751a60afe8651da7f84d118e50b4d38f275aecdf2ef7fd89022e3968074dbd00d14e23ac3c533a82c1daeec2ad0a729c0ab65c5c0375c0234fa9d0cfe4b4a89c8e3e20cc522b18c73c56d62638b19cbc7f3a0b5d03b108fd1b912e0c5458b14f3cb6bc49cf6158a035a77cd38af323618f5c08311d77298ea4324f08266a6c561c0a0188136c4f47b16ea3603648432f45764eec0b090b0ba6fe03bdb057e1474e6cb369fa78a2ee484a13aa2bcb88452d7977b4fdca0d28829ed07f09d3177fba494240d11bf0433cbaded6ace02fd2d0e7303f0634a00ed92a6377e48b14a0483d560bb94ab55a4adaf5a34e2338483cb14440e9f8c9b172d52d59f31aee3f7b73f1f714048fe511e496c8550e06a700ade3b983a0b426d5cf4f6e29746893c8717946c88f5249005a271637ead003df1b2cd41f41b08dae54c00d2d4d8f42a3b100aed136485b260844d9927126265f7a1f05d301499df38d4a003d27a22e4ab0f382b559f66a87624ded8aa84e7e623730f26da39f20075f6231852f8f1004b1cc032395bfc362aa9c067209ff305ef02d2c6644579a9664781a903c2a45621d8ed70839ab84f3e9a952293fa5e82915dabf83f07de1ce2652e0b5506cfb20f1d0648ea33180f477c69e451a7b58856312409bea0f6e2274e65dae8420d8bdc060adceb745251edc9fa068ae23006276eb161b7098a5432fc9d81e81b0ca498b24820e5b53704462a4a655c0146540ca16009db6b9c5af9b52d23b05752d548596c60834878fe92e070fbb5daac7ada19a6564d4fcde160b013f20ab5786d29ccb5d05ec4d1a10368c2ca362b935d5b2aeb19ca10120f34403efbf78b6f425070ecfe16ddbb993dcb5519adc999da7da9a7e3dde8c2cb9dea2fe843377c538dac473e6c06f812ec5314376ce44fd69b872ce849ae27273c392a5965a03674da7580a229b73263d4b7f48bbff652cbb78b6e69cf9e0ccc71363af5a23a084c6effd28e59ed35dd98ecca00e2c3f866a7a692665d3f89899d95e8ec7657a7955f4498dc06e62a4687655cd99c119b468e804b0307ac311e44aa3b007e680098e1f1f24d4c8dd9fafb3676a45212d7986a5e2cd3310c09ec7daaccac3590c8a3677ce9c10e20801d47294dc0d74da03b6fc0bf6bc22e6cf576d1ce6d25c9cff9c2369dcbc99732c72dfbeb7fe361fbfe4b2c4a11f8fc51ff74f4e4cb51c428c0133020a005bc4e94807c27f921a0c8e82b8154fb2df29c56aa6ced8abfa6439b8d02d55d3fb5b4f381eba5f3f536573c7060849301f96ab71dd6cb24f59ee5103ecb2c426803595d3ede6979f9161a9d7c2aee61463857b7f79da891926eda4b73b87d794c9c6be9f449ff19141cf65b29d4e93b39c77cd4358dbc4cf6b4d517cc52b61382f91ab660195aa7a87a4a28926b3237ac437c7b9502c91e17bcde2a847e4d19b0a63f603da66dbd57ac7c11c74ae130db409f89e336cc4d86bfcb63bb3f062b255c54cd7d6356656dbb3c77997c32b36b126343eebcf0ebe2762ddcbfa9ed03166a55144d630970207dbacdc3d072172b9feac02046e8ee44ad24d3caccd7fe2491cf998c16061c78819e45952a040e1d1a17e56e8661cdd292a7b6ca1c27a475d1e0850512388f29063d46ac41f2347ad28ca1f4129104685b2703c07dbe18553a8225655971bf1fa2185b414fd12ab568ea40af9831639ac47532a31c62e9f2c967eeb4290c44de9ceb868fac83a15e4b8e5125163c4bcf6fb7ef57554d65dac7da1c15a03a4a981c476086c201919aadb3931f35609cafd067ca20fbf98879713a0c87fcb299d710060c274cd3da7f0a4f16611cfd6873c429e504474b25c65cee202c2779de260b7c42d202609125f4a9b6ffff35b97f2c3c21fc08ad1523dbdf64bbf65a937ae8729371c039b59a97c0ece0c438c2efecfcfaa941f96e0802a464ab1f4fa931d03ce0c07040e0c03f33da93132116f6ecc5dfb9fdf9a94272ce8802a9414324f47e6c14617fea724e5cad009c310fa03073f5e46c3d218a9c7cd5f5c94adf2d12f26023c70f70131e2c2be3e2a3b8f73a62123ff1f9871f9e7be742747d5f8825d5c29868c6a5745ba28a55d7aba30fe2caab02f4ab7638107e4cd629aca00a87abe0b5b81a86ee00b7f5d577a38b2581b1ef34ab46c47e3c62a4fef2cc02977549bb691b55619ea0d80c6ec46814a159aae992d400ad34ca3f5d719ce9b97f61a0186be9ac02700494bf4e0acb11ab2cb4d833b76060475e49600c3a30df85d221107f8c3ab0e485e02f41907a1be6cc627e90974060c54c7928dc3b5b07c040823864549c52d100acd95263c0f78405389802256be0f66d36d0a78a608d909b4236a5f59ff1382fbd2b7fd5c5bb6d1586dd9399ec46b415f6330b2bd9b83c2ee55dc045759223f394a1974dcca9bf72ce36ab44f7f0671f894c47b836c4819b6fc47916d9d20de9a727d8342682d80d22e2ec2ef56afef612ccd31eaff59cb6c451c5c2985c50e836b65d1e22e05513bcf83be4f9d9cb44fa54859d38007174068f305513f2e00d43cab5abfb108c16e39c41f6cd19d2ac91970867433db8e17be369ee72671a052ae556f8466aae55ae132d5b939b7c2848a6c884fc50cba5ff01a7e942355f825d8340bf000b202c2be17de0cf0bebac8b3f6dedd69c0640a7a8118a823210440739526302cb9c47706f50dce50934dcf4e5cff93c5e8b9043c6d9d9c18ee15e8431b4f12753f2557882fa29fb4e80d72d8db86877e4e17bb135f62fbb3fcf9539b0307ba32d1fdb14d561a464dde926e626261c9c63608303157b68143136213dbb413fb2d347e396f2f048c2be045b059c60dbb255171dce29e764bf8e961d12c4efd4d83365eeee69ca83635d7bdf185ca86af01fe972df4ee0ebf7fe2c84a98cd2d52994131ffbd34b39d8e5beb60312312743946e10dbfbd44c0aec1412fe265fd0a65743c52366c4989bdde2dba576777ce939bd132a6128cba211076a45c070b5f918942b7d5e0b9cfec2695eb6f8ef4cb36f98ffc4faef6a661a64787d9a9f2299d0f4003d375a29d527259a539292337adf7e389415ba9f73135e8416c589ce0477dd9bc5bfa92d3808fb5bcef1284ee3082c53efd4b3bfd2feeb99817637b8857f14a80dab7840ed30ab4584c087887806940d1e3d8e86201e9e97112cc9c7c110ccfda590bd825742733f437b9888f068a70fc23efbdb950930ab81f9992c341c47b73638827e0ee4cece8e3a1ad1a63414e0e854143102ea69dfd75ca1741496ef2bc9408541e1ad6f6e58b93217a3ca8541dbf4585e282b4b277e7154b9b805c43b33bee0468a650914f324feffca65063107d770b5f3e87fc931550b2918af665cc91d180b2f51910b969b2ac50562c16996c9fa0a160c22a19ad734759747afe6cce98b9ce9807b07601fd8fb05c51f4c55f46f2fc31bff122e712e51f4d5e25cf58b52d07e6a9e51cf1c1c28803d4a330bbc883a08d3840fbedbad55a1f0e4a89c25f42cdc8cf3a4a862eda5c0cdea2ba741e00c31f218c94231c64d475ff24038b85a09dbab0692342cb30685a5618a3eb5fa322e613b896aa3b90c9a57ac0ed2adcf4ac64a71132223222e5904a8cb391f1e1f297da3287cc276a054a4fa0cb2e24a1166f2be2ead1d03a55b84e49dd283c387f91c0528166dacc5231263173c48c6ddadc01f0e8aaa79dd00a2ac9c05ba432c2057ca72b9d51fda11038fbaf546d558ef17465bd32fa4dc1484e63006ae48b113c60c6a5f0a440af934349506a7b57e6c002493214713ef762b9b0c4dbe15f63115aaef3af893abb55591063996f41fce58167b74a303b0d5df7780d12d558ddd0ea7696b8bcb509b78e1bd8fa9302a5ba3943fa28d9ed46234e8c621174829e22c835e3fd34515e5e55d2b65a4075235543d58028c4c74a64f28491cb38ee45617fee85984c42776829f537c0be85b234974f6a3e8e7d613c0015b817eabf2ba2fdb93262bcd16f443c0ede0078550b362e849f31c4d7a3fe6420b6182177634c1dd57f3aa998a01651bfe33b0b81fc7a18c48515870c1a1ae88b4158b41b5244f89dd9b5159237a8123be905a36e8b40e5a1705ce35fb74df9f8919e1132b2fcef2458f05171082a0327818affad2fa20d0bce082b0305728a008c03a5990a1ba9b6bcea0792889c1d62c2890f4042330bc71a81c19be930b9faae1356287928f2351867c811ec40c82fcfe44ab8e5f94901edab3889cdeea6c9aeecfd5506af9e849d2f79cc765952fd6cbe79d9588ae3bb33b1ad6df653e0e9086d6a3dcb84fb6f2dc9a8637996684518f43e2672995b6424c0936a95b3e3dde2669353de7a2d140923018a452efc8e2f4c1fb4b6bfbea0150c701788c07600cc1442f73586dda14054a97bc2fa5b881d9014d8aa4971fa487da281e617020c0b68d4e2c48bb381965b63c5e0a695d5c58d0671cc496e430c787104e6fba2fe6f29d34f3b1fde236579f52ff44ce57f7149d8bdb7ed3f7870428ada5055c921dbb3b466adb907ec6a1b6a990aab0d7ac04e6e6ce6df49eabdaf7ca43b7b1913ca94e8dd1b9401205aa90c4b36822b391329320c9539478c1e071d77e8d011071c3becd8a10e1c39e668393038d7adef08be63c71d39748443c71d3be8808372e0d6a1fc3bccf22a388987f307072ede4a5f8e4df8e2cf5ee527baad4a855f9b59b98f7a82d4cf1756ae2955f16756096d056c75e1f012c1cb93e7d6e271044171d4e2a96d09c04545d23084a3152c267480532930ccaeac78766da09b0aa9159733ed4c092e7bc4e6c2054a1d9f2aca981eee39afa134596875af74774bb8d4ce720b594e5a73d6ac07eeca4a08adcd9a61915b49bb949519663cf0165b85b26ad61963ccd6d451d69d31cb8cbbb1325a2bb366bae456d22e656586190fbcc556a1ac9a75c618b33575947567cc32e36eac8cd6caac992eb95525a83c1a7048b33a1bcc50f975c53ea2710484f4fac26b5b69d5943d2a3f105ecc467fb9e913c3669e39254a7be09664257a951e954720ca3c00de277ae9b976411645e567df59ff5acd045eda356aacd8969bedb64b6c9a756dc5b4ee968bb56cd1ac552bd6fa719451d959f12542f0dae8d7a73b4ae59b87b22701e66a075f90242c2ec653ae791a9d224235fa72ca738273ee7261339b12c2afd6d63092bc9806aee44e00356d86b3772f6569b2e05219310f0872115a38928b10d4dae0e17ef053494dc4c5886c94901ec8213a50ba90a0b39afb1aca722edb1c801b9daa513fce47ab708f90c2aed1380f51b362a19c408cef9e9874b45376036ec1a2f11288c07ebde8948b57a4d2de8c5c019e18db290e1cd28e50123eb0fc2364b962b8745071da645ccc002b69a070c48e080d49601f1cefeff5fac933fcef1073cbb7b33b9a77ed0a83d1eaa8b6316622d4bde0cb1f03153adb0b1f4cf6cfc55d3e9c019112e55b38ac7e79ad21a95f930bcb651c0bbb24528c474aeb49eb00b2ec22fa981d676968a07b14c55cb54728d31e6d130142b838df9ffe0773354ea5816e40c96f5bddb6d0eacab6daa465ec3e911d378ef8d33a746ba2439badbf72dc0dfd020e980ef23496df2a0345b899921f73fe8cd7604be6751d89ece9a2f97bc57929c985633c7c3203f1b5fa81a056f8b952d0cfa80fb73c93a50e626335b61c70c40fc813ac5c1b1b8e68552e05e2e974b36c32add18447b2d2e177b1d14f05f3ed31042e8eb8dd186027b4ac3e2e4edf0d7bb2b2b84f45a5b51d9d1b0f0511bdecb33a88fca40ccf9bd601d2772319555e43858ae9794786a41c85f1ace26db60e4a73da45eca6d70ec50188855603818ee005100c3cef92308b8db5b70f11eb1e0f0ca0dc758992a783c8119affe8b87871040649c5902be2e2e4c860ff47b3f0e1e63bd38d9c40720139bcde1ded593f2bff48333edd8895abc437d7825be87b1976c0d15a813d4f22055293035db3e895038c1c31e143361404714eb59e27aa7813d3d2085f61e18c174c2260689f317ab3d912e5f0dda6e9bbd8985d3401a4059e4e5ba0f496c36008773ef3e4d3e2d298947882372b6b5cba7ee2a2709ee1b1a3672fb4a5a1bb124d4240fc64afbaaca3f0365acc1f8b03ffb723ddf4431bf3e7430b97092c2f7dc31876c101f114c3abc90f4d2f6252ce98149ecc5ed8acd140defa71dd636ca1455be13cb5e9cababc4def79878e231e33ff1d7fa33cdf18befab12f3191839a49dc568e4f3883ebf7b90d9b0b1d63a8b95c5b5b20afb6fa3b0cec9e160773cad9030412343bfb00b5ef33206a03e5d03b661e7ed88613431b2040eed0aefc126997e7336ee124fcdc86cd858e31d45caeadd590575bfd5d0b764fcb8839e56c0c02099a9dcdc16e83f6c40b35061946a6290149643b4578388b4ecfcba63701696bd947ea762ee6861bde1a765ad8f5272463887f756cb8b3e67dd97a5715df29a0e0c5d339a223f3d9d26af0b3b6bdfc5cf5be095eba5a84dd27928140ca026cfdb1065040138038fcc890b0cfb159dff8d76385cef2e880b7091d06da2810da3b9d68e30f4be18b8deb236b5d16918c458422d83c68bd878e9410a2fe3a4a76ca4709e7c62febdd8608748864c5e6a8c87f49e25193061881e7701527d99d4083633f1749b4a6d86baa84ce8bb79a48577b58b8f866b283c566230a3840b26a7150ace4181b8320e06c07bfc1b181cc8439aa33ab0cb9a890238d60a96bab9e0a1d157a152a09f17e4ea331a96ed1e031046450025809b16a26ec3c780a4eb9485aee49549e7a9d2bc505cd06de08b3dd0ee7d9a474bbead9791b69b68b64d0a23dde3b17d9a16bacf56ba96d857b561310c53ac43dae331958f2b2476ba0e0428ce6f3417c19b8258a512175b58fe9c75f79909bbd4ecbf0033479461f63dd19fe943dc395a0d6e5ca99c2b98f5ce3ce4f9f3a60da3ac0696bc84f04634c159501c5515f4eb2b7ec8b0e3091a739a0b8259673959de6cced29d11c561575e34588dba84c7a76665f90decdbc1f9d8d841705ff4a688e0d231ad4ecd6a1e8466744b1b4240a1c2f1fc4cb86e19a0ee2943d448a0a297ea7b27a9e96da16edb608246a4d5d3c1fc8a7a5349aa46163423eb0e15fa8554454d60846a3cf767a340fcd2f0b35b456c44282894150461685fea1cef0ea0f316fdadb6373b7e882e835b7025a236544b84e753d6a8c1abf1a60940dbf970be16f8cf7b5a67139d8adec08bf3dc1d4afdcce654b21a3ddc8fc4a9741cf9c29322507b176991554ef53a71ba7c6068f1acad74da87a6a47856174631ed7e5a20bb122055ba38eaf43fd69856bbe98d2fba8cb8cf45e649a08c3f767101cc014c46aceb07561c4c04a67a4017995aba4c3790325af2b52cddb4fd990aed8464981ccbe9c4483ffc9f121c579047ae82c9282ab67f83394ca0b2b013c5d010af4dc355770ce61750acc7240ae50e5c6eed0e64552951712ddec19e28f9907ee30612477136af386f61118d7dfb570de7c4c681f1a17cbcbe442e5640ae7627934cce10ecae6a60ee50db66f3adb8ae077af16e826774a726fff1d310353034603bbd91abffc38f9013491ad3faddd27ebb42e0a815ab25104e6c8c69b1841e3436badaf84d45b4a84c494f20daea8dca3b05a62389b43522d3ea7e8c72edbeceeee454bf596369d4f280845d13751c939e77c216eb5eb2cdcbae49c732e06d55b3a241a8fc8f7504cfeeb160114632a525e623a322358a5206f51e99218d7dbbfd8b1ddb54dab893df7fe5ddbb45a4b654282836539f671ec9680792cbb1a42d13ccf6ac2076868beef8e919022393c2e4f366b46f1e60d14026e7c26b97d77f7a757bda5c121d108e2e9f84074adebb60994b4ee09aead75dbaf623173aa4ac22c2feb68df5f4d0a526fa9101152fe208959b2b4c64d388e3c5e3d40215b8df242415b3846476f06a7d06050573713ac8cdad0ca2006f3ffbf4b918434501865e6bddb3e0b93c403bb9510dd21c32b47a7a9882964f4a5e4a4fa78d7366def7ffd9b2ddf61fcd4bebfceffff1fa0cd9cdb2521c396bd01f5c13313beaa6214a198f8ca5131817ccfeeee6e347c71eebcb603b1a123693161a59c734671aab7f409fd0f28d1dc1bfc0f29c3e4c7bc20cbf250396041e15ddbb4bd8bf07b767777a42c249c7b4fe7eebe33a1213a24472867e3c50b628e0b0a510dbe6727e672391c77777777f7202e45a2f1c4b907714076e7f9406f79efd96b3877770722aab7d4a839001db5f6271c9c3bace52ac6d58f7a25963688745f6cd88d301d399ad1e5ee633f50398ee4cca880445afa1191437d91c15ddbb4bd995eb6e9459a59bbd62517311185a42a2c9f2a1f8f9755c60739b2e433f4734b5c9beb2ae97a9a58aca666347d718506a31f2490e5a91f8447166fd9658097845112381fedd004dd4d582da26b83dd12d7e6ba0e5cd15a6b2049f5962a974de7f30408d42b70cf2f7067edc0427304deb54ddbfb573282918f530b41bc1c434b30a6b2dcfcfeffffcc1d1edc7fd631710861884168bdce05efdaa6edbdc232a237e3e07b76b5c5dddddddd3d884b959ac4e02f75bab2345592daad2e40b28ebc5ddbb49deeffbc5c89b95c4e664d9734904b66d48b5fce90231f4515d8e8124b2fbb62964400843a939503ab36b304cd8a22b49195861d58402c79027ee1220691e97892e1f3b58b8699392504b24d5b62ef481ccdf07bee6469ebdaa22accc2ad8fdd1a6e11b78c5bc72de456d24ddc8133eeee24c434ce4ee3c01a27d6383425232e30d6955b3ac4c6c7552583a9b5d33484daf9704319c673e7fdb825aecd759943ddcb4fadb5f6edd45b0ac734451e10bea09c73ce334cf5964a5165759dcc889dd0ec5ae5114f1844e697ecc17a1159660c3073d0d3c00a9c94183c3830ef4e366ab1b5c23291c31eafdfb3bbbb3ff5732765815dd05299624083d05411244c4d5490cce869986293dfffffff6f2cc5b890915b41d972c29b8246997fe55aa429168b3cc5a0a25091a88854646a560928eb55aa9135dbd1b0f1003272411f71244e73ec4312791ec1c4258466bc51878ff7eceeee4c285e9c7bf03dbbbbbbd18c13e7ce33519fef7a4bdb15b7c4b5b9ee9531c32c1c81abec98e80549015271e532637be0947307823005757757712084ef7d72090af311c3062a2689e297fdff3fb9ded20c9c7573ce43e0213bdffb493e7c21592e930b38764ca86b6b63e085e6d18326b2851d764b5c9beb12d9620f98bec1f596b659a78265310192e20aa9eae7ad1a3e65bc8899c96514a2ac788ebef0aa6924516e4c315b286dc9744f392ec664f411926819ffabbef5c4c9b80e69261ed30a0cd06ddf3f5725d55baa5c660e554dd7b9e00c9390fd40babbf3dc6cb5536f291ceb132b1aad81447cc35313fefff7b9998318d0dcfef37c81102dc6863a44468071d9d440aeb04a61ec040f32c566e728c4696cd8d5530ca291330386870ab6e20945e5e55f07f724393c1e8ec35ff332a5ab088bd34d915a109d56bf3361c38eac6b6bc788ebbfecdf2d716d25399bb43d77f7a41a1a9c3b2cab81758ddc32153b7ef944249d6822a1313d323306486fa6f4815f1519083da22dd39d791585553c4392a8de5263e6903cd60280ea322cd3911bea04800a0bf2518304588f1d3771115186020d4ec0e44a70d960690153ae40116080686c5ac134d9fc40be2c19309bd1110ad205f463e1844b8b1b0818f05a19723c423da990fd789249326aad354b53bda5ce27d4034b34e701f8aeed0666d2bdf5e9a6640a366af6fbffefad37c6a06cd8cd1ab6407ae4e55cf9cdb8d0ca11c118cd84181ab934c1a69c463f6c58cb2b513d97ee3f7548f4dd5f49885c386106754b9cedf45a1ea9c771a8b523f366b311ddc731eb04a3f9822175aa01cf02459c197ebb756678d7366d6fd7efd95b9d5b92ea2d55b61d5a96bb16c248840550aa0b860dd205fe2970fe42585860435081e637b10e25730f28c011fc6cd7a312523e7777f7b8c697c117ad91c5c1f596b62f3976b76b9bb61d8ed16affff4f51aab774d9747e90f2f4921b37d3188b6ac75a66c2ac20502df4cceccc3499c68c18728c6e896b735d1d31b3fcffbf2a12a45e51ad183b6ac4dc58a135c3bc514fa6920afc98a4bcb4442075df33d5e399a6ac66b4d65af7bcde13f49ea1f710bdc7e83d47ef417a4f32c984738f73ebeefe434abda554595d1d7e88ed3a54c15fc10a6cddf663fdcb56eb0bf4c05ddbb4bd31ac00b0ccf994002061c7468526a08f538c98b8c5c56d75feff0fa2516f699127731014e4debc0ea9e248584700f4065207e44a41e8e05ddbb4bde1155c7777e5abded2e0507fa0246a1d858348486444c72b16d1b2820aa8a753cc8a698d94a8962bbbb6697bc7231d4c0c77aa663a1440129dd24e13834ec693526f29555617089e18887717ab42761d08817db4708a95814b24b0495334e9da7819feb9b2a45c87a477fb1e4e617f20dd3cd98e3094268aac64ec70295dd860b1462c60a5bb3b8f8010402eaa601848979ca1e342e28576f4d3f56c555fee9085d46dfea924274bc6ffff1ed3d38e16ce3de21bbbdefbff07aecc2fa432e92b6c967da0146559d98b09d22763cf2f6719e6d3028d4c0c7725aa9bb66b9b5693edfd2b091203014530203a3e2b462ec213575f31173cd08d207aabb0686c5878666830069deeeeb323142b091316954284104aa68965450015cdf32034eeee4094989669c8e9be6bdf1febc50a6ea74eb3ac36dca25f1a172e71906a5e1362553f532b869e47096a6473584efcff2fd930e2fe338fe5a9649885d906a7d74bc3221ca524c1403183a973f4a29725fbffb96877241ca872ea0f1ff9c99261bd8481129f2d0f832e25a70b9c17e371a5015de6b480493acbd199cbe55ed4232e352221750c03ba25aecd75571ebc382ebe50311383d34c8704616c0af0a0e9d803babb2fed1071eebed4a38cdbf7d7f9ff7fb9ded256657bff40adb536a2516f6991274808841191bb3bef3dbbbb7b8ee77a4b5bf6cbab60a092fb3c1c5945474bae1b37b61e05ebdc1ded162c2a63d3966dd7a089caa45c6f01adbb7b902cf596767d0822b683e30c7e84e4eb5d5b12a77b5cdfa3891584172873f88efc766dd3f6ce3f150709a2a88c651485081d9c98c705ef02f72fc40264bde00b3c7ce01da11a3122d6236b66246548887ae755ed150b295750e7b925aecd7575bcff7f9f32c33107e36628e377241bf13462e7844c0ac2c5b8c2ed8861924437a42899c2ffff41f70e46a83becb2cb215459c3eeb8c6cc96505fce395329d55bba6c3a9fd00da8a239126954acec11ab2073a28627a6451126299e8f4ccc8acc8e64d218f62ca2a43453d48f180e50345b9ed25b2533072be5762e38c32939e79c6984d45b4a84c494a2cada40d38562b66b9bb6b78f75e69fce9945a2e085988ddde9ce5b89ab556ec5d7dbb54ddb7b85317410b582cc9325684515c49a31a26cd95d31c3da646d7a01bbb65d5e6979b1c97b4871746df091c8c26e896b737d85b58e987a4b77704c53f413238fbb5e79afd65ab740546fa9f1884c9eb4a074770756d07ece39db20a9b794294595d505828dd8575044f8ffaf9273ce5f39f596bec121d178f4f08564c36e9e96d4647c75199e81f550656042dac84e988830861eeba64caf67be20d9a35733f6a3455cabd81234bbb6697b8f2d6742d7bfde0ebe829bc54ce8738189e853b3719622f524d51d0a115b2a502d18797e85d602ebad0fa452503174f0f8111183215b412648cc1fb690d1e77f001b2e7d252f7cceaba98feeeebe96242aa6ded21ddc012cceee6e8d3578f0d414ba2619a880089d1c3b7ec2c246a4d0a79b212c95f79edddd1deb897307aaaf6b6389818ac26f23e9aea991109adc340d64793430674eef6c802e6bccffbf12972a091571ff4a9c056a9bb36fd7366d6f1a361e324052d21328b97b81ba7e5804edc080250cd103244aa80495a36a840a6631035385d09d64ace4ae6351d82e8851c70fa8488015b7c4b5b9ee53f0c52d716d6ed4f94e09470c39edfc8f58002f5b6eba444cc6bbb6697b67e07b76777727a025cebdc689c84aa70ed912aa3ac4a54ba1e7cf4ce5bde02a0b6c05a99899a13fc2c8604442a849e83e29022c5feecacbab97e37d6c6624651b881cddf3d1901808804a1e42b2b0cc6617785cbf614909b7bef7ecad9635b5866bd820789221634a450c142c1b303f6a52b462ffff19dec761e2d803b61d8ca2be357d6a0d7b5390f2a8342d9e46c5009d00331a041c41c3401cca12b9a6031480070d8890bc7888d02c1286c6c180480c0086024100180c0602018140202c8a817096ca62b805e646dbf3bd2e64f64fe4f004eb23ff0e699e3247d24b954b5088a40ecaa685d8258c2a3eb4ecfe165df5168aaf83b3641af522ff11f7da40c0a2cca02c46d2efcdcc6a1175a1daa9d70e2a750166303407f0b645b3fc83a82b1e4899184fb508d9357cefb368f6496662150a6f7e785018b411225e0e15f5890e2d8d344d1149d7f438da024305fadde9c440f6f31737f3568755bb2fe44eb98682b9ed86ba66f98ab4db8a862750ed262b4417862278d552ef5a1ee7874ac9ff53c3143d4951a8845e4981874b338fdb6644167f70da9070c9695afbc2886b3fb9fd8c545bb6e2da3c96ac914c53d3127a343d6fafc05c2dd69c4ce918a410957a367bd3c9241d7c1218cba1ccb4637b47115544336634360960ff0568b26be93a44a1a2d9f59b24d4d66695674084fdbd875a244b6e018a06c2095e2dd4ab8dc170c6fe37ba6bdbdbc1e8b152458909ab6d0d8a7ecbca1261aef5c8f04b1c3068a2b35b45a74db7b089e3dd78d2b48c6259262ccc5d1dad46da316394643eedd95a6410cd1e4fcd54fca74e1f6da5b8bb3250970d8d5cf4a4a195d4643556a2188b7bfd5559311193eef6871797c02bc2de8bc8834292b0731e924411129b9ca537a6ceae2e270dec2c56a22b96e463f562dd90156539a401cfda7ebb33e130c2b32ef4653f2413e0940f5b53b2c237b31677e5fd1a5ba367da9a8f52f3ba211cd0a86acd14aa4534574928ada454c5c91726a7a4cca5c9e68a194a25bdd2e1ff64f5ce58c21b4eaffe332f7dd0630929ead5abf96f88b98f4712bb4d25861070fa396f6c3f228aa769d1bef21e9e5f3ddee813d442ae61f6e8103da1331e1fa9e78d1736369bc1bb1251fb2176cbdf28b32e55ade7e471266c81a4dd06282c44d1aff58c1627dfb68302d90a51f44d311893b313a8307cd6fb1475788acc3457c7b4fd399f0880683d5c650b11cd9d249aff68132935cc4ed966f320996adad48ae4546194780947df9a4e180080b6741118468028346c2867ff5c8cb30a14e029c09e75d282ea573c268928293a1f81872649162790292cfc5167a458b89fc6f0fd6686b2a058e8390076939b60b212624324df61daca31cfee89ab31cb4570f2e01b2296c28f8a721385c653b853610477395dfa84c8bd4a47210579b988cb39a3a2c755a18cc1bd73f346275a053857a265e82b441021e8be4a6224d4d03359ca241f3d7c14887a47646ca2c411139ff02fd8d3318ad02125e321b6237dee5811d423c8a9a8510786e2f6c731a10a01da3d9a5796898a7ca49222908098242196da2adf276c85713d91c840af479be33dd44a5eb0e00c6ea0f66ace750ee1db88d1ed6f050d177a8d4d6c6088a44ce2b2a490949a94b4b2a527de01514f2f3a3301a46b8f2609de770961ca6bc649fd8aa25cdac6883f60893904bfeddb91fe918bdfef655f3c8e517069c457ef5e66a650f89c7757ddf4bd252eb8bdd1c0613bdade3b21caf239f0030c93d7aa99a04305b4a17589908f2b2fac28b7981b3ae9e762258d3f881e1aec599b58884cf5cc63bdb190a6736247b98d5cb9fcd2610614e180a3298dde507fbebd8deb27dc403ec12cd41cf3fd70695f9192f71dfbf654235c6e90353490315fd6c832bde9802d0853ba985356e9db8af0566a27858c95138b2db3035d7c7a3a75dba3b647d4c995ce53d2c5b4d91dc8ada94cac2660ae2d76e22ba90317ed99b94c64fd6e32287beb800bfca12f1d2a2218ad7d1db5ca5d3b15a28269a39f7cad6b19a4b66f7e239af7f89607a8a796e0afd971daf7767d82898e207218b3bc828092d7b227d770933e0ce683636435391513bab68dad1389cd709cfd3c40e78d02454f3c51050c97d335cb8a9278364abbbcaba81e076ad8261218b934eccc8c43dff240fab4aabaa8d5c6efdba7c461f7be65bb39cdccadab8549a5b52571d0cb81fd05257f52bc54670ca95a3f890155712e5ed711a3a30a6ddb7ff721dc7d60c33917ccde9916c22a0b4b04d0af2cf4d33c4e6e10b9e3dce44cc35e553ab56df458700e79afb0220d95858da2acac25d9fec14943b4a5a1fff0bfb26a4dd03fd1d0b18b36fe593955a56e6c0b044fb29e7ae1b62415fe0edb3738f03ddadf16285f195300a2f2ee76deb793c71105df2539ba939f5340d31ede2c94a4d025862142884f01f8b598b61adddd5408ab29283531086f384921340b5dd1eb61b37b68394340723ded2dae6516f96f14f7a417ab1907ef8f3d8d002807e59119c18490349de164614cf2a4d018c482df8a54fadff007d1eb27bd59e53fb0f0af55089995afded158869ab33264e0d9ec7bf7a2abfb5b2d0686615895ea0b03f339048da25177b97eb715f87d1b76e542097067eb609b821735cd01863e577b6046e6f6ab46eeb5e81bd192f6d9586dbe510289c53f0e68887b0360b42f22b057fc969c9d4526900a413ef5d1d03f08bfb46d36078b080c9db4de95ca92f557d53820708327b4cadc8b2892f67e0abd2f158e8c14f38877a6bc6b9c4f3276af85a56859bc1a55a67fd2bec14f8b438d341d5082006a530ba240c0df3f9c5422ddddca3933d61c22138dde9184ceb36a9cbf6fb9b5fef5e3ad28aea8c716ac000560b91113db931cc3057892624067762b3a6a9b0941a5a9c4c7ef8dc2f56ac24df912b4161ccd44b5ed0e896a88f89219c826ce8f87bde318ea29d098ca286bce340d0a3ea2b03a4dc786543814d0d7e91bbd61729a3651746d1fa26c0c61c4f34605d342b5c3ac9f66ac3c78bad3d11a88b7ddb2cd19c17b8dce701d600e729e31978ed283e17bd13975471a3cc41380f61b06e6398572c969e0a97c8f0e0e1ffc1e75de3e8ad321dc86edf9aa0ae222941c5ad99472e7b497fce170223b142e1d9d710f84883ec8f038cf50403ac8bfa5be213df902c8c309038fd2b8b108378a2f35bfbd9111e646f33569271d6b03fb15ccd2e646434a6bd7d12267fb3cff5d27d2e98334a71d985bcbabddebd1423a96cd10e7887b26343f81de4cabc0c072afa62e8c7cb05c6aeed6bd1769bf89179a93f1a6cc33976e5b49095f86a79dc57f7cd358d583071d03968b122cb8d09a05fc47adec2af778011f7c542ec21fc2ac4299c316b9b112b064e15331e6cac305901a167eefdd1defcaf6816b685ba73f8b3cbecee1f82bdd2b89654323073de92f0279361ddc40b851462f2bef68bf7c6c788e0b05565c089e13c681f12679ab7ef9ba9ff896d673ef47529ac26095eec0fe4123407ab6faa661484aebaa842b7201eb6dfb528c06d760a637fbae62f1d3bc14d90c54b814df5806b6921652d24ab8e6143eada637b0cd72fa85511925f5d25b5019bc983960a35c72aef5d1235a162eea205db3afa039ad0d2e604bf6e5f0aa5b5d8b602d54635bd41d408a472636a64855314e8282d9b4bbf573350423b6d42693728633f2df5fdea4fc962764d5f80192fe48a89bfd148273cb239add4821e82f9643abd542ec7632e379c8cea7bebd05e38fb9e072b7da4c52bf436734d31fba1ec750e77db919b597124bdfa5214b2c8ecdf37ee72fc8b79da76f87306ec947a989a549238b1e6af86b9577b39aa4d7f14d3acf0d74b922082611b0eba19c2f31d3580b2505c2c15283ef06b9132264df40f716c2c541561bac5bf141f24d724920545cceede2df3f42b39d5aa37946d4b5ed98f05e1fb0e30bee5a504e93f4498f0e111d342b496ad2f4e1f4ff849c982949182dbad8a5fa4c9801369e0f9fc5680685ac05030ad4c4241faf9eb0a0e17b0b25f92004ccf3f3f5ac6bc5d61ab9e36c80a5b3397a18d538253934ee2c38bc13353534c1106bfafb9dd6d6cb96c74f050444df427c6c0ae8fd0b2952172f5f981c4cd6d23cbd4789c704e0d6f09bc12e080065c7516174ac9d4a35b5035767f959234c16fa345d631204cd16fc04323b557bc95c8ccc5953e3abab2b23c449f7f2660da91a9d322a49ce4fb65751590f6623eef1398865d64680c9156e7910a949b7f4048c7010441292de7129ce5036b030541da3d701c3fce1c711ef2e44b0e8046bca4094dea630ed38264db66adaafdbbaa81a47cc44616ce544e1419ac089e82c1be936dd76769eece46f515713c1f02649320b810a4062230def881095b3f4937fe3b0e8c6dbe3ca7818253db8ed10c6d2eedaeeb1e1ab8f3be1ff8f77209a1a6e5c21860d572e8f0300173fc6c2a2347e456ba045efc66b1abf12b2d76760aba0ec363e19c1a04e44733a3ea7bc8ad605d8e9725d0d49fc0c25d494e0344c50b616f398b342edb8a1894229c75ecd67eb20a2d7687cae4c330d13435ee91c21c74aa81054cfd8170ac3306e3fb097327717c6a0cbdc5de3d921fd7ef7a56c2e839d01a4b883fe558446cf79df45115ae624eb16c1960813dd326f3cbf8594aa969d497a53f2030e1964eae09908068ffb047e7ffa76a9d4564c424e87b4d7e412f2dad17699342f204022683428ec9295c3ca27e0ddb0c2f6e11701e9cd20969fd2f6c41a10902704f0ac8769bd1a50d1832c23d4aa888bf3c5d6bcda86761f92709fcb0c60c26f20e1ab9a0955c7a446dc5a28e9e1e371b81126b7c9588b349d0642927c3c733ce462d4b41fa86eddb5f655e5ae883ce7799b4156466c33b15f4e0f4cdd142eae599fabe121b1cd09c5fbf759726074cb015a22dc7dd33f7e1dd1ee44b9b9999cf6b7afe8b95e1331cbc12822a14a6b76411b37f65ffbff06f45ad9a19d4f7b3dc8c2ab945aed68f4d6a80bdddb98040e8fabf9ea2e33939ed1f67f68f7711d1ba6b4090d8e1ad131584f88aa1b87b0d1794ae4e9eeffae272335d065c65885e150a41a0983350131a91f648dd8ed9750e39c62b72e532def6ea17e0bffd88aaa70451cede72200c471d3a5002c5e0f6fa24180d1471824492dbdde688bfa41fb4029e138da56dfeb755a3dc949d459cbbc5a31c0aaebfade577cb36c49acf1af887f6679dee08e0e36bfa2d64938bd418cebe8c1067b011ea30f7dbd88d884d65e340493895c03c835bc0ba48e1c402839721a7e20a414691b12ef4ca1ba04fa31f71ea6a357967a29919bc7e7fe5edb61ba0eb613ccec12d7779d4cf61a6380d3dec324e84529b72deeb6bf2ae9b31323744956ab86e8d7bc0a41493c245c0ca0ad88cfea98bf6a97582645ff908de9a238ceb9c1415b68eadeb2f8ab10624f72c6559612ba7fc1323dfeb15bea4a857a0f56ec1353ced24deb1172eefe871e56575a0bc12c3d50b90bca175f125b9a3da0bf442cd46a68e06a86a547ffe5c17cfa11b977d968e4a02fbd41d07ff9e4d54d866965af5736f497b826c02f6d9d3e1e9cbd13fab1286ad5c4965502d99cf32622d97a200ac8f446cd0a477a938c0c13c9703a4dd57eecbcbc742fa1944608a93744ce7051996ee05a4239cfda769080fa6744df4ff541f1d08e784b07876778caeb692bb9e788673d9b17bf49fc5dac918d70377f1c6c587b8ba233cc12320a303b55b31d2b34a115e96a5eafc5da000152a60c3294c4817b3077df6cf4f3b60db39983e05f13e7c66e084105d287db7ba5328224be105f81cf482361c6d34632366b58484e8d25ec257da93ff08871708b6dd9ea121730964a1b8ca3e88d1063efb192853e840942130a0669a653da7d2df75522648aa574bd3a29257594a111c53b7421b60b088916063356ccf5d84fd6432eebe295c3552803800da55593b9f378fc8ffe76e2371bd072d3193c91b24adf9ec7b751630309762c578eec13a747e353128e64daa4a423933ef1e90a451973330c923cdfee5d7712d0ac9426173e69ed3e21c4b2cbc82d5fda3292b2d9a5799380030ab75cc9f823bcabdfd466e66a4f8ef611c1c7583d93470ed5c1d64217769222423cf278a34bffe5e1c513f0fcd84146f85880f415c520cab65967690184994116c5216fc10793b5c5c415896083570e317e7d7ddeb3719e333f36ca82805985976c9e4b50f355f9f194840ac79abd232f7d7d32f9a6efbedbc36b661bb782ecabbfd9ce877b745b38e77ce3e681d601763490bacf0a99d8c177aee097ed7eec233aea98077c7ac2b979e385d474805fab5aa88bcd8a964ecd4957841b07361c04408e03909bdb6cf698070725ba56209131b194cb54404694c68f6c36a071ecc559dc54e5b87c7c05264aa022c965107a3ae4cb2f0c72bd56eb113534f7590d4dcfb928264ebfa28f548becd638e42fdd4cd72429abde6d3bf20c95220a5c6a481da5ea48ed54da96b1e8577c332d98bc5a6b6d73267ca80bce40cd021d4447ae98300aa46b93c2d31a2d2246d8c45411993f7cf9215b7624817d8779a3c2db8328d10db2204bbc787500fdbc5ebc9bcd71e28038a7da15324eedd453fb6a54e921c908e3694405019695788ecca1f3b46b20bf381b859732ddd868edd1a7237ade96a05a5cf12e093438f1ef7f3b33b5cc7436835a6db765cbb14643b682b0ff9dcd4e4ef8dbf1af4097881b93880b6456dad962ab116944774b732b3f1c45a0520ab8514a0e5700cf36510c31584b061f1836b699c521ffdce1e99c0e89b5dcfea76c532f34f0f92d811440128a9bdfe03e2f4d24683d7fe8f183977ea8f2d944bf544358e0918041d55223dc07b43685381e118c6a3ba1a4fa713d1c2a4a3a97839c994f20c3013ebdddb59b65bb0011f6051f586c24eaf547bac67197cbfb47e939d5e9b7dcde3c5d4ea20b1f7c060741c76b732e4bda7ff60d96ed4821e2b971b86bba909b6939b71f8608eb662e62914c0d4b538c147a086beee2d3dafe979001300568cf821027573870356c330c6e036badeb53df3d357b97f1c7f9d7d370119e77913b12bcd79dd62c0785216292327e3a6880af7b6d7807929cd3342eba9283427020456e980d1d643f21d73522a0cb0ad4482931688409d813baeeb6ee34f654c2cf8cbe81eab8dfbae134ec60deb1b3ead5e5eecfc8cf8c804c49a988d5bffa77054d3b6fbe58bd941948de3e200e1fc316a70edbb85925d469c5e2897465d7c3e5c010646c36e09f1593e3f51735adc6bee6f9fa557169481a8695dbadd30c6f2dee99e6513c54376ede961d573c1f3cb16db9bb8185c3b908e91bb54be0225aa6d51efe24857de5891a7709bc9eee680c756461af9753bbfb475daf342e6ebcc6fb748536c58fd5b7dae3d2d8abdae2af7f8a116bdad5cb00d4a8215a349bd917ff1d450cae6d3b975f40630270d820fb983a30d602c66dad4f0c1cad0ec3e58a3315bfe59bba5018e46b956bbba8c77281aa25360bc431f499e549e8f928ba59527ac5cdcf39c07d3042e86e39d6a6ae8ef0f70a08d1fde3f7ec1bf6f66b9dad0cf8ae90428b8444168a90c0580156220318e5bf2e8b5e70e2bc6ece9cb04fa0bbfae26f607b804aa5736168509658c609c86a0cce70ae5daa08b14feab81e48c9bb3c59c1b0f62ddf690d91db364ea3286cf219e29f0f005579897d1bd9a8a1c97a41b800825c44be6c736fe3bd7047d0a2bf3124d921b7254bfb9d5993a440c0954d4c6ec53301b96e1735344e1c425a189ef90c7293dca0312772189f5c57ca07e950dd954a766d5779a191922e19966174075facb6308c5726174b9510075770f7915f3b06f658ce2a5c7b60d86ebd88574fc55cc1c02b4ac3b4320748eeea6132dca3dc6f5ac179fe71d6bec523be767cd55a9f31f6289966041c98313d957871d67fd236e24cb549ee258f7fe3171f1b09ce4b8f4d696fea15fcd087c75248bc919c76e974643797a90056c9b6398a340bacebab26a790fac76ceec942170938318fd9cc51d85817315db8580f5e34d1a00c1af6d05a5cf14e76f8e84477425ec910a3dac1e6c098c1f147c127e95b3f4bad2e01dec8c35931dce2c7bee53598b6bde7973071a0a6955d9cb9972566a80cec26f0e8ed81e7951a4efbb575ad6610a864e58422045f40d1f252564ec548b5546695e831bfa1fde8b6c5f919eeb6c8ac9e52753d0a6c678609984a35f7b19cad9d965d343a2b71f34dc84040834cc3d4e2fcce21788c995a6662684ed2a4be0e04f9acac09651a0754727b7dbd694d2a53536b13af58c89bc8f73397012d16edaf370a3bdcf4a5328e898a1626611c9f10af07b53c1deda04033e1b81101daadf8ff6df92ee534e5c8c3bbaa20b44253fabea55b28d22d2391346911816d0d8cc248a2140fbfcd8f2d0fe273d0e462acfff6244408d2c5b54e63afe35b07bf388cf2d7daa7fdc18785549b578d659486d0fb1a18d1809526af40485a1a19d2139346285d54edaf80860de4d4ccaf17529a48a69c4e4122edc0994dcfb1a90fc88ee511332d5337d4508933a67a95d0dd4a23250ba18c0c1235783de853b9011daf25daaafb7eb80b34e1f5a26cc4d3512b453fa11825a5bebc5a20d0910fe038ad381bcfdd14243566610fb1b5a3352cfa6b5be72c1704e45c9c956ee598be33456ae20458f125abc95b84b80240f89842a3138b5ccc9822bf3179799844872390fb597743cd1aa9d2b44d4759fd48e2d162ed7fb41c0bc62122f892364cd6b72927fc6061d4cf26958e1ce168a1919b6d894173dc8d85412268b0d379eba4528ce085e30955786f12d57962fe451c2d523d51cb9baca878322bb7e297b4bd87e43b7f6801cc12245a7bb8d7cfd9ac8ccc44795035eed38ad0ed080afb4c3c5485a52ee4f5e620ba11240addda1868e97c3bdcd8a83282330f2597b97030d425ff6092396afb002591b82fd07892c47c68bf3f62e4081783b7a4d67486693a735b34f2eee3b1746714ed4125aceacc82d19c9b343f54535b23619509c2cd0420c1cb025183376bac82afa30eea09ab0c9ed51a644b4ae5e5a863a63f7654adb046b295240b0e88616e53cf40aa602bb033d908a5444e5a8b78adc19930591c01946db369fc3f6850c874119197ed6d45624fb2a56890498be906d1ce405ab12ff4ac82ce1e23002c7a366128a2f758c59bb3d6bd306e7f4e8efbbd3d244b555c0a5192564390d69a9711fafb7b379ee72889f1f74cf58806bdfde655bc93d6b07831bf75cb4c71033198f2e8058f188a297a486a32835104968cf14f3d755d9bdecec0f94035c8679618a245f22d81a0461ad0e024054dbe3016f1fcf2a879ac891fd5106608ffbdcf3d11f45ccee091dda2cb13f2aa44f20f1ab83914b7b0aa2ba97b88d928a4b8cc0290f25af77e643422aae968415e4bce91a313487745c8d3c0505cef3be10c1f036a336c9eecdb3d4a6cc3d0e51cc8d0cd274f0e7242fe5f3139b383ff3f9515d9c97c000128f1522679070c725ee9fc00736a3c7441915bbe9c3c6ea7bc38e42b1d2d6f17717519c1b81961ffe69773e700549e494b90ff1e010340a9d813f2eb6df8887acda12fb90000e0e505db744670a3208f4c0e7e920f7ce35834657f4ab8c0184cab5bf9f0232a88ac0051e5798174988806eeea46858b5ed68cae539ea970199c98b3207671c4c270452761cfdc2c7119139629d19215776be4531642195195a6abfe50b50abe36bd26ac4e9b72536890598f4ccea5b5bb026c21ba3eef3f14875f10b37d783f7120bfae425dce01f2323a04515a6c9ee957f91dea4811d11b7f188ff5a272d89bc507bc2eee213e03917cd2c8c8fc27cf6ac5fc6c89dd8b629ece1f6a754feb15d595bf71bd6efc3947040427ae02fdada3d6579900ad5d668c16804ea9309ec56a54bd70502cd7a8a787833923604a3565d7b5fe2ff4e17e53cae04ec8f7a783b4fff7744826a42f38d87fc31c88239e828ea71d96cec115c0fd62a611daf325c5b310ee38c6d306358bfd6c1c6943b37aa6463083c843acf5a56c29e8ad8a2b92972d33d2ad507858986e49f9d4b578d2d02bfd5bdf320bb32fca19adcfdf3a1ceb49f3b1066b5af0165e8d449fe64a3e206270fbea7495ee99a4abc3eed2d591e4067a1a5a47655eadf13388bdf7305abf9385585130b1136a61b853b6e18118905c18629862ef04ce1f9139aeec31c2b763aacea6371067789a8f58c305fef29f4f6f8cdc789e5d7755120169e858363d8ea82ed17da2381a03ba9a4d94a8ec3bad452f278bfffb40a2879205aef144deaaebe4bcf2ca13d15911cc682f0a0cbe43c023f742dad5ea478ab83d98aeb6ec983b57c0011f22db7a8cb99f2782e7d055ec5ee8288f62d240627c95521277bfe3d2e121ceb296baed42a2fbc8819e343492a39730a110977e7d1d8054f6fad40ed6dac8c31eddda1205b087243335b323b969591237811282f7e83e6639672f9ad9fc553c37d93d946de542851b058b3020664141533e76557d75ca8fc5b0ef810ec596079e81d1460f3091bbda7a7c1efa3aed6003185ca144617f87e3a7ede9f743e6e977786f6fe53b4fea9a3619261d783f946a13c3fb9114856aa06f18b8c3be73ed7e2866370ab06f7979a58c16f7af20ed76d2d6f91b0af738cc5264d6f2c6a786aafc1c01d598bb8fa84f6a7e454d0348b8bcd41f3ae1f7b028337faf664209272fc7ec0611a8172dcc269acd9f607e26acfa24ff16d8e772dfda4283a63a73a372ef55226ad58d19144a526ecbc8419646485e042106076b482d87c9648963d2368ade1b834caa97552b417043cb900136df1c242e3f7eb56d79ebaf56bdeb6e235090f971905b7f2e182548929e025697e2edb295f3bc57b63a16f9efa707c277dccbe9ba94087596c46f533dbbd05ce3a6d605f37f1d3358776ffa50d7228d4a5237d2ed02d77a834fcfa7fc44c1102aad4e244a6cf26724aeb15dda5981cf8177623f249b8b763ba4edad621d119c4b017ada842f58708e25a503f6a3b75858eda21f429f7aa0d80f9444ec0bf5ab0cac03d0c682caa3257748598db2963fb767ee8393d7c5f631bbd4b858b236b4ce4599c576b9d8801cc9dff2af3ee5d67be05e4a5137c263b04d64f6278490f802c202f70230305157835e2d6174702ea87ecc213b267e7070ca091e77e6726d5951d6937555e6f6ff3bb8c59c9f73ce990712a59949a5071e4b9b07a42e674ae95cb0544f6722bdc59c1e8e471d4986c04cced04526e48825672ccb4430a78fffff4d2ebe953f97b541d6065a98a7aaaaaaaaed8ffaaaae1106d0e6eb05aa295befc88f891210a6745865536c5cac54244229692549abb2964c2477944a301972b960cdb411c2216c8a563645f5c99868533389a46555d814b635eae3ff3f972e83e0eaad3f9a6be0040fae9a96872786232aa3a3af1ac618bfb428cd7c717a8084bad60e4d702bca7ab2ae555b582d0d8d20451897f91ad2605eee91f9d329019cf1d8da8367b0bbbb3dd1bec88a61e179c191fa6c01ecf88211c312e300ab0cc74156db5b51d693751dfbd1fdf021d5c2939c2140d61b74621372a222a25122e1c2ebe3c6a6a6f6bbbbbb3ba010629cfea829943526f115326b5dd30865d5b2a2ac27ebfab076002a143a70eb122f898b369b6dc5995c318b5043c9169d5bd489546fac0a9bc2b2972003ab13461ea28c32ae13c8140d63580153dddd352bca7ab2aeed29b5b505c4465c3f13292ea09e8fe3865b7777d48aa42e2b63570dae271ab8da08a9c4058e4b77b7cb6229182d3696dc9278dceec32e5ab66c5ddbffbb2af84ed830fd9284c0d29a5e52841d2fe5ad47940f175dd45392f4c678ac7954ddeef64c8110774337f6ffb18e28cd44263d60298b584b9c333fb843b537e79d4bce39b6d6387d0504f3ce30012e3039f4aa0ce994450301de8ab29eac2b0f2de608012128bbaaa1a94645950c4de4d443b49112e0032a9d4bb76b8ac061bb9935c3929715653d5957fc12d6c7ffff9e77e57fa744f0007d88ab592308930ddf970d163234323864f87f14c39c7d5a5c162e276fd99aa41bcc38b6c0a2cd669b494da72041e0180faebbdb4ddf0c052676400b30785cb07462109da8ce14c12dafeeee0f3d1dc61b5a93b6ff33862494663e19ad368861eb1f72b1f2e7bc588c802220b960ed9011290a6a1933b8009821769ab090d8de7f96ccb53bac509ab9d5e56187d82c67360d4d76dddd6d18aa4c69a6fabf10d562fbff4c4294660e3308a662ce6fc953b2f7df4ca621b6a47a6b8d6cb8f16289dd97354f0e8ec063d0e45804b3a2ac27ebca7b9b73ce594594661e91496587aca5ad8396eea58fffff3860702bbf0e1b2d8b8cd40c59fa4b793897125d379272c920998f1b215217f15694f5645d77217cb6a6b6e4a5bbbbc99029cd549f32fd60e175bfacf8733ea91411e3f0ff8150261ac4feff0dc00986d588592e07dd750c09142f70090d21239a351853b06c77f7900fa5993f6088a160568f6aae45edbe6aa4436966cff7fb6004bc0ac618639c5bbabbd95a46a1f63be3ee6e2b94666ee10e325d286ce35815368565310c4ecdb1350cb41a5be3bbb229dffd4a5ab56555d81496c5ed77523d9f6d5d328258b2c26b7ef9ffe70280e9f6ec51eeb8c4136d58fdffcbb91473d3b7b19aff1fcb86d24c5d4f032cdf8f76769e865c923bd2610fb2fb6079fbf8ff0f2534b4f2b7b8d6ddad944c69a69abee4803cb37703c09b84ec171b390caf16c22ba89996a797946606cf30ee8131c637cabe59368ead71748dd36b1c5fe3fc1a0728ecf7ee09fc727158f7d41dc7b52a97aed89d7b61cf20dd7ad2cbba36f310b6a42d8ec8e7f032e7bf55f3ff2be9509ad9f3fd3c5002fe3f6f841a5b6245239c747c332335f5cffdff3d6c28cdd4f57cbf103d805db37a8b31c61877dbf296addd880132ba8e242ec618d718a234b378442695226a2c557c9382e2c17b51067892b62bca7ab2aebc921b43160c94d01325b613b2947104caf7bb3141614609163bac5e7e3ace760ad8fbea1b3b4598c7aab0292c5b03005e77378ec40b39dc4995245b7c97430a9cab1897c5fdeeee6ea916dd4a37c61d6731631fbe106be707a29e9a33bb73e6f1ed8992b1f4f1ff9f461289e4089b9e8eb6d7474b12a511bba99c730e16519a794426952182cb1aa7deeec52da705590b8104494f46ab7bf06efd3d4f91c32c901b784f8b218e9a8c68a05c0cd13a8f3b7eb83a154c62329634f4248f43ad087684305ce398f0f471146488cd022bc8f811923b2292c3e3a846850efbff6f3184d24ce1f0438be2ff322b9ab44769113c76e1fcffc16a5cdc92654a33d5d95bb25c4f36e89eeabc227470569fab730855da6cb69a99596c912872dd66deff77e8c043aaeb5f1e6753532f898a2bc8c66206f9b1e85249810848d68b209e196228fa884ecc540437f0d2d5e9e888712372bb3efeff5f97338e8cad29221a6c36ba860d163d97a5006ecc38ba526c94f08731cf3d41f410b6d8a4694811efc61e308dfc5a862472445a384ee494d0f755c345219b825d5cd50659dc11917839a71042af8e28cd4482b84afe38ae942b3760e5baf45dced852300f0613cd6870bb181508993671ec97ec7e79250930206408955717d111e70c4eb930f2faffaa224a338f1eaa9039dc345ce668055f1ebeb12a6c0acb0a91f4b4d7dad16f7a8325af407a5891347700cdaaa189d1825a6e93156528cdc4b52f4e4f0816406d8117071a8c31c638aa09001a7528aba5458f251c48bd4444aa5154342d3ad478a4fd7f18d44be97be9eec6f86d0b3f2576ba96c8ae545c8105d970c09df8cd7a5cb09afbff9584509a5994f464eca0648577aaaaaaaa8a337bdf5a95daff1f86446966324cb9862d51c46cac984e45ac57b3402522449e488f9501820792373d2db723c1125a472d406ee648ea5815368565956adb4ef0eed4d0b8015b21c319b60454b26abfbbbb7b92509af96414d16385c2b6dd3e3b2990a2ce8ab29eac6b1490b21cd54562e66c81a7d301f3824caba8c914d21b3143c0d6d4955113d412a8e753c4cb811398b2b46010f278f05801416163fe08fe3fcf645194c143bedc691221bbe15b427edd30b6d2afd8c7477d39c77004df6e47621cb16952ca51559574238a2498d24cf56f4dafa4f9012a996b915d581536856571cbaab029ecdb4c5d6663d1c7ffff1cb61c22872e6723e4520c1d442f1518bc69acdc7835e49d36ec9e2716d030a88fffff01ef8fcecc06d23d4368853c3e4470158cbb0257e002a6c88b5b8fedc724e34176cd7a39d7c7ffffa82572e5ffdbffff3bf5509ae9fb013308a7a0caff12e0e4c379f14ceec9399b90455ecbaab029acfae69cb38f10a59943b5b81e51902c083e49959a8bae2ca6254b6e68d316f5e043974b956a50ef58153685655b75c74077edbcf08562c44c52d4901f27b058b28bc2b12a6c0acbe217dd993986b86555d814961debd66505807a594f742bca7ab29a6bdb8404d4ddad43820bb025b29b2043390da51563657adf740b57c3d5fad121ca41e4f6c562654a33d5decdff5fc48d62ce3fbefdbf4c12a5994a0f64966f115343195239bcd8755dbabbdd54f9b7c45bb0375bc21155228eea85ffffff15302436214a3387c52332790f6cca9fe4869f7ae07e1cd0bcf58aff8c4ca78fffffc5b0b295ffdd4035030fd44c6f85f24d81f5758e7530b662d724fd09d6201fe3a2635a35e6fc254df00e05feae0257316163238af8308a0103ceab86fa529aa97eb744db59dbc7ff7f61cea80957336e37398ad408eaa7e12e9733054d3e2762f070a167c609d70425263c7182f39bf5b8bcb5d4f8432cd5b5efb74f27889baf1baf28ebc9bace786b075e60415c42e38e19352a88cbb3bb73a8cb8b0b7c90a674c4909161a49b8080b93881bf9b09d9ef37c36abf33eeeefe4262115fc995ac8acc07b79393d14cfd55d653dec0dadb060e54b5fdff1fb72c0df81e9596924f3227118d12274a4b48079f8527231f1c28ca48109934f460d0c1579ffbff91921ae9a9918c8d64d5485b8dd4d548628d54968998ec54555555754bdda563099408c20092540449662868464cc786d5ddff3f0a08a5994220a28a5694af92165e50f514b946388854a72ea6c80cfa04b595ba6ef75b53fb9d7177b71630a599eadbeeee96b7cfee99931cc298d8e8b101b4aa6deb1a65a4ca5bce2df43ae59c734e12a599ca10394b9baee7fbff16530a08885b1c331bb639e79c5f21946616059020a8946ddee6c40079131a68040d0371248cd31863001400070c6a94b4787808311687c5c1a0480c0484026130180c040040a140182c0a82d068aeaad17800a9362d9b755087dcf8d252e1468564365bc5a23186ea284aae53a43daa469d13895e5178dc5b2b30f6db8c5b8ee015ec4a8f461befdb5090b46549080152a5f5c6c1719314fdc262e01b2f7cb846499bac1e5a5c8b5520cd8497002e3e611b62c8d6356057eee490bdaea48a573a2d93e28830caef4f278d631235b561e51fce0ed32674e53f5140a552cb7c0d081c6388e0e53a5effafcadb1e9bb7b2e6da0865e6d8d3fa48fdd8523785e51f71ea1a27128cee1c5d7b313a04ba1da2b55f9be41aedc5ca165f290751cf0590cd0d4fda27e79761ae35b87b26687011cf31c28ad9d213cb6344da3e3f74b2b3537c3c235bd069f7683ed20d21d4b088ed678cb5fa2a8b5490f1b7bf61818323e03ed030f223019ad1775bb4d2eb1c7b0de8168040063925f1d163b92332dc60a11e8fa243ab93c61fa4c3ddd46864a2d2308e8fc72fba05207a5cff261d3ce57fd7f153a8add024690786bf3c69bd9e960280f148bfdd347b2c4e8917a4c69317fbdbe47b65fe1897ca90aaf0343c7295c6066ae25db25a775eb1efc34f746a1c8bcbd876ca1a2cda61554b1aa040360a9311f60f9eb859eb6db7cc115a2a1a8a6941cbebcaf6f168f51d1ad31f04589a44a940b87979d0107e60ec29246a8ccc420d8fcb4e3ea5c9ad027bd880c3948e6b7159b4a8a37f4cd55e4911a95427b5d31f4d2e256e27bced380dd8d246cc8829b585e8f429a07f12dd81c90e4aef2cc53877cd856996c1478ea4231f03dbb9cde6d8de7297ec59b50b45e032049ebd53d24e41279dbfbc974d67b9938564044581bd86a8ef7d6cdc08a29870510420372809173a9fafcedcec4568d34eab4f895b5024e18176a716b453bba8d17c4aa98641e0ade052f9272f7b879a028af2df79c3a056ba73b0dcc0ab1dbfbb3ecf2a45e574a9214208b332241ef642a9b3a700c8f059c4882a07b51c53c0c4c948530ddf491f68f199e39d885c90506d27e3097393314eaf300cd5dc2bdd5d2f3e57a48999a80f83cbf9c362786b75587cc2c110b6df4957d4e698db840b57f3990871489c67d1bb9709db69cb1d53706044b9c4beb1b1adb6a704c44f90b01ba8672f30afb5ce0a199edd47bcb8e111e93344e453b4d2244f262a824b6193cf528a7639103ac8b8f2a9bf48c2343523120f50b2cd953e8adc09dcbbc14975bd51999a584ada2ca8feb61edad30207495f2dceb01a900e462e6039dc910c907c662c6d15f58cd2e8df6e596b650c42f533dd0d81d1c710855ed25db4128e89420b34545bcd5aa3e8d1c07cf9370547599d25c9fc3535068d70d49bf3178596ab1957e640d3c1c9ec30ab08cf814acf3bb05ca64ff945703c097977487372036f25de21d993cf004ee36e30c75fe07c8e237a9a01dbb35788df885ddc18fa956f48bfe02d9850367135038276ad7ac5cfdd5aeaa97facc69751b681d13c3fcf0f0452f8018b861cc8eb837bdb995d8d04681627cc7ad38588b3024e671d9ad1941ac2b52ff4a94ff9ec9119c77e26f4f16b2da84947bae7c04807e6d7896a4eb000169b8967d635e87bccbb421fdf28d726084833494e7f3dd1f6aa5243fb3ac63ed839d08a4bcdc162b3595e9d5ebf4459d602799f8bfbd5586690b47ed354eab043c80b429f3b4ca1c0e42edb66179c066fac66bde8536217815d0c77fee1a07f688191d7f3b54a0584cb0b21db23e415fe7ad635084a5e405de7c966d880a6746f69be38a57145cbc3f38f131b5b21e5bdf0498a9e2bb907d5aa73183bbd5e33089b4311958b675a65fc285b577710993c27a5231644a4e96ae90a487ee4b5f25548fd8b6d66f546e5ebf31b5b22d90630e8e9505e9479a09a0b589aeb9267946fb14fe12e4b5bf927e8662b339ca794bcebe7924a432b6b215078fc811dd261095648c9f765445061abd52791a68bd1ae7496b0272b2185476bc1989a6dba82cd1e6595ba632c3c2539e2cc4490fddc1983701b68a2790f7ff8a7b6f3504497669a34a6b5df26ac7103b1be2d273be43d06cfe8503da2511ac3b9093abf2e5a23c034eb8629158e83605bf704cea275cba29d285a1868e5d6848115adca25851956892766d702a8f8df05f238a80c2a402072804596d9116cd5da5f2fe3f834001ac6bb2609735f829a9729c75aace0d836b8065dbf5c8df4e036076bdbc0eec5596eb839aa9f283ab945c837d4e89aec18423f40e74d8cf50c31ff70db8457a528c77a3e540551713c65e776e3272224a12720a9b9e434ddd663f81bf7a6ee289d5fc5c00297698a96aba2e380fea4cb7fcbfc438db3b037c9112df4f081eff2c5bf86c1c8ca5e279d4970ce10ac5b53260a9c2fefd849d37021d84db480268086fe18e2e5b029c72a3852341224da2635397b4231b8fbd5efb58bf18e9920bae9463a4980e06a20cf274249c6005bbcfe7ce8dd18c24732cdb4ac1bf83d11b657c55bf551d7b85cc81352d7a7c53e89bb842e069f8b2c124a6af601f9b69bcef5e6740a0ce981c6c7714b376ac1ed6ca411b2609a0d4efddf50162a9a18c4213803c0ed22e26c04dce7b9032a2229a2c083b04df26a7f4c657e471792b80fb7e08dcdd5298ea718e1805344b41e197f95e6edea21c20a06b24c5f28922d3d784a3db550c565354db5df30631d0935751bcf76da87e0e9a011f121f4da51412404c9d533ea8f1b47c2a766ba97b8751079d3abc46d9e1afb396038767c27d5737cd76cfd19027c2517ace2a75d1f2d7da2387c4a568d9c5904311c9fef21808ab53b5830728ccff0c1a15829d66e8dc39a93d514b76b89e25e9d555cb043cb9d3a0e67a77c8ef637ae9e59c1be788c702cc609d62950454a5d9c578b09ceda25e6bf646822edea84c8e59816c400a69df0178f8bda50a373bfb87b442a100764cf27fa86762ddb6f1c5aa225543fb481039a155063cef8bb3ff2e592becc16f12b8f93d10ca7bd85a644beaf8ce26b7b20a3ca57a6780188e2f69f9d7eab8449aa11e447e81c35a2a2c7771cdb03c1d4f793c81db60088efa7525a972757d5d2eb1776feba731f8830be46903f5f1411464cfc60e10d96c79d2503bd6befe426afa55f50d91c298ecb41f2cc0f06dd7fb4a868b05a8a9ef77b074e8fba84274b104ce035f24e70b17b13a5dddc4b3ae4afb24ae106e84657b1ff6c49fa936154f1752f996f6ea52be089cb271a71c6f783c5997e91a0552fac57f96413a7df75af1f4f8a66abe2e4135ccbc3d8d8d6fca963f9f533e50307e01cc56d5bdf4feacc7a481144a30d3c7c55c6ab2a5c9ac38d13b0cb04ecc556f816d52db834b498546e7824764d532fc61e67023e4d178d2707147b0092326e06550ccb01985b2a541513973be2ddf77c2c98099f8990cf5b973b5286c9544566989fb38bc5247f2d5a1236d6beb0cb0f494e67b19d4dac0e73700df4781b55eaba76e47bf70066d446295b25417f50d0814e4b3530c7024fddb0944d737fced13c0d7468f2a1cae92ba079f3a212f1c0875d95c9f86399f8bc530c36c8a3da191668369fd1b569cb210f73b09820cf233bbe24d37d370caad446009b2e668b8ebf147e6fee3cac7025d0a2ead12c4e9e4d048658313f79ad5a630264c542096ffffcafc920a94d3ded10ef4a19a576f6e80444ec71712de5894aaf9d467b4a712365766e2347beb00c4b98017a2fd4955a3ab7285639dd392b77110c9c1c1b2d12b20a6db882c00473798ffe2d7b3ab67acdf9581ac9484a5000817f0b57a48b9ece8473f75d62ed4be91653391c90280e4be7b125992693464a0a244222174232851f3db684db69d822b1b8c75f36af99cd6babc646e9796a2a2e1551f9862c5df57abbcc8b6873a900b24ce7abea3c9078941bc4807f073d624a9514c8b2146fb8ed92221633ea1b5444cd927035792ae8e2c22a206cac941f7da4616aa0e32cb533bcfc704b421a5a676a2e93b0505c95b550d4c544e00267dbb6f72c08576e2350e1f469f3e98483749111862081de8772d4b56e7a49e96a234abe8eeea3e116b5c24746bdaef6b7d3493275a86ddaff53e254618fb4a19fb07b06e30ae735850e508df75c9f19de0ef29821309f3215956c124129bf0e4098b807229ce8086074b272accd3281655a57a501733a1da0399981da2ec635447792f9e548724a19f09c3fdbb2b78e747385b38d78a995c203d80254b4994420fdd4e01a32d812921ecc8b9c7d183ddad7d23d1f49477977a961f6ed52ee658fc49db2bead3b00d4445d8cb97b6af667936aac950647d04f7dec399d5bb2afa1d386b5c29a41583892242fac073acee0c7740470225faea9d56a87c6215b60c2c7ca9734afe6bf501ef2b16a25b8c64276475862d9a795aa5e5e334442f9a253d1aa5a930b88a83970585559b7ee75ca8a2fe119d85d7b8249c9ca40a079b1b873568e1b5551ca752b629be3e06ff7eb0faa4b490e11f169d15fc97c4dfa13f10fa031ae24e8080137e5b3690ed6ab8a7ecf87c7577fc2d8ad055c8214e103cc8920ec43fadb5e332b4e05261adaa0a2413b37b1ff24c2b81ef63b9bdad754cd1737d28904bb5670779d624f5bc1068cd0801e6001b8f0306d27a5957f15f60d1f3b443ec1b2d9802710178009518133e01a4fa23257e2302ff1ddff9750f6fc024a837c6af43b46a17aecf7bcc0a5d980bf7e91c36b631cab928e7652a3e5395289179e82399b02c978398c4e4796c2efa5a6cf4fd704ede56334970e33d447d6146bdbcdfeef36d319c0d40d3cadfe267a4bc688acbc30c9fc52689aaf22b132504da6328d3d259c692c8614c6f114c2136c651b2849ed57681cbce5d7ec868975d13f3b22e2bcb18c369a150158f43c588f848518a1c3503f525491444e881a8696f16b519af71ccdd9780c76e973e452fa293057a143030a0b4e6bd895823cf05a6dc82cb945740fa2e2e85dd61fcea1f0fa746c15674086a9839964f7382b9a0ffe83fa03816808fe035a21d6a7ca3675d7b1bb7c16eadd24b159d2d42ac8cfdc552389d1980bfda9ecf64ea08426a51851b7d24f05511f813af01fb2d15ec806d423e72ed88249a22b5c3428e0e51b9d1d6b3b180430513c2b90157403a533edc59300a243854217afd93138c291a9d91c861a66597f1c0a75708ce831bfe516e8a23f9eead92a0086d518d7da90b5e096652efffb956a443fa022a07dfd5aca652255b4bbfa4d6e7bb38778a0ecf9441718c9fd6e352c12b38112bb9575bfd5bafbfcc2bee0b8d36cbdaf4d8a279fb2f2c4688d7900602553a9d939afb7bded4c3c37a5d2fb7bd02ea964bd9a69a5c263234759936adf5f51f07cd2cdaeb75dd5ff76b4902adfc34dfa2ba87d1eb5bf0bb7d3aa9ea8308dc0156b5ee68c85c67cb39352aa13391e126a2308ca6a4d75e5110b0e9ae17cb0c50de55f41dc61f0cd5ca6e79715841d7bc10692cd9881858bc51c94e9229bf1992361e2e2bf02015ba3a9b1cb2f04f2ec93a44956c4af931d28f597f0c6ca20278ea4d5888d53971a147aea2cfa9aba84a0e82fed663cc3f65ee777e3e125eddc757ae872a6b7dc2560d07201882badc1fd2affe4b7775ef80d45ff2feb7cb12a9121ddd54807c1587bea92c8ec3bb40d154828071b6e417fbd5511957577aacf68db03863d4776bac2cafffeb84b7b9182131aabddc536355432bcf0f696a9aa126619489e1c2f837e56090a568740e2d036e1ad58ddc669f3eaeb25d781b0ca4a2309e5ffb034be64faccff6797b8a4fc845ba4f183033f1ab983220670ffc2231999ae83ffadb834c048dbb4d7fa5ccc3bd941084927cabb0c7ae5e10f7fdcd441232723b42a53c91c716cfd60bd1371d858f58b717353649911cd818916db570b2c071169ebb7653c9c590d438783eaab595ea2702f0ce7b618fb342fd2e44324ab75a30a4e6d5d41dd8d926328e96f99d871e4a368dbdd0b747bb8532b48be47f39a153c69b843532a8e93d7af5d643312d8e5b5a0c4a25f42219acb58f2a70185476e6e08384c5c6f49b77819579789c11ad80cc72568b6c9e9ca2dce874dabd6bba4de1b94886a49fec7cc5493534bee914bb968949d4e9831174104147cb09ecb22bc02aa12f5f38d0b5f26f0053046901c43f244c279943ba89b11e1f1c79b935d56d06f6bef29bb9aceaebb31884f5044cf3a013a91e663af156b02a26eb6f83c8cfabb0ba9646523a610d6fc98009973a394195fe11da460de87f866fe5573402d801d4f263ff9965a7280e28bbac1fb3a91532b116666f1014580a361bcd554368c3ca907f8255487d14c049ee329f36d1c965f0fb871c65390ce44bb2842a5678922df7aadef656b933226b0e5f49f1a87934e662483344f5b90192273be59a78d50521104002e0bb4d70665d566e48f7448cc15fea67bc89905dd64d560c9edc3772d7fd0fe79f04e1e16e153b4c198788d4958a48c5bf6907e77553abddebc4d936111042d296b4a8cc2094514829e441437056cd6627d8e24d3402595c750e0d6bdaccf61142b9eb40d792fc1263427c75c4804ff7a085386eb563553020d5a2d91f2b938c451d0e1e46a5e477f6fdf6230c4eafbddeb1e129692601de45a6c59a2902133e5f6a230745579e1ee33275b7949954beb877d6213b8f9e936bb6029697b62096eca3b90903a67767f7171721fad61db7398a629fc90e9e847224950390feb0d01424edb4a4e35b638e838aa595cb525d25225e5b021ec60f55019655ea536c72983be55d13bd4e96587c7ed40f3b4f505f0768d27f3233e3826a779b6da96ea128905ab30ae2dd9a9274f0d2b6d99e1d25bba5b08dce4414451e17b3fa4c6859ba8c4afc647dbedc69b6fc8b76d45f0224eba4fd5c40dba0b542bf719df330757f504b21a1aad7f1d401e2a5f83d2c6d2def10155855e5956be4409a483234b7c34f32756ffc36f090c50992fe760a384fe8b8b759e16c0e65b8ce2469aad7cc75aa3126b186eb7be7f494dac0d612e66872435f5ae50ab6971f21e6e8e22a4029135a61b7984c9f5f7c4e18a0410c3badf31e95fac7cfa3d154312c3a4682813c11462febbe9dab5402b434a4f05e49f052ff85a0d4f45bb7192106f45466f5b20c3c70847587331ac70b6d0f4bb56230fa6e66c50038cfb4624fac635162f6fb87cc1cec3726c00722b670a1efea68938df478446285c70505ffceb73aa508ecd914b96c4918556a269d38f495a8251b2b0be5adb91bfec46f849b060967363139e2d45f127b22b0f8439cea55ae51766bb3b92cfca3f4180bc8a1ef7f548bd86c4cc348b8544cb3db6ee5cffaf1929356d9288cb80407e847082711ea1d233184ae64a78a6a46ae59a4510ca760ab19d7d4e0ca3059ec005c0e864e5589b654150b015d2d314b519eafcc0b80c768df4e54836a95ea3ba878b035b0a05ac8826c5306e3659bd8126f5a261dae5b3851472b3cb8e118dab83603a556232584de2ab5d5cfac6d967120e469e0a249c2a84e7c2aa22391db067a3a275dc2a092b81d30f0fdcbc34faaf08bda5adca3346ec6454a80d366e899c2f7cadb8f4033ef6801f4ce7505fb9f5e9297932d4b73ff34b69074d344473edea2d1c4925f5c2b53c8436839466313c99b431c4a191275219dcca464842fd4a8352f4bc28ca6816e5564428a218d1396eb5f4fb77fd978bb38af36fb6e95a205e34cd60cfdc31d8d3139427e3707d7c278e09dc9b8a779e465269b1a008776d0858f259f86cb188578370b958c4bb8b76a169daeffc32533e6dd1c2de30d379e0bfb66a448b0f3060859cf1e061ee028af35bf19db4166d67846564c600ee1b32ed8ce2f2804d5646aad46f6f8777af521c4dd6a0bb77abcdb88c74ed3e9aa987517d220108b1a922ebe9bdd0598967c5d22003d678dfb92172a40a02c641e9346e75e2312cd96e7ac7691062d74ed5b2011007f14b9d6efae4e95b3ac039efac873f07191ce29a3a77aac664ae8246c66268b102f78c60330563d7a56c026214a8af6f2dab6207bcfa4f7edba740107d51fc2eb52afa264c2ddfe1827d1a1d7748cb4445b2f45b4bf953239bf7e60864399f74f5d24c1561c1176b8b564bfe086fe44fe158da0ec3a66ebcda0345bf73b5dc84a4698bc1c3b5bf5181f49cf3c818972d1d5b58802a4b7314b83be9d99ec89bb4015c386d96e87e99b3ced7eee25b9a7a22986e0843d77aef9caca912b5d0c88f55061380ea1529cdf84a222eff2e5864596c8cd9124646384f0cff8ce257aa2eee2dea3c3845c968e7b1c794963ab7e0908141a0d262886a99bb5fb9cc2ecd67c5574ffb4e7cef3e257c8aacc503f343ca9f02380b6e2726d982d1591818417dfa0ecca45fde0ed31a94b84e858415e52e22f5b9766a6daa8ec656da88c25c5c79700a6b9a107647fbfc45bd95232a2bdf60db1fb6bdf309bdb0ccccad1f11efb5dc1711d7cc3edc0caf0ca1909cc8283cf2a0042aec2a386cf7460c95d20944ab34213b702d777f31df7d266c3b12e63de1627802154a13eca949db37729d8d5ea9638b3e2706bcfdc5f20f2f822940ccc657083040464a9f93aba30924406880913b6f50cc7c28ca331d8d1d02f0e1def8a485517c0419fc11c39e23914fad2699e57923472d7145fd0f06afc16b379d8bb8a69cd0273254d470cf70030ce3fa02f1baa7213f2c34953515ac2e0ec98348f36fca03d91e8697cfd74614cfccd74c7e4798cb3d28bfc961136e2ac71ea2aebacb81bbb0829a0c07f672be14b67c03d8d9f31f0e2f079bc71de86b8b96a0d04e057fda6d35a7bdb49db820fd89817a7a9a28a5521eaa2d59858b96cb65a380b589ad04836f715661cd76a7eb5fa6c9f6d0a2d59634a960fc11538d24812af0cd6afc0d4b093f1801b6d001c24ce443e21590c524d303000805b45bdbb0e03447da1360ac21ad213a6027bc06c3e10587b4e26967b33564b96b530a58e6a5a8051ca179fbb5547e2e57a0d43287cd018565b6b75ee05ae07b8016948de2c4ca919a920991e025149b093a8c2fdc9ff78c7cc9406c25da988d7193b2bbe45cd379fed3a6ca7c322b05e4bbe017f1f2fdd3b2dff9d641e99a3d566d6134550196467e4326a20f8d1b090b8009c3633d80e776b2179d52854d461e1a8929d1e5096dc8743cdd1cfe7ca31ae14ad046ca127e58a3097e81c1dc164bf8e17d4829cd8de665a551a0b1de6c4d5e094ea71ee568863068de987841de2327e4b70d7b87d846fc1e05d1939bca72c93146344659f71332aa2166e1ff3417077cb28429b10d1c22da1a2ffe7c673ce6ce8fa1e66f22b1e28fbe63a915a507b095a8aab9bd16244cd369d39451dbb5db4145a583f48c2d2a7365424c4e77ad4999163905a029b4ae74f9a8720f310a7d4012a44aec6af7f92c71f53f5e9a9d92bb8329157068338468af607b2e87c5e3a455459dc62ecd7a00fc3a18cfc04b5e46c352bd863720b26a9369ca080274574e2766041555b8cc21515907e700c78a89e0f619a757e999b034773ba04951e3f7b70ddb668d91c8c0698815e16abb03d03ca9813aabf069f6ea2a1583eb4ef5288d43257e704ae86f80404db7822f605e894c5997382d6adf78ad1d86fec29589e76e887142ca7caf53df09fd1390b84365b0ba89bebaa1aca0fc5510538954961b137296bcfa152db73535223a74a4ebbf9d25eb30c726e450229ec2bf5a7d06026856c6281c0a62e534a486ea8f80fd2ccf5356a8e50f54fd2677db304d5220fbfcd29548d41b91a9971e84594ee5bef6577bec7127915eb0d24ac0c82b0d207e565d5b130d4bf2f1b50230b381934c79ef5cea9f8ec62c7344b17c91b3fbbf0f85753f70ebdd44d7629623304549ba870cb87bac1b9343d114855e38c4d9ada3d145541990aeb857eab77e2f70e400ff1a8bb12a429b3647749339e20a044d26bd685974d10be43e38266dc96e9c543670040e9443eda9b180d4a8a7d95f7d05ead92a4ac3d23723de7658e57e3d5693a945dac2d5962852c65fe9eb597ea9b06b3395f5d1132bc756c03a1b5da16d1e87705053bd9ce85a9d73412ee9aab365298c5cc7b0dc456282424934ed9746e084c088c62ce9467c5b0b06338b6dd9215b763372b481fafbabba955234d57fb24d9e93bd9e06afca8855a6b057f998b3e58f3d5902dd95cca4b81591a7bd3c8ce56b2915d27dcd51679cf75d43925efb841a040bf357f0d0cc86abcafe2456937483f7d5b58ddd0dac538fa3fe2342ae65669f760a288f9f523bbfd1e24dbc76b196aa82164a287a63a2970b881454a346aaa0dec14f963cc01bf266e801828d44584e73d3b94320fcaa79691c947539aca8a24bf6d9c9c693876e5f9cf7cc807051bb4d4c21b53bd5da722bf7f58a36b964d03786e4f165e04faf96b68a74e0f09a628f297cd9c348275c32d1dfe014aedbd76714faef4d670835d13d71bc14a868a5249d5c94c8f9a698afdca73b3a0b8808c4ed165f9a5552a9774963112457136bc5be16f2c70510dacccbe8a8dae3b917518f6af4a772d573e18b947a8af60a601823eb85ca0d83d429d7ef7cd186b6f555203a9729c067e422178ac293c12158962c6c03842f021ac12960562090d1930e50943f6b202ede345de177ed4ed3b36f485cc931ca5eb7bb0efe1be5bfa857a41381c089f7c31ab085328e55aaff6455cd76bdf74cec0ad2a706c47222ff2aaa7de08e034f88fdb6c9265b4a29a59452060f064f05e8056fe9929f4ff23cb951a8526506cdb77ea32d50a8e2350d30dfd61da62ebc4e6eb2a432f6d8715dc1376b49e5eba796c7d66bcfadaed88e55d08d22ac42d349a0c736271980c342bbea6cad5334dfac3fef37eb37fdeb60db21ea53fca9befb767e4c807dfa1011ccc62113037aede8fa0875db05e091c8f49f3a6680e9c8fa10127c18e2c310128c48301f307d82286c1c8eeed8b4b3761405c2911d7958a2322a53b59487274e4960c4a06f735d8ad7a4d2086b243e3b0ce86bff94e8db98f08baaa331e85f578200d37ba309f37712d276eb49d5f1f873c276eb2e36055d40a03abee3766fbe0c76bbddf62bc6a027d0d7fee3f9f5f99e6f9f3fde68c2f6ebb5ea288c18291ee79194c18cbf30e6d66ef7495777dd5eaf1e23e98e3f4a736b0783578192086b6b558cf17d0d773a9fafa3752c924915063d12104186e973249201a62318d62740ffdb19323d86b5adb5adddee2b1d79b0c9a0153fcd39270913ac3e4511767392f0ed38518281528abf5efdc784f9b583bd734ad5ed18e38e247debdfce6ece4942f50e6e9500d46f55013c6ab09b9384e954ed2f65e1ef18c3c4d7516b521b7c2c4c9f63d57427a4d2d26eb77bd2a25d8f419fc4fea3a4a9ff589faf02d3579faf441f8fa41d2140abbd38574defaedbbba1edbb51706e551d7a77ea2675e10d476ce709c9b945e2276047d269cfdcc20ea356af7834c13a75183a9f9f801d61d09fec30ee6e97f4934713b05787f143fdb6dbed7e97f809e01146dded766fc71feab8dd6ef7e16eb7fb3a9223d8ed76afa34ffed011c9d365afab4f479d12fd3b924c33fec77a0c134f3da9baf730ee6eb75b7bdd8f1d91ee6e8e2aa04e794cb79a3e7d822e9474da6eb77b3a6a65ea79a14a7053880b7bc2f0525532bc33be513c69664aa03ca7f9d9c0565a6b6dc38dbe23377e7829a648c1a1e6879427301a3b8ae8e0338315192bb12d3562778ee7799e1785a40a4d1db4a2744520ea4bc8961874e4889385871a58a21c3132b342738351394dd92f1b5821f6c2b2318354a13903121c7632c49adb9c1ee004e3032d8d0b61d6c4a0e5c0522bd346cc6c5d34ba72be72846c25192a6709c207f69ef09495f6d219e79c73c6b203c9e76c85735416d09c78a05f727659da861cb6160491fa9a210c980db46e121b1ee440eed54a6b1cd3ca87d5102bac2dc46bba57ceb060396d3569af1abb26298a95f31496016d870796730e2ffbb53515f62009db4c59e51ea2c072ce5987a5b185c5c985fc14e2c29e708642e4d01e58df7703dfd05c36ec9448648e95225934cc98c1e2a4c79490106c8cf1d8820108a719dcbc91e2c3cd072288e3a508950f1fd28cdca0851b865c2a62419a3cc881dca93cc881bccae9172cb2b04748539c1c3abc6e13d61286858700a283e389d5cd063872ce19890ecfcb996a4e7d4d894d8d4d954d9d4da5e170ca0ab03a6269c8c25b6bad776455a1b9c585e5c50eb01c84f7de7bef1d984ae692cab173e50712ec39799003b9df9cd6749855c656396e6c9db5d65aeb2f3055689299e10c7ca179d72d87f94e799ee7a1d1aa42334ad797d818129ab29c6f50ac0de5fb0a2cecd13da692d868c0f2c2ce9a929c73b6a17b6435e51db308980f6b15e2c29e30d4509e3170669f51f3a4425cd813864fb58a191f39e79c438be79c330e1daad0d48ae2bdc0d1e520543583f2eaaaf185c52c3b612a32990a4f210085302d64de10b1b1a6254d142968b059ce535e235a588295c52a889919179048299b52662fa4d0bc6c28d953daa3d20526c4ca856bb5ad9688588098f51045446dcdeac992303435ed2b2aecb1c156b69293af88e11757c6c23651c07ec0e94aeb41447b29e1f182f274b5d65aefc0aad004436676811d5a0ec218638cb1a8d010c5a13144091324374c512f4faed8b8e0799e6725aa0a4d1d3e866029c9ca19b0b2b51583b132ca0e0d8d0894a3acaf7cc656b87a54b3a979430e0f7220c7e15b67adb5d63e7455a1f9855ff820860b71614f185a5d1e39e73cc40acd9d33946faf365f61cf5c43c32c076160a69cb9b141a8a992b3508554716ab86b1ae7d9b5eb8aead2f1f365ea862e555e806839d124376429bb6d74641bb6daf03ccff3920ca942134b49d6169705926091e2f9408b0c8e140e4da684d1209f3d0ce41d75f5ef08f28497f0eff2b8bf8bb3847ff4e6e23cba5f0924eb493dbe9d82483dbe470bbe5081e98d88befd13fd8ea4ad033109e7b89194f1382fe15714a3fc48bc263cca8f4437218e4b23584186fbac215c17dde623d82b562775fc7a093149c9fe2091257cc745d1667331898fff884a1d8ce4895157bfc7af9b709b97487ee867a965f7624fdfc7c7250fdcdd472499dec74571e90317dd821d9081c293f8b8530a177d042b8a4f7ee45e41f71e9164fa1eff7c4423a2bbb8820c0f5c1c771eb8e8e2e89404b799dec5a524db93e0760a1fbf8e134d8ce4f5126f93d1236adf587cf2230fc62437e711939464bc55a2494936075d474c02475dd55510c484369f7f9de66c5e08a4aede4692c9366d22dee24cb28ee9cbc84cb8aad3e6b3bba7064d1ffa219c4fde7e7efed56fd4d5cf9f4f1029467decd99132fe3ce72ae2cff3a8ab3bfee9781ec9251df1c98f76becc32bd9e5d0b923ff1b3fcd2f2dffc6c37193b62f6273f5a41860374c61de83ba3d3ac21507ff2a31d9f479febaaf6efbbe988f3288b641593643d2e25a5907dfb1e5d909d8e3094ac2efca414b26bd7a30bb2ef11468cf0a92e7c3a82a54bffadade9eacf1a34f7d6596bd1a2e0a6aec1f036ab8a15412b705668c807ea4655450e22fa6e5a67d7d1c93fd6ab1d9a12e2b04854837c070d4109f5a894e2b5ef3197dd8eb80d4d09adcd37ef8f7e2ac8a3cd75f5ef6d86b7f91fcfed5655648cb348a9125dfbc93e9f29b7b5bbd8a31e993e8be4d267d72299fdf6e59b8bebd86d48b7ce4db36f6dc2fc3cdee8cf1984abcfb086b5d65a6b69a5420ce5a0eabed65df53beae8db71522f59ca1e849f6596acf7448d5d5749ecd5f55d7a6facd61393741583fe2dd9e6f644281156f0a044b0ad306b089e7b62f5db6f9deff883649dcaa84cbbd6b16fd4d5ab49fa4e3576edd397e8d2d31208aea75e7dfbd6d37c5bd259cf2fd0e9acdb3c46f53ccae0698ceab6915cca696f73bc455db535ebea6b9cd3da27282655a78e41a48ce9d674c4baaac017d709d66cc23c81aedda7a01e93aae7d18278d7a01f3cce5183f926d0f4eb79c612b02f62d4c7a3b52246ca4bd8b5679164fa3c7f89f41c8fba6ab361512242e8ddee0f21f6bb2f98cb9ec5189e5f18d975da2df644a5f95a544af176d4d5af172ca12255c7e30573d9b32761d7d5cfe3985d571ffb1ec9ea7be9eb579c382425ac0ebea83d385bebf883547dfcc17e6fd97707209ce59b45cf27b8f1c6f72798a95ff10af200f8595ea97aadb7f92dbd90bcf5ebdfce0ffd10da2add493afbd775f5c9eda0ef7182255ca4ebb691b479f5adab6ff36f484a9874c78b048a4950d8595b5b5bc339bb4dc4f7e623ec55242fb9f4365b92aeeaea83180841ffaa67723bde23597fbb57a3fc0824ef7f1ee547d701e0b8346b08365f9a3584ea37af3e82ad22769b98dd139564bc9d3f95c4a9777cd30066429bd5818b5e502e2d67470146418e0b6fd43eb2d38d58bfe3dd7fefbd3731d6628c2fa698e28bf1a423a65c18638c31c634bfd9b1d65a6b6dc5855f3dc6feea2754b7e35093903ecf4f33326d8ad0a8b1153c61da396647d2da91720de120fc7e9657a27e3a53a5b71cbe09e4d488d953aeb20a98a75c4fd1beb4a2e4c96dc5aca472f6d689e8eeff05e5fae9942ad13525a0a74e9d9e5ed15987b2c2595ad1a4a6547ebaf59b751c96b78ec2ab8a59768179eb406fb73c9559a4442d28012ae5afd73bd67a6bf5112a9d32595c914e9945601d95f653b7930575ccc23781b8688b4a43389d1a992c54a04e4f8dcca2e92c54a05366d1747a5576ed9e725d3de59a2c5498b3684af93b2a5d9172cda239d4249ccfc34565b2983f47af348474050a55b24c21aa02852a5c66d07cb2443f4b32459e0ca7e0286e4d787f966488fc0b7808cdfd2213e47159645ad0f2659f40b4524b2bad14db4aebc5bbde6a2badd4d24a2bfd284833ddd4a3975e5ae9a596567aa9a5955a5a69a5d8565a2fdef5aee096c5695500e66c0ac0dc5cc19caf259c787bcbb9bd53800230972911270073db04604e8f71a19edb24a802fded2498b3360198f35a94a08e718d4004604ed7b1253c2200735e084ebc70020463521f70d3e38131617a3ae0c5c381314e466c80c4cf72cc11129ea701132898f8598eb132918131347830b07381bdd366a72ab7b0402de1a54405c05c1633a5870245ec1056e5cd981a54da1ccd29b4ab5e7c5759c504ae18279f04c05cae62b04a44404c90db096272801e04c09cb63c7378906e6b6e1e3601cc691f311f17602ed7305478aa4ac881396db5741e400298cb340c92f0280c91efc3f4b079de083a57228491d2a9724003be2b068039af0518c209ab588039afc502324f199e2a05b0b02bb6f7c5236d2ac09ca76d08604e7bde170f84045c30595870002330a72f98ad112c30402d401198cb3e477c88866e7845000d064810cf95e709008c140fa90390d33784131e01c008e6740827bc126d0827243da7c9f4785ed61f8039ed0198cb148c44f103e6b415a2cab6b967f34e8039af0af1e46301e8f916e96b1325c09cae42e0f020096a16b807869e9e1c2484d8f1d8353b79841d03c32f60b71d9d9fe5172a3a399d45ca93c57305f2fc2cbf64f1dc2e7804ac9a5305b6d9b6ed67f98588cdfba263c4fe3263a785ae56cd56a9c837a3e52b15f866b47c47941961c190a0ae6739fc4892033956a094399a5368d35ba5955a5a6da5957e14a4996eead14b2fadf4524b2bbdb4e8467b80436442ec57af303ac28e9b2cf06ba2a220be7a5151d50f2222fdb3f4d2f57a0f81391d6a855a679de990171bb6b641606e5b2f572ee8bda900c0dca640800ca06616f20bdad620fbd9f8de3aad12a2ebd7165e6ab8c06f9de2aa21604ed32d04e6b4cf522c2dbff53a03055dd41643d54bf3be14b616f583efbbddbecf368bf22cca0e7af0ad9847da5180df9a2fff5c151de4b107abe63ec102b7efd58fbdb5ceb3487b9e45791669c7b328cfa2ec9e0952bf76b7534509d0451e730394313f67d0fde1d303e6b405261b1cb85c59d2a6c6d8d9d19c42db00604eff6774805b058180b9ed731655d0057d17d6d6eab9972fb5950073b69c776f60b75b09f8a9d7122d7c6ac8155ef580b94d02cce9902be41971afc29cb61f0f5fd501c19caea012f08ade28a8048401cccda2ecf7bb4734053df75e679db5cf599447bbb5beaab3287b9d457a1665a7b328fb9c457ab4d9abb61f90efa330dcc03c6e16dd3cfecf52ccc757c73f4bb12a1a0474819f7a16e70b600e8435e1ed672906f533df9eba531f6bbe17d605eda12092756c2faeb859441d378ba6634ad388cd781223e769ff2cc3da8461664d62c29cc48499691213d6252c882c61c2a4a6be84b130f5258c888d2f6147a4bedc301e5e07a91b0bb69f651891c79561476a7801ea92c5a52bec8bd70416c407e6a50bd7ee82a509acece2e6757e965dca3ce946f889cb962d624358b0167bbc25eda98aa5a23df8597249c24506babf608489ddb834e9b02a3a5c747e965c5830c285446e7b5c452969e28252a48b930b45ea7a9003b933c1ee41976aa77af47ba9950b632ebca1f1264d7339a27180727707b23f4b2e3c9e74231c85b9e8a0c1858a06d63f4b2e505a3603669415fba8dc5256966a4358f0254be811e40895314ca444d16052394790148c703831c61ed2788c4bb0a7c74d47f88e55762ec1d6bcf7b30423b36686accc6d8354e0b2f9da7bed9b28cbc6a986383acb9e565c7aefed962599bf77a82b9ca11df3cecfb22cdff6b32cbb7096212a5f52004291b5660c884a0a463e346839928291d84d6bbf9e97b35d6d133ded83745d7b76d6a09bf6fc529c8fe3dcc7e636dbbdf4ba09b789267c489be3fc2ee96a7d1f138e73138e53e1a3a5ebe95f2d5baec7b7b82392f7775c4724975ec74391a42a489453412848823ce15e0e020c10609ff6db7bb494bdcd7b2ee882e9716e13afcf6bc9d0b7dbdc8adfc5b93b6e0cd96755412fd5d9b75e7dee7105ed19037a9c56c80a51eab75e2919ec2d3390d82d631f080a7806ec50ef06fa8e488fbe0956a082db77443aadd075901e55318b7e459be79b45ed37f18a3de3d4592c5b763e2470bf7c73c6b85c732ac0569a53bbd3715352ae613d39c5f0b55bc7dbbf4058fdbb4048ddfaa66ef6d55c830098945d19fb414a199b1c46787864d4a2e6b8409416465504d60f31305c3655a9e6e3ef258a23809fa5da1335264ee4c08173f3195182e7629c091d520f4ee40e7676b5811fa4f8f974ac93f0504c5b7b6830a76a3787aac6d69c1d95cba91116e1b4d3ce9aae579a9a1a358d0cca2b2dcceb428b2a02088e0808341f3888aecc1e7eccc131258e952c376c1c695d706940e0f8a8d1dc1ac104fc00132210e44035688407a469a5657d0e923c693425f4668892064395b42a1fa8421ca449ed90e694b5c624ce12375c663a1041c31054c2d42096c3161c80e8c1ca13ae1bf8a0c4890d9868e98065c30d34fc10e222368478089220497c34b1dad2c1c3d4912828729ca4c8d975a53d6944e4240edacf6aadb571cede5a3b9f9c5b2b6e1dbd1de28c11051152a15cf587195fa3ccf8ea1fbe5dbf275010c484d6ad6b3047b30e9b3c59648c9da4d41bafabc0f4d7317d4ae10c29e3383db9b3d0b6beda6aadcef4aa6489b2d8fc6d60cedb1ef69c8e6412f6ebf9d314c479dcb388fabcde2c7a018b152b40568cea0aac2535d850264c0d607b6ac43d6dbd600398dd0b6bcedce00104b5e3e7aa488d16d09c9419b3c58685001421bcdcdc20062e3548dd70e81143181c9c68b88216150000881b6ac7d5e981c90e438c64e192b59f7a44e07186eccc17133332ec1aba0612235fa4685185d94e24ce3491e18c5a94263a84d84d3ce8931f3eb2667003856ac8a6915d20a64717acb118ca7cd93376806ca95135264a8d72ef78ec10e04401b91a0325e4c99a138979099b51840d5f566a1e600992034c4790afb1393c3e48116707355b7478128395b41461b45a984104d85a0c7276a000f2c4860f1d48ba1479128707cfd65690355a3b9098ed008747070d086288ac356072680305ad040a2e4dc2d8a80147899c227cde802468c264cc4a0e6590b81971f6a19064ca922142e03809d2c684561a1498304b98e4c06687b766876d6681c34406316084f8f2658699587821082a24438ad090a5e644106b80f08222c30d333748c3234cd3c30b674d5ac072068d0e9117998eb4af1b67dea8e932a22b4195cbd75a271065b26d88737c31c645583dbe3d38b28a7111548f2fc618bbc9222a52c4eebe41b3d68d9bc73f4b37664f863e2767a218c29c513833f33c22ced0c0d24d0d1c4432e833448c8800c10043d566ce72d04424c428c4b90715a0c9bd2900110539d33ad3218a5e7b56c313d190eb40672c9cdd78d28d6adc13fe555ab3ac01332b239a5aa39bf4106bc8f29a34529b524aa9d2b54e73e02caa3e8baa4fb1cdee6910ce928dd9d7323f59cc59e267c926cbe38a588326c566061b29363b9ed8f48022b2c126091b18b2d7b7066dc6962ad9ecd03f4b363d2e9ee19b75d61e280be41ac1b71cbe9f659b27f98631c6e2cc5d2bb6016b436593f859b621a36de46c9d1fa4fdd6ebdb919cc1dfa68fa8ca66d1c5b4379e9f659a1920ee6799a64a47fc59a639b293a6eb71651a227c843f4b35509ee7d93e355b267e966ab04010d4d9517306809fa51a2c5fcf8d8afeb334737adc8d8a297e966633f4dc48902041a2c7a73413c34d16277e96663e3cce6ff3fbdebe1d4915be7a6abe59777e6e21b3f138141f02ba6e10451901114519a1f1a25243d3c44a0d8d112b35ff59a2817af0675916a70c08b5322a6a6550d4ca70a05686835a190d6a65576a287e966536ca769ad2595429d2dded763b8cabcd741a00dfb7f43f7cbb31ea67a46b2bb53adaf588ddd65a9f1819ed8c88661035116146040d22a7b5126564b839738ce600cda13866bec8ccc0b9beefbdf7cd073fcb3354fefe2ccffcc0e5242fc1676a88ba6ece5c455937e31918a2dc9c6921cacd191e4e6e7269268d99312b381c1aae88331ceeececccec86eb41650ee4d8882dc847960324ab01e776ef55632b23d3579aeebd60ca64b995f172a30c9781722b73a5ac24f3fafcb3249bf2a41b8140d839555427edb573ce592b8cfabafab5c5bdf5ad935557bf7eb55043656988208208a3216ae08166082238d00c71e3bf9fe5103b3321ca6895917143e68ccc1632568e944132c496d9c5f7de2f7fc9b4fede92cc87bf40d59691d1e17702949ba8c4eb9a2a6a1022c50810000000c3170000200c0608244194244110835cf8011400095a924e543228a10444a130140c8581280a621808821004621004a3108603599e4379006f08f625a52f948835d9a91cf4e87e805b4d6f4df601d52d4ec33bcf3d2d3cb2ef4fa9b91f13e453a7286cfd7e8fdac537da566343a7491b1cc373e4b609d88932d64caf92684f9c1034e1d0a249b244afb01cd0faaf5e890543d89466a24a107bf452882f2f7655c21e63184f5a1211b5b7854fde71975188f7dfe82e7626de93b73ab7205a3e88db850b9ed2700fe052e8227d43892d0f9e0a890df5a8292dfcdd9c319a368becad0b221fe3d33c420a543c767b20ddfe5c764e092255a42feed35312f4bf3144d892cb770951a74d32648585e0da50e8ab43760ca2e7ce409b4da4706f4b8b06731862c939f45b5a5aa71b3e8867c6f5ac641db00cd6ce4e1b222e12dfebf302f45ee3e2d9ac0cc6ad6d7d4ab823b4b06a5fdef9ad22c8adee66d72d36d8ad786c782661414b245898c148db51440d011450f010ee8398131faff942ec5208f7e5ef2d1ccc21a262a20cb37a500841a3031097ad9f31483a2882124a7d18101198160045036a53e505710e7a3168e594c5dd7f64d1398afcdb0fdc23f5ba786357702997c9c5ddcd6d5dcffdded81ddeca7bbaf9bbbd6df7bfdf1bbb83b7724f377f376eeb7ef77b63eef0566e626a3f0e16c337fbaf1ffa39f18cee60f6c5246bbd2b22f01f85bfecebee5079eafebd571fff9aad298321a14848d670ec65890022d8cf7d4f62be8c0b3618661945fbb2a405c71d2c938fe25d95ba286e9d4f2331374906c68f5be2daa789e8528d054e053941107c14d5c678e05c3ead5a18ec07b3ec040230766313faa8069b20c3fa51d5e9eb27931cdba7796860e2f58cbbe8ecf560470f7310e3cdeb508a3d87de9a87aeca0e102834ab3e11b9296e0a3a9b298010e095f65f4ba53c2c2cf8600b69d2458dc159b07a96aba364a935308e1d0268d2e6f9bf189eb43cb4d61926a1c0073c3823cb6b7d376abd199cb3532de9876240c4c7401d9f8193c9b93ddd927f3807637c0cb07eb8e2639ddfd99c5bd22df986b29663626a20a30ec9ce7826e278953010f7f2300f5556f619473fcf97638470f9d64532c1b2e142876991e660667e5105037cfacab5670b835c418e009bd1fea3aadbc60b0118995b0b90fcd06d804e1eaa2005c4baa5e0ec0a7ea697e289f063f44252ae61e92d5069a44c3f3c0f2c192f8fa8edb0c10b20be72a73b5ef8c765af2c9941fc0a8db7d2d321d8214fe2c548ba653ec3cf7c25dba3ce1155eb54748f790bec9e87d2d11f41e83414c1a35d1a17c99e47ac3bac0a1b3757a0258a3aad9b1f0551b16ddf8498b0eb2715daee53d45e4772fb3dd9fee4368de2ea00fd45bf955bd2b4dca24bbd71975b113df686187fad43635358bf0fe976cb36f3cf86a3de28fc03cc35f141da38c9e6450a1f4081578f3f04301d68b7d5a2800960bb15471668714417bdb2039d7b2d2789f11ef588a2381ec7f122d7eebb1c3157087cf1e884ef48f14b7a032a4e607fb04706330273e0dabb033e68fa6725ebf0962426a4d04df6f55eab0db04f1c406dfefe849aba0c61ea22ffb307c79d75940ece2f8cea004b9826b80a72533d1a795a3244c3fcfe326120233d650a87a3b4900d8ee18e7b59a8f7ad35e513acafcdf96cc1a5283cea440c46a326f85bc4d489b05d0de1db35b54b76e4b31bc76b159c3f7ff51e1660e9e326cfea4e88e5e376b2152df47b7386967e694e57f1775a680f416f8bb63f83ae6219b423a1f5739e91e0f645fb4b84a2fba1039459fea0e70a618044bd52f66be2a7c6b2e2f8b9adf6b2a3fe8a3f2f3a7ed93d59eb3fa9eb137a246f69f18415f05ff76397fecb955367a248a54519099e6bbd81a0b21fa5270b0b85ff6e7d993e6f75b7f67aabd8f9ed41485f237278ade530d445bff18c75b85ff7fc2f9de27be68f78e665fae71601be0dccdb7ee91117f18fc46ee142690239f18177ad42eb53847210a930bff3a0d56d72547cd9fc03a30dbfb49dabf82fc7bed842ea0f92e9d0f90d41b320c2e329d4690cf682e8ae47866d63c0c00bcb07237ebe1a216ed20cd2e9570d3efa86ff968e2f962fa923e2b15671157ab70c8fdbaf07bb229e33c2d132922d035f529dfe292dfb8956c26c632bbc66fcb21e435e7409d3d27da04ed2aa043ab300a267fedcf7a96ee874bdf6ab1125ef22a33c8a36cdfaee5bc0305c94604e52f2a4da592a6a59c1f4ca2c583f0c87459c07c344b4a48de1dcfd77d0f08c464f42ae4e62002637cf83a0eccf104d644ec545c9582047d19b027738d00f2ed5510b93c27be604146f3d95e207f8dca8eed6cee43786779a57831fd86de207a99c683182ee11bb6e1acff97e521abdf913497f063ca2e616dddb6a2bfac013dc1a982ee576bf4565af01f4cefe7412c19ad9dd912fe737913110040c4de2d85ab80db42ccab34779d582a09e8522a70104c8d88d12751c020224d481dc7651f0bec65e745638835c539c06a90165d6def00526e1a641277fe3d17aad37c3229f345c2deee09107fd449ff066d17201a95e194cfcbed970359ecbd949f8a39a83fcebfc10b75d046603bbf8b83f6dade6e002116f63d82f71f9c3784abc1c3f2a252faeff43631d7818bfc486e057c117587713b4b5b11c6989e10ae4f565520ef49d5811721a5e2dc8033c90f31299b78b83721a3a39dff6dd94a441374635b66b3ca41fa23fc105447415c0e561e45bc2755ea714402b7e2e1bb4dd55a6866781b6dcf21d705221bc764621fdc95844111d660191010ae80eb3170c3a47aefae9de817d421010892f9af0af0086fe30884891fce2f9f14f4f8cea0edc00bc96bf4a3e32704131b6aba03055e1135c25687890a19cca4b8b63f20e7842048daa4534863b80e3d39a73c03d12f49e8f265262a256f36651730e8c4872ff782a1200097d92203e24e1156ff35937922443dfac89d4b6ec80164959c4373830417aaf5db1ad5df4b2561695b89b5dae157857c1f0b69635107800919e869959546152853edc074579282773d03043e8f9ae5d1e5f4163700e5441bb6fe4c9de9a663dd70d03e7b0915db4a6fa5c582c71db6b0a0948c4ab3b4bdf01d249a5cb5b38dec3da6f88c9e2186f95c3ad0425ffece3e04e380fcc166865285e67d20f4c6e4c4addf7c05fc1061f13841a2982ea494a8354d27827790d5589d111516ae393bd6087192577c8d57dc9969cb9825debdfdb12fe70b5134ea01b85b7f14ac0bdacc11adfefd5baa661de989898fc06e6592c00e29700e0b32ed9bde950fc166dda832ad7ba4331239a281e25a61337fd5feb61900e036470920aca9e2d67c2aad8cf4727f4e613dd3f02c7f802c19318df719d543fe6d3517ac181a6a347b4ebb2f9507367149ff3895d96740aedadbdbd2f44066c564a8036f731457f571b4c0fc706b476c29b772ab2aba1e8f62f9d43a716fc497b602f17cb4708555721f80ca5a46a001f17dde0fdb01c7b771e1f02d3cb73b833f601f8d5d108c43fc9479017dcf36f242a7c4ff8639b13585c9cff49e3d24fab2e47253d7f39cc9a57ad2546d5c53fc6f9516a2b14f1c31f832dee4196f1edc98d6402c949e3a1e7cee8bde2880bce3a582f209ff609c7f1e285ba2c60c6886b82d9b8558c6120451aeb7bc254adc7afa8ea87ac48a52f2b949173130f1eb32d208a10671872194d3cfd8d79fec66015f1ac3f384426c6396eb9eef3144e459c539b68e9fe79d8f0295c504e78d58a5e0377d513000b0d28dc92b67c765a906168544ce77bcdc3b5ccf06758c27c1641103c5b86f0f5483424d44cf614894a71e02801eb2fa757bbd5c0a661d40047c8752ad9087c3aa2e42ea5990d41a63efe3f40e15f44b2258e3adca0c7beb74b35873a70395eebb7b138ceef7e8c02f9e26536f1c4ee7234bf51034ce338f65c04fdc1c57f08f0a72a884b225e09833430f5d2565566edbf0f28f8d7a16097e31711f9859e7c9815c63f17e6f5e4c8b3ba1ffa74e64f602ce27fd408967b8d11264c8b0f283fbcaf3a4751981e45d2ede514808974f1599df6516e71ad2c8a0ede32a0350d1abdd6d238d537594124f384f6dc7bad8172ea026a376b5d4ce47b144f6b861249c80af9949c9807850ac769d6c7d57bb00960185c899fb79acd3dcd896756d5842fbd2937c437d3413d517f06d79df13d9d0a82a310577f76115cb7ce8f183a0488c29acfbb2270c5dbb873d63e651fc08a0a2ea50b9300430fa204f4d527c93bc13a78f8b181254304aec9919c15a1b27cf16e80fb954c301857b01e7f438effe7bd7d9f1d9b77dc9d3fe6ff58269d59e5df21f3bcf57f2cdbc4cc43bba61feaec9b6e5e360ecd3baec94f689ca7ff694bae5b1042127e91897e3842438bfb517b16cb48c9194b7d7785cd6354bc336d879323a2355751bf1d66eff21d7393752a040f2103d5635c1a8ae0c790ac8de30878b64ef22a5bbdc5fe652e9668de6c7744b258d21e2e7e9bcd199effd4858c2956fb182175e66c579809dc0b0abeac765d1b637818b6f4f32510c672220788b7a25b4e2bdd2e430f69d893881d4ca51d274e36514a3550436939f2a9c0bd90ec3c53fc63664a4c3038714a5ef0667cbf5b318b15ca28eca3c3c5cf206da3273f211cbf2e9f8dbf5bfc651ef5021145594a72cda05965ba0d5800692a8b220f104db55839af75c0ceaf16f4d499025f029762a8276271c3413a21ced80f784aed850c286ed166c69744c0bec04b0088202dd3f3cbe6a7ddab11bd9e4919eb08510d48d32638a3c97d01eccf7aff0d902d7935616a35392fd9ff1ff6c3d0b05bc362c63d466f0f087015abe01b838d07bb5ccda1a6c653a6d3d912098b8414c63a9bc263260a889259724a895f4623008181bee3bb060e97fb40e9a1c45f92876d28281e855352949800f2f41410638338aed668bff597401d05a4b7132d88b4494adea600b8e511fa8103bc62143fffc39ba50b69a32f1811fd4f05c3fb3f45e312e8aa374494acbdd5ebed202d3e5ad841e35568e081091c1ecca26a4fe73c74c644301117a3faf662b3dd8dd49055d05b1691958eef1a7cb3e95455d8712745743d274070b67654a0f242ba1e3c5b7d94333aeb1886256290e8b0383326fac8cf4d4f6855316046979c9c705c4f6d8f85dcae983952f8a5a37466220985a8db6c9c79c81308fb1603f8afa5f631d01bb6b1ed0910498fe369f44cfad48fa7338dbecfff4aacf5b4af05bb3ab2bd779c46c139175c39b00d03844580f37bbd0c3974170a42ff3a77781003b754ffcd2c21e82f7461f0522396e0d3592ed83ab75a7ddabd88ed66f08e5b48c6124963897a829c8792771e13b8ae9eb53f7d2d3b05188705678a5f4a432cb55a7674f5cf23d34994b377aea71cb9c5b570cdb2c60fd22103c79908875410eda70aa732f0b9f997feb18761c70c9362f0d8ba5294b580f534d3925f35b0e7d3d9a1250d7f05830894ef2e45d06db64355b1fe17b0a388429ad61e864c93b72a1ee94bb5c48c02941a578ef17a81c0c965375f333dce1d7af1b58a1899265dfd8d866268ed80ddb500f320475d38a06459d4879be976e8fda7f657846a8e822293dedb4e89a93c7a965e48e21b58df6d4f019480120112204db01082eadea8d3587279845f21e9fca1ff6ce0425adda94afe106f2b3089ef95e088eff329b3d69b1e68ae5c78836af78e2af38f7bf3cebbffc7ec138bb4cba69fb5fc3354438a394d721b5b2bb9218d98409ca79adeb7a8ad271abdc95e036a34de06410d5187e480ef7719a17902fdcc970e27903b58d10171efdb31067d98f72605021ca9ba6fe8316d59588e410edc4e21a5fd58687113823b94f49b6f8e67d06391640dde45ece52890794aeef96927d9c6f826c584b880d8b25c0702b888558a8896dbad6135035c70c2826bdce0f8aaee34beba7df23a229628c9487c9b814fbb34384bc14e0f9d1a8b25519c376017ea0522201a9002ec2f0ba8b1d98903c7ce8387106e0485fc0bf9a25dc0c3028ee35e65d53e7a0348892393e599761292b3b4589d231998ce01d7d70813711dd4e8543bdd9c631aa84d51462dd4c85dece81a6baf4791c0f0c74fd7673827b6e8068f4313b96f4006b7061fbd7e7bba679b3c6cf1a3984d9d318b2faeadc4db92dbda621a0f8fec3b8887fb4332ab5dc64aac7d0e9521cd3a3370c6867b3cc5846a21a1649a76734af2ba22a2852ce15b0ab53b6022d3f6f5c2c5d52f6d20299302e737ab6aa4f788b785c19fdf6284ab9bdec3d55ca22c9039e83a809b2cae9ca098013dbf489fc3b7e8a3fd68ad92a2a47ba26e478d49f9c55c0427a9e2bc970e8c80465b30ae794be41c338289ab6102a4d108059e03500fc88a65b5307f4efff528134fc3bf3ed0b3f6dda14faa9a5218e194d6d2beeef229795c8534313b9be8a29e56f3d0363cef8814a31a13b873615f56cc4c4ca0cca8013ea56a1632e52de396aa9dab565b15538221aeabdd3094e22f9554fb6d84e3e2d39fbefd30adc835b8d0b9b7b96ac0ae194b4e2612a466f1078bcc59e50bf17330882382284735daf404e55a5e710cb2f92e8e21269efacbfc9171798ed1a39ad5bc23a59613ede5be283750d64b3eb62cc2b49e8201a62a2a3f255668d4f75585913144c832becdccf64a1dba7494d94716e6f695e73185f0529457e7c967493cfa6fb365c50df115c33aef0eee52c07ffe50384a208a9662347cce083e28c48bd4ba36a9f185f4ef91a3bd85fbf97b6e1c96b10f9f59ce03468035899bd61f9753503a4d74105935ea8c2cbd206ddb97d5a1fc1f4d4558041f427a0c8219aef47a1e05109dceb79e3eccf4090d4520f599ed31346247c5aee8e09fcfdf8b4d84cbce98bf32def5a5c2c455c672ce30c1f31f2d3fdc21e5be3bd85555052d991f125e4509826ab9ff190ed51c7b959d636456f20111a569fa29075d44c58df552227a5bc2815abbca36f535b5ef380d14ac95826688542774e5b7bdd7f8675e6a0e4e9ba85368164a4f508023811c835df08c85fdf908c4f5f5c099ea6225c7a1380914a33c65c5287e392993afc3eab4a91baf82c4947ce631d09648998f534f2d2780d4f4e8b2464cdabdc7a5ad5995735a44b080ab6b82ddab0f4f36466665929a9ff34565ff06159becd0f696d7ac246c750e36d7a6f7854b9df58ca295dad97a2066af1ca4c706b341b8cd5d90c5c5dd3f85042707b66e47405c19ec9a37a7e9fce015207a761c932f11cc6aad441341f29dd6b6833d490d4c6437cd36947fabf40cf55ca5888498d774033adab5d92e766807896948cb1dd4029d3278370a24e8e28c8884117195254238d531f6ddf04d634f8239745707938c581eb95658582266563928be82482a1f22c86c4994a14c3c12a01d70d827703e090b0caabe13f34cee6e1bf6ce3747ed3835589e7b1a0e4d0207e3bf03baee1a4f88b1cd9b9f229ba0f47be70e85258a6759d36da31e27c1f017480295217f2a5f6af7b026246696c99a7f8bf49122ceaed3c258514ee00fba085f6a2bee10d69f2574c27be352b5389b1f7e1232c055d8fc5ecba40e36785dba784f59696f39dfaeb18fe6e738f2896b3849a3122cf36292fb9ffa108ee0a140b0a5ed177c06939de4ef1db3b6708707d16b094ef7ec3059a454d8d443da37d1ab0fee2bf4c0cd2c31cc6a667840df26d690dc65c78e14d956605ba29beef803b87e2bd550e101b0706cacf77832b56674d5bc429d5b9fea85a919e12f74d64ab2851f54821b9f84789b187dafcd813a4d73f9bb7a869ae048d031eacb48bee13485b910d88268bf068d753ed9b9bf62d2d2bbe9229186e82b3a24a3aa10cdf4045484ba4fd0b0c96119535c5835fbe17d821fa3e76e016d5a67755f9cf9d98010b50c95ebda505f2754c3ee2d7b38bc398dfa81e9d4b0dbb3a38c0cf2659db4ac786a216fabb604639ca7833e7d39011c742a12c7dee8cc14a3faf8b43d2a29a69c6bc3e1eeba9ac3469e4dc672b360dab155c84db6df08ef16b263f2ef264bc56092583431252e1bcd0c7e4b8b689535d6cfebadd0eb04998ab505d93e3c2713d4f74ba7fa12f429b674c3f3fd2b11369df79667840c09d0c53259edb1b9e99ed7c9da418b9ce3346b03b80d8d5ed68d36745c12339eb03bfa0f9f47581dcc67393091525429b08842a16d53c57211e606cfc9d2c64923932bb2560885f8ed8149b09ffc2837ac7f654ea49ea2a03fc457fa3c98e78cb4113763aa6cf85a6559f4842269fa96b19026a33e0a93f7fc4f42405eaae74f2f9b3de7582af42db3ad747c5ee6a72b28e36ad50bdb5ce98d2f9043b6e42de9954e15c8f4f1686ee2652273b9ec7527b800e4b2279dc242d4c653b7b8934d6bad3cb56fedfe5ff560f2328160d4ea4f30704026f3974125939392d595cc7624476ae9f66ee1997cf0d3e395e67d5baa43af7dbf596bb3380341314081a3d82a0726006ccc1f4c5ab85f70585d7eb402f2a13535776807fde8cec59141882cb347633c198cd31e6a60664c4e749f18443fcfcdbedd0e52c4d1276440c47d8ab9dfba245bce3681f83f552db95d4e9e95883d007032b2638ac1cc972e6a796f4852c5f3b69b7eaa6ebb26ca62b54bb181533fd6ee5ad331cdb711227589ad63b43254284f79fe478acdef813cd26b0f86b3b093325f2412e1bf68589c401080d833fb97113e741d0e427dda0af9721742e2ee89badd512d9effdb9cf8bbccc1db77103974f07ac4512ff07e39473de93d8593be55cdf78c88fc91575aca8e387dd5c46f152ea3fd14ca040e65e1d7e48e74716ec3a4df6ea7604b5dba0eabf15017cf221eb846a7e9545953906e3d868f366017f08f76d53dbdc550363c6ac6f57e19cb34d19acaa6e8adf510967a1ecfcfc00bfa2667a78103d3be1fccef7d0a61a66ebe4294c7563633f99eb50c38c4dee1f1fb9558e67478e072efbc257a454a716cb1fb7bb1b9905c2c55a9ce7e0f0110fd853793a1c8082feed4e65462e3b9e49eec511e0da82171b0baec5ec8073600c5abec0d3e1d482a71f73c42cfafa8f1dd0d63fa3ed483dc1b8a87698eee95e6b9323dbdb1f2bbde05f48d0d9c7e4e596e30da07422f458b3cdb8827b41d9c9d3089bd1cf1e9dcfa429c6dd5c2f8fe5f20d151761a73267e07ae3df15aa6ec951b0f65d69db94139fb9c76ac6ca00db0893dc880b8fe81e8e1f900787f90596aa97dd72acdcb00ef67438046e38096398c53349fb95a6919efac8d03d14c41b183205d5568cf86f7e36ad2ae17ebdd3abe6ddb00ab775047d3cc8807c21cff016025ff9f145ea3b2352e975cddd8bae00f08a0c123ec9cf2f31ac75f45d30ffcf9b5d65d7e7e338ff5f400e80b71ba45eda3f60be08d71c484c8268b26ebeef92ffb3baeccbbdbe0355bd5fab1220f29c08eaa40c96a8aeb53fcdf4ac84585dc372e95eb1e6060ab8c6730b4ec04d05690966679167e79862304a8fb717f4d05432a9ab6082f7d35e8646cdb3eb69a31bba7868870b7871ed44826d4fe400f1c5c43537f80173a49a1088f8abdbf171f16e41368d3dfbb8c7b1cce0a85232440e33dbae5f61fd7ddd17ebaafef2e5e06617880c0054e000b2030fbb5ba4a02073aca13eab276c1e874c376e2a2b76dc7d4923d6905b65245297165d41a55e95c30e623c0b2d8815b5c9986bcd801706f6b9261fa9b0d26c1ccdb8e283639600a9d283cc8e4bbc4b9564ef7df332bc4f9905797a26e05e5cd7af38fd655260c32ff83a26ef1746433d23dc61259ec835af1322767f06703f2e6548d858321e4ac4939f8c2c77bba97c91677b3607b231d0c774fcaeb9394dfdf96c9a899341421c90f4e14758c8a9a048d125aff20669fed7b0d474bd44caf74b52a336a671c493db06c7c92a8c1f379fbe2a8382bfac44d244fec0e84cc0d61a7c30798093b78c538158017f884aa3347092aa17ea04a0e3151fc40fa48543b01e00b85d8a71f6aefba7a299a49827fbe147f9ade3a0f052cec21a0b5de196599f4fda1f050e6633fbf267be3c63d997e12c00f9c84e322d22c6b94a226a31dff4823267a748cc81eb9ed25589685e524f629c5c69ea8a3fde99e60f68bc92739b5c6e76c6dc1adb3bbd7ecab4897e753241a8098f1c2b97dba8bdb0adca96f9ea5e608509c4d44dfcb60eb73f40254aa577c1bcec7eb270f9d07b4e5dde04b9cfb41df49120dac2efeddf431725c44bcabc887a98bc5d5bfb7e873e442bb1dee089a00b955970bcec608d2851637fa5d0c11c88b5727c11fd2becd04cbd493c666b77eef24949390b5df72ce0fadc30eccd3027f67a2363fecbd4285becabbba57688e361fc1822ab6d1046d67ec5c44ee785a1eeac309296fcfe39b33c275e7e502372bea67d7e4b8899a573198e0194ff835b19bdfc91ed42687dff87fcce6997338d735b1c019194e72f9efc8aa4a2141f45371790d2a6a4c9b5b9dfddd693f556cf28ebf758ea235268d712fd9a9f1ee0e7efbc51f7ea49e15ff3cc3a138c00ddd0559e6c9d93f649937516b4d630667a2fdc345c1bb1c1d218e5bb96f924271bfc2c359e6089f136c68330eec2d46c3afdea2ef18aaf9c35f5b12281635f88c8a6ba24b62dba60682027f826b3117365b3ac7b37da680a707a9f955f98fa417fdb1589e36a471c4b7c7f66f9cf73a619885618466a4db06782bacf095a564ee030959ea4a0c4e18f861e0a61c657c2afa8ee3a97b98e67bcca8bff16d76769cfddc16d37d163f74691e65e79b790ba4d3624cd1f3afa601b50cd72a52067077d31a24145638b9269ffae4ff9564d2388d3f63f917a95d3407850b3cc464737060a4bc6bd79937ce2498fc21a0f56963bcc95b34e578621b9246604a6eab683367bde78ecb4edb56aff1e3f11b71cc3d5bc75279babf9bf726c96ee469eb290793d075066e2142fe4d03c09f9e4da606077404f91b0382baebe1513a7c9e78fbdc9fe4d53bdd50ba2e3d8e2ad7039e61d23928247b29ed788c7fbbbf2748197c4dd173c5576c6443d630ba44f702429924ff2751666fe2d34e266eb3234fe3e9714c66aa2bfc16cd54bf02b827a6f16e41a67bb87c8a87ffe9233faaf8c79274eb1ba74fe85fbeb8c2619975e415e60df508bfe7e8c8f4cc26e34f0367aa1070505f414fd9897f828378f7993f380e0f7b2d672dd2fc5df4cf200607f63d5a4b0caf05a001a9932a53b00a8370802fa03830a95e4ce2578f304746103c5d9b605f0d6bc60793165b214dc65e507c31dee160af1f207ca541d98cc67776368152f50ca6fb84ab326283d00f220a93e7c931e615feff41bdc56f73a73be4ee12531407f25887fbe693b0a4b6aab279ee25858e139838c1ab0404cd9407597d45c833aefaa2771760946c16f1e3879a9b4f29dd8c95d8e713f11a3a49406bbd7d6d828e0bfc98bd62cf1d1bf8e36c4725fe19941dc9937e89e32f0cdba3b8946c7da54063260ed9df879c827c92a529796c412ab0c889999c50729d28f042e0112978e7811e2a5c42fffec9c68668d577d89fb78229fdd5d97238cb33259b33c700f8ca52a25209ec3e10c742618e219105eb9bb6735cf6f1fddbab1bfd0c8b05c6e9484ce5599a819779a6b81c386730a87e39ff12023df3427bb02522f80456bb8e2c7bb6d948b8810d8a80acba3a34ccfeff4e31bd1c2671b0e555abbe6b498deb0a0d7d07ea096fef8ce84e4986eb8b7004141642c85faae28193616c2fe7b76b9de48db86600b8981c5f6ef747df083382c2f47aef577cb4346547b7f43ac0f41a7372332f2f3234289daea64c95c769769895c7340c38596cc9f5e31bdb5526d9bb24917b5ce6d354c4186632a9d6299e0c8618b5a2fe36eec5e37f2801fd6d84e7ca462385a1ac58f486fc1c9d1832ebbbadc41a3fd0f82503eccf798388548c348210cf69f4dabcb6f3e70c332acdf7a5b54a022e98a81fadca535bc7440f4a6e2aa361d68e3cfc733f679cfb2120f07f63cdadf67f5bf748a71d19d664e555f76a3c7e4614c97555f232439625ab73c0d8feab4f534fa91354acc90dccfa886b3e71e3454a0ad183c0a486c219122dad0c7488d17c88f88f53d65ceab9834eaf8166a982539b69b8bb82bf0c3b30e9a2e61ca91202752277e207319a35691a4f347bf1d6fc56190a3e43cb59e447c41e2aaa2054d3fffcff323a42789e3f6218a4c4162bd1e574121b69294acb375c5da3b70b8b010bd8a629ae64054ed83d63802813bc8af9782146efca9565d7d32ef466cce972964afc1b6e1190708292ed48ba12085844f6b3a9d5a31b0b2641271378b5e5d216568db0e19c735932d15ddf7ec63e91a59de4e3797dcc628ddbbffa20af29563e64e78313e4f2656c6f57ac414730df464ff451e603e5bd2d2b8bc6ba62eb3b2e1e710f20faf2a0c232f4f4c2053dafdb14bd6e4fac6b212e81d23f510a4755e8dab7e8f543303a1494be1ed505b366f28eb0a93c476f02605e0ca082c928a91057112c11d02a602c7a37623cbd4d599da03434f1884fba8701b9323c642f0dcf983e39b5f682e014eca3ab06d1734aeb415a714dca15188dd88b473b5b06c985cc481f4e96ffc4d54f1774d75ffa9a7d58fa7bc53d5bc7fc4e465a9d8e105900ad074a46ad711521a3d4d5bf278acea9a887ef789d962b17d91dc082783d0314f67e4952dc5e0cd507af0d98cc92ad91f6e2d1b14b395c70f22494713ae3cb38ed9e6028e859bb379fddde512295d8220cd7d16968dc3d7db1e81f6575a7a63988839cca6ad3857f1198b2054c4a775f756d2c3fd60b922ab2b34fd3065cb487ca5b0f19a0ec198a45fb2b4b9efaf8dc82a260a741c148f22acb2aa7b6ef0909eed5f7292bdba20195ed81014e2582511b88c7c30b7a0334216555d712149fa73c7cf034a6363c3fe1e18687ccc22fa64160ea2165933a4a7a09033a2a4f71945ac18e70aa68be25493f37e19d9e2adbda64a9b3db6314c47461ef63c69d024d5d1c9bf5ed32a350ef6787b722bd483e131a1eee097f49508e45fc519b2c0780bc95f0caab77379de21097aeab9e2cfc5720f47fca642370e17de94b0830632b1ad3a28c98c76776a019d3708d0a6cf276777330385716cc014e04894fdf4c0b52cee7d30bda21a1dd22d4756847447b57975ad120205f30ccc1900a5a0b9b439e46428b168c2c05350ee1c4061e7c4de9284aaca9b0e2ad5f19fa3314c26ee93a79b560a91645ed7a6774a2af802865dcd6eb6ed4621f96ede841c6434803764c3bcd27cbf92ff7ea5b76d77a40246c274cd83f37d7f3ed70481a387e1fd6deb790004dea14dcb062f3f5975943ea801e12887f364a76a8a0ece689b2c1d589ad60fbf6b1e3105bcf3b111cbfa39f6b01af73e4cc51f2162631e79202452c805423938ff5c1685e26ba5e0882f6d9e1eff0c465a5f571f3a181a20dcaacb49078dfdb9bc9aaf63d25d46c9cc683c352e40d1e19a929bccdc4a6b5125d2a7929477d57756cbdc1e9a3e72655b5029aede61c395a29f566ee33bbe66b0217b04c2597e4cd0218b436abbbe6d024238d08e4cecda778d6e3e50a5754689d3bb4aa8668f1bfa0a101dcec9edbeb730c6dbc1e83264ab96b6ee75f7ca748c876f72e3bb75f053c879497bde09092dd653237a79515b4d856563482a13e969ccc9d37a8627d7227fa78a7f8a17939bf92574aede9e7658a80dae2439228f95b2e3e2d26e29583da6b80d3cdd3d23f025729a2322dcea557d531583ba9112dc0e061bda12a9dc07b420d6f7d9ed44e0f2eb8eef8fe34a96180e4ff7a3d51a365a2cdf8add5a7569f0e01a2398c1413d3bce43134b0a43db1dc09133ca6b7b4813b7bbe553aee80265f375a0e8b0219bc710255e9c52199b9de6547aa4da874aea2d5ed1552e3cd13102ec625861fd9c0d25c9b7908440c582613b4a061d8b19611b0b25eb7f1623a2584ee9f5a64fb9165047756f7edfdefb841b41844546f918e694e2c46b278f30801e370da56434239909ede42d6f5b61276759949c9f9ba347ac972de480de83d49d55b2c10749f41da528f1750508b263a956a5d3d3b35417881bd9044a97dbca8c956d3719e38f34a9767082a2eb3a299381ee90cb65485f76c76848bf3f47ba156fb9f9f2f0c869f163b54f84583fc84e74c1df5f0e5c708afaf324d24dc20ab3f8d0516f30df92f869c4a4100e318abba7540d5ca3bac2424cca56c053f0a5837810712f9462fa8ae765a5139f4005d0b460f318aabff72a651fd6b4700946804265eac12461b7703386c04fc3e4904925df87d8d7e35e9bebd192d8fc1c04854d81f9b94d4c3d2196e4498f85067fb1f97fe7030262991fbefd912909e6041cb7def18c7e8ae85aece40418ccee42cb1f1843327d60f1ff419316584a2a67771480e89334cbd1a6b33fafe419f10d55dc34c597003f2ba75043be6a725f83aed41ca91c8d709fe780145f4201a76a54bda0cbd22030350e89a3116da16a6a6ea7b763eee767206d057dd10216c39bc1a51dba4d007b23dbf2cd6325c8ad3aa82d6cae6a5287806447dc143273f500998b85400cc27b350c27c262e0e38f0a50033e5e4a9cc95587d6098fb1d2bcc104df3f1f5e8989c8dae1a48540532045e742987591e2bad48df05036d30f35ef3aee01c10d983dbeaa944ce5f3406a141ed59d5fcb1561397129c48323a965dcabd89127ee444b01ecc83aef5a2466dd2fdc4b59a5ca3abe50477f42bd881bb9abef562a7761f9eab54c04f6af33eeef4bf240d158b0488c4c19376ddad59c4c01e8d59bb022824539e7a00c67a8ebc97a3aa9f582c3ddfede0fb47db07f297a6340f0a8e9651bf416fd3ccabfc12f69ffa81ab0cb04817cb566ea7c6f2e98109d278cc6552492bbc8b6aa67b885e9f38694f970f7504cc35091f20a97ec324b8e912aa3af3d03701c0137826aac74c548dd55a05c75323a0f950efd6d29717d2ee7414725c5da0660791248fee222cd0b0c02dfb61597e0ee9e51b809586e01c1390c60564554f1b9d0f1ac86a74fa59834d0ccbf386a0cc6cf644fd97b4e795990e53a5cca75f7a86dff1ef33b8deb8be9290a7da49805921d3ad50db89d1a7b3ad1c7ae03e2ce9642f69993c144cd8e5965c9ada5c5360098dcb008d84bfb33e10002b73616e34cfc076ec826d8cc02879b42f6fc94e501723797dbb816627ad5c48f7e352794feda0cf77fc1c65e4dd6e4010efa061c09d56e7ed6e8e114676b50a14abbad14cf2a664127b9199d42c543e5ef4692b9ff5821da9f9b6891bc74880d07a0953d504ea3b5d27fcf6d062d1ecd58f495a60458a4061ee440dfb8713b34cb5065eb18392851c69c52420a5b0615ec80c0ad1e54b7297504273aae6a4c434b2b83ebca82f80870862959315e11292a5a8414c99d6b1bdbc7ae66ac0468a39ac90342357159307b1580b2883b6ff98b4e0caf6f8816f94e94a0390493629de113ee80cc56670866aba0ff70c43057b0921eaaa3a313cd090d8862b60ac688c16f2731051c1844eaa168335b6e884fbe093f1ddc0c707b714d750aa4909a91523a8cd9504a2e3a20c0bbffb753ba4212cbd3c3e0e0ebdb6a6be818a149e114c5fbaa9831ed830d80196a00c4be9ab8d4e2f3c75266288936cfd0b27fe1adfcf500f4d2cd736165e80b765598297a57f28ae75e54480c96d3b4a39206f9f900f993909c28697b553601a150d33fd921d36425483618e00d07b953ca7a5eec7b9e0cbf187b6be811a159d0d30bf94e3e03f209d3066dabc90a71c92ab37bf6c8b1726ebf32adaa58b8a9ec1ae9d3b0d5df3a504f2df55f72c2c4c7d24405b55f81430a339b74e40e4ba5adb599ac60b1934a785ddd2b7a8140896571ad23c2038464c8f087d4d3c81902a14b282695f3027ca72add8388c5d21b8257243d666eee44ddb1b36afb76c05361bbba9087ec58f3a9f5a993fe467d2b8fd674a66cf05264c09a52ed99342145c9615b3b6e2eb85a6e2ef96fbff37f0286fe2189325627e8d9338f4805d089edf52626d7be864964406fbd39ac2ba5e3125592a7a338d4a5808e9b3961de474701222ed554ad18bea31b4286b9d2f2f2a19e34f0f71ce6136cfa5849f05032660014660e830cd012dc1445ffe19ff4c158de1216496446ded1793c296578baebe6c4d2e07faf117cb0a52250589914f2da356311186b53bb6bc2a36f2e43cdbec8114489f9e1e128f0eaeed7b59a531fd45e91f423b7a26e3960ce64cdff14e48898f806366b1ffb6a00468e6095034103464d86b589ff2bca59d92467f245fdfc69b034df08ef5f56445abd9f50cbdc19e80f0e55c6dcfc6d19ac5bb598e5183ec69d7767f5666599eb295c1ac3bdf2e735d86f094955746592928aad4b2ef59cf815107248a3cb00d62141aaae8f9c3a84322e25f1802030ad5114f6b4c7f20ae4d70e1ca57b2725e2ea060e5a49fca82bad1a209e6508bbfe56607cd1ea35ceba58d02c45c036ab4ae8e5710400b398d01e149323e741ce94282775c1cbe664cd0cf78bc3d3e3038041707c220e96dc40f08e2196ce00e3a9c734bb965461c169cfc91795d7063c9ad56944c24484b530c129166c027bda4edbdf7de7b4b29a59401be0976083808ac9353cd9e9740ed11037f37087ed23e38a1f688859f341d4faa3d62e2ef1663e2276d76729eb1573e727cdea8f6b0f7d159a07a778e3c69201557b7b97570cbac0320ffb02ecb3f9975200bde9083fcf31d1dd6330e1c30182c76e3062c0683e1c071a29a33396e1c7ae7ac4c26dd9df31da24a61e10db7cf5d2e3dabcc759ac970bb7c5cb7d9e7f52a16b9ceba158b62076db893797ec5a29d83532c729da8c6ec9c7356ba52d9f890509c89e26c0bc5ccfa4c0c75e48c0306c3913fc5a29c43aa830bc3ccba8ed0d20c7e9073ce3860301c19fc3399e40330b3fe41fef9160efe65d669fe60b00ff67ddf7798e5e0cbac73907f7e7078457b9bb778f0d3e79c33ec200c070c8623e74c2679107a99750fb2d65cb805688bec9e58c4fd070cc5a22d003828166dffb1c71d2091615907aa1067d63bc0f63f99836fc9b1bacdb1caace7c85ae726a918ece02a1c6b0f1c27ed66c57cceb3a393734a4feb653528ca681f168002a954e32880cdadb3f278904e4ac18f7aad95ea8626996c1bda27cf99e78c757bbef29c2e9d3d653cf94746ea107aed613faeb85015ce5d69f5e34f0d975e6350e0ac330093a513b0b4e795bccc4f9a5417327016c648bffa9cb3e452250a971780a85d239aea140e58a26d57e2eec46927416c192597a62d658ce145470f863e51b1b0ac3d4b2c2eb858cec04e9b0cdc0e9d6a082f54e28b022c431688aa3d4b1cc850b5b09a325e61853eb488c81197d031b353f278d153e61003570e3678814eb0a8180d3e586cf070830b8e4d887360f297847e224727c50f7705076b3ad8ba0000f8464606101a1ee810e703271d51661b0c415c0d3bbe1c84b804a0fb22002ecc00c6f0d89e00c14c8f2a7c64f183734300170c6d6200c16414603bc30059d608c1298814224e866d032dcb01b61f10e079293242842d09214c8c5066081509e0b020c28502362f16805fc0806d8c226518d9c106475c20817bd28028231a1cb0617980c743095d906c4440e00813c444800909909980190aa0a9c01516d0e2026d30f045065ea0013cc606c8e0001a1de021ce094e1e8842c2705443920d870ff48002e60202292525588204d298149e4832130215a8508a60e362052f580869ee61043d8841821ed4e8818cae5645ca55f2a20564e28416319b3e5c4ac3e446dcb3d402a7052d3770e173d2c483a1e9b3c1c9d3f224e5c40828259e744f4091224a29650553c0a062c60b3eac11e504860f86186e90c1d352456a06303424514315ae89ce4a1457bc2a6cd8e2062fb0747b963f8081c30f64e4f0031a3a401967a9c5cd9ee50f37e8b2dc273b6c5378d8828593a187eb458b962a5f36a53ed8227eb8466ca94870b967800869a64fc6596e11b3c970d72d68367d1844b9e58a2e21cd5bd4d02e34080a04e542b7d01fa80f540bed81f24077a059a80e34078a03c5426fa036d02bd40aad81d24067a055a80c34060a038da22f502a740a9542a35028f40985a24ee81375a24db4097581b64099969828d5483002165610810a21484a0169891210a0f0812447a4074ee8000736a0810c60e00216a800052620810898000124253cc001460d20e18891220c588002882460c80842442842c001689b284808062800902102fcf0d103041e03104000847604cd747ce001908c7ae1e1059c75f686da4a3fc51e33d35d457a2a7a818728b168993f188b0ba071e88efa5b430a640b412eecb20a75c80debcc8bf16e3224aa219e43ddab8cdcce11118990606fc799c443ee3eda6e0f762156a1ee6e88bdd4aa30d64becee463bd61280d4623dafee7acc5927903a91e81eaaefe69c75d634f0efbd07f1ad9a5cdad75e9188266dfd148be6d0b5b4d2004e2a83fd5662a9b2377c70ac3db66bbf7dd364bd1e527f74496ae935d3e6c7062a6e6c90e2829b2b6b5ca141b7512d464bc616639581b5026705576220b45d79c10ee32fe3eaf1c5f89663b86779858b2cae5841668a1d5dae33ee4721acd4cba8ac041ab88ce5952c54289c999a2b51b212ae4071569e5869e25aa8426f996ddb368ee3b819a2700933581967e08267909a82c50c59f00c3e34c1a2099e5d51420d6f3a2c35c0296ba001951916698466694a19d29431b86056451a5f182a2aba1b64f8029732bc6046c5ce9e659518ca2a4d68b629a680e14dd40da228226c6226862e3786219a988941871007331886336270010733314811c312294881d099281bb80045d40c9e405146a9894243e687bc67f608c65946899965d4112fa0a1d2c5940b9a28f1829b5bbef0c54c89d59e651497282d4d4b44e5f0b4448e3dcb176cb0c73dcb17c0a863c488f91123342ba2a4c2460b9b4549c58a8dca1927a0d4134ebe259c7861ba22382fdb0f984a96a74bc586272a559ea85079a2d2c2940e0d318a31a24c61c113a9295c40494db1028a14b76739e54c9d22834ba3a9062e70cad2a40627dc7456baf8c270b20418181833c41a6160d0c50c42d4104319058fb809d700d54df9d24319a58c32ca9319988e8627700aa6e012b426103540e9f095529e3c990921658d2dee594ea161fb01bc1dbdb7c746ea78fa88fb778bf591fd7634bf4ca4de9b9b7ea9a3f489d9974a67523321b66a032f332f5e84bc04f113a5890da0b0186f9cbc3650d438e902a58aa72e50cc38e952425162df3d4b284868f9d12234fb014a540950ac64114b285252663ef4f024ea095409e504ca05a129a2c062b6eb02ca666bc289a7c56dc249699b70c255819b70e2546736a431830e0a0264bc18a18316694c896206275f6ca5131840cd6ed061fcf62c9d14b1c96719597b964e76d8e438db611cf72c9dc0d093c6c66620b15a40fdbcaec92c90fdb474d746210b641f8b19e14ec2a43560d2b8739ac802d96fd6dace02cd1891796d2fa5df178469f12908f668fede7655f6c5fd7473e380f6a5145cf66bcfd2ccd49c7a0b1313a918fd3c08f3415f8c731fdd237a3c841eeb9008fdd535b479fbbcdeb269265228cc4b5da1d8fcdcb121b7f4a1ae22117a1bda9c635b6246dbe79ea209760d18555aa06d5a69b440f3320b546f81de8c732bb92cd06c02aa8fc58c6c2761d236dd8049dbbee96981e683da4c9a1bdce68ad0951fa1d1f5cbf0b57736c30c66348c76cff22906fff9f62c9f58507d289fb4944e47ccaadcfb11cc0ed33483a17c9262877b964f4ccc62289fc438cd6098c9308bfa89aa325b69207419bef85219b93d4ba72f9bfc65253871f9716ab2f19e65d31a9bbcd0396e0665e4b8321568a229a149cd78abbce9a87432b8b965d3174db328a3d093262eb4000b16512e3811830b61a4b890830d38f62c5b88b3557b962dc8c00550dc38712a5b58d33473ead9b36ce189eded59b6204605b3a69f0a52f108f7edf786650b58c0883922617ba76bdd9ca25d1dadeb1ed3bad7365a775288a375277f4cd188a3795bb6dbbc853b087ebab644fbdee51aed4e8d7cba0947eb6a4e4b340e09f77a63f2aa71c47326db66d26efc4ae90de396b137af88771dee1b99549f54ad97c9ba79e0ec8d966ec62ca60a764ece324a28c7594c0fd2a9a9da23e73c94a3534aee47b0d336932fede9efdc3724d6078d7a3da6c91e982647f1ae1ead8647df05e05b4d0ec1c7b714883dd59eb524ec1bf73a1ed9f37c251609373ee5a2c6bf34d9b3d2a2ee71e9284bef82e915be4a14427fe469f122c523eef44bdbbb7af48ada8b21ceb2c1106fa1b5cff67787d9de7e3c6108c1675d27733959759ab7d71efb23aac7bb73a86bbd63fd870d7f58efc7eed9e48fa8f0944671e948a1ed79da1bb577f26e6f0c0a8414dab85672bae101c27888814f4feb022e3dbe97c9a4290b747b7ac6118ef72d098f1d9e4cc2638f5f9d14da5c7832a9b2b26bcee83b5148f8a35193747b07cf8945c21dbe262d816ebd3cb6f79516f51d8302093599c2d2f60e7e78db1d0b1b1f9cb4623c04dc9cbe63d01d9b5e72d17341c20b12511b5c81734271c76f1229081e6311c4182b31d97e1f625dfb8e7f2f88cfc4fbbde045503f714a4cbe5fcdf43d7ca89b9eb8dfc7eca1ba8334226bb311fc2f7bb9c9aac39f342e370942c3bf796efa096b16ee2d65f2d11a46100e73da16f62ca90ebfa1b3377416c71c42c281e3c61c42ba71c3c61c42b261c3670e21f9f8c4e610522c069b434830d86b0e21bd5eae39abef9943483d3d3c73088967ce26d2ce8ece1c42d299b339849493d39a4348ad166b0e21b190c639ab5fcd21a4d54a358790542a710e218962388790c2212410fce610d2f7797308c9f3ba39abdfcecd21248edbe610d2b6e1398484e76c22dd399b48d6d6398454e7acbeced91c42a273ceeab7d85b3d972c135a2f1990f6501adbb40c23a53c224b50963065182c5041584a1388d977cff2082c6ae89a103b6dca54bb9174ed490b9f73257c235a6dfb146b58d76ad3027137028fb7df1b8ddffe4d7d17f5953252bde36e24fe1ee37f376a1d1ffcb273f0463adf3e6a1ae57d3c4b8fbab675bac6b9f97eefc11b71bf1775cd5e4aa5299a9a94d178d55bbac6b4bdf51d4dbfe0e7e85a4bd754ba268affde690bd4a48cba83e7748dbbea2c4db35820fb51ff7ea56baa4fd76a9f91dab4405246dd435d0375add3a3127b0bc46481acb54016ce54944bc8cd97f11d1a8e66d9e074a3cb0da74734347c995a4bb4764d5c3f46a130b2ae9b6166ad79c86d7aa2d56eba6bb69bc8d24cf72b3709427b95814dc50e956fc38656a25fda75b777b0c87b2b9374b7be4cae003ba973925a391232b7f7bbc57b393956ef02e77e8864f4c76ea4e32f5d938addc675bc6643d79ad0383429fc987efdfb0d55dfe1519de7b121aa87573d3c19d3aebb32a94335c904c9e8f51b37fa5f52484636fefaf5d44a2f5d7bdd86a66a5e474b82ec7c09dd54c78680e739781eddfaf89dbc84b28680d7d1b24dc5a2159c39be79e99abd05b24216c88e87d16c82a6af9b60e33cda8c9306804f1accd69e4e833df65786caf1d89b6c5091083d08c66207ab88a4c9462cf69812130e0efb8d14f05eda266042f2d2359fbf0ecbb5251aec20169134bdcea483c360afacc4c4e730cde4f31bbfa19b9e72fcf598911c87fa39ec93168bdd464c3fe5386c72f0bbab26b98b26e8e72cd1a658046faa49d53d4d2f8e5a05bb49d93eaa43b62dfe836cc402d97b909158207ba06c010b642fcb2758207b00acb0c90e72931507fa29c7f57f72932034a51c9a05ad270dcc643da23ac96eee5527596ac58a15fa653fe5c75eb7b0db5328ebe3c6ed6fafcae48d5cbbf1a77c92aad9b406fb537eedf5a77c1baf0cf5bacf6119ea751bbf91a15e875dcc47cf50af4f0bf43c693e36b2a7c9240bd32a6c9bfe95a5ae5e1264537dd47d4e6daac6fa88b9b946d0a8b0ed7ab049109acf6d1c6bf0b0dff807e5a39570fce1b43e9e9a04a1e1b88f37e79c739ae08c09ae6cf086b73e9458a0d1fed833688791a46194582ce46e4f937649907dfd84e3361e821aa9eefa271c879df44e52355b3fe1f8eb9f263d9d6477d52a6cbbd35b7068166e5c93dde8d22a6ce44403b393898d87607da48fb6c07eb7bc8ef1abbe2ad3066f9e531b9659989985d7412a1a79e94e24616a4fb1c8c84b57a65d7596782a2c0a905ee602b3eb7dd4d8f53f3a8ed8f561ddc048bab6cb045e76bd8fcc882abb5ec79d4dc0596957e3f515895dbf6202d273f182a67a7963a5a0ae141a2c55c6568513d309f1c6abf20d21a682437c117e613262d75fcca22ab3fa0267acc222224ccb4d0e1167e858d929228c104f11687a8a28c38505cccb8912c08ad8f58f95e04c47c4aebf4f09c2a835f4e9421b3f6a0f72b442c75a6537ca6c602465bb04f366d7eba8a51729bbe6791e26b8d689ea14774e547b6253f43951c5b1e553b1e8a7a27ef7916635f9b32b185234c1449b4cfbf5a0a89174fdbcdee6f8eb53467afb23fbf0a7f6f8d1640e9d446853fd7ad594295f8b5a8d4d93aaacca6ed8c011e28073239f60238c18cce7f3d1590255460fcb6ebcfdfa2b1bf5f0b83a179a9e3c02cf199d9c1d6e47ebe49ccc1a5b5b4b68e4a1125778a563d31f51067ee10d35fd7c3aceb3455e38e293f786db8eb3cb41555e8461776b10942028425a64e0ac207532762fb08b19651735e6ec83336fc410420f82b5522b82dd5593b2a2dae96b6fc539a76d0a620d4ae9bd58b62ad65a8bf1ec02a68b178a8f29bddd5c24e8e244e57400679d73d24fd102f1660d1067765dc2da0fcc998228c38c2f3679d7ec3341a02008306a04a125082c4148d9358816bc33d000c128206e00820b07486a054a0a65a24db5246befbdae15949cc0711cd759dbdd7bb9241aa8b2eedafbce765df729165924dd08f75a6befbd5cc6c70aa8b28a801f171299cc474703fbfa24f1193b34ac74a3eb32a11f96c88e60c422b11a489254abb4494a558d098261245dbbf688e1df8df127ad8393e3f6bb37009cf4d931ee7773dc274d567bc4badfdd019de4b163deeff6bc4f9a071fa1f6887dbffbfb3e691c042a9fd5b1988f5c958d7cf46bf57195c4354e98365a693289cbba88f6bc975e3af2d49c12a441900983cc1764d69464d4b852261921c818b15d5546a689edaa32b1c85a7b2f1932656243ec26992ccdaff1043ece4beea990797baa91867c46363ed212da66e38b30db10d96a4fd28d978cd9b0bda4c3be22cc36334c2e3d41c6cbd681e76da6db6791abc95a4c84f94d54afb8648526175a60fad1bcd54b6a9bb47bda1e829c18a3904481c1519b4e61242b6c1c45a144d1a8bb46a98413a5182e3c6e18bbde314a3258ca27a22e1c7a4b409b435caaf0eb36a702d44981602057870c757e18bd93a110581098f1de2704a8d36a08f5aeeff8e0b66d0fa8174f42bda897b6973fef987ba7667b79d6183cc1b4b977f4c87e567104a23d750a74177520f8caf6ae6c9f936fde41b3b9c788dc73bf9af41e3b5704e3e9ba91bcf79c80b3b95b27dcecf017f69b7b6ef34a2ce285178570dfb99777deca4721197e7c4e0cf6b95d1deb5d8ec1fefa3c7830c788848f3d7c4cc7605a84a5adf31891d7592cfbed3ef73cefafac7357d6794f2655f7ce938f5a9fdc773419de7b8efef169e523eff9cb3fc2107bfa7b9753a0db1efcee0782ffe07c48df93b60dbaf9bc10b87692eb66701ba3c3fb262ea03b772b825095ddcfa9ee0a38c1dfed6e9be5acb55673f4cecae3c7be97ead4dfe625d4356773da30f15d749b4353cabbb5dafbf42ac5f976d786f0dde25ff6203123f93d29f528f5b890e46e3f02026a8feef63aacb5a30b88917e7a5d9559fb201fc6b9ef9c33341c131a384ab0eec2a9156a0f4ad636f7a9497b255c469a5465f5e1deb24e95e12b49f2a1da5565415323793457034ea2f10b51982f15e3cd23d41e56d7eef3642b1079199520203c3d39d11059fb638739d4138dcb5569d78dae5fb68530bef435c90f5a65ceea83881875aaac5231922ed797129cf48571962880736febb69dfb100410cd213a87e8a6dc11d7e31289454b689ceb537be053fced604eeede7ab77db1f16344ba6fd779eb5b26bb93db5597ecbed3d444191b0c37b1a8f52e8ff7597d3b9d7346ce59158b6c45c0779a5c0a7f445184a5cdba4fed71bbdf1355f137c75acff9d4f94efe740e5e47c75a8f11112fd2a0e60c00d9dd9e2727b3deca222c6dd6988f56faaa7c24ea4d35a867956ddbc34dc7865c2626bbbb734328d3eee8c6224c279898c02e2fa17190964c384c4c3ba9b5bfdba4707f4a7687b424c84edab687e43d692b6281dd1dc792209b8a0958d2c16972bbdd0e7ae208447b4e1de417525184edf76ee19cd15f1e73ce48d52e1a3267f44ce8924eed986e410f0c2d78ee734f8fbcf7265dfc39758380b43a317e155e0cedb55693dcf157994719e3eade571f3fc5d57857f7aeaeaa0b37f86111b8d77349b8d6a515b55c942870a3ea3ad5491d7cef05bf5d95799431aa1e923faaab4ab27baaf4e78c3eecf2b418d3d1b600ac32da755dd7755cd775f73ecf3b97bd9fcd6df73c6fdbb67b9bee964cf0cb28fce08cc24fd0970dcacf4f10f7feb85c5d5c231365302106136b9a687ae1327166cb6a8f0e0926c4f8a8341b54c558c97ba918708a609c251352f62ca7b26c01ec598e19635b4003dd2d0aaa629c5546492a72b4e6a02de3fded829a18f161468c6a8f4b373ebd32268c7d5f05382b7ee7bd83733749adb5f66a926a926e22916f7376ee3bc7b845b4cb0a1861eb98b36a64e69187919c6fc8a9a6ebc6f122b993562a543fef18368b9a33f6f72c91107382375b68cff20447ec316bf6077b964ba0d92092da63e277dd3924f748a2ba3c7af7529d1e46fa2934d74c09d41e36ebd833828cab7707df913e36fe23d341f270ed1177e1a827d7a9ee6a65ad95816098b9306fdbc3fbb56dde0eebc422efa0e775a0d779777361b661ae9b2a1106d9a031e3fdc765cfda5a67fd06054f2541bd7d77c73a3cdd778e431d34c448ca36d639c639cf39fe8f90475fb14887874727e11dde8a444810274e1c347bdcc93af9ee9cf0adf0ac1c7ecc2178fb2ed424f80d1cab6cb4e1528bda34c0ef344f3a3339dd6cfab3a9fdbe8368d22c99710eea2f1f71ef4e43f2d3a4d0feeaf45d587d8c3e58b2fa31cfbcaa3a1eea805f69f23b47bac60dea2fcf4d392d748500ed838c18c77b9ff60ad9b9432b1689a08eeb6777dbb98fe3b88dd3dd9220e3eebafcd34d9ab78d9261789ac9ef902e0972ebbd7462d5f689d0ed4de669f267671f8ed3a40edde8ddbe713f8fef62c662d1d6d11fe4ea0ebf1fd60785a25142412818e7c711bad1e5e2b237c5bbe81673901023495baff5ad30d41d9875aa6cc35e4ee2dadb7de80873733ae9120912274e9ced537bdc5d6b5556c7aeca683767219971aca2111c0d1262acf73e394c8ff8d1d212850c3f5c497eb424e19b8483b4586a6d9b5b83b4586a2bbddba7a236b80255d9674feba3155ec79539906afbed765b47df6da758f2d177269b66d2ee0da476bb1dbb23d0de6f47e03f91ac7583a326936ad781dbbdf86a122fd536ba6d14cb26f1bb7c64cf64df7c91d07724adb249bc7145c8baadcef656e049efdba6491a3541bd6992d3db266ef6dda59bce57c786d0df22f4f74828140572bf697b49d6712764fb15b7eeb2f4d86d611e3539d708e93eb1c803c13cf7ddc07cafddc11db72ee67095bd1359ba21494aaa6412ed3e7d74c9ac43a15020780d39d71cdd6fa7dfde515c414a528dbbe3cf7b2b4f0b744cce351b7f03bb7fc7502810aac9ef9eb7e1cd6df7debbb94d53287407be4ce892aa8e2640b3c34d4f8fb388b1b241d8b34cc24d125fb66ccf3209a89dc4d4067778fa338758dc75f66c82b067013a0ea60ff0cbf5f54862309705c1d0f33c2fcc47dfec3c2f0c5fa75f62f751f8af087da7c9babf8727aa96ce2cacb53f569aee8fb4567bfa2824bd935693176f4b35262b6cbc052c30e24c9318b65c41850aa2a8ae6c19d326aa0d531a4be8a06123031b19b059019b2deebd3b76c0dacc0e1a54e082cb1316fc0046091a3a2a90ec1a356bdc4cb1a9114cdc7174d924467aed9a3454054e382976c884146c9a9cac0973860b35d600b1c11cf6bc55765411da820b7c31be343466b0823245b0f1024a67868d102d3653556ce0650d2d35f0618935d6e852c0e6068e0d0f52ba30b808830ba61d3e44c1268d931d336cb1c69a1748b005182da0d831650b279aec88d2dab3dcc2cc1650ec90b203ca0e2818632c05638ca1f0ec596a41841634dc90051a38642186942cd66481e60325c66ffa510b5b4ab1e73f5862ac65137bfe0331e5127b7e0e81dc86b76ddbb66ddbb66d4bb3831b1a7cd12587285eca8889aa200c4e64740146142d5aa03853c375bcb8811733dabc81c1541764ca266219258506db0a153da8c1e50657b0a07b028b3328166840b14135c801c6932e6caca81203369d1358783143100d9ac61a039d2d60d8d1640cf72cd350b1c3290d133b9ed29c200d1120ad61677de0d1de90ce3bbbe9cd29ce1f0f4ca0fa273218b6d0aef5549f80c6483fc27d9d3540cc780e11374e614f7b2bb52a72697f01ebd7b4b36799064b53cf9e651a2718c400c60e4e51bcf083161460d1c5c3020cd4fd7f8ebb26ac7c3c3ce1eeb550601175b1a8d2744518ab2b6c3083156058b105973328b5e18a356964a046898506563c61c598d91560e4809ced79f2677b9003888ceea827b3d783f4117f58a07a12ef793a8118a13bea4998b38a4fde15c8790653b7da24655a9190c3364281502cdd28f3e06eb25b6d72b4fa225183825bbac5943161a140e814dd515fdba841ad78a18939ab27690ebb0a608d917e09bd00d61845a05e689b393465b95aeb69166b7b90581f9f40f6554d0f8ea9c3fee620538725daf653db32794d6698f16346694596b20a2382c8b0e2cb0b3228abb0a18a2a33801304a78a261ce26af18558bdb43359497a7f846f871c71f7fee17f07bf4ce3eced1fdd2038456b6b772ea9ba6ad326d58da92882e0e7ed4f18aa549fafaa9cc71849a14d647d048931f250a9c2f0f334488cff541f736aae3e7e3eac218c9ff3a178f04eaad811f1e08d446da4a2713658c322234da29126279553a8418dc6ab1d0cc02ff6f6895590666fc74b58c80d0fce1ac2ea24f74d2cb27d92f6e2b7af32d4694b35b216c24ad76aab33991584fa5941507de299c32e5bf1f75ec81d4caa966e5cee5ec5a2ee134f1d23b22f96520565ec1558ef9e3cca18aba55badf5081fbfca2024c4a6eb84c04e889b29352bbaea446e953b0be4ddb31b05d7de7412d70ec573a73a488b91fcc1719947191dd771b6be8a4482bcb12a166b6419b13ebe356aaa5b44c273b1d811d77b1ee3deddf66425969411eb3a571a5f93325a5d47d75a5fbd9673d57b726c7b4f6cd3b123e3ed1fc07a8f566a29f5dc7ed2d46cf19c5256ea79ec48cec79bd0eac95259c9953b0be4fde6696f6cb2a302873dbb14c447815ca8d2b5255a4d8aae9668abe9810ef62cd164d968ca6c3176c47ebc093d67bdb33e4656cc84d65937caf99863d43a4bd728ad4524dc44234daaaf94d493747347ec5937a1e7741cb7d7723e6a23d6afaeb5ced25624121ad9f9dc463cd7e9b119dfb36e643f3e288b31545a29f55ce52adaa4c0abb292ea3b42c4779abc9be3b873bb23855c737a625e42396f87ef9680d968751dae735920bc6d22110fae08e20a9ee7c87890314e1a8f96aadfd14a5246aaafae94f3d675b492eaab9332234d4e393ad6b5748c7352e9d8f758a78d8c629f3ec2fa65fdc61e30de7ebc655d95c757d15b8dbac611d9952ae9824b5e650d31c5384193b7674945952e69446931861a4c6a58d03101a6c9146fa6704393b2076b56aa9f349b4948816eac5370c0a435717772128456cf9da8727702c598fdf6bd1a71ffea3d13ea3d5da3b4dad43d8c1df13414a5c58e78ba064569debb1b7d6288892cd07d988dcc32e211a11ad4867b585f754d2a76a49ebb513da76b52f635ababeef4b440429b6bca75db0dd27a410c8149030f52bc55cede493b9513fd6afd1ac0e518be9320340feb23f49e56a29fb42526daa7d8804933526feb9644c5720488c65a29a6aa684489c93df7cdbaf0d138cd74bfbd8e31fa983edaa69598e0dfd18ed955335d3d2d90c5126693e3cd6ca9519aa7c4a4bbf70028136a5094d6bd09a57577a13b372f53a7a5ee39cd8426556b914d934b8e92e0578d023ebd127c7a229b56825f7f330a98da59af6a098d8302be1a85fa7a2676c9d6aa86d01938e0f77df105f7867b33a788b271e61451db942cdb942c47490cd034b5efed99333a4abe8d82019aa6b635f3e628c9ed42aa33b366571a7669146e17524c5b8893c14c83022e87a926db9ea51456ca28b49465c204855146b1840b416bca3371ce9c31069c3f9df5517f4fba40fa39c1b97acd3529a31004b591ea56d7a858f538f220e3fed34d55d7a48c787acef3daceeb29fdbd11fdbda7f7b491eb56d7ec5dda8805e24e754dc802713cba46a46bb740dc7534f855a86b52de6be1bf7bda687515f7d5e9a90e754d8905aaba46413d2d10ada7f5352923faaa6b4c54d72cf0da78d6d428c4faa44d375ceb9346395ab8cac9352923f02d5da334f0abb3b252f8ef635692320a35cd6295c2f368b3d29a7eb1403cc818b5524d8a8449c3df3e699f56c237d2544f53c0250553fb0fa0344c9b3fdc6ac9560e88a02dd86cb185150f82f7de7befbda0787aef1559e238de0b8aa2a8fa15c14f111441711445d678a20a8ee3388ee338b258add63bebe3f5d6c156265da35095b56eebf82f5d65e32852718e5a3cce2c16164f1031c6d8a51297c2f054960f2af1b67e652bacc9d541bccae291ead4b5319e26c05658a54ae2529565b8ecfb9db28c15d74ed8c3b3dae8576519a8bd7a584271c6bedf61a9be8a81ab71c43ac6d23150c7b491cf61f778cca408ae34fec6fa8db10eae56ac1b815ad4e409fb7e458ae756392a955e81fa28fcea44555cfd86e1d5a46ba7876747b55aa956ab202edebc11bf3a61b36e3beb03f6f1e098b15ea9ce3acea1e8da57f599335bfaa56577257189b48442cd06bf7a3de7ad1c4d8e2f55a71abf164bcce40946ad7c04a65de3a793b8c4d545708a4531f1ac079cf4a8542cb7953d4d32a56844000080048315002028140c88c40181482c4dc32c647e14800c8b904c6244154a22418ee32088814806a1184388318600630840082169ca46001418201158a2d577ecb846043cf14c7c438620918a066b84dc99b810adb5b4fd522608dab7562a9c3434482543684e2ae184f7720053e0fa25bbb099c630e86ea9e253d24e22cf2a5520fb06205a3b3072bea38bdff101383d761ce609962d3c15cb319fbe29163cfd34a72b841244ec1567e48281e58e78dedd6962e25c31f5d174f9984e107ef44b6d9597dfa579430bb81e119fd5e5ca611fb0048bdd20c01136cd6520cd1d5d2f7680965d00fc0dfb2ea7be4641e0fde1537a128c00e5a801cf37a823355a3366cfdfa1373c326ca6440f74b9ecdb7a7fa4cff0adf64c808f8d8e820dea28595f6a786a5019bdef8f85b0dd0b91d8ad6f2c15b117104d2e9800f1e4f54331d4ee7fb973f5dc16bb608988c721082dcb6c09157725a4d5ada288a1613cfad6864e2b49e5e6d61884974da39bb45d670f293140fde30e14e353c9c228dc34b30adb9e86469172b7108f28f01d92d6ad27832cf6c4bd1e7f026f380318cfa1f1ea6857111a25e84f96a5eb799640d1836f89bda1c3b99c4b4a188072fd52e4225eb2eebb54dba0bb4e5771a738717151e011e3e7822503212650da0f504e75b55b6d0672bf9b9e38684aa5d5780923fc4ffda11e942e141755b0d4c3db57c73aff42cbf65bddf4d9ea6226e14a93d5401862af123730a1d581ab5ab4540e6fa0a46e2ba4cf3e1ae5568aef82369a1505e04df7939b6f3a3a9ddcf5bba07370867aabb44bf228e799f37739456c8c5472c455441d03e73192f4679c9155d22dbad5bdc11690208bc3a31a55c2672d0bda8157957f8dbb8e178c961c6e803e4b1fa72f35a52243dfba9e6142873785c9643631bd9acd931d0598658d2f025e1ae4e36e6c6bf4afe3e3c0b4654e3361da24632834b6c04764095429bbad47d91ab1e92b163266d15577fb439948d46f0e6b55dce9a6c32dcde79c6521aed209409fe2bcdefc98b4cbdbc5b23c4cdadc6d1c3b8faa7ba3e387c6b1bf7f1193d675f9479417aca3d35c84145139815c5943950309f8b5767069cf5a6c828a2cfe29f5dec48261ed07d416bbb18931b4a70c3b98aa20924623461b92225dc71330e809ec9e420a6cc45abab3d36c9055b31d17ce2e59a1ec4f3fef3fdcddfbe8e784dc7a03318a3bc16f6965cebcabadc0f45734ba6c0a7828239a31d0d5e29b2c83ca48cbdf03a5c1c0dcc573cb47f073baec4a8579203466d78788264cdb333e353061ef40b0976bcf502fc2a95a18cd877b695c574000df50501315e9905e2a0e0d0eea9d868795231005a79b337d7e4c7bfc50de8315579b2ef2cf93c9feea000cf22632f0a3bdd2f7e7db25ac11a9f2b9520e7561eb6794217968bd43d754cae53266c9ba7a84cbe3e823e2d4ef9df0afa1d6b7fc9dc719a6933bc89a3ea6075dd69131473f3a15c54f8a59726ac4cd86ab6833b4c77f4a57ddd59ccacd97f8e81f52dae0a6feb685684bd4f798dd99eb0b492d364b22304da8f2fd6a6f342a3ee8cf2ce8959f6078cc9615537dae85888671579d11596cd552a32ee40982e86110cdbbd534333e5b4b0d4dd821d83fdb99434342e0d6a4220a5d991cd6ed3f1bd32355552cf0455d48161ec5eca4394e0d91c76b41c0f5e86cc5cc808471854f70a0e83bbc80176579824aaa1c05a121d56bbc80abfd80d42829f246270492f334d6590c0213fc29e1630bb7ae9d2169dca6a4e62624281c0c7cf88ab8d0c95b4e12ddcf76474c59a4cd54918fd262a38a67a4ff30278d39a4cc396b0773f2845a70d8dc884a7a4e94ab1f1699e044458b466b8f7926b6762e5cd44647bc7977d9600407386f4764cdd8d3fc5a6141246a6c1916c75713d117a8088d5c0911d5589e13a9f905d9691728878f3716430500992e97c55ecf3aaf48426eadf3816bd7015f66d469fc836fa6dbc8bc9763f9b34929ee790370359082575303bedae2ef7100ea15307c9124af2b487f4c36988dce6dc4c1110ff6cae36a44dfab582f39bae77ed0bffda5875a43d70b39df6e266c190b6664732cd8a59dd5cf99af8d22c50550ded7e01e467aec7174e46f0eb511706ec5bbc1dda545b963ae2be154d4867522a3ff6c4bde7ef707051ab4777b96d2e3796cc529c578f57007023492e112defce4105e17836e5dd9faf2392c9e152b364d24f29196bb3eadb04e26c8b120eb458e02d5570e745c8989a860cf2bae2d7c527fcd78fffa2a0ae4a0cbcc3e5dd689b4b15e4395bc90a990fe72c06c49d8d57a74a838c16f50a422c023201350c8e2eac4c65ee0b6ea579a611859be6da66b3a8ebe99366857c2cac83d6303b83a1beb989b578e79c505a0896315688a114a406c372fd4a36d40ad32c24d29f665b90db211a8b7311484049be543d6035c27f10754b1bfe2ef3dcc57923dab2819ce3c2de0002208ccce3a89c81f3ee8d88645bc2c3bfaefddb1c286269d6aaba2966de2f305a97b90c64bb67ac0e0933cc4cdd861d27f8c54df2e4d775d8d5ab7e48b825ac5aed491819c0a74168230109b7220a1ce231a59dc2c77149997806544932a2f59d4eeaeb15bbb1d4f064949f863e4ee640babec0d969ed3c9f51a1721015759d104e9b6f83bdd3f2f4eb557ccb8e9ed8afa8e211a48defde0c42e55bc44ad102611e5104370eca4a2b28bd1216debc0a0bb659fe6a8d4b40f2813c9aea27cf7c006386f049395bd50c77a0a83fcafbfc79600953a9626dd3ddf0e30ae36e7827ce8aa93aec9a146e04ce289177cb2acd8d38aa80d2e7da2f6e6bc688c46ff53b5494260bc9ac10ae3b9ad4bbdc0c2ee156e737dbead3861741e58f12b03402c1bda38ac4b9ae58b312b468cf7e03e7a3d0769724fd7a24c2b6c37026158e80ff0fcb65a31cb1031f471dce4f2719bd4cb56773b013326b18cc40e1b982a374ecf29673c75364da7445a747e6e2bdf2428c8e7137bc5ee923c60526eaac1d7d89e77c983a9469a43299272fa4662f8786957720f988e1e49071002f79c4536547ab25f50cd969ff643cb502b64b0a7e70668926d1b86d0193416498134febbeb97d571d89e79fcaf044908a129f82e9b0e8911ec39bb08061abe4e224f8487ff0394b22f2f46de9619bcb45042bdcd3396b0be0d337dfd1433444775e6c17be52892cd9d65428db381c5879bbcd9699cc0f16190c1ecdf217d3761136ca413878bb2a874f2c5a5ede8c4c1940aaa66946eac4789163c02dfa94e3245aad9b124897c8a9098e444e66e1aa93990f1437d62b2d95c62fbaca8615e7c0ee5ac76887f2eb9865b3dd9b837adf70c69bc516efa27db985e04bfc04944d5f1d81d6b270ad73cd80b2a72c4f96c243ad7c57014fced287e3055560118a88f8c4f07c266528686030f458f58dad06d2e6e5040b411213eaccd3a820bf7d82fb109b8af28b7b86361445f0488940b6f7ca48fb0fe93c716b6c3eb253888830e42681674514552f80242fb5d08dd0752374e9eae6ac68387d7296c62ad4340909c900f0c25bc84b6fa74884b502787256d75a6a74d780ea423fe456ae079f0da730ab43b0970d0e55514f9acadb6596ce46841effc8a0494421cd83d84da3d50952ddb4e9f3dd2cd4f7d13def6a6960b3948dfad36577137c589cf2240a95f60928676ac26b6f03c1a0771839728595c2bcd42e0c4b0d205f38d120e534a69e4dc4df04eab0a3160a6b81b598f620b8313bd4c660ca0480f4a5313b02c324a169b45a5bbf506fbd36b269bccfca182c9edd56139f1bb574c15569ffee7097b54de6cd526308db0392cdb9dcbe6586a013d4d54b0d800c1143cda812d4014206724322e23089c2cbb3dae362557b0d5fbe0db21a4b8cd069a6bb435313c514b6cf1bc984758b9054a5b741be6a67da1e3f13d3b2657633b216297ee20db866a7ca34ad6c9a011286b180d5e89d450854d21e6e26f06008ed6b486bd635e50f6a71a04467871806996965195405b05586ca45337cc5245210597b0770d6c9fb087f0841c896650604d4a5be54db55b682d726e0045e34888c6cb3f44ed21b78e5b7583dfabcf1ebc46138f6b59a30f816ce2ec9288f7d129591f422e1264018696828b2e5498d2738403ed8f280b071c74dafeb0ab27a5bc22b70c1214301cd0abe059225c572581aebe581c7b71ecf2cc7481f0acbdcd3e8ca0f02c829f2a9312c5bbf4573783cdee6df8aa512db5e418c11b9840b975859a9ac3bb962c74f92abac648af97556b435159b6f83a4cd98651fcf45633b749aaefe26ad15ba565d3b922c927538524f2e0686e6be7e3879f6a88436a4b798bdebaf461c335563335d7ec4d4ef810b524d4c883cddd8c1656fe480be8f63bb03569ddddfaf0c2880dc1d17530d9a832a5bef9ff729aa5cbe59c75372846a79ba3102d47cba719215c25652ee3ac264fc1b496ae668d85cb7083d4ddd77c79280f4f5cbc19c5c1dd39b1f92d3acb8d83d865927e873aea288941e04f51946ccce313c62eee9cfc26130ee18bfcc5a02f4ea19eb3c0f2f4f740366abb1fc199d0a603c90fef13c3b3ede3d4422b714dbc150ca278f50a2f7132e0b6c3d68dabcb3af5e2bd93ddd8c013ccb24e3e36cb483ab1f5fad65ae8a24a135aeb4516cc29349de02b2334fc42e92ad9e106674ce3d4d0287d80293b2897382b954f8f040e67279e4c1f116b6b5b777b51e2a0bc80438dd2406c2da00cb04dbaa2c3e9ea9cb7315022fe5bb03025fc85e4b853e5fd49f7ae0f83f83addf427630dafd03c5ab391734f255510e166c3adec2325989a51810d715e031979de12e289ec574c04536aa56cac1a1bbab7de94b918738cf103b427c5751e95da33b2a760d41148c411674cf6a0542954a44307ebd4bdc65a653af4f80165e60df92850dec7a84588959b929f99fc9432ea48df5a593fce625d3e2ef433243ece4230cc3d015d578b41f183e8d07f1e999aa09e8beaf6827df7cd003e430b11a8a89b2097b84662b77b1a24840537f1906c183a34f7e997689597db11e924fbe94c22e443fafcdf2050040fad4d9d8ff043643a136fadb908f8edede1bd265b7cfbd621eb9676d934917726cbad248a53a33caa5bcbede8cc2901136bcd575940e08b076b0ce328b516f9d496a21ec416c93249275e8db92d267eddf462c26b36caff620ec9ca86b48fdb624a6fa65c2b86b59345be98ef171ddc0927b6c6e009c42d0a32b06721b798e82b8ae916435f93fd95bcee28a8b64cbf8602d11f48bb5bf78f10e331701629ec2117bf278912667d8c628fbde47f0ded3068d6a5c4c575e7b581e920ab8341bd7c0d6c424059a490321185c70336757bdfe1df9c4ff85f4e2d9cc6dcd82c54375667da9f20bac3464386d65da88e5a709231f7115be4eec5059ae911adb158b5084448aa5fe3426e4a2e492af50ab050a146e7b89ded71c1d5938fe3fef170b84c08ddf2648905bd214df6dd94398980d2c6ef7ca0e8f35a3b4d2c78dd6b95fa31a7d9fabb35f215dfadcdef395905b1389aa782d163e49626fcbb8b8187ad981b3800d331f2a384198b98d4c6e095db5e76c31e0949667014c98efdc882d184551cc1af8f608d2c18aab8063491f0aa27206ddc796aa0891218c583e67588164e6ab23f3c18160c143ff67638d85bdfca873ea2b7a95262648cdb8a3b2cf64c0db22fb8c848d455c6f575d2e9af63795b44b10eb773e293757b14c4ed552517f9baacfe780932df19c18251201ef67d5a56666e4fe7e06c3d622fcf2c6331fbbe5790353ad622191c3145436e43e8b33b8c45c09db93f80b77b23ec31f25f6148c466f41e4d270b04d4c0beba7f6a7788f79c9e7f1b4ebcb06634dbdb20ca283ca8a8596b28053e56bdd4723943562e542c2c88d73689e50b096d78c74f04390e3eb10f54097a91ec29928a9207902b73b037408594195ae0b9658f3cac217002e5ab71e5b448a32175e4cd164e355f11ccd26441964dbc3db7eccfb27bbb65679789a10ec109edbc60a2e8793c842d7b0c0a2219ec550c8d9d04381a431a41e1d4e5c7f9f9f192be4b2f188a223bf0f9f109396eaf57f0c55a5e063b34114cc9df22d8ae2ddb49e0c360079a10bcbe59624c96bb6202a50bf6428c4363d516ca8b33e750614ca58e1bb72717060220fa2d2cf3a2d4e72de9173a9eab61f8f464a1e56edc1e1d0280a2f7594edd1abe3c6ee2c8ed41938d4feb4d50574d49b1168e2bff7d74d76e790016faba103c30900278d58d5ec2327bd980dca03c7211585f13d78f06df3a7f54beac95a8fed34d49db5a080ef5d4242236ef2208d0fa9e731b29c10e500972d776aa751db54ed50a79e232b253bc4652656eb74386e08c5931e016adbb3d46d735d1172eeec6ce23b15a4de1c318861f5621cefd514f798118b52766f7245f93bf269f7a6eb501837f38134c470a4fc591c54d0fcaad7984f767351bafcf222cc1916d1dec2bfa0f188b2e62586309ee71268d6e4a2c8307f44485419bd5e3e36a4737425d4ffa2078ec0366bc60bfa4e0d5179b05de949651af3ff17f593a07442d2f407353f0dea9154f620deb7927b5854cacb40566bf67c592d5896012f2b8395ee4cee2f65a723ed0c776a90c2c24ca457be678511ec67cd7a19410112c472848eac9d020e5bf6c0d88b2606806722dafcb323325703efb9b44ce6045132c3a5cf93b355d1d4762dd8cfb590df85e192b8d675d8e54b99446e915ab480321634036277003cbe49fe2606f5366955787a23ab085a59306b1f3d3aff61b5ea80a2095ba25936e8b6822253b0155ae797262ebbbf49253568035cf625925c07087d8494088f983d8a3b5ce31749d8e42ea0a01b4892c48a6224a17904ca21798c4ccb9f12b3cc39b9d8f756701c0d66aba6c60043a012b81f6bfe850ed993211a5df900b6a8f473d762b778680b7a049ff91ab97d520cf4924d5b01d6bb42c4466d4d2c63725f83ade648f3bea9aab74f96b58812ba31876f8d054f32d07ceb63c4127955087ed1b53b5f6b4d753d274d1790d3795b994a860002f97f369ce6042a8c434111caf0ec191b8b171e505abe32883419ca136ed99d77e3429cab897d5b2a46f77dddcf15e81e4c292fb8130e094a1b13158b4d315fac1cb56784f8bfa8cc4bac21793adfeaf43e3b72f3b458d8d81066fd9d636d490f4b733ef160cadc8d890592727e5fa6a393977b0c57696511bcb776ebd7f976f322db442186ce6fdded6097697a686535d09cd98c0f6a95f6885d761fc7f1f105d377f85584261f750591f1f94249139367836d1387dd25ec235c1d700ecfe4fadf44e911faf1df6bc5e12eae2961050a609f99fa4c3f66239bb980a2a492b5a64eae164324b5d7761b49aea69c8adb37d832e7032d0a041d402201e718e29fbfe3eb633185995e1e848da250b7686500c132733b40c41605077dece7c5fcebf10c1b83976f9dc912cba624a08b64cecf20df211a3490bed2f093edde5fc6d7e4957d9c74f09dc88626a6136bc3cc55f607909a1e9c5af38d4d9ef16ed6d92e2600ee55d130872db418fcbbe23a8d37bd4bac0e688f2d669858b4f5c258a5af97b26e5440b13f7ce517a9eedb4d6ad897403979644596a1d306f02a277e1c9c76d89e9353c1d7045ab90ab6197a0647a433a6c21218795b57bd0d2427962d057c1b013c2b03772ed100242fe40818444c4398293ffd87422fa2c9f83548d9169e9e7b578569a05c89b245e22f59f4c55758e942fee57c42db33d874993d496263fdd146162674343e5852e65136b23b98a46ab0c7a907e3c8d175e76fadf384bc60993bdd784fe919f585f6d105c39b299b80f39cf4a4b44decc421371c1db23d7cc5ba70043f73b4501a47e6e43e9cdd6853c182e69d57b657a370a80ce30ec4d81dba877bf005bade09d84f4e4ce24c3d47d180b4a1551b4eadab3ca8c0bcd111501c5ef9ffceb915adf368f364c0f4abc48e0e32325eda3e139b904a2b01293a88c8bf8da8b50b2bfe4da64433c66b50a7ba47d56882b81ebb733b8a4aa5d16ae8de623485ada78d5c87b2e001c783b8b84cb4b0c2b8964e985f04e0218c4e8da8fd5576ad25735fe70371e065101982f061e9197b75a5fb87bcf7871479166c1eb420f96985aec42e9f56c01acb10d7c091a188caaa7758449fef068c420ab9fa7299618899fc4d3a0dcd00b8d7537f400839e8e28408d55f9a99fe6d0050a5de6dcba337544c108e2ae71e883e3aee780b812f88edddf472073c377c5452969952a537e5de6372da1c2689c18fe038a472ab5e742ad61c7d4606b30d98d7ed0843d7ad4690f489d901fc333a893a3e8fa676dfb51494a94665d6e2f3f213b8c04f516704a216dd47c6cf2162085dacf9c06376fe60f9e688d35e4c97c8d20386185cc48f556f1cd395c7f3a492cc2e136959a4f05cb0be84708ac56e1aba75129f1456d874b74f1788db8c8d1da72183a5b24519d0264eb7a8ea863d96b2603c76e592c25a71cc0d22b7d0c142d047a867e43d4fe21b39216dd2a2d4ca4c8cba9f5a6bffb1799bcfbe3e6e5e41dab005d285e6a744d39e0425250b4528244b80eda2ddc4d2686dcdf4120d9027b14bb70ea2e03c32a68d71cb586feb421fb806ff089b90a9eb5a809b96ad9aa8ece3166c7f415c2dad714fa14c6f344155b567ebfeba835b82bbb06578a17959ec13189e44807354fd6eac811dc674ac748c28b3b6b20eec3a97d42d948f8456814512f440eb16344d79c5dacc02f9e5859afe90e051e8cfaca03218c6695845968181991f5869fd9d57fe7ecd53f5cb2c7141dd5ce9d9646954306d07efea29fce502535de8953d5ed681b1872241eb7589ca5c79f0d42ceb08794c56b7ddb869f75fcc8a28245ce3198d4875b4739b02ad28384c65b8c2740e43914d591d6eb064ce622f95feb007592082472c02a5b44ac2da580f5a4ca73485803f4e814ce40b65b71586231de1829694535980c02ba85566b9e54bfc34a81277f0bbef96e4df60546382f8a1dad379549136133da853646684e36bd9993cb6692668313700ebdedd683a65babf356e9436bcc67e513221dcafe34a092d113bb55223644eca266129da8990c3140bae2af9f6a452e28b04298d988139225646683112c64be939c2f7e5335eb328e265744103723aa3f30c0c4e1e1c929a6c96290177cd4e06d2b44a671c0d8b45357cd1134d57d81acd39bd0ebf62f3765038bf621448d10b0bdbd858b1073cab64a6c1bf7c39ed10ec1ad36c946bb09c71a66fae7a9fb5a013aa2f45e4c18d3a3e76d9ca9c381eac3325ec67f2eaadff6c01ce2fc0ee08744a34d447fd0a52467a8382d2ec745f8bc75cc57ccf53196000aa4f6da565cd8faa726d0f6dee6264bbe46400bdfd2f6ec23f1972ecdafe860d76f397bd135d3a74349767d5d7155dba54c409c15e6e3c71212225a1b0f2039acbaf8883a7d6bc0e09903a2b2bf518ea391e897e4426c454dc570946fa7440bb3ccb62e9225eed0ba25b35508f137627d46114fe3afb1091d85f7e5c1a2d50ea3d4993aeaeee101d7289e74b8b40716756b27ae4c9aa5e3519d059e303878a9cf7054222af30afdbe743ad99f53058152c24586b06133f836c6e16c0cadcb9ef41fe50ad84f7829f216e08a0aa25a3828c992d92da3c72a1eac348f9a136935711fc83913dbef91e4121f4037749868819045d4c97e9c8df1b348e8ad724dc0f160f7e9ac50b7546b45fe6d72cd91b88b1180b80e7e87b261c17cb4c3fd172dbf078f71cf1cd60114342dae02810f2c390f9cb920451438b811c93b68fb0d8dc2878833930d27172ab53ed1f6176e1bb20513727b5ae557f3adc57edf1b7800238aa988699de524570809d18397ba7ce4b30944a91490846d550d0ae8a762cdce2df65f7915191bd2f275245026867150a18a51223585513afafcf13481921fb3f61dddc35b4e5454d6d11762f2306e4c7e0838f05542207a1254bba376137115c24bb7a4399ee50e2f7dcc668fb888ae82f37e2d594ed567387abdaf284d7ae0e4e1c56fd75d8f5b24eadd3a94f107b02f02d37c226f679348cf3d3c0d7a42491d21d3f98171e75e0c208a47ab4b4a32050c3317757a4cca29fd0d8c7c672e94eb02c85696f8d985d79c8b21f624c31febab3153aabe275b1729248435e6438b9f7a8e2e424d5754d69b44fc73afa6b1d23a7f19617c1b5a7130a902a267740a5f7e5da973969ca48afa4516e34ce2e1ad9b4f52a6ef197030bf27663a34d673fa6ced37fc82a044af5f214149c2dcf99b10834866512b16db42ea2f93ed44376f88b0844583791ca04057545e66cbad13d2384b5ee738c47546b64c9c765ac6cbb626e52401da95f315a172a284a8e4eca5aaab700d9fbe90101144068ca09940837a1a5ab9f3f494801776bf5a9750230c98be899d98b6d8587f7e5e2915a161287170c9689b7da15d99a01a9a3eee6422deaefdc265cd65504531114fd30d800225efd3311195d0975b448cbf4928931ff70f1fc1fbfffb33f8937e2c85c0a241868329d434182959e123b420dc5004580d120fcd3c8eda29a1e98ce03e780881d06124c559fe0ba64058d82ebb831732dca707e54ee41bedf0d2f02823877d015d998818a3a63cc845f6afc6c82023ed95d8b90925c6f7a18edd906da0a5cab62c85ae9c04c209225948b3c4bcc5c53ebbd872fb3bf281e5b00721f9dd2344f0c0b5a344f0916d02e4ed0ce40a38e6af79f156083b35e1116223d5eadc98ccc9b3816b88257e6187a0372709f26f9f2256fe744b7a49885e41b434e0264b441aa1a98f0e4d915d89856d1af9179298a7654851da3b642d9cc4131c9a52efba9e1683e772a793e4d2edea95b20eeb2883d212fa42b4795be85e1580f71ee2eff8458e3ff2a1b49fcc1a0b4da76e04ea6ca9b90507104d5bcbcf62318b2877c62c24cc11eda5c1dc9c1179412a4054b4f2028193a67096d2095256e9589791fbe1edd128ecb805beb82a37639c6b0c243b351f93b0fb0a892061eeaace2fc0256c84ef0032e3a8951d27d1550897b07a21f5e2ed31e671fa198c76d5bf12ce1ff6f109c79cc48f8015af107f77f2b862a9a3a12e48e4a6b4bcb74968be534688958bf6cbf2f694aba2c0e50068d2dbc9978acaf21e33c2534a48ed044735b1a57fb75a2abc68a317f0beb8e799e3aecccc5198f7b39cfc1f330c66583ffa7c3f66122d40c11cdbe53818a97856daa051e6e2b03d221f9fec210822bc8705fe26e20c0433a41ea1082d8dcd79e0adb1b623c78257aeeb6c79abdfa428b436ed940c64f0e40ce61e20dcf14895f1c0cdd80a7ee22a09d3fa91316219b1ac3725c7559b18873be6c730d65e7e2ac1c2ea6c228920531adcd85e66daa411384fc76f780ed06837b331607c8d0d1e18d6363e6e5075eeba2212e985b6af168ecd439794c183d38cfa6b4142e04a877886f80a578642a1a55ee1573d5ffd61d682ab2cc57dbfeb234915dcd13bd5ed6a2d50a8022a2eabfb118135086a627c27016b1ca17475ec31fab5c73188e67bd9c1b540984bd4a70e46a62917a4500f0b5d058c995c74d5ff2676f897d837e0cfc11fa8a408061feac5a8724dce74cc41c91eab62a48e2be25195d66a7f841ab135a56a5bc9e64ce161c3c156cd9bf707055bde6cb889c28afab349a93832303c08b6f82b7a7572d80a0c0efb6edf9123a3c5a9513b53b13eebc8bfa5da07b2a5a72f7391849e472820bae446d0808d5bbe4613d4d8b1c98adc4e6206d90840ee460170a9cd28ebf41cc126b54227adda5dd26adba8fd874dde316852a6f27d7441a2a3221fe46ebd5f780f21d77abe0916a086e4968c964efa937f2f2486e014c3094dfaf186054babc7864af938623ed3c0be3602dd20e37b11bf5ec588c1e1e82707d9bdc922baab90d8ba78c96e0e45556a40425d639e3e4550ef3c0698e541046c47b85917b521ebc18c8398516c0da2ad35f0ffda6fb9abaf0b8b6f5ff2636e5b5086537a6f512a2137c5b209bb94f7b092102067e52426d02d87622c3a86d81fdcaa9b571fae25c5f50aa7f2111859b9bb6b200f838fe36486b935eaf20611f27381665a97afa43920ce3013b62f0160dbb52e64dc9bd31675567b4c8b97419fbcc1bbd80e342847de462911897583dc57898ddf4fe3f9d2fe6fb75187b1021ac35c2c10e4dcb3a5cc2b15911e1812b921d79b3e9d1f39d5833c9de929d0d06ac6c9b71bb0caea6ba90cec9f5b3977d83fcd0412289c800703161cf40b7c89740f7fe1f03e226d97a30474f851d402a88cbad3e16de4079f22e0baef28003cd11a8a82b8f489fd9df0c9c713fbc348202e48f3e24bfd4e8094701159a97f35d378c4f926f535a11c251c7372b0bc6b527fded5c4f3de11d6a120bc44f0525858d99ee036aa3062ef140ab6355000098c37c14e167713f485c28ed1be2e35df98fccc84805f15b74c262beb35c9c148656086a540e2b027f491b6e08ba409b070ca0386940c1cace44f2b4e4a81e4d38b404506adc094402b6beb329c24e578844e0b0913398bfa3741f64e371abf3bc30b737e6cdf1da88eae298f859b5d5937d55cd680bda55811f9f24731b4997168de5e896c1dc83032712044ce90294a54a5d703d40ba73523021214e29292fe4217c53ef313c286c0861ad0b424b76606c183cbfb0af1cfe945b0d117e58ad30bde837887171dd11801ec082574f8331e29ca3bd752582cf4e6559644af3808580304f0c2fd5343b69c9807705fc98db600d52c9cee43af5de176495ac7ee9abb7886f31a1ec5ee60321c4096bd7fa513d82d4de573ad6d91fb1dc8a57ca4d24509d20997618879a585f0a06ee639ca66ffa8a78e5fabed09cfc2042f5ab71e2960df79936d1346a4a1631ce44993bcc6fb53bd2efd5770853b081dfa6944879d822abc8984a6bf1d8b4b190663a001dfbb4dbd2379ec5c21a0b1309713c0a63ce8e0db91079c46525d0f27434e3be2dd52b9dbac8d9dbc5a0dd613d5a3fbf8bd279679fb019ba519fc7704766bc44d997eb98efce58bf02a2481eb83ddd23259b14a9a92bcfa0af23e4739264df77238e2c25a0a26c3fb564b2a6aafdbb514917559c7376c07c62c5aa3da013b9a001b7f19b0db3e6d8fc0727cfcd26be6929f721f204e40dbddfa2ffa45797b08f870f6a3f0ca2738507d98be8a80c2ffe0b8a07cfd80dabbb48b9b9ff9b7eb8ef113973307e8e2c5a50f261097d54c0e435f6418148c733281cc31bfc9ea53484ead400d4462927cc812c5bf2aa664bee315ee6b409900cc7e71baa640079768b7aeed6f41f22efed0792493dba054f7e43088ee9332c1887af7d4e2f5edb9511294f70c4a1f4ff82e3b5fdd0bb3666d92fb93b65590c7b068cfa378dfe5e653f8be329e0f86d60775bbe4fb665c6a20be4d0c995f0e1fa060d1328802fb0846baaaa88fc736bc6d73986c94f9234ec521ddf2fd9fc32aa851b38adcb575cca8b61bede1e81bc531ef2f194972bd712e8a3b807bda5175799b2f9693261316525a824f99b7b0bb95483213ad8c153ed0da04c353399c26f23cf90aaa2c0da4db07c1272c4db047acb8e39636a9dc0d61e5d2bb3b4ce8321146e823a6692c3609fc0b23ad060024c7c8301d99a05f7c418fd2202aeb1724ad969fa5ac66761b1a4e0dc0bdca8d5133f2a5275c4f6fa6362f428a18149a8e551fd49a90263665d82c88363c201e451f79906f5047ecdd1409f959c288f19a5170cd15a8265ae0e43a8c27f52811da31f5a4292ae01acb9e990e333976597e3324061ae3c258069a8bc1696b102014b27dc607222621c7c2c37d3a0185c555903dfcd69c12cf7768ca6643a59887417ff0cd9bf90fe8d3cdf14ae074e56cec4eabc51e723c51a35008124d0a4234a4240003701b3ea2d9639847f2b468a3324650f5a537071f70f05c3ade0bb071beb12c6cb7ca73b24bf4d5e6f4910a8225e62eafe155730cede643cd362247d42b5a200e23f7b6fe46492f911b01f910c05456c0b210c0e697e4a4ad432094599e1268a7088cc5ba9a6ef48b039e418ae512d86e96e86e7e50608d3bda774984e2faa22e2703fb2d1991c2a48c4da7b80dc28d722d83c3369a57ebd8a96c5d88deb65e40812872d43cdbc2707d0f70c67063f06c878010e11b7bb58f863cf545b81818dcb14d6171edb7e4a2f48ebc81d9d1113d95ac78be7aa964b53b0630e680307c42bc2c1fefe16b8e0e748685d9719368dd1b7ae53774e14bc11cb22e6152ceff88ec343e56462344d79785e60bad0a081ff058caac14814e8596b977c7f924260b502170e58d0a3c387b50861e4c615735f69da69301e48159cc60fb238ee2b378e51c2537781bc32d492a0f57aed8e12f296e20da5a001e9094b0e312dec8d0bcba0a60a0efe6044ad3a94553266559d86c1a3a8e4b451fc065aa24995ae40c5ee381ed82a580d983d865d83b9997bdd02c570f0a9879d1158ec059c0010569831e78eb3c482a77f2c1c8fcf227f0d7128a0ac460fa1c634e4e1d84cea337ae915392ca23d252d320cb611511d0c9febd06795c54e744f2c00227959a67029f340f8e8bc51837478f2c4aa5503a03774e03926419530cc3c372fb8249a7a2e5370b22cef2110712c2ba151dd6279a91a8386cbb435bd9392f22c508304b79fc0ee10b5497b4a28594bb697f01c4caedb62fc3f3be271b1c092dab0a4b8a3d9ef890bae7284a0a562b115c5ae8d86bb508878059ab170ac245ac291c1406a79c1edc7d233d20298fba9837868a42848540f6ebd8a06b04a1e91b07f4900a619d8012c4d1145edad88f1c07d49214dc5b75b516aff81fb237d2849890b5b3a711bc7c661e9635d44cb550ea8b883289ae737bcdc871e76dce438d2c415080d4eb8841c9ea3d72ba0d5a493f81574e256bffa7ae6848ac0e75aa0e0248ff4a28af4b49396290600a29a8dc88f4f47ebe2cfe06a2e2049a02f4e45dc4bb1801dc005347cb83ee469e32bd301f2144978a5a9018a0b6494e7cb6bc4918437cc82fdbde1139989cf7856ffa11e400901b009f45d150d020f5e74c15a2fd1a4f830e650225c31604b74afe17d662a568173590ba710edf45edd4769a5f42a709901a0d9132789c77a19069ae3f5e19aa37ab47cb5ee3a3047a6c36059a88e5012621737dd004639218ebb134c45bcf09da211e6dd592972bf3f44894be9ce4607014e4536dfb343898762436cc712947905cd7e8e725151bad273cc7e134bb53544855bd903e19b4e200cfbe4527594d5733937b02e171e311b2e2cdb5bc0989af370095f9cd03221dd0bbbc9933ac862f377dda023ebbe4d1e940875814da93074d84437018cad2abc1a484a26a187d5c126c2fdfe460b46c89976b02857e086aea2e61fba5eed0e4c4c0f1e843524aad3723f5d55180701bb94b87229e9753fee6d56b03e8966697e69b605b941574ecf0a733941d86861c16cf935a2a514f88ef40a870088789e811a43462a81e5c85208021086f6ca5fda9837051ecf0a337cb68ea3c136762b654279c8c54e590041f968e371b802038e36d8c5df3570141671b5cfeadbaffd7fb269b428db56d31174064b119508b971e7917ce6fb5497195a05ce5ac8975bdc266732b280638fe83968a65c7e88e62e97e0a9a17fa728824ca35084c55d88da80d7872caa71d091774707b2a8d3e93a2292db0151f5734402f0008db4a08b7c2d8be43859c006a62f0e7fb6537f6ac8fc43d09b88f122d7c5432d06460b30c2001e340fbb4a0f323c02b8b412a447181a116844e1e24e11887971cfba6638ed6d1bb13062e85f8623e6f6936415eea6fd264d9a8e3551712ef325d48560567a96c6ffe962d00f5a059d7bf33da7fe2279eeebde8433b00ad5f810d7fb625e6b8ff205ecc732434406f51a5b86a62a33b3c35a974df0d0e128f47e5d49c0d2b856718bc9a30451d148c5f51e77425fd6a1c37b0d797cd6cc029a8ddedc020518f7c2c102a34a8b416dd85aa7bfeeea400ec3eb47405c0d611547925780f6a67cf6ec26464b70d8927a20d1078cdc01909d1be1d52a77707355b61ade20b1177861d8ccd01c435d3910b85906c78e6879c02288d5921ee560d7906f18223ac797160110207082dbf5ba5faa0203332d3eac7534ef46162a25742a440b13a882838b091e2ef1eaa0ec1c6286eb15f18c57b346dcb058d93a085ca04d7b24460d148395efeb08993bb5a67c800752d4f2067a00798f67453a921e5672101ac6f0b3dcc15e6cbd97f3dbfefbe76e5e2768d2272ce7782ed67188bb5158f769771b323e4fe838e566a508e3f8bdc84d1475b449da6266a665c098e195ed38c7d54408c954e59b78ae1c0f2528d779380f6a502cac503f89c02f5d1f46a8c56867e5526b0e604dbc1a2195c980d61831ff9831b8647e20130c5f3c900259e15d1da5695fc89aa2a0b1f3302c3adb87f068f868465af72d2c264a28dea23538cbfdacae66038d525c9e6f5b4b1dcae87ae93635308431adb0f887b8494c650b89f5b33d25ce5f5604dcd87cfc4448ec077ce6a152d0c954489c8be4909575b0052fd516c5305cdd9e01d9294878a4e01ed1cc185dcd43f318c79cbce8d09f47e57e1da7fb1741035de0dd0e2f4fe8dcc184da5b4977dcb84f94bf6e3c5c1ad2850e413d075c1f59b376cb0bf9c3ba1aad0fb4870945c70a4f376f67e24b04a640d0c4504fe5f13a292a32e54df1e396046768c641766cd5bff9ba508fec52e54ae1b05a46090004a69995b01d1a754d3c2e4a5b686f413b5a92e2b62130f37f1dff12d29e9861366a1d379a08bcafe3b7665457c56508e642bce2c202256dcf200cd7488f3348fddfd88529b0ab754fa9dc12a5b545355fbfbc65dd206635f5f44009a1a6a4bfb99b0d5990f33dd67207c2f14b220012cebec72616ae02f60420ca6a6b861e9613d4413977f618590e4f5849a82a09657ecb51477076b1826a6861c21755b9e25111417c13aff1d109f3611102f514487b92700d19115c9d0a24593dea658c53a061cdebcc2f93445565d83a1eb2d8fb8e5a300a98af15b30618237416ea8f41902f6f9b496d31e8d6c24cd8f5e9e305e0141dba80c835802e8fd4a86ce0517b2b90ead1103ddf455a75e664aa3c13046eb53eae20d1f0ec2171e531245e571c25da123dc77e1b06063bc2d890c870fb227839556505a418331498820de7f84d0a7eadfc689f63095fcd0a9b96d80e20a96e04d79f109f59daa0dc831b4f31667a84feca68d2c9bcd0ec2df902f0232e05dfd8f80668f0d8863105171586d83e878d3ae9e87562e2e0be0df4a7540bb1f62d22b28975dcb143384c9ffb7051a7377fa848816a1a6165216f299ac9f0eb3c13379a9a65a37c29a08f7b5ca60e703388040ab2e0f9f9edecac72f4450fa95024c13532a6b7679e1444e62fff8a9f4382d198aa1c0e4e7d0913fd01dcaaeacec8411fe98e9772d2690ea211e8733b68e596295fe0bd30851e067f3d5df139d10e04f95182923dbc7a04d6c17de6a5f6c18103cb4ba19d0a5262f85faba3e9aca6cb5fd9b4d3ffb29ab2b45afad0a6d0124b57629d0c0b1e2b71e859fe745e9254dc898a282beed28c409adc68705812561e10ff93b62177f3a71620b2aebec38c32ef21cc0a76591b5d1a22674d002a87203ea46e237119404ed6ce8ae1b39b961bb33f6eea42d498ace063f1a1f2bc32a123d865c1ba36d0234e472b9a5f7bc7d37d40cd10b08742de7d0923ddad89ea3a503b838009acc9561881044293aae5cbbd4a19190f23c064e97eed720669da61f7c0752ac2a2cc7886e6ab17453299409a888059330ee742b6322734b89791f550bb40bb6cd9998ad51508bf2f47e660dc2b3f922d45e58b98c0528ede4515d416b711c7f4a12d30532aab07aed285075441aca0b029060f93b363fdd34e704ff392c5d88ebbdd9c93f34a06842252d65b63b1ec5ae10c3b6d76c35bd25596fe83515d2423c979cb01c199d2c657ffa60acd52b6dbbb5b5cd6a560155c79efee534d6b158c0a41811e292878cb2f01e257b200730a05c943054e2ae1726ab063ea6780f3838782f41d8cae302566cfe669a47ee88af71320e551e06332a6bd73038e08f24f55b0a01425b1a2ef917f3217846ef50b2dfc6ebfbd9f105c66568c5e5c7551da47065e5d659cd92415b739257a668c958701491ec3bb4266a9061dfbe8be448852f25d525debec0316cba2cb325d1fe09dadcbed33122953fed9b0fed4bf6319f810f45f6b1f58a5ab2dee38b6e777161af5b8ea60008d75154d7d7354ecbbb6b7d4de39877797def1affd8b7ed4d9f0fbf43e43c7bfb116b76ac33932fc27d4107fbc987e42fb8597d8c4bf8cb26af6418f2102cb245a9792760bbdecab359e43905034e646a4affada4f844e4536d3c411e9623ab415aee61f215922b004d98c18e71b74ec2cd92d58433287efbb9700ad523cf24aece6b30d8965c4aef6cc41972859d0be6473f5ed05f476295ee252b5ce5fceb6c932d3e2bf695ba16716b8507a87db37545359747b77426a969d7f10f2ca4d06fbc73bd752949be25b6a56f1829087827b2d715ae272b90d5bc20adf6475fe1aaec9cd74790df01e540f8fd3e93ed9441a09eaeb52c73dff7b71b3849b84ff893a0391c9f0085702b11f16aaba8ff0e3604fb50835b140af2a742b352179c89bfc0d4e013a98bc0d816f7d9c93f37b40b68d3b12835b3d1a759f50c8fece6258b5b11e2bf0bd0f9cf160c5d162b51755ee79d26218120c03909aee1633c3757a3eb91cb3a401a387ff8b24c18f6d31f6c92abff1b7969bbc4ad2d5b7d45c2cea1cfa1e622935c00539f6475204b12173b017887401e4be0ae67061c874d90b05495e68ea178adfcd1328ff7c08f01d61e4c389f1efbb1b462ff8e205bfe74f3c75362fdb5626e9f389455ae4caf39b00088fed13b1c3d43644eeb04f070d57e6b620a6c7cfebb6016c0897f4bd7568dc50cf105fbc9311391ed6bc3f9a93467a151c353986e030228e5d69068b9111dec876a91f4afc5c1c13ad3b90537f38bd235e915eb0b85168199f58b4c6474504bc562771f0833b41a632658b5bee28f371ca848f1636b73fd8dbe0fa1bcd66814b7843ed042d6743ebdd707dcdcd39bcb330774a616614f698a01f2617d236e2c8891d4860373f1917d3d4de40dbaa2076d69394033d8dfdb6caf37d1f53a030955cec1be6fc131cbe153cae0df6cc8646e5cf289d96c4e7f07bf035d10702e39742cb724f55299ab3509009c42934188929030682a837e984135e84bb8b08a481a948e63016477d93a377adc2a804cb3278fac40387bd6677205f9b3221b2ae8cbbd43ea3965b58c78be4f09c71ebaffc76e524aff609021074ef9b09a274b7ba62d601d05a50c31869fb23315d6c5d195aeee1171e15d20454eb4da390970222290fe81b0c9c795ab15a48da09ec80edf7067dcd1e488677b3546db280fac43a7d1cf00af8e7ba02538e711329d1623d925bff89b63829990a1e878c38994d687d46700e2417f18d087ea7ea9698c432e3101b16a0a4377a356ff10838e9c358a38ffae21e36865b199a215b9b2332bd7c920603e27367724c6c2df1292a9b13160282cfdda2518838b1098ab036b4f3b0b1a2fedb939fb6f3e4e5f01317da779f0e780538334fca2ea1bac5821f8385ec1f2ec32b19140c1c6b02860fdde082e5e446c2c08060bda3223c2e8b8d5d2648744dc435d2287c97e31d4ecc088fc2ce16bf4c05a8acd1d59071494793b80ee41d44432de6b8480b664df153a8020e6c140d182561b8dff0edae34acd750c18336bf9edbabff981c2ab4ef9b67d6e16ec5975b93575221e1a8054c811da5dda424dcb9a37c873d6d48c7b3c885fb395892390fb84e3a38fa142fe24f0ac0d434ab14c25072d2de9cad4943718fe1b228592bdd69964c789abf7390c69c1842ae08ec325b6422017d5d23bc69484b70815219f3077b966253dcf87cb17250b4ef00c621b25e1f5cd123c30f04a99516a831e3ade53cb3f224cefcb900d5207e26797d4a44844451c0d9b067485f68ac6fcc03db623dc99bd6395cde143f0d2a3744eeb1597435ff1ad5bd2b50e8a5ed591b170b35415414ce271129aabfe8fa9558876ee508f92e94eabcd2d755986d5793dbe36ef83b1117a7264cc4d741aa6135894c5f5cf751d4965fd55e3b2e17060d33f45a91583d9f08278e4adb71e0e26e864e4e079a064d3583d96871cb60053f611c0284eea9bbaae7202df7cb3bb10e8565aa827c05b307209c890f73dcb4ea156dce923d578fa16fd5db779597454264772701fe9e9f858758750102f7da75791a8a1294e206e0658efd6c26e8965652d4892b7514c70ac83f0b998d7024ea34d515ab93f85c82ee17e43d01f5b7c48cb13b687d1ca54b1640141200800a2f56911a8b8aa82853069ddaf66012cc3f6c1f8a340bff67ecccf45bfd6a9f031f6f37f87d6d963e7f2e93c557fd3f7b3b28f4a672c4ea00829584abfacc4efc1c1ff78574e5b02a8bf9ff229bf1e50bcdde0603e3cafdddb374f19a5dbf4cdc7ec994745951d71ef4f39946dedf04b483c4e8d9e39da357bfef40ee0914dafe5369b2ebef0cf18d4535cf0ee296ee3bfc6bbe9040f71cc361bb368b68e1f3b38a5d0f35b357d1f3e7a54792d9b1af5a84f94d3616e5b9c577d42072adf36e96d8ce1f56151f7845991e93cd2a5f023c42e254cad1be990a36396d0200a21116a7afbf10c308dcb202fe115a264f12084b31b32e1988e892556aa14b68c83d657cb9fd74d80fac6266c19b17aeccd579c48551421065f59f90b0eed6e51a8d381e3ebd2a5fac1e93edcc52ba959fe687dd0bf15a62d271691236bfa5614a2d4bd80a91900b91bce59b227ef8b8fbc2bb3c221b1c9d4afe1926d7fcb90a5193671d2c0bc7f0f9fade4a8c742d8e9a8252056f937a771d53cc75ae83824c404fdefb0dd6dd4ba93acb1c6fcce3b7e0770705d6cbf30c9cdd9a02a6158afa4d92043ac6a2b05dae97856fd0008e417d6345036829d8cd37be6322a20bca1cff5ae6fd08c525c8fda31a151fb1661080cdea5858a99f3a5e25552b194d025048441370bfd12bd187aea4f08d8d340512e61a7823723b48770c7fda02cd45101b1c276799180bc375a6910383f93b60e62789c3407e587322ff0258d8c25b5ca3800a59d5d61655c919dc3ec1d2cc3de6da90a483b23c0587dd8060f586c72d02525b67f6d3857eae8e80ff881660026376734314003ea52e3a3654ea6b3c8b0877266d6239421926fbd2d53dd87ab40df0c19e466c623534acfcdd593eb5f72af43b400712ab7799aadb6f6da7e95c97310aa43d9451097f077dd6cb601b6a4724410864696511d90ae181b71b23af5bb7a2d0129da6d43ab643a93748c6768b5f5e91128106a363e5e020342a7661e8455dacf528d5b876b59a711597cd26110a0175b8daecfc4c646b395342debe32b4028e4567b4d7533b4a5bd22a7eb48187b1257b53e49356e85058dbe0f02a1d4fd1508319bf588ad3e4bc77a04565a3e27353784a3469f0752ed1bc76b49bae667e9d6edc955c53ddca6e9bae21e56dd0f4128f38ce76875ebe3238044eb5668d0e0f730a5b9e1bc2ea5d57e226dba3dbbaedd866cb4ae57aec32633d5fd150cb1ddb84aa5b77d4a686f0a071abf1ea6b5b74e579274ed0fcf408150d359f55be735298df6b3274069c3edd1752e7d732bad8f8f808110b3d94fe1aae62789d6edc95522bdede3293020746ab6eef674ad7219d6738b9e31a7cfe01564f314eda535a6bd02c2d161d4a8d18df93344becfbec845f89fd4a3acb91f32e5f7ebdc5e2709f29f0cadf0bc7dfc0759f5707a7bf03fd90a0cd1dfdef8db5bcfff255bd090fee84dd827c9da6ed737f704e010c4e42ec41f7b77e82f320b1ea2d33fc92a70487f7ba7fff4be1f8028cdbe30b103431fbaa0ab7fbd88c6004fb9fd4af5e0c1be917cc85d73fc4163f909542ba244883507167566c96021699e0754e8d9fc0f5edac2c2d4046b4a192a2681e1430b18d11d2454965df7dbebe52e9c90fa5e0de7efa61f5576fc316a36a17166d84f674080348781eae1d840772156751734d28d90cfd8bdc2cc791f874dfd7afcb572f7bfa39b94fefa9aacbeb2e6d8b11fed667f56b07329463e306c5efff263ac4fe08deb47c963970032bb1160c1d422119b9ad2497ed8a37e040fb983ff5af2b1fb7f80bfabed6dc251e13015dd984f99769bbaed1ec83c941d7e880834c29eee585be12f8bab6978e8f5ee0ea216f27e9daff5079901da399756d327c192f6a48365d869880322e18c572370fe3a0f724bf22fb7abc157fefada15473cf55fca1653a56a0464150784821d6fba4b0b0cad77b38815423bde72eaa24b5eb38a67eb1549061309d134405e7b9fd1d8a27af697beebc6dd580c624a21d7acc42a5624a22ce972f16a4a7c9c401b73501a611e07f285f42747c157ae801b8a2de289924794f86779ce4725a62d91aead5948dccbdd752219682119b45bba2d0c6854b04919639c40074ab1907f894662c258b4c6268f2ce49acc42c9cd23a5c674591dcb8bf0476481d205f8dcc8e8d0f3c89f0c2b5fd1619be27f8accff0745c3f9525a6865b10885b359a4d42f1ad2ad821a1a64818cd3109047398d50be4a96e37b6f5e9262853ee67371d1f8fbfa4e4d0ea4c31cfe6bc85dee47fc7439fa2b78ce2803172d9c5b5962665b162b28422cb943cf1b5b9e78b5baef3a517a9a7dfb372b6a7b8685c5ef8fbfbb21e8000d7be14c738ccb66a631ef7584aaaf4860d0740bfe0fc21dde115d4374e8d5da5d58e96d319179a91168d93c79f1d621bae6d1b792bd9f90812053ca4ed4a90502cc2536cb80008f171fddf736df8a7a62986899c8fd7cad17028b0ba10d08dab614602261ae2b98f31d3f4c47d68de2abc3eb6a4656437eca6b205bea11d53e8142ae95be95b3da8cd18ef76ddec7cb851b7622d506a87aee324bf452277d932e4b7f795e41d434ef7058459d3d0eb7ec64ee8d4954ea0c4ac23a448a20d5794dc2c380d9bb3cd28561104a0bffa92c15436b516a7d459e265472d9540c1c849657fea4b25db5f08d377f8b0a1dda891a8f582d5a585bac33aef7026b17b73e43d8e1f540d990ebb9fdaa4399a9aa65fd4b406bef88025aa7a191dd2554c88ab39a93b696307d3656fa01474cdaa63fa00e04ef19ca1d428e60e2daaafec19b7b505fef43dc6731a6c9e3b8bde6953f40b7678c6970b825e62efd77987ac940706b21f5bc8b4e69a19b717a4bfc659f88d2579cf97781dbb9134bb82dd5ff48752606c331234c1a71bf3adb953b59c563fc752e0fb1e3982f83d01845e654fca040e700e69b193ec82fe3e38eb87f456078e88d5390f0b20cdee94b3ec55c343ff114e57efe2fc35ef73a7d62848ff7dd405f6c6b769349df166b12aea1338054674c358903cf8d8b48bf737394b7f744544d2feac34c37e72c9f5a59bc2d557d23962623bc62fd3efb72b6140e3abf81fb865ca7368ed6d46a7a85f224ee5e9f6884aa0d9164302b3f80510360bf169168ec1a5d8b38bcf19e32d84fcea3be09d1bfc7d701b3fb9ed1d0fd27d9286928501bfca543ef61ed20a1d4bb7631c925b9ac320c5830d2cd81df5f8e8e0bedbcee622a6ffd305a70de82a53b876859b3abebc5c901de69fea666d7e609a0891b7ff40f467483b87bd967f0c6cdab30795e5d715d559c9fb8fd95b789651ab5318a1b92cdd0db88b63736c3f12e009631799c125d08ea60d12ea03e806a438a3557d2fecc8dd28524555f863638676a8df56ec46e602e7122d517ab4cc876155f1fd07a70f50f71cf09a230f5c8864f3f8cd2325a822734953e5bd85aa3ed48d4772516d543e691a7c759ffbbc39d0375e4972e4a8173d7edb990a66e54fc1212888ba79c1581d7bd47e255084917dbf56f8d26058f929534ffb48ac8c558bfa9a8b9db2722c52df3085e1ac2a87660af55c4d5685d8c849c8634ea0cffda4d91ee1674e2018cab89a554a6f8a7d974ecb45ac30513631651af6660f9f45c7b1919bdcbe29fe62980ea659c28e58ef0f6440d2a59b764c30901cbf4afa9d72b9ed69d275310f02fecc99548619ba50de5d1252ceae54035be7437774dbeb7799901171b54ad0d6540098b1667e450fff2ab2e125998ee843e44e9a6ccbaf69aa42a7108ca0ad3db02fd1ce0ee376ed3d0300b7fcb421f11af3330032b4d9a7f22415f3cfc5761776e880566d7ebb32956d0d9a33534d1c3e326b9faa744da12c1a491fcaa50fb5271250a65ea70eb7a5d67523797dc0ac05b7593602a46634c29781b6325e36949c508e8af3e76d84f534a897ab4ec3c124abaa0e367cdf1d06b97780efac5528e477e88ba1b87ff8cc8901babf44e0b19d6f2f0e551212d12a7c995638ed8f86457e555586b1d89ded3506731853815a27fe195ab9dd25788de93121aeed1eb4bf8172ee91990d8b7c9b3dcc48e6ae6456b68386e3c91761d6bd0d43371515b4134bd8a59886633ffac12576c5f3a09d7424de3ffbbbf4793efc90446aaf3684ce09a50e73430aab60a5e6b588c99da6fbe825c7c9ff45449df600b20bf77ee9d039497d61e2d8ab6ed6ba045828635d4907ec5c7f580701c3049b80284817bcca4506eb12cc7b54e97e483c21ebacd67e2ca1b565e119d49c6e84813ffe9da21faddc3c4099a151cbc04f76f22472703e6ec03f218c3e5722c66d3e0988b2f49108a13f32f5445de7e68b10373eba433ddda86816c6bf2acdd40bc07d9cc31f4fb93e4f2c4c0c41d57daaa55b40b8eda685bfa3be43ef755d875e4150870f48268eeffa84e9f725ac38360fe6c036d711cd0d67cbf85522a2c3788fa84d802e485ecf7b241858c48097f055432227d19ad9b1d8a0046ff16f668c50a9961e4d69fe24f2eb0f0d8a215c99e104c785c4f26970e14a788c8ae84f3b3cf111f016610b17805e6bfc7509dbe5111a3f69f7281235e1e91ec8acd47d273ceb0b05740cde61c2c8120f0f110808bd24ac59d11dc3821aca83849cbf868118bf0d7d3f98633f4a407cc4ed7a43d3ead57bdaff4eb2e5bfeb932fba6af2f7bdd0bd4c60deb887a738fcc0dca647b4959d1ec76ed97f1e0b72edd5a63924e0119a1e0bda7706e1122d0d72c3b7e0802876cfca8714a0dd5fde04bdc655a8b8a984e063bb256ea33c6caf8017a2d3b6193a868f6494b506c944c51740c6e1fbc69be9c859daf3f16d2584e374f35fe6b8521828f6023f939c68842198291ec47d82d13c9355a837cb49d75475a6af0c81b1bdeb81c42ddd4b186ccea18a89ee699bf8c0bf77c626501dadd9e59c4ed5aa4d9b03b8e89121bc67dd8dd84ba0f3c18014b75d6a7d5f0eb0981546063bceb4fce36e1d4a869a4d35e10d9cf3b03a7ffce7e8dd94c45f83688e1fe1a4f8f0b3712865e2ebf17064cdc3dec5709d3a1a1b2b2426f4846fe4c19e7791b9b3116a2cdc3cd84cafc528a3bdd167a63b372f9721a46aef15663c7e7615d6023ec6f3a368c1c24661a128d0fb5f5ef0a34fb7a68339d0ad5a78bd3a1fcf0561291ada4fd1269709e02076abe9eb848fffce711b73ddd557f01aab18e773dd45825174035165107bdaba788abae04bd3b2bd56fe1b4107deff3e2e1eea6bfbbfac25e1a277c02d4eaa3f9788a02750468e6b09a77083a6a047e7945c50f979a82f21c49b4c537359682549599251ce618b011554411cfb1b244d6980da2a69a45ef3d1c0a708003e96e4de7e4796bc52ab1ea4a2104a50955d5cb989469db0f2d83230a7024c2be5108e72a04fdeefeddf883ebd6abbd52f757d6d7f25e51b7bf181ed348a838d86c4a54d208a2944326018cbb01fa3b254459404c31930e92f708f42ca7254892a7b48a3d650cda9c609b016bd8103ecf350f35795247f6966aa65ff1929d68ec07e65df15ee9a609331e46676e78508e2bf29a6564abb910a462d88c01fb43d329d436d964efbda59432c9e20370049f04f603021aca40426d32bede257ec8f48ab60a81aa4157d2ccdd0d10cb4ceff566b5e6b9905716cad83dc0e0d21ad9839858a2bf436640a637cf6a0d93600bf996dbeb02ac4405742d234975e5402412c50464504480cc5a6b2d50ad592f7119888307d50e90fb8e186480ec430681206dc82fa0404005f88024401e12db5af08548ab2e0d13bae60908b556fd839229dfe8960a7c7001103ac0554bca21df9c3dafcec16bb797dd25d28a446b06b6edec1b570f6cb01561db85a264ebe11066b6b62052a135b97a259feab4d43c2500fec1ed5e85cc64ece02c6b00aaf7822ea2d60a921c3292946f690ae2fa976908fae1af59e2065204c1592e69e41bf8a0830efa0c04414b53d15e10ec85e400f2e74852d8735fb200638c31c618037170dbf75a7b814a24510014cc8081174a5a59df2f94958562c837bfcdca0ae2b6c13718d61e5435e811fdb7a1efa5ea1544b523093b8ec837f06df50ac2da0dd5a94e6bf7f42fcdda06e1738c3fecded841f7f97dd8bf8afdd6b63e0bafab9318804c6f05d885dc640d35087f4023e4a96e8bea3178aadb92915dc8f549be799341ac576f1200f9067ead20da0b05d666da4276a8d6acd76acd0a69c9e550adb5bbde0cb97a45412cd19f33fdc412fd3fd95620529f8c2485c9183e02a86660cba50c456c2e324f7568bc7ee8a90e0dd5d3f184f74563165169108d8038d3695175104559a64e3d3104aad72b8ba73d05b22003183ef5b61d54b7ee8d2558fcf55bab5bc763071b209ff644d7eb4ff5c1edd55adbc482f6f5c8ca245f5a85aaf20c8492db6fa05fcf17935c2298e905bfc51f13f4afd3a00dd7e96f28d17fc90bbe4978d0cafdb70bbec1bf8086d000742f19e47311155ffb915f4692aa640c1f416b0637f0bb56514abdec81d8b5efaed59a50967cabd5a8b5f65e8bb3d50bf16624297b9d00e2aca621f25497060a83270106d5da750e252e3b10870aa2faad59c1187b4c53d1df1ec473ab1848ba3456df34aae3f27a3a5692b41949aa5a37d1dfe51ef770a5b49a91a4aec7f01152d891a4c09a81e79e974b39c8a3217646926a135da29f66fa095d095d39096146bfa4f0252a84995013dc2b70184c82de0a21279e8cb434835e07fefab58d2ff83cd208ff256f69895c8b73a5dd80fedeea1863bf98620dbe1066d62a5fb71a84e2e4b6d6f6f5be77f6445f3fb982f06eb55ab93a7decb5d6ea4454441a7433dc5a9b0999c94f240a29c9ed336befe7e16bab9610bf21e857129156db7d868b16d2ebce84fd6df6f5ccd6d75ab5745a37bc445aa5f840d2e5caaa2b57c78b935c560a4ecef4c89a5241770296d41a158ebc11d3e58c1126a9ac5c162e86317cabdd2ed06d696eadc5350bb0a4990293142142c40319c4636a6510ac54e30585cb26c34c149d39c296d284174a2d28a6aeea6f6e53a273e46f6559eae810512acb058cd1a93107690e4ec69c11c2847861533170d812dc0d9382b3615c706594b892c2bc4018be5e589a7d61cd10bfccc890993ab2b9954b1232ee4165c303abb284bd345d1b52eeac031b9351c360e0c8b026187276883aaef7f585f565c65a7bf157d8979a6fe6799ef7816036c090c2300c65229810b01e198c0aac0a98979f9f9f9f9cc1bcc0d0808dc1d0a04183860a464adb86e9086b12755f3f92bea270412878aafb52c266cd1bdb13dc8604dc068f0fdc26076e03f5c2060449f925c3502e99626392e304cb41222748154521ded49299941997b032615c1efeb4441b95af1f5f510f546b12f8baf1851bfb1b18f65427078d9c3272ba7cfbac922410c3e8cc419a830b81ad6a8a4d08d5234629cf17374ecc4c098788e001cdd5e71b449f398b111b3e18dd8782e0504dd0af510f35eaa1c120d9c7212888839d7d08b3f73c030db924914762cba5ab0a98fc817cab0501790909faf1d5f38826623fbdb42bab6bd745064806441b946c6b926f05b8304010e95714ba2e23d6b69567344457ab37f26c9c2ffade888271f6edb55b7c5104dec8fb69916948445addaf83f5da10883626b97d66abb61f41a5add65a35bcf55aed07272c6a42d040c66b3d14eb060b55cee6d14d74535a67ae0d9493ba99727572907b01038f4a8e474e890c3b27d8f9e5ac9c2470b016c02a1255c74a8f330e948f25a7184e30e70372755e9c7abaebca19811c97ad3337766e39c99cb95c97b37339b293f8e4a29c5f7250289e3a6387c971a08d0b4d6294f92145eca90d1593932cd725b40e387dd4545184349533399aa74d2873684ecac6022f098a4e812125e5f4713a97b301b90cb9761246bfb8fa044aa795a8988db38d20c06a03a60db927670b66fa85b3079133b73586926918b4502f37627a085503e6dc71622585c8751634e700725761de50aede42d784625f504c60319090ece82221e754e46c3a676ec9d94072d4ca996b1c58ce3267063406addd99cb79d13bfd8467860a678e3e69ab14ec60da0585734c379d39386067aeb3c6ce386797e91044a95c600024272dcc7d9d09c869c9d19d9cce33078637540addd3317427fdd24b754c8f8c335752acb042a72e1b3bba8a9e51e3e53c72fe6893739c233820c849bb7214a4f172774e30c7845ea15927f5b2878d99d30590294cce496cce49ab206970d234f77e3afe63932729cca163e3d52442209783a2d12ed66439c3a8983387651627eacc39e1ce38556270985c4c9933c2c3f19ee73645de9b31c2db21a7458d156c4128141b9053a4ae9433945983f554b726ca8f4adaaf9a286974e50d3b1998e2844c580dd6a8e60c53995b03e4c7a7ba354ed5b6ad55e55936954d69aadcbb01351580dd76c0c2097df29708e3ae569e7a74f0a07a49f5d587a27c78fe8d627d6dcf6f50ad559f40b68e1b9344d7f189ebd80357670f63a9b57660bdbaadb492ade5d75dbcddd7fb3a514731c1370dc4175f4cded103f286ffda296289fef60c3654d043105c41677fbdd28a0278b4c88ee8e08c5b688a02ee9ab01cd1d9d938c106d49c393638f8032fbabaec50129ba366840d97bcdd7a20fb74c794f5796a58d1c082038569c9c834e5851bcc0404000e2822c606cc489a1f7242aaa75b421213d3d10c0b4e06a61647f7de3434dc5c11588d45dd71fa121287ea6a6c097735c2d0b0b9710fda3181799fa73ba62a7ff35274022956a8844a035a98e244513903c74992375e5ebba5b1dd52938d4d8804a703e378e06c06e2ca1a720e8fe07078876b4ab271ed70562f80a73bdc1010bced374afd5a15d501df5ffdf12561bb9ba4fd2c6e5737698ea041a3956fb91a5df022b0d0a0dbc988b3938164cb1af2435503638cf1ed70ea94f1c0b5f8763392703632bb195ffee7e96ec694a4322cd8c6d8c6ec94bcc031afafcea12d50e37e754aeb0fdfda1fa291ad1be991d65a6dd0c7ba881abe25e9102e5bbb530af3e2d39d52162b0fa8c96262430718205be10a6d90feb651f29eee94aadceeeeeeeeeea63a6a8d3e25bbd80a6206d415a8167d6fbd679f375a073fafe14802246bed4d4713f5ad8bdfccd2ec539b01032e7a3cdd291d79513692189293c3d184b54e7a60146cf28215db7bdb12b5c57e6ba7b576f3a8607baddd293d398db0d65a6bed968c9fee9470947894722b28c9f091f7fcc5bb24342f7bba4b129382dbb2dc005b0ecaa15d1294b74f77494efe56ba90e46fa5ed04a932861f729ecca35c799524c3b694d4428d18d51b130757a9848c102fb61956d23ce14155fa50a19c03048a470a37cc183196cc3c315e64b1c3244e0a6aca2e664a295faeb0983daeecb460735ac1dc09f92ac17684a3ac8241030e0ca43978ca3a5f43d8985c593274e52e0b10296617931f53259714be4c305fd0b10389a9dc428a619a90472aac44a16b4c2b29cc912d245009d3c47603898985c8181fb67387c9e6a849c99152a9678f39858e0dca4d0b68ca27634aac2e495e306f58915162a351c47c8342c906aadc72be98612be1858b72471d17e0b84893a38c090396c9c295f20428659e25a610236282bc4082798e50da71812bb5b838ba72078a9c2f6bcca918cc2c30b61830d8784429a5aa4c24165018d2a3470c39b0d43099762d605c19b3f5009b6a23e74c29b6339b6831974c797281c891b284a812cc092f9c6cb83d364cab3d493ef4849993c5ab44b265961163eee11255a58472528a49a2470acc0fb127734dce84a1474639a6a785ad8598e9e4cd132e3364a47809b3624679526651c551c13403a424c3c336c20d332acf525925cf0b5b0e3b5de2984e6a82982959f8f294432c5e7278651aa932674c955ea24a273e969e909c34c696cc2263472ae0090266f2e82acf6cd99ec6bcf0a2bb6233e1aa6c5255de99ba1365d2f16112792a879c65f02cd5c073545ae1319bc06c4d5d65922daf315abc9463ae985e57260955361c53e59e28b3ca8789e7a954e1b4b9b8b364cbdd392aade03901ccacd2d5c25679648cb98217dc9512cd559baa335368a24c2c1f2d9e6c36ce387696ca37768ecc1c1e9b0e302a5d495b25d718938917b3ce95d2cd559b2a38536ba24c2b3ecaaea71a67589d251b8c3a475478cc12c04c2e5d2590ad126c8c69c38ba974a5c85549a76aca1495a812061f604fa69bd34c23b654fe103b3212ed9e5bdae798835aabe5bf0736509d12580d3a005aa7510fd5ab530fd53fccc822ec065f39f8aa0297b556bbc90e94be1d010d61bd7a394443d0c7b7471b4c82bcbae184e71774a72d4f773ad63c039eee743071acf7a7bb1d475ebc333be219fe661eed5e0ad0f71cf44b43d06f4f71ab8ba8200e7aef44fdea462060f1f445d4149e57cfbda741f6e2f0edba672f066b2de8b1cc2fedfb482b0b41ecd73dff2ee897e679d05f32c85aaa3abbbf5eabfcf5ddd31a11cf9a7641152b8a3eeb4645df5b3ca2c0d6abd75eff76704c7167753c71ab4822530fedf4b675f2b9f7f50718d93a55cad741bfde3444e8177c9987b4a29f6fda4d067afd79f8b6980e281db86ff7c6beddf37afa2c76fcf8023cdded40fa158d45b0bd968d24ba7be620f8854dde46bf79ee5303123978947a18fda3c93c9016f41f088e34702cd16e5d846d1c1a5ddc0dfa6d44e1b9d7f5236ab7fe91e2877b3cd1ee919e75fa91e8d0e4adc353a71f60e4a29f6feaa1dbc11c93173d0791a72f6281c23d7d1c632ff3d0739079d143bff9f82d8721a9211465b4998fa1e8216de6e38c06bdcc43da8d05d8e1cdfb79a3fb335f711f03cd380061fc83431f1f692038a290b948fed06e78863d4f45d1087e74a3eb9ee823f369998ba3e8230d4d84a4e83ea2288e331765b4cf7de8480b7a198dbacf38862842b0d6c21926c70e2e7bc1040791a7cfe28b3ace53fc2376a03416df23099ae74d54bdfd03699edfebb74a3159d46f9d08457bb56c70407d88a7bb11b27ee714e694f51d3aa8e8dbe79ed7cfbb794ec7122568d1a2858bf7c8d9075ceea7ee7d14d3a0ef403d50f77639afbf4ec35aefb8c147269bb94fb563079ecf3ae4b16d588915fd9f7f7091653ec296ff5c7683cc5ff1861ddc121b464a650ecedce83ea52e8e764421238dfa3b45ff8cbcb5254bb47b200d6560fd3c046927bc15fdb3b65cd7ed05afcfb07b8e6d4561dd5e9a11ee6641b463ef89e0f5d89b4c1396af1e3279bb3b897cf5d0c51fd0fb1b4d84dd32df50d44fdde87e7bd1cf37e9e2471a759346fd94bc591abd61577122bf08d543ff42af5eff0b7d457fe8962422896827784abb854e43af2e86e058f4f32250eae1665358f2d63145f5108734a3fefa3d82e486131f1149547d453f2eeabf41441a74fdc3f5757077425d10cc65592b55dcbf95290ef04d0e0de0c38d6cade592df62aa00a7d4baf776f616c4758f8487e997a2c5ea15df4a5e3a14f68d1eb0f803807e1d743c9e188bdaa81a358aea449804bd2c22ad208aaedf0bfe54500687d935cdb94d5d1f3edd35698979156a674349d3111b53029023a804cc983162e8146d800093160000180c08060422912446a250dba90f1480076ba22a604026a74a0261280a83180662188461000040300060100041188ac11854fc696ae111e10cd10d4adfbc73855d6c5157cacdaeffab8fd83d5225acdcdc68de077071832af5d2cdfabc90ca8e2f7f8c1279d154a6e33d9d7558cb590c132fc61a6a10901aa598ce0cc1f20c0ccb2640a9b6728ea42ba70bcbbf03fe651d285140cc105c091dacb07029941d5b2f1e33e25c8b669a1dfc76c6e6ac9840555cf00274f7292556e0b781ee185bca54e5e5559c2475ee2120161b6c648dd381deb760b75ee8d7b9eb6ad3e9a0d46f6f4b2f1cdee79429846ae5f5beaf9532df5bc74d7d3181fd89326ed2c0238d99ba2300cf3b1c8729d8563f61c925b8630918259a2b756a1069717ea2177a486599dad36d5b4743f8d45b02eccf814f269d4916990b8615c0150c1805a6a9cbe9367557b336dc615b3773ee07ec408d282987cdd98a249f89508a3bed15e2d030f1b7c65196289326d671f83f4c878ac08855dd3d15145450e449dcc2e46af93e83280e881ac1020d2cc3a45c96ef1714c1c9b4ce3e988238eb9141308294b8486afc40e39761217f810a6ce77e0eb6bd62d73bb0bb5e9de13343551cfaf0293203fc8ed9a3d3179c0fceae403a7d1ed6aba382889dd9c365da81c119667005bb2a6264678abb08f9c4f86c8377f47c0c10759f5adbf434f2d7a12ac4ba24b2c88528832468a6dd6d7603127a32bc643541a8053100f150193110540072110aa43d4d604e40eb86f280a677056eea8d98a8091b66bf68ac3d5bbfce958c2bc68ddf448399cb0e2736b27cbf2c090e7c3447f81609b8fa5236dd012b3b2e7092ec10984d1d100d3e4cbdfacc0a81f6b55b82fa622ad9fc8283971daacc47ac8a602cd2167804472ce35b200b78f860d56d07c8bf43cc5c36226dfe2e8603a3e007e1fd3ddf6f00b8a51595b6d6bcc8f6586c39f7f950014c6acf1d7ee5dfc5b21e7639f2faca85c490d85232a757185e1379443f4766b451b3978dcf28afe121259f5a428cfb9b8a05fd9dfa241b6f91df05bff4a848e24d692ed71484f1d701dbb6240c88174a71a434aad0b97aa508af2a69839902773466c15aa40a7e8ca38ec26fb4e9a88d3ef3543582d0950342440f1d07de537c36772a18692039a61e08724eca09b2e20a6bfe040f4c0cd8cc82f0e68cb5b8cb10f300857a0de483f3e358fafb7ca02f03c68657034be02aaf97dc25cbe11021142143908d6ff1517bbb0380726303058f17ea7a30baba110dafee98c9a78d0289b6ab0c425e5b131539ea8010898a0ccfda467de2acc708de1b9305bed8fee4859cc9b998872d7c3d40427dc96ccc69cd5f37ea15ab0c19ba41203c47439b7dca07b3e5849fd63221580870aa7f1b82d297dd3be103b97117fe3305da99aaa26500d855c84a1bd93cf9264cf7434dea47152942bc4684482632cc0d2a8da1d03804eb8638036b6985d590f6dc058877b64e9487a434499188dbbc8fcb1a8e226da167e70e075fbdf7c048916f998a9f18a96f32da0bf692e218396ae83dc27fa11a79077bb774e01c0526fdeb10ac2d3c9d01d230d23de224ab256f81d95cae2704eb805268241aef58b49834cfce937034b68750c284ef428d3655ebf60466ff44253edeae876ef04a7308533c65f3a2191604fe675815ef8ad254ae12403751020103a396798a16f409de6c177c113fb874b652893761f5aa8c06d4372431fc9c64b4394024a02263f7e842c4a28b36ec22ea43c877f561526c6d4223e0ccaaf6eab4b6d5a12e11af82674ebcdb3e5889b5ee4eb65b828ceb40c77c219825f6613dcff4ea2a0e0d16d1664db8be1aac149300db70f56bd3b7d8406a156b11107e30d626d10ad5fbb75bcca7bcd41f140fb3421d85e5103ea54945bd62d5749a7a8423c7360205160497a5d01cf00bbbb96cf0a03812aefa0931a6d6917321ea4591dd0b0511ecbff0c3d2f7b4c230ffb2ffff626608b94c6fa52a698bdb39feb55787a42fef2ae9f537adb7d501c308a5bdb9b989a9fe6b57f3b49ae23be0e1cf1cb01ddfce3921218a2dc91280dbd7b89357640a411a0b9f52a181364e7790298afa9213bde4ea2c53f71d4bec974d3e234dc72a791404ec71b1136e8c7613bdad0a6905f138d70b201d85633374c3f9731ef882e7a96f0939c5238d25c881cd8050ea74cf1a34c6df452c976e59fd4e40d99066c1ae90d6dd1352bc401759f0e42a8abf4adf5b6a0631ad27b030c6507a22a05c8971705026832d817aac4ea137775eebf5199d576358929b989bd6673f3287903d8509ff64299b3ef273ef634a9e55c20e7dc0bc7199779071513d208746ea4342d61b51297f052a79c589f141f6b48fb5885a62609d4de4aba808de60b2bea5497b2599df07bd949890766321d1fd058489cf8c6007b19694f57d25fcdb1a1742cb12875d87a381db63700d6f236b9dc7a2cee5c5e7f93ff13bb92852ce48c39ae53f3c1440ce67f81a7744082950d5d7e2a51a26fc946437189e0e40de978a6ab75ee422d02036efbb1b301b35b071867907547f39288f674ea7a324062ae8cd21a2f510d52a46d6ffa945b3991c109be2ec2d87bf52471c8f25b4c44964ac3864e66c12b6cc4b21198f8ddf31b33da55434e883a41ac67fa30a872049cd9cd3d8845b4de12aa4f25ff08c414f9e142d4bf7ccb32ec6b4830a392ee26f58b33cfd46426017271918797bed8d8f4bdd09569ce95cd8224c843345b67e4d1bc6643d39e1813bfeb1d249249c56e445d599ba67895a07c43fa7164723078dd1d15c1e9a6bdb2759666c06d0a48b9cd36ebcc7aaf844dbe2783cbcbaf3826f36b7bd63331829a9e5a261044adf67f1cc81a01331cc06ee82b106540203e234cc3e20d36c6c69d5ac98c930b87f633efcf8945b08d4a0c9a8a43c37d30eb00a4dbd7b61a925a5ac77bd835c4cd7e3ed79bf85bafc44ec9d609af201db1c6bccc60ce906ad489d9e9674a526cab6d1c23321b4c097054f62f65244e6c0c7b559d85a45a8030ea72505a4fdb5ad856177eaa9005d99a8b42a9885af4d202eb0bd029488a5019b0f31abac32b9376389d784f8d4375dec91a61cba3fda995a65e3d32adacb5ad33e1819f843c288e2cbc0dda463c69b0653cd530da60a1c59792b1ecf899bfa7a9d8fedbca79893e047a31e925855e70ef875be3d1eb4294eefcc6d1b38b092d854eb75036bc24bb5475a066ae517b0c869eb91caa8a24442f68899ac572c3a81c16eb4921d276afb4ce383f741f8086f2210e3f8797928702898b743a53c5b4d59d8141c420e221db033f651634ecf23cd438eddd1b3d9517f1ad3301b7d514b68d7876e6fb230092a1b5eab1f8c9dc942364eee66e1732b188514d13dfde661ae15713916075a818e245c694e46fbad8a73e80f074caee4ec23398a30f83b0e76731f2524a8e1893fbbb38cee05a0f159ff844817bb5e22801c6eb222bdd7b528476a6c1920008cdabace58c39f5035be4f5afcd1a294a801a6f74d42b20aea1440aad492cfe492a60c503f4eff54473b9a1bc1aededc5249cdc72c7d5bfa1440ab67ca8f334ea48c93f7616a9d4b9514357a2e55b32c4f63e698d22d754b10bfb81c97966f138222445ae6959923db6693ccd474dfa64767cdc863f20f2cd1682b1a82f03e652a9c5f080e660ff75e4549610143e1e35dfa75d5bdb1c1bd982873057734dbb6530894b52e6c9912c8b9d45182d89d03208974d0b8749a1099cd4a3c3317a5c8bed30eebaedb7dac9ef639f070642bd6013df7b5cd1e3b71685bb1297cc5853610912173779da50f3f5dc400d1b30f55208ef98e7e401fc5044421fefff76913d8779e11e93bfa38059a96cb755ef7878214bfa9d04526be9866ec0d1c136fdad2da0d20b60e4d5c5d73f35dd8684354afb2b4a44994f63846f3a0d3b5222caa64010772638ce33f271a7f03b649bd8ae339b5a2c18fa4f87398839c87ec7cb41dcda1fece1a80ef23c340593706486b07b1e18d80f827ceda83351477e8ed76d2375135df7626abda085130034f22880e054d6f84f18528d81c5adb32cd85275d11758c30ae049c7225ee1e0311d04db57dd4af396edd1bbd36be9ed649f12c82e1065393afe5f314c8db69cc2b4f8811ca4e63115843018ebdd64a6879c86fd4db45b5e452dd8c3b58490081cd7b08c9d02482c9c20c3d9bcdd15723bdef419732b5c417dbb7f9f63d471a287a80b8563e2c19692e596e5038abe631affae8b8866e02085530f33dc65ef6031ba69c318ee631cbc0515c723d82113835ef1d998c264b4909698caf265a53630897a2a495c5c716d5d8c542e4233ad5d542eb18eb04f0d00791e082b988b574bc3f128f9704ead41cd77a9426844103a98167d44d97ace544a52243e87c04e2d94d18cb89f0754e6b9a69b19b7c7bf4c58b3a37589bae0e8c8ac3e927a4b25190d030b0bed72c0ba734409e3b68097504ae3b1f8a48ae31e323b0fe4e2a4d7027d0ac60acda63a2df71d58b61c418ba08b0ae987e774c204c8ec405126d35e30b300a838fb01444b75f7fda0e2838a3c749c6b9c8cf04dd644420a278bd85a41d38b81376fd08300f263e09622509dde9e1d751835efe0d7d56559986d160d432021dd8df83f3e41e32a9058c54e60994f8cb24978289123316b9679a738c056994a4ca0b0aef3fac7013f7057a4bcedc14765d0ef7dc85013dec130b174e3c13e266cfe592d74496f1decd8598c8db4763abcf863c878c6cf804f4642f306171163a8ee22ac2d1ccbc1474467f20f508bdd7098f732c60c8f41b303fc6113cc74afdb0d8a8af79ca06ded9c1497f6b439bef2e08d0d36a6a03fc0888bd6574017c5c9bb845aa2723878d0857f7ad68bb4a9695c7ad8f94695d6d7e4a38e520ac37b7c7c1862419144a37df5c014bf1a6af88d38b2293b0b2792c52a82f7c90081a0dc47cb1760521b1e52259aa8452a46de052d92140987b1349d1c58dd979e0254d7f54ce0c772c3dc9f5364f3fdb2c89de47b390fe3c268438b06a516647b3aba8e9959401ed982bb16b901f496afa453e5d6b32bf9171c93f48cbb3a2acb7c85c2ff7989c71b011db0e9b6d61b62acf96b3f08619e84fb1a0a5cc13ce9aac1bbddef4f8ce4f285c1b975271cb9c422f93cd869019817d34a5eaa920faf2d4b24df84e4a0bfa8670911c6f3a2c42d6d46c198a021dbbde9d046e1451c5dd60c5b5bd27344f4a80799f54c7526c7f4b2746ce44bd45cbeeac757c26fb9660a3587353c0aad0633aee0e4964d08e25def8072bfe54b420b050d282a0c3aa32fb2b721e96d09bd1f9c48d66cf325de169cda579b32f3ce64c525c773d0f79d54b7e17e0b17eeb4ab505d72114f250d61c5645b96e7f612524deeeab54c6a0bd71bd661864fb08baa6e2a078df09d9c683f13249f2655f4d018a83bdf4562e57874e1e34323d47a49e67cfd546deda6ff61d1130b1da5694c65723f416081d3b6d89f2a7cfb4b0adcb46f6e4fff19877ae73cad32226ed0458479ac746d41a3c22b45f1dd622856eb457ea1e4a0d283276fa9d91131b4e4e02072a17cf9c2b6e5d4953de16e98e34052b61c3edc5efdd4a22989aef84692c3eec2171efac1a43d46c4804b506a4cc3f20efd2e1a8383cd313bdecb2840899b0c49d74f753eacd82fc3f1cb1ee71cc385a84837be940b9a5b16da816af903ffcece73d13f6e993c40d7e9b9190d917046adddec151a39315bc2f9550c75f0c34d67d42321da1b751fcdac4c4de99b907358a19584069f280ed63ae55f3dc88eb41bc1a75abc9490c5a8227dbd6a79ee2d481c4fdc1989d3c4f751a0ae5e14b9dbf4ed15de08ef136c73e517c36a28ff34f6b5e3a4c940e6f5c07990eaab578b23c8faf347eb4acc01177534c20596a8792fa5661ee71b96d0dc71419a2fa94b71f0641058859fc6273452ecaaae827f53be886484806f7d5764fe0c38ced1bec1763a8c6390b2563ab10bd05a245b66c9af386e6ca65d23ca45d8f8ace2fd12423e62e9152fee9079d7cae11ee0c46404c63f16ba36d3269c5da10f215e23e5cc3bb1bc26bedc6c97e77903caa6f4933c07629f00943aa6f11a79c443bef83a0bb521de7439a2e061cc09ba5d8739a15a5196e7d58c9a45f178c59dd9162efa08e0837dd0d3ffdb3f8dc0755158a8ebd9e7d482e1221b461966a1536855eb9333e1abc4a5ad08e5771fbab8389c10e5d13fe08f5502abc93735d579031df138cfd4655067855640cd072c7ac4b62673cfed9288feaa01a539c6e1c2d0783946eae08666b6efcf5ec235a83213855303ca62777f8a70dbd9588bd7b2c55f4702e113a1442f600ebf58410a41a685c4d8a99dcf81693aeb1d7fe9647aac1cc74fcd4f9d3a40047c4d99f562a720041283d6325943605fd7c171ddfa2bc8c82cb737beeb172c02b4ceb2bc618e63a86bd1c3d95cfc93ace95110ba10286d1830410fd85efc91f18a9cad9363b419e3a28f62fbedd09c648f5bd30095a7ac99ec7fe4e9ff83502a40a6581eedcf45a2d591bb0bfdba27f6b2f27486ce5a095102841bca9e1c09043f23b71d2aaec20dff05c63c222536acb6efa65c672062b23fb3f3f334108f6d36e2ec5fe0df601ebc87ec3a22a4944b6e0f34256bd081cb00673e0c6fbb9f47ef25965bb915629560cad0a42028f607b5905aff98c05a7e2ae8f57249a5040da96205f343ce035a89809c780b81e47aa17243d11083460aef2f99e399fca13aca616b53b2f895e37aa65efbf75a30277b8834fc5e873149fd7c666d2a6fa281806736265093ef5ee44c3ac18a198bd0e7b582d253388ec7a630ec1ee924b60721f0b48eec1851c2826900b68815eda050274b4f6dcc7c6dfd4f1e0cea735a12bddbab9a9b3d977907889782200a117f75b1b8778b816075e543571dbe5c96d11384bce3f971772bab81c616dbee9f29a78acfa3c5848b5837bad7bf192660a403dc88ac997c4e941c812dd4889402a349e02ee3f56ad1108e8c824c58f2b8ec01582ec8239e720f0ad16f10b5e14249364631e181e626ab5b04dfba6caec86a3f05f5027171c62acf2e1c2246e4487d39655f218faf1519a4b6aac9eeb5a2b1de13c2a8591c234a89fe2e04891ede138eb07296704451920370205497dab2db1055167210c6345eee3aef8045d2997921ef2a240d9fa8540b8c183c1ca36f44d5e8dee48a79a20a5a314609e767159808b47a4c2e2c0389ac983be28ada68a5be3db009d2e233058a0e9eb887203061f5b0b1a567ae91bebb30cd7a3dcc9b86782b838fc2509ad9ad6cec539bacb0970f19ee0d66d95ff0a3e7de0ac74037b8f9e73046e59f95bdb146499a176687bbc6f582dcfa49013d47a66a0a1a393c10cc2efe68854a1e66d3a25c4cf8854409dac16c0d82a44de006faf8133b9c1bf2ac77aacc6809707416b037a94ace98895a81e7f3b815a2d03bf08f6be3cb2a5799fe1d46fea7c61dbf9f74fd86549e24c686199fef9275c49ecae94726cc59238b2ada8beca99b882ed9c6b0b6ae9829ff8401b8d28edc4d3996bb17f209fb07f446e0821b00f1dd3b00c78c214c09d2aa3d769ee8539a846b1dce63d83e93ff6ef37b4e932b9fbdf5c5c807d02bb9f9e26ce61c44a3184f2754d61a5e791cf609813e68d498541ccac3e5a4fdc134e8fd5062fc075cecfead1b846be30f65e15a95619fdd85a11fff33eb5866150176209bb004aa0070190cb867409e6b01e969569b10aa9d9846f32209423de08368ec038fdd46d29217c11b408c08b003499c526c21eb776ae72370830e2c7ca05178141f819de12234413218bdf0fb2ede2e903ee0610af440143f41b30411f18e7609abf9dcdbec32dfaeed8cced654efe54bb2e828a2330709f3d3816703eab66bd880b27ac724df5108de342f9d13936c40a84de311a120854373bc648049c15579f3dbaf3e13ebcd62e703e6de78f8816e64f1a9712e2fd08ca699f1a6a17899f9a438b78ef5b01c1672e762ef253aa0e43f7332c07ade707610e2808fd8b66c29c115f81fa6a47418e0effaef627bf0e43fc33b5250594d6bb759fdde53eab960702882158e43f747456e07faedc73417faeedeadde2fcc039822196112f840888310501c1a7b97370d11fc372d02e458c7c2ca89bdd92d667b0bd25e8020d2ef19e789ff5ad0f4231021f041b44244255841e842322c5113f0df3cf9a2d0dcad266854014e4f35e0e63f629ddce20d4053a8ebb6875e9afbafbb2959bb2deb13039ee79d52811bb11e088c0207c76e05820f8d49aa54400fd7f5cdb2e9e3e076b2c849fa4db59c43bc4388269884d107f10e940203e0283fdd96c592128a09a7411a6407022d409c102e1134327b4f8437351237e1055bb5e7e6a49ebd6880b7d29979b870bdf02cbe7c78d22744238431c8b3081d08cf823d485c00f016c0486c8cf789d15621f89a344ac4e88a0cf4f7b4b4704a04f3e436f17a84f96ab4680475c422884f808210e61265c248dcfbf8b17741c250cc048264bd21dc6c2874edbf6574b3446a74892fd915f8731fa99de9e0262d16d5c68ffb88f352df89f8a70319da245d6c7f706a31118bacf263a16909f55e383501881f808b598fd17ec2673f70e2de957be63c06e932b281f8c5383c010e88a37ed6c8422e21191e35f3721fce4c2816a7fbeb462207420441719e14054a1502c84b70d32a2c5c347e39e42d4e9074dc999136707f904ba154658f14aff68b140cbb5ef2bfcd06e2915d1e2ff09b6abe2f919b7092116205e208e20cc204c1b01d4f7c96afb84a0c2f4415a3739ea936d6d1174b1baabd20bfbc7eb680c4a1f49bb5b5fe573afc98b70401446e023b622a0405040ec8350cf8816a34fcc5608840294d4900db0df68a55fbafc8c65cc02f5e43cc54d5b118213f14e0446d18706ce0a854fad43477c10aa362268fe73e0e221c48df093f823b841019416a6b9e1c4533e99561501200816970f953a6a9d3e15d92e923f2bd70da13804e0d715c924b9f94598223623a6b7be0844f976408d98a1dd828db8618c9cdfd743e1db1d73004ead119cc5fad16eac880ac4dd0807083128be19027a4b71258e1ff68759f0d66c994b3d18a323dcd18cd3fd406a67b04d11f9a6ba33f81fe1004676014597d65c88155df8aa582d9ccbe104f904bbc208e5106e448a44b460fad41c6a08f17db5444ceec813217e562e078218818f48d60f8a7db20ea351f273e5ba1157fc73551fef001839445f18f8bcd0e4a3d14d5431848ef013f80fd7883081ce1ad144b06810c67adc093bca4985829345bda9710f90e7d9e0b721cb61c9668aeb3366c6c2656071dfc0de396942d496aa8534524d91816a37785ea7b69e1632d42c2a8082f6c12f6023922e74ba70508c6c3d8dad4b0feabbd3855e720de3e7c033df67e219269e088187828f76292525dba9f2f02273c4fa3e72acaeb1f17d5d06ac51f942519e752883cc8c104a31ca6dbfb0d0ad27056cce2c74281dbabf04b164849eea78bb0d563a40e03af63efddf22790ec5ae19baeae5f51c9ac1cae8a9668937d4fa622593724a7cb20ba44965d7a2e73f0a75174dc61806c13c60756e02f90537bc085b4b5bc2f02329b9850037f6e4f22f9af210dde652fa91af1d5f35a972d2c2f29b3ff152ca6208cd670a662f7f9ce18c40877aab9f5a23f04a4b4e3013263b42d0be41490b6735c105bb62f59e221a3f7835e5a12cf6781d742643e8c94c4581d6ed1df6022d46aa8a570c44372f61d8473c759e464480cca582c34cab1a7341215aa45f418e9bd52486a3e347a6ead63944e7dac11010cc17783a52b2d843d0c56cc17d75cbf045e3773eb550c54a4d7b3ce7ca5f0c3b308e821abed32e312b104f1977cb2ae372e25ac8bb2bab65c1bfb128b14497321398e3de11c120044641a31b42fef43dff86cfe76fd9cebd7f39cb4d455496019d4b289f5da96e04d061397a11243a1cef3080f5e1a9c1e15da744691101c4795341f017cbe057837d3978085c2387a53d0c06528895e1e428fbea0f59518a7c0d26fb2674c41453445fe7e7beff06ea43a92e791cf61584c5adecaae6922ff22125a0eefe3fd6b102809473f59ee10b63178a7b177b358d86db064f65b867ca6e8bb5002a921841ead9742607aa4d03f7a8aa58b66c18066cb0e22572294f6ee02d45289e277b0ec81e36d3d36863c9566473e18689a202f15a576003f8ac00f240d1b0fdb6bcd0857a6e7b86eaa8d277b99e50c9399fd48a1cdaa10b63c57506a039eef84e4e922c267376159c66abaaffdbb186b6d4f691e88e01096f081af7add35a6ab5d5bd259549e9672d48f7b1c8be26f6de0cd6fd3eba1220dac1b416752d3a4b46ac7713fa55b3ee3f300c2df0f08eb5992ed682098e77128e86de69978032b80a0f26b909b04c3b546eef6e727836a12daea981ff85d0f6d3e385c7668be1d2e422a859ec0e921e7dd6bccf144ec4fa805fdd037905708732b7f5560810661d6678496bbdc706201ecd62c2b1a02302605dad60517727a20679aadc708d1023db52dac037851174654d5aee99c8c1c633e150c39a0cf06aa4dc5e2cb630f4d492616b288f3ba90cb38dbae6ccbdf9e038dee09d196ee63df81b309f644277cac67154cf54da4eb71fcd3c1f5789e73a8f9eb08d579e2b5bef842f0330ccaf29662ffa74787ce4fd8f1e29dc46133088960ec5cc62528f4dd0109f5720d5fb88d13a3415c1be9241085cc1bbb6c5e7e13cbcc1d5e340e91037b99f62001dc4288ca577e6586e5fc4861e3290fbc9baf51d78ef7f46720e16bd840c451e99ddcf172f27a7b35c6fef2e3c82535c9aa6f9d92384ecd130a49f6454c16060525529f68ee21c439ea9b1193f8a5cf7af1d0a09f4516a905b138283c05ec1c03aa4ed05d274155bbf5d5b7fad5b1ad1218788d6646543b7cf5d2b85cd366c53ce0e4fd7287047c7b2bf1d2a79e956f0af1d99ae73c92dbdee061f71e4f661646b1c8003c4cb356b5f25524ea1fe5ea07196b40c2221dd2abc4158059b9122be16b618c64d05a763106bc07882ddd768ce7bc0650ae0baacdb227a7514bffb8336b7783c048603927abc8201cc87214e24a7b0cffba678726b8d4dbbd590124811b1427c05fc205d37730a9914140ddf0cbf8573b05753da8b48dff27b6f7c7dd5679db2f1dddae9c7af267c218c62fb9a7729c846e2cf29a0f11140ecd658b5ac6e36b8ae8490ddb84a07214e22824bc1da031cb9fe4549ea348434b81c43c153a70ce1cbd08a8bab1c8cfe4b0c052d17ea21f209e1118525c287ae01a8dda41e658d16e8f4c015ba3fe94002a376311e901a18d15a199ab6e16c874089284775ce7594970a56836ca23ce68963c9fec58e96590a81b41b37f17486065e0e3f472549d33683d2ed59d6f968e08e311ed075b19590760400b20c23aca3d19ce1aeb9e844a68602b4515855e5f1b24ecb1c2d9aba5dae071fe50b4863e3e9723be9fcdcb60da082c68f3d7758b48333eff20fa89bc0aa46db3fe58df6a19f7d1d46ed781522c7cc0abfeed35736572dbb640fdb980e604b4ae5c6aedd02051adae4d77c9e3ee6985cd7542c50bed2aa89658e9f3522d7f47baa55e9f13ef23cd98b79447f27f1192ef038aa2bd2cbb70ff4c05e27dd8c645dea92bd070675cda7d7ad8bc0c2e3ce6193428815ebd698c6700de565f4ce8adb9812373582755924d124f5a8dcb9d81b2f44eb2acbb6d73c2b272bf310bb9e67acbdc84f8a6c260a85344ae80e3850c9a0cd28d2225f0bee6bb3023adf19fe1c224c16d3ad0c238b5472fe0cd50963c0140e592e9a25ae7489ac89c109492c885595f80b538264b2b04b96e74b57b9fade342c1c9b6b18c9149665edd7698c8e446493459060e618d80a9b8d6619a0b3c8b2d621f75c598de46004895960b53f09fa9d8cfc9d3d110f0deb8ec5521d51953ef6f5be2b1a2dcda9a5c4cd3452bf3f13190916eb4a5e1d104f1506bdc8e8cf770ae7a2bc2d6ac5b79eacbf4c6c05c45bae4f447058d4e1fc6e79769f0624ce03b8d267841ac2d2fa3bc7c7c62e1a634660ca15eacf306932c5462a6005e08aaacdb4fa24818b911629ce0294f3d5026c1f53a640ce2ecf177011235d0b119c3f957e07f1b41189a265c89d11c904ea69bf9c3ca62ad86a17d9ca1adac34d2da70756712e90dad7a4a66fd6bebd694088b53e77b6ac51a9396af2356a77c5e67a6c53abec0a6f8a0526332dd61942a4cad5bf7a9a8ab88aa5e5fdd9bec59f9a216d90162e1d64b5742ecff9ec71695a1ce4067978eecdfbe9e02a47137370bf21b931963b21002dcd74eb6d227419154b8ef69a70eaf013eeda25eae5e2137cb7e56337b32615bea5c5745d20da9013f264ee084af59f841b34cd65228fd7226611f2827be14ed1b0f5c84c56f3af448be1e6facb0a9dbfe1dd1c8160d56824dc12f5177861c7496cf06868e4106d7e14eedad125f216b6712818fe4ce48606fcb79d6f31a56606384d57d5429394dcde3da5c62259d35262b5c5e6f89c7694e03dff1adbd75687d829a46809679488135536786406d6dc269d4c0734f04f1478fbb1cf39facdc42bf87cb10450acd1c9fed1f5a10e56be65eeb4ccb19f4036de83023047dcc2de521a38ab690c6499c113c0751a8b09c73539c16030e9689d52bdbd0cd8c4e96cef17b8b7fbfed1f6bcbf61f9384aa415394febcc05b1522051710e4647fc027e8be625a2133c691e48a309e6f03fd81af657d13a0b243f5a37715b31d9af8d0e45d98122c0d1cc3f5371bcf25ab655a3ba0aafe770f0ea8d463f18220a522b0827f52685695ffb9d470335e896d8501313ed174b78bf98b3ca6bf0a0f1bd34a814c4153ecbc4db6f4835aa1302b2e41f15cf1d5604b730cc687f502f73dd15a84d490b298c8e9d77cf0d3e78199f63084760959e1ec7fe0158ce978e22e99714840f1f425ca2e3fadcd58b268eba294b89f5a6e799b71dc4a42b47376527678ec25558a98ce88fa1d0db7341bfcfcbb94e1c520e5c608e6417a4ac1dd03d6c6143097460de87e9c5a3d880b770a294140ccd9b4fd4ab73b3b4b31833a4350974b438378614420676ed08f09d0db01b8360f6d51a721ceb74d91bbadccd14c122612c3388942f7272515262179a08aa4d9ce0353c46ea7aa09c4609b7d4704106941a5687148bba795e1ffb2cf3e922ac1f3fcc448131cf3f9cb21b157d88fd6bdb633b8e216e4b3f3e6680d689f11124a8fdf78506f78682978c367354e234dc8dba6ac475e7ee6fb3c9878e62f9bf09bed28b7899423c99758fa49ef322b0851fbe24ebfd2314095c9ef755895b25439a5f7db90a99521d1e7947db30efe84c51b5983329e62f99dfac60327fea0790031d69e1f3fcbbd2b0598b9c6f2d318f26476e15627e26b0d6b27ad027a50a832a892f1abb7cf21d4bf9a6fcfa05a62fd4da6aad5ded61ddddf405196a55fc552a782d2ed840a065d0a925a1b626cc83f3d737e9d09f6af0f678609f301efd750755af7efdf383003f0d10df0322547aaa1cd2c0c19598951da7d35d1abeb3ad4b720f1dac250075b6fa5010573fb8efd79ce826e9c97cd73d78f088f4a13efca13751095d35d816235ac5ec0a324277032bc9034422e354cfb62c12218546c9896316b456f2473f0647aeb29aed0463389d1d98c92518d3081c49116005ed6d9011d45049c448fd8bcb6fdcdf94545df8683984a5c762654b391d629b68ab236b62d1c3a00e42eb673a2ffb48b0b36bba70dd2b1789431130b0bf3837e302d1dcc45c5bab0aac1204fa193ba45410380b4d3a958292b819245d14f8bac8c1917fc8f55059c341d6c367a511eee873a983b4bf2a3ed17923ad32d6439de64465637772e31287f89281863798f56d40659be0800f7dd3d79b10b9e5de726f296592329909670ac40937dba261d3d9a871dbdaf463fa7ed51fb5404a80e39c9a0a95e63eca79fa33cb856439389ee6e070a19c108643c7d400c7c775e792b0bf5973a1d152ed32d7856e48ef175e172d154887a8a492baaaeb615a6e1ced3beb51c93796f6c1b723576553ae0acd48f92dc65866abef77a16743997db9c71fe21be771f4bcd13734a8c72633aeca0bfff74fcb3876b1ab5cf66ef7d5a6a676f797d3b2ee3babf1d55ed85d3b2ae1b2749c032fb0fcec72bdcc433c39dd75e5b58069f4fa4e00ef9c57ca4efeb679dfcdb0e3244fd7fd94fa765dd8fda5434a1d97e4f518d49cbe2e7aafa79267be9cdfcddf5b379299aba352959f8bdecbdc79b7bbf7c7bb3fdfb230b54a2ec27200e923c92453503b842d6b59d66cec1fb54ab6b72114fb5b28af8b339425a994f2503a36896dfaf82505cc7621ce7b99658a6b997d0eb69549d9f7bd135bd68e58b3b3d0b6fc62cb9a14135b695f7de9f75866297dc251dc7648f7f73f5d44bfb189b8dddd9dda9724fa729571cfbd7beeee491c4f8b85116be6951bf1d453a5d0b55472e4fae34de2deea3185238a084ae26e085a0d8b02fd79eccfc3f982d18e76958523a02240ae6ee0b6dbc6e3658c87de997d12e028ad8aa82b1c7fb6cb8ff8228791fc9c1cdf2d6d8cf3321fc14ec04fea66521f078efbdee3f81aeaf891d2211e5248cf04627300ed6b0f6bdf56ac4039394d71266772da9a356ace9c3143864cd69831565bb64c59b102e5e43465324da64953523ac104139628519204638c43115ca445d2470810fc187e8b35d00fe06b6323aecd0487a00892c867cf2417381bdfb81096443e42a08b509268680ab94c336b9a914478d31d379bfe5ce342f8a9cfa6ef4c2e347bfa3e85be4349229c4d71a8f3de4b99d6f94ee31d7aa6af8b5d8ed7d1b2c739f04bb13e0f7d25cf9440ead7d7d1b3e994da5f28a54ff534ce773ae7b117a4c726df0f0e7e9806f1fbe3e8cf458cb5f7327bb77ab5de2d7313d7b6619023c0515a1d7121efe90dcceb91eb73817af4a94dffa547579edea6672e6e3a08c8082af777e40757bc53eb48c2c65ff147edc45f7bb0be17e4a005506e22225025bb3e09bb86586afcd3d34d7ef6fd9824badbb669201dc0ad7abf69ece9b1674cb26baddb05e201dc9eabffd551c9c63e5b126ddb5699cf86c02f7fc7f8af27a1c0b93d97f52bcd37942ee2f00ea15dbcafe0ed019c7b0e000fe0bb726f38d2edddafc3b3da1b5ec18fc318e5a143d208080bb83dbd1fb5c2dddb755dd775f75eaeebbadbddaeab33ee76dde5e4b66db7ebae54aab5c9acca29a59cdf757a84edae0b9dd039b76ddb6ed7dd6dab72e3b83be6a36e0451f472c582e50ea516fb47abb848ab1938366a6b9b61cbbbc3964584d9f793b90817e2efb3fab571feac9e3a838da9576aeb66b9ed726fa7784526c6e9c938c40fe7f85c39477ef0f1ec9bde6f3bdfd41fd6f9808800a5e8e6429ede42a4ba9dcc36738a0daff4f164432892f77263f19b1a96419e6fead92b16f3f14dbdf33c3f32d2f7d5c551a9aed9536fb0373dd6bdbd72527e309481cfc4beeb651bf77c5617b1a16ce328f97472d1a56fe4a31c7a6175d1f33104785dfcf14d1d04fbc13ff473f2910d83f4641b4e282b59407f11007065cb9c645db4e19c62c322cffbedc73905cc36248239f8a65602802418c8373509f9c88623d44c70912e01ba027a930c3030b5d28e137000bea9a1b8487f837c64c314c40db0074c216d0a7df4779aae80738f730aeef931978e8436f87c88b988ef219eef878f259f7ca423c88faf271ff5841ce8f80048073f40be00007d2b74908bf806e1e7f3f2277dbcc701f8bc2ccb49de6fa1ccfb9c2d807ce4e120200fbe39c585f2910df1feac96b522cad838d90bab6be3a16f4e11ca453ca33cc4f7379fe81be5232f9c4f2ed6a2a6eca9736c1ce483b2c7a27c64c3f9f461cff3ba7cefbd2e6eb337ea0d0e85e1f01c311d3ab2f9c18ed9ec0339a55c5b28a54f372a39256d3f43d9f6397be72967cb8ca4fbcfc52bb95bef379d7400e586dd0db65724833d36996d5b6d682483edf5f3e873ba0bc79fb16773494625db86b231ec579bc1a65af6499ffab216b5c496fbb5a996b5a82eb675b18636c97e8eac1151266733813fafdbbfbafe74004309e0323b8ab69d8816ec9752ddf5939c86b2ed9fb3e9d109e1092ed207c12631919ef084e65f7e1550ca70e3a8d4512ff67ece106807d0e5d2b15d86e30cd39ce459eff364cedbdfb8017e4cfadc07f5cbf5f77efaf3eccbbc05491f294baf69d57cdf7d1bbfc077bf7428632a298fddb8db79f8cbae1778e30607862327474c878e6cb6a3c7eb10003b1f978106df75218efbc73df9e8761f11b9bbf0473632dbb46953e41e4fdd7803f1e3348f8f1fdfa6710f8fb8810619ec0020d4dfd3c36d3db6d29e1e9fb2a7a703977db9c61e93a4fc15e692e75d15419c01222a0832fb67cb5a10426c22b0f68f2d6b4358ed4aa368a896a5965a6aa9a569aca5340dd572217a75110896ba58b508404080a3acb9bb13b9aa56abeac90ad3f660cb9a1058423c6d71cb5a106bb610521bef4f6be317de91b01b2fbfc78774bc0b71372fed4bbb727264fa755dd7510976ddf7e1f8d98121930fa46946f052ad3ddf8ef2ea7b6b434b6d38564aada59b7dca86e65f615b6ae948d5d8d1777e3a522dcefbd1c30f36a433431a8e124be7836c33e7b7e1927b414bd350ab471c618e5c84bea568a6106badd61e73502dfb99fee4be28165a5d2b1ca19ddde7e40871507a63c9a537cc09c710c78d90a6a94fd65a6badb539a4692690ede96fda7e1185e91b389a7bfbb582e04534cd8759a25813699a2c5114459aa6880e2da13cf563348d28ce5c9caf43bff32c916662a2199a86a6a15a340d0ed7314505c22116fb99b9bb3b9d651ce642561508876a95ea17f5b19444cf66cf9768b6dcb20675b5e57355f6ac01d1b4e78fdcf7c409b767524e09640338fedeef42236c4774a17dbf3a827ae4be43c23df732042592a64a6f3b241989fc2d23a13fff0b6b0d61341fc90956fbd3e624fa3017a23f5f3cef3bcffb8e8aca0a4b92d9f275bacb5926dd7b45bad0b3dd7f437c6fdbb99fb9c8e52e0ef7b9396673dbe6c2a03520f76e2b13ee6f112ebca18ccb479c6f6ba3ecdf2a5137d0e770b8dadcdf1a7dfb03d5b6bfe16cddd6c1703a9c9b0a62b910dd32270f99569b0bc7efd6fb7e7383c97dfb37b2915ce4de2bb77411fbfef417dbf901818892254d52609af214e5bb3ece61f317f5753776df9fc97dceb7ff26960bd56c64d67ea8dd5ce4825f918d7a9d925ad7b47a5599f2016acb5a0f637aa8b2ab13285c9b366dd66c7f2272bb162ceb102c1dcdf61f97760f4ddbff89cd7623f2e73be1b6943e54c909b7bbcd3db513c9f61e72db943f7fc2c07bb276ae7cabba03399807529a10a6cdc659898d4327af36fbca073dded21ff57b68199e5f0ffddfa34708f5df8308fde043207abc0cc77ad41e3ab1f04b1554b024152ba2bcb2658a45318abdcc0890463a51ae0bb1904a4ec223c849d8564145159f638d2063abf3bb15d8aff81c8b4cc2ec672f33093b7e470826ff949c646dc8491667e160430d5938d54083163748f7e123d320a50454a4f80c4ea6ea471650ba0ffbf6addea26ac76730339a9492006168b31f4880b39f99843034991f10022d480d58440218275537f0f8d9f3d060709e879ee160d72f6daecd0eed248dc81dbfe3f10e4d250c4d4a0250e1f13ba8ecf8998f2410e677682a3c2210829492003cc21b763c952aa49404a062444a49801da14ccaec67dfc6c63cf4ec77e85908267361b513fb1d58da198cfd6a3f87b222b2cd76fd93d1dbf997b62b63133871bd077a6cfb733a11c196d7f0678c35dda2d1b0c676db77854fb0abc853a0d16834fb3a44dc477d223323b28d8d671a8cb5386cec0a9fe4d8f604d73de882a33c3e831732cc204399186488e18ba6189ac080a10986305e80e105332ebce082184c2e30e5246cf318519cc9a3d88c8216d044692127657c06c7340bd3d34c775dd179aceb8e6969a413429946ce42b664d0622f298fb3e0b49c9471194bb1d7797b060296684b46b12f721dac6b2ccc4ecbd8a66085366da05161b6c1861950d8d84a23823342a005822d47432a85ad355c05521f58430d5f81d4133562e06aa43c100336ce022925363068810b3048c3054c69bc604d9417acb1f90527a8b1066317e4a46c711aaecf2ecd819b972e6e4d778d73dfe04c1758db829c84b385c1065c9fed962bbc925b7057c06ca43490df6e99915258dad98575dd32e7f08aa43c210b7252b63929631b831524e5c9af66056aa8b00215ac01c31bbdf04650548086ddca001b18f0362e205d44c3db54000d2d9a05fe7b54e0c31ea1a43fb472923d232759cc0205ce48d3820969cc8852821968982620e98ffc92fe40930217249082325ef8644b76fcec6558864309ec0867a1741f2e970be1327292252327590c0304c838d314813328888104148c21c303a4fbc82fddc71862cce00031cc48698099306818210c306a6000185fd8b0802fca4c5140192f46a0c48b2ea824a08b13dc808013d49c0e20290fe5a99900872492f2982087241370a18301b8d8a20a922dc890e0089912ec60a4045af050002db2782a9245560f44b2b0f001098b2b7e18225d0cafb0c24261399a5600414410578488b212d3d583ec635ac410f6b1cb155abdb4b17f33898b5c4ee262953792b8583f3f969e8b888bf57318640338b3c84778661181e509b847daa6cdb67f84cc45f0cc9a581c1185c511565c215584155255606131a20a24b0b2646b8ff86a7051918f6eb25c4d8184540d381f7531b3f1e3e029e4141629a6a28022892c502431e6092590b84f28e18418a025c0515e0de990479ccc540d60ddae2b464b2e62df158eaeb7a1554eca3fdfa56b189cbc25090fc2e423bb05cc124a80c94776892f390967259af8c2449516aaaa26bce4249ca996e8c00b972e3929632e4cfc74e9c2252765cc0490172ef9e8a60b5513df9d01a832c02472640ae926101da101260d47c9c5ae9ffde64e2154f9e8c64b133909e72a2e1d34f185899c9431123924529efa92a80322457d44fad4072285a414698844f2d42e57fecc3dde2b8998c049782e919332ee126609305bbc7860b5258c962aca23e6d69f5aac94f8e2123de8eb111b1209a201ac5b9c4a88c945ece770cc8f13ce2472120693434cc37c3a74acc8330637ae7141b3658dcbcf96352e519b880bd95dff48123a54d5d068b42b46b54af2c831170b242379e4bec20bc923f7d863d39f4ae423bac19619ebf81cfa4a8c0b9badc196352a339b4b95edd4a963225ba2327d22d3c79aa34bf9a955c66fb5efa598c662635aca5e17f3c774900f4486985319091124f21056b224b1fc9d452227d9c7e1a8c2c63fb34c2c5336768242f1053d05fd008ee2ae37378f6f6ebec8a16c8c839e408bb5d537373fd68d83c3e69ddf7600a9ee1c7ed207f64875db10c7fe9c19c97503f637b42ce7717cce37a166ef6c596b628a7d5d283fd012201d123de7cfd13623c1be1f2fd6370bcee320995bae8b593e9f8b29d68735ed410178e3c3cdd7b7f928e79b89251f49e558943dc2da27827c005f145db48fc371ee6f1e9191e0178dc8491c11172a221fe530c4633eabed7e72f71756176995d207052570d3ef2410fad3888cc4ed22425991fcf4732829a51308293eab7d06f181fa4aec5fb1cf3199b18ee9ab6ca372127e1b32718d4bdb85e9148fafe424fc396492dffe242227616bb3ae547c56fb967428f6ddc7f2db70d6a8a030cdfab0e6f6cc45ece330874cb6d436912d59bbfe91fa236c41b3b7ffbe9f511909fd2f1c71759ac5949b9d572af519e552aa45946a7b39bd7d150a5581ae50976075ae20065886a1dab2f6456b6f59fb5266d620a0a67489d6aacaec9b2d6b5557bb56455535a5a5caf6a3a5cd05cbd98ec82389ec960db1db7fe3426b61dbfbab412fa007023501facf5bb70fc93c8c19e1b7fafb5c8cb9582b8db65ddf4ecd8b43eefbdd9eb761e15e8a73595db57b8abda7f4066dfaa6b0c7ed95e81eb9f7be53a2bbf33617c445a0312067eff6e4f442ee6efaeb026a27773f99edab9da4b0bde7f413babdedfd7571ebac68b4cde94ba3ed4ddffd4387ae91bd4c8e65e3fe93443226f37dcf876d39cebacd761b4b1fefaaae9bfede76a48f2d22bb33dc913e1ee4a27fa7f78f2b70bcdd7cfaf631ddec36b739e76c739e73dd7bd76dd37a0145b5767d92e1d844d4a25a55d48698aa597112291a9aa6a6a5a996c416d5a25a5ab82ca022c0ed81b4803a404180e38c7dcee7bc0e3438723cfdb94676adec1817ea1e470eec6b3ef26ecd431cf6f6efbdcf7d3afefe876327e771fc38db76ecd9361cbde7381d1d9d7abf3ed2fd1a5a3dd6d9cb8c64ff5bda9e92ef998ea635176948cbb8e8e1c08123680d98f3f5bb2e27279cdbd373e7a836775716def8aa618fb7afd356eb6c998ddc8761acc70aa4051c41a70ac79f8df3e07f2106bf778c3d1fe5d70b17d1ac713cd6399f8b1f0dc7d9867d125a5354ec211374100c5f5f445f300b1ba2f3301b8ebfeda8e4f53daf8eb36d4358ce17b98e7d986b79ff3dd6361f61dc853223dc778fc47df74bfb8af9f3b7bfaf7adcc11ac77ff5084966d55c04847deda7759ac0b685ed1b8e4db6cd099bc070be623ba630e2502a0e6c00c759b3db47abaaa8a4a4a29e9eaa5029540a6ddada62a3a585a64c991a16d6555515959454d4d3531597e252bc696b8b8d96169a32d686b34c6de642937b202be0c855e1aa38f7e442e0d3e7a05ce8f5f4392b2e14c549f9d08ea7cf6d71aeca85acb8312e247bfadc1587e5423a4fffdeb7d6eb3acf52ab54b74c532c2b058d8bb4aaa96bea194f5b9a65b2525ca48fa3ed14eba46d156d9fb485d2d68ab65156ca4e511e5ab35476ca46d9a91b3b65a5ac0e930b8df639269db1fb6dcb458ec907d07ef73a9e4917724cb4b9e51c937352c66c66e88666d35243e96f6c249113a5592e44b35cb45496ca5a55b9500efbf629968b54774fb35cc866bde895d534cb458a44b13ab9eda5575859b6b3d862706ab9d06d92443852aaf8539453f995a37136be459ba8942af48946512a498463775c0ca0ff5d402d0c3261a009238c23aa6a5848dd1a16524e566a5838cd1c2cb91ba08b332b5844050d415f5a3b6b5830edda021a3a0c7c7a16f5c349a3dffd5e6e8519acac8dadadf2256baed7ebf57aab9dd03b34b73b739ab5da61ae2d9c5b538d9a352019e7a91fc6983d4edafdda69b1c3a8daf527533e723dc2c0dd645ba7ae5f7fb5cb0b5fa112359cf6a68a4fd87604f3169e3102c7993545d6aeb0b287a851a5bb3e91aa242ed465b1eb2b39c1ae3fcd58b9c806f4e6a472201880f42f1d22522489fcc798f8b3a590696602a93f694276f8ba726d210ec71f3353881751858cb1ebbbf628e953a79a1f57388aff0a69369c520ebd201a45996d8cb070415580726fdc1a1c7e21d32b1c63e36fe12882e1f853db4cb5a16a63d5e6ca6ad74b27d977e9fb8552896e1cd2bba51ac923ad607369924736491ee9ffb968b9ac8dc071aa715aad6d31edfa738d0fb9118dc9df3fc7d992726a8f5bd6b0ac6c9d1080b286e5b41d8b861a16d38e6d55b165cbda14517b0cb2eb1455f608ee3973af428bc27adc6546eccfa5cf6b54646d599b226b0aaa8d5fdf77b511388a9bca5a154f43d4883e08b4d61a7e77cbedefb63d2ce643f7b77fdfd95617b9589fe621419840b9e70701c185ec66b939e75f0bfe136c848282762af5f7e2a12cc0192c1d6389a574f7395f4ae91ece2c5f414fe058eb9cd405c614b425352cab3b9d56bb71f7ebe494f73a4d9973765d54b54229a59e67a9acb516e3acefe3388efbbe8d8bfc755dd7e56ccbb83e8c3176b9ae18af2fe79c5fafda9519afd70b046b5756b5ab346ee7f2170bae685bd6a4b032b4652d0aad8d5ff2af5700ac4541668f5f83c114ef9cd86e345f7e10bb85a00dafd89f3a0853d31bd970c779fc03c179fc7d0c326100e957bda38348008e4182b87b6d02120314e6fbc25550068dac7966bbd33403f40538cea63d2ad15d5fbefb9979668c33f30c972a141403388a206c2db0cf19fe7d5bb775e307acd8b4eb2cedbe7694d6bdc56d928edaabc5dabc7da79bdea8fb4d2fe0d5c58a28cd493a7bb56cfa553bf1dd81a57da5690046f73b2da3f7a3bcda307a611add67ba51b8f04b9ba512b828befaf241d8cc797e5c4cb3ebff0405b9d2f446f7cafd2d518c19b3062881ac00679248a604bc4f7feefbfe600dc7d8f62dc4740efdfc70027c62b713dac6cc9e396c10e6b37eb1988d7d92886e0973ebb9c8cc61d7a7da89ef273c7607969ee4d8f49d706ddab4b195e6a68f42a561559b072cbdcc24cc9fef5b14dc753da514767fdbb6bad5eae0cc1d080b1cc59f2049c4fdac2ff6d46a7d73f73bf7fcd86ff6b9cce51acaea2f6da4397fec21449440e68f3baa3f28615b76bfbe33f1eeca9a5f5e6e47fa6c9bbd57ec09b29bb538a410bfe36b6ff7b7c7d4e3aaacbb6a99fdedeb6452dff7116cd372dbdfd76df58ef4d9dccbbc50d43d4175c666dfcf07619228ffec7e736baf75ab5d2e5babb4dea7e7acc6661e7e0745d49834dbfb3eff700204bd2f3ba0d2c097f9e8fa3bf7d51f77daead82ff2a3ee6dd697bb1bf7f6feacefdfe9edafcecfe9fbdee7eebdef5d7bf937ddbdebfa786edbf6485ce8bf415142ba9ff3ff1803e6f979168b6dddcfffe12eb75dfc713636abb7ded8ec9f009df4d8575e7fa5c91abdbee7c7a9f72f50cb94bcfe755f19ca12a3d7cf4c41ad02f85830205dcc21942546e0cfec7f5d1a757afcc2d07bcf3dbf9e87c1ba3b5d77d01aef7bbff7f3ffb6cafdccfe557a2d0a2b1bdcb20605d5c6fe79cc4519c39b8fdf96b1ed5560100b402547ba1eba9c471658ee602969bd2ed211047dfb21e667d69d0f7fceeb2e0491793fb7bd3f2ee49bf7d4eb3acfebbcce9b92a86e32fc7333b1ef9f93b9f187d508feedc96b7f8939d7f7957c63efbbcf7fe622fe4b7b7ad37f8c01f1df4fd7a357de5e7e9288e32a955523db738fb47d0d47285fb5b908c65daef5b1add4529713baeb7363de4e5d36fca4cffc4ffa383ed38d9956b3d2799f60da9452f167df1e637b661b561ab0dca9d1f98db5c89da87242ca2e722149eba54377da5cebf4af19c9d29ebae62217fdc56c51316b36a55fca88d932375be9f41eae710321dd02cb2ac64a90f9b9e3ce29a55dd7751ded3a988b414f200a75dbcf5ad84be8c7c5faf25f624cccd39cbbfecc56ead405ba58ab0c226b566a86dca8fe111772d0b6b1c3ecf1ab5f3f4912dce5234abd7c4429ce479466bc6d5c7e24b97328733d931cc402902eed51696357e7d1a58d75936f7be11c832949fd0ffb7da2b6bdd6c07cb1a75fdac5bb74a1944efaa4c7eec0d2ddf32f165de817b098569a26e8f2055017d8bc617bfa056bd33bdee77ebc746f5eaf745456570da7d432f9db1f3901de97db6545c227a3127d3752e48677055fdadcc9c0f8e5e3a01ec0b1eeca84993d6bd8b2b685cdde9ed2bff47195d6c68fe3ea9edea7f46fa6d8d3f9257e9c70ecc01275590abed2ddafc79c4fc9ad808293d9667faf74f78dc7292022bd2ac80c084686f3f6f38391e184b58c04c80d7f0952011846963ff63a1f46e7eb62ffc2b139e76c6dce16678cb1cd365b6bede36cbd4c11e369cb9a99293355b6179a1a8b93858333b6cab8cbd4d660cb5a97a68d7f83dd54fb22b5ede32f4fdb7e96b2edbbd850d176065bd69ab0b263574e4ca8d9221a34b52f5b1bcb28282d5adbd2aaad9da9b2367efba5ca6ae3cf531bbfc501bb954f63fc1e95876cfb3106f595181157a28a30c28b741f477491e222290f16aa2c4d4817916042d21f534b24b145521e25b4685142bac8c316aa2a2e5c7838420a4b961f9000337dca9503ebd20587892e436cce63ea60e4a4d585f3765b2d613739385ebcb876c8593e243e15745e1a61397d4ae743269da598c69ec467b502965cef3e6424dff9259e5f7e25745c3ada6aba16595db0776b5d39195655758343e501972e60c48409e3250c145658803dac26d0e36f70aa5cd92a1f59d88d0d9580b09d93f3312ddd0358cc9f90d3ab0be775b434b25ac26e7270c488717db9e9dec5e423d71739d6d13257a8048412d32ac87eba4c014b46b247802c64922ded9856c092512cacee817d9a91dcf390b9f3c7ea7477e96c9996ee8195696984a54b31abac2ed74b23aced0e02421de18804172b4c1f49a295489ffa405bee85015d01ceb0deb6cc0f98dde0389820128459920990463a0a582241152e545c3eef522527611d7212b631adf3ee057bd5e75f72093dfeffc3303ab8870ee33ae4e0841039e0f0441038388d0142d21f3fb2d30d5040f90d4e258a0f7e4c8192fe90e2ff6325488944f645fe2309b0c7cf5c421835324f20043541628045b2843051be00a1b5d63d5ee6231d5991bf0e424a22f4837fa8306af43f54086a663eaa40987f0da5ff5f66134230a343203e94edd0e37b7c91b761a4a3230b7bfcebab305162efc29291ce13a11f40219d90244aec75429912ae8d91ec9ba064239d5f3292329d708afc61d404fe1e8e75bb7b38e7f436f0fc9f17631fbb41412e607777f79d9e0f8c92b57b827e071673ecee2478232c71f1e8494a915b6badb5d6a7136d322961c0c5265068d4cc336e45e4c80ee0085e38ceb6f759793e9a735a2f66f3519d765aaf42d1683423b46a5a66b4d00819414b8d04729565b1f5e1c393d9291380df0c7b5b5a4ea0d6534c8b494b4a9bfb094d2c52f4979edda6d068b422d068010d19d0a04234021a3b481fb94751ab055a6a481eb9399e3d33cff65c25a536d3b96d8865e7b6e196e88564382bcee30e05e55330169ee246bc9f4ebe23d2cf0a8fd70eb97ebc9e8fd9a69d52f9c80b4f00facc50f4846ba7f62b1fe3ad61e546bc87e2a27b3e6545a1fa6ccc7d77737ee5629a315b5e6d5933c3873464368f2d6b66d036fe3e97669a3fb5008d3389ed3fb148b712f233ac7462dc6fe68f227948e83fa19b89fd8c94df4ece1bbf90fb201d402617b0ff5bda198e998100fec044cff3685704841446d8534f2bd1ed791b80ed26b0ddbdd5f94bb3c6a6d9b26730d93362c860db4f73c6a6d1923cd2676357d46bcf252490d7e77aeb0a7d0ae509a758a1e16e755535c6cc1ebfed55533e45737733cdd2c52d33aa064a1771b76409a766842556b5ed242c51c3850e5506e8fdfc51a281ca5ce8563e46cb6ace3d4e2d2b9f34b5340a75e70f77958e4a3292b5f6b367ea0e2561560922c58726564f0f0dcdd0dc02bb7704c31915f533f18e985f31900235e5d12268115e28a5cfe7539268066d012671d15fa96a2ef4e69c33045d7a4a398ffb7bd46ef2cd4d697b3fe73767c6a15bb9e86fe4542e84b587dcc87dbb4f8d0ee5504d5c68eec7e1940b4df7749369bd30888b6eed2cd2b6ce992409c544927c01bd1fe5d591efb116c5fc9ee7fd8b5380dedf1dd145ff3078c10f9ccd84c6ebc16658a0f75d6ce66fa3820459017aef85452e42d9f6c799883dabe9ecf909fa71d1843ddf5a1be4a2f778fa0407aa021ce7cf2817921a251a6b59b027d6533799f9d4389c9eae7b84fdcf1e61890b49179241c41fe95947326c596accd4d04c810ad22148871a1a5a2d055653413904e11054058a4a5a535881f3524bc194329acaa8613f399dc97232d85032d46c4a51b0b5291952bbfed46345e228a594ba4615b6529d61a5563018e953ca29ba65ad0c2c9bb3e252ce329c8ef02d6b655839ad428e81e5639469e26063bad96f62ac2579e49a2bc616c814932279e4185434d8755a33341a2d8b917c9b8f26077bf470b4ef36dcdcbe2f06bccfc43ecd48740d9540a1fa371bf1df68341acde650060b4b8951b5b777a92d6b669a6c186cb4dca37fe6be0ea003c1796c102d37b2db7eec8d4017b228a8b2ed1ff1a16a44b7fd98b51f34b3f63d23b9e1f6b022efffb9906ff14f6efb757ff5eb827800ef57ed142b5502c980f49164fd5bad24b25512552a89a84b22aff5c8927c77a79bc317c6188763936f672028c0b933d52f7757f8737bf5c35f6c02f1ffbeef8b4d21b319def63b0912e19a91e8c63fbb8ee7f7481f7fa035c0ed69917b7abc3edb9fd317e62edfb66cdba6e461918f1ede6aa5a5096c4d1318f6a7be54270a58eecc2fe809b41873e198c2fe0b8e31eb2485cd8d2bece984dbdcb67163fd5a819e00e757f965297db52bce34f92e8dd66ddbb6edb73265f6b66dffdb6f1fb4d5f6c664fc1932b15f5fd8c9acf4058fbbde538ee3386e72f3bfc97d94523ddf6afad16f7ebb8eb0c96d8ee3b86ddb40342ca5da7e2b18d097f6f69b06b29a77537d7bc0f3fd3fd8fc7009969503e36ad39f4d341a7403385429f79de6ae7451d226b00f89d79cbeb052466b8b6d6cbfea4274e24e02d938fab8bae4b63d92dc8ad89f219322f3392ebc2bec51896e2e8d335bd6d638eded83b6aca531b5a50bc9dfe8f66c48916c3849369e646cab4d60347db1820d5650c8c64e2551ec2deea4101d1248fd106c01942e521ffa55fa50243982520a906e7a933e4b9b7e5b248fac926324962423cbc82fce482934c217670c7153341acd8b9114b265b6954a81c1a80206141851604c491ec964fecc74d2bf014ba01580b2e6c5d31ec52dfa9549840f41ad54216c101b101cd4fda1f3c1eb013f7d3ce41d5c2478550175b891c30d0e384eb01b7050c919418e29311b74d4a043834cca6c861d32f088a147d3c3a05f085d0000d34e940c5ad060b2b0c1a4896d78daf0c1c68fad9e357cd4e0200640d8fcc0a0833402f002a035412ef0a005422c18521364051fa880080d215a209c21803403302304344529d865189141803322a0e0688c2162209921124611300af085913247bc40d285014e90544b6282037081802d124046490914a0c502b26040d6085834e00a0758f1002c12aa80001511984202574ba49840142540418131263c5101272c802f6085811032308051004c4068224403441bf88003414e1872228482071d085202f240009e74f0819f14808080835d1f87c067d7c722e881f243051fbbfe0f5e816749dcb40d360b1ab49041941d2600b810bea061f8a61e31f09061c70c3329321a746ad061436c4a8e11e450c17103cc0907879b1c6ee800567991c06577701ebeb99f640fd9872f88cab77fc0501e107606918fb60f62012cb28209f105d1bedeb007a80d38deda15185bd6b8300387a53976fd118888ef471275df105d6c15c5ba58b3ebf704a92f42ad9dc069d75aaf50fbf381da502fa4587ae5036aa3678b3d3f364a04da026908c406387f02bd001cc51e203640a02d10280d50ea88202a178b67a9486c53d7fb6c46a2405be07c117ce886d579b65742895a6dcb176c59db62cdc69e91eaa661934fa422901ae0267d828c00fdcca7e59b5abebb059979b5658d8c947de9508fbbf291e8a2ddf26d81f948144ba02575d8b2a605995d14debac45767be5214a108680d90fb1da02cc051dc3bd37ead61b5e18e0b855a8cd9b2a6c5963db5d8b2966566772085dc90407a7e2491c51bef0f943e1f13f9888614480d900958133954312e3a5d66db66a9171e55559f8f6c28f6c400b4f38bf82504b353c36860258af1e144cf131c8cf9812200510449217415640a222a40a86200584556185d21021643b2886451002d8e94c00064926c8180cb8512132ca036c2091cd00509d38b08dc324bbe28010c13c2b080190c88318ed104051b387302192894a1948227685230230469a09cb182160d8d1654c0b48217d434b14086164871410d6ba6bc804a1a4e30c8814d9518eca0c6d31a3e6c41b111441b56661b2268512c18d18254942c4c532e28f1c216189868a28aa18b0c5533809162458313358cb1218a295723a0820ad60d573865e1a0450e6474e0a24a8d045dec508607309eccf430860f677e28030a0d106982d012420556d40cd10222d65c49238a4d116a18b175c46c438a86a5852c4c48bc30d594840c4a48d152c396294b5061c2a9891ca8aa70d9a1cb93171faaa0be0401c64a1822aca2c418e184d41359c64c41a144145ba460e28a6a8a2e5454550106cbca0a27ae1883451459575950a1055609ae2093b585165c90e9e1221fd1508c02e93bad76e36ee7e12fbb5ee08d1b1c188e9cd90e1e3d5e8700d8c940830d441e1f3f7a7c3800f2d3410080823c101a0af201911010043080108ab6110144381a8244a448018c1c416280a42407404002942860010c18a1010e78000910888004964ca0040a9850010b5c000319189934d1c00638708213143aa0e481271f480104211001141556589ab4c9c26c6146994cd385f9c2846136cd18a60c73862965d2306b9836cc29730493cabc613a4d1c660e538759659260ee3079984fb387e9c3fc61424d206610538869650e3189985766d42c621a21e71153cae5c40719fb0b03f2024a3046a9ad3e04969f9535b140b3e79c73ca39017caf5b05a4eca854ecd8e992562a9a110000001a5315000020140c088542a1509806622ee61e14800d86963e6644950844511aa3389261209a310600838c013023403052c200e2bc81b72899f9e7cd6fd94238a6202ef7316b79e56720c2df59f15d4193a3422d7f5c7fd6e72626066d5eae68c65d70846f165ad570db2644f984d2952ba1f4e0e586ca8c371cdb51c4f6f77d77b6616085a60f624cdc9e0dc6c06f929dc35e5b3213203a3748268a061ebb5924a00487d941f763060fc7d3ba613e0a555fbdfda1f2bd8633d15304cf0398122568d56bd3afce8293915f1dd891b9ec8eb639b13be4a9e945db66fa17a9b3bf4a9e6e04f2513a4d8b368716b6dc3fe600652f29387dbfab43bdd8c698d5a0cb29b303139af69d500d30ed69d5a0f3cc21a78306b43c8ee9eb42301d35d3f111b422b84340c03e52ce0800c03c5d1aadd1103ca840326ad89d14edae928aeddf06655fd8054580bf7fab038a354b85f296a9918481b5f28945d9dffa1c53f85bee953d5863a4601803ee49643661d45e041ff98531af7f5613b878f8d5d52e46a3e2865da20efa1102ca37fa4a4e02cec7d743f9475a8dcaf0e14e6fc3f4db09727d561b4b742c5a77bbacda1dc07866b5a3a32988de12b71c0b9b48dea7830b802e65f00dfc029ad1f82f5f7feda24371e01287d1db568c0255e39d00983baff1e7269ddd2618c5bb79c63a748679a82debd2c32c79b9ac3f2f755277b8fc2ef4a11311a5c8c0e2a0560a138217524d607c503ee1a4d9efd70ebc87c96655eb7040837e8ab04919e28a6c623b96b0b54313a53227d26357a8ceeaf5450a9699dc13a7d5bc691205c0716e5b6d42ba580d86ab6232670c5c8d1a06b48a0d346af5be24a4a5440b3e93278e1f1e152bdd4b198efd6f8cf45f53ad1aa69852b31c8d4881c38146c0b028b21603bf50a8f0cab564026c09f369ab3b91f7c962e517675f7c23f9f3c44b7c1574b2e6799a58d462330eab8c25dc34d6a9b2698575f9c379eccea95d9b3ee5c14cc0d3cdc078604a30c0aa075d0437c5c5d3c50504ef991ea190c5ee833435e538c176c606662cd193aacf527ee6cf4fe6a2852ff3d025d218a75801f149a5484cc8d703f25ccad9a19947fc4b56dc33cc3a60a2d88f42a0375cb49e7f4a18c5f6ddcde3c3b479fb9832bbde149554f6f0d7f1ebe1f3a32f3d722c90bfbfc9f25d95ee9fae61af2f68cf847731a078558e5f9858d84fc4628356dab389a8d38cb6d26c8fcf6311a43a8de227c46dba0d34b1daec67cb564618ecf124c93eff025b4955a2d3f738915c805796a65e3a6e0a7674ea6a89bcadd5a89b3be58fcd3d69de8f59370814fb7ea02cf7f1b00694e9b31fd00fa807b07d10fa4e9f0447df7e93033f2e0c3c36365993e1da49fba9173ab4eec947c54e6f0f2c184d855e9725ebf48be60afa19053011a43a0353fabd99c4b43676c5610a55ea1bfa35ba5325a750f315f2f13d06b113cc67a18a0d547a5e85b02a76b01b1da7c35eb0871af840d7b91f2c60b1ad80e5351a6a526f49115c3726f0ede861559fc900d65b5c675bd0e548220760d68d41e127c26a11e1aebe2c9628fb2735676dcd6e423d1303d46a2bcee761964ca712c3ef1b58f8b12b74fe0bbec74da5bfeef9416f90c34d028ddbef4c31a6a5a39e36fc311c9eebc68dc756398c1eac625070c1acfa8dfbd737ca4d600064b03ade029a51040f6d7efb1ad94241d662dca5776f80f9518c19b154c8b064b1b87fba8981f0bb3ba40fae8dd4c0c74c2c4c2baccd0d9c97446dc773b4d53cc9a60d72383a78acc78d4e598702bccc80dbe41e740e60ade3c209b57cfcee2a097d661a57f9788353118e2e6944f81a68a750c34635025b81047c3433d011b8ff9a7c262bc1f383761486ba3ad7cee4ffbac6ce04e405e454e9695045dcf28706c5fbfca4e6948cf41cb353b9d15b983130a1099f708e693f8e1add347df153023559984cee54ec537866262c8088731f39283370ef6c9cc245d2e4962e3f09646518c53bc3b61188f5c044e45e91260fcf9b7bcf6a06df0c35902601bbbcba0a0da24986afbd612de7f7720a8f1cd5145cc91358ddb0bef95df19f6dbeb02135836a1f405dcf40b85aa04e6ed39e907429dec5e1cdea3472e0b7ce4938b47012a8d0755857b8e80ad7632944e3f322ab2a7fb2d56160a9f3e1358f8584c399bdd8b103afc04ef958704f4eb73bc1ff53b89a2782dcf13efebe0ca17bf3957df2466148761880605472c82f24f3dc662575c9cd8dca3e65f68a713321d67177de77e55a965644712ee600d17a760e724c5da0da5d41a5a799449b9370c1281ff496e14dde1f5069be1799405be2554b72d4bbb836a9e5b06d31288b464ebc5a6c76569269ff06e52a409b6dca59a37fc7a102e8d9c359d447614a880409e8c5fbc5bc7c3c46536103e650ea0e0856a5aac4a5e1147c4fa46e9d7eec862e76ba00664cd79aa1944130162196065f914d019bcd1b55a00d5cc12efc0b9f1fbcbf48790ecd4c5ac4ebe89ad469c4bfa4479794b8114f1cd04db45ac61aece26bd43c51e1156b4c3c499faecec5349a97c9304edb9dfb44140dce5b07cbdfa8abe5fa7869ff93e38e09b24467a9970d43c77e62aa7d2020c6e613183d3ef7a630ef56446851267735e272af9fee95ec17c39b3e589b41ae20461847f342c8d22c4b3c28d6d1d723fc7d3c8d9ea04365266ca2f6dfaeeba9662e91c82d820bc157e222f70abe265dd3ffbb236990c8fe485e66860d81286a402ab310950e700f60b1f4ac262a068b292b3e3cd558bdf128a0643590b7aee983c538408edc7dccb1a8ca5791f9ba5212e63b6a3bf20e51151184497ada94a07f3c1c2b4d0aaa2422b4217cc9e1cf4eaa66a8bce14cd267bd6e60d13fc343f221852206855b9f71d7428936b37c0411d29eb12fe61da96cc93b5c4bfee701c875e6e79f4629f42ed817c38abfdeb87d6be6d82af3a342e3ed8b8b341776b23c5edc42fc1e9db4de12ce3d93c9159b9f6b6465e2de74a3d496662d0290d8d11efbc0a94a7da4b84d63838671389ae4aee356740782152d9565383ea3a22288649ef2b7045c9c5db760e1b05e9e112b6e4b50e0e9fdfa2cb9e79d7204a42778af7b0fbc061e16aa4ff9ec28dd39a248feb2d4dea6e84cb2b5c6d4ce19a453c1dc51d0f40c3065f7913f7323b0e79122243e8f6e481cc5058fbcaad7960870327cf722ff0947a260c8e5f71c7f72670f7883d64ec1a2fc45c88a2199f84e7c5f49be80d1c1392259b7580268f38fd5c41b06d0202454ba5e79d5f6a98bb412b6639ff57020da4b30d7a13a7b6d0d86685a55a629e10c940a8d2f58ba06403abafdb4e916c697dec40ad9b6c2c336a1676e88c0f00c65e445ba53614c3064658fe9c261781e36afccd7884808c97604bf4d485be8198d19e4ba3134092864bcaeb80f29cea2973c17e3d7873cdbfe54dd48ad7d311002e1c9365e0cd7bf4ed9d94620f6528c4c2cf3ffdc6dc82efc1a9a1cedcdfc230ca473f5fd7e83ff62834d36791115cba1e96a503af9d223eeca1c8ecc9f830348963098ab221fadf84c87c2a765a45570d4eb50d7f830a65ba0157e842a23a8f9abd0c9ff73348735d3ec253cac949a5eb40bb5ff23568191d0b6c02e2c8925eb976758de3e6fa4d73c2b409207bdcd096c1f76a6e06b30217eb22772b8ad39bcc819f97f91124240384ed84733141a4b54d5b96759bc4e90b3312c9b7771e8f535c8986d931a0a9b365b101920956af64c19f449ebcd678bcc7efc5c73a92197f5fa5629227d883c4dced4b2fbeb1ae49de71f6d6ff052c815d537f8b087b7c6b0cdd914b88cc104a10355ac69f383d3e3d0456c1cb22d3772a750b3c1626011eb3e36191baf8de2c459599cefecf5b8602917f15c8eb3596847b2f1f21b58b882995023a5e7cac2767dce1a8f9f16a5e8d95b8445b4e949ba84932f0b90cd15320fe3d237dfde415e6ab17d9f3a3afce9cca17b5ae3a2e706c0faecee3750f125cc104d5e95a16e6e481da1c7991cb9127801e43c3e0ee0280ed0e1f276d93da76783df2d360f54962d0ca421812e4269b2fe3b43f86debc6bbda8179326201011e62e6fc5fcf48ff268e83ec4cb3b1f44a6e0e4080bb7c3f1bea8720e7620f45f87cf35dd3e05e80f357f87f70db8b095b917b38df685a4bcf59a4686a3847828a571d019550116edcb2812f7e9a04f0d6267380542503804048a6ffd2b3b8f38c05db4ae9f462cc0da1a963ba847b0df8a0ec775f7817b3a7a24b86c7e57a0a4a10f19d9509415089557846623d1ddf938e1d72242743114aeb0e4c23afe0d7e56638747dea688a4431ae834284bda82f6cd161c620f6601ddff296d6c65c4a754e09f5ca02692d57705c944924b46aef351ee4bc2ca11bfae85e06c1dc4c59de534875e42a14c499a5a0a55f213adf2d4da62c1ecd55e0dafe9756133e86b3a038b7ecd0db0f32de7e3af94eeaa0d57653adf62e131b085c757585a7e0ba0be4a25a8a23df3de50d8dad786cef7142b2bdfb24d4b1fc205f8a450ed9e858d3f8240ae1972f3e906080f852a926bf25315031950d0f1f66720499f819ff31da02143f676a5318a04470038a9f3288bbadff3e00e028cbc9d1077b201bbe547be96ff810736205ce889171f2749ad182003d0ec8c8ee2a82b395a41b3e8c90b48a937605a0d90a3b8da2b575fc5496e2a24e04a4c2c8465d34c99ee11cd1c7d09b3b97dc65837547222c25eb5f8a9b87095da22870a2b1475c9306f7134a6a23e3893a42f69249cbdd3bbf97a0cd4bda7b64a8366971b6c215494d6fb841a4116a5b0f9c7c68cea28f1c973ff276566544fef557c389b13846d89d572094f8786e6d19858c4538697eed2dd0be18d2a14d581137ac04a21fef59a7697c6b37abdfd733a1c7f5c9aaa69d9e1e69b6b494ed95eb4f1a577683e94b9bb1631cb101284c2c4c9bc394824e1c3354a8215b9f512147e5bfdb71c723a7bdc60361c0aa41c676d00a2b7db66e93eaedc344839167dcbae76bb076d3d1beb383d8b5406233dcf0439af87f7e86bd73d42bd63bd86d47c2c4d8c7eb223f86c400b902ec0d55e3bb744eb2fbf8da782a49dced8137292f34623b034ed6c9da3588b1ec938cdd0b894407e1f811d5ad5d61b84ce5f9bdb8007cfb3520d857f9765f62d8ddda5f0973a9ac2cd0839190a434e5a62c7f67ac52560c47e2a2edbe56f6fa0660d8652691841966f69d88856ac048b93d1538e7ec7012eaf9d3af9cb716a1c13c9e20caf1c152ca5465a1be6df36b7b05d738c009f4dd60d720526d772375b058457cee3825413b021663aeb17fcc26b7759bd0f5e1f1022d7623bc47ef50c86ea07c5453cc44fb9452deb8c11ee8c535b52901513b031101c28efa7dd0fe62b3d53bb53f5a6a8a5ed680413c2e722bea9e935c6919fdbda6d5ed4bb8f9b891f8f045b60e77ad31dce4fd096011ec46a95f1ac1b7ffcf39184ddd4eb7bd82e49af45c054f568bc5f22626c512e397ddc93e023e7379d357ceb73a6447e5bea952f206553409643bc7bf357ff60860de76e6716306127e580df9e25ce2524115b0681bb4344bbeb76f49f7591f842b1ed698e26b480a78f6f0c5eee593ff2c5a8114018755abcfc5bbd4f11b3bbc5590882f8cb82c1e00f614db9e6b7773ffe1cb84d0141a6fcb85cd7101dddf7c211de38c05756ebd0b378884e28de1ee3101cf398010e5242a1f2d58fc81bdb937e76c28689e0c743ed976bf23de71ecdd6f501adf74ba9f0e4f00849b98371b3e330e49506bc13a37c5520a5bd2c3b78c3c9ff1ece4dbb7c87c99747627de3941dd0703a1c4c19c42503ecf6edabe189d8f2ba864fa9f052585fbd82fb9d863217421de006d2200b4377880232a199ba26e7c39627cb2fd2d1e16c81f96a5b044878a8319ec215b2182df011612e3bd1e3283997fd82725181bad0e48d7867d3ebf3312aaffc0fc9b8d48f61afbb590b197aec69df6c0968b90ccd11e45a560ec4aec522bd5440cd43d3542b37aac71db94355e8cea6583edeb465684b99178d30e2de6de19a45224584c49cd531e2d81d3bb95fa97569d2a56c39e38c9e89e46ce130e6de1867ffb2e5803f9e8805734a705a699666bcc0ade326b6236cbbb056289950494bdd34f0d2f6ec26f88ad67fcfe028954557a8a939c42484bfde452b644e63fd2dd404e6e172088810431525f89f7026e8f6bea92a27af34032955d78060083fd40e44bdd507c011c4e68eecf9a13ff40031b102d60a7abee8bba18a57076e3a3c9b070b03a6ca59684ef810ade6638bde1bb863bedc0a1da6a8278429f41e138a6766ae122f2ff014864d3d6ca0e17293d5baf400209a633227e9e824737576cace05089c6daba68a0600d23d4d72150bcedd66a158e2265a85445830b5532a67e9f000436edf6ec90a8d2a3554a1426c38ffc750418dcdcb5db40c1241bab2a1181c2b5cc243f4740e146aba55d3856f2a44a050928486596fa3f0a0ed9b058b242e1a54eab6b910102148cc87f67d0d0ed55ab050444d2bcae8e142a58c948f07100176f5bdb5a8522250e2aead060c1744689df83c0c0369b9d351c5ae2b0b212092344638cbedfe67840e0551151c5ee111b76faca9262327e697e398f59fe54fe1c3e196f39bf24df2cef2cff0c9f14bf9ca7bcf0c709e9d4a03d30f960487ac69389d2e20bb9cb21c9f288f2346fc273096c34f946438d861c0d3f183e26cc20b47168e390034347c38f84180a371c6e18e260f8d8f0815046a18d430d861c0d7f53031fbb670c01afa0760822d2aae28824b125904b23b04874faa18979ddf2aa5a31b7b392c7c9f1d406517e9ed0ca538ed43bd82b53a4e23010c6a8ed15ac7b5abdd874b0f7fa61cf9a1d267dc9025c633c5bc5855a93cb13c5bfbed84e9eb51c0ebb85fcb41ba980c40457629d764d09904b9800ddd7060ec6456c11384b3c06f4d4da0a22b942d551fabc8ebb6e6d0e7accd4168b6449114bfcab358eecdb0c0ab0a07af0e4c840a755152d8af1cc9fa9fafbe5040d191a1d04ee13664c132867aa8f079898dcfdc1db1e0f60d73aa542b75e860d72dc2df1020cd8652a6ec310cb6f143ee63b848df5ab40b254ffad4f3f2a33bfe7d88a3251630d93be51cb333c6ddf6edcc6a862c3e450da8ac972a306e23b74e8275117fad9d89b00420337c66ad01104f15bbb5824a4c4c608089730b4b0c9228075365139fcd0b9bedb38704fe1c7c28c4e80c7965d3751e6746038fc9409cbf32ee2f0d3fc911bc6da33ed04c7155800a800929c3763ea3f2deed7fc97fdd3f61686b14126a995f074949e622156fca4c88333ae414d1fc8e6b0ef9b1cafa978458d0174dadb79805743ad61ae0c068c5d1e7ed40f1767232ed7067f004ea14575b20ca0f04898a73b789e1301394b4b8b91989f38d36f6bf33725890bae0ee3994de5f87c60b6b68fc5df2999055468af7e00003c1a99682e2bf05b6ebb6815f67f27ad51976159dadeb8de690cf2635a9f8ea67d5ce848369a1941e00e2f815c8bc6199effc3c70b000b79917fb1a6107ee7e2489cf11d8446fa1ce223eb29698b484d47468e3f4a828ef49aa9277dbde84b6f0fe436f6d95bffaf40d592a58887b1c746a660df8a694d0ac050a9d01e1b38cc73e80b7ddf7aceac7c0139de9883ffbd9ed2ed57bcd4b91e6f24b0aaf15a6dc124b271a91371c862575ab3fe10f4b963211fbe877e7f6f09ee3d72fc000ee43370d138c5291ed3cbe4c73a4c3acb9c75145916cc3fa9ae775443c177584949c0c7940090525dda2343e937ddda34467563d703d0e30d8ee3a0b80e8af320380c80d360dc0dc671a0b8078a7b001c0cc06d70dc06e274509c07c539001c06e16e304e03e33a50dc03e2a807556c9ee940dead9fbb2b051206fc950220a85b2067c91b5b9d3e5fd100384de83a9aab7509608f2a5b54479e5d0aa28d47495720adc03482d00844151c5da0b402290aa6100c45306ac1a805a4149032055cf4c2921dab2f2af9fb202f833649ceaa9749ff7cc89351331927b5c5e4bf3df264a899d079cd12a97f8fb655fd42c9ff4ff9996849e4a8ba90f4e7b3fcac9a9338525924f9eb915f56ed449dab2c90fcf4c88b5d6b52c7b58b257e3ce0c5d42ed969d565a9bf0f7860684aeeacbea8ecef87bc0c342438575f26fdf7515e468d091cd42d93fff6cc2ba306d20a26e0a4b298fcb7679e0c35933aaf5920f5ef313fa686099deb2e4bfcf2c41b5b534287b5cb657f1fe1c3a42dd1a1ea52b9ff0f79b36892e4a87291fcdfa7bc4cb51371525948faef395f46cd491dd72c90f8f5cc1f5b63a2ce5597b5b487f05f3957a2e2bc3a19115fb4094434904e5513d1fcc07e9720f58bce8c56f56f9a127a3291a103a03fa19da6a481d1b667639227cb94274dcc3f9200ff85a5f316e5e2547fc8d7a281cadb2300401732b8b42a4f28d77d30731258b5b647e1639700835fd4683b59d99a50a28e5a8506e2133753073b8654689ed0dddd208f95f159f51caf8abda5cbad49180528bb7ff87bbe4df882a563ce084310b3b3e8b85d559ae817785064dfb9bee6ccc6f8b3965aac4f4fc5ffacc6b04bf82d5949d24ddc836a211d4a8bc5d6aefb50f9e3c058a910a429a5a5ebee014e60014fd62ca0e52b2657c051464d83270715eff06f62ed35be36f0d5b534eea3217368f4729b88ab505f70d821e26754b37f5e46241b30bceed67a401bacd48bbb2eab814d5601040e0123b3b23009e84f17a6931a59688cc6b20992d08210e88561646c34968f540c471894321444c60e53ff66f4caee184ed262684ae6ecb0bb45065b056c77c16eba925a13b11b582562521fbf94873d619eeab70531e24714acbe94356bb91915c0fac77c542a93b488724726e8d6fd9e1a49216a7631e54a89d4da83b200cc283bd4d5731180441606e3d7cc53ed285a6580eb22dc7c814bb801b0fd45c3a95f1d881aeb23c85cbb818bdc1267304b300fa1931a04c97265ced83100e11f14e58db4695f7c1c8f9154906181106d9835d501370c59e9948660ad847152050f176cf790761b770943a707586b658461134187b090bc5d141d0b490e01861f715c3c5723190abfe31685d8a2f60e098b82359419927ef08cfa9f1257c6a0c43e8a1e375b7a460c2084c5c8c69dc9315dc12a80a46e5eafe083b67a826015c0ad9d26a66fbdbc2035ae9cfb0c37387c1bf329c9827c7fe557029b6add91d76d70298e123ac84fcb22ea6ba6504c237bb872d875024238bcaffdc7da628e79d15d5b93620315093de0b9da45ab349c77125e0025d3e14192ffdddbd78c992ea16c449f177c70db36dc6ba90540e44ad7eaec08310b4eed30a3df91ac1d72625c5f80f1e1c68b700c63bc1472d7fac01e7b1e1e1d5cab15ea14c89bb6cd3f14a7e2074df41270dad480aecb3004e0041681fd598297d441085ee3104b634c22c2b123744afdcc0a9ad4e782f5e7edf042f138c6300dbcac674bcb1c1aea0445cdeaf7af129f5d9a11807c3999068be878336933957d889cd239c8a5d84869ac317676cb639f552d1ce6872a78a7c307e1282e4992e07d906961041e389fb11d767dc59235451f269a33781a175ca40d697112c4019f772636dbc157e396be82e5d36fd8ce34036fc3d0e34f1824c5206f6c0f7ad4623cff513d1ae8b997598678ee300ce48f30125de34e5ec7ffe71ad0a71b18c0b9a297fe0db5c3cd66c66b940823ca88e66194138a1c93d979f5c5497187c71a826d3789886a1e6723ba6f82a1c11cef5893ca3373868fe4625ca454c482d179f2d961b4724a0d372e84e8e47f24c2c08c32569dc1d69dc6e9f000d4939d93d25b814ed3b709af4f6e2132bbf7ee55157c27c9fe51f9bc8efea51a8bb93aec27ab903a9c277db20b8dde60a96d149e539fade01961f450c9521889251468649401d99e3538308dcd721a4092ff426d1dc4e52de7a05a902bb29c8e6cb4c6b400021a4be749c5117954894d4bc38d041c2e025eb89df769703c125d2fb449d3b9f825e20e0aa4cf309d77cbdef8d99d7d19fc8be3c44b942beab9e2dacc55fe5dcaa3cbe4402a6a29edeb54433a59c542172be559018c2b2cb972441f3078cf0f478c4b3ea98573cc54b9d39c7a69c7eb702ecd5ba52deaa59da39c4e6db914b92693c743facf9cab8481815aac13f7d9bd8cd655aaccf55654a1fa188a5167a1dd5c7da4214970dd09aeb67dbd7e6de7b9cc72a491c6d0e443f655c03fb3c9d27585162f326ca2bea75cba0676aaa1f7ec5661c3f898862cba4d0d6390992b98d475930ad4219128f516753fdec5e9f9a3261f88c3d61bf3ad11795fa5fd8d13bbed798f3ae5c2ab572aade07c861c7199fe5de3e246d6bcd7685c0c16cbee624bf8358efe934a0443d68be4c9be174e730b76c0261e92439a23dd8562f1e627c3a72067ace8fcc744fa54ffcf29a98ac765225a52e5c64f165bd3171e2256103ea07815e661cafc66621aafb037eb88be171d14db7b3fbf322f2ee8ccfc69e40cbb637fa6716fa5601922da82231017411fd955f291ff5b60f9e6aca056464fbc8ec546f582eca333af1de4f8be44004f5f49e37d8629824a304d5736822ee25d045c4a955c42cc154e267ae73830f73fb4400ca8662a96ccac90561a3651a6c19723c0b279ec9a6b10c600ffcf6f08802846d2358ec19624f369fe6c05a4f5c5fc70f1737911e444edfa7d1b5c79630f852ee8593381c1572ce2a01bfcca1b7d0d2760d39e25463f917a216217f95b7480f8c35d1ea78fcf2399c6e8cc74486bd29a4ec7aab838a5ddd2da8336f85dcdca54ef3e71ad737f8dfb02eb7cd92a0bd07e861bf766d8a1f898cfba50b5f9da3215224b78b2073b94e35e81557b6a420bec86ac754f18d25a279c6753a20e9cd8619cec2ed7c5e40f3fd0d91deb6d5b695c30839cef5afe5694ee893bd8c98ee16837e97ff8619d76678fae59038a343171f6e48881a14ded0777b8f31d467ddf88c7396e3c28b5a0c0b1130bdd357d4f57543cd76bce087367ddb90aac059bb9f7ad5b7b7864e05dcc0912e5e001fbb32cdae097e6f7dcb8f8ee8abc40d111d336e43d78600ceb1654c6ee7a2cf58a117111ee599ca301f8e51d390694c6852b1258436e3b04ae4dc61b19948414e7769919d74d76466c0c5f085272b4aa891b5615a2158fe04e2c0bcf9947c9d22d1e7c08a194ed6516285f5b22743f1b3219755b0ef53d2de56c62207b9865319c97aa71205eb1a538f1dcc31bf7d1acd549d4316199187d44f553d8f4ca4b7b37148ab57383e182e2fb1df4660fc5acf0218b81df436baee404be7a864e0456bec5e71e16de4a6da1e46a3db9692ae2cf9b3eef3be15c99c5d270273c2822b3fa32a769a55b1bcfe708df6e2bbf6b0da0711953c2b8f0c994d21a2b6a0141ecbdb3461eed3f8ec0d40fc4cd5f41728ed510676822062f80bd7b6b3303fc0b6abd4c540ebe99cbf3cae1c2406285d4d2d8e196fe5e32471e96ad92874b4617179ffb70175fc3faab89ba677b890e7b54eecd7b940de69829815257d0d18828705ecee8d1e1ada10e3c4ca96662d1987e879c69c5fa8ea3006da4a3acc9ad13b02cf6c9d79cb094ebcfd47826f4afcdbc443e6cba6e952acd52dcb85b5600fed6f06139ad32680b4552d272c4125d6f87e8d6a10aa2aeaaf64b83f21fbd4bf11b7135ae9e7160db71e7c092d171641ddd2210aeb44428167e1b1f474942481d646991391424788d606f8a12e0544bb11edf5e53413b556bda8acf173b43bf0a80d59a65678c5435a356746fe56990bdab94a57142e3d472623cfc1d71cbf21da931ac49dc89c53c667928155a945770d529bf1335a7869bc80588461ff00e7a4b7be008851b841f29c2050a0aed7bf5d9c8fafcc42baa72df290ad1460150a57c55699ea612977c7200c375657df1acdef3a79437858f541ac3d2dc8702ede5f19fab9913ab0983f77e0f4c5c0a7842fe316c6094f303cc84707f018ddb5fab080a201762fe80d824b7be5e314a8c0d071e20ea23f470162765fb29875637c24725d919eb3c9748025432e9d100f2ff89c44757c2511ec3fa40c4f2fd3f78bb6640696eddff515e7e79e9d2d2877505dc676e6d8f20375f7c126739b5d634082f53d68a29b1929f526bf701604dca66981f86a23af3e80e264f2750cb38731807453791178c54456fca431418662feddc54784cc29a18e5942485f609b4250c7a439553b91b4ac79397712b8c711e8dce38a6243495c741de7f4ee93b3022dd89e03c0ea25463854c1c6c11c6be59a07833e5b21d671838420c4a23d66dec9149024d0425e86718e431fa799261669ae56685a03c2b8c21199162637f1effbd81bc538df7cb13e204bd2470b042f2371b542231ff1db1437efbb1a7fa53dcb25a1213c96ce28db42568cca10984c75a1a8a2c27176572da05cf340fff011c729c307ce33bfdbde6d7b315393024c2282bc5ddfc46814a12d41760ec5f96df39c397d54adac182132eb666aa8766ae9f5f680128105f99410ec7f50859c95f61c5269a63cde2bdd02361958a9f1ba3bbdbe5197113579560252aff0a57119a638af448cdc2ea2fb021e9dd586c6b519b73d974044fc3d1619f30f3e28219e3efa78f54990ab470b95bb035c8e01a31421766157e5544f5c5d615890fa801adda41379d19b1c5a3cfcbe92128644abd19ba41779d93590e6492c8e906e7454805da0cd2679ebeaff1f941a278e3e34cd2cb76918dd238faf48b943d4eec898381394a41bc3328518124d8b3a954089d52c9088ae045065fd54058d9bb60516b11a76bc3c24389a0901f80747210278c631e381384edd8308b1c7f866b30852a49bcb74411cf8fb2c1901729206b55b077c3ea573a6ff8d26e6c808942ccb88c0222670bf09b3df601cd76e344a20d53feb9f016cd34d9394412dabbd1bd7fecc750dc6aef2a40230633318f76019b0e4c89b19a17d3c63b6c73b3ef5532f67942bb4a7f4a16c16a0d60c412b311501d0d51f3a5a0aa8d692539b87eceaded56ac132e2acf2d9738ec4944a45a44c40a1eabf38f1149be81277190664186be65eb12f61b027c3d3a0c842819830d212ab7635d58c976c14784e414aae792c9d283302a792809b2c02e5db13afd385c2e6625d9f79c07d58f6093bb4653d3d0c172a51c94a49c0756a9b7f8533499bbf3384a925b153d06169c6e02367f8d4ae0facbc659ffd89c3121efbee219445f61dd6eb9a057e199a973627288034013206dafa58b194eaf76090d801219e77cd01df6a1504d5f57ea58d351fdc20be8a9ad5ed4f2ec78320c47a754ee8b0ec4617a3935ac5139d188a575c3fc01f52334350e14d65891dfdd2615658da107bb3b5ef1bd177828c96c3d9df2cafd10487eca304507ef4ef022b157d4e2830eaab088f6e5151082f7f43b0dd0a983acd8d50862058db0c3fcc3fd5863bd0c7578144eb4e42431c2c4d6f435ce6f795adf34e919c367bae02b5241a32e451c8db62e7a1bf275780b85e7e0b8996a0127d920398a6ba60531d7ce9cab62e3d12c634f9cfb015cf027364827718d40891abec56e83e4ab53bcdd1da577583d8b2ead4380bba0ac41e8b817b310254a56957e14255320257693c97fa87266b1aaca70d561cb397bfda00dee61f0488c7e01295375aec4fa7483532aef4c6d1750f8b9e2f56d1816fb3aa83ec428485c02f2170249b52d0c24bd5854c8e3a46a92ba5410b709952e55c3673f58c7d166d5bc0bd547b7a17347ae9cae38e121b5c4f182a955e286be5ffec09f1032a063c7d48784518df844fbf46cda52aa51b1591514f4ebc0e1260219c372d5cde8c922ef99c5e5f5517c62d73d0591626287b34cd1ccaa6d35159c0ffa71b16e8eb13bc702168e33ba90185c946dde7a7a08c46f79d731d7a6a0ed0c1201094fba0f53faa6155be8053aa1b1a7dd85cf350332a20084b86c4482af597e17e3b3920fa2cce942185c9721636dec466f6f93660b7464c04f861d9aeea848617792ccaa7c5d520381bb1d360d38b2794b1ec9d9aaa44094ca90593e9ef3d7f15a0601477cb852a8294bcd4545727e98fa5d138ecf05f86c6f8723e886985c09fa9d6491975fa00577529f7b638f6d086ad051e3d952f215771cb4abab01758fa1711ee5313270165c8bcfdeb63c8317068d9268b0f7f6f22108a16bf38f02697b873309489b038c6c40280bd28bbaca0486a5adfc90dd0017ae736ab3bb0bdd306b8003c5d413fa2809168bc451b6b61c310af53f64b4d784bb43c532ee440d5409dd25c0303b4402ed7b67b94231a0a1d86afbf5398a48a2c024ce33238e93e27673f3526e87da6bcf9a16e991ad0d13c81611f4d5650d77366a17de0462de76d02b82db9357e23171c269c65dc92b215864b5b4e91ae6dc23e0bc3643a90103b06dc0c450083bf1767b2875fa0c7c3e264b91e3a5c0bd58727282493c2f87f4af126172e39992b8c331ed13e5048e8fb5eaa3efb74032fdd44e007273274d3662ab306bf8bebdff987815ab0156a6b8ffa0da1c7b79b86ab5192fd30972b1c2032eb2db38e0809c44c76538fe910e1cd1a4e785ac2f5150a0752d584b2bc658c8a01b524d339a4bb970bb5b6ed43e82e986a1fc048f9e2a491cd9bf2c78b08ef486aa02e3917217c8b671c7b45f8c8ba700299ed127c3d0fd3f12e8bffb7c03647876fa96552241c9eed3782df6315a43252a4508f12aa200cc080c7e3bb8ee8b8810b8def132f395fecee287250f6e7bef415ff10119afcbfa5ecd1be4add1ab234fa30b8fe5350c13d8c17908e6728bae1ba4fc1e1c072b8c94f42faf58efbbee59de4348271fac73e1dfe7d33ee46b7558f5c4d20bdfbcee3e82cb9f1618256d501a2e0e9a81b8d8f0d23c04dd23fb6990c884b9e026590f985b7b1d31d6cad366394c811a88fc52544147dafcf8a57bc068a26949fc0c26256b9fbc7c3f503fb9630a2dad9f2802495eb3b898cdc6aaf95c1c70bf3630edd9352a5faafdd8b59c78aa4de6a33af874ed960854ddad1495940564679810673f13cae7f9cf014664823e5d40ffd8fd6cf0f053f6d4fdb8a938c9b6784c72659ae660422036e822910a608c50087de16aa0243f3dc44511681a42bb82d9d608ec7b59f56da555af749f0f1a524d28e4610ecc15817b9f4da8370a5f98e3ae3911b0fb12bb3fb1ceb70323214537a74f0516aa8726468636e5302645dbec7b1fbec1f0006efc71af65f4c54d84bcf7b33f71ce6e233ee13c743f169279595000270ca94757f4f6a3f2f23f636b40bd317fecec2243fbad6db8ffb3dbf1a50eaba69872cfe5c2e5eeb6c29d2637faaf37aed8639fd19f8bdc51f9b1341b98ea34baeab3ea9689dbb02572dcfefea6efe9eba3ef0eee9d9c569472deca19cd46b1040753a4967e43b4e22243ace19a2c03e5d295f28df8435253d1f85abfd4dee5736a31ab10e155d39544596d82845fdf3aff5fd01e9a4ab2d3409ee730406192df3f389f53caed958614f1bc1e69361012689030b0ee149b52d508ffd7c21905d1a9050709f6efb9c53884318428c4858d1e84bfd64d10f1d6a601f320ef75dc27ef429e329a90884e58fba1f772fc0f5dd7c210f3bbdc3ef860851841fb59f2a26d249150129dffdcc5ba1fefeb7fa19dbc64b08bd8e4410b456ddc15b59862bfec4086dcafdf66f97f538585d9ade8b4735f8d084c9407525491555cc01a798c5cfd8d6915b653ba1421e095f9f36d36d9d54314e842dfd3e4845af1670a8b444320281f2100b47dcaa3cd6e88f414716585a23bcabe91afdc83aa9fbb4d781f7cf4a93931f424cf7ad41d3f7e9f84dc064ce7c3166bee18272e8d234196161b5f3700aa67c2cfc2473bf94adbbaca9a9a5e6d2768fa521fb6359db929bbe74eff0906b27e5654c0aa82ce381dfde34df49cb993be982fd633df33106db0ea409144e9d8af5099a657d6b3e7048111ef0a8856b7028255e375a118f55aa4c75ac2b912aa09fc79e8ffc4b9ec686b2b7c5022ba457fb392bab81e9ddf9cf7919a07dbda1f41c7ee671f4473deafdcfb01f24ec3d74992d38883508a31784d8eb87d995275d791bf8975626935584fb06f55c3fee2769115e970228f6b7f4d1c2c6428dcdee9083a82026346f49d6f21927895cdea9b7d85d8e79b9e044d38d72e1c925f6e1fbd26958299c4a6c2c84205cbd03a12a57526383052cc0c24c90f62dbacbcad10c44de686572c99702552f8b1b37a9bee72d7f2e7e20aecdd73046c95c818e66b66e6142a8523679822434c4221076fb208746202dc2f8abe131d096881c366668de033d150164ad7ba27ba1a3ae5eaaff0b994323413ab7198536fcbedf2ff627a7cac37bc1dbf624eae3f8b7e8a8805fd163144ff5b9356c87e379eec00102d365a4fe0fbfa650f0708340a39020d484505342dddb0f04658d637f106642c9b55fc6f26933468a51049b8ad3e59b05efe56c047357d553e9f428c4b44dac044f4485e5c85f42a394f96532c795071021e297290f51724e578b741882ca0d4832bbb48dc05ff6f542601d9414462538b97e3df592c901a4e754b7e4a4a76ffce0dfefcff0d54264302a83fff43d7ebade7d0930f5fb038945503c360d5bc206ff5ee18369bc7aed8c57d61fd12f04fe29c8a97c14bf6b434223105a7694cc71d795c315f3e37c936a20cfcc42600d05e0d0b936a2059ad9eab76815c6a938bba286fad6544e463b660065cf476d600ff0fe641cf3e1ecab36ab16e30892ee8ad8c18a724b8759a7b718ce9ae94728b2dbdad7198cc3ad36fbd4eeb8a1855e14d989888b6d65b34be223d6a152d3757723621886ba44e380f2ed0082086126310996e82ec742d3d52045bea78dbd8dc37c5303e583f18db09f8e075586237f08860662393fadc0fbc06afc49a57ad50a7fe9e7e9163566ff93850b988f75a1294bc1ac150be1f449883bc9590124193dd51410463aa68afe6c1b29f4509c660f86e75dffdaacb14f93188543f695f0ac49d11380853c84e2edf88bd83ed99dbf9012f91cc251a6856a240eabc5755dedbb71e30b8330b80b41a75f5c40bbf3929b7e99a8ed341cbe01bc1f824b8c0701e3c00795581cfc9bfd4b5f7de1ce533f9e60106009a1748f5e1798be5b3184dfd696dbd4249aa32e6846cb5817da8280d221e03288b93eadeafd768f4b0d2055ef2a32369e6f71eff7d8db951a88c2c2cec294842de1a62ac0a7c47ecc6f3c1f9eb1337935dab2fbb8e63637cfe067084ef586d862e2e9a48d1875f9b2df6bafa846d10810d6ed6422d8e3f3115f8168597f6f0d7d87bdbc378da3db6a32d2d162502a0f9eb7940ddcf532ab6182f57b7bb62317ec7cdb6ec463d6c802747482951a55bb7842247aa2e847af215ea81e93afb1095cd315b839e4073a97f717f9cf251374fc192ac190390c66451f1769f2b895a63fe6092ef8f8d9ec09a793857ac85684e0df92bfcb6a8fe11b69577b23b0addf2a6cb3806c7c643397de7228e16ff1b4fd8f3722d5450027b652fc7c48804c71ed239b773c48b72594db00576172d345b50f287abc1284ab52c28b818b1906429594439344c6a4e5585a229b40c51565751d52e659851fa1ab908d237e649808be24ea99b0405e8ababe1ca765307343531db4187335b096c689d668d582edac7047878825840ed90a00f8ef6681dd759f34462992265498c09e99f4f779829895e02041b45d43b525bcafaf8e600b33fa5b4f8012059284992b1bc86bbbea350597a498abdf3effd8d31ec6852fed4bde085665bef81ba98b8c9777450c473bc033efc8b7ce275868d1ab9517abbd105b9fc2504db4476eb44203a62aa451ff06f8c38697b7aebc416cf000bf6c17772f27122ab280afed2a2a81705c7fa12754d087973f7011c960e17fa3cf4df062a10882a923b035edc3893ba9d2a77126bf1ceab18b383c53e3f0f81b0caca25b0ec797312a8d57c1e02c096979b40d9e7e625a06c2b6884f990776698c2a391b8cdc50feb8773050fe4c8112f919d25b08647e972010000d2fe751f230b254e1c2e7162f552154ee96b0376523227ae1e8f3602405e3d0d0769043b35c76ec6ca95d9603ba2f8214038b98df3d3e65502f5f3105616d003b5d22e35a91563fd0b2630fbe67a14cc1c130240859c88d04e194c0db43c3cf40cb408ac76ee7e8a761c38c3e90272c712d1254a4f63d9dbb663c7e4b6b7c6ddcb6f9b45779f73fb6b748f1469e55c5970d2fe586e601b75ade37bebdae9fcde5cff7ed381d00f5c40d1876f5b79051c8c040aca7324ca7f83646348f48c1467e15e19cc18e4470b2d4c73acf9d72df9e3c8a329b951ae0da3d18da13229b40197f94e5aac6a58577c7be682b66955bec4f2a0c8aafa07ddf5efcdaec4f35b793f17395d791780630cbf6fc5fa5f1e5d15bc7baacac304111f49b7140c2733d98166e027945feae464ab690e085f60a729981d8975e68c2cdd5fec1ce27e964d8f7b5d8a0a9c23eea98c323b36287297a4bf55a67a6133a7ca4dfdb90d42c8b54f4a11f9e76e4ab3858a3828a9bddcdaa6552b7b64c2571f0464898c05b6f6a6b747b4b5d30c156e5abf1f956f6e26914dd8bb0f42cd8eee6e9c5f2529d9e66e27a61d76e4e0ed4a0196af29ba99ea099453bc7f5529d9676ed2d80f9f270feffd5505c99fbb99627f78de6cf8f75728257fd66d9ab2878c7c52597a1a983d52152027aabe702fde2ee6f65f1539d9e5b7a5da9dabb705fe7c1489e497bf9568f7bc9e1d78f3a12493bffc4652fb734d6fd84d8111480a44b634bf8ba2c214061c08c5c742db378beef95f8636321fff326c5b249c0efa1d682aff0dd9557bfac0739484e920afce4f1c01147ed3661e81413e6fc4c584a48ac7a54e9d3986cc87a81ae2edde442ea4632ce4d0a6929f5856a6af0f56eddbcd9ae7b8a668129fcd2d23a8c9bb8da47d4d16ae987099d1db582c719f7b87d02c20cce0ee19113ca715f61e08b0510174da6ff7d18f7956db67fb09d7987d98aad837189535117a8637c0f15d67009477cb2d6ae2c22e01c5d8d892456885dd30340a6e327eb032138d28c4fe50594e6c164419638a1d74c7afd4132b53b7772d8aeea61ce16159c97867544ace78921f2c1b4168eaf6506cd9514622a98eed4cd1ed3ad55269e2382448742a1c7689484db4c011bafad42dc73d5d64f93b03097a0f9ae5886745283616a789d2f1e5e0d88852acdfbb920581066f3d28336ba02dd0887a79f7ef3b2b055f9bada3d5541f4fc7cb708a3a03c112c19c3d868c525e4f9063946c7740195b7ca0a9e2ae213f4d720ea413b9a2e3abaae397ab33b06eaace27b8c3dadd840cbf5ba6d0dd6f19d8038ca627b818e3fb9d4951c7da80ed3bcfbc046910776a87389f367dfcf3b24c0c1312f8973796161ee298349cd2b5175cbab35f89372a179fcf2ddb5b798ec6f9f4f6f389c69bfe99a1394020321916afa277a4bbdf2486bd5f17d1babf8ebd59f4f2f8bb3bb7719ea973d7edf2d2871e6c796df39fa54cb17aa50e6a6872d4eb9eb8e8e8be4a6a24cb590ab1c5e517c7b18625a022387c174d22b6618d2f582ba289852224c9fc69cc3207ea084305cf5fc1301672e2a809331fa7ffefa04523a67b6fc43bd5b5319c4ec73d10cf44d74efacaf7939a755aa0a683a1aa5db6388174242194fb1bbc69602a23241a81cfe574e5245c556dbda03eac64ece5a5b19461225e3006aed1ea401a5137c07a50dde03604889e7880917cd7da84df57e9e70b41bb5e2007180489fa8bc11e4e8b5a407ed72ae35b15c0df7a0d3ae4a532504696e61ba6f9bda0166b2b8d7fa49ffdde1eb2414418e1df07ccba396dca45d2cbdcfd72ad66c54a74f9c87d2d8fa006e1ced177a8645d7b171f1ca95789a8413de4536d70eaa16e1c24cb1066947de1f78a3bc61c60d43a7a259c3fac2c88ab1acd05b89d7fca604c872103ecd34120ade48c48ec79b0f934c07e47b167163ea3fd0e07587cac6b01923b6ab899ad380eef4221f5b25d736e590f5e6754b8b526b3f3ef2b0f3607c3ff3e8ce465b32826e98132d27039d605b9dd9879f12692667763020c2f10976797c03cd1d17181f0966b1c28e3ecc02f21e5ed3c5e06af70544eb89ad87c4528a41142a17f64ebf53c37b596fe249c24c05a82089e627cfd0833878b6f0e42fa30154bad0e8e60d86d917d300a5813967eede2371672ac81cecfedb7fb665c905b4b82749747712bae563e28afd0464039f17d232d581f42e2d11d6fbb86ecd97e88c260eed466455c4509de45dff9b147036af7b13f2224bc02d95d399279ac5e4be329a76bbef6cacf5135988aad925d772a87af5a5edb0b4afd1250ad4a816ddfb33502d620cdf8af3cd12a99bac594440b70b449a5b9cc554a2d346a41f6a66573fddd506da59e98bcda9f3fdfa4f2e0f296c427f320db8f664ea695a14c39e5fc6ac557e06d6082f62a185e6b46028ca4cd3f5bfead0113ad9f5c5481ded52605c2253d698e7ea006260b63377ee59c46c62e8f88c628c09bbe306fe2d2976946b51eb0c6ff710e08db721f1385101ac163a52149970892eea45666c2b78e941ee62e6ec7d9a66828620fe053b607fb5bb1634a5f4a4890028f14b8b4a835ab988ccf2159ce73eea956810dcc221f6c88cc43f70a8724c6c748123940cc95ab39ad8d70e47aed04d303f3aa07d87e83528fa008618157ef03ed4dda648927cadc3ffcc0407f0b8d66c80a6b158115659586a5ecd8b68814d246a9fc4674578da4299a27eb1618fdb5bd4fa8d188b84116d4e4df7fad24b449f54d696d5344f4a3230b29e5db91af1d6f2c6249146b1f09444db30add7f8e88ff56644e834ca313fb7cb25e394af3bce32102f9b362d73aaa16b9cde0d4357fb3e5161b0b4ddb7c351321cd904626be85561da98739397c20339ec68289b5e58b83e0467a0ce8274e6832eb6e43650224bd2974c867ffcdad9440dca8c9fddbdc8b6fa701206ae776e55908691fd9a2842dd136482f139e1afbb4272de27e0b43df896a91e0fe088a4b2c2bf2d33f11c30c68a19e196cd3ca97331f742249d85e8a35c6fd685f28fa3e66afd842b8e28865035e071ccb12f62dc7100ef6952c032e1178f8db0958fd2b20fdce1b5ad8c04d65410646bef32a6b28709b5d8166e302a6e8e97742e158b353e26b0e9adbd6c951dcb4fb6155f24c93f5eccc17bf9f20b8751ada81f2a1d1d2489ecc3df5e8bb7c288fe91be75c22d6a4b3a33e58de84f6bc2b01a637ce05cab51484ed0a166bb966cc23ee56431200c1366c6f18f4fe64a382aeb357c7c9647c9340881f6ad95b6fb66b2d3e33e6aa1cb041ae66024b0e03ddb2fb17ee287be38977d06c267ec9025bdfcf9f347bb9f1490f2e492c788910cbf486a1b5e87588f4b4d6c31d4f140be49b8d0c689adb3adee6a82e89e9ef1b96b3f00a4b3a81853186140aa4cc599f61882a55ab5cb8a818acdb15a6e6037f85eb0e411a85ab26da0f994c16cacddbd562dd5d9c11b64e4d297bc70cf39c32c39e5b514f30442637573a2d5d5bc45367c74168e68a45e830e72b165b66288af8e19d8d2465a255c7d966a3ae693f92f20269ca9588f4ee5130c91f8cdb4820d022723fdf40b916b2d1c06cb2c9e5708e35c726644dfa2b90fe627befbb83b30a5a59cb119d0f6c663d7b69dd5b4bc410ce5050685387610f70f983f45e5d798b0bc6845c6998d7888412bf77967103b6e399099a41feb3166a887ae81b1b4e649341c9c9e51e6d6108f3bf4ad754598ff0821673ab777f87f861a09fa27096be0ee0b098377719fc331bce8bbfe02ce9e2b24f767a56402ab456f37e0eb21200575135286288306fc9c9dd4a10ef4572a7341cbeabf61d6d5ddaad70eaee1a23ddab6ef09b9c4d1d61807439282c37c798894d692030d16e8e98b9e18e1e7a28934b626b4d105e22494ce31642600badea37e18b34866762aba2c8f9dc4fe641e99c38045b426d6e40069e08378cdeb32d0abdaebe8851edf018661d4da4f875ba70ca42fe75b48cb0edb58e8952f04d181e5ab76413902e26fd1a0e93382b2e87da7a5aa1402b94ff02cc9d2a13d9b56b65feca5f5089f74598e42a5c91869c25c432482fa14f218b60dabb5f6bdfdec9cd2df10ad2d750570728fb69e244ef6734612630215ea29ae0c0c02fade927e1fd177ef2892b95fe9392f13550ffbe0900c9a6f7603e2cb13a77f70ff1fed2b4f4efc2396cd00dcc344809d8ac9f9b0517ef6b9839430a1ee307687a72e588fe8a926da4aff893636ca2f8ce337c7d8e30e7650868e440edf1e1f1a0efa9810626673372228e1350ab8179ea03b9dfa94e9995b3a3b6121418b48af77a4a2c087fdb014601fe736ae80cc26181978f466b221967a2ef7351d60c7878c2353c8141a334c6ce5521b8efc5a15a0b31e6152ebffb9dd5c3ccd35a5cc4d39979a1db6c296a171b5b4e62a97000ddc05b6f0a9e93590eaf8d09468900a2ba254a9dc21c8579009c427b485e96406b5ec6fe0d6ddfb165eb5714aa64036b1d99db667b8e66cc196c64827d6d967c693c8bbc9015506b93b05b317b76ec0eed64889fe618dff9aecbda004915fe88aaea7d9d06b4332245e660571f4fafe4da9e84d32261fb3216bf3c2355cfb18396844186eb8f55827beec0530812aed470a68ab3fb9442c9de9ef26b3a405bca2ad5e97e4c352b7c45cb32e890c498bf870804eb68013a0b3f8ee810182e4aad47d1d65213d3ce905b966726392ce69cf3955198bada521cdaebd77d538b3659c6ea428ad96d7918ee5b3d5d50289add16e33378f28653fda087a527ea08f33e8a1b7241a3e6770f32f0d9b78acab098f8eb1529553e88daa6d68619337a1651ec27659ee9683f36db19086fa73be70e04548dc10235a9a29c18c0fa48d6c04dc134dede492089619a1824587f1a9526397483f13dabae8f2b2172496b66b8c5d860d940216313742f3323a9e3c2b03e59dc7ececc679c49407b056d6c3806b9c8d0123913b6510314e2de36953533ae63e76a5d125dea4da9c5ea42e9f3a9e25599d958b216c2e035c13f04b2615aec119dcc812a6b17b1ee4a09340acb1846521fd056a366c9c7479b49c20731f886a56c21e0796d5ee19c0b6fa4547fd89d653c56e0cb5f3847c15b29eb1b9863982d94f20be6507823e57ac3e92ce3b10a5f7ec1398200a1fb9555ac267625f9868d5301a25969ae2a8d7bc0d19fe4fd8730eb7923e30c06e8fdacf345d2bfda7adfa2cc5652b8abd4ebef29f3190c3dc36f85248876fa2a3387d544ff00f64df30534458aa0381028baed1fda57a5b329ca2a628a30e834c6f7e09af9178fa2eaf2fc49348dceed4f9417717142be3019b94ea5a9feb4b2fd082e1b099ff8350ccbb653b8370b4f9ea4ee77cc01a4c9f4894453645944cd7019e4a09f647024e843e4a0a06f1b639470944c3e9b5f40e28d49858f86dbebc5db762bea9cddafd721e09b8762adbc1eb3f8c0a026636e1a943874b406804ade017f5fdf223a380321c5c98618afb3364209ac28d130f3471e4628473d2a3b23d6329bb11e2da10068d1d6b8374bb4f740919bd7f8cfd9a5de82236dd38332acc8e4440060f8be477c7209a745a4665b8bef4d7629a18813d7bd24ccbfdfed9021c0227a7fd1ac755f644eb46bc9d18c8ee4da56d2abe4595594fa68b54c09502fd59694cbae8dd33929c59fdf546020954bc63472b26f28b3e321f306c91256be8c02a7cdc99e6b43d350fba9d24df29fc8a93030f93776590a357e49ae0d03c40b5b300e499e90a2022cb8743b9e1af3834b633c8a8a6320f34c5d9ca0f298c6a17738ced89c2b4a1f826a3aaa76fa93303a979e3a04250ae7b211cebd2d262fb4533c9796e56d465e1a4f4db56c5027ecfec255186b21e31b5991f615164b2682d3706374d6caa680423159fea37b475770ec849f0534c9a516922989aeb2e61bd965ded4210a2b28e7fb21effe49ce119ac426e181f848bdf3fe6435bcdc9e114b5a6b0576adc87231ba064798107f311f292bde1e62a0f6f79c33bc7f61820d90d4ad01180a4a7096845f8ac9c2dd09c941843688cf3168216ea6e8bd5203853f6f6b5ab978950258a90b2bcb637ec844f9a96e8a0dc4ef08e4327e8e4a2ad493e1560b2da6645792b08d316c40bcc6c2f442727679eefd8fad4c19adb18e07f8bd74537f06eb9530330c0b992a91e0d95d6e62b3acb7be3bfa50ea9d83ad1d00a0b594a813ff505bd01f55381d386c0978bc589cb4028ecd7519d91144ca24b8b7e699c1975c5d5193d697d0240a382051943c2444c1a58f5867f364cb8927cb55b475c0b445f5a91821b175456bad3a4617d4e09ef4ef756179c06611eb4a49cdc0be82efeaaf5cdac56d3fa1507e285155d40212bb8af207b8bfde03c4956795f187a121cef8ae1750865d4e1914bca7710927441933b80c68ed16cd388ec6c8a132573a1e7abba1ad17e9f8e0b9a93c9d0db11e406c7983c42e394f500bf20bed9cfd781c9d57c2c3798e3b52722cbf8e603cd82c3b7692b38a02f42ad838a2ff4930ca63b8fe6cf37a56d9a48cf5878d23ef04178bab4855283bde449035a147bb4d108017f1aa777a72272aa7c389fc5a8db2170fd820991d8c5070834a17edf818828da398614b6aafc9629e4bab6c1547801a826dccdb79f78ccb38b8eec2872c5bc7d6a605831bb2c67a8c8052a65a19eb049a63606a0c1c2b67d82685dbfa6a9b3ab76dacf611c516c62fcfc8de84825393a6fb9d42ee38d3fc3c53da8557769b5a98c6528c5a50146c61927cea5089e9c4d596b529e74ddd3d9d13cee41bb744db68b753894486c6e897c2f5392e1baf303290dd72a530b8192e81c25c66cce8dbb0ba02196aa855bab08ab92774f8cd8262c3ef04f0658f30bf56142999d0f75997a18ace4045b5d1da4716ec64a3b3725e6f252df4134b2e405b41a06d669ed010ad204fb250861bddffe08f5486b81a01d95c5e5dbb01022cb05094c981e73a5c9cb5e9e5d03df8d04cb8129731b93d2f8f9916c2303010f75a17ba8f54f05a5508380159ff21084cda8d5a55e31ea9784b666c4306e82a2115d19f9ed4979b3774acdabfe5bfaff6874fb5c24e1053b42fd5892a9bffe38901c8ba110319cfeb39a03f769b67aca52710c7e88fd75738e1aaf37ed04b2b295326cadaaa6dd7346e530ef88484b1bfa769f50f672b3b82feb7d11e946dbe8c050cecb6ae9a441109ea7a17d7ca98694e4dd149d1e292123e28b31055f320b0f4dfee8d3e6e2520ff74245e1bfe344a8572c5b441172adc12a8f33b393bbeed8c0f7e2624b7bcb57b7da8a1f53fa5c8b599b7778bd85cb1e8b90cd987dea1170cc900c5e80db6450acc99fa4c8dabb159ceb1140a9d2aac75468400494e733781294e93b38273324cd3b9260fc7053b0ba0c69d78bcd230467a9a4ddc5d8c06fba4da7e2e4983aa63498dd69c04a5feae7a93f49c8d25bf83205a2cc0f6fd176d32656387f33c83ce4eb96275f532358b9e45bf6a2acfedf0147f17a310f8549884c3bfd80eb110ec8ce29cf41272220fe960ab2c59964e20a9d4d2186b84b560c40167412c0758c51503fd0ba4d3ba864308742c75b05dde782ef0432ac37deb30add13dac0e6021705649e82d2cb8e30b23143e04c643738ac69cdda9cd97f97cbfa1baae2b076fa78c3feccd32962bf2e9339893301b94f30fcb31e8564afb067320608bf2fc177624ceb4a1612a5ded969beec3a10e00b84db5e8180579da0abd40dcace57de8705d80ba468a365ccd0c8c29397fc10a58101a0b71c4524f8b8e3e85d6b720f08507542e71b29501daea58601a2f8c9ec9971cd07b38a18f3f2bc68ff3b6c7532d9de6c7f0a9a69637f427f2bc787eb8ff8e40bdcc9bf18ffad53c3610eba57a53abff388e44db0da5db1367e80b51fa709a30323c59ea2e0c4bb7c9b4ab335648d49149bd827e9d13fc7e7c977aa5d38dda79c7b9cb600b73a115989527e80f9759ce975fc544bf5e00454763bdbe29c47f2cc7d97aea1368fc44c3fc5d9115372a9e429188985911900c62fb737308e290cec5dd26a37a1d835414d620f17167cd15dfaf2c91c911c26ad604b79195bf96023745a32cff21320bd862cd4b8c382d92e8d1bd75e120e27905ee7e1748507fcd0fbd8cc395c46c641b4d881e1f4a7380a7abf9d64528ae1a0bd256c41baab928948172a5ea1c50862454782b70148575c771211488a495cc77e969c74b4fafe09711c6d7093ce993e1f190e4bc9645352354517c34bb8ca173ac6b7f6e91e56d16a6ed899020cdf524af9e93dd333b09b48764117ce6eceafa4a4a13702538e667f8b04e99ada0b28902161f7edd4f0aa5d13be4582d4553e409425091f71ea261820277a1f3b392e5e7bf6857c3aa5ea07f07211f31203572012654e67223fa74f351475492bfca950119b4a456c0d9a1938a56181b6ece68e5cb76509a6ff4ed0a797d5b55536190baab4be3855014104c153e9e00853e9596f5ba6e5fb6ba70a6c279c878609faa35744aeffcbcf3e230a0f945db6c042ca2fec4ba59e8eec387f50a2dbf815c10a229a659ec44b63b3f866979d17c97ab4d34f0e0819ae8f627bc5a03216b7c1738513aa8905c8763ea82fbf15d4a5b2e543a30a913880f81702e8531d9f9e3ba5a2aba1cc21a1c1201f9d6d3984d6be271c24567791a708e638ec311843eaa32c93b89971d0d0b948d414899597b7808d7360cd5cb10eac20be53dbfa3ccf25984240e3accb26ef25a55a3d4e40f3f83b978032d24a33a07c78b3973eb5ef8f7dd4c63c8c2fcad8cead4589323cdd8ad1a4ad21b9d087f2154d954c959ecf3377c2e3665e9adda15310fc12d38cc20ab519f7ae365737c9b6ff33512a8f82efc44c9b171fa73f85b7fb8241ad0a4d545ddd6c606cc18e70006e4e0d534815d66d7911161ba83add966935550e28e1d90841a64e494143b21c5ce32e664a6487df95e2aa585fc49fb8cbf32bc2be86957dab37855bd4c99c45799a1d39835a02f47f4bacb97aaaf3b6f19a82816636a2f80bea9e4942e18087dbe52b1ec92a8663ac03fecb21747e7b0d2da2cd2e9bb80cb5840c0c27960a577b4cda60e26dd948b112d9ae621d81e2a56f2ee0aae1a270146179a6593246baad2a5fe5e65d097c66e90eef4d04a0c2d4ca9f9863fcc54ac54d9065758b8609d958a5c65b7c49de60847170dce5e6edd6590943189853c27ea5d049dbb21ec523d46b5e0d14a8f528ad5c750ce35580c3a61b4a6e6ff68a6326b9677178fa67e84c60b15998e339492af73ad20005e7b0ae34c7e1146d7ecaf4874fa4ce97dc541b26aac593349b32020a8a6ba57cf188cf3607170fb101f86b4310221b0c20fa290d96e9c42111a286e64490c3dc45f2e8dbe1546fbede92aef6a51fa8480fa5b21785f59571792ad4ca1d7d6b3a5117b5d20c728eaf627ddaa20034a08e10b6538096375847427270c646ea1a797ba6880a0dd36e5d00490904022f9643f0b8fffb53ccc01546726428cfde3313d66d98468914c75230fbd51e112d38cc50a9c27e6da079282c651f9eacf760e907890bdd26f1838a9ff1915e20fd357008b0a166934f8bc4f83d8a71adb96ec23c6c25d95812a28d8924e8981bad767ae1e8e426b1af0dcb24f7306a4617d25598e3188525caef981c4dada077696cb01c2d3edaa301844ab3270a22addf3e2a8b59927e4faefe39db21ea7c8973ffe32ccfa1a245895c513d8f329c6a5d25126eb5e215f53554dfe601e5032be3b7725907d4c5b9c3f96995f5970c6a2e353535626c7e3d8a60b4f402efd6183a6cfee9139c5724dfe2bd5ea8e9c70c3f52b2e91a23bdea04675ff7651c06dcbc35215e797ab1df2a2ca2fd6f0c9aeed0700e01f5127578e807c3cc1d86d41f79f355b0124e0af71bef97f0b3bcbd17cb0078548b656eee0daa5cff37c2298827e7bd3b03050798b2517c069ca36c644bc8816d6c0875cab47d22fe0549d0bf73fbcfc44bd25020e502b0ddd32022b065a4b9f7daf4964d86fd3d8584856001e6c480cbb400da434149dc4b71f6b0d234497f840b36ff8c1da4638dad478c276773ce83ec65074ffc67ff750c22b449b4c40c040cb4966e9b7e56456f3cff643a34a5a135f976773473fdc3e159be69b6b87604e4b04385f9abe759f4b7a5859ce79531880c78018b2277113bec70c9fe48693c53402fd4c1f8cb8c4a371be0c46cb074312b2a2a1f601608bc56d09eb47c9010a0a0cb428865d521ff17b757f6c9cd25a9cfb45d0cd751bb4676869d73aced65105a1606d94c9a023731cf4c01e9dcfabb27a059678697ffd48937d81dad0bfa98590f977e248b39d17aa2d9fe2edabf51a2283f8583e17bdb0241992fd89a9007f0a923ba675320a45daf41b7f0b00490a107115362aa16f1570b9acefbf06645a41a3c868774c7249ae2b77375c94e93c4e9e2a29d8c43a60af2c1f9c3dfaa6a234810105db3795ede906dfc19b7e9eda26225e4eed4ce43e4a4e9d41a71556f507e78b55c018e6a6f6997e3051bda142d4b84c530181b93920c8e258c4991ecbcc8a06eea8408184b88def4fa906f59d2eb096532ea2ebac20f4cb79efae1814086516486f35de3459bdeeb8d9f12d5e95a08141638128c063bf2b52d044773427ef5dee7360270b4a507bc36d37c8881f5a86a99ef3abf51594c9bf079f391b59464c96fc2319644a5be30a1c009ef9c205b86ac903c21c11092daba944714d2d997e18b2e3502a7ab56661501c4c7cd210678f42b6ede3005168d7b55e3c5e70f8770243f9e1be5d489b090f30d9ec24a31eba25be0524fbe5b710306395bb930f7bd3ed4ffe0e2091be6495f4a40ed063bb2bd5ee21193836273069e9a82744d37c9d31b8d10d8b9eb4a87d6c8fa47b579519c85ff500f592df69984d18d40ecf2f3f593aae4493f84c69a6f3b6f09b4dd9bd81d3ea079234cf6ee08281cb5de7c6f1894c5d35757d598535a83aacf5352f447abbb3ed109c1d3601122ef7f6645bee1426f1039aebefb95737363a73999fde4655c093872dbfded9053c4752d364edc0a1be8668eb8278004bc7fada6e1f286512219da67f59ed3ee4562b4c3ebeed8c9522d32db4374feeb79dd4f9256ab82104d13267d0d5d05ad4de6c21ee569f310158116189063624cf8f687d8da80d2ecfcfb5390b568c8c941bd30b7d7d2ff1899dd792138601cda5046576ce52b82a112745d6948cde41b28493571dfa3b80b0ee792468bb009e9c9ea63c6397242dce0cc71666c25deb59e1b4f54ce16c610670b4343bdc5acf74c36b32b5a06f3e6cee020e8493935cadda985761b567cd3b394ea7a1a9c19cc3aa64fcf817e4a96a7e7fb7c81f6860d66213cf187bbc0dcacb429deaafdda5757a41d02918a122103cdaf0aa6558747fa4c32569e9ae490913828e10be0e6956c7e1317ce5e46823f9df6f275cd7eddde90f2ecc1ad638928e4f89d38beb5d7d661d44c3c099e22d5af1cacb7ceb3584d095a579600af5e8dcbe0aa166024377debdf49cc82cf03e2b786fea659f3e58c55917ee649b530caa93a3dc78abca9d0addce4d17bf5e74f34719e58249863107751ab8e964acc9749b568e811bc71c35d67faa6e904397d57165c09491ffb2bc69f7394c9923e83cd39337bdf7982a9ddb7adc1603da7e9b98b91a98d0f960386fe0dee54f01f3e8ff3af17690af8728ecbf7ad2baff247d9c9cedb8e07e04c7663cebdfe8f47d200e119d7a1b43d2e482ea8d9425f27fa32746a3a5cdf86fdb034d182e5ed8d6de535a8a16f27f361ffc852287fc1f8ea312737d35a0dac8ffbdd4b044ca5b84f3299c900854576290dbc266c830baa9966ae14b4f81260163ca8e9d0020bae9b32ee93aa93fbbb7a628e23782bc002c1846b87ccee32199a208c6bd33bc004c92a42a56640fafb6ddd2f34567dc5d606f117c5a6895aaa5708cc2909a761bf988c6e033be00accc897f2f0000c5a326d0771ec4008c649b4f25501305f7e2e6312c142ee4b47615bbd649479bc2be7d8572f091acf1358309109d61e47651738a4ff4d5d619cf0e6ae30989833ef2377ef907159caf89663eff8703c112fe00f10e93fcfe834addce21061a032aee988c762ecf6cc2a1014b9bdda2f7739425e0bc25a0818b2860b16ea7377aef064ad52e85f7ea3398ab808374d3e4efd9028028085ab7be4002b38a09266691bd868718505c36c11e13478ddc35c7b5691b09300c92ba25368fbe11030a2bb832adc916840d5069d92514c3cf50ca1b6083100c88e92b7cbf84051a7877033a754c4391a4d0316dc0f41308a79af4606db8bfa7aa8b00ee274b2b149cae04722bb7e0c701a088bff2d44399191901c69a394b2988f32b7e0e20fd1d0fa8c3772ea557c39c9e8fb553327e8bda430fa2aa3be8334156dbdddf01ad0ea21eb0b9a107e84070f1be01080394058be145811efe5a5d1502fd37c9082b369c40dccae3244282c08241d092872f204ae3aaf7b21fec48530f7b981f29875cf7d4a0ad91ed091ccd8266c4e45b93acfc37d34e81965d356ed50aa22bc7de55b26f49642248bdb744605deb38162d30fc2a0b24502df0c95d031fb3455f60964987088fc992d80bcc52f55c66421d3603ff9eb1555ee0e9ca4117ebff8adea3c2959c1ced5f0cb049613168fedb8aeead139b57284a1e5a62b56fa88ccdc54ca34fe7675d0bafb6831fac4f96869a717b688fe82f71f4117b846d1fd80eb692cff378cd599ddd4a3445fb898502a00abca687187461f2d34d8fc08ab5449785e395947e2ea5d6a817860afba6cfbf0723d100163adac3282cbbddde96d95cfeff4e85da9b4b6965bccdc1ce203c10f64a5fe6dc33a09439cbc5eafaf7d104c26d36912fbe55a7eb02aa0f260e9dd0f763d44753836033309bff29aab095be0fc70d944f494f170621abf4184f908d78fd945d44df28171fe43d50bbfc9cbfe7a7b45478e3d8c0712c9e6f8e509a5610cda471e6dbc1ebda75aa44ba0821262b16f9fe71f2a616940556b7d116ec7c0bbcb560be6c24add594b559094e0234e9302a6cfe8e38954615a3195ea7cdea8b36929506b793e50df0d3b98a89a6d823d0ea8123815e8f297250d33ac85ce0df2e55f01c338c547450abd85542382cec31a165241a29098ddae78129196488981e1b5e9995df3699f264e3470feab7656598ca52ae1ea71c6cd1c3f60491026bc4dc2bab9bd929fa5bfd00c1c893f6b7b4f04bcdcd808a619776144c89e308fd88681cfb309bd2dd7203da1ddeb6c08abdc051ad34e3adfc86539bee35d5a423ae301396736da34189d96982adbad2b031ece7ce3939af8dc4de689fd2aeb2e8a9ac6a2230034d6bacac920588eaad65a4ec5eb0e39891bec00a59b7cb957a0a49cfd0e2cb21fa43bf2ed23174592a41d33667ef5ddef028e86f899cbcd5b78873cfa6cf27c1a918cde36ff40d0ddf646c9f7d339e1a0ac0040ec096e5a64adad5b347fe40038bfcdf95d9a99d003dd8eb5185e0d9a7b31dd4decc53af4d2856649e62ea74e2f2ef373aee9d7cc15889473d724d809b2bbd45f7ae7a179ff682f29aafee556925d1c3080bb65f3db3388195d04958d674fef3b5528284231c95991735aa5d1b64f9300c8980c749d1cab0dee9f89b621299be67cc407da1e058d21e7df217ac41e2a559de995930b10342bcac95d02c7c2f981011cf505592aa2f9f4bcd1cb3028d1b2a82a79215daef167014ca99949638b3ca5903dc073ea50f486c695a35b43b98b6600a7cea88aa6ead88248013b7a48320c1bb1fbaadf40177dbfa50d2f32ac8f04bb85c2942e50f9a6a0e43d208616f32c26b98bed6a34ed8da24071facf8e199ca173602a6c233a40268bf9c9b4db9ac16d0580789f42cbd13331945f54bfb092aa2279e4f032e92cfabd04805a670ba89eb29349e015f36d34d3c9aff267f90f45db44489d070f39fc787de66dda5e6c32f371451af3c6a68417f6de7a4ff622ab3367dc64acf039266559628ea6a73935560b439d2750418678b18588647ec1c7a3cc4307c32f55ebf73480f7b5c43fb56d8c49871374d85a4f38c0fa2df8234b5056efb854814551030ed6a3a0c581bee38d40832fccd155c40f303b5048c0ca7d3e988fb81d36345d58afe29c46e29194d64174fa891b4bb45f88b4f249bce8df5e3f3b0b70b13abdff33aa42b5203fbcd31b4fd40c2ba096db2770a1216e814f9125e91b7f27c3c1b2fc7f3bc27de05dfcdcae5bd40eff33c98e7b52cf87a78e0e7b140cfc987e3adbe0bbcd5eafbc00ff49c7c9f17e4bbf16cbc17f8799e07f3887c37ae9507be40cff38aa8f703c90bca6a0396b7f26c3cf087676303c3f922e402af08e87d5feb73bd1756308463c5f33eeff33c2fc85d9ef30fc8786abca8e1d44b0f6778e185870c1174a96328b1458c303c8a47030ed5c3041eaa4ac09f7aa04287271e9c454b0f61d43855a229587ab8a00456ea9021273dfc107252751c5124868e91229e1baf89181e0a9accd6ecd084a6c0c7991c1704cd88f880c2100203d121241bf000b40606339a8301f5b1e33cf000e333560c4ec132bec17d5386b72c77ca8c6a7b8bbb23f98b9b1e87a1741eebaea5ac65e626832daa5c362e1b50c7ab89cb66452427ca84a69e974febe66504e6ecf0c09c960cd4ad74562298a6cc0d552d1d9fcb0604d22162a444d50d979c1204d261c2472484b505f45a50185015054b9258e6036f402c3f27a802c10948bc30c163c1440ca698f081415641398f470a6b832faa5a2d0c9ccc54b0e30885551513005005c4a64b4e8f9c1e2250a920f7d2376e5441d936556632842e9b950e4b0a54a105135ea08e16116fc71213ccf0c106c905ccbcde18808e0f873545743901affc17264ee0c1f2b9a172a3410b041089cbc90952f450e504235eb0201c32a3436259e18a2a9cb38643191d253b3c2e43564f5c409a9870e372d2ba79e958152d555911b9a1e246d58d6b4907ea65458812951b1b2b3950251429aa7c80404d6e5a301e3aad1b1ca59e1084a8ba7959b980c642156b15b2bc9696d60deb490b8994239cd3e6c8cb4aebc6054427c9c608d864e3a3c7100d825880e4b5c195270d4cb8798dae253a4b8a969c5c39a02b080bb66a6293031ea1535c36ae1b50887505349ae2b184ac7858160cd9bcc89513a5273f5e3716d8149980d48443f4f2018d80465a465421d10126b136605d6063041cb223c98ac80955a390b7aaad82c0156b849e10046005be4b28e1051a269630028815c89040a5a56a4fce4200e1030fb86c8172c2d2811598d1121f0d322842412c1cb0660d1198c181940c7a820071f110012e8610428b1207302215e4e0b4aea0220149103104103ff4c0830e5b7c8e088d38dc1005b5660e13d03e58430d05c8b028c30bb626e5c98d8b88186028b2c9409a2288803969e2a307cecdcb35440802a000e1cb2c4d1d2c5151da21002a0060270c26552ca1841164ba70296af2a30738012f4ac0454a942636ae56132924c0091f6a10000b231460871d5ec8d224e5c9911c4f8e551cdf1ba01aaac6770668c627062b0c4f031f06be0bd87c9163816f029f043e2abe26564b7c526018cf8887c18b086b880e90cfc75781d743e4c1daf9442f04757c38ac9bcfe6f5fa5c2ed6077a2bcf6bb9ce8e9590951555a00b5520961612eab9da302114a28ac5ca7169a00113c01e92803e728038a2b5421588c5c6829b213fca54d978013f585d80b35b392f2a2fa3d6cdeac98f1bd61655af28aa7a8060824dd14a073672c0a862ada962e504b94055ceeeb1a2cae6878ecff58425c49ae14c053840067002d842d50e9e1d3cab2051e8f5435c73026b8397910bca6ac70aca0b88aa155312a1d64d0b89aa154e2b0808849364e5813920d00d15d6062e2fad20ad9b56989c1e393d6c7ed858e0f2b242c109ad202b264e60655195411810476519100161393b5a22e8ad825648a0538e8fd6e7eae212e26a6255a0c36be1ac5860ce6ab5fa56aed56b657333045cb9b6b8767638dd88ae96cb5bb940225008f45e4056424cb0c1807544950e244c105ba09057d209201827f4c8a8af498119194833012f2090c569838a0106a5230ea214e1bb861a4247c21d2fd7970fb234ade9c20214c0e085211b0630e50630302081072800014b0c21346a50c3b95fd0b90d12a8e1c0171670c001c8941002081f7890a5a90a150c2ef061011553905942891248901119822f063ac0f080185cd59a149cc00d10bef8b040c4c003b0588002100045132f04400559085eba64695a9240175844a00a1bac6c4003127880031460851040b080421912a8c1c0170ab0820a27cc547db9a424eaa829830c3020e0802912f0441131b8f0a52c0410be7459527a02fb117524d0050474c02106335f86d0c11215a527301d3aa097418604bad0028b2912f0441345c8100033603a588af204564494e46747047d4d196478400b0860e1802912d0441132c4100017cc8c210303020742e31c7260b9b243470786c0d0020b1e7419f2c10516765449d8ac59a5e043014b8e2f8ed711a0115f11df109e922fc96ac81b7182561baca805187c433c21ae202b20de0fd08767810d8f9d9d5508eec8d111ea04c9018233e446b471bd5aaeafd562b1569e0771bf09739870779dc73769755f9b73425f62d2de9388bbe7ca31e32d0f73b884394d618ecf0ff5a1f44babafa5dad26de9935daeb9de485a2dc57103878d10870cf719b55dfb371dfda151a0509f2850e8b5b6a7e13c11e214e13ea3354cb5a57b4703338ee397252bad2043eeac273dac23ee8e818738465ce7f1e6dfbbe684f20641100457abefdbc0431b316cb8b876eb2f29ddf852fdb8a43f3e4fe613ebd046091b1bbcbcbf2bd5a1873654e5fdd0868a0d1135690d539a712dd7ade9db6ecd371b50e731df48dce3849eb4a6b639c1dd2f70b719e185e6b5596b7fe61f77f771f7235e194e92502934e79344e2c489919322274d9c307142e4648913254e92381972e2c4c8c8a8c8a88911132322a325464a8c92180d193929322a2a2a6a52c4a488a868499192a2244543454e9a1835296ad2a4099326444d963451d2244993a1264e9818312962d284091326444c963051c224099321264e888c888a889a10312122225a42a4842809d110919325464b8a963459c26409d192254b942c49b26468891325464a8a943451c244099192254a942849a2644889932446498a923449c2240951922549942449926428899321a3a1a2a126434c868886960c29194a323434441ba21a7a7717e2a10b059d47dc798536aef6199360dc5d03f0cb0d90cf8d0f0994043a02a54037403723504a2975f70cdcdd88bb17717790c7354461ae9c1908c29606b27077201eb69698cd28ec066836a3b05d7b93d29bd9cfbed96ca674d7def4995158f9a6ad355da34489c2a3ff46ee8aebe1095b3b2a0a74e408dd95962f45da5b732d2777582bc8dda9872d1da7daee9346a9b67fdbfb64d2e19cf151c83ac37d1cbb70779787ac07b072f01f0a050a4de243ff34eb149dfdc54d335ae9c574f6e393ab59774f18ec62bb735966b47cd3ac240cee3e86e01cac9355bafff8e4b735bbef974b9bfc30b42c0270f72e1e8273b8cd82977bf9b88c6b66c5d1a4a52c45ee2ee4ee50dcdd030fc1289d474de6ea641c0a2a2d0edafa62d2dd7b90b9923966b3471847913966a424c9eb7ae4ee3cdccd7a6eb296bfc9cf51296d6bb349b76dbb8f45933976b39df82a6ddcb47192ed5ad3d61cb5db399cd2cdb2317eda5f65dfdfdaea8bc9bbe4ee629871ce4a9ff1862a7fe7ce9c3192fea9fa584a6bd6a9bb9431951ac6d9e97e95ecee501eae80e83cd2acb933a65577dfe1ee3a3cc7dd71dcfdc693c1ddb778f899e177fb16e0ee14a6cb7b621aad61ba4f4cf196e93c0e8d49c62441faf78e26b4448911f94e68a513274a9a90e42709c26172a49db76d6dbbbcb5b57d16c2b65cc951a84a89a02d4ee871f82644a340a1fe7a1955e0e1477d5b33a7e96cd7d98e879e09dccd1a840f0fbd27dcbd87871e0f1e25bfbed6dd75dc5963bcadf358629c10f953a1e7c191bb5be0a107e4eef74d0a853e8d96e6896f9afe506d7d28d597d7d29fc2b8cfb25cd56977b771ff4a77ffe1a107ea3c6e5cade5ee9e679cf3d35b69f9379ffb33bdd8eebaefe3decce12cddb6e64d6dd7ee5d7119d768fd5aee3ec443a77277d1439fc1dd75d8ace59fc274b72d597395d479247f4aa6ab5988cc5a88c4a6598576b9669a35dd9db502ddfde3c0cc17073840151b1f6890f8410004c63875c08aa89a0488d0c831957c784060e4050fdd6b8cc3061134992a006163d4f35380245ef000b2c2b8cae924231c843983c20faf0a00a98a02c7d15455c57c05c48c0d38384467bcfc4b013473c6a3291085600d2b72de3e622c1ac63525c5a25e071e134355d5b3955a3d521db4284b0a4b6b4bb5a817fb5c3ddb8bd9ec10426506779c1d3478d4eb99c1a3cef3ad78563c5b8a45bd954dec6bf56c2f06ae89b1c01e2a7c7bb1af870a6f35021c213492080defcc19907ed4e3317306ec49420c07522b2ac6d174c159519c55cf151c5f8959992f332798e07df403bd038020084a7dd4061c33f55156cf5e51d62ae6d1970be49162f5881913b3e9916a51d69415eba32fa957cf981808ae562b70058220b85a510ff43ceaac2270dcf3bc559817b5b1a1ceca1ba4ada98fb6280be411f32326f5f578cce31163c58bbdc4ac3cda23664cccd52305d28fb65a3d62563129a71ea3ae7365471538424000234408e9f5c011a265b5e3078f7a3d3e780a30b3a26652203a62d13154ad332dea3a5bfc8845cb90a9aa02573cad9e174f1488b3c307779ccfd3f1c0a945573cad568be2b8ce961665f5ac7870583a4e2e123ebaead2a2df9702092dea1d7d2e176db97a52c869b5563c600a24b0a87704521c9c1565f18c2173c43a5af5a4907304d2291f4bc989453d58d1564f0aded18e35472cb0a70aef49c13bfa7aaaf04a687d200e38ab2216d107429042c1a1be2ad2e1f9705639383cded1475f3d551ff5e88bc78f7272a88deb06a4371ffdcc8034877a3c24d850efb3d1a17e94c343a6aaaa8a27050e8e56f486c7cc19ef684553e0e008a4ae23440b487568d4ea0887474787dee0d033ded10df5e88bc74c0ec5f17848b8a19e8d4749c0a15f4ec8a323848a8bb6a45c3d43ec3edaea1962b7a21cbccce0d033d48c0e4d811edd504fa7a76a453deaf59859d19b9e2a3329d0231dbad2f940878784125614a7a76a4575b4e844ade877c363c373c63bb2a1383c6652e0e00887e27c3c3a2b1d25215a40aa433f58d11cfae23163d385cc914e0f0994841b4a020eebc686278524c470106b51ef88888d508caf89b57aa486d87d74889d102d20fd68d48a7af4e5b4a274f5b5a4c0214a23fca84789ca086d70848000867aaf55d19831257c74454bf02848c72411e5a22d3ac6cc197a14654359b48417bda13e26057ac4ea1953428bdef49ca1472b6ad353c2ab87455d3d25b43ccaea29d3f26e06aa8858c3c31a1b5060460df0f33ed002de1c4f78ab700e1318f17e2001fe00636172629f8ff7792b2f470e325e46af550370d05cc941220e35719cc1c6cf8e385a8863863872880388389088e309c7f158211c73b88ee741ab1d8671a461f580ab1c560e1c5a787050c08343031e1c6378dee781ac9507470e7000010712703c010715703000fc5a28a0f15eafd70f9082a0d11b676220f8860dde8a7e3a5756d4a3204501e7fbc21ef405d27ae0f84794055116a0e7795e16edad413dd08d39dc308197e6042dc4da30b3a24f629ea6046956ab96c33cf6a238613eeaf2c0d8176bf57c445d84ad9694778515868c7f06d820063aac0d11ecf8d183c562adbc1ed08d1871d88f188b4533f0271e63d1230ef3d80cfee36de4f06a0388b00d247eb4f1046d638a232c26aaaaaa8e38720aea286d10c3017df5e908332616c67b85f9562c23282cb0e5020816b96898bd22dac24c122b6a26057ad4ea49e33908e6803daf2f8ad6ebd57ab53c25cf080a96b0c7aac56ab58c5a2ed7b76ad196165e822c3e1c56192008b672a07c3facb0022c232deab2f96c40b0f5d2c971b9e84b47e7f4540163383d2c0f02691530e6eab90163208e0dbde159c16c28ebd371b128c8638487d2d0c5fa7c86582c106c5110a42d90b66e22f0b2f93e9beffba80d0fd0f7b32a8aad98b810ebfbc0d647592da0cf670512f9a409d18055d8785ecf8fefb55af5f8b4284be7f54d59794e73404f07a43d3c90b27ae440f3796872c0f93e96fbc4b4cbf5ad58ac6fc5ea01f2201608aebef0c2c603592005592c10044116c862816a8a7c7c7c5e2478c3f5234c8f21b1154841230e6319b91c06d2211f209f17359263c40b968d0dcb8605d2cf676504054b0fb0e56ab568185713578b7a8cc562fde09902335005c3ca87e903af7c14a42c5087e5e2a161fcf359c57af8be8f8705027d31acbcd56a459b4cf97c5661c80611abd54b07908d19b13044b195b7c3615e0f140f5a515087c3be1ea75032107ab18fbedef83e8ac3e209c3a11378432c4a8d7846565febd3f97ac2901a617d4394d230fcc162b1a6f8a0184531f2fdb0582c168b0d13c4582c8ae5e5c35a7d3e465f8fd1e7c3a25f0fb562c4a250b084445f93152513db1ff5629f7b01c6c7a24ebf8f05ae562003beeff3becffb28a53f28b85a7dabefbb22c4f1becf410aead06a79200be4a2f5b558dff7b1be6fd5fabeaff57d9f1cacef6bb1582c568b25c4cae359ad56463cf40182e2b55a2d6aa4b55ab5807c82a8cfe7e3d35ac901ba3e10044110044190822008822005bfeffb7ad0ac84f0224aa91b3c0c7d4386b0f156dfe7f91a3455aed60742f1a0d52a8afb185d11be62af1e9607b97a7a8c7c3ee12aa4d4c8ea034d80b302f220162bc87d5854c7a31e0f088a7a7c3f2cca3a0ac3f8c45814e88302bebcd52a67f5d1950b9ee785a18a85d92b4fe7079c149c112312f33428382386f3e540f97e563a4a203562a4275c03430812690152d0e7c7eaeb6179d0e7638445874e80650804b1d547575f48519066e5a373586fe01039200c12c359812b6781617c629ed7628134e61ae2412bfa83e54d88b8cf7745e831d0d56a40f8d12ff662391112e147411e9607b128cecb49c00409a220011a17f298072420c27f5ce90b12033d88f5d9bc1810b25e37397ef4830436c45674489832622b8a839383b3001d1c34e1ab47478e8e520ece0e1d0a087758118a6115e18e4845c863678a1e3c1250410f292ca8208ad0870508087ff870203f5e41807c3150489055ec3544089121de054430b8c0631fc580877a3d3c457a3e234532300245e8c5400d32008f68f0f239f2c5c01f9f55ec85e4c7db0089c73ea00dbc20a06f0c3a40e8c5c098d0f8443824648030c9902b49e22d51f2112d71225c31216aa20993a22646454e8c604e9ec0a03c8902454a140ea4f8140e9a884d398a211d252129255151aa42c54a95252b1d2c79573ac07285094b13539626a72c9f16a7272d504f51505ba2b86c61225c75e1e241172f1e7ce0e5cb07207c01034208606421cc647546ab24ad24bfacbdad76b3b9bd85b152619630a570e6c5e19bc36272598cce5b9f7bead44d51e9765422ecbc114418330209634a20814c0955644ca83ac18432279829838299145000400a6700f0a9706605155858a105165c6821002ebc10802560782106186488410032cc20001a66a8810625c2d5006af06c1800016cf86e20000e37ac72c041871c7c071d0ab0c3c743017ae0c1871e7ef0c181f82108203c2182580d210411437811441851847784112b248ef0d89704125e6ca544125fcc975022897015f3623945433e6f091d252442228e083feac57c3e9f1f5f8f9107093124844e15108cb55820cb83be1e222342cf0b42e7eb212a22043d26dcf38cf08ef090f092f09af098f868921a38fe799fe7b570c6f3bcef5b83873572f82e80e31f0e0f26741a6081173b40b0461b62190b66f011f3346a8c50b3001e70887e48a3c61b3e9e6d0905c230ce819847b178e1feada07c3e1e1aa02120cf89ce84de43c641f0cbf11ea0c762008ee779dff7b97b40e0e0b80750f0267f8ad69f9bae5660b028baa3d1f2f77dfd39f7d98792368737adf4feebf6d3a8edda5cae32d1716b6bd299942828331cce358afbdba6da34edd6f42f7e1cd6f47525e9b574eb334c0d07d9e8c6970a1fd1719731ee7141d7d2b7e56a562ed8897e356bd7d2d75697ffb63149f712cdf46934ffd45ffa19d73eff6d97b7d27b526d29eeb78529456569a263f925d574a4589b95026dfd369b44c7dcecf7493ed5cfe88e46f743d1b17cad9fc439476dd73e8dfe6c4bf1d64774aee27c68ddb5a49a8efaf1a63a579980f2dff6dbf0a6fbcc99f547ffded1e8b52399716de3283a262de56a9ea4ddb9e668bd96ee7b6292acd434cf1afd5b147d5c6d223f07f5ba7e26ef128ed2e57f1395128e7a1c46dae6596b9f7471d3be2746aafd13ceffc1c54da6a5629a158965dd1d8b874dd870f7809cb9e97a7b7d52bc29fd1237a13005d3665398a64c99e264cad0142153804cf9e1394c7f32f0a1302a447f32a01428030acb610a9401f5a1140644c17cf912931c8c117290c58f4f95c1a8b62deac98baf0c06d3175b2dfb8c492d2b7feb33ef7bca7027ce5e8b334ffd7947deb7e5c7bd6cbfde16473bb5145969a6596b325b5050d0eb7acbc9ec364f9c4c26cb057d142ca25c41f1a67f96b4527a4f4a0d308002a8a1608a7c09a354b9e3bb7b29ba7b29d9a6fa1b46c1e2ee3f40bb97f259dd70d3d5ca0d06236dfefbb6fc657d2bdaeabf5f85fc1cd49317375d6c757e2e545152e4df90b4bb7ff1d06805771f8f9268ad96a5fc8cb73e738e7eaef4a7bcd527ffbd16484bd5cf694c5e4c526d6f9bfe131393fe9bae56e8f8e3f3b72d82d574dfcff944a2e3c6f8893e3131d1ad7f572cb4525d6f95eef24f298d74eba7d1bfedfcdaca68f9a59dbab6dec86be9d37de4e6bf45651c6eb4a3c6fdb61953fd53f5d26ba9ae375ac3389bb8aaadac56dfa465593fef5e4af6b65b6f24cee69c2cd737495892bbfff01006e44e6174574af1a6bb975aade1c4012ec3597cca82aeadfb9e1817f4a6547e5cc6e1642b94c18510c5c3221e34ce2f02b55dbb332fa6657d93e24df5b634baef5bd2d66aa7a6e5ef9abd69af8bbb971e36190a8baa0c2d094109bdf5b6a9d66fee6ea5fa71b9ea13dba9cf4ebb3e6de7fa379d475a509322dc71145f8a420e6348795cbd34474f283a8fe49231c9b82488fc1293413f65779db2f94b99aea65965baca6ee4ae34d99319d77010ce864cc6b853d89798a46fd2ea8dac9fef49cbcfbfef5398596ffe7dad69da9a7e5bcdc972f56fb2fd5a4b917f2365fbc45f6272bfa6b2b5cf3999695aa97d7fea65b76d7532a9ec01c0ddab42a14c0aee1e0965aaaacca040c68cbb37a68cbb7782bb47668c092578644a70f748f0c6d8b48a0cb183851934eeee1d8800cb147fd99866452a2de692df342bd2b5369bb3ecfb534a2f55b7be5bb6ae26762a5fead6276d62ec84c34d17db8d999ebcd59a666c6b6bfeb6b72eb66b37e662a72e4692ca15f74fb5bc9fbff64fa5696f4836cbf43647fdae1b6a6b8b25ffc6a7187bb76cd356d9e5df1b63c93826ad73364abfddf7c44736abdf5671f6b3537ef32cff4642d9b8fcd36ebf2531d46ee7ac49e5a74c8ca4f55f1c2e6f7d322b6933deda5ad14fdeafd5b2ecf24f6152dba4ddb67fad79b3e096707942e94dd6bc2f46dae553d7cf513557de9aab3c0df74cfa7f6f8bc361a41bf951a625ef89917664cd24c67d92dde6c94415b55febd8be8fdb55a728ad9fb4e55dcad99bcef8b6cf580e57b7a6d5a3ddb6a6dd5ba05aee1e0b74f756eedee7ee9ebbb7668e14b83b0ae658b366cd9a356bd6ac51e3811d41d07490f3317a18a2e6cb1743fec5131e8a5c98809167c1030748d8a20a0f9c8a2b2e807326e70c6f22072a649600c37d882132f8788115ba0e4faab0010d32ee0250535ac00010b0e22c2469428a32f3e0279c004a04d504463e8213aa31608059c0b59a2b72222079c271162c6dc1ba800baffd60860d178dfa4c86a812c86069c13fe04009ba0000f9866f21a10d1548f0a83be5a4e87abcb8c2af2ce14060e072e15598406181230120e1484e85788c0039073750a0044172e44f9caa8457c4c086177571802a3868428d139da06404272035e0496c38a091a901f27184391e20440a667c032a5e90522543e93f1a4041e29e1eae0102e698a3042430c28d5c1eab2f4178c07ba86c81238384294ea704e141024202fc020fc439c2a8220a1fd2c2075cfe00250fc281344e24b1011138102a1ec482a478c07d3c008c27b528a15b30fb3e1d01c2f00a74c24c816abe701e5d5c4089129070f8ce0f3e2230fb32858b2a8469962815e021990330a143180df88e146e74745140e83a70a0330001c305ae630510358cbcc0798e1a45e028536081aa1c243b6f728348154e0e618e38be38c36f1ca0c119392d3e54ddf400f5883aaa729b33150880074b44f8eb0d165a0841115ff82ba705193d607155b94810a2f000244bd0b8a83ee019c00808d0b85c5ef870ca41cd94960e26151376e4d0b49c3cd933709929ac0d14e172058f31ce3a018513725ab8d0b0848c24d1c1ce195560184dc810430b4f54813588a1c38e1826d0805a2e0f4e4a48e26068010d60304064ca8a0238802047192e61ac6c6040991fb43853b5a26db9917a5a4357421828e9e18703f44b811a2c3ab88d876f0237a87066800c0f9f106c48e0cc000c345f095ec430c48c32fcf3a2230401d040c1ec6392c20f09b373f6f5f8f184c36ecd14cf04407851c314aaf23090e3816c2799e24d41b48117b10d26bc1ebc0f5c5314a8f2ce7c30479014270c79627e7cd08d71a5cafbd265ca122c47428f8a042cf000a80683b784840278f174c6cce3712ae10c1a95175ed8442a031c68000f9e636004cc8d4995ab910304264dc8aa7c0c2a940082891639ff620b0c0930a2829167214512d59305d4a9d800174e78651879134d883184a7a5ca87180241075128e0e43a3479000910350b9fe1234de004151f67e18887324cdcc08497711d893064051e7c042b535ea43ce1e33a056084b89d271c572400739041430e5e63c283924106239fedc0438d15de08fd83333c2a2a6aa2f02d67beac41abf0c29d44708df0050b16f02b5130a9c1880788bc8a94595e8304261ce9450501243471c63910cdc05de08a0e7fd2a584264c200081177d41c68925e220c389ced470e4869b1c3c096e07270b8ce1858f2600f1443411c2375880181d3cd57082ff6c808010c4f18305ae01154e5c51039a13dc4836c1cc65c108efe9c00d0fa080c790d3a4d66e882e0bbfe00b03dc2813a5880f81010423527c013d481844620fe8d1c0812c4981104769ba0f2d1ae04511111db78096002f661b08f10a76c4c143842058701e13e08085a33796f84e1020284981dd719105256bbc1a21f0d0040490b141d7c37700600425eca0a1e33a7230233785180db88e021c608610379e3c670d288268c30584e76c500230644fe538ba015356d834f11b2ca6b2448004a5df14b111b1c11983dba83087084a700002fe82c30711e05c89c25f622680a68808d6b88b0d170604504214eeda6df1441ab013b8eb75844d50ad89b776805541f24410de82f58862c464c7591bc8c0136b5838c1596592fc80033480b384b0c0608ae6c241319676590c2b1c1c404e052b601839a8a50d204168910107431a84a4b921f45505766e5d2c5184af08a0040514e078e02b9a15b80700b1c65742277c51f2a307ffd630d930841948f8370136b42461060eff8650b23519f1c23f3217541942c60dfe79e172850f4e49f8c7448b36c41cac7f3dbce814c1f1807b2610634a097e28710f034a413ca00136f7a6085341014258e25e0f443d7007d8c1bd332b40a14651927b62b8ac50822139ee7d31c104213ca8dca3e20606d248d9b9b7e407d7132ad8dce3d14ca0f161cabdb08b0a42ca18c33d9782c50c104bdc49f00309317a38701f230d921305d871ff029b2957f0e29ec5049608634607ee54b08145911cda706fe20164f0e8c47d0825286e7c2b771d685ef8a2729fa1c72b19e1e1ce02540c60b2acdccbf85072021408e13e4297d3070514e1be73e4b0dd10c51d37f40517ae22dc6b54907152821ddc674558808b5c18f70f4a108ad0e4343194c6b9a66fe6ee4e1e1e4912439f949f4f9bad5d4c525ae94fd51bd594d58618fe73f34aa0e1f6e4062ddc2b4107a327554adcbc12361005ec880b508fcc4f1a2713f821c22303001b8ce038c0cd2333471646432870c6ab72c1a9484a4f149e09517e349931208d77828f344cc8a489c22bc34345c4030ad47866ac0d4bd07031f25090438d1230aca01e00cea049d6fcf0f25688319184522b9e0b47c218e240c9140f06285e42217441e3d1b0c3692c41270bef865a55900eb8303c1ed8b00244c5088c3c22e60863030ee8f0c273228d131239a060f0ac20c11b4882e03cf022a0059a1f27ba2a0f8c149ce1e3010f67786e6c21e288ce89303eb10a2b1ad470459a6fc9076832388006a2f840a02167c4026fdcbe17901051c4043aac7c1170322bc2834e5901f142448f122546ab290578c1a305ca6c8506541a9aa372063cc930c2e0052726583d51682e50e28334ac36c4f048a09aa2777fe562abf543d9f2ac17e3ecd32aedd66cb35d2b75f34fe12af96bfad61c7571d3eec6a4aed5f76fe4b6529fdb45eda87076fa421f23ee9ee3ee373e3d7c42164280c14f664139c2dd3d0f244001267e80f1450ceefe79c93d1df1458f352570f78f081f8a70e231451645eeee0dc101047a7a9082e2e6ee1e07327cc902830a3a50e1ee1e0356642ad0810544b8b9fb0780223d0af0810cb4d8c2dd3d032841731d2a84f0e1eeab3646108213483880002370f70f8b120765b070848513b8fbaa8217bc50630036504101776fdd40c31b9e05602998e2ee1e196dc430668e017ca0c8dd411c662e02dcb6080386fb97f8067d89afbbbb77640677271281f727376871b331ce3a8f34a12ff115ba96e6ee42a8c119552fafaaca84aa9b2af7aa2affaa7c55e52a54390b2cb8dfb88dbb9f715fa1ca6daaaa4ca8aa62a1cabdaa6a85aa133c03203cf0bcd048933baedda52f6bb6e5b66c5db1d86def9208d6acd75251da99b95c4cd79b53d4932ca71f57ef266b0e87916030f24b4c4e2959724a91e5cf51559c14894d5a36ebc65b4655715415475a7d6dde542f7b5d6952942c39a1e477a2aa387ca3703d61f23b69184c8ac4e69194f273399bb786e174d541c9e2218f936ff273c624ddf86a4a6b96526de9dfa2e893992c63d9acfc9bb6d7d668fed2b4e791cea3de16674ddc6fbd73f79e7c6bec846b776997f1aee5ad4c66cd9fcb62565cc64de5ae3ae9f7829b16e0ee57dc1decc06d75531b8662d179dcd7e2dc9271282809f9608ce813a3f38b14287a579dfe6b32bdab39adab89742377cd699ba47535c92efb66b3afabc2dd3ff010031a4c7bd2928c49462541b8766dd09b95d4d766b5beedeaa4f36856b382e0e105398ce37864bf09b4eb671bddb8898e9a9ab59a27695692deb6d551dbb5e53de9e74abf0bccfca9376555df9a374ce7f1a6ebeda6eb8dc81bee3be39ba618ef9bae370a7b9d7fea33752f7f2abffea7ff4c85d4160783e9fb5278c3605a9fbb6ea8276f35ebd1c556e7e702e5ee4b56623c5eb9b4df34ab130e43fdc54d51ee4ea99090d0ef9ab7a69d5a88bc3b1d94cb5517b4ffbea5e1b008414c868476dbe69a2bffa6ebed9e30d8ce38973bb5ec7135d7de5633de1a067b9cddf7cce1101e9cee4a7fd7ada9bb2be94479380448e7b1bcbfa9fe89bb87e0a1102afcb7fe2bcbbfad0c87734d467e4e0b913f25b4847c1a49d6222325342743b4a2225a7d2323263f4454abfdd00f91b526444e6aff4542e4d6671612c283bbc7dc5d5f6c37f9a692c6b926779fc241fed24ee1ec54774d72f72858e8f772f7206b0441836ac8c756a39a624a58b264397a52aaa214d5b4b47454a569e9290a8a4629452165c182654bd393d21295a5a826a5a4251a85a4d4c151162c31a5a724a5a8d89312d3962625232a515b94909a94b66851828a5282226d7edcd9e4c424b6f4381b8514b5e57136aa7c11ac59f3dfaff226cd6acbc5766dde504e4c47417ab8fb17da9465292a86b484c5c92906e40cf72f146989ca12929213545094074a514d59908044e08af23779e69fb2dbe26cce012102481021901cdcfdc78782f94295a2625c969e9c1e67a37ed7242af74dd97d53f6bfdfcce1307997cc4ada8b6f252f6e622a6d16772f2a2d2e6d16dbb5f9cb9c653f5496fc5359f297f59fbec44cb61af534dcd964ab51eedee4dd9dc9ef1dedeaf095ba5b6c35ca76edb515f7d9dd85ca2f31f965ddd5dd898c6a7b8bbb8fb6baff342b17775f72b1dd56e39cbb2b49e2eee5b5523f427717bab8c9ddc7a0c761285f4d0940081b511039c8c5b54309a50deec2401537ac983272af0cbe19c0121470971b3c2c8d300a59e36b44e0ac1a58285aa2c600f7a810d70ea008ff704f96e860e7e3ac1a560d58a20113677d6044d20898a8fb8f0b090b1b487197e2428b2d6a80e15e03c8c0e044cb8cbb801c81450f736471d0660133c81630e5ae14b280018438d6784b0b4e8d92c51a7757fd36151ef817022b63970a38714781115560a6a4c1a24dba18038a35ee728205c90394299c65860f330fcd8f7b44f83ca9e056e12b262d7878a1fac1bf29381d1c805be32dddc203a6ec74be9240129a55d593af9c9ca6907440046fe13083064e90c08b7f5128b102ce41c45d28d0204415961ddcb546083954913373ff2288395ef8a183b7841ce940070f4c38d0b559b3be0856ef13ef2a160ec29dedaea66ae62e9ccb4107a378eee2c12b59a2e7f21d1ecb024f87f7520216f96a06f002d7f77ab1c095e7b98f6ff4cf3d2d20079ebb9eb07c47cb73cff3582ca20fe773cff3569f15cf73799fe7ad5a227cdee7b93e0b9e3cafe5b9fcf374c6d5d76af9dc500ffc3e10060ff4bcefc66be3f36c9ce581dfea3d2fcce779df6bcb27e4f3be16f87921f83caff5795a3c1f9eb7fa5e0d60ad5c3a3c0b3caf82d7cad3c1fbbcd6e779df4ae67de08d0dd197e34979792c23ab289eebfbc024ab9beff37e7634f1460093e05059397d0efa178407ae7c40d6e7ad9c45833786e79f8761e89e7ba0f7796a78af0f89e77dacef5b79de124fc87b7939dff77d2d24efc8f7819f37b41a7180bc34f07476827c03f0581f100ff4569e7f4e1fbdf140ff6ccbe9cc7361e53c0df07c0a16dfca6b79a0cbd3f156367cdf0bb6f28c56def781f4cbb1f1f1589ee71a02936030e47d37add7e7f23c98f77d1b58b53c9b0f5c4d793ddfe979ab20cf06e7f35e2eef9bf25df05df01159f9cabd1d5eebfb3c1d305c811f90effbbc9607d63c1b231e0eebf3589e8e2221df920bc7e6f368af1cd7e769f15e9eb7fa3cd0b3e211f99c7c433e9ccff5b1bc95f7791fcee702bd156be50543421f3a3a3d425146868bb212b8285be3e26c878bb31f2ece885c9c39b9389bb938d3b9380b808bb302b83863c2c5191a1767127071c60117676a5c9ca5c0c56a818bb5888b55898b758a8bf58a8bb58b8b9574b16a176b0a2ed6195cac44b8589970b1a2213244489070274890ef0499e23b41b804a9b9fbcb46c78d8e17d0452f392e7af9e1a297222e7a0172d10bcc452f4a2e7a6972d18b072e7aa9b9e885ca452f29b8e8e50517bd10c0452f45b8e8050acfd90152e53b4074be03a405df013283ef00e9c1778024e1ee3a383b3b7038e3210e34788803101ee280000f7170000e5e7888431a0f7108818739b88739ec789843110f7320f230072477b7e0a54347c7cd6b473cc377c414f8ce8e10dfd929e23b3b3fbeb353e43b3b567c67678befecd07c67e7face4e09beb3d382efece0e03b3b41f8cece12beb32385efec2cc077762010eee84143141ed2e0000f69c88087348ce1210d6b7848031c1ed6c0f2b0061e1ed650c4c31a843cace1098e0d8f9d1c23dfc9a9e23b394fbe93637d2727fb4e8e19dfc91980efe420b1c3fdc78d0e1e54f4e10a226a4972510b948b5a4c17b59871518b005cd4a2838b5a9a70518b025cd4420117b594e1a296375c7ceae1e213918b4f4a2e3e7171f1e9baf884828b4f33b8f834848b4f50b8f874858b4f5db8f8f4858b4f1e70f129042e42b180fca88085325c64210e175bd871b105212eb630e4ee366278d3bac9e942878b5dfc70b18b222e7641e46217555ceca28b8b5d8071b10b9b8b5d8871b18b115cece28c8b5d04c0c52e04e062173ab8d8c5102e76f1848b5d38c0dd71c257053b3b3a628e4e8886150fd180f2100dd24334b68768a0e0211a347888c60e1ea2818487683ce1211a5a788806181ea231020fd158e3611a403c4c63030fd328f2308d240fd370f2308d99876988f1300d321ea6c1828769d8e0611a41789846123737e08e1cf10411b87842095c3c01052e9671b95886878b658ab85826c8c532442e963172b10c928b659e5c2c535d2c835d2ca373b14c1917cbb4e0621902b858c687968f17a8f0f0050778f8c2161ebe90c6c317d670771d3d6e76585e7c8765f31d96f61d16007c872583efb076f01d16112f177a78e802110f5df0f1d085200f5d60e212b7b8e2e216d5c52d762e6e11838b5bd8e0e21641b8b845133a3737ad1c7104352e8e00878b633e17c7ec70718c0f17c7f0b83806c8c5314d5c1c93e4e2982617c740b938c68b8b63662e8e315d1c73bae3d8bc76a060e03b5090f80e1426be0325e63b503af01d285b7c070a18df81f2ee9ec3c383c4a07818b3e261ac8b87b1301ec600e061ac079b1cb101402e3680898b0d90e26203aab8d8002d2e36008cbbbf5eee363a44295070510a175c9462002e4a510017a510c245299670518a2a5c94620b77efe13f2a403dac00918715e8c0c30ad05e2b8400063a81266a8490a8d2dc3059c08f9a963339f8f0717777cd016483bbfb8e009039e3ee3e840511d2ac59f317ffd7644a5e15544c910029bc6789300ad77934cd271100841705029e78e2092928b2701b28c078d0dbbc83b874a1a3edce92b22c3d2939c994aac49eb24441c9a2b06c8951514a82c1727a0645df56a5ee792369f974dcd12815a48d9bcad7fdfda7ba6bd2efba93cadfb8a98673c58271d4a6fa7d3152598f5c8c15039c711f67abf9960bd25e020c50827b5036cd7a84136fb048276208aa4eff547070e215549af646864d04e167d0d6b5f6fba4d9bc77accab479e8ee3226d494f8dec0040c486552f91b731162d49a56690d676ca7eaed69f4379eda3a67fe6dfcd9d17cca5c6b146baae968bb16db2ea6e34dd75b8c8eb7df980bc6517a772b1314b08912403ce58b60931841120a4802091f7f7ce88f0fd57d936f20092cf507210f930091c000121170172a4205891328d535a764c9d2eb5a0ddb5e23f1b27da6478441614fd69c3ec201eef4c17ca1528228982ff4733bbce9085ccd1141c4d027f74d992c4a141a8587de376532234e30a274f73781a8b6541bf1b9eb46fd9a6a7be2923eade633661bffbea675679a95ea53dfbfaff18da4f9c45ee8a8f5b96baeb95c4582a4eafe888e9a8a036d8b10f1fa4244263384156f8c33caef04429c719caeba7de456a41b1d6d9646f1aeb67c1cce527de44d5a8dca475493d8bc5f969696718d6a4bafc566ad7c3deafcfb7135d71cdddd8ad35b6c43667dadaf7d7dca64667d0dd31ac774e44869beadd1bf6d5cc6356d561a366db47c6d29dd991796c3597a71d67d6eb7ebc5b7f699ea2bdd6133f7a5ae369dc7cf35f7390783e9f0b087325f62a4f16963a67f23a9e829e69f70ed2ee174d5c1606f9e2566c2e9dac5490b1e8c0c50001378a9c10ca7ae9fb7b7410188dc33093b5c2067964a36ab754f5599469df1262b95aa6449794f9a3f77a464c9eea5663f3f148802f9f8d4f006944867d6b2acf4568ac9f29bb45963b849f754308eca5fe62cb52cfbcca2df66bceb52fef23e958d9b1e67a374180bc6510b88000e2f57c2d96a22c51e57c93f0af34fbba5376d0d0349e58abb91f78fe898df29a9ee9a34669cb33b7fee5a264a55cdaa318e665cabb4fc769c5d3ba3aa4c33fd3a6ce6cfe54e6a566cbf466d75df27f3e7e8ae4ed726e56878d35dae96b4b5bf4fc3367f8eeeaa3992d534fff6b7a829c6f2cbdfd18d6f4977369374fcb2e234dd35d1ec74972ed6f59fee16a47f7a2a4e4e9fcf5d719f9d76d54ae9706c9a066848a26599818c7a8ae12242d10c4453ae8305dcbd04016cffa41040e841210313ee3a94e18cbb8fe6b87ba90de8ac5cfa2b33fab7dd4b5d1dae7a26bb78762373fd6bf5a92dad371d2d77eea57e4aef5e4a1fd15d5e53dbadb79bdda4c5e18bc937f3974bbfab25b1d631dcb4fb2b7af75770d391f085ecb69b6f32a7e285efbe08f83315aa2fef520070eeeec2085c9861bc27ce9d52189bf46f51b18d9bf25f5d968d9b5e6fb94b1b37edfbb7e2f23bf5f0705a00c2dd6b184f27ca9701c0177a9f4f28d3ac485a7f2ecb98cbe1b3f37d2e2fa80a095fc28211cf8a15b058817a73783f4c48e107ef042e0498c0a4822f881745dc9dc943326276e009cfa8f11d15ceb91d6766d079a491e0eebbbf62cbb12b4e001881cee3eea58478f67ddcc50e819cced92d02a0072e4fed1545e1a6b5091d200510dc69d50be241e122582d82d594fe94dd41543b482827a3da55a6fbfb39056688c0e9aa23c778688647ccc657f7170663b2b979f2b4f1cdb3c43819add63ee746f2a97eeabe8c6a07bd2dd71b12aedd258d9d6edb76c93867a5ac24954529eae97fe3a9d2c432b36a5bca64af710ee7dc3d339695b9eaea2edb1cd5cb76b2d28410689bd54964b25c95e92a339fbc95a495517e69a768eefec3dde570f72cee1dc0ddbdf2e9cebc5d4ba38672756b9cddbb97ca670e67ab49fee75afeda8bf09f6bdae284486d7125249570848434db0b220c770aa871b754ee4454b80709c15d47c8033d9c2f4410a3830d170fc7784182061437e93c925fe26bf3f642afc7aec434fd4487cda751d9b57a6b6c45ffb53b87fb6b83e89fd7ee8b49fa64b773b820bacbfb3ecd616cd28bddc930c3dd87689428b403236cd179cc7fef99938c433058fefd391725ca58335fbb7b30fc7801f40260d2ea3e71ee9e0b345a4b0884225819afcd591d9a703747a5b82a76402cb14b9231fdfc53621c257d28addc36eb38e04e3ca4327f9f544b5419bc08f65a9c2e8cddb6a6d54de1ee473cd4cd4033aee9a0cf3937743aa478380543fe6b71e114964c472ae114f8fbcce130199e599c30c0f699dbe76ddbf0c41285d2b1fc6c4f3164a5b5b367cf41cb3769f53ea53b8c4dc5487321ec5b19f43947a376583fb35208676148c9994217a1612029108549a114262b479ba5e9253da4fc325752db9202510e804c327320cc298cb66b6fba9a9a0295f7c435aaf3088389a180bb0f7928c6875dcde69fa278973785c9caf277ae1bca842abf0c736ae8d717e7f4677a6d7e27aaca14f526ad72b15daaca346afa5275eff6b5414f5e0ca4f7f91b4f51dbddbba0fc4e6599abee88eaa7af51f356fb7c5e5b6d957ea678c360b32ff4fe943e546f186cf685d69668d55495a96add5fa02cbabf663dc53ccd585baaef53d9d6274e464fbc3be98977b4b644476db505ba4b74d457874baa3759f359526d6fa5c553555345e5bf96e969d44772b8dffb75ae9a4f3513ba757db33e0e88c99b395ca5aa7f8b2a4d7cd39f6f514723544ffebd9fd64375bdb6f6e514ce395c7d5d6f495f947136a3d54ed1fd1a898e6fd2acae5f2be9d36cddfbc891fc37f235dd996625ed2d57b30defdc2e93b4ee23f7c926bf56ab3463bac9cfe19b5f9f41789f95de5cc5ed5e0af61b5f4a85755174bcf8a4b66bdfb4371d9de959141df5dd42c75d29ce34cb5bc35f31b9ab398a3f1f79dd6f7b8ad15074ac7457d376ada6f5efec5a3a9b7d9efd0001f9cc806274fcfcb7b2fed677898e3a6c6a9bab4c3aff14a6dbeeaae9cecce1acde96026d5d73f4c97e4dcbaf541fd9f5b681663f3ef7cd71465f9ffb48173a0e01dd2c74dcf5c949abb634f7a4b65d8bc39b3e8de24df5e53df1d3ec0bd5968299cdbee89d79b3bc2dd75d7eb6fa934cb392b4bc95a4e3c5b9f3fc530c05ca1848cf28ae7ede3a77d25be9aeff64263f3f8eb45be36babb6a4362013e34d6b65d097e6597b7de65fa23b3357a52eb68bb354fd2e9f9d4cf3a4f2e79f5a9276d569e37b2b92d67fb7ed72234f268ca3a06a56b25d9cbb986645d2911bdf74ae5e28dbb55a5b268ba39de5ebb069c6366e32cd4a6586048ed27d9502c0c26cc954107931ed6ec9556762ec443ba1f643e5306eca65d16fe2271363a7fcf72e99183b955fe6aafba7ff4ca5bcf82848489b18e7645756c4dd7375e3ab7154b9a4dfc64c249d5faa6e285db352b984a3cad7b5cf4eba66a55a96adef92be18a966a5bb74d3d584cab896e5499b9d70bf9732a6a243305f5c6b96f54b0a547ea61bd3fc5219ebb014894da120770ff2108c096688decf61301f086c80c001770aabbb96753f8e0251204f05af052077cf04ad71eeca721aea6302fb86dcdd8b8720d8b8eb208d735748ef70b833a944dafd95fdfa9f742e577550faa9fcb2fc1975cd5220f2b9d0f281cafad98c6ddca4f56fcc0587f7ad3727d94fe92a1ba01ce10708f88008779fe2432fb696eed8aec4347acf1a0dbab8c9abc20f2c70f29e3526e3505090c5e9aafbf1d9ed1cee65da8e5264dde3a62af5e36a9396959fe57118aac4b88a69c6ca9fb24ffe53e885ee6836363637e5d32ff46f5136363637509cb68c5a9b39a08c6bdb6aac6b19db9c8b518fc9582bd578974b5c6652a432d6ed6f2abfbc273e82f2a7989acb923f97abb9cb8ce27ddb676c46cb5bebeb2850c654e858e9eb59fe5d8bcd3e9f24599fa65fe3dba6ba52a0197eda54df34fb5d7176eba75a7fb6e19cab74bf8de24d4d5b6bb4a4ed72d5fac98d2fcdbfab49ffb6819e3c4b9c73b6ea28ad5fdad22c4b8bdb9937db896ba7d5f47537b2daaebdd7da3e46475aded7146f2a85a93eb22dbd91f6a6a3f9776df757ac94bf2b69cddc9984e361d400a27451d89dc27c288551dbb54f140c857d110a1acb2fff2f7d1add55dcd66798b2d2181defeea5e8678cd3b66b6fae5dbec44d4bb67a0475a604a81c42dda066545bdd5f0a446d9546f1be96525a3e982f3487180aa0868271f75a70f758f054f0ce3ccde1ee545b2cda568fe8135ba595b6746f044f51dc7d2c5f0c1d6d954677556756baf589bb3f656d5b5baaff12d37490b6559ab6260cb6b53565da5669b2500b07dcb4b5b4f8a6a395da2e696dd8a4ee4ee5a11610f49f5adb1bf99beefababe99a34fee3fedf33965f5fdda97b4326182d5d21264ccb852a730ac1494131a77ff1a7d6262ca389c530e7ed3678e3e313189c185061e308ef93535eb7971497514e3306ed76ca92dd04c8ad109857c2e359cb1a69feb0e67b159ebef9a1f775e253a6e6c6afa38bb6979ad548c8e7a679cfbbc7b29aacb8cc9cf949626bee593ee72c5d99cb35d6bd66a1836654f310f7b5aa555006480bb931e4a796ac279c8e484dbd8d8dc8c363636373f423a484893b80cbab6e284b01d474de2524808a6737bdf7cc0e82f1487b73e916c6c6c6e64b251976f9a1569772b13c6519576ea5dce2c31ce44da412643132770318be2b5a23796c0506584a2260f9dc8b0883263de00983061019fa6ffbc946f9af68aee9bcadfb5cc6df9122369fd2479425dc9c07dec000519e7acb4e4610740b8fbb8a3d1a7d15b499a3f47f7ad36d729fa1723996645723262020535da4ab754c551bc69fe32637aed896995eaf095da965eaceb3fd191fc9cae26f925ddedfc399c73b467575cc6548c9868dd435633f7595b6ac4040a8a96bfebb559fde5a88fecaf426949e34be1a96be9ad24d575d712df74b5b275b6b5adbf34b10fcdfd4be128131d7127ce5255dca8b5ae340ad31ad02b7ca4175bfab7d9e7dbc6b5bb14dbf8d29aa53fa50f7d6d562a749c295972cea8b67436a3bbde36c59a96678ea263f9f773b97aa3e7d6b586e96c369b7dc61b8a8e5b63933e0d47f579db369bcd9b3e8d9aa6d5515bb31b5f8aa9feee8bc99abd7fdb74f6faa4afa3354b5f9f957c1a9dfd507d79978060b0289f19fddc6ed37adbfaf36f6bdec8fdb51ac6694b6dd74a9135efb77953fdb6fb536f5a930cda379b35a9d661d3ac54db5b141df17d129bf46d95b45bdb6b71f9f797a6190b578765b8fa5bb65fcb642fc3e5a9c5ec828725b744312110438628a614f3843940613f35a5bf335e1f10718ebb2ba1484a13e72a798af95246fe9490f65ef8bce4bc38e2fe49e02ab96f8b532a31d2d23be17e5b3a56303318ac66fe43d1516b4bf749a3d79eb65cb1e05dee44a8e498b3b129af54cd6fbb18cbb63636f8a62b6f2d1f77dad0efa269362a574ab1aeafabcddbb4b5963bf5b6f6da6de96c47abd1ad31d216b6eadd54fddb94121dcb92ea4a6b56a73f8916d1389395de1387c364d54d615aa51b5f3a96bb343fef4c2b423569f9fbcdba2b2d2fd171091de9a8848e48744c4261356c4d13933f4f5e7c295069653e375d6f96becd9b522448487bf1c617e39020a1556fa01d8dfe6d9bf55a9bb555b35631cd8a83aad5cea37de66e3bf749a65962a42f31924e2aa3f69bf6d625632afbccdd25a827d2e67094d2b5f586849b4cf3a4929431d25d2aa38ca84431d94eda27956fef273d796bdef7cf7aa352da1667b1ecfc53ff7497c6a0cf3224d76314121a4537a0982ee1ad31be91b4ac345a4fa09d69c713ef6805dae7111d3d8831c1604d929cf5f3b64d929c545b1caed666651d957c316eaba9d051ef74c6f93fd0549569e3a6376975e7f26fe4894dd36ea81d15cee52795d7e20e76a619d37d13aedc0ff5e6f9a4fba62e11e8c2c4c58d25d3ac485495699c5155a6d99b38e7cf69aaca649ab111cbd3165aa919baba6bb6d18bafc6fd97678ddedf516aa3f8a6fbdc6ee39deddff47d1cddddfae689f74f05fdc5d4acb67e50fedcb9f357f26be69969d2121285a2e2b4b4dfcbc64d3bd37cb25d5b2ad55d7395ab63e56f6db16cdca471bf97ccbcc5767f5b4dfb2ee58dedaa99d336a9c4b84a979bae56dea455bc9fcbc64db17a64bbf6fe94adb27153d393ce4f83884e1470c2c2c90ad1e9004e41884e40388d426140b4e695e194e41fbeaec99f326d4def6b6b5632eb59da29bba54cfa7878728011b35bf29e98fee2268c9f9c3c3756529e54053c046207aba52786942859a2ab4fe512d59757ebc72200bcdf5e5bde4ad3b67cf3dcf771b7caaec56176a65965bfebd49bffa4ddf8caf67d9ceca7acedb3dd52a4952acb2ad32736cfdc99497dcfb26233777e8949d3aca4aee5ef1b79e2299cade50b097959f0c690103b7811820086b20c93c8c8156478f192c6dd573a579c33775f5d40e5f396086854a102549859d02447142d2531a85a491c54dd88415e8460c498c921012bb45c85850e0cee5e1099fb1702980f0423d18a189d472128f2a764e5c3609f3129cb69a95c10ee06d5f29458850cf7efcb0b5e9a0fbc78f075f9b8b8fbce7a678854ae70779dc71c2e7285ea8b4d133b5524291263d9d6cc4195b996b76ea8df780a069371d992a4757512937444241744a4a224aaca34eeee9333faaff3efccfc5398de4a81b0366374acd5b08931d2182445d61cb575a5328e3b73df273fd37aa3d5762d05caddaac36128bc2feec4587e632ea369ebbe4f75b6625e5ff3ac690d54c33be3dbd36af8b18c6535ebcfaee17d1fb7c97a95c68a4dbcabfaa7ccd7b8bfed29bccdbdad492b7ddad20446f3d4a6b9edcfb63e7a8ae21cce4e4e331a4433a64f6633255dbb945b572c331a4461fb7158b775f97febb67fd3edcc52b7b3d570c6a4d604a81a233f7931b9b48fdc99121d69f7a4f71655ab54a540f43fd7be56f5eea528d0d7ea54141def163afef8e830962326282bb8a97cbc9f0beefc8b9b366ec24ddf943f97db62bb387779d24ea5d04b551a853d65797a52a2484b55b260d1e57d9ba938e1b5c0840c77a7957a669830e1f92032a1721f77884ca08ca5a5e9884473e0fcb9dc8fb8484445d21a221094b84844dd475bcee0640cdee2c5c50d606711387c8002359ec64712fb16a21206f82c6a466b67eee24e5d6ad3d6fd49356f2a25b39a6f052a3feed41723e576aff5934acf830ae28fa954774dc2b51b8583192949a2c3d72693ddf6cbf096555d92ffb6aaf5e364b693f6a59192244a5eaa9a34a94a922fd584891299927ad3b29baeb7276f95edd732bc652ffb8bb7a6e1f0d6e1f2b36cd7de94e96afbafc9f096ed9bcdbeae6299b632d9d5e19bae3799b6d9acaff799bbf677ddd56ed3ac3625667d9dc4acab28442053e7f149f9629ee81367b2e2e80cccd47dd99722b39b9cfe9d619f7341bb52b6db7667cc24139150e12fdb6d9b71988a93c9b6ae35b3de8cf74fe1cedfd5f6e697f7d45b5b538aac264de6791f8ffbb883e0e29137968c4a928c4341578733eda6ebcd027b3582149aaba44cf7d71b722f26664085bb5746ce4a337a8f84a399eb538ca340206ac085fb78b1d5a7495ebb6ded4247ba2bdd57d4c0080d78a074a741d5b559adc1cc5dd4c0c8dd450d76dc8dc62441e44f9966c569318359064614c8dd5b2e66e043cc20c7c81cee0e44a94a0ba39534e3702e178d84e114a6716f7ef95f9ad8caae8fcb8fcb1fa5fb262e195f9bee9e99aa32514d475cfea8f14b4caf05c2f94530cd9a7ba99adba273a76cc4fbc7a7bcf8c876edc65c46fd1b4f9176df6ab3365a77b4ba77ddd5f2e223a47147ea237f73a73e127480714675a54a546250b9f33449aaa740e5c5e443d1b1e24ead2bdd99db92f76f2533a6650efa916dfd3459c5b92d74fcd9da874add9a736790a634466177898ee5ffc65c70d3ee5626fd1b73d1612cd509476dc9475b9f4cb92d4f4875d7a4df98cbb54fe2902cc4214cb88f60be50994c1c8254fe99b740fd99a5b4d5a9441282852b45e97ded587e06434eee9b95866daef7494da1e89804a6ab619db18e3ead029d5a5bd95012ba35c6d1bd8792e0bda3d53069f7e36edbe659fe9959f5adb3fb393ca3b7da5d7310de417a2c2d3d62620c4ad9f2a76cfeb2b4d45a4b8fc44cb3e69f52a29d9afe8d02d5dc89b38ddec8aa44c7dc897395af3d0e9354dbfa64cdbafc396cea2fcf7b8e26c63d8fab3629dd436bf8cc659c4d8c73380cde26363176c251da76b1f9473bd33ca9ec6e0c1f4969fbb471d3c64db66b77386ffd49394bdeb8e97e2ecbc64d5a5355a6b7dd7a2bd24f552b1b37e9c7494a493b53eb37f33b55bb7375422abf2c632355652233fe146e5756243a8e74e3a6b734729ce9f09dcd92d00da89e0599f54baa93222b8db630eaba504d9d5edf6cd60ac651e597a749be49de2d4f92a73e731aa8fcfc3977d7f1c8f880eb74eeaee4ee11f1395ac3f4f5e7b28cd9ae2d317995caa5fcef4413738c728af889ffe29c9576db9af5d4a2c3587ed7a412e32a5787ef953769758b8e5155266d2bcf0ea82ad3c576d7a76d71164b69bea50283ed70ed2e5dc0bde8b1637f3ee428815781cf480aa23802538457c3cd8a1e2b7820031e2b42308128f643957fe57629bffc9c549a59ca2f6d75aa59497c69e1ee321955c56da1aa4cf9a576b72ad1f1a7ea0dcc175a67b66b67d91ac95ffba4fb53dbde13d37d62a09d59b37488e20db4a3d15d6ff868fc9a95f23be951876f9ace76eeccb42ab35d4b9ee5e719fd4a9f6649a819f95cca9ffd6c9631ae6adbadfdbe35d7d336bbeddc97b2202a85cec61f97aaf9e54ccbca1ff3dbcac70235a34033aa2fcdba7194ded7c2664034887e99594c858e3b7aed98f1e7988c3f3324549569e6437f664832a632f3a1fabeeeefd03834164d69bbefefcc7a2bb5edda7246e2fbd987eadca969add5309899ee2f0c96df566f6522ef4ed36b75a59d9896b6e66ee489d3959ae6ebea4d3fd151df9ad374ac396ffd379bbdd5fc407a06863e91d9ae7dd9971985697d32d18262f59e3f3e54e34e325bb2ccb8ee68b4ea6ebaceccf2d65b26f363a6a1a1da121d1f97bb7de4d6ac54f3436121fba3675787674d3e74d4d966697496df29e3a81998d9ec4b352dce6a3ae6af2d95745baa976efabef92595324f1ad561d3dcd12ad53847a544c719989a95806810ad59e9cb8ce6ff817dcd4f6756a29c9492645e9eb244412d59998d60661a067bb2d6f2ef2ff9e9583e59f3e7ba2d05ca785b0da5c337a09a9f966fd6fc3b303318cc342bd2eccbce2c7f77ebcedcba62a1637e5cc6545b186c46afc59f31131d731547b59dfdf88c3f3e7af75230d8cc9a399ceeb30dfa4c77401bdf5aed4b4ccb16e86737dbbd946c36f536efcfda666b3fd75c5a3033aafab76dd26ad0c537577137bd3381b22df76b8bc3e4c89cfb795abdb7ea7cae74a16399abed33c5b56b7be84ff9a70fbe14a8b65471b61587c3e4d32ea6e99e1e5abe6956bdff6efda4cd5b7fa6f9c4359d0f35eb8d3e79f3e77ea7ebd3cf47767fdb9a92cf65898e527467d22c494bbdbb313ae2ecc524d5fbd69c1d6fba6ea88d9b8eb2ad4e171fe9cf98093749ddf386244562f36ec14d547054cd4ab52ce58d416ddc74dbb94fdae95b7350b66b97be1e95f5c834ed0d496ba41da2c13ae38d6b29d57f061a7a4655d6569f0175869433a09861821faa2d50f93ef43735a30a3372a035bf19db8c29666c60061133723405d259e3dca5b4fc0d822940d082ebf0a59a4661b319bd5689cb1254931215a5a4b17e2e57631778088271acf9a16a7ed96b58195a5051861265cc8037fda1d7d26b6906e60f0fcb3075191c543347a3902187b364cc408600dc69143214400d05f3e5068b6fc903bed50616a020021068a08a3044f02b7a946002d896d21381980fc75c838b18b450830b2118101ae2bd207073772c4b3bb2da1e8bf6606e029dc7a0a08b4d0fc5f02f314e3ffed24e6d2757e3815b7805b4d8c0e3e113f0882ef00037c3cbc019303ace4330a090dd6ae66460fc70778f7a4b55996a98aa32d1c7d9a8f167b773b8af9bce481a4493cc7ce88c9cd16dbfd27476cae66d5a5aa5e1a7291dc58eea789652a4cd34ac292e631ae776e6d6d5047a9cdd77fc793d9b51bc4fbcf301ba38bf083b6d6f7f6aa1e3c5f4d637cb5b6bdef7b7a53a6c858eb9936e7c81a4347dda8d7cf25a283afee84cc378d3abebf8a3f5a1e553d91ad5554777399cf73531ee326b4a12d434a33b13a87c2dbb78f69a6afb43e5890bad741fb9b8f431cd1cdde55a5e4cbea65abf794ffaa479e6708e3e4ddbfb9f3f476b96ce1e87a16654ffec72cd3887fb283aeeaa14b6fdbeb6cea066411e64e91245c7d9d08ce27dab69d68bf5dfc8faf966a1e3cc4a549526252cb2a82625a62a4a50b2a62c4b51485b9ea2a09ab220c99e94929ab6e44e5ac3d706e52fefd9656b5babe1a02f6bdeb4d297aab88b2d6d22eb7daaad3573b94a475dbea9c3572ae85a4c06557a234f4df56b9ccb676e97abce3f55566d2b694dda99a3fa27e862fadabced97b2a60fd5f6b3aed476b1997fea75f84681b48949bc71ced16bbdc49870f937dd19dfb43eb125ad5971b492f74fb376aded5a9386ab369a712d2a4b138d82dac274920f458bd01109926cda7d4f3a54feced479d7abcbf2c5f01d61e9cc34add48c664c672f55f7ec46e29cd24780717fadea9bae99a4444d4e5ab3f4c95f4cd4e4bc9138f72ba92caa9800cf6c637363535fa5a7d5239d475aa5790165bc56e8d20c6e0850e1ae67d44966bb7647d6d296b792b227ef89efe729d997b25bdf24711859696526c639d9ef3771969517d3eac5b4f24f99aee6f2b6adeeff7f5773b2fd78bfb63299aef965f8ea376935ff35cfa96b65fbb5ace697e9fecacacff96b5fcaca8b699f6fba6e2dcbd5cf52a495d2329969da1b89c3a4ec5a9c6c67e672350246221024022fba7ba9f2a730131721e0228b17c1227151e5eeb66bafc597c2fe9e19c685c94554adbe49f16ee2a2888b0cdc9dee5e6a579aeb9b483a8f19870bb798dac26e11e5f4a6eb4db6efe79aa354e31c85edd7b98a93d9aebd67ce651caeac32fd26794fd97e9dc5e97ea58d33677c60c226c783591a16a248410a6e3e8454d0c3bae361a51ed6261e56260f2b081e56ec612de36125808795080feb023cac1c0022a651e4621a486980e0621a615c4c63bbfb4db86347f8c4ccc327c278f804091e3e0183874f14c0c3278cf0f0890478f8c21a1e3e8185874f7ce1e1136578f884093c3c802706c1710de1808b43d47071c81a1789ec7091088f8b4490b84824c945224f2e1209c14522d8452227b848c4051789e4e02211215c249200178960e1229108ecd0218e61c6c5316470718c1c5c1c430817d30071710c03b83846152e8e81c5cb25fee02efef072f1071e2efed0e3e20f485cfca1c8c51facb8f8c316177fa8fd70baf80309eeae73a4021dae1c20e2ceccc51d9c8b3b542eee14c0c51d225cdca9c2c51d0ab8b8f301177748e0e20e1c2ef270177954e0220f1e17798c2ef280b9c8e38a8b3c40709107ce451e23b8c86306177900e1220f285ce491051031bce262287331a472771d56887cc7ca91ef5861f21d2b5c7cc74ae93b56c4b8bb8ef08b91875fa878f8258bbbf31095d81e2a01000f95b8c143258cf05009273c54e20a0f959880874a6cc04325d2f0500939dcfd95138a3aca7051c7085cdce1b9b843c7c51d405cdca1818b3b9ab8bb053f441cb68b38ace0220e3bb88803142ee2e0007767f9d849dabe93b4f39d24169272707642f0c07742a8be1302ce7742d0f94e0825f84e082af84e0835f84e083cf84e0840f84e0848f84e0850dcd8bc5ca22836b9808b4dca70b189085c6c22878b45372e16edb85854c4c5a220178b602e165d71b10804178b6e2e16e55c2c1ac1c5a2165c2c22808b453fb858a4848b450a70b188022e167dc0c522365c34f27604a8c4a804f06da5999a744c213333321009000000931200304024180bc70312c16c9e86ab0314800372a67290521d8aa32849415219c20c0100000000000020981a02a5295a29c7c8d19282f33e01009b2b0265b1c6599152c7f19b177053c43555c0be14b1bf9c5fad8f1a6dea9b6020874aa802915f6811ad2cc131fe698eb2978a280d50a1ef92c4c70a46efe9525d1ac4e81034e235d5337003df3edf50b9ecdfb7fc52a17fd7f4086af45247e6af2f529cfe8e3149cd0db26bd903b6e488f4226b19e01250a3e9a2110a17132954b2729741f1642cb47251420a7add4522169a8e46a998080f4d3c70dbf53a0f36dd9ed1c8f0df14b9ebe397d2791b889d13924dd5ccfa3023e766edd6e5f676fa397900b4def30b070fc2477525eed178d3873c083c285cff25a8309d66ea8a70a0cda27360f908165e7a25aea728e39205ebc3182a9f66d740cdc774053a8fc169e04bd9730ba7ae045646414bb46fa4aab1c482844d99f544e36a4259f867339763fe05898bb9a20a5658329e25a8d15c75f9d01add8f8653879714a8aad6362c8b67c86edf38147d9b817573d83271a1192d830a836cba0e6fdba0230b64a236790111a66f81a5c10025160bf3b1c71baafe84642c27f24d5aa5b18e4a0d42349f848a78a8c5202d01ccbbc8193300d990b5dc5ef3a30dc7d2f8c94a8fb15806e435a5310fe768b2f226898bc334e1510b7a2d8832e01c3ad6a8577600183ffc3dc1aea0c6f80a6ea843ac6c2739af045a022eb631c4714cee200558fa4f71291f4ddc0f7c327e2b8899b4e1cdb3329b898df6cd3ab645eba7a92d95ec51ede37df57e99b801ed5c31978614f484bdf085cf2f9379c6af1120197c6cb5b66bd50fa4cc28e8bc48e48d19411a1545469298215a4902f6e8280b71a16f6ca743e3034371caff99d6ba142902ea7ea1086f52c049b0fc08ee310b25951c59f3b14f485ccc16fa5ff90fd808deeb3a1a08e59eac455db4ab051a1e400c887887a0bfb39a36a4e01da402a3c3a68f6a2efb681b4cecb44ec43a9ebffccc0595e83e47958328886dd0ee15aacd5d04cdba3deaab3a039a5dc52f0f519ce93515b2dc2513e911cc68f85a50fbc8fbb01215320a1f629f5a82146128b840010e72d8235bf803c66df8c2769a9b6728b807c3c830ccbce0954bc0b7a6c81a5479db1e6b6a1c1ab2054f8ddc9786ef7c608c6aac64feca2dc8299441eac3ec4006dee5b87a887605520c18f44abb9208536d5ff3a004775f8087e46a35398e9c9bd99239b8dd985493b305d7bc1dfc36a84680bb83712ea93ac6e77dc19b02843dc5658d2a48c3656cdee3e633a8652e69e51d218c104c913ba5229253f3b5f86fe348a62ac219d79ee8eb07221073e3671eb8a0e38b5b1be86b644f52ea8aede9db96da275c18687ce2625a8eaca81e37b2c1ea06d007a0eb32feb6e9ee7fa8ca87d8a12286ba76c42793dcb7842e72b0161d36b22fad1f79c893a5cfee9a3e5bdf39de13c96632e5a4dfa92d310fe400b42884e1bf8bc7d66877da31a00477e4e039009220603780f1dde98ee7f890819d0022e88a8ae4203cc01a21c1e19541288e058e01e7a016e78c7dd60600bce0e888096c3a436ff7ebe513b3f3a7cb90bd6b244751329e56b5e45f5930781b959a41239a2796118cb91708a260dafb8158d5bc416e3d8ad1a2e8eb9a4695851d5583db437d32eea0098e4efff1c8538d7b75b7b179ec8909901f5c4bd627463240404c14ac09ebb49dfa20f840094e90038886fcde1a7fcc3dc8ba721a727db6f1d62f32803a81673d13991381179cc736790a9155f57f35e2266486d9f7b1bca38de118a5c8eb9098f3c9de038e6af2da766307d48efae1b6e13f86d6ab2585d86dc3276cc781c28cf08a2ec7501620d10b0e2bd1bda1f7c9ec93531806e46d096a4521a3ef7fb00b084a6549150e5f2463db5db0e6da1a1c2d439b35f1c7b7062be2c5a844adeace61be787c05ebca890a291224cf00fbb807edcb6cb25f99ee38d82d40af621a3611f1685c7b2af18bb111591b4158ef6f0f334cb1e205439a8a88f00483b1421695df54f7fe4cb310dbc651faf56b3c6089113f87bc71e8011b0eb24cfeaa13e71c87319ac45469861e8ef0c66dd44eba1cdf368ef2a4a7fb53fee63260d5f839034786a9388b3d635b10174966d94df2d14686954b5e877c1bc53e2f96db90f8712979933cd632179ffc90b229d0bea9c5472297da974a00976f8de68b1da90c550fa7f98ae401e0ad26dfc0612ffaa83cd97de3432c98f8e8840dacedcd49d146b2de82944d8bb8ba11a581bc28573335f946148b0fd5aa7bb9162d183911dadefa20fdbb894b5578203e7524ca6d1869e0296931f0cce558f1431c54de2ce131f31149e4445db510120612a2a2ff227d3811547ca8f1aa21936d55b39b3ce73b42347802869f2287c36e9bd5e58c0b13e5473b2f08361a2e82357501170d42d7b3ad97a92ccde36342c70b4b442b8d351931650afa7340eb4088a34907604de457bc0bd4e77e585d152591cf9b366dc3dd46ba6b64777e2fdfc3e35e71e1b7cecf0730156c9c5c9ab70412c47c1be40be0da9e5dd53f4e357538d3d62570ada641da09a5f071f27c4aa9ee8f295d61b1aa2025a69a62468270ceb18b6def77924bdd6e97ee68d29cddea60a01cf9f529e77fa3498499ccc9dc7059a0bfa99d54fbb93fd9316266891df4f2429432624cb5530787f41792c433c1f2100fa8165b49d4ae8fe60fde9ed641140538a16078310cd2bfc96f5447273741d92b0eba157fccc94a8c22483acc34d222f5df2324e2dea7fb0367e354e8d76565969d2cb84dc91aee3a58d28ecdba617dddd6fe34f64deb4455192ec92b02fc8d757f90caed2538567840a37ab042fd0f91d221e0c22ee41025aa123f5a0a6703855edf03e4ec821f2a06ad04872b03046f0edc776d0035040448dcc7f29277cbb9ad186731b0ef6556859b9da7b5ae7651af672ec568a241597883a16f9a553d031a5426d87534edd71fc0d8c1de4be2c5630cb4781a1f40c770a345bc2e9d3cd23cc154028cf0f34efb0e84beebae9ff904d60ef88098bb49138caf8d30e15e2e8cbf035939a9ca1c1607bb793ad8efb3221f86c5a0e80f85e9ce1b5b36f69acfcb5890cee7b7043de4cd954f378b5c218a1a5cbd28585f7f7b527d303f92c92a5e2b7ca3496ff9cd2fa7fe91b3db65acdcc4df634e483fd35507818e0848bf0c69dde68d580e176f59aa23de20f1021f2eede580327e2963e955e0b57ee1b1bdf42cd74bba8149927cc17d43a69e12d0ae8ae63b550780397835ddaa317434d1eb604918909ad781231636d1140c1b3cfa67530fe761ef9b2a98ea0cde1c51f1769845824feccf0f9fd54f12871cfd878f0780fbf1d60f4ef606df7e0d4e16a36d6439c62a20a8a5cd7014a828e69af6a1800089a0671460b23438acd3796f99864be1b682eb30f5d50e90922a99a35049934a92238c8dce91cff51de3dfd934b783d8ccd7be13bd319551841047369a7d41671cedac2ff1c6da4b0f8922d4a394d442de5fa35c64cc6f8fcd322245a92cfe6a7b5b7fb10e81ef4e587994876fc98b3a2fb7c02863ec020dcd82ecde9650599cf0ff767e152b40d0b1cfd91b5007911aa8834467ab8048eae88771918c100245fb8d2c3a0e110169673a9b96c3fde2ffb930d2019db13224023e8a27f168e14d59166c8183710ead7229bc7a3e6b3f780111488e81897b0aae6c6116883ffc1192c84e6890a92148352b2c5d35466abe150e5bae7c34ccf91726ca13cb2e3f1e1c13af06f3aa7260736b08ef9fec83988549592c14a872ec98e69e05c11ef3e9564a6d90516ad899bd304b88d8b75a480ccd2e1c7a1cfaddc05a3f5ae3427d6079b01ffe349e67495e128abcf630ba66324532bacd578adb9cd99086dc15994f4d1f79a4bd85eb9359e6b0df118198f55d99d841e54f7fca270b7f48e927acf9a7daf36a5cac2ea0974045d4811f97e21a61c4d30177074876aad26316a95eaa476beaad9678bb64a326752da93c346c878c071edbd15f39636dc7da9621a8a6ca71973297c94afa2e5580bb27f5a3e5794f6be7c5dfa2a1f71477a0c5b8da0461d4183de6800c3724a4f023fbdfd1c70b9b7acee58d28cc2ec9c81996e13ad4e09c771ed94503fbe24b97f30215c7f1cc25315cc01b6d1e7bb2b28b8c6994561c4ba5fa605b35f358a0518b14159140c06934cfe69cbf0b86730630812fa559818e81608a55a02af318528ae8fe3d5c405708c9329bd78c36b00e3a2b45b31c4982513dedd629875a026822fc2e54697ac53b636ddb016519463e1924ba9640b02c1e42eace837c8b3892a7c0200d97d54de3ea9cf1c38d6e8b5695142abdafca574dce53086fdaaeb4bbf67690fe105df6319a1aa8d3acacf51c58a8b279faa6e7688613f822b87717733da453eb9ca6b52861d13699bc35d3a4de762515c1f0098218d6b8c82793f899343cde6823fa06f12a2bafe42185f8897a7b8e0f944cd0e510f3486504eb821cebbc22068640a3160d34c41f0976cbb3e710bf957012a322cf32e4e8ce574d1e4a2a404484d2f3ab355d144a703781422113c92238adfef0f48c356e9017455a77464921212c1cf496a528c66333d3c7804435da3d4b6fd8dc197fdb3b2f43908700d5f619ed33ae395a9925361f82a7df1bd1e8b154139c61cd3e8651d57b2cc48ea71a1411f29084c65ca89fff7661d0d2ad61a6de140e82448ca7997c066fec0eaa7b58387d7108a8c1bfafbf54e8bd1299e2608764891abf95f6eb6f7d5c31bff1a49c216b7ce8aa00773547cb592c57ed96ef8320ea846810ca8a5ef45e64ce028663af4fb2586f22cd354a86750cd9bee1b345ecb213e36ca183b3b538b928a54cddf5484742e46e920ccc42438580f71cb8cc9410e884c1e95191796b0ff55f07024941630d6ed2450a90c5cb8d3dbfc41b8e38126148635e3460901c2e9d2e3dcf22f3029c1b01a6fa8305524dbc8b9f52c3a87ceaeb3ed09a74221ecbd19fcc6e4c21d92855e936a902aa6a724cc817c08e5668ac0c7c10c2bc4278d8e57ab0c28094ba3ac20bb5692f4b7fb35c30aad25a952ebac0fd084cdc29a499d1b74dc2472f23089ea343465a12094566a9ae347fd5f38a78b31b62ad89ead323a884983d1bcd97328fc3d08081662bcbf0c021e7594624140058de19c854f9fc248aa069123e25218884fa4905dc1018f523c8e05c9ccc241f143a17b214d4a7099d7cd4b381c603b9cc94895d58970422c182626dc44197544388cd6b92a9b0612f683084a53b114c91094b2e5dedb992f2bf4de0005bf6deb0f0bdc143b9757f720778c692b474cb985d1022d2d5e7c9b639911bec47134fb36a3160449ff29823dec41d34c035476cc4406363d480de3e12220242b653421a15a5ed521f3d89e95da810e47e87a356a9c0837f19d72c092b2912b33a7bbfef0c6064828675706d9994ab3ae7dc235d9cafb852356300e951c8b662b1719382a78dd900856fb9d6b6a59557f38f7516ad15465e469e3ebb1707c97bfb060e48e71b39e2f5173985b27242d4c12a00d755a50f7336bd665bc125e8a756faede236086949c3d5ec911ea6a0b6dfd16e4fb2e4050686c38764a35275a0506f65a920254c173243675610d5b5e36404ec005e93e43a3482972e500fc9ffe124a0133690f08182e830069ca1310c66bf2dd407e8160034ec5098caab0c84a1297dd45d59d388093e9710b6fef01ec03556c745ebb103c72a805f058bad4de22772743a34a6d4c4d3b8a1116c7d4c06229ff4a5fb9d7e28dae5b4915c2268c0b9e8632a5bbe78913f569325ad9b329b6410a3cac52b8927548bdc64d00652796ede21570f3f2fc5c0cee8c0a2c2a3f90b051a11d7812b1d30b1bf8d544e381978b903f008cdbf3a0d13008bd1d80b0bc40cca00ee27d2fdf56a677b1bb9e757483e09a2df3de737c18286dd7813efba4d9525309b54d313ca9fc01779acf153291250225212a284eb69e5b521a11fa2ac38458338725ba40ba2dd9d7b7ecaf0efedeed42c03007458ca80f6185db70bf1d59b0faf906fd07a821fdddd4189a1cd360cf76fe4d8a46823d16726e2715b68c6c74f826cbec7b893f2ea5f48abf73109c2dd111cedb1816510885154d17d0935d62bde262898d45c5c1265a7bca14ac19d5d1a5ea251592828a246d17f9d90cb99ab4626480cc614a8812004f88ebe8849a830f6c29d754bfa2c732f14f3557f4060c2a4308b93536304d19626656af1622784cf0e78ee96bef16c465465c3d315e9055dfd49497c12f6228da394f56d95bc0f0644260eabdc14bf0a7ea4142b713113541880880405656484c378b00ea9bce23bfe17c4116ac23c0d8be3ae81138d83a533d45f311d0a10f8df7813dd3ddedae798d7243deb938ff57541e3625036effd654d855f8e459b31b83bcd6b08a6f3946899e113c9bd1f119318063906d6e2f0b3ae2bc9246e436073a20b3061e223cba9c51d8d7f7ef76e02e01e14faed7d77e2cc5652496d850520e96f86ccd3df7a03a946416e15e72de9fd83dc605516721de3767af2f9e518067105f4f87bb4572a7bf328fd390f1ed2b3ee51894deddc090188a1bcc2f363b2bb2f3d307254c0d8243056c789c395dcb1562c8b4addb53942970d4da5fae223045644c5fc6dd31697d648f048362059b5da8dfc6f652754ada18af960e30de9ca989bbd83c97d301256b37c92ee23bec863d9a7f9b07e28fc754c189a50bada349e56f95e326d75066dde76b855e16f792ed8c0178aa992cbbf7d0b14494d7e85ee60e9d05615d92c0e4b16f6ec8d9afdf610bf311c0a31f23db028c46296de47355db2653b57562f35a8e905c19c9e126a43a0e07adf810a5d66a12c9623ebec70a1d69a5ece3edab920b6360099a9df2f2088a1dbd2c8e0dcc9d0fd5e5949ad024d0d82c402a506ab8fc4d25290c5f20548818bf6683d12761d566adc07b26a6231f39ec1c5b1e26af7d594b17313101fa188fe718b009677dae0c8d53ae3e04142da5f6d3bd4076c54f3775c792d073cd84da7d7025903c5b908cac3456dc8c4a7c60a16af90159fd6ea02f0744b2bc702f1ea05646683a67648c7981da39354f4d258abc3aaee0abb458e39352c0e449282c57033d189305ab766fe04f79c35e02dba19dfb56fc3e6a75b3315c59430baa303324c0ccd8317482ca5842d23251bcbff6fe5b585b8762c5b906cbd60b16e0ca0ad2a837b7fdd86047b63e1b239fe2267bd4b61eddd6bd0da6d5c830a82d1035d1a30d9fe73cac1120e86f1fe8948764b9e73985de4b542c8ba75b98b275ca4878ca9025d8a3b40299849f41b7f01dd3f456c11fc919721b9c06c0dde4b6d9a22ad7abd2da1e99729bb383bef075e74ee91e41f76695a467f1596ca92900b6d3064edd5d3f33a7805935c02d98ea99efc922966d0d2d7bd6bf87fda18937316d5e5b7c73ef4978c34e4ad41b96fd4beba2507296ead3a778bfef7e1b4e1be6dc856dc3d9102304dfe139a2fa8680e5de887d3cac6418ab65f51f03b3e7c47f57320e3ea2a16d5b660270189cd275fea9d6386a7d5bf491c161bc28ec2f3663446fd0e4601e832257b976a08b631a4df8c9c4e6278c44038ab781e02ab1a43a91bfc2e6760464cea889a51f1970bbc07588a9908a7af5807963306ad4a57cbe6864d5d85e2598b9c8e5223e1ca1a1522f338edd78842ebc824699e8697f1f708252b028dfc4c7165e8a56b0f31da73ab71eb160569f8a5b71d8c101e79754d5d26230d80ab5d7a005ecdfdcca1c203b7f977dac8744901efc6069cb10674d2884baf10111157f76a10c3f623bff044a0d7f271b4d4e12c3590628806a43f3c2a88a87f84e9949369ccdbaf68a879740cf6f38e18b8e5247558f734a9ac52ae94d6fa159bcf5a3d193898c4349298146b8ade3cee785f7691027815cfa0690f7751e856517e1ad27ecb6fc44c4f48ccb1e6fe45b00b556f1f8170afddc15f31bca3cf78a23d8569ec74f8b2022f0700f7a62328a11945163121b79decdc5156ddb05e50e01c3aef189b61d1157386d4049098b5b94f04cb4046038145a6ac3fc1d144bf9877d9da82955461304ef72d9cc75ed5d81013f92461907c8979e11cb2f301250e0d1a05dbd317312b6711f344571ec461a3f7455af61413600390b751107c90c1f1affb88c68d3481071dec063cbca549223b6d34a1631fe1d2ee644cde5af17a06352bfe0fde4d7b2da0e3daf5ee2fa07f196c15974897bff81d61e435d489b27d2af986be6e297564e520c167617d323e94d1c6de1e859962a4b32074a63d8c8046fba8e3eccbde89c8c65a8fce93abf3db8bf2dd82a68f38744cd98c663a6c1e643b47b8ced4e8382f4f9232ef24801e6f0be047252cda4abb5d78bbba4145b2c6dd810fba1ebdd891ac38ecb7441ef62625bc5c86ef723965af821210f2ac7783e20ccff44fc0d60b258be4f3078499040b033918504eccfd647ca4f410a1c7eddc080e3421f4711933928d5af62f489dc09087393bdff1431ae54943b3d88a8882e9b1292ccbc506384b963915fc1610a1ecd41d36504b7cb8a14cd5ddbcedc487fc7600ee88249eea728ac6b8b78fe187aa05e8ed8216f9d107452a1cc0f05c0483074c5ff949301f9fe3c3942b6b80cd55ae5e1c7e18401809a5ce6b1ff47737540aadb96f106f7691e931709ebda44839852ef403659d1583f5390e5cf83280e0be6ee837d6b7566c919c95696ad77aeb91301587bab76b564f3088cea47caa69794445f5c2f74e4d1a034c3661a32ec8270d2d45b7d9bccc53204e57eb8874b932d0c4933535106e0e10593c95e13bbf42342209fe73ada9948470098aedf8ed810e607adfa7a7b37a25a17ec5dacf3fa4525d3f1a029b7ac08b64068cbcded6d7266ee2a550e8df04a1561e3ea2b1d9c0e7f1782f3838bb49441119ed74bfa7702e54a88354999ddd77af9325e073465fd0628a627dc0159a7391da9c322a6b15d802af107069d5ef217338767259adb17310c4cdaf9913d82d395dc7e827fcd0d27c493887247a9cc9a157c373f8ad9d76adfb503c03e44a795f499b823f7ff69b9c479c23a7f11f776436915cf735b96f07b2d63a52d7bc834f581db62c36a43346d7d4218025550935872da447019bc06232b34c1f710005988010cdec2eea65df145025e2c6b55c50b77a467b91901ad36056b203372be10fc682d64d7bc7fef30cfc0c0f9d20dbc14aef366739e3f9677c6e3c00c371de4e70dfff9369a230c530db6293c4fdfabd16895bfd64b44f703e9eefd42f8d3f165140316b529244787d0f11e321e4d840af54b0e2de66df5ed6eeff61b9825894d7dab9e9ef31b2a5b3afdbbd81e9ea7517154725317107ae2b530838061a34f0c546a1325cda3658f00fd7858e0c3a476796434d16f057d1188cc3dcc9b0341b253a19a2e8965e3f06f61ca9d3e8c9ee6c084ff5180536f3052eb38543076843e08fee0d3f62bb2d7bd3baeb27528bda8f39c927b2b2bfffcb9df83c96302ed5e82c4342e42cd9f734b8318aa69be2e9e201d47d78779840c179c8ec37a76d750e09dfe7093674021b4f55b708cff85a11388254d88830d4e6d1e8f63ac43b2610f483ad4bf95309d47e3583825bd10104a1ba3b5ce830e8c5808930ea274284c0941304ab6509c64e90f9aa8d8382d3c3bdc88ea2fd9486e38c93772665d353d2426e32e69778abbe50a342174d4b25ec58bfe550f375ca786e6c8f3d5661a1339ad41c227f8ca18f476bc457257d6ad6b6a9fcc564c6dd50aab81b2962ff5e3d034e1447059d00c4fa5d4936ca004aedec2fa03259317e05daa1e5d04b1f77063a4b314a937c0d04426cd991dc05f68dc37f99f151c6cd4ff7fab42ada690df2332724dc168ce9c5d33b6ef201b358b764b37c51f6ffaf884695caca94f556087d26e82fff1ba2434af893b9052aea6fd1d37f9cc490268535e6064f07b7682dc8f709b5bbab8867e940df8fadd940a8865de41ed839a63290e35dd577dc2a89ac58824e8cb9ce37a64243fe5447b68ec3a56a96202232e77196bee9f3a8db17f7b3854dcb871440b43855bce9d6e1fdc9485f59b2b33f8a8e1f8a8706aba83fc199317b8fbacb1f8b278c6a92558ab7b3c005a71c22fcdc766d71416c50e7e5905a02b4b90ed6350f22cd012007738df16b0d473c31b4676dc1f4a837d15993193be17cded6e673922f043bd2b455ce4807ee8e2ad64cbc45693182239433a83d44a8648120c66247c7a530c23b4c478600878b14c74686193f5382e921ed7c1d5b97d1bda96c1e2e852b5be654df1ff2a3ea0a84efee9c70b20909697a2e2a6ebb2200de5e82e8600b5b39261e9ac22b41706a02b0d371c344ed7e665bcf55dbfba1a78df805f1431059b499fcc9bbe0d835470bcc224458a89bdaefb2592531535f31b61ae10904f533be9890e0995d93590fbb4cf5dad08d3c16cf2a8c5b3eaf51b19e3b66331f55a2321e1158f985955991b794ce182d52a4fc450bf54ef735eb7c7d84f256979b6cb24b527877cc01ed758ae28850e3c514fbd9848f15135ae7feb59470336d008348c7b7f293611f6d3c439060297ae29016f12085057487388e8dc333b500d59a0cff3f2f3cf5ebcd07ab7414cabdf15406dd99e22c629cea505742622f121a21d679a40b559143edd23041c6866d33df1a1460b79853ee318666455ca8abe60c2a606b3a13f3be46d15d59ccbe4db48fbcd19ba517b3e9dec272fa6b200154117274da09a43d9ccc93730626ef9a6a40b89449660c03f8285bd03f16652884116e02b876ed62e183f5789a580763efd60fa8feb4aa6ada391b93c868089b4658c8a795fca8e4df7c20a5ec6bce3e63953df8a0a5b2593690d0f061fb9b0e8a13d4626ba5ce5f5a2a5a120c4e9f601762068c31295ac0c2206e6090f3758f163171fef3f1842c8e48ad21118ed83fca30a53388987fd77bfbf0461ca7be92fe4c77ddd27d3e381a87a8b3f93258be15e43481f43975df687af40b743d6c5e87766cec57a0d946a6f46fe9118f6cdeb68fd1650bc74948325fdb5de23901345b6d7958747c262e79249b88b3c423d5eabf767a3fcd6ed80060c1759707d24323f41fab51405edac29ed4ee103bf90c494c6ea381954a2f0c268f69aa394808472e0f5c64bccdecc7b87e8fc34a6cb81ea685140ace3d0eb36cb8f4692786da468aa84572b06655b1d5d488f4c35c958def19a31f7a3aea1fe5e3376f8238b79656b54a668808c7f043e42d4209f85a9d95029702e5ddc9f5ec38e4b09f8baa002809855e6c21c15883c8da6b95acbb99023ed0276975052022109844d216872252b3bf3a2dbac0b2d46deb08fad4fbde0d980c740147fa22b8dc88c8ac26ea98bae55e6215caad98a84646105982c15b879c83c5b6b6a8faaa4010f0e11244c8125e10aa0d6aa80d2b0c5bed83b554f813bf95ed2e54e6fd845ac6106b32c1921e06274280897936a262bf86f8634b023982f166a00a54fb4c3a23609327e2c02c00021fc19350fed96e111fa48912fb2a250b2f0f6c850fd698ef14c7ee9e764451e50ffdf2e17cf86cb1d4bfd56929639387e1459e3f3e4dcf0c2e0f7af233b0c90ce49f0a3d8b26b8ae43c4e2ddc4e59e3f9f27c759afc68c58956d3172c0967a3413a231430834807f85f734459b8552b33b45096803bcad2a1e04079fff6984667d0581ba0221032500c173da12f621606b564b00e5f5f57fa64cf0ed0c350b1357aacff45c35925f29476bb652b8fb606104c07245fa575a5fd00801a03361673721af9f2a0031cc4510a6488b910d985fe560062a65f8833277ecd0a9baeddec616cc5dd8334fd1778dcf4f9b66e71e69c96510f3cf0ee848dbd73d327092640187220e451df2da4c531a1022848296cedf69fcd50f6bab042bab36c68ddd0843d2612ee7c92fb517ac3b1ab37fb99c57d4bcc02d743d985380921cd4d8b0a78063026c720af59e05192c973d36463a5561db939423df2fc5999e89e863538df06fdd8a42fba346be291ba10b16b35a14ca539db0b4a0317a9527aafc05c147bf80f0f39387bd40b950c6b94e84216867f98e2c59e5b53e8c42141a0eb32016e1cb866a25451a0e33444f390a87babfbbc1784f54092a41685c0e78eb9d8413eacc21ffda28365bab4dc0023aa64e725a35d1698995aab2f268dc0f870650701717151bd860b4f962273e50936f6f2bc733a8091e4583d93da75b194b724b78146057eacac9c35f93df546c7e14bf3dde6464f85859d046faa98a437e8a136e10d0df5b95365939fe61038ca788038162c12053a60223e8cec033d986c11e02b17a56e5ed2d9cd7e6d0b45fefefe86a21af0a9f0e50e7afc8ecbafbf25c53f88264ceb4650790abc7b6a5f7cfdc8ed53643e16586f4125ca3cf7af1c88a9acc19e2951c78d72f2af47020c5b600cd8fac92f8a22cf9a270caadc88d1663a63e3289f304f3dffc26f1ee79af36ee4d6d5a5f05fe6d75ff65473d26386727715ca204da7eecc2453a25101afc33901258cea2a825223368c13db01a4135105899ee2a4a0c0b41b70cf2e4cc974c380401f49c30ea7b270aa0d922db7c72ef27ffd4c8bf2a10c9958422f7cbc2a7b94d0439d6a17157284c1e6725a338d38c1ffa4f6ceaba03c1774dbe7a3e0bc283c9b2e44c94ca1d16f1b7ed85cb1abb280b6bd82efb3020671b682e071e8cff8f38afdc68ed8470712fea2b908eab5433159f53314f28869f8751afb970bee9bb769db23d8f3b06a392e7bf5050adcf841c8ac1f702fe0ffc97e3a8ea77879e991765c2a20d19628dc69f6782c1d23ccd5f75ce1c8d81b4694894d56f30613ede0cb37262ffd109043945b9e6facc2238a5cc47f61ffa5ccd73988fa83629c6cac00cc7fb35728253405745c91a11380e476bd01618540e8443c3301ce825ee418ddcef67f09d70bc24f44b826f41db1c6698cfd4b8a046bea4450f0891565ec3b7efc550d01cbc8f300cd2dd9ce388b43a3fe94b0e85816c4b92f8c33f2b762a8521d962dece5df34e99a3ae686aea1f168d201c65c5cd273962ef517ad83c8cf6ea259c75b28299f3d9e9088847dd365240d0c0734b6bd91231d00b3f5776475ce9265a86ca4c13d747597ccc9dd0da897b14a3c5e46227d697a7ec1597d018b325e8fc93e14d1a2de9f4ff22066d7ebb8318713e3f4fd977f9ffe94b8c5305c8a2014fa309fa3f7e410d48b954e88fcff301ae85e1ba3e855458789d3b197495d069dcbe936324c46a5cf12507e2bebfcb98329c3f8f941bd2d8878f47438c00d5f879db370569e0f13740d3e1d798fde52a9a633af5e5168ffc88013dd2af1d397cc8408672a1ada8905a9d0d8f43412eff5a893fdb6a653d768199ff98020dda889ee9081c043db59e09e789622ac08b99c87d03407fa4bb6de3a7579a5d756014a710d591b516df34501135fa3195bcc6d6b198c86e44f54ba4738af34003c85ea49be5f70bc3f1c95296b6e049b8b88b7650b11665de106ceb6fb48ba77e81f07e0bd69a1a30f4d12278900db6d4cbed0a950addbfed611966407a91e0b7461c88332bb0225c2905ea7d2552b2727ac332268ad8038afd2d9685fa15bc6257f25c9423e3aa1c4b4b05db0f7b122599f2d4aa430957accbbe8a243246c78082da2211fa9073155e6e3e1bbd688058941d131c0e71cca84b24d41a9ce4476a2e290b02bb835e86f4cebe786a56429d71619dee7a0c04930af14e4bc4bf45dbb8b19e0a057e0d17469b566f450afb6dad06ad08cde0dd414c67b49bdd490925af05e1d5c08ca5d518957ce39354cfe2975a2803b6e949bd61ab16cd77db6a76c1579dac5e7a0c17165f7641277bf11b86ed5705bb66f1d53b2f40fbce1f4b3e55f8b0817c7125e4618c93b9469e8609fa82ae220e50c32b4322f0ace8bc800167498234a4633da9a717111f984f0719b405b6fe8a3ea66b181591baf4e6dd4187ee0555fddb0100484252ea8c8711728073c74dd47b45748edd9c0cb6ba028f79cab38d5c97d478745e73ee64b9a52c52ab1657a56c75cd312a9df548e5fb084b7db3585e0f0756415cae17b10c3060eb91889d66d84eb365c658c16d856991a33b2d26cae66a0e1367528297d40c93e77ec10425463c878289141df2661ad374005698e2544870302e0655a32f45007fa69cb3499964240c02658fe3735d4907a8cbfd17daa80e3afd991cad35748cd248ed4a973d3c74e716215833df7f4d130283269a48c9e662669c56e964be558c0d2d7bdddbd4734880e341acc2a2a2fc26f1bff619414ece0b6c247799d31bc6ad8cd7af42013d99eaf4cf7341580c909502559ebeed4463233df5606c6e8e169c3edf2a5410cbb0d13b5ebf02ddb823d7abf05e932e93dbee65c6aebef06db71442b8c1c4b0f1ac6098d32d017a05d3cdd837f7ffb335020fe2db8d0fd4073af82a3e40213b5421f9700fa50f747603a8e5fb7f82da1ed61cdf3d6a57bf1efe4e01f40ced79ba5426ce7ab5bd84aa783ff5623453afbf19028615c9610b09e4813b39bc662dde8d5f5f504cf7cf2b459a7f30c2ac81272904cd1ebe600ab92c0bd12b2883eb7164b4d505b2f27838b1d66398d9a10eeb9bd212736d555abae41bb441885f7a070a6ec6b68a951b37bc7f63eada6ffe696e460ced8d05e2bd6ec8a8186409e63b762c2f236e9e9c2b717e319d33a5c6c172d0e36b2baf0c677b868039bef68558d0ea381006215d09174a586def6f309a5d7d209f3ffe1685938043bcc2ca972ec7d4a1e418e706da36a1dbafdd4cbbc0b187b936af694f44962c38d8ffb3a8386dd50b44ac7dc2c7298cbf2219d94cd18019df32477eeb8f7b62dfe99f268b8616e7e7577db0c83b93f2771025280e86ecdd9ac94e940e99141778a53deeef584b99a7eda0e0def498a4bb9670fe149a97305e9c804055025720ca0821082141bec4589030148c093200d5ccd98e49409ce910b29b1821af64a1d473c42e17fa713576cb7a620ee32b3fdcfe3c3bb38c93090c77370547a5e330f368bcbe8d46e54918085506bd7c6ec03db423a7d60fe0db740796d211ecb51d640aaf1da43a753c7ba4d14666da9bdccee1b4379881776473bd994f762fcb4f211d0af5a3599f657408536ffafe15f666b3a4db544744e70f22fd63f446d3a375a77782aa2fae9c93db612bbf508e0212dc1025fd5b93f5865bdfb7fc48b7193399ce21dc97e1b3e839f39cb72d824337b1c926643e87ce835b1cff22dceb4a87874dc790df12b5bbd334f5a471b3ed70c9d44fc673156b727256c3ee0b961977936f068878fde313fb0e1e54bce3085ce62ab3cb4037bb3d57f51def027acc4c8ec5a2863d1fcededb5b38b2d94c322176edb135fbbc966f13fe2aa3e4eb4497df8684f75affc4765eb386969828de234775d3d9f203c60f81829f41cd66a1f1e523f53c702a5fe2e4d9ca33d022fd2bc09d2a517772cf978f3edb8b107ce37a00cd5fa31b67e7bc7a6a6fc1cf48fed7470b5d70d4c68fe4dde08dd4533bbfbc28bd725c3176663e1319e90792078e6b7b6672cf569f8bcd5603749053dec3b0c60ba9861f38cef523974018f38c52f7d7c3e2fae39151f5eedc5efc1b78916ff651cff13cce3da58b657774e2408ee20a74431bcd75ada8c51236e86e0c89f600d62fc1c0d81e6b983b8147fb5592c3401f0ec6a17be3969ba705785e6890c735dda9791b9d3115ebdbb77e13f13e5cd81345aed5d5d9a75ddf41ee8e17c48f9ef3c7e98c8ea57e24983283b868b6d44683ca8bc3bb347f4f6964887dbaf1aad183f2038ea4f6972ef2db371a470c8f40700ba284ed93dc75b7541c299694cdc976beab9b422a9b147a1cdeb716a26bead34ffaa7655379532637ce0aa6ef5905f9a5b229947a10c60d5efe6b51c0a5913cd40376036200da732f84f50952d1b507adbbfa489f58d49275e084cc165a62d3e3f7753a1dd2baab16536116cf8d9a73be3957498c471f407656b3e80a730a3ce280b2fe5f88242cf636e176dab073e0fae4e3e0eff8cc41b097b8225706a2ed68b355626c1c26f2f16bee2dd8f55080fdbfcab60d43d0a5683984e2d05eb88a773ec27d18bdaf3606f1d6eb9bfaf4ed973b40ce05236283cc90e50c98f0f94ce62675f0bef5d522441e03c0633a3e59aa681778b8c14672e7714886313a55ffd32dd99f9b9e7155d99a052b48b7441117581af151929d5c380ae0fa916596c716889514e6925ca2c2d739df4d4d8186d4daefef2d5add409eb980ff670166d286ffbcedff087ef85a2971f3bbf96ba315f9d50bac0c7fee089107e0cfa54abcfd8bd2092baff6ed93b621204b8edf9f3b39adde28cfca3bdcf40b2d06de1b1efe726a4d878171acd11f96c5f4d46ab9da20ec2040fc42a6d3d8ed1a7fe9821dedf2929e3c10f5590510699a2328a046d545c71d1b8aa9a953c046a8bca0115c3daf4707caaaede9c552515ecfb40e7690a7bcd10d70b435545248b87b957a74d8c4f1320c7bfdba0453ee320ad832b2a9b3d9da7215b584ad709469a89ad272663efb4b54c9b1ede8cd8977e7d9e3f581caac677e4189f441ea59f96bea003f8f7a5b22838e38786d7fab8b2a40c8ea29801637f7915ee6731535eee47acf4a88646274d3280edbb8e27706b9c92550e848662ec0daff0b1c5b274765223d376061f2328c817b6bc51c1115e809ed41e1edcd1e63c2c2a14dade148688a15b881ce83df9b4ab7d0149a3c8e2305f02fa4f8ef2e1b5492a8f97a0f82728a0c7bbf0cbfa8dcb4882658b950fda246fd78f77bbe7f1e626492f60ab6da1a02192bd4c814121cdf169af1247bcb0e4eb57280993ffad34213398eef69e547ce6a5fbb643fc658b618eabf3898b37d7a729168068c786049b9ababbbd4d964eb22adc3db0c78c514eb6360402687ac02ad42644b14d07da89f1d07f101f3a42ef5f5e02025ee41e4bbf0031ef283323d2257b3ef8eb6ab03c2eac79a2387126d5b9f4b1c8448131c169cbf3d9f814e5881991d6981750d7dbb5df57ed15da96d5b333a8a1f44d56b0b4325556c44f56111a95e193b7c713f21de7bf791cd82f09e0118b0d22132ff9a6ab406a27c82afc57678c33f41a36c019c952e4a8e0696a769bc0416667b7a839d7501864fa622639e645c764a1b9609f371cb2e18e59fbcbfe1f94b2c1dd66e62038a4188247a2d3d999fc99414b307c83d8121a80364597fb125187484e676fd793e69f9b9f93b480353b8335d7f7d8d3af8319e5ec694568e83799b2ffa553f2b8dd2ba1606a53bbab07a16b2d99431355320e81004f18c73d851504a19764c0fb9e00d860ef5ba38051f6cf9c5e3a9155b5e5fd240463cec544eeaa23f5a3b033faf9c05f2efe42f78ca17c3d63ee769c74b0dbcc8cbb10b199d4b250dcb15e1a6aa0f0772da15690f08097ef239b7492d8a1f698debb94e03cdb3c28369afa037149f8ef4bbba748bf3c47542a97cc6bee1f9182dc959e4a1d1f6cad492c8ea5a8509a0360984b01ccdd6650f8e56328c6b3ffc0c0da123e321fa79e017fd1ed75d84c446df576aa241e70dfaf73dcf8708e62bfb84ae18661daad9d9689e9c2aaf5c341b8a861072cd961f8024576615767c78f70bc63139d4f19f86e7adc0ec0c2339821441b74b9af4de5c1a93056140e5c3bac990e692a75b941b36a33404a069ef2a30db953041beed4c08f4da8046696574d7e16ec20792e87c885034ebc1c272bf7f70b5b3dbf0b6f4aec9b742f9ff569765300c9489192f01a9236798e51abeac7c5cc7aaccf4cfd142ccfdfb37ee800330ab03fe8e5df92b2c2ebf768a463040f8d234759f75f0f96b6555e161be384229cd65450961bcbc5f3ba0196283b1a2b7bc33312d396ef2466b515fdd3ae713bc7a5ea1a7b3f7e8657112109210fec788e5fd705799f8fa0b286c401ac754392d14e3eada14edf87ec881d5fc1e90e3b097a272a56001773c1b111003232aa31d44491e80d1424dc182998d79c3387b1d98e63fb804d8ac59a41b97261b2ca373ca784ab9186e9ac0cb6cffef6e283384d810e68afa646a1d528ded0da549e686940755a125b2f6488ef2a3a1d16628a31ba48a9d2788c519956598e695a983fc583011f136f64794719e7e11407e40d0c7362048550f8f2c49e4130373a2016ebda8eade3804f73c874fcd4d53a686ac7e2545c803340477034d120ef7b369b245221e924194d7f587bf62db6767f45df9613ff3363510db929f8aad83befc91e50335bb13dc8c3bc3baed84e10eed5e8d20c8d099ea33e7a79819e237ea46cb6c090cb120e8c62a59a9bafc45c69843d3f0e7cd6e22779455f2fef10f2a367abc72631e57084d20956a8217c0e0a97792105d830b85038883b0ed2d4886650afa0582e16b677c4267ba46bc3571ed7491a2ccb28621892af552c4425ad933d443e878ceaa7d1b70e16e0ddb0c78c10c63602d804ca208e3b348a987e44d36aae5f02c0a4a505ee5ca9d8c8bd5c631ba0ef37f4383aac774b6031d0fc0baac33b83e95b1854fdd261e0c45cf297ad9c931e4c04100a25b4289dc5959fd48ccbd4f426e0f8f7abbe16d32399f0a1c6325d3b6cda2ce53c73bb7f641542b0ef8bd7cbcb39c4c81831c987d7ab380b40e91813c83c561f098c6fcf7f0e97614476c763f0950a7448324e155e76d4c6f4b685583befd23575fb8684434e26c0edc04eedbfe6a8bf2e9a3b1307f29230005559742689b1aa1d6414d5b422e0c86332a9c5a857c6f1a8c5b911c85018d6b36ec6b5c83aa75f9a90c6c7c025e88d7081adceca07ab1613cb4611869e1d622f3b33d0a5d166fc4c3b4f7b42ec46b76c1e34305359f33268d43b3db5ff2800889d8f43b90380b22f19c07c7c38f199d859f41e470d4501c5e29c51dc9153b76964a2cf7c054d360e472260a363f5dc859ef68eddd7e9ec5c1cb127396a517be0b18370ee509558817a622035347efdb1935ea02cb9e654e1c1ecb08eafa78e2287c60c9f4a946be43f7d14adf8c303315983a86d7f4e4c1588a48064eb030cc05c2b66eaa3af6aa51b8f32e92f2e6b5fc55a2ead9fa72138e2c547cc18ab96ddf1794fd0bfe13306123fe54476b906d5423cd05f0dd72b90841afaa149861801cd2cfc0895d1bf2d0a2e65453a2d2ced8682e78e3d8a76255da354846227be9eb18a98b346ad7266fad0b70c9a1f7cca00484c79cbc383ae39c3c4aa1ea7992ae0e40b0c94718b95171c0c9c00273eaa24f81aa11a341e6210048bcaccb07b8c73b735ee8dbd0796706df803e2a5bb8b825991cc2dd4da543c110b6ddce16dcd2b81411f4f1a3b5f5dd1e5f7e8613e14dccdb32c215230d72a949b41e5eed74c1e2f330bc812cdb81211a5bc16b02d97a547bb3d5dc3b1521f1c308cc071144d6d6195c460c0080c54f561fdf70a5607f8a7090edab11e2dbdc60dad6d8ba940bd975438dbc1b6c0b595d1538ebae52b3860f569ea786b75cb7e4067754b2479d1ccbf9886dc2da6cfa6080d14efa3dd4a952d6fef83643ec807b383a9d65540e7f0252f8456e1095527ce440565152fdc2bae1bf1da201a4d2905021e33ac0692e300ce5acdc4cd316cf9296506fc0c3d1d9d1d0c9f936d9c0bbe7515309ce20411ef4ab77dfad988c38d96c74b1f0efab33fcabf7eba317c929b71704171e13a108714b4f45e09cc02406f961697747667d5db00adf870a115ad4366c96bd72bd009f6a1a194d205efe8adabda68d96a526d1d242856f28d6ba70b9fb7142f2a15ca1201ba572d6f61da6836c2071535accc9ffbbff37b71e5ee5e5edee8c941ec8482ce04f4c7fdafab9978fd0763d32888e9803641e18e78dafc0f89791dd08f4764140ddd6cf2855cd408c6dbbff117ecd2e82c05444805e57f703ac8222720321cbb4743efe56a76ccecfc226e4b287f75a0e58209f974a5da6a4e5d0eac8c74957dac09960ebb102b2b05ed6738adb4c24c14b24ecc2da436df960103bdc5d2d39e5d032fe162d205b7761cd49e966b83118f74bbf010cef8286790db3396ee24ade17720f2e2670b2083e1a069941b48de58a1872ab0116929b5bf1fcc9cbdfcb1d61f22a0634d91f48c056184f9c99a92d47c840469d32dca7c0bae9572c9d082604c761f0af30c1a45689de4dd4482653162324454e0d226407e1b8ec08d57ea9abe7eb3af3d5768c1967e091af01da1edb98c8ee581a6a2d3434328b5aecb9c66bff89b60d988ff9fbc9488e514e828fa6fcf1cf219f3d49ccf2805c09be8fe3a3da368f5319d4d16725733f812ee8b187621449b8a1ddacd7039d180022692aa95ada864c78994f97fc7220eb1ee1c762dc9e4910859c5d5fe7e133bab771740a063f9f71ac03d32c138b13d2872d569b58393b6cc5734201ca4a6820739df66ff041944444f4bee59ac051a33610f49f9ec1e857b61eea2ef39eb73425aaa73a69ff871be0d795f9efa9d52b7a8d40dd1cb2609f1e3b718c531f416de2d142ebede5edeedf53586e907aef3a54b333579c6b677ee1f6ea111aa616ac05d6892c9234c4f348de55abe4633f1ffd9fe1b55b59599d2f0eadef785cb0d419779b2e652d1ed6bdef3a9d57e2f4b98672ad88a3583a936e38580fc2dcd0ef8311f79ebf607c8d611d9554674dad6c27e3c05d8d318a1ce1f73188d9d327b10fce1a709c2368b6e1049d0bed51e2913dbc2de7d348a777449a5256cd1158d40ffbdbacd222073591f572df5f1ef1239254b17e50f0b7b0e88fef73a091f732733cc95d69d319241a70422e1a6525239ec872f0bf670b7337f5f785c8efad9636a666cc0046902eae8eb0e95d6e40f2c023d5a405726a77d1a7ad3ee9d6344ab4b48c2bd441c1b83410bcbf7da3a89fe3a26bdb079c9199a4067c9616c411af9cd97309b7122ef1e1fc7590268bda4837bff7b4e4d9a403b0cf78bd09ea63d5ae74df5954ea57d6d403cde7415d33cf365f921354f7243645b9de900480f177f983df3e6949b79668679552d1870c5968ab79bf751b1337c6eae8089ba8262367c4867d8ebcbfc3dc470f673408da133b67ea51bb93ea11abccd5f9e7735312454f604c2a692578ad619e8ba324dc5e849a3c8ab93c8466f0ff22caf67bb8b03f743af0e5830747c67be40c9c28ea584d52f69a869dc0dc14516a9a2b679244b5b70c1f186443f3257b6d146be78958decf68aa1b9d2de88bc908746c2d65a31a02dfa4ce02af64e38a295d83ecc49834e2f22df173c107213cd41f95e3e0453a6d536599b52f11941ef547b181e6136390ea8e8d4824321dac7fe143211af79e4903846767ee2f2d6a78433fb2af5ae29c8c930d2f16616d569d7824dbec08bb7108e9c621d364af3dd3f56bc7afaa03ecc232c0371532dc033fdda996a5d51069875623798dc94b4273ce6192e3a60bedbb3c0c7f2d487656edde1a2cad5a8ff7d34b27dfc579c8de7448bd2621ee1e47ac6d82d6b8eb90576477c209541c4f6067e7a0aafc1ac90035032d82dafe55f29264b4863ca5ccfd383357acc6b9a4c69792b4e14e30aa08f03854509e9040e5b1fc12e42ac6fa66c32324352195138f0880d86dd83d12b0852c75b5249b91bc339a5ed86f98330d064e11b449be44439a0529b556b76ad7c3405388971cbb083eeadd1b07b67c9fc660e852260e7c6bb686a2c095cd023a630a5bcf38a51cc86ac7920d3adbaf9c99a44b61033fb25e53366c67f1856aa6bd5c574c1b55f43f0df311df44b9e567a0350bc32fd8699f1ec5d2fc03600a2dce51917278f94f71e429fa273662ab5fc250af96b6854d99075458e225c1cc0d2fd5435e2126fca7aa6ba7ebf8e1a74f628ef71b98487bc11d4a3810492a14f09a113e6d191630d1eb4d44ba4d8380844e851e4881f208d21176c3d91509148958c6b5187b140afe3a8ce1380ad66c40f17b1b82a876c7d71cccf2b04b588de6fda296e84a71bad0c228d08ddd778a89bf075ed3326795343d7e0c046479c779d8013d217e559709f2d51a02024be9d861ef1bb072e973c62274c02aa6b6a49502537e90913c0e17c504fd6e6a586209b0452e6fb48ba97ff8cb162aade0f0d8542de017ad79d37979ca42e03c8ae5bc556faf2510661216cd6338e474481656d039c04d48eab98a09f7ab2c479718bbb6624236832defbe44cc122f5c36c1a178c1245b09c7bbe06ac468f18b085fd9fe21ab917d72c92f31e4a5be48bd2490750084eb4ed06a9a9d07ec6bcb4e34a903f013e7f429f25f71451659edd2f975ad0de32e05ae3364bc0979a6aafcba2692a36c79ea44041ec40d0059d5cf73466e174610de40335c9ad48167a0f7512d97a2b6cb335ee992d83b639c01d9a8fb01a9cb9598629172829f8989f70a365bddbb97d44aa990c5947a9ac07e671eefe3c8682017850d75f460afda209e387084ad8e11ed5cac5ec3eea25bd24443ae1869b030d6f41a6ff573048a19f45c463c7d122ec16bd7e32631af79e16359464da9380e800373dd4e5e844a5634bfa129e015663540ddfd8b4da3c5b9ca7f01131e151dba39a208cc56adad9cc70182205ceb56b9046ace1e2d6a244d548cfac5aece3a0077990b9915d3778b7f2441380f9509fcd89f0af3dfe54d44d05a31198716265637d75b4c5088be49930b08333469253f64dc15ccb9b995e15433d0087eb869b9e8d4dedd2bde5d1bb4eecea9b20c740f8c190f076dfafea718a3667afd9d8d9e2b2ce668ff6d346685a307d44584684982ba84cc3e8a213e005922b7777c50d8d3720fcc5649d9219d065e98d212ed5e989d10f96a7aa5921a5ca3f362c30c89d805e81f96922686377e4bcc5435b82ca75f3587758440af27687a408a0b6a15625424a9ed21c1ef58c7162d7a703bf5e36c061111adfee4c099351537b5d2a355cf4be00cb17832a03893de4543931d1f6d3ebcef84ef61f5f609ff7fb9f85d2793a787df83604b88b2ff7c6ad2f6598abd4d39596271b930005e23f602d042e4fdfbcef9174fe0c2b061f35472db3e97f10ab09a1f4dc3565231b48d39e51e002e5cd644a08e18db7a3be901b6f52571dfea13e6c7cfe179b52f8ef6423e3919905bd5e04daf08827c9bd13f6e1488cd0a04cfcd850443d5389c4ba251a3c06365e48cdc1ea5c2af27d262eab393745d1592c211b8dac952a930c3a2e24707537033da6f8d6c2e8fb07f3b0b0e6caf8aea09c8a105a8b99c2575ad3a1a4d7667e7344c1e6bc1d247c65f8e9770fe0c7a9951b645167c9cd1604abce2fcfb9b30dc15cd9993d5ece2203dc7a0a8666ce3dfe87c2ac8a6fa67a684e012f0a9bcc5ff25cb334f4fa9a76ad3514ff5114664557a32bc55643851f21ef644b0992d4f28e533b3ca43f8758cb88ea7f989d96dd91b965ef639ba60f14b892ab6de99bfba5c437dee44a6b5c485be7cc82d4a8ca08bb0de5c7987122a566589bc661e62792fdfc168c0fcbdacee2add9789f2e5ebc0ae92d0a235ecc7873ce1b40bcd74d1c3b6d5598aa0ed5b5f5007e293e395f2c2921a6e7f446a836155e4cafd075b2f3cfc60ea07893ef8eb40b9d59309b0601e3df57c2909340ce8089e9ed2325deffa7b310cab43ece4e4d0d27c773c10075228ab59e7f8182965abd0dcb6b652050b2d365e7b1d05f649b19ce7b62f47ff95a31d052ad4ee397b2cca64e292c6cb37dacb1aeb0f547ef44c998b4b1d7980441e01b7446e164fc643aee74afe9a6c5a8e69a0a34f1829750167641f1749da69f44809109a2a4c5d0312929049f38f9929fb7191ad69eb2da3e357757973d86362bc6c9511a347ce88106bce57e35c4dfe9a33d5d76a1a6d7cb74766356456b7a15a66b7d2e483647cb594768c48402704e09206985cd644a3e61a51bdf1c1f451a55910932e37e1d11e7e3d60929d96a29f93c7c382870d29deaf4666f9c6498becb1dca7a0f03a63998377b82d587aead568928700b483b71483f53a2543c1eed3b9b4bafaf2d45e10ff99da3943aa77ee5c2e1a84a6f0578654ed576171308d00096ab25c93f244ba3104662f94298bcb6a7fc1bbd7f3bcac219b574146ef64bcf0cd1feac4b74bc276102781f62ca04eb2c6d085012e0d06a2c2412ab1be32698f304d73b0a533c64920d8b33530b65e16fce14b4d615f1bfb236fab226c9c90b9448490db19bfe5d529963885ff0938a175e112d72c6c4327cef3f44702cdd7f83ba63ddea8565b001d6a23fe51eef3471f828f5568faed064ab3e2390c86882b5a6ee8782f0ec6c37d98acf968e5bd613da46c36bbcf9f9f0707590a19c28a509b27a8bfbdfb730032a64bbba14471ebea2c398a506612b785922597ee76139034cee2ff3e74a4d1eeb471cdd7f43068bad806a26c8d86412362793c7823e1e500fc4495399b0011a6988eb76f3be8047a849951eb253ab9bc2767db88e5cad1100b1fe331b68968198a2e033211de0c804009f95c409a4e13210a30cf12d725bae62b6f6ca513b8e7d9ca8eb0cb9b646bb178847fbd01b44b4fe1e6b15b0ab7c2962874d5fdf70a455f03d2e4454163e89b20e6a1d8033870e1e177cc6d856457c3624dd76227bf6b5d4f0c2243dadbc11235dd58a95a272903af1ee6671b636d8f47e90bc36d5c6e070d9067e7d81d504f566d040c3f193aee5b48195fc5910ecc4926d1ebb121c2d451d3e99a2386a69104bb5c15171ddad5caf8e09d7a051b33c1db342cc4ec3d81e59ff6f19983a44ca609a48d86a053d635ea49fd90b0bbe55be786c28205cbdaee73e6ed957f8e93b8f2a9209555609d3346a1b044750221bf76b3d1c10504ab8aabb28c0aeb89fbacce4dbc40afc27e0b3b9e86a3913cbcc9bdd343045e0ce24e72c6f52b6852683fd9a61533e5994c107ca65a0d81d4ee5322c0f7168e4c2b32eba8a7fd783eec9ef68d7f20950595988bf1f857bbd3e184aca1ee2e7f5f71fd30a1075e59ef1103345a8835806dfc6a8a246dd79b7551c26a9b16a7628dc65ea8c930abfc7a7cf04f1e475664e1373cfe6ca0396d4c750d114081f22d33d7981eb536e60ed2d3ea5fa72cd046c5fb110fddfb14466f0d8c35b2658eb69742505e7d1d4161f1d4c1b9af6e373fc984baa35f81392bbfb56c201ae31e2b6fe2a22680802d51d9ef98e7ea830eefa07667879bc93921d4bf4f117fe0d328d1586ad150b58685eaaf61e704a2fc6ec854d19ac27a9d236d88a1fc310c23255187c88a5e2f7684a02098fa671d68a592e2096466c5b87191efc3163193123feeaaac57b4fbb7d78352b01f5ddf29563946a45c325c74c70e2af45167619af91d10088f0699695decdea4b41d43be14e0b0fc95c150e122d56dcb90ced5efa1ec926bcb5eb55287c855f215bf7dd0159a7cfffab8b8f897ecef49764928f3792c5e100c8a3fa3044361276a2b987f57624a509b2a119c12a678d25791101d182af43f1d44749055116496fd0ad78598cfd78bb185d0a5b02fa302f3559a5f952b9ca6e639da088792470e04ce9d76c431d94f0633b7c9893c3a72e01289b8a7573e60cec814bd2e2a553eb1728a9f629cc9324f93f7e440a11a39a437bf3ed1ed4ae684904cdc4fa3546e583adcd55d48a8d0ad228e4eca534dfa0cfe35dddc7b817f9a01d9da79ea45cc49915a2935d6226b7e0b99417c55e29c070eca6c394f3994026fdffe5382a05ef7e348dfa260c790b9549d5ee560c7a83c1ef7f413a46885df2a15f14d81d83d4fa51c709d5ddd62a34b6a8d531e04c48920e525e98875cf188e8c800876a3186d081472c3e3c5180eb3f8b1dfe1b2bd18f271262211601f7b0a7ed0620c5dfdfcb981ba7a33b8b10af6995bf36b19889b9938a460659e2454ab63e1e0d423ad0d984d4d3c0874aba21623d301d12f606fc3296c427dd4aa1bd6dce71c0f8b31b1c2be774382b8db3f518ffa883b97da0dadd6dfee0a647ffaf1e89515ca8a6adeee729e35a3a3b43cbd4826397b4b7b627e41ca18eede322eada538090e4b924104dfa67909d1a4d4941ac4448d911e21c6068c8d094fe3359bc8a26ed66962bf618c6c93f9177d5f93527789b89ba3d68a9e1b467d3616e26af40dcf7e8e71e6fd1b88aa1340ebea1c0e8ed4400f13904be8e90cb4af01bb7be6b8843da9f878948cd705f2db20ff131ed1cfa4695d6352b78a2d9bee47e45f19a30cbfeeb561853cbb0ccd307a5c95c9824001f9e74ca4cbe5ea9448f3ad32cba1087603c3627ec1c11ce2d2a8692240d650735dd67199fcf92bfac80b38d535d4c40063632bb63ce185a3c5aa992b7a28b761f0683010d3a3c058a07d8dfcdf7e9714c9c639cfb4de8a8ee7bf4efff8c5fa8ded4d8d5bf9e00efe6edc2ffc87b886a071b6e65465192361c76ef37897f7eeb2d13fc3ed6fb02bf2b9b7dc973d4a5239cef90bf5e60fbf5d6fe601963d3d39fc92451d5fccfe61d8873bb2ec8e386d2752f7d3d64f3f01b90f6a8b13ec9fdef7842f7d7efeabb2e6dc7a2279860fb434b71fb32eb73f0f199ed35e4832a03f1409b38eb0b76df9f885aa7d741f777b24feac604b4dee56aa9412461c5784182c78129ade4459596463f7f53eac7e8eb8b15c2747a3dd1a848c6a5ce7974a54d6ded7b739ada83925ab0a1c7cf2cd2a1871513c77fc80f8ac1c8b12ee425bbbeaad522e8670902a42a597f04db17ce3394e0a1423ec869dc9de9f790da539d84cdf51c4b98c5803a02c6141d1b42c2fd896e4cdc9b9fc2854f5d67da69304f70bc3c4dbafa5354a0b896c60de031a0fceb8d8c1c2bebb7ebf1feb0d549dcaac1ed75ab7e4e6ca0842ba63a95fb17ec222dc836f37171981b6b15f6680a67bbead684b46ab654d35843f036b0a51c24a73d0ad34104b473c59cd7f6083cf94079024b5f18d50b9515dcfb63b4440189659718bd1e288348ecc7df7d80a9e8f65a8611049fd9d8ce67e831283a524fa8893c9d0d907c05fb8ea777c015a1b707008dc17fd1c2646fe9e85df960eb1e8c00e6dec6603ad15190860e1f1cf1f61a332f2df442b7a48e59fb5914ac307fcccd938ab4eceafd708bf0fde6d8017ac1d2e00cdc3c240978173f357d0f80847edc43d1498c1368e8251fe4ad885606a0078415e17dd3dd3ffd23d1385c5a02fea1688941bd1dd7e547001ae785bed2b7d02b117b4b665ce3306a37af2017009b41e56ea7b23e8a7c0f8ec0766a690f0d8dfb105156ef8042413af70ae21d1e75bbc6cf8df1017a144d736241c745774935344ee98b7c964c2e522ef4d2f71f7560704a505b14fb90e5b1fabe63b43a17c3c242dd49c46135d9cc8e1e118d401b64410fdb89d04b851870846e68db8351733ae52d52bac06fdb52efc13c8ff37187a755dabfc3018f6efc4fa1fd06aaf544f0f2333083af3dc6f335a957824187d3988ba57e605e43f68deca71724add64be8cfb852099ef7382343ffc9fd367e881f3b0aaf60f7c4d7d5c7b38f687be5027a7d675b827e4aab8856986bf77858fb039a83dc9cc4b10d281a01b026aa81cfd5ba674039c9cc6301ea3a7f32a4f28bfb059bc530d5ffaf4192a0a9eb0fd1137380a195ee1f3ffe308843862a6bf97e4a071f00791d7e7ad63f83ec2b22e3ddf41437fe96f1f32068481d3108e6b47849641eb7dc8e969a596fa374e1919517129d9d0d6d78056508c39e0df6b9eeb2982c2d98033193aeea3ebb47d7ca0663caddb4dcbd63fa8f6fe4e64427bf2ffdafdf4b89bc2625f4e9fb31e01e894321a337e3cd977aa1dc8fc5fe3fba2d504dff188d77e898ab92cf566377ff837fb106b8bf986c903053ec44f263acd65661423d30c33821061f32e594110ff80588f33315b06f6bb1893c25fc0a031cd0cbd3370f532c5572e06dd70ce7f3e9a1f0595c6ec10f5de3b2de433dd9c99e5cf1c6ee3762f30dca011f14be2629f56322724fb5f885e3785914a1bcbd017bef734471916ea0fc99cf8fe21fd6120ba7fcc1466ed1d7d570ae0e4e5dbc7602331de15d9ecdc821d65d79da065a8744cd47f37eec88287f9a1f3e8db1fcc7c88913afafdc66298b6f126fd1bee2fc3b0735baaaba2a324d607ba179b396e830e238d8d884e8d7bc018e1d731170dd447bca53a2b5a7bbaa8fbcee62bfaac97eaa96156d67395cd5140e54c2d2a2af27a7e2441758528d65e5ec7de319fcdab11ed2500de72962e81ea3d297683045dfcd54856393162bd9fd2a25313003e10d822011f3d30df0994a752c692c6233f60b2c8b3c0529bfcc878685bdfd1aeb6028971a0b16b86dfd40f6f34943cb19d1230e2ee8be4b346748377b4dab8de3a2456c22e06e0c0c72a54207fb0896fd02538d305e53d20b0ec8b7c8d506cfbace3c43a778f9e60b076ee3f6769cb070ddb077bce84044b24d69a8bdfd91738f48b62d1cdac43f20f7f56ec55794515eed0367dbac7d45f2afb47c04491455d41c5be1b78c410bdf11d4ad2a53b4c854f9b111babb072697727aa0ce0fb907f1d500f8436b57fae4511fb63eb9f84bb327cbdc939476cc577c11e6b4fb6c02f5c122e541003e2feef34b763424b3f66b9600dccd931e38d2475b0edb929f1288515b644abc3327e2136944d835814498caf7553c3f724d4c102922a772db3536562766d0b06b8453ad78d139d426ea5f882d11d24bdd728e1432ed77c77f3fd55b740c29187dd6f2a12d94a1a9f096f5cdbd00d754f24a23efa621338ff70c9aca84e4c8673e6c20c630d3e315c223fdf15ec03d1968a67b7593d1fd6676c5b586a40352270eef173badbc307a6be314255c84414134650469a960f0978dd09b711ad5d1dfedadfe916503e9c7d2b31b170012a7192916e54d541ac5ca57a4571c5aecdb0894084d0b95fd4673c630f7b5d55681174526928b8a9159fecd90c6d8e50c5f2285c1a10de52e5a3b8d273e075bdf8eeaceb75da6ad8120a90e252d9880271d9b61469d08c8fa02b4ce64a3379c1bbfe1fba817488ae9373e425e0f1974a9147efa9ead2d4105662eda3737edea4c9a63d17a8e567aac50897a0ecd72906c28dfc2e41a5df5dd61c17d7f6a60604fad402c8cab492d26f551585c095ce39c17e0fc598a3140f34aaeb6e486b2ac5d458a2f1cd1c54b503d34623dee4b93c9dc02fcfbab72f621cb0e90256ad537da4a4afdb38ef218922723d1551b8bd581027ac79c5578695975b096d0200d48b0ff1aa6548badd57fadbc6b60648a4f8f4ac2699d98343e7dd3d0632c2a0cf7a6b3813a0e21e8eccab9cda6a91dc71c3c42f3c2f564d9a998b107b93211ba01ef7abe7bd8e991bb15daa16745427db628ce4cb393599bde748c1731260609985c0e1b3dfa512a42f051a7510dc3e26a387ecdf236569ea6cb1b469a72336489fa5e9576c3d93fbb9d7e4b44216865aa75afd9c27c3fa497910acb90d1e36d3e958fad04fc2a4f33e25148ae183579c210fa8bed9aed5661943f52dbb362e25b036aee90ea2a23918228e466e645d878670c81e4b1f2669c1cacb0585afb9db3b64b7ca2f948a48d8204fbfdb2994e1e87a7d2a8a7bc49e553f844b86cefb2a414972f2a95b6b7dd650b18298de4122a258414464a5df39e162e4cd4d4de5aa5340605813ebe403367d7f157a93793f595f53aca8e2a4d7ee4074012a02cb5323a417d8eeb494eb0cc17033de1d08d65c4a4f9ddde4cbf28698699cc94163a910f1de124935da8a45019f05898fd4fc183863573b1f0d67572e29f8b97d6f2472f8b711b7cf173ad7f50d3a810e506a2ff93e270281a5ef40dde744ca6ca4b71e811c76d801457935455bdcd5f855c3d29d2a7d3b9d78b5c48b88df16362af28d795643c440b4be67229f7ba80c4bb4881cd168195b3a196bf2e5531c60d60dc6793fcf3866d691cf8cae131be330ac3c01830c027ccb88d1527d6425fc37f48b27e8afc958e277c4113910103c7d274b61f22b4a04f7b0c975385979fb794c20dc9b2ca5f8cd90be81aafc8448fe77ab128f429740b51918408ce4b4f3c05423e52a0347bf44ef670812a7a9c1a9bb93ad9854f1a5af49d0bff294229004042f513808a35fdc15d07533ec9f551ab32623d2dbfbe12029110a141e179e4d2efbbbd6e4212fa114b78ee6ad47d68534d195204d625040354bcfc573e98073d8bdf549a6d3485c382716bcfe1ac369898258ab299ab2c1d1ec2f3d79482903038841b46663e68ca4d6a4789f0ba25598530bdafd0783c2bbcff336a54a995f8f67d092546acc1150fcdc49b036bda364c1042cd0a88a19b9aa890001178a8b63edc7b773707e26ffca4bebd34c8cde6362a4afdf31eefb46bd79db3f49ade0b7a5a9e134ec2c5d0104d3848607b04818f2d42c7f07749c4d571d550c5023442af41bbb78512eab19f8f7674a126fd20467509033cd228564d178596f90d0c55260a20e153a6f5c6da7e1cdcdbc5a192200bfb1715311b7ce8f379c802673459710d3d2682b1ace45be05d605fed6552f7f77a6de7f6eeab7da6e7ce8f78a69d67a3fc478bfe9d7fece3df9f8a13e95e0cd73cbd49b3e371230cde67bf52a34799f978baf0b5ab12e5ffe67c2f027a8cb8d59e610e749a61eafd76f165cfc1649ef71f48647b7061e1669de833b89d8b5cf95e3384ec121a1b741b13b7430ed13e5307ac34fa1fdde82ed4f34bb3164292f40a57dba8d8a34fb76816e746a2c1b85197dc3081273fa7d23c6d296ab2218b76353f59388064cca506d4d95e486ebadab684e182f53a84d851e0facac28a7863c485c039e862ef22a152e74c17b250a9a10db26196755c0938a193ffbcc80e62d93468ea1998481dc5062a3d89ec69a2ebd7e3cc9d739145bad5a421896eb7db228eba4d5869885a212a382dec9184fb2bb12f43d4a175414e6eaf22f5c77ddb3488e539a920284462ead356142bcf3041cddcbeb22cf6a8f9c5261f769f22b5cee8f77b3a0580b44a8f0a72ffdf1251abc5ed4926ffc98851e150de81ff441238c21a8bae46611d933c3fbb94fc198502fc0d639b45bb04137fee531b1cf9ea4898b52fe3ac9f0ef9004831cd34490ba5b957070f302c8fcb117076f67ae27d846de2ebf9bd1ac7aba319d39f3b362c774a666225f58652f7dbede7de62e5d5d0858dbd5c90be6aa5dda51889584019af8a9ffdf3023f686ce85acf977a6e2cae426bfbc516c432f6cfe1bcaae638808e992e1bb47b3fcab9b00c75a8173c74f589730f38f4d6087b5440f413f12ca5612e095125ac4b300e424008c16f6fe99898d129bf596bd25b17060ffb061b3b00d5c43454bb21f063c7b03d062cbcd6be4ea4a86b8deab28c7bfeb87be73c8a8959890216864a6e34a58ee9ce02d5999ffc9decef9cfffefba7afaf3d92b490efd4a83c3483591a2748b66228cbdd1e7420fd46f1d26b0d8dbd83601e15a5d76087b89fddd684d211da4fc42a611ddac22dcb3cf6d6fea51dfc20f0cd3c3eba248ea9967fd84c248ef6b434b16e0b50447329e1ed4ad799b1a0e1e89dd434cecae57d6949670520c67c9398ec2220a77efa2e1893420bfd9afb9d50b2d8e5173236941285a45670292538054ee4fdc9effbad56206d54a78ff9d12916ea0f1a44f8169e209a0a3ee0fa71fa1153ab5ac683770a1d48e59da4ff59a8feb9c364f7a99d25aa57cbfa45b76a010cede3ec6219965f0550bc1443f8c6010a8298339343a0da30712d740cbc81d24fd937674451a1993bbd75aea251f0b43e76a4333aff96ac2b34df28ec2a9fcba1d5a87884ee61645558b3074812f4f53b0884ec568f5d8517cd01cade1e903f784c6e78869bce9e2093253f6407452ec3ecd9657efd68191d648124c2392f0597022c91ff866a946a2a1fe2cf37d61b99ec177a85efc6cda315b1b3050bd70186123a8d1695e5d927ac39733d84024910da51c09a72aaf082471980db826fa885d6aafad98aa269a65cab6b0b2f992940c0fc6fc1fb6ba1e1d79ca2bb15c0580bff044d6076722dcf7f8cf5fba79411e6319ac2686fa8d19298074af70b8bf8a241076ce88b093cdc27699b66127a3031be25b6d82f0543a5006033d02d5678a3870635f651351448ad649e90092cbbca12fa80262b42a9c2903454e30d56a1aa586d20f2494347a5738cbb5aecca0d6b226a7be8a595337738b70ffbf523f47c815732b0c02682e417c3bd3f3d5984cc04daee30c98b5d497234c12f598c79040054874b3ea8e01d91b50080a498c2025cb49d590dd207a6ccc7fe26321814095f28518458d7016925b4b39413882ea1fb1b2580e58fa7e69bd549e1b0a1648440ede71cca2d68f0fba44283f498c3c5acbbd885a187888050677f87728a62493270134ee7ab158ea1da10e82f243908e25e8908885a2746b8e448d3a02c9c3d0026f36780c81cf052efd83b1c9ffff815fad11688a308ce38e52c632eb9e4cb59c26e22e8a01d8cba3148c1f7af3ba17597cd95a3c9b78865fdff3ad553d340782be52f929d83f6f4d6044ae82731e51ddc9ef43bab1e4ae9a22bdae4e98c7bd3de47efaf2dfb54719408e404da99e5c834b5f3b8183fa553fefccb5f2bfaca3a4e5338a7810b4fc1d0f97a13403c7599a607bc7bd121beb627c83fd37283487df756ea7d38b8db9fe57ba7f9ebcc6cab3ecc7d70127a0f0e80a111b4309ee7a6118085cd548e8b797bfe2c4570a9ebdbfc2c44cc8852f45d3f7a7e6778b553f42a24c6c299b5fd959a111cb9c97b73c23a13dd7e22b980c645e40cdbe23d0ed56dbc18b95d03ae0e698162182c9f6ba881a4849681c3ada9fb6c906e7eecfa39e3d43780852e42121622f84235255fb3f7db690db4c3d1fb8d86e8b8c4eed38e05864fac37d926c0a29c4aaee6f316e37bdedaf8eb1fd3d957b9d0a24197a3539bc9bc76bb314bcff9e84db94bfded0c195e28254e6042e6a563e8bd12793134a919d48d012fce325c9ba590aaef758e70bd43757a60dc65a6a9138987008bc14718be687ba42bdb10654a00bc4a644f65e00a61238e892a70ee5d56d89b1345789a6f7e5000c82e5f9763385e62e1cb0864e1a2e89a7be3073adf92c4e8854a1b336c8c5414a768f3ab4a9eba29eeaefc1f221ef954297e597ba3615d3e3792a332ac892a0899fb259039fba984484cd435c1d8bb01d4bd3e63581dc277440eb28be7f1966315312ed1500e9b07a39ba5e6ad63ffc146f1fcda7aecb8645d2a29a7a4128243041bc1bef1618de2f9746160f47c28f6852e466e2558ec0f27607e9ace220347b6ee85d443c8865c1c782c88f2cee8556f6d37b7a1de249d3a3c52ad60b4c7fc83fb3124a6c7da2a1ecc2e9a1f0a17a5568287e0404c60c9b108cb381325606fc88043352579b5e3c11e84478e8ee72d9cd989e4ff6e3aa1234f7d01b6cdc03fc7c7e1ce17bee16878301c8d2de0f64de9bedddfa953e85570aabc00df6c5e8844c4ae92b82b2001c16a60d582f960e73514e19a1d7d97fb1cc82ebeb0acac2c1960c8144a571076c28caaed6cbf89f803a96254ee3588fb6c5169025046de87a04460be98b2f22da176a460464229239549091ff904654dac96fee466eccb16247b4bf75b0e4fd1feac1a67548d055705e75f82921562f76c5d3fe9823c76702c33a8b0dde91b4e9de9541882b6dccb1d4bc1fc8bca7d7bbf5e930f94caca5a7d455be1ff3a05b3d3b61d6c5736b9ee8451623d443bf977f558f425bd6b9ffd58cabef15e5f6e491df483d4e1fa493d5415ddfe79e1de7b0a0030038ce77a30822d93ff8605625319f6d363f81d237836a2ea2eef526c53c92d7a43100f0184195fe42e0636c0d9e7dcba9f69d905094cf23510a63003a30d874f7aa670566d89ecfadfd4622987d5e4ba5223828cbb662ec7c2fbc881f427c354261d028688879378dd18a404fd82784e63b983968f3a8af2d76ac10c66f8c8cf336bcb4292ae1bcf565d0ac5760f324bf0a78084515c034b32c8d71fa53f670d0cd479c43f16aabce9febb19edab9b60eed8de41cd5989a2ffbb5bbf1e32936f805e0e3845abeadb68c05ab6b8205c860b73f76572c45da3cccf247d89249ba60d3c72cde0ab3ff9ee1b025b3948adbc6e02179905eb6365be80ee3954ef3a69bdebee464ad0f1a49584dbc88c496ce703b18436259bee9eeba3596a13ffbdc222103788ab1d322749016a8879f4840f52841f018f87f09efa5dcecd0e34932d73fb92ba4b9580261d82c63eca0c56b0f1a6d25d01b70f661fc0f7d7ddc84f14d0addddb46d8cda3aaf5c2a47152135280cdc9bc45dcaccd52714e209f848bf84d1a43c37b8fdf9f834334f65e4ffd1f6f18bd3a0479d008bd179c9ef18c8baca0a2dd1e4d79d85858304dd3b61aa7d5a69b3910780cb355f8d01f33e13f4335a95a21a6729857bf1b2397d9e2ad270739354cf8c06a3137d6fb74b872ec25ed45534881cd5818e044ebf5b540210913abd90a0b29c28b69d1856f01e01eaea889ac3f67e058ea8d83d5395e3ca98a89da33eec58ef9ae016956c3c913a647632e87bc54810966d85442c0050325cc681b9122a2fc5b842860491fec9eb277a0dd3ad7958d2ecf91bca76bc0374bea8a1caef412040258e846f2ae86aae6edf2c96c7f6df29b9bbf1fe39249cc791304b4b8a886adcf68af96bf853c6df5a1c3d48289ea36041113896880178dcfaf39f10422de08a79af3272a95621dbe26231e5e5e9101d632291231d127fcc4481f6238640038bd525ba5dc0e18b57661569bee11712711cd0bc4a70394f55207e7464c7caad5888e680baffcb2810c7d1e95314e322b2dea7e024132565c788b293162ed4b586cf327b7c1f3684161d21186f0e3b87f160517884e9f2c9340a8f485ffef92129024c15cfc82226102e74560bd567fe2559b679526acf95f6b8a75a0601b81a91dc6382f59f912bb40211609d5d8cc201b177e7695dd2a8b5bc5fa7c19cd92c024107dce6a004d031f217d68d86fe4685c850abc2fd0f34a6b6bcc654317b0b125be78b863494ddbf6519b245f927a4673869f7213c1458717c6fda6b884862e74b6523de7af15634197513fa2b4706da848ab6bf8280de1fcf7dca7d7b127075bea38de190a33d7812ad6bb09f05dd500f75fa9a1d3eb76a31262f690d359d82e437d4b752c4c904872144c847f0c7cec02ceef18bbcffea4f7b79a966c6b566bd0d5278d48ba69a2e8e286422d8df8647a78755e743e06bbc19cec962033237b3134de52fbdb470be8c6f2de7fbcb0619fd679b4cffd9c6f830bf0f2fbe19fbedf0d8f9ce85187fe26d90b3af33de3c6b7d269c1a6b474088596834cedd819c29566442d925481809a5c6b4177062179634d4c22ae52cf659f28ce0b52d79ead2b242e74c2d7e4e30ed7839639a4222cd41b90e166592ca0659449e6a6634cdd459b24487757dd2c21c77f6df3ec3bf3ec2e2d387b0f9f7115e3e834e4b58fb3143e564a689acb3926e42062ad3d4a4e6b0a455dd81427059aeb8977ce9f16e3f1b51cca924d96924abacd385d081cc1435d724ac695957a0094ad3d1b2ab3ee3d0bace40117453a2998db18a357bca834bb394230b9b63192b760725a0399956ba348d452cd889640fe592d9a7794a91810db18c25bb831c109c4c539ac94ad6e92454a033859a6b0c2b5ad725d004d954b4540293ccf7109555739550b61be7fe4f4eba0919a84c53939ac3925675070a4137194d6c8e9558b333a880e69434a791acb24e17424750e3bc1aae00be0f86ccf0699a5264b349ac63c9ba5aa0074dcd764f39ded9f7248184fbc73575917b750977d619b7d44d6eaa8722a0a8ad3148bbdcdf5cafebdcab4bb8b3ceb8a56e7253dde1ca2a742c21ee650c6ecc1c6e9299b89629b83393887f64ade69b7fd83bc9e214912771c7ffc63f1eafe0086b5bc28f082e5ec50dff10d7e8bc8fdbfd092eb178074f585b126e05b0b615f7fe437ca3f33aeefb27e147a3e5a5f873c6c4fbb8ed5ff089897770cb7fe10743dee166c6ecd51490eddb390e4ace73c4fc1f6b8b8cf9827d49277cf18a93cdd959fde0010f925ded112bd606cf3044294d4948489649e5bb3486bd5ab11fa6877828302ed88ef0f3ea1012a9c89bf504ec3ac41e20554768931d9f4d430457f5ee328c897c4a044650281a3c60d1a0effd447f59460569a605c6fde96aa58ba462391e6281c55872fa8c1e5ea89deb10c3b21e8a41a09d52a1cb95c9bbc2b30fef0a0cde320669cee13566186bed2ec1385ac7208d6cdfebe27d0fee09dfa9e73bd6594a84a343f41b6369736d6285a7da9cb5c97bd1a77b56ddfeea0293658c6a7ab8a822d0d928237b5376fcb37fbec6101e2be50f2ce0dc4e9a390a62ec6793e8a3ddb5a52f6a04e5a24611d3526c86af495d23553a16564e4c47e0896825eff448b00616d7d47323d4cd928b92ca942754a2e3fa3e0025dc855e2cf29b51aba72f549d44c1f737bb9c2c5594a073c1673a9dc41777b3bea2e5b7e01f8cffc4dc2344dd65b5ee655a95a741f750c53c74b722e582528b03d3637f78f320a84c0eea6dfabc1b095776570d3546aa9f695138ee05aaac3ef8427320ea2d225e279fd407b68ba4e017de3b4957a877b1080c2f051a1c6ba5551048cb481dc419bc348900023f1a70d2ce4fdf84b8eb9df6510e60c22b079c4043120119812f49dc52926d915c2b2169114b646ed29e0609211eccead0076a416c002b2fa01770a7fa0615ac3aaa63748516f17f0adb09130a980a3838aeca18bc5dd77954a53e9a51be0a3df0aebace697fbfda8fe68b65ccbacddefdcf601da65de797beb5423e9a2f5d12d2757eed2c530fbcf9ffcc5b2bd4986cf0458d243576d4f8d11da57ddc9ae961c6872e70edd264ac65ecb3df0c2b8d308d6fbcb75b9155cc26b355893443a58b31a7f3a75599fb99e50db65579976c55da23a4afb6a85a6aab12098d1b62d2194d74b7b3a60b86c12a366798babdd9ea9756b42a9192eec3eeed06fbf0bdddcca042c50c0194e18332c69421a68c31baa3cfac4b57e1f06dfed9ec3ebf024ae424d10f61cf6bcd6a7f6cd17b20d2acf6a7525b43a544d622fdb5b224a5f740a42246943c8c2fd1bf50cefdbf507873ad94087bb652229c2dcad67befa3f7655f53de2532fb2db942352c3dfae3ca9268a88665139c9344951261fc9277c9d1e37fa1cfe10fbd4747f86d32fac2e13b9917dba39cfba29cfba2f09de0fc4993626c33d89add274a193fce0edd3fb22f2cef926cbd24fc7368ca6f72add03c4ac2e13bc9b92fc24119cb218cbf7e964393063d96b5a30c1acdbfd1bf59223bab2e9b4c5e5aa593ca3e4868565d299b31a234be343dc2bcd336627889a1274615e3bb6f6ee8cdf5fde6c669955aa14fe9055e3aea2cc16c86e52c4d01d304992e3bc4a0bd048004448aa040aed140fb73fbecca9f245bf993e623e54fba7d76c5f09dc4f06dd70a11a28ab7cfb7cfaed8c2dfbf13bf471eebf44be7579cc5940747acb2455569be4d6687f287def7dd3ebb3c9974abfc921e83eefaa4ee3d5ca64386b8cfac0bc98f85e0a77456892b4539ee1ebcb9f3786d4c4d95272b8d6294b31c83f2e806e247aad62659fb82b1f0537ce34cdaa4200d13001105036cd077809d04e80691d66b2608d513021d700446741b33ad0292259e4075d37ce145172a480117345b74d3a0a09b468b6e9a2c684e1047b5b30faa61d9048661f6fe1518be478ac49c556cce07fae8eba32fdc17b93efa8a2f843ffa8a1fcd9686b3f3a3af88ef7f1fcdbef4b42693f53f2b3f5ca9a4b53cca2c27bef199f7387bb1f7bf5dcad670ae4275f39344f8a32fffe86b562a44530023badba34cb24799cc479af4e768d21f2c148045cda4b14963b02b76982efa933dca247b9489962068a2e0497f729228d664927a3497feb119efe370a6b59fb53cdad9ec2988f112ae56a852a248672beb37f9615b338dc63e866996f28abd334cbdb791c6eced66675428769dad46422e2197f8c8eb0fe524518cb7af9428563b9b579542799486278d75391daed6c3980a7761ecda8c3f0fbdc7577ed867f6976492c7db673af384132350a17193fe5cfae596645bf4f466d86799edfdb2bdfdf43cfbc13e10663328619e857d36cb608fedfd037cf46fb5b7cf343f9616e67d1f8865f47eb0fc2118bb59fc38f9d10cfb8bfb0afb68fe6886658fd29e1863bee7655b675eb0712237be11e360ef8fc49cd50d4bdc8a0600432278234b1a5d9233b11fb8af3d941450a7a8544a7c94782a37292acf7e88c5ba4679f643eeee754a023c3729cc0727c0a287133a28f199bdd7c3014a7a9c086c7ad64b05783c691f61643c651713f9f06841135dfaa7ad364de41e22a0e32b25f169f38148691b1080b9e1e5026a1db580bcc7739336089e0d088874cb1b0e1675b4f083046614e008160d267cd122c7e647005f0837365b70c084b0e504590a5bba1b8709299070d1dd3925a460730c2055595c701b264ac0305aa5b78ba6b100cd966e1a22ba692ad0365e9eb0f1d28566886e1a217ed050a0bb939e38f9e40df67f73ec9344fff7f533dfc6cb0f9a0948404b10516880c8d29386668a8d07c8687ce377cbc9dbcdc603583acaa4cc03f3fe9267f1916e1b0ff858628d25ccb0b7bf36cb2a78b3ab70ae7f731a5fc2a73d4fcaf08d5dc2609b2e46d41061d3c547171edd3518982140960b588002361d38e24193a569f5d2257abf9f1fbe6dca58b6d5d2283163eae59957ad27417bffcaaadafa184bcf28ff0d53fe244182eeeed136493c914406922022891cba553e74f4721f7af272157695d2276f3e7fda6e9a1fba697ce8a6e981c70e56a620510211708056e5e5d1f11586f114a1cd87cdef217600432e3cd02aad34276f2e7f89cef8e89623061b24b2c81fd84356c59cd4d0028d378de7d96c4cba6964d348a16e9a8ea6a3194233e48e3b66ea98a923d2c0984b4bf3a4774b867e5a4d805a4b908a94b098a167ae68a1674a3053848117bef131b06383012246f4002f790a158d80626344ca8801747b9e04a2c99beb3d2a976455f23c095495b22dc22e4f0281718110ba3d49b34f4fb3c0025eb28d05ac74871ead66d95f1a14e463b3050536450c4002a12e79fe937830c5f773efb323f452eab1d9d2753b2be64be6d36832f648f7eb7ea8f32ca15099ff4ecf7e2aec2e7894e577954e11558e8e5feb39ee954a8cd155465c55c4553a523ac73b478a449dd0cbe5e4142982e9928d5d70becce7774790384dc63c2f85ca4f63b1304dfeac1ccb661da6c9d80a77f73fef6bb6ae4be938919cbcc5146c885341b912254e45c789b84d4a8755f865f4e6bfd9a7b9ccb79b95fd7579b514c85df06871b95a8bc32ae5833c5d94165152e7337bffa3defdafca5c65be5f6e89bbe091266319fcfad96796055bd1db6398df57cda75e71a6f826674c93d42b8d5641b779ba3068e9954b8f1dc3e2cc7b8fca8c3b9afce96832469331a79e57fad3d616ce49222966b2348bf58a81b8a6052e4d0071c40608158008a1fb87357e28e3872b9c855fe52c95b3f04c1126f460b32442f704faa186279512c5ae739bbd028e8b704e12e516ce0ee57e5617bec92b8f28ac064c0f45b8c70e363c84a184fba2595d93c6aa92cd606bd29f1a356c32571750c436991dca49a218b1e3ccfa5f52fce8ebfe0cf2585f56a98dde9c52c96b515e2d0dfa4993622871d7ca68fede869dfefc5969cc257e3a6325ced4c7424984729f955f9f954b998a27e6d743cfe3a4313086278da9306ad298e39b9c7bdcf288dd7966754c3df0b63c1e4df993946d912d9af68856e9a43fd916dd97df24dba21e3c39393aee13c3ed630a9a7b1f533cadbee7c99aac7da030df8af3dc3e671eac4489cfacebc4af16e623063ea414c9968c295048531540492fbb5628683e4ed65ef36ff25ff3ff8218bb80b0007145cfc66e21bec91a715f73046de8367b9c2fa34b5caa0a72f9b5b90b65337b7f26a3332afb0f4fcfd6bc3ace0e29d5bf62af90bd7f004ab4642f0dc2b6a85ef0677aa085a204ca0844402a105298a94112e166670e9b8f88951e423e5cf727bfc62ac015dc2cf2c4be9ea8c8128bb50a4960135e0943c77110c98300c346038a40e3072c68f01044b541b865279834e90493725fe494e86e3bc1242c617682303b416c27886d722f2cdd17d809da092675c4b15902c35953fed03c66a15736d9d780b08cfd9d5958aef6ce6095c232f8d1d8a4b10a56f0b35d9a15fcfc6110c39e36c12a39992bbdd50bc696a82761dfbf0d276b19276b394676565d33a3991146ba5608e36ae9fd59959e26f1cce893447866942b7efc1403fd3dc232cba099d1acd449f8836b5836c1ffa13cb2b1d6edd259b541322664ab980bbafb15b127976e577aad385f56d0339551efcb2df1886f3878cabf1ff83689cb5582367806eddfe82be2c7587e61ac52da0eb1cbc91b8bd5e1ce67f3bd866593297f3c0f149af2c706d9972daa4a3c623550f090000f20a850d12828b589ca2b7cdb6781800089b406fa570b622c73ef3b3a0e8379cc347604c9ec2f52473149c1d2f8c6088038df558332e078d5ba4d37a0d1dd9efde48dc562b16ef0a1bbc3b7b13e4b0f577973f7096e40e1da283ef8f4ec105170ba6f368946abfc759e1c0cd66082e2a5bb3114af4d5088f4943f4d4f74e81e8a0003e2928d659ccfa7343dd8bca691297de9e6a1bb7fd4ecd0d77e4a44aaa5d8a3b61b58270cf6d59a5e4768495ce01b33786b05212347072963420729734337120bdd311060862071e4749038987490388ae8207184d141e2f0410721a342072103d441c8c43a0899b08390e1d241c8c0e0841d4e50e60520657c20c34577f78905091c7577cfac208709a94b371bdd3c4052f57c7af38f0d5658ca03e887470f8f1d1e383c5ae0d1c3061e4348613ad28126073caa697c230e903146471adfd83e42cf65cd50778d931a665e5034c50d13d11bbdfa51d3240662d8877fe65f7bef1213518d690807d350adbbdb5947aaeebd9965ac1e7a354c3f21b92664e66bf8a1060538eb6f569641eb2a9f954aba8469f78c0b78504dbc709171d5c01586fdfbb96a7ecfa4a08689104f7525b9869ce50ad2a48d2667b8ca95a8f09b9a2ca0490e98fafd0f66330ccbfbb6080826466c214573400b031020f4c5fd25a3d09b42551a87358a889f1e59729b91c85064f57364c97d566e6429dab01cf258adcf8f859eb13caa2e5bf4483533f0aed29323a69878a2e8eec7e050d1ec322c18acc8c89584b4e4098cb56488204c3c4033409a0ed30c3998173cfa015b70b48ed841d2d5b06cd2b9aa4a243afe8519e59df4bafa1bcd4f73339b42c5ffc20cebaecd3229d4d1e80cacbdbceb7c663d3a69d30c5cba632a7c1bcac39cbca96cf689abd2ac4a33ef936c110c06833d71721f6662195d0a7ef56f13c66306043128306104129c2146890148e8ee26a10914ec8c5087aaf66303b2813811471620345657c1e5fc280304c48d12ccd8c93b2fdb4ece4e096d4aa2834e12268915d9da0bbbb71b976e8cdf839992f81026c9dda624af243da624deddb0ff599210baedac5258994ea3c3f49b9064e179d24b7723d9d2f829121dbcee4672d4b09acc7e4876ba1b89b70c74846f83c9904577c3bc744faaa5730d93618a0c418d6132a01acb4042f791326d3a32e648982360ba1b049a235b1a85361df1da6d9ef2a77b660440aebdd787139311312623b51e130c5a4c4654353324311999b2d35d93a4bb064977cd901a196a33b12504a2bbe6484e4ebebf54adecfb3a10ae9874dec03762d8fd0f859a15e8fc2fe4e972f2f682f7782a276f2fe0584179f4658ee356ba2f3362ac718236e56409ab94535a2ff43c82d276d718e9ae2952b30202681c60abcd1a0659acf91567f4d3167552d65da3d35d9353b3020d269c283d2606b18c68648a44daffb332ec1566938f930f85860634ab15cc8c11868a0a26a0d04256c159a6339f71c244a4892e6284fa1a3338009165a6015f835313bbbbeb98d4c033dffb98f45a33249850748861425d81da407763eaff85d883504add5d429b502d136ac7934b30130a05d3cd0f6ec62ce9ae5975d7a84c37b91a22606682689a134c336e74175d9051230210244c3144c9d7621a23189248a1ba6b8644dbb5b3b6810986ee5ead5635a91d7f9cab5ae1948f849348dd3528a1992a6880e8aeb999b688c55a329b19d51f2c6b4add9dff8ec3e057de7cfe632c7576925c480db1751454d44262626ac1089bc9c3e8d6c00c2a7423a04d0ed44d5cb7cfaef7645509bb8432b64df21d3c985800405b9af464ba323d2a6a12667a41138da9b1a4cdb4615ac1d5b44a6158d2ee2f59cfb348b95ef08756a9490535babb67f4e8a9a4d1bfbfa4bae04fbc602c85fa2b3f7c14b1ff1205f151b4c9646c7efebee5319625cecfb628d779a3fe5ece8f3178c516cdb7d9a5478a8149f395804ca09da5d32a55455a7c57e56e3949e431e7bf791ae1b8acc7209757e93d8d4fef7fab188823c6a0f739ad3732398b81385219c5f247ca6287531e9555d0ca3c59cb56455b1eebcdcce538532131100bf186c082863b31b299e66c8b5ec816853619cd493130e85a21a4a934b11dca180c9a8fb3b32e71e52451adff592d8a14799c55aa92bcae73f993e2ce31add2d9cc76dd0d0a70f7f1e9915906b9ca553e3d3d375a4bfa5c89bb7b029cc5727f8f7e372bb3b5eb3ac7995610574b1f29cc7452d00afd25eab5be7f27b44ae50761dba6005c117f263118fbd05501f0e96e619bcc0eb9db6432cb20a41e21f868757748e7675b27fda9c940fba4522793fef43099806820aec29bdd6b62e8ae81a1bb7fd4bcd05de342778d90a7f73e36fd48c3f4434c37f1e79c248aa177a9b4510ff765fb6149636e3d70da5b4d8637e36237a31eca6aff71e7f829feeb441eefcd4c1db9ab1ce5d1654d76c14fc96375cd9f7549c69e87e9129df2e6821936c4d4800e1edd261e6faab181a9264b4fe08c3156b0032d32d0404031470a4c8ce916c8d1ad563830d8473ffaaaff199966b484d79a663ad34c4e77e70fdd3523c2aca09ddd6861f3e7fcd8ed592cd887733636ab343592a96d30750ddd5d73c95af2a62680a97fd8dc5183ee2cbfbf77c880ba03053bcab4b3e67dcb627df8654a76987ae98232d0ae6677a9d2d5ec2e09213da961d94408e9c9acffe5c79e2761303a96ba8215fc8109dd1736a78c81ffb0fc55603110c3e694b1fa3110c33205adc84147cfc7a00b06ebaef1ee1a166a56e8eea0dab291e34877c7db67174c8e0c0bc118f63c09837d36db94a1dddd3d58404607dd99e2ccd01ca9834c945e7a2f16a320ac200e3a6ce2e002840d1c62e048011c5c781873ef32818e0247149b37c6d8bce145775fa11856c359c3d94d33821ace6e9a1674370f1a30fab3d9bede4801cba1ee4ed9b80fbc9ca8b3cad1dbea671247a08bb638d2dd5fa957a91b5984a159018f14ca0d2c1550c2c68d26ce7a18cc0d1368957ef1af6ca34c1b61b8bd6d08c10adf066b0347168bd5060bed4a3c7c1becb3d9bfef41998edf835c0fbebbdb59e13bc9580ed9d7910dcb97cd608b8d31366c4051f36a69fda1f09de424910f8eecbb2b499756ab95151c9c9b95457a85de98cc15276b38e270382124331c41e22c6751e956ce7a59d491c103a5c47469cad5fd259b3d59ff734ffeede6674f36e95c7a3e458e20d1d1c1e97156b543808e907c88bf52e851ea819fe5ac9f69ec7e92a8a893429318fc2bab367cdb12a2169290db5cde689cefd8560cda5a845d46b3bc59f7d9cd6db6f869357c2753fe446c33fea84ccac775df0d8cb9ca2679ccf5b1771fcdb8eb3ae74911711e1fe244fc7f0643b912ff6886793ca98f6694cc93329812cfef05b2e658d65a37a875772833fe25ba74418cbb01aa5b0d31dd1dd5006352234babd175c720d74b8d02d8000536f8bae36ae63db66e031ad22813d3e09246678bd2d8498384ee1abca03b66fc93c57a4d178bf59a140d2bd028020d20344ae8e28c069cd1a4fb263fe31be954761ec6fe2f36430733aa98d113432fa9a8e8f3ecbd32ae28434b370dc6a08107688044031766d0c50c8e98419319b4c860838c1b9021c4adb34a79a3cd64182143003220a33b7615c459cf6bc94028063fe81203a518dcc480070cbec0e00a0ca674c72e7f52b53486de4dd6ce57f1bd4bd5f762fc9bac281fe244fc314b7ae0edda99981b88d932eb1864c6d0c018750c14b410c38a5183182fbc00062ff0d21df14f1cc69818861017d8e18211b800e602a216906941981674a005575ad0d3dd75b250de640dd358579562e881a10318303062981c84f1120601617aba3bce2cadd24a6fb95222167cc10213b0a0b6c3821f2f1bab5bb522be76b602225680c30a4e5f50f1c5952fba2f72ba6705595dbd600c96a5175478a14377ec028e2e9ee822d645933854a0c4ec3a0f63f86bf95a9f5915ac5240c7ac52958214c890821e5c8ce1820a131755b828c0166d6c91832db66c81650b225bc8ce579d12c79dafba148e2b8f187f55e2b2ce572ce944fc73222e9335cf37e9995679f31bce411b82b3cffb9acb0367df85f86bd9ad2c625a05673eb3d251d04201102da2d0226b21d3c294451d597c208b2759d49c004c77c7d0fb259cc398bd559c8dff379bbf0fcf5c33eb5dbe16e75d8797c47ce5b7ba3f3b410a587081851558502c6cae40e38a1eae306245195680b1225b81002b7e7477e4e1f9190dbd5ca3516f5639b35765894c5001135013d06002ef8eddfc2bdf9385b55502232588a18a31aa6041153854714345185460a142670a1a4ce1c4143e4c219b42680aefee88333ff7a95ac6d94b7bcf6619b4c4abc4602dbfac93996669bd18abc39fcc94ba32066f61959daf705f3b5fddce579dd792628a9b36492184149f143048f123768f71dff92a1a01918008cc1d603e00468299218a39a24041146114a88eab0378c6f6f318fcef91f3ac32b647cec23f2d51f6308d753ff826bcd91bfbf953fe6cfd60896716090a16406105144140f103858f66c58ece269db4f3afe5fc18cb59c10bce1cd3a0fb572abd557a9b128669e8d303c3f46fb2ee80d893372afbe9c957adef7da55e75cd87fd05bf6b9f10d3fd04982770e82cc312f704932748e858cbb8af2d27c2b49ce812e67c4ee951aec526277e447c932b6d89c0daacf23c1164114127829d10a4417b8f2ed12b65f83d09e4c362bd90e8809e040a4100be60f1654b47fcc5a6892f9ac02156fb8519d6ddafb6891540c00508bc74c77ac1f94ca0c104154cd4989031410213ed65092f4a5e927c208d0f68f9c0079078604cab5498e68f41f198315805b4a177f706b4a12b794c752c5606bbbf494c5561eebdd92f61fbb5fb3356c37f672ebde2e497e481214b9cb1c40596b86189205dbce8027639d279f2afdb7a1fcb9faef3997d7545d1debff2d11aae76fe2cfe0533385bc90e88a0036007541c0883035dbafb63b055942914be3435511c686103606c4089fe9f3ea361ac2a2951a6af28d17a43031dd000150d0809230908242193084006ecc88017549b32a083acfd05bfdb79540ae5bef355adf3d52ba793208dc66e12d3e9f3bddbf92ae20b7e5d18f358e990213163ebd9d69021ce6252e42a426a418917fce487b9c4c1e5082e52b81c718981cb09486cd11d59ab18e66432f749ceea609dafa2c73a8a298cc2b1eb280da5f7352c6b9efc6b67d585c40e1226f4116558dd1161aad1115f3e893b02cb6c1d71a46593ca28988401336618d0a11d031d064830c2099311b9eeba6b8196c4dc1bb113bb9fbf7cc2b7791e8ddd3f9a5616be932348409f77d223fb271790e2025cbabb94ce055edf4dae7fabf6d2a5d8ed316ec51e8a05e6b0c0062cd000a13659a0c723b6b30815148183a9889dee8ea197a3e1db6220063d0f193264487c1bdeb2c496275b86b6f46c4161cb08dd6d439b88a882082b2622a274f4bc1bde0cc6727d1abb1410caa873d94f2bd3f1554cfd12c595c5b214f6e10ee55d2a49c7575d95b2cebba4eea795012539110742194d2b23e2c7ac550133ba3b7a1e755281fc49157040a6b7bf6f93444b7c5e8f65edc3f8de252cd62b8336acc13e9a8730a3bb7b882fdd1d6d6b88af3b0e41430f4142377e1a0dc42d21c07816220bceaca09d499c10b58f662148c0996fa280151d3f9a294081ee6e1f97ab6a58d62630c50488304de0278044026848c0879b1b9f59a44abbae8bf597b494400b125a685a9e680940104f04d10501640a0208103e00c20820baee9066b686e4eeac95cf6c87e4441ce9a379fe95ade21092d26a6725f45272e93c1fcd3e5d75edcc57b1be4d667c63abf3e0befadf2c931f3cf003961f8e7ca0c387287ce0a28afe61eb01f52066043d2ca0075b0f4d786883071af000040f3c3cf0d8e14b775d90afdcbb944e902ba6ba6c6b18fb68065d915669b7fa68f66b69961964b9c294e5872c48225046048488404f0472202006021a80c01104801ea0830708f180dc035ed0a14c77f833bd506241beeab08a85a77ca45890af62a7b37a75917a38d3eea3af2af35c255375afce67b6eb522f9451e7acd057b2ced67095f6368a3ffad12057d77d20ad6159eb7c15f363ef64a5511ca40314930e26981c40c6015238408b036c1dbb178bf5ea3c097aec5e9d7bec96c48c1fc4b8c562bd3a49143f9a5755d6b06ce283fa68f6ac8df9e0994f0f6c7ab6015da84c9132c5a3cdda56038e7832c5612a53eedb09622953fc6baf06c4d0dd3e2c960fcdb1f4574f4fcefe789e9455068460c262069621b034e978bd550c6dfe5aced622e9b46901255840071640846901411d73727254393a4ec493b2cea1f82aa6507eb3b7eb72c0410e3030e570450e52e4f0c59483027208731082d3e32efc04f957235f455fc5940ecac8573e3dee4284c1c2b7d909a63adcd70ee5b996c1d674bd600ad0a2805a6d29c08454f6280de513871e74475c0b073034f6c2414b96d3438943cd16e1e009a0a39b4902b848008d558ab39e5b9b31f5982901347cb5a62b7774479f57cfcca88811f04a92e98a90e98a4d38453833852818e47ac8f5e44ce88ea99cbca1a848c12b9a8b5da7137f62597339fe3ac7d9a3b43a3df4268df14c1aabd4840b010e02381bae8437565de7333be94a4ed312104b499608005e017a31af62f628cd56e9ade1eca53da67e671ec154764186c274f304bace57b1abf4365b5d5ccdacdb1a8e564bf3f568ae14b472ff684c0b6fe9ee9609d3206bcb8483442aafcdb92ff2f161b15e3d336b7b72f6a767d6b258af6b80394c564670adcc4c565c307d68983e2e114b5a938138d5a13c530fbd6ae3f43c8aa357be4f2bb321a6379c307f55be124cd60a4b3bcada376d2dff4d16258dcebaae5aff9877e473bf47f64f303662b1eeb5f72e995557c6f6a392536452296224f7b39aea923beea0a9b2a68a88dfb5428d89c796377c10be0dd642cd1d4a4b92d400d15df3c38f0661e8dafbd3d6f8d05dd3c30e5e6c65ba25ee8b90b051f1c4649bb1d5335376305549e3892a5d5a70531559054a15a52a475568f09945b9a9ca0f13f582461163a62dd8ea59fbd5d430d558505b8195dfb54238578a69f227633a24677d5a6b6684636052f8d1179e19e17fa1fa776674ff0393aafcae15c26096d70ad95975cd02b10afacc118fe9e98158e2686de12fddd486bb8c2f99abc4fd3aaa4229e912c694ca149594291e5b7707532ff2fbc38a98eef82d2b56b0745bc9e9c6d40a09dd31ffed6693be32f10b135ddf97eef881b64c77fc9bc5368b68bb3e591ababbadc7a46a46fd124ff5ab4e6e6522a6556a8b3730b1bb69e9be4589d35d372f4c4789ab96b63c2f318fc86687ad4b77bc56565b29980a63d6b6d45502cdfe7e458c803e3e3e9e47633e3d30d80dacf4a32fd4d0a8fd5073d57e9c685bd0be8ef16fde7558ba2a762c56a5d403679947e2c752f68a5e77adc7229176518765eb32066747713c6fa6c40cca2c8607e3c33732679065c6a60859c812203b8a5385bdcffee73659c436c7f94b3c761e3f36b33e640828cbc9592d0c56e9adf321d1b152ec8cd817f914c786ba5b4e40da709249240c9d19dd0aba06743bf16b39e71a8de29ae4a924949187d54eb759e2d0c3f8c30a23a33baa42ef334c0758d7ed081083001f2a6c5031e244c501548ca880709a42c51423a604650c4ec7d2f3d31e3683c1baff507edfa11c5bdc4fd0dfdacb5791a8eb62a68ee3ccfa077a0dfbeb24650a293e48497280340e20e60058ba63175699aa602765561b4351e966b5b14e8ad2077673b66e98e30631372871bac1deb0a372168eaba1d410ca08e9069b28248852eb686b38e950ea800206142f1eb5b9cd2ea3352cbd2416eb0514fd890e9ec8e0c9104f983c01a234859255a2e1c5c6ab03afdaeb3f2976b453d54fcd28956a531dea97ecad73cf935ffd512c963c253520099624c209490d24213a7bb6aac0d47cd9b5286fb9bc6fc318365c4002363cc08604d87073b281c7f48c705fa472564a07657474e5c87674c4480d232e464418796d8b7c151fcb5c88a1f799568afb7a8b70d10aa7a21188a42092111960684888a125a7a11f273f383961c3c91627312721505183d7ddf1710d37a337cfb6e2fb9fe36a7f2af5d5575bc39e9337efbce54358d7c80e61e5bfe1cef3680dbfadf2ab0d61d662f9d5d0f1da2c9333294b723dd11d5d374dd4e86e277d6a72812651dcfef82aa670b514e52f17916b88c56a6599cc52962a42659abb89f86d762d9838c164034c8c60626322c4440421129c847c6867392b7c9b1773b5d22848bbf26775f92aa630c558a23cf452ab47422d096249cf92108272109446d0114114089205bd10040290088066402afc74f19325623b31c5adee7ff26b47ee3185f2bfd253b44a510e942a4219396bbaaf52a8228c3262dde8cb5771465d55a58ca7d6c9fbdd953815be56deff501efd985a55b47e68b14e34a841030db918daecf8bb1139cbd62b71ab866596f7bd9f451a086080384e06f0a2bb23954e26c56c2dd24feaf2a1f2be8f8f8f0fae96e2f91f88e55d5229cc47f64f7c70765298cfbc4aca9c947451f275b712a39e32dded3d61bae74bf76cf57c7dffb6ea53cf4e0f093c5cdc2ccf96ce8fa94cc9c99b94293c94a784ee8ee04d4d965bac2d5897d259a507b91cdfbc268db1765077b6582674f70c635a3370d19156e90c5c66f8669801b553a63b565a74dad9d9a911edd0b0e3dd3149991693248916ac94e48bf3948486a64bd5d6561215e2949f93b78f666cbf8be408121264104386214e3230408624477870440547b074a78c3e85a967bb1667248d93112f46964e466a30c2a373f29693b7979111124ca7969344119f8ad08a149d8ac4a0b3860e17295fcd54ad3772aa38e53020474999138e08706e3845701cbf07ded96a76dfca5ac6740ac524e203a2778a1f495849715a6d49e95cd00ae1292deef5d57ab5b39bf29b74b56c631e58eb4995864a081592eae644040d22491099272241447a9c8684b1be9ad56dc67578d29faec3f6fb4963d77a0e7f2ebd4306302675c40e29a0532ac829d5a82650b7136aa85340734a2020a0506256f7f91483173160200629a7184e80410630100183100c51062f2c717a819e5e18800b57b8705d4841081742721d23b61983acbf85a00d6bb8d847f387b3074e6ab3ad97d230665b48727239dc8c938fe15abc8a13b90a2cb880852a58c8e1c402664185130b21ac208315905881ae50e4a44299930a635410e2a482014e2af038a510c529052c93c67c858d7c15696c0524023911ff4101d9225fbd8a948c806041ae1f589416d18ce2e0dc6f347f96651464f44279ea076584fb4a8432024afda08cdcf3242b0534692b56c1ce278d5589420e28c8503889713ac18b13ae9c4e507242905301d028c00f0590a7020831010c139030a1869309dda99f1f6cc46d264aa11ca3dc71a655fa9eb5316ff9cabbb04a15add2e92aa992b44a63b5140fa088d3001230801f804e10344e415e700ad24310a5ee1ea14f41989c4a80a3041594a0815309b8127c4e25fc3865713a8de0a4c3c97512c134839309042713ccc46363039b246c8a4e36454e2490712241cb4fac208dfdf8ca5771e20c5a8bcb11488a28d6f26330dbef6b407c15511e4595c293c67eae5d79b77258b514c368d2d2669593c66a93c662fe389aabfedad9941d15c0152701e4208029027861041d9c465062040498fa34c2d0083f4e22e440042544304284a153087474a7a238113f72224e44b39e1371229451ea0865e4310acac8a3e749998ee7c9d4518ac869d6431911a18c5247ae4a619413392b7c2711678ac336e34c6f3e3db3525b085d8a42c062429f42e86208a8b645a7008411becd6f96a8f74e70005800401a27004cd1dd51fa0400d909003e27206b9c8050e004c4e7042484c6ff85299d94a7308892d19b20975f2b3f96547f84a71f379c7ee89c4088e3044218107c388110e50402cac719271f5dba5993fae79dabba99d1745d8bc293c2c2b7b158387c2791669b6387c3b775298c0adf96947b1f47f9b1e7a1ea635b3bf9e858ebd4434c0bf34f3db8749f7ae45ee8538f26ddddaf530f7ae251a61b9f787ce9eea6a0bde1c4034b6ec9fec9a9a6cca9a689534ded54f34204fa19cdf2963fe9447381eeb6a14f34359c66e838cdbc4089cef2874439eb0f8919bcd663759d0f8956362476b74e9f664e38351ba7fec0a96ba7ee4ebdc21d34b8838a076de8aa2cfbfcd3edcb3d8a551f9db922ce14cb1a823f77cc3b64b0238eeeaee0fd3c7365f9df5f3bc0d881a5630ae51fe8f3df8e9d2057cb8e3ac2d4d1a58e1a4e1d293a78e8a8f9c11a3fc8c00ff00f52c8f5e947657f577f319da3ce3134470b73f890e38b1c5de488d2bdaab64a582d0b95b1658a9489a1cc0f3260c848323b644088e38b3872dd9d42a95878e56e957c1559dd6757b63aabc37d6af51705cbd622dd4c2f18bb51ef6b705c01c703e0288203d5ddb9cbfdacd2dfc06f746fbcdee87963006f88e0031df860091fbcdca8c30d319dc28e33e523b98eff04f92a56231df0f619676bebff0ab9c1a5adc56eb11b51badd50a10d32ba636afe12f5da48a20d0474c7d412f550f331283fcf37f5faf8b8aa86650f8ae881941eacc00619dd5fcbcec6b371840767f0c0071eecece08c1d04b183243a2843075a3a666b6f8d783aee2b6c66fcb10ef7b5bee761a69fe57de5e4e4e8d895ecda9f495ca3d1da4b9b5d95a53f6d4a9a7f2d873e3d19d3a1d0a7681665099b486428d2440724748fe99883ae65ca010362a5d6e69bbdb31cece4e0c71a62566b6869adf175afd1e4263fdfd648a12785753878a34b9cbfd461896be1e02885c201ea961913a67b4c97a431b93eeaee380635e6479c4937b0a23baf79a8e413d7fba0756a4600002028931000003020140bc62332b16c3ee9fd14800162b856b05ea1cae33cc821648c21c400000000000000461b004c9b09795e5624098c79f82d8f62a2db4da8cb9481beb753c7d4fbcc0011a86a0442ea728843db6e9ea28405b06369186c3adf84313ee780fc84c70dd954eafb8e16a8e4389d4f1da75f9f31a087b47002345f0c1bbfd168222388e873103723ea63ca1e609a996a569696e0a3f60d3a08c7bed33fe313dbb6cab83436bbd4b8b9952d20984220b5d43340e31c519134d91a32b30f0eb627444280abb4106955136c0a1596c7216f454370f684804fcbee4ac7a7682ffe0d3458cc1405c2943dbd27b3325f10f50d2f5aaea27f2d3b72e4adc5bd847ac9a6dfdc7c27041afe45d39257f942171e1a335331236d26797a7863c63e830af8a3e51cb410fa0ac629fd6f4a50cbcc48f437a1c08bf086cf3f58163f9cb40e3945e635adbc8350628b3b7b34966a4b2e01bf64ec1854e4a8e051e4bfe512fcdcced905a14b317ac6a40f5b2cbc00f80c190bd944b6bb630ba7f03118c8a4273cbd22cc8f9e6087bddb30ebf7a6b6ffc12d590f83dab58de83b9a3782789ca57a4e0fcc97bf2ede6fe54c018879a7dbac20827cd0cd8d97e935d4be20f409bf7581c2daa8bb29a1107bf20d7880db2d0f844e075c715f4eb36a621ebe4123aa00c065e400bfb096963198a776e239c4eb1efe551a5c82b0586a3f708b58a917a50281ed99a5f7e1e5e2939348ff6fafd01272f01f80fb315c175e3f219e0aa4f88ee3094e8e263e282c9b577ecb4e8a2d9d0496310523ed58a01061e5acee370cf5a07ae2c32029e430d5fa0cccd63b5089984902d232b511ff0eaf841fa90869876aab058e62c0870d821609caac5541eabbb8e42da37086971958920080bab497b46cb2b4b68a49260ff7573c5c9d89804227f2851aecad918ffea473d6ccc8985ab2d3797b4b868f9e5d6af03906a6027d77b8d71126b606831de9d2036ef4ddb4d2f4ffb2f3f5b5226bc027741a26d284f4251ada6b49e69f9378b523f9c12ad2b5288de3218940d0866c2fdf16cc6fae6b6c14deb20bab4735188e10b5dd66fba11123e901c016571bc6568980c9d96d06a1d3181a3a6ccff54eb129946118158fd231f2becaa3123e968f6fdf47ba0de7d2268d9c0903346c1cfa6df49ba212a7a520e919f5ba354cdf68a17b40caccfa6e7e030248d47b152b4c69a91594906cfc4e562844088dc11cdc1e242e82275d6da4b42adad609c6b36451a54abf7dcaf966c0c7a723f476e66e78f61e497f9bc700caf7dba1933ad0f604b6da4287284346d1e129fabafdbdd33bbb7a329a50bd8a029ca7472185f82b83e1e5e16d4a754b7fa3b3ce5f9761c0c7563518d204319a113d57f10c8c30d5714727695f60e7b8e66bf57a61b0b7b0f366c1791d5fbf3ee4fff9cb26c000aff73799c37f20ff7f192b7afd4edf03f814d57e358a5c53422421669ca69ce5550840b8475b1044c1d1c204a61b61af3ea82cf487e90bb4684b045599e9afb1bd551b2f6f1571255d2bea727d2cdeb2bc96045e717d3c866a2afe57b57e43f018e35c37a231220d8c2e3b74a2fd2bae8cc5cdf5f295a3ed21bbdb415af746739cd5a5afc47932751450118c85348455d352f8d61138536c4f6eac0829a3d7c85ae1dce77e8477a2180176e6dff21007ec543ba808a8ee9014fcc8953ae3228ded0297b856cb8babf4bccbcc238e5646c06c88061f154331648b9e51a5db10e04805b189393d592bf228a6764f3fc123a1f6bcb6a5c7a0b6aa078491fd3a65b78381de1687be99254ec0937347f9568980e70652b0fef6901549db3442fda73af177c3e07d5a6beee297f16a4fd0f942a58447b84fd2f8c1d8f721c126c6815bc626cb88eb2d44e92f2cb828aeb9b5f82120e6f7c5ac12ca49461ae18b69806e75305cddd443ebc832826b65fcbbc911366466fd5b8621399cadf3f5e59c04d0bb89475b7ef43ff753e0867516c47ccd4881d1921591c9c102a82371eec375745867e51916fa99dc2ef666257c8d665e05d9098a59788388babe2adf32103ec6661d728f40c7aecc52defd20a2a640107d9963c0abfe1497f9ddebf50b0fb74a79d9f683d5b094760ca50883e0b9f6ac473e7b17535ffd1b6f0e1991841f89a8253278c4953d68994ffff131bc4d17f5f3db01e74df94e0bbb7a21b7821b5d8ab51ce288be1a8cb9a6b6943d252bdc6535a151e6693030f5faecccc008e18042b78f14b4b082db325c21f58a5c85e18248ea611059641e32f41414727278122948112a198d05d14091e53d46074dc24c7ff3b3e3ef8246c8067eb55505655ce1ed4c27ba980a17d52fb6f41352802a9bb3c69248706bbc8f96cc7b5fa03c1686a9b24f82ca13a9491cb727baa70f123f5fe141e90cedd2b2e3c112cf6c86699d24c829ab3ccbe3a4d93b891e8bb57e3dab70076e508882aba4b2e0a0235922f89926e5a59bd352b65343e8c3e2682fab57b36cc73b18141ad8d69ac1149820456831eabc3dd96f787b163b4c5c1e7c5c4e4219c14760a74eddce611a3a7be33fd005236b1242c368becff57eaee707e31740bd999bc1afeabf6508294894d57b3eb7aac9f5222ab1b1b314731cbc2b38a914c266d9ac507850da4109eae721ae79ee1b2c222c20306ab08e04d7af322ee1382f3520da815a522ba98b7a3b82eb643363bfbbb2d4492da5980923f37153be92be0074007448856a67883d0044b52c135dd7ec6804f2777ab7eb848773adafab7f2eb8b143ea30cc60c772ed3d6c97f38ac89cecf23fc41b975080550e7b8bcfb0937643968804a77d67a1757e6d004bf122554b31cfae2e3e7afcd5226e5487be980deeb679523c47f5b715ff96c69ec96f56c5cf357b06ba36c2afc30df38c1f57417ec03ed40913d6c490e4cc3f70e21b20913007c888aa63a7b39bd732ca1be9c8482b151b7c2ccd78c1db6c7483d6c10f7bff1dfe091db70b4b1903695e0f56dcbadf44e072d3dd6262732ab6d0f75264f3d6c7e05533f3988b9599cbe3c4638ff120ee6d0859ad23d1295689043680d7892691fd6444ccea62717e8e11849571c493b2dab75e7b62c3cab656430ec5b6bdbc1363b7a5cefa14ce5a4abc73a2ba451b333158e906ce1129bd9bcdd1e5b524a8d35d43c6f44c633564bd7a2ec710cbe008e89f9baaf3cdd27b83306d68ff1f85598f4139212a1daf97a94d0c12b4d0a4fdfffdcb86cb3a4dfdc78911fdf2d661057422739c62781c2d0985d66a70e40df538c2566ad29af2c75c90aab6aa4f1b2244e2bd1ac5f9f17f857f34fa692e8a921e8bd5762c74db758e23555ffcd8c17acb49e3f6fa41d3ed2bc42b8b2711fc59d0f4b787416f30f18b05db9edd3faad4db22f21e400818c6ac4ff389c9acbf67242b0da4652ac9968d101d96e2a158fcfb819c7d6a0a44607448150a16d1b5a7c3d4f53b09f2174d7f1978512d3f7b1bbb8d3d73c28d627108b76cc98403cf6e52374c0b976c54001eb770d821c59586c4c3829a9bdc765c410316401df09282195f8e2cfa066a3bd912201e8423121ce11b38fcd9853d39991269b63d2d0920cbc629603b68639c8175808f3244162b5fbce135e046104264f16a336f290bf9b339e6ea99f0e9176d0c3eeb11635cab78bb81d04db82ccf3ceef2fc97ddefb3b8ea30bcff557f003c8a464687bac1ed9e4d251cdea62dfd71a2151bca4a6bad751afd2e36fb60d4f8f69409acb329b5e58a8418242130bcc76cd4ef2eca071dd23d1c053a50a6594057e1567f4ada5be2ada1f1e206c39f7b7f80402e3da83c594d87ccce9f0fb74a39f900aaf5d8c2db75db186d4a3f6a4394f80193d7139710b50a59bf463d213963afcdb5058605e454f6891ba1d6c6a3e1d1046b47b83537a0c44f3405e81cada1bd7dace161a3ed676abcfc717ce3ea444b70c194899ccc8d3854d2d2f9445f1b2d4b0aa98da0b0c6236c8a88d1e7b0d38222057aa45e223f3927064629209da4cea3e7c5c1478817588b20202c5d1bed513a0af75aab15e24d40c8ade1cc8c066517a460d5c65d137eedfca6f9a86449a200ec12cd4ca441a724e24eed47e21c414093faae6a9c8b6ad50eb7f297d646637a810cf7f92c0d3f2cf7e8a0df0b1aa7bf03aed13bae12fe6a58bf1947ece4c780918b3b7a926125b1610b56ffa57dca835baf1b3f54c82f33aebdb9f1f0c9840c83e52b87304bda75842a3f11007bef835e8c4fa3358910e54d7c1208fc2c750bdad7e36d0bb3d2a8d60a5defedfee6fa28ccf95d430085705e03f890b5b2aa871387b1e1ec19123a6fdb1e0d3c319625fb7829b804d957a0fbb5f7f926de6ac6ba994a8e857e5e8cadc3885a4a0f57d32e040511fc3dbd86a160765cc251aa9188b267a5225cf23b20ab391cf41a126d70b0867016a3122e3f971308aa40eb913be0750d3522f03e6f0642f503202a4706f973392abeb9f161b5da47a2d82eb47ba57f4110ff5fe78366feeec71fc9fb21195d8ff39228cf55f4c0c2fafc778e1ed5e6151ca79d71ef52ae0a87f77015615658d9f4915d35a5f28d2778bf43ced6c6415981bdaae586ad182137c92b1b65ddfc9cc0ca2fcce039fa81e6e5c9adaa7dee49f4996d696204042871355126cf3d6c6cbda28c33d91fc311d9b870b6ebacca7a96d6d9467e0d4b366a03c535c1b76c11418917969af3d5040807b4588f90868a4b018feeaf743cccf0e72001013396fd42102340639c8353a9381b9a2797224dc589ab87e6d184386814651341e8aa616742e8972eb20194156d2c6a0001100648761083c26fd712a3c8e0a9167a70c6b8da4bb00f1b7ec03d352d2a3ef1399cd5cf3e9d24f970f773aab39edd1e9f6f572203d272b2a361977c81856a0291e106a298399c6b6c342f1d661690a3fd7a4bb6d46c180a99ef183c254330c2e1b0398110e7f797ff868dc323c632afd0924f195c86965285315e907e92c7603535db1ef2a7ebbe6b31266df92478a7d9547246af57067757511f6340dc8e2819414add8970566ef7b231e9a860fece979579836775f3f27af5b0a106eed61c579fe37cf4e40fe09e5d1b3b9dc9dd8a344b3baf93b51b6622b4b94309f6710c0fa7a756a2aa4d90102e4cb31b21b89e5a24a8bc4a5b73bec3a56bdfb82101f80263a25b70f83068dd8d1e64cc98e63b3aeeccb78d6c9e3878b62a9c41abda9f32e3164453e971e6acdf7f29f55818e7dc5cf8d5c1a7bdb1896fba395692a30dee4dfdc509a42c658e854c1bdba8dddd1aec59e43b8a2c0a6d0fb8865c98d269cd04b978eefcf844f76d99251a999e2df85daa8352343c13bb49a0d681ea4c513829e8131c939ffa81a34aec3aae07773adadb9660b79ee1f063b8783e5f4a833a405deb1f5a7991f87d0bd131d8f8168600ddbd0058c3eb0017b83c1b05588996f8ca7ce0562878a9a8266d889a03b5cf0f2fb63be78735f9544ab4cec7ac59c16dda5e488b185b62bd528e9e19ffd4a52e5530df56d0f0a407be88f4de93a16986c0ed4e3e6433a8158e71c20d23c0bbfc107c79526e4557e05e681754b0571f8e8a5f7b03bdcad40d29fffbd6ea56a614b41cea3942dd73d6ba7c347ea9471caa6ebf7d7ab348fcb822b808929cf9dad5741f9556a6c73af196d4bf904222325a45a9e906976e93fe647b8d8adf09e5861e510a821b54adc469d9d3eac678229742a0312da848336eb6db59955c445d3f376f6874057af80d7dec8bcd18ccf140fd6b0831148c775656fd9fdea9c539f62adbfb21f3bd978e3926f4e64fdfc806779260b650e6d6cd0a0d534449e428a18620c98e070876f9a3daa5e5c5c8363ef1b6f4bacd049ae85b09707c1a5eccb2b26e5deb424523ca53d307b01855b4b62f416e2513eb84ef45f6a4f2f4d6d03edbbba4b05b8501561907c58cf8b106d6e2ef3b4bc1605a2ab601c36be51116a617b2c2eeed06e44027bc7b1f4cb023e3aa5de92803782256b75c5a0c7e9e30ae6c0812ece56e00b160d1a47562fbfd22db64204ea2657f713ff995cfec26972b056dd41671e17bd693c81feafbad81f73022d5f5e955f13a1de1a6b2f4fc4b975d0b1b21b4a213e4c67850abd75569df51a3c516d11577f2ba23d320b1b2e852469f02d762ef1684d514a8de7c7c7c4068f2475c52d2058964a6fe272491712c254e183cc293667605b9afe4c7151d6b79d3c4eb2f493e2fcb1afe4753e212fea6daf32293597b00035c4a0d08268ed71fb9deb84b5e5dceb2680026cedff5902994a259c44373cf7b15ec29e0893994772278f74280f5068023a0ee7b7b03393f0ef95f6acf13b6f4f3d3543124c867e5ea070a4fd85fdb7ff604b8ed13bc6a747abb9843f55de90478b66a21861d86efae0a8e53d0fb17fa9d055afe7a9e2c8c3f007c874fb6814077fc863f66f4141a48600ff7236b7602666a599ada2d22e6e0a968c803153acc98959906c20e6aab31be32554f4719a89b1a2680d12e9dbec0255699f3ea87214c999ce0c86fb1a23d7dd6d40c225bd449265bf500f6d6a01d7b50883eb7e4d32e0e657b50e39e36e1118274435dbbd6b30ce624509cd7894cb910836478f7fb2a15611ca792178d57f78984459496e97894409337b6fba4fa1d807f21a1d930801ec10608091e3fd4768f5f4b513906c11c750b15e72d4e6c55ecc9f87ce9f4100a29ed52fc29d936356e72a82f24966ea33213db9ed55439f90e7fc720c516a68e6a740d0c08cbc83f5aca4259cba5b2adce4b1fb689ebfab5ac149ca9957a6c801dd93b64fc75b4fa8f9ac3930145710c63f3e78ca0fbffd058d447b941815fc38ae182c1ae539b253bdf1d3b43829bce332157d12743c76e2a30826cb9de44a8415097f876b6822c22d094d35618045444bc515aa0ca28f3f14c24f0fdc1e2d3e0f6d1410f37c88e1057c35d679908e8f50e40c91e350472d468d34590ebd05a11b88587b8da6c8a2be46370a9bef2b2d42cb30e87904b5f6e345d335336bdac3f37781f8abfe29f85b9a4cb8d450a78cdda1be4a18b3f31908f2ce85805a0365dc7677793ab8d68b8c7af6438afbd211ff9109f0d322ed630f462f2f954b63d2408c0b2847113a23af6d434d0e746ff24299f9eeb1647e1fa4da63849f01379673dcec6875d470d284a24f9db9ff890a47b1fe0b55755fa998a6b32b88102dbc84f4c5796e7fd2334b6f15335baced92ff0a4cf253a6676c71983cf893eaabd3e93cfefadf56fa4da19a0bedcc46fc5b479fa7180307071475cc03d73cb686f2de4c9fcfb87d14d1cc2bf4d99fcca7b2a3127d1d0738579b8651cc32e3778afd3c698c10c4c1bf2d64a1e41fd4445b1cd10ad427cfd91d0630578aeab84ab548d00ad4a03d85d04edc9f4e8def432ad47ec3ef7ac9d20a44623a4aa3b87cc3a07b14fb0c07d313d02d1bfc704e01999139813ef1586549aabc0c1f984d3cf7df11b3e19ce06f67e36d4093110303fbf0f4668d8dce7a512ad94bc7f6008c98c3b6c036a0c7cb68d983a3c39a7e0d0d63c8a0ee75c70da24dd1d3484904804ad6e1755c92148c51b8d9e90cb9289cfad2180275a015408d83bc7ce12207bc48a61d1b9519839e081390850d51319855e220c5df296c52f1fdb01e25db2d048866b58de962590ce337cf77f986eac751857f1392a64f79707a3ad42bf6f1186414c7514cc5a5e2394c4ce8ecafa598f4f0740b7e06c982da5f7f6c866e4f12b07db104ff3f264504643826048cf83355b0323765b9330293837056746fa9cfbfc423e7eb7e96d0b784e53679e557bd2e24199f122a1c849de09a72aac58c58a18d1f44e116fa914c129b325f1c2e01cd00a4462d68f2d5765cd794064c37e278b90f351057fc2beec202c946bab29efd3693ca8fc6904c81a8ef85de40e3c3811ac6442ded8051ec73b2c8660982b8b271145b8d68477764362962880474e9765358c39295a8fbbfaa0c74cc2a2081888d02b6f094f9d72a9f722fb1ed0731a00ae9d41937ce9a420b850066b515d6a96e209c03433857fce0c51bed1a1cc8e8aadb4a60c999b8a894e7e351f5d470c74a5ce15c1617dacf4cd5ad97d738365df496fcabcf37998b52604ac87304a0ad52b5ed19b720b66216f59c48e8ecf3dbe906b1b271826f2c5872c55778ba6cd9a6ea1a42fab8ab515d39f6c23ad107179594ed09078d4ba3c8c7e104adc9881fce03190c68cad30b398d88c24f4f801f46252b6f12343084f844ada05d13e1b33c220904e913a2dc63d3dee26f15c4cf27533a8fa7f8e30d3da3950f93229f4da06748b773c07c24bd0d59a0ea502741418a980389a52ae296a3380219457e9493b46400291732e3fe7ca259a25e8c58aa173ef66abf170eeb4bf1f2e591b4952182315a9860a117c97c3ea7fce33a1a9334d1b7d7862fefdff14f68c0f1606f2c51524bfec7d4297fcb2d3100d1a7aedae7e05333a16448d3a7f0b4829c60b330e672dcb38c356804b8790c478ef74f61fb96e3c90cd01c19f0ae0a6094b7c3089c6e1cc396573d8c0783d561b072a6a3bac71d8a391e69be35442737055691335886d5bf2b264b7771892920140ab9c1ba4d7fd81c6f561e8e1344662c1769566d87101806eb9e1b9e5eb21a7b92c2e5ddc85d98b758e8935a37dc86dfb6ec9f460f80feb6ec7c2c5b929fd6faa2d7d692377f3b8e4bb33c898942c2c23e3a332f2796e88e22f720af86f8650e7710ecaccdc5879bad1d5e46c41ff96ede80bc0b69e64d3dd167915d149ae8e7c3bf8cb5fb0911f2794ce0bc68237a881231990eb324cbddb9b575f86cda0678d39c0aa054a20b2521396f363198fd1acffcff81585cc255115c2fbff2e3bc218afc2b4da254ae2d337c4e1faefb532ee53d8065161495441ac587cfd391717979bfc274924a39e4dcd2554ba1533a888da1cb52a47ea8e082b4a18b86e48cfe3acc54d2f4c64c82d22429eba88b1e9e663304b95df3e22509666551bf971dd3cb5fc054a82b2e0212600e549b476e726010037c6615cab92849bd247046008e9dca88207ddc5cc36310cc88bb284d0836a66cd231340f36f3ff6fce1e5bbd74991ae4364442d04635e6a9d8fcfa52a4425ae7c82df238a8be7e63217ffd6ac3104c988e2894caf1a12450da86b29599c4fb17097e4f9a8e7ee167d164d1990ad117a08469aedaabac3c919c301b5babc1fef4e135382f3849deb3aba67a045319c5b2d61d8427f138d741b8179bd8f6d36e73e4baabfef963e208ff419e65143c7a028394e227c53502eec93591522dae8726ecc0d5baa77e73f5ac39707c27fb1004ae16bf10fc565d31d4eb381c6cbeaea0e4f3b99ab9f6e30cb455a017996c0fa7ae1eace7dd644f8f6ed2e9d9171461403977559dea507539d8157e52a698a82dc88ff53678c3d0af010a441bb0107dfbdf19ba403412bb3a41bfb7468b6258a77f2d1e18434b97e09385bb74e90b03abf7a9a029d780ba2cf0efa68700a99343152dfa6adad5123f70c3d9eb4c85e3ecff798e54045b3dfe0fffe428bf31c611ab08257d3a75b296d2da6d7b94c2af28ebe1fa78b4cfa6a24762cee53b29b85cf10f3c42b401846fa1d800e6e549afc70e9884237f520da7a68a0bbf765872b57f26bf29425e10c0770dd192a94944d50d6a0654bd62e3513bcfa1648e339abcda083f8a522e3ddb0b4113c5b8e955f267a5065b64eed0c24c983cedf3a36e101afa0ddec9e7a2c607abab235b13f794d2bbe88e585dcd959693a93cb0118f8392a649b26f16ef1f93232a1fd95c522c740393310ec318d5a37dc382fc3ad1fe3fdb64cf44a71053f0ee0cb60b57efb47a683eec6ef4abff5fd41d8b055abc79364e2b960376d26ac7300ee4ea583dbfad6f2784362e8a9489aef24c4ab262b63df667dda2ae6d9c3eed0ff5a445d4f031369dabc861db4a74b0c5124f082e864e3531d483c780c009c2a66de1d0be999ad2deaa746500c2ba2e4c797ed4f57376ab2d2ba017af1ccdc1ff343105e4450dc9172bcaf03966a065669982f443f3cc572154758637eb7b02f3c396249699b59a461a74df339b3f967b1269d1f5551e78c8a40bcea004cd0fd216664868867f29524eb75efd9bb71e0f08f9f21314803697c583e9384ff5da01b7692606cb6c51356ab399e3a41130e782422102cfc5a095c3a918b06b1e2869730590f408d5d478367f5721e368d54e75d727594a778ac4a3e188a44305f0c611cf1c7ba90b0f284f52a245ac0e5667b3bfe9bfa32d077124189fca04c8fb7fecdb48c99d96d497c1fff7945c48c2a13a9c4ff0885a25d5fb843e7f80e21b7234335141d008ea78c3df60959c8467398beb19c997128ed67b122def88facf5c52f86f7aa1c65ef017ee387344a35c6e2bafccd256f35d5ca195cc43e88deec202bce1fcde419fd59d3abf12067be1d67d06c07adc87eff05ef3ebed3fa5b9621f0f3cba6b19d26009952968f7596f313dcdcc0ca6a423578720a1d430582ac2908a7bd295c8992353bb14a092dbddc4a2b9c6264a4dfcbf26f0e8fa4bd8d64e97081c7d1477365f97f79f950df70e9aae1224462120cb029c635b8cfb39b3653f13f795e3f2094937cfeffca4ccc8c09b8334ffaa1ae8750024ad9fb89fdded472fbc6ab425f20e96dd2aea6acb4f2d15830889a0531cf9dde2bcd713eacc09648bb7609be257521d59f2532de6198ee98cdecefde9a1751916c76494ad36020b95c6b8e0a83680eb4f44297ce15fdb21c6be70a170f6b3813f63e1eb0ddf1b239e80e95c1fc49f25672cfc0d90f849fdde9c3bf090e6da84e30e999500ee7337e691d71eff4df09e30c3bb222a55c1850df266e1f74c2af02c69bfc06c2f46d8656ab10151600a57737588e18e3ddaf228643cbcbe77487e3b9b5f14d4c8464e7e980d406efb926d465f5cd51d1c1dbbf12fc3d8cd4e43cc8eb91bb477679b47d7288e8f0c3ecbc2e52df50d882682b8911f6c5a05b27bae01988015d59ce3c3e032ba4ef2d7c2d9514318021404a9cb6475c559344fb1450b148bd8de464b2ac2e2b89cbb4b7ef079424a5b4705817ec876363262181dda41d8001a071411987a54eee566b1608ba445bad5ea61eb6fcfbb219dbc2c5ddb90a2c502cc9370288ac84e3e6d54ed60c690f1198778c213f9466fe8dfd7037a13744da90bca11633cb1e4c23776b90383b5ec52abac49558aa2e7c74d1bdbfc58e39531fb0c312705d4ead7e6f1ecf709a7173a8ab4e97cfe5ebf2644cd4738a9dba58f948316fc4dfa301186144c8a801ca7f889ef6091c811d108ddb5721b4684acad063fbf0a28c1a1b467ead6d45d7077af719219078843f3885a8e711b4be17123ace7a5f845b8fcb4da74ae5c847ca4dd3e49b65eb28877f1c89731902bef392605724ae61300a5f573f53a74f6bd9ee47ba098e2656df384a4a1a1d9b04df1d000a89ab229448d7bc6e62f9a399e30c89476593f5116b67484c6396dbfa9c863342f2af43cb0ed016852c07a1a6e94290fd82a901d2f25d0940a4df13a90a82d156a0c6db95e7ac70e622b829da16f6aa1e3d6d50d491d735a308de22af118d610934a3aa71189c5c3579f7d256755cc6379cabc02e5941225aa76a406fd3bfff7040381c01ba0fdd52395cc6567860ae2c47b31d62e9637471072611c886201253897644fbafbeee42b7512381d3bcb76d63bb465cf8b52c0bb3138ec68f3da34ef533edee7fd38877fe1dab45308da756d0a0d4dcf636d6cd5c09c61114a4cc28548121e9775b72e9fbc6ea6baf2ebf2e11a650ace454382e9ee75185a79e4e1e8cccb83f9987b3ffd946391d1724dee56532b60fc5e9c304dcffc8fd6d7c2638e87b8e0c89f227f28962fa9aba2ec7daeb5fc9a309d00ab4e8e0c4ab70bef20077f5d47794cef872f463a70f1c2cd99f50c6a176febcc741c9c1751e9892c73bf40be3b5471fb7d01fcb22397aae5dc2eb5f86bfb5d1ae5c85a384c362d1f9e58ad5840321e911bee8c449bd489bd07ff9c7442defde6ab9827fe8b20c4b105750d5be9fef585193b761faffb2a82ce4b535f8fe30c77b2ebf178dde7f0e484a4be4231e00a31dd514806505c1c46b3e6882fc31305073cf71b490a1e6eca614fee30c70485a97d470e1711e7759fedae6a25efc99d7a2c7dffe2fc7ffcd17be07d5ed178b2611152c82c69181fc751cf77d3b7fe8a93c7a9c04df6d7f1dd3d3e7fb2da6077bc637cf9b7c880e21e1cca517f0817da785997eb97c99bc08adccfa5610b78653c20a12888404470bfc896d4d03d2d9cf6396434126dd326a97733998f11e82ba88c456a1a897bf512aad9fca3500dcb2528128101a395e63b5bc6ffd24c069b8f5efc7de3710f5f458f48e279e9daf8011b60936c982df8733c5c275fec22222a7101f5c22c985ad1b890ba73a82fbaa697952833745d871d710b49bc5730a4189c90c14dbdcc1a0a49c3535a6cba9d394480ab004d2eed7acac25ec33c0eb3cecd59e98d8db11a1d84c15b03779ec0bcaabfbe04939211e48fc40244dd0499c3e075753e6159e0df8b6cdcdf595fde2531b193e435fe0fea9aec993d7f0b6d492c656904fc509f847516510e3109bef7f93e5c441845250ab4cb3b3d2cafb7c0d1ddc6990f33f94abd402c1755ea57a2996e1b774e5ab144b454742140c7243f8fbcaac21a2308baaf947f5971ded9372e0203b26b53c379d6fcc30e86de854320554b32e889bac7396b40561e7276de5c79d3c871f33878f984af500d76cc73229b39f2f6604cd5035d96dc396174d9d854461042fd80edd7ba511eb84f5bce64f05efebe22d3d274f4947b5a12a2002174ca5ccdcfbfff1064f612a49116e17d06b2332b310899e8e04c2ebc55e15523d411f7bc3249eab2d3d0ebb311dd86f9fcc5fe81ea109f8bdd57aa0030de1cfb89eb916cf9bd81e786e29bf627709d335a20bdfcb332a7bac7b5bed134b90e9e094ab1c2c654c7898a5ead3c352b313095b55ec91cde6aec6ba0d4643a65266cb22668dd9b8cd8bb333c8765ecad3a86bca291deeca48a15d7b24ebfa96954f699d41d1b624b03f55322c605b0d3f8f3066b79e53d2c43315e5cd14b0d8411a5378a7afc745c5974799a74e13c5500e2831a0e7353d91b0fb64a2e4f7cc3ca6d29f06f489827243b0299d19665a3fbc1e1a4e3922a7b83177e5cf012601c6c4bc460676ef8dbcc47ce367e4dcb2efd596f0ba98d688e198fdcf488c94f6fef27d368a419684b3aa1024f7c7c0d884aeae60f66996b998fa8b29dd71e46d7a2c6944426dcba89e9447d30afe444f69924c9cbf9986577d0b9929710928da1cff9a8b85cefdb79c1b21bb12c513119bf9694884c8eaefba81dd4893c93e0d97bb8b70ab0843e0d36a24d17747a7e7dc26db9bb61316f94113af354b21e46b608e7d6a57b3aaadb3402f68b8151cc740aa0eb10ee8da1e3003bfb004cf0132ed0289fe784d7c8af65ebce7d86ce1e12d438219b85389e7c67d727c9be6683a12871975e3785ed7ea43f3275a3b78b08c8ff8bd2cf4c2d3dbec2f39544dc726493ab3ef2536f313e6a77bb6d623d8e8e1b925035b53a66b429ef77111b22d3f9db082c9e24d5953a28d818bd47ff27b602ff680bf42f98461e12166a841f4e1227f83a97d0ba85f057257243dfec59ff6e8a16238188228f8d416be52ed655cb0f5b8d7ac508023a981cf238d385a66eb102647e6e2185bae0c4568c1efb09af0cea464cc2673eb0dd5263e473529a6b66e643a68bcbd5a15a0d817ca9a10997debfbc8bcd4bd0ccece7fed8b3d5ef0492dfe676c7bfbea2b52e792204566c96c39154b4a62f2c031cd0761a21ac84a8a9c694d53251b544315675a40e9b407f4807b8dcb229be9a54fdb4eb0a8e58ef6e83f64e60c5397ff2e62448ca933009c111b85c53b247456a8e0cfeb56f9f0f31db62cfddcb24cf35a54951a7724a5dea6139df116a6102239d2d6b2734e0b9762b3e0d11cbdb7ecdf3ddc8262504ac39e0fc33de223abea3e3bffff553220fd7c449afe7c0d88a5218d28b0ae7c597da4638503d427903917faaf9df0df0b526bbef8ca7427706644f8fa3e83610206378234a5605d68ab44f7549f7bc04bd3786f33948b60d25225c619da3fad7b76b38cde5bb24ffbc6f36eee4d57117037851707b10fd5b2345c786340c2156fb785fd1a24277bfb9a82f52be0c448104281cdf1dd4fc44bbc0ea535b918cebccba708f78637e2fc23e67849ca2da7055977fa38c8fbf2420b3d68df73d47f10afa87dcbe4315d8fca16bc62ec365f7ef8584d43be3cbadba65bbd11889596c90d8765e5f632487329acfd2192b74c4bbce73869f68da7e364b1d87cdeccc48547ee376fd39bfed6791e9df7fb7618b71342c74d4cf9706f9a4cc4c7daf4c59e5159eabde73f4db0c164ad5b5986d614bf8263ac728375e2a722c1db8ea82cb74aa312b5d85b66ea1fdf6bf2a6c8e781303f51d0d89995364e86d46b70f72baff39b3e822933376bfd511ff4fbd877ded65a629cb433e8cb1b313333bc0336ea17069d8c460c9dd54fbe16ecb198563e4aca3f98ed055befe295a58bfe05018819cb973a327ddc44c0536c1a8aa511ca8694549dcce404661bdbd14f915df2c6ebe782e73ef988a31b96a025608d021882b68244c841ef86217ed97146e3d59e84dbc61290cb312d4c20271f55f2a8931a4b1491113401ab6cbbb94e8a0151282eaf34eb423b31bff23745a752e1aed2fc57568ce222b8efec217ab499146b3741eb9433107137326b8c5d3411ce467fcb4fbad9971f5cab0334025f33a6d5eac4baf8caa953fd20418f3a08296ed1e9c69ad84f9f726c98465cfc0c440a3ea6b57fc0f8daf160dc557219709cab043432a9dc01de9f8bf4f551a1ab22d802d66793c887da0c079b3a2daf731cbaeb58cb648c6e350b3118bcb80b5be4fd7f2dee182271e7935c0466aad2d31a12f339c0d52029b5108d0702b9ee9aad2c1d0cda345408f90cdd2d60ae9bccceff4f921a8a323d3d5ac6af26e61db8cf7e8a0d8f00c907cedd730ed13a3182ed2871c1c9a2e49d40f5454eb101e9cd94b7a805120bd0e948d34bacac14f96d8e3dc6a262f3278cf3fdcb0e460618db29ce2df78e39e83d9c7773b3ee3f8e24f4e985e78ba3e307972d9fc277b0687365208ea4c1d38285bf1b1ebd6c386055c20145d8e6060d297c67962d049aca38828fcfe5bc8786c74cc5c2c21c0424839984708b7170cb78577aed3690e62041bd03748448f749d169cac92db3e24de44a5bf5f108c379599d42cd898235c907357437c2367167ca841ca4652ef32556f590475586821270b2cbded67fc0786905064d14c580608bce0255d33f21d2949fcf1de07eeb63ac3ecf8b9e8d632fe5baa3997d7c2233a9c37bce1c3052990fe6ec4f1a84fb037fb17a2f0ff6e90efdb52eaf07a0aedb8f3facda4d59fc12611e01a90f71f3657abdc33b0f7d3d02e772f32cc8037ee3935d6b32935830d69c034a1877bb73da3844a5e4a9f5fc6cc12692ba6eecae7c89bccdb462dc7c81edb0a65a97ab965777f8dd6bbed9d734cb36d2493d64012589bd6f529b157c893c866343f17bc87021110dac1ccdc560f257483b5a9f42ce5645f4b773fa84462d7227c85334834764c2e65d0c410a134b5c82e0aa4b35ebcd80987d907ed47ddc5fc9a3552e80bd0ab51a1201e2468e1455f3e7b1ca9b63c6f2dc1641065dfbfbda817b65e6553d74a42479739426c495dd81ad0ece43abd657f564f1ad5360222c8da00d1da0abd66c88176dd4e9ccfa113630606d7817883765665dd308e3ffb5a44a99f0561c2842e3b4e0cefb64b6b01a23f18e5f6b55c04d5b2202091eb47bf66bf19093e0392c6b0c16868cb8e698554ea7683c516a9e6ca7848de0018b77e31d86dd3f06256d0dac0312ab5bdcda836ab8e7cf663a01a2a1a2fd0cb7d5c5f1a8e462519e58642d1f4ceeb9afb76f59f7635fb051e116f555b1378a4432a0633ecf57368b993ffd94e14407e35f9711aa62fef293e824035ddfc3cd5354e21fc9576326ea79121ddd05e12a268064e07d0e023828f16fdb72920c5749765a32afe788067d0a16f8d9c09ae624b065d6cef8e79bcc44c02675fe54964a8236fce226ff020aa951bd64826571ea289017e424a53997be347341abfd865baab5c76fd44475f885329fc7457d2cd24c4bef6163c0b0d294905ca9f3569945a2bf191f72519a05948d7a60937c73628493b0afcb56e30bc0cb43a7eaf02f564dcc00b87a6f18d3372cf3d795b5082e9151e0854d067ecb439a02b17b352ec0374c31177da92308e8a294a00f2b70876d962f6fa30472e909aa6ca540627c651e453e8a3dc8be31fc86b78a76d527596768d9dc328ab584ba9c3a838b71683ccf914184f02c9b86595b556edc67e27d003ece8525105a4497b75b2afa1729b6b3cc5fbe8f03bc7adf08bea68d4f1d1f0691b3dccbc1dc52b44e9247326ac61ad3034e66d3cf1f648d0e1cb3cd89df5fa1dd0d1703be6c78093da98bad0c6f1a5632415cadb8ee7e146804d37f48136a81efe221432bde0a79a0b524c56db635c5dfe6220524a5e2028cd055397a26f85545c7eca12b78ca10faa0b2d88717032d7e816bfea16f0f16b589770f19b8fc850cd2e4e84f8717dfe2433fd774ff40c53f428eb7b89e4d52010334c1f9d27944070c42c64b59251c2eb6117f574bab20ae84404836b346d8391b25bf4236dad98b1b63a7e68ec0aef40502d4f8fc7261d7f63444b7622a10816899e8b8c770db00027133d1cf56716172b3f639b59a701b37af34dc76a7de4776ca77cf504ced0257c27a57e77f274532f4b217177aa9cf28b6bb5523adaea03985391a34f67f06edb10f13e83e7a38279b893db6647c8845a4c0259883577a83e4d3e1c945f044f3d1cf1e687369c3ad036ddddca3b6eeb42dab8ee7b9b81ac8f13750324d9583a1b8b5322b47cd4a040cc5d9f823fc517cc2e3e405aea619c281d02727987a7b134a15de2ff0b560c1d0791daf024011b7340df5da612c44e5eed676debec01f2080390883290a1ca257e103f15a8288c2db9e230f0b004600f6fcbc0b1755ceb073cafe3745497d4f8867d2d109856a73e8d16ed037e07fdeee6e3ab7c67e6a59713e8197ba7a7e8282b82e7a0f9d5ddb34e82c20a3bca7d42a3386c233825b5ecae0800d821066169f955c3effc34f0badefaca5cb16723644d00e4c3e3c564ee266c9c53a66a9d9c05dc2106b15f0e4876d42e342641401e4b8730f323aefc626b4af118df949e027c187f312f8240545fe6f7f14f15fb70388da8805f16d27abfae51cdc10071cbb678b26f6f4299ac1fc26a500c0e9c5827c737070880462100695918d3560ef30a3a41df5f9b433da349de06762e0507e14ddbbd0962c245dcc2b9a060bddd3f41aea6824e2a160b4ce4983e04ff88a1e524c56677d71305ebbea4387e46b28dcffbc20fc7db41a85ecb305fd5f204320f0182fe34bc02ff9100ffbac7e0a6a969b621b1486b9787d4e4ad0543bda6c5e43debc84408268df2b1828d14d0bcfcdab1bf52051ae32df0baef37ea77a0e311b0dc817146643c1b5eeffbe85ad49c757291895b08b65ddb39dae54bb1e0639fdc948440e1a792e7d12cc48bdc7f5bca4cc98f8ebf778de000e92bc5e805a709cd9cae0f9830be3597d545561d3ec418e3a8122188d250cccc578600b39b542ea3e1059c2b563486c897842a89b5196a1c35c480847eaea307fc1ea0ec38d904cb0224d2cb8f5d53afd32b917a4c7fea376030e6da56750540dcf6cb23d760149f7c16344a42f7607e097a3cd985e2b35fd77afe22e04fdc5b0f1c2e483e6b7a05b675c9f6035e29723934d60f050c43d381d328247a17829cf025431b8e7c803561508ebb35091b790b72fa5539927a21ad090e2df510662b897307a424cd380cd252f3f231326bcef276adb13f3d52e461ecfefa94ee89fb3473afbb62b0969f73ffb99d67b21444b644e207839278c6598a96dd5fbc1a185e1abb6395ff4927d14b707a2f1d3faf58f85ddf6ecf5329d8561f814c51de8f6c1f19e07f32bf9323ac5fe6f5bace5b476c4ae403085110bd2ad0585b891208a40b54c1fcb84d33d8cc3d9c47cdce5ae1595c4f80007cf4b1df3b644a123076d9adfe65f2d6a1b22393e3d01733a7185b04cc5f8b6204973269affa3494a963efb2dd603e7950d6056610685079e5d95db507a5c935cd306f02398d1564103613cfafc2a0ecdf7f793f0c21113d2686b6362a6e40f22773b24009ab6e91ab26964612cda4a36bd20cc1e9615cd0f73422a2148ab3116537ecb468e4371b3b65c2042d5b8778946d00c8ed0f8ec2bf70e329b66bf0fd1679d88e35d1c72361e3a30495efc0ee9712edfc27f0b6d6fe539bdecd1d2ed3f40e74792e74ef74ee3c8704ca4347af6e0f44d7a622aa6c1bba98f4d14643754156300cb84fff44f40b36835090dd16e719fc99e694b982988d8f24e9c3a8dcc088d95a7862d863c426fa48724bb0b90f8f78096b59735d7ffd708754e825fe405d3a71c2dc42eebd6099e514dccbdc75ac613c194ac9f0602280a83f98a7563bdb7ac498ea6d024d28719dc3ba132f4a5087a1b8d5d0dfb10d837a17150d1877b87eb3689fb6bd60d373111ddfc6a434983b6a5c0533ad4195359510df316f64046c7117e19b31685e6c60d4d875ce1f216280b00a546b19307ad0e381d007783e58b793a3b5b0e6406c992d0801d0df6c99ea6d6d771d9256ef949b507887a923bde54b2eef110861d33c1558870321d30100af179b872abb3c207d311b7759c32ad6d06daeef3280264afd14814569eb10fa9a18a25b78ef0e2aabfe2660403075a0c9a1c6fe3cf9fe82d8dad96ffd3f407cebc7ed4704c8092e5a8d95cc2e438cc606ac1e092d6ef72758c2d60017803caf0ad413bebd45eda701de08717c768da7b440b433d42d04dd0f6f973df16506a246b2032cf24e01e0826a0262da609b87914d4368bb16ab0c35c16c8cfe3a3cc738ef4e3a56a810c8c62cdc40bb121da1aae6c911907e75f42cd5e0a756212135650e0ead81f63ac5f8c521e522d86f0ce45eb8485f9281bb138b93430395f5e5e7db556b46dabfd0b15a9a47f75d65264f5123ab95422c8e5f8ccfbb55d481f8532eeacedc4f45d86da88a31ae93a0230cd3e45ea2e91624eaa5bc3b63d74f868be2fdedd83c1fdcd24f5c9407e7eecf780c5bc74701b792c788a146dd1107348c8f4cb69a39a93081361854d99546261afa9f2e7cdcea7e3e361063ec8f54e47bfb5cb382690b5036d20e8127437e17053ae7dd9ef5f302e3ac9dadcc197e37a346b3845d95e50e2f023fb184c9948a2bd441d02d840b3baa551082bc50f6c2f5591eb2f94ccf04e2e92aa970d2a53ec382f09c3841f15452f331062e3a5041d89eaca6464d24171c394f3f568962cb119a292da9d6d0ebc0836bc6a791260feedd878947863fc2f30bdb908a479091a50f70cb363740add9b19a26056101634e84226c971caab804dda3fc9475774245ba63453bfc01d3a1ea4771003fbac56a7d9ed25c1032ad0a72d71490e9c0f1eb888c04ca2446af89f6debaa211f1cba467c66f9b6a7f4f541587414fa11df2593eaec07e4ff0702e3716b723e172a41a4e58738019e2c0c22bb8b0daa92370a6c2533899dcda7685b4878e452d22c3a679a60e4e8be9e9c57eacd51d914a3dcd28aacc2b79b2e19173d3bfbc47347ad9f79b704e82b4bb1790546763ebcc0e79965aa6b207b86922d1b24279c28ba44742bee3892b74bad562fef0477547190f8af031c33a1a2a32dc4de3cbd3ba7fa0141ec0d55fe0ddc5f23d423488e13811d9b7b03356a9ee70d1e919252d542f4534ad0836817c53a76ed9dc4d45980654952305b73a36ee7964e7f94c7f33c22ae2e9d385505ea0b89fde8015c7a687840029af9eda6b109a4890a5a8ee18842210ba13602811fbaed726eea2d95cdd7834bc9df1db190db964d058b1f985ceb10b50f94fe1a7e65747bbd457b9dff8f80eb00539a7336d2b681619eedf33a09e5f4f96ed3fe12c6e93a7e259386865e6bb8d0778ede5c08c95939e6958adc40eafd8189cfffb2c65f99f4c9344bd7b29e470f32c874fd102b3dccd6bcca3e9c191ba38e3d2c91a0801a2032907f7c2c920085ba8a71b7b9939a0961dc9f796c0187cdbd07919eee538f47a6bddc982cd49c4ffd129e76933de8e158f44e1353c704c26e42891897f58cc8b2349a67859289c30e9852c974c4030d398932ff5a1cda80abaabba6ce93ef20d8d8392d4a0a3e6c7121c9cc24190ab68dcc762df4015eef827acccf11f554ceb6594ac7c2cb6e51ec7c03c3e163b942a750bfc7f4f1539c33073c142f0b1be9a69965399411dd3f6c4df9f5d9912eb3ed09e1191e3093dc1496e88ecd03654e65bc1ecb14ed14a60ebbc913cef32bff7f8f063f43a242e4b3402afca766719d56fd5325b3f1788d49b7fa3742d5e3d1b0c8c7a1ad9ef0100035f7d10a50ae442e4d2d9c6c8417fdd3cb3919b9a2bed815238e33b1161a69aec9ca393a875fd823e8ce9221a276e2b6c13b3427167c99f768b31db70f8f56648071e73e257eaed4756bcf9ef34286b3661a23f93f41eaff6e00b6070257eb736ef34aca3e06002ff8ebfe27ef0f3d65411087707822f40139e3223cc1cad7af9dd21afc0a6632e0e87e838fc3c112a695e1634eccf67dea9e761fcb4c24f0a7ffdf576b1b1c1d5c6734a50b3621a1768330275714d8ee24e91dc05f7e6871fbf2a20b1a7346fbcffb614e251bfd8fef687dfff77d06dea4d1e09ef2fcdbeebe6ffd56f4f9c84ea71e97255f819a5ce34d406435f24d75a1e1f2454410768a5a780d6cda24647a5f968b418c5739aea413e21c8739680824e1cb4f2e23feb5c30abe397e0e0da979fe79918f4bbecee5ebde3df7ec169ff1001f6c8a8410eb5c146d2caa3d36e11c7ae20a651ff4c49999c0c76e19991ef4ebb247fe84970837c9fef1752cf4da64df791adee734ccd1703b1967cda82c63dac5979609062423bac67a1707e7dee077330c40e557b5dfedcc785a3621cc2fd423cce8928a314575b0e3c4765335e778be730f55decab94424099b482cfc29def59c8a7dd88d6799a77390c2fa3af9a6d7d60461a26be36525d32ea93333af46bcd989f50878685f61081e87d42ef12d8c8572eac67ec459d29fdcb1c19ce4429163f726b4d86de1cea274d0084dad4b33d7b1c09541fc568b5444593207912ee3023feffecf97234473aba21b7232e6a497f003be931e5542ab8932dbb9450515ce2daee75b64cdc541981cc18baf1dfeedb6d3c76b00d84f9aaefab307b02334e24f24124432ba8ca8d32b6a51e62bcdaf707689bf170571f2e647c8e020d5a7b0584127a37f91bd0577dee6cd8ce19ca30d65a629937811977d423b849c5be4b3871194d13d1c0030bffd4091944b0a488022c9de8898e1aef743643b1f9913d2c2ab610e470fc6bb314b7a80763ed6ee8f8a585721fb1405f6d0c6472c96c81ed766bd466729178dbd1808002445f9ada8057f9a205ff6d03c2abc99b34f9afec32d14342f6343a02656981d58fb747a3b75b15ad7eb40ddf5d366e548869d231f900e85fd774feafe173e23977d2211692c3a9b67112808758cb1c5a6003548a0d1dfca9781e5a9086e9c53be47ae49a18d2bd0ca12306c2ace63d73dc5b9c2948992d06b900df6206930ba312049a632877b351eb4d9846f5de4c543aea061be8bc207885324a38547e7f3d59349d1ec99162d72b78372230daf2a88bd5458272240a729c737d4da7e4779e26221f1b77df5a7cec0b8b205dbff86f71732eab9423b990f2900a7f1d7420bcc7ad82fed3c1b131fac61c6aff72897c9d01cbc8e3e298a00e480e129cc344a236a38bc6e2616df6abae7c1523bc294e4853441792d57728aa18e234c195082b140f8b73d751f18e209a04b2d246df6e50e78c19e752146d54174a780414bfcd8ac2722d1b642df67df3126720a5e47f9f3749f790db51967dd570d05a50559f8f2308532720ed24d2da0840cdf964b575daf006cb5ed43f0f4d53c03f0962c7ee926d6dc18308d79b6956489fa64f7ecf84e123eeecd35f91fe55b3d8a62828db98ac44a27d72186deeee284d268d951fb1a5413bba5d9885737b54b19fb039a29b2e493bd6177cb8b8b6cffb5d130b4c9653f1ee3046de38b05db5966be41298d0179713bd666bdc720a4892d6656f8abf3d8b0f072d070649f09080f687f63c1ed34b7384c3e6b0bf6c18807109f1cf2fa67f3bef9d16c1ea1c6bff3498f6492b1267170f56c789b957ae4561f3d123af3600beafe857d945e7c8433463dc1e965b03ae85109ce253d331dfa4e3332bbf5d443c29d1936e7fd37c0bf41226c40c0d4c3f8d923f16cb9c57db5f1ebdcefcade110f8b40406d8e4f63d89b86ef0a199d8e21b224d3394322833c95ce1c12c20c08dfe018b4c1e5dd42c29f953a1f6734af19240f4632d01bd12c897ab7af67a58e7f75430a8a4fe6ecab10b4e8bf556e4c82f1bbfbf875fb5986293f516c9fecc4cec5bc439ebfd7e16eb935f111e5eb20012fd5b9f0f9033421c1b6836560065d44084758f3bebf9a3db94e3922fd9a1e4d3fe7f334225864bb23f7b9a8597bc0e627669a8e7a748c09b83ee7b26b630eebf43485b984f6fd7865ce6359f7623d119bf6e645d34e58e2fc7dfc1c1d4baf8c543a57ead500ce6d6a38657585975d34cdd9bfea4ea6b2b46243545f914f8f08d8c1e1e15d3b0a6884813e0947ce2a3a53c628b9fe87e509a0150f32cbe9d2e8b056c2429538326e4bfb499ddc79dbc203f1fff4492d53b02eeb9320ef3195dd7994e1f8c1f293c23a76a3fb13fce83dab58ec091e667b16cc1c557abfba1adbc852abdb1960476099d3b01adadcb883c1023492aae49404338730b75e4834e12c3a3573219da09d93a296ae4f6b7f5b127dcd245cf2549c714a24ba6f411373e1ed0be7516296514a96c6ee3c3ef0a4de11b38ba7c6cf9af282cc9c45b69803e45252a35c5c4cc950bdb4b1764a371a103a0454c7d43a1f60a2ae90b246f9023a2b642382bed9b0c17342974c85778f0fc67fe577bf8d1f944fb70d7fdbc589547c5ca4221772a83e7636c87e29b9445d1186e9b8ad585f1df53b2c92f788251689fe36e5564806ee450c36f1280720d6147c027f2fcc715ebc7ddacd1a016730b03f637d519b31a17d691d0470eae8bd36f944f21317e1bd2cf1c27a8b00bd7b9040b88341dcab28c085564b7a37b6f9e9faa9416db9d1e45f25056c49b8f81520f0382ba1ff8b564f2b0163221e13b54bbdb745c8081cbf9d5a6d94961862696e973f84a009a309e5b6c4dde339dca1df35a2d7d09273e32b59fab9863c03685cbd98454817f395cb899b8e4a0650990d3bd7585807853f12c26e50e1d04168efe08843b49fe8331c15fcb0b7df197be691182aab05bd0de73f9b45fe874654213e93079b68b5515a7db956066df092ecbdb50856aa976e3434e7e0c6f93b478202574555744ad7a67412f8205e6fb1fa7001d2b8a78fdfd01455303b797b450d895829a4d5a2ba62b02a1699a8853e8ceff64576084917b0b9bd90a708d8c22a469a245c03c9c4e8b4d608535eec1c0a8d28c9d379f8908c7a87c2e3792c55a697ceaa1530f66e05aacb55abd2c5e8c4ece658bcbc22a63e6378c3b129887dca929ae39155d9d2a9085ecd049f5514f8eaee2a50215fd10ad34070b4698b316dc88a65f651acaa023288aaf4644f456c886a896ad93a369334ca22e6370f1ed929d48417e0aad3612bc022e0ffb52a21d1fde6602e1f59da088ee4558519551150a59fb4baa4c05bc67222bea465a8cda8e81f897d27dd35dc091c96972e69f0e0744932bfa36816796d83262378ba47ceb4e25915c68eb21d1d56840d6fccc3c681035d0a16d31e1581e10ac338f0db408aa5b29fd4b0ad1b31536c5a0995a976bb165d6ac0f90be033ba95716b36a7482315a9cc79e97827b1abc28dd232c2156f70c4d1a3f1e7b18b5ed02ebffaf7073bb015020725a9e26fd291d43a3a18f2800f2e61b1f38e9b76f9f062a093674dc951eea14732d2d2ac93fe0ebbf79f5882a7aca7ba57e0290a1ff0a15c5a1cacaa78d6b2a56bae3b79a1becf7f837d8e4cf9ab559e08fd3c8ec1f34e48ff4376525defefbf46a85f2325c913adedc433e2e7a4078bc5e2fe9d90b6f62f781a914127744444b7ba0bd67e38b03f38eb4c50fd659e75f6c0983368ffece99a2392fc42e23e8f3b173d7922ef3c1eace06bb8748c0aa13adfc19b2953e196aab2b7834dfdd04f0520c03550bcf6df4ff5bdfb7ce30adbb27adfa0ccd0e1d6895745a21682d5fbe0ad39f5689ececa7d1bdfc8c1b1adb9286a6f0e9793671841244b76e723e94216e9a4ee6f8d2f42843eadcddf36912504bde40e683a38cbf970af2d4183f1c78f49d92b364324dc35b0c75c8cc00bcd02b8f818ec91e1f89df735f933e4ff81fd7c2cdbafdbdfefcb6d2467b0b5910aaa84a051780cbddf3d5fd52910d31be47256e5be39e40fbac5965af40d199210a9f1b7e81ce6c47a4f3b6427ded880db6007a2bedcf66b1ba6a7710dfcd079f5176a00fccc25b12c2a66c371a271710c3dc70cef254c0c341daccc062dc7800570be1c12b082bd63d4c1f2ad7cdb1f08b5a30e1b8973ff18040cbf1320f0d0e95b564fcc94dd68d97a455e464e69c1062b85679623266e3183c94e06eabfc4332d634e727a0038e59109ba347ddee28c068d74134992420d2caacc2c1bb8cf0d8f14dcf8c930d851b08ef59fd13e3ca3c2466cd912a0091d2bceae98f102f065a8566b5068b2b1f5da2edc7406d58ef240f780b45448d432d8c780d68f6d3e0b11a2c07fd6859804c8e28ad5342c666102510082c025e0238991920be52b21a3356aca954bd6eb9e39318701395325c5930271206a7b6871de2a5c37037e45e07ad5c55624f14416063c160012a0817da1134829299e3e370e3181a17c6054ffc56ccf902c36a8dae8d5c0fb6e9ce87d395963012805995e43275268ea440accf2617620e7f14573de637b27ecfd117cc1e1541bc859cbab0158238c5b54da9561aaba7d1875a62264b7650de83b32e588c3f0d0716acb3a1ce6e6cae2a080bbbd4b3e5b3609234c41de6681ccf7f04eef61c9030cb22f65473feb7fc878a5f7fd83475dd2f4b3ed77815ac595867cdcf9a9060b71672024ae3609cadd62d5cdc24483325c58e1ad90f42f012adeb74eacdc1e8021c7470fbf122618bad4c88876ecabe808d531a85de449a8271581be4de728f73f02db9adc53e64d674b90ad61994a4dc4eb16d5e3eb8c20e997247413a366156a7a4edafe662c3c3c0801fca05799c4f878301ab0e9c9fa6cd40b3e092b26788189c625e59e2f69273501502c1a886cb454180812a93f269addf13657a67e282b59e06c27a0618c129497386c99379edd48c6b8a8a8ffbe982ed66f201184e1fa0d8141168211ef4568c4c4720404c893fffe02f92879edc8ceca3bcc7fb6bd62cc4368fd2aeeee7f88f248694e8e7afc4a5c0f5bf8d61625ed227a1b1a0b9e9358b1dcf92f2138fd483127ea5046888b389201555cee98439f08df19745a69e9792057a21b1f4e75e447569916d1301505644f0a8510e970fcf3427cf22ce6f28471d88cd6bce768ca7f4d0d449316b9581221ba501aa5d9fb58fe4d84bb068c8f5f20af2e6fe3c0a3046199a5ec2ef5d6486d7355a76cb5f8790b3415c0931ca7b32ca065e36174280a6ce2ddc919951a7464ac6a4db61ff61de1f8e6c89eb23ded61bf7ddcd32a4fc7db45d0d9891684cb414f759961ad5a41ad3bb14186261fced6feda0479b05bd99a78d639a3066df1f5b08dbeb0c9c27da8f7744e3d98b9d4082b17b10a5f0593119e0cc63c3cb48e253636ee70b3a84faf0876ef0b387670e641b5fd2577fe838c920e2e9b9cb98ac698c832c9fd54f5fef3fff77c06c41cd73346751d4a7c70fe5a4e41a05cbf9bf8fa4edebe16a52c4c11b2416d30537691bfeac03b28c85341cff57c7fdb21bb1858d14aea4e4a0ebd69f1df88770e2dcfa40450ecf8fa3951eebbfec871d043295c0173ce751af4e0650e9842a69f58b0a1a0a1e795cace27e05a821cf4dcef531d31d0ad20f14c75352f6c4e1af3d57ad52fd4f57d52992e2ca6bedf1a7d7a8e97ce59fccfc40976264c844f881b0cfa9c107f351ef2968e53f8c84ec5db6e40adce48a63ea09bcdced80cdd57427f643e15e8f862bb02adf726031fa2653ddfa1a30f9af08abff25da4b45bb8a122dd3cde29cdbe1f6b0c24b32a94fe483420c91e8c130fffcef401fe8f9ef0451036638f6650a337da5bf11d1237f8216be2b09f3c233672c1f87a57ee27fbccd3e828b72172c5f2872dc487665538c96e1dd72ff394598f69e5896dd3b81946ccbd295032d9815f83a0d026cab3c541a1594bdd3e7818b743047817e7dc11d50b0d01751be0525324fe270d7e917acb042f3a35ccdcf27b9497a9a4218908c3ba264e88b02cd43e6b8938f3c13f03b02047125f5f327d0c6e2125eef895aa3d7e66013556a4c56cf3494f20a5a6160048de5ff8e33f605367c062c23f17ce7f489e6d517ece617e1883cac7730f69baba2760b71484a2723a5cf6aedbe68a33ddc3ef7791279d16b785e094d6ffbedb998f54fc060699a373c7dedaeccbaff5c696ee854b6556e04505bfa761079b2c0cad0ab2cafbac49774a7050348adce7584e07df446ee95aa46ef26a6138641b099895f97c90120d3320d098259e62c2ece5025322272071f92a95161fccba3b73d18f0485d3ac91bec90df3b30dbfa6410bf50aa7e74364bc4f71797cfbc681886eaa0621159c0707262a705c8f42cbbd165fe83d4aa3ef8686dc3a603e7c4d192587fa809a7c19cedf30cb51ee58875b508918bc411dc98ff163c9d7ab2f12403f440f876fc5d14ae2cb624a2e7f11c15fa9f49105af3694bc88fe1b3e49997df81e58fb997d339fbbad324e626a583282279cdb5daf409bbeccbc080f91cfb4c576722fed3bf574d918f7846d857245fc73457e6532a1cf991f0c9a41429c3cd33696396a092f4f5aa84ee710d89e9020cc064c670ec4186b427f2c1e6cb6555d4822578236e48d70ac0ac04ef7bdd330ace409a38843ea2e4ace31979df7eba9fc74c35c149c7c6a88b357b92c1b75d59d512013741b45969c54bfefb89f7cff1c35d858f0f1c2a2519b46e33937f56263f6b9ea349760dadf7993966e557cde75b29ee0cfba9d3d7bdc41aa9cf9541695808b3c7833d3aab16352fd8b749e9bd5c457edbac0f92d4cf71a8d96b47682a78edaa0d467ebb9f355e6ba5e65f9c7ae668e30b29dd089ded68f6b2df10fbbe472617596fdda6c8e00e664348a25d8ae7cc4af4c7fb7ff7368f3b1ec0cce33201ad74a300c39067802f3a7669db71d875bc3afe73d429f74eb2cde3d2029fc82e867610dcb487ad1df03d3a876e38c496bed99c4bfb9eb321ee7ca7c8590839b66fc3a4ec78c335bd79153afc23b0a0978f052beb25d714aa11caacf44d80d0b8b97741b5bb84406b7a69c8d57acb06f0f064f9aecc4de8a2c2b95f1d8b8b508e9da3ef637028582f5baa7411be43cd8b143887693c4d2b0c7ca28b3efbf27bcbdcf19be83a27921ad519a6e411e9970957d04374d9d60f8f65774d1a760a9c1eef61ae07a425e66ca735c7cd0ba663982e23600c8f8e287586cd41f1a886d5c21b03a283976332da9781a3ff87885cd539b0077701406a6d62e6515da9e2a048491f05c48a49f0d0436c6a10b36708bcfe0e2df91f851d7343b05ead9dcceefab81dc74432b84687ffcade5b3a5d3ba8744fa58d2fdcb4a366191ad292ae840b9a71855db2c1c3fc2a88ca0cffbcf11cc12ff71192b37b5ac15cb02a1b8247516c52ec9e7e1a0bb82aea480619518057c8ac85799c60c7ac8fd8ec3eb611a467c273ecd9108a56d6029db0420107618de084edc93d09c83b6232f5434949898ae9f826433eb3fbbed52b1509bb48cc084c5acad143b0d62c03953bb0f0a17b7e5f7e7c63e538cb505172b98fff2e166ceb5fcc525a9866879c87dae3be5ab65e3d8d5779da23cabe64ccdeec47d382475412552e0541d460bb19a65a2356f27acc394d441341416ffbf8572018183ac46cd1280022115bb1d51b776c61e005559738821c67226b73beccbdd4b6ef9eaa10df43f76d1d4489c59eac32c2c6977ef2157f84ff7a1d719fb681090cac6702e9d914d36aa8d6b5f0cd2e6378bd83d943bbe8376575b3cd78a7ff63b4424e9a1f7ae415048ecb6202aeb020011108fbebde7cf001c03300ca0135a56044f35a84502ad4e68082de90e6006ef0f5e8f0d1e6480d4025696375cc8dc4e9fed3799e21edd5138d627152ccf01e411dca1f8a83deac5df4ae736aedeb315b8cff43add3ebc97408300ef9c59d20277f77a5b023b79f6979c1f13f8090f613de7b6869cf0366d9cc7fa621b1ea57a83fd090ef76c3daea76c43dea3c9f91b78f84da99882f6db30dc984856055eabcc486a2a92919bf9c9e261a2ae020f53d35c8397e8b5d7fc5d3534f5aa68ce4de91dc068d8fee3898c95bf568b4eb06445d9e806805570ddf189d08211827e90559cb7d4d2dd2b9ed79a2363cc42c151f3077a339b3e2b5512316f6f3e4a30921af1d37ef4c42e1b9888b700fba6ebd4c7483dcbd6ff67fd50fecce49f1158149a069dbf459618efb2d401fa8109bc8a305dae68341ecd47146d2345df8f70dade6e8ac94f78d7b746e660a1f93fc6bd68b288970c507fec3cf080a66f8b7e45ce19bb579a6cd4ecbf97976770074c45be6f7b3c9fd32caff7bca21d921ea418cf19fecfd45d2ec28f8dd8bf4a4485e4e16f07635cc1ff84a0a0baf69aff2f6df0e27aaa28e13add852b72f69979171b47ecc62669807c24689ef34caa5e960c56b802be3338cd6f81b781918f17d670d4ed66358fcc0bf4341f8ac9327f340e67e400886ef9ba03330546d586b71068bcd17241d7270886caecdd8db48b953c1a2a9d4e25bc696684801413a57993883018f478c4838160f4b193bbb62b401cc4f9b35e8388aace39f82be8a9cd704033a7c68c657bb1034802ec60969e1c3f11963c37473c68a6eff74dbddb4e5bcd8bcd8b00f8b0f0e4b8feacbcf7b51bc5ab13675e636dfaacd39606b38f1c24cae8765a027b26dd44da7ef872c20bd0d36e8559f372c6dce3ad666c805258f92336b79cbd96340710e6b1916d0f391804c9846b8860eace1a39cde73784533ccddc1be5700c608f21165eff24fcafe7069f08fdaa371fa84508bc1ce1d89c1c2beeed807e77603e7716dd0cc9824d07c00c829d82eb0da413666f2e1b0eca2ec4ae1a36c2dc6672f0b2ef442e5c0b18ffaa917fdc17d56ceb3512cca352d659dbf9b8629d292cc1a7a630c2648b0998e1b3c3b9bc169a521a614138d89f579f4e7c2714a4f57f57e38730707ef9516f753130d44403af3a277cf413dc3f762aeadb0c918fb1896acfb4c189cec9c36b4671d8803c5be4f1cc12d1c6983e95d8f10509c1f9a61b491ae689195987e856931eb384db3e9688029d3229f7fdb2f27e092a098805cb6e1a2323a63ff6c58f370fbcaac70a1daaadde1c5bc1eb3feb0fd6e68e44fa0280a9897cda9798235bc188622bddeaa401af18f60e2e9525250db8c7e56dc54cf00f4abdbb9a71e00cec93505a1285fdbbc5187567f5acfd81e5cf8173532db56da124a29938b7a60aff8fed7f4bdff67148cb38c6ce416e21c36d0398a1455cd037f537fcae49fa79de829905732728bb1ce510cefc1cd6cb389fb3e35627ad3767e89c03bd2ab8da14c69f08d76e7ce7bb97d941dfdef5061746b857d9993f835f9e96075ef911d65d05cd3cd09419c0f2efb7d9f05ee94528b1cf6006f30158e70a751bac1fc0e9c60c03c231ca8f8f24e438d3a3f3e2b39e6795a452b6d870c0b1f114ed9f5c61c267c4a377600b301b50cd29d29c585b386f9f7439b375e6876d44814710c1e2961aea3e2d23f6bee6077d02b6494e67003cde03953666a98f259340f20c7bbda9d183eca7e0eccba974474b59f00fda4dc586bdf7c3f330fa1a60464adcf296a057e58d090a406253483762ffc07996fb9ac58672030d46deea411067d60afa0aa30082949d64497def516a58d0e490c5cbc86f11d3042938f47f85cbc7ab6fbdffb418fef709f5e52a25cd0ebc5552b1ee1fbc81d0dc764b3f87d06b02e49043395e7ce3fd95393038f3334b41dc55e0ccd7cbcd4d9af5932f58ca0c3ef9d34a670956596bfc06fa066b0a1f2514c4d2f8c228a17c7ae2001c967b200d1ad2dd1f96cffa2edd21836bb2d4a7effba7c99557e14e589f79d6679eca9682f1ad1d2a502b85e6a682f3baaf030bc79814dd5cfb23a65a6cf76895f992fbd935f4d3832a668799d9c55ff087c8f07cb4faa8446f164ba5868164d647f95818287bc120077f3704d6a92257ab6947f809c13bf407ea1f26423f41dd980630610e391b6463c8076c1474e267688f1dd068b5a8563bf7b465d39d8ec1ff8f3a04e0ffdb4fe1612ade3cf00abd10010af1570a6c690eefbfaf256ee7fe32d5c6489f9796bdc46ef0d5cbe7728e204a234d273060f61c74b0219cbe5f3d8da244412230276db2585e4839d71f58c2d0ae270c02b9503a1ddd54aded0afb711f866c0f46a4e62f184104eafa95e47872f3b482b3d35beb1f62fb1a83d35440b05395d0bed173b63fbe4b554f8ae7dedfaf9b0c4a415520446e8a6d93b55ec9c418a45dcc9ac5850b2105d813b8cb335e6419e2b2e1b70cb20c928cafe61c73da6d28ac82316c43db0cb004c52a833b51e6fac2a23c4f88ec53fbe0a4ad71e6e6729aad5070fdc393ed6058b627b4b42f407c07ecf72ab91c237b6345136849661a57ae89b3ca933c68dcb8c5bd0a549ed900c49c0086bd60c3ab7f5379ff5df1148f98e64ae1ad1c24cf0e6fae5ffd9eb852a718df05ee56dac13c675092d7c664d98641c83d45c39c913f9b063670b5dcc9c5adda2103bca43e3b2e36db3d3034056e672c5fd5325e193acd69dc82c58b366769484d6fc2efefa31733418de6b12c725d15277a975623540f84d409405441ceb02a3f84108e8b17712871e22c16243300b42462384f391027f3e03955f4cfd2dcdbad39464a647a90755dcca10644858434f2a12fba3855200a6d8ddee54fe960e70195f3b20f5107e4a1be91eb88f996167dc6e232031b7bcbc85aea537d5907c2c2ac0fb13037b96baae283f6e3577dec72e23adf2de724ac59f52d44ecddc1b6fdac900cfa7528c8371e88296364d2a02c585638f1c759d942493072717e875bd3accd7c5b431045a9ea9900b9db2f06d2a04bda0088f73817ed04b7be29370439d2da35708e2898bc5d1e8434a8b4b012d971087269393e6fd0945ba1ebcd4c687c8ca50eed0767707f7c8b979ec26d2eb1f05620381cc40c537248f49b2785f133cb86d397b8257640dffc276e9462cfd1dd4233f7a95cf01293460543bbb6342ae6c798b95c94838a0bf01ea961453bb899edb63f59e6aaccaadcd2bc98bc9fb30166786921e809f45be7fdded8f031efaf70caa77b06b6882868ae0474c4ffdf190f3b364e3ac4a6fd9f9c1bc2274bd7232c10e004bc03247390412130923c92e5c3dbbd13cd8f8d4598ea616448e8dd4f8195027c241ad34a23ce720e740fd702af0cab783832304b91f2c2ddd14d68cb07c56eb1384768cdf40fc68b55a8eda7e4c05d89475419402b1c3faa212fcb3dcaf9705f55717b88629d5bc7dc7ec3b4b82a734fb62eae974ea7ec1e939372d3fd6843bf3ae60c6fddcc7953a769f7c28766dd1a9039caa2b6fbc75d40b75d87c5c1fb91bbdb8db5000978ce03f7c78aea767600cdc74ea6ae8bb8899979a6b513ef5add17b2c4aef417bf8ef80527fb0cccfd8173f562c66603f99857a2d572bf5b38b7615900ff334b8c9f717c05a30a041b31bfacb9008957f44679bdf2e8de7a12c28739e0114fd40ba21db041860f5686f40a2817b86984f6095d4c63e513550fe0610ea3be5880c43d8c48740ec34b0389a0aa9507f10c7c5640a1e85c852ee202a3dc9546ea9531e90b25c14e3e0b16709497e9ecf5888ca573b30f9f4e5a4f4405fd47fd3a6cfe3410ce724d1baf3117a5b6c8ccee50aa282b8e1b745dfa940e07fc8a8af4d2107f3d808309822774a502a8c774d0ae71b19a077bd704211756babcda608e0d080da536d642428221ed66d949b4d390b44eecb73a7ddb30c66a11f7717f70800fbd11bda5db75cffd68c001d2e7b14fcbae6475142fb33ed76a699ae0c5c2ae54f8c2b25a125d1936e9d707b0c24dfbd62579750d0d1d3c0a92c991892c88caf91ee918e458fb27e1e2fe6b462c873d84e37983fb810cd76204e8d328759a1327456b38336c22e0f34b468807ca5fc79946337fafcb87b06ae8dd93f44bd37771d3eb805d997e9e66cffad20ec9390d31c7d9335c652fb9c195d8f7708b7d3c0f394f828e25f5a653f6ef00d3eac410f65523a9448c30b9f7c8a7752a078ea248be98036769a119fc97909180385d6dd0b431add4d463816ffbc5b72cc680d2b859491499cc680474ad490b7e030ad92e20bfa2003d6f2e5614304cbd4acff73a3565a6afc5e832f9c654a53c645eb08a7ef13a635d5402fc780ac0dc541f1c593a3ce01e5b09cf93d664215400d3fa3f153c44f47e50ef988c73b4abde12e46d3b7b9197b6353204f19d91a309444a528c9937361dbf0f4df64a1e2fbd65ee327cdf5a9bec94996d30700aa5ffb25d664569f736d85cedff8d52c01f623a3f08b6160a3c6ae77734a1a098aee24e2741c28e010788f0042dec0653dadf42e945ae4ad1a18a5b44eb0335deaab18c19d04060dc417ce632aec07f8c665d8c7f40cb0398b2ad685483051c50fd7c580003130af0f687528f83cad622f55ee82d20de0f0121d0eef55d3cf194d91c074fce47e1e9395e4741021b2218996ac9ff71d463a0f083d3a013d61fcc7eb3efb5f915caf4e7af380fa968448f2f00841d904ef2422db4794771c33aae3105f6c9c4176480313f6083cc9357d424af6e74cc213b8186314a3c6020ad52a825ce16027342e446f59e58c7055633d468e1c6d4d578af479390d9c4bb7707ff4cefe279125a3613fed160f8dcff7aa44776fcc6d3323e38842910ea3497af7e54884712f4fecdc356f63752ecb27aa6be4a6ec6380a2e659be18d4f68d4d9fed9e5a8789413c52171b4512be72c62b9d7ecffce452a63ce3dbb6ffe7274b4ac0732cd900e281cdcd95b5410f126a3f6b0bf1fac1661da073b5d55c5423b6f855ff562ce88c0e48b523dcae35f47ec573b5f4d9a168bd469eb9c5eacf9f6b05743ffc6d643082cccf52cc3a94a793a844fe40da8a768c240b82f78310b1a03c5bbf230ffe3646378690d4687e4f30fc06f5310a1027e570be311b8669987c9257ef5eb7e7bc6850fe0295315275e6a37aafa8b1e2e5f93dfb46dd10b43f4bbf9141b116275f01556668f6c3000d3456f49cec0d7fe65a1e113d5893f0848b0da91b3f1208608d0452362081c5dce062d3e7c303b0b40b26c67c1a64304e8e56e1b921f7557650bad38e59faa5c9d741ae8e1fbf504c067f8b1a4071169c77a23e610dee5d87e52e3f7f2cdc9889b928ef06e4e2a78a1785ce1c3d4fae09febdec70c1fde4bc45c1530a4d106cf83b7c7b31d8b7e1f318520ffab2d78b3a68d7f0d28721c373606be86b8ed81b8e7b0dc0b3d5e51bd3b9a10f1947e31ce1514c95753893f7dff997e8f1087993118763012ee3fbca81edb1d72c413a856f65eb13d7613b2be67efd60bee9022b52cbef3bfcfa3539c987d7232770f142df7bfc557b977dfc157cc3be421948cbd6c91fbff204404172481d9d8837f127e7b7da2eda72fb9fbc224b3f841aa948e2d07989b3035c12451be793239f192bd7718137cf4a065c5e3603687393704326dd4abce59986f9c74c59aa6524f6fb490549e8e9e9dec42894d4e5d7ec5de75a538d320461be86979a201264f33f251c39ed65ffa74c57f7bdc803f0fd7b09250c61be7796b41c5e17231fdd706fc5adab8eaca494cc5f06b205c105ea8fe923273db5bbdfea62c3babffe72e5f49b7939be7a9910c55c8e32dc7987ceaad2c40001d5d458728e6a3d676a4aef296d7c3c255ed79a7e56ac50e9c0a376210be388a8676e121a7bb7da61d5ef0c6fab88afed2bf02e33854c43e8f14f1b7811021777bef2cd3f5d965979040b6e09d9fe5501c1612892933f30d23d529de17ff9d4b4718b645eae623f96833ed5452c8ed039a50779bcc665439e7db8f59fbd1325b36fa741d0079c431c1c1db25530e8cba3a7ce0e1bf48776260e1425014cf9a86ae30fe6c5a3fd0144817d8066da3b393514d49d025afca0c356f3e2bebbf8599632b4df74e8e8c52350edc41cd72687ef939269764f6c45a45a3fdd73483778f22053a1d9fd82fcf549d308a201d8dd126347e405a63851cf5554dd6459b412b3e0abd19f9fc9bb42614f1ef7d328e919991d3dcbc8c62bf7971ac9c82ee101a7eaec4e40398b264f35109cab8514d18ec1add6e04f127ecbdb0e8f34014ea340b18302538d95b8a95566678bdfd03213b0138676c67693367012f0b520bb3113645b62502929a944445cb53b08d1915a850e0b18a2d05679cc5c2c80e6274b638d80bee3f1c67572ac9d4b3e6c6ea95606d4c3bb83ba1e4945c240f5a1913ec683e8e9cb4209e3108ebe46ad5b055274bba1752fc606c2bac2b0c6128ca910499571d3a0b6e4800e42404b5d8ce49128d14f5d402f7a25bf298bd6ded4f8cc1d17bbae658bb05a8b69d3fabc7ccf20e173e5b5fe41d3b55dee8ddccb15e65b40126fe786f84eb4b036acfb74311f884548179b0d88dd246306ee42df5ea5913dbac269594b8fe1a0d8f0c4a39cc402846ce941393a7e4e45b0e2173dc1495cbea2913c46467068f06516dafbc78653be054e8d772793582e80e5573e00339041e4788185530b4fa7747d1b236c25581ab3462a235f4c7ada43f5c032e1d63651a3c81bb45f3c7fdf2311e56fd6f2b2f5eba59daf453d338ef5024141ea92b94ae2e79043088880a099a45209e52dd812415c90debe3f1eaa2a3ee8accfd405b0c06c7057ac396f2f92381b338893405fd2b4be98a37a1a9d89c5df54c54e3e049c2b1f8a42af8a58f0cbc272d887e89816b21fbaf5898b8e2dddc58f0974c68f3248ed9f7bd30b566b478d5a96f127eee13c13b787673953e0713a76005fcaf35ea7d3da343da225496a3634f65c6b6a4c8b8a393c9425af20fed69df27f6650ceb5040cff7d997c3d7e722792722472af730b8f55791ee78de29414a1deeea4462be62f9022771d1c61d30739f6db1bd0ef78f0aba2db2131d4714f7f1760a5193b1ea823aba146ebbd1f88d43f90471e5278526966f79bf03a676a11839f4a83cd80eee6c2604723eeb1b4d4b95538c6fa0067993d4bf8d0e6168c414112698bd7b3a35ec98144f245a99aa827d9121c92745ed7ae8b61475f5dcf69bd21ca4bc90c0038b53d5d2bcac6dc8225a182c671c7a3efec9b7e1387aa14f56bc9561feb09a804797f08a42dee6dbb652e90878330701cab614b1efecc9a69204ab859be2826aacd9a27d579634ab888333cfd319354c7f58c6856b583c536a5f09638506b864146241246f2dcc884cb61fe0463229d78f6f0af3857d09c022a59f30b4beeaecccc0bff4f6663a0e48d7dedcc8ead42ec383557f91d86f1e08b3d42fe10605b22e0c7280c69c4ae670eb61c3cc5e7f6f15c5f9f03a07a71ae2511c7cbe54675926f6b96c06b1f8aed70724122b720203d6b18c6af7442ac29f87d33b666743521c1bf3fd36b5d0ff730aea22e5d664280116832f8e16fd4096f24ff6624571cce426066098d5af78d06da04050bca5b4a060faa42e0bd156557f1600ad9978f52c3cd8e4a879910e7c320b971e61b755a2b930f2f150d9f548377769808a5afee0b2051a677b8b0d4d0daec9bbb78cdbc4d623bee1e224585077c7eeb458aceb118ce1bd49e43c2c578c11e2d0c6421f57380d496442893077cc7f0db3b5421d784c86c45f4ade46ff8a008467e4681126f6e4c912778e0d10f9b7dc4bbc4bbe4a0674b1dd0fc83705171cbd12a9f6467806d10a03b7c887c943237333891c99b03d6eeb0466d608ddc9ba7792cfcdf5062f06be7b6191a6572e1a8195aa1bea7abb550969534f05b369a4e882898b79accf58443d5e62c476898c2dcb768f4f348d6cb36b9aacef65658d2aa0a1297b734990108d0ff144c06ea1e9d3a7a79ed58fa3e1745da88a00b4adf3da41ed247e8d61dd98dff68352a157fae7efa20c175eec2344b088a00e030cd14225b4a4c4fee992c1fa7c64647977ae93e2474945178c9765a649f8f8549305e0196068661061032a37093f304c80e11d44eff503e3473bb1ced4d820253f109972ea4794bd0c6bc76410c1be9429d62d360b8ca2b525bb573c591988ca65452ba3b249a03c7d5199ebcc3ba3f79f0306841bdccc34623b5ec71737d06ae1ad642ec13ee393f4741a33a667dab704be3c037fd20a512b11862a897a33813738693e7920a2484f0294ec43eeda8812b5af0b4418387c53144cdc4d9198b803e933dbf23f6355e9d372589108e12f4e6c3c32155469f5eb601fe8537a67dbff3b3a24155ec66c103aa2979cd852421ecd6da2468c03729fe06b587d949835d7a8d5020d62144657cbe1e8db1162206f1790d898a8946f5535820074afc2ae6d3e81c7c7de63ab55ce460c56c2fd3bd29a366f3979e4c6c1910e9d9c9e4931240f6e8873a63c786aea91d678cd96e2d2400d72dab983c2da1170bdeda68bd359dc2f098479a449a62ac3799fdfc188afd96f8cb74f4edc6ee227c0152dae700c436082842c265fdbe48dd5d43f0e99e8b0ac4c6ac640ff3b931a5db2705a3a1f4ba6efb1a15c649eac8230930abae799eb8e2044df54f474a6165c47953d5e9a0dbdec4aba69671f15c776902794dc8557fa7513a33751b3dfb4499342edea0e97d422d196afac73e35e03dc38e2652961dc4dd1d46273574ca5c4b3d00f5b4c71ff7471e4626673f8cb59a27b9c6c8292c5606f7333a539b7eeca3a7ef2a938220315bfa5bb8f5b882723fb7ccfc19250b8391d431f4d1778dadfa3666e65db2bc5deae4a2e02e28f07aad2764d272efe0a6fdc3c0a3cf3808810eeffb207bbac00b5c0870a07283065e6fbdffd7f7fae1ef12cc9fc1eabbef49f286c28cb2cbb3025e866de0448b8454b7e744648750ca065e95579cc1ba156891bd3cd3e8059d2b861f9485e25c186263956337ce10619aaf48aec5e20a5a43524d1971b6200718c18f9a73b25e56ef73f3fceeaea66a0d3c22260f4886714feea12b51e23995dbdde924e184f8d5544a613e2bf43b1ee263a594f70ca88b33059a1d04df485e573b6f37bce6f62adae609afba7c489b67436e5cf91b6936cfe350441439e074e6a7acb27fcf33bb7bece54650d601d3f3f7fe6637504e5ea989001eeede4d6bb988ae749aebab31a933df7bcf6fe32f450b11b76813c2785ca84399329697a06aba3a4d5c69ce27d3985ed318e51908dec231aa343afca754caa305b714f8df450381e2e9ef5d8a11329228775d7cfa59c5b065f36fb4cbad2c0e7282e0db0891ff35367f512b39308b5d25f4804487e40dd9a00cfd71d9c3bccd5597287ec39405dead1d50d6904ee96629b0a3756f30af27d9ecaa8c937f99a1d70391834e10317d8e00e6cc09b4a253eca8fe42e68e1098d4ec9eca411e015aff630791ba73035858b2d043246f0139fe535fb1baf51180d553939dcc56ff930e39c2bcf6abe5ed7cb15b4c6a13fc95b0475ce69896fe20c6ad987d89e5562804f1abb452f2d9dcd16b5b7a87eaf54411ee2c48058d08073a67b21a935840adaf02496c1ca2c3b618c2432f4ae1bbd3e66b47a516281cba014abd6f58cdb590238beb88544b8c21c8b6b6a0cbccb34715fc85cda581604ac8822a1ed680ba9a5f922850f12b2a6e51ec2da7d557e14d729322db62739129624d114ea15c16027bd9248cd706d73b984cc59be405b25677b91f60d61c3aab7867078390c6a0c7165e6cf1cae47f9a68046ec85d10748fe4cacc3662f60397e856042c68f391de94d02cf0b62b2af4eff67bcef61dbfe0b62f061dfaf74ac383ac6713b4c1657abcdf52467fc4bb1b8bfdc25af84f6286864e1f695fc433d2d40d7adf5fe5f55572304c16a20eac4aa45ed8dc1410f65464b82d20b8587e0eb023324cc7709cd39bfef1f9458ce0a4127fb441ae3a7dcadb5c41e834e2c50b49297bab7df75aa39a17ae37d7441915c82783ad0d8fed51ff672f2c54e77b0da6200ed46cd4bf377248a505066426a17eac38ccafbbd40603a88e4a48bc596858dca7631d4aaa02fcc964f60c46a72e9a9f649e90926a4738292fd77f27696de0eece6ea6e89075843613fd5c5d2a413795c6b646eb9ad866303c9e953a89712ec529a41abd9d105cb0e77fd951a7257d5f668546204b2f128799dc7e662c73138deda8e5dc53358a67044e256092a5445f23be2e7c9dfd856439861cdbfc25c1993d861cc0aa05a2bd1e1a924407d85fbdac9d6eab266aa4082a751bcab26887d146e5ed381fae7f7cea4eef8c619138d470dc2c6faff92a762bc1d3465f041e1ff32e1611b303b2f4e49ebdb04744f8c96793e69ae0d27a915a4d966e7977d33af21b425132ad6743f6657d4cd6a786a7b902bfa01c038af536dbb3b7f0e5dd210b29b3b0d3364d57446d131de5510c0fad9987c8d6e0e1d81ed2a46128d778eca411b49539eb548024a13e7477d9a6f1a6cc3dd879f3cb2c89211594b0645fab966b05188b2d34a5319682b1dfe544a9453dbf4d31f6cdf9c4c2ecc27ffadba746cc892f7d2d2080ed858629abce0ef50d4771b8570b79ac764a3ba03b81436a25f4ff4a9b8bc928855c63fed5cd8679a68268751cddfd823cb78f65d74373b2a70334760bee2748c3d55a0a683e00f7f263924afb67dad80156710d28527310609baccd156f6c99629ebd3006aa8559f63448908188386caf629a05306aad3a0fdfc5135858413a3b296a1f1a75a70e1873618bb23f639d267a00c0363046d1d0f6391499d4d8f03503c4debc0e6225a40f20747ce70d8f2b00ce6cb04bec57233433ac958a570759f7c43842bd0c8ba9fa4e798d373331108f074f319f2b5f2771cea7907e9b5a8d4469b9f8966c2b803b47f080fcb5318abe4a0050d08941f0fbf5e0a20ca4cf4731f7263a1a8b7ed32281303242694f007e9846c5fb368421272e06a4af0da5c452f21f9c10acff627e0f106cf1328030aee69789010b87f1db4796e57f69118c9e062d80ad3f06d65c1b6729f6eef17017521956af10e77d6cd26985e5c00ddd29db6cafbb6669199dafb158af4e34b1a46736994323c3e2c09ae7f9340f955494e2758e5e1098967b15cbd9f46058f3ada060ee40d7191ff0930371c6c8d9fa95d593f07f1c5fe1612cb11d00d4b26d343b5fc63548d41007f961f1434dcbb0b6b9562e67deb7b13e40cfe5f2df99c26f7ea7882cf71bbd092c9022231e52feca39781e50e03c8e08e760ff9d9a8efd41f82637e7f93dcf41ffaa7276dc09a91f4ff23c0fe2c1ed4bb5b387ec18809fe5908b283064db48a392f60e03b7eea7f0e12a6dcb5c38689a8b24fcb7eea8407b8aa97afd455cc1a4b9ddfd30dde064cf35ccfef77e8034d494847b305d9598337baf0a386c909e182e67f75bc9f57c58398201a794c4817d3837b580fd4101767ab197e8484e8875ced2d2373bfc30c1a1081cfb74b6dc68046d9cd784d688894cc8d5113b9d1196afa6cb8a28df82361c53300a003000a001600d000202a00800316e91e0f0a564ffd00da939301c9295966a752d0822ff881f14a664ac5b74fdd6f1ad272e38b9576c0b4bcb3f4fa1715eb7f21ef2e4bd77cc0ec89d302fde0a587e1ff9cd07a11de95afdb1b9acdfccce99f743c7765aaad2061437e72fec1559fbf84997b9d05a31708f52d674c775fc479f78485597be3d64d87668f771ea4f2ea9ed6e36783b110b3134f1e12c4bee6e186aaddd9509c5ae859f7affc40c4161df2678c50a5f8a3b43b5586aaea1200dff9e224c42442a9edd96b1302e7ea5f1284ba70357eb185c25963fa94823fc17609e66266a5707ae96df9025570d34c88cc0fcaf6abaef588bded6f29d3f22e03b579db8e1aa0bb317896db3c237893a0323d9c7d69d6f232e807bff04590be0a8fbd74486dd8be2468966ef68ae1117ae4003cff53facbea2eb97aed4c0dcf57b225ed0c3356be957983021d41c14bba73a785a8ca7ab3fe8f0c3889fb2c7a1c96cddced5e6040f2bedcfe41e98442a4683cd01550f49939b4048b432009d36ae8b4997b2bc783546bbc569d8ce64c761937b151fac08ca1a091da6e21a3ea69940c891facb6d6e7ecde2b55a439a56290e2efe33ad89a10dfc97197486f74e900861d0caa6e747a47f9224d33b9d6492d87f4bc42ffd9759247add2f39080e72e8e01ec567dbf83b708d59a351ebec73433a1a19b79e3f3662f3b8ba729afc4a57007e80f044bc4349a880616e71f1c16002b6eb08f9f01a61793b8c133e1f72f1195f30a8a7db4c037cdd850fa1f5014edbb0331863d1692d65bb4039d29d812373d4896f70420bc6a3744cb43d0aa2d5f4f643997ad4fd98cf81081545c2cbfbe90635dae8e75eef2b695d77578390b350fa1fa4b64f226b4be7c54b066db735e4bb027b6e95900091be946d33e0f37ce0337a01e0b001e202d80c8e6bd3d47d89f7585f0b668d5fcdb66a48c0ec63a76ed0766d576f695e1aa12725a40ba549b1bca02a5a9db5038407ecbfa2f05aa8e62f37bbec4ba2c60b43b29d5c3fed2c8ed617cd6165afb60f3696f04baa98647eed8efbb8687523deb0abbb48a85fc20047d12f629d84826dae0a30c7bbcb3bf3787d186d86d9c518718dfcb49cba91be9ce48fda10fec5a78d40037fc7078a23c59e32c8f5cdea85e02e24d989370ff2070ca087a1617fc81867bf855c986b26163deca5bc745f4a25295a061db6d4819d8047cd5545dc66e67ecc24af55fd00deca200ff46dd0c43aa9383ca05e924dff743af420182621c34a3b465cb7b5557d3cef8caa1a8827b1795c1267ada3f2f37a0ec884daccdeb1ee61d61edeb3677d2ebe59f502e99ce9ae652883786c1fe657887824aed13fa852c1e0ee5c4706d583a0d884b1b2ee26a030eb44e9863b5375823f4c12b980dae77dd7c448a0228aea726f4e5a8e11759554f8e6f72e7592acd47a1620dc3883d4af230ca975ca87f03ea13a7569f4aa63687eac28d6dcf83966a546487797c452aaf7d9915a649d01f690bafd8d8558566bad9abfed313e293023d1490207ce971d6d85742566568e1f08a802d70f4f2070acd5e2e8f691b955fce22b59fe94d49bf9817a29c58c74b91a16c5ee50ce7bcff5dd5be05bf250698626f290b9d943fe319d563bdfc712fab57ea2aac38ad57236ccfedc378eae9046b09527d02b5aa5ce8ea38b6b3586e6efa2d7224ab0de5b2f36cdc05d3bf5e6f9ad6921d1671918b8653033828fea71a9bdd92a4175f42c9f634055cc4a89d950e6809350dfe8940b137b3c832672ad4e5664f9ce7213e3848e3f7d5ee88be2e9fc17aed09feda48a3a3a1dc94c9ca45ba0e500f9dbe231e90bec78f91b89336eaf70a90adcca493b67b44c37f8fd6a7f21afd06d6164c8e8af28c726460fd46ef795727b8fb030e5230ba72312dd9abf5ffa5099c1d3d156e1ff43ee649857417fd628c53ee78aa6b8f7fe23fba21da1bebaeaf183803f4dbc7e4cbd9ac0f34a9fc1aa37ea20bf8e78348beadc665d08a469d3d40ed5c307a17140c418ece6fcfb70aa1e43feec6a49762c3504c7769f27679e5fc682a5ecc6749acc043fda3674956f67f1eedaf65b29da8bc899db6d48decb44278c3c0fe17de1b663f4dec396d5d5d1fd352ffdd5426bd2cfd4fe38a026c32314c11deaad8548ab284a6c20abc53952dff633f99fc6163e15778f72314984d5417106034b0a671ceca2ce9a2dba12559014c2039c19c399cc55a4849ab99c565b14b74c0564d2c49f04b910054af2c500516ec8f22538f1456e06a3f88e7ad5f3675bd422fefc03405d594171ed4f9684958f092a8d16c987c84c3862b900a46b92444dfcc108ea44e249a6568dd0b4567ea70f375a5d98c25f4b9ed900bd76eccaebd9c376ea35bd0e073ffb2b7260509f3fbdaa26dfc1c1ca92c0b6f581078d86ada9f1a539a5f36e7f587534fd73078eef37bcc1baa69253fc724d6336c7e261369ba076b2930f708615e32e6e52ee8258a0390d242506cb011c85766f1111b00eeb07a6900b9c6b44f1eed594386cc0301d223a919cec928f18de709dc7906cc6cd4ddda0bafc40e6147381413d96932e97f607c9067754bf0ba9578bb21363e445626ded6c67766f33deae67cdcdc97571deb4eaf1901deeb8f8460ce4e1830b6bd58035c1869862e5e3a55ae10387e61541eaea8d4d6b861e3d4d4b8aadaff30ced160074544f941648e41b72c165d3160538113e7d031de33d89c4aac68c714c13b32c5ac1a8673028090762cc6bb893853f5e2c5dfa13e9ecdd6cf990fad7067c60194b585a11a5e68a767bc8e2c0f0da692d2703e6bf1044a329210dc9335291db43d619a9f94589665dfaf5b59adfefe88004575dc67276a00cd40574ce6c608f0b963d53801030f0323bbef84767988951bf0075d5ae995f86c8affe0bc9898e33bf4496d10e00528e30d669095d0c0bb68ce281ff04fb968bdb2263e8458ed215dc3bb682c916b70531fc9c1e0c157859f4f8bfe18d65ca7676299d2a06a3aeb1e9829a67c904902eca0183f5aaacb1a14ee40898651aab968ef6b3a3a94a6b3e86b8ba60318c08721993225d72840d7e651f5f014d87d6d96e3817a3f9cf899a8a6f40fb39287c8802825df1d0e0cee488399646f1f78a266c8226a73111a6d60b32de2cbf0cba142a640496f90a72b44df20bb41a179ad6011af9bfad62f4f5681104eb31393280558fed38752617d6d43eb2b7a02eb3f50261f4262225a1c49b4bf6ff3f6ebd6abf3457862c8369ed1506f1715d2a901c633af4cf378f757747ed57ddf2c17c445b910d7f6cbf789cb609754c21beedc871aa5ded75ab19f994949d93d06b880788353d908854c21f7607480384b0878fe0d2d4bccfb9ac9d7f65d318f71d4524aa5adc74b49306d200cea4952d60af73b817151bb5119effb0c3bf9429b77c11b19087e89165a951b743e7a9688a7c5a04d813d414c90c78dbdc0425c0bfe9f1293052ae88df1b9a60bab5f7b883ba0c312e234eb026fc0c9869898d982a3ffb2a98da8be4b5308e94e1cf72938a5a7ca0034cf14833731a4959cd18aabb95177cf4c8a092609044b4dd5ccbd49760b6aac03edb8379d53283e0e47e5719b7596db74f6d946d72a6b2b523ad5162aab117132d50ba338c69be3ef4a1f62f50702c18eb62971b8fe5a746beec7b0582c8c127ae69b07f7e6a1f8f4621fb8c1b43b0d157d47f948c1a7b1250c776649ccc9b3310f26110ace85c76a04db80604b6dd06e40000bb691e6a3890966dc50301df01ef9b2c3d2fc4f419f7d72d711bfb9d68963a972859b7972e1a0db3e392b576ca6e23147a232284ecd9494a75bd0e3cdccf63bfc7bec32404e22524557ac751b2dc6aa33cc3929397ec75a8d290c2e0a82527eac5b62c43e3b384ce3eb5dfebeaf86b55fd457161df2004d4c8419a4b3d24becc1f17aa2e288db63b55a96b3416d4a3b2bfc9a6c6bb8c7c1f1cc0a94273161888d07a91227387c33d240b17fb27c8a8071ade1f11b4073e6e869efe27347e52aef54cd3d482767a30b846f6863c0ce6d6c026d835300db594635392134032552511f85b82150d57ab3ceb49c803916248c1ac1cff59c1cc29504c1017d1d7b6789eb8858c545c2f43f8493a4bfd3d9ee9894a5d6cf8e5400f8f97a69414e680891cb32823e8113adb154ad77f02ec3174456121aeb04d15046018c376791155f2e45f02f1c3aedcdb5dc9d3582a3487ccd9a4cc71ed0e22b41079b1b70cc380e5f47cd0d2e627368287346d0efc433084183be92fef49e0cfea93d7ad341f119dd2feec3d19156250b7a31133e812511f34dd00019c5205934c22527f2ada2e0f99d875e3a90a0ca489fa42ccbd732aa3c99939183dc541223fd1839c6601ef004f51495ce73e86b17cd991086d7969f2b2768c1bd5d72c79476d491067443438ec60548737cf538081a99ca9338353b5600db9d4a3ec8b9d0c568965908e8d44f3467aaf654f592c9b10ed32b65e690172fe34828afe285e994f115291396530f30c82e8c283dd8a8f1b60427a928fa3a756c19a676a519c079f0a573d58551edc3dcd2fe69c78cb735dab07decb3b2781e6c41feb573e1f823ee2c13dae7e46444e892cc49ab1808c3867bbd6ca0746be316138a0b7ce661a87d183b083b75b922006caa161c5c08dabc0da06a64a4edc96c4ebd47545f378e40831468b4dc659a272ea6ad15b7e891c090933ef3371d9166b7dd71b0c4281671ae24bce535ddfdb13e43d64d500b1c1ca476a362e189d34ac6ba856c5e6832fd003e6783fed439026c0d2eed3e0df85fed9bc6a4b7f1647f30047706e58dfeadae31d0ba81d5db7c2354bdb3dd4f4dc8c6448a9c3a054a93268f4e1010f08a3ae782dc87c35491ce8f784e11da89c83a80c5d90e3aa1a20cd9e38d46ecdefebd25838864d442d26409117168fc07a14d71813ca0313502a8f3f9935d3c0017e56a77edbd19147296020709ba08fc083f94c955b36539bb7bacb187167b80792442496a923d747b00fdbfa5bb9d3b7bd5e38b1e4ef45092071a792891072e8fa20f96ba8cfb69f2ed636eede3227367a7585b8bc7173c4cc0c3c42368cb8ead345bc02da557cf3d585e7ecf2b9deaec73d19e8414940321241588fa5026fa438788866a0734a6776672d5cc0cec3d5f2edb3b9d65747cc99cd0b1778e4d3303cdf2f27099c3c0325f1f9615e9cc7f1cf378e6ce5c46c7d7352f121daf79915c0900faff7a479cff577247d6d21d64eeb863c97fbd77d0ff9ae560fdbf1c2fff55ced4ffefe424fd57b2cb11b297ecd0c20eabff5bb5a3ea2b931dbf7f3b68a6ba3a12f0f55647ae0e10eac8311bc5af0fd37155e938a2038239103047d57fed73fcc431234e135f75712c90030f39ccc831460edbff5f5d1c69c471260eb2da0b871a38bafce01082838237aa78c3ea1fef468c7778c7bbba07b41456f91e73d58d2c37a6dcd0a00d30da30a18d0dd810830d16d8386283c87f25733f9dc6bc5c6692e293dcdae7eb3db4e7f25549dccfdcedd945eb14cfc0fd3b6919cc6d1228cf3d074d1d058e01e05c8143548d7047b27e6252ef9c797127896bc2916b94fff5b5c61035b27e4113d79d1a55bfa3064e0d9dbe4b6a9e9bea91a601e783b9591a61ea2b0daa3480bebefa0536bdd9fa7f73555fdb74d1a6375c7e466f92fe0d7d34e2fc1f41e38aa28146d2ffde261a05fcffe3805863cc773820d667d470c6ec8c0ecc78f335783985555377a798986165860a5f6fb51a53c5ba9c1913b8d17243839b10dc7c196dbe5a736f6e69cfd645310f962f6b5df456a332b294b15406080878a30e297d66afd2982f9b1b78e0c185b26420800c27c858818c236db4da00d1864a1b2063dc31069c31c08c011c2303366cd818c18604362f31ecf8af3e6c8444b7aed1a8e005c2f4595eb39f48b1d8cd325dbd63b1255deb144669b02471faea7031a64aa7348dc26ad944352eaf2e9b99eb74e5dbda915b2b064e0c08b4cad0eaa245b4c6cd9aab355cd64cad317280ac034c1da0270c337e08a5d5ec3c1b199dd0173eb9e9a22f17ce5d1d1d69595e1cacd67307c7cb474b751f3b0e97b94ba3741b0ac63b5dcf3e8cf92e5896b7e3feffc9ef8401465613ba4d30ac4a30a66660b8bed8fae2ea79367b5fd88efb1117ed5f50f3dc6ad0d06825d550a971a901e285d6bf17545e3cfdd71d2f78febf6661bdce9b84acaaac5e9652175b5f499ca37c971878bbb8aab60baa97eb76d105fdca05b9378f7281f55c503d142e92ec6c01e7ff2bd98d6c81f5ff556a8baaef37975b24556bb7e0f94f03e7d360a569e1abb532998c56b4ab15e9b053afdf1e59d68265794f4ee6232da6ba163c5968fda6ac3d77308b25b2b05f75a6dbe4bc2c807c182361610725710e8b3075ebe0a51a0baaaf583cfdff6f2c8ca071534ddca55434366041834343bfd64ef2bb737ad3717685d68cee4a7c8597285d7e7245ef8a0bac00e3bf623ca356dc6085b642c87f1d476b2d76aa028c2aa8fe2bdff49298ac42c97fede4ace451aaf8af5055a854a17a4685182a6cfaacba29d89822ccff578ec9dcf53ed2f593da296c53e85ce04c0a3752c4f0bf779a16852e8a09b0f2c052022b0b16396ba2e3d010144d401100282e140a3cd1e68584fa130738a184134027ac13449a98a309ad87a242c76ac926e8993767ae3843c3192c678aec1860ebffe9489b68ac7724744442ea2b65800898d062020526624cb8aec6b8c2ba0ae2aab6441f4b44b1b384124be09608a2841925a0fe5f08f338fea12f9779713566855e38a11ae47a9a29591ab34a3d738f98a7338f635fb7424196ee68fa1c7137a374bbecf962a9ab53827b26a9f1d8318f6325252a78592c762b9ada4637895c1240ae7e717ba769b1d8cd8c16334264202103124596a4658931797d348b7ba6be74ece9cc1b553b62ea88a32388bcf93b79df298059000facdeec58dd600582d50e01b42040553d3a72d52ceed72816bbbf608ef69e7b3717ad7c739d5dd4c6a8b975b9b9cfb74d23dc30228d115346d022ba28224c114f8a08abc5fd5aa1201f1b2f771b840b72535fe0b983e70e565cb38bee1eecd22eaf06f5264fdc7f9ad4b8a9e30ec4ba4c56193265a6ca2c512a83001158b59b3b445461222c113a4368e9f41056bf330497ff1f62082021dcfc5fed08c1e5ab105688255508325aff64ccfc3f991ac824fd7fbdc11c19d74e105a40bc1404d54e1049fe7f4c9cffdfe7183463c8e43154ff558f791ae31a03e4ff81c8faaf647680a07a2034c6393176ece0d4f5c962b11b8ec4411c259391b458ec46d27cd8a4c562371f36f300fe872d7e70e107a31f787e681047b94cdce4030b3e3cf940c364852153751800f4c0460f34f470f64079d88287137818daa18d1da8f87ffbca26ad1a197d3a20ee75cc77dd8773b02cc9cb31df1466764cf28e9976180213074c1260b28041327193ee0bdff982bf4ca043153ae8a0c3eddf87cd0cc47a27073239ec7278e500e49f6ed3d2a0e9c5031cf6c081091c64c041041cc627cf5cf0cce932effddae80d40dc3075c3941b10b0818d0d3fecd8e042b52f17f6ed8eebb9027db8675a8bb3e1470d65d400a686df7fb5380a47f14d69808306313450d150c00c59cce065062019f2e842063132ec6428ba3f5d2673ce52580d9e395cde7e06cf1cdd669510552d544955415545a90a12039a188c88414a0c406f7d96c2acb61416f3d11faa712e667bc71a0643171892bce0e685245ed02f8c6fed6e53174c178ab4a0a6053228b430a585a11682b0f086ef1d16645de2e8f244972532f89d2e5e9ecb175cb47009dae14264cb165bacfebb111f36f316ba821d2b58b1020b2b7cb002102d2c6861d2e2da518105159a54e84923052f290420052b296880821d2850f15f2d7f5d9792de0d0507042080140420446507950f54552a8969d5874d9c3b61eb8434279c7042d25f18660a809a9d009c09802e000fec98b08709624cb0b5f2d2ad29edf9eef26ed39789bb8b9ab8290b145978960c4a18a3041e4a482aa1032c6760c1fa7ccdab71b9db5876825004b30461c121536d4c113175c314d554d15712e782d707abbaeec3666ca6c4fab0992dc53025b0d8ee08a8052c03840262f0ff3158b5b6779c0f9b198731dfc9e898e49499e7c691f9fe2ec73829a0ffff73ebf4f2ffffeafd8c72fe2b812931b199f55f7914f36be4668e316d5f03663007c41a87cd608ea4e1cc6f64c6e1cccd7e4da86242583335b3c091254c35739991b29cd546963b4b922c40be92b8842d96c212aa4a48fa2f81ab81c5eaff93b0f0d059504d904bb0289cdafaaf98df5e96d77769309c9af2f23f65a78404734031802c84c0df3fd055cbf2fa3adddba4d966525a7c2b498579a9258ffb792475c05712632cb949e7738b93eabf9e40d59cc2d96cb3dba1994d093a9cb093397b972a77c7fc5696e7ae93df25e07038d6bb5bfef45f494c7b374eaff4ccac6d57b45c09fab9c9a56e9b94c4e5ee7aebb6e6b34a86bf2a0ed35bf378b7dc456d29fd4bf235092c5bca3b73bf7047b17f9df274e69b735c37d04c6bd59da69ae9f17d6c7c29f8824a389803758eaa1dd998e8cd5294ef5e3bc7529787250fe5d37f2d79f6521496e5dd7217d54394ad5abacd282015eb4d827944c209ffff34db8c9ae5ed32fc0bde623159bf49b25b51afdf244ceea3dc940be839d19ba147ebf1f0e4e0e9c0a3c20bdac5f9d1e2acb975596e3df21ca9716ef4a9758647a51d98ffdf25c16ce01db00e47109259ffbdeb8bb38f0fb526dff5ab336de4944e8ace486e8d9c0d39107249ee19d789f05eb943f787edc262b11ce0ce0871070871617022e08258d9c38a15cc0677f98399fcfa6ea71877fe7cb7d77b758cf57e6985b27ae3283466cdf276e0e554679f4ad22324e6714b7d6abf3c9ea53e40ec4387d47dea3649833a1fa94f235aa1ec0f85b2b7d7793a5b91b242046a0d9497af3f6cfab02e77a8a1ff7f4b75de65cc7f18a94a5905032a5c5099a24205c99433a6bc306584294323c019a1cc085446d879d2aa50169fdcf45d12fbf0ad665c4d9de92eeb4b2994d5653316b354f0e9828ac52c151d27b7a9bb519e76788222a58fffeaa27c5639d6c0ddb30fc9d7b30c93fb88c453b7c44c25e65b174ab1752914f0947a86e224c67f9d39dd9c8e38f544c9238a9830cacff24b61bb5cdb6d4ad238b6349334da8d707c7323bc8d71eb12de8cc29baca98da61a6aa7dceabddb556fbd7937172c390e9663c799efdba33d9316c7bb39730a6b29cc7fb1d88dbcb857e66bbe9ae8e8e42269a43e374fe291678ee3b2be92215398df6da6a9af4c400fdc5a0ad37f15818cc63f8ea5b69226119644789a8900544500f2a1cd46c35732e36c08d4d8d4ac086b586a4098ded3b684c412d5925e02fa587f0529bd51a2416904a520509480c2a5c248de84e39d6db216eb274b3cd13d59e0ff13f8f0c9014ece70428493149c3439095ea0595eba7b79c78a6b1e6dcf2151589fd919ee86b41e6859425aa6edfc7f0c86ebbb4cba4a7a4ad2811202560821b83e98e3bbc95613344daa824d9af03071f353d7c7c40bf63149fa0f320182e4e6bf6a1cb5790ec9cb77a4a719920e3dd23afaf2ff7db7cdbecb232323378cc2184d31d2998151cf1da43498d39bc463e79bc4313258f6d059389332a3397797785912b524b6040310b4fe6b4f4fcfad5663022129048101d91ab21e42991559051f50f1726d4ac30f46f80082a233455bc2a2a8a29ea2204a7aceb775bbb414d6d343af92590c8f308615cb12c66e1506f3000b0f82a107503c3820c913611215ea8be2dcc5598a73b90ee074504407be0e8088d80889b2422217be9eba7bea3ae6d61acd3808830314420ea6381832b4c5d0008644181a82440d922d2192a0ffeffdfe28ce71939e3b6871bb8d1b427da8b5f487da20ae772138a110d6bf9097ff1a0a3d09b978fc066585415e8292aed9cd73cf80f8d81a076f2800c180747e041e39e2083f82a4ee51771b977c9bd7d21ded0ceb0b35090cced7cef386e9de83fac25008614d962ecd926eb4898912ed469930d594e8528d22318102657644991835d1924899109293a7334fe711ebffe48d5575b43fd2ff89f3b5bf30df3e170deafb1366b77f7a0fc4bbf087672f853e702ae953f53ff3b17df5e1f97f655d20c7e42c7c51e5a8694467aa8486afa7af7c53a1dbebff9f977bcd850574bd50cd9e4b59d1d4f515d152c4a988920dead8a08c7003321b3411e183c812447e4490febfe3123635654a595efa439bec58e6a89a0db223ee6714dde39dfb0e29c09030e110010c5132a4811e37c21e227aaa843d409ef6b3b4514a8fa6d01f3ac555ab18766948efa057140c2d814285148130833032a0fa7f4a474b61c172f3607ad311030f3058e002362e18407881ed8208849021c4082150421c1823b4404d688196d002a6d0820bc20ab0082be8153c5500840224420ac6841448a180011e2e7882b3232a31ea437ff02e97b49b3956edaba9a976a4c9ba8a58ababb4c955b3e3d854962e9a7b367f2f17ed14c6b7edb69fe58f8ed6065118b5bb7cf99e1569dc03ea7c7de1046e3e9cc08bc98d7c38416f827e811f7c28419cff0f253042825e28c14cdc0ac5abffff99c8e57f168abf2ada44a09ba813c609432d8c9b8464be29b4ffe3a74eb7c390e73fa82fad4ae1ced6ffbf041fee68853b56e14ed5d71d9beb77be2902381170f155262b9ada463257eeecb9b343f0e667101ca0f326d429a383756ea18e90ce030fe8f1c098f0812af505d38d30cb64e469a781588fa10354a103b6d0010dc206daf87f007cd8401a0d50d1400b0d9cd04053d8001006900819a8aa2f1bc451b8586c577335bd6c94beba111285d50e8391e5a6d66673d55e40ac5db41b099a3d77769be33cdea541dd1788024cb840090b38850b1c102a7044a80009a102b250019d8741d9bdd3342ae4f599b6dac2203b0c32250c127ac0411092da010e81680981140199204c008e300123c204aa9ec952582e4c20294c80e7bfd69e3b7b8880508840901001044200688500480300600800a010003e0784718055b52f26fa439f5c354b71101dedab89c95a8bf1aed697cbc4bcfa72f5186c9be7a53897ef0fe3dd2ef3bd9bddd0003d20f8d080364203aa080d20131ad0141a208605c829404c580094f0c71e5f7b13fd9d662c76b33e6b4baac4c992fa863f7ae18f26e18f22e10f0ac28f23fc1dc2ef127e511f73fa78f34fbc3e7afaf83957735298639bb3644e057320e0e3007c70a99dfed0266b852c85551826b70e669f6024bfbd9e3b3bb54da40bc62d138c52db4403fcfae868fb2cce56128d8305d3671ca93849ffb5cab1f535cb81f5726cf9c7c921c611a7963b0e20e29012c70f38848083eaabe5b1d8367376b769af3c0783e5b9437a63cd1b27bce17a230337b2dc38c18d1f371668c34c1bbb366e6cf4c1061998f34c831948ea4b06e99847dfa8738977b4dab9c6baa7cb3b1aad5b6fbbcd5186b1e4a658ec46c7974c26bb15f1749eba4ae62e9bcc5d768ec98b7b38d90b4ed6c3a98263fbafc00ebcc1f935b4fed7a046746d8daaa5dfaf71fb7ff2aad1871a6dfe5f8dad46086af0fc63a9edfb5d60532c7623c96bd338401a574f92b358ec66e2591ab4341a78a3554d7dcd29bc4d9b0de228fabbc03755e11ba537ae8e0619684c8186feafb6fb2cedddc45b771c44c3e88c38ff729dd1cff0c08c2d1c745d9ece52179739ea3203cb0cfb66e8287193e5c6ea9fcc6e7a5fbbedbb7db91b9b797d3d2bb90152fb2ed19441a68c5e19495f06fd100173fc63c90878820010c868830c2c3254f851a23c9d6d647c1b356d826883dbdc360d8c71660c2fff97e6ce265a6f6cccb0d1c286890d0562a0214612627431686234a0e546cb072d520b8916903575ac19c01a14d620bd92d1aed63f60aa313149bad198c48c90826ccff66585e80732a1207d79eec5f7cb0ad122eaa2353c009230f608638e30be84f114c61130f800c30a306000e30a1844d27c31c517515f846a9a08d574351a7841871761bc7812666d3d45027bd9209b750951a35dcdba68474261f4655f41364b47fb52a231eba25d88c2846c76b7836c19ef603d93678d846cf48706d11f6ac44f3b443ff880be5cb1d86ee376e7b1d8cd5294d69ffa53492c952f90e29cee76bd33e6b9cb37a96f173c74a1d445045d24c005125cbc10858b0bb808c2c56fb1660b14b6b8a59993c68e304d99344f698ed21cd1420c2d7cd0c2a605d0ff636dad92ab666db6bc9472a099b9ab66e9685f4a945a1b106b6a97ac925da23fd4a6041b5279575f5fcfcc409d0e7613f3ca7387f9d541d96ea95033e39ec9bbdad2213558624ebbb6487a5792e29c35c546f4977b1d4b658143165ab258ca22812c7e6041c4ffaed669cca7fbf4a9ebb3d95c351af3a1584ce98cc592ffaf4634163c51d0c4f90fd160fda321533b1a9d06628dc656fb4e86e68a2bf20a2ef55783ed25aae48a5e7805fdaf405c2c760bada8590142156b54f1a58a5d154e55204085175464a18257ad893b8ee3f245a3ae198b59badb2e5a5f354bb3c1dc1dc912fb7254bfb7a87e4d199410122a781eeba6d8fa29acc229aa483c4592ff1bd5bb146ebe02b18eead794428aaaaf522491590a200fd35b875184717f19fbee5254bf375914bfbf611421d60b2b0b8bcc7f25b1a8b096604960a9d0a8ee0beaad6732ecbb4b396866992c16d365287e2114c127e0fcbf9ec07aa2ea770f183ef1abb42792c462b7279cb8aa79e704d557276c5f9d7055276e1356df9bf8fdd7739b4d2cf926787a3e03a7d228ddd2992b7e86ea4cd21903a0b906e08275ee66802524135b4c50e193d1e8c8040357695ca1f9aff6cabcfae08a678937d5363d29cbdc97a05aa20325e850828750092c4a24a94a5e58eff2528e8178079c254146125e92302109a1248a98c93223e6ab6dc2516b668a191090d8fa4ade1aad7d4b9133247a40a206246648547044968fe9887dc4ee88080ad04601c014e0cafed19ee9ad1b89d2f96cb2978bdff2cacaf21ef528dde5264e675911f6e59b6ca4a302231d473a8e244d26e36637bb912226dfbfdfd9713afb703afba4f879a4b38fa7b38fcb64b7225cfece5b910e3b9134dc35228ede4658fdcc88a96a499a35e2a9067194b9978c20229b6231d9ef37eb9792b422cc22ac14a153268d32539479a10c07968ea31d6db794c911264f9eb86ab632a92e6a7b103fb14f98d01ffac455b3380546d998e4ec5846c724671199711ce31fc744e8f4c221dcfcff0f613544d5104f43f07c0d85d8aa24e6098146882a2184709181f35fc9585532543adc69bf3d324f644232f4fa82082308aaaf63994d7de61ec4d3fff374ee4100d97a0c1add49eabdf518aaaf24dfbbdd1d637f0c2d331070f0145e02e2ea4320aa2a1053ff0fc4d3ff3b01e17a3170423157525ec4f4fe5f2726898ad991117120945496f7680a939af6dbdc3680cac60f667ea8e187da0faeaf967c59ebf281071f62f001c90707c2b8e1d4e9e5e472da2676da26760aa31406d6431ba49b73301896d23cb8e101061ea2f4b183103b60d9e1680720601000060d982b606c602cf8d2e6cbd5971cbe00bf98ff3e3e58aa0c5edfe5586ac3603152df1d4ddf5ab1b9a5b636cbb3c328df74dcd1be00d5ae83153a78d161b799e84b9b394891430e403958e0058d971fbc287929000702843854f5dbbb4cb72da56467bee948626b39c661c80d5aff7fc3ee06211be8b0c1cd5b5c2c26bb15c5629d7e407fbfd0869c0d3a358c514398b0862e350c551286f90f47031b210d6668508186dfff101a1a98018b19bacc50e5eb8839efd25e5ecdcb4d333020835628830ea10c31c850f4d5be8236d0d4550d5195a5ca15c318313011432e860d609803862560c0c1c0c0e5bb499020e5ed3d9ece96eece1cd69bc4658e5f1d0c162c83e50b48bc90e505272f087121cbdc3bbacf2b5bc9c6c18bebb962734bd9ede482052dc4d1c20d2d3461610e16c2b050020b1bb000a44b01c22e2774f1e1d2e675366229ac1bb13dd3b4a7a5e696c2fd2e792468660da3946faa2487b90b898b932d7a6c59638b9a2d02d8e264853a56c0aabbcc7167b2244fdf6a07ee1586b4bcd1c2464b172d4fb4e8a870c70f2a2c852a8090421cf54561fade9ee9a2f6f573d52cced158497176ec7987b9e5e97ca9de3b5a95c2e6b9353fcd8e83b707afc6e6760a96e535352eaf997f578724ebd8dc4eae221d7632b7d491ec6a1cbcb25b51efb25bd10e67f6dd1b22ca587267e7616ce24e5c96389eeb3e5cea868832df53434439c84d8d8590e4b31c22cafdf4613e44943970731edf4344d9d4badffe0d11e5ce7326ee57f01051c65a9bbc21a2dc4fb31c22ca389eeb9c24ef10513e8788f2ef72a010923c44944ddc71a5b9f91051c662ea8688f22e0fe1424872278788868832ae3c7d4344b93ca5323944944fdeeb4344b963de105136310f87f5ee976796b7e338fe01b7c6e1a86be27c9bd49793e5c63cde71e5d56133981bc27197eb2c15cc54a16c2c86cdcb75960ae5beb393fa52286ba990d804ce64b7a2a87e6f51fd9abf6dca8af4ce26c6923bc692bb4c96a4f3d89535aee47025caeff71be10764a3b165d84ab41b567a048d812fce9b66e6408ea536b6b493c1d2de0b84b2b15854bf375f50dfdc2fb0d3f166caf73571184be636aadfdbedb8c7cb308c77b01d895d514633e35c39a375ebdac4c489765b6272b444a32330cd6e4c966a4ab4b6d4ed91af26fa8a945c94c1b2272aceff88cb5fd6e9cc7fbb97f7776a5add3d58eadd336d6c46ab99e92f47f5cb6fcfccc09c33a770bf3a9d9b4499f9a8a7a82424504102d557acf78c840c721699cb07f57d41599b92a5e2a21c2be5a7cfaeef6d7deda1f9ef55fd7ab6b35f1dce057baedef3f8cdfa7f1e99caa3faffcab3ff95b7e4bf8a3c9dff17776f7ed7b41b773d580d9e0143f90a041494548d90783ae736ff515a7905527eb1ce5076bb475f2e2b927890498864183289fcff6a84448325cedcecd9c5dba58f1ebba575ea2ad99ec72051e7842e8c4ed44d904b53cddb67d602b1ee3adf7acdbb73622ace776adfa9c5dbe526b93f360c6b8275e1bec059e19e7042ac6461e58c951e30bfa8505a6969de71d439868456116a0050b52a7d8855e254d1a14a09558a50f182ca1154aa7cddbcebebb4badb8db8974ce8c94d6cc79e477d72ccad796e71ca11e21415c42948532818018dbeb8d4970c8eb080f894c49395a71f296048a9bf0e8542599cf93be98e4999f91e8fe0deed37ab525e4e7a38b989e2459420a230450921ca04e2ad0af12680db78b340bc01694aa2c98ad85484290b97386c6e1c2e868496a54f4aed3cfbf8dc6a4b3e3e4386d40d3429edb9ee97ab48879d38362fee4c404498238255b9f9a637dfdd4c04aaff1ad4358940e4ff83b960cebcb8d3992d8eed8bcd494d4e4dccd79e9981dbc49ad43bb779335aef14a6789b1788758d4b2d49b5966fba1467a9ccd20baa0a952a2e1fc7becd77afbdb4c4f3d55a7a2989315752bafaffaaf4fbda332731b72d51d2f9af50b2a090f99f9308a50765498502646b2d7d323d21f364ea959e24119fd0cab1b99da2f4e54eacb6e8848b139f131e7b2f2ea948eb342f2fd27e220db869409292acfe6b52af2cf72f842842582184104200a1091c4da2109bb8f05f5ffa02b18bdad7d4f5b96a96d298bd348a4da6262f2672884cb4b699b7be3491c91626529814bda2f4e5389d7d4858482820e99018f8af51faf2bc39c612a5af7991c423258eaa8eecefb652946874c68804233a93f3ff412fcea818ed2b88627ee998631607afa5b05b734a1d072f0ef39b9bd987aa32ee1e904a153ae35982f55f73bf4becff9913972cd9b6253a48342a82c0e6ff65b2de6f7924db42d68228db1fc08ade145151e4138b9a8afed25e9677ecf82a794154b24509d0ffd77d448c85896931e6daa2075b3c30c1830d92b4e1bb031fc40e70073a446210392112e54054428ea8894804fbdfbfda2d7596c26a59ee18a9effed5b2dc3f3af67bc4d3481c407971688c212e434890e481240b24412444840e206445ebf5e532854ea10cc4a03541565f839a7a262dcf6210d5ff2cc8f6ff391168ebdf5e8ead080444f55fe91581aa1c89e3089afffad219783919e4d7d467b9a5ee91a96d1e713a52b134ce2d8995cb57a0ad1689b03e6056db0ceaeb824dfdd77e05935784b96ab97754cf9c8ae31a2394ceb3d11c5db573acdbe2cfd6bf5c74f663f5ffe20f95167f6cfd3cf2e3f249f37a5d26a20f113e4fff7afb344f8b3e406ff3f9e7fbf562f28a4074a1a93c4d45579877f5a0acc65044174f2d6f9f1981639a7bf75f0d887591208a9c453610379043dca0890db46c30f424d67993a0f1af168b592a3e35d31f4a49736bead371cf14897041248b480436248c2154ffb5a7a7e3a82e45af7939e517d75766aab9781687040d59a027eb2b25b3a53ebb6dc92cf61cd1404b832fa20651a20656340842dff8ffba4dfac2e11c6e9bb8a89c897d9857a1b1dcfb578362d8bca645caa04d062f881910612007063a88184c61b0c3a047c4c0820bacf8ff0f5ebca0cb05572ea0a2103942d87411b2a3512c79dc3bf6c2bd3ca4f2285d8ea2243633def1bdbbd8d7bb49a3b51bb14214b0c08c68410dffb5dc9b52d102a50af0a8c0870a441029d8a35a8b710772dccf600efb76a74714740a9c8814bc441e3e78c21079b2441eaa0a65b1addcfbd7cbbd6d531737757d306aeecd34c19c09d64ca0459c60e8ffcbd24742594b652483b8b7f53612253842022ba20440a2688688a5b099cf2b9d16659769751cc7308e182a218655621882b8a3c70e13e24e0ce2ce0a3b48c41d0d2248f32486e1da35597d8ee890fa1a39d6510e411f22044188105481800251e78da86346d4b1a2ce4dd4f9213e50467cc0870754b0b81fae64d4b40a65a94f858ac52c15da7daaef42a13ebbd3dd7de810f10110c4079644076a70a0141db0406ca08fff11caf672ef1f2df7fe755c2779bfa54c762bba9c49cfcc527ab965b7a2cb316e6b8e4b1e6f9b38736f2e2b2a37c7e6353bc7220329880cc082b911e31dcd7626932d90c5f3e20256e20253ffe20274b6801515b8caa2025bea2d881931c896204d82182002c1128178f9aab798801e62024d245042024e0944202220c78c8840504420c97f0fd5992cd28f68de4bf580350e10e200de01444403f830e08c015844034ad100971a05882940271640452c80e7ff672ffe58f3238cf843ff18c57f437c23c4078acf81f83a5f3766d273fd9dc192674aea2b85f671c11c3ce6383107cb9c2a73329813f2a1c587152af061850f217b68fdbf6e6c528bd03eda6c2dbfb406617b04d1830c3d5808f540410f233d84e441471e0408f3a8210fa63c20c0830d1e5ff050c19cc23d3ddba419caeab214ce45a96327f5b9e34568b0dc5ab395668bcb5693ad5702da4840990470a9346629df774c71c70d77ac7007d21d3f42397bc8b9928345ce939c0be4e8d8f1851d63fe6bd05218ed99f5d06de619ad3db3a7278a6f60b6ed7293a0295360a4632c265ba2635291ec725c47b00ed7ffd77be940d32fc7744c753a9efee9006e4dc93a879939707324fd575a350eb135a8349545d458aa19000000500053100000181c24140dc8a462c1644cde3e14000051b47468dd9836d1649d54c81863600600004000400002775171a3e24156cadb10d96309df5fbd43f80c71507817fd707ca2c8e3c1d2c485cc87081f139026bb817ed313d887db583c563a9ebaeb5bb7465f92774d263f8d759f1c612a3d75c6e395969eef524e684d403039fded06032b9b8b89b1ad7039aab78d2d2f47692ba1e789b1137e6601a9be07bc8d895542b0971e36218b2f75be69489b84c1fe475eb70f9ee2f68d428f3f249d366b89df4583a6d4774b7559d03c9bdb48ed737f74ed6c625826a0006c7c21caf0724b86ad3fb229fceec5e1ccf3fd0df78cdd3e6eee6eedee483f8216b7e4a2dd6eeaaed2f7bd3bebe526a482e5357980d1cfe321c504e645716361f4761349d1818a174b657cc657a067877a3280ac2c82cf37b81a2e08cc74e7f20c4ebb55bda8aa05e39fb1f2864b60b424dfe96a000b7f94d84597095048a0101dd28cf37f4742b1717e4d0aa7bb14e47820446962155d27e3b9561af6014400add1ce74c2806852823a07782bd282522c52989188714c5fb60f17be64700c3be4e71ad3e92bd352738e569f5248fde93fbd02fbb45553ea760903b68a5da9b11740df2c9d7b33225a60b18279bc82351f7f7f974a1683920d4c981b42fe32395ec054b2949f6a39a3ace33cd21b2e356352a0a25343403a9037cc39166fedfce56c18b2b50eb9f6b8dbda5265a0e0bf4ce92e6aa0aa908736a8a57602b8a15f037d8171cc42a87db3094f7166539737a90b43a84344dc96d1377cfbc4f181761ebf30d4837fed1d28f2f0b81a23b59bce23fb8fa34a3fc048d61b171cf93ca840a4c69395ac7b4fad4e17b519e9ef94c1f3936f56fdff8441122b00ac6e16696e10909e7aa894156852d1bb91404fde7f1cd93c394617050641433236c309b9e31659f93dbaf9c143e3626036d248dc84a010620012796412a8d6661c6ca4a8a81a74b82e95e32a80f3d7694369ece556fe2148be8bcb1f3e2d4ee2f78501e83e229e331eae567489bfc48ead1b6133be1b837cc62c0e0de3a38a284ae8509a241e0ea0ca3d65b2d287c6e38d0ff6c5cfcba3375cd7cfc2f82f6f26596c900524ef851710d66d278b03d4903f76b00ddaa26ef7fcb507a889d598e915baa8ed2bd0fc63e68aa2cc46d96b8fb92625caa608a42be71154f9808222a06d62f4941b4907235e5e0cb6fc428297fa84e3914fcb8aa21f35eb1f4e3efa48d33287e4b1c525426070d5ecbf09e71ddd2051dd77b7b0d7dedda1b2cbcbb55ab504970ea21c04635b7f6d536c203f3d8a4cbead514548e382222dfd9723479271b35cf7887374495ba6a099ee386472ff770698215588c601b4dba6a8c981967ffbda66cc348bb3639bb1bc38bc1bdd7048808057c1bf480403ad0644329cec5701f92c861dc4ceae7755e8b3596465cfcc913fdfda9e5c348a921d980833cb0aabf0d0af2d233ac23c7f743e86033314c91709ba6756b364ed92bdc9d52003e54953eb85354500b251666f971fad0521fd20812e405fccd99035add92a61782ae43bb93141232aad6cfd8ad71087158ccae296b54447278c839e8536f0e86a55e2ad13e7c86df20524abe12cdccd9d45b144ef69afb4d6b1e0d50b99ddbd7187b808395f77aeb60e6f042c8fd21e501147273969bc6b4abc81744d90005d96c48a4b57bf18b91fe7c1d7a5258fa78b2879d15b9c6491b336d8b5b549573ee5c65930f4049f751bb77edb9bbf836980e34317c2314a1abcec9b11beb4209ee15943d8bff61b0b6ade42d640bd7ebe44ad29394855fc3fe6b19ac91b4d55002d62887bfe83fc7f431fbc3b5354f67e420a455fae7b4f572cd23e2d8a22cdce13765a36c309f4356977becf04e0cd9ebe9644c939131b62502b0676e4b20201db11e5fa8758c84a2f739c6ac1da4462c11ea5d729b9e3a3109166e6da97a247816fd0dfbc9a49a4f8996cb12f007c272c1dfe870ed836a4bf793e00ec2728fc7f1ed814d35f9384dbb6fcad0cb8eb2adfcc713b22b5b6f167a81ec187402bdb1ba080fe264942221a2b9067714f6e71dc0067b42e36b36ca7fb45cccb6739f75c0773b45bfcc395d0435eb8f8a125d7f1cdd1897982fb7c2b5a18919b3ee6e490276fcfee9631ed40c5ce0d822c663d996d823da258e0405d234af66b570e482f394e9590dc7a45afaff02e5bb4531429ab89aff67d69a376a83b935a9ca5b8157b7549994e1a09b8ab34bc5f63f2414f08829a599f4578deee8e044d68354970f442b13b9614a4ae3b7781411e13ad77fd496a91b72490e754019a7c78aa39782a073d3cb5efefa9d2f2d5905dc7873499e343c3e61aed628795593bbb4facec798bc7a4496508809e61e024b2b314f4fb35136312fad3a05e615c1b9eb706361f10d2bd919d7f1ba457288068e3db68386d44569b480dea4d8cb86702440cef39b136f09491398c313d2d8696d268582cadc5ad2df883eaec5d9a6c72eecac5fa2358745988923db4f7d585aec6ecafd99015b41fd0c2e393d81f081665b89158d5f39410969ae340685babfbfc2049d2fae3a3e76fcad774217616dc9d659dd46ea19b43661991db2d885eeca836e411f7ff34c034102160019910e831b48f54bc834c59e19962e952f6fc48077fcedcd2695dd280a687bfd30200ee1aa37c8448a0c56592691e48d459834139723e0c74c8bcb420a655a3e8d01f27b54ca76e627b8df283d809da70153c5483cb63e9801cad5057d7d1e846b512f6efcf1fd9b8a00210dd8cfb2af26287610d0e7096d6229e759c87545c77429723e4b9b1cdba6d30f74b67e46d8d73ecb0640e483c4007b1cca60ce7618a8bcfd7ca907090b16dd427fcfc114f471bfa1d20c8faa9c1d37bc9d01bc9e746a58ea6304698e5c5cc9faaefdb5e0af3de64831f63bf00451cc1bc4b68fcabfe1c62e029ccf3f9d82edafed7b461a2adfd28594cdc9a18e47af01d18fc6c6154cbf9acecec1d4d94be7769cc7f00cb771f49cf6463ce00169763628784172b58858c64f2ae55561ac55372d9c8ed559df71f0d0449495c81f12b77fb3391c31cc647d091c59c437387d3e0c54b4a1d3cc93572d6f4b942e41c83f67f8ef69f30fbf3216acf155facbed553df092374dd21211adf1331561c033774120c487b4f8880993e186ceb209eb0f2ac68b47612e1b83851d7c0ce61f90e669b2e3e364659c236871ff0ff5ac45d51904d0bb8515b40d6cd2c2f9acd54917957ac0dfbe845c9e9c915a753c5c33ce3b6ca9297faf3d6c8f896c91c1a58492b36934b3ad9d0e29898e4353a958e67a98c8c5a5d29c5db1f9f28ad797380010600a83d13176b3800a36f1776b94916e4d9b2b86448587a40c896b1e266c4648e2ba99176ba86e42b251df4d19ac48921d7283c834d02be558bf76b9c06ea22fcfaf6a8f7a3540c46f73a7c2d19dbde919c49c1f6f5ffb217fa421fe838393009cda0d358f2bc242293f1c0268eb2cc6a534196bdcee5c5358662e5ab1db400c9441768d050bd24ecbaa0f426f437e03b94ace7124ac2d667106dc9df2e05923249cd50e949efa52ce557ad99edef7ddc48831be8926dbb9d63299e09a350d4acdcf7034745fb19310180b52485c68306ef97d896f010ddc39012ed162bc5c07e5b47d2fa307974227a5cd3ea77b1351e0913563034fea7429e3f0aafe13a349e4ea1412db71df8200fd5fb4db617c34b842bc1b500a60c53153893bc8c74039ba79115da275b0d949c9f4c90521b9337972eaccc7b2c007aa5416e163665b51d3b22c405e61bbeb2465196277331caef2092e6f155e8b4ca1a8fcab8415f3d73096f305cc4502bbab3503cd0e90acebbffee6f1b5d5cb12c8c37cabe37e2ed497a4c9ca3475bb2a4c285638979b04adc41ea3405475cbfd9afb140bd7f899c855547b425dee813824694dde1038c6f22ab98cf4ae5923ec5709428eb27eaa863dbe40fc4ff665edfad82effbb1e811a7fd2141a9c46849f63063689aae5ade2d51bcdda66e335856cce2e66b1385d372393463290cc4e9a9490bf7843ab38953d2ff9437eb9d27690c0cc955b9ba451ca57a628ccf47709ae496133c905cd751494ee9a4e68ac14618c51c11cb1e6e0b83a90547542073e137bffb261c59008a99f9ed693070adebacb8409c5c4bdbd3d27303f4477e9e2f4a1cd4671b03836165a42d5a18b7855600ce7183eababb78327e1b097d06df400a421d9cb6afc5d5c9766e943f4f2c867d4761d840f4b4e09d6cbd0ee7ff11a93d81a7b4103dbcb8639c4e2bbc198667a3af4413f0fecf1b029d757b60d9da83476a2ae7d4a688b5c1b249e3b0ac94f3b97d71b690398ee64a46bda2edcb2981d1dcd227cf429693721c9efa560e9fea303ae39671d1df9132ad776aabecfb7866319c38cc495ae9568c30100ec09e02cc67353e868ae452f3f8683fb73a630ab79ae08dabb7ff4f65b683e4423ba5fa1abd67126c0346e2c80c77f0f634ac16539f8a994cce7ea77e7f3d21cf518b871277fe1923105b6bb9191e50595aa81c0604eb3c57bc109927363ee99f2c4db9ddb7af2e2bb166aeacd4857962b18ec4e1af6c5398b7e2c97edcd1853dd1798e86900a7406967194318f950c7115b2277443e2daa6d1c028af35fb91d7268857122687b0fe6a8b21c962fb1e371d6406b390313748dffac8394ef2be8e7a8a5409de1790880aba4d90b33f9974800c05cda41e21d43ac572d5d9dc1d3d301064d20acf204e6d44d8a6217b63a8c7b095cfb5088f12e2821d6d61719d13a82374022551b8f6bcc56c1b502b84eb9fa0deb0814eedc76308e83c3d2e493c2cbe96d7ebde5269f457f3511e7efe6aba1db8e6b1a106ef1f64c5845d9d7cf099e5872bfae2c9fb752a057057997ca3a0a203e12308d67fbfd4ae34dd9bd03c9fbc1184e3e866f7101569c4357808242985f5d793e9ad5cf91b69be5817c2f2d4c8f01757861d62f4bbef7bdd043f8f8fe9ee1fa8477ac2d2da62ee0cad16e9aa10ea8a989c909c7c5e48dfd7d5e500721d07a527f4f860ea3c92b3a65223646357bde4d1d111ac009c065ff50e68d6974e32227193e0c0dedad1e35853ef3761b1e029babd9cca9af399e0061643c11a65c42af84f2f8e3f38aa69d045a7f38cb18302b5817eabe5248a8b8a0d5d8a3950abe3ff237c4de42d16d2c614fafe6f3007bdca9ec0d2e57a4e82870a06bb56bbf3c1329535beb2c1773e0f347607daf93c2771108fe77073d8abf04379c17bdd89f3fa896df8890853ead950a3b40b1d200d72ae57b999796a75e3aea310c8a78e7533ce3b9467b9b0ccdef0c506464d5d1c46b7ea7a4d48d721375d5eb1b055dcd6c676562305f499efe83791985f600f54dc91ea511b0a93baa7e73b856ed206e6321a3df0386b1367e058292818868e4bea19f844bd6f6eaf38896e568190de16a487086bf3ac279e482b0913d5a2b4c7d0641bc48ce918d1ba6bb1c7e9c0d58a0eabad985efa2cfe7b04bb23f5d800d0de0ffa4c6c5e5061c192d4673fb5dcbee650739aecc817c64792f6ed9a97705d75d475b0a908aade2948910662f85acba260d617191717bd2f8326d92fb1728e93198d130b6006c25fda320d32351f912f832869a9a6c31af5d04a038160b194a0480f692195dd948f07b848ff42b3c378898d22b7e33d4f8e8f29a083ba70a5ba7be9372ae7acf753e42f5ab88762d25e0810f847d7b84e5f73506562bb5c6d815c74b84de897e8989dfef4330a7c0d06ab5a52fa8fad16913d2df9cea779553a35c2e7f41c6177108fb31fb5bf953128530674d1d37c8ad88b7c60a051af84d8437a9491fc8ca0311e78646af93fd18521f3e609387b117d4c0207c35d0efcdea697cc0ef4c96bc7cd639c9fd21619209f11cf635a92e17fe39df11cd2a0f68d87eacd8967e5a0feda6e7a7b18ebeebbd82e5521f98aa9f50763b981c3551b22762ae161805a1223a55c5e919f2f36b4272c0bc229fe64781a91a46e0e96256754f077e090855b316b7c336060cdea31c33433a5b1c6ca7fb66a2ed8ab44a8c6e5196458403d36aa07d1c7f0d678c632fa7993b20b0880d58e611b801154ded240fe3e649f264bfb04bf377d89d5da43e14b07e113b3770fbe69a3ffcc863133f826537c62f555f0ce22ffc9d84326de3592ff116399453711b6978da7dfa4105a5c2e7518740990d91f297002b88718ec311988a157922a7407476925ff325bffea886fe94e54bf5a993d0d9ea1c02c81682ee1d388a08579d75de603b53f57e78d5d53bf0c5927ad7891c9921482b5bf6892c995e6810109197b42db6f3e2e72f8563c989a865370f050b332209c40aa0b273d0c923bc440dd7c1708ddb98e1f1a3174e25ca082a96607728e86c97b666696c4e776430f37f5a754ea6f5a73648f2462bebaa5167c23ba80065d244cdb18b10fef334d91d1969d8ca27721b25e90b1233e8b44b8e4894912bb698b6d680d5f77dd81c4c02065586147f1be5384c36824d550cce5194cbf1c42d3925fd0cd2e816ea209b06bccbfa55dac3e78ca5a4be0e3f33181e3c6fd9300d9031608c73927e06285bb8075bd946a51785dd4a9c6d5d7e7e32768070797707b9a70d45d5d2c74f1f708d0a69eb50c231a501c6702639cca7f5c09da976f8d914230ab5c5ff788fc1e4a39481a49d737408d115a534c344c763d94b459879174a05bc8240b439153b372f0d516f89da1a804df2b65a78611da9d1ff922afccab36b82f6eb30e6f850998fd335afb9d29e4ece7709b55c7f7bf8f5413a0cd236e3edd0187dbcdc9455eb35bf0bc69d785d5141e92c219ca33d09d64b4c14ed0d58d9f02233dd4ced9a379993c0c8541c35c79c1af4e6c60982dd8cb55899a787534a69c72c00822bd0df36734805a65419fa87c7103fad03264e11307d06e614c1a34aa0884ad300dd5e5308000caa68e812df133ca6f14a8abaa185ede3ebb59041f6c566af611c46d5fb7d2b24b1e0b92d9ac577007b8489e6f41927a38fbd5b8690328c73b8a4e65b65c3c01c205eabdc3207c167e07b08b88a2b7b26b01fd1725cff7774df0920f5b76275d8fa2e0d9272494260db19a2304c01aa9ecbf699a21e7c3f6532cbd6ce926fbd4ba2c3a58be347304dc97f99783cd174f42e53eb8a9e2fee9dd2a995ba554d7d4e8ea475725390888cbb95119d9012b2eeb8e64f749d9783c902ea548d2bb17160b0e9c34ed05e1af04ae9403d7cba9588b0f8eb7684796e4b04883fb5580f6142338d970303248f4af43fcefeaa420f86b4630fcda9d6bef478c15dfc0fb2e733f16cc0848d3660bffc3443543c2c15ddd13e7202260bf07e6070f035cf4de1bc80a33ac174f870f8c009c64a57c4d4e34a3a62f5af16289c8a69550c1bbc7ece2631cac9a2001bc1400549b4138a051ce3c0f654e360fd2c0dabcd50b0b7da3bdc421fd7434b0f43cacc242138f5486a040a41b465cdee75ee1b27733260234e164e529d10e187ab413793dbc35b3a1979ecaf13528bc6e65e358dcb53c725de6e3d370aa58e04f6993298b87adb167187715659b2e85f755dd0648a3f3f5f009cffd81647764e134f8f32cbafdd79b00764ec24b02dc02c582b23fad89c39ac54d531bece98929bbfbce8c08f4adfc8ebc0911c5988cb0df10d8feb99ae89fa8f77e0fad3a8228d2299db807a695632a047f8457efb1e535990aeac15be2d6573cdaf0f0cea54c7f0baf0d5280815c605470119add45f0ba2cfe6af5c18ac63c75377891f458381eb3a14cb4ba9ccc397c67584e41a7ec17ee2207d48dfff7060125a1006fb1a2096e20e94d75eaaa0771c567ce85aec684b405ce200cf429c702508b899c74eecdc95ee08005d0fe2feee3b15b07728535421f9ca401a27336aec80bfceb6872f72865b07b0f343909ee7457d4a96142dceeb161a727d23811c0b71b986bcd54d17977f1996eb6b293b896232303aa8200c3f277cc5a79a465e127647e49134bd2b47b7c6fd1e2ff863dcd29d77363deb15f098ba98d8ab34060ee7b3ee1e16edb09f83953dc9d3202763e673a0653aae4a5006e39edc963045ecdbab1495c2e44b36dcab62694f35b99f3552ad5d3af077dc0f040ad48206eaaaaf22020dce7f33153a31f1f1e2cd3d6a85973b15f320ea047616cf67d2007e6d0e8b924fb660fef00f2451bcedaf860e5389474abdd8f06e40c1a7ffdeb8dc6c189c1fb6c74ee7a0c726b4f86cbc39ff1cdb88584be247c17e4bee8a1baeb82fff42c1fccc3efc857047e721870338954cc167473ecec22a7552e3b16b7d7da5b82189fdff58b3598b892907ffb496f919a471590b04106454b22e490a7027e449ae3379193e4670891575e9dc661796a7063ff789883ffe7b9962bb92f4ed55520a14acdfe49ee9c563fcb1cbc006097f2a2da17edc7a04e07d17137f3e483d29f0ea420143c9c43ed173f52a292aa941c611f9f579e0282501e701c5fdee386bfa7e5b12ed140bd15be54913c94d358d95405402762f0c4eab6e2f2c21baba3bb722887ef0ad7eb5157ff07b865ee2525f27263101adcfd8e7c9369304c8c2bfb4de777bff0387e31361c5e28a65ebd86ab835e89b22c67afab7c76e7be5fa8cc787e40348607c3e0b3cc394e1453d098fb14c9e8973f5501d72077cb3aa1f82178b461a087430fc62f06163ff6411eabd8d7dd19f4c6ba6c274b1b1b016edad5c87f6fd16ed7267d70777ef19b19d8d40ad560d5e1739eab8cae4792a7b5d89812ad88e7c475a8459d69262be10ec1f91c3af1d67fe77a6fbf7f0d4ebe6ef0bd166de7dee03761812e2b1858fb944d7cd5a22edb8fa5ff83fa1be9c5ba8b8e549cec2505023d195758713388d22c7035dcd4354d7aa33767aebb2a2c587b7f42f70e337791afd9547af8fc8c4bbb3f80d2f36a96502ed3e965fdcb566fea2c8a2253690b9f5a3801996887af6e44c10340469a7e4149407de51ee2e9eab83fc854b5612c13d0140c2e4d01afa309a353d007e0442283e07ad6fbbdf2507d9cf056ab1de56c919c71823a43ad718a1e45221dc2ebc43873db969d036e0950df683d9d3273c08647089cabac97e551e043fc87ee82edca133daf03c00070603a4fa91280d6c0366f347795597334fb68ea882d87ee1f87f4d1a9f638429c3a75637d78262143b10903ac4123bb1e8e36a56473c15d490fa19066d17e9c45aac5c838cf4c8ec5ca20208553c672184ab065802d4c959d3c778ab4c5d1bd93f5fab5fb0e5cd2d12dd21cffa1c1bee77e29327577669c64f005ef8fd06d8c3a44e0f032b233c7fa3fecc6bf2ecaaf87948e82476a97ff2ed5a830af9df585ce74a097dbf04e306ddda7926c2217eea6c8daaa92a33f95799daace88acc0b9aebf0f85cde627d9a58c003821ceaaa52245c59670bf8de4190600af0283af4d86cfe59a8c7f90edddfbd9ae15ea04021f3c0b8bf9259f6cb9a454847708755e370094511e715aea178c5d3ce2fb3afeb3456f891cebce1ea3e768a49afc4a7f6828d8036513cd1bbb5d07a327af24af4c7d8daa816a3c1a9be94ed60f70235f30ae3f3df0e469eddcce81bd2c8473c1f8d3ef02ee93f93367e53ecd005cf440cabf0203c08b06cc135c0e892ea47e571af10615223522013ea483b35137b6c8eb492810c6fc6d2c2dede45c22ffde58a3902cc5be61cd95100c3ac9ccebce100a3ec1c315640abbddae20eecdcb04ffbc9c58b7be1c60a40ddb15cd640b1b048b6cb462bb1c0b055d29a27d1391f7edb4264bcf52c7c9a9d4ee00e8da32a0b6876e82b11f41430552363e3eae4c4435218d95a09deb2f576df0efd71b34410df5a81ab656d6508814cb4702e1b0ede6fd08c03d1f1499ad31e046632a0472d03297d86444c3c6bfda8a4b8bdb748c683edd71551b1e28135637e064f00dd2720206e471813d9908fc5303d37916a54fe4509790419baebc00d33afa6aa037eee719b95f8eafe38f0f1267393fe60c9c8a1ed911a1443522edef65e21a9f4d90068a878b7855c1d50fad3ad9881bfc14ca70c4602352e27fc991ece95b7be797939d6f2e79e94c41bb70c2cc7e7433d907dd7ef882f38ceec0a43e648b072b19efafb84e60be3f5ee478c235385ee3fa59a1d6a324781a1b39fc67c9d1a956163e20c2fb873e057ca0703f6e1dde9acb60bcd2668b9b4a21763feac09557918ffc4f4eea0a4f0d9cc44eed83429381337c8712718eb9a927ce6cc0293bf836cae45d535a45159196d4839700f6cc604f3d2d8c584c0155e30cda8c8c01717fa0c7e5d9984c4c14856f7767a6f730538e669de833de781c1b271fb9a232d15700145b955f363550b57452a63da8dcbe91e46a1216106cbd184e90eee6ff0d15acc2b25ec79a74aa4f71d6b3a5005fedac263db7ec10f5071ab64bca6b59eeab8e7e035c4ee68396e79c28f6cd911798ed0d31fe99f342178078322a49077089b0b03864874150a5c689190c20ff20d2022679d1fa1c6d34f62e26ce115cdf2599e9cee7c1793f4dff71a43a40a56effee4d863233d7a6241d1fa82146042cca02ea20d2d1602c6eae8f158c5a0a60be1360dcfee05a38f682a99749ef3ddbacad74a9d813a1248a4bfca18e199e164b58a4e919862a287aa91a63fc07f7a07e88d0bee25109f496f98955ca29a89738fb5b3c33a96f1d3ad6b2ee027dc4170a339cc9b6e0713b9bd628f098fbbfbcb1a3ddb9436628998e592f72548ca331cc564ef923f4d834cccecf4595071a8c235c1fde16ed8e6823b9824be2b7f030fbe256b0dc35f76eabea2e804fee81c82dc47e262610a7697e2a5a4bce754320d4d44c30859d1f49b848b0403ac81841b9c660718ca144d3a039da6e17c39375be90011f2099cefaa288f46623856d77f6587688b8e38a1a8f5c204ae9c2d88ad622e89ba77061484eb283625d18aee09a88d852db82dec238b5803ba6a9309297e2a37ec457b29273b2be5218752f5f77c2b86e755a05613055ea2625b407018b5d088328b92e49408263f8bc8fe4ff2ac70b06d4006baf4c7afb1f389453574bd72162b12cc8e7f173226d165af8735e024371b730f4674135335e63f0b25d131294b529d9db250a9fb84b16c7373a116b8c16efc1ef63525e89a9063e09d8547781d7a24126d23bf138049d621daed9bf5b70e522760cf5816f23b2031adadd6ed6378beb1d596f3f7b6c509eb57dec36c6f78decc1c7b4c1eec68941ecf57ea3d5881ceb14220d00d948b310348ffdc03557fb6184df31324ff5504c56f22f8c056410f922c32bc1990d1ada45aea152e8a52aa58d591da4c0562c8aee15182408f5172f8f5a35675a6776610ea051930b52b39d65da2d341b02fbcd7e84508cfd7e0dcbb5b4054b936d41c91948b89c00194ac8f23d087ecdb4a508ac1265422edfdaae7bd768cc67fd19fe75b43f70a8bbd913895cd59899e4f020a1c64ef2ac2cbe46064637873425782790c2e5ced53bbe8e83ac5f56f15de66ece8a748f15402bb7d32bd70246106f97f728e36baeba596034846168b81921bf21f90e3ade509bf411c9576281979e61851f85e5afbb1b5f591b0269582223dd51b073ae5106852a4dfac27e953fbb4fb873668aa015113c7613d56bc3524fcf02918f95fbba16661198676028f3a0229a68883a6ac6c8d8c77e878f73fbf84f54c5ec37bfb9f25ff0aa00f76f9a96217d21a23a3d627edeff01e39000f644e6bfffd25fd1cee1f1bdc3de6bfd0010708781f28d2bb777de718c22a6c5e4c6189a11719d2f24a18b55fbd02b3db45270d4488aec686b5f6a7afdc2d132832f6b947cb6250a0e82204d31e3933ea780b8e90b4238a3ceb483b0ffe4760b0abb85931f74f3211c33cf486ad7c7b8cd71d6662479c8487b6b92bcbd9c81b3236b497911f132d8566d0a07de0f8e6cb0ee0c80bbce90dd211bd0439d123506de95391bdf26379c641d8e577281d7719aea805e5a0661576ab69740fdb5b5f578eb5ffa614b586c7bbacd7269afa70ee2abeb92ba1587c5c923aac7b87512589eb92d1bb1404c9d1bd2b4c20da610242f4043a7628c21827249f6ad51ec799c2018f0a6c3c0b501008f14ee6942152f843569b1ed03d866d0945e3b9fb819e4438006513695915a3035e1158dda51a6393f7be1ad08ef88673315be260c55a88e24a89dda96c308afa221e0a10d08d933006272088972d2dda687f27a873311c30a3de6573ae3561e996e4b0b688e29e6f40b32f9481d70fc4074c975633cfc71a966f0eae175e2e74e3026fe4c9bdb1ad0734061de6d7e0c52af17648faff0b7b861f19a8c50e6fee04514f4e4d2095157f5366ab71a00495ac353a5ac6f6b0b0c56baf80080356e16786042c5e72dd5e29da871dec73e6a1f68148c7117a84bf6cd00116143fc865b4d431edd449b5a2963e9748e6d88908f7909318cd48fbaa201dc64b87d518596ec00372cd3638f7fc326b623a1922db1a59d77be8148428281aeb063b60dc8e0a91df3f2a49d2304575a5f6d9c2cb8e7baaecbf6f5961d3117436a829a43620b859a8ac4de1f2e79f72e30d08122fd0ae2e3447e3ce626d4c71f1a7a7c126a4f55ad6fd380ba9e99cca42a759bae5304d26f4cf1b53d88cb112269b547d035d482990125688a3082401a8f1ce8e3d20ebc5517888c4d180d44c5108ab013f55106fe1c8eeb4f4ad70964ae6dcae3b82c73fcbddefe80dd4280ee3818bd4ae4bde751b8a3a0747d921577ad0f7936019f23378bf850a6501e50b19344e7734cbc462e0f492c2d36343c13785988e617429b96166108c12920791a3ef18b7f5e252631831a9bb7664391a998455aa7d742fa6b49fc52c7c759d63e36152ca18c6ab846148dcb351b07606333ad7668a6ed69f1d6acc9a6ecfaa8515845469a426d64dc7483d4d90b1e35e14386036d680bca3632e9403f385c2b463dca9799a3e57cc31fe40b633744bf5f7cb818c5af7a8f27c9a4d88b7daea99ba1db01bba02002d2bec3075796991db30ec1dc2f98929c0ef031b7446206c9454ad111226f19614f391eccad26ca805f039f6d3a4d712528a00408045172bf502b5a5c90488c4f2c6356061f01ef33fc04763c086600e37b65f581bd24a27c9b39a7f0c71b9cea5430e36e091998b19afed0741a66bc5c06089689e8111aefcece76e8a5c3ae27d1913ded3d60f17c989d019146871c37bfe619287d227453baae9b2020f46910b821c276fe3266707a64679050650a22e2133aa7a0f16b34345144c4ae87134dacd0e1ce3abfeab8a55a7f9827b2d0892a369d4051bb0c3cbac10d04f254f0215cd1cb4438293c497cd186130cf701ab42e94ff165ffc43310fcdcbbbe5fc60a25a8546e4274199d30d213708a60f739f2cad37ee467e370a3d517bd96ff405cfe44406081334a00ac618c80a681892d6c77852a57ea8ea3a0184b8891f25277d80e941b4fad9b3959fd37e1b4af560bc46c08c868fad56c0d58baf73e6466d959e7fa4047aac032ef09550d0c748b045fd1799dd0d5a3274c272c7291af54c621c1d35379f8b17b5ee8ccfbbe94e88ce136c3d990b9c0e2e7b2372080532e836a6340f730e95dfe9d74f031fde53835a19943bce7b52baeba50b8a2111fa65be1e97b5b46c3c0f2d16105a191dbb0ee22cd5820c28e8020bdc2cc92b0021f5bbb78e8d5b1045b43c482107b6cdf2d0dbf8f9ba124e761136e84a1ce9b0094d484fa620957fee615abcf8441348e477fcc64bb493316b0984de66e4d773e620a22dc8919621606c78ffe63b4d3db468688a05d2c5060305e203ce78059c49118577d4548c462680702fb76910eaff70e27419cdfc7b1054d1da93677efd4ca53a0becfea6f2d33880c4c117e2d057e322a7608f7ae3323eff09622bbe1430ae4eb25f090470ed5684ad9d119235ebcdb3e3c2724f2acb589d57fd55feb38c51f5ec2343e1305a75e0660c875c17f820675af3a382e2c3e47e5347f4172610855131b863296a1f0b887fea519a57bc76aa6df23d7fa723d3a749679d5f599c76d130c9f97a66f8d83313ee7c0083fdf5caa5fd459a72ccd671c3d54cdc681cc82f6982ae84989e663caa45ae7346adeb36bb7abcc2736b763c7d827a97c2f701992f11b90791307ea793daf7b20d0b57aee07b39270aa754d980a0b600946fda80db8432f6dd1b420ac08242ca0196960d12c2a00719c1ae439b94d43082d2624046a2fcdb36a93826956b827e1c2e6ee8aef4a5c5c2d5442252e5fded5a2fdb1715943b656dc2e84643bbd1d7e85a66d55d6678c1314461a71ec7635e8f94acf22c15f903c2fe3df71203d07a2bcb2b58f76d9b74f5ee302abd2a0cac12ae0abb0ad118db49ee5632a6e3afae38cb07831e5fd6243aba101d532e58c70c5f5c4e6f9a30a7600e75254ffd463fe55f947ca6d2af283cb5f3c4e7cf1afff93c707c4d037723a2be99b4bc39e9940d1247c9f48752772e23344cba813cc76b9ef7dadf50b7f51e76744be74741017857c9357cd9c09910f34ac55caa1b5a768163a6ebe8b289e315c090769dff8c983ec2d66e65a8b3131cc81812d8336c5170671867990015d7d66d12ef26c17c48d40d686fdd8bc8c7f07e87d4dbc7583a7c3fc85ad396adedb961bce0d497c2384fc8432ef0ae40df9b45d5b09e811a1aa5152035b26d4f396c54cfa6661389b37ec2a48711c33dc258a1f53ae0281633a98083bb118a7508d114fd003156cfab92284225f7c9f2c46db167386f730d7ed1e99508baa14c300e337013a6633fea98b4a52602153186d869acc0b44461b8face84c3be8e1856d51b71f38908bb1e57b09aabadc53ea2445fdca47913840bfcaf2d2854a0f6a18adcfdf0167d4ccf450e4a891e3485f6c58837e983a25d310392a5ba70acf99ad481f1f830a3b256ccbaee8b8227a35e8a58b28dc0d1da29a6be70ff16734d580b26606c1bdf3afbc7d2bc21ffa79928e43bbfe1ed7fb481db9d1b1cf91c7d7abda83ff750db30fc7e8bec7cbfb369cb36fa021e943b6b47ca28c9c56765e9f7c61e5d5110d898b05918bc778c726a64ccf68a60a4d446e104a32af659fe3eab134fbb3193f83b3a9fd39ccb32f2e2dea2da23cf9abbdc8f15f224983506056f87469511bff2f01b18f25c9228f152fc2310529a28c5553eb71a7a6801aab79a472f5800c4608be08b59f9e7152e3687309383a2d174abc57ba9e7267c834e657d741e73709fd77f76c0289d568a7adce14331bfad9a0f33a4ca976804efe23db5ced0182e968224ce30c924bcd082e995de57faf2573d4fda0ed66eb3ae0d759865d4b7741dedd3c69981fa17dcf3b8ba773bfbf75cd2445f642225fce3203d24f965ddda233fa41d66710b6f154cd4958f3a32a819881fc2ff1ca757d3386d408db447a20b68774b0820074fb97481611ec3bf3e264b01481fd107025c770081994c532457386e1dde683397b8f28d8307af81e240eacf4ca8a513044dee4ab9ae80e89c23cddde6ad871345553c3cf5a80cb29106d71839d1085a4b710fbdc5bc82c37f033d2be65661c56f33cb905bf0c12b4bd18c8e6f786430357e35daa5693099d67aa69c036416c37519b933c37ffd02a5f63e7090771abcd90f7d9e01bb9a3ec919a7cfa5fe38c82a80a59f5ad1ada62613959a2c1f3a7a0b7dcc2e5a2a32fee6e3a8f58eacd2d840b624199f7d1c640bacbf639708e3c9025d19c9bfc6794b5c0bd33aea9a67d79897e8c0e48f2854e44a3f0f6a9928c3ae400e01afc26acbd75282f76e5024b7af66fbd3097f6081cc020d62773e03438bf0d1dcff01f272f36ce1de013cd8e10b1279a375dcfc95bdb1fc8361cbe102636390cea7808793be4b2a13744110a504731e6dd06f0b740d556ef5891368763ffa4126389fcc256ab8cb9fe69d47493a615559a3cfbb059f7eb8e1d77859acee4b6c48a401b60123d4ef0296a888fb67afacb9cc5da7f09a912beca4a62b023f10b3b64bf9e28138d9c01f87c3757cc8b3cfbaac6358feb11cf8fcea8515b6268c34f672a3e83de3ddef7f2c5b5488546d26108b28e01074f50f180133bf2ab757ce0aaed3793727f2e987fca522f29e8ca35ae335544a7b9cde7b8013cb9992d660560b5e3a603f48907773a405e70f56685b9c36504e30181c096a0386a98a03aec70705033154efa3aace2c4f39474751e3d34b084f692fe2b0069179f0446d012fa9ad591608cd8da48a7d9c3eb06721fdd00e020618ca20c2e9ac66c59777c38088cd9ede70eec1ecf8ceda63d73edc792cd4414360d684bf57241b5d2dc6b81b5ee4956a6313fdbd41ad6d5e8edd070a21e755d6528af5865961676dd9f5940749f36c41fb8fd8e51b83e8af26d88358526b2ca4e1a2e1bdb7f9d361e6b33dc095d18ed43be423d466d004be044083623d22633eec7af79711f9bf9b8b18b47c4924fa9aed721ce2663a7dfa4bb13f9138e28f91c9d05cd88c698a00e34a80d0cbc402a7abd1f00457f2807404e5fea8e51103140ae88a2fa49d76bd4f72d6152104376652bbda6fa30dbab2c25a0e3417f61c985d7c90af74313fd1da67ce86adcee4955ff4fc502d24b0bc33cbce06ce002b7e5b367d9de5ad44f6b676cf27db35901071670afe22d54c9b1233db73836c4e6ac1cac0090b5c3a7f4b51db9de5d1734c631f49c18ded45dc2652548a659f19baa99a63d62f5cd9608b8661cf7fc88ec0ade0d13568d8cc4091e07c7d9d8544b4b521e616ef141187ddcb9f4caaec7be8d90cb66ec8ac2f78a19c48f7d18b55d596b7e6aeec7c3ec1222fad3d5bea16033b6ea9ff30f94e97d661e922ed23bfb9557fb1742bc36dca6b202f9218b2ff870b64c7aff4acd74513fcb8b1d456c917adcc57a88b77c978093edbdf8a5d4cbca0319f731c8b61253a77077ac2d575cc29c65290174ab6bf04860b8168737dfc9102903df24368f1f7b2e178eb3a0867537fbf301dd75b00070e5715cbbbef5f1164c30a0acaf78ea75b958af893f4cf76def228bc6ab77bb89d7977d26dd07a957c35321ebeeeb8e8b784947a4c5c44c85ee3dfbbcf8cd429f2cdc161c5203aab4b572b63f2e40759197311856661a94e1cc9cd9aca266af0489cfcbdd653a6ca84cd1b70175bd6ce932840f5f9dfba39dcca3b15b8bcc9d7a077cf38b4e43c2d0579de4186a7db488367c2afd8cc8732d7b3dde96a334335804c0bba48fd0465286160129e4481274a79aa46b54e93c2e6b557fb2d2e72de5f0f92079de6b46cbe80af70ddc05dbf944d01ef405129b75cfae13b113425d8444715edfb667ef8228415d7cecb2402405401583d9a714aaa3b8afb9b9b1626e82f85bd8d23c1677f9be212935ca19d341b726495f50d178a17db767ee7b105b922361938e6ef2420cf5572ab2cde3a05894170bb10df9a35a2e5a0d9d5a9078f169825924259a9ddd8cf0b7284105c4ac8453aea54056c6ace84413998ee6af9e2d8cdfe06af335164e90275613a357765f1c55c718d037941820d837b766264d093bbfdc7b703e589a9db324df79612fcef727e912acd2fbc12c395e98c7d8b1278d5a3facfb2093ba0a2cda2da673c300c8747da11574389142f859a588005ddb716964efe42cab10f97dd4a04a3260a5a6026825e1129294e92e7f63388d2923e31d8fc74a34a0a42857e41f811d3c74131ab73ceeba63417c3b902d4b962a476d8c8323527f6af03dd7aa2add82d9e5d21d4bd3b63f8d5508cc8ccd55c07a3e10328ba0c51bcbb616b001141ad6ccff3f3a52ae6bd1fd4ca26c6d2c219c7bf9e79d7a2e3c7ba9257c7b5092002ecd22082564eb9dc786681fe513752b2064882ce7dd52c19c32984ee445399c00e09bd31682a5f0d7b8268c65436ba0d4356018998a8dc55b108c85c25e42587866757b4341222cb7008dfdcfb23e80fe0352dff5687b3b98ef8289650de50556b045ccd304befccdb98564b82a096291f8cdf2760984f3a5290a44e3871e8295d4ce30812799d74142d12bd245dcf315f4b211384f454e489e87a80855f5edc06f7ba2ebd1219103f1ae4c0ece8e9e7b86b196566358b05cc4df733a2ca3e5cab2fdc14807f1aee61f8988252b09f975d591c82c0db757b294e16594f050824badd4fdc72bda7cc0988fd65817435a9cfc5ecf0ae843a427087ab7c2fe99ff5d4f65861a8bb8355a059593c638fecbf1e3a826ecba88b6706a738c63869960964139c3d0270f6aeb4a99ebbb0167d39f4efcbc8d84c7b0935899900a22b627eb81ea21dabe8162c59ef159e27d7df1fc13d46305e063c08e38fa1a2f902cf4a1ef9a9283f950a2b9ce2ef5120a77f313e7e5b123c64f0262a11e2327d243d216cc9c58fb1ef7b3994793308c3bd16c10a0d18170e663db168a09583b91ff6e79fed89629b3d856c6602382e5409d99925aa9c119ed429896044003f6012d99094afd538de684beba6b7652791a8a5cf3cc39ca1b042183b72a4339e9ae4c3196da559cc8660297bf7c499e329fb91e7279aa905c9257458f8b6d393ccb510c8b509b941841e1961a04bd5ae7bdad1b7132039f8d7582d268021d3a9ed91be2b17950d7a099dfe963834521e13ec9a009ef037c908b8fd40e854dd14a69eb2352ff34b3fc44cdd4cfdc9b2068c69794998649b60252d9ec53ce26c30323a7be0a1d9d2e8dcc9241d66c4f8baa1878a66b4c3d20318f7cb8119a5a146e839291b1a2dfb1c458b63f01f802ebb396276b7631605d285d631b0245bbb3efd7c7e23feb5c8456d6cee8430299591b9e3b0aafeae5fb0349ee5408ea192cbb605ca9957d1edeb73c077b7f08c421c4156c966017978faa5301bfeb8174e71731e1e08101ae94f23449880823d054c3666ee2df0d44641641d218e3c297868bb7b60d9d0a92abf6f012d403850546b74f4f85c1a0b4216084a12aee858dcb42edffbac874d5913fc4c19c893c06c9afb179fea443106df3a19bee90a03da1acb91ce8bb69102d4fa1c5c32eb3bbc478b459ab40428ea0d9c4381b988e037b5714621a2c733015a3240eb88844075a1807ab8be0318244ee29076f71d9f8a70c88e30f36c992efc24cf97666adf3d19bea51ee0ec446856ea36020bab08b7a543c33ac54794be993443b9eb60dfd9e55a112f64bd2ea993a220a46dca03c0d79634f6c63396940b1e7b052747d1651306971e7ef68338af2c5dddbcb20bab86bba3c2b43348c31284d9b759c7a62251a47203ade31ce59a9ad379f21f9b8279d35140f3b8c1b02a0fb61d1c10f7f5f8cf8fd72dbf29fe5c51026a557d536d2eeb105b53aea8897aa7a6f0670429f948c44e550a0d914072b73a8014b2010a6188e915f83981895bd3f5a0a3493aa1dc054fbd7f9d990dbefee196519f2ca6883d1118b85ebf42c4c50b655a7201d13516671a134d50284bff0ad0bdb09a642f3c149d889eb587d73424b1a32debfac2e237cc88176630518141aa95eff800710bbe87ba9ddfbb24defd95f2b706d96acb173bdb27c42d9b5a0198b2eabbc80641676ba36470a070db368460d3923af702771e00ec74270081b3a0bd4e043147b9479bab7ffc6c287bb0f5995a3a23f49393ee0dc99d4f9c6368b10a22322cc73741c13e90ad22260bb6e01ca9849e002338e225d1603c0f31949bc406f8a9f5594225739d39227158c362c3708b0ae5132b2224e2d300ee728f9917ced6901cac10590fedea57360968d08e0134e50abe98d3a12acfd393e93a526b6d6cdd88673aabfe7bdec8abd88bd6d07db79c2025e19661fbf91cfaf830f763b38f52a7448f1398926105ba8e4df2ec3e8b5089a60e6be3b649181c949eb2f9852670ad6ab8a9b538aa4b69d0ef48b3b10b06dbc8723b837ccb1d522a7ae506a04b72f50cfa0d8029744da6d3633f95df991d2dfe04842c66a06639cd166f741b5d9d5c794fe3761022f13ccda325af6e7a574a012bc766bd3f88ff20248afee366c2787572fd475d9f2a9ece7994603213016e52f93b13429d82d26d13b2bc0b584a4b19a5a664cc0bc5772d40f77cb956666e67125ffa396f8927a77bd2512bfec6ce210515b81ed8f56b4be8b677895b7e98d3ce2eb7959aceeb05926b0b2cf7c47c32c93cdd464445b4983eaab69ced1c1e33e60aebf28cec6e2f957aa635516e881ee6ef78862e828859161a6f2cedba7422d1f6b35d7c0e6be66e29510698b73d202c52dd1e043800fc32ef58e5b08312f240765450dec88fec10d5131b04d83cad60f921ebd85b7379cc879a4f33ff23ccedbc7787005f55e0cf19c39af55315289af8ae296630e85ffb7b3df5583ef8616a39011bf9e3008e02148d589c904ab567674b2c46ab30be054f9ae23201323e58848f789ee48d6d5f335941c3bc54de838887327dbd702d2bf7fa861dc1421bf8e76bbb4adf1207a43a155ed98cfbcee1cd6cebaf351d124c1bc1179d3eadcfde0f22a88c8989aa9db91cb79d54171d796877822c3bc2e1562a2443edb6ab0a2a7f37a14e76b028a6590d4eb80c30ccfb5bc114b4c4153b94e122fe565db7be43655d8b88053aec5eb9e2f65bdb00574971e4b59cfbe40db65204821fc04df783278f6e99e77e3888ea5591bbce12810546d759d278f5c90ff46e33d3f2ca3651533e0122c5e4135590d486ba75d4c6c8013cbeae0786b06c4aac200f828c8a6961c6cd8b538a82717996fe49da94f8233ad294faf9c3b24ac6737ac755b44be07c2fc96f6b688663dde191507238f2bceac46760f726fae51697c06e7ed9b5ce2cdb499388067a40388f59f7d87e37cb9053921ca5a26ac03b1f533367da375c4a86d21d7249a66257a9f6bfe3ddc0c35af0d945d7863fef070007f71c151e1e22bdfd4494841a7f405a87fe6214276c71bac5a6f959e7439650973e4072467a063cd51174a4373ee278e1eedc3b0525b8ba74abbe0a51bd950ee7a0b7f58503537e2931561179fd81f6620d96d05a1ce12a9588bd09bcbd0adba6a3b836432dd3950c29c582df42872e21225f9ac6c24cc0cc81add0518f518b0745207957b9fd8d4f974dbf1367bdfe6a757b33d6a5507c9ae0bd8483e4a5333d164d88fb4bba5d5280b4fed5edaeefcb198ff614ce7a3cb765dfd6a339b1d1a97f9a3e030d8c540eb64322bdfd77ab8493c5b1804716a94e973faa764e88fc59f9557ff259c8cdcebc62c6630904b0baab372ed174ebc92128aa5f1a316defeeb9768d8953f195a8a08e29185c56dd5624f975ba1c500d9e21e82ba46343579b241998baecd1d9d840bd48f094573a6064612bb4cf730c76199051b14fa2d882af6c2bccfc70d068deb1c24dc1fc5ced0c4f967b5b58408d1e0475d7cfec7395c6db77ef08abb88f7bf2aba975dd7f733362a3b61a1dd84d0af2fc069d688b404d460a4661767b7c6e6ba056ab141ffd59e5a39bb96a6adb350989016f8c6d3dbcabffc3958cd035aeedafbb0f1e6976eaf42cbd8110a3dd8b6871779a9b4d39618158b0876567b3306c12a1027ef9a5c4cf74f486c6e4d4f808d4656625e7c05754a54b499ff679b9a9e37d376f67b15f10001fe2faf3fd9f89a8df44ac7bfa64a7901fefc5a4232201eaf8d61ffd33814e7012e51a7bb1847a35849f522a00889dfb618038d4a35bca21f6a1292a921e0e7c6b87b77a73d00585df2a559aa3061a851f40c4d2d9af5e398a792f09bdd8b72dc03b4f648cd2b31ddc8610fda382040e8a8262ef3100d158888fbb2aca235482c1fd05fbf5f15ed8ee0e39bd643f3fd0963e77669a72f86f04157134a56a59404fccb4f288cdb5dd6182523db42524102b46662d011f34240237a017dfa56f857e732c8071e6ed4d9abf67ecee7a78162ad46393da94945bb9e91291b15d63871848a79122f652cd1311b5fb1d6e0d80b47c27e9179e7fc00651a8a4573a71ec348fa120d90f27041cfac1a3c3df5ca7fadd77b0c1f64f6bc62af40f5e47eabe93e4cbe0e25442b0043e3f55b12fbbef210827b03518f8401cf9f7d2090fe9c889bacf0c3b35bdcdda4e22a0c098a72fe06fada48220109f57d30a56a498c6b5060629f5c0859d98a5a6735067cca06c44e9351d531a26362006264bfc8c1bceec9111b157d582abcef55cb9be5241628dd22bbb92931ab09409f9d5e04f90250f9b4f16c648781d4abdc988716255429a068798c64497d20b22d0e380afe29ba2ffe570c9f2509c434964869e4800366b89bbd3950233276fb561e940276338d5f485953b494c32509e14b7b584002c889fc6cc68a92f262498fe9ec3288d2eea7a4288377af78e6f29a8e2f68501a57d0d9cdb246eaf32240cea76f93145b24a11289e0a143346f4f09285e8cb6d64799a83b307f8ff50d83c2de70e4d73ec80fa8bd6f689e53e57a6f6e5dad847d0c0a1168eaa546215fc45ecc8662427a15e44c356809791542719226eb21977f46c680c0bdef68f167b67af47b7d8bc8128e0d5b1a28e854d252f5b20b081af2a86369a710518a754092d1a3330205341c31279a96fe04a60c9abe2b97d9b953978cc7a12f30d2a0226f8898f7807285da2268f78cab075cba6b367af136caf5024d717a5cbd15f99e441c3afeed3c6f92130c2960ba487250a1538e7e1aeff17ae4451a27d51bc6943a6b1f56f41b27a56c8914944ea48cab098496f35ea1b8d44aa81615db4328fd9aafd9488c5fee59d2046401312dd60b3efebfd18f8f156168025c9a1bd64bd79303716913f754b8f8ed156f617c0bb6c28e83998f2414b0a0daee0f4ee3b0ca936616c638f3022ca543508ab108d4bbd342cc4845def43cb89beea0106eb8d748b6cf4c95c017e4c4a3ac25d17df8f525dcf903650e8eddf931554b6de7544e8f751ed159c2e2f5b82ac46240f60867cf508df4a693813f89ca9d0dc54851e27491527c16f83e5249e4b3f22c2433080ef379b5bcfdd5cbf44648dd9559b52a95dbf395aaec5ab17a56f213c2c69c2bed246f161dc1cda431d606573ddd67de005b2c9dd71c9d1a8f2da9bb7d928d76431b95f3e60fffc6c8a52f4c534489db616f207aef8ace4b05e8267287cc63357e603e2c377b98e2470a7f23c7fe7e86e80df2c4768e64ea4952c696606c15cb24cbc32a69866c524166c55d0f81e3e46fca39bd4e5651eb01c90935ab000ffad5d6735e85005b80a4cd24214a5d6018ea4b858a2e745c7552d999ee40dee0df7ef9a01208c0094b1794871dfb90ae984ab469038802991a76687e68800a764ff3123b3f0da7a4fca4540b379c45a779a0aa9f935336a453f87ec801d8b00063867ba8be5487ba668bc881a6a704154756625499248912dd347e642bfc949f7e801b158a3444cd9f06473cdc06faa139a002c3551667f22136dde5ea14fd273509d0ea69e2a14712b6f151357c87d1c558c75586f548c7b871d780a425c14ef869e8816993695663e226573700d9006aa4f4535aa75505f2a144e2ed4134376485125184b1f73a9da58a34b12226c12265a0ee0b4ccf682c1b3779af32bae8be28c10822e4a027948ac60f2b20d974c32afcafe314563a939ad2d1a6bdd1553685e0ea359defa12224e5eac09504edc2f99e3290860633f9e4b63bd8f6e6aadc1ef4cba681aaf996ec242c3519e4a2fd64d78c9c8de488e96853f347dbe9ccd45899765898c7b2e59ad29ce60d2b43c5309fb2a70446e1c0e13ce58bd58ec20a69911d7655e984ae43f120684b0f702d870c6a92c349d949e9b39fde5c9b62978d4eb8a43f6bd41a996b60af63218ae4403cf764210cbef154efdcf6df34debd58f1b66ea750079f0a97b9368e0a360d92df70e09365714acf2114971e912dd5777d1ccbf2d2a83d5c038001846bb18837b74ce525150e30bc0b079a353eb1b27183372b1ac0be01fa3e7cabd34f92318d3fb5d33e6e8936787da645c29c41fb3cc03a235a85dc4bcfd6b4e98073ba2d51f14a70b65f5e62bc007e76d90b3eb086e91b6bde38fdbe70fde2dde1272cc665b4677ff6f5b6da432aa221ae4217aa11be315f9391895f8c17ffacef7630d64537896d79abe4731db3dc2af978b478f43bc7bae44e06b61f9a528035598837062f7807e51108822fce16e1727566768a157565b7369c6151113804459f7495c46648bcebab6d8e187149d98a2e3360a0ecbfc0b607ac48160418bbcc1864f2845f26712844c931945e73fd3c819ddb145163bc851ce598770d4fc1f1f298d2f32d76709df7142bf3f29c5697e7c421b0baa043de67baa9e14043062b53ac425091b395a975447678fd136746f08f592427478487c1e99e52e9c75c2d362938bb478333b6a3a60d518f8f15222cc17690396145d0d6f8cff859aeb94371b12cbc0dbafe8e7be138020143c1444d0211f3aea1f861ef1922d8a15380ba9b46e141255c15e9e4aa08238d9039812855e3ac7bc5e9bb84dbd3016c94430de1a0d53ca5ce8c135db3fffe2a53e44e1b0f29b629bb4f52fea7c0ad3dcb59a72b569a43bfa804e05d07c20b7813f1902fd2b07983cf9a13fa8655fcfd937344779f21d62651b891d95ec9520a409d2eac6d3d22466c8be96b65512de6fa4f6bc0b50b6e6c48394bfbc4362d3334812f328ab4fb6b372949f6a89a23f8b2cf6a1e5447f9e9a8854f462853215981056e5ac11ad9384ee58cc6e3b0c79b72c800288a01ec4a0172092cba8cdf5df0787ebbb0830296ceb513f491f2cee7e01c5744800d66a7436f01106966c5d9ae21142efa1c065c260d18fafb302f3d46fd7a010c6c546318021e1ea4167b89c43689bb7ecd0e6f6cab5fc5fdc489233b6ca24a210eca212d9245d1edb0b80df7ea805d6564a3ba0271283dc611e72cabadc61848cbbedacfc66a08d84716c35080f97988d217223aca933289717ac1cb605a80f7e692225504f8951b3bb4bd4cb2a7f62459bf2b7c976f4044fcce3a76ac1f7d4bdc5bb0b6c5c2976fc84b5e1047cc41ba48093ccd7b6b23b43ab8812d90dad273891877f558444037165d04f67b00027340f337e0ffd68836ef72be029b6b089f8ac86f46f9403b40cc267b7edc5e2ddb7815bb72bc220bb09cf91bd00828a0c3cb79d11b50e9dfed861d15e38b6341957f32f5f19222a18435d9d02bd3eea26b622a74bea261164a8130d977000d9f7b855c4ac05f6d50d43b79336d5bf7ebccbc5fc42f9d5049035f92e095fef308da00045dd39c8ff63005e488b29f6f7683210412896e631445f1d7fced33075cd402215a67d31767fb5c0eb2edf1df30dfdf6b73c85b3ce93cab4ca9be8c67d9214073e821d9ba578f059ea87c4f921e4fb7e69110ecad840ea011e9bef87ad8de6eea178256ba846b0916e1eb12b724e04014598780899cad7fadd7505c3e55d10919c0e0613af22f526e42ffe9b8d149ed9b1b003587d2f46038100cb3f93b4f2eb618df9f49af6e4f322183db85644a1bd13f333c94d7b4a05745cbdd8ca45a613c6f6708a0d7d0ed603b03f98e95f33a923d2909804c77dbce2aad0e329603fb034ae7ecca9e006104d4650b3cd5f81886f219344af5d823712042a726a88b301d5784d40c67f20f8b1b2a8e4c93dfd5496581505204cb7fabc772f393bb4c25117b5f322247d585cad46044c9c3069904cf25f91af792840dc898a0e8c83df7039bb17c433d929a5919a80b974c4b1823d9140094c82cdc17a800ab6c1c85127f9b7b9accab494194f216763c21c1e1297dacf22fc8f2f8154a9acbb2f662d032964aad0c98de9320da00a936efcba3c934657f2903ba410718864d78d13fc9014307a74d05cdd6d77ca055831f94905073b51dcf0b571360728311f78ea3cdd3ac35dcbe453e28906f9dea726dc6181ef81eb513c993ac531138935a3c10856c9418accfb0314997413689337e22407cd0a4a188778d25a5b557e141cfd5cbd13f5d9dd114be4df070bb01dd30377ef16675defaa5b2634d0f9f97987d9a33c5167e3cf8226c9c2f57baa730d3ea8b3be75ff9c7f51929643a770c87150365b7a15bdad94f72a526ead36c52e26fff75dbb967b9eaa1161746e80c62192cec76a7d5538b65f15af7580513134284d11eaebc2c83f319e5475980b605d152f8b5c0a34be43317ef664f4f00841135a93f1336bc9240fe752d4564ab1d83bdb21aa396d925df00975963088900745b080a8f6ac7217839cc4c8bd65e9e862160a12dc36bed1a7e0191399839c92099c85cc943a8474c0e4f0bf8ea04a10b36bb9e65460cd9306dd48258cd62805136a6dd0b5cbb9def29852689e3b7aaccb20cc32d14225f67be7ec94a1b739ea941455cf04b96931dd1d410387b557f9949b18da4ca7531d311c75b5551b744f20181513e250034d67b528ef99023d58a2431a8e0ac195c3b38c6731b279eab0bda61e37c29974c14ef036c25629c674eededc587df5bbd40529f3ef9b98b7f889ff056904943b07b805479bb1c6714dc8fa83c87a16091107cc1f4a3cb187f15c074e071d96b3f5fac79af477aa3b8b1a8691cfce9784a0bd73eb8bbf9d8b881e44fcf9e8a86f1167a97bc54d2a7f7b447398e33be97fd2ece3f8ced7c835f2aa344dd2a74f6ec88b2b6e177875c74562ab6567182bfa4abeac052b6afd7ab7bdd2fc8d364a87afb6efefd2d7eb26227de664942bb6b6f0191c4df8ed93d3e5d5c1932b08f6234e3a62e1ed49ff07ab498e3417b7920e17eaddf159a7417e6ff63cb6f39eff3d4ff5ca89a21120611c5677c2c0efa2b78ed6b35a444797dcaae8539c45a2c83c843dcfc35f833f10091418bced14e3eb7fe4c7d2baec7ade52b2ea1ed93f351bfe02d4389f51ffede91f5c33f66eb2693d0e98bb59581da7cbe951747105682d232e78f46f578303436c2c6c078afcab31957af13d60c4c31155cc97894273366230e0bedf8963a423d48313e250b80270d828ffa5cc9b50a708a4be5bc2f0318b80d5fb5de2d8425d5de2dd5b90eb5127a8c2c9956388391387954b1047aa056b6c94b13bffced9075ab059ee96ab4614bdf341e80abd4efc7d122f28787ca53ccec0011deaaa704ccd2c4215347cc34da525b4f937188dbdd137df599754d246648e0bc791d0cb215ed9e88f70246779b7db2985270a09812a0c40d4fb92522b8b2155ef3f5fd4678bbd58767fb7d7a5a7b39bbbc7be735d04f61887bdda5492d3a9d645ad176b47cea41a26b120bf9e2c9f79c5d0faa7a164d383852d3fb7ae5171d8eb4f77898f1c82d1a244a3e2aedf7932d4588f267d9b10b85fa06dcc4473a8ddf1614b88445a2aca1cf7e9e9706bd5aeb9b000a0434fc835404421a97a938dd017a82c1826410b590f457b284bc1fe4de550c692b20a31223dcaf1447f70528630b14a19b85d4fb48458f75ca34b478435fb83e13edd79c1eec1de17313c867bf4e00b61b56f633540552c6f0b9ed45230eeccc4921719c470768c45e48c209bc6fb6bd4c38afad91fdbe308fdb24a5df4b28d739902a4a7094286fc968438657e99b1d7b621f0c7affddaf1c1938f18db5ab59907114e60c06a24b861be315bd0d15f5292d0d9ef43215a895f00bdb1caef75b614839221d5445966ac0d030d9a4233b4156d8f0e83aa7860ac7967e167b21bbd7586e4965ac9e6a4e650b0d9aedb944d9862afffc270b5c6792c84761ff155047291402ebfb7d26b906b65c371e07a4a065cca6f8bb7f1d4183244c0c606bdc9ce633cded1cceb6347b3670433a942f2c18a877c536f775072677e25ba9cb2dfe0642d7cf8a9db7d628a69e15d4c15a28e034c8a3f734046b3d09c57e364b0ec4b2ad7bc36dda91f0ae51b0545a1a8efc67d5c13936358bfa6971c14239db16d21aef2944785371bf53c47669fa39a30ab98865b0b007c6eb1095dbedc267a820ba9e56cc3b9ef24f7a440ea970e0899b57ea5258c43b2b4dc02ae620ff65f0c48cf0c044bd330fe86e80089976599c3d91aca0a6a08f5f33df86c8e10abd7ecc1850b3e7fb7946f2dc763d6fb362db58a970cb7dd82e9aa6b81f0fc2f873a204bd35b6d1fb8bd43cb79cce8c6590e1c19e14154b2becb426cb685a6b0b98ef9e56167374979120fc4493a4275863c52bc41033175cf4e94aaa6944722114712cf0f6a571e31cd8144866aa2602879659a307b675f0e5793877b32b6d6db7a2db3058b1471cd270fd99ac88cfc09996f18dda4f9bd73d08cc72d39d8e54bbef4509a2f758cff232611f0474e336a6c42ef9779de2adf6c8361048a39cff98ac3eb37ffddfff2e68d8f1387678d839be716c4af8abc66012702f504edd87b227dfcd4dad71c1edc236beb3308d4a237c990697e194a0aed0f8ec71c140e90540bada5e1c19629d76126b2c858ab6370914598e68c71b026865df9d6043d2c591a2fafa927212b69666cf539a47b3906747d9feac5fc6e91621454b08c0fdb43ed0c0899268e16597986216fc83c79e1b266cad42b32d324a0fbfcd501ec044b6fd181f3e7142969996d0b7f93e6149955e0b9309fbc3f0a4d70a1ae6389c6688453b4c4f2a85e85bd0130ffc169b8583d694d565e58330d125b22d0d961c8638919991084f6d9e96fad1cd844977596160083fe7814ea6d4456389982b24a0ce6a39e5df2c53d1f7162c93d8335bad25b36898ead329848e9b6c5325d4a8583bb3ff00495d4f138f7733816b208edbcabf7a5543fa1764a27995129494895d02de29a10f12db1a7c950de8d31c2a86149c1e0396430a1658ca220a6755333f9178b21af295fcc8f5474e081d83e47975d77374b325f68d8e276672783ef168238e7683c8222e1aa601108ac7edf415980faa1e9f9820dba28d4403e2d78767564a3231241206ca6d12496f30314253b19a1a477e314a16dc8c0223b60ed823606d2a0638b19178a8ae690bdae60c403c76e46d8c97b1e010a485c9f062f0cc4b88deaeacde714e84306296eb837140a8785f9dc6acbb879de001f2517801d7e0ece131774fbb692932c2c92be824928adf23c24a68e7240bad554aa12a44fd5d4efed2d96b4766b4902bb57ec0afe0fab1a2ee770b191efe87395fee284ed376ca7aab68ab2370a6b52e49e10614dea151465a99fd761f89f31392da2d55ee5675918b0c1b0493a83970e6238965149d3bf6d3c16bc093863db6e293f31678f18073938584531608ed6e5a1b11c3b00228af7def4593494e9b00bdb3390c955554a6874267852b4b3d5eb5547fea55f2f77f868bae4ff4b505913147da4f4aa125540f128f9cc9b2e557583e0b1511cd3d8d1a96ad4b6c5d90629780f618e6503356159ea1121702eacddf6094079bef8e1c792cf95fa2822cc46909bc4e6c2c70d4c0b1075e4d9aeaca18d2422c1bc5033c84a2f2911dd956b7298c26f1f17f41b0ec0ffdfa1b8dbde474d647b59ea60903f00c33b44a6d190f388bea577c344c753b47766327b9f7bcdb80bbdfd5b309ec5ae66917998478a3c116f60c389e00b380f9b6a80ba3ab5982be8ccef39e30c396517050890d877c72f816e541d00a7d7c070bdda59408ffd23875ec2a0ab036e8bb7090b7c7e08e83d32924d33d4ead57010e4024df5fe4b02961ca44117c246cb442071c95f664ce7692e4be280d625adc9d3ff73b846a6bd0c8f73ae0a417ee7204433b5f92bda4c93902e46d6349945a86ce54c2b3b96f3ece4d505d971c0d73bae610f82c6f3d7d61a3fb468b8386299edf0f5c7ff3bd3766bcbbc8ab09195a66ec34c41c251ebe3460aa3c1eda404d33d5e910ca065b363b8e5de961ba3ba00d74dd0d5943f23b1c55d2a45e657e9c63833c2ac3e5e80591770503957c9e2ecf8dea6bd3d2c56515a273ec8994cfea9b2a9cc8c27b764d3d230c9af37f03d5ab0a57fec6cf4913418d66a8a92629d34183be83f700749106a22c193416030072c9cd52656c8a15dce2bd12ad638d1d9148861aa9df8aa1b57996af1bd945e52379a333d1702b3ee9bd5e73e993ea99ca998aef496b4b1d2297d329490ad0773e89f5ab38c9f31ebd7d136ddc87f4183374e182d6dc633f9f28de847ba364213c905f323dc221ee8f060e99fb33be1d767afdaf25b084b012850a0c6117e2ad7dc803806a5d2a983bbd3b2a23c0199cca25af2b66dd9f4780f617aed3e8fd8f1b9d73ac8f59415db5e97f14c204f710ca44e42139bd39eb3971e13e9de3f48772220b2f1cc517abe155643f8ddce3bf437dbd5184655cd8f737047e88301ce1587d069dd7f0521e476f29a9856def84c282c3f87103e0a51b9dc997cda9e24890a496869e1c444626470ddef2dadc6a5a2bada4ad2b7d2cfaa7d539c6c738ccf82df2b1627c28e9d729e49ab071459e1fdc2cff09cf47bb8a77c2d7375ba2ca98aac738f18a55b372b122eb19dc42d29aa356fe18e0ece19ad052083ead5f1a17e996a40ecad1a68e683fc9b419b27d8c00439a1448d8482e1029e6302b525070b7b67b4e7aaf3cc6b9ce703c04671c52c98689b35ffeba2aefbb6d6279cd0b6dcc88b9180d2a8e0a663e451ce733850a38dbb278ba4fe9482b2508d9eb164fd37ef25964e9ea7fbc8446b6c4346d51eb234b08cc5fb4e57d49f737b9b6119e051c1b0ba3d6c19c40ce64b68ac04fc9f8a97ef972ebaa0ccf017399af2e0815d518b8ecb4d19ed7db30e99dd62064b136f2d96e66edfc55fa20f1cb3cb70a12868ee1e5c17722654c15e74da7641728005b2a138e9f4b930668ab5163d05081f18290e766d24a4e34609d8b235a4d420dd1b54bb58d90e3b8f4682250b8f4f66c1ddd8dad6cd992f6bf1854a8bfc3a72207c95dcdaccbd2a971d28ff0cf690387235f7203a1ed4febc7e8a69e4e73b58bb264b7c010139df84c80f6c8b63dd277d0ecbcfd8ff93d66d66583d29f778b81aed1c11092868a1bc72e4634889b65040830362cd2773fd2efc38bc9457c8c75bbd110a079130f38d0057fba390e750dbdcaa18ab8fcc679664cfb0345eeda3b2f90f2655e8592b95426e1a91c2a00b1e91e1478d618c3a75343489e00789b311aafd4989291a0f95b05b6c642e42eceb6be82e3df95e1b1725db9c3dc5d4206686cf390f1875bcca454b5118277196222cdf04c5d8cb1c8a23f35844aa329ac10753d02dda2ec5f2b0b6283c4c91125f2b64c9ccc000ed29f756128551caf52368aeca6465afa203550640fad21c79714a3d0704a13e2bec7a89efb63c57664343a3192d43caa2f04e49ee2022e85bce5bd810358fbf90487e8d4011227c199a9c483066e4fb317dd8af46c6187e55b70762e63bdae226802d20f3ba3f6055e197465aefa6537acd2c0dc78c0aebbdb2d4e8b34ff00070e12ffca9748dfc6764482b1f0937080e4111de8b9a400e03590c3ba718e1fb7568d627b16bb52603751f80af05a79024ffeadcb24879fa013d7210ec6b5985e63ece346c5e7ce9713fc40bba189d7cff44e5e228a01952785593b31469b3269f483e48a30f1d0ef2ca75aa2fc2b4ccf56fbc90df8ea4cde65494e49aa01748521a6763926ac71b1ec8c26f66b06fbda6e8b43e43bb8368588bc61ed09c5efa72b309ba37076c0a0a4e1b04ae2fa4c2140eebbf19953639284b67cfe6ee3e0ef1c82f154431071650620dfef846d2bad030e6624b8392fb2e55cb192c43d2f0e373f7c694d1416a3350ad63f58858e5935823efb44ec484c5db24c452080a90cb24c7bc9ec20851b4c1ab90edee04e973809be5f748cef2f0758cf95ba89a71ba13d27f13f2f1db1885a56447fb79e260c56b8131905e9cc36ca676df6b4f7ed31b0f3f7413da720b83161994d5137459b0a6d6f2193ef3717cd14bd349f8c9c873fd594e7c03543139071053086089904502e0e9a41eb1f7f5daea68212ea0d6e9e42e04c0542ec969c7631ba49f3e443aa5c7ea96c40c60bc2b175abbed4e0ceca35945210f22d773b15009d9f5a616dd92946eda518b4ea9094c22157bbe71d4b0371c88e8e9dc0a79fcd14e4ca484f1eb7521c0aee2053d83f0b47864252ee1e983dc92bf798490fb7f2149d5733ce3db3fea3c905ceda983833cc07f319e20eadc69351407965adeb381ba45d80d414d21fdef32d062233d4b1cea905ce29676e99cc79dcc002e70d380e60f388d51acad0c5f778107c23e500d47371c9ca5e596ecd715ec4baf0d630a23a4153af80891a4e3b4cea2342969a125457135710796961fbec09a93075171ca2a98ce2391f02d5c4bbecb530930aa5671ff732d6d330d42eb82dba70d7bd5799b888026d53c5ab0a5d63c97ebe2ee2ac6a7bf3d85c8bb84d481306364d7401f76ed5e040500b462780616bfeb441ede9055bab0e410d2f676e68d346a4e5297b8d3ed0d239ab155728d5e67fe5d722154bc1cdc60f8597d7799987c0b1021a4c82c9f826b7a7e0586079f36d3c184be6796956aeeeafc66a0e99fedf0b6fb5de52c42de6fdc1e6d5c013a43ed71be613ff6fcfbb2164dd16d0c0765cfa9319d7fd0382792d3eaa2f2fe778e949d62a8a1c9aecdfdee82baaffa125b5522a0ed833d06b20aaed71f446628125747db601d03c7314140f8aa2005e64314f522ac6d7c82e36f2a6265467827e2935ed017e610229c13b575f42b2866658a6c36374b975135a37e9fbe7db4404ebf810ef7066f7c62ac1cd8c8ff3c15805370bf3510f2fbc87b34b4c557b11db1804422f332fccbecf0c39fe8571a2be6f35692121d960c8d8bd8b76e2b2430c9990b4f818fdd60b00337ca18d4138dabc61a40d575ef55510215882ab47bcaa44d5a993a16da446173d044a06e9511c83c2dcb63fe08db0127759b4cdb011cf1cc5d68f902cad88424948ab067009ac9bc3ebb62151e3b9a5ef92c99243f1d795d8407f7de18354da2b8c9b7349cda0487e7118927ff53b2dcc54a776222d502092cf93c12ca50910cae16bc8d57bd42b489d87865961a7f769bd325c4a068c36be9505e65500e453d827ec9b6effe16901479d78b74a816df89abc9ca93ce477c3af117fb5975bb11deaf0fec06cef16b86b68ca3f13f7a60b1e4929178d35f5e645def10791c560c8f064e3b8c3bc01ca74bd809ea5b74dc685b42cd0bc8b3994778cd912511cd0850069afa8d23162dc8609c82743ffcec06969408df0452437deb35614db518556999ffdaa311586cf7173c55ab77db58570168c273b829072819ba2c2ae355484aa6fbcdf5a553bcaaf183d194fd8c6ce51df58e0e7746272561ab3478b01145c5dd6a3746551e69a5cb75361dbd184a6fe3b65f92bbba6988cacf130407e37c99c6d57fc276eff14c40b3045862e63e63b5b9fe81f583f58d03d02ea1ba19c849d429aa79787a56ce6e7a86322535aff634a644d359889f0475f8e6b79800471304cbab32f0f3ebb733b325f5edb8da95df78ef6ceed92377486a88ec81b0c37257ca3cd9fc769f23088c3ba4cd3605f77ba308e97f7fa6766dd4603c7738c356e1d7b36f56d41f3c25a083e72dfd969a852fc190891a74214349fb69562ffb68b36aa231915512658c3ac9f2a2c1377712b50cbcbfe52403268c1f401a565a0641c7bb1ced2b6fdc09e4cc722ad83a687eb995598f8af59720d3fa01bc85b4833563f096a0caa683741f4cbfde885dd89fb7b20aa2d7024987bd188e5416825301d86123bc0ef39135c7a3f0e28dad8de98f4e02f0240554d25b5ca6bd76bb6225fa5a08e49244dc9988be5d1965fbef9e94495a6747298d7f33b9b8a84e2a4c9ef6af4b802acac18526987619e09ce86e900b64e83d96ddc3ff6cd375ffdc58c9f346cfe26d2f7b48563c37796df6593b274ffa3dc35a4dba545f20ecd6b2e4874bc55bcda23ef12f39c2d1e7ae8015193df44460618157465b60f6c1c2c0fa5046920504bdc10ed654508c4bee9e00bbd325f6d5deed02c32a53f3296e5654372cee007721f13b916e43657b06bfa66ea1eec0e2afdbe0707a064bb731f5e5802ce3900d8c0603d1eb5ca11bebcafa0bdfd3ce04ddae776707f916b27e8224557f806e17745ae9df26b413d83013ca511e96027fec11412de148d4b673ceb0c5df0d8812c13890d2e3cb10ebd460f9c620e1a354d58a36fa9dd6218ed939b02cb23cc04b087a7d4aa6d601d36f2e6e1a03e5f497c969e1744af136b51761eb3423f91c20b35b9f02bfe6d0fc2af37f68aa7a494d86118295b40236dddfa892dae337cdd2bf1cb1e085deee0d256d723d979fc74fbb7bfd8eb2a2eb4364ce95c63f5b742fb20ff7992cef6f2a22defb1f747959718a0269faea870ab64408df838c4c3edc618e18af691445f64fc382f84bee2f8b46b6285669de8a5178fcbeae44cde5affd52197f54c8ccfcc8468a53f1d66024a870b6c71664584f600e83029839cb8284bb774019a173665ed29f11cebaa0b387c074d6803c6e52bf083a5d2a0715f7f2d370baff7b35de4f753582daf8c247962b1c85ffad6c891ad7d2d0e22daea491500a4cd5a0cb525ac656598dc4c18f8e27eb90f719f8c3f6e7844d2a477767f7b869ff1045f0e6e938810d02a1bc4103d7d8cc204e075e65c47881b10398a570b50961b4cfdac2769c6ac3d388ed4cfacfe9587e5c4e209549684760b9f7d78afd8fcf98ec27b63885ffd7bd74a329919a57a104109562df538d05bdc067cb4ba46ea178ecef47babf30677fbf34d273537726702d37a0f97fae6d17443908af94a4ea457002e89d9d1888a254bba1eb5b364f4c80da30050484b15bb7935a06056143f32895225ac77119d25b735c1cf6d71a71f101a0e0856443170c834b344c18ddf5323e305d9db55f7ec5939c711334dce1d64f44787aac03a04cdbb0156729b40b64ee7e3e570a709b175cc5b8a7078bdf6257a4d434fc332df57ae191e7239899fd77cd598ec0fdf31460d4b49885a68c97a1933aede95cca5614d5e34c4cbd26c372d82f883ae3cea8a31789941f696edfbd6c7525db9858e0f82de4d019ab06c2c7d89a52db13867b6723966029b6dccc12ecdba90843093845bfd5b4a5d859788fd1d7ce6e28cbe9a5b40a8c3d508cccf6361512f47878637af8a7810fe61f306dbcfbf6bb88ea8d7f16d2ed48133c8600660207f4525fa236fd107983127afc36dc24bec5649912fe65fec13941415b07ec3ad859b05a695e7b2791caef5e49d0f508bcfe4783cc251d8e1af023c236c10456058439025a342962b3ea7e763610cdfc6b3a7c96a2b945cf1ff2cd2c835485bff97672439f2a9fcedfced9f106c32b09b59bc60703f6470087f62dac55e77ef457b53cf326127491e571f026c2f8cde2d3604c52e2a45832eb7647a5e2039d0d1f509834ec6e9d60e26ec5f37a2f7053fa29f1de51e47d130e5169de4fd461cfb4b76b8f8595c36b7bfaa07a0896a5b6f466a79f6c5b4974750c7762a8c2213ec2176ecf59ab0cc2effa63b113caf753d405f610b415435aaa3da48d75b2da2bc2d1dc3dc3c7e9a63e3cd6375da722ac5a73c4dbb05ae8fb602e85c67ae8d393b3ef70b5f8eda7eb93ae4aa0f824d540de5e91a2fbc4d25bd07d721e5e629084f71a04f1d67416b7d7185c53fe39dd436ceef9d01241b4cca952c3cb46bc2e88744d153f34c0b591fece10640a87211a2785892114c0f1daa26e722b7ee39cdcfc1c147f76efa76a07a855f3c3f0955ff9cd65fce8e24e9c973db5887632ae7afec037a50e17b02a799f2bff9685c3f5ffeee702e99c4dfc984bb58f51d1658103acee5873bf82cc3bd516e2e85dfd7d3d755988223f79699b9eb3125c3fa95ff5fed1500ed2b8b0cad571dde47fd5feac1b9f776d5c9414ed8d26ec04b2651e1e1f0b10e48119f9d59ce38dac16b6b3a80fed6fee2cc7e474400abc0e5875da4d4c40277101ff9cb3d6564ca8f6f6b5d4dbc93eb17f586283a6c2c33f036bbeae61e8008c62e7fadd92d151a50697c1f23c697d7a8ef38a1809ce5a69f39ba69f017272e7d34e2ad7b7620fa1baaa9e764079928bfcecd5c4af617d4ce317d562ea232a23b1b19d479d083cd57e87f55053c4120317557b4ccda8e3b19f815b119226dfd7865bd680644aaf2cf33a47da526a0e81c78c20910fcb669efe452401c53b57356afd1229afccbb99c9331148439c93c643d72b861838efc8adf50006e88cc8be415b64739d55786d83f641c97b685426640deb180c454e1372228c6fc6167e80e01777b975a22e3c48356b74ccbb399994a6f8e4fc32bd91d26d8f0e78c91d4c853c6b6ff35c18ed93a3b40b71789d0b045299b29f37828daf43d3d1ae705526f0c3d3b42083e847471f66de3cb018c3f50489aed65ea2dda7f8e2511eb6aa12388fe80b68d897db7277644888d8f7e84e39ff78e03bed918b727ad35269335e68555d822eab7c7b38639a4139645f8205fe88349326c8faf2e7737b74c5d60de2aebffbccbcbacbb58df7a5a74cdcbb5ef90aaf1eaafd862cc67f42dab73855011c043c78873f3af0216ce1b2b8032b5c23b0a43e3160f9f0e6e3e8cdc07457c6fa3628dd72b5a150cf3fead105bbe9b841bd72c9d801227a29e7fcb7288f12289c730bc2a1ee4a6878e56c9f4bdf2516239026b88d4a5ab0817411aed08b12dfadbc76e53ddbea7b615b9d87a4cfba4f9ce62f594aff1a30721dc374c2299f390fd4e3e9108bdc4281f85f068b8a832fa9bd64e712544b2315b42d81c3e335f8e5759ca5865a1b45ea466a2a7efa82d7f4d421960fa6c53b11215839e20d65ac47468cfb7fb1f333b25292d54f7842290e8ab22c8363d3212f0b0a165237294ab91c6f58bfc88300d1b0baecea2cf5babc3b6ef0f973a234ec80a7a2f5b559b7d91c99f82e45252e7a7b63f69cb0d278269bc523e16ef2d7dec10883f6a2220b1c849dc731d13abb920d59fa4bfdbb18d8a9abe71e2a31f3ee7175388785d1e44a69fee27a820e5e7785031a05333878183a4168480256406890125fed87ca77a1fab4ef6583b4cbecf1e7b2ff2b604e3860bb679a11e2ddec7f993abf2905e2ffd5ecd9b5711f0ee042f22240af2101b5e29e62985b1a2ac0c51c7cef44b62476410e3976b326db1fae13c4ab4b5f5fa6bc1e20d14d2c83943386dfc4040db1bec83fdf3e7ba7e1840ba74738355ef57a8aaa2712822247c9e3aff77bf59425458445e4a0a92d6992a7d0c22b97564f83ee6ca33931389186f759faa395b644086654bb9affa5301604f708e5f0af2441b269131eaa518572a2272d148d9ad32f56b5ace8dc96093c4def4d2a91c33998e0b18e09cdb1c2a228946655f69277224fbffe10881dc344261664ab005d0224308ad77b84c5e9e7ee7b71cc40cabc7a201086572bdbdb6d7a5b60fd90e4daf25778c7da75408336e93e00dd5f206a2e2de0c24284fc1b0a5ffebb36b766c8298ce231601677bd04476a86efe144fe6e580f904020ac9d228061f22136a093a343c3cd442a54428f0e345849345af104f6c0a83cb44f1bf76dda9ec758a4623a120612097c74aead060778dd39629b6c4d772116b48bbc5c7ed4986eec15465201aff25ec63332beaefbf9235e2162e9cccd5c9ad9a08e58c96d4075488d5e383086d58f1c2dc002ca94e09339fb901c3fd6cb747486cb37fe1f20041f9c6b2ee6cc746d28590a2bcd01fe04a3a801f081d6d00769255e584d7af5b1a043e4d700b7560d405557c3af67f1838427c01648847cb29d05c1693d5ea04f4ec3bffcd8fa938cd6de857a49193dea3a1050ecf5a0bb94213abce7934f31bf644900e9465cf865868edffa4ef92bdba5a16dfd48a997cc6c4b2b6c4d616b4d074c74e166dec166c9a4af99ed5692aa419485cd3d7953e18698e139ec7307b74d8b84c9f94d86fac73a5dcf407a2b6456f4a7a111001d3a48b06a881e15f171e6f7eaf519c813a9264a86dc2a7d7d45c702101366752bf26ac066cd33a337c65c76c93f4e1ad7fc9bb0f0ca43a83ed7cf35fe9ad932d7bbfd79340c3fd8d61f84b8ad4c2cb605086ed891c5fc3da0381183866a179fc955b0cf88f1460e620b492de69d53a151c8d44e35c3d65bdc527f1855f13b07d45bc49a48362a2d64512432c362492671ab74b91337fa6590b739483e3a3484a2c8eeffca75b0461628471c25da07ccd4db3909b85e5a20df09d85ac01fb545625dc9cbdf78806796b5a38e21b812851dbbecd92e3b3594c4bd8f39e59085cc642e6b5a1b2356ae15f4a00ff9cba9e7c3ec742e760e0e4520d9c3c014b677cf5aac74c1c9500b94b02a98a106d0e8357ecc2f2ab363ae0d8c270c9a543bb257f33469b02384bf1a4843890ab329943bb10c43aae35e5d358f97d5a2a8f2ab8c36b86dba276f9fa4e607b04afdcc308850fff9c3eb46bde66026c08c89dedc7b6d96e91bf5fb3eb550182b39d5c89ac7554bb8fda0037d29f89f9392a2fb35c35b50efac2064f9f540fa8b78307c3d7db0a0d292c602f23f873220bbc5a34c2d3b6dad603b50b0934c05a6db27e46c0f85ce7493d98bb7ac56b24c54e94675d5517a5c73616e178eb40d5833d1f021fa8baa4e86160f1bdc8ce0d0ca102dc2e36008b27493dc0436c97d6e2976212f229713a2d33ccbd13921ce0decc6adc61bd398c989b4b37208cca4d57c9e524bb6976d7042811595439f4d58a85d98a36dad3f4112aa44b13601d13e0e97de50d4922c7d6f3508147c93e620cccd4225c750a539c3e482707112e5f8a79a89980a3ac6f993f7926f7ea959970a7c94d8a3b11e52658019ca09c0e7d8a0aadc2f322cff6707a65bae011430323053e7d3807a01f0cf0a1dfe6b2641a0d3e2e63111e29805f95cc3475ef3115dde93d3b0933b2de91a73d8df686730cebc7942549476e81a42bbad9c89a9e21a23c975f09d64bbf2f92fa5db19f2945f723c5e6d825ddbbd6ad9b96dfd7d510f3092755dfc7b759d08f8c37aeb1ab8bd445af960d8296961397e063418a41c36275670e65e10332b7a43088218bd836d5d3b702575a052f31d340034f64a2941f261e537a311c2c56ef4fd539915c0068d7f1ad0145acb88af7daf1c6ff510b76a405644c84babd7931bf1c09855705f2985beab880d6a30bc9e29317e3321726d4e10220cc70747369c4cf71a2fb6fe4c4c8efae276959a4ab33cb909a1ab307e6639cc65641a55badf6eb734199213c440289a37a1d936d0ec9c870e7514c6eb1cc3130ac659382816f6dbfdcda1bacb3b0799a61feae3be4ee21dded12048e4d5766a3ad9237d2aa62cc636a761a28868d1be495206466d1a99266bf9ba226ece9e8484b8c5676f1e1cc2986284aebb60c94b19c531931e2d0c23f09a471755ea49d3d53c4e3e4125db8cfdd7a8a09ee1ef9a7b61db2fe122cdcdfa9b750dedc7c2f329b574ff42d0934dd58db76f6796fe59b7312545fad99b929f4e29f4adf3f5c68827b446a87e28398789b9c98125baecbfbfac1b74a0103bae63d9ada8bd72c8291c22432db0935301e7cc2cecde1ca47b27e80f8677238210fc86a624c34042b68e1589c71df730c42a3bdfe2ac27d7ea7d15ab1249d07c6829bd2a9bfe710f3df6a8621675d990677b2f3381fe1bb132cf352a36b557b608bbb0bc13adaa04eb7c196393fc09f5339afbdc647e8b6de66672fcf9a9983191de3ba96133dc3bc2cdfe9c73e3e8c8cf133f7f46e7c3f2adb80197dc272ec44f95176580b6eabf159b6cf45776244f3e96c8c64825a6eadbd29e3851ee3ae106dfb0ea304b973cd4a136ccbd70dec7a5a6234fba70b1ed1b5a676b78fa5e187d8ffdcdbf9ef083fbf5f0d1e07028fc955efee2d7d1fb0f94716cada0a610c98e68ccf903a4ef5ac467173e8e0771d39e1a2267898171d47365f2c2000e6d93930dcade2c56d3cbfc620cdc2fa9fd29b960c2aca0295fb176b1618cc8352a9b6f160f80d29dc29a3c1a5138d39c97c36c9f0b59a1a5769ca450cbc24067dd65000898b6ff3dc41b19bb9a17548f2c9cfe2b30e4c8b2a1eda017e22f884e5697aa8d89640f0ce10dabb4132c001baf860f070dd2581e1b286c89fa8eb84563483ce08372c6cf1310218027c28e2272e927f46be9c444dbf78e27846f97bece201152380c2bf774aaf6d50f68721f1044f3e7837d864233c4db90b40163171986b574b2b258070b6e8b1284763613d792c257a12f5ccba391b6e26d06e7c5ca7009ec4837cc0f98192a9875cde1ac1d229e59a765260e8fa4807629311d01c4fb0316ba857fe05f70ebf8e0f260177d9168c8c03ba732f4214178f0f8df92a3f0fa73962e6ed2525dcb49768f51dbb442768824163d6fc7ffb059a75f8b8ca49eaab4a2aa3e03bf66f4ffd91307d75c6f1b09b53b29f2f2773c4641e4323296414a83d773913d15f593cc07c0c1bc4abe486aec3a48c1de23164f2e229ecc48a24cf20581ec64dccef38491511f79c852ac2d5457bf003e89662a48cb5dd23871aefe21f155fc9ed8bf6fffb6c9e613f7289c68b3dca1b0ba294ff6b0b1aebfbb05fdd8a65424cbcab8e11736776ca3b3aabd27044f949b970509b732cdfd4c9419e22f18c266172026148d51f6cba006608e035fa1b5cd74ad4666e463c87ffd0d58c173d02c2c9da43bd751415925df6cc2ba6f68d1894cd38e44d079e7f596d3a42d135b5f700188dbefb8e25dd83441e75d385006d239978968ee3c0d183bf9a60efee98fb78d1a50347d084674944f78781216ce77ea4f52a0cf0aa6c264507f34cfc54517b4e07a13197e3c6438a96d76571c93f0e040591e4d1ffed0f1c2807d2138269246da0cfde522f38edf7b370e03fe1885db5d3539aeb0444a6176f4f24f9e4c482aae8fc4546870b12f1b9e418abb48c41e71528a4474f881496cfa32d968522ac9acfbf35d9104338a2b2ac0387d6f44677dcd5d3453f318d89ae0abf4c88a724eb73a803aad84b3dbb4a355ddd1eca51e3968df81ac779318bfa61585f3c38c30c026a08e033c06955f1e4b743b38466b6b7200f8c9252de85ca491888fb5d8195e7d11d8596e4b583a4e63b576443f446b9f46d8f5bb4949c756164730496240a54f85e416a5552c9a93f38b5929706e2903f52604077f9623bc7927081cb9722a66b81389462b8acd7781e10a9880b41a3882060036acc5a371ee26f7e29f71857d73a257ae7b90c09ededd1044da2dbb2576052ab6a51dc44f00a6982a7b38a061c59ffd588498bdcba05853faa5d50cf4d03960c9344be7fdf68b44b56f65328e90972c10f748a35251b4812bc1b904d68e9d055a6d913860a1e2ab4a0e87cdf0bb89c67893cb8203784e2771183a3da68a319a39e15347b13af0b8d0392dbce9dcb6903a4f25b359131cd9e956c935b0494966438932bbbcaed34b374730e0050b5ca3f1dd82e8db25ae72db57d744c1741f18b7377e17c6e9186b6c71a9d46313fcfaa0026b0fb62b601f708e860742e567df188f44c1597ad8ea2fb0060346cebbcd0387c1f17b4711abb03f3ae1afaf0f59df867a4fb5767efd9f8cfa9259dd29f5bbc90cd7873cc076ec1f6c3540bfeb9d723929c4aeefc02f265d40f2ab9c35692d6cb3ad954cc7021cdc2cf875084e3dc548db3a0c774dbeb2315f3a9aa3221c33923fbad00b692bb984f67a437dcc4802d6e93a801442f5a13234f487b20bc40c5edb0d280878305bfb8370dbd5b170adfbc6392bc396106b3920bec0d3a63e2c2033c9fc41cd79282087d46019bd0bb17bee52c833d998db6b18100a551887b080b3ed9442ba30af4382a5406abd472328602b9f5bfdb65ddc37b6eb419bea9d9d3756f8d384820af18ffa02e32de964e0a5d3b1b75fd048da067390b5fdffed94e5a53099495c3d5d728d05cff30658ab80eb752a323b4b3d1f06677e858183d0a433c031451d9401a9afbb0bfa563ecf11cd2922513b52f79f0852a07daa5fe6941863a300160bbe07cfc5ea23d438fad2e4aef262ff687dfc376e850290e472ff55ee8107d4e8466d68c8654c9b9fa35e1fbf31e1865c5d1a5ed887d13b32076d49b83099286c420ab69043b9db3ed1810d97d372869ccb53d4e0458170aa6376c4126be983e4f3211e049619299c475e2852ac42daf960112153a46dab256f426463c17629a436362b00088a953ce04566d900a2a0cb62634d9bc22a05c84791fe1af05493bfe59243d8024ca91d7b0be5440343dca336a71fe205e02b3ca39b5efa0979f162addf292e995229142f627437e5882d92fb9d541538fdf9cf9f5ca790272e12f691da14ff1c3b5a63d44f8e3bc9c693e68c0cde53ebcb17b9e30fe44f4866f60e23632bdc692ee9cd6a8e4e0a00db836e89acc0e44aee9197261569cbb3766c6c619d376df36fec7a77f083e6343de3121c4276efed0c43340559604083e7e68d21b07f1349483918cb0e8c651e1711406e492e567cbc09e3b8ff7eb25de7f14cd2e20d1aa2ed42eccf34610a64d2fe3fe11b723a8e36e3a95036bbbd5b8ca76bf44764f657add728d678a598299e53ca7736ab1c1a39fca0b72c3f40fd74c6739f5caabfa60afa45f463d5ed7fe4b88fd92bb5a411de657efa3312b001f2edff252677969e19f3b19fc41eaf9951efff7144160968b7334f93241b6074ca902f16d780a143c294ecf0017b86d03a9bca96822c93b9dd085038196fb82e81bb4393c175cb2a559c92545c48d346b5974816a67c3017688439f9c1c6d1263b7481eb83ac581a88005559d73ccf6f68382cc7800ebfb65fb879b4b0334235195ad3e7c9c3c7f45bec6d044c54b3cd50e8512ca2bf71c10888baf14f4223d7455ee945b431e23b01ae22af99b58422731fe1cc4dbfe75a36da9b44c0cd1082e941580fcc7b6df20cea445ec9d5c8ff0f318f03c0506fd330d5db6cfbba23c73d296a4e49ed8b9226b631b7e5d8fed080cf5139a1f8842d25c35c5a5c23e34329fee4e64a12d79ad01697ae68596979b1016bd13d6cd40803c147a5224d2cbffda67902751a154ea2dc10ebc20df068d429472e697b60b67ac023d8ce6723f457c78e93ef87be2421df03fcb841f8d2ad5a5bb9e2ccf9d3032537fc9db16e1b27df119aa30713ea5962850213164d32c4175e934c56c5a00613488375c4e39ae1176606bf79e1733750fcd8aff65983642ac60d1271a9c4365af7d48b59bdc1e062eabf4904e181646d49eaca52d071d65c4bc436d00a7b94237404953b8b444933c3c5ee9b4d0e18de4813b507a58ae86a037aa94e81f4e02b6b5e01972ee68d6a711c85dca653a5799247a10471d5b0fb48dffd0f3caf5c90201262df96240f0848c02f5ce82dea42eacaf96882c58dcb4999c35753b733d6009ac80e9e49f00cbe633b7f4249e947bfd5618130e150d292b12e36c6b3b3fe8a0b8159cb5a2f160e3ea9ec271daf9d3ce3f82055384b3bfa081b78486cb6846601341db24867505cebc9a508214e07167bcf6877ea901102ff99b0a21f58c92aab856ee4e5bd579a34b026cdc24d09807177580afc0f5d5c210ed795707c6101d119c5e7ca334b4b3d7e7cf664464994f7d44e74ebd8d1d6e0166e75cf61df8a3bfeb605b059bccc8b0ca65a20a6cd62f9c9017e62a24bbc887753fab15f5198b1318c4318b57ff4f6ef39f8c622fd3491829350fd765e3268684d40510eaf26bf68b3e1da64e26e60dc1f402dac64f6e3517ef51b37073c321db4fb956c14adcd5f5620d565dd8e7ec916dd14ba60cb51ce861c55b7d31892a79a8e0c42ca9c32e0bb82a239ec55ed3113aaecf5172492f65d26c135b977a4f5c3bb396c4a2c33355918da88d80b852c39614db94a9a3b95d4d7b16c813911f5710c38fafb709966baa9ab6b2da64b8e83ca4dffb33ef12e818405686cbf32597c04e114191fea64c5b398c0901a17d0d540cdb23df83ce0d94cb8d2c1f3a24df6af2d18776114d16cd48a60bb674fafc53bab6ba2f75e1325f3148ac83c0c91d02d02bf81f86fc01ce554e7126966a937cc7597ce63f86ec46cecedc21f1e08d3f7992d834505bd0f1f9ef2c2e03b063feac80bb57da9b4650ba5eff7759df73f074a0a5f3b54630b080448845e4a90df898c4ff53d3c02b91e5dfa4d67acf9f589cfba25d377407b79b49cf7c787da3caa93f42cba758a78a42797f387394a118f87d9efccf2c19be11653275633fa849eba096c8886c845fe5f922a2001add80ac2d6bf306b55a8b15db4d0c1b1c39ff25ee7b8407aef21772e783e3bff3f20be2f7441c6e1bf1dc012cae1873f57389863dd240c335372605a187f05f0b51dd69678d452714e151eeb2dce75c330186ea3e0c9eaa87c632218457c4cd1cf2316e70717d81cf5df97421f6d3a41cff1916b89dc4f50668c665ff0e873254b417eef95a88066b2b0a1f1a2801890b5a78e117d3ae1450981454ae09c7c8b8fbfd1c8f92f839bbfa2f89d816ac57d12c97a677d8dbdacb261310099b5c319b00c49ca4d73afae45c12e30dc4d9dd39f0e5c5c7aecb3df1b9db88a07577d01ef7cd9ba172eb0bbe82cc97b1dfafc8d216984c7e24c3bd6ff2eaa7f3003b4abb3b689b43c81d423a1d80468ce64115487d9fb35098cfc4fc6547664a582bfe94a28a0b97c97a839b7f49f4fa3aba2e6adf8e38de90e46a7ae1f7a791cbdb318777264646f27cce7f8fe9c0c73bbef402bfc7234238dee234bb1d1119828359a5665cc715b511f9aad8d3810a925fff520615a6f687d68e20a7bf56cade375f389b700adc6ded373abc4a979fd23f54663213c91026245d1d814a9bd63888e3c78d0a44d00f6fcd0837f651e3cb3760ba021e151cf9825bb0608a637bc47663e06bf29bc3cdcfcd8b0f1ddef13f684cb859cd9af580b17b0c4485e82202d43e92cf8ad3116ac168562507a57c0d5789e525dc108160f8f20c43bae08fdae24594b8351f0705162d86ab3ae44d511eb11052db378c42b1df8c22ba92315d217cbfb95188d58bccdd13d62deeab55def673caae5271cbe55f020fb7993320054d2b4d27ac59334efd384de0c953f08be8d05d437a831aeaeadb1cfd02d49371000a8af56253f53877b1b353163a020c3f84a68218cbb6c23150b6bdd57da061720e3b70d26efa31884a5b627aec0fed84cbeae4ef97cde04cb5a207a96b6dd9c7fdc06e54ffaa9f737306dc943173f3d601c58fbf0f6d49152e4a6f346b67487bf6665c3104efa6ef4830ac353a9ae0bd9bc3f38e96cb60dccc99ef8efa64d43418736e5a54304b9f54eaef8c625cd8f5d46dca98fa81178cbd12eb52a2b74f4c9993bcaca369af876fdf36f64c717b0fb0364f27f3fefec4d9e44fa666209d5e21f24bb1bea1ff16d4d776fb35f9b731d8a5a981f0cd0a413ca41fb8f77479faa48d542889368251969951ba302a8883b511d3b5e37dbf8d713e579e4dc836781d3b9ef11f707b708e7839720fbb0b96d48b57fae034fdc875bc5f6682fee2cb87cda41c214b60ee573e9240071bd34b6817729c7ece5122ed824131b1c5b3f25a879c3aaed700addccbba69b3cc1c830462733d7e2f489d511766038c2a30b982c46676153b1d7d6e2470c8b7e90748f9fec97379f5b4e37f9bd3444f7e9565246f3ebf72e2cc45166ec2b4afa3b94fbe0edbae7e6cc19c5eb13112f864a28da29760e7831779b4af8c9f0e44d6dbec2368c3d1e3fb6a7cb31e3c3246fca7a6671080f355fa489f2b7a1e9c9e9e9ef17b909b3b7573e3c089bfcfe43395f5e35ceb1bd5e99a1c7226db1edb4ab89e450c49a29ce64707b2edf45123baa59ca9f9f95988bc289e3b8f549617321f055105a6d94f6faaf2164517b99c001dc9a1504aee55acfacfe4848951e81af6adb30bcf3d80aadaf1f17337df4aba7560f0787fcbf823c9bf9bb8b761f16df481d6a36e574c28a5786c7acc41ad994eb74584188f57290d01c1db457e4f49ffdf56acec90fb95737722fdc70cbf6e286b37c985f63e04bc1786f2793a929fe4ba9b7344cd1f47a61bfe4b8419884a42084c2bc6adac46ffe216ea2cc2b487ecec9c9caa2c4cb35b5dbc3b29882886452736b6536e9e973ef4d4bfce1ea3229f9442034d148be8b1d00099b505b74cec877a6d39082da9a35b96982d5d2d894388b42d4ecd0cc66bb03daddef08ecdfe4fe53eda98d70ae0a236c2038ff39e4bbef85a79fd1a75dc4aec1527484d31abc09829951d01876d9c777abce7dba14e04df89b3da386516e674b95bbedc8a48b6276476dcdce623da8fffa36dc95bb6d22b621f017d544404265c8a9d81912a6ace036db7d5ec43347005beb8097a434662f5f56aef1a6c717a9d98fd0d4ff958a8bb2ec7517b715901d4a9e22c66b150451682f3f1f52743eed71290ad60b41daccec84abfad4971c5a0a91b2d39ffdd199cc32f16161ba7978f447360257ed3fa6f01fb61836dcf9ddefebaebf003890d54173a07a2dd1d9d28d1a8fe2fbb1936e63687ed46a6cceb1610cc57006e48b58db5ca9d1e6df1259098702b692f7bc04b1e357f78920405a181f95f726e017849b17939a7ec6f494f288fd003ac2f430612e4fe0fa86cdf358228fc0c9cef4cd07759a54721c673c14fc220aa51a3ef9943422c88e7919580f75b6eb4dad912cda9781cc3f549caecb61c387d2cbacbc842c13a8a6a3cfa8f16c86cbecd3fe9a238992301a8c42024690b88ab85da58a1d5ac5861a7d7bfe786f3b8c190b202c3893c2a1f8d53606984964fa073d6731a241e7729d37bf301b063674fde91b5007dabe42e897041081184c4026feec0313873514d6a218d6df6c6f09356ac950f237a65bbf48c47d73a038f210a1f15ef1214f4accc5fdbc8bf1449c390697b12a9e3d9b99db6621a356ea273d833057ac2c58ae0ded3678c378eba5e4885b4266441fe9f82e3c119ad0118bb940082701f7dc057ebd9385a42cf58cda71d1266ddfae9a1d2e185274c7cc8c006ab160a9cc5d8a77e263bd8d98703daa52ec1080cf71251f70cd91efb74ed4c439721d8d7b0504ce0e221f2c98f6bd60e3d5a3f002f91c7588b5d20aa8d9f7f6fe25680c3563279d903ac8a27f69de517bd162a07ea64dc775940d1594c7fb642f8e258c62eb9ab176a8760c1511e67df24267084f1165c137aca9150b5adb093275d9a5978904f79eb638219568df090c4167dd67cabadc2a432625d5032648b2d7a225dd36a031612326cd45cad626255d2e6b66dcaa4204f8521b54ff25a91d29e5b64bf1400fe25f98fdf1476e1275ca024f811947886ba212108f63403151e4f5e6484c3230868390c45833821890c36432197a253c51918d26e4a3af8659abe29b807034a2fea248ee37db7dc064ba3295e4b626021fa79a16f124e16affa3be43d057ff8042da38d59258e50db158bc553707ddef8fd270a0d8fb431992203e768a174c81b58f96c9a61cf8eab72544ba42bfb0be70b8e93c0ef131315b12d0893d3982dfef0df5e80ec8c231546eb1c066a830840c7237097f307b3bafc85cc1a4fc92491f601af9eb9c3ed07d767b92c98ec53ee9f0f0e15698c05d985d06d3c022ff0a8a8f17a9b14747da009c1838901b2ca8aa4938baa8c8b037e6644150d4410a0b5491bc3300c268688858a0aa6e8608e02b668392d8b1f5b631b8d9a8da5db4aae6ceb002fa27907dfba4d782bbe000f8aecdeb4339254de1dbac0bbbdfc5687fd24eb0f9e0cf40b27b08d139806494279d00c8aad3a5c9c401e9c402128d5e4fc4099aaea6bf5f12112f31eb922faac502f1a3834831084a8a2b63011e232e19c70e288609ffa2071299274d2d438695438693a3871429c38cb89af13f62389ed54d01f585c1c0d93054ffa0b4f883ad6c409fbe184c120a1301038065c86c704d3b448304d2440a1003b610938d69a9ba1288f50441c8250c3f2801ffcb0871611c5a2a1c415419024141e705ce1c58b66103d7898cf0c555b68261045753279608200ca8ce763c5264eac70e3c40a2a7cde43c50e1ef645d5858d2124e1a48a227cf1a3d0cb5dd9802811e599a128204a745205c70d921bd5733fb40849b2c4668a0cb02469faf0c614202a64bf9811816250088045057a51e5630a4754a1e9640a3948523592240d1f2449e3461b7b58430f92468d3c744213094a4c07e573324514275340713285025078509101314e9e473a799474f242d2c91b4992c60e92a4a983544d219d48b106a90627526c7122451927527c21492c28410a20e210070d1d2449330749d2c8210e1faa23d2c0419224cd1b4033f422499a34dc20aaa82d4e30c0058d1b346d90240d1b2449b3068d1a502a7081345688b2271710802445218828d488020c2751f4800349664c9219731285e8240a21ede1c0c49e1c1440f9745072447182b206499242a00f553f1d29c78989154e50c43841f1e2048a1440bcc1861a7690431a6b38031a50cc2a34d5cf48e53d749e40f14ea0d88024492b404149018a67164922d1605f860a8528fa441c9e08c2133778c206663c918227a82772fc139a930a2441b20f060a16d5dce20b97203980f00372949828c2119a3224a20f6a98d0c1640e6e300943124ca660c203144c000092244d5fd8cf7f811213fa509dd9f2269c1309404992649c44401392f41272c14904825085a659759470128129aa1308b4a00a4d473e238ee8c867347d94884e1eb0054962142b8ed455f93839e18124893e584c4e12dbd9545888a88f2864ff379bca332b556931252b40312314847c3c68e6658a4fd5794bb180a80f16270db0815485a62433e68ad057f652568846d40903129172c2800e9c28208d0cfc60499258208303b420018628590423dc2841a3840c2709f080a4714333831304a821499f190d19d080a20331384180002449bac401ec19064822499a31942000094728421124c90c498a410c66408624955146a8c7013a1d901a450a94cf7bda09013c484a1620090e5042ba42152e70841f42c8010ea42152910e50e064001ac9ce0fb91b09400492d448363c78542702c81180499c04000e2a50785c8a0a7d4c47b4b91fdaf122430101557054bf48d501f9a83aa0e901c5740010872405917a9c00a0049274292af4c412de529f138d0f4e341e49a2b6902aeb99eff2a12133f43c78d4ce0cc9d65a6badb9bbbbbbbb33c618638c31d6ddddddddcdccccccccbc7af5ead5ab57af5ebd7a758c31c618638c104208218410bef7de7befbde79c73ce39e75c6badb5d65a6beeeeeeeeee8c31c618638c757777777733333333afb5d65a6badc531c618638c31420821841042f8de7befbdf79e73ce39e79c73adb5d65a6badb9bbbbbbbb33c618638c31d6ddddddddcdcccccccc8b237cae396be62266451a01269a2049d20cfd15b5633f44a8d01b99f9a105c5224379fe878609a1e78844d4e7a3c1424b45b1549a2b4892c60a92a4a98267cad0cf4c21148b2469a820499a29485290a459d92a2515a0cfca8746a2d1ed4c8a220a7528caf4f18caaa9fa0a0b4aec9024cd1992a4818224699e20e9a084e40449d2304192344b508224699220491a244892e60846308134b2e2b2d1426ad1051016b969e9a2a5456ea410225a74c1c5c60b1f5cf8f08c2cc5051cf58879d10a0ce5e901472bffe9c07c241049b44092248d0f7a603731a3ea3d33121c2a764892541201a9e40c48a8a00a4d3374535513a49141621f0c142c449ff9e9c4744231a3cf3f220d49b222029a91e4197926b505cca7230a51d407ca147a84171a2f44204992a429a221f2224992678aa68a23b4901a8105fba11e709445fd2226f0e69c113ad0c1082f6c872ab2f220a8cdb8a84494a772375a841792f474fe506929623f3e66423386f2c82402119224c95c217ff8f8214af2a32e3e2b2a2d459ece1f9588f2b8d8a7c2dd08a50a77a31df552d40d21516929b2b92124a1078d2a44b8808ba8b414516929f2e94ccf10974a543dcc10106856a3ea7308151068f2e0b1015552891f2428c40089219214620cb09988008c20499209241d847843920a1137f693746e10e2708424491e8f6322a11fc283c7065411c223499214253ae288e30d499274c43102a90b38dad0a1b079535d5143d515b523fa1836723c8223d83c723772d781636510660091248d073a20499e5048564d0f0ef0a7f2418940332120a24f871a8281900f04310641481a0ae808440fa27080243d118427cc084403a44a93058fd04bd20c208af007454892540249072056159a38a209ba62468485864715fa4e0b0a473441281e8f0f1e3c74fc21057e8843871f5c241d6fbc21bd91224992157cc8826bbd850034259274828d1b3d9024c944d2c1072aa4246b4dcdd3b2c20ae7064842e181d2c5152b6e0c91dc409124e93d9d3b61664c1b9a9024e943a40d38d440d1b186019ad0860aa436bc90daf000cc075aa1f242c48a0fc582d286244928f6d339c286212410c5c61924141e5c8409483ad84832a4191b3b24c91dca870d014892f459d983225078d88e1c244972d1b18726ec218924edd88310499276a07cdd438a24ed40d1b1870668828a1d6bb081c223f4301d6ac5435923079224893e6b8024141e6b7821556baccaae610022920e3dbc21e9d0c31f241d7a1084a4430f71d4cf4865e6e38362599124cd8e25499a2ba4c98a07cdbc4892a60a49d240f11e3ac3e854df22499a0d5492a4d140cc7bfe41332f92a4a16214b3a93a92a4d93185243d6846f4a099299a97925334b5d0a1468a068a0c4892a491423445530b22208a054451422449937257bef248fb91a11f7725f443923418780f9d2fa11f9fd04824aa1f0251212d5c8288dec5332b25e4ff25886752ca1344f42e2a2a2e10ba40e842c4b10b9166765242dcada8a8c0662bd0b1bb15cd05242e64a8501049d24481b222491a2844d547923416a88024aac20043365b7916214244414441840869b6e242e483345b09030ce96ee5598408110511051122c4dd8a0b910fe26e050cd94c4524a4998a2848339517d24ce58334530143ba5311e24e4514c49dca0b71a7f241dca984e101d2ac59b39595951517222b2e4462ecc747b3955017cd56429417cd563c2ecd563ccd56c2f00071b712860888bb9521ee565656565c88acb81089b11f1fee56425db85b09515eb85bf1b81009715cdcad784222aac5dd4a457d346bd6ac994ab366cd9a356bd6ac59457db85381f1741e883b9510457954dca9a888aa0fcd6771a7220a81e1e24ec55a1fee54e8c8c59d8a67c4853b15241d2ddca97c3a5cb8534932b970a7f281e9543320d00c89a059339715222e2a445cdca910a99f51e8c5dd8a8a16cd549aadb8ac107171b74204c810772acd5456dca9ac346ba6e26e45f421d0b7b85301b284396041b21398c3169224e99803cf410159a0a0f0f80284c24556111ff6635eb412a2427280220e3fe320461cd6e4c0a18283186cb3111c5400872924a99a28554c33ffe30d492469d38353cd2d50aa8992451a37e022312fea4892460211903e8c4e15f3a24eb3d187d1a9dc504992c4bc38c2e79ab3221f1fffa1649e45a5ba1f1ae2a1286abf873b2af4617ca8cf045513e4c28621fc70598321d63004697a149967594305663649dcadd84fd2510951a23aafa53aa1ca7efed38179eae97c0d5194e77244309f0f813af55b5ee4121a0199992c3fe3230d6974a1230d4310511e978fe9bcd84f0704f6d339928646431c68d041094494a7aac27e3a47485082890a2beca7736487fd748e5c6ac60382cfcad70972b1ff79501792a46180660167c8e20c28d2190a80c22326c60c1f141e9beac87cd006049a3c78446ead435519a620bda8806086783a33468aa84e0b7d117826cc0b7911757eb8d64766ad0f02d77a725c5c6b4ae6a599cc6c6126335b1693992db165660b6c99d9f25a66b6b896992dad65668bb7cc6c612d335bba6566cb6a99d9125966b64096992d8f65668b6399d9d25866b638cbcc16c632b3a55966b630cbcc9628335ba0cc6c7932b3c5c9cc9626335b5c66b63099d9d232b3856566cb929946c8e0802a34a181051d687840071a5248d218a820d98f1955a339a4fa8e8e31144092c4700669b262d8c124aa3a42445f8961b2341b8188e488e34554910f513294108a7aa97218a21092247dba683602c30a2429471c3e3e44e0a83e3464109294431081908e544a8865f91025aa428fc443b9f0e0f13c4413a5d90845542728e66d17782049d28b2824ae39eb242ce43f140b17c2f080debe67f36174aaea61266853553d9a8d389b23758ab6108298da114d198f6756fb9bcd666dd6a68baccdda7091ea5b3c1e1fb5ea70f1af22aa3a9ed07790c0cc1f1f335d3e2b228a85a5d9880a7d7079b11d205c641569c6a92687534d4e911e3da0cba2191177a3b754eda1630a389024984f67c6e3f141b550a224a5240a0a40a0128ad8c2195638a20a4d1faa138514e83843ea3863c8342945015285263aaa2469650a1d5090c3ed702a8903137540c1495568ba42f4e98c603a54f4e9c074a81531a34f35d7c44850b8d0c113b220bda07c3592410e9327c427a048f6613e2820aa330249059c8d740227882449ea694d8c44fee8a2e5c5e58b12a9e48c922a343da542a2aaadf8743ca18f59430f00804d384392a42fde43911ce984ac98f9238f279f9615309427871a9294230f9294030f928e2688710749929078286a088b085a26912042beaa7c803a2f9d992454a80865472e40aa17899ece1f445cb208514078f02080ed504578f0d86c389b0e686e4055d58335379bf6139a1155dc3df8916c1c3333f3e2089f6bce9ab5c86233b3983fb2783a29f5928252cd2c8aa05401820d920f81cab084244d2865a0a1a38c468624c870021949282af428a03ac880128a8e17c02149d214eabca0063a5e9044aa4414cba4e3053b2617a4f1a1444f47d7053990241d2ef8a2ba940be01896f850a11f238d313e44cd9719832349938e31c650c0141fdb411103109224e910430b301f2a242302205d7c18a11e3d7af4e8228b2c92503f363233546d92cc98169ca0058f0570b0e0062c60010ba658011e56f082158460053b2469a258a62355473402cd78aa98993228339f593b9f09f2783a321fd3114d199499cf7c99a8a876a06840031ad08087a240339ece8ce7fe87faa8600d1590a1024b89503ed6043c787c353943be2001ca4b0a38a1230569e8484111244a3483f29c2f5270002fe808630961804047180f987184247d159b8dcb0c0f663a90a410497ea023892889053e9d19125d41a1cc7c6638a2102523aa461765a68a992f3ef6333955f8c8a272a95ca8f8fc40a9c2435174478f1e2f59802811109922320298bef8e20b2aaa0051a21d4190b071a49a2324399024c98a1085c40b898241b29084466c48f3c7080cd38fd18f0020ea822846644575081d151a3c78d8f6565e2e4ee765bbf282992f5d94b8415af66e2fd6eae8fd67bf0d92e3747ce3a48fba4b091b9e2ffa6ebb3e1f7df72d1494ac414edb8eb60b19fab58e3afbe98000aa213216a35b2e3277ee3984ce756cb7b9da1546cb6ea64136aeac513bafc7dbde9a25689077756dbc286d4a2b7d7b06591fba7a6ba3fed331ea7ca5c40c3246e896ede6aa776dff6590933ebba6d768db65bf22192475d335b3e5a8bbf442ca7930f3659a60a6689a426f210b89406848469fad85cf698d77353a06c990c51bdfd106a97b3dc52097d7ee56effae6ebdf711e83f11b516d292f4152c0901fd37999a6f788e8a89aa669822c4636a39230c4e55c8bafdd5b33d36b9bf2b2b539eec7791ca2904ccfddc0743c9e779da4040cf241cadcf47adb85d0310a5d48c917647d4799473a632fbab8398fdfae1620912991e843a2aa138a2f255e90eddafa59bf1bc736db7bbec3f248a8d09069e2caf3968ad9346fa2a40b925dbfdd26b79f9cc7d64e6b7d7c3ad41039d97f420917e4b3f6b167df5e6f8c31dc826cef3d76ee55e8d6c6398a619e1117d304f150a205d92ab5f1d5d78feb43879cc7475a8602b52c48b7de366ef4beea4d9f711e77362130aa9ea67783122cc8cb6ea9f37a70c549a95322c8cd239d04255790cbdb9db596ddc62eab97f3787137e86ec4dcf3a2c40a72b6af715a5b5fb3ed68398f8fd409f2364dd3e46e344d9ed9097d354df6f347eaa4d3e4af648c922a48f8d5c6d870d6f88c4de63c7671a1e999b57247de33ed23098daa6316239b1e255490f1ba696f2fd666b3ed6f0ab2b276bfa953c6d7be8342a6490af2d2bb608d6f756bebc209af88484854b96331b231414914e4ebafb459c7d67dcbb4e40c16978d913176b4a785c2bc7632abed1032af469f10695bf79cb9eb6ea55de204595d43b69e9b9fb6d6604913a4dfc996564ae9b3bcde723762825cf37fbdb858ec5fffbe53b20439e17458bfbfceebbeee4809d22d8495ff45b7d6b976dc664912bee37af146e670f1bd8ff3d8dd03c574181224c7492b638f526699d796e4b708bd0c25a44f507204d99885d727bd9479d53a61e747094a8c2027bbed59c7f9f3fe6a91c8344d53cce8e3588c6c684911e45bfd1fd9b6ae0d3ad8bc8408d2b9e172d4dac7b859ee583204e9b8b1061f5dd5edba0d398f65fec342ef1212b930bf90a862254290d7d5fa2bae7819df662fe731bca251ac60485431561204c9ec91ef751c2be5c83c66f379cfe31910559b6da3949714da4629b1bf0408727d5df4daf92cbdb1236d404a7e20fb56a66d5fa41432ffd91e28f181bc77ce06a9e3c990d1c8af07f25f84cdb117df9a3c5db49ff71f253c90f72d7d1effc5d0260629d981ac4fd9eb59613773bbfc79cfa69b16253a90be7c460a99df3d2f6e939303c9d6d25ae9bd1cdd2e4a398f1d8744150ea4c3e8d0b6bab8365a5be3bc4dd5698e253790ce46c673c567c8ec98721ebb8ff9c6cd6fa9daf98cde074a6c2029f5eacbb1b3175a873124aacc90bf9cd6d6e0bbccbdd8d44351748b921a48e75864af51fad1b1edd53490aed93aed5ccb456b9b35b72e99818ccdadb9536e0ed7bc8bf3986d363023fad6b50c24bb9fd45b5cafad5d6c82d854120339db71f7e2777456e8cd79dc2a2530906fc61b9fc2f916b56b3dcee38ab2ce2d24aa5c4919d25a16abab75fd5defee9221636d7d2d6d2cd2551fbf921748c7b5f65ab0a3b3b3d92e71819cf6db3facf6ff17f7978c21ef7b5debfdcb1c63daae084ac490b741d89aba836f396cc779ec992198177d3a1b3b41cda1174551cc65b005eeb11dadbe2a33ea9cf318368f36a2980b250b2a8bcb276d934d7addfc7436ee461be798d18709598163754eafecf69d3777cee3e6a202e91c8cccdd9eb35ddb6a5d2990acadcfe82cf79b963dc679dc5c98a128e6b22cc2907efd51fbaca3fdda99398fed6f9c5d8b3392521629dbd6377ab3d4f9b073c83455143a16231b9824325af85c7de83ad6c9e064ec3e44d59691357e7bee2de60b23a32e3616231b2f9044760eeb7af4d535db659b91640fe16af5c5791bf2f5888548c6e5ffdc8496357bec17e77105b9923ca17b6cebf5ca666dcf79bc61dc42f2fdc256237deb9cd1c89cc7ad0a3024f7a3b76f8490b9b6064110bae7acdab5ea4706d9a4a4f78c7f67846ed95bb582623a8c7e248c77bdbf8c3efe5be9c7c8e6f7da2ef66f6aafb7158f7431b2b5b05d643c672fea7e566ae733874cd36331b2b9472473ccbafb4e069945c6603e9d2371a140b63867b7566db4b42ba48c29d71df93d59c3aebf5cfcd79a3916239b1270e4336ef7b95557a3d72d384d1ecfd769fa108c74b6eb55f6eb32e89e3ae7b1ed6c1e34237a1f1fd334f3998cc5c8465ee97445ff45ddd1e66c9df3b889626e065e72858f3257e383d3195bcd5609acacf7fa7adc7ad2ebd3b2247605e6335ba6e9612a10b5e1c76264632b7b6c113ad8cd1d8b8cdb6f33cbef4e4ae36ce6cb79fc222425f4a29569fa109069b2df0151759aaad08b40d3cea9178744a0122550099fd97b778deef176fd50f1eba78b8f8a831588e231253f7b93a17b945d74479d6331b2d97cc19c37165fec059941e8fe9c57637451e775b1a61fd98c3b8b914ddd480b1985cfc2a60e42fb28e7f14c4804ab1af2e1550d4d8f2c46367e02c79039e6e6cd7163e6a037c3d8683b1bf95deaed1c13c86fabbbfda573f27a7e4b20ff3d3285b63d7576d049201bd7faec3ae8aab769dd76a88d6331b2198111f9e89cacad5a997afbdb11c8f8b6314abb1d737a1715e285f479eb57766f74ccb4af08e4aaf7c5c87e9bb1d58ebdb4b318d904292299766cecececb3aebd4d53880a857e9adc6331b2e91071ad57ece7e05b8b6133638fddebf2e833c2c7cc3072de344d93f3900884821779a3d3396375adc6089d398fab23f3411bd1c344b7d9bce748e86336f4486dcd626403c4455ac8de7270dd672b6cdb388f31d022adc717fd3686cfb5785d0824e3d5b6a1bdb4bae7f035f3907f0f176d96d77cf53b86a00bdb5e7cddacae56af375b4bb5b02c06eb738b42beacc245570b1792553759b3fe8f596ad9e43c4ef5d80979104e7eeb7174cbc276efa113c4ed0202796773b4f536befdde6d9cc7202a64797d405e861fbb317e0621dfc979fc1fca5337cdbc22df3adae2bbfcdef557e63c863fb6801c39bef5da41f61673b39b6290783c9364bb38cee66efddddb9585d1a94621ea7a40319d6e31a30f13c55c9920b2ae8efcacdde9566bffeea53651888a7c11bac3d791c2f51cab52327b993a870dc6bf6d4db7a285b4d32b64ebae5b61b376711e6f18b3e3fa48581740e46448adbdbed8f3d3d99cc79b66278ab9ccfd903e9f8b753ddb45db5f1585a0077cc8666f3df36963a4efadc579bc79d003d21d3ebbfedad93e1d759cc7905f072483b3ced5ebde1827e4c9792c438560b87bc837eb73cbe6536eb0c5cb791c12551c90ebbbf56dcdb173cc59e43c16c5dc48240b79dd5cded0b98eedfa659cc71bfb991bca72bf116021a7d7472b37eb687b66c879cca946a04d88ba1be7230548ca4bca8b9094fa45ea23d9c8ccd06b2c46364278c87fb345c8a8e5efb72ce43c0e4d4eddac491473a7e9435415816814b3598dc5c846045142b771d186937a3be793f3e02845a52525e54548cae6d3791d1281d887a8ea2c46361c80b23e6be37d675ef9bab9388f43ff79922e08e7746fb5c5ed59ca795cd9ff6a237a98c6292f41aa99b27a94e2324d33e54548cac6238b914d890c5c6ba0d9d780f3142c02dd14159200531a5812b8c2025304aca04015bc6303ddb91f025d400313a082023ba498428a0c5c408a2b1aa5f0e6391b0a60c05d052e9084921159200a0ba040e15a5b80613307a502cce3e91c5914986202f3433b2430f347aa88c01410d850f10051688aa638690ce880e954513880ab993fb2d9d1002918d03154dd6cf8886b0daa5fbd056c7628000309d86ca440c026c98c81e20030946703f3e9c0c0509e081820c60205e0cd87ee2644892605083000dec44c118a00a80880fdaf36170040ca13950666478e8dfdcc8d9bf0460325bca93a9b8d28c96340f249b0195028f998f9d1b0197d304001a4365ee0081fd20e7490031cdcc00666d480063390410c605006192f70c11862b480052b50410ac298495285c000519f18cf1114743830f76da5f38bcd094c500212181981a4a30966483a9aa0891c602880d4861860f02179200090e8801d24a96a44079248928402820e3c213d128a7aa8da630d3d9a207a981e64f4b05af448e9e1000e48e2840376c082c4811c70400c69118a901c6892cce2115920e2d3a158baf359c902491643b2a0408924652161f1862461e1052ccec02208120a0f2c7220cd7c7c4892261188a82e92260c27583880872624942a78acc1c30c28222a08ca34adf048398421a4dae1804254cc8c87026d409468f347664c54c34934038507caa72324824ea2094ea2072449a26207ca099aa2a2024ea02002af979d8bcdb7f84d19c8da8b5a56d946763dd218c8c9f8dde7bd60b35f87815cad39cea6abf172ccbe0c19977bfc98838d23fc66c9901de17d089d6defb5f802c9eebbceefcdf68db2ea02492b6c18977d7863f71c4376743a2b74d4325ff7e51043daf6bede8790a3b3d16b27a475b4c2656f9bbcba3527248bf1cec88d2dbe90996e42b2c5fe99efbba76fba2664e4ebe65cb523a3f4ae9909c9973e65b7b7c18e7e3121ddaf576b74d3c2d96d5e423ef7cbfdb3eff64dee6809c9ff0c5a0717f51befad847c37ce76935aafedce2125249d8e272fea209b4cdf2464d759f9dfa22daefa4d1292f6578ed6c5b6bf6024e45d8db2e59a29bbafad07095923bceece603b65661f21a765cf7fbeb8e0a5d61192d5be7fd9e4c9b339d808f92cb3f1da767667d76b8c90d1bd3dda8f7b3d48d91721fd397be7cf672b5f5a1521dd178dabf93367b3c144c8cacdaeb5ec22651e9b2142b69bb536b798ad6ca33d8464af56dbcc3c9b568786906d1d5aefe6f6d9dbba101246d6737e74e8cd1242b2c81ddfe30b59838b3b0ef9b87937bef7d1d5ecc22163bc4ddfed7d2f3e1d846cc8b7c5b6e074ccad1784a4cf5ebfceceedeae61c0819db9b2c5e7ee88dad0a0849ed83af419e75c23be31f24f3361f7f74b4b9c86d711ea7bc04892c46362c30f1839cedb2f5b1ad7b591fa4c74b1bad9332b6be46ee37e46d9451f73042e66ed15a01f11d98f0c1dfd1775a1983f4d5673898b821979b96dee66e7d5f5d534f790992c20d0993362adf5ad9fe7dea1a3664c3ba76ddb9afbb6e03fb0c267b90ec8cd558638bd0aba56bc89f8b3e9cf3d2ba685d1c4df420dbc15f347a9bafad852f4a1335e465bcaaebc7deadea6e1cd20224c5ae4cd3102129ce358b918dc919267990af59e85875f6dea06d95f3382482292f4152aa0f202922aac8e763545a5266422faca182091e24eb55fbf1a4eed141fa9cc77e0749996de62284ce1ca59529133b48bed4ad66f0ef7ae89cd641ceaecc7e2f76fb68ad7490ef6e4f7e6ea616421add03267390fd60843edf84d6a77b4b0ed2a34fe66b9f3d52bf1e0709d74fdadc2f16ffd566e120e36cc6ccdab61a69ac363233b4ba0b9337488f90b6f8e6ab2f360b997f98a421edacbce663c691d947a90b17dd5b98b841faabd1328b3e277bf33658039336c858a3ffa38dcdd896b187619b206c901056c8bcdc5b5edb83eb6e8ed4190292029b47292f42a6a95a839cd5a37dcf986b0fdbbd1640305183e479eb6c0f2bb5d6d1c7d2209bbad66ebb471a29f5478364d5e37acc395a5985ebb9c919e4740cb6eb7a5adbbaf96e4ccc205fa5b6b93fc75665ee1e91212665903c394e37a9b3962f7ded8c98904132c71afb572dfcf518da24d48f69b29f84a2f0814cd0e0dbeeaf1fd20b6f739cc7ac81613206d7fcd88b8d357d7ecc793c93840a8136949da6bbb1764e5367865e9849224cc4305fb5b45f5bac3de77d6854519e3a4d53e83d9e182aa4b21e17c2240cef3a5e70ae581fb4d13d189c7d733ab53d2b6cfef00b72b1757d7673fd66a5de9cc72ee525488aa7a33245c1c40bdc5c65cdd256a985b1b92e3c1bdbd23abd4dca1ce33c4eb16f9412d3e154139214fa4629cb45265cf8371facb71db37651cb792c1ac540d1888b8f4adb026c6ef56aedb9c6dd1a64fce69bd746e79abb4e3d9e59fda3d22030d1826ccb1a3ac8fc9f7d6fb61504932c4867eae6643a2b6d079b731eb766662c46362e265890cdba069d4dcbcf98f6e43c66579071c2bb38be7bef627f3d04132bc8ffa737fae4e8f7f97cce635705f911367ae15f375d7beeaad935c82c46362a26549073567693f2c3087f7af4782610b8e13805792dc765dd84cdcd27749cc76b39becdb118d9a4984801f2eaf62284efc1d51a3bf77dcf2d8fb53637635b6d1205e96e8b7771bfb7cbac3e108b918d8cc91972b1e5a08dfe1e3e9bd78251b90f1328c8cb3f5b651446fec93a4ea59f201d65dc3d2f53187d42a6448e7a6113274886f6e73bfb8cb5fa2ae53c8636860aa938c1a40992bdcba67dac27b747df5db498a669f2314dffe9dc3b231bc184097245a78b31857cdfb4fe388f3715ce783cf34e936b7d642d41da67b638ce18e37cd52fe7b112a43b07aff3ba2abc0bc69504e93f9fddc5fc39caacd6161324c8f962534b5dacd4f9b109f31f47c4e408b24e678f56efbecdb6d08701999a1841b2b35add63336be6fa721e7f189de8854911e49dbf9a6bcfd1c5a87d8b08d2fdb2d141fa66753152e63ceec7ae932cde5c5eec5a1f7116231b6b3204e9bc5ccfe9edf2377bbd0c132148e766ff73778cf673b03d4803932048369fb2d71c8b6d326ceeabc919f2363c630204f99cbdaf6db40d726bb5b24cd334b118d9844c7e20abfbaf7eb698e3846f721e3b2f9a890fa45b3a9973ae9741d87f7b205b7cd545fb2e65d6c1f64c30e181b4363ed61ada7676f19dc90ee47aba28eb782174d7ec7520dd9dd3b665cf368ff152931c48777b2783eb31bb22e3e7e2c28184ce5f63ef39dbeab3fb8603931b48fad839d28ebe9ef5d9e00e4c6c20eb8c2ebef9dcc15ae77aedb118d9704ccc90efbeb7dada0bdfe4781b6e4c6a20df3167a753da9c451b8398d04052d88cddd2599ff31856cdae99c5c80688c90c6074ce9c6b7f10765cabad685babf1aee5fe276b9cc7d3544d5343622203596f7d75dd8ff73d378c233089819c7f9bf9c6c9e8746c2d0c644fc60ee374ad52669d97d1984318dd45778e5f5bcc316ef1cdf687acb137a7bb266448baeec3ba38b63ae37bd85108262f901c63cfc50dd6875cdda10f4c5c202f75edbbe7bafda86537fec0640c392b6dd4b67737da7cb69939649a360c6d6022863be7b55c73ed357c9039b8daba8d5aa72f42cee3fa5564b5a413322ee7b35576ca9abdbf9c57411e259c90ce36666e578d1f9aa3209e394dd32446c926243f1a1bf2b51f1dfb47ab91688aa6695abce15a7562dc394d48ea169deeba7b4e48d933212fbdb1cec66e7d7ed15ac79890942dac7fffc218ed9b7665c3979014d2e50e2ea74e7b5d670969af853e9d9dcdbe52d69590df78be77a947cbeebc8b12f2c50b639d8bd955198d91eb570d48ca8b9094990701e4c59bda11813a1ecffb24e4ac8bbd236b6c424948e7e9ecff47d79e832ffe480192e223e545488a5b33288984ecf6565df48ece7dba2889f4e1a20510394dd3344d90c5c8268c1248c855175cb0dd9ef436b695f338446d440fe30c1fe347445f0df9f973065fb7b51c33c7d5f4428ed4fab5f03942ae76b7e79ad3679bac9df398ba9fc842d983665e5e63414923248db0b1fb63f3dd1779aec0c808591b65f4d9e95eb7858e721ec7182aa4521fe4b2796c11b2d969335bb718b5cfa822646cebd9fbcf5bf516991b414922e465cf1d72bf35bf794f4448f7e5fc6f63361947f74a0eb19c47e8abf98aaef16bcc1e650cd665dfbdfd9ffe40506208f9d37943d86075866f7e2124acec99bfc5bad967e12384847f1dfeab1edb743ae390f317736eb96773f6b21eea8628d154490192b292f222240534b99826d08ce9324ae090fdeee9a3cb32c83aaee83fa41907258390fe587baf7d9dcc7fc21b75502208c93f21b4f7576366cee683665e88501208c95ffd7f51870d99afc8bca9420920249c175a667eaef7f99b4c09257f90b5698dceaeed175f73c779fc1eca42d4104af45999a699d0342d1794f841be65b66e9b8fceda16bb73913ec8ca13de1559a3b732636f2624fa4817672a94bc215d64fdf74e36d763cb66737c908de965efda581d8cad35b8dc90b5fa7dbfdab1c6ff1c4e9333926962deb03312e656286943ce0a61bfbf8bcd7e3d1925fa6c28d1a7232a61433eba383a6646e9622fd23dc8dad6dfadaf1f46d8ba398f535e82a480a89016d304a242769aaa69aa8f6495ac21efe2ff77bfac46dace2ede70e56644bd84123dc8051775d6ef5ecabed52f324dd334239aa6b678c3a2cf9b1135753f9d2794a8216d63f1babecc2a3f742b461894e441de6bd9a3ff36ceaef7effd16519dcb8c123cc8562be5fae0ba6e417fb889d3b4d9cc696290c5c80649c91d2483d646da5f27eb1669b483bc953ebfeb75ba66b7da3a48171964b8ae57ea3532ff50d304598c6cc028a1434718577bee45065d2f876f45c6b6319dccbd2ea8d292c24222d01c64335fac358badeb7cb0c941aec8d7c26e8bcde96f31ce6318070929bb93f6b4bc22abfd70906dd6196d8bebbdbeb0716331b20941c91ba4a55eb9b11aebbc0c574d43062f4252364d038d23649ddf3e46c7ae1fda081b21a3ede9bcfd2dbb191a46c8c8eea3b4c50b2df5e7e822a4ab8e99f7b20e6173c740a3084967b5fdeeb17395e1738990eeba375dadad5dbbd5102161eb0bafed09b9aec9da2124ff7dd3f5c7387dd96a08e922e5b7babeb5dc72ad0b21ad63466f7b3769a5712d186808e1bdea8bdcdc1ce1f78cc3c5d68dc1c8adb1e7eeb56efad6a5ee3dd8e87c4e82060ee9ea5bec5a7737725bf7721e6faa8dfd7cd556a019847c666dc1e7bebecd5717e7f1a713f3a4a011847c3376376bb0d90addb46ff10d348190ec56fabe3d63ffd673721e57b5534dda0c9da0018484b75ad7ec6c43e6f15dcee3c647429408182f012841f307f9f8adf8a2c3469dcfc63e0fa2366f0a1a3f48db9a2db7dceb56576bd5e480b81d15347d90adb2e50aadff7d96b5c9dd469a37e48b7ff9b6bbfe3d5d35721e579bc62548d0f081a7a57143bef59e1bb4f645ce63c647d0b4215bc3778fb6ab76cef63c76e5d3c5345547288ae3adb5d6dcdddddddd19638c31c618ebeeeeeeee66666666e6089f6bce9a81a4acf780462f294384a42c6631b211800e346c48f7f6359bf0d9e9cfd5f620e3e2bfcfd56b3d46ebb8d9e84dcd1a92ad2fee3a6f63ad3646f520ad3b9f163e74ecae7b5ba3c6f2e570b1079b7384f1c566462773eaaea3f63e7e1102aaf8bd409307d9d3be6e3cd9ecea98313c48c69eb7fb6bf6838e69c71f9a3bc837db337e34fea26ddd0eb23a735e8fe9a477cdf60e68ea20ed73cfc566975d66f05a3ac8fa3ddd5747e6bfdeb47390f15e66d4c27add5bfea61c66f3652cbaa6d4c541def637c2e53436beed3138c89eec208cb3ddf6e3c741346f906cfb7eecf726cfc830529f4d1af2b5b61c6cf3ad7aafabd70dd2adffd7cd93da36c87f7d216dcc3247a78b970d32b668a79bf4bed77adb5d8384cf2efbfcded76c9dedaa4146660bf6856cb548df6f1ae45d0c23a4ccbae69af58b06f996bb95ae696b3357d73d83a4b4f65b77c1cbf0f6bf19a4c7199b7de7e5e674fa6590917d652dda66f6b9854f06f96ebd75dac9dcc70ae1a3212b5cdffcf15d47eefe31c8f90e63abebc1d8dcdf8a4142eabca67f43ead1551b06d95c5c3fdfa37ea1756ac120affde6da8d6fbe38e9d72fc84b678c76c667de62acd50b923ecbb4f2ec1b99fd58bb206d6d8cb5eacb7573ec552ec88e76b1b5d3f67df55ddd82ac8f29336fac6a41b265c8bee9ab1f6d8c340b92cee83ef247cb20a38e6241ba1aa15738bf3d63cbd12b48f7e2bdf0557bd77b736a0549ed62a6edec73dbdecf2a4846db32ebd6b3d7cc7d5241be5358df7c5fac7ae3390519d775eea96b8fe9ec35a5209fadd459bcd4b5e7ed4641cef91d6775bde0d7f6ce9035fa85f0d508e1bbd7452848c8ce27bb6af92db72d3e41c6b710d6bf3dffb2d8a213a4bbcc42a6def4459e2b3641da47e97ad0ae09978b8f09d2eb8490f56d0f2e415a7f73ad1721bbf3de0695202fe409d9196de7983b4c82646eb3ce7ed6dfcfcb201264b3f9bcd9fa864790d1c5b68d56462773fd4690ce1bbbf894de7efab408d2dfb608e3c70799ad4904d9b53edbaaa5add9f8e21064b53ee76b8ed54b2f83429097b63b57d7b7af36c320c816ad5bfd2e7f9bec1d10e4ac90f9b6b37bcc20fb81bc6e3d325ff7e9a48bf94032e89ec1f72e75ffdc3d90f52bafb69c756c35ca8f07d2b5f6eeceb7dd4819bf1dc8f64ddf23adf6efbcebe9404ec636c6c9de0bfebd3990b679edba66a50d69b338900debb2eead577703d9ee3eb76d5a6add84cfd940fabb5f6ffd67f75b73ce8cda6fd1afd0b9d740c29fb0b27a978dec6f391ac8f6ae5f9fbf9beed29f81a4f02b6bcfb1177b95817c7ddfd7357945b89c8d8174cbd8ec4921bfad0e0319ebfc6f6fc50927645a86f4b71c4f47a3c775a92343fe5fbaa24f1a6d6d97db0b6465cdda36cbfc2d57ad0b248dee35bcfdcc3563c8bbd8aaae27ab1ce784ae1143fa5ddc225bff1af4e8bc13f231d6e072d6debcce35e7847cede8dbda70bd5ed57d1312b6ed78617f378cd35d1332b6d7ec3ad8d5bd789967423e6be3ebf6d31b42cb1c13f21d73f5fbcef64bc86967bd7e2f83cc685f4b4867e17ff7476baddf5a09f9f7bdbeeb21656e394a09b9dc3abd0e7a7bc70a27216f3fdb9a316ffd6d1f9384642e3a73cae6eafb5cb348c8fbb73e67f359e7e6650609e9bafb3aeb1f6b3767f608f9fffd6ddd9d73b62de608d9dc3f658d6ffb657b1b212d85ebed5a333257fa8c9090619dff6b45d61eed22e4735b8b5abecc5fe354846cfada6d77e337a77176222465eb229df0355eb3672342ae49ab6dd0a77de65cec43c8c5abf6834d27fbc9b00d215f5bd6d969d95b685f77212474b4ba181fe3f5e6432684b46db6dff65ab3f5b28f43d246dbbdccad166be387433eb62c9b0e5aff87d10e42366dad5d6bad20e473eef2f4e8de318d3110f2efdbaf8fa3f5c72820e48c6eba7f0b36cb6dc23fc876aeedaacd9b3af3e60759e16567ecf274f7f0f39efa48369173f441322f8f6ebab518f305b90b39de9073725f9eab3666bb32c879cc2c0d39f8209f3d5ede7aaee777bdc779bcfa0b39dc9097327bed2edb7541db57c8d1867c2eda6efc6fcd79a333a79a5b8462b828401672b021d7b42ed6d77cb97563b59cc7cc43c8b107e97f9fe1b4eded5ad5ddb5de629aaa14205ca4ea6ecc626463801de458433ea73f2bbbcfcf1b7cce79dcfc1239f4206bd7e598ffdb4ba9c3c979ccbac83475403e9ecea7936eeaeb3672a821e18dee28ad2bde08b945cee3694a22da7cfdfaa13e33a24e10d1344d53354d4080cc70e1a323aaaa8f21326d804cd334d5ce34b9223f3621ea6e3cf5ed34857ec30800478e3c48e71832ff1bdfbc8e617540c88107196df51a1f8b34b267ebba83b4de0e3aeaac33c86b393bc8e5ee9dbb8bd55e16610721471de4bc17d267635cfcdcbf4a07e9ef5683973d5cd6798b73905c9f33b57e7fdd385ddf20871c64ac77ddff07637d74d6fb221172c441fe6cb7cfaf9baf3ebf8ef378f3e01839e020ed8ab3e19dac2b85eb43a056418e3748d7adcddb207ddc2b726f380db95ae40bd7a4d429576b398fe1e3400e37c8656b75932f5dd4c55579866eba3f44a6c9c275841c6d9097c1fb1c63af45ebd41fe7c9e460836c7575f3e8b1b24a1b6d1bd1c33c167d9098630db2f1737e6b756bcec5feaa417eabefebfb6c0e7fd99b06b9a83b5ae3a37da1adeea241526e7459fe37fffff93d83e4659fa374da7663f4f56690cdb3f931b33ce7b3d396415a0bab63abebaa707a5b32485ef3dbd5699fa50ed9a221eb9ad3ad6d95f6dbbe750c92b5f77c7dde39fd2dad6290b12fdb7fb4b938b9d21a0669179bcb2984ccd6d6af8241ae3aadc73add3bceeaf50bd2b2ef091757e610ba55bd20ed74d4b2f7ac1bb37e6917a483b6ce7f2dde8e943ee582b45da3ffe2c5fafeb2740b32aec8625cf6368dd629d582b4afed6b5a5f73cbb549b320a38bfe91f2adac9f6b140bd27eeb369f3b579f2da35790fd2d7a5dde627c0b32b5825c6bba8eafddd51cfd45ab20179bb53e66deef61a4930a9299bf76e7699db519e714246d6daec6fe4ed6e6d32805b95e83d456d74ffd7bc628481a1d5b67bd315cb1bd78866cf76c2d7ece277db52314e43f87d7adc77139641c9f2027d308fbbd06676d6b4527488793c6f7decd9fa50f9b20e743f8d8f3e89c65944226483b6765cff432537e0f2e414ef7165f7b87b335e7500992bddeeb7132d88bdb7d49909399478e70fa74bdfa2141be175d656c99df11a4d3a68b3238e9f5690469d7a2ad394bfbc6d56811648c8c3a868d3173ab3a11243386cc9e5617d7a3ed86202fadd0b6a5d3df53674d0832bae560bb752dc3176310a48ddc6bc1bf0f3a770804d996a3cc2ba476adc6dc7f20fdf662d3ef5bee6f6b1fc8fbccd66b117275372ed6037919474bb93a7b7651ca03795f73436bdde78b0d7b07b21973e5c8e2ac1e2bd581846c9b356df3bde5eb9c0319efbd8e3fb275f7d98a0309e1f467b13d7803e993ffd6cbae8dbf9e17e7f1c7cc8faf20870de474c7663fb61665d3a319b2df556be7d7e991d25703f9dec5c9fc60b3fb5cbb349090adfa1de15fe89abd33901e9788a864f0aa959196a22086310010425949150063130030302c1e8d86a3f178a2287af203140004597e6a9a422e124983c1500ca3208a82180662100008208410628cf14a116e00c03ef6a440347b8ef61d6168e8db8d0df74bf5611d510b68ad2dc4e0fd4d8a705a5d0d20b5810f42cc59fd9f4544d406b19c83a5727210ac88d8bc7a22b7f22c74413e376766d43f788a5fac2fdf055a20aaed78093242b7db8e7c7791f20bbdc6d4e1d1c0af715c1647479b78593ce764b6f93fdf909a5f5470cd7fea27029f280eab52b3137df15661e3323b8ff7015830f651a7273da49283635c46bc941e5f330b5c53695aa4c414c740ac11cc8cf434f43e3b88e4e300cbcf1b5855b61443e3b7cf3ae2f58cbe3a857309bbf341814fac97189f5c7dbbd563afe96028de3f4d559c637775ef79a3917c321f061cf708f244ab71818e38d3693729bbadf51eb454afaaa0d78a3f4d96679d2e21ac2a4724c040e4260cf2b64988d8840a4f118c18df8bb57c4b12d0f1378c3da0aaf7845d991ff3d2e20506a60f0aa374a0c8efa7fca2a0a21527897b47f660b144cda256ae37b42d3e55cd014e24343b0245cc00d7683431844a4d024634b43b04256600d928b43186524d014344b43a0635a600ad515aa79145472ba4a62af3940685bac813d562c2a15454487a9b188e64b508086641d025fc6b50cfeb6ff590124f46d0febcbdae95a0e40be251b52f6d3c1b83e9328941be548d995566b1a4bacbc4fb17900195418442a82bab0fe602070a2030b4eed4f5c941dd598cb823f387efd4d4632b82e965e5d48ddeaf1cb73606e2728a1388010a05ff86fe03848fab7cf105a2e4006f4b5c860909ce6cb43b4223b61ad1179b067e396222d21b4b2fb206a21d49117b35622ed207421dbb22a26e4e92315b2af3bd8e0d743812230636e22eb60c9c3ab2a2440bdbb494cb10a390de587a252ed413d3d1fe6ff0381dd8e5b83a72928c8a309d4fe6985bc7bfc47c0397c5f1dbe276fa2f2b5ff4b24dac5c183104e9264bedc585d4cb8a6f1d058ca38db8ce6e276d68cbd03a55b24bd0f5d2245eb95cd7fc4bcf0aad93978332a153c81bf241fd5a11d38da48bbd066e1cb1474aeaa8c08413c8ccb8a5ff1bcd97b5b4daf0a490ab1173b17d4c28bf2cb7e87f9bb272156230d209961ab214a29b2415dbcb2d9a5f38cb4feb8525bd6b510ddf74b7417f656659966b0d9b666a9e068e85db46721620ba2f4a001204182f961cef46f5396b295ef6f0725dcc37df84e951d1da2c706a31db74f290f1fde9e2a18c31dc6c1ca3fd8cd71ea7da9dbbdcddb6d970c5a861734295122e6558f85afbaa0ced3d0e494ddca948262bb01a15bd7ba69d12db681ba49b30cbdb7d19f28eb2710b7b85237b6b0e9d4bde82f1c80913ca03e9e787d9c605cec4ea6c6783853117b6c7ce70855075dd0ab7eca674514177509eea1601f155d06c868f7e9ac0a2bb4a93744b2c62e552a7df82c7c80e9410c6568f1a106f5926ebb53c43f68cca8287dd6264cde7ba3e28ed5506c217ce7c270e662b2b5907a87fe8cbf965efb3b7b4b5c7729ea531371476482df40841102bfaf61c052b0f0f36752491b2b48b4d352190a2dbc7847ac805d697dac9d4db56be954d37ef10cef6614f290e60e834e594ac79483a807ecc9167e8b4e7fc17e64d6a7197a7ffb60324d2c0f265701d05829a8c5336af89c889c2e384a5052572bc0366cc4b3f92f636866a4a0ba2bb4e41041b08b68d12286dc13911d2ed884c9befd5c8c24c76df134c28b0aefdbe99986f8071a010630bccc416981aba14c7171b16bdf4baa4b93303585488bca03c33f1fe37cec4ed5e7db1cd7835346c12ffd18a84c2146237de06cdcdc199f64778ebcd2534020bf1c675f868d73e0457b31e2ec063f3fb0a85487dd6f5fb2fd7689e767eed5744420be03cd8cc05d25fc961271803032f9c728593fcde6332a54317018b5a471ad5e0677b35288e62d90ee373d126c2e93a29f3d340ac41f68f323ae6e2f80e81b27bcb68aada9e470c4716f437ef848369151d5cf6d460e9a38c27532573379c0dfc3517c428f52d4c94ae18c3bc42254650002d2c6493ba240c163a89265f3d0fe4f44eb282f3933c701949a201f1c217b46b28d8f5f9340f6559debd6f3a3d304a6926664c1c7740d4ebe62c7e155015dcba450bd716f65abdc3fadd02458bd856f5b550c362b9c58c56695bf06b51864573ab2e5ad8b6b8d7e28755e416315ae8b650d7ea1a16d32d68b4e06db55c8b362c9c5bd868f5b6c55f0b342ca25b55475d0368f1440ef9b59d3bde22f31d0369abd85d6897557dec455c4cc6a0f8559e314a7dd11cc36957b5cb6c70c7015b75b281a61ea2d4062f8d92666d82865b3a2cbf715deb6b0cd46144d7a1ffad2adf582b639a0c6ab5f23da3b046c3335cbed52e8d6b32de6aa03d6be437b4d3509f63115927af415d07ff1a85606d26c3d91b16695c36eb171bc86dc4df0d3dbdaa93b17e8d9977d0a5959b8c626fd8908db0ce78dfdc349ff6e9ba2abdbc22ab74781919c3ce111c1ef12dfe652bec7b42cb849dc1fa7340d3d0f77f6d6337f7df82cc9804a31b7468715d4016c709787d4f16e4065c7fdb19eaa279bfa83073765a6739bf1351e54f40e22a2ed8f7640d0d477fa262f489c1ae543fa3dad1213ec7a51a841cd666717e315c756d879077f10f392723212f29cee423df9a0c0bc3faca117b65bf562bac05247f0d95d89e1da5722ffc705194fa79afadfc08b0004faca5e3497d63ae05195f38e2262756700962671bde531c050685e24b445c7a0a995f26d17990d010f188d9b926e1d70e778b0f1e1411ccbe087cbf73c6a8274169ba76a8ed86ea20f28f3d7f27e94f2ec26f5cc5086c13f48d54e9f2bb5f0e7aac40cdc85e5c5522d0d07c8f135a0d67d9ef80b87cb7c8ef882c1dfd379e4b1ca17ff286936d98fb4fcbfd43a428c3b888510f14cef3f8a1228446e62f3ebfbf801a18e91203712601f84ebdd7b4c8d53ee143988c3ba2e7cf07f0de2e122141d06a40c9bdaea3c5d667edada6624e0edd34b520922ba36ff7db796b15cd379c03b7eb0cb5923ea410a9d47b0e2f223fa4fbd6e450354e7f2504f4df5cf4e1ac89b8eb197ca51d44e6c78fe0d066e627ec8ba4549b6f0a72cd04ebbfe59cf1eebb1e294fc75bedce8f02c3c5ce6769e661f1af1366453ffaea09e3e2c3dfb8dcfbb9def19990f05124a4d3bd87cfa9959ca9249122ee17e94c077ba424750a2a93e1fbaeada94959aab5b77616f436a9c6f788db4ad2b58c1a6d0e684530461ea6ec69e43b661f939b7b3cb02792844409aee219971f726fc7e7d87c8991a18a7adac404bb843238f551c28e41c55f69e772ac4eb54eb212708e75e70f176c0addba40d31567ed46f177816bc26d2282a95b7ef80148e8d3b689cc47ce52f679a1435fca2503b85ef17607876b7f92923f06f74b9908c0cc0730b031422c631b3e7a26ad511bbebfe0a908d334737b839333df9933a4b779edcf934dc68fd7432a623579ebe5cd3188f5d1aab6bf1ce093dd198020139c92dba6a4850e0596c1f317978aa487884803257c71fb8e8bbfcb2566fc88572d71082992bff27a380da0e51382d7f438f4070dd56fa239e89f7e4888d5c9ed7685e2290dcdf23aa6a8c02fde2cd94903d347eb7033a77246c998f064b19f922b17855903e69d553d04a0f6713a78de139d6d1ceb4694ce727a5c27ea06a3249a2e2a559a6da78357142b20e0089356cc31a9083ae55380fa68403147bd78ece1123224cc9f5dbfa4b07e09f8bd21a114249896b4df49dae845a4cb3cbecef597936000d8be25cd19e5b88bef4a557c398606e21965771450f4a5aa10597598a6fd4bf570190c308bc353d738c6f7891c4f12ca7b93a81fbc00505211291fffb4fef73b01f66eaac5a1bd1756c0818ea8abdee109d333e504cfa007d240142d0039dc1dfde81437d3e7add96ce88716644aaa3e275acd46d28fd9dc73f345f6b3f66ab40446a8c05e06ddd0845d07bc27f6b7cad87d00408eae632c5022bc42de5f160def1dc433bb5fe55d7b31e80aaa249219f58926c5970b2ab480bb6f8faf43ca4d833d97708dd997e0897c4ffe2efcb8f305b23c2015a9f70502c42bc0e5242403c3c90272dba7f8fd2538604a5e6221148eba4566f9af324ef844bee428366f32b279beabe6d0ab7e9c63c7dfc2e59e9281e8305eb71f0391a8aab77a1fb73db179e58d100ad2b09be65dddae58f344dca000f71200e27b63e0e7f9563481aa3dd2143e80d118a5aca32c02782db66e6439c9cd38c4fba064de9bc0de147522bc701f2e00f04212ef34ed702e017b623e53f9e48fd659da91e073b3ae75d2861cb3b41a77770d3627fb83aa75a2e88907482fd8586f69c8acce86d35a3b515a06c59c3e1e16c5944bf4e1718d6261d46e2fec908b569dbee34e195b6d751e188d3c60822849889cde55ad4c92d74111e0ac2b65a50683789c797e0d99f3b8b9398b744d2cc17c366868104f1b9f9db742325431149ff2546b7a51514829021be68331d8dd47b31d3799476ea3020419016cdb2c37e6a0a96dd19bc7e99a554f2a2ca1227f5db47b7e749526c9d65fee40e3fa2d6c0a6311e014c430a0f3220de787861ea4478eae17993b2ed31ec8fdd1ca7884d399f2e31fc8c9fe12c6c38890fd6b190bfa1341e63dd6f167abe9986a0853e59c434b30c1582a5385524445e2d7beb7176add1b8ff3d1a26b790c03697bc14646b4429f71eb20ae4db9a8e6b2d5d05e33fdb4e71caa9d35f692746242f49b6ccf5d44df5fd1011df591a66e3b9ce474156ac66113d8332499af1a49a1bea188fed2952c70de93d1b7779edbcb4c5e51b5c3816a728bf0308616e637cef76fa6d26ce824c5930dcb0dc1782df32613e8c29dd89461967585c0073f204507682b1cfee09e7cd246e0259b780afbf3fb8e297b2df2898245b5ff37a816a148cb1c185a76798c4209ea1a6cb3bb71fedf267553e622a49767e724aa594ffe57652577ca2036b1429cc9e7a528185d4302b9dd17fb3ef69c66a59620a9ead454d36ba99a88a8b94b1ed22a5677ae3fba95d37a179571f3d4ff693c1de9a1a36bba217ac15f026aca5ec0e28188eaf2cc243f89bb9591c44da6b52e9d773d17112fd1eb4b9df655206a6b87a074a0c55fdf375e319590e62e4969c93b0e254a792ee253a25c9dcff4d506ed53961066aaa304f2b18d2b4f965137a6f668f9e2914cb8b78f8590d852023ef7afb80f030d5659ea1613b2dbecb41277da0def3deba0051b1f5e71c93259b7dedb64d844c61cedd5a8c377739bcf408afc50546cab65551eb3d8615e6f1a500fa5bad569a772507b12a557abbfef016b4e869298811cee56683b7be466088f2050cfbed4cd922dc70c3753a04dc841acbf06d11da42057d57e45f57fc304edf0d22a84a55d982864e8c754e09c40aa1fc9e134d83b965702b8a48bd9075ee7108fe36a68d18034e3f5bf1997d1bf273880807d32fcd6487bac28315d286ed401814f921de50198bc57604ef4674175d442492f921d0630d261ab364cb8057aaae69a6b334aa85d99754146b59264b6cf27be1b87cf894fa7fec72f3d4be3dba0344647c0dcd4fad9a0dae4e20a12947f02c2f33c9ee5110e8a83a04871538980d9dca4b797ecdc32a735a53528123741455be5d05e27968fdfdd2cb8f81e75d2d902be426a9ce9e718a14bf41134e6e93b4e3e0ae32dd9e1bd8cc355dc6c58095a4e90d8b3169fb0ca5cf2c76dd4b0cc5bc19b54665370d63e51191cf0c7863d68ebbc1f22335fbd4247b004a43ff3e5e398e32b2b5b00fe91668af55a94639d340bb836564cc5503e18b7423fbeb9204554b9b41987bbcd82afff1362d666b76baaa3beebd58451579bfe256c66c5291132383fe50a489c041394b6fa2501b98b8d9ad5cfdc79a3a7172817c0be07f91154af6125638cdfa772e5390a4014952440686d30e572d1242c68d4fa8e1e4c381e1c461badaeb023523944d8d49a3599240faa8fa8462137b89c17a6117f2b3a9114421b9a1df747148b22681bbe9617c0a6647584499be813f8474cb31fbcdca8572c269770c1fb721ed75c3be1d0bf36bccc99d533b007ba7f80ea2035f5c5587f28bc08da9185a7e80d62ae65e90440ea2df604b2fd2b747feb45eee25eeedff5ff7228f4823e108e7506d4b0b8a864065686a2fda4f6b056480ad750c41e436abef1444eb1201061109633e14a99f081d0df7ae3ed84a69f8ff8095191e9f35e7a936588e3bb6e518b1c8dd5520ad87c6662381c920a44b1368709cc922e0763574ad0f8a5e106b4ddf2cf90331013318ce0698922771336c3e778b6f92f8298132ed5519b82bc9354544ccc3d8413997b4e641973c296e87ace7f9d26cd58a13461fe902626f4972106d7c1d4b87e6d33ef76f8a840572bd0762a0273f4beb07e74e48e481c7d61f3c6c54e758dab93d8525966583625153c95e472d537b64b9a4f532a830b43043700aaa39c8a7f6e54bb1f3185200f12e9e24e31a827493a4bd3e44849844d0a617b77304829c986729ec4b6c0b0fd58e2703e6fe4d990328d6c30f0ca073a0c8da671939601005ce50221ba2c4a5375e525649a12d86d8d6b67dd4dae83d82f2d4e519410a4b7285480feb5919d995fe3bd95752841aad49190b4d809316bc0b658fc75b9979931c9eae19279e5eb6fdfca417fb9a9f6ea5efe4ef1a370e67c68f2e98a3695be1d099f8731f5aa5f5a14b534368a4c2b4c7b5b6b542709b4becfcafbb8e73e6ce0f7e61edaeed2dd28736ebedf03e8132c3cb8f3c917e0f95cb08c28b3c93fa4e1a468a4e967e8058ed173fff9bc1135503c50e7f6aa0b6a80cf1941a4bb1216db06d8a5d9387c8451f889420a5b9d78485512e496d262e2c170c11a5fb88abfe294daae3615c6234a3b443ce6b7ccf98abe848e7cde138095329f9bf2f591a577b1954dd07d5a278048dc3eb1a1ede406bb26b7b8def94b67fa1e91a49291c11225936b163cb38a1b24b1b2504ae35ed4afd2010d96493f0a2e8c791a596fad4dba977ade6be75e6b32d8592363b0936cd8f9e47c9e3a6addcfa17fce0df7b885d27a64dfd6d8b2ab779470afb259403799c9e50a4e2024d09b92b43ce18776002c5799925932a561ea3512179400436170d10ef6866a5f2cc22b97c81cb495ee77379ab21e4c2c4b5d150fe7c5327dd6bb1ddb9275bd95cbfbd760462a798f00a2787c4366d2942fa8c2a779441dfa3a40387e1b5831698a9b4148380d157dc25f9186455146cd767dbbf4b858576b03dae235e5e9bd54039e34f55a7e26c60b1ce8a51b4de9c0d82fc766a2d032de07011ea0732a90431bac4dd5abed3e11979e3ec6614d12041bdfbcbe16e01f370e9e2c3887385e0ea7f50cbc3cf6e4f2dad01c243cb7bc6267e100b6c01ab62b2f0f0367287fee581ccde8adf52ef81fa2a5eadebbdbc21888550bbbf25b110b3e8f17720570f2421e5ccb58b3c687f050a20eb9f853c9d23bc9e2a2c6a79994de238040d1dc7a0f266158d08a800eacef02346932bc301f712801f068d24a095192310014fb0d1723eff46c802169607eb100908fc48247546758569e9f109b1ef79c5c4a72cce6c4e8c841a88e3b278426c307c76872285f3a058df5271786c874c2afb1b2f8b1fc73bf7d72d1b341c6585f8f9fade1b6e5d6bdf442ae84a7de9d14c82a49bcaa16aa217dd2a1b625812c352cc0ad60a958382702c2f6903eba36872d22582cffec8d4885a1f7ea2a8c21083effbad3cf83d14b75e746c67a69c74fbe61d8c6be7e5455a24f92c671b98117011c1ac04dd9afb756cf440606a1ac349509f3bdc171f51fd526b06fa21d41a74176fc92029899cbdd311876cb419644facd1d5765fe24e06926bf1c2a3c5549383c287d49bb9340eafd09642634eef18504e04ddf0ccf62b06bfc0455f1c52e2978cbc105507d43c5d391fd6e60e243005886e9082a488c80f09564067f9329d546ce0b47435484e53bfb8a3010bdd463c8d4901bee7d2d21222f618e5714580eacbffe81fbef2739c0713a880e8ba7111650da432a158a6ce78f77644c1ecba2cf34915f50ce62d35dbc9103e0aede0442700e43935d1732f89e9816ddf920a10589d0502ff690d3e25663d2e84f49ed758909865c01fc44c568d0b8bba6e1f9bf59e56a146f1dcdf4e4d458e841d5836a6a57c2ae2e04f2f195093c09372dadf73ec1ef940daa4603f770d4ac65338e0f90cd03d758184879c4d17767de5108b0d4f22db99048f10f46f0d321284058a158a716c077093ca5215b380644d1d922e1145a7dddf716015dfd7499a381be17f4ab2ab9fae43d5db30973aaafcc5223883940d0d3b0a7833903c90904293c4b7b88c7e6a8fd734f5428f0a29c4648c418a6e0357f2ee9c883daa3135e28eca0a75e06b18b51f8081c452114027f1b64006c59047e96c200505405f048f82d108130e593f0190a0241d0178123e0b64200c7a9f2fb40a87ccf1755ebcbb25c509d595394aaae116e3932d53b538c88877f023f9863bf09f61898f179bc670cc5efec43c1f319c1ec5d8d737aac1a2286c1787242140145936f5397db79dc31f8626dfedff3101993725363c3246bbb538d2c454bd0a25cdb69cffb77f6130391dc204e6d7f6fd2bd7aaf37f649bcb48f6ebfb5533becf4bef2e5e4530a49a088f88fb1581d153fb0200621229d9f3b6f87b6d007c589f3e4b8241b5549fdeca443fbf6fdb5f3ca34f45abf8368f47e269ce28e1091d392f91a755e647abcedaf931fc270206837028b605727b0649f35f92ff79edfef7f31971628892ded4412ffe68702577d8c0a008cca2b21dbbdf19ab264a423fe5105c80880bfefb1b04635a45ceed05040a342edb74ab7298c817ec3a69fce4c638ef407b43a4c022839aa1542ef34c57aaa6f86ed755e9199adc08ec0cca900b9f9055148b23d931eb70a9594e72389e1e639be4641befb49b6283b53a6fa2b48a27bbc90dce3cb532319a6fbfbb9c132e1ed0e52ebd1d627472098d895c0fa10cebf8d7f3a3911e0e5def01884ceed6d2bd854ab07808e83942d3b37b0feeaef0f36f0a87abde0620696634cbc1466917b0d2ed67e70bfe1e5e0512cfcb27c80e8d7537cda2a9d93649fa61e2fc705df0b6ad3f520bd8faa12140f6b52398b8b0c503f11a6eda3977e60cbcbb8fa55b389668d5f6e258d8123608a591d9cd52dc24a7f7f8debf662dfe958de04526bd4a9c8c37b8c44b1b7ca7bbfff6357afdf6edf047f13988dbc4cb4641bb9003658e1c763c8fd206b96821d1281d2fee0be074c3b4b56a48850e09a8eb9c165631d805d85f6d238b847a676428dc9b04b3b742021d7dd890972be0a32abbc913558a4f417fbfcfb396dd189dde5623fecf9af4dda686518cbd2db55081fe335ad8081db398c83421cdcf230ffd96721e7d8d921092d617cac5a8bf4dc3380c0d83ae6f739318fe3936f4f021b45bf1a112c9167a48a04079b8422ab4e70bece405eed0cc0626fc4bb1e5a1cfedd31c74abfc41b64ec5b6a9419a002fd88959e72a605ed59206ae936b79ec0a9743f981e128e05f709a83521d048c3fc2fd7f9607042476c06ef5fabc2332c583f68de3c4bd884da90996ff53012dbd7e66ed4ae4ca24c7e5461b4221fc0e77752678ee914ec852658452bac7c7320bcd5c11f32b09bdcfa49f714cacb9a2076eaed96c46b1b95806c65e154516a08832d769fea12ed0504340d1f8799d77757b00f3a568fc137fd06346bde0d06ae1f50856b6c0d79c1ae930ae85c9b9ce4d3d83f6bed66187a6a6703346b27bfd6bbcb0daa3801ea1cec5af7b85a76c7ba94a3752ff3d6083e5f4ced826068453092c869d74847ed76ff96f6b8258b2f752d1041431af1d6847f51ad9911ced22d2c5ba38aa970f63c7bc28aa647caacf1c0db3a612a35d5fa9b74efc917b9cd8d727ecda576f0fcd0f92fe45e0be1fbcdeebf68f460d09511647b6448a2a791fe7dd2a392defcb3d7e461f68b0b42ad999d645549e2411ffba6f120fbac7af9c24e72d818e96a8884a75b72eff36d0e081ae6b030de0c3037d1fa22e8a99b29616cc7924b0319346d756f3ce9eb78f62d9fb0a6319845546cff6f395d16e19e0f370241eabb1cc453170fdeafbe019f0130e28814890900510e15cc9d3bf124b28bd4eb0018a8aad49acc320cf08d58f7d32f486ca56ffaa2c83b4b394cd1d5d9e39cb303ea2de75b2a3f5c7e3261a345f2a3919d2ebb0e1a873e9a76d568bcf8eee7649b5973acf71bfea9e1be04c6e36f387bdbcfb65e5efe2d1a73c70e79c9569724cc3fcbd7ced46ca039b2140a46307f86a172b558eac56da7922d72378db41c14ba8b0d02f70c436c623a01828d724342d000c5d6dc9e70f40013035e4d1c9a5559ef39bea42c5cdc001c6e735390bb9370ec05e99fd364b6b5068be035e66edbaa08b38db961fa35dbd2ab754f1f8dae74c8d07cd36520447eb6d52e78981a22dde389761af78e72d7bfa7ede68cc5cd4f175b5b6817119e9db4c71f2d4298bcd5223f37d85d72e5dedd8466ce5c0679e85ec930bc8da509be3654062107f0dbd5789fed93a1e2ff2398ad48317786135b7b10d3b8081bf62b3bf4e26f338bc7a38b8727e083d4ab3839bf8427135efd0d82895bbc9f9e5397b117beabcbdd403fc0c9f13006e2a699819352b73a9627ece9df07df6b4e93e8f8a3bb9ed5baff4915f9987dacf7a409549f77ba0f8e58b5354921994b0bf061a32ed41e3dfcf4d534caac4356eccae07c98a338af9245ae7e1addd5dd6c0c534431875f09cb334c344966eb19066d6b150b8198f40a6d66afe827fcf8cef3f3c3b2f3a31c992e11fc1fab3f4d802e4071ff7f1dda64cf41867347a4d25d98fd5557a34bfd07b77efd1796d92ffbf28bcdfbd3b8ebb38ae393879a8c44d60b8de1095c7462fdf67077bacc80117e78ad2c4cc86081f24fcfd1238d23d1d018e194e50f287ed4bcbc51aa836cff51729b9a019fba6ebdf979dfb7656ac33a5ebf12f9524af9d68ee00f30a1500ab688edcf2e495afaf7504a8e340e5bd8f9de33878933691f04c72feced32bbe92e301d7550b2217708cffbbe632094beb5a9bf23cfdbf5aff71d9d8556ecfefbd326ded2d331e1ad67f25f966e604c89445dd307500817c9c5f9d5fab86d44fa7ed2737c43d3782e759da40ad9e8418cbcfe50cf36ce4de3e479a4ac5e23c690b73b69f0d27c5983ba79f37d4d97ac3dcca5ed0dfcf65f11b6a7862decce17ed67689dda38a4df71563a9329991b1db0887b720a30f28a74ece4e1a96ccac07988f462854e67f9380c242ef09a8efe95a6be4c02706f8a767765b67a7bad4e8bb479482e163264db6521dbaf70ed6bff9820e3c1f850a06e9b37b2eb3ef95956ca3677c0c84ff827c61cd492197eb3d2e0f8910ffc73639e9fcc2978aacdbf453d307f5788445cd48e339a0bcd69e4693bbb078598b3370fc9a99ebcf8217dcbaf1e6df993c58d2450c4747bc637db8d7d6fda1a3beccc7b28ebc28e5638da8f94a997f6aae02aee6953dac94fe83a89800394ca57f00c68c63dadfc0476428cd7ae79ae796f362a21dacbeafe232d827ea9b47b8d88cb18f9ce661148c2dcb03e7aa80730bbf4b12c9b38e77db696593c1b9916ef633bd1bf164a9e70353d5d73bf6d4ba405b24700917aaa4076f11d10e9ef21268077620b0c9fd4075feb4d4c298bd85913b18d854e909371b241a2b4cb7b6bade9fb7fbc8dcb67f631955134197d30f13b7a8fae12b7307426f55ffe6c477d4bf90aa352da52402608ad4e91f14d414b0fda2cb9567643c8f6994a6a0d1704e7bbb38e9007cb4c52748e7b575960bd572b1cd338a900d8dc2f4b95a2029c99579767e666e17b98f2ae3e438f99c46a420404039b0d0cc2f151fd5359a1ce6290169a330b9f5c73b7fc705ec9831fe2b949869b74ec5e83adc3101a7a6886a72fe89a748eee48d4f270dd00f1e982fbe3da37c00062b8bccfef710dc6a81764d66f5d05906166d02da05b041e7fd4e890bd5fb63d717f5e03f6f8271f8e32fa46e753c073c878a0fc3d2041f4b1233e3f077e6e22b99265becd40641939794df96b46c6c78810fc45949b6054245f53ca2f71e2dc14f97b32007710852f07930f0b2cd3403c85cfb83e58e073719f05728cedd3bb2b3d194f96d3f0bdaac5fc67f3d782be3c84c7fba177c6ca61f307addeaf3931510fc45d31b9504ecb6baa7c2dcfe637eebd6d6203863f3fe95d91a5010dae7950ff0b6c14675dd77f8e2bd9b37f727af36be013e00dfcec11044c80db44288f783507e69878ce26e0a9c6e29ad5c8e0a181ee9ed023824f12035680596bb80758461dd835597f05605ef5756d2edfb8f1ede05351ffbc2752e1f3b016ad321a362abcd28e251fcbb663d246d1402f1e10990faf604733ddf1ecae3a66649f9dd3a1b9ffbd132d4bb1af11ccc174106ba9df3dff558c03489ec2446776e2e52190492e3677fbc865a4dec930d5a14d185ab7bc0284581a4b32fae82e1b384cce3317d7e8409fcc7201be49c10488e2288694d029070641f57a0f8142b559a5622366c4f91f8f41f315b905711d18a3fb5b310107d5d000dcd57a641e325a7aaa283301becbaee001fd693f2626aed0ddd517d39048fc4abb656841ed2e669be012e160271bb44479cd8dfc83ab08427e845060c849ad94db01bd5156a8de42c76f236cf1920e56b8f7798c61d1fef9a4588147b4dad0d3ddff7afc5bb02f4157ca808cfff53b0dd86a760db56cac048ee18e3b6d0887aec975dcf9c1aed39ba265ed82ae74101403e46784fc96f9a7af9444a96897f7a73e2d8718abfd497ec01215e841d0055022443f0d9ad14a05e097e12380a508e89c760496aa059c03ef8104419aa3f0292f801b06e1fe6c81832d45054316c108901d5428463b35885f8a87009726a1b3da1078ea367016ac071a0c6b8ec45154b369f48538087e65089ea88a4147eed153813ae0dca8722812429f1ac56f4503e04bf3d041650468761b3a8b56ab3bec12c0960d73d3631b9cb6c664d30b1b886b0d58535a0d22d42a2890e4319a7e519017c1c320f662000729470184aac04f835c000b8bd09f0518a0141500be123e0562210c7c927e0640a012f420f00ab85408c571e0097a9900c0cba003402be2d02329c68025ea6107c2780a84746a541044e419b9839fe63a538285e0e1ce0686965d0606c5e0fa05f9317632c0ec2a7f75871c9b7ff2569e640323df9a8af16a107e6382da6bfc5e59533c6f14cf03a726b0ff07358e2aa814a3fc686a78aef0fcaed790115fa9eb3264d024e82abd80d0f7a74acc55c46a8cdb3c593cf95fd08a08b6a659bf02883543418f71d2607bae5fed5207952f5efb13ee73cf597cb8c58306bb16d4c7f59d25c23ced72a6bd04b0ed87e2534233e2aaacdb04e9c0c77ff20235d952b1d9e69c8c7abbcad49ee595ead1070fbc4a5938bc1bb1b4ed431185dcd7b92dfb8d04d4fd0ebfc0aff2f17faee4309abdd8b1e6f2a995208112a5ba83d4fda2cd1491bc9e199bdca4223522bb30dbe198844c06cac52414a4b7f46c8bb4e74c9d4fc90483b53c348ffcf9ed89ff800ac23b2957a711031638dd9b21751184fb3b7c3924499647a200cf88888c0ecda7d14584a7eb5dbc02d64bcfc80eaa3d826c087a67e6c4885939f74f23cd37808172a3ae9b79134c040da756d6da5372617d339c8b5fcece04e71cd32cf605d7cb84fd98a3f5e10c6c844207c88f2ed27e838d0d243af92d9e7aae63909863d5493ce0e0ef45560a0eb9ae0243cbc74da16d171fdd1bee46a23f07c300cb726c60dfc23e271b623f185f69049fcd4921e50defe37c4ca777bccc8174fe80797015468743fa65c57f7937ed54214694d69028287e7c7552e976a23bd5c7c46f7f52d1b3473333e136048abd60f26d47fb65d7ed16814a7eeec98e44274b5229637ab96295fe0a579da178f26631d94cb012e0e1fa37facc309a51be243292a89d42412680d2329ec4b901747be18fbbb520f9a4f6e6c181881ddc5bd3f773658c9a160475b9b520a8cc5fc6c470c55e364bfe6263a07d44c8a7ed0a045456992116ba411c0caca952377d9b58b27d6758c597cc89ee5835f0848af387dbc8de13765e2dadce1fc03fec394a6d5b747e8040e08010ecb31ca4db31c7d985e7d054823549a65cee90d3d9afd64ff61b97eb7c079fc453a55c4d97379bc2bea3b92c6496a265b012a3d3f35ab0fb09ba21d3422896065612374eaa805ccc417c6c2f8673b9c33c8b5717b042d9e0db14b067ca06c4ad2f42c00ca7ee9faf87c7b6ec446f8243a85f218f4fbf77410c8d741d3b0463046684fb8e1af3991fa593d1693ac60d1ad5ab4e6b0982a20abb6796f9acd62ddc323d76d24f03bd37cec28fbda6430494361afd74eb3dac0c9bb227509b51b0cd5e4a4303844c641e091976e1bdf707114ecfcf919b0688038a73a1cfa39c050a4a4fe8c1403fefde5d778714353f6dc412f727c54a2c4a9180944588883af17b55f6644e906f7c5de55da56464604c647175a2e28a290ce1b284090410b8f990650c1da1771adf39d831f2cede93121b745fcb95b8a20d428103ff73d7578cd646f65c869b86b44f0b2be734b387fcb464ba2c3fc7b59f8cb0eff2c6e92e05f6cf5dcb173df2c5084ee3ca5606abf07222b057178d722d3fbe2e4f20e277b6f9fb3b4de253680b48adb05a8bde23242d1270b66989e17f365d2d3cbf7c22a1b11b52d023f1ea4119fb2aeefebe5bf7b7d949e8abd0e57048dee8979fa8f9a923689f5399e2691e91dfda84737a3957358218f174566e5a07857b28656c36f624bf23d917ecdb60b68fa859d0b0fcaf511b0deb591e137131542ccb16436dbbf1a3aacd7008e7237e8d58f9d026932331668169d0ddf7ab3a9c8d73c1fd305e7513bc7f12f2182f9b58bfdc36a56c6a6679f3ad8875b493b2c5bec3aa380788a6d35c4ce754139e74f2627efcba8b838ac86085f3ef65b12df9145656a71bef88ecca6ee97a02b79e2691e0e9036be9d6a3b10441186b426bd7338ce79e806733087a7b8f37e22bfb3247ba09399f6d696c85bea41b139e723c22f918fab56ac96075961f4b787ac1d0a70bee3736d882b95246ed29075db870b758b8e07e7e87e6c4852e3d9b45ead8383c59a08ac2851b403557dbc3b2237556e211e8f50affd7bd30c6203da89b9f022e4b267c037bbd31ca665aed9eb1ac7dc62769928695cf1556b17d16ed57ecf342bd483fff7f4e80db65973fda918844498c6fd469154941a01bb734d950e6b590129eb34626b0e3b2f6079ab3c41d6ce99f330d782e76499e23e6fdc18535eb0889759789ddd6b3a7af0a74a96a5fb37bd9a3c6c040ab2441f66a4d0ed1f14d1dedfc59af3f68d8fc3c98596a859033d7bd3c167e4313fb2d12dfbcc7067cd2308f02e73bbed98e412059b129f5a73fc6f8785c1708654789d09d28b46c747021d65f592833b9a7768bf3e2df0a397128f9f090b3011f5b5eedf548c04acca020192319d4c3f77d95bfd5f82ef1b2f96b37471c2fa0dc4a5a7c1cfa211e378e223b495d0f9cab91d85a2614bd1d6614dee8a37d496ab68538f788d73d28c9cc3cb1eed4a5b76b0f335c9fca8e68f37ea8d85783e7a935248f05bea224f1522797347a3ce77445a3434d5dde8e119f4fda400cbb37d91e6fd6bdbb6cbd441afaa21c1149b5fd04fedc83bcd6caf7b9421462761a29187c126ec80c68bf04de83339e9ab521fa28750d5bd8add669ba83d2fdec9a1e0cd0823f263e7fa25a3ae301731c4aa98bdea0d95bd4eae0e0630b73f315007b71ef4ef344d7ad62ff171abaaef55dfc82abf1d53f136cb859108cfab933036d3372c1e5b90d022737efe30c27f2e1c833fa655ee2ad5fa0cbfcf895e60dab807e3fcd3861b0467e3fb6f007658af778aa47a66a5b18ef77897989756c0c99461874aea86bce804951f47f86cc8e33da5f59f5565806f18066aff4ceeeccadb52e530849838132516863e7c44c6138f4f428cefd85952202805b7df9cff2fc7a9d502ffa304763b8ff578efc5ab580dc98b4b99fe5f090ec1c58983e340075253120916f10e6e496142b1f4d6627434094f714a40fcf3f8524de0ad21584b982bc3d74b3982e3c0647d59b471906934a7820f8e65a48214948b4b3f818b5af0b451b610cfac76ca0c2d301aa7eb92000a894793a057eb8020883764718075e0e2761cb025eb0b91f5387eb73c44c800038c182604cc096949dd392206804592ece1bf5ce6f195d83aeff758142cb7a8d19bccc43b4366ab5834638b1db7c2ba783db2722bc811b1ca4c61d0b14deb70fc48d8ab896aa50a18581f37c38dd83254d6510694daa145052c84b78a53020b2117168deaf9663b72d18e6adc5306662f1005a338e0a04020598bdf848894e1687f356fad082532d63e4fda3f5c796c836a1f2441b498767a52cae94b92207349714b4a7d3693e8b6f0bf7fab665db7ddb0fdab158c4141a7781c8a4c672f57838789925b812de7aee0f053ca88232ad582e98af92bac36f3943110cf07f96c288d3df98d8c515a29777ecc10c7aa2b976b2490df83365a5ddbf1994991e9ce856958325024411c9f7f78d02b50bfd9e73038af47bb1480d10c2729cea20cf4df1979f3535f3ba04f0b63c352d62230ee4e14c7032078026815295d3f8014b0bd366f2bbf5f91101fb2388f7217b072e05b41f270739d5bef78bb8a304265a9f32b153feb9dbf16be6046a15c917445ee7eb310b12b2871e156e0fc5d0c9c26b6e571caf9618b819e42c33a82b583fbb44c150ff333737b1439479f12b51352983688060b7c29cc260962432318a955ef92b2e811b2e89f6e75f20e1708b2022414f8aa5995ea3a15b13c25c6d4fe327aa2654b2798670ea8752c07fb5a47c6592fc2bf5a9a52487afcf7a2051d3bd53ff175527a0015553335aca6c03c55c177d9f94c88dbcfd3efee47bee84d36bb7c282354c7c0f84716084b9e754c3ebee02f4d027df4b08131311d05504f5c426d961a24e72e3b8bc0d717f6c3dd9a3991bfa53f4f2c448a18701cef19701eef285d252a85edbdb9b502b196ea77c5a95567de216097a34ceae75aef3c1360a84a8244381cdf0edfa73b0fc76958cbc2394c54c11417bf479dd86577dd4a8cf4bb52848bb9997263cca7bc9dd599ccee7d0aec1adfad55aae38cd38c5eb40dec8ae2518f48851129d3d7e38bd9d767b43158c59e3bdceec2fb327857ca6780227ff3937d393272989a76592999db2ac03af9261a0ae45b6627872a0e528b6ce6921c0e974678d446887821ae38a8798bcba3706348f6f0ab780b6c77f50f2dfb0ffdd2874a30b7a3ea6cfe38b75105bb34a7fbf39b482e9fb1eeeb700bd1a9316f6acdcd3ebaf6349460924e130856920974aa020d9c6295f11a6b1ed8dedfade8653233aebe4e2c0399aeef49169b6da154cc2471694a613ef60bf7cfbfd636dd1da0b5bb05b259715543710af6126a5242a6f7d4d7e324887a08578eaea5c8938cab7c5e3e4317122e7d08aade36614c01a1f5d36a54ba51abb90a433add9247384874e7d2e001d86c6600b85b2343b0f690e310b825873f91141921d9ca266afe4c63a244023150a57accce96947b3bcd3ac39125d4ee472e75bc831becc3c7c651aba627976598f72bb75834ab83ec878012c8355abe7ad876a2ef96c630fad84c96f710c1ae2f59613d3c52d38f054667c0d2a9259ba576ca828c9fd632178973386afb6e845c8a2e1e3194de0af1a16a4b04349c9d154a4ca39a3f8d8c4f138249e41d77179a99da09f9e4a73b64041ac16073472fdfa89d1ce7c15946595f0db3f9972484f290a9a91fdffbc3c114eeea4106dd91b89bb5f5f99d01323385ed83d1b5693733a5140b06d832977036e2d03721c7002425845bdff156c3f519d064e1df2873fe72a8375a50c6d5bf290249fa7a4dea6b840f0a6fd5a8536ab3238eea474bf74ced4771158a8c7ba1f32401cb8481bb0fe5d259a16abffea8f8ff1d15b0542122741c32088b7a705e96178daf28379ee9bc3df3ebf3e624962493f6750cf6e82009636c24f15cfdaa8620dbd945875664ce01688cbf318c2500003e9246495a6cbe91a0e0a41399b4d9d6c26509ace35442075ccf188d29dae494da195ae388e3a6c8bb29a1dba574a1130845468714d97f91b879ee28d2768960740bd49d43c94b0c002e93a52e3d07e0a447b26193ebffe2a10ebe0cd518d0221a2b118b3f6c170b0f185780880d7a2f324ae4e207539dbeb167b6b840281de93191ddc5d9afd9ea50838d8c3c64671047c4fad289d5b887889ab42e50731d16cdc1e630742bb263bffa6a56cd445766b26d1f7640f296a4bbac8404307cca0176f0a51f0d8983a0a7560f38095ae39b4f588d0de5ab01f419454c424064854ca8845cd36464300e277180bc74933ff8b15e0d4d47344e9dac182d686a1b4390bbb68b10ad57394f83cd480c4c50510e3c9038f56bd4d061e82b60681ee5fcf9a1c722a4e39c7c01d7ace184b9f2c38c8bfd5bd99682ffe893650c6c0f83178d0064a1bc2ee449e3ef2e71490b473eb80ec1efb95d5c3ccbf35fc755932528351aa6c50b38c9ae5eb58e8fa009b47be9279b5c3fd1bad68b95960841197d52ae062e0c4c865701f26f59ffb419571fc3c056cc86dc3607e4caf2406fd5154c064118a3482dca517d7dc6f6aca2acbeca999a3e5ad0df9cde74b1e9e728791ac3180d9fbe230c5ce73ebc309d457532e1d519a20b491f3c8282cfe2ca6765117c4c92de8c08ff9fd11abd2e5e1b186dcec0372c8ef3d1542a7cfc6a5344b15a56d13b06fbbcf2a8f9d416c98ddfe04d96a5d9f90d8c8e9e575d51b772302c41b3b16b9bc2ee9f198863f8f5ba4cd7603135dbc961cebfb34b473ed4a367666adf2b07e44ab1fcb10d4217441fbabea1eb428bc7045f6f1ea9e8aff9db94971de26ff960e4226aa964e21b8804d5536cdb05ba46acf8689183624aa0ef0465c2b963d1ee809379cbfd6a98831af3dd9d6629e3de56a084277f132b67c17717f385cda1566f9dd9a1daa4def780d35d5827dc0f6ac2639ab78f7f08a480cf18bef0e69e34ffe825884ea2a015fa5c5ad34d1f623f0822e9a15450cc367dd8078bd75b71002969a06734dbc47bf613a4c744006e8aa37e01eae1070041b5a119fc7e0d822e04660b21a16af8c077c59ec97d98f75350bcdbeaa548c3006da4ef08918ab26f16e1f8bb5bb88638a899514e0009573196ffbf510dd0981f02c61c569aafba7f025602aaaf5cbe2c517cef47a219feb00eb44119ba8b2df1109b655f1eeba3c4e84b80acbb2c7b5df040c303f311758e22928fa2ade189f1eb232d4940ac1361d17645ddee7fd893a1095d866e0d461201c08d3562b32ec556b2bbc2fb01db08c671fd1ff9a64d33d082889a0ad53af4d7ac0f02e01fc703a1baa7145e2e9123f903c5a185fe094233cf3bf18521960b11932b412bde418e556843bab311212ae173b6f0c1e11e2192d86b89ebe484df2f7c56215151f96afee4eef2eb1f225af644dc578821727cb4f28e19046c0a0d1be5c1bfa490fccb8ae8478d2bc424df6d34d6dc9ed8387d41702720df1c382c0d44bc3fd423cd4be54281e88a6da4192870c43c1f2eb7fa711fa4442a66543ac4f81e90483fc3b1bc5351c11557552732ebb7ef0f4022ac4d17ad720b3975029daf394684242fe4a03e94421608fecd0e51a0ecaa5d6e82e08485352503305ac470adc8bc90cae9820008abd270a8368871721f487a6c518603b7a5adf1d1111b2fb34196d35286003d19dd652f979e137d79ac4932d541ab2a0fc05855515bf0a132f6d5b1a3d2a21bdba1e6732c65f9d5304853839942e90184c5993db5c4a4a22a86cd323a89becb26a4a6de39b09ee77b798957db5a105c1212514d3470fe412d64979222d99c49e6114b1b7bbcf2a2531f5cded156292d56923e7e673d475040bdfc54c4b6b95bbeff75c686dc8f57d2cb7d496686e896635a38627e83c8de3abc2787f0b4e801ffbfbcf33628e816baed975bf2c60987ea3c6babc4c723c0f9c8cb91514deee9e6947246f573e4c7d773901e0ef79f91796abd65e0d5e732a3dff06a842fb4d2c9d8b8a7629190936c3fcf779fcf36f1451d55ca24194ce40fb5472cfb10cd51c69dd9a17dbb6ea4e1ed34f668e20ddb50a1142bfe01bba81df274a3855aaac96101f9ca0ddff95549483c6ff9abf6960f94b206c1caca4d548acd3f97588806bbf432f73147ea954bbd07f457292bca089de8040a0e69277e07e69a1cc9af124c1ec0318372cb8045ec7a52953c346eb8728acb1e2117f7c85401f322834281cfb982f24af2431b447f5f27ee579185f6e7c6e34579bb27d13f53b894f9fd9933ed8d1318bb06ccda1221af2236f9ef573e7ad68d6309f988a028ff863c275386c425812a24e18a80b07df91b9a316228a92e2da39a3e9f48213cadf61dc25647a16a2141a996a69d755cc458c321aba0c748f666112400d473d4c11617e70765e82832b0869b98528a33ef8ace312d96f3f4d25be8f2c1c66d07dbebfede4169174be0153cbfd9568eaac4f9ba065095786a87e5004f07fc71c085e2f5740698113dcef8f07738ebfed0dd736530d3931dccadcb8b772f68be37aac959a3ce9dff6180941f5ce55665c13324a037185720764f3737eb0030294b91ce519c05f3467e76f8164cb9dbd287e034b6b012f93816d221ad8f9cd68a03c2e3a8ca14bd40d79bdb1842e52e7cd094c7f329efe79e02981def22f01d1117b0650b0e04a2be79ff942c6bc751309fe40f7b7fbade6585f772c617e3c890c2ec07f980b16027a68b46c926bc8f205613c4f1acd35dc9158a4a6b136f745cda0901ea9624cfec022b8f034c55ce20a3b1a3badaefdd5e2602731221807a2ef05c68dfca3c88caeaeced8f973ea59828fc9b50b997d27caab83936ffac0de02e87b25b9e1133f12a90341c7df3917d21fa7ad18eb89f7b300c75f10fd712e6fb217bf36d3be4d8cab614ea644c026ec29e7f087a1e93e19bfd371fc035c519289a1004ba914541b5ff63d786680f7da09139a6397226c705bc6279239afba6c7f53a6416556abe8680eefddefe3795158054039e9c1095d23da86022bc49b07d2abfd97a94574d6626026768b21148068095d7669e8031e242a81582f8597aaf31325312bc2b8c975020743d0e22c9c47846e3c42d65d4bc26580d1ce392f629568d11d74456aa8609ddb3af0d9f72be147300a636c97b0aff027282e449361fb3c3d5f8f26978d09eaddcfd37327684eedda2970b8bc13608a547a473fefa09a2b242cfe106b75ce3c64e6c34cf1c6ca183c19e4fb92e146f50496a4764c443b8e7b2365b7a912454b50b02015d7244a0f66c5bbaa9a50377524a0b11e4a349f3720c2f4ad2e64a749d66aa9ba0eda7194e4bcd391ebc9977855fe2c9cdf41bc630180861a18f308727dcbd0d39b976d37a1dfdf904f77a6a40ed06dcd94738374389027d9018b1943c5b713325dc8b5de53e34db7cf819a55ea2fa52b11b05b2f06f31084fc2a47a1f72d6098df6e66181ceb8d40135120f055587a63e793dc731c3439b83d9ad34d6c234c11b5e368983e17679ed558a5ea7a3ad33d5b9ebe300778ef8b8f578f6e30d7363c82cd9f03e1d780087c4c182d72e0319acec822f2de2a32f24733f57413f6d2e693c7de3ded77e48fed03176d03e7629c68662b9410a68580f98cd4c0ade2e5d113f8d72379e78ac6038525b81ef549b934f85e4714425e6990805ea4f011f7e62088d86b11de93ae990135dfda335ba4d6899d2cecc2c7cd8497f5226b3c59af0264c537de2ee237fdaa594788747be37d35c4755475ddb9647ceb63b2066cd4ffd3500d1c8e023e5a04220b3760b46439330f0f0f0f0f0f0f0f6f4337426a6b9f908494a454aa976679010b644a29c99492d81dbc0b9c99f87466e2d31546ea6e34e301030b400bd30a89305b754ea153cc4f17311a1a68000d2d6854c0c3c3c3c38bbc0c801a6220c214d28388d6a39e7fa625c4388441e5edce5e2284ea63058d011b39ba7880214c3a9430d9298b440b63214c21c78b914df249fe104218b465c813f91f15620cc21871e5d6434b9e902208e3440b19a91e545479a2102310264f59952647c8881a157284188030a5e777f438191b6b016084187f30968e74f988efb9a75363edc3c647c12d4b31fc6076b7083243e465059386161e1e5588d1077357fef6f47137b6f3c1246eb35ebf9237ddefa26b68c06fd898c0923d186de28afa0e1d3d18e5524823aa3a7f3a711ecc5f174b955267a972120f8697a4b4aca5548086062440e3430b1ced12c891000b2851438c3b986ad27c74ad38f91b362002e50a31ec60be0c7df524667bb5af83792dced6c4fa09da6e3a986e5baf64752ef5e139183ce54ccb19a24a29530e26a13e57b8a89e9519d6588b83f14a456cb56ec2292538184ef6d8c9d0918488a637e8337975ebd52f23c70d068ba6bba5847e25f3b4c1782a629b087a7ad5c26c3089f59c82129d3c8851d7b09af89d69d3fbfb5d0dc6d1f62762470e252d4d83316757528ea8f360123418f542e3924e6a44fcc81990e79e7592d8f0dd88194c9fafebc329b7951896c134e23e23f4d998fa6430bd560e39a29ab0a4d9180cf2246987284292490cc69ff9d4ad94264cbe6aac1962848131b7d9ae20536c3430587a3cfe09791771fc4272a23de2880929cfc70bac96e87069d927e98241e9d349674cdc0575e242a272ac3d2f919152bd05decf52e447e9f4c1d58229e72454dae4f24ad5c982c94212972fe88d05632491b3d1174a4551b942fa4b5e9f1a1d4f3c620593aafcdb1dea296787ab7067cb13dbd46a6255a86052e6de267ddd4444740c31a660c48e14d192429784670d0f69267f4761d7125365924b5ff250307b2425df15c2ab3e9e438c2798cebc4dc6e52427182d8f7c87f2fc155d9b9078710fa1cc4c2618c4b6044942924b583ca565c7d2217fca9460b6317defb0d57f214930f68dca496adb87f00e09c6f8ddf3aaba54b61ec1d319b135ce74881ac1b8af27c1be6e2c9f72885104e34a34a14f45869e1e11c1f41971d72dd4ef538660505921e5e715e517662198aeb346eaa80ad2471b0483e855f79c4a6dafac4030999950b9206db573f20786f7efec4e7af744f6d00d317c9068a6965ddd0373b6c41c939397fb82789007af3e53a24f9c24edc024277c56ba206f92b6bbd8a26f241d982aabd675f47091d29203c385e7eb087bed220eec0992b642527cd9788353fc38ba446f820ef2183630ba6e8b6e0fdb298aceb521460d887f49f4d7280f950662d0c02ce6e13cae598ae4a1b121c60c8c5e3aa5a81246eeb8be8618323069cefde76f856889c5ba9f53d8cfb8d2aec1c254276ef73aebe597e91546fdb4edca31b57923572073ca9369bf4a2b0ce7a3d4bdc8c6fdafac30dba7baec905ff2bd5d85c1e444e5e6fcc83cb9c69aab1960a8a2ce761792852429684f2accf196653997fa4d1f15261d523855d7317e3a790a5418cd4ea2744c5863ad057e820fbe1b3672743185b13b6591f11c23966534b4a0d1821c5ea0c07451011a5ab4e0867f71011a5ad0f02fb8680ed0d082c69db5c02340430b1a76c39d8bf3850d2df3a251f05186c3bd68418e2c85414f7a0f8b2426297b5218d56cd4a8f4f1f6b98fc26c71d2cf88c5eed35714c6d1e965fdc36fe49ba130cfbc8f288f7b8b2c0185617f3b4be7fe2cefdf270c2e4946d0155a3ea25d066078c2a04694e5ed59d7fabf138614f22c56a8958afbd4d80d745181b3829c30870d911e72e6dfb29cb809e39c271d99a142de4713c60a7af1223444ae8533611049c5fbe69e4bae0f26cc79c5de7b74ec10a2a55cc214b45c4d9f92aa22644b98d46595136182ead866f2004625d491cfcfbb1682caa2008312e6a0ad2797ee5b93e1f8c8f3008c49186553c4055d59e46449560086244c1529ec8e55d808ebdcb8617608302261aaca9ef8b521b2eef187106040c21ce36e73278b47109700c6234c6d415f0e42bba9578405301c61d8b7aa2ecfb7b0145463edc373d8e8e24e05301a81d0191e3e6444790306238c713f62d1e43efdeb35d63e10522f602cc2544ae26e9b84a4425a6aacdd5084a954764cf86c3fe67101231126b566592e665c46b4cb4c000311066d912da851fe41a4d3210c7f61db43caa5b9681bc29ce467e498a51121a51026e5392e9f449810061d134a790ae141982508f5385721447a2c087329ebef2f5526e60f8439ce6ea464656af2640161b69446be8d8a21e4db3f987d62593c6bedec52fac164f7df69fb631f4c29ee6e65adff50b3f1c16049eb6666df8331458ed029b2249f5735d672b4e0468eb5ced10bf0e223c905400c30f460bcb4f1faa542ce53aac6da471d0d0d34600b30f2600a6982a4b82e5539b45c808107f3e81e93b318dec1a4646858588a4e96658db5b230ec608e26579b3ee26bfe5263ed830d07a30047af200130ea60103bc23f8556eef5c3737c617e18061dcca924a54fd2f75ba489e760d64939b722bf65cfa68f3b030c399844d8a9a0ff63df851e0783a58689a7541bfe4170308e507955fa6358f5fd06931033ea6bb47583f1d37376b8d5aff8691b0c1adace3e87f8b922369882fa4cf652daf4e49035183f2d98fb051d6f92d460887339fc7327b17f8d8b56810d143806baa8000d2d380dc6d02222d553f5ee99ae00030da60cfbf57af7703eca3318b7be26ee8752224c806106f359678b37eea9457b30ca60f2cad24946844f8da700830c067517ed83b4fe1ba53406e35ad871cbd7e3b29be50186188c5a5b37bbb67d9d3d6130a7883fda42ae99f6d100030ca6b3b00b914275ea60d6ad01c6174c9d5444af9b8100c30b86f43b97bffd9e7459140230ba60ead4157ac7cc264b8ad987a11c6070c16cdd162752f27a1bf9ecc3181030b6609049317a1e52d809920f3336c0d08229291d26ce8fc9979033fb30b5d2038c2c98f7e2f3558a8afee75830a8591ecb3d59bd3aba82f9564b7b5cefaaf3b582e9b7a3e413d23e29bd55c1f4c9b28856bb5890142a9854e764a53fbedbea3b05534d2c7d77aae2e7775230a9a06137d2ac24465214cc7d1a298d2c75d9520e0a062d93b297d095ad84fb0473cdbfa5abb658eed209c6d5b4504ae57cf6779a601a6d61ff45878ffa2513cc6992b07fff8ba054b40453ec0bd3d6ad3fd7394a30789add480bffd22d4a8239e8a860fb5fc94bad483089bb9f117942749b0fc6114c49d99a488bb011cc11840a2a87d1cfdeba08e6ad6c6a3f4b53438f4430682f11b211f3b22f3f028c21984bece28afc8a259df6f0281f8607430826ed4eb1743e136f97140473bea978d6ecd68d05046377f5886b7a7779f903c3488ab964a13a9f181d0e183e30d75d075d6e32ebb33d30a8b9a43d7479903d2e0fcc155ad41d988358499ae02af945d68131e4c73ebb49c2c4e57360743b717eee57a16aa3a1c58729da62cb60e0c03c16225cccc98c8ad40dcc1f44880525f46f5ad60626fd172d47dc9aeceed6c0702a3e8a4eb942be80410393b4483fc2d7e4251d9d81712b23a7bbd58fd41d18323005a97dff932a8ec9321646d3f2380f23a2bb25b0305df06afbbbecc9f4fb0ac39f4732f3d167b1a72b4c62a284dc5e893551d28286161690000d6d81005a6148fadada644c92159b5ab10b08801546133af5abd492d72cf00f1b393ef05620805598c2da6ee7d3a810376320005518e4e7be3ecd5f68130502488529ed2911adb40987aba09d8b0f1514c31b282801a0c294ee84ae4bcb97eb2fd2028f403985c9c427ff298b98c26057396e9696ecd1430194c2204566e6ff7bc8be202408801426f7bc6769a476d2e6e8628b1a450b0f8f519852f8879265f7a62ca6111e672887243dfc2e5f7c7461833b8ad0610643d075973fa67a8f9a40e828c32a2a3ce928a24e7e51e82003ee5f5e9d25954612133ac65072bb2f31cdd0b30b5c4287188ebb9611cf7f346ec1f9280b833945131efaf24608dae90083214938713af79c599a5f3086e917ada2aae7c4e4dbb9282fe8f082e15bc4bd54e42e186f74ce695ebac73fc905d3e4169973514af6f45174d0b105939294f522df4d9cc65a3098ce2945c991d4a0230ba634f12a4b92e4ce6663c114f428a56c47f44b7e35d6cc6868a0010ea0a10109d0f0f09041c7158c3a493c699153b357a264830225d9e8a2eca4a500083e0cd0610573a4ac9491a89d26710f0f5e0a80e003004ee8a8824175ca972e6fc99354d5584bbbe139fc861766aa800e2a18640979f398e421775330a470175b17b4e335cd8f0e29184777ac185636f3f9d482060768584002343aa260f41c6f25c5fd8ad599066868416303342c20011a1d502874864861fccc3f5d634d058e010f0f15f80db41b39bcc043c7138cdf5f51527f36d52d7e31812e18e0e1e1e18163071d4e309c502297969610c4af8e269844926a0f777e6a7eac4da18309e6642a590ea1b53721a9c6dadbf8e0251872a86441f95a12d26a724a30ce4fda14f14f27d54c82797420c1f82bea25071d9f4ec5fad071044350cf11092a4a90df8d604ed1a5478f96fbe6a4a308a6ce26259d8aa076f5c2367410c1e861c4e517216774ea5e740cc11442cea3152d76ca2d4bd02104934a95e6d564769c488d0e1d4130c7121533b2e59ce3b36573e80082b12ac6071ff57ac2439258213a7e608a9d8492d9fe8a7caad436b8f0f020f6611b880e1f18d447f47fc7fa5356730f0ce2a942a57c499bafc63c3047c822c67284a9b2c80ecc29fbe46025569e832e9fd0a103b3e7fc1232faa774e7e4c01cf793f3466a383084dcc93a25f5ff0df50d4ca9ca7476dc49133a6c60b29bd1f7afb14b538aa1a306660f22f9626db8458e7ea18306e6206b92a3ed857ee5ac858e1918420a7bd723c553493a6460d0f5c1f452ca1efb7d2c0cefb13be446fc8bc8c2c214b73d6ae7707e2987bcc27841ee5225a929617b579872aa3d15272e88e7a915e6d6cf9654e58ffb5cb3c21037437755ffa2e4945598ca3ebf8f885615a6cd946839e59c0f6aa258c82215a6cbbcb99c13ef0a59a0c274ebf6e154485b3e39a730c7ca4a4a23ec1b3958604c61f0109552ca1f254afea5308ff04ff18d70fe1eaab19611c88214a6109479ce3e7d7a71bf1b4116a330e58e7b1a4134925b4a8d355118c54f951eb57f2af13d01b2c1c586c270e2262d9bb4acd0528db58f0485215e4acac38ea8c6da65208b4f18546d3dcd495acff2d558bb40169e3065ae97764f1225bcacb1c614c8a21346fd9113ec52a6fd7d1865c1095359fc4fe2d69ddb6235d69c8bd37622c86213a65431d789fb6ec9ae1aab09e3ed44f5b62dd3fba61a6ba90b1ba78b2f361326bf8a1f275d5f5ca45a70234762c26041a50b2b571344446aacb5e0460eb2812c2e61fa9c3e2e2ad5d60879324b98455987b058c23ec4a2812c2a6108b2697fa6b273ccd696035950c22074ec7394093126ea933086850bd1ba6a24ae92306beaa591266143422261bcb738a2539daac41224cca2428c70df0f6a52e711a6ab18177cf257bec91106d331e4771aedf183dd0893f214e18410c16cd28c30e78f9dae5f21f5b96511e65329fb9cc9274f9e2ac238ee3da3edb7f2fc441862a7a0ec65e554658908c366cdac8dc9da499243982cce9ccd07194bdd19c2b0ef49cbe6c2e4d9ad1006d51ac22d564ce6a93c3c70b8172dc8714701107c0400085910c214a367e4d6764b8a21b40fbb2c0661bc783d31fa395996bc200c49de09f123b1cc4e0d8461ce65948e8a1ff40f0863a898dc172d79c59559fcc1fc1366accc52fe3c310b3f185fd2986f044bd9c9f7c1105b2f2595a4d3635e0f0f3164c107b3af873455d11e4c41c8d1f1e446bf4bab078369dddffd8fe855963c184eb52b5be8feed13c18361bbf3fa95f60ee6ca932d9cecb0371766610753aa121242aace33f70c1059d4c1d439e88837fbe5a9743a1826c6554ab24d5cead11c8c6f66a3abd255e4242407d39aa5aca7a7d42774e2605c0f7796173a7030ae99d9870bf274c4ce1b4c25cbb446abf55da5dd602c714b4145a9ba384b1b0c1bdb299dc50a1bcced3b22ffd55e94145d83b14ee48aa74feab35435183f452a593339c8b7d360cc095ff91192a48b241a4cf9447eca10e6198c6ddaaf636c9abf04cd608aee22eb352c9a50aa2a64510693ba1b212f7e4f0653ccb17062d26ca6c8c76036212df34b2589c17429a87e1ded15742585c190d3d6efa4ead494243098b33e77309bcf174cd9c4a28a27715e757bc190824a3255933f7ba574c1ec7f9def42902729cb5c30968e6f2642b21ecf780b86a81f2584b0ea13e3d582694dad72fcceaa77370b463951a5f379b29b5ab1601cd5599613a24d4af30aa6b0fd59478d6905934cf68f3b4a8ee79454c1b835794d7627557d122a98ceab3f2b9a369192640aa63d75bd5f4ac63d77523056866817f92267b68bc2b1c32585d1eba0603cad94ee53e8cc0bb227183e4e9e30fa932cf79813b4b2cf394284ad09c66d131e572a74d29d09269d3e2c89c6e5d32fc1b027df4dbd64cd1229c12446477f3ea1524c4b12cca9a76a5fb3c2642498365436cbda826b5716904216473075e990f354e794e36a239846082d22d4cbcd7f8a60d056ada0e324f5e7b03040430b1a75011a1690000d2ebc40c1166a4430afa7d8b6cfebaa1492c30607ee3e90c5108c3732d64dc7ee3295f5f01082498e65af74a3ab1e4e48c82208c68cd1d9a16a9ea51d0f0f0058210b2018eed273ec4bf331e12106b2f881219ad6aca4e2f4f42a3e30778970abafb6d037d758c3f1e1c517391cc771163d3077deceb58dce4ab68d1c669b050f4ce13f7f6d9b8b0ed9da813925fd6b5e39393e6f4ac84207a6246e23f3126543c63930e851973c6f527d4bd60b59e0c064f5a542b05c69a7a31b18b4424f87da883a216f210b1b98438e962bfa05a5b4493c90450dccdf9d45c4cef7d1b28d42163430880ba7d353d6f0ad9c8442163330c44b5de92da84f56a2608b1b5e98690bb2908131c3b5d4d743462ad5be102316a620d1935cb45426b65b2ec480854925c7d3f5ab6b4ac5f415860b2a26bab64711f95d61cacd4a4a08f1126df256182396f8925e49cd66b816831506959c4e091d4ebfa4508d3557c1163752a22ebe78d2858d2db640018e5e0189b10ab3255dd3adf5d5afcb3eec0f315461ba3c2b39ba92f2a7570b1a34b4a0a1050d2d68380a9a021e1e681f568718a930867c864ef19239f10dff40157cd8c8f171e3867f78d12858bb618018a8307f2775dac6235e7ec8294ca2d627a414e6bd448708314c618813d383e4b7fc214f928518a530a88dacc84ff13c7c0e298ceb4195cf8fdd859895e9284cfea984509d5fc4a5105198a49afe05a11e4e3ccf408c50985c4b948c999d94da9a82c21c44d0cad974fc42d0ca270cda542cff7896212fa29e30c514d33457b7c61a0bbee80f1c5be017313a71c8a6ad478f87306d08313861cc104965dc566aacbd9f0b000e626cc23cbaaccf36de1aeb802e2ae0e161ae88a10963eca7c8eecec494adc69a2f51c19503c4c88429eb7b4ae1930af3c13a25c4c084b13be7e87ac1930c317b78e4c0b105171e1e1f312e61d4f6bb0a8babae33046258c2786f229ef77fc66a8f5109b39a9a4e5ac3ccbc932861d0e72762446848b10b8e2d100bdc930d0a4cc210627f98999af02177bab0b1c547310ec4908461562b89bc7d593ad901312261300db38ed725499bb68504562161160f13df7212de1344ec1e508256805a0e1c5b70413a10e311a6bc2345655bc8b11c2a00f010c311c61f75df31ae5de72a1f311a6150dfeb104ae9deab1703311861fe523e267f2b4a387b11a6ee7c29fdc65c6dcd0f1c5b281b6228c2a49f133f8450d7d93e0163b06b542449d1830c311823a41c429a2d7d1f2936920d1961309d460e22d7c608e1078e2dccc820030ce6141d11f4b94ef81a71f105938c116df11452e507820c2f18fbeed674f67f4f3b5d156474c138f21e724bbbc3f7cc05639cf7288b2d5241c6164c414372f658bb5acb6ac1706739548a3769cb436464c1b43d49697dcfb7a17a1958308f34ede929ea46907105e39c1c5da645a78ecd56307c96be28afff6cc184b6c8d1393290041955c827b5d5cdfc766190410563ffc80e1d65259ca59ce046a3c06f988c2998d74cf252302421dac3bccf838a7714cc637fb1f6c2733e8f0d20030a86744a6a990433e1697f8239ccc5eb15a1ca3b742798723aa19d3dcb92ce5213cc7d715c46a911b1549860b48ab5adeb4b1eb46709c6ff5db7121f3c5ebc4a30e7d332d5ac78398ff20b2f92606ecf2d39c6073569511948309e8e246549e49bbf1a20e308a68b37799b2ef2846f04e36f4bfe2c7f8b6711ccffb13e92ea49189d08269d3121f2c63f04839c5d8a13a24230574e9ffbd485ad6406c1a0276a9927395a8940306d9747f8c85eeafc812927a1b4cec49095d8fbc06413a4a48b1727e1ef81219dcd5dbe9ede34f1c0785abd25f72ca47c7660d8fdfe55af9589b60e0c96a735442cadbf951c18e4ab465c9771604a29172e37b981a9cf45b72d5be77236305a4842a5ebce6b60aabbb3b3f78ff697cba081e9f63c47ca5f19e272193330859a9d9e58c182da65c8c06842627c7e512c0a69a74aecd30916862065724e69a7571873d5b63582d29bbf2b8cabd7a9b5a6e4b8a91506a5ea2fc590eb131756989359a7ca4dd6d3d9559824971e95f2f9a4e0aac294525a8b7ab721ac4d8551542f5c695d2935418539dae81021a354c7083a85c9545a5c9e780c292253985b2624a16c4f98cea15298f2670f2aa69228f9218571ad757f3b72eedca3309f49cb232f8bc2581d3af2e321556c4361bed355ca666ebfce939fc87e9e13b3f309c3856cb933e2d7f9c713a620d16f27db9d30f78ccae1479eb4b339611c3f933549ba095376bf2d9de029775213a648e17c6e24ebca9809b3cc5c7a8753553a870973d4eaa4bb737d0973c8a7ce5a2679b4ad2d61ce96ee44c5889c9b75254c7b41b99d122bb25953829cd47d98e92761cafe9096462d4f58256110d671a2e935ad279648182bc951dbda175b59818441ebe8d121afe7cbfe11e638aa6639976fbec511e6942645e5c4c8e26a238c9246a5b1b29326228c305f2e616245a87b087911865827743eed5811e68bebd039f57d3e281106af78ed517911612e4bfd126631b6c73d8439480c15a1d22d4eb88630453c3121d9a38414dc42984c77c87b77a5d7c32584a9e4984ad162e5e4dc0ec2143a65975feb69782b08e3e7efa7916d0361cad0102994a97aef0161d0b19444134a5abdfe07e3cec4cb0e29723ec90f66391d7eef7592eeba0fc60b25d5bc2f760a5a3e187e2e74a5e0374a64750f6611a52d29554e4f52d583c1c7bc52ce97e6c16012827e7d13d5764a3c18fd4556843e1d3f72f20ee61aa5ef2ccd6907e3e98513b93baad696753097ee7ca53da659793a18b4a50a573274b2d8cfc1ac716159576ff1477230d7249b5119119205c5c168a64ca8fcb1a363070e86fbb871153335b6bec1f8c1e3578a923b65d50da65c7a674ae9ae0a4ad20643caf993bd7472b6246c30ad5bb8e59b4ee126640de69a942729495e0de65441271db75fe43f9206530e42a913d16bdb7244835135d3eb3ad8095bf10c8637a1bbbd3da2f58666309bb4d116452b8341a8532989d1cb7e2a2483c1cbd446b8f14aed8fc1bcd75eb27ff296c4c560d24baea5544da91c0a83b92be8d23679bd94c0601ad393e2cdb466eb2f9845ad83c872ab1cd50be62de91ff183e75eba601053ad8b13b96098a0a4e54bb9db82415bdcee24cf23f93a2d18f5ec27dd5e9705d3e44fada15d39873c164c5db2464e5e8d35f915cc296f217dac3d9492158cfa15b154073597ee2a98e6d449b5f8ad242e5430b7685321f2650a06bd9552ca69a1cf220573300f3337d9a260b0f568d95c8382d962786544b9f330da134c41c48f7d2a6d04cb9913ccde25545b5049c653d60443d637cfbf9ebd6f6782c97eb4ab5eec4e3f5982e9edcb7427794ac98812cc27d62992b44f72912418336d6fd5ec73f61009261521a84d2b1dc13c5acc549424a22719c16ca67eafcbdd4254044390977366d224c98c08061d41ac7efcda11a91a8239e6c4ea24a2e71195178221659985e510776196060c2008e6fdcea346628dfd2801c15c39e4fa32747ed07d095d42dfd828aff8c0246fe16ff969db43ac0703e8814196840927c663a97d82ce417630001eb8225a3d5ef54d773430801d68413582ba3ef5531778662e18800ecc152c7f0ab2b4f7d44bc630801c1854687c9e5c153d49968401e0c060276458fefd907be1220ce006a5a553594e8c906766060660039365b913ff3c9e44258930801a18238e4cb83c5e0106400383b6163f378b1eb4245be011c0f5a251d00511c2006660f814b157d953ae08fb00646098d892b3e85d64b13716e652da7c929cbd4ab1030b738510c2bbea5252b3fc6f91f6bae315e6385b11827b52f1f3b7f3b000d0a0c315469dd176399c9599b66ed8c0f131015c0f74b4c2a04ce850b245e67b973a58619e1ba533f12ec9885b598549479f12c24e52d05176a14315a66842c768951f9dac4d854962a90baf734ad829e585a10315e42c3eda6ada276d5c1a10011a5ad0708087470bfc0486868e5318d2861a5f516df9c46f0a73e84fe91e762d8579cf65ec53ae3c499f5ce8208561a4f5a5edbc7aa98238bcd822070e2fb6c8310ab3e9edeb1055e31f3f36d217386ca42f7088c2f09f3b848e3bcac1c51625c8c1c516250885d9b3529061a7ce521a14a65425b98298c849253f61dcb1742aa9cb4a2192c40a1d9e30ae6556d76aa5147fd40953ecee70499b862ccb55a18313a64b6da9fb713d04d111063c3cd00d12746cc22827a731c2df83ce1a0f0fe42cf0227468c2149a963aeced99484c18ae3ae4ab8aa2924aacb1f6710963f5966ecd975029a325cca6e47ad2a66b47e95f09730cd36952ca497815d558434a943aaec4728667cb4551cb4fea63b44e5de77f564e051d93309879b614725ee949649230e812b2fa431821b1bb8c844977c88927c47dd01ff6a00312c611b1b3b7a542627798071d5af8cd9fb7c311c6399d45ed9aaf97bd1d8d30fb8cc821653f31c290e298bc93ba16473bad60e8588421a94b48563271f72b1d8a30c7b3fc20b612ce4247228c1d45f4e6a868a3bbef408471648a3839d96bd9e24a42c7210c3f5252929b9763fa39011a5a78785c87210c9ee22eff25154eda1d8530bf5e24ed22272f84141a5a94a183100629a3a346ec5bcad5a20047afc0c30305387a056410c69c18395cfecaa7a60bc22841b74cfef4d1d080046804c2ace2b936449dd5870410a6ce1dd15ab1c6d5f407d3c8fe68932bc70fe61479da4542fa60ec0fdda253ed7c307827a184b49c4e5edb7b30c90cdd2e916b7e62ebc174f9913b87388d3cca3c184fdcc9951362c2e50c0fe6dc503a6cab750753a40bea2145b285f8d9c1ec21ea46f588141d3b753005a9262aa2891d7430d8c955cea542e660d4d3d972b59a6e179283219e69b7bf98f99cd7389827ce5d7b4a952d7fdc0107a3f587d0e9fbeb7883d15484f82f67ca725248e87083a9b3b9c90921049316521d6d306b4891756a724e1c42071b8c23f4b9a4fecdfd0ad0b10673be7b48296adea7c4c343081d6a30da450e56b9fbc34b5a63cd16e0e19106537ebdcf16561ae72b1acc957209f510cf3f6ed6582b868b2f7038063c024320c0118c1dce577eb535ac7335d60a4d312010c008e6740bd1907d93f6322305018a70acdf9b134135d66a060420827962c65c14496241da359653172671f1858d1a19f0f050f3d3c7120438a4d1129773dcf25e40002130d97ce7b2778a1408100453c4c9aaa7957b963c35d63e706c0104536ddaa6c80b277bb25c70f1c59fa96941801f3cfa6dd573f09c6404475a00f5cb81003e58b4859121ba3dc90e6d81a38b2d6e786a77400e82003d30083d96a654fdae530a1608c003e3a57e1aa154ce65fadf81c94d52c8d949d781396c7c85b86f93accd81d192d01b42c6a4981207c648f941282182b211961b18dd2dc6842023cdc93420800dcc791d22972c754ab849011e1e384a170850037396c7fbe42b42bf9b6860b0da89faa193e9f119025120c00c8cd9a7eb525b3dbe3402c8c09456d2c5504ad5079dcd8885416b4cb58734a977e219b028686f31939fa4bbaf4862c7965ac4b4f60a2965862b4c25f94cac3a74098b7e71c38b3b2d683c400b0f8f1b284001eb0f66b4c2f82571acebc4f6c7ae3061062bcc15752429d5a37b3f5aab30e5709e52b2bce7742f0933546130ad8ff73948b7a4c244c28c5498f75dc38490ab919e438419a830887deed02927a475afc6da47b261a3041f38ba066f91805232cc3885299b8821c26e5e3efd1a6be8c3b9485d740db4a1811c5b24bfa18587070540f0118055cc3085b94d564e720a95752a2508334a61906b5a5a1f7e56749881308314062df1bf54e5745d4af72fb8680e8cc2e0b1bb4327468a284cf1f65772b04fa1e357426150e2f4e99fb662300314a63065a1174f277ebe19f88451c6434fcd44519d843c61566f730d99e3cee884a93c5aa4582a48eaf2ac31274c17c73fd207314956ca26cc9dc27c5d8eb6260c3f6e41e57abd9f0433615a7f397539ed56ce1f264ca523a83c9d439f50e94b9854c6b8664d48726fb18429a730e51bebae61d14a183e68d16e71d348caabb1f6c10517fdb11f357278e18905332861b034361e33cf547d28d38c4918c485e420fcd4b6fe481286b5fa8c74fa481845a724f14cc84bd69119903049ed097f2ab47a9a6bc6238c59fa51aebb43f423331c611e376634228dab8f90bd749fb6c61ad96206238e9e949794d1293fc48b22331661d2f0d1c82ade9e29a24b6a62673b2dcab22bcc48844124c49b0da12d9de820c224fa41c49ed8ea0ef321cc21c4eab54a13dc3f8630c4cbfec82ba33aa46c214c6ace62d5fb87ff132184494e777bc8f191a3c8660cc210d4098f587922fea28d98210853e8f5f8c17357ce1c91dbc38c4098b48ce714237eefc68285198030e77fd3d5bc325d769236ccf883e95a457c7ffc770b397e30a62761f1473c7f64990d33fa60d6dd970f337a3e98c7a45c9e442d0d33f66010aea53c6bdbde776f01175e78d1650333f4606e13af1fff5973d619c18c3c9c2b54e9cb18dfc030030fa694ee1e543813415dbc83b1b453e70e9f676a5a543b98e5834417651e4f82e4f20e33ea609891d5ad20f3437b8c0ec64fe95325516225a56c0ea69ce3277a2521621e948339bcf7a99b7822c8ea92e461461c8cfefb123cd672bfc739cc80834967ad9cbbbbc68bf4e30c33de6090ba1ecdde34bb196e30674b2929a5f4a53af56a6983c1e44d8f7eaf173d6283a9e3e547ec3416ee396b30f5e82aaf48a5238d5a0d268b1fd352750d6d350d06b127d4d5040d0d660fb338a9973f652d04cc38c30c3318923e09162b46cee69d32185b82969482d0ea53693298743cb9fa57650ce6d8ad11f546e66973c56050ad7c51e24388d96130a5759ff0b62518f0ed9359f51232e30be6f7d98a97e45dde0fa9c20c2f98438ee36d2945081eda195d309a1097c9e15752fde68249bc24b595976405af6cc130b952c5113bd24ab45d70266668c160a2257e4e938967264f624616cc9b577e73dab31f52f81133b060b6d2f37e21be2e9a0d1b37881633ae7045b2deaaad9067d5580fd7c2bf7174b1013531ccb082d9459e4ade39fe96622412985105c3d749f556b1e449785430078b71b361af12664cc160da258267b887ba902fcc90825154e5d55139a419a5ce8882e146aa8d6da90e95d70c2898f7edf4c7c64497bf673cc174419fe9a0ea7f37c6194e308c9ae751e1cc53ec74130ceb26f6d7a2a9347799607ed32df12f397ef2dc8c2598cf530e167b7a12523843090613d367128c3aea3c7f98f03f2b418229ce4d90f5249a71046368b5af390bd2427f8c60ace4d9a9ae7253f45904f38f989f50a7afa31f36127f31830886e855317b524b3afd0fc1204baa8948faa6d46f2118a4e4594f25bb73c26843051f36d2e69811049376f80f6159cbb72540306957123331e5a5c4f603c38b49d28bab3835a98fd445670e66f8c028294e6be59ce6b39e7a60d059462387d1f4d12e0f0cd73a314bed8a4e4ed948773698b103f39f8e45d62b91b4bd3a3049d9bdd0c9ddea7fcd81415d794ed73d75ea331c184e828c982af777756f6010bb11271baa849cd00606f3db53e9f35ba5e86aac7dd8486506336a60320b0f6926b95a142d0d4c6aae22da08f5fce799310383eeb0ac9c77a13b5f3364601259fd2c87285af9cc5818bc732e1fd7de6ef5790d3260610e336184901ecb40c62b4cb51a6af53f25774bb36980d440862bcce1d923495e6798c4c8b6c2f4162dc952ff64a6a2066e68218315a6decbf5679ea6626833a0058d08d4c0d10ee0f3a25180850220f878808c5518649f24990f931a6bfa0719aa307eb07c75a227e1625f63ad0aa981a36b905418dcde372ec70e16b373c3c6ef7d51010f8f1a38da013570740d09f8163734208174c3e3865742062acca2557a45c9ca2c55d758ebc286c93885d9920841a514bf7462de43c830852958b63f616b6ed9ef52987bb393474ba9d4682e831406e9177ca2ac6d895491310a43eaa0b46acd9808f9230219a23028eb683149e8b32482a130dfc5ea4f3c29aab2ffa10419a030041b4941ded7d3e4e41326eded53ffd6371bb9278c92fd846a9b14d13ea9138611caca7276b9cb71e38449e4544984d39d4d94d3bd7388b264d5adba0c4d182fe4c497efece51241cd8439f6e9a03bdba6cb24e5c9c084b92ce7afec98923c65370d322e61f034d244551239794e3901f21a5ae04186250c415b2bcb8b76391369250c7a4f9ed74bb4d0e651c298a15ea2a85c8892a449983bdeaafd841589b949c2a4567e31c5437b1991309fc895b311ae23d117820c48184b5cc9d1e6e31e43081f619c147bbb43a538c21467fc83887b712bc55690d108a3e58ab6f9113a5e486284a93a8c2ab91c3ac8b86f0a321661c86ef292ac07f3b858086428c2242c94fc7c0a41fd53321261b820395775fa3071224106228c1727e976bcec9e2dafb156ba90710893c5d0115e2d5d3ccb8d761c1f072d20c310c64e592f7a59b6c46cb990510873c81d2689d029abc70961f8339dd5889eab84103918c81884b1439cd50fe5b1c2bd9a2a9021088396edcbba957e3ccc390619813029b1772d3934d34c650908a3e8ac9dd35a589c5f760b32fe604a26f258a7dbfc60aa0f594aff6ca873530b32fa60c8dad1d2980ef3c1202c8eea76d0f649d92bc8d883e1e36513225c8a5631f221430f26257f2e6c6e555b8c0332f260cef693e38308614cecd9041978305ed6f3964b09a2fdc30419773029bbbf102c9a0aca4576300839313546e6a9520b7274f1059aae40461d4c215a58cde94ff16cd3c12444ecadea8fde1964ccc1a0bba154a7c98a8fca0153461ccc167496cfeddde229d37c830c38987b25560ad59fb3dcf806e3b9beaec7d3718351444e7a4433de93966d30d74f7a75ef9c9316b1c041061bcca2f4baa9f5f7e764ca32c858833174e4cfe78e3ce2924e95c544728f3a4a83597d3f9dbd550e6a3b1a8c9a753a9728ff0cbdcf600a71d1d76fa10bc8308339766ad3dff1fdf288cb601a3917226ad4ba332383e1e4255987ec1faedf1a6b1f3612a640468d87a8449165d2581c0a8542a130280c8360f97514c3130000000c1e144763f1589e29bbb20114000442343056343c1a261e141c180d04e3703014068403623028100883c1a04028748ea6700c8b0f2515e935d557e99e1353aeb200d9fcc6754ce80c44c8119e6eca14f2955f0f1c331dc4d506d130fd1664da9c86913a19b432400ec18bb911457805b235891c4adeb651768091eab5d77e70eb293d81c4e3c2663444d433d431c64abd4c5e2ac4d1c74d6a65fd83cc3375796e80d929b7b27a360695d2efaa2e71a4927e3b85bf4b25079595fbd2e12d5ce34315756e9604bdbcf2447447949e31e3d015327d4ce6ff8eec4b19a3a68e3a2824240fec1a0c4a29cd8fb6e60e69df702c238fdf5a221c2e4854653009cfbb492309094f03eaf9fe9a44aef6c32dd154193276cd51fd88a713bc8bbb72614d62280f0dcb61821b33646137b0ed5c93179a1cec5d49039ba3d9b0fda804b3e9f375fff67495c531f413d2479b2969aceb4e308b44c3595f73e813bb039d68c5d33d15d074468af96cdc2007853353c5e872a516d2328d89ebea6ff34492bfb54746e9263e4f46a88595c6eb656166e4b4ff48995183760f133699db9bd3f91713e84bb02dc6f0280d3e028b4cb443aae45f1c59f868e386af9883444a0c3313163316ca65789f5ea051f15275dba98b19f039901ed51a26979dcee3cc0057f7ce0115f98c7c50561c4c70dd3a131d688e4dc0c8dbf95b9318d566b8c1a7f13a1820aad3e997bddc246f0501819d6dbe2124d360abe4e6f8b9d368b0e6c9650340fa75193d3f6165c390a5645f63a4630ccf23d0e988b646e2c50832237d45d706f09fd133460c2820c85317b4b310e3f2236f200911d6195baf9b946e95aec8046f4bc1f070cb369f9d994f47d747526de2682ce100b47127ff8a3d2a827fdb6947e9232edde09dc9d118655613024789b870a8c4c4a9023cfda92f006b7e5c2e9d26fce8d3531917d86936c09666eeb5d85105f5d96a1285d0cdc6088b5a9ce6545550fc62a55a82a94c5285fea0553886d36acb8394807da585bc695904589de91399533c582c41d09282f086e43284ed66f127a2d58407d58c66b9c3921c44d3fcd24a15ae568a5ebe6f550257005784a41641645c8feea284388cb28047a9670165e2f8fb11a03a18ace39a114bd716a330bd6276af016d6933466fd716a0dcb83e56b4553be0fa08563cc701c9322b365491d6fe6e067f4ba7832e5027bb66a42fbf982ccc6768fc7dc748ef8e74372037cd9c2e33969a5a8ba4340cca6fee1aac57efee90f26fbc8d5fed521609bde34a2c38edbe02fea1995c710f46cd8926f3ee054665cdef666013642696a1346dce3d9b935fa5dd876d522b5059bc58d8f7019d0b133494c18577e1ade092c4b7e1b12250b93e882fcc097e2ae27503b3d2c0545f0c15adc5328f93e951e631230d6579f7ee5aa358ed8e6ebb9f50ef288a543c45723ff414f08891a6362b4a7c585477a951373813a589b3513eb6ddff4d1dc76fbea8e4b03563d4f4bf6943bedc84aeb862c66254a72fc70a4f997d631840619285488e43d4e4b0618555bf6ff7993cd9c8e83d4507d42df8ac79ecbe7cf2cd1c8d51ef005c319d1b591c3dcaedeee1909129f14831e8e934bd575423155c6daca0b333ac0a6288eff7190aa94d26df0fdcf706f610be82a7eb81c69d36165f0f97435d6808c0b1cb38c91ef6680f65f2e8219ef2f12c2412c80db41909203cb5da2bf76d21f0a6edf92ef1be0a0dc7415ae763989a43686280f82c90a64bafda406d829e67911fbce7e50f8c9d9e4886e2a7f0ae954326d3c32b98d18ea3780d645642a76850a5eb633d09e8062181b846d2463553631a11bde2cb0b24489d5c41f0e67fb1e1e37159e99ea2a66f93fdec382ed9b60121641223d50946ac1edcf085e577a3607d2305510bc6127b38b6ce897021a09a554ad83c3d3d47a05f281ba95037af91c7229f145b624c8e36600b5adef16bcfb3e1760774c91ec83d9675d492ff9d7f424f813ec8f343d935f6c7aa7cfc9c9ad4a26408f147452bee24bf1154bc1421e669da917700f30d08c8414dc3d346bba0a73dd70d13a03c2c17d109ed09a71b79f24fdb04b7e9f589a33452ea6200b3c60c75e108fb01d33f2326022f2babacee5cdd3a9dcd48a8142f5dbb74c2bae7f146779f26c070527dadc04e5142a12d06ac44d69c68bf20ab902b3411ca990c250ab4a8295f5cc92e01c46c6f8313e0730889f94103c04a271a1a9668810665cbd920688daa2ab0ac9089d20193eca01b8c49e910e2df46f68e6512ad5385b1363d016da0c30f6def5453d35149ea2f30c5454043b06f36702f4f07841ed2c652a2d80f84f250e051e9685d1b9bf4710af4ddd3b1d58fa546fbb26549d5cff4339a3fe2537e46cd1a727a1f3f31cace2767cf447b6a6c2b2cd19c2803d01efa9f731c22a779f5235b4e70027e715161c209e23408ee15fe61c60367185730afa1e881b4be630e3034af70f8f12a7a902977fa47676b95d68889364acb145d639177c06c402624e91f0de01b5dc7d81f6f3157d71c7eaa54ed09b7247420260af12fa8d1892c80b00edbbf50cff96ad8f070f160ba02ccb6cdc5422419fe282f4a314fc05b20ce8d96f4f17d56882b6ebf66059e8d107135521051db202c099b7307e57c67fdc9b27b44e03c49d1c0c37b7104c0a645017d4c3bbf39d81951e3fd03ba90de3ac1ec34e460461009e16a010e9b2fbf61e8843fea3b97269bd44ddcfcef2e4f50978aede1bcb4735261489dcb44ede40c69ab926d5ab441563a5cb5cd513c0f480d8247ae1b730a9ca315ebd32be660f6efd4c762259ef8d71dd7b3c4ccd7b401479be91147834f9d154b8f4bfd1783a4174d4dbe951736a5e4549c86cf4313a4b6b52a8645720a692995df1f2ddafc3fca341e4ef76ba08b57f15a397087c02db6ab1caa3c75426fd433828c9d4dff765c57a22196fac2487ea9d1ec49c876ce273a353ff85278af8ab87510bd80ccd5a2a3672c6a1cb965cc4b0ace8757a1d06370430b48829dc1d645c47c30e91d02628f193ebaaaeedfc51f1bda6474eadade3e208cee33da6295565679256c4d188b64fd6f31b0614a508f9efe35e025317eac65e6f1b12ff2ace24a92b0a39e8e280de735f5e47a8ea2a9cd582c7c9f913def21a62c0d9708ac19216f7f90ec15018c5730f95e1f3b416bff1787514842ab6e6242c89073f69b261dd381c169785b0928962393eb211141e728da5a1147b02fdc6cf000f60b8adde605862ca16e03b1bf6e64e4f14ce7584274415967a248abb91498aad5e26818ae60c8a3985822946614154bf53ec087e4a9f42de02d4853b0b5020a2650ef0c9a8866091ddb41163b0f292e0dfa48426251b06f839e40f4b681606e5e4991057489c916c23a1a10f111120ab4a0eea4af2f70dddbfec18ac75d8058ad40d59ef704ab045c8e0506e67794163844b9efd8016364da9804ac9febbadd75aff27748af82661223f915301a21dcc370238d0a602024bd1fd430247b53534b3682fc35b9613ebe0830be262ba420825ba247918cb8c481a0ee359042660b38e7d2380f61c534dd182473045b738b3bd88d0f7cee823bf926103a40ecf63ea4bd14bba392e0725d3e2de186a6ed7d2dd18c0184147882d7d74f20a07f02b5e7c63e49b7d6586fc4b740f60a502dc94fd264c5691ed44e71655069eaa4c6a162a7f9d272abc898c52f2ad1c7f8a443fa2a18d66d8e95035cab372cb177a34618be41c976fec7648352223915586f21a5d968298ae628298b34cc729542169ad6216c156184b117d152582cd571ac306c5c6660a165f72e05a864c8b45e53dd71d6e8f2ead82e65944f5b7e0a3d16f1b608bb280a4b308d129e041a9c05d1260d01661bba70b3bd1596006a0c0e61dce18377031308211085a10ce0fff7f2d175e34669d839a773b2ee70f4e5ac544f1096d2479c1c3d1e8a42363e4511da8a34528dcd5a5986636ac1f1e55b5597d2fb1c7e13d4884e80a663246dba34bad54f32c81aa4d1a664651e8ab850c73c754189b59218b021d5a99db2c9cfd4eda91acac26cab18ad5c587f90ea981b8b1105e91d4b568ba85fbeee8fa609f2542566d22726436032c6f448b381725f00d7f6da63a51e2e5bb3f2080587eb64acd05a38f7b5faa830ccf81e6d83f6050098080b0a6e49c80709e02b988b6e97c2361c40a28eb5bc1f3cc321f7b2dcfd0d10510144060a3d04a1375e0e0825c87bfc2fc3cad2be668e87590abb907db333a2fc263766775c14e4c38ac3f41d5effc1410dcf315b1eece3416c9780ee738e6d0855aeffe1097e9329fc55d3e9ca9087c6df6b80a17e541c786e827099ce4b58a149e602dc906d8922365610ddb843ae98151c14a236db415ee677135d1b7075275f7097a2c7db3723e3d758d21e5462c54bf262b17e835eb50eb854909cddb05de2a3a42e75a28776ce6f754706652b4c3cefe11a91deb97fe26dd998a738d10a1fcfa3090139df3e1792db3d3792fa1a799d5734fe19a0ed81c15a46af7498e80f9e7f4dc8631d6f358b4fb7ce6d8ef496d391fab6b460156a256ac6030b05a1b43bfa89002589276a70e0e265de40dc84a84e5c84865a088ac1bee1ec06abf228b313a4ebfbbf9dcf8943699c0804b3d9fd09db19451f95616d378139a8d9e406c494208525791223aeb81cf08efd360eae8862706d403ae2817bbfe4490fc935d1bb4936a7d0c9544246d4d18beb20606739bb92e84044a44f27a1510d425446305293c1ae2b314d6feffc1b34a3638c94be379af27d12542d4045a5dfb6c7b559947740d5a75e6c831d8220cd4cf8a7e00e3812dd86d3fffdd6d5fe91cdcc08177ebd8e7c15948781be697d412d29e519ede69afd26dbe7d7d40ab6ba565eea80e62448f634b9b4d3ec6d36086214007381ce5e45158e66f6c7617970635580749fbe6003ee96cc3aa74de57ea131aca128ed9258365d8c49e1ee9bebad8baea4ea045a1dd445eb9aa551bff1f35282303aaab55f3159c040cf6999ccc99b2c93d965f3a952315162c2efaeb5d8ba5ab5142e77a35ed59e91f440cfe965d6baa7c729cf219e87ddcea44aab19f5662da7aea85642e55cae1cac810e8ce49c94c99ccc2dc588be5b20033a670bad09ea292df6aed65a28001fb8419a0bfa913f9782bba69d9c96ee5889373a889929155af5a87cd6e6dc1f13309c0064880edea01dda712c0691a2bcdc7a8dd32fd9d35ca9da2a44f5592f67f7b44a519e663a2d9c5b17032bd5cdbeaa0d0a7d36e035512c2eb62b309c35cd4b9408d1eb36395c47f42b69baa83781988d9f9af68c9693d0a2cfb19685467f9358aec464b1a400e3e90b4d15b63f6c618fb20b0e7bfca8b3d223538cbe49551a50f524d8696eaa54bdecccda325b59721ad7040b6dbd9576c28307109f07c6a45f02ada4021f4c27c70ad3fdd457f540fcd71ed041b90b96e85629e021e26b91fb357836a29e1b315ff8a662ceaa88cbc2dae08ab4cab5781264a89cf863656dd54a781afef003aff17b00b649223c61b3fc4ed93210d4083df7f2054ad5f8916a11250776bb474afcde8070a03ac3ab4642a82fd0d8b21d44f9e76c5f7b4306a0afba43828c8f86006948b2a39793e2c5fb67cd36e357a3ffaa4b5257f13d519c84c7e16c014db804ae7b16153b2ab5159a22ee0a8e47d2fcdc0c58a6f5bf55b235a4d367064e0e4dc820982ec17c53e9e09d18cea18a9b89ed4cc921459958cc60da9cd53225ff0e63ed03076fc4a041e6dc7b4e4a14fac99986f362dd8951d8eb7b95d7a28611de36663c2ebd3653358ca005f881c3d9897f0a16e74072eacd5f118d9b2a5efdb9b7b9e891cc2303021a86e3a6c8a1e43f0838d29c226cf57ff04dec33e016d6a36252c36ea35849c8b21a9763f5cfa7921011dab0f91be0ad109e2b39683674bf07674f3dad160c776503f680490ccbce93eb4cfde05655613ba1a611f364a06e2c4a0adef6d9cb79d12ba577a44d7295a8af828c9cc08b0db413592262d83227468524b572695a32bb50bb6910c25f221d1e459e6ed04a760fa838d2590b68433940f58691bdd1f17e25022389e0ea0ca70c8f94b603dae629bccf5a3dd7fafa071d093a0b6d97fc204a4c3ff45e90b1cab00874ad7d756880c480951be64e1ebaca40f0d107a5da0f1d81d059d59379131bb2288c1dcc769827490f4395a5248b4d40e0a1738c6c7df960866c8818bf2f167307a65a474d0e4e7f121d42dae258bd8e368c5f2520a4508a530495d3ac908ef6073ff0c535069c532deb887e8605b2867889a39b28730467b1df045fe000e47e3989990ba1981973806ad4085165a4bc1593924bf2b67ee49a022238f35b57b614c78c50f3931289fbcca9cdcb914d25302c994c1975870e2564a21ce049701d4afadc798e713ade09bd7411a40a316586aa3726e33c138cac22a532ee7c0a93fc9aff14bd712bc8ac13544d8efdfef2e7843779aae61ce72de0f97e7088bb782d4c6e9896a9a7632a536222338323a231b66e690cf92f64e8bbb466abee417ee2713178169814ce3f3cab878fa7828da4eb68c204976587778f0215358ff93d86c9f1da7f5c1a562049cc6a3435ba8543f69b84b781a58a98b04a45c9bf1f81e90db6a5a88153d7df27f23b4aa50662c433b04c5b20103079344120d91c52b119c1bb22f9100ef26cc7d746f92798e276ff8e7ca09fd068f4be423fbf992c0f99d10f7766b89b22c683c64dc34cc33b35d65871eb3f821d063403ed749c9bc0d400fea5c9ac30075cf431c69a462b1a7b219f68ba782255c7e683e988dea42343b4deada812efc2515b95025b3c5dfbd1e36217cb6fd280f0ea49fc22e81368b2a4fc6950ca1ead6c41c756b5cae6fda17481f4cd02a363b2c0518ccb650065be36a855cfb91c4d72196970e151c9429e25dae3d601bcbdea7a8468b85b3b2dfcd06e1a4d8f62a5f9ba3c8de9dd1429530265b37467ac74b3bd920d6b3e86f06b758f4fdc869a213da35b2971ae65609c05d6d6084e0d06e1c838a1dfeb6e0e9981011a7c0e427f88380cc0c2cacdd5752df7ad611a0f182d975ce17712a9cc292b30aa502c2ee3732fa41b2645d9eb76b7d081b0a24508b99c398440d7745229514a28e27a06002167b822982287535039c5f900163da00abc06453a610cbc71752de78db24f47e2841492a81119999032364964a5acf9aec81ee548df6ab3843717447163c286d6a084cfcd2b9f4dc1449111b2b9368afbc09e623171271f8f9ae165710844a9f0f6990e8a43c38f90d9b11560ca380ffe0eb9b59b411e0f65e1c8c6e28f2a1e8049692e181536899aa314129127804e3cbcbb56df21fd5be2c03c036039669bb355c3162a2d55eb00a0c9ee55fdc4e397f151504888d76721fb5d70e156d8a1258c6b39e6bf19bfb5130b57122f4b3b31b9d227e542b7e08768b3ddfc906067efb8909062ae0341bb0bad6e9bba5ed97ed8236beb408a1c2934f37a12f3feb891a1600f14ce07c2e6a83553293ff1441a2ad5e526a9a1cc1bfaa2e0a0097797cc20903967660c36109c602819029fc977350b10d505799971085720cb2c3d0cbb9cd37ba15ebeed57a3444fb18a2c4a10f80bd3b80240124d00086ba8f8576700a84c17239c5732c7a5725b5a22133aa23f7a9d9de905924c9c82c19adb4ea2c5575e61bc497c11f0c04cc23a32fa9a003cecc551d9ea042384a5f201ad5cadcae108a6f41aa960b032dbbaa1a7911ac62da0d819b7e744fa4ae22652b694bcd044772a323281d90205c5d5ee210a6b82251577c6ae0d70c99148ada5170d36751954b0bca23d7de4e8d8a033919278656d6e21f25c700041b0d6296e629f5bb8405c4ff6710ee00e56c1f7a4763b50ca5f061362ceef80e1904b2c69529064cc6b7c5c548fb3b64f259dacf03b36d9ddf5429993ae44100a51d35c2596505a5c040501a0f17751d00d8588369aac0cb8094d3e8b7ae995e87a88d9a3154c393762ef11323a406759431b868e3836867b8756c5dfefc509d8b75ba9614bb62e242becc15a7e25a13b623c0906d6a7a563835424a72c5f0f24e9e428b113c6901459832a3faa68543122575a27282e598e4acb2094be296b260f68461de88f59a22f600c0d4faaef59df66ba38876544c45fbcba2a6fca748dea1c57eb3c297f76d2004cd7a6d2b1001d19559189f29df22407539e9d4a396221bde703d8a83f2aa3c7650da8986bf9018711c782343210861750e0a1ba40ff641ee85635ca4642a6950fe3542eaa1fe3fd05ebee38f56d829c4bb9774623c4aaa54368ddc6509a01ed3334701e80e6774965fd7ab1199de4f99261b602e0d6549df6895d56053b1a492eefe45a0f23c9e58b5eb4af03fa7d6d9ef477a23754125a9b0a8e48f0253de535443e94eb0d9603118d0079697ab395ca1f16c9160c73600dc4bce18130aa31073abbcc1853ebabd656e8d6b432b42cd0597b70f674ca63111b6a2ce57ce8a60fe24597b02aa58fed7bdb80f5343cdfc92a225e8b02fb39eab7c6ef4e5f3cfbbef25b67bf053f58fa5fdab7c4ef565f2c7d4ffaa6a45f91a57df278f53447bbd75979c211925976126a1b251a27ffafa7fb836955d0e6f734b63f4ee894414c595b994e62f69f5dfc09093d7a5a1b410e66c398bc6f4b47a5a07eb14f0cef5a8ecc660f3f7dbc9226d7409b5368f9eff0b0b0c6bc9ccccb0347cf5c992bb49e631458b127ef44c6eb62acd7b203d62621f2d79834a481646816d50cf5669330dbb8d911dff6730373b8ff0674dbd82b6d95485bbf93ae338754ac7d72adc3897e2c41f29bc42a124704c92725e30bc7044937127d24963712a3a2929a8442e22bc9d625924d8c4625518955ca5383447c24aa2bed158073893be9c9b8b4a6a3edb07970af3dde535c082d078f661e3eeec771f892a7c89c475d1e2d3d08f5f8eb61d543aa87411eee95c77615349bc7c523a687821e1e3decf410cee3fd75b7ab207afcec71d68fed4d21c73a7b1fcb800ced88ae215601f185c8a9718ca8e8fea2304402136852e773ef5cc6dc38154709d11ba8328f2ef69ba23cd8f38e0917f7fa4d3b17ab02d6d936067d42722c0d223844530d6951c4dbaf4996653eef3684b379659624809d510afd6d024c57b6b4c4f39aa96ab5c3f991a6838adcf05bd98c7e00db019324c72d133e925ecab8105380e606e7c17f6d8a48295206ef1a84518b7de68e2e209810874e1c2343320a40f1b4dbc21222d3592f062ed80ae8c9a5e8611c534ddf0bacc3d815371278144dc15602ca81bca77d12b599654bb48d3530dfa0894c2a0cec0a33f31ce4b295491e9a716dd2b44043f846b544cc29aa5b420353b830475b0ad4b1d921f2742b8475a9a468637eb2c9724e430dd83247386e389c7a2d01701a0b6127b160a955e938c289cf05c7b3e2973a37ce871adbbdce2e1ab2114a9165a43179437663c6b010db77abcc2786499a1254e337a76a58360c54210ac9fc503d6965dd62437cf0db39b438b5e38219f22413635d0cd2dad7ebb4f8f680eafc362daa48e534991ee59ff25e409cc2097a5bccfdbdd82d9a0af8053c796d80d2482e28b3e47db119aaf2e005942160600ade7379ecacbb61b786d23e501ce9966c9715392a9d0c443b8730c07e787bab84968c58414d7026386141914f631fdbf871078f5baf3e6d6c3d48e16f255407b8e434084640a7c4716e1215323c2666ff88b68b6bd9b11a482cec660cb8586519420036266ed50e4a46870e6a49be064bb1b2fe617a189964cc13a7b6f55350c9e47a48efd563925ba6b746bf34499f85b1ff15f89f6e9148fd39c4839b8f5635a0226184a7aa63ee8d26ee9b4522bf0612846a6fa71cf5a4e7e95cb70bb944e8f0a2083ceed422ca8bf291d12eadd76a283194730311534b83d63c31940547b61049726078751186f9f6808515936e46c375436d78272173d80a20eb96efe242572ba38394724d3b7affe1894e34c4a46804be6ae3a2ab85007f400f4d00ab092d0c802ee0f9f1936b154aa37814cfa7de1542c0e2e67328d7387b3b7615ac044b68e4871e358000f4eeb2736a2abbb01501f26812c729f5cfc034b281e89c10709c5000e29e3ac5c4e65a59f09e34a7257560d1493dc1669ed86e005e7c7c2eed258ffa9cca61f7ae1d1cdb600b5bdb86d501c78206f056b279093c4bf34f53a9f7b40f8065ac623c7389285355f30ae06b429715bbe542a81fb1db278e316c68bd5818da16cd9d2ef063ba5026008fe0acc34c156710c480da7fb0e34668e5876c504a10e8fb7c4e6f07238189ee2d273f5b785ef929d876f94422645f08290457f9b1233676e2b6d51c6525448b06c9988d009923c72398bf297d971300f41a14f2951fdb95a06c4961c8ada0df257e68371bd16c010e43fdc3789c5f671e770f36a86440c45758b44034ff1b16454adf482ba65c0f93c26af075296796c02ff744904ef533db4090cd329522b2053bc24489a4ce527f9c783c227482d9110368caf03081ed464811e98626af381b718793196e6a3439e036c48d52360bec0432f046263ca3e706345dd7c283151ac431b06a5d9bb21278077bb5be32ad756c55b166646b427c3b6a6adad3b7dd5225b3b9af5a103a7d6e99d328fa5b986427b6484f5c5b1a92390a2607ae0a540a68a1f5e8d9193a4fd969267aa04b919c53642753ffe937704d27d1237467a8bb68d66c881de86f74dab694b0cc64ca809fe4a20bb3b4433a4e6be4a3e31d41d3fcac39078512da528f71927d7dc0494c51c15a490fdb2b8eb667cf9e5898d82f57384295f5aa0d761a55f0915048f53ada6d1515c04f93a579fa1d761a47a3639708c398914f465a8e4c3061367ad1368a6cb4da48f46070460b041ff58eb43d232355da918aeb9cc26f9cab1ccee9352a83eafc84869b782abf711c145aff6ce44d10b77ec1ef51193420ced8dfc141387e2c201fda4046f30455b2ab0ddb4036c4541a4a690e27247c29e9e5bd67e90c1627d3e7302ada05801bc118252935b9d6fd729c58552b9d376586f1308bfcccef22092febbd8ed82b390135e3154ea3e7e0fae5fb6a088f70c5c0f2f1d711ed60598fb8e9f5bd388644d7b493591821bcda8165c9ca8d95185676b71ace0a438e9585b482291764a89f3a65e8d499d080b48c851b94e44a410f8d8c0407daa6485209474185cb1711ee3a1cf3ea0dfc4645478157512e0433e4256a180698ce52c6e88255648aff76ee29943c60257aed1187b9a5be03a71ad645440c5c22a8a4542ea834f2f1362820b55135788acba4ba50956084a7ef114a81f87eea84e022f79cb9e30175a4804c444969a55df25710177434ea8a4fb48337c90f0dd289c71f3d0f00214229f0e9a381ce1d38bdef8056cd6e2a67e19850308a02b5743f285e818c56861ea407b2313bf1e9cdf8ab7193622de702fa0e09ee882b84e3c2c085d42f326fc4c423f783fb2a93b223540823bdf4f3762589803485210cdc40243ca219683c6a0a372b6b32ad5fcc174375c931adb1a6d58574d697e45a66fb79181559c8b71f816a90eaad50be42f3f741fdc7057f5e9544c8b8e4d94acda85b543398d3038c13f3de1697219b3407b3519b8d266024ecbcca858d4c3d2788c22b0ddb80641b18ed807b5081df10982f55b10c4cc49ea976c3cff91c73449f665d5134702930e091782ff2f88d7e5ad6582a0d0492edc0f9c3a52f57c00fb20f08e607104f2d0b7b96e92f97211632e90998c85d0fe722d2adb6524728f97f6802c85ea002f3b884379fdf4f23f8dadf5c0582ddce2ef23de637d3bcf6fe6e7b6b495e266b758f847d9d83dd179e38705a9ab87fd8fefb850bb4038a460987e2e52746597357b121ba6ece14ad9c14dcc082aa3d8a5320ec01bca340dc06cd1269c3bbf151efff71bd48901bed4d84905f411f3baf9166d0ab7cd4fb7fd40b20f4cac7bdf161bda88f355d7706d8eb7d4ceff8b8f11a2fb87a852037dce3d5c5bb25d156d5c92020d886c6b87290b213a4308548b3ba0cf6de6190751fc72032ad26acdd500fc1a720eaa4a762a274048c70c3b8cc2c3ed7b7deeeeffd78ffca3dbeb3f8ab9ab7579653a85aac3ff87e421a62fb19b6d99adf42cf146c9967a0e52381cd40bc2d0b75b7098c37d644725581eb609e8cc5c044ec81dd044c49389bf0a85165ff8a24e593f4fff04477fc0c5bb2de8c423ea20969dfc5d95a4db48b8a18bdcb601bc2a6d66c0700d63790155d884e9b4fbad5242e16906233635d2d8d453fef428faffac90deeb00840d88de0dc270024710739a621fc6ac206054925b84a81d3e89f10961b21482f37482a0fe20d0842ef222aca9c29825469a7d5bdf6bff881e13124dc0c6e5402750450015da19672fcc76340c634d8037cccb65a58caedaf501b047c3c69b794a256b5857c0881648784ef3e1517033a6d15e33111774898956608481597398027af7cab65075a5ea87f8909b13ee98a0cf1a40c443a51fa3c0bdb119a11815c7ca9f7ef86f13a1c6fb4c8038c7961c2320aeeb4e758895cab3cddece684873ad1cba72cd9aa3a40283c117722a0d197dfc71833ba3b1bb164a52f4fc7a1266b8a840937a33eb338f33360d475859b1d0dfff8dc3f88190fc4a014cb31b55748203c6ab6fca35a1b1e64c1a3eb2217454abf630c48495fadcf049f13e3306f652bfd225de84581b01bbee778bec44af16e551a87014bf6a1037c61f558233492dd320cabd87ea73802b3237cc792d2e520fd46380fc17e089cab21a84e3aec1e561646071b01731ed38f298e59c784c6a4625ac65c1123505716eb5e22e8ce81d03a7019465d22849273208a9b5b722f77bd37353bb48349da92b91bae90a610fa435d58f7b4ada1e16909c43de3fc97e094c3de8843355c5c101c60a0767679c823b7d6b3b7bde87ac1f262c0196de7c755e705b049e05096ca9a6cdeb141029844fe645fe976a0553c32ece269c82370576322f4ab1d4d8858dcdbef59f6e2ec0370d2ba9f39b8ae1e30766c2cae199eefd86c44759c75f43a2eeafc5fa4092f909c967278f689a8384cf8b0dd3e040fe03c2598811955d1301ebf2c6985e70ed978b83e131afa13c6af2b5cc25ec6e97733292be305b9a537430c2a6ccffcee4904c813933dfa228373af89f71aa0d6d216dc6d2ffff748a2bc0c527ddf106149a52584fbf05a780b8f179782300377280716d916ac67813ac52964e22bf1f453b7595487f3fc078e24c4f35cfac6c27ad708ccdab1bf84a8969bcb8243482399106f3b0a3d280e4187136b8e65811cf974eb94ab2fcd433aac47ddcb36e61d4c0f79f2cf17269290f1f3159e2f75b1f0dee813eca4021e89848e0b3660b46439330f0f0f0f0f0f0f0f8f3121b5b54f48424a524a4aabd75247e420a524a54c2989b7483b7a3a33f129a6c9de6498f8040402c20bc70b970ba529194ac5532a28a5c7a80127ee5f214f5a8e783a3168c07a4a297878ca32d5b663164c8ed729a9f85bf27ae3396c6c60c60ccf614305336698a143166c66448d9d4468e4d7c4823fedaff9a34760c189ee1077731c9d745baf489ecac9cdb74757703a42ca15730a19947dad605f82662425ace3dbc70acecdc3d44790ef216dabe0934a4a664a4e1e3cffaae0a444492b62a954b09a76a742aca8bd6799252a785bcb1d59a994498c769c82ffa484d211377595af29f83af9a33f9b8f1252540a6ee2a65df797284953527016f4925c3f4fdb3419052f2612c54f6645c1f9c5dc94dbaf34e8e0e1e80805abab13420e25bede9b147480824d136356ebd99fe0d4fd57f637992df6da800b68514ed0e1095ebb2477ddc43bc1c41c925f98162dbe419c603d922e696b494d66cf2698e469a1e67e5737d2046fcaf467d7f6c7c8e94c70494fd9e40ea57dbfc504aba5c77b83ada714d24b30392c55292d1ef27f8a25f8ad0c169e1daa76639560547559b885790a756180c60942072598bc41529c1c3ad4e23e6346c724b8b5f8e31b31eb3eac1a6934eaa6d021094ef848c8e1679d69523f414f60c60c2f3a22c1d786082a9a6b4ec999ea80049783526d2288fe11bc8ed021c7ecd31bb2c8118cd60ccbef49d4434f7e8398024e021d8de034e4dff43da943ca0d6211e86004abc12b8bcceb9e47c72c82efec5082418722d8efec7927294b29c9b023119cea94dd4bc6122ef22e0e175d74d1ef051765800e44f01526d3c4cfd095cf6c41c721ceebcba2eb4e372ae83004fb1ad2b43f3c2479ea1bdc51082e67e57491a5ec20042ba23db5ad8e490a5dc0050e1a9e6c98391737726cc720f00b49a530e5a2a26e0c3a04c1994ab595f54d67aadf11085627e5b2720b992de21d8060628d797b55a9945448c71f38a1ffa305b788ada1593fb06fb62629546366d3fbc05fafb7794bb0ccb4f9c07e8c6cb2d4b4072eaa78aa4efa3cbd5c3d30da79a2e7f4f595369a072ebf267597206944060f5c063d12ef2f596a767760b4268d3177ec16f5abc30e33e8a8c349765042c7545a0ac90e3af03958c88a215f4ea2b21d73b0191d72603fc9d131258fef9713904003a0868e38701b2c8d5c1711b225b57ff1451b1278c1c50d0a748e66c0052420811933686400ced00107f672f2159d624cea94ae60a1e30d8caba958a752c8775d631b3adcc0d7c7e049d3db36f023838aa0a116717b3674b081754b225685a05e035b6eaf6ea2a63ffda506d663cea1be7e3f8f9b062684a42fee6890adf92fde810636e7e5a4824cb3b45981a0e30cecae8ae6bd3bbfb69c02860e337071623ecd1c3aa59c45eb858e32707f994d591695e4c46fd0485e943b420719b84b4dc143b2181c5c181f748c814d31668b90f24bed88e4b0a182d221063ef2fa8dd96ec4be48018d30b041481229e4244fe434707071050c5c98fa4d41a62b499e44838e2fb09147d4d94775f111aa91c44a0076d0e10526ad28d15841684613e902a7b5b784a4bb50a1830b8cb6eefc93906369e8d802a74bea5f67ffeef4592d30aafbb3d783e7deb4d0d09105ce72d04188125a648c3916b811faaf6a21bd4d4cf10aec5a0c1dc945a9521347d46105365db2fe0ec2bf62765805f6ddf4e7385eeb163aa8c0bf7d324b093a22f7d33105c6826e0da9e33987d0b09156061d52e0af920ceaed453fa64a0abe880257a36e528aa8a3aff324b0fb4107143ccf5fb5fa136dd0501e743c81f7495a993a7a9ac72cdde81518740263df1aa4c5b1bca63d4d60438f6bf64d4a3d05bf461adea083099c1cfba469540c9a7a61435b80bca0000ae00e1d4b6092d6e49a827e97b549097cad779654913faba808858e24702ad47b2d54590ee16f193a90c0e71044c8bdb1f65dcd11b864392d57cc15b91742860e23f0b529c4226e4811f8c937713787e7583a18860e2270c94a9d48d14e770c818d1fb1b5e4256971371a2dd00f740881fb4fc24e488aa03cfb0e7404815359a966f235d79368aed001045e34770a399ba44e3a2b56e8f80117fc3d04b52153238d469d0e3a7cc076a4944a074fdd7b658db416acf5800d21966bcc53513dc303fe92f6ca1b32d4b10326e6a6354f162fa7d420a043077c090922ea256f8624e580af983daee4b30bcf260eb8bca8a34fc449bd7f75dc805352d3fe7609ad4ad7068c52e33b4144936be91b1d35e03c09fd697f6b9dc65423cd4632d322030ec841070d18b7affeffb190aacf8213622a88adbe27adb194051bb7bf2bc62ad3696e0660c4824d69539e64ab1efd9297b0e0bdd7df630c39e60418afe043bcb451ff25e84b2379d1e45cc16669ab6822061932556d048c56f0371e36415465d18c16197080b10183158c655325a3dadd5b560b2360ac821dbd92ae2fa7a42a7acf8dee95ed2e239b0418a96054a4c9c9f2078931a660a082d5089e9d95a2ed22458b2db4d8420bf30818a7e093d2eb2d49957809cd14bc4f4e5bfae40499925a0abe45e69d2521318690cad17e72dc80410a368b7efd90745809114f41c01805ebd9eb7df1828aba9fc11005fbaf41dfeb4b67356d8d2c2c1a80110a2ec4fbced39b2b928ae2e8030c50f0ad31c7d4aaaf9553e4e200e3137caea67753be9a2fe8a400c31368cf76b24dda453db5000010303a91a7eeed0ac13c5e3f80c109f6433669e5c1e2a9bce826b86c3a98ca21f754a4cf8dbe40299a6033fe691f9591f2f51f061899e0da6fbf52abe4fbab6082d3bfa395426b525a62b904ef19724c97a24e12ffa025f810b47defc40ede2eaa042754c794bc36e9a98a53820932fe645162b973266b128cf726a96ff142352b0926ae7ebeedbcf4b596014624f85361a75b7df6714220c1a5b6f4a4b324791e458f60f3ed72cc19b9bf73c411e40fc1bd73f746b0a63ba3a6902445513a23d82a4b5dea679d37ca4570327da9a4529922d8a094f64f113dc97037115c5e6f48c132e76bf8886025e62b7dd63985db7b08ae93e98ca9534ba5983704db9382f9067d39f7ad8560ef93daf2b28d105c3013135372b389b006c187875022b778cafb7bc31004bf195ff3dd0895589a0ec0080497f306cf3c75a1f29a040b3000c1e48ea5cc2649f78ca32bc0f803fb9927e9896b69681f0330fcc06898876c17327ddabc0f9ca5b6564ca67225e52f1060f0810b9195d734e7db905335d26ca4f4018c3df0b962e6deca9962d10327a964f2daf423b7926aa4a5d4346c24d30318796062aac98a936e6cadad9176c34990bc0bd4010c3cd89ff95fb91e52793968b4e0468e25598071076e82d4d518d592aeb809fa0476605c84ceb7119279bc8fe100461dd8d2a419aa555474a7d448ab1bc0a0032b1a24a49cb2eaea8e36ca66ccc0d145172968c0161478038c39f071640ae197b264f2a01a69280032c090c3b61be32565eabb730088028c3870494d44dfa4a275f90b07f6b3a62447ff7f48ad1a09da8b968041018801c61bb8f83d312ff807bd34ddb08132100b186ee0b754a760992f681c2260b481d32f4fb596db647c370301830de5a997875f673c598db4b741e3d0c05803a7329e92372a4e022fb830247f80a1063ec4cfb94d6ee6268ba681ab89f599a496d22112c3051868e0feb285b28d24e35ab600e30c9c45aad211a692d000c30c8cfd071962d41ad174712307a2d18243a30cfc7b99672b4d3949ca910c30c8c0e6fb243379cebf51df301b03373ad2bd2e26494a4462e044040f7d15d1a46a65038c30f09541c8b6104769a81b0c7c753279426e48a9319906185f60fb5fbc62975a47fc6b24046078e11113c9fef9ffaa3287d105b6d428d51f5754d0ae6aa4599a4ee003341200830b8c48d7a98269ee18ca13dcb8a10005cc98817c035d74d127c0c08c195be03d87de9e28e1e96987a105f64cc7d041ec3e30c0c802e721079545971a4d8f1d16d81c796326216f185740e74c1db28bfd56e02a66f68a0ec96973ea0930aac06bce6221a6f206993354e0aa468b0aadf89a9d62028c297039a4d32a414bff27111a0f6048814b49640ed31b31e8e93900230a9c087a2fc4d39ca43d9f000c28f06a6a44ccc94d5d502a3304184fe02ba69096f49350e67921c070022342321d794253232d078c2630f9c72af37ba8a568aca1000613187915d4af36a95cf5daf8c2d1043096c08e96fa9873df35d26ac050029faf3fad093d31a5bbd448c3190148f068059bf52d66ef901923bf1eace06aaf764210b9c72a188f9ff4c5948c9a6378e0a10ac62a77084a4491f719d3399a0137f04805e321b2c6b0efaed31954b06dea962789a6b47c993123070e270117094fc16a8af92586a89a6448c1c0c3144cce7275f3a8ae79548f52f06ada31a4d51f7539b413f8000d16789082bffd0e3dd31e09ccf01805eb39da26ed639d5b4913f8008d2f3c44c1a68bf729df42279d6a90808b1b34be7037c1bd4728f8ecde762a4ba06072e61a75f22d9fe0f45bea88dc9954d248199e073c3cc165ab77d027833c61f94eb0773a832cab0921c939c1b8baa88852f24df0b9e2080daa214df0216c7754b724ef0c658293a63ab2a6ce23f53e98e04ab9e48b1ad44ab29c4bb02151c342ecd38b765b82c9a92a497ecaa2c6dd4a30394d7e68087aeddba504374a2d26097253fc5d27c17f8dd08ea0a5e67f4a82cfa8adf7613196dc4d24b88ffeff9699ef7e3190e0e4782c31cf1174102a8f60d7f3a71ba5c4930c298e6035d46628651db61d6c04971db7da649fe7df8e119c4a4149b3d32f82ff68317ca32711324811dc774e7921672d119c8e57e90c4ac8eab210c1a9a484d4f862a534bc1a695e3c0dcfd13772e038880bf03804aba1f7c1c79432dd202f52d080b3f430041b736f8e9c4699f6da0bc1e656cb1d25d64ab6122118f91d6f7d62e6cfb11e04eb112be9d19f20b80c49738f504b1de225106c929982b0af511b6904045f7921f7e9789327e80f8c6adb68c7eab7d1cec30fbc67045922aa059dd85d7af4e13cf8e0b1079b31060f3d9c7aa33fa5e47469c005b420c7068f3c9c071e583d3df244526d2a6f907fe1450abe01382640812db4d8a2015a64200236328064021fa021804a78dc81bf0d6e5b25a2259196871dd8fdd7f50e0f41e3df1e7560cb5377ba9c3f5552e2c276e0410746e926d55deabf23a95c0088e03107ded42c4ecc7d3284ea75f09003ab6a31d364c7a0db937160d399507949b5396c64408b2de0c05bcee94ba998ea2f21273cdec0e7ce23fc94d64d88f90624e1e1063eb5e670ef5c9a438ab581bd931f6a4c4653a173b4d8821cc2830d5c9a103fe4f4e59f7d6468f0580397b32521c57cf2070f35b0d92e53f576877c927273f048039323270d41b99fb7bf72f04003939406952f23581eef68b4c1e30cbc66cb6baa63c94b9d3703139210adf71d595a82cac0ae07e193840613912032f09d95425afd6360f2e4cd21aae7564c550c9c309df933b7835fb084810bb5b2d21002031b9a2f495745be53ff17d8531aa4b5e6eceb96e405ee5255b6d6dcd0340f011e5de0367b68470f1616e20227d2eddc37d85b6074eedc9c72b25a60ec46d5e999ba9c72cd0223ccf46de5a6e9742616f80f5a91b3d66fced75c811f11f57e5267d660b202a355edfd29248f6d972a7059416d95ec8c1a3c4205ced6f2b628d3794c810f3993f6bc9162324f57800a1e52e04b480b3d5513a4f5ed110576f3e514c735a45d4e99c0030a7c861063984930eb9026e4c2e3099c9b4e22688eceaafa3b81dba429a7984a9dab83155080210ef36802579b635df44d954ce916e1c1043ea46c9aa563e6b10446dfaaa6bf4a15aca246f050023b4282dede5df90725d982471258f7fe2816af3ddd5624703bdea7bfedf4678c68c1e3089cd064fb7d2d2286c9088c29c91a52d4eb0a1e45e0236e6886c7495a821c041e4460834c49176c73fe7beb31044ea60a91738a71250979088111312747b5adcc23084c122aa44cf22a0f20f02121c7b6cf4954f7a6465a0b6ee45063c3e307ec7dda34fe39a66e30053ee0dd83728baf21dd644b8db413a4007909bce0225be0d1035e350615834e212c49be465a5981070f98aca24b34da6ae51e3be07a5dff4ab958238d460b6ee428393c74c0bd8f10aab27257ea9f032e5db598a794e57a4133669475c10307bcc6fe2e3d6ec07e5433559bf2353da7461a0d1b393c6cc0267d6f955c9e46b68b9c056e47c3a306dc45ec51a5ac2d66b088c08306fcf7a6a74ee9e324b8d135d02c181d3529ffa859324991460e1c292841eaca400c59b0f9a2b3a59be853da353162c1c64cd66964ff38a41003169c46ba9c41ad826e88f10a2627156a375fd4158cf4aaec20820ac973e7857371a3015a6cd182193368588c56c46005e36b1152455256a95b05d7d93dfb3e6570f71055704aa64bb13685f869cc18a960c7dc3666f7a9fd9ca382dd08c94e6737113b055f6661f984c590738818c314b9aa55e52c8dd1d42ca34ed618d26dd6766aa499c0868d1a34ba485cdce1214629b8bebc3751ea79498514c4f4be979a6246c19b50eaf52b8735d2708b1a39b8e0008e2eba5080165b68a1c5165a68b1851618d0220311d0824999ab808b220a46a407b5cba6a2878c8682cf2721265332573140c1e51845e5ec4d9a945e7d88f10946342519ef3bd4b37ec4137c0ac9b736d6a89887189de064ce75fa3ce48f5613ac430c4eb07e9e3668d9654e21493818b07088b1093ed3636a11a62e52e50a1b626882d7789692a9a568b14506228003c71a62648295e456ee27caf276d4628b2e1a139caa84549a218f5ec9b900a315625c828d25d4a5b78cf55a1e4b1435a9d363414670e4701c38ba125cb692b13b6f55de2053236d8d023128c176fab84954526692a449b0976152d46411de16920497432af9313b4511033122c1a9bb2074eea0b682ed376cd0b03b400c4870d952ac8b56e3997edc0208311ec1a70f96a643e893f96b4770324cae5725a9d9444412a3114c6c537aad47a6f4d931825127db2f4af61c82ed050d37bb8bb10836e91c159328fd0e221e85188a60cc738a693fe2798960f74d88a4d01ef48b880836289d79b2e8e468216bc1a1c145dd03ee109c8a95b7c4daab9166086e4da84ea9757c73eac210a310dca53549323b48fa3d1182f114abb64f445cd42fc41804af6bde71b437444e108c972a59225e786bcc6ba4adc0861781e0be53d49ecaa2e91ebf91022fc40004bb9e556ff1b56bf207fec2bbd2c8bdb30c215088e1074e864ed0aa8c90009f10a30f5caab547d172ad97748d5c420c3e303a6811e11b74759a7a0facb85568868c9ea3c50fc4d003a3217bd6504d4b22796083f0902e3f2fe95c1762e081dfd35c53c29412ca3bf09691ded36f8e88fad8e18b22aa73fdf31a69756077e455363b9d0e5c5fd4117aae29a84cb939f0f6197244cfb9dc532907ee2a6dc516370b1762c48133f5a733f7a4706064ceafb42b421be30ddcc4cefe65b981bbe0be9e326d884fbe0d7c1c25b24ab610830d7cee0a524f6de7b57e6aa4ad81fffbecff4ce376e5500397634bae1d93c68d1b9e062e9d4efa13cfa0611349f7ca83e40c7c9410724e976fd4c5941a696660e4e94b2252ccad0c6cbcbe9c734ceb924a9aa3b9487e821b9ea39c200619585189227d6d53236d0c671e59694a2d0656638ab9c9ae114134b00431c2c089b098f64352cc3e18f88f16169479ab4d8e91408c2f302aef87d2ccb92d6e9404de8517f80aa9dee3b9f1235d606c94e609e5f5e29ae4029b73cd6d4cd828114f13630bdca9f10c95a216626881510b651d923784329d2cb0164310125c628658e0748a3997a8a0235ffd735760b29fda6ef2743926252b701f2aed2c998a355e57818da59134354becd1392a303a07d3f9da3b2429b11a6953e0c2c7ff3a6a6ecaa954234d0a9c950c49a7ccbc4446a2c0059d114385b4af13d51a696b218801052ed365ccb81e47984fe0b2b86aba31218244538e14d810410c27703a66a769319926b0a33d3f54d289099c6b6b277dea2cc325303176895e9126253022b609ed96df22799e04fe235b4e503b2652be91c0a5da65151162e63ca523f0f6499d5263954da79206621881cd74559335bc7b3f5223cdb010a3086c5ace7fb7691a3452fb8d3b33821844e04352a9e933aa246b085c4a7731e9979043525c084cfed1e29152a74d9fad915670e4c061e30c023182c06afddff5be8554c2d222030ec84004baf8e2040f68c08c19ef89860abc701578c176851840e02387b5c6d5fed1efb161e302c609317ec0a59b6c95b4e9bc9bef183ee0eabff7278d27460f38d339692d57a8a044bb80183ce04d73e59113a4694b290d2b7a88b103463d3b2fc92b0fbb910ef84c32e68831b988a63a07fc6bce5477fb49e4d662e080338f9c5493f73760e409adced33f1862d880039c08cf9b25e97bec3e158cecf15477f5a5af4605233f6ad67442648af6145c060dd994b6ce501353b01edbc264d2e9631d96825196ddad4a65fff6470a3e68d2ae4e3712832419551b05ef31d7c79052ca8f1aab91562307172b30d645a30c5170a9628c1d42893a335d8db44caa38b6033242c1574a224529710d3223a0e03ca449572363b02ffd27f810ddd5d77c927d9072514c8b0c38c0bbf8220519b8fb820c4fb0994b748a87529f773a199d6092f0fba821d489dcd5066470821beda5bdffe287fd65a12063138cfab497fd2fc752d76568824d21fd6eb5780e299a8c4cb041bbc44bb2490626f8bf20e4e4d23b694bc4041997e0fc3b8752d7cfb5ce58a212ec45d7915ae973ccdfcba0047b27c4d742f2c89804fbaa27d126e73224c17f8dbd28cfcf2163908c48305a2a5f8f4e823e2a03125cacf348224ddc2ec87804bb7efb5927e9e4b9ef0826fa9b66cbbe89a7f346706bd9e79a274670275f456d1cb9d79d5d04f717ab4aa915c16529a5cc3b79ca5ad944f049e93a11dbdc537244f05555715b63a52c113d04679f4fd4784ac92b260dc19e7a89ff1d3b3db68560624413797dc44ea247083e272539550461f69a41702129e5a9b7b1bf4e10bc650a2a684fd5039c2023105c7894689faaad363f37640082af3e15f33af687468e147c91e60f6cd75a249d5367b3f2981fb8cc1941c4d5f4411fa5566b5bf36b5c1c4f34bef01b5fe05824c8e0039f2b55ae05197b40aded84f25e53da480603197a60edc5f4b49d87a4cf3379487b9bb4ef3c92aea3810c3c305a53e4a9ac216e6db0469a17ed5d2075818c3bf096d3856429febb586d59906107f66a74529d3585a424c70b1975e03ce4f3b428f21a8932e8c0765f2a33515a3f9ad648f3c2067216b8c998039719d6d9e46a726074f342551c292a6e70780964c48153aa72678e1fd93c66ec0a32e0c09a0875a5335310163926e374c173bdafe3062e89147c9372977c226d603de22515c911e375cf062ec8f83942eef1d0a0b30626f88e760941d6e566d5c09d4649232ff4594cc934f05d2aa297ba0822f468e05cd2441749f2abd233b0e9af36f456e4aa57ccc0e7a9feab495f414e481978d33962b628f53e3224039b33e62c495a8b0ce518380d316ed5e8ee754a254186183820230cec6d8b16111fa920030c9cd20d79a695a1fd5f20c8f80217f2b95712ea933686d0f0027b1a3e3182b264e0ae0b8c12215510dae45c60359dc817af94b6c0e8a79c1bb5d4f77aa405de5cdb74446ada484159e0765fc732af57e70e6181bbfb492b5a934e10cf15d8d4ea262ae5ea50aab502174be68d21a8282ae25a054e0615d5be4f757fe7cc9821830a7ca84fc1c44598104a3a05b65b35f6a4cea14d4c2970e3253b8ddb8b484a13053642b45819dc4424cb4081dfcc1e2a8ebb2425234fb82f67ee04eead83762a4fd9bc824d6024e75869344529e5392694efb3658d29a42c81db10aa4fb608b5053294c0d6ededa72f51d1b93256909104fe5350a1a35208a9420709ac67b869485abaceeb2338d691d35ea669047ed4ffc9319d9c31e32c0e328ac0b85f36ddd490085c7f705bdfdd5279913104fee45e0e35bdb0b31b0d3284c069845c55d922230849ad749dec723287175c68e0466f607b200308ecc4aca53f758a3925f90363704b63e6dd1753b620c30736c368c8e841c9e0017bf162d555b56ffea406942063079c576b281d7453788f3366b4e0460e2f3c083274c06a66cc3984a0e1a6fa1164e4a0dc7e3175ff8e051938b0b345634e704d31fb1b6c29659b670649deaeb962c84f8b20c49b820c1bf09ab531638990242765041935e0b209194b8aa56c70c181e43366e0281cc8a001972a7808edaca79de41fb3e084243522869cee8490f990056b23729b50e1971c7cc482512958127d13359b5748830f58a0ef6a820aaa57d493b49a3ccb1e4d57b062a7720a3ab5db0a45957a48c242061964057b3a66cc1fdfa4986a6f7cc102fec286aee224355228212fbeaf0a540a75de49fb099954e03f298f28212accb415c93ae8ebd66e9080d8ad59093e4ec1a78ed0a31282d0112153707b766934c54e29188f144b7e1c99b4a73429b80dcb2958688ba697320a46e9fc60f92a9ed3591fa2e094c79f9433fb72d3130a56744e4a59e6a0d1e20414dc79a83e0fda8f4f1462b28db1fcdbdaac91863a840f4fdcd9ad734d35937a1df8e8c47d7082ed3ed11925d5fbbbffb10956c5f24788e62e2ab626d89cda749490d68f4cf031a78da619417aeeeb295070021fa021801b7c6082d3a172723b9d3ab1342e6ed820c11962fab804671da3c6eca492363dd6489b3163c68c0e68b1851612c08002dc1b90fc4af161092e5432f5ddd6a07f3c3492175cbcb1c0d3096cdc6840f2828b06ac81e2a3124c3aaf9843997f8e9bfda0046ff6edfaabd19304f524f8714dfde9dd2aa693241811c5837787de3d8f44824d31b9566ae7cfbaf003126c2ea5520ee54978883e8271cf9e2cd3bf594efa8723b832fff8fed9f4d108f6745a8d765c8b91941f8ce07227b73f09f93a7ef063119c8668ca4c89a24ead0f45b031f55b5626a5f9d6fc48041b64c829e59cb4acaf43b6f0810846e555d08fa1ea53c80fc16e4e272187e8c3106cade68e8a26f4c4d41f85e0b3a6ca9244559b90dd072158cd49f976f613fa921f8360b527dae9d54b3db9fa1004db16d4494c8baffdf98f40f0e1c9e4269deb0720186dadbd1d524c117ddb2f3efec0a80d322c5e103d513f303e29650e1d7699bcac83c1471fb8b8d7975797e927311a7ea38fe10337d69de4988a8468e11e78afa0df752ff8a107ae23951069554bfde9e0f8c8036f1f2b92f094f4c64e1b5d9032047ce081efca6b394dcfc23bfd710756cb4d6f527f69418ee6c30e5c52634adde8e8764ca903636d52cf723a912fda0f3af0312499f39afe1f7360443a2d3f21ed36c6fc871cf8afa064b2ace79dcef2110776829d483929992a09990f38302a26a1e3e8c9b13ac67cbc8151dd793ae712a279ff0f37f021ae96c689e2973c79818f36b062ff914c722af92967cb123ed8c0fe27fd1b542acb7c9f123ed6c0c9d8a984d20c491c5cac171f6ae0fa4d997e0abdcf7da6c166d4f840032f9a9521585ac82dffe30cdcc99824050fd10f33701e296f44cdf6471958eda02455068f9653f0830caca4d8c9d5c61f037e8881099af268efcd787dfb4718d89c967b9f2fb5ed8a3ec0c0e7a0524a8a9b4654ce7d7c81cfec761df32e5161faf0026feafea22eb21f5d60cc3f075315838841eb35d2f04a0d3eb890a6a45649e70d8eae912ff8d802efef9a4ce87e2c65f91a6938b8282df8d00267e95e35d384605b1678ddcb11fa29c898597917366ed0c002ab959b4b42eeed3ca181830bc2828f2b7039989652d32935d2f0c30a8cb0cbfadb552724c96ba4e1e81af751052e26f389969ade23bd393ea8c0a99c838be7f4a9c24a16828f295c7d4881539ffe63786f28f01105c6b5cefb92e9943bfe8002e7aba669924ccbf59c65c2c713780972926f50c92685c8096ca520d4468914dc6296081f4de0ececedcc474a0e8f0be183097ce50b3afd268b153f3880f0b1044e54b99907e996dbb4317c288153f25ce28d6b08258307868f24f01394298d6216eb24db850f24704974344fc1f25d2afbe3089c9d7b56a8a7f248b9113849933798e5ca471118a51db47eae8e296e0c143e88c0462e2d42771aad1ffd6bc2c7103895835d8ed61b5397324008fcf7557592a3a2dc844a193e82c0e8ed52f23d8af85b3a0c1f40606f54cec1e266d3d3e7fd80310f26477f1041552f1f70935f538af8d9470f88de1953cd552cab32b6c7f01c644a2aad91f67ee3cee40a3e78c0beb678a50ea6c55fb4032e551295830c2a4a2cb50ed8108fa2b565c9d19e02143803906f60c68c1933707861e32307bc85a45719f69124c4940b1f386063d6179d379fcc6a9d0ca4143e6ec0fa96fe98a23fb796f861035ea4a80fe22afaa8011f16aae3ba994c42947af8a0011b928520ae51fc63d299055fe7aa5e2a043bb1943b78c88211a637c87a3b8b6beaf400128b42a98d31e8178f153c60c177e8535afe1921063d7905a752b44eca21befe8ece156c0e52fbce621c25540c034660b246ea4d3ddfd6eb8bc0d9e438923de514ab9e08dc55bceb9359a6213f04467b7fc8dcf974d0bb10124a973e08bcd65f32ede974aa4060358a12aad631acfc01db5bd9da366cb3877cc0dd081935633c1573ba075c44d752625a76223c60bbdb2bfba885d276c0be08116c475d94d4ea8037e91fff4348b12d25079c1275e341e898f7c5019baafdb5d2ed4e94dc80094a8688faa7367fd006fc8e8b0879a38abed48051a35bd9399632b402d080514987fc591b29f7b3e072c71169b34f845616acd768659094c782334d4987da0e0bde4d4f92a04fc96bd32bd8db20fb3a23858b5cc1c65115720e21d3d256306e4944922521c81c6205ff12ad5acf4c4d735661b2e0aaa9fe2655f09e7f6df487a454b0112fa994dd3c882c49a86054cc1e32675db749d22998ece1a7b326b72c319982f5b8a9a28fe54bafa352302a640549dbe2dd3a22053fcaf49b0a32e42073340aced33f9b3ca5ee4b098982b3e0b14a864877a38442c149d09031e974b92642a0e0c736c5fc63a15f1ff409eed426a6679ac674234f70597db5bcfb62451a3bc185a8ee649ba29e082758f3ee64ff7b3a68bd092ee55431b5e8e4f94d4db019fdd3645e6a4f4d2638f59758ba2ca9fd10136c0e6e962aa86a3ad9253879273f830a5132a7902538b7aae41f9a45f44ab09f16646fc85322bd9634b7ae3e097ee3faebe9cffb133f9260527e3a2d6a234acc23c175d61c2dcfb4165a487015761f43c8a052c4a047f0174476915b11a3ba8e6037e6760d2286598c368293f43ee249533a9711ac9e5a456c11a96d2e82ff4a4205957e2ad75404a74ac6dd74ffa85f2682913104d1a33aa2990c22f8daa04ee792f9c6c2738833ea7bc7102891b1f772af5308c6ef53e9484944ca09c1760c4aa8d1f126c02018a55df42475775abd092008cee4673a61fe23e4a809100836bf490d299dd4baa0260020b83eef0d162d2c784e13e00f9c68eec578e9cb33dd04f00313f226dfa46356fa7a1d40803e70fdaae555793d8a960f7ca89c83c8a7ddbfee1ed8533a2664ff27c9ab1e182b8d225cbf4d4c9a07269e59b020647b87140f8c502a478a3596fdd33bb0218ae6ffdcf4aba71df87fdb10849e75607469124ae7ad9c7d3af0a92798a64f4977690e5cd0294ab6502b21c6c8811bdb507a648d3e691cf851b5a3e3a83c2584032333d3a95fe40d6c599239ed836ee054ccf27b450c53b10d8c46cfa4bb535e8e1f1bf8f4c9a37e1b9244afd7c0ad5e4e509e1af8b48dbdbb9252779706ae425c2de88b896b8d06fef365c59c381e72aacfc086dc78d994a87863b51958fd98eb4fbf5344be0c9c9b4e4fe6fd92813753e331df6fde7d770c8ce7a9241649589eec8a81c99fe65a22dd30f07e2295debed62475c1c07632f97f7fba5d93fb052e6e9eaea02c45af5c2f70be25bf53ba0ddd97ed026fa6f7d2225ab0bb2c17b8b4e69daa73251d25bb0526b59b97855467fead16b8cccc9723699294b4cd02af31fa885930192fb558e073e8eb9ca87fbafd15b8ac29079dc5ad2e7b2b30229e43b036e9fdf12af0df1dec6490ac224a546033849482f9a46c6a4a3d8d9b72ab921478d39bb3461b09ea465160db4fd53ed8a649395060b37c54f0c8392ba69fc089144aec4bc809ecba68f8abfdff649bc09d90e857f1e4df4799c0a88adfbde5934a2f4be0fc42e5de88d257224a606cd773b0d34147962481cb4f2aa2aa4dc5844860f4a5dff020fcb67447e0f45a963693a4533d3502e7172632fb7cb3e6b4084cd0542d2aaffba54d89f07b4abca0623f0436598aa2eb34a488bb10b8917ac1f4aa53043d089c85a85dc27320b0eb5b17cd6bb3b9e807ecaaa94b0cdeff29f201df22a3c9c8d9267fa807bc5e92d57f5726f38707ac8a4e9a4209ff08393be0f488a42dd557c8bf75c004616aa7195295da39e0324b1635ca2cb969098003d6c6a4e8e9ec66eb12e0066c90b4b6b6173b882c016cc056b0983ea5a8854509500376cc2f5945ca909f24000df824c382e6537b92ca59709ddfa53b424a7e1759b076a6b641c60c522bb160537373b5a5dab44ac28271ef60b28469935992af602bfa75bafd75c82d5dc19767325532877c3995ade064a41cfa3cd5534a252bd82832e56e92af828ff77f27732d784852056726fdad3e948e3f4a05639bf756648aa71ea182cde5162258c58b203a059f47f209a5644a326f53b057693de7c71cdee952b041e5181e942865272505ab19644753fb9c951c05ab9e7b629be40e188028f813e969292d9a90985070f9b369b3112aba33a0605df3080de9ad82b27c820d9ea2acbcc4ed2a9ee044e3998a1576824ba52767df585fae71823b4def29a538b6fb26386df17b4f838d95ca6882179d420a3287ce142d93894e49ddf7cd31c18810fd4bfee8a5892ec14634f330f75882d5887942eeb7128c58048fda796b2f4b09d64b42889663b77b26c1f6ada5e7ae24f5488251da368904275cafa4e45ff59c16487035bad56b928f607f37a7183db9a4be388213b2bbc472d48c498de0f2ef5a2df3525c0923389d2ac4f4b499b4bd4570a579d3798cd1f46e8ae0bf2fc6cfd024a94f04134f5d4cc92cf305218249e199cba2e6113987e0437f4fcadbcf905531047f52624816c5358d5e08c69274fdcf55999a44083e95a70f593364af67105ce6da82e0b296ce9cd492403021e8da8b360920d820f9435dfa482195e40fac961a651994702d11e2076e74aa3269e5153b47fac08a12cacf54be897d113e70faea6a748708d1f13d70da293394e4e49a263d70d9572b4d06397ea53cf0ba397a7b42dea04a78e0255be5a6e5fd7fe80e4c7c5f4d13f3b8396407fef692a759882d1eaa03ab55c2237be8c046fdfb1c648890b4670e5cfaece6e37127e7550e5c921d446853d58b6b1cb8c929489cb89722513870912708cd19a2e4ade01bf894e9bfb265ee0626a8a50b12b462d0126c03972a752911dcad830ed9c0af7f3c0f31e5f59ce11ab8ec987b29c343c8ba1a38fdb739bf47358ba932810fd020c100d2c09659ba9037d9b77fd0c0a88c1c84ec4dc163e70c6cbacba944e47676d70c4cfcfc94acc192d4b60c5cbccdf849cc92a62819d8bdec185449ddbb730c6c4a691e1273c8184d31704a2b881034ab4ac78481ddea949426ffd5b380810b1e3d8bc48e6842f805be4b822a617b810d6d3a5a43e78917ec029bd449cd1e6348ae8f0b6cc564a62fb40546a50b6943c94f0b40184016d858395c6428a1165387055eaf45555cd7d45777052697c7ee4f1983bb6f0546a89b8a14325ee8be0a5c341d4beaca2df26e546034d9d88754973aee53e0b54308b9a69b774752e03c6ccbe2e6f5e09e28f0d1ca74a6fa483a7aa0603372004fe0b3ed56d079438a41ea047e937abcba4e96f45e9ac04f2e254547caa55e6502e7d9efd746dbac93b6047e4350358f9272168b2981c9196a2f51f34a12b124dc5929a8a44c6848602c990613f61ad2373b029fafd6f92a42cc296404f63b6e124ae494497e8ac05f87b2e021a6a63d4418c0106c86090620044647b030192369d6741058fb1e151d2d6e9206022f4a74fc983b6e08fb078c48e2f53d9a6f93d6075cfe911ab3d268d5b607bc9a0c5db595079caa0afe41f6968cef809131bcdd2c7e854cea80ed986e25541c5d269303de2f8ffe18623a6d161cb039fde37a5ad0a32637e0f7b73f834a9d43fa6cc0c9f7cd394ac6b425ab01b79b7a5574a6d8e901d08011ebb4ff2131df320b269d5abf0c9a6a4177a11690210b26554822a5309142751f0bb6ededaa72c5b0711d169cdf7755f06841a8ca79057ba2927e4ac2e30a3e7de6f2fe2b916de956b063aa6962bcb4b7cab082f532939e2d5d7bcaaf82932cc1335a44ffdb8a2a028daea8b4b065e2481c0e0643c150300c86e193d305f31308001838268e4582c188a0c9c27c140005462c344e3c2c121c2222101a87832261181c08048461302818080302c140201c0c0fcb0a3d0f70ba8f5f491016d864f976f89f9f6cc6117429941c168278c0b9b07d0815e404de0c45821520e670e1e5139aceb8b6510afd64fcfb39e4ce9fbb2013ec291e18f7c24987cb2b154ec964de2daae97b54239ed0ad7351dc81fbde55ba856e92bb72ffbb4b17c79a275646910142c82f341eaa41b97b68e27ac682828f1c8ed83a29f60620933f7c98077ff8c5c23660953125fc191e84fbe020fecf5a5be93f0b2486d02df86a78cc77b6b8e483bdd94b2bd69876c18ea9c3e9b8bfba31ecae906f3034c80c7a060782f402fb1d7c662be5a09df06fa80eac0bcd3aae9906d46562c1d45d0c3f2365681b6c0181b3efa37c48116e0115b0e65eb8022fc6863fe4a0d763f8b5de50f777e8caca8096f7719c8de7cc137b751e9237f21e3cb55f4d48255409658606a28d3c6a5412880d5143821c3e4bbafbc486aec342c3dd306a78126e0943c014bfdf2fd9ffbc768945ee80761076b837d7b9864e17dc05fc42e2a17a082bb419ea09417942cfc88b78ac3db41025e402cd790bef83b7f570dd4f45ca0b35c1c20e81b9503d647a987a15f4198c099742e804a3b7063ce5b68c772b397239073d8303417a81d3ce2f0b9484a6c08f50cbb5f9557c4a2e5edb70cee08a250e1e1d374c4933347bc6402890220472ac522af9fdd060527b71f1474ca16e3143847d0023a34a28333400f989c245b4c8f72f13320909325cc1b101a820efbc43c2d5a4655adf62fdfc53d0f5b676af1c79cfd00bfe584299a141e82fa41eaa87cc1aeccf08436b9da1e03067d8273c02a7287f9a631cf4bf3f725e3cf5cfc5a076eaac93f035292c16b610ca441f2f119c84a661ab00cc102774121209354308a15dc876c2bf01dc48c5f7512f285ee09a81af08882f2eb120a570cf3f5ac4e9a94703e96d4c5ff2c32cec270550797e4294904b8893a0065426c1af2b1c83ec1fb8460349ff27e0a12b72a9f95304a5fbc2d961f4a20629415cc068e654e214fdb546c9c891cee1cca69d54c4a07118669813ce09e1de0915941c0ce00fc382eba0d3a06cb01a44014e82766a5c03693bfcd7112c0272d12f62eea86ff4ba7b6fbec6b15e309a31356a6562f1ae8badfb56acadcd8aacd5cc747f2f97081490c712fa1c5f8a39ee7d7220591a2265feb4b715eb5f0e2ab1021add3c48f29fa4b4f451ff77bb3c8e8c08701c73b9772e720a97b8b690712a86b69c3875324825807cf72b681be8888ad73b25e053ec152aa72a4f20447dd9e25f5c4f910f5f588d9d391373a491bd0f4807514725378a640fb1cd88b9917cf0329a302a1ba8b535dba013232201cf5a5b87e2974edb1eed80a2aad8f699e8aee1773c94cf5df2063acc00c022b9b900db54fb2fb96be32f8bc82d66c77e572182a8c777095db6e84879f0c1c4b91fe6443a4ad5e4722d66fce13534d8d5ca091de539a65b59985e316c2a8cae81fbcd2ab326f80100310aafd155cb3bdcb99ad4ed9a9cc445b593f849c592be9de4ad3ae91a3ce030e74ad4a5e55c39d6b5faf9597614b79322e58f91828ee657da92d9531ffd93648bbd4a812cbfe527ba142e6b4ed819d968642400a1d9e8150b031e7653e983d2209e94f8ca91f889e33ff6f624ae3989c830eeed45ef2dba9d90422194841513e6a2d06942a515724111cab1d812ff6057560d02cf3f4a90b1ec6bb134bf5bd0732520061c29e3113b1bc40f61607a7a08c1848c4aa153b27c708c8aba0d9dbc9f713e81d7ca9032d8af3396d032ac59ec61a1340280c58d192117ea89176d1b7b498168928e093ce7eba743e3ddbadb95acadfb5ed98da9af6ef7a3db279ce88a6a7e6af10cc06cdd5f2c32456c144ece54c0cc9190028d80926471a24623796b91389ed04f6c6f566d0e16ddcc8959a5fe8e509032ed539b38d83c9c803d6dd24db586f08fa8f2eafdacc79eae9d06860c42e295e0d05bae40b3e46b314daf094539bf80180c0ba573ba1835ce2964cac961a85e3c580c5feda6dbcaa94b5ef2850c357302fc11dcffec3860800fca504e407b72fd923b7cb1da25148ac503fc90d4b3b4e0464ff9ea0dcfd58dce9ee043c6f49b1ee0c1e9239c8c5a547805f3d4504601493944bcf9307907d60182d40802b9a1feb9638a14165650c081d046848e0811270465a18a423908f9115236217d0a9cde4f16ff327c68710f33dcbbd31c94af58edd4686cefffe637aefe4b6a405483c7499cc2e07035820a26473d083cee6b561c953d1a211529e8d3ebe7407ad73fd316328e48918d144de870ac3221ba37b63165834b3125ffeef762e3434340140c7273b808311b62dd835191475fcf0f3aa7c5aef663dbf6b955b3308e298e85e3acea2ec7bd46a2f949d805b6492bfc63818d05a83342614185ae81061a9a299a2851a5aa490088d80cae64deaffe05217fe3b47893f8664bb16c2835e803e8e9d1f3927aa60a5aae012d3c5751a50295019481192141219ca4ae09b6469bcb85f67ade63210000e10b4c56ad48c66aec4a5b1575b37196a23dceccd8811d41571ec328ae81eca57a7dabe1a4f635b5a10905ac8b32611af6ec7c027d574a090559633b95de2ece656234bf422f2f9621e535a6e3940b322791f5f99c4ada6ea2e1ec3e5b4e715a660a646a68faf9cdb92f5b36dd7d602bcbe6cf4390bd87626a1618c7d9751a3f40a926835c23d525905d5d5be3b9ad2c6d8d02256d755f6692f0f710c054064598e0df5d47e94192eda34b7c1603c8732d6deeed3e9efc0ffbaea61e78ecfd22a2214e25ce8150096d90ca5296849ab31629926554339c101dd6f88160be726314416170260887ee0f4834dd18684c104d2fdaaa53e351d64c034a730af7c3d3c8ba64a11d6c121aaab4919fa90994ae1d7443dc4b69dbd7daa9054592532b53c10e36207650e0630c1776d9fde6ed60a97a248e40f4a42ee555341d860e64a3d13955da00c5b9eb23d732a908474ad49d80f2b0d767f813add37042b30ce9dcf8eaac7d7f82f1100cbc123cc06206fd97e78d155decae172ae58a8f6decc3103587d4d0cdbc87722ad7f9129bb66ba0954f4649f4e224c362b9e31fa9f88538d8c39d3051d2b68475da531fc0094ebd60c52ac2e38170526fc4464d64cd387fcbd677f3ad0cf71031c179b0a5a1ca049b86bdfc1b55b8d926340e1e42c7e6d3f9c345f84ea7e75d7f9247f8e116d9099db5cb9d53ad727af78fe0347643149152c5e665b8b402a76cb87ece60107201bcccfea7e5df35d6ae7f075186a3531c667295af6ed3fad1e528325c56f5dab99cbb470d78c502bc7c43a63199b0b8c573ba3a3b6669dd25074bfd4e3e6b6241135cf710f0a496b9d9fa1f89d68504cf65da483262183aa10a9080a7f95c62ab7743b55754495ca2361141f35b27d13b898d4fe498819789574301584fff2be37d434f7dbe30ab11d8f603c4648e2a1c5e36ba86d0252a504ce2408cba2e9338bf0791b6d2c2a9af4da5d051c906d5b2f9e86cfe91abdb7ff281adf8f188dc48c15624d70f908f33d9d7e288797b41b5846f0dafa65b4bc8d81653346ebfa19f3cfec6e3c743c8306255eb6b4addecd55feb25392f55597f20db544e1dfd3260d71cb5d44563c657a365e580bf8df1f9fb3383ff4d1923012272d67427be0d275922c2da74581c7ca846600397a2e58ac1548343cb0b75d07a946ce2ca9f2cd5fcb7d3c642b1bbc4a9890a6e16093d3cc641443b7464584612baf3bc824341458ad8e02dab48a833d4b6bc32822a4c17e03ed5c4b0fc9cbb95968acadf06880ac2c6b14492faefa1769b704c89b202b27f93d3d2c675951c6f32f07d9f96718d55285f25be7973aa2adc73aa46587cd51353b1f17b3f2f5279aa86210b5a9c3b18bd4fc878020f0d2ded14f6011188453b857bff2161ba5a6c73040350dcac56f6a0611917619bd9ad03a4ab53ac8e737b327a2fcb86e38ada87f115058f64b4fcf2798fc10e690bdb636c2adcf0355b28b9b09110c19455d8aa6d7121b28d76797d2cc6ea797dab4b87c376127b7167d51d2dea67d7f288b4d10442a9a817140106ae9e3ad25041c94e40cabd467172548b8e5190b67346aaae4ab45253f2aa68cf3104d3982c2912e086d4681bc55984be9b8d2e578e69a8b2364dc8c56a038cc7d7783b5f67493afa521eaea6092d4b764854f9946100ddf67b775b42cc84245dfd1152893d2d11a3a28c42d52a72fcb1a833654a8a548d5854e725d4c09611b9f77b0356fbf4cd4aabe061134a0247667b231855219b21d394e66a3a8fc3b8d330aa1c60fab3379f1a93d3c2323c2369da8493e746af60547391ad642ca084c4cf179cadf35ea3495c2694ddb220395bfda006f6e31119c7e02d0d66cada4dade822b09ee8601c3a8d0867b09b7a3155749df05fa874d12ce557e3c9f9fb2518c31a1e86686e597e20a72b5a3931883fe62671244b3ebba5f291dbefb72fc9190ccb37016c40dcb635ceb0587a3758a6a3cd6543d5182f1982f0347fc3da6c69bde2b636f27cea1a4fe6d8cca77e0a54e961bb4a01164223862dd4181010e353d3ac76c414c3d2ba574f8a12e4d0da148bc0fbdf07f41f85b2cb699ea24c5ce5e8e9d0ec10cdcefae347e3c4f2dc7e1d36e182f5c3539a68c01c94a2d2616c626236b221ff9280f9840b299b1dbddfce77080029fccecdc0ca27826ecf4a2d2463ba44f33e3ab6069f4420ef133784a51e4c966cc107274824dd62d0caccb80b936b01dc483035a216c6c4a3c88274d5a2179f93c3c06b42c1a44249a90c60de23bc4314f3b316a0b0fa55568f668a8aecf609d501e29e9277aec0df93c809f9638c40d766a7efa69528bc9d140d52a9b453c939f6571c095113098ac0f2001c41968da1529510d14f915c25bb1a416e5de4fc818d5769f94a55746ce3b499e2199309f965543ea3a25cc87d35d8aa2e8528e878f0ab7bf9eb488947eb87551dee5515f45554a98d2edca78186a4127ff35ffe5b4650e75d940c2efa3f9da2572afac1bd884f1a87a6ca08cef4d44a065392a0264612ff45c229b3818c49a86d28f9ed3acc37400a2aa010450f186d58b103029dfc21d456be086c0f4d8aa5c8f75d79209c419c3a92dba7af37604205779f82885c043791c7ab58cfc4e5686fe831e62cc821d84a7a992d3dfd1f5528c0be028cc66a8d13b58b59a959639e428cf79b244e13b0143ca6c568e55000a3eab6220ae32552799f898ba569dafbfca371507e0799f2a873f1b8590dbfe9104d72f481b53e1264eaf7d617691560a823feb120806f0c38bd6f81d6b029e9fb0fcf1be4c6e8cb494f8ee4894ae4de48857a17a64ef09a06160b6a3cfd373f8b0564f6e364625a9f698e29c4770d7e58aca888c1d351af4496ecfb33ce83a385656ab9cfd8250d6171474f91b92a3a9f57312493ee2f895f6d2b591cb849ca9c4ddd2ea73e29dfc7a3cd7e70a9b08a601ca7f4efc11b1ee83965cfd819e3323459c45ee23bfb0914bf361928412b0956a130d3faff8a0a135eb985c1f0c2847f1a0dc1160072b506076f8c347a7381852e0083ae2989217dc6b735946d5e6bc029041e780d9d72b757e506cd4e08f5c7640c65c997ff331ca9a0585f63b76c38715f5dd4f15c82ad2e68e1e450d5ce7ac2df7d175fc65a0158dcd02dff4565d8ac24ceeaf885458782c7cdbe78407d742c099b77d4c3a3405b58223959807db53f9a3e62d9c1ecbe31fad605cfdd4778bddf4a4a8cc8eb5b4bfcd18880d8407bc56ed43367b6e37332ec8b9b293d5abd92aed8ecb341c526874dfb6d3ada44b409caa6924df96d72daa46893cfa61d9b3cec66cc096d72e42e2721e7ccc2ab7d88e336333af546f03374c357f152153c391abc353c518c94055d0fa1c072bc965cefc95e1c6471578def335f1a371d63f62217f7423ffa45e36d678c97c265598ee00701a19636308103c6bfb6bd220e5d7b71bcf61d3e83f1efc7cf9169ba9330c7a6330cfdc88404af186a05be6e90e38524a116786b4585acbd4026c64ccd092acdcad81640469df7fb0c38d690a107e1cf7a305e41fbfda779e4aa5a3cf11d3f0cf208d4a2d4676e2d123be85a9b62dfc6205ee359cc382ab5dde4ed6dc08fd39666181df734747610c207b2d71158fe8e75b64ac8de9528ef107d27ee5bdb83d88d6c328de948c0d92b9949b6c8e4b0ddd6662041da419b333240515dd66718226336a9728fb205414515bacda8e0e8888833465108b42a7da409b4faa1f4da78b611c31ea4d42fcb76736363e651c0ec1a097d47561b581ac3f580378ac39db31f9eb50cc7978e8a95c76dc702a4f83032a80f3721119e1a5c15aaac7379272073e52cc7079aa5638e3b0eb673c46c6031a58323be060cc195b388f0e45b1afc45e0df96f3ea6e2a955d97a47343e62ad193f6391301541db3ba6301b3faa16466351582d9444e695a5d178ce842a6bd6f7a92507c2f9243a3425df218082be8ec18f288384d2e073ecb93a87212bcacc7f4bd470f2ff7a4aa70597f52b4812cdb4ce79298246b2a6ac9b1114db1674b65f44012c6e204b51476eeb1879683190667d6e73a00158102033024595516da4def680cc36dda08f18113e6abf6124ca84915a7a810125d4bd0b78316ce44c95200b0f154699820411dcaf534c49b214ba6a218b991be40ab3b5e383db51972fa382deb818902d46f16ccaa034dbd26601fc97e33b5b536570db41eb00dea66509be6dbf65fddccb36194bf20b4cc371e0a080969f0fd65060bf0e64ffa3928e36ef5fd246cbce35539384836efd3d5ca23c473ebcdae16a36878f3cb06abe378975d565e0390c76bb50065d890dbd386d07a044f6354f9272812b4eca08bd9365b47b1be49de0e3b3886f16cf314641ffc15fc508f46efa923a094f3a8bc375bef412450298879b4d748677208e45769e5c8992e75a1f5642b2aad4d544bf02a72efd5d55e7ddd022f8b0909962a9b16600607cb0edfdbaa4b005b6a2468d51549d2cc3ae5764747baf8fa223eefb5e73fb886bb6b4137f7368b34a78fd16ac34eea55d574c339c0b9b48e37acb522f50dd37859a2573afffdd9664864ad496e8bdba7da1b36bfc3b626c5d212210360872760727ef30da783335fe8b1737c41017bb00ee45401ca59b5cc4eb9e92153326314b4469b3556eab840fd0c21e8ed94afc1585c2b84d973d007ac707ccfa198b0e02a4f1858a10a54a08fe0c67c9d61c280cb6a0b72649f15b6ba258e1b2ca093042dea9c5f46394541633b2ec86a17014d230bc6ecd105b753cbb8cbee3c07b59bb7c800fc0ebaa46f9dc0335ab5c13546de116806b87a2bddac8e24c609c0b6f5f9304197a0db82d6a3a4eb675212685b89d1a64972b656dc9c6b691f30713708628147ecddefc1a18aa860a8adae579c01463f9a362976d069aba9ef7580866639c7481b9ca9933445abfcdc445678839157645b6f9949b0dbedf07447f3b78b9352915dcbdf74ad882ef2c6054bf4d017e90383a1817fe7c9c5bbeb3c7a1976b22874570436d64332500b67a511d6308ccf9f1230493dfd1cd4cd03d3a6cc3ac93e14145b7e368d87ad4fa8b21436071b910215b571658a16647bc7b64e2758ac084821ed9f00122b45dcf4bac0039020299be99987d90443e68be7def628975479732951dcc4ad6b315e19519b3f7eeb329adb56f0af64168e669a8d7ffbf51d49a5e20c3a1abd676e73ea6c0ea9ac04f7cbe3efbc8007f219f69d05deb114d617d0ad815833ba716b12c16467222189bd1102ff13ffc759ead670b5b18868dbdac8a85750fde785e1a23def49794d5621f06f9826b78c9de596cacbffa02fc41a3e0797a160bcac175a710e61ef9e5fa8e3df44590bb2951fe048140cc9afc38a1db64005f3c8bf8006694eb8abaaea883a72ec2b002eb82d5d394c87e29754bd29403913b45c39169d5817400a1f924614cf8573904e5185fb575d2f4ced6d452fb7e8d5086676cb48efac17063a740666f0a2993ae9a816535792355a989eeb3265a1c62dc22de1a4a7648672edff76dac6724b005471ab3f62cb282f46b975eb4097ec61a16b15491a77c17fbd01163f5f219c1df6b953e4ea807edfb3813c1fb4ce69caaebd4693e26089392e6ba5b6ce4c8173220bdb8762ca36d53170f2f1ebc4cea5e71a82a1ae95f4ad34e81d1b01d1484fc24f4e9df436d0c73a204297ec1f716922e77e7d8769c61e94cf51c2ec5da082290d98c83304fb0060bbf7b33abc96cab0ab3117b1bf6b125671e7bf5a6bdcc532752be94c66d0c0090750151f90bdeb24ee63c26f540fe6020cdfc12fe2f9eae2af22a68d70b15ebdc4fe626a6a0de792d8a375e826f45a848181888b855b1b73b213ee52e0843550179e1c0a68c6b44b0ff3c07f309406e75fbe33d904d2c3c940c24281a6a46aced01dff8ff905128a46165688380e0c2d209a4a109b1bda1614a5fe380d4101eaa14780a43602c14358b6567ad16b58007a1a23d433124b0d888c6ca6fb8fb083f403010f3911c06a034c033b82162f797a4f99b7ea3b6a41c00053b4df0b803d0d99132ca704882dd4d20ee014e0de7e708f83e974657f0f6da0f8c8adc6455792fd900e69ecb115f9185c626d200a45851c6ee269deef68858dc0f5309ca795fd5806d6aa533e1d03383b27e1d6b300040f2be852349237d1ac4fe182cd00eb1a5c73155c35ee949e4846f8d95a89c7d556d082427b686b4fa1e5ee01da1a961ea52b7588836b10e6101145db80d327d0584393592bff00c37d0c7b0f6cd9805d1ce70bbdaaaf68c1753477dc7a175ae0a0254471ea334755e8f07104d2733614802d63b2772c7727e22a3d2e369b53f5a8eaff865a68f90844a65f474e0ffed6b9848286a93331f8b57a515b1aac94f4b1c4b75e99a7c5140efdfb2e82d2a4a08adc40b01779ccdedda1a5fe6a4727829a79a5d082742a0771ec88b82813559635d0e758ffe82c80991473f724bc61ab29665c8480e53e4d4bb24ec1da62984b4b7799a3c10a2c9bab4d2be89bd0b971d8af469aacd0673280eab61fef85d8119337f68795f02571822c5606e764a197534e24140636ee8299a2c66b4bc1f019f9aef0a91619cf8258f972182f4f0032c9dbf71834c41905879c655c06dbdc0b4f166f62b38f711c86952943947da12603096f5c51573db48947358515143519087ba37c1c07d6be56312e29414379b8c4e1882f82fc2da19e509e400167febf68daf17409795f045f1b7aa9a09a5c25f7f21e0074921f1fd0d0f7a35dcb91a0e1640aac10c004c131175f4e93fe877897378ee476b417b1d9a682edb8b454f803d681e7df350bf4bd9e082b48ad60e02a032df1586a11a4b4916e19803ac6a0e9b2f2ec24a1e0010f0b3e12d8cc7a97ac360c586bfda0b4487e1ac1f08601acffbc4f291016d0f510368a51eba8b8574ec2184e4eec92c311c486bc1188fce8027434708a46c2c3f21e23c47e5d5a49e9c20448ffe258c98258ca7895d89905580a01ab089161b1942121c3d2d154492e6500c30032b7f1f0734b2f007f97e089e18f657d045c830b8c63b159a62c0e2360a3081bf0668c8ad6004dd9ace0c15601571b14e954c64e65133f07cc87497d7548b2f2f0e484e2e7f795cd067c464686548668f8d81723b8427d8cf85d74ade7e3a1a36cc6d140f73cfba4fa88c2c7df0548762eca7ec398889d69e9440bfd278419e7bf81350c4ccf000f2e83a5e069e2839a6fae4f8faf1f1f24be217e5438456928a1835a9f5d02df7973421512e1c3a6f7712b583ebaf2c02764d7faab2971645037b17731bb2f3d408e4cbe348a730714c7942f01ea9e8187ac4b320e20c24199ed9b549900952562fbd7434579645c1788b96ee5ed4be32339a3135f52b209c90ec0fff9e1e26a908642c74a17b79081190970517153859d2029c148c457c04fe99d3dd1bac1961faf4316f2f30c221a26846cd1b0486802e63df3143f6144555c09349abe3b8521638cc37993c48555ea5f60de1f857bc366d711f17f6890ca5a22fd6ac4f332cbb5ff5ca6a9c1e77c98c06741a34499f44d8c6ae074715a0909283a28637c1f24f5e7381a1293fa8e753d36ed198e9ea70023c3cb8be0e7f7046382b1d38d61b4bbade954f907d3c420066a9cc37c5906bc702693d6f7e9d0666e00cf7d66aaa58d5097c1b63b20e192e11214076b048b7b23c765f3b379a00b0471a5a260aab879057cd630cf0acdaac222a53a9866da61c97841ea67050addf463d95efb1b46b06a21c717624864a23aa2c512c1087530c60549623a53df694afa694df24e9ad93fb6a2b5637a187d9bea30ea90cc0a245215d26bf8cce53497bd00441e1970918924562eaf68b547b0ab63d760205927ba5451acd96d80c51edfc163247c3f0d7fe939c793bcc06601dfa1ddb2274448c3a6279076df8b779753685122d2794a99a2598b14bf2c65b7a1645aaa157a8d002eb3304a59e0412cf8d3f8a08c283ffd9ed82409897e26f5c4828772659ab347e22d7e921a3237b51b16ea134909f332474f7cbb3658a9f57bd4f5e45812aa9767b7683102993db0616192740fab0809566d6fbeec361733b792e475d96b7b26bd2553d603da20f78400fcdb5965fd0505473174ef239cf39407c7fabae2f6662ac2e3ed5f07184584dd223bc8fe908a4ef10182ed367f32da45153e78851d45175cd954598b5263540bc9bc4dd2c14ff169b1d5aab148f0d0777c1c321776f0d5dea17437a111371e9e61bad0aac889c7a93f09693fb519c96b452a7e486fe9184ea4e906a35619ac5fa45ec82e07870f7090cc049ecc624f37516cd9107920635d45641b65b45b4a1ef581807774a49f31104e6971d4c71413156b29b14f39d5fac2180d2e6de38004296f63410232d95ca1c11dc9f5b546423f6e9dd8c6e4bb659b254bcc2482ad9328d1c44384237390af69c2b65413d703c9c9e78d588adc82415b55933a0d42d1dc4e4232039f2d369bb16ab214b2869ef1ff231d7f26ce8456fde1e559af494db7d3df30e3beeca0eee54c180e6c9397894c6afe93330ba25821e23aa9edb29aa939cf10eca3754da50d4c6038edb04adbc05d7638680318f5183cbed609baaf7e3c5bed654fb335ecfd76482a56b98005c10206fd665ab4407a525087a29b3d42e122a45c9e3f743ba6ceedc466a3e86305a39808ca56c04cae69a7a205ee907b72092d4fd889c33e105ed56e485e14bea958ba6f6cecc7cee673cbc84bccddc662c2bdd334752d9d5b1f0ecd19f8e3a01e69f44a27a7ea05e16ca234905d243840303498212a83f9615a984abb1309bc07eb64ca98943818b9de52ecdba481e46d4b64f61d471af7e0c7b0e054be389a8b1b7635c284000085d81684e831b977f0a18532dec98d1883d7561293bb43949408d057815d420fa1462d3b16f417dbd0d94474016c3187d406a14dd9cf2700f21ce0c4dc7d44e8ba2d66c911484b059e29392c845c795f5e319ae10bd6fe2815d6f15787f08118fd6047ce993441f85648e3e44fb802848429e33eb08096d46aaab8807f48ef8e3f79d602445b4c5816b9942fd7075b7336709e2232cda8415e9e224dd891441bf4849f9678362744983b0e2348669bda4206986721d6649afb7817dcc1f6a50f7a411a2241984425887dcc03b83ae5f7ee320f4844dbab088553f4058b2771226037ce7faf4073f0f6800ef4a8d69f73256cb707b152bba4e470d57ce9c142d9f288fecb1e41418f2ddcd5e9e5e4d3bab4a23833d5086bcb4d97e6b549e465cf603643e3a523b48e83b7ea2f3a2d1fa015f2779fe271c6e7efd34ff100048dd842571ab924add6256e142d5ff9f192349151ad215129946e8ac62263165f27cb58d0fce9771b92fac71305d48e56a627c91dd530e6dd2d8dcd4c2a8ce0ffdb1038e75d33e7cfaf85ee3fe31e21ccd5789d53c9c9c8ac0e58456e81ef08203088b04656a2f6c1772158a21602fb5ea06f5b9a85e3d033d9e616343ce928487d5fbb87aacebc0323bcac3b41faadb610aec92a3777686a4f7e9b72d358e7c388055975afdf31f59935d8f17d9019d41cd52a4da3755bcd07dee3829ad370808726e96a7689b34f59f2b93b0331bb068990770b5b7a74abcf79241788ae04de0a3b1225763ceb094535841f5e22a9f26efe845601e7f0fedf8c0fc8c4dcaba9c98a06b8369132df4254de30e5e641ba45ea43acf7c410001e92a8dd1017c4ce3bc09fc4f7ac11769b14e0a86626c7106ec6c498ea7bf3e975eb3d090bea00ea65dd7c91808b0af15221fe364a398b81bc02c4fa6c5976088717eb70603163870be2e2010359a1b18472194128c47782a80255c8b1eb3486a8122aa1a2c80a870b1d24842ed7367245a1177a0a670dc2e9195354056fce67b608ac6dcd930e007c873bd9a3b950b9cb0b634b7fd8ebf2027fbb1e8eece5b82580edc5fe1fb5be43fad4d1c4c235f6b8376124423c2b2ada5cbc80b0a23a3f571d6e9f1dc132c3a6f0c5bf03c5be074ca18c266c9ebcc9da170bbcdbeab38ae09d061c7060f1886d5c6fd292ab2ff6b0724683b17211393e8832ba08d6544a4094ddabffd05cae4206813246f5c6c20d134680dd3a46285810fde053033d8eec23708a709b17218338e15af105d5dc938c2d4c52bcdd903815630bd56ab09e79a1c0b437343d4dab09e282a56f8ca297a8fb409e3eb22c561ca4b9a30e5aa490f90811c5b2718c290d9b4f040df8a3d01e70d4d8a0581103cb713db0d670efbe41ae8dacb2408d2d1987938651a2c2e850f523a642df26a789abe881ae21d07fb17059c355cb0a945840866309e783c5b7590829ebb1363f7d38d0cee8f05c27e102e268d47c7b52f737e0a8f942846c1f1abc1a9ef4a099dc2893f1ca01a0458735765c8c3f08970e59a4a33c3f41b870d23d9d772956e00aac8276152022d503d578927b9a4d0618425d3de94bb6ff87601e68630b21ac07eef95c708b0ce07d90a80dfca2ddf3c7213140cc1568a30596d1bb093fc64f85ee6e68ea7292d435e11ee041e13407c11015a6a07386ff668ef2bc26a7af9467f672845016982dbf37d55f368487ec60853ae288ec3037bbde789fd0102a4b25d120210fee66d60370fa2c5cb6b03832094ca8b7c5db58695a0f55a0e8b80dd9cf61ad05abe543af922d094e8f9bbace9a66b18e8185a2717ba8f94cb303dced1e55cd0ce2740a822a44a71fb5625ed980872cd5af1f126f16f7dee4d591b142ecfc77ee26d8f89b7aa5eb9794dc1b98c7e3e473a04ca094ddc8f92abf9147b9dfc9939a32441ddb816c551eff16163beb5ac283990d0eaede71a5b27803c4e21a3fa87c633242ec80af7d21883296180f29751338d08a21072cf812ad73e699ec72b1f5d7e2c62d44ebd65a7540a0da1c514da27e5ac6d3cf13ff5499c161d86876f49659e7c90043b2324730db637430337939a52f5fc28fa2b870066e3b1053fcc6b80dea7b5a179233b810443514a2bf0b5eeb1f533ce1c9c3397cee5b0f5a3cde27d6db953bdf87a7c99d10d165ebe381fe6829f61f2c60890eb7956218a0d0d6922033f802638d5076bb9489a376b6aa8ac67f25c4280922dcad9819583756eb7714b40b86bb8adfc21bc4268a4f8a1cdf12708080b7b98456eea30a55a6ff8bc5097b9e586382c47db412e438de5bfad90266ee2639c3dc2bedb3a94f9a84dc0b8859014486015382105ae040fc2514ec7802fe4044df0a3760b46439330f0f0f0f0f0f0f0f6f706ba4b6d6368024a49424a9adf153f8ee9029a594648a8433d417ce4cdc86362184d1863fda080d02010bf40ac50ac9f499f2b5ab325cc1873eb19c2fda07c868051b529a92123cf3a59820b00219ac6064bd6b472a993a476d6ce001362820011b5bd880c0183256c1a43e4f3166e855c1c61acb9f53da53c19f680d31c8fc7ef9265430f92ce750ff21afa77b0a268e8a96c73bd8b6ada6e07a77f46be99c25eb2d059362481a6aea99de1e29b8ed9027e4cadd28f8205a9265d09e29658b28f88faf7fb6d9d3a67c2818d3f8666a440c146ceee8b952b0de0b1df904bf25748af61bd2a8c83dc1260b59c53f923e25f24ef0f9647a8db69fa4fecd093ed8a866ad64294a3a6d825162c9dfe228799a9334c197902c5a24277bf55226b88a215a44537f25e208136ce4d0f71f73e275e55c82495a21e85e907973722cc1badec678997542094d25d81d913975ca5ea276a504b739a9c99e96d6f58293e034b7492b957eefc59304a373d25ff59e48f06d25421242de9f881448705192529f839074df23189521287d9e2a6d8a992378492a85a44935b6ed8d606c5c536806d5604284119c34d3bd2d79a47fde8be0840a26b2e45c5322a22a824f1354aede4ba52e272611ec560a22654919e97b47049b738a0abefb15aed14370c13268bbff6032a225c3109c14d1993d665536e11582cfcb9f9d2ea7641082dd2cfa73cca064705707c155f610e99a5434b5a80c41302a7b6be39f84a4ab77b1c3e8588055faa2053bee003202c1081193fbf9aa3db72203106cfa9317539ac4913193f107ee4a4726c30f4c12eb517a55374ef4f481534ae8535dcf49883ac4088380a12351061fbe14f5141a5e3a4765ece1f275d3b496255af0764d20430f2729fa2495b6cf6a870e301cf0001c376e84c1011d1798808dcb01cc41461e4e061e4c9b335a1aef0e8c48cbaf1284251d499b63c781c0160c68c08d1ba87164f941861d72edba183a090bfabd066edcd8a1038c3176dcb8b163c78d1b3a5c0c2efc04376e7419c3860d6263d84036c6172d6807e0d0e16274c18518a70b07dcb8f1858b21c6e9e24b11400c32eac06fd22554acab15931ecf72d482430162635879e15e14d70146ea800c3a3041dae44f8d7539863e073ed6c64b6152f3c80fc981f59c444c1b458e4c37f9e313e8a28b311c1007d6439d44cf312c37e370e0369ed2759d296508a63770c2bf4f3cb6ae471cc97003b7f77f9db263ee48a78c36b0c17c63f9a70f2d1dae2081a12090c1067662b59f8c41d449f30332d6c026e9d95c443019fd35db21430d69cdc135a5e995430b87abe0ec28c5011969606c4dfb6e64730c32d0c05d4a2633a325b520e30c8c9af2717dcde61b649881cb9644fd5f1acbc0c9143f5bfecccb5d2237c820039beede4698f24befa731f039a434e919ba83369535c81003e3be9e3f8fba06adc130f07d26ec5e82fbc43791db820c30f0d69fb55792f294542c878c2ff0232baff6f208870c2f30c1d4b485f4503131c8e802bb49a8532143e9a867c220830b5cf4283107a53c684b627761811b374c05376ee028db838c2df0d9f71b3d564add6b818f29f7c40a53da3cd959e0e249cd8c604158d0278b50be279e2bb0fa95449f2e09c9feb50297525636f7f414f42a70f1541236ba1a29c5a9c0aea9905e9ffadd53700a8cf29ca2c4b558c890023f4a5f27939f4d57d089b1838b09686087efe080b9848c28705e22483b9d37275a1e0aac5b9ff6e54cda4dc99fc0e62611c72d74c6de9dc087bc632da2e926b0aa96eb25242526302a3285ab9bcee8c9b40446869434428852c93fa4045652c81bed6d97174349e02b67b4b43e993de206097cbc48e3e621e608fc85749aa2c734b333023978deb6089c58a848a9b359f24e128111a6eec992a8f5dcfd10f820737da759c960260aa1282a276da7c552818c2070f6616363397dcc07029b2b22ed86d2b92ffb03c6f622e5ec5bdd26293ee0c23f72fc1b0f41a67bc0e7f8554a1649a23b3ce04df379695bab8f15db01373af39bdde4a4a242142043074b126f2d99262707bcfb6637355183034e729217e468ac0e326ec06b72b7aab497954d9561032ea5bc9a9625dd0695caa841b6412b92d438914103aec7473b0b4e7eedc8d49bf290052bde954f23668dec522c18bffc94549f62400a1eb0e0a44790f69af335ad690b1bc40236b6b0412a60630b1b840236b6b0412660630b1b4402362e30011b71f078059354c8316b4816cb84af06011eae60bcb5caf2ae294f9e6d05a739ad24d3525975b783218616f560c517e25b5269d15d8f55f02571729694d23278a8825dcf6717d306a5673b20031db871a30b47810e1d607c9123edb871e375a800061ea9e082ce6f2a29e615edcc0e555470ff1949b54474d4a46a233c4ec1abee6866f28e872918ebd4fbf020ed8292799482bf0a0dba5ba204bdfc48c1c90e161a3125798c82cfd5a33e652d3d9aa31ea2e0f562a5fa1c52a964268f507032db574590b955bdf40005a78334117358baefec797c820d795355ec8b1e4ac9c313ec8590d247f1d31325280f101e9d605490da994da8bcb93225ede0c109369552d1941d3d356463f0d804bb492413dbfc1f3bed9a6025d9e59dd8a273867960f0c804eb6bc18205d1a14665c5e081095427a9f157c97e31868e9580c725f88c3e22c326059d7181c723e47d06cb4be5183a2af15bd8bd9f679b128c8d92b9baae3777d4bf1843076ac063127c0e91458a5bbfee3872ec18230cc22848470c0d6c2567815f053c24c1061f69226ccd4830ae39a4a037dc8404a3e2263565ccce27fcfd82c7231889c1435bef7604b71f6a6b6ff75386c9a3119c4efdaefba57a30820beeda9b97443ce0b108268888a87f168c828722d8a4f4c42da12af7c41c143c12c17d9b56e9b859a445cb0311ac046d77cfdf158eca83c721b890f2cba2ffb628bffa0d1e86e0a4d999f87757082e87c6ce924e242f21941e8460d5d3a59844dac9fb111b049ba2c6cf79d46c33d382e094509bc54ac4f2897904824f4127a154f4e7c8db01c1d7a63a9d628e1172c5a6e0f1072e0909aef9727f52e843e0e107ae3704d11b5f1bf5ad471fd8a0db69d48be9b4343df8c04b0ed3bccb252478ec81cd173be8d254e5d0c2d17d881d142430bab00b00103cf4c0468e6629b45f27ab5efce09107fe93c633bb94c9e2c8f0c0597253bfd1be6c7d2167048f3bf0716d2d2d640e4916ca2d78d881cf509d2ce5300d95d781d321655656cb972548b4e04107ce7474bd749df4e50dcd811f75dde65532d6b8eb21073eaf280bb2627c8fe92e81471cb8bc9e665f59d4697239e001073ebf9a8e1435291e6fe06356341942f28d091e6ee05bbd72fe0db24c9989a305270c331978b481cb394951cfbfb1aa325f78183bc6d871e5c10626c9ec5e4a94aa1ceabec0630d8ca757f430b563297335f03149fb8e96df64d27f0b113cd2c0f765fecffb3d61d2c62c78a081c9299a9e2382d23a4967606db227a146d89ec528163cccc0b82921e26e2c9f1cdb32b0c92f467190819139e93791e4e7381a2a24f018039b338410d67b31256d1bf01003fbaa498a5d503a3b84b6048f3070a66c930c1d47b55f0a0cc7943456d25d2bc1e30b6cdde8cde9b1cb74ee385aa0430cbd828717380f6a7dbfcad3877ebbc085d7a9884c4a7bd0e102a3694925e854b7c0f9980e52e2297f53d10a1e5ac0721e0d2ae446aae091053e6fa8648d1d1ad444565df0c08297aa479859fc0abc84a851cb46895edb0a7c90dfde2e32fea9a90adc066d7df24ab8fa54e03e43f57966bacb4e81f7493172aacc4ba18ba5722995321f055e3325896aa5172bf350e04db3f376d929a1db7f02a73a7947c545855cdf09ac5eea076dcda263f49bc004cf5144aaccd39f3e133855f12c4b0aa1f2cc5f0223dcf472c9cddf48be12b8142237affe78d6f093c0968e65bdc89b62d23d1238b9e329c935f99ef547603f256bf650933ea57e47a5c949d34a70e71d84ca9643095633b86bc8ec24f88c9a63e79f940497512d491db7d4798491e083ccd9d245de8ed53e0d4864aade848aa351748f821d670336091a8f60848c5b413c54e7c71dc195345de7666a04bbaee7495e75868a0a23f8d0ab20624417c1e7a9ba20661d73f614c1e54d0db6b9f636b49208b6d2e9688f31b6f588d84c6434134a7608c6538a489fc97208779b8b2d0c0d43f0d9b549ff540aca275508464e4a8e11062118cf214cc5d1bdafef0f824fbdfb510b7241b01e96dd3546dc1f650a04772a846c49f99eb6090204ebc14699aa49273de9fc81f3bd1391ef3c7ee07774e3d5fb875cbbe9032f31644a9bc6bccc357ce0fd5753cb93660f5cec8951ebdf54e8bb7ae0aba2a8542a836ef6681e8cdc12cfd7e28907ce92103d31787ee91eefc0c6ad1c4c5abed8818b39cc3e3de705cfd78157cf9f8489c6b4392b3af04175ca7819a4258d990397530a49454c264a47090b68c8819598c13e84150736554a4ab27a8d5b88c3814daf9cfbe1f71bb84b4ae78edcddc089decda0cbbaf74edd0636a6ca2fbe9577da64d8c075ea5222d89fda5e740dacc8f6d49b37a806de7f84083958c839669706ae2496d03946fe247434f09b4262d299fb5eac7206ae84f6cf6f1e4a249399810f22e554d9eb32b06e19926d940e19f8902c54ee8da977947a0cac78796bce9253a5b41818bb3ae175a623e78ce068810e31c2c0ae47fbcd29497eb01218b8cd67a9fd93f70556f32af57684c5909917d83ae59d25a9c9415845031a5d602ba5a6cf6a4ae988991d6870818f9c45e8df36dd10475e70b181a5b105ce6dd369519a94e8a4a3052ee5e7fdd5dc1d92a259603746caa0eea7e244110b9c5b6acfea96bf026312526aaa7117216334acc048099ef267a5142f48a30a5ca5bfdbafbdf5b594a5025fb16c83690795ad73f431ba382ac017d098c2e291228aa8a454d19002b71f4443d787a8161b1b78c016c50b34a2c0a9260bd1b41e9e46690c1b1798800d30d08002175fad339b526a39b883c613381135bff42e7a8aa71750aa8481861358df4f0bdaf55354d57194c181461318b5cde7fe1a732c956101183ac240030d2670da83c48c6e4a62eab30c3496c029ef13eaa9367b4990d15002a7dff25f3bd2e4895d0e2d1c6178914a79b145175f58804612b8d4eb4a492b8a042e289534ea0633a55febe31928a510a071043676964ef622358268530a348cc0adc44b7a22e6b459544da051044e5409cf215b9e38a944042ec35a74d774567c1110680c81afca49bf52c4abcc7aa9020d217039ba74090b324fe62a08ec290926c1454408518106109890e495e89646aaa0fe80afd69c534b8714ffb328d0f001574aa594f4ea95e9b422a0d103ae57af92a6d2ec3b960cd0e001db63a3b761c98249d61c81c60e78136197ee62e80fa937020d1d70e2ff3175ac0b3adf99432b0c2ec45893018d1c70390691d73dd3f512061762e0d811061762744103078cf63bcdba1464a040e3068c77648d314516c976db804f4b216b9b29dfb7c91368d480dfb6d23ebf4d3d7dd1a0011fa459fc6c153263169cdd9bb6dadf376139b2e0a4de8e48f2cf34cc8805ffaa9236d7f2657fdb033360c1495131534fa65598f18a728b4dcad2d9aec00c57b063dfa52b2c58cc21cb21cc68059be49769d0bfe725439615ac66b4ccd851447432550366ac824f5971efb23e6dda76862ab8a483074bd974275d71093352c1be86fa4dffb9fd928f0a46435747e750b9b5834ec16d52da1d4d57bcccd514769e1434799fc508334ac189ec7699b71bc2724a0ade54667c932034e576678c82db6891fed385cc1005a73972758a9332a599a8302314dcc8bc4d2a5eefa5e08282179dd926a9ef6b62ceccf80497530eb641095d5a3ff7049784befc38d2e4a77d3ac15f5bf747b335c78e0381d3199ce0b46565fe379dbced83a3ca143163137c648dfc9eb7ffda6a08d8d8c2c603b6b0c136b6b0d1802d34c1675226f72f7dbe3023135cda10845ee650113bba0516666082b56c11746307675c820b41648af9f653841cb28019966062daaeafa0aeda2cb4b1c5186654824f7192501339c434da86125ccaf190439b9981044617ae6312dc067da6a794d6955f39b4ba0b303e083324c196de0d495e3a1b21ea48b0935da2e64c419060b2281d31974ee2a6848f6025fe577a9852e121770413728aa5f8e9d35a4b663482d31bd916538f8e3083117c74cdd74e593daa25bdf02202691b8043cc58045bd1ef2aa9529e79438ae03557f2aec816edb42711fc5a0ed9d6e2410463216ed01f2166d6c839041f84c4cc546117ed5a43703e7a3a53e45821b8d0e3993cab4fd54d08ae8450bde412f39f84e81003479587d1820d2462c62098a4b1d2d5f34316e90b82abd49bd9ed8329b33b10dcdf6e0ebab29724150182fd78edede611f278f40f8c69ab3f75b671d32a40c1e62366f881f5ebeb11d2ea2c5abe0ffc55c7cafece5515743eb0669182f0f4901df2b3072ee5a818214e1296ffd3039b4c3f72929a5ef3a57268a9000538fe781738ae3a2001731580e185eb08402066e481d39a1154d2a022a6d5f1c05d06559363d21a9edf81ffdb20313ceae474db0e7c2cf1d7b2cb1dbda375e03705b5187445f5e9920e6cbabe9873fbd69412e7c0279945e65dcf4cc927a6fd8b15580000aa98210776426e491294478ec98f0327b2e385507582039bd386beab3db7d1ef1b1855c942afc8f7b6f204ed08c60c37f0b59752c5bcb95deb6f0393763d5f48b55945c6b0819359b2fea8fd2079b26b60b744e8babaae8d9b570372848c41e8e86f1a1899befdf2539e986488063ef6e556aefc9693b2cfc08714da64c87631490d320397829222476b4f2caa6560944ad31084480c79f46460cdabb35e8292a5427e0c6cfb65a618835c0c8c25193a657dc8b1b31e0636a7dad0621a4212afc1c09eeddda8d2a72ff079eaa3a36e4a2add78814d7709a61642aebaa60bbc78784efda6e102a37394741fbf32627e0b6c0c4942c98c76fad1d50223536df69f8e27c966814fea937ecd251618cd88a9be976f17b91c5a27f8828b1d3ac648ae03479da0991414665c81314f2999e5106b7df3de0596cfb002a736a7ca88a12be860e238c1175ca0209d40877b5186821955e0728e223d847ecf2552ba382ec60e1c28e8620c946563030f6080316f30beb89f41056e83dc8865a332ef1de98b8303477f7170b0808baff2850e0bfcf109883106056edcd0e128d071bc281670f10503c0831953e04678091169d3bfa2648614b8b4416dc40b966d6b93436bfbc60d0e9828b0af395993ccd2983a0a057e948aa62a3fbf3c98f1044e099154b9b27437955608339cc0bbea086d79a46e507130a3095c08491e6b27fead884ce0a3ed77e7cededb9212011da7033798b1042645ae98aa4c6707515202e79f747d103a1dcc48023b69b494bedc9954f410099c52724379d01dcaab37338ec086c474ee1743974660527a8a418847af084c5caffe4a3146df9815e30b2e3870e346210263fd357a3b9dffc5b0cc60c61038916b4cc4d19f35e9940b6608818b7d2e4a853ecf82c0a6aafa87851ac78e436ac7026a3003086c28c949691fd359321983193fe03e65f41cfc7447440f0b66f880519372fabc37cd49e66c1166f4804fca247aba8a179ec61d3ce0c7bba36fe48ad88e2f8e0e318a0161c60e184f6a21aaae9f04e1660e66984eaa246da9949e03be72c774e1a9ce269970c097dd56ff58325def8a0d66dc80112965b1a0aeed554b1b705a7931e36a8ec9d3ad01e76a75f913626d0ce50c1ab09ade5a33a485560b0319b3b0c3b36f5398c9dac5f8e2a4d3012bb2d08245b5bb78569b2423167cde18740e1a3b5870a79dbf6486e0173b5d84e185eb0b64bc828fb12f0184e025c5531a565223bac6c60626604307c1ba9f106db59932725210e64caa83d0fb2924ff77081f81e053df5396a067396777820f40f0f9bde6e33105b533b1e1e30f9c773495f75250093efcc0c7902fa5595bd9c79429193efac0a7d7575aabe8aea0c307c6fddc4a3d85e41e58d3b4906741add6d71f7ae0cc3a06f724624a4247ff9107c6266ba747dcc9a22d1f78604c44fecf154d738595430b8cff820ba2838f3b70aaeee65e5ffa473d36b610400e3eecf0f9a638d16dc4b31c5a2b383a70b8bfd58193c147a452f1e4093dd1814d2e6abb746ce7c0244d49f7a27b7268e506bc0b14e8700a9c073ee4c0a8512f21670b922cf42ffc04a6031f71e084ce6bff4da1b36f0a072e2328ab0a96477aec38f0f1066efbdbdb424e11512c6ee0bddb46787814d16fdcb831838f36f0e6c9d2fac95117526e470e1d384e7050290a6480021420a6c30b2f8a083ed8c0e68a14a93dfff4d28f3570b5d9be59faa5e5fb871ab8d6f02c916b64d221fa9106bef7bf9392b42f42d51f6860474566919474d37bf6e30cbc89a6cc6a29a6f4261f666063652d4d32bd9652df4719d8a4845f977a503a499e434b4df041065efbcc1c03dfab66427e4e36494d7268a1e08ba303c78718d84b217ed9b985d8df8ae1230c6c6b2e4fc1c3b4cfd361f80003b7a75f728ed61633828f2f90f369ce21626e8ba60221c0d11f5ee02bac56c453e66f497268e1c8808d2dc84717b8d2bd110b1d65ee1d2e702a9ab698d47e47af6c0c1f5be0a2a5b8a9152adfb82106185decd80f2df07ea3465e8be6230b6ca51c44b4ba5ae4a4fdc0022337d3b78eaedaef908f2bf0315f658ba57b52d2b815f809b2cb3dfbf85105c63ce43abbbdd2967e50810b5a2c988f58f89e3ea6c075468f9e3a434a7c488193626a9d7a2df2c75013cf131f51f880427e31eea952761727c761e0e309fcf9dba64ebaf7e39999171f4e602d87aadcdf4a11d9a2253e9ac0240b21656b089120a41f4ce04784452dd31c423c1502bb858f3bc894d2561c9d600ce48de304607461f0001f4ae0aea35958974aba52b4c24712dc16d3bb112a6e60023992188701376e4ce103094cd011a39a7dae12253f026b2e4994c80db5592123f09573cab896828e6b8ac0a9acf02836e61584ba880f22f06d2a78d6d05be1ba6b213e86c08798be1f2a3c4d16c78381c330c013172ad0e11a2895237d7174a0cc0d7c08c13d7d6abef7dba1a4021d636c91868f20703ab3b957a94060af445bd03b293d7d82838f1f305a216d52952e59d5ee0336a88a646eb71d64090350838f1ef0f93de7dcc9d6d5d7c4032604f53cff705bf1b1032e8390a20e387d4164c93622fb8765630b1b5bd8e8557ce4809718ed3bb73c53c50f1c70c2f4a4511d947e52495ff8b8019354445f93b89fab83884de0c306ac4d7a2d21847077cd7ed480fd125943ca8bf9e18306dce8186a84ccd8c13212851ab3e0a2272931c82c416551d240b1430d59702986d6abfcf70c41e9c51874a8110b4e459219a9d75cf72758a8010b6e75344c52a6e41eabc62b38e1f527b45e7ccb9e0b430d5770414da9c896bf2b06b7157c6f1659fa9e55f28face0e257084144ea5bf09c55b03569432c846de88b2ab8eca0294216eba81b4a051b64c648769a6dfc34d77109811aa8e0f5b44e8ac1cff2f9478c8370eca9710a7645c6cd8ba233342e7c8c1494aa610a3e7752dee57c8b399dc610354ac1e6502a8927350bcac73cd42005df159a6388aea87aba35d41805efaeea2198f47cf92d68a8210a26ed56a5e5e46d691dd60805e3aaa6f9d53e3540c166f178d27cdcdda2c62798bce973507ad74ba9c7136c9e7cd283dcd4e804e71233aa44bfd0a12ca8c109fe6d3c27fdfb20121cc46a6c828de6f76d3aed4454870b1d61580d4db07dffb9419b094e78d69ea9742931b939b42a063530c17a301df3c70fea4daf4bb021d8764848a633420d4b70aaae9e63142537422cb750a312bca8f98ba81af364419460ac430ea62fae5aa831092ea4cb9433955fccadd790047b4ae8f04832640bc2ab1109c673ed8b9ca06cd4cf1c5acb831a90e0afbb228f0e41dd9258e311bcd9b8aa988b0993780d47f0d94386beccd22c4ad70836dc4f07651d32a10623b8fc20ef42d59563c721e6801a8be03ae7c5494a868fba48166a2882cdcf5a9b58969e16fc070347f72916811a89602c2421d2d4acaf236e705003115cd22023c70c22631138e3a2c621f8ce7b4994d68a212b65085e23778f2cddd933a61405d42884f9cd27082183493381d15b83108cdc284ae4f4bb210779d526a0c62018c929df7e754a923e6b420d41f029c2bef2b90969a5d608049b3428a549281b108c5ac664b5ead6f803dfa731dbe990fbc1ec5e55bb27cb1a7de0b49bee7ade4e1042abc10746b48894d56a4165f76aec81b37613ab7f6b7d8d1c126ae881ad4a6f13cfa6b684d40535f2c0a778a523a2f7d608ef0635f0c0ad69932631ff8d3af91dd8eecc3156d96d07264b0eddcfd014a1461dd8607282bc92acf59dcba1656450830e7cd6c5a45d42e4dc9e9f0397eb94ac499e9d9bcb814b9b344aee7890691407b6ec4dd7f78dfa2c7138b051d34949195266c4dfc095e63d91e2686d44ddc06778528d1ce936f8b731d69a8f121bd86a1d393a5f7bbd92d6c049c8912bc4a44d7c4b6ae0e32569a93508a1cb4a6960359d8bf6efe7cb104203b7eb163347d252223f67602c47d2bfdb1c3330e66e5b1dd275828897812b2522932599f44b78c8c0776513ca3f6dfc0d9931b0daf9b2978f0eb14e2306565f5d6f57fc523313064e2591d7bf3ed7791c0c9ccabbe165ba52b4e80b4cfc1b61315a1ce99ff50297d466536eb92ef0a7dff6c13ec7acd4728193d183f9a515a13f64b7c086eda5c62879d2a6b5c0698aa2524fa259e0b76b6d62c8799983140b6c348f9f4765afc07af6fc31c7d3a52cb4029fd3f44893a0a24d50abc004212ca7ec954ac94da9c0bec88d39a81016c934054e66bc1249e74b152229f0e51a4183c8a54382320aac9b8514445fb9a832a1c06da860aa2c9d89d69fc09932adaba43b81df51ba62cce1e9d3a8097c85901acd6702a3a642be3caafde359021b2fc6603906d9b7a612f811b58ff9fa25940476336af85eccff202430a24c9608d9edb45a47606d2f7e0eeaa1e349d008dca60da57ff222b0317f478e9913811f573f9554fa29350e813555710f293c21307a27679f9c267b8c5d1058d5ca9072b4030267ef218b12964564d00fd898e29d2afb33a7d0075c6ecf65b2ca2fa3567ac0e9602a7fe2a4f080c9a93b9a49ba3c95db0ef818546dd7064df144a603c6bec2444595d4ebb11cb095cb4a72f6ce66711c7041cf7c736faddda56bdc805327b2a434211bf09fa7314f87906fda1ab0935b47ecd574f4b4060d38f513aa2f79c5b59c053b6a21efddc9a8db952c180f12e26de6c7b87e2c1811d2858a9be9939081052f31497c2f9d4235f90a36bbb3baafe94a52bd2bd8a0932e765d48a14c6b059393ca9a83b5ac60fd4cbd0a3e4b08376d49a7cca2a30a4ec5dc935372489b924d055b7d49988e39890a36861092734eedac693c051ba92208d19ef89f33145c0b6898820fd26209095ae3dbc74bc1fee668427207d34c2229f8d2f9d1cc64879c9a8d8253dd88792354be4314fc6810c9724d3779c943c18b44fd4e32dea0e0a2c5db20ba235d2bfd092e7708393c8614c26ae409763be4ce58163a69e67482ab144bef7258999887138c6a93d9e4ae4896acd9049b2fa6ee3e32a5c78c26f85839ebb3c61e69419960a4efe51482f4d2d7c18449249172df622ec1c9143cb3a6a5aaac5a821149a925510a82e07d726b5ef2afa0f94070266408f393f963ed80e02f89a0a1f4d286eefc819164eba15ba134c8f88109f69e3a338d7eae7de0542da7de562b9129c6074eb4a63d95bb228910db03233fc7de4fdaf4938e1eb84c5a73c84fefa0993cb02ad613634af1c069c72da59eb9acbf3bb0f97c82767fc60eec06491b5ca3d8491201831675e03a8a75e79c2157254d07c63206a5936c9115f4670e8ca7ca206388460eecd686cc263c64d17be3c0dfa8e6e63af9164385031b24e80f2a47c9667dbe81b549b9574fd44c65ee88e106f3fb6fe7f70b2e8e18ae23dbc06ae44c3ea2d3a7feb46003934efb95fefabf3f79166bb1063e8b07f3ed77b38fab811321e58a1213d1917e1a98e42243772cb9a7bfa381b7092149db889a75e93370417bd0d4694f3b7333b041a8bc1a2f8bd44b3965e064875b2acd8999364306fe4ec99222bdbbbcdb31f0b931ad7c6442a08518388f563fc294302d790d037b22af629b0c4174a7e4d04a2dc0905a7c818d18a225a8e4926397fa85165ec01468d1852b560c92cb2aa5985482a50517d2a5ca936184716e145a6c814dba6b9fbe628f086e0e2d1c8866630313b0b1636c60021cb05068a185d3220ba605160a2981165760b466cfdaeaea6b8bb50019066861054ed6d85dee5e5125ea2ab0de952e89f85a323df4002da8c0569508caebf2efd42b052da6c0ebfb69a5dfd96b74a5c008b541837e8f16b4a951e0b3e429b5dd5947d4f5042da0c0572af51aba792d9ec009a54330a1326f94667368556d2079181ab891650c5a3881b1caeda7164f4de49bc07a0c399e3909372162029bdd2445123a889ae812b8512926124f25707d2bb12cc34f021b39078bb1d4048fda2381efcf9e11d7fa2370595a7a1f9407f5593702ffaf49e974bf6b99ee2270359aca6dbcb75a6b22f0515f84a5ec8b7eff10f8d29f6ba38a84c0c6b21d592f9a93dc04812b619bd12a757e3281c09d4c3a7b24d30f98a0397ff04a412b7dc9077c8e7977cf54520fb8f748f5fd67a1829e079c1e57b318f1be436807fc4911ef5c121d305a2cde955273c06e0a1952c896d702075cf95edc4b934ce710a4c50df8ab7c93b694bc95dcd1c2065cfef71ffd8a17f276b4a80193269824cd7ac9bb375ad0800bc1268594de654a6d66c107ab7849c8989105dfd1ba946c9360e9df587097b935686adefebdb0604f8b8b77d03b4f7b5fc1267da3b39ff0cb5b5a57b01a74db238af58ad0b682cda3fa94d0e21f27baac60b3630e65c164ac28d255b0e1a643f2dc54f1185505576ba7b3aa8d7e8d9a0a36e29dae1ed724ed525470273db55fe490ad969e82b118628c94276a0a2ead56cfbb735ff452701e528a112dc7cbf9430a2e4dd4d39eb37b8898a3e02a2fe535756ab52b290a2ea58f299d122ae2d6a1e0ae4774c49c82f60c0205234a63f013fc089946a8969e94a3e8095682870ca242ec049b3f2775a2e3a7d61c7282cb7173b668fa4e63ea36c1778908955384741e2f9a50d2c84efe37964cf06b9d930c15b1eaf498e0db43ec4b37e61fb376093e76c8912442fccaad5982d1c8ba5582df929a9ac3cc7d45a3042742c42cd974c658a29a04a34aa62043ed2da8b52451b220e3a75e5224b8d6984bcb74a9535a48f059e47be8603e828f29b2950cd13cfae8087e35457d75e27b758de0933c53afdd63ed298c60a4879c91b2bc736364116cac521a74288b7944882238cf9932af3289cade26825395af6a3bc9a415728a08ce377656804330c9e42695f431c5c43704972ea69cbadb7527e71482136b112253660eb23d842863b0b89a9b6206c146755d13a1a729a95410fce994d74d9d8e869d81e08465aee6d1699f441010ac59778cf92b850afdfec07936d7bffb2a15acf30393d44573d3362de84b1f3899f4f5f8ed5e755af8c0b5bad5e40bd97e54ca1e38bfd431b5edb607ade881310bd3e6eb77af21250f7cda969cf8e9747a53e181abde9cce844a4d975377e06ceb742b857765b4ecc09a4850d1939cd4b2aa0337d1dd4a3bdd4a448a0e6cb699aefd909a03a327eb5637269996037feda6e63147f41ac5817135dfcd6295f47be0c009fd953d6955eccbbe814f4a081f0b4ae77817379823c970cbd136f01d5b37828a101014800dbc29fd418479148976af81b111af0a3ae389f4d4c08a0c7da79a9a543594063ef44ee6cd29b134b268e0f55465ce9ef9b7ef33b03af2ba479decae1833b0d53157363dd2b72b65e02b68d357593793c4c8c07846091e54ea1bbf740c5c9fea335da23fec1403a7424ffe5c4a448b2ac3c046cd1959ccfe4566c0c068d0567b4104bfc07586e0f182bcc02599428a88f9eb02bf751593d2f47181b3a4fbb2ea2d85e5650b5c65fd20d3a4ef85a4a205bee295d41331bb4c2959e083e5f62fb10d8b55c102232c8ba5ce26bb0227b3d4fdd52651619a15384d1fcbdcb2d9e8d1aac08d4833952fc92a591915d8da167df7a4a64c3d054e7f571231674b0afcb6bd8898c98927ad28f0f9233306f118a3490b0a9c485c09b9d3e94bb39ec0f5c79c325dccf3f5ca09ec579b664b3781b5d12d66723386f299c0a9f79026ba2a33be043ef8a4281a456c728e12b898ff944e891f1e9249e0a3889853c7cb413f8804b637447e5fff09ea1d813b5da739067d79296604764c453ce6a0725e844ca000456037d4c9899e2775d7446093d0bdf5214534290e8191e32124a4f6244d6542e0b373d404d3d0be292e70b818a71e508020a0955a52ca2ac1c3ddd328fdace06b4105000277a2471c42017ec096674ada92bf55aed1d1a0003ee04e64d0a1f35e6efd0d0705e801e7a22663c7f1b8800b0dbca0003c602cc80c936f1253e5ba450e0ab0034e9dc64b29b5c69c357643074cbc8b41df69ddf353b7a00039e02e549b524205c001ff1d430711537c741670e1050e34c60e6501175ee0f03f0628c00db88b1eb3fb4325e8e5779c85c059060a6003ee227e964eeb9492fd05a8011be2e94f5a2359f20d0b4003aead455e8a36d3eb3139b494371815b8fd9805db29e6383abd6e0ed5f9172b00030718626091057cc8828b933d66f4ca3e69f7c1b1e3c610e368a0051fb1b0ac7478081e3432f880051f4b6b8a93ce7292267a059f49a72821c6f6c3157ca5c66c6923ed06f9b582bf2b1d2a241f3f58c1e6a86f4274b86afed22af88f3c42574cf1c775f4a10afefa46a78e54f57ffb2315ecb95d7896d2ec6b3f2a18ddac9b60ca7d349a4e510ea6378a6ade146ca90f1643bacd91d4a8146c5b881ba6695dba37a4e026b5f9dec56e14fc8d9dcedd561105973ce8cce9db534c950a056f6a5f5782bec9a0ef0728182147fe5f0eea4f70797793fc623e3fa1798289924d4bc7ddceecaa13dc7a2475f31141098f7c7082cd223429996983d2b13e36f1a109bca13a3e32513e30c1c5a08386c4bc5832dac5c725b864ba7372fb6b89d1cba159a47c5882bbfa90548a64f5163f8756a9faa8045721b46b497dfba0e91c5aa4b6bcc1f8e21af04109ce638c28d174ee9849e863129cde04914d968e24d88a112346bf10178f5d043e22c1eaa411adb316f7ff3e20c195da7595eca08f47f0df1b838cf963b6baa00f47b031e45831a51311a34a1bc1bafb687dbc5c9bc6c2850f46b0934c5dbca4758c41868be02ce4adb4bd493fdfae08fe43ab55501ac2e123119ce6fc1ad48990d32d09116c12c1f7538d8cd4f71e828dd949dbfd53cc0e1f86e0b4585a2abb0d19ca367c1482495ebebe6fbba5806fdc08c38b84868f8c7da8a4f024e25824100883a1703810808102e603e3130000000c201486429160248db465fc1480034b2824443e2c16281c2014161a8744c24028140608038160180c0884c2e15048201ccf84bcfc23fccdd9b69cdbc999e79ce7382bda9ccdcd90cd990f73c6dcce96250f9f0356c899d94abb0c693383abc7709596ce29ca2e9d82b9731b616f895c47837bfccc3d8f7b0ed169fade7f1c99303dde814deb1166c45d17335dcd2866080662750208a22d4acfbd8604ee0af7cbdeac53946a23355256678910c3f65c9854a1168be89b326a1661224fd6afd2d834fa7f6ba163cc780669085c05be215e24f785c3c2091c82a5b011009d7eb9d6810d19965687ffc01b79050cb0f5b5b7672bd90d49735ed176c4c29d8d26ff6656b1e3c5e68d6ccf32d42c88053d878cb4d89b356121c6e908fcc0d1bd5cad382fb3b985f4189e1093ff24a0eae6b9fdfaa5c7a996959b0743986eb3e77bfe4605f12f85557bcef57a853fb8c2e504163bad26ee3c3ea29dc52ec3ff66161d54e2d4b1c5b3e2b1d8a8fa2dc1c74d655e42884f4d4810f3bc45686720a124b49317bc4b00dd21d03b6a1336613f500a90229a4311fc3d17d50eb0280a5da010519193d28dfbc28a775be808433e704c21984d39b0c669aad8aca0c5806e182d8d867bbadb9df3cc3f86b525e4119e3efb0c8d706a78dfc143abb1fe5c0be0c2c521203af53ca677c9ba39e3cbeca9f2bebc27be75013e16e90f2092a199c3f5820ba5d5bac5913d05fd3d76a36e59f99f62c3158687dd53c9fd0d0f09e2b431be040f1436264a3ab96e5837d71dee86d59bb134156ffae571f766bcf11e8cf7cbbbf24c79a93c40de9537d1bdcf1cfd78a3c48f9d5008e8e819fc591a6ada7061db859d298fb06cbd5afe4de88881a8bb791ade8ab79f6fc28c1d2a15f31502527a13e44de07ae7b742f73c0bb357ef11f662e12d4432e6e2d6e105279c8059942ec486ca86b68539070ff4f306dbbcfb8c7f6469779a9ad6d6e460e0f52feb163274ec32f5c5b8c9815381b6604c5444de1d710bba30f9860b3a6929b90bcd9de75673efdc656e3f37b43bd4ad706fdde56e6f37843bc48d7add0ade9271a6ee3e7ea8e28d8a169d272a257e744752c67b9c5f0aee9cdb39a6a5d6a90aa9876c593a757dfb6dfecde67b6c4b5627db17d51a7852e9d76d7a612591324a962f1a4ac820d435cb2b47c6a3885148b1210d1152344927471f21a5202279479bd1ecae196a8e968b5c85bba4161d4103d0b8457b982332b1d19553c8189acb7927775e74317ac0cea48f51a9c552f9a8b1e799c1364f4e29153cc4d252a0a897a4a537a33a6f15869f05fb91614864694b76b2e641ce1cb30551477508c0e4932148e488940265835ca018c424be145e26d16c32e43082fb265165683c125a0171f9de345d0d14722bf73ccf4b5eef8122d7d0dd48bb5130da3845b20b0ce1fa21a9263f386e59ae48006dbd0dd2fb4101030163b313ad0e806b7b9135c8d4e8b36d2784b8b4ecba2f7e6fbcccb7bc7e71d0cb1288cf4d76b2f0555c40df0435082727b1ed7296cd0a83ba6c4df8d4210763e4ffece9e133f723b60a90efc57503079ee3f01ea559081746cad4dca70c00ea7d8b58dd8bb06ee401d05e25a05eb73dd78d934ea15db7a1745097603af90d5070b4aea9f052948fb361c7552134e496f19f0c34dbb77ce8c0d557251e7cf83215a122f935e5bc50db6d668269bcaab06e8297114998b432c92ce1d823e6783c1aae200af5ef57218fa4fdbf9dcbb015d3dec5fa2e8ab104889334ca906cf1b43a385ea9c07b2c09dfba501a262c88e36d4460cf7c4c3d4c0552c389c1010e92b78295a6b1e5d9495c0afa9a342b588eddf8989866462f8b5b2440cd9061b5c69132b7c48b483c7767d28780369438019d780586ca75b854e7d910baa21e6ba3e173eea11140c9b570b904fd41ecc560a217958c710becbd45d5389aded202876af974244a67c73952ad23619c2359f59b6dcfe3bf27b20851dad1a8b476a1b437443670d596c9630e547ff648a3749d2e00722ef2c6299394c8e993409bcd1d51553acfe093925723dea2900c7a55f6702e5f1f0a5cb3a2e8c8da7eaf2b330320861109db08ddc8add9f8b2359bb21ad000cf634ac2f598b19572b5d7708f19919632f6ae6ee4c0193252aba1ef3f8d4c441fb35297b165a611137747194e85e0883819076371a71099cc6adf9bdf4987f02ef182fae849bcddee61fa210009f1bcf999142eb03226982cee916ab5b02d44c8638000b246e30d3df95dbfa1be68858ae49e3fd0867190ea866edc860df2691ead54994a8555a2810f9e03a87b8adb40b4b81bb791cf86d3baad762f010aeed10190c80f89f19d2021873ef935831249264f3377a1dc9c5696c5c95484ec51fae7c0ed7a521bd037d182153ddbb0943d6f5440c1bd0ceca4fa4256974d0162b8b551462b1798ef7f3d5d5f44ca9d8686cc723e80a2c24496afa0f1d15133b6cc24fe824cfcb0e117cfea3abf962e31cb05af36e1be6dda2c07ec38d240c0761465fa1274c1a449b078c7b17401a5866d7a3468687e756852de462442d1c01cffab5d7f9e8b21c98e0b29fb214d56d44b4e6d41485d8d840ea2496a5dd51c30814c6f83c87ada6c8dd6980db039807197050b388d3433f6c9168949cd2d6bd8e0a70f29b7535694a600fee9b95fe98d8f1a6058cf8907852b3ef7ce25afaf81217188683c5459a21db11b15543cfeb47038fe752987062bd70956d0fa79660bfe538c4d10506ce825ca1ec9f18ecfa36c6a13045040410b2442bd5599307717502e0e697eb016517d8b090b497808392dc5c5a9bc8bb9d83fbe2f26606ab7520f9aa8cab5345cef62ea4796f45b325e6350c351528a86e3341311be125464182f4c566fbee8a3685c6452dec82941eb71c65412a74d56baadff13ab9b36d9c4a1ee4ab0575c83e8b488d15d9d3da1728f98bf1c840d621ffefdc294422da4424ba5e55013bcca5d07a3841724605c56df971d060567a16871405fcd63ae10619c3b8a9cc4f2eb4f6cd9182de961267e1278c9d59e06d00f5ad3a38fc3f19846635b4ac57707981c1b374094bb306cd73a02b7ed97a5d36822429e2c784579fda9809789b0e06ae0114ffd2da224e36f20126057b42f015905e4e28c09a4c915f4e2cd73f5ae06e0c45fe7bfd04b68f4267c235863e814c442c77631c9f9a34274e0b1655836d63fd7febf448e5ecb4e9aa1f103b0aa1bd456b3727e60278f5587faeec1aef32fe58362324bd29f6e4d061b7a9535998d0965b54089746c243fc63d7214d999f9a7f864c94e08ad517a298a654263eca4912262adee08a1b85a85cee0b35e8b67ab9c8e60216e5a5b67aef7004410477468d6126bbc8a81240518a7332a8a205f5dada0225f270174a03284325868ff7e2e03d05ba1b6f23533bf0a2aa8b73ed36dcc197ef27d2842010caa5538c60eaa81338aa1a559043f5658963ee5da999ab2847181ec470666b9ddc796797f29e3ab2270833f833991556e4b030611163d0ec60baac74a6faa4fcc27c226b87b2700c9e5a6cb4379bd3aa6b8cc9254b43eddb05e61c43ee4a91a1311891fe33f358e8da3178d6b49eed1ce3ca810fc8f4675039a7e6af4340a0e8440a270e14efdde9276a19776a26df890ee0ae41030858a7bbb4db02d4ea274cd846b7151ba7449874a4c410dfd89112d21f9cd127049c80c59dbdc98e8a95e8d72bfed25cac77c01ffb338001e970f2beb1e1a1c812977ff27238001b4d8522051521641e1e23e3480896fcfe198f6855ba8f995bf1a80e8bad7f4adc6143967bb1806abcf8e590abf26801b2244ed42b22fc25a3f248712961bece57c951a7e6485e4e4553c6ca8e61dabaf409bab6bb65cccf867e69054d0f621b84066e0f219d3f34702d9aa420139d0e421509e577c275e59fcf874c6acdbe1a741a6102e9f9464c478da4f748e9e83c6c3853edbcddaadc405b6e3a0bb8cc7f1d1dea915fc142bb8868bd9366b852fd7918f263e359a32d118704358481daf2149749c8e0003034d731cc498f0963b52301352e8d97f3528518e120c98f967812fdb5e7f95c63f57ee74bcf85af6a46e4d019e8a4b4318db5023c221cc603934afb720f8d4dd6fb2ed54700339a87fdf43379ddb237eb207ae32695bfdd68bfbc8fddec44cb117b95a286d37e49eb5ae809417641b0e7ca3fcfe6c6eb8ff424da114e99c39f4674c7c55376e0864a7e7ed14fec299d727f223e655b7caec63ab0535a3d14e349cd31247c70b4f472f4577851691f627052e77b25932965753d54abbb43e051ee0082c9026c74c450ae42203b2b47bd9c6880c284a0530edba2796dc8962b62155ad3dd2d6f16cd0e7036dbf80976396180a58f54e25c63a46b57cea14db74d96ab12d3c5e754a7e3991a0f3081a5105e3ded0cc4f3e83e9522a11daf3d8876f3b1eb4da137463fac6b2858bdfc14b1dcb80d0cc2dc3ea1e6c4c21fb0db448df5116580b6e05c40590d9240cf3521a4e8c2352f837302892e1a94c834369f51057c6bd385d786d3c97cfa524254632d407bf81cab69648308f9bb6c81c8b1daffecdcbc1a04c040d53b12e167491343c9244bd10ff11e5f2049c6a3621a5dae536c88de633c4556d5253f9d170516d98e0d5b654e72527d520aa0f16ad7903bfd25a5b1837c20d7ee24bb6e640e5cf09d0f98883787fb0cae54ca35f87d3bceb2f08857d634e990ff8b2a32daff820fda3a9423459f74c0e662ec0ec29b68fd39ea8c840160a94ce98cf1dcb405cceccea4317f8c473c0ea007198e6880ed9a8fcf4a83c2fab7ebc7be473aa0136eec07269d701849a0563825409352a3b0104a167275254581b92f503b8c2ea8518f386c8b19623ac78d146d58447b1d32fc08a355c93a9e77e16e99a9ccbff2593a2afcd7911958e9c2a3aee748522a3927e5e8712f11214ee84a11aa37e3a19365e9fe1746c86a377a96207a4615932a55eb100ef6ecde046f73e6a2a5b309e53ddcef26ebc6eff80d1e1935d93590974c0f1464dadf78ac48c95ed67f996f064bb784496ccc348cbf448aa313222be76bf78f292e4b81c1759dce6bb36adc3548e6825e20dee8fa622b0991e17915b8f8c1fdac02257dccc73dd8f002ad3061af9497b795b4d80b0084f9db04fe7247c9c0ffa97804feea0a05bd04ef0a0d7a6287c003033e40aab411300e60405b63ce460cbb452481bbc0e7c4461c193649fcab96ccb365aec8185e03c7c7cd79c094ca87856ea12ddc224fe4a8652c8a4b7126d55c0a6b7d70d25dd3b9e0711489252245dc9bda42d29c1290e7d5744b9225d4b01246aa98074f3b4c08a8adad3d23eae3cb180425d25d24e5ba7fa1392e3c45e92c7298e5a96ca3a719510ea523a78bab45a4c5d484f9ce679f5c7e26339f24ab053759973a37cce2bfecacb5361c0c0bd66a9fd3105b61f8e6837a87f8a2254a290ea10a9fe2d1aa7f228595dd2fe6f670b2389c9baea04359da53f024271ef25fb93fa9aa5be2662a96fff3e7696a2686435b2fa4a152f89a0e49a7cada4b30ad8c48fb003fc8158cfe90d24b314a0efee034d51f831a85f8c61e2a596954d4d70803ab3f3ec1b4d73a4bf8e55ceff43120e5c184d88d16054f6790590ff38ebf95f2201ec106305819f1beec2b43288995be639f3cd3cdcece819ead019ed9c7ef93eb51cd0d6f8f755420932c72769ce1c4fe8f14356b5973fc67714335cb4f3ec88ec299de90970f5dd73418865d253dada3aed3f96b829bf2284bc8cebe52c673a8f426b1888e493ff4cca36103a1009f3ee2c537e14fbb89d81313347fe8370035d514c86d40bcd159683b6053302ff9c96d679a30d68f33e669c78a1dccb4f0320df894e9a9b54f462d27ec08834efe3e400d221869df9cd1c220c1d7e235636327b4cdcc1d085beaf1c2113c30cd4e495f32e2bc5cc99d1bc637da41ebacdb543a2660bc70f20e9df33a1ad3566dd64a21b5475efa082e88b0bdf3721601ecee0025e3dc4deb67907de0c53efef0de38b020d7a42e71d4c8898673d0e1125f36859e1f51c1c6c185c7eca1c289c5848350a435c048cafb790486545e2c1e61d0c5e7071b13e7363184aba2434849911e806b327f01c4b58117ad9a256c56112184b25ff1187f54210d93ce74256f0f48ef5393044dfafb6751e71d80bd8822b506472b54caa1199a8817f15dcb0165e31a1a9039a00f9797536944cfbffb99801671d38f2e91e39a4efd422a11d0d8715c17562f42613a7461f90e6c42f9a74d86309d63c1c4e4877bf145f5a7a25256d64268cedbaf2c7661ff060d8207405e31db6c642717a6316b8cc16c27e944218dfabdcff02df4ebe2e7e7165934937e983215e16314a0a8d79cdcc05286cb6cb894de0d24c0069a9d852e14bcbffc6077234a2366823ffecb7f598eeb98c6ee7039c5a890baab20df2a49e4b2a59766d26480ac8330726356198778953cdd4b3eabb5400e8ba341271c30578f3f7a18a7a7d6dc6837beb0659bbf598dc4109112b7251148c97a06ec0dc844dc50f234e5258b828d78a6f364ac3261e2d2a60ba478c32af82743548901490f42eead5dd515a1f97d7a11704265f15fb8b7af19608aa9fe8509069d66c172e09f8291af272c3effcbcab6ab9fca97076e60628f93afe921a0b7e52b3748f14d5e1e3491281a5621d096f04a066f9fc2e306481aa260acdbf4067db93ca7a3f06be8e6a61deb5a681befc1d310b60a86c2c443a03c720aae06a260ab51bae780a53bd0222ba3cb4f927769fc084178f13aef04b12f5d1f74e43c118a49a0d519508a115f5541c00fc904813f3300135cb35c21c806ae3ee9111634d7f9d8f3c5750c4f84a06b5f3c9e8744444e590137f527afc65c7a02750d0a08028c3321594737515e0235dc67f10339f949467c24a7c3d7e21eb99a524dc37867bfc1af616ae3613652010d5cd4d05b67e0b2165a472b8e07e0f35a4914234747f81c67fb88684cd24e66ef858f545da8249b63525d37ffa641c20c5d6bc0c7b14e0d3155cb7ac6c0c5bd7955de2430101f3daed09c603d7350df9f4c09a813579cf234f9feb00117b9507bffa46135451bd260b384aef457b1bfc377943099bf87753ba518e0a2795d583b017f217ad58e18ab08ef3c2757d81348959b1630a97cac9c233790d84ddcbd94a404001295a34115cd63ee9b6201c79d720014110c08f40664d240566c2bda1b5c7bfd148953a56a4f16fe1fad1a26eae501b49962a50157555bcced0460fe98e3ec22c20ad98b7c8aeee7c284df811bd5e36d021a8defa91af1177e25391ebd5e27a6946184ecb8845aaa2951f2370586cd6cfa5fb55a6b6f9539fec900388af9224beda6a9c2c8bd12d56f7431dbf5a701948eee12f1a36fee9997f7d1067ef4accef88a92088a254f5a39287927a0af24c43f9d9d54bd043c404182d8a7eb26a7d6c5406cb700b7b47ee9d0f21d5bb3fea7dabd72a92a41f5b877318f729aac74abe9160a25d123c13af47541a53479e17158cbccdab65970878ced3a0ec1bea7c997ba7e9f174d73bede04133368b3719b3da7b199f52df3696e6f7498992c79b6561aef1ca30198f2338862f1df0a60a61c10156025dc54297d3ce78d848d3209c8f49b24dd8a036f7e6f96b1755a8a19802e735a6fcac53c7fb899137b083b22e5edfcf46521b0e6530995ee7ae497010c462ac6d2ebb119a04487be53dba42083180fea1454b32bf2dc165dd439301729c7d0761e49dea2717f64673411f439057300b270c39fb61596b13d0c10ac48a4cbaad4a80c7ce6e4f80ecfb72a8504071bff29f75dba42709c43424583f4f57a6390c7ff28c4dd06b91eda67e497bfe01a8edce4f93f612149436c2368c82bdf9b7426716ac4fa1cc5e176126f9a76516720f119d6637f51bf27e1efe978aee0b513920d151fa2928031df16644c42b622dd6158f8274e2512872bbfd6feec1e0f4215e0995db5c179d4924bd25543a7bf9906fca12504fa3a394d413a56ea2c47a4d492c343f1e5b2f90241e66455d70d44a01b214061f6f862689c75bf0d80b1ed54b150fd5be1ef1757fddffc72716ba1fcce9a4f62872cbda99d3a80cb7f213ca5356ee6f36c016c52259fffd302f28b13cb23173f0aa0030abd1412f6d972ff5590f8d67e45fc7bdbb6ef9850e9b93b40caffdf968b97165dcf725c360df884d2810084e85584c8374c4b5c6f59b75c4362bd01d2ce02d2e8c9d662b640b0c0bf76641ea4d0b637b0a97b2e958f0160bdb7b17062151805156602f7013067b7541e85119467240c9124e239861246fc75108cc77596c5d5e63b143916f5e221175ee1295ceda8ba483f321bc5c275aed647182b655a0311b7fc4da579f939bb29e154340e7468adc46f198c96c64ba9e3914954704dfd6f831cdc13da1f1281a46879db31a3a5a9afcd38ab73d52521ed4a191a7df8d845c3b2b4fbc06585c322c4e4e0a2c48fe3833cdc6818b6f02ea9cadb61619c8525302055b594db7de4db4ded5b1e8442bdbe73a756c65d56d089c9535301867813d5c8efce87f78eb745981415a0963208730d204e2b912d1627c7a48e4163aec994d160e678a7bdaa9ea4e8659b924114cc5d9270d57a6e4ed1f34dc5889846260fb2b16420688ecd0408268014ebe2600eab0f03a790612183687b195f9f6975fec47c3146a7f816d1d1b23dd610a23acbbdf419c90f19c8f7028fb0f3b1c9a64a25cc2a7944d9bfd29f76aa05473cdb075de04f6a1ef2b809d816080d885c6036c25f7d26f9e903e1c18d9a2c690f2648d8bb050da8c4a2e310a61b3e61f9cb08f7fc46528ee248d68174c490fe837129014ca4e0ec3506b9f0efbdc38cd5b39be743d4a8e65d5063c1a880685899f465b4784a730b496863443775f0351153267d71300d5c79013346b800889fba4d8ee1512c6f92b317e50d753d76e4c373c4cc2e10cca8ea7c2c120471e93a32de5842ed50479e062816ac6769f781a2a47fd5d25b9dd7fed580f473052298b8770e50d566d2f9c0c8ff2aa036f7d6a48a1a538a6258e581a5f0e2cbd3fa9a0249c8d6122b60b6aebc811d2797b44cb80ff27825e2120ceb43e045140449c346dceedfd4f5ccfbc488d65ed8e282522b6b2946f00fb59979609db58ea6d03c2569ef1a1340719cf51190368b557ba40fe7e21711349b696b1dc0012241c428b9420b7c8811f71c35648c9da23ae92f952f8226a848ed611deeb90a5e209ec843d614df82dbc0a3fc2acf05fb8164e0837c2c21ba24cad43042d08b6824f43da41fe44ce9a6bd66a111c7ecc8a84100c0990f481cfb0b0754382a2fee9bdae163e7f2c24072fa4ce9450782f33fa58c63eb75bf7e2c420476291243c88091a2908a405e1bb17f54f675d08ba7c96204fb097dc26bae259b41ae333a5ecb4be0415020b24eaa09301498f69898642326b1d2aec9755f26846ee8cd210e5bd491d26f8cd808656e44eca690ae33e9b6882188fb299327dc88753a326de4eec2913dc8e5095d7dcd963872dd4e95cd56468a83bf42586b56803cb0063cef42b41c5c863e691d22102acb329ea2d47f5018fff59caf96ef219e525abd6a29599aec65dc650b04c327c1f454af352beda3a5e22ba32eb0fd7e44d34fd07b5d2b311def030f7caeede5acc3eb5536a2242406faa2ef235099680394da385e27887c3265959f760f80980c611fd3a9566627a32ebbe6a165d2c00d84aba57944f1ea4cef4d592179623fad97d2880e600e635311233b973952902bb62b658a7c5184a9b21b370b8fe60d56c3236b2a00327f4dc556746a4323b0d17559d6fa49878230f58eb4d01f3faf44d67e5009c7f33dc416c018a58c551848797444988cfc275a8bd2da85a363b0a366d9a52086722cfec3b418f8d4702f636938caaa574259fea6148382440c79c528fc09190b0edbb0b9cd02c8dec1fa66797405aba8d05a58328e3ce3b37804b24300a9f3b875a2d8345be2d0c30d9e2e40a55a5e0760de9f02a66299116412797f21adc770ee9c3e5edea62d19306284a0b9fb72c65f5c1d14b54ec9cfc9a5080a5c162be5c52eb15930cb3faae303323fdc3ed1780bab1721fa05c9b72dc1e01a1c55dc7bf16a804b31c75b800dd1c6df8ca914b40022f0b26130c1dd8083c79fc546fd6f8eec49113e4ace29fe1dd1b40a255c412c0986827e8ead4a25d0b36f993f9e187574e9167adc002ccb28fdbf0f1643a3a91621fdfc0d7e7de909600e8d9cea1b92abde5460f34b0d313c219a13d62137338ce96cf2c3dd6c07601270d178d1d95e15d042d8ac35a5f2f0aa294aecf4c2a681fcd1e92da2e6b5081f766f281bfc4d27af3152b82f1b1427a608da3dd2751b35b1c6f9ec44bd57036974ca64ea777ca0e7c14fcdb91942455d7dd46300c973166ce03631fe6bc8cb7b801a0a171ea0c086b474bf6786d7462ce501a4161b1cfec274839c29599c99eb10b2542a10f52fbecb082e9ad4b52657afdd8670043a599568836db4903b1f1b225dac8686c7fcaec5c47dfdbacf94954e506fb6013f8decf2620b111829b3902b0b1b5035b2d77c3d79ec9661378b51b25d8016c670a4cd0e68813d9933f8d024e4d4eec4ea895fdd6966ab3817a726cbaadd321f029015a4f65bf482190de98a213feb9815a65caec141e687189d56712608243c6982c7eb0109894967865003a63b1f83f84f39fb1031a1662b3dcee42e2162940d082a25bd3b3513a31b20c2530e9477a4fe9c056a49f9f5ef36461daaa946f289485470061560fc4b7bc110253332e90621105bca9d8d9b386a7ad1a675b15206daaa00a2887a7558d5e9e3656bbe83bf25e8964558dbdd2344a0d7c7ac3ce03750bd054a46fc29e8bc11d97c13cc71aae8cf9fcd03d8c784aaf62f39f438689035ef0f96ff406b4e4b98473cce6b9579eb16ec327bca6bfbb6499da22364a10312ca2bdc5b08344e0e1838797d282b8043aefdf2611f84e3d717e06bbe1f6a6357fa453852d4a6407f4f52ddd4537c6982a9e2af7c5901d2c43b1092f0b3819ef564f37a6a04a8d4d81a1fa7a346187a5ce904d719ffc6efb74deea9ff6106e4907577022cb1868ee833b23c5cb608cd7b0fd308f207e4b9491538069991c3d1e00861801efa834f6a89de69b647fcb9e648c231ae80cc31147fadb619e1b480dcdcb43b58c1cdd65e0f8a25cb4e25cea6123bd5ca4b60eca7b0a56a92c4bce58463ce48223e21de84747f308097df3686e91605f7971e1e108eb7b974b29e48a93d903d65bfca9557523a3140b35d7e12bb234064d7dd704e9e6df68785545f580356d1cbc54e2e53ada37445e24b173370a360ade6dd16cb994b1cc358c9eae0e1a2b26361b66ca45216dd374f8f9d13f6b8f871bdbb9de2445afb3b04104b479fad1779449a092b9f154d4302cdc0b02ac5f660fde041baa3162988bdbcc002de380e0bedc3323d07050045dd3c6beb68208850196fa08e75719335321bef6d8bd7a22b784f4e1712167cee0e471223c23a5c776ddc9249a5bcf4a71090dd04e188f7949481e21f33ef96cbc8c2b96db23b55f6f6358347108e185d2e4f93f0f1120aaaad6664dc4079e945f02553df450e07a40e5f8ce3ddca2ac00b6fef4e44e6a805d241a167ec797b702a0880220491597fff8dc17b098f00bbd13516c9469b39182610e92e02899852cbff53c19325697d34d573cfc48c76771155b178a3cc8e56de086fdb830923674d9707a3186b617b29e04466c919a8e40dff20c41baf878c79bb1b11c7ad855de0864a0d5b7200fddc769a6364a32d9c922c56242609419430db85a12518c273ae4f42d96cde2c90d7ce90f47b92d44821743f443ebcda91c62048588b59c67db69e7d45355e3727cf0e42ee7a87382c09f94631427b910681c5330aa82c01cdc0767ae4c9cfae5467371bdd3aeb85b2ad2ce4f864a9db95ce6d71ce22d22188cbe5b4132374ff52f40495c1e914d66e03ab65cf49f79dae419102e5c47305c04e98ca4da7c03ad1adc0ef4e3f841068dc861f24aeddb62b85bb83884f1aae70fa0d6100b6a087a25ae0098821562d46db8bac21b4c62a9d093abeaa546543429f02ef404f1a7d32a343cf94f468bc9798a594756c889aaa314beeff83a9a44de5efcf9ec528b738c7ff559be327244aadb8b67bf8868ea7a808a9767929634c99556563cbf7a72813e570ddcca3b8b6ae791702e38d793270b2426dabf358627e4d78f777969adbe8af345a845d211b3a44fb070cd139ca575ddf27f2d25f23b67d48444357ff888f60008015f90336a8d59969c005e0cbff7686203a95207121f3ec5f691dae75e29a5fb83a82d9fce81cc6e1f641d445380deb97657089c741218ddaf56d3b1c91493041bd1aa3386feef390d8f724f0c070da501787f361ea0be9914f7b08de820ce6d645199cd232cfdfd5df43f76d37ded55b4c512dead744f18755f300aa1676cf627a08ebb10e0b5645493d57ff93381ee7efa7eabca6410d49666e884f89ddb65ce67eae91725133a9cba5ccae5f4ff8c3ddd9374e14e9be46e510c54cd9abe842384b8973010117474ea30c673f0e0d6928b5ab803de511a18d851dfe4253c6ba6182d9398c2443fd5d26954fc1c60ef59aac3f7b9ba9bab19796e229b00dd517d61ba51a1ec230af124a97fd046ab2e9282a8d14915145020663b0634bb8f8b29c0a0e86c23145514597781bea48cf03f300daa13dc0c5256806f8beb40b65fa3f8503a59af32ba7fe79939ecbc315fce4f73ae7d539fc51df5c81f208094012c89023e1bc4920a3150b46439fcffffffffffffffc2836884b6196b5f2699a4d43fb65e9f93292599524aea8a507fdb7bb1adce6bb700705002370ab70a2a0b6783f633402006064ec07808a3a48a09795f762b7b3b7018c25829438599f9948102331e8d11b081a310e628ae57faf7251fe28430560a3955f2e8312999580d1c8330b9a85c29994ed23e98046112426bde9d754e218a0261d07d7969a132cd4407104693e521f7d357d7e40fe60ba2e53fb473d4b2f8c1a0f7df345674b793d70763e86041041737efed2c1f0c6bd97abda71bd6e93d184497ae9efc5c8c9f9881430f063993f263296112dd1603471e100d0e3c1872492a49b12d6b2a5d068e3b20871d8c132f3b826ea7856a1403001638ea6078ad98aedfd9a225a7834955a4eb9a373122c77d1b9cbe2570ccc12c794be95eb864acd982430e5d8bfa49b2f3ccf19a0c573150b0813478607860666046197130fe7552e3a1974fdfaf070e389842ee11d52945d7f7eb0e1c6f306e8dbe9896f466a204898101316c6c1b38dc60125f5ab6e106071bcc7d7593ef53487a13101070acc1bc9ae57741d7a5d21b047567dc020e3598d5c45354f6e3ed48c958ab71e36f50461a36520d1b67a41a957fa38c1dbc7f192420f63ab091032788230de61093bb64954b86771a69f49a064ca08292010e3498b387c52461225f83be41195380e30c061dfe9e2fa4b012a5b4c1e9b7810dfa0635920d1db0251b3ac0097098c17055f24d25a70b950f9571018e3218ff928509db5056bb93c13022fe95bca4c492c818cca9bbb2b7544c072c050e3198a3e536e1f14708cb3a0c869c336554cd4704638ca154e00083e16db4d5271d9f274f38be60f894945f9dd6923eb91a26070e2f985267c7e40f2a2718630ce50347170cbbebefa72b574fff655c000b1c5c3057906a7f41733150000213ac81630b465739bf1b09deb5d7c0cf000187160c2178fac64d9e4923e1c8827135ecc297b84ce0c082c12ec7df8591db298538ae604a5a44856eed2748638c31ac803c930bf1c55392048e2a18c73e575e388d3d1351c1246fb174ef6939a6604a634244106627cb13a0148c56aa74a9724f0d237044c16416f1da547fc4fd13148c21737f36436f434d4f3007a1baa7f3236278de09c6ebeba4d7e72618f29d0863ea7cc276ce04d35dfce533a14e657909a67abdaf0f2b17613d4a302515d45e5e11d2c328098657fb71fb2072b86023c1d43eb69eb49e490e3a828d911c46e02882f9b292522907217663250229824ee710cce152cbbe49eef0d86e091c423059ecfb750ed7dd49b5040504c3a419174f9d4812820c0cc35f28ebd2ff22ed41c55cd0fac258226fe4dd30f5c2242b2cee93887861eaf91369e3ad257476615041d72bf2a90b53c8ad203ba48d94839d0bb27857c8d6a6ed1e63d973f4c5195b3109e38db791831a4b06a100bc26cc7b5d8d7c0b835249453ce7d71686fc513c4c8749a6c5ad85717f84cc979b1626d9ff9d90d35c3ca9b33059c8b8f0105516c6f8adc94959c82657120b73b00ea33fe8242c0c3ff163c40e93d542f015a60e5a34b55baebfe40a536e919ebf9f43c9cab5c260c9266dc6dd5cd28a025861d0cabe52d12ec53dab30a8d9114ac7fe51492e551844a6bd455c3f1559292d3fe975428521dfa7a97e855c21994e610a23fb22a27fa776c9146657b3acbb5ca6564ba530a5ad0cd1331e24a94f0a43f851ba4cedcd24ef4761f2944d5d52e5a5f14561cab75a290579288c7212dd84b8bd8bb183c2e435c2528c2c132be29f30bdec7dee2f1d2996ee09d37e6f558ca4d309e3a5668d50677db23c9c30c7a9d23fcf3e56a9b30993d25b39450a61f683ad09739a8a093a6974084225135ad70861c23023f1b2043ba9e1954b98c56554f6c92c215ac268651523e71257c20c5921e694982961708965563a65ad09f224cc7e966c7410af2a2749984f6915356b3d267a244c324ab456f81bbfc842c22897bccff28ac7fff61166d1eea0ac3ea7a4e408e3248addd908437e4f759e57216c4698e4979658f2751127a1bc3c8408524598724a1059eceee74b4d84513f449c7017f4e85111611eb9172e589ce8f9218730cebd5d9e53223f7c8821ea1b359284b4904298e25fd8ab6f5d8e242184d145dac5f828a195e7208c25f47da49935152a8230d998aa88b6a022693110a6549e4ad47f29b32002c21841cc6bffff07c3ae78ab5ebeaf1ff9a130eda7f4b3de0793e88fd65fdac34b870fc6d221f7e2a9f76048fe313c62877f5b4f0fa65879a62f25793089ffb7d3e21659abc38321d7e4ce509d456e7f07838ef2cc0e66754f2289b7eb60f4a03da4b42cdd4ae12328800ea6ec10dffad91ead3207b387d6940fa2a5c4dca2003998fbf24452c943e26048dedba17f3412a4a100381872342de35b6fb9737a83f9822ef5f0e979544e6e30ac6ec9a888ccf5d1b7c1a443bcefbd9899be3e1b0c56c235363cc6f885d660c8b9f9e94ca538b1e36a308d4895eb2fea6930aee47cf1a9ffd3480e1a4c419c9ff9aa256db13e83d9a468c93035aa92d56630ccc752132929cb099532982607bb6b13e299a3420663878430a6372a5a5dc660f4dc29463c93c560f211b978fd881bb286c1385b51422a3d35b925188ea12a1b694f5f3077aa7fb76a7fc88f17cc4976bd85a958296dec82b992fab64acf3f9ec305a35795aa0bf5313ebd05e38d57e70fb95c49755a305e3a799eb3ea3afd62331490057387e4b69792d2e94fc282b1ce3ac9f8f824e2e8af609219aaa532defcceb702e2773c96fe1155c17013f594d4ff3c21712a9884d6c4b4d27fe94b5330e80af627642b05937cedd25e9d4c27d95130bb752cb3ef0a14be74332f215ee509865b8f717146b632632798f774cc12fd9eac83b609667935ed977a32c12cd721844b30b5b2e112cce925d6de3a68195109061d52e6bcd905e1d725c170fed1c727f9aa9690604ad53152f8ed9e9c1752c01130533119c170a942d07d9db37d9e8a6048c27a72ca257be3f40a208241a28ca88a91ec29e7153004a3a96895537f4a59880284604a9e7a3ec5d472134ac13088a50b297e47c0305d4af2c182d07809fe2f0cd739d4e979331dfefac224b353a530b7f6c278313232744f5e9842faecc5d83995f3db85715de5e6f279498c5917866c15c53a56f8503a940b53ef4e8aee2192880be2c214645e97e81423a8945b983dd6fb920ed726c01606196d73f772511e24a016c66eddbae461e634946861f0cefb984bf75039cdc2a04c58ce963c5bca7564611cabfa4c11722c0c631fe729fc75953861619411ca530435aff6ed1506f9f3a554541235b62bcc2dc122a41ccd3071b5c290a3479a5821ebfb618549fddbdd53bb7fd87e1506cdb8930f2a5bfa5d15c64c4d0941881cfa74a93045319d5efc42a830e9b85fa9273f8541f25cc85cd85870dd14a618e1d44ebf530a8350572e661b290cd1f6dd763dcde8708fc2144edc4589708bfdb6284c7a31bba183426190dfa1e6d2749e561914e9c4cbded922fc09935dce8e16b395728a3d614efd6a3bf2e2f5aed2099356779ccbe179d3b39c3049d4ce92266fc298b7d6313ff5cb6435618aa73aaf6d24874f66c294a447b1d4b09c53b5983025b30ed94a5bf457f412c6dc52625da3a3a65ac2e09f442e91c4f38e48254c395da570e96927344209e37dd09fb2e53909e34bfe78a6e22909d32789332af983a57889846946470d4f199e119c10f7d596c118caf4f2ba875fa64a06c3ac794839e1ef434cc760fe9338b23ea204f5a71850296c961695c530983774de5fe5c748f20383d1d2b8cee7ff0be62496e339a8a4eb6e73720ae2b36c85e5d105a3c950932ea9d86c2b3db860b6d149c731d3cdf46f0bd97552ab164cb26eaa652b5ee66264aca14fc306b2db814716d0f1f3224c23076714d3838207164c65256e94e7b42d3cae602e65639ea2468ab896580d53567858c194f74a4df8ab2c964c457854c1f8d1ffe2ab04ddce122a98cbd2c80efba78318a76010b2ffca76c7837b09111e52307e89119b76f9fcab838228182325fd9b955216f0808239c78b9e9f72f48f952798e5ddb479d0179961999d8387130c3329cae51cd63bc96f341924f06842422fcf621c030f26982485cacf7b48a3a3843c96607a5753b31234069f87124c51547c3cddbc637cc85813030362dce06d604c108210f095f64882073c9060be8e707b3a3df668c97dc1e308c6fc7cdb17ebb81c3f848711ccd142463df582ae24d2078f2218dcf334846bac6f8ef2e0410453e7aa4a975b1a824988bb4e6ff11082e1a4ee894a21f4c54e308c7dbb9643b9db48f081618a37b12e59857e61cc53162d5b12b71c725f186b3ed552c3fc92e5bc0614111dbd30ca8b0e49bd041d7288f0c2fcb7b26f21780a964cbb30d90911b4a4a02da8ef7591e75cd92b9872a1e5647fb612c7854105b311b9c27912b2dfa21c31f2a70eb3a0c316e6be9c5417fc24574a1203033d18630c5344472dcc9d35dbce9456684f6961d02968a8b78549299a85c15625260b53ce49a7d873652c4c3ae5ca93ccbaed14c2c2e069da74ce64e5f4b98e5718bbdacf7792fe77571dae30699af4371d2a3ce7dad10a434cffb8dc69647f7d32f6d3384304638cd1c10ad3f864356bdb5c0997d3b10aa3e5fba0eea49e19e85085e174e8f939c041a331023c0f74a4c2e4329f7b2c8ad0b1827fe84085a923567708ba7c63c5e544c7290c9ee6f24254ccc841aa91c68d33c8404d7498c29436fa417c45aacf3db071c6096eec253a4a614ec173e62d78e578925d1f3a4861eebf35f959234ffe2cd101d1c1758cc2a4c39d48e695547aa92c0af328e195b2495d0c1da130689c27bbcff2f711ae403b40610a955eecf3488aa1b78e4f1824071d545937de0639b041b6c3132695b835264f85d1a771e34590fa848e4e985e23453f51fe196798e0d4071d9c305dcbaec7db0f196bc4522a2303d9b10983f6a94b8a569d674732d68e8d336e9cd6343e043b681390232de8d08439f27ff220642713863989f712a29fd26562c2f4c94eb2dae9507fed1206dd79424a8f9c3aaab584aa23e62b4db850072a61d2da2ed692844cd94dad83128618579683b02c49e19540c7244c62419c4823e37fca27634d67604619356ef40cce6a7448c224af575db2bb3fde8f84e1841ed562112b749f206188233ebea894479867c4430425b28e30c6964e12b2cc8d3027dd2108310bf3fa3123cc5d2b41ae04a1e5aa2dc2a4de2ddc27e6841cd48a30b6c77f92e93a237212619ee41255b54584b94aa5646f7934d0061d87304dfe9027e6892779ad210cca2f0511b9301b3b29842959ff7e8ed157f33c210c9ee3e792131984399a668c87b06d7d16411864e4a598b33810a653eff944673e7c5e0161bc4e3aac94bef422437f30be75886a79b0cde81bd4b071460d2074f8c13cd79ff695ef1d731f4c3a7ae3734c5d7c9b3af8601eb153224da7875adfd0b10793698951e93f7e435ec618a30c1d7a30bb8952ba9245ca65e7c110d79385c8e94e2733e1c1bcf93d16376542cc10093aee602eab8a15254f7630e959f96c48899192b6a30e86a0b23fc71ef5cb693a982559306de95547a890be3107d365f5f021e7b782fa7230460e21fff563bca6280ea6b9601eb7930eda3c380231306028820e3898fbb44d77fa88625f7983b9e3267c7b753adc604aeaed6404a196efe28e3618741cf16f3a5eacc975b0c15c322a5b6a0f95f8411d6b305d57c8cbc147fb5dd5a106534e1777429851c971471acc29b376245af09c67d4810693b89265f1e2d67106731e0f3ab23aad7b3a7598a1a30ce6da8e961ef9e62fcf64302755af9274cb8ba5948e319894a5d457d9573d9a5087184ca6e25e59f2bebeb07784c17cc97b84ea297580c1dc9e6309d9d1449e24ecf8823154dab0681ee75e6387170c42f99ce890e30a7474c12caa62c988ab7570c16ce15497752a756cc124776fbc453fc2850e2d18af5ef74d05fb3897ecc882d9c4086175fa43b0bc93b156b2091d583078593ce541e92041d664dc681bdc48cb29745cc120b664bd8fb0a07492c6df60247458c1249e9752be824856e111dbbe362263ed460dfc8c8b4207154c4ae7f34a7491ef9b2103c10180091d5330c9deee284b21edd26a850e2998d2547ab31ca794fe3f0a86ddf2c841c72c31f291a1030a26a12a28efef70161d4f30d6571e7d227a6adc103a9c60f6d4a347848409b9e464ac1513424713ccee275e4b54aa850992b176b68b0e26984daaff4eb0cba3bb97e139406f63cd3a96607cf5908210da11a14309e692ffd68cdd8f5a0d6446d748c3df868d331c2f0523400108c6188334a22309df46d01e420551714400f0840e249842fc3ec9ed18255f54888e2398c37b12570949449f91fdd0610493d23616ef2f8fd277923d7414c194ac2e2bfdd9b67ae8120f1d44308b4851b27c5e8e4a7a1d3a8660f8d7c8edd194086b4f87108c592167f4ffb94bce81812318c6eb30c2e2538255489bc0010c773e8ee46f512612387e61b60a539dd86eca26cbe10b47cb9caab16ad4c0cb40a30c397a61baadf06217c6a485928c355e9894f6961c612732d65e0767d84083ecc2fc966cb764f9cea582347270460f1a0417c8c097610305638c91460e6e705217e6fd78b23d89989eefb3326ca060066694a183336c90a0ae56c0910bb3e849cbeef87061f25c953c67d3e9d3ad5b98635bf0cbbfda41cdcd610ba3951ed39da487ad7a6a610acbf38e60e1bb744c0bf3b55e165521b48fa924143866610ec26b525239fd39aae39085d9de544bf4b2da4a411cb130e8b855faf6179f4212072ccc398654911b59626285e315a612ad4ed656b6256b0e5798f4a7f411b4b3f5c511472b0c2ff973be37915cf671b0c26c2f42eca8a45444876315e66d13fa626dd35694f4c68d37430f8d326c906abc0eca4011b8a0060018c1a10ad3b6c9a7f6df6eddd2e04885f127d68358c5b67d97a83064b94f9f2646d0959392e31486590f49e280c314e6f46b4a59104ff1692e85397ad22108f70bf9107c3446200606cc18811818e8c018637090c2f8f964a8a8898ec214d4d57a5f885887958619efa2c83cd6fe509834b6d4723c670f7080c2587ad243d076291f1232d6d0b6031c9f307b780c612d2f7a47ed9e30a5bb1c1fbd7119a70c393a618e3197191ec2bde3be64a0208b15383861ba60e931c7b38854934d18437877140fe1ff276263e0d084b9e4e64f503365c2144786b22bd3bfc08109c32753b1d2449aabc0710963aa9ff097fc1de32e352c6132a1449f0877f996304725cc5f9574ce7f2ea38451ee2eb6fb6dd44426b1fa9998ee08faf693b1468ac02109e388ddd115f73f1a9130868cb8723a1f27fdcd0109a3e9f0ccfa0d353a35ce40c6f10843d2ce12ea16ff53ce7a60e3735002fc0087238cf1d9b2875ce2137034c27c65daa5df7eeafcba4119c8c1089350a224690ba14347ed171c8b30c8336d51fab4894990220c23f52585ca3825a3cd011ac57024c274255b95c389c958eb341a751a9d81b3438439c4d39c471fc958db47b7349694868d336ed80dc26883594250dbe1c43ae80f1b4c6147aa4593abf1c1b20653f64b951f5613c7623518bee3dce28abc9d531a4c924288cad0a2c198163d480b5b9fc12022e6b283f97d4a9ac110abd2fd7bd3329854dda4d86d12321852f27ca3151e4f98c6600e537fcdcf5a621783b9de47fe23c9ffcd1806f37cfe9f705142550583d1de4b46f768651208e30ba6204743ab68ef05c3c758b270739bbf6417b0ce26d404317a4789b96012c9ea2b56ae101e4f1b67e409630be6533284058fe4215ed1824988faf82d162b744a66c11027cb4e106e95ddf5c0461a663c89411858305e5b95eafa3afbabac208c2b983a3c5e88e65ba1d5b182f1f397d896ac963ba5aa60aa160b299274cba57c2a1854851439cfc147dd3305d3dd7b1493659182f9530a2e63428d82e9f4d9aa7cdfc51625148cb17f925372fb1df5134cbdd96155c24b92f085e104539ae7f5b0939d208c2618648616e126526ecb1e0cc26002f6de273bff787f7210c612b09453c4f7ca49fd26c377108612cc7d236e7e157ebc2c1784910493fca9d3eb9d173f289d9186191d20411848205dd6da35753a0c8871c836d240037b10c611380c23a8f9e93a240f855104d34e6e65ec056da4814618443098dac9a93a23faff52e36d80c60dca40038d36e30c33126da481068631041286104c31a2f5f3920e1f3a45c69a06c6182318a6687d6e23929c6c42fc018c740827dc7d2f4670f0f10b435061ed93529523a4f6c317c6f413c9d94ea6ff5b7a61ceb7f0218f5e0f0b3f2fcc25ce639998eff0b6efc25421897cdd7186c3872e4cb32729ffc80e2f415d7261f6eb38aa53e592f4d4bee10317862826afec736b753bb730974549410715a4a6986c61aad634fdf079265abcd4c2ec914ad6a79fe546190b820f5a9874d8b8dc7df92c0c2a7fbf1fe7e9e2c8edf0210bf37cd995ca444b2cccb61e210513172c4c25b994b6607dbcc2b415f9b5a4e7ae305ef0fce941eb34444a2b8c559b16174197a710b3c21455a26bcf9fac45c9c72a8cb2f19fb2a8bea99c25636d35f0a10a735f9a9790162d7a0eb1c0472a0ceed9f49da77b8d7f64a4518619638c199851061d3e5061a7305d4e397448994fe2ae0f5318258e38f9a92b87b7df818f52900f529852860a9d6db370d9761406d7d1511db449a9f0210a539e3d95f2b89e1825e64728cc215c298b1eb2a030b75558fb089553840a0d1f9f30ad87917849a924af2f9e309b6993eda4e2de32a265f8e884794543355efb635cee8313660f3e3a8470f98f4d98ba93c8ac905532d66a70a3d3a8516ae34313261d43cbeba578e1b3f6e02313e63d19c1d2363624e8c607264cba7d9dc289741f3e87c6c7254c29e5cb49930ddecde81b669c618605c618037530c618a800c0c587250cf249a9eb590f495755ad84c96425bba4bf38418614111f9430ede8a5511d2d859c3a0e1f933024cf8ff6f24149bc21b14f99928cad8183bf71fc1109d3c70f22debb95c6dfb861010c70400c0c8821821168a007e4111f903048cf8e2621761e611295b91a164e7bb54a8b0f4718ed3bd88e078f7a154d679c6183046cbbf868842145f21073f11da45a186110f727721e7b89f7e5224c4129ad7f31354518feab5a23774c909927c2a4cbe646cfafb347bef081088367f7202c99de05391dc2982ad142fe4ba4da785cf830c47d14c2a4a56321f444de9e9d091f84307fae3c158bba222ae4c2f0310853b84bb5753da7eaf282308a7653e13c53d72a1bc147204c2d9e1fc94b8aecf00c3e00610a6d12dc428c94feb7910334ce061f7f30ab8974fa6247c6a9948cb51ef0a08c5484061f7e3045494269fe763efa60922c626fe9bc2284341fccaaa6246509eb88f51e0c1f64549c9fb01ecc67b222f2e7c98339c99e252d953db6bc2a7ce0c174f59323c7506175d28f3b98b3a5eb10dba10f3b1896e245855d4cc6da1563c1471d8c15cf4c7c96243a986c52994d1a1532d66e2033fa0c4f061f73302595836f9c0e231ac21ea45106213ee4600eae5931b392ce103a7571c074459cffb87e95ac81c68d3a7cc0e1f5eb9873667d51926f3042ce7c3593967a4fe30469860f376479e27ba54c64d369433733f14f48bc78c992870f3620ff392c8d1cf93da99e1a3ed690b2a85a97730a4a203ed4905645f0f865aa19c23490beb6f78325870f34a0426cbce362e9ec1952a1ef74e912bd916f0d1f6628ccb9fccb778847e7f0518666b7829ad193c2a8c4c08018221003052010030377f82043aa6a219e923de1630ce82efdb394b23664031b29e0c387183c73dd0b27bb33d20b43aa2f1792b42fbe3330986639463b2baa46cc8ad9ed21044be1e1e30b065162559e23e9875fcaf80037e1c30b0695b3e4eaca5df615ea8239a8357517a16142ee5c307ae9f270a65aa9287c6cc19cba2d651bedf92ab5160c9264f68d05350be63c31dfca7a7bed2c5830298df89aee6123c6750573be8e2ea2a276df3d2b186c4bad2aac5527cf84955f86f041059350495dcea65bef43ff988229e74a9db746968241626b8a897a11d1520b10a2828f28782962425a8da806cc28a36d6440a16008ba744cbace4eec0b0a1f4f309dae382295b897f0e104939abd0c611e84c930d9f86882c707136ef4e06b50238c8f2530e0430908f84842033e9090808f23dce00602f830c28d1b6f038e8f2214e08308370881013e86501f42b0810d1a0310f00886033c8051a30c342c40820f8f5ff8e2d1195e2307bd3880072f6c6083f60178ec82c3431765a06181303c72413c702186c72d08e0618b5a24c08316350ce0318b1a65a061811b3638e30c122cc043160ef088057bc0a2461a08f078c58d2fa30c342cb0810e0f57d428030d0bd8f81ad83863056178b402fd0c66d0288880072b6eace2c3431509f048450d0578a062001ea73080872908e0518a007890c21c2ce6e7be2c29d6ef0a048f51182f646ea5e4c87fe692b1766383e0210ac34910d533b146e68743612efd1a4943e5010a939795bea825f4f8842188fd91952304356a2a62a06003c80c0b8ca145f0f08441447a4bdd0a69545f88e0d10973ed6b856fcf17365f74c605dc8c1c68608c1f7870c29055b4e38ea4cf61271e9b30d7759ce84e5ae5c08d31c6f0d084a95b2e4fe4f4f1eb7c076760608c3170e0910983b25d55cb63c00313864b52dc7c3b889f1cbe81c7250c49dec48a914535f0b08431aec4884e1da37b66b5107854c298a5433db59f2861aa39cfa5b233099387081e1f4aa83cc2968479c4c4c68275f49461240ce7f9644e529ed35307097358f12e91bbec3995c7234c62f27eec43ac7a3db081027f1d98230cf9449a96f9eed108c35c3c7d532fad6e71469894322daa1ed4a5057711a6bb0fbeef29238ff03c1461b2e8e2315f65e75d7924c2b4399ea59727951ccd0311e6e46e2a4832a14584ef3d018f439836e5c3488d943394da109efaa8daee17c274263f8f28bbbd9315214c725ff9fa3482463f195c1e83f0f36c49ed3c046188752a0e84498a560c08737b96f18963c2e61a68dcfdc1a43b82be9b14fea94a7e30e8181f6fd4c99cfbc4a30f06655afcd26ae8755ba306fd364eda7af0c17cbd22136b2f1278ec616d78e881a453120b0f313e66f8028f3cb89664a747ad8ebefd0a3cf0606338c2e30ee6203ee94f6ea2ab82871d4c7bb22fb6f809cdcfa981460e6ad4e08c336cb0251003036280400c1480a0e8c0a30ee630d76e6b13a2e731d1c17c67d7af1e3c8f39182f088ba24c720a49a1871c4cf2f9ab3a2996a59c338f38a4071ccc39b669b57791c71bcc7daf6e2a2fe8f627b3031e6e387d8affb63b95b6e191a4d54b2663cd6dd4e881196d34f06003713f69d3b6ce93dca0460fcce81a37ba3230c618376cd448e346bb193b3803a1c71a4c6fb9748732a9c1aca743ce97143cbae9a4c13c729d2a4c84892829683097aa8f92e44b1471ee194caab2ddd26a78bfb966305f4dae907a30a9bcf72123e6e1bab0248456c933e1c19c435b78cf967758558488d2d9c15cb2ab73964f12ecc63a98828758212b6685db74308baabf8e16f2dce53998e279793cc92feeaf1cb8582faa447fe26090951622fad4a8b41d1c4c4905d7fbfe10cd4c6f400303dc601cbd922d0989b5c1f4e5397c2fc60643983753d3f521a4656b30ae88ce3ad594d3613598dbd4bec47f7acf621a18800693d07be94756673044cd0d8b55fb494290016678bf52366de16bff736528cf579678f82af7c9603c393975f544977cfa184c97ef3987cac8adff88c1c9f1b63dd49f9b1e065318e97a39444f0c910306639d99327f516ae4ec2fa47223e547574b222fbca572178c3e422871cb664a81015c684f655d4656ce16ecf3a0f447bab72d5d0ba5f39c6654d6d6fecdc2215bd6c8cd8b61420103b080a7d71c193978fa5ca1ec17747c39252b14d143845827a2df2a5c1e7794120b217fa860921842bd6be9cebdff144e7edfdad1df3a2978c1647b4a416707191085435ab38a58595a3b28607f3259d4e49ff8099f095f1d8fd59d7342365676a31d235e450c6802327475989409e414ecd4bcbdc29b4b40bde3ed56deecaa90014af0d2884a27843a71332009a59633292a0c4002f2e3c595fa08c6d1972c45eca4b4b2114cd1f524c50929025a548a949245b1141b110c42c9f814bd7df259870143b83d82bccecb16b34d0c10824107d321b32b899fddc140e959f809d3253960984388da294bf40b436eed911dd717e6f41c948efc25556a2f8c1bdaa176262fcc1f734df4574ef1bbdd85af615226e88aab31e922bdf1a32d5a59bab97023e9911b5e41f85db8f092ef9f0a8d6fe155ce3cb3493b3a5b90e7f5926ab530d8a828c172e4f7f8d0223b1d2a84d12c0c415285121e4c59a43a480b1553fa7b2c529193fe38963b395868a63eec45ae3a855f61d2397c8ed45859dd15caa46829e47a044b2bf8be5049c9911c2b5afb4e6a3f8818afc289a9754a45d57454e1f75bfee55829ea5498c2e6a4921d0b3d294385c14743a7687f293f7d0a934a21757eb1cb7e2f0a0b30853959c69c8dd2fb29924a610e93e396fd8c07ad3f298c7edf4994b83841c43f0ab3950896c34b4dc4f84551bec88c89cb6b288c236eb3a22561541228cc412b451555a14f1892cab7b9e81f12f5e3098350d9bbde743e7afa74c214cf6d5bf2af085d36278ea434bfbf4cc90d954d18cd752d9c12b226cca73b2cb4d794fe8b33618871134e998e1f36c5c47a1d2c7c76a77bb92f61d44d3d6521a7a4476a0bb0844949cb4105f949859754c2146a65f2c84b29615e8b2264a77021494a27815097f36479fa699230473bcf22e6bafd4a9130e48e94cc2f04a57d26244cd5e7c1eb7de7477f8471c259289323c62b4447742179b776c5d04a23383bcb95ad14762246182ea2c7bcd32dc29c6d5bde4f8a307f9555b8f6dd5b3711a63f53df9e3e879e1043c42bf1b4f34e0e6a3b4431a9bb5f24ba7c0c61f48a704a843f1d6654089334bb4f2a2949f22d5563c1b0004218c2e85c219b31263b6510c64ba373d2058f2823fdc2020491cea8c9d9de06820f12a2e7ea7ad30362dbeb2c1ee1dcc72e7f5075745ec9048ffac12054bc4f1f9e758f9b741525870f86a0e452b22e8fd41dc28505ec61ad2d9110947a3858d03e696363ed3c984f279d922aad9d85090fe80c2dfe3d498864f13b20d2da32353b989386fa89abd317aeaf83a9374f74a8d27fca79d405cdc188223b25650d9197433237794b88fba97148a86ca9f0fb210d07934edb177c674d5aed6f604288a6748aee92f2c80da653428a9c644adb5cde0663a968699122365c2aaa04b9db1accf9469efad8cb97ac06c39f48a583103969b83a42b85cf30a42868682cf758844abaad319ca67deb1b47274a66d06a3e5b0f859d75b6997c154414b87d2ebc89029b12be93194b52fa6a9899dcec2627075c6b32aa4340a83414e6acba9d34c4f70c1706c91ffa6fe73aaacbef004b112c7d2f45e30e55877e99d4a871d912e6031c4ca88a813ff5f2e9c75733f29fdf3d942e761f611625eda73b5902e2fbfb2c05fee4fdee69d16164c61299994246bd7737905254553a3c368d10d1d2b982d455d2e76a4492b56613797943ae8a95088262c23a94e5f9a42e2af33e3ea2c2d05d3e94b252feb8b422259758ab8bd408124bb4edba40fea53fe09a674314b926c277095527487f0f14b13cc2656742cd6ae5a9609860b1aaa4f65848fb3b6b0802598c3cb271de4eb93ec4a30bee59b28426c88d1a62474399ea1e90290505297bc69c9439ab5802318f3928909cfed93928c6038b3f8cff98b6048dda5eb927b2298820a226b5c12b1da1e8249567cc9e593ead287162004931e35af95b907031193456e74c0309d9874e661ffc220bc4e47febca4ea7c61b6cb23ed428d900b61f4c29cce4a5744cdca5e7861ee8bd61d17f35df8f15794c812d68549a84baa77252717a60b49b2246dc28529277b95ba5ecea2ea5b9892127fded52962a98a2dccab7a9a922b5c0bb34dec54c14dbc63480b438e53114b446761d2799de593c3b996b228ba08eb148e852a13643c448717c1c2bc13b322aec809dbf815a60f4946da89743922ea0a5365bb9c9cf89c2bc55a61aa5211b34e1d46c594156978bc8c972454d7ab30e54ed172c98dc892a20a9398e0ed274723473415c6f6cad3a67f54186b52fa58e74e61b214bd6db9f4fd2a628af7de53bcc25218e5ef629e5d69e47c92c2789f5ac9e36514e6ec14bd7f3d8ac294963f8c94a82a59140a7d4ba48fe381c268ee6945825be8b57c828b5f732967957cf27ae21e2d173d24ffaaf0e984499f595b291155173d4e1884458be623b409936e957a50353bdfad268c575244c676eadc936782ff2a516d499830a9bb49f95a5498fc12bf889b0ec94410a9f49628ae47bddd5abba412c6f932955a8424fdcc28717cd3218e9787f52661d05177bbe32e6f7e4920ae3e4685302281c93ecb275de6d49030fba85239151fdd6e91b1f60843bec8a62e7e658c46df00c370049eea6147dcad2935824932e691bdab2a8597a0a1e24f55901f7ee5a61d86c10893a7702742bc24f36a2fc2e83977989a7db310175b43188a30277d229e68b1a04350224cc9f3bc696d57080311064f41a9e77ab50baf1cc2f4621e57c3574318249d909594c8dd33b24218b3439dc97a09cbde12c2242c5bbb6ea73cb9a04198b2754f6586ba76d205611256a35f94481372cc0361943c4997d5a5957705100639a264d49af40fc6d1b553f14ca77799f8c1f8b9f6e1aa651f4c91e25f25f93a1f4c49a848496c3c5c2e59187b305ce94e6a4bdc30f470505be16ae4e9f82935100d61e4c1702ef36f957e9382e4cbb081827c4018783068939fcc4324a5aa5abf096e9c51469211c61d0cb1f77cf34cbb8248dbc1a0643c3b9c97e585b80e26ebed1cba5e9e1cdc58d4811f844107931239e9fc217ba553a13998fb23cb049192eef33a0c399874df53437785187799a9851107a3e6884923adee924e8283713635df82ee1094fc6f3024cb101e1e73371843a7f0ba038d5ea8f4af6652612012070402711c045248f51e00c3130010404078441a8bc462d13c0e85e1071480035228245836301a241e18161808842351201404844382502010088541814028240a27e196ec03ab41f74a5cbda0cf38737cb323ed8a86cfd25657810116da0600aa70ce296e6ae67566a0e70b7654604720f737c5c02e0d64724730741c6dd034c8fc50bdd6880191268b4923507cf74d766582231df89f0599770a1a4c373db28b2b0e747984ad00cdafcf08c0997b5e6e6eea267b805f181beeb6aac9539bb444f24f37fc683c9ca949faf447fea535fe479cd290c5f455fae73b6e1d5cb06d98220fc824eb376fd0c1de1d4480b743ac6e74a47b0f15e183e4091e968a808de6f2541c8e584911c341ca0bb86b6c938d8b7c3c2b5b544b4461523983f31de5922592fc69c79a4524c8671d92de4c7e09eead39f5d4bfe1a94df4278e6c79a33ca999541f6dbce7ee7fb86a418d54ffd00120f8140dbc9d22f28d7ebb9f1dee3f4a15f72cbe0681c61d3aeb4ed422a08aff1d39bc1c530481076fad49d9cbd92fe58118611efcd5e88ef6c36e3b08c946c14b908e148a6dc8692dcdeb6d7b5af9ff140f940d20c69f84ff2d9748fc199f7c0bb77bbf8d53bdbeaf305628eb7dd3512db1809933c2bbff7a5fda6e21c36a472e0fb4d899ab41175cdde6bf844c934671899fafe9b6045225aa6d60af4015ccc7662a87ee9f1d9487cfe553ed082d1c63fd550089f109f0a6476ec7a7ad90111def3be9d273a26aa725beb92225aeee3fdc6b4808250dcc5ef550e2b79094d8a6a18a5a039dc6d8357c3774190f03370365a7a63c1c251171ae9748682c174f23718da31b7e8203a5cb808e5808638d4d6dcc72b807b6a18fc51b26e5a233c7511c35f57d8d463b36ca40ebd4c484661f9c80837d1b25c6513ac446e73ed07f0e9d13fd339a4114886b7d702ba65328781a479cb5bf8ce1ec300d47a91f363af189a59d4e56ea9f7b9ab99c439bd905f5c0513afead53f1b3046c811da319a4ac96a56eb9ed2007c14672098d6710e0e0debf6e209b3fad0c9146ff7e6189d501df8bdd8147ca7b8f21e8e05ea2633757f4e6f3966859bab60e8bb5c1be05cd7c762000b61b38b6124703725a8808cc2d040efdcba015185262701af9e67ad7d2d63104284feb64d6eb5f0993002109aaaac1112dd71c4e6e0b4bb5e4d97cd9d90ceb3f0e98260ce2bae69033045f53409dfbba11a06654e95522090e53da0559ce056640975de35c9600f50932ddd94450109a84a94701587bffa21592dfb9e65012a0fa4773ae11140ebaafebe8fd28132685d21eca13d6a10f9ae18ec94421dc56084c35c66e1d1eca59547ad0fc0ec170bca1abe70d078bd5c17ed7772e7effbd63e4f37c520dc8aa5b2a5dd5887f3a93080e6504827ece1014f80622f0586fd0d16ab76c4055f7ffbba49c96edef3a47c98e35851308cc6ac01b602cc3fa2f23a0a7e6a87f02ed28826339280daa37f1d8ef6e28012ac3b7992e2db11c3c4767b0fcd54f9a2def88673a7a85ad3d44afb14ac087e5771d3c87903115f818a8250e062f6d1ad0383886173cf08a56cfa2c142da31174c58dd48e3914fd2ed80b49b531898070f7670907e822dc10d19421bb4d67440104da63b2a891a3ca4d51acf651f3c71e361870a357836b71da9ee74a069a5b6fe107877373c9b81ac41405283634e106755661a13990f1ee43a58befc278558c50436c5fdb45d569720729a7cd112c1206b628641d3171a19641d5baabba99dff00902c5ce031d27cee7732841421211974f0002e3e1cb08387e50c1a123dd7dc2192f560b40032b1c1f30413780ae0e0715db9d5c76af008ef83c6ed0f548e6a0d9ee7a387811d6507cb7d0d40fdab60c7a3f11fc20d9ec428864318099b40e6b307b4583b067722f1f9925eb156d5cb4058ea76f8be871624460c1a0669aa32683c0155cc4212c68ee001f6e97a29fa702c27bb2822a855719d837524ec18bb7678848d649a3dfd5dbbb5ff0073e0de55a38c9edb7b04a6dd12d71b34ae2dde6aac54b5f57a3d1efb775e284666143f458a510dce73dfbbec702304ab4c344334181886046dc4a1c66408a01c8b2d70ae21424e16835246a5346d0dae1894169478c33b565351cc4ca7e176a8e929d2614cc0e98350313f9e1584987a8375820e80054cd8f0c08b0e06505803113633f98021e3e088628c88c30ad93daa20163c484fc40afd50a4bb448f3255450e5a401a0ccca1b83c4e32bd7500e94f16a58d3b472e6c8135694c26de69913c7710d4a8ca034d91f673434fd44fb3e5691d2add2003467379045a5d75f175d3db4231ae012afab4ff57c033658c6d6d4f6eb167658f7e374eeff55ed2110f629d4a028503cb647c662edf598abc8f49fe9b384c8d9fd8c05cd5af0d549d3c550297945fd5da01e83faf6967c602eef7ba0baa36279709254d0da59235a64a31b69feddba8ab7b6a2354f20fc11ebab08f3e441faf066b10ad9b25ac15ca5516837a0861300f507c4e244389afa32cdffd7e37eb3294eedd42b1644ccbc68a20f37635b74cb661cf4e9445865d99cbe342c009b91a06000b63ff24116ce487b11a992c81c9b9db81761434f949930bdaba7bbd6c038d55283df3271dd4e7520c73008b4737df2c31aa1888f3b0e1878b1cf0b921327884844bee5a714bccac1ea25bd7f0f11e1b6e82f1d96dbe6a0d516fe8d1912c35f05a0e75b787ed813de8600d653d9c6b17aef7d2fbf5809de265ff366719869ee80311899639a26019e509f8151c866ae27e19840b2463f1408e11784a253050c4c141771070e9670e5cadc8b03d1c743106f610bc51c4fb0a2262485030b4fe61fa81a9fcdffce8654d55a8bf96213db4db24e8f92428c50414d5f7ea1db0927440a7dc31df51da2554386a7a54a906b88bc36eae965fdd8d8657d007f771fb405e35798557e9a6009aef56e2fa71caa281d2d71190a7f09990632fd0a90bd39bf0d50cf8b1a02981f8f9c18c838a061083a9012486c106702d84a56ae81d3e989721f1561cb282b4b0546640a1ea5e6d8e9deb6e2e06331afcc76b697620892e0bbe5e4b2457166de73ab573f8611a1f80b5d6c422c073900b3116aad24de0f3a0acbf719c3ea3988b7180b8014c160535c3871bc6433615e2ece25ae9c9197d090c624b1fd1c0c0dc0a8286d070801706b12c6e587ff95a8150da4896b50d079916720ae98d2bef454ed81c969944bc4257c8bc372dd3d215fca33f816fbabd579cd2d0d56a60667ef624413aa08a31415638ea0526713b34d30661cb537620f9b9eca9e0ca09059b34e11691a16421408d55bfd206f0b868ebbe6e9524a87530e7eac8c8f0a3b3d3c0bb8ec320c000eb4b950a0f5a89df0e3def45b0507e33fc65716c7f25654279a5fc2bbb791669c662cd9ee4aa39ce62389a39c175a76af872761a0d7d6ba436037aa1d7973c62eda5b1aba73eda03bc29ad35bc4e73cd0d34fcc2344ae4f6ea2aa4ceae9b1271ca760f1dbc0a03bcb7fcf552b02ae762167f9c545a6a41dc9e7235f35e42152451c14d5d2e2473e2bb1a7663b038cf79f831fbf1a41399f39950df4cfe176f8f416ac5a09f7c098a38c98b3c927aa2fbb697f0b31de0df2935f1839ad316e184e6e2be53b26e8ecdd88875969f73193c263885ec0f809f6c4fb0f29ccddc163e0a369d4e211774edd66eca0ba7a4c3ed95a8ae40cb70c1c693aca14c43ed386b24c54a524893515afc1fe2048b2c806b04fe8bb54d320e34542fd88ba4066eb37e8d2dc3d1c5c0eb4aac5923d9a76f3f610fbace67f550b5203e31a6e93cf4aa2ceb2eb429338628bb8c8d74bde9079364008916d853126280f980d9dd74e40d3449a65c58bb6e46b691fb52791582cfc56098ab40016e046131a182c29043533120706c40423b3d7a72b4dd417623d49548ae078f503e39425e044db78c0b65a8d5c12b5968e9c4897ce33b75f273e1bcb07ba86b3680161be25abeb05cb2cc30ea5db9a3ae57f41bbe72e94f6d8cc3bab49f93700c6dee1444990c542a8153af88f10494ee60e068957e8b5f945f483b67ebadbff389931e049939a0f6dca66cdbcc92fc7af95ad9d400f09e415a5ddfaa0ab5b495c9848644c8e68e964b84bcd453f28e142b58ac7795d12f3f0f47f20d966f0252cbe851e5e4eb152a546cb880600923287414314b9bc651bf76675cb6ad8129fb192e57bd2a386935e1352ee250d3c45bc5b02d53f201f0dd3199001ac0c98be3486af99261294008cbf83dfcf5f828e8867b750a5a3f6199b715c2a3b2e9fc78cf446b11896e53a15d74145014079aef2c1db5058166d1039dae12685e5f4e9caa0f973d08ff0af43e3310cc9a5e573052ff7610ee045b505e49106ee9dba0a4070bb03f007f417c967e3c455356bbfadf7ec49e47a40c2852004a5ddb1ca97bdc04e43cf650f8ea68bacfd829b8c90f0c9b24186e76fae090eb90a0007ce265734afa1281e43bc97fd59f05333e36138d5b1e2f802f83d18091015291935b2a23e02553970a05e55fa032c6cd4580f27fbe48db17a7788e5ee6f4074a331de655c63453a71d691f68a42db119e9c977de2b076dea2d63df8373c0090794bc017fd689b3bd7bb3205c9c8b9b60ae44a9c961ecb7a156be601ea334a873a870dd16fde4c2239bb8ee4788ca3b839c18cefa2c59b21f019f0b37e30471098be5daf339f81e07688ebea1e0a691e6851d422d3b74aac8d33de2ec6894006daddeb1b0448d6165971ee04a4bbbb7465093ab6950bcf93a930e7c00f8356e9d1bc67da851e07b79e39a579b39caddf4bda6333dd9c46c2a72cb2b034b261e209c301231cdf434b7e828f2c7f4464fd6cde242445cfc43c77ffb02800603bbe3c6fa133f04203b8a7d5bd1cec0efee9dd082435a3fc13bda89336040fa736b11e51fe7aa5ccfc20a6507c42bdf69fab2221e1e382593c02adcbf45b5add7a506c92e9ac02f51e620d37209ad315b59ef012ad22d3b3bb85ae67a4ed1f1a4a12c901c2549273db085365ce1f769958c7b0f1bc64a66dbe8318deabdb8986dda60f4b6e648a3dbc21263225b8b325402b39f406b7d23937360fa309a5bb68c60d1beeb67800a3100047ab552c63c5f777bcb8160ed2d0bcf74bd604723d4fa42f45b13874e9e95186c43835b3b80b5b1028acec69d0f71325de08d5327e9d8e0009896f340190fc7232b2991e1bfd1ff4133eed2cb74ee46c98f7b2a170847355a911f9cdd4c6c13ce38cddf9f9da12caac149f750c72238ede31641f0183f2e2bdc266a4f1823b01a888fa2f31316897220b27bc7461daa80cf6fb489f53d9f93ff6482e016258325523d4ea4d421566ed4384a3db5f5d987aa9401250734f5b7352f0d87befc1dbe41c92a4101e73a0b485923ccf99520071d634a14a8ae50d44d118de7a11743a78e936444da33f07ce3fe6a5bac02c14ee24de0752df349db8d0b2c2ac6960b8c58d3338be231e3a0a641c3a8326240b4593cde9804adafed63a74f0fd12b1a9ebefe57a445a814301addd9afe8522aa3f6f7206153ab1e480d043dbf52f9057adc890c5942f3801b6852d1c845a1a404a83efa945e3860990f826ed5a85574fe5182748320a55046810e52e0494a23c9f83c18c71a462c071b3903cd222be6baa84c8b9e44de1be90a54b6ddec20ee637e4e31337b734f05942b1e473d587bf3a711b1ef22391f2f6601cc8ebc6a92d684e72b04a1c99113ba37d665d60d8969bc13a5ab1da250b79960f23c0eaeb799deb659c22b917ec462a4f3b1a2b332cb16885033f106b17d04d12d2821d354305e6d04ad0b3bd3fe31488089727e858e118a5882e59628975ef7ad0529ad1d58b22b32c00a852d7d66b26225fae6fd03791dbe0992d2079d71b5e93972423cc1c3b2c4750307e12b136b6826583a11450877eae119c3ca6d26d6660276bb2a2d51445e14743b1465846471ebc9a8820750ecdd4133e7956a89034b7b6b427c53840f5ba764501366f91bf26cfcd4f040c261466dbbdf1880781125f3c2a946a936a61697763341490cd65e0ebd9c712e71224408a6210b7762fafd5faef3a9a5871585a6e94033c199d77f8e7989771fee7191990da6b5afacf4c3f5c67e0fa39af96315f44d15a02fd8e7cfdb37831e87e930a81e882b293284689ba1a5c3e7126bcdb4498c5b045ea120e4abbff1b4eb03c1799ab56e1155ae0fbad636244d1a9323b50fbe84836e19a98e52b5a490432a88d2a294441f4d16d1ced6ac6e02040fc82a20cd1b63b2a87f13c12441a199da27b8da08a9822f645096078ab13d8ba83e78a970596418829f786a24274aaac14ce13bb6909a26a42ef5ca50e4af0810e51f85df0ae9e7e0f9a9e749fca9f2a7cf816abced80cb722490ce22b75e441342ef96ca933641dd02cb38aacf8820ddfd85b9385822e5bba7ae972a817df8a66f96db2666e915ae768423ec88f188968a321d62a8178964af08cd2e104d774a34a87c4d4c00fbbb3834cd3a8b2626b0a015b393137efc39fd82249d0b67ad09043473ed2316bc27c142f254b0edde9f1805ccfb2986eacb51f67499b124b08405fe7f824b6b39651a4f9d9ba8753a5b525b5a8ac00b4ee70f035ccd38751480f8833a71680278183e8aec9d776283fdcf445b71abbbd05a6896653bc7d651ba5c680841eedc27a3266ccc67411c0b458d67f63d5a6daa13f31b62eb8b55f5c155ac30873801bcda2c0c6726a64f54527511838329578edde956aa40ca0d8db018d20f5a86401b8f2f8d5daaf7edad1667e97da463b3451f9e66606912225bf228fdc820ea3db5d6baad7c2905e8c22a22ab237645cdaef6db8b36dc9dab536a4edb4b8fdb24f3f250daa0e47cf7924e1a51b890ba5d81fba2d363dfefd2e90bc52a74320796f53465c2683f8be470ab5b2739c76cd445a8f2004b672115ecb8a44c7e29e2a04b4b5c07a8201c512357b4297056ba1eb08f706e259408c3fe2778bfe47e5cfdb356dbee57a0b07a26938e43e3c81147328375778c26e7706c74920e3f8c416dc7fe525b4b781f7de7cdcfd38649ffc9e71914596cc2a1a8a9b1ca11d31346982a5e898880c5dc092585f19bd2d171811440a85995b8fde6b4b1646a1e6557e3f4c95479690c1357fda02fc43e872074bdde2f020ab9647e6c9210bd745e1fd1f98974459fbd8cf1eacef6fa9b5513f524b8e203631c4d520f7da9f0f27153f976a125374edcabb5ee2b6024c33b19fc9fe2bdde45f3619a50912b174cb0603c186e61e065092a62f25757e7a7512276899aeea41c12eae45a019dcf612222d2c1b0d273a91d172cb93288c354e1c76293fcc7d6058169a804abd08557db0a5ab1086082fc9a4b7a74bc324dc109486c8778003825dc43babf0f8f04e2df4c69863747907ce3acf49944c82032de235bdb5a32fd364e9f13812a59aa92c1471b97eb3374791cce52a1e011231eda1f1b87bfd4b192eee36f40b89ac4525f0803108f2738964fd711cc844025c304b5d48020616223bdf31aa4dd557ca1931068551223374dbe4f2668037d05c35847884055e3ab5c152ce466865d15a01d3d5c7b02f2b1c42db858cb49d39143c96426b9a6551b5b90949f2eb87631757679534d21a64ddc36db3dd172c3ab28e378646efab7b6562cb76fe7e54c62ad2d6f12e008c49df8e2525a89169d3f409075e6221e99a93a6a7f463960fd47524e9954df4f609c1ecbf7f23e546e6c04dc4144778635ba1794c04b00af52d6d32684898e9317bf3b460627abab17cd42a055b28fd6e2d58f5a9c6df5b70d09596a29272329197c07b0433454bdfc25c02934e00115eddbd462e4577f2ceb67c44982d2c2b84eca7652f46466548bcd9fb28a60eb0f2941fbb67b2bbfc6d21be3379eb37bcf56c6b44ef24f50861b00de64cb3d6b0c060d955a50f5582222eb2802b3ed1de51bb8af2867d5989c45a92e7c7ddaca24fa404690d4bf5dfacfac2a22c0b7252f2e57fa74e02eea5a1b9797490ce100e487ba3bb892dc693cc60c27720aabc9e8057dd093b5d4d20505918635a1af736eada7b781254afbbd4a94c720f1f73e786d85e86d327affc96c1ceec4e1b28c70b113a61d5a5d15cf859471f435ec632113db6421abb55c6dc95fc8740acdfbaca88f5f52cb5bc31314b791509b1598e3773dae0ed3df6e13fbb43d6b91a1609c5d2447dfd4c8032d083b921e4939953e954a1c63393416d3a1d83708a9cabdd1e34b7bc53c4983f97742da9345aeccf5ee4f454e77c883d2314b1c6c65c2a926b63776e5f2b2c95f4d47ffba37c9b6f722cce51252cb2db3d87c35d2e5d25f778fbd950aa35a99f94556eeeabe5e60212c8484b072b5881cf2791bb6e618d4a407ac5841d1d84ea0053f32eb4d4e4abcf8dd8adb4b8f7d8e4122b4ded650b64245d8363663f109ed85315872e1c3c94dd2fbc6841a0845564b617d80697d51267c3827b588a48754838bd51abb13825cd36b931d0aac8503dc661e99c20946f185361dddab351e32385fa2ceee6c618b20a4d58cac8efd71478dc3821f1b44ad685a1a1d05bfe0087ca31db53adb69e9a4373a72901da6b761b2d5b04a470d90d312607eaf6189a71e1bcf7f79b9a58cd4a763a225197c3578f2f79646cc8b05c435740658e9065bc7343a21c4accf676c40cee25ac331941db3d2b6e27b3a44f56a9019e9e3586225ddd1bc29dd9b1a29a384e151a30ead38fb03236ca6dce87d46081ac9c54ae9254f70d22773c86f2b8a151e89f9c1fcd37dae479dac3207c03324a6dd0a65bbbbd76482a02eba30b02f58699f2cae7246e94b5e2a98c206435dc03cbd31989c0a83292745c8b5c57ff0749402fc3ca04d964d99a6fd5797b90ad30eb9b7b31f30b9fe80e2e666475596f17709dbbfdb54212d0d1a99f1209dbf446610e7b7f15370c45d8af465fd70c690045e0540c0500a3b81f489fa60bc869957c5f51f10e2e3d38beb1f8cce010c5b01ba2422421045219cd85c8ff667c460c49a519b94da79332ae6af53c562859499f8be6ed3353721e334b5087eb2ebe2c1114d9180e9779a9d3f1ca20477a6ec856c423be0232afd67689ba661cc549e08f75ffbf24fc42d234994e66470d1ccf7c48f86a1b48a81c4d61181019518afefcc281180f5d19c45e3f59d138f45c6ad546715851dfb6b8def5c350c277d8af1b072bba022bf1b1f7e1f8806df8ed2d6f6dcc4b30fb6e1700ffea230b5874bc89e9102d1629c0056438c1dd1e08c00abe1c62cd1e48c001535326ec0727683cf4b60bdfaaa56b9f5ba5064c2973f1fc833b148588970712a192edd4ee483f698fbf81a6fac471423c4b97aac52119ae5af7846a74f314cf587c0d12466d756890513df5c7fa9fc18330832df9f8473bd3ad7c1bfb6e6207ed183e4d015c58b5fdccdcdaa1fe1e0dc8b9792fe1c8c5933d91811967c0fbb31678d3fc578f5546f90389e232861b30088774d8a459628238c8554700599994923bbcdb7137eedcb76244b70f044c59f7e105a3822bfbd42e8e8b73b93f2c54cda5835738b51af0782c0b50fd6464177b85e414aa2b50071e2949954e43b0d24711abf56d6e800ae6cf8231d20a8e9ef672128b1fd670cede931905f65c8840b4c0c99d70ddef68663272021b8bb387ff5a0d7ae1b24ba65ab0783ea0235f20b54bff568acf1ec016e7f6e4cf6babb5914c03d55299e6c6b84bba1e92d4fe9254b982bc3f76b53c2bf3a007a6be2e3d18c0b3c860392acbbfdeba93aae1d1957a59f1a809784e3a791f428a3fdf6183f99039506c64624ed6e74f263aa7c19e14d1dccd51635f5e004343aef20ac11ec979936715701a820f9b34733b456e29e0aa83e6d540ccd037f7eb10c9620686ff5a27ac04f2a71271908e01c6f6a07bdb7cb16d84d8c1c7a18dbed310c2610bf5695c3e95dcdda912cacc075c44714d4bd0a775f65d6a69ffca7a7e3977890c910ed3f27fdb2cc51605f7446d9fe5a7e13c48c921b2f34d70f8e84469e6d9b43b4ab2854a1caccd81ff6c2e0d963dea992902e5144288d16d0f8188d01ebf147849a7b1e9b2ebb155f8f2902cf5a7f4cadd768c25fb907b97319fd7e897ae89caa42d9b6a8c066fa17e3b3235587a0376fbdc7bac3afc35c8e16cc58e5311ac5beae69e8ca7e94100268553f4f76a5619f0c8fdfa18eb468af8d51b8928dd351ae37813c47afe73c7cbcf928c808df3ed395a0e7e0cafe003dce4eb74dd71cee51574eaeda133b1fb0a3442e710a6f06ab29e7f33c0b31ca3e3b41094bd4c48548b8cc1b4259c8330dc00a3dcd8ae70d0a77a6669348ca8638c116ba91e9658d3e85866f77bcc16400438b68a58707ab1fd4b02e53814a3c7e2ad4542224d5c597e5842224290b9ea7a9edd8978a8c8fb94f85f837ed90d075b7c04992a167b3fd3e1795b97d87c3a225961b1f4de9d41b6b81f11ee6963a14bc8c048e9e54260adbef3537cbadc8df12b7441ae6e1aa85a58130094f186aaea06607c1cc3d4bfa06ce304b5c268977b03d6416a6748ad3a5d9d4ec8fe94ded80c0a2f0d0cd3d052a69b9129b217819fc2c4150192bc729b5edc0f7609d31a936365ad1f306a6aef0d892ec27834b9ab217dcab7b3a82fbb2dc5c530dd7018b2e102b7ea288afd850f1aca3927042afb6c69937ba552121f5b2c9a78fa21321eb629054e83eccbcfa490ab27717d435126ef5c9eead9964cc3376221e000aebeba8c9d3b3b041be98e9073aa21758b5ea04d0832f04fb99377872248c54a67ee7bcbe82db24a0edf465d8654d0c3ab5c41aca66a2533c05496426e11d3c658e0a773ef5358348c3526acf586ed06d857a2b2b19044522f2811db6f5e35f22f0a1edca8c8d49ddb32fd47443d2394f556ac9710c510ba4d793013f3063c7e78973dd0681cd02a6209a0b4202b596720e93945e4ed1ccdfd9490327a9a90d7fbc23832472af6e420b18759fced10ab618baf16c4043e94d84e296d3e5295fdeb09fc8ccba2dcb41274cc62b9af00f95a0885d51521fd9727c9a24ced543ab38e436ae80ccf88c4fd2ab1f58046132470bb8058387e098aba5b9361a140c824c8ef3ff12dbb787c9d6b612eae4c248319c3c865dc3c913a47efddb5d202d61f45f921ec60513803fc70afb50bd64777b10f7d649554eec1335b980054a1beb72416d78a871fc323589a6aa87e89d07b6f282de5190cc753336c96126820326dc6755b92e70ec41ffc6a47856c204223924c42f7d67ffd792fcc3889e63f5c600258311b93510ee600a2636b2579357ddb745b8b6489dc1ef29703ea877eb23994ce8afcd7f6e66999f30a06431f46e14e3e5c1417918ed5464d2b723fd0c527ae1e8b04e8e6f1dcf5e2b03cc89088f7271ff5317d2239d87f25df731f2c829d2c8c5e57912ebb159b72827ee8389b35f75f29a6ad16fe6e16c44b555aa9944dbc96ae0b1604d59038216957ef4fa29f452b5b27657431526f66125cbb6c2a03610ec9b82d0147c6cc12643f1143a270b9dd52d57335adf404945093315295d7f489ff562949a17b3bf97e1c08ea044dc581cf06d94c156f831e137e0c928ec018772da783813678286d8a0089c0a3317bc723dbe8bd8610a045cf614309e0257014c5b4cfc82688b70127d68400c8a3305523bf276903dd471a6a6da0faefaf07f25d0d97c713c8c1770ff55602d7dac5ab097b26206133af8bb155783333144b45dcbfc574900268996e2477bb7d7fc3b8171a2ea12d04a465af84e82a42f53a555cfecdb13fe2526bffaac5de2197c3a5dc993ebd7c9c77f14b3e86bca1579f2eb38a490b700e7db049e685b4bf6ec9e23fe9910db357521408aa0978d43b66ad8d80d075f49f89147287d0c3089df397e27dcf21f70f82567237c0dcfce6b85f3e3333d4eeef15f53931641e927830e7a0cc89c5d11008edbe92f904218d371beeb1b4f9136828205f1cb710e29b8115da70396a815cd9112791a240742ceaea4caa9a962e5cdc1a7f753c20cc7e89026850a342758d04ee47b4814b43ceb5236fffd5cf328758afbb723f92557fdf70bc16b3cea036f2deca4f4aa2bf7a1576b33d79b110a09f6b292885bccf7c6805e21b0748488ab60025184a53a47bbee1fda045ce314cef2e9067044434ef088a12b3d6c1ce270f2b378ce9436736e21b38c92ca8244ef3ac14a3af6e5803cc8d4c81b0f3fd412f3ef668e34ed585b62603322cef5bd8fa79aba174a447c8f9498ebb8a951545a2a735e0662bdf7fd29cac541eaffde43b21c429ffa4b86ec1725d6e832116c3f6d979641778a698848cb70ae22b03b26abee697fea238f8f6c61649aaf73a199e9d72fd3e4d4f126a17c802a286803884131fc5d0b14e93fe56586d542ab511f6d0e762e83560a930361e55fd88e1a18a7cd2120fb875bdd120b73f3161908119fa0d709f304f9d87efc53a2ecab3f9055bba5a70524a91cd3e0ee610e55bf8be531d90db4d7d949ad615151ae345f0fa522bc393c02a9796d7bcab3fedf26f1e1662e319c12173b200c6f72cfec3913c7cfc58c395d3b025a6ae4a8135baddcacb538e195a2b5750f1cf7e90227273684425e01d8fc67c35dfc8ea2f3d768c3122bff90187d207738ee4d758b776e53cd68da3118d063a1d0f5bf2a2234969fe0d76416fb69693b1e6badc9160d6b36dc69121fe2661911e704be85a8ee1c6d30bed5ddc3398bd76e0180674c9aac92531dc4ccc3c3c1ae721c152258507a92cb3dce4c6668fca56ccc6348c7cd6dac295010453725ee9694c2ed040525f66690b823115f13838bf5031d7827480bba37a936e151d4a45215c147655c01b5ea6b2cc3928c21dea76594baf491c37ab402416b2a244c99b8ca18046c01d22a03828946c15246708e70106ad758347c2f6728d04028a278c5bb2fa491b30737aa950fb2482bba68b5c12a7435a515a8e890088c4aa2a615a804cb91d968af8a7a82ca2c455794fc53b357193a46b1ea1adfd640a53250b7255d2eeaba76095e6744c7463c031de7330c3d83434d8553522cf484a1c3a19750c5a17a5147a51e88f5c49183cd373afa0afd1ceaa9a3b094f7afcb701488a8655431d4b9a827ee08ed15531e869fca16a84cb08cff9f241f4ba8f0285f65c42f346ba943ab6fab32d1a244b1652b6c987374c8dc11c732ec5ad6f2fea1b02efb1dd8cbd1cad184519ce80fd09c1b9db7ef07ea3f7432a999aa46d44d422bf2c4f9ade8553db582f75f8fb2e47566af15f565b95b560d37a9a72e109848ac3633f5bb2a63d00c882c1a059c18ca30c54ba7284465a04e75cb4a8c8e4695416dcc018d169f8a942168a6d486701d30f79b58279cb5db3c09a632c18b06369a218be336273cad4b0fa10a9dc27d41bc0386ace2214127f248d3a4f32149e19d6e89dc6af5f07cb95bec3cdcf3b884982df87ad8609e354a6c08ca6bc63dc526c36e88adc6c68645d2d8ea6192838fc62780eb764ee5cdf44f346d2f41cf5ed6b0dbf0b46dc734563e5d6730a2d92ce4bb2a5c2a8e601923ae4604a3e05c13e81eeb227c5a918498e897caae8a611a735141f7b59ca22138fce786949d91d9c831b418506c1ca03fc8bc5c0c63da50c17a462d56fd724401c0d66e5c85d05a5e48ca310c2b4ff902402288e3a3f6030b26694086c9ff4c1f0d4e066ec3024aa10591d94008b37af412a40d7a0c9814b8095871b0d817a845a4831f3af5dd66be611c05b01d7977c159a1431f0dc1763b774f348c8326176d47460d8c9014bfc7263c1f32a0284e12c6a2b17e64a36932d79cecbdb41a4feb715524f2011b14790be7073739ccc10e38335c4566fc709bb14a447a85e0444b9b597196626524361376c9ef1038ba6cae039b315e6ca9711067e837c3661bfe0a721bd86d2cb61191d80cb6c5d5b9bd91136c399ee3d8b691d98d65e51887534f6c6677d6be0b9dd88cc9a128cdae8d7af166dc144cc86ae5c8c6b46caa02beb045f90d3660b0a4900330333333333333333333e3ca4bfde37ddc1ff7de8d60c232aa45279a9292921209ac35aebce01de7e01dbc8377f0d67401ed0da10d1f0e4d52abe0757486b10e62c41ca48223a94327bb321b1fa7e055a63ce1d2e660724e0a6e8ebfe737785c565f141c4b1e13738c50991e149c94426d5585f6a5ee098ea47c5b29c7098edda4077297396c821f2ed492e698473a98e0b57d479bedef79094e698ae918f197b1129ccd6695ebd2243829e68b15296548706a3e6bf050691b2d3b821f3ebb9d49eab09fcc08cec713724cc9f4712c5911fcee30daf6690c11bc4f93de1ee45552ce109c24b143ffe8393cfe08c10f6311d467d6233d4170e3fb6e36eb2b3b0304d7b3cac52c5de59a1fb831ba6a5f7ce085cba91a626726b7f4c0b188eed1d5b47a5978e0a4c6e6cb13d9819344ea6732248f611db8793a3e87b4505a7e0ebcf6d0217ad8e3c0cb4813d66e3c546ee06d9eb7cb1f04b081ab5d9eaed2b33adab5f082d87f94ef83acb569e1b6c725211d3c0baf652e4731480eb46659781b24180b2779e6c95154d58c312cbc489a7f2bb2fb8faff09225c9a1f9871c6deb0abfe3284a9fc754af6d2b1cf97854438ea7565b56f81a3de4f918415388aec2bf64112d4398e081aa0ae762aeb8acfee1a334156e69baef49bf7df944859f3a948ea297a7f02579a648ee5c1fc4148e678f9e2d867cab580a2ad4c986258f144e942011fd330a5f2ddfa71ccdc7cc88c2df509a156982c71d0a377687f7e8e0592d28dccc21dada625dea3ee1f49d947ac8924b9927fc8ea12558b020695727fca05eb3a74cc509672285e718de849325a7d4d392ae929af0439248513535d57932e1a71c4ff89c35740e0f263cf7499b263269adb984f763ee36d1e953ca58c2dfd80c7fdf1e5fa412fe86ac1e49ce9a630f4a38496ab2cf5c26e1ada698aa6221ea2b92f067b3860f5f8984ebc9936d65b5ed0f020947cee6db72103d6df2083f847f2c094f911e71847329dc86d481a7a46984532187240b33c2bff0d03767c851c72ec20b29592ffb304554116e064dcfaf19bacf4f841f7bf0a0437920c217b90b73c1e3104e6a5bd6beb74a1343787fa163f3cb66e1e342f8d32621dd29ff84f0c2079d6254f6993708af3abe93f1141fa515841b358636cf5aaa3981f0ca3c27bff1f3ce00c233ff20d774567cf40fce775027a942747cfac1f50fa66346abd14efbe0fde5caefb1458b1c3ef871920d8f7b0f5eb7e414ba23f5e0556bd01a3bf3e066a4d57469ed2d9c787043dc6f23fcc776c13bf81f77297898e2b6038d29d9071d07621dfc4f39cec17d460acdd1c1f19caa3e883707377c10739a433e084f0e7eaa34ed992da5b7581c9c34ab9eb482e58b191c9ccf9c2db25f8c31526f707cb2ed322d37b83982abfbaa4fcc698333f39f4288f6d92d1b3cbf98c2aaf755e6881220630d6efb848fc963560d4e48993e30b7ec3fcd64a4c19b481552485ff5c6340c178421030d4ef504dfd8b0ac40c619fced38168fdfcdbd449366011966f0524e89acd441a357b803168601a30c4ea4cc1f59c425bd5eac066490212f937073e943c618bcb4b8decc95a1ff3b2eb6bef0a28b2d3220430c95e6b0fea3142eb68c8c30f8e21ea4b4a9089721cc850c3010c3fb2455fd525f30a656c6e688be1788e497e6735e0b97404617dc0e1b724c4939d892b8e057e7cae361477af1e82df8ad193faf355ae1046468c1ffb19c3286adc6908f0c0332b2e08cb77a78cd1d7e230b032fb20232b0e08590c1be821ba92e68f274d1257fc8b08293c26a32976da4bb8e2fbcd8c2aca08b14aca00ba4808c2a781f89a7d45e953cbe80046450c1df4e9273e8925ce00432a650351620430a2d230ace5cd0147a26c3c5568d1a99749001053fe23f3e5189b858be31c87882d31756db2d212d4b8e2623c87042d540818c26f89299cf238fc2cc4dc6c542c074f1850714d72083094e6dee4de7d31a1daf8c25f821cdfc440c2a5143274309e590638979a2e3586720230947e6f6c163acb83a073290e06dfe9c25a40d1f117304c773f81023a6c4b88a11a86ed5f4f4be087ea8b6cf1fcfa9559808debb89440ec9330427cf769c329a840bb1230427b84b47f3b17e529d20b895dded953d40f03d677498f0d19c477ee04555e9fc99c423f73ef05faa8349ead1d7647be0bd664fd61ee450226878e0a4c8a5327b074e86f71073c4e69a4a077e900d39c8a164fb2293037f656cc35598780f0ebcaa8c0d9afd22e3065e4a15edfd4b82e71c45860d1c8d1e5f4ff239f8e8520b377264c4bcb1ce63b2d0c2b9f6d68aba6916cec9c7b45923a1a52bb2f07290104453864f6d2516de7c8e3b280d8fa25705168efdff6726af8e1e79857f96a5239f9493d4872bbc1e4d4dfb186a3d6e8567993e9ccfd979b4125678eddee5a1a9526125abf003adcc9a93d554481255f8697d36e498c35a8224155ebbc75a1d9ed9c1ffa8f04c730c7b4df6e6fe9fc25b93e41d95b973646f0a7f2cdbecc77394c20fb12395f0b1a884b449e19ca71ca588ee8d3b7b14defd871134ea3ffc220a3f720eed22c77328fc10ed339c87b1fb9941711c99c7c19f70433e881e8ba6d58678c28df6618794365c6ad209d7cc2549ce0ed207ff72c28b5531df99d4a3fcbb09d74396ca13a2ab09a7ec2a64dd0489566d26fca0fe3ce68f35cb6a30e1d9bb745f889472b0f5126e1e0d9fa36bb58417251ecbc420e991a795f0dfaade3e085f29524a09a723db7d32fa78949293f0dca26886895312fe69ae0c1e1d44a66846c215d90ed70e2b64c925249c1829ddbb33a5922e1fe1a51c87312186565c948e70a23e78e9c923252936c271cd718a5ad4a41c898cf07d653a74e416633b5c843749b3e414b25d21a1225c4b216ca203a99839897025367ddc610e39db7388f02a5d468a9eaab4ce1dc2ebb40e54428721bc1c237d20e139469314c29b4a3f214751199226841fd65fab669f2c293908af2e0495a0412a624e10defdcf0709afd61e7781702b4d88b163f848313a40782672f75761d2a4cc1fbcdf902b85c5f62d8b1f5c8ddc954f34d607ff438a629b35ab6ccef8e0c6584915d6d91efc389e1cba87b3ec74991e9c98825d5a87fec9c195073f84e8108b1dc777b6f0e0e794356f26bf15c6ba83ebf669726059b3a65776702d6cc2255955072ff769c7b9a347e242073fd688ec40dad7d3630e5eceee961e678b62b71cbc4a1a25a925264b8c83f3f663f963ea8bebe0e04cdf5f86fb4fd5aa4911e30d6e05cfa8e571109f330911c30d8ebce6ded0b8b4c19f2ca99307f5fd5f151bbe10ce53071d522cb9585d839ba14722db5c4c88a106bf7c443ab6b7480d49831fe608319a79ec65f5e130c44083a795455624bf60887106e72fa9484bcc47b61dc30cde799aa7e89872989ac528831f2d7325cdfeb125916290c10f324d6fad3706bf277ffa24993e77c7108317a1e53c36c86676334618bcf940ae03cd124cd3c4781c31bee066180b2ea576c966e20557d5524564bef34f7e17dccc79429c489ca70c73b1759683185cf0da5375852017184c83185bf02ace3f36cdac11fed1829bb53abedab6919c3fb64819c4c8821fa2e2be3d858ea7432c3897f264df1837358f71b1750557d42c64a4325bcbf62186159c1c6677dfacf6f5e657c1dbd8398e3368baf78de162cb84e1c50b900831a8e0df98faddfb7de0999c8217db248449a99e66f2c59182638b300c181d460c29f8fd3972d49c3c59c48882575953d7696da9fd5070d26a44e592cfeb63f182055b1ce58a184fe8b25648bbe49ce0d5f747e9552fd61a89d104275eed82cfa4907e7ba18156c46082779b43878f30cff0314bf0a254f5775039281b318612fc3084075feef9c24d6c8f102309fe4fba0ade59eab23b48f0d6fe55a5031569d31cc17f995eef0c8911344611300611fca822fb3f6a6d086eb7c79184c41cb9c4b5841842e8da73bfa46304c18f4655e5644323463e06100c125307296ac80fbcdc71322dedf8e08e37a6c5b85c7a40e6efcec14cf43cf02a25252ace63077edcdf9922bad681a329f2479be53843c639f0b3e6385eadf78eeb4371e057faa0b6de73032f8b86908378897677c6b0814925e7ca746731520ba746e2dd2c2b5af8173dad66e224327766e1d4473ff389b62c6e93bc19258e85f7e95aaeb18485571da444b779b5c89157b831d53be6945f336c5ce1a470999c337bc8f05be1c7d6d829bade53dacf0a37a510555931ab36c7b10ab7831c72982c5bc9660f55f83163f9b6844db726a7c29c474af2c72e7563a2c2f73c1fd541d3fc3f7b0a377dac1e3d336b8a536ed55c653e61624729fc6c39bf5d7497983325851762dea8f48e2c878f1c459646f2460ea273bc280c8b312387394a6ff443e1791023e4b01550381b3165a3867cc2c9f1519e34d2e4709527fc888f91d6f368ca612736fb73959c533bc6092fee4f5bc262ea33da8411368410c4e4628b0c03d08413d22f7d1061fd539e4c381f7afa1c652dd9670c261295d26097bf84f3398a9d27ceea52d2125ed234c135bbd587a152096f43fa8fde424ca97228e1e7d618553e3aae8f3209e73aad87906f3b7f1c92f0b27d881ac3a5657723e18739bd234374dc390909673407a17a62e411841b4d6e12e308d77ebd3e8a4eef5d69849f2ba78df0b072dc3d23fc8bd5dae571ec5c2ec24f7f4babab15f1796ccca525c2d97c215b9e17d91c8a08ef3f6b4c1faf5fe6104e84b7dca14c5b6df20de16be5cc8a9d42782a1d45d4ea3808e1f6dc6f068d109db3c7208e209e839298d22505e15fae79fbab74200a89946eeb3e8e2640387193ed347bf41fbc8f43e71cc9a3fd4084f475b91493c7edf5c1b5e47f29a7cd714735f3e178fb38b6700fae87afee20c5d5839f83d89a87b3bb8418427454f93e78f0ef53761c5479e41e7707bf2c2c7c799437c729b583339a3bb68cf920eaa30e7c8c8a31e530c71da3832ff6ff9dbeedd5b49c83511573dc217b8614b61c3c2fadd70a9e38781f793c34724f761e0e84a87cc125f75418dfe0cabd44ccb176f5c92c089552f02ca4bacc32a131c22878193af41c778c1de51c14fc8ce91d85f4b149774ff0c3d08aec0e7382975324f6f6868ba135c1174d299364d487d162821b7294a3e4b496e049fba87c903257564af05245c8d2ecefb72a097e10527aba8b69ec4307129cba0f59f5417290d271042faf5fba8c7418c10d1da607ddf3299d53043745998c670fa34d0e11bc645d15dc21b81e955d730826590bc1cd1965b662d9cc1b0427be83ba9f9b6513089eac445947da4af0fcc0f95c1f7da776b3cf077e84a76fca6678cbf4c077b94f159172ff8407de5f8e63794739c00efca8e3ad4992538c8e72001d385ed6215d8b79b8ca0172e0845039ccd9c73f29e40038f03dca41c2a6567b0939c00dbc288fa255d286f2200938800d7cd91c2b74476ae1d846b9ca10ecc3e769e19dd684c7f9cd3d3c66e14713424c394e9bdab42c9c9e0876962b7786742c9cc91bf75148f9c9372c9c35efe0ad738ae9ef573817c3c71b7c57f8693ac5a887a487dd0a279d44e720e5ce97352bbcdb8ad6395ac50a5985f31f4a6d6c8d4aad0a37668aa7cc4a332ea7c2f76023a36999dfcaa87063b6edaccf1fc7fa148ea57bc8a19885aa4de18d67a57043fbdcb3fca3284941858cc20df691d78c2b0ab7deb447c35068bf390812ab8282abb168159a7ec2f598bbaa247b2a0f4f381a835cf8d8eaf46527bc28293ec55776f992137ee8f7d6482321addc849b3caadf9837849b50135e8478554e294cf62f139e4d5d761c478bd51b269ca834d9a1b2be8413a2dd87d1375eea6a4bf8513a97927b9f94a7ae84eb2396ae634a53c20f52ca42e6ca2c1bf524fcd414434e1d95fa7b24e1a5d4603967ef38b427126e0e326f4bf44f8b1e4838993654f6795f34cf233cc93efed31ec5fa8e23bc10b52e6a348494368df02bb52a4c76480b1b46b8151a953ea3c2ad6611aef4864647b5390c1945f82942fecd914984137a2a6787c84abe20c273b7b69aced4f09743f861b0581fc28518b518c20fe672fc9bb3570a2185f0ad627658dfa7f21342f8311a72c60aa61d4c06e10762ad599f20fcee50bff271a1251208afde526c594b5c0f083f8e393a8f473a4dfd07a7cc3ca60a17c362ef07afdc36c264fae065c8c79afae083a7c9c6638cedc1f114317f242322293d38f741d24abfec6a1d79f0623c6aba089ac3e8c08397af4e43dbb22ce60ebec4beede0fb658ec3b5c73373d781d90ea483db95b263344799369e831f74bc695210e5e0c6d439fabcc6c1dfa4b943f1150eae654e92f9dc342a7d839721d9f12de5414abac10d1d87d9632b5cfb6c83671a39c741d2d431996cf0ac5243541fd7e07465956c1ec658caa9c10fdb614e558fe9bc4b83ff7190c3b8d5d67cd0e087f07392dd3378bdedd1a5ac0e0f33381bdd7bc3bc6570bd83f5b05346062f25e6495932ba778cc17b4bb9376214afaec4e09d057b0f6518bcecc8476390180ccefdbf77c871bee0877e29671b8f17bc145273f061da2c962ef85a23bd31fabbf35cf06354262651f1efb7e0a5ec142cc3460b4e6bc654a9e262684d16885e1d7c482958f0b5dd6247989bf82857d8ae6445836a0527749810217c7b1456c14bde6973850a61b64205e754623a4cade1a932055742beec41ecaceb102938a15fbd3af351705cbeec3f780ebee3a1e07b0e3b753a9fe05cd01056511b6639c10f2ebae5ac9247e99be06594778f3f8c0e1a2678b933640a1faf5b64095e84d09b11568267f769f1ef9104a73e7f1c7ae88104e7fa7344f5f4b1cfe3084e0e3a42bc4c99dd6d044722653bb06c566a17c10f513307496622f82769a93ea423cdd921f892751fe628c71c9b2b0437e58e62f28c06c1f730735467962e4305821f27c96129da64f8c81ff822b612c2f47a10a50f1c33fbc91b246687600fbc4c3158a69b8f5b451ef87d1a6fb9fb8314b20327d9c7a1e7f9dbf6381db831776ba596f9162f075eb4ec100f217b640c07aeab4fe6685bb63206b8819b5fca26967d5c6f001b781df97a35c7e553d7c20bda21244d7972eecaf058c93ccc5acdc2f98aa134e7cd70175938123d3bfe2894e68b36165e7ce8133d473e2cdcb056a1736d7988cf2b9c303194cb755ce16a85bef4c85208dfb4c28ff6917fda9e70b16185d71f555cfe2c4a8e55f8d7b1c74345b0e05154e1ab767c3908396ab44a2a9c9e994b97b7a35454b8f5a9c25a8edc29494ee1e49bdc418839c8111253f8ef963c8e42be147e8c9ddd36ea34073d298a985635cb1fa370bd3ecc3c7fc4ce1ea2f073ecb952ce23147ed0724b33d11dc530289c1ccea3f9c935a7f127fc1017347bc7ed61c69e703e481eb6aadc0937b9698e6cd2478d2c27fc568f327290183ede4de09303a9f50fd584dbe9cd639294cc5ccd8493e67148ee9a63d78909ff35c2574a6dbd6dea25bc99bb54b163094f428eeea38e2cf66325fc28b6e3d764172585126ebabece7c7196fd49f8c1d29b6f4f648c92f0d652847f0f6be922e1479914113465c57490f043aca6f031e5e0ed23dcbca5da97426f089a23bc28a61d474937b3aa11fe878c9d3c3a9ec830c2f30e3e9c644c49c3a48b7025b2cfa25fc44c114ee694e1eb2b89f02d3a8ec73c06117e0c3955d33c840e630ee14a6ad792397b558d219c7416ca3fe410b92b85702b7518c27f74ccac10c20da95f4f7910aea5cc9c42737f50124178919e6adff1658e914038e3b1fa67f324361140f871d47879349dfff3073f6c9c5ffe2d898e1fdc10cd41a3c61c65f4fbe0c72c6ad71d84d2f0f9e0e5a9081297ce93f57bf07269d80e2b7f90cf430fde86ca520dc9fb3f79f093bf44a494c6833f371e9d64b21bb3efe0aaa7e710edae1df51f695e8f3267b90e6eb7664df1618e89321dfcbba43ed391de4a9e83172cfbfc6a2c073fd2ae58173193e5c4c10be396fe29c2fa0f073fb4ed896e1e43a76f7025888a7bfe4e9aad1b9c546fed39d536f86e1ee788930d6e6cb986e6c91b9daec18f3247fd1e199d326af0a312ef8e47ab7325d3e0fbe5b0535a5b0f3e687025ddc392cd61a9e50c8e4d6a0e22df9ce4308367f159b56337ff0ecbe0498cc86f6bca9a0c9eca4756f48cc14d93261e3a2ec570598ebd30b8b93e0a9ed181c18f88ff5af7dc51e60b7e468de778096abf17fcb0a93f66c4f4a1ed821bf37b5cfa7555d2b8e07768161beb3129b305af42a37cc428b5e0474c512aeec982df9192da771c2cf861e63c76472186d4b9829fa3beebb11cbaf35638086fabe0a73877f31012fba6829f72ec9b704ec189704b69a191829f3b9889cb11053f7b700b4bf93ca40b147cad309adda33cc1cfc93d4c158ba810e204a7530869823716b3b2e4ac262d61825b2949aae6ef9827bf045f353a7b234b18f195e066f449bb764bc93992e07de68f526c4ac99303097eb2aeb5f18ad27e1fc17f9ff6a8e41d7dd846f0c27806499ddbaec345703bd88e93c6f441083311dc9a8ab89b5893cf1d82f3eed2df9999735e85e05884c672d70e490d829fb286c98a09047f237bfa851ca4cff2077e58e40c8b613eaef481272947c991e981abed9653a2689685077ed0d1367fb13f0eb2033f567c681b3d126975e0ffe63953150b90032f6cfec366bfb2fc1500077e249d9d255d3e89ae0037f023bf5c3612726d8a15c0067ed8d7f93cad3286ad165e6cb41c2a365af8519a986d9259a7c92c9cbc621d694bcef943167e858995e314652bb1f0d45f2be7e8520561e1b84792263d5fe5fa157ef0dfd3ada66d63b9c2d9f48186fe68ec2aadf0d62b47b0b14829cb0ab7630f2b7d24e12adc8c728b2947a80abfdea227582c9b8e3015be5fd545e5303a8e0851e104fb0efaaa62adc7398537f641ba2bc7b1bcc7145e6dc4d4ea94c2ed305ad424e37711527895434f72f5d1dd3c0a3f4fca986286cdde8ac271f30b17257d6a86c2eb3a3593cd80c29fa9d7f8cc7cc20fedb183af142a2d9ef05288b38b3df127e984177f412c956685ac70c2ebb1f338a6ca26fc186e42c2868c29af09dffeb2df8924136e6a8851e22398f0256d5427e98ea27309cf434b59f5bf25fcf451c75399551ec757c2491f6d78743c259c143cca3a940dd932092f3c580a9e9364f99084ff31855812b6634a1f91f082a58b1e3cb49608094fb3624e533fc289fefb0f153bc24d1edb46f8514cc5a7d258b95e4638eea369c5738e83f02ec29f0ed3be43c7b1ab5584efa16a089dd22a443411ae660e55d4a453f78a0827c4b4f92b26a3c27a083fea682bb93cb5c7aa21dc786bf5ac4d9e2f2d8497aa766df3b9ce4d42f8a9b9d36b3a085f543d8a09f2d6a320bc694ddd51bb2731108eb8e4b8de83b4660908c77fe5d4a492471effe074a8e17fd38c4f871f9c4a135bfbc32746fbe0044f95cc32ed933c3e387ee1de7bed324bf6e078b40ea2a64eafad1efc73efeee8687bcd3cb81edfe6af110f7e0cefce76d91d0ca9af52f6783b90e6b73ab81e5ad6e0db35b92a3ab81d8748afcc1d2e85c8c12b8b2995dd2671703d54ef8f573a38781fcaebfa63c8aa746f703a7a0e2ef391f345e70657d2628769a80f2ca60dce46878e1e877414346c7072868f6369bf64cb1a7ceb0f79de72f5fd6a703d7be4a04572be701a5ccb7739c75f4183bf2134a4671a0b5e398313a33d9121a6ceaa98c18fead0d724a40c8e89a7c5ecfe4af927836f39ce399aabf1a8ff3178926e2ec4f42858ff6270c2fc6d75dcbacc1f066f2a849ef09863c607839f525ef320390cf71c5ff04337d5140f29217d78c18f3653b8ea4bd1bfa30b6e48b19c2485b372990bce59f684a40f72d0f1161ca91cbedfbde318b35af08367faccc741d566b3e046bb1cabc574d074b1e0d55b78c755dd26ed153c099b434d9f90c9572b3861fba543e47ca779f1b0a3b4c20d99a952adfb2353cb0a672efdba8ac52cac5556c5baff63ac3437d31db3a10a2ff664ffcf9b6e1e7ede48859f2de95108d73aef3750e1c5ca1aa9627b8698be53f827d33d99f2fba5e698c28bbba41eb2f3c4985429fca0f597a23b59be5092c299bed8162dd9a370a3e40fda91dce5ba4c149c654578b95876677cc7a1ba22caebe650b829ddff7564c7b61cb2010abf446c638aea486df9849b428ed581470a911896c28627fc8f5bcd42a8eb550b373ae19c78960e82b1c50a0e521d48c20627fc2862c71d346724e0c5c6265c0f1bce43e227c7686bd4d8a270b0a109b7f307edd8983f7a8f63055e8071180e6c511b99f083905c6d5372112d4c50e92932965d1313193652f89770babb2445788dd259870146172940b584bfb221b572b08401461717d8a8847fd9f5b17b4e295157b4997d887a7a109d7b42cbbbfc4938eefeb9225473f6201e614312cef976678e241d093e4e2675f9e28684973aec3a5d5269afec1187f1b87338c29678abb0b70ef70a79aff1289bd036c2550be92b98472184ba1b8cf0e37c1693a6cfb1087f725853eb137b091b8af0673d7c18d357527f4d02612311fe9a7af9c7f9d307416344d84084af6934c2a52c1b61e310ce8c4c276d172f4def8621908fb78314a23c0b001b6c14c2db681e2ce6d01d734cae0e3608e17b1c678cae0c51a9a941f821a4b76c99b3e5941e82f0831c66cc71777ebd06a292abcdaaacaeac7c5e51228df87ff600e1af44358b31440c6697fdc10feeda2b751cf7e4982fb65081f9628b177451aa0a3b60c30f4e4ecb24b12f52c7aa45b0d1072f870813d9fbaa2ae40d3e2053a7eeb61aa6d26de961f4506334dfaefed88d3d781163aaec9f8fb1adc2c596bd0bc0c0aa056ce8c1ff402cb43a0cbfc8835f21e4683347cfc596a5800b30b6b00ae374c10518c9800d3cf871b9a475de1cc3a4f9c25841171f3841170fa851a3460dde2d54e08215d0c61dbe08030c177cb1c51602d8b083a7f59623dfd47182159814a4e0e08264d5c129b79c3938952437aa0ba81eb0410727786be6f76b71f54e7080018651c1165812d8988313527ef3205c7a6ffa2a0c1674f1052939f076112f2257259135356f96b3db448c1e0bbce00d6cc4c12fd390d992c1a3a6145b75a0cd7bf1451860e80aba48c11746058a81116c711b70f0f387db0c39763ce98336dee07faab75f8c1eb9e106ada32eee4ecb23d4253eaf8a791c39ecb68f8160a30dbe7a6c99f6d1c63cd9dba20b161c5b18166cb1050646b0458d0d36f89f39a5c89363b61e8b8b2d2ec070410a4e5a60630d6ea6314f79324586ac1729b80d3538d61dfc88bc5e5ce02eb091062dadd3ba34c35633cdb6e66290b953b58f49738213787101167881811a354ee0450af2be0883051b68702c9ac6a0622f7e1ee4622b055d187206ef425b7cf6278fe09a2f5a00066e6c98c1f7d850219fd4d25c9e8bad93822e0c29832713730af3fa4bea09e38b1680915c8471a80055f05da4003130822d686c90c1dba4314721746757c7e08ac66cfa7d58a11d31f8315b8e223950cb09831f7f0ea2b6e251ee3060f06cc6265708ff3959bee05784201ff786f178c10f7334ef318dfb8574c149e96f51e9a639940b6ece9ec1453ad58c640b4eca665b16ee344cb4e08b5d6a0a92d2a5f6f70e26fbdd7b2c389d992ed9edea9d2b38b235bfa1d6a3d86105e72a778ef139b09c1d557033648d930ace9b7cb470e9297819fee73cdb43b1b414fc9118b2759028b8b6a93f8378471681821f89c6a658988fb59fe07ab86fada8589ced044f52da48f1f19a857013fcd0c346abc87124394cf02d36422563489163096e481fc12347555a2bc10bd1c166869cd2c649f03227b3489535d521c1f5207fdcae1259b53d82e3a5213ab5467053e5e8e314db577711b4cb9ed006113c3b931ca6979cd9d3b3310427a70c95b4729c3aa4674308ceca7fe4f164f74fc9b3110437f465cae1a291f3793680e06d0e391ef994424ef36cfcc08b6079e22323692acf860ffce875de4198e46dc1b3d1833f7bdc23d1e581131e9f63348c45b703e762b4f7adeaa0a203bf62088dd530078e7814e9adbe0d1c789fea714ef213a6be8d1b782aee5d975ed299b7610327b79a4711e9e31baf165e8a0923293b8ed26b87167e471b169ebd269c7666e1857d1c449ab2f02a3c76670a1b2967b1f0259a8ca51c62d607165e0e136b3ec7ce9257f817f239ca945aa9e50a27e714d3e5dd61c76985b72efd59c3c692301d56b85146e3fa833a4f59859bb54d3ac5b8146355f84126c5d87452e15f7d6b06f3a0c20f16730aafc7dd6352f630a9620a5f632bd67df4518a29851bf31e2ba490c2b3f0968c12320abf26b4c207d1ef3f88289c4d3d69fa0c853715bd4dd3dfc620289cb6912c62295f0a924f7829115390a858a9239e707ab3739035b30c914eb81aae1e568c8e909f134e5a86ec3064aba0fe26fc38720fb1f3cd4bea356125bf9cda1f4726fc54291fa61ef1f83c30e1a6ed914b96dcc2765cc2e94b1f65d7b425dc0e72b678f88e1ea42be1db7d18d971e71c7253c2d39c695b622b33d793f07360725922857f9925e149c664f69fce718c23e1c61434e7ce3239430c09275e24856675966b1f4184fbf3924d1de17d14c226767dd49a36c215f54f6da119c62319e1a8c744d5ca1e4de722fc8f467324e36d174e45f841d2491eef12e16f4c1aa53ffa87194484f7e92d857d203165f0107e040b734136fca268083723e51842f028c28485f0936407a9912384ff39b3cd86078f633708df0397bbedc958a320bc8f2aaad53eacc50a849783b07eb36eeb6105083f9c995fd6fcc1fba01a2ac37399871f9ca416aa434da60f7e289363f48997d0c8f0c10932397d58397bf0a37a771817a207ffe8c129cb9d3a79270f9ec6589f6c62f0bae0c1afdcd9730cffe023e60e5e7b7cb183539a3b2454eae0873972f4317d70c1a583ef992a31690ae123998363f9a3935c297db372f03de54aee7d1c9c742ba6ea3d1c9c90439979a455abbcc153f5a0b28accb4ef063774fac852eed82dc7d106ef5d3eb00ff9f38d071bbc8f4e6a9dd3327af41afc8ee73ebe540d4eda8a9d95638f2ac569702673e821cd46839f912c26979cc149e7a3d616dac3bc197cb3dff064d3e797c1495531f75493c1b3b33149511d8393c94672faa468a38ac1491be722a1d3a34bc3e0c9e4e06d29f64a8e04832bee41ff57a4aa07bfe0f95a88d029e8052f7b9691986ace65ec829f6ae1e319f34873b8e0e7cd9888892db2b92d78b93fb5848a18fd3a2d78136361426c89e7c8826f79f278967df96b58f0d275fc5a26623969577022222bf67c58e295150ec248feee5855c1894924a6145eb93a54f083c9db1e3705254f8e9143450a5e6a5289e935240a6e482ef2713c1628b8c96da225a67f7c79821fc6c58ccb92db3e27b8e1710ed5e2728cd56982bf39ce379b63cd39c304bf54ec249c4bc8d6c743865109558ccffa7c26094e8e2a4729a305099e851ca61c5cf9d7468ee0bb472166e690b152c4085e08398e6899c2f5e58be0a73c29c7131e4470f37d184663de48d13104b743c81d875e087e5c21c79572e430dd41f0e3102ec7dbea1db440702d8450267f3167f3074eea891dc5ec286dd83ef07e26bc6358c90c750fd61c2a5a2c0d0ffcb2b90e3a8ea6fb8377e0e6aaeb491e1df8a6ea1f07570edc8eab3ca590c581f315be56ed0dfc1c269329f769481060033f3e0a59f2abb570724c4134a6ce725269e1c76130d114d159f8df79629ab01256230b3ff020f7e6138bbac4c20d19636bbab78f21b0f027d8b5759a0c96bdc2f1d86453b694ef2a5de15a8a11e71f6c851fa6ac3c6b6485133bf038b29854307115dee638248f1682670855e19bc731a46473ee38970abf5a3e920f2544a643856b1e473139fa3cfad829fcaebc21a5c542d29829bc4a2e5b29973f47560a6fc42f420ab7ad4b7286ef706a915178122b872963f7077922a2f0abb327c4431f0adfc3467b8efe79c20714be450a0bb93d5e8d9e4fb89a3656e4303a5a763ce1dc8da758673ef1309df0443458b0bc2177a4e1842fd9999673d4ea39b309b77275889af346b2144df8914afa8e3a997574c9846fffd92c5b65e5ab60c27331b32c0f3999e6124e4ae7ccad29470cb18413738c1243ae849761fad206cfd9b194f0d64cb352ead6ecc824bcf4edb61d7e49f869528cc9b28f849f72ba9c3e9258110f48b81d2654943693db8e47f89e82aa457438c20d29738e432c6af67423fc38fa2883947b349a66846b729eb2868ae93e7a11ae67d9e09f61430ab5229cea286bca5227c28b219484b31c45af20c2b394fca14b88d6371fc22d93ec3996cdaa8a21bc0ff5d01f47918b6c219c1c4426757c84f06bdc73902a1d8493d7c3741b9ded422a08ffdd53e64eed953b32108e66ef9421de2ad70908cf23654df0f30f5bfec1d7709f29b2e32d1ffde0888ba528967a7cbe3e78a9352b92d1dfb6e3831ba135dd6a5f9c670fde64694bada93175f4e098650ec5c62f36260f4e5cdd5a0ea3078f030f5ea61c6bee204436b63b7875de1f840caff0cc0e5eb40a390ec283c831ab83df1263989842072f4a4a39889123c65b3f072f7d98e39e88c90aebe5e04627390e6e4e6917f5beeecc818393c7db3f9387cfe50d4e270b6e91376ef0ede5235aa749744d1bbccb1c25a2648397c390cf18d5e39e5983e3817f320feb715d8a1a1c1b19f1acb1b672d2e0fa892509963b98074183932ae7e6317557cc193ce94e131e7d33a02165703c89aac5e8f155f964706ce63c8cc1f2e6c818dc48313c3226710d8bc10bf9d80e3eabc5360cbe44c8299face4283bc0e085c9e123491e31a45ff03fec0e2486da94587bc195902991a84f92d3053f4afa63c2528ec25cf02a7d18973d795bdd2d38e9fd2b47f7214b460b5e856745084133250bfea754b9b3ad58f0b4cfa34a41bd8297572b86902243f2482bf8d27188ce548b746615bc8e9db3974905ef3a96d95c15ea564ec1cfa27541baa30b0335d6e04fce29a79c3e31c73fd5508313323b7a98bd37c7d2a7c14b52eba1e71c26b9bfa0c17bcb29575a98902c6c6a9cc10fd45c2c977c7e50c30c4eb5a492d58eb5366519dc4c1a1d7358ab99f385b9e200c7e00bae4106ef3dc8df352b1d440f1a83ff95917cb3e5387fe41083ffb16dff88ca61f0cb6d523078b162eedb58096a577dc1691993bce0c714d27f7c1c540cf9d305b727d347b221d6e0829f43b68fffac3b47c6d4d80256eda66a9fb121993a72ca9152430b4e287f0f5972eef5ecb2e07dc584a5a5160029d4c082d71e76567d566feae4158c2dd917b493a68615fc1cc40edb516d35aae0dda58739f8daa648550d2a781d9322f685cad86b4dc133f1b39f4b661f41ab21854a225cbdc4b3db3f45c761f25ca79428f81efc6f76924f179f5f64208c2e9e6a40c189317956091ead553abb45d1a0c613bc0c691f9a48ac66ab38c195892cff0c25175b2970810ab01650a3095eead452dede71b155bc50c1162fe8a2aca67d00b7c81a4cc0446d6525cd3d6ea4da530ea15e61420ebd1a4bc0c3ab3a66a4bd1a4af0a3e418d523bf6b24c1ff782e33410d2478c95cca23357bb8ce41358ee07cd011933a0acdea670d23b839cdb6f2dc45bf0b1d1f6a14c1cdad31720ec254938670c0053588e044f5285ba7ef188217a3d4f26d9ba7206b0d21789b377e7387641070950bad2f0f0f912daf48d95463f0fce7670d20b8f943baa5af508d1f78317d1c8597dc686c54c3075eb28a3c217610f56bf4c08b1dd39e03ffe0f3b13578e0d6467d798efe0ebc4ff2d7b93e7fecb8d6812f2556fea1c8f9a6a9460efc157b8f21fca4f848db410d1c78221f85b49023dd6adca069cb3ad5f07899f56ad8c0e9f4183643566ae157a7e4db964fe7a9a685174d93254b9b5938d1cc276f92ee78a3280b37326bc5dbe5dbbdf3a20b1ab1f0b5bd43f4f54fb1013460e17c14ab932f890abcf8420229f0e29c8374e0cd0a345ee10a67a4a245394b5be1c874960929fa8b4a8815a0c10a3707d6410aef1e1e73ce2afca02124e4281d55f82d39e88b1239346f4c2a9c50cb7d11613c5f5950e17cb04a1a73189e39f23885e313293964ba47e9834de19da4cb5c67bf1e79b0145e5aaa0b69aa228597f2f45cc8d1cf7ab246e1895864b5dbf049461285e7bd1d9235191de51e0323d8a20b1aa120bcbb2ea6e6ccba5c3a68a59453d88e06289c641d2264dfe827f6ac4ef35a8f9334f3ce41e5f65852746c3ce1b4af64acfc49245cccd3e8841f29a7f021f6458f5f2a3438e15ae80ed3d6e6686cc2f18f5972e4f46868c2bb0966131fb287d9c74cb895d583a68eee474303c384118307c48006263009198d93b4cf6040e312cec73fd541ce1c96a024625cc2e34443e6b633ca448e9b5425dc4822f9bd2e4c0947aa7e23c3497e406312ae6a76da761b25e175745e1725e6bca69a48f8a1e55053884820e1a58491508f5a2585f8115e24f7ff984c39d2d83ac2f7d0d73c8e36a68fc735c2cf41960d29d46784f7b92ba5c71d5c4461e6716791f1e63dc9c358af082f04092a398eee72611a89f043aa7b54f76977c88108673de6f83dc474eeef1cc2d99088507eb51ed6c410ce673b0fcb3b3afdef42e4b6a22a59ef6521de5539a8ca1e2ca5c8488f107e1c7b5699746d15680cc2f3bb10fe628809c297ae18a5a7c3e88f628158a542522542d6ace63ebe903420fc384b984e9321c337f8072f49769c2b578c4f1de407277fce2629c4e8e9238f3e34652d27ef51976a5e26d197eda5154172f0c1fb38233eaeb2ecc1b1b4f133db512fa0a107d73bb67e992a4d1eb677061a7970ad2622bbe51c68a08107278878184248be71e3d1b883375152d49e790fddb71dbc8be930b2265407ff553ec8418e2a74f0a34d26edb183e409cfc192ad16abd6146f49096b8d918a395a0ece07a13f556d44230e7ed2ec5959b4c39472c0c1ab8fb6159115491de40b2f24805b031a6ff0b2e5ec9d3199774cef063f98921c42da1c297ae6f8c2ab0d7e1ccb79ccae90d35684061bdc482a41c634668af28a69acc1cf1e39b45384d5e05ca808b229d633a6541af2940fb9cbe9a3c1cd39a38f7a449be990c6191c09e571ec2853766c9bc15b7ff140727f06b9b60c7e871c7a90c10fb2cdf585bf1c61b531705fa2219231657116eb115b44424aa798187c390ffad42a0b4356b1769e325612369f11338520f6c126248a60f03587f8ccb3e182b8c00b10f080c617dc54b38f1eede8b56337a0e1053ff9df588748a769ac9d018d2ef8bd1e7c5bce2c122b840b4eff871ea22593c61614abaea9913497d4d8b6df88f513a9a6a105377518bdcef3a65c9366c18f5268cf97333b738ea48105bf2d6a7aceafb973ecb98213ca254be890ade0a5a70eb36c39c8c6a80a6e5db8e49155f0cec1a854f0e353796c6c7aceb1e262eb042bd805d098829fcddd33cdc4b8d88ac1910253321540430aaed445394b9675952e5170c58309e91edd52b98602776b5b25325257e3a7bd91557c825f79c343a8faa8734e2f136838c1ed28933be6b553f9c409349ae06a872d1afdeb3bf630c19199edde90f925f8e9f279f4b14b3494e067ea4ce96147a6393322a09104ff25bb47c1e3be889e41829b7b735839a87d84466dd553bc624decbb3607afacb9e6e3a36104b7d3ed338fa56814c1cff1f44732398a06117c8fe9e3991ccf07991c34a03104ec3e4aa6ee89da808610bc770d5e1ec60cb1262708defc4848fe7cf141f00061ab1b95956c2dab8de84afe33c973c77624971f387296d2c5687e631e4cc3075e0ee222a5b1e84d6147a00b347ae04a38891e476a8e83845790041a3cf0d64224a4740bc933b883c2d42a3ac6cb5536694c7a69e860cbccb8cb2e29bbcce8aa8c14c22b648ec3252eb668e4c0f9d06f1d7ab686988368e0c0b9f671f1ec48534e1b1a3770fdfbc4a3d9102eff84860d1ceb9cd13e6ebeac4f2eb6bc08630b3050701816a05560462dbc943752c59c6bb2f7dc8119b4702d3349aa68698d1a555f80b13366e1a5cdd41dbc86b270227c74f5314962e1a47c9329c33278871558d4a215dbd52255ee362f390e8f36f2fb0a3fb4d0719a438e2bbcfa9446f36d8c291fd30a6f4a63ca9d31acf05346d8a8b461269255f89dee293586c7fc5155781333bf59678af4b1920a673aecd248539f6c54385d696424b8f97d4d4ee106af0991a9d9716a4ce19987ac3158cab210520a3fc8ca07673e299cdad05bb79d517877122dbb32224f8828dc1c07eb21797684c2f758631e73ea008553214a92b3530f72e8f8849f2f8f6f867464966a4ff8416ffed0622abadf9d70343dc6b2c709bfc62b4fb466e4ab78139ea598796e6e4d38e79fae836c33416b68ef50fe03137e0a2657de1ea690f5125e4a0f35f96b7dea534bb8e13dc8c16f07aa15530967d5633bda9794f03ab68fcba1949370726945cf1463dab428093f8c65f9bd031ff1c922e105f7b0963fbe20e1fd47e4d4563fc2e98e93da54d48e2b48331ce17534b39e7e723ca311be79d49683f530a964b88cf03b70e9e8e55e04d9a723781ca33314e18b847ccb895ae78c6724c2c951da76d0b1bb72cc20c2894f95b3d2d9f57bee105e0eeff5ca1355529a0ce107b7f12ab318f3854e219cba0b3174744f88b32c4dcb3be32ea55bac374d06f3c8caa4c283f03b44f1118bc8917546108e66fed01dc2a7e5c01208af62d68890feb2ab2a80f0eb62b68f273a3089fcc1f5c966b13d9ef8c1c9e01fa3f45dfab0923e38a5d1b3797ff0c18fd44335cdc27bfa7f0fde8f45cb79fcdbbf7d3df851a4bf27f67fc5c89107ef3d0e9d2b4bee8c4be3c1eb309447d25cdfc1ad30f93e779ebef1d90e07d9265b07e7435d75103ff2331ddc181ea6e093d2d46673703cba972441e33f2296831fc4b4f153afc9cddc3878722fe629f7a4dc3170f06efb93f4ff779027fa066f2d438ee4a7e330f36ef0ff7d6dc2e5c891c56df063dfc810739633d8e047464ab9af424ed98233d6e0686c984b31c786f7c0196a288b787cb2b5310dbe475f479d91ef230dd1e084b449a288f765ca9fc1cf72c93c58c80c8e7daa9ca9528644580667d663c911aadc543a32f89d345888cb6163f0358568c65c89c1f14054b335fb6c0e5918bcaeebacbee4e69a0383ebae5e31878acb53d517fc943c0a1d741879c1cdd453c9c3aa762fcb8c2e78ee1b62b5fa9f7a79580833b8e0c7305ff9349b2db82eb2c12635a5091e99a10537cc7868a5edde285f169ca9c98e3e87cb2fdb61e1b2aa78cd4e891a29ab37b96e91f20aae45becc71b48c153c559f8f3fa70b39e5ac0a4db47c4aa5c7cac7bbf95f7d67474a05cfd3d7490efb7328dd14bc34bbd9f00831a637430a9e7b581dbb07761f75a4e1408d1a4e9811052755858d51161dcf57a0e047592343d966b7cf3cc1bfd41922d3d638cc70829783ce1f623af72c95ecc0173498d104472c46df768f8a89640613fc38b630591fc7db12aa194bd02b2eb6eab2264e666653c7a8b4fe511382194af0725dfada0cd28c24f4b35951ca2f8404cf2ab8a85a08d6a8b10500dc30e308acc86c744ad6c59667a894657694a24db2314930c308cea44a9875190bb57946115ca9243ed9fb23bfed10c14f512de5b29bc71e3d660cc1b35c29b7577d9c334709c1cda1c3346bbad4586d10bc105d913768464bf00082ef71f420da27e9f3b366fca0ecb2aff77439f5d841d4af48ca9fc2337ce0c7fa383b6bf7356a7c61460ffc9aa88f1d59c87970171566f0c01b9f0fe5993eca5d31e2c18c1d3839fd85e4b4195386ac03bf2ea4106be55353c766e4c00f36513296c77bcc51b89881033f4d4ae49f1ce5067e2c19daba932c821936f02c4c6c6b8e8a149d530ba7430ef28dc84f0bacbcd2eba3a3dc453c347314a29d2db3f0e3e389d948933b458981116cc1820d5938dd414cb5a33f547446868d58381d33425c7f59f48e030bd7facffb7e62c45ce52b7cbbf2f90f993762785ce1755de8988ad8a874e903e3e10182c161c16824128642c1cd7e23006315080010541a0c45721c0792381c7e1480015d1e1428281c1010120e101210080a120a060806060008060606000006080685414276608e65301f113ffb4f02b53e38081a6d1e2e448eefe4e0fb5cc5a118bde5a418ef40a7a3ff30dfdc617d9b34ffd7f073d12f7e1bb37969b31c27467445e227f755a721f8aaf4ed82f77e9665f5bdba1d93731f72fa089045db88560ebb5199a4d834c95ed3e0c3d8e404d1a98cdaa2134f16942e68ba9fba41b2ba300222fd6a06b1c1ca6a896b6ccfdc63360f18c3951a5ac915597f4aedadcef6e00fc63a94a3a3b4606bbf1e2970253b672a21a18e88f40dbbad6a3435a3816bde8308bdcfcb169b680e54db13f5408b1acb24ca84317ee7365b9dbc93794b5579120d7a4ae5240048fbc521b26c423e8706e73d5f85f5ac1c5afa60d3a589fa8152aa14171dd0cfd013d14c91443aa1ce2f0943e1a836035a3a983c7980437517be3861a1f8f79b8c1b1f927fafd06f66e40a472275af5edc8473ad22bf37e0afda5492f7681368876d3003b81786e5f64530fec6c8e0351b39ae5ce166f2c92c2e4f8d4fd982f223b601fc623f544a3904486588ffa2f4609c33ecec7c0752a2af78c9e50cae72365db4ffd4d9d5f034c4951e28cf8a206bc3158b8034ff1d147bd080f7bb7c5215e1bf86aa9ef40ebca6dc46c27dcf35d31a2dfed9e13d1ce94590fa7dea84d510c01c78a5e7e03ea7262b79991f097b16d45de0bc047f6bf565f0d5bd5466c0150d689bd21e2d4b011cf4ce4d925ee8b33a8c9d29b4da52c74554706903e0f8fc281d6b396f1f8c7a4b0b1c7cfe80e15baf1acb1670945e9f36ab648e2ae2c6d954997c2cc80ddee4ee6cb41a4e12ede533a4097a029fa4a4ec773ec05976c55a48b8f09349043b5b4a28a671479ba5b3069e22dc94788a66345e7fb8cad240ac505a4a328a82ff007dccce8d4ab68283ef9412e3b125435a8180fa0a4a56afb162ed4be7443bd5c403962e29574c00cb41c01e849dbb75a9b68cf96f89d63dd92d6eed27aaeca953f48c9963ea2c04db3bbe3d7f183d25a25d32a480d55f84827834a980560455393d3e25adda443cc509046da84f6a044407c91208f7a3f80a52b9c0cca6f2890cdd31822bc35c105bcbce2f53bf4d765625b1393ed2d44b9268cd2bc06ea4e09b5bcd5bac084fed0034fb21abf6115ab06e62168ddbf0feb95e4f7a4ce9cde2074d78a9d21c6d57f7b5cafbd44dfc3f328e2e5b5ee76589391d0fd824bbd2c3b7d43c9e8a2ad048888d77a5745e48aa7f2d7e3a12101ba7a136ec898891e432fcdabccf66ea0f52ae4d4dcc70754345454b8a8052b684ffff4a5f26add98134ee9460064916abf75c718583de2c9ceb02e2b4e15afca12711fe70cba5fb1bc39e9e95e39764a8cbcc907b0dc5c5486c44c9ca42be1102f402ba5b95234f000fe40158a79905ffbc14f40d2525f74f40a85166555322f1f70d2f4c06fb2b339a1b2af326ab74f181a4ebcce5d6e6b32fb9051a7e9cc2341be933e18cdf05087ece950e94975163b55431635214680eb1e45e63900ed1901c956df73349cc845b2a216ab50ef40049e2a0fc6b9bb803e146d5aedb7e4fdc789983c9ed8817ce44849c0ca78be6c68b159b3b8a97609dc37edcd8158226241d48b004d8f039933a65a25fa0236cb4cd74d06c94966ecad561f4a198f8487c8518669a09c76cc6ab824819b89d942325ca44e8dd2f9795174188ccc62a4d2ea26b5656cf15d7b2eae7a0bca35a67ae7e83d26ef945f036f900b1030d8f107ec1700caa943234993983d18a22a49650d76d20897aecbc70e29c154e2578275b2af301cd9257af42bda9126b6a1b51d3cc92457abbf8a38e9985799d8b2d951ba41710f283d174e059d883163a0ba2b624bc6ed34d99ce504e31b2c173a342ffb6f102b49e09585ad229b0cb2bed38bff2f439d37a65995dbae0abbc1048961f62ee362f04b4d213c22eb7ac8b7342784be7ad77a30a41233bf7b46ecb290f83833f9bc307696ea4777580c982a941c4f715fc34589d90f834a4adbccef9fe469290344b86a7dbbea28281f0561a5fbca2408e420ea552337eac8227057a935e8a52d892e0334c9ce596c635652108f790a16cbd412e96f3a9631fa5b76cdcd1d096178a1bca5a55a454cef8f0f51949590312af52fd24695dab7da521e9811855046b01d728180095e577b1c659665fd3208243b8f42903c07959e8901640a990bb89bc9cf2b1b4b0a014182183ea944e1013da0aa376dedf002b4136c0533c1ba5ef98054da57ff75b7a8ba7f0905690241fbebabec72460bbe53b34434e4158a7a4c3bf7172ab95a54de9030704f411f2e3560e193d60d7a90b8a68fe143a8300f0acaa5600cb64c89291f98d8fee99156c19c26cb4a4c86c005c5b4c74286f9e05416ed8adc9b0443dabcd45031a54a44c719b5c5a3f1d93b47208fd96a81c9eeec29d02c5bfb7d31cc52c95b380ce2b6ba652f96cdbd52d64d609064a090f33b5bf85ac07c2019eaee010202013b6c352bb2ada2e5dbe4661697ff80a0678af24fda14cada154ad109d768a34502f1ba57b035dd4df1e3662e40725b987da53ee41685d6fde29cc4b342adc9b43efd6690cfa5860dedef1a4361a948e62504cde6d35ba2d5a49d758aa21ec1439e477e9424faf023dda2409af5aec88c2b99ef188836c7cc52ba3de648ab5b38515a91687abe5cbc924935d37f1407150cd2345a7b90f4f2e6af4e259950ee6a07676a7f2301b1c4df9ebf65ec7e7ad5133889599596d0595e355e213ea6e7bf67101aceae57912a7985c82d11e8475c5e4aac4c3f01d09cb25441a6adb32a478309590a6e8a47d3a1c16a3c61608f78e70e7b7dc2dc45af4983f1c94cccf2669dd08f98f5563319a15505053770e6f11298d706013bcf3c60e3c727feaf918fe988819a1a215ecaf25eecb1cfe29232477c21f97a611945678d33728766099f9a14ab62dd78cc888b346b0709f917598638607ca9305263612d850c308be4401402ec908f0454546af35427ec40a97580069739498b124c803d968a10f1b8a7afa46ab84ddeb2511df72a696ca1b55be0bd0ecd58ec06909711c7b5a46a28d5547ee7cf1daaffc286401a45201e09ad4cec8b8c0513b6d0f097de3e6358420431f80bba358630827fa5459c551b535731f80e59c2cde40c25e39b763f82009b4f293383d5ea81074fda72bbc32106e688a10450914041825764d7fe7f58f2e0081c24e56152e6e8e475250be06c6d984c32752011dbcc593b5fdffbfeeaf99c5022f40395dbe3bec2e11dc014c3c3a2311c13d063a2f0fbad1447b00f87c80490824da5260aa5c85ca450b745158e263cab2ce3196d4056012db99b102a3100c6a3f99e76a64bac0e628ae133b3aa59006bd2d43959391a90cdacc87ac62a02ee4288121dc11159330b63de8a333fc1d77968bb7faa4b0166a25258b4e4310767b7a0ae651928a396b91d6d7048fc6fc437a0ef7237ccd4d4e66b00dd59f29ab01b4c9373985b515a7d989d8d5c8afad9b9b72a3054e9f8e195ca45e01c234ecb5d4994779a860d15374497bdee5f84618095df991e2744ae8a492ae497b846f48d88e4b58ac7ab46dd8912832d97873e79925ef0151a2d5a415c4b0de7a664f6a342d0ee6b501e22044b6513ca105ee3ca45117bee4e33cb01d461e78ed9c4204f99127636cd27ad2995adfe6206925d63354a8ce1679490e0f7895f46a9384f28f8c4a9cea12b48b675492948cd1c96aa7a9bfd9cb71adf730b7d83b4d41c28781d44e55213cfca21db544ed4b01a96fa24c8bc80f16b34abd5dd1b6899832e75082d3261a560aef9aee4651f5a150498e30e4fa30acb318fe21e917892ff3621c4646ad88b2cfa491a410c4a19df7c6091657125e3f49297b7a29809c2f7812f1e64a04c27dc801b6248c043de9bd5094749de19cd6007d97af0837aa29414e5148890be1f44c0550d3c8259770c19462f8b426bc3daa66ec46d72955555e0daf4245bc289eb0c5213e4353d5014c141575761f954003e3d0381c8e60a9a3fbe47607feab92f4dc8c66a6edad89ef52645c7d3f5570a112f49ec6a26e92761f6242c5ff7634567ca4fc4754e64e5dc591501639fd3c03633e8a2c63ede1316ff31e4988257ef77ffbcdf7a1690f58f47c9c42d73bbde0e289a73074cea6149252022552b3fdb1f9229034ea5747a56744e6707b8d03ec9245596b9a45173a2e48c1e24a0e1c0a6c41201584a458099c7f64a85d894fd7c98d51e352810e725f2a163144a8d3750888740a5e9439186063ba8a88209cd34aa2f4e11e91e0eb79f4ddba3e334eb5128b6cc825536940f9114ab0de49509212767c01867d7b918bd325894adfa7fab8669db630a03f7f3ea91182dc083cee7c83b1e644a082ebb1ad0b8ce949f2a433e7986f08c20120b1651fdc53667b0d20fb9a07bd532eae5700d443a401ba89f34502ed9f5477b62a52c0c666ae6daa0a4c659fdca3d28aa184d1994b09d9ed5661c919d488dd708f5cb11803e6132da66803d121633adb0b90039cf3170e0b37e65862bfca93611503c61074734b1535dabe239326746a2902cd32a92a0ad1aa9dab303e5c74bb4b2713989cee1cdaca26c402c1c868eaa0249d7ad4af0d6795e3d3680d09b79dfae20410ca217cda3c1760ff686ac454c08548961c5996541617154f0b87333c72a254de1e097bb386516a314600e041c444a7699478e5b34b5385e3f226ae00461af8b234efbf4b22d1e89710095321397a22a98179e92936eb336ba64d1bd35f7f30d84d07f344595035b30978e9b6c9311cd1f7723ed5346591f1188be49540c62932a08be7228441999a9d7a9075efd56ce0034a8c000928ca8416f91dec4a7433f24562e75db5632be999334f2a6726b557845a773768d8c9b8495dcdd4e474c6913676198968f139478a3f7e4da5a1821e8d25c52de7dfb4358ab744c8e44ed8a2e70a3a749341c53b230937dfde8cade31f4db385b80239c1e65f29b74681c5d8f5e876f113aac87ddaeaf0da31c1ef83695c3253864d4d8329a37741aba91c3e8a664a83f0b7eaf34b87b879b1757469a62410d51059f44711276347a1c5a3c7c7678d2f3a75368ba8c0174a1f834c1dce04c7c4fb5455f438e008cc656046e1ce7ff5c44b86bb6ac6fb3438021ecfa34b8e70ae7718788987121100e52f5b6300cba7ae0f7e0aa7d9326479bd643658ffd181b0c1f01042f5029cdc15d051113fd001a253c10c78b3f10357907dc7407ce136c8ce71052a0370a826f2a24443da59a7a38c2807410f999029f05be30586edd3b7c76dcdb8cf23e5c88df94e1ef26c93f972c6cc04283f90b5cc8f3afe258b0f4f9c19d1791ae707eaf9df318fdbf526fa68cdb9756d9f0ff85c2b923ef549a5ec073d4ad22f2904404ec439f7fb5898e27ac347a2b0b1a1500eb7f6f5635118d18852ac24ebeb50868c9efa0ae0cc5d007a401778f2e11cc6877d98036bf02fec3dfaeb801338993300e53cdb670634730fc83c9e28b1e80f620745123475ea5a4252ff5d807aa8542fb0c6f03bdb04e8525d45e9e91a3ec93c55016a322d8a7f02e8aad2b2be3d708ddd5d779dd63b89385111d5d8367e74d0f44be72d9280c1cc985debc2eced4503d3f7878fcc9b1bd1904df9d9f7eea4bd11db68b4f84df69ec13e23c66896b206517b34ebb6a6faeb6aba287327fb50c167388ca8f4cbb17044f47e952efcd10cb1e9d56c017fc51dc959d88c5bd7fdaf374f10fc9dfa2b812aa11ff6a9280e32e91b4a558c522727a8faee1a7ed56d7f00d2debcbbd3d9f15524c4491ce740c818957da3f17700e6f4d2deb0a37628284503368c8ad4b4a753eae0a4d637eb130700519400d800ea3c0c686825aef012d8b8fd9c8ed0b896918621602d239a05e24b81cbf602b9d0fd0b50000d3c38c7af7b035c6b9d8b56f782f5957386eb766d713d478c5f23e3e2507444eb57a78817369d02eed898104002d7ab2bf811f05f0bf1ada8770030925b691310db3640f63a41718a91e4b5d5a811222daf022e668e0cf15b5f4c1f40f4a14d36c3c7a7cc897444652ea15844500d1bda8c6fdb8c9946f094d1a96906f6970711a21cd58598c5d391bbfa0da0d26207503a85844ef6c57bf8683cc2400b9cb0df71e41fcd2ee2263fe17adaa5ef808f106af4cbb2a01c25b09da9e166c64c1749a1a04edecb6ee4285d50607d1a546040e8f2671d9a63500dc6a8577bdae4e8dbb37371dd292589c2163477b47401eacbb169348e6f2f1d141bc0ca8d7d2aa6246e56a4949c6e45f2ea7a064eb745193fd209a9c2fa0f38a01437e719a862ad0dcf6e347ad359e0685f6c6f138a069fd5dcc685f5d50031d0f9607636e61910b61e9e17ddc846dcc89508ad55a9045e1b13b70a17097c4022216d2102e80c3c17b6772cdd19298762b73ebbade239b2146a8db532eaf6d378ff39acc89550a640a46bf1fc8d04f7105280a6858c3e9100aa50b2f32b205fda164ed2faa9613b8b34b6ac1f1fb26e684c67f17ab955bc686244bd6d2e4124e669db06470e9c2f1a9804ce35a3eb54d1b3137bad0f8cb6d1140f8c235c56955be911004ca25d810472a90df4b6d1d0cc026440b77f83d2da8f0a17e009b6286dfb5d3f32e22bbce92081572c018b5839740794c965b8586434577dd6b4ddb3b36c6a77ef30745dbad89608b3ffd70f06d442ec0da09164087dba36f038c2820d3e6088079a6821e09bbea05c14801a2e787160dd9db28b0e2414990c6affd7ad95ee64fda22f1e1c8d6134e2054b4a7956ac8d46f5bf2813e45ecba3690cc10991684333e92900dd4fc9aa2980a385e63174ff12f998cdb4b39dd3cd4a9aa56d61e93c5113eb3473ec4d9a4c3b55d865d770c1dc69a5b6a8810e91c955d32f3208a86098500cbeb1628c9a93aa33a9b4eb22af1f7e33e77822d73b0fc3059bf9ba5f0f0726fdc4f4b639dd30a748eb8ea9e93e55cc35969d6d4b6c70345c5d16588c8233799272522d2725a20f106f60950a3eac8fc60dacbbf0e0e270a38f44f1820697be78ff8630b20a7386fb94a5ea3c5d11a0cdb9b79a743bf6f7f7de689ce18a6eabc4cfdbc4062011cb263a198ad9f06dc8364a2997469db87f15c8814167893b3c7b649e7c27ec59825a18a57a6c71852b23968563e12e22de09ac620a5db2036cb0f2706a4b579c523cf402a5b41fba1bb72953e281c457604453293ea7001e1b9147763d2a8ec587c2563f7ffb65c8f217c7395b56d648a779bbd6170c0e9b99337bbecd9ac8a82ec1b880eb4b33a5ee01484550b3a2456a02715a5b77c2c3207c6c158a69c578a549a82c495e5aabd192f10566b7a9c85941d39c1c580a6883081eb744b320304d88213f5fe64b0012f0e665dd7d62d9be9c345334a2fa71305ce4e2964071a768b7506f17b670a94ece1ecdf2e17691168d6c3ac277e2347525738196b25356f602a42400e3682b33f497062515a4bf4fed6db51d3933d5f4643451866922918cb67fef684d5022948a3515f854ae1d8e0530e84e7f274041d60005b25faa0f7b9e6137c68ac301c8dd56771048f7a4ab331d60a333053a139c4e21eb48a94dbd80872461441c324a515d0e0fa6c52e5d70e7d7e1ff1bbf41ab3d5b778e0e8c355e323b6fcec0eef53063b366676e23a35971d3510999cd83d27721f01e6ad1bb7b26d37ab5f8826089200e8fbb587294f18deb220b75a1b437dc747e26912ec11934b95e1551ff8b22e060eac8c09ae15d0b4b6be9afc75dda0deaae5e190305338f758410ce49ad9b9b75787b61a42e5dc2cb6a147a351df04e35d38c992bf985dc1cb8e87808ad4e54012de7ba0d2a593f142807ca71dbd6c5706194d3be79c0c6c11e99a2190391dd1970c88e76ed7dba93a5913e8bee33e0b6b32af9ba6f62399a6649ab915e1d6381083dea087d142d8171496ca623808337a0e0ebde307554e19b96ec224d0e9fdbc74834bd0a4a5f8dd8d93582a7634660a9a11252200046da4066c5bd4298b9669ee185d029c790179a43fc08b2ee170beecb6d3f57b19ef4e9e14720867500f83ece013679e5bc5ed613b8cbbbc4d5c9664aa790984b227b2d8df330200f0cc0a16c167a0225d6e1c1bbf3623e19077a6fb8fd20941a0b9309196968477e8c9a12e6cdf7c8ab8498509b2e7baab63fa76c25c4a5cd06b5069e952157203c83654f51f5a2b2ad1a434ed5ba2842e875d78d06dc8f595d91cb28b81a0bdff4da939462c9e045f8e914be8d359d3eed869d42da1694bc64ca56acf6000486c8e844982ca2f8ef43139f6e93d76016772d40177d16d7c457651ae4ae45dd2cd16a30f8b8f2dbf30e942c9011d8a2ae5d2a2dcf237e24e84445b37dd8435f4eeca89af80e7f6c0386824180445586f6ec821d866ac93da463624e075f1772cec40b134eb44786f78a96fff28e09468c81594d4c71260676f3110589e879aa0731a888f3b14428a4d5422d63f4076c15a77c2862b420ab1fae43b6e5f0c2f831379bf3af7fe9748aac1842d262487139e3550d54e1801c63445ce6602a56b4998b828a0c49a5485a24d2fe624e86b88d66051cc1a01b960f1cbdbe3cfdb50290400132a07819de3ae92c500584fa09beff22efd3cc9f6d7db49e5516f0d70b15b4168441806c818284fa3b031d5c5251a70e21e28e4a6ab554bed883d617c2418cab0310a7e6aabe1ff60e9ef71c54cf7c51e99d4ebdd9b48744321d5d1bbaa2ed6e2f6de673a1d97bec65d8abc57b7a749727fb5fa28cdf46bc475dd2fcb79e907491f6b935b90eeae631bb3f9576722ca14b48b7be2ee3aeb1d7bed76e5e5ddd6fba1f5dde7441754de9fa55bacb8751de8913bab1ba82bb94d37b5e0c1d5cc7baf6d3ede5bc2a0b435a4e2bf2eeb0372daf015d36b5bbfc43963ac8d635dcade75d23bacbdf7895d9ab6e48dd1ababf5c776b650691c7e9c2d415ac4b52d74e177a5d64dd8abaca5d8d783dea8ab3dd42e87011d2eeddf4d6f476e9f5f0f2d405ed9aeaf5ddbde744cfe0f9583720d17dada1678d5974ebe93ed4fdd7fdd55d10bbaff990675408ede6f4727a7d7a817a35ebadfbeaafe2a66b9be1facc689bf8c930abca77684f8956c0b8e719c67893cbcf07acd96a53f5706355fe36225188880a23fa090a0983b42efd80622050158e645920a3a3e79efacc0535e24664cb277d86a2b1fef7660e7f141ad76eb950045eb3a80522ad1dc22b8ab5b73c6c58ac4cbf9b9b927bb5045957a867ff01ed2017181711feca5a8f8696be0879c9662ba0fd6f2b3215cbedb600ebce62638facd876494f9e84ed5107e8e6d857f580f4dda4797c5757f5cc8de220280f2e4dda454c90ad1cb649e5d9b0d7d09af97b2e89b99a867dbb64a904208b51fca950279fd0046d09cd38f5ee40d794bdca940b38025a5181532d6c071840baabd90215e000569667e851533f098d91226053424ecc8565812c46ba5997b570300abb2970f659d5a6d528fa2578736588e78d00ed9a2555573bf7ac19bae4736776cebb85ef1df6f67cccd87173c51671ffeb0b2a98163707103cb76c5f70a4a6f42e04897f5d591c2bfc5ede80097d1e43b30ee56f59ff5a4fcab15fdd6cd74f8927b0eea5398ef55fe7d3f44d6bd641fff3c20beecee4ec0903f129e187add46db60bbd1da525e3033eec967aabcf936b6c2e49fa8003dd1574f501219874cedb2632f573b05c430c4b21e2471ff5086a4e74c6934d3f9b8368e66a687fb288eca34891b8448e8e2cc1b5c8e438694800864e62d8bb3d1cdd1b9f2ee83b8a2798a4a75dae39b5d0fdb0f59b374aa7e72ff32137519b284623602b02a7755a09f82ee10b290d1be4ad3d814fa6db0ccc198eda902a1181439a20f48686f5f016d9334c4c375937569b7ffd5523c106436c2fd8c75bb943e57a0c54429001ab84a78230814c789c1732e94046e8c4c017a30399598114fa5581e2126808ea8e405601b80c6418e6bff97423c83457052f37c1effc7dacfedd74108147102fd5fa380bf3ed070668e82ba913204d78e6272d5ffab557d7347489406e20b7e00a411a0532ed422048bfc0af00a0b1a80be0815509bd41ba5ee0270aa55ec546810cbf0722e39a7fd5ffc005aea5af11c90288bc48f03209ebf526023c0f64b247505134a600e5d7a8e7194f18a3d485c363d1b58e491b807a3bfc3748fc143b25120b08c5999247fcef837a74fc149423b1c2485860e00632697ac8ab9eb3c1aa39f64b0b029001c102d902b6026481a4106432498381eb0f049169b06924441db2e7ec6b6ac12881c31a647cae7bb34108826c1f85c3707edc54d8f600ad48d08d505c0632f9a6f40035952023490215d22cd0eb60a01e373c18580c2ebb02610f4d0913f8410135124ab856bbd78c8f1a972a8155098d446776e00d44a7a92750511cb095f42fd205d9ab573352e50bd60dac46cdfbf659e1d2c7807d94fb0ef2360932c11d28838bc08f1235000187ab2ba806eaadba654f65ef868daa2029c195d1347b68a1c87aeaad0b028ab53b972f4074904207f003fc003fc00f20db0889adb5de499429c9f2a95e21e722534a29a59412c3d7751fce38df70a4d134079709030935092849abb65d984491a3fee424a6e2a73fd8564f1746d50dbd3b9fb4051d2acb8569cee2bca8a43f8ee75de8c00597d4c6926c49cadda2f0b4af6ab999afc3166693740853f7512dcc277ff20875afca26b409c0600436669420081f1011392cf01a20e03043072dba55adbba8662db216c654740a22f24255981db3308aee1953e2d9202965094420821a23344af0e8a4ea90c58c1a353c7410e2e181808e5898836753caf4336ad4400974c0c268723ed9548b5f613a59a7962b2a115718bba40e2f4abea9e9a81506b93f71fb16962459b3c268b296ebe8f419f1d32acc419930419892df249d9781d44e154e03e407346e38a02315954e6a54ad96f11e21353c6c9c19666a830e54e8f2d9a6b16919e3a5599f3f5e44ce969119a9e314a652f9920a6d4ba2b63264809c8083c314a610a679b2c80f35cbf9606b187494c220bedddc454cd0771529cc7ae55f41f4543d3474002272a330a6dc29b92d1fd5ea1685d94de9c99f243b140695a49473d4149d3a897fb0257a07288c2e6245895ad3232ffa602b9f30594a1f269dd88e12c4f484a99389df9315d64f8c3ed8d63a612eeba4dd7992d6a5241f6c8513a6b53bef52ea3927c71f6c3a3661ca3b2a558a73fdea8cd584c92efe255325ac8e124a5948d09109f3ff870902e19d0688475a0a3a30619244d733bdbea47b4c973069359ddb36276809e21f7458c224b99f08ed163b23229530a7493a8a50514c5aac3ed8da2861ba50a92f9ea75caaf5c1662be89884d154c7bc18714ad8820e49984e662f947685f3794522615272ca387d79623d27618384593fdd95595ee41e61d0caf124b784913dd20fb6e208636f9a6625a1a4dc973fd8ce4464d89801f27b35403c6c887434c29cbda2ab6ea92ff7920e4698edaea477d30f223b7e4de85884a9c4f1f867e1977c4c14613ad1392de82ceae78e44986cc484e9a65a07718208533a5954f81cb7435cf77176b9adab62a525cf8ea13f091ac27c27ae5fe5523a9e2d84693e9c924d90760721cc5616b4bd9f18b24feb1884c184ba98af5d259bccd621089312ba3a84b0a04018554b892245ff75923f40184b527be6268a8fdd3312aa20082724e4e81f0ab36929c7cd9c5bae5c8c396b3bfc6012dd52a85a1377f4c1e4256a46f498d09d8422aa821a78c4a0830f26f13e42cdc9792fe5eb83bb6f64c8888d8e3d98b34bc9b65e930439793d98af32cb3a85f3141ec22c74e421f5c935b4db1380c1086ac08083e30735424254d0810783ca89a73b18b6949ae06f723c39be7630db6a9f60a5e44fcb50471dd00c2b4bd72a97b36c2ce4f88f2a41caec4e07f3dde75d0e6929f44c47c4d1eb113ae6605e7d0df5bfe35e6b92834909ea5f64b8dd5f34fde360f44ee2cd3e880d07e3096af4fb83e90dc650928e39a69d6e2fd5e106d37d3c21f465bb0de64bef954ac5b4adc36c30fe8d09195f4b0d5f91d0b10663854b265a54e6684b01e149c0c10184ffe85083f9d6f32fc813e75d4f3ad2601a17bdcc4b9ec3554783712c8993cb44cb9a1b9dc1d42d26754bbcd196630683ca9757fff94cee313bca604a2f222da7920e3298540e65c9dc741a135f0d19cce81883297fe7fc8d0e3118467aa83269fd92d8274e4247180c3aad5ce5e9f99ca7c4c376091dde9c86cee9c4afbf600af1966eeef582f9c58210e7e9a4527717ae7ab313b7994dad98756fd17392439570c1a44dc53cb1e537efa28e2d984d7afb8fa6e374bdd81d5a30ed7d527ae6dd3fef25c4fd7cc8143ab280a990915dead21d583049a635cf4787c99620918e2b98326d2d3ea592d74e4ac60b38389a091d5630763895b3b37c324c4a47158c1754c5d7621d543029c9566684c7765653c714f28ef3ba9495d7935f171f1374523ba460cab1f4e7a81b2a5ce88882c953b0f6b8b5143aa060b279cf13f3781e9c196a19e8788271942499605234f931718269b404dd1e4c124ac95213cc3177fceeb3f466e799604e41899730af6309e6a09e942cda2525983a9cb8f87e9a76720a868e249884db074f7e695b4307124c723577e4ee925462551b388e619254924cad9cfc388c61b439658276ecb929f972068e6298b4f4092bb292fbe89318a6f333311fbd3bfe05710cc334b7a2ba392ac9deaa55814318a693175efc4d46048e60184c67c9057d9ae3b10103d31d3c484be62f4ce2e2d45ce9926f2ff4456ec9526d56854b8b7535f2b282f7961ca6ed854910f9568fa6b2534962c3c6c13b0e5e98cf3e872d41d8c6077b17297da5dd84fd521766f99225d989df0825cec59ae95ad1dbce62c75b5792df4f52427333345c98bce5c63ea5a758da988170057de6020e8e5b984ee85293db45877f12c400872d4c7262e1653f6ab8273fd86c80848c88c858ae85b18332d554911686cff6ea9372b8291316c72c4c26e7e027a87d979c9418b42cccd9a4a44b905ac99249e2e00003472c4c9296539694c8115a270fffdc12b4b980031606a54774897657c2c0f10a634813b30f0e5798cbedb35af34c5a61bbdaca96b6654b75cf3ccdeece25439f943870b0c23c6641fbd4662e5c3ee158857176f4a792649978b104324315264d0f4ad250f231482e70a4c2b4ed39c4ce84bac0810aa307cf3ed87c00e21882d2672f23b951c34b1a9ec224dc67c9ab25c94a6599c2ec1dbef2f7e875f1bc14c6b724e50b7d5143059b145bae2eabb4bb751d9e3da51c1e85d1475b4e59beb38e2a0a8385eb9653ff3842612e25fe277f89bb711d14a6ca72594cbcb44bd18280e313be8f4eda4c505d042e9880274c27b7569cf8160c383a61545d51d144ab50ed45e0820924270cfa4d7bd28fe979d2de84f16af446b343cb9e18248980b489c80019b9a109639d0927c73b7b763765c22cfb6796e34149e0c084a95285ef94175fc2f4a6cade74ee78be74b1c16109c3e79874262c8921c408a4122629dbaf99f820b231250c26464428256f1abf4dc2b4b26179b115d44d2a09d348936276e364ad9f6bc9038e48987a2c6c54ec29c9b40477c00109c359b0923fc1843c1765d4a891c8e07884415e520aa9e15102b6906f157038c224267928f996653fe97cb089fc071a39333c6680848c74c88848238c96529ef49dd6f296bc0c3818611e7195f7d361fa10bc8d20c0c0071c8b30474fea55d7ab83072545184b94a45b4f8e2311a68c7867a87aa8572f5372124ed7111019365ec08108639e24fa872d9d43986a4457f024491bc29c946423f3528927d7c85108e37bed5a901bba73bf1c84307a0c7593d5ef20cce339f24e0ce5f971148449a92027ad053d9167b22aa881c7e1088479d4847eccabd97ad00d191f62839b0310a6d8d9d3e7fc273d6a7d8c1c16e4c7c8b1914118b9e4f80356b1ec6a634cbd5bb37cac73107e69a2fe317258c0c1f13132c2c1a17e305f366972beae9bc5f203ac0a93f6492ac928413ebfa930bd68115969c162afa830d8a7480b699652509ec2a0e24f990a52f468690a83dcc53b154f5c44a530e5ae20eaba39290c361f74bcaaae52988cc21cd6a669aa4b4461f435dd925e234a6a4928cc967368adb1d4410a0a63c5d2f1e3efafcaf409835dda2755923c6192c4c4ce897fe9a64e182f48d1b024cf0983c79ccfeb1f59d9df84b9a468116abce25aaf09d3499fb9519e4c18f6b26e9c7c4225318409839e494ae91111a7722e61b293827a7ac5e7e558c2e4392a8ad2719ea25c09838a0aafcb2f254c6fb735df97c4f1741226f5f9fbbe939230a53f490811ad2261bedd5d4be13a90308993d352546d8ab6ce23cc335b7ada29b47a92234caf7b4932370b96fd469894f849a8934c18614e25c9b13bd48a5ece224c175774db3d5bd88b220c625e45ceec449872927e6f225dc562441877f4ca2cff9e68da4318845736c9f7358441e4986c117ee9e52c84494a55c1e5469f1426218c6d3909ba94386f826c10a67c26eaa912cca365451026d94356c6c711e656098439fb870061b6bffe2cffa46256fe80074f1955dd0fe6fa792b15b7b5d3ee83c92e8e0e9bd712223e984e3a0be1fe274c76f760d029087562c57ac57a305f8b1e1d6b1ecc2e7ad4f9a5783099ec15bff6bb8339c7495a449576305556f2ce72296bb93a183fc95972796c74481042c8f3cf5acfc1f84969a8d5fa3442d57230c9a8feea8dd697521ccc696d9f544576551c0e462f9d4bd408bdc130ef234225533d4ae406937dbc29b96b0f0fb5c17019fa3f8657149d840d2651c7732baf3598048f3e7b6aab4e76d4605092da2739c53b7785d3609273eace1f677f5e643498a4554ad94f9413d9f1198c9fcc44d39db1190ca7f38c302fd9affc32984ed465a98eb7e1276430783c75d24236bb4dc6603a7949145d256230e5d35af154faf76c87c1204b67f3e3c67ad082c15c2697d2299a4c4ba25f30765730c94b5e30ca999e57f4e4ffc92e18ed635fb7ecc5ff920b66bf4ae2762ab7600e7d1d4a27cfa99d168ca2738e995fa7f27559308a2ad9e7795b2c9884efe027bbed15cc2ba78458e8c5d69315cc41df8ba59baa6052ded9d3b253d03351c19caaa37988eea8ea4fc1d427e90bbd242779232918ece4caae2ec9841645c1dc37faf7f6de2a28818271ee5a4d499a6ae9f20483bafaebce75ae753bc19c3c44aa5eba09a6607f1f3d9f5bd033138c6a32c593bb4b30d78f4a09a61b694a2979b424985c2d0927f2c63d8a86048392d23e8eeef079bd631877cf4ae82842c6620ca3afa969db8a613e41cd7897f061b4a6c43088948f32ae0dc36c5eda73ca63e8662a0c734e1f2b6997eb9e4a83612a532a0973a6835e1230cc298791a7f3bf308d6c8c7a2f6194ecfb829d7dcffff47b613ae195f37249d0d6f3c218ab5aa12e8db9eabb30fdffeb29f9dd924e7461d86ded24654f2ecc23f37bdbcbc4380f2e4c2a9e3b4e7dd2fa975b18fc527c7f4ecac2a5d8c264e94185c9762d4c6a61f2c930a13e575a18d552f6f924ad5baeb33096bc54a7722a0b9370e287fd598a99672c8cff7ad2c68dde37818559c5da4d5d2e5f613893f2f44f922b18b968af1ea65698479aa5ed9c7e84fbac3097a68a7dc719b39c5518ce7e2df4e4ad68a20a93a47167b25352618e514f4a33541843c9d36bda97baa44f915222fc047db229cc39a6e9f025a514e6901e3474b2f029aea4305f92426d864661364918dd1f11da9e8ac2dca33b4ee8bc1439148651da4de78a7cba101486731925cf7d8a957cc22016432825c7d013a68bdbc104a5c24e184fce29e72f53c1c7424e98e3bd5cbabf8739a54d98d5e4fd88993ea9e49a30c82bdd75c29e09839fbd27956f553909268c71a5f774782e618a919febbea48f53b184397e99c99dfaab84f9bfe4fcaf4b09f39e64e296ba9c525027618e3f5acdd3f98f1825615215524f4c353bc945c298f2a9c4c95d694d3b481854904f9d5a2b9cccee1126d5f93d7ad6aaef758e30e82c3541da9b6c5e5d234cf9f49274ae7e57d531c2e426a7ff1599351b5a84b9b2945cea278a307c8ad7e9de245549893056aa4ef52375748a0883c9a57b74c58a501ec2a0a4943f076d6782aa18c29c93bd5ab6ba10a6de17b724ec57384b7ff1c38330d7985c2e72bfab654198f2a4104a4e6a5a9403613e91f1e649de2f95058429f8772595a42f53d23f982d4992a0e412f4c48ffac1bcf9bb21d45c929df6c124d42939db9412cc523e184f8af638a54a1247b907a3a9f423dea2be45d3f460ce4fb597d3cc83d96d47b6bec9fb56e2c16079754dafab787f7607934ecfad346d3da9991d0c3bea92f4252c8a685607835a4bb24952d492974407c3bb9fa0533acdc1242e49a1bfe29efe921ccc9adb41e9949224d57130557b9f54c2c16469fed2c28434417f83e164f9fc2eba1bcc7e552a4ec5d0d1416d30578d8e2174101b8c57733d622d073fd11a8c9f4b2853f293ba6851832997905d49fec84a953498b37db455caa2c19c4cea6798588207f70c86ef4a65a7543318e4fa49eb6b49977019cce9aa372cc5899592c1fcd94c6754f2b4dc8dc1a0c45f0c79366bb75589c1247f0c154d8979f5255961307dca5652f894625fcad7402f984281c1b836d23a97dcbf605dc6bbbd785ac8c5eaf86c51f289ac4982ffc1565e30499d8332fb924baeb5aa0be6926925593e8f6122545c48aaa7aff821ac2d187dc6e74eb9a985bdb2775da6c6abd7ebc5d3f569a9da56160c2346794ef9ce55410d3c6a5058306a48efe05a5fc154826acddeee18ff90154c67294ec6e560150cead57639c2945052904851c114dac376baf2d3693b3505c35eae79de393fd8a4600e1b65926c4af688b61f6c782021231905c3b5883a412e37c6836c302828982bcd9224c90a5a6e4e912718f54a3a4ff2f555e96bd47082b93dc6a68d58dca86e06d504c3253968cb6ef557ca144131c19cf27a4b92a4be874e9ba09690a4c52a2c2518c3ed7c734dbe888e6f25c1dce7a6e494d57bb70b09c66cebb174c24d10fa3e3b86495909fd1e5c94bc26c518265982bef0dd9e2d86717b33440cd3a9ecbadb6d7ec8304cc2e30459329e1fc46cc230ca49f15db9aea4124f1f6c3ed0609843094af283add3c88d326098e4fb13c424fffc5c930fb682444066e42f8ca162414bd6764b612e41872f4c62e1b405ab245b762fcc76268a67bf796198bdb45f8613afe3a81ecf56124901e2217d1684ec5bd73ed8fcef5806780793a4bc4a76bcdbb8bc201a37928709d00ee6b0aff77bb2223550d7c1b0196332342f1b403a982dcc7f4e594bbe2e3938076349fa4e89e19e1fb3837230d7971c95837e1de360f0a482d041ac2465c3c124a527a9f49c341584928d1a6f307792f44dc5995cd51c037483418f29a57298c90949aa65886d305b3ca9233f55de6ba0e7410d1fd0280107870818816c30aa992c39467e7efa6b0d66d39ef4a8c5a806b34793b55cb2c8fe5b300d064fdafddf4af2d1602a9db486e797eeec9c3318deabeae7e33efea56630088f23f65bff83924f1a60194cdadc73d6de3edd514206939ce3def3d7640ca69cf3c8d28ffd95223198fc3b89bde25fa353270c0659420a06a3b598b0a1bfc42f98dd84aa28fb9f7126e805c3c45ec3c552b06daf14b4546fb6dc74494bd205f3dcbbe9d91dd11d6711b151c3a3900b66ad68d5237fadde1d8d238305885b3076f05379f1839ed3e1c7a1164c999697fb3e6300b39055d66856cc5bbaac7026a79cdf4b59c482c9d34e094ab89c2b18be6409bae72dade2bf080216a040042e98809a26d00a7cb977785d6d886c85d1258daead94c42a2c77a544f9091da9603cf9ea94ec789eaf94e0140c4add892daae45c4b270d11d0484f402998c410b95ee27a2b58ea83cd460d8f432460140c3aacadb3cefbc14603a1602aebf58a71293f07133ec1d8714d977f3d22726494ed009d60fc142cdef67e12bd0f0ac22698ca2413cd32edb4ad432f036482410997bd8ff9f62f7a1f888c849c11912598367dafa42474b76cab1aa804a3f89f244c8c8b93ea43a3c68c1935108660124cea5795df7f1dd038211e6a238804e3a86579f224aaaeab814064bc40051c1cf908448688d9cb4812101a770c63a95c6277d06fd501a146e2c103746424e5658c243744560535f0a8c0196359ed12844a7e128262182fdd9e891a9d2742ec32561084238651de2c98ba4f0dc33c6362c9134bc7eca84e1826614de5557ad4b5985f304c96f2626aa2c5c1c1c131850386490e7d27c99b17edabe417c6fc53dbf9a2993b8dbb3ab116eff255d3f13b7e9e9ccdbb5e1876adde4b12345e183cfdbbdededc85712d49e275094a5d1894d29f4d10a57d827a2f1706d5d034b58f7f4a3e8b0b732c3ff1e27b720b83e7ac7897bee6399dd8c2942eeff5d80515cb502d38d178f9acbaa07a5bb93c7a4f0be3e7f50adb4fa5fe4274b859983edb28397d9770b230f889a6ae73f0b8658a85d1cf47ffc84fbb9d5f5898ce4407b7bbec15461bebca15a6f30da9a2f7d30ac42e2cca8ce5504b277a95b4b2fbb7362b0cb282ca13254fee746815d65636bb9b4ba3f19d756fe1ec842c13cfa20a838c097f429c1c97479c0ae3792a49e7d36e264e438559dcae4b924c7a8a4259123f5318ecb7cc47c7afc4b74b61ce65ee152f67cb49795214b5bb3aee2bd7a76dad658892b398d5a33075da553979432913af1385295dd8863eb19d47edd9830b85c153570afaea2bea754440e1ba5b8a5a49ad4f982fe9bf699fd21f4dda13065b3dbd2b4149272849d7097357b8a09fa4ead239bf5e384e18d46bc7b84ba10f3619c7ecdce881b94d98d2b7a5aa9cdb7388f35d5034613aadfde741492f13c6aaffa417ec04214a4988e1e038210f72983098a5d56c0f51f273b62f61f4b10f4afea44a091a42219c258c9f4227259c9c4feeb815e12a61f8b060a9c3dc892fdaad0a6ae06183a384a9848659c951547616651c25dc24cc2f4ac57512164a9c931cc249c26c52c5933f6ab288ae46b848942b5cca974ebcc141c2a04baf07a5fd5dcdef7b8441eb99de7aadf4f88f239284d59ff8a88db811e5fa12b9ee107b7b59cd3ce11a77a58212de3946982ddde52aed27d494d8224c9e54f80a72564ded451179892faac43bf3126152feaf972daf890afa21c2f425871357f62fa9b97708c355db7fceb69e214cdb77ba5784acfcd10049f60a61ec38d144b124e57ac9470873e6b789bf3519fbfe06612e3be526990ef5d3f69f208c26331fd5e402618e3ab25f8210f5b59d0e0e10a9a552b1512bfb0aed4e69f13c07d3ae87bc0e745043c62d10ee0f6ec8b9bff6d12b91c1f9c1fce147e4ca8c2cf1751f148bb714626e25df16d54a506a3c4e759658f1f8603c133de588aab894f3116e0fa63ad94ecb98d827057170e8800648c88888c78dd343fa29492d0b7342270f57b2bf92d5925f94c6e121edb157d24299279d34031b77874cc35a5ebbb3dae325b19e46bff4ea23221e36446670eee0ec603ab7ec7152086509ae0ee6ea4f82b613db5309277430aa7b8e65793ee751627330b7c94a9e2773474c2707f3f625f18adac5c1249ed2d5c1c1e849e9e93149c91b4c49861026ba17dd3de706838652fa96a349137f5d1bcef8545bb939ab4b66d244ad6851d7e4d860165929cec7d21a8c76764950693f4689abc1a4be453c76c54e17940663ef2549c9374283a99327c9c4efcae549ce603a25bdc74fd946891e3318e647c7d22e6530ec08919e7193c1d41a97b28dc1fc39aec991264a3ea518ccd7e361e6bc8292c63018cdb4fcdb6779972e81c1204dbb2f1844ebe44ef67bc1b027ac855c3bbfbb0b26a5d484e90b71c11cd482103b275b309d64668210134f44470b06b9962a3c56b260d0f9d35b5d8d05a3e737256cc65730fa5bba7072ba24955ac124447a6ead4fd258f2ab60ec92fa79a194b426f6543048b91735da340583b070c19485f2517a2918f6635ce7fb5130eaa8d86f224ab47728983ba885dc18f917fd09269d24bd52b932490e39c1e8490aed67254d304965951e6b4cae799860104ab4a52429f7a02f4b30a7a6aaf6f77c4a3089b85df83709a696d71a1d91f2be1e124c49990996963c865174cc536951725bd08d61b494214edbb675febc18c613f6c4b3b29c180635af1794cacfdaf961182f9a7af219cf345d18c633f5f3e8a76098ff656c642bc030a8feeb906f3a55aafcc214dae49c4bb6a074dabe30a8d0e14e16a1f2a8b917c68bd6a2836b658d14f1c2a4ca42dd785dca268a7661146149ed6249b2a514e9c26c99a7848b622ecce19df359958f38e1c2b4fde14d56ad3a93bb85592dd693203d4e3c6d618e596257b9a96ecd502d4cf5be7ae2b9cc2749b4302729c5ca836cd7d02c4ce13c78ee139dbd735998449ed83f268a85295b49f23142b030456dcf35695ea94a5e6110aad54a1631492eed0a63bf9eacdeba15464be993480baf92c40a838725f511b29d6f5e85b184d01f5a7c76b316d8ba80d905eeea22f0a103101a17280e5e0ea34001021084e402fe22228f6a84d858000046446eb8800001781d9c1b3aa0716420400022218f6e788d101b09880100020000000040000b00000000a080f71a342e20d2ef35683480012b0262e32c20008900808848c24100019c91e31c0508c019395e2384e300001800041210f21e0c0040001e708020d8e005828cf800c477812023323e448e87470252173666a05c24af3123152071615cf53c13363d086dd25b00e1cc4804485b1c206b812023211f22323c3c1290b430c5afe8a973bdb3307d2749127a61f2a7bd6561aa3e0b2ab7eccd95508d19337e06cff8199bb1c8aa64f9bb1cb38166fc8c9df1335013168906c88c54807c050d90198900e90a933039efe53fce2e5e6346fa1839337203d90a0419f11a336e7878242059617259339959b26398f083fbecc69987bf1040641c10f436448a8dd00041111a202332406e241e212dc8558cbcc88c9f61805485d798910890a94840383352011215083222e3c60c0f0f04e4294c996b623fc8c714e67eef20634adaf81db314064f5be2fdb32895a693a41885716c8ac2943a7b10f2f4e85bf364288c9e2dc8bbd7b7fe17144611622354e446e315e4274cd2579f9f245e8ff484493095bf53fc2a41cb101b40f84e9892bc8e4f52d65f7a13274cbda6b43de8b44f3a64426ec2e8a62b3c090fa6e2b7d4c4952aa558a92e7d651bcd36794866c258f964dc73ce0942942270c1041213e664b2c7eedb4a55335ec264f52727597c63d52a6909e3765ae94f59a98451fdc4932ecdfd9f7a29611c4fd13357ee244c954f98d60f3296ed9384e1d35c0a3e62dedeb48c44f2f5ae2297d6522be4b28dd07ba71adad9848439bfcfc4550ba33dc224687a16154a6bfae97070a4238c955f6ed1ac64bb20fe60f3b05163cf04d908f3a8df761125c60873caa8ee9ce453d7cf2fc2f4ab57823a71f74b5a146192f784caa23d904c8449ae54ce9ba1eb732d8c08539e7ac9a3a64b16cfa143983aa7e792f25677bc3b6908e30533adabb2562a540883ca1bdba5122184f94bdb9b0e2ad7c120cc7f5549befdb5fc1c09c270f2e93022fe48e3200361d6bea43b7a98db5c8e0e006170cb5b51a2a585ca655243860e3c465ea4d81f8c6392e82728a52ecc637e309ca04a92f298c925057d1fccf94f2a4916bdd926a7201f4c3a66265f540e22eb0f6a84e8600fe6dd3693c3789204bbfd834d0fa62ccb97041f59e447b4069907a3c55392924eec9c04b58c0fc183c984efb2587ba972971b7807b358e9854e2e27fd441a20891d4c1e4caf6ea7e8880f505a1d4c9220ed9467e9d7ce061d0c7736d25b3cb6ce723f0793a404d9498e92fb602b72304949dd79cebb566a486226838c8371b6f333c7a46839367e4424657088c68d942a483898f20815d3c193f20daaa8979c87a757fecfbd56da04991d0e0e37184bca33f7bf62b6e19465477f967daacc68241b4cd963625e0304366c9c909155410d3c6e906b3065c4c2dd67cc254ba5de56f24dd492d4e239483598d693f89877d134985d3cefa9ac9dd4c8190da60c75722c693c4b797f06c37a9c1394ecde0cc63329995c921cce949c530673573ce9697d2383612ffcd8889534f623442e30432167390663f52561f721db90111b2906d3e7effc24491eb530988338c94b54f23269a604832673c962ddad5adec5ed5a56944eda7112e4174c3254099e37cdf482c13bb848f5f4c92e98db243769bba28dff13a8a0061e2e482e98542c754bc7452fc73fd86cac6dc1a467ba3d94ec95fd93c8d91101c1d4827188c0059d055350c2633cde050ba6a454a509f369547f2eaf60b090a3f27cc765f52aad6036314ae6b3831ac82a982ca6c71331f53962ccc54052a1b94aa55ea72d6259ede5144ca17aefe24af61c264f2998af3ca5d352b28a783ca360ce3115fcb2bb75be100aa69caff760a97c82c9478967f1b2934e30951244454d5213d7441f6c2320890648c88708da07b209869393eeb3133da88f950e24138ceb2979c96df15c82179eabe7243d359e0f360fa3412ac1e8a59f6295ee83d13c904930894b7fba6e3a5b094a9e4622c1bc2545ff1e773fd8f818a61b517aa55f9d52461f6c42e8c17b84241e232033decc18e6fb64d9d1c35a9f30fee0699462a0426c8ab4301a0f6880bc203d80c43056ffc9e1243fdb123e1f6cc330d8a7cf262bba73e3000ac3a43fa4cbfb97efbafec1e6f13e001939323ea42e18d6ac59b433710fb54ad1732895a4e4e2df0829d6c030f8a8784e2a5b481715d1b8210210fcc2e47174cae7259ed2657c726478388d2f6f230830f819274404692f03847d61d269eb63ae27a47fb017869357c4ecaeafa8517961be8ba6d2a9b57117a6feb292e2093a8a757461182594b01667920bd3769f24fb4fb034b9425c187c2feba756d589ad6ec1c69bab5a0adfa51dd75ef25e50137eb0d53822201c1c323e3928e0e07819203666d4401c1c2c40c1a22d0ce22bf7c2dce42446570bc3d5c591abf849848ed1c29cfec4ec5de66fba6916e6184b4a897f0e29a69785399824a8fc21be047142b1306a7ce509164d4ed21f58184c5578b4e4c19368e32b4c9dd4ce6d4cec0a53692dc93636bcdeeeda56186dacc489134dada88f082b4c925ab14f4ae9e0a75d5c853989e96c71848926070668daa80a9368e6a2ae63d2dabfa9309992b257befc537e212acc16a746cfe675d78d33c0536061561d6fb39d366aa77da0298cfd5be779f6a4507e82a530b577f04b625ddf4ea79014e620c65198c3a5d47759944461300bf5ef24e93db92ca130697a0963a269e75853c9fdb48f50e169003ff103f44410b013e911a7656aefe43349c809c3b48285db8ba5ec953a4775f1e0260cdb167af5834c13c6ad784947e4a5f5e83013869d5382dcd88c983086502a7e5749f2323b9730ffecd58e7f32a5e7ab21a3aca025ccf296e3f2f9c70e0faa0496bf4af5c196dc10c112e880064872e3053b05a484d1eca4a9f211da4eca380993b0f8d1c46e64953c3ed0c899718792304d89956a374c3ed8426c00e13d641c1f7c62244c76af5fb515e4724148ce1ed1382322344a900609b369114ff3a21f7e1f3e42b170e9dd6d2e887a59c54fb2ffef9ca90374844168d7b21cd637c274c19364d7297950254698e452be2df33f96b4651186932d5b8a3028d3237af47789308baa8b735751829039228c95e4bcfc5582fcf2d0210cbab492fca8723f416c08a3aa0725cb4e8cebfd0a6174cfa1795a374218439ab60f2dd5208ecfda56595171d30ad71022ae65dcca4f4198468bd82dd11d1716d141c8f1b00b84c1646a494a673f893c68dc08f1a8e108085332d1d3abb5ac05ff60fc2d495e4ada154ae68a7e30e9132e9fe8174fa50941807d30f6de9d9ede356444d33c66dca071c807f3788f2511d332a2f4b80783b0b21cc6b41bf22325b002eac1246fa851f2a8fc1425f6c126e2a1c6031a373e46540598878951a8642c858342d27030168c44025130105236cb0100e312080020601c8ac502c2509ecab9aa071400037044263e3c242220180705428138200a06c3602028100604018140301004851e12b5466aec6dd202d642c7d1043a3303790457422b52b1483765c50ec603e390758149ef1a47ccd51a256ad7a2e30d23725ebd5c390a40dda5cabbb9717f88fa83d8b896bc0928eac4fa97dd102efab30dfb58fa3bded1b9d10e8546d76a118e1c7db2e8731f9f7d1d4d1ccaa4110e4e65f18d10bd829455e32ffae1b30f91a700bc1ca8a529d8343d6a8c7151890a723baa76852b405a0c5136421b3b0652fa88f6dfc192d60c7b43bffdebbab681df7f89f73aea123ce3b6576a6073f0fc6250e4f2cb84ccc00f8aced3ac643766987c360ba84482a1b0f5c1aa89f0bf35e10272e09a962168652212740c44ce96238fbd12658eda8941beef21a08cd2a481450f73a0818bc4417dd0255067cf95fa01007124cea80f88831e7652235a0a50afb1d7e731cf341e044ae71b71316280a05e5c349e3fa2d75c7f7a3f2d8db63885800b15fbad77d2f0671046049a64c10145aa098011d16299e65000abd35f9542496231ea457845d2a067cad9e46a030ccbbbb8ed374887fe5c083bd8fc8e6cc4ebccc5cb17862b9da243a72582a8118a599d8b36ba711a87d89898b4002a655995ca2be404f2a23bdf430e88093b2a0690be06977f38b56e30d0fab96c7ea6cc82c62cff16440f2171544c2d025c61f1925ad00fb72917faa9302493f43b14ee2a6f4e310a7bbcef9458daa0e7863a7c43752e0fe720907224f53870c716e0a3225ffb68efb1cd035717e80f26283493f19f6d3b39b8414abe59f047bc24ef9d6d088edc2de906c7843cdd04319a535bfd982e4459fd26ca2b9b1228e28e41497eeedbaf08a387f5a87a5e51bfa44cb2b9465972c196215d675b0f2e6260dcd11756f37f755e2c86b95d9a01d8102b782d16da29d22b8ae7b4e6f8883ad1f322e26fa57c80bcae8b747476d411b7adac62c233c2619ed943a4b876ea232ad1861471462392ef62494bf68e048d312d54568079b57794281c91f827e3c0a18f157c5b0e1c65d24a58a827e17c1a5378c930ffd84f3238cd837c11b905fcd88eb61f864b6079f339f45faddd987b29f6f8072eaaec4800ddb11689b7ecd78fb5204973113050a121e0fac233b40a106490989d6fff2515f0da8486682c5b705cf471df6279ec4965f206ca8896e32b9c34d589a659f18aa264672d106e0e19b5d3b1c66f6390c4519a8cd6d17f805b2f8677b6a7a8bb647356f9bf3effacac6914789ef01a39d5d775d634ec2c8921437e56501c775ea6f98e4ef902b0adf5ee88f9aa49c5f7010e6b16e6efebe134896b0c5d3c43afdfc09ae7f0588987216b055276cabb102522c103b7badfc9c42befd4e2deef1f42548e55c7de9985927e5a47be46ce1ddece4bd331a29b5a9d035deab7216ddc26c9261fe9920ea204740d41889a950cc5561d63b0070a4f8848e66172d69fb4f4a9ef5ad9a29ac9fb3ac05e14932e6ecae241ff586980da5d5d4a733f2f2a57d089ef538953137aa0883dbbd042f48f1de869728dcb57129f478199153eb1a7fd9760b4d585c6c808a537107d605516310bec2e7388d483190439d129e099ad6b04d5548a42ce1982aba0223972b107efb78e2568732db94ee9bee544b10f31adb6b1f9d8867460ac1bb5c450e40a5985c0c316ff80aa34138a361bfb291b466255a3210c72f296335e37c5073a314e7775f146eaf05ee224dc4393f28a7e8ca114448643bc0083899467574d3303e26854d5aa238a4786530804fb26961674882cf1a3f164063260b83eaa32e434b781cfd15f4a93c3bf8358b63dee87d00e9828574a92a5d539b45c37f24bd7a8c5d683da32b2f45e6859b6bca23221cb529d871ad042bda4cadf28ea4ba5bf07521e0cb0089868bbac1be6d46010ff54b331c81164211d7baa4711a898b9b49a27257f7e4017886c659e9aa19e4f9bc9bdda25c66d0fecde1dd224c7b9eab77faf1fc35dddf7aac41fb3b8102a30b9442c76cb10dd6b614bf1e3e692041d902e9b4e035a1d82665a484b778bd1bbebfb73897f9e8e0f23423eacc2d750314760364849fc49bfe1496c7f5671daec006a8f4b8305a75c00b937b20c78b4a94605cb0ae9324c8529847636b603f7ad512d5e02c681621a52f767082127b737acebb3b5b11fd93d8039027f1a47afda0ee071645304ce4bd77e458ee384159e6b5b587efef9df2066adc1e2932155486c8ab88e3a39f5616307c6ecaa44b573ebc61c2081d89d8428e997d17d016ef43d30b788b71182ab1e471588c1cced97288a4640029a6f51a74a070490db2c59403c751c0aafbe6102aa00dc361b6bdec27aee50f94a7be8695ece5ec5dcb98a80a8e18d4dd63c68bfd2bc9e14d42ff2ceefa39cef1e11084f0f1a99495a9d4c9f412748abb5e2fd1e0f45c9bd0608118b0a4460278b3723903530dd94d965419d70ee3a751e3e04a9e15008f8e51223fd921cd8a55f2ca5702231a0f64920d6b8679e79eb2c9ae29045d1bb37d4905abbafa59cbd9c5b197790ba6fce4a497be58a6a2f7a1cf12591b29226dfb22d392429adb9abc66b59f03ee64fd6d081af2197e63f971f24841804c03b129090293e14dc23bdf2f50bcd29f3e25e29e31f0b9ea46ef955319c7d788e140e97cca1ad466a0785adee2a000653f7b5b09b13463c8368dcefebbb4e761c2941bf36b6dc215e57731fd5c6633297dd722db240eacc4c98632c89e6a23d3e925db23fa3c0d4b176cd1cbc307bd7b22f6081302011e1eed53f45c63cd3d845e220701a9600d2670f78b802e9fc65c602827741b57fa7653382338de1e8f7d485d20a0bcc46f2472c9fa2bebf602c1e04ccc41210268a32699e42e3b906e50bcfba8837ca9235d17958ce868c2615f02ef8e811ae58cd7ddb196f550b521519713a543e5f8a62024e558258d37082aa6938d7e701d01b83d938945a8c84b4d1cd167b00ba7dd9edfe07c50e653f2844167133d42fd970dfacb889930514cc0581bc948ef89fcf0bd66b7968021791b468518ab65870c72a1ba2ce420d3bb897c5a55d275278d0fa7f67e638e742e83e872b2302b4b5f7bbe0a70d290cac1ac56c52d24fe589f3e1d3afcb49127ddae7004546bff062c62ca2dd8885450a3219cfd50fd84e4da6fe0fac56bcf6f9c6bcf64278b465a209adfed5c614a057ededcb7bff2c0088965aa85afcfc4d04273366da26af227c9971d84092bebdbffd14a074121d1ba617910216d1bf8edb6371c10b601340450413381867dc080cc733a50fffefebfafb7f80b51b00fdb7cdd170e0f13237e0679bac452723101dc83234b5bb1196f5165dc2aabc208994c186295fbf700b7d18e255965977a86f0eef1a2fca416265cdc114473f318f992231e0982fc26a1f35c3f8f7f5a43736b3dbddb3d1056673b6da7f83a3b609a38a86d8abb86a566bdda4413ffadaa1d6551aa6a0317812f3fc1c7769a3b8417da10e8a08cad44af99a7a8b45329702d65d234dc41b0d419d5e12c5abbb88b95e9f8161b439610ea18a474f62c40284c81380bcdf8c038a89ae78c34098f036c5047fa69d3dd1b878c53dfe695b5f8864d32d390a5d7ac2f38cb4d087e5984056ea6afcfa9ee0234576a10a784af94d92fdc6f56543da896a18e9d798f36a8506ea22f48f3eb5c22052a76de41527395f18b92e0b41ce1e4f41026061715a73fd0ec0eed3ffc31e1fc66525340bae443a40628a57f349ed51beda8ae2359c5f76249fe3f9f8429858fa18208315a9a298333d2380014232466f68e3d545191ccb7ab62e2a2984d5bc7ea28cf47c366a6218b6e0a8f67ec25ed86fb8b580b2cc49d5d9fa7647a0ad14fdb25e430260a5c28218c6e48745e3c70a16aacb1e4f074503a254cceb27b9cc0fe3cc191a1c72d1771522c51ec0a18d9cd7b6d9991028474346be9c31ccc2cf988bd23c0ee7c4cc1f4f6ab499061df36abf8824cd42b292014f9be97db9a9331eeef10ee40cc8561cfcbe8e4d364cf0cd954ff8d7ef613bb4be68fc195bc2ef2477dc962cc25551bb74b22d0477100727b0964eb70bbc1ac0baae647db86ddc9b4ab7cc4d0e12f150e764f73ab808b767bf438e3a5d42f9fa298dc7abaa9639303301689d189b64235f0eb415965d1ab3d466d6c6d1b07d4baf6075793f83dc77e41e28cfb80885827f8a506d32265c5918d036c3b23798a08f4b8268671e8435a73d7d9b09eaf040dba1459bb5d3375286baa334784e1864f848bcaeaca8607d2d18571c48938125c65eb8dfec2fd33904df9d443641eea5c250f71caddca2882c2824851a965b2751e525cef9efdddc11c54158d7dcf74f83f29fd681d17852d9d57f3948befa84fbec052cfb8109234973ca65d8d9e6bcef4f9854e5803b3aba0f2e8abad37568cc5464bde14ca3c5e76839ae278c151449cf0bb104080756b907292fe9ca1f72a631ad64e02e3066e31aa73d1b795b1c0ac4939a7314e94416dad496d5fb159fe49f9a1e5eb428c573b8aa35b47385f21441503d741872fd1730b2cbe066093f3d511c0dbe264f381361cb6f93e62da6e55c6445320de65a10040c8cfba98f908752c813f049a27450466a0517d21ce714b064cff6992cb05c330ad441f9b7862a904439ac5ecbc22b8e72375c6fe8d7c955391cdb338bff23904a681f7176fc53f05aaa07bf301b23ee5bbae34a9354369c25aafb7a39a7767509f09f5a364eb3ef5918d91ec179b9b594392f67a8fa435de527bc133ad6b489b2cc523252e7101ef287191f1f1b9854e4d2b16034c533418f616e6c3a96d426c1e246a90e70b8edb2bcd8e6e21fba0dbf250ba4d465dc788d6b750c00cbfba7e2477bf29fc19015786a729b9e3058c35d4a87b0236f7c74341f05766b4a6c51b7f990ce6736332bb7fa53b9e2882e5ee4ed3cdd0379cc9711b47fb751849d1f75f8bc1b060da4c1fadce78177c2f5c4daa6ff1c6d23eb9d9aa9b6e71369f96e243fb31702478756c77a7bebb81d6d48d478dcc6f9b44f43f9b8f29626f7d994b2fbfdb7c55d4f231dda56dcb0a04bfae52a949e6222815543d7c8a555e8241b5bb7173f9dadb2bf16c6d359273bda357b1ad603dc39140a0c02de290e0ab967f7d379854037bb712fee010c1b6e6b09d8e791fa9f9cb3bc5e60486070ab8986d4b3c480dde0e139601c63c3db6e80381975661f74cf32d8af7b38a2c15aa16710675ffa66547743fedadcdad5306fd14dd7813f249c0ecd44af802d327629e3f81be6b1e6010e004cf56a4faf4880a5e88267aa3a6143b4023ba0ed8f2d6bae6b3be370c09e5b89816926aa207db24e6c87cc073a506722c5a2d12d164a4fc0b025df68d9a52341d4142cf498c1505cbd386eea446a0dc7d8882e70b3adaac404fb45050d1eafc23d4576b21188fec0d7a55ccfc5c0b11104abb3bef167a6e10db4168d7bcd61d62c2df5106c33f57ce2cd51f9c26a0010e084986ec05be05b01c67d8e24fa75201be42b8ad29d07e5531891819b74cb2e78aa619632a0569c3b08210530dca661705a0b594a0f7b21cf172b7ae737aa68d87521b74265216d15274714d8592723b617e08ac5b7080ff010818583fa4619a6327d301130e484633028ec95fdb9c1e4906da575af185d373bdb36a85b4544b350418102531123f04398b8476dea4aa34d8c916dc5766f20dc60760d6fa2cd0aa41b9241ce69291ae44cb7ff4c2570c5b68a30001c1dd0fa442f211b1033453a6a1d3dde5b2298cc981ef9156de0659c54ab5fe378236de6c201e347b168210166de6508e669563220711a4702d104c0553efbb62bed3c724155ee6b7833365d579124d058ef11269c5f0f9eeee6ee19ed91bd1bd456d8ee55ef52f6673fc3bdc53f5d15d61e617c144c35d3752333791a4327592d30f0bb0da70f402e6e2790a3fbf842fdaf8da4b15611049a6b1f4a10f5446041f13edc03c0ffdb413a5ffde64d99e149fd68d044bc15b2c549f6ca4ebb5a80e1a4f9123eee4d989aaf079c113557af7f72a2af066bb5f58bb64e75792814005e2040221f6560e26ba7f67e8f86d98916a0442bc6a16464423454616b05976c11325b58510740ab5b4e222030173489cd49ebc29db438166d9a4dc29c8dd41a9c909833e22a67e56fdd3ea909ec3134f92edd68f6fa546e32e9803d93be02884671a1bea34854efde78ec6ecbc2487b5e2e6e48ab7731f0463cebb7bb9f6705b7651e911ab10027e22a19c9dfa324bf79f58f9550a965bda60f8fa030a6b25d7066f22df78ab82c01273d5ef08059ac93da83b522fc007601385e234f9207022f1bf4d96ca922b3d0fbf056b0aa5917e05a89962c79944fa505135c0339cb9c0a628096b65abb3fe79304a144fb142a8e0eddbbd63220c0f5420e1245927826e4124f7c2df0c75c9d6bc512680fb4bb91bdb0ef343ca5ff242152ab8f9c9d92af3b9913036e290d36d60ed53254d15ab979b5a2069914a0bc11fae5477c133419a3f144dd01704a3e7394026ec52d8f0dac21633949149988a4313cf04aa59ab45a70e03cbd269034fa25297cd3f4cf0185e94cf02cf8def2d91642c560bb061377f9ac56471eb2e908e0717acf103b1987701ba470dcba4c3147be0a26cc1915c99eefc75186ff1252abec10e15a52c67179ec0a4ba586ea31b0642303260191e6e2d1d19deda9d37e4cacde5ca9526068caaf02b6233613aa1d21b1473d9ce2e5d95ce5fae69681356248550d7eb45ce136b8028cbe1df541cad7953c25b197ed3adf32d3f9dd3442552f7767fc5c25f445c1a7ea7461007a2e3c30d76123307ac379982cd193ec6cb1d2a109ec509f0588a1b70a4fb0def95285a750f55987fd7460a90a46fc30c4dce33ff4b000f0e63bb1298c4681830dcdaf23c8a67db30e028f07db816a5ea9606b94f235838a5aed97c9fd322b9d37e887aa7596c4d79b6c5ef6715f28c44bbc4abfa191fed1d3e4527f0e0ca7865daba8858234f57140e3cb1e754f5c78370c587dff4c6e5d8f2418142ab553573f18979b05f95c489377cfa275a50118d20c6a33bcb519bb26b5e2c495b9a3a6749b6b4f921db042f860729ba77af44107b6a1d676c4a7c99b86565d489f1b302186761f4bbb386773373ad5839b53dc23655a6bb4d20cdce7d4dd7c0ab8e90211b3cc0cf6a2308ebfc83fc0c1e834d6dde20581862b576c5179b4ecb5f6a04a3f784bb73543a44b59408ebdc6a1ac8031710cf5c31171493c14ac4c3f9abb283cadb04a5adafc9bedd363e5fbeba67692caa69abe260c0d791cfa6d37e1055381dec62006db84d9f47421ef5e7268e5f2a85b3cded468531775db7d6c33763add8c7f2b89682527c30070e44b4d54102b476757c037d26d21613222918291bd762089bfe886d8172676723bd8c176f67a69663d59d06d2ca0babc54bcabf570a740b848f8987c959eed4a236bbc73ab7ca7138cd3962e80565749aff806cebe8bb2168c44b8d2d3c1c2c7fe26078956a5e887925ec48289f394a19ad789321abd6205972db2e6144671c9ad757a31db7fd97091db7e875762db7c01b601e4862242eea8ba4d980107701b795616499789361055ea7da88d4b1ff4c0455740b9cb2290e2d6c69268f7596bd5889b86f0b92823b0e43c4955236ef6f26c6dcd4adb9917de652216aa62b551a55af86dab96f4263fed1278db316990c2ffc914b2fe6f424254cfde29d37699582021d6708ff2ab76f21ed6a39b13054b6b3fa60e9ed2b681b050dd4b0af3efaa0ec45a912e549b4b682d289b1df507464d66ff2ba8f2e45e9d3bb6ec63c10fbd82dd9a6e49c945233587e82da87eaaee4490390263344fa1660982dc1959f12574ffde37845983cca07922fc0f75319ab217e664ebdc79b194e02ac958ab816519587e1acca4fecb0bd963aba110cc0a754e3b1413448113ce9cc9dec2b2c9880ab7fb3fc4098a8d445926786c6669651713894f8ba69b0698d636d372b785030a76773c2ba0a11dadacb9cc33f18f76e9134898c88705e007f12d45c3f696de4481a13c4486fcd568b47437371ceaeb5c3bbb211b4f9aaef4c1e3a0f96683ef45aec90f51bf23e2dc2dfb9ed2d2cb7fb80c7520b9dca4032174de30e58bc7f9d9eaf40e869664e87d170c6953c16d12b4758d79324d47c9c34f97aad2aae7eb61c798f9af34c2b96625e0536c5b0931fdceedec387b929fb164a0d5715f16f3d2de3bdd5f7d282223f65dc46e093632d9e08cedb10ed5d3554c263aecdea0540a43a5d83c29314bad4497d3e7f1c1edde729ec51d853d29c15977801fd36064ec15ac8fbbd59c7114f4dde54835c9ecedef3255a250cbbbbcc45151c92a692a04141c321052343bd200faea101ee8c72d9e605146904c45061c7a3acf1d34627048a871c6deba7dd1a8e9801162eb2b0540608481cb0e7bee262d2b89f93e462ce078802405eb63f2695d4edc9d3cd5c02a22b6c7bd95d37407b29e2a113da40679f1304aaa5ca92aec08acdc985f77f232a0b0f9de32c0fed3bb13b859e65913248b6bc2b71c186f5ac48375fd05bf7f45ceece8ca65b4ce327e75264d1d12486b1b6e709726058d8a0068a1107ac1e01a1b6cba60b1ca0054543059e9b5da1ba58686329ec3245355cf99e2d04e27d07f2446e8fab54a0ee41cd453004aae1b66c5d69e7a2a8a9f87dd546f7321d027a2c7aa358d0405fec0eb110622567ce455278607f05c1b032c04f3f1220deb220e2fd5f5552769bcde517a988ce1354fc0425935c8d220ea3c853bef1109994bebc66f2c26c29614bd832f0d890382d35814224e0c173d2e83b37cc3d8c8779e6a272c324ff5297d09e2f234b3f3c256219690a43d4c2d585a94460249e5c0c09d9448e457cdd9921bcef82f63ff8c0c8ca4b410345011c01f28db20a6d39865840299fa90757222b5f0da0331ec443241d43b115a342ef2a6c5c3395ee0b243d2bb52c3b9a82fda19439d382d48894b31eb3a24fa1cd85f42983a9c3dc992a52230a8d91e0e4badd33f5fe7471d01ba4b7ae89b7ee66e220b4288403e51640a34a984a3367834bb2ac8db6bce2341b144808087bf0cc3de51b9b0abb71f75d34f1b124c4a6b5b1c95897b20d828842804c2141cda623d4a2ca134e219d7dfd0f2754e25900a3dd8a30f1fdc87e27f58958cd7af7d8446a7fb3d96ade5e1bcb3f015de47e3d24db8508579347befe9324125afada274e2981d0358d5248a18122e124b615985a6c0eaeb99689270c2626c2868649c5ba7f6f17a0c7438e648f89d0cc57e7463b43a9262f489b0e650b9fa04306300b20632a90e1b6f4e3d69b3a4417cead386a6ad8bac776f04447afe5c4d6868fdaa95000ad5e1ba60af4460e710147baf586fb7a8816adfc98bd6d43932cd16b56e1d04d9d5d8bb032f57e89cca12d3cbf259ce0a8b4e00f8341494a94aeae4915a8f8bf7a202a3ca1291fc211451a6326810a12e25b1e4f9b2b41b44eac41bb016ac6c0d8c19cb8558052632c3892187a008f60b18d9471c356e8082187c6c16af703249cb6ada558ae264398b686694036bfd2e64d93506574dfac010bda8652fa7792b4a1032fa3b77e7db3e9a9a9d665b50ec1491e483b6b93959870cfb8b9a3e2c5d724ab616f69b970a425ac3aba6fe3677558506eae0262bc934d8a1ebde696c56230e5b55ba283e9c44c117a9e745f38e26cf98c39a435e1254db2813ed291394a19e866139934a3623652fc44808d60891689b3d2a35be87ad5ebd891bdf0c03d1bdf495c5604e5461b57736aa4b802a43d9b406ca00adcd9f337f13b8c64a7e58700aece3d08afe38918b9e9bacbcc02e67a94d16abba965ec0c53090747b6e93864c03897375fbb6ed9cce93bf84511dbab07a8cbd75abfdcadd7b0e099b19bf371dfd0a", + "0x3a65787472696e7369635f696e646578": "0x00000000", + "0x3a6772616e6470615f617574686f726974696573": "0x010888dc3417d5058ec4b4503e0c12ea1a0a89be200fe98922423d4334014fa6b0ee0100000000000000d17c2d7823ebf260fd138f2d7e27d114c0145d968b5ff5006125f2414fadae690100000000000000", + "0x3db7a24cfdc9de785974746c14a99df94e7b9012096b41c4eb3aaf947f6ea429": "0x0300", + "0x3f1467a096bcd71a5b6a0c8155e20810308ce9615de0775a82f8a94dc3d285a1": "0x01", + "0x3f1467a096bcd71a5b6a0c8155e208103f2edf3bdf381debe331ab7446addfdc": "0x000064a7b3b6e00d0000000000000000", + "0x3f1467a096bcd71a5b6a0c8155e208104e7b9012096b41c4eb3aaf947f6ea429": "0x0000", + "0x426e15054d267946093858132eb537f105fe52c2045750c3c492ccdcf62e2b9c": "0xd43593c715fdd31c61141abd04a99fd6822c8558854ccde39a5684e7a56da27d", + "0x426e15054d267946093858132eb537f14e7b9012096b41c4eb3aaf947f6ea429": "0x0000", + "0x426e15054d267946093858132eb537f195999521c6c89cd80b677e53ce20f98c": "0xd43593c715fdd31c61141abd04a99fd6822c8558854ccde39a5684e7a56da27d", + "0x426e15054d267946093858132eb537f1a47a9ff5cd5bf4d848a80a0b1a947dc3": "0x00000000000000000000000000000000", + "0x426e15054d267946093858132eb537f1ba7fb8745735dc3be2a2c61a72c39e78": "0x181cbd2d43530a44705ad088af313e18f80b53ef16b36177cd4b77b846f2a5f07c306721211d5404bd9da88e0204360a1a9ab8b87c66c1bc2fcdd37f3c2222cc208eaf04151687736326c9fea17e25fc5287613693c912909cb226aa4794f26a4890b5ab205c6974c9ea841be688864633dc9ca8a357843eeacf2314649965fe22d43593c715fdd31c61141abd04a99fd6822c8558854ccde39a5684e7a56da27de659a7a1628cdd93febc04a4e0646ea20e9f5f0ce097d9a05290d4a9e054df4e", + "0x426e15054d267946093858132eb537f1d0b4a3f7631f0c0e761898fe198211de": "0xe7030000", + "0x4342193e496fab7ec59d615ed0dc55304e7b9012096b41c4eb3aaf947f6ea429": "0x0900", + "0x4a83351006488ef6369cb758091f878c4e7b9012096b41c4eb3aaf947f6ea429": "0x0000", + "0x4ff3897794d496d78686afcfe760a1144e7b9012096b41c4eb3aaf947f6ea429": "0x0000", + "0x5c0d1176a568c1f92944340dbfed9e9c4e7b9012096b41c4eb3aaf947f6ea429": "0x0000", + "0x5c0d1176a568c1f92944340dbfed9e9c530ebca703c85910e7164cb7d1c9e47b": "0xd43593c715fdd31c61141abd04a99fd6822c8558854ccde39a5684e7a56da27d", + "0x5e8a19e3cd1b7c148b33880c479c02814e7b9012096b41c4eb3aaf947f6ea429": "0x0000", + "0x5f27b51b5ec208ee9cb25b55d8728243308ce9615de0775a82f8a94dc3d285a1": "0x01", + "0x5f27b51b5ec208ee9cb25b55d87282434e7b9012096b41c4eb3aaf947f6ea429": "0x0000", + "0x5f3e4907f716ac89b6347d15ececedca0b6a45321efae92aea15e0740ec7afe7": "0x00000000", + "0x5f3e4907f716ac89b6347d15ececedca138e71612491192d68deab7e6f563fe1": "0x02000000", + "0x5f3e4907f716ac89b6347d15ececedca28dccb559b95c40168a1b2696581b5a7": "0x00000000000000000000000000000000", + "0x5f3e4907f716ac89b6347d15ececedca308ce9615de0775a82f8a94dc3d285a1": "0x06", + "0x5f3e4907f716ac89b6347d15ececedca3ed14b45ed20d054f05e37e2542cfe700e5be00fbc2e15b5fe65717dad0447d715f660a0a58411de509b42e6efb8375f562f58a554d5860e": "0x8eaf04151687736326c9fea17e25fc5287613693c912909cb226aa4794f26a48", + "0x5f3e4907f716ac89b6347d15ececedca3ed14b45ed20d054f05e37e2542cfe70e535263148daaf49be5ddb1579b72e84524fc29e78609e3caf42e85aa118ebfe0b0ad404b5bdd25f": "0xd43593c715fdd31c61141abd04a99fd6822c8558854ccde39a5684e7a56da27d", + "0x5f3e4907f716ac89b6347d15ececedca422adb579f1dbf4f3886c5cfa3bb8cc44f9aea1afa791265fae359272badc1cf8eaf04151687736326c9fea17e25fc5287613693c912909cb226aa4794f26a48": "0xfe65717dad0447d715f660a0a58411de509b42e6efb8375f562f58a554d5860e13000064a7b3b6e00d13000064a7b3b6e00d0000", + "0x5f3e4907f716ac89b6347d15ececedca422adb579f1dbf4f3886c5cfa3bb8cc4de1e86a9a8c739864cf3cc5ec2bea59fd43593c715fdd31c61141abd04a99fd6822c8558854ccde39a5684e7a56da27d": "0xbe5ddb1579b72e84524fc29e78609e3caf42e85aa118ebfe0b0ad404b5bdd25f13000064a7b3b6e00d13000064a7b3b6e00d0000", + "0x5f3e4907f716ac89b6347d15ececedca42982b9d6c7acc99faa9094c912372c2b4def25cfda6ef3a000000000e5be00fbc2e15b5fe65717dad0447d715f660a0a58411de509b42e6efb8375f562f58a554d5860e": "0x13d4fe63a7b3b6e00d13d4fe63a7b3b6e00d00", + "0x5f3e4907f716ac89b6347d15ececedca42982b9d6c7acc99faa9094c912372c2b4def25cfda6ef3a00000000e535263148daaf49be5ddb1579b72e84524fc29e78609e3caf42e85aa118ebfe0b0ad404b5bdd25f": "0x13d4fe63a7b3b6e00d13d4fe63a7b3b6e00d00", + "0x5f3e4907f716ac89b6347d15ececedca487df464e44a534ba6b0cbb32407b587": "0x0000000000", + "0x5f3e4907f716ac89b6347d15ececedca4e7b9012096b41c4eb3aaf947f6ea429": "0x0000", + "0x5f3e4907f716ac89b6347d15ececedca5579297f4dfb9609e7e4c2ebab9ce40a": "0x00", + "0x5f3e4907f716ac89b6347d15ececedca666fdcbb473985b3ac933d13f4acff8d": "0x00000000000000000000000000000000", + "0x5f3e4907f716ac89b6347d15ececedca682db92dde20a10d96d00ff0e9e221c0b4def25cfda6ef3a000000000e5be00fbc2e15b5fe65717dad0447d715f660a0a58411de509b42e6efb8375f562f58a554d5860e": "0x0000", + "0x5f3e4907f716ac89b6347d15ececedca682db92dde20a10d96d00ff0e9e221c0b4def25cfda6ef3a00000000e535263148daaf49be5ddb1579b72e84524fc29e78609e3caf42e85aa118ebfe0b0ad404b5bdd25f": "0x0000", + "0x5f3e4907f716ac89b6347d15ececedca6ddc7809c6da9bb6093ee22e0fda4ba8": "0x02000000", + "0x5f3e4907f716ac89b6347d15ececedca88dcde934c658227ee1dfafcd6e169030e5be00fbc2e15b5fe65717dad0447d715f660a0a58411de509b42e6efb8375f562f58a554d5860e": "0x0000", + "0x5f3e4907f716ac89b6347d15ececedca88dcde934c658227ee1dfafcd6e16903e535263148daaf49be5ddb1579b72e84524fc29e78609e3caf42e85aa118ebfe0b0ad404b5bdd25f": "0x0000", + "0x5f3e4907f716ac89b6347d15ececedca8bde0a0ea8864605e3b68ed9cb2da01bb4def25cfda6ef3a000000000e5be00fbc2e15b5fe65717dad0447d715f660a0a58411de509b42e6efb8375f562f58a554d5860e": "0x13d4fe63a7b3b6e00d13d4fe63a7b3b6e00d00", + "0x5f3e4907f716ac89b6347d15ececedca8bde0a0ea8864605e3b68ed9cb2da01bb4def25cfda6ef3a00000000e535263148daaf49be5ddb1579b72e84524fc29e78609e3caf42e85aa118ebfe0b0ad404b5bdd25f": "0x13d4fe63a7b3b6e00d13d4fe63a7b3b6e00d00", + "0x5f3e4907f716ac89b6347d15ececedca9220e172bed316605f73f1ff7b4ade980e5be00fbc2e15b5fe65717dad0447d715f660a0a58411de509b42e6efb8375f562f58a554d5860e": "0x00", + "0x5f3e4907f716ac89b6347d15ececedca9220e172bed316605f73f1ff7b4ade98e535263148daaf49be5ddb1579b72e84524fc29e78609e3caf42e85aa118ebfe0b0ad404b5bdd25f": "0x00", + "0x5f3e4907f716ac89b6347d15ececedcaa141c4fe67c2d11f4a10c6aca7a79a04b4def25cfda6ef3a00000000": "0xa8fdc74e676dc11b0000000000000000", + "0x5f3e4907f716ac89b6347d15ececedcaad811cd65a470ddc5f1d628ff0550982b4def25cfda6ef3a00000000": "0x00000000", + "0x5f3e4907f716ac89b6347d15ececedcab49a2738eeb30896aacb8b3fb46471bd": "0x02000000", + "0x5f3e4907f716ac89b6347d15ececedcac0d39ff577af2cc6b67ac3641fa9c4e7": "0x01000000", + "0x5f3e4907f716ac89b6347d15ececedcac29a0310e1bb45d20cace77ccb62c97d": "0x00e1f505", + "0x5f3e4907f716ac89b6347d15ececedcaea07de2b8f010516dca3f7ef52f7ac5a": "0x040000000000000000", + "0x5f3e4907f716ac89b6347d15ececedcaed441ceb81326c56263efbb60c95c2e4": "0x00000000000000000000000000000000", + "0x5f3e4907f716ac89b6347d15ececedcaf7dad0317324aecae8744b87fc95f2f3": "0x00", + "0x5f9cc45b7a00c5899361e1c6099678dc4e7b9012096b41c4eb3aaf947f6ea429": "0x0400", + "0x5f9cc45b7a00c5899361e1c6099678dc8a2d09463effcc78a22d75b9cb87dffc": "0x0000000000000000", + "0x5f9cc45b7a00c5899361e1c6099678dcd47cb8f5328af743ddfb361e7180e7fcbb1bdbcacd6ac9340000000000000000": "0x00000000", + "0x6441fb391296410bd2f14381bb7494334e7b9012096b41c4eb3aaf947f6ea429": "0x0000", + "0x6786c4cec8d628b6598d7a70ace7acd44e7b9012096b41c4eb3aaf947f6ea429": "0x0000", + "0x682a59d51ab9e48a8c8cc418ff9708d24e7b9012096b41c4eb3aaf947f6ea429": "0x0000", + "0x6c63e84bfc5a0d62149aaab70897685c4ba24bcd9ac206424105f255ae95a355": "0xb104000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", + "0x6c63e84bfc5a0d62149aaab70897685c4e7b9012096b41c4eb3aaf947f6ea429": "0x0000", + "0x7474449cca95dc5d0c00e71735a6d17d4e7b9012096b41c4eb3aaf947f6ea429": "0x0100", + "0x74dd702da46f77d7acf77f5a48d4af7d4e7b9012096b41c4eb3aaf947f6ea429": "0x0000", + "0x74dd702da46f77d7acf77f5a48d4af7d62556a85fcb7c61b2c6c750924846b150e5be00fbc2e15b5fe65717dad0447d715f660a0a58411de509b42e6efb8375f562f58a554d5860e": "0xfe65717dad0447d715f660a0a58411de509b42e6efb8375f562f58a554d5860e01be5ddb1579b72e84524fc29e78609e3caf42e85aa118ebfe0b0ad404b5bdd25f00b304f91831830500622780fd38770500", + "0x74dd702da46f77d7acf77f5a48d4af7d62556a85fcb7c61b2c6c750924846b15e535263148daaf49be5ddb1579b72e84524fc29e78609e3caf42e85aa118ebfe0b0ad404b5bdd25f": "0xbe5ddb1579b72e84524fc29e78609e3caf42e85aa118ebfe0b0ad404b5bdd25f0001fe65717dad0447d715f660a0a58411de509b42e6efb8375f562f58a554d5860eb304f91831830500622780fd38770500", + "0x74dd702da46f77d7acf77f5a48d4af7d7a6dc62e324093ba1331bf49fdb2f24a": "0x02000000", + "0x74dd702da46f77d7acf77f5a48d4af7de5c03730c8f59f00941607850b6633d81fad1867486365c5b304f91831830500": "0x01be5ddb1579b72e84524fc29e78609e3caf42e85aa118ebfe0b0ad404b5bdd25f01fe65717dad0447d715f660a0a58411de509b42e6efb8375f562f58a554d5860e", + "0x7a6d38deaa01cb6e76ee69889f1696272be9a4e88368a2188d2b9100a9f3cd43": "0x00407a10f35a00000000000000000000", + "0x7a6d38deaa01cb6e76ee69889f16962730256ea2c545a3e5e3744665ffb2ed28": "0x00020000", + "0x7a6d38deaa01cb6e76ee69889f1696273f0d64e1907361c689834a9c1cb0fbe0": "0x20000000", + "0x7a6d38deaa01cb6e76ee69889f16962749d67997de33812a1cc37310f765b82e": "0x0080c6a47e8d03000000000000000000", + "0x7a6d38deaa01cb6e76ee69889f1696274e7b9012096b41c4eb3aaf947f6ea429": "0x0300", + "0x7a6d38deaa01cb6e76ee69889f169627ba93302f3b868c50785e6ade45c6a1d8": "0x10000000", + "0x7cda3cfa86b349fdafce4979b197118f4e7b9012096b41c4eb3aaf947f6ea429": "0x0400", + "0x7cda3cfa86b349fdafce4979b197118f71cd3068e6118bfb392b798317f63a8910c174c55fd2c633e659a7a1628cdd93febc04a4e0646ea20e9f5f0ce097d9a05290d4a9e054df4e": "0x04e659a7a1628cdd93febc04a4e0646ea20e9f5f0ce097d9a05290d4a9e054df4e000064a7b3b6e00d000000000000000000000000000000000000000000000000", + "0x7cda3cfa86b349fdafce4979b197118f71cd3068e6118bfb392b798317f63a893e73123ebcdee9161cbd2d43530a44705ad088af313e18f80b53ef16b36177cd4b77b846f2a5f07c": "0x041cbd2d43530a44705ad088af313e18f80b53ef16b36177cd4b77b846f2a5f07c000064a7b3b6e00d000000000000000000000000000000000000000000000000", + "0x7cda3cfa86b349fdafce4979b197118f71cd3068e6118bfb392b798317f63a894f58b588ac077bd5306721211d5404bd9da88e0204360a1a9ab8b87c66c1bc2fcdd37f3c2222cc20": "0x04306721211d5404bd9da88e0204360a1a9ab8b87c66c1bc2fcdd37f3c2222cc20000064a7b3b6e00d000000000000000000000000000000000000000000000000", + "0x7cda3cfa86b349fdafce4979b197118f71cd3068e6118bfb392b798317f63a89518366b5b1bc7c99d43593c715fdd31c61141abd04a99fd6822c8558854ccde39a5684e7a56da27d": "0x04d43593c715fdd31c61141abd04a99fd6822c8558854ccde39a5684e7a56da27d000064a7b3b6e00d000000000000000000000000000000000000000000000000", + "0x7cda3cfa86b349fdafce4979b197118f71cd3068e6118bfb392b798317f63a89a647e755c30521d38eaf04151687736326c9fea17e25fc5287613693c912909cb226aa4794f26a48": "0x048eaf04151687736326c9fea17e25fc5287613693c912909cb226aa4794f26a48000064a7b3b6e00d000000000000000000000000000000000000000000000000", + "0x7cda3cfa86b349fdafce4979b197118f71cd3068e6118bfb392b798317f63a89dd4e3f25f5378a6d90b5ab205c6974c9ea841be688864633dc9ca8a357843eeacf2314649965fe22": "0x0490b5ab205c6974c9ea841be688864633dc9ca8a357843eeacf2314649965fe22000064a7b3b6e00d000000000000000000000000000000000000000000000000", + "0x7cda3cfa86b349fdafce4979b197118fba7fb8745735dc3be2a2c61a72c39e78": "0x181cbd2d43530a44705ad088af313e18f80b53ef16b36177cd4b77b846f2a5f07c000064a7b3b6e00d000000000000000000000000000000000000000000000000306721211d5404bd9da88e0204360a1a9ab8b87c66c1bc2fcdd37f3c2222cc20000064a7b3b6e00d0000000000000000000000000000000000000000000000008eaf04151687736326c9fea17e25fc5287613693c912909cb226aa4794f26a48000064a7b3b6e00d00000000000000000000000000000000000000000000000090b5ab205c6974c9ea841be688864633dc9ca8a357843eeacf2314649965fe22000064a7b3b6e00d000000000000000000000000000000000000000000000000d43593c715fdd31c61141abd04a99fd6822c8558854ccde39a5684e7a56da27d000064a7b3b6e00d000000000000000000000000000000000000000000000000e659a7a1628cdd93febc04a4e0646ea20e9f5f0ce097d9a05290d4a9e054df4e000064a7b3b6e00d000000000000000000000000000000000000000000000000", + "0x89d139e01a5eb2256f222e5fc5dbe6b34e7b9012096b41c4eb3aaf947f6ea429": "0x0000", + "0x913b40454eb582a66ab74c86f6137db94e7b9012096b41c4eb3aaf947f6ea429": "0x0400", + "0xa0eb495036d368196a2b6c51d9d788814e7b9012096b41c4eb3aaf947f6ea429": "0x0000", + "0xa2ce73642c549ae79c14f0a671cf45f94e7b9012096b41c4eb3aaf947f6ea429": "0x0000", + "0xa37f719efab16103103a0c8c2c784ce14e7b9012096b41c4eb3aaf947f6ea429": "0x0000", + "0xa42f90c8b47838c3a5332d85ee9aa5c34e7b9012096b41c4eb3aaf947f6ea429": "0x0100", + "0xa8c65209d47ee80f56b0011e8fd91f504e7b9012096b41c4eb3aaf947f6ea429": "0x0000", + "0xaebd463ed9925c488c112434d61debc04e7b9012096b41c4eb3aaf947f6ea429": "0x0400", + "0xaebd463ed9925c488c112434d61debc0ba7fb8745735dc3be2a2c61a72c39e78": "0x18d43593c715fdd31c61141abd04a99fd6822c8558854ccde39a5684e7a56da27d8eaf04151687736326c9fea17e25fc5287613693c912909cb226aa4794f26a4890b5ab205c6974c9ea841be688864633dc9ca8a357843eeacf2314649965fe22306721211d5404bd9da88e0204360a1a9ab8b87c66c1bc2fcdd37f3c2222cc20e659a7a1628cdd93febc04a4e0646ea20e9f5f0ce097d9a05290d4a9e054df4e1cbd2d43530a44705ad088af313e18f80b53ef16b36177cd4b77b846f2a5f07c", + "0xbd2a529379475088d3e29a918cd478724e7b9012096b41c4eb3aaf947f6ea429": "0x0000", + "0xc2261276cc9d1f8598ea4b6a74b15c2f218f26c73add634897550b4003b26bc632a5935f6edc617ae178fef9eb1e211fbe5ddb1579b72e84524fc29e78609e3caf42e85aa118ebfe0b0ad404b5bdd25f": "0x047374616b696e6720000064a7b3b6e00d000000000000000002", + "0xc2261276cc9d1f8598ea4b6a74b15c2f218f26c73add634897550b4003b26bc66f2e33376834a63c86a195bcf685aebbfe65717dad0447d715f660a0a58411de509b42e6efb8375f562f58a554d5860e": "0x047374616b696e6720000064a7b3b6e00d000000000000000002", + "0xc2261276cc9d1f8598ea4b6a74b15c2f308ce9615de0775a82f8a94dc3d285a1": "0x01", + "0xc2261276cc9d1f8598ea4b6a74b15c2f4e7b9012096b41c4eb3aaf947f6ea429": "0x0000", + "0xc2261276cc9d1f8598ea4b6a74b15c2f57c875e4cff74148e4628f264b974c80": "0x0040fa7f398074858a02000000000000", + "0xcec5070d609dd3497f72bde07fc96ba04c014e6bf8b8c2c011e7290b85696bb30e5be00fbc2e15b5fe65717dad0447d715f660a0a58411de509b42e6efb8375f562f58a554d5860e": "0xd17c2d7823ebf260fd138f2d7e27d114c0145d968b5ff5006125f2414fadae698eaf04151687736326c9fea17e25fc5287613693c912909cb226aa4794f26a488eaf04151687736326c9fea17e25fc5287613693c912909cb226aa4794f26a488eaf04151687736326c9fea17e25fc5287613693c912909cb226aa4794f26a48", + "0xcec5070d609dd3497f72bde07fc96ba04c014e6bf8b8c2c011e7290b85696bb3e535263148daaf49be5ddb1579b72e84524fc29e78609e3caf42e85aa118ebfe0b0ad404b5bdd25f": "0x88dc3417d5058ec4b4503e0c12ea1a0a89be200fe98922423d4334014fa6b0eed43593c715fdd31c61141abd04a99fd6822c8558854ccde39a5684e7a56da27dd43593c715fdd31c61141abd04a99fd6822c8558854ccde39a5684e7a56da27dd43593c715fdd31c61141abd04a99fd6822c8558854ccde39a5684e7a56da27d", + "0xcec5070d609dd3497f72bde07fc96ba04e7b9012096b41c4eb3aaf947f6ea429": "0x0000", + "0xcec5070d609dd3497f72bde07fc96ba0726380404683fc89e8233450c8aa19500e3a507571a62417696d6f6e808eaf04151687736326c9fea17e25fc5287613693c912909cb226aa4794f26a48": "0xfe65717dad0447d715f660a0a58411de509b42e6efb8375f562f58a554d5860e", + "0xcec5070d609dd3497f72bde07fc96ba0726380404683fc89e8233450c8aa19505905fe216cc5924c6772616e80d17c2d7823ebf260fd138f2d7e27d114c0145d968b5ff5006125f2414fadae69": "0xfe65717dad0447d715f660a0a58411de509b42e6efb8375f562f58a554d5860e", + "0xcec5070d609dd3497f72bde07fc96ba0726380404683fc89e8233450c8aa195066b8d48da86b869b6261626580d43593c715fdd31c61141abd04a99fd6822c8558854ccde39a5684e7a56da27d": "0xbe5ddb1579b72e84524fc29e78609e3caf42e85aa118ebfe0b0ad404b5bdd25f", + "0xcec5070d609dd3497f72bde07fc96ba0726380404683fc89e8233450c8aa19509d4a4cfe1c2ef0b961756469808eaf04151687736326c9fea17e25fc5287613693c912909cb226aa4794f26a48": "0xfe65717dad0447d715f660a0a58411de509b42e6efb8375f562f58a554d5860e", + "0xcec5070d609dd3497f72bde07fc96ba0726380404683fc89e8233450c8aa1950c9b0c13125732d276175646980d43593c715fdd31c61141abd04a99fd6822c8558854ccde39a5684e7a56da27d": "0xbe5ddb1579b72e84524fc29e78609e3caf42e85aa118ebfe0b0ad404b5bdd25f", + "0xcec5070d609dd3497f72bde07fc96ba0726380404683fc89e8233450c8aa1950d62c40514b41f31962616265808eaf04151687736326c9fea17e25fc5287613693c912909cb226aa4794f26a48": "0xfe65717dad0447d715f660a0a58411de509b42e6efb8375f562f58a554d5860e", + "0xcec5070d609dd3497f72bde07fc96ba0726380404683fc89e8233450c8aa1950ed43a85541921049696d6f6e80d43593c715fdd31c61141abd04a99fd6822c8558854ccde39a5684e7a56da27d": "0xbe5ddb1579b72e84524fc29e78609e3caf42e85aa118ebfe0b0ad404b5bdd25f", + "0xcec5070d609dd3497f72bde07fc96ba0726380404683fc89e8233450c8aa1950f5537bdb2a1f626b6772616e8088dc3417d5058ec4b4503e0c12ea1a0a89be200fe98922423d4334014fa6b0ee": "0xbe5ddb1579b72e84524fc29e78609e3caf42e85aa118ebfe0b0ad404b5bdd25f", + "0xcec5070d609dd3497f72bde07fc96ba088dcde934c658227ee1dfafcd6e16903": "0x08be5ddb1579b72e84524fc29e78609e3caf42e85aa118ebfe0b0ad404b5bdd25ffe65717dad0447d715f660a0a58411de509b42e6efb8375f562f58a554d5860e", + "0xcec5070d609dd3497f72bde07fc96ba0e0cdd062e6eaf24295ad4ccfc41d4609": "0x08be5ddb1579b72e84524fc29e78609e3caf42e85aa118ebfe0b0ad404b5bdd25f88dc3417d5058ec4b4503e0c12ea1a0a89be200fe98922423d4334014fa6b0eed43593c715fdd31c61141abd04a99fd6822c8558854ccde39a5684e7a56da27dd43593c715fdd31c61141abd04a99fd6822c8558854ccde39a5684e7a56da27dd43593c715fdd31c61141abd04a99fd6822c8558854ccde39a5684e7a56da27dfe65717dad0447d715f660a0a58411de509b42e6efb8375f562f58a554d5860ed17c2d7823ebf260fd138f2d7e27d114c0145d968b5ff5006125f2414fadae698eaf04151687736326c9fea17e25fc5287613693c912909cb226aa4794f26a488eaf04151687736326c9fea17e25fc5287613693c912909cb226aa4794f26a488eaf04151687736326c9fea17e25fc5287613693c912909cb226aa4794f26a48", + "0xd57bce545fb382c34570e5dfbf338f5e4e7b9012096b41c4eb3aaf947f6ea429": "0x0000", + "0xd5c41b52a371aa36c9254ce34324f2a54e7b9012096b41c4eb3aaf947f6ea429": "0x0000", + "0xd5e1a2fa16732ce6906189438c0a82c64e7b9012096b41c4eb3aaf947f6ea429": "0x0000", + "0xd8f314b7f4e6b095f0f8ee4656a448254e7b9012096b41c4eb3aaf947f6ea429": "0x0100", + "0xed25f63942de25ac5253ba64b5eb64d14e7b9012096b41c4eb3aaf947f6ea429": "0x0400", + "0xed25f63942de25ac5253ba64b5eb64d1ba7fb8745735dc3be2a2c61a72c39e78": "0x18d43593c715fdd31c61141abd04a99fd6822c8558854ccde39a5684e7a56da27d8eaf04151687736326c9fea17e25fc5287613693c912909cb226aa4794f26a4890b5ab205c6974c9ea841be688864633dc9ca8a357843eeacf2314649965fe22306721211d5404bd9da88e0204360a1a9ab8b87c66c1bc2fcdd37f3c2222cc20e659a7a1628cdd93febc04a4e0646ea20e9f5f0ce097d9a05290d4a9e054df4e1cbd2d43530a44705ad088af313e18f80b53ef16b36177cd4b77b846f2a5f07c", + "0xede8e4fdc3c8b556f0ce2f77fc2575e34e7b9012096b41c4eb3aaf947f6ea429": "0x0100", + "0xedfb05b766f199ce00df85317e33050e4e7b9012096b41c4eb3aaf947f6ea429": "0x0000", + "0xf0c365c3cf59d671eb72da0e7a4113c44e7b9012096b41c4eb3aaf947f6ea429": "0x0000", + "0xf2794c22e353e9a839f12faab03a911b4e7b9012096b41c4eb3aaf947f6ea429": "0x0100", + "0xf2794c22e353e9a839f12faab03a911b7f17cdfbfa73331856cca0acddd7842e": "0x00000000", + "0xf2794c22e353e9a839f12faab03a911bbdcb0c5143a8617ed38ae3810dd45bc6": "0x00000000", + "0xf2794c22e353e9a839f12faab03a911be2f6cb0456905c189bcb0458f9440f13": "0x00000000", + "0xf5a4963e4efb097983d7a693b0c1ee454e7b9012096b41c4eb3aaf947f6ea429": "0x0100", + "0xfbc9f53700f75f681f234e70fb7241eb4e7b9012096b41c4eb3aaf947f6ea429": "0x0000" + }, + "childrenDefault": {} + } + } +} \ No newline at end of file diff --git a/zombienet/0003-block-building-warp-sync/test-block-building-warp-sync.toml b/zombienet/0003-block-building-warp-sync/test-block-building-warp-sync.toml new file mode 100644 index 000000000..66176845c --- /dev/null +++ b/zombienet/0003-block-building-warp-sync/test-block-building-warp-sync.toml @@ -0,0 +1,30 @@ +[settings] +enable_tracing = false + +[relaychain] +default_image = "{{ZOMBIENET_INTEGRATION_TEST_IMAGE}}" +default_command = "substrate" + +chain = "gen-db" +chain_spec_path = "zombienet/0003-block-building-warp-sync/chain-spec.json" + + #we need at least 3 nodes for warp sync + [[relaychain.nodes]] + name = "alice" + validator = true + db_snapshot="https://storage.googleapis.com/zombienet-db-snaps/substrate/0001-basic-warp-sync/chains-0bb3f0be2ce41b5615b224215bcc8363aa0416a6.tgz" + + [[relaychain.nodes]] + name = "bob" + validator = true + db_snapshot="https://storage.googleapis.com/zombienet-db-snaps/substrate/0001-basic-warp-sync/chains-0bb3f0be2ce41b5615b224215bcc8363aa0416a6.tgz" + + [[relaychain.nodes]] + name = "charlie" + validator = false + db_snapshot="https://storage.googleapis.com/zombienet-db-snaps/substrate/0001-basic-warp-sync/chains-0bb3f0be2ce41b5615b224215bcc8363aa0416a6.tgz" + + [[relaychain.nodes]] + name = "dave" + validator = false + args = ["--sync warp"] diff --git a/zombienet/0003-block-building-warp-sync/test-block-building-warp-sync.zndsl b/zombienet/0003-block-building-warp-sync/test-block-building-warp-sync.zndsl new file mode 100644 index 000000000..2041366a6 --- /dev/null +++ b/zombienet/0003-block-building-warp-sync/test-block-building-warp-sync.zndsl @@ -0,0 +1,35 @@ +Description: Warp sync +Network: ./test-block-building-warp-sync.toml +Creds: config + +alice: is up +bob: is up +charlie: is up +dave: is up + +alice: reports node_roles is 4 +bob: reports node_roles is 4 +charlie: reports node_roles is 1 +dave: reports node_roles is 1 + +alice: reports peers count is at least 3 within 60 seconds +bob: reports peers count is at least 3 within 60 seconds +charlie: reports peers count is at least 3 within 60 seconds +dave: reports peers count is at least 3 within 60 seconds + + +# db snapshot has 12133 blocks +dave: reports block height is at least 1 within 60 seconds +dave: reports block height is at least 12132 within 60 seconds +dave: reports block height is at least 12133 within 60 seconds + +alice: reports block height is at least 12133 within 60 seconds +bob: reports block height is at least 12133 within 60 seconds +charlie: reports block height is at least 12133 within 60 seconds + +dave: log line matches "Warp sync is complete" within 60 seconds +# workaround for: https://github.com/paritytech/zombienet/issues/580 +dave: count of log lines containing "Block history download is complete" is 1 within 10 seconds + +dave: count of log lines containing "error" is 0 within 10 seconds +dave: count of log lines containing "verification failed" is 0 within 10 seconds From 3d60070c1373bc0383aa4f003a5b243c4c4f7118 Mon Sep 17 00:00:00 2001 From: Koute Date: Wed, 8 Feb 2023 00:29:14 +0900 Subject: [PATCH 095/558] Disable default features for `strum` (#13326) --- primitives/beefy/Cargo.toml | 2 +- primitives/keyring/Cargo.toml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/primitives/beefy/Cargo.toml b/primitives/beefy/Cargo.toml index e4154071d..5dfda3e37 100644 --- a/primitives/beefy/Cargo.toml +++ b/primitives/beefy/Cargo.toml @@ -22,7 +22,7 @@ sp-io = { version = "7.0.0", default-features = false, path = "../io" } sp-mmr-primitives = { version = "4.0.0-dev", default-features = false, path = "../merkle-mountain-range" } sp-runtime = { version = "7.0.0", default-features = false, path = "../runtime" } sp-std = { version = "5.0.0", default-features = false, path = "../std" } -strum = { version = "0.24.1", features = ["derive"] } +strum = { version = "0.24.1", features = ["derive"], default-features = false } lazy_static = "1.4.0" [dev-dependencies] diff --git a/primitives/keyring/Cargo.toml b/primitives/keyring/Cargo.toml index 0646dde4f..db3a8de2b 100644 --- a/primitives/keyring/Cargo.toml +++ b/primitives/keyring/Cargo.toml @@ -15,6 +15,6 @@ targets = ["x86_64-unknown-linux-gnu"] [dependencies] lazy_static = "1.4.0" -strum = { version = "0.24.1", features = ["derive"] } +strum = { version = "0.24.1", features = ["derive"], default-features = false } sp-core = { version = "7.0.0", path = "../core" } sp-runtime = { version = "7.0.0", path = "../runtime" } From 38c3e5ee10be760115a77a115ef0ac6ba381542b Mon Sep 17 00:00:00 2001 From: Arkadiy Paronyan Date: Tue, 7 Feb 2023 19:15:01 +0100 Subject: [PATCH 096/558] Fix block pruning (#13323) --- client/db/src/lib.rs | 84 +++++++++++++++++++++++++++++++++----------- 1 file changed, 64 insertions(+), 20 deletions(-) diff --git a/client/db/src/lib.rs b/client/db/src/lib.rs index f217eb648..f54f1bdb1 100644 --- a/client/db/src/lib.rs +++ b/client/db/src/lib.rs @@ -1806,7 +1806,13 @@ impl Backend { } let new_displaced = self.blockchain.leaves.write().finalize_height(f_num); - self.prune_blocks(transaction, f_num, &new_displaced, current_transaction_justifications)?; + self.prune_blocks( + transaction, + f_num, + f_hash, + &new_displaced, + current_transaction_justifications, + )?; Ok(()) } @@ -1815,6 +1821,7 @@ impl Backend { &self, transaction: &mut Transaction, finalized_number: NumberFor, + finalized_hash: Block::Hash, displaced: &FinalizationOutcome>, current_transaction_justifications: &mut HashMap, ) -> ClientResult<()> { @@ -1843,10 +1850,10 @@ impl Backend { self.prune_block(transaction, BlockId::::number(number))?; } - self.prune_displaced_branches(transaction, finalized_number, displaced)?; + self.prune_displaced_branches(transaction, finalized_hash, displaced)?; }, BlocksPruning::KeepFinalized => { - self.prune_displaced_branches(transaction, finalized_number, displaced)?; + self.prune_displaced_branches(transaction, finalized_hash, displaced)?; }, } Ok(()) @@ -1855,28 +1862,21 @@ impl Backend { fn prune_displaced_branches( &self, transaction: &mut Transaction, - finalized: NumberFor, + finalized: Block::Hash, displaced: &FinalizationOutcome>, ) -> ClientResult<()> { // Discard all blocks from displaced branches for h in displaced.leaves() { - let mut number = finalized; - let mut hash = *h; - // Follow displaced chains back until we reach a finalized block. - // Since leaves are discarded due to finality, they can't have parents - // that are canonical, but not yet finalized. So we stop deleting as soon as - // we reach canonical chain. - while self.blockchain.hash(number)? != Some(hash) { - match self.blockchain.header(hash)? { - Some(header) => { - self.blockchain.insert_persisted_body_if_pinned(hash)?; - - self.prune_block(transaction, BlockId::::hash(hash))?; - number = header.number().saturating_sub(One::one()); - hash = *header.parent_hash(); + match sp_blockchain::tree_route(&self.blockchain, *h, finalized) { + Ok(tree_route) => + for r in tree_route.retracted() { + self.blockchain.insert_persisted_body_if_pinned(r.hash)?; + self.prune_block(transaction, BlockId::::hash(r.hash))?; }, - None => break, - } + Err(sp_blockchain::Error::UnknownBlock(_)) => { + // Sometimes routes can't be calculated. E.g. after warp sync. + }, + Err(e) => Err(e)?, } } Ok(()) @@ -3569,6 +3569,50 @@ pub(crate) mod tests { assert_eq!(Some(vec![4.into()]), bc.body(blocks[4]).unwrap()); } + #[test] + fn prune_blocks_on_finalize_and_reorg() { + // 0 - 1b + // \ - 1a - 2a - 3a + // \ - 2b + + let backend = Backend::::new_test_with_tx_storage(BlocksPruning::Some(10), 10); + + let make_block = |index, parent, val: u64| { + insert_block(&backend, index, parent, None, H256::random(), vec![val.into()], None) + .unwrap() + }; + + let block_0 = make_block(0, Default::default(), 0x00); + let block_1a = make_block(1, block_0, 0x1a); + let block_1b = make_block(1, block_0, 0x1b); + let block_2a = make_block(2, block_1a, 0x2a); + let block_2b = make_block(2, block_1a, 0x2b); + let block_3a = make_block(3, block_2a, 0x3a); + + // Make sure 1b is head + let mut op = backend.begin_operation().unwrap(); + backend.begin_state_operation(&mut op, block_0).unwrap(); + op.mark_head(block_1b).unwrap(); + backend.commit_operation(op).unwrap(); + + // Finalize 3a + let mut op = backend.begin_operation().unwrap(); + backend.begin_state_operation(&mut op, block_0).unwrap(); + op.mark_head(block_3a).unwrap(); + op.mark_finalized(block_1a, None).unwrap(); + op.mark_finalized(block_2a, None).unwrap(); + op.mark_finalized(block_3a, None).unwrap(); + backend.commit_operation(op).unwrap(); + + let bc = backend.blockchain(); + assert_eq!(None, bc.body(block_1b).unwrap()); + assert_eq!(None, bc.body(block_2b).unwrap()); + assert_eq!(Some(vec![0x00.into()]), bc.body(block_0).unwrap()); + assert_eq!(Some(vec![0x1a.into()]), bc.body(block_1a).unwrap()); + assert_eq!(Some(vec![0x2a.into()]), bc.body(block_2a).unwrap()); + assert_eq!(Some(vec![0x3a.into()]), bc.body(block_3a).unwrap()); + } + #[test] fn indexed_data_block_body() { let backend = Backend::::new_test_with_tx_storage(BlocksPruning::Some(1), 10); From 2f1644860b318e23aa893bdd2955bb6bd6ec8d04 Mon Sep 17 00:00:00 2001 From: Muharem Ismailov Date: Wed, 8 Feb 2023 09:33:34 +0100 Subject: [PATCH 097/558] Referendum proposal's metadata (#12568) * referenda metadata * todo comment * remove TODO, update rustdocs * referenda clear_metadata origin signed or root * referenda metadata unit tests * drop schema type for referenda metadata * remove metadata type * referenda metadata benches * note different preimages * metadata for democracy pallet * metadata democracy pallet tests and benches * fix cargo clippy * update docs * ".git/.scripts/bench-bot.sh" pallet dev pallet_democracy * ".git/.scripts/bench-bot.sh" pallet dev pallet_referenda * Update the doc frame/democracy/src/lib.rs Co-authored-by: Roman Useinov * Update the doc frame/democracy/src/lib.rs Co-authored-by: Anthony Alaribe * reference instead clone for take Co-authored-by: Anthony Alaribe * error rename BadMetadata to PreimageNotExist * clear metadata within internal_cancel_referendum fn * remove redundant clone * collapse metadata api into one set_metadata method * fmt * review fixes * not request preimage on set_metadata * rename events and update docs * ".git/.scripts/commands/bench/bench.sh" pallet dev pallet_democracy * ".git/.scripts/commands/bench/bench.sh" pallet dev pallet_referenda * rename reset_metadata to transfer_metadata --------- Co-authored-by: command-bot <> Co-authored-by: Roman Useinov Co-authored-by: Anthony Alaribe --- frame/democracy/src/benchmarking.rs | 203 +++++- frame/democracy/src/lib.rs | 176 +++++- frame/democracy/src/tests.rs | 15 +- frame/democracy/src/tests/fast_tracking.rs | 11 + frame/democracy/src/tests/metadata.rs | 209 +++++++ frame/democracy/src/tests/public_proposals.rs | 14 +- frame/democracy/src/types.rs | 17 + frame/democracy/src/weights.rs | 581 ++++++++++++------ frame/referenda/src/benchmarking.rs | 26 +- frame/referenda/src/lib.rs | 72 ++- frame/referenda/src/mock.rs | 14 +- frame/referenda/src/tests.rs | 72 +++ frame/referenda/src/weights.rs | 307 +++++---- 13 files changed, 1367 insertions(+), 350 deletions(-) create mode 100644 frame/democracy/src/tests/metadata.rs diff --git a/frame/democracy/src/benchmarking.rs b/frame/democracy/src/benchmarking.rs index f2d45b719..f376b733a 100644 --- a/frame/democracy/src/benchmarking.rs +++ b/frame/democracy/src/benchmarking.rs @@ -54,19 +54,20 @@ fn add_proposal(n: u32) -> Result { Ok(proposal.hash()) } -fn add_referendum(n: u32) -> (ReferendumIndex, H256) { +// add a referendum with a metadata. +fn add_referendum(n: u32) -> (ReferendumIndex, H256, PreimageHash) { let vote_threshold = VoteThreshold::SimpleMajority; let proposal = make_proposal::(n); let hash = proposal.hash(); - ( - Democracy::::inject_referendum( - T::LaunchPeriod::get(), - proposal, - vote_threshold, - 0u32.into(), - ), - hash, - ) + let index = Democracy::::inject_referendum( + T::LaunchPeriod::get(), + proposal, + vote_threshold, + 0u32.into(), + ); + let preimage_hash = note_preimage::(); + MetadataOf::::insert(crate::MetadataOwner::Referendum(index), preimage_hash.clone()); + (index, hash, preimage_hash) } fn account_vote(b: BalanceOf) -> AccountVote> { @@ -75,6 +76,25 @@ fn account_vote(b: BalanceOf) -> AccountVote> { AccountVote::Standard { vote: v, balance: b } } +fn assert_last_event(generic_event: ::RuntimeEvent) { + frame_system::Pallet::::assert_last_event(generic_event.into()); +} + +fn assert_has_event(generic_event: ::RuntimeEvent) { + frame_system::Pallet::::assert_has_event(generic_event.into()); +} + +// note a new preimage. +fn note_preimage() -> PreimageHash { + use core::sync::atomic::{AtomicU8, Ordering}; + use sp_std::borrow::Cow; + // note a new preimage on every function invoke. + static COUNTER: AtomicU8 = AtomicU8::new(0); + let data = Cow::from(vec![COUNTER.fetch_add(1, Ordering::Relaxed)]); + let hash = ::Preimages::note(data).unwrap(); + hash +} + benchmarks! { propose { let p = T::MaxProposals::get(); @@ -179,7 +199,7 @@ benchmarks! { emergency_cancel { let origin = T::CancellationOrigin::try_successful_origin().map_err(|_| BenchmarkError::Weightless)?; - let ref_index = add_referendum::(0).0; + let (ref_index, _, preimage_hash) = add_referendum::(0); assert_ok!(Democracy::::referendum_status(ref_index)); }: _(origin, ref_index) verify { @@ -188,6 +208,10 @@ benchmarks! { Democracy::::referendum_status(ref_index), Error::::ReferendumInvalid, ); + assert_last_event::(crate::Event::MetadataCleared { + owner: MetadataOwner::Referendum(ref_index), + hash: preimage_hash, + }.into()); } blacklist { @@ -198,7 +222,7 @@ benchmarks! { // We should really add a lot of seconds here, but we're not doing it elsewhere. // Add a referendum of our proposal. - let (ref_index, hash) = add_referendum::(0); + let (ref_index, hash, preimage_hash) = add_referendum::(0); assert_ok!(Democracy::::referendum_status(ref_index)); // Place our proposal in the external queue, too. assert_ok!(Democracy::::external_propose( @@ -215,6 +239,10 @@ benchmarks! { Democracy::::referendum_status(ref_index), Error::::ReferendumInvalid ); + assert_has_event::(crate::Event::MetadataCleared { + owner: MetadataOwner::Referendum(ref_index), + hash: preimage_hash, + }.into()); } // Worst case scenario, we external propose a previously blacklisted proposal @@ -262,8 +290,13 @@ benchmarks! { .expect("ExternalDefaultOrigin has no successful origin required for the benchmark"); let proposal = make_proposal::(0); let proposal_hash = proposal.hash(); - Democracy::::external_propose_default(origin_propose, proposal)?; - + Democracy::::external_propose_default(origin_propose.clone(), proposal)?; + // Set metadata to the external proposal. + let preimage_hash = note_preimage::(); + assert_ok!(Democracy::::set_metadata( + origin_propose, + MetadataOwner::External, + Some(preimage_hash))); // NOTE: Instant origin may invoke a little bit more logic, but may not always succeed. let origin_fast_track = T::FastTrackOrigin::try_successful_origin().map_err(|_| BenchmarkError::Weightless)?; @@ -271,7 +304,12 @@ benchmarks! { let delay = 0u32; }: _(origin_fast_track, proposal_hash, voting_period, delay.into()) verify { - assert_eq!(Democracy::::referendum_count(), 1, "referendum not created") + assert_eq!(Democracy::::referendum_count(), 1, "referendum not created"); + assert_last_event::(crate::Event::MetadataTransferred { + prev_owner: MetadataOwner::External, + owner: MetadataOwner::Referendum(0), + hash: preimage_hash, + }.into()); } veto_external { @@ -280,7 +318,14 @@ benchmarks! { let origin_propose = T::ExternalDefaultOrigin::try_successful_origin() .expect("ExternalDefaultOrigin has no successful origin required for the benchmark"); - Democracy::::external_propose_default(origin_propose, proposal)?; + Democracy::::external_propose_default(origin_propose.clone(), proposal)?; + + let preimage_hash = note_preimage::(); + assert_ok!(Democracy::::set_metadata( + origin_propose, + MetadataOwner::External, + Some(preimage_hash)) + ); let mut vetoers: BoundedVec = Default::default(); for i in 0 .. (T::MaxBlacklisted::get() - 1) { @@ -303,13 +348,32 @@ benchmarks! { for i in 0 .. T::MaxProposals::get() { add_proposal::(i)?; } + // Add metadata to the first proposal. + let proposer = funded_account::("proposer", 0); + let preimage_hash = note_preimage::(); + assert_ok!(Democracy::::set_metadata( + RawOrigin::Signed(proposer).into(), + MetadataOwner::Proposal(0), + Some(preimage_hash))); let cancel_origin = T::CancelProposalOrigin::try_successful_origin() .map_err(|_| BenchmarkError::Weightless)?; }: _(cancel_origin, 0) + verify { + assert_last_event::(crate::Event::MetadataCleared { + owner: MetadataOwner::Proposal(0), + hash: preimage_hash, + }.into()); + } cancel_referendum { - let ref_index = add_referendum::(0).0; + let (ref_index, _, preimage_hash) = add_referendum::(0); }: _(RawOrigin::Root, ref_index) + verify { + assert_last_event::(crate::Event::MetadataCleared { + owner: MetadataOwner::Referendum(0), + hash: preimage_hash, + }.into()); + } #[extra] on_initialize_external { @@ -678,6 +742,111 @@ benchmarks! { assert_eq!(votes.len(), (r - 1) as usize, "Vote was not removed"); } + set_external_metadata { + let origin = T::ExternalOrigin::try_successful_origin() + .expect("ExternalOrigin has no successful origin required for the benchmark"); + assert_ok!( + Democracy::::external_propose(origin.clone(), make_proposal::(0)) + ); + let owner = MetadataOwner::External; + let hash = note_preimage::(); + }: set_metadata(origin, owner.clone(), Some(hash)) + verify { + assert_last_event::(crate::Event::MetadataSet { + owner, + hash, + }.into()); + } + + clear_external_metadata { + let origin = T::ExternalOrigin::try_successful_origin() + .expect("ExternalOrigin has no successful origin required for the benchmark"); + assert_ok!( + Democracy::::external_propose(origin.clone(), make_proposal::(0)) + ); + let owner = MetadataOwner::External; + let proposer = funded_account::("proposer", 0); + let hash = note_preimage::(); + assert_ok!(Democracy::::set_metadata(origin.clone(), owner.clone(), Some(hash))); + }: set_metadata(origin, owner.clone(), None) + verify { + assert_last_event::(crate::Event::MetadataCleared { + owner, + hash, + }.into()); + } + + set_proposal_metadata { + // Place our proposal at the end to make sure it's worst case. + for i in 0 .. T::MaxProposals::get() { + add_proposal::(i)?; + } + let owner = MetadataOwner::Proposal(0); + let proposer = funded_account::("proposer", 0); + let hash = note_preimage::(); + }: set_metadata(RawOrigin::Signed(proposer).into(), owner.clone(), Some(hash)) + verify { + assert_last_event::(crate::Event::MetadataSet { + owner, + hash, + }.into()); + } + + clear_proposal_metadata { + // Place our proposal at the end to make sure it's worst case. + for i in 0 .. T::MaxProposals::get() { + add_proposal::(i)?; + } + let proposer = funded_account::("proposer", 0); + let owner = MetadataOwner::Proposal(0); + let hash = note_preimage::(); + assert_ok!(Democracy::::set_metadata( + RawOrigin::Signed(proposer.clone()).into(), + owner.clone(), + Some(hash))); + }: set_metadata(RawOrigin::Signed(proposer).into(), owner.clone(), None) + verify { + assert_last_event::(crate::Event::MetadataCleared { + owner, + hash, + }.into()); + } + + set_referendum_metadata { + // create not ongoing referendum. + ReferendumInfoOf::::insert( + 0, + ReferendumInfo::Finished { end: T::BlockNumber::zero(), approved: true }, + ); + let owner = MetadataOwner::Referendum(0); + let caller = funded_account::("caller", 0); + let hash = note_preimage::(); + }: set_metadata(RawOrigin::Root.into(), owner.clone(), Some(hash)) + verify { + assert_last_event::(crate::Event::MetadataSet { + owner, + hash, + }.into()); + } + + clear_referendum_metadata { + // create not ongoing referendum. + ReferendumInfoOf::::insert( + 0, + ReferendumInfo::Finished { end: T::BlockNumber::zero(), approved: true }, + ); + let owner = MetadataOwner::Referendum(0); + let hash = note_preimage::(); + MetadataOf::::insert(owner.clone(), hash); + let caller = funded_account::("caller", 0); + }: set_metadata(RawOrigin::Signed(caller).into(), owner.clone(), None) + verify { + assert_last_event::(crate::Event::MetadataCleared { + owner, + hash, + }.into()); + } + impl_benchmark_test_suite!( Democracy, crate::tests::new_test_ext(), diff --git a/frame/democracy/src/lib.rs b/frame/democracy/src/lib.rs index 2c65e5d94..3aa8bc001 100644 --- a/frame/democracy/src/lib.rs +++ b/frame/democracy/src/lib.rs @@ -155,14 +155,17 @@ use codec::{Decode, Encode}; use frame_support::{ ensure, + error::BadOrigin, traits::{ defensive_prelude::*, schedule::{v3::Named as ScheduleNamed, DispatchTime}, - Bounded, Currency, Get, LockIdentifier, LockableCurrency, OnUnbalanced, QueryPreimage, - ReservableCurrency, StorePreimage, WithdrawReasons, + Bounded, Currency, EnsureOrigin, Get, Hash as PreimageHash, LockIdentifier, + LockableCurrency, OnUnbalanced, QueryPreimage, ReservableCurrency, StorePreimage, + WithdrawReasons, }, weights::Weight, }; +use frame_system::pallet_prelude::OriginFor; use sp_runtime::{ traits::{Bounded as ArithBounded, One, Saturating, StaticLookup, Zero}, ArithmeticError, DispatchError, DispatchResult, @@ -176,7 +179,10 @@ mod vote_threshold; pub mod weights; pub use conviction::Conviction; pub use pallet::*; -pub use types::{Delegations, ReferendumInfo, ReferendumStatus, Tally, UnvoteScope}; +pub use types::{ + Delegations, MetadataOwner, PropIndex, ReferendumIndex, ReferendumInfo, ReferendumStatus, + Tally, UnvoteScope, +}; pub use vote::{AccountVote, Vote, Voting}; pub use vote_threshold::{Approved, VoteThreshold}; pub use weights::WeightInfo; @@ -191,12 +197,6 @@ pub mod migrations; const DEMOCRACY_ID: LockIdentifier = *b"democrac"; -/// A proposal index. -pub type PropIndex = u32; - -/// A referendum index. -pub type ReferendumIndex = u32; - type BalanceOf = <::Currency as Currency<::AccountId>>::Balance; type NegativeImbalanceOf = <::Currency as Currency< @@ -425,6 +425,15 @@ pub mod pallet { #[pallet::storage] pub type Cancellations = StorageMap<_, Identity, H256, bool, ValueQuery>; + /// General information concerning any proposal or referendum. + /// The `PreimageHash` refers to the preimage of the `Preimages` provider which can be a JSON + /// dump or IPFS hash of a JSON file. + /// + /// Consider a garbage collection for a metadata of finished referendums to `unrequest` (remove) + /// large preimages. + #[pallet::storage] + pub type MetadataOf = StorageMap<_, Blake2_128Concat, MetadataOwner, PreimageHash>; + #[pallet::genesis_config] pub struct GenesisConfig { _phantom: sp_std::marker::PhantomData, @@ -477,6 +486,29 @@ pub mod pallet { Seconded { seconder: T::AccountId, prop_index: PropIndex }, /// A proposal got canceled. ProposalCanceled { prop_index: PropIndex }, + /// Metadata for a proposal or a referendum has been set. + MetadataSet { + /// Metadata owner. + owner: MetadataOwner, + /// Preimage hash. + hash: PreimageHash, + }, + /// Metadata for a proposal or a referendum has been cleared. + MetadataCleared { + /// Metadata owner. + owner: MetadataOwner, + /// Preimage hash. + hash: PreimageHash, + }, + /// Metadata has been transferred to new owner. + MetadataTransferred { + /// Previous metadata owner. + prev_owner: MetadataOwner, + /// New metadata owner. + owner: MetadataOwner, + /// Preimage hash. + hash: PreimageHash, + }, } #[pallet::error] @@ -528,6 +560,8 @@ pub mod pallet { TooMany, /// Voting period too low VotingPeriodLow, + /// The preimage does not exist. + PreimageNotExist, } #[pallet::hooks] @@ -773,12 +807,13 @@ pub mod pallet { >::kill(); let now = >::block_number(); - Self::inject_referendum( + let ref_index = Self::inject_referendum( now.saturating_add(voting_period), ext_proposal, threshold, delay, ); + Self::transfer_metadata(MetadataOwner::External, MetadataOwner::Referendum(ref_index)); Ok(()) } @@ -816,6 +851,7 @@ pub mod pallet { Self::deposit_event(Event::::Vetoed { who, proposal_hash, until }); >::kill(); + Self::clear_metadata(MetadataOwner::External); Ok(()) } @@ -1025,12 +1061,14 @@ pub mod pallet { T::Slash::on_unbalanced(T::Currency::slash_reserved(&who, amount).0); } } + Self::clear_metadata(MetadataOwner::Proposal(prop_index)); } }); // Remove the external queued referendum, if it's there. if matches!(NextExternal::::get(), Some((p, ..)) if p.hash() == proposal_hash) { NextExternal::::kill(); + Self::clear_metadata(MetadataOwner::External); } // Remove the referendum, if it's there. @@ -1067,8 +1105,68 @@ pub mod pallet { T::Slash::on_unbalanced(T::Currency::slash_reserved(&who, amount).0); } } - Self::deposit_event(Event::::ProposalCanceled { prop_index }); + Self::clear_metadata(MetadataOwner::Proposal(prop_index)); + Ok(()) + } + + /// Set or clear a metadata of a proposal or a referendum. + /// + /// Parameters: + /// - `origin`: Must correspond to the `MetadataOwner`. + /// - `ExternalOrigin` for an external proposal with the `SuperMajorityApprove` + /// threshold. + /// - `ExternalDefaultOrigin` for an external proposal with the `SuperMajorityAgainst` + /// threshold. + /// - `ExternalMajorityOrigin` for an external proposal with the `SimpleMajority` + /// threshold. + /// - `Signed` by a creator for a public proposal. + /// - `Signed` to clear a metadata for a finished referendum. + /// - `Root` to set a metadata for an ongoing referendum. + /// - `owner`: an identifier of a metadata owner. + /// - `maybe_hash`: The hash of an on-chain stored preimage. `None` to clear a metadata. + #[pallet::call_index(18)] + #[pallet::weight( + match (owner, maybe_hash) { + (MetadataOwner::External, Some(_)) => T::WeightInfo::set_external_metadata(), + (MetadataOwner::External, None) => T::WeightInfo::clear_external_metadata(), + (MetadataOwner::Proposal(_), Some(_)) => T::WeightInfo::set_proposal_metadata(), + (MetadataOwner::Proposal(_), None) => T::WeightInfo::clear_proposal_metadata(), + (MetadataOwner::Referendum(_), Some(_)) => T::WeightInfo::set_referendum_metadata(), + (MetadataOwner::Referendum(_), None) => T::WeightInfo::clear_referendum_metadata(), + } + )] + pub fn set_metadata( + origin: OriginFor, + owner: MetadataOwner, + maybe_hash: Option, + ) -> DispatchResult { + match owner { + MetadataOwner::External => { + let (_, threshold) = >::get().ok_or(Error::::NoProposal)?; + Self::ensure_external_origin(threshold, origin)?; + }, + MetadataOwner::Proposal(index) => { + let who = ensure_signed(origin)?; + let (_, _, proposer) = Self::proposal(index)?; + ensure!(proposer == who, Error::::NoPermission); + }, + MetadataOwner::Referendum(index) => { + let is_root = ensure_signed_or_root(origin)?.is_none(); + ensure!(is_root || maybe_hash.is_none(), Error::::NoPermission); + ensure!( + is_root || Self::referendum_status(index).is_err(), + Error::::NoPermission + ); + }, + } + if let Some(hash) = maybe_hash { + ensure!(T::Preimages::len(&hash).is_some(), Error::::PreimageNotExist); + MetadataOf::::insert(owner.clone(), hash); + Self::deposit_event(Event::::MetadataSet { owner, hash }); + } else { + Self::clear_metadata(owner); + } Ok(()) } } @@ -1146,6 +1244,7 @@ impl Pallet { pub fn internal_cancel_referendum(ref_index: ReferendumIndex) { Self::deposit_event(Event::::Cancelled { ref_index }); ReferendumInfoOf::::remove(ref_index); + Self::clear_metadata(MetadataOwner::Referendum(ref_index)); } // private. @@ -1432,12 +1531,13 @@ impl Pallet { if let Some((proposal, threshold)) = >::take() { LastTabledWasExternal::::put(true); Self::deposit_event(Event::::ExternalTabled); - Self::inject_referendum( + let ref_index = Self::inject_referendum( now.saturating_add(T::VotingPeriod::get()), proposal, threshold, T::EnactmentPeriod::get(), ); + Self::transfer_metadata(MetadataOwner::External, MetadataOwner::Referendum(ref_index)); Ok(()) } else { return Err(Error::::NoneWaiting.into()) @@ -1460,12 +1560,16 @@ impl Pallet { T::Currency::unreserve(d, deposit); } Self::deposit_event(Event::::Tabled { proposal_index: prop_index, deposit }); - Self::inject_referendum( + let ref_index = Self::inject_referendum( now.saturating_add(T::VotingPeriod::get()), proposal, VoteThreshold::SuperMajorityApprove, T::EnactmentPeriod::get(), ); + Self::transfer_metadata( + MetadataOwner::Proposal(prop_index), + MetadataOwner::Referendum(ref_index), + ) } Ok(()) } else { @@ -1578,6 +1682,52 @@ impl Pallet { // `Compact`. decode_compact_u32_at(&>::hashed_key_for(proposal)) } + + /// Return a proposal of an index. + fn proposal(index: PropIndex) -> Result<(PropIndex, BoundedCallOf, T::AccountId), Error> { + PublicProps::::get() + .into_iter() + .find(|(prop_index, _, _)| prop_index == &index) + .ok_or(Error::::ProposalMissing) + } + + /// Clear metadata if exist for a given owner. + fn clear_metadata(owner: MetadataOwner) { + if let Some(hash) = MetadataOf::::take(&owner) { + Self::deposit_event(Event::::MetadataCleared { owner, hash }); + } + } + + /// Transfer the metadata of an `owner` to a `new_owner`. + fn transfer_metadata(owner: MetadataOwner, new_owner: MetadataOwner) { + if let Some(hash) = MetadataOf::::take(&owner) { + MetadataOf::::insert(&new_owner, hash); + Self::deposit_event(Event::::MetadataTransferred { + prev_owner: owner, + owner: new_owner, + hash, + }); + } + } + + /// Ensure external origin for corresponding vote threshold. + fn ensure_external_origin( + threshold: VoteThreshold, + origin: OriginFor, + ) -> Result<(), BadOrigin> { + match threshold { + VoteThreshold::SuperMajorityApprove => { + let _ = T::ExternalOrigin::ensure_origin(origin)?; + }, + VoteThreshold::SuperMajorityAgainst => { + let _ = T::ExternalDefaultOrigin::ensure_origin(origin)?; + }, + VoteThreshold::SimpleMajority => { + let _ = T::ExternalMajorityOrigin::ensure_origin(origin)?; + }, + }; + Ok(()) + } } /// Decode `Compact` from the trie at given key. diff --git a/frame/democracy/src/tests.rs b/frame/democracy/src/tests.rs index 41b279035..9c7ec2906 100644 --- a/frame/democracy/src/tests.rs +++ b/frame/democracy/src/tests.rs @@ -32,7 +32,7 @@ use pallet_balances::{BalanceLock, Error as BalancesError}; use sp_core::H256; use sp_runtime::{ testing::Header, - traits::{BadOrigin, BlakeTwo256, IdentityLookup}, + traits::{BadOrigin, BlakeTwo256, Hash, IdentityLookup}, Perbill, }; mod cancellation; @@ -41,6 +41,7 @@ mod delegation; mod external_proposing; mod fast_tracking; mod lock_voting; +mod metadata; mod public_proposals; mod scheduling; mod voting; @@ -276,3 +277,15 @@ fn big_nay(who: u64) -> AccountVote { fn tally(r: ReferendumIndex) -> Tally { Democracy::referendum_status(r).unwrap().tally } + +/// note a new preimage without registering. +fn note_preimage(who: u64) -> PreimageHash { + use std::sync::atomic::{AtomicU8, Ordering}; + // note a new preimage on every function invoke. + static COUNTER: AtomicU8 = AtomicU8::new(0); + let data = vec![COUNTER.fetch_add(1, Ordering::Relaxed)]; + assert_ok!(Preimage::note_preimage(RuntimeOrigin::signed(who), data.clone())); + let hash = BlakeTwo256::hash(&data); + assert!(!Preimage::is_requested(&hash)); + hash +} diff --git a/frame/democracy/src/tests/fast_tracking.rs b/frame/democracy/src/tests/fast_tracking.rs index 97bb7a639..09af3dbf3 100644 --- a/frame/democracy/src/tests/fast_tracking.rs +++ b/frame/democracy/src/tests/fast_tracking.rs @@ -32,6 +32,14 @@ fn fast_track_referendum_works() { RuntimeOrigin::signed(3), set_balance_proposal(2) )); + let hash = note_preimage(1); + assert!(>::get(MetadataOwner::External).is_none()); + assert_ok!(Democracy::set_metadata( + RuntimeOrigin::signed(3), + MetadataOwner::External, + Some(hash), + ),); + assert!(>::get(MetadataOwner::External).is_some()); assert_noop!(Democracy::fast_track(RuntimeOrigin::signed(1), h, 3, 2), BadOrigin); assert_ok!(Democracy::fast_track(RuntimeOrigin::signed(5), h, 2, 0)); assert_eq!( @@ -44,6 +52,9 @@ fn fast_track_referendum_works() { tally: Tally { ayes: 0, nays: 0, turnout: 0 }, }) ); + // metadata reset from the external proposal to the referendum. + assert!(>::get(MetadataOwner::External).is_none()); + assert!(>::get(MetadataOwner::Referendum(0)).is_some()); }); } diff --git a/frame/democracy/src/tests/metadata.rs b/frame/democracy/src/tests/metadata.rs new file mode 100644 index 000000000..95b617e37 --- /dev/null +++ b/frame/democracy/src/tests/metadata.rs @@ -0,0 +1,209 @@ +// This file is part of Substrate. + +// Copyright (C) 2017-2022 Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +//! The tests for functionality concerning the metadata. + +use super::*; + +#[test] +fn set_external_metadata_works() { + new_test_ext().execute_with(|| { + use frame_support::traits::Hash as PreimageHash; + // invalid preimage hash. + let invalid_hash: PreimageHash = [1u8; 32].into(); + // metadata owner is an external proposal. + let owner = MetadataOwner::External; + // fails to set metadata if an external proposal does not exist. + assert_noop!( + Democracy::set_metadata(RuntimeOrigin::signed(2), owner.clone(), Some(invalid_hash)), + Error::::NoProposal, + ); + // create an external proposal. + assert_ok!(Democracy::external_propose(RuntimeOrigin::signed(2), set_balance_proposal(2),)); + assert!(>::exists()); + // fails to set metadata with non external origin. + assert_noop!( + Democracy::set_metadata(RuntimeOrigin::signed(1), owner.clone(), Some(invalid_hash)), + BadOrigin, + ); + // fails to set non-existing preimage. + assert_noop!( + Democracy::set_metadata(RuntimeOrigin::signed(2), owner.clone(), Some(invalid_hash)), + Error::::PreimageNotExist, + ); + // set metadata successful. + let hash = note_preimage(1); + assert_ok!(Democracy::set_metadata(RuntimeOrigin::signed(2), owner.clone(), Some(hash),),); + System::assert_last_event(RuntimeEvent::Democracy(crate::Event::MetadataSet { + owner, + hash, + })); + }); +} + +#[test] +fn clear_metadata_works() { + new_test_ext().execute_with(|| { + // metadata owner is an external proposal. + let owner = MetadataOwner::External; + // create an external proposal. + assert_ok!(Democracy::external_propose(RuntimeOrigin::signed(2), set_balance_proposal(2),)); + assert!(>::exists()); + // set metadata. + let hash = note_preimage(1); + assert_ok!(Democracy::set_metadata(RuntimeOrigin::signed(2), owner.clone(), Some(hash),)); + // fails to clear metadata with a wrong origin. + assert_noop!( + Democracy::set_metadata(RuntimeOrigin::signed(1), owner.clone(), None), + BadOrigin, + ); + // clear metadata successful. + assert_ok!(Democracy::set_metadata(RuntimeOrigin::signed(2), owner.clone(), None)); + System::assert_last_event(RuntimeEvent::Democracy(crate::Event::MetadataCleared { + owner, + hash, + })); + }); +} + +#[test] +fn set_proposal_metadata_works() { + new_test_ext().execute_with(|| { + use frame_support::traits::Hash as PreimageHash; + // invalid preimage hash. + let invalid_hash: PreimageHash = [1u8; 32].into(); + // create an external proposal. + assert_ok!(propose_set_balance(1, 2, 5)); + // metadata owner is a public proposal. + let owner = MetadataOwner::Proposal(Democracy::public_prop_count() - 1); + // fails to set non-existing preimage. + assert_noop!( + Democracy::set_metadata(RuntimeOrigin::signed(1), owner.clone(), Some(invalid_hash),), + Error::::PreimageNotExist, + ); + // note preimage. + let hash = note_preimage(1); + // fails to set a preimage if an origin is not a proposer. + assert_noop!( + Democracy::set_metadata(RuntimeOrigin::signed(3), owner.clone(), Some(hash),), + Error::::NoPermission, + ); + // set metadata successful. + assert_ok!(Democracy::set_metadata(RuntimeOrigin::signed(1), owner.clone(), Some(hash),),); + System::assert_last_event(RuntimeEvent::Democracy(crate::Event::MetadataSet { + owner, + hash, + })); + }); +} + +#[test] +fn clear_proposal_metadata_works() { + new_test_ext().execute_with(|| { + // create an external proposal. + assert_ok!(propose_set_balance(1, 2, 5)); + // metadata owner is a public proposal. + let owner = MetadataOwner::Proposal(Democracy::public_prop_count() - 1); + // set metadata. + let hash = note_preimage(1); + assert_ok!(Democracy::set_metadata(RuntimeOrigin::signed(1), owner.clone(), Some(hash),)); + // fails to clear metadata with a wrong origin. + assert_noop!( + Democracy::set_metadata(RuntimeOrigin::signed(3), owner.clone(), None), + Error::::NoPermission, + ); + // clear metadata successful. + assert_ok!(Democracy::set_metadata(RuntimeOrigin::signed(1), owner.clone(), None)); + System::assert_last_event(RuntimeEvent::Democracy(crate::Event::MetadataCleared { + owner, + hash, + })); + }); +} + +#[test] +fn set_referendum_metadata_by_root() { + new_test_ext().execute_with(|| { + let index = Democracy::inject_referendum( + 2, + set_balance_proposal(2), + VoteThreshold::SuperMajorityApprove, + 0, + ); + // metadata owner is a referendum. + let owner = MetadataOwner::Referendum(index); + // note preimage. + let hash = note_preimage(1); + // fails to set if not a root. + assert_noop!( + Democracy::set_metadata(RuntimeOrigin::signed(3), owner.clone(), Some(hash),), + Error::::NoPermission, + ); + // fails to clear if not a root. + assert_noop!( + Democracy::set_metadata(RuntimeOrigin::signed(3), owner.clone(), None,), + Error::::NoPermission, + ); + // succeed to set metadata by a root for an ongoing referendum. + assert_ok!(Democracy::set_metadata(RuntimeOrigin::root(), owner.clone(), Some(hash),)); + System::assert_last_event(RuntimeEvent::Democracy(crate::Event::MetadataSet { + owner: owner.clone(), + hash, + })); + // succeed to clear metadata by a root for an ongoing referendum. + assert_ok!(Democracy::set_metadata(RuntimeOrigin::root(), owner.clone(), None)); + System::assert_last_event(RuntimeEvent::Democracy(crate::Event::MetadataCleared { + owner, + hash, + })); + }); +} + +#[test] +fn clear_referendum_metadata_works() { + new_test_ext().execute_with(|| { + // create a referendum. + let index = Democracy::inject_referendum( + 2, + set_balance_proposal(2), + VoteThreshold::SuperMajorityApprove, + 0, + ); + // metadata owner is a referendum. + let owner = MetadataOwner::Referendum(index); + // set metadata. + let hash = note_preimage(1); + // referendum finished. + MetadataOf::::insert(owner.clone(), hash); + // no permission to clear metadata of an ongoing referendum. + assert_noop!( + Democracy::set_metadata(RuntimeOrigin::signed(1), owner.clone(), None), + Error::::NoPermission, + ); + // referendum finished. + ReferendumInfoOf::::insert( + index, + ReferendumInfo::Finished { end: 1, approved: true }, + ); + // clear metadata successful. + assert_ok!(Democracy::set_metadata(RuntimeOrigin::signed(1), owner.clone(), None)); + System::assert_last_event(RuntimeEvent::Democracy(crate::Event::MetadataCleared { + owner, + hash, + })); + }); +} diff --git a/frame/democracy/src/tests/public_proposals.rs b/frame/democracy/src/tests/public_proposals.rs index f48824dc9..f9471442c 100644 --- a/frame/democracy/src/tests/public_proposals.rs +++ b/frame/democracy/src/tests/public_proposals.rs @@ -91,8 +91,20 @@ fn cancel_proposal_should_work() { assert_ok!(propose_set_balance(1, 2, 2)); assert_ok!(propose_set_balance(1, 4, 4)); assert_noop!(Democracy::cancel_proposal(RuntimeOrigin::signed(1), 0), BadOrigin); + let hash = note_preimage(1); + assert_ok!(Democracy::set_metadata( + RuntimeOrigin::signed(1), + MetadataOwner::Proposal(0), + Some(hash) + )); + assert!(>::get(MetadataOwner::Proposal(0)).is_some()); assert_ok!(Democracy::cancel_proposal(RuntimeOrigin::root(), 0)); - System::assert_last_event(crate::Event::ProposalCanceled { prop_index: 0 }.into()); + // metadata cleared, preimage unrequested. + assert!(>::get(MetadataOwner::Proposal(0)).is_none()); + System::assert_has_event(crate::Event::ProposalCanceled { prop_index: 0 }.into()); + System::assert_last_event( + crate::Event::MetadataCleared { owner: MetadataOwner::Proposal(0), hash }.into(), + ); assert_eq!(Democracy::backing_for(0), None); assert_eq!(Democracy::backing_for(1), Some(4)); }); diff --git a/frame/democracy/src/types.rs b/frame/democracy/src/types.rs index 4b7f1a0fa..25954e054 100644 --- a/frame/democracy/src/types.rs +++ b/frame/democracy/src/types.rs @@ -25,6 +25,12 @@ use sp_runtime::{ RuntimeDebug, }; +/// A proposal index. +pub type PropIndex = u32; + +/// A referendum index. +pub type ReferendumIndex = u32; + /// Info regarding an ongoing referendum. #[derive(Encode, MaxEncodedLen, Decode, Default, Clone, PartialEq, Eq, RuntimeDebug, TypeInfo)] pub struct Tally { @@ -206,3 +212,14 @@ pub enum UnvoteScope { /// Permitted to do only the changes that do not need the owner's permission. OnlyExpired, } + +/// Identifies an owner of a metadata. +#[derive(Encode, Decode, Clone, PartialEq, Eq, RuntimeDebug, TypeInfo, MaxEncodedLen)] +pub enum MetadataOwner { + /// External proposal. + External, + /// Public proposal of the index. + Proposal(PropIndex), + /// Referendum of the index. + Referendum(ReferendumIndex), +} diff --git a/frame/democracy/src/weights.rs b/frame/democracy/src/weights.rs index 985fe8fbd..10d0744ea 100644 --- a/frame/democracy/src/weights.rs +++ b/frame/democracy/src/weights.rs @@ -18,25 +18,26 @@ //! Autogenerated weights for pallet_democracy //! //! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev -//! DATE: 2023-01-24, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! DATE: 2023-01-27, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` //! WORST CASE MAP SIZE: `1000000` -//! HOSTNAME: `bm2`, CPU: `Intel(R) Core(TM) i7-7700K CPU @ 4.20GHz` +//! HOSTNAME: `runner-b3zmxxc-project-145-concurrent-0`, CPU: `Intel(R) Xeon(R) CPU @ 2.60GHz` //! EXECUTION: Some(Wasm), WASM-EXECUTION: Compiled, CHAIN: Some("dev"), DB CACHE: 1024 // Executed Command: -// ./target/production/substrate +// target/production/substrate // benchmark // pallet -// --chain=dev // --steps=50 // --repeat=20 -// --pallet=pallet_democracy // --extrinsic=* // --execution=wasm // --wasm-execution=compiled // --heap-pages=4096 -// --output=./frame/democracy/src/weights.rs +// --json-file=/builds/parity/mirrors/substrate/.git/.artifacts/bench.json +// --pallet=pallet_democracy +// --chain=dev // --header=./HEADER-APACHE2 +// --output=./frame/democracy/src/weights.rs // --template=./.maintain/frame-weight-template.hbs #![cfg_attr(rustfmt, rustfmt_skip)] @@ -70,6 +71,12 @@ pub trait WeightInfo { fn unlock_set(r: u32, ) -> Weight; fn remove_vote(r: u32, ) -> Weight; fn remove_other_vote(r: u32, ) -> Weight; + fn set_external_metadata() -> Weight; + fn clear_external_metadata() -> Weight; + fn set_proposal_metadata() -> Weight; + fn clear_proposal_metadata() -> Weight; + fn set_referendum_metadata() -> Weight; + fn clear_referendum_metadata() -> Weight; } /// Weights for pallet_democracy using the Substrate node and recommended hardware. @@ -87,8 +94,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `4864` // Estimated: `23409` - // Minimum execution time: 34_509 nanoseconds. - Weight::from_parts(34_781_000, 23409) + // Minimum execution time: 42_939 nanoseconds. + Weight::from_parts(43_543_000, 23409) .saturating_add(T::DbWeight::get().reads(3_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) } @@ -98,8 +105,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `3620` // Estimated: `5705` - // Minimum execution time: 31_151 nanoseconds. - Weight::from_parts(31_566_000, 5705) + // Minimum execution time: 36_475 nanoseconds. + Weight::from_parts(37_863_000, 5705) .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -111,10 +118,10 @@ impl WeightInfo for SubstrateWeight { /// Proof: Balances Locks (max_values: None, max_size: Some(1299), added: 3774, mode: MaxEncodedLen) fn vote_new() -> Weight { // Proof Size summary in bytes: - // Measured: `3555` + // Measured: `3565` // Estimated: `12720` - // Minimum execution time: 42_618 nanoseconds. - Weight::from_parts(43_231_000, 12720) + // Minimum execution time: 56_372 nanoseconds. + Weight::from_parts(57_483_000, 12720) .saturating_add(T::DbWeight::get().reads(3_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) } @@ -126,10 +133,10 @@ impl WeightInfo for SubstrateWeight { /// Proof: Balances Locks (max_values: None, max_size: Some(1299), added: 3774, mode: MaxEncodedLen) fn vote_existing() -> Weight { // Proof Size summary in bytes: - // Measured: `3577` + // Measured: `3587` // Estimated: `12720` - // Minimum execution time: 42_875 nanoseconds. - Weight::from_parts(43_338_000, 12720) + // Minimum execution time: 56_789 nanoseconds. + Weight::from_parts(57_737_000, 12720) .saturating_add(T::DbWeight::get().reads(3_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) } @@ -137,14 +144,16 @@ impl WeightInfo for SubstrateWeight { /// Proof: Democracy ReferendumInfoOf (max_values: None, max_size: Some(201), added: 2676, mode: MaxEncodedLen) /// Storage: Democracy Cancellations (r:1 w:1) /// Proof: Democracy Cancellations (max_values: None, max_size: Some(33), added: 2508, mode: MaxEncodedLen) + /// Storage: Democracy MetadataOf (r:1 w:1) + /// Proof: Democracy MetadataOf (max_values: None, max_size: Some(53), added: 2528, mode: MaxEncodedLen) fn emergency_cancel() -> Weight { // Proof Size summary in bytes: - // Measured: `320` - // Estimated: `5184` - // Minimum execution time: 16_543 nanoseconds. - Weight::from_parts(16_762_000, 5184) - .saturating_add(T::DbWeight::get().reads(2_u64)) - .saturating_add(T::DbWeight::get().writes(2_u64)) + // Measured: `398` + // Estimated: `7712` + // Minimum execution time: 24_379 nanoseconds. + Weight::from_parts(25_302_000, 7712) + .saturating_add(T::DbWeight::get().reads(3_u64)) + .saturating_add(T::DbWeight::get().writes(3_u64)) } /// Storage: Democracy PublicProps (r:1 w:1) /// Proof: Democracy PublicProps (max_values: Some(1), max_size: Some(16702), added: 17197, mode: MaxEncodedLen) @@ -152,6 +161,8 @@ impl WeightInfo for SubstrateWeight { /// Proof: Democracy DepositOf (max_values: None, max_size: Some(3230), added: 5705, mode: MaxEncodedLen) /// Storage: System Account (r:1 w:1) /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) + /// Storage: Democracy MetadataOf (r:3 w:1) + /// Proof: Democracy MetadataOf (max_values: None, max_size: Some(53), added: 2528, mode: MaxEncodedLen) /// Storage: Democracy NextExternal (r:1 w:1) /// Proof: Democracy NextExternal (max_values: Some(1), max_size: Some(132), added: 627, mode: MaxEncodedLen) /// Storage: Democracy ReferendumInfoOf (r:1 w:1) @@ -160,12 +171,12 @@ impl WeightInfo for SubstrateWeight { /// Proof: Democracy Blacklist (max_values: None, max_size: Some(3238), added: 5713, mode: MaxEncodedLen) fn blacklist() -> Weight { // Proof Size summary in bytes: - // Measured: `5958` - // Estimated: `28808` - // Minimum execution time: 70_135 nanoseconds. - Weight::from_parts(70_616_000, 28808) - .saturating_add(T::DbWeight::get().reads(5_u64)) - .saturating_add(T::DbWeight::get().writes(6_u64)) + // Measured: `6036` + // Estimated: `36392` + // Minimum execution time: 100_345 nanoseconds. + Weight::from_parts(102_233_000, 36392) + .saturating_add(T::DbWeight::get().reads(8_u64)) + .saturating_add(T::DbWeight::get().writes(7_u64)) } /// Storage: Democracy NextExternal (r:1 w:1) /// Proof: Democracy NextExternal (max_values: Some(1), max_size: Some(132), added: 627, mode: MaxEncodedLen) @@ -175,8 +186,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `3448` // Estimated: `6340` - // Minimum execution time: 12_580 nanoseconds. - Weight::from_parts(12_987_000, 6340) + // Minimum execution time: 13_155 nanoseconds. + Weight::from_parts(14_158_000, 6340) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -186,8 +197,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 3_320 nanoseconds. - Weight::from_ref_time(3_513_000) + // Minimum execution time: 2_961 nanoseconds. + Weight::from_ref_time(3_139_000) .saturating_add(T::DbWeight::get().writes(1_u64)) } /// Storage: Democracy NextExternal (r:0 w:1) @@ -196,37 +207,41 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 3_407 nanoseconds. - Weight::from_ref_time(3_565_000) + // Minimum execution time: 3_040 nanoseconds. + Weight::from_ref_time(3_261_000) .saturating_add(T::DbWeight::get().writes(1_u64)) } /// Storage: Democracy NextExternal (r:1 w:1) /// Proof: Democracy NextExternal (max_values: Some(1), max_size: Some(132), added: 627, mode: MaxEncodedLen) /// Storage: Democracy ReferendumCount (r:1 w:1) /// Proof: Democracy ReferendumCount (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) + /// Storage: Democracy MetadataOf (r:1 w:2) + /// Proof: Democracy MetadataOf (max_values: None, max_size: Some(53), added: 2528, mode: MaxEncodedLen) /// Storage: Democracy ReferendumInfoOf (r:0 w:1) /// Proof: Democracy ReferendumInfoOf (max_values: None, max_size: Some(201), added: 2676, mode: MaxEncodedLen) fn fast_track() -> Weight { // Proof Size summary in bytes: - // Measured: `212` - // Estimated: `1126` - // Minimum execution time: 16_831 nanoseconds. - Weight::from_parts(17_155_000, 1126) - .saturating_add(T::DbWeight::get().reads(2_u64)) - .saturating_add(T::DbWeight::get().writes(3_u64)) + // Measured: `286` + // Estimated: `3654` + // Minimum execution time: 26_666 nanoseconds. + Weight::from_parts(28_014_000, 3654) + .saturating_add(T::DbWeight::get().reads(3_u64)) + .saturating_add(T::DbWeight::get().writes(5_u64)) } /// Storage: Democracy NextExternal (r:1 w:1) /// Proof: Democracy NextExternal (max_values: Some(1), max_size: Some(132), added: 627, mode: MaxEncodedLen) /// Storage: Democracy Blacklist (r:1 w:1) /// Proof: Democracy Blacklist (max_values: None, max_size: Some(3238), added: 5713, mode: MaxEncodedLen) + /// Storage: Democracy MetadataOf (r:1 w:1) + /// Proof: Democracy MetadataOf (max_values: None, max_size: Some(53), added: 2528, mode: MaxEncodedLen) fn veto_external() -> Weight { // Proof Size summary in bytes: - // Measured: `3477` - // Estimated: `6340` - // Minimum execution time: 22_072 nanoseconds. - Weight::from_parts(22_517_000, 6340) - .saturating_add(T::DbWeight::get().reads(2_u64)) - .saturating_add(T::DbWeight::get().writes(2_u64)) + // Measured: `3551` + // Estimated: `8868` + // Minimum execution time: 30_180 nanoseconds. + Weight::from_parts(31_593_000, 8868) + .saturating_add(T::DbWeight::get().reads(3_u64)) + .saturating_add(T::DbWeight::get().writes(3_u64)) } /// Storage: Democracy PublicProps (r:1 w:1) /// Proof: Democracy PublicProps (max_values: Some(1), max_size: Some(16702), added: 17197, mode: MaxEncodedLen) @@ -234,24 +249,29 @@ impl WeightInfo for SubstrateWeight { /// Proof: Democracy DepositOf (max_values: None, max_size: Some(3230), added: 5705, mode: MaxEncodedLen) /// Storage: System Account (r:1 w:1) /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) + /// Storage: Democracy MetadataOf (r:1 w:1) + /// Proof: Democracy MetadataOf (max_values: None, max_size: Some(53), added: 2528, mode: MaxEncodedLen) fn cancel_proposal() -> Weight { // Proof Size summary in bytes: - // Measured: `5837` - // Estimated: `25505` - // Minimum execution time: 56_925 nanoseconds. - Weight::from_parts(57_253_000, 25505) - .saturating_add(T::DbWeight::get().reads(3_u64)) - .saturating_add(T::DbWeight::get().writes(3_u64)) + // Measured: `5915` + // Estimated: `28033` + // Minimum execution time: 80_780 nanoseconds. + Weight::from_parts(82_070_000, 28033) + .saturating_add(T::DbWeight::get().reads(4_u64)) + .saturating_add(T::DbWeight::get().writes(4_u64)) } + /// Storage: Democracy MetadataOf (r:1 w:1) + /// Proof: Democracy MetadataOf (max_values: None, max_size: Some(53), added: 2528, mode: MaxEncodedLen) /// Storage: Democracy ReferendumInfoOf (r:0 w:1) /// Proof: Democracy ReferendumInfoOf (max_values: None, max_size: Some(201), added: 2676, mode: MaxEncodedLen) fn cancel_referendum() -> Weight { // Proof Size summary in bytes: - // Measured: `0` - // Estimated: `0` - // Minimum execution time: 8_582 nanoseconds. - Weight::from_ref_time(8_754_000) - .saturating_add(T::DbWeight::get().writes(1_u64)) + // Measured: `271` + // Estimated: `2528` + // Minimum execution time: 18_117 nanoseconds. + Weight::from_parts(19_027_000, 2528) + .saturating_add(T::DbWeight::get().reads(1_u64)) + .saturating_add(T::DbWeight::get().writes(2_u64)) } /// Storage: Democracy LowestUnbaked (r:1 w:1) /// Proof: Democracy LowestUnbaked (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) @@ -262,12 +282,12 @@ impl WeightInfo for SubstrateWeight { /// The range of component `r` is `[0, 99]`. fn on_initialize_base(r: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `207 + r * (117 ±0)` + // Measured: `244 + r * (117 ±0)` // Estimated: `998 + r * (2676 ±0)` - // Minimum execution time: 6_665 nanoseconds. - Weight::from_parts(9_219_932, 998) - // Standard Error: 4_236 - .saturating_add(Weight::from_ref_time(2_194_623).saturating_mul(r.into())) + // Minimum execution time: 7_093 nanoseconds. + Weight::from_parts(8_792_955, 998) + // Standard Error: 6_630 + .saturating_add(Weight::from_ref_time(3_091_565).saturating_mul(r.into())) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().reads((1_u64).saturating_mul(r.into()))) .saturating_add(T::DbWeight::get().writes(1_u64)) @@ -288,12 +308,12 @@ impl WeightInfo for SubstrateWeight { /// The range of component `r` is `[0, 99]`. fn on_initialize_base_with_launch_period(r: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `207 + r * (117 ±0)` + // Measured: `244 + r * (117 ±0)` // Estimated: `19318 + r * (2676 ±0)` - // Minimum execution time: 9_842 nanoseconds. - Weight::from_parts(11_932_535, 19318) - // Standard Error: 4_413 - .saturating_add(Weight::from_ref_time(2_199_644).saturating_mul(r.into())) + // Minimum execution time: 10_372 nanoseconds. + Weight::from_parts(10_961_165, 19318) + // Standard Error: 7_284 + .saturating_add(Weight::from_ref_time(3_112_573).saturating_mul(r.into())) .saturating_add(T::DbWeight::get().reads(5_u64)) .saturating_add(T::DbWeight::get().reads((1_u64).saturating_mul(r.into()))) .saturating_add(T::DbWeight::get().writes(1_u64)) @@ -308,12 +328,12 @@ impl WeightInfo for SubstrateWeight { /// The range of component `r` is `[0, 99]`. fn delegate(r: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `948 + r * (139 ±0)` + // Measured: `958 + r * (139 ±0)` // Estimated: `22584 + r * (2676 ±0)` - // Minimum execution time: 34_740 nanoseconds. - Weight::from_parts(38_366_374, 22584) - // Standard Error: 4_868 - .saturating_add(Weight::from_ref_time(3_286_516).saturating_mul(r.into())) + // Minimum execution time: 36_618 nanoseconds. + Weight::from_parts(42_803_184, 22584) + // Standard Error: 7_268 + .saturating_add(Weight::from_ref_time(4_537_902).saturating_mul(r.into())) .saturating_add(T::DbWeight::get().reads(4_u64)) .saturating_add(T::DbWeight::get().reads((1_u64).saturating_mul(r.into()))) .saturating_add(T::DbWeight::get().writes(4_u64)) @@ -327,12 +347,12 @@ impl WeightInfo for SubstrateWeight { /// The range of component `r` is `[0, 99]`. fn undelegate(r: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `547 + r * (139 ±0)` + // Measured: `557 + r * (139 ±0)` // Estimated: `12540 + r * (2676 ±0)` - // Minimum execution time: 19_516 nanoseconds. - Weight::from_parts(21_629_605, 12540) - // Standard Error: 4_401 - .saturating_add(Weight::from_ref_time(3_238_187).saturating_mul(r.into())) + // Minimum execution time: 19_758 nanoseconds. + Weight::from_parts(21_641_793, 12540) + // Standard Error: 6_889 + .saturating_add(Weight::from_ref_time(4_478_884).saturating_mul(r.into())) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().reads((1_u64).saturating_mul(r.into()))) .saturating_add(T::DbWeight::get().writes(2_u64)) @@ -345,8 +365,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 3_291 nanoseconds. - Weight::from_ref_time(3_485_000) + // Minimum execution time: 2_844 nanoseconds. + Weight::from_ref_time(3_017_000) .saturating_add(T::DbWeight::get().writes(1_u64)) } /// Storage: Democracy VotingOf (r:1 w:1) @@ -358,12 +378,12 @@ impl WeightInfo for SubstrateWeight { /// The range of component `r` is `[0, 99]`. fn unlock_remove(r: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `617` + // Measured: `627` // Estimated: `12647` - // Minimum execution time: 19_357 nanoseconds. - Weight::from_parts(24_014_517, 12647) - // Standard Error: 994 - .saturating_add(Weight::from_ref_time(17_096).saturating_mul(r.into())) + // Minimum execution time: 20_380 nanoseconds. + Weight::from_parts(28_295_875, 12647) + // Standard Error: 2_331 + .saturating_add(Weight::from_ref_time(67_348).saturating_mul(r.into())) .saturating_add(T::DbWeight::get().reads(3_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) } @@ -376,12 +396,12 @@ impl WeightInfo for SubstrateWeight { /// The range of component `r` is `[0, 99]`. fn unlock_set(r: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `618 + r * (22 ±0)` + // Measured: `628 + r * (22 ±0)` // Estimated: `12647` - // Minimum execution time: 22_340 nanoseconds. - Weight::from_parts(23_355_734, 12647) - // Standard Error: 548 - .saturating_add(Weight::from_ref_time(64_308).saturating_mul(r.into())) + // Minimum execution time: 24_475 nanoseconds. + Weight::from_parts(27_102_576, 12647) + // Standard Error: 1_464 + .saturating_add(Weight::from_ref_time(128_921).saturating_mul(r.into())) .saturating_add(T::DbWeight::get().reads(3_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) } @@ -392,12 +412,12 @@ impl WeightInfo for SubstrateWeight { /// The range of component `r` is `[1, 100]`. fn remove_vote(r: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `781 + r * (26 ±0)` + // Measured: `791 + r * (26 ±0)` // Estimated: `8946` - // Minimum execution time: 14_542 nanoseconds. - Weight::from_parts(16_411_916, 8946) - // Standard Error: 839 - .saturating_add(Weight::from_ref_time(73_268).saturating_mul(r.into())) + // Minimum execution time: 15_039 nanoseconds. + Weight::from_parts(19_252_498, 8946) + // Standard Error: 1_798 + .saturating_add(Weight::from_ref_time(131_855).saturating_mul(r.into())) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().writes(2_u64)) } @@ -408,15 +428,97 @@ impl WeightInfo for SubstrateWeight { /// The range of component `r` is `[1, 100]`. fn remove_other_vote(r: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `781 + r * (26 ±0)` + // Measured: `791 + r * (26 ±0)` // Estimated: `8946` - // Minimum execution time: 14_463 nanoseconds. - Weight::from_parts(16_302_901, 8946) - // Standard Error: 809 - .saturating_add(Weight::from_ref_time(73_692).saturating_mul(r.into())) + // Minimum execution time: 14_837 nanoseconds. + Weight::from_parts(19_144_929, 8946) + // Standard Error: 1_875 + .saturating_add(Weight::from_ref_time(136_819).saturating_mul(r.into())) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().writes(2_u64)) } + /// Storage: Democracy NextExternal (r:1 w:0) + /// Proof: Democracy NextExternal (max_values: Some(1), max_size: Some(132), added: 627, mode: MaxEncodedLen) + /// Storage: Preimage StatusFor (r:1 w:0) + /// Proof: Preimage StatusFor (max_values: None, max_size: Some(91), added: 2566, mode: MaxEncodedLen) + /// Storage: Democracy MetadataOf (r:0 w:1) + /// Proof: Democracy MetadataOf (max_values: None, max_size: Some(53), added: 2528, mode: MaxEncodedLen) + fn set_external_metadata() -> Weight { + // Proof Size summary in bytes: + // Measured: `356` + // Estimated: `3193` + // Minimum execution time: 17_338 nanoseconds. + Weight::from_parts(17_946_000, 3193) + .saturating_add(T::DbWeight::get().reads(2_u64)) + .saturating_add(T::DbWeight::get().writes(1_u64)) + } + /// Storage: Democracy NextExternal (r:1 w:0) + /// Proof: Democracy NextExternal (max_values: Some(1), max_size: Some(132), added: 627, mode: MaxEncodedLen) + /// Storage: Democracy MetadataOf (r:1 w:1) + /// Proof: Democracy MetadataOf (max_values: None, max_size: Some(53), added: 2528, mode: MaxEncodedLen) + fn clear_external_metadata() -> Weight { + // Proof Size summary in bytes: + // Measured: `286` + // Estimated: `3155` + // Minimum execution time: 15_364 nanoseconds. + Weight::from_parts(15_990_000, 3155) + .saturating_add(T::DbWeight::get().reads(2_u64)) + .saturating_add(T::DbWeight::get().writes(1_u64)) + } + /// Storage: Democracy PublicProps (r:1 w:0) + /// Proof: Democracy PublicProps (max_values: Some(1), max_size: Some(16702), added: 17197, mode: MaxEncodedLen) + /// Storage: Preimage StatusFor (r:1 w:0) + /// Proof: Preimage StatusFor (max_values: None, max_size: Some(91), added: 2566, mode: MaxEncodedLen) + /// Storage: Democracy MetadataOf (r:0 w:1) + /// Proof: Democracy MetadataOf (max_values: None, max_size: Some(53), added: 2528, mode: MaxEncodedLen) + fn set_proposal_metadata() -> Weight { + // Proof Size summary in bytes: + // Measured: `4919` + // Estimated: `19763` + // Minimum execution time: 37_147 nanoseconds. + Weight::from_parts(37_778_000, 19763) + .saturating_add(T::DbWeight::get().reads(2_u64)) + .saturating_add(T::DbWeight::get().writes(1_u64)) + } + /// Storage: Democracy PublicProps (r:1 w:0) + /// Proof: Democracy PublicProps (max_values: Some(1), max_size: Some(16702), added: 17197, mode: MaxEncodedLen) + /// Storage: Democracy MetadataOf (r:1 w:1) + /// Proof: Democracy MetadataOf (max_values: None, max_size: Some(53), added: 2528, mode: MaxEncodedLen) + fn clear_proposal_metadata() -> Weight { + // Proof Size summary in bytes: + // Measured: `4853` + // Estimated: `19725` + // Minimum execution time: 34_118 nanoseconds. + Weight::from_parts(34_737_000, 19725) + .saturating_add(T::DbWeight::get().reads(2_u64)) + .saturating_add(T::DbWeight::get().writes(1_u64)) + } + /// Storage: Preimage StatusFor (r:1 w:0) + /// Proof: Preimage StatusFor (max_values: None, max_size: Some(91), added: 2566, mode: MaxEncodedLen) + /// Storage: Democracy MetadataOf (r:0 w:1) + /// Proof: Democracy MetadataOf (max_values: None, max_size: Some(53), added: 2528, mode: MaxEncodedLen) + fn set_referendum_metadata() -> Weight { + // Proof Size summary in bytes: + // Measured: `144` + // Estimated: `2566` + // Minimum execution time: 12_787 nanoseconds. + Weight::from_parts(13_463_000, 2566) + .saturating_add(T::DbWeight::get().reads(1_u64)) + .saturating_add(T::DbWeight::get().writes(1_u64)) + } + /// Storage: Democracy ReferendumInfoOf (r:1 w:0) + /// Proof: Democracy ReferendumInfoOf (max_values: None, max_size: Some(201), added: 2676, mode: MaxEncodedLen) + /// Storage: Democracy MetadataOf (r:1 w:1) + /// Proof: Democracy MetadataOf (max_values: None, max_size: Some(53), added: 2528, mode: MaxEncodedLen) + fn clear_referendum_metadata() -> Weight { + // Proof Size summary in bytes: + // Measured: `302` + // Estimated: `5204` + // Minimum execution time: 17_636 nanoseconds. + Weight::from_parts(18_399_000, 5204) + .saturating_add(T::DbWeight::get().reads(2_u64)) + .saturating_add(T::DbWeight::get().writes(1_u64)) + } } // For backwards compatibility and tests @@ -433,8 +535,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `4864` // Estimated: `23409` - // Minimum execution time: 34_509 nanoseconds. - Weight::from_parts(34_781_000, 23409) + // Minimum execution time: 42_939 nanoseconds. + Weight::from_parts(43_543_000, 23409) .saturating_add(RocksDbWeight::get().reads(3_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) } @@ -444,8 +546,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `3620` // Estimated: `5705` - // Minimum execution time: 31_151 nanoseconds. - Weight::from_parts(31_566_000, 5705) + // Minimum execution time: 36_475 nanoseconds. + Weight::from_parts(37_863_000, 5705) .saturating_add(RocksDbWeight::get().reads(1_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } @@ -457,10 +559,10 @@ impl WeightInfo for () { /// Proof: Balances Locks (max_values: None, max_size: Some(1299), added: 3774, mode: MaxEncodedLen) fn vote_new() -> Weight { // Proof Size summary in bytes: - // Measured: `3555` + // Measured: `3565` // Estimated: `12720` - // Minimum execution time: 42_618 nanoseconds. - Weight::from_parts(43_231_000, 12720) + // Minimum execution time: 56_372 nanoseconds. + Weight::from_parts(57_483_000, 12720) .saturating_add(RocksDbWeight::get().reads(3_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) } @@ -472,10 +574,10 @@ impl WeightInfo for () { /// Proof: Balances Locks (max_values: None, max_size: Some(1299), added: 3774, mode: MaxEncodedLen) fn vote_existing() -> Weight { // Proof Size summary in bytes: - // Measured: `3577` + // Measured: `3587` // Estimated: `12720` - // Minimum execution time: 42_875 nanoseconds. - Weight::from_parts(43_338_000, 12720) + // Minimum execution time: 56_789 nanoseconds. + Weight::from_parts(57_737_000, 12720) .saturating_add(RocksDbWeight::get().reads(3_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) } @@ -483,14 +585,16 @@ impl WeightInfo for () { /// Proof: Democracy ReferendumInfoOf (max_values: None, max_size: Some(201), added: 2676, mode: MaxEncodedLen) /// Storage: Democracy Cancellations (r:1 w:1) /// Proof: Democracy Cancellations (max_values: None, max_size: Some(33), added: 2508, mode: MaxEncodedLen) + /// Storage: Democracy MetadataOf (r:1 w:1) + /// Proof: Democracy MetadataOf (max_values: None, max_size: Some(53), added: 2528, mode: MaxEncodedLen) fn emergency_cancel() -> Weight { // Proof Size summary in bytes: - // Measured: `320` - // Estimated: `5184` - // Minimum execution time: 16_543 nanoseconds. - Weight::from_parts(16_762_000, 5184) - .saturating_add(RocksDbWeight::get().reads(2_u64)) - .saturating_add(RocksDbWeight::get().writes(2_u64)) + // Measured: `398` + // Estimated: `7712` + // Minimum execution time: 24_379 nanoseconds. + Weight::from_parts(25_302_000, 7712) + .saturating_add(RocksDbWeight::get().reads(3_u64)) + .saturating_add(RocksDbWeight::get().writes(3_u64)) } /// Storage: Democracy PublicProps (r:1 w:1) /// Proof: Democracy PublicProps (max_values: Some(1), max_size: Some(16702), added: 17197, mode: MaxEncodedLen) @@ -498,6 +602,8 @@ impl WeightInfo for () { /// Proof: Democracy DepositOf (max_values: None, max_size: Some(3230), added: 5705, mode: MaxEncodedLen) /// Storage: System Account (r:1 w:1) /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) + /// Storage: Democracy MetadataOf (r:3 w:1) + /// Proof: Democracy MetadataOf (max_values: None, max_size: Some(53), added: 2528, mode: MaxEncodedLen) /// Storage: Democracy NextExternal (r:1 w:1) /// Proof: Democracy NextExternal (max_values: Some(1), max_size: Some(132), added: 627, mode: MaxEncodedLen) /// Storage: Democracy ReferendumInfoOf (r:1 w:1) @@ -506,12 +612,12 @@ impl WeightInfo for () { /// Proof: Democracy Blacklist (max_values: None, max_size: Some(3238), added: 5713, mode: MaxEncodedLen) fn blacklist() -> Weight { // Proof Size summary in bytes: - // Measured: `5958` - // Estimated: `28808` - // Minimum execution time: 70_135 nanoseconds. - Weight::from_parts(70_616_000, 28808) - .saturating_add(RocksDbWeight::get().reads(5_u64)) - .saturating_add(RocksDbWeight::get().writes(6_u64)) + // Measured: `6036` + // Estimated: `36392` + // Minimum execution time: 100_345 nanoseconds. + Weight::from_parts(102_233_000, 36392) + .saturating_add(RocksDbWeight::get().reads(8_u64)) + .saturating_add(RocksDbWeight::get().writes(7_u64)) } /// Storage: Democracy NextExternal (r:1 w:1) /// Proof: Democracy NextExternal (max_values: Some(1), max_size: Some(132), added: 627, mode: MaxEncodedLen) @@ -521,8 +627,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `3448` // Estimated: `6340` - // Minimum execution time: 12_580 nanoseconds. - Weight::from_parts(12_987_000, 6340) + // Minimum execution time: 13_155 nanoseconds. + Weight::from_parts(14_158_000, 6340) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } @@ -532,8 +638,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 3_320 nanoseconds. - Weight::from_ref_time(3_513_000) + // Minimum execution time: 2_961 nanoseconds. + Weight::from_ref_time(3_139_000) .saturating_add(RocksDbWeight::get().writes(1_u64)) } /// Storage: Democracy NextExternal (r:0 w:1) @@ -542,37 +648,41 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 3_407 nanoseconds. - Weight::from_ref_time(3_565_000) + // Minimum execution time: 3_040 nanoseconds. + Weight::from_ref_time(3_261_000) .saturating_add(RocksDbWeight::get().writes(1_u64)) } /// Storage: Democracy NextExternal (r:1 w:1) /// Proof: Democracy NextExternal (max_values: Some(1), max_size: Some(132), added: 627, mode: MaxEncodedLen) /// Storage: Democracy ReferendumCount (r:1 w:1) /// Proof: Democracy ReferendumCount (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) + /// Storage: Democracy MetadataOf (r:1 w:2) + /// Proof: Democracy MetadataOf (max_values: None, max_size: Some(53), added: 2528, mode: MaxEncodedLen) /// Storage: Democracy ReferendumInfoOf (r:0 w:1) /// Proof: Democracy ReferendumInfoOf (max_values: None, max_size: Some(201), added: 2676, mode: MaxEncodedLen) fn fast_track() -> Weight { // Proof Size summary in bytes: - // Measured: `212` - // Estimated: `1126` - // Minimum execution time: 16_831 nanoseconds. - Weight::from_parts(17_155_000, 1126) - .saturating_add(RocksDbWeight::get().reads(2_u64)) - .saturating_add(RocksDbWeight::get().writes(3_u64)) + // Measured: `286` + // Estimated: `3654` + // Minimum execution time: 26_666 nanoseconds. + Weight::from_parts(28_014_000, 3654) + .saturating_add(RocksDbWeight::get().reads(3_u64)) + .saturating_add(RocksDbWeight::get().writes(5_u64)) } /// Storage: Democracy NextExternal (r:1 w:1) /// Proof: Democracy NextExternal (max_values: Some(1), max_size: Some(132), added: 627, mode: MaxEncodedLen) /// Storage: Democracy Blacklist (r:1 w:1) /// Proof: Democracy Blacklist (max_values: None, max_size: Some(3238), added: 5713, mode: MaxEncodedLen) + /// Storage: Democracy MetadataOf (r:1 w:1) + /// Proof: Democracy MetadataOf (max_values: None, max_size: Some(53), added: 2528, mode: MaxEncodedLen) fn veto_external() -> Weight { // Proof Size summary in bytes: - // Measured: `3477` - // Estimated: `6340` - // Minimum execution time: 22_072 nanoseconds. - Weight::from_parts(22_517_000, 6340) - .saturating_add(RocksDbWeight::get().reads(2_u64)) - .saturating_add(RocksDbWeight::get().writes(2_u64)) + // Measured: `3551` + // Estimated: `8868` + // Minimum execution time: 30_180 nanoseconds. + Weight::from_parts(31_593_000, 8868) + .saturating_add(RocksDbWeight::get().reads(3_u64)) + .saturating_add(RocksDbWeight::get().writes(3_u64)) } /// Storage: Democracy PublicProps (r:1 w:1) /// Proof: Democracy PublicProps (max_values: Some(1), max_size: Some(16702), added: 17197, mode: MaxEncodedLen) @@ -580,24 +690,29 @@ impl WeightInfo for () { /// Proof: Democracy DepositOf (max_values: None, max_size: Some(3230), added: 5705, mode: MaxEncodedLen) /// Storage: System Account (r:1 w:1) /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) + /// Storage: Democracy MetadataOf (r:1 w:1) + /// Proof: Democracy MetadataOf (max_values: None, max_size: Some(53), added: 2528, mode: MaxEncodedLen) fn cancel_proposal() -> Weight { // Proof Size summary in bytes: - // Measured: `5837` - // Estimated: `25505` - // Minimum execution time: 56_925 nanoseconds. - Weight::from_parts(57_253_000, 25505) - .saturating_add(RocksDbWeight::get().reads(3_u64)) - .saturating_add(RocksDbWeight::get().writes(3_u64)) + // Measured: `5915` + // Estimated: `28033` + // Minimum execution time: 80_780 nanoseconds. + Weight::from_parts(82_070_000, 28033) + .saturating_add(RocksDbWeight::get().reads(4_u64)) + .saturating_add(RocksDbWeight::get().writes(4_u64)) } + /// Storage: Democracy MetadataOf (r:1 w:1) + /// Proof: Democracy MetadataOf (max_values: None, max_size: Some(53), added: 2528, mode: MaxEncodedLen) /// Storage: Democracy ReferendumInfoOf (r:0 w:1) /// Proof: Democracy ReferendumInfoOf (max_values: None, max_size: Some(201), added: 2676, mode: MaxEncodedLen) fn cancel_referendum() -> Weight { // Proof Size summary in bytes: - // Measured: `0` - // Estimated: `0` - // Minimum execution time: 8_582 nanoseconds. - Weight::from_ref_time(8_754_000) - .saturating_add(RocksDbWeight::get().writes(1_u64)) + // Measured: `271` + // Estimated: `2528` + // Minimum execution time: 18_117 nanoseconds. + Weight::from_parts(19_027_000, 2528) + .saturating_add(RocksDbWeight::get().reads(1_u64)) + .saturating_add(RocksDbWeight::get().writes(2_u64)) } /// Storage: Democracy LowestUnbaked (r:1 w:1) /// Proof: Democracy LowestUnbaked (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) @@ -608,12 +723,12 @@ impl WeightInfo for () { /// The range of component `r` is `[0, 99]`. fn on_initialize_base(r: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `207 + r * (117 ±0)` + // Measured: `244 + r * (117 ±0)` // Estimated: `998 + r * (2676 ±0)` - // Minimum execution time: 6_665 nanoseconds. - Weight::from_parts(9_219_932, 998) - // Standard Error: 4_236 - .saturating_add(Weight::from_ref_time(2_194_623).saturating_mul(r.into())) + // Minimum execution time: 7_093 nanoseconds. + Weight::from_parts(8_792_955, 998) + // Standard Error: 6_630 + .saturating_add(Weight::from_ref_time(3_091_565).saturating_mul(r.into())) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().reads((1_u64).saturating_mul(r.into()))) .saturating_add(RocksDbWeight::get().writes(1_u64)) @@ -634,12 +749,12 @@ impl WeightInfo for () { /// The range of component `r` is `[0, 99]`. fn on_initialize_base_with_launch_period(r: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `207 + r * (117 ±0)` + // Measured: `244 + r * (117 ±0)` // Estimated: `19318 + r * (2676 ±0)` - // Minimum execution time: 9_842 nanoseconds. - Weight::from_parts(11_932_535, 19318) - // Standard Error: 4_413 - .saturating_add(Weight::from_ref_time(2_199_644).saturating_mul(r.into())) + // Minimum execution time: 10_372 nanoseconds. + Weight::from_parts(10_961_165, 19318) + // Standard Error: 7_284 + .saturating_add(Weight::from_ref_time(3_112_573).saturating_mul(r.into())) .saturating_add(RocksDbWeight::get().reads(5_u64)) .saturating_add(RocksDbWeight::get().reads((1_u64).saturating_mul(r.into()))) .saturating_add(RocksDbWeight::get().writes(1_u64)) @@ -654,12 +769,12 @@ impl WeightInfo for () { /// The range of component `r` is `[0, 99]`. fn delegate(r: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `948 + r * (139 ±0)` + // Measured: `958 + r * (139 ±0)` // Estimated: `22584 + r * (2676 ±0)` - // Minimum execution time: 34_740 nanoseconds. - Weight::from_parts(38_366_374, 22584) - // Standard Error: 4_868 - .saturating_add(Weight::from_ref_time(3_286_516).saturating_mul(r.into())) + // Minimum execution time: 36_618 nanoseconds. + Weight::from_parts(42_803_184, 22584) + // Standard Error: 7_268 + .saturating_add(Weight::from_ref_time(4_537_902).saturating_mul(r.into())) .saturating_add(RocksDbWeight::get().reads(4_u64)) .saturating_add(RocksDbWeight::get().reads((1_u64).saturating_mul(r.into()))) .saturating_add(RocksDbWeight::get().writes(4_u64)) @@ -673,12 +788,12 @@ impl WeightInfo for () { /// The range of component `r` is `[0, 99]`. fn undelegate(r: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `547 + r * (139 ±0)` + // Measured: `557 + r * (139 ±0)` // Estimated: `12540 + r * (2676 ±0)` - // Minimum execution time: 19_516 nanoseconds. - Weight::from_parts(21_629_605, 12540) - // Standard Error: 4_401 - .saturating_add(Weight::from_ref_time(3_238_187).saturating_mul(r.into())) + // Minimum execution time: 19_758 nanoseconds. + Weight::from_parts(21_641_793, 12540) + // Standard Error: 6_889 + .saturating_add(Weight::from_ref_time(4_478_884).saturating_mul(r.into())) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().reads((1_u64).saturating_mul(r.into()))) .saturating_add(RocksDbWeight::get().writes(2_u64)) @@ -691,8 +806,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 3_291 nanoseconds. - Weight::from_ref_time(3_485_000) + // Minimum execution time: 2_844 nanoseconds. + Weight::from_ref_time(3_017_000) .saturating_add(RocksDbWeight::get().writes(1_u64)) } /// Storage: Democracy VotingOf (r:1 w:1) @@ -704,12 +819,12 @@ impl WeightInfo for () { /// The range of component `r` is `[0, 99]`. fn unlock_remove(r: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `617` + // Measured: `627` // Estimated: `12647` - // Minimum execution time: 19_357 nanoseconds. - Weight::from_parts(24_014_517, 12647) - // Standard Error: 994 - .saturating_add(Weight::from_ref_time(17_096).saturating_mul(r.into())) + // Minimum execution time: 20_380 nanoseconds. + Weight::from_parts(28_295_875, 12647) + // Standard Error: 2_331 + .saturating_add(Weight::from_ref_time(67_348).saturating_mul(r.into())) .saturating_add(RocksDbWeight::get().reads(3_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) } @@ -722,12 +837,12 @@ impl WeightInfo for () { /// The range of component `r` is `[0, 99]`. fn unlock_set(r: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `618 + r * (22 ±0)` + // Measured: `628 + r * (22 ±0)` // Estimated: `12647` - // Minimum execution time: 22_340 nanoseconds. - Weight::from_parts(23_355_734, 12647) - // Standard Error: 548 - .saturating_add(Weight::from_ref_time(64_308).saturating_mul(r.into())) + // Minimum execution time: 24_475 nanoseconds. + Weight::from_parts(27_102_576, 12647) + // Standard Error: 1_464 + .saturating_add(Weight::from_ref_time(128_921).saturating_mul(r.into())) .saturating_add(RocksDbWeight::get().reads(3_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) } @@ -738,12 +853,12 @@ impl WeightInfo for () { /// The range of component `r` is `[1, 100]`. fn remove_vote(r: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `781 + r * (26 ±0)` + // Measured: `791 + r * (26 ±0)` // Estimated: `8946` - // Minimum execution time: 14_542 nanoseconds. - Weight::from_parts(16_411_916, 8946) - // Standard Error: 839 - .saturating_add(Weight::from_ref_time(73_268).saturating_mul(r.into())) + // Minimum execution time: 15_039 nanoseconds. + Weight::from_parts(19_252_498, 8946) + // Standard Error: 1_798 + .saturating_add(Weight::from_ref_time(131_855).saturating_mul(r.into())) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().writes(2_u64)) } @@ -754,13 +869,95 @@ impl WeightInfo for () { /// The range of component `r` is `[1, 100]`. fn remove_other_vote(r: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `781 + r * (26 ±0)` + // Measured: `791 + r * (26 ±0)` // Estimated: `8946` - // Minimum execution time: 14_463 nanoseconds. - Weight::from_parts(16_302_901, 8946) - // Standard Error: 809 - .saturating_add(Weight::from_ref_time(73_692).saturating_mul(r.into())) + // Minimum execution time: 14_837 nanoseconds. + Weight::from_parts(19_144_929, 8946) + // Standard Error: 1_875 + .saturating_add(Weight::from_ref_time(136_819).saturating_mul(r.into())) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().writes(2_u64)) } + /// Storage: Democracy NextExternal (r:1 w:0) + /// Proof: Democracy NextExternal (max_values: Some(1), max_size: Some(132), added: 627, mode: MaxEncodedLen) + /// Storage: Preimage StatusFor (r:1 w:0) + /// Proof: Preimage StatusFor (max_values: None, max_size: Some(91), added: 2566, mode: MaxEncodedLen) + /// Storage: Democracy MetadataOf (r:0 w:1) + /// Proof: Democracy MetadataOf (max_values: None, max_size: Some(53), added: 2528, mode: MaxEncodedLen) + fn set_external_metadata() -> Weight { + // Proof Size summary in bytes: + // Measured: `356` + // Estimated: `3193` + // Minimum execution time: 17_338 nanoseconds. + Weight::from_parts(17_946_000, 3193) + .saturating_add(RocksDbWeight::get().reads(2_u64)) + .saturating_add(RocksDbWeight::get().writes(1_u64)) + } + /// Storage: Democracy NextExternal (r:1 w:0) + /// Proof: Democracy NextExternal (max_values: Some(1), max_size: Some(132), added: 627, mode: MaxEncodedLen) + /// Storage: Democracy MetadataOf (r:1 w:1) + /// Proof: Democracy MetadataOf (max_values: None, max_size: Some(53), added: 2528, mode: MaxEncodedLen) + fn clear_external_metadata() -> Weight { + // Proof Size summary in bytes: + // Measured: `286` + // Estimated: `3155` + // Minimum execution time: 15_364 nanoseconds. + Weight::from_parts(15_990_000, 3155) + .saturating_add(RocksDbWeight::get().reads(2_u64)) + .saturating_add(RocksDbWeight::get().writes(1_u64)) + } + /// Storage: Democracy PublicProps (r:1 w:0) + /// Proof: Democracy PublicProps (max_values: Some(1), max_size: Some(16702), added: 17197, mode: MaxEncodedLen) + /// Storage: Preimage StatusFor (r:1 w:0) + /// Proof: Preimage StatusFor (max_values: None, max_size: Some(91), added: 2566, mode: MaxEncodedLen) + /// Storage: Democracy MetadataOf (r:0 w:1) + /// Proof: Democracy MetadataOf (max_values: None, max_size: Some(53), added: 2528, mode: MaxEncodedLen) + fn set_proposal_metadata() -> Weight { + // Proof Size summary in bytes: + // Measured: `4919` + // Estimated: `19763` + // Minimum execution time: 37_147 nanoseconds. + Weight::from_parts(37_778_000, 19763) + .saturating_add(RocksDbWeight::get().reads(2_u64)) + .saturating_add(RocksDbWeight::get().writes(1_u64)) + } + /// Storage: Democracy PublicProps (r:1 w:0) + /// Proof: Democracy PublicProps (max_values: Some(1), max_size: Some(16702), added: 17197, mode: MaxEncodedLen) + /// Storage: Democracy MetadataOf (r:1 w:1) + /// Proof: Democracy MetadataOf (max_values: None, max_size: Some(53), added: 2528, mode: MaxEncodedLen) + fn clear_proposal_metadata() -> Weight { + // Proof Size summary in bytes: + // Measured: `4853` + // Estimated: `19725` + // Minimum execution time: 34_118 nanoseconds. + Weight::from_parts(34_737_000, 19725) + .saturating_add(RocksDbWeight::get().reads(2_u64)) + .saturating_add(RocksDbWeight::get().writes(1_u64)) + } + /// Storage: Preimage StatusFor (r:1 w:0) + /// Proof: Preimage StatusFor (max_values: None, max_size: Some(91), added: 2566, mode: MaxEncodedLen) + /// Storage: Democracy MetadataOf (r:0 w:1) + /// Proof: Democracy MetadataOf (max_values: None, max_size: Some(53), added: 2528, mode: MaxEncodedLen) + fn set_referendum_metadata() -> Weight { + // Proof Size summary in bytes: + // Measured: `144` + // Estimated: `2566` + // Minimum execution time: 12_787 nanoseconds. + Weight::from_parts(13_463_000, 2566) + .saturating_add(RocksDbWeight::get().reads(1_u64)) + .saturating_add(RocksDbWeight::get().writes(1_u64)) + } + /// Storage: Democracy ReferendumInfoOf (r:1 w:0) + /// Proof: Democracy ReferendumInfoOf (max_values: None, max_size: Some(201), added: 2676, mode: MaxEncodedLen) + /// Storage: Democracy MetadataOf (r:1 w:1) + /// Proof: Democracy MetadataOf (max_values: None, max_size: Some(53), added: 2528, mode: MaxEncodedLen) + fn clear_referendum_metadata() -> Weight { + // Proof Size summary in bytes: + // Measured: `302` + // Estimated: `5204` + // Minimum execution time: 17_636 nanoseconds. + Weight::from_parts(18_399_000, 5204) + .saturating_add(RocksDbWeight::get().reads(2_u64)) + .saturating_add(RocksDbWeight::get().writes(1_u64)) + } } diff --git a/frame/referenda/src/benchmarking.rs b/frame/referenda/src/benchmarking.rs index 62b37857f..ea44beeb3 100644 --- a/frame/referenda/src/benchmarking.rs +++ b/frame/referenda/src/benchmarking.rs @@ -33,7 +33,6 @@ use sp_runtime::traits::Bounded as ArithBounded; const SEED: u32 = 0; -#[allow(dead_code)] fn assert_last_event, I: 'static>(generic_event: >::RuntimeEvent) { frame_system::Pallet::::assert_last_event(generic_event.into()); } @@ -631,6 +630,31 @@ benchmarks_instance_pallet! { assert_matches!(info, ReferendumInfo::Rejected(..)); } + set_some_metadata { + use sp_std::borrow::Cow; + let origin = T::SubmitOrigin::try_successful_origin() + .expect("SubmitOrigin has no successful origin required for the benchmark"); + let index = create_referendum::(origin.clone()); + let hash = T::Preimages::note(Cow::from(vec![5, 6])).unwrap(); + }: set_metadata(origin, index, Some(hash)) + verify { + assert_last_event::(Event::MetadataSet { index, hash }.into()); + } + + clear_metadata { + use sp_std::borrow::Cow; + let origin = T::SubmitOrigin::try_successful_origin() + .expect("SubmitOrigin has no successful origin required for the benchmark"); + let index = create_referendum::(origin.clone()); + let hash = T::Preimages::note(Cow::from(vec![6, 7, 8])).unwrap(); + assert_ok!( + Referenda::::set_metadata(origin.clone(), index, Some(hash)) + ); + }: set_metadata(origin, index, None) + verify { + assert_last_event::(Event::MetadataCleared { index, hash }.into()); + } + impl_benchmark_test_suite!( Referenda, crate::mock::new_test_ext(), diff --git a/frame/referenda/src/lib.rs b/frame/referenda/src/lib.rs index 0b846faf8..4491c1b40 100644 --- a/frame/referenda/src/lib.rs +++ b/frame/referenda/src/lib.rs @@ -72,8 +72,8 @@ use frame_support::{ v3::{Anon as ScheduleAnon, Named as ScheduleNamed}, DispatchTime, }, - Currency, LockIdentifier, OnUnbalanced, OriginTrait, PollStatus, Polling, QueryPreimage, - ReservableCurrency, StorePreimage, VoteTally, + Currency, Hash as PreimageHash, LockIdentifier, OnUnbalanced, OriginTrait, PollStatus, + Polling, QueryPreimage, ReservableCurrency, StorePreimage, VoteTally, }, BoundedVec, }; @@ -251,6 +251,16 @@ pub mod pallet { pub type DecidingCount, I: 'static = ()> = StorageMap<_, Twox64Concat, TrackIdOf, u32, ValueQuery>; + /// The metadata is a general information concerning the referendum. + /// The `PreimageHash` refers to the preimage of the `Preimages` provider which can be a JSON + /// dump or IPFS hash of a JSON file. + /// + /// Consider a garbage collection for a metadata of finished referendums to `unrequest` (remove) + /// large preimages. + #[pallet::storage] + pub type MetadataOf, I: 'static = ()> = + StorageMap<_, Blake2_128Concat, ReferendumIndex, PreimageHash>; + #[pallet::event] #[pallet::generate_deposit(pub(super) fn deposit_event)] pub enum Event, I: 'static = ()> { @@ -356,6 +366,20 @@ pub mod pallet { /// The amount placed by the account. amount: BalanceOf, }, + /// Metadata for a referendum has been set. + MetadataSet { + /// Index of the referendum. + index: ReferendumIndex, + /// Preimage hash. + hash: PreimageHash, + }, + /// Metadata for a referendum has been cleared. + MetadataCleared { + /// Index of the referendum. + index: ReferendumIndex, + /// Preimage hash. + hash: PreimageHash, + }, } #[pallet::error] @@ -384,6 +408,8 @@ pub mod pallet { NoDeposit, /// The referendum status is invalid for this operation. BadStatus, + /// The preimage does not exist. + PreimageNotExist, } #[pallet::call] @@ -540,6 +566,7 @@ pub mod pallet { Self::deposit_event(Event::::Killed { index, tally: status.tally }); Self::slash_deposit(Some(status.submission_deposit.clone())); Self::slash_deposit(status.decision_deposit.clone()); + Self::do_clear_metadata(index); let info = ReferendumInfo::Killed(frame_system::Pallet::::block_number()); ReferendumInfoFor::::insert(index, info); Ok(()) @@ -633,6 +660,40 @@ pub mod pallet { Self::deposit_event(e); Ok(()) } + + /// Set or clear metadata of a referendum. + /// + /// Parameters: + /// - `origin`: Must be `Signed` by a creator of a referendum or by anyone to clear a + /// metadata of a finished referendum. + /// - `index`: The index of a referendum to set or clear metadata for. + /// - `maybe_hash`: The hash of an on-chain stored preimage. `None` to clear a metadata. + #[pallet::call_index(8)] + #[pallet::weight( + maybe_hash.map_or( + T::WeightInfo::clear_metadata(), |_| T::WeightInfo::set_some_metadata()) + )] + pub fn set_metadata( + origin: OriginFor, + index: ReferendumIndex, + maybe_hash: Option, + ) -> DispatchResult { + let who = ensure_signed(origin)?; + if let Some(hash) = maybe_hash { + let status = Self::ensure_ongoing(index)?; + ensure!(status.submission_deposit.who == who, Error::::NoPermission); + ensure!(T::Preimages::len(&hash).is_some(), Error::::PreimageNotExist); + MetadataOf::::insert(index, hash); + Self::deposit_event(Event::::MetadataSet { index, hash }); + Ok(()) + } else { + if let Some(status) = Self::ensure_ongoing(index).ok() { + ensure!(status.submission_deposit.who == who, Error::::NoPermission); + } + Self::do_clear_metadata(index); + Ok(()) + } + } } } @@ -1204,4 +1265,11 @@ impl, I: 'static> Pallet { support_needed.passing(x, tally.support(id)) && approval_needed.passing(x, tally.approval(id)) } + + /// Clear metadata if exist for a given referendum index. + fn do_clear_metadata(index: ReferendumIndex) { + if let Some(hash) = MetadataOf::::take(index) { + Self::deposit_event(Event::::MetadataCleared { index, hash }); + } + } } diff --git a/frame/referenda/src/mock.rs b/frame/referenda/src/mock.rs index 82ae508d5..f6e19d06e 100644 --- a/frame/referenda/src/mock.rs +++ b/frame/referenda/src/mock.rs @@ -32,7 +32,7 @@ use frame_system::{EnsureRoot, EnsureSignedBy}; use sp_core::H256; use sp_runtime::{ testing::Header, - traits::{BlakeTwo256, IdentityLookup}, + traits::{BlakeTwo256, Hash, IdentityLookup}, DispatchResult, Perbill, }; @@ -470,3 +470,15 @@ impl RefState { index } } + +/// note a new preimage without registering. +pub fn note_preimage(who: u64) -> PreimageHash { + use std::sync::atomic::{AtomicU8, Ordering}; + // note a new preimage on every function invoke. + static COUNTER: AtomicU8 = AtomicU8::new(0); + let data = vec![COUNTER.fetch_add(1, Ordering::Relaxed)]; + assert_ok!(Preimage::note_preimage(RuntimeOrigin::signed(who), data.clone())); + let hash = BlakeTwo256::hash(&data); + assert!(!Preimage::is_requested(&hash)); + hash +} diff --git a/frame/referenda/src/tests.rs b/frame/referenda/src/tests.rs index c109fafe3..034454cfc 100644 --- a/frame/referenda/src/tests.rs +++ b/frame/referenda/src/tests.rs @@ -578,3 +578,75 @@ fn curve_handles_all_inputs() { let threshold = test_curve.threshold(Perbill::one()); assert_eq!(threshold, Perbill::zero()); } + +#[test] +fn set_metadata_works() { + new_test_ext().execute_with(|| { + use frame_support::traits::Hash as PreimageHash; + // invalid preimage hash. + let invalid_hash: PreimageHash = [1u8; 32].into(); + // fails to set metadata for a finished referendum. + assert_ok!(Referenda::submit( + RuntimeOrigin::signed(1), + Box::new(RawOrigin::Root.into()), + set_balance_proposal_bounded(1), + DispatchTime::At(1), + )); + let index = ReferendumCount::::get() - 1; + assert_ok!(Referenda::kill(RuntimeOrigin::root(), index)); + assert_noop!( + Referenda::set_metadata(RuntimeOrigin::signed(1), index, Some(invalid_hash)), + Error::::NotOngoing, + ); + // no permission to set metadata. + assert_ok!(Referenda::submit( + RuntimeOrigin::signed(1), + Box::new(RawOrigin::Root.into()), + set_balance_proposal_bounded(1), + DispatchTime::At(1), + )); + let index = ReferendumCount::::get() - 1; + assert_noop!( + Referenda::set_metadata(RuntimeOrigin::signed(2), index, Some(invalid_hash)), + Error::::NoPermission, + ); + // preimage does not exist. + let index = ReferendumCount::::get() - 1; + assert_noop!( + Referenda::set_metadata(RuntimeOrigin::signed(1), index, Some(invalid_hash)), + Error::::PreimageNotExist, + ); + // metadata set. + let index = ReferendumCount::::get() - 1; + let hash = note_preimage(1); + assert_ok!(Referenda::set_metadata(RuntimeOrigin::signed(1), index, Some(hash))); + System::assert_last_event(RuntimeEvent::Referenda(crate::Event::MetadataSet { + index, + hash, + })); + }); +} + +#[test] +fn clear_metadata_works() { + new_test_ext().execute_with(|| { + let hash = note_preimage(1); + assert_ok!(Referenda::submit( + RuntimeOrigin::signed(1), + Box::new(RawOrigin::Root.into()), + set_balance_proposal_bounded(1), + DispatchTime::At(1), + )); + let index = ReferendumCount::::get() - 1; + assert_ok!(Referenda::set_metadata(RuntimeOrigin::signed(1), index, Some(hash))); + assert_noop!( + Referenda::set_metadata(RuntimeOrigin::signed(2), index, None), + Error::::NoPermission, + ); + assert_ok!(Referenda::set_metadata(RuntimeOrigin::signed(1), index, None),); + System::assert_last_event(RuntimeEvent::Referenda(crate::Event::MetadataCleared { + index, + hash, + })); + }); +} diff --git a/frame/referenda/src/weights.rs b/frame/referenda/src/weights.rs index 68b44e8be..ab4975951 100644 --- a/frame/referenda/src/weights.rs +++ b/frame/referenda/src/weights.rs @@ -18,25 +18,26 @@ //! Autogenerated weights for pallet_referenda //! //! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev -//! DATE: 2023-01-24, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! DATE: 2023-01-27, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` //! WORST CASE MAP SIZE: `1000000` -//! HOSTNAME: `bm2`, CPU: `Intel(R) Core(TM) i7-7700K CPU @ 4.20GHz` +//! HOSTNAME: `runner-b3zmxxc-project-145-concurrent-0`, CPU: `Intel(R) Xeon(R) CPU @ 2.60GHz` //! EXECUTION: Some(Wasm), WASM-EXECUTION: Compiled, CHAIN: Some("dev"), DB CACHE: 1024 // Executed Command: -// ./target/production/substrate +// target/production/substrate // benchmark // pallet -// --chain=dev // --steps=50 // --repeat=20 -// --pallet=pallet_referenda // --extrinsic=* // --execution=wasm // --wasm-execution=compiled // --heap-pages=4096 -// --output=./frame/referenda/src/weights.rs +// --json-file=/builds/parity/mirrors/substrate/.git/.artifacts/bench.json +// --pallet=pallet_referenda +// --chain=dev // --header=./HEADER-APACHE2 +// --output=./frame/referenda/src/weights.rs // --template=./.maintain/frame-weight-template.hbs #![cfg_attr(rustfmt, rustfmt_skip)] @@ -76,6 +77,8 @@ pub trait WeightInfo { fn nudge_referendum_continue_confirming() -> Weight; fn nudge_referendum_approved() -> Weight; fn nudge_referendum_rejected() -> Weight; + fn set_some_metadata() -> Weight; + fn clear_metadata() -> Weight; } /// Weights for pallet_referenda using the Substrate node and recommended hardware. @@ -91,8 +94,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `251` // Estimated: `109996` - // Minimum execution time: 32_207 nanoseconds. - Weight::from_parts(32_639_000, 109996) + // Minimum execution time: 34_540 nanoseconds. + Weight::from_parts(36_144_000, 109996) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) } @@ -104,8 +107,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `536` // Estimated: `221835` - // Minimum execution time: 43_766 nanoseconds. - Weight::from_parts(44_494_000, 221835) + // Minimum execution time: 46_963 nanoseconds. + Weight::from_parts(48_459_000, 221835) .saturating_add(T::DbWeight::get().reads(3_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) } @@ -119,8 +122,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `3203` // Estimated: `9817` - // Minimum execution time: 41_561 nanoseconds. - Weight::from_parts(42_180_000, 9817) + // Minimum execution time: 55_798 nanoseconds. + Weight::from_parts(58_260_000, 9817) .saturating_add(T::DbWeight::get().reads(3_u64)) .saturating_add(T::DbWeight::get().writes(2_u64)) } @@ -134,8 +137,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `3223` // Estimated: `9817` - // Minimum execution time: 41_039 nanoseconds. - Weight::from_parts(41_673_000, 9817) + // Minimum execution time: 53_888 nanoseconds. + Weight::from_parts(57_919_000, 9817) .saturating_add(T::DbWeight::get().reads(3_u64)) .saturating_add(T::DbWeight::get().writes(2_u64)) } @@ -149,8 +152,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `536` // Estimated: `224324` - // Minimum execution time: 52_922 nanoseconds. - Weight::from_parts(53_395_000, 224324) + // Minimum execution time: 56_121 nanoseconds. + Weight::from_parts(58_301_000, 224324) .saturating_add(T::DbWeight::get().reads(4_u64)) .saturating_add(T::DbWeight::get().writes(4_u64)) } @@ -164,8 +167,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `536` // Estimated: `224324` - // Minimum execution time: 51_050 nanoseconds. - Weight::from_parts(51_736_000, 224324) + // Minimum execution time: 54_237 nanoseconds. + Weight::from_parts(55_681_000, 224324) .saturating_add(T::DbWeight::get().reads(4_u64)) .saturating_add(T::DbWeight::get().writes(4_u64)) } @@ -175,8 +178,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `415` // Estimated: `2841` - // Minimum execution time: 24_102 nanoseconds. - Weight::from_parts(24_372_000, 2841) + // Minimum execution time: 25_734 nanoseconds. + Weight::from_parts(26_429_000, 2841) .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -186,8 +189,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `405` // Estimated: `2841` - // Minimum execution time: 24_162 nanoseconds. - Weight::from_parts(24_547_000, 2841) + // Minimum execution time: 26_000 nanoseconds. + Weight::from_parts(26_786_000, 2841) .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -199,8 +202,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `412` // Estimated: `221835` - // Minimum execution time: 32_247 nanoseconds. - Weight::from_parts(32_731_000, 221835) + // Minimum execution time: 34_567 nanoseconds. + Weight::from_parts(35_939_000, 221835) .saturating_add(T::DbWeight::get().reads(3_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) } @@ -208,13 +211,15 @@ impl WeightInfo for SubstrateWeight { /// Proof: Referenda ReferendumInfoFor (max_values: None, max_size: Some(366), added: 2841, mode: MaxEncodedLen) /// Storage: Scheduler Agenda (r:2 w:2) /// Proof: Scheduler Agenda (max_values: None, max_size: Some(107022), added: 109497, mode: MaxEncodedLen) + /// Storage: Referenda MetadataOf (r:1 w:0) + /// Proof: Referenda MetadataOf (max_values: None, max_size: Some(52), added: 2527, mode: MaxEncodedLen) fn kill() -> Weight { // Proof Size summary in bytes: // Measured: `717` - // Estimated: `221835` - // Minimum execution time: 59_900 nanoseconds. - Weight::from_parts(60_659_000, 221835) - .saturating_add(T::DbWeight::get().reads(3_u64)) + // Estimated: `224362` + // Minimum execution time: 67_744 nanoseconds. + Weight::from_parts(70_047_000, 224362) + .saturating_add(T::DbWeight::get().reads(4_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) } /// Storage: Referenda TrackQueue (r:1 w:0) @@ -225,8 +230,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `174` // Estimated: `6976` - // Minimum execution time: 9_322 nanoseconds. - Weight::from_parts(9_638_000, 6976) + // Minimum execution time: 9_886 nanoseconds. + Weight::from_parts(10_406_000, 6976) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -240,8 +245,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `4661` // Estimated: `226322` - // Minimum execution time: 76_976 nanoseconds. - Weight::from_parts(77_597_000, 226322) + // Minimum execution time: 100_449 nanoseconds. + Weight::from_parts(101_812_000, 226322) .saturating_add(T::DbWeight::get().reads(4_u64)) .saturating_add(T::DbWeight::get().writes(4_u64)) } @@ -255,8 +260,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `4661` // Estimated: `226322` - // Minimum execution time: 78_405 nanoseconds. - Weight::from_parts(78_972_000, 226322) + // Minimum execution time: 101_430 nanoseconds. + Weight::from_parts(103_704_000, 226322) .saturating_add(T::DbWeight::get().reads(4_u64)) .saturating_add(T::DbWeight::get().writes(4_u64)) } @@ -270,8 +275,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `4682` // Estimated: `116825` - // Minimum execution time: 51_360 nanoseconds. - Weight::from_parts(51_737_000, 116825) + // Minimum execution time: 67_224 nanoseconds. + Weight::from_parts(70_596_000, 116825) .saturating_add(T::DbWeight::get().reads(3_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) } @@ -285,8 +290,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `4668` // Estimated: `116825` - // Minimum execution time: 50_485 nanoseconds. - Weight::from_parts(51_601_000, 116825) + // Minimum execution time: 65_461 nanoseconds. + Weight::from_parts(69_624_000, 116825) .saturating_add(T::DbWeight::get().reads(3_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) } @@ -302,8 +307,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `4642` // Estimated: `119314` - // Minimum execution time: 53_075 nanoseconds. - Weight::from_parts(54_014_000, 119314) + // Minimum execution time: 69_848 nanoseconds. + Weight::from_parts(74_480_000, 119314) .saturating_add(T::DbWeight::get().reads(4_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) } @@ -319,8 +324,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `4676` // Estimated: `119314` - // Minimum execution time: 52_916 nanoseconds. - Weight::from_parts(53_716_000, 119314) + // Minimum execution time: 70_042 nanoseconds. + Weight::from_parts(72_912_000, 119314) .saturating_add(T::DbWeight::get().reads(4_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) } @@ -332,8 +337,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `364` // Estimated: `112338` - // Minimum execution time: 21_920 nanoseconds. - Weight::from_parts(22_172_000, 112338) + // Minimum execution time: 23_008 nanoseconds. + Weight::from_parts(23_767_000, 112338) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().writes(2_u64)) } @@ -345,8 +350,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `412` // Estimated: `112338` - // Minimum execution time: 22_094 nanoseconds. - Weight::from_parts(22_314_000, 112338) + // Minimum execution time: 23_550 nanoseconds. + Weight::from_parts(24_081_000, 112338) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().writes(2_u64)) } @@ -356,8 +361,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `310` // Estimated: `2841` - // Minimum execution time: 15_696 nanoseconds. - Weight::from_parts(15_964_000, 2841) + // Minimum execution time: 15_850 nanoseconds. + Weight::from_parts(16_773_000, 2841) .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -371,8 +376,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `412` // Estimated: `114827` - // Minimum execution time: 30_604 nanoseconds. - Weight::from_parts(31_126_000, 114827) + // Minimum execution time: 32_126 nanoseconds. + Weight::from_parts(33_313_000, 114827) .saturating_add(T::DbWeight::get().reads(3_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) } @@ -386,8 +391,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `412` // Estimated: `114827` - // Minimum execution time: 32_961 nanoseconds. - Weight::from_parts(33_295_000, 114827) + // Minimum execution time: 34_698 nanoseconds. + Weight::from_parts(35_802_000, 114827) .saturating_add(T::DbWeight::get().reads(3_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) } @@ -399,8 +404,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `465` // Estimated: `112338` - // Minimum execution time: 27_072 nanoseconds. - Weight::from_parts(27_405_000, 112338) + // Minimum execution time: 28_710 nanoseconds. + Weight::from_parts(29_574_000, 112338) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().writes(2_u64)) } @@ -412,8 +417,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `448` // Estimated: `112338` - // Minimum execution time: 27_056 nanoseconds. - Weight::from_parts(27_768_000, 112338) + // Minimum execution time: 29_030 nanoseconds. + Weight::from_parts(30_308_000, 112338) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().writes(2_u64)) } @@ -425,8 +430,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `465` // Estimated: `112338` - // Minimum execution time: 24_599 nanoseconds. - Weight::from_parts(25_170_000, 112338) + // Minimum execution time: 26_382 nanoseconds. + Weight::from_parts(27_219_000, 112338) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().writes(2_u64)) } @@ -438,8 +443,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `469` // Estimated: `112338` - // Minimum execution time: 23_737 nanoseconds. - Weight::from_parts(24_184_000, 112338) + // Minimum execution time: 25_445 nanoseconds. + Weight::from_parts(26_010_000, 112338) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().writes(2_u64)) } @@ -453,8 +458,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `469` // Estimated: `224358` - // Minimum execution time: 37_880 nanoseconds. - Weight::from_parts(38_537_000, 224358) + // Minimum execution time: 41_064 nanoseconds. + Weight::from_parts(42_895_000, 224358) .saturating_add(T::DbWeight::get().reads(4_u64)) .saturating_add(T::DbWeight::get().writes(4_u64)) } @@ -466,11 +471,39 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `465` // Estimated: `112338` - // Minimum execution time: 26_898 nanoseconds. - Weight::from_parts(27_496_000, 112338) + // Minimum execution time: 29_472 nanoseconds. + Weight::from_parts(30_011_000, 112338) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().writes(2_u64)) } + /// Storage: Referenda ReferendumInfoFor (r:1 w:0) + /// Proof: Referenda ReferendumInfoFor (max_values: None, max_size: Some(366), added: 2841, mode: MaxEncodedLen) + /// Storage: Preimage StatusFor (r:1 w:0) + /// Proof: Preimage StatusFor (max_values: None, max_size: Some(91), added: 2566, mode: MaxEncodedLen) + /// Storage: Referenda MetadataOf (r:0 w:1) + /// Proof: Referenda MetadataOf (max_values: None, max_size: Some(52), added: 2527, mode: MaxEncodedLen) + fn set_some_metadata() -> Weight { + // Proof Size summary in bytes: + // Measured: `454` + // Estimated: `5407` + // Minimum execution time: 19_389 nanoseconds. + Weight::from_parts(20_490_000, 5407) + .saturating_add(T::DbWeight::get().reads(2_u64)) + .saturating_add(T::DbWeight::get().writes(1_u64)) + } + /// Storage: Referenda ReferendumInfoFor (r:1 w:0) + /// Proof: Referenda ReferendumInfoFor (max_values: None, max_size: Some(366), added: 2841, mode: MaxEncodedLen) + /// Storage: Referenda MetadataOf (r:1 w:1) + /// Proof: Referenda MetadataOf (max_values: None, max_size: Some(52), added: 2527, mode: MaxEncodedLen) + fn clear_metadata() -> Weight { + // Proof Size summary in bytes: + // Measured: `387` + // Estimated: `5368` + // Minimum execution time: 18_195 nanoseconds. + Weight::from_parts(19_917_000, 5368) + .saturating_add(T::DbWeight::get().reads(2_u64)) + .saturating_add(T::DbWeight::get().writes(1_u64)) + } } // For backwards compatibility and tests @@ -485,8 +518,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `251` // Estimated: `109996` - // Minimum execution time: 32_207 nanoseconds. - Weight::from_parts(32_639_000, 109996) + // Minimum execution time: 34_540 nanoseconds. + Weight::from_parts(36_144_000, 109996) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) } @@ -498,8 +531,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `536` // Estimated: `221835` - // Minimum execution time: 43_766 nanoseconds. - Weight::from_parts(44_494_000, 221835) + // Minimum execution time: 46_963 nanoseconds. + Weight::from_parts(48_459_000, 221835) .saturating_add(RocksDbWeight::get().reads(3_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) } @@ -513,8 +546,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `3203` // Estimated: `9817` - // Minimum execution time: 41_561 nanoseconds. - Weight::from_parts(42_180_000, 9817) + // Minimum execution time: 55_798 nanoseconds. + Weight::from_parts(58_260_000, 9817) .saturating_add(RocksDbWeight::get().reads(3_u64)) .saturating_add(RocksDbWeight::get().writes(2_u64)) } @@ -528,8 +561,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `3223` // Estimated: `9817` - // Minimum execution time: 41_039 nanoseconds. - Weight::from_parts(41_673_000, 9817) + // Minimum execution time: 53_888 nanoseconds. + Weight::from_parts(57_919_000, 9817) .saturating_add(RocksDbWeight::get().reads(3_u64)) .saturating_add(RocksDbWeight::get().writes(2_u64)) } @@ -543,8 +576,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `536` // Estimated: `224324` - // Minimum execution time: 52_922 nanoseconds. - Weight::from_parts(53_395_000, 224324) + // Minimum execution time: 56_121 nanoseconds. + Weight::from_parts(58_301_000, 224324) .saturating_add(RocksDbWeight::get().reads(4_u64)) .saturating_add(RocksDbWeight::get().writes(4_u64)) } @@ -558,8 +591,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `536` // Estimated: `224324` - // Minimum execution time: 51_050 nanoseconds. - Weight::from_parts(51_736_000, 224324) + // Minimum execution time: 54_237 nanoseconds. + Weight::from_parts(55_681_000, 224324) .saturating_add(RocksDbWeight::get().reads(4_u64)) .saturating_add(RocksDbWeight::get().writes(4_u64)) } @@ -569,8 +602,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `415` // Estimated: `2841` - // Minimum execution time: 24_102 nanoseconds. - Weight::from_parts(24_372_000, 2841) + // Minimum execution time: 25_734 nanoseconds. + Weight::from_parts(26_429_000, 2841) .saturating_add(RocksDbWeight::get().reads(1_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } @@ -580,8 +613,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `405` // Estimated: `2841` - // Minimum execution time: 24_162 nanoseconds. - Weight::from_parts(24_547_000, 2841) + // Minimum execution time: 26_000 nanoseconds. + Weight::from_parts(26_786_000, 2841) .saturating_add(RocksDbWeight::get().reads(1_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } @@ -593,8 +626,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `412` // Estimated: `221835` - // Minimum execution time: 32_247 nanoseconds. - Weight::from_parts(32_731_000, 221835) + // Minimum execution time: 34_567 nanoseconds. + Weight::from_parts(35_939_000, 221835) .saturating_add(RocksDbWeight::get().reads(3_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) } @@ -602,13 +635,15 @@ impl WeightInfo for () { /// Proof: Referenda ReferendumInfoFor (max_values: None, max_size: Some(366), added: 2841, mode: MaxEncodedLen) /// Storage: Scheduler Agenda (r:2 w:2) /// Proof: Scheduler Agenda (max_values: None, max_size: Some(107022), added: 109497, mode: MaxEncodedLen) + /// Storage: Referenda MetadataOf (r:1 w:0) + /// Proof: Referenda MetadataOf (max_values: None, max_size: Some(52), added: 2527, mode: MaxEncodedLen) fn kill() -> Weight { // Proof Size summary in bytes: // Measured: `717` - // Estimated: `221835` - // Minimum execution time: 59_900 nanoseconds. - Weight::from_parts(60_659_000, 221835) - .saturating_add(RocksDbWeight::get().reads(3_u64)) + // Estimated: `224362` + // Minimum execution time: 67_744 nanoseconds. + Weight::from_parts(70_047_000, 224362) + .saturating_add(RocksDbWeight::get().reads(4_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) } /// Storage: Referenda TrackQueue (r:1 w:0) @@ -619,8 +654,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `174` // Estimated: `6976` - // Minimum execution time: 9_322 nanoseconds. - Weight::from_parts(9_638_000, 6976) + // Minimum execution time: 9_886 nanoseconds. + Weight::from_parts(10_406_000, 6976) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } @@ -634,8 +669,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `4661` // Estimated: `226322` - // Minimum execution time: 76_976 nanoseconds. - Weight::from_parts(77_597_000, 226322) + // Minimum execution time: 100_449 nanoseconds. + Weight::from_parts(101_812_000, 226322) .saturating_add(RocksDbWeight::get().reads(4_u64)) .saturating_add(RocksDbWeight::get().writes(4_u64)) } @@ -649,8 +684,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `4661` // Estimated: `226322` - // Minimum execution time: 78_405 nanoseconds. - Weight::from_parts(78_972_000, 226322) + // Minimum execution time: 101_430 nanoseconds. + Weight::from_parts(103_704_000, 226322) .saturating_add(RocksDbWeight::get().reads(4_u64)) .saturating_add(RocksDbWeight::get().writes(4_u64)) } @@ -664,8 +699,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `4682` // Estimated: `116825` - // Minimum execution time: 51_360 nanoseconds. - Weight::from_parts(51_737_000, 116825) + // Minimum execution time: 67_224 nanoseconds. + Weight::from_parts(70_596_000, 116825) .saturating_add(RocksDbWeight::get().reads(3_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) } @@ -679,8 +714,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `4668` // Estimated: `116825` - // Minimum execution time: 50_485 nanoseconds. - Weight::from_parts(51_601_000, 116825) + // Minimum execution time: 65_461 nanoseconds. + Weight::from_parts(69_624_000, 116825) .saturating_add(RocksDbWeight::get().reads(3_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) } @@ -696,8 +731,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `4642` // Estimated: `119314` - // Minimum execution time: 53_075 nanoseconds. - Weight::from_parts(54_014_000, 119314) + // Minimum execution time: 69_848 nanoseconds. + Weight::from_parts(74_480_000, 119314) .saturating_add(RocksDbWeight::get().reads(4_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) } @@ -713,8 +748,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `4676` // Estimated: `119314` - // Minimum execution time: 52_916 nanoseconds. - Weight::from_parts(53_716_000, 119314) + // Minimum execution time: 70_042 nanoseconds. + Weight::from_parts(72_912_000, 119314) .saturating_add(RocksDbWeight::get().reads(4_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) } @@ -726,8 +761,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `364` // Estimated: `112338` - // Minimum execution time: 21_920 nanoseconds. - Weight::from_parts(22_172_000, 112338) + // Minimum execution time: 23_008 nanoseconds. + Weight::from_parts(23_767_000, 112338) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().writes(2_u64)) } @@ -739,8 +774,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `412` // Estimated: `112338` - // Minimum execution time: 22_094 nanoseconds. - Weight::from_parts(22_314_000, 112338) + // Minimum execution time: 23_550 nanoseconds. + Weight::from_parts(24_081_000, 112338) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().writes(2_u64)) } @@ -750,8 +785,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `310` // Estimated: `2841` - // Minimum execution time: 15_696 nanoseconds. - Weight::from_parts(15_964_000, 2841) + // Minimum execution time: 15_850 nanoseconds. + Weight::from_parts(16_773_000, 2841) .saturating_add(RocksDbWeight::get().reads(1_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } @@ -765,8 +800,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `412` // Estimated: `114827` - // Minimum execution time: 30_604 nanoseconds. - Weight::from_parts(31_126_000, 114827) + // Minimum execution time: 32_126 nanoseconds. + Weight::from_parts(33_313_000, 114827) .saturating_add(RocksDbWeight::get().reads(3_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) } @@ -780,8 +815,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `412` // Estimated: `114827` - // Minimum execution time: 32_961 nanoseconds. - Weight::from_parts(33_295_000, 114827) + // Minimum execution time: 34_698 nanoseconds. + Weight::from_parts(35_802_000, 114827) .saturating_add(RocksDbWeight::get().reads(3_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) } @@ -793,8 +828,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `465` // Estimated: `112338` - // Minimum execution time: 27_072 nanoseconds. - Weight::from_parts(27_405_000, 112338) + // Minimum execution time: 28_710 nanoseconds. + Weight::from_parts(29_574_000, 112338) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().writes(2_u64)) } @@ -806,8 +841,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `448` // Estimated: `112338` - // Minimum execution time: 27_056 nanoseconds. - Weight::from_parts(27_768_000, 112338) + // Minimum execution time: 29_030 nanoseconds. + Weight::from_parts(30_308_000, 112338) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().writes(2_u64)) } @@ -819,8 +854,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `465` // Estimated: `112338` - // Minimum execution time: 24_599 nanoseconds. - Weight::from_parts(25_170_000, 112338) + // Minimum execution time: 26_382 nanoseconds. + Weight::from_parts(27_219_000, 112338) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().writes(2_u64)) } @@ -832,8 +867,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `469` // Estimated: `112338` - // Minimum execution time: 23_737 nanoseconds. - Weight::from_parts(24_184_000, 112338) + // Minimum execution time: 25_445 nanoseconds. + Weight::from_parts(26_010_000, 112338) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().writes(2_u64)) } @@ -847,8 +882,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `469` // Estimated: `224358` - // Minimum execution time: 37_880 nanoseconds. - Weight::from_parts(38_537_000, 224358) + // Minimum execution time: 41_064 nanoseconds. + Weight::from_parts(42_895_000, 224358) .saturating_add(RocksDbWeight::get().reads(4_u64)) .saturating_add(RocksDbWeight::get().writes(4_u64)) } @@ -860,9 +895,37 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `465` // Estimated: `112338` - // Minimum execution time: 26_898 nanoseconds. - Weight::from_parts(27_496_000, 112338) + // Minimum execution time: 29_472 nanoseconds. + Weight::from_parts(30_011_000, 112338) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().writes(2_u64)) } + /// Storage: Referenda ReferendumInfoFor (r:1 w:0) + /// Proof: Referenda ReferendumInfoFor (max_values: None, max_size: Some(366), added: 2841, mode: MaxEncodedLen) + /// Storage: Preimage StatusFor (r:1 w:0) + /// Proof: Preimage StatusFor (max_values: None, max_size: Some(91), added: 2566, mode: MaxEncodedLen) + /// Storage: Referenda MetadataOf (r:0 w:1) + /// Proof: Referenda MetadataOf (max_values: None, max_size: Some(52), added: 2527, mode: MaxEncodedLen) + fn set_some_metadata() -> Weight { + // Proof Size summary in bytes: + // Measured: `454` + // Estimated: `5407` + // Minimum execution time: 19_389 nanoseconds. + Weight::from_parts(20_490_000, 5407) + .saturating_add(RocksDbWeight::get().reads(2_u64)) + .saturating_add(RocksDbWeight::get().writes(1_u64)) + } + /// Storage: Referenda ReferendumInfoFor (r:1 w:0) + /// Proof: Referenda ReferendumInfoFor (max_values: None, max_size: Some(366), added: 2841, mode: MaxEncodedLen) + /// Storage: Referenda MetadataOf (r:1 w:1) + /// Proof: Referenda MetadataOf (max_values: None, max_size: Some(52), added: 2527, mode: MaxEncodedLen) + fn clear_metadata() -> Weight { + // Proof Size summary in bytes: + // Measured: `387` + // Estimated: `5368` + // Minimum execution time: 18_195 nanoseconds. + Weight::from_parts(19_917_000, 5368) + .saturating_add(RocksDbWeight::get().reads(2_u64)) + .saturating_add(RocksDbWeight::get().writes(1_u64)) + } } From 2a636e26e9c200bb77910e4956210bce2bef0e22 Mon Sep 17 00:00:00 2001 From: Aaro Altonen <48052676+altonen@users.noreply.github.com> Date: Wed, 8 Feb 2023 11:04:02 +0200 Subject: [PATCH 098/558] Improve test coverage of the `Notifications` protocol (#13033) * Add handler and upgrade tests * Add tests for `behaviour.rs` * Apply review comments * Update dependencies * Apply suggestions from code review Co-authored-by: Dmitry Markin * Apply review comments * Fix clippy * Update mockall * Apply review comment --------- Co-authored-by: Dmitry Markin --- Cargo.lock | 702 +++-- client/consensus/common/Cargo.toml | 2 +- client/network/Cargo.toml | 4 + .../src/protocol/notifications/behaviour.rs | 2417 +++++++++++++++++ .../src/protocol/notifications/handler.rs | 1015 ++++++- .../src/protocol/notifications/upgrade.rs | 4 +- .../protocol/notifications/upgrade/collec.rs | 80 +- .../notifications/upgrade/notifications.rs | 18 +- client/network/sync/Cargo.toml | 2 +- 9 files changed, 3773 insertions(+), 471 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index c870fb0a9..3e5ab65c0 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -27,7 +27,7 @@ version = "0.19.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a76fd60b23679b7d19bd066031410fb7e458ccc5e958eb5c325888ce4baedc97" dependencies = [ - "gimli 0.27.0", + "gimli 0.27.1", ] [[package]] @@ -139,9 +139,9 @@ dependencies = [ [[package]] name = "ahash" -version = "0.8.2" +version = "0.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bf6ccdb167abbf410dcb915cabd428929d7f6a04980b54a11f26a39f1c7f7107" +checksum = "2c99f64d1e06488f620f932677e24bc6e2897582980441ae90a671415bd7ec2f" dependencies = [ "cfg-if", "getrandom 0.2.8", @@ -184,9 +184,9 @@ dependencies = [ [[package]] name = "anyhow" -version = "1.0.68" +version = "1.0.69" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2cb2f989d18dd141ab8ae82f64d1a8cdd37e0840f73a406896cf5e99502fab61" +checksum = "224afbd727c3d6e4b90103ece64b8d1b67fbb1973b1046c2281eed3f3803f800" [[package]] name = "approx" @@ -199,9 +199,9 @@ dependencies = [ [[package]] name = "arbitrary" -version = "1.2.0" +version = "1.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "29d47fbf90d5149a107494b15a7dc8d69b351be2db3bb9691740e88ec17fd880" +checksum = "3e90af4de65aa7b293ef2d09daff88501eb254f58edde2e1ac02c82d873eadad" [[package]] name = "arc-swap" @@ -308,11 +308,11 @@ checksum = "e22d1f4b888c298a027c99dc9048015fac177587de20fc30232a057dfbe24a21" [[package]] name = "assert_cmd" -version = "2.0.7" +version = "2.0.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fa3d466004a8b4cb1bc34044240a2fd29d17607e2e3bd613eb44fd48e8100da3" +checksum = "9834fcc22e0874394a010230586367d4a3e9f11b560f469262678547e1d2575e" dependencies = [ - "bstr 1.1.0", + "bstr", "doc-comment", "predicates", "predicates-core", @@ -379,9 +379,9 @@ dependencies = [ [[package]] name = "async-trait" -version = "0.1.60" +version = "0.1.64" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "677d1d8ab452a3936018a687b20e6f7cf5363d713b732b8884001317b0e48aa3" +checksum = "1cd7fce9ba8c3c042128ce72d8b2ddbf3a05747efb67ea0313c635e10bda47a2" dependencies = [ "proc-macro2", "quote", @@ -403,9 +403,9 @@ dependencies = [ [[package]] name = "atomic-waker" -version = "1.0.0" +version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "065374052e7df7ee4047b1160cca5e1467a12351a40b3da123c870ba0b8eda2a" +checksum = "debc29dde2e69f9e47506b525f639ed42300fc014a3e007832592448fa8e4599" [[package]] name = "atty" @@ -435,7 +435,7 @@ dependencies = [ "cfg-if", "libc", "miniz_oxide", - "object 0.30.0", + "object 0.30.3", "rustc-demangle", ] @@ -463,12 +463,27 @@ version = "0.13.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9e1b586273c5702936fe7b7d6896644d8be71e6314cfe09d3167c95f712589e8" +[[package]] +name = "base64" +version = "0.21.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a4a4ddaa51a5bc52a6948f74c06d20aaaddb71924eab79b8c97a8c556e942d6a" + [[package]] name = "base64ct" version = "1.5.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b645a089122eccb6111b4f81cbc1a49f5900ac4666bb93ac027feaecf15607bf" +[[package]] +name = "basic-toml" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2e819b667739967cd44d308b8c7b71305d8bb0729ac44a248aa08f33d01950b4" +dependencies = [ + "serde", +] + [[package]] name = "beef" version = "0.5.2" @@ -546,7 +561,7 @@ name = "binary-merkle-tree" version = "4.0.0-dev" dependencies = [ "array-bytes", - "env_logger", + "env_logger 0.9.3", "hash-db", "log", "sp-core", @@ -718,18 +733,9 @@ checksum = "771fe0050b883fcc3ea2359b1a96bcfbc090b7116eae7c3c512c7a083fdf23d3" [[package]] name = "bstr" -version = "0.2.17" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ba3569f383e8f1598449f1a423e72e99569137b47740b1da11ef19af3d5c3223" -dependencies = [ - "memchr", -] - -[[package]] -name = "bstr" -version = "1.1.0" +version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b45ea9b00a7b3f2988e9a65ad3917e62123c38dba709b666506207be96d1790b" +checksum = "b7f0778972c64420fdedc63f09919c8a88bda7b25135357fd25a5d9f3257e832" dependencies = [ "memchr", "once_cell", @@ -748,9 +754,9 @@ dependencies = [ [[package]] name = "bumpalo" -version = "3.11.1" +version = "3.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "572f695136211188308f16ad2ca5c851a712c464060ae6974944458eb83880ba" +checksum = "0d261e256854913907f67ed06efbc3338dfe6179796deefc1ff763fc1aee5535" [[package]] name = "byte-slice-cast" @@ -778,9 +784,9 @@ checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610" [[package]] name = "bytes" -version = "1.3.0" +version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dfb24e866b15a1af2a1b663f10c6b6b8f397a84aadb828f12e5b289ec23a3a3c" +checksum = "89b2fd2a0dcf38d7971e2194b6b6eebab45ae01067456a7fd93d5547a61b70be" [[package]] name = "bzip2-sys" @@ -795,9 +801,9 @@ dependencies = [ [[package]] name = "camino" -version = "1.1.1" +version = "1.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "88ad0e1e3e88dd237a156ab9f571021b8a158caa0ae44b1968a241efb5144c1e" +checksum = "c77df041dc383319cc661b428b6961a005db4d6808d5e12536931b1ca9556055" dependencies = [ "serde", ] @@ -813,9 +819,9 @@ dependencies = [ [[package]] name = "cargo_metadata" -version = "0.15.2" +version = "0.15.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "982a0cf6a99c350d7246035613882e376d58cebe571785abc5da4f648d53ac0a" +checksum = "08a1ec454bc3eead8719cb56e15dbbfecdbc14e4b3a3ae4936cc6e31f5fc0d07" dependencies = [ "camino", "cargo-platform", @@ -833,9 +839,9 @@ checksum = "37b2a672a2cb129a2e41c10b1224bb368f9f37a2b16b612598138befd7b37eb5" [[package]] name = "cc" -version = "1.0.78" +version = "1.0.79" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a20104e2335ce8a659d6dd92a51a767a0c062599c73b343fd152cb401e828c3d" +checksum = "50d30906286121d95be3d479533b458f87493b30a4b5f79a607db8f5d11aa91f" dependencies = [ "jobserver", ] @@ -911,7 +917,7 @@ name = "chain-spec-builder" version = "2.0.0" dependencies = [ "ansi_term", - "clap 4.0.32", + "clap 4.1.4", "node-cli", "rand 0.8.5", "sc-chain-spec", @@ -1027,13 +1033,13 @@ dependencies = [ [[package]] name = "clap" -version = "4.0.32" +version = "4.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a7db700bc935f9e43e88d00b0850dae18a63773cfbec6d8e070fccf7fef89a39" +checksum = "f13b9c79b5d1dd500d20ef541215a6423c75829ef43117e1b4d17fd8af0b5d76" dependencies = [ "bitflags", "clap_derive", - "clap_lex 0.3.0", + "clap_lex 0.3.1", "is-terminal", "once_cell", "strsim", @@ -1042,18 +1048,18 @@ dependencies = [ [[package]] name = "clap_complete" -version = "4.0.7" +version = "4.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "10861370d2ba66b0f5989f83ebf35db6421713fd92351790e7fdd6c36774c56b" +checksum = "3d6540eedc41f8a5a76cf3d8d458057dcdf817be4158a55b5f861f7a5483de75" dependencies = [ - "clap 4.0.32", + "clap 4.1.4", ] [[package]] name = "clap_derive" -version = "4.0.21" +version = "4.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0177313f9f02afc995627906bbd8967e2be069f5261954222dac78290c2b9014" +checksum = "684a277d672e91966334af371f1a7b5833f9aa00b07c84e92fbce95e00208ce8" dependencies = [ "heck", "proc-macro-error", @@ -1073,9 +1079,9 @@ dependencies = [ [[package]] name = "clap_lex" -version = "0.3.0" +version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0d4198f73e42b4936b35b5bb248d81d2b595ecb170da0bac7655c54eedfa8da8" +checksum = "783fe232adfca04f90f56201b26d79682d4cd2625e0bc7290b95123afe558ade" dependencies = [ "os_str_bytes", ] @@ -1103,9 +1109,9 @@ dependencies = [ [[package]] name = "concurrent-queue" -version = "2.0.0" +version = "2.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bd7bef69dc86e3c610e4e7aed41035e2a7ed12e72dd7530f61327a6579a4390b" +checksum = "c278839b831783b70278b14df4d45e1beb1aad306c07bb796637de9a0e323e8e" dependencies = [ "crossbeam-utils", ] @@ -1278,18 +1284,18 @@ dependencies = [ [[package]] name = "crc" -version = "3.0.0" +version = "3.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "53757d12b596c16c78b83458d732a5d1a17ab3f53f2f7412f6fb57cc8a140ab3" +checksum = "86ec7a15cbe22e59248fc7eadb1907dab5ba09372595da4d73dd805ed4417dfe" dependencies = [ "crc-catalog", ] [[package]] name = "crc-catalog" -version = "2.1.0" +version = "2.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2d0165d2900ae6778e36e80bbc4da3b5eefccee9ba939761f9c2882a5d9af3ff" +checksum = "9cace84e55f07e7301bae1c519df89cdad8cc3cd868413d3fdbdeca9ff3db484" [[package]] name = "crc32fast" @@ -1495,9 +1501,9 @@ dependencies = [ [[package]] name = "curve25519-dalek" -version = "4.0.0-pre.5" +version = "4.0.0-rc.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "67bc65846be335cb20f4e52d49a437b773a2c1fdb42b19fc84e79e6f6771536f" +checksum = "8da00a7a9a4eb92a0a0f8e75660926d48f0d0f3c537e455c457bcdaa1e16b1ac" dependencies = [ "cfg-if", "fiat-crypto", @@ -1509,9 +1515,9 @@ dependencies = [ [[package]] name = "cxx" -version = "1.0.85" +version = "1.0.89" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5add3fc1717409d029b20c5b6903fc0c0b02fa6741d820054f4a2efa5e5816fd" +checksum = "bc831ee6a32dd495436e317595e639a587aa9907bef96fe6e6abc290ab6204e9" dependencies = [ "cc", "cxxbridge-flags", @@ -1521,9 +1527,9 @@ dependencies = [ [[package]] name = "cxx-build" -version = "1.0.85" +version = "1.0.89" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b4c87959ba14bc6fbc61df77c3fcfe180fc32b93538c4f1031dd802ccb5f2ff0" +checksum = "94331d54f1b1a8895cd81049f7eaaaef9d05a7dcb4d1fd08bf3ff0806246789d" dependencies = [ "cc", "codespan-reporting", @@ -1536,15 +1542,15 @@ dependencies = [ [[package]] name = "cxxbridge-flags" -version = "1.0.85" +version = "1.0.89" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "69a3e162fde4e594ed2b07d0f83c6c67b745e7f28ce58c6df5e6b6bef99dfb59" +checksum = "48dcd35ba14ca9b40d6e4b4b39961f23d835dbb8eed74565ded361d93e1feb8a" [[package]] name = "cxxbridge-macro" -version = "1.0.85" +version = "1.0.89" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3e7e2adeb6a0d4a282e581096b06e1791532b7d576dcde5ccd9382acf55db8e6" +checksum = "81bbeb29798b407ccd82a3324ade1a7286e0d29851475990b612670f6f5124d2" dependencies = [ "proc-macro2", "quote", @@ -1553,9 +1559,9 @@ dependencies = [ [[package]] name = "darling" -version = "0.14.2" +version = "0.14.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b0dd3cd20dc6b5a876612a6e5accfe7f3dd883db6d07acfbf14c128f61550dfa" +checksum = "c0808e1bd8671fb44a113a14e13497557533369847788fa2ae912b6ebfce9fa8" dependencies = [ "darling_core", "darling_macro", @@ -1563,9 +1569,9 @@ dependencies = [ [[package]] name = "darling_core" -version = "0.14.2" +version = "0.14.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a784d2ccaf7c98501746bf0be29b2022ba41fd62a2e622af997a03e9f972859f" +checksum = "001d80444f28e193f30c2f293455da62dcf9a6b29918a4253152ae2b1de592cb" dependencies = [ "fnv", "ident_case", @@ -1577,9 +1583,9 @@ dependencies = [ [[package]] name = "darling_macro" -version = "0.14.2" +version = "0.14.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7618812407e9402654622dd402b0a89dff9ba93badd6540781526117b92aab7e" +checksum = "b36230598a2d5de7ec1c6f51f72d8a99a9208daff41de2084d06e3fd3ea56685" dependencies = [ "darling_core", "quote", @@ -1799,9 +1805,9 @@ dependencies = [ [[package]] name = "dissimilar" -version = "1.0.5" +version = "1.0.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bd5f0c7e4bd266b8ab2550e6238d2e74977c23c15536ac7be45e9c95e2e3fbbb" +checksum = "210ec60ae7d710bed8683e333e9d2855a8a56a3e9892b38bad3bb0d4d29b0d5e" [[package]] name = "doc-comment" @@ -1868,9 +1874,9 @@ dependencies = [ [[package]] name = "ed25519" -version = "1.5.2" +version = "1.5.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e9c280362032ea4203659fc489832d0204ef09f247a0506f170dafcac08c369" +checksum = "91cff35c70bba8a626e3185d8cd48cc11b5437e1a5bcd15b9b5fa3c64b6dfee7" dependencies = [ "signature", ] @@ -1905,9 +1911,9 @@ dependencies = [ [[package]] name = "either" -version = "1.8.0" +version = "1.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "90e5c1c8368803113bf0c9584fc495a58b86dc8a29edbf8fe877d21d9507e797" +checksum = "7fcaabb2fef8c910e7f4c7ce9f67a1283a1715879a7c230ca9d6d1ae31f16d91" [[package]] name = "elliptic-curve" @@ -1976,6 +1982,19 @@ dependencies = [ "termcolor", ] +[[package]] +name = "env_logger" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85cdab6a89accf66733ad5a1693a4dcced6aeff64602b634530dd73c1f3ee9f0" +dependencies = [ + "humantime", + "is-terminal", + "log", + "regex", + "termcolor", +] + [[package]] name = "environmental" version = "1.1.4" @@ -2066,11 +2085,11 @@ checksum = "a214f5bb88731d436478f3ae1f8a277b62124089ba9fb67f4f93fb100ef73c90" [[package]] name = "file-per-thread-logger" -version = "0.1.5" +version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "21e16290574b39ee41c71aeb90ae960c504ebaf1e2a1c87bd52aa56ed6e1a02f" +checksum = "84f2e425d9790201ba4af4630191feac6dcc98765b118d4d18e91d23c2353866" dependencies = [ - "env_logger", + "env_logger 0.10.0", "log", ] @@ -2203,7 +2222,7 @@ dependencies = [ "Inflector", "array-bytes", "chrono", - "clap 4.0.32", + "clap 4.1.4", "comfy-table", "frame-benchmarking", "frame-support", @@ -2294,7 +2313,7 @@ dependencies = [ name = "frame-election-solution-type-fuzzer" version = "2.0.0-alpha.5" dependencies = [ - "clap 4.0.32", + "clap 4.1.4", "frame-election-provider-solution-type", "frame-election-provider-support", "frame-support", @@ -2539,9 +2558,9 @@ dependencies = [ [[package]] name = "fs_extra" -version = "1.2.0" +version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2022715d62ab30faffd124d40b76f4134a550a87792276512b18d63272333394" +checksum = "42703706b716c37f96a77aea830392ad231f44c9e9a67872fa5548707e11b11c" [[package]] name = "funty" @@ -2551,9 +2570,9 @@ checksum = "e6d5a32815ae3f33302d95fdcb2ce17862f8c65363dcfd29360480ba1001fc9c" [[package]] name = "futures" -version = "0.3.25" +version = "0.3.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "38390104763dc37a5145a53c29c63c1290b5d316d6086ec32c293f6736051bb0" +checksum = "13e2792b0ff0340399d58445b88fd9770e3489eff258a4cbc1523418f12abf84" dependencies = [ "futures-channel", "futures-core", @@ -2566,9 +2585,9 @@ dependencies = [ [[package]] name = "futures-channel" -version = "0.3.25" +version = "0.3.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "52ba265a92256105f45b719605a571ffe2d1f0fea3807304b522c1d778f79eed" +checksum = "2e5317663a9089767a1ec00a487df42e0ca174b61b4483213ac24448e4664df5" dependencies = [ "futures-core", "futures-sink", @@ -2576,15 +2595,15 @@ dependencies = [ [[package]] name = "futures-core" -version = "0.3.25" +version = "0.3.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "04909a7a7e4633ae6c4a9ab280aeb86da1236243a77b694a49eacd659a4bd3ac" +checksum = "ec90ff4d0fe1f57d600049061dc6bb68ed03c7d2fbd697274c41805dcb3f8608" [[package]] name = "futures-executor" -version = "0.3.25" +version = "0.3.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7acc85df6714c176ab5edf386123fafe217be88c0840ec11f199441134a074e2" +checksum = "e8de0a35a6ab97ec8869e32a2473f4b1324459e14c29275d14b10cb1fd19b50e" dependencies = [ "futures-core", "futures-task", @@ -2594,9 +2613,9 @@ dependencies = [ [[package]] name = "futures-io" -version = "0.3.25" +version = "0.3.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "00f5fb52a06bdcadeb54e8d3671f8888a39697dcb0b81b23b55174030427f4eb" +checksum = "bfb8371b6fb2aeb2d280374607aeabfc99d95c72edfe51692e42d3d7f0d08531" [[package]] name = "futures-lite" @@ -2615,9 +2634,9 @@ dependencies = [ [[package]] name = "futures-macro" -version = "0.3.25" +version = "0.3.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bdfb8ce053d86b91919aad980c220b1fb8401a9394410e1c289ed7e66b61835d" +checksum = "95a73af87da33b5acf53acfebdc339fe592ecf5357ac7c0a7734ab9d8c876a70" dependencies = [ "proc-macro2", "quote", @@ -2631,21 +2650,21 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d2411eed028cdf8c8034eaf21f9915f956b6c3abec4d4c7949ee67f0721127bd" dependencies = [ "futures-io", - "rustls 0.20.7", + "rustls 0.20.8", "webpki 0.22.0", ] [[package]] name = "futures-sink" -version = "0.3.25" +version = "0.3.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "39c15cf1a4aa79df40f1bb462fb39676d0ad9e366c2a33b590d7c66f4f81fcf9" +checksum = "f310820bb3e8cfd46c80db4d7fb8353e15dfff853a127158425f31e0be6c8364" [[package]] name = "futures-task" -version = "0.3.25" +version = "0.3.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2ffb393ac5d9a6eaa9d3fdf37ae2776656b706e200c8e16b1bdb227f5198e6ea" +checksum = "dcf79a1bf610b10f42aea489289c5a2c478a786509693b80cd39c44ccd936366" [[package]] name = "futures-timer" @@ -2655,9 +2674,9 @@ checksum = "e64b03909df88034c26dc1547e8970b91f98bdb65165d6a4e9110d94263dbb2c" [[package]] name = "futures-util" -version = "0.3.25" +version = "0.3.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "197676987abd2f9cadff84926f410af1c183608d36641465df73ae8211dc65d6" +checksum = "9c1d6de3acfef38d2be4b1f543f553131788603495be83da675e180c8d6b7bd1" dependencies = [ "futures-channel", "futures-core", @@ -2777,15 +2796,15 @@ dependencies = [ [[package]] name = "gimli" -version = "0.27.0" +version = "0.27.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dec7af912d60cdbd3677c1af9352ebae6fb8394d165568a2234df0fa00f87793" +checksum = "221996f774192f0f718773def8201c4ae31f02616a54ccfc2d358bb0e5cefdec" [[package]] name = "git2" -version = "0.16.0" +version = "0.16.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "be36bc9e0546df253c0cc41fd0af34f5e92845ad8509462ec76672fac6997f5b" +checksum = "ccf7f68c2995f392c49fffb4f95ae2c873297830eb25c6bc4c114ce8f4562acc" dependencies = [ "bitflags", "libc", @@ -2796,18 +2815,18 @@ dependencies = [ [[package]] name = "glob" -version = "0.3.0" +version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9b919933a397b79c37e33b77bb2aa3dc8eb6e165ad809e58ff75bc7db2e34574" +checksum = "d2fabcfbdc87f4758337ca535fb41a6d701b65693ce38287d856d1674551ec9b" [[package]] name = "globset" -version = "0.4.9" +version = "0.4.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0a1e17342619edbc21a964c2afbeb6c820c6a2560032872f397bb97ea127bd0a" +checksum = "029d74589adefde59de1a0c4f4732695c32805624aec7b68d91503d4dba79afc" dependencies = [ "aho-corasick", - "bstr 0.2.17", + "bstr", "fnv", "log", "regex", @@ -2895,9 +2914,9 @@ checksum = "43a3c133739dddd0d2990f9a4bdf8eb4b21ef50e4851ca85ab661199821d510e" [[package]] name = "heck" -version = "0.4.0" +version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2540771e65fc8cb83cd6e8a237f70c319bd5c29f78ed1084ba5d50eeac86f7f9" +checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8" [[package]] name = "hermit-abi" @@ -2917,6 +2936,12 @@ dependencies = [ "libc", ] +[[package]] +name = "hermit-abi" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "856b5cb0902c2b6d65d5fd97dfa30f9b70c7538e770b98eab5ed52d8db923e01" + [[package]] name = "hex" version = "0.4.3" @@ -3053,9 +3078,9 @@ checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4" [[package]] name = "hyper" -version = "0.14.23" +version = "0.14.24" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "034711faac9d2166cb1baf1a2fb0b60b1f277f8492fd72176c17f3515e1abd3c" +checksum = "5e011372fa0b68db8350aa7a248930ecc7839bf46d8485577d69f117a75f164c" dependencies = [ "bytes", "futures-channel", @@ -3084,7 +3109,7 @@ dependencies = [ "http", "hyper", "log", - "rustls 0.20.7", + "rustls 0.20.8", "rustls-native-certs", "tokio", "tokio-rustls", @@ -3255,12 +3280,12 @@ dependencies = [ [[package]] name = "io-lifetimes" -version = "1.0.3" +version = "1.0.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "46112a93252b123d31a119a8d1a1ac19deac4fac6e0e8b0df58f0d4e5870e63c" +checksum = "1abeb7a0dd0f8181267ff8adc397075586500b81b28a73e8a0208b00fc170fb3" dependencies = [ "libc", - "windows-sys 0.42.0", + "windows-sys 0.45.0", ] [[package]] @@ -3283,20 +3308,20 @@ dependencies = [ [[package]] name = "ipnet" -version = "2.7.0" +version = "2.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "11b0d96e660696543b251e58030cf9787df56da39dab19ad60eae7353040917e" +checksum = "30e22bd8629359895450b59ea7a776c850561b96a3b1d31321c1949d9e6c9146" [[package]] name = "is-terminal" -version = "0.4.2" +version = "0.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "28dfb6c8100ccc63462345b67d1bbc3679177c75ee4bf59bf29c8b1d110b8189" +checksum = "22e18b0a45d56fe973d6db23972bf5bc46f988a4a2385deac9cc29572f09daef" dependencies = [ - "hermit-abi 0.2.6", + "hermit-abi 0.3.0", "io-lifetimes", "rustix", - "windows-sys 0.42.0", + "windows-sys 0.45.0", ] [[package]] @@ -3325,9 +3350,9 @@ dependencies = [ [[package]] name = "js-sys" -version = "0.3.60" +version = "0.3.61" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "49409df3e3bf0856b916e2ceaca09ee28e6871cf7d9ce97a692cacfdb2a25a47" +checksum = "445dde2150c55e483f3d8416706b97ec8e8237c307e5b7b4b8dd15e6af2a0730" dependencies = [ "wasm-bindgen", ] @@ -3643,9 +3668,9 @@ checksum = "201de327520df007757c1f0adce6e827fe8562fbc28bfd9c15571c66ca1f5f79" [[package]] name = "libgit2-sys" -version = "0.14.1+1.5.0" +version = "0.14.2+1.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4a07fb2692bc3593bda59de45a502bb3071659f2c515e28c71e728306b038e17" +checksum = "7f3d95f6b51075fe9810a7ae22c7095f12b98005ab364d8544797a825ce946a4" dependencies = [ "cc", "libc", @@ -3913,7 +3938,7 @@ dependencies = [ "parking_lot 0.12.1", "quinn-proto", "rand 0.8.5", - "rustls 0.20.7", + "rustls 0.20.8", "thiserror", "tokio", ] @@ -3996,7 +4021,7 @@ dependencies = [ "libp2p-core", "rcgen 0.10.0", "ring", - "rustls 0.20.7", + "rustls 0.20.8", "thiserror", "webpki 0.22.0", "x509-parser 0.14.0", @@ -4103,7 +4128,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "95b09eff1b35ed3b33b877ced3a691fc7a481919c7e29c53c906226fcf55e2a1" dependencies = [ "arrayref", - "base64", + "base64 0.13.1", "digest 0.9.0", "hmac-drbg", "libsecp256k1-core", @@ -4305,9 +4330,9 @@ dependencies = [ [[package]] name = "matches" -version = "0.1.9" +version = "0.1.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a3e378b66a060d48947b590737b30a1be76706c8dd7b8ba0f2fe3989c68a853f" +checksum = "2532096657941c2fea9c289d370a250971c689d4f143798ff67113ec042024a5" [[package]] name = "matrixmultiply" @@ -4538,9 +4563,9 @@ dependencies = [ [[package]] name = "multihash-derive" -version = "0.8.0" +version = "0.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fc076939022111618a5026d3be019fd8b366e76314538ff9a1b59ffbcbf98bcd" +checksum = "1d6d4752e6230d8ef7adf7bd5d8c4b1f6561c1014c5ba9a37445ccefe18aa1db" dependencies = [ "proc-macro-crate", "proc-macro-error", @@ -4634,9 +4659,9 @@ dependencies = [ [[package]] name = "netlink-packet-utils" -version = "0.5.1" +version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "25af9cf0dc55498b7bd94a1508af7a78706aa0ab715a73c5169273e03c84845e" +checksum = "0ede8a08c71ad5a95cdd0e4e52facd37190977039a4704eb82a283f713747d34" dependencies = [ "anyhow", "byteorder", @@ -4661,9 +4686,9 @@ dependencies = [ [[package]] name = "netlink-sys" -version = "0.8.3" +version = "0.8.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "92b654097027250401127914afb37cb1f311df6610a9891ff07a757e94199027" +checksum = "260e21fbb6f3d253a14df90eb0000a6066780a15dd901a7519ce02d77a94985b" dependencies = [ "bytes", "futures", @@ -4686,9 +4711,9 @@ dependencies = [ [[package]] name = "nix" -version = "0.26.1" +version = "0.26.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "46a58d1d356c6597d08cde02c2f09d785b09e28711837b1ed667dc652c08a694" +checksum = "bfdda3d196821d6af13126e40375cdf7da646a96114af134d5f417a9a1dc8e1a" dependencies = [ "bitflags", "cfg-if", @@ -4703,7 +4728,7 @@ name = "node-bench" version = "0.9.0-dev" dependencies = [ "array-bytes", - "clap 4.0.32", + "clap 4.1.4", "derive_more", "fs_extra", "futures", @@ -4740,7 +4765,7 @@ version = "3.0.0-dev" dependencies = [ "array-bytes", "assert_cmd", - "clap 4.0.32", + "clap 4.1.4", "clap_complete", "criterion", "frame-benchmarking-cli", @@ -4750,7 +4775,7 @@ dependencies = [ "jsonrpsee", "kitchensink-runtime", "log", - "nix 0.26.1", + "nix 0.26.2", "node-executor", "node-inspect", "node-primitives", @@ -4859,7 +4884,7 @@ dependencies = [ name = "node-inspect" version = "0.9.0-dev" dependencies = [ - "clap 4.0.32", + "clap 4.1.4", "parity-scale-codec", "sc-cli", "sc-client-api", @@ -4918,7 +4943,7 @@ dependencies = [ name = "node-runtime-generate-bags" version = "3.0.0" dependencies = [ - "clap 4.0.32", + "clap 4.1.4", "generate-bags", "kitchensink-runtime", ] @@ -4927,7 +4952,7 @@ dependencies = [ name = "node-template" version = "4.0.0-dev" dependencies = [ - "clap 4.0.32", + "clap 4.1.4", "frame-benchmarking", "frame-benchmarking-cli", "frame-system", @@ -5046,9 +5071,9 @@ checksum = "2bf50223579dc7cdcfb3bfcacf7069ff68243f8c363f62ffa99cf000a6b9c451" [[package]] name = "nom" -version = "7.1.2" +version = "7.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e5507769c4919c998e69e49c839d9dc6e693ede4cc4290d6ad8b41d4f09c548c" +checksum = "d273983c5a657a70a3e8f2a01329822f3b8c8172b73826411a55751e404a0a4a" dependencies = [ "memchr", "minimal-lexical", @@ -5083,9 +5108,9 @@ dependencies = [ [[package]] name = "num-complex" -version = "0.4.2" +version = "0.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7ae39348c8bc5fbd7f40c727a9925f03517afd2ab27d46702108b6a7e5414c19" +checksum = "02e0d21255c828d6f128a1e41534206671e8c3ea0c62f32291e808dc82cff17d" dependencies = [ "num-traits", ] @@ -5156,9 +5181,9 @@ dependencies = [ [[package]] name = "object" -version = "0.30.0" +version = "0.30.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "239da7f290cfa979f43f85a8efeee9a8a76d0827c356d37f9d3d7254d6b537fb" +checksum = "ea86265d3d3dcb6a27fc51bd29a4bf387fae9d2986b823079d4986af253eb439" dependencies = [ "memchr", ] @@ -5582,7 +5607,7 @@ dependencies = [ "array-bytes", "assert_matches", "bitflags", - "env_logger", + "env_logger 0.9.3", "frame-benchmarking", "frame-support", "frame-system", @@ -5947,7 +5972,7 @@ name = "pallet-mmr" version = "4.0.0-dev" dependencies = [ "array-bytes", - "env_logger", + "env_logger 0.9.3", "frame-benchmarking", "frame-support", "frame-system", @@ -6787,7 +6812,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3742b2c103b9f06bc9fff0a37ff4912935851bee6d36f3c02bcc755bcfec228f" dependencies = [ "lock_api", - "parking_lot_core 0.9.5", + "parking_lot_core 0.9.7", ] [[package]] @@ -6806,15 +6831,15 @@ dependencies = [ [[package]] name = "parking_lot_core" -version = "0.9.5" +version = "0.9.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7ff9f3fef3968a3ec5945535ed654cb38ff72d7495a25619e2247fb15a2ed9ba" +checksum = "9069cbb9f99e3a5083476ccb29ceb1de18b9118cafa53e90c9551235de2b9521" dependencies = [ "cfg-if", "libc", "redox_syscall", "smallvec", - "windows-sys 0.42.0", + "windows-sys 0.45.0", ] [[package]] @@ -6849,11 +6874,11 @@ checksum = "19b17cddbe7ec3f8bc800887bab5e717348c95ea2ca0b1bf0837fb964dc67099" [[package]] name = "pem" -version = "1.1.0" +version = "1.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "03c64931a1a212348ec4f3b4362585eca7159d0d09cbdf4a7f74f02173596fd4" +checksum = "a8835c273a76a90455d7344889b0964598e3316e2a79ede8e36f16bdcf2228b8" dependencies = [ - "base64", + "base64 0.13.1", ] [[package]] @@ -6873,9 +6898,9 @@ checksum = "478c572c3d73181ff3c2539045f6eb99e5491218eae919370993b890cdbdd98e" [[package]] name = "pest" -version = "2.5.2" +version = "2.5.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0f6e86fb9e7026527a0d46bc308b841d73170ef8f443e1807f6ef88526a816d4" +checksum = "4ab62d2fa33726dbe6321cc97ef96d8cde531e3eeaf858a058de53a8a6d40d8f" dependencies = [ "thiserror", "ucd-trie", @@ -6883,9 +6908,9 @@ dependencies = [ [[package]] name = "pest_derive" -version = "2.5.2" +version = "2.5.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "96504449aa860c8dcde14f9fba5c58dc6658688ca1fe363589d6327b8662c603" +checksum = "8bf026e2d0581559db66d837fe5242320f525d85c76283c61f4d51a1238d65ea" dependencies = [ "pest", "pest_generator", @@ -6893,9 +6918,9 @@ dependencies = [ [[package]] name = "pest_generator" -version = "2.5.2" +version = "2.5.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "798e0220d1111ae63d66cb66a5dcb3fc2d986d520b98e49e1852bfdb11d7c5e7" +checksum = "2b27bd18aa01d91c8ed2b61ea23406a676b42d82609c6e2581fba42f0c15f17f" dependencies = [ "pest", "pest_meta", @@ -6906,20 +6931,20 @@ dependencies = [ [[package]] name = "pest_meta" -version = "2.5.2" +version = "2.5.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "984298b75898e30a843e278a9f2452c31e349a073a0ce6fd950a12a74464e065" +checksum = "9f02b677c1859756359fc9983c2e56a0237f18624a3789528804406b7e915e5d" dependencies = [ "once_cell", "pest", - "sha1", + "sha2 0.10.6", ] [[package]] name = "petgraph" -version = "0.6.2" +version = "0.6.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e6d5014253a1331579ce62aa67443b4a658c5e7dd03d4bc6d302b94474888143" +checksum = "4dd7d28ee937e54fe3080c91faa1c3a46c06de6252988a7f4592ba2310ef22a4" dependencies = [ "fixedbitset", "indexmap", @@ -7117,9 +7142,9 @@ dependencies = [ [[package]] name = "prettyplease" -version = "0.1.22" +version = "0.1.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2c8992a85d8e93a28bdf76137db888d3874e3b230dee5ed8bebac4c9f7617773" +checksum = "e97e3215779627f01ee256d2fad52f3d95e8e1c11e9fc6fd08f7cd455d5d5c78" dependencies = [ "proc-macro2", "syn", @@ -7140,11 +7165,10 @@ dependencies = [ [[package]] name = "proc-macro-crate" -version = "1.2.1" +version = "1.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eda0fc3b0fb7c975631757e14d9049da17374063edb6ebbcbc54d880d4fe94e9" +checksum = "e17d47ce914bf4de440332250b0edd23ce48c005f59fab39d3335866b114f11a" dependencies = [ - "once_cell", "thiserror", "toml", ] @@ -7175,9 +7199,9 @@ dependencies = [ [[package]] name = "proc-macro2" -version = "1.0.50" +version = "1.0.51" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6ef7d57beacfaf2d8aee5937dab7b7f28de3cb8b1828479bb5de2a7106f2bae2" +checksum = "5d727cae5b39d21da60fa540906919ad737832fe0b1c165da3a34d6548c849d6" dependencies = [ "unicode-ident", ] @@ -7221,9 +7245,9 @@ dependencies = [ [[package]] name = "prost" -version = "0.11.5" +version = "0.11.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c01db6702aa05baa3f57dec92b8eeeeb4cb19e894e73996b32a4093289e54592" +checksum = "21dc42e00223fc37204bd4aa177e69420c604ca4a183209a8f9de30c6d934698" dependencies = [ "bytes", "prost-derive", @@ -7231,9 +7255,9 @@ dependencies = [ [[package]] name = "prost-build" -version = "0.11.5" +version = "0.11.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cb5320c680de74ba083512704acb90fe00f28f79207286a848e730c45dd73ed6" +checksum = "a3f8ad728fb08fe212df3c05169e940fbb6d9d16a877ddde14644a983ba2012e" dependencies = [ "bytes", "heck", @@ -7266,9 +7290,9 @@ dependencies = [ [[package]] name = "prost-derive" -version = "0.11.5" +version = "0.11.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c8842bad1a5419bca14eac663ba798f6bc19c413c2fdceb5f3ba3b0932d96720" +checksum = "8bda8c0881ea9f722eb9629376db3d0b903b462477c1aafcb0566610ac28ac5d" dependencies = [ "anyhow", "itertools", @@ -7279,9 +7303,9 @@ dependencies = [ [[package]] name = "prost-types" -version = "0.11.5" +version = "0.11.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "017f79637768cde62820bc2d4fe0e45daaa027755c323ad077767c6c5f173091" +checksum = "a5e0526209433e96d83d750dd81a99118edbc55739e7e61a46764fd2ad537788" dependencies = [ "bytes", "prost", @@ -7332,7 +7356,7 @@ dependencies = [ "rand 0.8.5", "ring", "rustc-hash", - "rustls 0.20.7", + "rustls 0.20.8", "slab", "thiserror", "tinyvec", @@ -7463,9 +7487,9 @@ dependencies = [ [[package]] name = "rayon-core" -version = "1.10.1" +version = "1.10.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cac410af5d00ab6884528b4ab69d1e8e146e8d471201800fa1b4524126de6ad3" +checksum = "356a0625f1954f730c0201cdab48611198dc6ce21f4acff55089b5a78e6e835b" dependencies = [ "crossbeam-channel", "crossbeam-deque", @@ -7552,9 +7576,9 @@ dependencies = [ [[package]] name = "regex" -version = "1.7.0" +version = "1.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e076559ef8e241f2ae3479e36f97bd5741c0330689e217ad51ce2c76808b868a" +checksum = "48aaa5748ba571fb95cd2c85c09f629215d3a6ece942baa100950af03a34f733" dependencies = [ "aho-corasick", "memchr", @@ -7739,16 +7763,16 @@ dependencies = [ [[package]] name = "rustix" -version = "0.36.6" +version = "0.36.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4feacf7db682c6c329c4ede12649cd36ecab0f3be5b7d74e6a20304725db4549" +checksum = "f43abb88211988493c1abb44a70efa56ff0ce98f233b7b276146f1f3f7ba9644" dependencies = [ "bitflags", "errno", "io-lifetimes", "libc", "linux-raw-sys", - "windows-sys 0.42.0", + "windows-sys 0.45.0", ] [[package]] @@ -7757,7 +7781,7 @@ version = "0.19.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "35edb675feee39aec9c99fa5ff985081995a06d594114ae14cbe797ad7b7a6d7" dependencies = [ - "base64", + "base64 0.13.1", "log", "ring", "sct 0.6.1", @@ -7766,9 +7790,9 @@ dependencies = [ [[package]] name = "rustls" -version = "0.20.7" +version = "0.20.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "539a2bfe908f471bfa933876bd1eb6a19cf2176d375f82ef7f99530a40e48c2c" +checksum = "fff78fc74d175294f4e83b28343315ffcfb114b156f0185e9741cb5570f50e2f" dependencies = [ "log", "ring", @@ -7790,11 +7814,11 @@ dependencies = [ [[package]] name = "rustls-pemfile" -version = "1.0.1" +version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0864aeff53f8c05aa08d86e5ef839d3dfcf07aeba2db32f12db0ef716e87bd55" +checksum = "d194b56d58803a43635bdc398cd17e383d6f71f9182b9a192c127ca42494a59b" dependencies = [ - "base64", + "base64 0.21.0", ] [[package]] @@ -7968,7 +7992,7 @@ version = "0.10.0-dev" dependencies = [ "array-bytes", "chrono", - "clap 4.0.32", + "clap 4.1.4", "fdlimit", "futures", "futures-timer", @@ -8305,7 +8329,7 @@ dependencies = [ "array-bytes", "assert_matches", "criterion", - "env_logger", + "env_logger 0.9.3", "lru", "num_cpus", "parity-scale-codec", @@ -8389,7 +8413,7 @@ dependencies = [ name = "sc-finality-grandpa" version = "0.10.0-dev" dependencies = [ - "ahash 0.8.2", + "ahash 0.8.3", "array-bytes", "assert_matches", "async-trait", @@ -8502,6 +8526,8 @@ dependencies = [ "libp2p", "log", "lru", + "mockall", + "multistream-select", "parity-scale-codec", "parking_lot 0.12.1", "pin-project", @@ -8530,6 +8556,7 @@ dependencies = [ "tempfile", "thiserror", "tokio", + "tokio-test", "tokio-util", "unsigned-varint", "zeroize", @@ -8589,7 +8616,7 @@ dependencies = [ name = "sc-network-gossip" version = "0.10.0-dev" dependencies = [ - "ahash 0.8.2", + "ahash 0.8.3", "futures", "futures-timer", "libp2p", @@ -8774,7 +8801,7 @@ name = "sc-rpc" version = "4.0.0-dev" dependencies = [ "assert_matches", - "env_logger", + "env_logger 0.9.3", "futures", "jsonrpsee", "log", @@ -8999,10 +9026,10 @@ dependencies = [ name = "sc-storage-monitor" version = "0.1.0" dependencies = [ - "clap 4.0.32", + "clap 4.1.4", "futures", "log", - "nix 0.26.1", + "nix 0.26.2", "sc-client-db", "sc-utils", "sp-core", @@ -9196,12 +9223,11 @@ dependencies = [ [[package]] name = "schannel" -version = "0.1.20" +version = "0.1.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "88d6731146462ea25d9244b2ed5fd1d716d25c52e4d54aa4fb0f3c4e9854dbe2" +checksum = "713cfb06c7059f3588fb8044c0fad1d09e3c01d225e25b9220dbfdcf16dbb1b3" dependencies = [ - "lazy_static", - "windows-sys 0.36.1", + "windows-sys 0.42.0", ] [[package]] @@ -9210,7 +9236,7 @@ version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "772575a524feeb803e5b0fcbc6dd9f367e579488197c94c6e4023aad2305774d" dependencies = [ - "ahash 0.8.2", + "ahash 0.8.3", "cfg-if", "hashbrown 0.13.2", ] @@ -9293,9 +9319,9 @@ dependencies = [ [[package]] name = "secp256k1" -version = "0.24.2" +version = "0.24.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d9512ffd81e3a3503ed401f79c33168b9148c75038956039166cd750eaa037c3" +checksum = "6b1629c9c557ef9b293568b338dddfc8208c98a18c59d722a9d53f859d9c9b62" dependencies = [ "secp256k1-sys", ] @@ -9320,9 +9346,9 @@ dependencies = [ [[package]] name = "security-framework" -version = "2.7.0" +version = "2.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2bc1bb97804af6631813c55739f771071e0f2ed33ee20b68c86ec505d906356c" +checksum = "a332be01508d814fed64bf28f798a146d73792121129962fdf335bb3c49a4254" dependencies = [ "bitflags", "core-foundation", @@ -9333,9 +9359,9 @@ dependencies = [ [[package]] name = "security-framework-sys" -version = "2.6.1" +version = "2.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0160a13a177a45bfb43ce71c01580998474f556ad854dcbca936dd2841a5c556" +checksum = "31c9bb296072e961fcbd8853511dd39c2d8be2deb1e17c6860b1d30732b323b4" dependencies = [ "core-foundation-sys", "libc", @@ -9396,9 +9422,9 @@ dependencies = [ [[package]] name = "serde_json" -version = "1.0.91" +version = "1.0.92" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "877c235533714907a8c2464236f5c4b2a17262ef1bd71f38f35ea592c8da6883" +checksum = "7434af0dc1cbd59268aa98b4c22c131c0584d2232f6fb166efb993e2832e896a" dependencies = [ "itoa", "ryu", @@ -9418,17 +9444,6 @@ dependencies = [ "opaque-debug 0.3.0", ] -[[package]] -name = "sha1" -version = "0.10.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f04293dc80c3993519f2d7f6f511707ee7094fe0c6d3406feb330cdb3540eba3" -dependencies = [ - "cfg-if", - "cpufeatures", - "digest 0.10.6", -] - [[package]] name = "sha2" version = "0.8.2" @@ -9551,14 +9566,14 @@ checksum = "5e9f0ab6ef7eb7353d9119c170a436d1bf248eea575ac42d19d12f4e34130831" [[package]] name = "snow" -version = "0.9.0" +version = "0.9.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "774d05a3edae07ce6d68ea6984f3c05e9bba8927e3dd591e3b479e5b03213d0d" +checksum = "12ba5f4d4ff12bdb6a169ed51b7c48c0e0ac4b0b4b31012b2571e97d78d3201d" dependencies = [ "aes-gcm 0.9.4", "blake2", "chacha20poly1305", - "curve25519-dalek 4.0.0-pre.5", + "curve25519-dalek 4.0.0-rc.0", "rand_core 0.6.4", "ring", "rustc_version 0.4.0", @@ -9582,7 +9597,7 @@ version = "0.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "41d1c5305e39e09653383c2c7244f2f78b3bcae37cf50c64cb4789c9f5096ec2" dependencies = [ - "base64", + "base64 0.13.1", "bytes", "flate2", "futures", @@ -10070,7 +10085,7 @@ dependencies = [ name = "sp-npos-elections-fuzzer" version = "2.0.0-alpha.5" dependencies = [ - "clap 4.0.32", + "clap 4.1.4", "honggfuzz", "parity-scale-codec", "rand 0.8.5", @@ -10342,7 +10357,7 @@ dependencies = [ name = "sp-trie" version = "7.0.0" dependencies = [ - "ahash 0.8.2", + "ahash 0.8.3", "array-bytes", "criterion", "hash-db", @@ -10443,9 +10458,9 @@ dependencies = [ [[package]] name = "ss58-registry" -version = "1.36.0" +version = "1.38.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "23d92659e7d18d82b803824a9ba5a6022cff101c3491d027c1c1d8d30e749284" +checksum = "e40c020d72bc0a9c5660bb71e4a6fdef081493583062c474740a7d59f55f0e7b" dependencies = [ "Inflector", "num-format", @@ -10530,7 +10545,7 @@ version = "0.4.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a7e94b1ec00bad60e6410e058b52f1c66de3dc5fe4d62d09b3e52bb7d3b73e25" dependencies = [ - "base64", + "base64 0.13.1", "crc", "lazy_static", "md-5", @@ -10547,7 +10562,7 @@ dependencies = [ name = "subkey" version = "2.0.2" dependencies = [ - "clap 4.0.32", + "clap 4.1.4", "sc-cli", ] @@ -10575,7 +10590,7 @@ dependencies = [ name = "substrate-frame-cli" version = "4.0.0-dev" dependencies = [ - "clap 4.0.32", + "clap 4.1.4", "frame-support", "frame-system", "sc-cli", @@ -10905,9 +10920,9 @@ dependencies = [ [[package]] name = "termcolor" -version = "1.1.3" +version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bab24d30b911b2376f3a13cc2cd443142f0c81dda04c118693e35b3835757755" +checksum = "be55cf8942feac5c765c2c993422806843c9a9a45d4d5c407ad6dd2ea95eb9b6" dependencies = [ "winapi-util", ] @@ -10970,12 +10985,11 @@ dependencies = [ [[package]] name = "tikv-jemalloc-sys" -version = "0.5.2+5.3.0-patched" +version = "0.5.3+5.3.0-patched" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ec45c14da997d0925c7835883e4d5c181f196fa142f8c19d7643d1e9af2592c3" +checksum = "a678df20055b43e57ef8cddde41cdfda9a3c1a060b67f4c5836dfb1d78543ba8" dependencies = [ "cc", - "fs_extra", "libc", ] @@ -11066,15 +11080,15 @@ dependencies = [ [[package]] name = "tinyvec_macros" -version = "0.1.0" +version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cda74da7e1a664f795bb1f8a87ec406fb89a02522cf6e50620d016add6dbbf5c" +checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" [[package]] name = "tokio" -version = "1.23.0" +version = "1.25.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eab6d665857cc6ca78d6e80303a02cea7a7851e85dfbd77cbdc09bd129f1ef46" +checksum = "c8e00990ebabbe4c14c08aca901caed183ecd5c09562a12c824bb53d3c3fd3af" dependencies = [ "autocfg", "bytes", @@ -11107,7 +11121,7 @@ version = "0.23.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c43ee83903113e03984cb9e5cebe6c04a5116269e900e3ddba8f068a62adda59" dependencies = [ - "rustls 0.20.7", + "rustls 0.20.8", "tokio", "webpki 0.22.0", ] @@ -11154,9 +11168,9 @@ dependencies = [ [[package]] name = "toml" -version = "0.5.10" +version = "0.5.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1333c76748e868a4d9d1017b5ab53171dfd095f70c712fdb4653a406547f598f" +checksum = "f4f7f0dd8d50a853a531c426359045b1998f04219d88799810762cd4ad314234" dependencies = [ "serde", ] @@ -11404,15 +11418,15 @@ dependencies = [ [[package]] name = "try-lock" -version = "0.2.3" +version = "0.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "59547bce71d9c38b83d9c0e92b6066c4253371f15005def0c30d9657f50c7642" +checksum = "3528ecfd12c466c6f163363caf2d02a71161dd5e1cc6ae7b34207ea2d42d81ed" [[package]] name = "try-runtime-cli" version = "0.10.0-dev" dependencies = [ - "clap 4.0.32", + "clap 4.1.4", "frame-remote-externalities", "frame-try-runtime", "hex", @@ -11441,10 +11455,11 @@ dependencies = [ [[package]] name = "trybuild" -version = "1.0.74" +version = "1.0.77" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "654bfc024d30963fce210f22f98956407fe8c8365eb85a1fa530f6f8844137ed" +checksum = "a44da5a6f2164c8e14d3bbc0657d69c5966af9f5f6930d4f600b1f5c4a673413" dependencies = [ + "basic-toml", "dissimilar", "glob", "once_cell", @@ -11452,7 +11467,6 @@ dependencies = [ "serde_derive", "serde_json", "termcolor", - "toml", ] [[package]] @@ -11468,7 +11482,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4712ee30d123ec7ae26d1e1b218395a16c87cdbaf4b3925d170d684af62ea5e8" dependencies = [ "async-trait", - "base64", + "base64 0.13.1", "futures", "log", "md-5", @@ -11518,9 +11532,9 @@ dependencies = [ [[package]] name = "unicode-bidi" -version = "0.3.8" +version = "0.3.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "099b7128301d285f79ddd55b9a83d5e6b9e97c92e0ea0daebee7263e932de992" +checksum = "d54675592c1dbefd78cbd98db9bacd89886e1ca50692a0692baefffdeb92dd58" [[package]] name = "unicode-ident" @@ -11590,9 +11604,9 @@ dependencies = [ [[package]] name = "uuid" -version = "1.2.2" +version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "422ee0de9031b5b948b97a8fc04e3aa35230001a722ddd27943e0be31564ce4c" +checksum = "1674845326ee10d37ca60470760d4288a6f80f304007d92e5c53bab78c9cfd79" dependencies = [ "getrandom 0.2.8", ] @@ -11686,9 +11700,9 @@ checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" [[package]] name = "wasm-bindgen" -version = "0.2.83" +version = "0.2.84" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eaf9f5aceeec8be17c128b2e93e031fb8a4d469bb9c4ae2d7dc1888b26887268" +checksum = "31f8dcbc21f30d9b8f2ea926ecb58f6b91192c17e9d33594b3df58b2007ca53b" dependencies = [ "cfg-if", "wasm-bindgen-macro", @@ -11696,9 +11710,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-backend" -version = "0.2.83" +version = "0.2.84" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4c8ffb332579b0557b52d268b91feab8df3615f265d5270fec2a8c95b17c1142" +checksum = "95ce90fd5bcc06af55a641a86428ee4229e44e07033963a2290a8e241607ccb9" dependencies = [ "bumpalo", "log", @@ -11711,9 +11725,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-futures" -version = "0.4.33" +version = "0.4.34" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "23639446165ca5a5de86ae1d8896b737ae80319560fbaa4c2887b7da6e7ebd7d" +checksum = "f219e0d211ba40266969f6dbdd90636da12f75bee4fc9d6c23d1260dadb51454" dependencies = [ "cfg-if", "js-sys", @@ -11723,9 +11737,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro" -version = "0.2.83" +version = "0.2.84" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "052be0f94026e6cbc75cdefc9bae13fd6052cdcaf532fa6c45e7ae33a1e6c810" +checksum = "4c21f77c0bedc37fd5dc21f897894a5ca01e7bb159884559461862ae90c0b4c5" dependencies = [ "quote", "wasm-bindgen-macro-support", @@ -11733,9 +11747,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro-support" -version = "0.2.83" +version = "0.2.84" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "07bc0c051dc5f23e307b13285f9d75df86bfdf816c5721e573dec1f9b8aa193c" +checksum = "2aff81306fcac3c7515ad4e177f521b5c9a15f2b08f4e32d823066102f35a5f6" dependencies = [ "proc-macro2", "quote", @@ -11746,15 +11760,15 @@ dependencies = [ [[package]] name = "wasm-bindgen-shared" -version = "0.2.83" +version = "0.2.84" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1c38c045535d93ec4f0b4defec448e4291638ee608530863b1e2ba115d4fff7f" +checksum = "0046fef7e28c3804e5e38bfa31ea2a0f73905319b677e57ebe37e49358989b5d" [[package]] name = "wasm-encoder" -version = "0.20.0" +version = "0.22.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "05632e0a66a6ed8cca593c24223aabd6262f256c3693ad9822c315285f010614" +checksum = "9a584273ccc2d9311f1dd19dc3fb26054661fa3e373d53ede5d1144ba07a9acd" dependencies = [ "leb128", ] @@ -11958,7 +11972,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "830570847f905b8f6d2ca635c33cf42ce701dd8e4abd7d1806c631f8f06e9e4b" dependencies = [ "anyhow", - "base64", + "base64 0.13.1", "bincode", "directories-next", "file-per-thread-logger", @@ -12095,9 +12109,9 @@ dependencies = [ [[package]] name = "wast" -version = "50.0.0" +version = "52.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a2cbb59d4ac799842791fe7e806fa5dbbf6b5554d538e51cc8e176db6ff0ae34" +checksum = "15942180f265280eede7bc38b239e9770031d1821c02d905284216c645316430" dependencies = [ "leb128", "memchr", @@ -12107,18 +12121,18 @@ dependencies = [ [[package]] name = "wat" -version = "1.0.52" +version = "1.0.57" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "584aaf7a1ecf4d383bbe1a25eeab0cbb8ff96acc6796707ff65cde48f4632f15" +checksum = "37212100d4cbe6f0f6ff6e707f1e5a5b5b675f0451231ed9e4235e234e127ed3" dependencies = [ "wast", ] [[package]] name = "web-sys" -version = "0.3.60" +version = "0.3.61" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bcda906d8be16e728fd5adc5b729afad4e444e106ab28cd1c7256e54fa61510f" +checksum = "e33b99f4b23ba3eec1a53ac264e35a755f00e966e0065077d6027c0f575b0b97" dependencies = [ "js-sys", "wasm-bindgen", @@ -12376,9 +12390,9 @@ dependencies = [ [[package]] name = "which" -version = "4.3.0" +version = "4.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1c831fbbee9e129a8cf93e7747a82da9d95ba8e16621cae60ec2cdc849bacb7b" +checksum = "2441c784c52b289a054b7201fc93253e288f094e2f4be9058343127c4226a269" dependencies = [ "either", "libc", @@ -12447,37 +12461,48 @@ dependencies = [ [[package]] name = "windows-sys" -version = "0.36.1" +version = "0.42.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ea04155a16a59f9eab786fe12a4a450e75cdb175f9e0d80da1e17db09f55b8d2" +checksum = "5a3e1820f08b8513f676f7ab6c1f99ff312fb97b553d30ff4dd86f9f15728aa7" dependencies = [ - "windows_aarch64_msvc 0.36.1", - "windows_i686_gnu 0.36.1", - "windows_i686_msvc 0.36.1", - "windows_x86_64_gnu 0.36.1", - "windows_x86_64_msvc 0.36.1", + "windows_aarch64_gnullvm", + "windows_aarch64_msvc 0.42.1", + "windows_i686_gnu 0.42.1", + "windows_i686_msvc 0.42.1", + "windows_x86_64_gnu 0.42.1", + "windows_x86_64_gnullvm", + "windows_x86_64_msvc 0.42.1", ] [[package]] name = "windows-sys" -version = "0.42.0" +version = "0.45.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5a3e1820f08b8513f676f7ab6c1f99ff312fb97b553d30ff4dd86f9f15728aa7" +checksum = "75283be5efb2831d37ea142365f009c02ec203cd29a3ebecbc093d52315b66d0" +dependencies = [ + "windows-targets", +] + +[[package]] +name = "windows-targets" +version = "0.42.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e2522491fbfcd58cc84d47aeb2958948c4b8982e9a2d8a2a35bbaed431390e7" dependencies = [ "windows_aarch64_gnullvm", - "windows_aarch64_msvc 0.42.0", - "windows_i686_gnu 0.42.0", - "windows_i686_msvc 0.42.0", - "windows_x86_64_gnu 0.42.0", + "windows_aarch64_msvc 0.42.1", + "windows_i686_gnu 0.42.1", + "windows_i686_msvc 0.42.1", + "windows_x86_64_gnu 0.42.1", "windows_x86_64_gnullvm", - "windows_x86_64_msvc 0.42.0", + "windows_x86_64_msvc 0.42.1", ] [[package]] name = "windows_aarch64_gnullvm" -version = "0.42.0" +version = "0.42.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "41d2aa71f6f0cbe00ae5167d90ef3cfe66527d6f613ca78ac8024c3ccab9a19e" +checksum = "8c9864e83243fdec7fc9c5444389dcbbfd258f745e7853198f365e3c4968a608" [[package]] name = "windows_aarch64_msvc" @@ -12487,15 +12512,9 @@ checksum = "17cffbe740121affb56fad0fc0e421804adf0ae00891205213b5cecd30db881d" [[package]] name = "windows_aarch64_msvc" -version = "0.36.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9bb8c3fd39ade2d67e9874ac4f3db21f0d710bee00fe7cab16949ec184eeaa47" - -[[package]] -name = "windows_aarch64_msvc" -version = "0.42.0" +version = "0.42.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dd0f252f5a35cac83d6311b2e795981f5ee6e67eb1f9a7f64eb4500fbc4dcdb4" +checksum = "4c8b1b673ffc16c47a9ff48570a9d85e25d265735c503681332589af6253c6c7" [[package]] name = "windows_i686_gnu" @@ -12505,15 +12524,9 @@ checksum = "2564fde759adb79129d9b4f54be42b32c89970c18ebf93124ca8870a498688ed" [[package]] name = "windows_i686_gnu" -version = "0.36.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "180e6ccf01daf4c426b846dfc66db1fc518f074baa793aa7d9b9aaeffad6a3b6" - -[[package]] -name = "windows_i686_gnu" -version = "0.42.0" +version = "0.42.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fbeae19f6716841636c28d695375df17562ca208b2b7d0dc47635a50ae6c5de7" +checksum = "de3887528ad530ba7bdbb1faa8275ec7a1155a45ffa57c37993960277145d640" [[package]] name = "windows_i686_msvc" @@ -12523,15 +12536,9 @@ checksum = "9cd9d32ba70453522332c14d38814bceeb747d80b3958676007acadd7e166956" [[package]] name = "windows_i686_msvc" -version = "0.36.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e2e7917148b2812d1eeafaeb22a97e4813dfa60a3f8f78ebe204bcc88f12f024" - -[[package]] -name = "windows_i686_msvc" -version = "0.42.0" +version = "0.42.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "84c12f65daa39dd2babe6e442988fc329d6243fdce47d7d2d155b8d874862246" +checksum = "bf4d1122317eddd6ff351aa852118a2418ad4214e6613a50e0191f7004372605" [[package]] name = "windows_x86_64_gnu" @@ -12541,21 +12548,15 @@ checksum = "cfce6deae227ee8d356d19effc141a509cc503dfd1f850622ec4b0f84428e1f4" [[package]] name = "windows_x86_64_gnu" -version = "0.36.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4dcd171b8776c41b97521e5da127a2d86ad280114807d0b2ab1e462bc764d9e1" - -[[package]] -name = "windows_x86_64_gnu" -version = "0.42.0" +version = "0.42.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bf7b1b21b5362cbc318f686150e5bcea75ecedc74dd157d874d754a2ca44b0ed" +checksum = "c1040f221285e17ebccbc2591ffdc2d44ee1f9186324dd3e84e99ac68d699c45" [[package]] name = "windows_x86_64_gnullvm" -version = "0.42.0" +version = "0.42.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "09d525d2ba30eeb3297665bd434a54297e4170c7f1a44cad4ef58095b4cd2028" +checksum = "628bfdf232daa22b0d64fdb62b09fcc36bb01f05a3939e20ab73aaf9470d0463" [[package]] name = "windows_x86_64_msvc" @@ -12565,15 +12566,9 @@ checksum = "d19538ccc21819d01deaf88d6a17eae6596a12e9aafdbb97916fb49896d89de9" [[package]] name = "windows_x86_64_msvc" -version = "0.36.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c811ca4a8c853ef420abd8592ba53ddbbac90410fab6903b3e79972a631f7680" - -[[package]] -name = "windows_x86_64_msvc" -version = "0.42.0" +version = "0.42.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f40009d85759725a34da6d89a94e63d7bdc50a862acf0dbc7c8e488f1edcb6f5" +checksum = "447660ad36a13288b1db4d4248e857b510e8c3a225c822ba4fb748c0aafecffd" [[package]] name = "winreg" @@ -12622,7 +12617,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9fb9bace5b5589ffead1afb76e43e34cff39cd0f3ce7e170ae0c29e53b88eb1c" dependencies = [ "asn1-rs 0.3.1", - "base64", + "base64 0.13.1", "data-encoding", "der-parser 7.0.0", "lazy_static", @@ -12641,7 +12636,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e0ecbeb7b67ce215e40e3cc7f2ff902f94a223acf44995934763467e7b1febc8" dependencies = [ "asn1-rs 0.5.1", - "base64", + "base64 0.13.1", "data-encoding", "der-parser 8.1.0", "lazy_static", @@ -12723,10 +12718,11 @@ dependencies = [ [[package]] name = "zstd-sys" -version = "2.0.4+zstd.1.5.2" +version = "2.0.6+zstd.1.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4fa202f2ef00074143e219d15b62ffc317d17cc33909feac471c044087cad7b0" +checksum = "68a3f9792c0c3dc6c165840a75f47ae1f4da402c2d006881129579f6597e801b" dependencies = [ "cc", "libc", + "pkg-config", ] diff --git a/client/consensus/common/Cargo.toml b/client/consensus/common/Cargo.toml index 87b052d43..d9e80e1e5 100644 --- a/client/consensus/common/Cargo.toml +++ b/client/consensus/common/Cargo.toml @@ -18,7 +18,7 @@ futures = { version = "0.3.21", features = ["thread-pool"] } futures-timer = "3.0.1" libp2p = "0.50.0" log = "0.4.17" -mockall = "0.11.2" +mockall = "0.11.3" parking_lot = "0.12.1" serde = { version = "1.0", features = ["derive"] } thiserror = "1.0.30" diff --git a/client/network/Cargo.toml b/client/network/Cargo.toml index 44a7973b3..b6b212471 100644 --- a/client/network/Cargo.toml +++ b/client/network/Cargo.toml @@ -28,6 +28,7 @@ ip_network = "0.4.1" libp2p = { version = "0.50.0", features = ["dns", "identify", "kad", "macros", "mdns", "mplex", "noise", "ping", "tcp", "tokio", "yamux", "websocket"] } log = "0.4.17" lru = "0.8.1" +mockall = "0.11.3" parking_lot = "0.12.1" pin-project = "1.0.12" rand = "0.8.5" @@ -52,9 +53,12 @@ sp-runtime = { version = "7.0.0", path = "../../primitives/runtime" } [dev-dependencies] assert_matches = "1.3" +multistream-select = "0.12.1" +rand = "0.8.5" tempfile = "3.1.0" tokio = { version = "1.22.0", features = ["macros"] } tokio-util = { version = "0.7.4", features = ["compat"] } +tokio-test = "0.4.2" sc-network-light = { version = "0.10.0-dev", path = "./light" } sc-network-sync = { version = "0.10.0-dev", path = "./sync" } sp-test-primitives = { version = "2.0.0", path = "../../primitives/test-primitives" } diff --git a/client/network/src/protocol/notifications/behaviour.rs b/client/network/src/protocol/notifications/behaviour.rs index 2db468671..c546f6b6f 100644 --- a/client/network/src/protocol/notifications/behaviour.rs +++ b/client/network/src/protocol/notifications/behaviour.rs @@ -2111,3 +2111,2420 @@ impl NetworkBehaviour for Notifications { Poll::Pending } } + +#[cfg(test)] +mod tests { + use super::*; + use crate::protocol::notifications::handler::tests::*; + use libp2p::{ + core::ConnectedPoint, + swarm::{behaviour::FromSwarm, AddressRecord}, + }; + use std::{collections::HashSet, iter}; + + impl PartialEq for ConnectionState { + fn eq(&self, other: &ConnectionState) -> bool { + match (self, other) { + (ConnectionState::Closed, ConnectionState::Closed) => true, + (ConnectionState::Closing, ConnectionState::Closing) => true, + (ConnectionState::Opening, ConnectionState::Opening) => true, + (ConnectionState::OpeningThenClosing, ConnectionState::OpeningThenClosing) => true, + (ConnectionState::OpenDesiredByRemote, ConnectionState::OpenDesiredByRemote) => + true, + (ConnectionState::Open(_), ConnectionState::Open(_)) => true, + _ => false, + } + } + } + + #[derive(Clone)] + struct MockPollParams { + peer_id: PeerId, + addr: Multiaddr, + } + + impl PollParameters for MockPollParams { + type SupportedProtocolsIter = std::vec::IntoIter>; + type ListenedAddressesIter = std::vec::IntoIter; + type ExternalAddressesIter = std::vec::IntoIter; + + fn supported_protocols(&self) -> Self::SupportedProtocolsIter { + vec![].into_iter() + } + + fn listened_addresses(&self) -> Self::ListenedAddressesIter { + vec![self.addr.clone()].into_iter() + } + + fn external_addresses(&self) -> Self::ExternalAddressesIter { + vec![].into_iter() + } + + fn local_peer_id(&self) -> &PeerId { + &self.peer_id + } + } + + fn development_notifs() -> (Notifications, sc_peerset::PeersetHandle) { + let (peerset, peerset_handle) = { + let mut sets = Vec::with_capacity(1); + + sets.push(sc_peerset::SetConfig { + in_peers: 25, + out_peers: 25, + bootnodes: Vec::new(), + reserved_nodes: HashSet::new(), + reserved_only: false, + }); + + sc_peerset::Peerset::from_config(sc_peerset::PeersetConfig { sets }) + }; + + ( + Notifications::new( + peerset, + iter::once(ProtocolConfig { + name: "/foo".into(), + fallback_names: Vec::new(), + handshake: vec![1, 2, 3, 4], + max_notification_size: u64::MAX, + }), + ), + peerset_handle, + ) + } + + #[test] + fn update_handshake() { + let (mut notif, _peerset) = development_notifs(); + + let inner = notif.notif_protocols.get_mut(0).unwrap().handshake.read().clone(); + assert_eq!(inner, vec![1, 2, 3, 4]); + + notif.set_notif_protocol_handshake(0.into(), vec![5, 6, 7, 8]); + + let inner = notif.notif_protocols.get_mut(0).unwrap().handshake.read().clone(); + assert_eq!(inner, vec![5, 6, 7, 8]); + } + + #[test] + #[should_panic] + #[cfg(debug_assertions)] + fn update_unknown_handshake() { + let (mut notif, _peerset) = development_notifs(); + + notif.set_notif_protocol_handshake(1337.into(), vec![5, 6, 7, 8]); + } + + #[test] + fn disconnect_backoff_peer() { + let (mut notif, _peerset) = development_notifs(); + + let peer = PeerId::random(); + notif.peers.insert( + (peer, 0.into()), + PeerState::Backoff { timer: DelayId(0), timer_deadline: Instant::now() }, + ); + notif.disconnect_peer(&peer, 0.into()); + + assert!(std::matches!( + notif.peers.get(&(peer, 0.into())), + Some(PeerState::Backoff { timer: DelayId(0), .. }) + )); + } + + #[test] + fn disconnect_pending_request() { + let (mut notif, _peerset) = development_notifs(); + let peer = PeerId::random(); + + notif.peers.insert( + (peer, 0.into()), + PeerState::PendingRequest { timer: DelayId(0), timer_deadline: Instant::now() }, + ); + notif.disconnect_peer(&peer, 0.into()); + + assert!(std::matches!( + notif.peers.get(&(peer, 0.into())), + Some(PeerState::PendingRequest { timer: DelayId(0), .. }) + )); + } + + #[test] + fn disconnect_requested_peer() { + let (mut notif, _peerset) = development_notifs(); + + let peer = PeerId::random(); + notif.peers.insert((peer, 0.into()), PeerState::Requested); + notif.disconnect_peer(&peer, 0.into()); + + assert!(std::matches!(notif.peers.get(&(peer, 0.into())), Some(PeerState::Requested))); + } + + #[test] + fn disconnect_disabled_peer() { + let (mut notif, _peerset) = development_notifs(); + let peer = PeerId::random(); + notif.peers.insert( + (peer, 0.into()), + PeerState::Disabled { backoff_until: None, connections: SmallVec::new() }, + ); + notif.disconnect_peer(&peer, 0.into()); + + assert!(std::matches!( + notif.peers.get(&(peer, 0.into())), + Some(PeerState::Disabled { backoff_until: None, .. }) + )); + } + + #[test] + fn remote_opens_connection_and_substream() { + let (mut notif, _peerset) = development_notifs(); + let peer = PeerId::random(); + let conn = ConnectionId::new(0usize); + let connected = ConnectedPoint::Listener { + local_addr: Multiaddr::empty(), + send_back_addr: Multiaddr::empty(), + }; + + notif.on_swarm_event(FromSwarm::ConnectionEstablished( + libp2p::swarm::behaviour::ConnectionEstablished { + peer_id: peer, + connection_id: conn, + endpoint: &connected, + failed_addresses: &[], + other_established: 0usize, + }, + )); + + if let Some(&PeerState::Disabled { ref connections, backoff_until: None }) = + notif.peers.get(&(peer, 0.into())) + { + assert_eq!(connections[0], (conn, ConnectionState::Closed)); + } else { + panic!("invalid state"); + } + + // remote opens a substream, verify that peer state is updated to `Incoming` + notif.on_connection_handler_event( + peer, + conn, + NotifsHandlerOut::OpenDesiredByRemote { protocol_index: 0 }, + ); + + if let Some(&PeerState::Incoming { ref connections, backoff_until: None }) = + notif.peers.get(&(peer, 0.into())) + { + assert_eq!(connections.len(), 1); + assert_eq!(connections[0], (conn, ConnectionState::OpenDesiredByRemote)); + } else { + panic!("invalid state"); + } + + assert!(std::matches!( + notif.incoming.pop(), + Some(IncomingPeer { alive: true, incoming_id: sc_peerset::IncomingIndex(0), .. }), + )); + } + + #[tokio::test] + async fn disconnect_remote_substream_before_handled_by_peerset() { + let (mut notif, _peerset) = development_notifs(); + let peer = PeerId::random(); + let conn = ConnectionId::new(0usize); + let connected = ConnectedPoint::Listener { + local_addr: Multiaddr::empty(), + send_back_addr: Multiaddr::empty(), + }; + + notif.on_swarm_event(FromSwarm::ConnectionEstablished( + libp2p::swarm::behaviour::ConnectionEstablished { + peer_id: peer, + connection_id: conn, + endpoint: &connected, + failed_addresses: &[], + other_established: 0usize, + }, + )); + notif.on_connection_handler_event( + peer, + conn, + NotifsHandlerOut::OpenDesiredByRemote { protocol_index: 0 }, + ); + notif.disconnect_peer(&peer, 0.into()); + + if let Some(&PeerState::Disabled { ref connections, backoff_until: None }) = + notif.peers.get(&(peer, 0.into())) + { + assert_eq!(connections.len(), 1); + assert_eq!(connections[0], (conn, ConnectionState::Closing)); + } else { + panic!("invalid state"); + } + } + + #[test] + fn peerset_report_connect_backoff() { + let (mut notif, _peerset) = development_notifs(); + let set_id = sc_peerset::SetId::from(0); + let peer = PeerId::random(); + let conn = ConnectionId::new(0usize); + let connected = ConnectedPoint::Listener { + local_addr: Multiaddr::empty(), + send_back_addr: Multiaddr::empty(), + }; + + notif.on_swarm_event(FromSwarm::ConnectionEstablished( + libp2p::swarm::behaviour::ConnectionEstablished { + peer_id: peer, + connection_id: conn, + endpoint: &connected, + failed_addresses: &[], + other_established: 0usize, + }, + )); + assert!(std::matches!(notif.peers.get(&(peer, set_id)), Some(&PeerState::Disabled { .. }))); + + // manually add backoff for the entry + // + // there is not straight-forward way of adding backoff to `PeerState::Disabled` + // so manually adjust the value in order to progress on to the next stage. + // This modification together with `ConnectionClosed` will conver the peer + // state into `PeerState::Backoff`. + if let Some(PeerState::Disabled { ref mut backoff_until, .. }) = + notif.peers.get_mut(&(peer, set_id)) + { + *backoff_until = + Some(Instant::now().checked_add(std::time::Duration::from_secs(5)).unwrap()); + } + + notif.on_swarm_event(FromSwarm::ConnectionClosed( + libp2p::swarm::behaviour::ConnectionClosed { + peer_id: peer, + connection_id: conn, + endpoint: &connected, + handler: NotifsHandlerProto::new(vec![]).into_handler(&peer, &connected), + remaining_established: 0usize, + }, + )); + + let timer = if let Some(&PeerState::Backoff { timer_deadline, .. }) = + notif.peers.get(&(peer, set_id)) + { + timer_deadline + } else { + panic!("invalid state"); + }; + + // attempt to connect the backed-off peer and verify that the request is pending + notif.peerset_report_connect(peer, set_id); + + if let Some(&PeerState::PendingRequest { timer_deadline, .. }) = + notif.peers.get(&(peer, set_id)) + { + assert_eq!(timer, timer_deadline); + } else { + panic!("invalid state"); + } + } + + #[test] + fn peerset_connect_incoming() { + let (mut notif, _peerset) = development_notifs(); + let peer = PeerId::random(); + let conn = ConnectionId::new(0usize); + let set_id = sc_peerset::SetId::from(0); + let connected = ConnectedPoint::Listener { + local_addr: Multiaddr::empty(), + send_back_addr: Multiaddr::empty(), + }; + + notif.on_swarm_event(FromSwarm::ConnectionEstablished( + libp2p::swarm::behaviour::ConnectionEstablished { + peer_id: peer, + connection_id: conn, + endpoint: &connected, + failed_addresses: &[], + other_established: 0usize, + }, + )); + assert!(std::matches!(notif.peers.get(&(peer, set_id)), Some(&PeerState::Disabled { .. }))); + + // remote opens a substream, verify that peer state is updated to `Incoming` + notif.on_connection_handler_event( + peer, + conn, + NotifsHandlerOut::OpenDesiredByRemote { protocol_index: 0 }, + ); + + // attempt to connect to the peer and verify that the peer state is `Enabled` + notif.peerset_report_connect(peer, set_id); + assert!(std::matches!(notif.peers.get(&(peer, set_id)), Some(&PeerState::Enabled { .. }))); + } + + #[test] + fn peerset_disconnect_disable_pending_enable() { + let (mut notif, _peerset) = development_notifs(); + let set_id = sc_peerset::SetId::from(0); + let peer = PeerId::random(); + let conn = ConnectionId::new(0usize); + let connected = ConnectedPoint::Listener { + local_addr: Multiaddr::empty(), + send_back_addr: Multiaddr::empty(), + }; + + notif.on_swarm_event(FromSwarm::ConnectionEstablished( + libp2p::swarm::behaviour::ConnectionEstablished { + peer_id: peer, + connection_id: conn, + endpoint: &connected, + failed_addresses: &[], + other_established: 0usize, + }, + )); + assert!(std::matches!(notif.peers.get(&(peer, set_id)), Some(&PeerState::Disabled { .. }))); + + // manually add backoff for the entry + if let Some(PeerState::Disabled { ref mut backoff_until, .. }) = + notif.peers.get_mut(&(peer, set_id)) + { + *backoff_until = + Some(Instant::now().checked_add(std::time::Duration::from_secs(5)).unwrap()); + } + + // switch state to `DisabledPendingEnable` + notif.peerset_report_connect(peer, set_id); + assert!(std::matches!( + notif.peers.get(&(peer, set_id)), + Some(&PeerState::DisabledPendingEnable { .. }) + )); + + notif.peerset_report_disconnect(peer, set_id); + + if let Some(PeerState::Disabled { backoff_until, .. }) = notif.peers.get(&(peer, set_id)) { + assert!(backoff_until.is_some()); + assert!(backoff_until.unwrap() > Instant::now()); + } else { + panic!("invalid state"); + } + } + + #[test] + fn peerset_disconnect_enabled() { + let (mut notif, _peerset) = development_notifs(); + let peer = PeerId::random(); + let conn = ConnectionId::new(0usize); + let set_id = sc_peerset::SetId::from(0); + let connected = ConnectedPoint::Listener { + local_addr: Multiaddr::empty(), + send_back_addr: Multiaddr::empty(), + }; + + // Set peer into `Enabled` state. + notif.on_swarm_event(FromSwarm::ConnectionEstablished( + libp2p::swarm::behaviour::ConnectionEstablished { + peer_id: peer, + connection_id: conn, + endpoint: &connected, + failed_addresses: &[], + other_established: 0usize, + }, + )); + notif.on_connection_handler_event( + peer, + conn, + NotifsHandlerOut::OpenDesiredByRemote { protocol_index: 0 }, + ); + notif.peerset_report_connect(peer, set_id); + assert!(std::matches!(notif.peers.get(&(peer, set_id)), Some(&PeerState::Enabled { .. }))); + + // disconnect peer and verify that the state is `Disabled` + notif.peerset_report_disconnect(peer, set_id); + assert!(std::matches!(notif.peers.get(&(peer, set_id)), Some(&PeerState::Disabled { .. }))); + } + + #[test] + fn peerset_disconnect_requested() { + let (mut notif, _peerset) = development_notifs(); + let peer = PeerId::random(); + let set_id = sc_peerset::SetId::from(0); + + // Set peer into `Requested` state. + notif.peerset_report_connect(peer, set_id); + assert!(std::matches!(notif.peers.get(&(peer, set_id)), Some(&PeerState::Requested))); + + // disconnect peer and verify that the state is `Disabled` + notif.peerset_report_disconnect(peer, set_id); + assert!(notif.peers.get(&(peer, set_id)).is_none()); + } + + #[test] + fn peerset_disconnect_pending_request() { + let (mut notif, _peerset) = development_notifs(); + let set_id = sc_peerset::SetId::from(0); + let peer = PeerId::random(); + let conn = ConnectionId::new(0usize); + let connected = ConnectedPoint::Listener { + local_addr: Multiaddr::empty(), + send_back_addr: Multiaddr::empty(), + }; + + notif.on_swarm_event(FromSwarm::ConnectionEstablished( + libp2p::swarm::behaviour::ConnectionEstablished { + peer_id: peer, + connection_id: conn, + endpoint: &connected, + failed_addresses: &[], + other_established: 0usize, + }, + )); + assert!(std::matches!(notif.peers.get(&(peer, set_id)), Some(&PeerState::Disabled { .. }))); + + // manually add backoff for the entry + if let Some(PeerState::Disabled { ref mut backoff_until, .. }) = + notif.peers.get_mut(&(peer, set_id)) + { + *backoff_until = + Some(Instant::now().checked_add(std::time::Duration::from_secs(5)).unwrap()); + } + + notif.on_swarm_event(FromSwarm::ConnectionClosed( + libp2p::swarm::behaviour::ConnectionClosed { + peer_id: peer, + connection_id: conn, + endpoint: &connected, + handler: NotifsHandlerProto::new(vec![]).into_handler(&peer, &connected), + remaining_established: 0usize, + }, + )); + assert!(std::matches!(notif.peers.get(&(peer, set_id)), Some(&PeerState::Backoff { .. }))); + + // attempt to connect the backed-off peer and verify that the request is pending + notif.peerset_report_connect(peer, set_id); + assert!(std::matches!( + notif.peers.get(&(peer, set_id)), + Some(&PeerState::PendingRequest { .. }) + )); + + // attempt to disconnect the backed-off peer and verify that the request is pending + notif.peerset_report_disconnect(peer, set_id); + assert!(std::matches!(notif.peers.get(&(peer, set_id)), Some(&PeerState::Backoff { .. }))); + } + + #[test] + fn peerset_accept_peer_not_alive() { + let (mut notif, _peerset) = development_notifs(); + let peer = PeerId::random(); + let conn = ConnectionId::new(0usize); + let set_id = sc_peerset::SetId::from(0); + let connected = ConnectedPoint::Listener { + local_addr: Multiaddr::empty(), + send_back_addr: Multiaddr::empty(), + }; + + notif.on_swarm_event(FromSwarm::ConnectionEstablished( + libp2p::swarm::behaviour::ConnectionEstablished { + peer_id: peer, + connection_id: conn, + endpoint: &connected, + failed_addresses: &[], + other_established: 0usize, + }, + )); + assert!(std::matches!(notif.peers.get(&(peer, set_id)), Some(&PeerState::Disabled { .. }))); + + // remote opens a substream, verify that peer state is updated to `Incoming` + notif.on_connection_handler_event( + peer, + conn, + NotifsHandlerOut::OpenDesiredByRemote { protocol_index: 0 }, + ); + assert!(std::matches!(notif.peers.get(&(peer, set_id)), Some(&PeerState::Incoming { .. }))); + + assert!(std::matches!( + notif.incoming[0], + IncomingPeer { alive: true, incoming_id: sc_peerset::IncomingIndex(0), .. }, + )); + + notif.disconnect_peer(&peer, set_id); + assert!(std::matches!(notif.peers.get(&(peer, set_id)), Some(&PeerState::Disabled { .. }))); + assert!(std::matches!( + notif.incoming[0], + IncomingPeer { alive: false, incoming_id: sc_peerset::IncomingIndex(0), .. }, + )); + + notif.peerset_report_accept(sc_peerset::IncomingIndex(0)); + assert_eq!(notif.incoming.len(), 0); + assert!(std::matches!(notif.peers.get(&(peer, set_id)), Some(PeerState::Disabled { .. }))); + } + + #[test] + fn secondary_connection_peer_state_incoming() { + let (mut notif, _peerset) = development_notifs(); + let peer = PeerId::random(); + let conn = ConnectionId::new(0usize); + let conn2 = ConnectionId::new(1usize); + let set_id = sc_peerset::SetId::from(0); + let connected = ConnectedPoint::Listener { + local_addr: Multiaddr::empty(), + send_back_addr: Multiaddr::empty(), + }; + + notif.on_swarm_event(FromSwarm::ConnectionEstablished( + libp2p::swarm::behaviour::ConnectionEstablished { + peer_id: peer, + connection_id: conn, + endpoint: &connected, + failed_addresses: &[], + other_established: 0usize, + }, + )); + assert!(std::matches!(notif.peers.get(&(peer, set_id)), Some(&PeerState::Disabled { .. }))); + + notif.on_connection_handler_event( + peer, + conn, + NotifsHandlerOut::OpenDesiredByRemote { protocol_index: 0 }, + ); + if let Some(&PeerState::Incoming { ref connections, .. }) = notif.peers.get(&(peer, set_id)) + { + assert_eq!(connections.len(), 1); + assert_eq!(connections[0], (conn, ConnectionState::OpenDesiredByRemote)); + } else { + panic!("invalid state"); + } + + // add another connection + notif.on_swarm_event(FromSwarm::ConnectionEstablished( + libp2p::swarm::behaviour::ConnectionEstablished { + peer_id: peer, + connection_id: conn2, + endpoint: &connected, + failed_addresses: &[], + other_established: 0usize, + }, + )); + + if let Some(&PeerState::Incoming { ref connections, .. }) = notif.peers.get(&(peer, set_id)) + { + assert_eq!(connections.len(), 2); + assert_eq!(connections[0], (conn, ConnectionState::OpenDesiredByRemote)); + assert_eq!(connections[1], (conn2, ConnectionState::Closed)); + } else { + panic!("invalid state"); + } + } + + #[test] + fn close_connection_for_disabled_peer() { + let (mut notif, _peerset) = development_notifs(); + let peer = PeerId::random(); + let conn = ConnectionId::new(0usize); + let set_id = sc_peerset::SetId::from(0); + let connected = ConnectedPoint::Listener { + local_addr: Multiaddr::empty(), + send_back_addr: Multiaddr::empty(), + }; + + notif.on_swarm_event(FromSwarm::ConnectionEstablished( + libp2p::swarm::behaviour::ConnectionEstablished { + peer_id: peer, + connection_id: conn, + endpoint: &connected, + failed_addresses: &[], + other_established: 0usize, + }, + )); + assert!(std::matches!(notif.peers.get(&(peer, set_id)), Some(&PeerState::Disabled { .. }))); + + notif.on_swarm_event(FromSwarm::ConnectionClosed( + libp2p::swarm::behaviour::ConnectionClosed { + peer_id: peer, + connection_id: conn, + endpoint: &connected, + handler: NotifsHandlerProto::new(vec![]).into_handler(&peer, &connected), + remaining_established: 0usize, + }, + )); + assert!(notif.peers.get(&(peer, set_id)).is_none()); + } + + #[test] + fn close_connection_for_incoming_peer_one_connection() { + let (mut notif, _peerset) = development_notifs(); + let peer = PeerId::random(); + let conn = ConnectionId::new(0usize); + let set_id = sc_peerset::SetId::from(0); + let connected = ConnectedPoint::Listener { + local_addr: Multiaddr::empty(), + send_back_addr: Multiaddr::empty(), + }; + + notif.on_swarm_event(FromSwarm::ConnectionEstablished( + libp2p::swarm::behaviour::ConnectionEstablished { + peer_id: peer, + connection_id: conn, + endpoint: &connected, + failed_addresses: &[], + other_established: 0usize, + }, + )); + assert!(std::matches!(notif.peers.get(&(peer, set_id)), Some(&PeerState::Disabled { .. }))); + + notif.on_connection_handler_event( + peer, + conn, + NotifsHandlerOut::OpenDesiredByRemote { protocol_index: 0 }, + ); + assert!(std::matches!(notif.peers.get(&(peer, set_id)), Some(&PeerState::Incoming { .. }))); + + notif.on_swarm_event(FromSwarm::ConnectionClosed( + libp2p::swarm::behaviour::ConnectionClosed { + peer_id: peer, + connection_id: conn, + endpoint: &connected, + handler: NotifsHandlerProto::new(vec![]).into_handler(&peer, &connected), + remaining_established: 0usize, + }, + )); + assert!(notif.peers.get(&(peer, set_id)).is_none()); + assert!(std::matches!( + notif.incoming[0], + IncomingPeer { alive: false, incoming_id: sc_peerset::IncomingIndex(0), .. }, + )); + } + + #[test] + fn close_connection_for_incoming_peer_two_connections() { + let (mut notif, _peerset) = development_notifs(); + let peer = PeerId::random(); + let conn = ConnectionId::new(0usize); + let conn1 = ConnectionId::new(1usize); + let set_id = sc_peerset::SetId::from(0); + let connected = ConnectedPoint::Listener { + local_addr: Multiaddr::empty(), + send_back_addr: Multiaddr::empty(), + }; + let mut conns = SmallVec::< + [(ConnectionId, ConnectionState); crate::MAX_CONNECTIONS_PER_PEER], + >::from(vec![(conn, ConnectionState::Closed)]); + + notif.on_swarm_event(FromSwarm::ConnectionEstablished( + libp2p::swarm::behaviour::ConnectionEstablished { + peer_id: peer, + connection_id: conn, + endpoint: &connected, + failed_addresses: &[], + other_established: 0usize, + }, + )); + assert!(std::matches!(notif.peers.get(&(peer, set_id)), Some(&PeerState::Disabled { .. }))); + + notif.on_connection_handler_event( + peer, + conn, + NotifsHandlerOut::OpenDesiredByRemote { protocol_index: 0 }, + ); + assert!(std::matches!(notif.peers.get(&(peer, set_id)), Some(&PeerState::Incoming { .. }))); + + notif.on_swarm_event(FromSwarm::ConnectionEstablished( + libp2p::swarm::behaviour::ConnectionEstablished { + peer_id: peer, + connection_id: conn1, + endpoint: &connected, + failed_addresses: &[], + other_established: 0usize, + }, + )); + conns.push((conn1, ConnectionState::Closed)); + + if let Some(PeerState::Incoming { ref connections, .. }) = notif.peers.get(&(peer, set_id)) + { + assert_eq!(connections.len(), 2); + assert_eq!(connections[0], (conn, ConnectionState::OpenDesiredByRemote)); + assert_eq!(connections[1], (conn1, ConnectionState::Closed)); + } + + notif.on_swarm_event(FromSwarm::ConnectionClosed( + libp2p::swarm::behaviour::ConnectionClosed { + peer_id: peer, + connection_id: conn, + endpoint: &connected, + handler: NotifsHandlerProto::new(vec![]).into_handler(&peer, &connected), + remaining_established: 0usize, + }, + )); + + if let Some(&PeerState::Disabled { ref connections, .. }) = notif.peers.get(&(peer, set_id)) + { + assert_eq!(connections.len(), 1); + assert_eq!(connections[0], (conn1, ConnectionState::Closed)); + } else { + panic!("invalid state"); + } + } + + #[test] + fn connection_and_substream_open() { + let (mut notif, _peerset) = development_notifs(); + let peer = PeerId::random(); + let conn = ConnectionId::new(0usize); + let set_id = sc_peerset::SetId::from(0); + let connected = ConnectedPoint::Listener { + local_addr: Multiaddr::empty(), + send_back_addr: Multiaddr::empty(), + }; + let mut conn_yielder = ConnectionYielder::new(); + + // move the peer to `Enabled` state + notif.on_swarm_event(FromSwarm::ConnectionEstablished( + libp2p::swarm::behaviour::ConnectionEstablished { + peer_id: peer, + connection_id: conn, + endpoint: &connected, + failed_addresses: &[], + other_established: 0usize, + }, + )); + assert!(std::matches!(notif.peers.get(&(peer, set_id)), Some(&PeerState::Disabled { .. }))); + + notif.on_connection_handler_event( + peer, + conn, + NotifsHandlerOut::OpenDesiredByRemote { protocol_index: 0 }, + ); + assert!(std::matches!(notif.peers.get(&(peer, set_id)), Some(&PeerState::Incoming { .. }))); + + notif.peerset_report_connect(peer, set_id); + assert!(std::matches!(notif.peers.get(&(peer, set_id)), Some(&PeerState::Enabled { .. }))); + + // open new substream + let event = conn_yielder.open_substream(peer, 0, connected, vec![1, 2, 3, 4]); + + notif.on_connection_handler_event(peer, conn, event); + assert!(std::matches!(notif.peers.get(&(peer, set_id)), Some(&PeerState::Enabled { .. }))); + + if let Some(PeerState::Enabled { ref connections, .. }) = notif.peers.get(&(peer, set_id)) { + assert_eq!(connections.len(), 1); + assert_eq!(connections[0].0, conn); + assert!(std::matches!(connections[0].1, ConnectionState::Open(_))); + } + + assert!(std::matches!( + notif.events[notif.events.len() - 1], + NetworkBehaviourAction::GenerateEvent(NotificationsOut::CustomProtocolOpen { .. }) + )); + } + + #[test] + fn connection_closed_sink_replaced() { + let (mut notif, _peerset) = development_notifs(); + let peer = PeerId::random(); + let conn1 = ConnectionId::new(0usize); + let conn2 = ConnectionId::new(1usize); + let set_id = sc_peerset::SetId::from(0); + let connected = ConnectedPoint::Listener { + local_addr: Multiaddr::empty(), + send_back_addr: Multiaddr::empty(), + }; + let mut conn_yielder = ConnectionYielder::new(); + + // open two connections + for conn_id in vec![conn1, conn2] { + notif.on_swarm_event(FromSwarm::ConnectionEstablished( + libp2p::swarm::behaviour::ConnectionEstablished { + peer_id: peer, + connection_id: conn_id, + endpoint: &connected, + failed_addresses: &[], + other_established: 0usize, + }, + )); + } + + if let Some(&PeerState::Disabled { ref connections, .. }) = notif.peers.get(&(peer, set_id)) + { + assert_eq!(connections[0], (conn1, ConnectionState::Closed)); + assert_eq!(connections[1], (conn2, ConnectionState::Closed)); + } else { + panic!("invalid state"); + } + + // open substreams on both active connections + notif.peerset_report_connect(peer, set_id); + notif.on_connection_handler_event( + peer, + conn2, + NotifsHandlerOut::OpenDesiredByRemote { protocol_index: 0 }, + ); + + if let Some(&PeerState::Enabled { ref connections, .. }) = notif.peers.get(&(peer, set_id)) + { + assert_eq!(connections[0], (conn1, ConnectionState::Opening)); + assert_eq!(connections[1], (conn2, ConnectionState::Opening)); + } else { + panic!("invalid state"); + } + + // add two new substreams, one for each connection and verify that both are in open state + for conn in vec![conn1, conn2].iter() { + notif.on_connection_handler_event( + peer, + *conn, + conn_yielder.open_substream(peer, 0, connected.clone(), vec![1, 2, 3, 4]), + ); + } + + if let Some(PeerState::Enabled { ref connections, .. }) = notif.peers.get(&(peer, set_id)) { + assert_eq!(connections[0].0, conn1); + assert!(std::matches!(connections[0].1, ConnectionState::Open(_))); + assert_eq!(connections[1].0, conn2); + assert!(std::matches!(connections[1].1, ConnectionState::Open(_))); + } else { + panic!("invalid state"); + } + + // check peer information + assert_eq!(notif.open_peers().collect::>(), vec![&peer],); + assert_eq!(notif.reserved_peers(set_id).collect::>(), Vec::<&PeerId>::new(),); + assert_eq!(notif.num_discovered_peers(), 0usize); + + // close the other connection and verify that notification replacement event is emitted + notif.on_swarm_event(FromSwarm::ConnectionClosed( + libp2p::swarm::behaviour::ConnectionClosed { + peer_id: peer, + connection_id: conn1, + endpoint: &connected, + handler: NotifsHandlerProto::new(vec![]).into_handler(&peer, &connected), + remaining_established: 0usize, + }, + )); + + if let Some(PeerState::Enabled { ref connections, .. }) = notif.peers.get(&(peer, set_id)) { + assert_eq!(connections.len(), 1); + assert_eq!(connections[0].0, conn2); + assert!(std::matches!(connections[0].1, ConnectionState::Open(_))); + } else { + panic!("invalid state"); + } + + assert!(std::matches!( + notif.events[notif.events.len() - 1], + NetworkBehaviourAction::GenerateEvent(NotificationsOut::CustomProtocolReplaced { .. }) + )); + } + + #[test] + fn dial_failure_for_requested_peer() { + let (mut notif, _peerset) = development_notifs(); + let peer = PeerId::random(); + let set_id = sc_peerset::SetId::from(0); + + // Set peer into `Requested` state. + notif.peerset_report_connect(peer, set_id); + assert!(std::matches!(notif.peers.get(&(peer, set_id)), Some(&PeerState::Requested))); + + notif.on_swarm_event(FromSwarm::DialFailure(libp2p::swarm::behaviour::DialFailure { + peer_id: Some(peer), + handler: NotifsHandlerProto::new(vec![]), + error: &libp2p::swarm::DialError::Banned, + })); + + if let Some(PeerState::Backoff { timer_deadline, .. }) = notif.peers.get(&(peer, set_id)) { + assert!(timer_deadline > &Instant::now()); + } else { + panic!("invalid state"); + } + } + + #[tokio::test] + async fn write_notification() { + let (mut notif, _peerset) = development_notifs(); + let peer = PeerId::random(); + let conn = ConnectionId::new(0usize); + let set_id = sc_peerset::SetId::from(0); + let connected = ConnectedPoint::Listener { + local_addr: Multiaddr::empty(), + send_back_addr: Multiaddr::empty(), + }; + let mut conn_yielder = ConnectionYielder::new(); + + notif.on_swarm_event(FromSwarm::ConnectionEstablished( + libp2p::swarm::behaviour::ConnectionEstablished { + peer_id: peer, + connection_id: conn, + endpoint: &connected, + failed_addresses: &[], + other_established: 0usize, + }, + )); + assert!(std::matches!(notif.peers.get(&(peer, set_id)), Some(&PeerState::Disabled { .. }))); + + notif.peerset_report_connect(peer, set_id); + assert!(std::matches!(notif.peers.get(&(peer, set_id)), Some(&PeerState::Enabled { .. }))); + + notif.on_connection_handler_event( + peer, + conn, + conn_yielder.open_substream(peer, 0, connected, vec![1, 2, 3, 4]), + ); + + if let Some(PeerState::Enabled { ref connections, .. }) = notif.peers.get(&(peer, set_id)) { + assert_eq!(connections[0].0, conn); + assert!(std::matches!(connections[0].1, ConnectionState::Open(_))); + } else { + panic!("invalid state"); + } + + notif.write_notification(&peer, set_id, vec![1, 3, 3, 7]); + assert_eq!(conn_yielder.get_next_event(peer, set_id.into()).await, Some(vec![1, 3, 3, 7])); + } + + #[test] + fn peerset_report_connect_backoff_expired() { + let (mut notif, _peerset) = development_notifs(); + let set_id = sc_peerset::SetId::from(0); + let peer = PeerId::random(); + let conn = ConnectionId::new(0usize); + let connected = ConnectedPoint::Listener { + local_addr: Multiaddr::empty(), + send_back_addr: Multiaddr::empty(), + }; + let backoff_duration = Duration::from_millis(100); + + notif.on_swarm_event(FromSwarm::ConnectionEstablished( + libp2p::swarm::behaviour::ConnectionEstablished { + peer_id: peer, + connection_id: conn, + endpoint: &connected, + failed_addresses: &[], + other_established: 0usize, + }, + )); + assert!(std::matches!(notif.peers.get(&(peer, set_id)), Some(&PeerState::Disabled { .. }))); + + // manually add backoff for the entry + if let Some(PeerState::Disabled { ref mut backoff_until, .. }) = + notif.peers.get_mut(&(peer, set_id)) + { + *backoff_until = Some(Instant::now().checked_add(backoff_duration).unwrap()); + } + + notif.on_swarm_event(FromSwarm::ConnectionClosed( + libp2p::swarm::behaviour::ConnectionClosed { + peer_id: peer, + connection_id: conn, + endpoint: &connected, + handler: NotifsHandlerProto::new(vec![]).into_handler(&peer, &connected), + remaining_established: 0usize, + }, + )); + + // wait until the backoff time has passed + std::thread::sleep(backoff_duration * 2); + + // attempt to connect the backed-off peer and verify that the request is pending + notif.peerset_report_connect(peer, set_id); + assert!(std::matches!(notif.peers.get(&(peer, set_id)), Some(&PeerState::Requested { .. }))) + } + + #[test] + fn peerset_report_disconnect_disabled() { + let (mut notif, _peerset) = development_notifs(); + let peer = PeerId::random(); + let set_id = sc_peerset::SetId::from(0); + let conn = ConnectionId::new(0usize); + let connected = ConnectedPoint::Listener { + local_addr: Multiaddr::empty(), + send_back_addr: Multiaddr::empty(), + }; + + notif.on_swarm_event(FromSwarm::ConnectionEstablished( + libp2p::swarm::behaviour::ConnectionEstablished { + peer_id: peer, + connection_id: conn, + endpoint: &connected, + failed_addresses: &[], + other_established: 0usize, + }, + )); + assert!(std::matches!(notif.peers.get(&(peer, set_id)), Some(&PeerState::Disabled { .. }))); + + notif.peerset_report_disconnect(peer, set_id); + assert!(std::matches!(notif.peers.get(&(peer, set_id)), Some(&PeerState::Disabled { .. }))); + } + + #[test] + fn peerset_report_disconnect_backoff() { + let (mut notif, _peerset) = development_notifs(); + let set_id = sc_peerset::SetId::from(0); + let peer = PeerId::random(); + let conn = ConnectionId::new(0usize); + let connected = ConnectedPoint::Listener { + local_addr: Multiaddr::empty(), + send_back_addr: Multiaddr::empty(), + }; + let backoff_duration = Duration::from_secs(2); + + notif.on_swarm_event(FromSwarm::ConnectionEstablished( + libp2p::swarm::behaviour::ConnectionEstablished { + peer_id: peer, + connection_id: conn, + endpoint: &connected, + failed_addresses: &[], + other_established: 0usize, + }, + )); + assert!(std::matches!(notif.peers.get(&(peer, set_id)), Some(&PeerState::Disabled { .. }))); + + // manually add backoff for the entry + if let Some(PeerState::Disabled { ref mut backoff_until, .. }) = + notif.peers.get_mut(&(peer, set_id)) + { + *backoff_until = Some(Instant::now().checked_add(backoff_duration).unwrap()); + } + + notif.on_swarm_event(FromSwarm::ConnectionClosed( + libp2p::swarm::behaviour::ConnectionClosed { + peer_id: peer, + connection_id: conn, + endpoint: &connected, + handler: NotifsHandlerProto::new(vec![]).into_handler(&peer, &connected), + remaining_established: 0usize, + }, + )); + + assert!(std::matches!(notif.peers.get(&(peer, set_id)), Some(&PeerState::Backoff { .. }))); + + notif.peerset_report_disconnect(peer, set_id); + assert!(std::matches!(notif.peers.get(&(peer, set_id)), Some(&PeerState::Backoff { .. }))); + } + + #[test] + fn peer_is_backed_off_if_both_connections_get_closed_while_peer_is_disabled_with_back_off() { + let (mut notif, _peerset) = development_notifs(); + let set_id = sc_peerset::SetId::from(0); + let peer = PeerId::random(); + let conn1 = ConnectionId::new(0usize); + let conn2 = ConnectionId::new(0usize); + let connected = ConnectedPoint::Listener { + local_addr: Multiaddr::empty(), + send_back_addr: Multiaddr::empty(), + }; + + notif.on_swarm_event(FromSwarm::ConnectionEstablished( + libp2p::swarm::behaviour::ConnectionEstablished { + peer_id: peer, + connection_id: conn1, + endpoint: &connected, + failed_addresses: &[], + other_established: 0usize, + }, + )); + notif.on_swarm_event(FromSwarm::ConnectionEstablished( + libp2p::swarm::behaviour::ConnectionEstablished { + peer_id: peer, + connection_id: conn2, + endpoint: &connected, + failed_addresses: &[], + other_established: 0usize, + }, + )); + assert!(std::matches!(notif.peers.get(&(peer, set_id)), Some(&PeerState::Disabled { .. }))); + + // manually add backoff for the entry + if let Some(PeerState::Disabled { ref mut backoff_until, .. }) = + notif.peers.get_mut(&(peer, set_id)) + { + *backoff_until = + Some(Instant::now().checked_add(std::time::Duration::from_secs(5)).unwrap()); + } + + // switch state to `DisabledPendingEnable` + notif.peerset_report_connect(peer, set_id); + assert!(std::matches!( + notif.peers.get(&(peer, set_id)), + Some(&PeerState::DisabledPendingEnable { .. }) + )); + + notif.on_swarm_event(FromSwarm::ConnectionClosed( + libp2p::swarm::behaviour::ConnectionClosed { + peer_id: peer, + connection_id: conn1, + endpoint: &connected, + handler: NotifsHandlerProto::new(vec![]).into_handler(&peer, &connected), + remaining_established: 0usize, + }, + )); + assert!(std::matches!( + notif.peers.get(&(peer, set_id)), + Some(&PeerState::DisabledPendingEnable { .. }) + )); + + notif.on_swarm_event(FromSwarm::ConnectionClosed( + libp2p::swarm::behaviour::ConnectionClosed { + peer_id: peer, + connection_id: conn2, + endpoint: &connected, + handler: NotifsHandlerProto::new(vec![]).into_handler(&peer, &connected), + remaining_established: 0usize, + }, + )); + assert!(std::matches!(notif.peers.get(&(peer, set_id)), Some(&PeerState::Backoff { .. }))); + } + + #[test] + fn inject_connection_closed_incoming_with_backoff() { + let (mut notif, _peerset) = development_notifs(); + let peer = PeerId::random(); + let set_id = sc_peerset::SetId::from(0); + let conn = ConnectionId::new(0usize); + let connected = ConnectedPoint::Listener { + local_addr: Multiaddr::empty(), + send_back_addr: Multiaddr::empty(), + }; + + notif.on_swarm_event(FromSwarm::ConnectionEstablished( + libp2p::swarm::behaviour::ConnectionEstablished { + peer_id: peer, + connection_id: conn, + endpoint: &connected, + failed_addresses: &[], + other_established: 0usize, + }, + )); + assert!(std::matches!(notif.peers.get(&(peer, set_id)), Some(&PeerState::Disabled { .. }))); + + // remote opens a substream, verify that peer state is updated to `Incoming` + notif.on_connection_handler_event( + peer, + conn, + NotifsHandlerOut::OpenDesiredByRemote { protocol_index: 0 }, + ); + + // manually add backoff for the entry + if let Some(&mut PeerState::Incoming { ref mut backoff_until, .. }) = + notif.peers.get_mut(&(peer, 0.into())) + { + *backoff_until = + Some(Instant::now().checked_add(std::time::Duration::from_secs(5)).unwrap()); + } else { + panic!("invalid state"); + } + + notif.on_swarm_event(FromSwarm::ConnectionClosed( + libp2p::swarm::behaviour::ConnectionClosed { + peer_id: peer, + connection_id: conn, + endpoint: &connected, + handler: NotifsHandlerProto::new(vec![]).into_handler(&peer, &connected), + remaining_established: 0usize, + }, + )); + assert!(std::matches!(notif.peers.get(&(peer, set_id)), Some(&PeerState::Backoff { .. }))); + } + + #[test] + fn two_connections_inactive_connection_gets_closed_peer_state_is_still_incoming() { + let (mut notif, _peerset) = development_notifs(); + let peer = PeerId::random(); + let conn1 = ConnectionId::new(0usize); + let conn2 = ConnectionId::new(1usize); + let set_id = sc_peerset::SetId::from(0); + let connected = ConnectedPoint::Listener { + local_addr: Multiaddr::empty(), + send_back_addr: Multiaddr::empty(), + }; + + // open two connections + for conn_id in vec![conn1, conn2] { + notif.on_swarm_event(FromSwarm::ConnectionEstablished( + libp2p::swarm::behaviour::ConnectionEstablished { + peer_id: peer, + connection_id: conn_id, + endpoint: &connected, + failed_addresses: &[], + other_established: 0usize, + }, + )); + } + + if let Some(&PeerState::Disabled { ref connections, .. }) = notif.peers.get(&(peer, set_id)) + { + assert_eq!(connections[0], (conn1, ConnectionState::Closed)); + assert_eq!(connections[1], (conn2, ConnectionState::Closed)); + } else { + panic!("invalid state"); + } + + // remote opens a substream, verify that peer state is updated to `Incoming` + notif.on_connection_handler_event( + peer, + conn1, + NotifsHandlerOut::OpenDesiredByRemote { protocol_index: 0 }, + ); + assert!(std::matches!( + notif.peers.get_mut(&(peer, 0.into())), + Some(&mut PeerState::Incoming { .. }) + )); + + notif.on_swarm_event(FromSwarm::ConnectionClosed( + libp2p::swarm::behaviour::ConnectionClosed { + peer_id: peer, + connection_id: conn2, + endpoint: &connected, + handler: NotifsHandlerProto::new(vec![]).into_handler(&peer, &connected), + remaining_established: 0usize, + }, + )); + assert!(std::matches!(notif.peers.get(&(peer, set_id)), Some(&PeerState::Incoming { .. }))); + } + + #[test] + fn two_connections_active_connection_gets_closed_peer_state_is_disabled() { + let (mut notif, _peerset) = development_notifs(); + let peer = PeerId::random(); + let conn1 = ConnectionId::new(0usize); + let conn2 = ConnectionId::new(1usize); + let set_id = sc_peerset::SetId::from(0); + let connected = ConnectedPoint::Listener { + local_addr: Multiaddr::empty(), + send_back_addr: Multiaddr::empty(), + }; + + // open two connections + for conn_id in vec![conn1, conn2] { + notif.on_swarm_event(FromSwarm::ConnectionEstablished( + libp2p::swarm::behaviour::ConnectionEstablished { + peer_id: peer, + connection_id: conn_id, + endpoint: &ConnectedPoint::Listener { + local_addr: Multiaddr::empty(), + send_back_addr: Multiaddr::empty(), + }, + failed_addresses: &[], + other_established: 0usize, + }, + )); + } + + if let Some(&PeerState::Disabled { ref connections, .. }) = notif.peers.get(&(peer, set_id)) + { + assert_eq!(connections[0], (conn1, ConnectionState::Closed)); + assert_eq!(connections[1], (conn2, ConnectionState::Closed)); + } else { + panic!("invalid state"); + } + + // remote opens a substream, verify that peer state is updated to `Incoming` + notif.on_connection_handler_event( + peer, + conn1, + NotifsHandlerOut::OpenDesiredByRemote { protocol_index: 0 }, + ); + assert!(std::matches!( + notif.peers.get_mut(&(peer, 0.into())), + Some(PeerState::Incoming { .. }) + )); + + notif.on_swarm_event(FromSwarm::ConnectionClosed( + libp2p::swarm::behaviour::ConnectionClosed { + peer_id: peer, + connection_id: conn1, + endpoint: &connected, + handler: NotifsHandlerProto::new(vec![]).into_handler(&peer, &connected), + remaining_established: 0usize, + }, + )); + assert!(std::matches!(notif.peers.get(&(peer, set_id)), Some(&PeerState::Disabled { .. }))); + } + + #[test] + fn inject_connection_closed_for_active_connection() { + let (mut notif, _peerset) = development_notifs(); + let peer = PeerId::random(); + let conn1 = ConnectionId::new(0usize); + let conn2 = ConnectionId::new(1usize); + let set_id = sc_peerset::SetId::from(0); + let connected = ConnectedPoint::Listener { + local_addr: Multiaddr::empty(), + send_back_addr: Multiaddr::empty(), + }; + let mut conn_yielder = ConnectionYielder::new(); + + // open two connections + for conn_id in vec![conn1, conn2] { + notif.on_swarm_event(FromSwarm::ConnectionEstablished( + libp2p::swarm::behaviour::ConnectionEstablished { + peer_id: peer, + connection_id: conn_id, + endpoint: &connected, + failed_addresses: &[], + other_established: 0usize, + }, + )); + } + + if let Some(&PeerState::Disabled { ref connections, .. }) = notif.peers.get(&(peer, set_id)) + { + assert_eq!(connections[0], (conn1, ConnectionState::Closed)); + assert_eq!(connections[1], (conn2, ConnectionState::Closed)); + } else { + panic!("invalid state"); + } + + // open substreams on both active connections + notif.peerset_report_connect(peer, set_id); + + if let Some(&PeerState::Enabled { ref connections, .. }) = notif.peers.get(&(peer, set_id)) + { + assert_eq!(connections[0], (conn1, ConnectionState::Opening)); + assert_eq!(connections[1], (conn2, ConnectionState::Closed)); + } else { + panic!("invalid state"); + } + + notif.on_connection_handler_event( + peer, + conn1, + conn_yielder.open_substream(peer, 0, connected.clone(), vec![1, 2, 3, 4]), + ); + + if let Some(PeerState::Enabled { ref connections, .. }) = notif.peers.get(&(peer, set_id)) { + assert!(std::matches!(connections[0].1, ConnectionState::Open(_))); + assert_eq!(connections[0].0, conn1); + assert_eq!(connections[1], (conn2, ConnectionState::Closed)); + } else { + panic!("invalid state"); + } + + notif.on_swarm_event(FromSwarm::ConnectionClosed( + libp2p::swarm::behaviour::ConnectionClosed { + peer_id: peer, + connection_id: conn1, + endpoint: &connected, + handler: NotifsHandlerProto::new(vec![]).into_handler(&peer, &connected), + remaining_established: 0usize, + }, + )); + } + + #[test] + fn inject_dial_failure_for_pending_request() { + let (mut notif, _peerset) = development_notifs(); + let set_id = sc_peerset::SetId::from(0); + let peer = PeerId::random(); + let conn = ConnectionId::new(0usize); + let connected = ConnectedPoint::Listener { + local_addr: Multiaddr::empty(), + send_back_addr: Multiaddr::empty(), + }; + + notif.on_swarm_event(FromSwarm::ConnectionEstablished( + libp2p::swarm::behaviour::ConnectionEstablished { + peer_id: peer, + connection_id: conn, + endpoint: &connected, + failed_addresses: &[], + other_established: 0usize, + }, + )); + assert!(std::matches!(notif.peers.get(&(peer, set_id)), Some(&PeerState::Disabled { .. }))); + + // manually add backoff for the entry + if let Some(PeerState::Disabled { ref mut backoff_until, .. }) = + notif.peers.get_mut(&(peer, set_id)) + { + *backoff_until = + Some(Instant::now().checked_add(std::time::Duration::from_secs(5)).unwrap()); + } + + notif.on_swarm_event(FromSwarm::ConnectionClosed( + libp2p::swarm::behaviour::ConnectionClosed { + peer_id: peer, + connection_id: conn, + endpoint: &connected, + handler: NotifsHandlerProto::new(vec![]).into_handler(&peer, &connected), + remaining_established: 0usize, + }, + )); + + assert!(std::matches!(notif.peers.get(&(peer, set_id)), Some(&PeerState::Backoff { .. }))); + + // attempt to connect the backed-off peer and verify that the request is pending + notif.peerset_report_connect(peer, set_id); + assert!(std::matches!( + notif.peers.get(&(peer, set_id)), + Some(&PeerState::PendingRequest { .. }) + )); + + let now = Instant::now(); + notif.on_swarm_event(FromSwarm::DialFailure(libp2p::swarm::behaviour::DialFailure { + peer_id: Some(peer), + handler: NotifsHandlerProto::new(vec![]), + error: &libp2p::swarm::DialError::Banned, + })); + + if let Some(PeerState::PendingRequest { ref timer_deadline, .. }) = + notif.peers.get(&(peer, set_id)) + { + assert!(timer_deadline > &(now + std::time::Duration::from_secs(5))); + } + } + + #[test] + fn peerstate_incoming_open_desired_by_remote() { + let (mut notif, _peerset) = development_notifs(); + let peer = PeerId::random(); + let set_id = sc_peerset::SetId::from(0); + let conn1 = ConnectionId::new(0usize); + let conn2 = ConnectionId::new(1usize); + let connected = ConnectedPoint::Listener { + local_addr: Multiaddr::empty(), + send_back_addr: Multiaddr::empty(), + }; + + notif.on_swarm_event(FromSwarm::ConnectionEstablished( + libp2p::swarm::behaviour::ConnectionEstablished { + peer_id: peer, + connection_id: conn1, + endpoint: &connected, + failed_addresses: &[], + other_established: 0usize, + }, + )); + notif.on_swarm_event(FromSwarm::ConnectionEstablished( + libp2p::swarm::behaviour::ConnectionEstablished { + peer_id: peer, + connection_id: conn2, + endpoint: &connected, + failed_addresses: &[], + other_established: 0usize, + }, + )); + assert!(std::matches!(notif.peers.get(&(peer, set_id)), Some(&PeerState::Disabled { .. }))); + + // remote opens a substream, verify that peer state is updated to `Incoming` + notif.on_connection_handler_event( + peer, + conn1, + NotifsHandlerOut::OpenDesiredByRemote { protocol_index: 0 }, + ); + assert!(std::matches!(notif.peers.get(&(peer, set_id)), Some(&PeerState::Incoming { .. }))); + + // add another open event from remote + notif.on_connection_handler_event( + peer, + conn2, + NotifsHandlerOut::OpenDesiredByRemote { protocol_index: 0 }, + ); + + if let Some(PeerState::Incoming { ref connections, .. }) = notif.peers.get(&(peer, set_id)) + { + assert_eq!(connections[0], (conn1, ConnectionState::OpenDesiredByRemote)); + assert_eq!(connections[1], (conn2, ConnectionState::OpenDesiredByRemote)); + } + } + + #[tokio::test] + async fn remove_backoff_peer_after_timeout() { + let (mut notif, _peerset) = development_notifs(); + let peer = PeerId::random(); + let set_id = sc_peerset::SetId::from(0); + let conn = ConnectionId::new(0usize); + let connected = ConnectedPoint::Listener { + local_addr: Multiaddr::empty(), + send_back_addr: Multiaddr::empty(), + }; + + notif.on_swarm_event(FromSwarm::ConnectionEstablished( + libp2p::swarm::behaviour::ConnectionEstablished { + peer_id: peer, + connection_id: conn, + endpoint: &connected, + failed_addresses: &[], + other_established: 0usize, + }, + )); + + if let Some(&mut PeerState::Disabled { ref mut backoff_until, .. }) = + notif.peers.get_mut(&(peer, 0.into())) + { + *backoff_until = + Some(Instant::now().checked_add(std::time::Duration::from_millis(100)).unwrap()); + } else { + panic!("invalid state"); + } + + notif.on_swarm_event(FromSwarm::ConnectionClosed( + libp2p::swarm::behaviour::ConnectionClosed { + peer_id: peer, + connection_id: conn, + endpoint: &connected, + handler: NotifsHandlerProto::new(vec![]).into_handler(&peer, &connected), + remaining_established: 0usize, + }, + )); + + let until = if let Some(&PeerState::Backoff { timer_deadline, .. }) = + notif.peers.get(&(peer, set_id)) + { + timer_deadline + } else { + panic!("invalid state"); + }; + + if until > Instant::now() { + std::thread::sleep(until - Instant::now()); + } + + assert!(notif.peers.get(&(peer, set_id)).is_some()); + + if tokio::time::timeout(Duration::from_secs(5), async { + let mut params = MockPollParams { peer_id: PeerId::random(), addr: Multiaddr::empty() }; + + loop { + futures::future::poll_fn(|cx| { + let _ = notif.poll(cx, &mut params); + Poll::Ready(()) + }) + .await; + + if notif.peers.get(&(peer, set_id)).is_none() { + break + } + } + }) + .await + .is_err() + { + panic!("backoff peer was not removed in time"); + } + + assert!(notif.peers.get(&(peer, set_id)).is_none()); + } + + #[tokio::test] + async fn reschedule_disabled_pending_enable_when_connection_not_closed() { + let (mut notif, _peerset) = development_notifs(); + let peer = PeerId::random(); + let conn = ConnectionId::new(0usize); + let set_id = sc_peerset::SetId::from(0); + let connected = ConnectedPoint::Listener { + local_addr: Multiaddr::empty(), + send_back_addr: Multiaddr::empty(), + }; + let mut conn_yielder = ConnectionYielder::new(); + + // move the peer to `Enabled` state + notif.on_swarm_event(FromSwarm::ConnectionEstablished( + libp2p::swarm::behaviour::ConnectionEstablished { + peer_id: peer, + connection_id: conn, + endpoint: &ConnectedPoint::Listener { + local_addr: Multiaddr::empty(), + send_back_addr: Multiaddr::empty(), + }, + failed_addresses: &[], + other_established: 0usize, + }, + )); + assert!(std::matches!(notif.peers.get(&(peer, set_id)), Some(&PeerState::Disabled { .. }))); + + // open substream + notif.on_connection_handler_event( + peer, + conn, + NotifsHandlerOut::OpenDesiredByRemote { protocol_index: 0 }, + ); + assert!(std::matches!(notif.peers.get(&(peer, set_id)), Some(&PeerState::Incoming { .. }))); + + notif.peerset_report_connect(peer, set_id); + assert!(std::matches!(notif.peers.get(&(peer, set_id)), Some(&PeerState::Enabled { .. }))); + + let event = conn_yielder.open_substream(peer, 0, connected, vec![1, 2, 3, 4]); + + notif.on_connection_handler_event(peer, conn, event); + assert!(std::matches!(notif.peers.get(&(peer, set_id)), Some(&PeerState::Enabled { .. }))); + + if let Some(PeerState::Enabled { ref connections, .. }) = notif.peers.get(&(peer, set_id)) { + assert!(std::matches!(connections[0], (_, ConnectionState::Open(_)))); + assert_eq!(connections[0].0, conn); + } else { + panic!("invalid state"); + } + + notif.peerset_report_disconnect(peer, set_id); + + if let Some(PeerState::Disabled { ref connections, ref mut backoff_until }) = + notif.peers.get_mut(&(peer, set_id)) + { + assert!(std::matches!(connections[0], (_, ConnectionState::Closing))); + assert_eq!(connections[0].0, conn); + + *backoff_until = + Some(Instant::now().checked_add(std::time::Duration::from_secs(2)).unwrap()); + } else { + panic!("invalid state"); + } + + notif.peerset_report_connect(peer, set_id); + + let prev_instant = + if let Some(PeerState::DisabledPendingEnable { + ref connections, timer_deadline, .. + }) = notif.peers.get(&(peer, set_id)) + { + assert!(std::matches!(connections[0], (_, ConnectionState::Closing))); + assert_eq!(connections[0].0, conn); + + *timer_deadline + } else { + panic!("invalid state"); + }; + + // one of the peers has an active backoff timer so poll the notifications code until + // the timer has expired. Because the connection is still in the state of `Closing`, + // verify that the code continues to keep the peer disabled by resetting the timer + // after the first one expired. + if tokio::time::timeout(Duration::from_secs(5), async { + let mut params = MockPollParams { peer_id: PeerId::random(), addr: Multiaddr::empty() }; + + loop { + futures::future::poll_fn(|cx| { + let _ = notif.poll(cx, &mut params); + Poll::Ready(()) + }) + .await; + + if let Some(PeerState::DisabledPendingEnable { + timer_deadline, connections, .. + }) = notif.peers.get(&(peer, set_id)) + { + assert!(std::matches!(connections[0], (_, ConnectionState::Closing))); + + if timer_deadline != &prev_instant { + break + } + } else { + panic!("invalid state"); + } + } + }) + .await + .is_err() + { + panic!("backoff peer was not removed in time"); + } + } + + #[test] + #[should_panic] + #[cfg(debug_assertions)] + fn peerset_report_connect_with_enabled_peer() { + let (mut notif, _peerset) = development_notifs(); + let peer = PeerId::random(); + let conn = ConnectionId::new(0usize); + let set_id = sc_peerset::SetId::from(0); + let connected = ConnectedPoint::Listener { + local_addr: Multiaddr::empty(), + send_back_addr: Multiaddr::empty(), + }; + let mut conn_yielder = ConnectionYielder::new(); + + // move the peer to `Enabled` state + notif.on_swarm_event(FromSwarm::ConnectionEstablished( + libp2p::swarm::behaviour::ConnectionEstablished { + peer_id: peer, + connection_id: conn, + endpoint: &connected, + failed_addresses: &[], + other_established: 0usize, + }, + )); + assert!(std::matches!(notif.peers.get(&(peer, set_id)), Some(&PeerState::Disabled { .. }))); + + notif.on_connection_handler_event( + peer, + conn, + NotifsHandlerOut::OpenDesiredByRemote { protocol_index: 0 }, + ); + assert!(std::matches!(notif.peers.get(&(peer, set_id)), Some(&PeerState::Incoming { .. }))); + + notif.peerset_report_connect(peer, set_id); + assert!(std::matches!(notif.peers.get(&(peer, set_id)), Some(&PeerState::Enabled { .. }))); + + let event = conn_yielder.open_substream(peer, 0, connected, vec![1, 2, 3, 4]); + + notif.on_connection_handler_event(peer, conn, event); + assert!(std::matches!(notif.peers.get(&(peer, set_id)), Some(&PeerState::Enabled { .. }))); + + if let Some(PeerState::Enabled { ref connections, .. }) = notif.peers.get(&(peer, set_id)) { + assert!(std::matches!(connections[0], (_, ConnectionState::Open(_)))); + assert_eq!(connections[0].0, conn); + } else { + panic!("invalid state"); + } + + notif.peerset_report_connect(peer, set_id); + } + + #[test] + #[should_panic] + #[cfg(debug_assertions)] + fn peerset_report_connect_with_disabled_pending_enable_peer() { + let (mut notif, _peerset) = development_notifs(); + let set_id = sc_peerset::SetId::from(0); + let peer = PeerId::random(); + let conn = ConnectionId::new(0usize); + let connected = ConnectedPoint::Listener { + local_addr: Multiaddr::empty(), + send_back_addr: Multiaddr::empty(), + }; + + notif.on_swarm_event(FromSwarm::ConnectionEstablished( + libp2p::swarm::behaviour::ConnectionEstablished { + peer_id: peer, + connection_id: conn, + endpoint: &connected, + failed_addresses: &[], + other_established: 0usize, + }, + )); + assert!(std::matches!(notif.peers.get(&(peer, set_id)), Some(&PeerState::Disabled { .. }))); + + // manually add backoff for the entry + if let Some(PeerState::Disabled { ref mut backoff_until, .. }) = + notif.peers.get_mut(&(peer, set_id)) + { + *backoff_until = + Some(Instant::now().checked_add(std::time::Duration::from_secs(5)).unwrap()); + } + + // switch state to `DisabledPendingEnable` + notif.peerset_report_connect(peer, set_id); + assert!(std::matches!( + notif.peers.get(&(peer, set_id)), + Some(&PeerState::DisabledPendingEnable { .. }) + )); + + notif.peerset_report_connect(peer, set_id); + } + + #[test] + #[should_panic] + #[cfg(debug_assertions)] + fn peerset_report_connect_with_requested_peer() { + let (mut notif, _peerset) = development_notifs(); + let peer = PeerId::random(); + let set_id = sc_peerset::SetId::from(0); + + // Set peer into `Requested` state. + notif.peerset_report_connect(peer, set_id); + assert!(std::matches!(notif.peers.get(&(peer, set_id)), Some(&PeerState::Requested))); + + notif.peerset_report_connect(peer, set_id); + } + + #[test] + #[should_panic] + #[cfg(debug_assertions)] + fn peerset_report_connect_with_pending_requested() { + let (mut notif, _peerset) = development_notifs(); + let set_id = sc_peerset::SetId::from(0); + let peer = PeerId::random(); + let conn = ConnectionId::new(0usize); + let connected = ConnectedPoint::Listener { + local_addr: Multiaddr::empty(), + send_back_addr: Multiaddr::empty(), + }; + + notif.on_swarm_event(FromSwarm::ConnectionEstablished( + libp2p::swarm::behaviour::ConnectionEstablished { + peer_id: peer, + connection_id: conn, + endpoint: &connected, + failed_addresses: &[], + other_established: 0usize, + }, + )); + assert!(std::matches!(notif.peers.get(&(peer, set_id)), Some(&PeerState::Disabled { .. }))); + + // manually add backoff for the entry + if let Some(PeerState::Disabled { ref mut backoff_until, .. }) = + notif.peers.get_mut(&(peer, set_id)) + { + *backoff_until = + Some(Instant::now().checked_add(std::time::Duration::from_secs(5)).unwrap()); + } + + notif.on_swarm_event(FromSwarm::ConnectionClosed( + libp2p::swarm::behaviour::ConnectionClosed { + peer_id: peer, + connection_id: conn, + endpoint: &connected, + handler: NotifsHandlerProto::new(vec![]).into_handler(&peer, &connected), + remaining_established: 0usize, + }, + )); + assert!(std::matches!(notif.peers.get(&(peer, set_id)), Some(&PeerState::Backoff { .. }))); + + // attempt to connect the backed-off peer and verify that the request is pending + notif.peerset_report_connect(peer, set_id); + assert!(std::matches!( + notif.peers.get(&(peer, set_id)), + Some(&PeerState::PendingRequest { .. }) + )); + + notif.peerset_report_connect(peer, set_id); + } + + #[test] + #[should_panic] + #[cfg(debug_assertions)] + fn peerset_report_disconnect_with_incoming_peer() { + let (mut notif, _peerset) = development_notifs(); + let peer = PeerId::random(); + let set_id = sc_peerset::SetId::from(0); + let conn = ConnectionId::new(0usize); + let connected = ConnectedPoint::Listener { + local_addr: Multiaddr::empty(), + send_back_addr: Multiaddr::empty(), + }; + + notif.on_swarm_event(FromSwarm::ConnectionEstablished( + libp2p::swarm::behaviour::ConnectionEstablished { + peer_id: peer, + connection_id: conn, + endpoint: &connected, + failed_addresses: &[], + other_established: 0usize, + }, + )); + assert!(std::matches!(notif.peers.get(&(peer, set_id)), Some(&PeerState::Disabled { .. }))); + + // remote opens a substream, verify that peer state is updated to `Incoming` + notif.on_connection_handler_event( + peer, + conn, + NotifsHandlerOut::OpenDesiredByRemote { protocol_index: 0 }, + ); + + assert!(std::matches!(notif.peers.get(&(peer, set_id)), Some(&PeerState::Incoming { .. }))); + + notif.peerset_report_disconnect(peer, set_id); + } + + #[test] + #[should_panic] + #[cfg(debug_assertions)] + fn peerset_report_accept_incoming_peer() { + let (mut notif, _peerset) = development_notifs(); + let peer = PeerId::random(); + let conn = ConnectionId::new(0usize); + let set_id = sc_peerset::SetId::from(0); + let connected = ConnectedPoint::Listener { + local_addr: Multiaddr::empty(), + send_back_addr: Multiaddr::empty(), + }; + + notif.on_swarm_event(FromSwarm::ConnectionEstablished( + libp2p::swarm::behaviour::ConnectionEstablished { + peer_id: peer, + connection_id: conn, + endpoint: &connected, + failed_addresses: &[], + other_established: 0usize, + }, + )); + assert!(std::matches!(notif.peers.get(&(peer, set_id)), Some(&PeerState::Disabled { .. }))); + + // remote opens a substream, verify that peer state is updated to `Incoming` + notif.on_connection_handler_event( + peer, + conn, + NotifsHandlerOut::OpenDesiredByRemote { protocol_index: 0 }, + ); + assert!(std::matches!(notif.peers.get(&(peer, set_id)), Some(&PeerState::Incoming { .. }))); + + assert!(std::matches!( + notif.incoming[0], + IncomingPeer { alive: true, incoming_id: sc_peerset::IncomingIndex(0), .. }, + )); + + notif.peers.remove(&(peer, set_id)); + notif.peerset_report_accept(sc_peerset::IncomingIndex(0)); + } + + #[test] + #[should_panic] + #[cfg(debug_assertions)] + fn peerset_report_accept_not_incoming_peer() { + let (mut notif, _peerset) = development_notifs(); + let peer = PeerId::random(); + let conn = ConnectionId::new(0usize); + let set_id = sc_peerset::SetId::from(0); + let connected = ConnectedPoint::Listener { + local_addr: Multiaddr::empty(), + send_back_addr: Multiaddr::empty(), + }; + let mut conn_yielder = ConnectionYielder::new(); + + notif.on_swarm_event(FromSwarm::ConnectionEstablished( + libp2p::swarm::behaviour::ConnectionEstablished { + peer_id: peer, + connection_id: conn, + endpoint: &connected, + failed_addresses: &[], + other_established: 0usize, + }, + )); + assert!(std::matches!(notif.peers.get(&(peer, set_id)), Some(&PeerState::Disabled { .. }))); + + // remote opens a substream, verify that peer state is updated to `Incoming` + notif.on_connection_handler_event( + peer, + conn, + NotifsHandlerOut::OpenDesiredByRemote { protocol_index: 0 }, + ); + assert!(std::matches!(notif.peers.get(&(peer, set_id)), Some(&PeerState::Incoming { .. }))); + + assert!(std::matches!( + notif.incoming[0], + IncomingPeer { alive: true, incoming_id: sc_peerset::IncomingIndex(0), .. }, + )); + + notif.peerset_report_connect(peer, set_id); + assert!(std::matches!(notif.peers.get(&(peer, set_id)), Some(&PeerState::Enabled { .. }))); + + let event = conn_yielder.open_substream(peer, 0, connected, vec![1, 2, 3, 4]); + notif.on_connection_handler_event(peer, conn, event); + + assert!(std::matches!(notif.peers.get(&(peer, set_id)), Some(&PeerState::Enabled { .. }))); + notif.incoming[0].alive = true; + notif.peerset_report_accept(sc_peerset::IncomingIndex(0)); + } + + #[test] + #[should_panic] + #[cfg(debug_assertions)] + fn inject_connection_closed_non_existent_peer() { + let (mut notif, _peerset) = development_notifs(); + let peer = PeerId::random(); + let endpoint = ConnectedPoint::Listener { + local_addr: Multiaddr::empty(), + send_back_addr: Multiaddr::empty(), + }; + + notif.on_swarm_event(FromSwarm::ConnectionClosed( + libp2p::swarm::behaviour::ConnectionClosed { + peer_id: peer, + connection_id: ConnectionId::new(0usize), + endpoint: &endpoint, + handler: NotifsHandlerProto::new(vec![]).into_handler(&peer, &endpoint), + remaining_established: 0usize, + }, + )); + } + + #[test] + fn disconnect_non_existent_peer() { + let (mut notif, _peerset) = development_notifs(); + let peer = PeerId::random(); + let set_id = sc_peerset::SetId::from(0); + + notif.peerset_report_disconnect(peer, set_id); + + assert!(notif.peers.is_empty()); + assert!(notif.incoming.is_empty()); + } + + #[test] + fn accept_non_existent_connection() { + let (mut notif, _peerset) = development_notifs(); + + notif.peerset_report_accept(0.into()); + + assert!(notif.peers.is_empty()); + assert!(notif.incoming.is_empty()); + } + + #[test] + fn reject_non_existent_connection() { + let (mut notif, _peerset) = development_notifs(); + + notif.peerset_report_reject(0.into()); + + assert!(notif.peers.is_empty()); + assert!(notif.incoming.is_empty()); + } + + #[test] + fn reject_non_active_connection() { + let (mut notif, _peerset) = development_notifs(); + let peer = PeerId::random(); + let conn = ConnectionId::new(0usize); + let set_id = sc_peerset::SetId::from(0); + let connected = ConnectedPoint::Listener { + local_addr: Multiaddr::empty(), + send_back_addr: Multiaddr::empty(), + }; + + notif.on_swarm_event(FromSwarm::ConnectionEstablished( + libp2p::swarm::behaviour::ConnectionEstablished { + peer_id: peer, + connection_id: conn, + endpoint: &connected, + failed_addresses: &[], + other_established: 0usize, + }, + )); + assert!(std::matches!(notif.peers.get(&(peer, set_id)), Some(&PeerState::Disabled { .. }))); + + // remote opens a substream, verify that peer state is updated to `Incoming` + notif.on_connection_handler_event( + peer, + conn, + NotifsHandlerOut::OpenDesiredByRemote { protocol_index: 0 }, + ); + assert!(std::matches!(notif.peers.get(&(peer, set_id)), Some(&PeerState::Incoming { .. }))); + + notif.incoming[0].alive = false; + notif.peerset_report_reject(0.into()); + + assert!(std::matches!(notif.peers.get(&(peer, set_id)), Some(&PeerState::Incoming { .. }))); + } + + #[test] + #[should_panic] + #[cfg(debug_assertions)] + fn reject_non_existent_peer_but_alive_connection() { + let (mut notif, _peerset) = development_notifs(); + let peer = PeerId::random(); + let conn = ConnectionId::new(0usize); + let set_id = sc_peerset::SetId::from(0); + let connected = ConnectedPoint::Listener { + local_addr: Multiaddr::empty(), + send_back_addr: Multiaddr::empty(), + }; + + notif.on_swarm_event(FromSwarm::ConnectionEstablished( + libp2p::swarm::behaviour::ConnectionEstablished { + peer_id: peer, + connection_id: conn, + endpoint: &connected, + failed_addresses: &[], + other_established: 0usize, + }, + )); + assert!(std::matches!(notif.peers.get(&(peer, set_id)), Some(&PeerState::Disabled { .. }))); + + // remote opens a substream, verify that peer state is updated to `Incoming` + notif.on_connection_handler_event( + peer, + conn, + NotifsHandlerOut::OpenDesiredByRemote { protocol_index: 0 }, + ); + assert!(std::matches!(notif.peers.get(&(peer, set_id)), Some(&PeerState::Incoming { .. }))); + assert!(std::matches!( + notif.incoming[0], + IncomingPeer { alive: true, incoming_id: sc_peerset::IncomingIndex(0), .. }, + )); + + notif.peers.remove(&(peer, set_id)); + notif.peerset_report_reject(0.into()); + } + + #[test] + #[should_panic] + #[cfg(debug_assertions)] + fn inject_non_existent_connection_closed_for_incoming_peer() { + let (mut notif, _peerset) = development_notifs(); + let peer = PeerId::random(); + let conn = ConnectionId::new(0usize); + let set_id = sc_peerset::SetId::from(0); + let connected = ConnectedPoint::Listener { + local_addr: Multiaddr::empty(), + send_back_addr: Multiaddr::empty(), + }; + + notif.on_swarm_event(FromSwarm::ConnectionEstablished( + libp2p::swarm::behaviour::ConnectionEstablished { + peer_id: peer, + connection_id: conn, + endpoint: &connected, + failed_addresses: &[], + other_established: 0usize, + }, + )); + assert!(std::matches!(notif.peers.get(&(peer, set_id)), Some(&PeerState::Disabled { .. }))); + + // remote opens a substream, verify that peer state is updated to `Incoming` + notif.on_connection_handler_event( + peer, + conn, + NotifsHandlerOut::OpenDesiredByRemote { protocol_index: 0 }, + ); + assert!(std::matches!(notif.peers.get(&(peer, set_id)), Some(&PeerState::Incoming { .. }))); + + notif.on_swarm_event(FromSwarm::ConnectionClosed( + libp2p::swarm::behaviour::ConnectionClosed { + peer_id: peer, + connection_id: ConnectionId::new(1337usize), + endpoint: &connected, + handler: NotifsHandlerProto::new(vec![]).into_handler(&peer, &connected), + remaining_established: 0usize, + }, + )); + } + + #[test] + #[should_panic] + #[cfg(debug_assertions)] + fn inject_non_existent_connection_closed_for_disabled_peer() { + let (mut notif, _peerset) = development_notifs(); + let set_id = sc_peerset::SetId::from(0); + let peer = PeerId::random(); + let conn = ConnectionId::new(0usize); + let connected = ConnectedPoint::Listener { + local_addr: Multiaddr::empty(), + send_back_addr: Multiaddr::empty(), + }; + + notif.on_swarm_event(FromSwarm::ConnectionEstablished( + libp2p::swarm::behaviour::ConnectionEstablished { + peer_id: peer, + connection_id: conn, + endpoint: &connected, + failed_addresses: &[], + other_established: 0usize, + }, + )); + assert!(std::matches!(notif.peers.get(&(peer, set_id)), Some(&PeerState::Disabled { .. }))); + + notif.on_swarm_event(FromSwarm::ConnectionClosed( + libp2p::swarm::behaviour::ConnectionClosed { + peer_id: peer, + connection_id: ConnectionId::new(1337usize), + endpoint: &connected, + handler: NotifsHandlerProto::new(vec![]).into_handler(&peer, &connected), + remaining_established: 0usize, + }, + )); + } + + #[test] + #[should_panic] + #[cfg(debug_assertions)] + fn inject_non_existent_connection_closed_for_disabled_pending_enable() { + let (mut notif, _peerset) = development_notifs(); + let set_id = sc_peerset::SetId::from(0); + let peer = PeerId::random(); + let conn = ConnectionId::new(0usize); + let connected = ConnectedPoint::Listener { + local_addr: Multiaddr::empty(), + send_back_addr: Multiaddr::empty(), + }; + + notif.on_swarm_event(FromSwarm::ConnectionEstablished( + libp2p::swarm::behaviour::ConnectionEstablished { + peer_id: peer, + connection_id: conn, + endpoint: &connected, + failed_addresses: &[], + other_established: 0usize, + }, + )); + assert!(std::matches!(notif.peers.get(&(peer, set_id)), Some(&PeerState::Disabled { .. }))); + + // manually add backoff for the entry + if let Some(PeerState::Disabled { ref mut backoff_until, .. }) = + notif.peers.get_mut(&(peer, set_id)) + { + *backoff_until = + Some(Instant::now().checked_add(std::time::Duration::from_secs(5)).unwrap()); + } + + // switch state to `DisabledPendingEnable` + notif.peerset_report_connect(peer, set_id); + + assert!(std::matches!( + notif.peers.get(&(peer, set_id)), + Some(&PeerState::DisabledPendingEnable { .. }) + )); + + notif.on_swarm_event(FromSwarm::ConnectionClosed( + libp2p::swarm::behaviour::ConnectionClosed { + peer_id: peer, + connection_id: ConnectionId::new(1337usize), + endpoint: &connected, + handler: NotifsHandlerProto::new(vec![]).into_handler(&peer, &connected), + remaining_established: 0usize, + }, + )); + } + + #[test] + #[should_panic] + #[cfg(debug_assertions)] + fn inject_connection_closed_for_incoming_peer_state_mismatch() { + let (mut notif, _peerset) = development_notifs(); + let peer = PeerId::random(); + let conn = ConnectionId::new(0usize); + let set_id = sc_peerset::SetId::from(0); + let connected = ConnectedPoint::Listener { + local_addr: Multiaddr::empty(), + send_back_addr: Multiaddr::empty(), + }; + + notif.on_swarm_event(FromSwarm::ConnectionEstablished( + libp2p::swarm::behaviour::ConnectionEstablished { + peer_id: peer, + connection_id: conn, + endpoint: &connected, + failed_addresses: &[], + other_established: 0usize, + }, + )); + assert!(std::matches!(notif.peers.get(&(peer, set_id)), Some(&PeerState::Disabled { .. }))); + + // remote opens a substream, verify that peer state is updated to `Incoming` + notif.on_connection_handler_event( + peer, + conn, + NotifsHandlerOut::OpenDesiredByRemote { protocol_index: 0 }, + ); + assert!(std::matches!(notif.peers.get(&(peer, set_id)), Some(&PeerState::Incoming { .. }))); + notif.incoming[0].alive = false; + + notif.on_swarm_event(FromSwarm::ConnectionClosed( + libp2p::swarm::behaviour::ConnectionClosed { + peer_id: peer, + connection_id: conn, + endpoint: &connected, + handler: NotifsHandlerProto::new(vec![]).into_handler(&peer, &connected), + remaining_established: 0usize, + }, + )); + } + + #[test] + #[should_panic] + #[cfg(debug_assertions)] + fn inject_connection_closed_for_enabled_state_mismatch() { + let (mut notif, _peerset) = development_notifs(); + let peer = PeerId::random(); + let conn = ConnectionId::new(0usize); + let set_id = sc_peerset::SetId::from(0); + let connected = ConnectedPoint::Listener { + local_addr: Multiaddr::empty(), + send_back_addr: Multiaddr::empty(), + }; + + notif.on_swarm_event(FromSwarm::ConnectionEstablished( + libp2p::swarm::behaviour::ConnectionEstablished { + peer_id: peer, + connection_id: conn, + endpoint: &connected, + failed_addresses: &[], + other_established: 0usize, + }, + )); + assert!(std::matches!(notif.peers.get(&(peer, set_id)), Some(&PeerState::Disabled { .. }))); + + // remote opens a substream, verify that peer state is updated to `Incoming` + notif.on_connection_handler_event( + peer, + conn, + NotifsHandlerOut::OpenDesiredByRemote { protocol_index: 0 }, + ); + assert!(std::matches!(notif.peers.get(&(peer, set_id)), Some(&PeerState::Incoming { .. }))); + + // attempt to connect to the peer and verify that the peer state is `Enabled` + notif.peerset_report_connect(peer, set_id); + assert!(std::matches!(notif.peers.get(&(peer, set_id)), Some(&PeerState::Enabled { .. }))); + + notif.on_swarm_event(FromSwarm::ConnectionClosed( + libp2p::swarm::behaviour::ConnectionClosed { + peer_id: peer, + connection_id: ConnectionId::new(1337usize), + endpoint: &connected, + handler: NotifsHandlerProto::new(vec![]).into_handler(&peer, &connected), + remaining_established: 0usize, + }, + )); + } + + #[test] + #[should_panic] + #[cfg(debug_assertions)] + fn inject_connection_closed_for_backoff_peer() { + let (mut notif, _peerset) = development_notifs(); + let set_id = sc_peerset::SetId::from(0); + let peer = PeerId::random(); + let conn = ConnectionId::new(0usize); + let connected = ConnectedPoint::Listener { + local_addr: Multiaddr::empty(), + send_back_addr: Multiaddr::empty(), + }; + + notif.on_swarm_event(FromSwarm::ConnectionEstablished( + libp2p::swarm::behaviour::ConnectionEstablished { + peer_id: peer, + connection_id: conn, + endpoint: &connected, + failed_addresses: &[], + other_established: 0usize, + }, + )); + assert!(std::matches!(notif.peers.get(&(peer, set_id)), Some(&PeerState::Disabled { .. }))); + + // manually add backoff for the entry + if let Some(PeerState::Disabled { ref mut backoff_until, .. }) = + notif.peers.get_mut(&(peer, set_id)) + { + *backoff_until = + Some(Instant::now().checked_add(std::time::Duration::from_secs(5)).unwrap()); + } + + notif.on_swarm_event(FromSwarm::ConnectionClosed( + libp2p::swarm::behaviour::ConnectionClosed { + peer_id: peer, + connection_id: conn, + endpoint: &connected, + handler: NotifsHandlerProto::new(vec![]).into_handler(&peer, &connected), + remaining_established: 0usize, + }, + )); + assert!(std::matches!(notif.peers.get(&(peer, set_id)), Some(&PeerState::Backoff { .. }))); + + notif.on_swarm_event(FromSwarm::ConnectionClosed( + libp2p::swarm::behaviour::ConnectionClosed { + peer_id: peer, + connection_id: conn, + endpoint: &connected, + handler: NotifsHandlerProto::new(vec![]).into_handler(&peer, &connected), + remaining_established: 0usize, + }, + )); + } + + #[test] + #[should_panic] + #[cfg(debug_assertions)] + fn open_result_ok_non_existent_peer() { + let (mut notif, _peerset) = development_notifs(); + let conn = ConnectionId::new(0usize); + let connected = ConnectedPoint::Listener { + local_addr: Multiaddr::empty(), + send_back_addr: Multiaddr::empty(), + }; + let mut conn_yielder = ConnectionYielder::new(); + + notif.on_connection_handler_event( + PeerId::random(), + conn, + conn_yielder.open_substream(PeerId::random(), 0, connected, vec![1, 2, 3, 4]), + ); + } +} diff --git a/client/network/src/protocol/notifications/handler.rs b/client/network/src/protocol/notifications/handler.rs index ea09cb76e..57561c7b9 100644 --- a/client/network/src/protocol/notifications/handler.rs +++ b/client/network/src/protocol/notifications/handler.rs @@ -58,8 +58,8 @@ //! [`NotifsHandlerIn::Open`] has gotten an answer. use crate::protocol::notifications::upgrade::{ - NotificationsHandshakeError, NotificationsIn, NotificationsInSubstream, NotificationsOut, - NotificationsOutSubstream, UpgradeCollec, + NotificationsIn, NotificationsInSubstream, NotificationsOut, NotificationsOutSubstream, + UpgradeCollec, }; use bytes::BytesMut; @@ -69,12 +69,9 @@ use futures::{ prelude::*, }; use libp2p::{ - core::{ - upgrade::{InboundUpgrade, OutboundUpgrade}, - ConnectedPoint, PeerId, - }, + core::{ConnectedPoint, PeerId}, swarm::{ - ConnectionHandler, ConnectionHandlerEvent, ConnectionHandlerUpgrErr, IntoConnectionHandler, + handler::ConnectionEvent, ConnectionHandler, ConnectionHandlerEvent, IntoConnectionHandler, KeepAlive, NegotiatedSubstream, SubstreamProtocol, }, }; @@ -494,98 +491,128 @@ impl ConnectionHandler for NotifsHandler { SubstreamProtocol::new(protocols, ()) } - fn inject_fully_negotiated_inbound( + fn on_connection_event( &mut self, - (mut in_substream_open, protocol_index): >::Output, - (): (), + event: ConnectionEvent< + '_, + Self::InboundProtocol, + Self::OutboundProtocol, + Self::InboundOpenInfo, + Self::OutboundOpenInfo, + >, ) { - let mut protocol_info = &mut self.protocols[protocol_index]; - match protocol_info.state { - State::Closed { pending_opening } => { - self.events_queue.push_back(ConnectionHandlerEvent::Custom( - NotifsHandlerOut::OpenDesiredByRemote { protocol_index }, - )); + match event { + ConnectionEvent::FullyNegotiatedInbound(inbound) => { + let (mut in_substream_open, protocol_index) = inbound.protocol; + let mut protocol_info = &mut self.protocols[protocol_index]; - protocol_info.state = State::OpenDesiredByRemote { - in_substream: in_substream_open.substream, - pending_opening, - }; - }, - State::OpenDesiredByRemote { .. } => { - // If a substream already exists, silently drop the new one. - // Note that we drop the substream, which will send an equivalent to a - // TCP "RST" to the remote and force-close the substream. It might - // seem like an unclean way to get rid of a substream. However, keep - // in mind that it is invalid for the remote to open multiple such - // substreams, and therefore sending a "RST" is the most correct thing - // to do. - return - }, - State::Opening { ref mut in_substream, .. } | - State::Open { ref mut in_substream, .. } => { - if in_substream.is_some() { - // Same remark as above. - return - } + match protocol_info.state { + State::Closed { pending_opening } => { + self.events_queue.push_back(ConnectionHandlerEvent::Custom( + NotifsHandlerOut::OpenDesiredByRemote { protocol_index }, + )); - // Create `handshake_message` on a separate line to be sure that the - // lock is released as soon as possible. - let handshake_message = protocol_info.config.handshake.read().clone(); - in_substream_open.substream.send_handshake(handshake_message); - *in_substream = Some(in_substream_open.substream); - }, - } - } + protocol_info.state = State::OpenDesiredByRemote { + in_substream: in_substream_open.substream, + pending_opening, + }; + }, + State::OpenDesiredByRemote { .. } => { + // If a substream already exists, silently drop the new one. + // Note that we drop the substream, which will send an equivalent to a + // TCP "RST" to the remote and force-close the substream. It might + // seem like an unclean way to get rid of a substream. However, keep + // in mind that it is invalid for the remote to open multiple such + // substreams, and therefore sending a "RST" is the most correct thing + // to do. + return + }, + State::Opening { ref mut in_substream, .. } | + State::Open { ref mut in_substream, .. } => { + if in_substream.is_some() { + // Same remark as above. + return + } - fn inject_fully_negotiated_outbound( - &mut self, - new_open: >::Output, - protocol_index: Self::OutboundOpenInfo, - ) { - match self.protocols[protocol_index].state { - State::Closed { ref mut pending_opening } | - State::OpenDesiredByRemote { ref mut pending_opening, .. } => { - debug_assert!(*pending_opening); - *pending_opening = false; - }, - State::Open { .. } => { - error!(target: "sub-libp2p", "☎️ State mismatch in notifications handler"); - debug_assert!(false); + // Create `handshake_message` on a separate line to be sure that the + // lock is released as soon as possible. + let handshake_message = protocol_info.config.handshake.read().clone(); + in_substream_open.substream.send_handshake(handshake_message); + *in_substream = Some(in_substream_open.substream); + }, + } }, - State::Opening { ref mut in_substream } => { - let (async_tx, async_rx) = mpsc::channel(ASYNC_NOTIFICATIONS_BUFFER_SIZE); - let (sync_tx, sync_rx) = mpsc::channel(SYNC_NOTIFICATIONS_BUFFER_SIZE); - let notifications_sink = NotificationsSink { - inner: Arc::new(NotificationsSinkInner { - peer_id: self.peer_id, - async_channel: FuturesMutex::new(async_tx), - sync_channel: Mutex::new(Some(sync_tx)), - }), - }; - - self.protocols[protocol_index].state = State::Open { - notifications_sink_rx: stream::select(async_rx.fuse(), sync_rx.fuse()) - .peekable(), - out_substream: Some(new_open.substream), - in_substream: in_substream.take(), - }; + ConnectionEvent::FullyNegotiatedOutbound(outbound) => { + let (new_open, protocol_index) = (outbound.protocol, outbound.info); - self.events_queue.push_back(ConnectionHandlerEvent::Custom( - NotifsHandlerOut::OpenResultOk { - protocol_index, - negotiated_fallback: new_open.negotiated_fallback, - endpoint: self.endpoint.clone(), - received_handshake: new_open.handshake, - notifications_sink, + match self.protocols[protocol_index].state { + State::Closed { ref mut pending_opening } | + State::OpenDesiredByRemote { ref mut pending_opening, .. } => { + debug_assert!(*pending_opening); + *pending_opening = false; }, - )); + State::Open { .. } => { + error!(target: "sub-libp2p", "☎️ State mismatch in notifications handler"); + debug_assert!(false); + }, + State::Opening { ref mut in_substream } => { + let (async_tx, async_rx) = mpsc::channel(ASYNC_NOTIFICATIONS_BUFFER_SIZE); + let (sync_tx, sync_rx) = mpsc::channel(SYNC_NOTIFICATIONS_BUFFER_SIZE); + let notifications_sink = NotificationsSink { + inner: Arc::new(NotificationsSinkInner { + peer_id: self.peer_id, + async_channel: FuturesMutex::new(async_tx), + sync_channel: Mutex::new(Some(sync_tx)), + }), + }; + + self.protocols[protocol_index].state = State::Open { + notifications_sink_rx: stream::select(async_rx.fuse(), sync_rx.fuse()) + .peekable(), + out_substream: Some(new_open.substream), + in_substream: in_substream.take(), + }; + + self.events_queue.push_back(ConnectionHandlerEvent::Custom( + NotifsHandlerOut::OpenResultOk { + protocol_index, + negotiated_fallback: new_open.negotiated_fallback, + endpoint: self.endpoint.clone(), + received_handshake: new_open.handshake, + notifications_sink, + }, + )); + }, + } }, + ConnectionEvent::AddressChange(_address_change) => {}, + ConnectionEvent::DialUpgradeError(dial_upgrade_error) => match self.protocols + [dial_upgrade_error.info] + .state + { + State::Closed { ref mut pending_opening } | + State::OpenDesiredByRemote { ref mut pending_opening, .. } => { + debug_assert!(*pending_opening); + *pending_opening = false; + }, + + State::Opening { .. } => { + self.protocols[dial_upgrade_error.info].state = + State::Closed { pending_opening: false }; + + self.events_queue.push_back(ConnectionHandlerEvent::Custom( + NotifsHandlerOut::OpenResultErr { protocol_index: dial_upgrade_error.info }, + )); + }, + + // No substream is being open when already `Open`. + State::Open { .. } => debug_assert!(false), + }, + ConnectionEvent::ListenUpgradeError(_listen_upgrade_error) => {}, } } - fn inject_event(&mut self, message: NotifsHandlerIn) { + fn on_behaviour_event(&mut self, message: NotifsHandlerIn) { match message { NotifsHandlerIn::Open { protocol_index } => { let protocol_info = &mut self.protocols[protocol_index]; @@ -676,31 +703,6 @@ impl ConnectionHandler for NotifsHandler { } } - fn inject_dial_upgrade_error( - &mut self, - num: usize, - _: ConnectionHandlerUpgrErr, - ) { - match self.protocols[num].state { - State::Closed { ref mut pending_opening } | - State::OpenDesiredByRemote { ref mut pending_opening, .. } => { - debug_assert!(*pending_opening); - *pending_opening = false; - }, - - State::Opening { .. } => { - self.protocols[num].state = State::Closed { pending_opening: false }; - - self.events_queue.push_back(ConnectionHandlerEvent::Custom( - NotifsHandlerOut::OpenResultErr { protocol_index: num }, - )); - }, - - // No substream is being open when already `Open`. - State::Open { .. } => debug_assert!(false), - } - } - fn connection_keep_alive(&self) -> KeepAlive { // `Yes` if any protocol has some activity. if self.protocols.iter().any(|p| !matches!(p.state, State::Closed { .. })) { @@ -850,3 +852,792 @@ impl ConnectionHandler for NotifsHandler { Poll::Pending } } + +#[cfg(test)] +pub mod tests { + use super::*; + use crate::protocol::notifications::upgrade::{ + NotificationsInOpen, NotificationsInSubstreamHandshake, NotificationsOutOpen, + }; + use asynchronous_codec::Framed; + use libp2p::{ + core::muxing::SubstreamBox, + swarm::{handler, ConnectionHandlerUpgrErr}, + Multiaddr, + }; + use multistream_select::{dialer_select_proto, listener_select_proto, Negotiated, Version}; + use std::{ + collections::HashMap, + io::{Error, IoSlice, IoSliceMut}, + }; + use tokio::sync::mpsc; + use unsigned_varint::codec::UviBytes; + + struct OpenSubstream { + notifications: stream::Peekable< + stream::Select< + stream::Fuse>, + stream::Fuse>, + >, + >, + _in_substream: MockSubstream, + _out_substream: MockSubstream, + } + + pub struct ConnectionYielder { + connections: HashMap<(PeerId, usize), OpenSubstream>, + } + + impl ConnectionYielder { + /// Create new [`ConnectionYielder`]. + pub fn new() -> Self { + Self { connections: HashMap::new() } + } + + /// Open a new substream for peer. + pub fn open_substream( + &mut self, + peer: PeerId, + protocol_index: usize, + endpoint: ConnectedPoint, + received_handshake: Vec, + ) -> NotifsHandlerOut { + let (async_tx, async_rx) = + futures::channel::mpsc::channel(ASYNC_NOTIFICATIONS_BUFFER_SIZE); + let (sync_tx, sync_rx) = + futures::channel::mpsc::channel(SYNC_NOTIFICATIONS_BUFFER_SIZE); + let notifications_sink = NotificationsSink { + inner: Arc::new(NotificationsSinkInner { + peer_id: peer, + async_channel: FuturesMutex::new(async_tx), + sync_channel: Mutex::new(Some(sync_tx)), + }), + }; + let (in_substream, out_substream) = MockSubstream::new(); + + self.connections.insert( + (peer, protocol_index), + OpenSubstream { + notifications: stream::select(async_rx.fuse(), sync_rx.fuse()).peekable(), + _in_substream: in_substream, + _out_substream: out_substream, + }, + ); + + NotifsHandlerOut::OpenResultOk { + protocol_index, + negotiated_fallback: None, + endpoint, + received_handshake, + notifications_sink, + } + } + + /// Attempt to get next pending event from one of the notification sinks. + pub async fn get_next_event(&mut self, peer: PeerId, set: usize) -> Option> { + let substream = if let Some(info) = self.connections.get_mut(&(peer, set)) { + info + } else { + return None + }; + + futures::future::poll_fn(|cx| match substream.notifications.poll_next_unpin(cx) { + Poll::Ready(Some(NotificationsSinkMessage::Notification { message })) => + Poll::Ready(Some(message)), + Poll::Pending => Poll::Ready(None), + Poll::Ready(Some(NotificationsSinkMessage::ForceClose)) | Poll::Ready(None) => + panic!("sink closed"), + }) + .await + } + } + + struct MockSubstream { + pub rx: mpsc::Receiver>, + pub tx: mpsc::Sender>, + rx_buffer: BytesMut, + } + + impl MockSubstream { + /// Create new substream pair. + pub fn new() -> (Self, Self) { + let (tx1, rx1) = mpsc::channel(32); + let (tx2, rx2) = mpsc::channel(32); + + ( + Self { rx: rx1, tx: tx2, rx_buffer: BytesMut::with_capacity(512) }, + Self { rx: rx2, tx: tx1, rx_buffer: BytesMut::with_capacity(512) }, + ) + } + + /// Create new negotiated substream pair. + pub async fn negotiated() -> (Negotiated, Negotiated) { + let (socket1, socket2) = Self::new(); + let socket1 = SubstreamBox::new(socket1); + let socket2 = SubstreamBox::new(socket2); + + let protos = vec![b"/echo/1.0.0", b"/echo/2.5.0"]; + let (res1, res2) = tokio::join!( + dialer_select_proto(socket1, protos.clone(), Version::V1), + listener_select_proto(socket2, protos), + ); + + (res1.unwrap().1, res2.unwrap().1) + } + } + + impl AsyncWrite for MockSubstream { + fn poll_write<'a>( + self: Pin<&mut Self>, + _cx: &mut Context<'a>, + buf: &[u8], + ) -> Poll> { + match self.tx.try_send(buf.to_vec()) { + Ok(_) => Poll::Ready(Ok(buf.len())), + Err(_) => Poll::Ready(Err(std::io::ErrorKind::UnexpectedEof.into())), + } + } + + fn poll_flush<'a>(self: Pin<&mut Self>, _cx: &mut Context<'a>) -> Poll> { + Poll::Ready(Ok(())) + } + + fn poll_close<'a>(self: Pin<&mut Self>, _cx: &mut Context<'a>) -> Poll> { + Poll::Ready(Ok(())) + } + + fn poll_write_vectored<'a, 'b>( + self: Pin<&mut Self>, + _cx: &mut Context<'a>, + _bufs: &[IoSlice<'b>], + ) -> Poll> { + unimplemented!(); + } + } + + impl AsyncRead for MockSubstream { + fn poll_read<'a>( + mut self: Pin<&mut Self>, + cx: &mut Context<'a>, + buf: &mut [u8], + ) -> Poll> { + match self.rx.poll_recv(cx) { + Poll::Ready(Some(data)) => self.rx_buffer.extend_from_slice(&data), + Poll::Ready(None) => + return Poll::Ready(Err(std::io::ErrorKind::UnexpectedEof.into())), + _ => {}, + } + + let nsize = std::cmp::min(self.rx_buffer.len(), buf.len()); + let data = self.rx_buffer.split_to(nsize); + buf[..nsize].copy_from_slice(&data[..]); + + if nsize > 0 { + return Poll::Ready(Ok(nsize)) + } + + Poll::Pending + } + + fn poll_read_vectored<'a, 'b>( + self: Pin<&mut Self>, + _cx: &mut Context<'a>, + _bufs: &mut [IoSliceMut<'b>], + ) -> Poll> { + unimplemented!(); + } + } + + /// Create new [`NotifsHandler`]. + fn notifs_handler() -> NotifsHandler { + let proto = Protocol { + config: ProtocolConfig { + name: "/foo".into(), + fallback_names: vec![], + handshake: Arc::new(RwLock::new(b"hello, world".to_vec())), + max_notification_size: u64::MAX, + }, + in_upgrade: NotificationsIn::new("/foo", Vec::new(), u64::MAX), + state: State::Closed { pending_opening: false }, + }; + + NotifsHandler { + protocols: vec![proto], + when_connection_open: Instant::now(), + endpoint: ConnectedPoint::Listener { + local_addr: Multiaddr::empty(), + send_back_addr: Multiaddr::empty(), + }, + peer_id: PeerId::random(), + events_queue: VecDeque::new(), + } + } + + // verify that if another substream is attempted to be opened by remote while an inbound + // substream already exists, the new inbound stream is rejected and closed by the local node. + #[tokio::test] + async fn second_open_desired_by_remote_rejected() { + let mut handler = notifs_handler(); + let (io, mut io2) = MockSubstream::negotiated().await; + let mut codec = UviBytes::default(); + codec.set_max_len(usize::MAX); + + let notif_in = NotificationsInOpen { + handshake: b"hello, world".to_vec(), + negotiated_fallback: None, + substream: NotificationsInSubstream::new( + Framed::new(io, codec), + NotificationsInSubstreamHandshake::NotSent, + ), + }; + + handler.on_connection_event(handler::ConnectionEvent::FullyNegotiatedInbound( + handler::FullyNegotiatedInbound { protocol: (notif_in, 0), info: () }, + )); + + // verify that the substream is in (partly) opened state + assert!(std::matches!(handler.protocols[0].state, State::OpenDesiredByRemote { .. })); + futures::future::poll_fn(|cx| { + let mut buf = Vec::with_capacity(512); + assert!(std::matches!(Pin::new(&mut io2).poll_read(cx, &mut buf), Poll::Pending)); + Poll::Ready(()) + }) + .await; + + // attempt to open another inbound substream and verify that it is rejected + let (io, mut io2) = MockSubstream::negotiated().await; + let mut codec = UviBytes::default(); + codec.set_max_len(usize::MAX); + + let notif_in = NotificationsInOpen { + handshake: b"hello, world".to_vec(), + negotiated_fallback: None, + substream: NotificationsInSubstream::new( + Framed::new(io, codec), + NotificationsInSubstreamHandshake::NotSent, + ), + }; + + handler.on_connection_event(handler::ConnectionEvent::FullyNegotiatedInbound( + handler::FullyNegotiatedInbound { protocol: (notif_in, 0), info: () }, + )); + + // verify that the new substream is rejected and closed + futures::future::poll_fn(|cx| { + let mut buf = Vec::with_capacity(512); + + if let Poll::Ready(Err(err)) = Pin::new(&mut io2).poll_read(cx, &mut buf) { + assert_eq!(err.kind(), std::io::ErrorKind::UnexpectedEof,); + } + + Poll::Ready(()) + }) + .await; + } + + #[tokio::test] + async fn open_rejected_if_substream_is_opening() { + let mut handler = notifs_handler(); + let (io, mut io2) = MockSubstream::negotiated().await; + let mut codec = UviBytes::default(); + codec.set_max_len(usize::MAX); + + let notif_in = NotificationsInOpen { + handshake: b"hello, world".to_vec(), + negotiated_fallback: None, + substream: NotificationsInSubstream::new( + Framed::new(io, codec), + NotificationsInSubstreamHandshake::NotSent, + ), + }; + + handler.on_connection_event(handler::ConnectionEvent::FullyNegotiatedInbound( + handler::FullyNegotiatedInbound { protocol: (notif_in, 0), info: () }, + )); + + // verify that the substream is in (partly) opened state + assert!(std::matches!(handler.protocols[0].state, State::OpenDesiredByRemote { .. })); + futures::future::poll_fn(|cx| { + let mut buf = Vec::with_capacity(512); + assert!(std::matches!(Pin::new(&mut io2).poll_read(cx, &mut buf), Poll::Pending)); + Poll::Ready(()) + }) + .await; + + // move the handler state to 'Opening' + handler.on_behaviour_event(NotifsHandlerIn::Open { protocol_index: 0 }); + assert!(std::matches!( + handler.protocols[0].state, + State::Opening { in_substream: Some(_) } + )); + + // remote now tries to open another substream, verify that it is rejected and closed + let (io, mut io2) = MockSubstream::negotiated().await; + let mut codec = UviBytes::default(); + codec.set_max_len(usize::MAX); + + let notif_in = NotificationsInOpen { + handshake: b"hello, world".to_vec(), + negotiated_fallback: None, + substream: NotificationsInSubstream::new( + Framed::new(io, codec), + NotificationsInSubstreamHandshake::NotSent, + ), + }; + + handler.on_connection_event(handler::ConnectionEvent::FullyNegotiatedInbound( + handler::FullyNegotiatedInbound { protocol: (notif_in, 0), info: () }, + )); + + // verify that the new substream is rejected and closed but that the first substream is + // still in correct state + futures::future::poll_fn(|cx| { + let mut buf = Vec::with_capacity(512); + + if let Poll::Ready(Err(err)) = Pin::new(&mut io2).poll_read(cx, &mut buf) { + assert_eq!(err.kind(), std::io::ErrorKind::UnexpectedEof,); + } else { + panic!("unexpected result"); + } + + Poll::Ready(()) + }) + .await; + assert!(std::matches!( + handler.protocols[0].state, + State::Opening { in_substream: Some(_) } + )); + } + + #[tokio::test] + async fn open_rejected_if_substream_already_open() { + let mut handler = notifs_handler(); + let (io, mut io2) = MockSubstream::negotiated().await; + let mut codec = UviBytes::default(); + codec.set_max_len(usize::MAX); + + let notif_in = NotificationsInOpen { + handshake: b"hello, world".to_vec(), + negotiated_fallback: None, + substream: NotificationsInSubstream::new( + Framed::new(io, codec), + NotificationsInSubstreamHandshake::NotSent, + ), + }; + handler.on_connection_event(handler::ConnectionEvent::FullyNegotiatedInbound( + handler::FullyNegotiatedInbound { protocol: (notif_in, 0), info: () }, + )); + + // verify that the substream is in (partly) opened state + assert!(std::matches!(handler.protocols[0].state, State::OpenDesiredByRemote { .. })); + futures::future::poll_fn(|cx| { + let mut buf = Vec::with_capacity(512); + assert!(std::matches!(Pin::new(&mut io2).poll_read(cx, &mut buf), Poll::Pending)); + Poll::Ready(()) + }) + .await; + + // move the handler state to 'Opening' + handler.on_behaviour_event(NotifsHandlerIn::Open { protocol_index: 0 }); + assert!(std::matches!( + handler.protocols[0].state, + State::Opening { in_substream: Some(_) } + )); + + // accept the substream and move its state to `Open` + let (io, _io2) = MockSubstream::negotiated().await; + let mut codec = UviBytes::default(); + codec.set_max_len(usize::MAX); + + let notif_out = NotificationsOutOpen { + handshake: b"hello, world".to_vec(), + negotiated_fallback: None, + substream: NotificationsOutSubstream::new(Framed::new(io, codec)), + }; + handler.on_connection_event(handler::ConnectionEvent::FullyNegotiatedOutbound( + handler::FullyNegotiatedOutbound { protocol: notif_out, info: 0 }, + )); + + assert!(std::matches!( + handler.protocols[0].state, + State::Open { in_substream: Some(_), .. } + )); + + // remote now tries to open another substream, verify that it is rejected and closed + let (io, mut io2) = MockSubstream::negotiated().await; + let mut codec = UviBytes::default(); + codec.set_max_len(usize::MAX); + let notif_in = NotificationsInOpen { + handshake: b"hello, world".to_vec(), + negotiated_fallback: None, + substream: NotificationsInSubstream::new( + Framed::new(io, codec), + NotificationsInSubstreamHandshake::NotSent, + ), + }; + handler.on_connection_event(handler::ConnectionEvent::FullyNegotiatedInbound( + handler::FullyNegotiatedInbound { protocol: (notif_in, 0), info: () }, + )); + + // verify that the new substream is rejected and closed but that the first substream is + // still in correct state + futures::future::poll_fn(|cx| { + let mut buf = Vec::with_capacity(512); + + if let Poll::Ready(Err(err)) = Pin::new(&mut io2).poll_read(cx, &mut buf) { + assert_eq!(err.kind(), std::io::ErrorKind::UnexpectedEof); + } else { + panic!("unexpected result"); + } + + Poll::Ready(()) + }) + .await; + assert!(std::matches!( + handler.protocols[0].state, + State::Open { in_substream: Some(_), .. } + )); + } + + #[tokio::test] + async fn fully_negotiated_resets_state_for_closed_substream() { + let mut handler = notifs_handler(); + let (io, mut io2) = MockSubstream::negotiated().await; + let mut codec = UviBytes::default(); + codec.set_max_len(usize::MAX); + + let notif_in = NotificationsInOpen { + handshake: b"hello, world".to_vec(), + negotiated_fallback: None, + substream: NotificationsInSubstream::new( + Framed::new(io, codec), + NotificationsInSubstreamHandshake::NotSent, + ), + }; + handler.on_connection_event(handler::ConnectionEvent::FullyNegotiatedInbound( + handler::FullyNegotiatedInbound { protocol: (notif_in, 0), info: () }, + )); + + // verify that the substream is in (partly) opened state + assert!(std::matches!(handler.protocols[0].state, State::OpenDesiredByRemote { .. })); + futures::future::poll_fn(|cx| { + let mut buf = Vec::with_capacity(512); + assert!(std::matches!(Pin::new(&mut io2).poll_read(cx, &mut buf), Poll::Pending)); + Poll::Ready(()) + }) + .await; + + // first instruct the handler to open a connection and then close it right after + // so the handler is in state `Closed { pending_opening: true }` + handler.on_behaviour_event(NotifsHandlerIn::Open { protocol_index: 0 }); + assert!(std::matches!( + handler.protocols[0].state, + State::Opening { in_substream: Some(_) } + )); + + handler.on_behaviour_event(NotifsHandlerIn::Close { protocol_index: 0 }); + assert!(std::matches!(handler.protocols[0].state, State::Closed { pending_opening: true })); + + // verify that if the the outbound substream is successfully negotiated, the state is not + // changed as the substream was commanded to be closed by the handler. + let (io, _io2) = MockSubstream::negotiated().await; + let mut codec = UviBytes::default(); + codec.set_max_len(usize::MAX); + + let notif_out = NotificationsOutOpen { + handshake: b"hello, world".to_vec(), + negotiated_fallback: None, + substream: NotificationsOutSubstream::new(Framed::new(io, codec)), + }; + handler.on_connection_event(handler::ConnectionEvent::FullyNegotiatedOutbound( + handler::FullyNegotiatedOutbound { protocol: notif_out, info: 0 }, + )); + + assert!(std::matches!( + handler.protocols[0].state, + State::Closed { pending_opening: false } + )); + } + + #[tokio::test] + async fn fully_negotiated_resets_state_for_open_desired_substream() { + let mut handler = notifs_handler(); + let (io, mut io2) = MockSubstream::negotiated().await; + let mut codec = UviBytes::default(); + codec.set_max_len(usize::MAX); + + let notif_in = NotificationsInOpen { + handshake: b"hello, world".to_vec(), + negotiated_fallback: None, + substream: NotificationsInSubstream::new( + Framed::new(io, codec), + NotificationsInSubstreamHandshake::NotSent, + ), + }; + handler.on_connection_event(handler::ConnectionEvent::FullyNegotiatedInbound( + handler::FullyNegotiatedInbound { protocol: (notif_in, 0), info: () }, + )); + + // verify that the substream is in (partly) opened state + assert!(std::matches!(handler.protocols[0].state, State::OpenDesiredByRemote { .. })); + futures::future::poll_fn(|cx| { + let mut buf = Vec::with_capacity(512); + assert!(std::matches!(Pin::new(&mut io2).poll_read(cx, &mut buf), Poll::Pending)); + Poll::Ready(()) + }) + .await; + + // first instruct the handler to open a connection and then close it right after + // so the handler is in state `Closed { pending_opening: true }` + handler.on_behaviour_event(NotifsHandlerIn::Open { protocol_index: 0 }); + assert!(std::matches!( + handler.protocols[0].state, + State::Opening { in_substream: Some(_) } + )); + + handler.on_behaviour_event(NotifsHandlerIn::Close { protocol_index: 0 }); + assert!(std::matches!(handler.protocols[0].state, State::Closed { pending_opening: true })); + + // attempt to open another inbound substream and verify that it is rejected + let (io, _io2) = MockSubstream::negotiated().await; + let mut codec = UviBytes::default(); + codec.set_max_len(usize::MAX); + + let notif_in = NotificationsInOpen { + handshake: b"hello, world".to_vec(), + negotiated_fallback: None, + substream: NotificationsInSubstream::new( + Framed::new(io, codec), + NotificationsInSubstreamHandshake::NotSent, + ), + }; + handler.on_connection_event(handler::ConnectionEvent::FullyNegotiatedInbound( + handler::FullyNegotiatedInbound { protocol: (notif_in, 0), info: () }, + )); + + assert!(std::matches!( + handler.protocols[0].state, + State::OpenDesiredByRemote { pending_opening: true, .. } + )); + + // verify that if the the outbound substream is successfully negotiated, the state is not + // changed as the substream was commanded to be closed by the handler. + let (io, _io2) = MockSubstream::negotiated().await; + let mut codec = UviBytes::default(); + codec.set_max_len(usize::MAX); + + let notif_out = NotificationsOutOpen { + handshake: b"hello, world".to_vec(), + negotiated_fallback: None, + substream: NotificationsOutSubstream::new(Framed::new(io, codec)), + }; + + handler.on_connection_event(handler::ConnectionEvent::FullyNegotiatedOutbound( + handler::FullyNegotiatedOutbound { protocol: notif_out, info: 0 }, + )); + + assert!(std::matches!( + handler.protocols[0].state, + State::OpenDesiredByRemote { pending_opening: false, .. } + )); + } + + #[tokio::test] + async fn dial_upgrade_error_resets_closed_outbound_state() { + let mut handler = notifs_handler(); + let (io, mut io2) = MockSubstream::negotiated().await; + let mut codec = UviBytes::default(); + codec.set_max_len(usize::MAX); + + let notif_in = NotificationsInOpen { + handshake: b"hello, world".to_vec(), + negotiated_fallback: None, + substream: NotificationsInSubstream::new( + Framed::new(io, codec), + NotificationsInSubstreamHandshake::NotSent, + ), + }; + handler.on_connection_event(handler::ConnectionEvent::FullyNegotiatedInbound( + handler::FullyNegotiatedInbound { protocol: (notif_in, 0), info: () }, + )); + + // verify that the substream is in (partly) opened state + assert!(std::matches!(handler.protocols[0].state, State::OpenDesiredByRemote { .. })); + futures::future::poll_fn(|cx| { + let mut buf = Vec::with_capacity(512); + assert!(std::matches!(Pin::new(&mut io2).poll_read(cx, &mut buf), Poll::Pending)); + Poll::Ready(()) + }) + .await; + + // first instruct the handler to open a connection and then close it right after + // so the handler is in state `Closed { pending_opening: true }` + handler.on_behaviour_event(NotifsHandlerIn::Open { protocol_index: 0 }); + assert!(std::matches!( + handler.protocols[0].state, + State::Opening { in_substream: Some(_) } + )); + + handler.on_behaviour_event(NotifsHandlerIn::Close { protocol_index: 0 }); + assert!(std::matches!(handler.protocols[0].state, State::Closed { pending_opening: true })); + + // inject dial failure to an already closed substream and verify outbound state is reset + handler.on_connection_event(handler::ConnectionEvent::DialUpgradeError( + handler::DialUpgradeError { info: 0, error: ConnectionHandlerUpgrErr::Timeout }, + )); + assert!(std::matches!( + handler.protocols[0].state, + State::Closed { pending_opening: false } + )); + } + + #[tokio::test] + async fn dial_upgrade_error_resets_open_desired_state() { + let mut handler = notifs_handler(); + let (io, mut io2) = MockSubstream::negotiated().await; + let mut codec = UviBytes::default(); + codec.set_max_len(usize::MAX); + + let notif_in = NotificationsInOpen { + handshake: b"hello, world".to_vec(), + negotiated_fallback: None, + substream: NotificationsInSubstream::new( + Framed::new(io, codec), + NotificationsInSubstreamHandshake::NotSent, + ), + }; + handler.on_connection_event(handler::ConnectionEvent::FullyNegotiatedInbound( + handler::FullyNegotiatedInbound { protocol: (notif_in, 0), info: () }, + )); + + // verify that the substream is in (partly) opened state + assert!(std::matches!(handler.protocols[0].state, State::OpenDesiredByRemote { .. })); + futures::future::poll_fn(|cx| { + let mut buf = Vec::with_capacity(512); + assert!(std::matches!(Pin::new(&mut io2).poll_read(cx, &mut buf), Poll::Pending)); + Poll::Ready(()) + }) + .await; + + // first instruct the handler to open a connection and then close it right after + // so the handler is in state `Closed { pending_opening: true }` + handler.on_behaviour_event(NotifsHandlerIn::Open { protocol_index: 0 }); + assert!(std::matches!( + handler.protocols[0].state, + State::Opening { in_substream: Some(_) } + )); + + handler.on_behaviour_event(NotifsHandlerIn::Close { protocol_index: 0 }); + assert!(std::matches!(handler.protocols[0].state, State::Closed { pending_opening: true })); + + let (io, _io2) = MockSubstream::negotiated().await; + let mut codec = UviBytes::default(); + codec.set_max_len(usize::MAX); + + let notif_in = NotificationsInOpen { + handshake: b"hello, world".to_vec(), + negotiated_fallback: None, + substream: NotificationsInSubstream::new( + Framed::new(io, codec), + NotificationsInSubstreamHandshake::NotSent, + ), + }; + handler.on_connection_event(handler::ConnectionEvent::FullyNegotiatedInbound( + handler::FullyNegotiatedInbound { protocol: (notif_in, 0), info: () }, + )); + + assert!(std::matches!( + handler.protocols[0].state, + State::OpenDesiredByRemote { pending_opening: true, .. } + )); + + // inject dial failure to an already closed substream and verify outbound state is reset + handler.on_connection_event(handler::ConnectionEvent::DialUpgradeError( + handler::DialUpgradeError { info: 0, error: ConnectionHandlerUpgrErr::Timeout }, + )); + assert!(std::matches!( + handler.protocols[0].state, + State::OpenDesiredByRemote { pending_opening: false, .. } + )); + } + + #[tokio::test] + async fn sync_notifications_clogged() { + let mut handler = notifs_handler(); + let (io, _) = MockSubstream::negotiated().await; + let codec = UviBytes::default(); + + let (async_tx, async_rx) = futures::channel::mpsc::channel(ASYNC_NOTIFICATIONS_BUFFER_SIZE); + let (sync_tx, sync_rx) = futures::channel::mpsc::channel(1); + let notifications_sink = NotificationsSink { + inner: Arc::new(NotificationsSinkInner { + peer_id: PeerId::random(), + async_channel: FuturesMutex::new(async_tx), + sync_channel: Mutex::new(Some(sync_tx)), + }), + }; + + handler.protocols[0].state = State::Open { + notifications_sink_rx: stream::select(async_rx.fuse(), sync_rx.fuse()).peekable(), + out_substream: Some(NotificationsOutSubstream::new(Framed::new(io, codec))), + in_substream: None, + }; + + notifications_sink.send_sync_notification(vec![1, 3, 3, 7]); + notifications_sink.send_sync_notification(vec![1, 3, 3, 8]); + notifications_sink.send_sync_notification(vec![1, 3, 3, 9]); + notifications_sink.send_sync_notification(vec![1, 3, 4, 0]); + + futures::future::poll_fn(|cx| { + assert!(std::matches!( + handler.poll(cx), + Poll::Ready(ConnectionHandlerEvent::Close( + NotifsHandlerError::SyncNotificationsClogged, + )) + )); + Poll::Ready(()) + }) + .await; + } + + #[tokio::test] + async fn close_desired_by_remote() { + let mut handler = notifs_handler(); + let (io, io2) = MockSubstream::negotiated().await; + let mut codec = UviBytes::default(); + codec.set_max_len(usize::MAX); + + let notif_in = NotificationsInOpen { + handshake: b"hello, world".to_vec(), + negotiated_fallback: None, + substream: NotificationsInSubstream::new( + Framed::new(io, codec), + NotificationsInSubstreamHandshake::PendingSend(vec![1, 2, 3, 4]), + ), + }; + + // add new inbound substream but close it immediately and verify that correct events are + // emitted + handler.on_connection_event(handler::ConnectionEvent::FullyNegotiatedInbound( + handler::FullyNegotiatedInbound { protocol: (notif_in, 0), info: () }, + )); + drop(io2); + + futures::future::poll_fn(|cx| { + assert!(std::matches!( + handler.poll(cx), + Poll::Ready(ConnectionHandlerEvent::Custom( + NotifsHandlerOut::OpenDesiredByRemote { protocol_index: 0 }, + )) + )); + assert!(std::matches!( + handler.poll(cx), + Poll::Ready(ConnectionHandlerEvent::Custom(NotifsHandlerOut::CloseDesired { + protocol_index: 0 + },)) + )); + Poll::Ready(()) + }) + .await; + } +} diff --git a/client/network/src/protocol/notifications/upgrade.rs b/client/network/src/protocol/notifications/upgrade.rs index c273361ac..89fede25f 100644 --- a/client/network/src/protocol/notifications/upgrade.rs +++ b/client/network/src/protocol/notifications/upgrade.rs @@ -20,8 +20,8 @@ pub use self::{ collec::UpgradeCollec, notifications::{ NotificationsHandshakeError, NotificationsIn, NotificationsInOpen, - NotificationsInSubstream, NotificationsOut, NotificationsOutError, NotificationsOutOpen, - NotificationsOutSubstream, + NotificationsInSubstream, NotificationsInSubstreamHandshake, NotificationsOut, + NotificationsOutError, NotificationsOutOpen, NotificationsOutSubstream, }, }; diff --git a/client/network/src/protocol/notifications/upgrade/collec.rs b/client/network/src/protocol/notifications/upgrade/collec.rs index db9850c8d..288b8bfd4 100644 --- a/client/network/src/protocol/notifications/upgrade/collec.rs +++ b/client/network/src/protocol/notifications/upgrade/collec.rs @@ -73,7 +73,7 @@ where } /// Groups a `ProtocolName` with a `usize`. -#[derive(Debug, Clone)] +#[derive(Debug, Clone, PartialEq)] pub struct ProtoNameWithUsize(T, usize); impl ProtocolName for ProtoNameWithUsize { @@ -99,3 +99,81 @@ impl>, O, E> Future for FutWithUsize { } } } + +#[cfg(test)] +mod tests { + use super::*; + use libp2p::core::upgrade::{ProtocolName, UpgradeInfo}; + use sc_network_common::protocol::ProtocolName as ProtoName; + + // TODO: move to mocks + mockall::mock! { + pub ProtocolUpgrade {} + + impl UpgradeInfo for ProtocolUpgrade { + type Info = T; + type InfoIter = vec::IntoIter; + fn protocol_info(&self) -> vec::IntoIter; + } + } + + #[test] + fn protocol_info() { + let upgrades = (1..=3) + .map(|i| { + let mut upgrade = MockProtocolUpgrade::>::new(); + upgrade.expect_protocol_info().return_once(move || { + vec![ProtoNameWithUsize(ProtoName::from(format!("protocol{i}")), i)].into_iter() + }); + upgrade + }) + .collect::>(); + + let upgrade: UpgradeCollec<_> = upgrades.into_iter().collect::>(); + let protos = vec![ + ProtoNameWithUsize(ProtoNameWithUsize(ProtoName::from("protocol1".to_string()), 1), 0), + ProtoNameWithUsize(ProtoNameWithUsize(ProtoName::from("protocol2".to_string()), 2), 1), + ProtoNameWithUsize(ProtoNameWithUsize(ProtoName::from("protocol3".to_string()), 3), 2), + ]; + let upgrades = upgrade.protocol_info().collect::>(); + + assert_eq!(upgrades, protos,); + } + + #[test] + fn nested_protocol_info() { + let mut upgrades = (1..=2) + .map(|i| { + let mut upgrade = MockProtocolUpgrade::>::new(); + upgrade.expect_protocol_info().return_once(move || { + vec![ProtoNameWithUsize(ProtoName::from(format!("protocol{i}")), i)].into_iter() + }); + upgrade + }) + .collect::>(); + + upgrades.push({ + let mut upgrade = MockProtocolUpgrade::>::new(); + upgrade.expect_protocol_info().return_once(move || { + vec![ + ProtoNameWithUsize(ProtoName::from("protocol22".to_string()), 1), + ProtoNameWithUsize(ProtoName::from("protocol33".to_string()), 2), + ProtoNameWithUsize(ProtoName::from("protocol44".to_string()), 3), + ] + .into_iter() + }); + upgrade + }); + + let upgrade: UpgradeCollec<_> = upgrades.into_iter().collect::>(); + let protos = vec![ + ProtoNameWithUsize(ProtoNameWithUsize(ProtoName::from("protocol1".to_string()), 1), 0), + ProtoNameWithUsize(ProtoNameWithUsize(ProtoName::from("protocol2".to_string()), 2), 1), + ProtoNameWithUsize(ProtoNameWithUsize(ProtoName::from("protocol22".to_string()), 1), 2), + ProtoNameWithUsize(ProtoNameWithUsize(ProtoName::from("protocol33".to_string()), 2), 2), + ProtoNameWithUsize(ProtoNameWithUsize(ProtoName::from("protocol44".to_string()), 3), 2), + ]; + let upgrades = upgrade.protocol_info().collect::>(); + assert_eq!(upgrades, protos,); + } +} diff --git a/client/network/src/protocol/notifications/upgrade/notifications.rs b/client/network/src/protocol/notifications/upgrade/notifications.rs index 5a4ec832d..71afc3c90 100644 --- a/client/network/src/protocol/notifications/upgrade/notifications.rs +++ b/client/network/src/protocol/notifications/upgrade/notifications.rs @@ -88,7 +88,8 @@ pub struct NotificationsInSubstream { } /// State of the handshake sending back process. -enum NotificationsInSubstreamHandshake { +#[derive(Debug)] +pub enum NotificationsInSubstreamHandshake { /// Waiting for the user to give us the handshake message. NotSent, /// User gave us the handshake message. Trying to push it in the socket. @@ -111,6 +112,13 @@ pub struct NotificationsOutSubstream { socket: Framed>>>, } +#[cfg(test)] +impl NotificationsOutSubstream { + pub fn new(socket: Framed>>>) -> Self { + Self { socket } + } +} + impl NotificationsIn { /// Builds a new potential upgrade. pub fn new( @@ -193,6 +201,14 @@ impl NotificationsInSubstream where TSubstream: AsyncRead + AsyncWrite + Unpin, { + #[cfg(test)] + pub fn new( + socket: Framed>>>, + handshake: NotificationsInSubstreamHandshake, + ) -> Self { + Self { socket, handshake } + } + /// Sends the handshake in order to inform the remote that we accept the substream. pub fn send_handshake(&mut self, message: impl Into>) { if !matches!(self.handshake, NotificationsInSubstreamHandshake::NotSent) { diff --git a/client/network/sync/Cargo.toml b/client/network/sync/Cargo.toml index ff72b4e99..532e87383 100644 --- a/client/network/sync/Cargo.toml +++ b/client/network/sync/Cargo.toml @@ -23,7 +23,7 @@ futures = "0.3.21" libp2p = "0.50.0" log = "0.4.17" lru = "0.8.1" -mockall = "0.11.2" +mockall = "0.11.3" prost = "0.11" smallvec = "1.8.0" thiserror = "1.0" From 6a504b063cf66351b6e352ef18cc18d49146487b Mon Sep 17 00:00:00 2001 From: Kian Paimani <5588131+kianenigma@users.noreply.github.com> Date: Wed, 8 Feb 2023 11:37:36 -0300 Subject: [PATCH 099/558] Configurable voting-degree in council elections pallet (#13305) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * configurable council elections pallet * configurable council elections pallet * add warning * reduce sizes * ".git/.scripts/commands/bench/bench.sh" pallet dev pallet_elections_phragmen * fix stuff * make assert * fix docs * fix docs again * fix docs again * Update frame/elections-phragmen/src/lib.rs Co-authored-by: Gonçalo Pestana * Update frame/elections-phragmen/src/lib.rs Co-authored-by: Gonçalo Pestana * Update frame/elections-phragmen/src/lib.rs Co-authored-by: Gonçalo Pestana * fix docs --------- Co-authored-by: command-bot <> Co-authored-by: Gonçalo Pestana --- bin/node/runtime/src/lib.rs | 6 +- frame/elections-phragmen/src/benchmarking.rs | 88 +----- frame/elections-phragmen/src/lib.rs | 96 +++++-- frame/elections-phragmen/src/weights.rs | 265 +++++++++--------- .../procedural/src/construct_runtime/mod.rs | 1 + 5 files changed, 216 insertions(+), 240 deletions(-) diff --git a/bin/node/runtime/src/lib.rs b/bin/node/runtime/src/lib.rs index 3f4c46424..4298a6ece 100644 --- a/bin/node/runtime/src/lib.rs +++ b/bin/node/runtime/src/lib.rs @@ -1003,8 +1003,9 @@ parameter_types! { pub const TermDuration: BlockNumber = 7 * DAYS; pub const DesiredMembers: u32 = 13; pub const DesiredRunnersUp: u32 = 7; - pub const MaxVoters: u32 = 10 * 1000; - pub const MaxCandidates: u32 = 1000; + pub const MaxVotesPerVoter: u32 = 16; + pub const MaxVoters: u32 = 512; + pub const MaxCandidates: u32 = 64; pub const ElectionsPhragmenPalletId: LockIdentifier = *b"phrelect"; } @@ -1029,6 +1030,7 @@ impl pallet_elections_phragmen::Config for Runtime { type DesiredRunnersUp = DesiredRunnersUp; type TermDuration = TermDuration; type MaxVoters = MaxVoters; + type MaxVotesPerVoter = MaxVotesPerVoter; type MaxCandidates = MaxCandidates; type WeightInfo = pallet_elections_phragmen::weights::SubstrateWeight; } diff --git a/frame/elections-phragmen/src/benchmarking.rs b/frame/elections-phragmen/src/benchmarking.rs index 1fc500a8e..b7c04bad3 100644 --- a/frame/elections-phragmen/src/benchmarking.rs +++ b/frame/elections-phragmen/src/benchmarking.rs @@ -148,7 +148,7 @@ fn clean() { benchmarks! { // -- Signed ones vote_equal { - let v in 1 .. (MAXIMUM_VOTE as u32); + let v in 1 .. T::MaxVotesPerVoter::get(); clean::(); // create a bunch of candidates. @@ -168,7 +168,7 @@ benchmarks! { }: vote(RawOrigin::Signed(caller), votes, stake) vote_more { - let v in 2 .. (MAXIMUM_VOTE as u32); + let v in 2 .. T::MaxVotesPerVoter::get(); clean::(); // create a bunch of candidates. @@ -190,7 +190,7 @@ benchmarks! { }: vote(RawOrigin::Signed(caller), votes, stake / >::from(10u32)) vote_less { - let v in 2 .. (MAXIMUM_VOTE as u32); + let v in 2 .. T::MaxVotesPerVoter::get(); clean::(); // create a bunch of candidates. @@ -212,7 +212,7 @@ benchmarks! { remove_voter { // we fix the number of voted candidates to max - let v = MAXIMUM_VOTE as u32; + let v = T::MaxVotesPerVoter::get(); clean::(); // create a bunch of candidates. @@ -368,7 +368,7 @@ benchmarks! { clean::(); let all_candidates = submit_candidates::(T::MaxCandidates::get(), "candidates")?; - distribute_voters::(all_candidates, v, MAXIMUM_VOTE)?; + distribute_voters::(all_candidates, v, T::MaxVotesPerVoter::get() as usize)?; // all candidates leave. >::kill(); @@ -389,17 +389,17 @@ benchmarks! { // that we give all candidates a self vote to make sure they are all considered. let c in 1 .. T::MaxCandidates::get(); let v in 1 .. T::MaxVoters::get(); - let e in (T::MaxVoters::get()) .. T::MaxVoters::get() as u32 * MAXIMUM_VOTE as u32; + let e in (T::MaxVoters::get()) .. T::MaxVoters::get() * T::MaxVotesPerVoter::get(); clean::(); // so we have a situation with v and e. we want e to basically always be in the range of `e - // -> e * MAXIMUM_VOTE`, but we cannot express that now with the benchmarks. So what we do - // is: when c is being iterated, v, and e are max and fine. when v is being iterated, e is - // being set to max and this is a problem. In these cases, we cap e to a lower value, namely - // v * MAXIMUM_VOTE. when e is being iterated, v is at max, and again fine. all in all, - // votes_per_voter can never be more than MAXIMUM_VOTE. Note that this might cause `v` to be - // an overestimate. - let votes_per_voter = (e / v).min(MAXIMUM_VOTE as u32); + // -> e * T::MaxVotesPerVoter::get()`, but we cannot express that now with the benchmarks. + // So what we do is: when c is being iterated, v, and e are max and fine. when v is being + // iterated, e is being set to max and this is a problem. In these cases, we cap e to a + // lower value, namely v * T::MaxVotesPerVoter::get(). when e is being iterated, v is at + // max, and again fine. all in all, votes_per_voter can never be more than + // T::MaxVotesPerVoter::get(). Note that this might cause `v` to be an overestimate. + let votes_per_voter = (e / v).min(T::MaxVotesPerVoter::get()); let all_candidates = submit_candidates_with_self_vote::(c, "candidates")?; let _ = distribute_voters::(all_candidates, v.saturating_sub(c), votes_per_voter as usize)?; @@ -421,68 +421,6 @@ benchmarks! { } } - #[extra] - election_phragmen_c_e { - let c in 1 .. T::MaxCandidates::get(); - let e in (T::MaxVoters::get()) .. T::MaxVoters::get() * MAXIMUM_VOTE as u32; - let fixed_v = T::MaxVoters::get(); - clean::(); - - let votes_per_voter = e / fixed_v; - - let all_candidates = submit_candidates_with_self_vote::(c, "candidates")?; - let _ = distribute_voters::( - all_candidates, - fixed_v - c, - votes_per_voter as usize, - )?; - }: { - >::on_initialize(T::TermDuration::get()); - } - verify { - assert_eq!(>::members().len() as u32, T::DesiredMembers::get().min(c)); - assert_eq!( - >::runners_up().len() as u32, - T::DesiredRunnersUp::get().min(c.saturating_sub(T::DesiredMembers::get())), - ); - - #[cfg(test)] - { - // reset members in between benchmark tests. - use crate::tests::MEMBERS; - MEMBERS.with(|m| *m.borrow_mut() = vec![]); - } - } - - #[extra] - election_phragmen_v { - let v in 4 .. 16; - let fixed_c = T::MaxCandidates::get() / 10; - let fixed_e = 64; - clean::(); - - let votes_per_voter = fixed_e / v; - - let all_candidates = submit_candidates_with_self_vote::(fixed_c, "candidates")?; - let _ = distribute_voters::(all_candidates, v, votes_per_voter as usize)?; - }: { - >::on_initialize(T::TermDuration::get()); - } - verify { - assert_eq!(>::members().len() as u32, T::DesiredMembers::get().min(fixed_c)); - assert_eq!( - >::runners_up().len() as u32, - T::DesiredRunnersUp::get().min(fixed_c.saturating_sub(T::DesiredMembers::get())), - ); - - #[cfg(test)] - { - // reset members in between benchmark tests. - use crate::tests::MEMBERS; - MEMBERS.with(|m| *m.borrow_mut() = vec![]); - } - } - impl_benchmark_test_suite!( Elections, crate::tests::ExtBuilder::default().desired_members(13).desired_runners_up(7), diff --git a/frame/elections-phragmen/src/lib.rs b/frame/elections-phragmen/src/lib.rs index 1a020adb2..c655d4a16 100644 --- a/frame/elections-phragmen/src/lib.rs +++ b/frame/elections-phragmen/src/lib.rs @@ -43,14 +43,14 @@ //! ### Voting //! //! Voters can vote for a limited number of the candidates by providing a list of account ids, -//! bounded by [`MAXIMUM_VOTE`]. Invalid votes (voting for non-candidates) and duplicate votes are -//! ignored during election. Yet, a voter _might_ vote for a future candidate. Voters reserve a bond -//! as they vote. Each vote defines a `value`. This amount is locked from the account of the voter -//! and indicates the weight of the vote. Voters can update their votes at any time by calling -//! `vote()` again. This can update the vote targets (which might update the deposit) or update the -//! vote's stake ([`Voter::stake`]). After a round, votes are kept and might still be valid for -//! further rounds. A voter is responsible for calling `remove_voter` once they are done to have -//! their bond back and remove the lock. +//! bounded by [`Config::MaxVotesPerVoter`]. Invalid votes (voting for non-candidates) and duplicate +//! votes are ignored during election. Yet, a voter _might_ vote for a future candidate. Voters +//! reserve a bond as they vote. Each vote defines a `value`. This amount is locked from the account +//! of the voter and indicates the weight of the vote. Voters can update their votes at any time by +//! calling `vote()` again. This can update the vote targets (which might update the deposit) or +//! update the vote's stake ([`Voter::stake`]). After a round, votes are kept and might still be +//! valid for further rounds. A voter is responsible for calling `remove_voter` once they are done +//! to have their bond back and remove the lock. //! //! See [`Call::vote`], [`Call::remove_voter`]. //! @@ -124,9 +124,6 @@ pub mod migrations; const LOG_TARGET: &str = "runtime::elections-phragmen"; -/// The maximum votes allowed per voter. -pub const MAXIMUM_VOTE: usize = 16; - type BalanceOf = <::Currency as Currency<::AccountId>>::Balance; type NegativeImbalanceOf = <::Currency as Currency< @@ -254,19 +251,29 @@ pub mod pallet { /// The maximum number of candidates in a phragmen election. /// - /// Warning: The election happens onchain, and this value will determine - /// the size of the election. When this limit is reached no more - /// candidates are accepted in the election. + /// Warning: This impacts the size of the election which is run onchain. Chose wisely, and + /// consider how it will impact `T::WeightInfo::election_phragmen`. + /// + /// When this limit is reached no more candidates are accepted in the election. #[pallet::constant] type MaxCandidates: Get; /// The maximum number of voters to allow in a phragmen election. /// - /// Warning: This impacts the size of the election which is run onchain. + /// Warning: This impacts the size of the election which is run onchain. Chose wisely, and + /// consider how it will impact `T::WeightInfo::election_phragmen`. + /// /// When the limit is reached the new voters are ignored. #[pallet::constant] type MaxVoters: Get; + /// Maximum numbers of votes per voter. + /// + /// Warning: This impacts the size of the election which is run onchain. Chose wisely, and + /// consider how it will impact `T::WeightInfo::election_phragmen`. + #[pallet::constant] + type MaxVotesPerVoter: Get; + /// Weight information for extrinsics in this pallet. type WeightInfo: WeightInfo; } @@ -284,6 +291,41 @@ pub mod pallet { Weight::zero() } } + + fn integrity_test() { + let block_weight = T::BlockWeights::get().max_block; + // mind the order. + let election_weight = T::WeightInfo::election_phragmen( + T::MaxCandidates::get(), + T::MaxVoters::get(), + T::MaxVotesPerVoter::get() * T::MaxVoters::get(), + ); + + let to_seconds = |w: &Weight| { + w.ref_time() as f32 / + frame_support::weights::constants::WEIGHT_REF_TIME_PER_SECOND as f32 + }; + + frame_support::log::debug!( + target: LOG_TARGET, + "election weight {}s ({:?}) // chain's block weight {}s ({:?})", + to_seconds(&election_weight), + election_weight, + to_seconds(&block_weight), + block_weight, + ); + assert!( + election_weight.all_lt(block_weight), + "election weight {}s ({:?}) will exceed a {}s chain's block weight ({:?}) (MaxCandidates {}, MaxVoters {}, MaxVotesPerVoter {} -- tweak these parameters)", + election_weight, + to_seconds(&election_weight), + to_seconds(&block_weight), + block_weight, + T::MaxCandidates::get(), + T::MaxVoters::get(), + T::MaxVotesPerVoter::get(), + ); + } } #[pallet::call] @@ -307,10 +349,6 @@ pub mod pallet { /// /// It is the responsibility of the caller to **NOT** place all of their balance into the /// lock and keep some for further operations. - /// - /// # - /// We assume the maximum weight among all 3 cases: vote_equal, vote_more and vote_less. - /// # #[pallet::call_index(0)] #[pallet::weight( T::WeightInfo::vote_more(votes.len() as u32) @@ -324,8 +362,10 @@ pub mod pallet { ) -> DispatchResultWithPostInfo { let who = ensure_signed(origin)?; - // votes should not be empty and more than `MAXIMUM_VOTE` in any case. - ensure!(votes.len() <= MAXIMUM_VOTE, Error::::MaximumVotesExceeded); + ensure!( + votes.len() <= T::MaxVotesPerVoter::get() as usize, + Error::::MaximumVotesExceeded + ); ensure!(!votes.is_empty(), Error::::NoVotes); let candidates_count = >::decode_len().unwrap_or(0); @@ -1006,7 +1046,7 @@ impl Pallet { // count](https://en.wikipedia.org/wiki/Borda_count). We weigh everyone's vote for // that new member by a multiplier based on the order of the votes. i.e. the // first person a voter votes for gets a 16x multiplier, the next person gets a - // 15x multiplier, an so on... (assuming `MAXIMUM_VOTE` = 16) + // 15x multiplier, an so on... (assuming `T::MaxVotesPerVoter` = 16) let mut prime_votes = new_members_sorted_by_id .iter() .map(|c| (&c.0, BalanceOf::::zero())) @@ -1014,7 +1054,7 @@ impl Pallet { for (_, stake, votes) in voters_and_stakes.into_iter() { for (vote_multiplier, who) in votes.iter().enumerate().map(|(vote_position, who)| { - ((MAXIMUM_VOTE - vote_position) as u32, who) + ((T::MaxVotesPerVoter::get() as usize - vote_position) as u32, who) }) { if let Ok(i) = prime_votes.binary_search_by_key(&who, |k| k.0) { prime_votes[i].1 = prime_votes[i] @@ -1173,16 +1213,9 @@ mod tests { }; use substrate_test_utils::assert_eq_uvec; - parameter_types! { - pub BlockWeights: frame_system::limits::BlockWeights = - frame_system::limits::BlockWeights::simple_max( - frame_support::weights::Weight::from_ref_time(1024).set_proof_size(u64::MAX), - ); - } - impl frame_system::Config for Test { type BaseCallFilter = frame_support::traits::Everything; - type BlockWeights = BlockWeights; + type BlockWeights = (); type BlockLength = (); type DbWeight = (); type RuntimeOrigin = RuntimeOrigin; @@ -1297,6 +1330,7 @@ mod tests { type KickedMember = (); type WeightInfo = (); type MaxVoters = PhragmenMaxVoters; + type MaxVotesPerVoter = ConstU32<16>; type MaxCandidates = PhragmenMaxCandidates; } diff --git a/frame/elections-phragmen/src/weights.rs b/frame/elections-phragmen/src/weights.rs index dc5d937e3..a13dadf21 100644 --- a/frame/elections-phragmen/src/weights.rs +++ b/frame/elections-phragmen/src/weights.rs @@ -18,25 +18,26 @@ //! Autogenerated weights for pallet_elections_phragmen //! //! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev -//! DATE: 2023-01-24, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! DATE: 2023-02-03, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` //! WORST CASE MAP SIZE: `1000000` -//! HOSTNAME: `bm2`, CPU: `Intel(R) Core(TM) i7-7700K CPU @ 4.20GHz` +//! HOSTNAME: `runner-b3zmxxc-project-145-concurrent-0`, CPU: `Intel(R) Xeon(R) CPU @ 2.60GHz` //! EXECUTION: Some(Wasm), WASM-EXECUTION: Compiled, CHAIN: Some("dev"), DB CACHE: 1024 // Executed Command: -// ./target/production/substrate +// target/production/substrate // benchmark // pallet -// --chain=dev // --steps=50 // --repeat=20 -// --pallet=pallet_elections_phragmen // --extrinsic=* // --execution=wasm // --wasm-execution=compiled // --heap-pages=4096 -// --output=./frame/elections-phragmen/src/weights.rs +// --json-file=/builds/parity/mirrors/substrate/.git/.artifacts/bench.json +// --pallet=pallet_elections_phragmen +// --chain=dev // --header=./HEADER-APACHE2 +// --output=./frame/elections-phragmen/src/weights.rs // --template=./.maintain/frame-weight-template.hbs #![cfg_attr(rustfmt, rustfmt_skip)] @@ -80,10 +81,10 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `499 + v * (80 ±0)` // Estimated: `9726 + v * (320 ±0)` - // Minimum execution time: 25_407 nanoseconds. - Weight::from_parts(26_035_742, 9726) - // Standard Error: 2_162 - .saturating_add(Weight::from_ref_time(120_321).saturating_mul(v.into())) + // Minimum execution time: 27_362 nanoseconds. + Weight::from_parts(28_497_963, 9726) + // Standard Error: 3_968 + .saturating_add(Weight::from_ref_time(176_840).saturating_mul(v.into())) .saturating_add(T::DbWeight::get().reads(5_u64)) .saturating_add(T::DbWeight::get().writes(2_u64)) .saturating_add(Weight::from_proof_size(320).saturating_mul(v.into())) @@ -103,10 +104,10 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `467 + v * (80 ±0)` // Estimated: `9598 + v * (320 ±0)` - // Minimum execution time: 34_477 nanoseconds. - Weight::from_parts(35_197_694, 9598) - // Standard Error: 2_089 - .saturating_add(Weight::from_ref_time(116_792).saturating_mul(v.into())) + // Minimum execution time: 37_120 nanoseconds. + Weight::from_parts(38_455_302, 9598) + // Standard Error: 5_478 + .saturating_add(Weight::from_ref_time(219_678).saturating_mul(v.into())) .saturating_add(T::DbWeight::get().reads(5_u64)) .saturating_add(T::DbWeight::get().writes(2_u64)) .saturating_add(Weight::from_proof_size(320).saturating_mul(v.into())) @@ -126,10 +127,10 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `499 + v * (80 ±0)` // Estimated: `9726 + v * (320 ±0)` - // Minimum execution time: 34_573 nanoseconds. - Weight::from_parts(35_254_508, 9726) - // Standard Error: 2_076 - .saturating_add(Weight::from_ref_time(110_656).saturating_mul(v.into())) + // Minimum execution time: 36_928 nanoseconds. + Weight::from_parts(38_334_669, 9726) + // Standard Error: 5_271 + .saturating_add(Weight::from_ref_time(232_355).saturating_mul(v.into())) .saturating_add(T::DbWeight::get().reads(5_u64)) .saturating_add(T::DbWeight::get().writes(2_u64)) .saturating_add(Weight::from_proof_size(320).saturating_mul(v.into())) @@ -142,8 +143,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `989` // Estimated: `7238` - // Minimum execution time: 31_469 nanoseconds. - Weight::from_parts(31_877_000, 7238) + // Minimum execution time: 34_338 nanoseconds. + Weight::from_parts(35_672_000, 7238) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().writes(2_u64)) } @@ -153,30 +154,30 @@ impl WeightInfo for SubstrateWeight { /// Proof Skipped: Elections Members (max_values: Some(1), max_size: None, mode: Measured) /// Storage: Elections RunnersUp (r:1 w:0) /// Proof Skipped: Elections RunnersUp (max_values: Some(1), max_size: None, mode: Measured) - /// The range of component `c` is `[1, 1000]`. + /// The range of component `c` is `[1, 64]`. fn submit_candidacy(c: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `1687 + c * (48 ±0)` - // Estimated: `6540 + c * (144 ±0)` - // Minimum execution time: 26_969 nanoseconds. - Weight::from_parts(28_584_266, 6540) - // Standard Error: 93 - .saturating_add(Weight::from_ref_time(52_393).saturating_mul(c.into())) + // Measured: `1697 + c * (48 ±0)` + // Estimated: `6576 + c * (144 ±0)` + // Minimum execution time: 31_864 nanoseconds. + Weight::from_parts(33_490_161, 6576) + // Standard Error: 2_643 + .saturating_add(Weight::from_ref_time(158_386).saturating_mul(c.into())) .saturating_add(T::DbWeight::get().reads(3_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) .saturating_add(Weight::from_proof_size(144).saturating_mul(c.into())) } /// Storage: Elections Candidates (r:1 w:1) /// Proof Skipped: Elections Candidates (max_values: Some(1), max_size: None, mode: Measured) - /// The range of component `c` is `[1, 1000]`. + /// The range of component `c` is `[1, 64]`. fn renounce_candidacy_candidate(c: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `348 + c * (48 ±0)` - // Estimated: `830 + c * (48 ±0)` - // Minimum execution time: 23_635 nanoseconds. - Weight::from_parts(23_482_193, 830) - // Standard Error: 103 - .saturating_add(Weight::from_ref_time(33_759).saturating_mul(c.into())) + // Measured: `349 + c * (48 ±0)` + // Estimated: `844 + c * (48 ±0)` + // Minimum execution time: 27_292 nanoseconds. + Weight::from_parts(28_364_955, 844) + // Standard Error: 1_335 + .saturating_add(Weight::from_ref_time(78_086).saturating_mul(c.into())) .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) .saturating_add(Weight::from_proof_size(48).saturating_mul(c.into())) @@ -195,8 +196,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `2027` // Estimated: `12115` - // Minimum execution time: 39_124 nanoseconds. - Weight::from_parts(39_575_000, 12115) + // Minimum execution time: 45_975 nanoseconds. + Weight::from_parts(47_103_000, 12115) .saturating_add(T::DbWeight::get().reads(4_u64)) .saturating_add(T::DbWeight::get().writes(4_u64)) } @@ -206,8 +207,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `975` // Estimated: `1470` - // Minimum execution time: 25_377 nanoseconds. - Weight::from_parts(25_696_000, 1470) + // Minimum execution time: 29_243 nanoseconds. + Weight::from_parts(30_582_000, 1470) .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -236,12 +237,12 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `2027` // Estimated: `14718` - // Minimum execution time: 44_919 nanoseconds. - Weight::from_parts(45_548_000, 14718) + // Minimum execution time: 52_527 nanoseconds. + Weight::from_parts(53_538_000, 14718) .saturating_add(T::DbWeight::get().reads(5_u64)) .saturating_add(T::DbWeight::get().writes(5_u64)) } - /// Storage: Elections Voting (r:10001 w:10000) + /// Storage: Elections Voting (r:513 w:512) /// Proof Skipped: Elections Voting (max_values: None, max_size: None, mode: Measured) /// Storage: Elections Members (r:1 w:0) /// Proof Skipped: Elections Members (max_values: Some(1), max_size: None, mode: Measured) @@ -249,24 +250,24 @@ impl WeightInfo for SubstrateWeight { /// Proof Skipped: Elections RunnersUp (max_values: Some(1), max_size: None, mode: Measured) /// Storage: Elections Candidates (r:1 w:0) /// Proof Skipped: Elections Candidates (max_values: Some(1), max_size: None, mode: Measured) - /// Storage: Balances Locks (r:10000 w:10000) + /// Storage: Balances Locks (r:512 w:512) /// Proof: Balances Locks (max_values: None, max_size: Some(1299), added: 3774, mode: MaxEncodedLen) - /// Storage: System Account (r:10000 w:10000) + /// Storage: System Account (r:512 w:512) /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) - /// The range of component `v` is `[5000, 10000]`. - /// The range of component `d` is `[0, 5000]`. + /// The range of component `v` is `[256, 512]`. + /// The range of component `d` is `[0, 256]`. fn clean_defunct_voters(v: u32, _d: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `36028 + v * (872 ±0)` - // Estimated: `149172 + v * (12340 ±0)` - // Minimum execution time: 297_544_939 nanoseconds. - Weight::from_parts(298_088_024_000, 149172) - // Standard Error: 264_599 - .saturating_add(Weight::from_ref_time(38_142_857).saturating_mul(v.into())) + // Measured: `1115 + v * (875 ±0)` + // Estimated: `8448 + v * (12352 ±0)` + // Minimum execution time: 14_934_185 nanoseconds. + Weight::from_parts(15_014_057_000, 8448) + // Standard Error: 245_588 + .saturating_add(Weight::from_ref_time(35_586_946).saturating_mul(v.into())) .saturating_add(T::DbWeight::get().reads(4_u64)) .saturating_add(T::DbWeight::get().reads((3_u64).saturating_mul(v.into()))) .saturating_add(T::DbWeight::get().writes((3_u64).saturating_mul(v.into()))) - .saturating_add(Weight::from_proof_size(12340).saturating_mul(v.into())) + .saturating_add(Weight::from_proof_size(12352).saturating_mul(v.into())) } /// Storage: Elections Candidates (r:1 w:1) /// Proof Skipped: Elections Candidates (max_values: Some(1), max_size: None, mode: Measured) @@ -274,11 +275,11 @@ impl WeightInfo for SubstrateWeight { /// Proof Skipped: Elections Members (max_values: Some(1), max_size: None, mode: Measured) /// Storage: Elections RunnersUp (r:1 w:1) /// Proof Skipped: Elections RunnersUp (max_values: Some(1), max_size: None, mode: Measured) - /// Storage: Elections Voting (r:10001 w:0) + /// Storage: Elections Voting (r:513 w:0) /// Proof Skipped: Elections Voting (max_values: None, max_size: None, mode: Measured) /// Storage: Council Proposals (r:1 w:0) /// Proof Skipped: Council Proposals (max_values: Some(1), max_size: None, mode: Measured) - /// Storage: System Account (r:980 w:980) + /// Storage: System Account (r:44 w:44) /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) /// Storage: Elections ElectionRounds (r:1 w:1) /// Proof Skipped: Elections ElectionRounds (max_values: Some(1), max_size: None, mode: Measured) @@ -286,27 +287,27 @@ impl WeightInfo for SubstrateWeight { /// Proof Skipped: Council Members (max_values: Some(1), max_size: None, mode: Measured) /// Storage: Council Prime (r:0 w:1) /// Proof Skipped: Council Prime (max_values: Some(1), max_size: None, mode: Measured) - /// The range of component `c` is `[1, 1000]`. - /// The range of component `v` is `[1, 10000]`. - /// The range of component `e` is `[10000, 160000]`. + /// The range of component `c` is `[1, 64]`. + /// The range of component `v` is `[1, 512]`. + /// The range of component `e` is `[512, 8192]`. fn election_phragmen(c: u32, v: u32, e: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `0 + v * (639 ±0) + e * (28 ±0)` - // Estimated: `5350105 + c * (2582 ±0) + v * (5721 ±6) + e * (123 ±0)` - // Minimum execution time: 21_844_965 nanoseconds. - Weight::from_parts(21_979_826_000, 5350105) - // Standard Error: 229_799 - .saturating_add(Weight::from_ref_time(24_976_612).saturating_mul(v.into())) - // Standard Error: 14_747 - .saturating_add(Weight::from_ref_time(1_025_848).saturating_mul(e.into())) - .saturating_add(T::DbWeight::get().reads(280_u64)) + // Measured: `0 + v * (638 ±0) + e * (28 ±0)` + // Estimated: `330033 + v * (5229 ±6) + e * (89 ±0) + c * (2135 ±7)` + // Minimum execution time: 1_273_671 nanoseconds. + Weight::from_parts(1_279_716_000, 330033) + // Standard Error: 543_277 + .saturating_add(Weight::from_ref_time(20_613_753).saturating_mul(v.into())) + // Standard Error: 34_857 + .saturating_add(Weight::from_ref_time(688_354).saturating_mul(e.into())) + .saturating_add(T::DbWeight::get().reads(21_u64)) .saturating_add(T::DbWeight::get().reads((1_u64).saturating_mul(c.into()))) .saturating_add(T::DbWeight::get().reads((1_u64).saturating_mul(v.into()))) .saturating_add(T::DbWeight::get().writes(6_u64)) .saturating_add(T::DbWeight::get().writes((1_u64).saturating_mul(c.into()))) - .saturating_add(Weight::from_proof_size(2582).saturating_mul(c.into())) - .saturating_add(Weight::from_proof_size(5721).saturating_mul(v.into())) - .saturating_add(Weight::from_proof_size(123).saturating_mul(e.into())) + .saturating_add(Weight::from_proof_size(5229).saturating_mul(v.into())) + .saturating_add(Weight::from_proof_size(89).saturating_mul(e.into())) + .saturating_add(Weight::from_proof_size(2135).saturating_mul(c.into())) } } @@ -327,10 +328,10 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `499 + v * (80 ±0)` // Estimated: `9726 + v * (320 ±0)` - // Minimum execution time: 25_407 nanoseconds. - Weight::from_parts(26_035_742, 9726) - // Standard Error: 2_162 - .saturating_add(Weight::from_ref_time(120_321).saturating_mul(v.into())) + // Minimum execution time: 27_362 nanoseconds. + Weight::from_parts(28_497_963, 9726) + // Standard Error: 3_968 + .saturating_add(Weight::from_ref_time(176_840).saturating_mul(v.into())) .saturating_add(RocksDbWeight::get().reads(5_u64)) .saturating_add(RocksDbWeight::get().writes(2_u64)) .saturating_add(Weight::from_proof_size(320).saturating_mul(v.into())) @@ -350,10 +351,10 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `467 + v * (80 ±0)` // Estimated: `9598 + v * (320 ±0)` - // Minimum execution time: 34_477 nanoseconds. - Weight::from_parts(35_197_694, 9598) - // Standard Error: 2_089 - .saturating_add(Weight::from_ref_time(116_792).saturating_mul(v.into())) + // Minimum execution time: 37_120 nanoseconds. + Weight::from_parts(38_455_302, 9598) + // Standard Error: 5_478 + .saturating_add(Weight::from_ref_time(219_678).saturating_mul(v.into())) .saturating_add(RocksDbWeight::get().reads(5_u64)) .saturating_add(RocksDbWeight::get().writes(2_u64)) .saturating_add(Weight::from_proof_size(320).saturating_mul(v.into())) @@ -373,10 +374,10 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `499 + v * (80 ±0)` // Estimated: `9726 + v * (320 ±0)` - // Minimum execution time: 34_573 nanoseconds. - Weight::from_parts(35_254_508, 9726) - // Standard Error: 2_076 - .saturating_add(Weight::from_ref_time(110_656).saturating_mul(v.into())) + // Minimum execution time: 36_928 nanoseconds. + Weight::from_parts(38_334_669, 9726) + // Standard Error: 5_271 + .saturating_add(Weight::from_ref_time(232_355).saturating_mul(v.into())) .saturating_add(RocksDbWeight::get().reads(5_u64)) .saturating_add(RocksDbWeight::get().writes(2_u64)) .saturating_add(Weight::from_proof_size(320).saturating_mul(v.into())) @@ -389,8 +390,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `989` // Estimated: `7238` - // Minimum execution time: 31_469 nanoseconds. - Weight::from_parts(31_877_000, 7238) + // Minimum execution time: 34_338 nanoseconds. + Weight::from_parts(35_672_000, 7238) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().writes(2_u64)) } @@ -400,30 +401,30 @@ impl WeightInfo for () { /// Proof Skipped: Elections Members (max_values: Some(1), max_size: None, mode: Measured) /// Storage: Elections RunnersUp (r:1 w:0) /// Proof Skipped: Elections RunnersUp (max_values: Some(1), max_size: None, mode: Measured) - /// The range of component `c` is `[1, 1000]`. + /// The range of component `c` is `[1, 64]`. fn submit_candidacy(c: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `1687 + c * (48 ±0)` - // Estimated: `6540 + c * (144 ±0)` - // Minimum execution time: 26_969 nanoseconds. - Weight::from_parts(28_584_266, 6540) - // Standard Error: 93 - .saturating_add(Weight::from_ref_time(52_393).saturating_mul(c.into())) + // Measured: `1697 + c * (48 ±0)` + // Estimated: `6576 + c * (144 ±0)` + // Minimum execution time: 31_864 nanoseconds. + Weight::from_parts(33_490_161, 6576) + // Standard Error: 2_643 + .saturating_add(Weight::from_ref_time(158_386).saturating_mul(c.into())) .saturating_add(RocksDbWeight::get().reads(3_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) .saturating_add(Weight::from_proof_size(144).saturating_mul(c.into())) } /// Storage: Elections Candidates (r:1 w:1) /// Proof Skipped: Elections Candidates (max_values: Some(1), max_size: None, mode: Measured) - /// The range of component `c` is `[1, 1000]`. + /// The range of component `c` is `[1, 64]`. fn renounce_candidacy_candidate(c: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `348 + c * (48 ±0)` - // Estimated: `830 + c * (48 ±0)` - // Minimum execution time: 23_635 nanoseconds. - Weight::from_parts(23_482_193, 830) - // Standard Error: 103 - .saturating_add(Weight::from_ref_time(33_759).saturating_mul(c.into())) + // Measured: `349 + c * (48 ±0)` + // Estimated: `844 + c * (48 ±0)` + // Minimum execution time: 27_292 nanoseconds. + Weight::from_parts(28_364_955, 844) + // Standard Error: 1_335 + .saturating_add(Weight::from_ref_time(78_086).saturating_mul(c.into())) .saturating_add(RocksDbWeight::get().reads(1_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) .saturating_add(Weight::from_proof_size(48).saturating_mul(c.into())) @@ -442,8 +443,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `2027` // Estimated: `12115` - // Minimum execution time: 39_124 nanoseconds. - Weight::from_parts(39_575_000, 12115) + // Minimum execution time: 45_975 nanoseconds. + Weight::from_parts(47_103_000, 12115) .saturating_add(RocksDbWeight::get().reads(4_u64)) .saturating_add(RocksDbWeight::get().writes(4_u64)) } @@ -453,8 +454,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `975` // Estimated: `1470` - // Minimum execution time: 25_377 nanoseconds. - Weight::from_parts(25_696_000, 1470) + // Minimum execution time: 29_243 nanoseconds. + Weight::from_parts(30_582_000, 1470) .saturating_add(RocksDbWeight::get().reads(1_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } @@ -483,12 +484,12 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `2027` // Estimated: `14718` - // Minimum execution time: 44_919 nanoseconds. - Weight::from_parts(45_548_000, 14718) + // Minimum execution time: 52_527 nanoseconds. + Weight::from_parts(53_538_000, 14718) .saturating_add(RocksDbWeight::get().reads(5_u64)) .saturating_add(RocksDbWeight::get().writes(5_u64)) } - /// Storage: Elections Voting (r:10001 w:10000) + /// Storage: Elections Voting (r:513 w:512) /// Proof Skipped: Elections Voting (max_values: None, max_size: None, mode: Measured) /// Storage: Elections Members (r:1 w:0) /// Proof Skipped: Elections Members (max_values: Some(1), max_size: None, mode: Measured) @@ -496,24 +497,24 @@ impl WeightInfo for () { /// Proof Skipped: Elections RunnersUp (max_values: Some(1), max_size: None, mode: Measured) /// Storage: Elections Candidates (r:1 w:0) /// Proof Skipped: Elections Candidates (max_values: Some(1), max_size: None, mode: Measured) - /// Storage: Balances Locks (r:10000 w:10000) + /// Storage: Balances Locks (r:512 w:512) /// Proof: Balances Locks (max_values: None, max_size: Some(1299), added: 3774, mode: MaxEncodedLen) - /// Storage: System Account (r:10000 w:10000) + /// Storage: System Account (r:512 w:512) /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) - /// The range of component `v` is `[5000, 10000]`. - /// The range of component `d` is `[0, 5000]`. + /// The range of component `v` is `[256, 512]`. + /// The range of component `d` is `[0, 256]`. fn clean_defunct_voters(v: u32, _d: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `36028 + v * (872 ±0)` - // Estimated: `149172 + v * (12340 ±0)` - // Minimum execution time: 297_544_939 nanoseconds. - Weight::from_parts(298_088_024_000, 149172) - // Standard Error: 264_599 - .saturating_add(Weight::from_ref_time(38_142_857).saturating_mul(v.into())) + // Measured: `1115 + v * (875 ±0)` + // Estimated: `8448 + v * (12352 ±0)` + // Minimum execution time: 14_934_185 nanoseconds. + Weight::from_parts(15_014_057_000, 8448) + // Standard Error: 245_588 + .saturating_add(Weight::from_ref_time(35_586_946).saturating_mul(v.into())) .saturating_add(RocksDbWeight::get().reads(4_u64)) .saturating_add(RocksDbWeight::get().reads((3_u64).saturating_mul(v.into()))) .saturating_add(RocksDbWeight::get().writes((3_u64).saturating_mul(v.into()))) - .saturating_add(Weight::from_proof_size(12340).saturating_mul(v.into())) + .saturating_add(Weight::from_proof_size(12352).saturating_mul(v.into())) } /// Storage: Elections Candidates (r:1 w:1) /// Proof Skipped: Elections Candidates (max_values: Some(1), max_size: None, mode: Measured) @@ -521,11 +522,11 @@ impl WeightInfo for () { /// Proof Skipped: Elections Members (max_values: Some(1), max_size: None, mode: Measured) /// Storage: Elections RunnersUp (r:1 w:1) /// Proof Skipped: Elections RunnersUp (max_values: Some(1), max_size: None, mode: Measured) - /// Storage: Elections Voting (r:10001 w:0) + /// Storage: Elections Voting (r:513 w:0) /// Proof Skipped: Elections Voting (max_values: None, max_size: None, mode: Measured) /// Storage: Council Proposals (r:1 w:0) /// Proof Skipped: Council Proposals (max_values: Some(1), max_size: None, mode: Measured) - /// Storage: System Account (r:980 w:980) + /// Storage: System Account (r:44 w:44) /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) /// Storage: Elections ElectionRounds (r:1 w:1) /// Proof Skipped: Elections ElectionRounds (max_values: Some(1), max_size: None, mode: Measured) @@ -533,26 +534,26 @@ impl WeightInfo for () { /// Proof Skipped: Council Members (max_values: Some(1), max_size: None, mode: Measured) /// Storage: Council Prime (r:0 w:1) /// Proof Skipped: Council Prime (max_values: Some(1), max_size: None, mode: Measured) - /// The range of component `c` is `[1, 1000]`. - /// The range of component `v` is `[1, 10000]`. - /// The range of component `e` is `[10000, 160000]`. + /// The range of component `c` is `[1, 64]`. + /// The range of component `v` is `[1, 512]`. + /// The range of component `e` is `[512, 8192]`. fn election_phragmen(c: u32, v: u32, e: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `0 + v * (639 ±0) + e * (28 ±0)` - // Estimated: `5350105 + c * (2582 ±0) + v * (5721 ±6) + e * (123 ±0)` - // Minimum execution time: 21_844_965 nanoseconds. - Weight::from_parts(21_979_826_000, 5350105) - // Standard Error: 229_799 - .saturating_add(Weight::from_ref_time(24_976_612).saturating_mul(v.into())) - // Standard Error: 14_747 - .saturating_add(Weight::from_ref_time(1_025_848).saturating_mul(e.into())) - .saturating_add(RocksDbWeight::get().reads(280_u64)) + // Measured: `0 + v * (638 ±0) + e * (28 ±0)` + // Estimated: `330033 + v * (5229 ±6) + e * (89 ±0) + c * (2135 ±7)` + // Minimum execution time: 1_273_671 nanoseconds. + Weight::from_parts(1_279_716_000, 330033) + // Standard Error: 543_277 + .saturating_add(Weight::from_ref_time(20_613_753).saturating_mul(v.into())) + // Standard Error: 34_857 + .saturating_add(Weight::from_ref_time(688_354).saturating_mul(e.into())) + .saturating_add(RocksDbWeight::get().reads(21_u64)) .saturating_add(RocksDbWeight::get().reads((1_u64).saturating_mul(c.into()))) .saturating_add(RocksDbWeight::get().reads((1_u64).saturating_mul(v.into()))) .saturating_add(RocksDbWeight::get().writes(6_u64)) .saturating_add(RocksDbWeight::get().writes((1_u64).saturating_mul(c.into()))) - .saturating_add(Weight::from_proof_size(2582).saturating_mul(c.into())) - .saturating_add(Weight::from_proof_size(5721).saturating_mul(v.into())) - .saturating_add(Weight::from_proof_size(123).saturating_mul(e.into())) + .saturating_add(Weight::from_proof_size(5229).saturating_mul(v.into())) + .saturating_add(Weight::from_proof_size(89).saturating_mul(e.into())) + .saturating_add(Weight::from_proof_size(2135).saturating_mul(c.into())) } } diff --git a/frame/support/procedural/src/construct_runtime/mod.rs b/frame/support/procedural/src/construct_runtime/mod.rs index 9e22037a6..18b66ddff 100644 --- a/frame/support/procedural/src/construct_runtime/mod.rs +++ b/frame/support/procedural/src/construct_runtime/mod.rs @@ -579,6 +579,7 @@ fn decl_integrity_test(scrate: &TokenStream2) -> TokenStream2 { #[test] pub fn runtime_integrity_tests() { + #scrate::sp_tracing::try_init_simple(); ::integrity_test(); } } From 6f72780b87e41a025722284a52a7d85f41958a82 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alexander=20Thei=C3=9Fen?= Date: Wed, 8 Feb 2023 16:40:45 -0300 Subject: [PATCH 100/558] Rework generated API docs (#13178) --- frame/contracts/proc-macro/src/lib.rs | 219 +++++++++++++++----------- frame/contracts/src/wasm/mod.rs | 7 +- 2 files changed, 132 insertions(+), 94 deletions(-) diff --git a/frame/contracts/proc-macro/src/lib.rs b/frame/contracts/proc-macro/src/lib.rs index de7b9b881..c5f52f43a 100644 --- a/frame/contracts/proc-macro/src/lib.rs +++ b/frame/contracts/proc-macro/src/lib.rs @@ -25,10 +25,12 @@ extern crate alloc; use alloc::{ + collections::BTreeMap, format, string::{String, ToString}, vec::Vec, }; +use core::cmp::Reverse; use proc_macro::TokenStream; use proc_macro2::{Span, TokenStream as TokenStream2}; use quote::{quote, quote_spanned, ToTokens}; @@ -160,7 +162,7 @@ struct EnvDef { /// Parsed host function definition. struct HostFn { item: syn::ItemFn, - module: String, + version: u8, name: String, returns: HostFnReturn, is_stable: bool, @@ -208,7 +210,7 @@ impl HostFn { let span = item.span(); let mut attrs = item.attrs.clone(); attrs.retain(|a| !a.path.is_ident("doc")); - let mut maybe_module = None; + let mut maybe_version = None; let mut is_stable = true; let mut alias_to = None; let mut not_deprecated = true; @@ -216,12 +218,11 @@ impl HostFn { let ident = attr.path.get_ident().ok_or(err(span, msg))?.to_string(); match ident.as_str() { "version" => { - if maybe_module.is_some() { + if maybe_version.is_some() { return Err(err(span, "#[version] can only be specified once")) } - let ver: u8 = - attr.parse_args::().and_then(|lit| lit.base10_parse())?; - maybe_module = Some(format!("seal{}", ver)); + maybe_version = + Some(attr.parse_args::().and_then(|lit| lit.base10_parse())?); }, "unstable" => { if !is_stable { @@ -341,7 +342,7 @@ impl HostFn { Ok(Self { item, - module: maybe_module.unwrap_or_else(|| "seal0".to_string()), + version: maybe_version.unwrap_or_default(), name, returns, is_stable, @@ -355,6 +356,10 @@ impl HostFn { _ => Err(err(span, &msg)), } } + + fn module(&self) -> String { + format!("seal{}", self.version) + } } impl EnvDef { @@ -409,83 +414,116 @@ fn is_valid_special_arg(idx: usize, arg: &FnArg) -> bool { matches!(*pat.ty, syn::Type::Infer(_)) } -/// Expands documentation for host functions. -fn expand_docs(def: &mut EnvDef) -> TokenStream2 { - let mut modules = def.host_funcs.iter().map(|f| f.module.clone()).collect::>(); - modules.sort(); - modules.dedup(); - - let doc_selector = |a: &syn::Attribute| a.path.is_ident("doc"); - let docs = modules.iter().map(|m| { - let funcs = def.host_funcs.iter_mut().map(|f| { - if *m == f.module { - // Remove auxiliary args: `ctx: _` and `memory: _` - f.item.sig.inputs = f - .item - .sig - .inputs - .iter() - .skip(2) - .map(|p| p.clone()) - .collect::>(); - let func_decl = f.item.sig.to_token_stream(); - let func_doc = if let Some(origin_fn) = &f.alias_to { - let alias_doc = format!( - "This is just an alias function to [`{0}()`][`Self::{0}`] with backwards-compatible prefixed identifier.", - origin_fn, - ); - quote! { #[doc = #alias_doc] } - - } else { - let func_docs = f.item.attrs.iter().filter(|a| doc_selector(a)).map(|d| { - let docs = d.to_token_stream(); - quote! { #docs } - }); - let unstable_notice = if !f.is_stable { - let warning = "\n # Unstable\n\n \ - This function is unstable and it is a subject to change (or removal) in the future.\n \ - Do not deploy a contract using it to a production chain."; - quote! { #[doc = #warning] } - } else { - quote! {} - }; - quote! { - #( #func_docs )* - #unstable_notice - } - }; - quote! { - #func_doc - #func_decl; - } - } else { - quote! {} - } - }); +fn expand_func_doc(func: &HostFn) -> TokenStream2 { + // Remove auxiliary args: `ctx: _` and `memory: _` + let func_decl = { + let mut sig = func.item.sig.clone(); + sig.inputs = sig + .inputs + .iter() + .skip(2) + .map(|p| p.clone()) + .collect::>(); + sig.to_token_stream() + }; + let func_doc = { + let func_docs = if let Some(origin_fn) = &func.alias_to { + let alias_doc = format!( + "This is just an alias function to [`{0}()`][`Self::{0}`] with backwards-compatible prefixed identifier.", + origin_fn, + ); + quote! { #[doc = #alias_doc] } + } else { + let docs = func.item.attrs.iter().filter(|a| a.path.is_ident("doc")).map(|d| { + let docs = d.to_token_stream(); + quote! { #docs } + }); + quote! { #( #docs )* } + }; + let deprecation_notice = if !func.not_deprecated { + let warning = "\n # Deprecated\n\n \ + This function is deprecated and will be removed in future versions.\n \ + No new code or contracts with this API can be deployed."; + quote! { #[doc = #warning] } + } else { + quote! {} + }; + let import_notice = { + let info = format!( + "\n# Wasm Import Statement\n```wat\n(import \"seal{}\" \"{}\" (func ...))\n```", + func.version, func.name, + ); + quote! { #[doc = #info] } + }; + let unstable_notice = if !func.is_stable { + let warning = "\n # Unstable\n\n \ + This function is unstable and it is a subject to change (or removal) in the future.\n \ + Do not deploy a contract using it to a production chain."; + quote! { #[doc = #warning] } + } else { + quote! {} + }; + quote! { + #deprecation_notice + #func_docs + #import_notice + #unstable_notice + } + }; + quote! { + #func_doc + #func_decl; + } +} - let module = Ident::new(m, Span::call_site()); - let module_doc = format!( - "Documentation of the API available to contracts by importing `{}` WASM module.", - module - ); +/// Expands documentation for host functions. +fn expand_docs(def: &EnvDef) -> TokenStream2 { + // Create the `Current` trait with only the newest versions + // we sort so that only the newest versions make it into `docs` + let mut current_docs = BTreeMap::new(); + let mut funcs: Vec<_> = def.host_funcs.iter().filter(|f| f.alias_to.is_none()).collect(); + funcs.sort_unstable_by_key(|func| Reverse(func.version)); + for func in funcs { + if current_docs.contains_key(&func.name) { + continue + } + current_docs.insert(func.name.clone(), expand_func_doc(&func)); + } + let current_docs = current_docs.values(); + // Create the `legacy` module with all functions + // Maps from version to list of functions that have this version + let mut legacy_doc = BTreeMap::>::new(); + for func in def.host_funcs.iter() { + legacy_doc.entry(func.version).or_default().push(expand_func_doc(&func)); + } + let legacy_doc = legacy_doc.into_iter().map(|(version, funcs)| { + let doc = format!("All functions available in the **seal{}** module", version); + let version = Ident::new(&format!("Version{version}"), Span::call_site()); quote! { - #[doc = #module_doc] - pub mod #module { - use crate::wasm::runtime::{TrapReason, ReturnCode}; - /// Every function in this trait represents (at least) one function that can be imported by a contract. - /// - /// The function's identifier is to be set as the name in the import definition. - /// Where it is specifically indicated, an _alias_ function having `seal_`-prefixed identifier and - /// just the same signature and body, is also available (for backwards-compatibility purposes). - pub trait Api { - #( #funcs )* - } + #[doc = #doc] + pub trait #version { + #( #funcs )* } } }); + quote! { - #( #docs )* + /// Contains only the latest version of each function. + /// + /// In reality there are more functions available but they are all obsolete: When a function + /// is updated a new **version** is added and the old versions stays available as-is. + /// We only list the newest version here. Some functions are available under additional + /// names (aliases) for historic reasons which are omitted here. + /// + /// If you want an overview of all the functions available to a contact all you need + /// to look at is this trait. It contains only the latest version of each + /// function and no aliases. If you are writing a contract(language) from scratch + /// this is where you should look at. + pub trait Current { + #( #current_docs )* + } + #( #legacy_doc )* } } @@ -493,25 +531,26 @@ fn expand_docs(def: &mut EnvDef) -> TokenStream2 { /// Should generate source code for: /// - implementations of the host functions to be added to the wasm runtime environment (see /// `expand_impls()`). -fn expand_env(def: &mut EnvDef, docs: bool) -> TokenStream2 { +fn expand_env(def: &EnvDef, docs: bool) -> TokenStream2 { let impls = expand_impls(def); let docs = docs.then_some(expand_docs(def)).unwrap_or(TokenStream2::new()); quote! { pub struct Env; #impls - /// Contains the documentation of the API available to contracts. + /// Documentation of the API (host functions) available to contracts. /// - /// In order to generate this documentation, pass `doc` attribute to the [`#[define_env]`][`macro@define_env`] macro: - /// `#[define_env(doc)]`, and then run `cargo doc`. + /// The `Current` trait might be the most useful doc to look at. The versioned + /// traits only exist for reference: If trying to find out if a specific version of + /// `pallet-contracts` contains a certain function. /// - /// This module is not meant to be used by any code. Rather, it is meant to be consumed by humans through rustdoc. + /// # Note /// - /// Every function described in this module's sub module's traits uses this sub module's identifier - /// as its imported module name. The identifier of the function is the function's imported name. - /// According to the [WASM spec of imports](https://webassembly.github.io/spec/core/text/modules.html#text-import). + /// This module is not meant to be used by any code. Rather, it is meant to be + /// consumed by humans through rustdoc. #[cfg(doc)] pub mod api_doc { + use super::{TrapReason, ReturnCode}; #docs } } @@ -520,7 +559,7 @@ fn expand_env(def: &mut EnvDef, docs: bool) -> TokenStream2 { /// Generates for every host function: /// - real implementation, to register it in the contract execution environment; /// - dummy implementation, to be used as mocks for contract validation step. -fn expand_impls(def: &mut EnvDef) -> TokenStream2 { +fn expand_impls(def: &EnvDef) -> TokenStream2 { let impls = expand_functions(def, true, quote! { crate::wasm::Runtime }); let dummy_impls = expand_functions(def, false, quote! { () }); @@ -553,16 +592,12 @@ fn expand_impls(def: &mut EnvDef) -> TokenStream2 { } } -fn expand_functions( - def: &mut EnvDef, - expand_blocks: bool, - host_state: TokenStream2, -) -> TokenStream2 { +fn expand_functions(def: &EnvDef, expand_blocks: bool, host_state: TokenStream2) -> TokenStream2 { let impls = def.host_funcs.iter().map(|f| { // skip the context and memory argument let params = f.item.sig.inputs.iter().skip(2); let (module, name, body, wasm_output, output) = ( - &f.module, + f.module(), &f.name, &f.item.block, f.returns.to_wasm_sig(), diff --git a/frame/contracts/src/wasm/mod.rs b/frame/contracts/src/wasm/mod.rs index 6b9cefcdd..9bf36b47f 100644 --- a/frame/contracts/src/wasm/mod.rs +++ b/frame/contracts/src/wasm/mod.rs @@ -24,8 +24,13 @@ mod runtime; #[cfg(feature = "runtime-benchmarks")] pub use crate::wasm::code_cache::reinstrument; + #[cfg(doc)] pub use crate::wasm::runtime::api_doc; + +#[cfg(test)] +pub use tests::MockExt; + pub use crate::wasm::{ prepare::TryInstantiate, runtime::{ @@ -45,8 +50,6 @@ use frame_support::dispatch::{DispatchError, DispatchResult}; use sp_core::Get; use sp_runtime::RuntimeDebug; use sp_std::prelude::*; -#[cfg(test)] -pub use tests::MockExt; use wasmi::{ Config as WasmiConfig, Engine, Instance, Linker, Memory, MemoryType, Module, StackLimits, Store, }; From 0d611e091178f220455e2ec929ce38b0e9be3253 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bastian=20K=C3=B6cher?= Date: Thu, 9 Feb 2023 11:00:55 +0100 Subject: [PATCH 101/558] pallet-scheduler: Ensure we request a preimage (#13340) * pallet-scheduler: Ensure we request a preimage The scheduler was not requesting a preimage. When a preimage is requested, a user can deposit it without paying any fees. * Review changes --- frame/scheduler/src/lib.rs | 23 ++++++++++++++++-- frame/scheduler/src/tests.rs | 23 +++++++++++++----- frame/support/src/traits/preimages.rs | 35 ++++++++++++++++++--------- 3 files changed, 62 insertions(+), 19 deletions(-) diff --git a/frame/scheduler/src/lib.rs b/frame/scheduler/src/lib.rs index afc4fd66e..dfec98d4e 100644 --- a/frame/scheduler/src/lib.rs +++ b/frame/scheduler/src/lib.rs @@ -777,6 +777,8 @@ impl Pallet { ) -> Result, DispatchError> { let when = Self::resolve_time(when)?; + let lookup_hash = call.lookup_hash(); + // sanitize maybe_periodic let maybe_periodic = maybe_periodic .filter(|p| p.1 > 1 && !p.0.is_zero()) @@ -790,7 +792,14 @@ impl Pallet { origin, _phantom: PhantomData, }; - Self::place_task(when, task).map_err(|x| x.0) + let res = Self::place_task(when, task).map_err(|x| x.0)?; + + if let Some(hash) = lookup_hash { + // Request the call to be made available. + T::Preimages::request(&hash); + } + + Ok(res) } fn do_cancel( @@ -862,6 +871,8 @@ impl Pallet { let when = Self::resolve_time(when)?; + let lookup_hash = call.lookup_hash(); + // sanitize maybe_periodic let maybe_periodic = maybe_periodic .filter(|p| p.1 > 1 && !p.0.is_zero()) @@ -876,7 +887,14 @@ impl Pallet { origin, _phantom: Default::default(), }; - Self::place_task(when, task).map_err(|x| x.0) + let res = Self::place_task(when, task).map_err(|x| x.0)?; + + if let Some(hash) = lookup_hash { + // Request the call to be made available. + T::Preimages::request(&hash); + } + + Ok(res) } fn do_cancel_named(origin: Option, id: TaskName) -> DispatchResult { @@ -1027,6 +1045,7 @@ impl Pallet { } else { Agenda::::remove(when); } + postponed == 0 } diff --git a/frame/scheduler/src/tests.rs b/frame/scheduler/src/tests.rs index e5467fc8d..a261c6718 100644 --- a/frame/scheduler/src/tests.rs +++ b/frame/scheduler/src/tests.rs @@ -58,9 +58,10 @@ fn scheduling_with_preimages_works() { RuntimeCall::Logger(LoggerCall::log { i: 42, weight: Weight::from_ref_time(10) }); let hash = ::Hashing::hash_of(&call); let len = call.using_encoded(|x| x.len()) as u32; - let hashed = Preimage::pick(hash, len); - assert_ok!(Preimage::note_preimage(RuntimeOrigin::signed(0), call.encode())); + // Important to use here `Bounded::Lookup` to ensure that we request the hash. + let hashed = Bounded::Lookup { hash, len }; assert_ok!(Scheduler::do_schedule(DispatchTime::At(4), None, 127, root(), hashed)); + assert_ok!(Preimage::note_preimage(RuntimeOrigin::signed(0), call.encode())); assert!(Preimage::is_requested(&hash)); run_to_block(3); assert!(logger::log().is_empty()); @@ -479,8 +480,10 @@ fn scheduler_handles_periodic_unavailable_preimage() { let call = RuntimeCall::Logger(LoggerCall::log { i: 42, weight: (max_weight / 3) * 2 }); let hash = ::Hashing::hash_of(&call); let len = call.using_encoded(|x| x.len()) as u32; - let bound = Preimage::pick(hash, len); - assert_ok!(Preimage::note(call.encode().into())); + // Important to use here `Bounded::Lookup` to ensure that we request the hash. + let bound = Bounded::Lookup { hash, len }; + // The preimage isn't requested yet. + assert!(!Preimage::is_requested(&hash)); assert_ok!(Scheduler::do_schedule( DispatchTime::At(4), @@ -489,11 +492,18 @@ fn scheduler_handles_periodic_unavailable_preimage() { root(), bound.clone(), )); + + // The preimage is requested. + assert!(Preimage::is_requested(&hash)); + + // Note the preimage. + assert_ok!(Preimage::note_preimage(RuntimeOrigin::signed(1), call.encode())); + // Executes 1 times till block 4. run_to_block(4); assert_eq!(logger::log().len(), 1); - // Unnote the preimage. + // Unnote the preimage Preimage::unnote(&hash); // Does not ever execute again. @@ -1129,7 +1139,8 @@ fn postponed_named_task_cannot_be_rescheduled() { RuntimeCall::Logger(LoggerCall::log { i: 42, weight: Weight::from_ref_time(1000) }); let hash = ::Hashing::hash_of(&call); let len = call.using_encoded(|x| x.len()) as u32; - let hashed = Preimage::pick(hash, len); + // Important to use here `Bounded::Lookup` to ensure that we request the hash. + let hashed = Bounded::Lookup { hash, len }; let name: [u8; 32] = hash.as_ref().try_into().unwrap(); let address = Scheduler::do_schedule_named( diff --git a/frame/support/src/traits/preimages.rs b/frame/support/src/traits/preimages.rs index 594532ba9..ce3537c79 100644 --- a/frame/support/src/traits/preimages.rs +++ b/frame/support/src/traits/preimages.rs @@ -26,6 +26,9 @@ use sp_std::borrow::Cow; pub type Hash = H256; pub type BoundedInline = crate::BoundedVec>; +/// The maximum we expect a single legacy hash lookup to be. +const MAX_LEGACY_LEN: u32 = 1_000_000; + #[derive( Encode, Decode, MaxEncodedLen, Clone, Eq, PartialEq, scale_info::TypeInfo, RuntimeDebug, )] @@ -67,20 +70,25 @@ impl Bounded { /// Returns the hash of the preimage. /// /// The hash is re-calculated every time if the preimage is inlined. - pub fn hash(&self) -> H256 { + pub fn hash(&self) -> Hash { use Bounded::*; match self { - Legacy { hash, .. } => *hash, + Lookup { hash, .. } | Legacy { hash, .. } => *hash, Inline(x) => blake2_256(x.as_ref()).into(), - Lookup { hash, .. } => *hash, } } -} -// The maximum we expect a single legacy hash lookup to be. -const MAX_LEGACY_LEN: u32 = 1_000_000; + /// Returns the hash to lookup the preimage. + /// + /// If this is a `Bounded::Inline`, `None` is returned as no lookup is required. + pub fn lookup_hash(&self) -> Option { + use Bounded::*; + match self { + Lookup { hash, .. } | Legacy { hash, .. } => Some(*hash), + Inline(_) => None, + } + } -impl Bounded { /// Returns the length of the preimage or `None` if the length is unknown. pub fn len(&self) -> Option { match self { @@ -168,8 +176,11 @@ pub trait QueryPreimage { } } - /// Create a `Bounded` instance based on the `hash` and `len` of the encoded value. This may not - /// be `peek`-able or `realize`-able. + /// Create a `Bounded` instance based on the `hash` and `len` of the encoded value. + /// + /// It also directly requests the given `hash` using [`Self::request`]. + /// + /// This may not be `peek`-able or `realize`-able. fn pick(hash: Hash, len: u32) -> Bounded { Self::request(&hash); Bounded::Lookup { hash, len } @@ -228,10 +239,12 @@ pub trait StorePreimage: QueryPreimage { Self::unrequest(hash) } - /// Convert an otherwise unbounded or large value into a type ready for placing in storage. The - /// result is a type whose `MaxEncodedLen` is 131 bytes. + /// Convert an otherwise unbounded or large value into a type ready for placing in storage. + /// + /// The result is a type whose `MaxEncodedLen` is 131 bytes. /// /// NOTE: Once this API is used, you should use either `drop` or `realize`. + /// The value is also noted using [`Self::note`]. fn bound(t: T) -> Result, DispatchError> { let data = t.encode(); let len = data.len() as u32; From 2ced413e40eed0e294fbc4c0d539bac2376016f6 Mon Sep 17 00:00:00 2001 From: Roman Useinov Date: Thu, 9 Feb 2023 12:47:39 +0100 Subject: [PATCH 102/558] [Fix] Try-state feature-gated for BagsList (#13296) * [Fix] Try-state feature-gated for BagsList * fix comment * fix try_state remote-tests * feature-gate try-state remote test for bags-list * remove try-state from a migration * more SortedListProvider fixes * more fixes * more fixes to allow do_try_state usage in other crates * do-try-state for fuzz * more fixes * more fixes * remove feature-flag * do-try-state * fix review comments * Update frame/bags-list/src/mock.rs Co-authored-by: Anton --------- Co-authored-by: parity-processbot <> Co-authored-by: Anton --- frame/bags-list/Cargo.toml | 2 +- frame/bags-list/fuzzer/src/main.rs | 2 +- frame/bags-list/remote-tests/Cargo.toml | 2 +- frame/bags-list/remote-tests/src/try_state.rs | 5 ++-- frame/bags-list/src/lib.rs | 10 +++++++- frame/bags-list/src/list/mod.rs | 17 +++++++------ frame/bags-list/src/list/tests.rs | 25 ++++++++++--------- frame/bags-list/src/mock.rs | 2 +- frame/election-provider-support/Cargo.toml | 1 + frame/election-provider-support/src/lib.rs | 3 ++- frame/staking/Cargo.toml | 2 +- frame/staking/src/migrations.rs | 1 - frame/staking/src/pallet/impls.rs | 4 +++ 13 files changed, 46 insertions(+), 30 deletions(-) diff --git a/frame/bags-list/Cargo.toml b/frame/bags-list/Cargo.toml index e3a4965f6..379698b1d 100644 --- a/frame/bags-list/Cargo.toml +++ b/frame/bags-list/Cargo.toml @@ -75,4 +75,4 @@ fuzz = [ "sp-tracing", "frame-election-provider-support/fuzz", ] -try-runtime = [ "frame-support/try-runtime" ] +try-runtime = [ "frame-support/try-runtime", "frame-election-provider-support/try-runtime" ] diff --git a/frame/bags-list/fuzzer/src/main.rs b/frame/bags-list/fuzzer/src/main.rs index 9f7ca464c..c78e2a130 100644 --- a/frame/bags-list/fuzzer/src/main.rs +++ b/frame/bags-list/fuzzer/src/main.rs @@ -88,7 +88,7 @@ fn main() { }, } - assert!(BagsList::try_state().is_ok()); + assert!(BagsList::do_try_state().is_ok()); }) }); } diff --git a/frame/bags-list/remote-tests/Cargo.toml b/frame/bags-list/remote-tests/Cargo.toml index 9fb6d0154..6e951b43a 100644 --- a/frame/bags-list/remote-tests/Cargo.toml +++ b/frame/bags-list/remote-tests/Cargo.toml @@ -15,7 +15,7 @@ targets = ["x86_64-unknown-linux-gnu"] [dependencies] # frame pallet-staking = { path = "../../staking", version = "4.0.0-dev" } -pallet-bags-list = { path = "../../bags-list", version = "4.0.0-dev" } +pallet-bags-list = { path = "../../bags-list", version = "4.0.0-dev", features = ["fuzz"] } frame-election-provider-support = { path = "../../election-provider-support", version = "4.0.0-dev" } frame-system = { path = "../../system", version = "4.0.0-dev" } frame-support = { path = "../../support", version = "4.0.0-dev" } diff --git a/frame/bags-list/remote-tests/src/try_state.rs b/frame/bags-list/remote-tests/src/try_state.rs index 514c80d72..9ed877a43 100644 --- a/frame/bags-list/remote-tests/src/try_state.rs +++ b/frame/bags-list/remote-tests/src/try_state.rs @@ -16,7 +16,6 @@ //! Test to execute the sanity-check of the voter bag. -use frame_election_provider_support::SortedListProvider; use frame_support::{ storage::generator::StorageMap, traits::{Get, PalletInfoAccess}, @@ -51,7 +50,9 @@ pub async fn execute( ext.execute_with(|| { sp_core::crypto::set_default_ss58_version(Runtime::SS58Prefix::get().try_into().unwrap()); - pallet_bags_list::Pallet::::try_state().unwrap(); + + pallet_bags_list::Pallet::::do_try_state().unwrap(); + log::info!(target: crate::LOG_TARGET, "executed bags-list sanity check with no errors."); crate::display_and_check_bags::(currency_unit, currency_name); diff --git a/frame/bags-list/src/lib.rs b/frame/bags-list/src/lib.rs index 14f8a613e..f04c685ae 100644 --- a/frame/bags-list/src/lib.rs +++ b/frame/bags-list/src/lib.rs @@ -274,6 +274,13 @@ pub mod pallet { } } +#[cfg(any(test, feature = "try-runtime", feature = "fuzz"))] +impl, I: 'static> Pallet { + pub fn do_try_state() -> Result<(), &'static str> { + List::::do_try_state() + } +} + impl, I: 'static> Pallet { /// Move an account from one bag to another, depositing an event on success. /// @@ -348,8 +355,9 @@ impl, I: 'static> SortedListProvider for Pallet List::::unsafe_regenerate(all, score_of) } + #[cfg(feature = "try-runtime")] fn try_state() -> Result<(), &'static str> { - List::::try_state() + Self::do_try_state() } fn unsafe_clear() { diff --git a/frame/bags-list/src/list/mod.rs b/frame/bags-list/src/list/mod.rs index 272526ad1..4082a5324 100644 --- a/frame/bags-list/src/list/mod.rs +++ b/frame/bags-list/src/list/mod.rs @@ -220,9 +220,6 @@ impl, I: 'static> List { crate::ListBags::::remove(removed_bag); } - #[cfg(feature = "std")] - debug_assert_eq!(Self::try_state(), Ok(())); - num_affected } @@ -514,7 +511,8 @@ impl, I: 'static> List { /// * length of this list is in sync with `ListNodes::count()`, /// * and sanity-checks all bags and nodes. This will cascade down all the checks and makes sure /// all bags and nodes are checked per *any* update to `List`. - pub(crate) fn try_state() -> Result<(), &'static str> { + #[cfg(any(test, feature = "try-runtime", feature = "fuzz"))] + pub(crate) fn do_try_state() -> Result<(), &'static str> { let mut seen_in_list = BTreeSet::new(); ensure!( Self::iter().map(|node| node.id).all(|id| seen_in_list.insert(id)), @@ -542,7 +540,7 @@ impl, I: 'static> List { thresholds.into_iter().filter_map(|t| Bag::::get(t)) }; - let _ = active_bags.clone().try_for_each(|b| b.try_state())?; + let _ = active_bags.clone().try_for_each(|b| b.do_try_state())?; let nodes_in_bags_count = active_bags.clone().fold(0u32, |acc, cur| acc + cur.iter().count() as u32); @@ -553,7 +551,7 @@ impl, I: 'static> List { // check that all nodes are sane. We check the `ListNodes` storage item directly in case we // have some "stale" nodes that are not in a bag. for (_id, node) in crate::ListNodes::::iter() { - node.try_state()? + node.do_try_state()? } Ok(()) @@ -751,7 +749,8 @@ impl, I: 'static> Bag { /// * Ensures head has no prev. /// * Ensures tail has no next. /// * Ensures there are no loops, traversal from head to tail is correct. - fn try_state(&self) -> Result<(), &'static str> { + #[cfg(any(test, feature = "try-runtime", feature = "fuzz"))] + fn do_try_state(&self) -> Result<(), &'static str> { frame_support::ensure!( self.head() .map(|head| head.prev().is_none()) @@ -790,6 +789,7 @@ impl, I: 'static> Bag { } /// Check if the bag contains a node with `id`. + #[cfg(any(test, feature = "try-runtime", feature = "fuzz"))] fn contains(&self, id: &T::AccountId) -> bool { self.iter().any(|n| n.id() == id) } @@ -894,7 +894,8 @@ impl, I: 'static> Node { self.bag_upper } - fn try_state(&self) -> Result<(), &'static str> { + #[cfg(any(test, feature = "try-runtime", feature = "fuzz"))] + fn do_try_state(&self) -> Result<(), &'static str> { let expected_bag = Bag::::get(self.bag_upper).ok_or("bag not found for node")?; let id = self.id(); diff --git a/frame/bags-list/src/list/tests.rs b/frame/bags-list/src/list/tests.rs index 966ea1a74..3c4aa7c86 100644 --- a/frame/bags-list/src/list/tests.rs +++ b/frame/bags-list/src/list/tests.rs @@ -137,6 +137,7 @@ fn migrate_works() { BagThresholds::set(NEW_THRESHOLDS); // and we call List::::migrate(old_thresholds); + assert_eq!(List::::do_try_state(), Ok(())); // then assert_eq!( @@ -352,13 +353,13 @@ mod list { #[test] fn try_state_works() { ExtBuilder::default().build_and_execute_no_post_check(|| { - assert_ok!(List::::try_state()); + assert_ok!(List::::do_try_state()); }); // make sure there are no duplicates. ExtBuilder::default().build_and_execute_no_post_check(|| { Bag::::get(10).unwrap().insert_unchecked(2, 10); - assert_eq!(List::::try_state(), Err("duplicate identified")); + assert_eq!(List::::do_try_state(), Err("duplicate identified")); }); // ensure count is in sync with `ListNodes::count()`. @@ -372,7 +373,7 @@ mod list { CounterForListNodes::::mutate(|counter| *counter += 1); assert_eq!(crate::ListNodes::::count(), 5); - assert_eq!(List::::try_state(), Err("iter_count != stored_count")); + assert_eq!(List::::do_try_state(), Err("iter_count != stored_count")); }); } @@ -804,7 +805,7 @@ mod bags { // then assert_eq!(bag_as_ids(&bag_1000), vec![2, 3, 13, 14]); - assert_ok!(bag_1000.try_state()); + assert_ok!(bag_1000.do_try_state()); // and the node isn't mutated when its removed assert_eq!(node_4, node_4_pre_remove); @@ -814,7 +815,7 @@ mod bags { // then assert_eq!(bag_as_ids(&bag_1000), vec![3, 13, 14]); - assert_ok!(bag_1000.try_state()); + assert_ok!(bag_1000.do_try_state()); // when removing a tail that is not pointing at the head let node_14 = Node::::get(&14).unwrap(); @@ -822,7 +823,7 @@ mod bags { // then assert_eq!(bag_as_ids(&bag_1000), vec![3, 13]); - assert_ok!(bag_1000.try_state()); + assert_ok!(bag_1000.do_try_state()); // when removing a tail that is pointing at the head let node_13 = Node::::get(&13).unwrap(); @@ -830,7 +831,7 @@ mod bags { // then assert_eq!(bag_as_ids(&bag_1000), vec![3]); - assert_ok!(bag_1000.try_state()); + assert_ok!(bag_1000.do_try_state()); // when removing a node that is both the head & tail let node_3 = Node::::get(&3).unwrap(); @@ -846,7 +847,7 @@ mod bags { // then assert_eq!(bag_as_ids(&bag_10), vec![1, 12]); - assert_ok!(bag_10.try_state()); + assert_ok!(bag_10.do_try_state()); // when removing a head that is pointing at the tail let node_1 = Node::::get(&1).unwrap(); @@ -854,7 +855,7 @@ mod bags { // then assert_eq!(bag_as_ids(&bag_10), vec![12]); - assert_ok!(bag_10.try_state()); + assert_ok!(bag_10.do_try_state()); // and since we updated the bag's head/tail, we need to write this storage so we // can correctly `get` it again in later checks bag_10.put(); @@ -865,7 +866,7 @@ mod bags { // then assert_eq!(bag_as_ids(&bag_2000), vec![15, 17, 18, 19]); - assert_ok!(bag_2000.try_state()); + assert_ok!(bag_2000.do_try_state()); // when removing a node that is pointing at tail, but not head let node_18 = Node::::get(&18).unwrap(); @@ -873,7 +874,7 @@ mod bags { // then assert_eq!(bag_as_ids(&bag_2000), vec![15, 17, 19]); - assert_ok!(bag_2000.try_state()); + assert_ok!(bag_2000.do_try_state()); // finally, when reading from storage, the state of all bags is as expected assert_eq!( @@ -905,7 +906,7 @@ mod bags { // .. and the bag it was removed from let bag_1000 = Bag::::get(1_000).unwrap(); // is sane - assert_ok!(bag_1000.try_state()); + assert_ok!(bag_1000.do_try_state()); // and has the correct head and tail. assert_eq!(bag_1000.head, Some(3)); assert_eq!(bag_1000.tail, Some(4)); diff --git a/frame/bags-list/src/mock.rs b/frame/bags-list/src/mock.rs index 8cc96a988..32f6bb09e 100644 --- a/frame/bags-list/src/mock.rs +++ b/frame/bags-list/src/mock.rs @@ -148,7 +148,7 @@ impl ExtBuilder { pub fn build_and_execute(self, test: impl FnOnce() -> ()) { self.build().execute_with(|| { test(); - List::::try_state().expect("Try-state post condition failed") + List::::do_try_state().expect("do_try_state post condition failed") }) } diff --git a/frame/election-provider-support/Cargo.toml b/frame/election-provider-support/Cargo.toml index c7f47e721..114caee79 100644 --- a/frame/election-provider-support/Cargo.toml +++ b/frame/election-provider-support/Cargo.toml @@ -43,3 +43,4 @@ std = [ "sp-std/std", ] runtime-benchmarks = [] +try-runtime = [] diff --git a/frame/election-provider-support/src/lib.rs b/frame/election-provider-support/src/lib.rs index 9d5d6c018..9e60eb3be 100644 --- a/frame/election-provider-support/src/lib.rs +++ b/frame/election-provider-support/src/lib.rs @@ -562,7 +562,8 @@ pub trait SortedListProvider { /// unbounded amount of storage accesses. fn unsafe_clear(); - /// Check internal state of list. Only meant for debugging. + /// Check internal state of the list. Only meant for debugging. + #[cfg(feature = "try-runtime")] fn try_state() -> Result<(), &'static str>; /// If `who` changes by the returned amount they are guaranteed to have a worst case change diff --git a/frame/staking/Cargo.toml b/frame/staking/Cargo.toml index 3d5cf1161..79c0bb5c2 100644 --- a/frame/staking/Cargo.toml +++ b/frame/staking/Cargo.toml @@ -74,4 +74,4 @@ runtime-benchmarks = [ "rand_chacha", "sp-staking/runtime-benchmarks", ] -try-runtime = ["frame-support/try-runtime"] +try-runtime = ["frame-support/try-runtime", "frame-election-provider-support/try-runtime"] diff --git a/frame/staking/src/migrations.rs b/frame/staking/src/migrations.rs index 6253c3fee..a8d360c09 100644 --- a/frame/staking/src/migrations.rs +++ b/frame/staking/src/migrations.rs @@ -386,7 +386,6 @@ pub mod v8 { Nominators::::iter().map(|(id, _)| id), Pallet::::weight_of_fn(), ); - debug_assert_eq!(T::VoterList::try_state(), Ok(())); StorageVersion::::put(ObsoleteReleases::V8_0_0); crate::log!( diff --git a/frame/staking/src/pallet/impls.rs b/frame/staking/src/pallet/impls.rs index e59c2c5a0..6a35e2b86 100644 --- a/frame/staking/src/pallet/impls.rs +++ b/frame/staking/src/pallet/impls.rs @@ -1451,9 +1451,11 @@ impl SortedListProvider for UseValidatorsMap { // nothing to do upon regenerate. 0 } + #[cfg(feature = "try-runtime")] fn try_state() -> Result<(), &'static str> { Ok(()) } + fn unsafe_clear() { #[allow(deprecated)] Validators::::remove_all(); @@ -1525,6 +1527,8 @@ impl SortedListProvider for UseNominatorsAndValidatorsM // nothing to do upon regenerate. 0 } + + #[cfg(feature = "try-runtime")] fn try_state() -> Result<(), &'static str> { Ok(()) } From 21d58f6f7b3ed2ea529f3c118b2e042b29551672 Mon Sep 17 00:00:00 2001 From: Javier Viola Date: Fri, 10 Feb 2023 11:11:10 -0300 Subject: [PATCH 103/558] bump version of zombienet and update snaps links (#13359) --- .gitlab-ci.yml | 2 +- zombienet/0001-basic-warp-sync/test-warp-sync.toml | 6 +++--- .../test-validators-warp-sync.toml | 6 +++--- .../test-block-building-warp-sync.toml | 6 +++--- 4 files changed, 10 insertions(+), 10 deletions(-) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index ae25a2c59..ab3fd84d6 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -55,7 +55,7 @@ variables: RUSTY_CACHIER_COMPRESSION_METHOD: zstd NEXTEST_FAILURE_OUTPUT: immediate-final NEXTEST_SUCCESS_OUTPUT: final - ZOMBIENET_IMAGE: "docker.io/paritytech/zombienet:v1.3.22" + ZOMBIENET_IMAGE: "docker.io/paritytech/zombienet:v1.3.34" .shared-default: &shared-default retry: diff --git a/zombienet/0001-basic-warp-sync/test-warp-sync.toml b/zombienet/0001-basic-warp-sync/test-warp-sync.toml index ae2810b6e..272b5862e 100644 --- a/zombienet/0001-basic-warp-sync/test-warp-sync.toml +++ b/zombienet/0001-basic-warp-sync/test-warp-sync.toml @@ -11,18 +11,18 @@ chain_spec_path = "zombienet/0001-basic-warp-sync/chain-spec.json" [[relaychain.nodes]] name = "alice" validator = false - db_snapshot="https://storage.googleapis.com/zombienet-db-snaps/substrate/0001-basic-warp-sync/chains-0bb3f0be2ce41b5615b224215bcc8363aa0416a6.tgz" + db_snapshot="https://storage.googleapis.com/zombienet-db-snaps/substrate/0001-basic-warp-sync/chains-a233bbacff650aac6bcb56b4e4ef7db7dc143cfb.tgz" [[relaychain.nodes]] name = "bob" validator = false - db_snapshot="https://storage.googleapis.com/zombienet-db-snaps/substrate/0001-basic-warp-sync/chains-0bb3f0be2ce41b5615b224215bcc8363aa0416a6.tgz" + db_snapshot="https://storage.googleapis.com/zombienet-db-snaps/substrate/0001-basic-warp-sync/chains-a233bbacff650aac6bcb56b4e4ef7db7dc143cfb.tgz" #we need at least 3 nodes for warp sync [[relaychain.nodes]] name = "charlie" validator = false - db_snapshot="https://storage.googleapis.com/zombienet-db-snaps/substrate/0001-basic-warp-sync/chains-0bb3f0be2ce41b5615b224215bcc8363aa0416a6.tgz" + db_snapshot="https://storage.googleapis.com/zombienet-db-snaps/substrate/0001-basic-warp-sync/chains-a233bbacff650aac6bcb56b4e4ef7db7dc143cfb.tgz" [[relaychain.nodes]] name = "dave" diff --git a/zombienet/0002-validators-warp-sync/test-validators-warp-sync.toml b/zombienet/0002-validators-warp-sync/test-validators-warp-sync.toml index dae6e406f..df4414f5c 100644 --- a/zombienet/0002-validators-warp-sync/test-validators-warp-sync.toml +++ b/zombienet/0002-validators-warp-sync/test-validators-warp-sync.toml @@ -22,14 +22,14 @@ chain_spec_path = "zombienet/0002-validators-warp-sync/chain-spec.json" [[relaychain.nodes]] name = "charlie" validator = false - db_snapshot="https://storage.googleapis.com/zombienet-db-snaps/substrate/0001-basic-warp-sync/chains-0bb3f0be2ce41b5615b224215bcc8363aa0416a6.tgz" + db_snapshot="https://storage.googleapis.com/zombienet-db-snaps/substrate/0001-basic-warp-sync/chains-a233bbacff650aac6bcb56b4e4ef7db7dc143cfb.tgz" [[relaychain.nodes]] name = "dave" validator = false - db_snapshot="https://storage.googleapis.com/zombienet-db-snaps/substrate/0001-basic-warp-sync/chains-0bb3f0be2ce41b5615b224215bcc8363aa0416a6.tgz" + db_snapshot="https://storage.googleapis.com/zombienet-db-snaps/substrate/0001-basic-warp-sync/chains-a233bbacff650aac6bcb56b4e4ef7db7dc143cfb.tgz" [[relaychain.nodes]] name = "eve" validator = false - db_snapshot="https://storage.googleapis.com/zombienet-db-snaps/substrate/0001-basic-warp-sync/chains-0bb3f0be2ce41b5615b224215bcc8363aa0416a6.tgz" + db_snapshot="https://storage.googleapis.com/zombienet-db-snaps/substrate/0001-basic-warp-sync/chains-a233bbacff650aac6bcb56b4e4ef7db7dc143cfb.tgz" diff --git a/zombienet/0003-block-building-warp-sync/test-block-building-warp-sync.toml b/zombienet/0003-block-building-warp-sync/test-block-building-warp-sync.toml index 66176845c..5119c919c 100644 --- a/zombienet/0003-block-building-warp-sync/test-block-building-warp-sync.toml +++ b/zombienet/0003-block-building-warp-sync/test-block-building-warp-sync.toml @@ -12,17 +12,17 @@ chain_spec_path = "zombienet/0003-block-building-warp-sync/chain-spec.json" [[relaychain.nodes]] name = "alice" validator = true - db_snapshot="https://storage.googleapis.com/zombienet-db-snaps/substrate/0001-basic-warp-sync/chains-0bb3f0be2ce41b5615b224215bcc8363aa0416a6.tgz" + db_snapshot="https://storage.googleapis.com/zombienet-db-snaps/substrate/0001-basic-warp-sync/chains-a233bbacff650aac6bcb56b4e4ef7db7dc143cfb.tgz" [[relaychain.nodes]] name = "bob" validator = true - db_snapshot="https://storage.googleapis.com/zombienet-db-snaps/substrate/0001-basic-warp-sync/chains-0bb3f0be2ce41b5615b224215bcc8363aa0416a6.tgz" + db_snapshot="https://storage.googleapis.com/zombienet-db-snaps/substrate/0001-basic-warp-sync/chains-a233bbacff650aac6bcb56b4e4ef7db7dc143cfb.tgz" [[relaychain.nodes]] name = "charlie" validator = false - db_snapshot="https://storage.googleapis.com/zombienet-db-snaps/substrate/0001-basic-warp-sync/chains-0bb3f0be2ce41b5615b224215bcc8363aa0416a6.tgz" + db_snapshot="https://storage.googleapis.com/zombienet-db-snaps/substrate/0001-basic-warp-sync/chains-a233bbacff650aac6bcb56b4e4ef7db7dc143cfb.tgz" [[relaychain.nodes]] name = "dave" From ccd768ffc2b0f0282e1687794451841dcffda785 Mon Sep 17 00:00:00 2001 From: Davide Galassi Date: Sat, 11 Feb 2023 18:35:04 +0100 Subject: [PATCH 104/558] Fix longest chain finalization target lookup (#13289) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Finalization target should be chosed as some ancestor of SelectChain::best_chain * More test assertions * Improve docs * Removed stale docs * Rename 'target' to 'base' in lookup method * Fix typo * Apply suggestions from code review Co-authored-by: André Silva <123550+andresilva@users.noreply.github.com> * Rename 'target_hash' to 'base_hash' in 'SelectChain::finality_target()' * Apply suggestions from code review Co-authored-by: Anton * Docs improvement * Doc fix * Apply suggestions from code review Co-authored-by: Bastian Köcher * Apply more code suggestions --------- Co-authored-by: André Silva <123550+andresilva@users.noreply.github.com> Co-authored-by: Anton Co-authored-by: Bastian Köcher --- client/consensus/common/src/longest_chain.rs | 80 ++++++- client/finality-grandpa/src/tests.rs | 2 +- client/service/test/src/client/mod.rs | 217 ++++++++++++------ primitives/blockchain/src/backend.rs | 84 ++----- .../consensus/common/src/select_chain.rs | 8 +- 5 files changed, 242 insertions(+), 149 deletions(-) diff --git a/client/consensus/common/src/longest_chain.rs b/client/consensus/common/src/longest_chain.rs index fab2c3a4c..ece248620 100644 --- a/client/consensus/common/src/longest_chain.rs +++ b/client/consensus/common/src/longest_chain.rs @@ -21,7 +21,7 @@ use sc_client_api::backend; use sp_blockchain::{Backend, HeaderBackend}; use sp_consensus::{Error as ConsensusError, SelectChain}; -use sp_runtime::traits::{Block as BlockT, NumberFor}; +use sp_runtime::traits::{Block as BlockT, Header, NumberFor}; use std::{marker::PhantomData, sync::Arc}; /// Implement Longest Chain Select implementation @@ -48,15 +48,19 @@ where LongestChain { backend, _phantom: Default::default() } } - fn best_block_header(&self) -> sp_blockchain::Result<::Header> { + fn best_hash(&self) -> sp_blockchain::Result<::Hash> { let info = self.backend.blockchain().info(); let import_lock = self.backend.get_import_lock(); let best_hash = self .backend .blockchain() - .best_containing(info.best_hash, None, import_lock)? + .longest_containing(info.best_hash, import_lock)? .unwrap_or(info.best_hash); + Ok(best_hash) + } + fn best_header(&self) -> sp_blockchain::Result<::Header> { + let best_hash = self.best_hash()?; Ok(self .backend .blockchain() @@ -64,6 +68,65 @@ where .expect("given block hash was fetched from block in db; qed")) } + /// Returns the highest descendant of the given block that is a valid + /// candidate to be finalized. + /// + /// In this context, being a valid target means being an ancestor of + /// the best chain according to the `best_header` method. + /// + /// If `maybe_max_number` is `Some(max_block_number)` the search is + /// limited to block `number <= max_block_number`. In other words + /// as if there were no blocks greater than `max_block_number`. + fn finality_target( + &self, + base_hash: Block::Hash, + maybe_max_number: Option>, + ) -> sp_blockchain::Result { + use sp_blockchain::Error::{Application, MissingHeader}; + let blockchain = self.backend.blockchain(); + + let mut current_head = self.best_header()?; + let mut best_hash = current_head.hash(); + + let base_header = blockchain + .header(base_hash)? + .ok_or_else(|| MissingHeader(base_hash.to_string()))?; + let base_number = *base_header.number(); + + if let Some(max_number) = maybe_max_number { + if max_number < base_number { + let msg = format!( + "Requested a finality target using max number {} below the base number {}", + max_number, base_number + ); + return Err(Application(msg.into())) + } + + while current_head.number() > &max_number { + best_hash = *current_head.parent_hash(); + current_head = blockchain + .header(best_hash)? + .ok_or_else(|| MissingHeader(format!("{best_hash:?}")))?; + } + } + + while current_head.hash() != base_hash { + if *current_head.number() < base_number { + let msg = format!( + "Requested a finality target using a base {:?} not in the best chain {:?}", + base_hash, best_hash, + ); + return Err(Application(msg.into())) + } + let current_hash = *current_head.parent_hash(); + current_head = blockchain + .header(current_hash)? + .ok_or_else(|| MissingHeader(format!("{best_hash:?}")))?; + } + + Ok(best_hash) + } + fn leaves(&self) -> Result::Hash>, sp_blockchain::Error> { self.backend.blockchain().leaves() } @@ -80,20 +143,15 @@ where } async fn best_chain(&self) -> Result<::Header, ConsensusError> { - LongestChain::best_block_header(self) - .map_err(|e| ConsensusError::ChainLookup(e.to_string())) + LongestChain::best_header(self).map_err(|e| ConsensusError::ChainLookup(e.to_string())) } async fn finality_target( &self, - target_hash: Block::Hash, + base_hash: Block::Hash, maybe_max_number: Option>, ) -> Result { - let import_lock = self.backend.get_import_lock(); - self.backend - .blockchain() - .best_containing(target_hash, maybe_max_number, import_lock) - .map(|maybe_hash| maybe_hash.unwrap_or(target_hash)) + LongestChain::finality_target(self, base_hash, maybe_max_number) .map_err(|e| ConsensusError::ChainLookup(e.to_string())) } } diff --git a/client/finality-grandpa/src/tests.rs b/client/finality-grandpa/src/tests.rs index 07ea46491..62ef4709e 100644 --- a/client/finality-grandpa/src/tests.rs +++ b/client/finality-grandpa/src/tests.rs @@ -237,7 +237,7 @@ impl SelectChain for MockSelectChain { async fn finality_target( &self, - _target_hash: Hash, + _base_hash: Hash, _maybe_max_number: Option>, ) -> Result { Ok(self.finality_target.lock().take().unwrap()) diff --git a/client/service/test/src/client/mod.rs b/client/service/test/src/client/mod.rs index 97c22a1cb..12b92afc4 100644 --- a/client/service/test/src/client/mod.rs +++ b/client/service/test/src/client/mod.rs @@ -581,7 +581,7 @@ fn uncles_with_multiple_forks() { } #[test] -fn best_containing_on_longest_chain_with_single_chain_3_blocks() { +fn finality_target_on_longest_chain_with_single_chain_3_blocks() { // block tree: // G -> A1 -> A2 @@ -606,7 +606,7 @@ fn best_containing_on_longest_chain_with_single_chain_3_blocks() { } #[test] -fn best_containing_on_longest_chain_with_multiple_forks() { +fn finality_target_on_longest_chain_with_multiple_forks() { // block tree: // G -> A1 -> A2 -> A3 -> A4 -> A5 // A1 -> B2 -> B3 -> B4 @@ -731,8 +731,13 @@ fn best_containing_on_longest_chain_with_multiple_forks() { assert!(leaves.contains(&d2.hash())); assert_eq!(leaves.len(), 4); - let finality_target = |target_hash, number| { - block_on(longest_chain_select.finality_target(target_hash, number)).unwrap() + // On error we return a quite improbable hash + let error_hash = Hash::from([0xff; 32]); + let finality_target = |target_hash, number| match block_on( + longest_chain_select.finality_target(target_hash, number), + ) { + Ok(hash) => hash, + Err(_) => error_hash, }; // search without restriction @@ -742,11 +747,11 @@ fn best_containing_on_longest_chain_with_multiple_forks() { assert_eq!(a5.hash(), finality_target(a3.hash(), None)); assert_eq!(a5.hash(), finality_target(a4.hash(), None)); assert_eq!(a5.hash(), finality_target(a5.hash(), None)); - assert_eq!(b4.hash(), finality_target(b2.hash(), None)); - assert_eq!(b4.hash(), finality_target(b3.hash(), None)); - assert_eq!(b4.hash(), finality_target(b4.hash(), None)); - assert_eq!(c3.hash(), finality_target(c3.hash(), None)); - assert_eq!(d2.hash(), finality_target(d2.hash(), None)); + assert_eq!(error_hash, finality_target(b2.hash(), None)); + assert_eq!(error_hash, finality_target(b3.hash(), None)); + assert_eq!(error_hash, finality_target(b4.hash(), None)); + assert_eq!(error_hash, finality_target(c3.hash(), None)); + assert_eq!(error_hash, finality_target(d2.hash(), None)); // search only blocks with number <= 5. equivalent to without restriction for this scenario assert_eq!(a5.hash(), finality_target(genesis_hash, Some(5))); @@ -755,11 +760,11 @@ fn best_containing_on_longest_chain_with_multiple_forks() { assert_eq!(a5.hash(), finality_target(a3.hash(), Some(5))); assert_eq!(a5.hash(), finality_target(a4.hash(), Some(5))); assert_eq!(a5.hash(), finality_target(a5.hash(), Some(5))); - assert_eq!(b4.hash(), finality_target(b2.hash(), Some(5))); - assert_eq!(b4.hash(), finality_target(b3.hash(), Some(5))); - assert_eq!(b4.hash(), finality_target(b4.hash(), Some(5))); - assert_eq!(c3.hash(), finality_target(c3.hash(), Some(5))); - assert_eq!(d2.hash(), finality_target(d2.hash(), Some(5))); + assert_eq!(error_hash, finality_target(b2.hash(), Some(5))); + assert_eq!(error_hash, finality_target(b3.hash(), Some(5))); + assert_eq!(error_hash, finality_target(b4.hash(), Some(5))); + assert_eq!(error_hash, finality_target(c3.hash(), Some(5))); + assert_eq!(error_hash, finality_target(d2.hash(), Some(5))); // search only blocks with number <= 4 assert_eq!(a4.hash(), finality_target(genesis_hash, Some(4))); @@ -767,73 +772,72 @@ fn best_containing_on_longest_chain_with_multiple_forks() { assert_eq!(a4.hash(), finality_target(a2.hash(), Some(4))); assert_eq!(a4.hash(), finality_target(a3.hash(), Some(4))); assert_eq!(a4.hash(), finality_target(a4.hash(), Some(4))); - assert_eq!(a5.hash(), finality_target(a5.hash(), Some(4))); - assert_eq!(b4.hash(), finality_target(b2.hash(), Some(4))); - assert_eq!(b4.hash(), finality_target(b3.hash(), Some(4))); - assert_eq!(b4.hash(), finality_target(b4.hash(), Some(4))); - assert_eq!(c3.hash(), finality_target(c3.hash(), Some(4))); - assert_eq!(d2.hash(), finality_target(d2.hash(), Some(4))); + assert_eq!(error_hash, finality_target(a5.hash(), Some(4))); + assert_eq!(error_hash, finality_target(b2.hash(), Some(4))); + assert_eq!(error_hash, finality_target(b3.hash(), Some(4))); + assert_eq!(error_hash, finality_target(b4.hash(), Some(4))); + assert_eq!(error_hash, finality_target(c3.hash(), Some(4))); + assert_eq!(error_hash, finality_target(d2.hash(), Some(4))); // search only blocks with number <= 3 assert_eq!(a3.hash(), finality_target(genesis_hash, Some(3))); assert_eq!(a3.hash(), finality_target(a1.hash(), Some(3))); assert_eq!(a3.hash(), finality_target(a2.hash(), Some(3))); assert_eq!(a3.hash(), finality_target(a3.hash(), Some(3))); - assert_eq!(a4.hash(), finality_target(a4.hash(), Some(3))); - assert_eq!(a5.hash(), finality_target(a5.hash(), Some(3))); - assert_eq!(b3.hash(), finality_target(b2.hash(), Some(3))); - assert_eq!(b3.hash(), finality_target(b3.hash(), Some(3))); - assert_eq!(b4.hash(), finality_target(b4.hash(), Some(3))); - assert_eq!(c3.hash(), finality_target(c3.hash(), Some(3))); - assert_eq!(d2.hash(), finality_target(d2.hash(), Some(3))); + assert_eq!(error_hash, finality_target(a4.hash(), Some(3))); + assert_eq!(error_hash, finality_target(a5.hash(), Some(3))); + assert_eq!(error_hash, finality_target(b2.hash(), Some(3))); + assert_eq!(error_hash, finality_target(b3.hash(), Some(3))); + assert_eq!(error_hash, finality_target(b4.hash(), Some(3))); + assert_eq!(error_hash, finality_target(c3.hash(), Some(3))); + assert_eq!(error_hash, finality_target(d2.hash(), Some(3))); // search only blocks with number <= 2 assert_eq!(a2.hash(), finality_target(genesis_hash, Some(2))); assert_eq!(a2.hash(), finality_target(a1.hash(), Some(2))); assert_eq!(a2.hash(), finality_target(a2.hash(), Some(2))); - assert_eq!(a3.hash(), finality_target(a3.hash(), Some(2))); - assert_eq!(a4.hash(), finality_target(a4.hash(), Some(2))); - assert_eq!(a5.hash(), finality_target(a5.hash(), Some(2))); - assert_eq!(b2.hash(), finality_target(b2.hash(), Some(2))); - assert_eq!(b3.hash(), finality_target(b3.hash(), Some(2))); - assert_eq!(b4.hash(), finality_target(b4.hash(), Some(2))); - assert_eq!(c3.hash(), finality_target(c3.hash(), Some(2))); - assert_eq!(d2.hash(), finality_target(d2.hash(), Some(2))); + assert_eq!(error_hash, finality_target(a3.hash(), Some(2))); + assert_eq!(error_hash, finality_target(a4.hash(), Some(2))); + assert_eq!(error_hash, finality_target(a5.hash(), Some(2))); + assert_eq!(error_hash, finality_target(b2.hash(), Some(2))); + assert_eq!(error_hash, finality_target(b3.hash(), Some(2))); + assert_eq!(error_hash, finality_target(b4.hash(), Some(2))); + assert_eq!(error_hash, finality_target(c3.hash(), Some(2))); + assert_eq!(error_hash, finality_target(d2.hash(), Some(2))); // search only blocks with number <= 1 assert_eq!(a1.hash(), finality_target(genesis_hash, Some(1))); assert_eq!(a1.hash(), finality_target(a1.hash(), Some(1))); - assert_eq!(a2.hash(), finality_target(a2.hash(), Some(1))); - assert_eq!(a3.hash(), finality_target(a3.hash(), Some(1))); - assert_eq!(a4.hash(), finality_target(a4.hash(), Some(1))); - assert_eq!(a5.hash(), finality_target(a5.hash(), Some(1))); - - assert_eq!(b2.hash(), finality_target(b2.hash(), Some(1))); - assert_eq!(b3.hash(), finality_target(b3.hash(), Some(1))); - assert_eq!(b4.hash(), finality_target(b4.hash(), Some(1))); - assert_eq!(c3.hash(), finality_target(c3.hash(), Some(1))); - assert_eq!(d2.hash(), finality_target(d2.hash(), Some(1))); + assert_eq!(error_hash, finality_target(a2.hash(), Some(1))); + assert_eq!(error_hash, finality_target(a3.hash(), Some(1))); + assert_eq!(error_hash, finality_target(a4.hash(), Some(1))); + assert_eq!(error_hash, finality_target(a5.hash(), Some(1))); + assert_eq!(error_hash, finality_target(b2.hash(), Some(1))); + assert_eq!(error_hash, finality_target(b3.hash(), Some(1))); + assert_eq!(error_hash, finality_target(b4.hash(), Some(1))); + assert_eq!(error_hash, finality_target(c3.hash(), Some(1))); + assert_eq!(error_hash, finality_target(d2.hash(), Some(1))); // search only blocks with number <= 0 assert_eq!(genesis_hash, finality_target(genesis_hash, Some(0))); - assert_eq!(a1.hash(), finality_target(a1.hash(), Some(0))); - assert_eq!(a2.hash(), finality_target(a2.hash(), Some(0))); - assert_eq!(a3.hash(), finality_target(a3.hash(), Some(0))); - assert_eq!(a4.hash(), finality_target(a4.hash(), Some(0))); - assert_eq!(a5.hash(), finality_target(a5.hash(), Some(0))); - assert_eq!(b2.hash(), finality_target(b2.hash(), Some(0))); - assert_eq!(b3.hash(), finality_target(b3.hash(), Some(0))); - assert_eq!(b4.hash(), finality_target(b4.hash(), Some(0))); - assert_eq!(c3.hash(), finality_target(c3.hash(), Some(0))); - assert_eq!(d2.hash(), finality_target(d2.hash(), Some(0))); + assert_eq!(error_hash, finality_target(a1.hash(), Some(0))); + assert_eq!(error_hash, finality_target(a2.hash(), Some(0))); + assert_eq!(error_hash, finality_target(a3.hash(), Some(0))); + assert_eq!(error_hash, finality_target(a4.hash(), Some(0))); + assert_eq!(error_hash, finality_target(a5.hash(), Some(0))); + assert_eq!(error_hash, finality_target(b2.hash(), Some(0))); + assert_eq!(error_hash, finality_target(b3.hash(), Some(0))); + assert_eq!(error_hash, finality_target(b4.hash(), Some(0))); + assert_eq!(error_hash, finality_target(c3.hash(), Some(0))); + assert_eq!(error_hash, finality_target(d2.hash(), Some(0))); } #[test] -fn best_containing_on_longest_chain_with_max_depth_higher_than_best() { +fn finality_target_on_longest_chain_with_max_depth_higher_than_best() { // block tree: // G -> A1 -> A2 - let (mut client, longest_chain_select) = TestClientBuilder::new().build_with_longest_chain(); + let (mut client, chain_select) = TestClientBuilder::new().build_with_longest_chain(); // G -> A1 let a1 = client.new_block(Default::default()).unwrap().build().unwrap().block; @@ -845,10 +849,93 @@ fn best_containing_on_longest_chain_with_max_depth_higher_than_best() { let genesis_hash = client.chain_info().genesis_hash; - assert_eq!( - a2.hash(), - block_on(longest_chain_select.finality_target(genesis_hash, Some(10))).unwrap(), - ); + assert_eq!(a2.hash(), block_on(chain_select.finality_target(genesis_hash, Some(10))).unwrap(),); +} + +#[test] +fn finality_target_with_best_not_on_longest_chain() { + // block tree: + // G -> A1 -> A2 -> A3 -> A4 -> A5 + // -> B2 -> (B3) -> B4 + // ^best + + let (mut client, chain_select) = TestClientBuilder::new().build_with_longest_chain(); + let genesis_hash = client.chain_info().genesis_hash; + + // G -> A1 + let a1 = client.new_block(Default::default()).unwrap().build().unwrap().block; + block_on(client.import(BlockOrigin::Own, a1.clone())).unwrap(); + + // A1 -> A2 + let a2 = client.new_block(Default::default()).unwrap().build().unwrap().block; + block_on(client.import(BlockOrigin::Own, a2.clone())).unwrap(); + + // A2 -> A3 + let a3 = client.new_block(Default::default()).unwrap().build().unwrap().block; + block_on(client.import(BlockOrigin::Own, a3.clone())).unwrap(); + + // A3 -> A4 + let a4 = client.new_block(Default::default()).unwrap().build().unwrap().block; + block_on(client.import(BlockOrigin::Own, a4.clone())).unwrap(); + + // A3 -> A5 + let a5 = client.new_block(Default::default()).unwrap().build().unwrap().block; + block_on(client.import(BlockOrigin::Own, a5.clone())).unwrap(); + + // A1 -> B2 + let mut builder = client + .new_block_at(&BlockId::Hash(a1.hash()), Default::default(), false) + .unwrap(); + // this push is required as otherwise B2 has the same hash as A2 and won't get imported + builder + .push_transfer(Transfer { + from: AccountKeyring::Alice.into(), + to: AccountKeyring::Ferdie.into(), + amount: 41, + nonce: 0, + }) + .unwrap(); + let b2 = builder.build().unwrap().block; + block_on(client.import(BlockOrigin::Own, b2.clone())).unwrap(); + + assert_eq!(a5.hash(), block_on(chain_select.finality_target(genesis_hash, None)).unwrap()); + assert_eq!(a5.hash(), block_on(chain_select.finality_target(a1.hash(), None)).unwrap()); + assert_eq!(a5.hash(), block_on(chain_select.finality_target(a2.hash(), None)).unwrap()); + assert_eq!(a5.hash(), block_on(chain_select.finality_target(a3.hash(), None)).unwrap()); + assert_eq!(a5.hash(), block_on(chain_select.finality_target(a4.hash(), None)).unwrap()); + assert_eq!(a5.hash(), block_on(chain_select.finality_target(a5.hash(), None)).unwrap()); + + // B2 -> B3 + let b3 = client + .new_block_at(&BlockId::Hash(b2.hash()), Default::default(), false) + .unwrap() + .build() + .unwrap() + .block; + block_on(client.import_as_best(BlockOrigin::Own, b3.clone())).unwrap(); + + // B3 -> B4 + let b4 = client + .new_block_at(&BlockId::Hash(b3.hash()), Default::default(), false) + .unwrap() + .build() + .unwrap() + .block; + let (header, extrinsics) = b4.clone().deconstruct(); + let mut import_params = BlockImportParams::new(BlockOrigin::Own, header); + import_params.body = Some(extrinsics); + import_params.fork_choice = Some(ForkChoiceStrategy::Custom(false)); + block_on(client.import_block(import_params, Default::default())).unwrap(); + + // double check that B3 is still the best... + assert_eq!(client.info().best_hash, b3.hash()); + + assert_eq!(b4.hash(), block_on(chain_select.finality_target(genesis_hash, None)).unwrap()); + assert_eq!(b4.hash(), block_on(chain_select.finality_target(a1.hash(), None)).unwrap()); + assert!(block_on(chain_select.finality_target(a2.hash(), None)).is_err()); + assert_eq!(b4.hash(), block_on(chain_select.finality_target(b2.hash(), None)).unwrap()); + assert_eq!(b4.hash(), block_on(chain_select.finality_target(b3.hash(), None)).unwrap()); + assert_eq!(b4.hash(), block_on(chain_select.finality_target(b4.hash(), None)).unwrap()); } #[test] @@ -995,7 +1082,7 @@ fn finalizing_diverged_block_should_trigger_reorg() { .block; block_on(client.import(BlockOrigin::Own, b2.clone())).unwrap(); - // A2 is the current best since it's the longest chain + // A2 is the current best since it's the (first) longest chain assert_eq!(client.chain_info().best_hash, a2.hash()); // we finalize block B1 which is on a different branch from current best @@ -1012,8 +1099,7 @@ fn finalizing_diverged_block_should_trigger_reorg() { // `SelectChain` should report B2 as best block though assert_eq!(block_on(select_chain.best_chain()).unwrap().hash(), b2.hash()); - // after we build B3 on top of B2 and import it - // it should be the new best block, + // after we build B3 on top of B2 and import it, it should be the new best block let b3 = client .new_block_at(&BlockId::Hash(b2.hash()), Default::default(), false) .unwrap() @@ -1022,6 +1108,9 @@ fn finalizing_diverged_block_should_trigger_reorg() { .block; block_on(client.import(BlockOrigin::Own, b3.clone())).unwrap(); + // `SelectChain` should report B3 as best block though + assert_eq!(block_on(select_chain.best_chain()).unwrap().hash(), b3.hash()); + assert_eq!(client.chain_info().best_hash, b3.hash()); ClientExt::finalize_block(&client, b3.hash(), None).unwrap(); diff --git a/primitives/blockchain/src/backend.rs b/primitives/blockchain/src/backend.rs index ec9c8ac0d..7339d4c1a 100644 --- a/primitives/blockchain/src/backend.rs +++ b/primitives/blockchain/src/backend.rs @@ -183,96 +183,43 @@ pub trait Backend: /// Return hashes of all blocks that are children of the block with `parent_hash`. fn children(&self, parent_hash: Block::Hash) -> Result>; - /// Get the most recent block hash of the best (longest) chains - /// that contain block with the given `target_hash`. + /// Get the most recent block hash of the longest chain that contains + /// a block with the given `base_hash`. /// /// The search space is always limited to blocks which are in the finalized /// chain or descendents of it. /// - /// If `maybe_max_block_number` is `Some(max_block_number)` - /// the search is limited to block `numbers <= max_block_number`. - /// in other words as if there were no blocks greater `max_block_number`. - /// Returns `Ok(None)` if `target_hash` is not found in search space. - /// TODO: document time complexity of this, see [#1444](https://github.com/paritytech/substrate/issues/1444) - fn best_containing( + /// Returns `Ok(None)` if `base_hash` is not found in search space. + // TODO: document time complexity of this, see [#1444](https://github.com/paritytech/substrate/issues/1444) + fn longest_containing( &self, - target_hash: Block::Hash, - maybe_max_number: Option>, + base_hash: Block::Hash, import_lock: &RwLock<()>, ) -> Result> { - let target_header = { - match self.header(target_hash)? { - Some(x) => x, - // target not in blockchain - None => return Ok(None), - } + let Some(base_header) = self.header(base_hash)? else { + return Ok(None) }; - if let Some(max_number) = maybe_max_number { - // target outside search range - if target_header.number() > &max_number { - return Ok(None) - } - } - let leaves = { // ensure no blocks are imported during this code block. // an import could trigger a reorg which could change the canonical chain. // we depend on the canonical chain staying the same during this code block. let _import_guard = import_lock.read(); - let info = self.info(); - - // this can be `None` if the best chain is shorter than the target header. - let maybe_canon_hash = self.hash(*target_header.number())?; - - if maybe_canon_hash.as_ref() == Some(&target_hash) { - // if a `max_number` is given we try to fetch the block at the - // given depth, if it doesn't exist or `max_number` is not - // provided, we continue to search from all leaves below. - if let Some(max_number) = maybe_max_number { - if let Some(header) = self.hash(max_number)? { - return Ok(Some(header)) - } - } - } else if info.finalized_number >= *target_header.number() { - // header is on a dead fork. + if info.finalized_number > *base_header.number() { + // `base_header` is on a dead fork. return Ok(None) } - self.leaves()? }; // for each chain. longest chain first. shortest last for leaf_hash in leaves { - // start at the leaf let mut current_hash = leaf_hash; - - // if search is not restricted then the leaf is the best - let mut best_hash = leaf_hash; - - // go backwards entering the search space - // waiting until we are <= max_number - if let Some(max_number) = maybe_max_number { - loop { - let current_header = self - .header(current_hash)? - .ok_or_else(|| Error::MissingHeader(current_hash.to_string()))?; - - if current_header.number() <= &max_number { - best_hash = current_header.hash(); - break - } - - current_hash = *current_header.parent_hash(); - } - } - // go backwards through the chain (via parent links) loop { - // until we find target - if current_hash == target_hash { - return Ok(Some(best_hash)) + if current_hash == base_hash { + return Ok(Some(leaf_hash)) } let current_header = self @@ -280,7 +227,7 @@ pub trait Backend: .ok_or_else(|| Error::MissingHeader(current_hash.to_string()))?; // stop search in this chain once we go below the target's block number - if current_header.number() < target_header.number() { + if current_header.number() < base_header.number() { break } @@ -293,9 +240,8 @@ pub trait Backend: // // FIXME #1558 only issue this warning when not on a dead fork warn!( - "Block {:?} exists in chain but not found when following all \ - leaves backwards. Number limit = {:?}", - target_hash, maybe_max_number, + "Block {:?} exists in chain but not found when following all leaves backwards", + base_hash, ); Ok(None) diff --git a/primitives/consensus/common/src/select_chain.rs b/primitives/consensus/common/src/select_chain.rs index f366cd34c..5beab6705 100644 --- a/primitives/consensus/common/src/select_chain.rs +++ b/primitives/consensus/common/src/select_chain.rs @@ -43,14 +43,14 @@ pub trait SelectChain: Sync + Send + Clone { /// finalize. async fn best_chain(&self) -> Result<::Header, Error>; - /// Get the best descendent of `target_hash` that we should attempt to - /// finalize next, if any. It is valid to return the given `target_hash` + /// Get the best descendent of `base_hash` that we should attempt to + /// finalize next, if any. It is valid to return the given `base_hash` /// itself if no better descendent exists. async fn finality_target( &self, - target_hash: ::Hash, + base_hash: ::Hash, _maybe_max_number: Option>, ) -> Result<::Hash, Error> { - Ok(target_hash) + Ok(base_hash) } } From 11bd6cdd06041f478ee9ae5e678c8fb78ab46d63 Mon Sep 17 00:00:00 2001 From: girazoki Date: Sun, 12 Feb 2023 11:43:03 +0100 Subject: [PATCH 105/558] SetMembers configurable origin (#13159) * SetMembers configurable origin * root origin comment replaced * fmt --- bin/node/runtime/src/lib.rs | 3 +++ frame/alliance/src/mock.rs | 1 + frame/collective/src/lib.rs | 7 +++++-- frame/collective/src/tests.rs | 5 ++++- frame/utility/src/tests.rs | 1 + 5 files changed, 14 insertions(+), 3 deletions(-) diff --git a/bin/node/runtime/src/lib.rs b/bin/node/runtime/src/lib.rs index 4298a6ece..8f8a7ceef 100644 --- a/bin/node/runtime/src/lib.rs +++ b/bin/node/runtime/src/lib.rs @@ -992,6 +992,7 @@ impl pallet_collective::Config for Runtime { type MaxMembers = CouncilMaxMembers; type DefaultVote = pallet_collective::PrimeDefaultVote; type WeightInfo = pallet_collective::weights::SubstrateWeight; + type SetMembersOrigin = EnsureRoot; } parameter_types! { @@ -1051,6 +1052,7 @@ impl pallet_collective::Config for Runtime { type MaxMembers = TechnicalMaxMembers; type DefaultVote = pallet_collective::PrimeDefaultVote; type WeightInfo = pallet_collective::weights::SubstrateWeight; + type SetMembersOrigin = EnsureRoot; } type EnsureRootOrHalfCouncil = EitherOfDiverse< @@ -1652,6 +1654,7 @@ impl pallet_collective::Config for Runtime { type MaxMembers = AllianceMaxMembers; type DefaultVote = pallet_collective::PrimeDefaultVote; type WeightInfo = pallet_collective::weights::SubstrateWeight; + type SetMembersOrigin = EnsureRoot; } parameter_types! { diff --git a/frame/alliance/src/mock.rs b/frame/alliance/src/mock.rs index e708d29d5..0f774dc48 100644 --- a/frame/alliance/src/mock.rs +++ b/frame/alliance/src/mock.rs @@ -104,6 +104,7 @@ impl pallet_collective::Config for Test { type MaxMembers = MaxMembers; type DefaultVote = pallet_collective::PrimeDefaultVote; type WeightInfo = (); + type SetMembersOrigin = EnsureRoot; } parameter_types! { diff --git a/frame/collective/src/lib.rs b/frame/collective/src/lib.rs index 0fe05dbb0..7d625a69a 100644 --- a/frame/collective/src/lib.rs +++ b/frame/collective/src/lib.rs @@ -215,6 +215,9 @@ pub mod pallet { /// Weight information for extrinsics in this pallet. type WeightInfo: WeightInfo; + + /// Origin allowed to set collective members + type SetMembersOrigin: EnsureOrigin<::RuntimeOrigin>; } #[pallet::genesis_config] @@ -349,7 +352,7 @@ pub mod pallet { /// - `old_count`: The upper bound for the previous number of members in storage. Used for /// weight estimation. /// - /// Requires root origin. + /// The dispatch of this call must be `SetMembersOrigin`. /// /// NOTE: Does not enforce the expected `MaxMembers` limit on the amount of members, but /// the weight estimations rely on it to estimate dispatchable weight. @@ -389,7 +392,7 @@ pub mod pallet { prime: Option, old_count: MemberCount, ) -> DispatchResultWithPostInfo { - ensure_root(origin)?; + T::SetMembersOrigin::ensure_origin(origin)?; if new_members.len() > T::MaxMembers::get() as usize { log::error!( target: LOG_TARGET, diff --git a/frame/collective/src/tests.rs b/frame/collective/src/tests.rs index 5c90de9f9..b7cdeb338 100644 --- a/frame/collective/src/tests.rs +++ b/frame/collective/src/tests.rs @@ -24,7 +24,7 @@ use frame_support::{ traits::{ConstU32, ConstU64, GenesisBuild, StorageVersion}, Hashable, }; -use frame_system::{EventRecord, Phase}; +use frame_system::{EnsureRoot, EventRecord, Phase}; use sp_core::H256; use sp_runtime::{ testing::Header, @@ -127,6 +127,7 @@ impl Config for Test { type MaxMembers = MaxMembers; type DefaultVote = PrimeDefaultVote; type WeightInfo = (); + type SetMembersOrigin = EnsureRoot; } impl Config for Test { type RuntimeOrigin = RuntimeOrigin; @@ -137,6 +138,7 @@ impl Config for Test { type MaxMembers = MaxMembers; type DefaultVote = MoreThanMajorityThenPrimeDefaultVote; type WeightInfo = (); + type SetMembersOrigin = EnsureRoot; } impl mock_democracy::Config for Test { type RuntimeEvent = RuntimeEvent; @@ -151,6 +153,7 @@ impl Config for Test { type MaxMembers = MaxMembers; type DefaultVote = PrimeDefaultVote; type WeightInfo = (); + type SetMembersOrigin = EnsureRoot; } pub fn new_test_ext() -> sp_io::TestExternalities { diff --git a/frame/utility/src/tests.rs b/frame/utility/src/tests.rs index f9d6a16c1..c63ed24c6 100644 --- a/frame/utility/src/tests.rs +++ b/frame/utility/src/tests.rs @@ -217,6 +217,7 @@ impl pallet_collective::Config for Test { type MaxMembers = MaxMembers; type DefaultVote = pallet_collective::PrimeDefaultVote; type WeightInfo = (); + type SetMembersOrigin = frame_system::EnsureRoot; } impl example::Config for Test {} From 5590a4e0e793c98dd4d26ea34958c93775ce4635 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Silva?= <123550+andresilva@users.noreply.github.com> Date: Sun, 12 Feb 2023 12:35:01 +0000 Subject: [PATCH 106/558] grandpa: don't error if best block and finality target are inconsistent (#13364) * grandpa: don't error if best block and finality target are inconsistent * grandpa: add test for best block override * grandpa: make clippy happy * grandpa: log selectchain override as debug instead of warn --- client/finality-grandpa/src/environment.rs | 35 ++++- client/finality-grandpa/src/tests.rs | 144 +++++++++++++++++++-- 2 files changed, 166 insertions(+), 13 deletions(-) diff --git a/client/finality-grandpa/src/environment.rs b/client/finality-grandpa/src/environment.rs index 40ec720c4..a9aa204f1 100644 --- a/client/finality-grandpa/src/environment.rs +++ b/client/finality-grandpa/src/environment.rs @@ -1213,7 +1213,7 @@ where // NOTE: this is purposefully done after `finality_target` to prevent a case // where in-between these two requests there is a block import and // `finality_target` returns something higher than `best_chain`. - let best_header = match select_chain.best_chain().await { + let mut best_header = match select_chain.best_chain().await { Ok(best_header) => best_header, Err(err) => { warn!( @@ -1227,12 +1227,30 @@ where }, }; - if target_header.number() > best_header.number() { - return Err(Error::Safety( - "SelectChain returned a finality target higher than its best block".into(), - )) + let is_descendent_of = is_descendent_of(&*client, None); + + if target_header.number() > best_header.number() || + target_header.number() == best_header.number() && + target_header.hash() != best_header.hash() || + !is_descendent_of(&target_header.hash(), &best_header.hash())? + { + debug!( + target: LOG_TARGET, + "SelectChain returned a finality target inconsistent with its best block. Restricting best block to target block" + ); + + best_header = target_header.clone(); } + debug!( + target: LOG_TARGET, + "SelectChain: finality target: #{} ({}), best block: #{} ({})", + target_header.number(), + target_header.hash(), + best_header.number(), + best_header.hash(), + ); + // check if our vote is currently being limited due to a pending change, // in which case we will restrict our target header to the given limit if let Some(target_number) = limit.filter(|limit| limit < target_header.number()) { @@ -1254,6 +1272,13 @@ where .header(*target_header.parent_hash())? .expect("Header known to exist after `finality_target` call; qed"); } + + debug!( + target: LOG_TARGET, + "Finality target restricted to #{} ({}) due to pending authority set change", + target_header.number(), + target_header.hash() + ) } // restrict vote according to the given voting rule, if the voting rule diff --git a/client/finality-grandpa/src/tests.rs b/client/finality-grandpa/src/tests.rs index 62ef4709e..d132abd94 100644 --- a/client/finality-grandpa/src/tests.rs +++ b/client/finality-grandpa/src/tests.rs @@ -244,6 +244,35 @@ impl SelectChain for MockSelectChain { } } +// A mock voting rule that allows asserting an expected value for best block +#[derive(Clone, Default)] +struct AssertBestBlock(Arc>>); + +impl VotingRule for AssertBestBlock +where + B: HeaderBackend, +{ + fn restrict_vote( + &self, + _backend: Arc, + _base: &::Header, + best_target: &::Header, + _current_target: &::Header, + ) -> VotingRuleResult { + if let Some(expected) = *self.0.lock() { + assert_eq!(best_target.hash(), expected); + } + + Box::pin(std::future::ready(None)) + } +} + +impl AssertBestBlock { + fn set_expected_best_block(&self, hash: Hash) { + *self.0.lock() = Some(hash); + } +} + const TEST_GOSSIP_DURATION: Duration = Duration::from_millis(500); fn make_ids(keys: &[Ed25519Keyring]) -> AuthorityList { @@ -1566,16 +1595,115 @@ async fn grandpa_environment_passes_actual_best_block_to_voting_rules() { .1, 10, ); +} - // returning a finality target that's higher than the best block is an - // inconsistent state that should be handled - let hashof42 = client.expect_block_hash_from_id(&BlockId::Number(42)).unwrap(); - select_chain.set_best_chain(client.expect_header(hashof21).unwrap()); - select_chain.set_finality_target(client.expect_header(hashof42).unwrap().hash()); +#[tokio::test] +async fn grandpa_environment_checks_if_best_block_is_descendent_of_finality_target() { + use finality_grandpa::voter::Environment; + + let peers = &[Ed25519Keyring::Alice]; + let voters = make_ids(peers); + + let mut net = GrandpaTestNet::new(TestApi::new(voters), 1, 0); + let peer = net.peer(0); + let network_service = peer.network_service().clone(); + let link = peer.data.lock().take().unwrap(); + let client = peer.client().as_client().clone(); + let select_chain = MockSelectChain::default(); + let voting_rule = AssertBestBlock::default(); + let env = test_environment_with_select_chain( + &link, + None, + network_service.clone(), + select_chain.clone(), + voting_rule.clone(), + ); + + // create a chain that is 10 blocks long + peer.push_blocks(10, false); + + let hashof5_a = client.expect_block_hash_from_id(&BlockId::Number(5)).unwrap(); + let hashof10_a = client.expect_block_hash_from_id(&BlockId::Number(10)).unwrap(); + + // create a fork starting at block 4 that is 6 blocks long + let fork = peer.generate_blocks_at( + BlockId::Number(4), + 6, + BlockOrigin::File, + |builder| { + let mut block = builder.build().unwrap().block; + block.header.digest_mut().push(DigestItem::Other(vec![1])); + block + }, + false, + false, + true, + ForkChoiceStrategy::LongestChain, + ); + + let hashof5_b = *fork.first().unwrap(); + let hashof10_b = *fork.last().unwrap(); + + // returning a finality target that's higher than the best block is inconsistent, + // therefore the best block should be set to be the same block as the target + select_chain.set_best_chain(client.expect_header(hashof5_a).unwrap()); + select_chain.set_finality_target(client.expect_header(hashof10_a).unwrap().hash()); + voting_rule.set_expected_best_block(hashof10_a); + + // the voting rule will internally assert that the best block that was passed was `hashof10_a`, + // instead of the one returned by `SelectChain` + assert_eq!( + env.best_chain_containing(peer.client().info().finalized_hash) + .await + .unwrap() + .unwrap() + .0, + hashof10_a, + ); + + // best block and finality target are blocks at the same height but on different forks, + // we should override the initial best block (#5B) with the target block (#5A) + select_chain.set_best_chain(client.expect_header(hashof5_b).unwrap()); + select_chain.set_finality_target(client.expect_header(hashof5_a).unwrap().hash()); + voting_rule.set_expected_best_block(hashof5_a); + + assert_eq!( + env.best_chain_containing(peer.client().info().finalized_hash) + .await + .unwrap() + .unwrap() + .0, + hashof5_a, + ); - assert_matches!( - env.best_chain_containing(peer.client().info().finalized_hash).await, - Err(CommandOrError::Error(Error::Safety(_))) + // best block is higher than finality target but it's on a different fork, + // we should override the initial best block (#5A) with the target block (#5B) + select_chain.set_best_chain(client.expect_header(hashof10_b).unwrap()); + select_chain.set_finality_target(client.expect_header(hashof5_a).unwrap().hash()); + voting_rule.set_expected_best_block(hashof5_a); + + assert_eq!( + env.best_chain_containing(peer.client().info().finalized_hash) + .await + .unwrap() + .unwrap() + .0, + hashof5_a, + ); + + // best block is higher than finality target and it's on the same fork, + // the best block passed to the voting rule should not be overriden + select_chain.set_best_chain(client.expect_header(hashof10_a).unwrap()); + select_chain.set_finality_target(client.expect_header(hashof5_a).unwrap().hash()); + voting_rule.set_expected_best_block(hashof10_a); + + assert_eq!( + env.best_chain_containing(peer.client().info().finalized_hash) + .await + .unwrap() + .unwrap() + .0, + hashof5_a, ); } From 4fadc98e1fa6b3cbb158735007d46bfe48a74158 Mon Sep 17 00:00:00 2001 From: Shawn Tabrizi Date: Sun, 12 Feb 2023 12:01:06 -0300 Subject: [PATCH 107/558] Improve Weight Template and API (#13355) * improve weights template and api * follow template --- .maintain/frame-weight-template.hbs | 10 ++----- primitives/weights/src/weight_v2.rs | 26 ++++++++++++++----- .../benchmarking-cli/src/pallet/template.hbs | 5 +--- 3 files changed, 23 insertions(+), 18 deletions(-) diff --git a/.maintain/frame-weight-template.hbs b/.maintain/frame-weight-template.hbs index ff69fcb4a..186e3cb23 100644 --- a/.maintain/frame-weight-template.hbs +++ b/.maintain/frame-weight-template.hbs @@ -53,11 +53,8 @@ impl WeightInfo for SubstrateWeight { // Measured: `{{benchmark.base_recorded_proof_size}}{{#each benchmark.component_recorded_proof_size as |cp|}} + {{cp.name}} * ({{cp.slope}} ±{{underscore cp.error}}){{/each}}` // Estimated: `{{benchmark.base_calculated_proof_size}}{{#each benchmark.component_calculated_proof_size as |cp|}} + {{cp.name}} * ({{cp.slope}} ±{{underscore cp.error}}){{/each}}` // Minimum execution time: {{underscore benchmark.min_execution_time}} nanoseconds. - {{#if (ne benchmark.base_calculated_proof_size "0")}} - Weight::from_parts({{underscore benchmark.base_weight}}, {{benchmark.base_calculated_proof_size}}) - {{else}} Weight::from_ref_time({{underscore benchmark.base_weight}}) - {{/if}} + .saturating_add(Weight::from_proof_size({{benchmark.base_calculated_proof_size}})) {{#each benchmark.component_weight as |cw|}} // Standard Error: {{underscore cw.error}} .saturating_add(Weight::from_ref_time({{underscore cw.slope}}).saturating_mul({{cw.name}}.into())) @@ -99,11 +96,8 @@ impl WeightInfo for () { // Measured: `{{benchmark.base_recorded_proof_size}}{{#each benchmark.component_recorded_proof_size as |cp|}} + {{cp.name}} * ({{cp.slope}} ±{{underscore cp.error}}){{/each}}` // Estimated: `{{benchmark.base_calculated_proof_size}}{{#each benchmark.component_calculated_proof_size as |cp|}} + {{cp.name}} * ({{cp.slope}} ±{{underscore cp.error}}){{/each}}` // Minimum execution time: {{underscore benchmark.min_execution_time}} nanoseconds. - {{#if (ne benchmark.base_calculated_proof_size "0")}} - Weight::from_parts({{underscore benchmark.base_weight}}, {{benchmark.base_calculated_proof_size}}) - {{else}} Weight::from_ref_time({{underscore benchmark.base_weight}}) - {{/if}} + .saturating_add(Weight::from_proof_size({{benchmark.base_calculated_proof_size}})) {{#each benchmark.component_weight as |cw|}} // Standard Error: {{underscore cw.error}} .saturating_add(Weight::from_ref_time({{underscore cw.slope}}).saturating_mul({{cw.name}}.into())) diff --git a/primitives/weights/src/weight_v2.rs b/primitives/weights/src/weight_v2.rs index 33e61b695..3a15b1179 100644 --- a/primitives/weights/src/weight_v2.rs +++ b/primitives/weights/src/weight_v2.rs @@ -247,18 +247,32 @@ impl Weight { Self { ref_time: 0, proof_size: 0 } } - /// Constant version of Add with u64. + /// Constant version of Add for `ref_time` component with u64. /// /// Is only overflow safe when evaluated at compile-time. - pub const fn add(self, scalar: u64) -> Self { - Self { ref_time: self.ref_time + scalar, proof_size: self.proof_size + scalar } + pub const fn add_ref_time(self, scalar: u64) -> Self { + Self { ref_time: self.ref_time + scalar, proof_size: self.proof_size } } - /// Constant version of Sub with u64. + /// Constant version of Add for `proof_size` component with u64. /// /// Is only overflow safe when evaluated at compile-time. - pub const fn sub(self, scalar: u64) -> Self { - Self { ref_time: self.ref_time - scalar, proof_size: self.proof_size - scalar } + pub const fn add_proof_size(self, scalar: u64) -> Self { + Self { ref_time: self.ref_time, proof_size: self.proof_size + scalar } + } + + /// Constant version of Sub for `ref_time` component with u64. + /// + /// Is only overflow safe when evaluated at compile-time. + pub const fn sub_ref_time(self, scalar: u64) -> Self { + Self { ref_time: self.ref_time - scalar, proof_size: self.proof_size } + } + + /// Constant version of Sub for `proof_size` component with u64. + /// + /// Is only overflow safe when evaluated at compile-time. + pub const fn sub_proof_size(self, scalar: u64) -> Self { + Self { ref_time: self.ref_time, proof_size: self.proof_size - scalar } } /// Constant version of Div with u64. diff --git a/utils/frame/benchmarking-cli/src/pallet/template.hbs b/utils/frame/benchmarking-cli/src/pallet/template.hbs index 00dd1a353..0bec34eaa 100644 --- a/utils/frame/benchmarking-cli/src/pallet/template.hbs +++ b/utils/frame/benchmarking-cli/src/pallet/template.hbs @@ -38,11 +38,8 @@ impl {{pallet}}::WeightInfo for WeightInfo { // Measured: `{{benchmark.base_recorded_proof_size}}{{#each benchmark.component_recorded_proof_size as |cp|}} + {{cp.name}} * ({{cp.slope}} ±{{underscore cp.error}}){{/each}}` // Estimated: `{{benchmark.base_calculated_proof_size}}{{#each benchmark.component_calculated_proof_size as |cp|}} + {{cp.name}} * ({{cp.slope}} ±{{underscore cp.error}}){{/each}}` // Minimum execution time: {{underscore benchmark.min_execution_time}} nanoseconds. - {{#if (ne benchmark.base_calculated_proof_size "0")}} - Weight::from_parts({{underscore benchmark.base_weight}}, {{benchmark.base_calculated_proof_size}}) - {{else}} Weight::from_ref_time({{underscore benchmark.base_weight}}) - {{/if}} + .saturating_add(Weight::from_proof_size({{benchmark.base_calculated_proof_size}})) {{#each benchmark.component_weight as |cw|}} // Standard Error: {{underscore cw.error}} .saturating_add(Weight::from_ref_time({{underscore cw.slope}}).saturating_mul({{cw.name}}.into())) From 14fba11d9c474787bc8c46bba3c0b1883dc60099 Mon Sep 17 00:00:00 2001 From: Alexander Samusev <41779041+alvicsam@users.noreply.github.com> Date: Mon, 13 Feb 2023 10:54:47 +0100 Subject: [PATCH 108/558] [ci] Change label checker (#13360) * [ci] Change label checker * rm pr autolabel * fix specs file name to substrate --- .github/workflows/auto-label-prs.yml | 21 ------------ .github/workflows/check-D-labels.yml | 48 ++++++++++++++++++++++++++++ .github/workflows/check-labels.yml | 38 ++++++++++++++++++---- 3 files changed, 79 insertions(+), 28 deletions(-) delete mode 100644 .github/workflows/auto-label-prs.yml create mode 100644 .github/workflows/check-D-labels.yml diff --git a/.github/workflows/auto-label-prs.yml b/.github/workflows/auto-label-prs.yml deleted file mode 100644 index 50539b80b..000000000 --- a/.github/workflows/auto-label-prs.yml +++ /dev/null @@ -1,21 +0,0 @@ -name: Label PRs -on: - pull_request: - types: [opened,ready_for_review] - -jobs: - label-new-prs: - runs-on: ubuntu-latest - steps: - - name: Label drafts - uses: andymckay/labeler@e6c4322d0397f3240f0e7e30a33b5c5df2d39e90 # 1.0.4 - if: github.event.pull_request.draft == true - with: - add-labels: 'A3-inprogress' - remove-labels: 'A0-pleasereview' - - name: Label PRs - uses: andymckay/labeler@e6c4322d0397f3240f0e7e30a33b5c5df2d39e90 # 1.0.4 - if: github.event.pull_request.draft == false && ! contains(github.event.pull_request.labels.*.name, 'A2-insubstantial') - with: - add-labels: 'A0-pleasereview' - remove-labels: 'A3-inprogress' diff --git a/.github/workflows/check-D-labels.yml b/.github/workflows/check-D-labels.yml new file mode 100644 index 000000000..7bb358ce1 --- /dev/null +++ b/.github/workflows/check-D-labels.yml @@ -0,0 +1,48 @@ +name: Check D labels + +on: + pull_request: + types: [labeled, opened, synchronize, unlabeled] + paths: + - frame/** + - primitives/** + +env: + IMAGE: paritytech/ruled_labels:0.4.0 + +jobs: + check-labels: + runs-on: ubuntu-latest + steps: + - name: Pull image + run: docker pull $IMAGE + + - name: Check labels + env: + MOUNT: /work + GITHUB_PR: ${{ github.event.pull_request.number }} + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + API_BASE: https://api.github.com/repos + REPO: ${{ github.repository }} + RULES_PATH: labels/ruled_labels + CHECK_SPECS: specs_substrate.yaml + run: | + echo "REPO: ${REPO}" + echo "GITHUB_PR: ${GITHUB_PR}" + # Clone repo with labels specs + git clone https://github.com/paritytech/labels + # Fetch the labels for the PR under test + labels=$( curl -H "Authorization: token ${GITHUB_TOKEN}" -s "$API_BASE/${REPO}/pulls/${GITHUB_PR}" | jq '.labels | .[] | .name' | tr "\n" ",") + + if [ -z "${labels}" ]; then + docker run --rm -i -v $PWD/${RULES_PATH}/:$MOUNT $IMAGE check $MOUNT/$CHECK_SPECS --tags audit --no-label + fi + + labels_args=${labels: :-1} + printf "Checking labels: %s\n" "${labels_args}" + + # Prevent the shell from splitting labels with spaces + IFS="," + + # --dev is more useful to debug mode to debug + docker run --rm -i -v $PWD/${RULES_PATH}/:$MOUNT $IMAGE check $MOUNT/$CHECK_SPECS --labels ${labels_args} --dev --tags audit diff --git a/.github/workflows/check-labels.yml b/.github/workflows/check-labels.yml index de204ce9d..55b8f7389 100644 --- a/.github/workflows/check-labels.yml +++ b/.github/workflows/check-labels.yml @@ -4,18 +4,42 @@ on: pull_request: types: [labeled, opened, synchronize, unlabeled] +env: + IMAGE: paritytech/ruled_labels:0.4.0 + jobs: check-labels: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v3 - with: - fetch-depth: 0 - ref: ${{ github.event.pull_request.head.ref }} - repository: ${{ github.event.pull_request.head.repo.full_name }} + - name: Pull image + run: docker pull $IMAGE + - name: Check labels - run: bash ${{ github.workspace }}/scripts/ci/github/check_labels.sh env: + MOUNT: /work GITHUB_PR: ${{ github.event.pull_request.number }} GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - HEAD_SHA: ${{ github.event.pull_request.head.sha }} + API_BASE: https://api.github.com/repos + REPO: ${{ github.repository }} + RULES_PATH: labels/ruled_labels + CHECK_SPECS: specs_substrate.yaml + run: | + echo "REPO: ${REPO}" + echo "GITHUB_PR: ${GITHUB_PR}" + # Clone repo with labels specs + git clone https://github.com/paritytech/labels + # Fetch the labels for the PR under test + labels=$( curl -H "Authorization: token ${GITHUB_TOKEN}" -s "$API_BASE/${REPO}/pulls/${GITHUB_PR}" | jq '.labels | .[] | .name' | tr "\n" ",") + + if [ -z "${labels}" ]; then + docker run --rm -i -v $PWD/${RULES_PATH}/:$MOUNT $IMAGE check $MOUNT/$CHECK_SPECS --tags PR --no-label + fi + + labels_args=${labels: :-1} + printf "Checking labels: %s\n" "${labels_args}" + + # Prevent the shell from splitting labels with spaces + IFS="," + + # --dev is more useful to debug mode to debug + docker run --rm -i -v $PWD/${RULES_PATH}/:$MOUNT $IMAGE check $MOUNT/$CHECK_SPECS --labels ${labels_args} --dev --tags PR From fc49dddda28bbf8dcc1e9c208346cdfb2565501b Mon Sep 17 00:00:00 2001 From: Adrian Catangiu Date: Mon, 13 Feb 2023 12:21:20 +0200 Subject: [PATCH 109/558] client/beefy: request justifs from peers further in consensus (#13343) For on-demand justifications, peer selection is based on witnessed gossip votes. This commit changes the condition for selecting a peer to request justification for `block` from "last voted on >= `block`" to "peer last voted on strict > `block`". When allowing `>=` we see nodes continuously spamming unsuccessful on-demand requests to nodes which are still voting on a block without having a justification available. One way to fix the spam would be to add some rate-limiting or backoff period when requesting justifications. The other solution (present in this commit) is to simply request justifications from peers that are voting on future blocks so we know they're _guaranteed_ to have the wanted mandatory justification available to send back. Signed-off-by: acatangiu --- client/beefy/src/communication/peers.rs | 32 +++++++++---------- .../outgoing_requests_engine.rs | 3 +- client/beefy/src/tests.rs | 12 +++---- client/beefy/src/worker.rs | 2 +- 4 files changed, 24 insertions(+), 25 deletions(-) diff --git a/client/beefy/src/communication/peers.rs b/client/beefy/src/communication/peers.rs index d7927ea72..50158126a 100644 --- a/client/beefy/src/communication/peers.rs +++ b/client/beefy/src/communication/peers.rs @@ -57,11 +57,11 @@ impl KnownPeers { self.live.remove(peer); } - /// Return _filtered and cloned_ list of peers that have voted on `block` or higher. - pub fn at_least_at_block(&self, block: NumberFor) -> VecDeque { + /// Return _filtered and cloned_ list of peers that have voted on higher than `block`. + pub fn further_than(&self, block: NumberFor) -> VecDeque { self.live .iter() - .filter_map(|(k, v)| (v.last_voted_on >= block).then_some(k)) + .filter_map(|(k, v)| (v.last_voted_on > block).then_some(k)) .cloned() .collect() } @@ -92,22 +92,22 @@ mod tests { assert!(peers.contains(&bob)); assert!(peers.contains(&charlie)); - // Get peers at block >= 5 - let at_5 = peers.at_least_at_block(5); + // Get peers at block > 4 + let further_than_4 = peers.further_than(4); // Should be Bob and Charlie - assert_eq!(at_5.len(), 2); - assert!(at_5.contains(&bob)); - assert!(at_5.contains(&charlie)); + assert_eq!(further_than_4.len(), 2); + assert!(further_than_4.contains(&bob)); + assert!(further_than_4.contains(&charlie)); // 'Tracked' Alice seen voting for 10. peers.note_vote_for(alice, 10); - // Get peers at block >= 9 - let at_9 = peers.at_least_at_block(9); + // Get peers at block > 9 + let further_than_9 = peers.further_than(9); // Should be Charlie and Alice - assert_eq!(at_9.len(), 2); - assert!(at_9.contains(&charlie)); - assert!(at_9.contains(&alice)); + assert_eq!(further_than_9.len(), 2); + assert!(further_than_9.contains(&charlie)); + assert!(further_than_9.contains(&alice)); // Remove Alice peers.remove(&alice); @@ -115,9 +115,9 @@ mod tests { assert!(!peers.contains(&alice)); // Get peers at block >= 9 - let at_9 = peers.at_least_at_block(9); + let further_than_9 = peers.further_than(9); // Now should be just Charlie - assert_eq!(at_9.len(), 1); - assert!(at_9.contains(&charlie)); + assert_eq!(further_than_9.len(), 1); + assert!(further_than_9.contains(&charlie)); } } diff --git a/client/beefy/src/communication/request_response/outgoing_requests_engine.rs b/client/beefy/src/communication/request_response/outgoing_requests_engine.rs index c7dc269b4..766480f78 100644 --- a/client/beefy/src/communication/request_response/outgoing_requests_engine.rs +++ b/client/beefy/src/communication/request_response/outgoing_requests_engine.rs @@ -80,7 +80,7 @@ impl OnDemandJustificationsEngine { fn reset_peers_cache_for_block(&mut self, block: NumberFor) { // TODO (issue #12296): replace peer selection with generic one that involves all protocols. - self.peers_cache = self.live_peers.lock().at_least_at_block(block); + self.peers_cache = self.live_peers.lock().further_than(block); } fn try_next_peer(&mut self) -> Option { @@ -199,7 +199,6 @@ impl OnDemandJustificationsEngine { let (peer, req_info, resp) = match &mut self.state { State::Idle => { futures::pending!(); - // Doesn't happen as 'futures::pending!()' is an 'await' barrier that never passes. return None }, State::AwaitingResponse(peer, req_info, receiver) => { diff --git a/client/beefy/src/tests.rs b/client/beefy/src/tests.rs index 69cc9b2f6..932897c77 100644 --- a/client/beefy/src/tests.rs +++ b/client/beefy/src/tests.rs @@ -893,7 +893,7 @@ async fn on_demand_beefy_justification_sync() { [BeefyKeyring::Alice, BeefyKeyring::Bob, BeefyKeyring::Charlie, BeefyKeyring::Dave]; let validator_set = ValidatorSet::new(make_beefy_ids(&all_peers), 0).unwrap(); let session_len = 5; - let min_block_delta = 5; + let min_block_delta = 4; let mut net = BeefyTestNet::new(4); @@ -912,7 +912,7 @@ async fn on_demand_beefy_justification_sync() { let dave_index = 3; // push 30 blocks - let hashes = net.generate_blocks_and_sync(30, session_len, &validator_set, false).await; + let hashes = net.generate_blocks_and_sync(35, session_len, &validator_set, false).await; let fast_peers = fast_peers.into_iter().enumerate(); let net = Arc::new(Mutex::new(net)); @@ -921,7 +921,7 @@ async fn on_demand_beefy_justification_sync() { finalize_block_and_wait_for_beefy( &net, fast_peers.clone(), - &[hashes[1], hashes[6], hashes[10], hashes[17], hashes[24]], + &[hashes[1], hashes[6], hashes[10], hashes[17], hashes[23]], &[1, 5, 10, 15, 20], ) .await; @@ -941,12 +941,12 @@ async fn on_demand_beefy_justification_sync() { // freshly spun up Dave now needs to listen for gossip to figure out the state of their peers. // Have the other peers do some gossip so Dave finds out about their progress. - finalize_block_and_wait_for_beefy(&net, fast_peers, &[hashes[25]], &[25]).await; + finalize_block_and_wait_for_beefy(&net, fast_peers, &[hashes[25], hashes[29]], &[25, 29]).await; // Now verify Dave successfully finalized #1 (through on-demand justification request). wait_for_best_beefy_blocks(dave_best_blocks, &net, &[1]).await; - // Give Dave all tasks some cpu cycles to burn through their events queues, + // Give all tasks some cpu cycles to burn through their events queues, run_for(Duration::from_millis(100), &net).await; // then verify Dave catches up through on-demand justification requests. finalize_block_and_wait_for_beefy( @@ -959,7 +959,7 @@ async fn on_demand_beefy_justification_sync() { let all_peers = all_peers.into_iter().enumerate(); // Now that Dave has caught up, sanity check voting works for all of them. - finalize_block_and_wait_for_beefy(&net, all_peers, &[hashes[30]], &[30]).await; + finalize_block_and_wait_for_beefy(&net, all_peers, &[hashes[30], hashes[34]], &[30]).await; } #[tokio::test] diff --git a/client/beefy/src/worker.rs b/client/beefy/src/worker.rs index be5443d3d..f367c8b46 100644 --- a/client/beefy/src/worker.rs +++ b/client/beefy/src/worker.rs @@ -555,7 +555,6 @@ where let block_number = vote.commitment.block_number; match rounds.add_vote(vote) { VoteImportResult::RoundConcluded(signed_commitment) => { - self.gossip_validator.conclude_round(block_number); metric_set!(self, beefy_round_concluded, block_number); let finality_proof = VersionedFinalityProof::V1(signed_commitment); @@ -602,6 +601,7 @@ where // Finalize inner round and update voting_oracle state. self.persisted_state.voting_oracle.finalize(block_num)?; + self.gossip_validator.conclude_round(block_num); if block_num > self.best_beefy_block() { // Set new best BEEFY block number. From 6142fc29163d9824aedace79027a418acc2bc955 Mon Sep 17 00:00:00 2001 From: Alexander Samusev <41779041+alvicsam@users.noreply.github.com> Date: Mon, 13 Feb 2023 12:27:52 +0100 Subject: [PATCH 110/558] [ci] Change GHA to add J2 labels instead Z0 (#13375) --- .github/workflows/auto-label-issues.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/auto-label-issues.yml b/.github/workflows/auto-label-issues.yml index 2633bf55f..629966ed3 100644 --- a/.github/workflows/auto-label-issues.yml +++ b/.github/workflows/auto-label-issues.yml @@ -14,4 +14,4 @@ jobs: uses: andymckay/labeler@e6c4322d0397f3240f0e7e30a33b5c5df2d39e90 # 1.0.4 if: github.event.issue.author_association == 'NONE' with: - add-labels: 'Z0-unconfirmed' + add-labels: "J2-unconfirmed" From 10bf4bd0d9ed75c62bf9e094759fa9315e1c2017 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bastian=20K=C3=B6cher?= Date: Mon, 13 Feb 2023 12:39:15 +0100 Subject: [PATCH 111/558] sc-client-db: Fix `PruningMode::ArchiveCanonical` (#13361) * sc-client-db: Fix `PruningMode::ArchiveCanonical` When running a node with `--state-pruning archive-canonical` it was directly failing on genesis. There was an issue in the state-db `pin` implementation. It was not checking the state of a block correctly when running with archive canonical (and also not for every other block after they are canonicalized). * FMT --- client/db/src/lib.rs | 367 +++++++++++++++---------------------- client/state-db/src/lib.rs | 15 +- 2 files changed, 160 insertions(+), 222 deletions(-) diff --git a/client/db/src/lib.rs b/client/db/src/lib.rs index f54f1bdb1..03de383f2 100644 --- a/client/db/src/lib.rs +++ b/client/db/src/lib.rs @@ -1929,13 +1929,13 @@ impl Backend { Ok(()) } - fn empty_state(&self) -> ClientResult, Block>> { + fn empty_state(&self) -> RecordStatsState, Block> { let root = EmptyStorage::::new().0; // Empty trie let db_state = DbStateBuilder::::new(self.storage.clone(), root) .with_optional_cache(self.shared_trie_cache.as_ref().map(|c| c.local_cache())) .build(); let state = RefTrackingState::new(db_state, self.storage.clone(), None); - Ok(RecordStatsState::new(state, None, self.state_usage.clone())) + RecordStatsState::new(state, None, self.state_usage.clone()) } } @@ -2063,7 +2063,7 @@ impl sc_client_api::backend::Backend for Backend { fn begin_operation(&self) -> ClientResult { Ok(BlockImportOperation { pending_block: None, - old_state: self.empty_state()?, + old_state: self.empty_state(), db_updates: PrefixedMemoryDB::default(), storage_updates: Default::default(), child_storage_updates: Default::default(), @@ -2082,7 +2082,7 @@ impl sc_client_api::backend::Backend for Backend { block: Block::Hash, ) -> ClientResult<()> { if block == Default::default() { - operation.old_state = self.empty_state()?; + operation.old_state = self.empty_state(); } else { operation.old_state = self.state_at(block)?; } @@ -2439,6 +2439,7 @@ impl sc_client_api::backend::Backend for Backend { .unwrap_or(None) .is_some() }; + if let Ok(()) = self.storage.state_db.pin(&hash, hdr.number.saturated_into::(), hint) { @@ -2593,25 +2594,30 @@ pub(crate) mod tests { use sp_runtime::testing::Digest; let digest = Digest::default(); - let header = Header { - number, - parent_hash, - state_root: BlakeTwo256::trie_root(Vec::new(), StateVersion::V1), - digest, - extrinsics_root, - }; - let header_hash = header.hash(); + let mut header = + Header { number, parent_hash, state_root: Default::default(), digest, extrinsics_root }; let block_hash = if number == 0 { Default::default() } else { parent_hash }; let mut op = backend.begin_operation().unwrap(); backend.begin_state_operation(&mut op, block_hash).unwrap(); - op.set_block_data(header, Some(body), None, None, NewBlockState::Best).unwrap(); if let Some(index) = transaction_index { op.update_transaction_index(index).unwrap(); } + + // Insert some fake data to ensure that the block can be found in the state column. + let (root, overlay) = op.old_state.storage_root( + vec![(block_hash.as_ref(), Some(block_hash.as_ref()))].into_iter(), + StateVersion::V1, + ); + op.update_db_storage(overlay).unwrap(); + header.state_root = root.into(); + + op.set_block_data(header.clone(), Some(body), None, None, NewBlockState::Best) + .unwrap(); + backend.commit_operation(op)?; - Ok(header_hash) + Ok(header.hash()) } pub fn insert_header_no_head( @@ -2623,18 +2629,31 @@ pub(crate) mod tests { use sp_runtime::testing::Digest; let digest = Digest::default(); - let header = Header { - number, - parent_hash, - state_root: BlakeTwo256::trie_root(Vec::new(), StateVersion::V1), - digest, - extrinsics_root, - }; - let header_hash = header.hash(); + let mut header = + Header { number, parent_hash, state_root: Default::default(), digest, extrinsics_root }; let mut op = backend.begin_operation().unwrap(); - op.set_block_data(header, None, None, None, NewBlockState::Normal).unwrap(); + + let root = backend + .state_at(parent_hash) + .unwrap_or_else(|_| { + if parent_hash == Default::default() { + backend.empty_state() + } else { + panic!("Unknown block: {parent_hash:?}") + } + }) + .storage_root( + vec![(parent_hash.as_ref(), Some(parent_hash.as_ref()))].into_iter(), + StateVersion::V1, + ) + .0; + header.state_root = root.into(); + + op.set_block_data(header.clone(), None, None, None, NewBlockState::Normal) + .unwrap(); backend.commit_operation(op).unwrap(); - header_hash + + header.hash() } #[test] @@ -3369,204 +3388,131 @@ pub(crate) mod tests { #[test] fn prune_blocks_on_finalize() { - let backend = Backend::::new_test_with_tx_storage(BlocksPruning::Some(2), 0); - let mut blocks = Vec::new(); - let mut prev_hash = Default::default(); - for i in 0..5 { - let hash = insert_block( - &backend, - i, - prev_hash, - None, - Default::default(), - vec![i.into()], - None, - ) - .unwrap(); - blocks.push(hash); - prev_hash = hash; - } + let pruning_modes = + vec![BlocksPruning::Some(2), BlocksPruning::KeepFinalized, BlocksPruning::KeepAll]; - { - let mut op = backend.begin_operation().unwrap(); - backend.begin_state_operation(&mut op, blocks[4]).unwrap(); - for i in 1..5 { - op.mark_finalized(blocks[i], None).unwrap(); + for pruning_mode in pruning_modes { + let backend = Backend::::new_test_with_tx_storage(pruning_mode, 0); + let mut blocks = Vec::new(); + let mut prev_hash = Default::default(); + for i in 0..5 { + let hash = insert_block( + &backend, + i, + prev_hash, + None, + Default::default(), + vec![i.into()], + None, + ) + .unwrap(); + blocks.push(hash); + prev_hash = hash; } - backend.commit_operation(op).unwrap(); - } - let bc = backend.blockchain(); - assert_eq!(None, bc.body(blocks[0]).unwrap()); - assert_eq!(None, bc.body(blocks[1]).unwrap()); - assert_eq!(None, bc.body(blocks[2]).unwrap()); - assert_eq!(Some(vec![3.into()]), bc.body(blocks[3]).unwrap()); - assert_eq!(Some(vec![4.into()]), bc.body(blocks[4]).unwrap()); - } - #[test] - fn prune_blocks_on_finalize_in_keep_all() { - let backend = Backend::::new_test_with_tx_storage(BlocksPruning::KeepAll, 0); - let mut blocks = Vec::new(); - let mut prev_hash = Default::default(); - for i in 0..5 { - let hash = insert_block( - &backend, - i, - prev_hash, - None, - Default::default(), - vec![i.into()], - None, - ) - .unwrap(); - blocks.push(hash); - prev_hash = hash; - } + { + let mut op = backend.begin_operation().unwrap(); + backend.begin_state_operation(&mut op, blocks[4]).unwrap(); + for i in 1..5 { + op.mark_finalized(blocks[i], None).unwrap(); + } + backend.commit_operation(op).unwrap(); + } + let bc = backend.blockchain(); - let mut op = backend.begin_operation().unwrap(); - backend.begin_state_operation(&mut op, blocks[4]).unwrap(); - for i in 1..3 { - op.mark_finalized(blocks[i], None).unwrap(); + if matches!(pruning_mode, BlocksPruning::Some(_)) { + assert_eq!(None, bc.body(blocks[0]).unwrap()); + assert_eq!(None, bc.body(blocks[1]).unwrap()); + assert_eq!(None, bc.body(blocks[2]).unwrap()); + assert_eq!(Some(vec![3.into()]), bc.body(blocks[3]).unwrap()); + assert_eq!(Some(vec![4.into()]), bc.body(blocks[4]).unwrap()); + } else { + for i in 0..5 { + assert_eq!(Some(vec![(i as u64).into()]), bc.body(blocks[i]).unwrap()); + } + } } - backend.commit_operation(op).unwrap(); - - let bc = backend.blockchain(); - assert_eq!(Some(vec![0.into()]), bc.body(blocks[0]).unwrap()); - assert_eq!(Some(vec![1.into()]), bc.body(blocks[1]).unwrap()); - assert_eq!(Some(vec![2.into()]), bc.body(blocks[2]).unwrap()); - assert_eq!(Some(vec![3.into()]), bc.body(blocks[3]).unwrap()); - assert_eq!(Some(vec![4.into()]), bc.body(blocks[4]).unwrap()); } #[test] - fn prune_blocks_on_finalize_with_fork_in_keep_all() { - let backend = Backend::::new_test_with_tx_storage(BlocksPruning::KeepAll, 10); - let mut blocks = Vec::new(); - let mut prev_hash = Default::default(); - for i in 0..5 { - let hash = insert_block( + fn prune_blocks_on_finalize_with_fork() { + sp_tracing::try_init_simple(); + + let pruning_modes = + vec![BlocksPruning::Some(2), BlocksPruning::KeepFinalized, BlocksPruning::KeepAll]; + + for pruning in pruning_modes { + let backend = Backend::::new_test_with_tx_storage(pruning, 10); + let mut blocks = Vec::new(); + let mut prev_hash = Default::default(); + for i in 0..5 { + let hash = insert_block( + &backend, + i, + prev_hash, + None, + Default::default(), + vec![i.into()], + None, + ) + .unwrap(); + blocks.push(hash); + prev_hash = hash; + } + + // insert a fork at block 2 + let fork_hash_root = + insert_block(&backend, 2, blocks[1], None, H256::random(), vec![2.into()], None) + .unwrap(); + insert_block( &backend, - i, - prev_hash, + 3, + fork_hash_root, None, - Default::default(), - vec![i.into()], + H256::random(), + vec![3.into(), 11.into()], None, ) .unwrap(); - blocks.push(hash); - prev_hash = hash; - } - - // insert a fork at block 2 - let fork_hash_root = insert_block( - &backend, - 2, - blocks[1], - None, - sp_core::H256::random(), - vec![2.into()], - None, - ) - .unwrap(); - insert_block( - &backend, - 3, - fork_hash_root, - None, - H256::random(), - vec![3.into(), 11.into()], - None, - ) - .unwrap(); - - let mut op = backend.begin_operation().unwrap(); - backend.begin_state_operation(&mut op, blocks[4]).unwrap(); - op.mark_head(blocks[4]).unwrap(); - backend.commit_operation(op).unwrap(); - - let bc = backend.blockchain(); - assert_eq!(Some(vec![2.into()]), bc.body(fork_hash_root).unwrap()); - - for i in 1..5 { let mut op = backend.begin_operation().unwrap(); - backend.begin_state_operation(&mut op, blocks[i]).unwrap(); - op.mark_finalized(blocks[i], None).unwrap(); + backend.begin_state_operation(&mut op, blocks[4]).unwrap(); + op.mark_head(blocks[4]).unwrap(); backend.commit_operation(op).unwrap(); - } - assert_eq!(Some(vec![0.into()]), bc.body(blocks[0]).unwrap()); - assert_eq!(Some(vec![1.into()]), bc.body(blocks[1]).unwrap()); - assert_eq!(Some(vec![2.into()]), bc.body(blocks[2]).unwrap()); - assert_eq!(Some(vec![3.into()]), bc.body(blocks[3]).unwrap()); - assert_eq!(Some(vec![4.into()]), bc.body(blocks[4]).unwrap()); + let bc = backend.blockchain(); + assert_eq!(Some(vec![2.into()]), bc.body(fork_hash_root).unwrap()); - assert_eq!(Some(vec![2.into()]), bc.body(fork_hash_root).unwrap()); - assert_eq!(bc.info().best_number, 4); - for i in 0..5 { - assert!(bc.hash(i).unwrap().is_some()); - } - } + for i in 1..5 { + let mut op = backend.begin_operation().unwrap(); + backend.begin_state_operation(&mut op, blocks[4]).unwrap(); + op.mark_finalized(blocks[i], None).unwrap(); + backend.commit_operation(op).unwrap(); + } - #[test] - fn prune_blocks_on_finalize_with_fork() { - let backend = Backend::::new_test_with_tx_storage(BlocksPruning::Some(2), 10); - let mut blocks = Vec::new(); - let mut prev_hash = Default::default(); - for i in 0..5 { - let hash = insert_block( - &backend, - i, - prev_hash, - None, - Default::default(), - vec![i.into()], - None, - ) - .unwrap(); - blocks.push(hash); - prev_hash = hash; - } + if matches!(pruning, BlocksPruning::Some(_)) { + assert_eq!(None, bc.body(blocks[0]).unwrap()); + assert_eq!(None, bc.body(blocks[1]).unwrap()); + assert_eq!(None, bc.body(blocks[2]).unwrap()); - // insert a fork at block 2 - let fork_hash_root = insert_block( - &backend, - 2, - blocks[1], - None, - sp_core::H256::random(), - vec![2.into()], - None, - ) - .unwrap(); - insert_block( - &backend, - 3, - fork_hash_root, - None, - H256::random(), - vec![3.into(), 11.into()], - None, - ) - .unwrap(); - let mut op = backend.begin_operation().unwrap(); - backend.begin_state_operation(&mut op, blocks[4]).unwrap(); - op.mark_head(blocks[4]).unwrap(); - backend.commit_operation(op).unwrap(); + assert_eq!(Some(vec![3.into()]), bc.body(blocks[3]).unwrap()); + assert_eq!(Some(vec![4.into()]), bc.body(blocks[4]).unwrap()); + } else { + for i in 0..5 { + assert_eq!(Some(vec![(i as u64).into()]), bc.body(blocks[i]).unwrap()); + } + } - for i in 1..5 { - let mut op = backend.begin_operation().unwrap(); - backend.begin_state_operation(&mut op, blocks[4]).unwrap(); - op.mark_finalized(blocks[i], None).unwrap(); - backend.commit_operation(op).unwrap(); - } + if matches!(pruning, BlocksPruning::KeepAll) { + assert_eq!(Some(vec![2.into()]), bc.body(fork_hash_root).unwrap()); + } else { + assert_eq!(None, bc.body(fork_hash_root).unwrap()); + } - let bc = backend.blockchain(); - assert_eq!(None, bc.body(blocks[0]).unwrap()); - assert_eq!(None, bc.body(blocks[1]).unwrap()); - assert_eq!(None, bc.body(blocks[2]).unwrap()); - assert_eq!(Some(vec![3.into()]), bc.body(blocks[3]).unwrap()); - assert_eq!(Some(vec![4.into()]), bc.body(blocks[4]).unwrap()); + assert_eq!(bc.info().best_number, 4); + for i in 0..5 { + assert!(bc.hash(i).unwrap().is_some()); + } + } } #[test] @@ -3841,13 +3787,7 @@ pub(crate) mod tests { assert!(matches!(backend.commit_operation(op), Err(sp_blockchain::Error::SetHeadTooOld))); // Insert 2 as best again. - let header = Header { - number: 2, - parent_hash: block1, - state_root: BlakeTwo256::trie_root(Vec::new(), StateVersion::V1), - digest: Default::default(), - extrinsics_root: Default::default(), - }; + let header = backend.blockchain().header(block2).unwrap().unwrap(); let mut op = backend.begin_operation().unwrap(); op.set_block_data(header, None, None, None, NewBlockState::Best).unwrap(); backend.commit_operation(op).unwrap(); @@ -3864,13 +3804,7 @@ pub(crate) mod tests { assert_eq!(backend.blockchain().info().finalized_hash, block0); // Insert 1 as final again. - let header = Header { - number: 1, - parent_hash: block0, - state_root: BlakeTwo256::trie_root(Vec::new(), StateVersion::V1), - digest: Default::default(), - extrinsics_root: Default::default(), - }; + let header = backend.blockchain().header(block1).unwrap().unwrap(); let mut op = backend.begin_operation().unwrap(); op.set_block_data(header, None, None, None, NewBlockState::Final).unwrap(); @@ -4021,7 +3955,8 @@ pub(crate) mod tests { #[test] fn force_delayed_canonicalize_waiting_for_blocks_to_be_finalized() { - let pruning_modes = [BlocksPruning::Some(10), BlocksPruning::KeepAll]; + let pruning_modes = + [BlocksPruning::Some(10), BlocksPruning::KeepAll, BlocksPruning::KeepFinalized]; for pruning_mode in pruning_modes { eprintln!("Running with pruning mode: {:?}", pruning_mode); diff --git a/client/state-db/src/lib.rs b/client/state-db/src/lib.rs index 1befd6dff..6186a45f3 100644 --- a/client/state-db/src/lib.rs +++ b/client/state-db/src/lib.rs @@ -152,6 +152,7 @@ impl From for Error { } /// Pinning error type. +#[derive(Debug)] pub enum PinError { /// Trying to pin invalid block. InvalidBlock, @@ -389,7 +390,8 @@ impl StateDbSync { } } else { match self.pruning.as_ref() { - None => IsPruned::NotPruned, + // We don't know for sure. + None => IsPruned::MaybePruned, Some(pruning) => match pruning.have_block(hash, number) { HaveBlock::No => IsPruned::Pruned, HaveBlock::Yes => IsPruned::NotPruned, @@ -457,13 +459,14 @@ impl StateDbSync { PruningMode::ArchiveAll => Ok(()), PruningMode::ArchiveCanonical | PruningMode::Constrained(_) => { let have_block = self.non_canonical.have_block(hash) || - self.pruning.as_ref().map_or(false, |pruning| { - match pruning.have_block(hash, number) { + self.pruning.as_ref().map_or_else( + || hint(), + |pruning| match pruning.have_block(hash, number) { HaveBlock::No => false, HaveBlock::Yes => true, HaveBlock::Maybe => hint(), - } - }); + }, + ); if have_block { let refs = self.pinned.entry(hash.clone()).or_default(); if *refs == 0 { @@ -642,7 +645,7 @@ impl StateDb { /// Check if block is pruned away. pub fn is_pruned(&self, hash: &BlockHash, number: u64) -> IsPruned { - return self.db.read().is_pruned(hash, number) + self.db.read().is_pruned(hash, number) } /// Reset in-memory changes to the last disk-backed state. From 1cc2a007290ce32314a17e7d03af7b986e0630dc Mon Sep 17 00:00:00 2001 From: Oliver Tale-Yazdi Date: Mon, 13 Feb 2023 17:09:34 +0100 Subject: [PATCH 112/558] subkey: only decode hex if requested, CLI `0x` prefixed hex for all `stdout` (#13258) * Only decode hex if requested Signed-off-by: Oliver Tale-Yazdi * Cleanup code Signed-off-by: Oliver Tale-Yazdi * Add some tests Signed-off-by: Oliver Tale-Yazdi * Add license Signed-off-by: Oliver Tale-Yazdi * Docs Signed-off-by: Oliver Tale-Yazdi * Clippy Signed-off-by: Oliver Tale-Yazdi * bump version, breaking (tiny) change in output. * Move integration tests to own folder Signed-off-by: Oliver Tale-Yazdi --------- Signed-off-by: Oliver Tale-Yazdi Co-authored-by: Dan Shields --- Cargo.lock | 2 +- bin/utils/subkey/Cargo.toml | 2 +- client/cli/src/commands/mod.rs | 1 + client/cli/src/commands/sign.rs | 75 +++++++--- client/cli/src/commands/test/mod.rs | 21 +++ client/cli/src/commands/test/sig_verify.rs | 152 +++++++++++++++++++++ client/cli/src/commands/utils.rs | 19 +-- client/cli/src/commands/verify.rs | 69 ++++++++-- client/cli/src/params/message_params.rs | 121 ++++++++++++++++ client/cli/src/params/mod.rs | 3 +- 10 files changed, 412 insertions(+), 53 deletions(-) create mode 100644 client/cli/src/commands/test/mod.rs create mode 100644 client/cli/src/commands/test/sig_verify.rs create mode 100644 client/cli/src/params/message_params.rs diff --git a/Cargo.lock b/Cargo.lock index 3e5ab65c0..ad3267984 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -10560,7 +10560,7 @@ dependencies = [ [[package]] name = "subkey" -version = "2.0.2" +version = "3.0.0" dependencies = [ "clap 4.1.4", "sc-cli", diff --git a/bin/utils/subkey/Cargo.toml b/bin/utils/subkey/Cargo.toml index 77c323821..5d2d0f777 100644 --- a/bin/utils/subkey/Cargo.toml +++ b/bin/utils/subkey/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "subkey" -version = "2.0.2" +version = "3.0.0" authors = ["Parity Technologies "] edition = "2021" license = "GPL-3.0-or-later WITH Classpath-exception-2.0" diff --git a/client/cli/src/commands/mod.rs b/client/cli/src/commands/mod.rs index 8e84afa34..eeb9f3be3 100644 --- a/client/cli/src/commands/mod.rs +++ b/client/cli/src/commands/mod.rs @@ -31,6 +31,7 @@ mod purge_chain_cmd; mod revert_cmd; mod run_cmd; mod sign; +mod test; pub mod utils; mod vanity; mod verify; diff --git a/client/cli/src/commands/sign.rs b/client/cli/src/commands/sign.rs index 2c3ff3a15..b3da31e56 100644 --- a/client/cli/src/commands/sign.rs +++ b/client/cli/src/commands/sign.rs @@ -17,9 +17,13 @@ // along with this program. If not, see . //! Implementation of the `sign` subcommand -use crate::{error, utils, with_crypto_scheme, CryptoSchemeFlag, KeystoreParams}; +use crate::{ + error, params::MessageParams, utils, with_crypto_scheme, CryptoSchemeFlag, KeystoreParams, +}; +use array_bytes::bytes2hex; use clap::Parser; use sp_core::crypto::SecretString; +use std::io::{BufRead, Write}; /// The `sign` command #[derive(Debug, Clone, Parser)] @@ -31,14 +35,9 @@ pub struct SignCmd { #[arg(long)] suri: Option, - /// Message to sign, if not provided you will be prompted to - /// pass the message via STDIN - #[arg(long)] - message: Option, - - /// The message on STDIN is hex-encoded data - #[arg(long)] - hex: bool, + #[allow(missing_docs)] + #[clap(flatten)] + pub message_params: MessageParams, #[allow(missing_docs)] #[clap(flatten)] @@ -52,15 +51,26 @@ pub struct SignCmd { impl SignCmd { /// Run the command pub fn run(&self) -> error::Result<()> { - let message = utils::read_message(self.message.as_ref(), self.hex)?; + let sig = self.sign(|| std::io::stdin().lock())?; + std::io::stdout().lock().write_all(sig.as_bytes())?; + Ok(()) + } + + /// Sign a message. + /// + /// The message can either be provided as immediate argument via CLI or otherwise read from the + /// reader created by `create_reader`. The reader will only be created in case that the message + /// is not passed as immediate. + pub(crate) fn sign(&self, create_reader: F) -> error::Result + where + R: BufRead, + F: FnOnce() -> R, + { + let message = self.message_params.message_from(create_reader)?; let suri = utils::read_uri(self.suri.as_ref())?; let password = self.keystore_params.read_password()?; - let signature = - with_crypto_scheme!(self.crypto_scheme.scheme, sign(&suri, password, message))?; - - println!("{}", signature); - Ok(()) + with_crypto_scheme!(self.crypto_scheme.scheme, sign(&suri, password, message)) } } @@ -70,26 +80,47 @@ fn sign( message: Vec, ) -> error::Result { let pair = utils::pair_from_suri::

(suri, password)?; - Ok(array_bytes::bytes2hex("", pair.sign(&message).as_ref())) + Ok(bytes2hex("0x", pair.sign(&message).as_ref())) } #[cfg(test)] mod test { use super::*; + const SEED: &str = "0xe5be9a5092b81bca64be81d212e7f2f9eba183bb7a90954f7b76361f6edb5c0a"; + #[test] - fn sign() { - let seed = "0xad1fb77243b536b90cfe5f0d351ab1b1ac40e3890b41dc64f766ee56340cfca5"; + fn sign_arg() { + let cmd = SignCmd::parse_from(&[ + "sign", + "--suri", + &SEED, + "--message", + &SEED, + "--password", + "12345", + "--hex", + ]); + let sig = cmd.sign(|| std::io::stdin().lock()).expect("Must sign"); + + assert!(sig.starts_with("0x"), "Signature must start with 0x"); + assert!(array_bytes::hex2bytes(&sig).is_ok(), "Signature is valid hex"); + } - let sign = SignCmd::parse_from(&[ + #[test] + fn sign_stdin() { + let cmd = SignCmd::parse_from(&[ "sign", "--suri", - seed, + SEED, "--message", - &seed[2..], + &SEED, "--password", "12345", ]); - assert!(sign.run().is_ok()); + let sig = cmd.sign(|| SEED.as_bytes()).expect("Must sign"); + + assert!(sig.starts_with("0x"), "Signature must start with 0x"); + assert!(array_bytes::hex2bytes(&sig).is_ok(), "Signature is valid hex"); } } diff --git a/client/cli/src/commands/test/mod.rs b/client/cli/src/commands/test/mod.rs new file mode 100644 index 000000000..762918f70 --- /dev/null +++ b/client/cli/src/commands/test/mod.rs @@ -0,0 +1,21 @@ +// This file is part of Substrate. + +// Copyright (C) 2023 Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +//! Integration tests for subkey commands. + +mod sig_verify; diff --git a/client/cli/src/commands/test/sig_verify.rs b/client/cli/src/commands/test/sig_verify.rs new file mode 100644 index 000000000..8d7247480 --- /dev/null +++ b/client/cli/src/commands/test/sig_verify.rs @@ -0,0 +1,152 @@ +// This file is part of Substrate. + +// Copyright (C) 2023 Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +#![cfg(test)] + +//! Integration test that the `sign` and `verify` sub-commands work together. + +use crate::*; +use clap::Parser; + +const SEED: &str = "0xe5be9a5092b81bca64be81d212e7f2f9eba183bb7a90954f7b76361f6edb5c0a"; +const ALICE: &str = "5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY"; +const BOB: &str = "5FHneW46xGXgs5mUiveU4sbTyGBzmstUspZC92UhjJM694ty"; + +/// Sign a valid UFT-8 message which can be `hex` and passed either via `stdin` or as an argument. +fn sign(msg: &str, hex: bool, stdin: bool) -> String { + sign_raw(msg.as_bytes(), hex, stdin) +} + +/// Sign a raw message which can be `hex` and passed either via `stdin` or as an argument. +fn sign_raw(msg: &[u8], hex: bool, stdin: bool) -> String { + let mut args = vec!["sign", "--suri", SEED]; + if !stdin { + args.push("--message"); + args.push(std::str::from_utf8(msg).expect("Can only pass valid UTF-8 as arg")); + } + if hex { + args.push("--hex"); + } + let cmd = SignCmd::parse_from(&args); + cmd.sign(|| msg).expect("Static data is good; Must sign; qed") +} + +/// Verify a valid UFT-8 message which can be `hex` and passed either via `stdin` or as an argument. +fn verify(msg: &str, hex: bool, stdin: bool, who: &str, sig: &str) -> bool { + verify_raw(msg.as_bytes(), hex, stdin, who, sig) +} + +/// Verify a raw message which can be `hex` and passed either via `stdin` or as an argument. +fn verify_raw(msg: &[u8], hex: bool, stdin: bool, who: &str, sig: &str) -> bool { + let mut args = vec!["verify", sig, who]; + if !stdin { + args.push("--message"); + args.push(std::str::from_utf8(msg).expect("Can only pass valid UTF-8 as arg")); + } + if hex { + args.push("--hex"); + } + let cmd = VerifyCmd::parse_from(&args); + cmd.verify(|| msg).is_ok() +} + +/// Test that sig/verify works with UTF-8 bytes passed as arg. +#[test] +fn sig_verify_arg_utf8_work() { + let sig = sign("Something", false, false); + + assert!(verify("Something", false, false, ALICE, &sig)); + assert!(!verify("Something", false, false, BOB, &sig)); + + assert!(!verify("Wrong", false, false, ALICE, &sig)); + assert!(!verify("Not hex", true, false, ALICE, &sig)); + assert!(!verify("0x1234", true, false, ALICE, &sig)); + assert!(!verify("Wrong", false, false, BOB, &sig)); + assert!(!verify("Not hex", true, false, BOB, &sig)); + assert!(!verify("0x1234", true, false, BOB, &sig)); +} + +/// Test that sig/verify works with UTF-8 bytes passed via stdin. +#[test] +fn sig_verify_stdin_utf8_work() { + let sig = sign("Something", false, true); + + assert!(verify("Something", false, true, ALICE, &sig)); + assert!(!verify("Something", false, true, BOB, &sig)); + + assert!(!verify("Wrong", false, true, ALICE, &sig)); + assert!(!verify("Not hex", true, true, ALICE, &sig)); + assert!(!verify("0x1234", true, true, ALICE, &sig)); + assert!(!verify("Wrong", false, true, BOB, &sig)); + assert!(!verify("Not hex", true, true, BOB, &sig)); + assert!(!verify("0x1234", true, true, BOB, &sig)); +} + +/// Test that sig/verify works with hex bytes passed as arg. +#[test] +fn sig_verify_arg_hex_work() { + let sig = sign("0xaabbcc", true, false); + + assert!(verify("0xaabbcc", true, false, ALICE, &sig)); + assert!(verify("aabBcc", true, false, ALICE, &sig)); + assert!(verify("0xaAbbCC", true, false, ALICE, &sig)); + assert!(!verify("0xaabbcc", true, false, BOB, &sig)); + + assert!(!verify("0xaabbcc", false, false, ALICE, &sig)); +} + +/// Test that sig/verify works with hex bytes passed via stdin. +#[test] +fn sig_verify_stdin_hex_work() { + let sig = sign("0xaabbcc", true, true); + + assert!(verify("0xaabbcc", true, true, ALICE, &sig)); + assert!(verify("aabBcc", true, true, ALICE, &sig)); + assert!(verify("0xaAbbCC", true, true, ALICE, &sig)); + assert!(!verify("0xaabbcc", true, true, BOB, &sig)); + + assert!(!verify("0xaabbcc", false, true, ALICE, &sig)); +} + +/// Test that sig/verify works with random bytes. +#[test] +fn sig_verify_stdin_non_utf8_work() { + use rand::RngCore; + let mut rng = rand::thread_rng(); + + for _ in 0..100 { + let mut raw = [0u8; 32]; + rng.fill_bytes(&mut raw); + let sig = sign_raw(&raw, false, true); + + assert!(verify_raw(&raw, false, true, ALICE, &sig)); + assert!(!verify_raw(&raw, false, true, BOB, &sig)); + } +} + +/// Test that sig/verify works with invalid UTF-8 bytes. +#[test] +fn sig_verify_stdin_invalid_utf8_work() { + let raw = vec![192u8, 193]; + assert!(String::from_utf8(raw.clone()).is_err(), "Must be invalid UTF-8"); + + let sig = sign_raw(&raw, false, true); + + assert!(verify_raw(&raw, false, true, ALICE, &sig)); + assert!(!verify_raw(&raw, false, true, BOB, &sig)); +} diff --git a/client/cli/src/commands/utils.rs b/client/cli/src/commands/utils.rs index 1ce2b2322..8ced185d7 100644 --- a/client/cli/src/commands/utils.rs +++ b/client/cli/src/commands/utils.rs @@ -31,7 +31,7 @@ use sp_core::{ Pair, }; use sp_runtime::{traits::IdentifyAccount, MultiSigner}; -use std::{io::Read, path::PathBuf}; +use std::path::PathBuf; /// Public key type for Runtime pub type PublicFor

=

`. A user of this crate may statically assert that this can never happen and safely +/// `expect` this to return `Ok`. +pub fn approval_voting( + to_elect: usize, + candidates: Vec, + voters: Vec<(AccountId, VoteWeight, impl IntoIterator)>, +) -> Result, crate::Error> { + let to_elect = to_elect.min(candidates.len()); + + let (mut candidates, mut voters) = setup_inputs(candidates, voters); + + candidates.sort_by_key(|c| Reverse(c.borrow().approval_stake)); + + let winners = candidates + .into_iter() + .take(to_elect) + .map(|w| { + w.borrow_mut().elected = true; + w + }) + .map(|w_ptr| (w_ptr.borrow().who.clone(), w_ptr.borrow().approval_stake)) + .collect(); + + for voter in &mut voters { + for edge in &mut voter.edges { + if edge.candidate.borrow().elected { + edge.weight = voter.budget + } else { + edge.weight = Zero::zero() + } + } + } + + let assignments = voters.into_iter().filter_map(|v| v.into_assignment()).collect::>(); + + Ok(ElectionResult { winners, assignments }) +} diff --git a/primitives/npos-elections/src/lib.rs b/primitives/npos-elections/src/lib.rs index 716c4b283..a1a38b9b0 100644 --- a/primitives/npos-elections/src/lib.rs +++ b/primitives/npos-elections/src/lib.rs @@ -25,6 +25,8 @@ //! - [`balance`](balancing::balance): Implements the star balancing algorithm. This iterative //! process can push a solution toward being more "balanced", which in turn can increase its //! score. +//! - [`approval_voting`](approval_voting::approval_voting): Implements an approval voting electoral +//! system where voters can back multiple candidates with the same stake. //! //! ### Terminology //! @@ -90,6 +92,7 @@ mod mock; #[cfg(test)] mod tests; +pub mod approval_voting; mod assignments; pub mod balancing; pub mod helpers; @@ -100,6 +103,7 @@ pub mod pjr; pub mod reduce; pub mod traits; +pub use approval_voting::*; pub use assignments::{Assignment, StakedAssignment}; pub use balancing::*; pub use helpers::*; diff --git a/primitives/npos-elections/src/phragmen.rs b/primitives/npos-elections/src/phragmen.rs index c3578065f..b7204ce1c 100644 --- a/primitives/npos-elections/src/phragmen.rs +++ b/primitives/npos-elections/src/phragmen.rs @@ -57,7 +57,7 @@ const DEN: ExtendedBalance = ExtendedBalance::max_value(); /// - The returning weight distribution is _normalized_, meaning that it is guaranteed that the sum /// of the ratios in each voter's distribution sums up to exactly `P::one()`. /// -/// This can only fail of the normalization fails. This can happen if for any of the resulting +/// This can only fail if the normalization fails. This can happen if for any of the resulting /// assignments, `assignment.distribution.map(|p| p.deconstruct()).sum()` fails to fit inside /// `UpperOf

`. A user of this crate may statically assert that this can never happen and safely /// `expect` this to return `Ok`. diff --git a/primitives/npos-elections/src/tests.rs b/primitives/npos-elections/src/tests.rs index 72ae9a022..5fea4c250 100644 --- a/primitives/npos-elections/src/tests.rs +++ b/primitives/npos-elections/src/tests.rs @@ -18,12 +18,57 @@ //! Tests for npos-elections. use crate::{ - balancing, helpers::*, mock::*, seq_phragmen, seq_phragmen_core, setup_inputs, to_support_map, - Assignment, BalancingConfig, ElectionResult, ExtendedBalance, StakedAssignment, Support, Voter, + approval_voting::*, balancing, helpers::*, mock::*, seq_phragmen, seq_phragmen_core, + setup_inputs, to_support_map, Assignment, BalancingConfig, ElectionResult, ExtendedBalance, + StakedAssignment, Support, Voter, }; use sp_arithmetic::{PerU16, Perbill, Percent, Permill}; use substrate_test_utils::assert_eq_uvec; +#[test] +fn approval_voting_works() { + let candidates = vec![1, 2, 3, 4]; + let voters = vec![(10, vec![1, 2]), (20, vec![1, 2]), (30, vec![1, 2, 3]), (40, vec![4])]; + let stake_of = create_stake_of(&[(10, 10), (20, 20), (30, 30), (40, 40)]); + + let voters = voters + .iter() + .map(|(ref v, ref vs)| (*v, stake_of(v), vs.clone())) + .collect::>(); + + let ElectionResult::<_, Perbill> { winners, assignments } = + approval_voting(3, candidates, voters).unwrap(); + + assert_eq_uvec!(winners, vec![(1, 60), (2, 60), (4, 40)]); + assert_eq_uvec!( + assignments, + vec![ + Assignment { + who: 10u64, + distribution: vec![ + (1, Perbill::from_percent(100)), + (2, Perbill::from_percent(100)) + ] + }, + Assignment { + who: 20u64, + distribution: vec![ + (1, Perbill::from_percent(100)), + (2, Perbill::from_percent(100)) + ] + }, + Assignment { + who: 30u64, + distribution: vec![ + (1, Perbill::from_percent(100)), + (2, Perbill::from_percent(100)) + ] + }, + Assignment { who: 40u64, distribution: vec![(4, Perbill::from_percent(100))] }, + ] + ); +} + #[test] fn float_phragmen_poc_works() { let candidates = vec![1, 2, 3]; diff --git a/scripts/run_all_benchmarks.sh b/scripts/run_all_benchmarks.sh index b632cb5c1..818a5df7f 100755 --- a/scripts/run_all_benchmarks.sh +++ b/scripts/run_all_benchmarks.sh @@ -67,8 +67,6 @@ SUBSTRATE=./target/production/substrate # Manually exclude some pallets. EXCLUDED_PALLETS=( - # Helper pallets - "pallet_election_provider_support_benchmarking" # Pallets without automatic benchmarking "pallet_babe" "pallet_grandpa" diff --git a/utils/frame/remote-externalities/Cargo.toml b/utils/frame/remote-externalities/Cargo.toml index 8611ae498..e55b64efb 100644 --- a/utils/frame/remote-externalities/Cargo.toml +++ b/utils/frame/remote-externalities/Cargo.toml @@ -25,7 +25,7 @@ futures = "0.3" [dev-dependencies] frame-support = { version = "4.0.0-dev", path = "../../../frame/support" } -pallet-elections-phragmen = { version = "5.0.0-dev", path = "../../../frame/elections-phragmen" } +pallet-elections = { version = "5.0.0-dev", path = "../../../frame/elections" } tracing-subscriber = { version = "0.3.16", features = ["env-filter"] } [features] From 919d5cf3d4b188265a3dcc7da2e1697c68dfcdec Mon Sep 17 00:00:00 2001 From: Sergejs Kostjucenko <85877331+sergejparity@users.noreply.github.com> Date: Thu, 23 Feb 2023 16:53:03 +0200 Subject: [PATCH 157/558] Move rules into common place (#13428) --- scripts/ci/docker/substrate.Dockerfile.README.md | 1 + scripts/ci/gitlab/pipeline/publish.yml | 12 ++++++++++-- 2 files changed, 11 insertions(+), 2 deletions(-) create mode 100644 scripts/ci/docker/substrate.Dockerfile.README.md diff --git a/scripts/ci/docker/substrate.Dockerfile.README.md b/scripts/ci/docker/substrate.Dockerfile.README.md new file mode 100644 index 000000000..557fd8f83 --- /dev/null +++ b/scripts/ci/docker/substrate.Dockerfile.README.md @@ -0,0 +1 @@ +# Substrate Docker Image \ No newline at end of file diff --git a/scripts/ci/gitlab/pipeline/publish.yml b/scripts/ci/gitlab/pipeline/publish.yml index 0d2e0cac9..3c280dec7 100644 --- a/scripts/ci/gitlab/pipeline/publish.yml +++ b/scripts/ci/gitlab/pipeline/publish.yml @@ -60,6 +60,10 @@ DOCKER_USERNAME: $Docker_Hub_User_Parity DOCKER_PASSWORD: $Docker_Hub_Pass_Parity README_FILEPATH: $CI_PROJECT_DIR/scripts/ci/docker/$PRODUCT.Dockerfile.README.md + rules: + - if: $CI_COMMIT_REF_NAME == "master" + changes: + - scripts/ci/docker/$PRODUCT.Dockerfile.README.md script: - cd / && sh entrypoint.sh @@ -81,6 +85,12 @@ publish-docker-substrate: variables: PRODUCT: substrate +publish-docker-description-substrate: + extends: .push-docker-image-description + variables: + PRODUCT: substrate + SHORT_DESCRIPTION: "Substrate Docker Image." + publish-docker-substrate-temporary: extends: .build-push-image-temporary needs: @@ -105,8 +115,6 @@ publish-docker-subkey: publish-docker-description-subkey: extends: .push-docker-image-description - needs: - - job: build-subkey-linux variables: PRODUCT: subkey SHORT_DESCRIPTION: "The subkey program is a key management utility for Substrate-based blockchains." From 91b4739e25efb2a06932703d60484fc6c857a909 Mon Sep 17 00:00:00 2001 From: Daniel Shiposha Date: Thu, 23 Feb 2023 15:06:12 +0000 Subject: [PATCH 158/558] Nfts attribute read interface (#13349) * feat: add custom and system attributes to Inspect * feat: add nfts runtime api * fix: pass std feature to runtime api * fix: api copyright Co-authored-by: joe petrowski <25483142+joepetrowski@users.noreply.github.com> --------- Co-authored-by: joe petrowski <25483142+joepetrowski@users.noreply.github.com> --- Cargo.lock | 11 +++ Cargo.toml | 1 + bin/node/runtime/Cargo.toml | 2 + bin/node/runtime/src/lib.rs | 52 ++++++++++++- frame/nfts/runtime-api/Cargo.toml | 28 +++++++ frame/nfts/runtime-api/README.md | 3 + frame/nfts/runtime-api/src/lib.rs | 57 ++++++++++++++ frame/nfts/src/impl_nonfungibles.rs | 32 +++++++- .../src/traits/tokens/nonfungible_v2.rs | 76 ++++++++++++++----- .../src/traits/tokens/nonfungibles_v2.rs | 59 ++++++++++++-- 10 files changed, 291 insertions(+), 30 deletions(-) create mode 100644 frame/nfts/runtime-api/Cargo.toml create mode 100644 frame/nfts/runtime-api/README.md create mode 100644 frame/nfts/runtime-api/src/lib.rs diff --git a/Cargo.lock b/Cargo.lock index e485a6512..afe2b46ee 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3558,6 +3558,7 @@ dependencies = [ "pallet-mmr", "pallet-multisig", "pallet-nfts", + "pallet-nfts-runtime-api", "pallet-nis", "pallet-nomination-pools", "pallet-nomination-pools-benchmarking", @@ -6038,6 +6039,16 @@ dependencies = [ "sp-std", ] +[[package]] +name = "pallet-nfts-runtime-api" +version = "4.0.0-dev" +dependencies = [ + "frame-support", + "pallet-nfts", + "parity-scale-codec", + "sp-api", +] + [[package]] name = "pallet-nicks" version = "4.0.0-dev" diff --git a/Cargo.toml b/Cargo.toml index 2ae924bf6..b9f89365c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -122,6 +122,7 @@ members = [ "frame/proxy", "frame/message-queue", "frame/nfts", + "frame/nfts/runtime-api", "frame/nomination-pools", "frame/nomination-pools/fuzzer", "frame/nomination-pools/benchmarking", diff --git a/bin/node/runtime/Cargo.toml b/bin/node/runtime/Cargo.toml index d7b42efd4..71fa9320a 100644 --- a/bin/node/runtime/Cargo.toml +++ b/bin/node/runtime/Cargo.toml @@ -79,6 +79,7 @@ pallet-message-queue = { version = "7.0.0-dev", default-features = false, path = pallet-mmr = { version = "4.0.0-dev", default-features = false, path = "../../../frame/merkle-mountain-range" } pallet-multisig = { version = "4.0.0-dev", default-features = false, path = "../../../frame/multisig" } pallet-nfts = { version = "4.0.0-dev", default-features = false, path = "../../../frame/nfts" } +pallet-nfts-runtime-api = { version = "4.0.0-dev", default-features = false, path = "../../../frame/nfts/runtime-api" } pallet-nomination-pools = { version = "1.0.0", default-features = false, path = "../../../frame/nomination-pools"} pallet-nomination-pools-benchmarking = { version = "1.0.0", default-features = false, optional = true, path = "../../../frame/nomination-pools/benchmarking" } pallet-nomination-pools-runtime-api = { version = "1.0.0-dev", default-features = false, path = "../../../frame/nomination-pools/runtime-api" } @@ -203,6 +204,7 @@ std = [ "pallet-recovery/std", "pallet-uniques/std", "pallet-nfts/std", + "pallet-nfts-runtime-api/std", "pallet-vesting/std", "log/std", "frame-try-runtime?/std", diff --git a/bin/node/runtime/src/lib.rs b/bin/node/runtime/src/lib.rs index 1ed43c211..a332127fb 100644 --- a/bin/node/runtime/src/lib.rs +++ b/bin/node/runtime/src/lib.rs @@ -33,10 +33,10 @@ use frame_support::{ pallet_prelude::Get, parameter_types, traits::{ - fungible::ItemOf, AsEnsureOriginWithArg, ConstBool, ConstU128, ConstU16, ConstU32, - Currency, EitherOfDiverse, EqualPrivilegeOnly, Everything, Imbalance, InstanceFilter, - KeyOwnerProofSystem, LockIdentifier, Nothing, OnUnbalanced, U128CurrencyToVote, - WithdrawReasons, + fungible::ItemOf, tokens::nonfungibles_v2::Inspect, AsEnsureOriginWithArg, ConstBool, + ConstU128, ConstU16, ConstU32, Currency, EitherOfDiverse, EqualPrivilegeOnly, Everything, + Imbalance, InstanceFilter, KeyOwnerProofSystem, LockIdentifier, Nothing, OnUnbalanced, + U128CurrencyToVote, WithdrawReasons, }, weights::{ constants::{ @@ -2187,6 +2187,50 @@ impl_runtime_apis! { } } + impl pallet_nfts_runtime_api::NftsApi for Runtime { + fn owner(collection: u32, item: u32) -> Option { + >::owner(&collection, &item) + } + + fn collection_owner(collection: u32) -> Option { + >::collection_owner(&collection) + } + + fn attribute( + collection: u32, + item: u32, + key: Vec, + ) -> Option> { + >::attribute(&collection, &item, &key) + } + + fn custom_attribute( + account: AccountId, + collection: u32, + item: u32, + key: Vec, + ) -> Option> { + >::custom_attribute( + &account, + &collection, + &item, + &key, + ) + } + + fn system_attribute( + collection: u32, + item: u32, + key: Vec, + ) -> Option> { + >::system_attribute(&collection, &item, &key) + } + + fn collection_attribute(collection: u32, key: Vec) -> Option> { + >::collection_attribute(&collection, &key) + } + } + impl pallet_mmr::primitives::MmrApi< Block, mmr::Hash, diff --git a/frame/nfts/runtime-api/Cargo.toml b/frame/nfts/runtime-api/Cargo.toml new file mode 100644 index 000000000..29d79e776 --- /dev/null +++ b/frame/nfts/runtime-api/Cargo.toml @@ -0,0 +1,28 @@ +[package] +name = "pallet-nfts-runtime-api" +version = "4.0.0-dev" +authors = ["Parity Technologies "] +edition = "2021" +license = "Apache-2.0" +homepage = "https://substrate.io" +repository = "https://github.com/paritytech/substrate/" +description = "Runtime API for the FRAME NFTs pallet." +readme = "README.md" + +[package.metadata.docs.rs] +targets = ["x86_64-unknown-linux-gnu"] + +[dependencies] +codec = { package = "parity-scale-codec", version = "3.2.2", default-features = false, features = ["derive"] } +frame-support = { version = "4.0.0-dev", default-features = false, path = "../../support" } +pallet-nfts = { version = "4.0.0-dev", default-features = false, path = "../../nfts" } +sp-api = { version = "4.0.0-dev", default-features = false, path = "../../../primitives/api" } + +[features] +default = ["std"] +std = [ + "codec/std", + "frame-support/std", + "pallet-nfts/std", + "sp-api/std", +] diff --git a/frame/nfts/runtime-api/README.md b/frame/nfts/runtime-api/README.md new file mode 100644 index 000000000..289036d2c --- /dev/null +++ b/frame/nfts/runtime-api/README.md @@ -0,0 +1,3 @@ +RPC runtime API for the FRAME NFTs pallet. + +License: Apache-2.0 diff --git a/frame/nfts/runtime-api/src/lib.rs b/frame/nfts/runtime-api/src/lib.rs new file mode 100644 index 000000000..0c23d1781 --- /dev/null +++ b/frame/nfts/runtime-api/src/lib.rs @@ -0,0 +1,57 @@ +// This file is part of Substrate. + +// Copyright (C) 2023 Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +//! Runtime API definition for the FRAME NFTs pallet. + +#![cfg_attr(not(feature = "std"), no_std)] + +use codec::{Decode, Encode}; +use frame_support::dispatch::Vec; + +sp_api::decl_runtime_apis! { + pub trait NftsApi + where + AccountId: Encode + Decode, + CollectionId: Encode, + ItemId: Encode, + { + fn owner(collection: CollectionId, item: ItemId) -> Option; + + fn collection_owner(collection: CollectionId) -> Option; + + fn attribute( + collection: CollectionId, + item: ItemId, + key: Vec, + ) -> Option>; + + fn custom_attribute( + account: AccountId, + collection: CollectionId, + item: ItemId, + key: Vec, + ) -> Option>; + + fn system_attribute( + collection: CollectionId, + item: ItemId, + key: Vec, + ) -> Option>; + + fn collection_attribute(collection: CollectionId, key: Vec) -> Option>; + } +} diff --git a/frame/nfts/src/impl_nonfungibles.rs b/frame/nfts/src/impl_nonfungibles.rs index ac02355ba..ef6bbe765 100644 --- a/frame/nfts/src/impl_nonfungibles.rs +++ b/frame/nfts/src/impl_nonfungibles.rs @@ -50,18 +50,48 @@ impl, I: 'static> Inspect<::AccountId> for Palle fn attribute( collection: &Self::CollectionId, item: &Self::ItemId, - namespace: &AttributeNamespace<::AccountId>, key: &[u8], ) -> Option> { if key.is_empty() { // We make the empty key map to the item metadata value. ItemMetadataOf::::get(collection, item).map(|m| m.data.into()) } else { + let namespace = AttributeNamespace::CollectionOwner; let key = BoundedSlice::<_, _>::try_from(key).ok()?; Attribute::::get((collection, Some(item), namespace, key)).map(|a| a.0.into()) } } + /// Returns the custom attribute value of `item` of `collection` corresponding to `key`. + /// + /// By default this is `None`; no attributes are defined. + fn custom_attribute( + account: &T::AccountId, + collection: &Self::CollectionId, + item: &Self::ItemId, + key: &[u8], + ) -> Option> { + let namespace = Account::::get((account, collection, item)) + .map(|_| AttributeNamespace::ItemOwner) + .unwrap_or_else(|| AttributeNamespace::Account(account.clone())); + + let key = BoundedSlice::<_, _>::try_from(key).ok()?; + Attribute::::get((collection, Some(item), namespace, key)).map(|a| a.0.into()) + } + + /// Returns the system attribute value of `item` of `collection` corresponding to `key`. + /// + /// By default this is `None`; no attributes are defined. + fn system_attribute( + collection: &Self::CollectionId, + item: &Self::ItemId, + key: &[u8], + ) -> Option> { + let namespace = AttributeNamespace::Pallet; + let key = BoundedSlice::<_, _>::try_from(key).ok()?; + Attribute::::get((collection, Some(item), namespace, key)).map(|a| a.0.into()) + } + /// Returns the attribute value of `item` of `collection` corresponding to `key`. /// /// When `key` is empty, we return the item metadata value. diff --git a/frame/support/src/traits/tokens/nonfungible_v2.rs b/frame/support/src/traits/tokens/nonfungible_v2.rs index 36ef9ab0a..175d94324 100644 --- a/frame/support/src/traits/tokens/nonfungible_v2.rs +++ b/frame/support/src/traits/tokens/nonfungible_v2.rs @@ -25,10 +25,7 @@ //! use. use super::nonfungibles_v2 as nonfungibles; -use crate::{ - dispatch::DispatchResult, - traits::{tokens::misc::AttributeNamespace, Get}, -}; +use crate::{dispatch::DispatchResult, traits::Get}; use codec::{Decode, Encode}; use sp_runtime::TokenError; use sp_std::prelude::*; @@ -45,23 +42,53 @@ pub trait Inspect { /// Returns the attribute value of `item` corresponding to `key`. /// /// By default this is `None`; no attributes are defined. - fn attribute( + fn attribute(_item: &Self::ItemId, _key: &[u8]) -> Option> { + None + } + + /// Returns the custom attribute value of `item` corresponding to `key`. + /// + /// By default this is `None`; no attributes are defined. + fn custom_attribute( + _account: &AccountId, _item: &Self::ItemId, - _namespace: &AttributeNamespace, _key: &[u8], ) -> Option> { None } + /// Returns the system attribute value of `item` corresponding to `key`. + /// + /// By default this is `None`; no attributes are defined. + fn system_attribute(_item: &Self::ItemId, _key: &[u8]) -> Option> { + None + } + /// Returns the strongly-typed attribute value of `item` corresponding to `key`. /// /// By default this just attempts to use `attribute`. - fn typed_attribute( + fn typed_attribute(item: &Self::ItemId, key: &K) -> Option { + key.using_encoded(|d| Self::attribute(item, d)) + .and_then(|v| V::decode(&mut &v[..]).ok()) + } + + /// Returns the strongly-typed custom attribute value of `item` corresponding to `key`. + /// + /// By default this just attempts to use `custom_attribute`. + fn typed_custom_attribute( + account: &AccountId, item: &Self::ItemId, - namespace: &AttributeNamespace, key: &K, ) -> Option { - key.using_encoded(|d| Self::attribute(item, namespace, d)) + key.using_encoded(|d| Self::custom_attribute(account, item, d)) + .and_then(|v| V::decode(&mut &v[..]).ok()) + } + + /// Returns the strongly-typed system attribute value of `item` corresponding to `key`. + /// + /// By default this just attempts to use `system_attribute`. + fn typed_system_attribute(item: &Self::ItemId, key: &K) -> Option { + key.using_encoded(|d| Self::system_attribute(item, d)) .and_then(|v| V::decode(&mut &v[..]).ok()) } @@ -167,19 +194,32 @@ impl< fn owner(item: &Self::ItemId) -> Option { >::owner(&A::get(), item) } - fn attribute( - item: &Self::ItemId, - namespace: &AttributeNamespace, - key: &[u8], - ) -> Option> { - >::attribute(&A::get(), item, namespace, key) + fn attribute(item: &Self::ItemId, key: &[u8]) -> Option> { + >::attribute(&A::get(), item, key) + } + fn custom_attribute(account: &AccountId, item: &Self::ItemId, key: &[u8]) -> Option> { + >::custom_attribute(account, &A::get(), item, key) } - fn typed_attribute( + fn system_attribute(item: &Self::ItemId, key: &[u8]) -> Option> { + >::system_attribute(&A::get(), item, key) + } + fn typed_attribute(item: &Self::ItemId, key: &K) -> Option { + >::typed_attribute(&A::get(), item, key) + } + fn typed_custom_attribute( + account: &AccountId, item: &Self::ItemId, - namespace: &AttributeNamespace, key: &K, ) -> Option { - >::typed_attribute(&A::get(), item, namespace, key) + >::typed_custom_attribute( + account, + &A::get(), + item, + key, + ) + } + fn typed_system_attribute(item: &Self::ItemId, key: &K) -> Option { + >::typed_system_attribute(&A::get(), item, key) } fn can_transfer(item: &Self::ItemId) -> bool { >::can_transfer(&A::get(), item) diff --git a/frame/support/src/traits/tokens/nonfungibles_v2.rs b/frame/support/src/traits/tokens/nonfungibles_v2.rs index 9331f9668..5deb0c568 100644 --- a/frame/support/src/traits/tokens/nonfungibles_v2.rs +++ b/frame/support/src/traits/tokens/nonfungibles_v2.rs @@ -27,10 +27,7 @@ //! Implementations of these traits may be converted to implementations of corresponding //! `nonfungible` traits by using the `nonfungible::ItemOf` type adapter. -use crate::{ - dispatch::{DispatchError, DispatchResult}, - traits::tokens::misc::AttributeNamespace, -}; +use crate::dispatch::{DispatchError, DispatchResult}; use codec::{Decode, Encode}; use sp_runtime::TokenError; use sp_std::prelude::*; @@ -61,7 +58,29 @@ pub trait Inspect { fn attribute( _collection: &Self::CollectionId, _item: &Self::ItemId, - _namespace: &AttributeNamespace, + _key: &[u8], + ) -> Option> { + None + } + + /// Returns the custom attribute value of `item` of `collection` corresponding to `key`. + /// + /// By default this is `None`; no attributes are defined. + fn custom_attribute( + _account: &AccountId, + _collection: &Self::CollectionId, + _item: &Self::ItemId, + _key: &[u8], + ) -> Option> { + None + } + + /// Returns the system attribute value of `item` of `collection` corresponding to `key`. + /// + /// By default this is `None`; no attributes are defined. + fn system_attribute( + _collection: &Self::CollectionId, + _item: &Self::ItemId, _key: &[u8], ) -> Option> { None @@ -74,10 +93,36 @@ pub trait Inspect { fn typed_attribute( collection: &Self::CollectionId, item: &Self::ItemId, - namespace: &AttributeNamespace, key: &K, ) -> Option { - key.using_encoded(|d| Self::attribute(collection, item, namespace, d)) + key.using_encoded(|d| Self::attribute(collection, item, d)) + .and_then(|v| V::decode(&mut &v[..]).ok()) + } + + /// Returns the strongly-typed custom attribute value of `item` of `collection` corresponding to + /// `key`. + /// + /// By default this just attempts to use `custom_attribute`. + fn typed_custom_attribute( + account: &AccountId, + collection: &Self::CollectionId, + item: &Self::ItemId, + key: &K, + ) -> Option { + key.using_encoded(|d| Self::custom_attribute(account, collection, item, d)) + .and_then(|v| V::decode(&mut &v[..]).ok()) + } + + /// Returns the strongly-typed system attribute value of `item` of `collection` corresponding to + /// `key`. + /// + /// By default this just attempts to use `system_attribute`. + fn typed_system_attribute( + collection: &Self::CollectionId, + item: &Self::ItemId, + key: &K, + ) -> Option { + key.using_encoded(|d| Self::system_attribute(collection, item, d)) .and_then(|v| V::decode(&mut &v[..]).ok()) } From fbddfbd76c60c6fda0024e8a44e82ad776033e4b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gon=C3=A7alo=20Pestana?= Date: Thu, 23 Feb 2023 18:15:08 +0000 Subject: [PATCH 159/558] Revert "Abstracts elections-phragmen pallet to use NposSolver (#12588)" (#13451) This reverts commit 4be5dd2937ab9fffd4f13772b547450a95a59018. --- Cargo.lock | 265 +++++---- Cargo.toml | 3 +- bin/node/runtime/Cargo.toml | 12 +- bin/node/runtime/src/lib.rs | 30 +- .../election-provider-multi-phase/Cargo.toml | 2 + frame/election-provider-support/Cargo.toml | 8 +- .../benchmarking/Cargo.toml | 37 ++ .../src/lib.rs} | 18 +- frame/election-provider-support/src/lib.rs | 26 - .../election-provider-support/src/onchain.rs | 30 +- .../election-provider-support/src/weights.rs | 147 ++--- .../CHANGELOG.md | 11 - .../Cargo.toml | 5 +- .../README.md | 6 +- .../src/benchmarking.rs | 57 +- .../src/lib.rs | 482 ++++++--------- .../src/migrations/mod.rs | 0 .../src/migrations/v3.rs | 0 .../src/migrations/v4.rs | 0 .../src/migrations/v5.rs | 0 frame/elections-phragmen/src/weights.rs | 559 ++++++++++++++++++ frame/elections/src/weights.rs | 397 ------------- .../npos-elections/src/approval_voting.rs | 79 --- primitives/npos-elections/src/lib.rs | 4 - primitives/npos-elections/src/phragmen.rs | 2 +- primitives/npos-elections/src/tests.rs | 49 +- scripts/run_all_benchmarks.sh | 2 + utils/frame/remote-externalities/Cargo.toml | 2 +- 28 files changed, 1027 insertions(+), 1206 deletions(-) create mode 100644 frame/election-provider-support/benchmarking/Cargo.toml rename frame/election-provider-support/{src/benchmarking.rs => benchmarking/src/lib.rs} (86%) rename frame/{elections => elections-phragmen}/CHANGELOG.md (80%) rename frame/{elections => elections-phragmen}/Cargo.toml (90%) rename frame/{elections => elections-phragmen}/README.md (94%) rename frame/{elections => elections-phragmen}/src/benchmarking.rs (85%) rename frame/{elections => elections-phragmen}/src/lib.rs (90%) rename frame/{elections => elections-phragmen}/src/migrations/mod.rs (100%) rename frame/{elections => elections-phragmen}/src/migrations/v3.rs (100%) rename frame/{elections => elections-phragmen}/src/migrations/v4.rs (100%) rename frame/{elections => elections-phragmen}/src/migrations/v5.rs (100%) create mode 100644 frame/elections-phragmen/src/weights.rs delete mode 100644 frame/elections/src/weights.rs delete mode 100644 primitives/npos-elections/src/approval_voting.rs diff --git a/Cargo.lock b/Cargo.lock index afe2b46ee..7cfde0570 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -27,7 +27,7 @@ version = "0.19.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a76fd60b23679b7d19bd066031410fb7e458ccc5e958eb5c325888ce4baedc97" dependencies = [ - "gimli 0.27.2", + "gimli 0.27.1", ] [[package]] @@ -246,7 +246,7 @@ dependencies = [ "num-traits", "rusticata-macros", "thiserror", - "time 0.3.19", + "time 0.3.17", ] [[package]] @@ -262,7 +262,7 @@ dependencies = [ "num-traits", "rusticata-macros", "thiserror", - "time 0.3.19", + "time 0.3.17", ] [[package]] @@ -358,20 +358,19 @@ dependencies = [ [[package]] name = "async-stream" -version = "0.3.4" +version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ad445822218ce64be7a341abfb0b1ea43b5c23aa83902542a4542e78309d8e5e" +checksum = "dad5c83079eae9969be7fadefe640a1c566901f05ff91ab221de4b6f68d9507e" dependencies = [ "async-stream-impl", "futures-core", - "pin-project-lite 0.2.9", ] [[package]] name = "async-stream-impl" -version = "0.3.4" +version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e4655ae1a7b0cdf149156f780c5bf3f1352bc53cbd9e0a361a7ef7b22947e965" +checksum = "10f203db73a71dfa2fb6dd22763990fa26f3d2625a6da2da900d23b87d26be27" dependencies = [ "proc-macro2", "quote", @@ -580,9 +579,9 @@ dependencies = [ [[package]] name = "bindgen" -version = "0.64.0" +version = "0.60.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c4243e6031260db77ede97ad86c27e501d646a27ab57b59a574f725d98ab1fb4" +checksum = "062dddbc1ba4aca46de6338e2bf87771414c335f7b2f2036e8f3e9befebf88e6" dependencies = [ "bitflags", "cexpr", @@ -595,7 +594,6 @@ dependencies = [ "regex", "rustc-hash", "shlex", - "syn", ] [[package]] @@ -627,24 +625,24 @@ dependencies = [ [[package]] name = "blake2b_simd" -version = "1.0.1" +version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3c2f0dc9a68c6317d884f97cc36cf5a3d20ba14ce404227df55e1af708ab04bc" +checksum = "72936ee4afc7f8f736d1c38383b56480b5497b4617b4a77bdbf1d2ababc76127" dependencies = [ "arrayref", "arrayvec 0.7.2", - "constant_time_eq", + "constant_time_eq 0.1.5", ] [[package]] name = "blake2s_simd" -version = "1.0.1" +version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6637f448b9e61dfadbdcbae9a885fadee1f3eaffb1f8d3c1965d3ade8bdfd44f" +checksum = "db539cc2b5f6003621f1cd9ef92d7ded8ea5232c7de0f9faa2de251cd98730d4" dependencies = [ "arrayref", "arrayvec 0.7.2", - "constant_time_eq", + "constant_time_eq 0.1.5", ] [[package]] @@ -657,7 +655,7 @@ dependencies = [ "arrayvec 0.7.2", "cc", "cfg-if", - "constant_time_eq", + "constant_time_eq 0.2.4", ] [[package]] @@ -717,9 +715,9 @@ checksum = "8d696c370c750c948ada61c69a0ee2cbbb9c50b1019ddb86d9317157a99c2cae" [[package]] name = "bounded-collections" -version = "0.1.5" +version = "0.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a071c348a5ef6da1d3a87166b408170b46002382b1dda83992b5c2208cefb370" +checksum = "de2aff4807e40f478132150d80b031f2461d88f061851afcab537d7600c24120" dependencies = [ "log", "parity-scale-codec", @@ -735,9 +733,9 @@ checksum = "771fe0050b883fcc3ea2359b1a96bcfbc090b7116eae7c3c512c7a083fdf23d3" [[package]] name = "bstr" -version = "1.3.0" +version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5ffdb39cb703212f3c11973452c2861b972f757b021158f3516ba10f2fa8b2c1" +checksum = "b7f0778972c64420fdedc63f09919c8a88bda7b25135357fd25a5d9f3257e832" dependencies = [ "memchr", "once_cell", @@ -919,7 +917,7 @@ name = "chain-spec-builder" version = "2.0.0" dependencies = [ "ansi_term", - "clap 4.1.6", + "clap 4.1.4", "node-cli", "rand 0.8.5", "sc-chain-spec", @@ -1012,9 +1010,9 @@ dependencies = [ [[package]] name = "clang-sys" -version = "1.6.0" +version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "77ed9a53e5d4d9c573ae844bfac6872b159cb1d1585a83b29e7a64b7eef7332a" +checksum = "fa2e27ae6ab525c3d369ded447057bca5438d86dc3a68f6faafb8269ba82ebf3" dependencies = [ "glob", "libc", @@ -1035,9 +1033,9 @@ dependencies = [ [[package]] name = "clap" -version = "4.1.6" +version = "4.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ec0b0588d44d4d63a87dbd75c136c166bbfd9a86a31cb89e09906521c7d3f5e3" +checksum = "f13b9c79b5d1dd500d20ef541215a6423c75829ef43117e1b4d17fd8af0b5d76" dependencies = [ "bitflags", "clap_derive", @@ -1050,11 +1048,11 @@ dependencies = [ [[package]] name = "clap_complete" -version = "4.1.2" +version = "4.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bd125be87bf4c255ebc50de0b7f4d2a6201e8ac3dc86e39c0ad081dc5e7236fe" +checksum = "3d6540eedc41f8a5a76cf3d8d458057dcdf817be4158a55b5f861f7a5483de75" dependencies = [ - "clap 4.1.6", + "clap 4.1.4", ] [[package]] @@ -1124,6 +1122,12 @@ version = "0.9.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cec318a675afcb6a1ea1d4340e2d377e56e47c266f28043ceccbf4412ddfdd3b" +[[package]] +name = "constant_time_eq" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "245097e9a4535ee1e3e3931fcfcd55a796a44c643e8596ff6566d68f09b87bbc" + [[package]] name = "constant_time_eq" version = "0.2.4" @@ -1511,9 +1515,9 @@ dependencies = [ [[package]] name = "cxx" -version = "1.0.91" +version = "1.0.89" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "86d3488e7665a7a483b57e25bdd90d0aeb2bc7608c8d0346acf2ad3f1caf1d62" +checksum = "bc831ee6a32dd495436e317595e639a587aa9907bef96fe6e6abc290ab6204e9" dependencies = [ "cc", "cxxbridge-flags", @@ -1523,9 +1527,9 @@ dependencies = [ [[package]] name = "cxx-build" -version = "1.0.91" +version = "1.0.89" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "48fcaf066a053a41a81dfb14d57d99738b767febb8b735c3016e469fac5da690" +checksum = "94331d54f1b1a8895cd81049f7eaaaef9d05a7dcb4d1fd08bf3ff0806246789d" dependencies = [ "cc", "codespan-reporting", @@ -1538,15 +1542,15 @@ dependencies = [ [[package]] name = "cxxbridge-flags" -version = "1.0.91" +version = "1.0.89" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a2ef98b8b717a829ca5603af80e1f9e2e48013ab227b68ef37872ef84ee479bf" +checksum = "48dcd35ba14ca9b40d6e4b4b39961f23d835dbb8eed74565ded361d93e1feb8a" [[package]] name = "cxxbridge-macro" -version = "1.0.91" +version = "1.0.89" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "086c685979a698443656e5cf7856c95c642295a38599f12fb1ff76fb28d19892" +checksum = "81bbeb29798b407ccd82a3324ade1a7286e0d29851475990b612670f6f5124d2" dependencies = [ "proc-macro2", "quote", @@ -2047,9 +2051,9 @@ checksum = "4443176a9f2c162692bd3d352d745ef9413eec5782a80d8fd6f8a1ac692a07f7" [[package]] name = "fastrand" -version = "1.9.0" +version = "1.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e51093e27b0797c359783294ca4f0a911c270184cb10f85783b118614a1501be" +checksum = "a7a407cfaa3385c4ae6b23e84623d48c2798d06e3e6a1878f7f59f17b3f86499" dependencies = [ "instant", ] @@ -2091,14 +2095,14 @@ dependencies = [ [[package]] name = "filetime" -version = "0.2.20" +version = "0.2.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8a3de6e8d11b22ff9edc6d916f890800597d60f8b2da1caf2955c274638d6412" +checksum = "4e884668cd0c7480504233e951174ddc3b382f7c2666e3b7310b5c4e7b0c37f9" dependencies = [ "cfg-if", "libc", "redox_syscall", - "windows-sys 0.45.0", + "windows-sys 0.42.0", ] [[package]] @@ -2218,7 +2222,7 @@ dependencies = [ "Inflector", "array-bytes", "chrono", - "clap 4.1.6", + "clap 4.1.4", "comfy-table", "frame-benchmarking", "frame-support", @@ -2291,7 +2295,6 @@ dependencies = [ name = "frame-election-provider-support" version = "4.0.0-dev" dependencies = [ - "frame-benchmarking", "frame-election-provider-solution-type", "frame-support", "frame-system", @@ -2310,7 +2313,7 @@ dependencies = [ name = "frame-election-solution-type-fuzzer" version = "2.0.0-alpha.5" dependencies = [ - "clap 4.1.6", + "clap 4.1.4", "frame-election-provider-solution-type", "frame-election-provider-support", "frame-support", @@ -2363,7 +2366,7 @@ dependencies = [ "frame-support", "futures", "log", - "pallet-elections", + "pallet-elections-phragmen", "parity-scale-codec", "serde", "sp-core", @@ -2793,9 +2796,9 @@ dependencies = [ [[package]] name = "gimli" -version = "0.27.2" +version = "0.27.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ad0a93d233ebf96623465aad4046a8d3aa4da22d4f4beba5388838c8a434bbb4" +checksum = "221996f774192f0f718773def8201c4ae31f02616a54ccfc2d358bb0e5cefdec" [[package]] name = "git2" @@ -2938,9 +2941,9 @@ dependencies = [ [[package]] name = "hermit-abi" -version = "0.3.1" +version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fed44880c466736ef9a5c5b5facefb5ed0785676d0c02d612db14e54f0d84286" +checksum = "856b5cb0902c2b6d65d5fd97dfa30f9b70c7538e770b98eab5ed52d8db923e01" [[package]] name = "hex" @@ -3032,9 +3035,9 @@ dependencies = [ [[package]] name = "http" -version = "0.2.9" +version = "0.2.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bd6effc99afb63425aff9b05836f029929e345a6148a14b7ecd5ab67af944482" +checksum = "75f43d41e26995c17e71ee126451dd3941010b0514a81a9d11f3b341debc2399" dependencies = [ "bytes", "fnv", @@ -3318,7 +3321,7 @@ version = "0.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "22e18b0a45d56fe973d6db23972bf5bc46f988a4a2385deac9cc29572f09daef" dependencies = [ - "hermit-abi 0.3.1", + "hermit-abi 0.3.0", "io-lifetimes", "rustix", "windows-sys 0.45.0", @@ -3544,7 +3547,8 @@ dependencies = [ "pallet-conviction-voting", "pallet-democracy", "pallet-election-provider-multi-phase", - "pallet-elections", + "pallet-election-provider-support-benchmarking", + "pallet-elections-phragmen", "pallet-fast-unstake", "pallet-glutton", "pallet-grandpa", @@ -4110,9 +4114,9 @@ dependencies = [ [[package]] name = "librocksdb-sys" -version = "0.8.3+7.4.4" +version = "0.8.0+7.4.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "557b255ff04123fcc176162f56ed0c9cd42d8f357cf55b3fabeb60f7413741b3" +checksum = "611804e4666a25136fcc5f8cf425ab4d26c7f74ea245ffe92ea23b85b6420b5d" dependencies = [ "bindgen", "bzip2-sys", @@ -4371,9 +4375,9 @@ dependencies = [ [[package]] name = "memmap2" -version = "0.5.9" +version = "0.5.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2af2c65375e552a67fe3829ca63e8a7c27a378a62824594f43b2851d682b5ec2" +checksum = "4b182332558b18d807c4ce1ca8ca983b34c3ee32765e47b3f0f69b90355cc1dc" dependencies = [ "libc", ] @@ -4441,14 +4445,14 @@ dependencies = [ [[package]] name = "mio" -version = "0.8.6" +version = "0.8.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5b9d9a46eff5b4ff64b45a9e316a6d1e0bc719ef429cbec4dc630684212bfdf9" +checksum = "e5d732bc30207a6423068df043e3d02e0735b155ad7ce1a6f76fe2baa5b158de" dependencies = [ "libc", "log", "wasi 0.11.0+wasi-snapshot-preview1", - "windows-sys 0.45.0", + "windows-sys 0.42.0", ] [[package]] @@ -4730,7 +4734,7 @@ name = "node-bench" version = "0.9.0-dev" dependencies = [ "array-bytes", - "clap 4.1.6", + "clap 4.1.4", "derive_more", "fs_extra", "futures", @@ -4767,7 +4771,7 @@ version = "3.0.0-dev" dependencies = [ "array-bytes", "assert_cmd", - "clap 4.1.6", + "clap 4.1.4", "clap_complete", "criterion", "frame-benchmarking-cli", @@ -4887,7 +4891,7 @@ dependencies = [ name = "node-inspect" version = "0.9.0-dev" dependencies = [ - "clap 4.1.6", + "clap 4.1.4", "parity-scale-codec", "sc-cli", "sc-client-api", @@ -4946,7 +4950,7 @@ dependencies = [ name = "node-runtime-generate-bags" version = "3.0.0" dependencies = [ - "clap 4.1.6", + "clap 4.1.4", "generate-bags", "kitchensink-runtime", ] @@ -4955,7 +4959,7 @@ dependencies = [ name = "node-template" version = "4.0.0-dev" dependencies = [ - "clap 4.1.6", + "clap 4.1.4", "frame-benchmarking", "frame-benchmarking-cli", "frame-system", @@ -5211,9 +5215,9 @@ dependencies = [ [[package]] name = "once_cell" -version = "1.17.1" +version = "1.17.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b7e5500299e16ebb147ae15a00a942af264cf3688f47923b8fc2cd5858f23ad3" +checksum = "6f61fba1741ea2b3d6a1e3178721804bb716a68a6aeba1149b5d52e3d464ea66" [[package]] name = "oorandom" @@ -5719,6 +5723,7 @@ dependencies = [ "frame-system", "log", "pallet-balances", + "pallet-election-provider-support-benchmarking", "parity-scale-codec", "parking_lot 0.12.1", "rand 0.8.5", @@ -5734,11 +5739,22 @@ dependencies = [ ] [[package]] -name = "pallet-elections" -version = "5.0.0-dev" +name = "pallet-election-provider-support-benchmarking" +version = "4.0.0-dev" dependencies = [ "frame-benchmarking", "frame-election-provider-support", + "frame-system", + "parity-scale-codec", + "sp-npos-elections", + "sp-runtime", +] + +[[package]] +name = "pallet-elections-phragmen" +version = "5.0.0-dev" +dependencies = [ + "frame-benchmarking", "frame-support", "frame-system", "log", @@ -6788,9 +6804,9 @@ dependencies = [ [[package]] name = "parity-scale-codec" -version = "3.4.0" +version = "3.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "637935964ff85a605d114591d4d2c13c5d1ba2806dae97cea6bf180238a749ac" +checksum = "c3840933452adf7b3b9145e27086a5a3376c619dca1a21b1e5a5af0d54979bed" dependencies = [ "arrayvec 0.7.2", "bitvec", @@ -6935,9 +6951,9 @@ checksum = "478c572c3d73181ff3c2539045f6eb99e5491218eae919370993b890cdbdd98e" [[package]] name = "pest" -version = "2.5.5" +version = "2.5.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "028accff104c4e513bad663bbcd2ad7cfd5304144404c31ed0a77ac103d00660" +checksum = "4ab62d2fa33726dbe6321cc97ef96d8cde531e3eeaf858a058de53a8a6d40d8f" dependencies = [ "thiserror", "ucd-trie", @@ -6945,9 +6961,9 @@ dependencies = [ [[package]] name = "pest_derive" -version = "2.5.5" +version = "2.5.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2ac3922aac69a40733080f53c1ce7f91dcf57e1a5f6c52f421fadec7fbdc4b69" +checksum = "8bf026e2d0581559db66d837fe5242320f525d85c76283c61f4d51a1238d65ea" dependencies = [ "pest", "pest_generator", @@ -6955,9 +6971,9 @@ dependencies = [ [[package]] name = "pest_generator" -version = "2.5.5" +version = "2.5.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d06646e185566b5961b4058dd107e0a7f56e77c3f484549fb119867773c0f202" +checksum = "2b27bd18aa01d91c8ed2b61ea23406a676b42d82609c6e2581fba42f0c15f17f" dependencies = [ "pest", "pest_meta", @@ -6968,9 +6984,9 @@ dependencies = [ [[package]] name = "pest_meta" -version = "2.5.5" +version = "2.5.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e6f60b2ba541577e2a0c307c8f39d1439108120eb7903adeb6497fa880c59616" +checksum = "9f02b677c1859756359fc9983c2e56a0237f18624a3789528804406b7e915e5d" dependencies = [ "once_cell", "pest", @@ -7542,7 +7558,7 @@ checksum = "6413f3de1edee53342e6138e75b56d32e7bc6e332b3bd62d497b1929d4cfbcdd" dependencies = [ "pem", "ring", - "time 0.3.19", + "time 0.3.17", "x509-parser 0.13.2", "yasna", ] @@ -7555,7 +7571,7 @@ checksum = "ffbe84efe2f38dea12e9bfc1f65377fdf03e53a18cb3b995faedf7934c7e785b" dependencies = [ "pem", "ring", - "time 0.3.19", + "time 0.3.17", "yasna", ] @@ -8029,7 +8045,7 @@ version = "0.10.0-dev" dependencies = [ "array-bytes", "chrono", - "clap 4.1.6", + "clap 4.1.4", "fdlimit", "futures", "futures-timer", @@ -9064,7 +9080,7 @@ dependencies = [ name = "sc-storage-monitor" version = "0.1.0" dependencies = [ - "clap 4.1.6", + "clap 4.1.4", "futures", "log", "nix 0.26.2", @@ -9460,9 +9476,9 @@ dependencies = [ [[package]] name = "serde_json" -version = "1.0.93" +version = "1.0.92" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cad406b69c91885b5107daf2c29572f6c8cdb3c66826821e286c533490c0bc76" +checksum = "7434af0dc1cbd59268aa98b4c22c131c0584d2232f6fb166efb993e2832e896a" dependencies = [ "itoa", "ryu", @@ -9545,9 +9561,9 @@ checksum = "43b2853a4d09f215c24cc5489c992ce46052d359b5109343cbafbf26bc62f8a3" [[package]] name = "signal-hook-registry" -version = "1.4.1" +version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d8229b473baa5980ac72ef434c4415e70c4b5e71b423043adb4ba059f89c99a1" +checksum = "e51e73328dc4ac0c7ccbda3a494dfa03df1de2f46018127f60c693f2648455b0" dependencies = [ "libc", ] @@ -9577,9 +9593,9 @@ dependencies = [ [[package]] name = "slab" -version = "0.4.8" +version = "0.4.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6528351c9bc8ab22353f9d776db39a20288e8d6c37ef8cfe3317cf875eecfc2d" +checksum = "4614a76b2a8be0058caa9dbbaf66d988527d86d003c11a94fbd335d7661edcef" dependencies = [ "autocfg", ] @@ -10123,7 +10139,7 @@ dependencies = [ name = "sp-npos-elections-fuzzer" version = "2.0.0-alpha.5" dependencies = [ - "clap 4.1.6", + "clap 4.1.4", "honggfuzz", "parity-scale-codec", "rand 0.8.5", @@ -10480,9 +10496,9 @@ checksum = "6e63cff320ae2c57904679ba7cb63280a3dc4613885beafb148ee7bf9aa9042d" [[package]] name = "spin" -version = "0.9.5" +version = "0.9.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7dccf47db1b41fa1573ed27ccf5e08e3ca771cb994f776668c5ebda893b248fc" +checksum = "7f6002a767bff9e83f8eeecf883ecb8011875a21ae8da43bffb817a57e78cc09" [[package]] name = "spki" @@ -10600,7 +10616,7 @@ dependencies = [ name = "subkey" version = "3.0.0" dependencies = [ - "clap 4.1.6", + "clap 4.1.4", "sc-cli", ] @@ -10628,7 +10644,7 @@ dependencies = [ name = "substrate-frame-cli" version = "4.0.0-dev" dependencies = [ - "clap 4.1.6", + "clap 4.1.4", "frame-support", "frame-system", "sc-cli", @@ -10938,9 +10954,9 @@ checksum = "55937e1799185b12863d447f42597ed69d9928686b8d88a1df17376a097d8369" [[package]] name = "target-lexicon" -version = "0.12.6" +version = "0.12.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8ae9980cab1db3fceee2f6c6f643d5d8de2997c58ee8d25fb0cc8a9e9e7348e5" +checksum = "9410d0f6853b1d94f0e519fb95df60f29d2c1eff2d921ffdf01a4c8a3b54f12d" [[package]] name = "tempfile" @@ -11005,11 +11021,10 @@ checksum = "3bf63baf9f5039dadc247375c29eb13706706cfde997d0330d05aa63a77d8820" [[package]] name = "thread_local" -version = "1.1.7" +version = "1.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3fdd6f064ccff2d6567adcb3873ca630700f00b5ad3f060c25b5dcfd9a4ce152" +checksum = "5516c27b78311c50bf42c071425c560ac799b11c30b31f87e3081965fe5e0180" dependencies = [ - "cfg-if", "once_cell", ] @@ -11045,9 +11060,9 @@ dependencies = [ [[package]] name = "time" -version = "0.3.19" +version = "0.3.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "53250a3b3fed8ff8fd988587d8925d26a83ac3845d9e03b220b37f34c2b8d6c2" +checksum = "a561bf4617eebd33bca6434b988f39ed798e527f51a1e797d0ee4f61c0a38376" dependencies = [ "itoa", "serde", @@ -11063,9 +11078,9 @@ checksum = "2e153e1f1acaef8acc537e68b44906d2db6436e2b35ac2c6b42640fff91f00fd" [[package]] name = "time-macros" -version = "0.2.7" +version = "0.2.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a460aeb8de6dcb0f381e1ee05f1cd56fcf5a5f6eb8187ff3d8f0b11078d38b7c" +checksum = "d967f99f534ca7e495c575c62638eebc2898a8c84c119b89e250477bc4ba16b2" dependencies = [ "time-core", ] @@ -11167,9 +11182,9 @@ dependencies = [ [[package]] name = "tokio-stream" -version = "0.1.12" +version = "0.1.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8fb52b74f05dbf495a8fba459fdc331812b96aa086d9eb78101fa0d4569c3313" +checksum = "d660770404473ccd7bc9f8b28494a811bc18542b915c0855c51e8f419d5223ce" dependencies = [ "futures-core", "pin-project-lite 0.2.9", @@ -11192,9 +11207,9 @@ dependencies = [ [[package]] name = "tokio-util" -version = "0.7.7" +version = "0.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5427d89453009325de0d8f342c9490009f76e999cb7672d77e46267448f7e6b2" +checksum = "0bb2e075f03b3d66d8d8785356224ba688d2906a371015e225beeb65ca92c740" dependencies = [ "bytes", "futures-core", @@ -11466,7 +11481,7 @@ name = "try-runtime-cli" version = "0.10.0-dev" dependencies = [ "async-trait", - "clap 4.1.6", + "clap 4.1.4", "frame-remote-externalities", "frame-try-runtime", "hex", @@ -11811,9 +11826,9 @@ checksum = "0046fef7e28c3804e5e38bfa31ea2a0f73905319b677e57ebe37e49358989b5d" [[package]] name = "wasm-encoder" -version = "0.24.0" +version = "0.22.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "704553b4d614a47080b4a457a976b3c16174b19ce95b931b847561b590dd09ba" +checksum = "9a584273ccc2d9311f1dd19dc3fb26054661fa3e373d53ede5d1144ba07a9acd" dependencies = [ "leb128", ] @@ -11909,7 +11924,7 @@ version = "0.20.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "01bf50edb2ea9d922aa75a7bf3c15e26a6c9e2d18c56e862b49737a582901729" dependencies = [ - "spin 0.9.5", + "spin 0.9.4", "wasmi_arena", "wasmi_core 0.5.0", "wasmparser-nostd", @@ -12154,9 +12169,9 @@ dependencies = [ [[package]] name = "wast" -version = "54.0.0" +version = "52.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f0d3df4a63b10958fe98ab9d7e9a57a7bc900209d2b4edd10535bfb0703e6516" +checksum = "15942180f265280eede7bc38b239e9770031d1821c02d905284216c645316430" dependencies = [ "leb128", "memchr", @@ -12166,9 +12181,9 @@ dependencies = [ [[package]] name = "wat" -version = "1.0.59" +version = "1.0.57" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3e9a7c7d177696d0548178c36e377d49eba54170e885801d4270e2d44e82ac84" +checksum = "37212100d4cbe6f0f6ff6e707f1e5a5b5b675f0451231ed9e4235e234e127ed3" dependencies = [ "wast", ] @@ -12238,7 +12253,7 @@ dependencies = [ "sha2 0.10.6", "stun", "thiserror", - "time 0.3.19", + "time 0.3.17", "tokio", "turn", "url", @@ -12311,9 +12326,9 @@ dependencies = [ [[package]] name = "webrtc-ice" -version = "0.9.1" +version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "465a03cc11e9a7d7b4f9f99870558fe37a102b65b93f8045392fef7c67b39e80" +checksum = "494483fbb2f5492620871fdc78b084aed8807377f6e3fe88b2e49f0a9c9c41d7" dependencies = [ "arc-swap", "async-trait", @@ -12671,7 +12686,7 @@ dependencies = [ "ring", "rusticata-macros", "thiserror", - "time 0.3.19", + "time 0.3.17", ] [[package]] @@ -12689,7 +12704,7 @@ dependencies = [ "oid-registry 0.6.1", "rusticata-macros", "thiserror", - "time 0.3.19", + "time 0.3.17", ] [[package]] @@ -12718,7 +12733,7 @@ version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "aed2e7a52e3744ab4d0c05c20aa065258e84c49fd4226f5191b2ed29712710b4" dependencies = [ - "time 0.3.19", + "time 0.3.17", ] [[package]] @@ -12763,9 +12778,9 @@ dependencies = [ [[package]] name = "zstd-sys" -version = "2.0.7+zstd.1.5.4" +version = "2.0.6+zstd.1.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "94509c3ba2fe55294d752b79842c530ccfab760192521df74a081a78d2b3c7f5" +checksum = "68a3f9792c0c3dc6c165840a75f47ae1f4da402c2d006881129579f6597e801b" dependencies = [ "cc", "libc", diff --git a/Cargo.toml b/Cargo.toml index b9f89365c..80dd2b90f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -97,9 +97,10 @@ members = [ "frame/democracy", "frame/fast-unstake", "frame/try-runtime", - "frame/elections", + "frame/elections-phragmen", "frame/election-provider-multi-phase", "frame/election-provider-support", + "frame/election-provider-support/benchmarking", "frame/election-provider-support/solution-type", "frame/election-provider-support/solution-type/fuzzer", "frame/examples/basic", diff --git a/bin/node/runtime/Cargo.toml b/bin/node/runtime/Cargo.toml index 71fa9320a..3df4596aa 100644 --- a/bin/node/runtime/Cargo.toml +++ b/bin/node/runtime/Cargo.toml @@ -66,7 +66,8 @@ pallet-contracts-primitives = { version = "7.0.0", default-features = false, pat pallet-conviction-voting = { version = "4.0.0-dev", default-features = false, path = "../../../frame/conviction-voting" } pallet-democracy = { version = "4.0.0-dev", default-features = false, path = "../../../frame/democracy" } pallet-election-provider-multi-phase = { version = "4.0.0-dev", default-features = false, path = "../../../frame/election-provider-multi-phase" } -pallet-elections = { version = "5.0.0-dev", default-features = false, path = "../../../frame/elections" } +pallet-election-provider-support-benchmarking = { version = "4.0.0-dev", default-features = false, path = "../../../frame/election-provider-support/benchmarking", optional = true } +pallet-elections-phragmen = { version = "5.0.0-dev", default-features = false, path = "../../../frame/elections-phragmen" } pallet-fast-unstake = { version = "4.0.0-dev", default-features = false, path = "../../../frame/fast-unstake" } pallet-nis = { version = "4.0.0-dev", default-features = false, path = "../../../frame/nis" } pallet-grandpa = { version = "4.0.0-dev", default-features = false, path = "../../../frame/grandpa" } @@ -124,6 +125,7 @@ with-tracing = ["frame-executive/with-tracing"] std = [ "pallet-whitelist/std", "pallet-offences-benchmarking?/std", + "pallet-election-provider-support-benchmarking?/std", "pallet-asset-tx-payment/std", "frame-system-benchmarking?/std", "frame-election-provider-support/std", @@ -144,7 +146,7 @@ std = [ "pallet-contracts-primitives/std", "pallet-conviction-voting/std", "pallet-democracy/std", - "pallet-elections/std", + "pallet-elections-phragmen/std", "pallet-fast-unstake/std", "frame-executive/std", "pallet-nis/std", @@ -218,7 +220,6 @@ runtime-benchmarks = [ "frame-benchmarking-pallet-pov/runtime-benchmarks", "frame-support/runtime-benchmarks", "frame-system/runtime-benchmarks", - "frame-election-provider-support/runtime-benchmarks", "sp-runtime/runtime-benchmarks", "pallet-alliance/runtime-benchmarks", "pallet-assets/runtime-benchmarks", @@ -232,7 +233,8 @@ runtime-benchmarks = [ "pallet-conviction-voting/runtime-benchmarks", "pallet-democracy/runtime-benchmarks", "pallet-election-provider-multi-phase/runtime-benchmarks", - "pallet-elections/runtime-benchmarks", + "pallet-election-provider-support-benchmarking/runtime-benchmarks", + "pallet-elections-phragmen/runtime-benchmarks", "pallet-fast-unstake/runtime-benchmarks", "pallet-nis/runtime-benchmarks", "pallet-grandpa/runtime-benchmarks", @@ -289,7 +291,7 @@ try-runtime = [ "pallet-conviction-voting/try-runtime", "pallet-democracy/try-runtime", "pallet-election-provider-multi-phase/try-runtime", - "pallet-elections/try-runtime", + "pallet-elections-phragmen/try-runtime", "pallet-fast-unstake/try-runtime", "pallet-nis/try-runtime", "pallet-grandpa/try-runtime", diff --git a/bin/node/runtime/src/lib.rs b/bin/node/runtime/src/lib.rs index a332127fb..a869d309f 100644 --- a/bin/node/runtime/src/lib.rs +++ b/bin/node/runtime/src/lib.rs @@ -24,8 +24,7 @@ use codec::{Decode, Encode, MaxEncodedLen}; use frame_election_provider_support::{ - onchain, weights::SubstrateWeight, ApprovalVoting, BalancingConfig, ElectionDataProvider, - SequentialPhragmen, VoteWeight, + onchain, BalancingConfig, ElectionDataProvider, SequentialPhragmen, VoteWeight, }; use frame_support::{ construct_runtime, @@ -1011,21 +1010,18 @@ parameter_types! { pub const TermDuration: BlockNumber = 7 * DAYS; pub const DesiredMembers: u32 = 13; pub const DesiredRunnersUp: u32 = 7; + pub const MaxVotesPerVoter: u32 = 16; pub const MaxVoters: u32 = 512; pub const MaxCandidates: u32 = 64; - pub const MaxVotesPerVoter: u32 = 16; - // The ElectionsPalletId parameter name was changed along with the renaming of the elections - // pallet, but we keep the same lock ID to prevent a migration from current runtimes. - // Related to https://github.com/paritytech/substrate/issues/8250 - pub const ElectionsPalletId: LockIdentifier = *b"phrelect"; + pub const ElectionsPhragmenPalletId: LockIdentifier = *b"phrelect"; } // Make sure that there are no more than `MaxMembers` members elected via elections-phragmen. const_assert!(DesiredMembers::get() <= CouncilMaxMembers::get()); -impl pallet_elections::Config for Runtime { +impl pallet_elections_phragmen::Config for Runtime { type RuntimeEvent = RuntimeEvent; - type PalletId = ElectionsPalletId; + type PalletId = ElectionsPhragmenPalletId; type Currency = Balances; type ChangeMembers = Council; // NOTE: this implies that council's genesis members cannot be set directly and must come from @@ -1043,9 +1039,7 @@ impl pallet_elections::Config for Runtime { type MaxVoters = MaxVoters; type MaxVotesPerVoter = MaxVotesPerVoter; type MaxCandidates = MaxCandidates; - type ElectionSolver = ApprovalVoting; - type SolverWeightInfo = SubstrateWeight; - type WeightInfo = pallet_elections::weights::SubstrateWeight; + type WeightInfo = pallet_elections_phragmen::weights::SubstrateWeight; } parameter_types! { @@ -1743,7 +1737,7 @@ construct_runtime!( Democracy: pallet_democracy, Council: pallet_collective::, TechnicalCommittee: pallet_collective::, - Elections: pallet_elections, + Elections: pallet_elections_phragmen, TechnicalMembership: pallet_membership::, Grandpa: pallet_grandpa, Treasury: pallet_treasury, @@ -1857,7 +1851,6 @@ mod benches { frame_benchmarking::define_benchmarks!( [frame_benchmarking, BaselineBench::] [frame_benchmarking_pallet_pov, Pov] - [frame_election_provider_support, EPSBench::] [pallet_alliance, Alliance] [pallet_assets, Assets] [pallet_babe, Babe] @@ -1870,7 +1863,8 @@ mod benches { [pallet_contracts, Contracts] [pallet_democracy, Democracy] [pallet_election_provider_multi_phase, ElectionProviderMultiPhase] - [pallet_elections, Elections] + [pallet_election_provider_support_benchmarking, EPSBench::] + [pallet_elections_phragmen, Elections] [pallet_fast_unstake, FastUnstake] [pallet_nis, Nis] [pallet_grandpa, Grandpa] @@ -2329,7 +2323,7 @@ impl_runtime_apis! { // which is why we need these two lines below. use pallet_session_benchmarking::Pallet as SessionBench; use pallet_offences_benchmarking::Pallet as OffencesBench; - use frame_election_provider_support::benchmarking::Pallet as EPSBench; + use pallet_election_provider_support_benchmarking::Pallet as EPSBench; use frame_system_benchmarking::Pallet as SystemBench; use baseline::Pallet as BaselineBench; use pallet_nomination_pools_benchmarking::Pallet as NominationPoolsBench; @@ -2352,14 +2346,14 @@ impl_runtime_apis! { // which is why we need these two lines below. use pallet_session_benchmarking::Pallet as SessionBench; use pallet_offences_benchmarking::Pallet as OffencesBench; - use frame_election_provider_support::benchmarking::Pallet as EPSBench; + use pallet_election_provider_support_benchmarking::Pallet as EPSBench; use frame_system_benchmarking::Pallet as SystemBench; use baseline::Pallet as BaselineBench; use pallet_nomination_pools_benchmarking::Pallet as NominationPoolsBench; impl pallet_session_benchmarking::Config for Runtime {} impl pallet_offences_benchmarking::Config for Runtime {} - impl frame_election_provider_support::benchmarking::Config for Runtime {} + impl pallet_election_provider_support_benchmarking::Config for Runtime {} impl frame_system_benchmarking::Config for Runtime {} impl baseline::Config for Runtime {} impl pallet_nomination_pools_benchmarking::Config for Runtime {} diff --git a/frame/election-provider-multi-phase/Cargo.toml b/frame/election-provider-multi-phase/Cargo.toml index 996f40feb..aa734850a 100644 --- a/frame/election-provider-multi-phase/Cargo.toml +++ b/frame/election-provider-multi-phase/Cargo.toml @@ -33,6 +33,7 @@ frame-election-provider-support = { version = "4.0.0-dev", default-features = fa # Optional imports for benchmarking frame-benchmarking = { version = "4.0.0-dev", default-features = false, path = "../benchmarking", optional = true } +pallet-election-provider-support-benchmarking = { version = "4.0.0-dev", default-features = false, path = "../election-provider-support/benchmarking", optional = true } rand = { version = "0.8.5", default-features = false, features = ["alloc", "small_rng"], optional = true } strum = { version = "0.24.1", default-features = false, features = ["derive"], optional = true } @@ -49,6 +50,7 @@ frame-benchmarking = { version = "4.0.0-dev", path = "../benchmarking" } [features] default = ["std"] std = [ + "pallet-election-provider-support-benchmarking?/std", "codec/std", "scale-info/std", "log/std", diff --git a/frame/election-provider-support/Cargo.toml b/frame/election-provider-support/Cargo.toml index f9cb5233c..114caee79 100644 --- a/frame/election-provider-support/Cargo.toml +++ b/frame/election-provider-support/Cargo.toml @@ -23,8 +23,6 @@ sp-runtime = { version = "7.0.0", default-features = false, path = "../../primit sp-std = { version = "5.0.0", default-features = false, path = "../../primitives/std" } sp-core = { version = "7.0.0", default-features = false, path = "../../primitives/core" } -frame-benchmarking = { version = "4.0.0-dev", default-features = false, path = "../benchmarking", optional = true } - [dev-dependencies] rand = { version = "0.8.5", features = ["small_rng"] } sp-io = { version = "7.0.0", path = "../../primitives/io" } @@ -43,10 +41,6 @@ std = [ "sp-core/std", "sp-runtime/std", "sp-std/std", - - "frame-benchmarking?/std", -] -runtime-benchmarks = [ - "frame-benchmarking/runtime-benchmarks", ] +runtime-benchmarks = [] try-runtime = [] diff --git a/frame/election-provider-support/benchmarking/Cargo.toml b/frame/election-provider-support/benchmarking/Cargo.toml new file mode 100644 index 000000000..bef371ec5 --- /dev/null +++ b/frame/election-provider-support/benchmarking/Cargo.toml @@ -0,0 +1,37 @@ +[package] +name = "pallet-election-provider-support-benchmarking" +version = "4.0.0-dev" +authors = ["Parity Technologies "] +edition = "2021" +license = "Apache-2.0" +homepage = "https://substrate.io" +repository = "https://github.com/paritytech/substrate/" +description = "Benchmarking for election provider support onchain config trait" + +[package.metadata.docs.rs] +targets = ["x86_64-unknown-linux-gnu"] + +[dependencies] +codec = { package = "parity-scale-codec", version = "3.2.2", default-features = false, features = [ + "derive", +] } +frame-benchmarking = { version = "4.0.0-dev", default-features = false, optional = true, path = "../../benchmarking" } +frame-election-provider-support = { version = "4.0.0-dev", default-features = false, path = ".." } +frame-system = { version = "4.0.0-dev", default-features = false, path = "../../system" } +sp-npos-elections = { version = "4.0.0-dev", default-features = false, path = "../../../primitives/npos-elections" } +sp-runtime = { version = "7.0.0", default-features = false, path = "../../../primitives/runtime" } + +[features] +default = ["std"] +std = [ + "codec/std", + "frame-benchmarking?/std", + "frame-election-provider-support/std", + "frame-system/std", + "sp-npos-elections/std", + "sp-runtime/std", +] +runtime-benchmarks = [ + "frame-benchmarking/runtime-benchmarks", + "frame-election-provider-support/runtime-benchmarks", +] diff --git a/frame/election-provider-support/src/benchmarking.rs b/frame/election-provider-support/benchmarking/src/lib.rs similarity index 86% rename from frame/election-provider-support/src/benchmarking.rs rename to frame/election-provider-support/benchmarking/src/lib.rs index 5c5b952b6..774b7036f 100644 --- a/frame/election-provider-support/src/benchmarking.rs +++ b/frame/election-provider-support/benchmarking/src/lib.rs @@ -18,9 +18,12 @@ //! Election provider support pallet benchmarking. //! This is separated into its own crate to avoid bloating the size of the runtime. -use crate::{ApprovalVoting, NposSolver, PhragMMS, SequentialPhragmen}; +#![cfg(feature = "runtime-benchmarks")] +#![cfg_attr(not(feature = "std"), no_std)] + use codec::Decode; use frame_benchmarking::v1::{benchmarks, Vec}; +use frame_election_provider_support::{NposSolver, PhragMMS, SequentialPhragmen}; pub struct Pallet(frame_system::Pallet); pub trait Config: frame_system::Config {} @@ -85,17 +88,4 @@ benchmarks! { ::solve(d as usize, targets, voters).is_ok() ); } - - approval_voting { - let v in (VOTERS[0]) .. VOTERS[1]; - let t in (TARGETS[0]) .. TARGETS[1]; - let d in (VOTES_PER_VOTER[0]) .. VOTES_PER_VOTER[1]; - - let (voters, targets) = set_up_voters_targets::(v, t, d as usize); - }: { - assert!( - ApprovalVoting:: - ::solve(d as usize, targets, voters).is_ok() - ); - } } diff --git a/frame/election-provider-support/src/lib.rs b/frame/election-provider-support/src/lib.rs index 423ce3d67..750ccca46 100644 --- a/frame/election-provider-support/src/lib.rs +++ b/frame/election-provider-support/src/lib.rs @@ -199,9 +199,6 @@ pub use sp_arithmetic; #[doc(hidden)] pub use sp_std; -#[cfg(feature = "runtime-benchmarks")] -pub mod benchmarking; - pub mod weights; pub use weights::WeightInfo; @@ -663,29 +660,6 @@ impl(sp_std::marker::PhantomData<(AccountId, Accuracy)>); - -impl NposSolver - for ApprovalVoting -{ - type AccountId = AccountId; - type Accuracy = Accuracy; - type Error = sp_npos_elections::Error; - fn solve( - winners: usize, - targets: Vec, - voters: Vec<(Self::AccountId, VoteWeight, impl IntoIterator)>, - ) -> Result, Self::Error> { - sp_npos_elections::approval_voting(winners, targets, voters) - } - - fn weight(voters: u32, targets: u32, vote_degree: u32) -> Weight { - T::approval_voting(voters, targets, vote_degree) - } -} - /// A voter, at the level of abstraction of this crate. pub type Voter = (AccountId, VoteWeight, BoundedVec); diff --git a/frame/election-provider-support/src/onchain.rs b/frame/election-provider-support/src/onchain.rs index 60c7853c7..296ac976d 100644 --- a/frame/election-provider-support/src/onchain.rs +++ b/frame/election-provider-support/src/onchain.rs @@ -185,7 +185,7 @@ impl ElectionProvider for OnChainExecution { #[cfg(test)] mod tests { use super::*; - use crate::{ApprovalVoting, ElectionProvider, PhragMMS, SequentialPhragmen}; + use crate::{ElectionProvider, PhragMMS, SequentialPhragmen}; use frame_support::{assert_noop, parameter_types, traits::ConstU32}; use sp_npos_elections::Support; use sp_runtime::Perbill; @@ -235,7 +235,6 @@ mod tests { struct PhragmenParams; struct PhragMMSParams; - struct ApprovalVotingParams; parameter_types! { pub static MaxWinners: u32 = 10; @@ -262,16 +261,6 @@ mod tests { type TargetsBound = ConstU32<400>; } - impl Config for ApprovalVotingParams { - type System = Runtime; - type Solver = ApprovalVoting; - type DataProvider = mock_data_provider::DataProvider; - type WeightInfo = (); - type MaxWinners = MaxWinners; - type VotersBound = ConstU32<600>; - type TargetsBound = ConstU32<400>; - } - mod mock_data_provider { use frame_support::{bounded_vec, traits::ConstU32}; @@ -344,21 +333,4 @@ mod tests { ); }) } - - #[test] - fn onchain_approval_voting_works() { - sp_io::TestExternalities::new_empty().execute_with(|| { - DesiredTargets::set(3); - - // note that the `OnChainExecution::elect` implementation normalizes the vote weights. - assert_eq!( - as ElectionProvider>::elect().unwrap(), - vec![ - (10, Support { total: 20, voters: vec![(1, 5), (3, 15)] }), - (20, Support { total: 15, voters: vec![(1, 5), (2, 10)] }), - (30, Support { total: 25, voters: vec![(2, 10), (3, 15)] }) - ] - ) - }) - } } diff --git a/frame/election-provider-support/src/weights.rs b/frame/election-provider-support/src/weights.rs index c28794267..fd4db1374 100644 --- a/frame/election-provider-support/src/weights.rs +++ b/frame/election-provider-support/src/weights.rs @@ -1,13 +1,13 @@ // This file is part of Substrate. -// Copyright (C) 2023 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // -// http://www.apache.org/licenses/LICENSE-2.0 +// http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, @@ -15,29 +15,25 @@ // See the License for the specific language governing permissions and // limitations under the License. -//! Autogenerated weights for frame_election_provider_support +//! Autogenerated weights for pallet_election_provider_support_benchmarking //! //! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev -//! DATE: 2023-02-22, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` -//! WORST CASE MAP SIZE: `1000000` -//! HOSTNAME: `runner-ehxwxxsd-project-145-concurrent-0`, CPU: `Intel(R) Xeon(R) CPU @ 2.60GHz` +//! DATE: 2022-04-23, STEPS: `1`, REPEAT: 1, LOW RANGE: `[]`, HIGH RANGE: `[]` //! EXECUTION: Some(Wasm), WASM-EXECUTION: Compiled, CHAIN: Some("dev"), DB CACHE: 1024 // Executed Command: -// target/production/substrate +// target/release/substrate // benchmark // pallet -// --steps=50 -// --repeat=20 +// --chain=dev +// --steps=1 +// --repeat=1 +// --pallet=pallet_election_provider_support_benchmarking // --extrinsic=* // --execution=wasm // --wasm-execution=compiled // --heap-pages=4096 -// --json-file=/builds/parity/mirrors/substrate/.git/.artifacts/bench.json -// --pallet=frame_election_provider_support -// --chain=dev -// --header=./HEADER-APACHE2 -// --output=./frame/election-provider-support/src/weights.rs +// --output=frame/election-provider-support/src/weights.rs // --template=./.maintain/frame-weight-template.hbs #![cfg_attr(rustfmt, rustfmt_skip)] @@ -47,108 +43,53 @@ use frame_support::{traits::Get, weights::{Weight, constants::RocksDbWeight}}; use sp_std::marker::PhantomData; -/// Weight functions needed for frame_election_provider_support. +/// Weight functions needed for pallet_election_provider_support_benchmarking. pub trait WeightInfo { fn phragmen(v: u32, t: u32, d: u32, ) -> Weight; fn phragmms(v: u32, t: u32, d: u32, ) -> Weight; - fn approval_voting(v: u32, t: u32, d: u32, ) -> Weight; } -/// Weights for frame_election_provider_support using the Substrate node and recommended hardware. +/// Weights for pallet_election_provider_support_benchmarking using the Substrate node and recommended hardware. pub struct SubstrateWeight(PhantomData); impl WeightInfo for SubstrateWeight { - /// The range of component `v` is `[1000, 2000]`. - /// The range of component `t` is `[500, 1000]`. - /// The range of component `d` is `[5, 16]`. - fn phragmen(v: u32, _t: u32, d: u32, ) -> Weight { - // Proof Size summary in bytes: - // Measured: `0` - // Estimated: `0` - // Minimum execution time: 5_789_174 nanoseconds. - Weight::from_ref_time(5_826_449_000) - .saturating_add(Weight::from_proof_size(0)) - // Standard Error: 130_342 - .saturating_add(Weight::from_ref_time(5_332_741).saturating_mul(v.into())) - // Standard Error: 13_325_769 - .saturating_add(Weight::from_ref_time(1_416_874_101).saturating_mul(d.into())) + fn phragmen(v: u32, t: u32, d: u32, ) -> Weight { + Weight::from_ref_time(0 as u64) + // Standard Error: 667_000 + .saturating_add(Weight::from_ref_time(32_973_000 as u64).saturating_mul(v as u64)) + // Standard Error: 1_334_000 + .saturating_add(Weight::from_ref_time(1_334_000 as u64).saturating_mul(t as u64)) + // Standard Error: 60_644_000 + .saturating_add(Weight::from_ref_time(2_636_364_000 as u64).saturating_mul(d as u64)) } - /// The range of component `v` is `[1000, 2000]`. - /// The range of component `t` is `[500, 1000]`. - /// The range of component `d` is `[5, 16]`. - fn phragmms(v: u32, _t: u32, d: u32, ) -> Weight { - // Proof Size summary in bytes: - // Measured: `0` - // Estimated: `0` - // Minimum execution time: 4_151_790 nanoseconds. - Weight::from_ref_time(4_215_936_000) - .saturating_add(Weight::from_proof_size(0)) - // Standard Error: 125_135 - .saturating_add(Weight::from_ref_time(4_730_609).saturating_mul(v.into())) - // Standard Error: 12_793_390 - .saturating_add(Weight::from_ref_time(1_474_383_961).saturating_mul(d.into())) - } - /// The range of component `v` is `[1000, 2000]`. - /// The range of component `t` is `[500, 1000]`. - /// The range of component `d` is `[5, 16]`. - fn approval_voting(v: u32, _t: u32, d: u32, ) -> Weight { - // Proof Size summary in bytes: - // Measured: `0` - // Estimated: `0` - // Minimum execution time: 1_800_445 nanoseconds. - Weight::from_ref_time(1_824_645_000) - .saturating_add(Weight::from_proof_size(0)) - // Standard Error: 26_266 - .saturating_add(Weight::from_ref_time(1_229_576).saturating_mul(v.into())) - // Standard Error: 2_685_343 - .saturating_add(Weight::from_ref_time(213_080_804).saturating_mul(d.into())) + fn phragmms(v: u32, t: u32, d: u32, ) -> Weight { + Weight::from_ref_time(0 as u64) + // Standard Error: 73_000 + .saturating_add(Weight::from_ref_time(21_073_000 as u64).saturating_mul(v as u64)) + // Standard Error: 146_000 + .saturating_add(Weight::from_ref_time(65_000 as u64).saturating_mul(t as u64)) + // Standard Error: 6_649_000 + .saturating_add(Weight::from_ref_time(1_711_424_000 as u64).saturating_mul(d as u64)) } } // For backwards compatibility and tests impl WeightInfo for () { - /// The range of component `v` is `[1000, 2000]`. - /// The range of component `t` is `[500, 1000]`. - /// The range of component `d` is `[5, 16]`. - fn phragmen(v: u32, _t: u32, d: u32, ) -> Weight { - // Proof Size summary in bytes: - // Measured: `0` - // Estimated: `0` - // Minimum execution time: 5_789_174 nanoseconds. - Weight::from_ref_time(5_826_449_000) - .saturating_add(Weight::from_proof_size(0)) - // Standard Error: 130_342 - .saturating_add(Weight::from_ref_time(5_332_741).saturating_mul(v.into())) - // Standard Error: 13_325_769 - .saturating_add(Weight::from_ref_time(1_416_874_101).saturating_mul(d.into())) - } - /// The range of component `v` is `[1000, 2000]`. - /// The range of component `t` is `[500, 1000]`. - /// The range of component `d` is `[5, 16]`. - fn phragmms(v: u32, _t: u32, d: u32, ) -> Weight { - // Proof Size summary in bytes: - // Measured: `0` - // Estimated: `0` - // Minimum execution time: 4_151_790 nanoseconds. - Weight::from_ref_time(4_215_936_000) - .saturating_add(Weight::from_proof_size(0)) - // Standard Error: 125_135 - .saturating_add(Weight::from_ref_time(4_730_609).saturating_mul(v.into())) - // Standard Error: 12_793_390 - .saturating_add(Weight::from_ref_time(1_474_383_961).saturating_mul(d.into())) + fn phragmen(v: u32, t: u32, d: u32, ) -> Weight { + Weight::from_ref_time(0 as u64) + // Standard Error: 667_000 + .saturating_add(Weight::from_ref_time(32_973_000 as u64).saturating_mul(v as u64)) + // Standard Error: 1_334_000 + .saturating_add(Weight::from_ref_time(1_334_000 as u64).saturating_mul(t as u64)) + // Standard Error: 60_644_000 + .saturating_add(Weight::from_ref_time(2_636_364_000 as u64).saturating_mul(d as u64)) } - /// The range of component `v` is `[1000, 2000]`. - /// The range of component `t` is `[500, 1000]`. - /// The range of component `d` is `[5, 16]`. - fn approval_voting(v: u32, _t: u32, d: u32, ) -> Weight { - // Proof Size summary in bytes: - // Measured: `0` - // Estimated: `0` - // Minimum execution time: 1_800_445 nanoseconds. - Weight::from_ref_time(1_824_645_000) - .saturating_add(Weight::from_proof_size(0)) - // Standard Error: 26_266 - .saturating_add(Weight::from_ref_time(1_229_576).saturating_mul(v.into())) - // Standard Error: 2_685_343 - .saturating_add(Weight::from_ref_time(213_080_804).saturating_mul(d.into())) + fn phragmms(v: u32, t: u32, d: u32, ) -> Weight { + Weight::from_ref_time(0 as u64) + // Standard Error: 73_000 + .saturating_add(Weight::from_ref_time(21_073_000 as u64).saturating_mul(v as u64)) + // Standard Error: 146_000 + .saturating_add(Weight::from_ref_time(65_000 as u64).saturating_mul(t as u64)) + // Standard Error: 6_649_000 + .saturating_add(Weight::from_ref_time(1_711_424_000 as u64).saturating_mul(d as u64)) } } diff --git a/frame/elections/CHANGELOG.md b/frame/elections-phragmen/CHANGELOG.md similarity index 80% rename from frame/elections/CHANGELOG.md rename to frame/elections-phragmen/CHANGELOG.md index b173557be..231de1d2e 100644 --- a/frame/elections/CHANGELOG.md +++ b/frame/elections-phragmen/CHANGELOG.md @@ -4,17 +4,6 @@ All notable changes to this crate will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this crate adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). -## [5.0.0] - UNRELEASED - -### Added - -### Changed -Generalized the pallet to use `NposSolver` instead of hard coding the phragmen algorithm; Changed the name of the pallet from `pallet-elections-phragmen` to `pallet-elections` - -### Fixed - -### Security - ## [4.0.0] - UNRELEASED ### Added diff --git a/frame/elections/Cargo.toml b/frame/elections-phragmen/Cargo.toml similarity index 90% rename from frame/elections/Cargo.toml rename to frame/elections-phragmen/Cargo.toml index 14d07eca7..ce39e42b8 100644 --- a/frame/elections/Cargo.toml +++ b/frame/elections-phragmen/Cargo.toml @@ -1,12 +1,12 @@ [package] -name = "pallet-elections" +name = "pallet-elections-phragmen" version = "5.0.0-dev" authors = ["Parity Technologies "] edition = "2021" license = "Apache-2.0" homepage = "https://substrate.io" repository = "https://github.com/paritytech/substrate/" -description = "FRAME pallet for generic elections." +description = "FRAME pallet based on seq-Phragmén election method." readme = "README.md" [package.metadata.docs.rs] @@ -24,7 +24,6 @@ frame-system = { version = "4.0.0-dev", default-features = false, path = "../sys sp-core = { version = "7.0.0", default-features = false, path = "../../primitives/core" } sp-io = { version = "7.0.0", default-features = false, path = "../../primitives/io" } sp-npos-elections = { version = "4.0.0-dev", default-features = false, path = "../../primitives/npos-elections" } -frame-election-provider-support = { version = "4.0.0-dev", default-features = false, path = "../election-provider-support" } sp-runtime = { version = "7.0.0", default-features = false, path = "../../primitives/runtime" } sp-std = { version = "5.0.0", default-features = false, path = "../../primitives/std" } diff --git a/frame/elections/README.md b/frame/elections-phragmen/README.md similarity index 94% rename from frame/elections/README.md rename to frame/elections-phragmen/README.md index 0f27bdd18..26b3f260d 100644 --- a/frame/elections/README.md +++ b/frame/elections-phragmen/README.md @@ -1,6 +1,6 @@ -# Generic Elections Module. +# Phragmén Election Module. -A generic elections module. +An election module based on sequential phragmen. ### Term and Round @@ -60,7 +60,7 @@ being re-elected at the end of each round. ### Module Information -- [`elections::Config`](https://docs.rs/pallet-elections-phragmen/latest/pallet_elections_phragmen/trait.Config.html) +- [`election_sp_phragmen::Config`](https://docs.rs/pallet-elections-phragmen/latest/pallet_elections_phragmen/trait.Config.html) - [`Call`](https://docs.rs/pallet-elections-phragmen/latest/pallet_elections_phragmen/enum.Call.html) - [`Module`](https://docs.rs/pallet-elections-phragmen/latest/pallet_elections_phragmen/struct.Module.html) diff --git a/frame/elections/src/benchmarking.rs b/frame/elections-phragmen/src/benchmarking.rs similarity index 85% rename from frame/elections/src/benchmarking.rs rename to frame/elections-phragmen/src/benchmarking.rs index da755fe9b..56ea19578 100644 --- a/frame/elections/src/benchmarking.rs +++ b/frame/elections-phragmen/src/benchmarking.rs @@ -15,14 +15,14 @@ // See the License for the specific language governing permissions and // limitations under the License. -//! Elections pallet benchmarking. +//! Elections-Phragmen pallet benchmarking. #![cfg(feature = "runtime-benchmarks")] use super::*; use frame_benchmarking::v1::{account, benchmarks, whitelist, BenchmarkError, BenchmarkResult}; -use frame_support::dispatch::DispatchResultWithPostInfo; +use frame_support::{dispatch::DispatchResultWithPostInfo, traits::OnInitialize}; use frame_system::RawOrigin; use crate::Pallet as Elections; @@ -37,7 +37,7 @@ fn endowed_account(name: &'static str, index: u32) -> T::AccountId { let amount = default_stake::(T::MaxVoters::get()) * BalanceOf::::from(BALANCE_FACTOR); let _ = T::Currency::make_free_balance_be(&account, amount); // important to increase the total issuance since T::CurrencyToVote will need it to be sane for - // the election to work. + // phragmen to work. T::Currency::issue(amount); account @@ -122,7 +122,7 @@ fn distribute_voters( fn fill_seats_up_to(m: u32) -> Result, &'static str> { let _ = submit_candidates_with_self_vote::(m, "fill_seats_up_to")?; assert_eq!(>::candidates().len() as u32, m, "wrong number of candidates."); - >::do_election(); + >::do_phragmen(); assert_eq!(>::candidates().len(), 0, "some candidates remaining."); assert_eq!( >::members().len() + >::runners_up().len(), @@ -382,11 +382,11 @@ benchmarks! { assert_eq!(>::iter().count() as u32, 0); } - pre_solve_election { - // We always select 20 members, this is hard-coded in the runtime and cannot be trivially - // changed at this stage. Yet, change the number of voters, candidates and edge per voter - // to see the impact. Note that we give all candidates a self vote to make sure they are - // all considered. + election_phragmen { + // This is just to focus on phragmen in the context of this module. We always select 20 + // members, this is hard-coded in the runtime and cannot be trivially changed at this stage. + // Yet, change the number of voters, candidates and edge per voter to see the impact. Note + // that we give all candidates a self vote to make sure they are all considered. let c in 1 .. T::MaxCandidates::get(); let v in 1 .. T::MaxVoters::get(); let e in (T::MaxVoters::get()) .. T::MaxVoters::get() * T::MaxVotesPerVoter::get(); @@ -404,44 +404,7 @@ benchmarks! { let all_candidates = submit_candidates_with_self_vote::(c, "candidates")?; let _ = distribute_voters::(all_candidates, v.saturating_sub(c), votes_per_voter as usize)?; }: { - >::do_pre_solve_election().unwrap(); - } - - post_solve_election { - // We always select 20 members, this is hard-coded in the runtime and cannot be trivially - // changed at this stage. Yet, change the number of voters, candidates and edge per voter - // to see the impact. Note that we give all candidates a self vote to make sure they are - // all considered. - let c in 1 .. T::MaxCandidates::get(); - let v in 1 .. T::MaxVoters::get(); - let e in (T::MaxVoters::get()) .. T::MaxVoters::get() as u32 * T::MaxVotesPerVoter::get() as u32; - clean::(); - - // so we have a situation with v and e. we want e to basically always be in the range of `e - // -> e * T::MaxVotesPerVoter`, but we cannot express that now with the benchmarks. So what we do - // is: when c is being iterated, v, and e are max and fine. when v is being iterated, e is - // being set to max and this is a problem. In these cases, we cap e to a lower value, namely - // v * `T::MaxVotesPerVoter`. when e is being iterated, v is at max, and again fine. all in all, - // votes_per_voter can never be more than `T::MaxVotesPerVoter`. Note that this might cause `v` to be - // an overestimate. - let votes_per_voter = (e / v).min(T::MaxVotesPerVoter::get() as u32); - - let all_candidates = submit_candidates_with_self_vote::(c, "candidates")?; - let _ = distribute_voters::(all_candidates, v.saturating_sub(c), votes_per_voter as usize)?; - - let pre_election_result = >::do_pre_solve_election().unwrap(); - let election_result = T::ElectionSolver::solve( - pre_election_result.num_to_elect, - pre_election_result.candidate_ids, - pre_election_result.voters_and_votes, - ).unwrap(); - - }: { - >::do_post_solve_election( - election_result.winners, - pre_election_result.candidates_and_deposit, - pre_election_result.voters_and_stakes, - ); + >::on_initialize(T::TermDuration::get()); } verify { assert_eq!(>::members().len() as u32, T::DesiredMembers::get().min(c)); diff --git a/frame/elections/src/lib.rs b/frame/elections-phragmen/src/lib.rs similarity index 90% rename from frame/elections/src/lib.rs rename to frame/elections-phragmen/src/lib.rs index bdf0161ba..fa63762c5 100644 --- a/frame/elections/src/lib.rs +++ b/frame/elections-phragmen/src/lib.rs @@ -15,9 +15,9 @@ // See the License for the specific language governing permissions and // limitations under the License. -//! # Generic Election Module. +//! # Phragmén Election Module. //! -//! An election module based on a generic election algorithm. +//! An election module based on sequential phragmen. //! //! ### Term and Round //! @@ -99,7 +99,6 @@ #![cfg_attr(not(feature = "std"), no_std)] use codec::{Decode, Encode}; -use frame_election_provider_support::NposSolver; use frame_support::{ traits::{ defensive_prelude::*, ChangeMembers, Contains, ContainsLengthBound, Currency, @@ -109,12 +108,12 @@ use frame_support::{ weights::Weight, }; use scale_info::TypeInfo; -use sp_npos_elections::{ElectionResult, ExtendedBalance, VoteWeight}; +use sp_npos_elections::{ElectionResult, ExtendedBalance}; use sp_runtime::{ traits::{Saturating, StaticLookup, Zero}, - DispatchError, RuntimeDebug, + DispatchError, Perbill, RuntimeDebug, }; -use sp_std::{cmp::Ordering, iter::IntoIterator, prelude::*}; +use sp_std::{cmp::Ordering, prelude::*}; mod benchmarking; pub mod weights; @@ -123,17 +122,7 @@ pub use weights::WeightInfo; /// All migrations. pub mod migrations; -pub(crate) const LOG_TARGET: &str = "runtime::elections"; - -// logging helper. -macro_rules! log { - ($level:tt, $patter:expr $(, $values:expr)* $(,)?) => { - log::$level!( - target: crate::LOG_TARGET, - concat!("[{:?}] 🗳️ ", $patter), >::block_number() $(, $values)* - ) - }; -} +const LOG_TARGET: &str = "runtime::elections-phragmen"; type BalanceOf = <::Currency as Currency<::AccountId>>::Balance; @@ -184,17 +173,6 @@ pub struct SeatHolder { pub deposit: Balance, } -/// The results of running the pre-election step. -#[derive(Debug, Clone)] -struct PreElectionResults { - pub num_to_elect: usize, - pub candidate_ids: Vec, - pub candidates_and_deposit: Vec<(T::AccountId, BalanceOf)>, - pub voters_and_stakes: Vec<(T::AccountId, BalanceOf, Vec)>, - pub voters_and_votes: Vec<(T::AccountId, VoteWeight, Vec)>, - pub num_edges: u32, -} - pub use pallet::*; type AccountIdLookupOf = <::Lookup as StaticLookup>::Source; @@ -218,7 +196,7 @@ pub mod pallet { pub trait Config: frame_system::Config { type RuntimeEvent: From> + IsType<::RuntimeEvent>; - /// Identifier for the elections pallet's lock + /// Identifier for the elections-phragmen pallet's lock #[pallet::constant] type PalletId: Get; @@ -271,7 +249,7 @@ pub mod pallet { #[pallet::constant] type TermDuration: Get; - /// The maximum number of candidates in an election. + /// The maximum number of candidates in a phragmen election. /// /// Warning: This impacts the size of the election which is run onchain. Chose wisely, and /// consider how it will impact `T::WeightInfo::election_phragmen`. @@ -280,7 +258,7 @@ pub mod pallet { #[pallet::constant] type MaxCandidates: Get; - /// The maximum number of voters to allow in an election. + /// The maximum number of voters to allow in a phragmen election. /// /// Warning: This impacts the size of the election which is run onchain. Chose wisely, and /// consider how it will impact `T::WeightInfo::election_phragmen`. @@ -289,12 +267,6 @@ pub mod pallet { #[pallet::constant] type MaxVoters: Get; - /// Something that will calculate the result of elections. - type ElectionSolver: NposSolver; - - /// Weight information for the [`Config::ElectionSolver`]. - type SolverWeightInfo: frame_election_provider_support::WeightInfo; - /// Maximum numbers of votes per voter. /// /// Warning: This impacts the size of the election which is run onchain. Chose wisely, and @@ -314,7 +286,7 @@ pub mod pallet { fn on_initialize(n: T::BlockNumber) -> Weight { let term_duration = T::TermDuration::get(); if !term_duration.is_zero() && (n % term_duration).is_zero() { - Self::do_election() + Self::do_phragmen() } else { Weight::zero() } @@ -323,26 +295,11 @@ pub mod pallet { fn integrity_test() { let block_weight = T::BlockWeights::get().max_block; // mind the order. - let pre_solve_weight = T::WeightInfo::pre_solve_election( + let election_weight = T::WeightInfo::election_phragmen( T::MaxCandidates::get(), T::MaxVoters::get(), T::MaxVotesPerVoter::get() * T::MaxVoters::get(), ); - // mind the order. - let post_solve_weight = T::WeightInfo::post_solve_election( - T::MaxCandidates::get(), - T::MaxVoters::get(), - T::MaxVotesPerVoter::get() * T::MaxVoters::get(), - ); - // mind the order. - let election_weight = T::ElectionSolver::weight::( - T::MaxCandidates::get(), - T::MaxVoters::get(), - T::MaxVotesPerVoter::get() * T::MaxVoters::get(), - ); - let election_weight = pre_solve_weight - .saturating_sub(post_solve_weight) - .saturating_add(election_weight); let to_seconds = |w: &Weight| { w.ref_time() as f32 / @@ -577,8 +534,8 @@ pub mod pallet { /// the outgoing member is slashed. /// /// If a runner-up is available, then the best runner-up will be removed and replaces the - /// outgoing member. Otherwise, if `rerun_election` is `true`, a new election is started, - /// else, nothing happens. + /// outgoing member. Otherwise, if `rerun_election` is `true`, a new phragmen election is + /// started, else, nothing happens. /// /// If `slash_bond` is set to true, the bond of the member being removed is slashed. Else, /// it is returned. @@ -608,7 +565,7 @@ pub mod pallet { Self::deposit_event(Event::MemberKicked { member: who }); if rerun_election { - Self::do_election(); + Self::do_phragmen(); } // no refund needed. @@ -707,8 +664,6 @@ pub mod pallet { InvalidRenouncing, /// Prediction regarding replacement after member removal is wrong. InvalidReplacement, - /// No candidates for the next term. - EmptyTerm, } /// The current elected members. @@ -787,7 +742,7 @@ pub mod pallet { Members::::mutate(|members| { match members.binary_search_by(|m| m.who.cmp(member)) { Ok(_) => { - panic!("Duplicate member in elections genesis: {}", member) + panic!("Duplicate member in elections-phragmen genesis: {}", member) }, Err(pos) => members.insert( pos, @@ -979,77 +934,11 @@ impl Pallet { debug_assert!(_remainder.is_zero()); } - /// Run an election with all required side processes and state updates, if election - /// succeeds. Else, it will emit an `ElectionError` event. The election algorithm is defined - /// by the implementor of `Self::ElectionSolver`. + /// Run the phragmen election with all required side processes and state updates, if election + /// succeeds. Else, it will emit an `ElectionError` event. /// /// Calls the appropriate [`ChangeMembers`] function variant internally. - fn do_election() -> Weight { - let PreElectionResults { - num_to_elect, - candidate_ids, - candidates_and_deposit, - voters_and_stakes, - voters_and_votes, - num_edges, - } = match Self::do_pre_solve_election() { - Ok(results) => results, - Err(err) => match err { - Error::EmptyTerm => { - Self::deposit_event(Event::EmptyTerm); - return T::DbWeight::get().reads(3) - }, - Error::TooManyVotes => { - Self::deposit_event(Event::ElectionError); - log!(error, "Failed to run election. Number of voters exceeded",); - let max_voters = ::MaxVoters::get() as usize; - return T::DbWeight::get().reads(3 + max_voters as u64) - }, - _ => { - log!(error, "Unexpected pre-election error",); - let max_voters = ::MaxVoters::get() as usize; - return T::DbWeight::get().reads(3 + max_voters as u64) - }, - }, - }; - - let num_candidates = candidates_and_deposit.len() as u32; - let num_voters = voters_and_votes.len() as u32; - let num_edges = num_edges; - - let election_winners = - T::ElectionSolver::solve(num_to_elect, candidate_ids, voters_and_votes) - .map( - |ElectionResult::< - T::AccountId, - ::Accuracy, - > { - winners, - assignments: _, - }| winners, - ) - .map_err(|e| { - log!(warn, "Failed to run election [{:?}].", e); - Self::deposit_event(Event::ElectionError); - }); - - let post_election_weight = if let Ok(winners) = election_winners { - Self::do_post_solve_election(winners, candidates_and_deposit, voters_and_stakes); - T::WeightInfo::post_solve_election(num_candidates, num_voters, num_edges) - } else { - Weight::zero() - }; - - T::ElectionSolver::weight::(num_candidates, num_voters, num_edges) - .saturating_add(T::WeightInfo::pre_solve_election( - num_candidates, - num_voters, - num_edges, - )) - .saturating_add(post_election_weight) - } - - fn do_pre_solve_election() -> Result, Error> { + fn do_phragmen() -> Weight { let desired_seats = T::DesiredMembers::get() as usize; let desired_runners_up = T::DesiredRunnersUp::get() as usize; let num_to_elect = desired_runners_up + desired_seats; @@ -1059,33 +948,44 @@ impl Pallet { candidates_and_deposit.append(&mut Self::implicit_candidates_with_deposit()); if candidates_and_deposit.len().is_zero() { - return Err(Error::EmptyTerm) + Self::deposit_event(Event::EmptyTerm); + return T::DbWeight::get().reads(3) } - // All of the new winners that come out of the election will thus have a deposit recorded. + // All of the new winners that come out of phragmen will thus have a deposit recorded. let candidate_ids = candidates_and_deposit.iter().map(|(x, _)| x).cloned().collect::>(); // helper closures to deal with balance/stake. let total_issuance = T::Currency::total_issuance(); let to_votes = |b: BalanceOf| T::CurrencyToVote::to_vote(b, total_issuance); + let to_balance = |e: ExtendedBalance| T::CurrencyToVote::to_currency(e, total_issuance); let mut num_edges: u32 = 0; let max_voters = ::MaxVoters::get() as usize; // used for prime election. let mut voters_and_stakes = Vec::new(); - - Voting::::iter().try_for_each(|(voter, Voter { stake, votes, .. })| { + match Voting::::iter().try_for_each(|(voter, Voter { stake, votes, .. })| { if voters_and_stakes.len() < max_voters { voters_and_stakes.push((voter, stake, votes)); Ok(()) } else { - Err(Error::TooManyVotes) + Err(()) } - })?; + }) { + Ok(_) => (), + Err(_) => { + log::error!( + target: LOG_TARGET, + "Failed to run election. Number of voters exceeded", + ); + Self::deposit_event(Event::ElectionError); + return T::DbWeight::get().reads(3 + max_voters as u64) + }, + } - // used for elections. + // used for phragmen. let voters_and_votes = voters_and_stakes .iter() .cloned() @@ -1095,144 +995,157 @@ impl Pallet { }) .collect::>(); - Ok(PreElectionResults { - num_to_elect, - candidate_ids, - candidates_and_deposit, - voters_and_stakes, - voters_and_votes, - num_edges, - }) - } - - fn do_post_solve_election( - winners: Vec<(T::AccountId, u128)>, - candidates_and_deposit: Vec<(T::AccountId, BalanceOf)>, - voters_and_stakes: Vec<(T::AccountId, BalanceOf, Vec)>, - ) { - let desired_seats = T::DesiredMembers::get() as usize; - let total_issuance = T::Currency::total_issuance(); - let to_balance = |e: ExtendedBalance| T::CurrencyToVote::to_currency(e, total_issuance); - - // this is already sorted by id. - let old_members_ids_sorted = - >::take().into_iter().map(|m| m.who).collect::>(); - // this one needs sorted by id. - let mut old_runners_up_ids_sorted = - >::take().into_iter().map(|r| r.who).collect::>(); - old_runners_up_ids_sorted.sort(); + let weight_candidates = candidates_and_deposit.len() as u32; + let weight_voters = voters_and_votes.len() as u32; + let weight_edges = num_edges; + let _ = + sp_npos_elections::seq_phragmen(num_to_elect, candidate_ids, voters_and_votes, None) + .map(|ElectionResult:: { winners, assignments: _ }| { + // this is already sorted by id. + let old_members_ids_sorted = >::take() + .into_iter() + .map(|m| m.who) + .collect::>(); + // this one needs a sort by id. + let mut old_runners_up_ids_sorted = >::take() + .into_iter() + .map(|r| r.who) + .collect::>(); + old_runners_up_ids_sorted.sort(); + + // filter out those who end up with no backing stake. + let mut new_set_with_stake = winners + .into_iter() + .filter_map( + |(m, b)| if b.is_zero() { None } else { Some((m, to_balance(b))) }, + ) + .collect::)>>(); + + // OPTIMIZATION NOTE: we could bail out here if `new_set.len() == 0`. There + // isn't much left to do. Yet, re-arranging the code would require duplicating + // the slashing of exposed candidates, cleaning any previous members, and so on. + // For now, in favor of readability and veracity, we keep it simple. + + // split new set into winners and runners up. + let split_point = desired_seats.min(new_set_with_stake.len()); + let mut new_members_sorted_by_id = + new_set_with_stake.drain(..split_point).collect::>(); + new_members_sorted_by_id.sort_by(|i, j| i.0.cmp(&j.0)); + + // all the rest will be runners-up + new_set_with_stake.reverse(); + let new_runners_up_sorted_by_rank = new_set_with_stake; + let mut new_runners_up_ids_sorted = new_runners_up_sorted_by_rank + .iter() + .map(|(r, _)| r.clone()) + .collect::>(); + new_runners_up_ids_sorted.sort(); + + // Now we select a prime member using a [Borda + // count](https://en.wikipedia.org/wiki/Borda_count). We weigh everyone's vote for + // that new member by a multiplier based on the order of the votes. i.e. the + // first person a voter votes for gets a 16x multiplier, the next person gets a + // 15x multiplier, an so on... (assuming `T::MaxVotesPerVoter` = 16) + let mut prime_votes = new_members_sorted_by_id + .iter() + .map(|c| (&c.0, BalanceOf::::zero())) + .collect::>(); + for (_, stake, votes) in voters_and_stakes.into_iter() { + for (vote_multiplier, who) in + votes.iter().enumerate().map(|(vote_position, who)| { + ((T::MaxVotesPerVoter::get() as usize - vote_position) as u32, who) + }) { + if let Ok(i) = prime_votes.binary_search_by_key(&who, |k| k.0) { + prime_votes[i].1 = prime_votes[i] + .1 + .saturating_add(stake.saturating_mul(vote_multiplier.into())); + } + } + } + // We then select the new member with the highest weighted stake. In the case of + // a tie, the last person in the list with the tied score is selected. This is + // the person with the "highest" account id based on the sort above. + let prime = prime_votes.into_iter().max_by_key(|x| x.1).map(|x| x.0.clone()); + + // new_members_sorted_by_id is sorted by account id. + let new_members_ids_sorted = new_members_sorted_by_id + .iter() + .map(|(m, _)| m.clone()) + .collect::>(); + + // report member changes. We compute diff because we need the outgoing list. + let (incoming, outgoing) = T::ChangeMembers::compute_members_diff_sorted( + &new_members_ids_sorted, + &old_members_ids_sorted, + ); + T::ChangeMembers::change_members_sorted( + &incoming, + &outgoing, + &new_members_ids_sorted, + ); + T::ChangeMembers::set_prime(prime); + + // All candidates/members/runners-up who are no longer retaining a position as a + // seat holder will lose their bond. + candidates_and_deposit.iter().for_each(|(c, d)| { + if new_members_ids_sorted.binary_search(c).is_err() && + new_runners_up_ids_sorted.binary_search(c).is_err() + { + let (imbalance, _) = T::Currency::slash_reserved(c, *d); + T::LoserCandidate::on_unbalanced(imbalance); + Self::deposit_event(Event::CandidateSlashed { + candidate: c.clone(), + amount: *d, + }); + } + }); - // filter out those who end up with no backing stake. - let mut new_set_with_stake = winners - .into_iter() - .filter_map(|(m, b)| if b.is_zero() { None } else { Some((m, to_balance(b))) }) - .collect::)>>(); - - // OPTIMIZATION NOTE: we could bail out here if `new_set.len() == 0`. There - // isn't much left to do. Yet, re-arranging the code would require duplicating - // the slashing of exposed candidates, cleaning any previous members, and so on. - // For now, in favor of readability and veracity, we keep it simple. - - // split new set into winners and runners up. - let split_point = desired_seats.min(new_set_with_stake.len()); - let mut new_members_sorted_by_id = - new_set_with_stake.drain(..split_point).collect::>(); - new_members_sorted_by_id.sort_by(|i, j| i.0.cmp(&j.0)); - - // all the rest will be runners-up - new_set_with_stake.reverse(); - let new_runners_up_sorted_by_rank = new_set_with_stake; - let mut new_runners_up_ids_sorted = - new_runners_up_sorted_by_rank.iter().map(|(r, _)| r.clone()).collect::>(); - new_runners_up_ids_sorted.sort(); - - // Now we select a prime member using a [Borda - // count](https://en.wikipedia.org/wiki/Borda_count). We weigh everyone's vote for - // that new member by a multiplier based on the order of the votes. i.e. the - // first person a voter votes for gets a 16x multiplier, the next person gets a - // 15x multiplier, an so on... (assuming `T::MaxVotesPerVoter` = 16). - let mut prime_votes = new_members_sorted_by_id - .iter() - .map(|c| (&c.0, BalanceOf::::zero())) - .collect::>(); - for (_, stake, votes) in voters_and_stakes.into_iter() { - for (vote_multiplier, who) in votes.iter().enumerate().map(|(vote_position, who)| { - ((T::MaxVotesPerVoter::get() as usize - vote_position) as u32, who) - }) { - if let Ok(i) = prime_votes.binary_search_by_key(&who, |k| k.0) { - prime_votes[i].1 = prime_votes[i] - .1 - .saturating_add(stake.saturating_mul(vote_multiplier.into())); - } - } - } - // We then select the new member with the highest weighted stake. In the case of - // a tie, the last person in the list with the tied score is selected. This is - // the person with the "highest" account id based on the sort above. - let prime = prime_votes.into_iter().max_by_key(|x| x.1).map(|x| x.0.clone()); + // write final values to storage. + let deposit_of_candidate = |x: &T::AccountId| -> BalanceOf { + // defensive-only. This closure is used against the new members and new + // runners-up, both of which are phragmen winners and thus must have + // deposit. + candidates_and_deposit + .iter() + .find_map(|(c, d)| if c == x { Some(*d) } else { None }) + .defensive_unwrap_or_default() + }; + // fetch deposits from the one recorded one. This will make sure that a + // candidate who submitted candidacy before a change to candidacy deposit will + // have the correct amount recorded. + >::put( + new_members_sorted_by_id + .iter() + .map(|(who, stake)| SeatHolder { + deposit: deposit_of_candidate(who), + who: who.clone(), + stake: *stake, + }) + .collect::>(), + ); + >::put( + new_runners_up_sorted_by_rank + .into_iter() + .map(|(who, stake)| SeatHolder { + deposit: deposit_of_candidate(&who), + who, + stake, + }) + .collect::>(), + ); - // new_members_sorted_by_id is sorted by account id. - let new_members_ids_sorted = new_members_sorted_by_id - .iter() - .map(|(m, _)| m.clone()) - .collect::>(); - - // report member changes. We compute diff because we need the outgoing list. - let (incoming, outgoing) = T::ChangeMembers::compute_members_diff_sorted( - &new_members_ids_sorted, - &old_members_ids_sorted, - ); - T::ChangeMembers::change_members_sorted(&incoming, &outgoing, &new_members_ids_sorted); - T::ChangeMembers::set_prime(prime); - - // All candidates/members/runners-up who are no longer retaining a position as a - // seat holder will lose their bond. - candidates_and_deposit.iter().for_each(|(c, d)| { - if new_members_ids_sorted.binary_search(c).is_err() && - new_runners_up_ids_sorted.binary_search(c).is_err() - { - let (imbalance, _) = T::Currency::slash_reserved(c, *d); - T::LoserCandidate::on_unbalanced(imbalance); - Self::deposit_event(Event::CandidateSlashed { candidate: c.clone(), amount: *d }); - } - }); + // clean candidates. + >::kill(); - // write final values to storage. - let deposit_of_candidate = |x: &T::AccountId| -> BalanceOf { - // defensive-only. This closure is used against the new members and new - // runners-up, both of which are election winners and thus must have - // deposit. - candidates_and_deposit - .iter() - .find_map(|(c, d)| if c == x { Some(*d) } else { None }) - .defensive_unwrap_or_default() - }; - // fetch deposits from the one recorded one. This will make sure that a - // candidate who submitted candidacy before a change to candidacy deposit will - // have the correct amount recorded. - >::put( - new_members_sorted_by_id - .iter() - .map(|(who, stake)| SeatHolder { - deposit: deposit_of_candidate(who), - who: who.clone(), - stake: *stake, + Self::deposit_event(Event::NewTerm { new_members: new_members_sorted_by_id }); + >::mutate(|v| *v += 1); }) - .collect::>(), - ); - >::put( - new_runners_up_sorted_by_rank - .into_iter() - .map(|(who, stake)| SeatHolder { deposit: deposit_of_candidate(&who), who, stake }) - .collect::>(), - ); - - // clean candidates. - >::kill(); - - log!(info, "New term election successful."); - Self::deposit_event(Event::NewTerm { new_members: new_members_sorted_by_id }); - >::mutate(|v| *v += 1); + .map_err(|e| { + log::error!(target: LOG_TARGET, "Failed to run election [{:?}].", e,); + Self::deposit_event(Event::ElectionError); + }); + + T::WeightInfo::election_phragmen(weight_candidates, weight_voters, weight_edges) } } @@ -1283,8 +1196,7 @@ impl ContainsLengthBound for Pallet { #[cfg(test)] mod tests { use super::*; - use crate as elections; - use frame_election_provider_support::{weights::SubstrateWeight, ApprovalVoting}; + use crate as elections_phragmen; use frame_support::{ assert_noop, assert_ok, dispatch::DispatchResultWithPostInfo, @@ -1296,7 +1208,7 @@ mod tests { use sp_runtime::{ testing::Header, traits::{BlakeTwo256, IdentityLookup}, - BuildStorage, Perbill, + BuildStorage, }; use substrate_test_utils::assert_eq_uvec; @@ -1395,13 +1307,13 @@ mod tests { } parameter_types! { - pub const ElectionsPalletId: LockIdentifier = *b"phrelect"; - pub const MaxVoters: u32 = 256; - pub const MaxCandidates: u32 = 64; + pub const ElectionsPhragmenPalletId: LockIdentifier = *b"phrelect"; + pub const PhragmenMaxVoters: u32 = 1000; + pub const PhragmenMaxCandidates: u32 = 100; } impl Config for Test { - type PalletId = ElectionsPalletId; + type PalletId = ElectionsPhragmenPalletId; type RuntimeEvent = RuntimeEvent; type Currency = Balances; type CurrencyToVote = frame_support::traits::SaturatingCurrencyToVote; @@ -1416,11 +1328,9 @@ mod tests { type LoserCandidate = (); type KickedMember = (); type WeightInfo = (); - type MaxVoters = MaxVoters; - type MaxCandidates = MaxCandidates; - type ElectionSolver = ApprovalVoting; - type SolverWeightInfo = SubstrateWeight; + type MaxVoters = PhragmenMaxVoters; type MaxVotesPerVoter = ConstU32<16>; + type MaxCandidates = PhragmenMaxCandidates; } pub type Block = sp_runtime::generic::Block; @@ -1435,7 +1345,7 @@ mod tests { { System: frame_system::{Pallet, Call, Event}, Balances: pallet_balances::{Pallet, Call, Event, Config}, - Elections: elections::{Pallet, Call, Event, Config}, + Elections: elections_phragmen::{Pallet, Call, Event, Config}, } ); @@ -1496,7 +1406,9 @@ mod tests { (6, 60 * self.balance_factor), ], }, - elections: elections::GenesisConfig:: { members: self.genesis_members }, + elections: elections_phragmen::GenesisConfig:: { + members: self.genesis_members, + }, } .build_storage() .unwrap() @@ -1554,7 +1466,7 @@ mod tests { .get(0) .cloned() .map(|lock| { - assert_eq!(lock.id, ElectionsPalletId::get()); + assert_eq!(lock.id, ElectionsPhragmenPalletId::get()); lock.amount }) .unwrap_or_default() @@ -1745,7 +1657,7 @@ mod tests { } #[test] - #[should_panic = "Duplicate member in elections genesis: 2"] + #[should_panic = "Duplicate member in elections-phragmen genesis: 2"] fn genesis_members_cannot_be_duplicate() { ExtBuilder::default() .desired_members(3) diff --git a/frame/elections/src/migrations/mod.rs b/frame/elections-phragmen/src/migrations/mod.rs similarity index 100% rename from frame/elections/src/migrations/mod.rs rename to frame/elections-phragmen/src/migrations/mod.rs diff --git a/frame/elections/src/migrations/v3.rs b/frame/elections-phragmen/src/migrations/v3.rs similarity index 100% rename from frame/elections/src/migrations/v3.rs rename to frame/elections-phragmen/src/migrations/v3.rs diff --git a/frame/elections/src/migrations/v4.rs b/frame/elections-phragmen/src/migrations/v4.rs similarity index 100% rename from frame/elections/src/migrations/v4.rs rename to frame/elections-phragmen/src/migrations/v4.rs diff --git a/frame/elections/src/migrations/v5.rs b/frame/elections-phragmen/src/migrations/v5.rs similarity index 100% rename from frame/elections/src/migrations/v5.rs rename to frame/elections-phragmen/src/migrations/v5.rs diff --git a/frame/elections-phragmen/src/weights.rs b/frame/elections-phragmen/src/weights.rs new file mode 100644 index 000000000..24ab3bc15 --- /dev/null +++ b/frame/elections-phragmen/src/weights.rs @@ -0,0 +1,559 @@ +// This file is part of Substrate. + +// Copyright (C) Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +//! Autogenerated weights for pallet_elections_phragmen +//! +//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev +//! DATE: 2023-02-03, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! WORST CASE MAP SIZE: `1000000` +//! HOSTNAME: `runner-b3zmxxc-project-145-concurrent-0`, CPU: `Intel(R) Xeon(R) CPU @ 2.60GHz` +//! EXECUTION: Some(Wasm), WASM-EXECUTION: Compiled, CHAIN: Some("dev"), DB CACHE: 1024 + +// Executed Command: +// target/production/substrate +// benchmark +// pallet +// --steps=50 +// --repeat=20 +// --extrinsic=* +// --execution=wasm +// --wasm-execution=compiled +// --heap-pages=4096 +// --json-file=/builds/parity/mirrors/substrate/.git/.artifacts/bench.json +// --pallet=pallet_elections_phragmen +// --chain=dev +// --header=./HEADER-APACHE2 +// --output=./frame/elections-phragmen/src/weights.rs +// --template=./.maintain/frame-weight-template.hbs + +#![cfg_attr(rustfmt, rustfmt_skip)] +#![allow(unused_parens)] +#![allow(unused_imports)] + +use frame_support::{traits::Get, weights::{Weight, constants::RocksDbWeight}}; +use sp_std::marker::PhantomData; + +/// Weight functions needed for pallet_elections_phragmen. +pub trait WeightInfo { + fn vote_equal(v: u32, ) -> Weight; + fn vote_more(v: u32, ) -> Weight; + fn vote_less(v: u32, ) -> Weight; + fn remove_voter() -> Weight; + fn submit_candidacy(c: u32, ) -> Weight; + fn renounce_candidacy_candidate(c: u32, ) -> Weight; + fn renounce_candidacy_members() -> Weight; + fn renounce_candidacy_runners_up() -> Weight; + fn remove_member_without_replacement() -> Weight; + fn remove_member_with_replacement() -> Weight; + fn clean_defunct_voters(v: u32, d: u32, ) -> Weight; + fn election_phragmen(c: u32, v: u32, e: u32, ) -> Weight; +} + +/// Weights for pallet_elections_phragmen using the Substrate node and recommended hardware. +pub struct SubstrateWeight(PhantomData); +impl WeightInfo for SubstrateWeight { + /// Storage: Elections Candidates (r:1 w:0) + /// Proof Skipped: Elections Candidates (max_values: Some(1), max_size: None, mode: Measured) + /// Storage: Elections Members (r:1 w:0) + /// Proof Skipped: Elections Members (max_values: Some(1), max_size: None, mode: Measured) + /// Storage: Elections RunnersUp (r:1 w:0) + /// Proof Skipped: Elections RunnersUp (max_values: Some(1), max_size: None, mode: Measured) + /// Storage: Elections Voting (r:1 w:1) + /// Proof Skipped: Elections Voting (max_values: None, max_size: None, mode: Measured) + /// Storage: Balances Locks (r:1 w:1) + /// Proof: Balances Locks (max_values: None, max_size: Some(1299), added: 3774, mode: MaxEncodedLen) + /// The range of component `v` is `[1, 16]`. + fn vote_equal(v: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `499 + v * (80 ±0)` + // Estimated: `9726 + v * (320 ±0)` + // Minimum execution time: 27_362 nanoseconds. + Weight::from_parts(28_497_963, 9726) + // Standard Error: 3_968 + .saturating_add(Weight::from_ref_time(176_840).saturating_mul(v.into())) + .saturating_add(T::DbWeight::get().reads(5_u64)) + .saturating_add(T::DbWeight::get().writes(2_u64)) + .saturating_add(Weight::from_proof_size(320).saturating_mul(v.into())) + } + /// Storage: Elections Candidates (r:1 w:0) + /// Proof Skipped: Elections Candidates (max_values: Some(1), max_size: None, mode: Measured) + /// Storage: Elections Members (r:1 w:0) + /// Proof Skipped: Elections Members (max_values: Some(1), max_size: None, mode: Measured) + /// Storage: Elections RunnersUp (r:1 w:0) + /// Proof Skipped: Elections RunnersUp (max_values: Some(1), max_size: None, mode: Measured) + /// Storage: Elections Voting (r:1 w:1) + /// Proof Skipped: Elections Voting (max_values: None, max_size: None, mode: Measured) + /// Storage: Balances Locks (r:1 w:1) + /// Proof: Balances Locks (max_values: None, max_size: Some(1299), added: 3774, mode: MaxEncodedLen) + /// The range of component `v` is `[2, 16]`. + fn vote_more(v: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `467 + v * (80 ±0)` + // Estimated: `9598 + v * (320 ±0)` + // Minimum execution time: 37_120 nanoseconds. + Weight::from_parts(38_455_302, 9598) + // Standard Error: 5_478 + .saturating_add(Weight::from_ref_time(219_678).saturating_mul(v.into())) + .saturating_add(T::DbWeight::get().reads(5_u64)) + .saturating_add(T::DbWeight::get().writes(2_u64)) + .saturating_add(Weight::from_proof_size(320).saturating_mul(v.into())) + } + /// Storage: Elections Candidates (r:1 w:0) + /// Proof Skipped: Elections Candidates (max_values: Some(1), max_size: None, mode: Measured) + /// Storage: Elections Members (r:1 w:0) + /// Proof Skipped: Elections Members (max_values: Some(1), max_size: None, mode: Measured) + /// Storage: Elections RunnersUp (r:1 w:0) + /// Proof Skipped: Elections RunnersUp (max_values: Some(1), max_size: None, mode: Measured) + /// Storage: Elections Voting (r:1 w:1) + /// Proof Skipped: Elections Voting (max_values: None, max_size: None, mode: Measured) + /// Storage: Balances Locks (r:1 w:1) + /// Proof: Balances Locks (max_values: None, max_size: Some(1299), added: 3774, mode: MaxEncodedLen) + /// The range of component `v` is `[2, 16]`. + fn vote_less(v: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `499 + v * (80 ±0)` + // Estimated: `9726 + v * (320 ±0)` + // Minimum execution time: 36_928 nanoseconds. + Weight::from_parts(38_334_669, 9726) + // Standard Error: 5_271 + .saturating_add(Weight::from_ref_time(232_355).saturating_mul(v.into())) + .saturating_add(T::DbWeight::get().reads(5_u64)) + .saturating_add(T::DbWeight::get().writes(2_u64)) + .saturating_add(Weight::from_proof_size(320).saturating_mul(v.into())) + } + /// Storage: Elections Voting (r:1 w:1) + /// Proof Skipped: Elections Voting (max_values: None, max_size: None, mode: Measured) + /// Storage: Balances Locks (r:1 w:1) + /// Proof: Balances Locks (max_values: None, max_size: Some(1299), added: 3774, mode: MaxEncodedLen) + fn remove_voter() -> Weight { + // Proof Size summary in bytes: + // Measured: `989` + // Estimated: `7238` + // Minimum execution time: 34_338 nanoseconds. + Weight::from_parts(35_672_000, 7238) + .saturating_add(T::DbWeight::get().reads(2_u64)) + .saturating_add(T::DbWeight::get().writes(2_u64)) + } + /// Storage: Elections Candidates (r:1 w:1) + /// Proof Skipped: Elections Candidates (max_values: Some(1), max_size: None, mode: Measured) + /// Storage: Elections Members (r:1 w:0) + /// Proof Skipped: Elections Members (max_values: Some(1), max_size: None, mode: Measured) + /// Storage: Elections RunnersUp (r:1 w:0) + /// Proof Skipped: Elections RunnersUp (max_values: Some(1), max_size: None, mode: Measured) + /// The range of component `c` is `[1, 64]`. + fn submit_candidacy(c: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `1697 + c * (48 ±0)` + // Estimated: `6576 + c * (144 ±0)` + // Minimum execution time: 31_864 nanoseconds. + Weight::from_parts(33_490_161, 6576) + // Standard Error: 2_643 + .saturating_add(Weight::from_ref_time(158_386).saturating_mul(c.into())) + .saturating_add(T::DbWeight::get().reads(3_u64)) + .saturating_add(T::DbWeight::get().writes(1_u64)) + .saturating_add(Weight::from_proof_size(144).saturating_mul(c.into())) + } + /// Storage: Elections Candidates (r:1 w:1) + /// Proof Skipped: Elections Candidates (max_values: Some(1), max_size: None, mode: Measured) + /// The range of component `c` is `[1, 64]`. + fn renounce_candidacy_candidate(c: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `349 + c * (48 ±0)` + // Estimated: `844 + c * (48 ±0)` + // Minimum execution time: 27_292 nanoseconds. + Weight::from_parts(28_364_955, 844) + // Standard Error: 1_335 + .saturating_add(Weight::from_ref_time(78_086).saturating_mul(c.into())) + .saturating_add(T::DbWeight::get().reads(1_u64)) + .saturating_add(T::DbWeight::get().writes(1_u64)) + .saturating_add(Weight::from_proof_size(48).saturating_mul(c.into())) + } + /// Storage: Elections Members (r:1 w:1) + /// Proof Skipped: Elections Members (max_values: Some(1), max_size: None, mode: Measured) + /// Storage: Elections RunnersUp (r:1 w:1) + /// Proof Skipped: Elections RunnersUp (max_values: Some(1), max_size: None, mode: Measured) + /// Storage: Council Prime (r:1 w:1) + /// Proof Skipped: Council Prime (max_values: Some(1), max_size: None, mode: Measured) + /// Storage: Council Proposals (r:1 w:0) + /// Proof Skipped: Council Proposals (max_values: Some(1), max_size: None, mode: Measured) + /// Storage: Council Members (r:0 w:1) + /// Proof Skipped: Council Members (max_values: Some(1), max_size: None, mode: Measured) + fn renounce_candidacy_members() -> Weight { + // Proof Size summary in bytes: + // Measured: `2027` + // Estimated: `12115` + // Minimum execution time: 45_975 nanoseconds. + Weight::from_parts(47_103_000, 12115) + .saturating_add(T::DbWeight::get().reads(4_u64)) + .saturating_add(T::DbWeight::get().writes(4_u64)) + } + /// Storage: Elections RunnersUp (r:1 w:1) + /// Proof Skipped: Elections RunnersUp (max_values: Some(1), max_size: None, mode: Measured) + fn renounce_candidacy_runners_up() -> Weight { + // Proof Size summary in bytes: + // Measured: `975` + // Estimated: `1470` + // Minimum execution time: 29_243 nanoseconds. + Weight::from_parts(30_582_000, 1470) + .saturating_add(T::DbWeight::get().reads(1_u64)) + .saturating_add(T::DbWeight::get().writes(1_u64)) + } + /// Storage: Benchmark Override (r:0 w:0) + /// Proof Skipped: Benchmark Override (max_values: None, max_size: None, mode: Measured) + fn remove_member_without_replacement() -> Weight { + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 2_000_000_000 nanoseconds. + Weight::from_ref_time(2_000_000_000_000) + } + /// Storage: Elections Members (r:1 w:1) + /// Proof Skipped: Elections Members (max_values: Some(1), max_size: None, mode: Measured) + /// Storage: System Account (r:1 w:1) + /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) + /// Storage: Elections RunnersUp (r:1 w:1) + /// Proof Skipped: Elections RunnersUp (max_values: Some(1), max_size: None, mode: Measured) + /// Storage: Council Prime (r:1 w:1) + /// Proof Skipped: Council Prime (max_values: Some(1), max_size: None, mode: Measured) + /// Storage: Council Proposals (r:1 w:0) + /// Proof Skipped: Council Proposals (max_values: Some(1), max_size: None, mode: Measured) + /// Storage: Council Members (r:0 w:1) + /// Proof Skipped: Council Members (max_values: Some(1), max_size: None, mode: Measured) + fn remove_member_with_replacement() -> Weight { + // Proof Size summary in bytes: + // Measured: `2027` + // Estimated: `14718` + // Minimum execution time: 52_527 nanoseconds. + Weight::from_parts(53_538_000, 14718) + .saturating_add(T::DbWeight::get().reads(5_u64)) + .saturating_add(T::DbWeight::get().writes(5_u64)) + } + /// Storage: Elections Voting (r:513 w:512) + /// Proof Skipped: Elections Voting (max_values: None, max_size: None, mode: Measured) + /// Storage: Elections Members (r:1 w:0) + /// Proof Skipped: Elections Members (max_values: Some(1), max_size: None, mode: Measured) + /// Storage: Elections RunnersUp (r:1 w:0) + /// Proof Skipped: Elections RunnersUp (max_values: Some(1), max_size: None, mode: Measured) + /// Storage: Elections Candidates (r:1 w:0) + /// Proof Skipped: Elections Candidates (max_values: Some(1), max_size: None, mode: Measured) + /// Storage: Balances Locks (r:512 w:512) + /// Proof: Balances Locks (max_values: None, max_size: Some(1299), added: 3774, mode: MaxEncodedLen) + /// Storage: System Account (r:512 w:512) + /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) + /// The range of component `v` is `[256, 512]`. + /// The range of component `d` is `[0, 256]`. + fn clean_defunct_voters(v: u32, _d: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `1115 + v * (875 ±0)` + // Estimated: `8448 + v * (12352 ±0)` + // Minimum execution time: 14_934_185 nanoseconds. + Weight::from_parts(15_014_057_000, 8448) + // Standard Error: 245_588 + .saturating_add(Weight::from_ref_time(35_586_946).saturating_mul(v.into())) + .saturating_add(T::DbWeight::get().reads(4_u64)) + .saturating_add(T::DbWeight::get().reads((3_u64).saturating_mul(v.into()))) + .saturating_add(T::DbWeight::get().writes((3_u64).saturating_mul(v.into()))) + .saturating_add(Weight::from_proof_size(12352).saturating_mul(v.into())) + } + /// Storage: Elections Candidates (r:1 w:1) + /// Proof Skipped: Elections Candidates (max_values: Some(1), max_size: None, mode: Measured) + /// Storage: Elections Members (r:1 w:1) + /// Proof Skipped: Elections Members (max_values: Some(1), max_size: None, mode: Measured) + /// Storage: Elections RunnersUp (r:1 w:1) + /// Proof Skipped: Elections RunnersUp (max_values: Some(1), max_size: None, mode: Measured) + /// Storage: Elections Voting (r:513 w:0) + /// Proof Skipped: Elections Voting (max_values: None, max_size: None, mode: Measured) + /// Storage: Council Proposals (r:1 w:0) + /// Proof Skipped: Council Proposals (max_values: Some(1), max_size: None, mode: Measured) + /// Storage: System Account (r:44 w:44) + /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) + /// Storage: Elections ElectionRounds (r:1 w:1) + /// Proof Skipped: Elections ElectionRounds (max_values: Some(1), max_size: None, mode: Measured) + /// Storage: Council Members (r:0 w:1) + /// Proof Skipped: Council Members (max_values: Some(1), max_size: None, mode: Measured) + /// Storage: Council Prime (r:0 w:1) + /// Proof Skipped: Council Prime (max_values: Some(1), max_size: None, mode: Measured) + /// The range of component `c` is `[1, 64]`. + /// The range of component `v` is `[1, 512]`. + /// The range of component `e` is `[512, 8192]`. + fn election_phragmen(c: u32, v: u32, e: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `0 + v * (638 ±0) + e * (28 ±0)` + // Estimated: `330033 + v * (5229 ±6) + e * (89 ±0) + c * (2135 ±7)` + // Minimum execution time: 1_273_671 nanoseconds. + Weight::from_parts(1_279_716_000, 330033) + // Standard Error: 543_277 + .saturating_add(Weight::from_ref_time(20_613_753).saturating_mul(v.into())) + // Standard Error: 34_857 + .saturating_add(Weight::from_ref_time(688_354).saturating_mul(e.into())) + .saturating_add(T::DbWeight::get().reads(21_u64)) + .saturating_add(T::DbWeight::get().reads((1_u64).saturating_mul(c.into()))) + .saturating_add(T::DbWeight::get().reads((1_u64).saturating_mul(v.into()))) + .saturating_add(T::DbWeight::get().writes(6_u64)) + .saturating_add(T::DbWeight::get().writes((1_u64).saturating_mul(c.into()))) + .saturating_add(Weight::from_proof_size(5229).saturating_mul(v.into())) + .saturating_add(Weight::from_proof_size(89).saturating_mul(e.into())) + .saturating_add(Weight::from_proof_size(2135).saturating_mul(c.into())) + } +} + +// For backwards compatibility and tests +impl WeightInfo for () { + /// Storage: Elections Candidates (r:1 w:0) + /// Proof Skipped: Elections Candidates (max_values: Some(1), max_size: None, mode: Measured) + /// Storage: Elections Members (r:1 w:0) + /// Proof Skipped: Elections Members (max_values: Some(1), max_size: None, mode: Measured) + /// Storage: Elections RunnersUp (r:1 w:0) + /// Proof Skipped: Elections RunnersUp (max_values: Some(1), max_size: None, mode: Measured) + /// Storage: Elections Voting (r:1 w:1) + /// Proof Skipped: Elections Voting (max_values: None, max_size: None, mode: Measured) + /// Storage: Balances Locks (r:1 w:1) + /// Proof: Balances Locks (max_values: None, max_size: Some(1299), added: 3774, mode: MaxEncodedLen) + /// The range of component `v` is `[1, 16]`. + fn vote_equal(v: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `499 + v * (80 ±0)` + // Estimated: `9726 + v * (320 ±0)` + // Minimum execution time: 27_362 nanoseconds. + Weight::from_parts(28_497_963, 9726) + // Standard Error: 3_968 + .saturating_add(Weight::from_ref_time(176_840).saturating_mul(v.into())) + .saturating_add(RocksDbWeight::get().reads(5_u64)) + .saturating_add(RocksDbWeight::get().writes(2_u64)) + .saturating_add(Weight::from_proof_size(320).saturating_mul(v.into())) + } + /// Storage: Elections Candidates (r:1 w:0) + /// Proof Skipped: Elections Candidates (max_values: Some(1), max_size: None, mode: Measured) + /// Storage: Elections Members (r:1 w:0) + /// Proof Skipped: Elections Members (max_values: Some(1), max_size: None, mode: Measured) + /// Storage: Elections RunnersUp (r:1 w:0) + /// Proof Skipped: Elections RunnersUp (max_values: Some(1), max_size: None, mode: Measured) + /// Storage: Elections Voting (r:1 w:1) + /// Proof Skipped: Elections Voting (max_values: None, max_size: None, mode: Measured) + /// Storage: Balances Locks (r:1 w:1) + /// Proof: Balances Locks (max_values: None, max_size: Some(1299), added: 3774, mode: MaxEncodedLen) + /// The range of component `v` is `[2, 16]`. + fn vote_more(v: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `467 + v * (80 ±0)` + // Estimated: `9598 + v * (320 ±0)` + // Minimum execution time: 37_120 nanoseconds. + Weight::from_parts(38_455_302, 9598) + // Standard Error: 5_478 + .saturating_add(Weight::from_ref_time(219_678).saturating_mul(v.into())) + .saturating_add(RocksDbWeight::get().reads(5_u64)) + .saturating_add(RocksDbWeight::get().writes(2_u64)) + .saturating_add(Weight::from_proof_size(320).saturating_mul(v.into())) + } + /// Storage: Elections Candidates (r:1 w:0) + /// Proof Skipped: Elections Candidates (max_values: Some(1), max_size: None, mode: Measured) + /// Storage: Elections Members (r:1 w:0) + /// Proof Skipped: Elections Members (max_values: Some(1), max_size: None, mode: Measured) + /// Storage: Elections RunnersUp (r:1 w:0) + /// Proof Skipped: Elections RunnersUp (max_values: Some(1), max_size: None, mode: Measured) + /// Storage: Elections Voting (r:1 w:1) + /// Proof Skipped: Elections Voting (max_values: None, max_size: None, mode: Measured) + /// Storage: Balances Locks (r:1 w:1) + /// Proof: Balances Locks (max_values: None, max_size: Some(1299), added: 3774, mode: MaxEncodedLen) + /// The range of component `v` is `[2, 16]`. + fn vote_less(v: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `499 + v * (80 ±0)` + // Estimated: `9726 + v * (320 ±0)` + // Minimum execution time: 36_928 nanoseconds. + Weight::from_parts(38_334_669, 9726) + // Standard Error: 5_271 + .saturating_add(Weight::from_ref_time(232_355).saturating_mul(v.into())) + .saturating_add(RocksDbWeight::get().reads(5_u64)) + .saturating_add(RocksDbWeight::get().writes(2_u64)) + .saturating_add(Weight::from_proof_size(320).saturating_mul(v.into())) + } + /// Storage: Elections Voting (r:1 w:1) + /// Proof Skipped: Elections Voting (max_values: None, max_size: None, mode: Measured) + /// Storage: Balances Locks (r:1 w:1) + /// Proof: Balances Locks (max_values: None, max_size: Some(1299), added: 3774, mode: MaxEncodedLen) + fn remove_voter() -> Weight { + // Proof Size summary in bytes: + // Measured: `989` + // Estimated: `7238` + // Minimum execution time: 34_338 nanoseconds. + Weight::from_parts(35_672_000, 7238) + .saturating_add(RocksDbWeight::get().reads(2_u64)) + .saturating_add(RocksDbWeight::get().writes(2_u64)) + } + /// Storage: Elections Candidates (r:1 w:1) + /// Proof Skipped: Elections Candidates (max_values: Some(1), max_size: None, mode: Measured) + /// Storage: Elections Members (r:1 w:0) + /// Proof Skipped: Elections Members (max_values: Some(1), max_size: None, mode: Measured) + /// Storage: Elections RunnersUp (r:1 w:0) + /// Proof Skipped: Elections RunnersUp (max_values: Some(1), max_size: None, mode: Measured) + /// The range of component `c` is `[1, 64]`. + fn submit_candidacy(c: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `1697 + c * (48 ±0)` + // Estimated: `6576 + c * (144 ±0)` + // Minimum execution time: 31_864 nanoseconds. + Weight::from_parts(33_490_161, 6576) + // Standard Error: 2_643 + .saturating_add(Weight::from_ref_time(158_386).saturating_mul(c.into())) + .saturating_add(RocksDbWeight::get().reads(3_u64)) + .saturating_add(RocksDbWeight::get().writes(1_u64)) + .saturating_add(Weight::from_proof_size(144).saturating_mul(c.into())) + } + /// Storage: Elections Candidates (r:1 w:1) + /// Proof Skipped: Elections Candidates (max_values: Some(1), max_size: None, mode: Measured) + /// The range of component `c` is `[1, 64]`. + fn renounce_candidacy_candidate(c: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `349 + c * (48 ±0)` + // Estimated: `844 + c * (48 ±0)` + // Minimum execution time: 27_292 nanoseconds. + Weight::from_parts(28_364_955, 844) + // Standard Error: 1_335 + .saturating_add(Weight::from_ref_time(78_086).saturating_mul(c.into())) + .saturating_add(RocksDbWeight::get().reads(1_u64)) + .saturating_add(RocksDbWeight::get().writes(1_u64)) + .saturating_add(Weight::from_proof_size(48).saturating_mul(c.into())) + } + /// Storage: Elections Members (r:1 w:1) + /// Proof Skipped: Elections Members (max_values: Some(1), max_size: None, mode: Measured) + /// Storage: Elections RunnersUp (r:1 w:1) + /// Proof Skipped: Elections RunnersUp (max_values: Some(1), max_size: None, mode: Measured) + /// Storage: Council Prime (r:1 w:1) + /// Proof Skipped: Council Prime (max_values: Some(1), max_size: None, mode: Measured) + /// Storage: Council Proposals (r:1 w:0) + /// Proof Skipped: Council Proposals (max_values: Some(1), max_size: None, mode: Measured) + /// Storage: Council Members (r:0 w:1) + /// Proof Skipped: Council Members (max_values: Some(1), max_size: None, mode: Measured) + fn renounce_candidacy_members() -> Weight { + // Proof Size summary in bytes: + // Measured: `2027` + // Estimated: `12115` + // Minimum execution time: 45_975 nanoseconds. + Weight::from_parts(47_103_000, 12115) + .saturating_add(RocksDbWeight::get().reads(4_u64)) + .saturating_add(RocksDbWeight::get().writes(4_u64)) + } + /// Storage: Elections RunnersUp (r:1 w:1) + /// Proof Skipped: Elections RunnersUp (max_values: Some(1), max_size: None, mode: Measured) + fn renounce_candidacy_runners_up() -> Weight { + // Proof Size summary in bytes: + // Measured: `975` + // Estimated: `1470` + // Minimum execution time: 29_243 nanoseconds. + Weight::from_parts(30_582_000, 1470) + .saturating_add(RocksDbWeight::get().reads(1_u64)) + .saturating_add(RocksDbWeight::get().writes(1_u64)) + } + /// Storage: Benchmark Override (r:0 w:0) + /// Proof Skipped: Benchmark Override (max_values: None, max_size: None, mode: Measured) + fn remove_member_without_replacement() -> Weight { + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 2_000_000_000 nanoseconds. + Weight::from_ref_time(2_000_000_000_000) + } + /// Storage: Elections Members (r:1 w:1) + /// Proof Skipped: Elections Members (max_values: Some(1), max_size: None, mode: Measured) + /// Storage: System Account (r:1 w:1) + /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) + /// Storage: Elections RunnersUp (r:1 w:1) + /// Proof Skipped: Elections RunnersUp (max_values: Some(1), max_size: None, mode: Measured) + /// Storage: Council Prime (r:1 w:1) + /// Proof Skipped: Council Prime (max_values: Some(1), max_size: None, mode: Measured) + /// Storage: Council Proposals (r:1 w:0) + /// Proof Skipped: Council Proposals (max_values: Some(1), max_size: None, mode: Measured) + /// Storage: Council Members (r:0 w:1) + /// Proof Skipped: Council Members (max_values: Some(1), max_size: None, mode: Measured) + fn remove_member_with_replacement() -> Weight { + // Proof Size summary in bytes: + // Measured: `2027` + // Estimated: `14718` + // Minimum execution time: 52_527 nanoseconds. + Weight::from_parts(53_538_000, 14718) + .saturating_add(RocksDbWeight::get().reads(5_u64)) + .saturating_add(RocksDbWeight::get().writes(5_u64)) + } + /// Storage: Elections Voting (r:513 w:512) + /// Proof Skipped: Elections Voting (max_values: None, max_size: None, mode: Measured) + /// Storage: Elections Members (r:1 w:0) + /// Proof Skipped: Elections Members (max_values: Some(1), max_size: None, mode: Measured) + /// Storage: Elections RunnersUp (r:1 w:0) + /// Proof Skipped: Elections RunnersUp (max_values: Some(1), max_size: None, mode: Measured) + /// Storage: Elections Candidates (r:1 w:0) + /// Proof Skipped: Elections Candidates (max_values: Some(1), max_size: None, mode: Measured) + /// Storage: Balances Locks (r:512 w:512) + /// Proof: Balances Locks (max_values: None, max_size: Some(1299), added: 3774, mode: MaxEncodedLen) + /// Storage: System Account (r:512 w:512) + /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) + /// The range of component `v` is `[256, 512]`. + /// The range of component `d` is `[0, 256]`. + fn clean_defunct_voters(v: u32, _d: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `1115 + v * (875 ±0)` + // Estimated: `8448 + v * (12352 ±0)` + // Minimum execution time: 14_934_185 nanoseconds. + Weight::from_parts(15_014_057_000, 8448) + // Standard Error: 245_588 + .saturating_add(Weight::from_ref_time(35_586_946).saturating_mul(v.into())) + .saturating_add(RocksDbWeight::get().reads(4_u64)) + .saturating_add(RocksDbWeight::get().reads((3_u64).saturating_mul(v.into()))) + .saturating_add(RocksDbWeight::get().writes((3_u64).saturating_mul(v.into()))) + .saturating_add(Weight::from_proof_size(12352).saturating_mul(v.into())) + } + /// Storage: Elections Candidates (r:1 w:1) + /// Proof Skipped: Elections Candidates (max_values: Some(1), max_size: None, mode: Measured) + /// Storage: Elections Members (r:1 w:1) + /// Proof Skipped: Elections Members (max_values: Some(1), max_size: None, mode: Measured) + /// Storage: Elections RunnersUp (r:1 w:1) + /// Proof Skipped: Elections RunnersUp (max_values: Some(1), max_size: None, mode: Measured) + /// Storage: Elections Voting (r:513 w:0) + /// Proof Skipped: Elections Voting (max_values: None, max_size: None, mode: Measured) + /// Storage: Council Proposals (r:1 w:0) + /// Proof Skipped: Council Proposals (max_values: Some(1), max_size: None, mode: Measured) + /// Storage: System Account (r:44 w:44) + /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) + /// Storage: Elections ElectionRounds (r:1 w:1) + /// Proof Skipped: Elections ElectionRounds (max_values: Some(1), max_size: None, mode: Measured) + /// Storage: Council Members (r:0 w:1) + /// Proof Skipped: Council Members (max_values: Some(1), max_size: None, mode: Measured) + /// Storage: Council Prime (r:0 w:1) + /// Proof Skipped: Council Prime (max_values: Some(1), max_size: None, mode: Measured) + /// The range of component `c` is `[1, 64]`. + /// The range of component `v` is `[1, 512]`. + /// The range of component `e` is `[512, 8192]`. + fn election_phragmen(c: u32, v: u32, e: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `0 + v * (638 ±0) + e * (28 ±0)` + // Estimated: `330033 + v * (5229 ±6) + e * (89 ±0) + c * (2135 ±7)` + // Minimum execution time: 1_273_671 nanoseconds. + Weight::from_parts(1_279_716_000, 330033) + // Standard Error: 543_277 + .saturating_add(Weight::from_ref_time(20_613_753).saturating_mul(v.into())) + // Standard Error: 34_857 + .saturating_add(Weight::from_ref_time(688_354).saturating_mul(e.into())) + .saturating_add(RocksDbWeight::get().reads(21_u64)) + .saturating_add(RocksDbWeight::get().reads((1_u64).saturating_mul(c.into()))) + .saturating_add(RocksDbWeight::get().reads((1_u64).saturating_mul(v.into()))) + .saturating_add(RocksDbWeight::get().writes(6_u64)) + .saturating_add(RocksDbWeight::get().writes((1_u64).saturating_mul(c.into()))) + .saturating_add(Weight::from_proof_size(5229).saturating_mul(v.into())) + .saturating_add(Weight::from_proof_size(89).saturating_mul(e.into())) + .saturating_add(Weight::from_proof_size(2135).saturating_mul(c.into())) + } +} diff --git a/frame/elections/src/weights.rs b/frame/elections/src/weights.rs deleted file mode 100644 index 8caa93f0a..000000000 --- a/frame/elections/src/weights.rs +++ /dev/null @@ -1,397 +0,0 @@ -// This file is part of Substrate. - -// Copyright (C) 2022 Parity Technologies (UK) Ltd. -// SPDX-License-Identifier: Apache-2.0 - -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -//! Autogenerated weights for pallet_elections -//! -//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev -//! DATE: 2022-11-22, STEPS: `50`, REPEAT: 20, LOW RANGE: `[]`, HIGH RANGE: `[]` -//! HOSTNAME: `bm3`, CPU: `Intel(R) Core(TM) i7-7700K CPU @ 4.20GHz` -//! EXECUTION: Some(Wasm), WASM-EXECUTION: Compiled, CHAIN: Some("dev"), DB CACHE: 1024 - -// Executed Command: -// /home/benchbot/cargo_target_dir/production/substrate -// benchmark -// pallet -// --steps=50 -// --repeat=20 -// --extrinsic=* -// --execution=wasm -// --wasm-execution=compiled -// --heap-pages=4096 -// --json-file=/var/lib/gitlab-runner/builds/zyw4fam_/0/parity/mirrors/substrate/.git/.artifacts/bench.json -// --pallet=pallet_elections -// --chain=dev -// --header=./HEADER-APACHE2 -// --output=./frame/elections/src/weights.rs -// --template=./.maintain/frame-weight-template.hbs - -#![cfg_attr(rustfmt, rustfmt_skip)] -#![allow(unused_parens)] -#![allow(unused_imports)] - -use frame_support::{traits::Get, weights::{Weight, constants::RocksDbWeight}}; -use sp_std::marker::PhantomData; - -/// Weight functions needed for pallet_elections. -pub trait WeightInfo { - fn vote_equal(v: u32, ) -> Weight; - fn vote_more(v: u32, ) -> Weight; - fn vote_less(v: u32, ) -> Weight; - fn remove_voter() -> Weight; - fn submit_candidacy(c: u32, ) -> Weight; - fn renounce_candidacy_candidate(c: u32, ) -> Weight; - fn renounce_candidacy_members() -> Weight; - fn renounce_candidacy_runners_up() -> Weight; - fn remove_member_without_replacement() -> Weight; - fn remove_member_with_replacement() -> Weight; - fn clean_defunct_voters(v: u32, d: u32, ) -> Weight; - fn pre_solve_election(c: u32, v: u32, e: u32, ) -> Weight; - fn post_solve_election(c: u32, v: u32, e: u32, ) -> Weight; -} - -/// Weights for pallet_elections using the Substrate node and recommended hardware. -pub struct SubstrateWeight(PhantomData); -impl WeightInfo for SubstrateWeight { - // Storage: Elections Candidates (r:1 w:0) - // Storage: Elections Members (r:1 w:0) - // Storage: Elections RunnersUp (r:1 w:0) - // Storage: Elections Voting (r:1 w:1) - // Storage: Balances Locks (r:1 w:1) - /// The range of component `v` is `[1, 16]`. - fn vote_equal(v: u32, ) -> Weight { - // Minimum execution time: 37_500 nanoseconds. - Weight::from_ref_time(38_575_649) - // Standard Error: 2_961 - .saturating_add(Weight::from_ref_time(154_225).saturating_mul(v.into())) - .saturating_add(T::DbWeight::get().reads(5)) - .saturating_add(T::DbWeight::get().writes(2)) - } - // Storage: Elections Candidates (r:1 w:0) - // Storage: Elections Members (r:1 w:0) - // Storage: Elections RunnersUp (r:1 w:0) - // Storage: Elections Voting (r:1 w:1) - // Storage: Balances Locks (r:1 w:1) - /// The range of component `v` is `[2, 16]`. - fn vote_more(v: u32, ) -> Weight { - // Minimum execution time: 48_836 nanoseconds. - Weight::from_ref_time(49_566_969) - // Standard Error: 3_258 - .saturating_add(Weight::from_ref_time(153_342).saturating_mul(v.into())) - .saturating_add(T::DbWeight::get().reads(5)) - .saturating_add(T::DbWeight::get().writes(2)) - } - // Storage: Elections Candidates (r:1 w:0) - // Storage: Elections Members (r:1 w:0) - // Storage: Elections RunnersUp (r:1 w:0) - // Storage: Elections Voting (r:1 w:1) - // Storage: Balances Locks (r:1 w:1) - /// The range of component `v` is `[2, 16]`. - fn vote_less(v: u32, ) -> Weight { - // Minimum execution time: 47_664 nanoseconds. - Weight::from_ref_time(48_768_157) - // Standard Error: 3_321 - .saturating_add(Weight::from_ref_time(215_112).saturating_mul(v.into())) - .saturating_add(T::DbWeight::get().reads(5)) - .saturating_add(T::DbWeight::get().writes(2)) - } - // Storage: Elections Voting (r:1 w:1) - // Storage: Balances Locks (r:1 w:1) - fn remove_voter() -> Weight { - // Minimum execution time: 47_146 nanoseconds. - Weight::from_ref_time(47_846_000) - .saturating_add(T::DbWeight::get().reads(2)) - .saturating_add(T::DbWeight::get().writes(2)) - } - // Storage: Elections Candidates (r:1 w:1) - // Storage: Elections Members (r:1 w:0) - // Storage: Elections RunnersUp (r:1 w:0) - /// The range of component `c` is `[1, 1000]`. - fn submit_candidacy(c: u32, ) -> Weight { - // Minimum execution time: 42_799 nanoseconds. - Weight::from_ref_time(46_920_164) - // Standard Error: 780 - .saturating_add(Weight::from_ref_time(81_672).saturating_mul(c.into())) - .saturating_add(T::DbWeight::get().reads(3)) - .saturating_add(T::DbWeight::get().writes(1)) - } - // Storage: Elections Candidates (r:1 w:1) - /// The range of component `c` is `[1, 1000]`. - fn renounce_candidacy_candidate(c: u32, ) -> Weight { - // Minimum execution time: 40_946 nanoseconds. - Weight::from_ref_time(53_109_738) - // Standard Error: 1_220 - .saturating_add(Weight::from_ref_time(60_643).saturating_mul(c.into())) - .saturating_add(T::DbWeight::get().reads(1)) - .saturating_add(T::DbWeight::get().writes(1)) - } - // Storage: Elections Members (r:1 w:1) - // Storage: Elections RunnersUp (r:1 w:1) - // Storage: Council Prime (r:1 w:1) - // Storage: Council Proposals (r:1 w:0) - // Storage: Council Members (r:0 w:1) - fn renounce_candidacy_members() -> Weight { - // Minimum execution time: 53_454 nanoseconds. - Weight::from_ref_time(53_921_000) - .saturating_add(T::DbWeight::get().reads(4)) - .saturating_add(T::DbWeight::get().writes(4)) - } - // Storage: Elections RunnersUp (r:1 w:1) - fn renounce_candidacy_runners_up() -> Weight { - // Minimum execution time: 41_275 nanoseconds. - Weight::from_ref_time(42_444_000) - .saturating_add(T::DbWeight::get().reads(1)) - .saturating_add(T::DbWeight::get().writes(1)) - } - // Storage: Benchmark Override (r:0 w:0) - fn remove_member_without_replacement() -> Weight { - // Minimum execution time: 2_000_000_000 nanoseconds. - Weight::from_ref_time(2_000_000_000_000) - } - // Storage: Elections Members (r:1 w:1) - // Storage: System Account (r:1 w:1) - // Storage: Elections RunnersUp (r:1 w:1) - // Storage: Council Prime (r:1 w:1) - // Storage: Council Proposals (r:1 w:0) - // Storage: Council Members (r:0 w:1) - fn remove_member_with_replacement() -> Weight { - // Minimum execution time: 62_040 nanoseconds. - Weight::from_ref_time(62_569_000) - .saturating_add(T::DbWeight::get().reads(5)) - .saturating_add(T::DbWeight::get().writes(5)) - } - // Storage: Elections Voting (r:5001 w:5000) - // Storage: Elections Members (r:1 w:0) - // Storage: Elections RunnersUp (r:1 w:0) - // Storage: Elections Candidates (r:1 w:0) - // Storage: Balances Locks (r:5000 w:5000) - // Storage: System Account (r:5000 w:5000) - /// The range of component `v` is `[5000, 10000]`. - /// The range of component `d` is `[0, 5000]`. - fn clean_defunct_voters(v: u32, _d: u32, ) -> Weight { - // Minimum execution time: 298_212_195 nanoseconds. - Weight::from_ref_time(298_678_889_000) - // Standard Error: 264_713 - .saturating_add(Weight::from_ref_time(38_222_955).saturating_mul(v.into())) - .saturating_add(T::DbWeight::get().reads(4)) - .saturating_add(T::DbWeight::get().reads((3_u64).saturating_mul(v.into()))) - .saturating_add(T::DbWeight::get().writes((3_u64).saturating_mul(v.into()))) - } - // Storage: Elections Candidates (r:1 w:0) - // Storage: Elections Members (r:1 w:0) - // Storage: Elections RunnersUp (r:1 w:0) - // Storage: Elections Voting (r:10001 w:0) - /// The range of component `c` is `[1, 1000]`. - /// The range of component `v` is `[1, 10000]`. - /// The range of component `e` is `[10000, 160000]`. - fn pre_solve_election(_c: u32, v: u32, _e: u32, ) -> Weight { - // Minimum execution time: 6_543_626 nanoseconds. - Weight::from_ref_time(6_627_885_000) - // Standard Error: 14_605 - .saturating_add(Weight::from_ref_time(7_613_226).saturating_mul(v.into())) - .saturating_add(T::DbWeight::get().reads(296)) - .saturating_add(T::DbWeight::get().reads((1_u64).saturating_mul(v.into()))) - } - // Storage: Elections Members (r:1 w:1) - // Storage: Elections RunnersUp (r:1 w:1) - // Storage: Council Proposals (r:1 w:0) - // Storage: Elections ElectionRounds (r:1 w:1) - // Storage: Elections Candidates (r:0 w:1) - // Storage: Council Members (r:0 w:1) - // Storage: Council Prime (r:0 w:1) - // Storage: System Account (r:1 w:1) - /// The range of component `c` is `[1, 1000]`. - /// The range of component `v` is `[1, 10000]`. - /// The range of component `e` is `[10000, 160000]`. - fn post_solve_election(c: u32, v: u32, _e: u32, ) -> Weight { - // Minimum execution time: 3_843_566 nanoseconds. - Weight::from_ref_time(3_854_020_000) - // Standard Error: 59_766 - .saturating_add(Weight::from_ref_time(9_622_797).saturating_mul(c.into())) - // Standard Error: 5_975 - .saturating_add(Weight::from_ref_time(541_471).saturating_mul(v.into())) - .saturating_add(T::DbWeight::get().reads(4)) - .saturating_add(T::DbWeight::get().reads((1_u64).saturating_mul(c.into()))) - .saturating_add(T::DbWeight::get().writes(6)) - .saturating_add(T::DbWeight::get().writes((1_u64).saturating_mul(c.into()))) - } -} - -// For backwards compatibility and tests -impl WeightInfo for () { - // Storage: Elections Candidates (r:1 w:0) - // Storage: Elections Members (r:1 w:0) - // Storage: Elections RunnersUp (r:1 w:0) - // Storage: Elections Voting (r:1 w:1) - // Storage: Balances Locks (r:1 w:1) - /// The range of component `v` is `[1, 16]`. - fn vote_equal(v: u32, ) -> Weight { - // Minimum execution time: 37_500 nanoseconds. - Weight::from_ref_time(38_575_649) - // Standard Error: 2_961 - .saturating_add(Weight::from_ref_time(154_225).saturating_mul(v.into())) - .saturating_add(RocksDbWeight::get().reads(5)) - .saturating_add(RocksDbWeight::get().writes(2)) - } - // Storage: Elections Candidates (r:1 w:0) - // Storage: Elections Members (r:1 w:0) - // Storage: Elections RunnersUp (r:1 w:0) - // Storage: Elections Voting (r:1 w:1) - // Storage: Balances Locks (r:1 w:1) - /// The range of component `v` is `[2, 16]`. - fn vote_more(v: u32, ) -> Weight { - // Minimum execution time: 48_836 nanoseconds. - Weight::from_ref_time(49_566_969) - // Standard Error: 3_258 - .saturating_add(Weight::from_ref_time(153_342).saturating_mul(v.into())) - .saturating_add(RocksDbWeight::get().reads(5)) - .saturating_add(RocksDbWeight::get().writes(2)) - } - // Storage: Elections Candidates (r:1 w:0) - // Storage: Elections Members (r:1 w:0) - // Storage: Elections RunnersUp (r:1 w:0) - // Storage: Elections Voting (r:1 w:1) - // Storage: Balances Locks (r:1 w:1) - /// The range of component `v` is `[2, 16]`. - fn vote_less(v: u32, ) -> Weight { - // Minimum execution time: 47_664 nanoseconds. - Weight::from_ref_time(48_768_157) - // Standard Error: 3_321 - .saturating_add(Weight::from_ref_time(215_112).saturating_mul(v.into())) - .saturating_add(RocksDbWeight::get().reads(5)) - .saturating_add(RocksDbWeight::get().writes(2)) - } - // Storage: Elections Voting (r:1 w:1) - // Storage: Balances Locks (r:1 w:1) - fn remove_voter() -> Weight { - // Minimum execution time: 47_146 nanoseconds. - Weight::from_ref_time(47_846_000) - .saturating_add(RocksDbWeight::get().reads(2)) - .saturating_add(RocksDbWeight::get().writes(2)) - } - // Storage: Elections Candidates (r:1 w:1) - // Storage: Elections Members (r:1 w:0) - // Storage: Elections RunnersUp (r:1 w:0) - /// The range of component `c` is `[1, 1000]`. - fn submit_candidacy(c: u32, ) -> Weight { - // Minimum execution time: 42_799 nanoseconds. - Weight::from_ref_time(46_920_164) - // Standard Error: 780 - .saturating_add(Weight::from_ref_time(81_672).saturating_mul(c.into())) - .saturating_add(RocksDbWeight::get().reads(3)) - .saturating_add(RocksDbWeight::get().writes(1)) - } - // Storage: Elections Candidates (r:1 w:1) - /// The range of component `c` is `[1, 1000]`. - fn renounce_candidacy_candidate(c: u32, ) -> Weight { - // Minimum execution time: 40_946 nanoseconds. - Weight::from_ref_time(53_109_738) - // Standard Error: 1_220 - .saturating_add(Weight::from_ref_time(60_643).saturating_mul(c.into())) - .saturating_add(RocksDbWeight::get().reads(1)) - .saturating_add(RocksDbWeight::get().writes(1)) - } - // Storage: Elections Members (r:1 w:1) - // Storage: Elections RunnersUp (r:1 w:1) - // Storage: Council Prime (r:1 w:1) - // Storage: Council Proposals (r:1 w:0) - // Storage: Council Members (r:0 w:1) - fn renounce_candidacy_members() -> Weight { - // Minimum execution time: 53_454 nanoseconds. - Weight::from_ref_time(53_921_000) - .saturating_add(RocksDbWeight::get().reads(4)) - .saturating_add(RocksDbWeight::get().writes(4)) - } - // Storage: Elections RunnersUp (r:1 w:1) - fn renounce_candidacy_runners_up() -> Weight { - // Minimum execution time: 41_275 nanoseconds. - Weight::from_ref_time(42_444_000) - .saturating_add(RocksDbWeight::get().reads(1)) - .saturating_add(RocksDbWeight::get().writes(1)) - } - // Storage: Benchmark Override (r:0 w:0) - fn remove_member_without_replacement() -> Weight { - // Minimum execution time: 2_000_000_000 nanoseconds. - Weight::from_ref_time(2_000_000_000_000) - } - // Storage: Elections Members (r:1 w:1) - // Storage: System Account (r:1 w:1) - // Storage: Elections RunnersUp (r:1 w:1) - // Storage: Council Prime (r:1 w:1) - // Storage: Council Proposals (r:1 w:0) - // Storage: Council Members (r:0 w:1) - fn remove_member_with_replacement() -> Weight { - // Minimum execution time: 62_040 nanoseconds. - Weight::from_ref_time(62_569_000) - .saturating_add(RocksDbWeight::get().reads(5)) - .saturating_add(RocksDbWeight::get().writes(5)) - } - // Storage: Elections Voting (r:5001 w:5000) - // Storage: Elections Members (r:1 w:0) - // Storage: Elections RunnersUp (r:1 w:0) - // Storage: Elections Candidates (r:1 w:0) - // Storage: Balances Locks (r:5000 w:5000) - // Storage: System Account (r:5000 w:5000) - /// The range of component `v` is `[5000, 10000]`. - /// The range of component `d` is `[0, 5000]`. - fn clean_defunct_voters(v: u32, _d: u32, ) -> Weight { - // Minimum execution time: 298_212_195 nanoseconds. - Weight::from_ref_time(298_678_889_000) - // Standard Error: 264_713 - .saturating_add(Weight::from_ref_time(38_222_955).saturating_mul(v.into())) - .saturating_add(RocksDbWeight::get().reads(4)) - .saturating_add(RocksDbWeight::get().reads((3_u64).saturating_mul(v.into()))) - .saturating_add(RocksDbWeight::get().writes((3_u64).saturating_mul(v.into()))) - } - // Storage: Elections Candidates (r:1 w:0) - // Storage: Elections Members (r:1 w:0) - // Storage: Elections RunnersUp (r:1 w:0) - // Storage: Elections Voting (r:10001 w:0) - /// The range of component `c` is `[1, 1000]`. - /// The range of component `v` is `[1, 10000]`. - /// The range of component `e` is `[10000, 160000]`. - fn pre_solve_election(_c: u32, v: u32, _e: u32, ) -> Weight { - // Minimum execution time: 6_543_626 nanoseconds. - Weight::from_ref_time(6_627_885_000) - // Standard Error: 14_605 - .saturating_add(Weight::from_ref_time(7_613_226).saturating_mul(v.into())) - .saturating_add(RocksDbWeight::get().reads(296)) - .saturating_add(RocksDbWeight::get().reads((1_u64).saturating_mul(v.into()))) - } - // Storage: Elections Members (r:1 w:1) - // Storage: Elections RunnersUp (r:1 w:1) - // Storage: Council Proposals (r:1 w:0) - // Storage: Elections ElectionRounds (r:1 w:1) - // Storage: Elections Candidates (r:0 w:1) - // Storage: Council Members (r:0 w:1) - // Storage: Council Prime (r:0 w:1) - // Storage: System Account (r:1 w:1) - /// The range of component `c` is `[1, 1000]`. - /// The range of component `v` is `[1, 10000]`. - /// The range of component `e` is `[10000, 160000]`. - fn post_solve_election(c: u32, v: u32, _e: u32, ) -> Weight { - // Minimum execution time: 3_843_566 nanoseconds. - Weight::from_ref_time(3_854_020_000) - // Standard Error: 59_766 - .saturating_add(Weight::from_ref_time(9_622_797).saturating_mul(c.into())) - // Standard Error: 5_975 - .saturating_add(Weight::from_ref_time(541_471).saturating_mul(v.into())) - .saturating_add(RocksDbWeight::get().reads(4)) - .saturating_add(RocksDbWeight::get().reads((1_u64).saturating_mul(c.into()))) - .saturating_add(RocksDbWeight::get().writes(6)) - .saturating_add(RocksDbWeight::get().writes((1_u64).saturating_mul(c.into()))) - } -} diff --git a/primitives/npos-elections/src/approval_voting.rs b/primitives/npos-elections/src/approval_voting.rs deleted file mode 100644 index 2fcf17b60..000000000 --- a/primitives/npos-elections/src/approval_voting.rs +++ /dev/null @@ -1,79 +0,0 @@ -// This file is part of Substrate. - -// Copyright (C) 2023 Parity Technologies (UK) Ltd. -// SPDX-License-Identifier: Apache-2.0 - -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -//! Implementation of the approval voting election method. -//! -//! This method allows voters to select many candidates and backing each of them with the same -//! vote weight. The candidates with the most backing are the election winners. - -use crate::{setup_inputs, ElectionResult, IdentifierT, PerThing128, VoteWeight}; -use sp_arithmetic::traits::Zero; -use sp_std::{cmp::Reverse, vec::Vec}; - -/// Execute an approvals voting election scheme. The return type is a list of winners. The weight -/// vector of all voters who contribute to the winners, which for this scheme is always 100% per -/// vote. -/// -/// - The vote assignment distribution for each vote is always 100%, since a voter backs a candidate -/// with its full stake, regardless of how many candidates are backed by the same stake. However, -/// the caller may normalize votes on site if required. -/// - Returning winners are sorted based on desirability. Voters are unsorted. -/// - The returning winners are zipped with their final backing stake. Yet, to get the exact final -/// weight distribution from the winner's point of view, one needs to build a support map. See -/// [`crate::SupportMap`] for more info. Note that this backing stake is computed in -/// ExtendedBalance and may be slightly different that what will be computed from the support map, -/// due to accuracy loss. -/// -/// This can only fail if the normalization fails. This can happen if for any of the resulting -/// assignments, `assignment.distribution.map(|p| p.deconstruct()).sum()` fails to fit inside -/// `UpperOf

`. A user of this crate may statically assert that this can never happen and safely -/// `expect` this to return `Ok`. -pub fn approval_voting( - to_elect: usize, - candidates: Vec, - voters: Vec<(AccountId, VoteWeight, impl IntoIterator)>, -) -> Result, crate::Error> { - let to_elect = to_elect.min(candidates.len()); - - let (mut candidates, mut voters) = setup_inputs(candidates, voters); - - candidates.sort_by_key(|c| Reverse(c.borrow().approval_stake)); - - let winners = candidates - .into_iter() - .take(to_elect) - .map(|w| { - w.borrow_mut().elected = true; - w - }) - .map(|w_ptr| (w_ptr.borrow().who.clone(), w_ptr.borrow().approval_stake)) - .collect(); - - for voter in &mut voters { - for edge in &mut voter.edges { - if edge.candidate.borrow().elected { - edge.weight = voter.budget - } else { - edge.weight = Zero::zero() - } - } - } - - let assignments = voters.into_iter().filter_map(|v| v.into_assignment()).collect::>(); - - Ok(ElectionResult { winners, assignments }) -} diff --git a/primitives/npos-elections/src/lib.rs b/primitives/npos-elections/src/lib.rs index a1a38b9b0..716c4b283 100644 --- a/primitives/npos-elections/src/lib.rs +++ b/primitives/npos-elections/src/lib.rs @@ -25,8 +25,6 @@ //! - [`balance`](balancing::balance): Implements the star balancing algorithm. This iterative //! process can push a solution toward being more "balanced", which in turn can increase its //! score. -//! - [`approval_voting`](approval_voting::approval_voting): Implements an approval voting electoral -//! system where voters can back multiple candidates with the same stake. //! //! ### Terminology //! @@ -92,7 +90,6 @@ mod mock; #[cfg(test)] mod tests; -pub mod approval_voting; mod assignments; pub mod balancing; pub mod helpers; @@ -103,7 +100,6 @@ pub mod pjr; pub mod reduce; pub mod traits; -pub use approval_voting::*; pub use assignments::{Assignment, StakedAssignment}; pub use balancing::*; pub use helpers::*; diff --git a/primitives/npos-elections/src/phragmen.rs b/primitives/npos-elections/src/phragmen.rs index b7204ce1c..c3578065f 100644 --- a/primitives/npos-elections/src/phragmen.rs +++ b/primitives/npos-elections/src/phragmen.rs @@ -57,7 +57,7 @@ const DEN: ExtendedBalance = ExtendedBalance::max_value(); /// - The returning weight distribution is _normalized_, meaning that it is guaranteed that the sum /// of the ratios in each voter's distribution sums up to exactly `P::one()`. /// -/// This can only fail if the normalization fails. This can happen if for any of the resulting +/// This can only fail of the normalization fails. This can happen if for any of the resulting /// assignments, `assignment.distribution.map(|p| p.deconstruct()).sum()` fails to fit inside /// `UpperOf

`. A user of this crate may statically assert that this can never happen and safely /// `expect` this to return `Ok`. diff --git a/primitives/npos-elections/src/tests.rs b/primitives/npos-elections/src/tests.rs index 5fea4c250..72ae9a022 100644 --- a/primitives/npos-elections/src/tests.rs +++ b/primitives/npos-elections/src/tests.rs @@ -18,57 +18,12 @@ //! Tests for npos-elections. use crate::{ - approval_voting::*, balancing, helpers::*, mock::*, seq_phragmen, seq_phragmen_core, - setup_inputs, to_support_map, Assignment, BalancingConfig, ElectionResult, ExtendedBalance, - StakedAssignment, Support, Voter, + balancing, helpers::*, mock::*, seq_phragmen, seq_phragmen_core, setup_inputs, to_support_map, + Assignment, BalancingConfig, ElectionResult, ExtendedBalance, StakedAssignment, Support, Voter, }; use sp_arithmetic::{PerU16, Perbill, Percent, Permill}; use substrate_test_utils::assert_eq_uvec; -#[test] -fn approval_voting_works() { - let candidates = vec![1, 2, 3, 4]; - let voters = vec![(10, vec![1, 2]), (20, vec![1, 2]), (30, vec![1, 2, 3]), (40, vec![4])]; - let stake_of = create_stake_of(&[(10, 10), (20, 20), (30, 30), (40, 40)]); - - let voters = voters - .iter() - .map(|(ref v, ref vs)| (*v, stake_of(v), vs.clone())) - .collect::>(); - - let ElectionResult::<_, Perbill> { winners, assignments } = - approval_voting(3, candidates, voters).unwrap(); - - assert_eq_uvec!(winners, vec![(1, 60), (2, 60), (4, 40)]); - assert_eq_uvec!( - assignments, - vec![ - Assignment { - who: 10u64, - distribution: vec![ - (1, Perbill::from_percent(100)), - (2, Perbill::from_percent(100)) - ] - }, - Assignment { - who: 20u64, - distribution: vec![ - (1, Perbill::from_percent(100)), - (2, Perbill::from_percent(100)) - ] - }, - Assignment { - who: 30u64, - distribution: vec![ - (1, Perbill::from_percent(100)), - (2, Perbill::from_percent(100)) - ] - }, - Assignment { who: 40u64, distribution: vec![(4, Perbill::from_percent(100))] }, - ] - ); -} - #[test] fn float_phragmen_poc_works() { let candidates = vec![1, 2, 3]; diff --git a/scripts/run_all_benchmarks.sh b/scripts/run_all_benchmarks.sh index 818a5df7f..b632cb5c1 100755 --- a/scripts/run_all_benchmarks.sh +++ b/scripts/run_all_benchmarks.sh @@ -67,6 +67,8 @@ SUBSTRATE=./target/production/substrate # Manually exclude some pallets. EXCLUDED_PALLETS=( + # Helper pallets + "pallet_election_provider_support_benchmarking" # Pallets without automatic benchmarking "pallet_babe" "pallet_grandpa" diff --git a/utils/frame/remote-externalities/Cargo.toml b/utils/frame/remote-externalities/Cargo.toml index e55b64efb..8611ae498 100644 --- a/utils/frame/remote-externalities/Cargo.toml +++ b/utils/frame/remote-externalities/Cargo.toml @@ -25,7 +25,7 @@ futures = "0.3" [dev-dependencies] frame-support = { version = "4.0.0-dev", path = "../../../frame/support" } -pallet-elections = { version = "5.0.0-dev", path = "../../../frame/elections" } +pallet-elections-phragmen = { version = "5.0.0-dev", path = "../../../frame/elections-phragmen" } tracing-subscriber = { version = "0.3.16", features = ["env-filter"] } [features] From 1837f423b494254e1d27834b1c9da34b2c0c2375 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bastian=20K=C3=B6cher?= Date: Fri, 24 Feb 2023 12:43:01 +0100 Subject: [PATCH 160/558] wasm-executor: Support growing the memory (#12520) * As always, start with something :P * Add support for max_heap_pages * Add support for wasmtime * Make it compile * Fix compilation * Copy wrongly merged code * Fix compilation * Some fixes * Fix * Get stuff working * More work * More fixes * ... * More * FIXEs * Switch wasmi to use `RuntimeBlob` like wasmtime * Removed unused stuff * Cleanup * More cleanups * Introduce `CallContext` * Fixes * More fixes * Add builder for creating the `WasmExecutor` * Adds some docs * FMT * First round of feedback. * Review feedback round 2 * More fixes * Fix try-runtime * Update client/executor/wasmtime/src/instance_wrapper.rs Co-authored-by: Koute * Update client/executor/common/src/wasm_runtime.rs Co-authored-by: Koute * Update client/executor/common/src/runtime_blob/runtime_blob.rs Co-authored-by: Koute * Update client/executor/common/src/wasm_runtime.rs Co-authored-by: Koute * Update client/allocator/src/freeing_bump.rs Co-authored-by: Koute * Update client/allocator/src/freeing_bump.rs Co-authored-by: Koute * Feedback round 3 * FMT * Review comments --------- Co-authored-by: Koute --- Cargo.lock | 14 + bin/node/executor/benches/bench.rs | 30 +- bin/node/executor/tests/common.rs | 4 +- client/allocator/src/error.rs | 2 +- client/allocator/src/freeing_bump.rs | 403 +++++++++++------- client/allocator/src/lib.rs | 32 ++ client/api/src/call_executor.rs | 2 + client/executor/Cargo.toml | 3 +- client/executor/benches/bench.rs | 9 +- client/executor/common/Cargo.toml | 2 +- .../common/src/runtime_blob/runtime_blob.rs | 26 +- client/executor/common/src/util.rs | 6 +- client/executor/common/src/wasm_runtime.rs | 26 ++ client/executor/runtime-test/src/lib.rs | 9 + .../executor/src/integration_tests/linux.rs | 49 ++- client/executor/src/integration_tests/mod.rs | 39 +- client/executor/src/native_executor.rs | 250 +++++++++-- client/executor/src/wasm_runtime.rs | 30 +- client/executor/wasmi/Cargo.toml | 2 +- client/executor/wasmi/src/lib.rs | 205 +++++---- client/executor/wasmtime/Cargo.toml | 2 +- client/executor/wasmtime/src/host.rs | 51 ++- .../executor/wasmtime/src/instance_wrapper.rs | 106 +++-- client/executor/wasmtime/src/runtime.rs | 84 +--- client/executor/wasmtime/src/tests.rs | 260 +++++------ client/finality-grandpa/src/lib.rs | 3 +- .../rpc-spec-v2/src/chain_head/chain_head.rs | 3 +- client/rpc/src/state/state_full.rs | 2 + client/service/src/client/call_executor.rs | 16 +- client/service/test/src/client/mod.rs | 8 +- primitives/core/src/traits.rs | 19 +- primitives/state-machine/src/lib.rs | 14 +- primitives/wasm-interface/Cargo.toml | 2 +- test-utils/runtime/src/lib.rs | 9 +- test-utils/runtime/src/system.rs | 20 +- .../benchmarking-cli/src/pallet/command.rs | 13 +- utils/frame/try-runtime/cli/src/lib.rs | 4 +- 37 files changed, 1092 insertions(+), 667 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 7cfde0570..299f63921 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -7653,6 +7653,18 @@ version = "0.6.28" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "456c603be3e8d448b072f410900c09faf164fbce2d480456f50eea6e25f9c848" +[[package]] +name = "region" +version = "3.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "76e189c2369884dce920945e2ddf79b3dff49e071a167dd1817fa9c4c00d512e" +dependencies = [ + "bitflags", + "libc", + "mach", + "winapi", +] + [[package]] name = "remove_dir_all" version = "0.5.3" @@ -8404,6 +8416,7 @@ dependencies = [ "sp-runtime", "sp-runtime-interface", "sp-state-machine", + "sp-tracing", "sp-trie", "sp-version", "sp-wasm-interface", @@ -11956,6 +11969,7 @@ dependencies = [ "memory_units", "num-rational", "num-traits", + "region", ] [[package]] diff --git a/bin/node/executor/benches/bench.rs b/bin/node/executor/benches/bench.rs index 4154d1cff..19e7b158a 100644 --- a/bin/node/executor/benches/bench.rs +++ b/bin/node/executor/benches/bench.rs @@ -31,7 +31,7 @@ use sc_executor::{ }; use sp_core::{ storage::well_known_keys, - traits::{CodeExecutor, RuntimeCode}, + traits::{CallContext, CodeExecutor, RuntimeCode}, }; use sp_runtime::traits::BlakeTwo256; use sp_state_machine::TestExternalities as CoreTestExternalities; @@ -112,20 +112,41 @@ fn construct_block( // execute the block to get the real header. executor - .call(ext, &runtime_code, "Core_initialize_block", &header.encode(), true) + .call( + ext, + &runtime_code, + "Core_initialize_block", + &header.encode(), + true, + CallContext::Offchain, + ) .0 .unwrap(); for i in extrinsics.iter() { executor - .call(ext, &runtime_code, "BlockBuilder_apply_extrinsic", &i.encode(), true) + .call( + ext, + &runtime_code, + "BlockBuilder_apply_extrinsic", + &i.encode(), + true, + CallContext::Offchain, + ) .0 .unwrap(); } let header = Header::decode( &mut &executor - .call(ext, &runtime_code, "BlockBuilder_finalize_block", &[0u8; 0], true) + .call( + ext, + &runtime_code, + "BlockBuilder_finalize_block", + &[0u8; 0], + true, + CallContext::Offchain, + ) .0 .unwrap()[..], ) @@ -201,6 +222,7 @@ fn bench_execute_block(c: &mut Criterion) { "Core_execute_block", &block.0, use_native, + CallContext::Offchain, ) .0 .unwrap(); diff --git a/bin/node/executor/tests/common.rs b/bin/node/executor/tests/common.rs index b428e32ea..036528f8d 100644 --- a/bin/node/executor/tests/common.rs +++ b/bin/node/executor/tests/common.rs @@ -26,7 +26,7 @@ use sp_consensus_babe::{ use sp_core::{ crypto::KeyTypeId, sr25519::Signature, - traits::{CodeExecutor, RuntimeCode}, + traits::{CallContext, CodeExecutor, RuntimeCode}, }; use sp_runtime::{ traits::{BlakeTwo256, Header as HeaderT}, @@ -114,7 +114,7 @@ pub fn executor_call( heap_pages: heap_pages.and_then(|hp| Decode::decode(&mut &hp[..]).ok()), }; sp_tracing::try_init_simple(); - executor().call(&mut t, &runtime_code, method, data, use_native) + executor().call(&mut t, &runtime_code, method, data, use_native, CallContext::Onchain) } pub fn new_test_ext(code: &[u8]) -> TestExternalities { diff --git a/client/allocator/src/error.rs b/client/allocator/src/error.rs index a3d91a3e2..08d84317b 100644 --- a/client/allocator/src/error.rs +++ b/client/allocator/src/error.rs @@ -16,7 +16,7 @@ // limitations under the License. /// The error type used by the allocators. -#[derive(thiserror::Error, Debug)] +#[derive(thiserror::Error, Debug, PartialEq)] pub enum Error { /// Someone tried to allocate more memory than the allowed maximum per allocation. #[error("Requested allocation size is too large")] diff --git a/client/allocator/src/freeing_bump.rs b/client/allocator/src/freeing_bump.rs index fdd63bd66..c3cb827af 100644 --- a/client/allocator/src/freeing_bump.rs +++ b/client/allocator/src/freeing_bump.rs @@ -67,10 +67,11 @@ //! wasted. This is more pronounced (in terms of absolute heap amounts) with larger allocation //! sizes. -use crate::Error; +use crate::{Error, Memory, MAX_WASM_PAGES, PAGE_SIZE}; pub use sp_core::MAX_POSSIBLE_ALLOCATION; use sp_wasm_interface::{Pointer, WordSize}; use std::{ + cmp::{max, min}, mem, ops::{Index, IndexMut, Range}, }; @@ -237,7 +238,7 @@ impl Header { /// /// Returns an error if the `header_ptr` is out of bounds of the linear memory or if the read /// header is corrupted (e.g. the order is incorrect). - fn read_from(memory: &M, header_ptr: u32) -> Result { + fn read_from(memory: &impl Memory, header_ptr: u32) -> Result { let raw_header = memory.read_le_u64(header_ptr)?; // Check if the header represents an occupied or free allocation and extract the header data @@ -255,7 +256,7 @@ impl Header { /// Write out this header to memory. /// /// Returns an error if the `header_ptr` is out of bounds of the linear memory. - fn write_into(&self, memory: &mut M, header_ptr: u32) -> Result<(), Error> { + fn write_into(&self, memory: &mut impl Memory, header_ptr: u32) -> Result<(), Error> { let (header_data, occupied_mask) = match *self { Self::Occupied(order) => (order.into_raw(), 0x00000001_00000000), Self::Free(link) => (link.into_raw(), 0x00000000_00000000), @@ -343,6 +344,15 @@ pub struct AllocationStats { pub address_space_used: u32, } +/// Convert the given `size` in bytes into the number of pages. +/// +/// The returned number of pages is ensured to be big enough to hold memory with the given `size`. +/// +/// Returns `None` if the number of pages to not fit into `u32`. +fn pages_from_size(size: u64) -> Option { + u32::try_from((size + PAGE_SIZE as u64 - 1) / PAGE_SIZE as u64).ok() +} + /// An implementation of freeing bump allocator. /// /// Refer to the module-level documentation for further details. @@ -351,7 +361,7 @@ pub struct FreeingBumpHeapAllocator { bumper: u32, free_lists: FreeLists, poisoned: bool, - last_observed_memory_size: u32, + last_observed_memory_size: u64, stats: AllocationStats, } @@ -395,9 +405,9 @@ impl FreeingBumpHeapAllocator { /// /// - `mem` - a slice representing the linear memory on which this allocator operates. /// - `size` - size in bytes of the allocation request - pub fn allocate( + pub fn allocate( &mut self, - mem: &mut M, + mem: &mut impl Memory, size: WordSize, ) -> Result, Error> { if self.poisoned { @@ -412,7 +422,7 @@ impl FreeingBumpHeapAllocator { let header_ptr: u32 = match self.free_lists[order] { Link::Ptr(header_ptr) => { assert!( - header_ptr + order.size() + HEADER_SIZE <= mem.size(), + u64::from(header_ptr + order.size() + HEADER_SIZE) <= mem.size(), "Pointer is looked up in list of free entries, into which only valid values are inserted; qed" ); @@ -427,7 +437,7 @@ impl FreeingBumpHeapAllocator { }, Link::Nil => { // Corresponding free list is empty. Allocate a new item. - Self::bump(&mut self.bumper, order.size() + HEADER_SIZE, mem.size())? + Self::bump(&mut self.bumper, order.size() + HEADER_SIZE, mem)? }, }; @@ -437,7 +447,7 @@ impl FreeingBumpHeapAllocator { self.stats.bytes_allocated += order.size() + HEADER_SIZE; self.stats.bytes_allocated_sum += u128::from(order.size() + HEADER_SIZE); self.stats.bytes_allocated_peak = - std::cmp::max(self.stats.bytes_allocated_peak, self.stats.bytes_allocated); + max(self.stats.bytes_allocated_peak, self.stats.bytes_allocated); self.stats.address_space_used = self.bumper - self.original_heap_base; log::trace!(target: LOG_TARGET, "after allocation: {:?}", self.stats); @@ -457,11 +467,7 @@ impl FreeingBumpHeapAllocator { /// /// - `mem` - a slice representing the linear memory on which this allocator operates. /// - `ptr` - pointer to the allocated chunk - pub fn deallocate( - &mut self, - mem: &mut M, - ptr: Pointer, - ) -> Result<(), Error> { + pub fn deallocate(&mut self, mem: &mut impl Memory, ptr: Pointer) -> Result<(), Error> { if self.poisoned { return Err(error("the allocator has been poisoned")) } @@ -503,15 +509,52 @@ impl FreeingBumpHeapAllocator { /// /// Returns the `bumper` from before the increase. Returns an `Error::AllocatorOutOfSpace` if /// the operation would exhaust the heap. - fn bump(bumper: &mut u32, size: u32, heap_end: u32) -> Result { - if *bumper + size > heap_end { - log::error!( - target: LOG_TARGET, - "running out of space with current bumper {}, mem size {}", - bumper, - heap_end + fn bump(bumper: &mut u32, size: u32, memory: &mut impl Memory) -> Result { + let required_size = u64::from(*bumper) + u64::from(size); + + if required_size > memory.size() { + let required_pages = + pages_from_size(required_size).ok_or_else(|| Error::AllocatorOutOfSpace)?; + + let current_pages = memory.pages(); + let max_pages = memory.max_pages().unwrap_or(MAX_WASM_PAGES); + debug_assert!( + current_pages < required_pages, + "current pages {current_pages} < required pages {required_pages}" ); - return Err(Error::AllocatorOutOfSpace) + + if current_pages >= max_pages { + log::debug!( + target: LOG_TARGET, + "Wasm pages ({current_pages}) are already at the maximum.", + ); + + return Err(Error::AllocatorOutOfSpace) + } else if required_pages > max_pages { + log::debug!( + target: LOG_TARGET, + "Failed to grow memory from {current_pages} pages to at least {required_pages}\ + pages due to the maximum limit of {max_pages} pages", + ); + return Err(Error::AllocatorOutOfSpace) + } + + // Ideally we want to double our current number of pages, + // as long as it's less than the absolute maximum we can have. + let next_pages = min(current_pages * 2, max_pages); + // ...but if even more pages are required then try to allocate that many. + let next_pages = max(next_pages, required_pages); + + if memory.grow(next_pages - current_pages).is_err() { + log::error!( + target: LOG_TARGET, + "Failed to grow memory from {current_pages} pages to {next_pages} pages", + ); + + return Err(Error::AllocatorOutOfSpace) + } + + debug_assert_eq!(memory.pages(), next_pages, "Number of pages should have increased!"); } let res = *bumper; @@ -519,9 +562,9 @@ impl FreeingBumpHeapAllocator { Ok(res) } - fn observe_memory_size( - last_observed_memory_size: &mut u32, - mem: &mut M, + fn observe_memory_size( + last_observed_memory_size: &mut u64, + mem: &mut impl Memory, ) -> Result<(), Error> { if mem.size() < *last_observed_memory_size { return Err(Error::MemoryShrinked) @@ -538,38 +581,42 @@ impl FreeingBumpHeapAllocator { /// accessible up to the reported size. /// /// The linear memory can grow in size with the wasm page granularity (64KiB), but it cannot shrink. -pub trait Memory { +trait MemoryExt: Memory { /// Read a u64 from the heap in LE form. Returns an error if any of the bytes read are out of /// bounds. - fn read_le_u64(&self, ptr: u32) -> Result; - /// Write a u64 to the heap in LE form. Returns an error if any of the bytes written are out of - /// bounds. - fn write_le_u64(&mut self, ptr: u32, val: u64) -> Result<(), Error>; - /// Returns the full size of the memory in bytes. - fn size(&self) -> u32; -} - -impl Memory for [u8] { fn read_le_u64(&self, ptr: u32) -> Result { - let range = - heap_range(ptr, 8, self.len()).ok_or_else(|| error("read out of heap bounds"))?; - let bytes = self[range] - .try_into() - .expect("[u8] slice of length 8 must be convertible to [u8; 8]"); - Ok(u64::from_le_bytes(bytes)) + self.with_access(|memory| { + let range = + heap_range(ptr, 8, memory.len()).ok_or_else(|| error("read out of heap bounds"))?; + let bytes = memory[range] + .try_into() + .expect("[u8] slice of length 8 must be convertible to [u8; 8]"); + Ok(u64::from_le_bytes(bytes)) + }) } + + /// Write a u64 to the heap in LE form. Returns an error if any of the bytes written are out of + /// bounds. fn write_le_u64(&mut self, ptr: u32, val: u64) -> Result<(), Error> { - let range = - heap_range(ptr, 8, self.len()).ok_or_else(|| error("write out of heap bounds"))?; - let bytes = val.to_le_bytes(); - self[range].copy_from_slice(&bytes[..]); - Ok(()) + self.with_access_mut(|memory| { + let range = heap_range(ptr, 8, memory.len()) + .ok_or_else(|| error("write out of heap bounds"))?; + let bytes = val.to_le_bytes(); + memory[range].copy_from_slice(&bytes[..]); + Ok(()) + }) } - fn size(&self) -> u32 { - u32::try_from(self.len()).expect("size of Wasm linear memory is <2^32; qed") + + /// Returns the full size of the memory in bytes. + fn size(&self) -> u64 { + debug_assert!(self.pages() <= MAX_WASM_PAGES); + + self.pages() as u64 * PAGE_SIZE as u64 } } +impl MemoryExt for T {} + fn heap_range(offset: u32, length: u32, heap_len: usize) -> Option> { let start = offset as usize; let end = offset.checked_add(length)? as usize; @@ -601,21 +648,72 @@ impl<'a> Drop for PoisonBomb<'a> { mod tests { use super::*; - const PAGE_SIZE: u32 = 65536; - /// Makes a pointer out of the given address. fn to_pointer(address: u32) -> Pointer { Pointer::new(address) } + #[derive(Debug)] + struct MemoryInstance { + data: Vec, + max_wasm_pages: u32, + } + + impl MemoryInstance { + fn with_pages(pages: u32) -> Self { + Self { data: vec![0; (pages * PAGE_SIZE) as usize], max_wasm_pages: MAX_WASM_PAGES } + } + + fn set_max_wasm_pages(&mut self, max_pages: u32) { + self.max_wasm_pages = max_pages; + } + } + + impl Memory for MemoryInstance { + fn with_access(&self, run: impl FnOnce(&[u8]) -> R) -> R { + run(&self.data) + } + + fn with_access_mut(&mut self, run: impl FnOnce(&mut [u8]) -> R) -> R { + run(&mut self.data) + } + + fn pages(&self) -> u32 { + pages_from_size(self.data.len() as u64).unwrap() + } + + fn max_pages(&self) -> Option { + Some(self.max_wasm_pages) + } + + fn grow(&mut self, pages: u32) -> Result<(), ()> { + if self.pages() + pages > self.max_wasm_pages { + Err(()) + } else { + self.data.resize(((self.pages() + pages) * PAGE_SIZE) as usize, 0); + Ok(()) + } + } + } + + #[test] + fn test_pages_from_size() { + assert_eq!(pages_from_size(0).unwrap(), 0); + assert_eq!(pages_from_size(1).unwrap(), 1); + assert_eq!(pages_from_size(65536).unwrap(), 1); + assert_eq!(pages_from_size(65536 + 1).unwrap(), 2); + assert_eq!(pages_from_size(2 * 65536).unwrap(), 2); + assert_eq!(pages_from_size(2 * 65536 + 1).unwrap(), 3); + } + #[test] fn should_allocate_properly() { // given - let mut mem = [0u8; PAGE_SIZE as usize]; + let mut mem = MemoryInstance::with_pages(1); let mut heap = FreeingBumpHeapAllocator::new(0); // when - let ptr = heap.allocate(&mut mem[..], 1).unwrap(); + let ptr = heap.allocate(&mut mem, 1).unwrap(); // then // returned pointer must start right after `HEADER_SIZE` @@ -625,11 +723,11 @@ mod tests { #[test] fn should_always_align_pointers_to_multiples_of_8() { // given - let mut mem = [0u8; PAGE_SIZE as usize]; + let mut mem = MemoryInstance::with_pages(1); let mut heap = FreeingBumpHeapAllocator::new(13); // when - let ptr = heap.allocate(&mut mem[..], 1).unwrap(); + let ptr = heap.allocate(&mut mem, 1).unwrap(); // then // the pointer must start at the next multiple of 8 from 13 @@ -640,13 +738,13 @@ mod tests { #[test] fn should_increment_pointers_properly() { // given - let mut mem = [0u8; PAGE_SIZE as usize]; + let mut mem = MemoryInstance::with_pages(1); let mut heap = FreeingBumpHeapAllocator::new(0); // when - let ptr1 = heap.allocate(&mut mem[..], 1).unwrap(); - let ptr2 = heap.allocate(&mut mem[..], 9).unwrap(); - let ptr3 = heap.allocate(&mut mem[..], 1).unwrap(); + let ptr1 = heap.allocate(&mut mem, 1).unwrap(); + let ptr2 = heap.allocate(&mut mem, 9).unwrap(); + let ptr3 = heap.allocate(&mut mem, 1).unwrap(); // then // a prefix of 8 bytes is prepended to each pointer @@ -663,18 +761,18 @@ mod tests { #[test] fn should_free_properly() { // given - let mut mem = [0u8; PAGE_SIZE as usize]; + let mut mem = MemoryInstance::with_pages(1); let mut heap = FreeingBumpHeapAllocator::new(0); - let ptr1 = heap.allocate(&mut mem[..], 1).unwrap(); + let ptr1 = heap.allocate(&mut mem, 1).unwrap(); // the prefix of 8 bytes is prepended to the pointer assert_eq!(ptr1, to_pointer(HEADER_SIZE)); - let ptr2 = heap.allocate(&mut mem[..], 1).unwrap(); + let ptr2 = heap.allocate(&mut mem, 1).unwrap(); // the prefix of 8 bytes + the content of ptr 1 is prepended to the pointer assert_eq!(ptr2, to_pointer(24)); // when - heap.deallocate(&mut mem[..], ptr2).unwrap(); + heap.deallocate(&mut mem, ptr2).unwrap(); // then // then the heads table should contain a pointer to the @@ -685,23 +783,23 @@ mod tests { #[test] fn should_deallocate_and_reallocate_properly() { // given - let mut mem = [0u8; PAGE_SIZE as usize]; + let mut mem = MemoryInstance::with_pages(1); let padded_offset = 16; let mut heap = FreeingBumpHeapAllocator::new(13); - let ptr1 = heap.allocate(&mut mem[..], 1).unwrap(); + let ptr1 = heap.allocate(&mut mem, 1).unwrap(); // the prefix of 8 bytes is prepended to the pointer assert_eq!(ptr1, to_pointer(padded_offset + HEADER_SIZE)); - let ptr2 = heap.allocate(&mut mem[..], 9).unwrap(); + let ptr2 = heap.allocate(&mut mem, 9).unwrap(); // the padded_offset + the previously allocated ptr (8 bytes prefix + // 8 bytes content) + the prefix of 8 bytes which is prepended to the // current pointer assert_eq!(ptr2, to_pointer(padded_offset + 16 + HEADER_SIZE)); // when - heap.deallocate(&mut mem[..], ptr2).unwrap(); - let ptr3 = heap.allocate(&mut mem[..], 9).unwrap(); + heap.deallocate(&mut mem, ptr2).unwrap(); + let ptr3 = heap.allocate(&mut mem, 9).unwrap(); // then // should have re-allocated @@ -712,22 +810,22 @@ mod tests { #[test] fn should_build_linked_list_of_free_areas_properly() { // given - let mut mem = [0u8; PAGE_SIZE as usize]; + let mut mem = MemoryInstance::with_pages(1); let mut heap = FreeingBumpHeapAllocator::new(0); - let ptr1 = heap.allocate(&mut mem[..], 8).unwrap(); - let ptr2 = heap.allocate(&mut mem[..], 8).unwrap(); - let ptr3 = heap.allocate(&mut mem[..], 8).unwrap(); + let ptr1 = heap.allocate(&mut mem, 8).unwrap(); + let ptr2 = heap.allocate(&mut mem, 8).unwrap(); + let ptr3 = heap.allocate(&mut mem, 8).unwrap(); // when - heap.deallocate(&mut mem[..], ptr1).unwrap(); - heap.deallocate(&mut mem[..], ptr2).unwrap(); - heap.deallocate(&mut mem[..], ptr3).unwrap(); + heap.deallocate(&mut mem, ptr1).unwrap(); + heap.deallocate(&mut mem, ptr2).unwrap(); + heap.deallocate(&mut mem, ptr3).unwrap(); // then assert_eq!(heap.free_lists.heads[0], Link::Ptr(u32::from(ptr3) - HEADER_SIZE)); - let ptr4 = heap.allocate(&mut mem[..], 8).unwrap(); + let ptr4 = heap.allocate(&mut mem, 8).unwrap(); assert_eq!(ptr4, ptr3); assert_eq!(heap.free_lists.heads[0], Link::Ptr(u32::from(ptr2) - HEADER_SIZE)); @@ -736,29 +834,28 @@ mod tests { #[test] fn should_not_allocate_if_too_large() { // given - let mut mem = [0u8; PAGE_SIZE as usize]; + let mut mem = MemoryInstance::with_pages(1); + mem.set_max_wasm_pages(1); let mut heap = FreeingBumpHeapAllocator::new(13); // when - let ptr = heap.allocate(&mut mem[..], PAGE_SIZE - 13); + let ptr = heap.allocate(&mut mem, PAGE_SIZE - 13); // then - match ptr.unwrap_err() { - Error::AllocatorOutOfSpace => {}, - e => panic!("Expected allocator out of space error, got: {:?}", e), - } + assert_eq!(Error::AllocatorOutOfSpace, ptr.unwrap_err()); } #[test] fn should_not_allocate_if_full() { // given - let mut mem = [0u8; PAGE_SIZE as usize]; + let mut mem = MemoryInstance::with_pages(1); + mem.set_max_wasm_pages(1); let mut heap = FreeingBumpHeapAllocator::new(0); - let ptr1 = heap.allocate(&mut mem[..], (PAGE_SIZE / 2) - HEADER_SIZE).unwrap(); + let ptr1 = heap.allocate(&mut mem, (PAGE_SIZE / 2) - HEADER_SIZE).unwrap(); assert_eq!(ptr1, to_pointer(HEADER_SIZE)); // when - let ptr2 = heap.allocate(&mut mem[..], PAGE_SIZE / 2); + let ptr2 = heap.allocate(&mut mem, PAGE_SIZE / 2); // then // there is no room for another half page incl. its 8 byte prefix @@ -771,11 +868,11 @@ mod tests { #[test] fn should_allocate_max_possible_allocation_size() { // given - let mut mem = vec![0u8; (MAX_POSSIBLE_ALLOCATION + PAGE_SIZE) as usize]; + let mut mem = MemoryInstance::with_pages(1); let mut heap = FreeingBumpHeapAllocator::new(0); // when - let ptr = heap.allocate(&mut mem[..], MAX_POSSIBLE_ALLOCATION).unwrap(); + let ptr = heap.allocate(&mut mem, MAX_POSSIBLE_ALLOCATION).unwrap(); // then assert_eq!(ptr, to_pointer(HEADER_SIZE)); @@ -784,60 +881,62 @@ mod tests { #[test] fn should_not_allocate_if_requested_size_too_large() { // given - let mut mem = [0u8; PAGE_SIZE as usize]; + let mut mem = MemoryInstance::with_pages(1); let mut heap = FreeingBumpHeapAllocator::new(0); // when - let ptr = heap.allocate(&mut mem[..], MAX_POSSIBLE_ALLOCATION + 1); + let ptr = heap.allocate(&mut mem, MAX_POSSIBLE_ALLOCATION + 1); // then - match ptr.unwrap_err() { - Error::RequestedAllocationTooLarge => {}, - e => panic!("Expected allocation size too large error, got: {:?}", e), - } + assert_eq!(Error::RequestedAllocationTooLarge, ptr.unwrap_err()); } #[test] fn should_return_error_when_bumper_greater_than_heap_size() { // given - let mut mem = [0u8; 64]; + let mut mem = MemoryInstance::with_pages(1); + mem.set_max_wasm_pages(1); let mut heap = FreeingBumpHeapAllocator::new(0); - let ptr1 = heap.allocate(&mut mem[..], 32).unwrap(); - assert_eq!(ptr1, to_pointer(HEADER_SIZE)); - heap.deallocate(&mut mem[..], ptr1).expect("failed freeing ptr1"); - assert_eq!(heap.stats.bytes_allocated, 0); - assert_eq!(heap.bumper, 40); + let mut ptrs = Vec::new(); + for _ in 0..(PAGE_SIZE as usize / 40) { + ptrs.push(heap.allocate(&mut mem, 32).expect("Allocate 32 byte")); + } + + assert_eq!(heap.stats.bytes_allocated, PAGE_SIZE - 16); + assert_eq!(heap.bumper, PAGE_SIZE - 16); + + ptrs.into_iter() + .for_each(|ptr| heap.deallocate(&mut mem, ptr).expect("Deallocate 32 byte")); - let ptr2 = heap.allocate(&mut mem[..], 16).unwrap(); - assert_eq!(ptr2, to_pointer(48)); - heap.deallocate(&mut mem[..], ptr2).expect("failed freeing ptr2"); assert_eq!(heap.stats.bytes_allocated, 0); - assert_eq!(heap.bumper, 64); + assert_eq!(heap.stats.bytes_allocated_peak, PAGE_SIZE - 16); + assert_eq!(heap.bumper, PAGE_SIZE - 16); + + // Allocate another 8 byte to use the full heap. + heap.allocate(&mut mem, 8).expect("Allocate 8 byte"); // when // the `bumper` value is equal to `size` here and any // further allocation which would increment the bumper must fail. // we try to allocate 8 bytes here, which will increment the - // bumper since no 8 byte item has been allocated+freed before. - let ptr = heap.allocate(&mut mem[..], 8); + // bumper since no 8 byte item has been freed before. + assert_eq!(heap.bumper as u64, mem.size()); + let ptr = heap.allocate(&mut mem, 8); // then - match ptr.unwrap_err() { - Error::AllocatorOutOfSpace => {}, - e => panic!("Expected allocator out of space error, got: {:?}", e), - } + assert_eq!(Error::AllocatorOutOfSpace, ptr.unwrap_err()); } #[test] fn should_include_prefixes_in_total_heap_size() { // given - let mut mem = [0u8; PAGE_SIZE as usize]; + let mut mem = MemoryInstance::with_pages(1); let mut heap = FreeingBumpHeapAllocator::new(1); // when // an item size of 16 must be used then - heap.allocate(&mut mem[..], 9).unwrap(); + heap.allocate(&mut mem, 9).unwrap(); // then assert_eq!(heap.stats.bytes_allocated, HEADER_SIZE + 16); @@ -846,13 +945,13 @@ mod tests { #[test] fn should_calculate_total_heap_size_to_zero() { // given - let mut mem = [0u8; PAGE_SIZE as usize]; + let mut mem = MemoryInstance::with_pages(1); let mut heap = FreeingBumpHeapAllocator::new(13); // when - let ptr = heap.allocate(&mut mem[..], 42).unwrap(); + let ptr = heap.allocate(&mut mem, 42).unwrap(); assert_eq!(ptr, to_pointer(16 + HEADER_SIZE)); - heap.deallocate(&mut mem[..], ptr).unwrap(); + heap.deallocate(&mut mem, ptr).unwrap(); // then assert_eq!(heap.stats.bytes_allocated, 0); @@ -861,13 +960,13 @@ mod tests { #[test] fn should_calculate_total_size_of_zero() { // given - let mut mem = [0u8; PAGE_SIZE as usize]; + let mut mem = MemoryInstance::with_pages(1); let mut heap = FreeingBumpHeapAllocator::new(19); // when for _ in 1..10 { - let ptr = heap.allocate(&mut mem[..], 42).unwrap(); - heap.deallocate(&mut mem[..], ptr).unwrap(); + let ptr = heap.allocate(&mut mem, 42).unwrap(); + heap.deallocate(&mut mem, ptr).unwrap(); } // then @@ -877,13 +976,13 @@ mod tests { #[test] fn should_read_and_write_u64_correctly() { // given - let mut mem = [0u8; PAGE_SIZE as usize]; + let mut mem = MemoryInstance::with_pages(1); // when - Memory::write_le_u64(mem.as_mut(), 40, 4480113).unwrap(); + mem.write_le_u64(40, 4480113).unwrap(); // then - let value = Memory::read_le_u64(mem.as_mut(), 40).unwrap(); + let value = MemoryExt::read_le_u64(&mut mem, 40).unwrap(); assert_eq!(value, 4480113); } @@ -913,24 +1012,25 @@ mod tests { #[test] fn deallocate_needs_to_maintain_linked_list() { - let mut mem = [0u8; 8 * 2 * 4 + ALIGNMENT as usize]; + let mut mem = MemoryInstance::with_pages(1); let mut heap = FreeingBumpHeapAllocator::new(0); // Allocate and free some pointers - let ptrs = (0..4).map(|_| heap.allocate(&mut mem[..], 8).unwrap()).collect::>(); - ptrs.into_iter().for_each(|ptr| heap.deallocate(&mut mem[..], ptr).unwrap()); + let ptrs = (0..4).map(|_| heap.allocate(&mut mem, 8).unwrap()).collect::>(); + ptrs.iter().rev().for_each(|ptr| heap.deallocate(&mut mem, *ptr).unwrap()); - // Second time we should be able to allocate all of them again. - let _ = (0..4).map(|_| heap.allocate(&mut mem[..], 8).unwrap()).collect::>(); + // Second time we should be able to allocate all of them again and get the same pointers! + let new_ptrs = (0..4).map(|_| heap.allocate(&mut mem, 8).unwrap()).collect::>(); + assert_eq!(ptrs, new_ptrs); } #[test] fn header_read_write() { let roundtrip = |header: Header| { - let mut memory = [0u8; 32]; - header.write_into(memory.as_mut(), 0).unwrap(); + let mut memory = MemoryInstance::with_pages(1); + header.write_into(&mut memory, 0).unwrap(); - let read_header = Header::read_from(memory.as_mut(), 0).unwrap(); + let read_header = Header::read_from(&memory, 0).unwrap(); assert_eq!(header, read_header); }; @@ -944,18 +1044,18 @@ mod tests { #[test] fn poison_oom() { // given - // a heap of 32 bytes. Should be enough for two allocations. - let mut mem = [0u8; 32]; + let mut mem = MemoryInstance::with_pages(1); + mem.set_max_wasm_pages(1); + let mut heap = FreeingBumpHeapAllocator::new(0); // when - assert!(heap.allocate(mem.as_mut(), 8).is_ok()); - let alloc_ptr = heap.allocate(mem.as_mut(), 8).unwrap(); - assert!(heap.allocate(mem.as_mut(), 8).is_err()); + let alloc_ptr = heap.allocate(&mut mem, PAGE_SIZE / 2).unwrap(); + assert_eq!(Error::AllocatorOutOfSpace, heap.allocate(&mut mem, PAGE_SIZE).unwrap_err()); // then assert!(heap.poisoned); - assert!(heap.deallocate(mem.as_mut(), alloc_ptr).is_err()); + assert!(heap.deallocate(&mut mem, alloc_ptr).is_err()); } #[test] @@ -969,36 +1069,41 @@ mod tests { #[test] fn accepts_growing_memory() { - const ITEM_SIZE: u32 = 16; - const ITEM_ON_HEAP_SIZE: usize = 16 + HEADER_SIZE as usize; - - let mut mem = vec![0u8; ITEM_ON_HEAP_SIZE * 2]; + let mut mem = MemoryInstance::with_pages(1); let mut heap = FreeingBumpHeapAllocator::new(0); - let _ = heap.allocate(&mut mem[..], ITEM_SIZE).unwrap(); - let _ = heap.allocate(&mut mem[..], ITEM_SIZE).unwrap(); + heap.allocate(&mut mem, PAGE_SIZE / 2).unwrap(); + heap.allocate(&mut mem, PAGE_SIZE / 2).unwrap(); - mem.extend_from_slice(&[0u8; ITEM_ON_HEAP_SIZE]); + mem.grow(1).unwrap(); - let _ = heap.allocate(&mut mem[..], ITEM_SIZE).unwrap(); + heap.allocate(&mut mem, PAGE_SIZE / 2).unwrap(); } #[test] fn doesnt_accept_shrinking_memory() { - const ITEM_SIZE: u32 = 16; - const ITEM_ON_HEAP_SIZE: usize = 16 + HEADER_SIZE as usize; - - let initial_size = ITEM_ON_HEAP_SIZE * 3; - let mut mem = vec![0u8; initial_size]; + let mut mem = MemoryInstance::with_pages(2); let mut heap = FreeingBumpHeapAllocator::new(0); - let _ = heap.allocate(&mut mem[..], ITEM_SIZE).unwrap(); + heap.allocate(&mut mem, PAGE_SIZE / 2).unwrap(); - mem.truncate(initial_size - 1); + mem.data.truncate(PAGE_SIZE as usize); - match heap.allocate(&mut mem[..], ITEM_SIZE).unwrap_err() { + match heap.allocate(&mut mem, PAGE_SIZE / 2).unwrap_err() { Error::MemoryShrinked => (), _ => panic!(), } } + + #[test] + fn should_grow_memory_when_running_out_of_memory() { + let mut mem = MemoryInstance::with_pages(1); + let mut heap = FreeingBumpHeapAllocator::new(0); + + assert_eq!(1, mem.pages()); + + heap.allocate(&mut mem, PAGE_SIZE * 2).unwrap(); + + assert_eq!(3, mem.pages()); + } } diff --git a/client/allocator/src/lib.rs b/client/allocator/src/lib.rs index 31179ab5d..e50d7d54c 100644 --- a/client/allocator/src/lib.rs +++ b/client/allocator/src/lib.rs @@ -27,3 +27,35 @@ mod freeing_bump; pub use error::Error; pub use freeing_bump::{AllocationStats, FreeingBumpHeapAllocator}; + +/// The size of one wasm page in bytes. +/// +/// The wasm memory is divided into pages, meaning the minimum size of a memory is one page. +const PAGE_SIZE: u32 = 65536; + +/// The maximum number of wasm pages that can be allocated. +/// +/// 4GiB / [`PAGE_SIZE`]. +const MAX_WASM_PAGES: u32 = (4u64 * 1024 * 1024 * 1024 / PAGE_SIZE as u64) as u32; + +/// Grants access to the memory for the allocator. +/// +/// Memory of wasm is allocated in pages. A page has a constant size of 64KiB. The maximum allowed +/// memory size as defined in the wasm specification is 4GiB (65536 pages). +pub trait Memory { + /// Run the given closure `run` and grant it write access to the raw memory. + fn with_access_mut(&mut self, run: impl FnOnce(&mut [u8]) -> R) -> R; + /// Run the given closure `run` and grant it read access to the raw memory. + fn with_access(&self, run: impl FnOnce(&[u8]) -> R) -> R; + /// Grow the memory by `additional` pages. + fn grow(&mut self, additional: u32) -> Result<(), ()>; + /// Returns the current number of pages this memory has allocated. + fn pages(&self) -> u32; + /// Returns the maximum number of pages this memory is allowed to allocate. + /// + /// The returned number needs to be smaller or equal to `65536`. The returned number needs to be + /// bigger or equal to [`Self::pages`]. + /// + /// If `None` is returned, there is no maximum (besides the maximum defined in the wasm spec). + fn max_pages(&self) -> Option; +} diff --git a/client/api/src/call_executor.rs b/client/api/src/call_executor.rs index 42d3c4e34..db8e4d849 100644 --- a/client/api/src/call_executor.rs +++ b/client/api/src/call_executor.rs @@ -19,6 +19,7 @@ //! A method call executor interface. use sc_executor::{RuntimeVersion, RuntimeVersionOf}; +use sp_core::traits::CallContext; use sp_runtime::traits::Block as BlockT; use sp_state_machine::{ExecutionStrategy, OverlayedChanges, StorageProof}; use std::cell::RefCell; @@ -58,6 +59,7 @@ pub trait CallExecutor: RuntimeVersionOf { method: &str, call_data: &[u8], strategy: ExecutionStrategy, + context: CallContext, ) -> Result, sp_blockchain::Error>; /// Execute a contextual call on top of state in a block of a given hash. diff --git a/client/executor/Cargo.toml b/client/executor/Cargo.toml index 46b72565a..21a9bd70d 100644 --- a/client/executor/Cargo.toml +++ b/client/executor/Cargo.toml @@ -17,7 +17,7 @@ targets = ["x86_64-unknown-linux-gnu"] lru = "0.8.1" parking_lot = "0.12.1" tracing = "0.1.29" -wasmi = "0.13" +wasmi = "0.13.2" codec = { package = "parity-scale-codec", version = "3.2.2" } sc-executor-common = { version = "0.10.0-dev", path = "common" } @@ -43,6 +43,7 @@ sp-state-machine = { version = "0.13.0", path = "../../primitives/state-machine" sp-runtime = { version = "7.0.0", path = "../../primitives/runtime" } sp-maybe-compressed-blob = { version = "4.1.0-dev", path = "../../primitives/maybe-compressed-blob" } sc-tracing = { version = "4.0.0-dev", path = "../tracing" } +sp-tracing = { version = "6.0.0", path = "../../primitives/tracing" } tracing-subscriber = "0.2.19" paste = "1.0" regex = "1.6.0" diff --git a/client/executor/benches/bench.rs b/client/executor/benches/bench.rs index 62e8b261f..10425ea46 100644 --- a/client/executor/benches/bench.rs +++ b/client/executor/benches/bench.rs @@ -21,7 +21,7 @@ use codec::Encode; use sc_executor_common::{ runtime_blob::RuntimeBlob, - wasm_runtime::{WasmInstance, WasmModule}, + wasm_runtime::{HeapAllocStrategy, WasmInstance, WasmModule}, }; use sc_executor_wasmtime::InstantiationStrategy; use sc_runtime_test::wasm_binary_unwrap as test_runtime; @@ -51,13 +51,13 @@ fn initialize( ) -> Arc { let blob = RuntimeBlob::uncompress_if_needed(runtime).unwrap(); let host_functions = sp_io::SubstrateHostFunctions::host_functions(); - let heap_pages = 2048; + let extra_pages = 2048; let allow_missing_func_imports = true; match method { Method::Interpreted => sc_executor_wasmi::create_runtime( blob, - heap_pages, + HeapAllocStrategy::Static { extra_pages }, host_functions, allow_missing_func_imports, ) @@ -67,12 +67,11 @@ fn initialize( allow_missing_func_imports, cache_path: None, semantics: sc_executor_wasmtime::Semantics { - extra_heap_pages: heap_pages, + heap_alloc_strategy: HeapAllocStrategy::Static { extra_pages }, instantiation_strategy, deterministic_stack_limit: None, canonicalize_nans: false, parallel_compilation: true, - max_memory_size: None, }, }; diff --git a/client/executor/common/Cargo.toml b/client/executor/common/Cargo.toml index 648e937d3..dd74ea2cf 100644 --- a/client/executor/common/Cargo.toml +++ b/client/executor/common/Cargo.toml @@ -16,7 +16,7 @@ targets = ["x86_64-unknown-linux-gnu"] [dependencies] thiserror = "1.0.30" wasm-instrument = "0.3" -wasmi = "0.13" +wasmi = "0.13.2" sc-allocator = { version = "4.1.0-dev", path = "../../allocator" } sp-maybe-compressed-blob = { version = "4.1.0-dev", path = "../../../primitives/maybe-compressed-blob" } sp-wasm-interface = { version = "7.0.0", path = "../../../primitives/wasm-interface" } diff --git a/client/executor/common/src/runtime_blob/runtime_blob.rs b/client/executor/common/src/runtime_blob/runtime_blob.rs index dc0cde8d2..24dc7e393 100644 --- a/client/executor/common/src/runtime_blob/runtime_blob.rs +++ b/client/executor/common/src/runtime_blob/runtime_blob.rs @@ -16,7 +16,7 @@ // You should have received a copy of the GNU General Public License // along with this program. If not, see . -use crate::error::WasmError; +use crate::{error::WasmError, wasm_runtime::HeapAllocStrategy}; use wasm_instrument::{ export_mutable_globals, parity_wasm::elements::{ @@ -157,18 +157,13 @@ impl RuntimeBlob { Ok(()) } - /// Increases the number of memory pages requested by the WASM blob by - /// the given amount of `extra_heap_pages`. + /// Modifies the blob's memory section according to the given `heap_alloc_strategy`. /// /// Will return an error in case there is no memory section present, /// or if the memory section is empty. - /// - /// Only modifies the initial size of the memory; the maximum is unmodified - /// unless it's smaller than the initial size, in which case it will be increased - /// so that it's at least as big as the initial size. - pub fn add_extra_heap_pages_to_memory_section( + pub fn setup_memory_according_to_heap_alloc_strategy( &mut self, - extra_heap_pages: u32, + heap_alloc_strategy: HeapAllocStrategy, ) -> Result<(), WasmError> { let memory_section = self .raw_module @@ -179,8 +174,17 @@ impl RuntimeBlob { return Err(WasmError::Other("memory section is empty".into())) } for memory_ty in memory_section.entries_mut() { - let min = memory_ty.limits().initial().saturating_add(extra_heap_pages); - let max = memory_ty.limits().maximum().map(|max| std::cmp::max(min, max)); + let initial = memory_ty.limits().initial(); + let (min, max) = match heap_alloc_strategy { + HeapAllocStrategy::Dynamic { maximum_pages } => { + // Ensure `initial <= maximum_pages` + (maximum_pages.map(|m| m.min(initial)).unwrap_or(initial), maximum_pages) + }, + HeapAllocStrategy::Static { extra_pages } => { + let pages = initial.saturating_add(extra_pages); + (pages, Some(pages)) + }, + }; *memory_ty = MemoryType::new(min, max); } Ok(()) diff --git a/client/executor/common/src/util.rs b/client/executor/common/src/util.rs index e672861cb..34967f865 100644 --- a/client/executor/common/src/util.rs +++ b/client/executor/common/src/util.rs @@ -26,11 +26,7 @@ use std::ops::Range; /// Returns None if the end of the range would exceed some maximum offset. pub fn checked_range(offset: usize, len: usize, max: usize) -> Option> { let end = offset.checked_add(len)?; - if end <= max { - Some(offset..end) - } else { - None - } + (end <= max).then(|| offset..end) } /// Provides safe memory access interface using an external buffer diff --git a/client/executor/common/src/wasm_runtime.rs b/client/executor/common/src/wasm_runtime.rs index 589925206..e3db7e52f 100644 --- a/client/executor/common/src/wasm_runtime.rs +++ b/client/executor/common/src/wasm_runtime.rs @@ -119,3 +119,29 @@ pub trait WasmInstance: Send { None } } + +/// Defines the heap pages allocation strategy the wasm runtime should use. +/// +/// A heap page is defined as 64KiB of memory. +#[derive(Debug, Copy, Clone, PartialEq, Hash, Eq)] +pub enum HeapAllocStrategy { + /// Allocate a static number of heap pages. + /// + /// The total number of allocated heap pages is the initial number of heap pages requested by + /// the wasm file plus the `extra_pages`. + Static { + /// The number of pages that will be added on top of the initial heap pages requested by + /// the wasm file. + extra_pages: u32, + }, + /// Allocate the initial heap pages as requested by the wasm file and then allow it to grow + /// dynamically. + Dynamic { + /// The absolute maximum size of the linear memory (in pages). + /// + /// When `Some(_)` the linear memory will be allowed to grow up to this limit. + /// When `None` the linear memory will be allowed to grow up to the maximum limit supported + /// by WASM (4GB). + maximum_pages: Option, + }, +} diff --git a/client/executor/runtime-test/src/lib.rs b/client/executor/runtime-test/src/lib.rs index 23584006a..2bd2aeeb6 100644 --- a/client/executor/runtime-test/src/lib.rs +++ b/client/executor/runtime-test/src/lib.rs @@ -328,6 +328,15 @@ sp_core::wasm_export_functions! { assert_eq!(value, -66); } + fn allocate_two_gigabyte() -> u32 { + let mut data = Vec::new(); + for _ in 0..205 { + data.push(Vec::::with_capacity(10 * 1024 * 1024)); + } + + data.iter().map(|d| d.capacity() as u32).sum() + } + fn test_abort_on_panic() { sp_io::panic_handler::abort_on_panic("test_abort_on_panic called"); } diff --git a/client/executor/src/integration_tests/linux.rs b/client/executor/src/integration_tests/linux.rs index 9afe37037..38d75da4e 100644 --- a/client/executor/src/integration_tests/linux.rs +++ b/client/executor/src/integration_tests/linux.rs @@ -21,25 +21,60 @@ use super::mk_test_runtime; use crate::WasmExecutionMethod; use codec::Encode as _; +use sc_executor_common::wasm_runtime::HeapAllocStrategy; mod smaps; use self::smaps::Smaps; +#[test] +fn memory_consumption_interpreted() { + let _ = sp_tracing::try_init_simple(); + + if std::env::var("RUN_TEST").is_ok() { + memory_consumption(WasmExecutionMethod::Interpreted); + } else { + // We need to run the test in isolation, to not getting interfered by the other tests. + let executable = std::env::current_exe().unwrap(); + let output = std::process::Command::new(executable) + .env("RUN_TEST", "1") + .args(&["--nocapture", "memory_consumption_interpreted"]) + .output() + .unwrap(); + + assert!(output.status.success()); + } +} + #[test] fn memory_consumption_compiled() { + let _ = sp_tracing::try_init_simple(); + + if std::env::var("RUN_TEST").is_ok() { + memory_consumption(WasmExecutionMethod::Compiled { + instantiation_strategy: + sc_executor_wasmtime::InstantiationStrategy::LegacyInstanceReuse, + }); + } else { + // We need to run the test in isolation, to not getting interfered by the other tests. + let executable = std::env::current_exe().unwrap(); + let status = std::process::Command::new(executable) + .env("RUN_TEST", "1") + .args(&["--nocapture", "memory_consumption_compiled"]) + .status() + .unwrap(); + + assert!(status.success()); + } +} + +fn memory_consumption(wasm_method: WasmExecutionMethod) { // This aims to see if linear memory stays backed by the physical memory after a runtime call. // // For that we make a series of runtime calls, probing the RSS for the VMA matching the linear // memory. After the call we expect RSS to be equal to 0. - let runtime = mk_test_runtime( - WasmExecutionMethod::Compiled { - instantiation_strategy: - sc_executor_wasmtime::InstantiationStrategy::LegacyInstanceReuse, - }, - 1024, - ); + let runtime = mk_test_runtime(wasm_method, HeapAllocStrategy::Static { extra_pages: 1024 }); let mut instance = runtime.new_instance().unwrap(); let heap_base = instance diff --git a/client/executor/src/integration_tests/mod.rs b/client/executor/src/integration_tests/mod.rs index 324c8d515..066b1497f 100644 --- a/client/executor/src/integration_tests/mod.rs +++ b/client/executor/src/integration_tests/mod.rs @@ -24,7 +24,7 @@ use codec::{Decode, Encode}; use sc_executor_common::{ error::{Error, WasmError}, runtime_blob::RuntimeBlob, - wasm_runtime::WasmModule, + wasm_runtime::{HeapAllocStrategy, WasmModule}, }; use sc_runtime_test::wasm_binary_unwrap; use sp_core::{ @@ -52,11 +52,13 @@ macro_rules! test_wasm_execution { paste::item! { #[test] fn [<$method_name _interpreted>]() { + let _ = sp_tracing::try_init_simple(); $method_name(WasmExecutionMethod::Interpreted); } #[test] fn [<$method_name _compiled_recreate_instance_cow>]() { + let _ = sp_tracing::try_init_simple(); $method_name(WasmExecutionMethod::Compiled { instantiation_strategy: sc_executor_wasmtime::InstantiationStrategy::RecreateInstanceCopyOnWrite }); @@ -64,6 +66,7 @@ macro_rules! test_wasm_execution { #[test] fn [<$method_name _compiled_recreate_instance_vanilla>]() { + let _ = sp_tracing::try_init_simple(); $method_name(WasmExecutionMethod::Compiled { instantiation_strategy: sc_executor_wasmtime::InstantiationStrategy::RecreateInstance }); @@ -71,6 +74,7 @@ macro_rules! test_wasm_execution { #[test] fn [<$method_name _compiled_pooling_cow>]() { + let _ = sp_tracing::try_init_simple(); $method_name(WasmExecutionMethod::Compiled { instantiation_strategy: sc_executor_wasmtime::InstantiationStrategy::PoolingCopyOnWrite }); @@ -78,6 +82,7 @@ macro_rules! test_wasm_execution { #[test] fn [<$method_name _compiled_pooling_vanilla>]() { + let _ = sp_tracing::try_init_simple(); $method_name(WasmExecutionMethod::Compiled { instantiation_strategy: sc_executor_wasmtime::InstantiationStrategy::Pooling }); @@ -85,6 +90,7 @@ macro_rules! test_wasm_execution { #[test] fn [<$method_name _compiled_legacy_instance_reuse>]() { + let _ = sp_tracing::try_init_simple(); $method_name(WasmExecutionMethod::Compiled { instantiation_strategy: sc_executor_wasmtime::InstantiationStrategy::LegacyInstanceReuse }); @@ -474,7 +480,10 @@ fn should_trap_when_heap_exhausted(wasm_method: WasmExecutionMethod) { } } -fn mk_test_runtime(wasm_method: WasmExecutionMethod, pages: u64) -> Arc { +fn mk_test_runtime( + wasm_method: WasmExecutionMethod, + pages: HeapAllocStrategy, +) -> Arc { let blob = RuntimeBlob::uncompress_if_needed(wasm_binary_unwrap()) .expect("failed to create a runtime blob out of test runtime"); @@ -490,7 +499,8 @@ fn mk_test_runtime(wasm_method: WasmExecutionMethod, pages: u64) -> Arc( wasm_method, - 1024, + HeapAllocStrategy::Dynamic { maximum_pages: Some(1024) }, RuntimeBlob::uncompress_if_needed(&binary[..]).unwrap(), true, None, diff --git a/client/executor/src/native_executor.rs b/client/executor/src/native_executor.rs index a2af97ae4..c72cf3c9c 100644 --- a/client/executor/src/native_executor.rs +++ b/client/executor/src/native_executor.rs @@ -32,14 +32,15 @@ use std::{ use codec::Encode; use sc_executor_common::{ runtime_blob::RuntimeBlob, - wasm_runtime::{AllocationStats, WasmInstance, WasmModule}, + wasm_runtime::{AllocationStats, HeapAllocStrategy, WasmInstance, WasmModule}, }; -use sp_core::traits::{CodeExecutor, Externalities, RuntimeCode}; +use sp_core::traits::{CallContext, CodeExecutor, Externalities, RuntimeCode}; use sp_version::{GetNativeVersion, NativeVersion, RuntimeVersion}; use sp_wasm_interface::{ExtendedHostFunctions, HostFunctions}; -/// Default num of pages for the heap -const DEFAULT_HEAP_PAGES: u64 = 2048; +/// Default heap allocation strategy. +const DEFAULT_HEAP_ALLOC_STRATEGY: HeapAllocStrategy = + HeapAllocStrategy::Static { extra_pages: 2048 }; /// Set up the externalities and safe calling environment to execute runtime calls. /// @@ -79,13 +80,136 @@ pub trait NativeExecutionDispatch: Send + Sync { fn native_version() -> NativeVersion; } +fn unwrap_heap_pages(pages: Option) -> HeapAllocStrategy { + pages.unwrap_or_else(|| DEFAULT_HEAP_ALLOC_STRATEGY) +} + +/// Builder for creating a [`WasmExecutor`] instance. +pub struct WasmExecutorBuilder { + _phantom: PhantomData, + method: WasmExecutionMethod, + onchain_heap_alloc_strategy: Option, + offchain_heap_alloc_strategy: Option, + max_runtime_instances: usize, + cache_path: Option, + allow_missing_host_functions: bool, + runtime_cache_size: u8, +} + +impl WasmExecutorBuilder { + /// Create a new instance of `Self` + /// + /// - `method`: The wasm execution method that should be used by the executor. + pub fn new(method: WasmExecutionMethod) -> Self { + Self { + _phantom: PhantomData, + method, + onchain_heap_alloc_strategy: None, + offchain_heap_alloc_strategy: None, + max_runtime_instances: 2, + runtime_cache_size: 4, + allow_missing_host_functions: false, + cache_path: None, + } + } + + /// Create the wasm executor with the given number of `heap_alloc_strategy` for onchain runtime + /// calls. + pub fn with_onchain_heap_alloc_strategy( + mut self, + heap_alloc_strategy: HeapAllocStrategy, + ) -> Self { + self.onchain_heap_alloc_strategy = Some(heap_alloc_strategy); + self + } + + /// Create the wasm executor with the given number of `heap_alloc_strategy` for offchain runtime + /// calls. + pub fn with_offchain_heap_alloc_strategy( + mut self, + heap_alloc_strategy: HeapAllocStrategy, + ) -> Self { + self.offchain_heap_alloc_strategy = Some(heap_alloc_strategy); + self + } + + /// Create the wasm executor with the given maximum number of `instances`. + /// + /// The number of `instances` defines how many different instances of a runtime the cache is + /// storing. + /// + /// By default the maximum number of `instances` is `2`. + pub fn with_max_runtime_instances(mut self, instances: usize) -> Self { + self.max_runtime_instances = instances; + self + } + + /// Create the wasm executor with the given `cache_path`. + /// + /// The `cache_path` is A path to a directory where the executor can place its files for + /// purposes of caching. This may be important in cases when there are many different modules + /// with the compiled execution method is used. + /// + /// By default there is no `cache_path` given. + pub fn with_cache_path(mut self, cache_path: impl Into) -> Self { + self.cache_path = Some(cache_path.into()); + self + } + + /// Create the wasm executor and allow/forbid missing host functions. + /// + /// If missing host functions are forbidden, the instantiation of a wasm blob will fail + /// for imported host functions that the executor is not aware of. If they are allowed, + /// a stub is generated that will return an error when being called while executing the wasm. + /// + /// By default missing host functions are forbidden. + pub fn with_allow_missing_host_functions(mut self, allow: bool) -> Self { + self.allow_missing_host_functions = allow; + self + } + + /// Create the wasm executor with the given `runtime_cache_size`. + /// + /// Defines the number of different runtimes/instantiated wasm blobs the cache stores. + /// Runtimes/wasm blobs are differentiated based on the hash and the number of heap pages. + /// + /// By default this value is set to `4`. + pub fn with_runtime_cache_size(mut self, runtime_cache_size: u8) -> Self { + self.runtime_cache_size = runtime_cache_size; + self + } + + /// Build the configured [`WasmExecutor`]. + pub fn build(self) -> WasmExecutor { + WasmExecutor { + method: self.method, + default_offchain_heap_alloc_strategy: unwrap_heap_pages( + self.offchain_heap_alloc_strategy, + ), + default_onchain_heap_alloc_strategy: unwrap_heap_pages( + self.onchain_heap_alloc_strategy, + ), + cache: Arc::new(RuntimeCache::new( + self.max_runtime_instances, + self.cache_path.clone(), + self.runtime_cache_size, + )), + cache_path: self.cache_path, + allow_missing_host_functions: self.allow_missing_host_functions, + phantom: PhantomData, + } + } +} + /// An abstraction over Wasm code executor. Supports selecting execution backend and /// manages runtime cache. pub struct WasmExecutor { /// Method used to execute fallback Wasm code. method: WasmExecutionMethod, - /// The number of 64KB pages to allocate for Wasm execution. - default_heap_pages: u64, + /// The heap allocation strategy for onchain Wasm calls. + default_onchain_heap_alloc_strategy: HeapAllocStrategy, + /// The heap allocation strategy for offchain Wasm calls. + default_offchain_heap_alloc_strategy: HeapAllocStrategy, /// WASM runtime cache. cache: Arc, /// The path to a directory which the executor can leverage for a file cache, e.g. put there @@ -100,7 +224,8 @@ impl Clone for WasmExecutor { fn clone(&self) -> Self { Self { method: self.method, - default_heap_pages: self.default_heap_pages, + default_onchain_heap_alloc_strategy: self.default_onchain_heap_alloc_strategy, + default_offchain_heap_alloc_strategy: self.default_offchain_heap_alloc_strategy, cache: self.cache.clone(), cache_path: self.cache_path.clone(), allow_missing_host_functions: self.allow_missing_host_functions, @@ -119,8 +244,10 @@ where /// /// `method` - Method used to execute Wasm code. /// - /// `default_heap_pages` - Number of 64KB pages to allocate for Wasm execution. - /// Defaults to `DEFAULT_HEAP_PAGES` if `None` is provided. + /// `default_heap_pages` - Number of 64KB pages to allocate for Wasm execution. Internally this + /// will be mapped as [`HeapAllocStrategy::Static`] where `default_heap_pages` represent the + /// static number of heap pages to allocate. Defaults to `DEFAULT_HEAP_ALLOC_STRATEGY` if `None` + /// is provided. /// /// `max_runtime_instances` - The number of runtime instances to keep in memory ready for reuse. /// @@ -138,7 +265,12 @@ where ) -> Self { WasmExecutor { method, - default_heap_pages: default_heap_pages.unwrap_or(DEFAULT_HEAP_PAGES), + default_onchain_heap_alloc_strategy: unwrap_heap_pages( + default_heap_pages.map(|h| HeapAllocStrategy::Static { extra_pages: h as _ }), + ), + default_offchain_heap_alloc_strategy: unwrap_heap_pages( + default_heap_pages.map(|h| HeapAllocStrategy::Static { extra_pages: h as _ }), + ), cache: Arc::new(RuntimeCache::new( max_runtime_instances, cache_path.clone(), @@ -150,6 +282,11 @@ where } } + /// Instantiate a builder for creating an instance of `Self`. + pub fn builder(method: WasmExecutionMethod) -> WasmExecutorBuilder { + WasmExecutorBuilder::new(method) + } + /// Ignore missing function imports if set true. pub fn allow_missing_host_functions(&mut self, allow_missing_host_functions: bool) { self.allow_missing_host_functions = allow_missing_host_functions @@ -172,6 +309,7 @@ where &self, runtime_code: &RuntimeCode, ext: &mut dyn Externalities, + heap_alloc_strategy: HeapAllocStrategy, f: F, ) -> Result where @@ -186,7 +324,7 @@ where runtime_code, ext, self.method, - self.default_heap_pages, + heap_alloc_strategy, self.allow_missing_host_functions, |module, instance, version, ext| { let module = AssertUnwindSafe(module); @@ -259,7 +397,7 @@ where ) -> std::result::Result, Error> { let module = crate::wasm_runtime::create_wasm_runtime_with_code::( self.method, - self.default_heap_pages, + self.default_onchain_heap_alloc_strategy, runtime_blob, allow_missing_host_functions, self.cache_path.as_deref(), @@ -334,6 +472,7 @@ where method: &str, data: &[u8], _use_native: bool, + context: CallContext, ) -> (Result>, bool) { tracing::trace!( target: "executor", @@ -341,10 +480,25 @@ where "Executing function", ); - let result = - self.with_instance(runtime_code, ext, |_, mut instance, _onchain_version, mut ext| { + let on_chain_heap_alloc_strategy = runtime_code + .heap_pages + .map(|h| HeapAllocStrategy::Static { extra_pages: h as _ }) + .unwrap_or_else(|| self.default_onchain_heap_alloc_strategy); + + let heap_alloc_strategy = match context { + CallContext::Offchain => self.default_offchain_heap_alloc_strategy, + CallContext::Onchain => on_chain_heap_alloc_strategy, + }; + + let result = self.with_instance( + runtime_code, + ext, + heap_alloc_strategy, + |_, mut instance, _onchain_version, mut ext| { with_externalities_safe(&mut **ext, move || instance.call_export(method, data)) - }); + }, + ); + (result, false) } } @@ -358,20 +512,25 @@ where ext: &mut dyn Externalities, runtime_code: &RuntimeCode, ) -> Result { - self.with_instance(runtime_code, ext, |_module, _instance, version, _ext| { - Ok(version.cloned().ok_or_else(|| Error::ApiError("Unknown version".into()))) - }) + let on_chain_heap_pages = runtime_code + .heap_pages + .map(|h| HeapAllocStrategy::Static { extra_pages: h as _ }) + .unwrap_or_else(|| self.default_onchain_heap_alloc_strategy); + + self.with_instance( + runtime_code, + ext, + on_chain_heap_pages, + |_module, _instance, version, _ext| { + Ok(version.cloned().ok_or_else(|| Error::ApiError("Unknown version".into()))) + }, + ) } } /// A generic `CodeExecutor` implementation that uses a delegate to determine wasm code equivalence /// and dispatch to native code when possible, falling back on `WasmExecutor` when not. -pub struct NativeElseWasmExecutor -where - D: NativeExecutionDispatch, -{ - /// Dummy field to avoid the compiler complaining about us not using `D`. - _dummy: PhantomData, +pub struct NativeElseWasmExecutor { /// Native runtime version info. native_version: NativeVersion, /// Fallback wasm executor. @@ -386,8 +545,10 @@ impl NativeElseWasmExecutor { /// /// `fallback_method` - Method used to execute fallback Wasm code. /// - /// `default_heap_pages` - Number of 64KB pages to allocate for Wasm execution. - /// Defaults to `DEFAULT_HEAP_PAGES` if `None` is provided. + /// `default_heap_pages` - Number of 64KB pages to allocate for Wasm execution. Internally this + /// will be mapped as [`HeapAllocStrategy::Static`] where `default_heap_pages` represent the + /// static number of heap pages to allocate. Defaults to `DEFAULT_HEAP_ALLOC_STRATEGY` if `None` + /// is provided. /// /// `max_runtime_instances` - The number of runtime instances to keep in memory ready for reuse. /// @@ -406,11 +567,16 @@ impl NativeElseWasmExecutor { runtime_cache_size, ); - NativeElseWasmExecutor { - _dummy: Default::default(), - native_version: D::native_version(), - wasm, - } + NativeElseWasmExecutor { native_version: D::native_version(), wasm } + } + + /// Create a new instance using the given [`WasmExecutor`]. + pub fn new_with_wasm_executor( + executor: WasmExecutor< + ExtendedHostFunctions, + >, + ) -> Self { + Self { native_version: D::native_version(), wasm: executor } } /// Ignore missing function imports if set true. @@ -425,9 +591,7 @@ impl RuntimeVersionOf for NativeElseWasmExecutor ext: &mut dyn Externalities, runtime_code: &RuntimeCode, ) -> Result { - self.wasm.with_instance(runtime_code, ext, |_module, _instance, version, _ext| { - Ok(version.cloned().ok_or_else(|| Error::ApiError("Unknown version".into()))) - }) + self.wasm.runtime_version(ext, runtime_code) } } @@ -447,6 +611,7 @@ impl CodeExecutor for NativeElseWasmExecut method: &str, data: &[u8], use_native: bool, + context: CallContext, ) -> (Result>, bool) { tracing::trace!( target: "executor", @@ -454,10 +619,21 @@ impl CodeExecutor for NativeElseWasmExecut "Executing function", ); + let on_chain_heap_alloc_strategy = runtime_code + .heap_pages + .map(|h| HeapAllocStrategy::Static { extra_pages: h as _ }) + .unwrap_or_else(|| self.wasm.default_onchain_heap_alloc_strategy); + + let heap_alloc_strategy = match context { + CallContext::Offchain => self.wasm.default_offchain_heap_alloc_strategy, + CallContext::Onchain => on_chain_heap_alloc_strategy, + }; + let mut used_native = false; let result = self.wasm.with_instance( runtime_code, ext, + heap_alloc_strategy, |_, mut instance, onchain_version, mut ext| { let onchain_version = onchain_version.ok_or_else(|| Error::ApiError("Unknown version".into()))?; @@ -496,11 +672,7 @@ impl CodeExecutor for NativeElseWasmExecut impl Clone for NativeElseWasmExecutor { fn clone(&self) -> Self { - NativeElseWasmExecutor { - _dummy: Default::default(), - native_version: D::native_version(), - wasm: self.wasm.clone(), - } + NativeElseWasmExecutor { native_version: D::native_version(), wasm: self.wasm.clone() } } } diff --git a/client/executor/src/wasm_runtime.rs b/client/executor/src/wasm_runtime.rs index 4869fbae2..254380dbb 100644 --- a/client/executor/src/wasm_runtime.rs +++ b/client/executor/src/wasm_runtime.rs @@ -27,7 +27,7 @@ use lru::LruCache; use parking_lot::Mutex; use sc_executor_common::{ runtime_blob::RuntimeBlob, - wasm_runtime::{WasmInstance, WasmModule}, + wasm_runtime::{HeapAllocStrategy, WasmInstance, WasmModule}, }; use sp_core::traits::{Externalities, FetchRuntimeCode, RuntimeCode}; use sp_version::RuntimeVersion; @@ -64,8 +64,8 @@ struct VersionedRuntimeId { code_hash: Vec, /// Wasm runtime type. wasm_method: WasmExecutionMethod, - /// The number of WebAssembly heap pages this instance was created with. - heap_pages: u64, + /// The heap allocation strategy this runtime was created with. + heap_alloc_strategy: HeapAllocStrategy, } /// A Wasm runtime object along with its cached runtime version. @@ -197,10 +197,12 @@ impl RuntimeCache { /// /// `runtime_code` - The runtime wasm code used setup the runtime. /// - /// `default_heap_pages` - Number of 64KB pages to allocate for Wasm execution. + /// `ext` - The externalities to access the state. /// /// `wasm_method` - Type of WASM backend to use. /// + /// `heap_alloc_strategy` - The heap allocation strategy to use. + /// /// `allow_missing_func_imports` - Ignore missing function imports. /// /// `f` - Function to execute. @@ -219,7 +221,7 @@ impl RuntimeCache { runtime_code: &'c RuntimeCode<'c>, ext: &mut dyn Externalities, wasm_method: WasmExecutionMethod, - default_heap_pages: u64, + heap_alloc_strategy: HeapAllocStrategy, allow_missing_func_imports: bool, f: F, ) -> Result, Error> @@ -233,10 +235,9 @@ impl RuntimeCache { ) -> Result, { let code_hash = &runtime_code.hash; - let heap_pages = runtime_code.heap_pages.unwrap_or(default_heap_pages); let versioned_runtime_id = - VersionedRuntimeId { code_hash: code_hash.clone(), heap_pages, wasm_method }; + VersionedRuntimeId { code_hash: code_hash.clone(), heap_alloc_strategy, wasm_method }; let mut runtimes = self.runtimes.lock(); // this must be released prior to calling f let versioned_runtime = if let Some(versioned_runtime) = runtimes.get(&versioned_runtime_id) @@ -251,7 +252,7 @@ impl RuntimeCache { &code, ext, wasm_method, - heap_pages, + heap_alloc_strategy, allow_missing_func_imports, self.max_runtime_instances, self.cache_path.as_deref(), @@ -289,7 +290,7 @@ impl RuntimeCache { /// Create a wasm runtime with the given `code`. pub fn create_wasm_runtime_with_code( wasm_method: WasmExecutionMethod, - heap_pages: u64, + heap_alloc_strategy: HeapAllocStrategy, blob: RuntimeBlob, allow_missing_func_imports: bool, cache_path: Option<&Path>, @@ -307,7 +308,7 @@ where sc_executor_wasmi::create_runtime( blob, - heap_pages, + heap_alloc_strategy, H::host_functions(), allow_missing_func_imports, ) @@ -320,12 +321,11 @@ where allow_missing_func_imports, cache_path: cache_path.map(ToOwned::to_owned), semantics: sc_executor_wasmtime::Semantics { - extra_heap_pages: heap_pages, + heap_alloc_strategy, instantiation_strategy, deterministic_stack_limit: None, canonicalize_nans: false, parallel_compilation: true, - max_memory_size: None, }, }, ) @@ -393,7 +393,7 @@ fn create_versioned_wasm_runtime( code: &[u8], ext: &mut dyn Externalities, wasm_method: WasmExecutionMethod, - heap_pages: u64, + heap_alloc_strategy: HeapAllocStrategy, allow_missing_func_imports: bool, max_instances: usize, cache_path: Option<&Path>, @@ -408,11 +408,11 @@ where // Use the runtime blob to scan if there is any metadata embedded into the wasm binary // pertaining to runtime version. We do it before consuming the runtime blob for creating the // runtime. - let mut version: Option<_> = read_embedded_version(&blob)?; + let mut version = read_embedded_version(&blob)?; let runtime = create_wasm_runtime_with_code::( wasm_method, - heap_pages, + heap_alloc_strategy, blob, allow_missing_func_imports, cache_path, diff --git a/client/executor/wasmi/Cargo.toml b/client/executor/wasmi/Cargo.toml index 423544002..ded44f4ca 100644 --- a/client/executor/wasmi/Cargo.toml +++ b/client/executor/wasmi/Cargo.toml @@ -15,7 +15,7 @@ targets = ["x86_64-unknown-linux-gnu"] [dependencies] log = "0.4.17" -wasmi = "0.13" +wasmi = { version = "0.13.2", features = [ "virtual_memory" ] } sc-allocator = { version = "4.1.0-dev", path = "../../allocator" } sc-executor-common = { version = "0.10.0-dev", path = "../common" } sp-runtime-interface = { version = "7.0.0", path = "../../../primitives/runtime-interface" } diff --git a/client/executor/wasmi/src/lib.rs b/client/executor/wasmi/src/lib.rs index 78b32a027..c757ff8af 100644 --- a/client/executor/wasmi/src/lib.rs +++ b/client/executor/wasmi/src/lib.rs @@ -20,23 +20,58 @@ use std::{cell::RefCell, str, sync::Arc}; -use log::{debug, error, trace}; +use log::{error, trace}; use wasmi::{ memory_units::Pages, - FuncInstance, ImportsBuilder, MemoryInstance, MemoryRef, Module, ModuleInstance, ModuleRef, + FuncInstance, ImportsBuilder, MemoryRef, Module, ModuleInstance, ModuleRef, RuntimeValue::{self, I32, I64}, TableRef, }; -use sc_allocator::AllocationStats; +use sc_allocator::{AllocationStats, FreeingBumpHeapAllocator}; use sc_executor_common::{ error::{Error, MessageWithBacktrace, WasmError}, runtime_blob::{DataSegmentsSnapshot, RuntimeBlob}, - wasm_runtime::{InvokeMethod, WasmInstance, WasmModule}, + wasm_runtime::{HeapAllocStrategy, InvokeMethod, WasmInstance, WasmModule}, }; use sp_runtime_interface::unpack_ptr_and_len; use sp_wasm_interface::{Function, FunctionContext, Pointer, Result as WResult, WordSize}; +/// Wrapper around [`MemorRef`] that implements [`sc_allocator::Memory`]. +struct MemoryWrapper<'a>(&'a MemoryRef); + +impl sc_allocator::Memory for MemoryWrapper<'_> { + fn with_access_mut(&mut self, run: impl FnOnce(&mut [u8]) -> R) -> R { + self.0.with_direct_access_mut(run) + } + + fn with_access(&self, run: impl FnOnce(&[u8]) -> R) -> R { + self.0.with_direct_access(run) + } + + fn pages(&self) -> u32 { + self.0.current_size().0 as _ + } + + fn max_pages(&self) -> Option { + self.0.maximum().map(|p| p.0 as _) + } + + fn grow(&mut self, additional: u32) -> Result<(), ()> { + self.0 + .grow(Pages(additional as _)) + .map_err(|e| { + log::error!( + target: "wasm-executor", + "Failed to grow memory by {} pages: {}", + additional, + e, + ) + }) + .map(drop) + } +} + struct FunctionExecutor { heap: RefCell, memory: MemoryRef, @@ -55,7 +90,7 @@ impl FunctionExecutor { missing_functions: Arc>, ) -> Result { Ok(FunctionExecutor { - heap: RefCell::new(sc_allocator::FreeingBumpHeapAllocator::new(heap_base)), + heap: RefCell::new(FreeingBumpHeapAllocator::new(heap_base)), memory: m, host_functions, allow_missing_func_imports, @@ -75,15 +110,17 @@ impl FunctionContext for FunctionExecutor { } fn allocate_memory(&mut self, size: WordSize) -> WResult> { - let heap = &mut self.heap.borrow_mut(); - self.memory - .with_direct_access_mut(|mem| heap.allocate(mem, size).map_err(|e| e.to_string())) + self.heap + .borrow_mut() + .allocate(&mut MemoryWrapper(&self.memory), size) + .map_err(|e| e.to_string()) } fn deallocate_memory(&mut self, ptr: Pointer) -> WResult<()> { - let heap = &mut self.heap.borrow_mut(); - self.memory - .with_direct_access_mut(|mem| heap.deallocate(mem, ptr).map_err(|e| e.to_string())) + self.heap + .borrow_mut() + .deallocate(&mut MemoryWrapper(&self.memory), ptr) + .map_err(|e| e.to_string()) } fn register_panic_error_message(&mut self, message: &str) { @@ -101,26 +138,17 @@ struct Resolver<'a> { allow_missing_func_imports: bool, /// All the names of functions for that we did not provide a host function. missing_functions: RefCell>, - /// Will be used as initial and maximum size of the imported memory. - heap_pages: usize, - /// By default, runtimes should import memory and this is `Some(_)` after - /// resolving. However, to be backwards compatible, we also support memory - /// exported by the WASM blob (this will be `None` after resolving). - import_memory: RefCell>, } impl<'a> Resolver<'a> { fn new( host_functions: &'a [&'static dyn Function], allow_missing_func_imports: bool, - heap_pages: usize, ) -> Resolver<'a> { Resolver { host_functions, allow_missing_func_imports, missing_functions: RefCell::new(Vec::new()), - heap_pages, - import_memory: Default::default(), } } } @@ -148,7 +176,11 @@ impl<'a> wasmi::ModuleImportResolver for Resolver<'a> { } if self.allow_missing_func_imports { - trace!(target: "wasm-executor", "Could not find function `{}`, a stub will be provided instead.", name); + trace!( + target: "wasm-executor", + "Could not find function `{}`, a stub will be provided instead.", + name, + ); let id = self.missing_functions.borrow().len() + self.host_functions.len(); self.missing_functions.borrow_mut().push(name.to_string()); @@ -160,44 +192,12 @@ impl<'a> wasmi::ModuleImportResolver for Resolver<'a> { fn resolve_memory( &self, - field_name: &str, - memory_type: &wasmi::MemoryDescriptor, + _: &str, + _: &wasmi::MemoryDescriptor, ) -> Result { - if field_name == "memory" { - match &mut *self.import_memory.borrow_mut() { - Some(_) => - Err(wasmi::Error::Instantiation("Memory can not be imported twice!".into())), - memory_ref @ None => { - if memory_type - .maximum() - .map(|m| m.saturating_sub(memory_type.initial())) - .map(|m| self.heap_pages > m as usize) - .unwrap_or(false) - { - Err(wasmi::Error::Instantiation(format!( - "Heap pages ({}) is greater than imported memory maximum ({}).", - self.heap_pages, - memory_type - .maximum() - .map(|m| m.saturating_sub(memory_type.initial())) - .expect("Maximum is set, checked above; qed"), - ))) - } else { - let memory = MemoryInstance::alloc( - Pages(memory_type.initial() as usize + self.heap_pages), - Some(Pages(memory_type.initial() as usize + self.heap_pages)), - )?; - *memory_ref = Some(memory.clone()); - Ok(memory) - } - }, - } - } else { - Err(wasmi::Error::Instantiation(format!( - "Unknown memory reference with name: {}", - field_name - ))) - } + Err(wasmi::Error::Instantiation( + "Internal error, wasmi expects that the wasm blob exports memory.".into(), + )) } } @@ -358,12 +358,11 @@ fn call_in_wasm_module( /// Prepare module instance fn instantiate_module( - heap_pages: usize, module: &Module, host_functions: &[&'static dyn Function], allow_missing_func_imports: bool, ) -> Result<(ModuleRef, Vec, MemoryRef), Error> { - let resolver = Resolver::new(host_functions, allow_missing_func_imports, heap_pages); + let resolver = Resolver::new(host_functions, allow_missing_func_imports); // start module instantiation. Don't run 'start' function yet. let intermediate_instance = ModuleInstance::new(module, &ImportsBuilder::new().with_resolver("env", &resolver))?; @@ -371,22 +370,10 @@ fn instantiate_module( // Verify that the module has the heap base global variable. let _ = get_heap_base(intermediate_instance.not_started_instance())?; - // Get the memory reference. Runtimes should import memory, but to be backwards - // compatible we also support exported memory. - let memory = match resolver.import_memory.into_inner() { - Some(memory) => memory, - None => { - debug!( - target: "wasm-executor", - "WASM blob does not imports memory, falling back to exported memory", - ); - - let memory = get_mem_instance(intermediate_instance.not_started_instance())?; - memory.grow(Pages(heap_pages)).map_err(|_| Error::Runtime)?; - - memory - }, - }; + // The `module` should export the memory with the correct properties (min, max). + // + // This is ensured by modifying the `RuntimeBlob` before initializing the `Module`. + let memory = get_mem_instance(intermediate_instance.not_started_instance())?; if intermediate_instance.has_start() { // Runtime is not allowed to have the `start` function. @@ -451,8 +438,6 @@ pub struct WasmiRuntime { /// Enable stub generation for functions that are not available in `host_functions`. /// These stubs will error when the wasm blob tries to call them. allow_missing_func_imports: bool, - /// Numer of heap pages this runtime uses. - heap_pages: u64, global_vals_snapshot: GlobalValsSnapshot, data_segments_snapshot: DataSegmentsSnapshot, @@ -461,13 +446,9 @@ pub struct WasmiRuntime { impl WasmModule for WasmiRuntime { fn new_instance(&self) -> Result, Error> { // Instantiate this module. - let (instance, missing_functions, memory) = instantiate_module( - self.heap_pages as usize, - &self.module, - &self.host_functions, - self.allow_missing_func_imports, - ) - .map_err(|e| WasmError::Instantiation(e.to_string()))?; + let (instance, missing_functions, memory) = + instantiate_module(&self.module, &self.host_functions, self.allow_missing_func_imports) + .map_err(|e| WasmError::Instantiation(e.to_string()))?; Ok(Box::new(WasmiInstance { instance, @@ -477,6 +458,7 @@ impl WasmModule for WasmiRuntime { host_functions: self.host_functions.clone(), allow_missing_func_imports: self.allow_missing_func_imports, missing_functions: Arc::new(missing_functions), + memory_zeroed: true, })) } } @@ -484,25 +466,26 @@ impl WasmModule for WasmiRuntime { /// Create a new `WasmiRuntime` given the code. This function loads the module and /// stores it in the instance. pub fn create_runtime( - blob: RuntimeBlob, - heap_pages: u64, + mut blob: RuntimeBlob, + heap_alloc_strategy: HeapAllocStrategy, host_functions: Vec<&'static dyn Function>, allow_missing_func_imports: bool, ) -> Result { let data_segments_snapshot = DataSegmentsSnapshot::take(&blob).map_err(|e| WasmError::Other(e.to_string()))?; + // Make sure we only have exported memory to simplify the code of the wasmi executor. + blob.convert_memory_import_into_export()?; + // Ensure that the memory uses the correct heap pages. + blob.setup_memory_according_to_heap_alloc_strategy(heap_alloc_strategy)?; + let module = Module::from_parity_wasm_module(blob.into_inner()).map_err(|_| WasmError::InvalidModule)?; let global_vals_snapshot = { - let (instance, _, _) = instantiate_module( - heap_pages as usize, - &module, - &host_functions, - allow_missing_func_imports, - ) - .map_err(|e| WasmError::Instantiation(e.to_string()))?; + let (instance, _, _) = + instantiate_module(&module, &host_functions, allow_missing_func_imports) + .map_err(|e| WasmError::Instantiation(e.to_string()))?; GlobalValsSnapshot::take(&instance) }; @@ -512,7 +495,6 @@ pub fn create_runtime( global_vals_snapshot, host_functions: Arc::new(host_functions), allow_missing_func_imports, - heap_pages, }) } @@ -522,6 +504,8 @@ pub struct WasmiInstance { instance: ModuleRef, /// The memory instance of used by the wasm module. memory: MemoryRef, + /// Is the memory zeroed? + memory_zeroed: bool, /// The snapshot of global variable values just after instantiation. global_vals_snapshot: GlobalValsSnapshot, /// The snapshot of data segments. @@ -549,14 +533,16 @@ impl WasmiInstance { // We reuse a single wasm instance for multiple calls and a previous call (if any) // altered the state. Therefore, we need to restore the instance to original state. - // First, zero initialize the linear memory. - self.memory.erase().map_err(|e| { - // Snapshot restoration failed. This is pretty unexpected since this can happen - // if some invariant is broken or if the system is under extreme memory pressure - // (so erasing fails). - error!(target: "wasm-executor", "snapshot restoration failed: {}", e); - WasmError::ErasingFailed(e.to_string()) - })?; + if !self.memory_zeroed { + // First, zero initialize the linear memory. + self.memory.erase().map_err(|e| { + // Snapshot restoration failed. This is pretty unexpected since this can happen + // if some invariant is broken or if the system is under extreme memory pressure + // (so erasing fails). + error!(target: "wasm-executor", "snapshot restoration failed: {}", e); + WasmError::ErasingFailed(e.to_string()) + })?; + } // Second, reapply data segments into the linear memory. self.data_segments_snapshot @@ -565,7 +551,7 @@ impl WasmiInstance { // Third, restore the global variables to their initial values. self.global_vals_snapshot.apply(&self.instance)?; - call_in_wasm_module( + let res = call_in_wasm_module( &self.instance, &self.memory, method, @@ -574,7 +560,12 @@ impl WasmiInstance { self.allow_missing_func_imports, self.missing_functions.clone(), allocation_stats, - ) + ); + + // If we couldn't unmap it, erase the memory. + self.memory_zeroed = self.memory.erase().is_ok(); + + res } } @@ -601,4 +592,8 @@ impl WasmInstance for WasmiInstance { None => Ok(None), } } + + fn linear_memory_base_ptr(&self) -> Option<*const u8> { + Some(self.memory.direct_access().as_ref().as_ptr()) + } } diff --git a/client/executor/wasmtime/Cargo.toml b/client/executor/wasmtime/Cargo.toml index ca7daa970..2e892a98a 100644 --- a/client/executor/wasmtime/Cargo.toml +++ b/client/executor/wasmtime/Cargo.toml @@ -13,9 +13,9 @@ readme = "README.md" targets = ["x86_64-unknown-linux-gnu"] [dependencies] +log = "0.4.17" cfg-if = "1.0" libc = "0.2.121" -log = "0.4.17" # When bumping wasmtime do not forget to also bump rustix # to exactly the same version as used by wasmtime! diff --git a/client/executor/wasmtime/src/host.rs b/client/executor/wasmtime/src/host.rs index ead248385..9bd3ca3da 100644 --- a/client/executor/wasmtime/src/host.rs +++ b/client/executor/wasmtime/src/host.rs @@ -24,20 +24,25 @@ use wasmtime::Caller; use sc_allocator::{AllocationStats, FreeingBumpHeapAllocator}; use sp_wasm_interface::{Pointer, WordSize}; -use crate::{runtime::StoreData, util}; +use crate::{instance_wrapper::MemoryWrapper, runtime::StoreData, util}; /// The state required to construct a HostContext context. The context only lasts for one host /// call, whereas the state is maintained for the duration of a Wasm runtime call, which may make /// many different host calls that must share state. pub struct HostState { - allocator: FreeingBumpHeapAllocator, + /// The allocator instance to keep track of allocated memory. + /// + /// This is stored as an `Option` as we need to temporarly set this to `None` when we are + /// allocating/deallocating memory. The problem being that we can only mutable access `caller` + /// once. + allocator: Option, panic_message: Option, } impl HostState { /// Constructs a new `HostState`. pub fn new(allocator: FreeingBumpHeapAllocator) -> Self { - HostState { allocator, panic_message: None } + HostState { allocator: Some(allocator), panic_message: None } } /// Takes the error message out of the host state, leaving a `None` in its place. @@ -46,7 +51,9 @@ impl HostState { } pub(crate) fn allocation_stats(&self) -> AllocationStats { - self.allocator.stats() + self.allocator.as_ref() + .expect("Allocator is always set and only unavailable when doing an allocation/deallocation; qed") + .stats() } } @@ -81,22 +88,38 @@ impl<'a> sp_wasm_interface::FunctionContext for HostContext<'a> { fn allocate_memory(&mut self, size: WordSize) -> sp_wasm_interface::Result> { let memory = self.caller.data().memory(); - let (memory, data) = memory.data_and_store_mut(&mut self.caller); - data.host_state_mut() - .expect("host state is not empty when calling a function in wasm; qed") + let mut allocator = self + .host_state_mut() .allocator - .allocate(memory, size) - .map_err(|e| e.to_string()) + .take() + .expect("allocator is not empty when calling a function in wasm; qed"); + + // We can not return on error early, as we need to store back allocator. + let res = allocator + .allocate(&mut MemoryWrapper(&memory, &mut self.caller), size) + .map_err(|e| e.to_string()); + + self.host_state_mut().allocator = Some(allocator); + + res } fn deallocate_memory(&mut self, ptr: Pointer) -> sp_wasm_interface::Result<()> { let memory = self.caller.data().memory(); - let (memory, data) = memory.data_and_store_mut(&mut self.caller); - data.host_state_mut() - .expect("host state is not empty when calling a function in wasm; qed") + let mut allocator = self + .host_state_mut() .allocator - .deallocate(memory, ptr) - .map_err(|e| e.to_string()) + .take() + .expect("allocator is not empty when calling a function in wasm; qed"); + + // We can not return on error early, as we need to store back allocator. + let res = allocator + .deallocate(&mut MemoryWrapper(&memory, &mut self.caller), ptr) + .map_err(|e| e.to_string()); + + self.host_state_mut().allocator = Some(allocator); + + res } fn register_panic_error_message(&mut self, message: &str) { diff --git a/client/executor/wasmtime/src/instance_wrapper.rs b/client/executor/wasmtime/src/instance_wrapper.rs index 2fb27180f..6d319cce5 100644 --- a/client/executor/wasmtime/src/instance_wrapper.rs +++ b/client/executor/wasmtime/src/instance_wrapper.rs @@ -26,8 +26,7 @@ use sc_executor_common::{ }; use sp_wasm_interface::{Pointer, Value, WordSize}; use wasmtime::{ - AsContext, AsContextMut, Engine, Extern, Func, Global, Instance, InstancePre, Memory, Table, - Val, + AsContext, AsContextMut, Engine, Extern, Instance, InstancePre, Memory, Table, Val, }; /// Invoked entrypoint format. @@ -113,66 +112,58 @@ impl EntryPoint { } } -/// Wrap the given WebAssembly Instance of a wasm module with Substrate-runtime. -/// -/// This struct is a handy wrapper around a wasmtime `Instance` that provides substrate specific -/// routines. -pub struct InstanceWrapper { - instance: Instance, - memory: Memory, - store: Store, -} +/// Wrapper around [`Memory`] that implements [`sc_allocator::Memory`]. +pub(crate) struct MemoryWrapper<'a, C>(pub &'a wasmtime::Memory, pub &'a mut C); -fn extern_memory(extern_: &Extern) -> Option<&Memory> { - match extern_ { - Extern::Memory(mem) => Some(mem), - _ => None, +impl sc_allocator::Memory for MemoryWrapper<'_, C> { + fn with_access(&self, run: impl FnOnce(&[u8]) -> R) -> R { + run(self.0.data(&self.1)) } -} -fn extern_global(extern_: &Extern) -> Option<&Global> { - match extern_ { - Extern::Global(glob) => Some(glob), - _ => None, + fn with_access_mut(&mut self, run: impl FnOnce(&mut [u8]) -> R) -> R { + run(self.0.data_mut(&mut self.1)) } -} -fn extern_table(extern_: &Extern) -> Option<&Table> { - match extern_ { - Extern::Table(table) => Some(table), - _ => None, + fn grow(&mut self, additional: u32) -> std::result::Result<(), ()> { + self.0 + .grow(&mut self.1, additional as u64) + .map_err(|e| { + log::error!( + target: "wasm-executor", + "Failed to grow memory by {} pages: {}", + additional, + e, + ) + }) + .map(drop) } -} -fn extern_func(extern_: &Extern) -> Option<&Func> { - match extern_ { - Extern::Func(func) => Some(func), - _ => None, + fn pages(&self) -> u32 { + self.0.size(&self.1) as u32 } -} -pub(crate) fn create_store(engine: &wasmtime::Engine, max_memory_size: Option) -> Store { - let limits = if let Some(max_memory_size) = max_memory_size { - wasmtime::StoreLimitsBuilder::new().memory_size(max_memory_size).build() - } else { - Default::default() - }; - - let mut store = - Store::new(engine, StoreData { limits, host_state: None, memory: None, table: None }); - if max_memory_size.is_some() { - store.limiter(|s| &mut s.limits); + fn max_pages(&self) -> Option { + self.0.ty(&self.1).maximum().map(|p| p as _) } - store +} + +/// Wrap the given WebAssembly Instance of a wasm module with Substrate-runtime. +/// +/// This struct is a handy wrapper around a wasmtime `Instance` that provides substrate specific +/// routines. +pub struct InstanceWrapper { + instance: Instance, + /// The memory instance of the `instance`. + /// + /// It is important to make sure that we don't make any copies of this to make it easier to + /// proof + memory: Memory, + store: Store, } impl InstanceWrapper { - pub(crate) fn new( - engine: &Engine, - instance_pre: &InstancePre, - max_memory_size: Option, - ) -> Result { - let mut store = create_store(engine, max_memory_size); + pub(crate) fn new(engine: &Engine, instance_pre: &InstancePre) -> Result { + let mut store = Store::new(engine, Default::default()); let instance = instance_pre.instantiate(&mut store).map_err(|error| { WasmError::Other(format!( "failed to instantiate a new WASM module instance: {:#}", @@ -201,9 +192,10 @@ impl InstanceWrapper { self.instance.get_export(&mut self.store, method).ok_or_else(|| { Error::from(format!("Exported method {} is not found", method)) })?; - let func = extern_func(&export) + let func = export + .into_func() .ok_or_else(|| Error::from(format!("Export {} is not a function", method)))?; - EntryPoint::direct(*func, &self.store).map_err(|_| { + EntryPoint::direct(func, &self.store).map_err(|_| { Error::from(format!("Exported function '{}' has invalid signature.", method)) })? }, @@ -259,7 +251,8 @@ impl InstanceWrapper { .get_export(&mut self.store, "__heap_base") .ok_or_else(|| Error::from("__heap_base is not found"))?; - let heap_base_global = extern_global(&heap_base_export) + let heap_base_global = heap_base_export + .into_global() .ok_or_else(|| Error::from("__heap_base is not a global"))?; let heap_base = heap_base_global @@ -277,7 +270,7 @@ impl InstanceWrapper { None => return Ok(None), }; - let global = extern_global(&global).ok_or_else(|| format!("`{}` is not a global", name))?; + let global = global.into_global().ok_or_else(|| format!("`{}` is not a global", name))?; match global.get(&mut self.store) { Val::I32(val) => Ok(Some(Value::I32(val))), @@ -300,7 +293,8 @@ fn get_linear_memory(instance: &Instance, ctx: impl AsContextMut) -> Result Option { instance .get_export(ctx, "__indirect_function_table") .as_ref() - .and_then(extern_table) .cloned() + .and_then(Extern::into_table) } /// Functions related to memory. @@ -403,7 +397,7 @@ fn decommit_works() { let module = wasmtime::Module::new(&engine, code).unwrap(); let linker = wasmtime::Linker::new(&engine); let instance_pre = linker.instantiate_pre(&module).unwrap(); - let mut wrapper = InstanceWrapper::new(&engine, &instance_pre, None).unwrap(); + let mut wrapper = InstanceWrapper::new(&engine, &instance_pre).unwrap(); unsafe { *wrapper.memory.data_ptr(&wrapper.store) = 42 }; assert_eq!(unsafe { *wrapper.memory.data_ptr(&wrapper.store) }, 42); wrapper.decommit(); diff --git a/client/executor/wasmtime/src/runtime.rs b/client/executor/wasmtime/src/runtime.rs index ce3f889fc..de7a2ea0e 100644 --- a/client/executor/wasmtime/src/runtime.rs +++ b/client/executor/wasmtime/src/runtime.rs @@ -20,7 +20,7 @@ use crate::{ host::HostState, - instance_wrapper::{EntryPoint, InstanceWrapper}, + instance_wrapper::{EntryPoint, InstanceWrapper, MemoryWrapper}, util::{self, replace_strategy_if_broken}, }; @@ -31,7 +31,7 @@ use sc_executor_common::{ self, DataSegmentsSnapshot, ExposedMutableGlobalsSet, GlobalsSnapshot, RuntimeBlob, }, util::checked_range, - wasm_runtime::{InvokeMethod, WasmInstance, WasmModule}, + wasm_runtime::{HeapAllocStrategy, InvokeMethod, WasmInstance, WasmModule}, }; use sp_runtime_interface::unpack_ptr_and_len; use sp_wasm_interface::{HostFunctions, Pointer, Value, WordSize}; @@ -42,12 +42,10 @@ use std::{ Arc, }, }; -use wasmtime::{AsContext, Engine, Memory, StoreLimits, Table}; +use wasmtime::{AsContext, Engine, Memory, Table}; +#[derive(Default)] pub(crate) struct StoreData { - /// The limits we apply to the store. We need to store it here to return a reference to this - /// object when we have the limits enabled. - pub(crate) limits: StoreLimits, /// This will only be set when we call into the runtime. pub(crate) host_state: Option, /// This will be always set once the store is initialized. @@ -83,12 +81,11 @@ enum Strategy { struct InstanceCreator { engine: wasmtime::Engine, instance_pre: Arc>, - max_memory_size: Option, } impl InstanceCreator { fn instantiate(&mut self) -> Result { - InstanceWrapper::new(&self.engine, &self.instance_pre, self.max_memory_size) + InstanceWrapper::new(&self.engine, &self.instance_pre) } } @@ -128,18 +125,13 @@ pub struct WasmtimeRuntime { engine: wasmtime::Engine, instance_pre: Arc>, instantiation_strategy: InternalInstantiationStrategy, - config: Config, } impl WasmModule for WasmtimeRuntime { fn new_instance(&self) -> Result> { let strategy = match self.instantiation_strategy { InternalInstantiationStrategy::LegacyInstanceReuse(ref snapshot_data) => { - let mut instance_wrapper = InstanceWrapper::new( - &self.engine, - &self.instance_pre, - self.config.semantics.max_memory_size, - )?; + let mut instance_wrapper = InstanceWrapper::new(&self.engine, &self.instance_pre)?; let heap_base = instance_wrapper.extract_heap_base()?; // This function panics if the instance was created from a runtime blob different @@ -161,7 +153,6 @@ impl WasmModule for WasmtimeRuntime { InternalInstantiationStrategy::Builtin => Strategy::RecreateInstance(InstanceCreator { engine: self.engine.clone(), instance_pre: self.instance_pre.clone(), - max_memory_size: self.config.semantics.max_memory_size, }), }; @@ -350,25 +341,22 @@ fn common_config(semantics: &Semantics) -> std::result::Result (false, false), }; + const WASM_PAGE_SIZE: u64 = 65536; + config.memory_init_cow(use_cow); - config.memory_guaranteed_dense_image_size( - semantics.max_memory_size.map(|max| max as u64).unwrap_or(u64::MAX), - ); + config.memory_guaranteed_dense_image_size(match semantics.heap_alloc_strategy { + HeapAllocStrategy::Dynamic { maximum_pages } => + maximum_pages.map(|p| p as u64 * WASM_PAGE_SIZE).unwrap_or(u64::MAX), + HeapAllocStrategy::Static { .. } => u64::MAX, + }); if use_pooling { - const WASM_PAGE_SIZE: u64 = 65536; const MAX_WASM_PAGES: u64 = 0x10000; - let memory_pages = if let Some(max_memory_size) = semantics.max_memory_size { - let max_memory_size = max_memory_size as u64; - let mut pages = max_memory_size / WASM_PAGE_SIZE; - if max_memory_size % WASM_PAGE_SIZE != 0 { - pages += 1; - } - - std::cmp::min(MAX_WASM_PAGES, pages) - } else { - MAX_WASM_PAGES + let memory_pages = match semantics.heap_alloc_strategy { + HeapAllocStrategy::Dynamic { maximum_pages } => + maximum_pages.map(|p| p as u64).unwrap_or(MAX_WASM_PAGES), + HeapAllocStrategy::Static { .. } => MAX_WASM_PAGES, }; let mut pooling_config = wasmtime::PoolingAllocationConfig::default(); @@ -514,25 +502,8 @@ pub struct Semantics { /// Configures wasmtime to use multiple threads for compiling. pub parallel_compilation: bool, - /// The number of extra WASM pages which will be allocated - /// on top of what is requested by the WASM blob itself. - pub extra_heap_pages: u64, - - /// The total amount of memory in bytes an instance can request. - /// - /// If specified, the runtime will be able to allocate only that much of wasm memory. - /// This is the total number and therefore the [`Semantics::extra_heap_pages`] is accounted - /// for. - /// - /// That means that the initial number of pages of a linear memory plus the - /// [`Semantics::extra_heap_pages`] multiplied by the wasm page size (64KiB) should be less - /// than or equal to `max_memory_size`, otherwise the instance won't be created. - /// - /// Moreover, `memory.grow` will fail (return -1) if the sum of sizes of currently mounted - /// and additional pages exceeds `max_memory_size`. - /// - /// The default is `None`. - pub max_memory_size: Option, + /// The heap allocation strategy to use. + pub heap_alloc_strategy: HeapAllocStrategy, } #[derive(Clone)] @@ -689,12 +660,7 @@ where .instantiate_pre(&module) .map_err(|e| WasmError::Other(format!("cannot preinstantiate module: {:#}", e)))?; - Ok(WasmtimeRuntime { - engine, - instance_pre: Arc::new(instance_pre), - instantiation_strategy, - config, - }) + Ok(WasmtimeRuntime { engine, instance_pre: Arc::new(instance_pre), instantiation_strategy }) } fn prepare_blob_for_compilation( @@ -717,12 +683,7 @@ fn prepare_blob_for_compilation( // now automatically take care of creating the memory for us, and it is also necessary // to enable `wasmtime`'s instance pooling. (Imported memories are ineligible for pooling.) blob.convert_memory_import_into_export()?; - blob.add_extra_heap_pages_to_memory_section( - semantics - .extra_heap_pages - .try_into() - .map_err(|e| WasmError::Other(format!("invalid `extra_heap_pages`: {}", e)))?, - )?; + blob.setup_memory_according_to_heap_alloc_strategy(semantics.heap_alloc_strategy)?; Ok(blob) } @@ -783,9 +744,8 @@ fn inject_input_data( ) -> Result<(Pointer, WordSize)> { let mut ctx = instance.store_mut(); let memory = ctx.data().memory(); - let memory = memory.data_mut(&mut ctx); let data_len = data.len() as WordSize; - let data_ptr = allocator.allocate(memory, data_len)?; + let data_ptr = allocator.allocate(&mut MemoryWrapper(&memory, &mut ctx), data_len)?; util::write_memory_from(instance.store_mut(), data_ptr, data)?; Ok((data_ptr, data_len)) } diff --git a/client/executor/wasmtime/src/tests.rs b/client/executor/wasmtime/src/tests.rs index 57540b35d..7f222b6cf 100644 --- a/client/executor/wasmtime/src/tests.rs +++ b/client/executor/wasmtime/src/tests.rs @@ -17,7 +17,11 @@ // along with this program. If not, see . use codec::{Decode as _, Encode as _}; -use sc_executor_common::{error::Error, runtime_blob::RuntimeBlob, wasm_runtime::WasmModule}; +use sc_executor_common::{ + error::Error, + runtime_blob::RuntimeBlob, + wasm_runtime::{HeapAllocStrategy, WasmModule}, +}; use sc_runtime_test::wasm_binary_unwrap; use crate::InstantiationStrategy; @@ -77,8 +81,7 @@ struct RuntimeBuilder { instantiation_strategy: InstantiationStrategy, canonicalize_nans: bool, deterministic_stack: bool, - extra_heap_pages: u64, - max_memory_size: Option, + heap_pages: HeapAllocStrategy, precompile_runtime: bool, tmpdir: Option, } @@ -90,8 +93,7 @@ impl RuntimeBuilder { instantiation_strategy, canonicalize_nans: false, deterministic_stack: false, - extra_heap_pages: 1024, - max_memory_size: None, + heap_pages: HeapAllocStrategy::Static { extra_pages: 1024 }, precompile_runtime: false, tmpdir: None, } @@ -117,8 +119,8 @@ impl RuntimeBuilder { self } - fn max_memory_size(mut self, max_memory_size: Option) -> Self { - self.max_memory_size = max_memory_size; + fn heap_alloc_strategy(mut self, heap_pages: HeapAllocStrategy) -> Self { + self.heap_pages = heap_pages; self } @@ -152,8 +154,7 @@ impl RuntimeBuilder { }, canonicalize_nans: self.canonicalize_nans, parallel_compilation: true, - extra_heap_pages: self.extra_heap_pages, - max_memory_size: self.max_memory_size, + heap_alloc_strategy: self.heap_pages, }, }; @@ -227,7 +228,7 @@ fn deep_call_stack_wat(depth: usize) -> String { // We need two limits here since depending on whether the code is compiled in debug // or in release mode the maximum call depth is slightly different. const CALL_DEPTH_LOWER_LIMIT: usize = 65455; -const CALL_DEPTH_UPPER_LIMIT: usize = 65503; +const CALL_DEPTH_UPPER_LIMIT: usize = 65509; test_wasm_execution!(test_consume_under_1mb_of_stack_does_not_trap); fn test_consume_under_1mb_of_stack_does_not_trap(instantiation_strategy: InstantiationStrategy) { @@ -344,29 +345,25 @@ fn test_max_memory_pages( import_memory: bool, precompile_runtime: bool, ) { - fn try_instantiate( - max_memory_size: Option, + fn call( + heap_alloc_strategy: HeapAllocStrategy, wat: String, instantiation_strategy: InstantiationStrategy, precompile_runtime: bool, ) -> Result<(), Box> { let mut builder = RuntimeBuilder::new(instantiation_strategy) .use_wat(wat) - .max_memory_size(max_memory_size) + .heap_alloc_strategy(heap_alloc_strategy) .precompile_runtime(precompile_runtime); let runtime = builder.build(); - let mut instance = runtime.new_instance()?; + let mut instance = runtime.new_instance().unwrap(); let _ = instance.call_export("main", &[])?; Ok(()) } - fn memory(initial: u32, maximum: Option, import: bool) -> String { - let memory = if let Some(maximum) = maximum { - format!("(memory $0 {} {})", initial, maximum) - } else { - format!("(memory $0 {})", initial) - }; + fn memory(initial: u32, maximum: u32, import: bool) -> String { + let memory = format!("(memory $0 {} {})", initial, maximum); if import { format!("(import \"env\" \"memory\" {})", memory) @@ -375,152 +372,90 @@ fn test_max_memory_pages( } } - const WASM_PAGE_SIZE: usize = 65536; - - // check the old behavior if preserved. That is, if no limit is set we allow 4 GiB of memory. - try_instantiate( - None, - format!( - r#" - (module - {} - (global (export "__heap_base") i32 (i32.const 0)) - (func (export "main") - (param i32 i32) (result i64) - (i64.const 0) - ) - ) - "#, - /* - We want to allocate the maximum number of pages supported in wasm for this test. - However, due to a bug in wasmtime (I think wasmi is also affected) it is only possible - to allocate 65536 - 1 pages. - - Then, during creation of the Substrate Runtime instance, 1024 (heap_pages) pages are - mounted. - - Thus 65535 = 64511 + 1024 - */ - memory(64511, None, import_memory) - ), - instantiation_strategy, - precompile_runtime, - ) - .unwrap(); - - // max is not specified, therefore it's implied to be 65536 pages (4 GiB). - // - // max_memory_size = (1 (initial) + 1024 (heap_pages)) * WASM_PAGE_SIZE - try_instantiate( - Some((1 + 1024) * WASM_PAGE_SIZE), - format!( - r#" - (module - {} - (global (export "__heap_base") i32 (i32.const 0)) - (func (export "main") - (param i32 i32) (result i64) - (i64.const 0) - ) - ) - "#, - // 1 initial, max is not specified. - memory(1, None, import_memory) - ), - instantiation_strategy, - precompile_runtime, - ) - .unwrap(); - - // max is specified explicitly to 2048 pages. - try_instantiate( - Some((1 + 1024) * WASM_PAGE_SIZE), - format!( - r#" - (module - {} - (global (export "__heap_base") i32 (i32.const 0)) - (func (export "main") - (param i32 i32) (result i64) - (i64.const 0) - ) - ) - "#, - // Max is 2048. - memory(1, Some(2048), import_memory) - ), - instantiation_strategy, - precompile_runtime, - ) - .unwrap(); - - // memory grow should work as long as it doesn't exceed 1025 pages in total. - try_instantiate( - Some((0 + 1024 + 25) * WASM_PAGE_SIZE), - format!( - r#" - (module - {} - (global (export "__heap_base") i32 (i32.const 0)) - (func (export "main") - (param i32 i32) (result i64) - - ;; assert(memory.grow returns != -1) - (if - (i32.eq - (memory.grow - (i32.const 25) + let assert_grow_ok = |alloc_strategy: HeapAllocStrategy, initial_pages: u32, max_pages: u32| { + eprintln!("assert_grow_ok({alloc_strategy:?}, {initial_pages}, {max_pages})"); + + call( + alloc_strategy, + format!( + r#" + (module + {} + (global (export "__heap_base") i32 (i32.const 0)) + (func (export "main") + (param i32 i32) (result i64) + + ;; assert(memory.grow returns != -1) + (if + (i32.eq + (memory.grow + (i32.const 1) + ) + (i32.const -1) + ) + (unreachable) ) - (i32.const -1) + + (i64.const 0) ) - (unreachable) ) - - (i64.const 0) - ) - ) "#, - // Zero starting pages. - memory(0, None, import_memory) - ), - instantiation_strategy, - precompile_runtime, - ) - .unwrap(); + memory(initial_pages, max_pages, import_memory) + ), + instantiation_strategy, + precompile_runtime, + ) + .unwrap() + }; - // We start with 1025 pages and try to grow at least one. - try_instantiate( - Some((1 + 1024) * WASM_PAGE_SIZE), - format!( - r#" - (module - {} - (global (export "__heap_base") i32 (i32.const 0)) - (func (export "main") - (param i32 i32) (result i64) - - ;; assert(memory.grow returns == -1) - (if - (i32.ne - (memory.grow - (i32.const 1) + let assert_grow_fail = + |alloc_strategy: HeapAllocStrategy, initial_pages: u32, max_pages: u32| { + eprintln!("assert_grow_fail({alloc_strategy:?}, {initial_pages}, {max_pages})"); + + call( + alloc_strategy, + format!( + r#" + (module + {} + (global (export "__heap_base") i32 (i32.const 0)) + (func (export "main") + (param i32 i32) (result i64) + + ;; assert(memory.grow returns == -1) + (if + (i32.ne + (memory.grow + (i32.const 1) + ) + (i32.const -1) + ) + (unreachable) + ) + + (i64.const 0) ) - (i32.const -1) ) - (unreachable) - ) - - (i64.const 0) - ) + "#, + memory(initial_pages, max_pages, import_memory) + ), + instantiation_strategy, + precompile_runtime, ) - "#, - // Initial=1, meaning after heap pages mount the total will be already 1025. - memory(1, None, import_memory) - ), - instantiation_strategy, - precompile_runtime, - ) - .unwrap(); + .unwrap() + }; + + assert_grow_ok(HeapAllocStrategy::Dynamic { maximum_pages: Some(10) }, 1, 10); + assert_grow_ok(HeapAllocStrategy::Dynamic { maximum_pages: Some(10) }, 9, 10); + assert_grow_fail(HeapAllocStrategy::Dynamic { maximum_pages: Some(10) }, 10, 10); + + assert_grow_ok(HeapAllocStrategy::Dynamic { maximum_pages: None }, 1, 10); + assert_grow_ok(HeapAllocStrategy::Dynamic { maximum_pages: None }, 9, 10); + assert_grow_ok(HeapAllocStrategy::Dynamic { maximum_pages: None }, 10, 10); + + assert_grow_fail(HeapAllocStrategy::Static { extra_pages: 10 }, 1, 10); + assert_grow_fail(HeapAllocStrategy::Static { extra_pages: 10 }, 9, 10); + assert_grow_fail(HeapAllocStrategy::Static { extra_pages: 10 }, 10, 10); } // This test takes quite a while to execute in a debug build (over 6 minutes on a TR 3970x) @@ -538,8 +473,7 @@ fn test_instances_without_reuse_are_not_leaked() { deterministic_stack_limit: None, canonicalize_nans: false, parallel_compilation: true, - extra_heap_pages: 2048, - max_memory_size: None, + heap_alloc_strategy: HeapAllocStrategy::Static { extra_pages: 2048 }, }, }, ) @@ -583,6 +517,10 @@ fn test_rustix_version_matches_with_wasmtime() { .unwrap(); if wasmtime_rustix.req != our_rustix.req { - panic!("our version of rustix ({0}) doesn't match wasmtime's ({1}); bump the version in `sc-executor-wasmtime`'s `Cargo.toml' to '{1}' and try again", our_rustix.req, wasmtime_rustix.req); + panic!( + "our version of rustix ({0}) doesn't match wasmtime's ({1}); \ + bump the version in `sc-executor-wasmtime`'s `Cargo.toml' to '{1}' and try again", + our_rustix.req, wasmtime_rustix.req, + ); } } diff --git a/client/finality-grandpa/src/lib.rs b/client/finality-grandpa/src/lib.rs index fca1a8a83..4f72bb052 100644 --- a/client/finality-grandpa/src/lib.rs +++ b/client/finality-grandpa/src/lib.rs @@ -75,7 +75,7 @@ use sp_api::ProvideRuntimeApi; use sp_application_crypto::AppKey; use sp_blockchain::{Error as ClientError, HeaderBackend, HeaderMetadata, Result as ClientResult}; use sp_consensus::SelectChain; -use sp_core::crypto::ByteArray; +use sp_core::{crypto::ByteArray, traits::CallContext}; use sp_finality_grandpa::{ AuthorityList, AuthoritySignature, SetId, CLIENT_LOG_TARGET as LOG_TARGET, }; @@ -479,6 +479,7 @@ where "GrandpaApi_grandpa_authorities", &[], ExecutionStrategy::NativeElseWasm, + CallContext::Offchain, ) .and_then(|call_result| { Decode::decode(&mut &call_result[..]).map_err(|err| { diff --git a/client/rpc-spec-v2/src/chain_head/chain_head.rs b/client/rpc-spec-v2/src/chain_head/chain_head.rs index 4dbdf277e..4fa47dc40 100644 --- a/client/rpc-spec-v2/src/chain_head/chain_head.rs +++ b/client/rpc-spec-v2/src/chain_head/chain_head.rs @@ -52,7 +52,7 @@ use sp_api::CallApiAt; use sp_blockchain::{ Backend as BlockChainBackend, Error as BlockChainError, HeaderBackend, HeaderMetadata, }; -use sp_core::{hexdisplay::HexDisplay, storage::well_known_keys, Bytes}; +use sp_core::{hexdisplay::HexDisplay, storage::well_known_keys, traits::CallContext, Bytes}; use sp_runtime::traits::{Block as BlockT, Header}; use std::{marker::PhantomData, sync::Arc}; @@ -736,6 +736,7 @@ where &function, &call_parameters, client.execution_extensions().strategies().other, + CallContext::Offchain, ) .map(|result| { let result = format!("0x{:?}", HexDisplay::from(&result)); diff --git a/client/rpc/src/state/state_full.rs b/client/rpc/src/state/state_full.rs index efd208539..f26d42484 100644 --- a/client/rpc/src/state/state_full.rs +++ b/client/rpc/src/state/state_full.rs @@ -46,6 +46,7 @@ use sp_core::{ storage::{ ChildInfo, ChildType, PrefixedStorageKey, StorageChangeSet, StorageData, StorageKey, }, + traits::CallContext, Bytes, }; use sp_runtime::traits::Block as BlockT; @@ -207,6 +208,7 @@ where &method, &call_data, self.client.execution_extensions().strategies().other, + CallContext::Offchain, ) .map(Into::into) }) diff --git a/client/service/src/client/call_executor.rs b/client/service/src/client/call_executor.rs index 8942b20bd..82d3e36ac 100644 --- a/client/service/src/client/call_executor.rs +++ b/client/service/src/client/call_executor.rs @@ -21,8 +21,11 @@ use sc_client_api::{ backend, call_executor::CallExecutor, execution_extensions::ExecutionExtensions, HeaderBackend, }; use sc_executor::{RuntimeVersion, RuntimeVersionOf}; -use sp_api::{ExecutionContext, ProofRecorder, StorageTransactionCache}; -use sp_core::traits::{CodeExecutor, RuntimeCode, SpawnNamed}; +use sp_api::{ProofRecorder, StorageTransactionCache}; +use sp_core::{ + traits::{CallContext, CodeExecutor, RuntimeCode, SpawnNamed}, + ExecutionContext, +}; use sp_runtime::{generic::BlockId, traits::Block as BlockT}; use sp_state_machine::{ backend::AsTrieBackend, ExecutionStrategy, Ext, OverlayedChanges, StateMachine, StorageProof, @@ -166,6 +169,7 @@ where method: &str, call_data: &[u8], strategy: ExecutionStrategy, + context: CallContext, ) -> sp_blockchain::Result> { let mut changes = OverlayedChanges::default(); let at_number = @@ -193,6 +197,7 @@ where extensions, &runtime_code, self.spawn_handle.clone(), + context, ) .set_parent_hash(at_hash); @@ -216,6 +221,11 @@ where self.backend.blockchain().expect_block_number_from_id(&BlockId::Hash(at_hash))?; let state = self.backend.state_at(at_hash)?; + let call_context = match context { + ExecutionContext::OffchainCall(_) => CallContext::Offchain, + _ => CallContext::Onchain, + }; + let (execution_manager, extensions) = self.execution_extensions.manager_and_extensions(at_hash, at_number, context); @@ -247,6 +257,7 @@ where extensions, &runtime_code, self.spawn_handle.clone(), + call_context, ) .with_storage_transaction_cache(storage_transaction_cache.as_deref_mut()) .set_parent_hash(at_hash); @@ -262,6 +273,7 @@ where extensions, &runtime_code, self.spawn_handle.clone(), + call_context, ) .with_storage_transaction_cache(storage_transaction_cache.as_deref_mut()) .set_parent_hash(at_hash); diff --git a/client/service/test/src/client/mod.rs b/client/service/test/src/client/mod.rs index b2b029f22..f8988c5a4 100644 --- a/client/service/test/src/client/mod.rs +++ b/client/service/test/src/client/mod.rs @@ -30,7 +30,7 @@ use sc_consensus::{ use sc_service::client::{new_in_mem, Client, LocalCallExecutor}; use sp_api::ProvideRuntimeApi; use sp_consensus::{BlockOrigin, BlockStatus, Error as ConsensusError, SelectChain}; -use sp_core::{testing::TaskExecutor, H256}; +use sp_core::{testing::TaskExecutor, traits::CallContext, H256}; use sp_runtime::{ generic::BlockId, traits::{BlakeTwo256, Block as BlockT, Header as HeaderT}, @@ -114,6 +114,7 @@ fn construct_block( Default::default(), &runtime_code, task_executor.clone() as Box<_>, + CallContext::Onchain, ) .execute(ExecutionStrategy::NativeElseWasm) .unwrap(); @@ -128,6 +129,7 @@ fn construct_block( Default::default(), &runtime_code, task_executor.clone() as Box<_>, + CallContext::Onchain, ) .execute(ExecutionStrategy::NativeElseWasm) .unwrap(); @@ -142,6 +144,7 @@ fn construct_block( Default::default(), &runtime_code, task_executor.clone() as Box<_>, + CallContext::Onchain, ) .execute(ExecutionStrategy::NativeElseWasm) .unwrap(); @@ -213,6 +216,7 @@ fn construct_genesis_should_work_with_native() { Default::default(), &runtime_code, TaskExecutor::new(), + CallContext::Onchain, ) .execute(ExecutionStrategy::NativeElseWasm) .unwrap(); @@ -246,6 +250,7 @@ fn construct_genesis_should_work_with_wasm() { Default::default(), &runtime_code, TaskExecutor::new(), + CallContext::Onchain, ) .execute(ExecutionStrategy::AlwaysWasm) .unwrap(); @@ -279,6 +284,7 @@ fn construct_genesis_with_bad_transaction_should_panic() { Default::default(), &runtime_code, TaskExecutor::new(), + CallContext::Onchain, ) .execute(ExecutionStrategy::NativeElseWasm); assert!(r.is_err()); diff --git a/primitives/core/src/traits.rs b/primitives/core/src/traits.rs index 3fe2a2ed3..0e42ee3ad 100644 --- a/primitives/core/src/traits.rs +++ b/primitives/core/src/traits.rs @@ -24,13 +24,27 @@ use std::{ pub use sp_externalities::{Externalities, ExternalitiesExt}; +/// The context in which a call is done. +/// +/// Depending on the context the executor may chooses different kind of heap sizes for the runtime +/// instance. +#[derive(Clone, Copy, Debug, PartialEq, Eq, Ord, PartialOrd)] +pub enum CallContext { + /// The call is happening in some offchain context. + Offchain, + /// The call is happening in some on-chain context like building or importing a block. + Onchain, +} + /// Code execution engine. pub trait CodeExecutor: Sized + Send + Sync + ReadRuntimeVersion + Clone + 'static { /// Externalities error type. type Error: Display + Debug + Send + Sync + 'static; - /// Call a given method in the runtime. Returns a tuple of the result (either the output data - /// or an execution error) together with a `bool`, which is true if native execution was used. + /// Call a given method in the runtime. + /// + /// Returns a tuple of the result (either the output data or an execution error) together with a + /// `bool`, which is true if native execution was used. fn call( &self, ext: &mut dyn Externalities, @@ -38,6 +52,7 @@ pub trait CodeExecutor: Sized + Send + Sync + ReadRuntimeVersion + Clone + 'stat method: &str, data: &[u8], use_native: bool, + context: CallContext, ) -> (Result, Self::Error>, bool); } diff --git a/primitives/state-machine/src/lib.rs b/primitives/state-machine/src/lib.rs index 538c63837..5fb300e12 100644 --- a/primitives/state-machine/src/lib.rs +++ b/primitives/state-machine/src/lib.rs @@ -163,7 +163,7 @@ mod execution { use sp_core::{ hexdisplay::HexDisplay, storage::{ChildInfo, ChildType, PrefixedStorageKey}, - traits::{CodeExecutor, ReadRuntimeVersionExt, RuntimeCode, SpawnNamed}, + traits::{CallContext, CodeExecutor, ReadRuntimeVersionExt, RuntimeCode, SpawnNamed}, }; use sp_externalities::Extensions; use std::{ @@ -295,6 +295,7 @@ mod execution { /// /// Used for logging. parent_hash: Option, + context: CallContext, } impl<'a, B, H, Exec> Drop for StateMachine<'a, B, H, Exec> @@ -324,6 +325,7 @@ mod execution { mut extensions: Extensions, runtime_code: &'a RuntimeCode, spawn_handle: impl SpawnNamed + Send + 'static, + context: CallContext, ) -> Self { extensions.register(ReadRuntimeVersionExt::new(exec.clone())); extensions.register(sp_core::traits::TaskExecutorExt::new(spawn_handle)); @@ -339,6 +341,7 @@ mod execution { runtime_code, stats: StateMachineStats::default(), parent_hash: None, + context, } } @@ -408,6 +411,7 @@ mod execution { self.method, self.call_data, use_native, + self.context, ); self.overlay @@ -574,6 +578,7 @@ mod execution { extensions, runtime_code, spawn_handle, + CallContext::Offchain, ) .execute_using_consensus_failure_handler::<_>(always_wasm())?; @@ -638,6 +643,7 @@ mod execution { Extensions::default(), runtime_code, spawn_handle, + CallContext::Offchain, ) .execute_using_consensus_failure_handler(always_untrusted_wasm()) } @@ -1318,7 +1324,7 @@ mod tests { map, storage::{ChildInfo, StateVersion}, testing::TaskExecutor, - traits::{CodeExecutor, Externalities, RuntimeCode}, + traits::{CallContext, CodeExecutor, Externalities, RuntimeCode}, }; use sp_runtime::traits::BlakeTwo256; use sp_trie::trie_types::{TrieDBMutBuilderV0, TrieDBMutBuilderV1}; @@ -1341,6 +1347,7 @@ mod tests { _method: &str, _data: &[u8], use_native: bool, + _: CallContext, ) -> (CallResult, bool) { let using_native = use_native && self.native_available; match (using_native, self.native_succeeds, self.fallback_succeeds) { @@ -1388,6 +1395,7 @@ mod tests { Default::default(), &wasm_code, TaskExecutor::new(), + CallContext::Offchain, ); assert_eq!(state_machine.execute(ExecutionStrategy::NativeWhenPossible).unwrap(), vec![66]); @@ -1416,6 +1424,7 @@ mod tests { Default::default(), &wasm_code, TaskExecutor::new(), + CallContext::Offchain, ); assert_eq!(state_machine.execute(ExecutionStrategy::NativeElseWasm).unwrap(), vec![66]); @@ -1445,6 +1454,7 @@ mod tests { Default::default(), &wasm_code, TaskExecutor::new(), + CallContext::Offchain, ); assert!(state_machine diff --git a/primitives/wasm-interface/Cargo.toml b/primitives/wasm-interface/Cargo.toml index 65c4ae42f..306a47e6d 100644 --- a/primitives/wasm-interface/Cargo.toml +++ b/primitives/wasm-interface/Cargo.toml @@ -17,7 +17,7 @@ targets = ["x86_64-unknown-linux-gnu"] codec = { package = "parity-scale-codec", version = "3.2.2", default-features = false, features = ["derive"] } impl-trait-for-tuples = "0.2.2" log = { version = "0.4.17", optional = true } -wasmi = { version = "0.13", optional = true } +wasmi = { version = "0.13.2", optional = true } wasmtime = { version = "6.0.0", default-features = false, optional = true } anyhow = { version = "1.0.68", optional = true } sp-std = { version = "5.0.0", default-features = false, path = "../std" } diff --git a/test-utils/runtime/src/lib.rs b/test-utils/runtime/src/lib.rs index 1fa3c7a00..720b0a1e0 100644 --- a/test-utils/runtime/src/lib.rs +++ b/test-utils/runtime/src/lib.rs @@ -1352,7 +1352,7 @@ mod tests { use sc_block_builder::BlockBuilderProvider; use sp_api::ProvideRuntimeApi; use sp_consensus::BlockOrigin; - use sp_core::storage::well_known_keys::HEAP_PAGES; + use sp_core::{storage::well_known_keys::HEAP_PAGES, ExecutionContext}; use sp_state_machine::ExecutionStrategy; use substrate_test_runtime_client::{ prelude::*, runtime::TestAPI, DefaultTestClientBuilderExt, TestClientBuilder, @@ -1371,7 +1371,12 @@ mod tests { // Try to allocate 1024k of memory on heap. This is going to fail since it is twice larger // than the heap. - let ret = client.runtime_api().vec_with_capacity(best_hash, 1048576); + let ret = client.runtime_api().vec_with_capacity_with_context( + best_hash, + // Use `BlockImport` to ensure we use the on chain heap pages as configured above. + ExecutionContext::Importing, + 1048576, + ); assert!(ret.is_err()); // Create a block that sets the `:heap_pages` to 32 pages of memory which corresponds to diff --git a/test-utils/runtime/src/system.rs b/test-utils/runtime/src/system.rs index 76ddf11a8..657e3c90f 100644 --- a/test-utils/runtime/src/system.rs +++ b/test-utils/runtime/src/system.rs @@ -354,7 +354,7 @@ mod tests { use sc_executor::{NativeElseWasmExecutor, WasmExecutionMethod}; use sp_core::{ map, - traits::{CodeExecutor, RuntimeCode}, + traits::{CallContext, CodeExecutor, RuntimeCode}, }; use sp_io::{hashing::twox_128, TestExternalities}; use substrate_test_runtime_client::{AccountKeyring, Sr25519Keyring}; @@ -438,7 +438,14 @@ mod tests { }; executor() - .call(&mut ext, &runtime_code, "Core_execute_block", &b.encode(), false) + .call( + &mut ext, + &runtime_code, + "Core_execute_block", + &b.encode(), + false, + CallContext::Offchain, + ) .0 .unwrap(); }) @@ -540,7 +547,14 @@ mod tests { }; executor() - .call(&mut ext, &runtime_code, "Core_execute_block", &b.encode(), false) + .call( + &mut ext, + &runtime_code, + "Core_execute_block", + &b.encode(), + false, + CallContext::Offchain, + ) .0 .unwrap(); }) diff --git a/utils/frame/benchmarking-cli/src/pallet/command.rs b/utils/frame/benchmarking-cli/src/pallet/command.rs index 3f0e9298c..60f078142 100644 --- a/utils/frame/benchmarking-cli/src/pallet/command.rs +++ b/utils/frame/benchmarking-cli/src/pallet/command.rs @@ -30,9 +30,12 @@ use sc_client_db::BenchmarkingState; use sc_executor::NativeElseWasmExecutor; use sc_service::{Configuration, NativeExecutionDispatch}; use serde::Serialize; -use sp_core::offchain::{ - testing::{TestOffchainExt, TestTransactionPoolExt}, - OffchainDbExt, OffchainWorkerExt, TransactionPoolExt, +use sp_core::{ + offchain::{ + testing::{TestOffchainExt, TestTransactionPoolExt}, + OffchainDbExt, OffchainWorkerExt, TransactionPoolExt, + }, + traits::CallContext, }; use sp_externalities::Extensions; use sp_keystore::{testing::KeyStore, KeystoreExt, SyncCryptoStorePtr}; @@ -235,6 +238,7 @@ impl PalletCmd { extensions(), &sp_state_machine::backend::BackendRuntimeCode::new(state).runtime_code()?, sp_core::testing::TaskExecutor::new(), + CallContext::Offchain, ) .execute(strategy.into()) .map_err(|e| format!("{}: {}", ERROR_METADATA_NOT_FOUND, e))?; @@ -372,6 +376,7 @@ impl PalletCmd { &sp_state_machine::backend::BackendRuntimeCode::new(state) .runtime_code()?, sp_core::testing::TaskExecutor::new(), + CallContext::Offchain, ) .execute(strategy.into()) .map_err(|e| { @@ -412,6 +417,7 @@ impl PalletCmd { &sp_state_machine::backend::BackendRuntimeCode::new(state) .runtime_code()?, sp_core::testing::TaskExecutor::new(), + CallContext::Offchain, ) .execute(strategy.into()) .map_err(|e| format!("Error executing runtime benchmark: {}", e))?; @@ -444,6 +450,7 @@ impl PalletCmd { &sp_state_machine::backend::BackendRuntimeCode::new(state) .runtime_code()?, sp_core::testing::TaskExecutor::new(), + CallContext::Offchain, ) .execute(strategy.into()) .map_err(|e| format!("Error executing runtime benchmark: {}", e))?; diff --git a/utils/frame/try-runtime/cli/src/lib.rs b/utils/frame/try-runtime/cli/src/lib.rs index 04459ab59..6fb4b4439 100644 --- a/utils/frame/try-runtime/cli/src/lib.rs +++ b/utils/frame/try-runtime/cli/src/lib.rs @@ -378,7 +378,7 @@ use sp_core::{ }, storage::well_known_keys, testing::TaskExecutor, - traits::{ReadRuntimeVersion, TaskExecutorExt}, + traits::{CallContext, ReadRuntimeVersion, TaskExecutorExt}, twox_128, H256, }; use sp_externalities::Extensions; @@ -877,6 +877,7 @@ pub(crate) fn state_machine_call( extensions, &sp_state_machine::backend::BackendRuntimeCode::new(&ext.backend).runtime_code()?, sp_core::testing::TaskExecutor::new(), + CallContext::Offchain, ) .execute(sp_state_machine::ExecutionStrategy::AlwaysWasm) .map_err(|e| format!("failed to execute '{}': {}", method, e)) @@ -916,6 +917,7 @@ pub(crate) fn state_machine_call_with_proof Date: Fri, 24 Feb 2023 16:02:30 +0100 Subject: [PATCH 161/558] Automatically format ci pipeline specs with prettier (#13441) * [ci] Deduplicate variables: sections in pipeline specs The prettier yaml parser doesn't like these. * [ci] provide git clean filter to format pipeline specs * [ci] Reformat pipeline specs with prettier --- .gitattributes | 2 + .gitlab-ci.yml | 214 ++++++++++----------- scripts/ci/gitlab/pipeline/build.yml | 88 ++++----- scripts/ci/gitlab/pipeline/check.yml | 38 ++-- scripts/ci/gitlab/pipeline/publish.yml | 181 +++++++++--------- scripts/ci/gitlab/pipeline/test.yml | 230 +++++++++++------------ scripts/ci/gitlab/pipeline/zombienet.yml | 33 ++-- scripts/ci/gitlab/prettier.sh | 6 + 8 files changed, 396 insertions(+), 396 deletions(-) create mode 100755 scripts/ci/gitlab/prettier.sh diff --git a/.gitattributes b/.gitattributes index 9bd26362b..1762f1a04 100644 --- a/.gitattributes +++ b/.gitattributes @@ -1 +1,3 @@ Cargo.lock linguist-generated=true +/.gitlab-ci.yml filter=ci-prettier +/scripts/ci/gitlab/pipeline/*.yml filter=ci-prettier diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index ab3fd84d6..12a1ff825 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -43,57 +43,57 @@ workflow: - if: $CI_COMMIT_BRANCH variables: - GIT_STRATEGY: fetch - GIT_DEPTH: 100 - CARGO_INCREMENTAL: 0 - DOCKER_OS: "debian:stretch" - ARCH: "x86_64" - CI_IMAGE: "paritytech/ci-linux:production" - BUILDAH_IMAGE: "quay.io/buildah/stable:v1.27" - RUSTY_CACHIER_SINGLE_BRANCH: master + GIT_STRATEGY: fetch + GIT_DEPTH: 100 + CARGO_INCREMENTAL: 0 + DOCKER_OS: "debian:stretch" + ARCH: "x86_64" + CI_IMAGE: "paritytech/ci-linux:production" + BUILDAH_IMAGE: "quay.io/buildah/stable:v1.27" + RUSTY_CACHIER_SINGLE_BRANCH: master RUSTY_CACHIER_DONT_OPERATE_ON_MAIN_BRANCH: "true" RUSTY_CACHIER_COMPRESSION_METHOD: zstd - NEXTEST_FAILURE_OUTPUT: immediate-final - NEXTEST_SUCCESS_OUTPUT: final - ZOMBIENET_IMAGE: "docker.io/paritytech/zombienet:v1.3.34" + NEXTEST_FAILURE_OUTPUT: immediate-final + NEXTEST_SUCCESS_OUTPUT: final + ZOMBIENET_IMAGE: "docker.io/paritytech/zombienet:v1.3.34" -.shared-default: &shared-default +.shared-default: &shared-default retry: max: 2 when: - runner_system_failure - unknown_failure - api_failure - cache: {} + cache: {} .default-pipeline-definitions: default: - <<: *shared-default - interruptible: true + <<: *shared-default + interruptible: true .crate-publishing-pipeline-definitions: default: - <<: *shared-default + <<: *shared-default # The crate-publishing pipeline defaults to `interruptible: false` so that we'll be able to # reach and run the publishing jobs despite the "Auto-cancel redundant pipelines" CI setting. # The setting is relevant because the crate-publishing pipeline runs on `master`, thus future # pipelines on `master` (e.g. created for new commits or other schedules) might unintendedly # cancel the publishing jobs or its dependencies before we get to actually publish the crates. - interruptible: false + interruptible: false .collect-artifacts: artifacts: - name: "${CI_JOB_NAME}_${CI_COMMIT_REF_NAME}" - when: on_success - expire_in: 7 days + name: "${CI_JOB_NAME}_${CI_COMMIT_REF_NAME}" + when: on_success + expire_in: 7 days paths: - artifacts/ .collect-artifacts-short: artifacts: - name: "${CI_JOB_NAME}_${CI_COMMIT_REF_NAME}" - when: on_success - expire_in: 3 hours + name: "${CI_JOB_NAME}_${CI_COMMIT_REF_NAME}" + when: on_success + expire_in: 3 hours paths: - artifacts/ @@ -111,7 +111,7 @@ variables: - if echo "$CI_DISABLED_JOBS" | grep -xF "$CI_JOB_NAME"; then echo "The job has been cancelled in CI settings"; exit 0; fi .kubernetes-env: - image: "${CI_IMAGE}" + image: "${CI_IMAGE}" before_script: - !reference [.job-switcher, before_script] - !reference [.prepare-env, before_script] @@ -139,7 +139,7 @@ variables: dotenv: pipeline-stopper.env .docker-env: - image: "${CI_IMAGE}" + image: "${CI_IMAGE}" before_script: - !reference [.job-switcher, before_script] - !reference [.prepare-env, before_script] @@ -167,8 +167,8 @@ variables: - if: $CI_PIPELINE_SOURCE == "web" - if: $CI_PIPELINE_SOURCE == "schedule" - if: $CI_COMMIT_REF_NAME == "master" - - if: $CI_COMMIT_REF_NAME =~ /^[0-9]+$/ # PRs - - if: $CI_COMMIT_REF_NAME =~ /^v[0-9]+\.[0-9]+.*$/ # i.e. v1.0, v2.1rc1 + - if: $CI_COMMIT_REF_NAME =~ /^[0-9]+$/ # PRs + - if: $CI_COMMIT_REF_NAME =~ /^v[0-9]+\.[0-9]+.*$/ # i.e. v1.0, v2.1rc1 # handle the specific case where benches could store incorrect bench data because of the downstream staging runs # exclude cargo-check-benches from such runs @@ -179,8 +179,8 @@ variables: - if: $CI_PIPELINE_SOURCE == "web" - if: $CI_PIPELINE_SOURCE == "schedule" - if: $CI_COMMIT_REF_NAME == "master" - - if: $CI_COMMIT_REF_NAME =~ /^[0-9]+$/ # PRs - - if: $CI_COMMIT_REF_NAME =~ /^v[0-9]+\.[0-9]+.*$/ # i.e. v1.0, v2.1rc1 + - if: $CI_COMMIT_REF_NAME =~ /^[0-9]+$/ # PRs + - if: $CI_COMMIT_REF_NAME =~ /^v[0-9]+\.[0-9]+.*$/ # i.e. v1.0, v2.1rc1 .test-refs-no-trigger: rules: @@ -189,8 +189,8 @@ variables: - if: $CI_PIPELINE_SOURCE == "web" - if: $CI_PIPELINE_SOURCE == "schedule" - if: $CI_COMMIT_REF_NAME == "master" - - if: $CI_COMMIT_REF_NAME =~ /^[0-9]+$/ # PRs - - if: $CI_COMMIT_REF_NAME =~ /^v[0-9]+\.[0-9]+.*$/ # i.e. v1.0, v2.1rc1 + - if: $CI_COMMIT_REF_NAME =~ /^[0-9]+$/ # PRs + - if: $CI_COMMIT_REF_NAME =~ /^v[0-9]+\.[0-9]+.*$/ # i.e. v1.0, v2.1rc1 - if: $CI_COMMIT_REF_NAME =~ /^ci-release-.*$/ .test-refs-no-trigger-prs-only: @@ -199,7 +199,7 @@ variables: when: never - if: $CI_PIPELINE_SOURCE == "web" - if: $CI_PIPELINE_SOURCE == "schedule" - - if: $CI_COMMIT_REF_NAME =~ /^[0-9]+$/ # PRs + - if: $CI_COMMIT_REF_NAME =~ /^[0-9]+$/ # PRs .publish-refs: rules: @@ -208,7 +208,7 @@ variables: - if: $CI_PIPELINE_SOURCE == "web" - if: $CI_PIPELINE_SOURCE == "schedule" - if: $CI_COMMIT_REF_NAME == "master" - - if: $CI_COMMIT_REF_NAME =~ /^v[0-9]+\.[0-9]+.*$/ # i.e. v1.0, v2.1rc1 + - if: $CI_COMMIT_REF_NAME =~ /^v[0-9]+\.[0-9]+.*$/ # i.e. v1.0, v2.1rc1 .build-refs: # publish-refs + PRs @@ -218,8 +218,8 @@ variables: - if: $CI_PIPELINE_SOURCE == "web" - if: $CI_PIPELINE_SOURCE == "schedule" - if: $CI_COMMIT_REF_NAME == "master" - - if: $CI_COMMIT_REF_NAME =~ /^v[0-9]+\.[0-9]+.*$/ # i.e. v1.0, v2.1rc1 - - if: $CI_COMMIT_REF_NAME =~ /^[0-9]+$/ # PRs + - if: $CI_COMMIT_REF_NAME =~ /^v[0-9]+\.[0-9]+.*$/ # i.e. v1.0, v2.1rc1 + - if: $CI_COMMIT_REF_NAME =~ /^[0-9]+$/ # PRs .zombienet-refs: extends: .build-refs @@ -231,12 +231,12 @@ variables: .crates-publishing-variables: variables: - CRATESIO_CRATES_OWNER: parity-crate-owner - REPO: substrate - REPO_OWNER: paritytech + CRATESIO_CRATES_OWNER: parity-crate-owner + REPO: substrate + REPO_OWNER: paritytech .crates-publishing-pipeline: - extends: .crates-publishing-variables + extends: .crates-publishing-variables rules: - if: $CI_PIPELINE_SOURCE == "schedule" && $CI_COMMIT_REF_NAME == "master" && $PIPELINE == "automatic-crate-publishing" @@ -247,40 +247,40 @@ variables: # collect artifacts even on failure so that we know how the crates were generated (they'll be # generated to the artifacts folder according to SPUB_TMP further down) artifacts: - name: "${CI_JOB_NAME}_${CI_COMMIT_REF_NAME}" - when: always - expire_in: 7 days + name: "${CI_JOB_NAME}_${CI_COMMIT_REF_NAME}" + when: always + expire_in: 7 days paths: - artifacts/ variables: - SPUB_TMP: artifacts + SPUB_TMP: artifacts #### stage: .pre skip-if-draft: - extends: .kubernetes-env + extends: .kubernetes-env variables: - CI_IMAGE: "paritytech/tools:latest" - stage: .pre + CI_IMAGE: "paritytech/tools:latest" + stage: .pre rules: - - if: $CI_COMMIT_REF_NAME =~ /^[0-9]+$/ # PRs + - if: $CI_COMMIT_REF_NAME =~ /^[0-9]+$/ # PRs script: - - echo "Commit message is ${CI_COMMIT_MESSAGE}" - - echo "Ref is ${CI_COMMIT_REF_NAME}" - - echo "pipeline source is ${CI_PIPELINE_SOURCE}" - - ./scripts/ci/gitlab/skip_if_draft.sh - allow_failure: true + - echo "Commit message is ${CI_COMMIT_MESSAGE}" + - echo "Ref is ${CI_COMMIT_REF_NAME}" + - echo "pipeline source is ${CI_PIPELINE_SOURCE}" + - ./scripts/ci/gitlab/skip_if_draft.sh + allow_failure: true check-crates-publishing-pipeline: - stage: .pre + stage: .pre extends: - .kubernetes-env - .crates-publishing-pipeline script: - git clone - --depth 1 - --branch "$RELENG_SCRIPTS_BRANCH" - https://github.com/paritytech/releng-scripts.git + --depth 1 + --branch "$RELENG_SCRIPTS_BRANCH" + https://github.com/paritytech/releng-scripts.git - ONLY_CHECK_PIPELINE=true ./releng-scripts/publish-crates include: @@ -314,17 +314,17 @@ include: #### stage: deploy deploy-prometheus-alerting-rules: - stage: deploy + stage: deploy needs: - - job: test-prometheus-alerting-rules - artifacts: false - allow_failure: true + - job: test-prometheus-alerting-rules + artifacts: false + allow_failure: true trigger: - project: parity/infrastructure/cloud-infra + project: parity/infrastructure/cloud-infra variables: - SUBSTRATE_CI_COMMIT_NAME: "${CI_COMMIT_REF_NAME}" - SUBSTRATE_CI_COMMIT_REF: "${CI_COMMIT_SHORT_SHA}" - UPSTREAM_TRIGGER_PROJECT: "${CI_PROJECT_PATH}" + SUBSTRATE_CI_COMMIT_NAME: "${CI_COMMIT_REF_NAME}" + SUBSTRATE_CI_COMMIT_REF: "${CI_COMMIT_SHORT_SHA}" + UPSTREAM_TRIGGER_PROJECT: "${CI_PROJECT_PATH}" rules: - if: $CI_PIPELINE_SOURCE == "pipeline" when: never @@ -339,12 +339,12 @@ deploy-prometheus-alerting-rules: # This info is later used for the cache distribution and an overlay creation. # Note that we don't use any .rusty-cachier references as we assume that a pipeline has reached this stage with working rusty-cachier. rusty-cachier-notify: - stage: notify - extends: .kubernetes-env + stage: notify + extends: .kubernetes-env variables: - CI_IMAGE: paritytech/rusty-cachier-env:latest - GIT_STRATEGY: none - dependencies: [] + CI_IMAGE: paritytech/rusty-cachier-env:latest + GIT_STRATEGY: none + dependencies: [] script: - curl -s https://gitlab-ci-token:${CI_JOB_TOKEN}@gitlab.parity.io/parity/infrastructure/ci_cd/rusty-cachier/client/-/raw/release/util/install.sh | bash - rusty-cachier cache notify @@ -355,83 +355,83 @@ rusty-cachier-notify: # In a DAG, every jobs chain is executed independently of others. The `fail_fast` principle suggests # to fail the pipeline as soon as possible to shorten the feedback loop. .cancel-pipeline-template: - stage: .post + stage: .post rules: - - if: $CI_COMMIT_REF_NAME =~ /^[0-9]+$/ # PRs + - if: $CI_COMMIT_REF_NAME =~ /^[0-9]+$/ # PRs when: on_failure variables: - PROJECT_ID: "${CI_PROJECT_ID}" - PROJECT_NAME: "${CI_PROJECT_NAME}" - PIPELINE_ID: "${CI_PIPELINE_ID}" - FAILED_JOB_URL: "${FAILED_JOB_URL}" - FAILED_JOB_NAME: "${FAILED_JOB_NAME}" - PR_NUM: "${PR_NUM}" + PROJECT_ID: "${CI_PROJECT_ID}" + PROJECT_NAME: "${CI_PROJECT_NAME}" + PIPELINE_ID: "${CI_PIPELINE_ID}" + FAILED_JOB_URL: "${FAILED_JOB_URL}" + FAILED_JOB_NAME: "${FAILED_JOB_NAME}" + PR_NUM: "${PR_NUM}" trigger: - project: "parity/infrastructure/ci_cd/pipeline-stopper" + project: "parity/infrastructure/ci_cd/pipeline-stopper" remove-cancel-pipeline-message: stage: .post rules: - - if: $CI_COMMIT_REF_NAME =~ /^[0-9]+$/ # PRs + - if: $CI_COMMIT_REF_NAME =~ /^[0-9]+$/ # PRs variables: - PROJECT_ID: "${CI_PROJECT_ID}" - PROJECT_NAME: "${CI_PROJECT_NAME}" - PIPELINE_ID: "${CI_PIPELINE_ID}" - FAILED_JOB_URL: "https://gitlab.com" - FAILED_JOB_NAME: "nope" - PR_NUM: "${CI_COMMIT_REF_NAME}" + PROJECT_ID: "${CI_PROJECT_ID}" + PROJECT_NAME: "${CI_PROJECT_NAME}" + PIPELINE_ID: "${CI_PIPELINE_ID}" + FAILED_JOB_URL: "https://gitlab.com" + FAILED_JOB_NAME: "nope" + PR_NUM: "${CI_COMMIT_REF_NAME}" trigger: - project: "parity/infrastructure/ci_cd/pipeline-stopper" - branch: "as-improve" + project: "parity/infrastructure/ci_cd/pipeline-stopper" + branch: "as-improve" # need to copy jobs this way because otherwise gitlab will wait # for all 3 jobs to finish instead of cancelling if one fails cancel-pipeline-test-linux-stable1: - extends: .cancel-pipeline-template + extends: .cancel-pipeline-template needs: - - job: "test-linux-stable 1/3" + - job: "test-linux-stable 1/3" cancel-pipeline-test-linux-stable2: - extends: .cancel-pipeline-template + extends: .cancel-pipeline-template needs: - - job: "test-linux-stable 2/3" + - job: "test-linux-stable 2/3" cancel-pipeline-test-linux-stable3: - extends: .cancel-pipeline-template + extends: .cancel-pipeline-template needs: - - job: "test-linux-stable 3/3" + - job: "test-linux-stable 3/3" cancel-pipeline-cargo-check-benches1: - extends: .cancel-pipeline-template + extends: .cancel-pipeline-template needs: - - job: "cargo-check-benches 1/2" + - job: "cargo-check-benches 1/2" cancel-pipeline-cargo-check-benches2: - extends: .cancel-pipeline-template + extends: .cancel-pipeline-template needs: - - job: "cargo-check-benches 2/2" + - job: "cargo-check-benches 2/2" cancel-pipeline-test-linux-stable-int: - extends: .cancel-pipeline-template + extends: .cancel-pipeline-template needs: - - job: test-linux-stable-int + - job: test-linux-stable-int cancel-pipeline-cargo-check-each-crate-1: - extends: .cancel-pipeline-template + extends: .cancel-pipeline-template needs: - - job: "cargo-check-each-crate 1/2" + - job: "cargo-check-each-crate 1/2" cancel-pipeline-cargo-check-each-crate-2: - extends: .cancel-pipeline-template + extends: .cancel-pipeline-template needs: - - job: "cargo-check-each-crate 2/2" + - job: "cargo-check-each-crate 2/2" cancel-pipeline-cargo-check-each-crate-macos: - extends: .cancel-pipeline-template + extends: .cancel-pipeline-template needs: - - job: cargo-check-each-crate-macos + - job: cargo-check-each-crate-macos cancel-pipeline-check-tracing: - extends: .cancel-pipeline-template + extends: .cancel-pipeline-template needs: - - job: check-tracing + - job: check-tracing diff --git a/scripts/ci/gitlab/pipeline/build.yml b/scripts/ci/gitlab/pipeline/build.yml index ba529569d..02f25a02a 100644 --- a/scripts/ci/gitlab/pipeline/build.yml +++ b/scripts/ci/gitlab/pipeline/build.yml @@ -4,63 +4,63 @@ # PIPELINE_SCRIPTS_TAG can be found in the project variables .check-dependent-project: - stage: build + stage: build # DAG: this is artificial dependency needs: - - job: cargo-clippy - artifacts: false + - job: cargo-clippy + artifacts: false extends: - .docker-env - .test-refs-no-trigger-prs-only script: - git clone - --depth=1 - --branch="$PIPELINE_SCRIPTS_TAG" - https://github.com/paritytech/pipeline-scripts + --depth=1 + --branch="$PIPELINE_SCRIPTS_TAG" + https://github.com/paritytech/pipeline-scripts - ./pipeline-scripts/check_dependent_project.sh - --org paritytech - --dependent-repo "$DEPENDENT_REPO" - --github-api-token "$GITHUB_PR_TOKEN" - --extra-dependencies "$EXTRA_DEPENDENCIES" - --companion-overrides "$COMPANION_OVERRIDES" + --org paritytech + --dependent-repo "$DEPENDENT_REPO" + --github-api-token "$GITHUB_PR_TOKEN" + --extra-dependencies "$EXTRA_DEPENDENCIES" + --companion-overrides "$COMPANION_OVERRIDES" # Individual jobs are set up for each dependent project so that they can be ran in parallel. # Arguably we could generate a job for each companion in the PR's description using Gitlab's # parent-child pipelines but that's more complicated. check-dependent-polkadot: - extends: .check-dependent-project + extends: .check-dependent-project variables: - DEPENDENT_REPO: polkadot + DEPENDENT_REPO: polkadot COMPANION_OVERRIDES: | substrate: polkadot-v* polkadot: release-v* rules: - - if: $CI_COMMIT_REF_NAME =~ /^[0-9]+$/ #PRs + - if: $CI_COMMIT_REF_NAME =~ /^[0-9]+$/ #PRs check-dependent-cumulus: - extends: .check-dependent-project + extends: .check-dependent-project variables: - DEPENDENT_REPO: cumulus - EXTRA_DEPENDENCIES: polkadot + DEPENDENT_REPO: cumulus + EXTRA_DEPENDENCIES: polkadot COMPANION_OVERRIDES: | substrate: polkadot-v* polkadot: release-v* rules: - - if: $CI_COMMIT_REF_NAME =~ /^[0-9]+$/ #PRs + - if: $CI_COMMIT_REF_NAME =~ /^[0-9]+$/ #PRs build-linux-substrate: - stage: build + stage: build extends: - .collect-artifacts - .docker-env - .build-refs variables: # this variable gets overriden by "rusty-cachier environment inject", use the value as default - CARGO_TARGET_DIR: "$CI_PROJECT_DIR/target" + CARGO_TARGET_DIR: "$CI_PROJECT_DIR/target" needs: - - job: test-linux-stable - artifacts: false + - job: test-linux-stable + artifacts: false before_script: - !reference [.job-switcher, before_script] - mkdir -p ./artifacts/substrate/ @@ -74,10 +74,10 @@ build-linux-substrate: - mv $CARGO_TARGET_DIR/release/substrate ./artifacts/substrate/. - echo -n "Substrate version = " - if [ "${CI_COMMIT_TAG}" ]; then - echo "${CI_COMMIT_TAG}" | tee ./artifacts/substrate/VERSION; + echo "${CI_COMMIT_TAG}" | tee ./artifacts/substrate/VERSION; else - ./artifacts/substrate/substrate --version | - cut -d ' ' -f 2 | tee ./artifacts/substrate/VERSION; + ./artifacts/substrate/substrate --version | + cut -d ' ' -f 2 | tee ./artifacts/substrate/VERSION; fi - sha256sum ./artifacts/substrate/substrate | tee ./artifacts/substrate/substrate.sha256 - cp -r ./scripts/ci/docker/substrate.Dockerfile ./artifacts/substrate/ @@ -86,14 +86,14 @@ build-linux-substrate: - rusty-cachier cache upload .build-subkey: - stage: build + stage: build extends: - .collect-artifacts - .docker-env - .publish-refs variables: # this variable gets overriden by "rusty-cachier environment inject", use the value as default - CARGO_TARGET_DIR: "$CI_PROJECT_DIR/target" + CARGO_TARGET_DIR: "$CI_PROJECT_DIR/target" before_script: - !reference [.job-switcher, before_script] - mkdir -p ./artifacts/subkey @@ -106,17 +106,17 @@ build-linux-substrate: - mv $CARGO_TARGET_DIR/release/subkey ./artifacts/subkey/. - echo -n "Subkey version = " - ./artifacts/subkey/subkey --version | - sed -n -E 's/^subkey ([0-9.]+.*)/\1/p' | - tee ./artifacts/subkey/VERSION; + sed -n -E 's/^subkey ([0-9.]+.*)/\1/p' | + tee ./artifacts/subkey/VERSION; - sha256sum ./artifacts/subkey/subkey | tee ./artifacts/subkey/subkey.sha256 - cp -r ./scripts/ci/docker/subkey.Dockerfile ./artifacts/subkey/ - rusty-cachier cache upload build-subkey-linux: - extends: .build-subkey + extends: .build-subkey build-subkey-macos: - extends: .build-subkey + extends: .build-subkey # duplicating before_script & script sections from .build-subkey hidden job # to overwrite rusty-cachier integration as it doesn't work on macos before_script: @@ -129,8 +129,8 @@ build-subkey-macos: - mv ./target/release/subkey ./artifacts/subkey/. - echo -n "Subkey version = " - ./artifacts/subkey/subkey --version | - sed -n -E 's/^subkey ([0-9.]+.*)/\1/p' | - tee ./artifacts/subkey/VERSION; + sed -n -E 's/^subkey ([0-9.]+.*)/\1/p' | + tee ./artifacts/subkey/VERSION; - sha256sum ./artifacts/subkey/subkey | tee ./artifacts/subkey/subkey.sha256 - cp -r ./scripts/ci/docker/subkey.Dockerfile ./artifacts/subkey/ after_script: [""] @@ -138,26 +138,26 @@ build-subkey-macos: - osx build-rustdoc: - stage: build + stage: build extends: - .docker-env - .test-refs variables: - SKIP_WASM_BUILD: 1 - DOC_INDEX_PAGE: "sc_service/index.html" # default redirected page - RUSTY_CACHIER_TOOLCHAIN: nightly + SKIP_WASM_BUILD: 1 + DOC_INDEX_PAGE: "sc_service/index.html" # default redirected page + RUSTY_CACHIER_TOOLCHAIN: nightly # this variable gets overriden by "rusty-cachier environment inject", use the value as default - CARGO_TARGET_DIR: "$CI_PROJECT_DIR/target" + CARGO_TARGET_DIR: "$CI_PROJECT_DIR/target" artifacts: - name: "${CI_JOB_NAME}_${CI_COMMIT_REF_NAME}-doc" - when: on_success - expire_in: 7 days + name: "${CI_JOB_NAME}_${CI_COMMIT_REF_NAME}-doc" + when: on_success + expire_in: 7 days paths: - - ./crate-docs/ + - ./crate-docs/ # DAG: this is artificial dependency needs: - - job: cargo-clippy - artifacts: false + - job: cargo-clippy + artifacts: false script: - rusty-cachier snapshot create - time cargo +nightly doc --locked --workspace --all-features --verbose --no-deps diff --git a/scripts/ci/gitlab/pipeline/check.yml b/scripts/ci/gitlab/pipeline/check.yml index 55f006150..a29f31d4a 100644 --- a/scripts/ci/gitlab/pipeline/check.yml +++ b/scripts/ci/gitlab/pipeline/check.yml @@ -2,56 +2,56 @@ # Here are all jobs that are executed during "check" stage check-runtime: - stage: check + stage: check extends: - .kubernetes-env - .test-refs-no-trigger-prs-only variables: - CI_IMAGE: "paritytech/tools:latest" - GITLAB_API: "https://gitlab.parity.io/api/v4" - GITHUB_API_PROJECT: "parity%2Finfrastructure%2Fgithub-api" + CI_IMAGE: "paritytech/tools:latest" + GITLAB_API: "https://gitlab.parity.io/api/v4" + GITHUB_API_PROJECT: "parity%2Finfrastructure%2Fgithub-api" script: - ./scripts/ci/gitlab/check_runtime.sh - allow_failure: true + allow_failure: true check-signed-tag: - stage: check - extends: .kubernetes-env + stage: check + extends: .kubernetes-env variables: - CI_IMAGE: "paritytech/tools:latest" + CI_IMAGE: "paritytech/tools:latest" rules: - if: $CI_COMMIT_REF_NAME =~ /^ci-release-.*$/ - - if: $CI_COMMIT_REF_NAME =~ /^v[0-9]+\.[0-9]+.*$/ # i.e. v1.0, v2.1rc1 + - if: $CI_COMMIT_REF_NAME =~ /^v[0-9]+\.[0-9]+.*$/ # i.e. v1.0, v2.1rc1 script: - ./scripts/ci/gitlab/check_signed.sh test-dependency-rules: - stage: check + stage: check extends: - .kubernetes-env - .test-refs-no-trigger-prs-only variables: - CI_IMAGE: "paritytech/tools:latest" + CI_IMAGE: "paritytech/tools:latest" script: - ./scripts/ci/gitlab/ensure-deps.sh test-rust-features: - stage: check + stage: check extends: - .kubernetes-env - .test-refs-no-trigger-prs-only script: - git clone - --depth=1 - --branch="$PIPELINE_SCRIPTS_TAG" - https://github.com/paritytech/pipeline-scripts + --depth=1 + --branch="$PIPELINE_SCRIPTS_TAG" + https://github.com/paritytech/pipeline-scripts - bash ./pipeline-scripts/rust-features.sh . test-prometheus-alerting-rules: - stage: check - extends: .kubernetes-env + stage: check + extends: .kubernetes-env variables: - CI_IMAGE: "paritytech/tools:latest" + CI_IMAGE: "paritytech/tools:latest" rules: - if: $CI_PIPELINE_SOURCE == "pipeline" when: never @@ -62,4 +62,4 @@ test-prometheus-alerting-rules: script: - promtool check rules ./scripts/ci/monitoring/alerting-rules/alerting-rules.yaml - cat ./scripts/ci/monitoring/alerting-rules/alerting-rules.yaml | - promtool test rules ./scripts/ci/monitoring/alerting-rules/alerting-rule-tests.yaml + promtool test rules ./scripts/ci/monitoring/alerting-rules/alerting-rule-tests.yaml diff --git a/scripts/ci/gitlab/pipeline/publish.yml b/scripts/ci/gitlab/pipeline/publish.yml index 3c280dec7..188a09386 100644 --- a/scripts/ci/gitlab/pipeline/publish.yml +++ b/scripts/ci/gitlab/pipeline/publish.yml @@ -1,16 +1,15 @@ - # This file is part of .gitlab-ci.yml # Here are all jobs that are executed during "publish" stage .build-push-docker-image-common: extends: - .kubernetes-env - stage: publish + stage: publish variables: - CI_IMAGE: $BUILDAH_IMAGE - GIT_STRATEGY: none - DOCKERFILE: $PRODUCT.Dockerfile - IMAGE_NAME: docker.io/$IMAGE_PATH + CI_IMAGE: $BUILDAH_IMAGE + GIT_STRATEGY: none + DOCKERFILE: $PRODUCT.Dockerfile + IMAGE_NAME: docker.io/$IMAGE_PATH before_script: - !reference [.kubernetes-env, before_script] - cd ./artifacts/$PRODUCT/ @@ -19,17 +18,17 @@ - test -z "${VERSION}" && exit 1 script: - test "$DOCKER_USER" -a "$DOCKER_PASS" || - ( echo "no docker credentials provided"; exit 1 ) + ( echo "no docker credentials provided"; exit 1 ) - buildah bud - --format=docker - --build-arg VCS_REF="${CI_COMMIT_SHA}" - --build-arg BUILD_DATE="$(date -u '+%Y-%m-%dT%H:%M:%SZ')" - --build-arg IMAGE_NAME="${IMAGE_PATH}" - --tag "$IMAGE_NAME:$VERSION" - --tag "$IMAGE_NAME:latest" - --file "$DOCKERFILE" . + --format=docker + --build-arg VCS_REF="${CI_COMMIT_SHA}" + --build-arg BUILD_DATE="$(date -u '+%Y-%m-%dT%H:%M:%SZ')" + --build-arg IMAGE_NAME="${IMAGE_PATH}" + --tag "$IMAGE_NAME:$VERSION" + --tag "$IMAGE_NAME:latest" + --file "$DOCKERFILE" . - echo "$DOCKER_PASS" | - buildah login --username "$DOCKER_USER" --password-stdin docker.io + buildah login --username "$DOCKER_USER" --password-stdin docker.io - buildah info - buildah push --format=v2s2 "$IMAGE_NAME:$VERSION" - buildah push --format=v2s2 "$IMAGE_NAME:latest" @@ -45,25 +44,25 @@ - .publish-refs - .build-push-docker-image-common variables: - IMAGE_PATH: parity/$PRODUCT - DOCKER_USER: $Docker_Hub_User_Parity - DOCKER_PASS: $Docker_Hub_Pass_Parity + IMAGE_PATH: parity/$PRODUCT + DOCKER_USER: $Docker_Hub_User_Parity + DOCKER_PASS: $Docker_Hub_Pass_Parity .push-docker-image-description: - stage: publish + stage: publish extends: - .kubernetes-env - .publish-refs variables: - CI_IMAGE: paritytech/dockerhub-description - DOCKERHUB_REPOSITORY: parity/$PRODUCT - DOCKER_USERNAME: $Docker_Hub_User_Parity - DOCKER_PASSWORD: $Docker_Hub_Pass_Parity - README_FILEPATH: $CI_PROJECT_DIR/scripts/ci/docker/$PRODUCT.Dockerfile.README.md + CI_IMAGE: paritytech/dockerhub-description + DOCKERHUB_REPOSITORY: parity/$PRODUCT + DOCKER_USERNAME: $Docker_Hub_User_Parity + DOCKER_PASSWORD: $Docker_Hub_Pass_Parity + README_FILEPATH: $CI_PROJECT_DIR/scripts/ci/docker/$PRODUCT.Dockerfile.README.md rules: - - if: $CI_COMMIT_REF_NAME == "master" - changes: - - scripts/ci/docker/$PRODUCT.Dockerfile.README.md + - if: $CI_COMMIT_REF_NAME == "master" + changes: + - scripts/ci/docker/$PRODUCT.Dockerfile.README.md script: - cd / && sh entrypoint.sh @@ -73,94 +72,94 @@ - .build-refs - .build-push-docker-image-common variables: - IMAGE_PATH: paritypr/$PRODUCT - DOCKER_USER: $PARITYPR_USER - DOCKER_PASS: $PARITYPR_PASS + IMAGE_PATH: paritypr/$PRODUCT + DOCKER_USER: $PARITYPR_USER + DOCKER_PASS: $PARITYPR_PASS publish-docker-substrate: - extends: .build-push-docker-image + extends: .build-push-docker-image needs: - - job: build-linux-substrate - artifacts: true + - job: build-linux-substrate + artifacts: true variables: - PRODUCT: substrate + PRODUCT: substrate publish-docker-description-substrate: - extends: .push-docker-image-description + extends: .push-docker-image-description variables: - PRODUCT: substrate - SHORT_DESCRIPTION: "Substrate Docker Image." + PRODUCT: substrate + SHORT_DESCRIPTION: "Substrate Docker Image." publish-docker-substrate-temporary: - extends: .build-push-image-temporary + extends: .build-push-image-temporary needs: - - job: build-linux-substrate - artifacts: true + - job: build-linux-substrate + artifacts: true variables: - PRODUCT: substrate + PRODUCT: substrate artifacts: reports: # this artifact is used in zombienet-tests job # https://docs.gitlab.com/ee/ci/multi_project_pipelines.html#with-variable-inheritance dotenv: ./artifacts/$PRODUCT/build.env - expire_in: 24h + expire_in: 24h publish-docker-subkey: - extends: .build-push-docker-image + extends: .build-push-docker-image needs: - - job: build-subkey-linux - artifacts: true + - job: build-subkey-linux + artifacts: true variables: - PRODUCT: subkey + PRODUCT: subkey publish-docker-description-subkey: - extends: .push-docker-image-description + extends: .push-docker-image-description variables: - PRODUCT: subkey - SHORT_DESCRIPTION: "The subkey program is a key management utility for Substrate-based blockchains." + PRODUCT: subkey + SHORT_DESCRIPTION: "The subkey program is a key management utility for Substrate-based blockchains." publish-s3-release: - stage: publish + stage: publish extends: - .publish-refs - .kubernetes-env needs: - - job: build-linux-substrate - artifacts: true - - job: build-subkey-linux - artifacts: true - image: paritytech/awscli:latest + - job: build-linux-substrate + artifacts: true + - job: build-subkey-linux + artifacts: true + image: paritytech/awscli:latest variables: - GIT_STRATEGY: none - BUCKET: "releases.parity.io" - PREFIX: "substrate/${ARCH}-${DOCKER_OS}" + GIT_STRATEGY: none + BUCKET: "releases.parity.io" + PREFIX: "substrate/${ARCH}-${DOCKER_OS}" script: - aws s3 sync ./artifacts/ s3://${BUCKET}/${PREFIX}/$(cat ./artifacts/substrate/VERSION)/ - echo "update objects in latest path" - aws s3 sync s3://${BUCKET}/${PREFIX}/$(cat ./artifacts/substrate/VERSION)/ s3://${BUCKET}/${PREFIX}/latest/ after_script: - aws s3 ls s3://${BUCKET}/${PREFIX}/latest/ - --recursive --human-readable --summarize + --recursive --human-readable --summarize publish-rustdoc: - stage: publish - extends: .kubernetes-env + stage: publish + extends: .kubernetes-env variables: - CI_IMAGE: node:16 - GIT_DEPTH: 100 - RUSTDOCS_DEPLOY_REFS: "master" + CI_IMAGE: node:16 + GIT_DEPTH: 100 + RUSTDOCS_DEPLOY_REFS: "master" rules: - if: $CI_PIPELINE_SOURCE == "pipeline" when: never - if: $CI_PIPELINE_SOURCE == "web" && $CI_COMMIT_REF_NAME == "master" - if: $CI_COMMIT_REF_NAME == "master" - - if: $CI_COMMIT_REF_NAME =~ /^monthly-20[0-9]{2}-[0-9]{2}.*$/ # to support: monthly-2021-09+1 - - if: $CI_COMMIT_REF_NAME =~ /^v[0-9]+\.[0-9]+.*$/ # i.e. v1.0, v2.1rc1 + - if: $CI_COMMIT_REF_NAME =~ /^monthly-20[0-9]{2}-[0-9]{2}.*$/ # to support: monthly-2021-09+1 + - if: $CI_COMMIT_REF_NAME =~ /^v[0-9]+\.[0-9]+.*$/ # i.e. v1.0, v2.1rc1 # `needs:` can be removed after CI image gets nonroot. In this case `needs:` stops other # artifacts from being dowloaded by this job. needs: - - job: build-rustdoc - artifacts: true + - job: build-rustdoc + artifacts: true script: # If $CI_COMMIT_REF_NAME doesn't match one of $RUSTDOCS_DEPLOY_REFS space-separated values, we # exit immediately. @@ -196,75 +195,75 @@ publish-rustdoc: # We don't want to mark the entire job failed if there's nothing to # publish though, hence the `|| true`. - git commit -m "___Updated docs for ${CI_COMMIT_REF_NAME}___" || - echo "___Nothing to commit___" + echo "___Nothing to commit___" - git push origin gh-pages --force after_script: - rm -rf .git/ ./* publish-draft-release: - stage: publish - image: paritytech/tools:latest + stage: publish + image: paritytech/tools:latest rules: - if: $CI_COMMIT_REF_NAME =~ /^ci-release-.*$/ - - if: $CI_COMMIT_REF_NAME =~ /^v[0-9]+\.[0-9]+.*$/ # i.e. v1.0, v2.1rc1 + - if: $CI_COMMIT_REF_NAME =~ /^v[0-9]+\.[0-9]+.*$/ # i.e. v1.0, v2.1rc1 script: - ./scripts/ci/gitlab/publish_draft_release.sh - allow_failure: true + allow_failure: true .publish-crates-template: - stage: publish + stage: publish extends: - .crates-publishing-template - .crates-publishing-pipeline # We don't want multiple jobs racing to publish crates as it's redundant and they might overwrite # the releases of one another. Use resource_group to ensure that at most one instance of this job # is running at any given time. - resource_group: crates-publishing + resource_group: crates-publishing # crates.io currently rate limits crate publishing at 1 per minute: # https://github.com/paritytech/release-engineering/issues/123#issuecomment-1335509748 # Taking into account the 202 (as of Dec 07, 2022) publishable Substrate crates, in the worst # case, due to the rate limits alone, we'd have to wait through at least 202 minutes of delay. # Taking into account also the verification steps and extra synchronization delays after # publishing the crate, the job needs to have a much higher timeout than average. - timeout: 9h + timeout: 9h # A custom publishing environment is used for us to be able to set up protected secrets # specifically for it - environment: publish-crates + environment: publish-crates script: - rusty-cachier snapshot create - git clone - --depth 1 - --branch "$RELENG_SCRIPTS_BRANCH" - https://github.com/paritytech/releng-scripts.git + --depth 1 + --branch "$RELENG_SCRIPTS_BRANCH" + https://github.com/paritytech/releng-scripts.git - CRATESIO_TARGET_INSTANCE=default ./releng-scripts/publish-crates - rusty-cachier cache upload publish-crates: - extends: .publish-crates-template + extends: .publish-crates-template # publish-crates should only be run if publish-crates-locally passes needs: - - job: check-crate-publishing - artifacts: false + - job: check-crate-publishing + artifacts: false publish-crates-manual: - extends: .publish-crates-template - when: manual - interruptible: false + extends: .publish-crates-template + when: manual + interruptible: false check-crate-publishing: - stage: publish + stage: publish extends: - .test-refs - .crates-publishing-template # When lots of crates are taken into account (for example on master where all crates are tested) # the job might take a long time, as evidenced by: # https://gitlab.parity.io/parity/mirrors/substrate/-/jobs/2269364 - timeout: 4h + timeout: 4h script: - rusty-cachier snapshot create - git clone - --depth 1 - --branch "$RELENG_SCRIPTS_BRANCH" - https://github.com/paritytech/releng-scripts.git + --depth 1 + --branch "$RELENG_SCRIPTS_BRANCH" + https://github.com/paritytech/releng-scripts.git - CRATESIO_TARGET_INSTANCE=local ./releng-scripts/publish-crates - rusty-cachier cache upload diff --git a/scripts/ci/gitlab/pipeline/test.yml b/scripts/ci/gitlab/pipeline/test.yml index 732b8c0fd..49dbb194f 100644 --- a/scripts/ci/gitlab/pipeline/test.yml +++ b/scripts/ci/gitlab/pipeline/test.yml @@ -1,29 +1,28 @@ # This file is part of .gitlab-ci.yml # Here are all jobs that are executed during "test" stage - # It's more like a check and it belongs to the previous stage, but we want to run this job with real tests in parallel find-fail-ci-phrase: - stage: test + stage: test variables: - CI_IMAGE: "paritytech/tools:latest" - ASSERT_REGEX: "FAIL-CI" - GIT_DEPTH: 1 + CI_IMAGE: "paritytech/tools:latest" + ASSERT_REGEX: "FAIL-CI" + GIT_DEPTH: 1 extends: - .kubernetes-env script: - set +e - rg --line-number --hidden --type rust --glob '!{.git,target}' "$ASSERT_REGEX" .; exit_status=$? - if [ $exit_status -eq 0 ]; then - echo "$ASSERT_REGEX was found, exiting with 1"; - exit 1; + echo "$ASSERT_REGEX was found, exiting with 1"; + exit 1; else - echo "No $ASSERT_REGEX was found, exiting with 0"; - exit 0; + echo "No $ASSERT_REGEX was found, exiting with 0"; + exit 0; fi cargo-deny: - stage: test + stage: test extends: - .docker-env - .nightly-pipeline @@ -36,16 +35,16 @@ cargo-deny: - echo "___The complete log is in the artifacts___" - cargo deny check -c ./scripts/ci/deny.toml 2> deny.log artifacts: - name: $CI_COMMIT_SHORT_SHA - expire_in: 3 days - when: always + name: $CI_COMMIT_SHORT_SHA + expire_in: 3 days + when: always paths: - deny.log # FIXME: Temporarily allow to fail. - allow_failure: true + allow_failure: true cargo-fmt: - stage: test + stage: test variables: RUSTY_CACHIER_TOOLCHAIN: nightly extends: @@ -57,11 +56,11 @@ cargo-fmt: - rusty-cachier cache upload cargo-clippy: - stage: test + stage: test # this is an artificial job dependency, for pipeline optimization using GitLab's DAGs needs: - - job: cargo-fmt - artifacts: false + - job: cargo-fmt + artifacts: false variables: RUSTY_CACHIER_TOOLCHAIN: nightly extends: @@ -73,11 +72,11 @@ cargo-clippy: - rusty-cachier cache upload cargo-check-benches: - stage: test + stage: test variables: # Override to use nightly toolchain - RUSTY_CACHIER_TOOLCHAIN: "nightly" - CI_JOB_NAME: "cargo-check-benches" + RUSTY_CACHIER_TOOLCHAIN: "nightly" + CI_JOB_NAME: "cargo-check-benches" extends: - .docker-env - .test-refs-check-benches @@ -93,11 +92,11 @@ cargo-check-benches: - | export BASE=$(curl -s -H "Authorization: Bearer ${GITHUB_PR_TOKEN}" https://api.github.com/repos/paritytech/substrate/pulls/${$CI_COMMIT_REF_NAME} | jq .base.ref) - if [ $CI_COMMIT_REF_NAME != "master" ]; then - git fetch origin +${BASE}:${BASE}; - git fetch origin +$CI_COMMIT_REF_NAME:$CI_COMMIT_REF_NAME; - git checkout ${BASE}; - git config user.email "ci@gitlab.parity.io"; - git merge $CI_COMMIT_REF_NAME --verbose --no-edit; + git fetch origin +${BASE}:${BASE}; + git fetch origin +$CI_COMMIT_REF_NAME:$CI_COMMIT_REF_NAME; + git checkout ${BASE}; + git config user.email "ci@gitlab.parity.io"; + git merge $CI_COMMIT_REF_NAME --verbose --no-edit; fi parallel: 2 script: @@ -106,18 +105,18 @@ cargo-check-benches: # this job is executed in parallel on two runners - echo "___Running benchmarks___"; - case ${CI_NODE_INDEX} in - 1) - SKIP_WASM_BUILD=1 time cargo +nightly check --locked --benches --all; - cargo run --locked --release -p node-bench -- ::trie::read::small --json - | tee ./artifacts/benches/$CI_COMMIT_REF_NAME-$CI_COMMIT_SHORT_SHA/::trie::read::small.json; - echo "___Uploading cache for rusty-cachier___"; - rusty-cachier cache upload - ;; - 2) - cargo run --locked --release -p node-bench -- ::node::import::native::sr25519::transfer_keep_alive::paritydb::small --json - | tee ./artifacts/benches/$CI_COMMIT_REF_NAME-$CI_COMMIT_SHORT_SHA/::node::import::native::sr25519::transfer_keep_alive::paritydb::small.json - ;; - esac + 1) + SKIP_WASM_BUILD=1 time cargo +nightly check --locked --benches --all; + cargo run --locked --release -p node-bench -- ::trie::read::small --json + | tee ./artifacts/benches/$CI_COMMIT_REF_NAME-$CI_COMMIT_SHORT_SHA/::trie::read::small.json; + echo "___Uploading cache for rusty-cachier___"; + rusty-cachier cache upload + ;; + 2) + cargo run --locked --release -p node-bench -- ::node::import::native::sr25519::transfer_keep_alive::paritydb::small --json + | tee ./artifacts/benches/$CI_COMMIT_REF_NAME-$CI_COMMIT_SHORT_SHA/::node::import::native::sr25519::transfer_keep_alive::paritydb::small.json + ;; + esac tags: - linux-docker-benches @@ -125,38 +124,38 @@ node-bench-regression-guard: # it's not belong to `build` semantically, but dag jobs can't depend on each other # within the single stage - https://gitlab.com/gitlab-org/gitlab/-/issues/30632 # more: https://github.com/paritytech/substrate/pull/8519#discussion_r608012402 - stage: build + stage: build extends: - .docker-env - .test-refs-no-trigger-prs-only needs: # this is a DAG - - job: cargo-check-benches - artifacts: true + - job: cargo-check-benches + artifacts: true # polls artifact from master to compare with current result # need to specify both parallel jobs from master because of the bug # https://gitlab.com/gitlab-org/gitlab/-/issues/39063 - - project: $CI_PROJECT_PATH - job: "cargo-check-benches 1/2" - ref: master - artifacts: true - - project: $CI_PROJECT_PATH - job: "cargo-check-benches 2/2" - ref: master - artifacts: true + - project: $CI_PROJECT_PATH + job: "cargo-check-benches 1/2" + ref: master + artifacts: true + - project: $CI_PROJECT_PATH + job: "cargo-check-benches 2/2" + ref: master + artifacts: true variables: - CI_IMAGE: "paritytech/node-bench-regression-guard:latest" + CI_IMAGE: "paritytech/node-bench-regression-guard:latest" before_script: [""] script: - echo "------- IMPORTANT -------" - echo "node-bench-regression-guard depends on the results of a cargo-check-benches job" - echo "In case of this job failure, check your pipeline's cargo-check-benches" - - 'node-bench-regression-guard --reference artifacts/benches/master-* - --compare-with artifacts/benches/$CI_COMMIT_REF_NAME-$CI_COMMIT_SHORT_SHA' + - "node-bench-regression-guard --reference artifacts/benches/master-* + --compare-with artifacts/benches/$CI_COMMIT_REF_NAME-$CI_COMMIT_SHORT_SHA" after_script: [""] cargo-check-try-runtime: - stage: test + stage: test extends: - .docker-env - .test-refs @@ -166,18 +165,18 @@ cargo-check-try-runtime: - rusty-cachier cache upload test-deterministic-wasm: - stage: test + stage: test # this is an artificial job dependency, for pipeline optimization using GitLab's DAGs needs: - - job: cargo-check-try-runtime - artifacts: false + - job: cargo-check-try-runtime + artifacts: false extends: - .docker-env - .test-refs variables: - WASM_BUILD_NO_COLOR: 1 + WASM_BUILD_NO_COLOR: 1 # this variable gets overriden by "rusty-cachier environment inject", use the value as default - CARGO_TARGET_DIR: "$CI_PROJECT_DIR/target" + CARGO_TARGET_DIR: "$CI_PROJECT_DIR/target" script: - rusty-cachier snapshot create # build runtime @@ -195,7 +194,7 @@ test-deterministic-wasm: - rusty-cachier cache upload test-linux-stable: - stage: test + stage: test extends: - .docker-env - .test-refs @@ -203,14 +202,14 @@ test-linux-stable: variables: # Enable debug assertions since we are running optimized builds for testing # but still want to have debug assertions. - RUSTFLAGS: "-Cdebug-assertions=y -Dwarnings" - RUST_BACKTRACE: 1 - WASM_BUILD_NO_COLOR: 1 - WASM_BUILD_RUSTFLAGS: "-Cdebug-assertions=y -Dwarnings" + RUSTFLAGS: "-Cdebug-assertions=y -Dwarnings" + RUST_BACKTRACE: 1 + WASM_BUILD_NO_COLOR: 1 + WASM_BUILD_RUSTFLAGS: "-Cdebug-assertions=y -Dwarnings" # Ensure we run the UI tests. - RUN_UI_TESTS: 1 + RUN_UI_TESTS: 1 # needed for rusty-cachier to keep cache in test-linux-stable folder and not in test-linux-stable-1/3 - CI_JOB_NAME: "test-linux-stable" + CI_JOB_NAME: "test-linux-stable" parallel: 3 script: - rusty-cachier snapshot create @@ -219,30 +218,30 @@ test-linux-stable: # node-cli is excluded until https://github.com/paritytech/substrate/issues/11321 fixed - echo "Node index - ${CI_NODE_INDEX}. Total amount - ${CI_NODE_TOTAL}" - time cargo nextest run --workspace - --locked - --release - --verbose - --features runtime-benchmarks - --manifest-path ./bin/node/cli/Cargo.toml - --exclude node-cli - --partition count:${CI_NODE_INDEX}/${CI_NODE_TOTAL} + --locked + --release + --verbose + --features runtime-benchmarks + --manifest-path ./bin/node/cli/Cargo.toml + --exclude node-cli + --partition count:${CI_NODE_INDEX}/${CI_NODE_TOTAL} # we need to update cache only from one job - if [ ${CI_NODE_INDEX} == 1 ]; then rusty-cachier cache upload; fi test-frame-support: - stage: test + stage: test extends: - .docker-env - .test-refs variables: # Enable debug assertions since we are running optimized builds for testing # but still want to have debug assertions. - RUSTFLAGS: "-Cdebug-assertions=y -Dwarnings" - RUST_BACKTRACE: 1 - WASM_BUILD_NO_COLOR: 1 - WASM_BUILD_RUSTFLAGS: "-Cdebug-assertions=y -Dwarnings" + RUSTFLAGS: "-Cdebug-assertions=y -Dwarnings" + RUST_BACKTRACE: 1 + WASM_BUILD_NO_COLOR: 1 + WASM_BUILD_RUSTFLAGS: "-Cdebug-assertions=y -Dwarnings" # Ensure we run the UI tests. - RUN_UI_TESTS: 1 + RUN_UI_TESTS: 1 script: - rusty-cachier snapshot create - time cargo test --locked -p frame-support-test --features=frame-feature-testing,no-metadata-docs --manifest-path ./frame/support/test/Cargo.toml --test pallet # does not reuse cache 1 min 44 sec @@ -252,19 +251,19 @@ test-frame-support: # This job runs tests that don't work with cargo-nextest in test-linux-stable test-linux-stable-extra: - stage: test + stage: test extends: - .docker-env - .test-refs variables: # Enable debug assertions since we are running optimized builds for testing # but still want to have debug assertions. - RUSTFLAGS: "-Cdebug-assertions=y -Dwarnings" - RUST_BACKTRACE: 1 - WASM_BUILD_NO_COLOR: 1 - WASM_BUILD_RUSTFLAGS: "-Cdebug-assertions=y -Dwarnings" + RUSTFLAGS: "-Cdebug-assertions=y -Dwarnings" + RUST_BACKTRACE: 1 + WASM_BUILD_NO_COLOR: 1 + WASM_BUILD_RUSTFLAGS: "-Cdebug-assertions=y -Dwarnings" # Ensure we run the UI tests. - RUN_UI_TESTS: 1 + RUN_UI_TESTS: 1 script: - rusty-cachier snapshot create # Run node-cli tests @@ -277,17 +276,17 @@ test-linux-stable-extra: # This job runs all benchmarks defined in the `/bin/node/runtime` once to check that there are no errors. quick-benchmarks: - stage: test + stage: test extends: - .docker-env - .test-refs variables: # Enable debug assertions since we are running optimized builds for testing # but still want to have debug assertions. - RUSTFLAGS: "-Cdebug-assertions=y -Dwarnings" - RUST_BACKTRACE: "full" - WASM_BUILD_NO_COLOR: 1 - WASM_BUILD_RUSTFLAGS: "-Cdebug-assertions=y -Dwarnings" + RUSTFLAGS: "-Cdebug-assertions=y -Dwarnings" + RUST_BACKTRACE: "full" + WASM_BUILD_NO_COLOR: 1 + WASM_BUILD_RUSTFLAGS: "-Cdebug-assertions=y -Dwarnings" script: - rusty-cachier snapshot create - time cargo run --locked --release --features runtime-benchmarks -- benchmark pallet --execution wasm --wasm-execution compiled --chain dev --pallet "*" --extrinsic "*" --steps 2 --repeat 1 @@ -295,16 +294,15 @@ quick-benchmarks: test-frame-examples-compile-to-wasm: # into one job - stage: test - variables: - RUSTY_CACHIER_TOOLCHAIN: nightly + stage: test extends: - .docker-env - .test-refs variables: + RUSTY_CACHIER_TOOLCHAIN: nightly # Enable debug assertions since we are running optimized builds for testing # but still want to have debug assertions. - RUSTFLAGS: "-Cdebug-assertions=y" + RUSTFLAGS: "-Cdebug-assertions=y" RUST_BACKTRACE: 1 script: - rusty-cachier snapshot create @@ -315,7 +313,7 @@ test-frame-examples-compile-to-wasm: - rusty-cachier cache upload test-linux-stable-int: - stage: test + stage: test extends: - .docker-env - .test-refs @@ -323,27 +321,27 @@ test-linux-stable-int: variables: # Enable debug assertions since we are running optimized builds for testing # but still want to have debug assertions. - RUSTFLAGS: "-Cdebug-assertions=y -Dwarnings" - RUST_BACKTRACE: 1 - WASM_BUILD_NO_COLOR: 1 - WASM_BUILD_RUSTFLAGS: "-Cdebug-assertions=y -Dwarnings" + RUSTFLAGS: "-Cdebug-assertions=y -Dwarnings" + RUST_BACKTRACE: 1 + WASM_BUILD_NO_COLOR: 1 + WASM_BUILD_RUSTFLAGS: "-Cdebug-assertions=y -Dwarnings" # Ensure we run the UI tests. - RUN_UI_TESTS: 1 + RUN_UI_TESTS: 1 script: - rusty-cachier snapshot create - WASM_BUILD_NO_COLOR=1 RUST_LOG=sync=trace,consensus=trace,client=trace,state-db=trace,db=trace,forks=trace,state_db=trace,storage_cache=trace - time cargo test -p node-cli --release --verbose --locked -- --ignored + time cargo test -p node-cli --release --verbose --locked -- --ignored - rusty-cachier cache upload # more information about this job can be found here: # https://github.com/paritytech/substrate/pull/6916 check-tracing: - stage: test + stage: test # this is an artificial job dependency, for pipeline optimization using GitLab's DAGs needs: - - job: test-linux-stable-int - artifacts: false + - job: test-linux-stable-int + artifacts: false variables: RUSTY_CACHIER_TOOLCHAIN: nightly extends: @@ -360,20 +358,19 @@ check-tracing: # more information about this job can be found here: # https://github.com/paritytech/substrate/pull/3778 test-full-crypto-feature: - stage: test + stage: test # this is an artificial job dependency, for pipeline optimization using GitLab's DAGs needs: - - job: check-tracing - artifacts: false - variables: - RUSTY_CACHIER_TOOLCHAIN: nightly + - job: check-tracing + artifacts: false extends: - .docker-env - .test-refs variables: + RUSTY_CACHIER_TOOLCHAIN: nightly # Enable debug assertions since we are running optimized builds for testing # but still want to have debug assertions. - RUSTFLAGS: "-Cdebug-assertions=y" + RUSTFLAGS: "-Cdebug-assertions=y" RUST_BACKTRACE: 1 script: - rusty-cachier snapshot create @@ -384,22 +381,21 @@ test-full-crypto-feature: - rusty-cachier cache upload check-rustdoc: - stage: test - variables: - RUSTY_CACHIER_TOOLCHAIN: nightly + stage: test extends: - .docker-env - .test-refs variables: - SKIP_WASM_BUILD: 1 - RUSTDOCFLAGS: "-Dwarnings" + RUSTY_CACHIER_TOOLCHAIN: nightly + SKIP_WASM_BUILD: 1 + RUSTDOCFLAGS: "-Dwarnings" script: - rusty-cachier snapshot create - time cargo +nightly doc --locked --workspace --all-features --verbose --no-deps - rusty-cachier cache upload cargo-check-each-crate: - stage: test + stage: test extends: - .docker-env - .test-refs @@ -408,7 +404,7 @@ cargo-check-each-crate: variables: # $CI_JOB_NAME is set manually so that rusty-cachier can share the cache for all # "cargo-check-each-crate I/N" jobs - CI_JOB_NAME: cargo-check-each-crate + CI_JOB_NAME: cargo-check-each-crate script: - rusty-cachier snapshot create - PYTHONUNBUFFERED=x time ./scripts/ci/gitlab/check-each-crate.py "$CI_NODE_INDEX" "$CI_NODE_TOTAL" @@ -427,7 +423,7 @@ cargo-check-each-crate-macos: - !reference [.rust-info-script, script] - !reference [.pipeline-stopper-vars, script] variables: - SKIP_WASM_BUILD: 1 + SKIP_WASM_BUILD: 1 script: # TODO: enable rusty-cachier once it supports Mac # TODO: use parallel jobs, as per cargo-check-each-crate, once more Mac runners are available diff --git a/scripts/ci/gitlab/pipeline/zombienet.yml b/scripts/ci/gitlab/pipeline/zombienet.yml index 3429621e4..31ee51034 100644 --- a/scripts/ci/gitlab/pipeline/zombienet.yml +++ b/scripts/ci/gitlab/pipeline/zombienet.yml @@ -11,20 +11,20 @@ - export DEBUG=zombie,zombie::network-node - export ZOMBIENET_INTEGRATION_TEST_IMAGE=${SUBSTRATE_IMAGE_NAME}:${SUBSTRATE_IMAGE_TAG} - echo "${ZOMBIENET_INTEGRATION_TEST_IMAGE}" - stage: zombienet - image: "${ZOMBIENET_IMAGE}" + stage: zombienet + image: "${ZOMBIENET_IMAGE}" needs: - - job: publish-docker-substrate-temporary + - job: publish-docker-substrate-temporary extends: - .kubernetes-env - .zombienet-refs variables: - GH_DIR: "https://github.com/paritytech/substrate/tree/${CI_COMMIT_SHA}/zombienet" + GH_DIR: "https://github.com/paritytech/substrate/tree/${CI_COMMIT_SHA}/zombienet" FF_DISABLE_UMASK_FOR_DOCKER_EXECUTOR: 1 artifacts: - name: "${CI_JOB_NAME}_${CI_COMMIT_REF_NAME}" - when: always - expire_in: 2 days + name: "${CI_JOB_NAME}_${CI_COMMIT_REF_NAME}" + when: always + expire_in: 2 days paths: - ./zombienet-logs after_script: @@ -39,32 +39,29 @@ zombienet-0000-block-building: - .zombienet-common script: - /home/nonroot/zombie-net/scripts/ci/run-test-env-manager.sh - --github-remote-dir="${GH_DIR}/0000-block-building" - --test="block-building.zndsl" - + --github-remote-dir="${GH_DIR}/0000-block-building" + --test="block-building.zndsl" zombienet-0001-basic-warp-sync: extends: - .zombienet-common script: - /home/nonroot/zombie-net/scripts/ci/run-test-env-manager.sh - --github-remote-dir="${GH_DIR}/0001-basic-warp-sync" - --test="test-warp-sync.zndsl" - + --github-remote-dir="${GH_DIR}/0001-basic-warp-sync" + --test="test-warp-sync.zndsl" zombienet-0002-validators-warp-sync: extends: - .zombienet-common script: - /home/nonroot/zombie-net/scripts/ci/run-test-env-manager.sh - --github-remote-dir="${GH_DIR}/0002-validators-warp-sync" - --test="test-validators-warp-sync.zndsl" - + --github-remote-dir="${GH_DIR}/0002-validators-warp-sync" + --test="test-validators-warp-sync.zndsl" zombienet-0003-block-building-warp-sync: extends: - .zombienet-common script: - /home/nonroot/zombie-net/scripts/ci/run-test-env-manager.sh - --github-remote-dir="${GH_DIR}/0003-block-building-warp-sync" - --test="test-block-building-warp-sync.zndsl" + --github-remote-dir="${GH_DIR}/0003-block-building-warp-sync" + --test="test-block-building-warp-sync.zndsl" diff --git a/scripts/ci/gitlab/prettier.sh b/scripts/ci/gitlab/prettier.sh new file mode 100755 index 000000000..299bbee17 --- /dev/null +++ b/scripts/ci/gitlab/prettier.sh @@ -0,0 +1,6 @@ +#!/bin/sh + +# meant to be installed via +# git config filter.ci-prettier.clean "scripts/ci/gitlab/prettier.sh" + +prettier --parser yaml From 9526f040b487aa8642ba21450cddfbbcace9e1f2 Mon Sep 17 00:00:00 2001 From: Oliver Tale-Yazdi Date: Sat, 25 Feb 2023 17:13:20 +0100 Subject: [PATCH 162/558] Yieldable queues for pallet `MessageQueue` (#13424) * Add Yield message processing error Signed-off-by: Oliver Tale-Yazdi * Add NoopServiceQueues Signed-off-by: Oliver Tale-Yazdi * Implement temporary error aka Yield Signed-off-by: Oliver Tale-Yazdi * Make NoopMessageProcessor generic Signed-off-by: Oliver Tale-Yazdi * Mock pausable message processor Signed-off-by: Oliver Tale-Yazdi * Test paused queues Signed-off-by: Oliver Tale-Yazdi * Integration test paused queues Signed-off-by: Oliver Tale-Yazdi * Use WeightMeter instead of weight return Signed-off-by: Oliver Tale-Yazdi * fix Signed-off-by: Oliver Tale-Yazdi * Make compile Signed-off-by: Oliver Tale-Yazdi * Add tests Signed-off-by: Oliver Tale-Yazdi * ".git/.scripts/commands/bench/bench.sh" pallet dev pallet_message_queue * Fix test Signed-off-by: Oliver Tale-Yazdi --------- Signed-off-by: Oliver Tale-Yazdi Co-authored-by: command-bot <> --- bin/node/runtime/src/lib.rs | 2 +- frame/message-queue/src/integration_test.rs | 129 ++++++++++- frame/message-queue/src/lib.rs | 55 +++-- frame/message-queue/src/mock.rs | 49 ++-- frame/message-queue/src/mock_helpers.rs | 26 ++- frame/message-queue/src/tests.rs | 119 +++++++++- frame/message-queue/src/weights.rs | 241 +++++++++++--------- frame/support/src/traits.rs | 4 +- frame/support/src/traits/messages.rs | 27 ++- 9 files changed, 479 insertions(+), 173 deletions(-) diff --git a/bin/node/runtime/src/lib.rs b/bin/node/runtime/src/lib.rs index a869d309f..14b74425f 100644 --- a/bin/node/runtime/src/lib.rs +++ b/bin/node/runtime/src/lib.rs @@ -1155,7 +1155,7 @@ impl pallet_message_queue::Config for Runtime { type RuntimeEvent = RuntimeEvent; type WeightInfo = (); /// NOTE: Always set this to `NoopMessageProcessor` for benchmarking. - type MessageProcessor = pallet_message_queue::mock_helpers::NoopMessageProcessor; + type MessageProcessor = pallet_message_queue::mock_helpers::NoopMessageProcessor; type Size = u32; type QueueChangeHandler = (); type HeapSize = ConstU32<{ 64 * 1024 }>; diff --git a/frame/message-queue/src/integration_test.rs b/frame/message-queue/src/integration_test.rs index b89c78086..255098b3b 100644 --- a/frame/message-queue/src/integration_test.rs +++ b/frame/message-queue/src/integration_test.rs @@ -23,7 +23,9 @@ use crate::{ mock::{ new_test_ext, CountingMessageProcessor, IntoWeight, MockedWeightInfo, NumMessagesProcessed, + SuspendedQueues, }, + mock_helpers::MessageOrigin, *, }; @@ -39,6 +41,7 @@ use sp_runtime::{ testing::Header, traits::{BlakeTwo256, IdentityLookup}, }; +use std::collections::{BTreeMap, BTreeSet}; type UncheckedExtrinsic = frame_system::mocking::MockUncheckedExtrinsic; type Block = frame_system::mocking::MockBlock; @@ -100,7 +103,8 @@ impl Config for Test { /// Simulates heavy usage by enqueueing and processing large amounts of messages. /// -/// Best to run with `-r`, `RUST_LOG=info` and `RUSTFLAGS='-Cdebug-assertions=y'`. +/// Best to run with `RUST_LOG=info RUSTFLAGS='-Cdebug-assertions=y' cargo test -r -p +/// pallet-message-queue -- --ignored`. /// /// # Example output /// @@ -130,29 +134,131 @@ fn stress_test_enqueue_and_service() { let mut msgs_remaining = 0; for _ in 0..blocks { // Start by enqueuing a large number of messages. - let (enqueued, _) = + let enqueued = enqueue_messages(max_queues, max_messages_per_queue, max_msg_len, &mut rng); msgs_remaining += enqueued; // Pick a fraction of all messages currently in queue and process them. let processed = rng.gen_range(1..=msgs_remaining); log::info!("Processing {} of all messages {}", processed, msgs_remaining); - process_messages(processed); // This also advances the block. + process_some_messages(processed); // This also advances the block. msgs_remaining -= processed; } log::info!("Processing all remaining {} messages", msgs_remaining); - process_messages(msgs_remaining); + process_all_messages(msgs_remaining); post_conditions(); }); } +/// Simulates heavy usage of the suspension logic via `Yield`. +/// +/// Best to run with `RUST_LOG=info RUSTFLAGS='-Cdebug-assertions=y' cargo test -r -p +/// pallet-message-queue -- --ignored`. +/// +/// # Example output +/// +/// ```pre +/// Enqueued 11776 messages across 2526 queues. Payload 173.94 KiB +/// Suspended 63 and resumed 7 queues of 2526 in total +/// Processing 593 messages. Resumed msgs: 11599, All msgs: 11776 +/// Enqueued 30104 messages across 5533 queues. Payload 416.62 KiB +/// Suspended 24 and resumed 15 queues of 5533 in total +/// Processing 12841 messages. Resumed msgs: 40857, All msgs: 41287 +/// Processing all 28016 remaining resumed messages +/// Resumed all 64 suspended queues +/// Processing all remaining 430 messages +/// ``` +#[test] +#[ignore] // Only run in the CI. +fn stress_test_queue_suspension() { + let blocks = 20; + let max_queues = 10_000; + let max_messages_per_queue = 10_000; + let (max_suspend_per_block, max_resume_per_block) = (100, 50); + let max_msg_len = MaxMessageLenOf::::get(); + let mut rng = StdRng::seed_from_u64(41); + + new_test_ext::().execute_with(|| { + let mut suspended = BTreeSet::::new(); + let mut msgs_remaining = 0; + + for _ in 0..blocks { + // Start by enqueuing a large number of messages. + let enqueued = + enqueue_messages(max_queues, max_messages_per_queue, max_msg_len, &mut rng); + msgs_remaining += enqueued; + let per_queue = msgs_per_queue(); + + // Suspend a random subset of queues. + let to_suspend = rng.gen_range(0..max_suspend_per_block).min(per_queue.len()); + for _ in 0..to_suspend { + let q = rng.gen_range(0..per_queue.len()); + suspended.insert(*per_queue.iter().nth(q).map(|(q, _)| q).unwrap()); + } + // Resume a random subst of suspended queues. + let to_resume = rng.gen_range(0..max_resume_per_block).min(suspended.len()); + for _ in 0..to_resume { + let q = rng.gen_range(0..suspended.len()); + suspended.remove(&suspended.iter().nth(q).unwrap().clone()); + } + log::info!( + "Suspended {} and resumed {} queues of {} in total", + to_suspend, + to_resume, + per_queue.len() + ); + SuspendedQueues::set(suspended.iter().map(|q| MessageOrigin::Everywhere(*q)).collect()); + + // Pick a fraction of all messages currently in queue and process them. + let resumed_messages = + per_queue.iter().filter(|(q, _)| !suspended.contains(q)).map(|(_, n)| n).sum(); + let processed = rng.gen_range(1..=resumed_messages); + log::info!( + "Processing {} messages. Resumed msgs: {}, All msgs: {}", + processed, + resumed_messages, + msgs_remaining + ); + process_some_messages(processed); // This also advances the block. + msgs_remaining -= processed; + } + let per_queue = msgs_per_queue(); + let resumed_messages = + per_queue.iter().filter(|(q, _)| !suspended.contains(q)).map(|(_, n)| n).sum(); + log::info!("Processing all {} remaining resumed messages", resumed_messages); + process_all_messages(resumed_messages); + msgs_remaining -= resumed_messages; + + let resumed = SuspendedQueues::take(); + log::info!("Resumed all {} suspended queues", resumed.len()); + log::info!("Processing all remaining {} messages", msgs_remaining); + process_all_messages(msgs_remaining); + post_conditions(); + }); +} + +/// How many messages are in each queue. +fn msgs_per_queue() -> BTreeMap { + let mut per_queue = BTreeMap::new(); + for (o, q) in BookStateFor::::iter() { + let MessageOrigin::Everywhere(o) = o else { + unreachable!(); + }; + per_queue.insert(o, q.message_count as u32); + } + per_queue +} + /// Enqueue a random number of random messages into a random number of queues. +/// +/// Returns the total number of enqueued messages, their combined length and the number of messages +/// per queue. fn enqueue_messages( max_queues: u32, max_per_queue: u32, max_msg_len: u32, rng: &mut StdRng, -) -> (u32, usize) { +) -> u32 { let num_queues = rng.gen_range(1..max_queues); let mut num_messages = 0; let mut total_msg_len = 0; @@ -179,11 +285,11 @@ fn enqueue_messages( num_queues, total_msg_len as f64 / 1024.0 ); - (num_messages, total_msg_len as usize) + num_messages } /// Process the number of messages. -fn process_messages(num_msgs: u32) { +fn process_some_messages(num_msgs: u32) { let weight = (num_msgs as u64).into_weight(); ServiceWeight::set(Some(weight)); let consumed = next_block(); @@ -192,6 +298,15 @@ fn process_messages(num_msgs: u32) { assert_eq!(NumMessagesProcessed::take(), num_msgs as usize); } +/// Process all remaining messages and assert their number. +fn process_all_messages(expected: u32) { + ServiceWeight::set(Some(Weight::MAX)); + let consumed = next_block(); + + assert_eq!(consumed, Weight::from_all(expected as u64)); + assert_eq!(NumMessagesProcessed::take(), expected as usize); +} + /// Returns the weight consumed by `MessageQueue::on_initialize()`. fn next_block() -> Weight { MessageQueue::on_finalize(System::block_number()); diff --git a/frame/message-queue/src/lib.rs b/frame/message-queue/src/lib.rs index 2b64e3fc9..6c264be3c 100644 --- a/frame/message-queue/src/lib.rs +++ b/frame/message-queue/src/lib.rs @@ -533,6 +533,11 @@ pub mod pallet { Queued, /// There is temporarily not enough weight to continue servicing messages. InsufficientWeight, + /// This message is temporarily unprocessable. + /// + /// Such errors are expected, but not guaranteed, to resolve themselves eventually through + /// retrying. + TemporarilyUnprocessable, } /// The index of the first and last (non-empty) pages. @@ -588,6 +593,9 @@ pub mod pallet { /// Execute an overweight message. /// + /// Temporary processing errors will be propagated whereas permanent errors are treated + /// as success condition. + /// /// - `origin`: Must be `Signed`. /// - `message_origin`: The origin from which the message to be executed arrived. /// - `page`: The page in the queue in which the message to be executed is sitting. @@ -621,6 +629,10 @@ pub mod pallet { enum PageExecutionStatus { /// The execution bailed because there was not enough weight remaining. Bailed, + /// The page did not make any progress on its execution. + /// + /// This is a transient condition and can be handled by retrying - exactly like [Bailed]. + NoProgress, /// No more messages could be loaded. This does _not_ imply `page.is_complete()`. /// /// The reasons for this status are: @@ -634,6 +646,10 @@ enum PageExecutionStatus { enum ItemExecutionStatus { /// The execution bailed because there was not enough weight remaining. Bailed, + /// The item did not make any progress on its execution. + /// + /// This is a transient condition and can be handled by retrying - exactly like [Bailed]. + NoProgress, /// The item was not found. NoItem, /// Whether the execution of an item resulted in it being processed. @@ -651,8 +667,8 @@ enum MessageExecutionStatus { Overweight, /// The message was processed successfully. Processed, - /// The message was processed and resulted in a permanent error. - Unprocessable, + /// The message was processed and resulted in a, possibly permanent, error. + Unprocessable { permanent: bool }, } impl Pallet { @@ -814,7 +830,8 @@ impl Pallet { // additional overweight event being deposited. ) { Overweight | InsufficientWeight => Err(Error::::InsufficientWeight), - Unprocessable | Processed => { + Unprocessable { permanent: false } => Err(Error::::TemporarilyUnprocessable), + Unprocessable { permanent: true } | Processed => { page.note_processed_at_pos(pos); book_state.message_count.saturating_dec(); book_state.size.saturating_reduce(payload_len); @@ -921,6 +938,7 @@ impl Pallet { weight: &mut WeightMeter, overweight_limit: Weight, ) -> (bool, Option>) { + use PageExecutionStatus::*; if !weight.check_accrue( T::WeightInfo::service_queue_base().saturating_add(T::WeightInfo::ready_ring_unknit()), ) { @@ -936,9 +954,9 @@ impl Pallet { total_processed.saturating_accrue(processed); match status { // Store the page progress and do not go to the next one. - PageExecutionStatus::Bailed => break, + Bailed | NoProgress => break, // Go to the next page if this one is at the end. - PageExecutionStatus::NoMore => (), + NoMore => (), }; book_state.begin.saturating_inc(); } @@ -1003,6 +1021,7 @@ impl Pallet { ) { Bailed => break PageExecutionStatus::Bailed, NoItem => break PageExecutionStatus::NoMore, + NoProgress => break PageExecutionStatus::NoProgress, // Keep going as long as we make progress... Executed(true) => total_processed.saturating_inc(), Executed(false) => (), @@ -1053,7 +1072,8 @@ impl Pallet { overweight_limit, ) { InsufficientWeight => return ItemExecutionStatus::Bailed, - Processed | Unprocessable => true, + Unprocessable { permanent: false } => return ItemExecutionStatus::NoProgress, + Processed | Unprocessable { permanent: true } => true, Overweight => false, }; @@ -1125,12 +1145,14 @@ impl Pallet { page_index: PageIndex, message_index: T::Size, message: &[u8], - weight: &mut WeightMeter, + meter: &mut WeightMeter, overweight_limit: Weight, ) -> MessageExecutionStatus { let hash = T::Hashing::hash(message); - use ProcessMessageError::Overweight; - match T::MessageProcessor::process_message(message, origin.clone(), weight.remaining()) { + use ProcessMessageError::*; + let prev_consumed = meter.consumed; + + match T::MessageProcessor::process_message(message, origin.clone(), meter) { Err(Overweight(w)) if w.any_gt(overweight_limit) => { // Permanently overweight. Self::deposit_event(Event::::OverweightEnqueued { @@ -1146,16 +1168,19 @@ impl Pallet { // queue. MessageExecutionStatus::InsufficientWeight }, - Err(error) => { + Err(Yield) => { + // Processing should be reattempted later. + MessageExecutionStatus::Unprocessable { permanent: false } + }, + Err(error @ BadFormat | error @ Corrupt | error @ Unsupported) => { // Permanent error - drop Self::deposit_event(Event::::ProcessingFailed { hash, origin, error }); - MessageExecutionStatus::Unprocessable + MessageExecutionStatus::Unprocessable { permanent: true } }, - Ok((success, weight_used)) => { + Ok(success) => { // Success - weight.defensive_saturating_accrue(weight_used); - let event = Event::::Processed { hash, origin, weight_used, success }; - Self::deposit_event(event); + let weight_used = meter.consumed.saturating_sub(prev_consumed); + Self::deposit_event(Event::::Processed { hash, origin, weight_used, success }); MessageExecutionStatus::Processed }, } diff --git a/frame/message-queue/src/mock.rs b/frame/message-queue/src/mock.rs index 8817ebc35..28a599bcf 100644 --- a/frame/message-queue/src/mock.rs +++ b/frame/message-queue/src/mock.rs @@ -154,6 +154,7 @@ impl crate::weights::WeightInfo for MockedWeightInfo { parameter_types! { pub static MessagesProcessed: Vec<(Vec, MessageOrigin)> = vec![]; + pub static SuspendedQueues: Vec = vec![]; } /// A message processor which records all processed messages into [`MessagesProcessed`]. @@ -170,9 +171,9 @@ impl ProcessMessage for RecordingMessageProcessor { fn process_message( message: &[u8], origin: Self::Origin, - weight_limit: Weight, - ) -> Result<(bool, Weight), ProcessMessageError> { - processing_message(message)?; + meter: &mut WeightMeter, + ) -> Result { + processing_message(message, &origin)?; let weight = if message.starts_with(&b"weight="[..]) { let mut w: u64 = 0; @@ -187,22 +188,26 @@ impl ProcessMessage for RecordingMessageProcessor { } else { 1 }; - let weight = Weight::from_parts(weight, weight); + let required = Weight::from_parts(weight, weight); - if weight.all_lte(weight_limit) { + if meter.check_accrue(required) { let mut m = MessagesProcessed::get(); m.push((message.to_vec(), origin)); MessagesProcessed::set(m); - Ok((true, weight)) + Ok(true) } else { - Err(ProcessMessageError::Overweight(weight)) + Err(ProcessMessageError::Overweight(required)) } } } -/// Processed a mocked message. Messages that end with `badformat`, `corrupt` or `unsupported` will -/// fail with the respective error. -fn processing_message(msg: &[u8]) -> Result<(), ProcessMessageError> { +/// Processed a mocked message. Messages that end with `badformat`, `corrupt`, `unsupported` or +/// `yield` will fail with an error respectively. +fn processing_message(msg: &[u8], origin: &MessageOrigin) -> Result<(), ProcessMessageError> { + if SuspendedQueues::get().contains(&origin) { + return Err(ProcessMessageError::Yield) + } + let msg = String::from_utf8_lossy(msg); if msg.ends_with("badformat") { Err(ProcessMessageError::BadFormat) @@ -210,6 +215,8 @@ fn processing_message(msg: &[u8]) -> Result<(), ProcessMessageError> { Err(ProcessMessageError::Corrupt) } else if msg.ends_with("unsupported") { Err(ProcessMessageError::Unsupported) + } else if msg.ends_with("yield") { + Err(ProcessMessageError::Yield) } else { Ok(()) } @@ -230,20 +237,20 @@ impl ProcessMessage for CountingMessageProcessor { fn process_message( message: &[u8], - _origin: Self::Origin, - weight_limit: Weight, - ) -> Result<(bool, Weight), ProcessMessageError> { - if let Err(e) = processing_message(message) { + origin: Self::Origin, + meter: &mut WeightMeter, + ) -> Result { + if let Err(e) = processing_message(message, &origin) { NumMessagesErrored::set(NumMessagesErrored::get() + 1); return Err(e) } - let weight = Weight::from_parts(1, 1); + let required = Weight::from_parts(1, 1); - if weight.all_lte(weight_limit) { + if meter.check_accrue(required) { NumMessagesProcessed::set(NumMessagesProcessed::get() + 1); - Ok((true, weight)) + Ok(true) } else { - Err(ProcessMessageError::Overweight(weight)) + Err(ProcessMessageError::Overweight(required)) } } } @@ -285,7 +292,11 @@ pub fn set_weight(name: &str, w: Weight) { /// Assert that exactly these pages are present. Assumes `Here` origin. pub fn assert_pages(indices: &[u32]) { - assert_eq!(Pages::::iter().count(), indices.len()); + assert_eq!( + Pages::::iter_keys().count(), + indices.len(), + "Wrong number of pages in the queue" + ); for i in indices { assert!(Pages::::contains_key(MessageOrigin::Here, i)); } diff --git a/frame/message-queue/src/mock_helpers.rs b/frame/message-queue/src/mock_helpers.rs index 64fc70057..716a60782 100644 --- a/frame/message-queue/src/mock_helpers.rs +++ b/frame/message-queue/src/mock_helpers.rs @@ -47,22 +47,28 @@ impl From for MessageOrigin { } } -/// Processes any message and consumes (1, 1) weight per message. -pub struct NoopMessageProcessor; -impl ProcessMessage for NoopMessageProcessor { - type Origin = MessageOrigin; +/// Processes any message and consumes `(REQUIRED_WEIGHT, REQUIRED_WEIGHT)` weight. +/// +/// Returns [ProcessMessageError::Overweight] error if the weight limit is not sufficient. +pub struct NoopMessageProcessor(PhantomData); +impl ProcessMessage + for NoopMessageProcessor +where + Origin: codec::FullCodec + MaxEncodedLen + Clone + Eq + PartialEq + TypeInfo + Debug, +{ + type Origin = Origin; fn process_message( _message: &[u8], _origin: Self::Origin, - weight_limit: Weight, - ) -> Result<(bool, Weight), ProcessMessageError> { - let weight = Weight::from_parts(1, 1); + meter: &mut WeightMeter, + ) -> Result { + let required = Weight::from_parts(REQUIRED_WEIGHT, REQUIRED_WEIGHT); - if weight.all_lte(weight_limit) { - Ok((true, weight)) + if meter.check_accrue(required) { + Ok(true) } else { - Err(ProcessMessageError::Overweight(weight)) + Err(ProcessMessageError::Overweight(required)) } } } diff --git a/frame/message-queue/src/tests.rs b/frame/message-queue/src/tests.rs index f4cbd81ac..d3b0555f2 100644 --- a/frame/message-queue/src/tests.rs +++ b/frame/message-queue/src/tests.rs @@ -171,8 +171,9 @@ fn service_queues_failing_messages_works() { MessageQueue::enqueue_message(msg("badformat"), Here); MessageQueue::enqueue_message(msg("corrupt"), Here); MessageQueue::enqueue_message(msg("unsupported"), Here); - // Starts with three pages. - assert_pages(&[0, 1, 2]); + MessageQueue::enqueue_message(msg("yield"), Here); + // Starts with four pages. + assert_pages(&[0, 1, 2, 3]); assert_eq!(MessageQueue::service_queues(1.into_weight()), 1.into_weight()); assert_last_event::( @@ -201,8 +202,65 @@ fn service_queues_failing_messages_works() { } .into(), ); - // All pages removed. - assert_pages(&[]); + assert_eq!(MessageQueue::service_queues(1.into_weight()), 1.into_weight()); + assert_eq!(System::events().len(), 3); + // Last page with the `yield` stays in. + assert_pages(&[3]); + }); +} + +#[test] +fn service_queues_suspension_works() { + use MessageOrigin::*; + new_test_ext::().execute_with(|| { + MessageQueue::enqueue_messages(vec![msg("a"), msg("b"), msg("c")].into_iter(), Here); + MessageQueue::enqueue_messages(vec![msg("x"), msg("y"), msg("z")].into_iter(), There); + MessageQueue::enqueue_messages( + vec![msg("m"), msg("n"), msg("o")].into_iter(), + Everywhere(0), + ); + assert_eq!(QueueChanges::take(), vec![(Here, 3, 3), (There, 3, 3), (Everywhere(0), 3, 3)]); + + // Service one message from `Here`. + assert_eq!(MessageQueue::service_queues(1.into_weight()), 1.into_weight()); + assert_eq!(MessagesProcessed::take(), vec![(vmsg("a"), Here)]); + assert_eq!(QueueChanges::take(), vec![(Here, 2, 2)]); + + // Pause queue `Here` and `Everywhere(0)`. + SuspendedQueues::set(vec![Here, Everywhere(0)]); + + // Service one message from `There`. + assert_eq!(MessageQueue::service_queues(1.into_weight()), 1.into_weight()); + assert_eq!(MessagesProcessed::take(), vec![(vmsg("x"), There)]); + assert_eq!(QueueChanges::take(), vec![(There, 2, 2)]); + + // Now it would normally swap to `Everywhere(0)` and `Here`, but they are paused so we + // expect `There` again. + assert_eq!(MessageQueue::service_queues(2.into_weight()), 2.into_weight()); + assert_eq!(MessagesProcessed::take(), vec![(vmsg("y"), There), (vmsg("z"), There)]); + + // Processing with max-weight won't do anything. + assert_eq!(MessageQueue::service_queues(Weight::MAX), Weight::zero()); + assert_eq!(MessageQueue::service_queues(Weight::MAX), Weight::zero()); + + // ... until we resume `Here`: + SuspendedQueues::set(vec![Everywhere(0)]); + assert_eq!(MessageQueue::service_queues(Weight::MAX), 2.into_weight()); + assert_eq!(MessagesProcessed::take(), vec![(vmsg("b"), Here), (vmsg("c"), Here)]); + + // Everywhere still won't move. + assert_eq!(MessageQueue::service_queues(Weight::MAX), Weight::zero()); + SuspendedQueues::take(); + // Resume `Everywhere(0)` makes it work. + assert_eq!(MessageQueue::service_queues(Weight::MAX), 3.into_weight()); + assert_eq!( + MessagesProcessed::take(), + vec![ + (vmsg("m"), Everywhere(0)), + (vmsg("n"), Everywhere(0)), + (vmsg("o"), Everywhere(0)) + ] + ); }); } @@ -379,7 +437,7 @@ fn service_page_works() { assert_eq!(status, Bailed); } } - assert!(!Pages::::contains_key(Here, 0), "The page got removed"); + assert_pages(&[]); }); } @@ -445,6 +503,57 @@ fn service_page_item_bails() { }); } +#[test] +fn service_page_suspension_works() { + use super::integration_test::Test; // Run with larger page size. + use MessageOrigin::*; + use PageExecutionStatus::*; + + new_test_ext::().execute_with(|| { + let (page, mut msgs) = full_page::(); + assert!(msgs >= 10, "pre-condition: need at least 10 msgs per page"); + let mut book = book_for::(&page); + Pages::::insert(Here, 0, page); + + // First we process 5 messages from this page. + let mut meter = WeightMeter::from_limit(5.into_weight()); + let (_, status) = + crate::Pallet::::service_page(&Here, &mut book, &mut meter, Weight::MAX); + + assert_eq!(NumMessagesProcessed::take(), 5); + assert!(meter.remaining().is_zero()); + assert_eq!(status, Bailed); // It bailed since weight is missing. + msgs -= 5; + + // Then we pause the queue. + SuspendedQueues::set(vec![Here]); + // Noting happens... + for _ in 0..5 { + let (_, status) = crate::Pallet::::service_page( + &Here, + &mut book, + &mut WeightMeter::max_limit(), + Weight::MAX, + ); + assert_eq!(status, NoProgress); + assert!(NumMessagesProcessed::take().is_zero()); + } + + // Resume and process all remaining. + SuspendedQueues::take(); + let (_, status) = crate::Pallet::::service_page( + &Here, + &mut book, + &mut WeightMeter::max_limit(), + Weight::MAX, + ); + assert_eq!(status, NoMore); + assert_eq!(NumMessagesProcessed::take(), msgs); + + assert!(Pages::::iter_keys().count().is_zero()); + }); +} + #[test] fn bump_service_head_works() { use MessageOrigin::*; diff --git a/frame/message-queue/src/weights.rs b/frame/message-queue/src/weights.rs index be2f801f1..b48cf811a 100644 --- a/frame/message-queue/src/weights.rs +++ b/frame/message-queue/src/weights.rs @@ -18,25 +18,26 @@ //! Autogenerated weights for pallet_message_queue //! //! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev -//! DATE: 2023-01-24, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! DATE: 2023-02-24, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` //! WORST CASE MAP SIZE: `1000000` -//! HOSTNAME: `bm2`, CPU: `Intel(R) Core(TM) i7-7700K CPU @ 4.20GHz` +//! HOSTNAME: `bm3`, CPU: `Intel(R) Core(TM) i7-7700K CPU @ 4.20GHz` //! EXECUTION: Some(Wasm), WASM-EXECUTION: Compiled, CHAIN: Some("dev"), DB CACHE: 1024 // Executed Command: -// ./target/production/substrate +// target/production/substrate // benchmark // pallet -// --chain=dev // --steps=50 // --repeat=20 -// --pallet=pallet_message_queue // --extrinsic=* // --execution=wasm // --wasm-execution=compiled // --heap-pages=4096 -// --output=./frame/message-queue/src/weights.rs +// --json-file=/var/lib/gitlab-runner/builds/zyw4fam_/0/parity/mirrors/substrate/.git/.artifacts/bench.json +// --pallet=pallet_message_queue +// --chain=dev // --header=./HEADER-APACHE2 +// --output=./frame/message-queue/src/weights.rs // --template=./.maintain/frame-weight-template.hbs #![cfg_attr(rustfmt, rustfmt_skip)] @@ -64,61 +65,66 @@ pub trait WeightInfo { pub struct SubstrateWeight(PhantomData); impl WeightInfo for SubstrateWeight { /// Storage: MessageQueue ServiceHead (r:1 w:0) - /// Proof: MessageQueue ServiceHead (max_values: Some(1), max_size: Some(5), added: 500, mode: MaxEncodedLen) + /// Proof: MessageQueue ServiceHead (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) /// Storage: MessageQueue BookStateFor (r:2 w:2) - /// Proof: MessageQueue BookStateFor (max_values: None, max_size: Some(52), added: 2527, mode: MaxEncodedLen) + /// Proof: MessageQueue BookStateFor (max_values: None, max_size: Some(49), added: 2524, mode: MaxEncodedLen) fn ready_ring_knit() -> Weight { // Proof Size summary in bytes: - // Measured: `837` - // Estimated: `5554` - // Minimum execution time: 12_676 nanoseconds. - Weight::from_parts(13_113_000, 5554) + // Measured: `829` + // Estimated: `5547` + // Minimum execution time: 15_241 nanoseconds. + Weight::from_ref_time(15_603_000) + .saturating_add(Weight::from_proof_size(5547)) .saturating_add(T::DbWeight::get().reads(3_u64)) .saturating_add(T::DbWeight::get().writes(2_u64)) } /// Storage: MessageQueue BookStateFor (r:2 w:2) - /// Proof: MessageQueue BookStateFor (max_values: None, max_size: Some(52), added: 2527, mode: MaxEncodedLen) + /// Proof: MessageQueue BookStateFor (max_values: None, max_size: Some(49), added: 2524, mode: MaxEncodedLen) /// Storage: MessageQueue ServiceHead (r:1 w:1) - /// Proof: MessageQueue ServiceHead (max_values: Some(1), max_size: Some(5), added: 500, mode: MaxEncodedLen) + /// Proof: MessageQueue ServiceHead (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) fn ready_ring_unknit() -> Weight { // Proof Size summary in bytes: - // Measured: `837` - // Estimated: `5554` - // Minimum execution time: 12_654 nanoseconds. - Weight::from_parts(12_969_000, 5554) + // Measured: `829` + // Estimated: `5547` + // Minimum execution time: 14_652 nanoseconds. + Weight::from_ref_time(14_983_000) + .saturating_add(Weight::from_proof_size(5547)) .saturating_add(T::DbWeight::get().reads(3_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) } /// Storage: MessageQueue BookStateFor (r:1 w:1) - /// Proof: MessageQueue BookStateFor (max_values: None, max_size: Some(52), added: 2527, mode: MaxEncodedLen) + /// Proof: MessageQueue BookStateFor (max_values: None, max_size: Some(49), added: 2524, mode: MaxEncodedLen) fn service_queue_base() -> Weight { // Proof Size summary in bytes: // Measured: `576` - // Estimated: `2527` - // Minimum execution time: 5_096 nanoseconds. - Weight::from_parts(5_280_000, 2527) + // Estimated: `2524` + // Minimum execution time: 5_750 nanoseconds. + Weight::from_ref_time(6_003_000) + .saturating_add(Weight::from_proof_size(2524)) .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } /// Storage: MessageQueue Pages (r:1 w:1) - /// Proof: MessageQueue Pages (max_values: None, max_size: Some(65585), added: 68060, mode: MaxEncodedLen) + /// Proof: MessageQueue Pages (max_values: None, max_size: Some(65584), added: 68059, mode: MaxEncodedLen) fn service_page_base_completion() -> Weight { // Proof Size summary in bytes: - // Measured: `648` - // Estimated: `68060` - // Minimum execution time: 7_291 nanoseconds. - Weight::from_parts(7_564_000, 68060) + // Measured: `647` + // Estimated: `68059` + // Minimum execution time: 8_257 nanoseconds. + Weight::from_ref_time(8_506_000) + .saturating_add(Weight::from_proof_size(68059)) .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } /// Storage: MessageQueue Pages (r:1 w:1) - /// Proof: MessageQueue Pages (max_values: None, max_size: Some(65585), added: 68060, mode: MaxEncodedLen) + /// Proof: MessageQueue Pages (max_values: None, max_size: Some(65584), added: 68059, mode: MaxEncodedLen) fn service_page_base_no_completion() -> Weight { // Proof Size summary in bytes: - // Measured: `648` - // Estimated: `68060` - // Minimum execution time: 7_401 nanoseconds. - Weight::from_parts(7_681_000, 68060) + // Measured: `647` + // Estimated: `68059` + // Minimum execution time: 8_422 nanoseconds. + Weight::from_ref_time(8_589_000) + .saturating_add(Weight::from_proof_size(68059)) .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -126,58 +132,63 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `972` // Estimated: `0` - // Minimum execution time: 79_412 nanoseconds. - Weight::from_ref_time(79_816_000) + // Minimum execution time: 81_929 nanoseconds. + Weight::from_ref_time(82_375_000) + .saturating_add(Weight::from_proof_size(0)) } /// Storage: MessageQueue ServiceHead (r:1 w:1) - /// Proof: MessageQueue ServiceHead (max_values: Some(1), max_size: Some(5), added: 500, mode: MaxEncodedLen) + /// Proof: MessageQueue ServiceHead (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) /// Storage: MessageQueue BookStateFor (r:1 w:0) - /// Proof: MessageQueue BookStateFor (max_values: None, max_size: Some(52), added: 2527, mode: MaxEncodedLen) + /// Proof: MessageQueue BookStateFor (max_values: None, max_size: Some(49), added: 2524, mode: MaxEncodedLen) fn bump_service_head() -> Weight { // Proof Size summary in bytes: - // Measured: `712` - // Estimated: `3027` - // Minimum execution time: 8_258 nanoseconds. - Weight::from_parts(8_438_000, 3027) + // Measured: `706` + // Estimated: `3023` + // Minimum execution time: 8_992 nanoseconds. + Weight::from_ref_time(9_200_000) + .saturating_add(Weight::from_proof_size(3023)) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } /// Storage: MessageQueue BookStateFor (r:1 w:1) - /// Proof: MessageQueue BookStateFor (max_values: None, max_size: Some(52), added: 2527, mode: MaxEncodedLen) + /// Proof: MessageQueue BookStateFor (max_values: None, max_size: Some(49), added: 2524, mode: MaxEncodedLen) /// Storage: MessageQueue Pages (r:1 w:1) - /// Proof: MessageQueue Pages (max_values: None, max_size: Some(65585), added: 68060, mode: MaxEncodedLen) + /// Proof: MessageQueue Pages (max_values: None, max_size: Some(65584), added: 68059, mode: MaxEncodedLen) fn reap_page() -> Weight { // Proof Size summary in bytes: - // Measured: `66827` - // Estimated: `70587` - // Minimum execution time: 61_361 nanoseconds. - Weight::from_parts(62_103_000, 70587) + // Measured: `66825` + // Estimated: `70583` + // Minimum execution time: 68_292 nanoseconds. + Weight::from_ref_time(69_108_000) + .saturating_add(Weight::from_proof_size(70583)) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().writes(2_u64)) } /// Storage: MessageQueue BookStateFor (r:1 w:1) - /// Proof: MessageQueue BookStateFor (max_values: None, max_size: Some(52), added: 2527, mode: MaxEncodedLen) + /// Proof: MessageQueue BookStateFor (max_values: None, max_size: Some(49), added: 2524, mode: MaxEncodedLen) /// Storage: MessageQueue Pages (r:1 w:1) - /// Proof: MessageQueue Pages (max_values: None, max_size: Some(65585), added: 68060, mode: MaxEncodedLen) + /// Proof: MessageQueue Pages (max_values: None, max_size: Some(65584), added: 68059, mode: MaxEncodedLen) fn execute_overweight_page_removed() -> Weight { // Proof Size summary in bytes: - // Measured: `66827` - // Estimated: `70587` - // Minimum execution time: 75_153 nanoseconds. - Weight::from_parts(76_093_000, 70587) + // Measured: `66825` + // Estimated: `70583` + // Minimum execution time: 83_855 nanoseconds. + Weight::from_ref_time(84_946_000) + .saturating_add(Weight::from_proof_size(70583)) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().writes(2_u64)) } /// Storage: MessageQueue BookStateFor (r:1 w:1) - /// Proof: MessageQueue BookStateFor (max_values: None, max_size: Some(52), added: 2527, mode: MaxEncodedLen) + /// Proof: MessageQueue BookStateFor (max_values: None, max_size: Some(49), added: 2524, mode: MaxEncodedLen) /// Storage: MessageQueue Pages (r:1 w:1) - /// Proof: MessageQueue Pages (max_values: None, max_size: Some(65585), added: 68060, mode: MaxEncodedLen) + /// Proof: MessageQueue Pages (max_values: None, max_size: Some(65584), added: 68059, mode: MaxEncodedLen) fn execute_overweight_page_updated() -> Weight { // Proof Size summary in bytes: - // Measured: `66827` - // Estimated: `70587` - // Minimum execution time: 88_272 nanoseconds. - Weight::from_parts(89_373_000, 70587) + // Measured: `66825` + // Estimated: `70583` + // Minimum execution time: 96_997 nanoseconds. + Weight::from_ref_time(98_668_000) + .saturating_add(Weight::from_proof_size(70583)) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().writes(2_u64)) } @@ -186,61 +197,66 @@ impl WeightInfo for SubstrateWeight { // For backwards compatibility and tests impl WeightInfo for () { /// Storage: MessageQueue ServiceHead (r:1 w:0) - /// Proof: MessageQueue ServiceHead (max_values: Some(1), max_size: Some(5), added: 500, mode: MaxEncodedLen) + /// Proof: MessageQueue ServiceHead (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) /// Storage: MessageQueue BookStateFor (r:2 w:2) - /// Proof: MessageQueue BookStateFor (max_values: None, max_size: Some(52), added: 2527, mode: MaxEncodedLen) + /// Proof: MessageQueue BookStateFor (max_values: None, max_size: Some(49), added: 2524, mode: MaxEncodedLen) fn ready_ring_knit() -> Weight { // Proof Size summary in bytes: - // Measured: `837` - // Estimated: `5554` - // Minimum execution time: 12_676 nanoseconds. - Weight::from_parts(13_113_000, 5554) + // Measured: `829` + // Estimated: `5547` + // Minimum execution time: 15_241 nanoseconds. + Weight::from_ref_time(15_603_000) + .saturating_add(Weight::from_proof_size(5547)) .saturating_add(RocksDbWeight::get().reads(3_u64)) .saturating_add(RocksDbWeight::get().writes(2_u64)) } /// Storage: MessageQueue BookStateFor (r:2 w:2) - /// Proof: MessageQueue BookStateFor (max_values: None, max_size: Some(52), added: 2527, mode: MaxEncodedLen) + /// Proof: MessageQueue BookStateFor (max_values: None, max_size: Some(49), added: 2524, mode: MaxEncodedLen) /// Storage: MessageQueue ServiceHead (r:1 w:1) - /// Proof: MessageQueue ServiceHead (max_values: Some(1), max_size: Some(5), added: 500, mode: MaxEncodedLen) + /// Proof: MessageQueue ServiceHead (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) fn ready_ring_unknit() -> Weight { // Proof Size summary in bytes: - // Measured: `837` - // Estimated: `5554` - // Minimum execution time: 12_654 nanoseconds. - Weight::from_parts(12_969_000, 5554) + // Measured: `829` + // Estimated: `5547` + // Minimum execution time: 14_652 nanoseconds. + Weight::from_ref_time(14_983_000) + .saturating_add(Weight::from_proof_size(5547)) .saturating_add(RocksDbWeight::get().reads(3_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) } /// Storage: MessageQueue BookStateFor (r:1 w:1) - /// Proof: MessageQueue BookStateFor (max_values: None, max_size: Some(52), added: 2527, mode: MaxEncodedLen) + /// Proof: MessageQueue BookStateFor (max_values: None, max_size: Some(49), added: 2524, mode: MaxEncodedLen) fn service_queue_base() -> Weight { // Proof Size summary in bytes: // Measured: `576` - // Estimated: `2527` - // Minimum execution time: 5_096 nanoseconds. - Weight::from_parts(5_280_000, 2527) + // Estimated: `2524` + // Minimum execution time: 5_750 nanoseconds. + Weight::from_ref_time(6_003_000) + .saturating_add(Weight::from_proof_size(2524)) .saturating_add(RocksDbWeight::get().reads(1_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } /// Storage: MessageQueue Pages (r:1 w:1) - /// Proof: MessageQueue Pages (max_values: None, max_size: Some(65585), added: 68060, mode: MaxEncodedLen) + /// Proof: MessageQueue Pages (max_values: None, max_size: Some(65584), added: 68059, mode: MaxEncodedLen) fn service_page_base_completion() -> Weight { // Proof Size summary in bytes: - // Measured: `648` - // Estimated: `68060` - // Minimum execution time: 7_291 nanoseconds. - Weight::from_parts(7_564_000, 68060) + // Measured: `647` + // Estimated: `68059` + // Minimum execution time: 8_257 nanoseconds. + Weight::from_ref_time(8_506_000) + .saturating_add(Weight::from_proof_size(68059)) .saturating_add(RocksDbWeight::get().reads(1_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } /// Storage: MessageQueue Pages (r:1 w:1) - /// Proof: MessageQueue Pages (max_values: None, max_size: Some(65585), added: 68060, mode: MaxEncodedLen) + /// Proof: MessageQueue Pages (max_values: None, max_size: Some(65584), added: 68059, mode: MaxEncodedLen) fn service_page_base_no_completion() -> Weight { // Proof Size summary in bytes: - // Measured: `648` - // Estimated: `68060` - // Minimum execution time: 7_401 nanoseconds. - Weight::from_parts(7_681_000, 68060) + // Measured: `647` + // Estimated: `68059` + // Minimum execution time: 8_422 nanoseconds. + Weight::from_ref_time(8_589_000) + .saturating_add(Weight::from_proof_size(68059)) .saturating_add(RocksDbWeight::get().reads(1_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } @@ -248,58 +264,63 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `972` // Estimated: `0` - // Minimum execution time: 79_412 nanoseconds. - Weight::from_ref_time(79_816_000) + // Minimum execution time: 81_929 nanoseconds. + Weight::from_ref_time(82_375_000) + .saturating_add(Weight::from_proof_size(0)) } /// Storage: MessageQueue ServiceHead (r:1 w:1) - /// Proof: MessageQueue ServiceHead (max_values: Some(1), max_size: Some(5), added: 500, mode: MaxEncodedLen) + /// Proof: MessageQueue ServiceHead (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) /// Storage: MessageQueue BookStateFor (r:1 w:0) - /// Proof: MessageQueue BookStateFor (max_values: None, max_size: Some(52), added: 2527, mode: MaxEncodedLen) + /// Proof: MessageQueue BookStateFor (max_values: None, max_size: Some(49), added: 2524, mode: MaxEncodedLen) fn bump_service_head() -> Weight { // Proof Size summary in bytes: - // Measured: `712` - // Estimated: `3027` - // Minimum execution time: 8_258 nanoseconds. - Weight::from_parts(8_438_000, 3027) + // Measured: `706` + // Estimated: `3023` + // Minimum execution time: 8_992 nanoseconds. + Weight::from_ref_time(9_200_000) + .saturating_add(Weight::from_proof_size(3023)) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } /// Storage: MessageQueue BookStateFor (r:1 w:1) - /// Proof: MessageQueue BookStateFor (max_values: None, max_size: Some(52), added: 2527, mode: MaxEncodedLen) + /// Proof: MessageQueue BookStateFor (max_values: None, max_size: Some(49), added: 2524, mode: MaxEncodedLen) /// Storage: MessageQueue Pages (r:1 w:1) - /// Proof: MessageQueue Pages (max_values: None, max_size: Some(65585), added: 68060, mode: MaxEncodedLen) + /// Proof: MessageQueue Pages (max_values: None, max_size: Some(65584), added: 68059, mode: MaxEncodedLen) fn reap_page() -> Weight { // Proof Size summary in bytes: - // Measured: `66827` - // Estimated: `70587` - // Minimum execution time: 61_361 nanoseconds. - Weight::from_parts(62_103_000, 70587) + // Measured: `66825` + // Estimated: `70583` + // Minimum execution time: 68_292 nanoseconds. + Weight::from_ref_time(69_108_000) + .saturating_add(Weight::from_proof_size(70583)) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().writes(2_u64)) } /// Storage: MessageQueue BookStateFor (r:1 w:1) - /// Proof: MessageQueue BookStateFor (max_values: None, max_size: Some(52), added: 2527, mode: MaxEncodedLen) + /// Proof: MessageQueue BookStateFor (max_values: None, max_size: Some(49), added: 2524, mode: MaxEncodedLen) /// Storage: MessageQueue Pages (r:1 w:1) - /// Proof: MessageQueue Pages (max_values: None, max_size: Some(65585), added: 68060, mode: MaxEncodedLen) + /// Proof: MessageQueue Pages (max_values: None, max_size: Some(65584), added: 68059, mode: MaxEncodedLen) fn execute_overweight_page_removed() -> Weight { // Proof Size summary in bytes: - // Measured: `66827` - // Estimated: `70587` - // Minimum execution time: 75_153 nanoseconds. - Weight::from_parts(76_093_000, 70587) + // Measured: `66825` + // Estimated: `70583` + // Minimum execution time: 83_855 nanoseconds. + Weight::from_ref_time(84_946_000) + .saturating_add(Weight::from_proof_size(70583)) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().writes(2_u64)) } /// Storage: MessageQueue BookStateFor (r:1 w:1) - /// Proof: MessageQueue BookStateFor (max_values: None, max_size: Some(52), added: 2527, mode: MaxEncodedLen) + /// Proof: MessageQueue BookStateFor (max_values: None, max_size: Some(49), added: 2524, mode: MaxEncodedLen) /// Storage: MessageQueue Pages (r:1 w:1) - /// Proof: MessageQueue Pages (max_values: None, max_size: Some(65585), added: 68060, mode: MaxEncodedLen) + /// Proof: MessageQueue Pages (max_values: None, max_size: Some(65584), added: 68059, mode: MaxEncodedLen) fn execute_overweight_page_updated() -> Weight { // Proof Size summary in bytes: - // Measured: `66827` - // Estimated: `70587` - // Minimum execution time: 88_272 nanoseconds. - Weight::from_parts(89_373_000, 70587) + // Measured: `66825` + // Estimated: `70583` + // Minimum execution time: 96_997 nanoseconds. + Weight::from_ref_time(98_668_000) + .saturating_add(Weight::from_proof_size(70583)) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().writes(2_u64)) } diff --git a/frame/support/src/traits.rs b/frame/support/src/traits.rs index a649dad40..da8efe6af 100644 --- a/frame/support/src/traits.rs +++ b/frame/support/src/traits.rs @@ -114,8 +114,8 @@ pub use preimages::{Bounded, BoundedInline, FetchResult, Hash, QueryPreimage, St mod messages; pub use messages::{ - EnqueueMessage, ExecuteOverweightError, Footprint, ProcessMessage, ProcessMessageError, - ServiceQueues, + EnqueueMessage, ExecuteOverweightError, Footprint, NoopServiceQueues, ProcessMessage, + ProcessMessageError, ServiceQueues, TransformOrigin, }; #[cfg(feature = "try-runtime")] diff --git a/frame/support/src/traits/messages.rs b/frame/support/src/traits/messages.rs index 637aa7fdc..781da3ed6 100644 --- a/frame/support/src/traits/messages.rs +++ b/frame/support/src/traits/messages.rs @@ -22,7 +22,7 @@ use scale_info::TypeInfo; use sp_core::{ConstU32, Get, TypedGet}; use sp_runtime::{traits::Convert, BoundedSlice, RuntimeDebug}; use sp_std::{fmt::Debug, marker::PhantomData, prelude::*}; -use sp_weights::Weight; +use sp_weights::{Weight, WeightMeter}; /// Errors that can happen when attempting to process a message with /// [`ProcessMessage::process_message()`]. @@ -38,6 +38,13 @@ pub enum ProcessMessageError { /// would be respected. The parameter gives the maximum weight which the message could take /// to process. Overweight(Weight), + /// The queue wants to give up its current processing slot. + /// + /// Hints the message processor to cease servicing this queue and proceed to the next + /// one. This is seen as a *hint*, not an instruction. Implementations must therefore handle + /// the case that a queue is re-serviced within the same block after *yielding*. A queue is + /// not required to *yield* again when it is being re-serviced withing the same block. + Yield, } /// Can process messages from a specific origin. @@ -45,12 +52,14 @@ pub trait ProcessMessage { /// The transport from where a message originates. type Origin: FullCodec + MaxEncodedLen + Clone + Eq + PartialEq + TypeInfo + Debug; - /// Process the given message, using no more than `weight_limit` in weight to do so. + /// Process the given message, using no more than the remaining `meter` weight to do so. + /// + /// Returns whether the message was processed. fn process_message( message: &[u8], origin: Self::Origin, - weight_limit: Weight, - ) -> Result<(bool, Weight), ProcessMessageError>; + meter: &mut WeightMeter, + ) -> Result; } /// Errors that can happen when attempting to execute an overweight message with @@ -85,6 +94,16 @@ pub trait ServiceQueues { } } +/// Services queues by doing nothing. +pub struct NoopServiceQueues(PhantomData); +impl ServiceQueues for NoopServiceQueues { + type OverweightMessageAddress = OverweightAddr; + + fn service_queues(_: Weight) -> Weight { + Weight::zero() + } +} + /// The resource footprint of a queue. #[derive(Default, Copy, Clone, Eq, PartialEq, RuntimeDebug)] pub struct Footprint { From 3d8a0255994d0094342d88b9ce87cd97a15f647e Mon Sep 17 00:00:00 2001 From: Liu-Cheng Xu Date: Mon, 27 Feb 2023 17:02:54 +0800 Subject: [PATCH 163/558] Support the subscription of every imported block (#13372) * Support the subscription of every import block Close #13315 * Clean up any closed block import notification sinks * Apply review suggestions * Nit * `every_block_import_notification_sinks` -> `every_import_notification_sinks` * Apply review suggestions --- client/api/src/backend.rs | 15 +++ client/api/src/client.rs | 10 +- .../merkle-mountain-range/src/test_utils.rs | 4 + client/service/src/client/client.rs | 122 +++++++++++++----- 4 files changed, 120 insertions(+), 31 deletions(-) diff --git a/client/api/src/backend.rs b/client/api/src/backend.rs index 486ac5061..f991bea88 100644 --- a/client/api/src/backend.rs +++ b/client/api/src/backend.rs @@ -48,6 +48,19 @@ pub type TransactionForSB = >>::Trans /// Extracts the transaction for the given backend. pub type TransactionFor = TransactionForSB, Block>; +/// Describes which block import notification stream should be notified. +#[derive(Debug, Clone, Copy)] +pub enum ImportNotificationAction { + /// Notify only when the node has synced to the tip or there is a re-org. + RecentBlock, + /// Notify for every single block no matter what the sync state is. + EveryBlock, + /// Both block import notifications above should be fired. + Both, + /// No block import notification should be fired. + None, +} + /// Import operation summary. /// /// Contains information about the block that just got imported, @@ -67,6 +80,8 @@ pub struct ImportSummary { /// /// If `None`, there was no re-org while importing. pub tree_route: Option>, + /// What notify action to take for this import. + pub import_notification_action: ImportNotificationAction, } /// Finalization operation summary. diff --git a/client/api/src/client.rs b/client/api/src/client.rs index aeb119e0e..e334f2f9f 100644 --- a/client/api/src/client.rs +++ b/client/api/src/client.rs @@ -59,10 +59,16 @@ pub trait BlockOf { /// A source of blockchain events. pub trait BlockchainEvents { - /// Get block import event stream. Not guaranteed to be fired for every - /// imported block. + /// Get block import event stream. + /// + /// Not guaranteed to be fired for every imported block, only fired when the node + /// has synced to the tip or there is a re-org. Use `every_import_notification_stream()` + /// if you want a notification of every imported block regardless. fn import_notification_stream(&self) -> ImportNotifications; + /// Get a stream of every imported block. + fn every_import_notification_stream(&self) -> ImportNotifications; + /// Get a stream of finality notifications. Not guaranteed to be fired for every /// finalized block. fn finality_notification_stream(&self) -> FinalityNotifications; diff --git a/client/merkle-mountain-range/src/test_utils.rs b/client/merkle-mountain-range/src/test_utils.rs index 9e00d620a..010b48bb3 100644 --- a/client/merkle-mountain-range/src/test_utils.rs +++ b/client/merkle-mountain-range/src/test_utils.rs @@ -265,6 +265,10 @@ impl BlockchainEvents for MockClient { unimplemented!() } + fn every_import_notification_stream(&self) -> ImportNotifications { + unimplemented!() + } + fn finality_notification_stream(&self) -> FinalityNotifications { self.client.lock().finality_notification_stream() } diff --git a/client/service/src/client/client.rs b/client/service/src/client/client.rs index ddeec5c73..4b5822ae0 100644 --- a/client/service/src/client/client.rs +++ b/client/service/src/client/client.rs @@ -31,7 +31,7 @@ use sc_block_builder::{BlockBuilderApi, BlockBuilderProvider, RecordProof}; use sc_client_api::{ backend::{ self, apply_aux, BlockImportOperation, ClientImportOperation, FinalizeSummary, Finalizer, - ImportSummary, LockImportRun, NewBlockState, StorageProvider, + ImportNotificationAction, ImportSummary, LockImportRun, NewBlockState, StorageProvider, }, client::{ BadBlocks, BlockBackend, BlockImportNotification, BlockOf, BlockchainEvents, ClientInfo, @@ -106,6 +106,7 @@ where executor: E, storage_notifications: StorageNotifications, import_notification_sinks: NotificationSinks>, + every_import_notification_sinks: NotificationSinks>, finality_notification_sinks: NotificationSinks>, // Collects auxiliary operations to be performed atomically together with // block import operations. @@ -304,19 +305,22 @@ where FinalityNotification::from_summary(summary, self.unpin_worker_sender.clone()) }); - let (import_notification, storage_changes) = match notify_imported { - Some(mut summary) => { - let storage_changes = summary.storage_changes.take(); - ( - Some(BlockImportNotification::from_summary( - summary, - self.unpin_worker_sender.clone(), - )), - storage_changes, - ) - }, - None => (None, None), - }; + let (import_notification, storage_changes, import_notification_action) = + match notify_imported { + Some(mut summary) => { + let import_notification_action = summary.import_notification_action; + let storage_changes = summary.storage_changes.take(); + ( + Some(BlockImportNotification::from_summary( + summary, + self.unpin_worker_sender.clone(), + )), + storage_changes, + import_notification_action, + ) + }, + None => (None, None, ImportNotificationAction::None), + }; if let Some(ref notification) = finality_notification { for action in self.finality_actions.lock().iter_mut() { @@ -353,7 +357,7 @@ where } self.notify_finalized(finality_notification)?; - self.notify_imported(import_notification, storage_changes)?; + self.notify_imported(import_notification, import_notification_action, storage_changes)?; Ok(r) }; @@ -451,6 +455,7 @@ where executor, storage_notifications: StorageNotifications::new(prometheus_registry), import_notification_sinks: Default::default(), + every_import_notification_sinks: Default::default(), finality_notification_sinks: Default::default(), import_actions: Default::default(), finality_actions: Default::default(), @@ -769,11 +774,15 @@ where operation.op.insert_aux(aux)?; - // We only notify when we are already synced to the tip of the chain + let should_notify_every_block = !self.every_import_notification_sinks.lock().is_empty(); + + // Notify when we are already synced to the tip of the chain // or if this import triggers a re-org - if make_notifications || tree_route.is_some() { + let should_notify_recent_block = make_notifications || tree_route.is_some(); + + if should_notify_every_block || should_notify_recent_block { let header = import_headers.into_post(); - if finalized { + if finalized && should_notify_recent_block { let mut summary = match operation.notify_finalized.take() { Some(mut summary) => { summary.header = header.clone(); @@ -810,6 +819,16 @@ where operation.notify_finalized = Some(summary); } + let import_notification_action = if should_notify_every_block { + if should_notify_recent_block { + ImportNotificationAction::Both + } else { + ImportNotificationAction::EveryBlock + } + } else { + ImportNotificationAction::RecentBlock + }; + operation.notify_imported = Some(ImportSummary { hash, origin, @@ -817,6 +836,7 @@ where is_new_best, storage_changes, tree_route, + import_notification_action, }) } @@ -1012,6 +1032,7 @@ where fn notify_imported( &self, notification: Option>, + import_notification_action: ImportNotificationAction, storage_changes: Option<(StorageCollection, ChildStorageCollection)>, ) -> sp_blockchain::Result<()> { let notification = match notification { @@ -1024,22 +1045,59 @@ where // temporary leak of closed/discarded notification sinks (e.g. // from consensus code). self.import_notification_sinks.lock().retain(|sink| !sink.is_closed()); + + self.every_import_notification_sinks.lock().retain(|sink| !sink.is_closed()); + return Ok(()) }, }; - if let Some(storage_changes) = storage_changes { - // TODO [ToDr] How to handle re-orgs? Should we re-emit all storage changes? - self.storage_notifications.trigger( - ¬ification.hash, - storage_changes.0.into_iter(), - storage_changes.1.into_iter().map(|(sk, v)| (sk, v.into_iter())), - ); - } + let trigger_storage_changes_notification = || { + if let Some(storage_changes) = storage_changes { + // TODO [ToDr] How to handle re-orgs? Should we re-emit all storage changes? + self.storage_notifications.trigger( + ¬ification.hash, + storage_changes.0.into_iter(), + storage_changes.1.into_iter().map(|(sk, v)| (sk, v.into_iter())), + ); + } + }; + + match import_notification_action { + ImportNotificationAction::Both => { + trigger_storage_changes_notification(); + self.import_notification_sinks + .lock() + .retain(|sink| sink.unbounded_send(notification.clone()).is_ok()); + + self.every_import_notification_sinks + .lock() + .retain(|sink| sink.unbounded_send(notification.clone()).is_ok()); + }, + ImportNotificationAction::RecentBlock => { + trigger_storage_changes_notification(); + self.import_notification_sinks + .lock() + .retain(|sink| sink.unbounded_send(notification.clone()).is_ok()); + + self.every_import_notification_sinks.lock().retain(|sink| !sink.is_closed()); + }, + ImportNotificationAction::EveryBlock => { + self.every_import_notification_sinks + .lock() + .retain(|sink| sink.unbounded_send(notification.clone()).is_ok()); + + self.import_notification_sinks.lock().retain(|sink| !sink.is_closed()); + }, + ImportNotificationAction::None => { + // This branch is unreachable in fact because the block import notification must be + // Some(_) instead of None (it's already handled at the beginning of this function) + // at this point. + self.import_notification_sinks.lock().retain(|sink| !sink.is_closed()); - self.import_notification_sinks - .lock() - .retain(|sink| sink.unbounded_send(notification.clone()).is_ok()); + self.every_import_notification_sinks.lock().retain(|sink| !sink.is_closed()); + }, + } Ok(()) } @@ -1944,6 +2002,12 @@ where stream } + fn every_import_notification_stream(&self) -> ImportNotifications { + let (sink, stream) = tracing_unbounded("mpsc_every_import_notification_stream", 100_000); + self.every_import_notification_sinks.lock().push(sink); + stream + } + fn finality_notification_stream(&self) -> FinalityNotifications { let (sink, stream) = tracing_unbounded("mpsc_finality_notification_stream", 100_000); self.finality_notification_sinks.lock().push(sink); From 4ad96cfe44cca7fceac437de232a8c887d70f6ea Mon Sep 17 00:00:00 2001 From: Anton Date: Mon, 27 Feb 2023 14:43:15 +0400 Subject: [PATCH 164/558] [client/network] Remove unused argument (#13444) * improve error message * removed unused argument * docs: disconnect_peer_inner no longer accepts `ban` * remove redundant trace message ``` sync: Too many full nodes, rejecting 12D3KooWSQAP2fh4qBkLXBW4mvCtbAiK8sqMnExWHHTZtVAxZ8bQ sync: 12D3KooWSQAP2fh4qBkLXBW4mvCtbAiK8sqMnExWHHTZtVAxZ8bQ disconnected ``` is enough to understand that we've refused to connect to the given peer * Revert "removed unused argument" This reverts commit c87f755b1fd03494fb446b604fe25c2418da7c87. * ban peer for 10s after disconnect * do not accept incoming conns if peer was banned * Revert "do not accept incoming conns if peer was banned" This reverts commit 7e59d05975765f2547468e9dcfd1361516c41e06. * Revert "ban peer for 10s after disconnect" This reverts commit 3859201ced42a5b2d18c0600e29efd20962a7289. * Revert "Revert "removed unused argument"" This reverts commit f1dc623646dc5a69e1822c35f428e90dffe34d95. * format code * Revert "remove redundant trace message" This reverts commit a87e65f08553dbe69027e9aa4f7ca4779ccaa7f2. --- .../src/protocol/notifications/behaviour.rs | 30 ++++--------------- client/network/sync/src/lib.rs | 2 +- 2 files changed, 7 insertions(+), 25 deletions(-) diff --git a/client/network/src/protocol/notifications/behaviour.rs b/client/network/src/protocol/notifications/behaviour.rs index ebfb95afa..027ea0ab5 100644 --- a/client/network/src/protocol/notifications/behaviour.rs +++ b/client/network/src/protocol/notifications/behaviour.rs @@ -415,17 +415,11 @@ impl Notifications { /// Disconnects the given peer if we are connected to it. pub fn disconnect_peer(&mut self, peer_id: &PeerId, set_id: sc_peerset::SetId) { trace!(target: "sub-libp2p", "External API => Disconnect({}, {:?})", peer_id, set_id); - self.disconnect_peer_inner(peer_id, set_id, None); + self.disconnect_peer_inner(peer_id, set_id); } - /// Inner implementation of `disconnect_peer`. If `ban` is `Some`, we ban the peer - /// for the specific duration. - fn disconnect_peer_inner( - &mut self, - peer_id: &PeerId, - set_id: sc_peerset::SetId, - ban: Option, - ) { + /// Inner implementation of `disconnect_peer`. + fn disconnect_peer_inner(&mut self, peer_id: &PeerId, set_id: sc_peerset::SetId) { let mut entry = if let Entry::Occupied(entry) = self.peers.entry((*peer_id, set_id)) { entry } else { @@ -443,12 +437,8 @@ impl Notifications { PeerState::DisabledPendingEnable { connections, timer_deadline, timer: _ } => { trace!(target: "sub-libp2p", "PSM <= Dropped({}, {:?})", peer_id, set_id); self.peerset.dropped(set_id, *peer_id, DropReason::Unknown); - let backoff_until = Some(if let Some(ban) = ban { - cmp::max(timer_deadline, Instant::now() + ban) - } else { - timer_deadline - }); - *entry.into_mut() = PeerState::Disabled { connections, backoff_until } + *entry.into_mut() = + PeerState::Disabled { connections, backoff_until: Some(timer_deadline) } }, // Enabled => Disabled. @@ -496,8 +486,7 @@ impl Notifications { .iter() .any(|(_, s)| matches!(s, ConnectionState::Opening))); - let backoff_until = ban.map(|dur| Instant::now() + dur); - *entry.into_mut() = PeerState::Disabled { connections, backoff_until } + *entry.into_mut() = PeerState::Disabled { connections, backoff_until: None } }, // Incoming => Disabled. @@ -532,13 +521,6 @@ impl Notifications { *connec_state = ConnectionState::Closing; } - let backoff_until = match (backoff_until, ban) { - (Some(a), Some(b)) => Some(cmp::max(a, Instant::now() + b)), - (Some(a), None) => Some(a), - (None, Some(b)) => Some(Instant::now() + b), - (None, None) => None, - }; - debug_assert!(!connections .iter() .any(|(_, s)| matches!(s, ConnectionState::OpenDesiredByRemote))); diff --git a/client/network/sync/src/lib.rs b/client/network/sync/src/lib.rs index 5c1496e75..f710215e7 100644 --- a/client/network/sync/src/lib.rs +++ b/client/network/sync/src/lib.rs @@ -1015,7 +1015,7 @@ where let peer = if let Some(peer) = self.peers.get_mut(&who) { peer } else { - error!(target: "sync", "💔 Called on_block_justification with a bad peer ID"); + error!(target: "sync", "💔 Called on_block_justification with a peer ID of an unknown peer"); return Ok(OnBlockJustification::Nothing) }; From 7d767195cb60b00309c4c95e362df67244f63ad5 Mon Sep 17 00:00:00 2001 From: Koute Date: Mon, 27 Feb 2023 19:45:42 +0900 Subject: [PATCH 165/558] Further storage iterator refactoring (#13445) * Remove `Backend::apply_to_key_values_while` * Add `IterArgs::start_at_exclusive` * Use `start_at_exclusive` in functions which used `Backend::apply_to_key_values_while` * Remove `Backend::apply_to_keys_while` * Remove `for_keys_with_prefix`, `for_key_values_with_prefix` and `for_child_keys_with_prefix` * Remove unnecessary `to_vec` calls * Fix unused method warning in no_std * Remove unnecessary import * Also check proof sizes in the test * Iterate over both keys and values in `prove_range_read_with_size` and add a test --- client/api/src/backend.rs | 49 +--- primitives/state-machine/src/backend.rs | 120 ++------- primitives/state-machine/src/ext.rs | 74 +++--- primitives/state-machine/src/lib.rs | 230 ++++++++++-------- primitives/state-machine/src/trie_backend.rs | 90 +------ .../state-machine/src/trie_backend_essence.rs | 33 ++- 6 files changed, 226 insertions(+), 370 deletions(-) diff --git a/client/api/src/backend.rs b/client/api/src/backend.rs index f991bea88..26a0cf035 100644 --- a/client/api/src/backend.rs +++ b/client/api/src/backend.rs @@ -324,7 +324,6 @@ where { inner: >>::RawIter, state: State, - skip_if_first: Option, } impl KeysIter @@ -341,13 +340,9 @@ where let mut args = IterArgs::default(); args.prefix = prefix.as_ref().map(|prefix| prefix.0.as_slice()); args.start_at = start_at.as_ref().map(|start_at| start_at.0.as_slice()); + args.start_at_exclusive = true; - let start_at = args.start_at; - Ok(Self { - inner: state.raw_iter(args)?, - state, - skip_if_first: start_at.map(|key| StorageKey(key.to_vec())), - }) + Ok(Self { inner: state.raw_iter(args)?, state }) } /// Create a new iterator over a child storage's keys. @@ -361,13 +356,9 @@ where args.prefix = prefix.as_ref().map(|prefix| prefix.0.as_slice()); args.start_at = start_at.as_ref().map(|start_at| start_at.0.as_slice()); args.child_info = Some(child_info); + args.start_at_exclusive = true; - let start_at = args.start_at; - Ok(Self { - inner: state.raw_iter(args)?, - state, - skip_if_first: start_at.map(|key| StorageKey(key.to_vec())), - }) + Ok(Self { inner: state.raw_iter(args)?, state }) } } @@ -379,15 +370,7 @@ where type Item = StorageKey; fn next(&mut self) -> Option { - let key = self.inner.next_key(&self.state)?.ok().map(StorageKey)?; - - if let Some(skipped_key) = self.skip_if_first.take() { - if key == skipped_key { - return self.next() - } - } - - Some(key) + self.inner.next_key(&self.state)?.ok().map(StorageKey) } } @@ -399,7 +382,6 @@ where { inner: >>::RawIter, state: State, - skip_if_first: Option, } impl Iterator for PairsIter @@ -410,19 +392,10 @@ where type Item = (StorageKey, StorageData); fn next(&mut self) -> Option { - let (key, value) = self - .inner + self.inner .next_pair(&self.state)? .ok() - .map(|(key, value)| (StorageKey(key), StorageData(value)))?; - - if let Some(skipped_key) = self.skip_if_first.take() { - if key == skipped_key { - return self.next() - } - } - - Some((key, value)) + .map(|(key, value)| (StorageKey(key), StorageData(value))) } } @@ -440,13 +413,9 @@ where let mut args = IterArgs::default(); args.prefix = prefix.as_ref().map(|prefix| prefix.0.as_slice()); args.start_at = start_at.as_ref().map(|start_at| start_at.0.as_slice()); + args.start_at_exclusive = true; - let start_at = args.start_at; - Ok(Self { - inner: state.raw_iter(args)?, - state, - skip_if_first: start_at.map(|key| StorageKey(key.to_vec())), - }) + Ok(Self { inner: state.raw_iter(args)?, state }) } } diff --git a/primitives/state-machine/src/backend.rs b/primitives/state-machine/src/backend.rs index a8e742d1d..f3244308a 100644 --- a/primitives/state-machine/src/backend.rs +++ b/primitives/state-machine/src/backend.rs @@ -43,6 +43,10 @@ pub struct IterArgs<'a> { /// This is inclusive and the iteration will include the key which is specified here. pub start_at: Option<&'a [u8]>, + /// If this is `true` then the iteration will *not* include + /// the key specified in `start_at`, if there is such a key. + pub start_at_exclusive: bool, + /// The info of the child trie over which to iterate over. pub child_info: Option, @@ -117,6 +121,17 @@ where } } +impl<'a, H, I> PairsIter<'a, H, I> +where + H: Hasher, + I: StorageIterator + Default, +{ + #[cfg(feature = "std")] + pub(crate) fn was_complete(&self) -> bool { + self.raw_iter.was_complete() + } +} + /// An iterator over storage keys. pub struct KeysIter<'a, H, I> where @@ -214,111 +229,6 @@ pub trait Backend: sp_std::fmt::Debug { key: &[u8], ) -> Result, Self::Error>; - /// Iterate over storage starting at key, for a given prefix and child trie. - /// Aborts as soon as `f` returns false. - /// Warning, this fails at first error when usual iteration skips errors. - /// If `allow_missing` is true, iteration stops when it reaches a missing trie node. - /// Otherwise an error is produced. - /// - /// Returns `true` if trie end is reached. - // TODO: Remove this. - fn apply_to_key_values_while, Vec) -> bool>( - &self, - child_info: Option<&ChildInfo>, - prefix: Option<&[u8]>, - start_at: Option<&[u8]>, - mut f: F, - allow_missing: bool, - ) -> Result { - let args = IterArgs { - child_info: child_info.cloned(), - prefix, - start_at, - stop_on_incomplete_database: allow_missing, - ..IterArgs::default() - }; - let mut iter = self.pairs(args)?; - while let Some(key_value) = iter.next() { - let (key, value) = key_value?; - if !f(key, value) { - return Ok(false) - } - } - Ok(iter.raw_iter.was_complete()) - } - - /// Retrieve all entries keys of storage and call `f` for each of those keys. - /// Aborts as soon as `f` returns false. - // TODO: Remove this. - fn apply_to_keys_while bool>( - &self, - child_info: Option<&ChildInfo>, - prefix: Option<&[u8]>, - start_at: Option<&[u8]>, - mut f: F, - ) -> Result<(), Self::Error> { - let args = - IterArgs { child_info: child_info.cloned(), prefix, start_at, ..IterArgs::default() }; - - for key in self.keys(args)? { - if !f(&key?) { - return Ok(()) - } - } - Ok(()) - } - - /// Retrieve all entries keys which start with the given prefix and - /// call `f` for each of those keys. - // TODO: Remove this. - fn for_keys_with_prefix( - &self, - prefix: &[u8], - mut f: F, - ) -> Result<(), Self::Error> { - let args = IterArgs { prefix: Some(prefix), ..IterArgs::default() }; - self.keys(args)?.try_for_each(|key| { - f(&key?); - Ok(()) - }) - } - - /// Retrieve all entries keys and values of which start with the given prefix and - /// call `f` for each of those keys. - // TODO: Remove this. - fn for_key_values_with_prefix( - &self, - prefix: &[u8], - mut f: F, - ) -> Result<(), Self::Error> { - let args = IterArgs { prefix: Some(prefix), ..IterArgs::default() }; - self.pairs(args)?.try_for_each(|key_value| { - let (key, value) = key_value?; - f(&key, &value); - Ok(()) - }) - } - - /// Retrieve all child entries keys which start with the given prefix and - /// call `f` for each of those keys. - // TODO: Remove this. - fn for_child_keys_with_prefix( - &self, - child_info: &ChildInfo, - prefix: &[u8], - mut f: F, - ) -> Result<(), Self::Error> { - let args = IterArgs { - child_info: Some(child_info.clone()), - prefix: Some(prefix), - ..IterArgs::default() - }; - self.keys(args)?.try_for_each(|key| { - f(&key?); - Ok(()) - }) - } - /// Calculate the storage root, with given delta over what is already stored in /// the backend, and produce a "transaction" that can be used to commit. /// Does not include child storage updates. diff --git a/primitives/state-machine/src/ext.rs b/primitives/state-machine/src/ext.rs index 695868c96..3c088a217 100644 --- a/primitives/state-machine/src/ext.rs +++ b/primitives/state-machine/src/ext.rs @@ -19,7 +19,9 @@ #[cfg(feature = "std")] use crate::overlayed_changes::OverlayedExtensions; -use crate::{backend::Backend, IndexOperation, OverlayedChanges, StorageKey, StorageValue}; +use crate::{ + backend::Backend, IndexOperation, IterArgs, OverlayedChanges, StorageKey, StorageValue, +}; use codec::{Decode, Encode, EncodeAppend}; use hash_db::Hasher; #[cfg(feature = "std")] @@ -750,40 +752,54 @@ where { fn limit_remove_from_backend( &mut self, - maybe_child: Option<&ChildInfo>, - maybe_prefix: Option<&[u8]>, + child_info: Option<&ChildInfo>, + prefix: Option<&[u8]>, maybe_limit: Option, - maybe_cursor: Option<&[u8]>, + start_at: Option<&[u8]>, ) -> (Option>, u32, u32) { + let iter = match self.backend.keys(IterArgs { + child_info: child_info.cloned(), + prefix, + start_at, + ..IterArgs::default() + }) { + Ok(iter) => iter, + Err(error) => { + log::debug!(target: "trie", "Error while iterating the storage: {}", error); + return (None, 0, 0) + }, + }; + let mut delete_count: u32 = 0; let mut loop_count: u32 = 0; let mut maybe_next_key = None; - let result = - self.backend - .apply_to_keys_while(maybe_child, maybe_prefix, maybe_cursor, |key| { - if maybe_limit.map_or(false, |limit| loop_count == limit) { - maybe_next_key = Some(key.to_vec()); - return false - } - let overlay = match maybe_child { - Some(child_info) => self.overlay.child_storage(child_info, key), - None => self.overlay.storage(key), - }; - if !matches!(overlay, Some(None)) { - // not pending deletion from the backend - delete it. - if let Some(child_info) = maybe_child { - self.overlay.set_child_storage(child_info, key.to_vec(), None); - } else { - self.overlay.set_storage(key.to_vec(), None); - } - delete_count = delete_count.saturating_add(1); - } - loop_count = loop_count.saturating_add(1); - true - }); + for key in iter { + let key = match key { + Ok(key) => key, + Err(error) => { + log::debug!(target: "trie", "Error while iterating the storage: {}", error); + break + }, + }; - if let Err(error) = result { - log::debug!(target: "trie", "Error while iterating the storage: {}", error); + if maybe_limit.map_or(false, |limit| loop_count == limit) { + maybe_next_key = Some(key); + break + } + let overlay = match child_info { + Some(child_info) => self.overlay.child_storage(child_info, &key), + None => self.overlay.storage(&key), + }; + if !matches!(overlay, Some(None)) { + // not pending deletion from the backend - delete it. + if let Some(child_info) = child_info { + self.overlay.set_child_storage(child_info, key, None); + } else { + self.overlay.set_storage(key, None); + } + delete_count = delete_count.saturating_add(1); + } + loop_count = loop_count.saturating_add(1); } (maybe_next_key, delete_count, loop_count) diff --git a/primitives/state-machine/src/lib.rs b/primitives/state-machine/src/lib.rs index 5fb300e12..90ee962da 100644 --- a/primitives/state-machine/src/lib.rs +++ b/primitives/state-machine/src/lib.rs @@ -849,48 +849,37 @@ mod execution { let start_at_ref = start_at.as_ref().map(AsRef::as_ref); let mut switch_child_key = None; - let mut first = start_at.is_some(); - let completed = proving_backend - .apply_to_key_values_while( - child_info.as_ref(), - None, - start_at_ref, - |key, value| { - if first && - start_at_ref - .as_ref() - .map(|start| &key.as_slice() > start) - .unwrap_or(true) - { - first = false; - } - - if first { - true - } else if depth < MAX_NESTED_TRIE_DEPTH && - sp_core::storage::well_known_keys::is_child_storage_key( - key.as_slice(), - ) { - count += 1; - if !child_roots.contains(value.as_slice()) { - child_roots.insert(value); - switch_child_key = Some(key); - false - } else { - // do not add two child trie with same root - true - } - } else if recorder.estimate_encoded_size() <= size_limit { - count += 1; - true - } else { - false - } - }, - false, - ) + let mut iter = proving_backend + .pairs(IterArgs { + child_info, + start_at: start_at_ref, + start_at_exclusive: true, + ..IterArgs::default() + }) .map_err(|e| Box::new(e) as Box)?; + while let Some(item) = iter.next() { + let (key, value) = item.map_err(|e| Box::new(e) as Box)?; + + if depth < MAX_NESTED_TRIE_DEPTH && + sp_core::storage::well_known_keys::is_child_storage_key(key.as_slice()) + { + count += 1; + // do not add two child trie with same root + if !child_roots.contains(value.as_slice()) { + child_roots.insert(value); + switch_child_key = Some(key); + break + } + } else if recorder.estimate_encoded_size() <= size_limit { + count += 1; + } else { + break + } + } + + let completed = iter.was_complete(); + if switch_child_key.is_none() { if depth == 1 { break @@ -951,23 +940,27 @@ mod execution { let proving_backend = TrieBackendBuilder::wrap(trie_backend).with_recorder(recorder.clone()).build(); let mut count = 0; - proving_backend - .apply_to_key_values_while( - child_info, + let iter = proving_backend + // NOTE: Even though the loop below doesn't use these values + // this *must* fetch both the keys and the values so that + // the proof is correct. + .pairs(IterArgs { + child_info: child_info.cloned(), prefix, start_at, - |_key, _value| { - if count == 0 || recorder.estimate_encoded_size() <= size_limit { - count += 1; - true - } else { - false - } - }, - false, - ) + ..IterArgs::default() + }) .map_err(|e| Box::new(e) as Box)?; + for item in iter { + item.map_err(|e| Box::new(e) as Box)?; + if count == 0 || recorder.estimate_encoded_size() <= size_limit { + count += 1; + } else { + break + } + } + let proof = proving_backend .extract_proof() .expect("A recorder was set and thus, a storage proof can be extracted; qed"); @@ -1173,20 +1166,25 @@ mod execution { H::Out: Ord + Codec, { let mut values = Vec::new(); - let result = proving_backend.apply_to_key_values_while( - child_info, - prefix, - start_at, - |key, value| { - values.push((key.to_vec(), value.to_vec())); - count.as_ref().map_or(true, |c| (values.len() as u32) < *c) - }, - true, - ); - match result { - Ok(completed) => Ok((values, completed)), - Err(e) => Err(Box::new(e) as Box), + let mut iter = proving_backend + .pairs(IterArgs { + child_info: child_info.cloned(), + prefix, + start_at, + stop_on_incomplete_database: true, + ..IterArgs::default() + }) + .map_err(|e| Box::new(e) as Box)?; + + while let Some(item) = iter.next() { + let (key, value) = item.map_err(|e| Box::new(e) as Box)?; + values.push((key, value)); + if !count.as_ref().map_or(true, |c| (values.len() as u32) < *c) { + break + } } + + Ok((values, iter.was_complete())) } /// Check storage range proof on pre-created proving backend. @@ -1255,47 +1253,35 @@ mod execution { }; let start_at_ref = start_at.as_ref().map(AsRef::as_ref); let mut switch_child_key = None; - let mut first = start_at.is_some(); - let completed = proving_backend - .apply_to_key_values_while( - child_info.as_ref(), - None, - start_at_ref, - |key, value| { - if first && - start_at_ref - .as_ref() - .map(|start| &key.as_slice() > start) - .unwrap_or(true) - { - first = false; - } - if !first { - values.push((key.to_vec(), value.to_vec())); - } - if first { - true - } else if depth < MAX_NESTED_TRIE_DEPTH && - sp_core::storage::well_known_keys::is_child_storage_key( - key.as_slice(), - ) { - if child_roots.contains(value.as_slice()) { - // Do not add two chid trie with same root. - true - } else { - child_roots.insert(value.clone()); - switch_child_key = Some((key, value)); - false - } - } else { - true - } - }, - true, - ) + let mut iter = proving_backend + .pairs(IterArgs { + child_info, + start_at: start_at_ref, + start_at_exclusive: true, + stop_on_incomplete_database: true, + ..IterArgs::default() + }) .map_err(|e| Box::new(e) as Box)?; + while let Some(item) = iter.next() { + let (key, value) = item.map_err(|e| Box::new(e) as Box)?; + values.push((key.to_vec(), value.to_vec())); + + if depth < MAX_NESTED_TRIE_DEPTH && + sp_core::storage::well_known_keys::is_child_storage_key(key.as_slice()) + { + // Do not add two chid trie with same root. + if !child_roots.contains(value.as_slice()) { + child_roots.insert(value.clone()); + switch_child_key = Some((key, value)); + break + } + } + } + + let completed = iter.was_complete(); + if switch_child_key.is_none() { if !completed { break depth @@ -1325,9 +1311,13 @@ mod tests { storage::{ChildInfo, StateVersion}, testing::TaskExecutor, traits::{CallContext, CodeExecutor, Externalities, RuntimeCode}, + H256, }; use sp_runtime::traits::BlakeTwo256; - use sp_trie::trie_types::{TrieDBMutBuilderV0, TrieDBMutBuilderV1}; + use sp_trie::{ + trie_types::{TrieDBMutBuilderV0, TrieDBMutBuilderV1}, + KeySpacedDBMut, PrefixedMemoryDB, + }; use std::collections::{BTreeMap, HashMap}; #[derive(Clone)] @@ -1953,12 +1943,14 @@ mod tests { // Always contains at least some nodes. assert_eq!(proof.to_memory_db::().drain().len(), 3); assert_eq!(count, 1); + assert_eq!(proof.encoded_size(), 443); let remote_backend = trie_backend::tests::test_trie(state_version, None, None); let (proof, count) = prove_range_read_with_size(remote_backend, None, None, 800, Some(&[])).unwrap(); assert_eq!(proof.to_memory_db::().drain().len(), 9); assert_eq!(count, 85); + assert_eq!(proof.encoded_size(), 857); let (results, completed) = read_range_proof_check::( remote_root, proof.clone(), @@ -1982,6 +1974,8 @@ mod tests { prove_range_read_with_size(remote_backend, None, None, 50000, Some(&[])).unwrap(); assert_eq!(proof.to_memory_db::().drain().len(), 11); assert_eq!(count, 132); + assert_eq!(proof.encoded_size(), 990); + let (results, completed) = read_range_proof_check::(remote_root, proof, None, None, None, None) .unwrap(); @@ -1989,6 +1983,32 @@ mod tests { assert_eq!(completed, true); } + #[test] + fn prove_read_with_size_limit_proof_size() { + let mut root = H256::default(); + let mut mdb = PrefixedMemoryDB::::default(); + { + let mut mdb = KeySpacedDBMut::new(&mut mdb, b""); + let mut trie = TrieDBMutBuilderV1::new(&mut mdb, &mut root).build(); + trie.insert(b"value1", &[123; 1]).unwrap(); + trie.insert(b"value2", &[123; 10]).unwrap(); + trie.insert(b"value3", &[123; 100]).unwrap(); + trie.insert(b"value4", &[123; 1000]).unwrap(); + } + + let remote_backend: TrieBackend, BlakeTwo256> = + TrieBackendBuilder::new(mdb, root) + .with_optional_cache(None) + .with_optional_recorder(None) + .build(); + + let (proof, count) = + prove_range_read_with_size(remote_backend, None, None, 1000, None).unwrap(); + + assert_eq!(proof.encoded_size(), 1267); + assert_eq!(count, 3); + } + #[test] fn inner_state_versioning_switch_proofs() { let mut state_version = StateVersion::V0; diff --git a/primitives/state-machine/src/trie_backend.rs b/primitives/state-machine/src/trie_backend.rs index ec6db9be3..3e8d0a7a3 100644 --- a/primitives/state-machine/src/trie_backend.rs +++ b/primitives/state-machine/src/trie_backend.rs @@ -357,7 +357,7 @@ pub mod tests { trie_types::{TrieDBBuilder, TrieDBMutBuilderV0, TrieDBMutBuilderV1}, KeySpacedDBMut, PrefixedKey, PrefixedMemoryDB, Trie, TrieCache, TrieMut, }; - use std::{collections::HashSet, iter}; + use std::iter; use trie_db::NodeCodec; const CHILD_KEY_1: &[u8] = b"sub1"; @@ -643,73 +643,6 @@ pub mod tests { .collect::>(), vec![b"value1".to_vec(), b"value2".to_vec(),] ); - - // Also test out the wrapper methods. - // TODO: Remove this once these methods are gone. - - let mut list = Vec::new(); - assert!(trie - .apply_to_key_values_while( - None, - None, - Some(b"key"), - |key, _| { - list.push(key); - true - }, - false - ) - .unwrap()); - assert_eq!(list[0..3], vec![b"key".to_vec(), b"value1".to_vec(), b"value2".to_vec(),]); - - let mut list = Vec::new(); - trie.apply_to_keys_while(None, None, Some(b"key"), |key| { - list.push(key.to_vec()); - true - }) - .unwrap(); - assert_eq!(list[0..3], vec![b"key".to_vec(), b"value1".to_vec(), b"value2".to_vec(),]); - - let mut list = Vec::new(); - trie.apply_to_keys_while(None, None, Some(b"k"), |key| { - list.push(key.to_vec()); - true - }) - .unwrap(); - assert_eq!(list[0..3], vec![b"key".to_vec(), b"value1".to_vec(), b"value2".to_vec(),]); - - let mut list = Vec::new(); - trie.apply_to_keys_while(None, None, Some(b""), |key| { - list.push(key.to_vec()); - true - }) - .unwrap(); - assert_eq!( - list[0..5], - vec![ - b":child_storage:default:sub1".to_vec(), - b":code".to_vec(), - b"key".to_vec(), - b"value1".to_vec(), - b"value2".to_vec(), - ] - ); - - let mut list = Vec::new(); - trie.apply_to_keys_while(None, Some(b"value"), Some(b"key"), |key| { - list.push(key.to_vec()); - true - }) - .unwrap(); - assert!(list.is_empty()); - - let mut list = Vec::new(); - trie.apply_to_keys_while(None, Some(b"value"), Some(b"value"), |key| { - list.push(key.to_vec()); - true - }) - .unwrap(); - assert_eq!(list, vec![b"value1".to_vec(), b"value2".to_vec(),]); } parameterized_test!(storage_root_is_non_default, storage_root_is_non_default_inner); @@ -745,27 +678,6 @@ pub mod tests { ); } - parameterized_test!(prefix_walking_works, prefix_walking_works_inner); - fn prefix_walking_works_inner( - state_version: StateVersion, - cache: Option, - recorder: Option, - ) { - let trie = test_trie(state_version, cache, recorder); - - let mut seen = HashSet::new(); - trie.for_keys_with_prefix(b"value", |key| { - let for_first_time = seen.insert(key.to_vec()); - assert!(for_first_time, "Seen key '{:?}' more than once", key); - }) - .unwrap(); - - let mut expected = HashSet::new(); - expected.insert(b"value1".to_vec()); - expected.insert(b"value2".to_vec()); - assert_eq!(seen, expected); - } - parameterized_test!( keys_with_empty_prefix_returns_all_keys, keys_with_empty_prefix_returns_all_keys_inner diff --git a/primitives/state-machine/src/trie_backend_essence.rs b/primitives/state-machine/src/trie_backend_essence.rs index 81a627a65..f071a32ce 100644 --- a/primitives/state-machine/src/trie_backend_essence.rs +++ b/primitives/state-machine/src/trie_backend_essence.rs @@ -87,6 +87,7 @@ where H: Hasher, { stop_on_incomplete_database: bool, + skip_if_first: Option, root: H::Out, child_info: Option, trie_iter: TrieDBRawIterator>, @@ -144,6 +145,7 @@ where fn default() -> Self { Self { stop_on_incomplete_database: false, + skip_if_first: None, child_info: None, root: Default::default(), trie_iter: TrieDBRawIterator::empty(), @@ -165,12 +167,34 @@ where #[inline] fn next_key(&mut self, backend: &Self::Backend) -> Option> { - self.prepare(&backend.essence, |trie, trie_iter| trie_iter.next_key(&trie)) + let skip_if_first = self.skip_if_first.take(); + self.prepare(&backend.essence, |trie, trie_iter| { + let mut result = trie_iter.next_key(&trie); + if let Some(skipped_key) = skip_if_first { + if let Some(Ok(ref key)) = result { + if *key == skipped_key { + result = trie_iter.next_key(&trie); + } + } + } + result + }) } #[inline] fn next_pair(&mut self, backend: &Self::Backend) -> Option> { - self.prepare(&backend.essence, |trie, trie_iter| trie_iter.next_item(&trie)) + let skip_if_first = self.skip_if_first.take(); + self.prepare(&backend.essence, |trie, trie_iter| { + let mut result = trie_iter.next_item(&trie); + if let Some(skipped_key) = skip_if_first { + if let Some(Ok((ref key, _))) = result { + if *key == skipped_key { + result = trie_iter.next_item(&trie); + } + } + } + result + }) } fn was_complete(&self) -> bool { @@ -574,6 +598,11 @@ where Ok(RawIter { stop_on_incomplete_database: args.stop_on_incomplete_database, + skip_if_first: if args.start_at_exclusive { + args.start_at.map(|key| key.to_vec()) + } else { + None + }, child_info: args.child_info, root, trie_iter, From 7c8b89b3a6b603902709fc33fdb10a864b8b4204 Mon Sep 17 00:00:00 2001 From: Davide Galassi Date: Mon, 27 Feb 2023 17:15:08 +0100 Subject: [PATCH 166/558] Move grandpa crates to consensus folder (#13458) * Move grandpa under consensus dir * Rename grandpa folder * Finish grandpa renaming * Minor tweaks * Cargo fmt * Adjust path to chain spec --- Cargo.lock | 200 +++++++++--------- Cargo.toml | 6 +- bin/node-template/node/Cargo.toml | 4 +- bin/node-template/node/src/chain_spec.rs | 2 +- bin/node-template/node/src/command.rs | 2 +- bin/node-template/node/src/service.rs | 22 +- bin/node-template/runtime/Cargo.toml | 2 + bin/node-template/runtime/src/lib.rs | 18 +- bin/node/cli/Cargo.toml | 4 +- bin/node/rpc/Cargo.toml | 4 +- bin/node/rpc/src/lib.rs | 4 +- bin/node/runtime/Cargo.toml | 2 + bin/node/runtime/src/lib.rs | 22 +- client/beefy/Cargo.toml | 2 +- client/consensus/grandpa/Cargo.toml | 60 ++++++ .../grandpa}/README.md | 0 client/consensus/grandpa/rpc/Cargo.toml | 34 +++ .../grandpa}/rpc/README.md | 0 .../grandpa}/rpc/src/error.rs | 2 +- .../grandpa}/rpc/src/finality.rs | 6 +- .../grandpa}/rpc/src/lib.rs | 14 +- .../grandpa}/rpc/src/notification.rs | 2 +- .../grandpa}/rpc/src/report.rs | 2 +- .../grandpa}/src/authorities.rs | 2 +- .../grandpa}/src/aux_schema.rs | 4 +- .../grandpa}/src/communication/gossip.rs | 4 +- .../grandpa}/src/communication/mod.rs | 8 +- .../grandpa}/src/communication/periodic.rs | 0 .../grandpa}/src/communication/tests.rs | 14 +- .../grandpa}/src/environment.rs | 2 +- .../grandpa}/src/finality_proof.rs | 10 +- .../grandpa}/src/import.rs | 2 +- .../grandpa}/src/justification.rs | 16 +- .../grandpa}/src/lib.rs | 8 +- .../grandpa}/src/notification.rs | 0 .../grandpa}/src/observer.rs | 2 +- .../grandpa}/src/tests.rs | 16 +- .../grandpa}/src/until_imported.rs | 2 +- .../grandpa}/src/voting_rule.rs | 0 .../grandpa}/src/warp_proof.rs | 12 +- client/finality-grandpa/Cargo.toml | 60 ------ client/finality-grandpa/rpc/Cargo.toml | 36 ---- client/network/README.md | 2 +- client/network/common/Cargo.toml | 2 +- client/network/common/src/sync/warp.rs | 2 +- client/network/src/lib.rs | 2 +- client/network/sync/Cargo.toml | 2 +- client/network/sync/src/warp.rs | 2 +- client/sync-state-rpc/Cargo.toml | 2 +- client/sync-state-rpc/src/lib.rs | 4 +- docs/CODEOWNERS | 2 +- frame/grandpa/Cargo.toml | 4 +- frame/grandpa/src/benchmarking.rs | 6 +- frame/grandpa/src/equivocation.rs | 4 +- frame/grandpa/src/lib.rs | 4 +- frame/grandpa/src/mock.rs | 10 +- frame/grandpa/src/tests.rs | 16 +- .../grandpa}/Cargo.toml | 16 +- .../grandpa}/README.md | 0 .../grandpa}/src/lib.rs | 0 test-utils/runtime/Cargo.toml | 4 +- test-utils/runtime/src/lib.rs | 16 +- 62 files changed, 356 insertions(+), 356 deletions(-) create mode 100644 client/consensus/grandpa/Cargo.toml rename client/{finality-grandpa => consensus/grandpa}/README.md (100%) create mode 100644 client/consensus/grandpa/rpc/Cargo.toml rename client/{finality-grandpa => consensus/grandpa}/rpc/README.md (100%) rename client/{finality-grandpa => consensus/grandpa}/rpc/src/error.rs (97%) rename client/{finality-grandpa => consensus/grandpa}/rpc/src/finality.rs (88%) rename client/{finality-grandpa => consensus/grandpa}/rpc/src/lib.rs (96%) rename client/{finality-grandpa => consensus/grandpa}/rpc/src/notification.rs (96%) rename client/{finality-grandpa => consensus/grandpa}/rpc/src/report.rs (97%) rename client/{finality-grandpa => consensus/grandpa}/src/authorities.rs (99%) rename client/{finality-grandpa => consensus/grandpa}/src/aux_schema.rs (99%) rename client/{finality-grandpa => consensus/grandpa}/src/communication/gossip.rs (99%) rename client/{finality-grandpa => consensus/grandpa}/src/communication/mod.rs (99%) rename client/{finality-grandpa => consensus/grandpa}/src/communication/periodic.rs (100%) rename client/{finality-grandpa => consensus/grandpa}/src/communication/tests.rs (97%) rename client/{finality-grandpa => consensus/grandpa}/src/environment.rs (99%) rename client/{finality-grandpa => consensus/grandpa}/src/finality_proof.rs (98%) rename client/{finality-grandpa => consensus/grandpa}/src/import.rs (99%) rename client/{finality-grandpa => consensus/grandpa}/src/justification.rs (93%) rename client/{finality-grandpa => consensus/grandpa}/src/lib.rs (99%) rename client/{finality-grandpa => consensus/grandpa}/src/notification.rs (100%) rename client/{finality-grandpa => consensus/grandpa}/src/observer.rs (99%) rename client/{finality-grandpa => consensus/grandpa}/src/tests.rs (99%) rename client/{finality-grandpa => consensus/grandpa}/src/until_imported.rs (99%) rename client/{finality-grandpa => consensus/grandpa}/src/voting_rule.rs (100%) rename client/{finality-grandpa => consensus/grandpa}/src/warp_proof.rs (97%) delete mode 100644 client/finality-grandpa/Cargo.toml delete mode 100644 client/finality-grandpa/rpc/Cargo.toml rename primitives/{finality-grandpa => consensus/grandpa}/Cargo.toml (86%) rename primitives/{finality-grandpa => consensus/grandpa}/README.md (100%) rename primitives/{finality-grandpa => consensus/grandpa}/src/lib.rs (100%) diff --git a/Cargo.lock b/Cargo.lock index 299f63921..92db97a36 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -520,8 +520,8 @@ dependencies = [ "sp-beefy", "sp-blockchain", "sp-consensus", + "sp-consensus-grandpa", "sp-core", - "sp-finality-grandpa", "sp-keyring", "sp-keystore", "sp-mmr-primitives", @@ -3601,6 +3601,7 @@ dependencies = [ "sp-authority-discovery", "sp-block-builder", "sp-consensus-babe", + "sp-consensus-grandpa", "sp-core", "sp-inherents", "sp-io", @@ -4806,9 +4807,9 @@ dependencies = [ "sc-consensus", "sc-consensus-babe", "sc-consensus-epochs", + "sc-consensus-grandpa", "sc-consensus-slots", "sc-executor", - "sc-finality-grandpa", "sc-keystore", "sc-network", "sc-network-common", @@ -4829,8 +4830,8 @@ dependencies = [ "sp-blockchain", "sp-consensus", "sp-consensus-babe", + "sp-consensus-grandpa", "sp-core", - "sp-finality-grandpa", "sp-inherents", "sp-io", "sp-keyring", @@ -4928,8 +4929,8 @@ dependencies = [ "sc-consensus-babe", "sc-consensus-babe-rpc", "sc-consensus-epochs", - "sc-finality-grandpa", - "sc-finality-grandpa-rpc", + "sc-consensus-grandpa", + "sc-consensus-grandpa-rpc", "sc-rpc", "sc-rpc-api", "sc-rpc-spec-v2", @@ -4973,8 +4974,8 @@ dependencies = [ "sc-client-api", "sc-consensus", "sc-consensus-aura", + "sc-consensus-grandpa", "sc-executor", - "sc-finality-grandpa", "sc-keystore", "sc-rpc", "sc-rpc-api", @@ -4987,8 +4988,8 @@ dependencies = [ "sp-blockchain", "sp-consensus", "sp-consensus-aura", + "sp-consensus-grandpa", "sp-core", - "sp-finality-grandpa", "sp-inherents", "sp-io", "sp-keyring", @@ -5024,6 +5025,7 @@ dependencies = [ "sp-api", "sp-block-builder", "sp-consensus-aura", + "sp-consensus-grandpa", "sp-core", "sp-inherents", "sp-offchain", @@ -5866,8 +5868,8 @@ dependencies = [ "parity-scale-codec", "scale-info", "sp-application-crypto", + "sp-consensus-grandpa", "sp-core", - "sp-finality-grandpa", "sp-io", "sp-keyring", "sp-runtime", @@ -8304,6 +8306,76 @@ dependencies = [ "sp-runtime", ] +[[package]] +name = "sc-consensus-grandpa" +version = "0.10.0-dev" +dependencies = [ + "ahash 0.8.3", + "array-bytes", + "assert_matches", + "async-trait", + "dyn-clone", + "finality-grandpa", + "fork-tree", + "futures", + "futures-timer", + "log", + "parity-scale-codec", + "parking_lot 0.12.1", + "rand 0.8.5", + "sc-block-builder", + "sc-chain-spec", + "sc-client-api", + "sc-consensus", + "sc-network", + "sc-network-common", + "sc-network-gossip", + "sc-network-test", + "sc-telemetry", + "sc-utils", + "serde", + "serde_json", + "sp-api", + "sp-application-crypto", + "sp-arithmetic", + "sp-blockchain", + "sp-consensus", + "sp-consensus-grandpa", + "sp-core", + "sp-keyring", + "sp-keystore", + "sp-runtime", + "sp-tracing", + "substrate-prometheus-endpoint", + "substrate-test-runtime-client", + "thiserror", + "tokio", +] + +[[package]] +name = "sc-consensus-grandpa-rpc" +version = "0.10.0-dev" +dependencies = [ + "finality-grandpa", + "futures", + "jsonrpsee", + "log", + "parity-scale-codec", + "sc-block-builder", + "sc-client-api", + "sc-consensus-grandpa", + "sc-rpc", + "serde", + "sp-blockchain", + "sp-consensus-grandpa", + "sp-core", + "sp-keyring", + "sp-runtime", + "substrate-test-runtime-client", + "thiserror", + "tokio", +] + [[package]] name = "sc-consensus-manual-seal" version = "0.10.0-dev" @@ -8476,76 +8548,6 @@ dependencies = [ "wat", ] -[[package]] -name = "sc-finality-grandpa" -version = "0.10.0-dev" -dependencies = [ - "ahash 0.8.3", - "array-bytes", - "assert_matches", - "async-trait", - "dyn-clone", - "finality-grandpa", - "fork-tree", - "futures", - "futures-timer", - "log", - "parity-scale-codec", - "parking_lot 0.12.1", - "rand 0.8.5", - "sc-block-builder", - "sc-chain-spec", - "sc-client-api", - "sc-consensus", - "sc-network", - "sc-network-common", - "sc-network-gossip", - "sc-network-test", - "sc-telemetry", - "sc-utils", - "serde", - "serde_json", - "sp-api", - "sp-application-crypto", - "sp-arithmetic", - "sp-blockchain", - "sp-consensus", - "sp-core", - "sp-finality-grandpa", - "sp-keyring", - "sp-keystore", - "sp-runtime", - "sp-tracing", - "substrate-prometheus-endpoint", - "substrate-test-runtime-client", - "thiserror", - "tokio", -] - -[[package]] -name = "sc-finality-grandpa-rpc" -version = "0.10.0-dev" -dependencies = [ - "finality-grandpa", - "futures", - "jsonrpsee", - "log", - "parity-scale-codec", - "sc-block-builder", - "sc-client-api", - "sc-finality-grandpa", - "sc-rpc", - "serde", - "sp-blockchain", - "sp-core", - "sp-finality-grandpa", - "sp-keyring", - "sp-runtime", - "substrate-test-runtime-client", - "thiserror", - "tokio", -] - [[package]] name = "sc-informant" version = "0.10.0-dev" @@ -8673,7 +8675,7 @@ dependencies = [ "smallvec", "sp-blockchain", "sp-consensus", - "sp-finality-grandpa", + "sp-consensus-grandpa", "sp-runtime", "substrate-prometheus-endpoint", "thiserror", @@ -8745,8 +8747,8 @@ dependencies = [ "sp-arithmetic", "sp-blockchain", "sp-consensus", + "sp-consensus-grandpa", "sp-core", - "sp-finality-grandpa", "sp-runtime", "sp-test-primitives", "sp-tracing", @@ -9114,7 +9116,7 @@ dependencies = [ "sc-client-api", "sc-consensus-babe", "sc-consensus-epochs", - "sc-finality-grandpa", + "sc-consensus-grandpa", "serde", "serde_json", "sp-blockchain", @@ -9893,6 +9895,23 @@ dependencies = [ "sp-timestamp", ] +[[package]] +name = "sp-consensus-grandpa" +version = "4.0.0-dev" +dependencies = [ + "finality-grandpa", + "log", + "parity-scale-codec", + "scale-info", + "serde", + "sp-api", + "sp-application-crypto", + "sp-core", + "sp-keystore", + "sp-runtime", + "sp-std", +] + [[package]] name = "sp-consensus-pow" version = "0.10.0-dev" @@ -10023,23 +10042,6 @@ dependencies = [ "sp-storage", ] -[[package]] -name = "sp-finality-grandpa" -version = "4.0.0-dev" -dependencies = [ - "finality-grandpa", - "log", - "parity-scale-codec", - "scale-info", - "serde", - "sp-api", - "sp-application-crypto", - "sp-core", - "sp-keystore", - "sp-runtime", - "sp-std", -] - [[package]] name = "sp-inherents" version = "4.0.0-dev" @@ -10801,9 +10803,9 @@ dependencies = [ "sp-consensus", "sp-consensus-aura", "sp-consensus-babe", + "sp-consensus-grandpa", "sp-core", "sp-externalities", - "sp-finality-grandpa", "sp-inherents", "sp-io", "sp-keyring", diff --git a/Cargo.toml b/Cargo.toml index 80dd2b90f..88c9c1998 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -29,6 +29,8 @@ members = [ "client/consensus/babe/rpc", "client/consensus/common", "client/consensus/epochs", + "client/consensus/grandpa", + "client/consensus/grandpa/rpc", "client/consensus/manual-seal", "client/consensus/pow", "client/consensus/slots", @@ -38,8 +40,6 @@ members = [ "client/executor/runtime-test", "client/executor/wasmi", "client/executor/wasmtime", - "client/finality-grandpa", - "client/finality-grandpa/rpc", "client/informant", "client/keystore", "client/merkle-mountain-range", @@ -184,6 +184,7 @@ members = [ "primitives/consensus/aura", "primitives/consensus/babe", "primitives/consensus/common", + "primitives/consensus/grandpa", "primitives/consensus/pow", "primitives/consensus/slots", "primitives/consensus/vrf", @@ -193,7 +194,6 @@ members = [ "primitives/database", "primitives/debug-derive", "primitives/externalities", - "primitives/finality-grandpa", "primitives/inherents", "primitives/io", "primitives/keyring", diff --git a/bin/node-template/node/Cargo.toml b/bin/node-template/node/Cargo.toml index 364cfa25d..979c00120 100644 --- a/bin/node-template/node/Cargo.toml +++ b/bin/node-template/node/Cargo.toml @@ -32,8 +32,8 @@ sc-consensus-aura = { version = "0.10.0-dev", path = "../../../client/consensus/ sp-consensus-aura = { version = "0.10.0-dev", path = "../../../primitives/consensus/aura" } sp-consensus = { version = "0.10.0-dev", path = "../../../primitives/consensus/common" } sc-consensus = { version = "0.10.0-dev", path = "../../../client/consensus/common" } -sc-finality-grandpa = { version = "0.10.0-dev", path = "../../../client/finality-grandpa" } -sp-finality-grandpa = { version = "4.0.0-dev", path = "../../../primitives/finality-grandpa" } +sc-consensus-grandpa = { version = "0.10.0-dev", path = "../../../client/consensus/grandpa" } +sp-consensus-grandpa = { version = "4.0.0-dev", path = "../../../primitives/consensus/grandpa" } sc-client-api = { version = "4.0.0-dev", path = "../../../client/api" } sp-runtime = { version = "7.0.0", path = "../../../primitives/runtime" } sp-io = { version = "7.0.0", path = "../../../primitives/io" } diff --git a/bin/node-template/node/src/chain_spec.rs b/bin/node-template/node/src/chain_spec.rs index ef34ec369..e978596be 100644 --- a/bin/node-template/node/src/chain_spec.rs +++ b/bin/node-template/node/src/chain_spec.rs @@ -4,8 +4,8 @@ use node_template_runtime::{ }; use sc_service::ChainType; use sp_consensus_aura::sr25519::AuthorityId as AuraId; +use sp_consensus_grandpa::AuthorityId as GrandpaId; use sp_core::{sr25519, Pair, Public}; -use sp_finality_grandpa::AuthorityId as GrandpaId; use sp_runtime::traits::{IdentifyAccount, Verify}; // The URL for the telemetry server. diff --git a/bin/node-template/node/src/command.rs b/bin/node-template/node/src/command.rs index c3dc098cd..e121db820 100644 --- a/bin/node-template/node/src/command.rs +++ b/bin/node-template/node/src/command.rs @@ -102,7 +102,7 @@ pub fn run() -> sc_cli::Result<()> { let PartialComponents { client, task_manager, backend, .. } = service::new_partial(&config)?; let aux_revert = Box::new(|client, _, blocks| { - sc_finality_grandpa::revert(client, blocks)?; + sc_consensus_grandpa::revert(client, blocks)?; Ok(()) }); Ok((cmd.run(client, backend, Some(aux_revert)), task_manager)) diff --git a/bin/node-template/node/src/service.rs b/bin/node-template/node/src/service.rs index d7d075efd..98485a7ad 100644 --- a/bin/node-template/node/src/service.rs +++ b/bin/node-template/node/src/service.rs @@ -3,8 +3,8 @@ use node_template_runtime::{self, opaque::Block, RuntimeApi}; use sc_client_api::BlockBackend; use sc_consensus_aura::{ImportQueueParams, SlotProportion, StartAuraParams}; +use sc_consensus_grandpa::SharedVoterState; pub use sc_executor::NativeElseWasmExecutor; -use sc_finality_grandpa::SharedVoterState; use sc_keystore::LocalKeystore; use sc_service::{error::Error as ServiceError, Configuration, TaskManager, WarpSyncParams}; use sc_telemetry::{Telemetry, TelemetryWorker}; @@ -46,13 +46,13 @@ pub fn new_partial( sc_consensus::DefaultImportQueue, sc_transaction_pool::FullPool, ( - sc_finality_grandpa::GrandpaBlockImport< + sc_consensus_grandpa::GrandpaBlockImport< FullBackend, Block, FullClient, FullSelectChain, >, - sc_finality_grandpa::LinkHalf, + sc_consensus_grandpa::LinkHalf, Option, ), >, @@ -103,7 +103,7 @@ pub fn new_partial( client.clone(), ); - let (grandpa_block_import, grandpa_link) = sc_finality_grandpa::block_import( + let (grandpa_block_import, grandpa_link) = sc_consensus_grandpa::block_import( client.clone(), &(client.clone() as Arc<_>), select_chain.clone(), @@ -177,7 +177,7 @@ pub fn new_full(mut config: Configuration) -> Result ))), }; } - let grandpa_protocol_name = sc_finality_grandpa::protocol_standard_name( + let grandpa_protocol_name = sc_consensus_grandpa::protocol_standard_name( &client.block_hash(0).ok().flatten().expect("Genesis block exists; qed"), &config.chain_spec, ); @@ -185,8 +185,8 @@ pub fn new_full(mut config: Configuration) -> Result config .network .extra_sets - .push(sc_finality_grandpa::grandpa_peers_set_config(grandpa_protocol_name.clone())); - let warp_sync = Arc::new(sc_finality_grandpa::warp_proof::NetworkProvider::new( + .push(sc_consensus_grandpa::grandpa_peers_set_config(grandpa_protocol_name.clone())); + let warp_sync = Arc::new(sc_consensus_grandpa::warp_proof::NetworkProvider::new( backend.clone(), grandpa_link.shared_authority_set().clone(), Vec::default(), @@ -298,7 +298,7 @@ pub fn new_full(mut config: Configuration) -> Result let keystore = if role.is_authority() { Some(keystore_container.sync_keystore()) } else { None }; - let grandpa_config = sc_finality_grandpa::Config { + let grandpa_config = sc_consensus_grandpa::Config { // FIXME #1578 make this available through chainspec gossip_duration: Duration::from_millis(333), justification_period: 512, @@ -316,11 +316,11 @@ pub fn new_full(mut config: Configuration) -> Result // and vote data availability than the observer. The observer has not // been tested extensively yet and having most nodes in a network run it // could lead to finality stalls. - let grandpa_config = sc_finality_grandpa::GrandpaParams { + let grandpa_config = sc_consensus_grandpa::GrandpaParams { config: grandpa_config, link: grandpa_link, network, - voting_rule: sc_finality_grandpa::VotingRulesBuilder::default().build(), + voting_rule: sc_consensus_grandpa::VotingRulesBuilder::default().build(), prometheus_registry, shared_voter_state: SharedVoterState::empty(), telemetry: telemetry.as_ref().map(|x| x.handle()), @@ -331,7 +331,7 @@ pub fn new_full(mut config: Configuration) -> Result task_manager.spawn_essential_handle().spawn_blocking( "grandpa-voter", None, - sc_finality_grandpa::run_grandpa_voter(grandpa_config)?, + sc_consensus_grandpa::run_grandpa_voter(grandpa_config)?, ); } diff --git a/bin/node-template/runtime/Cargo.toml b/bin/node-template/runtime/Cargo.toml index dd453d9f1..55fdea7f5 100644 --- a/bin/node-template/runtime/Cargo.toml +++ b/bin/node-template/runtime/Cargo.toml @@ -30,6 +30,7 @@ frame-executive = { version = "4.0.0-dev", default-features = false, path = "../ sp-api = { version = "4.0.0-dev", default-features = false, path = "../../../primitives/api" } sp-block-builder = { version = "4.0.0-dev", default-features = false, path = "../../../primitives/block-builder"} sp-consensus-aura = { version = "0.10.0-dev", default-features = false, path = "../../../primitives/consensus/aura" } +sp-consensus-grandpa = { version = "4.0.0-dev", default-features = false, path = "../../../primitives/consensus/grandpa" } sp-core = { version = "7.0.0", default-features = false, path = "../../../primitives/core" } sp-inherents = { version = "4.0.0-dev", default-features = false, path = "../../../primitives/inherents"} sp-offchain = { version = "4.0.0-dev", default-features = false, path = "../../../primitives/offchain" } @@ -78,6 +79,7 @@ std = [ "sp-api/std", "sp-block-builder/std", "sp-consensus-aura/std", + "sp-consensus-grandpa/std", "sp-core/std", "sp-inherents/std", "sp-offchain/std", diff --git a/bin/node-template/runtime/src/lib.rs b/bin/node-template/runtime/src/lib.rs index ff9ac66d6..57e4470c7 100644 --- a/bin/node-template/runtime/src/lib.rs +++ b/bin/node-template/runtime/src/lib.rs @@ -6,9 +6,7 @@ #[cfg(feature = "std")] include!(concat!(env!("OUT_DIR"), "/wasm_binary.rs")); -use pallet_grandpa::{ - fg_primitives, AuthorityId as GrandpaId, AuthorityList as GrandpaAuthorityList, -}; +use pallet_grandpa::AuthorityId as GrandpaId; use sp_api::impl_runtime_apis; use sp_consensus_aura::sr25519::AuthorityId as AuraId; use sp_core::{crypto::KeyTypeId, OpaqueMetadata}; @@ -429,29 +427,29 @@ impl_runtime_apis! { } } - impl fg_primitives::GrandpaApi for Runtime { - fn grandpa_authorities() -> GrandpaAuthorityList { + impl sp_consensus_grandpa::GrandpaApi for Runtime { + fn grandpa_authorities() -> sp_consensus_grandpa::AuthorityList { Grandpa::grandpa_authorities() } - fn current_set_id() -> fg_primitives::SetId { + fn current_set_id() -> sp_consensus_grandpa::SetId { Grandpa::current_set_id() } fn submit_report_equivocation_unsigned_extrinsic( - _equivocation_proof: fg_primitives::EquivocationProof< + _equivocation_proof: sp_consensus_grandpa::EquivocationProof< ::Hash, NumberFor, >, - _key_owner_proof: fg_primitives::OpaqueKeyOwnershipProof, + _key_owner_proof: sp_consensus_grandpa::OpaqueKeyOwnershipProof, ) -> Option<()> { None } fn generate_key_ownership_proof( - _set_id: fg_primitives::SetId, + _set_id: sp_consensus_grandpa::SetId, _authority_id: GrandpaId, - ) -> Option { + ) -> Option { // NOTE: this is the only implementation possible since we've // defined our key owner proof type as a bottom type (i.e. a type // with no values). diff --git a/bin/node/cli/Cargo.toml b/bin/node/cli/Cargo.toml index 9ec06d9d9..b93019b7c 100644 --- a/bin/node/cli/Cargo.toml +++ b/bin/node/cli/Cargo.toml @@ -47,7 +47,7 @@ rand = "0.8" # primitives sp-authority-discovery = { version = "4.0.0-dev", path = "../../../primitives/authority-discovery" } sp-consensus-babe = { version = "0.10.0-dev", path = "../../../primitives/consensus/babe" } -grandpa-primitives = { version = "4.0.0-dev", package = "sp-finality-grandpa", path = "../../../primitives/finality-grandpa" } +grandpa-primitives = { version = "4.0.0-dev", package = "sp-consensus-grandpa", path = "../../../primitives/consensus/grandpa" } sp-api = { version = "4.0.0-dev", path = "../../../primitives/api" } sp-core = { version = "7.0.0", path = "../../../primitives/core" } sp-runtime = { version = "7.0.0", path = "../../../primitives/runtime" } @@ -70,7 +70,7 @@ sc-network = { version = "0.10.0-dev", path = "../../../client/network" } sc-network-common = { version = "0.10.0-dev", path = "../../../client/network/common" } sc-consensus-slots = { version = "0.10.0-dev", path = "../../../client/consensus/slots" } sc-consensus-babe = { version = "0.10.0-dev", path = "../../../client/consensus/babe" } -grandpa = { version = "0.10.0-dev", package = "sc-finality-grandpa", path = "../../../client/finality-grandpa" } +grandpa = { version = "0.10.0-dev", package = "sc-consensus-grandpa", path = "../../../client/consensus/grandpa" } sc-rpc = { version = "4.0.0-dev", path = "../../../client/rpc" } sc-basic-authorship = { version = "0.10.0-dev", path = "../../../client/basic-authorship" } sc-service = { version = "0.10.0-dev", default-features = false, path = "../../../client/service" } diff --git a/bin/node/rpc/Cargo.toml b/bin/node/rpc/Cargo.toml index f34922a28..0ea6f49bd 100644 --- a/bin/node/rpc/Cargo.toml +++ b/bin/node/rpc/Cargo.toml @@ -22,8 +22,8 @@ sc-client-api = { version = "4.0.0-dev", path = "../../../client/api" } sc-consensus-babe = { version = "0.10.0-dev", path = "../../../client/consensus/babe" } sc-consensus-babe-rpc = { version = "0.10.0-dev", path = "../../../client/consensus/babe/rpc" } sc-consensus-epochs = { version = "0.10.0-dev", path = "../../../client/consensus/epochs" } -sc-finality-grandpa = { version = "0.10.0-dev", path = "../../../client/finality-grandpa" } -sc-finality-grandpa-rpc = { version = "0.10.0-dev", path = "../../../client/finality-grandpa/rpc" } +sc-consensus-grandpa = { version = "0.10.0-dev", path = "../../../client/consensus/grandpa" } +sc-consensus-grandpa-rpc = { version = "0.10.0-dev", path = "../../../client/consensus/grandpa/rpc" } sc-rpc = { version = "4.0.0-dev", path = "../../../client/rpc" } sc-rpc-api = { version = "0.10.0-dev", path = "../../../client/rpc-api" } sc-rpc-spec-v2 = { version = "0.10.0-dev", path = "../../../client/rpc-spec-v2" } diff --git a/bin/node/rpc/src/lib.rs b/bin/node/rpc/src/lib.rs index 40e4de6b9..7c2f7c15e 100644 --- a/bin/node/rpc/src/lib.rs +++ b/bin/node/rpc/src/lib.rs @@ -38,7 +38,7 @@ use node_primitives::{AccountId, Balance, Block, BlockNumber, Hash, Index}; use sc_client_api::AuxStore; use sc_consensus_babe::{BabeConfiguration, Epoch}; use sc_consensus_epochs::SharedEpochChanges; -use sc_finality_grandpa::{ +use sc_consensus_grandpa::{ FinalityProofProvider, GrandpaJustificationStream, SharedAuthoritySet, SharedVoterState, }; use sc_rpc::SubscriptionTaskExecutor; @@ -120,7 +120,7 @@ where use mmr_rpc::{Mmr, MmrApiServer}; use pallet_transaction_payment_rpc::{TransactionPayment, TransactionPaymentApiServer}; use sc_consensus_babe_rpc::{Babe, BabeApiServer}; - use sc_finality_grandpa_rpc::{Grandpa, GrandpaApiServer}; + use sc_consensus_grandpa_rpc::{Grandpa, GrandpaApiServer}; use sc_rpc::dev::{Dev, DevApiServer}; use sc_rpc_spec_v2::chain_spec::{ChainSpec, ChainSpecApiServer}; use sc_sync_state_rpc::{SyncState, SyncStateApiServer}; diff --git a/bin/node/runtime/Cargo.toml b/bin/node/runtime/Cargo.toml index 3df4596aa..993108bf7 100644 --- a/bin/node/runtime/Cargo.toml +++ b/bin/node/runtime/Cargo.toml @@ -27,6 +27,7 @@ log = { version = "0.4.17", default-features = false } # primitives sp-authority-discovery = { version = "4.0.0-dev", default-features = false, path = "../../../primitives/authority-discovery" } sp-consensus-babe = { version = "0.10.0-dev", default-features = false, path = "../../../primitives/consensus/babe" } +sp-consensus-grandpa = { version = "4.0.0-dev", default-features = false, path = "../../../primitives/consensus/grandpa" } sp-block-builder = { path = "../../../primitives/block-builder", default-features = false, version = "4.0.0-dev" } sp-inherents = { version = "4.0.0-dev", default-features = false, path = "../../../primitives/inherents" } node-primitives = { version = "2.0.0", default-features = false, path = "../primitives" } @@ -134,6 +135,7 @@ std = [ "pallet-authority-discovery/std", "pallet-authorship/std", "sp-consensus-babe/std", + "sp-consensus-grandpa/std", "pallet-babe/std", "pallet-bags-list/std", "pallet-balances/std", diff --git a/bin/node/runtime/src/lib.rs b/bin/node/runtime/src/lib.rs index 14b74425f..f8589036f 100644 --- a/bin/node/runtime/src/lib.rs +++ b/bin/node/runtime/src/lib.rs @@ -52,9 +52,6 @@ use frame_system::{ pub use node_primitives::{AccountId, Signature}; use node_primitives::{AccountIndex, Balance, BlockNumber, Hash, Index, Moment}; use pallet_election_provider_multi_phase::SolutionAccuracyOf; -use pallet_grandpa::{ - fg_primitives, AuthorityId as GrandpaId, AuthorityList as GrandpaAuthorityList, -}; use pallet_im_online::sr25519::AuthorityId as ImOnlineId; use pallet_nfts::PalletFeatures; use pallet_nis::WithMaximumOf; @@ -63,6 +60,7 @@ pub use pallet_transaction_payment::{CurrencyAdapter, Multiplier, TargetedFeeAdj use pallet_transaction_payment::{FeeDetails, RuntimeDispatchInfo}; use sp_api::impl_runtime_apis; use sp_authority_discovery::AuthorityId as AuthorityDiscoveryId; +use sp_consensus_grandpa::AuthorityId as GrandpaId; use sp_core::{crypto::KeyTypeId, OpaqueMetadata}; use sp_inherents::{CheckInherentsResult, InherentData}; use sp_runtime::{ @@ -1957,21 +1955,21 @@ impl_runtime_apis! { } } - impl fg_primitives::GrandpaApi for Runtime { - fn grandpa_authorities() -> GrandpaAuthorityList { + impl sp_consensus_grandpa::GrandpaApi for Runtime { + fn grandpa_authorities() -> sp_consensus_grandpa::AuthorityList { Grandpa::grandpa_authorities() } - fn current_set_id() -> fg_primitives::SetId { + fn current_set_id() -> sp_consensus_grandpa::SetId { Grandpa::current_set_id() } fn submit_report_equivocation_unsigned_extrinsic( - equivocation_proof: fg_primitives::EquivocationProof< + equivocation_proof: sp_consensus_grandpa::EquivocationProof< ::Hash, NumberFor, >, - key_owner_proof: fg_primitives::OpaqueKeyOwnershipProof, + key_owner_proof: sp_consensus_grandpa::OpaqueKeyOwnershipProof, ) -> Option<()> { let key_owner_proof = key_owner_proof.decode()?; @@ -1982,14 +1980,14 @@ impl_runtime_apis! { } fn generate_key_ownership_proof( - _set_id: fg_primitives::SetId, + _set_id: sp_consensus_grandpa::SetId, authority_id: GrandpaId, - ) -> Option { + ) -> Option { use codec::Encode; - Historical::prove((fg_primitives::KEY_TYPE, authority_id)) + Historical::prove((sp_consensus_grandpa::KEY_TYPE, authority_id)) .map(|p| p.encode()) - .map(fg_primitives::OpaqueKeyOwnershipProof::new) + .map(sp_consensus_grandpa::OpaqueKeyOwnershipProof::new) } } diff --git a/client/beefy/Cargo.toml b/client/beefy/Cargo.toml index 2976d1474..eb6168236 100644 --- a/client/beefy/Cargo.toml +++ b/client/beefy/Cargo.toml @@ -43,7 +43,7 @@ tempfile = "3.1.0" tokio = "1.22.0" sc-block-builder = { version = "0.10.0-dev", path = "../block-builder" } sc-network-test = { version = "0.8.0", path = "../network/test" } -sp-finality-grandpa = { version = "4.0.0-dev", path = "../../primitives/finality-grandpa" } +sp-consensus-grandpa = { version = "4.0.0-dev", path = "../../primitives/consensus/grandpa" } sp-keyring = { version = "7.0.0", path = "../../primitives/keyring" } sp-tracing = { version = "6.0.0", path = "../../primitives/tracing" } substrate-test-runtime-client = { version = "2.0.0", path = "../../test-utils/runtime/client" } diff --git a/client/consensus/grandpa/Cargo.toml b/client/consensus/grandpa/Cargo.toml new file mode 100644 index 000000000..511e546aa --- /dev/null +++ b/client/consensus/grandpa/Cargo.toml @@ -0,0 +1,60 @@ +[package] +name = "sc-consensus-grandpa" +version = "0.10.0-dev" +authors = ["Parity Technologies "] +edition = "2021" +license = "GPL-3.0-or-later WITH Classpath-exception-2.0" +homepage = "https://substrate.io" +repository = "https://github.com/paritytech/substrate/" +description = "Integration of the GRANDPA finality gadget into substrate." +documentation = "https://docs.rs/sc-consensus-grandpa" +readme = "README.md" + +[package.metadata.docs.rs] +targets = ["x86_64-unknown-linux-gnu"] + +[dependencies] +ahash = "0.8.2" +array-bytes = "4.1" +async-trait = "0.1.57" +dyn-clone = "1.0" +finality-grandpa = { version = "0.16.1", features = ["derive-codec"] } +futures = "0.3.21" +futures-timer = "3.0.1" +log = "0.4.17" +parity-scale-codec = { version = "3.2.2", features = ["derive"] } +parking_lot = "0.12.1" +rand = "0.8.5" +serde_json = "1.0.85" +thiserror = "1.0" +fork-tree = { version = "3.0.0", path = "../../../utils/fork-tree" } +prometheus-endpoint = { package = "substrate-prometheus-endpoint", version = "0.10.0-dev", path = "../../../utils/prometheus" } +sc-block-builder = { version = "0.10.0-dev", path = "../../block-builder" } +sc-chain-spec = { version = "4.0.0-dev", path = "../../../client/chain-spec" } +sc-client-api = { version = "4.0.0-dev", path = "../../api" } +sc-consensus = { version = "0.10.0-dev", path = "../common" } +sc-network = { version = "0.10.0-dev", path = "../../network" } +sc-network-gossip = { version = "0.10.0-dev", path = "../../network-gossip" } +sc-network-common = { version = "0.10.0-dev", path = "../../network/common" } +sc-telemetry = { version = "4.0.0-dev", path = "../../telemetry" } +sc-utils = { version = "4.0.0-dev", path = "../../utils" } +sp-api = { version = "4.0.0-dev", path = "../../../primitives/api" } +sp-application-crypto = { version = "7.0.0", path = "../../../primitives/application-crypto" } +sp-arithmetic = { version = "6.0.0", path = "../../../primitives/arithmetic" } +sp-blockchain = { version = "4.0.0-dev", path = "../../../primitives/blockchain" } +sp-consensus = { version = "0.10.0-dev", path = "../../../primitives/consensus/common" } +sp-core = { version = "7.0.0", path = "../../../primitives/core" } +sp-consensus-grandpa = { version = "4.0.0-dev", path = "../../../primitives/consensus/grandpa" } +sp-keystore = { version = "0.13.0", path = "../../../primitives/keystore" } +sp-runtime = { version = "7.0.0", path = "../../../primitives/runtime" } + +[dev-dependencies] +assert_matches = "1.3.0" +finality-grandpa = { version = "0.16.1", features = ["derive-codec", "test-helpers"] } +serde = "1.0.136" +tokio = "1.22.0" +sc-network = { version = "0.10.0-dev", path = "../../network" } +sc-network-test = { version = "0.8.0", path = "../../network/test" } +sp-keyring = { version = "7.0.0", path = "../../../primitives/keyring" } +sp-tracing = { version = "6.0.0", path = "../../../primitives/tracing" } +substrate-test-runtime-client = { version = "2.0.0", path = "../../../test-utils/runtime/client" } diff --git a/client/finality-grandpa/README.md b/client/consensus/grandpa/README.md similarity index 100% rename from client/finality-grandpa/README.md rename to client/consensus/grandpa/README.md diff --git a/client/consensus/grandpa/rpc/Cargo.toml b/client/consensus/grandpa/rpc/Cargo.toml new file mode 100644 index 000000000..4880b50d3 --- /dev/null +++ b/client/consensus/grandpa/rpc/Cargo.toml @@ -0,0 +1,34 @@ +[package] +name = "sc-consensus-grandpa-rpc" +version = "0.10.0-dev" +authors = ["Parity Technologies "] +description = "RPC extensions for the GRANDPA finality gadget" +repository = "https://github.com/paritytech/substrate/" +edition = "2021" +license = "GPL-3.0-or-later WITH Classpath-exception-2.0" +readme = "README.md" +homepage = "https://substrate.io" + +[dependencies] +finality-grandpa = { version = "0.16.1", features = ["derive-codec"] } +futures = "0.3.16" +jsonrpsee = { version = "0.16.2", features = ["client-core", "server", "macros"] } +log = "0.4.8" +parity-scale-codec = { version = "3.2.2", features = ["derive"] } +serde = { version = "1.0.105", features = ["derive"] } +thiserror = "1.0" +sc-client-api = { version = "4.0.0-dev", path = "../../../api" } +sc-consensus-grandpa = { version = "0.10.0-dev", path = "../" } +sc-rpc = { version = "4.0.0-dev", path = "../../../rpc" } +sp-blockchain = { version = "4.0.0-dev", path = "../../../../primitives/blockchain" } +sp-core = { version = "7.0.0", path = "../../../../primitives/core" } +sp-runtime = { version = "7.0.0", path = "../../../../primitives/runtime" } + +[dev-dependencies] +sc-block-builder = { version = "0.10.0-dev", path = "../../../block-builder" } +sc-rpc = { version = "4.0.0-dev", features = ["test-helpers"], path = "../../../rpc" } +sp-core = { version = "7.0.0", path = "../../../../primitives/core" } +sp-consensus-grandpa = { version = "4.0.0-dev", path = "../../../../primitives/consensus/grandpa" } +sp-keyring = { version = "7.0.0", path = "../../../../primitives/keyring" } +substrate-test-runtime-client = { version = "2.0.0", path = "../../../../test-utils/runtime/client" } +tokio = { version = "1.22.0", features = ["macros"] } diff --git a/client/finality-grandpa/rpc/README.md b/client/consensus/grandpa/rpc/README.md similarity index 100% rename from client/finality-grandpa/rpc/README.md rename to client/consensus/grandpa/rpc/README.md diff --git a/client/finality-grandpa/rpc/src/error.rs b/client/consensus/grandpa/rpc/src/error.rs similarity index 97% rename from client/finality-grandpa/rpc/src/error.rs rename to client/consensus/grandpa/rpc/src/error.rs index 885408d8f..4884380cd 100644 --- a/client/finality-grandpa/rpc/src/error.rs +++ b/client/consensus/grandpa/rpc/src/error.rs @@ -35,7 +35,7 @@ pub enum Error { VoterStateReportsUnreasonablyLargeNumbers, /// GRANDPA prove finality failed. #[error("GRANDPA prove finality rpc failed: {0}")] - ProveFinalityFailed(#[from] sc_finality_grandpa::FinalityProofError), + ProveFinalityFailed(#[from] sc_consensus_grandpa::FinalityProofError), } /// The error codes returned by jsonrpc. diff --git a/client/finality-grandpa/rpc/src/finality.rs b/client/consensus/grandpa/rpc/src/finality.rs similarity index 88% rename from client/finality-grandpa/rpc/src/finality.rs rename to client/consensus/grandpa/rpc/src/finality.rs index dd1f0af86..f8ec01781 100644 --- a/client/finality-grandpa/rpc/src/finality.rs +++ b/client/consensus/grandpa/rpc/src/finality.rs @@ -18,7 +18,7 @@ use serde::{Deserialize, Serialize}; -use sc_finality_grandpa::FinalityProofProvider; +use sc_consensus_grandpa::FinalityProofProvider; use sp_runtime::traits::{Block as BlockT, NumberFor}; #[derive(Serialize, Deserialize)] @@ -31,7 +31,7 @@ pub trait RpcFinalityProofProvider { fn rpc_prove_finality( &self, block: NumberFor, - ) -> Result, sc_finality_grandpa::FinalityProofError>; + ) -> Result, sc_consensus_grandpa::FinalityProofError>; } impl RpcFinalityProofProvider for FinalityProofProvider @@ -43,7 +43,7 @@ where fn rpc_prove_finality( &self, block: NumberFor, - ) -> Result, sc_finality_grandpa::FinalityProofError> { + ) -> Result, sc_consensus_grandpa::FinalityProofError> { self.prove_finality(block).map(|x| x.map(|y| EncodedFinalityProof(y.into()))) } } diff --git a/client/finality-grandpa/rpc/src/lib.rs b/client/consensus/grandpa/rpc/src/lib.rs similarity index 96% rename from client/finality-grandpa/rpc/src/lib.rs rename to client/consensus/grandpa/rpc/src/lib.rs index 8f06531be..c6298bff9 100644 --- a/client/finality-grandpa/rpc/src/lib.rs +++ b/client/consensus/grandpa/rpc/src/lib.rs @@ -35,7 +35,7 @@ mod finality; mod notification; mod report; -use sc_finality_grandpa::GrandpaJustificationStream; +use sc_consensus_grandpa::GrandpaJustificationStream; use sc_rpc::SubscriptionTaskExecutor; use sp_runtime::traits::{Block as BlockT, NumberFor}; @@ -105,7 +105,7 @@ where fn subscribe_justifications(&self, mut sink: SubscriptionSink) -> SubscriptionResult { let stream = self.justification_stream.subscribe(100_000).map( - |x: sc_finality_grandpa::GrandpaJustification| { + |x: sc_consensus_grandpa::GrandpaJustification| { JustificationNotification::from(x) }, ); @@ -143,7 +143,7 @@ mod tests { }; use parity_scale_codec::{Decode, Encode}; use sc_block_builder::{BlockBuilder, RecordProof}; - use sc_finality_grandpa::{ + use sc_consensus_grandpa::{ report, AuthorityId, FinalityProof, GrandpaJustification, GrandpaJustificationSender, }; use sp_blockchain::HeaderBackend; @@ -200,7 +200,7 @@ mod tests { fn rpc_prove_finality( &self, _block: NumberFor, - ) -> Result, sc_finality_grandpa::FinalityProofError> { + ) -> Result, sc_consensus_grandpa::FinalityProofError> { Ok(Some(EncodedFinalityProof( self.finality_proof .as_ref() @@ -216,7 +216,7 @@ mod tests { let voter_id_1 = AuthorityId::from_slice(&[1; 32]).unwrap(); let voters_best: HashSet<_> = vec![voter_id_1].into_iter().collect(); - let best_round_state = sc_finality_grandpa::report::RoundState { + let best_round_state = sc_consensus_grandpa::report::RoundState { total_weight: 100_u64.try_into().unwrap(), threshold_weight: 67_u64.try_into().unwrap(), prevote_current_weight: 50.into(), @@ -225,7 +225,7 @@ mod tests { precommit_ids: HashSet::new(), }; - let past_round_state = sc_finality_grandpa::report::RoundState { + let past_round_state = sc_consensus_grandpa::report::RoundState { total_weight: 100_u64.try_into().unwrap(), threshold_weight: 67_u64.try_into().unwrap(), prevote_current_weight: 100.into(), @@ -364,7 +364,7 @@ mod tests { }; let msg = finality_grandpa::Message::Precommit(precommit.clone()); - let encoded = sp_finality_grandpa::localized_payload(round, set_id, &msg); + let encoded = sp_consensus_grandpa::localized_payload(round, set_id, &msg); let signature = peers[0].sign(&encoded[..]).into(); let precommit = finality_grandpa::SignedPrecommit { diff --git a/client/finality-grandpa/rpc/src/notification.rs b/client/consensus/grandpa/rpc/src/notification.rs similarity index 96% rename from client/finality-grandpa/rpc/src/notification.rs rename to client/consensus/grandpa/rpc/src/notification.rs index 45e7d3488..42b9123ed 100644 --- a/client/finality-grandpa/rpc/src/notification.rs +++ b/client/consensus/grandpa/rpc/src/notification.rs @@ -17,7 +17,7 @@ // along with this program. If not, see . use parity_scale_codec::Encode; -use sc_finality_grandpa::GrandpaJustification; +use sc_consensus_grandpa::GrandpaJustification; use serde::{Deserialize, Serialize}; use sp_runtime::traits::Block as BlockT; diff --git a/client/finality-grandpa/rpc/src/report.rs b/client/consensus/grandpa/rpc/src/report.rs similarity index 97% rename from client/finality-grandpa/rpc/src/report.rs rename to client/consensus/grandpa/rpc/src/report.rs index 58e59d8ae..ae4fd76d2 100644 --- a/client/finality-grandpa/rpc/src/report.rs +++ b/client/consensus/grandpa/rpc/src/report.rs @@ -24,7 +24,7 @@ use std::{ use serde::{Deserialize, Serialize}; -use sc_finality_grandpa::{report, AuthorityId, SharedAuthoritySet, SharedVoterState}; +use sc_consensus_grandpa::{report, AuthorityId, SharedAuthoritySet, SharedVoterState}; use crate::error::Error; diff --git a/client/finality-grandpa/src/authorities.rs b/client/consensus/grandpa/src/authorities.rs similarity index 99% rename from client/finality-grandpa/src/authorities.rs rename to client/consensus/grandpa/src/authorities.rs index 245c6bfe3..623223e41 100644 --- a/client/finality-grandpa/src/authorities.rs +++ b/client/consensus/grandpa/src/authorities.rs @@ -27,7 +27,7 @@ use parity_scale_codec::{Decode, Encode}; use parking_lot::MappedMutexGuard; use sc_consensus::shared_data::{SharedData, SharedDataLocked}; use sc_telemetry::{telemetry, TelemetryHandle, CONSENSUS_INFO}; -use sp_finality_grandpa::{AuthorityId, AuthorityList}; +use sp_consensus_grandpa::{AuthorityId, AuthorityList}; use crate::{SetId, LOG_TARGET}; diff --git a/client/finality-grandpa/src/aux_schema.rs b/client/consensus/grandpa/src/aux_schema.rs similarity index 99% rename from client/finality-grandpa/src/aux_schema.rs rename to client/consensus/grandpa/src/aux_schema.rs index fbb0ab8a8..97a8bc660 100644 --- a/client/finality-grandpa/src/aux_schema.rs +++ b/client/consensus/grandpa/src/aux_schema.rs @@ -27,7 +27,7 @@ use parity_scale_codec::{Decode, Encode}; use fork_tree::ForkTree; use sc_client_api::backend::AuxStore; use sp_blockchain::{Error as ClientError, Result as ClientResult}; -use sp_finality_grandpa::{AuthorityList, RoundNumber, SetId}; +use sp_consensus_grandpa::{AuthorityList, RoundNumber, SetId}; use sp_runtime::traits::{Block as BlockT, NumberFor}; use crate::{ @@ -501,8 +501,8 @@ pub(crate) fn load_authorities( #[cfg(test)] mod test { use super::*; + use sp_consensus_grandpa::AuthorityId; use sp_core::{crypto::UncheckedFrom, H256}; - use sp_finality_grandpa::AuthorityId; use substrate_test_runtime_client::{self, runtime::Block}; fn dummy_id() -> AuthorityId { diff --git a/client/finality-grandpa/src/communication/gossip.rs b/client/consensus/grandpa/src/communication/gossip.rs similarity index 99% rename from client/finality-grandpa/src/communication/gossip.rs rename to client/consensus/grandpa/src/communication/gossip.rs index 65e765a4c..cf476f8e3 100644 --- a/client/finality-grandpa/src/communication/gossip.rs +++ b/client/consensus/grandpa/src/communication/gossip.rs @@ -95,7 +95,7 @@ use sc_network_common::protocol::role::ObservedRole; use sc_network_gossip::{MessageIntent, ValidatorContext}; use sc_telemetry::{telemetry, TelemetryHandle, CONSENSUS_DEBUG}; use sc_utils::mpsc::{tracing_unbounded, TracingUnboundedReceiver, TracingUnboundedSender}; -use sp_finality_grandpa::AuthorityId; +use sp_consensus_grandpa::AuthorityId; use sp_runtime::traits::{Block as BlockT, NumberFor, Zero}; use super::{benefit, cost, Round, SetId, NEIGHBOR_REBROADCAST_PERIOD}; @@ -931,7 +931,7 @@ impl Inner { return Action::Discard(cost::UNKNOWN_VOTER) } - if !sp_finality_grandpa::check_message_signature( + if !sp_consensus_grandpa::check_message_signature( &full.message.message, &full.message.id, &full.message.signature, diff --git a/client/finality-grandpa/src/communication/mod.rs b/client/consensus/grandpa/src/communication/mod.rs similarity index 99% rename from client/finality-grandpa/src/communication/mod.rs rename to client/consensus/grandpa/src/communication/mod.rs index 3c1de1f46..d3ade4bf3 100644 --- a/client/finality-grandpa/src/communication/mod.rs +++ b/client/consensus/grandpa/src/communication/mod.rs @@ -61,7 +61,7 @@ use gossip::{ }; use sc_network_common::service::{NetworkBlock, NetworkSyncForkRequest}; use sc_utils::mpsc::TracingUnboundedReceiver; -use sp_finality_grandpa::{AuthorityId, AuthoritySignature, RoundNumber, SetId as SetIdNumber}; +use sp_consensus_grandpa::{AuthorityId, AuthoritySignature, RoundNumber, SetId as SetIdNumber}; pub mod gossip; mod periodic; @@ -739,7 +739,7 @@ impl Sink> for OutgoingMessages { // when locals exist, sign messages on import if let Some(ref keystore) = self.keystore { let target_hash = *(msg.target().0); - let signed = sp_finality_grandpa::sign_message( + let signed = sp_consensus_grandpa::sign_message( keystore.keystore(), msg, keystore.local_id().clone(), @@ -842,7 +842,7 @@ fn check_compact_commit( use crate::communication::gossip::Misbehavior; use finality_grandpa::Message as GrandpaMessage; - if !sp_finality_grandpa::check_message_signature_with_buffer( + if !sp_consensus_grandpa::check_message_signature_with_buffer( &GrandpaMessage::Precommit(precommit.clone()), id, sig, @@ -934,7 +934,7 @@ fn check_catch_up( for (msg, id, sig) in messages { signatures_checked += 1; - if !sp_finality_grandpa::check_message_signature_with_buffer( + if !sp_consensus_grandpa::check_message_signature_with_buffer( &msg, id, sig, round, set_id, buf, ) { debug!(target: LOG_TARGET, "Bad catch up message signature {}", id); diff --git a/client/finality-grandpa/src/communication/periodic.rs b/client/consensus/grandpa/src/communication/periodic.rs similarity index 100% rename from client/finality-grandpa/src/communication/periodic.rs rename to client/consensus/grandpa/src/communication/periodic.rs diff --git a/client/finality-grandpa/src/communication/tests.rs b/client/consensus/grandpa/src/communication/tests.rs similarity index 97% rename from client/finality-grandpa/src/communication/tests.rs rename to client/consensus/grandpa/src/communication/tests.rs index 19c2e4fef..21e2e978c 100644 --- a/client/finality-grandpa/src/communication/tests.rs +++ b/client/consensus/grandpa/src/communication/tests.rs @@ -37,7 +37,7 @@ use sc_network_common::{ use sc_network_gossip::Validator; use sc_network_test::{Block, Hash}; use sc_utils::mpsc::{tracing_unbounded, TracingUnboundedReceiver, TracingUnboundedSender}; -use sp_finality_grandpa::AuthorityList; +use sp_consensus_grandpa::AuthorityList; use sp_keyring::Ed25519Keyring; use sp_runtime::traits::NumberFor; use std::{ @@ -237,8 +237,8 @@ fn config() -> crate::Config { fn voter_set_state() -> SharedVoterSetState { use crate::{authorities::AuthoritySet, environment::VoterSetState}; use finality_grandpa::round::State as RoundState; + use sp_consensus_grandpa::AuthorityId; use sp_core::{crypto::ByteArray, H256}; - use sp_finality_grandpa::AuthorityId; let state = RoundState::genesis((H256::zero(), 0)); let base = state.prevote_ghost.unwrap(); @@ -306,7 +306,7 @@ fn good_commit_leads_to_relay() { let target_number = 500; let precommit = finality_grandpa::Precommit { target_hash, target_number }; - let payload = sp_finality_grandpa::localized_payload( + let payload = sp_consensus_grandpa::localized_payload( round, set_id, &finality_grandpa::Message::Precommit(precommit.clone()), @@ -318,7 +318,7 @@ fn good_commit_leads_to_relay() { for (i, key) in private.iter().enumerate() { precommits.push(precommit.clone()); - let signature = sp_finality_grandpa::AuthoritySignature::from(key.sign(&payload[..])); + let signature = sp_consensus_grandpa::AuthoritySignature::from(key.sign(&payload[..])); auth_data.push((signature, public[i].0.clone())) } @@ -456,7 +456,7 @@ fn bad_commit_leads_to_report() { let target_number = 500; let precommit = finality_grandpa::Precommit { target_hash, target_number }; - let payload = sp_finality_grandpa::localized_payload( + let payload = sp_consensus_grandpa::localized_payload( round, set_id, &finality_grandpa::Message::Precommit(precommit.clone()), @@ -468,7 +468,7 @@ fn bad_commit_leads_to_report() { for (i, key) in private.iter().enumerate() { precommits.push(precommit.clone()); - let signature = sp_finality_grandpa::AuthoritySignature::from(key.sign(&payload[..])); + let signature = sp_consensus_grandpa::AuthoritySignature::from(key.sign(&payload[..])); auth_data.push((signature, public[i].0.clone())) } @@ -631,7 +631,7 @@ fn local_chain_spec() -> Box { } } let chain_spec = GenericChainSpec::::from_json_bytes( - &include_bytes!("../../../chain-spec/res/chain_spec.json")[..], + &include_bytes!("../../../../chain-spec/res/chain_spec.json")[..], ) .unwrap(); chain_spec.cloned_box() diff --git a/client/finality-grandpa/src/environment.rs b/client/consensus/grandpa/src/environment.rs similarity index 99% rename from client/finality-grandpa/src/environment.rs rename to client/consensus/grandpa/src/environment.rs index cdc98fc22..342542661 100644 --- a/client/finality-grandpa/src/environment.rs +++ b/client/consensus/grandpa/src/environment.rs @@ -42,7 +42,7 @@ use sc_client_api::{ use sc_telemetry::{telemetry, TelemetryHandle, CONSENSUS_DEBUG, CONSENSUS_INFO}; use sp_blockchain::HeaderMetadata; use sp_consensus::SelectChain as SelectChainT; -use sp_finality_grandpa::{ +use sp_consensus_grandpa::{ AuthorityId, AuthoritySignature, Equivocation, EquivocationProof, GrandpaApi, RoundNumber, SetId, GRANDPA_ENGINE_ID, }; diff --git a/client/finality-grandpa/src/finality_proof.rs b/client/consensus/grandpa/src/finality_proof.rs similarity index 98% rename from client/finality-grandpa/src/finality_proof.rs rename to client/consensus/grandpa/src/finality_proof.rs index b042d6fce..8a8a68858 100644 --- a/client/finality-grandpa/src/finality_proof.rs +++ b/client/consensus/grandpa/src/finality_proof.rs @@ -42,7 +42,7 @@ use std::sync::Arc; use parity_scale_codec::{Decode, Encode}; use sc_client_api::backend::Backend; use sp_blockchain::{Backend as BlockchainBackend, HeaderBackend}; -use sp_finality_grandpa::GRANDPA_ENGINE_ID; +use sp_consensus_grandpa::GRANDPA_ENGINE_ID; use sp_runtime::{ generic::BlockId, traits::{Block as BlockT, Header as HeaderT, NumberFor, One}, @@ -264,8 +264,8 @@ mod tests { use sc_block_builder::BlockBuilderProvider; use sc_client_api::{apply_aux, LockImportRun}; use sp_consensus::BlockOrigin; + use sp_consensus_grandpa::GRANDPA_ENGINE_ID as ID; use sp_core::crypto::UncheckedFrom; - use sp_finality_grandpa::GRANDPA_ENGINE_ID as ID; use sp_keyring::Ed25519Keyring; use substrate_test_runtime_client::{ runtime::{Block, Header, H256}, @@ -279,7 +279,7 @@ mod tests { /// AND if at least one of those headers is invalid, all other MUST be considered invalid. fn check_finality_proof( current_set_id: SetId, - current_authorities: sp_finality_grandpa::AuthorityList, + current_authorities: sp_consensus_grandpa::AuthorityList, remote_proof: Vec, ) -> sp_blockchain::Result> where @@ -402,7 +402,7 @@ mod tests { }; let grandpa_just: GrandpaJustification = - sp_finality_grandpa::GrandpaJustification::
{ + sp_consensus_grandpa::GrandpaJustification::
{ round: 8, votes_ancestries: Vec::new(), commit, @@ -442,7 +442,7 @@ mod tests { }; let msg = finality_grandpa::Message::Precommit(precommit.clone()); - let encoded = sp_finality_grandpa::localized_payload(round, set_id, &msg); + let encoded = sp_consensus_grandpa::localized_payload(round, set_id, &msg); let signature = voter.sign(&encoded[..]).into(); let signed_precommit = finality_grandpa::SignedPrecommit { diff --git a/client/finality-grandpa/src/import.rs b/client/consensus/grandpa/src/import.rs similarity index 99% rename from client/finality-grandpa/src/import.rs rename to client/consensus/grandpa/src/import.rs index 19c424637..c1f6cb566 100644 --- a/client/finality-grandpa/src/import.rs +++ b/client/consensus/grandpa/src/import.rs @@ -31,8 +31,8 @@ use sc_utils::mpsc::TracingUnboundedSender; use sp_api::{Core, RuntimeApiInfo, TransactionFor}; use sp_blockchain::{well_known_cache_keys, BlockStatus}; use sp_consensus::{BlockOrigin, Error as ConsensusError, SelectChain}; +use sp_consensus_grandpa::{ConsensusLog, GrandpaApi, ScheduledChange, SetId, GRANDPA_ENGINE_ID}; use sp_core::hashing::twox_128; -use sp_finality_grandpa::{ConsensusLog, GrandpaApi, ScheduledChange, SetId, GRANDPA_ENGINE_ID}; use sp_runtime::{ generic::OpaqueDigestItemId, traits::{Block as BlockT, Header as HeaderT, NumberFor, Zero}, diff --git a/client/finality-grandpa/src/justification.rs b/client/consensus/grandpa/src/justification.rs similarity index 93% rename from client/finality-grandpa/src/justification.rs rename to client/consensus/grandpa/src/justification.rs index 1534ca752..c300a3d7a 100644 --- a/client/finality-grandpa/src/justification.rs +++ b/client/consensus/grandpa/src/justification.rs @@ -25,7 +25,7 @@ use std::{ use finality_grandpa::{voter_set::VoterSet, Error as GrandpaError}; use parity_scale_codec::{Decode, Encode}; use sp_blockchain::{Error as ClientError, HeaderBackend}; -use sp_finality_grandpa::AuthorityId; +use sp_consensus_grandpa::AuthorityId; use sp_runtime::traits::{Block as BlockT, Header as HeaderT, NumberFor}; use crate::{AuthorityList, Commit, Error}; @@ -41,22 +41,22 @@ use crate::{AuthorityList, Commit, Error}; #[derive(Clone, Encode, Decode, PartialEq, Eq, Debug)] pub struct GrandpaJustification { /// The GRANDPA justification for block finality. - pub justification: sp_finality_grandpa::GrandpaJustification, + pub justification: sp_consensus_grandpa::GrandpaJustification, _block: PhantomData, } -impl From> +impl From> for GrandpaJustification { - fn from(justification: sp_finality_grandpa::GrandpaJustification) -> Self { + fn from(justification: sp_consensus_grandpa::GrandpaJustification) -> Self { Self { justification, _block: Default::default() } } } -impl Into> +impl Into> for GrandpaJustification { - fn into(self) -> sp_finality_grandpa::GrandpaJustification { + fn into(self) -> sp_consensus_grandpa::GrandpaJustification { self.justification } } @@ -122,7 +122,7 @@ impl GrandpaJustification { } } - Ok(sp_finality_grandpa::GrandpaJustification { round, commit, votes_ancestries }.into()) + Ok(sp_consensus_grandpa::GrandpaJustification { round, commit, votes_ancestries }.into()) } /// Decode a GRANDPA justification and validate the commit and the votes' @@ -205,7 +205,7 @@ impl GrandpaJustification { let mut buf = Vec::new(); let mut visited_hashes = HashSet::new(); for signed in self.justification.commit.precommits.iter() { - if !sp_finality_grandpa::check_message_signature_with_buffer( + if !sp_consensus_grandpa::check_message_signature_with_buffer( &finality_grandpa::Message::Precommit(signed.precommit.clone()), &signed.id, &signed.signature, diff --git a/client/finality-grandpa/src/lib.rs b/client/consensus/grandpa/src/lib.rs similarity index 99% rename from client/finality-grandpa/src/lib.rs rename to client/consensus/grandpa/src/lib.rs index 4f72bb052..ef4beb51b 100644 --- a/client/finality-grandpa/src/lib.rs +++ b/client/consensus/grandpa/src/lib.rs @@ -75,10 +75,10 @@ use sp_api::ProvideRuntimeApi; use sp_application_crypto::AppKey; use sp_blockchain::{Error as ClientError, HeaderBackend, HeaderMetadata, Result as ClientResult}; use sp_consensus::SelectChain; -use sp_core::{crypto::ByteArray, traits::CallContext}; -use sp_finality_grandpa::{ +use sp_consensus_grandpa::{ AuthorityList, AuthoritySignature, SetId, CLIENT_LOG_TARGET as LOG_TARGET, }; +use sp_core::{crypto::ByteArray, traits::CallContext}; use sp_keystore::{SyncCryptoStore, SyncCryptoStorePtr}; use sp_runtime::{ generic::BlockId, @@ -146,7 +146,7 @@ use environment::{Environment, VoterSetState}; use until_imported::UntilGlobalMessageBlocksImported; // Re-export these two because it's just so damn convenient. -pub use sp_finality_grandpa::{ +pub use sp_consensus_grandpa::{ AuthorityId, AuthorityPair, CatchUp, Commit, CompactCommit, GrandpaApi, Message, Precommit, Prevote, PrimaryPropose, ScheduledChange, SignedMessage, }; @@ -1091,7 +1091,7 @@ where Poll::Ready(Ok(())) => { // voters don't conclude naturally return Poll::Ready(Err(Error::Safety( - "finality-grandpa inner voter has concluded.".into(), + "consensus-grandpa inner voter has concluded.".into(), ))) }, Poll::Ready(Err(CommandOrError::Error(e))) => { diff --git a/client/finality-grandpa/src/notification.rs b/client/consensus/grandpa/src/notification.rs similarity index 100% rename from client/finality-grandpa/src/notification.rs rename to client/consensus/grandpa/src/notification.rs diff --git a/client/finality-grandpa/src/observer.rs b/client/consensus/grandpa/src/observer.rs similarity index 99% rename from client/finality-grandpa/src/observer.rs rename to client/consensus/grandpa/src/observer.rs index af56f2ac3..b382430ef 100644 --- a/client/finality-grandpa/src/observer.rs +++ b/client/consensus/grandpa/src/observer.rs @@ -32,7 +32,7 @@ use sc_telemetry::TelemetryHandle; use sc_utils::mpsc::TracingUnboundedReceiver; use sp_blockchain::HeaderMetadata; use sp_consensus::SelectChain; -use sp_finality_grandpa::AuthorityId; +use sp_consensus_grandpa::AuthorityId; use sp_keystore::SyncCryptoStorePtr; use sp_runtime::traits::{Block as BlockT, NumberFor}; diff --git a/client/finality-grandpa/src/tests.rs b/client/consensus/grandpa/src/tests.rs similarity index 99% rename from client/finality-grandpa/src/tests.rs rename to client/consensus/grandpa/src/tests.rs index 1a1f20797..7cc5e75fa 100644 --- a/client/finality-grandpa/src/tests.rs +++ b/client/consensus/grandpa/src/tests.rs @@ -35,10 +35,10 @@ use sc_network_test::{ }; use sp_api::{ApiRef, ProvideRuntimeApi}; use sp_consensus::{BlockOrigin, Error as ConsensusError, SelectChain}; -use sp_core::H256; -use sp_finality_grandpa::{ +use sp_consensus_grandpa::{ AuthorityList, EquivocationProof, GrandpaApi, OpaqueKeyOwnershipProof, GRANDPA_ENGINE_ID, }; +use sp_core::H256; use sp_keyring::Ed25519Keyring; use sp_keystore::{testing::KeyStore as TestKeyStore, SyncCryptoStore, SyncCryptoStorePtr}; use sp_runtime::{ @@ -398,7 +398,7 @@ async fn run_to_completion( fn add_scheduled_change(block: &mut Block, change: ScheduledChange) { block.header.digest_mut().push(DigestItem::Consensus( GRANDPA_ENGINE_ID, - sp_finality_grandpa::ConsensusLog::ScheduledChange(change).encode(), + sp_consensus_grandpa::ConsensusLog::ScheduledChange(change).encode(), )); } @@ -409,7 +409,7 @@ fn add_forced_change( ) { block.header.digest_mut().push(DigestItem::Consensus( GRANDPA_ENGINE_ID, - sp_finality_grandpa::ConsensusLog::ForcedChange(median_last_finalized, change).encode(), + sp_consensus_grandpa::ConsensusLog::ForcedChange(median_last_finalized, change).encode(), )); } @@ -1798,7 +1798,7 @@ async fn justification_with_equivocation() { let precommit = finality_grandpa::Precommit { target_hash, target_number }; let msg = finality_grandpa::Message::Precommit(precommit.clone()); - let encoded = sp_finality_grandpa::localized_payload(round, set_id, &msg); + let encoded = sp_consensus_grandpa::localized_payload(round, set_id, &msg); let precommit = finality_grandpa::SignedPrecommit { precommit: precommit.clone(), @@ -1871,7 +1871,7 @@ async fn imports_justification_for_regular_blocks_on_import() { }; let msg = finality_grandpa::Message::Precommit(precommit.clone()); - let encoded = sp_finality_grandpa::localized_payload(round, set_id, &msg); + let encoded = sp_consensus_grandpa::localized_payload(round, set_id, &msg); let signature = peers[0].sign(&encoded[..]).into(); let precommit = finality_grandpa::SignedPrecommit { @@ -1946,13 +1946,13 @@ async fn grandpa_environment_doesnt_send_equivocation_reports_for_itself() { // reporting the equivocation should fail since the offender is a local // authority (i.e. we have keys in our keystore for the given id) - let equivocation_proof = sp_finality_grandpa::Equivocation::Prevote(equivocation.clone()); + let equivocation_proof = sp_consensus_grandpa::Equivocation::Prevote(equivocation.clone()); assert!(matches!(environment.report_equivocation(equivocation_proof), Err(Error::Safety(_)))); // if we set the equivocation offender to another id for which we don't have // keys it should work equivocation.identity = TryFrom::try_from(&[1; 32][..]).unwrap(); - let equivocation_proof = sp_finality_grandpa::Equivocation::Prevote(equivocation); + let equivocation_proof = sp_consensus_grandpa::Equivocation::Prevote(equivocation); assert!(environment.report_equivocation(equivocation_proof).is_ok()); } diff --git a/client/finality-grandpa/src/until_imported.rs b/client/consensus/grandpa/src/until_imported.rs similarity index 99% rename from client/finality-grandpa/src/until_imported.rs rename to client/consensus/grandpa/src/until_imported.rs index f9ecb7df3..14f32ecc8 100644 --- a/client/finality-grandpa/src/until_imported.rs +++ b/client/consensus/grandpa/src/until_imported.rs @@ -38,7 +38,7 @@ use parking_lot::Mutex; use prometheus_endpoint::{register, Gauge, PrometheusError, Registry, U64}; use sc_client_api::{BlockImportNotification, ImportNotifications}; use sc_utils::mpsc::TracingUnboundedReceiver; -use sp_finality_grandpa::AuthorityId; +use sp_consensus_grandpa::AuthorityId; use sp_runtime::traits::{Block as BlockT, Header as HeaderT, NumberFor}; use std::{ diff --git a/client/finality-grandpa/src/voting_rule.rs b/client/consensus/grandpa/src/voting_rule.rs similarity index 100% rename from client/finality-grandpa/src/voting_rule.rs rename to client/consensus/grandpa/src/voting_rule.rs diff --git a/client/finality-grandpa/src/warp_proof.rs b/client/consensus/grandpa/src/warp_proof.rs similarity index 97% rename from client/finality-grandpa/src/warp_proof.rs rename to client/consensus/grandpa/src/warp_proof.rs index f73a0a323..cd4fedf96 100644 --- a/client/finality-grandpa/src/warp_proof.rs +++ b/client/consensus/grandpa/src/warp_proof.rs @@ -25,7 +25,7 @@ use crate::{ use sc_client_api::Backend as ClientBackend; use sc_network_common::sync::warp::{EncodedProof, VerificationResult, WarpSyncProvider}; use sp_blockchain::{Backend as BlockchainBackend, HeaderBackend}; -use sp_finality_grandpa::{AuthorityList, SetId, GRANDPA_ENGINE_ID}; +use sp_consensus_grandpa::{AuthorityList, SetId, GRANDPA_ENGINE_ID}; use sp_runtime::{ generic::BlockId, traits::{Block as BlockT, Header as HeaderT, NumberFor, One}, @@ -324,7 +324,7 @@ mod tests { use sc_block_builder::BlockBuilderProvider; use sp_blockchain::HeaderBackend; use sp_consensus::BlockOrigin; - use sp_finality_grandpa::GRANDPA_ENGINE_ID; + use sp_consensus_grandpa::GRANDPA_ENGINE_ID; use sp_keyring::Ed25519Keyring; use sp_runtime::traits::Header as _; use std::sync::Arc; @@ -369,9 +369,9 @@ mod tests { .collect::>(); let digest = sp_runtime::generic::DigestItem::Consensus( - sp_finality_grandpa::GRANDPA_ENGINE_ID, - sp_finality_grandpa::ConsensusLog::ScheduledChange( - sp_finality_grandpa::ScheduledChange { delay: 0u64, next_authorities }, + sp_consensus_grandpa::GRANDPA_ENGINE_ID, + sp_consensus_grandpa::ConsensusLog::ScheduledChange( + sp_consensus_grandpa::ScheduledChange { delay: 0u64, next_authorities }, ) .encode(), ); @@ -394,7 +394,7 @@ mod tests { let precommit = finality_grandpa::Precommit { target_hash, target_number }; let msg = finality_grandpa::Message::Precommit(precommit.clone()); - let encoded = sp_finality_grandpa::localized_payload(42, current_set_id, &msg); + let encoded = sp_consensus_grandpa::localized_payload(42, current_set_id, &msg); let signature = keyring.sign(&encoded[..]).into(); let precommit = finality_grandpa::SignedPrecommit { diff --git a/client/finality-grandpa/Cargo.toml b/client/finality-grandpa/Cargo.toml deleted file mode 100644 index 8a4e0449f..000000000 --- a/client/finality-grandpa/Cargo.toml +++ /dev/null @@ -1,60 +0,0 @@ -[package] -name = "sc-finality-grandpa" -version = "0.10.0-dev" -authors = ["Parity Technologies "] -edition = "2021" -license = "GPL-3.0-or-later WITH Classpath-exception-2.0" -homepage = "https://substrate.io" -repository = "https://github.com/paritytech/substrate/" -description = "Integration of the GRANDPA finality gadget into substrate." -documentation = "https://docs.rs/sc-finality-grandpa" -readme = "README.md" - -[package.metadata.docs.rs] -targets = ["x86_64-unknown-linux-gnu"] - -[dependencies] -ahash = "0.8.2" -array-bytes = "4.1" -async-trait = "0.1.57" -dyn-clone = "1.0" -finality-grandpa = { version = "0.16.1", features = ["derive-codec"] } -futures = "0.3.21" -futures-timer = "3.0.1" -log = "0.4.17" -parity-scale-codec = { version = "3.2.2", features = ["derive"] } -parking_lot = "0.12.1" -rand = "0.8.5" -serde_json = "1.0.85" -thiserror = "1.0" -fork-tree = { version = "3.0.0", path = "../../utils/fork-tree" } -prometheus-endpoint = { package = "substrate-prometheus-endpoint", version = "0.10.0-dev", path = "../../utils/prometheus" } -sc-block-builder = { version = "0.10.0-dev", path = "../block-builder" } -sc-chain-spec = { version = "4.0.0-dev", path = "../../client/chain-spec" } -sc-client-api = { version = "4.0.0-dev", path = "../api" } -sc-consensus = { version = "0.10.0-dev", path = "../consensus/common" } -sc-network = { version = "0.10.0-dev", path = "../network" } -sc-network-gossip = { version = "0.10.0-dev", path = "../network-gossip" } -sc-network-common = { version = "0.10.0-dev", path = "../network/common" } -sc-telemetry = { version = "4.0.0-dev", path = "../telemetry" } -sc-utils = { version = "4.0.0-dev", path = "../utils" } -sp-api = { version = "4.0.0-dev", path = "../../primitives/api" } -sp-application-crypto = { version = "7.0.0", path = "../../primitives/application-crypto" } -sp-arithmetic = { version = "6.0.0", path = "../../primitives/arithmetic" } -sp-blockchain = { version = "4.0.0-dev", path = "../../primitives/blockchain" } -sp-consensus = { version = "0.10.0-dev", path = "../../primitives/consensus/common" } -sp-core = { version = "7.0.0", path = "../../primitives/core" } -sp-finality-grandpa = { version = "4.0.0-dev", path = "../../primitives/finality-grandpa" } -sp-keystore = { version = "0.13.0", path = "../../primitives/keystore" } -sp-runtime = { version = "7.0.0", path = "../../primitives/runtime" } - -[dev-dependencies] -assert_matches = "1.3.0" -finality-grandpa = { version = "0.16.1", features = ["derive-codec", "test-helpers"] } -serde = "1.0.136" -tokio = "1.22.0" -sc-network = { version = "0.10.0-dev", path = "../network" } -sc-network-test = { version = "0.8.0", path = "../network/test" } -sp-keyring = { version = "7.0.0", path = "../../primitives/keyring" } -sp-tracing = { version = "6.0.0", path = "../../primitives/tracing" } -substrate-test-runtime-client = { version = "2.0.0", path = "../../test-utils/runtime/client" } diff --git a/client/finality-grandpa/rpc/Cargo.toml b/client/finality-grandpa/rpc/Cargo.toml deleted file mode 100644 index e20ff174d..000000000 --- a/client/finality-grandpa/rpc/Cargo.toml +++ /dev/null @@ -1,36 +0,0 @@ -[package] -name = "sc-finality-grandpa-rpc" -version = "0.10.0-dev" -authors = ["Parity Technologies "] -description = "RPC extensions for the GRANDPA finality gadget" -repository = "https://github.com/paritytech/substrate/" -edition = "2021" -license = "GPL-3.0-or-later WITH Classpath-exception-2.0" -readme = "README.md" -homepage = "https://substrate.io" - -[dependencies] -finality-grandpa = { version = "0.16.1", features = ["derive-codec"] } -futures = "0.3.16" -jsonrpsee = { version = "0.16.2", features = ["client-core", "server", "macros"] } -log = "0.4.8" -parity-scale-codec = { version = "3.2.2", features = ["derive"] } -serde = { version = "1.0.105", features = ["derive"] } -thiserror = "1.0" -sc-client-api = { version = "4.0.0-dev", path = "../../api" } -sc-finality-grandpa = { version = "0.10.0-dev", path = "../" } -sc-rpc = { version = "4.0.0-dev", path = "../../rpc" } -sp-blockchain = { version = "4.0.0-dev", path = "../../../primitives/blockchain" } -sp-core = { version = "7.0.0", path = "../../../primitives/core" } -sp-runtime = { version = "7.0.0", path = "../../../primitives/runtime" } - -[dev-dependencies] -sc-block-builder = { version = "0.10.0-dev", path = "../../block-builder" } -sc-rpc = { version = "4.0.0-dev", features = [ - "test-helpers", -], path = "../../rpc" } -sp-core = { version = "7.0.0", path = "../../../primitives/core" } -sp-finality-grandpa = { version = "4.0.0-dev", path = "../../../primitives/finality-grandpa" } -sp-keyring = { version = "7.0.0", path = "../../../primitives/keyring" } -substrate-test-runtime-client = { version = "2.0.0", path = "../../../test-utils/runtime/client" } -tokio = { version = "1.22.0", features = ["macros"] } diff --git a/client/network/README.md b/client/network/README.md index c361bc924..d39674724 100644 --- a/client/network/README.md +++ b/client/network/README.md @@ -190,7 +190,7 @@ The API of `sc-network` allows one to register user-defined notification protoco `sc-network` automatically tries to open a substream towards each node for which the legacy Substream substream is open. The handshake is then performed automatically. -For example, the `sc-finality-grandpa` crate registers the `/paritytech/grandpa/1` +For example, the `sc-consensus-grandpa` crate registers the `/paritytech/grandpa/1` notifications protocol. At the moment, for backwards-compatibility, notification protocols are tied to the legacy diff --git a/client/network/common/Cargo.toml b/client/network/common/Cargo.toml index 2aaada021..06ff00ef7 100644 --- a/client/network/common/Cargo.toml +++ b/client/network/common/Cargo.toml @@ -32,7 +32,7 @@ sc-consensus = { version = "0.10.0-dev", path = "../../consensus/common" } sc-peerset = { version = "4.0.0-dev", path = "../../peerset" } serde = { version = "1.0.136", features = ["derive"] } sp-consensus = { version = "0.10.0-dev", path = "../../../primitives/consensus/common" } -sp-finality-grandpa = { version = "4.0.0-dev", path = "../../../primitives/finality-grandpa" } +sp-consensus-grandpa = { version = "4.0.0-dev", path = "../../../primitives/consensus/grandpa" } sp-runtime = { version = "7.0.0", path = "../../../primitives/runtime" } sp-blockchain = { version = "4.0.0-dev", path = "../../../primitives/blockchain" } thiserror = "1.0" diff --git a/client/network/common/src/sync/warp.rs b/client/network/common/src/sync/warp.rs index a906091ff..5f2cacd2e 100644 --- a/client/network/common/src/sync/warp.rs +++ b/client/network/common/src/sync/warp.rs @@ -16,7 +16,7 @@ use codec::{Decode, Encode}; use futures::channel::oneshot; -pub use sp_finality_grandpa::{AuthorityList, SetId}; +pub use sp_consensus_grandpa::{AuthorityList, SetId}; use sp_runtime::traits::{Block as BlockT, NumberFor}; use std::{fmt, sync::Arc}; diff --git a/client/network/src/lib.rs b/client/network/src/lib.rs index 8ecb3e549..8714d66f1 100644 --- a/client/network/src/lib.rs +++ b/client/network/src/lib.rs @@ -212,7 +212,7 @@ //! `sc-network` automatically tries to open a substream towards each node for which the legacy //! Substream substream is open. The handshake is then performed automatically. //! -//! For example, the `sc-finality-grandpa` crate registers the `/paritytech/grandpa/1` +//! For example, the `sc-consensus-grandpa` crate registers the `/paritytech/grandpa/1` //! notifications protocol. //! //! At the moment, for backwards-compatibility, notification protocols are tied to the legacy diff --git a/client/network/sync/Cargo.toml b/client/network/sync/Cargo.toml index 532e87383..bd51776d6 100644 --- a/client/network/sync/Cargo.toml +++ b/client/network/sync/Cargo.toml @@ -38,7 +38,7 @@ sp-arithmetic = { version = "6.0.0", path = "../../../primitives/arithmetic" } sp-blockchain = { version = "4.0.0-dev", path = "../../../primitives/blockchain" } sp-consensus = { version = "0.10.0-dev", path = "../../../primitives/consensus/common" } sp-core = { version = "7.0.0", path = "../../../primitives/core" } -sp-finality-grandpa = { version = "4.0.0-dev", path = "../../../primitives/finality-grandpa" } +sp-consensus-grandpa = { version = "4.0.0-dev", path = "../../../primitives/consensus/grandpa" } sp-runtime = { version = "7.0.0", path = "../../../primitives/runtime" } [dev-dependencies] diff --git a/client/network/sync/src/warp.rs b/client/network/sync/src/warp.rs index 21af50787..912ad78df 100644 --- a/client/network/sync/src/warp.rs +++ b/client/network/sync/src/warp.rs @@ -34,7 +34,7 @@ use sc_network_common::sync::{ }, }; use sp_blockchain::HeaderBackend; -use sp_finality_grandpa::{AuthorityList, SetId}; +use sp_consensus_grandpa::{AuthorityList, SetId}; use sp_runtime::traits::{Block as BlockT, Header, NumberFor, Zero}; use std::{sync::Arc, task::Poll}; diff --git a/client/sync-state-rpc/Cargo.toml b/client/sync-state-rpc/Cargo.toml index 3a964521c..5447c809c 100644 --- a/client/sync-state-rpc/Cargo.toml +++ b/client/sync-state-rpc/Cargo.toml @@ -21,6 +21,6 @@ sc-chain-spec = { version = "4.0.0-dev", path = "../chain-spec" } sc-client-api = { version = "4.0.0-dev", path = "../api" } sc-consensus-babe = { version = "0.10.0-dev", path = "../consensus/babe" } sc-consensus-epochs = { version = "0.10.0-dev", path = "../consensus/epochs" } -sc-finality-grandpa = { version = "0.10.0-dev", path = "../finality-grandpa" } +sc-consensus-grandpa = { version = "0.10.0-dev", path = "../consensus/grandpa" } sp-blockchain = { version = "4.0.0-dev", path = "../../primitives/blockchain" } sp-runtime = { version = "7.0.0", path = "../../primitives/runtime" } diff --git a/client/sync-state-rpc/src/lib.rs b/client/sync-state-rpc/src/lib.rs index 94bf53c5a..78d5cafa3 100644 --- a/client/sync-state-rpc/src/lib.rs +++ b/client/sync-state-rpc/src/lib.rs @@ -52,7 +52,7 @@ use sp_runtime::traits::{Block as BlockT, NumberFor}; use std::sync::Arc; type SharedAuthoritySet = - sc_finality_grandpa::SharedAuthoritySet<::Hash, NumberFor>; + sc_consensus_grandpa::SharedAuthoritySet<::Hash, NumberFor>; type SharedEpochChanges = sc_consensus_epochs::SharedEpochChanges; @@ -117,7 +117,7 @@ pub struct LightSyncState { /// The authority set for grandpa. #[serde(serialize_with = "serialize_encoded")] pub grandpa_authority_set: - sc_finality_grandpa::AuthoritySet<::Hash, NumberFor>, + sc_consensus_grandpa::AuthoritySet<::Hash, NumberFor>, } /// An api for sync state RPC calls. diff --git a/docs/CODEOWNERS b/docs/CODEOWNERS index 133ba7b09..4377e7ff0 100644 --- a/docs/CODEOWNERS +++ b/docs/CODEOWNERS @@ -41,7 +41,7 @@ # GRANDPA, BABE, consensus stuff /frame/babe/ @andresilva /frame/grandpa/ @andresilva -/client/finality-grandpa/ @andresilva +/client/consensus/grandpa/ @andresilva /client/consensus/babe/ @andresilva /client/consensus/slots/ @andresilva /client/consensus/pow/ @sorpaas diff --git a/frame/grandpa/Cargo.toml b/frame/grandpa/Cargo.toml index fa63b8427..3ffb51642 100644 --- a/frame/grandpa/Cargo.toml +++ b/frame/grandpa/Cargo.toml @@ -23,7 +23,7 @@ pallet-authorship = { version = "4.0.0-dev", default-features = false, path = ". pallet-session = { version = "4.0.0-dev", default-features = false, path = "../session" } sp-application-crypto = { version = "7.0.0", default-features = false, path = "../../primitives/application-crypto" } sp-core = { version = "7.0.0", default-features = false, path = "../../primitives/core" } -sp-finality-grandpa = { version = "4.0.0-dev", default-features = false, path = "../../primitives/finality-grandpa" } +sp-consensus-grandpa = { version = "4.0.0-dev", default-features = false, path = "../../primitives/consensus/grandpa" } sp-io = { version = "7.0.0", default-features = false, path = "../../primitives/io" } sp-runtime = { version = "7.0.0", default-features = false, path = "../../primitives/runtime" } sp-session = { version = "4.0.0-dev", default-features = false, path = "../../primitives/session" } @@ -54,7 +54,7 @@ std = [ "scale-info/std", "sp-application-crypto/std", "sp-core/std", - "sp-finality-grandpa/std", + "sp-consensus-grandpa/std", "sp-io/std", "sp-runtime/std", "sp-session/std", diff --git a/frame/grandpa/src/benchmarking.rs b/frame/grandpa/src/benchmarking.rs index 7a3849766..7a87f0c4b 100644 --- a/frame/grandpa/src/benchmarking.rs +++ b/frame/grandpa/src/benchmarking.rs @@ -50,14 +50,14 @@ benchmarks! { 251, 129, 29, 45, 32, 29, 6 ]; - let equivocation_proof1: sp_finality_grandpa::EquivocationProof = + let equivocation_proof1: sp_consensus_grandpa::EquivocationProof = Decode::decode(&mut &EQUIVOCATION_PROOF_BLOB[..]).unwrap(); let equivocation_proof2 = equivocation_proof1.clone(); }: { - sp_finality_grandpa::check_equivocation_proof(equivocation_proof1); + sp_consensus_grandpa::check_equivocation_proof(equivocation_proof1); } verify { - assert!(sp_finality_grandpa::check_equivocation_proof(equivocation_proof2)); + assert!(sp_consensus_grandpa::check_equivocation_proof(equivocation_proof2)); } note_stalled { diff --git a/frame/grandpa/src/equivocation.rs b/frame/grandpa/src/equivocation.rs index 8de8d7ae6..7d8eb6774 100644 --- a/frame/grandpa/src/equivocation.rs +++ b/frame/grandpa/src/equivocation.rs @@ -39,7 +39,7 @@ use sp_std::prelude::*; use codec::{self as codec, Decode, Encode}; use frame_support::traits::{Get, KeyOwnerProofSystem}; -use sp_finality_grandpa::{EquivocationProof, RoundNumber, SetId}; +use sp_consensus_grandpa::{EquivocationProof, RoundNumber, SetId}; use sp_runtime::{ transaction_validity::{ InvalidTransaction, TransactionPriority, TransactionSource, TransactionValidity, @@ -251,7 +251,7 @@ fn is_known_offence( key_owner_proof: &T::KeyOwnerProof, ) -> Result<(), TransactionValidityError> { // check the membership proof to extract the offender's id - let key = (sp_finality_grandpa::KEY_TYPE, equivocation_proof.offender().clone()); + let key = (sp_consensus_grandpa::KEY_TYPE, equivocation_proof.offender().clone()); let offender = T::KeyOwnerProofSystem::check_proof(key, key_owner_proof.clone()) .ok_or(InvalidTransaction::BadProof)?; diff --git a/frame/grandpa/src/lib.rs b/frame/grandpa/src/lib.rs index 86751e23a..85d116ee7 100644 --- a/frame/grandpa/src/lib.rs +++ b/frame/grandpa/src/lib.rs @@ -29,7 +29,7 @@ #![cfg_attr(not(feature = "std"), no_std)] // re-export since this is necessary for `impl_apis` in runtime. -pub use sp_finality_grandpa as fg_primitives; +pub use sp_consensus_grandpa as fg_primitives; use sp_std::prelude::*; @@ -555,7 +555,7 @@ impl Pallet { // validate equivocation proof (check votes are different and // signatures are valid). - if !sp_finality_grandpa::check_equivocation_proof(equivocation_proof) { + if !sp_consensus_grandpa::check_equivocation_proof(equivocation_proof) { return Err(Error::::InvalidEquivocationProof.into()) } diff --git a/frame/grandpa/src/mock.rs b/frame/grandpa/src/mock.rs index 0948be008..a33fbb22d 100644 --- a/frame/grandpa/src/mock.rs +++ b/frame/grandpa/src/mock.rs @@ -30,8 +30,8 @@ use frame_support::{ }, }; use pallet_session::historical as pallet_session_historical; +use sp_consensus_grandpa::{RoundNumber, SetId, GRANDPA_ENGINE_ID}; use sp_core::{crypto::KeyTypeId, H256}; -use sp_finality_grandpa::{RoundNumber, SetId, GRANDPA_ENGINE_ID}; use sp_keyring::Ed25519Keyring; use sp_runtime::{ curve::PiecewiseLinear, @@ -354,12 +354,12 @@ pub fn generate_equivocation_proof( set_id: SetId, vote1: (RoundNumber, H256, u64, &Ed25519Keyring), vote2: (RoundNumber, H256, u64, &Ed25519Keyring), -) -> sp_finality_grandpa::EquivocationProof { +) -> sp_consensus_grandpa::EquivocationProof { let signed_prevote = |round, hash, number, keyring: &Ed25519Keyring| { let prevote = finality_grandpa::Prevote { target_hash: hash, target_number: number }; let prevote_msg = finality_grandpa::Message::Prevote(prevote.clone()); - let payload = sp_finality_grandpa::localized_payload(round, set_id, &prevote_msg); + let payload = sp_consensus_grandpa::localized_payload(round, set_id, &prevote_msg); let signed = keyring.sign(&payload).into(); (prevote, signed) }; @@ -367,9 +367,9 @@ pub fn generate_equivocation_proof( let (prevote1, signed1) = signed_prevote(vote1.0, vote1.1, vote1.2, vote1.3); let (prevote2, signed2) = signed_prevote(vote2.0, vote2.1, vote2.2, vote2.3); - sp_finality_grandpa::EquivocationProof::new( + sp_consensus_grandpa::EquivocationProof::new( set_id, - sp_finality_grandpa::Equivocation::Prevote(finality_grandpa::Equivocation { + sp_consensus_grandpa::Equivocation::Prevote(finality_grandpa::Equivocation { round_number: vote1.0, identity: vote1.3.public().into(), first: (prevote1, signed1), diff --git a/frame/grandpa/src/tests.rs b/frame/grandpa/src/tests.rs index 4fd5727d5..0bf3c8ddf 100644 --- a/frame/grandpa/src/tests.rs +++ b/frame/grandpa/src/tests.rs @@ -355,7 +355,7 @@ fn report_equivocation_current_set_works() { // create the key ownership proof let key_owner_proof = - Historical::prove((sp_finality_grandpa::KEY_TYPE, &equivocation_key)).unwrap(); + Historical::prove((sp_consensus_grandpa::KEY_TYPE, &equivocation_key)).unwrap(); // report the equivocation and the tx should be dispatched successfully assert_ok!(Grandpa::report_equivocation_unsigned( @@ -408,7 +408,7 @@ fn report_equivocation_old_set_works() { // create the key ownership proof in the "old" set let key_owner_proof = - Historical::prove((sp_finality_grandpa::KEY_TYPE, &equivocation_key)).unwrap(); + Historical::prove((sp_consensus_grandpa::KEY_TYPE, &equivocation_key)).unwrap(); start_era(2); @@ -486,7 +486,7 @@ fn report_equivocation_invalid_set_id() { let equivocation_keyring = extract_keyring(equivocation_key); let key_owner_proof = - Historical::prove((sp_finality_grandpa::KEY_TYPE, &equivocation_key)).unwrap(); + Historical::prove((sp_consensus_grandpa::KEY_TYPE, &equivocation_key)).unwrap(); let set_id = Grandpa::current_set_id(); @@ -524,7 +524,7 @@ fn report_equivocation_invalid_session() { // generate a key ownership proof at set id = 1 let key_owner_proof = - Historical::prove((sp_finality_grandpa::KEY_TYPE, &equivocation_key)).unwrap(); + Historical::prove((sp_consensus_grandpa::KEY_TYPE, &equivocation_key)).unwrap(); start_era(2); @@ -563,7 +563,7 @@ fn report_equivocation_invalid_key_owner_proof() { // generate a key ownership proof for the authority at index 1 let invalid_key_owner_proof = - Historical::prove((sp_finality_grandpa::KEY_TYPE, &invalid_owner_key)).unwrap(); + Historical::prove((sp_consensus_grandpa::KEY_TYPE, &invalid_owner_key)).unwrap(); let equivocation_authority_index = 0; let equivocation_key = &authorities[equivocation_authority_index].0; @@ -610,7 +610,7 @@ fn report_equivocation_invalid_equivocation_proof() { // generate a key ownership proof at set id = 1 let key_owner_proof = - Historical::prove((sp_finality_grandpa::KEY_TYPE, &equivocation_key)).unwrap(); + Historical::prove((sp_consensus_grandpa::KEY_TYPE, &equivocation_key)).unwrap(); let set_id = Grandpa::current_set_id(); @@ -685,7 +685,7 @@ fn report_equivocation_validate_unsigned_prevents_duplicates() { ); let key_owner_proof = - Historical::prove((sp_finality_grandpa::KEY_TYPE, &equivocation_key)).unwrap(); + Historical::prove((sp_consensus_grandpa::KEY_TYPE, &equivocation_key)).unwrap(); let call = Call::report_equivocation_unsigned { equivocation_proof: Box::new(equivocation_proof.clone()), @@ -873,7 +873,7 @@ fn valid_equivocation_reports_dont_pay_fees() { // create the key ownership proof. let key_owner_proof = - Historical::prove((sp_finality_grandpa::KEY_TYPE, &equivocation_key)).unwrap(); + Historical::prove((sp_consensus_grandpa::KEY_TYPE, &equivocation_key)).unwrap(); // check the dispatch info for the call. let info = Call::::report_equivocation_unsigned { diff --git a/primitives/finality-grandpa/Cargo.toml b/primitives/consensus/grandpa/Cargo.toml similarity index 86% rename from primitives/finality-grandpa/Cargo.toml rename to primitives/consensus/grandpa/Cargo.toml index 39bb6713a..7146873e0 100644 --- a/primitives/finality-grandpa/Cargo.toml +++ b/primitives/consensus/grandpa/Cargo.toml @@ -1,5 +1,5 @@ [package] -name = "sp-finality-grandpa" +name = "sp-consensus-grandpa" version = "4.0.0-dev" authors = ["Parity Technologies "] edition = "2021" @@ -7,7 +7,7 @@ license = "Apache-2.0" homepage = "https://substrate.io" repository = "https://github.com/paritytech/substrate/" description = "Primitives for GRANDPA integration, suitable for WASM compilation." -documentation = "https://docs.rs/sp-finality-grandpa" +documentation = "https://docs.rs/sp-consensus-grandpa" readme = "README.md" [package.metadata.docs.rs] @@ -19,12 +19,12 @@ grandpa = { package = "finality-grandpa", version = "0.16.1", default-features = log = { version = "0.4.17", default-features = false } scale-info = { version = "2.1.1", default-features = false, features = ["derive"] } serde = { version = "1.0.136", features = ["derive"], optional = true } -sp-api = { version = "4.0.0-dev", default-features = false, path = "../api" } -sp-application-crypto = { version = "7.0.0", default-features = false, path = "../application-crypto" } -sp-core = { version = "7.0.0", default-features = false, path = "../core" } -sp-keystore = { version = "0.13.0", default-features = false, optional = true, path = "../keystore" } -sp-runtime = { version = "7.0.0", default-features = false, path = "../runtime" } -sp-std = { version = "5.0.0", default-features = false, path = "../std" } +sp-api = { version = "4.0.0-dev", default-features = false, path = "../../api" } +sp-application-crypto = { version = "7.0.0", default-features = false, path = "../../application-crypto" } +sp-core = { version = "7.0.0", default-features = false, path = "../../core" } +sp-keystore = { version = "0.13.0", default-features = false, optional = true, path = "../../keystore" } +sp-runtime = { version = "7.0.0", default-features = false, path = "../../runtime" } +sp-std = { version = "5.0.0", default-features = false, path = "../../std" } [features] default = ["std"] diff --git a/primitives/finality-grandpa/README.md b/primitives/consensus/grandpa/README.md similarity index 100% rename from primitives/finality-grandpa/README.md rename to primitives/consensus/grandpa/README.md diff --git a/primitives/finality-grandpa/src/lib.rs b/primitives/consensus/grandpa/src/lib.rs similarity index 100% rename from primitives/finality-grandpa/src/lib.rs rename to primitives/consensus/grandpa/src/lib.rs diff --git a/test-utils/runtime/Cargo.toml b/test-utils/runtime/Cargo.toml index 33dbfce68..f3222dc15 100644 --- a/test-utils/runtime/Cargo.toml +++ b/test-utils/runtime/Cargo.toml @@ -38,7 +38,7 @@ pallet-babe = { version = "4.0.0-dev", default-features = false, path = "../../f frame-system = { version = "4.0.0-dev", default-features = false, path = "../../frame/system" } frame-system-rpc-runtime-api = { version = "4.0.0-dev", default-features = false, path = "../../frame/system/rpc/runtime-api" } pallet-timestamp = { version = "4.0.0-dev", default-features = false, path = "../../frame/timestamp" } -sp-finality-grandpa = { version = "4.0.0-dev", default-features = false, path = "../../primitives/finality-grandpa" } +sp-consensus-grandpa = { version = "4.0.0-dev", default-features = false, path = "../../primitives/consensus/grandpa" } sp-trie = { version = "7.0.0", default-features = false, path = "../../primitives/trie" } sp-transaction-pool = { version = "4.0.0-dev", default-features = false, path = "../../primitives/transaction-pool" } trie-db = { version = "0.25.1", default-features = false } @@ -97,7 +97,7 @@ std = [ "frame-system/std", "pallet-timestamp/std", "sc-service", - "sp-finality-grandpa/std", + "sp-consensus-grandpa/std", "sp-trie/std", "sp-transaction-pool/std", "trie-db/std", diff --git a/test-utils/runtime/src/lib.rs b/test-utils/runtime/src/lib.rs index 720b0a1e0..9e8a4e119 100644 --- a/test-utils/runtime/src/lib.rs +++ b/test-utils/runtime/src/lib.rs @@ -945,29 +945,29 @@ cfg_if! { } } - impl sp_finality_grandpa::GrandpaApi for Runtime { - fn grandpa_authorities() -> sp_finality_grandpa::AuthorityList { + impl sp_consensus_grandpa::GrandpaApi for Runtime { + fn grandpa_authorities() -> sp_consensus_grandpa::AuthorityList { Vec::new() } - fn current_set_id() -> sp_finality_grandpa::SetId { + fn current_set_id() -> sp_consensus_grandpa::SetId { 0 } fn submit_report_equivocation_unsigned_extrinsic( - _equivocation_proof: sp_finality_grandpa::EquivocationProof< + _equivocation_proof: sp_consensus_grandpa::EquivocationProof< ::Hash, NumberFor, >, - _key_owner_proof: sp_finality_grandpa::OpaqueKeyOwnershipProof, + _key_owner_proof: sp_consensus_grandpa::OpaqueKeyOwnershipProof, ) -> Option<()> { None } fn generate_key_ownership_proof( - _set_id: sp_finality_grandpa::SetId, - _authority_id: sp_finality_grandpa::AuthorityId, - ) -> Option { + _set_id: sp_consensus_grandpa::SetId, + _authority_id: sp_consensus_grandpa::AuthorityId, + ) -> Option { None } } From 1cbb2ef4f2bc7486a60a5ece409525ae46bce3b6 Mon Sep 17 00:00:00 2001 From: Muharem Ismailov Date: Mon, 27 Feb 2023 17:41:23 +0100 Subject: [PATCH 167/558] constructor for MemberRecord (#13473) --- frame/ranked-collective/src/lib.rs | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/frame/ranked-collective/src/lib.rs b/frame/ranked-collective/src/lib.rs index 30bb280a1..66af9bf6e 100644 --- a/frame/ranked-collective/src/lib.rs +++ b/frame/ranked-collective/src/lib.rs @@ -170,6 +170,13 @@ pub struct MemberRecord { rank: Rank, } +impl MemberRecord { + // Constructs a new instance of [`MemberRecord`]. + pub fn new(rank: Rank) -> Self { + Self { rank } + } +} + /// Record needed for every vote. #[derive(PartialEq, Eq, Clone, Copy, Encode, Decode, RuntimeDebug, TypeInfo, MaxEncodedLen)] pub enum VoteRecord { From 295712018a10703362967376bfc7013d63fe37f6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bastian=20K=C3=B6cher?= Date: Mon, 27 Feb 2023 18:49:16 +0100 Subject: [PATCH 168/558] `pallet-treasury`: Ensure we respect `max_amount` for spend across batch calls (#13468) * `pallet-treasury`: Ensure we respect `max_amount` for spend across batch calls When calling `spend` the origin defines the `max_amount` of tokens it is allowed to spend. The problem is that someone can send a `batch(spend, spend)` to circumvent this restriction as we don't check across different calls that the `max_amount` is respected. This pull request fixes this behavior by introducing a so-called dispatch context. This dispatch context is created once per outer most `dispatch` call. For more information see the docs in this pr. The treasury then uses this dispatch context to attach information about already spent funds per `max_amount` (we assume that each origin has a different `max_amount` configured). So, a `batch(spend, spend)` is now checked to stay inside the allowed spending bounds. Fixes: https://github.com/paritytech/substrate/issues/13167 * Import `Box` for wasm * FMT --- Cargo.lock | 2 + frame/support/Cargo.toml | 2 + .../procedural/src/pallet/expand/call.rs | 32 +-- frame/support/src/dispatch_context.rs | 232 ++++++++++++++++++ frame/support/src/lib.rs | 1 + frame/support/test/tests/pallet.rs | 32 ++- frame/treasury/Cargo.toml | 1 + frame/treasury/src/lib.rs | 35 ++- frame/treasury/src/tests.rs | 40 ++- 9 files changed, 353 insertions(+), 24 deletions(-) create mode 100644 frame/support/src/dispatch_context.rs diff --git a/Cargo.lock b/Cargo.lock index 92db97a36..df6843364 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2383,6 +2383,7 @@ version = "4.0.0-dev" dependencies = [ "assert_matches", "bitflags", + "environmental", "frame-metadata", "frame-support-procedural", "frame-system", @@ -6705,6 +6706,7 @@ dependencies = [ "frame-system", "impl-trait-for-tuples", "pallet-balances", + "pallet-utility", "parity-scale-codec", "scale-info", "serde", diff --git a/frame/support/Cargo.toml b/frame/support/Cargo.toml index 4f62ae42e..008d4b3f2 100644 --- a/frame/support/Cargo.toml +++ b/frame/support/Cargo.toml @@ -38,6 +38,7 @@ smallvec = "1.8.0" log = { version = "0.4.17", default-features = false } sp-core-hashing-proc-macro = { version = "5.0.0", path = "../../primitives/core/hashing/proc-macro" } k256 = { version = "0.11.5", default-features = false, features = ["ecdsa"] } +environmental = { version = "1.1.4", default-features = false } [dev-dependencies] serde_json = "1.0.85" @@ -67,6 +68,7 @@ std = [ "sp-weights/std", "frame-support-procedural/std", "log/std", + "environmental/std", ] runtime-benchmarks = [] try-runtime = [] diff --git a/frame/support/procedural/src/pallet/expand/call.rs b/frame/support/procedural/src/pallet/expand/call.rs index 72367dc39..3db454eb6 100644 --- a/frame/support/procedural/src/pallet/expand/call.rs +++ b/frame/support/procedural/src/pallet/expand/call.rs @@ -333,22 +333,24 @@ pub fn expand_call(def: &mut Def) -> proc_macro2::TokenStream { self, origin: Self::RuntimeOrigin ) -> #frame_support::dispatch::DispatchResultWithPostInfo { - match self { - #( - Self::#fn_name { #( #args_name_pattern, )* } => { - #frame_support::sp_tracing::enter_span!( - #frame_support::sp_tracing::trace_span!(stringify!(#fn_name)) - ); - #maybe_allow_attrs - <#pallet_ident<#type_use_gen>>::#fn_name(origin, #( #args_name, )* ) - .map(Into::into).map_err(Into::into) + #frame_support::dispatch_context::run_in_context(|| { + match self { + #( + Self::#fn_name { #( #args_name_pattern, )* } => { + #frame_support::sp_tracing::enter_span!( + #frame_support::sp_tracing::trace_span!(stringify!(#fn_name)) + ); + #maybe_allow_attrs + <#pallet_ident<#type_use_gen>>::#fn_name(origin, #( #args_name, )* ) + .map(Into::into).map_err(Into::into) + }, + )* + Self::__Ignore(_, _) => { + let _ = origin; // Use origin for empty Call enum + unreachable!("__PhantomItem cannot be used."); }, - )* - Self::__Ignore(_, _) => { - let _ = origin; // Use origin for empty Call enum - unreachable!("__PhantomItem cannot be used."); - }, - } + } + }) } } diff --git a/frame/support/src/dispatch_context.rs b/frame/support/src/dispatch_context.rs new file mode 100644 index 000000000..31278ea9f --- /dev/null +++ b/frame/support/src/dispatch_context.rs @@ -0,0 +1,232 @@ +// This file is part of Substrate. + +// Copyright (C) Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +//! Provides functions to interact with the dispatch context. +//! +//! A Dispatch context is created by calling [`run_in_context`] and then the given closure will be +//! executed in this dispatch context. Everyting run in this `closure` will have access to the same +//! dispatch context. This also applies to nested calls of [`run_in_context`]. The dispatch context +//! can be used to store and retrieve information locally in this context. The dispatch context can +//! be accessed by using [`with_context`]. This function will execute the given closure and give it +//! access to the value stored in the dispatch context. +//! +//! # FRAME integration +//! +//! The FRAME macros implement [`UnfilteredDispatchable`](crate::traits::UnfilteredDispatchable) for +//! each pallet `Call` enum. Part of this implementation is the call to [`run_in_context`], so that +//! each call to +//! [`UnfilteredDispatchable::dispatch_bypass_filter`](crate::traits::UnfilteredDispatchable::dispatch_bypass_filter) +//! or [`Dispatchable::dispatch`](crate::dispatch::Dispatchable::dispatch) will run in a dispatch +//! context. +//! +//! # Example +//! +//! ``` +//! use frame_support::dispatch_context::{with_context, run_in_context}; +//! +//! // Not executed in a dispatch context, so it should return `None`. +//! assert!(with_context::<(), _>(|_| println!("Hello")).is_none()); +//! +//! // Run it in a dispatch context and `with_context` returns `Some(_)`. +//! run_in_context(|| { +//! assert!(with_context::<(), _>(|_| println!("Hello")).is_some()); +//! }); +//! +//! #[derive(Default)] +//! struct CustomContext(i32); +//! +//! run_in_context(|| { +//! with_context::(|v| { +//! // Intitialize the value to the default value. +//! assert_eq!(0, v.or_default().0); +//! v.or_default().0 = 10; +//! }); +//! +//! with_context::(|v| { +//! // We are still in the same context and can still access the set value. +//! assert_eq!(10, v.or_default().0); +//! }); +//! +//! run_in_context(|| { +//! with_context::(|v| { +//! // A nested call of `run_in_context` stays in the same dispatch context +//! assert_eq!(10, v.or_default().0); +//! }) +//! }) +//! }); +//! +//! run_in_context(|| { +//! with_context::(|v| { +//! // We left the other context and created a new one, so we should be back +//! // to our default value. +//! assert_eq!(0, v.or_default().0); +//! }); +//! }); +//! ``` +//! +//! In your pallet you will only have to use [`with_context`], because as described above +//! [`run_in_context`] will be handled by FRAME for you. + +use sp_std::{ + any::{Any, TypeId}, + boxed::Box, + collections::btree_map::{BTreeMap, Entry}, +}; + +environmental::environmental!(DISPATCH_CONTEXT: BTreeMap>); + +/// Abstraction over some optional value `T` that is stored in the dispatch context. +pub struct Value<'a, T> { + value: Option<&'a mut T>, + new_value: Option, +} + +impl Value<'_, T> { + /// Get the value as reference. + pub fn get(&self) -> Option<&T> { + self.new_value.as_ref().or_else(|| self.value.as_ref().map(|v| *v as &T)) + } + + /// Get the value as mutable reference. + pub fn get_mut(&mut self) -> Option<&mut T> { + self.new_value.as_mut().or_else(|| self.value.as_mut().map(|v| *v as &mut T)) + } + + /// Set to the given value. + /// + /// [`Self::get`] and [`Self::get_mut`] will return `new_value` afterwards. + pub fn set(&mut self, new_value: T) { + self.value = None; + self.new_value = Some(new_value); + } + + /// Returns a mutable reference to the value. + /// + /// If the internal value isn't initialized, this will set it to [`Default::default()`] before + /// returning the mutable reference. + pub fn or_default(&mut self) -> &mut T + where + T: Default, + { + if let Some(v) = &mut self.value { + return v + } + + self.new_value.get_or_insert_with(|| Default::default()) + } + + /// Clear the internal value. + /// + /// [`Self::get`] and [`Self::get_mut`] will return `None` afterwards. + pub fn clear(&mut self) { + self.new_value = None; + self.value = None; + } +} + +/// Runs the given `callback` in the dispatch context and gives access to some user defined value. +/// +/// Passes the a mutable reference of [`Value`] to the callback. The value will be of type `T` and +/// is identified using the [`TypeId`] of `T`. This means that `T` should be some unique type to +/// make the value unique. If no value is set yet [`Value::get()`] and [`Value::get_mut()`] will +/// return `None`. It is totally valid to have some `T` that is shared between different callers to +/// have access to the same value. +/// +/// Returns `None` if the current context is not a dispatch context. To create a context it is +/// required to call [`run_in_context`] with the closure to execute in this context. So, for example +/// in tests it could be that there isn't any dispatch context or when calling a dispatchable like a +/// normal Rust function from some FRAME hook. +pub fn with_context(callback: impl FnOnce(&mut Value) -> R) -> Option { + DISPATCH_CONTEXT::with(|c| match c.entry(TypeId::of::()) { + Entry::Occupied(mut o) => { + let value = o.get_mut().downcast_mut::(); + + if value.is_none() { + log::error!( + "Failed to downcast value for type {} in dispatch context!", + sp_std::any::type_name::(), + ); + } + + let mut value = Value { value, new_value: None }; + let res = callback(&mut value); + + if value.value.is_none() && value.new_value.is_none() { + o.remove(); + } else if let Some(new_value) = value.new_value { + o.insert(Box::new(new_value) as Box<_>); + } + + res + }, + Entry::Vacant(v) => { + let mut value = Value { value: None, new_value: None }; + + let res = callback(&mut value); + + if let Some(new_value) = value.new_value { + v.insert(Box::new(new_value) as Box<_>); + } + + res + }, + }) +} + +/// Run the given closure `run` in a dispatch context. +/// +/// Nested calls to this function will execute `run` in the same dispatch context as the initial +/// call to this function. In other words, all nested calls of this function will be done in the +/// same dispatch context. +pub fn run_in_context(run: impl FnOnce() -> R) -> R { + DISPATCH_CONTEXT::using_once(&mut Default::default(), run) +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn dispatch_context_works() { + // No context, so we don't execute + assert!(with_context::<(), _>(|_| ()).is_none()); + + let ret = run_in_context(|| with_context::<(), _>(|_| 1).unwrap()); + assert_eq!(1, ret); + + #[derive(Default)] + struct Context(i32); + + let res = run_in_context(|| { + with_context::(|v| { + assert_eq!(0, v.or_default().0); + + v.or_default().0 = 100; + }); + + run_in_context(|| { + run_in_context(|| { + run_in_context(|| with_context::(|v| v.or_default().0).unwrap()) + }) + }) + }); + + // Ensure that the initial value set in the context is also accessible after nesting the + // `run_in_context` calls. + assert_eq!(100, res); + } +} diff --git a/frame/support/src/lib.rs b/frame/support/src/lib.rs index c11c902d6..4e9bc32b0 100644 --- a/frame/support/src/lib.rs +++ b/frame/support/src/lib.rs @@ -78,6 +78,7 @@ pub mod inherent; #[macro_use] pub mod error; pub mod crypto; +pub mod dispatch_context; pub mod instances; pub mod migrations; pub mod traits; diff --git a/frame/support/test/tests/pallet.rs b/frame/support/test/tests/pallet.rs index 7dd9aa0dd..8ce85fa50 100644 --- a/frame/support/test/tests/pallet.rs +++ b/frame/support/test/tests/pallet.rs @@ -16,9 +16,12 @@ // limitations under the License. use frame_support::{ + assert_ok, dispatch::{ - DispatchClass, DispatchInfo, GetDispatchInfo, Parameter, Pays, UnfilteredDispatchable, + DispatchClass, DispatchInfo, Dispatchable, GetDispatchInfo, Parameter, Pays, + UnfilteredDispatchable, }, + dispatch_context::with_context, pallet_prelude::{StorageInfoTrait, ValueQuery}, storage::unhashed, traits::{ @@ -102,6 +105,7 @@ pub mod pallet { use super::*; use frame_support::pallet_prelude::*; use frame_system::pallet_prelude::*; + use sp_runtime::DispatchResult; type BalanceOf = ::Balance; @@ -227,6 +231,12 @@ pub mod pallet { pub fn foo_no_post_info(_origin: OriginFor) -> DispatchResult { Ok(()) } + + #[pallet::call_index(3)] + #[pallet::weight(1)] + pub fn check_for_dispatch_context(_origin: OriginFor) -> DispatchResult { + with_context::<(), _>(|_| ()).ok_or_else(|| DispatchError::Unavailable) + } } #[pallet::error] @@ -713,7 +723,7 @@ fn call_expand() { assert_eq!(call_foo.get_call_name(), "foo"); assert_eq!( pallet::Call::::get_call_names(), - &["foo", "foo_storage_layer", "foo_no_post_info"], + &["foo", "foo_storage_layer", "foo_no_post_info", "check_for_dispatch_context"], ); } @@ -1933,3 +1943,21 @@ fn test_storage_alias() { ); }) } + +#[test] +fn test_dispatch_context() { + TestExternalities::default().execute_with(|| { + // By default there is no context + assert!(with_context::<(), _>(|_| ()).is_none()); + + // When not using `dispatch`, there should be no dispatch context + assert_eq!( + DispatchError::Unavailable, + Example::check_for_dispatch_context(RuntimeOrigin::root()).unwrap_err(), + ); + + // When using `dispatch`, there should be a dispatch context + assert_ok!(RuntimeCall::from(pallet::Call::::check_for_dispatch_context {}) + .dispatch(RuntimeOrigin::root())); + }); +} diff --git a/frame/treasury/Cargo.toml b/frame/treasury/Cargo.toml index 1fd1d7b3d..23fcb7944 100644 --- a/frame/treasury/Cargo.toml +++ b/frame/treasury/Cargo.toml @@ -30,6 +30,7 @@ sp-std = { version = "5.0.0", default-features = false, path = "../../primitives [dev-dependencies] sp-core = { version = "7.0.0", path = "../../primitives/core" } sp-io = { version = "7.0.0", path = "../../primitives/io" } +pallet-utility = { version = "4.0.0-dev", path = "../utility" } [features] default = ["std"] diff --git a/frame/treasury/src/lib.rs b/frame/treasury/src/lib.rs index 2e22063eb..04e989373 100644 --- a/frame/treasury/src/lib.rs +++ b/frame/treasury/src/lib.rs @@ -67,10 +67,10 @@ use codec::{Decode, Encode, MaxEncodedLen}; use scale_info::TypeInfo; use sp_runtime::{ - traits::{AccountIdConversion, Saturating, StaticLookup, Zero}, + traits::{AccountIdConversion, CheckedAdd, Saturating, StaticLookup, Zero}, Permill, RuntimeDebug, }; -use sp_std::prelude::*; +use sp_std::{collections::btree_map::BTreeMap, prelude::*}; use frame_support::{ print, @@ -136,7 +136,7 @@ pub struct Proposal { #[frame_support::pallet] pub mod pallet { use super::*; - use frame_support::pallet_prelude::*; + use frame_support::{dispatch_context::with_context, pallet_prelude::*}; use frame_system::pallet_prelude::*; #[pallet::pallet] @@ -339,6 +339,11 @@ pub mod pallet { } } + #[derive(Default)] + struct SpendContext { + spend_in_context: BTreeMap, + } + #[pallet::call] impl, I: 'static> Pallet { /// Put forward a suggestion for spending. A deposit proportional to the value @@ -433,9 +438,29 @@ pub mod pallet { beneficiary: AccountIdLookupOf, ) -> DispatchResult { let max_amount = T::SpendOrigin::ensure_origin(origin)?; - let beneficiary = T::Lookup::lookup(beneficiary)?; - ensure!(amount <= max_amount, Error::::InsufficientPermission); + + with_context::>, _>(|v| { + let context = v.or_default(); + + // We group based on `max_amount`, to dinstinguish between different kind of + // origins. (assumes that all origins have different `max_amount`) + // + // Worst case is that we reject some "valid" request. + let spend = context.spend_in_context.entry(max_amount).or_default(); + + // Ensure that we don't overflow nor use more than `max_amount` + if spend.checked_add(&amount).map(|s| s > max_amount).unwrap_or(true) { + Err(Error::::InsufficientPermission) + } else { + *spend = spend.saturating_add(amount); + + Ok(()) + } + }) + .unwrap_or(Ok(()))?; + + let beneficiary = T::Lookup::lookup(beneficiary)?; let proposal_index = Self::proposal_count(); Approvals::::try_append(proposal_index) .map_err(|_| Error::::TooManyApprovals)?; diff --git a/frame/treasury/src/tests.rs b/frame/treasury/src/tests.rs index c9305dfd1..24d2d01f9 100644 --- a/frame/treasury/src/tests.rs +++ b/frame/treasury/src/tests.rs @@ -22,11 +22,11 @@ use sp_core::H256; use sp_runtime::{ testing::Header, - traits::{BadOrigin, BlakeTwo256, IdentityLookup}, + traits::{BadOrigin, BlakeTwo256, Dispatchable, IdentityLookup}, }; use frame_support::{ - assert_noop, assert_ok, + assert_err_ignore_postinfo, assert_noop, assert_ok, pallet_prelude::GenesisBuild, parameter_types, traits::{ConstU32, ConstU64, OnInitialize}, @@ -38,6 +38,8 @@ use crate as treasury; type UncheckedExtrinsic = frame_system::mocking::MockUncheckedExtrinsic; type Block = frame_system::mocking::MockBlock; +type UtilityCall = pallet_utility::Call; +type TreasuryCall = crate::Call; frame_support::construct_runtime!( pub enum Test where @@ -48,6 +50,7 @@ frame_support::construct_runtime!( System: frame_system::{Pallet, Call, Config, Storage, Event}, Balances: pallet_balances::{Pallet, Call, Storage, Config, Event}, Treasury: treasury::{Pallet, Call, Storage, Config, Event}, + Utility: pallet_utility, } ); @@ -88,6 +91,14 @@ impl pallet_balances::Config for Test { type AccountStore = System; type WeightInfo = (); } + +impl pallet_utility::Config for Test { + type RuntimeEvent = RuntimeEvent; + type RuntimeCall = RuntimeCall; + type PalletsOrigin = OriginCaller; + type WeightInfo = (); +} + parameter_types! { pub const ProposalBond: Permill = Permill::from_percent(5); pub const Burn: Permill = Permill::from_percent(50); @@ -470,3 +481,28 @@ fn remove_already_removed_approval_fails() { ); }); } + +#[test] +fn spending_in_batch_respects_max_total() { + new_test_ext().execute_with(|| { + // Respect the `max_total` for the given origin. + assert_ok!(RuntimeCall::from(UtilityCall::batch_all { + calls: vec![ + RuntimeCall::from(TreasuryCall::spend { amount: 2, beneficiary: 100 }), + RuntimeCall::from(TreasuryCall::spend { amount: 2, beneficiary: 101 }) + ] + }) + .dispatch(RuntimeOrigin::signed(10))); + + assert_err_ignore_postinfo!( + RuntimeCall::from(UtilityCall::batch_all { + calls: vec![ + RuntimeCall::from(TreasuryCall::spend { amount: 2, beneficiary: 100 }), + RuntimeCall::from(TreasuryCall::spend { amount: 4, beneficiary: 101 }) + ] + }) + .dispatch(RuntimeOrigin::signed(10)), + Error::::InsufficientPermission + ); + }) +} From 3d40b175fc7c93dbd7fec7871ec34b98d15e05a9 Mon Sep 17 00:00:00 2001 From: Arsenii Lyashenko Date: Mon, 27 Feb 2023 23:26:38 +0300 Subject: [PATCH 169/558] Add Windows support for storage monitor (#13466) * Add Windows support for storage monitor * Apply suggested changes --- Cargo.lock | 57 +++++++++++++++++++++++++------ client/storage-monitor/Cargo.toml | 2 +- client/storage-monitor/src/lib.rs | 11 ++---- 3 files changed, 51 insertions(+), 19 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index df6843364..91ae9ab42 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2557,6 +2557,17 @@ dependencies = [ "winapi", ] +[[package]] +name = "fs4" +version = "0.6.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ea55201cc351fdb478217c0fb641b59813da9b4efe4c414a9d8f989a657d149" +dependencies = [ + "libc", + "rustix 0.35.13", + "winapi", +] + [[package]] name = "fs_extra" version = "1.3.0" @@ -3282,6 +3293,12 @@ dependencies = [ "webrtc-util", ] +[[package]] +name = "io-lifetimes" +version = "0.7.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "59ce5ef949d49ee85593fc4d3f3f95ad61657076395cbbce23e2121fc5542074" + [[package]] name = "io-lifetimes" version = "1.0.5" @@ -3323,8 +3340,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "22e18b0a45d56fe973d6db23972bf5bc46f988a4a2385deac9cc29572f09daef" dependencies = [ "hermit-abi 0.3.0", - "io-lifetimes", - "rustix", + "io-lifetimes 1.0.5", + "rustix 0.36.8", "windows-sys 0.45.0", ] @@ -4222,6 +4239,12 @@ dependencies = [ "nalgebra", ] +[[package]] +name = "linux-raw-sys" +version = "0.0.46" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d4d2456c373231a208ad294c33dc5bff30051eafd954cd4caae83a712b12854d" + [[package]] name = "linux-raw-sys" version = "0.1.4" @@ -4372,7 +4395,7 @@ version = "0.6.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b20a59d985586e4a5aef64564ac77299f8586d8be6cf9106a5a40207e8908efb" dependencies = [ - "rustix", + "rustix 0.36.8", ] [[package]] @@ -7830,6 +7853,20 @@ dependencies = [ "nom", ] +[[package]] +name = "rustix" +version = "0.35.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "727a1a6d65f786ec22df8a81ca3121107f235970dc1705ed681d3e6e8b9cd5f9" +dependencies = [ + "bitflags", + "errno", + "io-lifetimes 0.7.5", + "libc", + "linux-raw-sys 0.0.46", + "windows-sys 0.42.0", +] + [[package]] name = "rustix" version = "0.36.8" @@ -7838,9 +7875,9 @@ checksum = "f43abb88211988493c1abb44a70efa56ff0ce98f233b7b276146f1f3f7ba9644" dependencies = [ "bitflags", "errno", - "io-lifetimes", + "io-lifetimes 1.0.5", "libc", - "linux-raw-sys", + "linux-raw-sys 0.1.4", "windows-sys 0.45.0", ] @@ -8538,7 +8575,7 @@ dependencies = [ "once_cell", "parity-scale-codec", "paste", - "rustix", + "rustix 0.36.8", "sc-allocator", "sc-executor-common", "sc-runtime-test", @@ -9098,9 +9135,9 @@ name = "sc-storage-monitor" version = "0.1.0" dependencies = [ "clap 4.1.4", + "fs4", "futures", "log", - "nix 0.26.2", "sc-client-db", "sc-utils", "sp-core", @@ -12055,7 +12092,7 @@ dependencies = [ "directories-next", "file-per-thread-logger", "log", - "rustix", + "rustix 0.36.8", "serde", "sha2 0.10.6", "toml", @@ -12135,7 +12172,7 @@ checksum = "f76ef2e410329aaf8555ac6571d6fe07711be0646dcdf7ff3ab750a42ed2e583" dependencies = [ "object 0.29.0", "once_cell", - "rustix", + "rustix 0.36.8", ] [[package]] @@ -12166,7 +12203,7 @@ dependencies = [ "memoffset 0.6.5", "paste", "rand 0.8.5", - "rustix", + "rustix 0.36.8", "wasmtime-asm-macros", "wasmtime-environ", "wasmtime-jit-debug", diff --git a/client/storage-monitor/Cargo.toml b/client/storage-monitor/Cargo.toml index 2ba24f9e2..52f34a296 100644 --- a/client/storage-monitor/Cargo.toml +++ b/client/storage-monitor/Cargo.toml @@ -12,7 +12,7 @@ homepage = "https://substrate.io" clap = { version = "4.0.9", features = ["derive", "string"] } futures = "0.3.21" log = "0.4.17" -nix = { version = "0.26.1", features = ["fs"] } +fs4 = "0.6.3" sc-client-db = { version = "0.10.0-dev", default-features = false, path = "../db" } sc-utils = { version = "4.0.0-dev", path = "../utils" } sp-core = { version = "7.0.0", path = "../../primitives/core" } diff --git a/client/storage-monitor/src/lib.rs b/client/storage-monitor/src/lib.rs index e65b13ff6..277a74de0 100644 --- a/client/storage-monitor/src/lib.rs +++ b/client/storage-monitor/src/lib.rs @@ -17,10 +17,10 @@ // along with this program. If not, see . use clap::Args; -use nix::{errno::Errno, sys::statvfs::statvfs}; use sc_client_db::DatabaseSource; use sp_core::traits::SpawnEssentialNamed; use std::{ + io, path::{Path, PathBuf}, time::Duration, }; @@ -31,7 +31,7 @@ const LOG_TARGET: &str = "storage-monitor"; #[derive(Debug, thiserror::Error)] pub enum Error { #[error("IO Error")] - IOError(#[from] Errno), + IOError(#[from] io::Error), #[error("Out of storage space: available {0}MB, required {1}MB")] StorageOutOfSpace(u64, u64), } @@ -117,12 +117,7 @@ impl StorageMonitorService { /// Returns free space in MB, or error if statvfs failed. fn free_space(path: &Path) -> Result { - statvfs(path) - .map(|stats| { - u64::from(stats.blocks_available()).saturating_mul(u64::from(stats.block_size())) / - 1_000_000 - }) - .map_err(Error::from) + Ok(fs4::available_space(path).map(|s| s / 1_000_000)?) } /// Checks if the amount of free space for given `path` is above given `threshold`. From 0d36e75bde372b297d9a4c3deb4dd9a42b756bc2 Mon Sep 17 00:00:00 2001 From: Michal Kucharczyk <1728078+michalkucharczyk@users.noreply.github.com> Date: Tue, 28 Feb 2023 00:10:34 +0100 Subject: [PATCH 170/558] Service::Error: Storage variant removed (#13481) --- bin/node/cli/src/service.rs | 3 ++- client/service/src/error.rs | 3 --- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/bin/node/cli/src/service.rs b/bin/node/cli/src/service.rs index 7b1ab2c1e..268a170a8 100644 --- a/bin/node/cli/src/service.rs +++ b/bin/node/cli/src/service.rs @@ -560,7 +560,8 @@ pub fn new_full(config: Configuration, cli: Cli) -> Result Date: Tue, 28 Feb 2023 09:52:11 +0200 Subject: [PATCH 171/558] Adjust the ratio of the `in-peers`/`out-peers` (#13477) Establish fewer outbound connections in an attempt to allow publicly available nodes to accept more full nodes. Maintain the overall number of connections node should establish. --- client/cli/src/params/network_params.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/client/cli/src/params/network_params.rs b/client/cli/src/params/network_params.rs index 2402b5d68..3aebee778 100644 --- a/client/cli/src/params/network_params.rs +++ b/client/cli/src/params/network_params.rs @@ -82,11 +82,11 @@ pub struct NetworkParams { pub allow_private_ip: bool, /// Specify the number of outgoing connections we're trying to maintain. - #[arg(long, value_name = "COUNT", default_value_t = 15)] + #[arg(long, value_name = "COUNT", default_value_t = 8)] pub out_peers: u32, /// Maximum number of inbound full nodes peers. - #[arg(long, value_name = "COUNT", default_value_t = 25)] + #[arg(long, value_name = "COUNT", default_value_t = 32)] pub in_peers: u32, /// Maximum number of inbound light nodes peers. From 2a7887e37569872919144c60afbd92cd36763f39 Mon Sep 17 00:00:00 2001 From: Oliver Tale-Yazdi Date: Tue, 28 Feb 2023 14:47:36 +0100 Subject: [PATCH 172/558] Fix V2 PoV benchmarking (#13485) * Bump default 'additional_trie_layers' to two The default here only works for extremely small runtimes, which have no more than 16 storage prefices. This is changed to a "sane" default of 2, which is save for runtimes with up to 4096 storage prefices (eg StorageValue). Signed-off-by: Oliver Tale-Yazdi * Update tests and test weights Signed-off-by: Oliver Tale-Yazdi * Fix PoV weights Signed-off-by: Oliver Tale-Yazdi * ".git/.scripts/commands/bench/bench.sh" pallet dev pallet_balances * ".git/.scripts/commands/bench/bench.sh" pallet dev pallet_message_queue * ".git/.scripts/commands/bench/bench.sh" pallet dev pallet_glutton * ".git/.scripts/commands/bench/bench.sh" pallet dev pallet_glutton * Fix sanity check >0 would also do as a check, but let's try this. Signed-off-by: Oliver Tale-Yazdi --------- Signed-off-by: Oliver Tale-Yazdi Co-authored-by: command-bot <> --- frame/balances/src/weights.rs | 139 ++--- frame/benchmarking/pov/src/benchmarking.rs | 11 + frame/benchmarking/pov/src/weights.rs | 508 ++++++++++-------- frame/glutton/src/tests.rs | 2 +- frame/glutton/src/weights.rs | 184 +++---- frame/message-queue/src/weights.rs | 194 +++---- frame/support/procedural/src/benchmark.rs | 13 +- .../frame/benchmarking-cli/src/pallet/mod.rs | 2 +- 8 files changed, 576 insertions(+), 477 deletions(-) diff --git a/frame/balances/src/weights.rs b/frame/balances/src/weights.rs index 6bd7d7548..8c307ff90 100644 --- a/frame/balances/src/weights.rs +++ b/frame/balances/src/weights.rs @@ -18,25 +18,26 @@ //! Autogenerated weights for pallet_balances //! //! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev -//! DATE: 2023-01-24, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! DATE: 2023-02-27, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` //! WORST CASE MAP SIZE: `1000000` -//! HOSTNAME: `bm2`, CPU: `Intel(R) Core(TM) i7-7700K CPU @ 4.20GHz` +//! HOSTNAME: `bm3`, CPU: `Intel(R) Core(TM) i7-7700K CPU @ 4.20GHz` //! EXECUTION: Some(Wasm), WASM-EXECUTION: Compiled, CHAIN: Some("dev"), DB CACHE: 1024 // Executed Command: -// ./target/production/substrate +// target/production/substrate // benchmark // pallet -// --chain=dev // --steps=50 // --repeat=20 -// --pallet=pallet_balances // --extrinsic=* // --execution=wasm // --wasm-execution=compiled // --heap-pages=4096 -// --output=./frame/balances/src/weights.rs +// --json-file=/var/lib/gitlab-runner/builds/zyw4fam_/0/parity/mirrors/substrate/.git/.artifacts/bench.json +// --pallet=pallet_balances +// --chain=dev // --header=./HEADER-APACHE2 +// --output=./frame/balances/src/weights.rs // --template=./.maintain/frame-weight-template.hbs #![cfg_attr(rustfmt, rustfmt_skip)] @@ -64,10 +65,11 @@ impl WeightInfo for SubstrateWeight { /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) fn transfer() -> Weight { // Proof Size summary in bytes: - // Measured: `1723` - // Estimated: `2603` - // Minimum execution time: 47_557 nanoseconds. - Weight::from_parts(48_314_000, 2603) + // Measured: `0` + // Estimated: `3593` + // Minimum execution time: 37_815 nanoseconds. + Weight::from_ref_time(38_109_000) + .saturating_add(Weight::from_proof_size(3593)) .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -75,10 +77,11 @@ impl WeightInfo for SubstrateWeight { /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) fn transfer_keep_alive() -> Weight { // Proof Size summary in bytes: - // Measured: `1607` - // Estimated: `2603` - // Minimum execution time: 36_372 nanoseconds. - Weight::from_parts(37_432_000, 2603) + // Measured: `0` + // Estimated: `3593` + // Minimum execution time: 28_184 nanoseconds. + Weight::from_ref_time(49_250_000) + .saturating_add(Weight::from_proof_size(3593)) .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -86,10 +89,11 @@ impl WeightInfo for SubstrateWeight { /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) fn set_balance_creating() -> Weight { // Proof Size summary in bytes: - // Measured: `1757` - // Estimated: `2603` - // Minimum execution time: 26_671 nanoseconds. - Weight::from_parts(28_287_000, 2603) + // Measured: `206` + // Estimated: `3593` + // Minimum execution time: 17_474 nanoseconds. + Weight::from_ref_time(17_777_000) + .saturating_add(Weight::from_proof_size(3593)) .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -97,10 +101,11 @@ impl WeightInfo for SubstrateWeight { /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) fn set_balance_killing() -> Weight { // Proof Size summary in bytes: - // Measured: `1757` - // Estimated: `2603` - // Minimum execution time: 30_122 nanoseconds. - Weight::from_parts(30_615_000, 2603) + // Measured: `206` + // Estimated: `3593` + // Minimum execution time: 20_962 nanoseconds. + Weight::from_ref_time(21_419_000) + .saturating_add(Weight::from_proof_size(3593)) .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -108,10 +113,11 @@ impl WeightInfo for SubstrateWeight { /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) fn force_transfer() -> Weight { // Proof Size summary in bytes: - // Measured: `1719` - // Estimated: `5206` - // Minimum execution time: 47_891 nanoseconds. - Weight::from_parts(48_496_000, 5206) + // Measured: `135` + // Estimated: `6196` + // Minimum execution time: 39_713 nanoseconds. + Weight::from_ref_time(40_360_000) + .saturating_add(Weight::from_proof_size(6196)) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().writes(2_u64)) } @@ -119,10 +125,11 @@ impl WeightInfo for SubstrateWeight { /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) fn transfer_all() -> Weight { // Proof Size summary in bytes: - // Measured: `1607` - // Estimated: `2603` - // Minimum execution time: 42_470 nanoseconds. - Weight::from_parts(42_950_000, 2603) + // Measured: `0` + // Estimated: `3593` + // Minimum execution time: 34_878 nanoseconds. + Weight::from_ref_time(35_121_000) + .saturating_add(Weight::from_proof_size(3593)) .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -130,10 +137,11 @@ impl WeightInfo for SubstrateWeight { /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) fn force_unreserve() -> Weight { // Proof Size summary in bytes: - // Measured: `1641` - // Estimated: `2603` - // Minimum execution time: 23_230 nanoseconds. - Weight::from_parts(23_951_000, 2603) + // Measured: `206` + // Estimated: `3593` + // Minimum execution time: 16_790 nanoseconds. + Weight::from_ref_time(17_029_000) + .saturating_add(Weight::from_proof_size(3593)) .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -145,10 +153,11 @@ impl WeightInfo for () { /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) fn transfer() -> Weight { // Proof Size summary in bytes: - // Measured: `1723` - // Estimated: `2603` - // Minimum execution time: 47_557 nanoseconds. - Weight::from_parts(48_314_000, 2603) + // Measured: `0` + // Estimated: `3593` + // Minimum execution time: 37_815 nanoseconds. + Weight::from_ref_time(38_109_000) + .saturating_add(Weight::from_proof_size(3593)) .saturating_add(RocksDbWeight::get().reads(1_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } @@ -156,10 +165,11 @@ impl WeightInfo for () { /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) fn transfer_keep_alive() -> Weight { // Proof Size summary in bytes: - // Measured: `1607` - // Estimated: `2603` - // Minimum execution time: 36_372 nanoseconds. - Weight::from_parts(37_432_000, 2603) + // Measured: `0` + // Estimated: `3593` + // Minimum execution time: 28_184 nanoseconds. + Weight::from_ref_time(49_250_000) + .saturating_add(Weight::from_proof_size(3593)) .saturating_add(RocksDbWeight::get().reads(1_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } @@ -167,10 +177,11 @@ impl WeightInfo for () { /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) fn set_balance_creating() -> Weight { // Proof Size summary in bytes: - // Measured: `1757` - // Estimated: `2603` - // Minimum execution time: 26_671 nanoseconds. - Weight::from_parts(28_287_000, 2603) + // Measured: `206` + // Estimated: `3593` + // Minimum execution time: 17_474 nanoseconds. + Weight::from_ref_time(17_777_000) + .saturating_add(Weight::from_proof_size(3593)) .saturating_add(RocksDbWeight::get().reads(1_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } @@ -178,10 +189,11 @@ impl WeightInfo for () { /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) fn set_balance_killing() -> Weight { // Proof Size summary in bytes: - // Measured: `1757` - // Estimated: `2603` - // Minimum execution time: 30_122 nanoseconds. - Weight::from_parts(30_615_000, 2603) + // Measured: `206` + // Estimated: `3593` + // Minimum execution time: 20_962 nanoseconds. + Weight::from_ref_time(21_419_000) + .saturating_add(Weight::from_proof_size(3593)) .saturating_add(RocksDbWeight::get().reads(1_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } @@ -189,10 +201,11 @@ impl WeightInfo for () { /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) fn force_transfer() -> Weight { // Proof Size summary in bytes: - // Measured: `1719` - // Estimated: `5206` - // Minimum execution time: 47_891 nanoseconds. - Weight::from_parts(48_496_000, 5206) + // Measured: `135` + // Estimated: `6196` + // Minimum execution time: 39_713 nanoseconds. + Weight::from_ref_time(40_360_000) + .saturating_add(Weight::from_proof_size(6196)) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().writes(2_u64)) } @@ -200,10 +213,11 @@ impl WeightInfo for () { /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) fn transfer_all() -> Weight { // Proof Size summary in bytes: - // Measured: `1607` - // Estimated: `2603` - // Minimum execution time: 42_470 nanoseconds. - Weight::from_parts(42_950_000, 2603) + // Measured: `0` + // Estimated: `3593` + // Minimum execution time: 34_878 nanoseconds. + Weight::from_ref_time(35_121_000) + .saturating_add(Weight::from_proof_size(3593)) .saturating_add(RocksDbWeight::get().reads(1_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } @@ -211,10 +225,11 @@ impl WeightInfo for () { /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) fn force_unreserve() -> Weight { // Proof Size summary in bytes: - // Measured: `1641` - // Estimated: `2603` - // Minimum execution time: 23_230 nanoseconds. - Weight::from_parts(23_951_000, 2603) + // Measured: `206` + // Estimated: `3593` + // Minimum execution time: 16_790 nanoseconds. + Weight::from_ref_time(17_029_000) + .saturating_add(Weight::from_proof_size(3593)) .saturating_add(RocksDbWeight::get().reads(1_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } diff --git a/frame/benchmarking/pov/src/benchmarking.rs b/frame/benchmarking/pov/src/benchmarking.rs index 331570ad2..ebe9b2414 100644 --- a/frame/benchmarking/pov/src/benchmarking.rs +++ b/frame/benchmarking/pov/src/benchmarking.rs @@ -39,6 +39,17 @@ frame_benchmarking::benchmarks! { assert_eq!(Value::::get(), Some(123)); } + #[pov_mode = MaxEncodedLen { + Pov::Value2: Ignored + }] + storage_single_value_ignored_some_read { + Value::::put(123); + Value2::::put(123); + }: { + assert_eq!(Value::::get(), Some(123)); + assert_eq!(Value2::::get(), Some(123)); + } + storage_single_value_read_twice { Value::::put(123); }: { diff --git a/frame/benchmarking/pov/src/weights.rs b/frame/benchmarking/pov/src/weights.rs index e890e9b2e..948d268cf 100644 --- a/frame/benchmarking/pov/src/weights.rs +++ b/frame/benchmarking/pov/src/weights.rs @@ -2,9 +2,9 @@ //! Autogenerated weights for frame_benchmarking_pallet_pov //! //! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev -//! DATE: 2023-01-23, STEPS: `50`, REPEAT: `1`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! DATE: 2023-02-27, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` //! WORST CASE MAP SIZE: `1000000` -//! HOSTNAME: `oty-parity`, CPU: `11th Gen Intel(R) Core(TM) i7-1165G7 @ 2.80GHz` +//! HOSTNAME: `i9`, CPU: `13th Gen Intel(R) Core(TM) i9-13900K` //! EXECUTION: None, WASM-EXECUTION: Compiled, CHAIN: None, DB CACHE: 1024 // Executed Command: @@ -19,7 +19,7 @@ // --steps // 50 // --repeat -// 1 +// 20 // --template=.maintain/frame-weight-template.hbs // --output=frame/benchmarking/pov/src/weights.rs @@ -71,9 +71,10 @@ impl WeightInfo for SubstrateWeight { fn storage_single_value_read() -> Weight { // Proof Size summary in bytes: // Measured: `136` - // Estimated: `499` - // Minimum execution time: 3_291 nanoseconds. - Weight::from_parts(3_291_000, 499) + // Estimated: `1489` + // Minimum execution time: 1_968 nanoseconds. + Weight::from_ref_time(2_060_000) + .saturating_add(Weight::from_proof_size(1489)) .saturating_add(T::DbWeight::get().reads(1_u64)) } /// Storage: Pov Value (r:1 w:0) @@ -82,8 +83,9 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `136` // Estimated: `0` - // Minimum execution time: 2_918 nanoseconds. - Weight::from_ref_time(2_918_000) + // Minimum execution time: 1_934 nanoseconds. + Weight::from_ref_time(2_092_000) + .saturating_add(Weight::from_proof_size(0)) .saturating_add(T::DbWeight::get().reads(1_u64)) } /// Storage: Pov Value (r:1 w:0) @@ -93,9 +95,10 @@ impl WeightInfo for SubstrateWeight { fn storage_single_value_ignored_some_read() -> Weight { // Proof Size summary in bytes: // Measured: `160` - // Estimated: `659` - // Minimum execution time: 4_056 nanoseconds. - Weight::from_parts(4_056_000, 659) + // Estimated: `1649` + // Minimum execution time: 2_605 nanoseconds. + Weight::from_ref_time(2_786_000) + .saturating_add(Weight::from_proof_size(1649)) .saturating_add(T::DbWeight::get().reads(2_u64)) } /// Storage: Pov Value (r:1 w:0) @@ -103,9 +106,10 @@ impl WeightInfo for SubstrateWeight { fn storage_single_value_read_twice() -> Weight { // Proof Size summary in bytes: // Measured: `136` - // Estimated: `499` - // Minimum execution time: 3_552 nanoseconds. - Weight::from_parts(3_552_000, 499) + // Estimated: `1489` + // Minimum execution time: 2_019 nanoseconds. + Weight::from_ref_time(2_214_000) + .saturating_add(Weight::from_proof_size(1489)) .saturating_add(T::DbWeight::get().reads(1_u64)) } /// Storage: Pov Value (r:0 w:1) @@ -114,8 +118,9 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 795 nanoseconds. - Weight::from_ref_time(795_000) + // Minimum execution time: 279 nanoseconds. + Weight::from_ref_time(357_000) + .saturating_add(Weight::from_proof_size(0)) .saturating_add(T::DbWeight::get().writes(1_u64)) } /// Storage: Pov Value (r:0 w:1) @@ -124,8 +129,9 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 865 nanoseconds. - Weight::from_ref_time(865_000) + // Minimum execution time: 291 nanoseconds. + Weight::from_ref_time(378_000) + .saturating_add(Weight::from_proof_size(0)) .saturating_add(T::DbWeight::get().writes(1_u64)) } /// Storage: Pov Map1M (r:1 w:0) @@ -133,9 +139,10 @@ impl WeightInfo for SubstrateWeight { fn storage_1m_map_read_one_value_two_additional_layers() -> Weight { // Proof Size summary in bytes: // Measured: `1275` - // Estimated: `3750` - // Minimum execution time: 9_571 nanoseconds. - Weight::from_parts(9_571_000, 3750) + // Estimated: `4740` + // Minimum execution time: 5_077 nanoseconds. + Weight::from_ref_time(5_400_000) + .saturating_add(Weight::from_proof_size(4740)) .saturating_add(T::DbWeight::get().reads(1_u64)) } /// Storage: Pov Map1M (r:1 w:0) @@ -143,9 +150,10 @@ impl WeightInfo for SubstrateWeight { fn storage_1m_map_read_one_value_three_additional_layers() -> Weight { // Proof Size summary in bytes: // Measured: `1544` - // Estimated: `4019` - // Minimum execution time: 14_597 nanoseconds. - Weight::from_parts(14_597_000, 4019) + // Estimated: `5009` + // Minimum execution time: 5_878 nanoseconds. + Weight::from_ref_time(6_239_000) + .saturating_add(Weight::from_proof_size(5009)) .saturating_add(T::DbWeight::get().reads(1_u64)) } /// Storage: Pov Map1M (r:1 w:0) @@ -153,9 +161,10 @@ impl WeightInfo for SubstrateWeight { fn storage_1m_map_read_one_value_four_additional_layers() -> Weight { // Proof Size summary in bytes: // Measured: `2044` - // Estimated: `4519` - // Minimum execution time: 19_793 nanoseconds. - Weight::from_parts(19_793_000, 4519) + // Estimated: `5509` + // Minimum execution time: 7_282 nanoseconds. + Weight::from_ref_time(8_022_000) + .saturating_add(Weight::from_proof_size(5509)) .saturating_add(T::DbWeight::get().reads(1_u64)) } /// Storage: Pov Map1M (r:100 w:0) @@ -167,13 +176,14 @@ impl WeightInfo for SubstrateWeight { fn storage_map_read_per_component(n: u32, m: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `515 + n * (188 ±0) + m * (188 ±0)` - // Estimated: `0 + n * (3006 ±0) + m * (2511 ±0)` - // Minimum execution time: 266_160 nanoseconds. - Weight::from_ref_time(190_477_747) - // Standard Error: 96_405 - .saturating_add(Weight::from_ref_time(1_049_993).saturating_mul(n.into())) - // Standard Error: 96_405 - .saturating_add(Weight::from_ref_time(1_202_546).saturating_mul(m.into())) + // Estimated: `1980 + n * (3006 ±0) + m * (2511 ±0)` + // Minimum execution time: 195_406 nanoseconds. + Weight::from_ref_time(129_093_464) + .saturating_add(Weight::from_proof_size(1980)) + // Standard Error: 12_134 + .saturating_add(Weight::from_ref_time(855_330).saturating_mul(n.into())) + // Standard Error: 12_134 + .saturating_add(Weight::from_ref_time(870_523).saturating_mul(m.into())) .saturating_add(T::DbWeight::get().reads((1_u64).saturating_mul(n.into()))) .saturating_add(T::DbWeight::get().reads((1_u64).saturating_mul(m.into()))) .saturating_add(Weight::from_proof_size(3006).saturating_mul(n.into())) @@ -188,13 +198,14 @@ impl WeightInfo for SubstrateWeight { fn storage_map_read_per_component_one_ignored(n: u32, m: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `515 + n * (188 ±0) + m * (188 ±0)` - // Estimated: `695 + n * (3195 ±2) + m * (189 ±2)` - // Minimum execution time: 265_113 nanoseconds. - Weight::from_parts(215_943_973, 695) - // Standard Error: 199_907 - .saturating_add(Weight::from_ref_time(988_371).saturating_mul(n.into())) - // Standard Error: 199_907 - .saturating_add(Weight::from_ref_time(1_145_693).saturating_mul(m.into())) + // Estimated: `1685 + n * (3195 ±0) + m * (189 ±0)` + // Minimum execution time: 195_053 nanoseconds. + Weight::from_ref_time(131_322_479) + .saturating_add(Weight::from_proof_size(1685)) + // Standard Error: 12_161 + .saturating_add(Weight::from_ref_time(843_047).saturating_mul(n.into())) + // Standard Error: 12_161 + .saturating_add(Weight::from_ref_time(858_668).saturating_mul(m.into())) .saturating_add(T::DbWeight::get().reads((1_u64).saturating_mul(n.into()))) .saturating_add(T::DbWeight::get().reads((1_u64).saturating_mul(m.into()))) .saturating_add(Weight::from_proof_size(3195).saturating_mul(n.into())) @@ -206,11 +217,12 @@ impl WeightInfo for SubstrateWeight { fn storage_1m_map_one_entry_repeated_read(n: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `170` - // Estimated: `2511` - // Minimum execution time: 119 nanoseconds. - Weight::from_parts(3_649_405, 2511) - // Standard Error: 4_472 - .saturating_add(Weight::from_ref_time(432_147).saturating_mul(n.into())) + // Estimated: `3501` + // Minimum execution time: 22 nanoseconds. + Weight::from_ref_time(2_334_945) + .saturating_add(Weight::from_proof_size(3501)) + // Standard Error: 624 + .saturating_add(Weight::from_ref_time(282_046).saturating_mul(n.into())) .saturating_add(T::DbWeight::get().reads(1_u64)) } /// Storage: Pov Map1M (r:100 w:0) @@ -219,11 +231,12 @@ impl WeightInfo for SubstrateWeight { fn storage_1m_map_multiple_entry_repeated_read(n: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `147 + n * (40 ±0)` - // Estimated: `0 + n * (2511 ±0)` - // Minimum execution time: 106 nanoseconds. - Weight::from_ref_time(59_892_609) - // Standard Error: 418_004 - .saturating_add(Weight::from_ref_time(5_919_793).saturating_mul(n.into())) + // Estimated: `990 + n * (2511 ±0)` + // Minimum execution time: 20 nanoseconds. + Weight::from_ref_time(525_027) + .saturating_add(Weight::from_proof_size(990)) + // Standard Error: 2_767 + .saturating_add(Weight::from_ref_time(3_887_350).saturating_mul(n.into())) .saturating_add(T::DbWeight::get().reads((1_u64).saturating_mul(n.into()))) .saturating_add(Weight::from_proof_size(2511).saturating_mul(n.into())) } @@ -233,11 +246,12 @@ impl WeightInfo for SubstrateWeight { fn storage_1m_double_map_read_per_component(n: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `21938 + n * (57 ±0)` - // Estimated: `0 + n * (2543 ±0)` - // Minimum execution time: 550 nanoseconds. - Weight::from_ref_time(65_431_603) - // Standard Error: 66_935 - .saturating_add(Weight::from_ref_time(2_825_371).saturating_mul(n.into())) + // Estimated: `990 + n * (2543 ±0)` + // Minimum execution time: 34 nanoseconds. + Weight::from_ref_time(18_341_393) + .saturating_add(Weight::from_proof_size(990)) + // Standard Error: 1_312 + .saturating_add(Weight::from_ref_time(2_053_135).saturating_mul(n.into())) .saturating_add(T::DbWeight::get().reads((1_u64).saturating_mul(n.into()))) .saturating_add(Weight::from_proof_size(2543).saturating_mul(n.into())) } @@ -246,9 +260,10 @@ impl WeightInfo for SubstrateWeight { fn storage_value_bounded_read() -> Weight { // Proof Size summary in bytes: // Measured: `109` - // Estimated: `528` - // Minimum execution time: 2_541 nanoseconds. - Weight::from_parts(2_541_000, 528) + // Estimated: `1518` + // Minimum execution time: 1_163 nanoseconds. + Weight::from_ref_time(1_274_000) + .saturating_add(Weight::from_proof_size(1518)) .saturating_add(T::DbWeight::get().reads(1_u64)) } /// Storage: Pov UnboundedValue (r:1 w:0) @@ -256,9 +271,10 @@ impl WeightInfo for SubstrateWeight { fn storage_value_unbounded_read() -> Weight { // Proof Size summary in bytes: // Measured: `109` - // Estimated: `604` - // Minimum execution time: 2_226 nanoseconds. - Weight::from_parts(2_226_000, 604) + // Estimated: `1594` + // Minimum execution time: 1_167 nanoseconds. + Weight::from_ref_time(1_367_000) + .saturating_add(Weight::from_proof_size(1594)) .saturating_add(T::DbWeight::get().reads(1_u64)) } /// Storage: Pov UnboundedValue (r:1 w:0) @@ -267,8 +283,9 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `109` // Estimated: `0` - // Minimum execution time: 2_100 nanoseconds. - Weight::from_ref_time(2_100_000) + // Minimum execution time: 1_155 nanoseconds. + Weight::from_ref_time(1_248_000) + .saturating_add(Weight::from_proof_size(0)) .saturating_add(T::DbWeight::get().reads(1_u64)) } /// Storage: Pov UnboundedValue (r:1 w:0) @@ -278,9 +295,10 @@ impl WeightInfo for SubstrateWeight { fn storage_value_bounded_and_unbounded_read() -> Weight { // Proof Size summary in bytes: // Measured: `109` - // Estimated: `1132` - // Minimum execution time: 2_808 nanoseconds. - Weight::from_parts(2_808_000, 1132) + // Estimated: `3112` + // Minimum execution time: 1_424 nanoseconds. + Weight::from_ref_time(1_601_000) + .saturating_add(Weight::from_proof_size(3112)) .saturating_add(T::DbWeight::get().reads(2_u64)) } /// Storage: Pov LargeValue (r:1 w:0) @@ -289,11 +307,12 @@ impl WeightInfo for SubstrateWeight { fn measured_storage_value_read_linear_size(l: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `174 + l * (1 ±0)` - // Estimated: `666 + l * (1 ±0)` - // Minimum execution time: 3_385 nanoseconds. - Weight::from_parts(3_385_000, 666) - // Standard Error: 10 - .saturating_add(Weight::from_ref_time(356).saturating_mul(l.into())) + // Estimated: `1656 + l * (1 ±0)` + // Minimum execution time: 1_744 nanoseconds. + Weight::from_ref_time(1_800_000) + .saturating_add(Weight::from_proof_size(1656)) + // Standard Error: 4 + .saturating_add(Weight::from_ref_time(443).saturating_mul(l.into())) .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(Weight::from_proof_size(1).saturating_mul(l.into())) } @@ -303,11 +322,12 @@ impl WeightInfo for SubstrateWeight { fn mel_storage_value_read_linear_size(l: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `174 + l * (1 ±0)` - // Estimated: `4194803` - // Minimum execution time: 3_342 nanoseconds. - Weight::from_parts(3_342_000, 4194803) + // Estimated: `4195793` + // Minimum execution time: 1_770 nanoseconds. + Weight::from_ref_time(1_813_000) + .saturating_add(Weight::from_proof_size(4195793)) // Standard Error: 6 - .saturating_add(Weight::from_ref_time(345).saturating_mul(l.into())) + .saturating_add(Weight::from_ref_time(495).saturating_mul(l.into())) .saturating_add(T::DbWeight::get().reads(1_u64)) } /// Storage: Pov LargeValue (r:1 w:0) @@ -318,11 +338,12 @@ impl WeightInfo for SubstrateWeight { fn measured_storage_double_value_read_linear_size(l: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `235 + l * (2 ±0)` - // Estimated: `1448 + l * (4 ±0)` - // Minimum execution time: 4_519 nanoseconds. - Weight::from_parts(4_519_000, 1448) - // Standard Error: 9 - .saturating_add(Weight::from_ref_time(560).saturating_mul(l.into())) + // Estimated: `3428 + l * (4 ±0)` + // Minimum execution time: 2_349 nanoseconds. + Weight::from_ref_time(2_423_000) + .saturating_add(Weight::from_proof_size(3428)) + // Standard Error: 11 + .saturating_add(Weight::from_ref_time(950).saturating_mul(l.into())) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(Weight::from_proof_size(4).saturating_mul(l.into())) } @@ -334,11 +355,12 @@ impl WeightInfo for SubstrateWeight { fn mel_storage_double_value_read_linear_size(l: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `235 + l * (2 ±0)` - // Estimated: `8389606` - // Minimum execution time: 4_462 nanoseconds. - Weight::from_parts(4_462_000, 8389606) - // Standard Error: 7 - .saturating_add(Weight::from_ref_time(538).saturating_mul(l.into())) + // Estimated: `8391586` + // Minimum execution time: 2_315 nanoseconds. + Weight::from_ref_time(2_409_000) + .saturating_add(Weight::from_proof_size(8391586)) + // Standard Error: 12 + .saturating_add(Weight::from_ref_time(984).saturating_mul(l.into())) .saturating_add(T::DbWeight::get().reads(2_u64)) } /// Storage: Pov LargeValue (r:1 w:0) @@ -349,11 +371,12 @@ impl WeightInfo for SubstrateWeight { fn mel_mixed_storage_double_value_read_linear_size(l: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `235 + l * (2 ±0)` - // Estimated: `4195527 + l * (2 ±0)` - // Minimum execution time: 4_552 nanoseconds. - Weight::from_parts(4_552_000, 4195527) - // Standard Error: 6 - .saturating_add(Weight::from_ref_time(507).saturating_mul(l.into())) + // Estimated: `4197507 + l * (2 ±0)` + // Minimum execution time: 2_370 nanoseconds. + Weight::from_ref_time(2_474_000) + .saturating_add(Weight::from_proof_size(4197507)) + // Standard Error: 11 + .saturating_add(Weight::from_ref_time(956).saturating_mul(l.into())) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(Weight::from_proof_size(2).saturating_mul(l.into())) } @@ -365,11 +388,12 @@ impl WeightInfo for SubstrateWeight { fn measured_mixed_storage_double_value_read_linear_size(l: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `235 + l * (2 ±0)` - // Estimated: `4195527 + l * (2 ±0)` - // Minimum execution time: 4_236 nanoseconds. - Weight::from_parts(4_236_000, 4195527) - // Standard Error: 8 - .saturating_add(Weight::from_ref_time(517).saturating_mul(l.into())) + // Estimated: `4197507 + l * (2 ±0)` + // Minimum execution time: 2_375 nanoseconds. + Weight::from_ref_time(2_420_000) + .saturating_add(Weight::from_proof_size(4197507)) + // Standard Error: 9 + .saturating_add(Weight::from_ref_time(914).saturating_mul(l.into())) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(Weight::from_proof_size(2).saturating_mul(l.into())) } @@ -381,11 +405,12 @@ impl WeightInfo for SubstrateWeight { fn storage_map_unbounded_both_measured_read(i: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `293 + i * (8 ±0)` - // Estimated: `5524 + i * (16 ±0)` - // Minimum execution time: 5_649 nanoseconds. - Weight::from_parts(6_111_237, 5524) - // Standard Error: 1_060 - .saturating_add(Weight::from_ref_time(2_693).saturating_mul(i.into())) + // Estimated: `7504 + i * (16 ±0)` + // Minimum execution time: 3_305 nanoseconds. + Weight::from_ref_time(3_689_335) + .saturating_add(Weight::from_proof_size(7504)) + // Standard Error: 29 + .saturating_add(Weight::from_ref_time(638).saturating_mul(i.into())) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(Weight::from_proof_size(16).saturating_mul(i.into())) } @@ -397,9 +422,12 @@ impl WeightInfo for SubstrateWeight { fn storage_map_partial_unbounded_read(i: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `260 + i * (4 ±0)` - // Estimated: `5243 + i * (4 ±0)` - // Minimum execution time: 5_997 nanoseconds. - Weight::from_parts(7_996_508, 5243) + // Estimated: `7223 + i * (4 ±0)` + // Minimum execution time: 3_469 nanoseconds. + Weight::from_ref_time(3_878_896) + .saturating_add(Weight::from_proof_size(7223)) + // Standard Error: 33 + .saturating_add(Weight::from_ref_time(356).saturating_mul(i.into())) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(Weight::from_proof_size(4).saturating_mul(i.into())) } @@ -411,11 +439,12 @@ impl WeightInfo for SubstrateWeight { fn storage_map_partial_unbounded_ignored_read(i: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `260 + i * (4 ±0)` - // Estimated: `2768 + i * (4 ±0)` - // Minimum execution time: 5_679 nanoseconds. - Weight::from_parts(6_496_804, 2768) - // Standard Error: 611 - .saturating_add(Weight::from_ref_time(930).saturating_mul(i.into())) + // Estimated: `3758 + i * (4 ±0)` + // Minimum execution time: 3_442 nanoseconds. + Weight::from_ref_time(3_881_051) + .saturating_add(Weight::from_proof_size(3758)) + // Standard Error: 35 + .saturating_add(Weight::from_ref_time(384).saturating_mul(i.into())) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(Weight::from_proof_size(4).saturating_mul(i.into())) } @@ -423,15 +452,17 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 7_087 nanoseconds. - Weight::from_ref_time(7_087_000) + // Minimum execution time: 1_619 nanoseconds. + Weight::from_ref_time(1_728_000) + .saturating_add(Weight::from_proof_size(0)) } fn noop() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 2_719 nanoseconds. - Weight::from_ref_time(2_719_000) + // Minimum execution time: 546 nanoseconds. + Weight::from_ref_time(640_000) + .saturating_add(Weight::from_proof_size(0)) } } @@ -442,9 +473,10 @@ impl WeightInfo for () { fn storage_single_value_read() -> Weight { // Proof Size summary in bytes: // Measured: `136` - // Estimated: `499` - // Minimum execution time: 3_291 nanoseconds. - Weight::from_parts(3_291_000, 499) + // Estimated: `1489` + // Minimum execution time: 1_968 nanoseconds. + Weight::from_ref_time(2_060_000) + .saturating_add(Weight::from_proof_size(1489)) .saturating_add(RocksDbWeight::get().reads(1_u64)) } /// Storage: Pov Value (r:1 w:0) @@ -453,8 +485,9 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `136` // Estimated: `0` - // Minimum execution time: 2_918 nanoseconds. - Weight::from_ref_time(2_918_000) + // Minimum execution time: 1_934 nanoseconds. + Weight::from_ref_time(2_092_000) + .saturating_add(Weight::from_proof_size(0)) .saturating_add(RocksDbWeight::get().reads(1_u64)) } /// Storage: Pov Value (r:1 w:0) @@ -464,9 +497,10 @@ impl WeightInfo for () { fn storage_single_value_ignored_some_read() -> Weight { // Proof Size summary in bytes: // Measured: `160` - // Estimated: `659` - // Minimum execution time: 4_056 nanoseconds. - Weight::from_parts(4_056_000, 659) + // Estimated: `1649` + // Minimum execution time: 2_605 nanoseconds. + Weight::from_ref_time(2_786_000) + .saturating_add(Weight::from_proof_size(1649)) .saturating_add(RocksDbWeight::get().reads(2_u64)) } /// Storage: Pov Value (r:1 w:0) @@ -474,9 +508,10 @@ impl WeightInfo for () { fn storage_single_value_read_twice() -> Weight { // Proof Size summary in bytes: // Measured: `136` - // Estimated: `499` - // Minimum execution time: 3_552 nanoseconds. - Weight::from_parts(3_552_000, 499) + // Estimated: `1489` + // Minimum execution time: 2_019 nanoseconds. + Weight::from_ref_time(2_214_000) + .saturating_add(Weight::from_proof_size(1489)) .saturating_add(RocksDbWeight::get().reads(1_u64)) } /// Storage: Pov Value (r:0 w:1) @@ -485,8 +520,9 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 795 nanoseconds. - Weight::from_ref_time(795_000) + // Minimum execution time: 279 nanoseconds. + Weight::from_ref_time(357_000) + .saturating_add(Weight::from_proof_size(0)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } /// Storage: Pov Value (r:0 w:1) @@ -495,8 +531,9 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 865 nanoseconds. - Weight::from_ref_time(865_000) + // Minimum execution time: 291 nanoseconds. + Weight::from_ref_time(378_000) + .saturating_add(Weight::from_proof_size(0)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } /// Storage: Pov Map1M (r:1 w:0) @@ -504,9 +541,10 @@ impl WeightInfo for () { fn storage_1m_map_read_one_value_two_additional_layers() -> Weight { // Proof Size summary in bytes: // Measured: `1275` - // Estimated: `3750` - // Minimum execution time: 9_571 nanoseconds. - Weight::from_parts(9_571_000, 3750) + // Estimated: `4740` + // Minimum execution time: 5_077 nanoseconds. + Weight::from_ref_time(5_400_000) + .saturating_add(Weight::from_proof_size(4740)) .saturating_add(RocksDbWeight::get().reads(1_u64)) } /// Storage: Pov Map1M (r:1 w:0) @@ -514,9 +552,10 @@ impl WeightInfo for () { fn storage_1m_map_read_one_value_three_additional_layers() -> Weight { // Proof Size summary in bytes: // Measured: `1544` - // Estimated: `4019` - // Minimum execution time: 14_597 nanoseconds. - Weight::from_parts(14_597_000, 4019) + // Estimated: `5009` + // Minimum execution time: 5_878 nanoseconds. + Weight::from_ref_time(6_239_000) + .saturating_add(Weight::from_proof_size(5009)) .saturating_add(RocksDbWeight::get().reads(1_u64)) } /// Storage: Pov Map1M (r:1 w:0) @@ -524,9 +563,10 @@ impl WeightInfo for () { fn storage_1m_map_read_one_value_four_additional_layers() -> Weight { // Proof Size summary in bytes: // Measured: `2044` - // Estimated: `4519` - // Minimum execution time: 19_793 nanoseconds. - Weight::from_parts(19_793_000, 4519) + // Estimated: `5509` + // Minimum execution time: 7_282 nanoseconds. + Weight::from_ref_time(8_022_000) + .saturating_add(Weight::from_proof_size(5509)) .saturating_add(RocksDbWeight::get().reads(1_u64)) } /// Storage: Pov Map1M (r:100 w:0) @@ -538,13 +578,14 @@ impl WeightInfo for () { fn storage_map_read_per_component(n: u32, m: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `515 + n * (188 ±0) + m * (188 ±0)` - // Estimated: `0 + n * (3006 ±0) + m * (2511 ±0)` - // Minimum execution time: 266_160 nanoseconds. - Weight::from_ref_time(190_477_747) - // Standard Error: 96_405 - .saturating_add(Weight::from_ref_time(1_049_993).saturating_mul(n.into())) - // Standard Error: 96_405 - .saturating_add(Weight::from_ref_time(1_202_546).saturating_mul(m.into())) + // Estimated: `1980 + n * (3006 ±0) + m * (2511 ±0)` + // Minimum execution time: 195_406 nanoseconds. + Weight::from_ref_time(129_093_464) + .saturating_add(Weight::from_proof_size(1980)) + // Standard Error: 12_134 + .saturating_add(Weight::from_ref_time(855_330).saturating_mul(n.into())) + // Standard Error: 12_134 + .saturating_add(Weight::from_ref_time(870_523).saturating_mul(m.into())) .saturating_add(RocksDbWeight::get().reads((1_u64).saturating_mul(n.into()))) .saturating_add(RocksDbWeight::get().reads((1_u64).saturating_mul(m.into()))) .saturating_add(Weight::from_proof_size(3006).saturating_mul(n.into())) @@ -559,13 +600,14 @@ impl WeightInfo for () { fn storage_map_read_per_component_one_ignored(n: u32, m: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `515 + n * (188 ±0) + m * (188 ±0)` - // Estimated: `695 + n * (3195 ±2) + m * (189 ±2)` - // Minimum execution time: 265_113 nanoseconds. - Weight::from_parts(215_943_973, 695) - // Standard Error: 199_907 - .saturating_add(Weight::from_ref_time(988_371).saturating_mul(n.into())) - // Standard Error: 199_907 - .saturating_add(Weight::from_ref_time(1_145_693).saturating_mul(m.into())) + // Estimated: `1685 + n * (3195 ±0) + m * (189 ±0)` + // Minimum execution time: 195_053 nanoseconds. + Weight::from_ref_time(131_322_479) + .saturating_add(Weight::from_proof_size(1685)) + // Standard Error: 12_161 + .saturating_add(Weight::from_ref_time(843_047).saturating_mul(n.into())) + // Standard Error: 12_161 + .saturating_add(Weight::from_ref_time(858_668).saturating_mul(m.into())) .saturating_add(RocksDbWeight::get().reads((1_u64).saturating_mul(n.into()))) .saturating_add(RocksDbWeight::get().reads((1_u64).saturating_mul(m.into()))) .saturating_add(Weight::from_proof_size(3195).saturating_mul(n.into())) @@ -577,11 +619,12 @@ impl WeightInfo for () { fn storage_1m_map_one_entry_repeated_read(n: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `170` - // Estimated: `2511` - // Minimum execution time: 119 nanoseconds. - Weight::from_parts(3_649_405, 2511) - // Standard Error: 4_472 - .saturating_add(Weight::from_ref_time(432_147).saturating_mul(n.into())) + // Estimated: `3501` + // Minimum execution time: 22 nanoseconds. + Weight::from_ref_time(2_334_945) + .saturating_add(Weight::from_proof_size(3501)) + // Standard Error: 624 + .saturating_add(Weight::from_ref_time(282_046).saturating_mul(n.into())) .saturating_add(RocksDbWeight::get().reads(1_u64)) } /// Storage: Pov Map1M (r:100 w:0) @@ -590,11 +633,12 @@ impl WeightInfo for () { fn storage_1m_map_multiple_entry_repeated_read(n: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `147 + n * (40 ±0)` - // Estimated: `0 + n * (2511 ±0)` - // Minimum execution time: 106 nanoseconds. - Weight::from_ref_time(59_892_609) - // Standard Error: 418_004 - .saturating_add(Weight::from_ref_time(5_919_793).saturating_mul(n.into())) + // Estimated: `990 + n * (2511 ±0)` + // Minimum execution time: 20 nanoseconds. + Weight::from_ref_time(525_027) + .saturating_add(Weight::from_proof_size(990)) + // Standard Error: 2_767 + .saturating_add(Weight::from_ref_time(3_887_350).saturating_mul(n.into())) .saturating_add(RocksDbWeight::get().reads((1_u64).saturating_mul(n.into()))) .saturating_add(Weight::from_proof_size(2511).saturating_mul(n.into())) } @@ -604,11 +648,12 @@ impl WeightInfo for () { fn storage_1m_double_map_read_per_component(n: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `21938 + n * (57 ±0)` - // Estimated: `0 + n * (2543 ±0)` - // Minimum execution time: 550 nanoseconds. - Weight::from_ref_time(65_431_603) - // Standard Error: 66_935 - .saturating_add(Weight::from_ref_time(2_825_371).saturating_mul(n.into())) + // Estimated: `990 + n * (2543 ±0)` + // Minimum execution time: 34 nanoseconds. + Weight::from_ref_time(18_341_393) + .saturating_add(Weight::from_proof_size(990)) + // Standard Error: 1_312 + .saturating_add(Weight::from_ref_time(2_053_135).saturating_mul(n.into())) .saturating_add(RocksDbWeight::get().reads((1_u64).saturating_mul(n.into()))) .saturating_add(Weight::from_proof_size(2543).saturating_mul(n.into())) } @@ -617,9 +662,10 @@ impl WeightInfo for () { fn storage_value_bounded_read() -> Weight { // Proof Size summary in bytes: // Measured: `109` - // Estimated: `528` - // Minimum execution time: 2_541 nanoseconds. - Weight::from_parts(2_541_000, 528) + // Estimated: `1518` + // Minimum execution time: 1_163 nanoseconds. + Weight::from_ref_time(1_274_000) + .saturating_add(Weight::from_proof_size(1518)) .saturating_add(RocksDbWeight::get().reads(1_u64)) } /// Storage: Pov UnboundedValue (r:1 w:0) @@ -627,9 +673,10 @@ impl WeightInfo for () { fn storage_value_unbounded_read() -> Weight { // Proof Size summary in bytes: // Measured: `109` - // Estimated: `604` - // Minimum execution time: 2_226 nanoseconds. - Weight::from_parts(2_226_000, 604) + // Estimated: `1594` + // Minimum execution time: 1_167 nanoseconds. + Weight::from_ref_time(1_367_000) + .saturating_add(Weight::from_proof_size(1594)) .saturating_add(RocksDbWeight::get().reads(1_u64)) } /// Storage: Pov UnboundedValue (r:1 w:0) @@ -638,8 +685,9 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `109` // Estimated: `0` - // Minimum execution time: 2_100 nanoseconds. - Weight::from_ref_time(2_100_000) + // Minimum execution time: 1_155 nanoseconds. + Weight::from_ref_time(1_248_000) + .saturating_add(Weight::from_proof_size(0)) .saturating_add(RocksDbWeight::get().reads(1_u64)) } /// Storage: Pov UnboundedValue (r:1 w:0) @@ -649,9 +697,10 @@ impl WeightInfo for () { fn storage_value_bounded_and_unbounded_read() -> Weight { // Proof Size summary in bytes: // Measured: `109` - // Estimated: `1132` - // Minimum execution time: 2_808 nanoseconds. - Weight::from_parts(2_808_000, 1132) + // Estimated: `3112` + // Minimum execution time: 1_424 nanoseconds. + Weight::from_ref_time(1_601_000) + .saturating_add(Weight::from_proof_size(3112)) .saturating_add(RocksDbWeight::get().reads(2_u64)) } /// Storage: Pov LargeValue (r:1 w:0) @@ -660,11 +709,12 @@ impl WeightInfo for () { fn measured_storage_value_read_linear_size(l: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `174 + l * (1 ±0)` - // Estimated: `666 + l * (1 ±0)` - // Minimum execution time: 3_385 nanoseconds. - Weight::from_parts(3_385_000, 666) - // Standard Error: 10 - .saturating_add(Weight::from_ref_time(356).saturating_mul(l.into())) + // Estimated: `1656 + l * (1 ±0)` + // Minimum execution time: 1_744 nanoseconds. + Weight::from_ref_time(1_800_000) + .saturating_add(Weight::from_proof_size(1656)) + // Standard Error: 4 + .saturating_add(Weight::from_ref_time(443).saturating_mul(l.into())) .saturating_add(RocksDbWeight::get().reads(1_u64)) .saturating_add(Weight::from_proof_size(1).saturating_mul(l.into())) } @@ -674,11 +724,12 @@ impl WeightInfo for () { fn mel_storage_value_read_linear_size(l: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `174 + l * (1 ±0)` - // Estimated: `4194803` - // Minimum execution time: 3_342 nanoseconds. - Weight::from_parts(3_342_000, 4194803) + // Estimated: `4195793` + // Minimum execution time: 1_770 nanoseconds. + Weight::from_ref_time(1_813_000) + .saturating_add(Weight::from_proof_size(4195793)) // Standard Error: 6 - .saturating_add(Weight::from_ref_time(345).saturating_mul(l.into())) + .saturating_add(Weight::from_ref_time(495).saturating_mul(l.into())) .saturating_add(RocksDbWeight::get().reads(1_u64)) } /// Storage: Pov LargeValue (r:1 w:0) @@ -689,11 +740,12 @@ impl WeightInfo for () { fn measured_storage_double_value_read_linear_size(l: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `235 + l * (2 ±0)` - // Estimated: `1448 + l * (4 ±0)` - // Minimum execution time: 4_519 nanoseconds. - Weight::from_parts(4_519_000, 1448) - // Standard Error: 9 - .saturating_add(Weight::from_ref_time(560).saturating_mul(l.into())) + // Estimated: `3428 + l * (4 ±0)` + // Minimum execution time: 2_349 nanoseconds. + Weight::from_ref_time(2_423_000) + .saturating_add(Weight::from_proof_size(3428)) + // Standard Error: 11 + .saturating_add(Weight::from_ref_time(950).saturating_mul(l.into())) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(Weight::from_proof_size(4).saturating_mul(l.into())) } @@ -705,11 +757,12 @@ impl WeightInfo for () { fn mel_storage_double_value_read_linear_size(l: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `235 + l * (2 ±0)` - // Estimated: `8389606` - // Minimum execution time: 4_462 nanoseconds. - Weight::from_parts(4_462_000, 8389606) - // Standard Error: 7 - .saturating_add(Weight::from_ref_time(538).saturating_mul(l.into())) + // Estimated: `8391586` + // Minimum execution time: 2_315 nanoseconds. + Weight::from_ref_time(2_409_000) + .saturating_add(Weight::from_proof_size(8391586)) + // Standard Error: 12 + .saturating_add(Weight::from_ref_time(984).saturating_mul(l.into())) .saturating_add(RocksDbWeight::get().reads(2_u64)) } /// Storage: Pov LargeValue (r:1 w:0) @@ -720,11 +773,12 @@ impl WeightInfo for () { fn mel_mixed_storage_double_value_read_linear_size(l: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `235 + l * (2 ±0)` - // Estimated: `4195527 + l * (2 ±0)` - // Minimum execution time: 4_552 nanoseconds. - Weight::from_parts(4_552_000, 4195527) - // Standard Error: 6 - .saturating_add(Weight::from_ref_time(507).saturating_mul(l.into())) + // Estimated: `4197507 + l * (2 ±0)` + // Minimum execution time: 2_370 nanoseconds. + Weight::from_ref_time(2_474_000) + .saturating_add(Weight::from_proof_size(4197507)) + // Standard Error: 11 + .saturating_add(Weight::from_ref_time(956).saturating_mul(l.into())) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(Weight::from_proof_size(2).saturating_mul(l.into())) } @@ -736,11 +790,12 @@ impl WeightInfo for () { fn measured_mixed_storage_double_value_read_linear_size(l: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `235 + l * (2 ±0)` - // Estimated: `4195527 + l * (2 ±0)` - // Minimum execution time: 4_236 nanoseconds. - Weight::from_parts(4_236_000, 4195527) - // Standard Error: 8 - .saturating_add(Weight::from_ref_time(517).saturating_mul(l.into())) + // Estimated: `4197507 + l * (2 ±0)` + // Minimum execution time: 2_375 nanoseconds. + Weight::from_ref_time(2_420_000) + .saturating_add(Weight::from_proof_size(4197507)) + // Standard Error: 9 + .saturating_add(Weight::from_ref_time(914).saturating_mul(l.into())) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(Weight::from_proof_size(2).saturating_mul(l.into())) } @@ -752,11 +807,12 @@ impl WeightInfo for () { fn storage_map_unbounded_both_measured_read(i: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `293 + i * (8 ±0)` - // Estimated: `5524 + i * (16 ±0)` - // Minimum execution time: 5_649 nanoseconds. - Weight::from_parts(6_111_237, 5524) - // Standard Error: 1_060 - .saturating_add(Weight::from_ref_time(2_693).saturating_mul(i.into())) + // Estimated: `7504 + i * (16 ±0)` + // Minimum execution time: 3_305 nanoseconds. + Weight::from_ref_time(3_689_335) + .saturating_add(Weight::from_proof_size(7504)) + // Standard Error: 29 + .saturating_add(Weight::from_ref_time(638).saturating_mul(i.into())) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(Weight::from_proof_size(16).saturating_mul(i.into())) } @@ -768,9 +824,12 @@ impl WeightInfo for () { fn storage_map_partial_unbounded_read(i: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `260 + i * (4 ±0)` - // Estimated: `5243 + i * (4 ±0)` - // Minimum execution time: 5_997 nanoseconds. - Weight::from_parts(7_996_508, 5243) + // Estimated: `7223 + i * (4 ±0)` + // Minimum execution time: 3_469 nanoseconds. + Weight::from_ref_time(3_878_896) + .saturating_add(Weight::from_proof_size(7223)) + // Standard Error: 33 + .saturating_add(Weight::from_ref_time(356).saturating_mul(i.into())) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(Weight::from_proof_size(4).saturating_mul(i.into())) } @@ -782,11 +841,12 @@ impl WeightInfo for () { fn storage_map_partial_unbounded_ignored_read(i: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `260 + i * (4 ±0)` - // Estimated: `2768 + i * (4 ±0)` - // Minimum execution time: 5_679 nanoseconds. - Weight::from_parts(6_496_804, 2768) - // Standard Error: 611 - .saturating_add(Weight::from_ref_time(930).saturating_mul(i.into())) + // Estimated: `3758 + i * (4 ±0)` + // Minimum execution time: 3_442 nanoseconds. + Weight::from_ref_time(3_881_051) + .saturating_add(Weight::from_proof_size(3758)) + // Standard Error: 35 + .saturating_add(Weight::from_ref_time(384).saturating_mul(i.into())) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(Weight::from_proof_size(4).saturating_mul(i.into())) } @@ -794,14 +854,16 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 7_087 nanoseconds. - Weight::from_ref_time(7_087_000) + // Minimum execution time: 1_619 nanoseconds. + Weight::from_ref_time(1_728_000) + .saturating_add(Weight::from_proof_size(0)) } fn noop() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 2_719 nanoseconds. - Weight::from_ref_time(2_719_000) + // Minimum execution time: 546 nanoseconds. + Weight::from_ref_time(640_000) + .saturating_add(Weight::from_proof_size(0)) } } diff --git a/frame/glutton/src/tests.rs b/frame/glutton/src/tests.rs index d2d2d4ede..8d2c86ed4 100644 --- a/frame/glutton/src/tests.rs +++ b/frame/glutton/src/tests.rs @@ -177,7 +177,7 @@ fn on_idle_weight_low_proof_is_close_enough_works() { let ratio = Perbill::from_rational(got.proof_size(), should.proof_size()); // Just a sanity check here. assert!( - ratio >= Perbill::from_percent(80), + ratio >= Perbill::from_percent(50), "Too few proof size consumed, was only {:?} of expected", ratio ); diff --git a/frame/glutton/src/weights.rs b/frame/glutton/src/weights.rs index 1e09d02c6..9c8e161c8 100644 --- a/frame/glutton/src/weights.rs +++ b/frame/glutton/src/weights.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2023 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); @@ -18,9 +18,9 @@ //! Autogenerated weights for pallet_glutton //! //! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev -//! DATE: 2023-02-20, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! DATE: 2023-02-28, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` //! WORST CASE MAP SIZE: `1000000` -//! HOSTNAME: `runner-ehxwxxsd-project-145-concurrent-0`, CPU: `Intel(R) Xeon(R) CPU @ 2.60GHz` +//! HOSTNAME: `bm3`, CPU: `Intel(R) Core(TM) i7-7700K CPU @ 4.20GHz` //! EXECUTION: Some(Wasm), WASM-EXECUTION: Compiled, CHAIN: Some("dev"), DB CACHE: 1024 // Executed Command: @@ -33,7 +33,7 @@ // --execution=wasm // --wasm-execution=compiled // --heap-pages=4096 -// --json-file=/builds/parity/mirrors/substrate/.git/.artifacts/bench.json +// --json-file=/var/lib/gitlab-runner/builds/zyw4fam_/0/parity/mirrors/substrate/.git/.artifacts/bench.json // --pallet=pallet_glutton // --chain=dev // --header=./HEADER-APACHE2 @@ -71,12 +71,12 @@ impl WeightInfo for SubstrateWeight { fn initialize_pallet_grow(n: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `4` - // Estimated: `499` - // Minimum execution time: 9_248 nanoseconds. - Weight::from_ref_time(9_582_000) - .saturating_add(Weight::from_proof_size(499)) - // Standard Error: 1_144 - .saturating_add(Weight::from_ref_time(1_624_225).saturating_mul(n.into())) + // Estimated: `1489` + // Minimum execution time: 10_218 nanoseconds. + Weight::from_ref_time(10_510_000) + .saturating_add(Weight::from_proof_size(1489)) + // Standard Error: 1_582 + .saturating_add(Weight::from_ref_time(1_577_660).saturating_mul(n.into())) .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) .saturating_add(T::DbWeight::get().writes((1_u64).saturating_mul(n.into()))) @@ -89,12 +89,12 @@ impl WeightInfo for SubstrateWeight { fn initialize_pallet_shrink(n: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `65` - // Estimated: `499` - // Minimum execution time: 10_210 nanoseconds. - Weight::from_ref_time(7_260_854) - .saturating_add(Weight::from_proof_size(499)) - // Standard Error: 1_459 - .saturating_add(Weight::from_ref_time(1_053_844).saturating_mul(n.into())) + // Estimated: `1489` + // Minimum execution time: 10_993 nanoseconds. + Weight::from_ref_time(11_208_000) + .saturating_add(Weight::from_proof_size(1489)) + // Standard Error: 1_386 + .saturating_add(Weight::from_ref_time(1_072_330).saturating_mul(n.into())) .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) .saturating_add(T::DbWeight::get().writes((1_u64).saturating_mul(n.into()))) @@ -104,11 +104,11 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 605 nanoseconds. - Weight::from_ref_time(1_102_112) + // Minimum execution time: 740 nanoseconds. + Weight::from_ref_time(770_000) .saturating_add(Weight::from_proof_size(0)) - // Standard Error: 28 - .saturating_add(Weight::from_ref_time(120_597).saturating_mul(i.into())) + // Standard Error: 24 + .saturating_add(Weight::from_ref_time(96_434).saturating_mul(i.into())) } /// Storage: Glutton TrashData (r:5000 w:0) /// Proof: Glutton TrashData (max_values: Some(65000), max_size: Some(1036), added: 3016, mode: MaxEncodedLen) @@ -116,12 +116,12 @@ impl WeightInfo for SubstrateWeight { fn waste_proof_size_some(i: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `119036 + i * (1053 ±0)` - // Estimated: `0 + i * (3016 ±0)` - // Minimum execution time: 464 nanoseconds. - Weight::from_ref_time(552_000) - .saturating_add(Weight::from_proof_size(0)) - // Standard Error: 1_962 - .saturating_add(Weight::from_ref_time(5_780_304).saturating_mul(i.into())) + // Estimated: `990 + i * (3016 ±0)` + // Minimum execution time: 630 nanoseconds. + Weight::from_ref_time(712_000) + .saturating_add(Weight::from_proof_size(990)) + // Standard Error: 4_326 + .saturating_add(Weight::from_ref_time(5_500_880).saturating_mul(i.into())) .saturating_add(T::DbWeight::get().reads((1_u64).saturating_mul(i.into()))) .saturating_add(Weight::from_proof_size(3016).saturating_mul(i.into())) } @@ -129,31 +129,31 @@ impl WeightInfo for SubstrateWeight { /// Proof: Glutton Storage (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) /// Storage: Glutton Compute (r:1 w:0) /// Proof: Glutton Compute (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) - /// Storage: Glutton TrashData (r:1738 w:0) + /// Storage: Glutton TrashData (r:1737 w:0) /// Proof: Glutton TrashData (max_values: Some(65000), max_size: Some(1036), added: 3016, mode: MaxEncodedLen) fn on_idle_high_proof_waste() -> Weight { // Proof Size summary in bytes: - // Measured: `1955391` - // Estimated: `5242806` - // Minimum execution time: 55_398_405 nanoseconds. - Weight::from_ref_time(55_594_848_000) - .saturating_add(Weight::from_proof_size(5242806)) - .saturating_add(T::DbWeight::get().reads(1740_u64)) + // Measured: `1954313` + // Estimated: `5242760` + // Minimum execution time: 56_743_236 nanoseconds. + Weight::from_ref_time(57_088_040_000) + .saturating_add(Weight::from_proof_size(5242760)) + .saturating_add(T::DbWeight::get().reads(1739_u64)) } /// Storage: Glutton Storage (r:1 w:0) /// Proof: Glutton Storage (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) /// Storage: Glutton Compute (r:1 w:0) /// Proof: Glutton Compute (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) - /// Storage: Glutton TrashData (r:6 w:0) + /// Storage: Glutton TrashData (r:5 w:0) /// Proof: Glutton TrashData (max_values: Some(65000), max_size: Some(1036), added: 3016, mode: MaxEncodedLen) fn on_idle_low_proof_waste() -> Weight { // Proof Size summary in bytes: - // Measured: `11717` - // Estimated: `19094` - // Minimum execution time: 98_888_667 nanoseconds. - Weight::from_ref_time(99_157_239_000) - .saturating_add(Weight::from_proof_size(19094)) - .saturating_add(T::DbWeight::get().reads(8_u64)) + // Measured: `9671` + // Estimated: `19048` + // Minimum execution time: 100_387_042 nanoseconds. + Weight::from_ref_time(100_987_577_000) + .saturating_add(Weight::from_proof_size(19048)) + .saturating_add(T::DbWeight::get().reads(7_u64)) } /// Storage: Glutton Storage (r:1 w:0) /// Proof: Glutton Storage (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) @@ -162,10 +162,10 @@ impl WeightInfo for SubstrateWeight { fn empty_on_idle() -> Weight { // Proof Size summary in bytes: // Measured: `4` - // Estimated: `998` - // Minimum execution time: 3_967 nanoseconds. - Weight::from_ref_time(4_121_000) - .saturating_add(Weight::from_proof_size(998)) + // Estimated: `2978` + // Minimum execution time: 4_256 nanoseconds. + Weight::from_ref_time(4_447_000) + .saturating_add(Weight::from_proof_size(2978)) .saturating_add(T::DbWeight::get().reads(2_u64)) } /// Storage: Glutton Compute (r:0 w:1) @@ -174,8 +174,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 7_112 nanoseconds. - Weight::from_ref_time(7_484_000) + // Minimum execution time: 8_663 nanoseconds. + Weight::from_ref_time(8_864_000) .saturating_add(Weight::from_proof_size(0)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -185,8 +185,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 7_056 nanoseconds. - Weight::from_ref_time(7_414_000) + // Minimum execution time: 8_653 nanoseconds. + Weight::from_ref_time(8_998_000) .saturating_add(Weight::from_proof_size(0)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -202,12 +202,12 @@ impl WeightInfo for () { fn initialize_pallet_grow(n: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `4` - // Estimated: `499` - // Minimum execution time: 9_248 nanoseconds. - Weight::from_ref_time(9_582_000) - .saturating_add(Weight::from_proof_size(499)) - // Standard Error: 1_144 - .saturating_add(Weight::from_ref_time(1_624_225).saturating_mul(n.into())) + // Estimated: `1489` + // Minimum execution time: 10_218 nanoseconds. + Weight::from_ref_time(10_510_000) + .saturating_add(Weight::from_proof_size(1489)) + // Standard Error: 1_582 + .saturating_add(Weight::from_ref_time(1_577_660).saturating_mul(n.into())) .saturating_add(RocksDbWeight::get().reads(1_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) .saturating_add(RocksDbWeight::get().writes((1_u64).saturating_mul(n.into()))) @@ -220,12 +220,12 @@ impl WeightInfo for () { fn initialize_pallet_shrink(n: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `65` - // Estimated: `499` - // Minimum execution time: 10_210 nanoseconds. - Weight::from_ref_time(7_260_854) - .saturating_add(Weight::from_proof_size(499)) - // Standard Error: 1_459 - .saturating_add(Weight::from_ref_time(1_053_844).saturating_mul(n.into())) + // Estimated: `1489` + // Minimum execution time: 10_993 nanoseconds. + Weight::from_ref_time(11_208_000) + .saturating_add(Weight::from_proof_size(1489)) + // Standard Error: 1_386 + .saturating_add(Weight::from_ref_time(1_072_330).saturating_mul(n.into())) .saturating_add(RocksDbWeight::get().reads(1_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) .saturating_add(RocksDbWeight::get().writes((1_u64).saturating_mul(n.into()))) @@ -235,11 +235,11 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 605 nanoseconds. - Weight::from_ref_time(1_102_112) + // Minimum execution time: 740 nanoseconds. + Weight::from_ref_time(770_000) .saturating_add(Weight::from_proof_size(0)) - // Standard Error: 28 - .saturating_add(Weight::from_ref_time(120_597).saturating_mul(i.into())) + // Standard Error: 24 + .saturating_add(Weight::from_ref_time(96_434).saturating_mul(i.into())) } /// Storage: Glutton TrashData (r:5000 w:0) /// Proof: Glutton TrashData (max_values: Some(65000), max_size: Some(1036), added: 3016, mode: MaxEncodedLen) @@ -247,12 +247,12 @@ impl WeightInfo for () { fn waste_proof_size_some(i: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `119036 + i * (1053 ±0)` - // Estimated: `0 + i * (3016 ±0)` - // Minimum execution time: 464 nanoseconds. - Weight::from_ref_time(552_000) - .saturating_add(Weight::from_proof_size(0)) - // Standard Error: 1_962 - .saturating_add(Weight::from_ref_time(5_780_304).saturating_mul(i.into())) + // Estimated: `990 + i * (3016 ±0)` + // Minimum execution time: 630 nanoseconds. + Weight::from_ref_time(712_000) + .saturating_add(Weight::from_proof_size(990)) + // Standard Error: 4_326 + .saturating_add(Weight::from_ref_time(5_500_880).saturating_mul(i.into())) .saturating_add(RocksDbWeight::get().reads((1_u64).saturating_mul(i.into()))) .saturating_add(Weight::from_proof_size(3016).saturating_mul(i.into())) } @@ -260,31 +260,31 @@ impl WeightInfo for () { /// Proof: Glutton Storage (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) /// Storage: Glutton Compute (r:1 w:0) /// Proof: Glutton Compute (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) - /// Storage: Glutton TrashData (r:1738 w:0) + /// Storage: Glutton TrashData (r:1737 w:0) /// Proof: Glutton TrashData (max_values: Some(65000), max_size: Some(1036), added: 3016, mode: MaxEncodedLen) fn on_idle_high_proof_waste() -> Weight { // Proof Size summary in bytes: - // Measured: `1955391` - // Estimated: `5242806` - // Minimum execution time: 55_398_405 nanoseconds. - Weight::from_ref_time(55_594_848_000) - .saturating_add(Weight::from_proof_size(5242806)) - .saturating_add(RocksDbWeight::get().reads(1740_u64)) + // Measured: `1954313` + // Estimated: `5242760` + // Minimum execution time: 56_743_236 nanoseconds. + Weight::from_ref_time(57_088_040_000) + .saturating_add(Weight::from_proof_size(5242760)) + .saturating_add(RocksDbWeight::get().reads(1739_u64)) } /// Storage: Glutton Storage (r:1 w:0) /// Proof: Glutton Storage (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) /// Storage: Glutton Compute (r:1 w:0) /// Proof: Glutton Compute (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) - /// Storage: Glutton TrashData (r:6 w:0) + /// Storage: Glutton TrashData (r:5 w:0) /// Proof: Glutton TrashData (max_values: Some(65000), max_size: Some(1036), added: 3016, mode: MaxEncodedLen) fn on_idle_low_proof_waste() -> Weight { // Proof Size summary in bytes: - // Measured: `11717` - // Estimated: `19094` - // Minimum execution time: 98_888_667 nanoseconds. - Weight::from_ref_time(99_157_239_000) - .saturating_add(Weight::from_proof_size(19094)) - .saturating_add(RocksDbWeight::get().reads(8_u64)) + // Measured: `9671` + // Estimated: `19048` + // Minimum execution time: 100_387_042 nanoseconds. + Weight::from_ref_time(100_987_577_000) + .saturating_add(Weight::from_proof_size(19048)) + .saturating_add(RocksDbWeight::get().reads(7_u64)) } /// Storage: Glutton Storage (r:1 w:0) /// Proof: Glutton Storage (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) @@ -293,10 +293,10 @@ impl WeightInfo for () { fn empty_on_idle() -> Weight { // Proof Size summary in bytes: // Measured: `4` - // Estimated: `998` - // Minimum execution time: 3_967 nanoseconds. - Weight::from_ref_time(4_121_000) - .saturating_add(Weight::from_proof_size(998)) + // Estimated: `2978` + // Minimum execution time: 4_256 nanoseconds. + Weight::from_ref_time(4_447_000) + .saturating_add(Weight::from_proof_size(2978)) .saturating_add(RocksDbWeight::get().reads(2_u64)) } /// Storage: Glutton Compute (r:0 w:1) @@ -305,8 +305,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 7_112 nanoseconds. - Weight::from_ref_time(7_484_000) + // Minimum execution time: 8_663 nanoseconds. + Weight::from_ref_time(8_864_000) .saturating_add(Weight::from_proof_size(0)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } @@ -316,8 +316,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 7_056 nanoseconds. - Weight::from_ref_time(7_414_000) + // Minimum execution time: 8_653 nanoseconds. + Weight::from_ref_time(8_998_000) .saturating_add(Weight::from_proof_size(0)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } diff --git a/frame/message-queue/src/weights.rs b/frame/message-queue/src/weights.rs index b48cf811a..3421c2f2f 100644 --- a/frame/message-queue/src/weights.rs +++ b/frame/message-queue/src/weights.rs @@ -18,7 +18,7 @@ //! Autogenerated weights for pallet_message_queue //! //! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev -//! DATE: 2023-02-24, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! DATE: 2023-02-27, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` //! WORST CASE MAP SIZE: `1000000` //! HOSTNAME: `bm3`, CPU: `Intel(R) Core(TM) i7-7700K CPU @ 4.20GHz` //! EXECUTION: Some(Wasm), WASM-EXECUTION: Compiled, CHAIN: Some("dev"), DB CACHE: 1024 @@ -70,11 +70,11 @@ impl WeightInfo for SubstrateWeight { /// Proof: MessageQueue BookStateFor (max_values: None, max_size: Some(49), added: 2524, mode: MaxEncodedLen) fn ready_ring_knit() -> Weight { // Proof Size summary in bytes: - // Measured: `829` - // Estimated: `5547` - // Minimum execution time: 15_241 nanoseconds. - Weight::from_ref_time(15_603_000) - .saturating_add(Weight::from_proof_size(5547)) + // Measured: `295` + // Estimated: `7527` + // Minimum execution time: 12_538 nanoseconds. + Weight::from_ref_time(12_799_000) + .saturating_add(Weight::from_proof_size(7527)) .saturating_add(T::DbWeight::get().reads(3_u64)) .saturating_add(T::DbWeight::get().writes(2_u64)) } @@ -84,11 +84,11 @@ impl WeightInfo for SubstrateWeight { /// Proof: MessageQueue ServiceHead (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) fn ready_ring_unknit() -> Weight { // Proof Size summary in bytes: - // Measured: `829` - // Estimated: `5547` - // Minimum execution time: 14_652 nanoseconds. - Weight::from_ref_time(14_983_000) - .saturating_add(Weight::from_proof_size(5547)) + // Measured: `295` + // Estimated: `7527` + // Minimum execution time: 11_727 nanoseconds. + Weight::from_ref_time(12_177_000) + .saturating_add(Weight::from_proof_size(7527)) .saturating_add(T::DbWeight::get().reads(3_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) } @@ -96,11 +96,11 @@ impl WeightInfo for SubstrateWeight { /// Proof: MessageQueue BookStateFor (max_values: None, max_size: Some(49), added: 2524, mode: MaxEncodedLen) fn service_queue_base() -> Weight { // Proof Size summary in bytes: - // Measured: `576` - // Estimated: `2524` - // Minimum execution time: 5_750 nanoseconds. - Weight::from_ref_time(6_003_000) - .saturating_add(Weight::from_proof_size(2524)) + // Measured: `42` + // Estimated: `3514` + // Minimum execution time: 4_983 nanoseconds. + Weight::from_ref_time(5_174_000) + .saturating_add(Weight::from_proof_size(3514)) .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -108,11 +108,11 @@ impl WeightInfo for SubstrateWeight { /// Proof: MessageQueue Pages (max_values: None, max_size: Some(65584), added: 68059, mode: MaxEncodedLen) fn service_page_base_completion() -> Weight { // Proof Size summary in bytes: - // Measured: `647` - // Estimated: `68059` - // Minimum execution time: 8_257 nanoseconds. - Weight::from_ref_time(8_506_000) - .saturating_add(Weight::from_proof_size(68059)) + // Measured: `113` + // Estimated: `69049` + // Minimum execution time: 6_299 nanoseconds. + Weight::from_ref_time(6_670_000) + .saturating_add(Weight::from_proof_size(69049)) .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -120,20 +120,20 @@ impl WeightInfo for SubstrateWeight { /// Proof: MessageQueue Pages (max_values: None, max_size: Some(65584), added: 68059, mode: MaxEncodedLen) fn service_page_base_no_completion() -> Weight { // Proof Size summary in bytes: - // Measured: `647` - // Estimated: `68059` - // Minimum execution time: 8_422 nanoseconds. - Weight::from_ref_time(8_589_000) - .saturating_add(Weight::from_proof_size(68059)) + // Measured: `113` + // Estimated: `69049` + // Minimum execution time: 6_762 nanoseconds. + Weight::from_ref_time(7_059_000) + .saturating_add(Weight::from_proof_size(69049)) .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } fn service_page_item() -> Weight { // Proof Size summary in bytes: - // Measured: `972` + // Measured: `0` // Estimated: `0` - // Minimum execution time: 81_929 nanoseconds. - Weight::from_ref_time(82_375_000) + // Minimum execution time: 72_681 nanoseconds. + Weight::from_ref_time(73_147_000) .saturating_add(Weight::from_proof_size(0)) } /// Storage: MessageQueue ServiceHead (r:1 w:1) @@ -142,11 +142,11 @@ impl WeightInfo for SubstrateWeight { /// Proof: MessageQueue BookStateFor (max_values: None, max_size: Some(49), added: 2524, mode: MaxEncodedLen) fn bump_service_head() -> Weight { // Proof Size summary in bytes: - // Measured: `706` - // Estimated: `3023` - // Minimum execution time: 8_992 nanoseconds. - Weight::from_ref_time(9_200_000) - .saturating_add(Weight::from_proof_size(3023)) + // Measured: `172` + // Estimated: `5003` + // Minimum execution time: 7_066 nanoseconds. + Weight::from_ref_time(7_214_000) + .saturating_add(Weight::from_proof_size(5003)) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -156,11 +156,11 @@ impl WeightInfo for SubstrateWeight { /// Proof: MessageQueue Pages (max_values: None, max_size: Some(65584), added: 68059, mode: MaxEncodedLen) fn reap_page() -> Weight { // Proof Size summary in bytes: - // Measured: `66825` - // Estimated: `70583` - // Minimum execution time: 68_292 nanoseconds. - Weight::from_ref_time(69_108_000) - .saturating_add(Weight::from_proof_size(70583)) + // Measured: `65742` + // Estimated: `72563` + // Minimum execution time: 57_778 nanoseconds. + Weight::from_ref_time(58_778_000) + .saturating_add(Weight::from_proof_size(72563)) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().writes(2_u64)) } @@ -170,11 +170,11 @@ impl WeightInfo for SubstrateWeight { /// Proof: MessageQueue Pages (max_values: None, max_size: Some(65584), added: 68059, mode: MaxEncodedLen) fn execute_overweight_page_removed() -> Weight { // Proof Size summary in bytes: - // Measured: `66825` - // Estimated: `70583` - // Minimum execution time: 83_855 nanoseconds. - Weight::from_ref_time(84_946_000) - .saturating_add(Weight::from_proof_size(70583)) + // Measured: `65742` + // Estimated: `72563` + // Minimum execution time: 72_144 nanoseconds. + Weight::from_ref_time(72_942_000) + .saturating_add(Weight::from_proof_size(72563)) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().writes(2_u64)) } @@ -184,11 +184,11 @@ impl WeightInfo for SubstrateWeight { /// Proof: MessageQueue Pages (max_values: None, max_size: Some(65584), added: 68059, mode: MaxEncodedLen) fn execute_overweight_page_updated() -> Weight { // Proof Size summary in bytes: - // Measured: `66825` - // Estimated: `70583` - // Minimum execution time: 96_997 nanoseconds. - Weight::from_ref_time(98_668_000) - .saturating_add(Weight::from_proof_size(70583)) + // Measured: `65742` + // Estimated: `72563` + // Minimum execution time: 84_890 nanoseconds. + Weight::from_ref_time(86_073_000) + .saturating_add(Weight::from_proof_size(72563)) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().writes(2_u64)) } @@ -202,11 +202,11 @@ impl WeightInfo for () { /// Proof: MessageQueue BookStateFor (max_values: None, max_size: Some(49), added: 2524, mode: MaxEncodedLen) fn ready_ring_knit() -> Weight { // Proof Size summary in bytes: - // Measured: `829` - // Estimated: `5547` - // Minimum execution time: 15_241 nanoseconds. - Weight::from_ref_time(15_603_000) - .saturating_add(Weight::from_proof_size(5547)) + // Measured: `295` + // Estimated: `7527` + // Minimum execution time: 12_538 nanoseconds. + Weight::from_ref_time(12_799_000) + .saturating_add(Weight::from_proof_size(7527)) .saturating_add(RocksDbWeight::get().reads(3_u64)) .saturating_add(RocksDbWeight::get().writes(2_u64)) } @@ -216,11 +216,11 @@ impl WeightInfo for () { /// Proof: MessageQueue ServiceHead (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) fn ready_ring_unknit() -> Weight { // Proof Size summary in bytes: - // Measured: `829` - // Estimated: `5547` - // Minimum execution time: 14_652 nanoseconds. - Weight::from_ref_time(14_983_000) - .saturating_add(Weight::from_proof_size(5547)) + // Measured: `295` + // Estimated: `7527` + // Minimum execution time: 11_727 nanoseconds. + Weight::from_ref_time(12_177_000) + .saturating_add(Weight::from_proof_size(7527)) .saturating_add(RocksDbWeight::get().reads(3_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) } @@ -228,11 +228,11 @@ impl WeightInfo for () { /// Proof: MessageQueue BookStateFor (max_values: None, max_size: Some(49), added: 2524, mode: MaxEncodedLen) fn service_queue_base() -> Weight { // Proof Size summary in bytes: - // Measured: `576` - // Estimated: `2524` - // Minimum execution time: 5_750 nanoseconds. - Weight::from_ref_time(6_003_000) - .saturating_add(Weight::from_proof_size(2524)) + // Measured: `42` + // Estimated: `3514` + // Minimum execution time: 4_983 nanoseconds. + Weight::from_ref_time(5_174_000) + .saturating_add(Weight::from_proof_size(3514)) .saturating_add(RocksDbWeight::get().reads(1_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } @@ -240,11 +240,11 @@ impl WeightInfo for () { /// Proof: MessageQueue Pages (max_values: None, max_size: Some(65584), added: 68059, mode: MaxEncodedLen) fn service_page_base_completion() -> Weight { // Proof Size summary in bytes: - // Measured: `647` - // Estimated: `68059` - // Minimum execution time: 8_257 nanoseconds. - Weight::from_ref_time(8_506_000) - .saturating_add(Weight::from_proof_size(68059)) + // Measured: `113` + // Estimated: `69049` + // Minimum execution time: 6_299 nanoseconds. + Weight::from_ref_time(6_670_000) + .saturating_add(Weight::from_proof_size(69049)) .saturating_add(RocksDbWeight::get().reads(1_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } @@ -252,20 +252,20 @@ impl WeightInfo for () { /// Proof: MessageQueue Pages (max_values: None, max_size: Some(65584), added: 68059, mode: MaxEncodedLen) fn service_page_base_no_completion() -> Weight { // Proof Size summary in bytes: - // Measured: `647` - // Estimated: `68059` - // Minimum execution time: 8_422 nanoseconds. - Weight::from_ref_time(8_589_000) - .saturating_add(Weight::from_proof_size(68059)) + // Measured: `113` + // Estimated: `69049` + // Minimum execution time: 6_762 nanoseconds. + Weight::from_ref_time(7_059_000) + .saturating_add(Weight::from_proof_size(69049)) .saturating_add(RocksDbWeight::get().reads(1_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } fn service_page_item() -> Weight { // Proof Size summary in bytes: - // Measured: `972` + // Measured: `0` // Estimated: `0` - // Minimum execution time: 81_929 nanoseconds. - Weight::from_ref_time(82_375_000) + // Minimum execution time: 72_681 nanoseconds. + Weight::from_ref_time(73_147_000) .saturating_add(Weight::from_proof_size(0)) } /// Storage: MessageQueue ServiceHead (r:1 w:1) @@ -274,11 +274,11 @@ impl WeightInfo for () { /// Proof: MessageQueue BookStateFor (max_values: None, max_size: Some(49), added: 2524, mode: MaxEncodedLen) fn bump_service_head() -> Weight { // Proof Size summary in bytes: - // Measured: `706` - // Estimated: `3023` - // Minimum execution time: 8_992 nanoseconds. - Weight::from_ref_time(9_200_000) - .saturating_add(Weight::from_proof_size(3023)) + // Measured: `172` + // Estimated: `5003` + // Minimum execution time: 7_066 nanoseconds. + Weight::from_ref_time(7_214_000) + .saturating_add(Weight::from_proof_size(5003)) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } @@ -288,11 +288,11 @@ impl WeightInfo for () { /// Proof: MessageQueue Pages (max_values: None, max_size: Some(65584), added: 68059, mode: MaxEncodedLen) fn reap_page() -> Weight { // Proof Size summary in bytes: - // Measured: `66825` - // Estimated: `70583` - // Minimum execution time: 68_292 nanoseconds. - Weight::from_ref_time(69_108_000) - .saturating_add(Weight::from_proof_size(70583)) + // Measured: `65742` + // Estimated: `72563` + // Minimum execution time: 57_778 nanoseconds. + Weight::from_ref_time(58_778_000) + .saturating_add(Weight::from_proof_size(72563)) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().writes(2_u64)) } @@ -302,11 +302,11 @@ impl WeightInfo for () { /// Proof: MessageQueue Pages (max_values: None, max_size: Some(65584), added: 68059, mode: MaxEncodedLen) fn execute_overweight_page_removed() -> Weight { // Proof Size summary in bytes: - // Measured: `66825` - // Estimated: `70583` - // Minimum execution time: 83_855 nanoseconds. - Weight::from_ref_time(84_946_000) - .saturating_add(Weight::from_proof_size(70583)) + // Measured: `65742` + // Estimated: `72563` + // Minimum execution time: 72_144 nanoseconds. + Weight::from_ref_time(72_942_000) + .saturating_add(Weight::from_proof_size(72563)) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().writes(2_u64)) } @@ -316,11 +316,11 @@ impl WeightInfo for () { /// Proof: MessageQueue Pages (max_values: None, max_size: Some(65584), added: 68059, mode: MaxEncodedLen) fn execute_overweight_page_updated() -> Weight { // Proof Size summary in bytes: - // Measured: `66825` - // Estimated: `70583` - // Minimum execution time: 96_997 nanoseconds. - Weight::from_ref_time(98_668_000) - .saturating_add(Weight::from_proof_size(70583)) + // Measured: `65742` + // Estimated: `72563` + // Minimum execution time: 84_890 nanoseconds. + Weight::from_ref_time(86_073_000) + .saturating_add(Weight::from_proof_size(72563)) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().writes(2_u64)) } diff --git a/frame/support/procedural/src/benchmark.rs b/frame/support/procedural/src/benchmark.rs index 1aca2f87b..4f9994b6c 100644 --- a/frame/support/procedural/src/benchmark.rs +++ b/frame/support/procedural/src/benchmark.rs @@ -567,7 +567,12 @@ pub fn benchmarks( #support::storage::transactional::TRANSACTION_LEVEL_KEY.into(), ); whitelist.push(transactional_layer_key); - #krate::benchmarking::set_whitelist(whitelist); + // Whitelist the `:extrinsic_index`. + let extrinsic_index = #krate::TrackedStorageKey::new( + #krate::well_known_keys::EXTRINSIC_INDEX.into() + ); + whitelist.push(extrinsic_index); + #krate::benchmarking::set_whitelist(whitelist.clone()); let mut results: #krate::Vec<#krate::BenchmarkResult> = #krate::Vec::new(); // Always do at least one internal repeat... @@ -590,6 +595,12 @@ pub fn benchmarks( // This will enable worst case scenario for reading from the database. #krate::benchmarking::commit_db(); + // Access all whitelisted keys to get them into the proof recorder since the + // recorder does now have a whitelist. + for key in &whitelist { + #krate::frame_support::storage::unhashed::get_raw(&key.key); + } + // Reset the read/write counter so we don't count operations in the setup process. #krate::benchmarking::reset_read_write_count(); diff --git a/utils/frame/benchmarking-cli/src/pallet/mod.rs b/utils/frame/benchmarking-cli/src/pallet/mod.rs index 9ae984674..f21456905 100644 --- a/utils/frame/benchmarking-cli/src/pallet/mod.rs +++ b/utils/frame/benchmarking-cli/src/pallet/mod.rs @@ -187,7 +187,7 @@ pub struct PalletCmd { /// Each layer will result in an additional 495 bytes PoV per distinct top-level access. /// Therefore multiple `StorageMap` accesses only suffer from this increase once. The exact /// number of storage items depends on the runtime and the deployed pallets. - #[clap(long, default_value = "0")] + #[clap(long, default_value = "2")] pub additional_trie_layers: u8, /// A path to a `.json` file with existing benchmark results generated with `--json` or From b7e0518966b145f22c16137da5c9796fcdb43b73 Mon Sep 17 00:00:00 2001 From: Davide Galassi Date: Tue, 28 Feb 2023 15:56:22 +0100 Subject: [PATCH 173/558] Move BEEFY code to consensus (#13484) * Move beefy primitives to consensus dir * Move beefy gadget to client consensus folder * Rename beefy crates --- Cargo.lock | 174 +++++++++--------- Cargo.toml | 6 +- client/beefy/Cargo.toml | 49 ----- client/consensus/beefy/Cargo.toml | 49 +++++ client/{ => consensus}/beefy/README.md | 0 client/{ => consensus}/beefy/rpc/Cargo.toml | 18 +- client/{ => consensus}/beefy/rpc/src/lib.rs | 12 +- .../beefy/rpc/src/notification.rs | 4 +- .../{ => consensus}/beefy/src/aux_schema.rs | 0 .../beefy/src/communication/gossip.rs | 4 +- .../beefy/src/communication/mod.rs | 0 .../beefy/src/communication/notification.rs | 0 .../beefy/src/communication/peers.rs | 0 .../incoming_requests_handler.rs | 2 +- .../src/communication/request_response/mod.rs | 0 .../outgoing_requests_engine.rs | 2 +- client/{ => consensus}/beefy/src/error.rs | 0 client/{ => consensus}/beefy/src/import.rs | 2 +- .../beefy/src/justification.rs | 10 +- client/{ => consensus}/beefy/src/keystore.rs | 8 +- client/{ => consensus}/beefy/src/lib.rs | 8 +- client/{ => consensus}/beefy/src/metrics.rs | 0 client/{ => consensus}/beefy/src/round.rs | 8 +- client/{ => consensus}/beefy/src/tests.rs | 18 +- client/{ => consensus}/beefy/src/worker.rs | 20 +- client/merkle-mountain-range/Cargo.toml | 4 +- client/merkle-mountain-range/src/lib.rs | 2 +- .../merkle-mountain-range/src/offchain_mmr.rs | 2 +- frame/beefy-mmr/Cargo.toml | 4 +- frame/beefy-mmr/src/lib.rs | 18 +- frame/beefy-mmr/src/mock.rs | 6 +- frame/beefy-mmr/src/tests.rs | 4 +- frame/beefy/Cargo.toml | 4 +- frame/beefy/src/equivocation.rs | 4 +- frame/beefy/src/lib.rs | 12 +- frame/beefy/src/mock.rs | 2 +- frame/beefy/src/tests.rs | 20 +- primitives/{ => consensus}/beefy/Cargo.toml | 18 +- .../{ => consensus}/beefy/src/commitment.rs | 0 primitives/{ => consensus}/beefy/src/lib.rs | 0 primitives/{ => consensus}/beefy/src/mmr.rs | 0 .../{ => consensus}/beefy/src/payload.rs | 0 .../{ => consensus}/beefy/src/test_utils.rs | 0 .../{ => consensus}/beefy/src/witness.rs | 0 .../beefy/test-res/large-raw-commitment | Bin test-utils/runtime/Cargo.toml | 4 +- test-utils/runtime/src/lib.rs | 24 +-- 47 files changed, 260 insertions(+), 262 deletions(-) delete mode 100644 client/beefy/Cargo.toml create mode 100644 client/consensus/beefy/Cargo.toml rename client/{ => consensus}/beefy/README.md (100%) rename client/{ => consensus}/beefy/rpc/Cargo.toml (60%) rename client/{ => consensus}/beefy/rpc/src/lib.rs (97%) rename client/{ => consensus}/beefy/rpc/src/notification.rs (90%) rename client/{ => consensus}/beefy/src/aux_schema.rs (100%) rename client/{ => consensus}/beefy/src/communication/gossip.rs (99%) rename client/{ => consensus}/beefy/src/communication/mod.rs (100%) rename client/{ => consensus}/beefy/src/communication/notification.rs (100%) rename client/{ => consensus}/beefy/src/communication/peers.rs (100%) rename client/{ => consensus}/beefy/src/communication/request_response/incoming_requests_handler.rs (99%) rename client/{ => consensus}/beefy/src/communication/request_response/mod.rs (100%) rename client/{ => consensus}/beefy/src/communication/request_response/outgoing_requests_engine.rs (99%) rename client/{ => consensus}/beefy/src/error.rs (100%) rename client/{ => consensus}/beefy/src/import.rs (99%) rename client/{ => consensus}/beefy/src/justification.rs (98%) rename client/{ => consensus}/beefy/src/keystore.rs (97%) rename client/{ => consensus}/beefy/src/lib.rs (99%) rename client/{ => consensus}/beefy/src/metrics.rs (100%) rename client/{ => consensus}/beefy/src/round.rs (99%) rename client/{ => consensus}/beefy/src/tests.rs (99%) rename client/{ => consensus}/beefy/src/worker.rs (99%) rename primitives/{ => consensus}/beefy/Cargo.toml (84%) rename primitives/{ => consensus}/beefy/src/commitment.rs (100%) rename primitives/{ => consensus}/beefy/src/lib.rs (100%) rename primitives/{ => consensus}/beefy/src/mmr.rs (100%) rename primitives/{ => consensus}/beefy/src/payload.rs (100%) rename primitives/{ => consensus}/beefy/src/test_utils.rs (100%) rename primitives/{ => consensus}/beefy/src/witness.rs (100%) rename primitives/{ => consensus}/beefy/test-res/large-raw-commitment (100%) diff --git a/Cargo.lock b/Cargo.lock index 91ae9ab42..a29213b45 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -493,69 +493,6 @@ dependencies = [ "serde", ] -[[package]] -name = "beefy-gadget" -version = "4.0.0-dev" -dependencies = [ - "array-bytes", - "async-trait", - "fnv", - "futures", - "log", - "parity-scale-codec", - "parking_lot 0.12.1", - "sc-block-builder", - "sc-client-api", - "sc-consensus", - "sc-keystore", - "sc-network", - "sc-network-common", - "sc-network-gossip", - "sc-network-test", - "sc-utils", - "serde", - "sp-api", - "sp-application-crypto", - "sp-arithmetic", - "sp-beefy", - "sp-blockchain", - "sp-consensus", - "sp-consensus-grandpa", - "sp-core", - "sp-keyring", - "sp-keystore", - "sp-mmr-primitives", - "sp-runtime", - "sp-tracing", - "substrate-prometheus-endpoint", - "substrate-test-runtime-client", - "tempfile", - "thiserror", - "tokio", - "wasm-timer", -] - -[[package]] -name = "beefy-gadget-rpc" -version = "4.0.0-dev" -dependencies = [ - "beefy-gadget", - "futures", - "jsonrpsee", - "log", - "parity-scale-codec", - "parking_lot 0.12.1", - "sc-rpc", - "serde", - "serde_json", - "sp-beefy", - "sp-core", - "sp-runtime", - "substrate-test-runtime-client", - "thiserror", - "tokio", -] - [[package]] name = "binary-merkle-tree" version = "4.0.0-dev" @@ -4492,9 +4429,9 @@ dependencies = [ "sc-client-api", "sc-offchain", "sp-api", - "sp-beefy", "sp-blockchain", "sp-consensus", + "sp-consensus-beefy", "sp-core", "sp-mmr-primitives", "sp-runtime", @@ -5555,7 +5492,7 @@ dependencies = [ "parity-scale-codec", "scale-info", "serde", - "sp-beefy", + "sp-consensus-beefy", "sp-core", "sp-io", "sp-runtime", @@ -5580,7 +5517,7 @@ dependencies = [ "scale-info", "serde", "sp-api", - "sp-beefy", + "sp-consensus-beefy", "sp-core", "sp-io", "sp-runtime", @@ -8333,6 +8270,69 @@ dependencies = [ "tokio", ] +[[package]] +name = "sc-consensus-beefy" +version = "4.0.0-dev" +dependencies = [ + "array-bytes", + "async-trait", + "fnv", + "futures", + "log", + "parity-scale-codec", + "parking_lot 0.12.1", + "sc-block-builder", + "sc-client-api", + "sc-consensus", + "sc-keystore", + "sc-network", + "sc-network-common", + "sc-network-gossip", + "sc-network-test", + "sc-utils", + "serde", + "sp-api", + "sp-application-crypto", + "sp-arithmetic", + "sp-blockchain", + "sp-consensus", + "sp-consensus-beefy", + "sp-consensus-grandpa", + "sp-core", + "sp-keyring", + "sp-keystore", + "sp-mmr-primitives", + "sp-runtime", + "sp-tracing", + "substrate-prometheus-endpoint", + "substrate-test-runtime-client", + "tempfile", + "thiserror", + "tokio", + "wasm-timer", +] + +[[package]] +name = "sc-consensus-beefy-rpc" +version = "4.0.0-dev" +dependencies = [ + "futures", + "jsonrpsee", + "log", + "parity-scale-codec", + "parking_lot 0.12.1", + "sc-consensus-beefy", + "sc-rpc", + "serde", + "serde_json", + "sp-consensus-beefy", + "sp-core", + "sp-runtime", + "substrate-test-runtime-client", + "thiserror", + "tokio", +] + [[package]] name = "sc-consensus-epochs" version = "0.10.0-dev" @@ -9829,26 +9829,6 @@ dependencies = [ "sp-std", ] -[[package]] -name = "sp-beefy" -version = "4.0.0-dev" -dependencies = [ - "array-bytes", - "lazy_static", - "parity-scale-codec", - "scale-info", - "serde", - "sp-api", - "sp-application-crypto", - "sp-core", - "sp-io", - "sp-keystore", - "sp-mmr-primitives", - "sp-runtime", - "sp-std", - "strum", -] - [[package]] name = "sp-block-builder" version = "4.0.0-dev" @@ -9934,6 +9914,26 @@ dependencies = [ "sp-timestamp", ] +[[package]] +name = "sp-consensus-beefy" +version = "4.0.0-dev" +dependencies = [ + "array-bytes", + "lazy_static", + "parity-scale-codec", + "scale-info", + "serde", + "sp-api", + "sp-application-crypto", + "sp-core", + "sp-io", + "sp-keystore", + "sp-mmr-primitives", + "sp-runtime", + "sp-std", + "strum", +] + [[package]] name = "sp-consensus-grandpa" version = "4.0.0-dev" @@ -10837,11 +10837,11 @@ dependencies = [ "serde", "sp-api", "sp-application-crypto", - "sp-beefy", "sp-block-builder", "sp-consensus", "sp-consensus-aura", "sp-consensus-babe", + "sp-consensus-beefy", "sp-consensus-grandpa", "sp-core", "sp-externalities", diff --git a/Cargo.toml b/Cargo.toml index 88c9c1998..af2d8f906 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -18,8 +18,6 @@ members = [ "client/api", "client/authority-discovery", "client/basic-authorship", - "client/beefy", - "client/beefy/rpc", "client/block-builder", "client/chain-spec", "client/chain-spec/derive", @@ -27,6 +25,8 @@ members = [ "client/consensus/aura", "client/consensus/babe", "client/consensus/babe/rpc", + "client/consensus/beefy", + "client/consensus/beefy/rpc", "client/consensus/common", "client/consensus/epochs", "client/consensus/grandpa", @@ -178,11 +178,11 @@ members = [ "primitives/arithmetic", "primitives/arithmetic/fuzzer", "primitives/authority-discovery", - "primitives/beefy", "primitives/block-builder", "primitives/blockchain", "primitives/consensus/aura", "primitives/consensus/babe", + "primitives/consensus/beefy", "primitives/consensus/common", "primitives/consensus/grandpa", "primitives/consensus/pow", diff --git a/client/beefy/Cargo.toml b/client/beefy/Cargo.toml deleted file mode 100644 index eb6168236..000000000 --- a/client/beefy/Cargo.toml +++ /dev/null @@ -1,49 +0,0 @@ -[package] -name = "beefy-gadget" -version = "4.0.0-dev" -authors = ["Parity Technologies "] -edition = "2021" -license = "GPL-3.0-or-later WITH Classpath-exception-2.0" -repository = "https://github.com/paritytech/substrate" -description = "BEEFY Client gadget for substrate" -homepage = "https://substrate.io" - -[dependencies] -array-bytes = "4.1" -async-trait = "0.1.57" -codec = { package = "parity-scale-codec", version = "3.2.2", features = ["derive"] } -fnv = "1.0.6" -futures = "0.3" -log = "0.4" -parking_lot = "0.12.1" -thiserror = "1.0" -wasm-timer = "0.2.5" -beefy-primitives = { version = "4.0.0-dev", path = "../../primitives/beefy", package = "sp-beefy" } -prometheus = { package = "substrate-prometheus-endpoint", version = "0.10.0-dev", path = "../../utils/prometheus" } -sc-client-api = { version = "4.0.0-dev", path = "../api" } -sc-consensus = { version = "0.10.0-dev", path = "../consensus/common" } -sc-keystore = { version = "4.0.0-dev", path = "../keystore" } -sc-network = { version = "0.10.0-dev", path = "../network" } -sc-network-common = { version = "0.10.0-dev", path = "../network/common" } -sc-network-gossip = { version = "0.10.0-dev", path = "../network-gossip" } -sc-utils = { version = "4.0.0-dev", path = "../utils" } -sp-api = { version = "4.0.0-dev", path = "../../primitives/api" } -sp-application-crypto = { version = "7.0.0", path = "../../primitives/application-crypto" } -sp-arithmetic = { version = "6.0.0", path = "../../primitives/arithmetic" } -sp-blockchain = { version = "4.0.0-dev", path = "../../primitives/blockchain" } -sp-consensus = { version = "0.10.0-dev", path = "../../primitives/consensus/common" } -sp-core = { version = "7.0.0", path = "../../primitives/core" } -sp-keystore = { version = "0.13.0", path = "../../primitives/keystore" } -sp-mmr-primitives = { version = "4.0.0-dev", path = "../../primitives/merkle-mountain-range" } -sp-runtime = { version = "7.0.0", path = "../../primitives/runtime" } - -[dev-dependencies] -serde = "1.0.136" -tempfile = "3.1.0" -tokio = "1.22.0" -sc-block-builder = { version = "0.10.0-dev", path = "../block-builder" } -sc-network-test = { version = "0.8.0", path = "../network/test" } -sp-consensus-grandpa = { version = "4.0.0-dev", path = "../../primitives/consensus/grandpa" } -sp-keyring = { version = "7.0.0", path = "../../primitives/keyring" } -sp-tracing = { version = "6.0.0", path = "../../primitives/tracing" } -substrate-test-runtime-client = { version = "2.0.0", path = "../../test-utils/runtime/client" } diff --git a/client/consensus/beefy/Cargo.toml b/client/consensus/beefy/Cargo.toml new file mode 100644 index 000000000..d88c07df2 --- /dev/null +++ b/client/consensus/beefy/Cargo.toml @@ -0,0 +1,49 @@ +[package] +name = "sc-consensus-beefy" +version = "4.0.0-dev" +authors = ["Parity Technologies "] +edition = "2021" +license = "GPL-3.0-or-later WITH Classpath-exception-2.0" +repository = "https://github.com/paritytech/substrate" +description = "BEEFY Client gadget for substrate" +homepage = "https://substrate.io" + +[dependencies] +array-bytes = "4.1" +async-trait = "0.1.57" +codec = { package = "parity-scale-codec", version = "3.2.2", features = ["derive"] } +fnv = "1.0.6" +futures = "0.3" +log = "0.4" +parking_lot = "0.12.1" +thiserror = "1.0" +wasm-timer = "0.2.5" +prometheus = { package = "substrate-prometheus-endpoint", version = "0.10.0-dev", path = "../../../utils/prometheus" } +sc-client-api = { version = "4.0.0-dev", path = "../../api" } +sc-consensus = { version = "0.10.0-dev", path = "../../consensus/common" } +sc-keystore = { version = "4.0.0-dev", path = "../../keystore" } +sc-network = { version = "0.10.0-dev", path = "../../network" } +sc-network-common = { version = "0.10.0-dev", path = "../../network/common" } +sc-network-gossip = { version = "0.10.0-dev", path = "../../network-gossip" } +sc-utils = { version = "4.0.0-dev", path = "../../utils" } +sp-api = { version = "4.0.0-dev", path = "../../../primitives/api" } +sp-application-crypto = { version = "7.0.0", path = "../../../primitives/application-crypto" } +sp-arithmetic = { version = "6.0.0", path = "../../../primitives/arithmetic" } +sp-blockchain = { version = "4.0.0-dev", path = "../../../primitives/blockchain" } +sp-consensus = { version = "0.10.0-dev", path = "../../../primitives/consensus/common" } +sp-consensus-beefy = { version = "4.0.0-dev", path = "../../../primitives/consensus/beefy" } +sp-core = { version = "7.0.0", path = "../../../primitives/core" } +sp-keystore = { version = "0.13.0", path = "../../../primitives/keystore" } +sp-mmr-primitives = { version = "4.0.0-dev", path = "../../../primitives/merkle-mountain-range" } +sp-runtime = { version = "7.0.0", path = "../../../primitives/runtime" } + +[dev-dependencies] +serde = "1.0.136" +tempfile = "3.1.0" +tokio = "1.22.0" +sc-block-builder = { version = "0.10.0-dev", path = "../../block-builder" } +sc-network-test = { version = "0.8.0", path = "../../network/test" } +sp-consensus-grandpa = { version = "4.0.0-dev", path = "../../../primitives/consensus/grandpa" } +sp-keyring = { version = "7.0.0", path = "../../../primitives/keyring" } +sp-tracing = { version = "6.0.0", path = "../../../primitives/tracing" } +substrate-test-runtime-client = { version = "2.0.0", path = "../../../test-utils/runtime/client" } diff --git a/client/beefy/README.md b/client/consensus/beefy/README.md similarity index 100% rename from client/beefy/README.md rename to client/consensus/beefy/README.md diff --git a/client/beefy/rpc/Cargo.toml b/client/consensus/beefy/rpc/Cargo.toml similarity index 60% rename from client/beefy/rpc/Cargo.toml rename to client/consensus/beefy/rpc/Cargo.toml index ab3e6921f..d6dfa8731 100644 --- a/client/beefy/rpc/Cargo.toml +++ b/client/consensus/beefy/rpc/Cargo.toml @@ -1,5 +1,5 @@ [package] -name = "beefy-gadget-rpc" +name = "sc-consensus-beefy-rpc" version = "4.0.0-dev" authors = ["Parity Technologies "] edition = "2021" @@ -16,16 +16,14 @@ log = "0.4" parking_lot = "0.12.1" serde = { version = "1.0.136", features = ["derive"] } thiserror = "1.0" -beefy-gadget = { version = "4.0.0-dev", path = "../." } -beefy-primitives = { version = "4.0.0-dev", path = "../../../primitives/beefy", package = "sp-beefy" } -sc-rpc = { version = "4.0.0-dev", path = "../../rpc" } -sp-core = { version = "7.0.0", path = "../../../primitives/core" } -sp-runtime = { version = "7.0.0", path = "../../../primitives/runtime" } +sc-consensus-beefy = { version = "4.0.0-dev", path = "../" } +sp-consensus-beefy = { version = "4.0.0-dev", path = "../../../../primitives/consensus/beefy" } +sc-rpc = { version = "4.0.0-dev", path = "../../../rpc" } +sp-core = { version = "7.0.0", path = "../../../../primitives/core" } +sp-runtime = { version = "7.0.0", path = "../../../../primitives/runtime" } [dev-dependencies] serde_json = "1.0.85" -sc-rpc = { version = "4.0.0-dev", features = [ - "test-helpers", -], path = "../../rpc" } -substrate-test-runtime-client = { version = "2.0.0", path = "../../../test-utils/runtime/client" } +sc-rpc = { version = "4.0.0-dev", features = ["test-helpers"], path = "../../../rpc" } +substrate-test-runtime-client = { version = "2.0.0", path = "../../../../test-utils/runtime/client" } tokio = { version = "1.22.0", features = ["macros"] } diff --git a/client/beefy/rpc/src/lib.rs b/client/consensus/beefy/rpc/src/lib.rs similarity index 97% rename from client/beefy/rpc/src/lib.rs rename to client/consensus/beefy/rpc/src/lib.rs index 0a7a4de14..f5c0ff326 100644 --- a/client/beefy/rpc/src/lib.rs +++ b/client/consensus/beefy/rpc/src/lib.rs @@ -35,7 +35,7 @@ use jsonrpsee::{ }; use log::warn; -use beefy_gadget::communication::notification::{ +use sc_consensus_beefy::communication::notification::{ BeefyBestBlockStream, BeefyVersionedFinalityProofStream, }; @@ -166,13 +166,13 @@ where mod tests { use super::*; - use beefy_gadget::{ + use codec::{Decode, Encode}; + use jsonrpsee::{types::EmptyServerParams as EmptyParams, RpcModule}; + use sc_consensus_beefy::{ communication::notification::BeefyVersionedFinalityProofSender, justification::BeefyVersionedFinalityProof, }; - use beefy_primitives::{known_payloads, Payload, SignedCommitment}; - use codec::{Decode, Encode}; - use jsonrpsee::{types::EmptyServerParams as EmptyParams, RpcModule}; + use sp_consensus_beefy::{known_payloads, Payload, SignedCommitment}; use sp_runtime::traits::{BlakeTwo256, Hash}; use substrate_test_runtime_client::runtime::Block; @@ -269,7 +269,7 @@ mod tests { let payload = Payload::from_single_entry(known_payloads::MMR_ROOT_ID, "Hello World!".encode()); BeefyVersionedFinalityProof::::V1(SignedCommitment { - commitment: beefy_primitives::Commitment { + commitment: sp_consensus_beefy::Commitment { payload, block_number: 5, validator_set_id: 0, diff --git a/client/beefy/rpc/src/notification.rs b/client/consensus/beefy/rpc/src/notification.rs similarity index 90% rename from client/beefy/rpc/src/notification.rs rename to client/consensus/beefy/rpc/src/notification.rs index 286385187..690c511b9 100644 --- a/client/beefy/rpc/src/notification.rs +++ b/client/consensus/beefy/rpc/src/notification.rs @@ -23,13 +23,13 @@ use sp_runtime::traits::Block as BlockT; /// An encoded finality proof proving that the given header has been finalized. /// The given bytes should be the SCALE-encoded representation of a -/// `beefy_primitives::VersionedFinalityProof`. +/// `sp_consensus_beefy::VersionedFinalityProof`. #[derive(Clone, Serialize, Deserialize)] pub struct EncodedVersionedFinalityProof(sp_core::Bytes); impl EncodedVersionedFinalityProof { pub fn new( - finality_proof: beefy_gadget::justification::BeefyVersionedFinalityProof, + finality_proof: sc_consensus_beefy::justification::BeefyVersionedFinalityProof, ) -> Self where Block: BlockT, diff --git a/client/beefy/src/aux_schema.rs b/client/consensus/beefy/src/aux_schema.rs similarity index 100% rename from client/beefy/src/aux_schema.rs rename to client/consensus/beefy/src/aux_schema.rs diff --git a/client/beefy/src/communication/gossip.rs b/client/consensus/beefy/src/communication/gossip.rs similarity index 99% rename from client/beefy/src/communication/gossip.rs rename to client/consensus/beefy/src/communication/gossip.rs index a1fb6fa26..219203ee4 100644 --- a/client/beefy/src/communication/gossip.rs +++ b/client/consensus/beefy/src/communication/gossip.rs @@ -29,7 +29,7 @@ use parking_lot::{Mutex, RwLock}; use wasm_timer::Instant; use crate::{communication::peers::KnownPeers, keystore::BeefyKeystore, LOG_TARGET}; -use beefy_primitives::{ +use sp_consensus_beefy::{ crypto::{Public, Signature}, VoteMessage, }; @@ -243,7 +243,7 @@ mod tests { use sp_keystore::{SyncCryptoStore, SyncCryptoStorePtr}; use crate::keystore::BeefyKeystore; - use beefy_primitives::{ + use sp_consensus_beefy::{ crypto::Signature, known_payloads, Commitment, Keyring, MmrRootHash, Payload, VoteMessage, KEY_TYPE, }; diff --git a/client/beefy/src/communication/mod.rs b/client/consensus/beefy/src/communication/mod.rs similarity index 100% rename from client/beefy/src/communication/mod.rs rename to client/consensus/beefy/src/communication/mod.rs diff --git a/client/beefy/src/communication/notification.rs b/client/consensus/beefy/src/communication/notification.rs similarity index 100% rename from client/beefy/src/communication/notification.rs rename to client/consensus/beefy/src/communication/notification.rs diff --git a/client/beefy/src/communication/peers.rs b/client/consensus/beefy/src/communication/peers.rs similarity index 100% rename from client/beefy/src/communication/peers.rs rename to client/consensus/beefy/src/communication/peers.rs diff --git a/client/beefy/src/communication/request_response/incoming_requests_handler.rs b/client/consensus/beefy/src/communication/request_response/incoming_requests_handler.rs similarity index 99% rename from client/beefy/src/communication/request_response/incoming_requests_handler.rs rename to client/consensus/beefy/src/communication/request_response/incoming_requests_handler.rs index 3266f6dba..ab6c21a18 100644 --- a/client/beefy/src/communication/request_response/incoming_requests_handler.rs +++ b/client/consensus/beefy/src/communication/request_response/incoming_requests_handler.rs @@ -16,7 +16,6 @@ //! Helper for handling (i.e. answering) BEEFY justifications requests from a remote peer. -use beefy_primitives::BEEFY_ENGINE_ID; use codec::Decode; use futures::{ channel::{mpsc, oneshot}, @@ -26,6 +25,7 @@ use log::{debug, trace}; use sc_client_api::BlockBackend; use sc_network::{config as netconfig, config::RequestResponseConfig, PeerId, ReputationChange}; use sc_network_common::protocol::ProtocolName; +use sp_consensus_beefy::BEEFY_ENGINE_ID; use sp_runtime::traits::Block; use std::{marker::PhantomData, sync::Arc}; diff --git a/client/beefy/src/communication/request_response/mod.rs b/client/consensus/beefy/src/communication/request_response/mod.rs similarity index 100% rename from client/beefy/src/communication/request_response/mod.rs rename to client/consensus/beefy/src/communication/request_response/mod.rs diff --git a/client/beefy/src/communication/request_response/outgoing_requests_engine.rs b/client/consensus/beefy/src/communication/request_response/outgoing_requests_engine.rs similarity index 99% rename from client/beefy/src/communication/request_response/outgoing_requests_engine.rs rename to client/consensus/beefy/src/communication/request_response/outgoing_requests_engine.rs index 0f4f07835..7450d4b32 100644 --- a/client/beefy/src/communication/request_response/outgoing_requests_engine.rs +++ b/client/consensus/beefy/src/communication/request_response/outgoing_requests_engine.rs @@ -18,7 +18,6 @@ //! Generating request logic for request/response protocol for syncing BEEFY justifications. -use beefy_primitives::{crypto::AuthorityId, ValidatorSet}; use codec::Encode; use futures::channel::{oneshot, oneshot::Canceled}; use log::{debug, warn}; @@ -28,6 +27,7 @@ use sc_network_common::{ request_responses::{IfDisconnected, RequestFailure}, service::NetworkRequest, }; +use sp_consensus_beefy::{crypto::AuthorityId, ValidatorSet}; use sp_runtime::traits::{Block, NumberFor}; use std::{collections::VecDeque, result::Result, sync::Arc}; diff --git a/client/beefy/src/error.rs b/client/consensus/beefy/src/error.rs similarity index 100% rename from client/beefy/src/error.rs rename to client/consensus/beefy/src/error.rs diff --git a/client/beefy/src/import.rs b/client/consensus/beefy/src/import.rs similarity index 99% rename from client/beefy/src/import.rs rename to client/consensus/beefy/src/import.rs index 44d30d5d7..177a99b10 100644 --- a/client/beefy/src/import.rs +++ b/client/consensus/beefy/src/import.rs @@ -16,8 +16,8 @@ // You should have received a copy of the GNU General Public License // along with this program. If not, see . -use beefy_primitives::{BeefyApi, BEEFY_ENGINE_ID}; use log::debug; +use sp_consensus_beefy::{BeefyApi, BEEFY_ENGINE_ID}; use std::{collections::HashMap, sync::Arc}; use sp_api::{ProvideRuntimeApi, TransactionFor}; diff --git a/client/beefy/src/justification.rs b/client/consensus/beefy/src/justification.rs similarity index 98% rename from client/beefy/src/justification.rs rename to client/consensus/beefy/src/justification.rs index 6ceb3d2f5..1bd250b2a 100644 --- a/client/beefy/src/justification.rs +++ b/client/consensus/beefy/src/justification.rs @@ -17,17 +17,17 @@ // along with this program. If not, see . use crate::keystore::BeefyKeystore; -use beefy_primitives::{ +use codec::{Decode, Encode}; +use sp_consensus::Error as ConsensusError; +use sp_consensus_beefy::{ crypto::{AuthorityId, Signature}, ValidatorSet, VersionedFinalityProof, }; -use codec::{Decode, Encode}; -use sp_consensus::Error as ConsensusError; use sp_runtime::traits::{Block as BlockT, NumberFor}; /// A finality proof with matching BEEFY authorities' signatures. pub type BeefyVersionedFinalityProof = - beefy_primitives::VersionedFinalityProof, Signature>; + sp_consensus_beefy::VersionedFinalityProof, Signature>; /// Decode and verify a Beefy FinalityProof. pub(crate) fn decode_and_verify_finality_proof( @@ -80,7 +80,7 @@ fn verify_with_validator_set( #[cfg(test)] pub(crate) mod tests { - use beefy_primitives::{ + use sp_consensus_beefy::{ known_payloads, Commitment, Keyring, Payload, SignedCommitment, VersionedFinalityProof, }; use substrate_test_runtime_client::runtime::Block; diff --git a/client/beefy/src/keystore.rs b/client/consensus/beefy/src/keystore.rs similarity index 97% rename from client/beefy/src/keystore.rs rename to client/consensus/beefy/src/keystore.rs index 3f0d8a5f3..421f71490 100644 --- a/client/beefy/src/keystore.rs +++ b/client/consensus/beefy/src/keystore.rs @@ -22,7 +22,7 @@ use sp_keystore::{SyncCryptoStore, SyncCryptoStorePtr}; use log::warn; -use beefy_primitives::{ +use sp_consensus_beefy::{ crypto::{Public, Signature}, BeefyAuthorityId, KEY_TYPE, }; @@ -89,8 +89,8 @@ impl BeefyKeystore { Ok(sig) } - /// Returns a vector of [`beefy_primitives::crypto::Public`] keys which are currently supported - /// (i.e. found in the keystore). + /// Returns a vector of [`sp_consensus_beefy::crypto::Public`] keys which are currently + /// supported (i.e. found in the keystore). pub fn public_keys(&self) -> Result, error::Error> { let store = self.0.clone().ok_or_else(|| error::Error::Keystore("no Keystore".into()))?; @@ -123,7 +123,7 @@ pub mod tests { use sc_keystore::LocalKeystore; use sp_core::{ecdsa, Pair}; - use beefy_primitives::{crypto, Keyring}; + use sp_consensus_beefy::{crypto, Keyring}; use super::*; use crate::error::Error; diff --git a/client/beefy/src/lib.rs b/client/consensus/beefy/src/lib.rs similarity index 99% rename from client/beefy/src/lib.rs rename to client/consensus/beefy/src/lib.rs index 4f5f8b443..9b627e3ff 100644 --- a/client/beefy/src/lib.rs +++ b/client/consensus/beefy/src/lib.rs @@ -32,10 +32,6 @@ use crate::{ round::Rounds, worker::PersistedState, }; -use beefy_primitives::{ - crypto::AuthorityId, BeefyApi, MmrRootHash, PayloadProvider, ValidatorSet, BEEFY_ENGINE_ID, - GENESIS_AUTHORITY_SET_ID, -}; use futures::{stream::Fuse, StreamExt}; use log::{error, info}; use parking_lot::Mutex; @@ -50,6 +46,10 @@ use sp_blockchain::{ Backend as BlockchainBackend, Error as ClientError, HeaderBackend, Result as ClientResult, }; use sp_consensus::{Error as ConsensusError, SyncOracle}; +use sp_consensus_beefy::{ + crypto::AuthorityId, BeefyApi, MmrRootHash, PayloadProvider, ValidatorSet, BEEFY_ENGINE_ID, + GENESIS_AUTHORITY_SET_ID, +}; use sp_keystore::SyncCryptoStorePtr; use sp_mmr_primitives::MmrApi; use sp_runtime::traits::{Block, Zero}; diff --git a/client/beefy/src/metrics.rs b/client/consensus/beefy/src/metrics.rs similarity index 100% rename from client/beefy/src/metrics.rs rename to client/consensus/beefy/src/metrics.rs diff --git a/client/beefy/src/round.rs b/client/consensus/beefy/src/round.rs similarity index 99% rename from client/beefy/src/round.rs rename to client/consensus/beefy/src/round.rs index 142e138f5..64d03beee 100644 --- a/client/beefy/src/round.rs +++ b/client/consensus/beefy/src/round.rs @@ -18,12 +18,12 @@ use crate::LOG_TARGET; -use beefy_primitives::{ +use codec::{Decode, Encode}; +use log::debug; +use sp_consensus_beefy::{ crypto::{AuthorityId, Public, Signature}, Commitment, EquivocationProof, SignedCommitment, ValidatorSet, ValidatorSetId, VoteMessage, }; -use codec::{Decode, Encode}; -use log::debug; use sp_runtime::traits::{Block, NumberFor}; use std::collections::BTreeMap; @@ -198,7 +198,7 @@ where mod tests { use sc_network_test::Block; - use beefy_primitives::{ + use sp_consensus_beefy::{ crypto::Public, known_payloads::MMR_ROOT_ID, Commitment, EquivocationProof, Keyring, Payload, SignedCommitment, ValidatorSet, VoteMessage, }; diff --git a/client/beefy/src/tests.rs b/client/consensus/beefy/src/tests.rs similarity index 99% rename from client/beefy/src/tests.rs rename to client/consensus/beefy/src/tests.rs index 419033efe..e4fe16a2a 100644 --- a/client/beefy/src/tests.rs +++ b/client/consensus/beefy/src/tests.rs @@ -29,14 +29,6 @@ use crate::{ load_or_init_voter_state, wait_for_runtime_pallet, BeefyRPCLinks, BeefyVoterLinks, KnownPeers, PersistedState, }; -use beefy_primitives::{ - crypto::{AuthorityId, Signature}, - known_payloads, - mmr::MmrRootProvider, - BeefyApi, Commitment, ConsensusLog, EquivocationProof, Keyring as BeefyKeyring, MmrRootHash, - OpaqueKeyOwnershipProof, Payload, SignedCommitment, ValidatorSet, ValidatorSetId, - VersionedFinalityProof, BEEFY_ENGINE_ID, KEY_TYPE as BeefyKeyType, -}; use futures::{future, stream::FuturesUnordered, Future, StreamExt}; use parking_lot::Mutex; use sc_client_api::{Backend as BackendT, BlockchainEvents, FinalityNotifications, HeaderBackend}; @@ -53,6 +45,14 @@ use sc_utils::notification::NotificationReceiver; use serde::{Deserialize, Serialize}; use sp_api::{ApiRef, ProvideRuntimeApi}; use sp_consensus::BlockOrigin; +use sp_consensus_beefy::{ + crypto::{AuthorityId, Signature}, + known_payloads, + mmr::MmrRootProvider, + BeefyApi, Commitment, ConsensusLog, EquivocationProof, Keyring as BeefyKeyring, MmrRootHash, + OpaqueKeyOwnershipProof, Payload, SignedCommitment, ValidatorSet, ValidatorSetId, + VersionedFinalityProof, BEEFY_ENGINE_ID, KEY_TYPE as BeefyKeyType, +}; use sp_core::H256; use sp_keystore::{testing::KeyStore as TestKeystore, SyncCryptoStore, SyncCryptoStorePtr}; use sp_mmr_primitives::{Error as MmrError, MmrApi}; @@ -484,7 +484,7 @@ async fn wait_for_beefy_signed_commitments( let expected = expected.next(); async move { let signed_commitment = match versioned_finality_proof { - beefy_primitives::VersionedFinalityProof::V1(sc) => sc, + sp_consensus_beefy::VersionedFinalityProof::V1(sc) => sc, }; let commitment_block_num = signed_commitment.commitment.block_number; assert_eq!(expected, Some(commitment_block_num).as_ref()); diff --git a/client/beefy/src/worker.rs b/client/consensus/beefy/src/worker.rs similarity index 99% rename from client/beefy/src/worker.rs rename to client/consensus/beefy/src/worker.rs index 439da2138..7f3114958 100644 --- a/client/beefy/src/worker.rs +++ b/client/consensus/beefy/src/worker.rs @@ -29,12 +29,6 @@ use crate::{ round::{Rounds, VoteImportResult}, BeefyVoterLinks, LOG_TARGET, }; -use beefy_primitives::{ - check_equivocation_proof, - crypto::{AuthorityId, Signature}, - BeefyApi, Commitment, ConsensusLog, EquivocationProof, PayloadProvider, ValidatorSet, - VersionedFinalityProof, VoteMessage, BEEFY_ENGINE_ID, -}; use codec::{Codec, Decode, Encode}; use futures::{stream::Fuse, FutureExt, StreamExt}; use log::{debug, error, info, log_enabled, trace, warn}; @@ -45,6 +39,12 @@ use sc_utils::notification::NotificationReceiver; use sp_api::{BlockId, ProvideRuntimeApi}; use sp_arithmetic::traits::{AtLeast32Bit, Saturating}; use sp_consensus::SyncOracle; +use sp_consensus_beefy::{ + check_equivocation_proof, + crypto::{AuthorityId, Signature}, + BeefyApi, Commitment, ConsensusLog, EquivocationProof, PayloadProvider, ValidatorSet, + VersionedFinalityProof, VoteMessage, BEEFY_ENGINE_ID, +}; use sp_runtime::{ generic::OpaqueDigestItemId, traits::{Block, ConstU32, Header, NumberFor, Zero}, @@ -1062,10 +1062,6 @@ pub(crate) mod tests { }, BeefyRPCLinks, KnownPeers, }; - use beefy_primitives::{ - generate_equivocation_proof, known_payloads, known_payloads::MMR_ROOT_ID, - mmr::MmrRootProvider, Keyring, Payload, SignedCommitment, - }; use futures::{future::poll_fn, task::Poll}; use parking_lot::Mutex; use sc_client_api::{Backend as BackendT, HeaderBackend}; @@ -1073,6 +1069,10 @@ pub(crate) mod tests { use sc_network_test::TestNetFactory; use sp_api::HeaderT; use sp_blockchain::Backend as BlockchainBackendT; + use sp_consensus_beefy::{ + generate_equivocation_proof, known_payloads, known_payloads::MMR_ROOT_ID, + mmr::MmrRootProvider, Keyring, Payload, SignedCommitment, + }; use sp_runtime::traits::One; use substrate_test_runtime_client::{ runtime::{Block, Digest, DigestItem, Header, H256}, diff --git a/client/merkle-mountain-range/Cargo.toml b/client/merkle-mountain-range/Cargo.toml index 90fb65453..899f4cc2e 100644 --- a/client/merkle-mountain-range/Cargo.toml +++ b/client/merkle-mountain-range/Cargo.toml @@ -14,10 +14,10 @@ homepage = "https://substrate.io" codec = { package = "parity-scale-codec", version = "3.2.2" } futures = "0.3" log = "0.4" -beefy-primitives = { version = "4.0.0-dev", path = "../../primitives/beefy", package = "sp-beefy" } -sc-client-api = { version = "4.0.0-dev", path = "../api" } sp-api = { version = "4.0.0-dev", path = "../../primitives/api" } sp-blockchain = { version = "4.0.0-dev", path = "../../primitives/blockchain" } +sc-client-api = { version = "4.0.0-dev", path = "../api" } +sp-consensus-beefy = { version = "4.0.0-dev", path = "../../primitives/consensus/beefy" } sp-consensus = { version = "0.10.0-dev", path = "../../primitives/consensus/common" } sp-core = { version = "7.0.0", path = "../../primitives/core" } sp-mmr-primitives = { version = "4.0.0-dev", path = "../../primitives/merkle-mountain-range" } diff --git a/client/merkle-mountain-range/src/lib.rs b/client/merkle-mountain-range/src/lib.rs index de21b9b49..e174af706 100644 --- a/client/merkle-mountain-range/src/lib.rs +++ b/client/merkle-mountain-range/src/lib.rs @@ -43,13 +43,13 @@ mod offchain_mmr; pub mod test_utils; use crate::offchain_mmr::OffchainMmr; -use beefy_primitives::MmrRootHash; use futures::StreamExt; use log::{debug, error, trace, warn}; use sc_client_api::{Backend, BlockchainEvents, FinalityNotification, FinalityNotifications}; use sc_offchain::OffchainDb; use sp_api::ProvideRuntimeApi; use sp_blockchain::{HeaderBackend, HeaderMetadata}; +use sp_consensus_beefy::MmrRootHash; use sp_mmr_primitives::{utils, LeafIndex, MmrApi}; use sp_runtime::traits::{Block, Header, NumberFor}; use std::{marker::PhantomData, sync::Arc}; diff --git a/client/merkle-mountain-range/src/offchain_mmr.rs b/client/merkle-mountain-range/src/offchain_mmr.rs index 33d85f0cb..3c3f0beb6 100644 --- a/client/merkle-mountain-range/src/offchain_mmr.rs +++ b/client/merkle-mountain-range/src/offchain_mmr.rs @@ -22,11 +22,11 @@ #![warn(missing_docs)] use crate::{aux_schema, MmrClient, LOG_TARGET}; -use beefy_primitives::MmrRootHash; use log::{debug, error, info, warn}; use sc_client_api::{Backend, FinalityNotification}; use sc_offchain::OffchainDb; use sp_blockchain::{CachedHeaderMetadata, ForkBackend}; +use sp_consensus_beefy::MmrRootHash; use sp_core::offchain::{DbExternalities, StorageKind}; use sp_mmr_primitives::{utils, utils::NodesUtils, MmrApi, NodeIndex}; use sp_runtime::{ diff --git a/frame/beefy-mmr/Cargo.toml b/frame/beefy-mmr/Cargo.toml index 1d24e821c..721e24282 100644 --- a/frame/beefy-mmr/Cargo.toml +++ b/frame/beefy-mmr/Cargo.toml @@ -15,12 +15,12 @@ log = { version = "0.4.17", default-features = false } scale-info = { version = "2.1.1", default-features = false, features = ["derive"] } serde = { version = "1.0.136", optional = true } binary-merkle-tree = { version = "4.0.0-dev", default-features = false, path = "../../utils/binary-merkle-tree" } -beefy-primitives = { version = "4.0.0-dev", default-features = false, path = "../../primitives/beefy", package = "sp-beefy" } frame-support = { version = "4.0.0-dev", default-features = false, path = "../support" } frame-system = { version = "4.0.0-dev", default-features = false, path = "../system" } pallet-beefy = { version = "4.0.0-dev", default-features = false, path = "../beefy" } pallet-mmr = { version = "4.0.0-dev", default-features = false, path = "../merkle-mountain-range" } pallet-session = { version = "4.0.0-dev", default-features = false, path = "../session" } +sp-consensus-beefy = { version = "4.0.0-dev", default-features = false, path = "../../primitives/consensus/beefy" } sp-core = { version = "7.0.0", default-features = false, path = "../../primitives/core" } sp-io = { version = "7.0.0", default-features = false, path = "../../primitives/io" } sp-runtime = { version = "7.0.0", default-features = false, path = "../../primitives/runtime" } @@ -36,7 +36,6 @@ default = ["std"] std = [ "array-bytes", "binary-merkle-tree/std", - "beefy-primitives/std", "codec/std", "frame-support/std", "frame-system/std", @@ -46,6 +45,7 @@ std = [ "pallet-session/std", "scale-info/std", "serde", + "sp-consensus-beefy/std", "sp-core/std", "sp-io/std", "sp-runtime/std", diff --git a/frame/beefy-mmr/src/lib.rs b/frame/beefy-mmr/src/lib.rs index 8bb3aaa7e..ba52be4f8 100644 --- a/frame/beefy-mmr/src/lib.rs +++ b/frame/beefy-mmr/src/lib.rs @@ -36,11 +36,11 @@ use sp_runtime::traits::{Convert, Member}; use sp_std::prelude::*; -use beefy_primitives::{ +use pallet_mmr::{LeafDataProvider, ParentNumberAndHash}; +use sp_consensus_beefy::{ mmr::{BeefyAuthoritySet, BeefyDataProvider, BeefyNextAuthoritySet, MmrLeaf, MmrLeafVersion}, ValidatorSet as BeefyValidatorSet, }; -use pallet_mmr::{LeafDataProvider, ParentNumberAndHash}; use frame_support::{crypto::ecdsa::ECDSAExt, traits::Get}; @@ -54,15 +54,15 @@ mod tests; /// A BEEFY consensus digest item with MMR root hash. pub struct DepositBeefyDigest(sp_std::marker::PhantomData); -impl pallet_mmr::primitives::OnNewRoot for DepositBeefyDigest +impl pallet_mmr::primitives::OnNewRoot for DepositBeefyDigest where - T: pallet_mmr::Config, + T: pallet_mmr::Config, T: pallet_beefy::Config, { fn on_new_root(root: &::Hash) { let digest = sp_runtime::generic::DigestItem::Consensus( - beefy_primitives::BEEFY_ENGINE_ID, - codec::Encode::encode(&beefy_primitives::ConsensusLog::< + sp_consensus_beefy::BEEFY_ENGINE_ID, + codec::Encode::encode(&sp_consensus_beefy::ConsensusLog::< ::BeefyId, >::MmrRoot(*root)), ); @@ -72,8 +72,8 @@ where /// Convert BEEFY secp256k1 public keys into Ethereum addresses pub struct BeefyEcdsaToEthereum; -impl Convert> for BeefyEcdsaToEthereum { - fn convert(beefy_id: beefy_primitives::crypto::AuthorityId) -> Vec { +impl Convert> for BeefyEcdsaToEthereum { + fn convert(beefy_id: sp_consensus_beefy::crypto::AuthorityId) -> Vec { sp_core::ecdsa::Public::from(beefy_id) .to_eth_address() .map(|v| v.to_vec()) @@ -156,7 +156,7 @@ impl LeafDataProvider for Pallet { } } -impl beefy_primitives::OnNewValidatorSet<::BeefyId> for Pallet +impl sp_consensus_beefy::OnNewValidatorSet<::BeefyId> for Pallet where T: pallet::Config, { diff --git a/frame/beefy-mmr/src/mock.rs b/frame/beefy-mmr/src/mock.rs index 922f882e1..d31effc9a 100644 --- a/frame/beefy-mmr/src/mock.rs +++ b/frame/beefy-mmr/src/mock.rs @@ -17,7 +17,6 @@ use std::vec; -use beefy_primitives::mmr::MmrLeafVersion; use codec::Encode; use frame_support::{ construct_runtime, parameter_types, @@ -25,6 +24,7 @@ use frame_support::{ traits::{ConstU16, ConstU32, ConstU64, GenesisBuild, KeyOwnerProofSystem}, BasicExternalities, }; +use sp_consensus_beefy::mmr::MmrLeafVersion; use sp_core::{crypto::KeyTypeId, Hasher, H256}; use sp_runtime::{ app_crypto::ecdsa::Public, @@ -35,7 +35,7 @@ use sp_runtime::{ use crate as pallet_beefy_mmr; -pub use beefy_primitives::{ +pub use sp_consensus_beefy::{ crypto::AuthorityId as BeefyId, mmr::BeefyDataProvider, ConsensusLog, BEEFY_ENGINE_ID, }; @@ -101,7 +101,7 @@ impl pallet_session::Config for Test { type WeightInfo = (); } -pub type MmrLeaf = beefy_primitives::mmr::MmrLeaf< +pub type MmrLeaf = sp_consensus_beefy::mmr::MmrLeaf< ::BlockNumber, ::Hash, ::Hash, diff --git a/frame/beefy-mmr/src/tests.rs b/frame/beefy-mmr/src/tests.rs index af10d1127..dc2e46f31 100644 --- a/frame/beefy-mmr/src/tests.rs +++ b/frame/beefy-mmr/src/tests.rs @@ -17,11 +17,11 @@ use std::vec; -use beefy_primitives::{ +use codec::{Decode, Encode}; +use sp_consensus_beefy::{ mmr::{BeefyNextAuthoritySet, MmrLeafVersion}, ValidatorSet, }; -use codec::{Decode, Encode}; use sp_core::H256; use sp_io::TestExternalities; diff --git a/frame/beefy/Cargo.toml b/frame/beefy/Cargo.toml index b2d663537..3778ca5f3 100644 --- a/frame/beefy/Cargo.toml +++ b/frame/beefy/Cargo.toml @@ -12,11 +12,11 @@ homepage = "https://substrate.io" codec = { package = "parity-scale-codec", version = "3.2.2", default-features = false, features = ["derive"] } scale-info = { version = "2.1.1", default-features = false, features = ["derive"] } serde = { version = "1.0.136", optional = true } -beefy-primitives = { version = "4.0.0-dev", default-features = false, path = "../../primitives/beefy", package = "sp-beefy" } frame-support = { version = "4.0.0-dev", default-features = false, path = "../support" } frame-system = { version = "4.0.0-dev", default-features = false, path = "../system" } pallet-authorship = { version = "4.0.0-dev", default-features = false, path = "../authorship" } pallet-session = { version = "4.0.0-dev", default-features = false, path = "../session" } +sp-consensus-beefy = { version = "4.0.0-dev", default-features = false, path = "../../primitives/consensus/beefy" } sp-runtime = { version = "7.0.0", default-features = false, path = "../../primitives/runtime" } sp-session = { version = "4.0.0-dev", default-features = false, path = "../../primitives/session" } sp-staking = { version = "4.0.0-dev", default-features = false, path = "../../primitives/staking" } @@ -36,7 +36,6 @@ sp-staking = { version = "4.0.0-dev", path = "../../primitives/staking" } [features] default = ["std"] std = [ - "beefy-primitives/std", "codec/std", "frame-support/std", "frame-system/std", @@ -44,6 +43,7 @@ std = [ "pallet-session/std", "scale-info/std", "serde", + "sp-consensus-beefy/std", "sp-runtime/std", "sp-session/std", "sp-staking/std", diff --git a/frame/beefy/src/equivocation.rs b/frame/beefy/src/equivocation.rs index 19c41cbfa..cc04f316c 100644 --- a/frame/beefy/src/equivocation.rs +++ b/frame/beefy/src/equivocation.rs @@ -36,13 +36,13 @@ use sp_std::prelude::*; -use beefy_primitives::{EquivocationProof, ValidatorSetId}; use codec::{self as codec, Decode, Encode}; use frame_support::{ log, traits::{Get, KeyOwnerProofSystem}, }; use frame_system::pallet_prelude::BlockNumberFor; +use sp_consensus_beefy::{EquivocationProof, ValidatorSetId}; use sp_runtime::{ transaction_validity::{ InvalidTransaction, TransactionPriority, TransactionSource, TransactionValidity, @@ -271,7 +271,7 @@ fn is_known_offence( ) -> Result<(), TransactionValidityError> { // check the membership proof to extract the offender's id, // equivocation validity will be fully checked during the call. - let key = (beefy_primitives::KEY_TYPE, equivocation_proof.offender_id().clone()); + let key = (sp_consensus_beefy::KEY_TYPE, equivocation_proof.offender_id().clone()); let offender = T::KeyOwnerProofSystem::check_proof(key, key_owner_proof.clone()) .ok_or(InvalidTransaction::BadProof)?; diff --git a/frame/beefy/src/lib.rs b/frame/beefy/src/lib.rs index bcb7fc126..698e6e733 100644 --- a/frame/beefy/src/lib.rs +++ b/frame/beefy/src/lib.rs @@ -40,7 +40,7 @@ use sp_session::{GetSessionNumber, GetValidatorCount}; use sp_staking::SessionIndex; use sp_std::prelude::*; -use beefy_primitives::{ +use sp_consensus_beefy::{ AuthorityIndex, BeefyAuthorityId, ConsensusLog, EquivocationProof, OnNewValidatorSet, ValidatorSet, BEEFY_ENGINE_ID, GENESIS_AUTHORITY_SET_ID, }; @@ -135,7 +135,7 @@ pub mod pallet { #[pallet::storage] #[pallet::getter(fn validator_set_id)] pub(super) type ValidatorSetId = - StorageValue<_, beefy_primitives::ValidatorSetId, ValueQuery>; + StorageValue<_, sp_consensus_beefy::ValidatorSetId, ValueQuery>; /// Authorities set scheduled to be used with the next session #[pallet::storage] @@ -156,7 +156,7 @@ pub mod pallet { #[pallet::storage] #[pallet::getter(fn session_for_set)] pub(super) type SetIdSession = - StorageMap<_, Twox64Concat, beefy_primitives::ValidatorSetId, SessionIndex>; + StorageMap<_, Twox64Concat, sp_consensus_beefy::ValidatorSetId, SessionIndex>; /// Block number where BEEFY consensus is enabled/started. /// If changing this, make sure `Self::ValidatorSetId` is also reset to @@ -280,7 +280,7 @@ impl Pallet { /// Return the current active BEEFY validator set. pub fn validator_set() -> Option> { let validators: BoundedVec = Self::authorities(); - let id: beefy_primitives::ValidatorSetId = Self::validator_set_id(); + let id: sp_consensus_beefy::ValidatorSetId = Self::validator_set_id(); ValidatorSet::::new(validators, id) } @@ -390,13 +390,13 @@ impl Pallet { // validate the key ownership proof extracting the id of the offender. let offender = T::KeyOwnerProofSystem::check_proof( - (beefy_primitives::KEY_TYPE, offender_id), + (sp_consensus_beefy::KEY_TYPE, offender_id), key_owner_proof, ) .ok_or(Error::::InvalidKeyOwnershipProof)?; // validate equivocation proof (check votes are different and signatures are valid). - if !beefy_primitives::check_equivocation_proof(&equivocation_proof) { + if !sp_consensus_beefy::check_equivocation_proof(&equivocation_proof) { return Err(Error::::InvalidEquivocationProof.into()) } diff --git a/frame/beefy/src/mock.rs b/frame/beefy/src/mock.rs index 74dba2a01..72e3f83df 100644 --- a/frame/beefy/src/mock.rs +++ b/frame/beefy/src/mock.rs @@ -40,7 +40,7 @@ use sp_staking::{EraIndex, SessionIndex}; use crate as pallet_beefy; -pub use beefy_primitives::{ +pub use sp_consensus_beefy::{ crypto::{AuthorityId as BeefyId, AuthoritySignature as BeefySignature}, ConsensusLog, EquivocationProof, BEEFY_ENGINE_ID, }; diff --git a/frame/beefy/src/tests.rs b/frame/beefy/src/tests.rs index 767b1e565..f9da20e90 100644 --- a/frame/beefy/src/tests.rs +++ b/frame/beefy/src/tests.rs @@ -17,11 +17,11 @@ use std::vec; -use beefy_primitives::{ +use codec::Encode; +use sp_consensus_beefy::{ check_equivocation_proof, generate_equivocation_proof, known_payloads::MMR_ROOT_ID, Keyring as BeefyKeyring, Payload, ValidatorSet, }; -use codec::Encode; use sp_runtime::DigestItem; @@ -298,7 +298,7 @@ fn report_equivocation_current_set_works() { // create the key ownership proof let key_owner_proof = - Historical::prove((beefy_primitives::KEY_TYPE, &equivocation_key)).unwrap(); + Historical::prove((sp_consensus_beefy::KEY_TYPE, &equivocation_key)).unwrap(); // report the equivocation and the tx should be dispatched successfully assert_ok!(Beefy::report_equivocation_unsigned( @@ -355,7 +355,7 @@ fn report_equivocation_old_set_works() { // create the key ownership proof in the "old" set let key_owner_proof = - Historical::prove((beefy_primitives::KEY_TYPE, &equivocation_key)).unwrap(); + Historical::prove((sp_consensus_beefy::KEY_TYPE, &equivocation_key)).unwrap(); start_era(2); @@ -437,7 +437,7 @@ fn report_equivocation_invalid_set_id() { let equivocation_keyring = BeefyKeyring::from_public(equivocation_key).unwrap(); let key_owner_proof = - Historical::prove((beefy_primitives::KEY_TYPE, &equivocation_key)).unwrap(); + Historical::prove((sp_consensus_beefy::KEY_TYPE, &equivocation_key)).unwrap(); let payload1 = Payload::from_single_entry(MMR_ROOT_ID, vec![42]); let payload2 = Payload::from_single_entry(MMR_ROOT_ID, vec![128]); @@ -476,7 +476,7 @@ fn report_equivocation_invalid_session() { // generate a key ownership proof at current era set id let key_owner_proof = - Historical::prove((beefy_primitives::KEY_TYPE, &equivocation_key)).unwrap(); + Historical::prove((sp_consensus_beefy::KEY_TYPE, &equivocation_key)).unwrap(); start_era(2); @@ -520,7 +520,7 @@ fn report_equivocation_invalid_key_owner_proof() { // generate a key ownership proof for the authority at index 1 let invalid_key_owner_proof = - Historical::prove((beefy_primitives::KEY_TYPE, &invalid_owner_key)).unwrap(); + Historical::prove((sp_consensus_beefy::KEY_TYPE, &invalid_owner_key)).unwrap(); let equivocation_authority_index = 0; let equivocation_key = &authorities[equivocation_authority_index]; @@ -569,7 +569,7 @@ fn report_equivocation_invalid_equivocation_proof() { // generate a key ownership proof at set id in era 1 let key_owner_proof = - Historical::prove((beefy_primitives::KEY_TYPE, &equivocation_key)).unwrap(); + Historical::prove((sp_consensus_beefy::KEY_TYPE, &equivocation_key)).unwrap(); let assert_invalid_equivocation_proof = |equivocation_proof| { assert_err!( @@ -650,7 +650,7 @@ fn report_equivocation_validate_unsigned_prevents_duplicates() { ); let key_owner_proof = - Historical::prove((beefy_primitives::KEY_TYPE, &equivocation_key)).unwrap(); + Historical::prove((sp_consensus_beefy::KEY_TYPE, &equivocation_key)).unwrap(); let call = Call::report_equivocation_unsigned { equivocation_proof: Box::new(equivocation_proof.clone()), @@ -756,7 +756,7 @@ fn valid_equivocation_reports_dont_pay_fees() { // create the key ownership proof. let key_owner_proof = - Historical::prove((beefy_primitives::KEY_TYPE, &equivocation_key)).unwrap(); + Historical::prove((sp_consensus_beefy::KEY_TYPE, &equivocation_key)).unwrap(); // check the dispatch info for the call. let info = Call::::report_equivocation_unsigned { diff --git a/primitives/beefy/Cargo.toml b/primitives/consensus/beefy/Cargo.toml similarity index 84% rename from primitives/beefy/Cargo.toml rename to primitives/consensus/beefy/Cargo.toml index 5dfda3e37..657569d12 100644 --- a/primitives/beefy/Cargo.toml +++ b/primitives/consensus/beefy/Cargo.toml @@ -1,5 +1,5 @@ [package] -name = "sp-beefy" +name = "sp-consensus-beefy" version = "4.0.0-dev" authors = ["Parity Technologies "] edition = "2021" @@ -15,19 +15,19 @@ targets = ["x86_64-unknown-linux-gnu"] codec = { package = "parity-scale-codec", version = "3.2.2", default-features = false, features = ["derive"] } serde = { version = "1.0.136", optional = true, features = ["derive"] } scale-info = { version = "2.1.1", default-features = false, features = ["derive"] } -sp-api = { version = "4.0.0-dev", default-features = false, path = "../api" } -sp-application-crypto = { version = "7.0.0", default-features = false, path = "../application-crypto" } -sp-core = { version = "7.0.0", default-features = false, path = "../core" } -sp-io = { version = "7.0.0", default-features = false, path = "../io" } -sp-mmr-primitives = { version = "4.0.0-dev", default-features = false, path = "../merkle-mountain-range" } -sp-runtime = { version = "7.0.0", default-features = false, path = "../runtime" } -sp-std = { version = "5.0.0", default-features = false, path = "../std" } +sp-api = { version = "4.0.0-dev", default-features = false, path = "../../api" } +sp-application-crypto = { version = "7.0.0", default-features = false, path = "../../application-crypto" } +sp-core = { version = "7.0.0", default-features = false, path = "../../core" } +sp-io = { version = "7.0.0", default-features = false, path = "../../io" } +sp-mmr-primitives = { version = "4.0.0-dev", default-features = false, path = "../../merkle-mountain-range" } +sp-runtime = { version = "7.0.0", default-features = false, path = "../../runtime" } +sp-std = { version = "5.0.0", default-features = false, path = "../../std" } strum = { version = "0.24.1", features = ["derive"], default-features = false } lazy_static = "1.4.0" [dev-dependencies] array-bytes = "4.1" -sp-keystore = { version = "0.13.0", path = "../keystore" } +sp-keystore = { version = "0.13.0", path = "../../keystore" } [features] default = ["std"] diff --git a/primitives/beefy/src/commitment.rs b/primitives/consensus/beefy/src/commitment.rs similarity index 100% rename from primitives/beefy/src/commitment.rs rename to primitives/consensus/beefy/src/commitment.rs diff --git a/primitives/beefy/src/lib.rs b/primitives/consensus/beefy/src/lib.rs similarity index 100% rename from primitives/beefy/src/lib.rs rename to primitives/consensus/beefy/src/lib.rs diff --git a/primitives/beefy/src/mmr.rs b/primitives/consensus/beefy/src/mmr.rs similarity index 100% rename from primitives/beefy/src/mmr.rs rename to primitives/consensus/beefy/src/mmr.rs diff --git a/primitives/beefy/src/payload.rs b/primitives/consensus/beefy/src/payload.rs similarity index 100% rename from primitives/beefy/src/payload.rs rename to primitives/consensus/beefy/src/payload.rs diff --git a/primitives/beefy/src/test_utils.rs b/primitives/consensus/beefy/src/test_utils.rs similarity index 100% rename from primitives/beefy/src/test_utils.rs rename to primitives/consensus/beefy/src/test_utils.rs diff --git a/primitives/beefy/src/witness.rs b/primitives/consensus/beefy/src/witness.rs similarity index 100% rename from primitives/beefy/src/witness.rs rename to primitives/consensus/beefy/src/witness.rs diff --git a/primitives/beefy/test-res/large-raw-commitment b/primitives/consensus/beefy/test-res/large-raw-commitment similarity index 100% rename from primitives/beefy/test-res/large-raw-commitment rename to primitives/consensus/beefy/test-res/large-raw-commitment diff --git a/test-utils/runtime/Cargo.toml b/test-utils/runtime/Cargo.toml index f3222dc15..339a435ef 100644 --- a/test-utils/runtime/Cargo.toml +++ b/test-utils/runtime/Cargo.toml @@ -13,11 +13,11 @@ publish = false targets = ["x86_64-unknown-linux-gnu"] [dependencies] -beefy-primitives = { version = "4.0.0-dev", default-features = false, path = "../../primitives/beefy", package = "sp-beefy" } pallet-beefy-mmr = { version = "4.0.0-dev", default-features = false, path = "../../frame/beefy-mmr" } sp-application-crypto = { version = "7.0.0", default-features = false, path = "../../primitives/application-crypto" } sp-consensus-aura = { version = "0.10.0-dev", default-features = false, path = "../../primitives/consensus/aura" } sp-consensus-babe = { version = "0.10.0-dev", default-features = false, path = "../../primitives/consensus/babe" } +sp-consensus-beefy = { version = "4.0.0-dev", default-features = false, path = "../../primitives/consensus/beefy" } sp-block-builder = { version = "4.0.0-dev", default-features = false, path = "../../primitives/block-builder" } codec = { package = "parity-scale-codec", version = "3.2.2", default-features = false, features = ["derive"] } scale-info = { version = "2.1.1", default-features = false, features = ["derive"] } @@ -66,11 +66,11 @@ default = [ "std", ] std = [ - "beefy-primitives/std", "pallet-beefy-mmr/std", "sp-application-crypto/std", "sp-consensus-aura/std", "sp-consensus-babe/std", + "sp-consensus-beefy/std", "sp-block-builder/std", "codec/std", "scale-info/std", diff --git a/test-utils/runtime/src/lib.rs b/test-utils/runtime/src/lib.rs index 9e8a4e119..87a60acfc 100644 --- a/test-utils/runtime/src/lib.rs +++ b/test-utils/runtime/src/lib.rs @@ -972,36 +972,36 @@ cfg_if! { } } - impl beefy_primitives::BeefyApi for Runtime { + impl sp_consensus_beefy::BeefyApi for Runtime { fn beefy_genesis() -> Option { None } - fn validator_set() -> Option> { + fn validator_set() -> Option> { None } fn submit_report_equivocation_unsigned_extrinsic( - _equivocation_proof: beefy_primitives::EquivocationProof< + _equivocation_proof: sp_consensus_beefy::EquivocationProof< NumberFor, - beefy_primitives::crypto::AuthorityId, - beefy_primitives::crypto::Signature + sp_consensus_beefy::crypto::AuthorityId, + sp_consensus_beefy::crypto::Signature >, - _key_owner_proof: beefy_primitives::OpaqueKeyOwnershipProof, + _key_owner_proof: sp_consensus_beefy::OpaqueKeyOwnershipProof, ) -> Option<()> { None } fn generate_key_ownership_proof( - _set_id: beefy_primitives::ValidatorSetId, - _authority_id: beefy_primitives::crypto::AuthorityId, - ) -> Option { None } + _set_id: sp_consensus_beefy::ValidatorSetId, + _authority_id: sp_consensus_beefy::crypto::AuthorityId, + ) -> Option { None } } - impl pallet_beefy_mmr::BeefyMmrApi for Runtime { - fn authority_set_proof() -> beefy_primitives::mmr::BeefyAuthoritySet { + impl pallet_beefy_mmr::BeefyMmrApi for Runtime { + fn authority_set_proof() -> sp_consensus_beefy::mmr::BeefyAuthoritySet { Default::default() } - fn next_authority_set_proof() -> beefy_primitives::mmr::BeefyNextAuthoritySet { + fn next_authority_set_proof() -> sp_consensus_beefy::mmr::BeefyNextAuthoritySet { Default::default() } } From ad5399644aebc54e32a107ac37ae08e6cd1f0cfb Mon Sep 17 00:00:00 2001 From: yjh Date: Wed, 1 Mar 2023 03:50:57 +0800 Subject: [PATCH 174/558] chore: move genesis block builder to chain-spec crate. (#13427) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * chore: move genesis block builder to block builder crate. * add missing file * chore: move genesis block builder to sc-chain-spec * Update client/chain-spec/src/genesis.rs Co-authored-by: Bastian Köcher * Update test-utils/runtime/src/genesismap.rs Co-authored-by: Bastian Köcher * Update test-utils/runtime/client/src/lib.rs * fix warnings * fix warnings --------- Co-authored-by: Bastian Köcher --- Cargo.lock | 5 ++ client/block-builder/Cargo.toml | 2 +- client/block-builder/src/lib.rs | 3 +- client/chain-spec/Cargo.toml | 4 ++ .../src/client => chain-spec/src}/genesis.rs | 49 +++++++++++++++--- client/chain-spec/src/lib.rs | 11 ++-- client/service/src/client/client.rs | 50 +++---------------- client/service/src/client/mod.rs | 3 +- client/service/src/lib.rs | 11 ++-- test-utils/runtime/client/Cargo.toml | 1 + test-utils/runtime/client/src/lib.rs | 17 +++---- test-utils/runtime/src/genesismap.rs | 6 +-- 12 files changed, 88 insertions(+), 74 deletions(-) rename client/{service/src/client => chain-spec/src}/genesis.rs (68%) diff --git a/Cargo.lock b/Cargo.lock index a29213b45..a412347bd 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -8011,12 +8011,16 @@ version = "4.0.0-dev" dependencies = [ "memmap2", "sc-chain-spec-derive", + "sc-client-api", + "sc-executor", "sc-network-common", "sc-telemetry", "serde", "serde_json", + "sp-blockchain", "sp-core", "sp-runtime", + "sp-state-machine", ] [[package]] @@ -10869,6 +10873,7 @@ dependencies = [ "futures", "parity-scale-codec", "sc-block-builder", + "sc-chain-spec", "sc-client-api", "sc-consensus", "sp-api", diff --git a/client/block-builder/Cargo.toml b/client/block-builder/Cargo.toml index 3b4bea981..d009826b2 100644 --- a/client/block-builder/Cargo.toml +++ b/client/block-builder/Cargo.toml @@ -23,7 +23,7 @@ sp-blockchain = { version = "4.0.0-dev", path = "../../primitives/blockchain" } sp-core = { version = "7.0.0", path = "../../primitives/core" } sp-inherents = { version = "4.0.0-dev", path = "../../primitives/inherents" } sp-runtime = { version = "7.0.0", path = "../../primitives/runtime" } -sp-state-machine = { version = "0.13.0", path = "../../primitives/state-machine" } [dev-dependencies] +sp-state-machine = { version = "0.13.0", path = "../../primitives/state-machine" } substrate-test-runtime-client = { path = "../../test-utils/runtime/client" } diff --git a/client/block-builder/src/lib.rs b/client/block-builder/src/lib.rs index d7ef3b1da..d97afadd4 100644 --- a/client/block-builder/src/lib.rs +++ b/client/block-builder/src/lib.rs @@ -39,9 +39,8 @@ use sp_runtime::{ Digest, }; -pub use sp_block_builder::BlockBuilder as BlockBuilderApi; - use sc_client_api::backend; +pub use sp_block_builder::BlockBuilder as BlockBuilderApi; /// Used as parameter to [`BlockBuilderProvider`] to express if proof recording should be enabled. /// diff --git a/client/chain-spec/Cargo.toml b/client/chain-spec/Cargo.toml index 0a4e54e8d..6168a897c 100644 --- a/client/chain-spec/Cargo.toml +++ b/client/chain-spec/Cargo.toml @@ -16,8 +16,12 @@ targets = ["x86_64-unknown-linux-gnu"] memmap2 = "0.5.0" serde = { version = "1.0.136", features = ["derive"] } serde_json = "1.0.85" +sc-client-api = { version = "4.0.0-dev", path = "../api" } sc-chain-spec-derive = { version = "4.0.0-dev", path = "./derive" } +sc-executor = { version = "0.10.0-dev", path = "../executor" } sc-network-common = { version = "0.10.0-dev", path = "../network/common" } sc-telemetry = { version = "4.0.0-dev", path = "../telemetry" } +sp-blockchain = { version = "4.0.0-dev", path = "../../primitives/blockchain" } sp-core = { version = "7.0.0", path = "../../primitives/core" } sp-runtime = { version = "7.0.0", path = "../../primitives/runtime" } +sp-state-machine = { version = "0.13.0", path = "../../primitives/state-machine" } diff --git a/client/service/src/client/genesis.rs b/client/chain-spec/src/genesis.rs similarity index 68% rename from client/service/src/client/genesis.rs rename to client/chain-spec/src/genesis.rs index b3ed46c15..6aa156a62 100644 --- a/client/service/src/client/genesis.rs +++ b/client/chain-spec/src/genesis.rs @@ -18,20 +18,56 @@ //! Tool for creating the genesis block. +use std::{collections::hash_map::DefaultHasher, marker::PhantomData, sync::Arc}; + use sc_client_api::{backend::Backend, BlockImportOperation}; use sc_executor::RuntimeVersionOf; -use sp_core::storage::Storage; +use sp_core::storage::{well_known_keys, StateVersion, Storage}; use sp_runtime::{ traits::{Block as BlockT, Hash as HashT, Header as HeaderT, Zero}, BuildStorage, }; -use std::{marker::PhantomData, sync::Arc}; + +/// Return the state version given the genesis storage and executor. +pub fn resolve_state_version_from_wasm( + storage: &Storage, + executor: &E, +) -> sp_blockchain::Result +where + E: RuntimeVersionOf, +{ + if let Some(wasm) = storage.top.get(well_known_keys::CODE) { + let mut ext = sp_state_machine::BasicExternalities::new_empty(); // just to read runtime version. + + let code_fetcher = sp_core::traits::WrappedRuntimeCode(wasm.as_slice().into()); + let runtime_code = sp_core::traits::RuntimeCode { + code_fetcher: &code_fetcher, + heap_pages: None, + hash: { + use std::hash::{Hash, Hasher}; + let mut state = DefaultHasher::new(); + wasm.hash(&mut state); + state.finish().to_le_bytes().to_vec() + }, + }; + let runtime_version = RuntimeVersionOf::runtime_version(executor, &mut ext, &runtime_code) + .map_err(|e| sp_blockchain::Error::VersionInvalid(e.to_string()))?; + Ok(runtime_version.state_version()) + } else { + Err(sp_blockchain::Error::VersionInvalid( + "Runtime missing from initial storage, could not read state version.".to_string(), + )) + } +} /// Create a genesis block, given the initial storage. -pub fn construct_genesis_block(state_root: Block::Hash) -> Block { +pub fn construct_genesis_block( + state_root: Block::Hash, + state_version: StateVersion, +) -> Block { let extrinsics_root = <<::Header as HeaderT>::Hashing as HashT>::trie_root( Vec::new(), - sp_runtime::StateVersion::V0, + state_version, ); Block::new( @@ -93,12 +129,11 @@ impl, E: RuntimeVersionOf> BuildGenesisBlock sp_blockchain::Result<(Block, Self::BlockImportOperation)> { let Self { genesis_storage, commit_genesis_state, backend, executor, _phantom } = self; - let genesis_state_version = - crate::resolve_state_version_from_wasm(&genesis_storage, &executor)?; + let genesis_state_version = resolve_state_version_from_wasm(&genesis_storage, &executor)?; let mut op = backend.begin_operation()?; let state_root = op.set_genesis_state(genesis_storage, commit_genesis_state, genesis_state_version)?; - let genesis_block = construct_genesis_block::(state_root); + let genesis_block = construct_genesis_block::(state_root, genesis_state_version); Ok((genesis_block, op)) } diff --git a/client/chain-spec/src/lib.rs b/client/chain-spec/src/lib.rs index 414df1fe1..e43a24796 100644 --- a/client/chain-spec/src/lib.rs +++ b/client/chain-spec/src/lib.rs @@ -177,10 +177,15 @@ mod chain_spec; mod extension; +mod genesis; -pub use chain_spec::{ChainSpec as GenericChainSpec, NoExtension}; -pub use extension::{ - get_extension, get_extension_mut, Extension, Fork, Forks, GetExtension, Group, +pub use self::{ + chain_spec::{ChainSpec as GenericChainSpec, NoExtension}, + extension::{get_extension, get_extension_mut, Extension, Fork, Forks, GetExtension, Group}, + genesis::{ + construct_genesis_block, resolve_state_version_from_wasm, BuildGenesisBlock, + GenesisBlockBuilder, + }, }; pub use sc_chain_spec_derive::{ChainSpecExtension, ChainSpecGroup}; diff --git a/client/service/src/client/client.rs b/client/service/src/client/client.rs index 4b5822ae0..9a2a376eb 100644 --- a/client/service/src/client/client.rs +++ b/client/service/src/client/client.rs @@ -18,16 +18,14 @@ //! Substrate Client -use super::{ - block_rules::{BlockRules, LookupResult as BlockLookupResult}, - genesis::BuildGenesisBlock, -}; +use super::block_rules::{BlockRules, LookupResult as BlockLookupResult}; use futures::{FutureExt, StreamExt}; use log::{error, info, trace, warn}; use parking_lot::{Mutex, RwLock}; use prometheus_endpoint::Registry; use rand::Rng; use sc_block_builder::{BlockBuilderApi, BlockBuilderProvider, RecordProof}; +use sc_chain_spec::{resolve_state_version_from_wasm, BuildGenesisBlock}; use sc_client_api::{ backend::{ self, apply_aux, BlockImportOperation, ClientImportOperation, FinalizeSummary, Finalizer, @@ -46,7 +44,7 @@ use sc_client_api::{ use sc_consensus::{ BlockCheckParams, BlockImportParams, ForkChoiceStrategy, ImportResult, StateAction, }; -use sc_executor::{RuntimeVersion, RuntimeVersionOf}; +use sc_executor::RuntimeVersion; use sc_telemetry::{telemetry, TelemetryHandle, SUBSTRATE_INFO}; use sp_api::{ ApiExt, ApiRef, CallApiAt, CallApiAtParams, ConstructRuntimeApi, Core as CoreApi, @@ -61,8 +59,8 @@ use sp_consensus::{BlockOrigin, BlockStatus, Error as ConsensusError}; use sc_utils::mpsc::{tracing_unbounded, TracingUnboundedSender}; use sp_core::{ storage::{ - well_known_keys, ChildInfo, ChildType, PrefixedStorageKey, Storage, StorageChild, - StorageData, StorageKey, + well_known_keys, ChildInfo, ChildType, PrefixedStorageKey, StorageChild, StorageData, + StorageKey, }, traits::SpawnNamed, }; @@ -84,7 +82,7 @@ use sp_state_machine::{ }; use sp_trie::{CompactProof, StorageProof}; use std::{ - collections::{hash_map::DefaultHasher, HashMap, HashSet}, + collections::{HashMap, HashSet}, marker::PhantomData, path::PathBuf, sync::Arc, @@ -172,7 +170,7 @@ pub fn new_in_mem( Client, LocalCallExecutor, E>, Block, RA>, > where - E: CodeExecutor + RuntimeVersionOf, + E: CodeExecutor + sc_executor::RuntimeVersionOf, Block: BlockT, G: BuildGenesisBlock< Block, @@ -233,7 +231,7 @@ pub fn new_with_backend( config: ClientConfig, ) -> sp_blockchain::Result, Block, RA>> where - E: CodeExecutor + RuntimeVersionOf, + E: CodeExecutor + sc_executor::RuntimeVersionOf, G: BuildGenesisBlock< Block, BlockImportOperation = >::BlockImportOperation, @@ -1222,38 +1220,6 @@ where } } -/// Return the genesis state version given the genesis storage and executor. -pub fn resolve_state_version_from_wasm( - storage: &Storage, - executor: &E, -) -> sp_blockchain::Result -where - E: RuntimeVersionOf, -{ - if let Some(wasm) = storage.top.get(well_known_keys::CODE) { - let mut ext = sp_state_machine::BasicExternalities::new_empty(); // just to read runtime version. - - let code_fetcher = sp_core::traits::WrappedRuntimeCode(wasm.as_slice().into()); - let runtime_code = sp_core::traits::RuntimeCode { - code_fetcher: &code_fetcher, - heap_pages: None, - hash: { - use std::hash::{Hash, Hasher}; - let mut state = DefaultHasher::new(); - wasm.hash(&mut state); - state.finish().to_le_bytes().to_vec() - }, - }; - let runtime_version = RuntimeVersionOf::runtime_version(executor, &mut ext, &runtime_code) - .map_err(|e| sp_blockchain::Error::VersionInvalid(e.to_string()))?; - Ok(runtime_version.state_version()) - } else { - Err(sp_blockchain::Error::VersionInvalid( - "Runtime missing from initial storage, could not read state version.".to_string(), - )) - } -} - impl UsageProvider for Client where B: backend::Backend, diff --git a/client/service/src/client/mod.rs b/client/service/src/client/mod.rs index 38d818c49..a13fd4317 100644 --- a/client/service/src/client/mod.rs +++ b/client/service/src/client/mod.rs @@ -47,13 +47,12 @@ mod block_rules; mod call_executor; mod client; -pub mod genesis; mod wasm_override; mod wasm_substitutes; pub use self::{ call_executor::LocalCallExecutor, - client::{resolve_state_version_from_wasm, Client, ClientConfig}, + client::{Client, ClientConfig}, }; #[cfg(feature = "test-helpers")] diff --git a/client/service/src/lib.rs b/client/service/src/lib.rs index af0b11f20..6bafa9936 100644 --- a/client/service/src/lib.rs +++ b/client/service/src/lib.rs @@ -61,12 +61,15 @@ pub use self::{ new_full_parts, spawn_tasks, BuildNetworkParams, KeystoreContainer, NetworkStarter, SpawnTasksParams, TFullBackend, TFullCallExecutor, TFullClient, }, - client::{ - genesis::{BuildGenesisBlock, GenesisBlockBuilder}, - resolve_state_version_from_wasm, ClientConfig, LocalCallExecutor, - }, + client::{ClientConfig, LocalCallExecutor}, error::Error, }; + +pub use sc_chain_spec::{ + construct_genesis_block, resolve_state_version_from_wasm, BuildGenesisBlock, + GenesisBlockBuilder, +}; + pub use config::{ BasePath, BlocksPruning, Configuration, DatabaseSource, PruningMode, Role, RpcMethods, TaskType, }; diff --git a/test-utils/runtime/client/Cargo.toml b/test-utils/runtime/client/Cargo.toml index 7d5c9673f..986db0ba6 100644 --- a/test-utils/runtime/client/Cargo.toml +++ b/test-utils/runtime/client/Cargo.toml @@ -15,6 +15,7 @@ targets = ["x86_64-unknown-linux-gnu"] codec = { package = "parity-scale-codec", version = "3.2.2" } futures = "0.3.21" sc-block-builder = { version = "0.10.0-dev", path = "../../../client/block-builder" } +sc-chain-spec = { version = "4.0.0-dev", path = "../../../client/chain-spec" } sc-client-api = { version = "4.0.0-dev", path = "../../../client/api" } sc-consensus = { version = "0.10.0-dev", path = "../../../client/consensus/common" } sp-api = { version = "4.0.0-dev", path = "../../../primitives/api" } diff --git a/test-utils/runtime/client/src/lib.rs b/test-utils/runtime/client/src/lib.rs index 97ee525f6..099aeab11 100644 --- a/test-utils/runtime/client/src/lib.rs +++ b/test-utils/runtime/client/src/lib.rs @@ -30,6 +30,8 @@ pub use substrate_test_runtime as runtime; pub use self::block_builder_ext::BlockBuilderExt; +use sc_chain_spec::construct_genesis_block; +use sp_api::StateVersion; use sp_core::{ sr25519, storage::{ChildInfo, Storage, StorageChild}, @@ -122,7 +124,7 @@ impl GenesisParameters { } } -impl substrate_test_client::GenesisInit for GenesisParameters { +impl GenesisInit for GenesisParameters { fn genesis_storage(&self) -> Storage { use codec::Encode; @@ -148,7 +150,7 @@ impl substrate_test_client::GenesisInit for GenesisParameters { storage.top.clone().into_iter().chain(child_roots).collect(), sp_runtime::StateVersion::V1, ); - let block: runtime::Block = client::genesis::construct_genesis_block(state_root); + let block: runtime::Block = construct_genesis_block(state_root, StateVersion::V1); storage.top.extend(additional_storage_with_genesis(&block)); storage @@ -260,7 +262,7 @@ impl TestClientBuilderExt client::LocalCallExecutor< substrate_test_runtime::Block, B, - sc_executor::NativeElseWasmExecutor, + NativeElseWasmExecutor, >, B, > where @@ -288,11 +290,6 @@ pub fn new() -> Client { } /// Create a new native executor. -pub fn new_native_executor() -> sc_executor::NativeElseWasmExecutor { - sc_executor::NativeElseWasmExecutor::new( - sc_executor::WasmExecutionMethod::Interpreted, - None, - 8, - 2, - ) +pub fn new_native_executor() -> NativeElseWasmExecutor { + NativeElseWasmExecutor::new(sc_executor::WasmExecutionMethod::Interpreted, None, 8, 2) } diff --git a/test-utils/runtime/src/genesismap.rs b/test-utils/runtime/src/genesismap.rs index 77fdf59ea..9e00dd299 100644 --- a/test-utils/runtime/src/genesismap.rs +++ b/test-utils/runtime/src/genesismap.rs @@ -20,10 +20,10 @@ use super::{system, wasm_binary_unwrap, AccountId, AuthorityId, Runtime}; use codec::{Encode, Joiner, KeyedVec}; use frame_support::traits::GenesisBuild; -use sc_service::client::genesis; +use sc_service::construct_genesis_block; use sp_core::{ map, - storage::{well_known_keys, Storage}, + storage::{well_known_keys, StateVersion, Storage}, }; use sp_io::hashing::{blake2_256, twox_128}; use sp_runtime::traits::{Block as BlockT, Hash as HashT, Header as HeaderT}; @@ -106,7 +106,7 @@ pub fn insert_genesis_block(storage: &mut Storage) -> sp_core::hash::H256 { storage.top.clone().into_iter().collect(), sp_runtime::StateVersion::V1, ); - let block: crate::Block = genesis::construct_genesis_block(state_root); + let block: crate::Block = construct_genesis_block(state_root, StateVersion::V1); let genesis_hash = block.header.hash(); storage.top.extend(additional_storage_with_genesis(&block)); genesis_hash From aa31d1b1ca12197c2d8b07e78d6c6c3a8bd34f98 Mon Sep 17 00:00:00 2001 From: Koute Date: Wed, 1 Mar 2023 17:58:18 +0900 Subject: [PATCH 175/558] Speed up storage iteration from within the runtime (#13479) * Speed up storage iteration from within the runtime * Move the cached iterator into an `Option` * Use `RefCell` in no_std * Simplify the code slightly * Use `Option::replace` * Update doc comment for `next_storage_key_slow` --- Cargo.lock | 8 +- frame/benchmarking/pov/src/benchmarking.rs | 13 +++ frame/benchmarking/pov/src/lib.rs | 5 ++ primitives/state-machine/Cargo.toml | 2 +- primitives/state-machine/src/trie_backend.rs | 85 ++++++++++++++++++- .../state-machine/src/trie_backend_essence.rs | 13 ++- primitives/trie/Cargo.toml | 4 +- test-utils/runtime/Cargo.toml | 2 +- .../rpc/state-trie-migration-rpc/Cargo.toml | 2 +- 9 files changed, 118 insertions(+), 16 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index a412347bd..a8f596c06 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -11437,9 +11437,9 @@ dependencies = [ [[package]] name = "trie-bench" -version = "0.35.0" +version = "0.36.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "22c1d18c423077531e693e87ace54ed9e4af1e4ce0a3ea8c9aa6608741074e2b" +checksum = "ac2b7695feb8041efc0adaa09ed3a692ca7b7c997395954fdc838b5b078346f6" dependencies = [ "criterion", "hash-db", @@ -11453,9 +11453,9 @@ dependencies = [ [[package]] name = "trie-db" -version = "0.25.1" +version = "0.26.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3390c0409daaa6027d6681393316f4ccd3ff82e1590a1e4725014e3ae2bf1920" +checksum = "879380c0061b165ba1f036325b7f3478bc1a947814d9fc36d22c5d8e960b11bd" dependencies = [ "hash-db", "hashbrown 0.13.2", diff --git a/frame/benchmarking/pov/src/benchmarking.rs b/frame/benchmarking/pov/src/benchmarking.rs index ebe9b2414..46a98ae3f 100644 --- a/frame/benchmarking/pov/src/benchmarking.rs +++ b/frame/benchmarking/pov/src/benchmarking.rs @@ -316,6 +316,19 @@ frame_benchmarking::benchmarks! { call.dispatch_bypass_filter(RawOrigin::Root.into()).unwrap(); } + storage_iteration { + for i in 0..65000 { + UnboundedMapTwox::::insert(i, sp_std::vec![0; 64]); + } + }: { + for (key, value) in UnboundedMapTwox::::iter() { + unsafe { + core::ptr::read_volatile(&key); + core::ptr::read_volatile(value.as_ptr()); + } + } + } + impl_benchmark_test_suite!( Pallet, mock::new_test_ext(), diff --git a/frame/benchmarking/pov/src/lib.rs b/frame/benchmarking/pov/src/lib.rs index 7dee5c37c..b66b5e417 100644 --- a/frame/benchmarking/pov/src/lib.rs +++ b/frame/benchmarking/pov/src/lib.rs @@ -107,6 +107,11 @@ pub mod pallet { pub(crate) type UnboundedMap2 = StorageMap, QueryKind = OptionQuery>; + #[pallet::storage] + #[pallet::unbounded] + pub(crate) type UnboundedMapTwox = + StorageMap, QueryKind = OptionQuery>; + #[pallet::event] #[pallet::generate_deposit(pub(super) fn deposit_event)] pub enum Event { diff --git a/primitives/state-machine/Cargo.toml b/primitives/state-machine/Cargo.toml index 2969a51c4..56fbe0726 100644 --- a/primitives/state-machine/Cargo.toml +++ b/primitives/state-machine/Cargo.toml @@ -33,7 +33,7 @@ array-bytes = "4.1" pretty_assertions = "1.2.1" rand = "0.8.5" sp-runtime = { version = "7.0.0", path = "../runtime" } -trie-db = "0.25.1" +trie-db = "0.26.0" assert_matches = "1.5" [features] diff --git a/primitives/state-machine/src/trie_backend.rs b/primitives/state-machine/src/trie_backend.rs index 3e8d0a7a3..10e2dfd16 100644 --- a/primitives/state-machine/src/trie_backend.rs +++ b/primitives/state-machine/src/trie_backend.rs @@ -20,8 +20,8 @@ #[cfg(feature = "std")] use crate::backend::AsTrieBackend; use crate::{ - backend::IterArgs, - trie_backend_essence::{TrieBackendEssence, TrieBackendStorage}, + backend::{IterArgs, StorageIterator}, + trie_backend_essence::{RawIter, TrieBackendEssence, TrieBackendStorage}, Backend, StorageKey, StorageValue, }; use codec::Codec; @@ -172,6 +172,7 @@ where self.cache, self.recorder, ), + next_storage_key_cache: Default::default(), } } @@ -180,19 +181,62 @@ where pub fn build(self) -> TrieBackend { let _ = self.cache; - TrieBackend { essence: TrieBackendEssence::new(self.storage, self.root) } + TrieBackend { + essence: TrieBackendEssence::new(self.storage, self.root), + next_storage_key_cache: Default::default(), + } + } +} + +/// A cached iterator. +struct CachedIter +where + H: Hasher, +{ + last_key: sp_std::vec::Vec, + iter: RawIter, +} + +impl Default for CachedIter +where + H: Hasher, +{ + fn default() -> Self { + Self { last_key: Default::default(), iter: Default::default() } } } +#[cfg(feature = "std")] +type CacheCell = parking_lot::Mutex; + +#[cfg(not(feature = "std"))] +type CacheCell = core::cell::RefCell; + +#[cfg(feature = "std")] +fn access_cache(cell: &CacheCell, callback: impl FnOnce(&mut T) -> R) -> R { + callback(&mut *cell.lock()) +} + +#[cfg(not(feature = "std"))] +fn access_cache(cell: &CacheCell, callback: impl FnOnce(&mut T) -> R) -> R { + callback(&mut *cell.borrow_mut()) +} + /// Patricia trie-based backend. Transaction type is an overlay of changes to commit. pub struct TrieBackend, H: Hasher, C = LocalTrieCache> { pub(crate) essence: TrieBackendEssence, + next_storage_key_cache: CacheCell>>, } impl, H: Hasher, C: AsLocalTrieCache + Send + Sync> TrieBackend where H::Out: Codec, { + #[cfg(test)] + pub(crate) fn from_essence(essence: TrieBackendEssence) -> Self { + Self { essence, next_storage_key_cache: Default::default() } + } + /// Get backend essence reference. pub fn essence(&self) -> &TrieBackendEssence { &self.essence @@ -265,7 +309,40 @@ where } fn next_storage_key(&self, key: &[u8]) -> Result, Self::Error> { - self.essence.next_storage_key(key) + let (is_cached, mut cache) = access_cache(&self.next_storage_key_cache, Option::take) + .map(|cache| (cache.last_key == key, cache)) + .unwrap_or_default(); + + if !is_cached { + cache.iter = self.raw_iter(IterArgs { + start_at: Some(key), + start_at_exclusive: true, + ..IterArgs::default() + })? + }; + + let next_key = match cache.iter.next_key(self) { + None => return Ok(None), + Some(Err(error)) => return Err(error), + Some(Ok(next_key)) => next_key, + }; + + cache.last_key.clear(); + cache.last_key.extend_from_slice(&next_key); + access_cache(&self.next_storage_key_cache, |cache_cell| cache_cell.replace(cache)); + + #[cfg(debug_assertions)] + debug_assert_eq!( + self.essence + .next_storage_key_slow(key) + .expect( + "fetching the next key through iterator didn't fail so this shouldn't either" + ) + .as_ref(), + Some(&next_key) + ); + + Ok(Some(next_key)) } fn next_child_storage_key( diff --git a/primitives/state-machine/src/trie_backend_essence.rs b/primitives/state-machine/src/trie_backend_essence.rs index f071a32ce..1f6d71b2d 100644 --- a/primitives/state-machine/src/trie_backend_essence.rs +++ b/primitives/state-machine/src/trie_backend_essence.rs @@ -415,9 +415,14 @@ where }) } - /// Return the next key in the trie i.e. the minimum key that is strictly superior to `key` in + /// Returns the next key in the trie i.e. the minimum key that is strictly superior to `key` in /// lexicographic order. - pub fn next_storage_key(&self, key: &[u8]) -> Result> { + /// + /// Will always traverse the trie from scratch in search of the key, which is slow. + /// Used only when debug assertions are enabled to crosscheck the results of finding + /// the next key through an iterator. + #[cfg(debug_assertions)] + pub fn next_storage_key_slow(&self, key: &[u8]) -> Result> { self.next_storage_key_from_root(&self.root, None, key) } @@ -859,6 +864,7 @@ impl, H: Hasher, C: AsLocalTrieCache + Send + Sync> #[cfg(test)] mod test { use super::*; + use crate::{Backend, TrieBackend}; use sp_core::{Blake2Hasher, H256}; use sp_trie::{ cache::LocalTrieCache, trie_types::TrieDBMutBuilderV1 as TrieDBMutBuilder, KeySpacedDBMut, @@ -897,6 +903,8 @@ mod test { }; let essence_1 = TrieBackendEssence::<_, _, LocalTrieCache<_>>::new(mdb, root_1); + let mdb = essence_1.backend_storage().clone(); + let essence_1 = TrieBackend::from_essence(essence_1); assert_eq!(essence_1.next_storage_key(b"2"), Ok(Some(b"3".to_vec()))); assert_eq!(essence_1.next_storage_key(b"3"), Ok(Some(b"4".to_vec()))); @@ -904,7 +912,6 @@ mod test { assert_eq!(essence_1.next_storage_key(b"5"), Ok(Some(b"6".to_vec()))); assert_eq!(essence_1.next_storage_key(b"6"), Ok(None)); - let mdb = essence_1.backend_storage().clone(); let essence_2 = TrieBackendEssence::<_, _, LocalTrieCache<_>>::new(mdb, root_2); assert_eq!(essence_2.next_child_storage_key(child_info, b"2"), Ok(Some(b"3".to_vec()))); diff --git a/primitives/trie/Cargo.toml b/primitives/trie/Cargo.toml index 33a62cdd9..fae39ec34 100644 --- a/primitives/trie/Cargo.toml +++ b/primitives/trie/Cargo.toml @@ -29,7 +29,7 @@ parking_lot = { version = "0.12.1", optional = true } scale-info = { version = "2.1.1", default-features = false, features = ["derive"] } thiserror = { version = "1.0.30", optional = true } tracing = { version = "0.1.29", optional = true } -trie-db = { version = "0.25.0", default-features = false } +trie-db = { version = "0.26.0", default-features = false } trie-root = { version = "0.17.0", default-features = false } sp-core = { version = "7.0.0", default-features = false, path = "../core" } sp-std = { version = "5.0.0", default-features = false, path = "../std" } @@ -38,7 +38,7 @@ schnellru = { version = "0.2.1", optional = true } [dev-dependencies] array-bytes = "4.1" criterion = "0.4.0" -trie-bench = "0.35.0" +trie-bench = "0.36.0" trie-standardmap = "0.15.2" sp-runtime = { version = "7.0.0", path = "../runtime" } diff --git a/test-utils/runtime/Cargo.toml b/test-utils/runtime/Cargo.toml index 339a435ef..e0f414b16 100644 --- a/test-utils/runtime/Cargo.toml +++ b/test-utils/runtime/Cargo.toml @@ -41,7 +41,7 @@ pallet-timestamp = { version = "4.0.0-dev", default-features = false, path = ".. sp-consensus-grandpa = { version = "4.0.0-dev", default-features = false, path = "../../primitives/consensus/grandpa" } sp-trie = { version = "7.0.0", default-features = false, path = "../../primitives/trie" } sp-transaction-pool = { version = "4.0.0-dev", default-features = false, path = "../../primitives/transaction-pool" } -trie-db = { version = "0.25.1", default-features = false } +trie-db = { version = "0.26.0", default-features = false } sc-service = { version = "0.10.0-dev", default-features = false, optional = true, features = ["test-helpers"], path = "../../client/service" } sp-state-machine = { version = "0.13.0", default-features = false, path = "../../primitives/state-machine" } sp-externalities = { version = "0.13.0", default-features = false, path = "../../primitives/externalities" } diff --git a/utils/frame/rpc/state-trie-migration-rpc/Cargo.toml b/utils/frame/rpc/state-trie-migration-rpc/Cargo.toml index 1dd9da9a5..e6eccc6ff 100644 --- a/utils/frame/rpc/state-trie-migration-rpc/Cargo.toml +++ b/utils/frame/rpc/state-trie-migration-rpc/Cargo.toml @@ -21,7 +21,7 @@ log = { version = "0.4.17", default-features = false } sp-core = { path = "../../../../primitives/core" } sp-state-machine = { path = "../../../../primitives/state-machine" } sp-trie = { path = "../../../../primitives/trie" } -trie-db = "0.25.1" +trie-db = "0.26.0" jsonrpsee = { version = "0.16.2", features = ["client-core", "server", "macros"] } From 5cd48479c5766161729666ac99091e60d7b8fef5 Mon Sep 17 00:00:00 2001 From: Dmitry Markin Date: Wed, 1 Mar 2023 12:54:31 +0300 Subject: [PATCH 176/558] Make unbounded channels size warning exact (part 1) (#13490) * Replace `futures-channel` with `async-channel` in `out_events` * Apply suggestions from code review Co-authored-by: Koute * Also print the backtrace of `send()` call * Switch from `backtrace` crate to `std::backtrace` * Remove outdated `backtrace` dependency * Remove `backtrace` from `Cargo.lock` --------- Co-authored-by: Koute --- Cargo.lock | 13 ++++++- client/network/Cargo.toml | 2 +- client/network/src/service/out_events.rs | 48 ++++++++++-------------- 3 files changed, 32 insertions(+), 31 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index a8f596c06..f37a3b41f 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -326,6 +326,17 @@ version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9b34d609dfbaf33d6889b2b7106d3ca345eacad44200913df5ba02bfd31d2ba9" +[[package]] +name = "async-channel" +version = "1.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cf46fee83e5ccffc220104713af3292ff9bc7c64c7de289f66dae8e38d826833" +dependencies = [ + "concurrent-queue", + "event-listener", + "futures-core", +] + [[package]] name = "async-io" version = "1.12.0" @@ -8626,9 +8637,9 @@ version = "0.10.0-dev" dependencies = [ "array-bytes", "assert_matches", + "async-channel", "async-trait", "asynchronous-codec", - "backtrace", "bytes", "either", "fnv", diff --git a/client/network/Cargo.toml b/client/network/Cargo.toml index b6b212471..5a918bebd 100644 --- a/client/network/Cargo.toml +++ b/client/network/Cargo.toml @@ -15,9 +15,9 @@ targets = ["x86_64-unknown-linux-gnu"] [dependencies] array-bytes = "4.1" +async-channel = "1.8.0" async-trait = "0.1" asynchronous-codec = "0.6" -backtrace = "0.3.67" bytes = "1" codec = { package = "parity-scale-codec", version = "3.2.2", features = ["derive"] } either = "1.5.3" diff --git a/client/network/src/service/out_events.rs b/client/network/src/service/out_events.rs index 57a5092ae..99ac022c2 100644 --- a/client/network/src/service/out_events.rs +++ b/client/network/src/service/out_events.rs @@ -31,20 +31,17 @@ //! - Send events by calling [`OutChannels::send`]. Events are cloned for each sender in the //! collection. -use backtrace::Backtrace; -use futures::{channel::mpsc, prelude::*, ready, stream::FusedStream}; +use futures::{prelude::*, ready, stream::FusedStream}; use log::error; use parking_lot::Mutex; use prometheus_endpoint::{register, CounterVec, GaugeVec, Opts, PrometheusError, Registry, U64}; use sc_network_common::protocol::event::Event; use std::{ + backtrace::Backtrace, cell::RefCell, fmt, pin::Pin, - sync::{ - atomic::{AtomicI64, Ordering}, - Arc, - }, + sync::Arc, task::{Context, Poll}, }; @@ -52,20 +49,18 @@ use std::{ /// /// The name is used in Prometheus reports, the queue size threshold is used /// to warn if there are too many unprocessed events in the channel. -pub fn channel(name: &'static str, queue_size_warning: i64) -> (Sender, Receiver) { - let (tx, rx) = mpsc::unbounded(); +pub fn channel(name: &'static str, queue_size_warning: usize) -> (Sender, Receiver) { + let (tx, rx) = async_channel::unbounded(); let metrics = Arc::new(Mutex::new(None)); - let queue_size = Arc::new(AtomicI64::new(0)); let tx = Sender { inner: tx, name, - queue_size: queue_size.clone(), queue_size_warning, warning_fired: false, - creation_backtrace: Backtrace::new_unresolved(), + creation_backtrace: Backtrace::force_capture(), metrics: metrics.clone(), }; - let rx = Receiver { inner: rx, name, queue_size, metrics }; + let rx = Receiver { inner: rx, name, metrics }; (tx, rx) } @@ -77,16 +72,11 @@ pub fn channel(name: &'static str, queue_size_warning: i64) -> (Sender, Receiver /// implement the `Clone` trait e.g. in Order to not complicate the logic keeping the metrics in /// sync on drop. If someone adds a `#[derive(Clone)]` below, it is **wrong**. pub struct Sender { - inner: mpsc::UnboundedSender, + inner: async_channel::Sender, /// Name to identify the channel (e.g., in Prometheus and logs). name: &'static str, - /// Number of events in the queue. Clone of [`Receiver::in_transit`]. - // To not bother with ordering and possible underflow errors of the unsigned counter - // we just use `i64` and `Ordering::Relaxed`, and perceive `queue_size` as approximate. - // It can turn < 0 though. - queue_size: Arc, /// Threshold queue size to generate an error message in the logs. - queue_size_warning: i64, + queue_size_warning: usize, /// We generate the error message only once to not spam the logs. warning_fired: bool, /// Backtrace of a place where the channel was created. @@ -113,9 +103,8 @@ impl Drop for Sender { /// Receiving side of a channel. pub struct Receiver { - inner: mpsc::UnboundedReceiver, + inner: async_channel::Receiver, name: &'static str, - queue_size: Arc, /// Initially contains `None`, and will be set to a value once the corresponding [`Sender`] /// is assigned to an instance of [`OutChannels`]. metrics: Arc>>>>, @@ -126,7 +115,6 @@ impl Stream for Receiver { fn poll_next(mut self: Pin<&mut Self>, cx: &mut Context) -> Poll> { if let Some(ev) = ready!(Pin::new(&mut self.inner).poll_next(cx)) { - let _ = self.queue_size.fetch_sub(1, Ordering::Relaxed); let metrics = self.metrics.lock().clone(); match metrics.as_ref().map(|m| m.as_ref()) { Some(Some(metrics)) => metrics.event_out(&ev, self.name), @@ -191,17 +179,19 @@ impl OutChannels { /// Sends an event. pub fn send(&mut self, event: Event) { self.event_streams.retain_mut(|sender| { - let queue_size = sender.queue_size.fetch_add(1, Ordering::Relaxed); - if queue_size == sender.queue_size_warning && !sender.warning_fired { + if sender.inner.len() >= sender.queue_size_warning && !sender.warning_fired { sender.warning_fired = true; - sender.creation_backtrace.resolve(); error!( - "The number of unprocessed events in channel `{}` reached {}.\n\ - The channel was created at:\n{:?}", - sender.name, sender.queue_size_warning, sender.creation_backtrace, + "The number of unprocessed events in channel `{}` exceeded {}.\n\ + The channel was created at:\n{:}\n + The last event was sent from:\n{:}", + sender.name, + sender.queue_size_warning, + sender.creation_backtrace, + Backtrace::force_capture(), ); } - sender.inner.unbounded_send(event.clone()).is_ok() + sender.inner.try_send(event.clone()).is_ok() }); if let Some(metrics) = &*self.metrics { From 8ae4738bd7ee57556ea42c33600dc95488b58db6 Mon Sep 17 00:00:00 2001 From: Anthony Lazam Date: Wed, 1 Mar 2023 19:15:18 +0800 Subject: [PATCH 177/558] Removal of Prometheus alerting rules deployment in cloud-infra (#13499) --- .gitlab-ci.yml | 22 ---------------------- 1 file changed, 22 deletions(-) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 12a1ff825..336e97cdd 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -311,28 +311,6 @@ include: rules: - if: $PIPELINE != "automatic-crate-publishing" -#### stage: deploy - -deploy-prometheus-alerting-rules: - stage: deploy - needs: - - job: test-prometheus-alerting-rules - artifacts: false - allow_failure: true - trigger: - project: parity/infrastructure/cloud-infra - variables: - SUBSTRATE_CI_COMMIT_NAME: "${CI_COMMIT_REF_NAME}" - SUBSTRATE_CI_COMMIT_REF: "${CI_COMMIT_SHORT_SHA}" - UPSTREAM_TRIGGER_PROJECT: "${CI_PROJECT_PATH}" - rules: - - if: $CI_PIPELINE_SOURCE == "pipeline" - when: never - - if: $CI_COMMIT_REF_NAME == "master" - changes: - - .gitlab-ci.yml - - ./scripts/ci/monitoring/**/* - #### stage: notify # This job notifies rusty-cachier about the latest commit with the cache. From 39865603b6ff020395177becc32fc6744c364779 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Silva?= <123550+andresilva@users.noreply.github.com> Date: Wed, 1 Mar 2023 13:04:57 +0000 Subject: [PATCH 178/558] sp-consensus: remove unused error variants (#13495) --- Cargo.lock | 3 -- client/consensus/pow/src/lib.rs | 3 +- primitives/consensus/common/Cargo.toml | 5 -- primitives/consensus/common/src/error.rs | 69 +++++------------------- 4 files changed, 15 insertions(+), 65 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index f37a3b41f..b7c7a813f 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -9879,14 +9879,11 @@ dependencies = [ "async-trait", "futures", "log", - "parity-scale-codec", "sp-core", "sp-inherents", "sp-runtime", "sp-state-machine", - "sp-std", "sp-test-primitives", - "sp-version", "thiserror", ] diff --git a/client/consensus/pow/src/lib.rs b/client/consensus/pow/src/lib.rs index 44eaccd2a..513386fb2 100644 --- a/client/consensus/pow/src/lib.rs +++ b/client/consensus/pow/src/lib.rs @@ -331,7 +331,8 @@ where .select_chain .best_chain() .await - .map_err(|e| format!("Fetch best chain failed via select chain: {}", e))?; + .map_err(|e| format!("Fetch best chain failed via select chain: {}", e)) + .map_err(ConsensusError::ChainLookup)?; let best_hash = best_header.hash(); let parent_hash = *block.header.parent_hash(); diff --git a/primitives/consensus/common/Cargo.toml b/primitives/consensus/common/Cargo.toml index 83b09f9eb..117926134 100644 --- a/primitives/consensus/common/Cargo.toml +++ b/primitives/consensus/common/Cargo.toml @@ -15,9 +15,6 @@ targets = ["x86_64-unknown-linux-gnu"] [dependencies] async-trait = "0.1.57" -codec = { package = "parity-scale-codec", version = "3.2.2", features = [ - "derive", -] } futures = { version = "0.3.21", features = ["thread-pool"] } log = "0.4.17" thiserror = "1.0.30" @@ -25,8 +22,6 @@ sp-core = { version = "7.0.0", path = "../../core" } sp-inherents = { version = "4.0.0-dev", path = "../../inherents" } sp-runtime = { version = "7.0.0", path = "../../runtime" } sp-state-machine = { version = "0.13.0", path = "../../state-machine" } -sp-std = { version = "5.0.0", path = "../../std" } -sp-version = { version = "5.0.0", path = "../../version" } [dev-dependencies] futures = "0.3.21" diff --git a/primitives/consensus/common/src/error.rs b/primitives/consensus/common/src/error.rs index 33c1afa63..e881259da 100644 --- a/primitives/consensus/common/src/error.rs +++ b/primitives/consensus/common/src/error.rs @@ -15,85 +15,42 @@ // See the License for the specific language governing permissions and // limitations under the License. -//! Error types in Consensus -use sp_core::ed25519::Public; -use sp_version::RuntimeVersion; -use std::error; +//! Error types for consensus modules. /// Result type alias. pub type Result = std::result::Result; -/// Error type. +/// The error type for consensus-related operations. #[derive(Debug, thiserror::Error)] -#[non_exhaustive] pub enum Error { /// Missing state at block with given descriptor. #[error("State unavailable at block {0}")] StateUnavailable(String), - /// I/O terminated unexpectedly - #[error("I/O terminated unexpectedly.")] - IoTerminated, /// Intermediate missing. - #[error("Missing intermediate.")] + #[error("Missing intermediate")] NoIntermediate, /// Intermediate is of wrong type. - #[error("Invalid intermediate.")] + #[error("Invalid intermediate")] InvalidIntermediate, - /// Unable to schedule wake-up. - #[error("Timer error: {0}")] - FaultyTimer(#[from] std::io::Error), - /// Error while working with inherent data. - #[error("InherentData error: {0}")] - InherentData(#[from] sp_inherents::Error), - /// Unable to propose a block. - #[error("Unable to create block proposal.")] - CannotPropose, - /// Error checking signature - #[error("Message signature {0:?} by {1:?} is invalid.")] + /// Error checking signature. + #[error("Message signature {0:?} by {1:?} is invalid")] InvalidSignature(Vec, Vec), /// Invalid authorities set received from the runtime. #[error("Current state of blockchain has invalid authorities set")] InvalidAuthoritiesSet, - /// Account is not an authority. - #[error("Message sender {0:?} is not a valid authority")] - InvalidAuthority(Public), - /// Authoring interface does not match the runtime. - #[error( - "Authoring for current \ - runtime is not supported. Native ({native}) cannot author for on-chain ({on_chain})." - )] - IncompatibleAuthoringRuntime { native: RuntimeVersion, on_chain: RuntimeVersion }, - /// Authoring interface does not match the runtime. - #[error("Authoring for current runtime is not supported since it has no version.")] - RuntimeVersionMissing, - /// Authoring interface does not match the runtime. - #[error("Authoring in current build is not supported since it has no runtime.")] - NativeRuntimeMissing, /// Justification requirements not met. - #[error("Invalid justification.")] + #[error("Invalid justification")] InvalidJustification, - /// Some other error. - #[error(transparent)] - Other(#[from] Box), - /// Error from the client while importing + /// Error from the client while importing. #[error("Import failed: {0}")] ClientImport(String), - /// Error from the client while importing + /// Error from the client while fetching some data from the chain. #[error("Chain lookup failed: {0}")] ChainLookup(String), - /// Signing failed + /// Signing failed. #[error("Failed to sign using key: {0:?}. Reason: {1}")] CannotSign(Vec, String), -} - -impl From for Error { - fn from(p: Public) -> Self { - Self::InvalidAuthority(p) - } -} - -impl From for Error { - fn from(s: String) -> Self { - Self::StateUnavailable(s) - } + /// Some other error. + #[error(transparent)] + Other(#[from] Box), } From 376ada82d77503d099a61fd198a3089caf7920c9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Piotr=20Miko=C5=82ajczyk?= Date: Wed, 1 Mar 2023 14:35:32 +0100 Subject: [PATCH 179/558] Expose `ChargedAmount` (#13488) * Expose `ChargedAmount` * Fix imports --- frame/contracts/src/chain_extension.rs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/frame/contracts/src/chain_extension.rs b/frame/contracts/src/chain_extension.rs index 9b9454254..56d41a759 100644 --- a/frame/contracts/src/chain_extension.rs +++ b/frame/contracts/src/chain_extension.rs @@ -71,7 +71,6 @@ //! on how to use a chain extension in order to provide new features to ink! contracts. use crate::{ - gas::ChargedAmount, wasm::{Runtime, RuntimeCosts}, Error, }; @@ -80,7 +79,7 @@ use frame_support::weights::Weight; use sp_runtime::DispatchError; use sp_std::{marker::PhantomData, vec::Vec}; -pub use crate::{exec::Ext, Config}; +pub use crate::{exec::Ext, gas::ChargedAmount, Config}; pub use frame_system::Config as SysConfig; pub use pallet_contracts_primitives::ReturnFlags; From 024bfcedb15c8421a90f0834f73a7b529c502c0b Mon Sep 17 00:00:00 2001 From: Adrian Catangiu Date: Wed, 1 Mar 2023 18:04:09 +0200 Subject: [PATCH 180/558] sc-consensus-beefy: fix metrics: use correct names (#13494) Signed-off-by: acatangiu --- client/consensus/beefy/src/metrics.rs | 34 +++++++++++++++++++++------ 1 file changed, 27 insertions(+), 7 deletions(-) diff --git a/client/consensus/beefy/src/metrics.rs b/client/consensus/beefy/src/metrics.rs index 85438f1ca..0ce48e60e 100644 --- a/client/consensus/beefy/src/metrics.rs +++ b/client/consensus/beefy/src/metrics.rs @@ -18,7 +18,8 @@ //! BEEFY Prometheus metrics definition -use log::debug; +use crate::LOG_TARGET; +use log::{debug, error}; use prometheus::{register, Counter, Gauge, PrometheusError, Registry, U64}; /// Helper trait for registering BEEFY metrics to Prometheus registry. @@ -129,13 +130,13 @@ impl PrometheusRegister for VoterMetrics { )?, beefy_equivocation_votes: register( Counter::new( - "substrate_beefy_stale_votes", + "substrate_beefy_equivocation_votes", "Number of equivocation votes received", )?, registry, )?, beefy_invalid_votes: register( - Counter::new("substrate_beefy_stale_votes", "Number of invalid votes received")?, + Counter::new("substrate_beefy_invalid_votes", "Number of invalid votes received")?, registry, )?, beefy_stale_votes: register( @@ -200,14 +201,14 @@ impl PrometheusRegister for BlockImportMetrics { beefy_good_justification_imports: register( Counter::new( "substrate_beefy_good_justification_imports", - "Number of Good Justification imports", + "Number of good justifications on block-import", )?, registry, )?, beefy_bad_justification_imports: register( Counter::new( "substrate_beefy_bad_justification_imports", - "Number of Bad Justification imports", + "Number of bad justifications on block-import", )?, registry, )?, @@ -309,11 +310,16 @@ pub(crate) fn register_metrics( ) -> Option { prometheus_registry.as_ref().map(T::register).and_then(|result| match result { Ok(metrics) => { - debug!(target: "beefy", "🥩 Registered {} metrics", T::DESCRIPTION); + debug!(target: LOG_TARGET, "🥩 Registered {} metrics", T::DESCRIPTION); Some(metrics) }, Err(err) => { - debug!(target: "beefy", "🥩 Failed to register {} metrics: {:?}", T::DESCRIPTION, err); + error!( + target: LOG_TARGET, + "🥩 Failed to register {} metrics: {:?}", + T::DESCRIPTION, + err + ); None }, }) @@ -347,3 +353,17 @@ macro_rules! metric_get { $self.metrics.as_ref().map(|metrics| metrics.$m.clone()) }}; } + +#[cfg(test)] +pub(crate) mod tests { + use super::*; + + #[test] + fn should_register_metrics() { + let registry = Some(Registry::new()); + assert!(register_metrics::(registry.clone()).is_some()); + assert!(register_metrics::(registry.clone()).is_some()); + assert!(register_metrics::(registry.clone()).is_some()); + assert!(register_metrics::(registry.clone()).is_some()); + } +} From 4aa213ce1aef94f97836e7ceec753e2a131c849a Mon Sep 17 00:00:00 2001 From: Sergej Sakac <73715684+Szegoo@users.noreply.github.com> Date: Thu, 2 Mar 2023 11:16:47 +0100 Subject: [PATCH 181/558] Assets pallet: Giving the asset owner the ability to set minimum balance (#13486) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * set_min_balance * allow when new_min_balance < old_min_balance * add more specific event * Update frame/assets/src/lib.rs Co-authored-by: Bastian Köcher * Update frame/assets/src/lib.rs Co-authored-by: Bastian Köcher * ".git/.scripts/commands/bench/bench.sh" pallet dev pallet_assets * use actual weight --------- Co-authored-by: Bastian Köcher Co-authored-by: command-bot <> --- frame/assets/src/benchmarking.rs | 7 + frame/assets/src/lib.rs | 45 ++++ frame/assets/src/tests.rs | 29 ++ frame/assets/src/weights.rs | 442 ++++++++++++++++++------------- 4 files changed, 337 insertions(+), 186 deletions(-) diff --git a/frame/assets/src/benchmarking.rs b/frame/assets/src/benchmarking.rs index 4fe951f56..0538e955b 100644 --- a/frame/assets/src/benchmarking.rs +++ b/frame/assets/src/benchmarking.rs @@ -471,5 +471,12 @@ benchmarks_instance_pallet! { assert_last_event::(Event::ApprovalCancelled { asset_id: asset_id.into(), owner: caller, delegate }.into()); } + set_min_balance { + let (asset_id, caller, caller_lookup) = create_default_asset::(true); + }: _(SystemOrigin::Signed(caller.clone()), asset_id, 50u32.into()) + verify { + assert_last_event::(Event::AssetMinBalanceChanged { asset_id: asset_id.into(), new_min_balance: 50u32.into() }.into()); + } + impl_benchmark_test_suite!(Assets, crate::mock::new_test_ext(), crate::mock::Test) } diff --git a/frame/assets/src/lib.rs b/frame/assets/src/lib.rs index 894f8f3a4..96da07806 100644 --- a/frame/assets/src/lib.rs +++ b/frame/assets/src/lib.rs @@ -514,6 +514,8 @@ pub mod pallet { }, /// An asset has had its attributes changed by the `Force` origin. AssetStatusChanged { asset_id: T::AssetId }, + /// The min_balance of an asset has been updated by the asset owner. + AssetMinBalanceChanged { asset_id: T::AssetId, new_min_balance: T::Balance }, } #[pallet::error] @@ -1511,6 +1513,49 @@ pub mod pallet { let id: T::AssetId = id.into(); Self::do_refund(id, ensure_signed(origin)?, allow_burn) } + + /// Sets the minimum balance of an asset. + /// + /// Only works if there aren't any accounts that are holding the asset or if + /// the new value of `min_balance` is less than the old one. + /// + /// Origin must be Signed and the sender has to be the Owner of the + /// asset `id`. + /// + /// - `id`: The identifier of the asset. + /// - `min_balance`: The new value of `min_balance`. + /// + /// Emits `AssetMinBalanceChanged` event when successful. + #[pallet::call_index(28)] + #[pallet::weight(T::WeightInfo::set_min_balance())] + pub fn set_min_balance( + origin: OriginFor, + id: T::AssetIdParameter, + min_balance: T::Balance, + ) -> DispatchResult { + let origin = ensure_signed(origin)?; + let id: T::AssetId = id.into(); + + let mut details = Asset::::get(id).ok_or(Error::::Unknown)?; + ensure!(origin == details.owner, Error::::NoPermission); + + let old_min_balance = details.min_balance; + // Ensure that either the new min_balance is less than old min_balance or there aren't + // any accounts holding the asset. + ensure!( + min_balance < old_min_balance || details.accounts == 0, + Error::::NoPermission + ); + + details.min_balance = min_balance; + Asset::::insert(&id, details); + + Self::deposit_event(Event::AssetMinBalanceChanged { + asset_id: id, + new_min_balance: min_balance, + }); + Ok(()) + } } } diff --git a/frame/assets/src/tests.rs b/frame/assets/src/tests.rs index 6a3fd672a..e76e989ef 100644 --- a/frame/assets/src/tests.rs +++ b/frame/assets/src/tests.rs @@ -1091,6 +1091,35 @@ fn force_asset_status_should_work() { }); } +#[test] +fn set_min_balance_should_work() { + new_test_ext().execute_with(|| { + let id = 42; + Balances::make_free_balance_be(&1, 10); + assert_ok!(Assets::create(RuntimeOrigin::signed(1), id, 1, 30)); + + assert_ok!(Assets::mint(RuntimeOrigin::signed(1), id, 1, 50)); + // won't execute because there is an asset holder. + assert_noop!( + Assets::set_min_balance(RuntimeOrigin::signed(1), id, 50), + Error::::NoPermission + ); + + // will execute because the new value of min_balance is less than the + // old value. 10 < 30 + assert_ok!(Assets::mint(RuntimeOrigin::signed(1), id, 1, 10)); + + assert_ok!(Assets::burn(RuntimeOrigin::signed(1), id, 1, 50)); + assert_noop!( + Assets::set_min_balance(RuntimeOrigin::signed(2), id, 50), + Error::::NoPermission + ); + + assert_ok!(Assets::set_min_balance(RuntimeOrigin::signed(1), id, 50)); + assert_eq!(Asset::::get(id).unwrap().min_balance, 50); + }); +} + #[test] fn balance_conversion_should_work() { new_test_ext().execute_with(|| { diff --git a/frame/assets/src/weights.rs b/frame/assets/src/weights.rs index 9ebba6223..27de4fa33 100644 --- a/frame/assets/src/weights.rs +++ b/frame/assets/src/weights.rs @@ -18,25 +18,26 @@ //! Autogenerated weights for pallet_assets //! //! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev -//! DATE: 2023-01-24, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! DATE: 2023-02-28, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` //! WORST CASE MAP SIZE: `1000000` -//! HOSTNAME: `bm2`, CPU: `Intel(R) Core(TM) i7-7700K CPU @ 4.20GHz` +//! HOSTNAME: `bm3`, CPU: `Intel(R) Core(TM) i7-7700K CPU @ 4.20GHz` //! EXECUTION: Some(Wasm), WASM-EXECUTION: Compiled, CHAIN: Some("dev"), DB CACHE: 1024 // Executed Command: -// ./target/production/substrate +// target/production/substrate // benchmark // pallet -// --chain=dev // --steps=50 // --repeat=20 -// --pallet=pallet_assets // --extrinsic=* // --execution=wasm // --wasm-execution=compiled // --heap-pages=4096 -// --output=./frame/assets/src/weights.rs +// --json-file=/var/lib/gitlab-runner/builds/zyw4fam_/0/parity/mirrors/substrate/.git/.artifacts/bench.json +// --pallet=pallet_assets +// --chain=dev // --header=./HEADER-APACHE2 +// --output=./frame/assets/src/weights.rs // --template=./.maintain/frame-weight-template.hbs #![cfg_attr(rustfmt, rustfmt_skip)] @@ -74,6 +75,7 @@ pub trait WeightInfo { fn transfer_approved() -> Weight; fn cancel_approval() -> Weight; fn force_cancel_approval() -> Weight; + fn set_min_balance() -> Weight; } /// Weights for pallet_assets using the Substrate node and recommended hardware. @@ -86,9 +88,10 @@ impl WeightInfo for SubstrateWeight { fn create() -> Weight { // Proof Size summary in bytes: // Measured: `325` - // Estimated: `5288` - // Minimum execution time: 23_623 nanoseconds. - Weight::from_parts(24_072_000, 5288) + // Estimated: `7268` + // Minimum execution time: 27_887 nanoseconds. + Weight::from_ref_time(28_190_000) + .saturating_add(Weight::from_proof_size(7268)) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().writes(2_u64)) } @@ -97,9 +100,10 @@ impl WeightInfo for SubstrateWeight { fn force_create() -> Weight { // Proof Size summary in bytes: // Measured: `153` - // Estimated: `2685` - // Minimum execution time: 13_145 nanoseconds. - Weight::from_parts(13_572_000, 2685) + // Estimated: `3675` + // Minimum execution time: 15_059 nanoseconds. + Weight::from_ref_time(15_600_000) + .saturating_add(Weight::from_proof_size(3675)) .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -108,9 +112,10 @@ impl WeightInfo for SubstrateWeight { fn start_destroy() -> Weight { // Proof Size summary in bytes: // Measured: `417` - // Estimated: `2685` - // Minimum execution time: 13_420 nanoseconds. - Weight::from_parts(13_649_000, 2685) + // Estimated: `3675` + // Minimum execution time: 15_581 nanoseconds. + Weight::from_ref_time(15_868_000) + .saturating_add(Weight::from_proof_size(3675)) .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -124,11 +129,12 @@ impl WeightInfo for SubstrateWeight { fn destroy_accounts(c: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `25 + c * (240 ±0)` - // Estimated: `5262 + c * (5180 ±0)` - // Minimum execution time: 17_565 nanoseconds. - Weight::from_parts(17_757_000, 5262) - // Standard Error: 15_192 - .saturating_add(Weight::from_ref_time(13_799_167).saturating_mul(c.into())) + // Estimated: `8232 + c * (5180 ±0)` + // Minimum execution time: 20_167 nanoseconds. + Weight::from_ref_time(20_436_000) + .saturating_add(Weight::from_proof_size(8232)) + // Standard Error: 12_761 + .saturating_add(Weight::from_ref_time(15_535_268).saturating_mul(c.into())) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().reads((2_u64).saturating_mul(c.into()))) .saturating_add(T::DbWeight::get().writes(1_u64)) @@ -143,11 +149,12 @@ impl WeightInfo for SubstrateWeight { fn destroy_approvals(a: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `554 + a * (86 ±0)` - // Estimated: `5308 + a * (2623 ±0)` - // Minimum execution time: 18_251 nanoseconds. - Weight::from_parts(18_359_000, 5308) - // Standard Error: 10_051 - .saturating_add(Weight::from_ref_time(13_613_342).saturating_mul(a.into())) + // Estimated: `7288 + a * (2623 ±0)` + // Minimum execution time: 20_349 nanoseconds. + Weight::from_ref_time(20_482_000) + .saturating_add(Weight::from_proof_size(7288)) + // Standard Error: 9_831 + .saturating_add(Weight::from_ref_time(15_771_918).saturating_mul(a.into())) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().reads((1_u64).saturating_mul(a.into()))) .saturating_add(T::DbWeight::get().writes(1_u64)) @@ -161,9 +168,10 @@ impl WeightInfo for SubstrateWeight { fn finish_destroy() -> Weight { // Proof Size summary in bytes: // Measured: `383` - // Estimated: `5300` - // Minimum execution time: 14_344 nanoseconds. - Weight::from_parts(14_619_000, 5300) + // Estimated: `7280` + // Minimum execution time: 15_647 nanoseconds. + Weight::from_ref_time(15_975_000) + .saturating_add(Weight::from_proof_size(7280)) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -174,9 +182,10 @@ impl WeightInfo for SubstrateWeight { fn mint() -> Weight { // Proof Size summary in bytes: // Measured: `383` - // Estimated: `5262` - // Minimum execution time: 24_992 nanoseconds. - Weight::from_parts(25_784_000, 5262) + // Estimated: `7242` + // Minimum execution time: 28_383 nanoseconds. + Weight::from_ref_time(29_055_000) + .saturating_add(Weight::from_proof_size(7242)) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().writes(2_u64)) } @@ -187,9 +196,10 @@ impl WeightInfo for SubstrateWeight { fn burn() -> Weight { // Proof Size summary in bytes: // Measured: `491` - // Estimated: `5262` - // Minimum execution time: 31_233 nanoseconds. - Weight::from_parts(31_511_000, 5262) + // Estimated: `7242` + // Minimum execution time: 34_950 nanoseconds. + Weight::from_ref_time(35_296_000) + .saturating_add(Weight::from_proof_size(7242)) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().writes(2_u64)) } @@ -202,9 +212,10 @@ impl WeightInfo for SubstrateWeight { fn transfer() -> Weight { // Proof Size summary in bytes: // Measured: `530` - // Estimated: `10442` - // Minimum execution time: 43_002 nanoseconds. - Weight::from_parts(43_533_000, 10442) + // Estimated: `13412` + // Minimum execution time: 50_129 nanoseconds. + Weight::from_ref_time(50_820_000) + .saturating_add(Weight::from_proof_size(13412)) .saturating_add(T::DbWeight::get().reads(4_u64)) .saturating_add(T::DbWeight::get().writes(4_u64)) } @@ -217,9 +228,10 @@ impl WeightInfo for SubstrateWeight { fn transfer_keep_alive() -> Weight { // Proof Size summary in bytes: // Measured: `530` - // Estimated: `10442` - // Minimum execution time: 38_220 nanoseconds. - Weight::from_parts(38_639_000, 10442) + // Estimated: `13412` + // Minimum execution time: 43_424 nanoseconds. + Weight::from_ref_time(44_080_000) + .saturating_add(Weight::from_proof_size(13412)) .saturating_add(T::DbWeight::get().reads(4_u64)) .saturating_add(T::DbWeight::get().writes(4_u64)) } @@ -232,9 +244,10 @@ impl WeightInfo for SubstrateWeight { fn force_transfer() -> Weight { // Proof Size summary in bytes: // Measured: `530` - // Estimated: `10442` - // Minimum execution time: 43_171 nanoseconds. - Weight::from_parts(43_543_000, 10442) + // Estimated: `13412` + // Minimum execution time: 48_919 nanoseconds. + Weight::from_ref_time(50_720_000) + .saturating_add(Weight::from_proof_size(13412)) .saturating_add(T::DbWeight::get().reads(4_u64)) .saturating_add(T::DbWeight::get().writes(4_u64)) } @@ -245,9 +258,10 @@ impl WeightInfo for SubstrateWeight { fn freeze() -> Weight { // Proof Size summary in bytes: // Measured: `491` - // Estimated: `5262` - // Minimum execution time: 16_972 nanoseconds. - Weight::from_parts(17_498_000, 5262) + // Estimated: `7242` + // Minimum execution time: 19_750 nanoseconds. + Weight::from_ref_time(20_053_000) + .saturating_add(Weight::from_proof_size(7242)) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -258,9 +272,10 @@ impl WeightInfo for SubstrateWeight { fn thaw() -> Weight { // Proof Size summary in bytes: // Measured: `491` - // Estimated: `5262` - // Minimum execution time: 17_471 nanoseconds. - Weight::from_parts(17_842_000, 5262) + // Estimated: `7242` + // Minimum execution time: 19_672 nanoseconds. + Weight::from_ref_time(19_928_000) + .saturating_add(Weight::from_proof_size(7242)) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -269,9 +284,10 @@ impl WeightInfo for SubstrateWeight { fn freeze_asset() -> Weight { // Proof Size summary in bytes: // Measured: `417` - // Estimated: `2685` - // Minimum execution time: 13_497 nanoseconds. - Weight::from_parts(13_719_000, 2685) + // Estimated: `3675` + // Minimum execution time: 15_367 nanoseconds. + Weight::from_ref_time(15_726_000) + .saturating_add(Weight::from_proof_size(3675)) .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -280,9 +296,10 @@ impl WeightInfo for SubstrateWeight { fn thaw_asset() -> Weight { // Proof Size summary in bytes: // Measured: `417` - // Estimated: `2685` - // Minimum execution time: 13_424 nanoseconds. - Weight::from_parts(13_764_000, 2685) + // Estimated: `3675` + // Minimum execution time: 14_814 nanoseconds. + Weight::from_ref_time(15_301_000) + .saturating_add(Weight::from_proof_size(3675)) .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -293,9 +310,10 @@ impl WeightInfo for SubstrateWeight { fn transfer_ownership() -> Weight { // Proof Size summary in bytes: // Measured: `383` - // Estimated: `5300` - // Minimum execution time: 15_028 nanoseconds. - Weight::from_parts(15_366_000, 5300) + // Estimated: `7280` + // Minimum execution time: 17_426 nanoseconds. + Weight::from_ref_time(17_804_000) + .saturating_add(Weight::from_proof_size(7280)) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -304,9 +322,10 @@ impl WeightInfo for SubstrateWeight { fn set_team() -> Weight { // Proof Size summary in bytes: // Measured: `383` - // Estimated: `2685` - // Minimum execution time: 14_197 nanoseconds. - Weight::from_parts(14_437_000, 2685) + // Estimated: `3675` + // Minimum execution time: 15_935 nanoseconds. + Weight::from_ref_time(16_165_000) + .saturating_add(Weight::from_proof_size(3675)) .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -316,16 +335,15 @@ impl WeightInfo for SubstrateWeight { /// Proof: Assets Metadata (max_values: None, max_size: Some(140), added: 2615, mode: MaxEncodedLen) /// The range of component `n` is `[0, 50]`. /// The range of component `s` is `[0, 50]`. - fn set_metadata(n: u32, s: u32, ) -> Weight { + fn set_metadata(n: u32, _s: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `383` - // Estimated: `5300` - // Minimum execution time: 23_707 nanoseconds. - Weight::from_parts(24_466_834, 5300) - // Standard Error: 635 - .saturating_add(Weight::from_ref_time(730).saturating_mul(n.into())) - // Standard Error: 635 - .saturating_add(Weight::from_ref_time(3_975).saturating_mul(s.into())) + // Estimated: `7280` + // Minimum execution time: 26_890 nanoseconds. + Weight::from_ref_time(28_766_510) + .saturating_add(Weight::from_proof_size(7280)) + // Standard Error: 7_444 + .saturating_add(Weight::from_ref_time(3_619).saturating_mul(n.into())) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -336,9 +354,10 @@ impl WeightInfo for SubstrateWeight { fn clear_metadata() -> Weight { // Proof Size summary in bytes: // Measured: `579` - // Estimated: `5300` - // Minimum execution time: 23_997 nanoseconds. - Weight::from_parts(24_812_000, 5300) + // Estimated: `7280` + // Minimum execution time: 27_146 nanoseconds. + Weight::from_ref_time(27_692_000) + .saturating_add(Weight::from_proof_size(7280)) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -348,14 +367,13 @@ impl WeightInfo for SubstrateWeight { /// Proof: Assets Metadata (max_values: None, max_size: Some(140), added: 2615, mode: MaxEncodedLen) /// The range of component `n` is `[0, 50]`. /// The range of component `s` is `[0, 50]`. - fn force_set_metadata(_n: u32, s: u32, ) -> Weight { + fn force_set_metadata(_n: u32, _s: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `190` - // Estimated: `5300` - // Minimum execution time: 14_028 nanoseconds. - Weight::from_parts(15_217_669, 5300) - // Standard Error: 2_266 - .saturating_add(Weight::from_ref_time(2_045).saturating_mul(s.into())) + // Estimated: `7280` + // Minimum execution time: 16_181 nanoseconds. + Weight::from_ref_time(18_317_178) + .saturating_add(Weight::from_proof_size(7280)) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -366,9 +384,10 @@ impl WeightInfo for SubstrateWeight { fn force_clear_metadata() -> Weight { // Proof Size summary in bytes: // Measured: `579` - // Estimated: `5300` - // Minimum execution time: 23_754 nanoseconds. - Weight::from_parts(24_154_000, 5300) + // Estimated: `7280` + // Minimum execution time: 26_962 nanoseconds. + Weight::from_ref_time(27_896_000) + .saturating_add(Weight::from_proof_size(7280)) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -377,9 +396,10 @@ impl WeightInfo for SubstrateWeight { fn force_asset_status() -> Weight { // Proof Size summary in bytes: // Measured: `383` - // Estimated: `2685` - // Minimum execution time: 13_128 nanoseconds. - Weight::from_parts(13_428_000, 2685) + // Estimated: `3675` + // Minimum execution time: 14_394 nanoseconds. + Weight::from_ref_time(14_917_000) + .saturating_add(Weight::from_proof_size(3675)) .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -390,9 +410,10 @@ impl WeightInfo for SubstrateWeight { fn approve_transfer() -> Weight { // Proof Size summary in bytes: // Measured: `417` - // Estimated: `5308` - // Minimum execution time: 27_224 nanoseconds. - Weight::from_parts(27_665_000, 5308) + // Estimated: `7288` + // Minimum execution time: 30_861 nanoseconds. + Weight::from_ref_time(31_356_000) + .saturating_add(Weight::from_proof_size(7288)) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().writes(2_u64)) } @@ -407,9 +428,10 @@ impl WeightInfo for SubstrateWeight { fn transfer_approved() -> Weight { // Proof Size summary in bytes: // Measured: `700` - // Estimated: `13065` - // Minimum execution time: 55_837 nanoseconds. - Weight::from_parts(56_636_000, 13065) + // Estimated: `17025` + // Minimum execution time: 64_510 nanoseconds. + Weight::from_ref_time(65_676_000) + .saturating_add(Weight::from_proof_size(17025)) .saturating_add(T::DbWeight::get().reads(5_u64)) .saturating_add(T::DbWeight::get().writes(5_u64)) } @@ -420,9 +442,10 @@ impl WeightInfo for SubstrateWeight { fn cancel_approval() -> Weight { // Proof Size summary in bytes: // Measured: `587` - // Estimated: `5308` - // Minimum execution time: 28_915 nanoseconds. - Weight::from_parts(29_325_000, 5308) + // Estimated: `7288` + // Minimum execution time: 32_620 nanoseconds. + Weight::from_ref_time(33_183_000) + .saturating_add(Weight::from_proof_size(7288)) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().writes(2_u64)) } @@ -433,12 +456,25 @@ impl WeightInfo for SubstrateWeight { fn force_cancel_approval() -> Weight { // Proof Size summary in bytes: // Measured: `587` - // Estimated: `5308` - // Minimum execution time: 29_103 nanoseconds. - Weight::from_parts(29_599_000, 5308) + // Estimated: `7288` + // Minimum execution time: 33_277 nanoseconds. + Weight::from_ref_time(34_438_000) + .saturating_add(Weight::from_proof_size(7288)) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().writes(2_u64)) } + /// Storage: Assets Asset (r:1 w:1) + /// Proof: Assets Asset (max_values: None, max_size: Some(210), added: 2685, mode: MaxEncodedLen) + fn set_min_balance() -> Weight { + // Proof Size summary in bytes: + // Measured: `383` + // Estimated: `3675` + // Minimum execution time: 16_213 nanoseconds. + Weight::from_ref_time(16_575_000) + .saturating_add(Weight::from_proof_size(3675)) + .saturating_add(T::DbWeight::get().reads(1_u64)) + .saturating_add(T::DbWeight::get().writes(1_u64)) + } } // For backwards compatibility and tests @@ -450,9 +486,10 @@ impl WeightInfo for () { fn create() -> Weight { // Proof Size summary in bytes: // Measured: `325` - // Estimated: `5288` - // Minimum execution time: 23_623 nanoseconds. - Weight::from_parts(24_072_000, 5288) + // Estimated: `7268` + // Minimum execution time: 27_887 nanoseconds. + Weight::from_ref_time(28_190_000) + .saturating_add(Weight::from_proof_size(7268)) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().writes(2_u64)) } @@ -461,9 +498,10 @@ impl WeightInfo for () { fn force_create() -> Weight { // Proof Size summary in bytes: // Measured: `153` - // Estimated: `2685` - // Minimum execution time: 13_145 nanoseconds. - Weight::from_parts(13_572_000, 2685) + // Estimated: `3675` + // Minimum execution time: 15_059 nanoseconds. + Weight::from_ref_time(15_600_000) + .saturating_add(Weight::from_proof_size(3675)) .saturating_add(RocksDbWeight::get().reads(1_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } @@ -472,9 +510,10 @@ impl WeightInfo for () { fn start_destroy() -> Weight { // Proof Size summary in bytes: // Measured: `417` - // Estimated: `2685` - // Minimum execution time: 13_420 nanoseconds. - Weight::from_parts(13_649_000, 2685) + // Estimated: `3675` + // Minimum execution time: 15_581 nanoseconds. + Weight::from_ref_time(15_868_000) + .saturating_add(Weight::from_proof_size(3675)) .saturating_add(RocksDbWeight::get().reads(1_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } @@ -488,11 +527,12 @@ impl WeightInfo for () { fn destroy_accounts(c: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `25 + c * (240 ±0)` - // Estimated: `5262 + c * (5180 ±0)` - // Minimum execution time: 17_565 nanoseconds. - Weight::from_parts(17_757_000, 5262) - // Standard Error: 15_192 - .saturating_add(Weight::from_ref_time(13_799_167).saturating_mul(c.into())) + // Estimated: `8232 + c * (5180 ±0)` + // Minimum execution time: 20_167 nanoseconds. + Weight::from_ref_time(20_436_000) + .saturating_add(Weight::from_proof_size(8232)) + // Standard Error: 12_761 + .saturating_add(Weight::from_ref_time(15_535_268).saturating_mul(c.into())) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().reads((2_u64).saturating_mul(c.into()))) .saturating_add(RocksDbWeight::get().writes(1_u64)) @@ -507,11 +547,12 @@ impl WeightInfo for () { fn destroy_approvals(a: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `554 + a * (86 ±0)` - // Estimated: `5308 + a * (2623 ±0)` - // Minimum execution time: 18_251 nanoseconds. - Weight::from_parts(18_359_000, 5308) - // Standard Error: 10_051 - .saturating_add(Weight::from_ref_time(13_613_342).saturating_mul(a.into())) + // Estimated: `7288 + a * (2623 ±0)` + // Minimum execution time: 20_349 nanoseconds. + Weight::from_ref_time(20_482_000) + .saturating_add(Weight::from_proof_size(7288)) + // Standard Error: 9_831 + .saturating_add(Weight::from_ref_time(15_771_918).saturating_mul(a.into())) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().reads((1_u64).saturating_mul(a.into()))) .saturating_add(RocksDbWeight::get().writes(1_u64)) @@ -525,9 +566,10 @@ impl WeightInfo for () { fn finish_destroy() -> Weight { // Proof Size summary in bytes: // Measured: `383` - // Estimated: `5300` - // Minimum execution time: 14_344 nanoseconds. - Weight::from_parts(14_619_000, 5300) + // Estimated: `7280` + // Minimum execution time: 15_647 nanoseconds. + Weight::from_ref_time(15_975_000) + .saturating_add(Weight::from_proof_size(7280)) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } @@ -538,9 +580,10 @@ impl WeightInfo for () { fn mint() -> Weight { // Proof Size summary in bytes: // Measured: `383` - // Estimated: `5262` - // Minimum execution time: 24_992 nanoseconds. - Weight::from_parts(25_784_000, 5262) + // Estimated: `7242` + // Minimum execution time: 28_383 nanoseconds. + Weight::from_ref_time(29_055_000) + .saturating_add(Weight::from_proof_size(7242)) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().writes(2_u64)) } @@ -551,9 +594,10 @@ impl WeightInfo for () { fn burn() -> Weight { // Proof Size summary in bytes: // Measured: `491` - // Estimated: `5262` - // Minimum execution time: 31_233 nanoseconds. - Weight::from_parts(31_511_000, 5262) + // Estimated: `7242` + // Minimum execution time: 34_950 nanoseconds. + Weight::from_ref_time(35_296_000) + .saturating_add(Weight::from_proof_size(7242)) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().writes(2_u64)) } @@ -566,9 +610,10 @@ impl WeightInfo for () { fn transfer() -> Weight { // Proof Size summary in bytes: // Measured: `530` - // Estimated: `10442` - // Minimum execution time: 43_002 nanoseconds. - Weight::from_parts(43_533_000, 10442) + // Estimated: `13412` + // Minimum execution time: 50_129 nanoseconds. + Weight::from_ref_time(50_820_000) + .saturating_add(Weight::from_proof_size(13412)) .saturating_add(RocksDbWeight::get().reads(4_u64)) .saturating_add(RocksDbWeight::get().writes(4_u64)) } @@ -581,9 +626,10 @@ impl WeightInfo for () { fn transfer_keep_alive() -> Weight { // Proof Size summary in bytes: // Measured: `530` - // Estimated: `10442` - // Minimum execution time: 38_220 nanoseconds. - Weight::from_parts(38_639_000, 10442) + // Estimated: `13412` + // Minimum execution time: 43_424 nanoseconds. + Weight::from_ref_time(44_080_000) + .saturating_add(Weight::from_proof_size(13412)) .saturating_add(RocksDbWeight::get().reads(4_u64)) .saturating_add(RocksDbWeight::get().writes(4_u64)) } @@ -596,9 +642,10 @@ impl WeightInfo for () { fn force_transfer() -> Weight { // Proof Size summary in bytes: // Measured: `530` - // Estimated: `10442` - // Minimum execution time: 43_171 nanoseconds. - Weight::from_parts(43_543_000, 10442) + // Estimated: `13412` + // Minimum execution time: 48_919 nanoseconds. + Weight::from_ref_time(50_720_000) + .saturating_add(Weight::from_proof_size(13412)) .saturating_add(RocksDbWeight::get().reads(4_u64)) .saturating_add(RocksDbWeight::get().writes(4_u64)) } @@ -609,9 +656,10 @@ impl WeightInfo for () { fn freeze() -> Weight { // Proof Size summary in bytes: // Measured: `491` - // Estimated: `5262` - // Minimum execution time: 16_972 nanoseconds. - Weight::from_parts(17_498_000, 5262) + // Estimated: `7242` + // Minimum execution time: 19_750 nanoseconds. + Weight::from_ref_time(20_053_000) + .saturating_add(Weight::from_proof_size(7242)) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } @@ -622,9 +670,10 @@ impl WeightInfo for () { fn thaw() -> Weight { // Proof Size summary in bytes: // Measured: `491` - // Estimated: `5262` - // Minimum execution time: 17_471 nanoseconds. - Weight::from_parts(17_842_000, 5262) + // Estimated: `7242` + // Minimum execution time: 19_672 nanoseconds. + Weight::from_ref_time(19_928_000) + .saturating_add(Weight::from_proof_size(7242)) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } @@ -633,9 +682,10 @@ impl WeightInfo for () { fn freeze_asset() -> Weight { // Proof Size summary in bytes: // Measured: `417` - // Estimated: `2685` - // Minimum execution time: 13_497 nanoseconds. - Weight::from_parts(13_719_000, 2685) + // Estimated: `3675` + // Minimum execution time: 15_367 nanoseconds. + Weight::from_ref_time(15_726_000) + .saturating_add(Weight::from_proof_size(3675)) .saturating_add(RocksDbWeight::get().reads(1_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } @@ -644,9 +694,10 @@ impl WeightInfo for () { fn thaw_asset() -> Weight { // Proof Size summary in bytes: // Measured: `417` - // Estimated: `2685` - // Minimum execution time: 13_424 nanoseconds. - Weight::from_parts(13_764_000, 2685) + // Estimated: `3675` + // Minimum execution time: 14_814 nanoseconds. + Weight::from_ref_time(15_301_000) + .saturating_add(Weight::from_proof_size(3675)) .saturating_add(RocksDbWeight::get().reads(1_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } @@ -657,9 +708,10 @@ impl WeightInfo for () { fn transfer_ownership() -> Weight { // Proof Size summary in bytes: // Measured: `383` - // Estimated: `5300` - // Minimum execution time: 15_028 nanoseconds. - Weight::from_parts(15_366_000, 5300) + // Estimated: `7280` + // Minimum execution time: 17_426 nanoseconds. + Weight::from_ref_time(17_804_000) + .saturating_add(Weight::from_proof_size(7280)) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } @@ -668,9 +720,10 @@ impl WeightInfo for () { fn set_team() -> Weight { // Proof Size summary in bytes: // Measured: `383` - // Estimated: `2685` - // Minimum execution time: 14_197 nanoseconds. - Weight::from_parts(14_437_000, 2685) + // Estimated: `3675` + // Minimum execution time: 15_935 nanoseconds. + Weight::from_ref_time(16_165_000) + .saturating_add(Weight::from_proof_size(3675)) .saturating_add(RocksDbWeight::get().reads(1_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } @@ -680,16 +733,15 @@ impl WeightInfo for () { /// Proof: Assets Metadata (max_values: None, max_size: Some(140), added: 2615, mode: MaxEncodedLen) /// The range of component `n` is `[0, 50]`. /// The range of component `s` is `[0, 50]`. - fn set_metadata(n: u32, s: u32, ) -> Weight { + fn set_metadata(n: u32, _s: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `383` - // Estimated: `5300` - // Minimum execution time: 23_707 nanoseconds. - Weight::from_parts(24_466_834, 5300) - // Standard Error: 635 - .saturating_add(Weight::from_ref_time(730).saturating_mul(n.into())) - // Standard Error: 635 - .saturating_add(Weight::from_ref_time(3_975).saturating_mul(s.into())) + // Estimated: `7280` + // Minimum execution time: 26_890 nanoseconds. + Weight::from_ref_time(28_766_510) + .saturating_add(Weight::from_proof_size(7280)) + // Standard Error: 7_444 + .saturating_add(Weight::from_ref_time(3_619).saturating_mul(n.into())) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } @@ -700,9 +752,10 @@ impl WeightInfo for () { fn clear_metadata() -> Weight { // Proof Size summary in bytes: // Measured: `579` - // Estimated: `5300` - // Minimum execution time: 23_997 nanoseconds. - Weight::from_parts(24_812_000, 5300) + // Estimated: `7280` + // Minimum execution time: 27_146 nanoseconds. + Weight::from_ref_time(27_692_000) + .saturating_add(Weight::from_proof_size(7280)) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } @@ -712,14 +765,13 @@ impl WeightInfo for () { /// Proof: Assets Metadata (max_values: None, max_size: Some(140), added: 2615, mode: MaxEncodedLen) /// The range of component `n` is `[0, 50]`. /// The range of component `s` is `[0, 50]`. - fn force_set_metadata(_n: u32, s: u32, ) -> Weight { + fn force_set_metadata(_n: u32, _s: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `190` - // Estimated: `5300` - // Minimum execution time: 14_028 nanoseconds. - Weight::from_parts(15_217_669, 5300) - // Standard Error: 2_266 - .saturating_add(Weight::from_ref_time(2_045).saturating_mul(s.into())) + // Estimated: `7280` + // Minimum execution time: 16_181 nanoseconds. + Weight::from_ref_time(18_317_178) + .saturating_add(Weight::from_proof_size(7280)) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } @@ -730,9 +782,10 @@ impl WeightInfo for () { fn force_clear_metadata() -> Weight { // Proof Size summary in bytes: // Measured: `579` - // Estimated: `5300` - // Minimum execution time: 23_754 nanoseconds. - Weight::from_parts(24_154_000, 5300) + // Estimated: `7280` + // Minimum execution time: 26_962 nanoseconds. + Weight::from_ref_time(27_896_000) + .saturating_add(Weight::from_proof_size(7280)) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } @@ -741,9 +794,10 @@ impl WeightInfo for () { fn force_asset_status() -> Weight { // Proof Size summary in bytes: // Measured: `383` - // Estimated: `2685` - // Minimum execution time: 13_128 nanoseconds. - Weight::from_parts(13_428_000, 2685) + // Estimated: `3675` + // Minimum execution time: 14_394 nanoseconds. + Weight::from_ref_time(14_917_000) + .saturating_add(Weight::from_proof_size(3675)) .saturating_add(RocksDbWeight::get().reads(1_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } @@ -754,9 +808,10 @@ impl WeightInfo for () { fn approve_transfer() -> Weight { // Proof Size summary in bytes: // Measured: `417` - // Estimated: `5308` - // Minimum execution time: 27_224 nanoseconds. - Weight::from_parts(27_665_000, 5308) + // Estimated: `7288` + // Minimum execution time: 30_861 nanoseconds. + Weight::from_ref_time(31_356_000) + .saturating_add(Weight::from_proof_size(7288)) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().writes(2_u64)) } @@ -771,9 +826,10 @@ impl WeightInfo for () { fn transfer_approved() -> Weight { // Proof Size summary in bytes: // Measured: `700` - // Estimated: `13065` - // Minimum execution time: 55_837 nanoseconds. - Weight::from_parts(56_636_000, 13065) + // Estimated: `17025` + // Minimum execution time: 64_510 nanoseconds. + Weight::from_ref_time(65_676_000) + .saturating_add(Weight::from_proof_size(17025)) .saturating_add(RocksDbWeight::get().reads(5_u64)) .saturating_add(RocksDbWeight::get().writes(5_u64)) } @@ -784,9 +840,10 @@ impl WeightInfo for () { fn cancel_approval() -> Weight { // Proof Size summary in bytes: // Measured: `587` - // Estimated: `5308` - // Minimum execution time: 28_915 nanoseconds. - Weight::from_parts(29_325_000, 5308) + // Estimated: `7288` + // Minimum execution time: 32_620 nanoseconds. + Weight::from_ref_time(33_183_000) + .saturating_add(Weight::from_proof_size(7288)) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().writes(2_u64)) } @@ -797,10 +854,23 @@ impl WeightInfo for () { fn force_cancel_approval() -> Weight { // Proof Size summary in bytes: // Measured: `587` - // Estimated: `5308` - // Minimum execution time: 29_103 nanoseconds. - Weight::from_parts(29_599_000, 5308) + // Estimated: `7288` + // Minimum execution time: 33_277 nanoseconds. + Weight::from_ref_time(34_438_000) + .saturating_add(Weight::from_proof_size(7288)) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().writes(2_u64)) } + /// Storage: Assets Asset (r:1 w:1) + /// Proof: Assets Asset (max_values: None, max_size: Some(210), added: 2685, mode: MaxEncodedLen) + fn set_min_balance() -> Weight { + // Proof Size summary in bytes: + // Measured: `383` + // Estimated: `3675` + // Minimum execution time: 16_213 nanoseconds. + Weight::from_ref_time(16_575_000) + .saturating_add(Weight::from_proof_size(3675)) + .saturating_add(RocksDbWeight::get().reads(1_u64)) + .saturating_add(RocksDbWeight::get().writes(1_u64)) + } } From 7d00033cbec0e2e3b50f7a42dd253ae55972371b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bastian=20K=C3=B6cher?= Date: Thu, 2 Mar 2023 12:06:46 +0100 Subject: [PATCH 182/558] network-gossip: Do not report peer on duplicate message if its the first time (#13508) Two peers can send us the same gossip message. Before this pr we would have reported the second peer for sending a duplicate message. However, this isn't correct if we haven't seen the message from this peer yet. So, we should not report them as they can not be aware of our internal state. --- client/network-gossip/src/state_machine.rs | 36 +++++++++++++++++++++- 1 file changed, 35 insertions(+), 1 deletion(-) diff --git a/client/network-gossip/src/state_machine.rs b/client/network-gossip/src/state_machine.rs index 48a1a2e53..7cd5b5613 100644 --- a/client/network-gossip/src/state_machine.rs +++ b/client/network-gossip/src/state_machine.rs @@ -354,7 +354,15 @@ impl ConsensusGossip { protocol = %self.protocol, "Ignored already known message", ); - network.report_peer(who, rep::DUPLICATE_GOSSIP); + + // If the peer already send us the message once, let's report them. + if self + .peers + .get_mut(&who) + .map_or(false, |p| !p.known_messages.insert(message_hash)) + { + network.report_peer(who, rep::DUPLICATE_GOSSIP); + } continue } @@ -814,4 +822,30 @@ mod tests { to_forward, ); } + + // Two peers can send us the same gossip message. We should not report the second peer + // sending the gossip message as long as its the first time the peer send us this message. + #[test] + fn do_not_report_peer_for_first_time_duplicate_gossip_message() { + let mut consensus = ConsensusGossip::::new(Arc::new(AllowAll), "/foo".into(), None); + + let mut network = NoOpNetwork::default(); + + let peer_id = PeerId::random(); + consensus.new_peer(&mut network, peer_id, ObservedRole::Full); + assert!(consensus.peers.contains_key(&peer_id)); + + let peer_id2 = PeerId::random(); + consensus.new_peer(&mut network, peer_id2, ObservedRole::Full); + assert!(consensus.peers.contains_key(&peer_id2)); + + let message = vec![vec![1, 2, 3]]; + consensus.on_incoming(&mut network, peer_id, message.clone()); + consensus.on_incoming(&mut network, peer_id2, message.clone()); + + assert_eq!( + vec![(peer_id, rep::GOSSIP_SUCCESS)], + network.inner.lock().unwrap().peer_reports + ); + } } From df93a0d673a786063f2c9e4b110b9a6194a76adf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Silva?= <123550+andresilva@users.noreply.github.com> Date: Thu, 2 Mar 2023 12:38:12 +0000 Subject: [PATCH 183/558] im-online: don't disable offending validators (#13493) --- frame/im-online/src/lib.rs | 6 +++++- frame/im-online/src/tests.rs | 3 +++ 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/frame/im-online/src/lib.rs b/frame/im-online/src/lib.rs index cdac51c94..39762945c 100644 --- a/frame/im-online/src/lib.rs +++ b/frame/im-online/src/lib.rs @@ -101,7 +101,7 @@ use sp_runtime::{ PerThing, Perbill, Permill, RuntimeDebug, SaturatedConversion, }; use sp_staking::{ - offence::{Kind, Offence, ReportOffence}, + offence::{DisableStrategy, Kind, Offence, ReportOffence}, SessionIndex, }; use sp_std::prelude::*; @@ -951,6 +951,10 @@ impl Offence for UnresponsivenessOffence { self.session_index } + fn disable_strategy(&self) -> DisableStrategy { + DisableStrategy::Never + } + fn slash_fraction(&self, offenders: u32) -> Perbill { // the formula is min((3 * (k - (n / 10 + 1))) / n, 1) * 0.07 // basically, 10% can be offline with no slash, but after that, it linearly climbs up to 7% diff --git a/frame/im-online/src/tests.rs b/frame/im-online/src/tests.rs index 8cc7f38eb..80320959c 100644 --- a/frame/im-online/src/tests.rs +++ b/frame/im-online/src/tests.rs @@ -56,6 +56,9 @@ fn test_unresponsiveness_slash_fraction() { dummy_offence.slash_fraction(17), Perbill::from_parts(46200000), // 4.62% ); + + // Offline offences should never lead to being disabled. + assert_eq!(dummy_offence.disable_strategy(), DisableStrategy::Never); } #[test] From d1d67cddf577ef275a2ca339312805c6d25ef9e5 Mon Sep 17 00:00:00 2001 From: Jegor Sidorenko <5252494+jsidorenko@users.noreply.github.com> Date: Thu, 2 Mar 2023 15:58:41 +0200 Subject: [PATCH 184/558] Return account's asset balances (#13352) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Runtime method to get user's assets balances * Fix test (typo) * Update frame/assets/src/functions.rs * Remove instance param * Update frame/assets/src/functions.rs Co-authored-by: Bastian Köcher * Remove instance param * Refactor * Chore * Update doc --------- Co-authored-by: Bastian Köcher Co-authored-by: parity-processbot <> --- bin/node/runtime/src/assets_api.rs | 34 ++++++++++++++++++++++++++++++ bin/node/runtime/src/lib.rs | 15 +++++++++++++ frame/assets/src/functions.rs | 7 ++++++ frame/assets/src/tests.rs | 5 ++++- 4 files changed, 60 insertions(+), 1 deletion(-) create mode 100644 bin/node/runtime/src/assets_api.rs diff --git a/bin/node/runtime/src/assets_api.rs b/bin/node/runtime/src/assets_api.rs new file mode 100644 index 000000000..cf1a663d7 --- /dev/null +++ b/bin/node/runtime/src/assets_api.rs @@ -0,0 +1,34 @@ +// This file is part of Substrate. + +// Copyright (C) 2018-2022 Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +//! Runtime API definition for assets. + +use codec::Codec; +use sp_std::vec::Vec; + +sp_api::decl_runtime_apis! { + pub trait AssetsApi + where + AccountId: Codec, + AssetBalance: Codec, + AssetId: Codec, + { + /// Returns the list of `AssetId`s and corresponding balance that an `AccountId` has. + fn account_balances(account: AccountId) -> Vec<(AssetId, AssetBalance)>; + } +} diff --git a/bin/node/runtime/src/lib.rs b/bin/node/runtime/src/lib.rs index f8589036f..9b317926a 100644 --- a/bin/node/runtime/src/lib.rs +++ b/bin/node/runtime/src/lib.rs @@ -105,6 +105,9 @@ use sp_runtime::generic::Era; /// Generated voter bag information. mod voter_bags; +/// Runtime API definition for assets. +pub mod assets_api; + // Make the WASM binary available. #[cfg(feature = "std")] include!(concat!(env!("OUT_DIR"), "/wasm_binary.rs")); @@ -2072,6 +2075,18 @@ impl_runtime_apis! { } } + impl assets_api::AssetsApi< + Block, + AccountId, + Balance, + u32, + > for Runtime + { + fn account_balances(account: AccountId) -> Vec<(u32, Balance)> { + Assets::account_balances(account) + } + } + impl pallet_contracts::ContractsApi for Runtime { fn call( diff --git a/frame/assets/src/functions.rs b/frame/assets/src/functions.rs index f970ce6cf..f6e027472 100644 --- a/frame/assets/src/functions.rs +++ b/frame/assets/src/functions.rs @@ -924,4 +924,11 @@ impl, I: 'static> Pallet { Ok(()) }) } + + /// Returns all the non-zero balances for all assets of the given `account`. + pub fn account_balances(account: T::AccountId) -> Vec<(T::AssetId, T::Balance)> { + Asset::::iter_keys() + .filter_map(|id| Self::maybe_balance(id, account.clone()).map(|balance| (id, balance))) + .collect::>() + } } diff --git a/frame/assets/src/tests.rs b/frame/assets/src/tests.rs index e76e989ef..e0f612e5c 100644 --- a/frame/assets/src/tests.rs +++ b/frame/assets/src/tests.rs @@ -37,11 +37,14 @@ fn asset_ids() -> Vec { fn basic_minting_should_work() { new_test_ext().execute_with(|| { assert_ok!(Assets::force_create(RuntimeOrigin::root(), 0, 1, true, 1)); + assert_ok!(Assets::force_create(RuntimeOrigin::root(), 1, 1, true, 1)); assert_ok!(Assets::mint(RuntimeOrigin::signed(1), 0, 1, 100)); assert_eq!(Assets::balance(0, 1), 100); assert_ok!(Assets::mint(RuntimeOrigin::signed(1), 0, 2, 100)); assert_eq!(Assets::balance(0, 2), 100); - assert_eq!(asset_ids(), vec![0, 999]); + assert_eq!(asset_ids(), vec![0, 1, 999]); + assert_ok!(Assets::mint(RuntimeOrigin::signed(1), 1, 1, 100)); + assert_eq!(Assets::account_balances(1), vec![(0, 100), (999, 100), (1, 100)]); }); } From 6e81199eac74ce9a7bf3b1c720989031dd9b18a1 Mon Sep 17 00:00:00 2001 From: Oliver Tale-Yazdi Date: Thu, 2 Mar 2023 22:28:17 +0100 Subject: [PATCH 185/558] Deprecate `Weight::from_{ref_time, proof_size}` (#13475) * Deprecate Weight::from_{ref_time, proof_size} Signed-off-by: Oliver Tale-Yazdi * Update templates Signed-off-by: Oliver Tale-Yazdi * Use from_parts Signed-off-by: Oliver Tale-Yazdi * Use from_parts Signed-off-by: Oliver Tale-Yazdi * Dont revert comment :facepalm: Signed-off-by: Oliver Tale-Yazdi * ".git/.scripts/commands/bench/bench.sh" pallet dev pallet_balances * Update weight files Signed-off-by: Oliver Tale-Yazdi * More fixes Signed-off-by: Oliver Tale-Yazdi * Adapt to Master changes Signed-off-by: Oliver Tale-Yazdi --------- Signed-off-by: Oliver Tale-Yazdi Co-authored-by: command-bot <> --- .maintain/frame-weight-template.hbs | 18 +- bin/node/executor/tests/basic.rs | 4 +- bin/node/runtime/src/impls.rs | 20 +- frame/alliance/src/weights.rs | 152 +- frame/assets/src/weights.rs | 236 +-- frame/babe/src/default_weights.rs | 11 +- frame/balances/src/tests.rs | 8 +- frame/balances/src/tests_composite.rs | 2 +- frame/balances/src/tests_local.rs | 2 +- frame/balances/src/tests_reentrancy.rs | 2 +- frame/balances/src/weights.rs | 56 +- frame/beefy/src/default_weights.rs | 11 +- frame/benchmarking/pov/src/weights.rs | 348 ++-- frame/benchmarking/src/weights.rs | 28 +- frame/bounties/src/weights.rs | 12 +- frame/child-bounties/src/tests.rs | 2 +- frame/child-bounties/src/weights.rs | 4 +- frame/collective/src/tests.rs | 6 +- frame/collective/src/weights.rs | 156 +- frame/contracts/src/gas.rs | 12 +- frame/contracts/src/tests.rs | 6 +- frame/contracts/src/wasm/runtime.rs | 8 +- frame/contracts/src/weights.rs | 1640 ++++++++--------- frame/conviction-voting/src/weights.rs | 16 +- frame/democracy/src/weights.rs | 60 +- .../election-provider-multi-phase/src/mock.rs | 5 +- .../src/signed.rs | 8 +- .../src/unsigned.rs | 260 +-- .../src/weights.rs | 56 +- .../election-provider-support/src/weights.rs | 32 +- frame/elections-phragmen/src/weights.rs | 72 +- frame/examples/basic/src/lib.rs | 2 +- frame/examples/basic/src/weights.rs | 16 +- frame/executive/src/lib.rs | 24 +- frame/fast-unstake/src/lib.rs | 4 +- frame/fast-unstake/src/tests.rs | 8 +- frame/fast-unstake/src/weights.rs | 32 +- frame/glutton/src/tests.rs | 2 +- frame/glutton/src/weights.rs | 92 +- frame/grandpa/src/default_weights.rs | 13 +- frame/identity/src/weights.rs | 100 +- frame/im-online/src/weights.rs | 16 +- frame/lottery/src/weights.rs | 8 +- frame/membership/src/weights.rs | 56 +- .../src/default_weights.rs | 2 +- frame/message-queue/src/weights.rs | 80 +- frame/multisig/src/weights.rs | 44 +- frame/nfts/src/weights.rs | 364 ++-- frame/nis/src/mock.rs | 2 +- frame/nis/src/weights.rs | 12 +- frame/node-authorization/src/weights.rs | 18 +- frame/nomination-pools/src/weights.rs | 148 +- frame/preimage/src/weights.rs | 12 +- frame/proxy/src/weights.rs | 56 +- frame/ranked-collective/src/weights.rs | 20 +- frame/recovery/src/weights.rs | 24 +- frame/remark/src/weights.rs | 8 +- frame/scheduler/src/mock.rs | 26 +- frame/scheduler/src/tests.rs | 134 +- frame/scheduler/src/weights.rs | 48 +- frame/staking/src/pallet/impls.rs | 2 +- frame/staking/src/weights.rs | 144 +- frame/state-trie-migration/src/lib.rs | 14 +- frame/state-trie-migration/src/weights.rs | 16 +- frame/sudo/src/tests.rs | 32 +- frame/support/src/dispatch.rs | 80 +- frame/support/src/traits/hooks.rs | 8 +- frame/support/src/weights/block_weights.rs | 2 +- .../support/src/weights/extrinsic_weights.rs | 2 +- frame/support/test/tests/construct_runtime.rs | 4 +- frame/support/test/tests/pallet.rs | 18 +- .../test/tests/pallet_compatibility.rs | 4 +- .../tests/pallet_compatibility_instance.rs | 4 +- frame/support/test/tests/pallet_instance.rs | 18 +- .../system/src/extensions/check_mortality.rs | 2 +- frame/system/src/extensions/check_weight.rs | 76 +- frame/system/src/mock.rs | 8 +- frame/system/src/tests.rs | 34 +- frame/system/src/weights.rs | 40 +- frame/timestamp/src/weights.rs | 4 +- frame/tips/src/weights.rs | 36 +- .../asset-tx-payment/src/mock.rs | 2 +- .../asset-tx-payment/src/tests.rs | 60 +- frame/transaction-payment/src/lib.rs | 2 +- frame/transaction-payment/src/mock.rs | 2 +- frame/transaction-payment/src/tests.rs | 98 +- frame/transaction-payment/src/types.rs | 4 +- frame/transaction-storage/src/weights.rs | 4 +- frame/treasury/src/weights.rs | 12 +- frame/uniques/src/weights.rs | 24 +- frame/utility/src/tests.rs | 14 +- frame/utility/src/weights.rs | 32 +- frame/vesting/src/weights.rs | 64 +- frame/whitelist/src/tests.rs | 2 +- frame/whitelist/src/weights.rs | 12 +- primitives/weights/src/lib.rs | 24 +- primitives/weights/src/weight_v2.rs | 6 +- test-utils/runtime/src/lib.rs | 2 +- .../benchmarking-cli/src/overhead/README.md | 4 +- .../benchmarking-cli/src/overhead/weights.hbs | 2 +- .../benchmarking-cli/src/pallet/template.hbs | 10 +- 101 files changed, 2695 insertions(+), 2857 deletions(-) diff --git a/.maintain/frame-weight-template.hbs b/.maintain/frame-weight-template.hbs index 186e3cb23..ba9ac0798 100644 --- a/.maintain/frame-weight-template.hbs +++ b/.maintain/frame-weight-template.hbs @@ -52,12 +52,11 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `{{benchmark.base_recorded_proof_size}}{{#each benchmark.component_recorded_proof_size as |cp|}} + {{cp.name}} * ({{cp.slope}} ±{{underscore cp.error}}){{/each}}` // Estimated: `{{benchmark.base_calculated_proof_size}}{{#each benchmark.component_calculated_proof_size as |cp|}} + {{cp.name}} * ({{cp.slope}} ±{{underscore cp.error}}){{/each}}` - // Minimum execution time: {{underscore benchmark.min_execution_time}} nanoseconds. - Weight::from_ref_time({{underscore benchmark.base_weight}}) - .saturating_add(Weight::from_proof_size({{benchmark.base_calculated_proof_size}})) + // Minimum execution time: {{underscore benchmark.min_execution_time}}_000 picoseconds. + Weight::from_parts({{underscore benchmark.base_weight}}, {{benchmark.base_calculated_proof_size}}) {{#each benchmark.component_weight as |cw|}} // Standard Error: {{underscore cw.error}} - .saturating_add(Weight::from_ref_time({{underscore cw.slope}}).saturating_mul({{cw.name}}.into())) + .saturating_add(Weight::from_parts({{underscore cw.slope}}, 0).saturating_mul({{cw.name}}.into())) {{/each}} {{#if (ne benchmark.base_reads "0")}} .saturating_add(T::DbWeight::get().reads({{benchmark.base_reads}}_u64)) @@ -72,7 +71,7 @@ impl WeightInfo for SubstrateWeight { .saturating_add(T::DbWeight::get().writes(({{cw.slope}}_u64).saturating_mul({{cw.name}}.into()))) {{/each}} {{#each benchmark.component_calculated_proof_size as |cp|}} - .saturating_add(Weight::from_proof_size({{cp.slope}}).saturating_mul({{cp.name}}.into())) + .saturating_add(Weight::from_parts(0, {{cp.slope}}).saturating_mul({{cp.name}}.into())) {{/each}} } {{/each}} @@ -95,12 +94,11 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `{{benchmark.base_recorded_proof_size}}{{#each benchmark.component_recorded_proof_size as |cp|}} + {{cp.name}} * ({{cp.slope}} ±{{underscore cp.error}}){{/each}}` // Estimated: `{{benchmark.base_calculated_proof_size}}{{#each benchmark.component_calculated_proof_size as |cp|}} + {{cp.name}} * ({{cp.slope}} ±{{underscore cp.error}}){{/each}}` - // Minimum execution time: {{underscore benchmark.min_execution_time}} nanoseconds. - Weight::from_ref_time({{underscore benchmark.base_weight}}) - .saturating_add(Weight::from_proof_size({{benchmark.base_calculated_proof_size}})) + // Minimum execution time: {{underscore benchmark.min_execution_time}}_000 picoseconds. + Weight::from_parts({{underscore benchmark.base_weight}}, {{benchmark.base_calculated_proof_size}}) {{#each benchmark.component_weight as |cw|}} // Standard Error: {{underscore cw.error}} - .saturating_add(Weight::from_ref_time({{underscore cw.slope}}).saturating_mul({{cw.name}}.into())) + .saturating_add(Weight::from_parts({{underscore cw.slope}}, 0).saturating_mul({{cw.name}}.into())) {{/each}} {{#if (ne benchmark.base_reads "0")}} .saturating_add(RocksDbWeight::get().reads({{benchmark.base_reads}}_u64)) @@ -115,7 +113,7 @@ impl WeightInfo for () { .saturating_add(RocksDbWeight::get().writes(({{cw.slope}}_u64).saturating_mul({{cw.name}}.into()))) {{/each}} {{#each benchmark.component_calculated_proof_size as |cp|}} - .saturating_add(Weight::from_proof_size({{cp.slope}}).saturating_mul({{cp.name}}.into())) + .saturating_add(Weight::from_parts(0, {{cp.slope}}).saturating_mul({{cp.name}}.into())) {{/each}} } {{/each}} diff --git a/bin/node/executor/tests/basic.rs b/bin/node/executor/tests/basic.rs index c19db5af5..ecfe02aa4 100644 --- a/bin/node/executor/tests/basic.rs +++ b/bin/node/executor/tests/basic.rs @@ -682,7 +682,7 @@ fn deploying_wasm_contract_should_work() { Runtime, > { value: 0, - gas_limit: Weight::from_ref_time(500_000_000), + gas_limit: Weight::from_parts(500_000_000, 0), storage_deposit_limit: None, code: transfer_code, data: Vec::new(), @@ -694,7 +694,7 @@ fn deploying_wasm_contract_should_work() { function: RuntimeCall::Contracts(pallet_contracts::Call::call:: { dest: sp_runtime::MultiAddress::Id(addr.clone()), value: 10, - gas_limit: Weight::from_ref_time(500_000_000), + gas_limit: Weight::from_parts(500_000_000, 0), storage_deposit_limit: None, data: vec![0x00, 0x01, 0x02, 0x03], }), diff --git a/bin/node/runtime/src/impls.rs b/bin/node/runtime/src/impls.rs index 9c606ff07..09f7b6a29 100644 --- a/bin/node/runtime/src/impls.rs +++ b/bin/node/runtime/src/impls.rs @@ -199,8 +199,8 @@ mod multiplier_tests { let fm = Multiplier::saturating_from_rational(1, 2); let test_set = vec![ (Weight::zero(), fm), - (Weight::from_ref_time(100), fm), - (Weight::from_ref_time(1000), fm), + (Weight::from_parts(100, 0), fm), + (Weight::from_parts(1000, 0), fm), (target(), fm), (max_normal() / 2, fm), (max_normal(), fm), @@ -285,7 +285,7 @@ mod multiplier_tests { // almost full. The entire quota of normal transactions is taken. let block_weight = BlockWeights::get().get(DispatchClass::Normal).max_total.unwrap() - - Weight::from_ref_time(100); + Weight::from_parts(100, 0); // Default substrate weight. let tx_weight = frame_support::weights::constants::ExtrinsicBaseWeight::get(); @@ -409,23 +409,23 @@ mod multiplier_tests { #[test] fn weight_to_fee_should_not_overflow_on_large_weights() { - let kb = Weight::from_ref_time(1024); + let kb = Weight::from_parts(1024, 0); let mb = 1024u64 * kb; let max_fm = Multiplier::saturating_from_integer(i128::MAX); // check that for all values it can compute, correctly. vec![ Weight::zero(), - Weight::from_ref_time(1), - Weight::from_ref_time(10), - Weight::from_ref_time(1000), + Weight::from_parts(1, 0), + Weight::from_parts(10, 0), + Weight::from_parts(1000, 0), kb, 10u64 * kb, 100u64 * kb, mb, 10u64 * mb, - Weight::from_ref_time(2147483647), - Weight::from_ref_time(4294967295), + Weight::from_parts(2147483647, 0), + Weight::from_parts(4294967295, 0), BlockWeights::get().max_block / 2, BlockWeights::get().max_block, Weight::MAX / 2, @@ -442,7 +442,7 @@ mod multiplier_tests { // Some values that are all above the target and will cause an increase. let t = target(); - vec![t + Weight::from_ref_time(100), t * 2, t * 4].into_iter().for_each(|i| { + vec![t + Weight::from_parts(100, 0), t * 2, t * 4].into_iter().for_each(|i| { run_with_system_weight(i, || { let fm = runtime_multiplier_update(max_fm); // won't grow. The convert saturates everything. diff --git a/frame/alliance/src/weights.rs b/frame/alliance/src/weights.rs index 6ccf6bed3..8ff4bad31 100644 --- a/frame/alliance/src/weights.rs +++ b/frame/alliance/src/weights.rs @@ -93,15 +93,15 @@ impl WeightInfo for SubstrateWeight { // Minimum execution time: 28_849 nanoseconds. Weight::from_parts(29_823_933, 11659) // Standard Error: 74 - .saturating_add(Weight::from_ref_time(830).saturating_mul(b.into())) + .saturating_add(Weight::from_parts(830, 0).saturating_mul(b.into())) // Standard Error: 775 - .saturating_add(Weight::from_ref_time(22_980).saturating_mul(m.into())) + .saturating_add(Weight::from_parts(22_980, 0).saturating_mul(m.into())) // Standard Error: 765 - .saturating_add(Weight::from_ref_time(90_520).saturating_mul(p.into())) + .saturating_add(Weight::from_parts(90_520, 0).saturating_mul(p.into())) .saturating_add(T::DbWeight::get().reads(4_u64)) .saturating_add(T::DbWeight::get().writes(4_u64)) - .saturating_add(Weight::from_proof_size(132).saturating_mul(m.into())) - .saturating_add(Weight::from_proof_size(144).saturating_mul(p.into())) + .saturating_add(Weight::from_parts(0, 132).saturating_mul(m.into())) + .saturating_add(Weight::from_parts(0, 144).saturating_mul(p.into())) } /// Storage: Alliance Members (r:1 w:0) /// Proof: Alliance Members (max_values: None, max_size: Some(3211), added: 5686, mode: MaxEncodedLen) @@ -115,10 +115,10 @@ impl WeightInfo for SubstrateWeight { // Minimum execution time: 23_570 nanoseconds. Weight::from_parts(25_473_196, 9337) // Standard Error: 824 - .saturating_add(Weight::from_ref_time(54_603).saturating_mul(m.into())) + .saturating_add(Weight::from_parts(54_603, 0).saturating_mul(m.into())) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) - .saturating_add(Weight::from_proof_size(64).saturating_mul(m.into())) + .saturating_add(Weight::from_parts(0, 64).saturating_mul(m.into())) } /// Storage: Alliance Members (r:1 w:0) /// Proof: Alliance Members (max_values: None, max_size: Some(3211), added: 5686, mode: MaxEncodedLen) @@ -139,13 +139,13 @@ impl WeightInfo for SubstrateWeight { // Minimum execution time: 35_373 nanoseconds. Weight::from_parts(32_763_656, 11979) // Standard Error: 2_041 - .saturating_add(Weight::from_ref_time(52_915).saturating_mul(m.into())) + .saturating_add(Weight::from_parts(52_915, 0).saturating_mul(m.into())) // Standard Error: 1_991 - .saturating_add(Weight::from_ref_time(78_594).saturating_mul(p.into())) + .saturating_add(Weight::from_parts(78_594, 0).saturating_mul(p.into())) .saturating_add(T::DbWeight::get().reads(4_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) - .saturating_add(Weight::from_proof_size(388).saturating_mul(m.into())) - .saturating_add(Weight::from_proof_size(148).saturating_mul(p.into())) + .saturating_add(Weight::from_parts(0, 388).saturating_mul(m.into())) + .saturating_add(Weight::from_parts(0, 148).saturating_mul(p.into())) } /// Storage: Alliance Members (r:1 w:0) /// Proof: Alliance Members (max_values: None, max_size: Some(3211), added: 5686, mode: MaxEncodedLen) @@ -167,15 +167,15 @@ impl WeightInfo for SubstrateWeight { // Minimum execution time: 44_590 nanoseconds. Weight::from_parts(43_367_913, 15730) // Standard Error: 71 - .saturating_add(Weight::from_ref_time(819).saturating_mul(b.into())) + .saturating_add(Weight::from_parts(819, 0).saturating_mul(b.into())) // Standard Error: 751 - .saturating_add(Weight::from_ref_time(39_124).saturating_mul(m.into())) + .saturating_add(Weight::from_parts(39_124, 0).saturating_mul(m.into())) // Standard Error: 732 - .saturating_add(Weight::from_ref_time(90_469).saturating_mul(p.into())) + .saturating_add(Weight::from_parts(90_469, 0).saturating_mul(p.into())) .saturating_add(T::DbWeight::get().reads(5_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) - .saturating_add(Weight::from_proof_size(388).saturating_mul(m.into())) - .saturating_add(Weight::from_proof_size(164).saturating_mul(p.into())) + .saturating_add(Weight::from_parts(0, 388).saturating_mul(m.into())) + .saturating_add(Weight::from_parts(0, 164).saturating_mul(p.into())) } /// Storage: Alliance Members (r:1 w:0) /// Proof: Alliance Members (max_values: None, max_size: Some(3211), added: 5686, mode: MaxEncodedLen) @@ -198,13 +198,13 @@ impl WeightInfo for SubstrateWeight { // Minimum execution time: 36_796 nanoseconds. Weight::from_parts(34_578_765, 13201) // Standard Error: 1_037 - .saturating_add(Weight::from_ref_time(43_508).saturating_mul(m.into())) + .saturating_add(Weight::from_parts(43_508, 0).saturating_mul(m.into())) // Standard Error: 1_024 - .saturating_add(Weight::from_ref_time(77_084).saturating_mul(p.into())) + .saturating_add(Weight::from_parts(77_084, 0).saturating_mul(p.into())) .saturating_add(T::DbWeight::get().reads(5_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) - .saturating_add(Weight::from_proof_size(485).saturating_mul(m.into())) - .saturating_add(Weight::from_proof_size(185).saturating_mul(p.into())) + .saturating_add(Weight::from_parts(0, 485).saturating_mul(m.into())) + .saturating_add(Weight::from_parts(0, 185).saturating_mul(p.into())) } /// Storage: Alliance Members (r:1 w:0) /// Proof: Alliance Members (max_values: None, max_size: Some(3211), added: 5686, mode: MaxEncodedLen) @@ -228,15 +228,15 @@ impl WeightInfo for SubstrateWeight { // Minimum execution time: 36_897 nanoseconds. Weight::from_parts(34_169_666, 13146) // Standard Error: 47 - .saturating_add(Weight::from_ref_time(972).saturating_mul(b.into())) + .saturating_add(Weight::from_parts(972, 0).saturating_mul(b.into())) // Standard Error: 510 - .saturating_add(Weight::from_ref_time(38_084).saturating_mul(m.into())) + .saturating_add(Weight::from_parts(38_084, 0).saturating_mul(m.into())) // Standard Error: 492 - .saturating_add(Weight::from_ref_time(78_576).saturating_mul(p.into())) + .saturating_add(Weight::from_parts(78_576, 0).saturating_mul(p.into())) .saturating_add(T::DbWeight::get().reads(5_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) - .saturating_add(Weight::from_proof_size(480).saturating_mul(m.into())) - .saturating_add(Weight::from_proof_size(185).saturating_mul(p.into())) + .saturating_add(Weight::from_parts(0, 480).saturating_mul(m.into())) + .saturating_add(Weight::from_parts(0, 185).saturating_mul(p.into())) } /// Storage: Alliance Members (r:2 w:2) /// Proof: Alliance Members (max_values: None, max_size: Some(3211), added: 5686, mode: MaxEncodedLen) @@ -251,9 +251,9 @@ impl WeightInfo for SubstrateWeight { // Minimum execution time: 29_313 nanoseconds. Weight::from_parts(20_502_244, 12084) // Standard Error: 304 - .saturating_add(Weight::from_ref_time(107_994).saturating_mul(m.into())) + .saturating_add(Weight::from_parts(107_994, 0).saturating_mul(m.into())) // Standard Error: 300 - .saturating_add(Weight::from_ref_time(92_645).saturating_mul(z.into())) + .saturating_add(Weight::from_parts(92_645, 0).saturating_mul(z.into())) .saturating_add(T::DbWeight::get().reads(3_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) } @@ -279,20 +279,20 @@ impl WeightInfo for SubstrateWeight { // Minimum execution time: 232_895 nanoseconds. Weight::from_parts(233_860_000, 32201) // Standard Error: 19_092 - .saturating_add(Weight::from_ref_time(445_664).saturating_mul(x.into())) + .saturating_add(Weight::from_parts(445_664, 0).saturating_mul(x.into())) // Standard Error: 19_000 - .saturating_add(Weight::from_ref_time(432_571).saturating_mul(y.into())) + .saturating_add(Weight::from_parts(432_571, 0).saturating_mul(y.into())) // Standard Error: 37_965 - .saturating_add(Weight::from_ref_time(9_386_151).saturating_mul(z.into())) + .saturating_add(Weight::from_parts(9_386_151, 0).saturating_mul(z.into())) .saturating_add(T::DbWeight::get().reads(3_u64)) .saturating_add(T::DbWeight::get().reads((1_u64).saturating_mul(x.into()))) .saturating_add(T::DbWeight::get().reads((1_u64).saturating_mul(y.into()))) .saturating_add(T::DbWeight::get().reads((1_u64).saturating_mul(z.into()))) .saturating_add(T::DbWeight::get().writes(4_u64)) .saturating_add(T::DbWeight::get().writes((2_u64).saturating_mul(z.into()))) - .saturating_add(Weight::from_proof_size(2587).saturating_mul(x.into())) - .saturating_add(Weight::from_proof_size(2590).saturating_mul(y.into())) - .saturating_add(Weight::from_proof_size(3209).saturating_mul(z.into())) + .saturating_add(Weight::from_parts(0, 2587).saturating_mul(x.into())) + .saturating_add(Weight::from_parts(0, 2590).saturating_mul(y.into())) + .saturating_add(Weight::from_parts(0, 3209).saturating_mul(z.into())) } /// Storage: Alliance Rule (r:0 w:1) /// Proof: Alliance Rule (max_values: Some(1), max_size: Some(87), added: 582, mode: MaxEncodedLen) @@ -301,7 +301,7 @@ impl WeightInfo for SubstrateWeight { // Measured: `0` // Estimated: `0` // Minimum execution time: 9_156 nanoseconds. - Weight::from_ref_time(9_376_000) + Weight::from_parts(9_376_000, 0) .saturating_add(T::DbWeight::get().writes(1_u64)) } /// Storage: Alliance Announcements (r:1 w:1) @@ -443,9 +443,9 @@ impl WeightInfo for SubstrateWeight { // Minimum execution time: 7_709 nanoseconds. Weight::from_parts(7_773_000, 29894) // Standard Error: 2_645 - .saturating_add(Weight::from_ref_time(1_266_755).saturating_mul(n.into())) + .saturating_add(Weight::from_parts(1_266_755, 0).saturating_mul(n.into())) // Standard Error: 1_036 - .saturating_add(Weight::from_ref_time(67_359).saturating_mul(l.into())) + .saturating_add(Weight::from_parts(67_359, 0).saturating_mul(l.into())) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().writes(2_u64)) } @@ -462,9 +462,9 @@ impl WeightInfo for SubstrateWeight { // Minimum execution time: 7_438 nanoseconds. Weight::from_parts(7_570_000, 29894) // Standard Error: 165_072 - .saturating_add(Weight::from_ref_time(13_026_975).saturating_mul(n.into())) + .saturating_add(Weight::from_parts(13_026_975, 0).saturating_mul(n.into())) // Standard Error: 64_649 - .saturating_add(Weight::from_ref_time(485_565).saturating_mul(l.into())) + .saturating_add(Weight::from_parts(485_565, 0).saturating_mul(l.into())) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().writes(2_u64)) } @@ -509,15 +509,15 @@ impl WeightInfo for () { // Minimum execution time: 28_849 nanoseconds. Weight::from_parts(29_823_933, 11659) // Standard Error: 74 - .saturating_add(Weight::from_ref_time(830).saturating_mul(b.into())) + .saturating_add(Weight::from_parts(830, 0).saturating_mul(b.into())) // Standard Error: 775 - .saturating_add(Weight::from_ref_time(22_980).saturating_mul(m.into())) + .saturating_add(Weight::from_parts(22_980, 0).saturating_mul(m.into())) // Standard Error: 765 - .saturating_add(Weight::from_ref_time(90_520).saturating_mul(p.into())) + .saturating_add(Weight::from_parts(90_520, 0).saturating_mul(p.into())) .saturating_add(RocksDbWeight::get().reads(4_u64)) .saturating_add(RocksDbWeight::get().writes(4_u64)) - .saturating_add(Weight::from_proof_size(132).saturating_mul(m.into())) - .saturating_add(Weight::from_proof_size(144).saturating_mul(p.into())) + .saturating_add(Weight::from_parts(0, 132).saturating_mul(m.into())) + .saturating_add(Weight::from_parts(0, 144).saturating_mul(p.into())) } /// Storage: Alliance Members (r:1 w:0) /// Proof: Alliance Members (max_values: None, max_size: Some(3211), added: 5686, mode: MaxEncodedLen) @@ -531,10 +531,10 @@ impl WeightInfo for () { // Minimum execution time: 23_570 nanoseconds. Weight::from_parts(25_473_196, 9337) // Standard Error: 824 - .saturating_add(Weight::from_ref_time(54_603).saturating_mul(m.into())) + .saturating_add(Weight::from_parts(54_603, 0).saturating_mul(m.into())) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) - .saturating_add(Weight::from_proof_size(64).saturating_mul(m.into())) + .saturating_add(Weight::from_parts(0, 64).saturating_mul(m.into())) } /// Storage: Alliance Members (r:1 w:0) /// Proof: Alliance Members (max_values: None, max_size: Some(3211), added: 5686, mode: MaxEncodedLen) @@ -555,13 +555,13 @@ impl WeightInfo for () { // Minimum execution time: 35_373 nanoseconds. Weight::from_parts(32_763_656, 11979) // Standard Error: 2_041 - .saturating_add(Weight::from_ref_time(52_915).saturating_mul(m.into())) + .saturating_add(Weight::from_parts(52_915, 0).saturating_mul(m.into())) // Standard Error: 1_991 - .saturating_add(Weight::from_ref_time(78_594).saturating_mul(p.into())) + .saturating_add(Weight::from_parts(78_594, 0).saturating_mul(p.into())) .saturating_add(RocksDbWeight::get().reads(4_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) - .saturating_add(Weight::from_proof_size(388).saturating_mul(m.into())) - .saturating_add(Weight::from_proof_size(148).saturating_mul(p.into())) + .saturating_add(Weight::from_parts(0, 388).saturating_mul(m.into())) + .saturating_add(Weight::from_parts(0, 148).saturating_mul(p.into())) } /// Storage: Alliance Members (r:1 w:0) /// Proof: Alliance Members (max_values: None, max_size: Some(3211), added: 5686, mode: MaxEncodedLen) @@ -583,15 +583,15 @@ impl WeightInfo for () { // Minimum execution time: 44_590 nanoseconds. Weight::from_parts(43_367_913, 15730) // Standard Error: 71 - .saturating_add(Weight::from_ref_time(819).saturating_mul(b.into())) + .saturating_add(Weight::from_parts(819, 0).saturating_mul(b.into())) // Standard Error: 751 - .saturating_add(Weight::from_ref_time(39_124).saturating_mul(m.into())) + .saturating_add(Weight::from_parts(39_124, 0).saturating_mul(m.into())) // Standard Error: 732 - .saturating_add(Weight::from_ref_time(90_469).saturating_mul(p.into())) + .saturating_add(Weight::from_parts(90_469, 0).saturating_mul(p.into())) .saturating_add(RocksDbWeight::get().reads(5_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) - .saturating_add(Weight::from_proof_size(388).saturating_mul(m.into())) - .saturating_add(Weight::from_proof_size(164).saturating_mul(p.into())) + .saturating_add(Weight::from_parts(0, 388).saturating_mul(m.into())) + .saturating_add(Weight::from_parts(0, 164).saturating_mul(p.into())) } /// Storage: Alliance Members (r:1 w:0) /// Proof: Alliance Members (max_values: None, max_size: Some(3211), added: 5686, mode: MaxEncodedLen) @@ -614,13 +614,13 @@ impl WeightInfo for () { // Minimum execution time: 36_796 nanoseconds. Weight::from_parts(34_578_765, 13201) // Standard Error: 1_037 - .saturating_add(Weight::from_ref_time(43_508).saturating_mul(m.into())) + .saturating_add(Weight::from_parts(43_508, 0).saturating_mul(m.into())) // Standard Error: 1_024 - .saturating_add(Weight::from_ref_time(77_084).saturating_mul(p.into())) + .saturating_add(Weight::from_parts(77_084, 0).saturating_mul(p.into())) .saturating_add(RocksDbWeight::get().reads(5_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) - .saturating_add(Weight::from_proof_size(485).saturating_mul(m.into())) - .saturating_add(Weight::from_proof_size(185).saturating_mul(p.into())) + .saturating_add(Weight::from_parts(0, 485).saturating_mul(m.into())) + .saturating_add(Weight::from_parts(0, 185).saturating_mul(p.into())) } /// Storage: Alliance Members (r:1 w:0) /// Proof: Alliance Members (max_values: None, max_size: Some(3211), added: 5686, mode: MaxEncodedLen) @@ -644,15 +644,15 @@ impl WeightInfo for () { // Minimum execution time: 36_897 nanoseconds. Weight::from_parts(34_169_666, 13146) // Standard Error: 47 - .saturating_add(Weight::from_ref_time(972).saturating_mul(b.into())) + .saturating_add(Weight::from_parts(972, 0).saturating_mul(b.into())) // Standard Error: 510 - .saturating_add(Weight::from_ref_time(38_084).saturating_mul(m.into())) + .saturating_add(Weight::from_parts(38_084, 0).saturating_mul(m.into())) // Standard Error: 492 - .saturating_add(Weight::from_ref_time(78_576).saturating_mul(p.into())) + .saturating_add(Weight::from_parts(78_576, 0).saturating_mul(p.into())) .saturating_add(RocksDbWeight::get().reads(5_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) - .saturating_add(Weight::from_proof_size(480).saturating_mul(m.into())) - .saturating_add(Weight::from_proof_size(185).saturating_mul(p.into())) + .saturating_add(Weight::from_parts(0, 480).saturating_mul(m.into())) + .saturating_add(Weight::from_parts(0, 185).saturating_mul(p.into())) } /// Storage: Alliance Members (r:2 w:2) /// Proof: Alliance Members (max_values: None, max_size: Some(3211), added: 5686, mode: MaxEncodedLen) @@ -667,9 +667,9 @@ impl WeightInfo for () { // Minimum execution time: 29_313 nanoseconds. Weight::from_parts(20_502_244, 12084) // Standard Error: 304 - .saturating_add(Weight::from_ref_time(107_994).saturating_mul(m.into())) + .saturating_add(Weight::from_parts(107_994, 0).saturating_mul(m.into())) // Standard Error: 300 - .saturating_add(Weight::from_ref_time(92_645).saturating_mul(z.into())) + .saturating_add(Weight::from_parts(92_645, 0).saturating_mul(z.into())) .saturating_add(RocksDbWeight::get().reads(3_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) } @@ -695,20 +695,20 @@ impl WeightInfo for () { // Minimum execution time: 232_895 nanoseconds. Weight::from_parts(233_860_000, 32201) // Standard Error: 19_092 - .saturating_add(Weight::from_ref_time(445_664).saturating_mul(x.into())) + .saturating_add(Weight::from_parts(445_664, 0).saturating_mul(x.into())) // Standard Error: 19_000 - .saturating_add(Weight::from_ref_time(432_571).saturating_mul(y.into())) + .saturating_add(Weight::from_parts(432_571, 0).saturating_mul(y.into())) // Standard Error: 37_965 - .saturating_add(Weight::from_ref_time(9_386_151).saturating_mul(z.into())) + .saturating_add(Weight::from_parts(9_386_151, 0).saturating_mul(z.into())) .saturating_add(RocksDbWeight::get().reads(3_u64)) .saturating_add(RocksDbWeight::get().reads((1_u64).saturating_mul(x.into()))) .saturating_add(RocksDbWeight::get().reads((1_u64).saturating_mul(y.into()))) .saturating_add(RocksDbWeight::get().reads((1_u64).saturating_mul(z.into()))) .saturating_add(RocksDbWeight::get().writes(4_u64)) .saturating_add(RocksDbWeight::get().writes((2_u64).saturating_mul(z.into()))) - .saturating_add(Weight::from_proof_size(2587).saturating_mul(x.into())) - .saturating_add(Weight::from_proof_size(2590).saturating_mul(y.into())) - .saturating_add(Weight::from_proof_size(3209).saturating_mul(z.into())) + .saturating_add(Weight::from_parts(0, 2587).saturating_mul(x.into())) + .saturating_add(Weight::from_parts(0, 2590).saturating_mul(y.into())) + .saturating_add(Weight::from_parts(0, 3209).saturating_mul(z.into())) } /// Storage: Alliance Rule (r:0 w:1) /// Proof: Alliance Rule (max_values: Some(1), max_size: Some(87), added: 582, mode: MaxEncodedLen) @@ -717,7 +717,7 @@ impl WeightInfo for () { // Measured: `0` // Estimated: `0` // Minimum execution time: 9_156 nanoseconds. - Weight::from_ref_time(9_376_000) + Weight::from_parts(9_376_000, 0) .saturating_add(RocksDbWeight::get().writes(1_u64)) } /// Storage: Alliance Announcements (r:1 w:1) @@ -859,9 +859,9 @@ impl WeightInfo for () { // Minimum execution time: 7_709 nanoseconds. Weight::from_parts(7_773_000, 29894) // Standard Error: 2_645 - .saturating_add(Weight::from_ref_time(1_266_755).saturating_mul(n.into())) + .saturating_add(Weight::from_parts(1_266_755, 0).saturating_mul(n.into())) // Standard Error: 1_036 - .saturating_add(Weight::from_ref_time(67_359).saturating_mul(l.into())) + .saturating_add(Weight::from_parts(67_359, 0).saturating_mul(l.into())) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().writes(2_u64)) } @@ -878,9 +878,9 @@ impl WeightInfo for () { // Minimum execution time: 7_438 nanoseconds. Weight::from_parts(7_570_000, 29894) // Standard Error: 165_072 - .saturating_add(Weight::from_ref_time(13_026_975).saturating_mul(n.into())) + .saturating_add(Weight::from_parts(13_026_975, 0).saturating_mul(n.into())) // Standard Error: 64_649 - .saturating_add(Weight::from_ref_time(485_565).saturating_mul(l.into())) + .saturating_add(Weight::from_parts(485_565, 0).saturating_mul(l.into())) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().writes(2_u64)) } diff --git a/frame/assets/src/weights.rs b/frame/assets/src/weights.rs index 27de4fa33..080800daa 100644 --- a/frame/assets/src/weights.rs +++ b/frame/assets/src/weights.rs @@ -90,8 +90,8 @@ impl WeightInfo for SubstrateWeight { // Measured: `325` // Estimated: `7268` // Minimum execution time: 27_887 nanoseconds. - Weight::from_ref_time(28_190_000) - .saturating_add(Weight::from_proof_size(7268)) + Weight::from_parts(28_190_000, 0) + .saturating_add(Weight::from_parts(0, 7268)) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().writes(2_u64)) } @@ -102,8 +102,8 @@ impl WeightInfo for SubstrateWeight { // Measured: `153` // Estimated: `3675` // Minimum execution time: 15_059 nanoseconds. - Weight::from_ref_time(15_600_000) - .saturating_add(Weight::from_proof_size(3675)) + Weight::from_parts(15_600_000, 0) + .saturating_add(Weight::from_parts(0, 3675)) .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -114,8 +114,8 @@ impl WeightInfo for SubstrateWeight { // Measured: `417` // Estimated: `3675` // Minimum execution time: 15_581 nanoseconds. - Weight::from_ref_time(15_868_000) - .saturating_add(Weight::from_proof_size(3675)) + Weight::from_parts(15_868_000, 0) + .saturating_add(Weight::from_parts(0, 3675)) .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -131,15 +131,15 @@ impl WeightInfo for SubstrateWeight { // Measured: `25 + c * (240 ±0)` // Estimated: `8232 + c * (5180 ±0)` // Minimum execution time: 20_167 nanoseconds. - Weight::from_ref_time(20_436_000) - .saturating_add(Weight::from_proof_size(8232)) + Weight::from_parts(20_436_000, 0) + .saturating_add(Weight::from_parts(0, 8232)) // Standard Error: 12_761 - .saturating_add(Weight::from_ref_time(15_535_268).saturating_mul(c.into())) + .saturating_add(Weight::from_parts(15_535_268, 0).saturating_mul(c.into())) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().reads((2_u64).saturating_mul(c.into()))) .saturating_add(T::DbWeight::get().writes(1_u64)) .saturating_add(T::DbWeight::get().writes((2_u64).saturating_mul(c.into()))) - .saturating_add(Weight::from_proof_size(5180).saturating_mul(c.into())) + .saturating_add(Weight::from_parts(0, 5180).saturating_mul(c.into())) } /// Storage: Assets Asset (r:1 w:1) /// Proof: Assets Asset (max_values: None, max_size: Some(210), added: 2685, mode: MaxEncodedLen) @@ -151,15 +151,15 @@ impl WeightInfo for SubstrateWeight { // Measured: `554 + a * (86 ±0)` // Estimated: `7288 + a * (2623 ±0)` // Minimum execution time: 20_349 nanoseconds. - Weight::from_ref_time(20_482_000) - .saturating_add(Weight::from_proof_size(7288)) + Weight::from_parts(20_482_000, 0) + .saturating_add(Weight::from_parts(0, 7288)) // Standard Error: 9_831 - .saturating_add(Weight::from_ref_time(15_771_918).saturating_mul(a.into())) + .saturating_add(Weight::from_parts(15_771_918, 0).saturating_mul(a.into())) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().reads((1_u64).saturating_mul(a.into()))) .saturating_add(T::DbWeight::get().writes(1_u64)) .saturating_add(T::DbWeight::get().writes((1_u64).saturating_mul(a.into()))) - .saturating_add(Weight::from_proof_size(2623).saturating_mul(a.into())) + .saturating_add(Weight::from_parts(0, 2623).saturating_mul(a.into())) } /// Storage: Assets Asset (r:1 w:1) /// Proof: Assets Asset (max_values: None, max_size: Some(210), added: 2685, mode: MaxEncodedLen) @@ -170,8 +170,8 @@ impl WeightInfo for SubstrateWeight { // Measured: `383` // Estimated: `7280` // Minimum execution time: 15_647 nanoseconds. - Weight::from_ref_time(15_975_000) - .saturating_add(Weight::from_proof_size(7280)) + Weight::from_parts(15_975_000, 0) + .saturating_add(Weight::from_parts(0, 7280)) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -184,8 +184,8 @@ impl WeightInfo for SubstrateWeight { // Measured: `383` // Estimated: `7242` // Minimum execution time: 28_383 nanoseconds. - Weight::from_ref_time(29_055_000) - .saturating_add(Weight::from_proof_size(7242)) + Weight::from_parts(29_055_000, 0) + .saturating_add(Weight::from_parts(0, 7242)) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().writes(2_u64)) } @@ -198,8 +198,8 @@ impl WeightInfo for SubstrateWeight { // Measured: `491` // Estimated: `7242` // Minimum execution time: 34_950 nanoseconds. - Weight::from_ref_time(35_296_000) - .saturating_add(Weight::from_proof_size(7242)) + Weight::from_parts(35_296_000, 0) + .saturating_add(Weight::from_parts(0, 7242)) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().writes(2_u64)) } @@ -214,8 +214,8 @@ impl WeightInfo for SubstrateWeight { // Measured: `530` // Estimated: `13412` // Minimum execution time: 50_129 nanoseconds. - Weight::from_ref_time(50_820_000) - .saturating_add(Weight::from_proof_size(13412)) + Weight::from_parts(50_820_000, 0) + .saturating_add(Weight::from_parts(0, 13412)) .saturating_add(T::DbWeight::get().reads(4_u64)) .saturating_add(T::DbWeight::get().writes(4_u64)) } @@ -230,8 +230,8 @@ impl WeightInfo for SubstrateWeight { // Measured: `530` // Estimated: `13412` // Minimum execution time: 43_424 nanoseconds. - Weight::from_ref_time(44_080_000) - .saturating_add(Weight::from_proof_size(13412)) + Weight::from_parts(44_080_000, 0) + .saturating_add(Weight::from_parts(0, 13412)) .saturating_add(T::DbWeight::get().reads(4_u64)) .saturating_add(T::DbWeight::get().writes(4_u64)) } @@ -246,8 +246,8 @@ impl WeightInfo for SubstrateWeight { // Measured: `530` // Estimated: `13412` // Minimum execution time: 48_919 nanoseconds. - Weight::from_ref_time(50_720_000) - .saturating_add(Weight::from_proof_size(13412)) + Weight::from_parts(50_720_000, 0) + .saturating_add(Weight::from_parts(0, 13412)) .saturating_add(T::DbWeight::get().reads(4_u64)) .saturating_add(T::DbWeight::get().writes(4_u64)) } @@ -260,8 +260,8 @@ impl WeightInfo for SubstrateWeight { // Measured: `491` // Estimated: `7242` // Minimum execution time: 19_750 nanoseconds. - Weight::from_ref_time(20_053_000) - .saturating_add(Weight::from_proof_size(7242)) + Weight::from_parts(20_053_000, 0) + .saturating_add(Weight::from_parts(0, 7242)) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -274,8 +274,8 @@ impl WeightInfo for SubstrateWeight { // Measured: `491` // Estimated: `7242` // Minimum execution time: 19_672 nanoseconds. - Weight::from_ref_time(19_928_000) - .saturating_add(Weight::from_proof_size(7242)) + Weight::from_parts(19_928_000, 0) + .saturating_add(Weight::from_parts(0, 7242)) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -286,8 +286,8 @@ impl WeightInfo for SubstrateWeight { // Measured: `417` // Estimated: `3675` // Minimum execution time: 15_367 nanoseconds. - Weight::from_ref_time(15_726_000) - .saturating_add(Weight::from_proof_size(3675)) + Weight::from_parts(15_726_000, 0) + .saturating_add(Weight::from_parts(0, 3675)) .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -298,8 +298,8 @@ impl WeightInfo for SubstrateWeight { // Measured: `417` // Estimated: `3675` // Minimum execution time: 14_814 nanoseconds. - Weight::from_ref_time(15_301_000) - .saturating_add(Weight::from_proof_size(3675)) + Weight::from_parts(15_301_000, 0) + .saturating_add(Weight::from_parts(0, 3675)) .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -312,8 +312,8 @@ impl WeightInfo for SubstrateWeight { // Measured: `383` // Estimated: `7280` // Minimum execution time: 17_426 nanoseconds. - Weight::from_ref_time(17_804_000) - .saturating_add(Weight::from_proof_size(7280)) + Weight::from_parts(17_804_000, 0) + .saturating_add(Weight::from_parts(0, 7280)) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -324,8 +324,8 @@ impl WeightInfo for SubstrateWeight { // Measured: `383` // Estimated: `3675` // Minimum execution time: 15_935 nanoseconds. - Weight::from_ref_time(16_165_000) - .saturating_add(Weight::from_proof_size(3675)) + Weight::from_parts(16_165_000, 0) + .saturating_add(Weight::from_parts(0, 3675)) .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -340,10 +340,10 @@ impl WeightInfo for SubstrateWeight { // Measured: `383` // Estimated: `7280` // Minimum execution time: 26_890 nanoseconds. - Weight::from_ref_time(28_766_510) - .saturating_add(Weight::from_proof_size(7280)) + Weight::from_parts(28_766_510, 0) + .saturating_add(Weight::from_parts(0, 7280)) // Standard Error: 7_444 - .saturating_add(Weight::from_ref_time(3_619).saturating_mul(n.into())) + .saturating_add(Weight::from_parts(3_619, 0).saturating_mul(n.into())) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -356,8 +356,8 @@ impl WeightInfo for SubstrateWeight { // Measured: `579` // Estimated: `7280` // Minimum execution time: 27_146 nanoseconds. - Weight::from_ref_time(27_692_000) - .saturating_add(Weight::from_proof_size(7280)) + Weight::from_parts(27_692_000, 0) + .saturating_add(Weight::from_parts(0, 7280)) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -372,8 +372,8 @@ impl WeightInfo for SubstrateWeight { // Measured: `190` // Estimated: `7280` // Minimum execution time: 16_181 nanoseconds. - Weight::from_ref_time(18_317_178) - .saturating_add(Weight::from_proof_size(7280)) + Weight::from_parts(18_317_178, 0) + .saturating_add(Weight::from_parts(0, 7280)) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -386,8 +386,8 @@ impl WeightInfo for SubstrateWeight { // Measured: `579` // Estimated: `7280` // Minimum execution time: 26_962 nanoseconds. - Weight::from_ref_time(27_896_000) - .saturating_add(Weight::from_proof_size(7280)) + Weight::from_parts(27_896_000, 0) + .saturating_add(Weight::from_parts(0, 7280)) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -398,8 +398,8 @@ impl WeightInfo for SubstrateWeight { // Measured: `383` // Estimated: `3675` // Minimum execution time: 14_394 nanoseconds. - Weight::from_ref_time(14_917_000) - .saturating_add(Weight::from_proof_size(3675)) + Weight::from_parts(14_917_000, 0) + .saturating_add(Weight::from_parts(0, 3675)) .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -412,8 +412,8 @@ impl WeightInfo for SubstrateWeight { // Measured: `417` // Estimated: `7288` // Minimum execution time: 30_861 nanoseconds. - Weight::from_ref_time(31_356_000) - .saturating_add(Weight::from_proof_size(7288)) + Weight::from_parts(31_356_000, 0) + .saturating_add(Weight::from_parts(0, 7288)) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().writes(2_u64)) } @@ -430,8 +430,8 @@ impl WeightInfo for SubstrateWeight { // Measured: `700` // Estimated: `17025` // Minimum execution time: 64_510 nanoseconds. - Weight::from_ref_time(65_676_000) - .saturating_add(Weight::from_proof_size(17025)) + Weight::from_parts(65_676_000, 0) + .saturating_add(Weight::from_parts(0, 17025)) .saturating_add(T::DbWeight::get().reads(5_u64)) .saturating_add(T::DbWeight::get().writes(5_u64)) } @@ -444,8 +444,8 @@ impl WeightInfo for SubstrateWeight { // Measured: `587` // Estimated: `7288` // Minimum execution time: 32_620 nanoseconds. - Weight::from_ref_time(33_183_000) - .saturating_add(Weight::from_proof_size(7288)) + Weight::from_parts(33_183_000, 0) + .saturating_add(Weight::from_parts(0, 7288)) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().writes(2_u64)) } @@ -458,8 +458,8 @@ impl WeightInfo for SubstrateWeight { // Measured: `587` // Estimated: `7288` // Minimum execution time: 33_277 nanoseconds. - Weight::from_ref_time(34_438_000) - .saturating_add(Weight::from_proof_size(7288)) + Weight::from_parts(34_438_000, 0) + .saturating_add(Weight::from_parts(0, 7288)) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().writes(2_u64)) } @@ -470,8 +470,8 @@ impl WeightInfo for SubstrateWeight { // Measured: `383` // Estimated: `3675` // Minimum execution time: 16_213 nanoseconds. - Weight::from_ref_time(16_575_000) - .saturating_add(Weight::from_proof_size(3675)) + Weight::from_parts(16_575_000, 0) + .saturating_add(Weight::from_parts(0, 3675)) .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -488,8 +488,8 @@ impl WeightInfo for () { // Measured: `325` // Estimated: `7268` // Minimum execution time: 27_887 nanoseconds. - Weight::from_ref_time(28_190_000) - .saturating_add(Weight::from_proof_size(7268)) + Weight::from_parts(28_190_000, 0) + .saturating_add(Weight::from_parts(0, 7268)) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().writes(2_u64)) } @@ -500,8 +500,8 @@ impl WeightInfo for () { // Measured: `153` // Estimated: `3675` // Minimum execution time: 15_059 nanoseconds. - Weight::from_ref_time(15_600_000) - .saturating_add(Weight::from_proof_size(3675)) + Weight::from_parts(15_600_000, 0) + .saturating_add(Weight::from_parts(0, 3675)) .saturating_add(RocksDbWeight::get().reads(1_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } @@ -512,8 +512,8 @@ impl WeightInfo for () { // Measured: `417` // Estimated: `3675` // Minimum execution time: 15_581 nanoseconds. - Weight::from_ref_time(15_868_000) - .saturating_add(Weight::from_proof_size(3675)) + Weight::from_parts(15_868_000, 0) + .saturating_add(Weight::from_parts(0, 3675)) .saturating_add(RocksDbWeight::get().reads(1_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } @@ -529,15 +529,15 @@ impl WeightInfo for () { // Measured: `25 + c * (240 ±0)` // Estimated: `8232 + c * (5180 ±0)` // Minimum execution time: 20_167 nanoseconds. - Weight::from_ref_time(20_436_000) - .saturating_add(Weight::from_proof_size(8232)) + Weight::from_parts(20_436_000, 0) + .saturating_add(Weight::from_parts(0, 8232)) // Standard Error: 12_761 - .saturating_add(Weight::from_ref_time(15_535_268).saturating_mul(c.into())) + .saturating_add(Weight::from_parts(15_535_268, 0).saturating_mul(c.into())) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().reads((2_u64).saturating_mul(c.into()))) .saturating_add(RocksDbWeight::get().writes(1_u64)) .saturating_add(RocksDbWeight::get().writes((2_u64).saturating_mul(c.into()))) - .saturating_add(Weight::from_proof_size(5180).saturating_mul(c.into())) + .saturating_add(Weight::from_parts(0, 5180).saturating_mul(c.into())) } /// Storage: Assets Asset (r:1 w:1) /// Proof: Assets Asset (max_values: None, max_size: Some(210), added: 2685, mode: MaxEncodedLen) @@ -549,15 +549,15 @@ impl WeightInfo for () { // Measured: `554 + a * (86 ±0)` // Estimated: `7288 + a * (2623 ±0)` // Minimum execution time: 20_349 nanoseconds. - Weight::from_ref_time(20_482_000) - .saturating_add(Weight::from_proof_size(7288)) + Weight::from_parts(20_482_000, 0) + .saturating_add(Weight::from_parts(0, 7288)) // Standard Error: 9_831 - .saturating_add(Weight::from_ref_time(15_771_918).saturating_mul(a.into())) + .saturating_add(Weight::from_parts(15_771_918, 0).saturating_mul(a.into())) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().reads((1_u64).saturating_mul(a.into()))) .saturating_add(RocksDbWeight::get().writes(1_u64)) .saturating_add(RocksDbWeight::get().writes((1_u64).saturating_mul(a.into()))) - .saturating_add(Weight::from_proof_size(2623).saturating_mul(a.into())) + .saturating_add(Weight::from_parts(0, 2623).saturating_mul(a.into())) } /// Storage: Assets Asset (r:1 w:1) /// Proof: Assets Asset (max_values: None, max_size: Some(210), added: 2685, mode: MaxEncodedLen) @@ -568,8 +568,8 @@ impl WeightInfo for () { // Measured: `383` // Estimated: `7280` // Minimum execution time: 15_647 nanoseconds. - Weight::from_ref_time(15_975_000) - .saturating_add(Weight::from_proof_size(7280)) + Weight::from_parts(15_975_000, 0) + .saturating_add(Weight::from_parts(0, 7280)) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } @@ -582,8 +582,8 @@ impl WeightInfo for () { // Measured: `383` // Estimated: `7242` // Minimum execution time: 28_383 nanoseconds. - Weight::from_ref_time(29_055_000) - .saturating_add(Weight::from_proof_size(7242)) + Weight::from_parts(29_055_000, 0) + .saturating_add(Weight::from_parts(0, 7242)) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().writes(2_u64)) } @@ -596,8 +596,8 @@ impl WeightInfo for () { // Measured: `491` // Estimated: `7242` // Minimum execution time: 34_950 nanoseconds. - Weight::from_ref_time(35_296_000) - .saturating_add(Weight::from_proof_size(7242)) + Weight::from_parts(35_296_000, 0) + .saturating_add(Weight::from_parts(0, 7242)) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().writes(2_u64)) } @@ -612,8 +612,8 @@ impl WeightInfo for () { // Measured: `530` // Estimated: `13412` // Minimum execution time: 50_129 nanoseconds. - Weight::from_ref_time(50_820_000) - .saturating_add(Weight::from_proof_size(13412)) + Weight::from_parts(50_820_000, 0) + .saturating_add(Weight::from_parts(0, 13412)) .saturating_add(RocksDbWeight::get().reads(4_u64)) .saturating_add(RocksDbWeight::get().writes(4_u64)) } @@ -628,8 +628,8 @@ impl WeightInfo for () { // Measured: `530` // Estimated: `13412` // Minimum execution time: 43_424 nanoseconds. - Weight::from_ref_time(44_080_000) - .saturating_add(Weight::from_proof_size(13412)) + Weight::from_parts(44_080_000, 0) + .saturating_add(Weight::from_parts(0, 13412)) .saturating_add(RocksDbWeight::get().reads(4_u64)) .saturating_add(RocksDbWeight::get().writes(4_u64)) } @@ -644,8 +644,8 @@ impl WeightInfo for () { // Measured: `530` // Estimated: `13412` // Minimum execution time: 48_919 nanoseconds. - Weight::from_ref_time(50_720_000) - .saturating_add(Weight::from_proof_size(13412)) + Weight::from_parts(50_720_000, 0) + .saturating_add(Weight::from_parts(0, 13412)) .saturating_add(RocksDbWeight::get().reads(4_u64)) .saturating_add(RocksDbWeight::get().writes(4_u64)) } @@ -658,8 +658,8 @@ impl WeightInfo for () { // Measured: `491` // Estimated: `7242` // Minimum execution time: 19_750 nanoseconds. - Weight::from_ref_time(20_053_000) - .saturating_add(Weight::from_proof_size(7242)) + Weight::from_parts(20_053_000, 0) + .saturating_add(Weight::from_parts(0, 7242)) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } @@ -672,8 +672,8 @@ impl WeightInfo for () { // Measured: `491` // Estimated: `7242` // Minimum execution time: 19_672 nanoseconds. - Weight::from_ref_time(19_928_000) - .saturating_add(Weight::from_proof_size(7242)) + Weight::from_parts(19_928_000, 0) + .saturating_add(Weight::from_parts(0, 7242)) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } @@ -684,8 +684,8 @@ impl WeightInfo for () { // Measured: `417` // Estimated: `3675` // Minimum execution time: 15_367 nanoseconds. - Weight::from_ref_time(15_726_000) - .saturating_add(Weight::from_proof_size(3675)) + Weight::from_parts(15_726_000, 0) + .saturating_add(Weight::from_parts(0, 3675)) .saturating_add(RocksDbWeight::get().reads(1_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } @@ -696,8 +696,8 @@ impl WeightInfo for () { // Measured: `417` // Estimated: `3675` // Minimum execution time: 14_814 nanoseconds. - Weight::from_ref_time(15_301_000) - .saturating_add(Weight::from_proof_size(3675)) + Weight::from_parts(15_301_000, 0) + .saturating_add(Weight::from_parts(0, 3675)) .saturating_add(RocksDbWeight::get().reads(1_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } @@ -710,8 +710,8 @@ impl WeightInfo for () { // Measured: `383` // Estimated: `7280` // Minimum execution time: 17_426 nanoseconds. - Weight::from_ref_time(17_804_000) - .saturating_add(Weight::from_proof_size(7280)) + Weight::from_parts(17_804_000, 0) + .saturating_add(Weight::from_parts(0, 7280)) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } @@ -722,8 +722,8 @@ impl WeightInfo for () { // Measured: `383` // Estimated: `3675` // Minimum execution time: 15_935 nanoseconds. - Weight::from_ref_time(16_165_000) - .saturating_add(Weight::from_proof_size(3675)) + Weight::from_parts(16_165_000, 0) + .saturating_add(Weight::from_parts(0, 3675)) .saturating_add(RocksDbWeight::get().reads(1_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } @@ -738,10 +738,10 @@ impl WeightInfo for () { // Measured: `383` // Estimated: `7280` // Minimum execution time: 26_890 nanoseconds. - Weight::from_ref_time(28_766_510) - .saturating_add(Weight::from_proof_size(7280)) + Weight::from_parts(28_766_510, 0) + .saturating_add(Weight::from_parts(0, 7280)) // Standard Error: 7_444 - .saturating_add(Weight::from_ref_time(3_619).saturating_mul(n.into())) + .saturating_add(Weight::from_parts(3_619, 0).saturating_mul(n.into())) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } @@ -754,8 +754,8 @@ impl WeightInfo for () { // Measured: `579` // Estimated: `7280` // Minimum execution time: 27_146 nanoseconds. - Weight::from_ref_time(27_692_000) - .saturating_add(Weight::from_proof_size(7280)) + Weight::from_parts(27_692_000, 0) + .saturating_add(Weight::from_parts(0, 7280)) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } @@ -770,8 +770,8 @@ impl WeightInfo for () { // Measured: `190` // Estimated: `7280` // Minimum execution time: 16_181 nanoseconds. - Weight::from_ref_time(18_317_178) - .saturating_add(Weight::from_proof_size(7280)) + Weight::from_parts(18_317_178, 0) + .saturating_add(Weight::from_parts(0, 7280)) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } @@ -784,8 +784,8 @@ impl WeightInfo for () { // Measured: `579` // Estimated: `7280` // Minimum execution time: 26_962 nanoseconds. - Weight::from_ref_time(27_896_000) - .saturating_add(Weight::from_proof_size(7280)) + Weight::from_parts(27_896_000, 0) + .saturating_add(Weight::from_parts(0, 7280)) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } @@ -796,8 +796,8 @@ impl WeightInfo for () { // Measured: `383` // Estimated: `3675` // Minimum execution time: 14_394 nanoseconds. - Weight::from_ref_time(14_917_000) - .saturating_add(Weight::from_proof_size(3675)) + Weight::from_parts(14_917_000, 0) + .saturating_add(Weight::from_parts(0, 3675)) .saturating_add(RocksDbWeight::get().reads(1_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } @@ -810,8 +810,8 @@ impl WeightInfo for () { // Measured: `417` // Estimated: `7288` // Minimum execution time: 30_861 nanoseconds. - Weight::from_ref_time(31_356_000) - .saturating_add(Weight::from_proof_size(7288)) + Weight::from_parts(31_356_000, 0) + .saturating_add(Weight::from_parts(0, 7288)) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().writes(2_u64)) } @@ -828,8 +828,8 @@ impl WeightInfo for () { // Measured: `700` // Estimated: `17025` // Minimum execution time: 64_510 nanoseconds. - Weight::from_ref_time(65_676_000) - .saturating_add(Weight::from_proof_size(17025)) + Weight::from_parts(65_676_000, 0) + .saturating_add(Weight::from_parts(0, 17025)) .saturating_add(RocksDbWeight::get().reads(5_u64)) .saturating_add(RocksDbWeight::get().writes(5_u64)) } @@ -842,8 +842,8 @@ impl WeightInfo for () { // Measured: `587` // Estimated: `7288` // Minimum execution time: 32_620 nanoseconds. - Weight::from_ref_time(33_183_000) - .saturating_add(Weight::from_proof_size(7288)) + Weight::from_parts(33_183_000, 0) + .saturating_add(Weight::from_parts(0, 7288)) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().writes(2_u64)) } @@ -856,8 +856,8 @@ impl WeightInfo for () { // Measured: `587` // Estimated: `7288` // Minimum execution time: 33_277 nanoseconds. - Weight::from_ref_time(34_438_000) - .saturating_add(Weight::from_proof_size(7288)) + Weight::from_parts(34_438_000, 0) + .saturating_add(Weight::from_parts(0, 7288)) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().writes(2_u64)) } @@ -868,8 +868,8 @@ impl WeightInfo for () { // Measured: `383` // Estimated: `3675` // Minimum execution time: 16_213 nanoseconds. - Weight::from_ref_time(16_575_000) - .saturating_add(Weight::from_proof_size(3675)) + Weight::from_parts(16_575_000, 0) + .saturating_add(Weight::from_parts(0, 3675)) .saturating_add(RocksDbWeight::get().reads(1_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } diff --git a/frame/babe/src/default_weights.rs b/frame/babe/src/default_weights.rs index e875bc835..2e880fd67 100644 --- a/frame/babe/src/default_weights.rs +++ b/frame/babe/src/default_weights.rs @@ -38,18 +38,19 @@ impl crate::WeightInfo for () { const MAX_NOMINATORS: u64 = 200; // checking membership proof - Weight::from_ref_time(35u64 * WEIGHT_REF_TIME_PER_MICROS) + Weight::from_parts(35u64 * WEIGHT_REF_TIME_PER_MICROS, 0) .saturating_add( - Weight::from_ref_time(175u64 * WEIGHT_REF_TIME_PER_NANOS) + Weight::from_parts(175u64 * WEIGHT_REF_TIME_PER_NANOS, 0) .saturating_mul(validator_count), ) .saturating_add(DbWeight::get().reads(5)) // check equivocation proof - .saturating_add(Weight::from_ref_time(110u64 * WEIGHT_REF_TIME_PER_MICROS)) + .saturating_add(Weight::from_parts(110u64 * WEIGHT_REF_TIME_PER_MICROS, 0)) // report offence - .saturating_add(Weight::from_ref_time(110u64 * WEIGHT_REF_TIME_PER_MICROS)) - .saturating_add(Weight::from_ref_time( + .saturating_add(Weight::from_parts(110u64 * WEIGHT_REF_TIME_PER_MICROS, 0)) + .saturating_add(Weight::from_parts( 25u64 * WEIGHT_REF_TIME_PER_MICROS * MAX_NOMINATORS, + 0, )) .saturating_add(DbWeight::get().reads(14 + 3 * MAX_NOMINATORS)) .saturating_add(DbWeight::get().writes(10 + 3 * MAX_NOMINATORS)) diff --git a/frame/balances/src/tests.rs b/frame/balances/src/tests.rs index 82a6ee5f6..b4233a6c3 100644 --- a/frame/balances/src/tests.rs +++ b/frame/balances/src/tests.rs @@ -188,14 +188,14 @@ macro_rules! decl_tests { ChargeTransactionPayment::from(1), &1, CALL, - &info_from_weight(Weight::from_ref_time(1)), + &info_from_weight(Weight::from_parts(1, 0)), 1, ).is_err()); assert_ok!( as SignedExtension>::pre_dispatch( ChargeTransactionPayment::from(0), &1, CALL, - &info_from_weight(Weight::from_ref_time(1)), + &info_from_weight(Weight::from_parts(1, 0)), 1, )); @@ -206,14 +206,14 @@ macro_rules! decl_tests { ChargeTransactionPayment::from(1), &1, CALL, - &info_from_weight(Weight::from_ref_time(1)), + &info_from_weight(Weight::from_parts(1, 0)), 1, ).is_err()); assert!( as SignedExtension>::pre_dispatch( ChargeTransactionPayment::from(0), &1, CALL, - &info_from_weight(Weight::from_ref_time(1)), + &info_from_weight(Weight::from_parts(1, 0)), 1, ).is_err()); }); diff --git a/frame/balances/src/tests_composite.rs b/frame/balances/src/tests_composite.rs index 6f371f1e2..765ab194d 100644 --- a/frame/balances/src/tests_composite.rs +++ b/frame/balances/src/tests_composite.rs @@ -48,7 +48,7 @@ frame_support::construct_runtime!( parameter_types! { pub BlockWeights: frame_system::limits::BlockWeights = frame_system::limits::BlockWeights::simple_max( - frame_support::weights::Weight::from_ref_time(1024).set_proof_size(u64::MAX), + frame_support::weights::Weight::from_parts(1024, u64::MAX), ); pub static ExistentialDeposit: u64 = 0; } diff --git a/frame/balances/src/tests_local.rs b/frame/balances/src/tests_local.rs index 0d57a3320..929f77b54 100644 --- a/frame/balances/src/tests_local.rs +++ b/frame/balances/src/tests_local.rs @@ -49,7 +49,7 @@ frame_support::construct_runtime!( parameter_types! { pub BlockWeights: frame_system::limits::BlockWeights = frame_system::limits::BlockWeights::simple_max( - frame_support::weights::Weight::from_ref_time(1024).set_proof_size(u64::MAX), + frame_support::weights::Weight::from_parts(1024, u64::MAX), ); pub static ExistentialDeposit: u64 = 0; } diff --git a/frame/balances/src/tests_reentrancy.rs b/frame/balances/src/tests_reentrancy.rs index 1699a8796..828dfa237 100644 --- a/frame/balances/src/tests_reentrancy.rs +++ b/frame/balances/src/tests_reentrancy.rs @@ -52,7 +52,7 @@ frame_support::construct_runtime!( parameter_types! { pub BlockWeights: frame_system::limits::BlockWeights = frame_system::limits::BlockWeights::simple_max( - frame_support::weights::Weight::from_ref_time(1024).set_proof_size(u64::MAX), + frame_support::weights::Weight::from_parts(1024, u64::MAX), ); pub static ExistentialDeposit: u64 = 0; } diff --git a/frame/balances/src/weights.rs b/frame/balances/src/weights.rs index 8c307ff90..cdc657ce1 100644 --- a/frame/balances/src/weights.rs +++ b/frame/balances/src/weights.rs @@ -68,8 +68,8 @@ impl WeightInfo for SubstrateWeight { // Measured: `0` // Estimated: `3593` // Minimum execution time: 37_815 nanoseconds. - Weight::from_ref_time(38_109_000) - .saturating_add(Weight::from_proof_size(3593)) + Weight::from_parts(38_109_000, 0) + .saturating_add(Weight::from_parts(0, 3593)) .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -80,8 +80,8 @@ impl WeightInfo for SubstrateWeight { // Measured: `0` // Estimated: `3593` // Minimum execution time: 28_184 nanoseconds. - Weight::from_ref_time(49_250_000) - .saturating_add(Weight::from_proof_size(3593)) + Weight::from_parts(49_250_000, 0) + .saturating_add(Weight::from_parts(0, 3593)) .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -92,8 +92,8 @@ impl WeightInfo for SubstrateWeight { // Measured: `206` // Estimated: `3593` // Minimum execution time: 17_474 nanoseconds. - Weight::from_ref_time(17_777_000) - .saturating_add(Weight::from_proof_size(3593)) + Weight::from_parts(17_777_000, 0) + .saturating_add(Weight::from_parts(0, 3593)) .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -104,8 +104,8 @@ impl WeightInfo for SubstrateWeight { // Measured: `206` // Estimated: `3593` // Minimum execution time: 20_962 nanoseconds. - Weight::from_ref_time(21_419_000) - .saturating_add(Weight::from_proof_size(3593)) + Weight::from_parts(21_419_000, 0) + .saturating_add(Weight::from_parts(0, 3593)) .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -116,8 +116,8 @@ impl WeightInfo for SubstrateWeight { // Measured: `135` // Estimated: `6196` // Minimum execution time: 39_713 nanoseconds. - Weight::from_ref_time(40_360_000) - .saturating_add(Weight::from_proof_size(6196)) + Weight::from_parts(40_360_000, 0) + .saturating_add(Weight::from_parts(0, 6196)) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().writes(2_u64)) } @@ -128,8 +128,8 @@ impl WeightInfo for SubstrateWeight { // Measured: `0` // Estimated: `3593` // Minimum execution time: 34_878 nanoseconds. - Weight::from_ref_time(35_121_000) - .saturating_add(Weight::from_proof_size(3593)) + Weight::from_parts(35_121_000, 0) + .saturating_add(Weight::from_parts(0, 3593)) .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -140,8 +140,8 @@ impl WeightInfo for SubstrateWeight { // Measured: `206` // Estimated: `3593` // Minimum execution time: 16_790 nanoseconds. - Weight::from_ref_time(17_029_000) - .saturating_add(Weight::from_proof_size(3593)) + Weight::from_parts(17_029_000, 0) + .saturating_add(Weight::from_parts(0, 3593)) .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -156,8 +156,8 @@ impl WeightInfo for () { // Measured: `0` // Estimated: `3593` // Minimum execution time: 37_815 nanoseconds. - Weight::from_ref_time(38_109_000) - .saturating_add(Weight::from_proof_size(3593)) + Weight::from_parts(38_109_000, 0) + .saturating_add(Weight::from_parts(0, 3593)) .saturating_add(RocksDbWeight::get().reads(1_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } @@ -168,8 +168,8 @@ impl WeightInfo for () { // Measured: `0` // Estimated: `3593` // Minimum execution time: 28_184 nanoseconds. - Weight::from_ref_time(49_250_000) - .saturating_add(Weight::from_proof_size(3593)) + Weight::from_parts(49_250_000, 0) + .saturating_add(Weight::from_parts(0, 3593)) .saturating_add(RocksDbWeight::get().reads(1_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } @@ -180,8 +180,8 @@ impl WeightInfo for () { // Measured: `206` // Estimated: `3593` // Minimum execution time: 17_474 nanoseconds. - Weight::from_ref_time(17_777_000) - .saturating_add(Weight::from_proof_size(3593)) + Weight::from_parts(17_777_000, 0) + .saturating_add(Weight::from_parts(0, 3593)) .saturating_add(RocksDbWeight::get().reads(1_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } @@ -192,8 +192,8 @@ impl WeightInfo for () { // Measured: `206` // Estimated: `3593` // Minimum execution time: 20_962 nanoseconds. - Weight::from_ref_time(21_419_000) - .saturating_add(Weight::from_proof_size(3593)) + Weight::from_parts(21_419_000, 0) + .saturating_add(Weight::from_parts(0, 3593)) .saturating_add(RocksDbWeight::get().reads(1_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } @@ -204,8 +204,8 @@ impl WeightInfo for () { // Measured: `135` // Estimated: `6196` // Minimum execution time: 39_713 nanoseconds. - Weight::from_ref_time(40_360_000) - .saturating_add(Weight::from_proof_size(6196)) + Weight::from_parts(40_360_000, 0) + .saturating_add(Weight::from_parts(0, 6196)) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().writes(2_u64)) } @@ -216,8 +216,8 @@ impl WeightInfo for () { // Measured: `0` // Estimated: `3593` // Minimum execution time: 34_878 nanoseconds. - Weight::from_ref_time(35_121_000) - .saturating_add(Weight::from_proof_size(3593)) + Weight::from_parts(35_121_000, 0) + .saturating_add(Weight::from_parts(0, 3593)) .saturating_add(RocksDbWeight::get().reads(1_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } @@ -228,8 +228,8 @@ impl WeightInfo for () { // Measured: `206` // Estimated: `3593` // Minimum execution time: 16_790 nanoseconds. - Weight::from_ref_time(17_029_000) - .saturating_add(Weight::from_proof_size(3593)) + Weight::from_parts(17_029_000, 0) + .saturating_add(Weight::from_parts(0, 3593)) .saturating_add(RocksDbWeight::get().reads(1_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } diff --git a/frame/beefy/src/default_weights.rs b/frame/beefy/src/default_weights.rs index 7743edf25..b15f1c88f 100644 --- a/frame/beefy/src/default_weights.rs +++ b/frame/beefy/src/default_weights.rs @@ -33,18 +33,19 @@ impl crate::WeightInfo for () { const MAX_NOMINATORS: u64 = 200; // checking membership proof - Weight::from_ref_time(35u64 * WEIGHT_REF_TIME_PER_MICROS) + Weight::from_parts(35u64 * WEIGHT_REF_TIME_PER_MICROS, 0) .saturating_add( - Weight::from_ref_time(175u64 * WEIGHT_REF_TIME_PER_NANOS) + Weight::from_parts(175u64 * WEIGHT_REF_TIME_PER_NANOS, 0) .saturating_mul(validator_count), ) .saturating_add(DbWeight::get().reads(5)) // check equivocation proof - .saturating_add(Weight::from_ref_time(95u64 * WEIGHT_REF_TIME_PER_MICROS)) + .saturating_add(Weight::from_parts(95u64 * WEIGHT_REF_TIME_PER_MICROS, 0)) // report offence - .saturating_add(Weight::from_ref_time(110u64 * WEIGHT_REF_TIME_PER_MICROS)) - .saturating_add(Weight::from_ref_time( + .saturating_add(Weight::from_parts(110u64 * WEIGHT_REF_TIME_PER_MICROS, 0)) + .saturating_add(Weight::from_parts( 25u64 * WEIGHT_REF_TIME_PER_MICROS * MAX_NOMINATORS, + 0, )) .saturating_add(DbWeight::get().reads(14 + 3 * MAX_NOMINATORS)) .saturating_add(DbWeight::get().writes(10 + 3 * MAX_NOMINATORS)) diff --git a/frame/benchmarking/pov/src/weights.rs b/frame/benchmarking/pov/src/weights.rs index 948d268cf..df6dba33b 100644 --- a/frame/benchmarking/pov/src/weights.rs +++ b/frame/benchmarking/pov/src/weights.rs @@ -73,8 +73,8 @@ impl WeightInfo for SubstrateWeight { // Measured: `136` // Estimated: `1489` // Minimum execution time: 1_968 nanoseconds. - Weight::from_ref_time(2_060_000) - .saturating_add(Weight::from_proof_size(1489)) + Weight::from_parts(2_060_000, 0) + .saturating_add(Weight::from_parts(0, 1489)) .saturating_add(T::DbWeight::get().reads(1_u64)) } /// Storage: Pov Value (r:1 w:0) @@ -84,8 +84,8 @@ impl WeightInfo for SubstrateWeight { // Measured: `136` // Estimated: `0` // Minimum execution time: 1_934 nanoseconds. - Weight::from_ref_time(2_092_000) - .saturating_add(Weight::from_proof_size(0)) + Weight::from_parts(2_092_000, 0) + .saturating_add(Weight::from_parts(0, 0)) .saturating_add(T::DbWeight::get().reads(1_u64)) } /// Storage: Pov Value (r:1 w:0) @@ -97,8 +97,8 @@ impl WeightInfo for SubstrateWeight { // Measured: `160` // Estimated: `1649` // Minimum execution time: 2_605 nanoseconds. - Weight::from_ref_time(2_786_000) - .saturating_add(Weight::from_proof_size(1649)) + Weight::from_parts(2_786_000, 0) + .saturating_add(Weight::from_parts(0, 1649)) .saturating_add(T::DbWeight::get().reads(2_u64)) } /// Storage: Pov Value (r:1 w:0) @@ -108,8 +108,8 @@ impl WeightInfo for SubstrateWeight { // Measured: `136` // Estimated: `1489` // Minimum execution time: 2_019 nanoseconds. - Weight::from_ref_time(2_214_000) - .saturating_add(Weight::from_proof_size(1489)) + Weight::from_parts(2_214_000, 0) + .saturating_add(Weight::from_parts(0, 1489)) .saturating_add(T::DbWeight::get().reads(1_u64)) } /// Storage: Pov Value (r:0 w:1) @@ -119,8 +119,8 @@ impl WeightInfo for SubstrateWeight { // Measured: `0` // Estimated: `0` // Minimum execution time: 279 nanoseconds. - Weight::from_ref_time(357_000) - .saturating_add(Weight::from_proof_size(0)) + Weight::from_parts(357_000, 0) + .saturating_add(Weight::from_parts(0, 0)) .saturating_add(T::DbWeight::get().writes(1_u64)) } /// Storage: Pov Value (r:0 w:1) @@ -130,8 +130,8 @@ impl WeightInfo for SubstrateWeight { // Measured: `0` // Estimated: `0` // Minimum execution time: 291 nanoseconds. - Weight::from_ref_time(378_000) - .saturating_add(Weight::from_proof_size(0)) + Weight::from_parts(378_000, 0) + .saturating_add(Weight::from_parts(0, 0)) .saturating_add(T::DbWeight::get().writes(1_u64)) } /// Storage: Pov Map1M (r:1 w:0) @@ -141,8 +141,8 @@ impl WeightInfo for SubstrateWeight { // Measured: `1275` // Estimated: `4740` // Minimum execution time: 5_077 nanoseconds. - Weight::from_ref_time(5_400_000) - .saturating_add(Weight::from_proof_size(4740)) + Weight::from_parts(5_400_000, 0) + .saturating_add(Weight::from_parts(0, 4740)) .saturating_add(T::DbWeight::get().reads(1_u64)) } /// Storage: Pov Map1M (r:1 w:0) @@ -152,8 +152,8 @@ impl WeightInfo for SubstrateWeight { // Measured: `1544` // Estimated: `5009` // Minimum execution time: 5_878 nanoseconds. - Weight::from_ref_time(6_239_000) - .saturating_add(Weight::from_proof_size(5009)) + Weight::from_parts(6_239_000, 0) + .saturating_add(Weight::from_parts(0, 5009)) .saturating_add(T::DbWeight::get().reads(1_u64)) } /// Storage: Pov Map1M (r:1 w:0) @@ -163,8 +163,8 @@ impl WeightInfo for SubstrateWeight { // Measured: `2044` // Estimated: `5509` // Minimum execution time: 7_282 nanoseconds. - Weight::from_ref_time(8_022_000) - .saturating_add(Weight::from_proof_size(5509)) + Weight::from_parts(8_022_000, 0) + .saturating_add(Weight::from_parts(0, 5509)) .saturating_add(T::DbWeight::get().reads(1_u64)) } /// Storage: Pov Map1M (r:100 w:0) @@ -178,16 +178,16 @@ impl WeightInfo for SubstrateWeight { // Measured: `515 + n * (188 ±0) + m * (188 ±0)` // Estimated: `1980 + n * (3006 ±0) + m * (2511 ±0)` // Minimum execution time: 195_406 nanoseconds. - Weight::from_ref_time(129_093_464) - .saturating_add(Weight::from_proof_size(1980)) + Weight::from_parts(129_093_464, 0) + .saturating_add(Weight::from_parts(0, 1980)) // Standard Error: 12_134 - .saturating_add(Weight::from_ref_time(855_330).saturating_mul(n.into())) + .saturating_add(Weight::from_parts(855_330, 0).saturating_mul(n.into())) // Standard Error: 12_134 - .saturating_add(Weight::from_ref_time(870_523).saturating_mul(m.into())) + .saturating_add(Weight::from_parts(870_523, 0).saturating_mul(m.into())) .saturating_add(T::DbWeight::get().reads((1_u64).saturating_mul(n.into()))) .saturating_add(T::DbWeight::get().reads((1_u64).saturating_mul(m.into()))) - .saturating_add(Weight::from_proof_size(3006).saturating_mul(n.into())) - .saturating_add(Weight::from_proof_size(2511).saturating_mul(m.into())) + .saturating_add(Weight::from_parts(0, 3006).saturating_mul(n.into())) + .saturating_add(Weight::from_parts(0, 2511).saturating_mul(m.into())) } /// Storage: Pov Map1M (r:100 w:0) /// Proof: Pov Map1M (max_values: Some(1000000), max_size: Some(36), added: 2511, mode: Ignored) @@ -200,16 +200,16 @@ impl WeightInfo for SubstrateWeight { // Measured: `515 + n * (188 ±0) + m * (188 ±0)` // Estimated: `1685 + n * (3195 ±0) + m * (189 ±0)` // Minimum execution time: 195_053 nanoseconds. - Weight::from_ref_time(131_322_479) - .saturating_add(Weight::from_proof_size(1685)) + Weight::from_parts(131_322_479, 0) + .saturating_add(Weight::from_parts(0, 1685)) // Standard Error: 12_161 - .saturating_add(Weight::from_ref_time(843_047).saturating_mul(n.into())) + .saturating_add(Weight::from_parts(843_047, 0).saturating_mul(n.into())) // Standard Error: 12_161 - .saturating_add(Weight::from_ref_time(858_668).saturating_mul(m.into())) + .saturating_add(Weight::from_parts(858_668, 0).saturating_mul(m.into())) .saturating_add(T::DbWeight::get().reads((1_u64).saturating_mul(n.into()))) .saturating_add(T::DbWeight::get().reads((1_u64).saturating_mul(m.into()))) - .saturating_add(Weight::from_proof_size(3195).saturating_mul(n.into())) - .saturating_add(Weight::from_proof_size(189).saturating_mul(m.into())) + .saturating_add(Weight::from_parts(0, 3195).saturating_mul(n.into())) + .saturating_add(Weight::from_parts(0, 189).saturating_mul(m.into())) } /// Storage: Pov Map1M (r:1 w:0) /// Proof: Pov Map1M (max_values: Some(1000000), max_size: Some(36), added: 2511, mode: MaxEncodedLen) @@ -219,10 +219,10 @@ impl WeightInfo for SubstrateWeight { // Measured: `170` // Estimated: `3501` // Minimum execution time: 22 nanoseconds. - Weight::from_ref_time(2_334_945) - .saturating_add(Weight::from_proof_size(3501)) + Weight::from_parts(2_334_945, 0) + .saturating_add(Weight::from_parts(0, 3501)) // Standard Error: 624 - .saturating_add(Weight::from_ref_time(282_046).saturating_mul(n.into())) + .saturating_add(Weight::from_parts(282_046, 0).saturating_mul(n.into())) .saturating_add(T::DbWeight::get().reads(1_u64)) } /// Storage: Pov Map1M (r:100 w:0) @@ -233,12 +233,12 @@ impl WeightInfo for SubstrateWeight { // Measured: `147 + n * (40 ±0)` // Estimated: `990 + n * (2511 ±0)` // Minimum execution time: 20 nanoseconds. - Weight::from_ref_time(525_027) - .saturating_add(Weight::from_proof_size(990)) + Weight::from_parts(525_027, 0) + .saturating_add(Weight::from_parts(0, 990)) // Standard Error: 2_767 - .saturating_add(Weight::from_ref_time(3_887_350).saturating_mul(n.into())) + .saturating_add(Weight::from_parts(3_887_350, 0).saturating_mul(n.into())) .saturating_add(T::DbWeight::get().reads((1_u64).saturating_mul(n.into()))) - .saturating_add(Weight::from_proof_size(2511).saturating_mul(n.into())) + .saturating_add(Weight::from_parts(0, 2511).saturating_mul(n.into())) } /// Storage: Pov DoubleMap1M (r:1024 w:0) /// Proof: Pov DoubleMap1M (max_values: Some(1000000), max_size: Some(68), added: 2543, mode: MaxEncodedLen) @@ -248,12 +248,12 @@ impl WeightInfo for SubstrateWeight { // Measured: `21938 + n * (57 ±0)` // Estimated: `990 + n * (2543 ±0)` // Minimum execution time: 34 nanoseconds. - Weight::from_ref_time(18_341_393) - .saturating_add(Weight::from_proof_size(990)) + Weight::from_parts(18_341_393, 0) + .saturating_add(Weight::from_parts(0, 990)) // Standard Error: 1_312 - .saturating_add(Weight::from_ref_time(2_053_135).saturating_mul(n.into())) + .saturating_add(Weight::from_parts(2_053_135, 0).saturating_mul(n.into())) .saturating_add(T::DbWeight::get().reads((1_u64).saturating_mul(n.into()))) - .saturating_add(Weight::from_proof_size(2543).saturating_mul(n.into())) + .saturating_add(Weight::from_parts(0, 2543).saturating_mul(n.into())) } /// Storage: Pov BoundedValue (r:1 w:0) /// Proof: Pov BoundedValue (max_values: Some(1), max_size: Some(33), added: 528, mode: MaxEncodedLen) @@ -262,8 +262,8 @@ impl WeightInfo for SubstrateWeight { // Measured: `109` // Estimated: `1518` // Minimum execution time: 1_163 nanoseconds. - Weight::from_ref_time(1_274_000) - .saturating_add(Weight::from_proof_size(1518)) + Weight::from_parts(1_274_000, 0) + .saturating_add(Weight::from_parts(0, 1518)) .saturating_add(T::DbWeight::get().reads(1_u64)) } /// Storage: Pov UnboundedValue (r:1 w:0) @@ -273,8 +273,8 @@ impl WeightInfo for SubstrateWeight { // Measured: `109` // Estimated: `1594` // Minimum execution time: 1_167 nanoseconds. - Weight::from_ref_time(1_367_000) - .saturating_add(Weight::from_proof_size(1594)) + Weight::from_parts(1_367_000, 0) + .saturating_add(Weight::from_parts(0, 1594)) .saturating_add(T::DbWeight::get().reads(1_u64)) } /// Storage: Pov UnboundedValue (r:1 w:0) @@ -284,8 +284,8 @@ impl WeightInfo for SubstrateWeight { // Measured: `109` // Estimated: `0` // Minimum execution time: 1_155 nanoseconds. - Weight::from_ref_time(1_248_000) - .saturating_add(Weight::from_proof_size(0)) + Weight::from_parts(1_248_000, 0) + .saturating_add(Weight::from_parts(0, 0)) .saturating_add(T::DbWeight::get().reads(1_u64)) } /// Storage: Pov UnboundedValue (r:1 w:0) @@ -297,8 +297,8 @@ impl WeightInfo for SubstrateWeight { // Measured: `109` // Estimated: `3112` // Minimum execution time: 1_424 nanoseconds. - Weight::from_ref_time(1_601_000) - .saturating_add(Weight::from_proof_size(3112)) + Weight::from_parts(1_601_000, 0) + .saturating_add(Weight::from_parts(0, 3112)) .saturating_add(T::DbWeight::get().reads(2_u64)) } /// Storage: Pov LargeValue (r:1 w:0) @@ -309,12 +309,12 @@ impl WeightInfo for SubstrateWeight { // Measured: `174 + l * (1 ±0)` // Estimated: `1656 + l * (1 ±0)` // Minimum execution time: 1_744 nanoseconds. - Weight::from_ref_time(1_800_000) - .saturating_add(Weight::from_proof_size(1656)) + Weight::from_parts(1_800_000, 0) + .saturating_add(Weight::from_parts(0, 1656)) // Standard Error: 4 - .saturating_add(Weight::from_ref_time(443).saturating_mul(l.into())) + .saturating_add(Weight::from_parts(443, 0).saturating_mul(l.into())) .saturating_add(T::DbWeight::get().reads(1_u64)) - .saturating_add(Weight::from_proof_size(1).saturating_mul(l.into())) + .saturating_add(Weight::from_parts(0, 1).saturating_mul(l.into())) } /// Storage: Pov LargeValue (r:1 w:0) /// Proof: Pov LargeValue (max_values: Some(1), max_size: Some(4194308), added: 4194803, mode: MaxEncodedLen) @@ -324,10 +324,10 @@ impl WeightInfo for SubstrateWeight { // Measured: `174 + l * (1 ±0)` // Estimated: `4195793` // Minimum execution time: 1_770 nanoseconds. - Weight::from_ref_time(1_813_000) - .saturating_add(Weight::from_proof_size(4195793)) + Weight::from_parts(1_813_000, 0) + .saturating_add(Weight::from_parts(0, 4195793)) // Standard Error: 6 - .saturating_add(Weight::from_ref_time(495).saturating_mul(l.into())) + .saturating_add(Weight::from_parts(495, 0).saturating_mul(l.into())) .saturating_add(T::DbWeight::get().reads(1_u64)) } /// Storage: Pov LargeValue (r:1 w:0) @@ -340,12 +340,12 @@ impl WeightInfo for SubstrateWeight { // Measured: `235 + l * (2 ±0)` // Estimated: `3428 + l * (4 ±0)` // Minimum execution time: 2_349 nanoseconds. - Weight::from_ref_time(2_423_000) - .saturating_add(Weight::from_proof_size(3428)) + Weight::from_parts(2_423_000, 0) + .saturating_add(Weight::from_parts(0, 3428)) // Standard Error: 11 - .saturating_add(Weight::from_ref_time(950).saturating_mul(l.into())) + .saturating_add(Weight::from_parts(950, 0).saturating_mul(l.into())) .saturating_add(T::DbWeight::get().reads(2_u64)) - .saturating_add(Weight::from_proof_size(4).saturating_mul(l.into())) + .saturating_add(Weight::from_parts(0, 4).saturating_mul(l.into())) } /// Storage: Pov LargeValue (r:1 w:0) /// Proof: Pov LargeValue (max_values: Some(1), max_size: Some(4194308), added: 4194803, mode: MaxEncodedLen) @@ -357,10 +357,10 @@ impl WeightInfo for SubstrateWeight { // Measured: `235 + l * (2 ±0)` // Estimated: `8391586` // Minimum execution time: 2_315 nanoseconds. - Weight::from_ref_time(2_409_000) - .saturating_add(Weight::from_proof_size(8391586)) + Weight::from_parts(2_409_000, 0) + .saturating_add(Weight::from_parts(0, 8391586)) // Standard Error: 12 - .saturating_add(Weight::from_ref_time(984).saturating_mul(l.into())) + .saturating_add(Weight::from_parts(984, 0).saturating_mul(l.into())) .saturating_add(T::DbWeight::get().reads(2_u64)) } /// Storage: Pov LargeValue (r:1 w:0) @@ -373,12 +373,12 @@ impl WeightInfo for SubstrateWeight { // Measured: `235 + l * (2 ±0)` // Estimated: `4197507 + l * (2 ±0)` // Minimum execution time: 2_370 nanoseconds. - Weight::from_ref_time(2_474_000) - .saturating_add(Weight::from_proof_size(4197507)) + Weight::from_parts(2_474_000, 0) + .saturating_add(Weight::from_parts(0, 4197507)) // Standard Error: 11 - .saturating_add(Weight::from_ref_time(956).saturating_mul(l.into())) + .saturating_add(Weight::from_parts(956, 0).saturating_mul(l.into())) .saturating_add(T::DbWeight::get().reads(2_u64)) - .saturating_add(Weight::from_proof_size(2).saturating_mul(l.into())) + .saturating_add(Weight::from_parts(0, 2).saturating_mul(l.into())) } /// Storage: Pov LargeValue (r:1 w:0) /// Proof: Pov LargeValue (max_values: Some(1), max_size: Some(4194308), added: 4194803, mode: Measured) @@ -390,12 +390,12 @@ impl WeightInfo for SubstrateWeight { // Measured: `235 + l * (2 ±0)` // Estimated: `4197507 + l * (2 ±0)` // Minimum execution time: 2_375 nanoseconds. - Weight::from_ref_time(2_420_000) - .saturating_add(Weight::from_proof_size(4197507)) + Weight::from_parts(2_420_000, 0) + .saturating_add(Weight::from_parts(0, 4197507)) // Standard Error: 9 - .saturating_add(Weight::from_ref_time(914).saturating_mul(l.into())) + .saturating_add(Weight::from_parts(914, 0).saturating_mul(l.into())) .saturating_add(T::DbWeight::get().reads(2_u64)) - .saturating_add(Weight::from_proof_size(2).saturating_mul(l.into())) + .saturating_add(Weight::from_parts(0, 2).saturating_mul(l.into())) } /// Storage: Pov UnboundedMap (r:1 w:0) /// Proof Skipped: Pov UnboundedMap (max_values: None, max_size: None, mode: Measured) @@ -407,12 +407,12 @@ impl WeightInfo for SubstrateWeight { // Measured: `293 + i * (8 ±0)` // Estimated: `7504 + i * (16 ±0)` // Minimum execution time: 3_305 nanoseconds. - Weight::from_ref_time(3_689_335) - .saturating_add(Weight::from_proof_size(7504)) + Weight::from_parts(3_689_335, 0) + .saturating_add(Weight::from_parts(0, 7504)) // Standard Error: 29 - .saturating_add(Weight::from_ref_time(638).saturating_mul(i.into())) + .saturating_add(Weight::from_parts(638, 0).saturating_mul(i.into())) .saturating_add(T::DbWeight::get().reads(2_u64)) - .saturating_add(Weight::from_proof_size(16).saturating_mul(i.into())) + .saturating_add(Weight::from_parts(0, 16).saturating_mul(i.into())) } /// Storage: Pov Map1M (r:1 w:0) /// Proof: Pov Map1M (max_values: Some(1000000), max_size: Some(36), added: 2511, mode: MaxEncodedLen) @@ -424,12 +424,12 @@ impl WeightInfo for SubstrateWeight { // Measured: `260 + i * (4 ±0)` // Estimated: `7223 + i * (4 ±0)` // Minimum execution time: 3_469 nanoseconds. - Weight::from_ref_time(3_878_896) - .saturating_add(Weight::from_proof_size(7223)) + Weight::from_parts(3_878_896, 0) + .saturating_add(Weight::from_parts(0, 7223)) // Standard Error: 33 - .saturating_add(Weight::from_ref_time(356).saturating_mul(i.into())) + .saturating_add(Weight::from_parts(356, 0).saturating_mul(i.into())) .saturating_add(T::DbWeight::get().reads(2_u64)) - .saturating_add(Weight::from_proof_size(4).saturating_mul(i.into())) + .saturating_add(Weight::from_parts(0, 4).saturating_mul(i.into())) } /// Storage: Pov Map1M (r:1 w:0) /// Proof: Pov Map1M (max_values: Some(1000000), max_size: Some(36), added: 2511, mode: MaxEncodedLen) @@ -441,28 +441,28 @@ impl WeightInfo for SubstrateWeight { // Measured: `260 + i * (4 ±0)` // Estimated: `3758 + i * (4 ±0)` // Minimum execution time: 3_442 nanoseconds. - Weight::from_ref_time(3_881_051) - .saturating_add(Weight::from_proof_size(3758)) + Weight::from_parts(3_881_051, 0) + .saturating_add(Weight::from_parts(0, 3758)) // Standard Error: 35 - .saturating_add(Weight::from_ref_time(384).saturating_mul(i.into())) + .saturating_add(Weight::from_parts(384, 0).saturating_mul(i.into())) .saturating_add(T::DbWeight::get().reads(2_u64)) - .saturating_add(Weight::from_proof_size(4).saturating_mul(i.into())) + .saturating_add(Weight::from_parts(0, 4).saturating_mul(i.into())) } fn emit_event() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` // Minimum execution time: 1_619 nanoseconds. - Weight::from_ref_time(1_728_000) - .saturating_add(Weight::from_proof_size(0)) + Weight::from_parts(1_728_000, 0) + .saturating_add(Weight::from_parts(0, 0)) } fn noop() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` // Minimum execution time: 546 nanoseconds. - Weight::from_ref_time(640_000) - .saturating_add(Weight::from_proof_size(0)) + Weight::from_parts(640_000, 0) + .saturating_add(Weight::from_parts(0, 0)) } } @@ -475,8 +475,8 @@ impl WeightInfo for () { // Measured: `136` // Estimated: `1489` // Minimum execution time: 1_968 nanoseconds. - Weight::from_ref_time(2_060_000) - .saturating_add(Weight::from_proof_size(1489)) + Weight::from_parts(2_060_000, 0) + .saturating_add(Weight::from_parts(0, 1489)) .saturating_add(RocksDbWeight::get().reads(1_u64)) } /// Storage: Pov Value (r:1 w:0) @@ -486,8 +486,8 @@ impl WeightInfo for () { // Measured: `136` // Estimated: `0` // Minimum execution time: 1_934 nanoseconds. - Weight::from_ref_time(2_092_000) - .saturating_add(Weight::from_proof_size(0)) + Weight::from_parts(2_092_000, 0) + .saturating_add(Weight::from_parts(0, 0)) .saturating_add(RocksDbWeight::get().reads(1_u64)) } /// Storage: Pov Value (r:1 w:0) @@ -499,8 +499,8 @@ impl WeightInfo for () { // Measured: `160` // Estimated: `1649` // Minimum execution time: 2_605 nanoseconds. - Weight::from_ref_time(2_786_000) - .saturating_add(Weight::from_proof_size(1649)) + Weight::from_parts(2_786_000, 0) + .saturating_add(Weight::from_parts(0, 1649)) .saturating_add(RocksDbWeight::get().reads(2_u64)) } /// Storage: Pov Value (r:1 w:0) @@ -510,8 +510,8 @@ impl WeightInfo for () { // Measured: `136` // Estimated: `1489` // Minimum execution time: 2_019 nanoseconds. - Weight::from_ref_time(2_214_000) - .saturating_add(Weight::from_proof_size(1489)) + Weight::from_parts(2_214_000, 0) + .saturating_add(Weight::from_parts(0, 1489)) .saturating_add(RocksDbWeight::get().reads(1_u64)) } /// Storage: Pov Value (r:0 w:1) @@ -521,8 +521,8 @@ impl WeightInfo for () { // Measured: `0` // Estimated: `0` // Minimum execution time: 279 nanoseconds. - Weight::from_ref_time(357_000) - .saturating_add(Weight::from_proof_size(0)) + Weight::from_parts(357_000, 0) + .saturating_add(Weight::from_parts(0, 0)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } /// Storage: Pov Value (r:0 w:1) @@ -532,8 +532,8 @@ impl WeightInfo for () { // Measured: `0` // Estimated: `0` // Minimum execution time: 291 nanoseconds. - Weight::from_ref_time(378_000) - .saturating_add(Weight::from_proof_size(0)) + Weight::from_parts(378_000, 0) + .saturating_add(Weight::from_parts(0, 0)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } /// Storage: Pov Map1M (r:1 w:0) @@ -543,8 +543,8 @@ impl WeightInfo for () { // Measured: `1275` // Estimated: `4740` // Minimum execution time: 5_077 nanoseconds. - Weight::from_ref_time(5_400_000) - .saturating_add(Weight::from_proof_size(4740)) + Weight::from_parts(5_400_000, 0) + .saturating_add(Weight::from_parts(0, 4740)) .saturating_add(RocksDbWeight::get().reads(1_u64)) } /// Storage: Pov Map1M (r:1 w:0) @@ -554,8 +554,8 @@ impl WeightInfo for () { // Measured: `1544` // Estimated: `5009` // Minimum execution time: 5_878 nanoseconds. - Weight::from_ref_time(6_239_000) - .saturating_add(Weight::from_proof_size(5009)) + Weight::from_parts(6_239_000, 0) + .saturating_add(Weight::from_parts(0, 5009)) .saturating_add(RocksDbWeight::get().reads(1_u64)) } /// Storage: Pov Map1M (r:1 w:0) @@ -565,8 +565,8 @@ impl WeightInfo for () { // Measured: `2044` // Estimated: `5509` // Minimum execution time: 7_282 nanoseconds. - Weight::from_ref_time(8_022_000) - .saturating_add(Weight::from_proof_size(5509)) + Weight::from_parts(8_022_000, 0) + .saturating_add(Weight::from_parts(0, 5509)) .saturating_add(RocksDbWeight::get().reads(1_u64)) } /// Storage: Pov Map1M (r:100 w:0) @@ -580,16 +580,16 @@ impl WeightInfo for () { // Measured: `515 + n * (188 ±0) + m * (188 ±0)` // Estimated: `1980 + n * (3006 ±0) + m * (2511 ±0)` // Minimum execution time: 195_406 nanoseconds. - Weight::from_ref_time(129_093_464) - .saturating_add(Weight::from_proof_size(1980)) + Weight::from_parts(129_093_464, 0) + .saturating_add(Weight::from_parts(0, 1980)) // Standard Error: 12_134 - .saturating_add(Weight::from_ref_time(855_330).saturating_mul(n.into())) + .saturating_add(Weight::from_parts(855_330, 0).saturating_mul(n.into())) // Standard Error: 12_134 - .saturating_add(Weight::from_ref_time(870_523).saturating_mul(m.into())) + .saturating_add(Weight::from_parts(870_523, 0).saturating_mul(m.into())) .saturating_add(RocksDbWeight::get().reads((1_u64).saturating_mul(n.into()))) .saturating_add(RocksDbWeight::get().reads((1_u64).saturating_mul(m.into()))) - .saturating_add(Weight::from_proof_size(3006).saturating_mul(n.into())) - .saturating_add(Weight::from_proof_size(2511).saturating_mul(m.into())) + .saturating_add(Weight::from_parts(0, 3006).saturating_mul(n.into())) + .saturating_add(Weight::from_parts(0, 2511).saturating_mul(m.into())) } /// Storage: Pov Map1M (r:100 w:0) /// Proof: Pov Map1M (max_values: Some(1000000), max_size: Some(36), added: 2511, mode: Ignored) @@ -602,16 +602,16 @@ impl WeightInfo for () { // Measured: `515 + n * (188 ±0) + m * (188 ±0)` // Estimated: `1685 + n * (3195 ±0) + m * (189 ±0)` // Minimum execution time: 195_053 nanoseconds. - Weight::from_ref_time(131_322_479) - .saturating_add(Weight::from_proof_size(1685)) + Weight::from_parts(131_322_479, 0) + .saturating_add(Weight::from_parts(0, 1685)) // Standard Error: 12_161 - .saturating_add(Weight::from_ref_time(843_047).saturating_mul(n.into())) + .saturating_add(Weight::from_parts(843_047, 0).saturating_mul(n.into())) // Standard Error: 12_161 - .saturating_add(Weight::from_ref_time(858_668).saturating_mul(m.into())) + .saturating_add(Weight::from_parts(858_668, 0).saturating_mul(m.into())) .saturating_add(RocksDbWeight::get().reads((1_u64).saturating_mul(n.into()))) .saturating_add(RocksDbWeight::get().reads((1_u64).saturating_mul(m.into()))) - .saturating_add(Weight::from_proof_size(3195).saturating_mul(n.into())) - .saturating_add(Weight::from_proof_size(189).saturating_mul(m.into())) + .saturating_add(Weight::from_parts(0, 3195).saturating_mul(n.into())) + .saturating_add(Weight::from_parts(0, 189).saturating_mul(m.into())) } /// Storage: Pov Map1M (r:1 w:0) /// Proof: Pov Map1M (max_values: Some(1000000), max_size: Some(36), added: 2511, mode: MaxEncodedLen) @@ -621,10 +621,10 @@ impl WeightInfo for () { // Measured: `170` // Estimated: `3501` // Minimum execution time: 22 nanoseconds. - Weight::from_ref_time(2_334_945) - .saturating_add(Weight::from_proof_size(3501)) + Weight::from_parts(2_334_945, 0) + .saturating_add(Weight::from_parts(0, 3501)) // Standard Error: 624 - .saturating_add(Weight::from_ref_time(282_046).saturating_mul(n.into())) + .saturating_add(Weight::from_parts(282_046, 0).saturating_mul(n.into())) .saturating_add(RocksDbWeight::get().reads(1_u64)) } /// Storage: Pov Map1M (r:100 w:0) @@ -635,12 +635,12 @@ impl WeightInfo for () { // Measured: `147 + n * (40 ±0)` // Estimated: `990 + n * (2511 ±0)` // Minimum execution time: 20 nanoseconds. - Weight::from_ref_time(525_027) - .saturating_add(Weight::from_proof_size(990)) + Weight::from_parts(525_027, 0) + .saturating_add(Weight::from_parts(0, 990)) // Standard Error: 2_767 - .saturating_add(Weight::from_ref_time(3_887_350).saturating_mul(n.into())) + .saturating_add(Weight::from_parts(3_887_350, 0).saturating_mul(n.into())) .saturating_add(RocksDbWeight::get().reads((1_u64).saturating_mul(n.into()))) - .saturating_add(Weight::from_proof_size(2511).saturating_mul(n.into())) + .saturating_add(Weight::from_parts(0, 2511).saturating_mul(n.into())) } /// Storage: Pov DoubleMap1M (r:1024 w:0) /// Proof: Pov DoubleMap1M (max_values: Some(1000000), max_size: Some(68), added: 2543, mode: MaxEncodedLen) @@ -650,12 +650,12 @@ impl WeightInfo for () { // Measured: `21938 + n * (57 ±0)` // Estimated: `990 + n * (2543 ±0)` // Minimum execution time: 34 nanoseconds. - Weight::from_ref_time(18_341_393) - .saturating_add(Weight::from_proof_size(990)) + Weight::from_parts(18_341_393, 0) + .saturating_add(Weight::from_parts(0, 990)) // Standard Error: 1_312 - .saturating_add(Weight::from_ref_time(2_053_135).saturating_mul(n.into())) + .saturating_add(Weight::from_parts(2_053_135, 0).saturating_mul(n.into())) .saturating_add(RocksDbWeight::get().reads((1_u64).saturating_mul(n.into()))) - .saturating_add(Weight::from_proof_size(2543).saturating_mul(n.into())) + .saturating_add(Weight::from_parts(0, 2543).saturating_mul(n.into())) } /// Storage: Pov BoundedValue (r:1 w:0) /// Proof: Pov BoundedValue (max_values: Some(1), max_size: Some(33), added: 528, mode: MaxEncodedLen) @@ -664,8 +664,8 @@ impl WeightInfo for () { // Measured: `109` // Estimated: `1518` // Minimum execution time: 1_163 nanoseconds. - Weight::from_ref_time(1_274_000) - .saturating_add(Weight::from_proof_size(1518)) + Weight::from_parts(1_274_000, 0) + .saturating_add(Weight::from_parts(0, 1518)) .saturating_add(RocksDbWeight::get().reads(1_u64)) } /// Storage: Pov UnboundedValue (r:1 w:0) @@ -675,8 +675,8 @@ impl WeightInfo for () { // Measured: `109` // Estimated: `1594` // Minimum execution time: 1_167 nanoseconds. - Weight::from_ref_time(1_367_000) - .saturating_add(Weight::from_proof_size(1594)) + Weight::from_parts(1_367_000, 0) + .saturating_add(Weight::from_parts(0, 1594)) .saturating_add(RocksDbWeight::get().reads(1_u64)) } /// Storage: Pov UnboundedValue (r:1 w:0) @@ -686,8 +686,8 @@ impl WeightInfo for () { // Measured: `109` // Estimated: `0` // Minimum execution time: 1_155 nanoseconds. - Weight::from_ref_time(1_248_000) - .saturating_add(Weight::from_proof_size(0)) + Weight::from_parts(1_248_000, 0) + .saturating_add(Weight::from_parts(0, 0)) .saturating_add(RocksDbWeight::get().reads(1_u64)) } /// Storage: Pov UnboundedValue (r:1 w:0) @@ -699,8 +699,8 @@ impl WeightInfo for () { // Measured: `109` // Estimated: `3112` // Minimum execution time: 1_424 nanoseconds. - Weight::from_ref_time(1_601_000) - .saturating_add(Weight::from_proof_size(3112)) + Weight::from_parts(1_601_000, 0) + .saturating_add(Weight::from_parts(0, 3112)) .saturating_add(RocksDbWeight::get().reads(2_u64)) } /// Storage: Pov LargeValue (r:1 w:0) @@ -711,12 +711,12 @@ impl WeightInfo for () { // Measured: `174 + l * (1 ±0)` // Estimated: `1656 + l * (1 ±0)` // Minimum execution time: 1_744 nanoseconds. - Weight::from_ref_time(1_800_000) - .saturating_add(Weight::from_proof_size(1656)) + Weight::from_parts(1_800_000, 0) + .saturating_add(Weight::from_parts(0, 1656)) // Standard Error: 4 - .saturating_add(Weight::from_ref_time(443).saturating_mul(l.into())) + .saturating_add(Weight::from_parts(443, 0).saturating_mul(l.into())) .saturating_add(RocksDbWeight::get().reads(1_u64)) - .saturating_add(Weight::from_proof_size(1).saturating_mul(l.into())) + .saturating_add(Weight::from_parts(0, 1).saturating_mul(l.into())) } /// Storage: Pov LargeValue (r:1 w:0) /// Proof: Pov LargeValue (max_values: Some(1), max_size: Some(4194308), added: 4194803, mode: MaxEncodedLen) @@ -726,10 +726,10 @@ impl WeightInfo for () { // Measured: `174 + l * (1 ±0)` // Estimated: `4195793` // Minimum execution time: 1_770 nanoseconds. - Weight::from_ref_time(1_813_000) - .saturating_add(Weight::from_proof_size(4195793)) + Weight::from_parts(1_813_000, 0) + .saturating_add(Weight::from_parts(0, 4195793)) // Standard Error: 6 - .saturating_add(Weight::from_ref_time(495).saturating_mul(l.into())) + .saturating_add(Weight::from_parts(495, 0).saturating_mul(l.into())) .saturating_add(RocksDbWeight::get().reads(1_u64)) } /// Storage: Pov LargeValue (r:1 w:0) @@ -742,12 +742,12 @@ impl WeightInfo for () { // Measured: `235 + l * (2 ±0)` // Estimated: `3428 + l * (4 ±0)` // Minimum execution time: 2_349 nanoseconds. - Weight::from_ref_time(2_423_000) - .saturating_add(Weight::from_proof_size(3428)) + Weight::from_parts(2_423_000, 0) + .saturating_add(Weight::from_parts(0, 3428)) // Standard Error: 11 - .saturating_add(Weight::from_ref_time(950).saturating_mul(l.into())) + .saturating_add(Weight::from_parts(950, 0).saturating_mul(l.into())) .saturating_add(RocksDbWeight::get().reads(2_u64)) - .saturating_add(Weight::from_proof_size(4).saturating_mul(l.into())) + .saturating_add(Weight::from_parts(0, 4).saturating_mul(l.into())) } /// Storage: Pov LargeValue (r:1 w:0) /// Proof: Pov LargeValue (max_values: Some(1), max_size: Some(4194308), added: 4194803, mode: MaxEncodedLen) @@ -759,10 +759,10 @@ impl WeightInfo for () { // Measured: `235 + l * (2 ±0)` // Estimated: `8391586` // Minimum execution time: 2_315 nanoseconds. - Weight::from_ref_time(2_409_000) - .saturating_add(Weight::from_proof_size(8391586)) + Weight::from_parts(2_409_000, 0) + .saturating_add(Weight::from_parts(0, 8391586)) // Standard Error: 12 - .saturating_add(Weight::from_ref_time(984).saturating_mul(l.into())) + .saturating_add(Weight::from_parts(984, 0).saturating_mul(l.into())) .saturating_add(RocksDbWeight::get().reads(2_u64)) } /// Storage: Pov LargeValue (r:1 w:0) @@ -775,12 +775,12 @@ impl WeightInfo for () { // Measured: `235 + l * (2 ±0)` // Estimated: `4197507 + l * (2 ±0)` // Minimum execution time: 2_370 nanoseconds. - Weight::from_ref_time(2_474_000) - .saturating_add(Weight::from_proof_size(4197507)) + Weight::from_parts(2_474_000, 0) + .saturating_add(Weight::from_parts(0, 4197507)) // Standard Error: 11 - .saturating_add(Weight::from_ref_time(956).saturating_mul(l.into())) + .saturating_add(Weight::from_parts(956, 0).saturating_mul(l.into())) .saturating_add(RocksDbWeight::get().reads(2_u64)) - .saturating_add(Weight::from_proof_size(2).saturating_mul(l.into())) + .saturating_add(Weight::from_parts(0, 2).saturating_mul(l.into())) } /// Storage: Pov LargeValue (r:1 w:0) /// Proof: Pov LargeValue (max_values: Some(1), max_size: Some(4194308), added: 4194803, mode: Measured) @@ -792,12 +792,12 @@ impl WeightInfo for () { // Measured: `235 + l * (2 ±0)` // Estimated: `4197507 + l * (2 ±0)` // Minimum execution time: 2_375 nanoseconds. - Weight::from_ref_time(2_420_000) - .saturating_add(Weight::from_proof_size(4197507)) + Weight::from_parts(2_420_000, 0) + .saturating_add(Weight::from_parts(0, 4197507)) // Standard Error: 9 - .saturating_add(Weight::from_ref_time(914).saturating_mul(l.into())) + .saturating_add(Weight::from_parts(914, 0).saturating_mul(l.into())) .saturating_add(RocksDbWeight::get().reads(2_u64)) - .saturating_add(Weight::from_proof_size(2).saturating_mul(l.into())) + .saturating_add(Weight::from_parts(0, 2).saturating_mul(l.into())) } /// Storage: Pov UnboundedMap (r:1 w:0) /// Proof Skipped: Pov UnboundedMap (max_values: None, max_size: None, mode: Measured) @@ -809,12 +809,12 @@ impl WeightInfo for () { // Measured: `293 + i * (8 ±0)` // Estimated: `7504 + i * (16 ±0)` // Minimum execution time: 3_305 nanoseconds. - Weight::from_ref_time(3_689_335) - .saturating_add(Weight::from_proof_size(7504)) + Weight::from_parts(3_689_335, 0) + .saturating_add(Weight::from_parts(0, 7504)) // Standard Error: 29 - .saturating_add(Weight::from_ref_time(638).saturating_mul(i.into())) + .saturating_add(Weight::from_parts(638, 0).saturating_mul(i.into())) .saturating_add(RocksDbWeight::get().reads(2_u64)) - .saturating_add(Weight::from_proof_size(16).saturating_mul(i.into())) + .saturating_add(Weight::from_parts(0, 16).saturating_mul(i.into())) } /// Storage: Pov Map1M (r:1 w:0) /// Proof: Pov Map1M (max_values: Some(1000000), max_size: Some(36), added: 2511, mode: MaxEncodedLen) @@ -826,12 +826,12 @@ impl WeightInfo for () { // Measured: `260 + i * (4 ±0)` // Estimated: `7223 + i * (4 ±0)` // Minimum execution time: 3_469 nanoseconds. - Weight::from_ref_time(3_878_896) - .saturating_add(Weight::from_proof_size(7223)) + Weight::from_parts(3_878_896, 0) + .saturating_add(Weight::from_parts(0, 7223)) // Standard Error: 33 - .saturating_add(Weight::from_ref_time(356).saturating_mul(i.into())) + .saturating_add(Weight::from_parts(356, 0).saturating_mul(i.into())) .saturating_add(RocksDbWeight::get().reads(2_u64)) - .saturating_add(Weight::from_proof_size(4).saturating_mul(i.into())) + .saturating_add(Weight::from_parts(0, 4).saturating_mul(i.into())) } /// Storage: Pov Map1M (r:1 w:0) /// Proof: Pov Map1M (max_values: Some(1000000), max_size: Some(36), added: 2511, mode: MaxEncodedLen) @@ -843,27 +843,27 @@ impl WeightInfo for () { // Measured: `260 + i * (4 ±0)` // Estimated: `3758 + i * (4 ±0)` // Minimum execution time: 3_442 nanoseconds. - Weight::from_ref_time(3_881_051) - .saturating_add(Weight::from_proof_size(3758)) + Weight::from_parts(3_881_051, 0) + .saturating_add(Weight::from_parts(0, 3758)) // Standard Error: 35 - .saturating_add(Weight::from_ref_time(384).saturating_mul(i.into())) + .saturating_add(Weight::from_parts(384, 0).saturating_mul(i.into())) .saturating_add(RocksDbWeight::get().reads(2_u64)) - .saturating_add(Weight::from_proof_size(4).saturating_mul(i.into())) + .saturating_add(Weight::from_parts(0, 4).saturating_mul(i.into())) } fn emit_event() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` // Minimum execution time: 1_619 nanoseconds. - Weight::from_ref_time(1_728_000) - .saturating_add(Weight::from_proof_size(0)) + Weight::from_parts(1_728_000, 0) + .saturating_add(Weight::from_parts(0, 0)) } fn noop() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` // Minimum execution time: 546 nanoseconds. - Weight::from_ref_time(640_000) - .saturating_add(Weight::from_proof_size(0)) + Weight::from_parts(640_000, 0) + .saturating_add(Weight::from_parts(0, 0)) } } diff --git a/frame/benchmarking/src/weights.rs b/frame/benchmarking/src/weights.rs index 85dbb18c7..fdd4429f2 100644 --- a/frame/benchmarking/src/weights.rs +++ b/frame/benchmarking/src/weights.rs @@ -65,7 +65,7 @@ impl WeightInfo for SubstrateWeight { // Measured: `0` // Estimated: `0` // Minimum execution time: 138 nanoseconds. - Weight::from_ref_time(199_805) + Weight::from_parts(199_805, 0) } /// The range of component `i` is `[0, 1000000]`. fn subtraction(_i: u32, ) -> Weight { @@ -73,7 +73,7 @@ impl WeightInfo for SubstrateWeight { // Measured: `0` // Estimated: `0` // Minimum execution time: 142 nanoseconds. - Weight::from_ref_time(201_435) + Weight::from_parts(201_435, 0) } /// The range of component `i` is `[0, 1000000]`. fn multiplication(_i: u32, ) -> Weight { @@ -81,7 +81,7 @@ impl WeightInfo for SubstrateWeight { // Measured: `0` // Estimated: `0` // Minimum execution time: 138 nanoseconds. - Weight::from_ref_time(207_037) + Weight::from_parts(207_037, 0) } /// The range of component `i` is `[0, 1000000]`. fn division(_i: u32, ) -> Weight { @@ -89,14 +89,14 @@ impl WeightInfo for SubstrateWeight { // Measured: `0` // Estimated: `0` // Minimum execution time: 151 nanoseconds. - Weight::from_ref_time(205_150) + Weight::from_parts(205_150, 0) } fn hashing() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` // Minimum execution time: 21_950_884 nanoseconds. - Weight::from_ref_time(21_994_001_000) + Weight::from_parts(21_994_001_000, 0) } /// The range of component `i` is `[0, 100]`. fn sr25519_verification(i: u32, ) -> Weight { @@ -104,9 +104,9 @@ impl WeightInfo for SubstrateWeight { // Measured: `0` // Estimated: `0` // Minimum execution time: 168 nanoseconds. - Weight::from_ref_time(1_680_898) + Weight::from_parts(1_680_898, 0) // Standard Error: 10_291 - .saturating_add(Weight::from_ref_time(46_867_301).saturating_mul(i.into())) + .saturating_add(Weight::from_parts(46_867_301, 0).saturating_mul(i.into())) } } @@ -118,7 +118,7 @@ impl WeightInfo for () { // Measured: `0` // Estimated: `0` // Minimum execution time: 138 nanoseconds. - Weight::from_ref_time(199_805) + Weight::from_parts(199_805, 0) } /// The range of component `i` is `[0, 1000000]`. fn subtraction(_i: u32, ) -> Weight { @@ -126,7 +126,7 @@ impl WeightInfo for () { // Measured: `0` // Estimated: `0` // Minimum execution time: 142 nanoseconds. - Weight::from_ref_time(201_435) + Weight::from_parts(201_435, 0) } /// The range of component `i` is `[0, 1000000]`. fn multiplication(_i: u32, ) -> Weight { @@ -134,7 +134,7 @@ impl WeightInfo for () { // Measured: `0` // Estimated: `0` // Minimum execution time: 138 nanoseconds. - Weight::from_ref_time(207_037) + Weight::from_parts(207_037, 0) } /// The range of component `i` is `[0, 1000000]`. fn division(_i: u32, ) -> Weight { @@ -142,14 +142,14 @@ impl WeightInfo for () { // Measured: `0` // Estimated: `0` // Minimum execution time: 151 nanoseconds. - Weight::from_ref_time(205_150) + Weight::from_parts(205_150, 0) } fn hashing() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` // Minimum execution time: 21_950_884 nanoseconds. - Weight::from_ref_time(21_994_001_000) + Weight::from_parts(21_994_001_000, 0) } /// The range of component `i` is `[0, 100]`. fn sr25519_verification(i: u32, ) -> Weight { @@ -157,8 +157,8 @@ impl WeightInfo for () { // Measured: `0` // Estimated: `0` // Minimum execution time: 168 nanoseconds. - Weight::from_ref_time(1_680_898) + Weight::from_parts(1_680_898, 0) // Standard Error: 10_291 - .saturating_add(Weight::from_ref_time(46_867_301).saturating_mul(i.into())) + .saturating_add(Weight::from_parts(46_867_301, 0).saturating_mul(i.into())) } } diff --git a/frame/bounties/src/weights.rs b/frame/bounties/src/weights.rs index 174891f95..f0ea78b5f 100644 --- a/frame/bounties/src/weights.rs +++ b/frame/bounties/src/weights.rs @@ -80,7 +80,7 @@ impl WeightInfo for SubstrateWeight { // Minimum execution time: 22_787 nanoseconds. Weight::from_parts(23_898_632, 3102) // Standard Error: 141 - .saturating_add(Weight::from_ref_time(568).saturating_mul(d.into())) + .saturating_add(Weight::from_parts(568, 0).saturating_mul(d.into())) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().writes(4_u64)) } @@ -223,12 +223,12 @@ impl WeightInfo for SubstrateWeight { // Minimum execution time: 4_685 nanoseconds. Weight::from_parts(9_932_840, 897) // Standard Error: 14_301 - .saturating_add(Weight::from_ref_time(27_178_347).saturating_mul(b.into())) + .saturating_add(Weight::from_parts(27_178_347, 0).saturating_mul(b.into())) .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(T::DbWeight::get().reads((3_u64).saturating_mul(b.into()))) .saturating_add(T::DbWeight::get().writes(1_u64)) .saturating_add(T::DbWeight::get().writes((3_u64).saturating_mul(b.into()))) - .saturating_add(Weight::from_proof_size(7858).saturating_mul(b.into())) + .saturating_add(Weight::from_parts(0, 7858).saturating_mul(b.into())) } } @@ -250,7 +250,7 @@ impl WeightInfo for () { // Minimum execution time: 22_787 nanoseconds. Weight::from_parts(23_898_632, 3102) // Standard Error: 141 - .saturating_add(Weight::from_ref_time(568).saturating_mul(d.into())) + .saturating_add(Weight::from_parts(568, 0).saturating_mul(d.into())) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().writes(4_u64)) } @@ -393,11 +393,11 @@ impl WeightInfo for () { // Minimum execution time: 4_685 nanoseconds. Weight::from_parts(9_932_840, 897) // Standard Error: 14_301 - .saturating_add(Weight::from_ref_time(27_178_347).saturating_mul(b.into())) + .saturating_add(Weight::from_parts(27_178_347, 0).saturating_mul(b.into())) .saturating_add(RocksDbWeight::get().reads(1_u64)) .saturating_add(RocksDbWeight::get().reads((3_u64).saturating_mul(b.into()))) .saturating_add(RocksDbWeight::get().writes(1_u64)) .saturating_add(RocksDbWeight::get().writes((3_u64).saturating_mul(b.into()))) - .saturating_add(Weight::from_proof_size(7858).saturating_mul(b.into())) + .saturating_add(Weight::from_parts(0, 7858).saturating_mul(b.into())) } } diff --git a/frame/child-bounties/src/tests.rs b/frame/child-bounties/src/tests.rs index b4b24eb91..0172468ec 100644 --- a/frame/child-bounties/src/tests.rs +++ b/frame/child-bounties/src/tests.rs @@ -59,7 +59,7 @@ frame_support::construct_runtime!( ); parameter_types! { - pub const MaximumBlockWeight: Weight = Weight::from_ref_time(1024); + pub const MaximumBlockWeight: Weight = Weight::from_parts(1024, 0); pub const MaximumBlockLength: u32 = 2 * 1024; pub const AvailableBlockRatio: Perbill = Perbill::one(); } diff --git a/frame/child-bounties/src/weights.rs b/frame/child-bounties/src/weights.rs index e27393de9..eeb144196 100644 --- a/frame/child-bounties/src/weights.rs +++ b/frame/child-bounties/src/weights.rs @@ -81,7 +81,7 @@ impl WeightInfo for SubstrateWeight { // Minimum execution time: 46_743 nanoseconds. Weight::from_parts(47_762_924, 10848) // Standard Error: 135 - .saturating_add(Weight::from_ref_time(599).saturating_mul(d.into())) + .saturating_add(Weight::from_parts(599, 0).saturating_mul(d.into())) .saturating_add(T::DbWeight::get().reads(5_u64)) .saturating_add(T::DbWeight::get().writes(6_u64)) } @@ -226,7 +226,7 @@ impl WeightInfo for () { // Minimum execution time: 46_743 nanoseconds. Weight::from_parts(47_762_924, 10848) // Standard Error: 135 - .saturating_add(Weight::from_ref_time(599).saturating_mul(d.into())) + .saturating_add(Weight::from_parts(599, 0).saturating_mul(d.into())) .saturating_add(RocksDbWeight::get().reads(5_u64)) .saturating_add(RocksDbWeight::get().writes(6_u64)) } diff --git a/frame/collective/src/tests.rs b/frame/collective/src/tests.rs index bfc6a0249..2d9b31a53 100644 --- a/frame/collective/src/tests.rs +++ b/frame/collective/src/tests.rs @@ -288,7 +288,7 @@ fn proposal_weight_limit_works_on_approve() { RuntimeOrigin::signed(4), hash, 0, - proposal_weight - Weight::from_ref_time(100), + proposal_weight - Weight::from_parts(100, 0), proposal_len ), Error::::WrongProposalWeight @@ -327,7 +327,7 @@ fn proposal_weight_limit_ignored_on_disapprove() { RuntimeOrigin::signed(4), hash, 0, - proposal_weight - Weight::from_ref_time(100), + proposal_weight - Weight::from_parts(100, 0), proposal_len )); }) @@ -750,7 +750,7 @@ fn correct_validate_and_get_proposal() { Collective::validate_and_get_proposal( &hash, length, - weight - Weight::from_ref_time(10) + weight - Weight::from_parts(10, 0) ), Error::::WrongProposalWeight ); diff --git a/frame/collective/src/weights.rs b/frame/collective/src/weights.rs index d8a713118..df233fc24 100644 --- a/frame/collective/src/weights.rs +++ b/frame/collective/src/weights.rs @@ -81,15 +81,15 @@ impl WeightInfo for SubstrateWeight { // Minimum execution time: 17_093 nanoseconds. Weight::from_parts(17_284_000, 16586) // Standard Error: 64_700 - .saturating_add(Weight::from_ref_time(5_143_145).saturating_mul(m.into())) + .saturating_add(Weight::from_parts(5_143_145, 0).saturating_mul(m.into())) // Standard Error: 64_700 - .saturating_add(Weight::from_ref_time(7_480_941).saturating_mul(p.into())) + .saturating_add(Weight::from_parts(7_480_941, 0).saturating_mul(p.into())) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().reads((1_u64).saturating_mul(p.into()))) .saturating_add(T::DbWeight::get().writes(2_u64)) .saturating_add(T::DbWeight::get().writes((1_u64).saturating_mul(p.into()))) - .saturating_add(Weight::from_proof_size(7809).saturating_mul(m.into())) - .saturating_add(Weight::from_proof_size(10238).saturating_mul(p.into())) + .saturating_add(Weight::from_parts(0, 7809).saturating_mul(m.into())) + .saturating_add(Weight::from_parts(0, 10238).saturating_mul(p.into())) } /// Storage: Council Members (r:1 w:0) /// Proof Skipped: Council Members (max_values: Some(1), max_size: None, mode: Measured) @@ -102,11 +102,11 @@ impl WeightInfo for SubstrateWeight { // Minimum execution time: 15_972 nanoseconds. Weight::from_parts(14_971_445, 730) // Standard Error: 32 - .saturating_add(Weight::from_ref_time(1_775).saturating_mul(b.into())) + .saturating_add(Weight::from_parts(1_775, 0).saturating_mul(b.into())) // Standard Error: 334 - .saturating_add(Weight::from_ref_time(17_052).saturating_mul(m.into())) + .saturating_add(Weight::from_parts(17_052, 0).saturating_mul(m.into())) .saturating_add(T::DbWeight::get().reads(1_u64)) - .saturating_add(Weight::from_proof_size(32).saturating_mul(m.into())) + .saturating_add(Weight::from_parts(0, 32).saturating_mul(m.into())) } /// Storage: Council Members (r:1 w:0) /// Proof Skipped: Council Members (max_values: Some(1), max_size: None, mode: Measured) @@ -121,11 +121,11 @@ impl WeightInfo for SubstrateWeight { // Minimum execution time: 17_950 nanoseconds. Weight::from_parts(17_019_558, 3440) // Standard Error: 41 - .saturating_add(Weight::from_ref_time(1_807).saturating_mul(b.into())) + .saturating_add(Weight::from_parts(1_807, 0).saturating_mul(b.into())) // Standard Error: 432 - .saturating_add(Weight::from_ref_time(27_986).saturating_mul(m.into())) + .saturating_add(Weight::from_parts(27_986, 0).saturating_mul(m.into())) .saturating_add(T::DbWeight::get().reads(2_u64)) - .saturating_add(Weight::from_proof_size(64).saturating_mul(m.into())) + .saturating_add(Weight::from_parts(0, 64).saturating_mul(m.into())) } /// Storage: Council Members (r:1 w:0) /// Proof Skipped: Council Members (max_values: Some(1), max_size: None, mode: Measured) @@ -147,15 +147,15 @@ impl WeightInfo for SubstrateWeight { // Minimum execution time: 24_817 nanoseconds. Weight::from_parts(24_778_955, 6355) // Standard Error: 73 - .saturating_add(Weight::from_ref_time(2_355).saturating_mul(b.into())) + .saturating_add(Weight::from_parts(2_355, 0).saturating_mul(b.into())) // Standard Error: 765 - .saturating_add(Weight::from_ref_time(20_518).saturating_mul(m.into())) + .saturating_add(Weight::from_parts(20_518, 0).saturating_mul(m.into())) // Standard Error: 755 - .saturating_add(Weight::from_ref_time(85_670).saturating_mul(p.into())) + .saturating_add(Weight::from_parts(85_670, 0).saturating_mul(p.into())) .saturating_add(T::DbWeight::get().reads(4_u64)) .saturating_add(T::DbWeight::get().writes(4_u64)) - .saturating_add(Weight::from_proof_size(165).saturating_mul(m.into())) - .saturating_add(Weight::from_proof_size(180).saturating_mul(p.into())) + .saturating_add(Weight::from_parts(0, 165).saturating_mul(m.into())) + .saturating_add(Weight::from_parts(0, 180).saturating_mul(p.into())) } /// Storage: Council Members (r:1 w:0) /// Proof Skipped: Council Members (max_values: Some(1), max_size: None, mode: Measured) @@ -169,10 +169,10 @@ impl WeightInfo for SubstrateWeight { // Minimum execution time: 19_790 nanoseconds. Weight::from_parts(20_528_275, 4980) // Standard Error: 651 - .saturating_add(Weight::from_ref_time(48_856).saturating_mul(m.into())) + .saturating_add(Weight::from_parts(48_856, 0).saturating_mul(m.into())) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) - .saturating_add(Weight::from_proof_size(128).saturating_mul(m.into())) + .saturating_add(Weight::from_parts(0, 128).saturating_mul(m.into())) } /// Storage: Council Voting (r:1 w:1) /// Proof Skipped: Council Voting (max_values: None, max_size: None, mode: Measured) @@ -191,13 +191,13 @@ impl WeightInfo for SubstrateWeight { // Minimum execution time: 25_564 nanoseconds. Weight::from_parts(25_535_497, 5893) // Standard Error: 610 - .saturating_add(Weight::from_ref_time(27_956).saturating_mul(m.into())) + .saturating_add(Weight::from_parts(27_956, 0).saturating_mul(m.into())) // Standard Error: 595 - .saturating_add(Weight::from_ref_time(84_835).saturating_mul(p.into())) + .saturating_add(Weight::from_parts(84_835, 0).saturating_mul(p.into())) .saturating_add(T::DbWeight::get().reads(3_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) - .saturating_add(Weight::from_proof_size(260).saturating_mul(m.into())) - .saturating_add(Weight::from_proof_size(144).saturating_mul(p.into())) + .saturating_add(Weight::from_parts(0, 260).saturating_mul(m.into())) + .saturating_add(Weight::from_parts(0, 144).saturating_mul(p.into())) } /// Storage: Council Voting (r:1 w:1) /// Proof Skipped: Council Voting (max_values: None, max_size: None, mode: Measured) @@ -217,16 +217,16 @@ impl WeightInfo for SubstrateWeight { // Minimum execution time: 36_515 nanoseconds. Weight::from_parts(36_626_648, 9164) // Standard Error: 98 - .saturating_add(Weight::from_ref_time(2_295).saturating_mul(b.into())) + .saturating_add(Weight::from_parts(2_295, 0).saturating_mul(b.into())) // Standard Error: 1_036 - .saturating_add(Weight::from_ref_time(22_182).saturating_mul(m.into())) + .saturating_add(Weight::from_parts(22_182, 0).saturating_mul(m.into())) // Standard Error: 1_010 - .saturating_add(Weight::from_ref_time(100_034).saturating_mul(p.into())) + .saturating_add(Weight::from_parts(100_034, 0).saturating_mul(p.into())) .saturating_add(T::DbWeight::get().reads(4_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) - .saturating_add(Weight::from_proof_size(4).saturating_mul(b.into())) - .saturating_add(Weight::from_proof_size(264).saturating_mul(m.into())) - .saturating_add(Weight::from_proof_size(160).saturating_mul(p.into())) + .saturating_add(Weight::from_parts(0, 4).saturating_mul(b.into())) + .saturating_add(Weight::from_parts(0, 264).saturating_mul(m.into())) + .saturating_add(Weight::from_parts(0, 160).saturating_mul(p.into())) } /// Storage: Council Voting (r:1 w:1) /// Proof Skipped: Council Voting (max_values: None, max_size: None, mode: Measured) @@ -247,13 +247,13 @@ impl WeightInfo for SubstrateWeight { // Minimum execution time: 28_858 nanoseconds. Weight::from_parts(28_050_047, 7095) // Standard Error: 614 - .saturating_add(Weight::from_ref_time(34_031).saturating_mul(m.into())) + .saturating_add(Weight::from_parts(34_031, 0).saturating_mul(m.into())) // Standard Error: 599 - .saturating_add(Weight::from_ref_time(85_744).saturating_mul(p.into())) + .saturating_add(Weight::from_parts(85_744, 0).saturating_mul(p.into())) .saturating_add(T::DbWeight::get().reads(4_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) - .saturating_add(Weight::from_proof_size(325).saturating_mul(m.into())) - .saturating_add(Weight::from_proof_size(180).saturating_mul(p.into())) + .saturating_add(Weight::from_parts(0, 325).saturating_mul(m.into())) + .saturating_add(Weight::from_parts(0, 180).saturating_mul(p.into())) } /// Storage: Council Voting (r:1 w:1) /// Proof Skipped: Council Voting (max_values: None, max_size: None, mode: Measured) @@ -275,16 +275,16 @@ impl WeightInfo for SubstrateWeight { // Minimum execution time: 38_608 nanoseconds. Weight::from_parts(39_948_329, 10565) // Standard Error: 84 - .saturating_add(Weight::from_ref_time(2_045).saturating_mul(b.into())) + .saturating_add(Weight::from_parts(2_045, 0).saturating_mul(b.into())) // Standard Error: 895 - .saturating_add(Weight::from_ref_time(22_669).saturating_mul(m.into())) + .saturating_add(Weight::from_parts(22_669, 0).saturating_mul(m.into())) // Standard Error: 872 - .saturating_add(Weight::from_ref_time(95_525).saturating_mul(p.into())) + .saturating_add(Weight::from_parts(95_525, 0).saturating_mul(p.into())) .saturating_add(T::DbWeight::get().reads(5_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) - .saturating_add(Weight::from_proof_size(5).saturating_mul(b.into())) - .saturating_add(Weight::from_proof_size(330).saturating_mul(m.into())) - .saturating_add(Weight::from_proof_size(200).saturating_mul(p.into())) + .saturating_add(Weight::from_parts(0, 5).saturating_mul(b.into())) + .saturating_add(Weight::from_parts(0, 330).saturating_mul(m.into())) + .saturating_add(Weight::from_parts(0, 200).saturating_mul(p.into())) } /// Storage: Council Proposals (r:1 w:1) /// Proof Skipped: Council Proposals (max_values: Some(1), max_size: None, mode: Measured) @@ -300,10 +300,10 @@ impl WeightInfo for SubstrateWeight { // Minimum execution time: 14_785 nanoseconds. Weight::from_parts(16_393_818, 1668) // Standard Error: 612 - .saturating_add(Weight::from_ref_time(76_786).saturating_mul(p.into())) + .saturating_add(Weight::from_parts(76_786, 0).saturating_mul(p.into())) .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) - .saturating_add(Weight::from_proof_size(96).saturating_mul(p.into())) + .saturating_add(Weight::from_parts(0, 96).saturating_mul(p.into())) } } @@ -327,15 +327,15 @@ impl WeightInfo for () { // Minimum execution time: 17_093 nanoseconds. Weight::from_parts(17_284_000, 16586) // Standard Error: 64_700 - .saturating_add(Weight::from_ref_time(5_143_145).saturating_mul(m.into())) + .saturating_add(Weight::from_parts(5_143_145, 0).saturating_mul(m.into())) // Standard Error: 64_700 - .saturating_add(Weight::from_ref_time(7_480_941).saturating_mul(p.into())) + .saturating_add(Weight::from_parts(7_480_941, 0).saturating_mul(p.into())) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().reads((1_u64).saturating_mul(p.into()))) .saturating_add(RocksDbWeight::get().writes(2_u64)) .saturating_add(RocksDbWeight::get().writes((1_u64).saturating_mul(p.into()))) - .saturating_add(Weight::from_proof_size(7809).saturating_mul(m.into())) - .saturating_add(Weight::from_proof_size(10238).saturating_mul(p.into())) + .saturating_add(Weight::from_parts(0, 7809).saturating_mul(m.into())) + .saturating_add(Weight::from_parts(0, 10238).saturating_mul(p.into())) } /// Storage: Council Members (r:1 w:0) /// Proof Skipped: Council Members (max_values: Some(1), max_size: None, mode: Measured) @@ -348,11 +348,11 @@ impl WeightInfo for () { // Minimum execution time: 15_972 nanoseconds. Weight::from_parts(14_971_445, 730) // Standard Error: 32 - .saturating_add(Weight::from_ref_time(1_775).saturating_mul(b.into())) + .saturating_add(Weight::from_parts(1_775, 0).saturating_mul(b.into())) // Standard Error: 334 - .saturating_add(Weight::from_ref_time(17_052).saturating_mul(m.into())) + .saturating_add(Weight::from_parts(17_052, 0).saturating_mul(m.into())) .saturating_add(RocksDbWeight::get().reads(1_u64)) - .saturating_add(Weight::from_proof_size(32).saturating_mul(m.into())) + .saturating_add(Weight::from_parts(0, 32).saturating_mul(m.into())) } /// Storage: Council Members (r:1 w:0) /// Proof Skipped: Council Members (max_values: Some(1), max_size: None, mode: Measured) @@ -367,11 +367,11 @@ impl WeightInfo for () { // Minimum execution time: 17_950 nanoseconds. Weight::from_parts(17_019_558, 3440) // Standard Error: 41 - .saturating_add(Weight::from_ref_time(1_807).saturating_mul(b.into())) + .saturating_add(Weight::from_parts(1_807, 0).saturating_mul(b.into())) // Standard Error: 432 - .saturating_add(Weight::from_ref_time(27_986).saturating_mul(m.into())) + .saturating_add(Weight::from_parts(27_986, 0).saturating_mul(m.into())) .saturating_add(RocksDbWeight::get().reads(2_u64)) - .saturating_add(Weight::from_proof_size(64).saturating_mul(m.into())) + .saturating_add(Weight::from_parts(0, 64).saturating_mul(m.into())) } /// Storage: Council Members (r:1 w:0) /// Proof Skipped: Council Members (max_values: Some(1), max_size: None, mode: Measured) @@ -393,15 +393,15 @@ impl WeightInfo for () { // Minimum execution time: 24_817 nanoseconds. Weight::from_parts(24_778_955, 6355) // Standard Error: 73 - .saturating_add(Weight::from_ref_time(2_355).saturating_mul(b.into())) + .saturating_add(Weight::from_parts(2_355, 0).saturating_mul(b.into())) // Standard Error: 765 - .saturating_add(Weight::from_ref_time(20_518).saturating_mul(m.into())) + .saturating_add(Weight::from_parts(20_518, 0).saturating_mul(m.into())) // Standard Error: 755 - .saturating_add(Weight::from_ref_time(85_670).saturating_mul(p.into())) + .saturating_add(Weight::from_parts(85_670, 0).saturating_mul(p.into())) .saturating_add(RocksDbWeight::get().reads(4_u64)) .saturating_add(RocksDbWeight::get().writes(4_u64)) - .saturating_add(Weight::from_proof_size(165).saturating_mul(m.into())) - .saturating_add(Weight::from_proof_size(180).saturating_mul(p.into())) + .saturating_add(Weight::from_parts(0, 165).saturating_mul(m.into())) + .saturating_add(Weight::from_parts(0, 180).saturating_mul(p.into())) } /// Storage: Council Members (r:1 w:0) /// Proof Skipped: Council Members (max_values: Some(1), max_size: None, mode: Measured) @@ -415,10 +415,10 @@ impl WeightInfo for () { // Minimum execution time: 19_790 nanoseconds. Weight::from_parts(20_528_275, 4980) // Standard Error: 651 - .saturating_add(Weight::from_ref_time(48_856).saturating_mul(m.into())) + .saturating_add(Weight::from_parts(48_856, 0).saturating_mul(m.into())) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) - .saturating_add(Weight::from_proof_size(128).saturating_mul(m.into())) + .saturating_add(Weight::from_parts(0, 128).saturating_mul(m.into())) } /// Storage: Council Voting (r:1 w:1) /// Proof Skipped: Council Voting (max_values: None, max_size: None, mode: Measured) @@ -437,13 +437,13 @@ impl WeightInfo for () { // Minimum execution time: 25_564 nanoseconds. Weight::from_parts(25_535_497, 5893) // Standard Error: 610 - .saturating_add(Weight::from_ref_time(27_956).saturating_mul(m.into())) + .saturating_add(Weight::from_parts(27_956, 0).saturating_mul(m.into())) // Standard Error: 595 - .saturating_add(Weight::from_ref_time(84_835).saturating_mul(p.into())) + .saturating_add(Weight::from_parts(84_835, 0).saturating_mul(p.into())) .saturating_add(RocksDbWeight::get().reads(3_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) - .saturating_add(Weight::from_proof_size(260).saturating_mul(m.into())) - .saturating_add(Weight::from_proof_size(144).saturating_mul(p.into())) + .saturating_add(Weight::from_parts(0, 260).saturating_mul(m.into())) + .saturating_add(Weight::from_parts(0, 144).saturating_mul(p.into())) } /// Storage: Council Voting (r:1 w:1) /// Proof Skipped: Council Voting (max_values: None, max_size: None, mode: Measured) @@ -463,16 +463,16 @@ impl WeightInfo for () { // Minimum execution time: 36_515 nanoseconds. Weight::from_parts(36_626_648, 9164) // Standard Error: 98 - .saturating_add(Weight::from_ref_time(2_295).saturating_mul(b.into())) + .saturating_add(Weight::from_parts(2_295, 0).saturating_mul(b.into())) // Standard Error: 1_036 - .saturating_add(Weight::from_ref_time(22_182).saturating_mul(m.into())) + .saturating_add(Weight::from_parts(22_182, 0).saturating_mul(m.into())) // Standard Error: 1_010 - .saturating_add(Weight::from_ref_time(100_034).saturating_mul(p.into())) + .saturating_add(Weight::from_parts(100_034, 0).saturating_mul(p.into())) .saturating_add(RocksDbWeight::get().reads(4_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) - .saturating_add(Weight::from_proof_size(4).saturating_mul(b.into())) - .saturating_add(Weight::from_proof_size(264).saturating_mul(m.into())) - .saturating_add(Weight::from_proof_size(160).saturating_mul(p.into())) + .saturating_add(Weight::from_parts(0, 4).saturating_mul(b.into())) + .saturating_add(Weight::from_parts(0, 264).saturating_mul(m.into())) + .saturating_add(Weight::from_parts(0, 160).saturating_mul(p.into())) } /// Storage: Council Voting (r:1 w:1) /// Proof Skipped: Council Voting (max_values: None, max_size: None, mode: Measured) @@ -493,13 +493,13 @@ impl WeightInfo for () { // Minimum execution time: 28_858 nanoseconds. Weight::from_parts(28_050_047, 7095) // Standard Error: 614 - .saturating_add(Weight::from_ref_time(34_031).saturating_mul(m.into())) + .saturating_add(Weight::from_parts(34_031, 0).saturating_mul(m.into())) // Standard Error: 599 - .saturating_add(Weight::from_ref_time(85_744).saturating_mul(p.into())) + .saturating_add(Weight::from_parts(85_744, 0).saturating_mul(p.into())) .saturating_add(RocksDbWeight::get().reads(4_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) - .saturating_add(Weight::from_proof_size(325).saturating_mul(m.into())) - .saturating_add(Weight::from_proof_size(180).saturating_mul(p.into())) + .saturating_add(Weight::from_parts(0, 325).saturating_mul(m.into())) + .saturating_add(Weight::from_parts(0, 180).saturating_mul(p.into())) } /// Storage: Council Voting (r:1 w:1) /// Proof Skipped: Council Voting (max_values: None, max_size: None, mode: Measured) @@ -521,16 +521,16 @@ impl WeightInfo for () { // Minimum execution time: 38_608 nanoseconds. Weight::from_parts(39_948_329, 10565) // Standard Error: 84 - .saturating_add(Weight::from_ref_time(2_045).saturating_mul(b.into())) + .saturating_add(Weight::from_parts(2_045, 0).saturating_mul(b.into())) // Standard Error: 895 - .saturating_add(Weight::from_ref_time(22_669).saturating_mul(m.into())) + .saturating_add(Weight::from_parts(22_669, 0).saturating_mul(m.into())) // Standard Error: 872 - .saturating_add(Weight::from_ref_time(95_525).saturating_mul(p.into())) + .saturating_add(Weight::from_parts(95_525, 0).saturating_mul(p.into())) .saturating_add(RocksDbWeight::get().reads(5_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) - .saturating_add(Weight::from_proof_size(5).saturating_mul(b.into())) - .saturating_add(Weight::from_proof_size(330).saturating_mul(m.into())) - .saturating_add(Weight::from_proof_size(200).saturating_mul(p.into())) + .saturating_add(Weight::from_parts(0, 5).saturating_mul(b.into())) + .saturating_add(Weight::from_parts(0, 330).saturating_mul(m.into())) + .saturating_add(Weight::from_parts(0, 200).saturating_mul(p.into())) } /// Storage: Council Proposals (r:1 w:1) /// Proof Skipped: Council Proposals (max_values: Some(1), max_size: None, mode: Measured) @@ -546,9 +546,9 @@ impl WeightInfo for () { // Minimum execution time: 14_785 nanoseconds. Weight::from_parts(16_393_818, 1668) // Standard Error: 612 - .saturating_add(Weight::from_ref_time(76_786).saturating_mul(p.into())) + .saturating_add(Weight::from_parts(76_786, 0).saturating_mul(p.into())) .saturating_add(RocksDbWeight::get().reads(1_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) - .saturating_add(Weight::from_proof_size(96).saturating_mul(p.into())) + .saturating_add(Weight::from_parts(0, 96).saturating_mul(p.into())) } } diff --git a/frame/contracts/src/gas.rs b/frame/contracts/src/gas.rs index c6f7d44be..f6484fbcf 100644 --- a/frame/contracts/src/gas.rs +++ b/frame/contracts/src/gas.rs @@ -275,19 +275,19 @@ mod tests { struct SimpleToken(u64); impl Token for SimpleToken { fn weight(&self) -> Weight { - Weight::from_ref_time(self.0) + Weight::from_parts(self.0, 0) } } #[test] fn it_works() { - let gas_meter = GasMeter::::new(Weight::from_ref_time(50000)); - assert_eq!(gas_meter.gas_left(), Weight::from_ref_time(50000)); + let gas_meter = GasMeter::::new(Weight::from_parts(50000, 0)); + assert_eq!(gas_meter.gas_left(), Weight::from_parts(50000, 0)); } #[test] fn tracing() { - let mut gas_meter = GasMeter::::new(Weight::from_ref_time(50000)); + let mut gas_meter = GasMeter::::new(Weight::from_parts(50000, 0)); assert!(!gas_meter.charge(SimpleToken(1)).is_err()); let mut tokens = gas_meter.tokens().iter(); @@ -304,7 +304,7 @@ mod tests { // Make sure that the gas meter does not charge in case of overcharger #[test] fn overcharge_does_not_charge() { - let mut gas_meter = GasMeter::::new(Weight::from_ref_time(200)); + let mut gas_meter = GasMeter::::new(Weight::from_parts(200, 0)); // The first charge is should lead to OOG. assert!(gas_meter.charge(SimpleToken(300)).is_err()); @@ -317,7 +317,7 @@ mod tests { // possible. #[test] fn charge_exact_amount() { - let mut gas_meter = GasMeter::::new(Weight::from_ref_time(25)); + let mut gas_meter = GasMeter::::new(Weight::from_parts(25, 0)); assert!(!gas_meter.charge(SimpleToken(25)).is_err()); } } diff --git a/frame/contracts/src/tests.rs b/frame/contracts/src/tests.rs index bf1bd4f65..cecac0338 100644 --- a/frame/contracts/src/tests.rs +++ b/frame/contracts/src/tests.rs @@ -190,7 +190,7 @@ impl ChainExtension for TestExtension { }, 2 => { let mut env = env.buf_in_buf_out(); - let weight = Weight::from_ref_time(env.read(5)?[4].into()); + let weight = Weight::from_parts(env.read(5)?[4].into(), 0); env.charge_weight(weight)?; Ok(RetVal::Converging(id)) }, @@ -705,7 +705,7 @@ fn run_out_of_gas() { RuntimeOrigin::signed(ALICE), addr, // newly created account 0, - Weight::from_ref_time(1_000_000_000_000).set_proof_size(u64::MAX), + Weight::from_parts(1_000_000_000_000, u64::MAX), None, vec![], ), @@ -2115,7 +2115,7 @@ fn lazy_removal_partial_remove_works() { // We create a contract with some extra keys above the weight limit let extra_keys = 7u32; - let weight_limit = Weight::from_ref_time(5_000_000_000); + let weight_limit = Weight::from_parts(5_000_000_000, 0); let (_, max_keys) = ContractInfo::::deletion_budget(1, weight_limit); let vals: Vec<_> = (0..max_keys + extra_keys) .map(|i| (blake2_256(&i.encode()), (i as u32), (i as u32).encode())) diff --git a/frame/contracts/src/wasm/runtime.rs b/frame/contracts/src/wasm/runtime.rs index ab873331c..e80e2cde3 100644 --- a/frame/contracts/src/wasm/runtime.rs +++ b/frame/contracts/src/wasm/runtime.rs @@ -282,7 +282,7 @@ impl RuntimeCosts { fn token(&self, s: &HostFnWeights) -> RuntimeToken { use self::RuntimeCosts::*; let weight = match *self { - MeteringBlock(amount) => s.gas.saturating_add(Weight::from_ref_time(amount)), + MeteringBlock(amount) => s.gas.saturating_add(Weight::from_parts(amount, 0)), CopyFromContract(len) => s.return_per_byte.saturating_mul(len.into()), CopyToContract(len) => s.input_per_byte.saturating_mul(len.into()), Caller => s.caller, @@ -903,7 +903,7 @@ impl<'a, E: Ext + 'a> Runtime<'a, E> { self.charge_gas(RuntimeCosts::CallSurchargeTransfer)?; } self.ext.call( - Weight::from_ref_time(gas), + Weight::from_parts(gas, 0), callee, value, input_data, @@ -958,7 +958,7 @@ impl<'a, E: Ext + 'a> Runtime<'a, E> { salt_ptr: u32, salt_len: u32, ) -> Result { - let gas = Weight::from_ref_time(gas); + let gas = Weight::from_parts(gas, 0); self.charge_gas(RuntimeCosts::InstantiateBase { input_data_len, salt_len })?; let value: BalanceOf<::T> = self.read_sandbox_memory_as(memory, value_ptr)?; if value > 0u32.into() { @@ -1802,7 +1802,7 @@ pub mod env { out_ptr: u32, out_len_ptr: u32, ) -> Result<(), TrapReason> { - let gas = Weight::from_ref_time(gas); + let gas = Weight::from_parts(gas, 0); ctx.charge_gas(RuntimeCosts::WeightToFee)?; Ok(ctx.write_sandbox_output( memory, diff --git a/frame/contracts/src/weights.rs b/frame/contracts/src/weights.rs index 880b7305f..14e680fb6 100644 --- a/frame/contracts/src/weights.rs +++ b/frame/contracts/src/weights.rs @@ -179,8 +179,8 @@ impl WeightInfo for SubstrateWeight { // Measured: `109` // Estimated: `604` // Minimum execution time: 2_591 nanoseconds. - Weight::from_ref_time(2_817_000) - .saturating_add(Weight::from_proof_size(604)) + Weight::from_parts(2_817_000, 0) + .saturating_add(Weight::from_parts(0, 604)) .saturating_add(T::DbWeight::get().reads(1_u64)) } /// Storage: Skipped Metadata (r:0 w:0) @@ -191,14 +191,14 @@ impl WeightInfo for SubstrateWeight { // Measured: `481 + k * (69 ±0)` // Estimated: `471 + k * (70 ±0)` // Minimum execution time: 10_190 nanoseconds. - Weight::from_ref_time(6_642_117) - .saturating_add(Weight::from_proof_size(471)) + Weight::from_parts(6_642_117, 0) + .saturating_add(Weight::from_parts(0, 471)) // Standard Error: 992 - .saturating_add(Weight::from_ref_time(919_828).saturating_mul(k.into())) + .saturating_add(Weight::from_parts(919_828, 0).saturating_mul(k.into())) .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) .saturating_add(T::DbWeight::get().writes((1_u64).saturating_mul(k.into()))) - .saturating_add(Weight::from_proof_size(70).saturating_mul(k.into())) + .saturating_add(Weight::from_parts(0, 70).saturating_mul(k.into())) } /// Storage: Contracts DeletionQueue (r:1 w:1) /// Proof: Contracts DeletionQueue (max_values: Some(1), max_size: Some(16642), added: 17137, mode: Measured) @@ -208,13 +208,13 @@ impl WeightInfo for SubstrateWeight { // Measured: `281 + q * (33 ±0)` // Estimated: `763 + q * (33 ±0)` // Minimum execution time: 2_598 nanoseconds. - Weight::from_ref_time(10_288_252) - .saturating_add(Weight::from_proof_size(763)) + Weight::from_parts(10_288_252, 0) + .saturating_add(Weight::from_parts(0, 763)) // Standard Error: 2_886 - .saturating_add(Weight::from_ref_time(1_092_420).saturating_mul(q.into())) + .saturating_add(Weight::from_parts(1_092_420, 0).saturating_mul(q.into())) .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) - .saturating_add(Weight::from_proof_size(33).saturating_mul(q.into())) + .saturating_add(Weight::from_parts(0, 33).saturating_mul(q.into())) } /// Storage: Contracts PristineCode (r:1 w:0) /// Proof: Contracts PristineCode (max_values: None, max_size: Some(125988), added: 128463, mode: Measured) @@ -226,13 +226,13 @@ impl WeightInfo for SubstrateWeight { // Measured: `270 + c * (1 ±0)` // Estimated: `3025 + c * (2 ±0)` // Minimum execution time: 34_338 nanoseconds. - Weight::from_ref_time(32_159_677) - .saturating_add(Weight::from_proof_size(3025)) + Weight::from_parts(32_159_677, 0) + .saturating_add(Weight::from_parts(0, 3025)) // Standard Error: 53 - .saturating_add(Weight::from_ref_time(51_034).saturating_mul(c.into())) + .saturating_add(Weight::from_parts(51_034, 0).saturating_mul(c.into())) .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) - .saturating_add(Weight::from_proof_size(2).saturating_mul(c.into())) + .saturating_add(Weight::from_parts(0, 2).saturating_mul(c.into())) } /// Storage: Contracts ContractInfoOf (r:1 w:1) /// Proof: Contracts ContractInfoOf (max_values: None, max_size: Some(290), added: 2765, mode: Measured) @@ -250,13 +250,13 @@ impl WeightInfo for SubstrateWeight { // Measured: `803` // Estimated: `16930 + c * (5 ±0)` // Minimum execution time: 385_587 nanoseconds. - Weight::from_ref_time(395_545_811) - .saturating_add(Weight::from_proof_size(16930)) + Weight::from_parts(395_545_811, 0) + .saturating_add(Weight::from_parts(0, 16930)) // Standard Error: 27 - .saturating_add(Weight::from_ref_time(31_342).saturating_mul(c.into())) + .saturating_add(Weight::from_parts(31_342, 0).saturating_mul(c.into())) .saturating_add(T::DbWeight::get().reads(6_u64)) .saturating_add(T::DbWeight::get().writes(4_u64)) - .saturating_add(Weight::from_proof_size(5).saturating_mul(c.into())) + .saturating_add(Weight::from_parts(0, 5).saturating_mul(c.into())) } /// Storage: Contracts OwnerInfoOf (r:1 w:1) /// Proof: Contracts OwnerInfoOf (max_values: None, max_size: Some(88), added: 2563, mode: Measured) @@ -282,14 +282,14 @@ impl WeightInfo for SubstrateWeight { // Measured: `270` // Estimated: `20267` // Minimum execution time: 3_799_742 nanoseconds. - Weight::from_ref_time(670_115_588) - .saturating_add(Weight::from_proof_size(20267)) + Weight::from_parts(670_115_588, 0) + .saturating_add(Weight::from_parts(0, 20267)) // Standard Error: 287 - .saturating_add(Weight::from_ref_time(93_885).saturating_mul(c.into())) + .saturating_add(Weight::from_parts(93_885, 0).saturating_mul(c.into())) // Standard Error: 16 - .saturating_add(Weight::from_ref_time(1_367).saturating_mul(i.into())) + .saturating_add(Weight::from_parts(1_367, 0).saturating_mul(i.into())) // Standard Error: 16 - .saturating_add(Weight::from_ref_time(1_781).saturating_mul(s.into())) + .saturating_add(Weight::from_parts(1_781, 0).saturating_mul(s.into())) .saturating_add(T::DbWeight::get().reads(9_u64)) .saturating_add(T::DbWeight::get().writes(10_u64)) } @@ -314,12 +314,12 @@ impl WeightInfo for SubstrateWeight { // Measured: `546` // Estimated: `22039` // Minimum execution time: 1_949_008 nanoseconds. - Weight::from_ref_time(214_033_418) - .saturating_add(Weight::from_proof_size(22039)) + Weight::from_parts(214_033_418, 0) + .saturating_add(Weight::from_parts(0, 22039)) // Standard Error: 8 - .saturating_add(Weight::from_ref_time(1_666).saturating_mul(i.into())) + .saturating_add(Weight::from_parts(1_666, 0).saturating_mul(i.into())) // Standard Error: 8 - .saturating_add(Weight::from_ref_time(1_801).saturating_mul(s.into())) + .saturating_add(Weight::from_parts(1_801, 0).saturating_mul(s.into())) .saturating_add(T::DbWeight::get().reads(9_u64)) .saturating_add(T::DbWeight::get().writes(7_u64)) } @@ -338,8 +338,8 @@ impl WeightInfo for SubstrateWeight { // Measured: `855` // Estimated: `17145` // Minimum execution time: 146_654 nanoseconds. - Weight::from_ref_time(147_528_000) - .saturating_add(Weight::from_proof_size(17145)) + Weight::from_parts(147_528_000, 0) + .saturating_add(Weight::from_parts(0, 17145)) .saturating_add(T::DbWeight::get().reads(6_u64)) .saturating_add(T::DbWeight::get().writes(4_u64)) } @@ -357,10 +357,10 @@ impl WeightInfo for SubstrateWeight { // Measured: `109` // Estimated: `5386` // Minimum execution time: 387_889 nanoseconds. - Weight::from_ref_time(391_379_335) - .saturating_add(Weight::from_proof_size(5386)) + Weight::from_parts(391_379_335, 0) + .saturating_add(Weight::from_parts(0, 5386)) // Standard Error: 89 - .saturating_add(Weight::from_ref_time(94_810).saturating_mul(c.into())) + .saturating_add(Weight::from_parts(94_810, 0).saturating_mul(c.into())) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().writes(4_u64)) } @@ -377,8 +377,8 @@ impl WeightInfo for SubstrateWeight { // Measured: `287` // Estimated: `6098` // Minimum execution time: 26_014 nanoseconds. - Weight::from_ref_time(26_510_000) - .saturating_add(Weight::from_proof_size(6098)) + Weight::from_parts(26_510_000, 0) + .saturating_add(Weight::from_parts(0, 6098)) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().writes(4_u64)) } @@ -393,8 +393,8 @@ impl WeightInfo for SubstrateWeight { // Measured: `666` // Estimated: `16848` // Minimum execution time: 30_177 nanoseconds. - Weight::from_ref_time(30_639_000) - .saturating_add(Weight::from_proof_size(16848)) + Weight::from_parts(30_639_000, 0) + .saturating_add(Weight::from_parts(0, 16848)) .saturating_add(T::DbWeight::get().reads(6_u64)) .saturating_add(T::DbWeight::get().writes(6_u64)) } @@ -414,13 +414,13 @@ impl WeightInfo for SubstrateWeight { // Measured: `877 + r * (480 ±0)` // Estimated: `17295 + r * (2400 ±0)` // Minimum execution time: 373_786 nanoseconds. - Weight::from_ref_time(377_332_691) - .saturating_add(Weight::from_proof_size(17295)) + Weight::from_parts(377_332_691, 0) + .saturating_add(Weight::from_parts(0, 17295)) // Standard Error: 51_211 - .saturating_add(Weight::from_ref_time(17_715_615).saturating_mul(r.into())) + .saturating_add(Weight::from_parts(17_715_615, 0).saturating_mul(r.into())) .saturating_add(T::DbWeight::get().reads(6_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) - .saturating_add(Weight::from_proof_size(2400).saturating_mul(r.into())) + .saturating_add(Weight::from_parts(0, 2400).saturating_mul(r.into())) } /// Storage: System Account (r:1 w:0) /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: Measured) @@ -438,14 +438,14 @@ impl WeightInfo for SubstrateWeight { // Measured: `917 + r * (21778 ±0)` // Estimated: `17295 + r * (306895 ±0)` // Minimum execution time: 374_009 nanoseconds. - Weight::from_ref_time(238_991_986) - .saturating_add(Weight::from_proof_size(17295)) + Weight::from_parts(238_991_986, 0) + .saturating_add(Weight::from_parts(0, 17295)) // Standard Error: 464_711 - .saturating_add(Weight::from_ref_time(249_099_538).saturating_mul(r.into())) + .saturating_add(Weight::from_parts(249_099_538, 0).saturating_mul(r.into())) .saturating_add(T::DbWeight::get().reads(6_u64)) .saturating_add(T::DbWeight::get().reads((80_u64).saturating_mul(r.into()))) .saturating_add(T::DbWeight::get().writes(3_u64)) - .saturating_add(Weight::from_proof_size(306895).saturating_mul(r.into())) + .saturating_add(Weight::from_parts(0, 306895).saturating_mul(r.into())) } /// Storage: System Account (r:1 w:0) /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: Measured) @@ -463,14 +463,14 @@ impl WeightInfo for SubstrateWeight { // Measured: `921 + r * (22099 ±0)` // Estimated: `17340 + r * (308500 ±0)` // Minimum execution time: 375_058 nanoseconds. - Weight::from_ref_time(238_765_068) - .saturating_add(Weight::from_proof_size(17340)) + Weight::from_parts(238_765_068, 0) + .saturating_add(Weight::from_parts(0, 17340)) // Standard Error: 662_617 - .saturating_add(Weight::from_ref_time(302_175_089).saturating_mul(r.into())) + .saturating_add(Weight::from_parts(302_175_089, 0).saturating_mul(r.into())) .saturating_add(T::DbWeight::get().reads(6_u64)) .saturating_add(T::DbWeight::get().reads((80_u64).saturating_mul(r.into()))) .saturating_add(T::DbWeight::get().writes(3_u64)) - .saturating_add(Weight::from_proof_size(308500).saturating_mul(r.into())) + .saturating_add(Weight::from_parts(0, 308500).saturating_mul(r.into())) } /// Storage: System Account (r:1 w:0) /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: Measured) @@ -488,13 +488,13 @@ impl WeightInfo for SubstrateWeight { // Measured: `884 + r * (480 ±0)` // Estimated: `17330 + r * (2400 ±0)` // Minimum execution time: 374_747 nanoseconds. - Weight::from_ref_time(376_482_380) - .saturating_add(Weight::from_proof_size(17330)) + Weight::from_parts(376_482_380, 0) + .saturating_add(Weight::from_parts(0, 17330)) // Standard Error: 61_919 - .saturating_add(Weight::from_ref_time(22_376_795).saturating_mul(r.into())) + .saturating_add(Weight::from_parts(22_376_795, 0).saturating_mul(r.into())) .saturating_add(T::DbWeight::get().reads(6_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) - .saturating_add(Weight::from_proof_size(2400).saturating_mul(r.into())) + .saturating_add(Weight::from_parts(0, 2400).saturating_mul(r.into())) } /// Storage: System Account (r:1 w:0) /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: Measured) @@ -512,13 +512,13 @@ impl WeightInfo for SubstrateWeight { // Measured: `874 + r * (240 ±0)` // Estimated: `17265 + r * (1200 ±0)` // Minimum execution time: 372_287 nanoseconds. - Weight::from_ref_time(376_250_858) - .saturating_add(Weight::from_proof_size(17265)) + Weight::from_parts(376_250_858, 0) + .saturating_add(Weight::from_parts(0, 17265)) // Standard Error: 40_119 - .saturating_add(Weight::from_ref_time(11_359_647).saturating_mul(r.into())) + .saturating_add(Weight::from_parts(11_359_647, 0).saturating_mul(r.into())) .saturating_add(T::DbWeight::get().reads(6_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) - .saturating_add(Weight::from_proof_size(1200).saturating_mul(r.into())) + .saturating_add(Weight::from_parts(0, 1200).saturating_mul(r.into())) } /// Storage: System Account (r:1 w:0) /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: Measured) @@ -536,13 +536,13 @@ impl WeightInfo for SubstrateWeight { // Measured: `878 + r * (480 ±0)` // Estimated: `17260 + r * (2400 ±0)` // Minimum execution time: 374_445 nanoseconds. - Weight::from_ref_time(377_243_521) - .saturating_add(Weight::from_proof_size(17260)) + Weight::from_parts(377_243_521, 0) + .saturating_add(Weight::from_parts(0, 17260)) // Standard Error: 53_032 - .saturating_add(Weight::from_ref_time(17_684_246).saturating_mul(r.into())) + .saturating_add(Weight::from_parts(17_684_246, 0).saturating_mul(r.into())) .saturating_add(T::DbWeight::get().reads(6_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) - .saturating_add(Weight::from_proof_size(2400).saturating_mul(r.into())) + .saturating_add(Weight::from_parts(0, 2400).saturating_mul(r.into())) } /// Storage: System Account (r:1 w:0) /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: Measured) @@ -560,13 +560,13 @@ impl WeightInfo for SubstrateWeight { // Measured: `879 + r * (480 ±0)` // Estimated: `17250 + r * (2405 ±0)` // Minimum execution time: 374_029 nanoseconds. - Weight::from_ref_time(380_415_186) - .saturating_add(Weight::from_proof_size(17250)) + Weight::from_parts(380_415_186, 0) + .saturating_add(Weight::from_parts(0, 17250)) // Standard Error: 60_562 - .saturating_add(Weight::from_ref_time(17_152_599).saturating_mul(r.into())) + .saturating_add(Weight::from_parts(17_152_599, 0).saturating_mul(r.into())) .saturating_add(T::DbWeight::get().reads(6_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) - .saturating_add(Weight::from_proof_size(2405).saturating_mul(r.into())) + .saturating_add(Weight::from_parts(0, 2405).saturating_mul(r.into())) } /// Storage: System Account (r:2 w:0) /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: Measured) @@ -584,13 +584,13 @@ impl WeightInfo for SubstrateWeight { // Measured: `1049 + r * (480 ±0)` // Estimated: `19849 + r * (2456 ±0)` // Minimum execution time: 373_999 nanoseconds. - Weight::from_ref_time(381_757_033) - .saturating_add(Weight::from_proof_size(19849)) + Weight::from_parts(381_757_033, 0) + .saturating_add(Weight::from_parts(0, 19849)) // Standard Error: 97_983 - .saturating_add(Weight::from_ref_time(98_290_984).saturating_mul(r.into())) + .saturating_add(Weight::from_parts(98_290_984, 0).saturating_mul(r.into())) .saturating_add(T::DbWeight::get().reads(7_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) - .saturating_add(Weight::from_proof_size(2456).saturating_mul(r.into())) + .saturating_add(Weight::from_parts(0, 2456).saturating_mul(r.into())) } /// Storage: System Account (r:1 w:0) /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: Measured) @@ -608,13 +608,13 @@ impl WeightInfo for SubstrateWeight { // Measured: `888 + r * (480 ±0)` // Estimated: `17360 + r * (2400 ±0)` // Minimum execution time: 374_197 nanoseconds. - Weight::from_ref_time(377_755_896) - .saturating_add(Weight::from_proof_size(17360)) + Weight::from_parts(377_755_896, 0) + .saturating_add(Weight::from_parts(0, 17360)) // Standard Error: 60_542 - .saturating_add(Weight::from_ref_time(17_442_065).saturating_mul(r.into())) + .saturating_add(Weight::from_parts(17_442_065, 0).saturating_mul(r.into())) .saturating_add(T::DbWeight::get().reads(6_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) - .saturating_add(Weight::from_proof_size(2400).saturating_mul(r.into())) + .saturating_add(Weight::from_parts(0, 2400).saturating_mul(r.into())) } /// Storage: System Account (r:1 w:0) /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: Measured) @@ -632,13 +632,13 @@ impl WeightInfo for SubstrateWeight { // Measured: `886 + r * (480 ±0)` // Estimated: `17290 + r * (2400 ±0)` // Minimum execution time: 373_888 nanoseconds. - Weight::from_ref_time(377_825_771) - .saturating_add(Weight::from_proof_size(17290)) + Weight::from_parts(377_825_771, 0) + .saturating_add(Weight::from_parts(0, 17290)) // Standard Error: 38_026 - .saturating_add(Weight::from_ref_time(17_147_903).saturating_mul(r.into())) + .saturating_add(Weight::from_parts(17_147_903, 0).saturating_mul(r.into())) .saturating_add(T::DbWeight::get().reads(6_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) - .saturating_add(Weight::from_proof_size(2400).saturating_mul(r.into())) + .saturating_add(Weight::from_parts(0, 2400).saturating_mul(r.into())) } /// Storage: System Account (r:1 w:0) /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: Measured) @@ -656,13 +656,13 @@ impl WeightInfo for SubstrateWeight { // Measured: `883 + r * (480 ±0)` // Estimated: `17315 + r * (2400 ±0)` // Minimum execution time: 373_904 nanoseconds. - Weight::from_ref_time(378_652_372) - .saturating_add(Weight::from_proof_size(17315)) + Weight::from_parts(378_652_372, 0) + .saturating_add(Weight::from_parts(0, 17315)) // Standard Error: 43_833 - .saturating_add(Weight::from_ref_time(16_936_781).saturating_mul(r.into())) + .saturating_add(Weight::from_parts(16_936_781, 0).saturating_mul(r.into())) .saturating_add(T::DbWeight::get().reads(6_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) - .saturating_add(Weight::from_proof_size(2400).saturating_mul(r.into())) + .saturating_add(Weight::from_parts(0, 2400).saturating_mul(r.into())) } /// Storage: System Account (r:1 w:0) /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: Measured) @@ -680,13 +680,13 @@ impl WeightInfo for SubstrateWeight { // Measured: `874 + r * (480 ±0)` // Estimated: `17245 + r * (2400 ±0)` // Minimum execution time: 373_473 nanoseconds. - Weight::from_ref_time(376_386_312) - .saturating_add(Weight::from_proof_size(17245)) + Weight::from_parts(376_386_312, 0) + .saturating_add(Weight::from_parts(0, 17245)) // Standard Error: 46_945 - .saturating_add(Weight::from_ref_time(17_336_462).saturating_mul(r.into())) + .saturating_add(Weight::from_parts(17_336_462, 0).saturating_mul(r.into())) .saturating_add(T::DbWeight::get().reads(6_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) - .saturating_add(Weight::from_proof_size(2400).saturating_mul(r.into())) + .saturating_add(Weight::from_parts(0, 2400).saturating_mul(r.into())) } /// Storage: System Account (r:1 w:0) /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: Measured) @@ -706,13 +706,13 @@ impl WeightInfo for SubstrateWeight { // Measured: `951 + r * (800 ±0)` // Estimated: `19046 + r * (4805 ±0)` // Minimum execution time: 373_661 nanoseconds. - Weight::from_ref_time(385_824_015) - .saturating_add(Weight::from_proof_size(19046)) + Weight::from_parts(385_824_015, 0) + .saturating_add(Weight::from_parts(0, 19046)) // Standard Error: 75_964 - .saturating_add(Weight::from_ref_time(88_530_074).saturating_mul(r.into())) + .saturating_add(Weight::from_parts(88_530_074, 0).saturating_mul(r.into())) .saturating_add(T::DbWeight::get().reads(7_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) - .saturating_add(Weight::from_proof_size(4805).saturating_mul(r.into())) + .saturating_add(Weight::from_parts(0, 4805).saturating_mul(r.into())) } /// Storage: System Account (r:1 w:0) /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: Measured) @@ -730,13 +730,13 @@ impl WeightInfo for SubstrateWeight { // Measured: `841 + r * (320 ±0)` // Estimated: `17120 + r * (1600 ±0)` // Minimum execution time: 133_849 nanoseconds. - Weight::from_ref_time(137_283_391) - .saturating_add(Weight::from_proof_size(17120)) + Weight::from_parts(137_283_391, 0) + .saturating_add(Weight::from_parts(0, 17120)) // Standard Error: 13_312 - .saturating_add(Weight::from_ref_time(8_055_328).saturating_mul(r.into())) + .saturating_add(Weight::from_parts(8_055_328, 0).saturating_mul(r.into())) .saturating_add(T::DbWeight::get().reads(6_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) - .saturating_add(Weight::from_proof_size(1600).saturating_mul(r.into())) + .saturating_add(Weight::from_parts(0, 1600).saturating_mul(r.into())) } /// Storage: System Account (r:1 w:0) /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: Measured) @@ -754,13 +754,13 @@ impl WeightInfo for SubstrateWeight { // Measured: `876 + r * (480 ±0)` // Estimated: `17245 + r * (2400 ±0)` // Minimum execution time: 373_468 nanoseconds. - Weight::from_ref_time(376_121_093) - .saturating_add(Weight::from_proof_size(17245)) + Weight::from_parts(376_121_093, 0) + .saturating_add(Weight::from_parts(0, 17245)) // Standard Error: 61_857 - .saturating_add(Weight::from_ref_time(15_868_414).saturating_mul(r.into())) + .saturating_add(Weight::from_parts(15_868_414, 0).saturating_mul(r.into())) .saturating_add(T::DbWeight::get().reads(6_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) - .saturating_add(Weight::from_proof_size(2400).saturating_mul(r.into())) + .saturating_add(Weight::from_parts(0, 2400).saturating_mul(r.into())) } /// Storage: System Account (r:1 w:0) /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: Measured) @@ -778,10 +778,10 @@ impl WeightInfo for SubstrateWeight { // Measured: `1356` // Estimated: `19650` // Minimum execution time: 390_668 nanoseconds. - Weight::from_ref_time(419_608_449) - .saturating_add(Weight::from_proof_size(19650)) + Weight::from_parts(419_608_449, 0) + .saturating_add(Weight::from_parts(0, 19650)) // Standard Error: 4_890 - .saturating_add(Weight::from_ref_time(9_672_288).saturating_mul(n.into())) + .saturating_add(Weight::from_parts(9_672_288, 0).saturating_mul(n.into())) .saturating_add(T::DbWeight::get().reads(6_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) } @@ -801,13 +801,13 @@ impl WeightInfo for SubstrateWeight { // Measured: `864 + r * (45 ±0)` // Estimated: `17190 + r * (225 ±0)` // Minimum execution time: 371_309 nanoseconds. - Weight::from_ref_time(373_625_402) - .saturating_add(Weight::from_proof_size(17190)) + Weight::from_parts(373_625_402, 0) + .saturating_add(Weight::from_parts(0, 17190)) // Standard Error: 419_605 - .saturating_add(Weight::from_ref_time(1_737_397).saturating_mul(r.into())) + .saturating_add(Weight::from_parts(1_737_397, 0).saturating_mul(r.into())) .saturating_add(T::DbWeight::get().reads(6_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) - .saturating_add(Weight::from_proof_size(225).saturating_mul(r.into())) + .saturating_add(Weight::from_parts(0, 225).saturating_mul(r.into())) } /// Storage: System Account (r:1 w:0) /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: Measured) @@ -825,10 +825,10 @@ impl WeightInfo for SubstrateWeight { // Measured: `874` // Estimated: `17285` // Minimum execution time: 374_094 nanoseconds. - Weight::from_ref_time(375_965_200) - .saturating_add(Weight::from_proof_size(17285)) + Weight::from_parts(375_965_200, 0) + .saturating_add(Weight::from_parts(0, 17285)) // Standard Error: 1_127 - .saturating_add(Weight::from_ref_time(232_645).saturating_mul(n.into())) + .saturating_add(Weight::from_parts(232_645, 0).saturating_mul(n.into())) .saturating_add(T::DbWeight::get().reads(6_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) } @@ -852,15 +852,15 @@ impl WeightInfo for SubstrateWeight { // Measured: `906 + r * (452 ±0)` // Estimated: `20242 + r * (15004 ±0)` // Minimum execution time: 373_123 nanoseconds. - Weight::from_ref_time(374_924_634) - .saturating_add(Weight::from_proof_size(20242)) + Weight::from_parts(374_924_634, 0) + .saturating_add(Weight::from_parts(0, 20242)) // Standard Error: 378_010 - .saturating_add(Weight::from_ref_time(70_441_665).saturating_mul(r.into())) + .saturating_add(Weight::from_parts(70_441_665, 0).saturating_mul(r.into())) .saturating_add(T::DbWeight::get().reads(6_u64)) .saturating_add(T::DbWeight::get().reads((6_u64).saturating_mul(r.into()))) .saturating_add(T::DbWeight::get().writes(3_u64)) .saturating_add(T::DbWeight::get().writes((7_u64).saturating_mul(r.into()))) - .saturating_add(Weight::from_proof_size(15004).saturating_mul(r.into())) + .saturating_add(Weight::from_parts(0, 15004).saturating_mul(r.into())) } /// Storage: System Account (r:1 w:0) /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: Measured) @@ -880,13 +880,13 @@ impl WeightInfo for SubstrateWeight { // Measured: `921 + r * (800 ±0)` // Estimated: `18835 + r * (4805 ±0)` // Minimum execution time: 373_291 nanoseconds. - Weight::from_ref_time(385_684_344) - .saturating_add(Weight::from_proof_size(18835)) + Weight::from_parts(385_684_344, 0) + .saturating_add(Weight::from_parts(0, 18835)) // Standard Error: 99_025 - .saturating_add(Weight::from_ref_time(111_308_793).saturating_mul(r.into())) + .saturating_add(Weight::from_parts(111_308_793, 0).saturating_mul(r.into())) .saturating_add(T::DbWeight::get().reads(7_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) - .saturating_add(Weight::from_proof_size(4805).saturating_mul(r.into())) + .saturating_add(Weight::from_parts(0, 4805).saturating_mul(r.into())) } /// Storage: System Account (r:1 w:0) /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: Measured) @@ -904,13 +904,13 @@ impl WeightInfo for SubstrateWeight { // Measured: `874 + r * (800 ±0)` // Estimated: `17250 + r * (4000 ±0)` // Minimum execution time: 371_900 nanoseconds. - Weight::from_ref_time(384_166_626) - .saturating_add(Weight::from_proof_size(17250)) + Weight::from_parts(384_166_626, 0) + .saturating_add(Weight::from_parts(0, 17250)) // Standard Error: 205_255 - .saturating_add(Weight::from_ref_time(229_214_157).saturating_mul(r.into())) + .saturating_add(Weight::from_parts(229_214_157, 0).saturating_mul(r.into())) .saturating_add(T::DbWeight::get().reads(6_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) - .saturating_add(Weight::from_proof_size(4000).saturating_mul(r.into())) + .saturating_add(Weight::from_parts(0, 4000).saturating_mul(r.into())) } /// Storage: System Account (r:1 w:0) /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: Measured) @@ -929,18 +929,18 @@ impl WeightInfo for SubstrateWeight { // Measured: `1821 + t * (2608 ±0) + n * (7 ±0)` // Estimated: `21870 + t * (211030 ±0) + n * (50 ±0)` // Minimum execution time: 1_289_873 nanoseconds. - Weight::from_ref_time(581_702_206) - .saturating_add(Weight::from_proof_size(21870)) + Weight::from_parts(581_702_206, 0) + .saturating_add(Weight::from_parts(0, 21870)) // Standard Error: 665_638 - .saturating_add(Weight::from_ref_time(181_470_553).saturating_mul(t.into())) + .saturating_add(Weight::from_parts(181_470_553, 0).saturating_mul(t.into())) // Standard Error: 182_816 - .saturating_add(Weight::from_ref_time(71_635_250).saturating_mul(n.into())) + .saturating_add(Weight::from_parts(71_635_250, 0).saturating_mul(n.into())) .saturating_add(T::DbWeight::get().reads(6_u64)) .saturating_add(T::DbWeight::get().reads((80_u64).saturating_mul(t.into()))) .saturating_add(T::DbWeight::get().writes(3_u64)) .saturating_add(T::DbWeight::get().writes((80_u64).saturating_mul(t.into()))) - .saturating_add(Weight::from_proof_size(211030).saturating_mul(t.into())) - .saturating_add(Weight::from_proof_size(50).saturating_mul(n.into())) + .saturating_add(Weight::from_parts(0, 211030).saturating_mul(t.into())) + .saturating_add(Weight::from_parts(0, 50).saturating_mul(n.into())) } /// Storage: System Account (r:1 w:0) /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: Measured) @@ -958,13 +958,13 @@ impl WeightInfo for SubstrateWeight { // Measured: `873 + r * (560 ±0)` // Estimated: `17240 + r * (2800 ±0)` // Minimum execution time: 148_635 nanoseconds. - Weight::from_ref_time(154_095_712) - .saturating_add(Weight::from_proof_size(17240)) + Weight::from_parts(154_095_712, 0) + .saturating_add(Weight::from_parts(0, 17240)) // Standard Error: 77_790 - .saturating_add(Weight::from_ref_time(14_837_085).saturating_mul(r.into())) + .saturating_add(Weight::from_parts(14_837_085, 0).saturating_mul(r.into())) .saturating_add(T::DbWeight::get().reads(6_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) - .saturating_add(Weight::from_proof_size(2800).saturating_mul(r.into())) + .saturating_add(Weight::from_parts(0, 2800).saturating_mul(r.into())) } /// Storage: System Account (r:1 w:0) /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) @@ -982,10 +982,10 @@ impl WeightInfo for SubstrateWeight { // Measured: `125824` // Estimated: `265128` // Minimum execution time: 501_014 nanoseconds. - Weight::from_ref_time(505_634_218) - .saturating_add(Weight::from_proof_size(265128)) + Weight::from_parts(505_634_218, 0) + .saturating_add(Weight::from_parts(0, 265128)) // Standard Error: 2_441 - .saturating_add(Weight::from_ref_time(819_257).saturating_mul(i.into())) + .saturating_add(Weight::from_parts(819_257, 0).saturating_mul(i.into())) .saturating_add(T::DbWeight::get().reads(6_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) } @@ -997,15 +997,15 @@ impl WeightInfo for SubstrateWeight { // Measured: `911 + r * (23420 ±0)` // Estimated: `911 + r * (23418 ±0)` // Minimum execution time: 375_301 nanoseconds. - Weight::from_ref_time(291_498_841) - .saturating_add(Weight::from_proof_size(911)) + Weight::from_parts(291_498_841, 0) + .saturating_add(Weight::from_parts(0, 911)) // Standard Error: 809_989 - .saturating_add(Weight::from_ref_time(464_550_291).saturating_mul(r.into())) + .saturating_add(Weight::from_parts(464_550_291, 0).saturating_mul(r.into())) .saturating_add(T::DbWeight::get().reads(6_u64)) .saturating_add(T::DbWeight::get().reads((80_u64).saturating_mul(r.into()))) .saturating_add(T::DbWeight::get().writes(3_u64)) .saturating_add(T::DbWeight::get().writes((80_u64).saturating_mul(r.into()))) - .saturating_add(Weight::from_proof_size(23418).saturating_mul(r.into())) + .saturating_add(Weight::from_parts(0, 23418).saturating_mul(r.into())) } /// Storage: Skipped Metadata (r:0 w:0) /// Proof Skipped: Skipped Metadata (max_values: None, max_size: None, mode: Measured) @@ -1015,15 +1015,15 @@ impl WeightInfo for SubstrateWeight { // Measured: `12672 + n * (11945 ±0)` // Estimated: `8529 + n * (12814 ±61)` // Minimum execution time: 506_318 nanoseconds. - Weight::from_ref_time(676_935_313) - .saturating_add(Weight::from_proof_size(8529)) + Weight::from_parts(676_935_313, 0) + .saturating_add(Weight::from_parts(0, 8529)) // Standard Error: 1_589_291 - .saturating_add(Weight::from_ref_time(97_839_399).saturating_mul(n.into())) + .saturating_add(Weight::from_parts(97_839_399, 0).saturating_mul(n.into())) .saturating_add(T::DbWeight::get().reads(52_u64)) .saturating_add(T::DbWeight::get().reads((7_u64).saturating_mul(n.into()))) .saturating_add(T::DbWeight::get().writes(50_u64)) .saturating_add(T::DbWeight::get().writes((7_u64).saturating_mul(n.into()))) - .saturating_add(Weight::from_proof_size(12814).saturating_mul(n.into())) + .saturating_add(Weight::from_parts(0, 12814).saturating_mul(n.into())) } /// Storage: Skipped Metadata (r:0 w:0) /// Proof Skipped: Skipped Metadata (max_values: None, max_size: None, mode: Measured) @@ -1033,15 +1033,15 @@ impl WeightInfo for SubstrateWeight { // Measured: `15170 + n * (175775 ±0)` // Estimated: `9914 + n * (176858 ±74)` // Minimum execution time: 506_148 nanoseconds. - Weight::from_ref_time(648_278_778) - .saturating_add(Weight::from_proof_size(9914)) + Weight::from_parts(648_278_778, 0) + .saturating_add(Weight::from_parts(0, 9914)) // Standard Error: 1_343_586 - .saturating_add(Weight::from_ref_time(65_789_595).saturating_mul(n.into())) + .saturating_add(Weight::from_parts(65_789_595, 0).saturating_mul(n.into())) .saturating_add(T::DbWeight::get().reads(51_u64)) .saturating_add(T::DbWeight::get().reads((7_u64).saturating_mul(n.into()))) .saturating_add(T::DbWeight::get().writes(49_u64)) .saturating_add(T::DbWeight::get().writes((7_u64).saturating_mul(n.into()))) - .saturating_add(Weight::from_proof_size(176858).saturating_mul(n.into())) + .saturating_add(Weight::from_parts(0, 176858).saturating_mul(n.into())) } /// Storage: Skipped Metadata (r:0 w:0) /// Proof Skipped: Skipped Metadata (max_values: None, max_size: None, mode: Measured) @@ -1051,15 +1051,15 @@ impl WeightInfo for SubstrateWeight { // Measured: `903 + r * (23099 ±0)` // Estimated: `908 + r * (23099 ±0)` // Minimum execution time: 374_344 nanoseconds. - Weight::from_ref_time(293_272_061) - .saturating_add(Weight::from_proof_size(908)) + Weight::from_parts(293_272_061, 0) + .saturating_add(Weight::from_parts(0, 908)) // Standard Error: 810_412 - .saturating_add(Weight::from_ref_time(453_315_956).saturating_mul(r.into())) + .saturating_add(Weight::from_parts(453_315_956, 0).saturating_mul(r.into())) .saturating_add(T::DbWeight::get().reads(6_u64)) .saturating_add(T::DbWeight::get().reads((80_u64).saturating_mul(r.into()))) .saturating_add(T::DbWeight::get().writes(3_u64)) .saturating_add(T::DbWeight::get().writes((80_u64).saturating_mul(r.into()))) - .saturating_add(Weight::from_proof_size(23099).saturating_mul(r.into())) + .saturating_add(Weight::from_parts(0, 23099).saturating_mul(r.into())) } /// Storage: Skipped Metadata (r:0 w:0) /// Proof Skipped: Skipped Metadata (max_values: None, max_size: None, mode: Measured) @@ -1069,15 +1069,15 @@ impl WeightInfo for SubstrateWeight { // Measured: `14895 + n * (175768 ±0)` // Estimated: `9551 + n * (176867 ±75)` // Minimum execution time: 478_564 nanoseconds. - Weight::from_ref_time(630_839_142) - .saturating_add(Weight::from_proof_size(9551)) + Weight::from_parts(630_839_142, 0) + .saturating_add(Weight::from_parts(0, 9551)) // Standard Error: 1_427_520 - .saturating_add(Weight::from_ref_time(66_813_592).saturating_mul(n.into())) + .saturating_add(Weight::from_parts(66_813_592, 0).saturating_mul(n.into())) .saturating_add(T::DbWeight::get().reads(51_u64)) .saturating_add(T::DbWeight::get().reads((7_u64).saturating_mul(n.into()))) .saturating_add(T::DbWeight::get().writes(48_u64)) .saturating_add(T::DbWeight::get().writes((7_u64).saturating_mul(n.into()))) - .saturating_add(Weight::from_proof_size(176867).saturating_mul(n.into())) + .saturating_add(Weight::from_parts(0, 176867).saturating_mul(n.into())) } /// Storage: Skipped Metadata (r:0 w:0) /// Proof Skipped: Skipped Metadata (max_values: None, max_size: None, mode: Measured) @@ -1087,14 +1087,14 @@ impl WeightInfo for SubstrateWeight { // Measured: `896 + r * (23744 ±0)` // Estimated: `909 + r * (23740 ±0)` // Minimum execution time: 374_479 nanoseconds. - Weight::from_ref_time(311_839_315) - .saturating_add(Weight::from_proof_size(909)) + Weight::from_parts(311_839_315, 0) + .saturating_add(Weight::from_parts(0, 909)) // Standard Error: 666_553 - .saturating_add(Weight::from_ref_time(371_213_042).saturating_mul(r.into())) + .saturating_add(Weight::from_parts(371_213_042, 0).saturating_mul(r.into())) .saturating_add(T::DbWeight::get().reads(6_u64)) .saturating_add(T::DbWeight::get().reads((80_u64).saturating_mul(r.into()))) .saturating_add(T::DbWeight::get().writes(3_u64)) - .saturating_add(Weight::from_proof_size(23740).saturating_mul(r.into())) + .saturating_add(Weight::from_parts(0, 23740).saturating_mul(r.into())) } /// Storage: Skipped Metadata (r:0 w:0) /// Proof Skipped: Skipped Metadata (max_values: None, max_size: None, mode: Measured) @@ -1104,14 +1104,14 @@ impl WeightInfo for SubstrateWeight { // Measured: `15501 + n * (175775 ±0)` // Estimated: `10042 + n * (176900 ±76)` // Minimum execution time: 460_639 nanoseconds. - Weight::from_ref_time(591_187_094) - .saturating_add(Weight::from_proof_size(10042)) + Weight::from_parts(591_187_094, 0) + .saturating_add(Weight::from_parts(0, 10042)) // Standard Error: 1_233_792 - .saturating_add(Weight::from_ref_time(160_874_477).saturating_mul(n.into())) + .saturating_add(Weight::from_parts(160_874_477, 0).saturating_mul(n.into())) .saturating_add(T::DbWeight::get().reads(51_u64)) .saturating_add(T::DbWeight::get().reads((7_u64).saturating_mul(n.into()))) .saturating_add(T::DbWeight::get().writes(3_u64)) - .saturating_add(Weight::from_proof_size(176900).saturating_mul(n.into())) + .saturating_add(Weight::from_parts(0, 176900).saturating_mul(n.into())) } /// Storage: Skipped Metadata (r:0 w:0) /// Proof Skipped: Skipped Metadata (max_values: None, max_size: None, mode: Measured) @@ -1121,14 +1121,14 @@ impl WeightInfo for SubstrateWeight { // Measured: `914 + r * (23098 ±0)` // Estimated: `920 + r * (23098 ±0)` // Minimum execution time: 374_272 nanoseconds. - Weight::from_ref_time(311_446_269) - .saturating_add(Weight::from_proof_size(920)) + Weight::from_parts(311_446_269, 0) + .saturating_add(Weight::from_parts(0, 920)) // Standard Error: 630_307 - .saturating_add(Weight::from_ref_time(357_134_931).saturating_mul(r.into())) + .saturating_add(Weight::from_parts(357_134_931, 0).saturating_mul(r.into())) .saturating_add(T::DbWeight::get().reads(6_u64)) .saturating_add(T::DbWeight::get().reads((80_u64).saturating_mul(r.into()))) .saturating_add(T::DbWeight::get().writes(3_u64)) - .saturating_add(Weight::from_proof_size(23098).saturating_mul(r.into())) + .saturating_add(Weight::from_parts(0, 23098).saturating_mul(r.into())) } /// Storage: Skipped Metadata (r:0 w:0) /// Proof Skipped: Skipped Metadata (max_values: None, max_size: None, mode: Measured) @@ -1138,14 +1138,14 @@ impl WeightInfo for SubstrateWeight { // Measured: `14839 + n * (175789 ±0)` // Estimated: `9532 + n * (176874 ±75)` // Minimum execution time: 456_013 nanoseconds. - Weight::from_ref_time(575_116_352) - .saturating_add(Weight::from_proof_size(9532)) + Weight::from_parts(575_116_352, 0) + .saturating_add(Weight::from_parts(0, 9532)) // Standard Error: 1_122_298 - .saturating_add(Weight::from_ref_time(61_786_107).saturating_mul(n.into())) + .saturating_add(Weight::from_parts(61_786_107, 0).saturating_mul(n.into())) .saturating_add(T::DbWeight::get().reads(51_u64)) .saturating_add(T::DbWeight::get().reads((7_u64).saturating_mul(n.into()))) .saturating_add(T::DbWeight::get().writes(3_u64)) - .saturating_add(Weight::from_proof_size(176874).saturating_mul(n.into())) + .saturating_add(Weight::from_parts(0, 176874).saturating_mul(n.into())) } /// Storage: Skipped Metadata (r:0 w:0) /// Proof Skipped: Skipped Metadata (max_values: None, max_size: None, mode: Measured) @@ -1155,15 +1155,15 @@ impl WeightInfo for SubstrateWeight { // Measured: `911 + r * (23740 ±0)` // Estimated: `913 + r * (23739 ±0)` // Minimum execution time: 374_621 nanoseconds. - Weight::from_ref_time(299_689_489) - .saturating_add(Weight::from_proof_size(913)) + Weight::from_parts(299_689_489, 0) + .saturating_add(Weight::from_parts(0, 913)) // Standard Error: 757_735 - .saturating_add(Weight::from_ref_time(465_213_246).saturating_mul(r.into())) + .saturating_add(Weight::from_parts(465_213_246, 0).saturating_mul(r.into())) .saturating_add(T::DbWeight::get().reads(6_u64)) .saturating_add(T::DbWeight::get().reads((80_u64).saturating_mul(r.into()))) .saturating_add(T::DbWeight::get().writes(3_u64)) .saturating_add(T::DbWeight::get().writes((80_u64).saturating_mul(r.into()))) - .saturating_add(Weight::from_proof_size(23739).saturating_mul(r.into())) + .saturating_add(Weight::from_parts(0, 23739).saturating_mul(r.into())) } /// Storage: Skipped Metadata (r:0 w:0) /// Proof Skipped: Skipped Metadata (max_values: None, max_size: None, mode: Measured) @@ -1173,15 +1173,15 @@ impl WeightInfo for SubstrateWeight { // Measured: `15502 + n * (175775 ±0)` // Estimated: `10042 + n * (176898 ±76)` // Minimum execution time: 481_980 nanoseconds. - Weight::from_ref_time(647_289_053) - .saturating_add(Weight::from_proof_size(10042)) + Weight::from_parts(647_289_053, 0) + .saturating_add(Weight::from_parts(0, 10042)) // Standard Error: 1_556_155 - .saturating_add(Weight::from_ref_time(166_592_657).saturating_mul(n.into())) + .saturating_add(Weight::from_parts(166_592_657, 0).saturating_mul(n.into())) .saturating_add(T::DbWeight::get().reads(51_u64)) .saturating_add(T::DbWeight::get().reads((7_u64).saturating_mul(n.into()))) .saturating_add(T::DbWeight::get().writes(48_u64)) .saturating_add(T::DbWeight::get().writes((7_u64).saturating_mul(n.into()))) - .saturating_add(Weight::from_proof_size(176898).saturating_mul(n.into())) + .saturating_add(Weight::from_parts(0, 176898).saturating_mul(n.into())) } /// Storage: System Account (r:1602 w:1601) /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: Measured) @@ -1199,15 +1199,15 @@ impl WeightInfo for SubstrateWeight { // Measured: `1457 + r * (3604 ±0)` // Estimated: `21583 + r * (216101 ±0)` // Minimum execution time: 374_962 nanoseconds. - Weight::from_ref_time(313_416_386) - .saturating_add(Weight::from_proof_size(21583)) + Weight::from_parts(313_416_386, 0) + .saturating_add(Weight::from_parts(0, 21583)) // Standard Error: 710_675 - .saturating_add(Weight::from_ref_time(1_396_551_156).saturating_mul(r.into())) + .saturating_add(Weight::from_parts(1_396_551_156, 0).saturating_mul(r.into())) .saturating_add(T::DbWeight::get().reads(7_u64)) .saturating_add(T::DbWeight::get().reads((80_u64).saturating_mul(r.into()))) .saturating_add(T::DbWeight::get().writes(4_u64)) .saturating_add(T::DbWeight::get().writes((80_u64).saturating_mul(r.into()))) - .saturating_add(Weight::from_proof_size(216101).saturating_mul(r.into())) + .saturating_add(Weight::from_parts(0, 216101).saturating_mul(r.into())) } /// Storage: System Account (r:1 w:0) /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: Measured) @@ -1225,15 +1225,15 @@ impl WeightInfo for SubstrateWeight { // Measured: `1609 + r * (23073 ±0)` // Estimated: `22098 + r * (511456 ±1)` // Minimum execution time: 375_916 nanoseconds. - Weight::from_ref_time(376_468_000) - .saturating_add(Weight::from_proof_size(22098)) + Weight::from_parts(376_468_000, 0) + .saturating_add(Weight::from_parts(0, 22098)) // Standard Error: 7_246_855 - .saturating_add(Weight::from_ref_time(28_982_425_139).saturating_mul(r.into())) + .saturating_add(Weight::from_parts(28_982_425_139, 0).saturating_mul(r.into())) .saturating_add(T::DbWeight::get().reads(7_u64)) .saturating_add(T::DbWeight::get().reads((160_u64).saturating_mul(r.into()))) .saturating_add(T::DbWeight::get().writes(3_u64)) .saturating_add(T::DbWeight::get().writes((160_u64).saturating_mul(r.into()))) - .saturating_add(Weight::from_proof_size(511456).saturating_mul(r.into())) + .saturating_add(Weight::from_parts(0, 511456).saturating_mul(r.into())) } /// Storage: System Account (r:1 w:0) /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: Measured) @@ -1251,15 +1251,15 @@ impl WeightInfo for SubstrateWeight { // Measured: `0 + r * (71670 ±0)` // Estimated: `17285 + r * (659930 ±563)` // Minimum execution time: 375_412 nanoseconds. - Weight::from_ref_time(376_493_000) - .saturating_add(Weight::from_proof_size(17285)) + Weight::from_parts(376_493_000, 0) + .saturating_add(Weight::from_parts(0, 17285)) // Standard Error: 8_239_575 - .saturating_add(Weight::from_ref_time(28_716_347_183).saturating_mul(r.into())) + .saturating_add(Weight::from_parts(28_716_347_183, 0).saturating_mul(r.into())) .saturating_add(T::DbWeight::get().reads(6_u64)) .saturating_add(T::DbWeight::get().reads((150_u64).saturating_mul(r.into()))) .saturating_add(T::DbWeight::get().writes(3_u64)) .saturating_add(T::DbWeight::get().writes((75_u64).saturating_mul(r.into()))) - .saturating_add(Weight::from_proof_size(659930).saturating_mul(r.into())) + .saturating_add(Weight::from_parts(0, 659930).saturating_mul(r.into())) } /// Storage: System Account (r:82 w:81) /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: Measured) @@ -1278,17 +1278,17 @@ impl WeightInfo for SubstrateWeight { // Measured: `24269 + t * (16910 ±0)` // Estimated: `532690 + t * (285025 ±0)` // Minimum execution time: 10_443_315 nanoseconds. - Weight::from_ref_time(9_342_574_069) - .saturating_add(Weight::from_proof_size(532690)) + Weight::from_parts(9_342_574_069, 0) + .saturating_add(Weight::from_parts(0, 532690)) // Standard Error: 7_237_279 - .saturating_add(Weight::from_ref_time(1_390_221_936).saturating_mul(t.into())) + .saturating_add(Weight::from_parts(1_390_221_936, 0).saturating_mul(t.into())) // Standard Error: 10_851 - .saturating_add(Weight::from_ref_time(9_842_151).saturating_mul(c.into())) + .saturating_add(Weight::from_parts(9_842_151, 0).saturating_mul(c.into())) .saturating_add(T::DbWeight::get().reads(167_u64)) .saturating_add(T::DbWeight::get().reads((81_u64).saturating_mul(t.into()))) .saturating_add(T::DbWeight::get().writes(163_u64)) .saturating_add(T::DbWeight::get().writes((81_u64).saturating_mul(t.into()))) - .saturating_add(Weight::from_proof_size(285025).saturating_mul(t.into())) + .saturating_add(Weight::from_parts(0, 285025).saturating_mul(t.into())) } /// Storage: System Account (r:3202 w:3202) /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: Measured) @@ -1310,15 +1310,15 @@ impl WeightInfo for SubstrateWeight { // Measured: `1775 + r * (25568 ±0)` // Estimated: `26563 + r * (1367114 ±2)` // Minimum execution time: 376_418 nanoseconds. - Weight::from_ref_time(377_292_000) - .saturating_add(Weight::from_proof_size(26563)) + Weight::from_parts(377_292_000, 0) + .saturating_add(Weight::from_parts(0, 26563)) // Standard Error: 32_312_545 - .saturating_add(Weight::from_ref_time(35_904_826_312).saturating_mul(r.into())) + .saturating_add(Weight::from_parts(35_904_826_312, 0).saturating_mul(r.into())) .saturating_add(T::DbWeight::get().reads(8_u64)) .saturating_add(T::DbWeight::get().reads((480_u64).saturating_mul(r.into()))) .saturating_add(T::DbWeight::get().writes(5_u64)) .saturating_add(T::DbWeight::get().writes((400_u64).saturating_mul(r.into()))) - .saturating_add(Weight::from_proof_size(1367114).saturating_mul(r.into())) + .saturating_add(Weight::from_parts(0, 1367114).saturating_mul(r.into())) } /// Storage: System Account (r:162 w:162) /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: Measured) @@ -1342,19 +1342,19 @@ impl WeightInfo for SubstrateWeight { // Measured: `5785 + t * (33 ±0)` // Estimated: `850985 + t * (2671 ±3)` // Minimum execution time: 132_157_340 nanoseconds. - Weight::from_ref_time(11_329_968_948) - .saturating_add(Weight::from_proof_size(850985)) + Weight::from_parts(11_329_968_948, 0) + .saturating_add(Weight::from_parts(0, 850985)) // Standard Error: 99_102_968 - .saturating_add(Weight::from_ref_time(84_719_458).saturating_mul(t.into())) + .saturating_add(Weight::from_parts(84_719_458, 0).saturating_mul(t.into())) // Standard Error: 161_609 - .saturating_add(Weight::from_ref_time(126_156_627).saturating_mul(i.into())) + .saturating_add(Weight::from_parts(126_156_627, 0).saturating_mul(i.into())) // Standard Error: 161_609 - .saturating_add(Weight::from_ref_time(126_628_313).saturating_mul(s.into())) + .saturating_add(Weight::from_parts(126_628_313, 0).saturating_mul(s.into())) .saturating_add(T::DbWeight::get().reads(329_u64)) .saturating_add(T::DbWeight::get().reads((1_u64).saturating_mul(t.into()))) .saturating_add(T::DbWeight::get().writes(326_u64)) .saturating_add(T::DbWeight::get().writes((1_u64).saturating_mul(t.into()))) - .saturating_add(Weight::from_proof_size(2671).saturating_mul(t.into())) + .saturating_add(Weight::from_parts(0, 2671).saturating_mul(t.into())) } /// Storage: System Account (r:1 w:0) /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: Measured) @@ -1372,13 +1372,13 @@ impl WeightInfo for SubstrateWeight { // Measured: `871 + r * (642 ±0)` // Estimated: `17225 + r * (3210 ±0)` // Minimum execution time: 373_559 nanoseconds. - Weight::from_ref_time(375_166_904) - .saturating_add(Weight::from_proof_size(17225)) + Weight::from_parts(375_166_904, 0) + .saturating_add(Weight::from_parts(0, 17225)) // Standard Error: 125_024 - .saturating_add(Weight::from_ref_time(42_291_595).saturating_mul(r.into())) + .saturating_add(Weight::from_parts(42_291_595, 0).saturating_mul(r.into())) .saturating_add(T::DbWeight::get().reads(6_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) - .saturating_add(Weight::from_proof_size(3210).saturating_mul(r.into())) + .saturating_add(Weight::from_parts(0, 3210).saturating_mul(r.into())) } /// Storage: System Account (r:1 w:0) /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: Measured) @@ -1396,10 +1396,10 @@ impl WeightInfo for SubstrateWeight { // Measured: `1673` // Estimated: `21160` // Minimum execution time: 416_233 nanoseconds. - Weight::from_ref_time(416_785_000) - .saturating_add(Weight::from_proof_size(21160)) + Weight::from_parts(416_785_000, 0) + .saturating_add(Weight::from_parts(0, 21160)) // Standard Error: 56_223 - .saturating_add(Weight::from_ref_time(324_513_835).saturating_mul(n.into())) + .saturating_add(Weight::from_parts(324_513_835, 0).saturating_mul(n.into())) .saturating_add(T::DbWeight::get().reads(6_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) } @@ -1419,13 +1419,13 @@ impl WeightInfo for SubstrateWeight { // Measured: `873 + r * (642 ±0)` // Estimated: `17235 + r * (3210 ±0)` // Minimum execution time: 371_735 nanoseconds. - Weight::from_ref_time(375_979_430) - .saturating_add(Weight::from_proof_size(17235)) + Weight::from_parts(375_979_430, 0) + .saturating_add(Weight::from_parts(0, 17235)) // Standard Error: 968_037 - .saturating_add(Weight::from_ref_time(57_780_769).saturating_mul(r.into())) + .saturating_add(Weight::from_parts(57_780_769, 0).saturating_mul(r.into())) .saturating_add(T::DbWeight::get().reads(6_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) - .saturating_add(Weight::from_proof_size(3210).saturating_mul(r.into())) + .saturating_add(Weight::from_parts(0, 3210).saturating_mul(r.into())) } /// Storage: System Account (r:1 w:0) /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: Measured) @@ -1443,10 +1443,10 @@ impl WeightInfo for SubstrateWeight { // Measured: `1675` // Estimated: `21205` // Minimum execution time: 428_196 nanoseconds. - Weight::from_ref_time(429_438_000) - .saturating_add(Weight::from_proof_size(21205)) + Weight::from_parts(429_438_000, 0) + .saturating_add(Weight::from_parts(0, 21205)) // Standard Error: 57_860 - .saturating_add(Weight::from_ref_time(260_917_896).saturating_mul(n.into())) + .saturating_add(Weight::from_parts(260_917_896, 0).saturating_mul(n.into())) .saturating_add(T::DbWeight::get().reads(6_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) } @@ -1466,13 +1466,13 @@ impl WeightInfo for SubstrateWeight { // Measured: `873 + r * (642 ±0)` // Estimated: `17235 + r * (3210 ±0)` // Minimum execution time: 371_412 nanoseconds. - Weight::from_ref_time(373_635_818) - .saturating_add(Weight::from_proof_size(17235)) + Weight::from_parts(373_635_818, 0) + .saturating_add(Weight::from_parts(0, 17235)) // Standard Error: 222_973 - .saturating_add(Weight::from_ref_time(33_347_181).saturating_mul(r.into())) + .saturating_add(Weight::from_parts(33_347_181, 0).saturating_mul(r.into())) .saturating_add(T::DbWeight::get().reads(6_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) - .saturating_add(Weight::from_proof_size(3210).saturating_mul(r.into())) + .saturating_add(Weight::from_parts(0, 3210).saturating_mul(r.into())) } /// Storage: System Account (r:1 w:0) /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: Measured) @@ -1490,10 +1490,10 @@ impl WeightInfo for SubstrateWeight { // Measured: `1675` // Estimated: `21180` // Minimum execution time: 405_858 nanoseconds. - Weight::from_ref_time(406_498_000) - .saturating_add(Weight::from_proof_size(21180)) + Weight::from_parts(406_498_000, 0) + .saturating_add(Weight::from_parts(0, 21180)) // Standard Error: 48_388 - .saturating_add(Weight::from_ref_time(103_283_157).saturating_mul(n.into())) + .saturating_add(Weight::from_parts(103_283_157, 0).saturating_mul(n.into())) .saturating_add(T::DbWeight::get().reads(6_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) } @@ -1513,13 +1513,13 @@ impl WeightInfo for SubstrateWeight { // Measured: `873 + r * (679 ±0)` // Estimated: `17235 + r * (3395 ±0)` // Minimum execution time: 371_746 nanoseconds. - Weight::from_ref_time(373_538_171) - .saturating_add(Weight::from_proof_size(17235)) + Weight::from_parts(373_538_171, 0) + .saturating_add(Weight::from_parts(0, 17235)) // Standard Error: 387_332 - .saturating_add(Weight::from_ref_time(35_933_528).saturating_mul(r.into())) + .saturating_add(Weight::from_parts(35_933_528, 0).saturating_mul(r.into())) .saturating_add(T::DbWeight::get().reads(6_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) - .saturating_add(Weight::from_proof_size(3395).saturating_mul(r.into())) + .saturating_add(Weight::from_parts(0, 3395).saturating_mul(r.into())) } /// Storage: System Account (r:1 w:0) /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: Measured) @@ -1537,10 +1537,10 @@ impl WeightInfo for SubstrateWeight { // Measured: `1675` // Estimated: `21225` // Minimum execution time: 405_752 nanoseconds. - Weight::from_ref_time(406_417_000) - .saturating_add(Weight::from_proof_size(21225)) + Weight::from_parts(406_417_000, 0) + .saturating_add(Weight::from_parts(0, 21225)) // Standard Error: 47_051 - .saturating_add(Weight::from_ref_time(103_325_027).saturating_mul(n.into())) + .saturating_add(Weight::from_parts(103_325_027, 0).saturating_mul(n.into())) .saturating_add(T::DbWeight::get().reads(6_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) } @@ -1560,13 +1560,13 @@ impl WeightInfo for SubstrateWeight { // Measured: `917 + r * (6083 ±0)` // Estimated: `17455 + r * (30415 ±0)` // Minimum execution time: 373_882 nanoseconds. - Weight::from_ref_time(376_553_787) - .saturating_add(Weight::from_proof_size(17455)) + Weight::from_parts(376_553_787, 0) + .saturating_add(Weight::from_parts(0, 17455)) // Standard Error: 912_833 - .saturating_add(Weight::from_ref_time(3_021_100_412).saturating_mul(r.into())) + .saturating_add(Weight::from_parts(3_021_100_412, 0).saturating_mul(r.into())) .saturating_add(T::DbWeight::get().reads(6_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) - .saturating_add(Weight::from_proof_size(30415).saturating_mul(r.into())) + .saturating_add(Weight::from_parts(0, 30415).saturating_mul(r.into())) } /// Storage: System Account (r:1 w:0) /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: Measured) @@ -1584,13 +1584,13 @@ impl WeightInfo for SubstrateWeight { // Measured: `886 + r * (3362 ±0)` // Estimated: `17300 + r * (16810 ±0)` // Minimum execution time: 373_673 nanoseconds. - Weight::from_ref_time(375_712_961) - .saturating_add(Weight::from_proof_size(17300)) + Weight::from_parts(375_712_961, 0) + .saturating_add(Weight::from_parts(0, 17300)) // Standard Error: 596_297 - .saturating_add(Weight::from_ref_time(738_257_638).saturating_mul(r.into())) + .saturating_add(Weight::from_parts(738_257_638, 0).saturating_mul(r.into())) .saturating_add(T::DbWeight::get().reads(6_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) - .saturating_add(Weight::from_proof_size(16810).saturating_mul(r.into())) + .saturating_add(Weight::from_parts(0, 16810).saturating_mul(r.into())) } /// Storage: System Account (r:1 w:0) /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: Measured) @@ -1610,15 +1610,15 @@ impl WeightInfo for SubstrateWeight { // Measured: `0 + r * (79300 ±0)` // Estimated: `64844 + r * (942952 ±833)` // Minimum execution time: 374_097 nanoseconds. - Weight::from_ref_time(374_985_000) - .saturating_add(Weight::from_proof_size(64844)) + Weight::from_parts(374_985_000, 0) + .saturating_add(Weight::from_parts(0, 64844)) // Standard Error: 3_772_336 - .saturating_add(Weight::from_ref_time(1_546_402_854).saturating_mul(r.into())) + .saturating_add(Weight::from_parts(1_546_402_854, 0).saturating_mul(r.into())) .saturating_add(T::DbWeight::get().reads(6_u64)) .saturating_add(T::DbWeight::get().reads((225_u64).saturating_mul(r.into()))) .saturating_add(T::DbWeight::get().writes(3_u64)) .saturating_add(T::DbWeight::get().writes((150_u64).saturating_mul(r.into()))) - .saturating_add(Weight::from_proof_size(942952).saturating_mul(r.into())) + .saturating_add(Weight::from_parts(0, 942952).saturating_mul(r.into())) } /// Storage: System Account (r:1 w:0) /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: Measured) @@ -1636,13 +1636,13 @@ impl WeightInfo for SubstrateWeight { // Measured: `869 + r * (240 ±0)` // Estimated: `17215 + r * (1200 ±0)` // Minimum execution time: 374_249 nanoseconds. - Weight::from_ref_time(377_990_998) - .saturating_add(Weight::from_proof_size(17215)) + Weight::from_parts(377_990_998, 0) + .saturating_add(Weight::from_parts(0, 17215)) // Standard Error: 38_133 - .saturating_add(Weight::from_ref_time(11_483_273).saturating_mul(r.into())) + .saturating_add(Weight::from_parts(11_483_273, 0).saturating_mul(r.into())) .saturating_add(T::DbWeight::get().reads(6_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) - .saturating_add(Weight::from_proof_size(1200).saturating_mul(r.into())) + .saturating_add(Weight::from_parts(0, 1200).saturating_mul(r.into())) } /// Storage: System Account (r:1 w:0) /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: Measured) @@ -1660,13 +1660,13 @@ impl WeightInfo for SubstrateWeight { // Measured: `2102 + r * (3154 ±0)` // Estimated: `21980 + r * (15875 ±2)` // Minimum execution time: 375_552 nanoseconds. - Weight::from_ref_time(400_624_032) - .saturating_add(Weight::from_proof_size(21980)) + Weight::from_parts(400_624_032, 0) + .saturating_add(Weight::from_parts(0, 21980)) // Standard Error: 82_523 - .saturating_add(Weight::from_ref_time(18_057_327).saturating_mul(r.into())) + .saturating_add(Weight::from_parts(18_057_327, 0).saturating_mul(r.into())) .saturating_add(T::DbWeight::get().reads(6_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) - .saturating_add(Weight::from_proof_size(15875).saturating_mul(r.into())) + .saturating_add(Weight::from_parts(0, 15875).saturating_mul(r.into())) } /// Storage: System Account (r:1 w:0) /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: Measured) @@ -1686,13 +1686,13 @@ impl WeightInfo for SubstrateWeight { // Measured: `872 + r * (240 ±0)` // Estimated: `18598 + r * (1440 ±0)` // Minimum execution time: 373_899 nanoseconds. - Weight::from_ref_time(379_733_943) - .saturating_add(Weight::from_proof_size(18598)) + Weight::from_parts(379_733_943, 0) + .saturating_add(Weight::from_parts(0, 18598)) // Standard Error: 32_022 - .saturating_add(Weight::from_ref_time(9_381_180).saturating_mul(r.into())) + .saturating_add(Weight::from_parts(9_381_180, 0).saturating_mul(r.into())) .saturating_add(T::DbWeight::get().reads(7_u64)) .saturating_add(T::DbWeight::get().writes(4_u64)) - .saturating_add(Weight::from_proof_size(1440).saturating_mul(r.into())) + .saturating_add(Weight::from_parts(0, 1440).saturating_mul(r.into())) } /// The range of component `r` is `[0, 50]`. fn instr_i64const(r: u32, ) -> Weight { @@ -1700,10 +1700,10 @@ impl WeightInfo for SubstrateWeight { // Measured: `0` // Estimated: `0` // Minimum execution time: 834 nanoseconds. - Weight::from_ref_time(1_009_646) - .saturating_add(Weight::from_proof_size(0)) + Weight::from_parts(1_009_646, 0) + .saturating_add(Weight::from_parts(0, 0)) // Standard Error: 388 - .saturating_add(Weight::from_ref_time(411_979).saturating_mul(r.into())) + .saturating_add(Weight::from_parts(411_979, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 50]`. fn instr_i64load(r: u32, ) -> Weight { @@ -1711,10 +1711,10 @@ impl WeightInfo for SubstrateWeight { // Measured: `0` // Estimated: `0` // Minimum execution time: 882 nanoseconds. - Weight::from_ref_time(1_416_377) - .saturating_add(Weight::from_proof_size(0)) + Weight::from_parts(1_416_377, 0) + .saturating_add(Weight::from_parts(0, 0)) // Standard Error: 1_133 - .saturating_add(Weight::from_ref_time(1_075_838).saturating_mul(r.into())) + .saturating_add(Weight::from_parts(1_075_838, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 50]`. fn instr_i64store(r: u32, ) -> Weight { @@ -1722,10 +1722,10 @@ impl WeightInfo for SubstrateWeight { // Measured: `0` // Estimated: `0` // Minimum execution time: 878 nanoseconds. - Weight::from_ref_time(1_343_056) - .saturating_add(Weight::from_proof_size(0)) + Weight::from_parts(1_343_056, 0) + .saturating_add(Weight::from_parts(0, 0)) // Standard Error: 426 - .saturating_add(Weight::from_ref_time(1_001_214).saturating_mul(r.into())) + .saturating_add(Weight::from_parts(1_001_214, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 50]`. fn instr_select(r: u32, ) -> Weight { @@ -1733,10 +1733,10 @@ impl WeightInfo for SubstrateWeight { // Measured: `0` // Estimated: `0` // Minimum execution time: 796 nanoseconds. - Weight::from_ref_time(1_079_086) - .saturating_add(Weight::from_proof_size(0)) + Weight::from_parts(1_079_086, 0) + .saturating_add(Weight::from_parts(0, 0)) // Standard Error: 409 - .saturating_add(Weight::from_ref_time(1_149_188).saturating_mul(r.into())) + .saturating_add(Weight::from_parts(1_149_188, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 50]`. fn instr_if(r: u32, ) -> Weight { @@ -1744,10 +1744,10 @@ impl WeightInfo for SubstrateWeight { // Measured: `0` // Estimated: `0` // Minimum execution time: 800 nanoseconds. - Weight::from_ref_time(1_044_184) - .saturating_add(Weight::from_proof_size(0)) + Weight::from_parts(1_044_184, 0) + .saturating_add(Weight::from_parts(0, 0)) // Standard Error: 707 - .saturating_add(Weight::from_ref_time(1_315_686).saturating_mul(r.into())) + .saturating_add(Weight::from_parts(1_315_686, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 50]`. fn instr_br(r: u32, ) -> Weight { @@ -1755,10 +1755,10 @@ impl WeightInfo for SubstrateWeight { // Measured: `0` // Estimated: `0` // Minimum execution time: 807 nanoseconds. - Weight::from_ref_time(1_049_633) - .saturating_add(Weight::from_proof_size(0)) + Weight::from_parts(1_049_633, 0) + .saturating_add(Weight::from_parts(0, 0)) // Standard Error: 361 - .saturating_add(Weight::from_ref_time(640_530).saturating_mul(r.into())) + .saturating_add(Weight::from_parts(640_530, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 50]`. fn instr_br_if(r: u32, ) -> Weight { @@ -1766,10 +1766,10 @@ impl WeightInfo for SubstrateWeight { // Measured: `0` // Estimated: `0` // Minimum execution time: 774 nanoseconds. - Weight::from_ref_time(1_124_053) - .saturating_add(Weight::from_proof_size(0)) + Weight::from_parts(1_124_053, 0) + .saturating_add(Weight::from_parts(0, 0)) // Standard Error: 784 - .saturating_add(Weight::from_ref_time(949_398).saturating_mul(r.into())) + .saturating_add(Weight::from_parts(949_398, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 50]`. fn instr_br_table(r: u32, ) -> Weight { @@ -1777,10 +1777,10 @@ impl WeightInfo for SubstrateWeight { // Measured: `0` // Estimated: `0` // Minimum execution time: 810 nanoseconds. - Weight::from_ref_time(676_581) - .saturating_add(Weight::from_proof_size(0)) + Weight::from_parts(676_581, 0) + .saturating_add(Weight::from_parts(0, 0)) // Standard Error: 2_356 - .saturating_add(Weight::from_ref_time(1_163_465).saturating_mul(r.into())) + .saturating_add(Weight::from_parts(1_163_465, 0).saturating_mul(r.into())) } /// The range of component `e` is `[1, 256]`. fn instr_br_table_per_entry(e: u32, ) -> Weight { @@ -1788,10 +1788,10 @@ impl WeightInfo for SubstrateWeight { // Measured: `0` // Estimated: `0` // Minimum execution time: 2_580 nanoseconds. - Weight::from_ref_time(2_835_656) - .saturating_add(Weight::from_proof_size(0)) + Weight::from_parts(2_835_656, 0) + .saturating_add(Weight::from_parts(0, 0)) // Standard Error: 71 - .saturating_add(Weight::from_ref_time(4_686).saturating_mul(e.into())) + .saturating_add(Weight::from_parts(4_686, 0).saturating_mul(e.into())) } /// The range of component `r` is `[0, 50]`. fn instr_call(r: u32, ) -> Weight { @@ -1799,10 +1799,10 @@ impl WeightInfo for SubstrateWeight { // Measured: `0` // Estimated: `0` // Minimum execution time: 826 nanoseconds. - Weight::from_ref_time(1_625_698) - .saturating_add(Weight::from_proof_size(0)) + Weight::from_parts(1_625_698, 0) + .saturating_add(Weight::from_parts(0, 0)) // Standard Error: 1_740 - .saturating_add(Weight::from_ref_time(2_332_187).saturating_mul(r.into())) + .saturating_add(Weight::from_parts(2_332_187, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 50]`. fn instr_call_indirect(r: u32, ) -> Weight { @@ -1810,10 +1810,10 @@ impl WeightInfo for SubstrateWeight { // Measured: `0` // Estimated: `0` // Minimum execution time: 901 nanoseconds. - Weight::from_ref_time(2_338_620) - .saturating_add(Weight::from_proof_size(0)) + Weight::from_parts(2_338_620, 0) + .saturating_add(Weight::from_parts(0, 0)) // Standard Error: 1_642 - .saturating_add(Weight::from_ref_time(2_924_090).saturating_mul(r.into())) + .saturating_add(Weight::from_parts(2_924_090, 0).saturating_mul(r.into())) } /// The range of component `p` is `[0, 128]`. fn instr_call_indirect_per_param(p: u32, ) -> Weight { @@ -1821,10 +1821,10 @@ impl WeightInfo for SubstrateWeight { // Measured: `0` // Estimated: `0` // Minimum execution time: 4_670 nanoseconds. - Weight::from_ref_time(5_556_246) - .saturating_add(Weight::from_proof_size(0)) + Weight::from_parts(5_556_246, 0) + .saturating_add(Weight::from_parts(0, 0)) // Standard Error: 1_491 - .saturating_add(Weight::from_ref_time(228_965).saturating_mul(p.into())) + .saturating_add(Weight::from_parts(228_965, 0).saturating_mul(p.into())) } /// The range of component `l` is `[0, 1024]`. fn instr_call_per_local(l: u32, ) -> Weight { @@ -1832,10 +1832,10 @@ impl WeightInfo for SubstrateWeight { // Measured: `0` // Estimated: `0` // Minimum execution time: 3_099 nanoseconds. - Weight::from_ref_time(3_896_177) - .saturating_add(Weight::from_proof_size(0)) + Weight::from_parts(3_896_177, 0) + .saturating_add(Weight::from_parts(0, 0)) // Standard Error: 99 - .saturating_add(Weight::from_ref_time(91_304).saturating_mul(l.into())) + .saturating_add(Weight::from_parts(91_304, 0).saturating_mul(l.into())) } /// The range of component `r` is `[0, 50]`. fn instr_local_get(r: u32, ) -> Weight { @@ -1843,10 +1843,10 @@ impl WeightInfo for SubstrateWeight { // Measured: `0` // Estimated: `0` // Minimum execution time: 3_042 nanoseconds. - Weight::from_ref_time(3_334_621) - .saturating_add(Weight::from_proof_size(0)) + Weight::from_parts(3_334_621, 0) + .saturating_add(Weight::from_parts(0, 0)) // Standard Error: 793 - .saturating_add(Weight::from_ref_time(459_346).saturating_mul(r.into())) + .saturating_add(Weight::from_parts(459_346, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 50]`. fn instr_local_set(r: u32, ) -> Weight { @@ -1854,10 +1854,10 @@ impl WeightInfo for SubstrateWeight { // Measured: `0` // Estimated: `0` // Minimum execution time: 2_968 nanoseconds. - Weight::from_ref_time(3_235_286) - .saturating_add(Weight::from_proof_size(0)) + Weight::from_parts(3_235_286, 0) + .saturating_add(Weight::from_parts(0, 0)) // Standard Error: 427 - .saturating_add(Weight::from_ref_time(485_454).saturating_mul(r.into())) + .saturating_add(Weight::from_parts(485_454, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 50]`. fn instr_local_tee(r: u32, ) -> Weight { @@ -1865,10 +1865,10 @@ impl WeightInfo for SubstrateWeight { // Measured: `0` // Estimated: `0` // Minimum execution time: 3_012 nanoseconds. - Weight::from_ref_time(3_303_555) - .saturating_add(Weight::from_proof_size(0)) + Weight::from_parts(3_303_555, 0) + .saturating_add(Weight::from_parts(0, 0)) // Standard Error: 371 - .saturating_add(Weight::from_ref_time(657_811).saturating_mul(r.into())) + .saturating_add(Weight::from_parts(657_811, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 50]`. fn instr_global_get(r: u32, ) -> Weight { @@ -1876,10 +1876,10 @@ impl WeightInfo for SubstrateWeight { // Measured: `0` // Estimated: `0` // Minimum execution time: 865 nanoseconds. - Weight::from_ref_time(1_249_987) - .saturating_add(Weight::from_proof_size(0)) + Weight::from_parts(1_249_987, 0) + .saturating_add(Weight::from_parts(0, 0)) // Standard Error: 417 - .saturating_add(Weight::from_ref_time(896_704).saturating_mul(r.into())) + .saturating_add(Weight::from_parts(896_704, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 50]`. fn instr_global_set(r: u32, ) -> Weight { @@ -1887,10 +1887,10 @@ impl WeightInfo for SubstrateWeight { // Measured: `0` // Estimated: `0` // Minimum execution time: 866 nanoseconds. - Weight::from_ref_time(1_216_218) - .saturating_add(Weight::from_proof_size(0)) + Weight::from_parts(1_216_218, 0) + .saturating_add(Weight::from_parts(0, 0)) // Standard Error: 503 - .saturating_add(Weight::from_ref_time(919_719).saturating_mul(r.into())) + .saturating_add(Weight::from_parts(919_719, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 50]`. fn instr_memory_current(r: u32, ) -> Weight { @@ -1898,10 +1898,10 @@ impl WeightInfo for SubstrateWeight { // Measured: `0` // Estimated: `0` // Minimum execution time: 921 nanoseconds. - Weight::from_ref_time(1_228_408) - .saturating_add(Weight::from_proof_size(0)) + Weight::from_parts(1_228_408, 0) + .saturating_add(Weight::from_parts(0, 0)) // Standard Error: 309 - .saturating_add(Weight::from_ref_time(813_007).saturating_mul(r.into())) + .saturating_add(Weight::from_parts(813_007, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 1]`. fn instr_memory_grow(r: u32, ) -> Weight { @@ -1909,10 +1909,10 @@ impl WeightInfo for SubstrateWeight { // Measured: `0` // Estimated: `0` // Minimum execution time: 820 nanoseconds. - Weight::from_ref_time(914_830) - .saturating_add(Weight::from_proof_size(0)) + Weight::from_parts(914_830, 0) + .saturating_add(Weight::from_parts(0, 0)) // Standard Error: 6_018 - .saturating_add(Weight::from_ref_time(237_062_769).saturating_mul(r.into())) + .saturating_add(Weight::from_parts(237_062_769, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 50]`. fn instr_i64clz(r: u32, ) -> Weight { @@ -1920,10 +1920,10 @@ impl WeightInfo for SubstrateWeight { // Measured: `0` // Estimated: `0` // Minimum execution time: 812 nanoseconds. - Weight::from_ref_time(1_554_406) - .saturating_add(Weight::from_proof_size(0)) + Weight::from_parts(1_554_406, 0) + .saturating_add(Weight::from_parts(0, 0)) // Standard Error: 9_979 - .saturating_add(Weight::from_ref_time(625_434).saturating_mul(r.into())) + .saturating_add(Weight::from_parts(625_434, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 50]`. fn instr_i64ctz(r: u32, ) -> Weight { @@ -1931,10 +1931,10 @@ impl WeightInfo for SubstrateWeight { // Measured: `0` // Estimated: `0` // Minimum execution time: 798 nanoseconds. - Weight::from_ref_time(1_095_113) - .saturating_add(Weight::from_proof_size(0)) + Weight::from_parts(1_095_113, 0) + .saturating_add(Weight::from_parts(0, 0)) // Standard Error: 243 - .saturating_add(Weight::from_ref_time(634_204).saturating_mul(r.into())) + .saturating_add(Weight::from_parts(634_204, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 50]`. fn instr_i64popcnt(r: u32, ) -> Weight { @@ -1942,10 +1942,10 @@ impl WeightInfo for SubstrateWeight { // Measured: `0` // Estimated: `0` // Minimum execution time: 792 nanoseconds. - Weight::from_ref_time(1_109_845) - .saturating_add(Weight::from_proof_size(0)) + Weight::from_parts(1_109_845, 0) + .saturating_add(Weight::from_parts(0, 0)) // Standard Error: 14_944 - .saturating_add(Weight::from_ref_time(658_834).saturating_mul(r.into())) + .saturating_add(Weight::from_parts(658_834, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 50]`. fn instr_i64eqz(r: u32, ) -> Weight { @@ -1953,10 +1953,10 @@ impl WeightInfo for SubstrateWeight { // Measured: `0` // Estimated: `0` // Minimum execution time: 815 nanoseconds. - Weight::from_ref_time(1_068_916) - .saturating_add(Weight::from_proof_size(0)) + Weight::from_parts(1_068_916, 0) + .saturating_add(Weight::from_parts(0, 0)) // Standard Error: 327 - .saturating_add(Weight::from_ref_time(652_897).saturating_mul(r.into())) + .saturating_add(Weight::from_parts(652_897, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 50]`. fn instr_i64extendsi32(r: u32, ) -> Weight { @@ -1964,10 +1964,10 @@ impl WeightInfo for SubstrateWeight { // Measured: `0` // Estimated: `0` // Minimum execution time: 798 nanoseconds. - Weight::from_ref_time(1_069_745) - .saturating_add(Weight::from_proof_size(0)) + Weight::from_parts(1_069_745, 0) + .saturating_add(Weight::from_parts(0, 0)) // Standard Error: 306 - .saturating_add(Weight::from_ref_time(618_481).saturating_mul(r.into())) + .saturating_add(Weight::from_parts(618_481, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 50]`. fn instr_i64extendui32(r: u32, ) -> Weight { @@ -1975,10 +1975,10 @@ impl WeightInfo for SubstrateWeight { // Measured: `0` // Estimated: `0` // Minimum execution time: 799 nanoseconds. - Weight::from_ref_time(1_398_001) - .saturating_add(Weight::from_proof_size(0)) + Weight::from_parts(1_398_001, 0) + .saturating_add(Weight::from_parts(0, 0)) // Standard Error: 6_234 - .saturating_add(Weight::from_ref_time(611_399).saturating_mul(r.into())) + .saturating_add(Weight::from_parts(611_399, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 50]`. fn instr_i32wrapi64(r: u32, ) -> Weight { @@ -1986,10 +1986,10 @@ impl WeightInfo for SubstrateWeight { // Measured: `0` // Estimated: `0` // Minimum execution time: 811 nanoseconds. - Weight::from_ref_time(1_098_083) - .saturating_add(Weight::from_proof_size(0)) + Weight::from_parts(1_098_083, 0) + .saturating_add(Weight::from_parts(0, 0)) // Standard Error: 297 - .saturating_add(Weight::from_ref_time(617_692).saturating_mul(r.into())) + .saturating_add(Weight::from_parts(617_692, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 50]`. fn instr_i64eq(r: u32, ) -> Weight { @@ -1997,10 +1997,10 @@ impl WeightInfo for SubstrateWeight { // Measured: `0` // Estimated: `0` // Minimum execution time: 815 nanoseconds. - Weight::from_ref_time(1_046_922) - .saturating_add(Weight::from_proof_size(0)) + Weight::from_parts(1_046_922, 0) + .saturating_add(Weight::from_parts(0, 0)) // Standard Error: 335 - .saturating_add(Weight::from_ref_time(909_196).saturating_mul(r.into())) + .saturating_add(Weight::from_parts(909_196, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 50]`. fn instr_i64ne(r: u32, ) -> Weight { @@ -2008,10 +2008,10 @@ impl WeightInfo for SubstrateWeight { // Measured: `0` // Estimated: `0` // Minimum execution time: 805 nanoseconds. - Weight::from_ref_time(1_093_667) - .saturating_add(Weight::from_proof_size(0)) + Weight::from_parts(1_093_667, 0) + .saturating_add(Weight::from_parts(0, 0)) // Standard Error: 233 - .saturating_add(Weight::from_ref_time(907_378).saturating_mul(r.into())) + .saturating_add(Weight::from_parts(907_378, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 50]`. fn instr_i64lts(r: u32, ) -> Weight { @@ -2019,10 +2019,10 @@ impl WeightInfo for SubstrateWeight { // Measured: `0` // Estimated: `0` // Minimum execution time: 805 nanoseconds. - Weight::from_ref_time(1_290_591) - .saturating_add(Weight::from_proof_size(0)) + Weight::from_parts(1_290_591, 0) + .saturating_add(Weight::from_parts(0, 0)) // Standard Error: 3_201 - .saturating_add(Weight::from_ref_time(902_006).saturating_mul(r.into())) + .saturating_add(Weight::from_parts(902_006, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 50]`. fn instr_i64ltu(r: u32, ) -> Weight { @@ -2030,10 +2030,10 @@ impl WeightInfo for SubstrateWeight { // Measured: `0` // Estimated: `0` // Minimum execution time: 783 nanoseconds. - Weight::from_ref_time(1_159_977) - .saturating_add(Weight::from_proof_size(0)) + Weight::from_parts(1_159_977, 0) + .saturating_add(Weight::from_parts(0, 0)) // Standard Error: 2_310 - .saturating_add(Weight::from_ref_time(906_489).saturating_mul(r.into())) + .saturating_add(Weight::from_parts(906_489, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 50]`. fn instr_i64gts(r: u32, ) -> Weight { @@ -2041,10 +2041,10 @@ impl WeightInfo for SubstrateWeight { // Measured: `0` // Estimated: `0` // Minimum execution time: 790 nanoseconds. - Weight::from_ref_time(1_109_719) - .saturating_add(Weight::from_proof_size(0)) + Weight::from_parts(1_109_719, 0) + .saturating_add(Weight::from_parts(0, 0)) // Standard Error: 261 - .saturating_add(Weight::from_ref_time(906_614).saturating_mul(r.into())) + .saturating_add(Weight::from_parts(906_614, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 50]`. fn instr_i64gtu(r: u32, ) -> Weight { @@ -2052,10 +2052,10 @@ impl WeightInfo for SubstrateWeight { // Measured: `0` // Estimated: `0` // Minimum execution time: 776 nanoseconds. - Weight::from_ref_time(1_076_567) - .saturating_add(Weight::from_proof_size(0)) + Weight::from_parts(1_076_567, 0) + .saturating_add(Weight::from_parts(0, 0)) // Standard Error: 348 - .saturating_add(Weight::from_ref_time(919_374).saturating_mul(r.into())) + .saturating_add(Weight::from_parts(919_374, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 50]`. fn instr_i64les(r: u32, ) -> Weight { @@ -2063,10 +2063,10 @@ impl WeightInfo for SubstrateWeight { // Measured: `0` // Estimated: `0` // Minimum execution time: 780 nanoseconds. - Weight::from_ref_time(1_069_663) - .saturating_add(Weight::from_proof_size(0)) + Weight::from_parts(1_069_663, 0) + .saturating_add(Weight::from_parts(0, 0)) // Standard Error: 265 - .saturating_add(Weight::from_ref_time(908_037).saturating_mul(r.into())) + .saturating_add(Weight::from_parts(908_037, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 50]`. fn instr_i64leu(r: u32, ) -> Weight { @@ -2074,10 +2074,10 @@ impl WeightInfo for SubstrateWeight { // Measured: `0` // Estimated: `0` // Minimum execution time: 832 nanoseconds. - Weight::from_ref_time(930_920) - .saturating_add(Weight::from_proof_size(0)) + Weight::from_parts(930_920, 0) + .saturating_add(Weight::from_parts(0, 0)) // Standard Error: 2_170 - .saturating_add(Weight::from_ref_time(929_811).saturating_mul(r.into())) + .saturating_add(Weight::from_parts(929_811, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 50]`. fn instr_i64ges(r: u32, ) -> Weight { @@ -2085,10 +2085,10 @@ impl WeightInfo for SubstrateWeight { // Measured: `0` // Estimated: `0` // Minimum execution time: 787 nanoseconds. - Weight::from_ref_time(1_087_325) - .saturating_add(Weight::from_proof_size(0)) + Weight::from_parts(1_087_325, 0) + .saturating_add(Weight::from_parts(0, 0)) // Standard Error: 315 - .saturating_add(Weight::from_ref_time(908_321).saturating_mul(r.into())) + .saturating_add(Weight::from_parts(908_321, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 50]`. fn instr_i64geu(r: u32, ) -> Weight { @@ -2096,10 +2096,10 @@ impl WeightInfo for SubstrateWeight { // Measured: `0` // Estimated: `0` // Minimum execution time: 798 nanoseconds. - Weight::from_ref_time(1_029_132) - .saturating_add(Weight::from_proof_size(0)) + Weight::from_parts(1_029_132, 0) + .saturating_add(Weight::from_parts(0, 0)) // Standard Error: 2_095 - .saturating_add(Weight::from_ref_time(913_553).saturating_mul(r.into())) + .saturating_add(Weight::from_parts(913_553, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 50]`. fn instr_i64add(r: u32, ) -> Weight { @@ -2107,10 +2107,10 @@ impl WeightInfo for SubstrateWeight { // Measured: `0` // Estimated: `0` // Minimum execution time: 791 nanoseconds. - Weight::from_ref_time(1_086_314) - .saturating_add(Weight::from_proof_size(0)) + Weight::from_parts(1_086_314, 0) + .saturating_add(Weight::from_parts(0, 0)) // Standard Error: 197 - .saturating_add(Weight::from_ref_time(896_392).saturating_mul(r.into())) + .saturating_add(Weight::from_parts(896_392, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 50]`. fn instr_i64sub(r: u32, ) -> Weight { @@ -2118,10 +2118,10 @@ impl WeightInfo for SubstrateWeight { // Measured: `0` // Estimated: `0` // Minimum execution time: 793 nanoseconds. - Weight::from_ref_time(1_078_172) - .saturating_add(Weight::from_proof_size(0)) + Weight::from_parts(1_078_172, 0) + .saturating_add(Weight::from_parts(0, 0)) // Standard Error: 404 - .saturating_add(Weight::from_ref_time(886_329).saturating_mul(r.into())) + .saturating_add(Weight::from_parts(886_329, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 50]`. fn instr_i64mul(r: u32, ) -> Weight { @@ -2129,10 +2129,10 @@ impl WeightInfo for SubstrateWeight { // Measured: `0` // Estimated: `0` // Minimum execution time: 799 nanoseconds. - Weight::from_ref_time(1_095_010) - .saturating_add(Weight::from_proof_size(0)) + Weight::from_parts(1_095_010, 0) + .saturating_add(Weight::from_parts(0, 0)) // Standard Error: 431 - .saturating_add(Weight::from_ref_time(886_513).saturating_mul(r.into())) + .saturating_add(Weight::from_parts(886_513, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 50]`. fn instr_i64divs(r: u32, ) -> Weight { @@ -2140,10 +2140,10 @@ impl WeightInfo for SubstrateWeight { // Measured: `0` // Estimated: `0` // Minimum execution time: 810 nanoseconds. - Weight::from_ref_time(1_114_325) - .saturating_add(Weight::from_proof_size(0)) + Weight::from_parts(1_114_325, 0) + .saturating_add(Weight::from_parts(0, 0)) // Standard Error: 452 - .saturating_add(Weight::from_ref_time(1_521_849).saturating_mul(r.into())) + .saturating_add(Weight::from_parts(1_521_849, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 50]`. fn instr_i64divu(r: u32, ) -> Weight { @@ -2151,10 +2151,10 @@ impl WeightInfo for SubstrateWeight { // Measured: `0` // Estimated: `0` // Minimum execution time: 784 nanoseconds. - Weight::from_ref_time(1_123_153) - .saturating_add(Weight::from_proof_size(0)) + Weight::from_parts(1_123_153, 0) + .saturating_add(Weight::from_parts(0, 0)) // Standard Error: 475 - .saturating_add(Weight::from_ref_time(1_457_746).saturating_mul(r.into())) + .saturating_add(Weight::from_parts(1_457_746, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 50]`. fn instr_i64rems(r: u32, ) -> Weight { @@ -2162,10 +2162,10 @@ impl WeightInfo for SubstrateWeight { // Measured: `0` // Estimated: `0` // Minimum execution time: 809 nanoseconds. - Weight::from_ref_time(1_145_906) - .saturating_add(Weight::from_proof_size(0)) + Weight::from_parts(1_145_906, 0) + .saturating_add(Weight::from_parts(0, 0)) // Standard Error: 718 - .saturating_add(Weight::from_ref_time(1_549_964).saturating_mul(r.into())) + .saturating_add(Weight::from_parts(1_549_964, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 50]`. fn instr_i64remu(r: u32, ) -> Weight { @@ -2173,10 +2173,10 @@ impl WeightInfo for SubstrateWeight { // Measured: `0` // Estimated: `0` // Minimum execution time: 803 nanoseconds. - Weight::from_ref_time(1_110_328) - .saturating_add(Weight::from_proof_size(0)) + Weight::from_parts(1_110_328, 0) + .saturating_add(Weight::from_parts(0, 0)) // Standard Error: 627 - .saturating_add(Weight::from_ref_time(1_453_013).saturating_mul(r.into())) + .saturating_add(Weight::from_parts(1_453_013, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 50]`. fn instr_i64and(r: u32, ) -> Weight { @@ -2184,10 +2184,10 @@ impl WeightInfo for SubstrateWeight { // Measured: `0` // Estimated: `0` // Minimum execution time: 786 nanoseconds. - Weight::from_ref_time(1_108_792) - .saturating_add(Weight::from_proof_size(0)) + Weight::from_parts(1_108_792, 0) + .saturating_add(Weight::from_parts(0, 0)) // Standard Error: 286 - .saturating_add(Weight::from_ref_time(897_035).saturating_mul(r.into())) + .saturating_add(Weight::from_parts(897_035, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 50]`. fn instr_i64or(r: u32, ) -> Weight { @@ -2195,10 +2195,10 @@ impl WeightInfo for SubstrateWeight { // Measured: `0` // Estimated: `0` // Minimum execution time: 787 nanoseconds. - Weight::from_ref_time(830_000) - .saturating_add(Weight::from_proof_size(0)) + Weight::from_parts(830_000, 0) + .saturating_add(Weight::from_parts(0, 0)) // Standard Error: 15_995 - .saturating_add(Weight::from_ref_time(963_344).saturating_mul(r.into())) + .saturating_add(Weight::from_parts(963_344, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 50]`. fn instr_i64xor(r: u32, ) -> Weight { @@ -2206,10 +2206,10 @@ impl WeightInfo for SubstrateWeight { // Measured: `0` // Estimated: `0` // Minimum execution time: 773 nanoseconds. - Weight::from_ref_time(1_082_459) - .saturating_add(Weight::from_proof_size(0)) + Weight::from_parts(1_082_459, 0) + .saturating_add(Weight::from_parts(0, 0)) // Standard Error: 330 - .saturating_add(Weight::from_ref_time(897_077).saturating_mul(r.into())) + .saturating_add(Weight::from_parts(897_077, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 50]`. fn instr_i64shl(r: u32, ) -> Weight { @@ -2217,10 +2217,10 @@ impl WeightInfo for SubstrateWeight { // Measured: `0` // Estimated: `0` // Minimum execution time: 810 nanoseconds. - Weight::from_ref_time(1_325_815) - .saturating_add(Weight::from_proof_size(0)) + Weight::from_parts(1_325_815, 0) + .saturating_add(Weight::from_parts(0, 0)) // Standard Error: 3_352 - .saturating_add(Weight::from_ref_time(899_555).saturating_mul(r.into())) + .saturating_add(Weight::from_parts(899_555, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 50]`. fn instr_i64shrs(r: u32, ) -> Weight { @@ -2228,10 +2228,10 @@ impl WeightInfo for SubstrateWeight { // Measured: `0` // Estimated: `0` // Minimum execution time: 808 nanoseconds. - Weight::from_ref_time(1_085_903) - .saturating_add(Weight::from_proof_size(0)) + Weight::from_parts(1_085_903, 0) + .saturating_add(Weight::from_parts(0, 0)) // Standard Error: 430 - .saturating_add(Weight::from_ref_time(903_249).saturating_mul(r.into())) + .saturating_add(Weight::from_parts(903_249, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 50]`. fn instr_i64shru(r: u32, ) -> Weight { @@ -2239,10 +2239,10 @@ impl WeightInfo for SubstrateWeight { // Measured: `0` // Estimated: `0` // Minimum execution time: 792 nanoseconds. - Weight::from_ref_time(1_091_261) - .saturating_add(Weight::from_proof_size(0)) + Weight::from_parts(1_091_261, 0) + .saturating_add(Weight::from_parts(0, 0)) // Standard Error: 312 - .saturating_add(Weight::from_ref_time(902_245).saturating_mul(r.into())) + .saturating_add(Weight::from_parts(902_245, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 50]`. fn instr_i64rotl(r: u32, ) -> Weight { @@ -2250,10 +2250,10 @@ impl WeightInfo for SubstrateWeight { // Measured: `0` // Estimated: `0` // Minimum execution time: 807 nanoseconds. - Weight::from_ref_time(1_121_052) - .saturating_add(Weight::from_proof_size(0)) + Weight::from_parts(1_121_052, 0) + .saturating_add(Weight::from_parts(0, 0)) // Standard Error: 506 - .saturating_add(Weight::from_ref_time(902_772).saturating_mul(r.into())) + .saturating_add(Weight::from_parts(902_772, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 50]`. fn instr_i64rotr(r: u32, ) -> Weight { @@ -2261,10 +2261,10 @@ impl WeightInfo for SubstrateWeight { // Measured: `0` // Estimated: `0` // Minimum execution time: 823 nanoseconds. - Weight::from_ref_time(1_317_597) - .saturating_add(Weight::from_proof_size(0)) + Weight::from_parts(1_317_597, 0) + .saturating_add(Weight::from_parts(0, 0)) // Standard Error: 6_219 - .saturating_add(Weight::from_ref_time(896_692).saturating_mul(r.into())) + .saturating_add(Weight::from_parts(896_692, 0).saturating_mul(r.into())) } } @@ -2277,8 +2277,8 @@ impl WeightInfo for () { // Measured: `109` // Estimated: `604` // Minimum execution time: 2_591 nanoseconds. - Weight::from_ref_time(2_817_000) - .saturating_add(Weight::from_proof_size(604)) + Weight::from_parts(2_817_000, 0) + .saturating_add(Weight::from_parts(0, 604)) .saturating_add(RocksDbWeight::get().reads(1_u64)) } /// Storage: Skipped Metadata (r:0 w:0) @@ -2289,14 +2289,14 @@ impl WeightInfo for () { // Measured: `481 + k * (69 ±0)` // Estimated: `471 + k * (70 ±0)` // Minimum execution time: 10_190 nanoseconds. - Weight::from_ref_time(6_642_117) - .saturating_add(Weight::from_proof_size(471)) + Weight::from_parts(6_642_117, 0) + .saturating_add(Weight::from_parts(0, 471)) // Standard Error: 992 - .saturating_add(Weight::from_ref_time(919_828).saturating_mul(k.into())) + .saturating_add(Weight::from_parts(919_828, 0).saturating_mul(k.into())) .saturating_add(RocksDbWeight::get().reads(1_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) .saturating_add(RocksDbWeight::get().writes((1_u64).saturating_mul(k.into()))) - .saturating_add(Weight::from_proof_size(70).saturating_mul(k.into())) + .saturating_add(Weight::from_parts(0, 70).saturating_mul(k.into())) } /// Storage: Contracts DeletionQueue (r:1 w:1) /// Proof: Contracts DeletionQueue (max_values: Some(1), max_size: Some(16642), added: 17137, mode: Measured) @@ -2306,13 +2306,13 @@ impl WeightInfo for () { // Measured: `281 + q * (33 ±0)` // Estimated: `763 + q * (33 ±0)` // Minimum execution time: 2_598 nanoseconds. - Weight::from_ref_time(10_288_252) - .saturating_add(Weight::from_proof_size(763)) + Weight::from_parts(10_288_252, 0) + .saturating_add(Weight::from_parts(0, 763)) // Standard Error: 2_886 - .saturating_add(Weight::from_ref_time(1_092_420).saturating_mul(q.into())) + .saturating_add(Weight::from_parts(1_092_420, 0).saturating_mul(q.into())) .saturating_add(RocksDbWeight::get().reads(1_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) - .saturating_add(Weight::from_proof_size(33).saturating_mul(q.into())) + .saturating_add(Weight::from_parts(0, 33).saturating_mul(q.into())) } /// Storage: Contracts PristineCode (r:1 w:0) /// Proof: Contracts PristineCode (max_values: None, max_size: Some(125988), added: 128463, mode: Measured) @@ -2324,13 +2324,13 @@ impl WeightInfo for () { // Measured: `270 + c * (1 ±0)` // Estimated: `3025 + c * (2 ±0)` // Minimum execution time: 34_338 nanoseconds. - Weight::from_ref_time(32_159_677) - .saturating_add(Weight::from_proof_size(3025)) + Weight::from_parts(32_159_677, 0) + .saturating_add(Weight::from_parts(0, 3025)) // Standard Error: 53 - .saturating_add(Weight::from_ref_time(51_034).saturating_mul(c.into())) + .saturating_add(Weight::from_parts(51_034, 0).saturating_mul(c.into())) .saturating_add(RocksDbWeight::get().reads(1_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) - .saturating_add(Weight::from_proof_size(2).saturating_mul(c.into())) + .saturating_add(Weight::from_parts(0, 2).saturating_mul(c.into())) } /// Storage: Contracts ContractInfoOf (r:1 w:1) /// Proof: Contracts ContractInfoOf (max_values: None, max_size: Some(290), added: 2765, mode: Measured) @@ -2348,13 +2348,13 @@ impl WeightInfo for () { // Measured: `803` // Estimated: `16930 + c * (5 ±0)` // Minimum execution time: 385_587 nanoseconds. - Weight::from_ref_time(395_545_811) - .saturating_add(Weight::from_proof_size(16930)) + Weight::from_parts(395_545_811, 0) + .saturating_add(Weight::from_parts(0, 16930)) // Standard Error: 27 - .saturating_add(Weight::from_ref_time(31_342).saturating_mul(c.into())) + .saturating_add(Weight::from_parts(31_342, 0).saturating_mul(c.into())) .saturating_add(RocksDbWeight::get().reads(6_u64)) .saturating_add(RocksDbWeight::get().writes(4_u64)) - .saturating_add(Weight::from_proof_size(5).saturating_mul(c.into())) + .saturating_add(Weight::from_parts(0, 5).saturating_mul(c.into())) } /// Storage: Contracts OwnerInfoOf (r:1 w:1) /// Proof: Contracts OwnerInfoOf (max_values: None, max_size: Some(88), added: 2563, mode: Measured) @@ -2380,14 +2380,14 @@ impl WeightInfo for () { // Measured: `270` // Estimated: `20267` // Minimum execution time: 3_799_742 nanoseconds. - Weight::from_ref_time(670_115_588) - .saturating_add(Weight::from_proof_size(20267)) + Weight::from_parts(670_115_588, 0) + .saturating_add(Weight::from_parts(0, 20267)) // Standard Error: 287 - .saturating_add(Weight::from_ref_time(93_885).saturating_mul(c.into())) + .saturating_add(Weight::from_parts(93_885, 0).saturating_mul(c.into())) // Standard Error: 16 - .saturating_add(Weight::from_ref_time(1_367).saturating_mul(i.into())) + .saturating_add(Weight::from_parts(1_367, 0).saturating_mul(i.into())) // Standard Error: 16 - .saturating_add(Weight::from_ref_time(1_781).saturating_mul(s.into())) + .saturating_add(Weight::from_parts(1_781, 0).saturating_mul(s.into())) .saturating_add(RocksDbWeight::get().reads(9_u64)) .saturating_add(RocksDbWeight::get().writes(10_u64)) } @@ -2412,12 +2412,12 @@ impl WeightInfo for () { // Measured: `546` // Estimated: `22039` // Minimum execution time: 1_949_008 nanoseconds. - Weight::from_ref_time(214_033_418) - .saturating_add(Weight::from_proof_size(22039)) + Weight::from_parts(214_033_418, 0) + .saturating_add(Weight::from_parts(0, 22039)) // Standard Error: 8 - .saturating_add(Weight::from_ref_time(1_666).saturating_mul(i.into())) + .saturating_add(Weight::from_parts(1_666, 0).saturating_mul(i.into())) // Standard Error: 8 - .saturating_add(Weight::from_ref_time(1_801).saturating_mul(s.into())) + .saturating_add(Weight::from_parts(1_801, 0).saturating_mul(s.into())) .saturating_add(RocksDbWeight::get().reads(9_u64)) .saturating_add(RocksDbWeight::get().writes(7_u64)) } @@ -2436,8 +2436,8 @@ impl WeightInfo for () { // Measured: `855` // Estimated: `17145` // Minimum execution time: 146_654 nanoseconds. - Weight::from_ref_time(147_528_000) - .saturating_add(Weight::from_proof_size(17145)) + Weight::from_parts(147_528_000, 0) + .saturating_add(Weight::from_parts(0, 17145)) .saturating_add(RocksDbWeight::get().reads(6_u64)) .saturating_add(RocksDbWeight::get().writes(4_u64)) } @@ -2455,10 +2455,10 @@ impl WeightInfo for () { // Measured: `109` // Estimated: `5386` // Minimum execution time: 387_889 nanoseconds. - Weight::from_ref_time(391_379_335) - .saturating_add(Weight::from_proof_size(5386)) + Weight::from_parts(391_379_335, 0) + .saturating_add(Weight::from_parts(0, 5386)) // Standard Error: 89 - .saturating_add(Weight::from_ref_time(94_810).saturating_mul(c.into())) + .saturating_add(Weight::from_parts(94_810, 0).saturating_mul(c.into())) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().writes(4_u64)) } @@ -2475,8 +2475,8 @@ impl WeightInfo for () { // Measured: `287` // Estimated: `6098` // Minimum execution time: 26_014 nanoseconds. - Weight::from_ref_time(26_510_000) - .saturating_add(Weight::from_proof_size(6098)) + Weight::from_parts(26_510_000, 0) + .saturating_add(Weight::from_parts(0, 6098)) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().writes(4_u64)) } @@ -2491,8 +2491,8 @@ impl WeightInfo for () { // Measured: `666` // Estimated: `16848` // Minimum execution time: 30_177 nanoseconds. - Weight::from_ref_time(30_639_000) - .saturating_add(Weight::from_proof_size(16848)) + Weight::from_parts(30_639_000, 0) + .saturating_add(Weight::from_parts(0, 16848)) .saturating_add(RocksDbWeight::get().reads(6_u64)) .saturating_add(RocksDbWeight::get().writes(6_u64)) } @@ -2512,13 +2512,13 @@ impl WeightInfo for () { // Measured: `877 + r * (480 ±0)` // Estimated: `17295 + r * (2400 ±0)` // Minimum execution time: 373_786 nanoseconds. - Weight::from_ref_time(377_332_691) - .saturating_add(Weight::from_proof_size(17295)) + Weight::from_parts(377_332_691, 0) + .saturating_add(Weight::from_parts(0, 17295)) // Standard Error: 51_211 - .saturating_add(Weight::from_ref_time(17_715_615).saturating_mul(r.into())) + .saturating_add(Weight::from_parts(17_715_615, 0).saturating_mul(r.into())) .saturating_add(RocksDbWeight::get().reads(6_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) - .saturating_add(Weight::from_proof_size(2400).saturating_mul(r.into())) + .saturating_add(Weight::from_parts(0, 2400).saturating_mul(r.into())) } /// Storage: System Account (r:1 w:0) /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: Measured) @@ -2536,14 +2536,14 @@ impl WeightInfo for () { // Measured: `917 + r * (21778 ±0)` // Estimated: `17295 + r * (306895 ±0)` // Minimum execution time: 374_009 nanoseconds. - Weight::from_ref_time(238_991_986) - .saturating_add(Weight::from_proof_size(17295)) + Weight::from_parts(238_991_986, 0) + .saturating_add(Weight::from_parts(0, 17295)) // Standard Error: 464_711 - .saturating_add(Weight::from_ref_time(249_099_538).saturating_mul(r.into())) + .saturating_add(Weight::from_parts(249_099_538, 0).saturating_mul(r.into())) .saturating_add(RocksDbWeight::get().reads(6_u64)) .saturating_add(RocksDbWeight::get().reads((80_u64).saturating_mul(r.into()))) .saturating_add(RocksDbWeight::get().writes(3_u64)) - .saturating_add(Weight::from_proof_size(306895).saturating_mul(r.into())) + .saturating_add(Weight::from_parts(0, 306895).saturating_mul(r.into())) } /// Storage: System Account (r:1 w:0) /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: Measured) @@ -2561,14 +2561,14 @@ impl WeightInfo for () { // Measured: `921 + r * (22099 ±0)` // Estimated: `17340 + r * (308500 ±0)` // Minimum execution time: 375_058 nanoseconds. - Weight::from_ref_time(238_765_068) - .saturating_add(Weight::from_proof_size(17340)) + Weight::from_parts(238_765_068, 0) + .saturating_add(Weight::from_parts(0, 17340)) // Standard Error: 662_617 - .saturating_add(Weight::from_ref_time(302_175_089).saturating_mul(r.into())) + .saturating_add(Weight::from_parts(302_175_089, 0).saturating_mul(r.into())) .saturating_add(RocksDbWeight::get().reads(6_u64)) .saturating_add(RocksDbWeight::get().reads((80_u64).saturating_mul(r.into()))) .saturating_add(RocksDbWeight::get().writes(3_u64)) - .saturating_add(Weight::from_proof_size(308500).saturating_mul(r.into())) + .saturating_add(Weight::from_parts(0, 308500).saturating_mul(r.into())) } /// Storage: System Account (r:1 w:0) /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: Measured) @@ -2586,13 +2586,13 @@ impl WeightInfo for () { // Measured: `884 + r * (480 ±0)` // Estimated: `17330 + r * (2400 ±0)` // Minimum execution time: 374_747 nanoseconds. - Weight::from_ref_time(376_482_380) - .saturating_add(Weight::from_proof_size(17330)) + Weight::from_parts(376_482_380, 0) + .saturating_add(Weight::from_parts(0, 17330)) // Standard Error: 61_919 - .saturating_add(Weight::from_ref_time(22_376_795).saturating_mul(r.into())) + .saturating_add(Weight::from_parts(22_376_795, 0).saturating_mul(r.into())) .saturating_add(RocksDbWeight::get().reads(6_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) - .saturating_add(Weight::from_proof_size(2400).saturating_mul(r.into())) + .saturating_add(Weight::from_parts(0, 2400).saturating_mul(r.into())) } /// Storage: System Account (r:1 w:0) /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: Measured) @@ -2610,13 +2610,13 @@ impl WeightInfo for () { // Measured: `874 + r * (240 ±0)` // Estimated: `17265 + r * (1200 ±0)` // Minimum execution time: 372_287 nanoseconds. - Weight::from_ref_time(376_250_858) - .saturating_add(Weight::from_proof_size(17265)) + Weight::from_parts(376_250_858, 0) + .saturating_add(Weight::from_parts(0, 17265)) // Standard Error: 40_119 - .saturating_add(Weight::from_ref_time(11_359_647).saturating_mul(r.into())) + .saturating_add(Weight::from_parts(11_359_647, 0).saturating_mul(r.into())) .saturating_add(RocksDbWeight::get().reads(6_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) - .saturating_add(Weight::from_proof_size(1200).saturating_mul(r.into())) + .saturating_add(Weight::from_parts(0, 1200).saturating_mul(r.into())) } /// Storage: System Account (r:1 w:0) /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: Measured) @@ -2634,13 +2634,13 @@ impl WeightInfo for () { // Measured: `878 + r * (480 ±0)` // Estimated: `17260 + r * (2400 ±0)` // Minimum execution time: 374_445 nanoseconds. - Weight::from_ref_time(377_243_521) - .saturating_add(Weight::from_proof_size(17260)) + Weight::from_parts(377_243_521, 0) + .saturating_add(Weight::from_parts(0, 17260)) // Standard Error: 53_032 - .saturating_add(Weight::from_ref_time(17_684_246).saturating_mul(r.into())) + .saturating_add(Weight::from_parts(17_684_246, 0).saturating_mul(r.into())) .saturating_add(RocksDbWeight::get().reads(6_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) - .saturating_add(Weight::from_proof_size(2400).saturating_mul(r.into())) + .saturating_add(Weight::from_parts(0, 2400).saturating_mul(r.into())) } /// Storage: System Account (r:1 w:0) /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: Measured) @@ -2658,13 +2658,13 @@ impl WeightInfo for () { // Measured: `879 + r * (480 ±0)` // Estimated: `17250 + r * (2405 ±0)` // Minimum execution time: 374_029 nanoseconds. - Weight::from_ref_time(380_415_186) - .saturating_add(Weight::from_proof_size(17250)) + Weight::from_parts(380_415_186, 0) + .saturating_add(Weight::from_parts(0, 17250)) // Standard Error: 60_562 - .saturating_add(Weight::from_ref_time(17_152_599).saturating_mul(r.into())) + .saturating_add(Weight::from_parts(17_152_599, 0).saturating_mul(r.into())) .saturating_add(RocksDbWeight::get().reads(6_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) - .saturating_add(Weight::from_proof_size(2405).saturating_mul(r.into())) + .saturating_add(Weight::from_parts(0, 2405).saturating_mul(r.into())) } /// Storage: System Account (r:2 w:0) /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: Measured) @@ -2682,13 +2682,13 @@ impl WeightInfo for () { // Measured: `1049 + r * (480 ±0)` // Estimated: `19849 + r * (2456 ±0)` // Minimum execution time: 373_999 nanoseconds. - Weight::from_ref_time(381_757_033) - .saturating_add(Weight::from_proof_size(19849)) + Weight::from_parts(381_757_033, 0) + .saturating_add(Weight::from_parts(0, 19849)) // Standard Error: 97_983 - .saturating_add(Weight::from_ref_time(98_290_984).saturating_mul(r.into())) + .saturating_add(Weight::from_parts(98_290_984, 0).saturating_mul(r.into())) .saturating_add(RocksDbWeight::get().reads(7_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) - .saturating_add(Weight::from_proof_size(2456).saturating_mul(r.into())) + .saturating_add(Weight::from_parts(0, 2456).saturating_mul(r.into())) } /// Storage: System Account (r:1 w:0) /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: Measured) @@ -2706,13 +2706,13 @@ impl WeightInfo for () { // Measured: `888 + r * (480 ±0)` // Estimated: `17360 + r * (2400 ±0)` // Minimum execution time: 374_197 nanoseconds. - Weight::from_ref_time(377_755_896) - .saturating_add(Weight::from_proof_size(17360)) + Weight::from_parts(377_755_896, 0) + .saturating_add(Weight::from_parts(0, 17360)) // Standard Error: 60_542 - .saturating_add(Weight::from_ref_time(17_442_065).saturating_mul(r.into())) + .saturating_add(Weight::from_parts(17_442_065, 0).saturating_mul(r.into())) .saturating_add(RocksDbWeight::get().reads(6_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) - .saturating_add(Weight::from_proof_size(2400).saturating_mul(r.into())) + .saturating_add(Weight::from_parts(0, 2400).saturating_mul(r.into())) } /// Storage: System Account (r:1 w:0) /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: Measured) @@ -2730,13 +2730,13 @@ impl WeightInfo for () { // Measured: `886 + r * (480 ±0)` // Estimated: `17290 + r * (2400 ±0)` // Minimum execution time: 373_888 nanoseconds. - Weight::from_ref_time(377_825_771) - .saturating_add(Weight::from_proof_size(17290)) + Weight::from_parts(377_825_771, 0) + .saturating_add(Weight::from_parts(0, 17290)) // Standard Error: 38_026 - .saturating_add(Weight::from_ref_time(17_147_903).saturating_mul(r.into())) + .saturating_add(Weight::from_parts(17_147_903, 0).saturating_mul(r.into())) .saturating_add(RocksDbWeight::get().reads(6_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) - .saturating_add(Weight::from_proof_size(2400).saturating_mul(r.into())) + .saturating_add(Weight::from_parts(0, 2400).saturating_mul(r.into())) } /// Storage: System Account (r:1 w:0) /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: Measured) @@ -2754,13 +2754,13 @@ impl WeightInfo for () { // Measured: `883 + r * (480 ±0)` // Estimated: `17315 + r * (2400 ±0)` // Minimum execution time: 373_904 nanoseconds. - Weight::from_ref_time(378_652_372) - .saturating_add(Weight::from_proof_size(17315)) + Weight::from_parts(378_652_372, 0) + .saturating_add(Weight::from_parts(0, 17315)) // Standard Error: 43_833 - .saturating_add(Weight::from_ref_time(16_936_781).saturating_mul(r.into())) + .saturating_add(Weight::from_parts(16_936_781, 0).saturating_mul(r.into())) .saturating_add(RocksDbWeight::get().reads(6_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) - .saturating_add(Weight::from_proof_size(2400).saturating_mul(r.into())) + .saturating_add(Weight::from_parts(0, 2400).saturating_mul(r.into())) } /// Storage: System Account (r:1 w:0) /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: Measured) @@ -2778,13 +2778,13 @@ impl WeightInfo for () { // Measured: `874 + r * (480 ±0)` // Estimated: `17245 + r * (2400 ±0)` // Minimum execution time: 373_473 nanoseconds. - Weight::from_ref_time(376_386_312) - .saturating_add(Weight::from_proof_size(17245)) + Weight::from_parts(376_386_312, 0) + .saturating_add(Weight::from_parts(0, 17245)) // Standard Error: 46_945 - .saturating_add(Weight::from_ref_time(17_336_462).saturating_mul(r.into())) + .saturating_add(Weight::from_parts(17_336_462, 0).saturating_mul(r.into())) .saturating_add(RocksDbWeight::get().reads(6_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) - .saturating_add(Weight::from_proof_size(2400).saturating_mul(r.into())) + .saturating_add(Weight::from_parts(0, 2400).saturating_mul(r.into())) } /// Storage: System Account (r:1 w:0) /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: Measured) @@ -2804,13 +2804,13 @@ impl WeightInfo for () { // Measured: `951 + r * (800 ±0)` // Estimated: `19046 + r * (4805 ±0)` // Minimum execution time: 373_661 nanoseconds. - Weight::from_ref_time(385_824_015) - .saturating_add(Weight::from_proof_size(19046)) + Weight::from_parts(385_824_015, 0) + .saturating_add(Weight::from_parts(0, 19046)) // Standard Error: 75_964 - .saturating_add(Weight::from_ref_time(88_530_074).saturating_mul(r.into())) + .saturating_add(Weight::from_parts(88_530_074, 0).saturating_mul(r.into())) .saturating_add(RocksDbWeight::get().reads(7_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) - .saturating_add(Weight::from_proof_size(4805).saturating_mul(r.into())) + .saturating_add(Weight::from_parts(0, 4805).saturating_mul(r.into())) } /// Storage: System Account (r:1 w:0) /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: Measured) @@ -2828,13 +2828,13 @@ impl WeightInfo for () { // Measured: `841 + r * (320 ±0)` // Estimated: `17120 + r * (1600 ±0)` // Minimum execution time: 133_849 nanoseconds. - Weight::from_ref_time(137_283_391) - .saturating_add(Weight::from_proof_size(17120)) + Weight::from_parts(137_283_391, 0) + .saturating_add(Weight::from_parts(0, 17120)) // Standard Error: 13_312 - .saturating_add(Weight::from_ref_time(8_055_328).saturating_mul(r.into())) + .saturating_add(Weight::from_parts(8_055_328, 0).saturating_mul(r.into())) .saturating_add(RocksDbWeight::get().reads(6_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) - .saturating_add(Weight::from_proof_size(1600).saturating_mul(r.into())) + .saturating_add(Weight::from_parts(0, 1600).saturating_mul(r.into())) } /// Storage: System Account (r:1 w:0) /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: Measured) @@ -2852,13 +2852,13 @@ impl WeightInfo for () { // Measured: `876 + r * (480 ±0)` // Estimated: `17245 + r * (2400 ±0)` // Minimum execution time: 373_468 nanoseconds. - Weight::from_ref_time(376_121_093) - .saturating_add(Weight::from_proof_size(17245)) + Weight::from_parts(376_121_093, 0) + .saturating_add(Weight::from_parts(0, 17245)) // Standard Error: 61_857 - .saturating_add(Weight::from_ref_time(15_868_414).saturating_mul(r.into())) + .saturating_add(Weight::from_parts(15_868_414, 0).saturating_mul(r.into())) .saturating_add(RocksDbWeight::get().reads(6_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) - .saturating_add(Weight::from_proof_size(2400).saturating_mul(r.into())) + .saturating_add(Weight::from_parts(0, 2400).saturating_mul(r.into())) } /// Storage: System Account (r:1 w:0) /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: Measured) @@ -2876,10 +2876,10 @@ impl WeightInfo for () { // Measured: `1356` // Estimated: `19650` // Minimum execution time: 390_668 nanoseconds. - Weight::from_ref_time(419_608_449) - .saturating_add(Weight::from_proof_size(19650)) + Weight::from_parts(419_608_449, 0) + .saturating_add(Weight::from_parts(0, 19650)) // Standard Error: 4_890 - .saturating_add(Weight::from_ref_time(9_672_288).saturating_mul(n.into())) + .saturating_add(Weight::from_parts(9_672_288, 0).saturating_mul(n.into())) .saturating_add(RocksDbWeight::get().reads(6_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) } @@ -2899,13 +2899,13 @@ impl WeightInfo for () { // Measured: `864 + r * (45 ±0)` // Estimated: `17190 + r * (225 ±0)` // Minimum execution time: 371_309 nanoseconds. - Weight::from_ref_time(373_625_402) - .saturating_add(Weight::from_proof_size(17190)) + Weight::from_parts(373_625_402, 0) + .saturating_add(Weight::from_parts(0, 17190)) // Standard Error: 419_605 - .saturating_add(Weight::from_ref_time(1_737_397).saturating_mul(r.into())) + .saturating_add(Weight::from_parts(1_737_397, 0).saturating_mul(r.into())) .saturating_add(RocksDbWeight::get().reads(6_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) - .saturating_add(Weight::from_proof_size(225).saturating_mul(r.into())) + .saturating_add(Weight::from_parts(0, 225).saturating_mul(r.into())) } /// Storage: System Account (r:1 w:0) /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: Measured) @@ -2923,10 +2923,10 @@ impl WeightInfo for () { // Measured: `874` // Estimated: `17285` // Minimum execution time: 374_094 nanoseconds. - Weight::from_ref_time(375_965_200) - .saturating_add(Weight::from_proof_size(17285)) + Weight::from_parts(375_965_200, 0) + .saturating_add(Weight::from_parts(0, 17285)) // Standard Error: 1_127 - .saturating_add(Weight::from_ref_time(232_645).saturating_mul(n.into())) + .saturating_add(Weight::from_parts(232_645, 0).saturating_mul(n.into())) .saturating_add(RocksDbWeight::get().reads(6_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) } @@ -2950,15 +2950,15 @@ impl WeightInfo for () { // Measured: `906 + r * (452 ±0)` // Estimated: `20242 + r * (15004 ±0)` // Minimum execution time: 373_123 nanoseconds. - Weight::from_ref_time(374_924_634) - .saturating_add(Weight::from_proof_size(20242)) + Weight::from_parts(374_924_634, 0) + .saturating_add(Weight::from_parts(0, 20242)) // Standard Error: 378_010 - .saturating_add(Weight::from_ref_time(70_441_665).saturating_mul(r.into())) + .saturating_add(Weight::from_parts(70_441_665, 0).saturating_mul(r.into())) .saturating_add(RocksDbWeight::get().reads(6_u64)) .saturating_add(RocksDbWeight::get().reads((6_u64).saturating_mul(r.into()))) .saturating_add(RocksDbWeight::get().writes(3_u64)) .saturating_add(RocksDbWeight::get().writes((7_u64).saturating_mul(r.into()))) - .saturating_add(Weight::from_proof_size(15004).saturating_mul(r.into())) + .saturating_add(Weight::from_parts(0, 15004).saturating_mul(r.into())) } /// Storage: System Account (r:1 w:0) /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: Measured) @@ -2978,13 +2978,13 @@ impl WeightInfo for () { // Measured: `921 + r * (800 ±0)` // Estimated: `18835 + r * (4805 ±0)` // Minimum execution time: 373_291 nanoseconds. - Weight::from_ref_time(385_684_344) - .saturating_add(Weight::from_proof_size(18835)) + Weight::from_parts(385_684_344, 0) + .saturating_add(Weight::from_parts(0, 18835)) // Standard Error: 99_025 - .saturating_add(Weight::from_ref_time(111_308_793).saturating_mul(r.into())) + .saturating_add(Weight::from_parts(111_308_793, 0).saturating_mul(r.into())) .saturating_add(RocksDbWeight::get().reads(7_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) - .saturating_add(Weight::from_proof_size(4805).saturating_mul(r.into())) + .saturating_add(Weight::from_parts(0, 4805).saturating_mul(r.into())) } /// Storage: System Account (r:1 w:0) /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: Measured) @@ -3002,13 +3002,13 @@ impl WeightInfo for () { // Measured: `874 + r * (800 ±0)` // Estimated: `17250 + r * (4000 ±0)` // Minimum execution time: 371_900 nanoseconds. - Weight::from_ref_time(384_166_626) - .saturating_add(Weight::from_proof_size(17250)) + Weight::from_parts(384_166_626, 0) + .saturating_add(Weight::from_parts(0, 17250)) // Standard Error: 205_255 - .saturating_add(Weight::from_ref_time(229_214_157).saturating_mul(r.into())) + .saturating_add(Weight::from_parts(229_214_157, 0).saturating_mul(r.into())) .saturating_add(RocksDbWeight::get().reads(6_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) - .saturating_add(Weight::from_proof_size(4000).saturating_mul(r.into())) + .saturating_add(Weight::from_parts(0, 4000).saturating_mul(r.into())) } /// Storage: System Account (r:1 w:0) /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: Measured) @@ -3027,18 +3027,18 @@ impl WeightInfo for () { // Measured: `1821 + t * (2608 ±0) + n * (7 ±0)` // Estimated: `21870 + t * (211030 ±0) + n * (50 ±0)` // Minimum execution time: 1_289_873 nanoseconds. - Weight::from_ref_time(581_702_206) - .saturating_add(Weight::from_proof_size(21870)) + Weight::from_parts(581_702_206, 0) + .saturating_add(Weight::from_parts(0, 21870)) // Standard Error: 665_638 - .saturating_add(Weight::from_ref_time(181_470_553).saturating_mul(t.into())) + .saturating_add(Weight::from_parts(181_470_553, 0).saturating_mul(t.into())) // Standard Error: 182_816 - .saturating_add(Weight::from_ref_time(71_635_250).saturating_mul(n.into())) + .saturating_add(Weight::from_parts(71_635_250, 0).saturating_mul(n.into())) .saturating_add(RocksDbWeight::get().reads(6_u64)) .saturating_add(RocksDbWeight::get().reads((80_u64).saturating_mul(t.into()))) .saturating_add(RocksDbWeight::get().writes(3_u64)) .saturating_add(RocksDbWeight::get().writes((80_u64).saturating_mul(t.into()))) - .saturating_add(Weight::from_proof_size(211030).saturating_mul(t.into())) - .saturating_add(Weight::from_proof_size(50).saturating_mul(n.into())) + .saturating_add(Weight::from_parts(0, 211030).saturating_mul(t.into())) + .saturating_add(Weight::from_parts(0, 50).saturating_mul(n.into())) } /// Storage: System Account (r:1 w:0) /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: Measured) @@ -3056,13 +3056,13 @@ impl WeightInfo for () { // Measured: `873 + r * (560 ±0)` // Estimated: `17240 + r * (2800 ±0)` // Minimum execution time: 148_635 nanoseconds. - Weight::from_ref_time(154_095_712) - .saturating_add(Weight::from_proof_size(17240)) + Weight::from_parts(154_095_712, 0) + .saturating_add(Weight::from_parts(0, 17240)) // Standard Error: 77_790 - .saturating_add(Weight::from_ref_time(14_837_085).saturating_mul(r.into())) + .saturating_add(Weight::from_parts(14_837_085, 0).saturating_mul(r.into())) .saturating_add(RocksDbWeight::get().reads(6_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) - .saturating_add(Weight::from_proof_size(2800).saturating_mul(r.into())) + .saturating_add(Weight::from_parts(0, 2800).saturating_mul(r.into())) } /// Storage: System Account (r:1 w:0) /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) @@ -3080,10 +3080,10 @@ impl WeightInfo for () { // Measured: `125824` // Estimated: `265128` // Minimum execution time: 501_014 nanoseconds. - Weight::from_ref_time(505_634_218) - .saturating_add(Weight::from_proof_size(265128)) + Weight::from_parts(505_634_218, 0) + .saturating_add(Weight::from_parts(0, 265128)) // Standard Error: 2_441 - .saturating_add(Weight::from_ref_time(819_257).saturating_mul(i.into())) + .saturating_add(Weight::from_parts(819_257, 0).saturating_mul(i.into())) .saturating_add(RocksDbWeight::get().reads(6_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) } @@ -3095,15 +3095,15 @@ impl WeightInfo for () { // Measured: `911 + r * (23420 ±0)` // Estimated: `911 + r * (23418 ±0)` // Minimum execution time: 375_301 nanoseconds. - Weight::from_ref_time(291_498_841) - .saturating_add(Weight::from_proof_size(911)) + Weight::from_parts(291_498_841, 0) + .saturating_add(Weight::from_parts(0, 911)) // Standard Error: 809_989 - .saturating_add(Weight::from_ref_time(464_550_291).saturating_mul(r.into())) + .saturating_add(Weight::from_parts(464_550_291, 0).saturating_mul(r.into())) .saturating_add(RocksDbWeight::get().reads(6_u64)) .saturating_add(RocksDbWeight::get().reads((80_u64).saturating_mul(r.into()))) .saturating_add(RocksDbWeight::get().writes(3_u64)) .saturating_add(RocksDbWeight::get().writes((80_u64).saturating_mul(r.into()))) - .saturating_add(Weight::from_proof_size(23418).saturating_mul(r.into())) + .saturating_add(Weight::from_parts(0, 23418).saturating_mul(r.into())) } /// Storage: Skipped Metadata (r:0 w:0) /// Proof Skipped: Skipped Metadata (max_values: None, max_size: None, mode: Measured) @@ -3113,15 +3113,15 @@ impl WeightInfo for () { // Measured: `12672 + n * (11945 ±0)` // Estimated: `8529 + n * (12814 ±61)` // Minimum execution time: 506_318 nanoseconds. - Weight::from_ref_time(676_935_313) - .saturating_add(Weight::from_proof_size(8529)) + Weight::from_parts(676_935_313, 0) + .saturating_add(Weight::from_parts(0, 8529)) // Standard Error: 1_589_291 - .saturating_add(Weight::from_ref_time(97_839_399).saturating_mul(n.into())) + .saturating_add(Weight::from_parts(97_839_399, 0).saturating_mul(n.into())) .saturating_add(RocksDbWeight::get().reads(52_u64)) .saturating_add(RocksDbWeight::get().reads((7_u64).saturating_mul(n.into()))) .saturating_add(RocksDbWeight::get().writes(50_u64)) .saturating_add(RocksDbWeight::get().writes((7_u64).saturating_mul(n.into()))) - .saturating_add(Weight::from_proof_size(12814).saturating_mul(n.into())) + .saturating_add(Weight::from_parts(0, 12814).saturating_mul(n.into())) } /// Storage: Skipped Metadata (r:0 w:0) /// Proof Skipped: Skipped Metadata (max_values: None, max_size: None, mode: Measured) @@ -3131,15 +3131,15 @@ impl WeightInfo for () { // Measured: `15170 + n * (175775 ±0)` // Estimated: `9914 + n * (176858 ±74)` // Minimum execution time: 506_148 nanoseconds. - Weight::from_ref_time(648_278_778) - .saturating_add(Weight::from_proof_size(9914)) + Weight::from_parts(648_278_778, 0) + .saturating_add(Weight::from_parts(0, 9914)) // Standard Error: 1_343_586 - .saturating_add(Weight::from_ref_time(65_789_595).saturating_mul(n.into())) + .saturating_add(Weight::from_parts(65_789_595, 0).saturating_mul(n.into())) .saturating_add(RocksDbWeight::get().reads(51_u64)) .saturating_add(RocksDbWeight::get().reads((7_u64).saturating_mul(n.into()))) .saturating_add(RocksDbWeight::get().writes(49_u64)) .saturating_add(RocksDbWeight::get().writes((7_u64).saturating_mul(n.into()))) - .saturating_add(Weight::from_proof_size(176858).saturating_mul(n.into())) + .saturating_add(Weight::from_parts(0, 176858).saturating_mul(n.into())) } /// Storage: Skipped Metadata (r:0 w:0) /// Proof Skipped: Skipped Metadata (max_values: None, max_size: None, mode: Measured) @@ -3149,15 +3149,15 @@ impl WeightInfo for () { // Measured: `903 + r * (23099 ±0)` // Estimated: `908 + r * (23099 ±0)` // Minimum execution time: 374_344 nanoseconds. - Weight::from_ref_time(293_272_061) - .saturating_add(Weight::from_proof_size(908)) + Weight::from_parts(293_272_061, 0) + .saturating_add(Weight::from_parts(0, 908)) // Standard Error: 810_412 - .saturating_add(Weight::from_ref_time(453_315_956).saturating_mul(r.into())) + .saturating_add(Weight::from_parts(453_315_956, 0).saturating_mul(r.into())) .saturating_add(RocksDbWeight::get().reads(6_u64)) .saturating_add(RocksDbWeight::get().reads((80_u64).saturating_mul(r.into()))) .saturating_add(RocksDbWeight::get().writes(3_u64)) .saturating_add(RocksDbWeight::get().writes((80_u64).saturating_mul(r.into()))) - .saturating_add(Weight::from_proof_size(23099).saturating_mul(r.into())) + .saturating_add(Weight::from_parts(0, 23099).saturating_mul(r.into())) } /// Storage: Skipped Metadata (r:0 w:0) /// Proof Skipped: Skipped Metadata (max_values: None, max_size: None, mode: Measured) @@ -3167,15 +3167,15 @@ impl WeightInfo for () { // Measured: `14895 + n * (175768 ±0)` // Estimated: `9551 + n * (176867 ±75)` // Minimum execution time: 478_564 nanoseconds. - Weight::from_ref_time(630_839_142) - .saturating_add(Weight::from_proof_size(9551)) + Weight::from_parts(630_839_142, 0) + .saturating_add(Weight::from_parts(0, 9551)) // Standard Error: 1_427_520 - .saturating_add(Weight::from_ref_time(66_813_592).saturating_mul(n.into())) + .saturating_add(Weight::from_parts(66_813_592, 0).saturating_mul(n.into())) .saturating_add(RocksDbWeight::get().reads(51_u64)) .saturating_add(RocksDbWeight::get().reads((7_u64).saturating_mul(n.into()))) .saturating_add(RocksDbWeight::get().writes(48_u64)) .saturating_add(RocksDbWeight::get().writes((7_u64).saturating_mul(n.into()))) - .saturating_add(Weight::from_proof_size(176867).saturating_mul(n.into())) + .saturating_add(Weight::from_parts(0, 176867).saturating_mul(n.into())) } /// Storage: Skipped Metadata (r:0 w:0) /// Proof Skipped: Skipped Metadata (max_values: None, max_size: None, mode: Measured) @@ -3185,14 +3185,14 @@ impl WeightInfo for () { // Measured: `896 + r * (23744 ±0)` // Estimated: `909 + r * (23740 ±0)` // Minimum execution time: 374_479 nanoseconds. - Weight::from_ref_time(311_839_315) - .saturating_add(Weight::from_proof_size(909)) + Weight::from_parts(311_839_315, 0) + .saturating_add(Weight::from_parts(0, 909)) // Standard Error: 666_553 - .saturating_add(Weight::from_ref_time(371_213_042).saturating_mul(r.into())) + .saturating_add(Weight::from_parts(371_213_042, 0).saturating_mul(r.into())) .saturating_add(RocksDbWeight::get().reads(6_u64)) .saturating_add(RocksDbWeight::get().reads((80_u64).saturating_mul(r.into()))) .saturating_add(RocksDbWeight::get().writes(3_u64)) - .saturating_add(Weight::from_proof_size(23740).saturating_mul(r.into())) + .saturating_add(Weight::from_parts(0, 23740).saturating_mul(r.into())) } /// Storage: Skipped Metadata (r:0 w:0) /// Proof Skipped: Skipped Metadata (max_values: None, max_size: None, mode: Measured) @@ -3202,14 +3202,14 @@ impl WeightInfo for () { // Measured: `15501 + n * (175775 ±0)` // Estimated: `10042 + n * (176900 ±76)` // Minimum execution time: 460_639 nanoseconds. - Weight::from_ref_time(591_187_094) - .saturating_add(Weight::from_proof_size(10042)) + Weight::from_parts(591_187_094, 0) + .saturating_add(Weight::from_parts(0, 10042)) // Standard Error: 1_233_792 - .saturating_add(Weight::from_ref_time(160_874_477).saturating_mul(n.into())) + .saturating_add(Weight::from_parts(160_874_477, 0).saturating_mul(n.into())) .saturating_add(RocksDbWeight::get().reads(51_u64)) .saturating_add(RocksDbWeight::get().reads((7_u64).saturating_mul(n.into()))) .saturating_add(RocksDbWeight::get().writes(3_u64)) - .saturating_add(Weight::from_proof_size(176900).saturating_mul(n.into())) + .saturating_add(Weight::from_parts(0, 176900).saturating_mul(n.into())) } /// Storage: Skipped Metadata (r:0 w:0) /// Proof Skipped: Skipped Metadata (max_values: None, max_size: None, mode: Measured) @@ -3219,14 +3219,14 @@ impl WeightInfo for () { // Measured: `914 + r * (23098 ±0)` // Estimated: `920 + r * (23098 ±0)` // Minimum execution time: 374_272 nanoseconds. - Weight::from_ref_time(311_446_269) - .saturating_add(Weight::from_proof_size(920)) + Weight::from_parts(311_446_269, 0) + .saturating_add(Weight::from_parts(0, 920)) // Standard Error: 630_307 - .saturating_add(Weight::from_ref_time(357_134_931).saturating_mul(r.into())) + .saturating_add(Weight::from_parts(357_134_931, 0).saturating_mul(r.into())) .saturating_add(RocksDbWeight::get().reads(6_u64)) .saturating_add(RocksDbWeight::get().reads((80_u64).saturating_mul(r.into()))) .saturating_add(RocksDbWeight::get().writes(3_u64)) - .saturating_add(Weight::from_proof_size(23098).saturating_mul(r.into())) + .saturating_add(Weight::from_parts(0, 23098).saturating_mul(r.into())) } /// Storage: Skipped Metadata (r:0 w:0) /// Proof Skipped: Skipped Metadata (max_values: None, max_size: None, mode: Measured) @@ -3236,14 +3236,14 @@ impl WeightInfo for () { // Measured: `14839 + n * (175789 ±0)` // Estimated: `9532 + n * (176874 ±75)` // Minimum execution time: 456_013 nanoseconds. - Weight::from_ref_time(575_116_352) - .saturating_add(Weight::from_proof_size(9532)) + Weight::from_parts(575_116_352, 0) + .saturating_add(Weight::from_parts(0, 9532)) // Standard Error: 1_122_298 - .saturating_add(Weight::from_ref_time(61_786_107).saturating_mul(n.into())) + .saturating_add(Weight::from_parts(61_786_107, 0).saturating_mul(n.into())) .saturating_add(RocksDbWeight::get().reads(51_u64)) .saturating_add(RocksDbWeight::get().reads((7_u64).saturating_mul(n.into()))) .saturating_add(RocksDbWeight::get().writes(3_u64)) - .saturating_add(Weight::from_proof_size(176874).saturating_mul(n.into())) + .saturating_add(Weight::from_parts(0, 176874).saturating_mul(n.into())) } /// Storage: Skipped Metadata (r:0 w:0) /// Proof Skipped: Skipped Metadata (max_values: None, max_size: None, mode: Measured) @@ -3253,15 +3253,15 @@ impl WeightInfo for () { // Measured: `911 + r * (23740 ±0)` // Estimated: `913 + r * (23739 ±0)` // Minimum execution time: 374_621 nanoseconds. - Weight::from_ref_time(299_689_489) - .saturating_add(Weight::from_proof_size(913)) + Weight::from_parts(299_689_489, 0) + .saturating_add(Weight::from_parts(0, 913)) // Standard Error: 757_735 - .saturating_add(Weight::from_ref_time(465_213_246).saturating_mul(r.into())) + .saturating_add(Weight::from_parts(465_213_246, 0).saturating_mul(r.into())) .saturating_add(RocksDbWeight::get().reads(6_u64)) .saturating_add(RocksDbWeight::get().reads((80_u64).saturating_mul(r.into()))) .saturating_add(RocksDbWeight::get().writes(3_u64)) .saturating_add(RocksDbWeight::get().writes((80_u64).saturating_mul(r.into()))) - .saturating_add(Weight::from_proof_size(23739).saturating_mul(r.into())) + .saturating_add(Weight::from_parts(0, 23739).saturating_mul(r.into())) } /// Storage: Skipped Metadata (r:0 w:0) /// Proof Skipped: Skipped Metadata (max_values: None, max_size: None, mode: Measured) @@ -3271,15 +3271,15 @@ impl WeightInfo for () { // Measured: `15502 + n * (175775 ±0)` // Estimated: `10042 + n * (176898 ±76)` // Minimum execution time: 481_980 nanoseconds. - Weight::from_ref_time(647_289_053) - .saturating_add(Weight::from_proof_size(10042)) + Weight::from_parts(647_289_053, 0) + .saturating_add(Weight::from_parts(0, 10042)) // Standard Error: 1_556_155 - .saturating_add(Weight::from_ref_time(166_592_657).saturating_mul(n.into())) + .saturating_add(Weight::from_parts(166_592_657, 0).saturating_mul(n.into())) .saturating_add(RocksDbWeight::get().reads(51_u64)) .saturating_add(RocksDbWeight::get().reads((7_u64).saturating_mul(n.into()))) .saturating_add(RocksDbWeight::get().writes(48_u64)) .saturating_add(RocksDbWeight::get().writes((7_u64).saturating_mul(n.into()))) - .saturating_add(Weight::from_proof_size(176898).saturating_mul(n.into())) + .saturating_add(Weight::from_parts(0, 176898).saturating_mul(n.into())) } /// Storage: System Account (r:1602 w:1601) /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: Measured) @@ -3297,15 +3297,15 @@ impl WeightInfo for () { // Measured: `1457 + r * (3604 ±0)` // Estimated: `21583 + r * (216101 ±0)` // Minimum execution time: 374_962 nanoseconds. - Weight::from_ref_time(313_416_386) - .saturating_add(Weight::from_proof_size(21583)) + Weight::from_parts(313_416_386, 0) + .saturating_add(Weight::from_parts(0, 21583)) // Standard Error: 710_675 - .saturating_add(Weight::from_ref_time(1_396_551_156).saturating_mul(r.into())) + .saturating_add(Weight::from_parts(1_396_551_156, 0).saturating_mul(r.into())) .saturating_add(RocksDbWeight::get().reads(7_u64)) .saturating_add(RocksDbWeight::get().reads((80_u64).saturating_mul(r.into()))) .saturating_add(RocksDbWeight::get().writes(4_u64)) .saturating_add(RocksDbWeight::get().writes((80_u64).saturating_mul(r.into()))) - .saturating_add(Weight::from_proof_size(216101).saturating_mul(r.into())) + .saturating_add(Weight::from_parts(0, 216101).saturating_mul(r.into())) } /// Storage: System Account (r:1 w:0) /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: Measured) @@ -3323,15 +3323,15 @@ impl WeightInfo for () { // Measured: `1609 + r * (23073 ±0)` // Estimated: `22098 + r * (511456 ±1)` // Minimum execution time: 375_916 nanoseconds. - Weight::from_ref_time(376_468_000) - .saturating_add(Weight::from_proof_size(22098)) + Weight::from_parts(376_468_000, 0) + .saturating_add(Weight::from_parts(0, 22098)) // Standard Error: 7_246_855 - .saturating_add(Weight::from_ref_time(28_982_425_139).saturating_mul(r.into())) + .saturating_add(Weight::from_parts(28_982_425_139, 0).saturating_mul(r.into())) .saturating_add(RocksDbWeight::get().reads(7_u64)) .saturating_add(RocksDbWeight::get().reads((160_u64).saturating_mul(r.into()))) .saturating_add(RocksDbWeight::get().writes(3_u64)) .saturating_add(RocksDbWeight::get().writes((160_u64).saturating_mul(r.into()))) - .saturating_add(Weight::from_proof_size(511456).saturating_mul(r.into())) + .saturating_add(Weight::from_parts(0, 511456).saturating_mul(r.into())) } /// Storage: System Account (r:1 w:0) /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: Measured) @@ -3349,15 +3349,15 @@ impl WeightInfo for () { // Measured: `0 + r * (71670 ±0)` // Estimated: `17285 + r * (659930 ±563)` // Minimum execution time: 375_412 nanoseconds. - Weight::from_ref_time(376_493_000) - .saturating_add(Weight::from_proof_size(17285)) + Weight::from_parts(376_493_000, 0) + .saturating_add(Weight::from_parts(0, 17285)) // Standard Error: 8_239_575 - .saturating_add(Weight::from_ref_time(28_716_347_183).saturating_mul(r.into())) + .saturating_add(Weight::from_parts(28_716_347_183, 0).saturating_mul(r.into())) .saturating_add(RocksDbWeight::get().reads(6_u64)) .saturating_add(RocksDbWeight::get().reads((150_u64).saturating_mul(r.into()))) .saturating_add(RocksDbWeight::get().writes(3_u64)) .saturating_add(RocksDbWeight::get().writes((75_u64).saturating_mul(r.into()))) - .saturating_add(Weight::from_proof_size(659930).saturating_mul(r.into())) + .saturating_add(Weight::from_parts(0, 659930).saturating_mul(r.into())) } /// Storage: System Account (r:82 w:81) /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: Measured) @@ -3376,17 +3376,17 @@ impl WeightInfo for () { // Measured: `24269 + t * (16910 ±0)` // Estimated: `532690 + t * (285025 ±0)` // Minimum execution time: 10_443_315 nanoseconds. - Weight::from_ref_time(9_342_574_069) - .saturating_add(Weight::from_proof_size(532690)) + Weight::from_parts(9_342_574_069, 0) + .saturating_add(Weight::from_parts(0, 532690)) // Standard Error: 7_237_279 - .saturating_add(Weight::from_ref_time(1_390_221_936).saturating_mul(t.into())) + .saturating_add(Weight::from_parts(1_390_221_936, 0).saturating_mul(t.into())) // Standard Error: 10_851 - .saturating_add(Weight::from_ref_time(9_842_151).saturating_mul(c.into())) + .saturating_add(Weight::from_parts(9_842_151, 0).saturating_mul(c.into())) .saturating_add(RocksDbWeight::get().reads(167_u64)) .saturating_add(RocksDbWeight::get().reads((81_u64).saturating_mul(t.into()))) .saturating_add(RocksDbWeight::get().writes(163_u64)) .saturating_add(RocksDbWeight::get().writes((81_u64).saturating_mul(t.into()))) - .saturating_add(Weight::from_proof_size(285025).saturating_mul(t.into())) + .saturating_add(Weight::from_parts(0, 285025).saturating_mul(t.into())) } /// Storage: System Account (r:3202 w:3202) /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: Measured) @@ -3408,15 +3408,15 @@ impl WeightInfo for () { // Measured: `1775 + r * (25568 ±0)` // Estimated: `26563 + r * (1367114 ±2)` // Minimum execution time: 376_418 nanoseconds. - Weight::from_ref_time(377_292_000) - .saturating_add(Weight::from_proof_size(26563)) + Weight::from_parts(377_292_000, 0) + .saturating_add(Weight::from_parts(0, 26563)) // Standard Error: 32_312_545 - .saturating_add(Weight::from_ref_time(35_904_826_312).saturating_mul(r.into())) + .saturating_add(Weight::from_parts(35_904_826_312, 0).saturating_mul(r.into())) .saturating_add(RocksDbWeight::get().reads(8_u64)) .saturating_add(RocksDbWeight::get().reads((480_u64).saturating_mul(r.into()))) .saturating_add(RocksDbWeight::get().writes(5_u64)) .saturating_add(RocksDbWeight::get().writes((400_u64).saturating_mul(r.into()))) - .saturating_add(Weight::from_proof_size(1367114).saturating_mul(r.into())) + .saturating_add(Weight::from_parts(0, 1367114).saturating_mul(r.into())) } /// Storage: System Account (r:162 w:162) /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: Measured) @@ -3440,19 +3440,19 @@ impl WeightInfo for () { // Measured: `5785 + t * (33 ±0)` // Estimated: `850985 + t * (2671 ±3)` // Minimum execution time: 132_157_340 nanoseconds. - Weight::from_ref_time(11_329_968_948) - .saturating_add(Weight::from_proof_size(850985)) + Weight::from_parts(11_329_968_948, 0) + .saturating_add(Weight::from_parts(0, 850985)) // Standard Error: 99_102_968 - .saturating_add(Weight::from_ref_time(84_719_458).saturating_mul(t.into())) + .saturating_add(Weight::from_parts(84_719_458, 0).saturating_mul(t.into())) // Standard Error: 161_609 - .saturating_add(Weight::from_ref_time(126_156_627).saturating_mul(i.into())) + .saturating_add(Weight::from_parts(126_156_627, 0).saturating_mul(i.into())) // Standard Error: 161_609 - .saturating_add(Weight::from_ref_time(126_628_313).saturating_mul(s.into())) + .saturating_add(Weight::from_parts(126_628_313, 0).saturating_mul(s.into())) .saturating_add(RocksDbWeight::get().reads(329_u64)) .saturating_add(RocksDbWeight::get().reads((1_u64).saturating_mul(t.into()))) .saturating_add(RocksDbWeight::get().writes(326_u64)) .saturating_add(RocksDbWeight::get().writes((1_u64).saturating_mul(t.into()))) - .saturating_add(Weight::from_proof_size(2671).saturating_mul(t.into())) + .saturating_add(Weight::from_parts(0, 2671).saturating_mul(t.into())) } /// Storage: System Account (r:1 w:0) /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: Measured) @@ -3470,13 +3470,13 @@ impl WeightInfo for () { // Measured: `871 + r * (642 ±0)` // Estimated: `17225 + r * (3210 ±0)` // Minimum execution time: 373_559 nanoseconds. - Weight::from_ref_time(375_166_904) - .saturating_add(Weight::from_proof_size(17225)) + Weight::from_parts(375_166_904, 0) + .saturating_add(Weight::from_parts(0, 17225)) // Standard Error: 125_024 - .saturating_add(Weight::from_ref_time(42_291_595).saturating_mul(r.into())) + .saturating_add(Weight::from_parts(42_291_595, 0).saturating_mul(r.into())) .saturating_add(RocksDbWeight::get().reads(6_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) - .saturating_add(Weight::from_proof_size(3210).saturating_mul(r.into())) + .saturating_add(Weight::from_parts(0, 3210).saturating_mul(r.into())) } /// Storage: System Account (r:1 w:0) /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: Measured) @@ -3494,10 +3494,10 @@ impl WeightInfo for () { // Measured: `1673` // Estimated: `21160` // Minimum execution time: 416_233 nanoseconds. - Weight::from_ref_time(416_785_000) - .saturating_add(Weight::from_proof_size(21160)) + Weight::from_parts(416_785_000, 0) + .saturating_add(Weight::from_parts(0, 21160)) // Standard Error: 56_223 - .saturating_add(Weight::from_ref_time(324_513_835).saturating_mul(n.into())) + .saturating_add(Weight::from_parts(324_513_835, 0).saturating_mul(n.into())) .saturating_add(RocksDbWeight::get().reads(6_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) } @@ -3517,13 +3517,13 @@ impl WeightInfo for () { // Measured: `873 + r * (642 ±0)` // Estimated: `17235 + r * (3210 ±0)` // Minimum execution time: 371_735 nanoseconds. - Weight::from_ref_time(375_979_430) - .saturating_add(Weight::from_proof_size(17235)) + Weight::from_parts(375_979_430, 0) + .saturating_add(Weight::from_parts(0, 17235)) // Standard Error: 968_037 - .saturating_add(Weight::from_ref_time(57_780_769).saturating_mul(r.into())) + .saturating_add(Weight::from_parts(57_780_769, 0).saturating_mul(r.into())) .saturating_add(RocksDbWeight::get().reads(6_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) - .saturating_add(Weight::from_proof_size(3210).saturating_mul(r.into())) + .saturating_add(Weight::from_parts(0, 3210).saturating_mul(r.into())) } /// Storage: System Account (r:1 w:0) /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: Measured) @@ -3541,10 +3541,10 @@ impl WeightInfo for () { // Measured: `1675` // Estimated: `21205` // Minimum execution time: 428_196 nanoseconds. - Weight::from_ref_time(429_438_000) - .saturating_add(Weight::from_proof_size(21205)) + Weight::from_parts(429_438_000, 0) + .saturating_add(Weight::from_parts(0, 21205)) // Standard Error: 57_860 - .saturating_add(Weight::from_ref_time(260_917_896).saturating_mul(n.into())) + .saturating_add(Weight::from_parts(260_917_896, 0).saturating_mul(n.into())) .saturating_add(RocksDbWeight::get().reads(6_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) } @@ -3564,13 +3564,13 @@ impl WeightInfo for () { // Measured: `873 + r * (642 ±0)` // Estimated: `17235 + r * (3210 ±0)` // Minimum execution time: 371_412 nanoseconds. - Weight::from_ref_time(373_635_818) - .saturating_add(Weight::from_proof_size(17235)) + Weight::from_parts(373_635_818, 0) + .saturating_add(Weight::from_parts(0, 17235)) // Standard Error: 222_973 - .saturating_add(Weight::from_ref_time(33_347_181).saturating_mul(r.into())) + .saturating_add(Weight::from_parts(33_347_181, 0).saturating_mul(r.into())) .saturating_add(RocksDbWeight::get().reads(6_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) - .saturating_add(Weight::from_proof_size(3210).saturating_mul(r.into())) + .saturating_add(Weight::from_parts(0, 3210).saturating_mul(r.into())) } /// Storage: System Account (r:1 w:0) /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: Measured) @@ -3588,10 +3588,10 @@ impl WeightInfo for () { // Measured: `1675` // Estimated: `21180` // Minimum execution time: 405_858 nanoseconds. - Weight::from_ref_time(406_498_000) - .saturating_add(Weight::from_proof_size(21180)) + Weight::from_parts(406_498_000, 0) + .saturating_add(Weight::from_parts(0, 21180)) // Standard Error: 48_388 - .saturating_add(Weight::from_ref_time(103_283_157).saturating_mul(n.into())) + .saturating_add(Weight::from_parts(103_283_157, 0).saturating_mul(n.into())) .saturating_add(RocksDbWeight::get().reads(6_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) } @@ -3611,13 +3611,13 @@ impl WeightInfo for () { // Measured: `873 + r * (679 ±0)` // Estimated: `17235 + r * (3395 ±0)` // Minimum execution time: 371_746 nanoseconds. - Weight::from_ref_time(373_538_171) - .saturating_add(Weight::from_proof_size(17235)) + Weight::from_parts(373_538_171, 0) + .saturating_add(Weight::from_parts(0, 17235)) // Standard Error: 387_332 - .saturating_add(Weight::from_ref_time(35_933_528).saturating_mul(r.into())) + .saturating_add(Weight::from_parts(35_933_528, 0).saturating_mul(r.into())) .saturating_add(RocksDbWeight::get().reads(6_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) - .saturating_add(Weight::from_proof_size(3395).saturating_mul(r.into())) + .saturating_add(Weight::from_parts(0, 3395).saturating_mul(r.into())) } /// Storage: System Account (r:1 w:0) /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: Measured) @@ -3635,10 +3635,10 @@ impl WeightInfo for () { // Measured: `1675` // Estimated: `21225` // Minimum execution time: 405_752 nanoseconds. - Weight::from_ref_time(406_417_000) - .saturating_add(Weight::from_proof_size(21225)) + Weight::from_parts(406_417_000, 0) + .saturating_add(Weight::from_parts(0, 21225)) // Standard Error: 47_051 - .saturating_add(Weight::from_ref_time(103_325_027).saturating_mul(n.into())) + .saturating_add(Weight::from_parts(103_325_027, 0).saturating_mul(n.into())) .saturating_add(RocksDbWeight::get().reads(6_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) } @@ -3658,13 +3658,13 @@ impl WeightInfo for () { // Measured: `917 + r * (6083 ±0)` // Estimated: `17455 + r * (30415 ±0)` // Minimum execution time: 373_882 nanoseconds. - Weight::from_ref_time(376_553_787) - .saturating_add(Weight::from_proof_size(17455)) + Weight::from_parts(376_553_787, 0) + .saturating_add(Weight::from_parts(0, 17455)) // Standard Error: 912_833 - .saturating_add(Weight::from_ref_time(3_021_100_412).saturating_mul(r.into())) + .saturating_add(Weight::from_parts(3_021_100_412, 0).saturating_mul(r.into())) .saturating_add(RocksDbWeight::get().reads(6_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) - .saturating_add(Weight::from_proof_size(30415).saturating_mul(r.into())) + .saturating_add(Weight::from_parts(0, 30415).saturating_mul(r.into())) } /// Storage: System Account (r:1 w:0) /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: Measured) @@ -3682,13 +3682,13 @@ impl WeightInfo for () { // Measured: `886 + r * (3362 ±0)` // Estimated: `17300 + r * (16810 ±0)` // Minimum execution time: 373_673 nanoseconds. - Weight::from_ref_time(375_712_961) - .saturating_add(Weight::from_proof_size(17300)) + Weight::from_parts(375_712_961, 0) + .saturating_add(Weight::from_parts(0, 17300)) // Standard Error: 596_297 - .saturating_add(Weight::from_ref_time(738_257_638).saturating_mul(r.into())) + .saturating_add(Weight::from_parts(738_257_638, 0).saturating_mul(r.into())) .saturating_add(RocksDbWeight::get().reads(6_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) - .saturating_add(Weight::from_proof_size(16810).saturating_mul(r.into())) + .saturating_add(Weight::from_parts(0, 16810).saturating_mul(r.into())) } /// Storage: System Account (r:1 w:0) /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: Measured) @@ -3708,15 +3708,15 @@ impl WeightInfo for () { // Measured: `0 + r * (79300 ±0)` // Estimated: `64844 + r * (942952 ±833)` // Minimum execution time: 374_097 nanoseconds. - Weight::from_ref_time(374_985_000) - .saturating_add(Weight::from_proof_size(64844)) + Weight::from_parts(374_985_000, 0) + .saturating_add(Weight::from_parts(0, 64844)) // Standard Error: 3_772_336 - .saturating_add(Weight::from_ref_time(1_546_402_854).saturating_mul(r.into())) + .saturating_add(Weight::from_parts(1_546_402_854, 0).saturating_mul(r.into())) .saturating_add(RocksDbWeight::get().reads(6_u64)) .saturating_add(RocksDbWeight::get().reads((225_u64).saturating_mul(r.into()))) .saturating_add(RocksDbWeight::get().writes(3_u64)) .saturating_add(RocksDbWeight::get().writes((150_u64).saturating_mul(r.into()))) - .saturating_add(Weight::from_proof_size(942952).saturating_mul(r.into())) + .saturating_add(Weight::from_parts(0, 942952).saturating_mul(r.into())) } /// Storage: System Account (r:1 w:0) /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: Measured) @@ -3734,13 +3734,13 @@ impl WeightInfo for () { // Measured: `869 + r * (240 ±0)` // Estimated: `17215 + r * (1200 ±0)` // Minimum execution time: 374_249 nanoseconds. - Weight::from_ref_time(377_990_998) - .saturating_add(Weight::from_proof_size(17215)) + Weight::from_parts(377_990_998, 0) + .saturating_add(Weight::from_parts(0, 17215)) // Standard Error: 38_133 - .saturating_add(Weight::from_ref_time(11_483_273).saturating_mul(r.into())) + .saturating_add(Weight::from_parts(11_483_273, 0).saturating_mul(r.into())) .saturating_add(RocksDbWeight::get().reads(6_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) - .saturating_add(Weight::from_proof_size(1200).saturating_mul(r.into())) + .saturating_add(Weight::from_parts(0, 1200).saturating_mul(r.into())) } /// Storage: System Account (r:1 w:0) /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: Measured) @@ -3758,13 +3758,13 @@ impl WeightInfo for () { // Measured: `2102 + r * (3154 ±0)` // Estimated: `21980 + r * (15875 ±2)` // Minimum execution time: 375_552 nanoseconds. - Weight::from_ref_time(400_624_032) - .saturating_add(Weight::from_proof_size(21980)) + Weight::from_parts(400_624_032, 0) + .saturating_add(Weight::from_parts(0, 21980)) // Standard Error: 82_523 - .saturating_add(Weight::from_ref_time(18_057_327).saturating_mul(r.into())) + .saturating_add(Weight::from_parts(18_057_327, 0).saturating_mul(r.into())) .saturating_add(RocksDbWeight::get().reads(6_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) - .saturating_add(Weight::from_proof_size(15875).saturating_mul(r.into())) + .saturating_add(Weight::from_parts(0, 15875).saturating_mul(r.into())) } /// Storage: System Account (r:1 w:0) /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: Measured) @@ -3784,13 +3784,13 @@ impl WeightInfo for () { // Measured: `872 + r * (240 ±0)` // Estimated: `18598 + r * (1440 ±0)` // Minimum execution time: 373_899 nanoseconds. - Weight::from_ref_time(379_733_943) - .saturating_add(Weight::from_proof_size(18598)) + Weight::from_parts(379_733_943, 0) + .saturating_add(Weight::from_parts(0, 18598)) // Standard Error: 32_022 - .saturating_add(Weight::from_ref_time(9_381_180).saturating_mul(r.into())) + .saturating_add(Weight::from_parts(9_381_180, 0).saturating_mul(r.into())) .saturating_add(RocksDbWeight::get().reads(7_u64)) .saturating_add(RocksDbWeight::get().writes(4_u64)) - .saturating_add(Weight::from_proof_size(1440).saturating_mul(r.into())) + .saturating_add(Weight::from_parts(0, 1440).saturating_mul(r.into())) } /// The range of component `r` is `[0, 50]`. fn instr_i64const(r: u32, ) -> Weight { @@ -3798,10 +3798,10 @@ impl WeightInfo for () { // Measured: `0` // Estimated: `0` // Minimum execution time: 834 nanoseconds. - Weight::from_ref_time(1_009_646) - .saturating_add(Weight::from_proof_size(0)) + Weight::from_parts(1_009_646, 0) + .saturating_add(Weight::from_parts(0, 0)) // Standard Error: 388 - .saturating_add(Weight::from_ref_time(411_979).saturating_mul(r.into())) + .saturating_add(Weight::from_parts(411_979, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 50]`. fn instr_i64load(r: u32, ) -> Weight { @@ -3809,10 +3809,10 @@ impl WeightInfo for () { // Measured: `0` // Estimated: `0` // Minimum execution time: 882 nanoseconds. - Weight::from_ref_time(1_416_377) - .saturating_add(Weight::from_proof_size(0)) + Weight::from_parts(1_416_377, 0) + .saturating_add(Weight::from_parts(0, 0)) // Standard Error: 1_133 - .saturating_add(Weight::from_ref_time(1_075_838).saturating_mul(r.into())) + .saturating_add(Weight::from_parts(1_075_838, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 50]`. fn instr_i64store(r: u32, ) -> Weight { @@ -3820,10 +3820,10 @@ impl WeightInfo for () { // Measured: `0` // Estimated: `0` // Minimum execution time: 878 nanoseconds. - Weight::from_ref_time(1_343_056) - .saturating_add(Weight::from_proof_size(0)) + Weight::from_parts(1_343_056, 0) + .saturating_add(Weight::from_parts(0, 0)) // Standard Error: 426 - .saturating_add(Weight::from_ref_time(1_001_214).saturating_mul(r.into())) + .saturating_add(Weight::from_parts(1_001_214, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 50]`. fn instr_select(r: u32, ) -> Weight { @@ -3831,10 +3831,10 @@ impl WeightInfo for () { // Measured: `0` // Estimated: `0` // Minimum execution time: 796 nanoseconds. - Weight::from_ref_time(1_079_086) - .saturating_add(Weight::from_proof_size(0)) + Weight::from_parts(1_079_086, 0) + .saturating_add(Weight::from_parts(0, 0)) // Standard Error: 409 - .saturating_add(Weight::from_ref_time(1_149_188).saturating_mul(r.into())) + .saturating_add(Weight::from_parts(1_149_188, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 50]`. fn instr_if(r: u32, ) -> Weight { @@ -3842,10 +3842,10 @@ impl WeightInfo for () { // Measured: `0` // Estimated: `0` // Minimum execution time: 800 nanoseconds. - Weight::from_ref_time(1_044_184) - .saturating_add(Weight::from_proof_size(0)) + Weight::from_parts(1_044_184, 0) + .saturating_add(Weight::from_parts(0, 0)) // Standard Error: 707 - .saturating_add(Weight::from_ref_time(1_315_686).saturating_mul(r.into())) + .saturating_add(Weight::from_parts(1_315_686, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 50]`. fn instr_br(r: u32, ) -> Weight { @@ -3853,10 +3853,10 @@ impl WeightInfo for () { // Measured: `0` // Estimated: `0` // Minimum execution time: 807 nanoseconds. - Weight::from_ref_time(1_049_633) - .saturating_add(Weight::from_proof_size(0)) + Weight::from_parts(1_049_633, 0) + .saturating_add(Weight::from_parts(0, 0)) // Standard Error: 361 - .saturating_add(Weight::from_ref_time(640_530).saturating_mul(r.into())) + .saturating_add(Weight::from_parts(640_530, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 50]`. fn instr_br_if(r: u32, ) -> Weight { @@ -3864,10 +3864,10 @@ impl WeightInfo for () { // Measured: `0` // Estimated: `0` // Minimum execution time: 774 nanoseconds. - Weight::from_ref_time(1_124_053) - .saturating_add(Weight::from_proof_size(0)) + Weight::from_parts(1_124_053, 0) + .saturating_add(Weight::from_parts(0, 0)) // Standard Error: 784 - .saturating_add(Weight::from_ref_time(949_398).saturating_mul(r.into())) + .saturating_add(Weight::from_parts(949_398, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 50]`. fn instr_br_table(r: u32, ) -> Weight { @@ -3875,10 +3875,10 @@ impl WeightInfo for () { // Measured: `0` // Estimated: `0` // Minimum execution time: 810 nanoseconds. - Weight::from_ref_time(676_581) - .saturating_add(Weight::from_proof_size(0)) + Weight::from_parts(676_581, 0) + .saturating_add(Weight::from_parts(0, 0)) // Standard Error: 2_356 - .saturating_add(Weight::from_ref_time(1_163_465).saturating_mul(r.into())) + .saturating_add(Weight::from_parts(1_163_465, 0).saturating_mul(r.into())) } /// The range of component `e` is `[1, 256]`. fn instr_br_table_per_entry(e: u32, ) -> Weight { @@ -3886,10 +3886,10 @@ impl WeightInfo for () { // Measured: `0` // Estimated: `0` // Minimum execution time: 2_580 nanoseconds. - Weight::from_ref_time(2_835_656) - .saturating_add(Weight::from_proof_size(0)) + Weight::from_parts(2_835_656, 0) + .saturating_add(Weight::from_parts(0, 0)) // Standard Error: 71 - .saturating_add(Weight::from_ref_time(4_686).saturating_mul(e.into())) + .saturating_add(Weight::from_parts(4_686, 0).saturating_mul(e.into())) } /// The range of component `r` is `[0, 50]`. fn instr_call(r: u32, ) -> Weight { @@ -3897,10 +3897,10 @@ impl WeightInfo for () { // Measured: `0` // Estimated: `0` // Minimum execution time: 826 nanoseconds. - Weight::from_ref_time(1_625_698) - .saturating_add(Weight::from_proof_size(0)) + Weight::from_parts(1_625_698, 0) + .saturating_add(Weight::from_parts(0, 0)) // Standard Error: 1_740 - .saturating_add(Weight::from_ref_time(2_332_187).saturating_mul(r.into())) + .saturating_add(Weight::from_parts(2_332_187, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 50]`. fn instr_call_indirect(r: u32, ) -> Weight { @@ -3908,10 +3908,10 @@ impl WeightInfo for () { // Measured: `0` // Estimated: `0` // Minimum execution time: 901 nanoseconds. - Weight::from_ref_time(2_338_620) - .saturating_add(Weight::from_proof_size(0)) + Weight::from_parts(2_338_620, 0) + .saturating_add(Weight::from_parts(0, 0)) // Standard Error: 1_642 - .saturating_add(Weight::from_ref_time(2_924_090).saturating_mul(r.into())) + .saturating_add(Weight::from_parts(2_924_090, 0).saturating_mul(r.into())) } /// The range of component `p` is `[0, 128]`. fn instr_call_indirect_per_param(p: u32, ) -> Weight { @@ -3919,10 +3919,10 @@ impl WeightInfo for () { // Measured: `0` // Estimated: `0` // Minimum execution time: 4_670 nanoseconds. - Weight::from_ref_time(5_556_246) - .saturating_add(Weight::from_proof_size(0)) + Weight::from_parts(5_556_246, 0) + .saturating_add(Weight::from_parts(0, 0)) // Standard Error: 1_491 - .saturating_add(Weight::from_ref_time(228_965).saturating_mul(p.into())) + .saturating_add(Weight::from_parts(228_965, 0).saturating_mul(p.into())) } /// The range of component `l` is `[0, 1024]`. fn instr_call_per_local(l: u32, ) -> Weight { @@ -3930,10 +3930,10 @@ impl WeightInfo for () { // Measured: `0` // Estimated: `0` // Minimum execution time: 3_099 nanoseconds. - Weight::from_ref_time(3_896_177) - .saturating_add(Weight::from_proof_size(0)) + Weight::from_parts(3_896_177, 0) + .saturating_add(Weight::from_parts(0, 0)) // Standard Error: 99 - .saturating_add(Weight::from_ref_time(91_304).saturating_mul(l.into())) + .saturating_add(Weight::from_parts(91_304, 0).saturating_mul(l.into())) } /// The range of component `r` is `[0, 50]`. fn instr_local_get(r: u32, ) -> Weight { @@ -3941,10 +3941,10 @@ impl WeightInfo for () { // Measured: `0` // Estimated: `0` // Minimum execution time: 3_042 nanoseconds. - Weight::from_ref_time(3_334_621) - .saturating_add(Weight::from_proof_size(0)) + Weight::from_parts(3_334_621, 0) + .saturating_add(Weight::from_parts(0, 0)) // Standard Error: 793 - .saturating_add(Weight::from_ref_time(459_346).saturating_mul(r.into())) + .saturating_add(Weight::from_parts(459_346, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 50]`. fn instr_local_set(r: u32, ) -> Weight { @@ -3952,10 +3952,10 @@ impl WeightInfo for () { // Measured: `0` // Estimated: `0` // Minimum execution time: 2_968 nanoseconds. - Weight::from_ref_time(3_235_286) - .saturating_add(Weight::from_proof_size(0)) + Weight::from_parts(3_235_286, 0) + .saturating_add(Weight::from_parts(0, 0)) // Standard Error: 427 - .saturating_add(Weight::from_ref_time(485_454).saturating_mul(r.into())) + .saturating_add(Weight::from_parts(485_454, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 50]`. fn instr_local_tee(r: u32, ) -> Weight { @@ -3963,10 +3963,10 @@ impl WeightInfo for () { // Measured: `0` // Estimated: `0` // Minimum execution time: 3_012 nanoseconds. - Weight::from_ref_time(3_303_555) - .saturating_add(Weight::from_proof_size(0)) + Weight::from_parts(3_303_555, 0) + .saturating_add(Weight::from_parts(0, 0)) // Standard Error: 371 - .saturating_add(Weight::from_ref_time(657_811).saturating_mul(r.into())) + .saturating_add(Weight::from_parts(657_811, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 50]`. fn instr_global_get(r: u32, ) -> Weight { @@ -3974,10 +3974,10 @@ impl WeightInfo for () { // Measured: `0` // Estimated: `0` // Minimum execution time: 865 nanoseconds. - Weight::from_ref_time(1_249_987) - .saturating_add(Weight::from_proof_size(0)) + Weight::from_parts(1_249_987, 0) + .saturating_add(Weight::from_parts(0, 0)) // Standard Error: 417 - .saturating_add(Weight::from_ref_time(896_704).saturating_mul(r.into())) + .saturating_add(Weight::from_parts(896_704, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 50]`. fn instr_global_set(r: u32, ) -> Weight { @@ -3985,10 +3985,10 @@ impl WeightInfo for () { // Measured: `0` // Estimated: `0` // Minimum execution time: 866 nanoseconds. - Weight::from_ref_time(1_216_218) - .saturating_add(Weight::from_proof_size(0)) + Weight::from_parts(1_216_218, 0) + .saturating_add(Weight::from_parts(0, 0)) // Standard Error: 503 - .saturating_add(Weight::from_ref_time(919_719).saturating_mul(r.into())) + .saturating_add(Weight::from_parts(919_719, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 50]`. fn instr_memory_current(r: u32, ) -> Weight { @@ -3996,10 +3996,10 @@ impl WeightInfo for () { // Measured: `0` // Estimated: `0` // Minimum execution time: 921 nanoseconds. - Weight::from_ref_time(1_228_408) - .saturating_add(Weight::from_proof_size(0)) + Weight::from_parts(1_228_408, 0) + .saturating_add(Weight::from_parts(0, 0)) // Standard Error: 309 - .saturating_add(Weight::from_ref_time(813_007).saturating_mul(r.into())) + .saturating_add(Weight::from_parts(813_007, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 1]`. fn instr_memory_grow(r: u32, ) -> Weight { @@ -4007,10 +4007,10 @@ impl WeightInfo for () { // Measured: `0` // Estimated: `0` // Minimum execution time: 820 nanoseconds. - Weight::from_ref_time(914_830) - .saturating_add(Weight::from_proof_size(0)) + Weight::from_parts(914_830, 0) + .saturating_add(Weight::from_parts(0, 0)) // Standard Error: 6_018 - .saturating_add(Weight::from_ref_time(237_062_769).saturating_mul(r.into())) + .saturating_add(Weight::from_parts(237_062_769, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 50]`. fn instr_i64clz(r: u32, ) -> Weight { @@ -4018,10 +4018,10 @@ impl WeightInfo for () { // Measured: `0` // Estimated: `0` // Minimum execution time: 812 nanoseconds. - Weight::from_ref_time(1_554_406) - .saturating_add(Weight::from_proof_size(0)) + Weight::from_parts(1_554_406, 0) + .saturating_add(Weight::from_parts(0, 0)) // Standard Error: 9_979 - .saturating_add(Weight::from_ref_time(625_434).saturating_mul(r.into())) + .saturating_add(Weight::from_parts(625_434, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 50]`. fn instr_i64ctz(r: u32, ) -> Weight { @@ -4029,10 +4029,10 @@ impl WeightInfo for () { // Measured: `0` // Estimated: `0` // Minimum execution time: 798 nanoseconds. - Weight::from_ref_time(1_095_113) - .saturating_add(Weight::from_proof_size(0)) + Weight::from_parts(1_095_113, 0) + .saturating_add(Weight::from_parts(0, 0)) // Standard Error: 243 - .saturating_add(Weight::from_ref_time(634_204).saturating_mul(r.into())) + .saturating_add(Weight::from_parts(634_204, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 50]`. fn instr_i64popcnt(r: u32, ) -> Weight { @@ -4040,10 +4040,10 @@ impl WeightInfo for () { // Measured: `0` // Estimated: `0` // Minimum execution time: 792 nanoseconds. - Weight::from_ref_time(1_109_845) - .saturating_add(Weight::from_proof_size(0)) + Weight::from_parts(1_109_845, 0) + .saturating_add(Weight::from_parts(0, 0)) // Standard Error: 14_944 - .saturating_add(Weight::from_ref_time(658_834).saturating_mul(r.into())) + .saturating_add(Weight::from_parts(658_834, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 50]`. fn instr_i64eqz(r: u32, ) -> Weight { @@ -4051,10 +4051,10 @@ impl WeightInfo for () { // Measured: `0` // Estimated: `0` // Minimum execution time: 815 nanoseconds. - Weight::from_ref_time(1_068_916) - .saturating_add(Weight::from_proof_size(0)) + Weight::from_parts(1_068_916, 0) + .saturating_add(Weight::from_parts(0, 0)) // Standard Error: 327 - .saturating_add(Weight::from_ref_time(652_897).saturating_mul(r.into())) + .saturating_add(Weight::from_parts(652_897, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 50]`. fn instr_i64extendsi32(r: u32, ) -> Weight { @@ -4062,10 +4062,10 @@ impl WeightInfo for () { // Measured: `0` // Estimated: `0` // Minimum execution time: 798 nanoseconds. - Weight::from_ref_time(1_069_745) - .saturating_add(Weight::from_proof_size(0)) + Weight::from_parts(1_069_745, 0) + .saturating_add(Weight::from_parts(0, 0)) // Standard Error: 306 - .saturating_add(Weight::from_ref_time(618_481).saturating_mul(r.into())) + .saturating_add(Weight::from_parts(618_481, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 50]`. fn instr_i64extendui32(r: u32, ) -> Weight { @@ -4073,10 +4073,10 @@ impl WeightInfo for () { // Measured: `0` // Estimated: `0` // Minimum execution time: 799 nanoseconds. - Weight::from_ref_time(1_398_001) - .saturating_add(Weight::from_proof_size(0)) + Weight::from_parts(1_398_001, 0) + .saturating_add(Weight::from_parts(0, 0)) // Standard Error: 6_234 - .saturating_add(Weight::from_ref_time(611_399).saturating_mul(r.into())) + .saturating_add(Weight::from_parts(611_399, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 50]`. fn instr_i32wrapi64(r: u32, ) -> Weight { @@ -4084,10 +4084,10 @@ impl WeightInfo for () { // Measured: `0` // Estimated: `0` // Minimum execution time: 811 nanoseconds. - Weight::from_ref_time(1_098_083) - .saturating_add(Weight::from_proof_size(0)) + Weight::from_parts(1_098_083, 0) + .saturating_add(Weight::from_parts(0, 0)) // Standard Error: 297 - .saturating_add(Weight::from_ref_time(617_692).saturating_mul(r.into())) + .saturating_add(Weight::from_parts(617_692, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 50]`. fn instr_i64eq(r: u32, ) -> Weight { @@ -4095,10 +4095,10 @@ impl WeightInfo for () { // Measured: `0` // Estimated: `0` // Minimum execution time: 815 nanoseconds. - Weight::from_ref_time(1_046_922) - .saturating_add(Weight::from_proof_size(0)) + Weight::from_parts(1_046_922, 0) + .saturating_add(Weight::from_parts(0, 0)) // Standard Error: 335 - .saturating_add(Weight::from_ref_time(909_196).saturating_mul(r.into())) + .saturating_add(Weight::from_parts(909_196, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 50]`. fn instr_i64ne(r: u32, ) -> Weight { @@ -4106,10 +4106,10 @@ impl WeightInfo for () { // Measured: `0` // Estimated: `0` // Minimum execution time: 805 nanoseconds. - Weight::from_ref_time(1_093_667) - .saturating_add(Weight::from_proof_size(0)) + Weight::from_parts(1_093_667, 0) + .saturating_add(Weight::from_parts(0, 0)) // Standard Error: 233 - .saturating_add(Weight::from_ref_time(907_378).saturating_mul(r.into())) + .saturating_add(Weight::from_parts(907_378, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 50]`. fn instr_i64lts(r: u32, ) -> Weight { @@ -4117,10 +4117,10 @@ impl WeightInfo for () { // Measured: `0` // Estimated: `0` // Minimum execution time: 805 nanoseconds. - Weight::from_ref_time(1_290_591) - .saturating_add(Weight::from_proof_size(0)) + Weight::from_parts(1_290_591, 0) + .saturating_add(Weight::from_parts(0, 0)) // Standard Error: 3_201 - .saturating_add(Weight::from_ref_time(902_006).saturating_mul(r.into())) + .saturating_add(Weight::from_parts(902_006, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 50]`. fn instr_i64ltu(r: u32, ) -> Weight { @@ -4128,10 +4128,10 @@ impl WeightInfo for () { // Measured: `0` // Estimated: `0` // Minimum execution time: 783 nanoseconds. - Weight::from_ref_time(1_159_977) - .saturating_add(Weight::from_proof_size(0)) + Weight::from_parts(1_159_977, 0) + .saturating_add(Weight::from_parts(0, 0)) // Standard Error: 2_310 - .saturating_add(Weight::from_ref_time(906_489).saturating_mul(r.into())) + .saturating_add(Weight::from_parts(906_489, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 50]`. fn instr_i64gts(r: u32, ) -> Weight { @@ -4139,10 +4139,10 @@ impl WeightInfo for () { // Measured: `0` // Estimated: `0` // Minimum execution time: 790 nanoseconds. - Weight::from_ref_time(1_109_719) - .saturating_add(Weight::from_proof_size(0)) + Weight::from_parts(1_109_719, 0) + .saturating_add(Weight::from_parts(0, 0)) // Standard Error: 261 - .saturating_add(Weight::from_ref_time(906_614).saturating_mul(r.into())) + .saturating_add(Weight::from_parts(906_614, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 50]`. fn instr_i64gtu(r: u32, ) -> Weight { @@ -4150,10 +4150,10 @@ impl WeightInfo for () { // Measured: `0` // Estimated: `0` // Minimum execution time: 776 nanoseconds. - Weight::from_ref_time(1_076_567) - .saturating_add(Weight::from_proof_size(0)) + Weight::from_parts(1_076_567, 0) + .saturating_add(Weight::from_parts(0, 0)) // Standard Error: 348 - .saturating_add(Weight::from_ref_time(919_374).saturating_mul(r.into())) + .saturating_add(Weight::from_parts(919_374, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 50]`. fn instr_i64les(r: u32, ) -> Weight { @@ -4161,10 +4161,10 @@ impl WeightInfo for () { // Measured: `0` // Estimated: `0` // Minimum execution time: 780 nanoseconds. - Weight::from_ref_time(1_069_663) - .saturating_add(Weight::from_proof_size(0)) + Weight::from_parts(1_069_663, 0) + .saturating_add(Weight::from_parts(0, 0)) // Standard Error: 265 - .saturating_add(Weight::from_ref_time(908_037).saturating_mul(r.into())) + .saturating_add(Weight::from_parts(908_037, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 50]`. fn instr_i64leu(r: u32, ) -> Weight { @@ -4172,10 +4172,10 @@ impl WeightInfo for () { // Measured: `0` // Estimated: `0` // Minimum execution time: 832 nanoseconds. - Weight::from_ref_time(930_920) - .saturating_add(Weight::from_proof_size(0)) + Weight::from_parts(930_920, 0) + .saturating_add(Weight::from_parts(0, 0)) // Standard Error: 2_170 - .saturating_add(Weight::from_ref_time(929_811).saturating_mul(r.into())) + .saturating_add(Weight::from_parts(929_811, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 50]`. fn instr_i64ges(r: u32, ) -> Weight { @@ -4183,10 +4183,10 @@ impl WeightInfo for () { // Measured: `0` // Estimated: `0` // Minimum execution time: 787 nanoseconds. - Weight::from_ref_time(1_087_325) - .saturating_add(Weight::from_proof_size(0)) + Weight::from_parts(1_087_325, 0) + .saturating_add(Weight::from_parts(0, 0)) // Standard Error: 315 - .saturating_add(Weight::from_ref_time(908_321).saturating_mul(r.into())) + .saturating_add(Weight::from_parts(908_321, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 50]`. fn instr_i64geu(r: u32, ) -> Weight { @@ -4194,10 +4194,10 @@ impl WeightInfo for () { // Measured: `0` // Estimated: `0` // Minimum execution time: 798 nanoseconds. - Weight::from_ref_time(1_029_132) - .saturating_add(Weight::from_proof_size(0)) + Weight::from_parts(1_029_132, 0) + .saturating_add(Weight::from_parts(0, 0)) // Standard Error: 2_095 - .saturating_add(Weight::from_ref_time(913_553).saturating_mul(r.into())) + .saturating_add(Weight::from_parts(913_553, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 50]`. fn instr_i64add(r: u32, ) -> Weight { @@ -4205,10 +4205,10 @@ impl WeightInfo for () { // Measured: `0` // Estimated: `0` // Minimum execution time: 791 nanoseconds. - Weight::from_ref_time(1_086_314) - .saturating_add(Weight::from_proof_size(0)) + Weight::from_parts(1_086_314, 0) + .saturating_add(Weight::from_parts(0, 0)) // Standard Error: 197 - .saturating_add(Weight::from_ref_time(896_392).saturating_mul(r.into())) + .saturating_add(Weight::from_parts(896_392, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 50]`. fn instr_i64sub(r: u32, ) -> Weight { @@ -4216,10 +4216,10 @@ impl WeightInfo for () { // Measured: `0` // Estimated: `0` // Minimum execution time: 793 nanoseconds. - Weight::from_ref_time(1_078_172) - .saturating_add(Weight::from_proof_size(0)) + Weight::from_parts(1_078_172, 0) + .saturating_add(Weight::from_parts(0, 0)) // Standard Error: 404 - .saturating_add(Weight::from_ref_time(886_329).saturating_mul(r.into())) + .saturating_add(Weight::from_parts(886_329, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 50]`. fn instr_i64mul(r: u32, ) -> Weight { @@ -4227,10 +4227,10 @@ impl WeightInfo for () { // Measured: `0` // Estimated: `0` // Minimum execution time: 799 nanoseconds. - Weight::from_ref_time(1_095_010) - .saturating_add(Weight::from_proof_size(0)) + Weight::from_parts(1_095_010, 0) + .saturating_add(Weight::from_parts(0, 0)) // Standard Error: 431 - .saturating_add(Weight::from_ref_time(886_513).saturating_mul(r.into())) + .saturating_add(Weight::from_parts(886_513, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 50]`. fn instr_i64divs(r: u32, ) -> Weight { @@ -4238,10 +4238,10 @@ impl WeightInfo for () { // Measured: `0` // Estimated: `0` // Minimum execution time: 810 nanoseconds. - Weight::from_ref_time(1_114_325) - .saturating_add(Weight::from_proof_size(0)) + Weight::from_parts(1_114_325, 0) + .saturating_add(Weight::from_parts(0, 0)) // Standard Error: 452 - .saturating_add(Weight::from_ref_time(1_521_849).saturating_mul(r.into())) + .saturating_add(Weight::from_parts(1_521_849, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 50]`. fn instr_i64divu(r: u32, ) -> Weight { @@ -4249,10 +4249,10 @@ impl WeightInfo for () { // Measured: `0` // Estimated: `0` // Minimum execution time: 784 nanoseconds. - Weight::from_ref_time(1_123_153) - .saturating_add(Weight::from_proof_size(0)) + Weight::from_parts(1_123_153, 0) + .saturating_add(Weight::from_parts(0, 0)) // Standard Error: 475 - .saturating_add(Weight::from_ref_time(1_457_746).saturating_mul(r.into())) + .saturating_add(Weight::from_parts(1_457_746, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 50]`. fn instr_i64rems(r: u32, ) -> Weight { @@ -4260,10 +4260,10 @@ impl WeightInfo for () { // Measured: `0` // Estimated: `0` // Minimum execution time: 809 nanoseconds. - Weight::from_ref_time(1_145_906) - .saturating_add(Weight::from_proof_size(0)) + Weight::from_parts(1_145_906, 0) + .saturating_add(Weight::from_parts(0, 0)) // Standard Error: 718 - .saturating_add(Weight::from_ref_time(1_549_964).saturating_mul(r.into())) + .saturating_add(Weight::from_parts(1_549_964, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 50]`. fn instr_i64remu(r: u32, ) -> Weight { @@ -4271,10 +4271,10 @@ impl WeightInfo for () { // Measured: `0` // Estimated: `0` // Minimum execution time: 803 nanoseconds. - Weight::from_ref_time(1_110_328) - .saturating_add(Weight::from_proof_size(0)) + Weight::from_parts(1_110_328, 0) + .saturating_add(Weight::from_parts(0, 0)) // Standard Error: 627 - .saturating_add(Weight::from_ref_time(1_453_013).saturating_mul(r.into())) + .saturating_add(Weight::from_parts(1_453_013, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 50]`. fn instr_i64and(r: u32, ) -> Weight { @@ -4282,10 +4282,10 @@ impl WeightInfo for () { // Measured: `0` // Estimated: `0` // Minimum execution time: 786 nanoseconds. - Weight::from_ref_time(1_108_792) - .saturating_add(Weight::from_proof_size(0)) + Weight::from_parts(1_108_792, 0) + .saturating_add(Weight::from_parts(0, 0)) // Standard Error: 286 - .saturating_add(Weight::from_ref_time(897_035).saturating_mul(r.into())) + .saturating_add(Weight::from_parts(897_035, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 50]`. fn instr_i64or(r: u32, ) -> Weight { @@ -4293,10 +4293,10 @@ impl WeightInfo for () { // Measured: `0` // Estimated: `0` // Minimum execution time: 787 nanoseconds. - Weight::from_ref_time(830_000) - .saturating_add(Weight::from_proof_size(0)) + Weight::from_parts(830_000, 0) + .saturating_add(Weight::from_parts(0, 0)) // Standard Error: 15_995 - .saturating_add(Weight::from_ref_time(963_344).saturating_mul(r.into())) + .saturating_add(Weight::from_parts(963_344, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 50]`. fn instr_i64xor(r: u32, ) -> Weight { @@ -4304,10 +4304,10 @@ impl WeightInfo for () { // Measured: `0` // Estimated: `0` // Minimum execution time: 773 nanoseconds. - Weight::from_ref_time(1_082_459) - .saturating_add(Weight::from_proof_size(0)) + Weight::from_parts(1_082_459, 0) + .saturating_add(Weight::from_parts(0, 0)) // Standard Error: 330 - .saturating_add(Weight::from_ref_time(897_077).saturating_mul(r.into())) + .saturating_add(Weight::from_parts(897_077, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 50]`. fn instr_i64shl(r: u32, ) -> Weight { @@ -4315,10 +4315,10 @@ impl WeightInfo for () { // Measured: `0` // Estimated: `0` // Minimum execution time: 810 nanoseconds. - Weight::from_ref_time(1_325_815) - .saturating_add(Weight::from_proof_size(0)) + Weight::from_parts(1_325_815, 0) + .saturating_add(Weight::from_parts(0, 0)) // Standard Error: 3_352 - .saturating_add(Weight::from_ref_time(899_555).saturating_mul(r.into())) + .saturating_add(Weight::from_parts(899_555, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 50]`. fn instr_i64shrs(r: u32, ) -> Weight { @@ -4326,10 +4326,10 @@ impl WeightInfo for () { // Measured: `0` // Estimated: `0` // Minimum execution time: 808 nanoseconds. - Weight::from_ref_time(1_085_903) - .saturating_add(Weight::from_proof_size(0)) + Weight::from_parts(1_085_903, 0) + .saturating_add(Weight::from_parts(0, 0)) // Standard Error: 430 - .saturating_add(Weight::from_ref_time(903_249).saturating_mul(r.into())) + .saturating_add(Weight::from_parts(903_249, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 50]`. fn instr_i64shru(r: u32, ) -> Weight { @@ -4337,10 +4337,10 @@ impl WeightInfo for () { // Measured: `0` // Estimated: `0` // Minimum execution time: 792 nanoseconds. - Weight::from_ref_time(1_091_261) - .saturating_add(Weight::from_proof_size(0)) + Weight::from_parts(1_091_261, 0) + .saturating_add(Weight::from_parts(0, 0)) // Standard Error: 312 - .saturating_add(Weight::from_ref_time(902_245).saturating_mul(r.into())) + .saturating_add(Weight::from_parts(902_245, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 50]`. fn instr_i64rotl(r: u32, ) -> Weight { @@ -4348,10 +4348,10 @@ impl WeightInfo for () { // Measured: `0` // Estimated: `0` // Minimum execution time: 807 nanoseconds. - Weight::from_ref_time(1_121_052) - .saturating_add(Weight::from_proof_size(0)) + Weight::from_parts(1_121_052, 0) + .saturating_add(Weight::from_parts(0, 0)) // Standard Error: 506 - .saturating_add(Weight::from_ref_time(902_772).saturating_mul(r.into())) + .saturating_add(Weight::from_parts(902_772, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 50]`. fn instr_i64rotr(r: u32, ) -> Weight { @@ -4359,9 +4359,9 @@ impl WeightInfo for () { // Measured: `0` // Estimated: `0` // Minimum execution time: 823 nanoseconds. - Weight::from_ref_time(1_317_597) - .saturating_add(Weight::from_proof_size(0)) + Weight::from_parts(1_317_597, 0) + .saturating_add(Weight::from_parts(0, 0)) // Standard Error: 6_219 - .saturating_add(Weight::from_ref_time(896_692).saturating_mul(r.into())) + .saturating_add(Weight::from_parts(896_692, 0).saturating_mul(r.into())) } } diff --git a/frame/conviction-voting/src/weights.rs b/frame/conviction-voting/src/weights.rs index a2f992b1f..91c4c0130 100644 --- a/frame/conviction-voting/src/weights.rs +++ b/frame/conviction-voting/src/weights.rs @@ -144,12 +144,12 @@ impl WeightInfo for SubstrateWeight { // Minimum execution time: 33_246 nanoseconds. Weight::from_parts(34_560_391, 176657) // Standard Error: 63_925 - .saturating_add(Weight::from_ref_time(34_500_408).saturating_mul(r.into())) + .saturating_add(Weight::from_parts(34_500_408, 0).saturating_mul(r.into())) .saturating_add(T::DbWeight::get().reads(4_u64)) .saturating_add(T::DbWeight::get().reads((3_u64).saturating_mul(r.into()))) .saturating_add(T::DbWeight::get().writes(4_u64)) .saturating_add(T::DbWeight::get().writes((3_u64).saturating_mul(r.into()))) - .saturating_add(Weight::from_proof_size(110917).saturating_mul(r.into())) + .saturating_add(Weight::from_parts(0, 110917).saturating_mul(r.into())) } /// Storage: ConvictionVoting VotingFor (r:2 w:2) /// Proof: ConvictionVoting VotingFor (max_values: None, max_size: Some(27241), added: 29716, mode: MaxEncodedLen) @@ -165,12 +165,12 @@ impl WeightInfo for SubstrateWeight { // Minimum execution time: 20_508 nanoseconds. Weight::from_parts(21_240_024, 170349) // Standard Error: 37_314 - .saturating_add(Weight::from_ref_time(30_890_875).saturating_mul(r.into())) + .saturating_add(Weight::from_parts(30_890_875, 0).saturating_mul(r.into())) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().reads((3_u64).saturating_mul(r.into()))) .saturating_add(T::DbWeight::get().writes(2_u64)) .saturating_add(T::DbWeight::get().writes((3_u64).saturating_mul(r.into()))) - .saturating_add(Weight::from_proof_size(110917).saturating_mul(r.into())) + .saturating_add(Weight::from_parts(0, 110917).saturating_mul(r.into())) } /// Storage: ConvictionVoting VotingFor (r:1 w:1) /// Proof: ConvictionVoting VotingFor (max_values: None, max_size: Some(27241), added: 29716, mode: MaxEncodedLen) @@ -275,12 +275,12 @@ impl WeightInfo for () { // Minimum execution time: 33_246 nanoseconds. Weight::from_parts(34_560_391, 176657) // Standard Error: 63_925 - .saturating_add(Weight::from_ref_time(34_500_408).saturating_mul(r.into())) + .saturating_add(Weight::from_parts(34_500_408, 0).saturating_mul(r.into())) .saturating_add(RocksDbWeight::get().reads(4_u64)) .saturating_add(RocksDbWeight::get().reads((3_u64).saturating_mul(r.into()))) .saturating_add(RocksDbWeight::get().writes(4_u64)) .saturating_add(RocksDbWeight::get().writes((3_u64).saturating_mul(r.into()))) - .saturating_add(Weight::from_proof_size(110917).saturating_mul(r.into())) + .saturating_add(Weight::from_parts(0, 110917).saturating_mul(r.into())) } /// Storage: ConvictionVoting VotingFor (r:2 w:2) /// Proof: ConvictionVoting VotingFor (max_values: None, max_size: Some(27241), added: 29716, mode: MaxEncodedLen) @@ -296,12 +296,12 @@ impl WeightInfo for () { // Minimum execution time: 20_508 nanoseconds. Weight::from_parts(21_240_024, 170349) // Standard Error: 37_314 - .saturating_add(Weight::from_ref_time(30_890_875).saturating_mul(r.into())) + .saturating_add(Weight::from_parts(30_890_875, 0).saturating_mul(r.into())) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().reads((3_u64).saturating_mul(r.into()))) .saturating_add(RocksDbWeight::get().writes(2_u64)) .saturating_add(RocksDbWeight::get().writes((3_u64).saturating_mul(r.into()))) - .saturating_add(Weight::from_proof_size(110917).saturating_mul(r.into())) + .saturating_add(Weight::from_parts(0, 110917).saturating_mul(r.into())) } /// Storage: ConvictionVoting VotingFor (r:1 w:1) /// Proof: ConvictionVoting VotingFor (max_values: None, max_size: Some(27241), added: 29716, mode: MaxEncodedLen) diff --git a/frame/democracy/src/weights.rs b/frame/democracy/src/weights.rs index a2cdeead6..9241b27bb 100644 --- a/frame/democracy/src/weights.rs +++ b/frame/democracy/src/weights.rs @@ -198,7 +198,7 @@ impl WeightInfo for SubstrateWeight { // Measured: `0` // Estimated: `0` // Minimum execution time: 2_961 nanoseconds. - Weight::from_ref_time(3_139_000) + Weight::from_parts(3_139_000, 0) .saturating_add(T::DbWeight::get().writes(1_u64)) } /// Storage: Democracy NextExternal (r:0 w:1) @@ -208,7 +208,7 @@ impl WeightInfo for SubstrateWeight { // Measured: `0` // Estimated: `0` // Minimum execution time: 3_040 nanoseconds. - Weight::from_ref_time(3_261_000) + Weight::from_parts(3_261_000, 0) .saturating_add(T::DbWeight::get().writes(1_u64)) } /// Storage: Democracy NextExternal (r:1 w:1) @@ -287,11 +287,11 @@ impl WeightInfo for SubstrateWeight { // Minimum execution time: 7_093 nanoseconds. Weight::from_parts(8_792_955, 998) // Standard Error: 6_630 - .saturating_add(Weight::from_ref_time(3_091_565).saturating_mul(r.into())) + .saturating_add(Weight::from_parts(3_091_565, 0).saturating_mul(r.into())) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().reads((1_u64).saturating_mul(r.into()))) .saturating_add(T::DbWeight::get().writes(1_u64)) - .saturating_add(Weight::from_proof_size(2676).saturating_mul(r.into())) + .saturating_add(Weight::from_parts(0, 2676).saturating_mul(r.into())) } /// Storage: Democracy LowestUnbaked (r:1 w:1) /// Proof: Democracy LowestUnbaked (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) @@ -313,11 +313,11 @@ impl WeightInfo for SubstrateWeight { // Minimum execution time: 10_372 nanoseconds. Weight::from_parts(10_961_165, 19318) // Standard Error: 7_284 - .saturating_add(Weight::from_ref_time(3_112_573).saturating_mul(r.into())) + .saturating_add(Weight::from_parts(3_112_573, 0).saturating_mul(r.into())) .saturating_add(T::DbWeight::get().reads(5_u64)) .saturating_add(T::DbWeight::get().reads((1_u64).saturating_mul(r.into()))) .saturating_add(T::DbWeight::get().writes(1_u64)) - .saturating_add(Weight::from_proof_size(2676).saturating_mul(r.into())) + .saturating_add(Weight::from_parts(0, 2676).saturating_mul(r.into())) } /// Storage: Democracy VotingOf (r:3 w:3) /// Proof: Democracy VotingOf (max_values: None, max_size: Some(3795), added: 6270, mode: MaxEncodedLen) @@ -333,12 +333,12 @@ impl WeightInfo for SubstrateWeight { // Minimum execution time: 36_618 nanoseconds. Weight::from_parts(42_803_184, 22584) // Standard Error: 7_268 - .saturating_add(Weight::from_ref_time(4_537_902).saturating_mul(r.into())) + .saturating_add(Weight::from_parts(4_537_902, 0).saturating_mul(r.into())) .saturating_add(T::DbWeight::get().reads(4_u64)) .saturating_add(T::DbWeight::get().reads((1_u64).saturating_mul(r.into()))) .saturating_add(T::DbWeight::get().writes(4_u64)) .saturating_add(T::DbWeight::get().writes((1_u64).saturating_mul(r.into()))) - .saturating_add(Weight::from_proof_size(2676).saturating_mul(r.into())) + .saturating_add(Weight::from_parts(0, 2676).saturating_mul(r.into())) } /// Storage: Democracy VotingOf (r:2 w:2) /// Proof: Democracy VotingOf (max_values: None, max_size: Some(3795), added: 6270, mode: MaxEncodedLen) @@ -352,12 +352,12 @@ impl WeightInfo for SubstrateWeight { // Minimum execution time: 19_758 nanoseconds. Weight::from_parts(21_641_793, 12540) // Standard Error: 6_889 - .saturating_add(Weight::from_ref_time(4_478_884).saturating_mul(r.into())) + .saturating_add(Weight::from_parts(4_478_884, 0).saturating_mul(r.into())) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().reads((1_u64).saturating_mul(r.into()))) .saturating_add(T::DbWeight::get().writes(2_u64)) .saturating_add(T::DbWeight::get().writes((1_u64).saturating_mul(r.into()))) - .saturating_add(Weight::from_proof_size(2676).saturating_mul(r.into())) + .saturating_add(Weight::from_parts(0, 2676).saturating_mul(r.into())) } /// Storage: Democracy PublicProps (r:0 w:1) /// Proof: Democracy PublicProps (max_values: Some(1), max_size: Some(16702), added: 17197, mode: MaxEncodedLen) @@ -366,7 +366,7 @@ impl WeightInfo for SubstrateWeight { // Measured: `0` // Estimated: `0` // Minimum execution time: 2_844 nanoseconds. - Weight::from_ref_time(3_017_000) + Weight::from_parts(3_017_000, 0) .saturating_add(T::DbWeight::get().writes(1_u64)) } /// Storage: Democracy VotingOf (r:1 w:1) @@ -383,7 +383,7 @@ impl WeightInfo for SubstrateWeight { // Minimum execution time: 20_380 nanoseconds. Weight::from_parts(28_295_875, 12647) // Standard Error: 2_331 - .saturating_add(Weight::from_ref_time(67_348).saturating_mul(r.into())) + .saturating_add(Weight::from_parts(67_348, 0).saturating_mul(r.into())) .saturating_add(T::DbWeight::get().reads(3_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) } @@ -401,7 +401,7 @@ impl WeightInfo for SubstrateWeight { // Minimum execution time: 24_475 nanoseconds. Weight::from_parts(27_102_576, 12647) // Standard Error: 1_464 - .saturating_add(Weight::from_ref_time(128_921).saturating_mul(r.into())) + .saturating_add(Weight::from_parts(128_921, 0).saturating_mul(r.into())) .saturating_add(T::DbWeight::get().reads(3_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) } @@ -417,7 +417,7 @@ impl WeightInfo for SubstrateWeight { // Minimum execution time: 15_039 nanoseconds. Weight::from_parts(19_252_498, 8946) // Standard Error: 1_798 - .saturating_add(Weight::from_ref_time(131_855).saturating_mul(r.into())) + .saturating_add(Weight::from_parts(131_855, 0).saturating_mul(r.into())) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().writes(2_u64)) } @@ -433,7 +433,7 @@ impl WeightInfo for SubstrateWeight { // Minimum execution time: 14_837 nanoseconds. Weight::from_parts(19_144_929, 8946) // Standard Error: 1_875 - .saturating_add(Weight::from_ref_time(136_819).saturating_mul(r.into())) + .saturating_add(Weight::from_parts(136_819, 0).saturating_mul(r.into())) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().writes(2_u64)) } @@ -639,7 +639,7 @@ impl WeightInfo for () { // Measured: `0` // Estimated: `0` // Minimum execution time: 2_961 nanoseconds. - Weight::from_ref_time(3_139_000) + Weight::from_parts(3_139_000, 0) .saturating_add(RocksDbWeight::get().writes(1_u64)) } /// Storage: Democracy NextExternal (r:0 w:1) @@ -649,7 +649,7 @@ impl WeightInfo for () { // Measured: `0` // Estimated: `0` // Minimum execution time: 3_040 nanoseconds. - Weight::from_ref_time(3_261_000) + Weight::from_parts(3_261_000, 0) .saturating_add(RocksDbWeight::get().writes(1_u64)) } /// Storage: Democracy NextExternal (r:1 w:1) @@ -728,11 +728,11 @@ impl WeightInfo for () { // Minimum execution time: 7_093 nanoseconds. Weight::from_parts(8_792_955, 998) // Standard Error: 6_630 - .saturating_add(Weight::from_ref_time(3_091_565).saturating_mul(r.into())) + .saturating_add(Weight::from_parts(3_091_565, 0).saturating_mul(r.into())) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().reads((1_u64).saturating_mul(r.into()))) .saturating_add(RocksDbWeight::get().writes(1_u64)) - .saturating_add(Weight::from_proof_size(2676).saturating_mul(r.into())) + .saturating_add(Weight::from_parts(0, 2676).saturating_mul(r.into())) } /// Storage: Democracy LowestUnbaked (r:1 w:1) /// Proof: Democracy LowestUnbaked (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) @@ -754,11 +754,11 @@ impl WeightInfo for () { // Minimum execution time: 10_372 nanoseconds. Weight::from_parts(10_961_165, 19318) // Standard Error: 7_284 - .saturating_add(Weight::from_ref_time(3_112_573).saturating_mul(r.into())) + .saturating_add(Weight::from_parts(3_112_573, 0).saturating_mul(r.into())) .saturating_add(RocksDbWeight::get().reads(5_u64)) .saturating_add(RocksDbWeight::get().reads((1_u64).saturating_mul(r.into()))) .saturating_add(RocksDbWeight::get().writes(1_u64)) - .saturating_add(Weight::from_proof_size(2676).saturating_mul(r.into())) + .saturating_add(Weight::from_parts(0, 2676).saturating_mul(r.into())) } /// Storage: Democracy VotingOf (r:3 w:3) /// Proof: Democracy VotingOf (max_values: None, max_size: Some(3795), added: 6270, mode: MaxEncodedLen) @@ -774,12 +774,12 @@ impl WeightInfo for () { // Minimum execution time: 36_618 nanoseconds. Weight::from_parts(42_803_184, 22584) // Standard Error: 7_268 - .saturating_add(Weight::from_ref_time(4_537_902).saturating_mul(r.into())) + .saturating_add(Weight::from_parts(4_537_902, 0).saturating_mul(r.into())) .saturating_add(RocksDbWeight::get().reads(4_u64)) .saturating_add(RocksDbWeight::get().reads((1_u64).saturating_mul(r.into()))) .saturating_add(RocksDbWeight::get().writes(4_u64)) .saturating_add(RocksDbWeight::get().writes((1_u64).saturating_mul(r.into()))) - .saturating_add(Weight::from_proof_size(2676).saturating_mul(r.into())) + .saturating_add(Weight::from_parts(0, 2676).saturating_mul(r.into())) } /// Storage: Democracy VotingOf (r:2 w:2) /// Proof: Democracy VotingOf (max_values: None, max_size: Some(3795), added: 6270, mode: MaxEncodedLen) @@ -793,12 +793,12 @@ impl WeightInfo for () { // Minimum execution time: 19_758 nanoseconds. Weight::from_parts(21_641_793, 12540) // Standard Error: 6_889 - .saturating_add(Weight::from_ref_time(4_478_884).saturating_mul(r.into())) + .saturating_add(Weight::from_parts(4_478_884, 0).saturating_mul(r.into())) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().reads((1_u64).saturating_mul(r.into()))) .saturating_add(RocksDbWeight::get().writes(2_u64)) .saturating_add(RocksDbWeight::get().writes((1_u64).saturating_mul(r.into()))) - .saturating_add(Weight::from_proof_size(2676).saturating_mul(r.into())) + .saturating_add(Weight::from_parts(0, 2676).saturating_mul(r.into())) } /// Storage: Democracy PublicProps (r:0 w:1) /// Proof: Democracy PublicProps (max_values: Some(1), max_size: Some(16702), added: 17197, mode: MaxEncodedLen) @@ -807,7 +807,7 @@ impl WeightInfo for () { // Measured: `0` // Estimated: `0` // Minimum execution time: 2_844 nanoseconds. - Weight::from_ref_time(3_017_000) + Weight::from_parts(3_017_000, 0) .saturating_add(RocksDbWeight::get().writes(1_u64)) } /// Storage: Democracy VotingOf (r:1 w:1) @@ -824,7 +824,7 @@ impl WeightInfo for () { // Minimum execution time: 20_380 nanoseconds. Weight::from_parts(28_295_875, 12647) // Standard Error: 2_331 - .saturating_add(Weight::from_ref_time(67_348).saturating_mul(r.into())) + .saturating_add(Weight::from_parts(67_348, 0).saturating_mul(r.into())) .saturating_add(RocksDbWeight::get().reads(3_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) } @@ -842,7 +842,7 @@ impl WeightInfo for () { // Minimum execution time: 24_475 nanoseconds. Weight::from_parts(27_102_576, 12647) // Standard Error: 1_464 - .saturating_add(Weight::from_ref_time(128_921).saturating_mul(r.into())) + .saturating_add(Weight::from_parts(128_921, 0).saturating_mul(r.into())) .saturating_add(RocksDbWeight::get().reads(3_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) } @@ -858,7 +858,7 @@ impl WeightInfo for () { // Minimum execution time: 15_039 nanoseconds. Weight::from_parts(19_252_498, 8946) // Standard Error: 1_798 - .saturating_add(Weight::from_ref_time(131_855).saturating_mul(r.into())) + .saturating_add(Weight::from_parts(131_855, 0).saturating_mul(r.into())) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().writes(2_u64)) } @@ -874,7 +874,7 @@ impl WeightInfo for () { // Minimum execution time: 14_837 nanoseconds. Weight::from_parts(19_144_929, 8946) // Standard Error: 1_875 - .saturating_add(Weight::from_ref_time(136_819).saturating_mul(r.into())) + .saturating_add(Weight::from_parts(136_819, 0).saturating_mul(r.into())) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().writes(2_u64)) } diff --git a/frame/election-provider-multi-phase/src/mock.rs b/frame/election-provider-multi-phase/src/mock.rs index 2ceaa6c23..a46aa861e 100644 --- a/frame/election-provider-multi-phase/src/mock.rs +++ b/frame/election-provider-multi-phase/src/mock.rs @@ -363,11 +363,12 @@ impl MinerConfig for Runtime { fn solution_weight(v: u32, t: u32, a: u32, d: u32) -> Weight { match MockWeightInfo::get() { - MockedWeightInfo::Basic => Weight::from_ref_time( + MockedWeightInfo::Basic => Weight::from_parts( (10 as u64).saturating_add((5 as u64).saturating_mul(a as u64)), + 0, ), MockedWeightInfo::Complex => - Weight::from_ref_time((0 * v + 0 * t + 1000 * a + 0 * d) as u64), + Weight::from_parts((0 * v + 0 * t + 1000 * a + 0 * d) as u64, 0), MockedWeightInfo::Real => <() as multi_phase::weights::WeightInfo>::feasibility_check(v, t, a, d), } diff --git a/frame/election-provider-multi-phase/src/signed.rs b/frame/election-provider-multi-phase/src/signed.rs index d5d17e26b..75bbcfcf9 100644 --- a/frame/election-provider-multi-phase/src/signed.rs +++ b/frame/election-provider-multi-phase/src/signed.rs @@ -1254,7 +1254,7 @@ mod tests { #[test] fn cannot_consume_too_much_future_weight() { ExtBuilder::default() - .signed_weight(Weight::from_ref_time(40).set_proof_size(u64::MAX)) + .signed_weight(Weight::from_parts(40, u64::MAX)) .mock_weight_info(MockedWeightInfo::Basic) .build_and_execute(|| { roll_to_signed(); @@ -1268,16 +1268,16 @@ mod tests { raw.solution.unique_targets().len() as u32, ); // default solution will have 5 edges (5 * 5 + 10) - assert_eq!(solution_weight, Weight::from_ref_time(35)); + assert_eq!(solution_weight, Weight::from_parts(35, 0)); assert_eq!(raw.solution.voter_count(), 5); assert_eq!( ::SignedMaxWeight::get(), - Weight::from_ref_time(40).set_proof_size(u64::MAX) + Weight::from_parts(40, u64::MAX) ); assert_ok!(MultiPhase::submit(RuntimeOrigin::signed(99), Box::new(raw.clone()))); - ::set(Weight::from_ref_time(30).set_proof_size(u64::MAX)); + ::set(Weight::from_parts(30, u64::MAX)); // note: resubmitting the same solution is technically okay as long as the queue has // space. diff --git a/frame/election-provider-multi-phase/src/unsigned.rs b/frame/election-provider-multi-phase/src/unsigned.rs index cff1e01d9..3d80f8f4d 100644 --- a/frame/election-provider-multi-phase/src/unsigned.rs +++ b/frame/election-provider-multi-phase/src/unsigned.rs @@ -701,345 +701,177 @@ mod max_weight { let w = SolutionOrSnapshotSize { voters: 10, targets: 0 }; MockWeightInfo::set(crate::mock::MockedWeightInfo::Complex); assert_eq!( - Miner::::maximum_voter_for_weight( - 0, - w, - Weight::zero().set_proof_size(u64::MAX) - ), + Miner::::maximum_voter_for_weight(0, w, Weight::from_parts(0, u64::MAX)), 0 ); assert_eq!( - Miner::::maximum_voter_for_weight( - 0, - w, - Weight::from_ref_time(1).set_proof_size(u64::MAX) - ), + Miner::::maximum_voter_for_weight(0, w, Weight::from_parts(1, u64::MAX)), 0 ); assert_eq!( - Miner::::maximum_voter_for_weight( - 0, - w, - Weight::from_ref_time(999).set_proof_size(u64::MAX) - ), + Miner::::maximum_voter_for_weight(0, w, Weight::from_parts(999, u64::MAX)), 0 ); assert_eq!( - Miner::::maximum_voter_for_weight( - 0, - w, - Weight::from_ref_time(1000).set_proof_size(u64::MAX) - ), + Miner::::maximum_voter_for_weight(0, w, Weight::from_parts(1000, u64::MAX)), 1 ); assert_eq!( - Miner::::maximum_voter_for_weight( - 0, - w, - Weight::from_ref_time(1001).set_proof_size(u64::MAX) - ), + Miner::::maximum_voter_for_weight(0, w, Weight::from_parts(1001, u64::MAX)), 1 ); assert_eq!( - Miner::::maximum_voter_for_weight( - 0, - w, - Weight::from_ref_time(1990).set_proof_size(u64::MAX) - ), + Miner::::maximum_voter_for_weight(0, w, Weight::from_parts(1990, u64::MAX)), 1 ); assert_eq!( - Miner::::maximum_voter_for_weight( - 0, - w, - Weight::from_ref_time(1999).set_proof_size(u64::MAX) - ), + Miner::::maximum_voter_for_weight(0, w, Weight::from_parts(1999, u64::MAX)), 1 ); assert_eq!( - Miner::::maximum_voter_for_weight( - 0, - w, - Weight::from_ref_time(2000).set_proof_size(u64::MAX) - ), + Miner::::maximum_voter_for_weight(0, w, Weight::from_parts(2000, u64::MAX)), 2 ); assert_eq!( - Miner::::maximum_voter_for_weight( - 0, - w, - Weight::from_ref_time(2001).set_proof_size(u64::MAX) - ), + Miner::::maximum_voter_for_weight(0, w, Weight::from_parts(2001, u64::MAX)), 2 ); assert_eq!( - Miner::::maximum_voter_for_weight( - 0, - w, - Weight::from_ref_time(2010).set_proof_size(u64::MAX) - ), + Miner::::maximum_voter_for_weight(0, w, Weight::from_parts(2010, u64::MAX)), 2 ); assert_eq!( - Miner::::maximum_voter_for_weight( - 0, - w, - Weight::from_ref_time(2990).set_proof_size(u64::MAX) - ), + Miner::::maximum_voter_for_weight(0, w, Weight::from_parts(2990, u64::MAX)), 2 ); assert_eq!( - Miner::::maximum_voter_for_weight( - 0, - w, - Weight::from_ref_time(2999).set_proof_size(u64::MAX) - ), + Miner::::maximum_voter_for_weight(0, w, Weight::from_parts(2999, u64::MAX)), 2 ); assert_eq!( - Miner::::maximum_voter_for_weight( - 0, - w, - Weight::from_ref_time(3000).set_proof_size(u64::MAX) - ), + Miner::::maximum_voter_for_weight(0, w, Weight::from_parts(3000, u64::MAX)), 3 ); assert_eq!( - Miner::::maximum_voter_for_weight( - 0, - w, - Weight::from_ref_time(3333).set_proof_size(u64::MAX) - ), + Miner::::maximum_voter_for_weight(0, w, Weight::from_parts(3333, u64::MAX)), 3 ); assert_eq!( - Miner::::maximum_voter_for_weight( - 0, - w, - Weight::from_ref_time(5500).set_proof_size(u64::MAX) - ), + Miner::::maximum_voter_for_weight(0, w, Weight::from_parts(5500, u64::MAX)), 5 ); assert_eq!( - Miner::::maximum_voter_for_weight( - 0, - w, - Weight::from_ref_time(7777).set_proof_size(u64::MAX) - ), + Miner::::maximum_voter_for_weight(0, w, Weight::from_parts(7777, u64::MAX)), 7 ); assert_eq!( - Miner::::maximum_voter_for_weight( - 0, - w, - Weight::from_ref_time(9999).set_proof_size(u64::MAX) - ), + Miner::::maximum_voter_for_weight(0, w, Weight::from_parts(9999, u64::MAX)), 9 ); assert_eq!( - Miner::::maximum_voter_for_weight( - 0, - w, - Weight::from_ref_time(10_000).set_proof_size(u64::MAX) - ), + Miner::::maximum_voter_for_weight(0, w, Weight::from_parts(10_000, u64::MAX)), 10 ); assert_eq!( - Miner::::maximum_voter_for_weight( - 0, - w, - Weight::from_ref_time(10_999).set_proof_size(u64::MAX) - ), + Miner::::maximum_voter_for_weight(0, w, Weight::from_parts(10_999, u64::MAX)), 10 ); assert_eq!( - Miner::::maximum_voter_for_weight( - 0, - w, - Weight::from_ref_time(11_000).set_proof_size(u64::MAX) - ), + Miner::::maximum_voter_for_weight(0, w, Weight::from_parts(11_000, u64::MAX)), 10 ); assert_eq!( - Miner::::maximum_voter_for_weight( - 0, - w, - Weight::from_ref_time(22_000).set_proof_size(u64::MAX) - ), + Miner::::maximum_voter_for_weight(0, w, Weight::from_parts(22_000, u64::MAX)), 10 ); let w = SolutionOrSnapshotSize { voters: 1, targets: 0 }; assert_eq!( - Miner::::maximum_voter_for_weight( - 0, - w, - Weight::from_ref_time(0).set_proof_size(u64::MAX) - ), + Miner::::maximum_voter_for_weight(0, w, Weight::from_parts(0, u64::MAX)), 0 ); assert_eq!( - Miner::::maximum_voter_for_weight( - 0, - w, - Weight::from_ref_time(1).set_proof_size(u64::MAX) - ), + Miner::::maximum_voter_for_weight(0, w, Weight::from_parts(1, u64::MAX)), 0 ); assert_eq!( - Miner::::maximum_voter_for_weight( - 0, - w, - Weight::from_ref_time(999).set_proof_size(u64::MAX) - ), + Miner::::maximum_voter_for_weight(0, w, Weight::from_parts(999, u64::MAX)), 0 ); assert_eq!( - Miner::::maximum_voter_for_weight( - 0, - w, - Weight::from_ref_time(1000).set_proof_size(u64::MAX) - ), + Miner::::maximum_voter_for_weight(0, w, Weight::from_parts(1000, u64::MAX)), 1 ); assert_eq!( - Miner::::maximum_voter_for_weight( - 0, - w, - Weight::from_ref_time(1001).set_proof_size(u64::MAX) - ), + Miner::::maximum_voter_for_weight(0, w, Weight::from_parts(1001, u64::MAX)), 1 ); assert_eq!( - Miner::::maximum_voter_for_weight( - 0, - w, - Weight::from_ref_time(1990).set_proof_size(u64::MAX) - ), + Miner::::maximum_voter_for_weight(0, w, Weight::from_parts(1990, u64::MAX)), 1 ); assert_eq!( - Miner::::maximum_voter_for_weight( - 0, - w, - Weight::from_ref_time(1999).set_proof_size(u64::MAX) - ), + Miner::::maximum_voter_for_weight(0, w, Weight::from_parts(1999, u64::MAX)), 1 ); assert_eq!( - Miner::::maximum_voter_for_weight( - 0, - w, - Weight::from_ref_time(2000).set_proof_size(u64::MAX) - ), + Miner::::maximum_voter_for_weight(0, w, Weight::from_parts(2000, u64::MAX)), 1 ); assert_eq!( - Miner::::maximum_voter_for_weight( - 0, - w, - Weight::from_ref_time(2001).set_proof_size(u64::MAX) - ), + Miner::::maximum_voter_for_weight(0, w, Weight::from_parts(2001, u64::MAX)), 1 ); assert_eq!( - Miner::::maximum_voter_for_weight( - 0, - w, - Weight::from_ref_time(2010).set_proof_size(u64::MAX) - ), + Miner::::maximum_voter_for_weight(0, w, Weight::from_parts(2010, u64::MAX)), 1 ); assert_eq!( - Miner::::maximum_voter_for_weight( - 0, - w, - Weight::from_ref_time(3333).set_proof_size(u64::MAX) - ), + Miner::::maximum_voter_for_weight(0, w, Weight::from_parts(3333, u64::MAX)), 1 ); let w = SolutionOrSnapshotSize { voters: 2, targets: 0 }; assert_eq!( - Miner::::maximum_voter_for_weight( - 0, - w, - Weight::from_ref_time(0).set_proof_size(u64::MAX) - ), + Miner::::maximum_voter_for_weight(0, w, Weight::from_parts(0, u64::MAX)), 0 ); assert_eq!( - Miner::::maximum_voter_for_weight( - 0, - w, - Weight::from_ref_time(1).set_proof_size(u64::MAX) - ), + Miner::::maximum_voter_for_weight(0, w, Weight::from_parts(1, u64::MAX)), 0 ); assert_eq!( - Miner::::maximum_voter_for_weight( - 0, - w, - Weight::from_ref_time(999).set_proof_size(u64::MAX) - ), + Miner::::maximum_voter_for_weight(0, w, Weight::from_parts(999, u64::MAX)), 0 ); assert_eq!( - Miner::::maximum_voter_for_weight( - 0, - w, - Weight::from_ref_time(1000).set_proof_size(u64::MAX) - ), + Miner::::maximum_voter_for_weight(0, w, Weight::from_parts(1000, u64::MAX)), 1 ); assert_eq!( - Miner::::maximum_voter_for_weight( - 0, - w, - Weight::from_ref_time(1001).set_proof_size(u64::MAX) - ), + Miner::::maximum_voter_for_weight(0, w, Weight::from_parts(1001, u64::MAX)), 1 ); assert_eq!( - Miner::::maximum_voter_for_weight( - 0, - w, - Weight::from_ref_time(1999).set_proof_size(u64::MAX) - ), + Miner::::maximum_voter_for_weight(0, w, Weight::from_parts(1999, u64::MAX)), 1 ); assert_eq!( - Miner::::maximum_voter_for_weight( - 0, - w, - Weight::from_ref_time(2000).set_proof_size(u64::MAX) - ), + Miner::::maximum_voter_for_weight(0, w, Weight::from_parts(2000, u64::MAX)), 2 ); assert_eq!( - Miner::::maximum_voter_for_weight( - 0, - w, - Weight::from_ref_time(2001).set_proof_size(u64::MAX) - ), + Miner::::maximum_voter_for_weight(0, w, Weight::from_parts(2001, u64::MAX)), 2 ); assert_eq!( - Miner::::maximum_voter_for_weight( - 0, - w, - Weight::from_ref_time(2010).set_proof_size(u64::MAX) - ), + Miner::::maximum_voter_for_weight(0, w, Weight::from_parts(2010, u64::MAX)), 2 ); assert_eq!( - Miner::::maximum_voter_for_weight( - 0, - w, - Weight::from_ref_time(3333).set_proof_size(u64::MAX) - ), + Miner::::maximum_voter_for_weight(0, w, Weight::from_parts(3333, u64::MAX)), 2 ); } @@ -1340,7 +1172,7 @@ mod tests { #[test] fn miner_trims_weight() { ExtBuilder::default() - .miner_weight(Weight::from_ref_time(100).set_proof_size(u64::MAX)) + .miner_weight(Weight::from_parts(100, u64::MAX)) .mock_weight_info(crate::mock::MockedWeightInfo::Basic) .build_and_execute(|| { roll_to_unsigned(); @@ -1354,11 +1186,11 @@ mod tests { raw.solution.unique_targets().len() as u32, ); // default solution will have 5 edges (5 * 5 + 10) - assert_eq!(solution_weight, Weight::from_ref_time(35)); + assert_eq!(solution_weight, Weight::from_parts(35, 0)); assert_eq!(raw.solution.voter_count(), 5); // now reduce the max weight - ::set(Weight::from_ref_time(25).set_proof_size(u64::MAX)); + ::set(Weight::from_parts(25, u64::MAX)); let (raw, witness) = MultiPhase::mine_solution().unwrap(); let solution_weight = ::solution_weight( @@ -1368,7 +1200,7 @@ mod tests { raw.solution.unique_targets().len() as u32, ); // default solution will have 5 edges (5 * 5 + 10) - assert_eq!(solution_weight, Weight::from_ref_time(25)); + assert_eq!(solution_weight, Weight::from_parts(25, 0)); assert_eq!(raw.solution.voter_count(), 3); }) } diff --git a/frame/election-provider-multi-phase/src/weights.rs b/frame/election-provider-multi-phase/src/weights.rs index f3ea218e4..90be98ece 100644 --- a/frame/election-provider-multi-phase/src/weights.rs +++ b/frame/election-provider-multi-phase/src/weights.rs @@ -150,9 +150,9 @@ impl WeightInfo for SubstrateWeight { // Measured: `0` // Estimated: `0` // Minimum execution time: 215_168 nanoseconds. - Weight::from_ref_time(219_887_000) + Weight::from_parts(219_887_000, 0) // Standard Error: 1_444 - .saturating_add(Weight::from_ref_time(146_388).saturating_mul(v.into())) + .saturating_add(Weight::from_parts(146_388, 0).saturating_mul(v.into())) .saturating_add(T::DbWeight::get().writes(3_u64)) } /// Storage: ElectionProviderMultiPhase SignedSubmissionIndices (r:1 w:1) @@ -182,13 +182,13 @@ impl WeightInfo for SubstrateWeight { // Minimum execution time: 268_021 nanoseconds. Weight::from_parts(72_491_937, 9540) // Standard Error: 2_910 - .saturating_add(Weight::from_ref_time(303_955).saturating_mul(a.into())) + .saturating_add(Weight::from_parts(303_955, 0).saturating_mul(a.into())) // Standard Error: 4_363 - .saturating_add(Weight::from_ref_time(167_369).saturating_mul(d.into())) + .saturating_add(Weight::from_parts(167_369, 0).saturating_mul(d.into())) .saturating_add(T::DbWeight::get().reads(7_u64)) .saturating_add(T::DbWeight::get().writes(8_u64)) - .saturating_add(Weight::from_proof_size(6912).saturating_mul(a.into())) - .saturating_add(Weight::from_proof_size(441).saturating_mul(d.into())) + .saturating_add(Weight::from_parts(0, 6912).saturating_mul(a.into())) + .saturating_add(Weight::from_parts(0, 441).saturating_mul(d.into())) } /// Storage: ElectionProviderMultiPhase CurrentPhase (r:1 w:0) /// Proof Skipped: ElectionProviderMultiPhase CurrentPhase (max_values: Some(1), max_size: None, mode: Measured) @@ -236,13 +236,13 @@ impl WeightInfo for SubstrateWeight { // Minimum execution time: 4_425_457 nanoseconds. Weight::from_parts(4_445_889_000, 5222) // Standard Error: 13_250 - .saturating_add(Weight::from_ref_time(48_844).saturating_mul(v.into())) + .saturating_add(Weight::from_parts(48_844, 0).saturating_mul(v.into())) // Standard Error: 39_266 - .saturating_add(Weight::from_ref_time(4_144_034).saturating_mul(a.into())) + .saturating_add(Weight::from_parts(4_144_034, 0).saturating_mul(a.into())) .saturating_add(T::DbWeight::get().reads(7_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) - .saturating_add(Weight::from_proof_size(3871).saturating_mul(v.into())) - .saturating_add(Weight::from_proof_size(224).saturating_mul(t.into())) + .saturating_add(Weight::from_parts(0, 3871).saturating_mul(v.into())) + .saturating_add(Weight::from_parts(0, 224).saturating_mul(t.into())) } /// Storage: ElectionProviderMultiPhase Round (r:1 w:0) /// Proof Skipped: ElectionProviderMultiPhase Round (max_values: Some(1), max_size: None, mode: Measured) @@ -263,12 +263,12 @@ impl WeightInfo for SubstrateWeight { // Minimum execution time: 3_812_071 nanoseconds. Weight::from_parts(3_826_375_000, 2884) // Standard Error: 11_601 - .saturating_add(Weight::from_ref_time(145_309).saturating_mul(v.into())) + .saturating_add(Weight::from_parts(145_309, 0).saturating_mul(v.into())) // Standard Error: 34_378 - .saturating_add(Weight::from_ref_time(3_223_977).saturating_mul(a.into())) + .saturating_add(Weight::from_parts(3_223_977, 0).saturating_mul(a.into())) .saturating_add(T::DbWeight::get().reads(4_u64)) - .saturating_add(Weight::from_proof_size(2212).saturating_mul(v.into())) - .saturating_add(Weight::from_proof_size(128).saturating_mul(t.into())) + .saturating_add(Weight::from_parts(0, 2212).saturating_mul(v.into())) + .saturating_add(Weight::from_parts(0, 128).saturating_mul(t.into())) } } @@ -361,9 +361,9 @@ impl WeightInfo for () { // Measured: `0` // Estimated: `0` // Minimum execution time: 215_168 nanoseconds. - Weight::from_ref_time(219_887_000) + Weight::from_parts(219_887_000, 0) // Standard Error: 1_444 - .saturating_add(Weight::from_ref_time(146_388).saturating_mul(v.into())) + .saturating_add(Weight::from_parts(146_388, 0).saturating_mul(v.into())) .saturating_add(RocksDbWeight::get().writes(3_u64)) } /// Storage: ElectionProviderMultiPhase SignedSubmissionIndices (r:1 w:1) @@ -393,13 +393,13 @@ impl WeightInfo for () { // Minimum execution time: 268_021 nanoseconds. Weight::from_parts(72_491_937, 9540) // Standard Error: 2_910 - .saturating_add(Weight::from_ref_time(303_955).saturating_mul(a.into())) + .saturating_add(Weight::from_parts(303_955, 0).saturating_mul(a.into())) // Standard Error: 4_363 - .saturating_add(Weight::from_ref_time(167_369).saturating_mul(d.into())) + .saturating_add(Weight::from_parts(167_369, 0).saturating_mul(d.into())) .saturating_add(RocksDbWeight::get().reads(7_u64)) .saturating_add(RocksDbWeight::get().writes(8_u64)) - .saturating_add(Weight::from_proof_size(6912).saturating_mul(a.into())) - .saturating_add(Weight::from_proof_size(441).saturating_mul(d.into())) + .saturating_add(Weight::from_parts(0, 6912).saturating_mul(a.into())) + .saturating_add(Weight::from_parts(0, 441).saturating_mul(d.into())) } /// Storage: ElectionProviderMultiPhase CurrentPhase (r:1 w:0) /// Proof Skipped: ElectionProviderMultiPhase CurrentPhase (max_values: Some(1), max_size: None, mode: Measured) @@ -447,13 +447,13 @@ impl WeightInfo for () { // Minimum execution time: 4_425_457 nanoseconds. Weight::from_parts(4_445_889_000, 5222) // Standard Error: 13_250 - .saturating_add(Weight::from_ref_time(48_844).saturating_mul(v.into())) + .saturating_add(Weight::from_parts(48_844, 0).saturating_mul(v.into())) // Standard Error: 39_266 - .saturating_add(Weight::from_ref_time(4_144_034).saturating_mul(a.into())) + .saturating_add(Weight::from_parts(4_144_034, 0).saturating_mul(a.into())) .saturating_add(RocksDbWeight::get().reads(7_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) - .saturating_add(Weight::from_proof_size(3871).saturating_mul(v.into())) - .saturating_add(Weight::from_proof_size(224).saturating_mul(t.into())) + .saturating_add(Weight::from_parts(0, 3871).saturating_mul(v.into())) + .saturating_add(Weight::from_parts(0, 224).saturating_mul(t.into())) } /// Storage: ElectionProviderMultiPhase Round (r:1 w:0) /// Proof Skipped: ElectionProviderMultiPhase Round (max_values: Some(1), max_size: None, mode: Measured) @@ -474,11 +474,11 @@ impl WeightInfo for () { // Minimum execution time: 3_812_071 nanoseconds. Weight::from_parts(3_826_375_000, 2884) // Standard Error: 11_601 - .saturating_add(Weight::from_ref_time(145_309).saturating_mul(v.into())) + .saturating_add(Weight::from_parts(145_309, 0).saturating_mul(v.into())) // Standard Error: 34_378 - .saturating_add(Weight::from_ref_time(3_223_977).saturating_mul(a.into())) + .saturating_add(Weight::from_parts(3_223_977, 0).saturating_mul(a.into())) .saturating_add(RocksDbWeight::get().reads(4_u64)) - .saturating_add(Weight::from_proof_size(2212).saturating_mul(v.into())) - .saturating_add(Weight::from_proof_size(128).saturating_mul(t.into())) + .saturating_add(Weight::from_parts(0, 2212).saturating_mul(v.into())) + .saturating_add(Weight::from_parts(0, 128).saturating_mul(t.into())) } } diff --git a/frame/election-provider-support/src/weights.rs b/frame/election-provider-support/src/weights.rs index fd4db1374..addb6ad8d 100644 --- a/frame/election-provider-support/src/weights.rs +++ b/frame/election-provider-support/src/weights.rs @@ -53,43 +53,43 @@ pub trait WeightInfo { pub struct SubstrateWeight(PhantomData); impl WeightInfo for SubstrateWeight { fn phragmen(v: u32, t: u32, d: u32, ) -> Weight { - Weight::from_ref_time(0 as u64) + Weight::from_parts(0 as u64, 0) // Standard Error: 667_000 - .saturating_add(Weight::from_ref_time(32_973_000 as u64).saturating_mul(v as u64)) + .saturating_add(Weight::from_parts(32_973_000 as u64, 0).saturating_mul(v as u64)) // Standard Error: 1_334_000 - .saturating_add(Weight::from_ref_time(1_334_000 as u64).saturating_mul(t as u64)) + .saturating_add(Weight::from_parts(1_334_000 as u64, 0).saturating_mul(t as u64)) // Standard Error: 60_644_000 - .saturating_add(Weight::from_ref_time(2_636_364_000 as u64).saturating_mul(d as u64)) + .saturating_add(Weight::from_parts(2_636_364_000 as u64, 0).saturating_mul(d as u64)) } fn phragmms(v: u32, t: u32, d: u32, ) -> Weight { - Weight::from_ref_time(0 as u64) + Weight::from_parts(0 as u64, 0) // Standard Error: 73_000 - .saturating_add(Weight::from_ref_time(21_073_000 as u64).saturating_mul(v as u64)) + .saturating_add(Weight::from_parts(21_073_000 as u64, 0).saturating_mul(v as u64)) // Standard Error: 146_000 - .saturating_add(Weight::from_ref_time(65_000 as u64).saturating_mul(t as u64)) + .saturating_add(Weight::from_parts(65_000 as u64, 0).saturating_mul(t as u64)) // Standard Error: 6_649_000 - .saturating_add(Weight::from_ref_time(1_711_424_000 as u64).saturating_mul(d as u64)) + .saturating_add(Weight::from_parts(1_711_424_000 as u64, 0).saturating_mul(d as u64)) } } // For backwards compatibility and tests impl WeightInfo for () { fn phragmen(v: u32, t: u32, d: u32, ) -> Weight { - Weight::from_ref_time(0 as u64) + Weight::from_parts(0 as u64, 0) // Standard Error: 667_000 - .saturating_add(Weight::from_ref_time(32_973_000 as u64).saturating_mul(v as u64)) + .saturating_add(Weight::from_parts(32_973_000 as u64, 0).saturating_mul(v as u64)) // Standard Error: 1_334_000 - .saturating_add(Weight::from_ref_time(1_334_000 as u64).saturating_mul(t as u64)) + .saturating_add(Weight::from_parts(1_334_000 as u64, 0).saturating_mul(t as u64)) // Standard Error: 60_644_000 - .saturating_add(Weight::from_ref_time(2_636_364_000 as u64).saturating_mul(d as u64)) + .saturating_add(Weight::from_parts(2_636_364_000 as u64, 0).saturating_mul(d as u64)) } fn phragmms(v: u32, t: u32, d: u32, ) -> Weight { - Weight::from_ref_time(0 as u64) + Weight::from_parts(0 as u64, 0) // Standard Error: 73_000 - .saturating_add(Weight::from_ref_time(21_073_000 as u64).saturating_mul(v as u64)) + .saturating_add(Weight::from_parts(21_073_000 as u64, 0).saturating_mul(v as u64)) // Standard Error: 146_000 - .saturating_add(Weight::from_ref_time(65_000 as u64).saturating_mul(t as u64)) + .saturating_add(Weight::from_parts(65_000 as u64, 0).saturating_mul(t as u64)) // Standard Error: 6_649_000 - .saturating_add(Weight::from_ref_time(1_711_424_000 as u64).saturating_mul(d as u64)) + .saturating_add(Weight::from_parts(1_711_424_000 as u64, 0).saturating_mul(d as u64)) } } diff --git a/frame/elections-phragmen/src/weights.rs b/frame/elections-phragmen/src/weights.rs index 24ab3bc15..bbe66d529 100644 --- a/frame/elections-phragmen/src/weights.rs +++ b/frame/elections-phragmen/src/weights.rs @@ -84,10 +84,10 @@ impl WeightInfo for SubstrateWeight { // Minimum execution time: 27_362 nanoseconds. Weight::from_parts(28_497_963, 9726) // Standard Error: 3_968 - .saturating_add(Weight::from_ref_time(176_840).saturating_mul(v.into())) + .saturating_add(Weight::from_parts(176_840, 0).saturating_mul(v.into())) .saturating_add(T::DbWeight::get().reads(5_u64)) .saturating_add(T::DbWeight::get().writes(2_u64)) - .saturating_add(Weight::from_proof_size(320).saturating_mul(v.into())) + .saturating_add(Weight::from_parts(0, 320).saturating_mul(v.into())) } /// Storage: Elections Candidates (r:1 w:0) /// Proof Skipped: Elections Candidates (max_values: Some(1), max_size: None, mode: Measured) @@ -107,10 +107,10 @@ impl WeightInfo for SubstrateWeight { // Minimum execution time: 37_120 nanoseconds. Weight::from_parts(38_455_302, 9598) // Standard Error: 5_478 - .saturating_add(Weight::from_ref_time(219_678).saturating_mul(v.into())) + .saturating_add(Weight::from_parts(219_678, 0).saturating_mul(v.into())) .saturating_add(T::DbWeight::get().reads(5_u64)) .saturating_add(T::DbWeight::get().writes(2_u64)) - .saturating_add(Weight::from_proof_size(320).saturating_mul(v.into())) + .saturating_add(Weight::from_parts(0, 320).saturating_mul(v.into())) } /// Storage: Elections Candidates (r:1 w:0) /// Proof Skipped: Elections Candidates (max_values: Some(1), max_size: None, mode: Measured) @@ -130,10 +130,10 @@ impl WeightInfo for SubstrateWeight { // Minimum execution time: 36_928 nanoseconds. Weight::from_parts(38_334_669, 9726) // Standard Error: 5_271 - .saturating_add(Weight::from_ref_time(232_355).saturating_mul(v.into())) + .saturating_add(Weight::from_parts(232_355, 0).saturating_mul(v.into())) .saturating_add(T::DbWeight::get().reads(5_u64)) .saturating_add(T::DbWeight::get().writes(2_u64)) - .saturating_add(Weight::from_proof_size(320).saturating_mul(v.into())) + .saturating_add(Weight::from_parts(0, 320).saturating_mul(v.into())) } /// Storage: Elections Voting (r:1 w:1) /// Proof Skipped: Elections Voting (max_values: None, max_size: None, mode: Measured) @@ -162,10 +162,10 @@ impl WeightInfo for SubstrateWeight { // Minimum execution time: 31_864 nanoseconds. Weight::from_parts(33_490_161, 6576) // Standard Error: 2_643 - .saturating_add(Weight::from_ref_time(158_386).saturating_mul(c.into())) + .saturating_add(Weight::from_parts(158_386, 0).saturating_mul(c.into())) .saturating_add(T::DbWeight::get().reads(3_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) - .saturating_add(Weight::from_proof_size(144).saturating_mul(c.into())) + .saturating_add(Weight::from_parts(0, 144).saturating_mul(c.into())) } /// Storage: Elections Candidates (r:1 w:1) /// Proof Skipped: Elections Candidates (max_values: Some(1), max_size: None, mode: Measured) @@ -177,10 +177,10 @@ impl WeightInfo for SubstrateWeight { // Minimum execution time: 27_292 nanoseconds. Weight::from_parts(28_364_955, 844) // Standard Error: 1_335 - .saturating_add(Weight::from_ref_time(78_086).saturating_mul(c.into())) + .saturating_add(Weight::from_parts(78_086, 0).saturating_mul(c.into())) .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) - .saturating_add(Weight::from_proof_size(48).saturating_mul(c.into())) + .saturating_add(Weight::from_parts(0, 48).saturating_mul(c.into())) } /// Storage: Elections Members (r:1 w:1) /// Proof Skipped: Elections Members (max_values: Some(1), max_size: None, mode: Measured) @@ -219,7 +219,7 @@ impl WeightInfo for SubstrateWeight { // Measured: `0` // Estimated: `0` // Minimum execution time: 2_000_000_000 nanoseconds. - Weight::from_ref_time(2_000_000_000_000) + Weight::from_parts(2_000_000_000_000, 0) } /// Storage: Elections Members (r:1 w:1) /// Proof Skipped: Elections Members (max_values: Some(1), max_size: None, mode: Measured) @@ -263,11 +263,11 @@ impl WeightInfo for SubstrateWeight { // Minimum execution time: 14_934_185 nanoseconds. Weight::from_parts(15_014_057_000, 8448) // Standard Error: 245_588 - .saturating_add(Weight::from_ref_time(35_586_946).saturating_mul(v.into())) + .saturating_add(Weight::from_parts(35_586_946, 0).saturating_mul(v.into())) .saturating_add(T::DbWeight::get().reads(4_u64)) .saturating_add(T::DbWeight::get().reads((3_u64).saturating_mul(v.into()))) .saturating_add(T::DbWeight::get().writes((3_u64).saturating_mul(v.into()))) - .saturating_add(Weight::from_proof_size(12352).saturating_mul(v.into())) + .saturating_add(Weight::from_parts(0, 12352).saturating_mul(v.into())) } /// Storage: Elections Candidates (r:1 w:1) /// Proof Skipped: Elections Candidates (max_values: Some(1), max_size: None, mode: Measured) @@ -297,17 +297,17 @@ impl WeightInfo for SubstrateWeight { // Minimum execution time: 1_273_671 nanoseconds. Weight::from_parts(1_279_716_000, 330033) // Standard Error: 543_277 - .saturating_add(Weight::from_ref_time(20_613_753).saturating_mul(v.into())) + .saturating_add(Weight::from_parts(20_613_753, 0).saturating_mul(v.into())) // Standard Error: 34_857 - .saturating_add(Weight::from_ref_time(688_354).saturating_mul(e.into())) + .saturating_add(Weight::from_parts(688_354, 0).saturating_mul(e.into())) .saturating_add(T::DbWeight::get().reads(21_u64)) .saturating_add(T::DbWeight::get().reads((1_u64).saturating_mul(c.into()))) .saturating_add(T::DbWeight::get().reads((1_u64).saturating_mul(v.into()))) .saturating_add(T::DbWeight::get().writes(6_u64)) .saturating_add(T::DbWeight::get().writes((1_u64).saturating_mul(c.into()))) - .saturating_add(Weight::from_proof_size(5229).saturating_mul(v.into())) - .saturating_add(Weight::from_proof_size(89).saturating_mul(e.into())) - .saturating_add(Weight::from_proof_size(2135).saturating_mul(c.into())) + .saturating_add(Weight::from_parts(0, 5229).saturating_mul(v.into())) + .saturating_add(Weight::from_parts(0, 89).saturating_mul(e.into())) + .saturating_add(Weight::from_parts(0, 2135).saturating_mul(c.into())) } } @@ -331,10 +331,10 @@ impl WeightInfo for () { // Minimum execution time: 27_362 nanoseconds. Weight::from_parts(28_497_963, 9726) // Standard Error: 3_968 - .saturating_add(Weight::from_ref_time(176_840).saturating_mul(v.into())) + .saturating_add(Weight::from_parts(176_840, 0).saturating_mul(v.into())) .saturating_add(RocksDbWeight::get().reads(5_u64)) .saturating_add(RocksDbWeight::get().writes(2_u64)) - .saturating_add(Weight::from_proof_size(320).saturating_mul(v.into())) + .saturating_add(Weight::from_parts(0, 320).saturating_mul(v.into())) } /// Storage: Elections Candidates (r:1 w:0) /// Proof Skipped: Elections Candidates (max_values: Some(1), max_size: None, mode: Measured) @@ -354,10 +354,10 @@ impl WeightInfo for () { // Minimum execution time: 37_120 nanoseconds. Weight::from_parts(38_455_302, 9598) // Standard Error: 5_478 - .saturating_add(Weight::from_ref_time(219_678).saturating_mul(v.into())) + .saturating_add(Weight::from_parts(219_678, 0).saturating_mul(v.into())) .saturating_add(RocksDbWeight::get().reads(5_u64)) .saturating_add(RocksDbWeight::get().writes(2_u64)) - .saturating_add(Weight::from_proof_size(320).saturating_mul(v.into())) + .saturating_add(Weight::from_parts(0, 320).saturating_mul(v.into())) } /// Storage: Elections Candidates (r:1 w:0) /// Proof Skipped: Elections Candidates (max_values: Some(1), max_size: None, mode: Measured) @@ -377,10 +377,10 @@ impl WeightInfo for () { // Minimum execution time: 36_928 nanoseconds. Weight::from_parts(38_334_669, 9726) // Standard Error: 5_271 - .saturating_add(Weight::from_ref_time(232_355).saturating_mul(v.into())) + .saturating_add(Weight::from_parts(232_355, 0).saturating_mul(v.into())) .saturating_add(RocksDbWeight::get().reads(5_u64)) .saturating_add(RocksDbWeight::get().writes(2_u64)) - .saturating_add(Weight::from_proof_size(320).saturating_mul(v.into())) + .saturating_add(Weight::from_parts(0, 320).saturating_mul(v.into())) } /// Storage: Elections Voting (r:1 w:1) /// Proof Skipped: Elections Voting (max_values: None, max_size: None, mode: Measured) @@ -409,10 +409,10 @@ impl WeightInfo for () { // Minimum execution time: 31_864 nanoseconds. Weight::from_parts(33_490_161, 6576) // Standard Error: 2_643 - .saturating_add(Weight::from_ref_time(158_386).saturating_mul(c.into())) + .saturating_add(Weight::from_parts(158_386, 0).saturating_mul(c.into())) .saturating_add(RocksDbWeight::get().reads(3_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) - .saturating_add(Weight::from_proof_size(144).saturating_mul(c.into())) + .saturating_add(Weight::from_parts(0, 144).saturating_mul(c.into())) } /// Storage: Elections Candidates (r:1 w:1) /// Proof Skipped: Elections Candidates (max_values: Some(1), max_size: None, mode: Measured) @@ -424,10 +424,10 @@ impl WeightInfo for () { // Minimum execution time: 27_292 nanoseconds. Weight::from_parts(28_364_955, 844) // Standard Error: 1_335 - .saturating_add(Weight::from_ref_time(78_086).saturating_mul(c.into())) + .saturating_add(Weight::from_parts(78_086, 0).saturating_mul(c.into())) .saturating_add(RocksDbWeight::get().reads(1_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) - .saturating_add(Weight::from_proof_size(48).saturating_mul(c.into())) + .saturating_add(Weight::from_parts(0, 48).saturating_mul(c.into())) } /// Storage: Elections Members (r:1 w:1) /// Proof Skipped: Elections Members (max_values: Some(1), max_size: None, mode: Measured) @@ -466,7 +466,7 @@ impl WeightInfo for () { // Measured: `0` // Estimated: `0` // Minimum execution time: 2_000_000_000 nanoseconds. - Weight::from_ref_time(2_000_000_000_000) + Weight::from_parts(2_000_000_000_000, 0) } /// Storage: Elections Members (r:1 w:1) /// Proof Skipped: Elections Members (max_values: Some(1), max_size: None, mode: Measured) @@ -510,11 +510,11 @@ impl WeightInfo for () { // Minimum execution time: 14_934_185 nanoseconds. Weight::from_parts(15_014_057_000, 8448) // Standard Error: 245_588 - .saturating_add(Weight::from_ref_time(35_586_946).saturating_mul(v.into())) + .saturating_add(Weight::from_parts(35_586_946, 0).saturating_mul(v.into())) .saturating_add(RocksDbWeight::get().reads(4_u64)) .saturating_add(RocksDbWeight::get().reads((3_u64).saturating_mul(v.into()))) .saturating_add(RocksDbWeight::get().writes((3_u64).saturating_mul(v.into()))) - .saturating_add(Weight::from_proof_size(12352).saturating_mul(v.into())) + .saturating_add(Weight::from_parts(0, 12352).saturating_mul(v.into())) } /// Storage: Elections Candidates (r:1 w:1) /// Proof Skipped: Elections Candidates (max_values: Some(1), max_size: None, mode: Measured) @@ -544,16 +544,16 @@ impl WeightInfo for () { // Minimum execution time: 1_273_671 nanoseconds. Weight::from_parts(1_279_716_000, 330033) // Standard Error: 543_277 - .saturating_add(Weight::from_ref_time(20_613_753).saturating_mul(v.into())) + .saturating_add(Weight::from_parts(20_613_753, 0).saturating_mul(v.into())) // Standard Error: 34_857 - .saturating_add(Weight::from_ref_time(688_354).saturating_mul(e.into())) + .saturating_add(Weight::from_parts(688_354, 0).saturating_mul(e.into())) .saturating_add(RocksDbWeight::get().reads(21_u64)) .saturating_add(RocksDbWeight::get().reads((1_u64).saturating_mul(c.into()))) .saturating_add(RocksDbWeight::get().reads((1_u64).saturating_mul(v.into()))) .saturating_add(RocksDbWeight::get().writes(6_u64)) .saturating_add(RocksDbWeight::get().writes((1_u64).saturating_mul(c.into()))) - .saturating_add(Weight::from_proof_size(5229).saturating_mul(v.into())) - .saturating_add(Weight::from_proof_size(89).saturating_mul(e.into())) - .saturating_add(Weight::from_proof_size(2135).saturating_mul(c.into())) + .saturating_add(Weight::from_parts(0, 5229).saturating_mul(v.into())) + .saturating_add(Weight::from_parts(0, 89).saturating_mul(e.into())) + .saturating_add(Weight::from_parts(0, 2135).saturating_mul(c.into())) } } diff --git a/frame/examples/basic/src/lib.rs b/frame/examples/basic/src/lib.rs index a2b55bff1..a75a0e17c 100644 --- a/frame/examples/basic/src/lib.rs +++ b/frame/examples/basic/src/lib.rs @@ -329,7 +329,7 @@ impl WeighData<(&BalanceOf,)> for WeightForSetDum let multiplier = self.0; // *target.0 is the amount passed into the extrinsic let cents = *target.0 / >::from(MILLICENTS); - Weight::from_ref_time((cents * multiplier).saturated_into::()) + Weight::from_parts((cents * multiplier).saturated_into::(), 0) } } diff --git a/frame/examples/basic/src/weights.rs b/frame/examples/basic/src/weights.rs index 4ea1e01d7..def944054 100644 --- a/frame/examples/basic/src/weights.rs +++ b/frame/examples/basic/src/weights.rs @@ -56,20 +56,20 @@ pub struct SubstrateWeight(PhantomData); impl WeightInfo for SubstrateWeight { // Storage: BasicExample Dummy (r:0 w:1) fn set_dummy_benchmark() -> Weight { - Weight::from_ref_time(19_000_000 as u64) + Weight::from_parts(19_000_000 as u64, 0) .saturating_add(T::DbWeight::get().writes(1 as u64)) } // Storage: BasicExample Dummy (r:1 w:1) fn accumulate_dummy() -> Weight { - Weight::from_ref_time(18_000_000 as u64) + Weight::from_parts(18_000_000 as u64, 0) .saturating_add(T::DbWeight::get().reads(1 as u64)) .saturating_add(T::DbWeight::get().writes(1 as u64)) } /// The range of component `x` is `[0, 10000]`. fn sort_vector(x: u32, ) -> Weight { - Weight::from_ref_time(0 as u64) + Weight::from_parts(0 as u64, 0) // Standard Error: 2 - .saturating_add(Weight::from_ref_time(520 as u64).saturating_mul(x as u64)) + .saturating_add(Weight::from_parts(520 as u64, 0).saturating_mul(x as u64)) } } @@ -77,19 +77,19 @@ impl WeightInfo for SubstrateWeight { impl WeightInfo for () { // Storage: BasicExample Dummy (r:0 w:1) fn set_dummy_benchmark() -> Weight { - Weight::from_ref_time(19_000_000 as u64) + Weight::from_parts(19_000_000 as u64, 0) .saturating_add(RocksDbWeight::get().writes(1 as u64)) } // Storage: BasicExample Dummy (r:1 w:1) fn accumulate_dummy() -> Weight { - Weight::from_ref_time(18_000_000 as u64) + Weight::from_parts(18_000_000 as u64, 0) .saturating_add(RocksDbWeight::get().reads(1 as u64)) .saturating_add(RocksDbWeight::get().writes(1 as u64)) } /// The range of component `x` is `[0, 10000]`. fn sort_vector(x: u32, ) -> Weight { - Weight::from_ref_time(0 as u64) + Weight::from_parts(0 as u64, 0) // Standard Error: 2 - .saturating_add(Weight::from_ref_time(520 as u64).saturating_mul(x as u64)) + .saturating_add(Weight::from_parts(520 as u64, 0).saturating_mul(x as u64)) } } diff --git a/frame/executive/src/lib.rs b/frame/executive/src/lib.rs index 573332294..05f3ee4f5 100644 --- a/frame/executive/src/lib.rs +++ b/frame/executive/src/lib.rs @@ -718,12 +718,12 @@ mod tests { // one with block number arg and one without fn on_initialize(n: T::BlockNumber) -> Weight { println!("on_initialize({})", n); - Weight::from_ref_time(175) + Weight::from_parts(175, 0) } fn on_idle(n: T::BlockNumber, remaining_weight: Weight) -> Weight { println!("on_idle{}, {})", n, remaining_weight); - Weight::from_ref_time(175) + Weight::from_parts(175, 0) } fn on_finalize(n: T::BlockNumber) { @@ -732,7 +732,7 @@ mod tests { fn on_runtime_upgrade() -> Weight { sp_io::storage::set(super::TEST_KEY, "module".as_bytes()); - Weight::from_ref_time(200) + Weight::from_parts(200, 0) } fn offchain_worker(n: T::BlockNumber) { @@ -853,9 +853,9 @@ mod tests { parameter_types! { pub BlockWeights: frame_system::limits::BlockWeights = frame_system::limits::BlockWeights::builder() - .base_block(Weight::from_ref_time(10)) - .for_class(DispatchClass::all(), |weights| weights.base_extrinsic = Weight::from_ref_time(5)) - .for_class(DispatchClass::non_mandatory(), |weights| weights.max_total = Weight::from_ref_time(1024).set_proof_size(u64::MAX).into()) + .base_block(Weight::from_parts(10, 0)) + .for_class(DispatchClass::all(), |weights| weights.base_extrinsic = Weight::from_parts(5, 0)) + .for_class(DispatchClass::non_mandatory(), |weights| weights.max_total = Weight::from_parts(1024, u64::MAX).into()) .build_or_panic(); pub const DbWeight: RuntimeDbWeight = RuntimeDbWeight { read: 10, @@ -946,7 +946,7 @@ mod tests { sp_io::storage::set(TEST_KEY, "custom_upgrade".as_bytes()); sp_io::storage::set(CUSTOM_ON_RUNTIME_KEY, &true.encode()); System::deposit_event(frame_system::Event::CodeUpdated); - Weight::from_ref_time(100) + Weight::from_parts(100, 0) } } @@ -1124,7 +1124,7 @@ mod tests { let encoded_len = encoded.len() as u64; // on_initialize weight + base block execution weight let block_weights = ::BlockWeights::get(); - let base_block_weight = Weight::from_ref_time(175) + block_weights.base_block; + let base_block_weight = Weight::from_parts(175, 0) + block_weights.base_block; let limit = block_weights.get(DispatchClass::Normal).max_total.unwrap() - base_block_weight; let num_to_exhaust_block = limit.ref_time() / (encoded_len + 5); t.execute_with(|| { @@ -1149,7 +1149,7 @@ mod tests { assert_eq!( >::block_weight().total(), //--------------------- on_initialize + block_execution + extrinsic_base weight - Weight::from_ref_time((encoded_len + 5) * (nonce + 1)) + base_block_weight, + Weight::from_parts((encoded_len + 5) * (nonce + 1), 0) + base_block_weight, ); assert_eq!( >::extrinsic_index(), @@ -1180,7 +1180,7 @@ mod tests { let mut t = new_test_ext(1); t.execute_with(|| { // Block execution weight + on_initialize weight from custom module - let base_block_weight = Weight::from_ref_time(175) + + let base_block_weight = Weight::from_parts(175, 0) + ::BlockWeights::get().base_block; Executive::initialize_block(&Header::new( @@ -1199,7 +1199,7 @@ mod tests { assert!(Executive::apply_extrinsic(x2.clone()).unwrap().is_ok()); // default weight for `TestXt` == encoded length. - let extrinsic_weight = Weight::from_ref_time(len as u64) + + let extrinsic_weight = Weight::from_parts(len as u64, 0) + ::BlockWeights::get() .get(DispatchClass::Normal) .base_extrinsic; @@ -1315,7 +1315,7 @@ mod tests { // the `on_initialize` weight defined in the custom test module. assert_eq!( >::block_weight().total(), - Weight::from_ref_time(175 + 175 + 10) + Weight::from_parts(175 + 175 + 10, 0) ); }) } diff --git a/frame/fast-unstake/src/lib.rs b/frame/fast-unstake/src/lib.rs index 81eb3087b..3e79bf407 100644 --- a/frame/fast-unstake/src/lib.rs +++ b/frame/fast-unstake/src/lib.rs @@ -208,7 +208,7 @@ pub mod pallet { impl Hooks for Pallet { fn on_idle(_: T::BlockNumber, remaining_weight: Weight) -> Weight { if remaining_weight.any_lt(T::DbWeight::get().reads(2)) { - return Weight::from_ref_time(0) + return Weight::from_parts(0, 0) } Self::do_on_idle(remaining_weight) @@ -356,7 +356,7 @@ pub mod pallet { /// checked, we don't finish the process. pub(crate) fn do_on_idle(remaining_weight: Weight) -> Weight { // any weight that is unaccounted for - let mut unaccounted_weight = Weight::from_ref_time(0); + let mut unaccounted_weight = Weight::from_parts(0, 0); let eras_to_check_per_block = ErasToCheckPerBlock::::get(); if eras_to_check_per_block.is_zero() { diff --git a/frame/fast-unstake/src/tests.rs b/frame/fast-unstake/src/tests.rs index 1bb6fc100..b4bf1f1cb 100644 --- a/frame/fast-unstake/src/tests.rs +++ b/frame/fast-unstake/src/tests.rs @@ -228,7 +228,7 @@ mod on_idle { assert_eq!(Queue::::get(1), Some(Deposit::get())); // call on_idle with no remaining weight - FastUnstake::on_idle(System::block_number(), Weight::from_ref_time(0)); + FastUnstake::on_idle(System::block_number(), Weight::from_parts(0, 0)); // assert nothing changed in Queue and Head assert_eq!(Head::::get(), None); @@ -1091,8 +1091,9 @@ mod batched { #[test] fn kusama_estimate() { use crate::WeightInfo; - let block_time = frame_support::weights::Weight::from_ref_time( + let block_time = frame_support::weights::Weight::from_parts( frame_support::weights::constants::WEIGHT_REF_TIME_PER_SECOND * 2, + 0, ) .ref_time() as f32; let on_idle = crate::weights::SubstrateWeight::::on_idle_check(1000, 64).ref_time() as f32; @@ -1102,8 +1103,9 @@ fn kusama_estimate() { #[test] fn polkadot_estimate() { use crate::WeightInfo; - let block_time = frame_support::weights::Weight::from_ref_time( + let block_time = frame_support::weights::Weight::from_parts( frame_support::weights::constants::WEIGHT_REF_TIME_PER_SECOND * 2, + 0, ) .ref_time() as f32; let on_idle = crate::weights::SubstrateWeight::::on_idle_check(300, 64).ref_time() as f32; diff --git a/frame/fast-unstake/src/weights.rs b/frame/fast-unstake/src/weights.rs index d1c659596..ced7acb95 100644 --- a/frame/fast-unstake/src/weights.rs +++ b/frame/fast-unstake/src/weights.rs @@ -75,9 +75,9 @@ impl WeightInfo for SubstrateWeight { /// The range of component `b` is `[1, 64]`. fn on_idle_unstake(b: u32, ) -> Weight { // Minimum execution time: 92_833 nanoseconds. - Weight::from_ref_time(62_136_346) + Weight::from_parts(62_136_346, 0) // Standard Error: 25_541 - .saturating_add(Weight::from_ref_time(42_904_859).saturating_mul(b.into())) + .saturating_add(Weight::from_parts(42_904_859, 0).saturating_mul(b.into())) .saturating_add(T::DbWeight::get().reads(6)) .saturating_add(T::DbWeight::get().reads((6_u64).saturating_mul(b.into()))) .saturating_add(T::DbWeight::get().writes(1)) @@ -94,11 +94,11 @@ impl WeightInfo for SubstrateWeight { /// The range of component `b` is `[1, 64]`. fn on_idle_check(v: u32, b: u32, ) -> Weight { // Minimum execution time: 1_775_293 nanoseconds. - Weight::from_ref_time(1_787_133_000) + Weight::from_parts(1_787_133_000, 0) // Standard Error: 17_109_142 - .saturating_add(Weight::from_ref_time(546_766_552).saturating_mul(v.into())) + .saturating_add(Weight::from_parts(546_766_552, 0).saturating_mul(v.into())) // Standard Error: 68_455_625 - .saturating_add(Weight::from_ref_time(2_135_980_830).saturating_mul(b.into())) + .saturating_add(Weight::from_parts(2_135_980_830, 0).saturating_mul(b.into())) .saturating_add(T::DbWeight::get().reads(7)) .saturating_add(T::DbWeight::get().reads((1_u64).saturating_mul(v.into()))) .saturating_add(T::DbWeight::get().writes(1)) @@ -119,7 +119,7 @@ impl WeightInfo for SubstrateWeight { // Storage: FastUnstake CounterForQueue (r:1 w:1) fn register_fast_unstake() -> Weight { // Minimum execution time: 124_849 nanoseconds. - Weight::from_ref_time(128_176_000) + Weight::from_parts(128_176_000, 0) .saturating_add(T::DbWeight::get().reads(14)) .saturating_add(T::DbWeight::get().writes(9)) } @@ -130,14 +130,14 @@ impl WeightInfo for SubstrateWeight { // Storage: FastUnstake CounterForQueue (r:1 w:1) fn deregister() -> Weight { // Minimum execution time: 48_246 nanoseconds. - Weight::from_ref_time(49_720_000) + Weight::from_parts(49_720_000, 0) .saturating_add(T::DbWeight::get().reads(5)) .saturating_add(T::DbWeight::get().writes(2)) } // Storage: FastUnstake ErasToCheckPerBlock (r:0 w:1) fn control() -> Weight { // Minimum execution time: 4_611 nanoseconds. - Weight::from_ref_time(4_844_000) + Weight::from_parts(4_844_000, 0) .saturating_add(T::DbWeight::get().writes(1)) } } @@ -161,9 +161,9 @@ impl WeightInfo for () { /// The range of component `b` is `[1, 64]`. fn on_idle_unstake(b: u32, ) -> Weight { // Minimum execution time: 92_833 nanoseconds. - Weight::from_ref_time(62_136_346) + Weight::from_parts(62_136_346, 0) // Standard Error: 25_541 - .saturating_add(Weight::from_ref_time(42_904_859).saturating_mul(b.into())) + .saturating_add(Weight::from_parts(42_904_859, 0).saturating_mul(b.into())) .saturating_add(RocksDbWeight::get().reads(6)) .saturating_add(RocksDbWeight::get().reads((6_u64).saturating_mul(b.into()))) .saturating_add(RocksDbWeight::get().writes(1)) @@ -180,11 +180,11 @@ impl WeightInfo for () { /// The range of component `b` is `[1, 64]`. fn on_idle_check(v: u32, b: u32, ) -> Weight { // Minimum execution time: 1_775_293 nanoseconds. - Weight::from_ref_time(1_787_133_000) + Weight::from_parts(1_787_133_000, 0) // Standard Error: 17_109_142 - .saturating_add(Weight::from_ref_time(546_766_552).saturating_mul(v.into())) + .saturating_add(Weight::from_parts(546_766_552, 0).saturating_mul(v.into())) // Standard Error: 68_455_625 - .saturating_add(Weight::from_ref_time(2_135_980_830).saturating_mul(b.into())) + .saturating_add(Weight::from_parts(2_135_980_830, 0).saturating_mul(b.into())) .saturating_add(RocksDbWeight::get().reads(7)) .saturating_add(RocksDbWeight::get().reads((1_u64).saturating_mul(v.into()))) .saturating_add(RocksDbWeight::get().writes(1)) @@ -205,7 +205,7 @@ impl WeightInfo for () { // Storage: FastUnstake CounterForQueue (r:1 w:1) fn register_fast_unstake() -> Weight { // Minimum execution time: 124_849 nanoseconds. - Weight::from_ref_time(128_176_000) + Weight::from_parts(128_176_000, 0) .saturating_add(RocksDbWeight::get().reads(14)) .saturating_add(RocksDbWeight::get().writes(9)) } @@ -216,14 +216,14 @@ impl WeightInfo for () { // Storage: FastUnstake CounterForQueue (r:1 w:1) fn deregister() -> Weight { // Minimum execution time: 48_246 nanoseconds. - Weight::from_ref_time(49_720_000) + Weight::from_parts(49_720_000, 0) .saturating_add(RocksDbWeight::get().reads(5)) .saturating_add(RocksDbWeight::get().writes(2)) } // Storage: FastUnstake ErasToCheckPerBlock (r:0 w:1) fn control() -> Weight { // Minimum execution time: 4_611 nanoseconds. - Weight::from_ref_time(4_844_000) + Weight::from_parts(4_844_000, 0) .saturating_add(RocksDbWeight::get().writes(1)) } } diff --git a/frame/glutton/src/tests.rs b/frame/glutton/src/tests.rs index 8d2c86ed4..d75f2da5c 100644 --- a/frame/glutton/src/tests.rs +++ b/frame/glutton/src/tests.rs @@ -133,7 +133,7 @@ fn on_idle_works() { assert_ok!(Glutton::set_compute(RuntimeOrigin::root(), Perbill::from_percent(100))); assert_ok!(Glutton::set_storage(RuntimeOrigin::root(), Perbill::from_percent(100))); - Glutton::on_idle(1, Weight::from_ref_time(20_000_000)); + Glutton::on_idle(1, Weight::from_parts(20_000_000, 0)); }); } diff --git a/frame/glutton/src/weights.rs b/frame/glutton/src/weights.rs index 9c8e161c8..1a7020c17 100644 --- a/frame/glutton/src/weights.rs +++ b/frame/glutton/src/weights.rs @@ -73,10 +73,10 @@ impl WeightInfo for SubstrateWeight { // Measured: `4` // Estimated: `1489` // Minimum execution time: 10_218 nanoseconds. - Weight::from_ref_time(10_510_000) - .saturating_add(Weight::from_proof_size(1489)) + Weight::from_parts(10_510_000, 0) + .saturating_add(Weight::from_parts(0, 1489)) // Standard Error: 1_582 - .saturating_add(Weight::from_ref_time(1_577_660).saturating_mul(n.into())) + .saturating_add(Weight::from_parts(1_577_660, 0).saturating_mul(n.into())) .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) .saturating_add(T::DbWeight::get().writes((1_u64).saturating_mul(n.into()))) @@ -91,10 +91,10 @@ impl WeightInfo for SubstrateWeight { // Measured: `65` // Estimated: `1489` // Minimum execution time: 10_993 nanoseconds. - Weight::from_ref_time(11_208_000) - .saturating_add(Weight::from_proof_size(1489)) + Weight::from_parts(11_208_000, 0) + .saturating_add(Weight::from_parts(0, 1489)) // Standard Error: 1_386 - .saturating_add(Weight::from_ref_time(1_072_330).saturating_mul(n.into())) + .saturating_add(Weight::from_parts(1_072_330, 0).saturating_mul(n.into())) .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) .saturating_add(T::DbWeight::get().writes((1_u64).saturating_mul(n.into()))) @@ -105,10 +105,10 @@ impl WeightInfo for SubstrateWeight { // Measured: `0` // Estimated: `0` // Minimum execution time: 740 nanoseconds. - Weight::from_ref_time(770_000) - .saturating_add(Weight::from_proof_size(0)) + Weight::from_parts(770_000, 0) + .saturating_add(Weight::from_parts(0, 0)) // Standard Error: 24 - .saturating_add(Weight::from_ref_time(96_434).saturating_mul(i.into())) + .saturating_add(Weight::from_parts(96_434, 0).saturating_mul(i.into())) } /// Storage: Glutton TrashData (r:5000 w:0) /// Proof: Glutton TrashData (max_values: Some(65000), max_size: Some(1036), added: 3016, mode: MaxEncodedLen) @@ -118,12 +118,12 @@ impl WeightInfo for SubstrateWeight { // Measured: `119036 + i * (1053 ±0)` // Estimated: `990 + i * (3016 ±0)` // Minimum execution time: 630 nanoseconds. - Weight::from_ref_time(712_000) - .saturating_add(Weight::from_proof_size(990)) + Weight::from_parts(712_000, 0) + .saturating_add(Weight::from_parts(0, 990)) // Standard Error: 4_326 - .saturating_add(Weight::from_ref_time(5_500_880).saturating_mul(i.into())) + .saturating_add(Weight::from_parts(5_500_880, 0).saturating_mul(i.into())) .saturating_add(T::DbWeight::get().reads((1_u64).saturating_mul(i.into()))) - .saturating_add(Weight::from_proof_size(3016).saturating_mul(i.into())) + .saturating_add(Weight::from_parts(0, 3016).saturating_mul(i.into())) } /// Storage: Glutton Storage (r:1 w:0) /// Proof: Glutton Storage (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) @@ -136,8 +136,8 @@ impl WeightInfo for SubstrateWeight { // Measured: `1954313` // Estimated: `5242760` // Minimum execution time: 56_743_236 nanoseconds. - Weight::from_ref_time(57_088_040_000) - .saturating_add(Weight::from_proof_size(5242760)) + Weight::from_parts(57_088_040_000, 0) + .saturating_add(Weight::from_parts(0, 5242760)) .saturating_add(T::DbWeight::get().reads(1739_u64)) } /// Storage: Glutton Storage (r:1 w:0) @@ -151,8 +151,8 @@ impl WeightInfo for SubstrateWeight { // Measured: `9671` // Estimated: `19048` // Minimum execution time: 100_387_042 nanoseconds. - Weight::from_ref_time(100_987_577_000) - .saturating_add(Weight::from_proof_size(19048)) + Weight::from_parts(100_987_577_000, 0) + .saturating_add(Weight::from_parts(0, 19048)) .saturating_add(T::DbWeight::get().reads(7_u64)) } /// Storage: Glutton Storage (r:1 w:0) @@ -164,8 +164,8 @@ impl WeightInfo for SubstrateWeight { // Measured: `4` // Estimated: `2978` // Minimum execution time: 4_256 nanoseconds. - Weight::from_ref_time(4_447_000) - .saturating_add(Weight::from_proof_size(2978)) + Weight::from_parts(4_447_000, 0) + .saturating_add(Weight::from_parts(0, 2978)) .saturating_add(T::DbWeight::get().reads(2_u64)) } /// Storage: Glutton Compute (r:0 w:1) @@ -175,8 +175,8 @@ impl WeightInfo for SubstrateWeight { // Measured: `0` // Estimated: `0` // Minimum execution time: 8_663 nanoseconds. - Weight::from_ref_time(8_864_000) - .saturating_add(Weight::from_proof_size(0)) + Weight::from_parts(8_864_000, 0) + .saturating_add(Weight::from_parts(0, 0)) .saturating_add(T::DbWeight::get().writes(1_u64)) } /// Storage: Glutton Storage (r:0 w:1) @@ -186,8 +186,8 @@ impl WeightInfo for SubstrateWeight { // Measured: `0` // Estimated: `0` // Minimum execution time: 8_653 nanoseconds. - Weight::from_ref_time(8_998_000) - .saturating_add(Weight::from_proof_size(0)) + Weight::from_parts(8_998_000, 0) + .saturating_add(Weight::from_parts(0, 0)) .saturating_add(T::DbWeight::get().writes(1_u64)) } } @@ -204,10 +204,10 @@ impl WeightInfo for () { // Measured: `4` // Estimated: `1489` // Minimum execution time: 10_218 nanoseconds. - Weight::from_ref_time(10_510_000) - .saturating_add(Weight::from_proof_size(1489)) + Weight::from_parts(10_510_000, 0) + .saturating_add(Weight::from_parts(0, 1489)) // Standard Error: 1_582 - .saturating_add(Weight::from_ref_time(1_577_660).saturating_mul(n.into())) + .saturating_add(Weight::from_parts(1_577_660, 0).saturating_mul(n.into())) .saturating_add(RocksDbWeight::get().reads(1_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) .saturating_add(RocksDbWeight::get().writes((1_u64).saturating_mul(n.into()))) @@ -222,10 +222,10 @@ impl WeightInfo for () { // Measured: `65` // Estimated: `1489` // Minimum execution time: 10_993 nanoseconds. - Weight::from_ref_time(11_208_000) - .saturating_add(Weight::from_proof_size(1489)) + Weight::from_parts(11_208_000, 0) + .saturating_add(Weight::from_parts(0, 1489)) // Standard Error: 1_386 - .saturating_add(Weight::from_ref_time(1_072_330).saturating_mul(n.into())) + .saturating_add(Weight::from_parts(1_072_330, 0).saturating_mul(n.into())) .saturating_add(RocksDbWeight::get().reads(1_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) .saturating_add(RocksDbWeight::get().writes((1_u64).saturating_mul(n.into()))) @@ -236,10 +236,10 @@ impl WeightInfo for () { // Measured: `0` // Estimated: `0` // Minimum execution time: 740 nanoseconds. - Weight::from_ref_time(770_000) - .saturating_add(Weight::from_proof_size(0)) + Weight::from_parts(770_000, 0) + .saturating_add(Weight::from_parts(0, 0)) // Standard Error: 24 - .saturating_add(Weight::from_ref_time(96_434).saturating_mul(i.into())) + .saturating_add(Weight::from_parts(96_434, 0).saturating_mul(i.into())) } /// Storage: Glutton TrashData (r:5000 w:0) /// Proof: Glutton TrashData (max_values: Some(65000), max_size: Some(1036), added: 3016, mode: MaxEncodedLen) @@ -249,12 +249,12 @@ impl WeightInfo for () { // Measured: `119036 + i * (1053 ±0)` // Estimated: `990 + i * (3016 ±0)` // Minimum execution time: 630 nanoseconds. - Weight::from_ref_time(712_000) - .saturating_add(Weight::from_proof_size(990)) + Weight::from_parts(712_000, 0) + .saturating_add(Weight::from_parts(0, 990)) // Standard Error: 4_326 - .saturating_add(Weight::from_ref_time(5_500_880).saturating_mul(i.into())) + .saturating_add(Weight::from_parts(5_500_880, 0).saturating_mul(i.into())) .saturating_add(RocksDbWeight::get().reads((1_u64).saturating_mul(i.into()))) - .saturating_add(Weight::from_proof_size(3016).saturating_mul(i.into())) + .saturating_add(Weight::from_parts(0, 3016).saturating_mul(i.into())) } /// Storage: Glutton Storage (r:1 w:0) /// Proof: Glutton Storage (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) @@ -267,8 +267,8 @@ impl WeightInfo for () { // Measured: `1954313` // Estimated: `5242760` // Minimum execution time: 56_743_236 nanoseconds. - Weight::from_ref_time(57_088_040_000) - .saturating_add(Weight::from_proof_size(5242760)) + Weight::from_parts(57_088_040_000, 0) + .saturating_add(Weight::from_parts(0, 5242760)) .saturating_add(RocksDbWeight::get().reads(1739_u64)) } /// Storage: Glutton Storage (r:1 w:0) @@ -282,8 +282,8 @@ impl WeightInfo for () { // Measured: `9671` // Estimated: `19048` // Minimum execution time: 100_387_042 nanoseconds. - Weight::from_ref_time(100_987_577_000) - .saturating_add(Weight::from_proof_size(19048)) + Weight::from_parts(100_987_577_000, 0) + .saturating_add(Weight::from_parts(0, 19048)) .saturating_add(RocksDbWeight::get().reads(7_u64)) } /// Storage: Glutton Storage (r:1 w:0) @@ -295,8 +295,8 @@ impl WeightInfo for () { // Measured: `4` // Estimated: `2978` // Minimum execution time: 4_256 nanoseconds. - Weight::from_ref_time(4_447_000) - .saturating_add(Weight::from_proof_size(2978)) + Weight::from_parts(4_447_000, 0) + .saturating_add(Weight::from_parts(0, 2978)) .saturating_add(RocksDbWeight::get().reads(2_u64)) } /// Storage: Glutton Compute (r:0 w:1) @@ -306,8 +306,8 @@ impl WeightInfo for () { // Measured: `0` // Estimated: `0` // Minimum execution time: 8_663 nanoseconds. - Weight::from_ref_time(8_864_000) - .saturating_add(Weight::from_proof_size(0)) + Weight::from_parts(8_864_000, 0) + .saturating_add(Weight::from_parts(0, 0)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } /// Storage: Glutton Storage (r:0 w:1) @@ -317,8 +317,8 @@ impl WeightInfo for () { // Measured: `0` // Estimated: `0` // Minimum execution time: 8_653 nanoseconds. - Weight::from_ref_time(8_998_000) - .saturating_add(Weight::from_proof_size(0)) + Weight::from_parts(8_998_000, 0) + .saturating_add(Weight::from_parts(0, 0)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } } diff --git a/frame/grandpa/src/default_weights.rs b/frame/grandpa/src/default_weights.rs index 6b203783e..3afd714f4 100644 --- a/frame/grandpa/src/default_weights.rs +++ b/frame/grandpa/src/default_weights.rs @@ -34,18 +34,19 @@ impl crate::WeightInfo for () { const MAX_NOMINATORS: u64 = 200; // checking membership proof - Weight::from_ref_time(35u64 * WEIGHT_REF_TIME_PER_MICROS) + Weight::from_parts(35u64 * WEIGHT_REF_TIME_PER_MICROS, 0) .saturating_add( - Weight::from_ref_time(175u64 * WEIGHT_REF_TIME_PER_NANOS) + Weight::from_parts(175u64 * WEIGHT_REF_TIME_PER_NANOS, 0) .saturating_mul(validator_count), ) .saturating_add(DbWeight::get().reads(5)) // check equivocation proof - .saturating_add(Weight::from_ref_time(95u64 * WEIGHT_REF_TIME_PER_MICROS)) + .saturating_add(Weight::from_parts(95u64 * WEIGHT_REF_TIME_PER_MICROS, 0)) // report offence - .saturating_add(Weight::from_ref_time(110u64 * WEIGHT_REF_TIME_PER_MICROS)) - .saturating_add(Weight::from_ref_time( + .saturating_add(Weight::from_parts(110u64 * WEIGHT_REF_TIME_PER_MICROS, 0)) + .saturating_add(Weight::from_parts( 25u64 * WEIGHT_REF_TIME_PER_MICROS * MAX_NOMINATORS, + 0, )) .saturating_add(DbWeight::get().reads(14 + 3 * MAX_NOMINATORS)) .saturating_add(DbWeight::get().writes(10 + 3 * MAX_NOMINATORS)) @@ -54,7 +55,7 @@ impl crate::WeightInfo for () { } fn note_stalled() -> Weight { - Weight::from_ref_time(3u64 * WEIGHT_REF_TIME_PER_MICROS) + Weight::from_parts(3u64 * WEIGHT_REF_TIME_PER_MICROS, 0) .saturating_add(DbWeight::get().writes(1)) } } diff --git a/frame/identity/src/weights.rs b/frame/identity/src/weights.rs index 647588052..65fe2b17b 100644 --- a/frame/identity/src/weights.rs +++ b/frame/identity/src/weights.rs @@ -79,7 +79,7 @@ impl WeightInfo for SubstrateWeight { // Minimum execution time: 10_964 nanoseconds. Weight::from_parts(11_800_935, 1636) // Standard Error: 1_334 - .saturating_add(Weight::from_ref_time(96_038).saturating_mul(r.into())) + .saturating_add(Weight::from_parts(96_038, 0).saturating_mul(r.into())) .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -94,9 +94,9 @@ impl WeightInfo for SubstrateWeight { // Minimum execution time: 26_400 nanoseconds. Weight::from_parts(26_060_549, 10013) // Standard Error: 1_561 - .saturating_add(Weight::from_ref_time(72_083).saturating_mul(r.into())) + .saturating_add(Weight::from_parts(72_083, 0).saturating_mul(r.into())) // Standard Error: 304 - .saturating_add(Weight::from_ref_time(306_994).saturating_mul(x.into())) + .saturating_add(Weight::from_parts(306_994, 0).saturating_mul(x.into())) .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -114,12 +114,12 @@ impl WeightInfo for SubstrateWeight { // Minimum execution time: 8_492 nanoseconds. Weight::from_parts(21_645_924, 15746) // Standard Error: 3_452 - .saturating_add(Weight::from_ref_time(2_442_604).saturating_mul(s.into())) + .saturating_add(Weight::from_parts(2_442_604, 0).saturating_mul(s.into())) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().reads((1_u64).saturating_mul(s.into()))) .saturating_add(T::DbWeight::get().writes(1_u64)) .saturating_add(T::DbWeight::get().writes((1_u64).saturating_mul(s.into()))) - .saturating_add(Weight::from_proof_size(2589).saturating_mul(s.into())) + .saturating_add(Weight::from_parts(0, 2589).saturating_mul(s.into())) } /// Storage: Identity IdentityOf (r:1 w:0) /// Proof: Identity IdentityOf (max_values: None, max_size: Some(7538), added: 10013, mode: MaxEncodedLen) @@ -135,7 +135,7 @@ impl WeightInfo for SubstrateWeight { // Minimum execution time: 8_488 nanoseconds. Weight::from_parts(20_202_601, 15746) // Standard Error: 2_834 - .saturating_add(Weight::from_ref_time(1_082_941).saturating_mul(p.into())) + .saturating_add(Weight::from_parts(1_082_941, 0).saturating_mul(p.into())) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) .saturating_add(T::DbWeight::get().writes((1_u64).saturating_mul(p.into()))) @@ -156,11 +156,11 @@ impl WeightInfo for SubstrateWeight { // Minimum execution time: 41_319 nanoseconds. Weight::from_parts(25_850_055, 15746) // Standard Error: 4_144 - .saturating_add(Weight::from_ref_time(59_619).saturating_mul(r.into())) + .saturating_add(Weight::from_parts(59_619, 0).saturating_mul(r.into())) // Standard Error: 809 - .saturating_add(Weight::from_ref_time(1_076_550).saturating_mul(s.into())) + .saturating_add(Weight::from_parts(1_076_550, 0).saturating_mul(s.into())) // Standard Error: 809 - .saturating_add(Weight::from_ref_time(163_191).saturating_mul(x.into())) + .saturating_add(Weight::from_parts(163_191, 0).saturating_mul(x.into())) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().writes(2_u64)) .saturating_add(T::DbWeight::get().writes((1_u64).saturating_mul(s.into()))) @@ -178,9 +178,9 @@ impl WeightInfo for SubstrateWeight { // Minimum execution time: 28_118 nanoseconds. Weight::from_parts(27_359_471, 11649) // Standard Error: 2_707 - .saturating_add(Weight::from_ref_time(107_279).saturating_mul(r.into())) + .saturating_add(Weight::from_parts(107_279, 0).saturating_mul(r.into())) // Standard Error: 528 - .saturating_add(Weight::from_ref_time(325_165).saturating_mul(x.into())) + .saturating_add(Weight::from_parts(325_165, 0).saturating_mul(x.into())) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -195,9 +195,9 @@ impl WeightInfo for SubstrateWeight { // Minimum execution time: 24_817 nanoseconds. Weight::from_parts(24_749_808, 10013) // Standard Error: 1_938 - .saturating_add(Weight::from_ref_time(63_396).saturating_mul(r.into())) + .saturating_add(Weight::from_parts(63_396, 0).saturating_mul(r.into())) // Standard Error: 378 - .saturating_add(Weight::from_ref_time(327_083).saturating_mul(x.into())) + .saturating_add(Weight::from_parts(327_083, 0).saturating_mul(x.into())) .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -211,7 +211,7 @@ impl WeightInfo for SubstrateWeight { // Minimum execution time: 6_664 nanoseconds. Weight::from_parts(7_286_307, 1636) // Standard Error: 1_560 - .saturating_add(Weight::from_ref_time(96_416).saturating_mul(r.into())) + .saturating_add(Weight::from_parts(96_416, 0).saturating_mul(r.into())) .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -225,7 +225,7 @@ impl WeightInfo for SubstrateWeight { // Minimum execution time: 7_054 nanoseconds. Weight::from_parts(7_382_954, 1636) // Standard Error: 1_621 - .saturating_add(Weight::from_ref_time(101_595).saturating_mul(r.into())) + .saturating_add(Weight::from_parts(101_595, 0).saturating_mul(r.into())) .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -239,7 +239,7 @@ impl WeightInfo for SubstrateWeight { // Minimum execution time: 6_659 nanoseconds. Weight::from_parts(7_188_883, 1636) // Standard Error: 1_377 - .saturating_add(Weight::from_ref_time(98_965).saturating_mul(r.into())) + .saturating_add(Weight::from_parts(98_965, 0).saturating_mul(r.into())) .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -256,9 +256,9 @@ impl WeightInfo for SubstrateWeight { // Minimum execution time: 21_567 nanoseconds. Weight::from_parts(21_015_310, 11649) // Standard Error: 2_516 - .saturating_add(Weight::from_ref_time(123_992).saturating_mul(r.into())) + .saturating_add(Weight::from_parts(123_992, 0).saturating_mul(r.into())) // Standard Error: 465 - .saturating_add(Weight::from_ref_time(552_116).saturating_mul(x.into())) + .saturating_add(Weight::from_parts(552_116, 0).saturating_mul(x.into())) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -280,11 +280,11 @@ impl WeightInfo for SubstrateWeight { // Minimum execution time: 52_881 nanoseconds. Weight::from_parts(38_504_388, 18349) // Standard Error: 3_909 - .saturating_add(Weight::from_ref_time(51_452).saturating_mul(r.into())) + .saturating_add(Weight::from_parts(51_452, 0).saturating_mul(r.into())) // Standard Error: 763 - .saturating_add(Weight::from_ref_time(1_069_924).saturating_mul(s.into())) + .saturating_add(Weight::from_parts(1_069_924, 0).saturating_mul(s.into())) // Standard Error: 763 - .saturating_add(Weight::from_ref_time(164_906).saturating_mul(x.into())) + .saturating_add(Weight::from_parts(164_906, 0).saturating_mul(x.into())) .saturating_add(T::DbWeight::get().reads(3_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) .saturating_add(T::DbWeight::get().writes((1_u64).saturating_mul(s.into()))) @@ -303,7 +303,7 @@ impl WeightInfo for SubstrateWeight { // Minimum execution time: 24_556 nanoseconds. Weight::from_parts(28_641_160, 18335) // Standard Error: 1_327 - .saturating_add(Weight::from_ref_time(66_150).saturating_mul(s.into())) + .saturating_add(Weight::from_parts(66_150, 0).saturating_mul(s.into())) .saturating_add(T::DbWeight::get().reads(3_u64)) .saturating_add(T::DbWeight::get().writes(2_u64)) } @@ -319,7 +319,7 @@ impl WeightInfo for SubstrateWeight { // Minimum execution time: 11_347 nanoseconds. Weight::from_parts(13_299_367, 12602) // Standard Error: 525 - .saturating_add(Weight::from_ref_time(16_472).saturating_mul(s.into())) + .saturating_add(Weight::from_parts(16_472, 0).saturating_mul(s.into())) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -337,7 +337,7 @@ impl WeightInfo for SubstrateWeight { // Minimum execution time: 27_810 nanoseconds. Weight::from_parts(30_347_763, 18335) // Standard Error: 928 - .saturating_add(Weight::from_ref_time(55_342).saturating_mul(s.into())) + .saturating_add(Weight::from_parts(55_342, 0).saturating_mul(s.into())) .saturating_add(T::DbWeight::get().reads(3_u64)) .saturating_add(T::DbWeight::get().writes(2_u64)) } @@ -353,7 +353,7 @@ impl WeightInfo for SubstrateWeight { // Minimum execution time: 17_601 nanoseconds. Weight::from_parts(19_794_971, 8322) // Standard Error: 934 - .saturating_add(Weight::from_ref_time(59_289).saturating_mul(s.into())) + .saturating_add(Weight::from_parts(59_289, 0).saturating_mul(s.into())) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().writes(2_u64)) } @@ -371,7 +371,7 @@ impl WeightInfo for () { // Minimum execution time: 10_964 nanoseconds. Weight::from_parts(11_800_935, 1636) // Standard Error: 1_334 - .saturating_add(Weight::from_ref_time(96_038).saturating_mul(r.into())) + .saturating_add(Weight::from_parts(96_038, 0).saturating_mul(r.into())) .saturating_add(RocksDbWeight::get().reads(1_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } @@ -386,9 +386,9 @@ impl WeightInfo for () { // Minimum execution time: 26_400 nanoseconds. Weight::from_parts(26_060_549, 10013) // Standard Error: 1_561 - .saturating_add(Weight::from_ref_time(72_083).saturating_mul(r.into())) + .saturating_add(Weight::from_parts(72_083, 0).saturating_mul(r.into())) // Standard Error: 304 - .saturating_add(Weight::from_ref_time(306_994).saturating_mul(x.into())) + .saturating_add(Weight::from_parts(306_994, 0).saturating_mul(x.into())) .saturating_add(RocksDbWeight::get().reads(1_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } @@ -406,12 +406,12 @@ impl WeightInfo for () { // Minimum execution time: 8_492 nanoseconds. Weight::from_parts(21_645_924, 15746) // Standard Error: 3_452 - .saturating_add(Weight::from_ref_time(2_442_604).saturating_mul(s.into())) + .saturating_add(Weight::from_parts(2_442_604, 0).saturating_mul(s.into())) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().reads((1_u64).saturating_mul(s.into()))) .saturating_add(RocksDbWeight::get().writes(1_u64)) .saturating_add(RocksDbWeight::get().writes((1_u64).saturating_mul(s.into()))) - .saturating_add(Weight::from_proof_size(2589).saturating_mul(s.into())) + .saturating_add(Weight::from_parts(0, 2589).saturating_mul(s.into())) } /// Storage: Identity IdentityOf (r:1 w:0) /// Proof: Identity IdentityOf (max_values: None, max_size: Some(7538), added: 10013, mode: MaxEncodedLen) @@ -427,7 +427,7 @@ impl WeightInfo for () { // Minimum execution time: 8_488 nanoseconds. Weight::from_parts(20_202_601, 15746) // Standard Error: 2_834 - .saturating_add(Weight::from_ref_time(1_082_941).saturating_mul(p.into())) + .saturating_add(Weight::from_parts(1_082_941, 0).saturating_mul(p.into())) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) .saturating_add(RocksDbWeight::get().writes((1_u64).saturating_mul(p.into()))) @@ -448,11 +448,11 @@ impl WeightInfo for () { // Minimum execution time: 41_319 nanoseconds. Weight::from_parts(25_850_055, 15746) // Standard Error: 4_144 - .saturating_add(Weight::from_ref_time(59_619).saturating_mul(r.into())) + .saturating_add(Weight::from_parts(59_619, 0).saturating_mul(r.into())) // Standard Error: 809 - .saturating_add(Weight::from_ref_time(1_076_550).saturating_mul(s.into())) + .saturating_add(Weight::from_parts(1_076_550, 0).saturating_mul(s.into())) // Standard Error: 809 - .saturating_add(Weight::from_ref_time(163_191).saturating_mul(x.into())) + .saturating_add(Weight::from_parts(163_191, 0).saturating_mul(x.into())) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().writes(2_u64)) .saturating_add(RocksDbWeight::get().writes((1_u64).saturating_mul(s.into()))) @@ -470,9 +470,9 @@ impl WeightInfo for () { // Minimum execution time: 28_118 nanoseconds. Weight::from_parts(27_359_471, 11649) // Standard Error: 2_707 - .saturating_add(Weight::from_ref_time(107_279).saturating_mul(r.into())) + .saturating_add(Weight::from_parts(107_279, 0).saturating_mul(r.into())) // Standard Error: 528 - .saturating_add(Weight::from_ref_time(325_165).saturating_mul(x.into())) + .saturating_add(Weight::from_parts(325_165, 0).saturating_mul(x.into())) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } @@ -487,9 +487,9 @@ impl WeightInfo for () { // Minimum execution time: 24_817 nanoseconds. Weight::from_parts(24_749_808, 10013) // Standard Error: 1_938 - .saturating_add(Weight::from_ref_time(63_396).saturating_mul(r.into())) + .saturating_add(Weight::from_parts(63_396, 0).saturating_mul(r.into())) // Standard Error: 378 - .saturating_add(Weight::from_ref_time(327_083).saturating_mul(x.into())) + .saturating_add(Weight::from_parts(327_083, 0).saturating_mul(x.into())) .saturating_add(RocksDbWeight::get().reads(1_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } @@ -503,7 +503,7 @@ impl WeightInfo for () { // Minimum execution time: 6_664 nanoseconds. Weight::from_parts(7_286_307, 1636) // Standard Error: 1_560 - .saturating_add(Weight::from_ref_time(96_416).saturating_mul(r.into())) + .saturating_add(Weight::from_parts(96_416, 0).saturating_mul(r.into())) .saturating_add(RocksDbWeight::get().reads(1_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } @@ -517,7 +517,7 @@ impl WeightInfo for () { // Minimum execution time: 7_054 nanoseconds. Weight::from_parts(7_382_954, 1636) // Standard Error: 1_621 - .saturating_add(Weight::from_ref_time(101_595).saturating_mul(r.into())) + .saturating_add(Weight::from_parts(101_595, 0).saturating_mul(r.into())) .saturating_add(RocksDbWeight::get().reads(1_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } @@ -531,7 +531,7 @@ impl WeightInfo for () { // Minimum execution time: 6_659 nanoseconds. Weight::from_parts(7_188_883, 1636) // Standard Error: 1_377 - .saturating_add(Weight::from_ref_time(98_965).saturating_mul(r.into())) + .saturating_add(Weight::from_parts(98_965, 0).saturating_mul(r.into())) .saturating_add(RocksDbWeight::get().reads(1_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } @@ -548,9 +548,9 @@ impl WeightInfo for () { // Minimum execution time: 21_567 nanoseconds. Weight::from_parts(21_015_310, 11649) // Standard Error: 2_516 - .saturating_add(Weight::from_ref_time(123_992).saturating_mul(r.into())) + .saturating_add(Weight::from_parts(123_992, 0).saturating_mul(r.into())) // Standard Error: 465 - .saturating_add(Weight::from_ref_time(552_116).saturating_mul(x.into())) + .saturating_add(Weight::from_parts(552_116, 0).saturating_mul(x.into())) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } @@ -572,11 +572,11 @@ impl WeightInfo for () { // Minimum execution time: 52_881 nanoseconds. Weight::from_parts(38_504_388, 18349) // Standard Error: 3_909 - .saturating_add(Weight::from_ref_time(51_452).saturating_mul(r.into())) + .saturating_add(Weight::from_parts(51_452, 0).saturating_mul(r.into())) // Standard Error: 763 - .saturating_add(Weight::from_ref_time(1_069_924).saturating_mul(s.into())) + .saturating_add(Weight::from_parts(1_069_924, 0).saturating_mul(s.into())) // Standard Error: 763 - .saturating_add(Weight::from_ref_time(164_906).saturating_mul(x.into())) + .saturating_add(Weight::from_parts(164_906, 0).saturating_mul(x.into())) .saturating_add(RocksDbWeight::get().reads(3_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) .saturating_add(RocksDbWeight::get().writes((1_u64).saturating_mul(s.into()))) @@ -595,7 +595,7 @@ impl WeightInfo for () { // Minimum execution time: 24_556 nanoseconds. Weight::from_parts(28_641_160, 18335) // Standard Error: 1_327 - .saturating_add(Weight::from_ref_time(66_150).saturating_mul(s.into())) + .saturating_add(Weight::from_parts(66_150, 0).saturating_mul(s.into())) .saturating_add(RocksDbWeight::get().reads(3_u64)) .saturating_add(RocksDbWeight::get().writes(2_u64)) } @@ -611,7 +611,7 @@ impl WeightInfo for () { // Minimum execution time: 11_347 nanoseconds. Weight::from_parts(13_299_367, 12602) // Standard Error: 525 - .saturating_add(Weight::from_ref_time(16_472).saturating_mul(s.into())) + .saturating_add(Weight::from_parts(16_472, 0).saturating_mul(s.into())) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } @@ -629,7 +629,7 @@ impl WeightInfo for () { // Minimum execution time: 27_810 nanoseconds. Weight::from_parts(30_347_763, 18335) // Standard Error: 928 - .saturating_add(Weight::from_ref_time(55_342).saturating_mul(s.into())) + .saturating_add(Weight::from_parts(55_342, 0).saturating_mul(s.into())) .saturating_add(RocksDbWeight::get().reads(3_u64)) .saturating_add(RocksDbWeight::get().writes(2_u64)) } @@ -645,7 +645,7 @@ impl WeightInfo for () { // Minimum execution time: 17_601 nanoseconds. Weight::from_parts(19_794_971, 8322) // Standard Error: 934 - .saturating_add(Weight::from_ref_time(59_289).saturating_mul(s.into())) + .saturating_add(Weight::from_parts(59_289, 0).saturating_mul(s.into())) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().writes(2_u64)) } diff --git a/frame/im-online/src/weights.rs b/frame/im-online/src/weights.rs index ee7cabf8a..675feb075 100644 --- a/frame/im-online/src/weights.rs +++ b/frame/im-online/src/weights.rs @@ -73,13 +73,13 @@ impl WeightInfo for SubstrateWeight { // Minimum execution time: 91_116 nanoseconds. Weight::from_parts(72_526_877, 10345712) // Standard Error: 95 - .saturating_add(Weight::from_ref_time(20_461).saturating_mul(k.into())) + .saturating_add(Weight::from_parts(20_461, 0).saturating_mul(k.into())) // Standard Error: 967 - .saturating_add(Weight::from_ref_time(307_869).saturating_mul(e.into())) + .saturating_add(Weight::from_parts(307_869, 0).saturating_mul(e.into())) .saturating_add(T::DbWeight::get().reads(4_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) - .saturating_add(Weight::from_proof_size(25).saturating_mul(e.into())) - .saturating_add(Weight::from_proof_size(64).saturating_mul(k.into())) + .saturating_add(Weight::from_parts(0, 25).saturating_mul(e.into())) + .saturating_add(Weight::from_parts(0, 64).saturating_mul(k.into())) } } @@ -104,12 +104,12 @@ impl WeightInfo for () { // Minimum execution time: 91_116 nanoseconds. Weight::from_parts(72_526_877, 10345712) // Standard Error: 95 - .saturating_add(Weight::from_ref_time(20_461).saturating_mul(k.into())) + .saturating_add(Weight::from_parts(20_461, 0).saturating_mul(k.into())) // Standard Error: 967 - .saturating_add(Weight::from_ref_time(307_869).saturating_mul(e.into())) + .saturating_add(Weight::from_parts(307_869, 0).saturating_mul(e.into())) .saturating_add(RocksDbWeight::get().reads(4_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) - .saturating_add(Weight::from_proof_size(25).saturating_mul(e.into())) - .saturating_add(Weight::from_proof_size(64).saturating_mul(k.into())) + .saturating_add(Weight::from_parts(0, 25).saturating_mul(e.into())) + .saturating_add(Weight::from_parts(0, 64).saturating_mul(k.into())) } } diff --git a/frame/lottery/src/weights.rs b/frame/lottery/src/weights.rs index e7857b230..0038db621 100644 --- a/frame/lottery/src/weights.rs +++ b/frame/lottery/src/weights.rs @@ -90,9 +90,9 @@ impl WeightInfo for SubstrateWeight { // Measured: `0` // Estimated: `0` // Minimum execution time: 7_650 nanoseconds. - Weight::from_ref_time(8_344_960) + Weight::from_parts(8_344_960, 0) // Standard Error: 2_629 - .saturating_add(Weight::from_ref_time(268_557).saturating_mul(n.into())) + .saturating_add(Weight::from_parts(268_557, 0).saturating_mul(n.into())) .saturating_add(T::DbWeight::get().writes(1_u64)) } /// Storage: Lottery Lottery (r:1 w:1) @@ -196,9 +196,9 @@ impl WeightInfo for () { // Measured: `0` // Estimated: `0` // Minimum execution time: 7_650 nanoseconds. - Weight::from_ref_time(8_344_960) + Weight::from_parts(8_344_960, 0) // Standard Error: 2_629 - .saturating_add(Weight::from_ref_time(268_557).saturating_mul(n.into())) + .saturating_add(Weight::from_parts(268_557, 0).saturating_mul(n.into())) .saturating_add(RocksDbWeight::get().writes(1_u64)) } /// Storage: Lottery Lottery (r:1 w:1) diff --git a/frame/membership/src/weights.rs b/frame/membership/src/weights.rs index 17f4b2f84..f080f842c 100644 --- a/frame/membership/src/weights.rs +++ b/frame/membership/src/weights.rs @@ -76,10 +76,10 @@ impl WeightInfo for SubstrateWeight { // Minimum execution time: 15_673 nanoseconds. Weight::from_parts(16_830_288, 4903) // Standard Error: 570 - .saturating_add(Weight::from_ref_time(41_959).saturating_mul(m.into())) + .saturating_add(Weight::from_parts(41_959, 0).saturating_mul(m.into())) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) - .saturating_add(Weight::from_proof_size(192).saturating_mul(m.into())) + .saturating_add(Weight::from_parts(0, 192).saturating_mul(m.into())) } /// Storage: TechnicalMembership Members (r:1 w:1) /// Proof: TechnicalMembership Members (max_values: Some(1), max_size: Some(3202), added: 3697, mode: MaxEncodedLen) @@ -99,10 +99,10 @@ impl WeightInfo for SubstrateWeight { // Minimum execution time: 18_231 nanoseconds. Weight::from_parts(19_081_297, 5742) // Standard Error: 571 - .saturating_add(Weight::from_ref_time(41_331).saturating_mul(m.into())) + .saturating_add(Weight::from_parts(41_331, 0).saturating_mul(m.into())) .saturating_add(T::DbWeight::get().reads(3_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) - .saturating_add(Weight::from_proof_size(192).saturating_mul(m.into())) + .saturating_add(Weight::from_parts(0, 192).saturating_mul(m.into())) } /// Storage: TechnicalMembership Members (r:1 w:1) /// Proof: TechnicalMembership Members (max_values: Some(1), max_size: Some(3202), added: 3697, mode: MaxEncodedLen) @@ -122,10 +122,10 @@ impl WeightInfo for SubstrateWeight { // Minimum execution time: 18_517 nanoseconds. Weight::from_parts(19_388_310, 5742) // Standard Error: 625 - .saturating_add(Weight::from_ref_time(51_422).saturating_mul(m.into())) + .saturating_add(Weight::from_parts(51_422, 0).saturating_mul(m.into())) .saturating_add(T::DbWeight::get().reads(3_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) - .saturating_add(Weight::from_proof_size(192).saturating_mul(m.into())) + .saturating_add(Weight::from_parts(0, 192).saturating_mul(m.into())) } /// Storage: TechnicalMembership Members (r:1 w:1) /// Proof: TechnicalMembership Members (max_values: Some(1), max_size: Some(3202), added: 3697, mode: MaxEncodedLen) @@ -145,10 +145,10 @@ impl WeightInfo for SubstrateWeight { // Minimum execution time: 17_628 nanoseconds. Weight::from_parts(19_258_882, 5742) // Standard Error: 820 - .saturating_add(Weight::from_ref_time(153_956).saturating_mul(m.into())) + .saturating_add(Weight::from_parts(153_956, 0).saturating_mul(m.into())) .saturating_add(T::DbWeight::get().reads(3_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) - .saturating_add(Weight::from_proof_size(192).saturating_mul(m.into())) + .saturating_add(Weight::from_parts(0, 192).saturating_mul(m.into())) } /// Storage: TechnicalMembership Members (r:1 w:1) /// Proof: TechnicalMembership Members (max_values: Some(1), max_size: Some(3202), added: 3697, mode: MaxEncodedLen) @@ -168,10 +168,10 @@ impl WeightInfo for SubstrateWeight { // Minimum execution time: 19_031 nanoseconds. Weight::from_parts(20_264_948, 5742) // Standard Error: 707 - .saturating_add(Weight::from_ref_time(51_060).saturating_mul(m.into())) + .saturating_add(Weight::from_parts(51_060, 0).saturating_mul(m.into())) .saturating_add(T::DbWeight::get().reads(3_u64)) .saturating_add(T::DbWeight::get().writes(4_u64)) - .saturating_add(Weight::from_proof_size(192).saturating_mul(m.into())) + .saturating_add(Weight::from_parts(0, 192).saturating_mul(m.into())) } /// Storage: TechnicalMembership Members (r:1 w:0) /// Proof: TechnicalMembership Members (max_values: Some(1), max_size: Some(3202), added: 3697, mode: MaxEncodedLen) @@ -187,10 +187,10 @@ impl WeightInfo for SubstrateWeight { // Minimum execution time: 6_897 nanoseconds. Weight::from_parts(7_455_387, 3761) // Standard Error: 326 - .saturating_add(Weight::from_ref_time(16_653).saturating_mul(m.into())) + .saturating_add(Weight::from_parts(16_653, 0).saturating_mul(m.into())) .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(T::DbWeight::get().writes(2_u64)) - .saturating_add(Weight::from_proof_size(32).saturating_mul(m.into())) + .saturating_add(Weight::from_parts(0, 32).saturating_mul(m.into())) } /// Storage: TechnicalMembership Prime (r:0 w:1) /// Proof: TechnicalMembership Prime (max_values: Some(1), max_size: Some(32), added: 527, mode: MaxEncodedLen) @@ -202,9 +202,9 @@ impl WeightInfo for SubstrateWeight { // Measured: `0` // Estimated: `0` // Minimum execution time: 3_400 nanoseconds. - Weight::from_ref_time(3_703_421) + Weight::from_parts(3_703_421, 0) // Standard Error: 119 - .saturating_add(Weight::from_ref_time(915).saturating_mul(m.into())) + .saturating_add(Weight::from_parts(915, 0).saturating_mul(m.into())) .saturating_add(T::DbWeight::get().writes(2_u64)) } } @@ -227,10 +227,10 @@ impl WeightInfo for () { // Minimum execution time: 15_673 nanoseconds. Weight::from_parts(16_830_288, 4903) // Standard Error: 570 - .saturating_add(Weight::from_ref_time(41_959).saturating_mul(m.into())) + .saturating_add(Weight::from_parts(41_959, 0).saturating_mul(m.into())) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) - .saturating_add(Weight::from_proof_size(192).saturating_mul(m.into())) + .saturating_add(Weight::from_parts(0, 192).saturating_mul(m.into())) } /// Storage: TechnicalMembership Members (r:1 w:1) /// Proof: TechnicalMembership Members (max_values: Some(1), max_size: Some(3202), added: 3697, mode: MaxEncodedLen) @@ -250,10 +250,10 @@ impl WeightInfo for () { // Minimum execution time: 18_231 nanoseconds. Weight::from_parts(19_081_297, 5742) // Standard Error: 571 - .saturating_add(Weight::from_ref_time(41_331).saturating_mul(m.into())) + .saturating_add(Weight::from_parts(41_331, 0).saturating_mul(m.into())) .saturating_add(RocksDbWeight::get().reads(3_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) - .saturating_add(Weight::from_proof_size(192).saturating_mul(m.into())) + .saturating_add(Weight::from_parts(0, 192).saturating_mul(m.into())) } /// Storage: TechnicalMembership Members (r:1 w:1) /// Proof: TechnicalMembership Members (max_values: Some(1), max_size: Some(3202), added: 3697, mode: MaxEncodedLen) @@ -273,10 +273,10 @@ impl WeightInfo for () { // Minimum execution time: 18_517 nanoseconds. Weight::from_parts(19_388_310, 5742) // Standard Error: 625 - .saturating_add(Weight::from_ref_time(51_422).saturating_mul(m.into())) + .saturating_add(Weight::from_parts(51_422, 0).saturating_mul(m.into())) .saturating_add(RocksDbWeight::get().reads(3_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) - .saturating_add(Weight::from_proof_size(192).saturating_mul(m.into())) + .saturating_add(Weight::from_parts(0, 192).saturating_mul(m.into())) } /// Storage: TechnicalMembership Members (r:1 w:1) /// Proof: TechnicalMembership Members (max_values: Some(1), max_size: Some(3202), added: 3697, mode: MaxEncodedLen) @@ -296,10 +296,10 @@ impl WeightInfo for () { // Minimum execution time: 17_628 nanoseconds. Weight::from_parts(19_258_882, 5742) // Standard Error: 820 - .saturating_add(Weight::from_ref_time(153_956).saturating_mul(m.into())) + .saturating_add(Weight::from_parts(153_956, 0).saturating_mul(m.into())) .saturating_add(RocksDbWeight::get().reads(3_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) - .saturating_add(Weight::from_proof_size(192).saturating_mul(m.into())) + .saturating_add(Weight::from_parts(0, 192).saturating_mul(m.into())) } /// Storage: TechnicalMembership Members (r:1 w:1) /// Proof: TechnicalMembership Members (max_values: Some(1), max_size: Some(3202), added: 3697, mode: MaxEncodedLen) @@ -319,10 +319,10 @@ impl WeightInfo for () { // Minimum execution time: 19_031 nanoseconds. Weight::from_parts(20_264_948, 5742) // Standard Error: 707 - .saturating_add(Weight::from_ref_time(51_060).saturating_mul(m.into())) + .saturating_add(Weight::from_parts(51_060, 0).saturating_mul(m.into())) .saturating_add(RocksDbWeight::get().reads(3_u64)) .saturating_add(RocksDbWeight::get().writes(4_u64)) - .saturating_add(Weight::from_proof_size(192).saturating_mul(m.into())) + .saturating_add(Weight::from_parts(0, 192).saturating_mul(m.into())) } /// Storage: TechnicalMembership Members (r:1 w:0) /// Proof: TechnicalMembership Members (max_values: Some(1), max_size: Some(3202), added: 3697, mode: MaxEncodedLen) @@ -338,10 +338,10 @@ impl WeightInfo for () { // Minimum execution time: 6_897 nanoseconds. Weight::from_parts(7_455_387, 3761) // Standard Error: 326 - .saturating_add(Weight::from_ref_time(16_653).saturating_mul(m.into())) + .saturating_add(Weight::from_parts(16_653, 0).saturating_mul(m.into())) .saturating_add(RocksDbWeight::get().reads(1_u64)) .saturating_add(RocksDbWeight::get().writes(2_u64)) - .saturating_add(Weight::from_proof_size(32).saturating_mul(m.into())) + .saturating_add(Weight::from_parts(0, 32).saturating_mul(m.into())) } /// Storage: TechnicalMembership Prime (r:0 w:1) /// Proof: TechnicalMembership Prime (max_values: Some(1), max_size: Some(32), added: 527, mode: MaxEncodedLen) @@ -353,9 +353,9 @@ impl WeightInfo for () { // Measured: `0` // Estimated: `0` // Minimum execution time: 3_400 nanoseconds. - Weight::from_ref_time(3_703_421) + Weight::from_parts(3_703_421, 0) // Standard Error: 119 - .saturating_add(Weight::from_ref_time(915).saturating_mul(m.into())) + .saturating_add(Weight::from_parts(915, 0).saturating_mul(m.into())) .saturating_add(RocksDbWeight::get().writes(2_u64)) } } diff --git a/frame/merkle-mountain-range/src/default_weights.rs b/frame/merkle-mountain-range/src/default_weights.rs index 5d2e37cdd..52e3f1303 100644 --- a/frame/merkle-mountain-range/src/default_weights.rs +++ b/frame/merkle-mountain-range/src/default_weights.rs @@ -28,7 +28,7 @@ impl crate::WeightInfo for () { // Reading the parent hash. let leaf_weight = DbWeight::get().reads(1); // Blake2 hash cost. - let hash_weight = Weight::from_ref_time(2u64 * WEIGHT_REF_TIME_PER_NANOS); + let hash_weight = Weight::from_parts(2u64 * WEIGHT_REF_TIME_PER_NANOS, 0); // No-op hook. let hook_weight = Weight::zero(); diff --git a/frame/message-queue/src/weights.rs b/frame/message-queue/src/weights.rs index 3421c2f2f..d1ccb338e 100644 --- a/frame/message-queue/src/weights.rs +++ b/frame/message-queue/src/weights.rs @@ -73,8 +73,8 @@ impl WeightInfo for SubstrateWeight { // Measured: `295` // Estimated: `7527` // Minimum execution time: 12_538 nanoseconds. - Weight::from_ref_time(12_799_000) - .saturating_add(Weight::from_proof_size(7527)) + Weight::from_parts(12_799_000, 0) + .saturating_add(Weight::from_parts(0, 7527)) .saturating_add(T::DbWeight::get().reads(3_u64)) .saturating_add(T::DbWeight::get().writes(2_u64)) } @@ -87,8 +87,8 @@ impl WeightInfo for SubstrateWeight { // Measured: `295` // Estimated: `7527` // Minimum execution time: 11_727 nanoseconds. - Weight::from_ref_time(12_177_000) - .saturating_add(Weight::from_proof_size(7527)) + Weight::from_parts(12_177_000, 0) + .saturating_add(Weight::from_parts(0, 7527)) .saturating_add(T::DbWeight::get().reads(3_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) } @@ -99,8 +99,8 @@ impl WeightInfo for SubstrateWeight { // Measured: `42` // Estimated: `3514` // Minimum execution time: 4_983 nanoseconds. - Weight::from_ref_time(5_174_000) - .saturating_add(Weight::from_proof_size(3514)) + Weight::from_parts(5_174_000, 0) + .saturating_add(Weight::from_parts(0, 3514)) .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -111,8 +111,8 @@ impl WeightInfo for SubstrateWeight { // Measured: `113` // Estimated: `69049` // Minimum execution time: 6_299 nanoseconds. - Weight::from_ref_time(6_670_000) - .saturating_add(Weight::from_proof_size(69049)) + Weight::from_parts(6_670_000, 0) + .saturating_add(Weight::from_parts(0, 69049)) .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -123,8 +123,8 @@ impl WeightInfo for SubstrateWeight { // Measured: `113` // Estimated: `69049` // Minimum execution time: 6_762 nanoseconds. - Weight::from_ref_time(7_059_000) - .saturating_add(Weight::from_proof_size(69049)) + Weight::from_parts(7_059_000, 0) + .saturating_add(Weight::from_parts(0, 69049)) .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -133,8 +133,8 @@ impl WeightInfo for SubstrateWeight { // Measured: `0` // Estimated: `0` // Minimum execution time: 72_681 nanoseconds. - Weight::from_ref_time(73_147_000) - .saturating_add(Weight::from_proof_size(0)) + Weight::from_parts(73_147_000, 0) + .saturating_add(Weight::from_parts(0, 0)) } /// Storage: MessageQueue ServiceHead (r:1 w:1) /// Proof: MessageQueue ServiceHead (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) @@ -145,8 +145,8 @@ impl WeightInfo for SubstrateWeight { // Measured: `172` // Estimated: `5003` // Minimum execution time: 7_066 nanoseconds. - Weight::from_ref_time(7_214_000) - .saturating_add(Weight::from_proof_size(5003)) + Weight::from_parts(7_214_000, 0) + .saturating_add(Weight::from_parts(0, 5003)) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -159,8 +159,8 @@ impl WeightInfo for SubstrateWeight { // Measured: `65742` // Estimated: `72563` // Minimum execution time: 57_778 nanoseconds. - Weight::from_ref_time(58_778_000) - .saturating_add(Weight::from_proof_size(72563)) + Weight::from_parts(58_778_000, 0) + .saturating_add(Weight::from_parts(0, 72563)) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().writes(2_u64)) } @@ -173,8 +173,8 @@ impl WeightInfo for SubstrateWeight { // Measured: `65742` // Estimated: `72563` // Minimum execution time: 72_144 nanoseconds. - Weight::from_ref_time(72_942_000) - .saturating_add(Weight::from_proof_size(72563)) + Weight::from_parts(72_942_000, 0) + .saturating_add(Weight::from_parts(0, 72563)) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().writes(2_u64)) } @@ -187,8 +187,8 @@ impl WeightInfo for SubstrateWeight { // Measured: `65742` // Estimated: `72563` // Minimum execution time: 84_890 nanoseconds. - Weight::from_ref_time(86_073_000) - .saturating_add(Weight::from_proof_size(72563)) + Weight::from_parts(86_073_000, 0) + .saturating_add(Weight::from_parts(0, 72563)) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().writes(2_u64)) } @@ -205,8 +205,8 @@ impl WeightInfo for () { // Measured: `295` // Estimated: `7527` // Minimum execution time: 12_538 nanoseconds. - Weight::from_ref_time(12_799_000) - .saturating_add(Weight::from_proof_size(7527)) + Weight::from_parts(12_799_000, 0) + .saturating_add(Weight::from_parts(0, 7527)) .saturating_add(RocksDbWeight::get().reads(3_u64)) .saturating_add(RocksDbWeight::get().writes(2_u64)) } @@ -219,8 +219,8 @@ impl WeightInfo for () { // Measured: `295` // Estimated: `7527` // Minimum execution time: 11_727 nanoseconds. - Weight::from_ref_time(12_177_000) - .saturating_add(Weight::from_proof_size(7527)) + Weight::from_parts(12_177_000, 0) + .saturating_add(Weight::from_parts(0, 7527)) .saturating_add(RocksDbWeight::get().reads(3_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) } @@ -231,8 +231,8 @@ impl WeightInfo for () { // Measured: `42` // Estimated: `3514` // Minimum execution time: 4_983 nanoseconds. - Weight::from_ref_time(5_174_000) - .saturating_add(Weight::from_proof_size(3514)) + Weight::from_parts(5_174_000, 0) + .saturating_add(Weight::from_parts(0, 3514)) .saturating_add(RocksDbWeight::get().reads(1_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } @@ -243,8 +243,8 @@ impl WeightInfo for () { // Measured: `113` // Estimated: `69049` // Minimum execution time: 6_299 nanoseconds. - Weight::from_ref_time(6_670_000) - .saturating_add(Weight::from_proof_size(69049)) + Weight::from_parts(6_670_000, 0) + .saturating_add(Weight::from_parts(0, 69049)) .saturating_add(RocksDbWeight::get().reads(1_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } @@ -255,8 +255,8 @@ impl WeightInfo for () { // Measured: `113` // Estimated: `69049` // Minimum execution time: 6_762 nanoseconds. - Weight::from_ref_time(7_059_000) - .saturating_add(Weight::from_proof_size(69049)) + Weight::from_parts(7_059_000, 0) + .saturating_add(Weight::from_parts(0, 69049)) .saturating_add(RocksDbWeight::get().reads(1_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } @@ -265,8 +265,8 @@ impl WeightInfo for () { // Measured: `0` // Estimated: `0` // Minimum execution time: 72_681 nanoseconds. - Weight::from_ref_time(73_147_000) - .saturating_add(Weight::from_proof_size(0)) + Weight::from_parts(73_147_000, 0) + .saturating_add(Weight::from_parts(0, 0)) } /// Storage: MessageQueue ServiceHead (r:1 w:1) /// Proof: MessageQueue ServiceHead (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) @@ -277,8 +277,8 @@ impl WeightInfo for () { // Measured: `172` // Estimated: `5003` // Minimum execution time: 7_066 nanoseconds. - Weight::from_ref_time(7_214_000) - .saturating_add(Weight::from_proof_size(5003)) + Weight::from_parts(7_214_000, 0) + .saturating_add(Weight::from_parts(0, 5003)) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } @@ -291,8 +291,8 @@ impl WeightInfo for () { // Measured: `65742` // Estimated: `72563` // Minimum execution time: 57_778 nanoseconds. - Weight::from_ref_time(58_778_000) - .saturating_add(Weight::from_proof_size(72563)) + Weight::from_parts(58_778_000, 0) + .saturating_add(Weight::from_parts(0, 72563)) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().writes(2_u64)) } @@ -305,8 +305,8 @@ impl WeightInfo for () { // Measured: `65742` // Estimated: `72563` // Minimum execution time: 72_144 nanoseconds. - Weight::from_ref_time(72_942_000) - .saturating_add(Weight::from_proof_size(72563)) + Weight::from_parts(72_942_000, 0) + .saturating_add(Weight::from_parts(0, 72563)) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().writes(2_u64)) } @@ -319,8 +319,8 @@ impl WeightInfo for () { // Measured: `65742` // Estimated: `72563` // Minimum execution time: 84_890 nanoseconds. - Weight::from_ref_time(86_073_000) - .saturating_add(Weight::from_proof_size(72563)) + Weight::from_parts(86_073_000, 0) + .saturating_add(Weight::from_parts(0, 72563)) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().writes(2_u64)) } diff --git a/frame/multisig/src/weights.rs b/frame/multisig/src/weights.rs index eff6a2980..fb155c97f 100644 --- a/frame/multisig/src/weights.rs +++ b/frame/multisig/src/weights.rs @@ -66,9 +66,9 @@ impl WeightInfo for SubstrateWeight { // Measured: `0` // Estimated: `0` // Minimum execution time: 12_086 nanoseconds. - Weight::from_ref_time(12_464_828) + Weight::from_parts(12_464_828, 0) // Standard Error: 1 - .saturating_add(Weight::from_ref_time(494).saturating_mul(z.into())) + .saturating_add(Weight::from_parts(494, 0).saturating_mul(z.into())) } /// Storage: Multisig Multisigs (r:1 w:1) /// Proof: Multisig Multisigs (max_values: None, max_size: Some(3346), added: 5821, mode: MaxEncodedLen) @@ -81,9 +81,9 @@ impl WeightInfo for SubstrateWeight { // Minimum execution time: 35_377 nanoseconds. Weight::from_parts(29_088_956, 5821) // Standard Error: 335 - .saturating_add(Weight::from_ref_time(67_846).saturating_mul(s.into())) + .saturating_add(Weight::from_parts(67_846, 0).saturating_mul(s.into())) // Standard Error: 3 - .saturating_add(Weight::from_ref_time(1_523).saturating_mul(z.into())) + .saturating_add(Weight::from_parts(1_523, 0).saturating_mul(z.into())) .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -98,9 +98,9 @@ impl WeightInfo for SubstrateWeight { // Minimum execution time: 26_138 nanoseconds. Weight::from_parts(20_479_380, 5821) // Standard Error: 259 - .saturating_add(Weight::from_ref_time(64_116).saturating_mul(s.into())) + .saturating_add(Weight::from_parts(64_116, 0).saturating_mul(s.into())) // Standard Error: 2 - .saturating_add(Weight::from_ref_time(1_520).saturating_mul(z.into())) + .saturating_add(Weight::from_parts(1_520, 0).saturating_mul(z.into())) .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -117,9 +117,9 @@ impl WeightInfo for SubstrateWeight { // Minimum execution time: 40_323 nanoseconds. Weight::from_parts(32_311_615, 8424) // Standard Error: 401 - .saturating_add(Weight::from_ref_time(85_999).saturating_mul(s.into())) + .saturating_add(Weight::from_parts(85_999, 0).saturating_mul(s.into())) // Standard Error: 3 - .saturating_add(Weight::from_ref_time(1_534).saturating_mul(z.into())) + .saturating_add(Weight::from_parts(1_534, 0).saturating_mul(z.into())) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().writes(2_u64)) } @@ -133,7 +133,7 @@ impl WeightInfo for SubstrateWeight { // Minimum execution time: 26_938 nanoseconds. Weight::from_parts(27_802_216, 5821) // Standard Error: 342 - .saturating_add(Weight::from_ref_time(69_282).saturating_mul(s.into())) + .saturating_add(Weight::from_parts(69_282, 0).saturating_mul(s.into())) .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -147,7 +147,7 @@ impl WeightInfo for SubstrateWeight { // Minimum execution time: 18_050 nanoseconds. Weight::from_parts(19_095_404, 5821) // Standard Error: 419 - .saturating_add(Weight::from_ref_time(66_914).saturating_mul(s.into())) + .saturating_add(Weight::from_parts(66_914, 0).saturating_mul(s.into())) .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -161,7 +161,7 @@ impl WeightInfo for SubstrateWeight { // Minimum execution time: 27_508 nanoseconds. Weight::from_parts(28_702_686, 5821) // Standard Error: 466 - .saturating_add(Weight::from_ref_time(69_419).saturating_mul(s.into())) + .saturating_add(Weight::from_parts(69_419, 0).saturating_mul(s.into())) .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -175,9 +175,9 @@ impl WeightInfo for () { // Measured: `0` // Estimated: `0` // Minimum execution time: 12_086 nanoseconds. - Weight::from_ref_time(12_464_828) + Weight::from_parts(12_464_828, 0) // Standard Error: 1 - .saturating_add(Weight::from_ref_time(494).saturating_mul(z.into())) + .saturating_add(Weight::from_parts(494, 0).saturating_mul(z.into())) } /// Storage: Multisig Multisigs (r:1 w:1) /// Proof: Multisig Multisigs (max_values: None, max_size: Some(3346), added: 5821, mode: MaxEncodedLen) @@ -190,9 +190,9 @@ impl WeightInfo for () { // Minimum execution time: 35_377 nanoseconds. Weight::from_parts(29_088_956, 5821) // Standard Error: 335 - .saturating_add(Weight::from_ref_time(67_846).saturating_mul(s.into())) + .saturating_add(Weight::from_parts(67_846, 0).saturating_mul(s.into())) // Standard Error: 3 - .saturating_add(Weight::from_ref_time(1_523).saturating_mul(z.into())) + .saturating_add(Weight::from_parts(1_523, 0).saturating_mul(z.into())) .saturating_add(RocksDbWeight::get().reads(1_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } @@ -207,9 +207,9 @@ impl WeightInfo for () { // Minimum execution time: 26_138 nanoseconds. Weight::from_parts(20_479_380, 5821) // Standard Error: 259 - .saturating_add(Weight::from_ref_time(64_116).saturating_mul(s.into())) + .saturating_add(Weight::from_parts(64_116, 0).saturating_mul(s.into())) // Standard Error: 2 - .saturating_add(Weight::from_ref_time(1_520).saturating_mul(z.into())) + .saturating_add(Weight::from_parts(1_520, 0).saturating_mul(z.into())) .saturating_add(RocksDbWeight::get().reads(1_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } @@ -226,9 +226,9 @@ impl WeightInfo for () { // Minimum execution time: 40_323 nanoseconds. Weight::from_parts(32_311_615, 8424) // Standard Error: 401 - .saturating_add(Weight::from_ref_time(85_999).saturating_mul(s.into())) + .saturating_add(Weight::from_parts(85_999, 0).saturating_mul(s.into())) // Standard Error: 3 - .saturating_add(Weight::from_ref_time(1_534).saturating_mul(z.into())) + .saturating_add(Weight::from_parts(1_534, 0).saturating_mul(z.into())) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().writes(2_u64)) } @@ -242,7 +242,7 @@ impl WeightInfo for () { // Minimum execution time: 26_938 nanoseconds. Weight::from_parts(27_802_216, 5821) // Standard Error: 342 - .saturating_add(Weight::from_ref_time(69_282).saturating_mul(s.into())) + .saturating_add(Weight::from_parts(69_282, 0).saturating_mul(s.into())) .saturating_add(RocksDbWeight::get().reads(1_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } @@ -256,7 +256,7 @@ impl WeightInfo for () { // Minimum execution time: 18_050 nanoseconds. Weight::from_parts(19_095_404, 5821) // Standard Error: 419 - .saturating_add(Weight::from_ref_time(66_914).saturating_mul(s.into())) + .saturating_add(Weight::from_parts(66_914, 0).saturating_mul(s.into())) .saturating_add(RocksDbWeight::get().reads(1_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } @@ -270,7 +270,7 @@ impl WeightInfo for () { // Minimum execution time: 27_508 nanoseconds. Weight::from_parts(28_702_686, 5821) // Standard Error: 466 - .saturating_add(Weight::from_ref_time(69_419).saturating_mul(s.into())) + .saturating_add(Weight::from_parts(69_419, 0).saturating_mul(s.into())) .saturating_add(RocksDbWeight::get().reads(1_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } diff --git a/frame/nfts/src/weights.rs b/frame/nfts/src/weights.rs index 9eedea958..858ae2fec 100644 --- a/frame/nfts/src/weights.rs +++ b/frame/nfts/src/weights.rs @@ -108,8 +108,8 @@ impl WeightInfo for SubstrateWeight { // Measured: `214` // Estimated: `3054` // Minimum execution time: 33_769 nanoseconds. - Weight::from_ref_time(36_031_000) - .saturating_add(Weight::from_proof_size(3054)) + Weight::from_parts(36_031_000, 0) + .saturating_add(Weight::from_parts(0, 3054)) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().writes(5_u64)) } @@ -128,8 +128,8 @@ impl WeightInfo for SubstrateWeight { // Measured: `42` // Estimated: `3054` // Minimum execution time: 21_767 nanoseconds. - Weight::from_ref_time(22_565_000) - .saturating_add(Weight::from_proof_size(3054)) + Weight::from_parts(22_565_000, 0) + .saturating_add(Weight::from_parts(0, 3054)) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().writes(5_u64)) } @@ -161,20 +161,20 @@ impl WeightInfo for SubstrateWeight { // Measured: `172781 + m * (127 ±0) + a * (402 ±0)` // Estimated: `3347427 + m * (2615 ±0) + a * (2921 ±0)` // Minimum execution time: 26_973_627 nanoseconds. - Weight::from_ref_time(19_692_361_714) - .saturating_add(Weight::from_proof_size(3347427)) + Weight::from_parts(19_692_361_714, 0) + .saturating_add(Weight::from_parts(0, 3347427)) // Standard Error: 17_036 - .saturating_add(Weight::from_ref_time(7_797_219).saturating_mul(m.into())) + .saturating_add(Weight::from_parts(7_797_219, 0).saturating_mul(m.into())) // Standard Error: 17_036 - .saturating_add(Weight::from_ref_time(9_504_128).saturating_mul(a.into())) + .saturating_add(Weight::from_parts(9_504_128, 0).saturating_mul(a.into())) .saturating_add(T::DbWeight::get().reads(1004_u64)) .saturating_add(T::DbWeight::get().reads((1_u64).saturating_mul(m.into()))) .saturating_add(T::DbWeight::get().reads((1_u64).saturating_mul(a.into()))) .saturating_add(T::DbWeight::get().writes(3005_u64)) .saturating_add(T::DbWeight::get().writes((1_u64).saturating_mul(m.into()))) .saturating_add(T::DbWeight::get().writes((1_u64).saturating_mul(a.into()))) - .saturating_add(Weight::from_proof_size(2615).saturating_mul(m.into())) - .saturating_add(Weight::from_proof_size(2921).saturating_mul(a.into())) + .saturating_add(Weight::from_parts(0, 2615).saturating_mul(m.into())) + .saturating_add(Weight::from_parts(0, 2921).saturating_mul(a.into())) } /// Storage: Nfts CollectionConfigOf (r:1 w:0) /// Proof: Nfts CollectionConfigOf (max_values: None, max_size: Some(73), added: 2548, mode: MaxEncodedLen) @@ -193,8 +193,8 @@ impl WeightInfo for SubstrateWeight { // Measured: `448` // Estimated: `13506` // Minimum execution time: 44_837 nanoseconds. - Weight::from_ref_time(46_794_000) - .saturating_add(Weight::from_proof_size(13506)) + Weight::from_parts(46_794_000, 0) + .saturating_add(Weight::from_parts(0, 13506)) .saturating_add(T::DbWeight::get().reads(5_u64)) .saturating_add(T::DbWeight::get().writes(4_u64)) } @@ -215,8 +215,8 @@ impl WeightInfo for SubstrateWeight { // Measured: `448` // Estimated: `13506` // Minimum execution time: 43_976 nanoseconds. - Weight::from_ref_time(44_831_000) - .saturating_add(Weight::from_proof_size(13506)) + Weight::from_parts(44_831_000, 0) + .saturating_add(Weight::from_parts(0, 13506)) .saturating_add(T::DbWeight::get().reads(5_u64)) .saturating_add(T::DbWeight::get().writes(4_u64)) } @@ -243,8 +243,8 @@ impl WeightInfo for SubstrateWeight { // Measured: `647` // Estimated: `13573` // Minimum execution time: 48_233 nanoseconds. - Weight::from_ref_time(50_113_000) - .saturating_add(Weight::from_proof_size(13573)) + Weight::from_parts(50_113_000, 0) + .saturating_add(Weight::from_parts(0, 13573)) .saturating_add(T::DbWeight::get().reads(5_u64)) .saturating_add(T::DbWeight::get().writes(7_u64)) } @@ -271,8 +271,8 @@ impl WeightInfo for SubstrateWeight { // Measured: `882` // Estimated: `16109` // Minimum execution time: 55_452 nanoseconds. - Weight::from_ref_time(57_642_000) - .saturating_add(Weight::from_proof_size(16109)) + Weight::from_parts(57_642_000, 0) + .saturating_add(Weight::from_parts(0, 16109)) .saturating_add(T::DbWeight::get().reads(6_u64)) .saturating_add(T::DbWeight::get().writes(6_u64)) } @@ -288,14 +288,14 @@ impl WeightInfo for SubstrateWeight { // Measured: `756 + i * (140 ±0)` // Estimated: `5103 + i * (3336 ±0)` // Minimum execution time: 15_598 nanoseconds. - Weight::from_ref_time(15_926_000) - .saturating_add(Weight::from_proof_size(5103)) + Weight::from_parts(15_926_000, 0) + .saturating_add(Weight::from_parts(0, 5103)) // Standard Error: 13_692 - .saturating_add(Weight::from_ref_time(14_040_741).saturating_mul(i.into())) + .saturating_add(Weight::from_parts(14_040_741, 0).saturating_mul(i.into())) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().reads((1_u64).saturating_mul(i.into()))) .saturating_add(T::DbWeight::get().writes((1_u64).saturating_mul(i.into()))) - .saturating_add(Weight::from_proof_size(3336).saturating_mul(i.into())) + .saturating_add(Weight::from_parts(0, 3336).saturating_mul(i.into())) } /// Storage: Nfts CollectionRoleOf (r:1 w:0) /// Proof: Nfts CollectionRoleOf (max_values: None, max_size: Some(69), added: 2544, mode: MaxEncodedLen) @@ -306,8 +306,8 @@ impl WeightInfo for SubstrateWeight { // Measured: `401` // Estimated: `5067` // Minimum execution time: 19_686 nanoseconds. - Weight::from_ref_time(20_404_000) - .saturating_add(Weight::from_proof_size(5067)) + Weight::from_parts(20_404_000, 0) + .saturating_add(Weight::from_parts(0, 5067)) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -320,8 +320,8 @@ impl WeightInfo for SubstrateWeight { // Measured: `401` // Estimated: `5067` // Minimum execution time: 19_172 nanoseconds. - Weight::from_ref_time(20_151_000) - .saturating_add(Weight::from_proof_size(5067)) + Weight::from_parts(20_151_000, 0) + .saturating_add(Weight::from_parts(0, 5067)) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -334,8 +334,8 @@ impl WeightInfo for SubstrateWeight { // Measured: `289` // Estimated: `5092` // Minimum execution time: 17_063 nanoseconds. - Weight::from_ref_time(17_482_000) - .saturating_add(Weight::from_proof_size(5092)) + Weight::from_parts(17_482_000, 0) + .saturating_add(Weight::from_parts(0, 5092)) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -350,8 +350,8 @@ impl WeightInfo for SubstrateWeight { // Measured: `381` // Estimated: `5082` // Minimum execution time: 21_974 nanoseconds. - Weight::from_ref_time(22_770_000) - .saturating_add(Weight::from_proof_size(5082)) + Weight::from_parts(22_770_000, 0) + .saturating_add(Weight::from_parts(0, 5082)) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().writes(4_u64)) } @@ -364,8 +364,8 @@ impl WeightInfo for SubstrateWeight { // Measured: `362` // Estimated: `2555` // Minimum execution time: 24_341 nanoseconds. - Weight::from_ref_time(25_059_000) - .saturating_add(Weight::from_proof_size(2555)) + Weight::from_parts(25_059_000, 0) + .saturating_add(Weight::from_parts(0, 2555)) .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(T::DbWeight::get().writes(5_u64)) } @@ -378,8 +378,8 @@ impl WeightInfo for SubstrateWeight { // Measured: `304` // Estimated: `2555` // Minimum execution time: 16_897 nanoseconds. - Weight::from_ref_time(17_560_000) - .saturating_add(Weight::from_proof_size(2555)) + Weight::from_parts(17_560_000, 0) + .saturating_add(Weight::from_parts(0, 2555)) .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) } @@ -392,8 +392,8 @@ impl WeightInfo for SubstrateWeight { // Measured: `242` // Estimated: `2555` // Minimum execution time: 13_239 nanoseconds. - Weight::from_ref_time(13_963_000) - .saturating_add(Weight::from_proof_size(2555)) + Weight::from_parts(13_963_000, 0) + .saturating_add(Weight::from_parts(0, 2555)) .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -406,8 +406,8 @@ impl WeightInfo for SubstrateWeight { // Measured: `445` // Estimated: `5078` // Minimum execution time: 17_187 nanoseconds. - Weight::from_ref_time(17_942_000) - .saturating_add(Weight::from_proof_size(5078)) + Weight::from_parts(17_942_000, 0) + .saturating_add(Weight::from_parts(0, 5078)) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -424,8 +424,8 @@ impl WeightInfo for SubstrateWeight { // Measured: `474` // Estimated: `10547` // Minimum execution time: 40_925 nanoseconds. - Weight::from_ref_time(42_733_000) - .saturating_add(Weight::from_proof_size(10547)) + Weight::from_parts(42_733_000, 0) + .saturating_add(Weight::from_parts(0, 10547)) .saturating_add(T::DbWeight::get().reads(4_u64)) .saturating_add(T::DbWeight::get().writes(2_u64)) } @@ -438,8 +438,8 @@ impl WeightInfo for SubstrateWeight { // Measured: `337` // Estimated: `5476` // Minimum execution time: 24_486 nanoseconds. - Weight::from_ref_time(25_409_000) - .saturating_add(Weight::from_proof_size(5476)) + Weight::from_parts(25_409_000, 0) + .saturating_add(Weight::from_parts(0, 5476)) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().writes(2_u64)) } @@ -454,8 +454,8 @@ impl WeightInfo for SubstrateWeight { // Measured: `916` // Estimated: `7999` // Minimum execution time: 36_643 nanoseconds. - Weight::from_ref_time(37_805_000) - .saturating_add(Weight::from_proof_size(7999)) + Weight::from_parts(37_805_000, 0) + .saturating_add(Weight::from_parts(0, 7999)) .saturating_add(T::DbWeight::get().reads(3_u64)) .saturating_add(T::DbWeight::get().writes(2_u64)) } @@ -468,8 +468,8 @@ impl WeightInfo for SubstrateWeight { // Measured: `379` // Estimated: `6492` // Minimum execution time: 16_798 nanoseconds. - Weight::from_ref_time(17_326_000) - .saturating_add(Weight::from_proof_size(6492)) + Weight::from_parts(17_326_000, 0) + .saturating_add(Weight::from_parts(0, 6492)) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -487,15 +487,15 @@ impl WeightInfo for SubstrateWeight { // Measured: `899 + n * (396 ±0)` // Estimated: `12016 + n * (2921 ±0)` // Minimum execution time: 25_524 nanoseconds. - Weight::from_ref_time(26_107_000) - .saturating_add(Weight::from_proof_size(12016)) + Weight::from_parts(26_107_000, 0) + .saturating_add(Weight::from_parts(0, 12016)) // Standard Error: 5_460 - .saturating_add(Weight::from_ref_time(9_030_830).saturating_mul(n.into())) + .saturating_add(Weight::from_parts(9_030_830, 0).saturating_mul(n.into())) .saturating_add(T::DbWeight::get().reads(4_u64)) .saturating_add(T::DbWeight::get().reads((1_u64).saturating_mul(n.into()))) .saturating_add(T::DbWeight::get().writes(2_u64)) .saturating_add(T::DbWeight::get().writes((1_u64).saturating_mul(n.into()))) - .saturating_add(Weight::from_proof_size(2921).saturating_mul(n.into())) + .saturating_add(Weight::from_parts(0, 2921).saturating_mul(n.into())) } /// Storage: Nfts Collection (r:1 w:1) /// Proof: Nfts Collection (max_values: None, max_size: Some(80), added: 2555, mode: MaxEncodedLen) @@ -510,8 +510,8 @@ impl WeightInfo for SubstrateWeight { // Measured: `474` // Estimated: `10241` // Minimum execution time: 34_400 nanoseconds. - Weight::from_ref_time(35_469_000) - .saturating_add(Weight::from_proof_size(10241)) + Weight::from_parts(35_469_000, 0) + .saturating_add(Weight::from_parts(0, 10241)) .saturating_add(T::DbWeight::get().reads(4_u64)) .saturating_add(T::DbWeight::get().writes(2_u64)) } @@ -526,8 +526,8 @@ impl WeightInfo for SubstrateWeight { // Measured: `609` // Estimated: `7693` // Minimum execution time: 31_560 nanoseconds. - Weight::from_ref_time(33_081_000) - .saturating_add(Weight::from_proof_size(7693)) + Weight::from_parts(33_081_000, 0) + .saturating_add(Weight::from_parts(0, 7693)) .saturating_add(T::DbWeight::get().reads(3_u64)) .saturating_add(T::DbWeight::get().writes(2_u64)) } @@ -542,8 +542,8 @@ impl WeightInfo for SubstrateWeight { // Measured: `333` // Estimated: `7665` // Minimum execution time: 28_821 nanoseconds. - Weight::from_ref_time(30_010_000) - .saturating_add(Weight::from_proof_size(7665)) + Weight::from_parts(30_010_000, 0) + .saturating_add(Weight::from_parts(0, 7665)) .saturating_add(T::DbWeight::get().reads(3_u64)) .saturating_add(T::DbWeight::get().writes(2_u64)) } @@ -558,8 +558,8 @@ impl WeightInfo for SubstrateWeight { // Measured: `476` // Estimated: `7665` // Minimum execution time: 27_608 nanoseconds. - Weight::from_ref_time(28_766_000) - .saturating_add(Weight::from_proof_size(7665)) + Weight::from_parts(28_766_000, 0) + .saturating_add(Weight::from_parts(0, 7665)) .saturating_add(T::DbWeight::get().reads(3_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -574,8 +574,8 @@ impl WeightInfo for SubstrateWeight { // Measured: `466` // Estimated: `8428` // Minimum execution time: 23_987 nanoseconds. - Weight::from_ref_time(24_819_000) - .saturating_add(Weight::from_proof_size(8428)) + Weight::from_parts(24_819_000, 0) + .saturating_add(Weight::from_parts(0, 8428)) .saturating_add(T::DbWeight::get().reads(3_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -588,8 +588,8 @@ impl WeightInfo for SubstrateWeight { // Measured: `474` // Estimated: `5880` // Minimum execution time: 21_254 nanoseconds. - Weight::from_ref_time(21_826_000) - .saturating_add(Weight::from_proof_size(5880)) + Weight::from_parts(21_826_000, 0) + .saturating_add(Weight::from_parts(0, 5880)) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -602,8 +602,8 @@ impl WeightInfo for SubstrateWeight { // Measured: `474` // Estimated: `5880` // Minimum execution time: 20_272 nanoseconds. - Weight::from_ref_time(20_922_000) - .saturating_add(Weight::from_proof_size(5880)) + Weight::from_parts(20_922_000, 0) + .saturating_add(Weight::from_parts(0, 5880)) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -614,8 +614,8 @@ impl WeightInfo for SubstrateWeight { // Measured: `42` // Estimated: `2527` // Minimum execution time: 14_287 nanoseconds. - Weight::from_ref_time(14_960_000) - .saturating_add(Weight::from_proof_size(2527)) + Weight::from_parts(14_960_000, 0) + .saturating_add(Weight::from_parts(0, 2527)) .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -628,8 +628,8 @@ impl WeightInfo for SubstrateWeight { // Measured: `333` // Estimated: `5103` // Minimum execution time: 17_948 nanoseconds. - Weight::from_ref_time(18_780_000) - .saturating_add(Weight::from_proof_size(5103)) + Weight::from_parts(18_780_000, 0) + .saturating_add(Weight::from_parts(0, 5103)) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -642,8 +642,8 @@ impl WeightInfo for SubstrateWeight { // Measured: `333` // Estimated: `5103` // Minimum execution time: 16_616 nanoseconds. - Weight::from_ref_time(17_155_000) - .saturating_add(Weight::from_proof_size(5103)) + Weight::from_parts(17_155_000, 0) + .saturating_add(Weight::from_parts(0, 5103)) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -660,8 +660,8 @@ impl WeightInfo for SubstrateWeight { // Measured: `516` // Estimated: `8407` // Minimum execution time: 22_777 nanoseconds. - Weight::from_ref_time(23_955_000) - .saturating_add(Weight::from_proof_size(8407)) + Weight::from_parts(23_955_000, 0) + .saturating_add(Weight::from_parts(0, 8407)) .saturating_add(T::DbWeight::get().reads(3_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -686,8 +686,8 @@ impl WeightInfo for SubstrateWeight { // Measured: `934` // Estimated: `16129` // Minimum execution time: 61_131 nanoseconds. - Weight::from_ref_time(62_791_000) - .saturating_add(Weight::from_proof_size(16129)) + Weight::from_parts(62_791_000, 0) + .saturating_add(Weight::from_parts(0, 16129)) .saturating_add(T::DbWeight::get().reads(6_u64)) .saturating_add(T::DbWeight::get().writes(6_u64)) } @@ -697,10 +697,10 @@ impl WeightInfo for SubstrateWeight { // Measured: `0` // Estimated: `0` // Minimum execution time: 1_952 nanoseconds. - Weight::from_ref_time(3_975_700) - .saturating_add(Weight::from_proof_size(0)) + Weight::from_parts(3_975_700, 0) + .saturating_add(Weight::from_parts(0, 0)) // Standard Error: 11_254 - .saturating_add(Weight::from_ref_time(3_501_698).saturating_mul(n.into())) + .saturating_add(Weight::from_parts(3_501_698, 0).saturating_mul(n.into())) } /// Storage: Nfts Item (r:2 w:0) /// Proof: Nfts Item (max_values: None, max_size: Some(861), added: 3336, mode: MaxEncodedLen) @@ -711,8 +711,8 @@ impl WeightInfo for SubstrateWeight { // Measured: `524` // Estimated: `6672` // Minimum execution time: 20_327 nanoseconds. - Weight::from_ref_time(21_714_000) - .saturating_add(Weight::from_proof_size(6672)) + Weight::from_parts(21_714_000, 0) + .saturating_add(Weight::from_parts(0, 6672)) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -725,8 +725,8 @@ impl WeightInfo for SubstrateWeight { // Measured: `511` // Estimated: `5882` // Minimum execution time: 20_668 nanoseconds. - Weight::from_ref_time(21_416_000) - .saturating_add(Weight::from_proof_size(5882)) + Weight::from_parts(21_416_000, 0) + .saturating_add(Weight::from_parts(0, 5882)) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -751,8 +751,8 @@ impl WeightInfo for SubstrateWeight { // Measured: `1097` // Estimated: `21970` // Minimum execution time: 88_006 nanoseconds. - Weight::from_ref_time(90_390_000) - .saturating_add(Weight::from_proof_size(21970)) + Weight::from_parts(90_390_000, 0) + .saturating_add(Weight::from_parts(0, 21970)) .saturating_add(T::DbWeight::get().reads(8_u64)) .saturating_add(T::DbWeight::get().writes(11_u64)) } @@ -778,15 +778,15 @@ impl WeightInfo for SubstrateWeight { // Measured: `596` // Estimated: `16180 + n * (2921 ±0)` // Minimum execution time: 124_967 nanoseconds. - Weight::from_ref_time(131_602_642) - .saturating_add(Weight::from_proof_size(16180)) + Weight::from_parts(131_602_642, 0) + .saturating_add(Weight::from_parts(0, 16180)) // Standard Error: 36_480 - .saturating_add(Weight::from_ref_time(25_811_394).saturating_mul(n.into())) + .saturating_add(Weight::from_parts(25_811_394, 0).saturating_mul(n.into())) .saturating_add(T::DbWeight::get().reads(6_u64)) .saturating_add(T::DbWeight::get().reads((1_u64).saturating_mul(n.into()))) .saturating_add(T::DbWeight::get().writes(6_u64)) .saturating_add(T::DbWeight::get().writes((1_u64).saturating_mul(n.into()))) - .saturating_add(Weight::from_proof_size(2921).saturating_mul(n.into())) + .saturating_add(Weight::from_parts(0, 2921).saturating_mul(n.into())) } /// Storage: Nfts Item (r:1 w:0) /// Proof: Nfts Item (max_values: None, max_size: Some(861), added: 3336, mode: MaxEncodedLen) @@ -806,15 +806,15 @@ impl WeightInfo for SubstrateWeight { // Measured: `716` // Estimated: `14198 + n * (2921 ±0)` // Minimum execution time: 84_153 nanoseconds. - Weight::from_ref_time(96_401_623) - .saturating_add(Weight::from_proof_size(14198)) + Weight::from_parts(96_401_623, 0) + .saturating_add(Weight::from_parts(0, 14198)) // Standard Error: 70_244 - .saturating_add(Weight::from_ref_time(26_866_222).saturating_mul(n.into())) + .saturating_add(Weight::from_parts(26_866_222, 0).saturating_mul(n.into())) .saturating_add(T::DbWeight::get().reads(4_u64)) .saturating_add(T::DbWeight::get().reads((1_u64).saturating_mul(n.into()))) .saturating_add(T::DbWeight::get().writes(2_u64)) .saturating_add(T::DbWeight::get().writes((1_u64).saturating_mul(n.into()))) - .saturating_add(Weight::from_proof_size(2921).saturating_mul(n.into())) + .saturating_add(Weight::from_parts(0, 2921).saturating_mul(n.into())) } } @@ -835,8 +835,8 @@ impl WeightInfo for () { // Measured: `214` // Estimated: `3054` // Minimum execution time: 33_769 nanoseconds. - Weight::from_ref_time(36_031_000) - .saturating_add(Weight::from_proof_size(3054)) + Weight::from_parts(36_031_000, 0) + .saturating_add(Weight::from_parts(0, 3054)) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().writes(5_u64)) } @@ -855,8 +855,8 @@ impl WeightInfo for () { // Measured: `42` // Estimated: `3054` // Minimum execution time: 21_767 nanoseconds. - Weight::from_ref_time(22_565_000) - .saturating_add(Weight::from_proof_size(3054)) + Weight::from_parts(22_565_000, 0) + .saturating_add(Weight::from_parts(0, 3054)) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().writes(5_u64)) } @@ -888,20 +888,20 @@ impl WeightInfo for () { // Measured: `172781 + m * (127 ±0) + a * (402 ±0)` // Estimated: `3347427 + m * (2615 ±0) + a * (2921 ±0)` // Minimum execution time: 26_973_627 nanoseconds. - Weight::from_ref_time(19_692_361_714) - .saturating_add(Weight::from_proof_size(3347427)) + Weight::from_parts(19_692_361_714, 0) + .saturating_add(Weight::from_parts(0, 3347427)) // Standard Error: 17_036 - .saturating_add(Weight::from_ref_time(7_797_219).saturating_mul(m.into())) + .saturating_add(Weight::from_parts(7_797_219, 0).saturating_mul(m.into())) // Standard Error: 17_036 - .saturating_add(Weight::from_ref_time(9_504_128).saturating_mul(a.into())) + .saturating_add(Weight::from_parts(9_504_128, 0).saturating_mul(a.into())) .saturating_add(RocksDbWeight::get().reads(1004_u64)) .saturating_add(RocksDbWeight::get().reads((1_u64).saturating_mul(m.into()))) .saturating_add(RocksDbWeight::get().reads((1_u64).saturating_mul(a.into()))) .saturating_add(RocksDbWeight::get().writes(3005_u64)) .saturating_add(RocksDbWeight::get().writes((1_u64).saturating_mul(m.into()))) .saturating_add(RocksDbWeight::get().writes((1_u64).saturating_mul(a.into()))) - .saturating_add(Weight::from_proof_size(2615).saturating_mul(m.into())) - .saturating_add(Weight::from_proof_size(2921).saturating_mul(a.into())) + .saturating_add(Weight::from_parts(0, 2615).saturating_mul(m.into())) + .saturating_add(Weight::from_parts(0, 2921).saturating_mul(a.into())) } /// Storage: Nfts CollectionConfigOf (r:1 w:0) /// Proof: Nfts CollectionConfigOf (max_values: None, max_size: Some(73), added: 2548, mode: MaxEncodedLen) @@ -920,8 +920,8 @@ impl WeightInfo for () { // Measured: `448` // Estimated: `13506` // Minimum execution time: 44_837 nanoseconds. - Weight::from_ref_time(46_794_000) - .saturating_add(Weight::from_proof_size(13506)) + Weight::from_parts(46_794_000, 0) + .saturating_add(Weight::from_parts(0, 13506)) .saturating_add(RocksDbWeight::get().reads(5_u64)) .saturating_add(RocksDbWeight::get().writes(4_u64)) } @@ -942,8 +942,8 @@ impl WeightInfo for () { // Measured: `448` // Estimated: `13506` // Minimum execution time: 43_976 nanoseconds. - Weight::from_ref_time(44_831_000) - .saturating_add(Weight::from_proof_size(13506)) + Weight::from_parts(44_831_000, 0) + .saturating_add(Weight::from_parts(0, 13506)) .saturating_add(RocksDbWeight::get().reads(5_u64)) .saturating_add(RocksDbWeight::get().writes(4_u64)) } @@ -970,8 +970,8 @@ impl WeightInfo for () { // Measured: `647` // Estimated: `13573` // Minimum execution time: 48_233 nanoseconds. - Weight::from_ref_time(50_113_000) - .saturating_add(Weight::from_proof_size(13573)) + Weight::from_parts(50_113_000, 0) + .saturating_add(Weight::from_parts(0, 13573)) .saturating_add(RocksDbWeight::get().reads(5_u64)) .saturating_add(RocksDbWeight::get().writes(7_u64)) } @@ -998,8 +998,8 @@ impl WeightInfo for () { // Measured: `882` // Estimated: `16109` // Minimum execution time: 55_452 nanoseconds. - Weight::from_ref_time(57_642_000) - .saturating_add(Weight::from_proof_size(16109)) + Weight::from_parts(57_642_000, 0) + .saturating_add(Weight::from_parts(0, 16109)) .saturating_add(RocksDbWeight::get().reads(6_u64)) .saturating_add(RocksDbWeight::get().writes(6_u64)) } @@ -1015,14 +1015,14 @@ impl WeightInfo for () { // Measured: `756 + i * (140 ±0)` // Estimated: `5103 + i * (3336 ±0)` // Minimum execution time: 15_598 nanoseconds. - Weight::from_ref_time(15_926_000) - .saturating_add(Weight::from_proof_size(5103)) + Weight::from_parts(15_926_000, 0) + .saturating_add(Weight::from_parts(0, 5103)) // Standard Error: 13_692 - .saturating_add(Weight::from_ref_time(14_040_741).saturating_mul(i.into())) + .saturating_add(Weight::from_parts(14_040_741, 0).saturating_mul(i.into())) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().reads((1_u64).saturating_mul(i.into()))) .saturating_add(RocksDbWeight::get().writes((1_u64).saturating_mul(i.into()))) - .saturating_add(Weight::from_proof_size(3336).saturating_mul(i.into())) + .saturating_add(Weight::from_parts(0, 3336).saturating_mul(i.into())) } /// Storage: Nfts CollectionRoleOf (r:1 w:0) /// Proof: Nfts CollectionRoleOf (max_values: None, max_size: Some(69), added: 2544, mode: MaxEncodedLen) @@ -1033,8 +1033,8 @@ impl WeightInfo for () { // Measured: `401` // Estimated: `5067` // Minimum execution time: 19_686 nanoseconds. - Weight::from_ref_time(20_404_000) - .saturating_add(Weight::from_proof_size(5067)) + Weight::from_parts(20_404_000, 0) + .saturating_add(Weight::from_parts(0, 5067)) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } @@ -1047,8 +1047,8 @@ impl WeightInfo for () { // Measured: `401` // Estimated: `5067` // Minimum execution time: 19_172 nanoseconds. - Weight::from_ref_time(20_151_000) - .saturating_add(Weight::from_proof_size(5067)) + Weight::from_parts(20_151_000, 0) + .saturating_add(Weight::from_parts(0, 5067)) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } @@ -1061,8 +1061,8 @@ impl WeightInfo for () { // Measured: `289` // Estimated: `5092` // Minimum execution time: 17_063 nanoseconds. - Weight::from_ref_time(17_482_000) - .saturating_add(Weight::from_proof_size(5092)) + Weight::from_parts(17_482_000, 0) + .saturating_add(Weight::from_parts(0, 5092)) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } @@ -1077,8 +1077,8 @@ impl WeightInfo for () { // Measured: `381` // Estimated: `5082` // Minimum execution time: 21_974 nanoseconds. - Weight::from_ref_time(22_770_000) - .saturating_add(Weight::from_proof_size(5082)) + Weight::from_parts(22_770_000, 0) + .saturating_add(Weight::from_parts(0, 5082)) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().writes(4_u64)) } @@ -1091,8 +1091,8 @@ impl WeightInfo for () { // Measured: `362` // Estimated: `2555` // Minimum execution time: 24_341 nanoseconds. - Weight::from_ref_time(25_059_000) - .saturating_add(Weight::from_proof_size(2555)) + Weight::from_parts(25_059_000, 0) + .saturating_add(Weight::from_parts(0, 2555)) .saturating_add(RocksDbWeight::get().reads(1_u64)) .saturating_add(RocksDbWeight::get().writes(5_u64)) } @@ -1105,8 +1105,8 @@ impl WeightInfo for () { // Measured: `304` // Estimated: `2555` // Minimum execution time: 16_897 nanoseconds. - Weight::from_ref_time(17_560_000) - .saturating_add(Weight::from_proof_size(2555)) + Weight::from_parts(17_560_000, 0) + .saturating_add(Weight::from_parts(0, 2555)) .saturating_add(RocksDbWeight::get().reads(1_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) } @@ -1119,8 +1119,8 @@ impl WeightInfo for () { // Measured: `242` // Estimated: `2555` // Minimum execution time: 13_239 nanoseconds. - Weight::from_ref_time(13_963_000) - .saturating_add(Weight::from_proof_size(2555)) + Weight::from_parts(13_963_000, 0) + .saturating_add(Weight::from_parts(0, 2555)) .saturating_add(RocksDbWeight::get().reads(1_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } @@ -1133,8 +1133,8 @@ impl WeightInfo for () { // Measured: `445` // Estimated: `5078` // Minimum execution time: 17_187 nanoseconds. - Weight::from_ref_time(17_942_000) - .saturating_add(Weight::from_proof_size(5078)) + Weight::from_parts(17_942_000, 0) + .saturating_add(Weight::from_parts(0, 5078)) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } @@ -1151,8 +1151,8 @@ impl WeightInfo for () { // Measured: `474` // Estimated: `10547` // Minimum execution time: 40_925 nanoseconds. - Weight::from_ref_time(42_733_000) - .saturating_add(Weight::from_proof_size(10547)) + Weight::from_parts(42_733_000, 0) + .saturating_add(Weight::from_parts(0, 10547)) .saturating_add(RocksDbWeight::get().reads(4_u64)) .saturating_add(RocksDbWeight::get().writes(2_u64)) } @@ -1165,8 +1165,8 @@ impl WeightInfo for () { // Measured: `337` // Estimated: `5476` // Minimum execution time: 24_486 nanoseconds. - Weight::from_ref_time(25_409_000) - .saturating_add(Weight::from_proof_size(5476)) + Weight::from_parts(25_409_000, 0) + .saturating_add(Weight::from_parts(0, 5476)) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().writes(2_u64)) } @@ -1181,8 +1181,8 @@ impl WeightInfo for () { // Measured: `916` // Estimated: `7999` // Minimum execution time: 36_643 nanoseconds. - Weight::from_ref_time(37_805_000) - .saturating_add(Weight::from_proof_size(7999)) + Weight::from_parts(37_805_000, 0) + .saturating_add(Weight::from_parts(0, 7999)) .saturating_add(RocksDbWeight::get().reads(3_u64)) .saturating_add(RocksDbWeight::get().writes(2_u64)) } @@ -1195,8 +1195,8 @@ impl WeightInfo for () { // Measured: `379` // Estimated: `6492` // Minimum execution time: 16_798 nanoseconds. - Weight::from_ref_time(17_326_000) - .saturating_add(Weight::from_proof_size(6492)) + Weight::from_parts(17_326_000, 0) + .saturating_add(Weight::from_parts(0, 6492)) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } @@ -1214,15 +1214,15 @@ impl WeightInfo for () { // Measured: `899 + n * (396 ±0)` // Estimated: `12016 + n * (2921 ±0)` // Minimum execution time: 25_524 nanoseconds. - Weight::from_ref_time(26_107_000) - .saturating_add(Weight::from_proof_size(12016)) + Weight::from_parts(26_107_000, 0) + .saturating_add(Weight::from_parts(0, 12016)) // Standard Error: 5_460 - .saturating_add(Weight::from_ref_time(9_030_830).saturating_mul(n.into())) + .saturating_add(Weight::from_parts(9_030_830, 0).saturating_mul(n.into())) .saturating_add(RocksDbWeight::get().reads(4_u64)) .saturating_add(RocksDbWeight::get().reads((1_u64).saturating_mul(n.into()))) .saturating_add(RocksDbWeight::get().writes(2_u64)) .saturating_add(RocksDbWeight::get().writes((1_u64).saturating_mul(n.into()))) - .saturating_add(Weight::from_proof_size(2921).saturating_mul(n.into())) + .saturating_add(Weight::from_parts(0, 2921).saturating_mul(n.into())) } /// Storage: Nfts Collection (r:1 w:1) /// Proof: Nfts Collection (max_values: None, max_size: Some(80), added: 2555, mode: MaxEncodedLen) @@ -1237,8 +1237,8 @@ impl WeightInfo for () { // Measured: `474` // Estimated: `10241` // Minimum execution time: 34_400 nanoseconds. - Weight::from_ref_time(35_469_000) - .saturating_add(Weight::from_proof_size(10241)) + Weight::from_parts(35_469_000, 0) + .saturating_add(Weight::from_parts(0, 10241)) .saturating_add(RocksDbWeight::get().reads(4_u64)) .saturating_add(RocksDbWeight::get().writes(2_u64)) } @@ -1253,8 +1253,8 @@ impl WeightInfo for () { // Measured: `609` // Estimated: `7693` // Minimum execution time: 31_560 nanoseconds. - Weight::from_ref_time(33_081_000) - .saturating_add(Weight::from_proof_size(7693)) + Weight::from_parts(33_081_000, 0) + .saturating_add(Weight::from_parts(0, 7693)) .saturating_add(RocksDbWeight::get().reads(3_u64)) .saturating_add(RocksDbWeight::get().writes(2_u64)) } @@ -1269,8 +1269,8 @@ impl WeightInfo for () { // Measured: `333` // Estimated: `7665` // Minimum execution time: 28_821 nanoseconds. - Weight::from_ref_time(30_010_000) - .saturating_add(Weight::from_proof_size(7665)) + Weight::from_parts(30_010_000, 0) + .saturating_add(Weight::from_parts(0, 7665)) .saturating_add(RocksDbWeight::get().reads(3_u64)) .saturating_add(RocksDbWeight::get().writes(2_u64)) } @@ -1285,8 +1285,8 @@ impl WeightInfo for () { // Measured: `476` // Estimated: `7665` // Minimum execution time: 27_608 nanoseconds. - Weight::from_ref_time(28_766_000) - .saturating_add(Weight::from_proof_size(7665)) + Weight::from_parts(28_766_000, 0) + .saturating_add(Weight::from_parts(0, 7665)) .saturating_add(RocksDbWeight::get().reads(3_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } @@ -1301,8 +1301,8 @@ impl WeightInfo for () { // Measured: `466` // Estimated: `8428` // Minimum execution time: 23_987 nanoseconds. - Weight::from_ref_time(24_819_000) - .saturating_add(Weight::from_proof_size(8428)) + Weight::from_parts(24_819_000, 0) + .saturating_add(Weight::from_parts(0, 8428)) .saturating_add(RocksDbWeight::get().reads(3_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } @@ -1315,8 +1315,8 @@ impl WeightInfo for () { // Measured: `474` // Estimated: `5880` // Minimum execution time: 21_254 nanoseconds. - Weight::from_ref_time(21_826_000) - .saturating_add(Weight::from_proof_size(5880)) + Weight::from_parts(21_826_000, 0) + .saturating_add(Weight::from_parts(0, 5880)) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } @@ -1329,8 +1329,8 @@ impl WeightInfo for () { // Measured: `474` // Estimated: `5880` // Minimum execution time: 20_272 nanoseconds. - Weight::from_ref_time(20_922_000) - .saturating_add(Weight::from_proof_size(5880)) + Weight::from_parts(20_922_000, 0) + .saturating_add(Weight::from_parts(0, 5880)) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } @@ -1341,8 +1341,8 @@ impl WeightInfo for () { // Measured: `42` // Estimated: `2527` // Minimum execution time: 14_287 nanoseconds. - Weight::from_ref_time(14_960_000) - .saturating_add(Weight::from_proof_size(2527)) + Weight::from_parts(14_960_000, 0) + .saturating_add(Weight::from_parts(0, 2527)) .saturating_add(RocksDbWeight::get().reads(1_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } @@ -1355,8 +1355,8 @@ impl WeightInfo for () { // Measured: `333` // Estimated: `5103` // Minimum execution time: 17_948 nanoseconds. - Weight::from_ref_time(18_780_000) - .saturating_add(Weight::from_proof_size(5103)) + Weight::from_parts(18_780_000, 0) + .saturating_add(Weight::from_parts(0, 5103)) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } @@ -1369,8 +1369,8 @@ impl WeightInfo for () { // Measured: `333` // Estimated: `5103` // Minimum execution time: 16_616 nanoseconds. - Weight::from_ref_time(17_155_000) - .saturating_add(Weight::from_proof_size(5103)) + Weight::from_parts(17_155_000, 0) + .saturating_add(Weight::from_parts(0, 5103)) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } @@ -1387,8 +1387,8 @@ impl WeightInfo for () { // Measured: `516` // Estimated: `8407` // Minimum execution time: 22_777 nanoseconds. - Weight::from_ref_time(23_955_000) - .saturating_add(Weight::from_proof_size(8407)) + Weight::from_parts(23_955_000, 0) + .saturating_add(Weight::from_parts(0, 8407)) .saturating_add(RocksDbWeight::get().reads(3_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } @@ -1413,8 +1413,8 @@ impl WeightInfo for () { // Measured: `934` // Estimated: `16129` // Minimum execution time: 61_131 nanoseconds. - Weight::from_ref_time(62_791_000) - .saturating_add(Weight::from_proof_size(16129)) + Weight::from_parts(62_791_000, 0) + .saturating_add(Weight::from_parts(0, 16129)) .saturating_add(RocksDbWeight::get().reads(6_u64)) .saturating_add(RocksDbWeight::get().writes(6_u64)) } @@ -1424,10 +1424,10 @@ impl WeightInfo for () { // Measured: `0` // Estimated: `0` // Minimum execution time: 1_952 nanoseconds. - Weight::from_ref_time(3_975_700) - .saturating_add(Weight::from_proof_size(0)) + Weight::from_parts(3_975_700, 0) + .saturating_add(Weight::from_parts(0, 0)) // Standard Error: 11_254 - .saturating_add(Weight::from_ref_time(3_501_698).saturating_mul(n.into())) + .saturating_add(Weight::from_parts(3_501_698, 0).saturating_mul(n.into())) } /// Storage: Nfts Item (r:2 w:0) /// Proof: Nfts Item (max_values: None, max_size: Some(861), added: 3336, mode: MaxEncodedLen) @@ -1438,8 +1438,8 @@ impl WeightInfo for () { // Measured: `524` // Estimated: `6672` // Minimum execution time: 20_327 nanoseconds. - Weight::from_ref_time(21_714_000) - .saturating_add(Weight::from_proof_size(6672)) + Weight::from_parts(21_714_000, 0) + .saturating_add(Weight::from_parts(0, 6672)) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } @@ -1452,8 +1452,8 @@ impl WeightInfo for () { // Measured: `511` // Estimated: `5882` // Minimum execution time: 20_668 nanoseconds. - Weight::from_ref_time(21_416_000) - .saturating_add(Weight::from_proof_size(5882)) + Weight::from_parts(21_416_000, 0) + .saturating_add(Weight::from_parts(0, 5882)) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } @@ -1478,8 +1478,8 @@ impl WeightInfo for () { // Measured: `1097` // Estimated: `21970` // Minimum execution time: 88_006 nanoseconds. - Weight::from_ref_time(90_390_000) - .saturating_add(Weight::from_proof_size(21970)) + Weight::from_parts(90_390_000, 0) + .saturating_add(Weight::from_parts(0, 21970)) .saturating_add(RocksDbWeight::get().reads(8_u64)) .saturating_add(RocksDbWeight::get().writes(11_u64)) } @@ -1505,15 +1505,15 @@ impl WeightInfo for () { // Measured: `596` // Estimated: `16180 + n * (2921 ±0)` // Minimum execution time: 124_967 nanoseconds. - Weight::from_ref_time(131_602_642) - .saturating_add(Weight::from_proof_size(16180)) + Weight::from_parts(131_602_642, 0) + .saturating_add(Weight::from_parts(0, 16180)) // Standard Error: 36_480 - .saturating_add(Weight::from_ref_time(25_811_394).saturating_mul(n.into())) + .saturating_add(Weight::from_parts(25_811_394, 0).saturating_mul(n.into())) .saturating_add(RocksDbWeight::get().reads(6_u64)) .saturating_add(RocksDbWeight::get().reads((1_u64).saturating_mul(n.into()))) .saturating_add(RocksDbWeight::get().writes(6_u64)) .saturating_add(RocksDbWeight::get().writes((1_u64).saturating_mul(n.into()))) - .saturating_add(Weight::from_proof_size(2921).saturating_mul(n.into())) + .saturating_add(Weight::from_parts(0, 2921).saturating_mul(n.into())) } /// Storage: Nfts Item (r:1 w:0) /// Proof: Nfts Item (max_values: None, max_size: Some(861), added: 3336, mode: MaxEncodedLen) @@ -1533,14 +1533,14 @@ impl WeightInfo for () { // Measured: `716` // Estimated: `14198 + n * (2921 ±0)` // Minimum execution time: 84_153 nanoseconds. - Weight::from_ref_time(96_401_623) - .saturating_add(Weight::from_proof_size(14198)) + Weight::from_parts(96_401_623, 0) + .saturating_add(Weight::from_parts(0, 14198)) // Standard Error: 70_244 - .saturating_add(Weight::from_ref_time(26_866_222).saturating_mul(n.into())) + .saturating_add(Weight::from_parts(26_866_222, 0).saturating_mul(n.into())) .saturating_add(RocksDbWeight::get().reads(4_u64)) .saturating_add(RocksDbWeight::get().reads((1_u64).saturating_mul(n.into()))) .saturating_add(RocksDbWeight::get().writes(2_u64)) .saturating_add(RocksDbWeight::get().writes((1_u64).saturating_mul(n.into()))) - .saturating_add(Weight::from_proof_size(2921).saturating_mul(n.into())) + .saturating_add(Weight::from_parts(0, 2921).saturating_mul(n.into())) } } diff --git a/frame/nis/src/mock.rs b/frame/nis/src/mock.rs index 8df9f89c1..8594c88a5 100644 --- a/frame/nis/src/mock.rs +++ b/frame/nis/src/mock.rs @@ -111,7 +111,7 @@ parameter_types! { pub static Target: Perquintill = Perquintill::zero(); pub const MinReceipt: Perquintill = Perquintill::from_percent(1); pub const ThawThrottle: (Perquintill, u64) = (Perquintill::from_percent(25), 5); - pub static MaxIntakeWeight: Weight = Weight::from_ref_time(2_000_000_000_000); + pub static MaxIntakeWeight: Weight = Weight::from_parts(2_000_000_000_000, 0); pub const ReserveId: [u8; 8] = *b"py/nis "; } diff --git a/frame/nis/src/weights.rs b/frame/nis/src/weights.rs index 36a169f16..27c2f23e0 100644 --- a/frame/nis/src/weights.rs +++ b/frame/nis/src/weights.rs @@ -78,7 +78,7 @@ impl WeightInfo for SubstrateWeight { // Minimum execution time: 28_814 nanoseconds. Weight::from_parts(35_245_917, 60718) // Standard Error: 189 - .saturating_add(Weight::from_ref_time(45_322).saturating_mul(l.into())) + .saturating_add(Weight::from_parts(45_322, 0).saturating_mul(l.into())) .saturating_add(T::DbWeight::get().reads(3_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) } @@ -111,7 +111,7 @@ impl WeightInfo for SubstrateWeight { // Minimum execution time: 34_426 nanoseconds. Weight::from_parts(36_434_166, 60718) // Standard Error: 135 - .saturating_add(Weight::from_ref_time(33_923).saturating_mul(l.into())) + .saturating_add(Weight::from_parts(33_923, 0).saturating_mul(l.into())) .saturating_add(T::DbWeight::get().reads(3_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) } @@ -239,7 +239,7 @@ impl WeightInfo for SubstrateWeight { // Measured: `0` // Estimated: `0` // Minimum execution time: 6_758 nanoseconds. - Weight::from_ref_time(6_911_000) + Weight::from_parts(6_911_000, 0) .saturating_add(T::DbWeight::get().writes(1_u64)) } } @@ -260,7 +260,7 @@ impl WeightInfo for () { // Minimum execution time: 28_814 nanoseconds. Weight::from_parts(35_245_917, 60718) // Standard Error: 189 - .saturating_add(Weight::from_ref_time(45_322).saturating_mul(l.into())) + .saturating_add(Weight::from_parts(45_322, 0).saturating_mul(l.into())) .saturating_add(RocksDbWeight::get().reads(3_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) } @@ -293,7 +293,7 @@ impl WeightInfo for () { // Minimum execution time: 34_426 nanoseconds. Weight::from_parts(36_434_166, 60718) // Standard Error: 135 - .saturating_add(Weight::from_ref_time(33_923).saturating_mul(l.into())) + .saturating_add(Weight::from_parts(33_923, 0).saturating_mul(l.into())) .saturating_add(RocksDbWeight::get().reads(3_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) } @@ -421,7 +421,7 @@ impl WeightInfo for () { // Measured: `0` // Estimated: `0` // Minimum execution time: 6_758 nanoseconds. - Weight::from_ref_time(6_911_000) + Weight::from_parts(6_911_000, 0) .saturating_add(RocksDbWeight::get().writes(1_u64)) } } diff --git a/frame/node-authorization/src/weights.rs b/frame/node-authorization/src/weights.rs index f1ff9580d..a4529c845 100644 --- a/frame/node-authorization/src/weights.rs +++ b/frame/node-authorization/src/weights.rs @@ -37,13 +37,13 @@ pub trait WeightInfo { } impl WeightInfo for () { - fn add_well_known_node() -> Weight { Weight::from_ref_time(50_000_000) } - fn remove_well_known_node() -> Weight { Weight::from_ref_time(50_000_000) } - fn swap_well_known_node() -> Weight { Weight::from_ref_time(50_000_000) } - fn reset_well_known_nodes() -> Weight { Weight::from_ref_time(50_000_000) } - fn claim_node() -> Weight { Weight::from_ref_time(50_000_000) } - fn remove_claim() -> Weight { Weight::from_ref_time(50_000_000) } - fn transfer_node() -> Weight { Weight::from_ref_time(50_000_000) } - fn add_connections() -> Weight { Weight::from_ref_time(50_000_000) } - fn remove_connections() -> Weight { Weight::from_ref_time(50_000_000) } + fn add_well_known_node() -> Weight { Weight::from_parts(50_000_000, 0) } + fn remove_well_known_node() -> Weight { Weight::from_parts(50_000_000, 0) } + fn swap_well_known_node() -> Weight { Weight::from_parts(50_000_000, 0) } + fn reset_well_known_nodes() -> Weight { Weight::from_parts(50_000_000, 0) } + fn claim_node() -> Weight { Weight::from_parts(50_000_000, 0) } + fn remove_claim() -> Weight { Weight::from_parts(50_000_000, 0) } + fn transfer_node() -> Weight { Weight::from_parts(50_000_000, 0) } + fn add_connections() -> Weight { Weight::from_parts(50_000_000, 0) } + fn remove_connections() -> Weight { Weight::from_parts(50_000_000, 0) } } diff --git a/frame/nomination-pools/src/weights.rs b/frame/nomination-pools/src/weights.rs index 1c3d1c633..ca7de7be6 100644 --- a/frame/nomination-pools/src/weights.rs +++ b/frame/nomination-pools/src/weights.rs @@ -101,8 +101,8 @@ impl WeightInfo for SubstrateWeight { // Measured: `3573` // Estimated: `37988` // Minimum execution time: 169_857 nanoseconds. - Weight::from_ref_time(173_895_000) - .saturating_add(Weight::from_proof_size(37988)) + Weight::from_parts(173_895_000, 0) + .saturating_add(Weight::from_parts(0, 37988)) .saturating_add(T::DbWeight::get().reads(17_u64)) .saturating_add(T::DbWeight::get().writes(12_u64)) } @@ -129,8 +129,8 @@ impl WeightInfo for SubstrateWeight { // Measured: `3615` // Estimated: `38583` // Minimum execution time: 167_372 nanoseconds. - Weight::from_ref_time(168_776_000) - .saturating_add(Weight::from_proof_size(38583)) + Weight::from_parts(168_776_000, 0) + .saturating_add(Weight::from_parts(0, 38583)) .saturating_add(T::DbWeight::get().reads(14_u64)) .saturating_add(T::DbWeight::get().writes(12_u64)) } @@ -159,8 +159,8 @@ impl WeightInfo for SubstrateWeight { // Measured: `3680` // Estimated: `41099` // Minimum execution time: 186_346 nanoseconds. - Weight::from_ref_time(191_308_000) - .saturating_add(Weight::from_proof_size(41099)) + Weight::from_parts(191_308_000, 0) + .saturating_add(Weight::from_parts(0, 41099)) .saturating_add(T::DbWeight::get().reads(15_u64)) .saturating_add(T::DbWeight::get().writes(13_u64)) } @@ -179,8 +179,8 @@ impl WeightInfo for SubstrateWeight { // Measured: `1254` // Estimated: `13005` // Minimum execution time: 61_423 nanoseconds. - Weight::from_ref_time(63_219_000) - .saturating_add(Weight::from_proof_size(13005)) + Weight::from_parts(63_219_000, 0) + .saturating_add(Weight::from_parts(0, 13005)) .saturating_add(T::DbWeight::get().reads(5_u64)) .saturating_add(T::DbWeight::get().writes(4_u64)) } @@ -219,8 +219,8 @@ impl WeightInfo for SubstrateWeight { // Measured: `3858` // Estimated: `67379` // Minimum execution time: 174_532 nanoseconds. - Weight::from_ref_time(180_032_000) - .saturating_add(Weight::from_proof_size(67379)) + Weight::from_parts(180_032_000, 0) + .saturating_add(Weight::from_parts(0, 67379)) .saturating_add(T::DbWeight::get().reads(18_u64)) .saturating_add(T::DbWeight::get().writes(14_u64)) } @@ -240,10 +240,10 @@ impl WeightInfo for SubstrateWeight { // Measured: `1779` // Estimated: `13025` // Minimum execution time: 55_327 nanoseconds. - Weight::from_ref_time(58_947_746) - .saturating_add(Weight::from_proof_size(13025)) + Weight::from_parts(58_947_746, 0) + .saturating_add(Weight::from_parts(0, 13025)) // Standard Error: 1_589 - .saturating_add(Weight::from_ref_time(40_696).saturating_mul(s.into())) + .saturating_add(Weight::from_parts(40_696, 0).saturating_mul(s.into())) .saturating_add(T::DbWeight::get().reads(5_u64)) .saturating_add(T::DbWeight::get().writes(2_u64)) } @@ -271,10 +271,10 @@ impl WeightInfo for SubstrateWeight { // Measured: `2303` // Estimated: `45696` // Minimum execution time: 105_923 nanoseconds. - Weight::from_ref_time(110_572_476) - .saturating_add(Weight::from_proof_size(45696)) + Weight::from_parts(110_572_476, 0) + .saturating_add(Weight::from_parts(0, 45696)) // Standard Error: 2_438 - .saturating_add(Weight::from_ref_time(69_045).saturating_mul(s.into())) + .saturating_add(Weight::from_parts(69_045, 0).saturating_mul(s.into())) .saturating_add(T::DbWeight::get().reads(9_u64)) .saturating_add(T::DbWeight::get().writes(7_u64)) } @@ -324,8 +324,8 @@ impl WeightInfo for SubstrateWeight { // Measured: `2690` // Estimated: `68812` // Minimum execution time: 169_700 nanoseconds. - Weight::from_ref_time(178_693_541) - .saturating_add(Weight::from_proof_size(68812)) + Weight::from_parts(178_693_541, 0) + .saturating_add(Weight::from_parts(0, 68812)) .saturating_add(T::DbWeight::get().reads(20_u64)) .saturating_add(T::DbWeight::get().writes(17_u64)) } @@ -376,8 +376,8 @@ impl WeightInfo for SubstrateWeight { // Measured: `1321` // Estimated: `31522` // Minimum execution time: 145_976 nanoseconds. - Weight::from_ref_time(150_664_000) - .saturating_add(Weight::from_proof_size(31522)) + Weight::from_parts(150_664_000, 0) + .saturating_add(Weight::from_parts(0, 31522)) .saturating_add(T::DbWeight::get().reads(21_u64)) .saturating_add(T::DbWeight::get().writes(15_u64)) } @@ -411,14 +411,14 @@ impl WeightInfo for SubstrateWeight { // Measured: `1909` // Estimated: `21998 + n * (2520 ±0)` // Minimum execution time: 69_288 nanoseconds. - Weight::from_ref_time(71_075_293) - .saturating_add(Weight::from_proof_size(21998)) + Weight::from_parts(71_075_293, 0) + .saturating_add(Weight::from_parts(0, 21998)) // Standard Error: 10_508 - .saturating_add(Weight::from_ref_time(1_384_674).saturating_mul(n.into())) + .saturating_add(Weight::from_parts(1_384_674, 0).saturating_mul(n.into())) .saturating_add(T::DbWeight::get().reads(12_u64)) .saturating_add(T::DbWeight::get().reads((1_u64).saturating_mul(n.into()))) .saturating_add(T::DbWeight::get().writes(5_u64)) - .saturating_add(Weight::from_proof_size(2520).saturating_mul(n.into())) + .saturating_add(Weight::from_parts(0, 2520).saturating_mul(n.into())) } /// Storage: NominationPools BondedPools (r:1 w:1) /// Proof: NominationPools BondedPools (max_values: None, max_size: Some(164), added: 2639, mode: MaxEncodedLen) @@ -431,8 +431,8 @@ impl WeightInfo for SubstrateWeight { // Measured: `1498` // Estimated: `8752` // Minimum execution time: 36_410 nanoseconds. - Weight::from_ref_time(37_585_000) - .saturating_add(Weight::from_proof_size(8752)) + Weight::from_parts(37_585_000, 0) + .saturating_add(Weight::from_parts(0, 8752)) .saturating_add(T::DbWeight::get().reads(3_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -448,10 +448,10 @@ impl WeightInfo for SubstrateWeight { // Measured: `559` // Estimated: `5883` // Minimum execution time: 14_322 nanoseconds. - Weight::from_ref_time(15_328_204) - .saturating_add(Weight::from_proof_size(5883)) + Weight::from_parts(15_328_204, 0) + .saturating_add(Weight::from_parts(0, 5883)) // Standard Error: 161 - .saturating_add(Weight::from_ref_time(1_406).saturating_mul(n.into())) + .saturating_add(Weight::from_parts(1_406, 0).saturating_mul(n.into())) .saturating_add(T::DbWeight::get().reads(3_u64)) .saturating_add(T::DbWeight::get().writes(2_u64)) } @@ -470,8 +470,8 @@ impl WeightInfo for SubstrateWeight { // Measured: `0` // Estimated: `0` // Minimum execution time: 5_968 nanoseconds. - Weight::from_ref_time(6_245_000) - .saturating_add(Weight::from_proof_size(0)) + Weight::from_parts(6_245_000, 0) + .saturating_add(Weight::from_parts(0, 0)) .saturating_add(T::DbWeight::get().writes(5_u64)) } /// Storage: NominationPools BondedPools (r:1 w:1) @@ -481,8 +481,8 @@ impl WeightInfo for SubstrateWeight { // Measured: `559` // Estimated: `2639` // Minimum execution time: 18_979 nanoseconds. - Weight::from_ref_time(19_795_000) - .saturating_add(Weight::from_proof_size(2639)) + Weight::from_parts(19_795_000, 0) + .saturating_add(Weight::from_parts(0, 2639)) .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -509,8 +509,8 @@ impl WeightInfo for SubstrateWeight { // Measured: `2136` // Estimated: `20489` // Minimum execution time: 68_145 nanoseconds. - Weight::from_ref_time(70_444_000) - .saturating_add(Weight::from_proof_size(20489)) + Weight::from_parts(70_444_000, 0) + .saturating_add(Weight::from_parts(0, 20489)) .saturating_add(T::DbWeight::get().reads(9_u64)) .saturating_add(T::DbWeight::get().writes(5_u64)) } @@ -523,8 +523,8 @@ impl WeightInfo for SubstrateWeight { // Measured: `542` // Estimated: `5228` // Minimum execution time: 15_112 nanoseconds. - Weight::from_ref_time(15_897_000) - .saturating_add(Weight::from_proof_size(5228)) + Weight::from_parts(15_897_000, 0) + .saturating_add(Weight::from_parts(0, 5228)) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -563,8 +563,8 @@ impl WeightInfo for () { // Measured: `3573` // Estimated: `37988` // Minimum execution time: 169_857 nanoseconds. - Weight::from_ref_time(173_895_000) - .saturating_add(Weight::from_proof_size(37988)) + Weight::from_parts(173_895_000, 0) + .saturating_add(Weight::from_parts(0, 37988)) .saturating_add(RocksDbWeight::get().reads(17_u64)) .saturating_add(RocksDbWeight::get().writes(12_u64)) } @@ -591,8 +591,8 @@ impl WeightInfo for () { // Measured: `3615` // Estimated: `38583` // Minimum execution time: 167_372 nanoseconds. - Weight::from_ref_time(168_776_000) - .saturating_add(Weight::from_proof_size(38583)) + Weight::from_parts(168_776_000, 0) + .saturating_add(Weight::from_parts(0, 38583)) .saturating_add(RocksDbWeight::get().reads(14_u64)) .saturating_add(RocksDbWeight::get().writes(12_u64)) } @@ -621,8 +621,8 @@ impl WeightInfo for () { // Measured: `3680` // Estimated: `41099` // Minimum execution time: 186_346 nanoseconds. - Weight::from_ref_time(191_308_000) - .saturating_add(Weight::from_proof_size(41099)) + Weight::from_parts(191_308_000, 0) + .saturating_add(Weight::from_parts(0, 41099)) .saturating_add(RocksDbWeight::get().reads(15_u64)) .saturating_add(RocksDbWeight::get().writes(13_u64)) } @@ -641,8 +641,8 @@ impl WeightInfo for () { // Measured: `1254` // Estimated: `13005` // Minimum execution time: 61_423 nanoseconds. - Weight::from_ref_time(63_219_000) - .saturating_add(Weight::from_proof_size(13005)) + Weight::from_parts(63_219_000, 0) + .saturating_add(Weight::from_parts(0, 13005)) .saturating_add(RocksDbWeight::get().reads(5_u64)) .saturating_add(RocksDbWeight::get().writes(4_u64)) } @@ -681,8 +681,8 @@ impl WeightInfo for () { // Measured: `3858` // Estimated: `67379` // Minimum execution time: 174_532 nanoseconds. - Weight::from_ref_time(180_032_000) - .saturating_add(Weight::from_proof_size(67379)) + Weight::from_parts(180_032_000, 0) + .saturating_add(Weight::from_parts(0, 67379)) .saturating_add(RocksDbWeight::get().reads(18_u64)) .saturating_add(RocksDbWeight::get().writes(14_u64)) } @@ -702,10 +702,10 @@ impl WeightInfo for () { // Measured: `1779` // Estimated: `13025` // Minimum execution time: 55_327 nanoseconds. - Weight::from_ref_time(58_947_746) - .saturating_add(Weight::from_proof_size(13025)) + Weight::from_parts(58_947_746, 0) + .saturating_add(Weight::from_parts(0, 13025)) // Standard Error: 1_589 - .saturating_add(Weight::from_ref_time(40_696).saturating_mul(s.into())) + .saturating_add(Weight::from_parts(40_696, 0).saturating_mul(s.into())) .saturating_add(RocksDbWeight::get().reads(5_u64)) .saturating_add(RocksDbWeight::get().writes(2_u64)) } @@ -733,10 +733,10 @@ impl WeightInfo for () { // Measured: `2303` // Estimated: `45696` // Minimum execution time: 105_923 nanoseconds. - Weight::from_ref_time(110_572_476) - .saturating_add(Weight::from_proof_size(45696)) + Weight::from_parts(110_572_476, 0) + .saturating_add(Weight::from_parts(0, 45696)) // Standard Error: 2_438 - .saturating_add(Weight::from_ref_time(69_045).saturating_mul(s.into())) + .saturating_add(Weight::from_parts(69_045, 0).saturating_mul(s.into())) .saturating_add(RocksDbWeight::get().reads(9_u64)) .saturating_add(RocksDbWeight::get().writes(7_u64)) } @@ -786,8 +786,8 @@ impl WeightInfo for () { // Measured: `2690` // Estimated: `68812` // Minimum execution time: 169_700 nanoseconds. - Weight::from_ref_time(178_693_541) - .saturating_add(Weight::from_proof_size(68812)) + Weight::from_parts(178_693_541, 0) + .saturating_add(Weight::from_parts(0, 68812)) .saturating_add(RocksDbWeight::get().reads(20_u64)) .saturating_add(RocksDbWeight::get().writes(17_u64)) } @@ -838,8 +838,8 @@ impl WeightInfo for () { // Measured: `1321` // Estimated: `31522` // Minimum execution time: 145_976 nanoseconds. - Weight::from_ref_time(150_664_000) - .saturating_add(Weight::from_proof_size(31522)) + Weight::from_parts(150_664_000, 0) + .saturating_add(Weight::from_parts(0, 31522)) .saturating_add(RocksDbWeight::get().reads(21_u64)) .saturating_add(RocksDbWeight::get().writes(15_u64)) } @@ -873,14 +873,14 @@ impl WeightInfo for () { // Measured: `1909` // Estimated: `21998 + n * (2520 ±0)` // Minimum execution time: 69_288 nanoseconds. - Weight::from_ref_time(71_075_293) - .saturating_add(Weight::from_proof_size(21998)) + Weight::from_parts(71_075_293, 0) + .saturating_add(Weight::from_parts(0, 21998)) // Standard Error: 10_508 - .saturating_add(Weight::from_ref_time(1_384_674).saturating_mul(n.into())) + .saturating_add(Weight::from_parts(1_384_674, 0).saturating_mul(n.into())) .saturating_add(RocksDbWeight::get().reads(12_u64)) .saturating_add(RocksDbWeight::get().reads((1_u64).saturating_mul(n.into()))) .saturating_add(RocksDbWeight::get().writes(5_u64)) - .saturating_add(Weight::from_proof_size(2520).saturating_mul(n.into())) + .saturating_add(Weight::from_parts(0, 2520).saturating_mul(n.into())) } /// Storage: NominationPools BondedPools (r:1 w:1) /// Proof: NominationPools BondedPools (max_values: None, max_size: Some(164), added: 2639, mode: MaxEncodedLen) @@ -893,8 +893,8 @@ impl WeightInfo for () { // Measured: `1498` // Estimated: `8752` // Minimum execution time: 36_410 nanoseconds. - Weight::from_ref_time(37_585_000) - .saturating_add(Weight::from_proof_size(8752)) + Weight::from_parts(37_585_000, 0) + .saturating_add(Weight::from_parts(0, 8752)) .saturating_add(RocksDbWeight::get().reads(3_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } @@ -910,10 +910,10 @@ impl WeightInfo for () { // Measured: `559` // Estimated: `5883` // Minimum execution time: 14_322 nanoseconds. - Weight::from_ref_time(15_328_204) - .saturating_add(Weight::from_proof_size(5883)) + Weight::from_parts(15_328_204, 0) + .saturating_add(Weight::from_parts(0, 5883)) // Standard Error: 161 - .saturating_add(Weight::from_ref_time(1_406).saturating_mul(n.into())) + .saturating_add(Weight::from_parts(1_406, 0).saturating_mul(n.into())) .saturating_add(RocksDbWeight::get().reads(3_u64)) .saturating_add(RocksDbWeight::get().writes(2_u64)) } @@ -932,8 +932,8 @@ impl WeightInfo for () { // Measured: `0` // Estimated: `0` // Minimum execution time: 5_968 nanoseconds. - Weight::from_ref_time(6_245_000) - .saturating_add(Weight::from_proof_size(0)) + Weight::from_parts(6_245_000, 0) + .saturating_add(Weight::from_parts(0, 0)) .saturating_add(RocksDbWeight::get().writes(5_u64)) } /// Storage: NominationPools BondedPools (r:1 w:1) @@ -943,8 +943,8 @@ impl WeightInfo for () { // Measured: `559` // Estimated: `2639` // Minimum execution time: 18_979 nanoseconds. - Weight::from_ref_time(19_795_000) - .saturating_add(Weight::from_proof_size(2639)) + Weight::from_parts(19_795_000, 0) + .saturating_add(Weight::from_parts(0, 2639)) .saturating_add(RocksDbWeight::get().reads(1_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } @@ -971,8 +971,8 @@ impl WeightInfo for () { // Measured: `2136` // Estimated: `20489` // Minimum execution time: 68_145 nanoseconds. - Weight::from_ref_time(70_444_000) - .saturating_add(Weight::from_proof_size(20489)) + Weight::from_parts(70_444_000, 0) + .saturating_add(Weight::from_parts(0, 20489)) .saturating_add(RocksDbWeight::get().reads(9_u64)) .saturating_add(RocksDbWeight::get().writes(5_u64)) } @@ -985,8 +985,8 @@ impl WeightInfo for () { // Measured: `542` // Estimated: `5228` // Minimum execution time: 15_112 nanoseconds. - Weight::from_ref_time(15_897_000) - .saturating_add(Weight::from_proof_size(5228)) + Weight::from_parts(15_897_000, 0) + .saturating_add(Weight::from_parts(0, 5228)) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } diff --git a/frame/preimage/src/weights.rs b/frame/preimage/src/weights.rs index 7d181be01..fa6bdc972 100644 --- a/frame/preimage/src/weights.rs +++ b/frame/preimage/src/weights.rs @@ -77,7 +77,7 @@ impl WeightInfo for SubstrateWeight { // Minimum execution time: 23_484 nanoseconds. Weight::from_parts(23_828_000, 2566) // Standard Error: 0 - .saturating_add(Weight::from_ref_time(1_705).saturating_mul(s.into())) + .saturating_add(Weight::from_parts(1_705, 0).saturating_mul(s.into())) .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(T::DbWeight::get().writes(2_u64)) } @@ -93,7 +93,7 @@ impl WeightInfo for SubstrateWeight { // Minimum execution time: 14_812 nanoseconds. Weight::from_parts(14_949_000, 2566) // Standard Error: 0 - .saturating_add(Weight::from_ref_time(1_707).saturating_mul(s.into())) + .saturating_add(Weight::from_parts(1_707, 0).saturating_mul(s.into())) .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(T::DbWeight::get().writes(2_u64)) } @@ -109,7 +109,7 @@ impl WeightInfo for SubstrateWeight { // Minimum execution time: 14_185 nanoseconds. Weight::from_parts(14_398_000, 2566) // Standard Error: 0 - .saturating_add(Weight::from_ref_time(1_709).saturating_mul(s.into())) + .saturating_add(Weight::from_parts(1_709, 0).saturating_mul(s.into())) .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(T::DbWeight::get().writes(2_u64)) } @@ -234,7 +234,7 @@ impl WeightInfo for () { // Minimum execution time: 23_484 nanoseconds. Weight::from_parts(23_828_000, 2566) // Standard Error: 0 - .saturating_add(Weight::from_ref_time(1_705).saturating_mul(s.into())) + .saturating_add(Weight::from_parts(1_705, 0).saturating_mul(s.into())) .saturating_add(RocksDbWeight::get().reads(1_u64)) .saturating_add(RocksDbWeight::get().writes(2_u64)) } @@ -250,7 +250,7 @@ impl WeightInfo for () { // Minimum execution time: 14_812 nanoseconds. Weight::from_parts(14_949_000, 2566) // Standard Error: 0 - .saturating_add(Weight::from_ref_time(1_707).saturating_mul(s.into())) + .saturating_add(Weight::from_parts(1_707, 0).saturating_mul(s.into())) .saturating_add(RocksDbWeight::get().reads(1_u64)) .saturating_add(RocksDbWeight::get().writes(2_u64)) } @@ -266,7 +266,7 @@ impl WeightInfo for () { // Minimum execution time: 14_185 nanoseconds. Weight::from_parts(14_398_000, 2566) // Standard Error: 0 - .saturating_add(Weight::from_ref_time(1_709).saturating_mul(s.into())) + .saturating_add(Weight::from_parts(1_709, 0).saturating_mul(s.into())) .saturating_add(RocksDbWeight::get().reads(1_u64)) .saturating_add(RocksDbWeight::get().writes(2_u64)) } diff --git a/frame/proxy/src/weights.rs b/frame/proxy/src/weights.rs index 5e85e148a..5b70b61e5 100644 --- a/frame/proxy/src/weights.rs +++ b/frame/proxy/src/weights.rs @@ -73,7 +73,7 @@ impl WeightInfo for SubstrateWeight { // Minimum execution time: 14_461 nanoseconds. Weight::from_parts(14_913_927, 3716) // Standard Error: 1_174 - .saturating_add(Weight::from_ref_time(36_087).saturating_mul(p.into())) + .saturating_add(Weight::from_parts(36_087, 0).saturating_mul(p.into())) .saturating_add(T::DbWeight::get().reads(1_u64)) } /// Storage: Proxy Proxies (r:1 w:0) @@ -91,9 +91,9 @@ impl WeightInfo for SubstrateWeight { // Minimum execution time: 31_523 nanoseconds. Weight::from_parts(31_116_270, 11027) // Standard Error: 1_789 - .saturating_add(Weight::from_ref_time(135_656).saturating_mul(a.into())) + .saturating_add(Weight::from_parts(135_656, 0).saturating_mul(a.into())) // Standard Error: 1_849 - .saturating_add(Weight::from_ref_time(53_893).saturating_mul(p.into())) + .saturating_add(Weight::from_parts(53_893, 0).saturating_mul(p.into())) .saturating_add(T::DbWeight::get().reads(3_u64)) .saturating_add(T::DbWeight::get().writes(2_u64)) } @@ -110,9 +110,9 @@ impl WeightInfo for SubstrateWeight { // Minimum execution time: 19_363 nanoseconds. Weight::from_parts(20_282_191, 7311) // Standard Error: 1_084 - .saturating_add(Weight::from_ref_time(133_825).saturating_mul(a.into())) + .saturating_add(Weight::from_parts(133_825, 0).saturating_mul(a.into())) // Standard Error: 1_120 - .saturating_add(Weight::from_ref_time(3_434).saturating_mul(p.into())) + .saturating_add(Weight::from_parts(3_434, 0).saturating_mul(p.into())) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().writes(2_u64)) } @@ -129,9 +129,9 @@ impl WeightInfo for SubstrateWeight { // Minimum execution time: 19_363 nanoseconds. Weight::from_parts(20_211_584, 7311) // Standard Error: 1_171 - .saturating_add(Weight::from_ref_time(136_984).saturating_mul(a.into())) + .saturating_add(Weight::from_parts(136_984, 0).saturating_mul(a.into())) // Standard Error: 1_210 - .saturating_add(Weight::from_ref_time(3_686).saturating_mul(p.into())) + .saturating_add(Weight::from_parts(3_686, 0).saturating_mul(p.into())) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().writes(2_u64)) } @@ -150,9 +150,9 @@ impl WeightInfo for SubstrateWeight { // Minimum execution time: 27_811 nanoseconds. Weight::from_parts(27_965_813, 11027) // Standard Error: 1_987 - .saturating_add(Weight::from_ref_time(124_133).saturating_mul(a.into())) + .saturating_add(Weight::from_parts(124_133, 0).saturating_mul(a.into())) // Standard Error: 2_053 - .saturating_add(Weight::from_ref_time(54_692).saturating_mul(p.into())) + .saturating_add(Weight::from_parts(54_692, 0).saturating_mul(p.into())) .saturating_add(T::DbWeight::get().reads(3_u64)) .saturating_add(T::DbWeight::get().writes(2_u64)) } @@ -166,7 +166,7 @@ impl WeightInfo for SubstrateWeight { // Minimum execution time: 20_922 nanoseconds. Weight::from_parts(21_551_797, 3716) // Standard Error: 1_425 - .saturating_add(Weight::from_ref_time(58_434).saturating_mul(p.into())) + .saturating_add(Weight::from_parts(58_434, 0).saturating_mul(p.into())) .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -180,7 +180,7 @@ impl WeightInfo for SubstrateWeight { // Minimum execution time: 20_812 nanoseconds. Weight::from_parts(21_660_732, 3716) // Standard Error: 1_438 - .saturating_add(Weight::from_ref_time(68_740).saturating_mul(p.into())) + .saturating_add(Weight::from_parts(68_740, 0).saturating_mul(p.into())) .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -194,7 +194,7 @@ impl WeightInfo for SubstrateWeight { // Minimum execution time: 16_786 nanoseconds. Weight::from_parts(17_249_958, 3716) // Standard Error: 1_007 - .saturating_add(Weight::from_ref_time(37_546).saturating_mul(p.into())) + .saturating_add(Weight::from_parts(37_546, 0).saturating_mul(p.into())) .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -208,7 +208,7 @@ impl WeightInfo for SubstrateWeight { // Minimum execution time: 22_764 nanoseconds. Weight::from_parts(23_539_039, 3716) // Standard Error: 814 - .saturating_add(Weight::from_ref_time(144).saturating_mul(p.into())) + .saturating_add(Weight::from_parts(144, 0).saturating_mul(p.into())) .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -222,7 +222,7 @@ impl WeightInfo for SubstrateWeight { // Minimum execution time: 17_720 nanoseconds. Weight::from_parts(18_428_849, 3716) // Standard Error: 1_093 - .saturating_add(Weight::from_ref_time(34_600).saturating_mul(p.into())) + .saturating_add(Weight::from_parts(34_600, 0).saturating_mul(p.into())) .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -240,7 +240,7 @@ impl WeightInfo for () { // Minimum execution time: 14_461 nanoseconds. Weight::from_parts(14_913_927, 3716) // Standard Error: 1_174 - .saturating_add(Weight::from_ref_time(36_087).saturating_mul(p.into())) + .saturating_add(Weight::from_parts(36_087, 0).saturating_mul(p.into())) .saturating_add(RocksDbWeight::get().reads(1_u64)) } /// Storage: Proxy Proxies (r:1 w:0) @@ -258,9 +258,9 @@ impl WeightInfo for () { // Minimum execution time: 31_523 nanoseconds. Weight::from_parts(31_116_270, 11027) // Standard Error: 1_789 - .saturating_add(Weight::from_ref_time(135_656).saturating_mul(a.into())) + .saturating_add(Weight::from_parts(135_656, 0).saturating_mul(a.into())) // Standard Error: 1_849 - .saturating_add(Weight::from_ref_time(53_893).saturating_mul(p.into())) + .saturating_add(Weight::from_parts(53_893, 0).saturating_mul(p.into())) .saturating_add(RocksDbWeight::get().reads(3_u64)) .saturating_add(RocksDbWeight::get().writes(2_u64)) } @@ -277,9 +277,9 @@ impl WeightInfo for () { // Minimum execution time: 19_363 nanoseconds. Weight::from_parts(20_282_191, 7311) // Standard Error: 1_084 - .saturating_add(Weight::from_ref_time(133_825).saturating_mul(a.into())) + .saturating_add(Weight::from_parts(133_825, 0).saturating_mul(a.into())) // Standard Error: 1_120 - .saturating_add(Weight::from_ref_time(3_434).saturating_mul(p.into())) + .saturating_add(Weight::from_parts(3_434, 0).saturating_mul(p.into())) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().writes(2_u64)) } @@ -296,9 +296,9 @@ impl WeightInfo for () { // Minimum execution time: 19_363 nanoseconds. Weight::from_parts(20_211_584, 7311) // Standard Error: 1_171 - .saturating_add(Weight::from_ref_time(136_984).saturating_mul(a.into())) + .saturating_add(Weight::from_parts(136_984, 0).saturating_mul(a.into())) // Standard Error: 1_210 - .saturating_add(Weight::from_ref_time(3_686).saturating_mul(p.into())) + .saturating_add(Weight::from_parts(3_686, 0).saturating_mul(p.into())) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().writes(2_u64)) } @@ -317,9 +317,9 @@ impl WeightInfo for () { // Minimum execution time: 27_811 nanoseconds. Weight::from_parts(27_965_813, 11027) // Standard Error: 1_987 - .saturating_add(Weight::from_ref_time(124_133).saturating_mul(a.into())) + .saturating_add(Weight::from_parts(124_133, 0).saturating_mul(a.into())) // Standard Error: 2_053 - .saturating_add(Weight::from_ref_time(54_692).saturating_mul(p.into())) + .saturating_add(Weight::from_parts(54_692, 0).saturating_mul(p.into())) .saturating_add(RocksDbWeight::get().reads(3_u64)) .saturating_add(RocksDbWeight::get().writes(2_u64)) } @@ -333,7 +333,7 @@ impl WeightInfo for () { // Minimum execution time: 20_922 nanoseconds. Weight::from_parts(21_551_797, 3716) // Standard Error: 1_425 - .saturating_add(Weight::from_ref_time(58_434).saturating_mul(p.into())) + .saturating_add(Weight::from_parts(58_434, 0).saturating_mul(p.into())) .saturating_add(RocksDbWeight::get().reads(1_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } @@ -347,7 +347,7 @@ impl WeightInfo for () { // Minimum execution time: 20_812 nanoseconds. Weight::from_parts(21_660_732, 3716) // Standard Error: 1_438 - .saturating_add(Weight::from_ref_time(68_740).saturating_mul(p.into())) + .saturating_add(Weight::from_parts(68_740, 0).saturating_mul(p.into())) .saturating_add(RocksDbWeight::get().reads(1_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } @@ -361,7 +361,7 @@ impl WeightInfo for () { // Minimum execution time: 16_786 nanoseconds. Weight::from_parts(17_249_958, 3716) // Standard Error: 1_007 - .saturating_add(Weight::from_ref_time(37_546).saturating_mul(p.into())) + .saturating_add(Weight::from_parts(37_546, 0).saturating_mul(p.into())) .saturating_add(RocksDbWeight::get().reads(1_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } @@ -375,7 +375,7 @@ impl WeightInfo for () { // Minimum execution time: 22_764 nanoseconds. Weight::from_parts(23_539_039, 3716) // Standard Error: 814 - .saturating_add(Weight::from_ref_time(144).saturating_mul(p.into())) + .saturating_add(Weight::from_parts(144, 0).saturating_mul(p.into())) .saturating_add(RocksDbWeight::get().reads(1_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } @@ -389,7 +389,7 @@ impl WeightInfo for () { // Minimum execution time: 17_720 nanoseconds. Weight::from_parts(18_428_849, 3716) // Standard Error: 1_093 - .saturating_add(Weight::from_ref_time(34_600).saturating_mul(p.into())) + .saturating_add(Weight::from_parts(34_600, 0).saturating_mul(p.into())) .saturating_add(RocksDbWeight::get().reads(1_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } diff --git a/frame/ranked-collective/src/weights.rs b/frame/ranked-collective/src/weights.rs index 1f50441aa..8d6c1620d 100644 --- a/frame/ranked-collective/src/weights.rs +++ b/frame/ranked-collective/src/weights.rs @@ -92,12 +92,12 @@ impl WeightInfo for SubstrateWeight { // Minimum execution time: 26_177 nanoseconds. Weight::from_parts(29_245_248, 10064) // Standard Error: 18_611 - .saturating_add(Weight::from_ref_time(10_916_516).saturating_mul(r.into())) + .saturating_add(Weight::from_parts(10_916_516, 0).saturating_mul(r.into())) .saturating_add(T::DbWeight::get().reads(4_u64)) .saturating_add(T::DbWeight::get().reads((3_u64).saturating_mul(r.into()))) .saturating_add(T::DbWeight::get().writes(4_u64)) .saturating_add(T::DbWeight::get().writes((3_u64).saturating_mul(r.into()))) - .saturating_add(Weight::from_proof_size(7547).saturating_mul(r.into())) + .saturating_add(Weight::from_parts(0, 7547).saturating_mul(r.into())) } /// Storage: RankedCollective Members (r:1 w:1) /// Proof: RankedCollective Members (max_values: None, max_size: Some(42), added: 2517, mode: MaxEncodedLen) @@ -115,7 +115,7 @@ impl WeightInfo for SubstrateWeight { // Minimum execution time: 18_953 nanoseconds. Weight::from_parts(19_570_567, 5006) // Standard Error: 4_156 - .saturating_add(Weight::from_ref_time(263_843).saturating_mul(r.into())) + .saturating_add(Weight::from_parts(263_843, 0).saturating_mul(r.into())) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().writes(4_u64)) } @@ -135,7 +135,7 @@ impl WeightInfo for SubstrateWeight { // Minimum execution time: 26_243 nanoseconds. Weight::from_parts(28_532_816, 10064) // Standard Error: 22_689 - .saturating_add(Weight::from_ref_time(614_464).saturating_mul(r.into())) + .saturating_add(Weight::from_parts(614_464, 0).saturating_mul(r.into())) .saturating_add(T::DbWeight::get().reads(4_u64)) .saturating_add(T::DbWeight::get().writes(4_u64)) } @@ -170,7 +170,7 @@ impl WeightInfo for SubstrateWeight { // Minimum execution time: 13_245 nanoseconds. Weight::from_parts(17_420_271, 5394) // Standard Error: 1_503 - .saturating_add(Weight::from_ref_time(952_500).saturating_mul(n.into())) + .saturating_add(Weight::from_parts(952_500, 0).saturating_mul(n.into())) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().writes((1_u64).saturating_mul(n.into()))) } @@ -211,12 +211,12 @@ impl WeightInfo for () { // Minimum execution time: 26_177 nanoseconds. Weight::from_parts(29_245_248, 10064) // Standard Error: 18_611 - .saturating_add(Weight::from_ref_time(10_916_516).saturating_mul(r.into())) + .saturating_add(Weight::from_parts(10_916_516, 0).saturating_mul(r.into())) .saturating_add(RocksDbWeight::get().reads(4_u64)) .saturating_add(RocksDbWeight::get().reads((3_u64).saturating_mul(r.into()))) .saturating_add(RocksDbWeight::get().writes(4_u64)) .saturating_add(RocksDbWeight::get().writes((3_u64).saturating_mul(r.into()))) - .saturating_add(Weight::from_proof_size(7547).saturating_mul(r.into())) + .saturating_add(Weight::from_parts(0, 7547).saturating_mul(r.into())) } /// Storage: RankedCollective Members (r:1 w:1) /// Proof: RankedCollective Members (max_values: None, max_size: Some(42), added: 2517, mode: MaxEncodedLen) @@ -234,7 +234,7 @@ impl WeightInfo for () { // Minimum execution time: 18_953 nanoseconds. Weight::from_parts(19_570_567, 5006) // Standard Error: 4_156 - .saturating_add(Weight::from_ref_time(263_843).saturating_mul(r.into())) + .saturating_add(Weight::from_parts(263_843, 0).saturating_mul(r.into())) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().writes(4_u64)) } @@ -254,7 +254,7 @@ impl WeightInfo for () { // Minimum execution time: 26_243 nanoseconds. Weight::from_parts(28_532_816, 10064) // Standard Error: 22_689 - .saturating_add(Weight::from_ref_time(614_464).saturating_mul(r.into())) + .saturating_add(Weight::from_parts(614_464, 0).saturating_mul(r.into())) .saturating_add(RocksDbWeight::get().reads(4_u64)) .saturating_add(RocksDbWeight::get().writes(4_u64)) } @@ -289,7 +289,7 @@ impl WeightInfo for () { // Minimum execution time: 13_245 nanoseconds. Weight::from_parts(17_420_271, 5394) // Standard Error: 1_503 - .saturating_add(Weight::from_ref_time(952_500).saturating_mul(n.into())) + .saturating_add(Weight::from_parts(952_500, 0).saturating_mul(n.into())) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().writes((1_u64).saturating_mul(n.into()))) } diff --git a/frame/recovery/src/weights.rs b/frame/recovery/src/weights.rs index 7a67c716f..6f8818f81 100644 --- a/frame/recovery/src/weights.rs +++ b/frame/recovery/src/weights.rs @@ -79,7 +79,7 @@ impl WeightInfo for SubstrateWeight { // Measured: `0` // Estimated: `0` // Minimum execution time: 8_893 nanoseconds. - Weight::from_ref_time(9_177_000) + Weight::from_parts(9_177_000, 0) .saturating_add(T::DbWeight::get().writes(1_u64)) } /// Storage: Recovery Recoverable (r:1 w:1) @@ -92,7 +92,7 @@ impl WeightInfo for SubstrateWeight { // Minimum execution time: 20_662 nanoseconds. Weight::from_parts(21_378_064, 2826) // Standard Error: 3_350 - .saturating_add(Weight::from_ref_time(83_738).saturating_mul(n.into())) + .saturating_add(Weight::from_parts(83_738, 0).saturating_mul(n.into())) .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -121,7 +121,7 @@ impl WeightInfo for SubstrateWeight { // Minimum execution time: 17_837 nanoseconds. Weight::from_parts(18_429_664, 5690) // Standard Error: 3_187 - .saturating_add(Weight::from_ref_time(143_648).saturating_mul(n.into())) + .saturating_add(Weight::from_parts(143_648, 0).saturating_mul(n.into())) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -139,7 +139,7 @@ impl WeightInfo for SubstrateWeight { // Minimum execution time: 21_960 nanoseconds. Weight::from_parts(22_529_644, 8245) // Standard Error: 2_945 - .saturating_add(Weight::from_ref_time(85_604).saturating_mul(n.into())) + .saturating_add(Weight::from_parts(85_604, 0).saturating_mul(n.into())) .saturating_add(T::DbWeight::get().reads(3_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -155,7 +155,7 @@ impl WeightInfo for SubstrateWeight { // Minimum execution time: 26_054 nanoseconds. Weight::from_parts(26_724_866, 5467) // Standard Error: 2_645 - .saturating_add(Weight::from_ref_time(104_301).saturating_mul(n.into())) + .saturating_add(Weight::from_parts(104_301, 0).saturating_mul(n.into())) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().writes(2_u64)) } @@ -171,7 +171,7 @@ impl WeightInfo for SubstrateWeight { // Minimum execution time: 25_110 nanoseconds. Weight::from_parts(25_805_837, 5690) // Standard Error: 2_732 - .saturating_add(Weight::from_ref_time(73_458).saturating_mul(n.into())) + .saturating_add(Weight::from_parts(73_458, 0).saturating_mul(n.into())) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -207,7 +207,7 @@ impl WeightInfo for () { // Measured: `0` // Estimated: `0` // Minimum execution time: 8_893 nanoseconds. - Weight::from_ref_time(9_177_000) + Weight::from_parts(9_177_000, 0) .saturating_add(RocksDbWeight::get().writes(1_u64)) } /// Storage: Recovery Recoverable (r:1 w:1) @@ -220,7 +220,7 @@ impl WeightInfo for () { // Minimum execution time: 20_662 nanoseconds. Weight::from_parts(21_378_064, 2826) // Standard Error: 3_350 - .saturating_add(Weight::from_ref_time(83_738).saturating_mul(n.into())) + .saturating_add(Weight::from_parts(83_738, 0).saturating_mul(n.into())) .saturating_add(RocksDbWeight::get().reads(1_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } @@ -249,7 +249,7 @@ impl WeightInfo for () { // Minimum execution time: 17_837 nanoseconds. Weight::from_parts(18_429_664, 5690) // Standard Error: 3_187 - .saturating_add(Weight::from_ref_time(143_648).saturating_mul(n.into())) + .saturating_add(Weight::from_parts(143_648, 0).saturating_mul(n.into())) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } @@ -267,7 +267,7 @@ impl WeightInfo for () { // Minimum execution time: 21_960 nanoseconds. Weight::from_parts(22_529_644, 8245) // Standard Error: 2_945 - .saturating_add(Weight::from_ref_time(85_604).saturating_mul(n.into())) + .saturating_add(Weight::from_parts(85_604, 0).saturating_mul(n.into())) .saturating_add(RocksDbWeight::get().reads(3_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } @@ -283,7 +283,7 @@ impl WeightInfo for () { // Minimum execution time: 26_054 nanoseconds. Weight::from_parts(26_724_866, 5467) // Standard Error: 2_645 - .saturating_add(Weight::from_ref_time(104_301).saturating_mul(n.into())) + .saturating_add(Weight::from_parts(104_301, 0).saturating_mul(n.into())) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().writes(2_u64)) } @@ -299,7 +299,7 @@ impl WeightInfo for () { // Minimum execution time: 25_110 nanoseconds. Weight::from_parts(25_805_837, 5690) // Standard Error: 2_732 - .saturating_add(Weight::from_ref_time(73_458).saturating_mul(n.into())) + .saturating_add(Weight::from_parts(73_458, 0).saturating_mul(n.into())) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } diff --git a/frame/remark/src/weights.rs b/frame/remark/src/weights.rs index 0e18e10c6..f9fcb7380 100644 --- a/frame/remark/src/weights.rs +++ b/frame/remark/src/weights.rs @@ -60,9 +60,9 @@ impl WeightInfo for SubstrateWeight { // Measured: `0` // Estimated: `0` // Minimum execution time: 8_404 nanoseconds. - Weight::from_ref_time(343_031) + Weight::from_parts(343_031, 0) // Standard Error: 1 - .saturating_add(Weight::from_ref_time(1_404).saturating_mul(l.into())) + .saturating_add(Weight::from_parts(1_404, 0).saturating_mul(l.into())) } } @@ -74,8 +74,8 @@ impl WeightInfo for () { // Measured: `0` // Estimated: `0` // Minimum execution time: 8_404 nanoseconds. - Weight::from_ref_time(343_031) + Weight::from_parts(343_031, 0) // Standard Error: 1 - .saturating_add(Weight::from_ref_time(1_404).saturating_mul(l.into())) + .saturating_add(Weight::from_parts(1_404, 0).saturating_mul(l.into())) } } diff --git a/frame/scheduler/src/mock.rs b/frame/scheduler/src/mock.rs index 5a2328971..914a97ab9 100644 --- a/frame/scheduler/src/mock.rs +++ b/frame/scheduler/src/mock.rs @@ -121,7 +121,7 @@ impl Contains for BaseFilter { parameter_types! { pub BlockWeights: frame_system::limits::BlockWeights = frame_system::limits::BlockWeights::simple_max( - Weight::from_ref_time(2_000_000_000_000).set_proof_size(u64::MAX), + Weight::from_parts(2_000_000_000_000, u64::MAX), ); } impl system::Config for Test { @@ -169,40 +169,40 @@ impl pallet_preimage::Config for Test { pub struct TestWeightInfo; impl WeightInfo for TestWeightInfo { fn service_agendas_base() -> Weight { - Weight::from_ref_time(0b0000_0001) + Weight::from_parts(0b0000_0001, 0) } fn service_agenda_base(i: u32) -> Weight { - Weight::from_ref_time((i << 8) as u64 + 0b0000_0010) + Weight::from_parts((i << 8) as u64 + 0b0000_0010, 0) } fn service_task_base() -> Weight { - Weight::from_ref_time(0b0000_0100) + Weight::from_parts(0b0000_0100, 0) } fn service_task_periodic() -> Weight { - Weight::from_ref_time(0b0000_1100) + Weight::from_parts(0b0000_1100, 0) } fn service_task_named() -> Weight { - Weight::from_ref_time(0b0001_0100) + Weight::from_parts(0b0001_0100, 0) } fn service_task_fetched(s: u32) -> Weight { - Weight::from_ref_time((s << 8) as u64 + 0b0010_0100) + Weight::from_parts((s << 8) as u64 + 0b0010_0100, 0) } fn execute_dispatch_signed() -> Weight { - Weight::from_ref_time(0b0100_0000) + Weight::from_parts(0b0100_0000, 0) } fn execute_dispatch_unsigned() -> Weight { - Weight::from_ref_time(0b1000_0000) + Weight::from_parts(0b1000_0000, 0) } fn schedule(_s: u32) -> Weight { - Weight::from_ref_time(50) + Weight::from_parts(50, 0) } fn cancel(_s: u32) -> Weight { - Weight::from_ref_time(50) + Weight::from_parts(50, 0) } fn schedule_named(_s: u32) -> Weight { - Weight::from_ref_time(50) + Weight::from_parts(50, 0) } fn cancel_named(_s: u32) -> Weight { - Weight::from_ref_time(50) + Weight::from_parts(50, 0) } } parameter_types! { diff --git a/frame/scheduler/src/tests.rs b/frame/scheduler/src/tests.rs index 258e6a281..a0cac897d 100644 --- a/frame/scheduler/src/tests.rs +++ b/frame/scheduler/src/tests.rs @@ -33,7 +33,7 @@ use substrate_test_utils::assert_eq_uvec; fn basic_scheduling_works() { new_test_ext().execute_with(|| { let call = - RuntimeCall::Logger(LoggerCall::log { i: 42, weight: Weight::from_ref_time(10) }); + RuntimeCall::Logger(LoggerCall::log { i: 42, weight: Weight::from_parts(10, 0) }); assert!(!::BaseCallFilter::contains(&call)); assert_ok!(Scheduler::do_schedule( DispatchTime::At(4), @@ -55,7 +55,7 @@ fn basic_scheduling_works() { fn scheduling_with_preimages_works() { new_test_ext().execute_with(|| { let call = - RuntimeCall::Logger(LoggerCall::log { i: 42, weight: Weight::from_ref_time(10) }); + RuntimeCall::Logger(LoggerCall::log { i: 42, weight: Weight::from_parts(10, 0) }); let hash = ::Hashing::hash_of(&call); let len = call.using_encoded(|x| x.len()) as u32; // Important to use here `Bounded::Lookup` to ensure that we request the hash. @@ -79,7 +79,7 @@ fn schedule_after_works() { new_test_ext().execute_with(|| { run_to_block(2); let call = - RuntimeCall::Logger(LoggerCall::log { i: 42, weight: Weight::from_ref_time(10) }); + RuntimeCall::Logger(LoggerCall::log { i: 42, weight: Weight::from_parts(10, 0) }); assert!(!::BaseCallFilter::contains(&call)); // This will schedule the call 3 blocks after the next block... so block 3 + 3 = 6 assert_ok!(Scheduler::do_schedule( @@ -103,7 +103,7 @@ fn schedule_after_zero_works() { new_test_ext().execute_with(|| { run_to_block(2); let call = - RuntimeCall::Logger(LoggerCall::log { i: 42, weight: Weight::from_ref_time(10) }); + RuntimeCall::Logger(LoggerCall::log { i: 42, weight: Weight::from_parts(10, 0) }); assert!(!::BaseCallFilter::contains(&call)); assert_ok!(Scheduler::do_schedule( DispatchTime::After(0), @@ -131,7 +131,7 @@ fn periodic_scheduling_works() { root(), Preimage::bound(RuntimeCall::Logger(logger::Call::log { i: 42, - weight: Weight::from_ref_time(10) + weight: Weight::from_parts(10, 0) })) .unwrap() )); @@ -156,7 +156,7 @@ fn periodic_scheduling_works() { fn reschedule_works() { new_test_ext().execute_with(|| { let call = - RuntimeCall::Logger(LoggerCall::log { i: 42, weight: Weight::from_ref_time(10) }); + RuntimeCall::Logger(LoggerCall::log { i: 42, weight: Weight::from_parts(10, 0) }); assert!(!::BaseCallFilter::contains(&call)); assert_eq!( Scheduler::do_schedule( @@ -195,7 +195,7 @@ fn reschedule_works() { fn reschedule_named_works() { new_test_ext().execute_with(|| { let call = - RuntimeCall::Logger(LoggerCall::log { i: 42, weight: Weight::from_ref_time(10) }); + RuntimeCall::Logger(LoggerCall::log { i: 42, weight: Weight::from_parts(10, 0) }); assert!(!::BaseCallFilter::contains(&call)); assert_eq!( Scheduler::do_schedule_named( @@ -235,7 +235,7 @@ fn reschedule_named_works() { fn reschedule_named_perodic_works() { new_test_ext().execute_with(|| { let call = - RuntimeCall::Logger(LoggerCall::log { i: 42, weight: Weight::from_ref_time(10) }); + RuntimeCall::Logger(LoggerCall::log { i: 42, weight: Weight::from_parts(10, 0) }); assert!(!::BaseCallFilter::contains(&call)); assert_eq!( Scheduler::do_schedule_named( @@ -293,7 +293,7 @@ fn cancel_named_scheduling_works_with_normal_cancel() { root(), Preimage::bound(RuntimeCall::Logger(LoggerCall::log { i: 69, - weight: Weight::from_ref_time(10), + weight: Weight::from_parts(10, 0), })) .unwrap(), ) @@ -305,7 +305,7 @@ fn cancel_named_scheduling_works_with_normal_cancel() { root(), Preimage::bound(RuntimeCall::Logger(LoggerCall::log { i: 42, - weight: Weight::from_ref_time(10), + weight: Weight::from_parts(10, 0), })) .unwrap(), ) @@ -331,7 +331,7 @@ fn cancel_named_periodic_scheduling_works() { root(), Preimage::bound(RuntimeCall::Logger(LoggerCall::log { i: 42, - weight: Weight::from_ref_time(10), + weight: Weight::from_parts(10, 0), })) .unwrap(), ) @@ -345,7 +345,7 @@ fn cancel_named_periodic_scheduling_works() { root(), Preimage::bound(RuntimeCall::Logger(LoggerCall::log { i: 69, - weight: Weight::from_ref_time(10) + weight: Weight::from_parts(10, 0) })) .unwrap(), ) @@ -359,7 +359,7 @@ fn cancel_named_periodic_scheduling_works() { root(), Preimage::bound(RuntimeCall::Logger(LoggerCall::log { i: 69, - weight: Weight::from_ref_time(10), + weight: Weight::from_parts(10, 0), })) .unwrap(), ) @@ -581,12 +581,12 @@ fn scheduler_respects_priority_ordering_with_soft_deadlines() { #[test] fn on_initialize_weight_is_correct() { new_test_ext().execute_with(|| { - let call_weight = Weight::from_ref_time(25); + let call_weight = Weight::from_parts(25, 0); // Named let call = RuntimeCall::Logger(LoggerCall::log { i: 3, - weight: call_weight + Weight::from_ref_time(1), + weight: call_weight + Weight::from_parts(1, 0), }); assert_ok!(Scheduler::do_schedule_named( [1u8; 32], @@ -598,7 +598,7 @@ fn on_initialize_weight_is_correct() { )); let call = RuntimeCall::Logger(LoggerCall::log { i: 42, - weight: call_weight + Weight::from_ref_time(2), + weight: call_weight + Weight::from_parts(2, 0), }); // Anon Periodic assert_ok!(Scheduler::do_schedule( @@ -610,7 +610,7 @@ fn on_initialize_weight_is_correct() { )); let call = RuntimeCall::Logger(LoggerCall::log { i: 69, - weight: call_weight + Weight::from_ref_time(3), + weight: call_weight + Weight::from_parts(3, 0), }); // Anon assert_ok!(Scheduler::do_schedule( @@ -623,7 +623,7 @@ fn on_initialize_weight_is_correct() { // Named Periodic let call = RuntimeCall::Logger(LoggerCall::log { i: 2600, - weight: call_weight + Weight::from_ref_time(4), + weight: call_weight + Weight::from_parts(4, 0), }); assert_ok!(Scheduler::do_schedule_named( [2u8; 32], @@ -641,7 +641,7 @@ fn on_initialize_weight_is_correct() { TestWeightInfo::service_agenda_base(1) + ::service_task(None, true, true) + TestWeightInfo::execute_dispatch_unsigned() + - call_weight + Weight::from_ref_time(4) + call_weight + Weight::from_parts(4, 0) ); assert_eq!(IncompleteSince::::get(), None); assert_eq!(logger::log(), vec![(root(), 2600u32)]); @@ -653,10 +653,10 @@ fn on_initialize_weight_is_correct() { TestWeightInfo::service_agenda_base(2) + ::service_task(None, false, true) + TestWeightInfo::execute_dispatch_unsigned() + - call_weight + Weight::from_ref_time(3) + + call_weight + Weight::from_parts(3, 0) + ::service_task(None, false, false) + TestWeightInfo::execute_dispatch_unsigned() + - call_weight + Weight::from_ref_time(2) + call_weight + Weight::from_parts(2, 0) ); assert_eq!(IncompleteSince::::get(), None); assert_eq!(logger::log(), vec![(root(), 2600u32), (root(), 69u32), (root(), 42u32)]); @@ -668,7 +668,7 @@ fn on_initialize_weight_is_correct() { TestWeightInfo::service_agenda_base(1) + ::service_task(None, true, false) + TestWeightInfo::execute_dispatch_unsigned() + - call_weight + Weight::from_ref_time(1) + call_weight + Weight::from_parts(1, 0) ); assert_eq!(IncompleteSince::::get(), None); assert_eq!( @@ -690,11 +690,11 @@ fn root_calls_works() { new_test_ext().execute_with(|| { let call = Box::new(RuntimeCall::Logger(LoggerCall::log { i: 69, - weight: Weight::from_ref_time(10), + weight: Weight::from_parts(10, 0), })); let call2 = Box::new(RuntimeCall::Logger(LoggerCall::log { i: 42, - weight: Weight::from_ref_time(10), + weight: Weight::from_parts(10, 0), })); assert_ok!( Scheduler::schedule_named(RuntimeOrigin::root(), [1u8; 32], 4, None, 127, call,) @@ -719,15 +719,15 @@ fn fails_to_schedule_task_in_the_past() { let call1 = Box::new(RuntimeCall::Logger(LoggerCall::log { i: 69, - weight: Weight::from_ref_time(10), + weight: Weight::from_parts(10, 0), })); let call2 = Box::new(RuntimeCall::Logger(LoggerCall::log { i: 42, - weight: Weight::from_ref_time(10), + weight: Weight::from_parts(10, 0), })); let call3 = Box::new(RuntimeCall::Logger(LoggerCall::log { i: 42, - weight: Weight::from_ref_time(10), + weight: Weight::from_parts(10, 0), })); assert_noop!( @@ -752,11 +752,11 @@ fn should_use_origin() { new_test_ext().execute_with(|| { let call = Box::new(RuntimeCall::Logger(LoggerCall::log { i: 69, - weight: Weight::from_ref_time(10), + weight: Weight::from_parts(10, 0), })); let call2 = Box::new(RuntimeCall::Logger(LoggerCall::log { i: 42, - weight: Weight::from_ref_time(10), + weight: Weight::from_parts(10, 0), })); assert_ok!(Scheduler::schedule_named( system::RawOrigin::Signed(1).into(), @@ -784,11 +784,11 @@ fn should_check_origin() { new_test_ext().execute_with(|| { let call = Box::new(RuntimeCall::Logger(LoggerCall::log { i: 69, - weight: Weight::from_ref_time(10), + weight: Weight::from_parts(10, 0), })); let call2 = Box::new(RuntimeCall::Logger(LoggerCall::log { i: 42, - weight: Weight::from_ref_time(10), + weight: Weight::from_parts(10, 0), })); assert_noop!( Scheduler::schedule_named( @@ -813,11 +813,11 @@ fn should_check_origin_for_cancel() { new_test_ext().execute_with(|| { let call = Box::new(RuntimeCall::Logger(LoggerCall::log_without_filter { i: 69, - weight: Weight::from_ref_time(10), + weight: Weight::from_parts(10, 0), })); let call2 = Box::new(RuntimeCall::Logger(LoggerCall::log_without_filter { i: 42, - weight: Weight::from_ref_time(10), + weight: Weight::from_parts(10, 0), })); assert_ok!(Scheduler::schedule_named( system::RawOrigin::Signed(1).into(), @@ -861,7 +861,7 @@ fn migration_to_v4_works() { priority: i as u8 + 10, call: RuntimeCall::Logger(LoggerCall::log { i: 96, - weight: Weight::from_ref_time(100), + weight: Weight::from_parts(100, 0), }), maybe_periodic: None, }), @@ -871,7 +871,7 @@ fn migration_to_v4_works() { priority: 123, call: RuntimeCall::Logger(LoggerCall::log { i: 69, - weight: Weight::from_ref_time(10), + weight: Weight::from_parts(10, 0), }), maybe_periodic: Some((456u64, 10)), }), @@ -892,7 +892,7 @@ fn migration_to_v4_works() { priority: 10, call: Preimage::bound(RuntimeCall::Logger(LoggerCall::log { i: 96, - weight: Weight::from_ref_time(100), + weight: Weight::from_parts(100, 0), })) .unwrap(), maybe_periodic: None, @@ -905,7 +905,7 @@ fn migration_to_v4_works() { priority: 123, call: Preimage::bound(RuntimeCall::Logger(LoggerCall::log { i: 69, - weight: Weight::from_ref_time(10), + weight: Weight::from_parts(10, 0), })) .unwrap(), maybe_periodic: Some((456u64, 10)), @@ -922,7 +922,7 @@ fn migration_to_v4_works() { priority: 11, call: Preimage::bound(RuntimeCall::Logger(LoggerCall::log { i: 96, - weight: Weight::from_ref_time(100), + weight: Weight::from_parts(100, 0), })) .unwrap(), maybe_periodic: None, @@ -935,7 +935,7 @@ fn migration_to_v4_works() { priority: 123, call: Preimage::bound(RuntimeCall::Logger(LoggerCall::log { i: 69, - weight: Weight::from_ref_time(10), + weight: Weight::from_parts(10, 0), })) .unwrap(), maybe_periodic: Some((456u64, 10)), @@ -952,7 +952,7 @@ fn migration_to_v4_works() { priority: 12, call: Preimage::bound(RuntimeCall::Logger(LoggerCall::log { i: 96, - weight: Weight::from_ref_time(100), + weight: Weight::from_parts(100, 0), })) .unwrap(), maybe_periodic: None, @@ -965,7 +965,7 @@ fn migration_to_v4_works() { priority: 123, call: Preimage::bound(RuntimeCall::Logger(LoggerCall::log { i: 69, - weight: Weight::from_ref_time(10), + weight: Weight::from_parts(10, 0), })) .unwrap(), maybe_periodic: Some((456u64, 10)), @@ -998,7 +998,7 @@ fn test_migrate_origin() { priority: i as u8 + 10, call: Preimage::bound(RuntimeCall::Logger(LoggerCall::log { i: 96, - weight: Weight::from_ref_time(100), + weight: Weight::from_parts(100, 0), })) .unwrap(), origin: 3u32, @@ -1012,7 +1012,7 @@ fn test_migrate_origin() { origin: 2u32, call: Preimage::bound(RuntimeCall::Logger(LoggerCall::log { i: 69, - weight: Weight::from_ref_time(10), + weight: Weight::from_parts(10, 0), })) .unwrap(), maybe_periodic: Some((456u64, 10)), @@ -1045,7 +1045,7 @@ fn test_migrate_origin() { priority: 10, call: Preimage::bound(RuntimeCall::Logger(LoggerCall::log { i: 96, - weight: Weight::from_ref_time(100) + weight: Weight::from_parts(100, 0) })) .unwrap(), maybe_periodic: None, @@ -1058,7 +1058,7 @@ fn test_migrate_origin() { priority: 123, call: Preimage::bound(RuntimeCall::Logger(LoggerCall::log { i: 69, - weight: Weight::from_ref_time(10) + weight: Weight::from_parts(10, 0) })) .unwrap(), maybe_periodic: Some((456u64, 10)), @@ -1075,7 +1075,7 @@ fn test_migrate_origin() { priority: 11, call: Preimage::bound(RuntimeCall::Logger(LoggerCall::log { i: 96, - weight: Weight::from_ref_time(100) + weight: Weight::from_parts(100, 0) })) .unwrap(), maybe_periodic: None, @@ -1088,7 +1088,7 @@ fn test_migrate_origin() { priority: 123, call: Preimage::bound(RuntimeCall::Logger(LoggerCall::log { i: 69, - weight: Weight::from_ref_time(10) + weight: Weight::from_parts(10, 0) })) .unwrap(), maybe_periodic: Some((456u64, 10)), @@ -1105,7 +1105,7 @@ fn test_migrate_origin() { priority: 12, call: Preimage::bound(RuntimeCall::Logger(LoggerCall::log { i: 96, - weight: Weight::from_ref_time(100) + weight: Weight::from_parts(100, 0) })) .unwrap(), maybe_periodic: None, @@ -1118,7 +1118,7 @@ fn test_migrate_origin() { priority: 123, call: Preimage::bound(RuntimeCall::Logger(LoggerCall::log { i: 69, - weight: Weight::from_ref_time(10) + weight: Weight::from_parts(10, 0) })) .unwrap(), maybe_periodic: Some((456u64, 10)), @@ -1136,7 +1136,7 @@ fn test_migrate_origin() { fn postponed_named_task_cannot_be_rescheduled() { new_test_ext().execute_with(|| { let call = - RuntimeCall::Logger(LoggerCall::log { i: 42, weight: Weight::from_ref_time(1000) }); + RuntimeCall::Logger(LoggerCall::log { i: 42, weight: Weight::from_parts(1000, 0) }); let hash = ::Hashing::hash_of(&call); let len = call.using_encoded(|x| x.len()) as u32; // Important to use here `Bounded::Lookup` to ensure that we request the hash. @@ -1204,7 +1204,7 @@ fn scheduler_v3_anon_basic_works() { use frame_support::traits::schedule::v3::Anon; new_test_ext().execute_with(|| { let call = - RuntimeCall::Logger(LoggerCall::log { i: 42, weight: Weight::from_ref_time(10) }); + RuntimeCall::Logger(LoggerCall::log { i: 42, weight: Weight::from_parts(10, 0) }); // Schedule a call. let _address = >::schedule( @@ -1233,7 +1233,7 @@ fn scheduler_v3_anon_cancel_works() { use frame_support::traits::schedule::v3::Anon; new_test_ext().execute_with(|| { let call = - RuntimeCall::Logger(LoggerCall::log { i: 42, weight: Weight::from_ref_time(10) }); + RuntimeCall::Logger(LoggerCall::log { i: 42, weight: Weight::from_parts(10, 0) }); let bound = Preimage::bound(call).unwrap(); // Schedule a call. @@ -1260,7 +1260,7 @@ fn scheduler_v3_anon_reschedule_works() { use frame_support::traits::schedule::v3::Anon; new_test_ext().execute_with(|| { let call = - RuntimeCall::Logger(LoggerCall::log { i: 42, weight: Weight::from_ref_time(10) }); + RuntimeCall::Logger(LoggerCall::log { i: 42, weight: Weight::from_parts(10, 0) }); // Schedule a call. let address = >::schedule( @@ -1307,7 +1307,7 @@ fn scheduler_v3_anon_next_schedule_time_works() { use frame_support::traits::schedule::v3::Anon; new_test_ext().execute_with(|| { let call = - RuntimeCall::Logger(LoggerCall::log { i: 42, weight: Weight::from_ref_time(10) }); + RuntimeCall::Logger(LoggerCall::log { i: 42, weight: Weight::from_parts(10, 0) }); let bound = Preimage::bound(call).unwrap(); // Schedule a call. @@ -1344,7 +1344,7 @@ fn scheduler_v3_anon_reschedule_and_next_schedule_time_work() { use frame_support::traits::schedule::v3::Anon; new_test_ext().execute_with(|| { let call = - RuntimeCall::Logger(LoggerCall::log { i: 42, weight: Weight::from_ref_time(10) }); + RuntimeCall::Logger(LoggerCall::log { i: 42, weight: Weight::from_parts(10, 0) }); let bound = Preimage::bound(call).unwrap(); // Schedule a call. @@ -1386,7 +1386,7 @@ fn scheduler_v3_anon_schedule_agenda_overflows() { new_test_ext().execute_with(|| { let call = - RuntimeCall::Logger(LoggerCall::log { i: 42, weight: Weight::from_ref_time(10) }); + RuntimeCall::Logger(LoggerCall::log { i: 42, weight: Weight::from_parts(10, 0) }); let bound = Preimage::bound(call).unwrap(); // Schedule the maximal number allowed per block. @@ -1422,7 +1422,7 @@ fn scheduler_v3_anon_cancel_and_schedule_fills_holes() { new_test_ext().execute_with(|| { let call = - RuntimeCall::Logger(LoggerCall::log { i: 42, weight: Weight::from_ref_time(10) }); + RuntimeCall::Logger(LoggerCall::log { i: 42, weight: Weight::from_parts(10, 0) }); let bound = Preimage::bound(call).unwrap(); let mut addrs = Vec::<_>::default(); @@ -1471,7 +1471,7 @@ fn scheduler_v3_anon_reschedule_fills_holes() { new_test_ext().execute_with(|| { let call = - RuntimeCall::Logger(LoggerCall::log { i: 42, weight: Weight::from_ref_time(10) }); + RuntimeCall::Logger(LoggerCall::log { i: 42, weight: Weight::from_parts(10, 0) }); let bound = Preimage::bound(call).unwrap(); let mut addrs = Vec::<_>::default(); @@ -1515,7 +1515,7 @@ fn scheduler_v3_named_basic_works() { new_test_ext().execute_with(|| { let call = - RuntimeCall::Logger(LoggerCall::log { i: 42, weight: Weight::from_ref_time(10) }); + RuntimeCall::Logger(LoggerCall::log { i: 42, weight: Weight::from_parts(10, 0) }); let name = [1u8; 32]; // Schedule a call. @@ -1547,7 +1547,7 @@ fn scheduler_v3_named_cancel_named_works() { use frame_support::traits::schedule::v3::Named; new_test_ext().execute_with(|| { let call = - RuntimeCall::Logger(LoggerCall::log { i: 42, weight: Weight::from_ref_time(10) }); + RuntimeCall::Logger(LoggerCall::log { i: 42, weight: Weight::from_parts(10, 0) }); let bound = Preimage::bound(call).unwrap(); let name = [1u8; 32]; @@ -1577,7 +1577,7 @@ fn scheduler_v3_named_cancel_without_name_works() { use frame_support::traits::schedule::v3::{Anon, Named}; new_test_ext().execute_with(|| { let call = - RuntimeCall::Logger(LoggerCall::log { i: 42, weight: Weight::from_ref_time(10) }); + RuntimeCall::Logger(LoggerCall::log { i: 42, weight: Weight::from_parts(10, 0) }); let bound = Preimage::bound(call).unwrap(); let name = [1u8; 32]; @@ -1607,7 +1607,7 @@ fn scheduler_v3_named_reschedule_named_works() { use frame_support::traits::schedule::v3::{Anon, Named}; new_test_ext().execute_with(|| { let call = - RuntimeCall::Logger(LoggerCall::log { i: 42, weight: Weight::from_ref_time(10) }); + RuntimeCall::Logger(LoggerCall::log { i: 42, weight: Weight::from_parts(10, 0) }); let name = [1u8; 32]; // Schedule a call. @@ -1666,7 +1666,7 @@ fn scheduler_v3_named_next_schedule_time_works() { use frame_support::traits::schedule::v3::{Anon, Named}; new_test_ext().execute_with(|| { let call = - RuntimeCall::Logger(LoggerCall::log { i: 42, weight: Weight::from_ref_time(10) }); + RuntimeCall::Logger(LoggerCall::log { i: 42, weight: Weight::from_parts(10, 0) }); let bound = Preimage::bound(call).unwrap(); let name = [1u8; 32]; @@ -1711,7 +1711,7 @@ fn cancel_last_task_removes_agenda() { new_test_ext().execute_with(|| { let when = 4; let call = - RuntimeCall::Logger(LoggerCall::log { i: 42, weight: Weight::from_ref_time(10) }); + RuntimeCall::Logger(LoggerCall::log { i: 42, weight: Weight::from_parts(10, 0) }); let address = Scheduler::do_schedule( DispatchTime::At(when), None, @@ -1745,7 +1745,7 @@ fn cancel_named_last_task_removes_agenda() { new_test_ext().execute_with(|| { let when = 4; let call = - RuntimeCall::Logger(LoggerCall::log { i: 42, weight: Weight::from_ref_time(10) }); + RuntimeCall::Logger(LoggerCall::log { i: 42, weight: Weight::from_parts(10, 0) }); Scheduler::do_schedule_named( [1u8; 32], DispatchTime::At(when), @@ -1781,7 +1781,7 @@ fn reschedule_last_task_removes_agenda() { new_test_ext().execute_with(|| { let when = 4; let call = - RuntimeCall::Logger(LoggerCall::log { i: 42, weight: Weight::from_ref_time(10) }); + RuntimeCall::Logger(LoggerCall::log { i: 42, weight: Weight::from_parts(10, 0) }); let address = Scheduler::do_schedule( DispatchTime::At(when), None, @@ -1818,7 +1818,7 @@ fn reschedule_named_last_task_removes_agenda() { new_test_ext().execute_with(|| { let when = 4; let call = - RuntimeCall::Logger(LoggerCall::log { i: 42, weight: Weight::from_ref_time(10) }); + RuntimeCall::Logger(LoggerCall::log { i: 42, weight: Weight::from_parts(10, 0) }); Scheduler::do_schedule_named( [1u8; 32], DispatchTime::At(when), diff --git a/frame/scheduler/src/weights.rs b/frame/scheduler/src/weights.rs index 6b786ef8b..577e5b0bc 100644 --- a/frame/scheduler/src/weights.rs +++ b/frame/scheduler/src/weights.rs @@ -86,7 +86,7 @@ impl WeightInfo for SubstrateWeight { // Minimum execution time: 3_079 nanoseconds. Weight::from_parts(7_087_647, 109497) // Standard Error: 658 - .saturating_add(Weight::from_ref_time(279_320).saturating_mul(s.into())) + .saturating_add(Weight::from_parts(279_320, 0).saturating_mul(s.into())) .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -95,7 +95,7 @@ impl WeightInfo for SubstrateWeight { // Measured: `0` // Estimated: `0` // Minimum execution time: 5_192 nanoseconds. - Weight::from_ref_time(5_528_000) + Weight::from_parts(5_528_000, 0) } /// Storage: Preimage PreimageFor (r:1 w:1) /// Proof: Preimage PreimageFor (max_values: None, max_size: Some(4194344), added: 4196819, mode: Measured) @@ -109,10 +109,10 @@ impl WeightInfo for SubstrateWeight { // Minimum execution time: 17_284 nanoseconds. Weight::from_parts(17_574_000, 5252) // Standard Error: 0 - .saturating_add(Weight::from_ref_time(1_126).saturating_mul(s.into())) + .saturating_add(Weight::from_parts(1_126, 0).saturating_mul(s.into())) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().writes(2_u64)) - .saturating_add(Weight::from_proof_size(1).saturating_mul(s.into())) + .saturating_add(Weight::from_parts(0, 1).saturating_mul(s.into())) } /// Storage: Scheduler Lookup (r:0 w:1) /// Proof: Scheduler Lookup (max_values: None, max_size: Some(48), added: 2523, mode: MaxEncodedLen) @@ -121,7 +121,7 @@ impl WeightInfo for SubstrateWeight { // Measured: `0` // Estimated: `0` // Minimum execution time: 7_020 nanoseconds. - Weight::from_ref_time(7_262_000) + Weight::from_parts(7_262_000, 0) .saturating_add(T::DbWeight::get().writes(1_u64)) } fn service_task_periodic() -> Weight { @@ -129,21 +129,21 @@ impl WeightInfo for SubstrateWeight { // Measured: `0` // Estimated: `0` // Minimum execution time: 5_187 nanoseconds. - Weight::from_ref_time(5_368_000) + Weight::from_parts(5_368_000, 0) } fn execute_dispatch_signed() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` // Minimum execution time: 2_313 nanoseconds. - Weight::from_ref_time(2_404_000) + Weight::from_parts(2_404_000, 0) } fn execute_dispatch_unsigned() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` // Minimum execution time: 2_187 nanoseconds. - Weight::from_ref_time(2_362_000) + Weight::from_parts(2_362_000, 0) } /// Storage: Scheduler Agenda (r:1 w:1) /// Proof: Scheduler Agenda (max_values: None, max_size: Some(107022), added: 109497, mode: MaxEncodedLen) @@ -155,7 +155,7 @@ impl WeightInfo for SubstrateWeight { // Minimum execution time: 11_971 nanoseconds. Weight::from_parts(16_060_361, 109497) // Standard Error: 665 - .saturating_add(Weight::from_ref_time(286_324).saturating_mul(s.into())) + .saturating_add(Weight::from_parts(286_324, 0).saturating_mul(s.into())) .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -171,7 +171,7 @@ impl WeightInfo for SubstrateWeight { // Minimum execution time: 15_594 nanoseconds. Weight::from_parts(17_191_501, 109497) // Standard Error: 626 - .saturating_add(Weight::from_ref_time(425_572).saturating_mul(s.into())) + .saturating_add(Weight::from_parts(425_572, 0).saturating_mul(s.into())) .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(T::DbWeight::get().writes(2_u64)) } @@ -187,7 +187,7 @@ impl WeightInfo for SubstrateWeight { // Minimum execution time: 15_127 nanoseconds. Weight::from_parts(20_932_642, 112020) // Standard Error: 692 - .saturating_add(Weight::from_ref_time(288_344).saturating_mul(s.into())) + .saturating_add(Weight::from_parts(288_344, 0).saturating_mul(s.into())) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().writes(2_u64)) } @@ -203,7 +203,7 @@ impl WeightInfo for SubstrateWeight { // Minimum execution time: 16_859 nanoseconds. Weight::from_parts(19_736_937, 112020) // Standard Error: 676 - .saturating_add(Weight::from_ref_time(429_770).saturating_mul(s.into())) + .saturating_add(Weight::from_parts(429_770, 0).saturating_mul(s.into())) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().writes(2_u64)) } @@ -232,7 +232,7 @@ impl WeightInfo for () { // Minimum execution time: 3_079 nanoseconds. Weight::from_parts(7_087_647, 109497) // Standard Error: 658 - .saturating_add(Weight::from_ref_time(279_320).saturating_mul(s.into())) + .saturating_add(Weight::from_parts(279_320, 0).saturating_mul(s.into())) .saturating_add(RocksDbWeight::get().reads(1_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } @@ -241,7 +241,7 @@ impl WeightInfo for () { // Measured: `0` // Estimated: `0` // Minimum execution time: 5_192 nanoseconds. - Weight::from_ref_time(5_528_000) + Weight::from_parts(5_528_000, 0) } /// Storage: Preimage PreimageFor (r:1 w:1) /// Proof: Preimage PreimageFor (max_values: None, max_size: Some(4194344), added: 4196819, mode: Measured) @@ -255,10 +255,10 @@ impl WeightInfo for () { // Minimum execution time: 17_284 nanoseconds. Weight::from_parts(17_574_000, 5252) // Standard Error: 0 - .saturating_add(Weight::from_ref_time(1_126).saturating_mul(s.into())) + .saturating_add(Weight::from_parts(1_126, 0).saturating_mul(s.into())) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().writes(2_u64)) - .saturating_add(Weight::from_proof_size(1).saturating_mul(s.into())) + .saturating_add(Weight::from_parts(0, 1).saturating_mul(s.into())) } /// Storage: Scheduler Lookup (r:0 w:1) /// Proof: Scheduler Lookup (max_values: None, max_size: Some(48), added: 2523, mode: MaxEncodedLen) @@ -267,7 +267,7 @@ impl WeightInfo for () { // Measured: `0` // Estimated: `0` // Minimum execution time: 7_020 nanoseconds. - Weight::from_ref_time(7_262_000) + Weight::from_parts(7_262_000, 0) .saturating_add(RocksDbWeight::get().writes(1_u64)) } fn service_task_periodic() -> Weight { @@ -275,21 +275,21 @@ impl WeightInfo for () { // Measured: `0` // Estimated: `0` // Minimum execution time: 5_187 nanoseconds. - Weight::from_ref_time(5_368_000) + Weight::from_parts(5_368_000, 0) } fn execute_dispatch_signed() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` // Minimum execution time: 2_313 nanoseconds. - Weight::from_ref_time(2_404_000) + Weight::from_parts(2_404_000, 0) } fn execute_dispatch_unsigned() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` // Minimum execution time: 2_187 nanoseconds. - Weight::from_ref_time(2_362_000) + Weight::from_parts(2_362_000, 0) } /// Storage: Scheduler Agenda (r:1 w:1) /// Proof: Scheduler Agenda (max_values: None, max_size: Some(107022), added: 109497, mode: MaxEncodedLen) @@ -301,7 +301,7 @@ impl WeightInfo for () { // Minimum execution time: 11_971 nanoseconds. Weight::from_parts(16_060_361, 109497) // Standard Error: 665 - .saturating_add(Weight::from_ref_time(286_324).saturating_mul(s.into())) + .saturating_add(Weight::from_parts(286_324, 0).saturating_mul(s.into())) .saturating_add(RocksDbWeight::get().reads(1_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } @@ -317,7 +317,7 @@ impl WeightInfo for () { // Minimum execution time: 15_594 nanoseconds. Weight::from_parts(17_191_501, 109497) // Standard Error: 626 - .saturating_add(Weight::from_ref_time(425_572).saturating_mul(s.into())) + .saturating_add(Weight::from_parts(425_572, 0).saturating_mul(s.into())) .saturating_add(RocksDbWeight::get().reads(1_u64)) .saturating_add(RocksDbWeight::get().writes(2_u64)) } @@ -333,7 +333,7 @@ impl WeightInfo for () { // Minimum execution time: 15_127 nanoseconds. Weight::from_parts(20_932_642, 112020) // Standard Error: 692 - .saturating_add(Weight::from_ref_time(288_344).saturating_mul(s.into())) + .saturating_add(Weight::from_parts(288_344, 0).saturating_mul(s.into())) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().writes(2_u64)) } @@ -349,7 +349,7 @@ impl WeightInfo for () { // Minimum execution time: 16_859 nanoseconds. Weight::from_parts(19_736_937, 112020) // Standard Error: 676 - .saturating_add(Weight::from_ref_time(429_770).saturating_mul(s.into())) + .saturating_add(Weight::from_parts(429_770, 0).saturating_mul(s.into())) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().writes(2_u64)) } diff --git a/frame/staking/src/pallet/impls.rs b/frame/staking/src/pallet/impls.rs index 99c2f6043..78c9a51bc 100644 --- a/frame/staking/src/pallet/impls.rs +++ b/frame/staking/src/pallet/impls.rs @@ -1267,7 +1267,7 @@ where disable_strategy: DisableStrategy, ) -> Weight { let reward_proportion = SlashRewardFraction::::get(); - let mut consumed_weight = Weight::from_ref_time(0); + let mut consumed_weight = Weight::from_parts(0, 0); let mut add_db_reads_writes = |reads, writes| { consumed_weight += T::DbWeight::get().reads_writes(reads, writes); }; diff --git a/frame/staking/src/weights.rs b/frame/staking/src/weights.rs index 2c9e1b266..d1814d89d 100644 --- a/frame/staking/src/weights.rs +++ b/frame/staking/src/weights.rs @@ -164,7 +164,7 @@ impl WeightInfo for SubstrateWeight { // Minimum execution time: 31_479 nanoseconds. Weight::from_parts(32_410_035, 10442) // Standard Error: 313 - .saturating_add(Weight::from_ref_time(9_090).saturating_mul(s.into())) + .saturating_add(Weight::from_parts(9_090, 0).saturating_mul(s.into())) .saturating_add(T::DbWeight::get().reads(4_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) } @@ -204,11 +204,11 @@ impl WeightInfo for SubstrateWeight { // Minimum execution time: 71_968 nanoseconds. Weight::from_parts(76_631_804, 32303) // Standard Error: 1_613 - .saturating_add(Weight::from_ref_time(1_058_968).saturating_mul(s.into())) + .saturating_add(Weight::from_parts(1_058_968, 0).saturating_mul(s.into())) .saturating_add(T::DbWeight::get().reads(13_u64)) .saturating_add(T::DbWeight::get().writes(12_u64)) .saturating_add(T::DbWeight::get().writes((1_u64).saturating_mul(s.into()))) - .saturating_add(Weight::from_proof_size(4).saturating_mul(s.into())) + .saturating_add(Weight::from_parts(0, 4).saturating_mul(s.into())) } /// Storage: Staking Ledger (r:1 w:0) /// Proof: Staking Ledger (max_values: None, max_size: Some(1091), added: 3566, mode: MaxEncodedLen) @@ -253,11 +253,11 @@ impl WeightInfo for SubstrateWeight { // Minimum execution time: 25_685 nanoseconds. Weight::from_parts(25_290_286, 3566) // Standard Error: 5_164 - .saturating_add(Weight::from_ref_time(6_445_608).saturating_mul(k.into())) + .saturating_add(Weight::from_parts(6_445_608, 0).saturating_mul(k.into())) .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(T::DbWeight::get().reads((1_u64).saturating_mul(k.into()))) .saturating_add(T::DbWeight::get().writes((1_u64).saturating_mul(k.into()))) - .saturating_add(Weight::from_proof_size(3033).saturating_mul(k.into())) + .saturating_add(Weight::from_parts(0, 3033).saturating_mul(k.into())) } /// Storage: Staking Ledger (r:1 w:0) /// Proof: Staking Ledger (max_values: None, max_size: Some(1091), added: 3566, mode: MaxEncodedLen) @@ -289,11 +289,11 @@ impl WeightInfo for SubstrateWeight { // Minimum execution time: 59_542 nanoseconds. Weight::from_parts(57_558_678, 21988) // Standard Error: 10_364 - .saturating_add(Weight::from_ref_time(2_759_713).saturating_mul(n.into())) + .saturating_add(Weight::from_parts(2_759_713, 0).saturating_mul(n.into())) .saturating_add(T::DbWeight::get().reads(12_u64)) .saturating_add(T::DbWeight::get().reads((1_u64).saturating_mul(n.into()))) .saturating_add(T::DbWeight::get().writes(6_u64)) - .saturating_add(Weight::from_proof_size(2520).saturating_mul(n.into())) + .saturating_add(Weight::from_parts(0, 2520).saturating_mul(n.into())) } /// Storage: Staking Ledger (r:1 w:0) /// Proof: Staking Ledger (max_values: None, max_size: Some(1091), added: 3566, mode: MaxEncodedLen) @@ -351,7 +351,7 @@ impl WeightInfo for SubstrateWeight { // Measured: `0` // Estimated: `0` // Minimum execution time: 3_069 nanoseconds. - Weight::from_ref_time(3_176_000) + Weight::from_parts(3_176_000, 0) .saturating_add(T::DbWeight::get().writes(1_u64)) } /// Storage: Staking ForceEra (r:0 w:1) @@ -361,7 +361,7 @@ impl WeightInfo for SubstrateWeight { // Measured: `0` // Estimated: `0` // Minimum execution time: 11_386 nanoseconds. - Weight::from_ref_time(11_672_000) + Weight::from_parts(11_672_000, 0) .saturating_add(T::DbWeight::get().writes(1_u64)) } /// Storage: Staking ForceEra (r:0 w:1) @@ -371,7 +371,7 @@ impl WeightInfo for SubstrateWeight { // Measured: `0` // Estimated: `0` // Minimum execution time: 11_591 nanoseconds. - Weight::from_ref_time(11_799_000) + Weight::from_parts(11_799_000, 0) .saturating_add(T::DbWeight::get().writes(1_u64)) } /// Storage: Staking ForceEra (r:0 w:1) @@ -381,7 +381,7 @@ impl WeightInfo for SubstrateWeight { // Measured: `0` // Estimated: `0` // Minimum execution time: 11_553 nanoseconds. - Weight::from_ref_time(11_871_000) + Weight::from_parts(11_871_000, 0) .saturating_add(T::DbWeight::get().writes(1_u64)) } /// Storage: Staking Invulnerables (r:0 w:1) @@ -392,9 +392,9 @@ impl WeightInfo for SubstrateWeight { // Measured: `0` // Estimated: `0` // Minimum execution time: 3_292 nanoseconds. - Weight::from_ref_time(3_754_352) + Weight::from_parts(3_754_352, 0) // Standard Error: 40 - .saturating_add(Weight::from_ref_time(9_838).saturating_mul(v.into())) + .saturating_add(Weight::from_parts(9_838, 0).saturating_mul(v.into())) .saturating_add(T::DbWeight::get().writes(1_u64)) } /// Storage: Staking Bonded (r:1 w:1) @@ -431,11 +431,11 @@ impl WeightInfo for SubstrateWeight { // Minimum execution time: 65_307 nanoseconds. Weight::from_parts(70_227_980, 27930) // Standard Error: 2_113 - .saturating_add(Weight::from_ref_time(1_059_856).saturating_mul(s.into())) + .saturating_add(Weight::from_parts(1_059_856, 0).saturating_mul(s.into())) .saturating_add(T::DbWeight::get().reads(11_u64)) .saturating_add(T::DbWeight::get().writes(12_u64)) .saturating_add(T::DbWeight::get().writes((1_u64).saturating_mul(s.into()))) - .saturating_add(Weight::from_proof_size(4).saturating_mul(s.into())) + .saturating_add(Weight::from_parts(0, 4).saturating_mul(s.into())) } /// Storage: Staking UnappliedSlashes (r:1 w:1) /// Proof Skipped: Staking UnappliedSlashes (max_values: None, max_size: None, mode: Measured) @@ -447,7 +447,7 @@ impl WeightInfo for SubstrateWeight { // Minimum execution time: 89_123 nanoseconds. Weight::from_parts(890_989_741, 69146) // Standard Error: 58_282 - .saturating_add(Weight::from_ref_time(4_920_413).saturating_mul(s.into())) + .saturating_add(Weight::from_parts(4_920_413, 0).saturating_mul(s.into())) .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -477,12 +477,12 @@ impl WeightInfo for SubstrateWeight { // Minimum execution time: 73_652 nanoseconds. Weight::from_parts(127_839_483, 54756) // Standard Error: 14_195 - .saturating_add(Weight::from_ref_time(21_932_079).saturating_mul(n.into())) + .saturating_add(Weight::from_parts(21_932_079, 0).saturating_mul(n.into())) .saturating_add(T::DbWeight::get().reads(9_u64)) .saturating_add(T::DbWeight::get().reads((3_u64).saturating_mul(n.into()))) .saturating_add(T::DbWeight::get().writes(2_u64)) .saturating_add(T::DbWeight::get().writes((1_u64).saturating_mul(n.into()))) - .saturating_add(Weight::from_proof_size(8024).saturating_mul(n.into())) + .saturating_add(Weight::from_parts(0, 8024).saturating_mul(n.into())) } /// Storage: Staking CurrentEra (r:1 w:0) /// Proof: Staking CurrentEra (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) @@ -512,12 +512,12 @@ impl WeightInfo for SubstrateWeight { // Minimum execution time: 94_560 nanoseconds. Weight::from_parts(154_033_219, 83594) // Standard Error: 26_663 - .saturating_add(Weight::from_ref_time(31_269_223).saturating_mul(n.into())) + .saturating_add(Weight::from_parts(31_269_223, 0).saturating_mul(n.into())) .saturating_add(T::DbWeight::get().reads(10_u64)) .saturating_add(T::DbWeight::get().reads((5_u64).saturating_mul(n.into()))) .saturating_add(T::DbWeight::get().writes(3_u64)) .saturating_add(T::DbWeight::get().writes((3_u64).saturating_mul(n.into()))) - .saturating_add(Weight::from_proof_size(16026).saturating_mul(n.into())) + .saturating_add(Weight::from_parts(0, 16026).saturating_mul(n.into())) } /// Storage: Staking Ledger (r:1 w:1) /// Proof: Staking Ledger (max_values: None, max_size: Some(1091), added: 3566, mode: MaxEncodedLen) @@ -539,7 +539,7 @@ impl WeightInfo for SubstrateWeight { // Minimum execution time: 74_764 nanoseconds. Weight::from_parts(75_814_067, 25491) // Standard Error: 1_217 - .saturating_add(Weight::from_ref_time(64_725).saturating_mul(l.into())) + .saturating_add(Weight::from_parts(64_725, 0).saturating_mul(l.into())) .saturating_add(T::DbWeight::get().reads(9_u64)) .saturating_add(T::DbWeight::get().writes(8_u64)) } @@ -577,11 +577,11 @@ impl WeightInfo for SubstrateWeight { // Minimum execution time: 77_611 nanoseconds. Weight::from_parts(79_760_034, 31810) // Standard Error: 1_597 - .saturating_add(Weight::from_ref_time(1_039_268).saturating_mul(s.into())) + .saturating_add(Weight::from_parts(1_039_268, 0).saturating_mul(s.into())) .saturating_add(T::DbWeight::get().reads(12_u64)) .saturating_add(T::DbWeight::get().writes(12_u64)) .saturating_add(T::DbWeight::get().writes((1_u64).saturating_mul(s.into()))) - .saturating_add(Weight::from_proof_size(4).saturating_mul(s.into())) + .saturating_add(Weight::from_parts(0, 4).saturating_mul(s.into())) } /// Storage: VoterList CounterForListNodes (r:1 w:0) /// Proof: VoterList CounterForListNodes (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) @@ -626,16 +626,16 @@ impl WeightInfo for SubstrateWeight { // Minimum execution time: 489_824 nanoseconds. Weight::from_parts(491_687_000, 528203) // Standard Error: 1_787_577 - .saturating_add(Weight::from_ref_time(58_719_498).saturating_mul(v.into())) + .saturating_add(Weight::from_parts(58_719_498, 0).saturating_mul(v.into())) // Standard Error: 178_122 - .saturating_add(Weight::from_ref_time(13_273_555).saturating_mul(n.into())) + .saturating_add(Weight::from_parts(13_273_555, 0).saturating_mul(n.into())) .saturating_add(T::DbWeight::get().reads(206_u64)) .saturating_add(T::DbWeight::get().reads((5_u64).saturating_mul(v.into()))) .saturating_add(T::DbWeight::get().reads((4_u64).saturating_mul(n.into()))) .saturating_add(T::DbWeight::get().writes(4_u64)) .saturating_add(T::DbWeight::get().writes((3_u64).saturating_mul(v.into()))) - .saturating_add(Weight::from_proof_size(16743).saturating_mul(v.into())) - .saturating_add(Weight::from_proof_size(12947).saturating_mul(n.into())) + .saturating_add(Weight::from_parts(0, 16743).saturating_mul(v.into())) + .saturating_add(Weight::from_parts(0, 12947).saturating_mul(n.into())) } /// Storage: VoterList CounterForListNodes (r:1 w:0) /// Proof: VoterList CounterForListNodes (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) @@ -662,15 +662,15 @@ impl WeightInfo for SubstrateWeight { // Minimum execution time: 23_373_467 nanoseconds. Weight::from_parts(23_497_257_000, 511899) // Standard Error: 299_205 - .saturating_add(Weight::from_ref_time(3_434_000).saturating_mul(v.into())) + .saturating_add(Weight::from_parts(3_434_000, 0).saturating_mul(v.into())) // Standard Error: 299_205 - .saturating_add(Weight::from_ref_time(2_568_954).saturating_mul(n.into())) + .saturating_add(Weight::from_parts(2_568_954, 0).saturating_mul(n.into())) .saturating_add(T::DbWeight::get().reads(201_u64)) .saturating_add(T::DbWeight::get().reads((5_u64).saturating_mul(v.into()))) .saturating_add(T::DbWeight::get().reads((4_u64).saturating_mul(n.into()))) .saturating_add(T::DbWeight::get().writes(1_u64)) - .saturating_add(Weight::from_proof_size(14295).saturating_mul(v.into())) - .saturating_add(Weight::from_proof_size(11775).saturating_mul(n.into())) + .saturating_add(Weight::from_parts(0, 14295).saturating_mul(v.into())) + .saturating_add(Weight::from_parts(0, 11775).saturating_mul(n.into())) } /// Storage: Staking CounterForValidators (r:1 w:0) /// Proof: Staking CounterForValidators (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) @@ -684,10 +684,10 @@ impl WeightInfo for SubstrateWeight { // Minimum execution time: 3_882_120 nanoseconds. Weight::from_parts(3_951_993_000, 3019) // Standard Error: 46_729 - .saturating_add(Weight::from_ref_time(2_856_043).saturating_mul(v.into())) + .saturating_add(Weight::from_parts(2_856_043, 0).saturating_mul(v.into())) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().reads((1_u64).saturating_mul(v.into()))) - .saturating_add(Weight::from_proof_size(2520).saturating_mul(v.into())) + .saturating_add(Weight::from_parts(0, 2520).saturating_mul(v.into())) } /// Storage: Staking MinCommission (r:0 w:1) /// Proof: Staking MinCommission (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) @@ -706,7 +706,7 @@ impl WeightInfo for SubstrateWeight { // Measured: `0` // Estimated: `0` // Minimum execution time: 8_427 nanoseconds. - Weight::from_ref_time(8_794_000) + Weight::from_parts(8_794_000, 0) .saturating_add(T::DbWeight::get().writes(6_u64)) } /// Storage: Staking MinCommission (r:0 w:1) @@ -726,7 +726,7 @@ impl WeightInfo for SubstrateWeight { // Measured: `0` // Estimated: `0` // Minimum execution time: 7_620 nanoseconds. - Weight::from_ref_time(7_901_000) + Weight::from_parts(7_901_000, 0) .saturating_add(T::DbWeight::get().writes(6_u64)) } /// Storage: Staking Ledger (r:1 w:0) @@ -778,7 +778,7 @@ impl WeightInfo for SubstrateWeight { // Measured: `0` // Estimated: `0` // Minimum execution time: 4_518 nanoseconds. - Weight::from_ref_time(4_656_000) + Weight::from_parts(4_656_000, 0) .saturating_add(T::DbWeight::get().writes(1_u64)) } } @@ -866,7 +866,7 @@ impl WeightInfo for () { // Minimum execution time: 31_479 nanoseconds. Weight::from_parts(32_410_035, 10442) // Standard Error: 313 - .saturating_add(Weight::from_ref_time(9_090).saturating_mul(s.into())) + .saturating_add(Weight::from_parts(9_090, 0).saturating_mul(s.into())) .saturating_add(RocksDbWeight::get().reads(4_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) } @@ -906,11 +906,11 @@ impl WeightInfo for () { // Minimum execution time: 71_968 nanoseconds. Weight::from_parts(76_631_804, 32303) // Standard Error: 1_613 - .saturating_add(Weight::from_ref_time(1_058_968).saturating_mul(s.into())) + .saturating_add(Weight::from_parts(1_058_968, 0).saturating_mul(s.into())) .saturating_add(RocksDbWeight::get().reads(13_u64)) .saturating_add(RocksDbWeight::get().writes(12_u64)) .saturating_add(RocksDbWeight::get().writes((1_u64).saturating_mul(s.into()))) - .saturating_add(Weight::from_proof_size(4).saturating_mul(s.into())) + .saturating_add(Weight::from_parts(0, 4).saturating_mul(s.into())) } /// Storage: Staking Ledger (r:1 w:0) /// Proof: Staking Ledger (max_values: None, max_size: Some(1091), added: 3566, mode: MaxEncodedLen) @@ -955,11 +955,11 @@ impl WeightInfo for () { // Minimum execution time: 25_685 nanoseconds. Weight::from_parts(25_290_286, 3566) // Standard Error: 5_164 - .saturating_add(Weight::from_ref_time(6_445_608).saturating_mul(k.into())) + .saturating_add(Weight::from_parts(6_445_608, 0).saturating_mul(k.into())) .saturating_add(RocksDbWeight::get().reads(1_u64)) .saturating_add(RocksDbWeight::get().reads((1_u64).saturating_mul(k.into()))) .saturating_add(RocksDbWeight::get().writes((1_u64).saturating_mul(k.into()))) - .saturating_add(Weight::from_proof_size(3033).saturating_mul(k.into())) + .saturating_add(Weight::from_parts(0, 3033).saturating_mul(k.into())) } /// Storage: Staking Ledger (r:1 w:0) /// Proof: Staking Ledger (max_values: None, max_size: Some(1091), added: 3566, mode: MaxEncodedLen) @@ -991,11 +991,11 @@ impl WeightInfo for () { // Minimum execution time: 59_542 nanoseconds. Weight::from_parts(57_558_678, 21988) // Standard Error: 10_364 - .saturating_add(Weight::from_ref_time(2_759_713).saturating_mul(n.into())) + .saturating_add(Weight::from_parts(2_759_713, 0).saturating_mul(n.into())) .saturating_add(RocksDbWeight::get().reads(12_u64)) .saturating_add(RocksDbWeight::get().reads((1_u64).saturating_mul(n.into()))) .saturating_add(RocksDbWeight::get().writes(6_u64)) - .saturating_add(Weight::from_proof_size(2520).saturating_mul(n.into())) + .saturating_add(Weight::from_parts(0, 2520).saturating_mul(n.into())) } /// Storage: Staking Ledger (r:1 w:0) /// Proof: Staking Ledger (max_values: None, max_size: Some(1091), added: 3566, mode: MaxEncodedLen) @@ -1053,7 +1053,7 @@ impl WeightInfo for () { // Measured: `0` // Estimated: `0` // Minimum execution time: 3_069 nanoseconds. - Weight::from_ref_time(3_176_000) + Weight::from_parts(3_176_000, 0) .saturating_add(RocksDbWeight::get().writes(1_u64)) } /// Storage: Staking ForceEra (r:0 w:1) @@ -1063,7 +1063,7 @@ impl WeightInfo for () { // Measured: `0` // Estimated: `0` // Minimum execution time: 11_386 nanoseconds. - Weight::from_ref_time(11_672_000) + Weight::from_parts(11_672_000, 0) .saturating_add(RocksDbWeight::get().writes(1_u64)) } /// Storage: Staking ForceEra (r:0 w:1) @@ -1073,7 +1073,7 @@ impl WeightInfo for () { // Measured: `0` // Estimated: `0` // Minimum execution time: 11_591 nanoseconds. - Weight::from_ref_time(11_799_000) + Weight::from_parts(11_799_000, 0) .saturating_add(RocksDbWeight::get().writes(1_u64)) } /// Storage: Staking ForceEra (r:0 w:1) @@ -1083,7 +1083,7 @@ impl WeightInfo for () { // Measured: `0` // Estimated: `0` // Minimum execution time: 11_553 nanoseconds. - Weight::from_ref_time(11_871_000) + Weight::from_parts(11_871_000, 0) .saturating_add(RocksDbWeight::get().writes(1_u64)) } /// Storage: Staking Invulnerables (r:0 w:1) @@ -1094,9 +1094,9 @@ impl WeightInfo for () { // Measured: `0` // Estimated: `0` // Minimum execution time: 3_292 nanoseconds. - Weight::from_ref_time(3_754_352) + Weight::from_parts(3_754_352, 0) // Standard Error: 40 - .saturating_add(Weight::from_ref_time(9_838).saturating_mul(v.into())) + .saturating_add(Weight::from_parts(9_838, 0).saturating_mul(v.into())) .saturating_add(RocksDbWeight::get().writes(1_u64)) } /// Storage: Staking Bonded (r:1 w:1) @@ -1133,11 +1133,11 @@ impl WeightInfo for () { // Minimum execution time: 65_307 nanoseconds. Weight::from_parts(70_227_980, 27930) // Standard Error: 2_113 - .saturating_add(Weight::from_ref_time(1_059_856).saturating_mul(s.into())) + .saturating_add(Weight::from_parts(1_059_856, 0).saturating_mul(s.into())) .saturating_add(RocksDbWeight::get().reads(11_u64)) .saturating_add(RocksDbWeight::get().writes(12_u64)) .saturating_add(RocksDbWeight::get().writes((1_u64).saturating_mul(s.into()))) - .saturating_add(Weight::from_proof_size(4).saturating_mul(s.into())) + .saturating_add(Weight::from_parts(0, 4).saturating_mul(s.into())) } /// Storage: Staking UnappliedSlashes (r:1 w:1) /// Proof Skipped: Staking UnappliedSlashes (max_values: None, max_size: None, mode: Measured) @@ -1149,7 +1149,7 @@ impl WeightInfo for () { // Minimum execution time: 89_123 nanoseconds. Weight::from_parts(890_989_741, 69146) // Standard Error: 58_282 - .saturating_add(Weight::from_ref_time(4_920_413).saturating_mul(s.into())) + .saturating_add(Weight::from_parts(4_920_413, 0).saturating_mul(s.into())) .saturating_add(RocksDbWeight::get().reads(1_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } @@ -1179,12 +1179,12 @@ impl WeightInfo for () { // Minimum execution time: 73_652 nanoseconds. Weight::from_parts(127_839_483, 54756) // Standard Error: 14_195 - .saturating_add(Weight::from_ref_time(21_932_079).saturating_mul(n.into())) + .saturating_add(Weight::from_parts(21_932_079, 0).saturating_mul(n.into())) .saturating_add(RocksDbWeight::get().reads(9_u64)) .saturating_add(RocksDbWeight::get().reads((3_u64).saturating_mul(n.into()))) .saturating_add(RocksDbWeight::get().writes(2_u64)) .saturating_add(RocksDbWeight::get().writes((1_u64).saturating_mul(n.into()))) - .saturating_add(Weight::from_proof_size(8024).saturating_mul(n.into())) + .saturating_add(Weight::from_parts(0, 8024).saturating_mul(n.into())) } /// Storage: Staking CurrentEra (r:1 w:0) /// Proof: Staking CurrentEra (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) @@ -1214,12 +1214,12 @@ impl WeightInfo for () { // Minimum execution time: 94_560 nanoseconds. Weight::from_parts(154_033_219, 83594) // Standard Error: 26_663 - .saturating_add(Weight::from_ref_time(31_269_223).saturating_mul(n.into())) + .saturating_add(Weight::from_parts(31_269_223, 0).saturating_mul(n.into())) .saturating_add(RocksDbWeight::get().reads(10_u64)) .saturating_add(RocksDbWeight::get().reads((5_u64).saturating_mul(n.into()))) .saturating_add(RocksDbWeight::get().writes(3_u64)) .saturating_add(RocksDbWeight::get().writes((3_u64).saturating_mul(n.into()))) - .saturating_add(Weight::from_proof_size(16026).saturating_mul(n.into())) + .saturating_add(Weight::from_parts(0, 16026).saturating_mul(n.into())) } /// Storage: Staking Ledger (r:1 w:1) /// Proof: Staking Ledger (max_values: None, max_size: Some(1091), added: 3566, mode: MaxEncodedLen) @@ -1241,7 +1241,7 @@ impl WeightInfo for () { // Minimum execution time: 74_764 nanoseconds. Weight::from_parts(75_814_067, 25491) // Standard Error: 1_217 - .saturating_add(Weight::from_ref_time(64_725).saturating_mul(l.into())) + .saturating_add(Weight::from_parts(64_725, 0).saturating_mul(l.into())) .saturating_add(RocksDbWeight::get().reads(9_u64)) .saturating_add(RocksDbWeight::get().writes(8_u64)) } @@ -1279,11 +1279,11 @@ impl WeightInfo for () { // Minimum execution time: 77_611 nanoseconds. Weight::from_parts(79_760_034, 31810) // Standard Error: 1_597 - .saturating_add(Weight::from_ref_time(1_039_268).saturating_mul(s.into())) + .saturating_add(Weight::from_parts(1_039_268, 0).saturating_mul(s.into())) .saturating_add(RocksDbWeight::get().reads(12_u64)) .saturating_add(RocksDbWeight::get().writes(12_u64)) .saturating_add(RocksDbWeight::get().writes((1_u64).saturating_mul(s.into()))) - .saturating_add(Weight::from_proof_size(4).saturating_mul(s.into())) + .saturating_add(Weight::from_parts(0, 4).saturating_mul(s.into())) } /// Storage: VoterList CounterForListNodes (r:1 w:0) /// Proof: VoterList CounterForListNodes (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) @@ -1328,16 +1328,16 @@ impl WeightInfo for () { // Minimum execution time: 489_824 nanoseconds. Weight::from_parts(491_687_000, 528203) // Standard Error: 1_787_577 - .saturating_add(Weight::from_ref_time(58_719_498).saturating_mul(v.into())) + .saturating_add(Weight::from_parts(58_719_498, 0).saturating_mul(v.into())) // Standard Error: 178_122 - .saturating_add(Weight::from_ref_time(13_273_555).saturating_mul(n.into())) + .saturating_add(Weight::from_parts(13_273_555, 0).saturating_mul(n.into())) .saturating_add(RocksDbWeight::get().reads(206_u64)) .saturating_add(RocksDbWeight::get().reads((5_u64).saturating_mul(v.into()))) .saturating_add(RocksDbWeight::get().reads((4_u64).saturating_mul(n.into()))) .saturating_add(RocksDbWeight::get().writes(4_u64)) .saturating_add(RocksDbWeight::get().writes((3_u64).saturating_mul(v.into()))) - .saturating_add(Weight::from_proof_size(16743).saturating_mul(v.into())) - .saturating_add(Weight::from_proof_size(12947).saturating_mul(n.into())) + .saturating_add(Weight::from_parts(0, 16743).saturating_mul(v.into())) + .saturating_add(Weight::from_parts(0, 12947).saturating_mul(n.into())) } /// Storage: VoterList CounterForListNodes (r:1 w:0) /// Proof: VoterList CounterForListNodes (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) @@ -1364,15 +1364,15 @@ impl WeightInfo for () { // Minimum execution time: 23_373_467 nanoseconds. Weight::from_parts(23_497_257_000, 511899) // Standard Error: 299_205 - .saturating_add(Weight::from_ref_time(3_434_000).saturating_mul(v.into())) + .saturating_add(Weight::from_parts(3_434_000, 0).saturating_mul(v.into())) // Standard Error: 299_205 - .saturating_add(Weight::from_ref_time(2_568_954).saturating_mul(n.into())) + .saturating_add(Weight::from_parts(2_568_954, 0).saturating_mul(n.into())) .saturating_add(RocksDbWeight::get().reads(201_u64)) .saturating_add(RocksDbWeight::get().reads((5_u64).saturating_mul(v.into()))) .saturating_add(RocksDbWeight::get().reads((4_u64).saturating_mul(n.into()))) .saturating_add(RocksDbWeight::get().writes(1_u64)) - .saturating_add(Weight::from_proof_size(14295).saturating_mul(v.into())) - .saturating_add(Weight::from_proof_size(11775).saturating_mul(n.into())) + .saturating_add(Weight::from_parts(0, 14295).saturating_mul(v.into())) + .saturating_add(Weight::from_parts(0, 11775).saturating_mul(n.into())) } /// Storage: Staking CounterForValidators (r:1 w:0) /// Proof: Staking CounterForValidators (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) @@ -1386,10 +1386,10 @@ impl WeightInfo for () { // Minimum execution time: 3_882_120 nanoseconds. Weight::from_parts(3_951_993_000, 3019) // Standard Error: 46_729 - .saturating_add(Weight::from_ref_time(2_856_043).saturating_mul(v.into())) + .saturating_add(Weight::from_parts(2_856_043, 0).saturating_mul(v.into())) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().reads((1_u64).saturating_mul(v.into()))) - .saturating_add(Weight::from_proof_size(2520).saturating_mul(v.into())) + .saturating_add(Weight::from_parts(0, 2520).saturating_mul(v.into())) } /// Storage: Staking MinCommission (r:0 w:1) /// Proof: Staking MinCommission (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) @@ -1408,7 +1408,7 @@ impl WeightInfo for () { // Measured: `0` // Estimated: `0` // Minimum execution time: 8_427 nanoseconds. - Weight::from_ref_time(8_794_000) + Weight::from_parts(8_794_000, 0) .saturating_add(RocksDbWeight::get().writes(6_u64)) } /// Storage: Staking MinCommission (r:0 w:1) @@ -1428,7 +1428,7 @@ impl WeightInfo for () { // Measured: `0` // Estimated: `0` // Minimum execution time: 7_620 nanoseconds. - Weight::from_ref_time(7_901_000) + Weight::from_parts(7_901_000, 0) .saturating_add(RocksDbWeight::get().writes(6_u64)) } /// Storage: Staking Ledger (r:1 w:0) @@ -1480,7 +1480,7 @@ impl WeightInfo for () { // Measured: `0` // Estimated: `0` // Minimum execution time: 4_518 nanoseconds. - Weight::from_ref_time(4_656_000) + Weight::from_parts(4_656_000, 0) .saturating_add(RocksDbWeight::get().writes(1_u64)) } } diff --git a/frame/state-trie-migration/src/lib.rs b/frame/state-trie-migration/src/lib.rs index 0ebb8122d..547dfa5cd 100644 --- a/frame/state-trie-migration/src/lib.rs +++ b/frame/state-trie-migration/src/lib.rs @@ -1136,25 +1136,25 @@ mod mock { impl WeightInfo for StateMigrationTestWeight { fn process_top_key(_: u32) -> Weight { - Weight::from_ref_time(1000000) + Weight::from_parts(1000000, 0) } fn continue_migrate() -> Weight { - Weight::from_ref_time(1000000) + Weight::from_parts(1000000, 0) } fn continue_migrate_wrong_witness() -> Weight { - Weight::from_ref_time(1000000) + Weight::from_parts(1000000, 0) } fn migrate_custom_top_fail() -> Weight { - Weight::from_ref_time(1000000) + Weight::from_parts(1000000, 0) } fn migrate_custom_top_success() -> Weight { - Weight::from_ref_time(1000000) + Weight::from_parts(1000000, 0) } fn migrate_custom_child_fail() -> Weight { - Weight::from_ref_time(1000000) + Weight::from_parts(1000000, 0) } fn migrate_custom_child_success() -> Weight { - Weight::from_ref_time(1000000) + Weight::from_parts(1000000, 0) } } diff --git a/frame/state-trie-migration/src/weights.rs b/frame/state-trie-migration/src/weights.rs index a833d816c..e087ba185 100644 --- a/frame/state-trie-migration/src/weights.rs +++ b/frame/state-trie-migration/src/weights.rs @@ -88,7 +88,7 @@ impl WeightInfo for SubstrateWeight { // Measured: `0` // Estimated: `0` // Minimum execution time: 8_817 nanoseconds. - Weight::from_ref_time(9_027_000) + Weight::from_parts(9_027_000, 0) } /// Storage: unknown `0x666f6f` (r:1 w:1) /// Proof Skipped: unknown `0x666f6f` (r:1 w:1) @@ -106,7 +106,7 @@ impl WeightInfo for SubstrateWeight { // Measured: `0` // Estimated: `0` // Minimum execution time: 9_020 nanoseconds. - Weight::from_ref_time(9_234_000) + Weight::from_parts(9_234_000, 0) } /// Storage: unknown `0x666f6f` (r:1 w:1) /// Proof Skipped: unknown `0x666f6f` (r:1 w:1) @@ -129,10 +129,10 @@ impl WeightInfo for SubstrateWeight { // Minimum execution time: 5_279 nanoseconds. Weight::from_parts(5_517_000, 2700) // Standard Error: 1 - .saturating_add(Weight::from_ref_time(1_230).saturating_mul(v.into())) + .saturating_add(Weight::from_parts(1_230, 0).saturating_mul(v.into())) .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) - .saturating_add(Weight::from_proof_size(1).saturating_mul(v.into())) + .saturating_add(Weight::from_parts(0, 1).saturating_mul(v.into())) } } @@ -166,7 +166,7 @@ impl WeightInfo for () { // Measured: `0` // Estimated: `0` // Minimum execution time: 8_817 nanoseconds. - Weight::from_ref_time(9_027_000) + Weight::from_parts(9_027_000, 0) } /// Storage: unknown `0x666f6f` (r:1 w:1) /// Proof Skipped: unknown `0x666f6f` (r:1 w:1) @@ -184,7 +184,7 @@ impl WeightInfo for () { // Measured: `0` // Estimated: `0` // Minimum execution time: 9_020 nanoseconds. - Weight::from_ref_time(9_234_000) + Weight::from_parts(9_234_000, 0) } /// Storage: unknown `0x666f6f` (r:1 w:1) /// Proof Skipped: unknown `0x666f6f` (r:1 w:1) @@ -207,9 +207,9 @@ impl WeightInfo for () { // Minimum execution time: 5_279 nanoseconds. Weight::from_parts(5_517_000, 2700) // Standard Error: 1 - .saturating_add(Weight::from_ref_time(1_230).saturating_mul(v.into())) + .saturating_add(Weight::from_parts(1_230, 0).saturating_mul(v.into())) .saturating_add(RocksDbWeight::get().reads(1_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) - .saturating_add(Weight::from_proof_size(1).saturating_mul(v.into())) + .saturating_add(Weight::from_parts(0, 1).saturating_mul(v.into())) } } diff --git a/frame/sudo/src/tests.rs b/frame/sudo/src/tests.rs index e924bc77c..c854fed8f 100644 --- a/frame/sudo/src/tests.rs +++ b/frame/sudo/src/tests.rs @@ -41,7 +41,7 @@ fn sudo_basics() { // A privileged function should work when `sudo` is passed the root `key` as `origin`. let call = Box::new(RuntimeCall::Logger(LoggerCall::privileged_i32_log { i: 42, - weight: Weight::from_ref_time(1_000), + weight: Weight::from_parts(1_000, 0), })); assert_ok!(Sudo::sudo(RuntimeOrigin::signed(1), call)); assert_eq!(Logger::i32_log(), vec![42i32]); @@ -49,7 +49,7 @@ fn sudo_basics() { // A privileged function should not work when `sudo` is passed a non-root `key` as `origin`. let call = Box::new(RuntimeCall::Logger(LoggerCall::privileged_i32_log { i: 42, - weight: Weight::from_ref_time(1_000), + weight: Weight::from_parts(1_000, 0), })); assert_noop!(Sudo::sudo(RuntimeOrigin::signed(2), call), Error::::RequireSudo); }); @@ -64,7 +64,7 @@ fn sudo_emits_events_correctly() { // Should emit event to indicate success when called with the root `key` and `call` is `Ok`. let call = Box::new(RuntimeCall::Logger(LoggerCall::privileged_i32_log { i: 42, - weight: Weight::from_ref_time(1), + weight: Weight::from_parts(1, 0), })); assert_ok!(Sudo::sudo(RuntimeOrigin::signed(1), call)); System::assert_has_event(TestEvent::Sudo(Event::Sudid { sudo_result: Ok(()) })); @@ -77,25 +77,25 @@ fn sudo_unchecked_weight_basics() { // A privileged function should work when `sudo` is passed the root `key` as origin. let call = Box::new(RuntimeCall::Logger(LoggerCall::privileged_i32_log { i: 42, - weight: Weight::from_ref_time(1_000), + weight: Weight::from_parts(1_000, 0), })); assert_ok!(Sudo::sudo_unchecked_weight( RuntimeOrigin::signed(1), call, - Weight::from_ref_time(1_000) + Weight::from_parts(1_000, 0) )); assert_eq!(Logger::i32_log(), vec![42i32]); // A privileged function should not work when called with a non-root `key`. let call = Box::new(RuntimeCall::Logger(LoggerCall::privileged_i32_log { i: 42, - weight: Weight::from_ref_time(1_000), + weight: Weight::from_parts(1_000, 0), })); assert_noop!( Sudo::sudo_unchecked_weight( RuntimeOrigin::signed(2), call, - Weight::from_ref_time(1_000) + Weight::from_parts(1_000, 0) ), Error::::RequireSudo, ); @@ -105,12 +105,12 @@ fn sudo_unchecked_weight_basics() { // Controls the dispatched weight. let call = Box::new(RuntimeCall::Logger(LoggerCall::privileged_i32_log { i: 42, - weight: Weight::from_ref_time(1), + weight: Weight::from_parts(1, 0), })); let sudo_unchecked_weight_call = - SudoCall::sudo_unchecked_weight { call, weight: Weight::from_ref_time(1_000) }; + SudoCall::sudo_unchecked_weight { call, weight: Weight::from_parts(1_000, 0) }; let info = sudo_unchecked_weight_call.get_dispatch_info(); - assert_eq!(info.weight, Weight::from_ref_time(1_000)); + assert_eq!(info.weight, Weight::from_parts(1_000, 0)); }); } @@ -123,12 +123,12 @@ fn sudo_unchecked_weight_emits_events_correctly() { // Should emit event to indicate success when called with the root `key` and `call` is `Ok`. let call = Box::new(RuntimeCall::Logger(LoggerCall::privileged_i32_log { i: 42, - weight: Weight::from_ref_time(1), + weight: Weight::from_parts(1, 0), })); assert_ok!(Sudo::sudo_unchecked_weight( RuntimeOrigin::signed(1), call, - Weight::from_ref_time(1_000) + Weight::from_parts(1_000, 0) )); System::assert_has_event(TestEvent::Sudo(Event::Sudid { sudo_result: Ok(()) })); }) @@ -170,7 +170,7 @@ fn sudo_as_basics() { // A privileged function will not work when passed to `sudo_as`. let call = Box::new(RuntimeCall::Logger(LoggerCall::privileged_i32_log { i: 42, - weight: Weight::from_ref_time(1_000), + weight: Weight::from_parts(1_000, 0), })); assert_ok!(Sudo::sudo_as(RuntimeOrigin::signed(1), 2, call)); assert!(Logger::i32_log().is_empty()); @@ -179,14 +179,14 @@ fn sudo_as_basics() { // A non-privileged function should not work when called with a non-root `key`. let call = Box::new(RuntimeCall::Logger(LoggerCall::non_privileged_log { i: 42, - weight: Weight::from_ref_time(1), + weight: Weight::from_parts(1, 0), })); assert_noop!(Sudo::sudo_as(RuntimeOrigin::signed(3), 2, call), Error::::RequireSudo); // A non-privileged function will work when passed to `sudo_as` with the root `key`. let call = Box::new(RuntimeCall::Logger(LoggerCall::non_privileged_log { i: 42, - weight: Weight::from_ref_time(1), + weight: Weight::from_parts(1, 0), })); assert_ok!(Sudo::sudo_as(RuntimeOrigin::signed(1), 2, call)); assert_eq!(Logger::i32_log(), vec![42i32]); @@ -204,7 +204,7 @@ fn sudo_as_emits_events_correctly() { // A non-privileged function will work when passed to `sudo_as` with the root `key`. let call = Box::new(RuntimeCall::Logger(LoggerCall::non_privileged_log { i: 42, - weight: Weight::from_ref_time(1), + weight: Weight::from_parts(1, 0), })); assert_ok!(Sudo::sudo_as(RuntimeOrigin::signed(1), 2, call)); System::assert_has_event(TestEvent::Sudo(Event::SudoAsDone { sudo_result: Ok(()) })); diff --git a/frame/support/src/dispatch.rs b/frame/support/src/dispatch.rs index c26a3d8e7..e2798763d 100644 --- a/frame/support/src/dispatch.rs +++ b/frame/support/src/dispatch.rs @@ -314,7 +314,7 @@ pub trait WithPostDispatchInfo { /// # Example /// /// ```ignore - /// let who = ensure_signed(origin).map_err(|e| e.with_weight(Weight::from_ref_time(100)))?; + /// let who = ensure_signed(origin).map_err(|e| e.with_weight(Weight::from_parts(100, 0)))?; /// ensure!(who == me, Error::::NotMe.with_weight(200_000)); /// ``` fn with_weight(self, actual_weight: Weight) -> DispatchErrorWithPostInfo; @@ -365,7 +365,7 @@ impl GetDispatchInfo fn get_dispatch_info(&self) -> DispatchInfo { // for testing: weight == size. DispatchInfo { - weight: Weight::from_ref_time(self.encode().len() as _), + weight: Weight::from_parts(self.encode().len() as _, 0), pays_fee: Pays::Yes, class: self.call.get_dispatch_info().class, } @@ -552,7 +552,7 @@ impl ClassifyDispatch for (Weight, DispatchClass, Pays) { impl From> for PostDispatchInfo { fn from(maybe_actual_computation: Option) -> Self { let actual_weight = match maybe_actual_computation { - Some(actual_computation) => Some(Weight::zero().set_ref_time(actual_computation)), + Some(actual_computation) => Some(Weight::from_parts(actual_computation, 0)), None => None, }; Self { actual_weight, pays_fee: Default::default() } @@ -563,7 +563,7 @@ impl From<(Option, Pays)> for PostDispatchInfo { fn from(post_weight_info: (Option, Pays)) -> Self { let (maybe_actual_time, pays_fee) = post_weight_info; let actual_weight = match maybe_actual_time { - Some(actual_time) => Some(Weight::zero().set_ref_time(actual_time)), + Some(actual_time) => Some(Weight::from_parts(actual_time, 0)), None => None, }; Self { actual_weight, pays_fee } @@ -584,7 +584,7 @@ impl PaysFee for u64 { impl WeighData for u64 { fn weigh_data(&self, _: T) -> Weight { - return Weight::zero().set_ref_time(*self) + return Weight::from_parts(*self, 0) } } @@ -735,7 +735,7 @@ impl PaysFee for (u64, Pays) { /// pub struct Module for enum Call where origin: T::RuntimeOrigin { /// #[weight = 1_000_000] /// fn my_long_function(origin, do_expensive_calc: bool) -> DispatchResultWithPostInfo { -/// ensure_signed(origin).map_err(|e| e.with_weight(Weight::from_ref_time(100_000)))?; +/// ensure_signed(origin).map_err(|e| e.with_weight(Weight::from_parts(100_000, 0)))?; /// if do_expensive_calc { /// // do the expensive calculation /// // ... @@ -3239,13 +3239,13 @@ mod tests { #[weight = (5, DispatchClass::Operational)] fn operational(_origin) { unreachable!() } - fn on_initialize(n: T::BlockNumber,) -> Weight { if n.into() == 42 { panic!("on_initialize") } Weight::from_ref_time(7) } + fn on_initialize(n: T::BlockNumber,) -> Weight { if n.into() == 42 { panic!("on_initialize") } Weight::from_parts(7, 0) } fn on_idle(n: T::BlockNumber, remaining_weight: Weight,) -> Weight { - if n.into() == 42 || remaining_weight == Weight::from_ref_time(42) { panic!("on_idle") } - Weight::from_ref_time(7) + if n.into() == 42 || remaining_weight == Weight::from_parts(42, 0) { panic!("on_idle") } + Weight::from_parts(7, 0) } fn on_finalize(n: T::BlockNumber,) { if n.into() == 42 { panic!("on_finalize") } } - fn on_runtime_upgrade() -> Weight { Weight::from_ref_time(10) } + fn on_runtime_upgrade() -> Weight { Weight::from_parts(10, 0) } fn offchain_worker() {} /// Some doc fn integrity_test() { panic!("integrity_test") } @@ -3423,27 +3423,27 @@ mod tests { fn on_initialize_should_work_2() { assert_eq!( as OnInitialize>::on_initialize(10), - Weight::from_ref_time(7) + Weight::from_parts(7, 0) ); } #[test] #[should_panic(expected = "on_idle")] fn on_idle_should_work_1() { - as OnIdle>::on_idle(42, Weight::from_ref_time(9)); + as OnIdle>::on_idle(42, Weight::from_parts(9, 0)); } #[test] #[should_panic(expected = "on_idle")] fn on_idle_should_work_2() { - as OnIdle>::on_idle(9, Weight::from_ref_time(42)); + as OnIdle>::on_idle(9, Weight::from_parts(42, 0)); } #[test] fn on_idle_should_work_3() { assert_eq!( - as OnIdle>::on_idle(10, Weight::from_ref_time(11)), - Weight::from_ref_time(7) + as OnIdle>::on_idle(10, Weight::from_parts(11, 0)), + Weight::from_parts(7, 0) ); } @@ -3458,7 +3458,7 @@ mod tests { sp_io::TestExternalities::default().execute_with(|| { assert_eq!( as OnRuntimeUpgrade>::on_runtime_upgrade(), - Weight::from_ref_time(10) + Weight::from_parts(10, 0) ) }); } @@ -3469,7 +3469,7 @@ mod tests { assert_eq!( Call::::operational {}.get_dispatch_info(), DispatchInfo { - weight: Weight::from_ref_time(5), + weight: Weight::from_parts(5, 0), class: DispatchClass::Operational, pays_fee: Pays::Yes }, @@ -3478,7 +3478,7 @@ mod tests { assert_eq!( Call::::aux_3 {}.get_dispatch_info(), DispatchInfo { - weight: Weight::from_ref_time(3), + weight: Weight::from_parts(3, 0), class: DispatchClass::Normal, pays_fee: Pays::Yes }, @@ -3567,10 +3567,10 @@ mod weight_tests { #[weight = (0, DispatchClass::Operational, Pays::Yes)] fn f12(_origin, _a: u32, _eb: u32) { unimplemented!(); } - #[weight = T::DbWeight::get().reads(3) + T::DbWeight::get().writes(2) + Weight::from_ref_time(10_000)] + #[weight = T::DbWeight::get().reads(3) + T::DbWeight::get().writes(2) + Weight::from_parts(10_000, 0)] fn f20(_origin) { unimplemented!(); } - #[weight = T::DbWeight::get().reads_writes(6, 5) + Weight::from_ref_time(40_000)] + #[weight = T::DbWeight::get().reads_writes(6, 5) + Weight::from_parts(40_000, 0)] fn f21(_origin) { unimplemented!(); } } @@ -3580,96 +3580,96 @@ mod weight_tests { fn weights_are_correct() { // #[weight = 1000] let info = Call::::f00 {}.get_dispatch_info(); - assert_eq!(info.weight, Weight::from_ref_time(1000)); + assert_eq!(info.weight, Weight::from_parts(1000, 0)); assert_eq!(info.class, DispatchClass::Normal); assert_eq!(info.pays_fee, Pays::Yes); // #[weight = (1000, DispatchClass::Mandatory)] let info = Call::::f01 {}.get_dispatch_info(); - assert_eq!(info.weight, Weight::from_ref_time(1000)); + assert_eq!(info.weight, Weight::from_parts(1000, 0)); assert_eq!(info.class, DispatchClass::Mandatory); assert_eq!(info.pays_fee, Pays::Yes); // #[weight = (1000, Pays::No)] let info = Call::::f02 {}.get_dispatch_info(); - assert_eq!(info.weight, Weight::from_ref_time(1000)); + assert_eq!(info.weight, Weight::from_parts(1000, 0)); assert_eq!(info.class, DispatchClass::Normal); assert_eq!(info.pays_fee, Pays::No); // #[weight = (1000, DispatchClass::Operational, Pays::No)] let info = Call::::f03 {}.get_dispatch_info(); - assert_eq!(info.weight, Weight::from_ref_time(1000)); + assert_eq!(info.weight, Weight::from_parts(1000, 0)); assert_eq!(info.class, DispatchClass::Operational); assert_eq!(info.pays_fee, Pays::No); // #[weight = ((_a * 10 + _eb * 1) as Weight, DispatchClass::Normal, Pays::Yes)] let info = Call::::f11 { _a: 13, _eb: 20 }.get_dispatch_info(); - assert_eq!(info.weight, Weight::from_ref_time(150)); // 13*10 + 20 + assert_eq!(info.weight, Weight::from_parts(150, 0)); // 13*10 + 20 assert_eq!(info.class, DispatchClass::Normal); assert_eq!(info.pays_fee, Pays::Yes); // #[weight = (0, DispatchClass::Operational, Pays::Yes)] let info = Call::::f12 { _a: 10, _eb: 20 }.get_dispatch_info(); - assert_eq!(info.weight, Weight::from_ref_time(0)); + assert_eq!(info.weight, Weight::from_parts(0, 0)); assert_eq!(info.class, DispatchClass::Operational); assert_eq!(info.pays_fee, Pays::Yes); // #[weight = T::DbWeight::get().reads(3) + T::DbWeight::get().writes(2) + 10_000] let info = Call::::f20 {}.get_dispatch_info(); - assert_eq!(info.weight, Weight::from_ref_time(12300)); // 100*3 + 1000*2 + 10_1000 + assert_eq!(info.weight, Weight::from_parts(12300, 0)); // 100*3 + 1000*2 + 10_1000 assert_eq!(info.class, DispatchClass::Normal); assert_eq!(info.pays_fee, Pays::Yes); // #[weight = T::DbWeight::get().reads_writes(6, 5) + 40_000] let info = Call::::f21 {}.get_dispatch_info(); - assert_eq!(info.weight, Weight::from_ref_time(45600)); // 100*6 + 1000*5 + 40_1000 + assert_eq!(info.weight, Weight::from_parts(45600, 0)); // 100*6 + 1000*5 + 40_1000 assert_eq!(info.class, DispatchClass::Normal); assert_eq!(info.pays_fee, Pays::Yes); } #[test] fn extract_actual_weight_works() { - let pre = DispatchInfo { weight: Weight::from_ref_time(1000), ..Default::default() }; - assert_eq!(extract_actual_weight(&Ok(Some(7).into()), &pre), Weight::from_ref_time(7)); + let pre = DispatchInfo { weight: Weight::from_parts(1000, 0), ..Default::default() }; + assert_eq!(extract_actual_weight(&Ok(Some(7).into()), &pre), Weight::from_parts(7, 0)); assert_eq!( extract_actual_weight(&Ok(Some(1000).into()), &pre), - Weight::from_ref_time(1000) + Weight::from_parts(1000, 0) ); assert_eq!( extract_actual_weight( - &Err(DispatchError::BadOrigin.with_weight(Weight::from_ref_time(9))), + &Err(DispatchError::BadOrigin.with_weight(Weight::from_parts(9, 0))), &pre ), - Weight::from_ref_time(9) + Weight::from_parts(9, 0) ); } #[test] fn extract_actual_weight_caps_at_pre_weight() { - let pre = DispatchInfo { weight: Weight::from_ref_time(1000), ..Default::default() }; + let pre = DispatchInfo { weight: Weight::from_parts(1000, 0), ..Default::default() }; assert_eq!( extract_actual_weight(&Ok(Some(1250).into()), &pre), - Weight::from_ref_time(1000) + Weight::from_parts(1000, 0) ); assert_eq!( extract_actual_weight( - &Err(DispatchError::BadOrigin.with_weight(Weight::from_ref_time(1300))), + &Err(DispatchError::BadOrigin.with_weight(Weight::from_parts(1300, 0))), &pre ), - Weight::from_ref_time(1000), + Weight::from_parts(1000, 0), ); } #[test] fn extract_actual_pays_fee_works() { - let pre = DispatchInfo { weight: Weight::from_ref_time(1000), ..Default::default() }; + let pre = DispatchInfo { weight: Weight::from_parts(1000, 0), ..Default::default() }; assert_eq!(extract_actual_pays_fee(&Ok(Some(7).into()), &pre), Pays::Yes); assert_eq!(extract_actual_pays_fee(&Ok(Some(1000).into()), &pre), Pays::Yes); assert_eq!(extract_actual_pays_fee(&Ok((Some(1000), Pays::Yes).into()), &pre), Pays::Yes); assert_eq!(extract_actual_pays_fee(&Ok((Some(1000), Pays::No).into()), &pre), Pays::No); assert_eq!( extract_actual_pays_fee( - &Err(DispatchError::BadOrigin.with_weight(Weight::from_ref_time(9))), + &Err(DispatchError::BadOrigin.with_weight(Weight::from_parts(9, 0))), &pre ), Pays::Yes @@ -3686,7 +3686,7 @@ mod weight_tests { ); let pre = DispatchInfo { - weight: Weight::from_ref_time(1000), + weight: Weight::from_parts(1000, 0), pays_fee: Pays::No, ..Default::default() }; diff --git a/frame/support/src/traits/hooks.rs b/frame/support/src/traits/hooks.rs index fe67e33d1..a3374d0bf 100644 --- a/frame/support/src/traits/hooks.rs +++ b/frame/support/src/traits/hooks.rs @@ -367,18 +367,18 @@ mod tests { impl OnInitialize for Test { fn on_initialize(_n: u8) -> Weight { - Weight::from_ref_time(10) + Weight::from_parts(10, 0) } } impl OnRuntimeUpgrade for Test { fn on_runtime_upgrade() -> Weight { - Weight::from_ref_time(20) + Weight::from_parts(20, 0) } } TestExternalities::default().execute_with(|| { - assert_eq!(<(Test, Test)>::on_initialize(0), Weight::from_ref_time(20)); - assert_eq!(<(Test, Test)>::on_runtime_upgrade(), Weight::from_ref_time(40)); + assert_eq!(<(Test, Test)>::on_initialize(0), Weight::from_parts(20, 0)); + assert_eq!(<(Test, Test)>::on_runtime_upgrade(), Weight::from_parts(40, 0)); }); } diff --git a/frame/support/src/weights/block_weights.rs b/frame/support/src/weights/block_weights.rs index a208c77aa..2389c51d9 100644 --- a/frame/support/src/weights/block_weights.rs +++ b/frame/support/src/weights/block_weights.rs @@ -54,7 +54,7 @@ parameter_types! { /// 95th: 384_876 /// 75th: 380_642 pub const BlockExecutionWeight: Weight = - Weight::from_ref_time(WEIGHT_REF_TIME_PER_NANOS.saturating_mul(381_015)); + Weight::from_parts(WEIGHT_REF_TIME_PER_NANOS.saturating_mul(381_015), 0); } #[cfg(test)] diff --git a/frame/support/src/weights/extrinsic_weights.rs b/frame/support/src/weights/extrinsic_weights.rs index 4e3a0fda8..78798d2e3 100644 --- a/frame/support/src/weights/extrinsic_weights.rs +++ b/frame/support/src/weights/extrinsic_weights.rs @@ -54,7 +54,7 @@ parameter_types! { /// 95th: 100_051 /// 75th: 99_916 pub const ExtrinsicBaseWeight: Weight = - Weight::from_ref_time(WEIGHT_REF_TIME_PER_NANOS.saturating_mul(99_840)); + Weight::from_parts(WEIGHT_REF_TIME_PER_NANOS.saturating_mul(99_840), 0); } #[cfg(test)] diff --git a/frame/support/test/tests/construct_runtime.rs b/frame/support/test/tests/construct_runtime.rs index 096557714..24f8a278b 100644 --- a/frame/support/test/tests/construct_runtime.rs +++ b/frame/support/test/tests/construct_runtime.rs @@ -508,7 +508,7 @@ fn call_weight_should_attach_to_call_enum() { assert_eq!( module3::Call::::operational {}.get_dispatch_info(), DispatchInfo { - weight: Weight::from_ref_time(5), + weight: Weight::from_parts(5, 0), class: DispatchClass::Operational, pays_fee: Pays::Yes }, @@ -517,7 +517,7 @@ fn call_weight_should_attach_to_call_enum() { assert_eq!( module3::Call::::aux_4 {}.get_dispatch_info(), DispatchInfo { - weight: Weight::from_ref_time(3), + weight: Weight::from_parts(3, 0), class: DispatchClass::Normal, pays_fee: Pays::Yes }, diff --git a/frame/support/test/tests/pallet.rs b/frame/support/test/tests/pallet.rs index 8ce85fa50..2a4732d02 100644 --- a/frame/support/test/tests/pallet.rs +++ b/frame/support/test/tests/pallet.rs @@ -171,7 +171,7 @@ pub mod pallet { let _ = T::AccountId::from(SomeType1); // Test for where clause let _ = T::AccountId::from(SomeType2); // Test for where clause Self::deposit_event(Event::Something(10)); - Weight::from_ref_time(10) + Weight::from_parts(10, 0) } fn on_finalize(_: BlockNumberFor) { let _ = T::AccountId::from(SomeType1); // Test for where clause @@ -182,7 +182,7 @@ pub mod pallet { let _ = T::AccountId::from(SomeType1); // Test for where clause let _ = T::AccountId::from(SomeType2); // Test for where clause Self::deposit_event(Event::Something(30)); - Weight::from_ref_time(30) + Weight::from_parts(30, 0) } fn integrity_test() { let _ = T::AccountId::from(SomeType1); // Test for where clause @@ -197,7 +197,7 @@ pub mod pallet { { /// Doc comment put in metadata #[pallet::call_index(0)] - #[pallet::weight(Weight::from_ref_time(*_foo as u64))] + #[pallet::weight(Weight::from_parts(*_foo as u64, 0))] pub fn foo( origin: OriginFor, #[pallet::compact] _foo: u32, @@ -715,7 +715,7 @@ fn call_expand() { assert_eq!( call_foo.get_dispatch_info(), DispatchInfo { - weight: frame_support::weights::Weight::from_ref_time(3), + weight: frame_support::weights::Weight::from_parts(3, 0), class: DispatchClass::Normal, pays_fee: Pays::Yes } @@ -1096,10 +1096,10 @@ fn pallet_hooks_expand() { TestExternalities::default().execute_with(|| { frame_system::Pallet::::set_block_number(1); - assert_eq!(AllPalletsWithoutSystem::on_initialize(1), Weight::from_ref_time(10)); + assert_eq!(AllPalletsWithoutSystem::on_initialize(1), Weight::from_parts(10, 0)); AllPalletsWithoutSystem::on_finalize(1); - assert_eq!(AllPalletsWithoutSystem::on_runtime_upgrade(), Weight::from_ref_time(30)); + assert_eq!(AllPalletsWithoutSystem::on_runtime_upgrade(), Weight::from_parts(30, 0)); assert_eq!( frame_system::Pallet::::events()[0].event, @@ -1137,13 +1137,13 @@ fn all_pallets_type_reversed_order_is_correct() { { assert_eq!( AllPalletsWithoutSystemReversed::on_initialize(1), - Weight::from_ref_time(10) + Weight::from_parts(10, 0) ); AllPalletsWithoutSystemReversed::on_finalize(1); assert_eq!( AllPalletsWithoutSystemReversed::on_runtime_upgrade(), - Weight::from_ref_time(30) + Weight::from_parts(30, 0) ); } @@ -1219,7 +1219,7 @@ fn migrate_from_pallet_version_to_storage_version() { }; // `pallet_num` pallets, 2 writes and every write costs 5 weight. - assert_eq!(Weight::from_ref_time(pallet_num * 2 * 5), weight); + assert_eq!(Weight::from_parts(pallet_num * 2 * 5, 0), weight); // All pallet versions should be removed assert!(sp_io::storage::get(&pallet_version_key(Example::name())).is_none()); diff --git a/frame/support/test/tests/pallet_compatibility.rs b/frame/support/test/tests/pallet_compatibility.rs index 8b0573398..bd11d0da3 100644 --- a/frame/support/test/tests/pallet_compatibility.rs +++ b/frame/support/test/tests/pallet_compatibility.rs @@ -85,7 +85,7 @@ mod pallet_old { fn on_initialize(_n: T::BlockNumber) -> Weight { >::put(T::Balance::from(10)); - Weight::from_ref_time(10) + Weight::from_parts(10, 0) } fn on_finalize(_n: T::BlockNumber) { @@ -131,7 +131,7 @@ pub mod pallet { impl Hooks for Pallet { fn on_initialize(_n: T::BlockNumber) -> Weight { >::put(T::Balance::from(10)); - Weight::from_ref_time(10) + Weight::from_parts(10, 0) } fn on_finalize(_n: T::BlockNumber) { diff --git a/frame/support/test/tests/pallet_compatibility_instance.rs b/frame/support/test/tests/pallet_compatibility_instance.rs index 5cc1c1f44..1d2705b12 100644 --- a/frame/support/test/tests/pallet_compatibility_instance.rs +++ b/frame/support/test/tests/pallet_compatibility_instance.rs @@ -72,7 +72,7 @@ mod pallet_old { fn on_initialize(_n: T::BlockNumber) -> Weight { >::put(T::Balance::from(10)); - Weight::from_ref_time(10) + Weight::from_parts(10, 0) } fn on_finalize(_n: T::BlockNumber) { @@ -117,7 +117,7 @@ pub mod pallet { impl, I: 'static> Hooks for Pallet { fn on_initialize(_n: T::BlockNumber) -> Weight { >::put(T::Balance::from(10)); - Weight::from_ref_time(10) + Weight::from_parts(10, 0) } fn on_finalize(_n: T::BlockNumber) { diff --git a/frame/support/test/tests/pallet_instance.rs b/frame/support/test/tests/pallet_instance.rs index ea1e27fe9..6dc73a6d4 100644 --- a/frame/support/test/tests/pallet_instance.rs +++ b/frame/support/test/tests/pallet_instance.rs @@ -54,10 +54,10 @@ pub mod pallet { fn on_initialize(_: BlockNumberFor) -> Weight { if TypeId::of::() == TypeId::of::<()>() { Self::deposit_event(Event::Something(10)); - Weight::from_ref_time(10) + Weight::from_parts(10, 0) } else { Self::deposit_event(Event::Something(11)); - Weight::from_ref_time(11) + Weight::from_parts(11, 0) } } fn on_finalize(_: BlockNumberFor) { @@ -70,10 +70,10 @@ pub mod pallet { fn on_runtime_upgrade() -> Weight { if TypeId::of::() == TypeId::of::<()>() { Self::deposit_event(Event::Something(30)); - Weight::from_ref_time(30) + Weight::from_parts(30, 0) } else { Self::deposit_event(Event::Something(31)); - Weight::from_ref_time(31) + Weight::from_parts(31, 0) } } fn integrity_test() {} @@ -83,7 +83,7 @@ pub mod pallet { impl, I: 'static> Pallet { /// Doc comment put in metadata #[pallet::call_index(0)] - #[pallet::weight(Weight::from_ref_time(*_foo as u64))] + #[pallet::weight(Weight::from_parts(*_foo as u64, 0))] pub fn foo( origin: OriginFor, #[pallet::compact] _foo: u32, @@ -356,7 +356,7 @@ fn call_expand() { assert_eq!( call_foo.get_dispatch_info(), DispatchInfo { - weight: Weight::from_ref_time(3), + weight: Weight::from_parts(3, 0), class: DispatchClass::Normal, pays_fee: Pays::Yes } @@ -368,7 +368,7 @@ fn call_expand() { assert_eq!( call_foo.get_dispatch_info(), DispatchInfo { - weight: Weight::from_ref_time(3), + weight: Weight::from_parts(3, 0), class: DispatchClass::Normal, pays_fee: Pays::Yes } @@ -649,10 +649,10 @@ fn pallet_hooks_expand() { TestExternalities::default().execute_with(|| { frame_system::Pallet::::set_block_number(1); - assert_eq!(AllPalletsWithoutSystem::on_initialize(1), Weight::from_ref_time(21)); + assert_eq!(AllPalletsWithoutSystem::on_initialize(1), Weight::from_parts(21, 0)); AllPalletsWithoutSystem::on_finalize(1); - assert_eq!(AllPalletsWithoutSystem::on_runtime_upgrade(), Weight::from_ref_time(61)); + assert_eq!(AllPalletsWithoutSystem::on_runtime_upgrade(), Weight::from_parts(61, 0)); assert_eq!( frame_system::Pallet::::events()[0].event, diff --git a/frame/system/src/extensions/check_mortality.rs b/frame/system/src/extensions/check_mortality.rs index 739a27a2a..23c357d48 100644 --- a/frame/system/src/extensions/check_mortality.rs +++ b/frame/system/src/extensions/check_mortality.rs @@ -130,7 +130,7 @@ mod tests { fn signed_ext_check_era_should_change_longevity() { new_test_ext().execute_with(|| { let normal = DispatchInfo { - weight: Weight::from_ref_time(100), + weight: Weight::from_parts(100, 0), class: DispatchClass::Normal, pays_fee: Pays::Yes, }; diff --git a/frame/system/src/extensions/check_weight.rs b/frame/system/src/extensions/check_weight.rs index ca5f7a82c..1030c8daf 100644 --- a/frame/system/src/extensions/check_weight.rs +++ b/frame/system/src/extensions/check_weight.rs @@ -308,7 +308,7 @@ mod tests { new_test_ext().execute_with(|| { let max = DispatchInfo { weight: block_weights().get(DispatchClass::Normal).max_extrinsic.unwrap() + - Weight::from_ref_time(1), + Weight::from_parts(1, 0), class: DispatchClass::Normal, ..Default::default() }; @@ -334,7 +334,7 @@ mod tests { let okay = DispatchInfo { weight, class: DispatchClass::Operational, ..Default::default() }; let max = DispatchInfo { - weight: weight + Weight::from_ref_time(1), + weight: weight + Weight::from_parts(1, 0), class: DispatchClass::Operational, ..Default::default() }; @@ -366,9 +366,9 @@ mod tests { // So normal extrinsic can be 758 weight (-5 for base extrinsic weight) // And Operational can be 246 to produce a full block (-10 for base) let max_normal = - DispatchInfo { weight: Weight::from_ref_time(753), ..Default::default() }; + DispatchInfo { weight: Weight::from_parts(753, 0), ..Default::default() }; let rest_operational = DispatchInfo { - weight: Weight::from_ref_time(246), + weight: Weight::from_parts(246, 0), class: DispatchClass::Operational, ..Default::default() }; @@ -376,9 +376,9 @@ mod tests { let len = 0_usize; assert_ok!(CheckWeight::::do_pre_dispatch(&max_normal, len)); - assert_eq!(System::block_weight().total(), Weight::from_ref_time(768)); + assert_eq!(System::block_weight().total(), Weight::from_parts(768, 0)); assert_ok!(CheckWeight::::do_pre_dispatch(&rest_operational, len)); - assert_eq!(block_weight_limit(), Weight::from_ref_time(1024).set_proof_size(u64::MAX)); + assert_eq!(block_weight_limit(), Weight::from_parts(1024, u64::MAX)); assert_eq!(System::block_weight().total(), block_weight_limit().set_proof_size(0)); // Checking single extrinsic should not take current block weight into account. assert_eq!(CheckWeight::::check_extrinsic_weight(&rest_operational), Ok(())); @@ -390,9 +390,9 @@ mod tests { new_test_ext().execute_with(|| { // We switch the order of `full_block_with_normal_and_operational` let max_normal = - DispatchInfo { weight: Weight::from_ref_time(753), ..Default::default() }; + DispatchInfo { weight: Weight::from_parts(753, 0), ..Default::default() }; let rest_operational = DispatchInfo { - weight: Weight::from_ref_time(246), + weight: Weight::from_parts(246, 0), class: DispatchClass::Operational, ..Default::default() }; @@ -401,9 +401,9 @@ mod tests { assert_ok!(CheckWeight::::do_pre_dispatch(&rest_operational, len)); // Extra 20 here from block execution + base extrinsic weight - assert_eq!(System::block_weight().total(), Weight::from_ref_time(266)); + assert_eq!(System::block_weight().total(), Weight::from_parts(266, 0)); assert_ok!(CheckWeight::::do_pre_dispatch(&max_normal, len)); - assert_eq!(block_weight_limit(), Weight::from_ref_time(1024).set_proof_size(u64::MAX)); + assert_eq!(block_weight_limit(), Weight::from_parts(1024, u64::MAX)); assert_eq!(System::block_weight().total(), block_weight_limit().set_proof_size(0)); }); } @@ -414,12 +414,12 @@ mod tests { // An on_initialize takes up the whole block! (Every time!) System::register_extra_weight_unchecked(Weight::MAX, DispatchClass::Mandatory); let dispatch_normal = DispatchInfo { - weight: Weight::from_ref_time(251), + weight: Weight::from_parts(251, 0), class: DispatchClass::Normal, ..Default::default() }; let dispatch_operational = DispatchInfo { - weight: Weight::from_ref_time(246), + weight: Weight::from_parts(246, 0), class: DispatchClass::Operational, ..Default::default() }; @@ -445,9 +445,9 @@ mod tests { #[test] fn signed_ext_check_weight_works_operational_tx() { new_test_ext().execute_with(|| { - let normal = DispatchInfo { weight: Weight::from_ref_time(100), ..Default::default() }; + let normal = DispatchInfo { weight: Weight::from_parts(100, 0), ..Default::default() }; let op = DispatchInfo { - weight: Weight::from_ref_time(100), + weight: Weight::from_parts(100, 0), class: DispatchClass::Operational, pays_fee: Pays::Yes, }; @@ -513,12 +513,12 @@ mod tests { fn signed_ext_check_weight_works_normal_tx() { new_test_ext().execute_with(|| { let normal_limit = normal_weight_limit(); - let small = DispatchInfo { weight: Weight::from_ref_time(100), ..Default::default() }; + let small = DispatchInfo { weight: Weight::from_parts(100, 0), ..Default::default() }; let base_extrinsic = block_weights().get(DispatchClass::Normal).base_extrinsic; let medium = DispatchInfo { weight: normal_limit - base_extrinsic, ..Default::default() }; let big = DispatchInfo { - weight: normal_limit - base_extrinsic + Weight::from_ref_time(1), + weight: normal_limit - base_extrinsic + Weight::from_parts(1, 0), ..Default::default() }; let len = 0_usize; @@ -537,7 +537,7 @@ mod tests { reset_check_weight(&small, false, Weight::zero()); reset_check_weight(&medium, false, Weight::zero()); - reset_check_weight(&big, true, Weight::from_ref_time(1)); + reset_check_weight(&big, true, Weight::from_parts(1, 0)); }) } @@ -545,9 +545,9 @@ mod tests { fn signed_ext_check_weight_refund_works() { new_test_ext().execute_with(|| { // This is half of the max block weight - let info = DispatchInfo { weight: Weight::from_ref_time(512), ..Default::default() }; + let info = DispatchInfo { weight: Weight::from_parts(512, 0), ..Default::default() }; let post_info = PostDispatchInfo { - actual_weight: Some(Weight::from_ref_time(128)), + actual_weight: Some(Weight::from_parts(128, 0)), pays_fee: Default::default(), }; let len = 0_usize; @@ -557,13 +557,13 @@ mod tests { BlockWeight::::mutate(|current_weight| { current_weight.set(Weight::zero(), DispatchClass::Mandatory); current_weight - .set(Weight::from_ref_time(256) - base_extrinsic, DispatchClass::Normal); + .set(Weight::from_parts(256, 0) - base_extrinsic, DispatchClass::Normal); }); let pre = CheckWeight::(PhantomData).pre_dispatch(&1, CALL, &info, len).unwrap(); assert_eq!( BlockWeight::::get().total(), - info.weight + Weight::from_ref_time(256) + info.weight + Weight::from_parts(256, 0) ); assert_ok!(CheckWeight::::post_dispatch( @@ -575,7 +575,7 @@ mod tests { )); assert_eq!( BlockWeight::::get().total(), - post_info.actual_weight.unwrap() + Weight::from_ref_time(256) + post_info.actual_weight.unwrap() + Weight::from_parts(256, 0) ); }) } @@ -583,23 +583,23 @@ mod tests { #[test] fn signed_ext_check_weight_actual_weight_higher_than_max_is_capped() { new_test_ext().execute_with(|| { - let info = DispatchInfo { weight: Weight::from_ref_time(512), ..Default::default() }; + let info = DispatchInfo { weight: Weight::from_parts(512, 0), ..Default::default() }; let post_info = PostDispatchInfo { - actual_weight: Some(Weight::from_ref_time(700)), + actual_weight: Some(Weight::from_parts(700, 0)), pays_fee: Default::default(), }; let len = 0_usize; BlockWeight::::mutate(|current_weight| { current_weight.set(Weight::zero(), DispatchClass::Mandatory); - current_weight.set(Weight::from_ref_time(128), DispatchClass::Normal); + current_weight.set(Weight::from_parts(128, 0), DispatchClass::Normal); }); let pre = CheckWeight::(PhantomData).pre_dispatch(&1, CALL, &info, len).unwrap(); assert_eq!( BlockWeight::::get().total(), info.weight + - Weight::from_ref_time(128) + + Weight::from_parts(128, 0) + block_weights().get(DispatchClass::Normal).base_extrinsic, ); @@ -613,7 +613,7 @@ mod tests { assert_eq!( BlockWeight::::get().total(), info.weight + - Weight::from_ref_time(128) + + Weight::from_parts(128, 0) + block_weights().get(DispatchClass::Normal).base_extrinsic, ); }) @@ -643,9 +643,9 @@ mod tests { // Max normal is 768 (75%) // Max mandatory is unlimited let max_normal = - DispatchInfo { weight: Weight::from_ref_time(753), ..Default::default() }; + DispatchInfo { weight: Weight::from_parts(753, 0), ..Default::default() }; let mandatory = DispatchInfo { - weight: Weight::from_ref_time(1019), + weight: Weight::from_parts(1019, 0), class: DispatchClass::Mandatory, ..Default::default() }; @@ -653,10 +653,10 @@ mod tests { let len = 0_usize; assert_ok!(CheckWeight::::do_pre_dispatch(&max_normal, len)); - assert_eq!(System::block_weight().total(), Weight::from_ref_time(768)); + assert_eq!(System::block_weight().total(), Weight::from_parts(768, 0)); assert_ok!(CheckWeight::::do_pre_dispatch(&mandatory, len)); - assert_eq!(block_weight_limit(), Weight::from_ref_time(1024).set_proof_size(u64::MAX)); - assert_eq!(System::block_weight().total(), Weight::from_ref_time(1024 + 768)); + assert_eq!(block_weight_limit(), Weight::from_parts(1024, u64::MAX)); + assert_eq!(System::block_weight().total(), Weight::from_parts(1024 + 768, 0)); assert_eq!(CheckWeight::::check_extrinsic_weight(&mandatory), Ok(())); }); } @@ -668,30 +668,30 @@ mod tests { .base_block(Weight::zero()) .for_class(DispatchClass::non_mandatory(), |w| { w.base_extrinsic = Weight::zero(); - w.max_total = Some(Weight::from_ref_time(20).set_proof_size(u64::MAX)); + w.max_total = Some(Weight::from_parts(20, u64::MAX)); }) .for_class(DispatchClass::Mandatory, |w| { w.base_extrinsic = Weight::zero(); - w.reserved = Some(Weight::from_ref_time(5).set_proof_size(u64::MAX)); + w.reserved = Some(Weight::from_parts(5, u64::MAX)); w.max_total = None; }) .build_or_panic(); let all_weight = crate::ConsumedWeight::new(|class| match class { - DispatchClass::Normal => Weight::from_ref_time(10), - DispatchClass::Operational => Weight::from_ref_time(10), + DispatchClass::Normal => Weight::from_parts(10, 0), + DispatchClass::Operational => Weight::from_parts(10, 0), DispatchClass::Mandatory => Weight::zero(), }); assert_eq!(maximum_weight.max_block, all_weight.total().set_proof_size(u64::MAX)); // fits into reserved let mandatory1 = DispatchInfo { - weight: Weight::from_ref_time(5), + weight: Weight::from_parts(5, 0), class: DispatchClass::Mandatory, ..Default::default() }; // does not fit into reserved and the block is full. let mandatory2 = DispatchInfo { - weight: Weight::from_ref_time(6), + weight: Weight::from_parts(6, 0), class: DispatchClass::Mandatory, ..Default::default() }; diff --git a/frame/system/src/mock.rs b/frame/system/src/mock.rs index a3807512d..83e12dcca 100644 --- a/frame/system/src/mock.rs +++ b/frame/system/src/mock.rs @@ -41,7 +41,7 @@ frame_support::construct_runtime!( ); const NORMAL_DISPATCH_RATIO: Perbill = Perbill::from_percent(75); -const MAX_BLOCK_WEIGHT: Weight = Weight::from_ref_time(1024).set_proof_size(u64::MAX); +const MAX_BLOCK_WEIGHT: Weight = Weight::from_parts(1024, u64::MAX); parameter_types! { pub Version: RuntimeVersion = RuntimeVersion { @@ -59,15 +59,15 @@ parameter_types! { write: 100, }; pub RuntimeBlockWeights: limits::BlockWeights = limits::BlockWeights::builder() - .base_block(Weight::from_ref_time(10)) + .base_block(Weight::from_parts(10, 0)) .for_class(DispatchClass::all(), |weights| { - weights.base_extrinsic = Weight::from_ref_time(5); + weights.base_extrinsic = Weight::from_parts(5, 0); }) .for_class(DispatchClass::Normal, |weights| { weights.max_total = Some(NORMAL_DISPATCH_RATIO * MAX_BLOCK_WEIGHT); }) .for_class(DispatchClass::Operational, |weights| { - weights.base_extrinsic = Weight::from_ref_time(10); + weights.base_extrinsic = Weight::from_parts(10, 0); weights.max_total = Some(MAX_BLOCK_WEIGHT); weights.reserved = Some( MAX_BLOCK_WEIGHT - NORMAL_DISPATCH_RATIO * MAX_BLOCK_WEIGHT diff --git a/frame/system/src/tests.rs b/frame/system/src/tests.rs index 3e24d8f3c..ebb28ca87 100644 --- a/frame/system/src/tests.rs +++ b/frame/system/src/tests.rs @@ -233,7 +233,7 @@ fn deposit_event_uses_actual_weight_and_pays_fee() { let normal_base = ::BlockWeights::get() .get(DispatchClass::Normal) .base_extrinsic; - let pre_info = DispatchInfo { weight: Weight::from_ref_time(1000), ..Default::default() }; + let pre_info = DispatchInfo { weight: Weight::from_parts(1000, 0), ..Default::default() }; System::note_applied_extrinsic(&Ok(Some(300).into()), pre_info); System::note_applied_extrinsic(&Ok(Some(1000).into()), pre_info); System::note_applied_extrinsic( @@ -246,7 +246,7 @@ fn deposit_event_uses_actual_weight_and_pays_fee() { System::note_applied_extrinsic(&Ok((Some(2_500_000), Pays::No).into()), pre_info); System::note_applied_extrinsic(&Ok((Some(500), Pays::No).into()), pre_info); System::note_applied_extrinsic( - &Err(DispatchError::BadOrigin.with_weight(Weight::from_ref_time(999))), + &Err(DispatchError::BadOrigin.with_weight(Weight::from_parts(999, 0))), pre_info, ); @@ -260,7 +260,7 @@ fn deposit_event_uses_actual_weight_and_pays_fee() { System::note_applied_extrinsic( &Err(DispatchErrorWithPostInfo { post_info: PostDispatchInfo { - actual_weight: Some(Weight::from_ref_time(800)), + actual_weight: Some(Weight::from_parts(800, 0)), pays_fee: Pays::Yes, }, error: DispatchError::BadOrigin, @@ -270,7 +270,7 @@ fn deposit_event_uses_actual_weight_and_pays_fee() { System::note_applied_extrinsic( &Err(DispatchErrorWithPostInfo { post_info: PostDispatchInfo { - actual_weight: Some(Weight::from_ref_time(800)), + actual_weight: Some(Weight::from_parts(800, 0)), pays_fee: Pays::No, }, error: DispatchError::BadOrigin, @@ -283,7 +283,7 @@ fn deposit_event_uses_actual_weight_and_pays_fee() { .base_extrinsic; assert!(normal_base != operational_base, "Test pre-condition violated"); let pre_info = DispatchInfo { - weight: Weight::from_ref_time(1000), + weight: Weight::from_parts(1000, 0), class: DispatchClass::Operational, ..Default::default() }; @@ -295,7 +295,7 @@ fn deposit_event_uses_actual_weight_and_pays_fee() { phase: Phase::ApplyExtrinsic(0), event: SysEvent::ExtrinsicSuccess { dispatch_info: DispatchInfo { - weight: Weight::from_ref_time(300).saturating_add(normal_base), + weight: Weight::from_parts(300, 0).saturating_add(normal_base), ..Default::default() }, } @@ -306,7 +306,7 @@ fn deposit_event_uses_actual_weight_and_pays_fee() { phase: Phase::ApplyExtrinsic(1), event: SysEvent::ExtrinsicSuccess { dispatch_info: DispatchInfo { - weight: Weight::from_ref_time(1000).saturating_add(normal_base), + weight: Weight::from_parts(1000, 0).saturating_add(normal_base), ..Default::default() }, } @@ -317,7 +317,7 @@ fn deposit_event_uses_actual_weight_and_pays_fee() { phase: Phase::ApplyExtrinsic(2), event: SysEvent::ExtrinsicSuccess { dispatch_info: DispatchInfo { - weight: Weight::from_ref_time(1000).saturating_add(normal_base), + weight: Weight::from_parts(1000, 0).saturating_add(normal_base), ..Default::default() }, } @@ -328,7 +328,7 @@ fn deposit_event_uses_actual_weight_and_pays_fee() { phase: Phase::ApplyExtrinsic(3), event: SysEvent::ExtrinsicSuccess { dispatch_info: DispatchInfo { - weight: Weight::from_ref_time(1000).saturating_add(normal_base), + weight: Weight::from_parts(1000, 0).saturating_add(normal_base), pays_fee: Pays::Yes, ..Default::default() }, @@ -340,7 +340,7 @@ fn deposit_event_uses_actual_weight_and_pays_fee() { phase: Phase::ApplyExtrinsic(4), event: SysEvent::ExtrinsicSuccess { dispatch_info: DispatchInfo { - weight: Weight::from_ref_time(1000).saturating_add(normal_base), + weight: Weight::from_parts(1000, 0).saturating_add(normal_base), pays_fee: Pays::No, ..Default::default() }, @@ -352,7 +352,7 @@ fn deposit_event_uses_actual_weight_and_pays_fee() { phase: Phase::ApplyExtrinsic(5), event: SysEvent::ExtrinsicSuccess { dispatch_info: DispatchInfo { - weight: Weight::from_ref_time(1000).saturating_add(normal_base), + weight: Weight::from_parts(1000, 0).saturating_add(normal_base), pays_fee: Pays::No, ..Default::default() }, @@ -364,7 +364,7 @@ fn deposit_event_uses_actual_weight_and_pays_fee() { phase: Phase::ApplyExtrinsic(6), event: SysEvent::ExtrinsicSuccess { dispatch_info: DispatchInfo { - weight: Weight::from_ref_time(500).saturating_add(normal_base), + weight: Weight::from_parts(500, 0).saturating_add(normal_base), pays_fee: Pays::No, ..Default::default() }, @@ -377,7 +377,7 @@ fn deposit_event_uses_actual_weight_and_pays_fee() { event: SysEvent::ExtrinsicFailed { dispatch_error: DispatchError::BadOrigin.into(), dispatch_info: DispatchInfo { - weight: Weight::from_ref_time(999).saturating_add(normal_base), + weight: Weight::from_parts(999, 0).saturating_add(normal_base), ..Default::default() }, } @@ -389,7 +389,7 @@ fn deposit_event_uses_actual_weight_and_pays_fee() { event: SysEvent::ExtrinsicFailed { dispatch_error: DispatchError::BadOrigin.into(), dispatch_info: DispatchInfo { - weight: Weight::from_ref_time(1000).saturating_add(normal_base), + weight: Weight::from_parts(1000, 0).saturating_add(normal_base), pays_fee: Pays::Yes, ..Default::default() }, @@ -402,7 +402,7 @@ fn deposit_event_uses_actual_weight_and_pays_fee() { event: SysEvent::ExtrinsicFailed { dispatch_error: DispatchError::BadOrigin.into(), dispatch_info: DispatchInfo { - weight: Weight::from_ref_time(800).saturating_add(normal_base), + weight: Weight::from_parts(800, 0).saturating_add(normal_base), pays_fee: Pays::Yes, ..Default::default() }, @@ -415,7 +415,7 @@ fn deposit_event_uses_actual_weight_and_pays_fee() { event: SysEvent::ExtrinsicFailed { dispatch_error: DispatchError::BadOrigin.into(), dispatch_info: DispatchInfo { - weight: Weight::from_ref_time(800).saturating_add(normal_base), + weight: Weight::from_parts(800, 0).saturating_add(normal_base), pays_fee: Pays::No, ..Default::default() }, @@ -427,7 +427,7 @@ fn deposit_event_uses_actual_weight_and_pays_fee() { phase: Phase::ApplyExtrinsic(11), event: SysEvent::ExtrinsicSuccess { dispatch_info: DispatchInfo { - weight: Weight::from_ref_time(300).saturating_add(operational_base), + weight: Weight::from_parts(300, 0).saturating_add(operational_base), class: DispatchClass::Operational, ..Default::default() }, diff --git a/frame/system/src/weights.rs b/frame/system/src/weights.rs index fa77db90d..812f2d091 100644 --- a/frame/system/src/weights.rs +++ b/frame/system/src/weights.rs @@ -65,9 +65,9 @@ impl WeightInfo for SubstrateWeight { // Measured: `0` // Estimated: `0` // Minimum execution time: 2_018 nanoseconds. - Weight::from_ref_time(2_091_000) + Weight::from_parts(2_091_000, 0) // Standard Error: 0 - .saturating_add(Weight::from_ref_time(362).saturating_mul(b.into())) + .saturating_add(Weight::from_parts(362, 0).saturating_mul(b.into())) } /// The range of component `b` is `[0, 3932160]`. fn remark_with_event(b: u32, ) -> Weight { @@ -75,9 +75,9 @@ impl WeightInfo for SubstrateWeight { // Measured: `0` // Estimated: `0` // Minimum execution time: 7_449 nanoseconds. - Weight::from_ref_time(7_748_000) + Weight::from_parts(7_748_000, 0) // Standard Error: 0 - .saturating_add(Weight::from_ref_time(1_423).saturating_mul(b.into())) + .saturating_add(Weight::from_parts(1_423, 0).saturating_mul(b.into())) } /// Storage: System Digest (r:1 w:1) /// Proof Skipped: System Digest (max_values: Some(1), max_size: None, mode: Measured) @@ -100,9 +100,9 @@ impl WeightInfo for SubstrateWeight { // Measured: `0` // Estimated: `0` // Minimum execution time: 1_981 nanoseconds. - Weight::from_ref_time(2_114_000) + Weight::from_parts(2_114_000, 0) // Standard Error: 804 - .saturating_add(Weight::from_ref_time(631_438).saturating_mul(i.into())) + .saturating_add(Weight::from_parts(631_438, 0).saturating_mul(i.into())) .saturating_add(T::DbWeight::get().writes((1_u64).saturating_mul(i.into()))) } /// Storage: Skipped Metadata (r:0 w:0) @@ -113,9 +113,9 @@ impl WeightInfo for SubstrateWeight { // Measured: `0` // Estimated: `0` // Minimum execution time: 2_061 nanoseconds. - Weight::from_ref_time(2_153_000) + Weight::from_parts(2_153_000, 0) // Standard Error: 952 - .saturating_add(Weight::from_ref_time(502_629).saturating_mul(i.into())) + .saturating_add(Weight::from_parts(502_629, 0).saturating_mul(i.into())) .saturating_add(T::DbWeight::get().writes((1_u64).saturating_mul(i.into()))) } /// Storage: Skipped Metadata (r:0 w:0) @@ -128,9 +128,9 @@ impl WeightInfo for SubstrateWeight { // Minimum execution time: 4_026 nanoseconds. Weight::from_parts(4_174_000, 121) // Standard Error: 1_148 - .saturating_add(Weight::from_ref_time(1_093_099).saturating_mul(p.into())) + .saturating_add(Weight::from_parts(1_093_099, 0).saturating_mul(p.into())) .saturating_add(T::DbWeight::get().writes((1_u64).saturating_mul(p.into()))) - .saturating_add(Weight::from_proof_size(70).saturating_mul(p.into())) + .saturating_add(Weight::from_parts(0, 70).saturating_mul(p.into())) } } @@ -142,9 +142,9 @@ impl WeightInfo for () { // Measured: `0` // Estimated: `0` // Minimum execution time: 2_018 nanoseconds. - Weight::from_ref_time(2_091_000) + Weight::from_parts(2_091_000, 0) // Standard Error: 0 - .saturating_add(Weight::from_ref_time(362).saturating_mul(b.into())) + .saturating_add(Weight::from_parts(362, 0).saturating_mul(b.into())) } /// The range of component `b` is `[0, 3932160]`. fn remark_with_event(b: u32, ) -> Weight { @@ -152,9 +152,9 @@ impl WeightInfo for () { // Measured: `0` // Estimated: `0` // Minimum execution time: 7_449 nanoseconds. - Weight::from_ref_time(7_748_000) + Weight::from_parts(7_748_000, 0) // Standard Error: 0 - .saturating_add(Weight::from_ref_time(1_423).saturating_mul(b.into())) + .saturating_add(Weight::from_parts(1_423, 0).saturating_mul(b.into())) } /// Storage: System Digest (r:1 w:1) /// Proof Skipped: System Digest (max_values: Some(1), max_size: None, mode: Measured) @@ -177,9 +177,9 @@ impl WeightInfo for () { // Measured: `0` // Estimated: `0` // Minimum execution time: 1_981 nanoseconds. - Weight::from_ref_time(2_114_000) + Weight::from_parts(2_114_000, 0) // Standard Error: 804 - .saturating_add(Weight::from_ref_time(631_438).saturating_mul(i.into())) + .saturating_add(Weight::from_parts(631_438, 0).saturating_mul(i.into())) .saturating_add(RocksDbWeight::get().writes((1_u64).saturating_mul(i.into()))) } /// Storage: Skipped Metadata (r:0 w:0) @@ -190,9 +190,9 @@ impl WeightInfo for () { // Measured: `0` // Estimated: `0` // Minimum execution time: 2_061 nanoseconds. - Weight::from_ref_time(2_153_000) + Weight::from_parts(2_153_000, 0) // Standard Error: 952 - .saturating_add(Weight::from_ref_time(502_629).saturating_mul(i.into())) + .saturating_add(Weight::from_parts(502_629, 0).saturating_mul(i.into())) .saturating_add(RocksDbWeight::get().writes((1_u64).saturating_mul(i.into()))) } /// Storage: Skipped Metadata (r:0 w:0) @@ -205,8 +205,8 @@ impl WeightInfo for () { // Minimum execution time: 4_026 nanoseconds. Weight::from_parts(4_174_000, 121) // Standard Error: 1_148 - .saturating_add(Weight::from_ref_time(1_093_099).saturating_mul(p.into())) + .saturating_add(Weight::from_parts(1_093_099, 0).saturating_mul(p.into())) .saturating_add(RocksDbWeight::get().writes((1_u64).saturating_mul(p.into()))) - .saturating_add(Weight::from_proof_size(70).saturating_mul(p.into())) + .saturating_add(Weight::from_parts(0, 70).saturating_mul(p.into())) } } diff --git a/frame/timestamp/src/weights.rs b/frame/timestamp/src/weights.rs index 8c7ef0d1f..943f941d5 100644 --- a/frame/timestamp/src/weights.rs +++ b/frame/timestamp/src/weights.rs @@ -73,7 +73,7 @@ impl WeightInfo for SubstrateWeight { // Measured: `161` // Estimated: `0` // Minimum execution time: 3_927 nanoseconds. - Weight::from_ref_time(4_078_000) + Weight::from_parts(4_078_000, 0) } } @@ -97,6 +97,6 @@ impl WeightInfo for () { // Measured: `161` // Estimated: `0` // Minimum execution time: 3_927 nanoseconds. - Weight::from_ref_time(4_078_000) + Weight::from_parts(4_078_000, 0) } } diff --git a/frame/tips/src/weights.rs b/frame/tips/src/weights.rs index 41fdf4928..2b265d787 100644 --- a/frame/tips/src/weights.rs +++ b/frame/tips/src/weights.rs @@ -71,7 +71,7 @@ impl WeightInfo for SubstrateWeight { // Minimum execution time: 23_262 nanoseconds. Weight::from_parts(24_104_224, 4958) // Standard Error: 148 - .saturating_add(Weight::from_ref_time(1_963).saturating_mul(r.into())) + .saturating_add(Weight::from_parts(1_963, 0).saturating_mul(r.into())) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().writes(2_u64)) } @@ -103,12 +103,12 @@ impl WeightInfo for SubstrateWeight { // Minimum execution time: 18_382 nanoseconds. Weight::from_parts(18_195_288, 4644) // Standard Error: 103 - .saturating_add(Weight::from_ref_time(2_096).saturating_mul(r.into())) + .saturating_add(Weight::from_parts(2_096, 0).saturating_mul(r.into())) // Standard Error: 2_469 - .saturating_add(Weight::from_ref_time(97_092).saturating_mul(t.into())) + .saturating_add(Weight::from_parts(97_092, 0).saturating_mul(t.into())) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().writes(2_u64)) - .saturating_add(Weight::from_proof_size(192).saturating_mul(t.into())) + .saturating_add(Weight::from_parts(0, 192).saturating_mul(t.into())) } /// Storage: Elections Members (r:1 w:0) /// Proof Skipped: Elections Members (max_values: Some(1), max_size: None, mode: Measured) @@ -122,10 +122,10 @@ impl WeightInfo for SubstrateWeight { // Minimum execution time: 13_564 nanoseconds. Weight::from_parts(13_867_280, 4592) // Standard Error: 4_245 - .saturating_add(Weight::from_ref_time(206_587).saturating_mul(t.into())) + .saturating_add(Weight::from_parts(206_587, 0).saturating_mul(t.into())) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) - .saturating_add(Weight::from_proof_size(224).saturating_mul(t.into())) + .saturating_add(Weight::from_parts(0, 224).saturating_mul(t.into())) } /// Storage: Tips Tips (r:1 w:1) /// Proof Skipped: Tips Tips (max_values: None, max_size: None, mode: Measured) @@ -143,10 +143,10 @@ impl WeightInfo for SubstrateWeight { // Minimum execution time: 39_902 nanoseconds. Weight::from_parts(40_747_650, 8096) // Standard Error: 5_322 - .saturating_add(Weight::from_ref_time(144_298).saturating_mul(t.into())) + .saturating_add(Weight::from_parts(144_298, 0).saturating_mul(t.into())) .saturating_add(T::DbWeight::get().reads(3_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) - .saturating_add(Weight::from_proof_size(336).saturating_mul(t.into())) + .saturating_add(Weight::from_parts(0, 336).saturating_mul(t.into())) } /// Storage: Tips Tips (r:1 w:1) /// Proof Skipped: Tips Tips (max_values: None, max_size: None, mode: Measured) @@ -160,7 +160,7 @@ impl WeightInfo for SubstrateWeight { // Minimum execution time: 13_511 nanoseconds. Weight::from_parts(14_114_101, 3077) // Standard Error: 1_815 - .saturating_add(Weight::from_ref_time(7_825).saturating_mul(t.into())) + .saturating_add(Weight::from_parts(7_825, 0).saturating_mul(t.into())) .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(T::DbWeight::get().writes(2_u64)) } @@ -180,7 +180,7 @@ impl WeightInfo for () { // Minimum execution time: 23_262 nanoseconds. Weight::from_parts(24_104_224, 4958) // Standard Error: 148 - .saturating_add(Weight::from_ref_time(1_963).saturating_mul(r.into())) + .saturating_add(Weight::from_parts(1_963, 0).saturating_mul(r.into())) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().writes(2_u64)) } @@ -212,12 +212,12 @@ impl WeightInfo for () { // Minimum execution time: 18_382 nanoseconds. Weight::from_parts(18_195_288, 4644) // Standard Error: 103 - .saturating_add(Weight::from_ref_time(2_096).saturating_mul(r.into())) + .saturating_add(Weight::from_parts(2_096, 0).saturating_mul(r.into())) // Standard Error: 2_469 - .saturating_add(Weight::from_ref_time(97_092).saturating_mul(t.into())) + .saturating_add(Weight::from_parts(97_092, 0).saturating_mul(t.into())) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().writes(2_u64)) - .saturating_add(Weight::from_proof_size(192).saturating_mul(t.into())) + .saturating_add(Weight::from_parts(0, 192).saturating_mul(t.into())) } /// Storage: Elections Members (r:1 w:0) /// Proof Skipped: Elections Members (max_values: Some(1), max_size: None, mode: Measured) @@ -231,10 +231,10 @@ impl WeightInfo for () { // Minimum execution time: 13_564 nanoseconds. Weight::from_parts(13_867_280, 4592) // Standard Error: 4_245 - .saturating_add(Weight::from_ref_time(206_587).saturating_mul(t.into())) + .saturating_add(Weight::from_parts(206_587, 0).saturating_mul(t.into())) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) - .saturating_add(Weight::from_proof_size(224).saturating_mul(t.into())) + .saturating_add(Weight::from_parts(0, 224).saturating_mul(t.into())) } /// Storage: Tips Tips (r:1 w:1) /// Proof Skipped: Tips Tips (max_values: None, max_size: None, mode: Measured) @@ -252,10 +252,10 @@ impl WeightInfo for () { // Minimum execution time: 39_902 nanoseconds. Weight::from_parts(40_747_650, 8096) // Standard Error: 5_322 - .saturating_add(Weight::from_ref_time(144_298).saturating_mul(t.into())) + .saturating_add(Weight::from_parts(144_298, 0).saturating_mul(t.into())) .saturating_add(RocksDbWeight::get().reads(3_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) - .saturating_add(Weight::from_proof_size(336).saturating_mul(t.into())) + .saturating_add(Weight::from_parts(0, 336).saturating_mul(t.into())) } /// Storage: Tips Tips (r:1 w:1) /// Proof Skipped: Tips Tips (max_values: None, max_size: None, mode: Measured) @@ -269,7 +269,7 @@ impl WeightInfo for () { // Minimum execution time: 13_511 nanoseconds. Weight::from_parts(14_114_101, 3077) // Standard Error: 1_815 - .saturating_add(Weight::from_ref_time(7_825).saturating_mul(t.into())) + .saturating_add(Weight::from_parts(7_825, 0).saturating_mul(t.into())) .saturating_add(RocksDbWeight::get().reads(1_u64)) .saturating_add(RocksDbWeight::get().writes(2_u64)) } diff --git a/frame/transaction-payment/asset-tx-payment/src/mock.rs b/frame/transaction-payment/asset-tx-payment/src/mock.rs index ada4777cf..bc994b4dc 100644 --- a/frame/transaction-payment/asset-tx-payment/src/mock.rs +++ b/frame/transaction-payment/asset-tx-payment/src/mock.rs @@ -67,7 +67,7 @@ impl Get for BlockWeights { weights.base_extrinsic = ExtrinsicBaseWeight::get().into(); }) .for_class(DispatchClass::non_mandatory(), |weights| { - weights.max_total = Weight::from_ref_time(1024).set_proof_size(u64::MAX).into(); + weights.max_total = Weight::from_parts(1024, u64::MAX).into(); }) .build_or_panic() } diff --git a/frame/transaction-payment/asset-tx-payment/src/tests.rs b/frame/transaction-payment/asset-tx-payment/src/tests.rs index 08699fef0..cd9891825 100644 --- a/frame/transaction-payment/asset-tx-payment/src/tests.rs +++ b/frame/transaction-payment/asset-tx-payment/src/tests.rs @@ -41,7 +41,7 @@ impl Default for ExtBuilder { fn default() -> Self { Self { balance_factor: 1, - base_weight: Weight::from_ref_time(0), + base_weight: Weight::from_parts(0, 0), byte_fee: 1, weight_to_fee: 1, } @@ -112,19 +112,19 @@ fn transaction_payment_in_native_possible() { let balance_factor = 100; ExtBuilder::default() .balance_factor(balance_factor) - .base_weight(Weight::from_ref_time(5)) + .base_weight(Weight::from_parts(5, 0)) .build() .execute_with(|| { let len = 10; let pre = ChargeAssetTxPayment::::from(0, None) - .pre_dispatch(&1, CALL, &info_from_weight(Weight::from_ref_time(5)), len) + .pre_dispatch(&1, CALL, &info_from_weight(Weight::from_parts(5, 0)), len) .unwrap(); let initial_balance = 10 * balance_factor; assert_eq!(Balances::free_balance(1), initial_balance - 5 - 5 - 10); assert_ok!(ChargeAssetTxPayment::::post_dispatch( Some(pre), - &info_from_weight(Weight::from_ref_time(5)), + &info_from_weight(Weight::from_parts(5, 0)), &default_post_info(), len, &Ok(()) @@ -132,15 +132,15 @@ fn transaction_payment_in_native_possible() { assert_eq!(Balances::free_balance(1), initial_balance - 5 - 5 - 10); let pre = ChargeAssetTxPayment::::from(5 /* tipped */, None) - .pre_dispatch(&2, CALL, &info_from_weight(Weight::from_ref_time(100)), len) + .pre_dispatch(&2, CALL, &info_from_weight(Weight::from_parts(100, 0)), len) .unwrap(); let initial_balance_for_2 = 20 * balance_factor; assert_eq!(Balances::free_balance(2), initial_balance_for_2 - 5 - 10 - 100 - 5); assert_ok!(ChargeAssetTxPayment::::post_dispatch( Some(pre), - &info_from_weight(Weight::from_ref_time(100)), - &post_info_from_weight(Weight::from_ref_time(50)), + &info_from_weight(Weight::from_parts(100, 0)), + &post_info_from_weight(Weight::from_parts(50, 0)), len, &Ok(()) )); @@ -154,7 +154,7 @@ fn transaction_payment_in_asset_possible() { let balance_factor = 100; ExtBuilder::default() .balance_factor(balance_factor) - .base_weight(Weight::from_ref_time(base_weight)) + .base_weight(Weight::from_parts(base_weight, 0)) .build() .execute_with(|| { // create the asset @@ -180,7 +180,7 @@ fn transaction_payment_in_asset_possible() { // existential deposit let fee = (base_weight + weight + len as u64) * min_balance / ExistentialDeposit::get(); let pre = ChargeAssetTxPayment::::from(0, Some(asset_id)) - .pre_dispatch(&caller, CALL, &info_from_weight(Weight::from_ref_time(weight)), len) + .pre_dispatch(&caller, CALL, &info_from_weight(Weight::from_parts(weight, 0)), len) .unwrap(); // assert that native balance is not used assert_eq!(Balances::free_balance(caller), 10 * balance_factor); @@ -190,7 +190,7 @@ fn transaction_payment_in_asset_possible() { assert_ok!(ChargeAssetTxPayment::::post_dispatch( Some(pre), - &info_from_weight(Weight::from_ref_time(weight)), + &info_from_weight(Weight::from_parts(weight, 0)), &default_post_info(), len, &Ok(()) @@ -207,7 +207,7 @@ fn transaction_payment_without_fee() { let balance_factor = 100; ExtBuilder::default() .balance_factor(balance_factor) - .base_weight(Weight::from_ref_time(base_weight)) + .base_weight(Weight::from_parts(base_weight, 0)) .build() .execute_with(|| { // create the asset @@ -233,7 +233,7 @@ fn transaction_payment_without_fee() { // existential deposit let fee = (base_weight + weight + len as u64) * min_balance / ExistentialDeposit::get(); let pre = ChargeAssetTxPayment::::from(0, Some(asset_id)) - .pre_dispatch(&caller, CALL, &info_from_weight(Weight::from_ref_time(weight)), len) + .pre_dispatch(&caller, CALL, &info_from_weight(Weight::from_parts(weight, 0)), len) .unwrap(); // assert that native balance is not used assert_eq!(Balances::free_balance(caller), 10 * balance_factor); @@ -243,7 +243,7 @@ fn transaction_payment_without_fee() { assert_ok!(ChargeAssetTxPayment::::post_dispatch( Some(pre), - &info_from_weight(Weight::from_ref_time(weight)), + &info_from_weight(Weight::from_parts(weight, 0)), &post_info_from_pays(Pays::No), len, &Ok(()) @@ -260,7 +260,7 @@ fn asset_transaction_payment_with_tip_and_refund() { let base_weight = 5; ExtBuilder::default() .balance_factor(100) - .base_weight(Weight::from_ref_time(base_weight)) + .base_weight(Weight::from_parts(base_weight, 0)) .build() .execute_with(|| { // create the asset @@ -288,15 +288,15 @@ fn asset_transaction_payment_with_tip_and_refund() { let fee_with_tip = (base_weight + weight + len as u64 + tip) * min_balance / ExistentialDeposit::get(); let pre = ChargeAssetTxPayment::::from(tip, Some(asset_id)) - .pre_dispatch(&caller, CALL, &info_from_weight(Weight::from_ref_time(weight)), len) + .pre_dispatch(&caller, CALL, &info_from_weight(Weight::from_parts(weight, 0)), len) .unwrap(); assert_eq!(Assets::balance(asset_id, caller), balance - fee_with_tip); let final_weight = 50; assert_ok!(ChargeAssetTxPayment::::post_dispatch( Some(pre), - &info_from_weight(Weight::from_ref_time(weight)), - &post_info_from_weight(Weight::from_ref_time(final_weight)), + &info_from_weight(Weight::from_parts(weight, 0)), + &post_info_from_weight(Weight::from_parts(final_weight, 0)), len, &Ok(()) )); @@ -312,7 +312,7 @@ fn payment_from_account_with_only_assets() { let base_weight = 5; ExtBuilder::default() .balance_factor(100) - .base_weight(Weight::from_ref_time(base_weight)) + .base_weight(Weight::from_parts(base_weight, 0)) .build() .execute_with(|| { // create the asset @@ -340,7 +340,7 @@ fn payment_from_account_with_only_assets() { // existential deposit let fee = (base_weight + weight + len as u64) * min_balance / ExistentialDeposit::get(); let pre = ChargeAssetTxPayment::::from(0, Some(asset_id)) - .pre_dispatch(&caller, CALL, &info_from_weight(Weight::from_ref_time(weight)), len) + .pre_dispatch(&caller, CALL, &info_from_weight(Weight::from_parts(weight, 0)), len) .unwrap(); assert_eq!(Balances::free_balance(caller), 0); // check that fee was charged in the given asset @@ -348,7 +348,7 @@ fn payment_from_account_with_only_assets() { assert_ok!(ChargeAssetTxPayment::::post_dispatch( Some(pre), - &info_from_weight(Weight::from_ref_time(weight)), + &info_from_weight(Weight::from_parts(weight, 0)), &default_post_info(), len, &Ok(()) @@ -363,7 +363,7 @@ fn payment_only_with_existing_sufficient_asset() { let base_weight = 5; ExtBuilder::default() .balance_factor(100) - .base_weight(Weight::from_ref_time(base_weight)) + .base_weight(Weight::from_parts(base_weight, 0)) .build() .execute_with(|| { let asset_id = 1; @@ -372,7 +372,7 @@ fn payment_only_with_existing_sufficient_asset() { let len = 10; // pre_dispatch fails for non-existent asset assert!(ChargeAssetTxPayment::::from(0, Some(asset_id)) - .pre_dispatch(&caller, CALL, &info_from_weight(Weight::from_ref_time(weight)), len) + .pre_dispatch(&caller, CALL, &info_from_weight(Weight::from_parts(weight, 0)), len) .is_err()); // create the non-sufficient asset @@ -386,7 +386,7 @@ fn payment_only_with_existing_sufficient_asset() { )); // pre_dispatch fails for non-sufficient asset assert!(ChargeAssetTxPayment::::from(0, Some(asset_id)) - .pre_dispatch(&caller, CALL, &info_from_weight(Weight::from_ref_time(weight)), len) + .pre_dispatch(&caller, CALL, &info_from_weight(Weight::from_parts(weight, 0)), len) .is_err()); }); } @@ -396,7 +396,7 @@ fn converted_fee_is_never_zero_if_input_fee_is_not() { let base_weight = 1; ExtBuilder::default() .balance_factor(100) - .base_weight(Weight::from_ref_time(base_weight)) + .base_weight(Weight::from_parts(base_weight, 0)) .build() .execute_with(|| { // create the asset @@ -440,14 +440,14 @@ fn converted_fee_is_never_zero_if_input_fee_is_not() { assert_eq!(Assets::balance(asset_id, caller), balance); } let pre = ChargeAssetTxPayment::::from(0, Some(asset_id)) - .pre_dispatch(&caller, CALL, &info_from_weight(Weight::from_ref_time(weight)), len) + .pre_dispatch(&caller, CALL, &info_from_weight(Weight::from_parts(weight, 0)), len) .unwrap(); // check that at least one coin was charged in the given asset assert_eq!(Assets::balance(asset_id, caller), balance - 1); assert_ok!(ChargeAssetTxPayment::::post_dispatch( Some(pre), - &info_from_weight(Weight::from_ref_time(weight)), + &info_from_weight(Weight::from_parts(weight, 0)), &default_post_info(), len, &Ok(()) @@ -461,7 +461,7 @@ fn post_dispatch_fee_is_zero_if_pre_dispatch_fee_is_zero() { let base_weight = 1; ExtBuilder::default() .balance_factor(100) - .base_weight(Weight::from_ref_time(base_weight)) + .base_weight(Weight::from_parts(base_weight, 0)) .build() .execute_with(|| { // create the asset @@ -518,7 +518,7 @@ fn post_dispatch_fee_is_zero_if_unsigned_pre_dispatch_fee_is_zero() { let base_weight = 1; ExtBuilder::default() .balance_factor(100) - .base_weight(Weight::from_ref_time(base_weight)) + .base_weight(Weight::from_parts(base_weight, 0)) .build() .execute_with(|| { // create the asset @@ -542,7 +542,7 @@ fn post_dispatch_fee_is_zero_if_unsigned_pre_dispatch_fee_is_zero() { let len = 1; ChargeAssetTxPayment::::pre_dispatch_unsigned( CALL, - &info_from_weight(Weight::from_ref_time(weight)), + &info_from_weight(Weight::from_parts(weight, 0)), len, ) .unwrap(); @@ -553,7 +553,7 @@ fn post_dispatch_fee_is_zero_if_unsigned_pre_dispatch_fee_is_zero() { // initial fee) assert_ok!(ChargeAssetTxPayment::::post_dispatch( None, - &info_from_weight(Weight::from_ref_time(weight)), + &info_from_weight(Weight::from_parts(weight, 0)), &post_info_from_pays(Pays::Yes), len, &Ok(()) diff --git a/frame/transaction-payment/src/lib.rs b/frame/transaction-payment/src/lib.rs index 6ed3e9789..42798c62f 100644 --- a/frame/transaction-payment/src/lib.rs +++ b/frame/transaction-payment/src/lib.rs @@ -614,7 +614,7 @@ where /// Compute the length portion of a fee by invoking the configured `LengthToFee` impl. pub fn length_to_fee(length: u32) -> BalanceOf { - T::LengthToFee::weight_to_fee(&Weight::from_ref_time(length as u64)) + T::LengthToFee::weight_to_fee(&Weight::from_parts(length as u64, 0)) } /// Compute the unadjusted portion of the weight fee by invoking the configured `WeightToFee` diff --git a/frame/transaction-payment/src/mock.rs b/frame/transaction-payment/src/mock.rs index b4b8a784a..92799bf5b 100644 --- a/frame/transaction-payment/src/mock.rs +++ b/frame/transaction-payment/src/mock.rs @@ -64,7 +64,7 @@ impl Get for BlockWeights { weights.base_extrinsic = ExtrinsicBaseWeight::get().into(); }) .for_class(DispatchClass::non_mandatory(), |weights| { - weights.max_total = Weight::from_ref_time(1024).set_proof_size(u64::MAX).into(); + weights.max_total = Weight::from_parts(1024, u64::MAX).into(); }) .build_or_panic() } diff --git a/frame/transaction-payment/src/tests.rs b/frame/transaction-payment/src/tests.rs index 2748b80b1..218f50e1c 100644 --- a/frame/transaction-payment/src/tests.rs +++ b/frame/transaction-payment/src/tests.rs @@ -129,18 +129,18 @@ fn default_post_info() -> PostDispatchInfo { fn signed_extension_transaction_payment_work() { ExtBuilder::default() .balance_factor(10) - .base_weight(Weight::from_ref_time(5)) + .base_weight(Weight::from_parts(5, 0)) .build() .execute_with(|| { let len = 10; let pre = ChargeTransactionPayment::::from(0) - .pre_dispatch(&1, CALL, &info_from_weight(Weight::from_ref_time(5)), len) + .pre_dispatch(&1, CALL, &info_from_weight(Weight::from_parts(5, 0)), len) .unwrap(); assert_eq!(Balances::free_balance(1), 100 - 5 - 5 - 10); assert_ok!(ChargeTransactionPayment::::post_dispatch( Some(pre), - &info_from_weight(Weight::from_ref_time(5)), + &info_from_weight(Weight::from_parts(5, 0)), &default_post_info(), len, &Ok(()) @@ -152,14 +152,14 @@ fn signed_extension_transaction_payment_work() { FeeUnbalancedAmount::mutate(|a| *a = 0); let pre = ChargeTransactionPayment::::from(5 /* tipped */) - .pre_dispatch(&2, CALL, &info_from_weight(Weight::from_ref_time(100)), len) + .pre_dispatch(&2, CALL, &info_from_weight(Weight::from_parts(100, 0)), len) .unwrap(); assert_eq!(Balances::free_balance(2), 200 - 5 - 10 - 100 - 5); assert_ok!(ChargeTransactionPayment::::post_dispatch( Some(pre), - &info_from_weight(Weight::from_ref_time(100)), - &post_info_from_weight(Weight::from_ref_time(50)), + &info_from_weight(Weight::from_parts(100, 0)), + &post_info_from_weight(Weight::from_parts(50, 0)), len, &Ok(()) )); @@ -173,22 +173,22 @@ fn signed_extension_transaction_payment_work() { fn signed_extension_transaction_payment_multiplied_refund_works() { ExtBuilder::default() .balance_factor(10) - .base_weight(Weight::from_ref_time(5)) + .base_weight(Weight::from_parts(5, 0)) .build() .execute_with(|| { let len = 10; >::put(Multiplier::saturating_from_rational(3, 2)); let pre = ChargeTransactionPayment::::from(5 /* tipped */) - .pre_dispatch(&2, CALL, &info_from_weight(Weight::from_ref_time(100)), len) + .pre_dispatch(&2, CALL, &info_from_weight(Weight::from_parts(100, 0)), len) .unwrap(); // 5 base fee, 10 byte fee, 3/2 * 100 weight fee, 5 tip assert_eq!(Balances::free_balance(2), 200 - 5 - 10 - 150 - 5); assert_ok!(ChargeTransactionPayment::::post_dispatch( Some(pre), - &info_from_weight(Weight::from_ref_time(100)), - &post_info_from_weight(Weight::from_ref_time(50)), + &info_from_weight(Weight::from_parts(100, 0)), + &post_info_from_weight(Weight::from_parts(50, 0)), len, &Ok(()) )); @@ -219,7 +219,7 @@ fn signed_extension_transaction_payment_is_bounded() { #[test] fn signed_extension_allows_free_transactions() { ExtBuilder::default() - .base_weight(Weight::from_ref_time(100)) + .base_weight(Weight::from_parts(100, 0)) .balance_factor(0) .build() .execute_with(|| { @@ -230,7 +230,7 @@ fn signed_extension_allows_free_transactions() { // This is a completely free (and thus wholly insecure/DoS-ridden) transaction. let operational_transaction = DispatchInfo { - weight: Weight::from_ref_time(0), + weight: Weight::from_parts(0, 0), class: DispatchClass::Operational, pays_fee: Pays::No, }; @@ -243,7 +243,7 @@ fn signed_extension_allows_free_transactions() { // like a InsecureFreeNormal let free_transaction = DispatchInfo { - weight: Weight::from_ref_time(0), + weight: Weight::from_parts(0, 0), class: DispatchClass::Normal, pays_fee: Pays::Yes, }; @@ -262,7 +262,7 @@ fn signed_extension_allows_free_transactions() { #[test] fn signed_ext_length_fee_is_also_updated_per_congestion() { ExtBuilder::default() - .base_weight(Weight::from_ref_time(5)) + .base_weight(Weight::from_parts(5, 0)) .balance_factor(10) .build() .execute_with(|| { @@ -271,7 +271,7 @@ fn signed_ext_length_fee_is_also_updated_per_congestion() { let len = 10; assert_ok!(ChargeTransactionPayment::::from(10) // tipped - .pre_dispatch(&1, CALL, &info_from_weight(Weight::from_ref_time(3)), len)); + .pre_dispatch(&1, CALL, &info_from_weight(Weight::from_parts(3, 0)), len)); assert_eq!( Balances::free_balance(1), 100 // original @@ -297,7 +297,7 @@ fn query_info_and_fee_details_works() { let unsigned_xt_info = unsigned_xt.get_dispatch_info(); ExtBuilder::default() - .base_weight(Weight::from_ref_time(5)) + .base_weight(Weight::from_parts(5, 0)) .weight_fee(2) .build() .execute_with(|| { @@ -354,7 +354,7 @@ fn query_call_info_and_fee_details_works() { let len = encoded_call.len() as u32; ExtBuilder::default() - .base_weight(Weight::from_ref_time(5)) + .base_weight(Weight::from_parts(5, 0)) .weight_fee(2) .build() .execute_with(|| { @@ -392,7 +392,7 @@ fn query_call_info_and_fee_details_works() { #[test] fn compute_fee_works_without_multiplier() { ExtBuilder::default() - .base_weight(Weight::from_ref_time(100)) + .base_weight(Weight::from_parts(100, 0)) .byte_fee(10) .balance_factor(0) .build() @@ -402,14 +402,14 @@ fn compute_fee_works_without_multiplier() { // Tip only, no fees works let dispatch_info = DispatchInfo { - weight: Weight::from_ref_time(0), + weight: Weight::from_parts(0, 0), class: DispatchClass::Operational, pays_fee: Pays::No, }; assert_eq!(Pallet::::compute_fee(0, &dispatch_info, 10), 10); // No tip, only base fee works let dispatch_info = DispatchInfo { - weight: Weight::from_ref_time(0), + weight: Weight::from_parts(0, 0), class: DispatchClass::Operational, pays_fee: Pays::Yes, }; @@ -420,7 +420,7 @@ fn compute_fee_works_without_multiplier() { assert_eq!(Pallet::::compute_fee(42, &dispatch_info, 0), 520); // Weight fee + base fee works let dispatch_info = DispatchInfo { - weight: Weight::from_ref_time(1000), + weight: Weight::from_parts(1000, 0), class: DispatchClass::Operational, pays_fee: Pays::Yes, }; @@ -431,7 +431,7 @@ fn compute_fee_works_without_multiplier() { #[test] fn compute_fee_works_with_multiplier() { ExtBuilder::default() - .base_weight(Weight::from_ref_time(100)) + .base_weight(Weight::from_parts(100, 0)) .byte_fee(10) .balance_factor(0) .build() @@ -440,7 +440,7 @@ fn compute_fee_works_with_multiplier() { >::put(Multiplier::saturating_from_rational(3, 2)); // Base fee is unaffected by multiplier let dispatch_info = DispatchInfo { - weight: Weight::from_ref_time(0), + weight: Weight::from_parts(0, 0), class: DispatchClass::Operational, pays_fee: Pays::Yes, }; @@ -448,7 +448,7 @@ fn compute_fee_works_with_multiplier() { // Everything works together :) let dispatch_info = DispatchInfo { - weight: Weight::from_ref_time(123), + weight: Weight::from_parts(123, 0), class: DispatchClass::Operational, pays_fee: Pays::Yes, }; @@ -463,7 +463,7 @@ fn compute_fee_works_with_multiplier() { #[test] fn compute_fee_works_with_negative_multiplier() { ExtBuilder::default() - .base_weight(Weight::from_ref_time(100)) + .base_weight(Weight::from_parts(100, 0)) .byte_fee(10) .balance_factor(0) .build() @@ -473,7 +473,7 @@ fn compute_fee_works_with_negative_multiplier() { // Base fee is unaffected by multiplier. let dispatch_info = DispatchInfo { - weight: Weight::from_ref_time(0), + weight: Weight::from_parts(0, 0), class: DispatchClass::Operational, pays_fee: Pays::Yes, }; @@ -481,7 +481,7 @@ fn compute_fee_works_with_negative_multiplier() { // Everything works together. let dispatch_info = DispatchInfo { - weight: Weight::from_ref_time(123), + weight: Weight::from_parts(123, 0), class: DispatchClass::Operational, pays_fee: Pays::Yes, }; @@ -496,7 +496,7 @@ fn compute_fee_works_with_negative_multiplier() { #[test] fn compute_fee_does_not_overflow() { ExtBuilder::default() - .base_weight(Weight::from_ref_time(100)) + .base_weight(Weight::from_parts(100, 0)) .byte_fee(10) .balance_factor(0) .build() @@ -518,14 +518,14 @@ fn compute_fee_does_not_overflow() { fn refund_does_not_recreate_account() { ExtBuilder::default() .balance_factor(10) - .base_weight(Weight::from_ref_time(5)) + .base_weight(Weight::from_parts(5, 0)) .build() .execute_with(|| { // So events are emitted System::set_block_number(10); let len = 10; let pre = ChargeTransactionPayment::::from(5 /* tipped */) - .pre_dispatch(&2, CALL, &info_from_weight(Weight::from_ref_time(100)), len) + .pre_dispatch(&2, CALL, &info_from_weight(Weight::from_parts(100, 0)), len) .unwrap(); assert_eq!(Balances::free_balance(2), 200 - 5 - 10 - 100 - 5); @@ -535,8 +535,8 @@ fn refund_does_not_recreate_account() { assert_ok!(ChargeTransactionPayment::::post_dispatch( Some(pre), - &info_from_weight(Weight::from_ref_time(100)), - &post_info_from_weight(Weight::from_ref_time(50)), + &info_from_weight(Weight::from_parts(100, 0)), + &post_info_from_weight(Weight::from_parts(50, 0)), len, &Ok(()) )); @@ -558,19 +558,19 @@ fn refund_does_not_recreate_account() { fn actual_weight_higher_than_max_refunds_nothing() { ExtBuilder::default() .balance_factor(10) - .base_weight(Weight::from_ref_time(5)) + .base_weight(Weight::from_parts(5, 0)) .build() .execute_with(|| { let len = 10; let pre = ChargeTransactionPayment::::from(5 /* tipped */) - .pre_dispatch(&2, CALL, &info_from_weight(Weight::from_ref_time(100)), len) + .pre_dispatch(&2, CALL, &info_from_weight(Weight::from_parts(100, 0)), len) .unwrap(); assert_eq!(Balances::free_balance(2), 200 - 5 - 10 - 100 - 5); assert_ok!(ChargeTransactionPayment::::post_dispatch( Some(pre), - &info_from_weight(Weight::from_ref_time(100)), - &post_info_from_weight(Weight::from_ref_time(101)), + &info_from_weight(Weight::from_parts(100, 0)), + &post_info_from_weight(Weight::from_parts(101, 0)), len, &Ok(()) )); @@ -582,14 +582,14 @@ fn actual_weight_higher_than_max_refunds_nothing() { fn zero_transfer_on_free_transaction() { ExtBuilder::default() .balance_factor(10) - .base_weight(Weight::from_ref_time(5)) + .base_weight(Weight::from_parts(5, 0)) .build() .execute_with(|| { // So events are emitted System::set_block_number(10); let len = 10; let dispatch_info = DispatchInfo { - weight: Weight::from_ref_time(100), + weight: Weight::from_parts(100, 0), pays_fee: Pays::No, class: DispatchClass::Normal, }; @@ -621,11 +621,11 @@ fn zero_transfer_on_free_transaction() { fn refund_consistent_with_actual_weight() { ExtBuilder::default() .balance_factor(10) - .base_weight(Weight::from_ref_time(7)) + .base_weight(Weight::from_parts(7, 0)) .build() .execute_with(|| { - let info = info_from_weight(Weight::from_ref_time(100)); - let post_info = post_info_from_weight(Weight::from_ref_time(33)); + let info = info_from_weight(Weight::from_parts(100, 0)); + let post_info = post_info_from_weight(Weight::from_parts(33, 0)); let prev_balance = Balances::free_balance(2); let len = 10; let tip = 5; @@ -662,7 +662,7 @@ fn should_alter_operational_priority() { ExtBuilder::default().balance_factor(100).build().execute_with(|| { let normal = DispatchInfo { - weight: Weight::from_ref_time(100), + weight: Weight::from_parts(100, 0), class: DispatchClass::Normal, pays_fee: Pays::Yes, }; @@ -683,7 +683,7 @@ fn should_alter_operational_priority() { ExtBuilder::default().balance_factor(100).build().execute_with(|| { let op = DispatchInfo { - weight: Weight::from_ref_time(100), + weight: Weight::from_parts(100, 0), class: DispatchClass::Operational, pays_fee: Pays::Yes, }; @@ -708,7 +708,7 @@ fn no_tip_has_some_priority() { ExtBuilder::default().balance_factor(100).build().execute_with(|| { let normal = DispatchInfo { - weight: Weight::from_ref_time(100), + weight: Weight::from_parts(100, 0), class: DispatchClass::Normal, pays_fee: Pays::Yes, }; @@ -722,7 +722,7 @@ fn no_tip_has_some_priority() { ExtBuilder::default().balance_factor(100).build().execute_with(|| { let op = DispatchInfo { - weight: Weight::from_ref_time(100), + weight: Weight::from_parts(100, 0), class: DispatchClass::Operational, pays_fee: Pays::Yes, }; @@ -742,7 +742,7 @@ fn higher_tip_have_higher_priority() { let len = 10; ExtBuilder::default().balance_factor(100).build().execute_with(|| { let normal = DispatchInfo { - weight: Weight::from_ref_time(100), + weight: Weight::from_parts(100, 0), class: DispatchClass::Normal, pays_fee: Pays::Yes, }; @@ -754,7 +754,7 @@ fn higher_tip_have_higher_priority() { ExtBuilder::default().balance_factor(100).build().execute_with(|| { let op = DispatchInfo { - weight: Weight::from_ref_time(100), + weight: Weight::from_parts(100, 0), class: DispatchClass::Operational, pays_fee: Pays::Yes, }; @@ -781,10 +781,10 @@ fn higher_tip_have_higher_priority() { fn post_info_can_change_pays_fee() { ExtBuilder::default() .balance_factor(10) - .base_weight(Weight::from_ref_time(7)) + .base_weight(Weight::from_parts(7, 0)) .build() .execute_with(|| { - let info = info_from_weight(Weight::from_ref_time(100)); + let info = info_from_weight(Weight::from_parts(100, 0)); let post_info = post_info_from_pays(Pays::No); let prev_balance = Balances::free_balance(2); let len = 10; diff --git a/frame/transaction-payment/src/types.rs b/frame/transaction-payment/src/types.rs index b8bc17918..cbe85309b 100644 --- a/frame/transaction-payment/src/types.rs +++ b/frame/transaction-payment/src/types.rs @@ -144,7 +144,7 @@ mod tests { #[test] fn should_serialize_and_deserialize_properly_with_string() { let info = RuntimeDispatchInfo { - weight: Weight::from_ref_time(5), + weight: Weight::from_parts(5, 0), class: DispatchClass::Normal, partial_fee: 1_000_000_u64, }; @@ -162,7 +162,7 @@ mod tests { #[test] fn should_serialize_and_deserialize_properly_large_value() { let info = RuntimeDispatchInfo { - weight: Weight::from_ref_time(5), + weight: Weight::from_parts(5, 0), class: DispatchClass::Normal, partial_fee: u128::max_value(), }; diff --git a/frame/transaction-storage/src/weights.rs b/frame/transaction-storage/src/weights.rs index d9659a925..896e1ebab 100644 --- a/frame/transaction-storage/src/weights.rs +++ b/frame/transaction-storage/src/weights.rs @@ -70,7 +70,7 @@ impl WeightInfo for SubstrateWeight { // Minimum execution time: 28_702 nanoseconds. Weight::from_parts(29_164_000, 38383) // Standard Error: 2 - .saturating_add(Weight::from_ref_time(5_601).saturating_mul(l.into())) + .saturating_add(Weight::from_parts(5_601, 0).saturating_mul(l.into())) .saturating_add(T::DbWeight::get().reads(3_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -128,7 +128,7 @@ impl WeightInfo for () { // Minimum execution time: 28_702 nanoseconds. Weight::from_parts(29_164_000, 38383) // Standard Error: 2 - .saturating_add(Weight::from_ref_time(5_601).saturating_mul(l.into())) + .saturating_add(Weight::from_parts(5_601, 0).saturating_mul(l.into())) .saturating_add(RocksDbWeight::get().reads(3_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } diff --git a/frame/treasury/src/weights.rs b/frame/treasury/src/weights.rs index 10bab0083..abf461c62 100644 --- a/frame/treasury/src/weights.rs +++ b/frame/treasury/src/weights.rs @@ -112,7 +112,7 @@ impl WeightInfo for SubstrateWeight { // Minimum execution time: 9_366 nanoseconds. Weight::from_parts(11_731_455, 3480) // Standard Error: 761 - .saturating_add(Weight::from_ref_time(21_665).saturating_mul(p.into())) + .saturating_add(Weight::from_parts(21_665, 0).saturating_mul(p.into())) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -145,12 +145,12 @@ impl WeightInfo for SubstrateWeight { // Minimum execution time: 37_834 nanoseconds. Weight::from_parts(47_496_917, 2305) // Standard Error: 12_505 - .saturating_add(Weight::from_ref_time(26_902_474).saturating_mul(p.into())) + .saturating_add(Weight::from_parts(26_902_474, 0).saturating_mul(p.into())) .saturating_add(T::DbWeight::get().reads(3_u64)) .saturating_add(T::DbWeight::get().reads((3_u64).saturating_mul(p.into()))) .saturating_add(T::DbWeight::get().writes(3_u64)) .saturating_add(T::DbWeight::get().writes((3_u64).saturating_mul(p.into()))) - .saturating_add(Weight::from_proof_size(7789).saturating_mul(p.into())) + .saturating_add(Weight::from_parts(0, 7789).saturating_mul(p.into())) } } @@ -209,7 +209,7 @@ impl WeightInfo for () { // Minimum execution time: 9_366 nanoseconds. Weight::from_parts(11_731_455, 3480) // Standard Error: 761 - .saturating_add(Weight::from_ref_time(21_665).saturating_mul(p.into())) + .saturating_add(Weight::from_parts(21_665, 0).saturating_mul(p.into())) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } @@ -242,11 +242,11 @@ impl WeightInfo for () { // Minimum execution time: 37_834 nanoseconds. Weight::from_parts(47_496_917, 2305) // Standard Error: 12_505 - .saturating_add(Weight::from_ref_time(26_902_474).saturating_mul(p.into())) + .saturating_add(Weight::from_parts(26_902_474, 0).saturating_mul(p.into())) .saturating_add(RocksDbWeight::get().reads(3_u64)) .saturating_add(RocksDbWeight::get().reads((3_u64).saturating_mul(p.into()))) .saturating_add(RocksDbWeight::get().writes(3_u64)) .saturating_add(RocksDbWeight::get().writes((3_u64).saturating_mul(p.into()))) - .saturating_add(Weight::from_proof_size(7789).saturating_mul(p.into())) + .saturating_add(Weight::from_parts(0, 7789).saturating_mul(p.into())) } } diff --git a/frame/uniques/src/weights.rs b/frame/uniques/src/weights.rs index 144a384f0..edf3bd35d 100644 --- a/frame/uniques/src/weights.rs +++ b/frame/uniques/src/weights.rs @@ -131,18 +131,18 @@ impl WeightInfo for SubstrateWeight { // Minimum execution time: 2_404_081 nanoseconds. Weight::from_parts(2_419_004_000, 5250) // Standard Error: 27_175 - .saturating_add(Weight::from_ref_time(8_616_904).saturating_mul(n.into())) + .saturating_add(Weight::from_parts(8_616_904, 0).saturating_mul(n.into())) // Standard Error: 27_175 - .saturating_add(Weight::from_ref_time(334_249).saturating_mul(m.into())) + .saturating_add(Weight::from_parts(334_249, 0).saturating_mul(m.into())) // Standard Error: 27_175 - .saturating_add(Weight::from_ref_time(213_038).saturating_mul(a.into())) + .saturating_add(Weight::from_parts(213_038, 0).saturating_mul(a.into())) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().reads((1_u64).saturating_mul(n.into()))) .saturating_add(T::DbWeight::get().writes(4_u64)) .saturating_add(T::DbWeight::get().writes((2_u64).saturating_mul(n.into()))) .saturating_add(T::DbWeight::get().writes((1_u64).saturating_mul(m.into()))) .saturating_add(T::DbWeight::get().writes((1_u64).saturating_mul(a.into()))) - .saturating_add(Weight::from_proof_size(2597).saturating_mul(n.into())) + .saturating_add(Weight::from_parts(0, 2597).saturating_mul(n.into())) } /// Storage: Uniques Asset (r:1 w:1) /// Proof: Uniques Asset (max_values: None, max_size: Some(122), added: 2597, mode: MaxEncodedLen) @@ -207,12 +207,12 @@ impl WeightInfo for SubstrateWeight { // Minimum execution time: 13_633 nanoseconds. Weight::from_parts(13_797_000, 2653) // Standard Error: 9_293 - .saturating_add(Weight::from_ref_time(11_163_914).saturating_mul(i.into())) + .saturating_add(Weight::from_parts(11_163_914, 0).saturating_mul(i.into())) .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(T::DbWeight::get().reads((1_u64).saturating_mul(i.into()))) .saturating_add(T::DbWeight::get().writes(1_u64)) .saturating_add(T::DbWeight::get().writes((1_u64).saturating_mul(i.into()))) - .saturating_add(Weight::from_proof_size(2597).saturating_mul(i.into())) + .saturating_add(Weight::from_parts(0, 2597).saturating_mul(i.into())) } /// Storage: Uniques Asset (r:1 w:1) /// Proof: Uniques Asset (max_values: None, max_size: Some(122), added: 2597, mode: MaxEncodedLen) @@ -519,18 +519,18 @@ impl WeightInfo for () { // Minimum execution time: 2_404_081 nanoseconds. Weight::from_parts(2_419_004_000, 5250) // Standard Error: 27_175 - .saturating_add(Weight::from_ref_time(8_616_904).saturating_mul(n.into())) + .saturating_add(Weight::from_parts(8_616_904, 0).saturating_mul(n.into())) // Standard Error: 27_175 - .saturating_add(Weight::from_ref_time(334_249).saturating_mul(m.into())) + .saturating_add(Weight::from_parts(334_249, 0).saturating_mul(m.into())) // Standard Error: 27_175 - .saturating_add(Weight::from_ref_time(213_038).saturating_mul(a.into())) + .saturating_add(Weight::from_parts(213_038, 0).saturating_mul(a.into())) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().reads((1_u64).saturating_mul(n.into()))) .saturating_add(RocksDbWeight::get().writes(4_u64)) .saturating_add(RocksDbWeight::get().writes((2_u64).saturating_mul(n.into()))) .saturating_add(RocksDbWeight::get().writes((1_u64).saturating_mul(m.into()))) .saturating_add(RocksDbWeight::get().writes((1_u64).saturating_mul(a.into()))) - .saturating_add(Weight::from_proof_size(2597).saturating_mul(n.into())) + .saturating_add(Weight::from_parts(0, 2597).saturating_mul(n.into())) } /// Storage: Uniques Asset (r:1 w:1) /// Proof: Uniques Asset (max_values: None, max_size: Some(122), added: 2597, mode: MaxEncodedLen) @@ -595,12 +595,12 @@ impl WeightInfo for () { // Minimum execution time: 13_633 nanoseconds. Weight::from_parts(13_797_000, 2653) // Standard Error: 9_293 - .saturating_add(Weight::from_ref_time(11_163_914).saturating_mul(i.into())) + .saturating_add(Weight::from_parts(11_163_914, 0).saturating_mul(i.into())) .saturating_add(RocksDbWeight::get().reads(1_u64)) .saturating_add(RocksDbWeight::get().reads((1_u64).saturating_mul(i.into()))) .saturating_add(RocksDbWeight::get().writes(1_u64)) .saturating_add(RocksDbWeight::get().writes((1_u64).saturating_mul(i.into()))) - .saturating_add(Weight::from_proof_size(2597).saturating_mul(i.into())) + .saturating_add(Weight::from_parts(0, 2597).saturating_mul(i.into())) } /// Storage: Uniques Asset (r:1 w:1) /// Proof: Uniques Asset (max_values: None, max_size: Some(122), added: 2597, mode: MaxEncodedLen) diff --git a/frame/utility/src/tests.rs b/frame/utility/src/tests.rs index 938a5392b..413315a25 100644 --- a/frame/utility/src/tests.rs +++ b/frame/utility/src/tests.rs @@ -308,8 +308,8 @@ fn as_derivative_works() { #[test] fn as_derivative_handles_weight_refund() { new_test_ext().execute_with(|| { - let start_weight = Weight::from_ref_time(100); - let end_weight = Weight::from_ref_time(75); + let start_weight = Weight::from_parts(100, 0); + let end_weight = Weight::from_parts(75, 0); let diff = start_weight - end_weight; // Full weight when ok @@ -495,8 +495,8 @@ fn batch_weight_calculation_doesnt_overflow() { #[test] fn batch_handles_weight_refund() { new_test_ext().execute_with(|| { - let start_weight = Weight::from_ref_time(100); - let end_weight = Weight::from_ref_time(75); + let start_weight = Weight::from_parts(100, 0); + let end_weight = Weight::from_parts(75, 0); let diff = start_weight - end_weight; let batch_len = 4; @@ -611,8 +611,8 @@ fn batch_all_revert() { #[test] fn batch_all_handles_weight_refund() { new_test_ext().execute_with(|| { - let start_weight = Weight::from_ref_time(100); - let end_weight = Weight::from_ref_time(75); + let start_weight = Weight::from_parts(100, 0); + let end_weight = Weight::from_parts(75, 0); let diff = start_weight - end_weight; let batch_len = 4; @@ -739,7 +739,7 @@ fn force_batch_works() { RuntimeOrigin::signed(1), vec![ call_transfer(2, 5), - call_foobar(true, Weight::from_ref_time(75), None), + call_foobar(true, Weight::from_parts(75, 0), None), call_transfer(2, 10), call_transfer(2, 5), ] diff --git a/frame/utility/src/weights.rs b/frame/utility/src/weights.rs index 8c7a14715..c680c9ff0 100644 --- a/frame/utility/src/weights.rs +++ b/frame/utility/src/weights.rs @@ -64,16 +64,16 @@ impl WeightInfo for SubstrateWeight { // Measured: `0` // Estimated: `0` // Minimum execution time: 6_742 nanoseconds. - Weight::from_ref_time(16_087_635) + Weight::from_parts(16_087_635, 0) // Standard Error: 2_411 - .saturating_add(Weight::from_ref_time(3_665_506).saturating_mul(c.into())) + .saturating_add(Weight::from_parts(3_665_506, 0).saturating_mul(c.into())) } fn as_derivative() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` // Minimum execution time: 4_802 nanoseconds. - Weight::from_ref_time(5_269_000) + Weight::from_parts(5_269_000, 0) } /// The range of component `c` is `[0, 1000]`. fn batch_all(c: u32, ) -> Weight { @@ -81,16 +81,16 @@ impl WeightInfo for SubstrateWeight { // Measured: `0` // Estimated: `0` // Minimum execution time: 7_100 nanoseconds. - Weight::from_ref_time(14_090_381) + Weight::from_parts(14_090_381, 0) // Standard Error: 1_917 - .saturating_add(Weight::from_ref_time(3_744_891).saturating_mul(c.into())) + .saturating_add(Weight::from_parts(3_744_891, 0).saturating_mul(c.into())) } fn dispatch_as() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` // Minimum execution time: 8_840 nanoseconds. - Weight::from_ref_time(9_280_000) + Weight::from_parts(9_280_000, 0) } /// The range of component `c` is `[0, 1000]`. fn force_batch(c: u32, ) -> Weight { @@ -98,9 +98,9 @@ impl WeightInfo for SubstrateWeight { // Measured: `0` // Estimated: `0` // Minimum execution time: 7_245 nanoseconds. - Weight::from_ref_time(14_292_923) + Weight::from_parts(14_292_923, 0) // Standard Error: 1_803 - .saturating_add(Weight::from_ref_time(3_645_950).saturating_mul(c.into())) + .saturating_add(Weight::from_parts(3_645_950, 0).saturating_mul(c.into())) } } @@ -112,16 +112,16 @@ impl WeightInfo for () { // Measured: `0` // Estimated: `0` // Minimum execution time: 6_742 nanoseconds. - Weight::from_ref_time(16_087_635) + Weight::from_parts(16_087_635, 0) // Standard Error: 2_411 - .saturating_add(Weight::from_ref_time(3_665_506).saturating_mul(c.into())) + .saturating_add(Weight::from_parts(3_665_506, 0).saturating_mul(c.into())) } fn as_derivative() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` // Minimum execution time: 4_802 nanoseconds. - Weight::from_ref_time(5_269_000) + Weight::from_parts(5_269_000, 0) } /// The range of component `c` is `[0, 1000]`. fn batch_all(c: u32, ) -> Weight { @@ -129,16 +129,16 @@ impl WeightInfo for () { // Measured: `0` // Estimated: `0` // Minimum execution time: 7_100 nanoseconds. - Weight::from_ref_time(14_090_381) + Weight::from_parts(14_090_381, 0) // Standard Error: 1_917 - .saturating_add(Weight::from_ref_time(3_744_891).saturating_mul(c.into())) + .saturating_add(Weight::from_parts(3_744_891, 0).saturating_mul(c.into())) } fn dispatch_as() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` // Minimum execution time: 8_840 nanoseconds. - Weight::from_ref_time(9_280_000) + Weight::from_parts(9_280_000, 0) } /// The range of component `c` is `[0, 1000]`. fn force_batch(c: u32, ) -> Weight { @@ -146,8 +146,8 @@ impl WeightInfo for () { // Measured: `0` // Estimated: `0` // Minimum execution time: 7_245 nanoseconds. - Weight::from_ref_time(14_292_923) + Weight::from_parts(14_292_923, 0) // Standard Error: 1_803 - .saturating_add(Weight::from_ref_time(3_645_950).saturating_mul(c.into())) + .saturating_add(Weight::from_parts(3_645_950, 0).saturating_mul(c.into())) } } diff --git a/frame/vesting/src/weights.rs b/frame/vesting/src/weights.rs index 350e6a86e..de3260fa1 100644 --- a/frame/vesting/src/weights.rs +++ b/frame/vesting/src/weights.rs @@ -74,9 +74,9 @@ impl WeightInfo for SubstrateWeight { // Minimum execution time: 28_062 nanoseconds. Weight::from_parts(26_857_563, 7306) // Standard Error: 623 - .saturating_add(Weight::from_ref_time(55_988).saturating_mul(l.into())) + .saturating_add(Weight::from_parts(55_988, 0).saturating_mul(l.into())) // Standard Error: 1_109 - .saturating_add(Weight::from_ref_time(59_714).saturating_mul(s.into())) + .saturating_add(Weight::from_parts(59_714, 0).saturating_mul(s.into())) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().writes(2_u64)) } @@ -93,9 +93,9 @@ impl WeightInfo for SubstrateWeight { // Minimum execution time: 27_027 nanoseconds. Weight::from_parts(26_509_364, 7306) // Standard Error: 815 - .saturating_add(Weight::from_ref_time(54_711).saturating_mul(l.into())) + .saturating_add(Weight::from_parts(54_711, 0).saturating_mul(l.into())) // Standard Error: 1_451 - .saturating_add(Weight::from_ref_time(32_792).saturating_mul(s.into())) + .saturating_add(Weight::from_parts(32_792, 0).saturating_mul(s.into())) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().writes(2_u64)) } @@ -114,9 +114,9 @@ impl WeightInfo for SubstrateWeight { // Minimum execution time: 29_554 nanoseconds. Weight::from_parts(28_269_203, 9909) // Standard Error: 623 - .saturating_add(Weight::from_ref_time(59_058).saturating_mul(l.into())) + .saturating_add(Weight::from_parts(59_058, 0).saturating_mul(l.into())) // Standard Error: 1_108 - .saturating_add(Weight::from_ref_time(63_429).saturating_mul(s.into())) + .saturating_add(Weight::from_parts(63_429, 0).saturating_mul(s.into())) .saturating_add(T::DbWeight::get().reads(3_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) } @@ -135,9 +135,9 @@ impl WeightInfo for SubstrateWeight { // Minimum execution time: 28_546 nanoseconds. Weight::from_parts(28_299_251, 9909) // Standard Error: 786 - .saturating_add(Weight::from_ref_time(53_401).saturating_mul(l.into())) + .saturating_add(Weight::from_parts(53_401, 0).saturating_mul(l.into())) // Standard Error: 1_399 - .saturating_add(Weight::from_ref_time(29_713).saturating_mul(s.into())) + .saturating_add(Weight::from_parts(29_713, 0).saturating_mul(s.into())) .saturating_add(T::DbWeight::get().reads(3_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) } @@ -156,9 +156,9 @@ impl WeightInfo for SubstrateWeight { // Minimum execution time: 43_436 nanoseconds. Weight::from_parts(44_885_707, 9909) // Standard Error: 1_516 - .saturating_add(Weight::from_ref_time(59_066).saturating_mul(l.into())) + .saturating_add(Weight::from_parts(59_066, 0).saturating_mul(l.into())) // Standard Error: 2_698 - .saturating_add(Weight::from_ref_time(32_053).saturating_mul(s.into())) + .saturating_add(Weight::from_parts(32_053, 0).saturating_mul(s.into())) .saturating_add(T::DbWeight::get().reads(3_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) } @@ -177,9 +177,9 @@ impl WeightInfo for SubstrateWeight { // Minimum execution time: 45_805 nanoseconds. Weight::from_parts(46_869_490, 12512) // Standard Error: 1_445 - .saturating_add(Weight::from_ref_time(52_654).saturating_mul(l.into())) + .saturating_add(Weight::from_parts(52_654, 0).saturating_mul(l.into())) // Standard Error: 2_571 - .saturating_add(Weight::from_ref_time(34_202).saturating_mul(s.into())) + .saturating_add(Weight::from_parts(34_202, 0).saturating_mul(s.into())) .saturating_add(T::DbWeight::get().reads(4_u64)) .saturating_add(T::DbWeight::get().writes(4_u64)) } @@ -198,9 +198,9 @@ impl WeightInfo for SubstrateWeight { // Minimum execution time: 30_460 nanoseconds. Weight::from_parts(29_407_637, 9909) // Standard Error: 794 - .saturating_add(Weight::from_ref_time(63_757).saturating_mul(l.into())) + .saturating_add(Weight::from_parts(63_757, 0).saturating_mul(l.into())) // Standard Error: 1_466 - .saturating_add(Weight::from_ref_time(56_032).saturating_mul(s.into())) + .saturating_add(Weight::from_parts(56_032, 0).saturating_mul(s.into())) .saturating_add(T::DbWeight::get().reads(3_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) } @@ -219,9 +219,9 @@ impl WeightInfo for SubstrateWeight { // Minimum execution time: 30_413 nanoseconds. Weight::from_parts(29_350_467, 9909) // Standard Error: 724 - .saturating_add(Weight::from_ref_time(65_366).saturating_mul(l.into())) + .saturating_add(Weight::from_parts(65_366, 0).saturating_mul(l.into())) // Standard Error: 1_337 - .saturating_add(Weight::from_ref_time(53_799).saturating_mul(s.into())) + .saturating_add(Weight::from_parts(53_799, 0).saturating_mul(s.into())) .saturating_add(T::DbWeight::get().reads(3_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) } @@ -242,9 +242,9 @@ impl WeightInfo for () { // Minimum execution time: 28_062 nanoseconds. Weight::from_parts(26_857_563, 7306) // Standard Error: 623 - .saturating_add(Weight::from_ref_time(55_988).saturating_mul(l.into())) + .saturating_add(Weight::from_parts(55_988, 0).saturating_mul(l.into())) // Standard Error: 1_109 - .saturating_add(Weight::from_ref_time(59_714).saturating_mul(s.into())) + .saturating_add(Weight::from_parts(59_714, 0).saturating_mul(s.into())) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().writes(2_u64)) } @@ -261,9 +261,9 @@ impl WeightInfo for () { // Minimum execution time: 27_027 nanoseconds. Weight::from_parts(26_509_364, 7306) // Standard Error: 815 - .saturating_add(Weight::from_ref_time(54_711).saturating_mul(l.into())) + .saturating_add(Weight::from_parts(54_711, 0).saturating_mul(l.into())) // Standard Error: 1_451 - .saturating_add(Weight::from_ref_time(32_792).saturating_mul(s.into())) + .saturating_add(Weight::from_parts(32_792, 0).saturating_mul(s.into())) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().writes(2_u64)) } @@ -282,9 +282,9 @@ impl WeightInfo for () { // Minimum execution time: 29_554 nanoseconds. Weight::from_parts(28_269_203, 9909) // Standard Error: 623 - .saturating_add(Weight::from_ref_time(59_058).saturating_mul(l.into())) + .saturating_add(Weight::from_parts(59_058, 0).saturating_mul(l.into())) // Standard Error: 1_108 - .saturating_add(Weight::from_ref_time(63_429).saturating_mul(s.into())) + .saturating_add(Weight::from_parts(63_429, 0).saturating_mul(s.into())) .saturating_add(RocksDbWeight::get().reads(3_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) } @@ -303,9 +303,9 @@ impl WeightInfo for () { // Minimum execution time: 28_546 nanoseconds. Weight::from_parts(28_299_251, 9909) // Standard Error: 786 - .saturating_add(Weight::from_ref_time(53_401).saturating_mul(l.into())) + .saturating_add(Weight::from_parts(53_401, 0).saturating_mul(l.into())) // Standard Error: 1_399 - .saturating_add(Weight::from_ref_time(29_713).saturating_mul(s.into())) + .saturating_add(Weight::from_parts(29_713, 0).saturating_mul(s.into())) .saturating_add(RocksDbWeight::get().reads(3_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) } @@ -324,9 +324,9 @@ impl WeightInfo for () { // Minimum execution time: 43_436 nanoseconds. Weight::from_parts(44_885_707, 9909) // Standard Error: 1_516 - .saturating_add(Weight::from_ref_time(59_066).saturating_mul(l.into())) + .saturating_add(Weight::from_parts(59_066, 0).saturating_mul(l.into())) // Standard Error: 2_698 - .saturating_add(Weight::from_ref_time(32_053).saturating_mul(s.into())) + .saturating_add(Weight::from_parts(32_053, 0).saturating_mul(s.into())) .saturating_add(RocksDbWeight::get().reads(3_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) } @@ -345,9 +345,9 @@ impl WeightInfo for () { // Minimum execution time: 45_805 nanoseconds. Weight::from_parts(46_869_490, 12512) // Standard Error: 1_445 - .saturating_add(Weight::from_ref_time(52_654).saturating_mul(l.into())) + .saturating_add(Weight::from_parts(52_654, 0).saturating_mul(l.into())) // Standard Error: 2_571 - .saturating_add(Weight::from_ref_time(34_202).saturating_mul(s.into())) + .saturating_add(Weight::from_parts(34_202, 0).saturating_mul(s.into())) .saturating_add(RocksDbWeight::get().reads(4_u64)) .saturating_add(RocksDbWeight::get().writes(4_u64)) } @@ -366,9 +366,9 @@ impl WeightInfo for () { // Minimum execution time: 30_460 nanoseconds. Weight::from_parts(29_407_637, 9909) // Standard Error: 794 - .saturating_add(Weight::from_ref_time(63_757).saturating_mul(l.into())) + .saturating_add(Weight::from_parts(63_757, 0).saturating_mul(l.into())) // Standard Error: 1_466 - .saturating_add(Weight::from_ref_time(56_032).saturating_mul(s.into())) + .saturating_add(Weight::from_parts(56_032, 0).saturating_mul(s.into())) .saturating_add(RocksDbWeight::get().reads(3_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) } @@ -387,9 +387,9 @@ impl WeightInfo for () { // Minimum execution time: 30_413 nanoseconds. Weight::from_parts(29_350_467, 9909) // Standard Error: 724 - .saturating_add(Weight::from_ref_time(65_366).saturating_mul(l.into())) + .saturating_add(Weight::from_parts(65_366, 0).saturating_mul(l.into())) // Standard Error: 1_337 - .saturating_add(Weight::from_ref_time(53_799).saturating_mul(s.into())) + .saturating_add(Weight::from_parts(53_799, 0).saturating_mul(s.into())) .saturating_add(RocksDbWeight::get().reads(3_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) } diff --git a/frame/whitelist/src/tests.rs b/frame/whitelist/src/tests.rs index 986a09b8e..3a60adbcf 100644 --- a/frame/whitelist/src/tests.rs +++ b/frame/whitelist/src/tests.rs @@ -119,7 +119,7 @@ fn test_whitelist_call_and_execute() { RuntimeOrigin::root(), call_hash, call_encoded_len, - call_weight - Weight::from_ref_time(1) + call_weight - Weight::from_parts(1, 0) ), crate::Error::::InvalidCallWeightWitness, ); diff --git a/frame/whitelist/src/weights.rs b/frame/whitelist/src/weights.rs index 2b25e8194..667d602a3 100644 --- a/frame/whitelist/src/weights.rs +++ b/frame/whitelist/src/weights.rs @@ -97,10 +97,10 @@ impl WeightInfo for SubstrateWeight { // Minimum execution time: 27_539 nanoseconds. Weight::from_parts(27_950_000, 8007) // Standard Error: 0 - .saturating_add(Weight::from_ref_time(1_134).saturating_mul(n.into())) + .saturating_add(Weight::from_parts(1_134, 0).saturating_mul(n.into())) .saturating_add(T::DbWeight::get().reads(3_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) - .saturating_add(Weight::from_proof_size(1).saturating_mul(n.into())) + .saturating_add(Weight::from_parts(0, 1).saturating_mul(n.into())) } /// Storage: Whitelist WhitelistedCall (r:1 w:1) /// Proof: Whitelist WhitelistedCall (max_values: None, max_size: Some(40), added: 2515, mode: MaxEncodedLen) @@ -114,7 +114,7 @@ impl WeightInfo for SubstrateWeight { // Minimum execution time: 20_581 nanoseconds. Weight::from_parts(21_762_318, 5081) // Standard Error: 4 - .saturating_add(Weight::from_ref_time(1_480).saturating_mul(n.into())) + .saturating_add(Weight::from_parts(1_480, 0).saturating_mul(n.into())) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().writes(2_u64)) } @@ -162,10 +162,10 @@ impl WeightInfo for () { // Minimum execution time: 27_539 nanoseconds. Weight::from_parts(27_950_000, 8007) // Standard Error: 0 - .saturating_add(Weight::from_ref_time(1_134).saturating_mul(n.into())) + .saturating_add(Weight::from_parts(1_134, 0).saturating_mul(n.into())) .saturating_add(RocksDbWeight::get().reads(3_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) - .saturating_add(Weight::from_proof_size(1).saturating_mul(n.into())) + .saturating_add(Weight::from_parts(0, 1).saturating_mul(n.into())) } /// Storage: Whitelist WhitelistedCall (r:1 w:1) /// Proof: Whitelist WhitelistedCall (max_values: None, max_size: Some(40), added: 2515, mode: MaxEncodedLen) @@ -179,7 +179,7 @@ impl WeightInfo for () { // Minimum execution time: 20_581 nanoseconds. Weight::from_parts(21_762_318, 5081) // Standard Error: 4 - .saturating_add(Weight::from_ref_time(1_480).saturating_mul(n.into())) + .saturating_add(Weight::from_parts(1_480, 0).saturating_mul(n.into())) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().writes(2_u64)) } diff --git a/primitives/weights/src/lib.rs b/primitives/weights/src/lib.rs index acfcae1df..8a8423073 100644 --- a/primitives/weights/src/lib.rs +++ b/primitives/weights/src/lib.rs @@ -82,17 +82,17 @@ pub struct RuntimeDbWeight { impl RuntimeDbWeight { pub fn reads(self, r: u64) -> Weight { - Weight::from_ref_time(self.read.saturating_mul(r)) + Weight::from_parts(self.read.saturating_mul(r), 0) } pub fn writes(self, w: u64) -> Weight { - Weight::from_ref_time(self.write.saturating_mul(w)) + Weight::from_parts(self.write.saturating_mul(w), 0) } pub fn reads_writes(self, r: u64, w: u64) -> Weight { let read_weight = self.read.saturating_mul(r); let write_weight = self.write.saturating_mul(w); - Weight::from_ref_time(read_weight.saturating_add(write_weight)) + Weight::from_parts(read_weight.saturating_add(write_weight), 0) } } @@ -264,15 +264,15 @@ mod tests { #[test] fn polynomial_works() { // 100^3/2=500000 100^2*(2+1/3)=23333 700 -10000 - assert_eq!(Poly::weight_to_fee(&Weight::from_ref_time(100)), 514033); + assert_eq!(Poly::weight_to_fee(&Weight::from_parts(100, 0)), 514033); // 10123^3/2=518677865433 10123^2*(2+1/3)=239108634 70861 -10000 - assert_eq!(Poly::weight_to_fee(&Weight::from_ref_time(10_123)), 518917034928); + assert_eq!(Poly::weight_to_fee(&Weight::from_parts(10_123, 0)), 518917034928); } #[test] fn polynomial_does_not_underflow() { assert_eq!(Poly::weight_to_fee(&Weight::zero()), 0); - assert_eq!(Poly::weight_to_fee(&Weight::from_ref_time(10)), 0); + assert_eq!(Poly::weight_to_fee(&Weight::from_parts(10, 0)), 0); } #[test] @@ -283,7 +283,7 @@ mod tests { #[test] fn identity_fee_works() { assert_eq!(IdentityFee::::weight_to_fee(&Weight::zero()), 0); - assert_eq!(IdentityFee::::weight_to_fee(&Weight::from_ref_time(50)), 50); + assert_eq!(IdentityFee::::weight_to_fee(&Weight::from_parts(50, 0)), 50); assert_eq!(IdentityFee::::weight_to_fee(&Weight::MAX), Balance::max_value()); } @@ -295,20 +295,20 @@ mod tests { 0 ); assert_eq!( - ConstantMultiplier::>::weight_to_fee(&Weight::from_ref_time( - 50 + ConstantMultiplier::>::weight_to_fee(&Weight::from_parts( + 50, 0 )), 500 ); assert_eq!( - ConstantMultiplier::>::weight_to_fee(&Weight::from_ref_time( - 16 + ConstantMultiplier::>::weight_to_fee(&Weight::from_parts( + 16, 0 )), 16384 ); assert_eq!( ConstantMultiplier::>::weight_to_fee( - &Weight::from_ref_time(2) + &Weight::from_parts(2, 0) ), u128::MAX ); diff --git a/primitives/weights/src/weight_v2.rs b/primitives/weights/src/weight_v2.rs index f6e6164b5..ca1371459 100644 --- a/primitives/weights/src/weight_v2.rs +++ b/primitives/weights/src/weight_v2.rs @@ -37,7 +37,7 @@ pub struct Weight { impl From for Weight { fn from(old: OldWeight) -> Self { - Weight::from_ref_time(old.0) + Weight::from_parts(old.0, 0) } } @@ -103,11 +103,13 @@ impl Weight { } /// Construct [`Weight`] with reference time weight and 0 storage size weight. + #[deprecated = "Will be removed soon; use `from_parts` instead."] pub const fn from_ref_time(ref_time: u64) -> Self { Self { ref_time, proof_size: 0 } } /// Construct [`Weight`] with storage size weight and 0 reference time weight. + #[deprecated = "Will be removed soon; use `from_parts` instead."] pub const fn from_proof_size(proof_size: u64) -> Self { Self { ref_time: 0, proof_size } } @@ -238,7 +240,7 @@ impl Weight { /// of all those divisions. Returns `None` in case **all** components of `other` are zero. /// /// This returns `Some` even if some components of `other` are zero as long as there is at least - /// one non-zero component in `other`. The devision for this particular component will then + /// one non-zero component in `other`. The division for this particular component will then /// yield the maximum value (e.g u64::MAX). This is because we assume not every operation and /// hence each `Weight` will necessarily use each resource. pub const fn checked_div_per_component(self, other: &Self) -> Option { diff --git a/test-utils/runtime/src/lib.rs b/test-utils/runtime/src/lib.rs index 87a60acfc..1ea8b8663 100644 --- a/test-utils/runtime/src/lib.rs +++ b/test-utils/runtime/src/lib.rs @@ -601,7 +601,7 @@ parameter_types! { pub RuntimeBlockLength: BlockLength = BlockLength::max(4 * 1024 * 1024); pub RuntimeBlockWeights: BlockWeights = - BlockWeights::with_sensible_defaults(Weight::from_ref_time(4 * 1024 * 1024), Perbill::from_percent(75)); + BlockWeights::with_sensible_defaults(Weight::from_parts(4 * 1024 * 1024, 0), Perbill::from_percent(75)); } impl From> for Extrinsic { diff --git a/utils/frame/benchmarking-cli/src/overhead/README.md b/utils/frame/benchmarking-cli/src/overhead/README.md index 1584c2aff..85bcc7fa3 100644 --- a/utils/frame/benchmarking-cli/src/overhead/README.md +++ b/utils/frame/benchmarking-cli/src/overhead/README.md @@ -31,7 +31,7 @@ The file will contain the concrete weight value and various statistics about the /// 95th: 3_595_674 /// 75th: 3_526_435 pub const BlockExecutionWeight: Weight = - Weight::from_ref_time(WEIGHT_REF_TIME_PER_NANOS.saturating_mul(3_532_484)); + Weight::from_parts(WEIGHT_REF_TIME_PER_NANOS.saturating_mul(3_532_484), 0); ``` In this example it takes 3.5 ms to execute an empty block. That means that it always takes at least 3.5 ms to execute *any* block. @@ -61,7 +61,7 @@ The relevant section in the output file looks like this: /// 95th: 67_843 /// 75th: 67_749 pub const ExtrinsicBaseWeight: Weight = - Weight::from_ref_time(WEIGHT_REF_TIME_PER_NANOS.saturating_mul(67_745)); + Weight::from_parts(WEIGHT_REF_TIME_PER_NANOS.saturating_mul(67_745), 0); ``` In this example it takes 67.7 µs to execute a NO-OP extrinsic. That means that it always takes at least 67.7 µs to execute *any* extrinsic. diff --git a/utils/frame/benchmarking-cli/src/overhead/weights.hbs b/utils/frame/benchmarking-cli/src/overhead/weights.hbs index c54393d20..6e364facc 100644 --- a/utils/frame/benchmarking-cli/src/overhead/weights.hbs +++ b/utils/frame/benchmarking-cli/src/overhead/weights.hbs @@ -35,7 +35,7 @@ parameter_types! { /// 95th: {{underscore stats.p95}} /// 75th: {{underscore stats.p75}} pub const {{long_name}}Weight: Weight = - Weight::from_ref_time(WEIGHT_REF_TIME_PER_NANOS.saturating_mul({{underscore weight}})); + Weight::from_parts(WEIGHT_REF_TIME_PER_NANOS.saturating_mul({{underscore weight}}), 0); } #[cfg(test)] diff --git a/utils/frame/benchmarking-cli/src/pallet/template.hbs b/utils/frame/benchmarking-cli/src/pallet/template.hbs index 0bec34eaa..88d6b69a6 100644 --- a/utils/frame/benchmarking-cli/src/pallet/template.hbs +++ b/utils/frame/benchmarking-cli/src/pallet/template.hbs @@ -37,12 +37,12 @@ impl {{pallet}}::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `{{benchmark.base_recorded_proof_size}}{{#each benchmark.component_recorded_proof_size as |cp|}} + {{cp.name}} * ({{cp.slope}} ±{{underscore cp.error}}){{/each}}` // Estimated: `{{benchmark.base_calculated_proof_size}}{{#each benchmark.component_calculated_proof_size as |cp|}} + {{cp.name}} * ({{cp.slope}} ±{{underscore cp.error}}){{/each}}` - // Minimum execution time: {{underscore benchmark.min_execution_time}} nanoseconds. - Weight::from_ref_time({{underscore benchmark.base_weight}}) - .saturating_add(Weight::from_proof_size({{benchmark.base_calculated_proof_size}})) + // Minimum execution time: {{underscore benchmark.min_execution_time}}_000 picoseconds. + Weight::from_parts({{underscore benchmark.base_weight}}, 0) + .saturating_add(Weight::from_parts(0, {{benchmark.base_calculated_proof_size}})) {{#each benchmark.component_weight as |cw|}} // Standard Error: {{underscore cw.error}} - .saturating_add(Weight::from_ref_time({{underscore cw.slope}}).saturating_mul({{cw.name}}.into())) + .saturating_add(Weight::from_parts({{underscore cw.slope}}, 0).saturating_mul({{cw.name}}.into())) {{/each}} {{#if (ne benchmark.base_reads "0")}} .saturating_add(T::DbWeight::get().reads({{benchmark.base_reads}})) @@ -57,7 +57,7 @@ impl {{pallet}}::WeightInfo for WeightInfo { .saturating_add(T::DbWeight::get().writes(({{cw.slope}}_u64).saturating_mul({{cw.name}}.into()))) {{/each}} {{#each benchmark.component_calculated_proof_size as |cp|}} - .saturating_add(Weight::from_proof_size({{cp.slope}}).saturating_mul({{cp.name}}.into())) + .saturating_add(Weight::from_parts(0, {{cp.slope}}).saturating_mul({{cp.name}}.into())) {{/each}} } {{/each}} From 6f923eedcac0fba4fd6b73e8792520f69722e527 Mon Sep 17 00:00:00 2001 From: Robert Hambrock Date: Fri, 3 Mar 2023 11:23:15 +0100 Subject: [PATCH 186/558] bump API versions of {Beefy,Mmr}Api (#13509) --- primitives/consensus/beefy/src/lib.rs | 1 + primitives/merkle-mountain-range/src/lib.rs | 1 + 2 files changed, 2 insertions(+) diff --git a/primitives/consensus/beefy/src/lib.rs b/primitives/consensus/beefy/src/lib.rs index 2e3ec2ef8..cc5d1e8cb 100644 --- a/primitives/consensus/beefy/src/lib.rs +++ b/primitives/consensus/beefy/src/lib.rs @@ -302,6 +302,7 @@ impl OpaqueKeyOwnershipProof { sp_api::decl_runtime_apis! { /// API necessary for BEEFY voters. + #[api_version(2)] pub trait BeefyApi { /// Return the block number where BEEFY consensus is enabled/started diff --git a/primitives/merkle-mountain-range/src/lib.rs b/primitives/merkle-mountain-range/src/lib.rs index 1c319c4e3..436755c03 100644 --- a/primitives/merkle-mountain-range/src/lib.rs +++ b/primitives/merkle-mountain-range/src/lib.rs @@ -422,6 +422,7 @@ impl Error { sp_api::decl_runtime_apis! { /// API to interact with MMR pallet. + #[api_version(2)] pub trait MmrApi { /// Return the on-chain MMR root hash. fn mmr_root() -> Result; From e8f5bc176ca33bc5a2fd375e64c67b91ce16bbc9 Mon Sep 17 00:00:00 2001 From: Jegor Sidorenko <5252494+jsidorenko@users.noreply.github.com> Date: Fri, 3 Mar 2023 14:17:55 +0200 Subject: [PATCH 187/558] [NFTs] Emit new PalletAttributeSet event (#13525) * Emit new PalletAttributeSet event * Chore --- frame/nfts/src/lib.rs | 33 ++++++++++++++++++++++++--------- 1 file changed, 24 insertions(+), 9 deletions(-) diff --git a/frame/nfts/src/lib.rs b/frame/nfts/src/lib.rs index 2c3ab290c..681d90842 100644 --- a/frame/nfts/src/lib.rs +++ b/frame/nfts/src/lib.rs @@ -532,6 +532,14 @@ pub mod pallet { item: T::ItemId, namespace: AttributeNamespace, }, + /// A new attribute in the `Pallet` namespace was set for the `collection` or an `item` + /// within that `collection`. + PalletAttributeSet { + collection: T::CollectionId, + item: Option, + attribute: PalletAttributes, + value: BoundedVec, + }, } #[pallet::error] @@ -804,32 +812,39 @@ pub mod pallet { let MintWitness { owner_of_item } = witness_data.ok_or(Error::::BadWitness)?; - let has_item = Account::::contains_key(( + let owns_item = Account::::contains_key(( &caller, &collection_id, &owner_of_item, )); - ensure!(has_item, Error::::BadWitness); + ensure!(owns_item, Error::::BadWitness); - let attribute_key = Self::construct_attribute_key( - PalletAttributes::::UsedToClaim(collection) - .encode(), - )?; + let pallet_attribute = + PalletAttributes::::UsedToClaim(collection); let key = ( &collection_id, Some(owner_of_item), AttributeNamespace::Pallet, - &attribute_key, + &Self::construct_attribute_key(pallet_attribute.encode())?, ); let already_claimed = Attribute::::contains_key(key.clone()); ensure!(!already_claimed, Error::::AlreadyClaimed); - let value = Self::construct_attribute_value(vec![0])?; + let attribute_value = Self::construct_attribute_value(vec![])?; Attribute::::insert( key, - (value, AttributeDeposit { account: None, amount: Zero::zero() }), + ( + attribute_value.clone(), + AttributeDeposit { account: None, amount: Zero::zero() }, + ), ); + Self::deposit_event(Event::PalletAttributeSet { + collection, + item: Some(item), + attribute: pallet_attribute, + value: attribute_value, + }); }, _ => {}, } From 96a6cd36d526439cea9bddebcd3286d0070fb7d7 Mon Sep 17 00:00:00 2001 From: abebeos <110243666+abebeos@users.noreply.github.com> Date: Fri, 3 Mar 2023 16:53:22 +0200 Subject: [PATCH 188/558] fix feature/enhancement issue template (#13522) --- .github/ISSUE_TEMPLATE/feature.yaml | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/.github/ISSUE_TEMPLATE/feature.yaml b/.github/ISSUE_TEMPLATE/feature.yaml index 92b2fea3e..6a59522ab 100644 --- a/.github/ISSUE_TEMPLATE/feature.yaml +++ b/.github/ISSUE_TEMPLATE/feature.yaml @@ -1,7 +1,9 @@ name: Feature Request description: Submit your requests and suggestions to improve! +labels: ["J0-enhancement"] body: - type: checkboxes + id: existing attributes: label: Is there an existing issue? description: Please search to see if an issue already exists and leave a comment that you also experienced this issue or add your specifics that are related to an existing issue. @@ -9,6 +11,7 @@ body: - label: I have searched the existing issues required: true - type: checkboxes + id: stackexchange attributes: label: Experiencing problems? Have you tried our Stack Exchange first? description: Please search to see if an post already exists, and ask if not. Please do not file support issues here. @@ -16,7 +19,7 @@ body: - label: This is not a support question. required: true - type: textarea - id: content + id: motivation attributes: label: Motivation description: Please give precedence as to what lead you to file this issue. @@ -24,7 +27,7 @@ body: validations: required: false - type: textarea - id: content + id: request attributes: label: Request description: Please describe what is needed. @@ -32,7 +35,7 @@ body: validations: required: true - type: textarea - id: content + id: solution attributes: label: Solution description: If possible, please describe what a solution could be. From 2cb4825ea0d196d6e23e8e908bc4986413bd5b9f Mon Sep 17 00:00:00 2001 From: Arkadiy Paronyan Date: Fri, 3 Mar 2023 18:16:59 +0100 Subject: [PATCH 189/558] Bump parity-db (#13521) --- Cargo.lock | 21 ++++++++++++++------- bin/node/bench/Cargo.toml | 2 +- client/db/Cargo.toml | 2 +- 3 files changed, 16 insertions(+), 9 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index b7c7a813f..79e528b60 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3310,9 +3310,9 @@ checksum = "fad582f4b9e86b6caa621cabeb0963332d92eea04729ab12892c2533951e6440" [[package]] name = "jobserver" -version = "0.1.25" +version = "0.1.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "068b1ee6743e4d11fb9c6a1e6064b3693a1b600e7f5f5988047d98b3dc9fb90b" +checksum = "936cfd212a0155903bcbc060e316fb6cc7cbf2e1907329391ebadc1fe0ce77c2" dependencies = [ "libc", ] @@ -4348,9 +4348,9 @@ dependencies = [ [[package]] name = "memmap2" -version = "0.5.8" +version = "0.5.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4b182332558b18d807c4ce1ca8ca983b34c3ee32765e47b3f0f69b90355cc1dc" +checksum = "83faa42c0a078c393f6b29d5db232d8be22776a891f8f56e5284faee4a20b327" dependencies = [ "libc", ] @@ -6760,9 +6760,9 @@ dependencies = [ [[package]] name = "parity-db" -version = "0.4.3" +version = "0.4.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dd684a725651d9588ef21f140a328b6b4f64e646b2e931f3e6f14f75eedf9980" +checksum = "df89dd8311063c54ae4e03d9aeb597b04212a57e82c339344130a9cad9b3e2d9" dependencies = [ "blake2", "crc32fast", @@ -6774,6 +6774,7 @@ dependencies = [ "memmap2", "parking_lot 0.12.1", "rand 0.8.5", + "siphasher", "snap", ] @@ -9660,6 +9661,12 @@ dependencies = [ "wide", ] +[[package]] +name = "siphasher" +version = "0.3.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7bd3e3206899af3f8b12af284fafc038cc1dc2b41d1b89dd17297221c5d225de" + [[package]] name = "slab" version = "0.4.7" @@ -11629,7 +11636,7 @@ checksum = "97fee6b57c6a41524a810daee9286c02d7752c4253064d0b05472833a438f675" dependencies = [ "cfg-if", "digest 0.10.6", - "rand 0.8.5", + "rand 0.7.3", "static_assertions", ] diff --git a/bin/node/bench/Cargo.toml b/bin/node/bench/Cargo.toml index 0097b51ab..dfff4db43 100644 --- a/bin/node/bench/Cargo.toml +++ b/bin/node/bench/Cargo.toml @@ -38,7 +38,7 @@ tempfile = "3.1.0" fs_extra = "1" rand = { version = "0.8.5", features = ["small_rng"] } lazy_static = "1.4.0" -parity-db = "0.4.3" +parity-db = "0.4.4" sc-transaction-pool = { version = "4.0.0-dev", path = "../../../client/transaction-pool" } sc-transaction-pool-api = { version = "4.0.0-dev", path = "../../../client/transaction-pool/api" } futures = { version = "0.3.21", features = ["thread-pool"] } diff --git a/client/db/Cargo.toml b/client/db/Cargo.toml index 73eca1dff..ed26f3737 100644 --- a/client/db/Cargo.toml +++ b/client/db/Cargo.toml @@ -22,7 +22,7 @@ kvdb-memorydb = "0.13.0" kvdb-rocksdb = { version = "0.17.0", optional = true } linked-hash-map = "0.5.4" log = "0.4.17" -parity-db = "0.4.3" +parity-db = "0.4.4" parking_lot = "0.12.1" sc-client-api = { version = "4.0.0-dev", path = "../api" } sc-state-db = { version = "0.10.0-dev", path = "../state-db" } From 48c9de8902d9d7e240fe6b3d29f8adcf522d605c Mon Sep 17 00:00:00 2001 From: Gavin Wood Date: Fri, 3 Mar 2023 19:05:01 +0000 Subject: [PATCH 190/558] Salary pallet (#13378) * More drafting * Paymaster pallet * Fix build * More tests * Rename * Rename * Renaming * Revert old changes * Multi-phase payouts to avoid bank-runs * Tests * Tests * Allow payment to be targeted elsewhere * Proper ssync payment failure handling * Test for repayment * Docs * Impl RankedMembers for RankedCollective * Implement Pay for Pot (i.e. basic account). * Benchmarks * Weights * Introduce Salary benchmark into node * Fix warning * ".git/.scripts/commands/bench/bench.sh" pallet dev pallet_salary * Update primitives/arithmetic/src/traits.rs Co-authored-by: Jegor Sidorenko <5252494+jsidorenko@users.noreply.github.com> * Update frame/salary/src/lib.rs Co-authored-by: Jegor Sidorenko <5252494+jsidorenko@users.noreply.github.com> * Update lib.rs * Update frame/salary/src/lib.rs Co-authored-by: joe petrowski <25483142+joepetrowski@users.noreply.github.com> * Docs * Update frame/salary/src/lib.rs Co-authored-by: joe petrowski <25483142+joepetrowski@users.noreply.github.com> * Update frame/salary/src/lib.rs Co-authored-by: joe petrowski <25483142+joepetrowski@users.noreply.github.com> * Fix * Fixes * Fixes * Move some salary traits stuff to a shared location * Fix * Update frame/salary/src/lib.rs Co-authored-by: Oliver Tale-Yazdi * Update frame/salary/src/lib.rs Co-authored-by: Oliver Tale-Yazdi * Mul floor * Fix warnings * Fix test * Docs --------- Co-authored-by: command-bot <> Co-authored-by: Jegor Sidorenko <5252494+jsidorenko@users.noreply.github.com> Co-authored-by: joe petrowski <25483142+joepetrowski@users.noreply.github.com> Co-authored-by: Oliver Tale-Yazdi --- .gitignore | 1 + Cargo.lock | 18 + Cargo.toml | 1 + bin/node/runtime/Cargo.toml | 4 + bin/node/runtime/src/lib.rs | 34 +- frame/ranked-collective/src/lib.rs | 75 ++- frame/salary/Cargo.toml | 49 ++ frame/salary/README.md | 3 + frame/salary/src/benchmarking.rs | 183 +++++++ frame/salary/src/lib.rs | 514 +++++++++++++++++++ frame/salary/src/tests.rs | 641 ++++++++++++++++++++++++ frame/salary/src/weights.rs | 276 ++++++++++ frame/support/src/traits.rs | 2 +- frame/support/src/traits/members.rs | 24 + frame/support/src/traits/tokens.rs | 5 +- frame/support/src/traits/tokens/misc.rs | 17 +- primitives/arithmetic/src/traits.rs | 14 + 17 files changed, 1833 insertions(+), 28 deletions(-) create mode 100644 frame/salary/Cargo.toml create mode 100644 frame/salary/README.md create mode 100644 frame/salary/src/benchmarking.rs create mode 100644 frame/salary/src/lib.rs create mode 100644 frame/salary/src/tests.rs create mode 100644 frame/salary/src/weights.rs diff --git a/.gitignore b/.gitignore index 5cd013e05..efb63520e 100644 --- a/.gitignore +++ b/.gitignore @@ -25,3 +25,4 @@ rls*.log *.bin *.iml scripts/ci/node-template-release/Cargo.lock +substrate.code-workspace diff --git a/Cargo.lock b/Cargo.lock index 79e528b60..d67aeda9f 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3542,6 +3542,7 @@ dependencies = [ "pallet-referenda", "pallet-remark", "pallet-root-testing", + "pallet-salary", "pallet-scheduler", "pallet-session", "pallet-session-benchmarking", @@ -6362,6 +6363,23 @@ dependencies = [ "sp-std", ] +[[package]] +name = "pallet-salary" +version = "4.0.0-dev" +dependencies = [ + "frame-benchmarking", + "frame-support", + "frame-system", + "log", + "parity-scale-codec", + "scale-info", + "sp-arithmetic", + "sp-core", + "sp-io", + "sp-runtime", + "sp-std", +] + [[package]] name = "pallet-scheduler" version = "4.0.0-dev" diff --git a/Cargo.toml b/Cargo.toml index af2d8f906..0b490bd17 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -134,6 +134,7 @@ members = [ "frame/recovery", "frame/referenda", "frame/remark", + "frame/salary", "frame/scheduler", "frame/scored-pool", "frame/session", diff --git a/bin/node/runtime/Cargo.toml b/bin/node/runtime/Cargo.toml index 993108bf7..a610bbe5d 100644 --- a/bin/node/runtime/Cargo.toml +++ b/bin/node/runtime/Cargo.toml @@ -96,6 +96,7 @@ pallet-recovery = { version = "4.0.0-dev", default-features = false, path = "../ pallet-referenda = { version = "4.0.0-dev", default-features = false, path = "../../../frame/referenda" } pallet-remark = { version = "4.0.0-dev", default-features = false, path = "../../../frame/remark" } pallet-root-testing = { version = "1.0.0-dev", default-features = false, path = "../../../frame/root-testing" } +pallet-salary = { version = "4.0.0-dev", default-features = false, path = "../../../frame/salary" } pallet-session = { version = "4.0.0-dev", features = [ "historical" ], path = "../../../frame/session", default-features = false } pallet-session-benchmarking = { version = "4.0.0-dev", path = "../../../frame/session/benchmarking", default-features = false, optional = true } pallet-staking = { version = "4.0.0-dev", default-features = false, path = "../../../frame/staking" } @@ -183,6 +184,7 @@ std = [ "pallet-staking/std", "pallet-staking-runtime-api/std", "pallet-state-trie-migration/std", + "pallet-salary/std", "sp-session/std", "pallet-sudo/std", "frame-support/std", @@ -258,6 +260,7 @@ runtime-benchmarks = [ "pallet-referenda/runtime-benchmarks", "pallet-recovery/runtime-benchmarks", "pallet-remark/runtime-benchmarks", + "pallet-salary/runtime-benchmarks", "pallet-session-benchmarking/runtime-benchmarks", "pallet-society/runtime-benchmarks", "pallet-staking/runtime-benchmarks", @@ -316,6 +319,7 @@ try-runtime = [ "pallet-referenda/try-runtime", "pallet-remark/try-runtime", "pallet-root-testing/try-runtime", + "pallet-salary/try-runtime", "pallet-session/try-runtime", "pallet-staking/try-runtime", "pallet-state-trie-migration/try-runtime", diff --git a/bin/node/runtime/src/lib.rs b/bin/node/runtime/src/lib.rs index 9b317926a..0c21fcc90 100644 --- a/bin/node/runtime/src/lib.rs +++ b/bin/node/runtime/src/lib.rs @@ -32,10 +32,11 @@ use frame_support::{ pallet_prelude::Get, parameter_types, traits::{ - fungible::ItemOf, tokens::nonfungibles_v2::Inspect, AsEnsureOriginWithArg, ConstBool, - ConstU128, ConstU16, ConstU32, Currency, EitherOfDiverse, EqualPrivilegeOnly, Everything, - Imbalance, InstanceFilter, KeyOwnerProofSystem, LockIdentifier, Nothing, OnUnbalanced, - U128CurrencyToVote, WithdrawReasons, + fungible::ItemOf, + tokens::{nonfungibles_v2::Inspect, GetSalary}, + AsEnsureOriginWithArg, ConstBool, ConstU128, ConstU16, ConstU32, Currency, EitherOfDiverse, + EqualPrivilegeOnly, Everything, Imbalance, InstanceFilter, KeyOwnerProofSystem, + LockIdentifier, Nothing, OnUnbalanced, U128CurrencyToVote, WithdrawReasons, }, weights::{ constants::{ @@ -1571,6 +1572,29 @@ impl pallet_uniques::Config for Runtime { type Locker = (); } +parameter_types! { + pub const Budget: Balance = 10_000 * DOLLARS; + pub TreasuryAccount: AccountId = Treasury::account_id(); +} + +pub struct SalaryForRank; +impl GetSalary for SalaryForRank { + fn get_salary(a: u16, _: &AccountId) -> Balance { + Balance::from(a) * 1000 * DOLLARS + } +} + +impl pallet_salary::Config for Runtime { + type WeightInfo = (); + type RuntimeEvent = RuntimeEvent; + type Paymaster = pallet_salary::PayFromAccount; + type Members = RankedCollective; + type Salary = SalaryForRank; + type RegistrationPeriod = ConstU32<200>; + type PayoutPeriod = ConstU32<200>; + type Budget = Budget; +} + parameter_types! { pub Features: PalletFeatures = PalletFeatures::all_enabled(); pub const MaxAttributesPerCall: u32 = 10; @@ -1766,6 +1790,7 @@ construct_runtime!( Nis: pallet_nis, Uniques: pallet_uniques, Nfts: pallet_nfts, + Salary: pallet_salary, TransactionStorage: pallet_transaction_storage, VoterList: pallet_bags_list::, StateTrieMigration: pallet_state_trie_migration, @@ -1885,6 +1910,7 @@ mod benches { [pallet_referenda, Referenda] [pallet_recovery, Recovery] [pallet_remark, Remark] + [pallet_salary, Salary] [pallet_scheduler, Scheduler] [pallet_glutton, Glutton] [pallet_session, SessionBench::] diff --git a/frame/ranked-collective/src/lib.rs b/frame/ranked-collective/src/lib.rs index 66af9bf6e..8ed1bbd2e 100644 --- a/frame/ranked-collective/src/lib.rs +++ b/frame/ranked-collective/src/lib.rs @@ -54,7 +54,7 @@ use frame_support::{ codec::{Decode, Encode, MaxEncodedLen}, dispatch::{DispatchError, DispatchResultWithPostInfo, PostDispatchInfo}, ensure, - traits::{EnsureOrigin, PollStatus, Polling, VoteTally}, + traits::{EnsureOrigin, PollStatus, Polling, RankedMembers, VoteTally}, CloneNoBound, EqNoBound, PartialEqNoBound, RuntimeDebugNoBound, }; @@ -472,24 +472,7 @@ pub mod pallet { pub fn demote_member(origin: OriginFor, who: AccountIdLookupOf) -> DispatchResult { let max_rank = T::DemoteOrigin::ensure_origin(origin)?; let who = T::Lookup::lookup(who)?; - let mut record = Self::ensure_member(&who)?; - let rank = record.rank; - ensure!(max_rank >= rank, Error::::NoPermission); - - Self::remove_from_rank(&who, rank)?; - let maybe_rank = rank.checked_sub(1); - match maybe_rank { - None => { - Members::::remove(&who); - Self::deposit_event(Event::MemberRemoved { who, rank: 0 }); - }, - Some(rank) => { - record.rank = rank; - Members::::insert(&who, &record); - Self::deposit_event(Event::RankChanged { who, rank }); - }, - } - Ok(()) + Self::do_demote_member(who, Some(max_rank)) } /// Remove the member entirely. @@ -659,7 +642,7 @@ pub mod pallet { Ok(()) } - /// Promotes a member in the ranked collective into the next role. + /// Promotes a member in the ranked collective into the next higher rank. /// /// A `maybe_max_rank` may be provided to check that the member does not get promoted beyond /// a certain rank. Is `None` is provided, then the rank will be incremented without checks. @@ -681,6 +664,33 @@ pub mod pallet { Ok(()) } + /// Demotes a member in the ranked collective into the next lower rank. + /// + /// A `maybe_max_rank` may be provided to check that the member does not get demoted from + /// a certain rank. Is `None` is provided, then the rank will be decremented without checks. + fn do_demote_member(who: T::AccountId, maybe_max_rank: Option) -> DispatchResult { + let mut record = Self::ensure_member(&who)?; + let rank = record.rank; + if let Some(max_rank) = maybe_max_rank { + ensure!(max_rank >= rank, Error::::NoPermission); + } + + Self::remove_from_rank(&who, rank)?; + let maybe_rank = rank.checked_sub(1); + match maybe_rank { + None => { + Members::::remove(&who); + Self::deposit_event(Event::MemberRemoved { who, rank: 0 }); + }, + Some(rank) => { + record.rank = rank; + Members::::insert(&who, &record); + Self::deposit_event(Event::RankChanged { who, rank }); + }, + } + Ok(()) + } + /// Add a member to the rank collective, and continue to promote them until a certain rank /// is reached. pub fn do_add_member_to_rank(who: T::AccountId, rank: Rank) -> DispatchResult { @@ -691,4 +701,29 @@ pub mod pallet { Ok(()) } } + + impl, I: 'static> RankedMembers for Pallet { + type AccountId = T::AccountId; + type Rank = Rank; + + fn min_rank() -> Self::Rank { + 0 + } + + fn rank_of(who: &Self::AccountId) -> Option { + Some(Self::ensure_member(&who).ok()?.rank) + } + + fn induct(who: &Self::AccountId) -> DispatchResult { + Self::do_add_member(who.clone()) + } + + fn promote(who: &Self::AccountId) -> DispatchResult { + Self::do_promote_member(who.clone(), None) + } + + fn demote(who: &Self::AccountId) -> DispatchResult { + Self::do_demote_member(who.clone(), None) + } + } } diff --git a/frame/salary/Cargo.toml b/frame/salary/Cargo.toml new file mode 100644 index 000000000..86cae16bb --- /dev/null +++ b/frame/salary/Cargo.toml @@ -0,0 +1,49 @@ +[package] +name = "pallet-salary" +version = "4.0.0-dev" +authors = ["Parity Technologies "] +edition = "2021" +license = "Apache-2.0" +homepage = "https://substrate.io" +repository = "https://github.com/paritytech/substrate/" +description = "Paymaster" +readme = "README.md" + +[package.metadata.docs.rs] +targets = ["x86_64-unknown-linux-gnu"] + +[dependencies] +codec = { package = "parity-scale-codec", version = "3.0.0", default-features = false, features = ["derive"] } +log = { version = "0.4.16", default-features = false } +scale-info = { version = "2.0.1", default-features = false, features = ["derive"] } +frame-benchmarking = { version = "4.0.0-dev", default-features = false, optional = true, path = "../benchmarking" } +frame-support = { version = "4.0.0-dev", default-features = false, path = "../support" } +frame-system = { version = "4.0.0-dev", default-features = false, path = "../system" } +sp-arithmetic = { version = "6.0.0", default-features = false, path = "../../primitives/arithmetic" } +sp-core = { version = "7.0.0", default-features = false, path = "../../primitives/core" } +sp-io = { version = "7.0.0", default-features = false, path = "../../primitives/io" } +sp-runtime = { version = "7.0.0", default-features = false, path = "../../primitives/runtime" } +sp-std = { version = "5.0.0", default-features = false, path = "../../primitives/std" } + +[features] +default = ["std"] +std = [ + "codec/std", + "frame-benchmarking?/std", + "frame-support/std", + "frame-system/std", + "log/std", + "scale-info/std", + "sp-arithmetic/std", + "sp-core/std", + "sp-io/std", + "sp-runtime/std", + "sp-std/std", +] +runtime-benchmarks = [ + "frame-benchmarking/runtime-benchmarks", + "frame-support/runtime-benchmarks", + "frame-system/runtime-benchmarks", + "sp-runtime/runtime-benchmarks", +] +try-runtime = ["frame-support/try-runtime"] diff --git a/frame/salary/README.md b/frame/salary/README.md new file mode 100644 index 000000000..25c1be0e8 --- /dev/null +++ b/frame/salary/README.md @@ -0,0 +1,3 @@ +# Salary + +Make periodic payment to members of a ranked collective according to rank. \ No newline at end of file diff --git a/frame/salary/src/benchmarking.rs b/frame/salary/src/benchmarking.rs new file mode 100644 index 000000000..339185b37 --- /dev/null +++ b/frame/salary/src/benchmarking.rs @@ -0,0 +1,183 @@ +// This file is part of Substrate. + +// Copyright (C) 2020-2022 Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +//! Salary pallet benchmarking. + +#![cfg(feature = "runtime-benchmarks")] + +use super::*; +use crate::Pallet as Salary; + +use frame_benchmarking::v2::*; +use frame_system::{Pallet as System, RawOrigin}; +use sp_core::Get; + +const SEED: u32 = 0; + +fn ensure_member_with_salary, I: 'static>(who: &T::AccountId) { + // induct if not a member. + if T::Members::rank_of(who).is_none() { + T::Members::induct(who).unwrap(); + } + // promote until they have a salary. + for _ in 0..255 { + let r = T::Members::rank_of(who).expect("prior guard ensures `who` is a member; qed"); + if !T::Salary::get_salary(r, &who).is_zero() { + break + } + T::Members::promote(who).unwrap(); + } +} + +#[instance_benchmarks] +mod benchmarks { + use super::*; + + #[benchmark] + fn init() { + let caller: T::AccountId = whitelisted_caller(); + + #[extrinsic_call] + _(RawOrigin::Signed(caller.clone())); + + assert!(Salary::::status().is_some()); + } + + #[benchmark] + fn bump() { + let caller: T::AccountId = whitelisted_caller(); + Salary::::init(RawOrigin::Signed(caller.clone()).into()).unwrap(); + System::::set_block_number(System::::block_number() + Salary::::cycle_period()); + + #[extrinsic_call] + _(RawOrigin::Signed(caller.clone())); + + assert_eq!(Salary::::status().unwrap().cycle_index, 1u32.into()); + } + + #[benchmark] + fn induct() { + let caller = whitelisted_caller(); + ensure_member_with_salary::(&caller); + Salary::::init(RawOrigin::Signed(caller.clone()).into()).unwrap(); + + #[extrinsic_call] + _(RawOrigin::Signed(caller.clone())); + + assert!(Salary::::last_active(&caller).is_ok()); + } + + #[benchmark] + fn register() { + let caller = whitelisted_caller(); + ensure_member_with_salary::(&caller); + Salary::::init(RawOrigin::Signed(caller.clone()).into()).unwrap(); + Salary::::induct(RawOrigin::Signed(caller.clone()).into()).unwrap(); + System::::set_block_number(System::::block_number() + Salary::::cycle_period()); + Salary::::bump(RawOrigin::Signed(caller.clone()).into()).unwrap(); + + #[extrinsic_call] + _(RawOrigin::Signed(caller.clone())); + + assert_eq!(Salary::::last_active(&caller).unwrap(), 1u32.into()); + } + + #[benchmark] + fn payout() { + let caller = whitelisted_caller(); + ensure_member_with_salary::(&caller); + Salary::::init(RawOrigin::Signed(caller.clone()).into()).unwrap(); + Salary::::induct(RawOrigin::Signed(caller.clone()).into()).unwrap(); + System::::set_block_number(System::::block_number() + Salary::::cycle_period()); + Salary::::bump(RawOrigin::Signed(caller.clone()).into()).unwrap(); + System::::set_block_number(System::::block_number() + T::RegistrationPeriod::get()); + + let salary = T::Salary::get_salary(T::Members::rank_of(&caller).unwrap(), &caller); + T::Paymaster::ensure_successful(&caller, salary); + + #[extrinsic_call] + _(RawOrigin::Signed(caller.clone())); + + match Claimant::::get(&caller) { + Some(ClaimantStatus { last_active, status: Attempted { id, .. } }) => { + assert_eq!(last_active, 1u32.into()); + assert_ne!(T::Paymaster::check_payment(id), PaymentStatus::Failure); + }, + _ => panic!("No claim made"), + } + assert!(Salary::::payout(RawOrigin::Signed(caller.clone()).into()).is_err()); + } + + #[benchmark] + fn payout_other() { + let caller = whitelisted_caller(); + ensure_member_with_salary::(&caller); + Salary::::init(RawOrigin::Signed(caller.clone()).into()).unwrap(); + Salary::::induct(RawOrigin::Signed(caller.clone()).into()).unwrap(); + System::::set_block_number(System::::block_number() + Salary::::cycle_period()); + Salary::::bump(RawOrigin::Signed(caller.clone()).into()).unwrap(); + System::::set_block_number(System::::block_number() + T::RegistrationPeriod::get()); + + let salary = T::Salary::get_salary(T::Members::rank_of(&caller).unwrap(), &caller); + let recipient: T::AccountId = account("recipient", 0, SEED); + T::Paymaster::ensure_successful(&recipient, salary); + + #[extrinsic_call] + _(RawOrigin::Signed(caller.clone()), recipient.clone()); + + match Claimant::::get(&caller) { + Some(ClaimantStatus { last_active, status: Attempted { id, .. } }) => { + assert_eq!(last_active, 1u32.into()); + assert_ne!(T::Paymaster::check_payment(id), PaymentStatus::Failure); + }, + _ => panic!("No claim made"), + } + assert!(Salary::::payout(RawOrigin::Signed(caller.clone()).into()).is_err()); + } + + #[benchmark] + fn check_payment() { + let caller = whitelisted_caller(); + ensure_member_with_salary::(&caller); + Salary::::init(RawOrigin::Signed(caller.clone()).into()).unwrap(); + Salary::::induct(RawOrigin::Signed(caller.clone()).into()).unwrap(); + System::::set_block_number(System::::block_number() + Salary::::cycle_period()); + Salary::::bump(RawOrigin::Signed(caller.clone()).into()).unwrap(); + System::::set_block_number(System::::block_number() + T::RegistrationPeriod::get()); + + let salary = T::Salary::get_salary(T::Members::rank_of(&caller).unwrap(), &caller); + let recipient: T::AccountId = account("recipient", 0, SEED); + T::Paymaster::ensure_successful(&recipient, salary); + Salary::::payout(RawOrigin::Signed(caller.clone()).into()).unwrap(); + let id = match Claimant::::get(&caller).unwrap().status { + Attempted { id, .. } => id, + _ => panic!("No claim made"), + }; + T::Paymaster::ensure_concluded(id); + + #[extrinsic_call] + _(RawOrigin::Signed(caller.clone())); + + assert!(!matches!(Claimant::::get(&caller).unwrap().status, Attempted { .. })); + } + + impl_benchmark_test_suite! { + Salary, + crate::tests::new_test_ext(), + crate::tests::Test, + } +} diff --git a/frame/salary/src/lib.rs b/frame/salary/src/lib.rs new file mode 100644 index 000000000..2169952f1 --- /dev/null +++ b/frame/salary/src/lib.rs @@ -0,0 +1,514 @@ +// This file is part of Substrate. + +// Copyright (C) 2017-2022 Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +//! Make periodic payment to members of a ranked collective according to rank. + +#![cfg_attr(not(feature = "std"), no_std)] +#![recursion_limit = "128"] + +use codec::{Decode, Encode, FullCodec, MaxEncodedLen}; +use scale_info::TypeInfo; +use sp_arithmetic::traits::{Saturating, Zero}; +use sp_core::TypedGet; +use sp_runtime::Perbill; +use sp_std::{fmt::Debug, marker::PhantomData, prelude::*}; + +use frame_support::{ + dispatch::DispatchResultWithPostInfo, + ensure, + traits::{ + tokens::{fungible, Balance, GetSalary}, + RankedMembers, + }, + RuntimeDebug, +}; + +#[cfg(test)] +mod tests; + +#[cfg(feature = "runtime-benchmarks")] +mod benchmarking; +pub mod weights; + +pub use pallet::*; +pub use weights::WeightInfo; + +/// Payroll cycle. +pub type Cycle = u32; + +/// Status for making a payment via the `Pay::pay` trait function. +#[derive(Encode, Decode, Eq, PartialEq, Clone, TypeInfo, MaxEncodedLen, RuntimeDebug)] +pub enum PaymentStatus { + /// Payment is in progress. Nothing to report yet. + InProgress, + /// Payment status is unknowable. It will never be reported successful or failed. + Unknown, + /// Payment happened successfully. + Success, + /// Payment failed. It may safely be retried. + Failure, +} + +/// Can be implemented by `PayFromAccount` using a `fungible` impl, but can also be implemented with +/// XCM/MultiAsset and made generic over assets. +pub trait Pay { + /// The type by which we measure units of the currency in which we make payments. + type Balance: Balance; + /// The type by which we identify the individuals to whom a payment may be made. + type AccountId; + /// An identifier given to an individual payment. + type Id: FullCodec + MaxEncodedLen + TypeInfo + Clone + Eq + PartialEq + Debug + Copy; + /// Make a payment and return an identifier for later evaluation of success in some off-chain + /// mechanism (likely an event, but possibly not on this chain). + fn pay(who: &Self::AccountId, amount: Self::Balance) -> Result; + /// Check how a payment has proceeded. `id` must have been a previously returned by `pay` for + /// the result of this call to be meaningful. + fn check_payment(id: Self::Id) -> PaymentStatus; + /// Ensure that a call to pay with the given parameters will be successful if done immediately + /// after this call. Used in benchmarking code. + #[cfg(feature = "runtime-benchmarks")] + fn ensure_successful(who: &Self::AccountId, amount: Self::Balance); + /// Ensure that a call to check_payment with the given parameters will return either Success + /// or Failure. + #[cfg(feature = "runtime-benchmarks")] + fn ensure_concluded(id: Self::Id); +} + +/// Simple implementation of `Pay` which makes a payment from a "pot" - i.e. a single account. +pub struct PayFromAccount(sp_std::marker::PhantomData<(F, A)>); +impl + fungible::Mutate> Pay + for PayFromAccount +{ + type Balance = F::Balance; + type AccountId = A::Type; + type Id = (); + fn pay(who: &Self::AccountId, amount: Self::Balance) -> Result { + >::transfer(&A::get(), who, amount, false).map_err(|_| ())?; + Ok(()) + } + fn check_payment(_: ()) -> PaymentStatus { + PaymentStatus::Success + } + #[cfg(feature = "runtime-benchmarks")] + fn ensure_successful(_: &Self::AccountId, amount: Self::Balance) { + >::mint_into(&A::get(), amount).unwrap(); + } + #[cfg(feature = "runtime-benchmarks")] + fn ensure_concluded(_: Self::Id) {} +} + +/// The status of the pallet instance. +#[derive(Encode, Decode, Eq, PartialEq, Clone, TypeInfo, MaxEncodedLen, RuntimeDebug)] +pub struct StatusType { + /// The index of the "current cycle" (i.e. the last cycle being processed). + cycle_index: CycleIndex, + /// The first block of the "current cycle" (i.e. the last cycle being processed). + cycle_start: BlockNumber, + /// The total budget available for all payments in the current cycle. + budget: Balance, + /// The total amount of the payments registered in the current cycle. + total_registrations: Balance, + /// The total amount of unregistered payments which have been made in the current cycle. + total_unregistered_paid: Balance, +} + +/// The state of a specific payment claim. +#[derive(Encode, Decode, Eq, PartialEq, Clone, TypeInfo, MaxEncodedLen, RuntimeDebug)] +pub enum ClaimState { + /// No claim recorded. + Nothing, + /// Amount reserved when last active. + Registered(Balance), + /// Amount attempted to be paid when last active as well as the identity of the payment. + Attempted { registered: Option, id: Id, amount: Balance }, +} + +use ClaimState::*; + +/// The status of a single payee/claimant. +#[derive(Encode, Decode, Eq, PartialEq, Clone, TypeInfo, MaxEncodedLen, RuntimeDebug)] +pub struct ClaimantStatus { + /// The most recent cycle in which the claimant was active. + last_active: CycleIndex, + /// The state of the payment/claim with in the above cycle. + status: ClaimState, +} + +#[frame_support::pallet] +pub mod pallet { + use super::*; + use frame_support::{dispatch::Pays, pallet_prelude::*}; + use frame_system::pallet_prelude::*; + + #[pallet::pallet] + #[pallet::generate_store(pub(super) trait Store)] + pub struct Pallet(PhantomData<(T, I)>); + + #[pallet::config] + pub trait Config: frame_system::Config { + /// Weight information for extrinsics in this pallet. + type WeightInfo: WeightInfo; + + /// The runtime event type. + type RuntimeEvent: From> + + IsType<::RuntimeEvent>; + + /// Means by which we can make payments to accounts. This also defines the currency and the + /// balance which we use to denote that currency. + type Paymaster: Pay::AccountId>; + + /// The current membership of payees. + type Members: RankedMembers::AccountId>; + + /// The maximum payout to be made for a single period to an active member of the given rank. + /// + /// The benchmarks require that this be non-zero for some rank at most 255. + type Salary: GetSalary< + ::Rank, + Self::AccountId, + ::Balance, + >; + + /// The number of blocks within a cycle which accounts have to register their intent to + /// claim. + /// + /// The number of blocks between sequential payout cycles is the sum of this and + /// `PayoutPeriod`. + #[pallet::constant] + type RegistrationPeriod: Get; + + /// The number of blocks within a cycle which accounts have to claim the payout. + /// + /// The number of blocks between sequential payout cycles is the sum of this and + /// `RegistrationPeriod`. + #[pallet::constant] + type PayoutPeriod: Get; + + /// The total budget per cycle. + /// + /// This may change over the course of a cycle without any problem. + #[pallet::constant] + type Budget: Get>; + } + + pub type CycleIndexOf = ::BlockNumber; + pub type BalanceOf = <>::Paymaster as Pay>::Balance; + pub type IdOf = <>::Paymaster as Pay>::Id; + pub type StatusOf = + StatusType, ::BlockNumber, BalanceOf>; + pub type ClaimantStatusOf = ClaimantStatus, BalanceOf, IdOf>; + + /// The overall status of the system. + #[pallet::storage] + pub(super) type Status, I: 'static = ()> = + StorageValue<_, StatusOf, OptionQuery>; + + /// The status of a claimant. + #[pallet::storage] + pub(super) type Claimant, I: 'static = ()> = + StorageMap<_, Twox64Concat, T::AccountId, ClaimantStatusOf, OptionQuery>; + + #[pallet::event] + #[pallet::generate_deposit(pub(super) fn deposit_event)] + pub enum Event, I: 'static = ()> { + /// A member is inducted into the payroll. + Inducted { who: T::AccountId }, + /// A member registered for a payout. + Registered { who: T::AccountId, amount: BalanceOf }, + /// A payment happened. + Paid { + who: T::AccountId, + beneficiary: T::AccountId, + amount: BalanceOf, + id: ::Id, + }, + /// The next cycle begins. + CycleStarted { index: CycleIndexOf }, + } + + #[pallet::error] + pub enum Error { + /// The salary system has already been started. + AlreadyStarted, + /// The account is not a ranked member. + NotMember, + /// The account is already inducted. + AlreadyInducted, + // The account is not yet inducted into the system. + NotInducted, + /// The member does not have a current valid claim. + NoClaim, + /// The member's claim is zero. + ClaimZero, + /// Current cycle's registration period is over. + TooLate, + /// Current cycle's payment period is not yet begun. + TooEarly, + /// Cycle is not yet over. + NotYet, + /// The payout cycles have not yet started. + NotStarted, + /// There is no budget left for the payout. + Bankrupt, + /// There was some issue with the mechanism of payment. + PayError, + /// The payment has neither failed nor succeeded yet. + Inconclusive, + /// The cycle is after that in which the payment was made. + NotCurrent, + } + + #[pallet::call] + impl, I: 'static> Pallet { + /// Start the first payout cycle. + /// + /// - `origin`: A `Signed` origin of an account. + #[pallet::weight(T::WeightInfo::init())] + #[pallet::call_index(0)] + pub fn init(origin: OriginFor) -> DispatchResultWithPostInfo { + let _ = ensure_signed(origin)?; + let now = frame_system::Pallet::::block_number(); + ensure!(!Status::::exists(), Error::::AlreadyStarted); + let status = StatusType { + cycle_index: Zero::zero(), + cycle_start: now, + budget: T::Budget::get(), + total_registrations: Zero::zero(), + total_unregistered_paid: Zero::zero(), + }; + Status::::put(&status); + + Self::deposit_event(Event::::CycleStarted { index: status.cycle_index }); + Ok(Pays::No.into()) + } + + /// Move to next payout cycle, assuming that the present block is now within that cycle. + /// + /// - `origin`: A `Signed` origin of an account. + #[pallet::weight(T::WeightInfo::bump())] + #[pallet::call_index(1)] + pub fn bump(origin: OriginFor) -> DispatchResultWithPostInfo { + let _ = ensure_signed(origin)?; + let now = frame_system::Pallet::::block_number(); + let cycle_period = Self::cycle_period(); + let mut status = Status::::get().ok_or(Error::::NotStarted)?; + status.cycle_start.saturating_accrue(cycle_period); + ensure!(now >= status.cycle_start, Error::::NotYet); + status.cycle_index.saturating_inc(); + status.budget = T::Budget::get(); + status.total_registrations = Zero::zero(); + status.total_unregistered_paid = Zero::zero(); + Status::::put(&status); + + Self::deposit_event(Event::::CycleStarted { index: status.cycle_index }); + Ok(Pays::No.into()) + } + + /// Induct oneself into the payout system. + #[pallet::weight(T::WeightInfo::induct())] + #[pallet::call_index(2)] + pub fn induct(origin: OriginFor) -> DispatchResultWithPostInfo { + let who = ensure_signed(origin)?; + let cycle_index = Status::::get().ok_or(Error::::NotStarted)?.cycle_index; + let _ = T::Members::rank_of(&who).ok_or(Error::::NotMember)?; + ensure!(!Claimant::::contains_key(&who), Error::::AlreadyInducted); + + Claimant::::insert( + &who, + ClaimantStatus { last_active: cycle_index, status: Nothing }, + ); + + Self::deposit_event(Event::::Inducted { who }); + Ok(Pays::No.into()) + } + + /// Register for a payout. + /// + /// Will only work if we are in the first `RegistrationPeriod` blocks since the cycle + /// started. + /// + /// - `origin`: A `Signed` origin of an account which is a member of `Members`. + #[pallet::weight(T::WeightInfo::register())] + #[pallet::call_index(3)] + pub fn register(origin: OriginFor) -> DispatchResultWithPostInfo { + let who = ensure_signed(origin)?; + let rank = T::Members::rank_of(&who).ok_or(Error::::NotMember)?; + let mut status = Status::::get().ok_or(Error::::NotStarted)?; + let mut claimant = Claimant::::get(&who).ok_or(Error::::NotInducted)?; + let now = frame_system::Pallet::::block_number(); + ensure!( + now < status.cycle_start + T::RegistrationPeriod::get(), + Error::::TooLate + ); + ensure!(claimant.last_active < status.cycle_index, Error::::NoClaim); + let payout = T::Salary::get_salary(rank, &who); + ensure!(!payout.is_zero(), Error::::ClaimZero); + claimant.last_active = status.cycle_index; + claimant.status = Registered(payout); + status.total_registrations.saturating_accrue(payout); + + Claimant::::insert(&who, &claimant); + Status::::put(&status); + + Self::deposit_event(Event::::Registered { who, amount: payout }); + Ok(Pays::No.into()) + } + + /// Request a payout. + /// + /// Will only work if we are after the first `RegistrationPeriod` blocks since the cycle + /// started but by no more than `PayoutPeriod` blocks. + /// + /// - `origin`: A `Signed` origin of an account which is a member of `Members`. + #[pallet::weight(T::WeightInfo::payout())] + #[pallet::call_index(4)] + pub fn payout(origin: OriginFor) -> DispatchResultWithPostInfo { + let who = ensure_signed(origin)?; + Self::do_payout(who.clone(), who)?; + Ok(Pays::No.into()) + } + + /// Request a payout to a secondary account. + /// + /// Will only work if we are after the first `RegistrationPeriod` blocks since the cycle + /// started but by no more than `PayoutPeriod` blocks. + /// + /// - `origin`: A `Signed` origin of an account which is a member of `Members`. + /// - `beneficiary`: The account to receive payment. + #[pallet::weight(T::WeightInfo::payout_other())] + #[pallet::call_index(5)] + pub fn payout_other( + origin: OriginFor, + beneficiary: T::AccountId, + ) -> DispatchResultWithPostInfo { + let who = ensure_signed(origin)?; + Self::do_payout(who, beneficiary)?; + Ok(Pays::No.into()) + } + + /// Update a payment's status; if it failed, alter the state so the payment can be retried. + /// + /// This must be called within the same cycle as the failed payment. It will fail with + /// `Event::NotCurrent` otherwise. + /// + /// - `origin`: A `Signed` origin of an account which is a member of `Members` who has + /// received a payment this cycle. + #[pallet::weight(T::WeightInfo::check_payment())] + #[pallet::call_index(6)] + pub fn check_payment(origin: OriginFor) -> DispatchResultWithPostInfo { + let who = ensure_signed(origin)?; + + let mut status = Status::::get().ok_or(Error::::NotStarted)?; + let mut claimant = Claimant::::get(&who).ok_or(Error::::NotInducted)?; + ensure!(claimant.last_active == status.cycle_index, Error::::NotCurrent); + let (id, registered, amount) = match claimant.status { + Attempted { id, registered, amount } => (id, registered, amount), + _ => return Err(Error::::NoClaim.into()), + }; + match T::Paymaster::check_payment(id) { + PaymentStatus::Failure => { + // Payment failed: we reset back to the status prior to payment. + if let Some(amount) = registered { + // Account registered; this makes it simple to roll back and allow retry. + claimant.status = ClaimState::Registered(amount); + } else { + // Account didn't register; we set it to `Nothing` but must decrement + // the `last_active` also to ensure a retry works. + claimant.last_active.saturating_reduce(1u32.into()); + claimant.status = ClaimState::Nothing; + // Since it is not registered, we must walk back our counter for what has + // been paid. + status.total_unregistered_paid.saturating_reduce(amount); + } + }, + PaymentStatus::Success => claimant.status = ClaimState::Nothing, + _ => return Err(Error::::Inconclusive.into()), + } + Claimant::::insert(&who, &claimant); + Status::::put(&status); + + Ok(Pays::No.into()) + } + } + + impl, I: 'static> Pallet { + pub fn status() -> Option> { + Status::::get() + } + pub fn last_active(who: &T::AccountId) -> Result, DispatchError> { + Ok(Claimant::::get(&who).ok_or(Error::::NotInducted)?.last_active) + } + pub fn cycle_period() -> T::BlockNumber { + T::RegistrationPeriod::get() + T::PayoutPeriod::get() + } + fn do_payout(who: T::AccountId, beneficiary: T::AccountId) -> DispatchResult { + let mut status = Status::::get().ok_or(Error::::NotStarted)?; + let mut claimant = Claimant::::get(&who).ok_or(Error::::NotInducted)?; + + let now = frame_system::Pallet::::block_number(); + ensure!( + now >= status.cycle_start + T::RegistrationPeriod::get(), + Error::::TooEarly, + ); + + let (payout, registered) = match claimant.status { + Registered(unpaid) if claimant.last_active == status.cycle_index => { + // Registered for this cycle. Pay accordingly. + let payout = if status.total_registrations <= status.budget { + // Can pay in full. + unpaid + } else { + // Must be reduced pro-rata + Perbill::from_rational(status.budget, status.total_registrations) + .mul_floor(unpaid) + }; + (payout, Some(unpaid)) + }, + Nothing | Attempted { .. } if claimant.last_active < status.cycle_index => { + // Not registered for this cycle. Pay from whatever is left. + let rank = T::Members::rank_of(&who).ok_or(Error::::NotMember)?; + let ideal_payout = T::Salary::get_salary(rank, &who); + + let pot = status + .budget + .saturating_sub(status.total_registrations) + .saturating_sub(status.total_unregistered_paid); + + let payout = ideal_payout.min(pot); + ensure!(!payout.is_zero(), Error::::ClaimZero); + + status.total_unregistered_paid.saturating_accrue(payout); + (payout, None) + }, + _ => return Err(Error::::NoClaim.into()), + }; + + claimant.last_active = status.cycle_index; + + let id = + T::Paymaster::pay(&beneficiary, payout).map_err(|()| Error::::PayError)?; + + claimant.status = Attempted { registered, id, amount: payout }; + + Claimant::::insert(&who, &claimant); + Status::::put(&status); + + Self::deposit_event(Event::::Paid { who, beneficiary, amount: payout, id }); + Ok(()) + } + } +} diff --git a/frame/salary/src/tests.rs b/frame/salary/src/tests.rs new file mode 100644 index 000000000..864929122 --- /dev/null +++ b/frame/salary/src/tests.rs @@ -0,0 +1,641 @@ +// This file is part of Substrate. + +// Copyright (C) 2017-2022 Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +//! The crate's tests. + +use std::collections::BTreeMap; + +use frame_support::{ + assert_noop, assert_ok, + pallet_prelude::Weight, + parameter_types, + traits::{tokens::ConvertRank, ConstU32, ConstU64, Everything}, +}; +use sp_core::H256; +use sp_runtime::{ + testing::Header, + traits::{BlakeTwo256, Identity, IdentityLookup}, + DispatchResult, +}; +use sp_std::cell::RefCell; + +use super::*; +use crate as pallet_salary; + +type UncheckedExtrinsic = frame_system::mocking::MockUncheckedExtrinsic; +type Block = frame_system::mocking::MockBlock; + +frame_support::construct_runtime!( + pub enum Test where + Block = Block, + NodeBlock = Block, + UncheckedExtrinsic = UncheckedExtrinsic, + { + System: frame_system::{Pallet, Call, Config, Storage, Event}, + Salary: pallet_salary::{Pallet, Call, Storage, Event}, + } +); + +parameter_types! { + pub BlockWeights: frame_system::limits::BlockWeights = + frame_system::limits::BlockWeights::simple_max(Weight::from_ref_time(1_000_000)); +} +impl frame_system::Config for Test { + type BaseCallFilter = Everything; + type BlockWeights = (); + type BlockLength = (); + type DbWeight = (); + type RuntimeOrigin = RuntimeOrigin; + type Index = u64; + type BlockNumber = u64; + type RuntimeCall = RuntimeCall; + type Hash = H256; + type Hashing = BlakeTwo256; + type AccountId = u64; + type Lookup = IdentityLookup; + type Header = Header; + type RuntimeEvent = RuntimeEvent; + type BlockHashCount = ConstU64<250>; + type Version = (); + type PalletInfo = PalletInfo; + type AccountData = (); + type OnNewAccount = (); + type OnKilledAccount = (); + type SystemWeightInfo = (); + type SS58Prefix = (); + type OnSetCode = (); + type MaxConsumers = ConstU32<16>; +} + +thread_local! { + pub static PAID: RefCell> = RefCell::new(BTreeMap::new()); + pub static STATUS: RefCell> = RefCell::new(BTreeMap::new()); + pub static LAST_ID: RefCell = RefCell::new(0u64); +} + +fn paid(who: u64) -> u64 { + PAID.with(|p| p.borrow().get(&who).cloned().unwrap_or(0)) +} +fn unpay(who: u64, amount: u64) { + PAID.with(|p| p.borrow_mut().entry(who).or_default().saturating_reduce(amount)) +} +fn set_status(id: u64, s: PaymentStatus) { + STATUS.with(|m| m.borrow_mut().insert(id, s)); +} + +pub struct TestPay; +impl Pay for TestPay { + type AccountId = u64; + type Balance = u64; + type Id = u64; + + fn pay(who: &Self::AccountId, amount: Self::Balance) -> Result { + PAID.with(|paid| *paid.borrow_mut().entry(*who).or_default() += amount); + Ok(LAST_ID.with(|lid| { + let x = *lid.borrow(); + lid.replace(x + 1); + x + })) + } + fn check_payment(id: Self::Id) -> PaymentStatus { + STATUS.with(|s| s.borrow().get(&id).cloned().unwrap_or(PaymentStatus::Unknown)) + } + #[cfg(feature = "runtime-benchmarks")] + fn ensure_successful(_: &Self::AccountId, _: Self::Balance) {} + #[cfg(feature = "runtime-benchmarks")] + fn ensure_concluded(id: Self::Id) { + set_status(id, PaymentStatus::Failure) + } +} + +thread_local! { + pub static CLUB: RefCell> = RefCell::new(BTreeMap::new()); +} + +pub struct TestClub; +impl RankedMembers for TestClub { + type AccountId = u64; + type Rank = u64; + fn min_rank() -> Self::Rank { + 0 + } + fn rank_of(who: &Self::AccountId) -> Option { + CLUB.with(|club| club.borrow().get(who).cloned()) + } + fn induct(who: &Self::AccountId) -> DispatchResult { + CLUB.with(|club| club.borrow_mut().insert(*who, 0)); + Ok(()) + } + fn promote(who: &Self::AccountId) -> DispatchResult { + CLUB.with(|club| { + club.borrow_mut().entry(*who).and_modify(|r| *r += 1); + }); + Ok(()) + } + fn demote(who: &Self::AccountId) -> DispatchResult { + CLUB.with(|club| match club.borrow().get(who) { + None => Err(sp_runtime::DispatchError::Unavailable), + Some(&0) => { + club.borrow_mut().remove(&who); + Ok(()) + }, + Some(_) => { + club.borrow_mut().entry(*who).and_modify(|x| *x -= 1); + Ok(()) + }, + }) + } +} + +fn set_rank(who: u64, rank: u64) { + CLUB.with(|club| club.borrow_mut().insert(who, rank)); +} + +parameter_types! { + pub static Budget: u64 = 10; +} + +impl Config for Test { + type WeightInfo = (); + type RuntimeEvent = RuntimeEvent; + type Paymaster = TestPay; + type Members = TestClub; + type Salary = ConvertRank; + type RegistrationPeriod = ConstU64<2>; + type PayoutPeriod = ConstU64<2>; + type Budget = Budget; +} + +pub fn new_test_ext() -> sp_io::TestExternalities { + let t = frame_system::GenesisConfig::default().build_storage::().unwrap(); + let mut ext = sp_io::TestExternalities::new(t); + ext.execute_with(|| System::set_block_number(1)); + ext +} + +fn next_block() { + System::set_block_number(System::block_number() + 1); +} + +#[allow(dead_code)] +fn run_to(n: u64) { + while System::block_number() < n { + next_block(); + } +} + +#[test] +fn basic_stuff() { + new_test_ext().execute_with(|| { + assert!(Salary::last_active(&0).is_err()); + assert_eq!(Salary::status(), None); + }); +} + +#[test] +fn can_start() { + new_test_ext().execute_with(|| { + assert_ok!(Salary::init(RuntimeOrigin::signed(1))); + assert_eq!( + Salary::status(), + Some(StatusType { + cycle_index: 0, + cycle_start: 1, + budget: 10, + total_registrations: 0, + total_unregistered_paid: 0, + }) + ); + }); +} + +#[test] +fn bump_works() { + new_test_ext().execute_with(|| { + assert_ok!(Salary::init(RuntimeOrigin::signed(1))); + run_to(4); + assert_noop!(Salary::bump(RuntimeOrigin::signed(1)), Error::::NotYet); + + run_to(5); + assert_ok!(Salary::bump(RuntimeOrigin::signed(1))); + assert_eq!( + Salary::status(), + Some(StatusType { + cycle_index: 1, + cycle_start: 5, + budget: 10, + total_registrations: 0, + total_unregistered_paid: 0 + }) + ); + + run_to(8); + assert_noop!(Salary::bump(RuntimeOrigin::signed(1)), Error::::NotYet); + + BUDGET.with(|b| b.replace(5)); + run_to(9); + assert_ok!(Salary::bump(RuntimeOrigin::signed(1))); + assert_eq!( + Salary::status(), + Some(StatusType { + cycle_index: 2, + cycle_start: 9, + budget: 5, + total_registrations: 0, + total_unregistered_paid: 0 + }) + ); + }); +} + +#[test] +fn induct_works() { + new_test_ext().execute_with(|| { + assert_ok!(Salary::init(RuntimeOrigin::signed(1))); + + assert_noop!(Salary::induct(RuntimeOrigin::signed(1)), Error::::NotMember); + set_rank(1, 1); + assert!(Salary::last_active(&1).is_err()); + assert_ok!(Salary::induct(RuntimeOrigin::signed(1))); + assert_eq!(Salary::last_active(&1).unwrap(), 0); + assert_noop!(Salary::induct(RuntimeOrigin::signed(1)), Error::::AlreadyInducted); + }); +} + +#[test] +fn unregistered_payment_works() { + new_test_ext().execute_with(|| { + set_rank(1, 1); + assert_noop!(Salary::induct(RuntimeOrigin::signed(1)), Error::::NotStarted); + assert_ok!(Salary::init(RuntimeOrigin::signed(1))); + assert_noop!(Salary::payout(RuntimeOrigin::signed(1)), Error::::NotInducted); + assert_ok!(Salary::induct(RuntimeOrigin::signed(1))); + // No claim on the cycle active during induction. + assert_noop!(Salary::payout(RuntimeOrigin::signed(1)), Error::::TooEarly); + run_to(3); + assert_noop!(Salary::payout(RuntimeOrigin::signed(1)), Error::::NoClaim); + + run_to(6); + assert_ok!(Salary::bump(RuntimeOrigin::signed(1))); + assert_noop!(Salary::payout(RuntimeOrigin::signed(1)), Error::::TooEarly); + run_to(7); + assert_ok!(Salary::payout(RuntimeOrigin::signed(1))); + assert_eq!(paid(1), 1); + assert_eq!(Salary::status().unwrap().total_unregistered_paid, 1); + assert_noop!(Salary::payout(RuntimeOrigin::signed(1)), Error::::NoClaim); + run_to(8); + assert_noop!(Salary::bump(RuntimeOrigin::signed(1)), Error::::NotYet); + run_to(9); + assert_ok!(Salary::bump(RuntimeOrigin::signed(1))); + run_to(11); + assert_ok!(Salary::payout_other(RuntimeOrigin::signed(1), 10)); + assert_eq!(paid(1), 1); + assert_eq!(paid(10), 1); + assert_eq!(Salary::status().unwrap().total_unregistered_paid, 1); + }); +} + +#[test] +fn retry_payment_works() { + new_test_ext().execute_with(|| { + set_rank(1, 1); + assert_ok!(Salary::init(RuntimeOrigin::signed(1))); + assert_ok!(Salary::induct(RuntimeOrigin::signed(1))); + run_to(6); + assert_ok!(Salary::bump(RuntimeOrigin::signed(1))); + run_to(7); + assert_ok!(Salary::payout(RuntimeOrigin::signed(1))); + + // Payment failed. + unpay(1, 1); + set_status(0, PaymentStatus::Failure); + + assert_eq!(paid(1), 0); + assert_eq!(Salary::status().unwrap().total_unregistered_paid, 1); + + // Can't just retry. + assert_noop!(Salary::payout(RuntimeOrigin::signed(1)), Error::::NoClaim); + // Check status. + assert_ok!(Salary::check_payment(RuntimeOrigin::signed(1))); + // Allowed to try again. + assert_ok!(Salary::payout(RuntimeOrigin::signed(1))); + + assert_eq!(paid(1), 1); + assert_eq!(Salary::status().unwrap().total_unregistered_paid, 1); + + assert_noop!(Salary::payout(RuntimeOrigin::signed(1)), Error::::NoClaim); + run_to(8); + assert_noop!(Salary::bump(RuntimeOrigin::signed(1)), Error::::NotYet); + run_to(9); + assert_ok!(Salary::bump(RuntimeOrigin::signed(1))); + run_to(11); + assert_ok!(Salary::payout_other(RuntimeOrigin::signed(1), 10)); + assert_eq!(paid(1), 1); + assert_eq!(paid(10), 1); + assert_eq!(Salary::status().unwrap().total_unregistered_paid, 1); + }); +} + +#[test] +fn retry_registered_payment_works() { + new_test_ext().execute_with(|| { + set_rank(1, 1); + assert_ok!(Salary::init(RuntimeOrigin::signed(1))); + assert_ok!(Salary::induct(RuntimeOrigin::signed(1))); + run_to(6); + assert_ok!(Salary::bump(RuntimeOrigin::signed(1))); + assert_ok!(Salary::register(RuntimeOrigin::signed(1))); + run_to(7); + assert_ok!(Salary::payout(RuntimeOrigin::signed(1))); + + // Payment failed. + unpay(1, 1); + set_status(0, PaymentStatus::Failure); + + assert_eq!(paid(1), 0); + assert_eq!(Salary::status().unwrap().total_unregistered_paid, 0); + + assert_noop!(Salary::payout(RuntimeOrigin::signed(1)), Error::::NoClaim); + // Check status. + assert_ok!(Salary::check_payment(RuntimeOrigin::signed(1))); + // Allowed to try again. + assert_ok!(Salary::payout(RuntimeOrigin::signed(1))); + + assert_eq!(paid(1), 1); + assert_eq!(Salary::status().unwrap().total_unregistered_paid, 0); + }); +} + +#[test] +fn retry_payment_later_is_not_allowed() { + new_test_ext().execute_with(|| { + set_rank(1, 1); + assert_ok!(Salary::init(RuntimeOrigin::signed(1))); + assert_ok!(Salary::induct(RuntimeOrigin::signed(1))); + run_to(6); + assert_ok!(Salary::bump(RuntimeOrigin::signed(1))); + run_to(7); + assert_ok!(Salary::payout(RuntimeOrigin::signed(1))); + + // Payment failed. + unpay(1, 1); + set_status(0, PaymentStatus::Failure); + + assert_eq!(paid(1), 0); + assert_eq!(Salary::status().unwrap().total_unregistered_paid, 1); + + // Can't just retry. + assert_noop!(Salary::payout(RuntimeOrigin::signed(1)), Error::::NoClaim); + + // Next cycle. + run_to(9); + assert_ok!(Salary::bump(RuntimeOrigin::signed(1))); + + // Payment did fail but now too late to retry. + assert_noop!(Salary::check_payment(RuntimeOrigin::signed(1)), Error::::NotCurrent); + + // We do get this cycle's payout, but we must wait for the payout period to start. + assert_noop!(Salary::payout(RuntimeOrigin::signed(1)), Error::::TooEarly); + + run_to(11); + assert_ok!(Salary::payout(RuntimeOrigin::signed(1))); + assert_eq!(paid(1), 1); + assert_eq!(Salary::status().unwrap().total_unregistered_paid, 1); + assert_noop!(Salary::payout(RuntimeOrigin::signed(1)), Error::::NoClaim); + }); +} + +#[test] +fn retry_payment_later_without_bump_is_allowed() { + new_test_ext().execute_with(|| { + set_rank(1, 1); + assert_ok!(Salary::init(RuntimeOrigin::signed(1))); + assert_ok!(Salary::induct(RuntimeOrigin::signed(1))); + run_to(6); + assert_ok!(Salary::bump(RuntimeOrigin::signed(1))); + run_to(7); + assert_ok!(Salary::payout(RuntimeOrigin::signed(1))); + + // Payment failed. + unpay(1, 1); + set_status(0, PaymentStatus::Failure); + + // Next cycle. + run_to(9); + + // Payment did fail but we can still retry as long as we don't `bump`. + assert_ok!(Salary::check_payment(RuntimeOrigin::signed(1))); + assert_ok!(Salary::payout(RuntimeOrigin::signed(1))); + + assert_eq!(paid(1), 1); + assert_eq!(Salary::status().unwrap().total_unregistered_paid, 1); + }); +} + +#[test] +fn retry_payment_to_other_works() { + new_test_ext().execute_with(|| { + set_rank(1, 1); + assert_ok!(Salary::init(RuntimeOrigin::signed(1))); + assert_ok!(Salary::induct(RuntimeOrigin::signed(1))); + run_to(6); + assert_ok!(Salary::bump(RuntimeOrigin::signed(1))); + run_to(7); + assert_ok!(Salary::payout_other(RuntimeOrigin::signed(1), 10)); + + // Payment failed. + unpay(10, 1); + set_status(0, PaymentStatus::Failure); + + // Can't just retry. + assert_noop!(Salary::payout_other(RuntimeOrigin::signed(1), 10), Error::::NoClaim); + // Check status. + assert_ok!(Salary::check_payment(RuntimeOrigin::signed(1))); + // Allowed to try again. + assert_ok!(Salary::payout_other(RuntimeOrigin::signed(1), 10)); + + assert_eq!(paid(10), 1); + assert_eq!(Salary::status().unwrap().total_unregistered_paid, 1); + + assert_noop!(Salary::payout_other(RuntimeOrigin::signed(1), 10), Error::::NoClaim); + run_to(8); + assert_noop!(Salary::bump(RuntimeOrigin::signed(1)), Error::::NotYet); + run_to(9); + assert_ok!(Salary::bump(RuntimeOrigin::signed(1))); + run_to(11); + assert_ok!(Salary::payout(RuntimeOrigin::signed(1))); + assert_eq!(paid(1), 1); + assert_eq!(paid(10), 1); + assert_eq!(Salary::status().unwrap().total_unregistered_paid, 1); + }); +} + +#[test] +fn registered_payment_works() { + new_test_ext().execute_with(|| { + set_rank(1, 1); + assert_noop!(Salary::induct(RuntimeOrigin::signed(1)), Error::::NotStarted); + assert_ok!(Salary::init(RuntimeOrigin::signed(1))); + assert_noop!(Salary::payout(RuntimeOrigin::signed(1)), Error::::NotInducted); + assert_ok!(Salary::induct(RuntimeOrigin::signed(1))); + // No claim on the cycle active during induction. + assert_noop!(Salary::register(RuntimeOrigin::signed(1)), Error::::NoClaim); + run_to(3); + assert_noop!(Salary::payout(RuntimeOrigin::signed(1)), Error::::NoClaim); + + run_to(5); + assert_ok!(Salary::bump(RuntimeOrigin::signed(1))); + assert_ok!(Salary::register(RuntimeOrigin::signed(1))); + assert_eq!(Salary::status().unwrap().total_registrations, 1); + run_to(7); + assert_ok!(Salary::payout(RuntimeOrigin::signed(1))); + assert_eq!(paid(1), 1); + assert_eq!(Salary::status().unwrap().total_unregistered_paid, 0); + assert_noop!(Salary::payout(RuntimeOrigin::signed(1)), Error::::NoClaim); + + run_to(9); + assert_ok!(Salary::bump(RuntimeOrigin::signed(1))); + assert_eq!(Salary::status().unwrap().total_registrations, 0); + assert_ok!(Salary::register(RuntimeOrigin::signed(1))); + assert_eq!(Salary::status().unwrap().total_registrations, 1); + run_to(11); + assert_ok!(Salary::payout(RuntimeOrigin::signed(1))); + assert_eq!(paid(1), 2); + assert_eq!(Salary::status().unwrap().total_unregistered_paid, 0); + }); +} + +#[test] +fn zero_payment_fails() { + new_test_ext().execute_with(|| { + assert_ok!(Salary::init(RuntimeOrigin::signed(1))); + set_rank(1, 0); + assert_ok!(Salary::induct(RuntimeOrigin::signed(1))); + run_to(7); + assert_ok!(Salary::bump(RuntimeOrigin::signed(1))); + assert_noop!(Salary::payout(RuntimeOrigin::signed(1)), Error::::ClaimZero); + }); +} + +#[test] +fn unregistered_bankrupcy_fails_gracefully() { + new_test_ext().execute_with(|| { + assert_ok!(Salary::init(RuntimeOrigin::signed(1))); + set_rank(1, 2); + set_rank(2, 6); + set_rank(3, 12); + + assert_ok!(Salary::induct(RuntimeOrigin::signed(1))); + assert_ok!(Salary::induct(RuntimeOrigin::signed(2))); + assert_ok!(Salary::induct(RuntimeOrigin::signed(3))); + + run_to(7); + assert_ok!(Salary::bump(RuntimeOrigin::signed(1))); + assert_ok!(Salary::payout(RuntimeOrigin::signed(1))); + assert_ok!(Salary::payout(RuntimeOrigin::signed(2))); + assert_ok!(Salary::payout(RuntimeOrigin::signed(3))); + + assert_eq!(paid(1), 2); + assert_eq!(paid(2), 6); + assert_eq!(paid(3), 2); + }); +} + +#[test] +fn registered_bankrupcy_fails_gracefully() { + new_test_ext().execute_with(|| { + assert_ok!(Salary::init(RuntimeOrigin::signed(1))); + set_rank(1, 2); + set_rank(2, 6); + set_rank(3, 12); + + assert_ok!(Salary::induct(RuntimeOrigin::signed(1))); + assert_ok!(Salary::induct(RuntimeOrigin::signed(2))); + assert_ok!(Salary::induct(RuntimeOrigin::signed(3))); + + run_to(5); + assert_ok!(Salary::bump(RuntimeOrigin::signed(1))); + assert_ok!(Salary::register(RuntimeOrigin::signed(1))); + assert_ok!(Salary::register(RuntimeOrigin::signed(2))); + assert_ok!(Salary::register(RuntimeOrigin::signed(3))); + + run_to(7); + assert_ok!(Salary::payout(RuntimeOrigin::signed(1))); + assert_ok!(Salary::payout(RuntimeOrigin::signed(2))); + assert_ok!(Salary::payout(RuntimeOrigin::signed(3))); + + assert_eq!(paid(1), 1); + assert_eq!(paid(2), 3); + assert_eq!(paid(3), 6); + }); +} + +#[test] +fn mixed_bankrupcy_fails_gracefully() { + new_test_ext().execute_with(|| { + assert_ok!(Salary::init(RuntimeOrigin::signed(1))); + set_rank(1, 2); + set_rank(2, 6); + set_rank(3, 12); + + assert_ok!(Salary::induct(RuntimeOrigin::signed(1))); + assert_ok!(Salary::induct(RuntimeOrigin::signed(2))); + assert_ok!(Salary::induct(RuntimeOrigin::signed(3))); + + run_to(5); + assert_ok!(Salary::bump(RuntimeOrigin::signed(1))); + assert_ok!(Salary::register(RuntimeOrigin::signed(1))); + assert_ok!(Salary::register(RuntimeOrigin::signed(2))); + + run_to(7); + assert_ok!(Salary::payout(RuntimeOrigin::signed(3))); + assert_ok!(Salary::payout(RuntimeOrigin::signed(2))); + assert_ok!(Salary::payout(RuntimeOrigin::signed(1))); + + assert_eq!(paid(1), 2); + assert_eq!(paid(2), 6); + assert_eq!(paid(3), 2); + }); +} + +#[test] +fn other_mixed_bankrupcy_fails_gracefully() { + new_test_ext().execute_with(|| { + assert_ok!(Salary::init(RuntimeOrigin::signed(1))); + set_rank(1, 2); + set_rank(2, 6); + set_rank(3, 12); + + assert_ok!(Salary::induct(RuntimeOrigin::signed(1))); + assert_ok!(Salary::induct(RuntimeOrigin::signed(2))); + assert_ok!(Salary::induct(RuntimeOrigin::signed(3))); + + run_to(5); + assert_ok!(Salary::bump(RuntimeOrigin::signed(1))); + assert_ok!(Salary::register(RuntimeOrigin::signed(2))); + assert_ok!(Salary::register(RuntimeOrigin::signed(3))); + + run_to(7); + assert_noop!(Salary::payout(RuntimeOrigin::signed(1)), Error::::ClaimZero); + assert_ok!(Salary::payout(RuntimeOrigin::signed(2))); + assert_ok!(Salary::payout(RuntimeOrigin::signed(3))); + + assert_eq!(paid(1), 0); + assert_eq!(paid(2), 3); + assert_eq!(paid(3), 6); + }); +} diff --git a/frame/salary/src/weights.rs b/frame/salary/src/weights.rs new file mode 100644 index 000000000..f678d086d --- /dev/null +++ b/frame/salary/src/weights.rs @@ -0,0 +1,276 @@ +// This file is part of Substrate. + +// Copyright (C) Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +//! Autogenerated weights for pallet_salary +//! +//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev +//! DATE: 2023-02-26, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! WORST CASE MAP SIZE: `1000000` +//! HOSTNAME: `bm3`, CPU: `Intel(R) Core(TM) i7-7700K CPU @ 4.20GHz` +//! EXECUTION: Some(Wasm), WASM-EXECUTION: Compiled, CHAIN: Some("dev"), DB CACHE: 1024 + +// Executed Command: +// target/production/substrate +// benchmark +// pallet +// --steps=50 +// --repeat=20 +// --extrinsic=* +// --execution=wasm +// --wasm-execution=compiled +// --heap-pages=4096 +// --json-file=/var/lib/gitlab-runner/builds/zyw4fam_/0/parity/mirrors/substrate/.git/.artifacts/bench.json +// --pallet=pallet_salary +// --chain=dev +// --header=./HEADER-APACHE2 +// --output=./frame/salary/src/weights.rs +// --template=./.maintain/frame-weight-template.hbs + +#![cfg_attr(rustfmt, rustfmt_skip)] +#![allow(unused_parens)] +#![allow(unused_imports)] + +use frame_support::{traits::Get, weights::{Weight, constants::RocksDbWeight}}; +use sp_std::marker::PhantomData; + +/// Weight functions needed for pallet_salary. +pub trait WeightInfo { + fn init() -> Weight; + fn bump() -> Weight; + fn induct() -> Weight; + fn register() -> Weight; + fn payout() -> Weight; + fn payout_other() -> Weight; + fn check_payment() -> Weight; +} + +/// Weights for pallet_salary using the Substrate node and recommended hardware. +pub struct SubstrateWeight(PhantomData); +impl WeightInfo for SubstrateWeight { + /// Storage: Salary Status (r:1 w:1) + /// Proof: Salary Status (max_values: Some(1), max_size: Some(56), added: 551, mode: MaxEncodedLen) + fn init() -> Weight { + // Proof Size summary in bytes: + // Measured: `1120` + // Estimated: `551` + // Minimum execution time: 21_058 nanoseconds. + Weight::from_ref_time(21_381_000) + .saturating_add(Weight::from_proof_size(551)) + .saturating_add(T::DbWeight::get().reads(1_u64)) + .saturating_add(T::DbWeight::get().writes(1_u64)) + } + /// Storage: Salary Status (r:1 w:1) + /// Proof: Salary Status (max_values: Some(1), max_size: Some(56), added: 551, mode: MaxEncodedLen) + fn bump() -> Weight { + // Proof Size summary in bytes: + // Measured: `1234` + // Estimated: `551` + // Minimum execution time: 22_272 nanoseconds. + Weight::from_ref_time(22_923_000) + .saturating_add(Weight::from_proof_size(551)) + .saturating_add(T::DbWeight::get().reads(1_u64)) + .saturating_add(T::DbWeight::get().writes(1_u64)) + } + /// Storage: Salary Status (r:1 w:0) + /// Proof: Salary Status (max_values: Some(1), max_size: Some(56), added: 551, mode: MaxEncodedLen) + /// Storage: RankedCollective Members (r:1 w:0) + /// Proof: RankedCollective Members (max_values: None, max_size: Some(42), added: 2517, mode: MaxEncodedLen) + /// Storage: Salary Claimant (r:1 w:1) + /// Proof: Salary Claimant (max_values: None, max_size: Some(78), added: 2553, mode: MaxEncodedLen) + fn induct() -> Weight { + // Proof Size summary in bytes: + // Measured: `1510` + // Estimated: `5621` + // Minimum execution time: 32_223 nanoseconds. + Weight::from_ref_time(32_663_000) + .saturating_add(Weight::from_proof_size(5621)) + .saturating_add(T::DbWeight::get().reads(3_u64)) + .saturating_add(T::DbWeight::get().writes(1_u64)) + } + /// Storage: RankedCollective Members (r:1 w:0) + /// Proof: RankedCollective Members (max_values: None, max_size: Some(42), added: 2517, mode: MaxEncodedLen) + /// Storage: Salary Status (r:1 w:1) + /// Proof: Salary Status (max_values: Some(1), max_size: Some(56), added: 551, mode: MaxEncodedLen) + /// Storage: Salary Claimant (r:1 w:1) + /// Proof: Salary Claimant (max_values: None, max_size: Some(78), added: 2553, mode: MaxEncodedLen) + fn register() -> Weight { + // Proof Size summary in bytes: + // Measured: `1616` + // Estimated: `5621` + // Minimum execution time: 38_279 nanoseconds. + Weight::from_ref_time(38_996_000) + .saturating_add(Weight::from_proof_size(5621)) + .saturating_add(T::DbWeight::get().reads(3_u64)) + .saturating_add(T::DbWeight::get().writes(2_u64)) + } + /// Storage: Salary Status (r:1 w:1) + /// Proof: Salary Status (max_values: Some(1), max_size: Some(56), added: 551, mode: MaxEncodedLen) + /// Storage: Salary Claimant (r:1 w:1) + /// Proof: Salary Claimant (max_values: None, max_size: Some(78), added: 2553, mode: MaxEncodedLen) + /// Storage: RankedCollective Members (r:1 w:0) + /// Proof: RankedCollective Members (max_values: None, max_size: Some(42), added: 2517, mode: MaxEncodedLen) + fn payout() -> Weight { + // Proof Size summary in bytes: + // Measured: `2241` + // Estimated: `5621` + // Minimum execution time: 68_868 nanoseconds. + Weight::from_ref_time(70_160_000) + .saturating_add(Weight::from_proof_size(5621)) + .saturating_add(T::DbWeight::get().reads(3_u64)) + .saturating_add(T::DbWeight::get().writes(2_u64)) + } + /// Storage: Salary Status (r:1 w:1) + /// Proof: Salary Status (max_values: Some(1), max_size: Some(56), added: 551, mode: MaxEncodedLen) + /// Storage: Salary Claimant (r:1 w:1) + /// Proof: Salary Claimant (max_values: None, max_size: Some(78), added: 2553, mode: MaxEncodedLen) + /// Storage: RankedCollective Members (r:1 w:0) + /// Proof: RankedCollective Members (max_values: None, max_size: Some(42), added: 2517, mode: MaxEncodedLen) + /// Storage: System Account (r:1 w:1) + /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) + fn payout_other() -> Weight { + // Proof Size summary in bytes: + // Measured: `2189` + // Estimated: `8224` + // Minimum execution time: 68_804 nanoseconds. + Weight::from_ref_time(69_223_000) + .saturating_add(Weight::from_proof_size(8224)) + .saturating_add(T::DbWeight::get().reads(4_u64)) + .saturating_add(T::DbWeight::get().writes(3_u64)) + } + /// Storage: Salary Status (r:1 w:1) + /// Proof: Salary Status (max_values: Some(1), max_size: Some(56), added: 551, mode: MaxEncodedLen) + /// Storage: Salary Claimant (r:1 w:1) + /// Proof: Salary Claimant (max_values: None, max_size: Some(78), added: 2553, mode: MaxEncodedLen) + fn check_payment() -> Weight { + // Proof Size summary in bytes: + // Measured: `880` + // Estimated: `3104` + // Minimum execution time: 19_027 nanoseconds. + Weight::from_ref_time(19_360_000) + .saturating_add(Weight::from_proof_size(3104)) + .saturating_add(T::DbWeight::get().reads(2_u64)) + .saturating_add(T::DbWeight::get().writes(2_u64)) + } +} + +// For backwards compatibility and tests +impl WeightInfo for () { + /// Storage: Salary Status (r:1 w:1) + /// Proof: Salary Status (max_values: Some(1), max_size: Some(56), added: 551, mode: MaxEncodedLen) + fn init() -> Weight { + // Proof Size summary in bytes: + // Measured: `1120` + // Estimated: `551` + // Minimum execution time: 21_058 nanoseconds. + Weight::from_ref_time(21_381_000) + .saturating_add(Weight::from_proof_size(551)) + .saturating_add(RocksDbWeight::get().reads(1_u64)) + .saturating_add(RocksDbWeight::get().writes(1_u64)) + } + /// Storage: Salary Status (r:1 w:1) + /// Proof: Salary Status (max_values: Some(1), max_size: Some(56), added: 551, mode: MaxEncodedLen) + fn bump() -> Weight { + // Proof Size summary in bytes: + // Measured: `1234` + // Estimated: `551` + // Minimum execution time: 22_272 nanoseconds. + Weight::from_ref_time(22_923_000) + .saturating_add(Weight::from_proof_size(551)) + .saturating_add(RocksDbWeight::get().reads(1_u64)) + .saturating_add(RocksDbWeight::get().writes(1_u64)) + } + /// Storage: Salary Status (r:1 w:0) + /// Proof: Salary Status (max_values: Some(1), max_size: Some(56), added: 551, mode: MaxEncodedLen) + /// Storage: RankedCollective Members (r:1 w:0) + /// Proof: RankedCollective Members (max_values: None, max_size: Some(42), added: 2517, mode: MaxEncodedLen) + /// Storage: Salary Claimant (r:1 w:1) + /// Proof: Salary Claimant (max_values: None, max_size: Some(78), added: 2553, mode: MaxEncodedLen) + fn induct() -> Weight { + // Proof Size summary in bytes: + // Measured: `1510` + // Estimated: `5621` + // Minimum execution time: 32_223 nanoseconds. + Weight::from_ref_time(32_663_000) + .saturating_add(Weight::from_proof_size(5621)) + .saturating_add(RocksDbWeight::get().reads(3_u64)) + .saturating_add(RocksDbWeight::get().writes(1_u64)) + } + /// Storage: RankedCollective Members (r:1 w:0) + /// Proof: RankedCollective Members (max_values: None, max_size: Some(42), added: 2517, mode: MaxEncodedLen) + /// Storage: Salary Status (r:1 w:1) + /// Proof: Salary Status (max_values: Some(1), max_size: Some(56), added: 551, mode: MaxEncodedLen) + /// Storage: Salary Claimant (r:1 w:1) + /// Proof: Salary Claimant (max_values: None, max_size: Some(78), added: 2553, mode: MaxEncodedLen) + fn register() -> Weight { + // Proof Size summary in bytes: + // Measured: `1616` + // Estimated: `5621` + // Minimum execution time: 38_279 nanoseconds. + Weight::from_ref_time(38_996_000) + .saturating_add(Weight::from_proof_size(5621)) + .saturating_add(RocksDbWeight::get().reads(3_u64)) + .saturating_add(RocksDbWeight::get().writes(2_u64)) + } + /// Storage: Salary Status (r:1 w:1) + /// Proof: Salary Status (max_values: Some(1), max_size: Some(56), added: 551, mode: MaxEncodedLen) + /// Storage: Salary Claimant (r:1 w:1) + /// Proof: Salary Claimant (max_values: None, max_size: Some(78), added: 2553, mode: MaxEncodedLen) + /// Storage: RankedCollective Members (r:1 w:0) + /// Proof: RankedCollective Members (max_values: None, max_size: Some(42), added: 2517, mode: MaxEncodedLen) + fn payout() -> Weight { + // Proof Size summary in bytes: + // Measured: `2241` + // Estimated: `5621` + // Minimum execution time: 68_868 nanoseconds. + Weight::from_ref_time(70_160_000) + .saturating_add(Weight::from_proof_size(5621)) + .saturating_add(RocksDbWeight::get().reads(3_u64)) + .saturating_add(RocksDbWeight::get().writes(2_u64)) + } + /// Storage: Salary Status (r:1 w:1) + /// Proof: Salary Status (max_values: Some(1), max_size: Some(56), added: 551, mode: MaxEncodedLen) + /// Storage: Salary Claimant (r:1 w:1) + /// Proof: Salary Claimant (max_values: None, max_size: Some(78), added: 2553, mode: MaxEncodedLen) + /// Storage: RankedCollective Members (r:1 w:0) + /// Proof: RankedCollective Members (max_values: None, max_size: Some(42), added: 2517, mode: MaxEncodedLen) + /// Storage: System Account (r:1 w:1) + /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) + fn payout_other() -> Weight { + // Proof Size summary in bytes: + // Measured: `2189` + // Estimated: `8224` + // Minimum execution time: 68_804 nanoseconds. + Weight::from_ref_time(69_223_000) + .saturating_add(Weight::from_proof_size(8224)) + .saturating_add(RocksDbWeight::get().reads(4_u64)) + .saturating_add(RocksDbWeight::get().writes(3_u64)) + } + /// Storage: Salary Status (r:1 w:1) + /// Proof: Salary Status (max_values: Some(1), max_size: Some(56), added: 551, mode: MaxEncodedLen) + /// Storage: Salary Claimant (r:1 w:1) + /// Proof: Salary Claimant (max_values: None, max_size: Some(78), added: 2553, mode: MaxEncodedLen) + fn check_payment() -> Weight { + // Proof Size summary in bytes: + // Measured: `880` + // Estimated: `3104` + // Minimum execution time: 19_027 nanoseconds. + Weight::from_ref_time(19_360_000) + .saturating_add(Weight::from_proof_size(3104)) + .saturating_add(RocksDbWeight::get().reads(2_u64)) + .saturating_add(RocksDbWeight::get().writes(2_u64)) + } +} diff --git a/frame/support/src/traits.rs b/frame/support/src/traits.rs index da8efe6af..4b92cfbae 100644 --- a/frame/support/src/traits.rs +++ b/frame/support/src/traits.rs @@ -36,7 +36,7 @@ pub use members::{AllowAll, DenyAll, Filter}; pub use members::{ AsContains, ChangeMembers, Contains, ContainsLengthBound, ContainsPair, Everything, EverythingBut, FromContainsPair, InitializeMembers, InsideBoth, IsInVec, Nothing, - SortedMembers, TheseExcept, + RankedMembers, SortedMembers, TheseExcept, }; mod validation; diff --git a/frame/support/src/traits/members.rs b/frame/support/src/traits/members.rs index f336b460c..fbba742eb 100644 --- a/frame/support/src/traits/members.rs +++ b/frame/support/src/traits/members.rs @@ -18,6 +18,8 @@ //! Traits for dealing with the idea of membership. use impl_trait_for_tuples::impl_for_tuples; +use sp_arithmetic::traits::AtLeast16BitUnsigned; +use sp_runtime::DispatchResult; use sp_std::{marker::PhantomData, prelude::*}; /// A trait for querying whether a type can be said to "contain" a value. @@ -265,6 +267,28 @@ pub trait ContainsLengthBound { fn max_len() -> usize; } +/// Ranked membership data structure. +pub trait RankedMembers { + type AccountId; + type Rank: AtLeast16BitUnsigned; + + /// The lowest rank possible in this membership organisation. + fn min_rank() -> Self::Rank; + + /// Return the rank of the given ID, or `None` if they are not a member. + fn rank_of(who: &Self::AccountId) -> Option; + + /// Add a member to the group at the `min_rank()`. + fn induct(who: &Self::AccountId) -> DispatchResult; + + /// Promote a member to the next higher rank. + fn promote(who: &Self::AccountId) -> DispatchResult; + + /// Demote a member to the next lower rank; demoting beyond the `min_rank` removes the + /// member entirely. + fn demote(who: &Self::AccountId) -> DispatchResult; +} + /// Trait for type that can handle the initialization of account IDs at genesis. pub trait InitializeMembers { /// Initialize the members to the given `members`. diff --git a/frame/support/src/traits/tokens.rs b/frame/support/src/traits/tokens.rs index 6055fde21..f8dcf6815 100644 --- a/frame/support/src/traits/tokens.rs +++ b/frame/support/src/traits/tokens.rs @@ -28,6 +28,7 @@ pub mod nonfungibles; pub mod nonfungibles_v2; pub use imbalance::Imbalance; pub use misc::{ - AssetId, AttributeNamespace, Balance, BalanceConversion, BalanceStatus, DepositConsequence, - ExistenceRequirement, Locker, WithdrawConsequence, WithdrawReasons, + AssetId, AttributeNamespace, Balance, BalanceConversion, BalanceStatus, ConvertRank, + DepositConsequence, ExistenceRequirement, GetSalary, Locker, WithdrawConsequence, + WithdrawReasons, }; diff --git a/frame/support/src/traits/tokens/misc.rs b/frame/support/src/traits/tokens/misc.rs index b4bd46401..eff65f362 100644 --- a/frame/support/src/traits/tokens/misc.rs +++ b/frame/support/src/traits/tokens/misc.rs @@ -20,7 +20,7 @@ use codec::{Decode, Encode, FullCodec, MaxEncodedLen}; use sp_arithmetic::traits::{AtLeast32BitUnsigned, Zero}; use sp_core::RuntimeDebug; -use sp_runtime::{ArithmeticError, DispatchError, TokenError}; +use sp_runtime::{traits::Convert, ArithmeticError, DispatchError, TokenError}; use sp_std::fmt::Debug; /// One of a number of consequences of withdrawing a fungible from an account. @@ -225,3 +225,18 @@ impl Locker for () { false } } + +/// Retrieve the salary for a member of a particular rank. +pub trait GetSalary { + /// Retrieve the salary for a given rank. The account ID is also supplied in case this changes + /// things. + fn get_salary(rank: Rank, who: &AccountId) -> Balance; +} + +/// Adapter for a rank-to-salary `Convert` implementation into a `GetSalary` implementation. +pub struct ConvertRank(sp_std::marker::PhantomData); +impl> GetSalary for ConvertRank { + fn get_salary(rank: R, _: &A) -> B { + C::convert(rank) + } +} diff --git a/primitives/arithmetic/src/traits.rs b/primitives/arithmetic/src/traits.rs index 91e13a832..84e3f60aa 100644 --- a/primitives/arithmetic/src/traits.rs +++ b/primitives/arithmetic/src/traits.rs @@ -148,6 +148,20 @@ impl< { } +/// A meta trait for arithmetic. +/// +/// Arithmetic types do all the usual stuff you'd expect numbers to do. They are guaranteed to +/// be able to represent at least `u16` values without loss, hence the trait implies `From` +/// and smaller integers. All other conversions are fallible. +pub trait AtLeast16Bit: BaseArithmetic + From {} + +impl> AtLeast16Bit for T {} + +/// A meta trait for arithmetic. Same as [`AtLeast16Bit `], but also bounded to be unsigned. +pub trait AtLeast16BitUnsigned: AtLeast16Bit + Unsigned {} + +impl AtLeast16BitUnsigned for T {} + /// A meta trait for arithmetic. /// /// Arithmetic types do all the usual stuff you'd expect numbers to do. They are guaranteed to From e923511e15ec5000acb2a44d3764020ea71b109d Mon Sep 17 00:00:00 2001 From: Sergej Sakac <73715684+Szegoo@users.noreply.github.com> Date: Fri, 3 Mar 2023 21:39:45 +0100 Subject: [PATCH 191/558] Assets pallet: Don't allow `set_min_balance` when sufficient (#13510) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Assets pallet: Don't allow set_min_balance when sufficient * fix * fix benchmark * ".git/.scripts/commands/bench/bench.sh" pallet dev pallet_assets * Update frame/assets/src/lib.rs * fix * ".git/.scripts/commands/bench/bench.sh" pallet dev pallet_assets --------- Co-authored-by: command-bot <> Co-authored-by: Bastian Köcher --- frame/assets/src/benchmarking.rs | 2 +- frame/assets/src/lib.rs | 8 +- frame/assets/src/tests.rs | 44 ++++- frame/assets/src/weights.rs | 304 +++++++++++++------------------ 4 files changed, 169 insertions(+), 189 deletions(-) diff --git a/frame/assets/src/benchmarking.rs b/frame/assets/src/benchmarking.rs index 0538e955b..5b4f1489d 100644 --- a/frame/assets/src/benchmarking.rs +++ b/frame/assets/src/benchmarking.rs @@ -472,7 +472,7 @@ benchmarks_instance_pallet! { } set_min_balance { - let (asset_id, caller, caller_lookup) = create_default_asset::(true); + let (asset_id, caller, caller_lookup) = create_default_asset::(false); }: _(SystemOrigin::Signed(caller.clone()), asset_id, 50u32.into()) verify { assert_last_event::(Event::AssetMinBalanceChanged { asset_id: asset_id.into(), new_min_balance: 50u32.into() }.into()); diff --git a/frame/assets/src/lib.rs b/frame/assets/src/lib.rs index 96da07806..662c20e00 100644 --- a/frame/assets/src/lib.rs +++ b/frame/assets/src/lib.rs @@ -1540,8 +1540,12 @@ pub mod pallet { ensure!(origin == details.owner, Error::::NoPermission); let old_min_balance = details.min_balance; - // Ensure that either the new min_balance is less than old min_balance or there aren't - // any accounts holding the asset. + // If the asset is marked as sufficient it won't be allowed to + // change the min_balance. + ensure!(!details.is_sufficient, Error::::NoPermission); + + // Ensure that either the new min_balance is less than old + // min_balance or there aren't any accounts holding the asset. ensure!( min_balance < old_min_balance || details.accounts == 0, Error::::NoPermission diff --git a/frame/assets/src/tests.rs b/frame/assets/src/tests.rs index e0f612e5c..a2788f7f4 100644 --- a/frame/assets/src/tests.rs +++ b/frame/assets/src/tests.rs @@ -1101,23 +1101,53 @@ fn set_min_balance_should_work() { Balances::make_free_balance_be(&1, 10); assert_ok!(Assets::create(RuntimeOrigin::signed(1), id, 1, 30)); - assert_ok!(Assets::mint(RuntimeOrigin::signed(1), id, 1, 50)); - // won't execute because there is an asset holder. + assert_ok!(Assets::mint(RuntimeOrigin::signed(1), id, 1, 100)); + // Won't execute because there is an asset holder. assert_noop!( Assets::set_min_balance(RuntimeOrigin::signed(1), id, 50), Error::::NoPermission ); - // will execute because the new value of min_balance is less than the - // old value. 10 < 30 - assert_ok!(Assets::mint(RuntimeOrigin::signed(1), id, 1, 10)); + // Force asset status to make this a sufficient asset. + assert_ok!(Assets::force_asset_status( + RuntimeOrigin::root(), + id, + 1, + 1, + 1, + 1, + 30, + true, + false + )); - assert_ok!(Assets::burn(RuntimeOrigin::signed(1), id, 1, 50)); + // Won't execute because there is an account holding the asset and the asset is marked as + // sufficient. assert_noop!( - Assets::set_min_balance(RuntimeOrigin::signed(2), id, 50), + Assets::set_min_balance(RuntimeOrigin::signed(1), id, 10), Error::::NoPermission ); + // Make the asset not sufficient. + assert_ok!(Assets::force_asset_status( + RuntimeOrigin::root(), + id, + 1, + 1, + 1, + 1, + 60, + false, + false + )); + + // Will execute because the new value of min_balance is less than the + // old value. 10 < 30 + assert_ok!(Assets::set_min_balance(RuntimeOrigin::signed(1), id, 10)); + assert_eq!(Asset::::get(id).unwrap().min_balance, 10); + + assert_ok!(Assets::burn(RuntimeOrigin::signed(1), id, 1, 100)); + assert_ok!(Assets::set_min_balance(RuntimeOrigin::signed(1), id, 50)); assert_eq!(Asset::::get(id).unwrap().min_balance, 50); }); diff --git a/frame/assets/src/weights.rs b/frame/assets/src/weights.rs index 080800daa..b9c3ab21d 100644 --- a/frame/assets/src/weights.rs +++ b/frame/assets/src/weights.rs @@ -18,7 +18,7 @@ //! Autogenerated weights for pallet_assets //! //! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev -//! DATE: 2023-02-28, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! DATE: 2023-03-03, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` //! WORST CASE MAP SIZE: `1000000` //! HOSTNAME: `bm3`, CPU: `Intel(R) Core(TM) i7-7700K CPU @ 4.20GHz` //! EXECUTION: Some(Wasm), WASM-EXECUTION: Compiled, CHAIN: Some("dev"), DB CACHE: 1024 @@ -89,9 +89,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `325` // Estimated: `7268` - // Minimum execution time: 27_887 nanoseconds. - Weight::from_parts(28_190_000, 0) - .saturating_add(Weight::from_parts(0, 7268)) + // Minimum execution time: 28_265_000 picoseconds. + Weight::from_parts(28_764_000, 7268) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().writes(2_u64)) } @@ -101,9 +100,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `153` // Estimated: `3675` - // Minimum execution time: 15_059 nanoseconds. - Weight::from_parts(15_600_000, 0) - .saturating_add(Weight::from_parts(0, 3675)) + // Minimum execution time: 15_125_000 picoseconds. + Weight::from_parts(15_468_000, 3675) .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -113,9 +111,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `417` // Estimated: `3675` - // Minimum execution time: 15_581 nanoseconds. - Weight::from_parts(15_868_000, 0) - .saturating_add(Weight::from_parts(0, 3675)) + // Minimum execution time: 15_368_000 picoseconds. + Weight::from_parts(15_625_000, 3675) .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -130,11 +127,10 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `25 + c * (240 ±0)` // Estimated: `8232 + c * (5180 ±0)` - // Minimum execution time: 20_167 nanoseconds. - Weight::from_parts(20_436_000, 0) - .saturating_add(Weight::from_parts(0, 8232)) - // Standard Error: 12_761 - .saturating_add(Weight::from_parts(15_535_268, 0).saturating_mul(c.into())) + // Minimum execution time: 20_816_000 picoseconds. + Weight::from_parts(21_045_000, 8232) + // Standard Error: 7_118 + .saturating_add(Weight::from_parts(12_723_454, 0).saturating_mul(c.into())) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().reads((2_u64).saturating_mul(c.into()))) .saturating_add(T::DbWeight::get().writes(1_u64)) @@ -150,11 +146,10 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `554 + a * (86 ±0)` // Estimated: `7288 + a * (2623 ±0)` - // Minimum execution time: 20_349 nanoseconds. - Weight::from_parts(20_482_000, 0) - .saturating_add(Weight::from_parts(0, 7288)) - // Standard Error: 9_831 - .saturating_add(Weight::from_parts(15_771_918, 0).saturating_mul(a.into())) + // Minimum execution time: 20_923_000 picoseconds. + Weight::from_parts(21_229_000, 7288) + // Standard Error: 7_215 + .saturating_add(Weight::from_parts(12_915_292, 0).saturating_mul(a.into())) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().reads((1_u64).saturating_mul(a.into()))) .saturating_add(T::DbWeight::get().writes(1_u64)) @@ -169,9 +164,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `383` // Estimated: `7280` - // Minimum execution time: 15_647 nanoseconds. - Weight::from_parts(15_975_000, 0) - .saturating_add(Weight::from_parts(0, 7280)) + // Minimum execution time: 15_764_000 picoseconds. + Weight::from_parts(16_245_000, 7280) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -183,9 +177,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `383` // Estimated: `7242` - // Minimum execution time: 28_383 nanoseconds. - Weight::from_parts(29_055_000, 0) - .saturating_add(Weight::from_parts(0, 7242)) + // Minimum execution time: 28_814_000 picoseconds. + Weight::from_parts(29_407_000, 7242) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().writes(2_u64)) } @@ -197,9 +190,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `491` // Estimated: `7242` - // Minimum execution time: 34_950 nanoseconds. - Weight::from_parts(35_296_000, 0) - .saturating_add(Weight::from_parts(0, 7242)) + // Minimum execution time: 34_784_000 picoseconds. + Weight::from_parts(35_402_000, 7242) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().writes(2_u64)) } @@ -213,9 +205,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `530` // Estimated: `13412` - // Minimum execution time: 50_129 nanoseconds. - Weight::from_parts(50_820_000, 0) - .saturating_add(Weight::from_parts(0, 13412)) + // Minimum execution time: 49_110_000 picoseconds. + Weight::from_parts(50_483_000, 13412) .saturating_add(T::DbWeight::get().reads(4_u64)) .saturating_add(T::DbWeight::get().writes(4_u64)) } @@ -229,9 +220,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `530` // Estimated: `13412` - // Minimum execution time: 43_424 nanoseconds. - Weight::from_parts(44_080_000, 0) - .saturating_add(Weight::from_parts(0, 13412)) + // Minimum execution time: 43_585_000 picoseconds. + Weight::from_parts(44_207_000, 13412) .saturating_add(T::DbWeight::get().reads(4_u64)) .saturating_add(T::DbWeight::get().writes(4_u64)) } @@ -245,9 +235,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `530` // Estimated: `13412` - // Minimum execution time: 48_919 nanoseconds. - Weight::from_parts(50_720_000, 0) - .saturating_add(Weight::from_parts(0, 13412)) + // Minimum execution time: 49_238_000 picoseconds. + Weight::from_parts(77_664_000, 13412) .saturating_add(T::DbWeight::get().reads(4_u64)) .saturating_add(T::DbWeight::get().writes(4_u64)) } @@ -259,9 +248,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `491` // Estimated: `7242` - // Minimum execution time: 19_750 nanoseconds. - Weight::from_parts(20_053_000, 0) - .saturating_add(Weight::from_parts(0, 7242)) + // Minimum execution time: 19_198_000 picoseconds. + Weight::from_parts(19_585_000, 7242) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -273,9 +261,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `491` // Estimated: `7242` - // Minimum execution time: 19_672 nanoseconds. - Weight::from_parts(19_928_000, 0) - .saturating_add(Weight::from_parts(0, 7242)) + // Minimum execution time: 19_659_000 picoseconds. + Weight::from_parts(20_079_000, 7242) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -285,9 +272,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `417` // Estimated: `3675` - // Minimum execution time: 15_367 nanoseconds. - Weight::from_parts(15_726_000, 0) - .saturating_add(Weight::from_parts(0, 3675)) + // Minimum execution time: 15_035_000 picoseconds. + Weight::from_parts(15_594_000, 3675) .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -297,9 +283,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `417` // Estimated: `3675` - // Minimum execution time: 14_814 nanoseconds. - Weight::from_parts(15_301_000, 0) - .saturating_add(Weight::from_parts(0, 3675)) + // Minimum execution time: 15_073_000 picoseconds. + Weight::from_parts(15_862_000, 3675) .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -311,9 +296,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `383` // Estimated: `7280` - // Minimum execution time: 17_426 nanoseconds. - Weight::from_parts(17_804_000, 0) - .saturating_add(Weight::from_parts(0, 7280)) + // Minimum execution time: 17_343_000 picoseconds. + Weight::from_parts(17_656_000, 7280) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -323,9 +307,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `383` // Estimated: `3675` - // Minimum execution time: 15_935 nanoseconds. - Weight::from_parts(16_165_000, 0) - .saturating_add(Weight::from_parts(0, 3675)) + // Minimum execution time: 15_902_000 picoseconds. + Weight::from_parts(16_439_000, 3675) .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -335,15 +318,12 @@ impl WeightInfo for SubstrateWeight { /// Proof: Assets Metadata (max_values: None, max_size: Some(140), added: 2615, mode: MaxEncodedLen) /// The range of component `n` is `[0, 50]`. /// The range of component `s` is `[0, 50]`. - fn set_metadata(n: u32, _s: u32, ) -> Weight { + fn set_metadata(_n: u32, _s: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `383` // Estimated: `7280` - // Minimum execution time: 26_890 nanoseconds. - Weight::from_parts(28_766_510, 0) - .saturating_add(Weight::from_parts(0, 7280)) - // Standard Error: 7_444 - .saturating_add(Weight::from_parts(3_619, 0).saturating_mul(n.into())) + // Minimum execution time: 27_222_000 picoseconds. + Weight::from_parts(29_151_657, 7280) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -355,9 +335,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `579` // Estimated: `7280` - // Minimum execution time: 27_146 nanoseconds. - Weight::from_parts(27_692_000, 0) - .saturating_add(Weight::from_parts(0, 7280)) + // Minimum execution time: 27_976_000 picoseconds. + Weight::from_parts(28_289_000, 7280) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -367,13 +346,14 @@ impl WeightInfo for SubstrateWeight { /// Proof: Assets Metadata (max_values: None, max_size: Some(140), added: 2615, mode: MaxEncodedLen) /// The range of component `n` is `[0, 50]`. /// The range of component `s` is `[0, 50]`. - fn force_set_metadata(_n: u32, _s: u32, ) -> Weight { + fn force_set_metadata(_n: u32, s: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `190` // Estimated: `7280` - // Minimum execution time: 16_181 nanoseconds. - Weight::from_parts(18_317_178, 0) - .saturating_add(Weight::from_parts(0, 7280)) + // Minimum execution time: 16_162_000 picoseconds. + Weight::from_parts(17_137_043, 7280) + // Standard Error: 982 + .saturating_add(Weight::from_parts(4_180, 0).saturating_mul(s.into())) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -385,9 +365,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `579` // Estimated: `7280` - // Minimum execution time: 26_962 nanoseconds. - Weight::from_parts(27_896_000, 0) - .saturating_add(Weight::from_parts(0, 7280)) + // Minimum execution time: 27_219_000 picoseconds. + Weight::from_parts(27_931_000, 7280) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -397,9 +376,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `383` // Estimated: `3675` - // Minimum execution time: 14_394 nanoseconds. - Weight::from_parts(14_917_000, 0) - .saturating_add(Weight::from_parts(0, 3675)) + // Minimum execution time: 15_313_000 picoseconds. + Weight::from_parts(15_775_000, 3675) .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -411,9 +389,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `417` // Estimated: `7288` - // Minimum execution time: 30_861 nanoseconds. - Weight::from_parts(31_356_000, 0) - .saturating_add(Weight::from_parts(0, 7288)) + // Minimum execution time: 31_865_000 picoseconds. + Weight::from_parts(32_316_000, 7288) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().writes(2_u64)) } @@ -429,9 +406,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `700` // Estimated: `17025` - // Minimum execution time: 64_510 nanoseconds. - Weight::from_parts(65_676_000, 0) - .saturating_add(Weight::from_parts(0, 17025)) + // Minimum execution time: 67_203_000 picoseconds. + Weight::from_parts(109_742_000, 17025) .saturating_add(T::DbWeight::get().reads(5_u64)) .saturating_add(T::DbWeight::get().writes(5_u64)) } @@ -443,9 +419,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `587` // Estimated: `7288` - // Minimum execution time: 32_620 nanoseconds. - Weight::from_parts(33_183_000, 0) - .saturating_add(Weight::from_parts(0, 7288)) + // Minimum execution time: 33_430_000 picoseconds. + Weight::from_parts(33_824_000, 7288) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().writes(2_u64)) } @@ -457,9 +432,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `587` // Estimated: `7288` - // Minimum execution time: 33_277 nanoseconds. - Weight::from_parts(34_438_000, 0) - .saturating_add(Weight::from_parts(0, 7288)) + // Minimum execution time: 33_596_000 picoseconds. + Weight::from_parts(34_226_000, 7288) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().writes(2_u64)) } @@ -469,9 +443,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `383` // Estimated: `3675` - // Minimum execution time: 16_213 nanoseconds. - Weight::from_parts(16_575_000, 0) - .saturating_add(Weight::from_parts(0, 3675)) + // Minimum execution time: 16_367_000 picoseconds. + Weight::from_parts(16_703_000, 3675) .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -487,9 +460,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `325` // Estimated: `7268` - // Minimum execution time: 27_887 nanoseconds. - Weight::from_parts(28_190_000, 0) - .saturating_add(Weight::from_parts(0, 7268)) + // Minimum execution time: 28_265_000 picoseconds. + Weight::from_parts(28_764_000, 7268) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().writes(2_u64)) } @@ -499,9 +471,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `153` // Estimated: `3675` - // Minimum execution time: 15_059 nanoseconds. - Weight::from_parts(15_600_000, 0) - .saturating_add(Weight::from_parts(0, 3675)) + // Minimum execution time: 15_125_000 picoseconds. + Weight::from_parts(15_468_000, 3675) .saturating_add(RocksDbWeight::get().reads(1_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } @@ -511,9 +482,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `417` // Estimated: `3675` - // Minimum execution time: 15_581 nanoseconds. - Weight::from_parts(15_868_000, 0) - .saturating_add(Weight::from_parts(0, 3675)) + // Minimum execution time: 15_368_000 picoseconds. + Weight::from_parts(15_625_000, 3675) .saturating_add(RocksDbWeight::get().reads(1_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } @@ -528,11 +498,10 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `25 + c * (240 ±0)` // Estimated: `8232 + c * (5180 ±0)` - // Minimum execution time: 20_167 nanoseconds. - Weight::from_parts(20_436_000, 0) - .saturating_add(Weight::from_parts(0, 8232)) - // Standard Error: 12_761 - .saturating_add(Weight::from_parts(15_535_268, 0).saturating_mul(c.into())) + // Minimum execution time: 20_816_000 picoseconds. + Weight::from_parts(21_045_000, 8232) + // Standard Error: 7_118 + .saturating_add(Weight::from_parts(12_723_454, 0).saturating_mul(c.into())) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().reads((2_u64).saturating_mul(c.into()))) .saturating_add(RocksDbWeight::get().writes(1_u64)) @@ -548,11 +517,10 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `554 + a * (86 ±0)` // Estimated: `7288 + a * (2623 ±0)` - // Minimum execution time: 20_349 nanoseconds. - Weight::from_parts(20_482_000, 0) - .saturating_add(Weight::from_parts(0, 7288)) - // Standard Error: 9_831 - .saturating_add(Weight::from_parts(15_771_918, 0).saturating_mul(a.into())) + // Minimum execution time: 20_923_000 picoseconds. + Weight::from_parts(21_229_000, 7288) + // Standard Error: 7_215 + .saturating_add(Weight::from_parts(12_915_292, 0).saturating_mul(a.into())) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().reads((1_u64).saturating_mul(a.into()))) .saturating_add(RocksDbWeight::get().writes(1_u64)) @@ -567,9 +535,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `383` // Estimated: `7280` - // Minimum execution time: 15_647 nanoseconds. - Weight::from_parts(15_975_000, 0) - .saturating_add(Weight::from_parts(0, 7280)) + // Minimum execution time: 15_764_000 picoseconds. + Weight::from_parts(16_245_000, 7280) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } @@ -581,9 +548,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `383` // Estimated: `7242` - // Minimum execution time: 28_383 nanoseconds. - Weight::from_parts(29_055_000, 0) - .saturating_add(Weight::from_parts(0, 7242)) + // Minimum execution time: 28_814_000 picoseconds. + Weight::from_parts(29_407_000, 7242) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().writes(2_u64)) } @@ -595,9 +561,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `491` // Estimated: `7242` - // Minimum execution time: 34_950 nanoseconds. - Weight::from_parts(35_296_000, 0) - .saturating_add(Weight::from_parts(0, 7242)) + // Minimum execution time: 34_784_000 picoseconds. + Weight::from_parts(35_402_000, 7242) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().writes(2_u64)) } @@ -611,9 +576,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `530` // Estimated: `13412` - // Minimum execution time: 50_129 nanoseconds. - Weight::from_parts(50_820_000, 0) - .saturating_add(Weight::from_parts(0, 13412)) + // Minimum execution time: 49_110_000 picoseconds. + Weight::from_parts(50_483_000, 13412) .saturating_add(RocksDbWeight::get().reads(4_u64)) .saturating_add(RocksDbWeight::get().writes(4_u64)) } @@ -627,9 +591,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `530` // Estimated: `13412` - // Minimum execution time: 43_424 nanoseconds. - Weight::from_parts(44_080_000, 0) - .saturating_add(Weight::from_parts(0, 13412)) + // Minimum execution time: 43_585_000 picoseconds. + Weight::from_parts(44_207_000, 13412) .saturating_add(RocksDbWeight::get().reads(4_u64)) .saturating_add(RocksDbWeight::get().writes(4_u64)) } @@ -643,9 +606,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `530` // Estimated: `13412` - // Minimum execution time: 48_919 nanoseconds. - Weight::from_parts(50_720_000, 0) - .saturating_add(Weight::from_parts(0, 13412)) + // Minimum execution time: 49_238_000 picoseconds. + Weight::from_parts(77_664_000, 13412) .saturating_add(RocksDbWeight::get().reads(4_u64)) .saturating_add(RocksDbWeight::get().writes(4_u64)) } @@ -657,9 +619,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `491` // Estimated: `7242` - // Minimum execution time: 19_750 nanoseconds. - Weight::from_parts(20_053_000, 0) - .saturating_add(Weight::from_parts(0, 7242)) + // Minimum execution time: 19_198_000 picoseconds. + Weight::from_parts(19_585_000, 7242) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } @@ -671,9 +632,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `491` // Estimated: `7242` - // Minimum execution time: 19_672 nanoseconds. - Weight::from_parts(19_928_000, 0) - .saturating_add(Weight::from_parts(0, 7242)) + // Minimum execution time: 19_659_000 picoseconds. + Weight::from_parts(20_079_000, 7242) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } @@ -683,9 +643,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `417` // Estimated: `3675` - // Minimum execution time: 15_367 nanoseconds. - Weight::from_parts(15_726_000, 0) - .saturating_add(Weight::from_parts(0, 3675)) + // Minimum execution time: 15_035_000 picoseconds. + Weight::from_parts(15_594_000, 3675) .saturating_add(RocksDbWeight::get().reads(1_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } @@ -695,9 +654,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `417` // Estimated: `3675` - // Minimum execution time: 14_814 nanoseconds. - Weight::from_parts(15_301_000, 0) - .saturating_add(Weight::from_parts(0, 3675)) + // Minimum execution time: 15_073_000 picoseconds. + Weight::from_parts(15_862_000, 3675) .saturating_add(RocksDbWeight::get().reads(1_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } @@ -709,9 +667,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `383` // Estimated: `7280` - // Minimum execution time: 17_426 nanoseconds. - Weight::from_parts(17_804_000, 0) - .saturating_add(Weight::from_parts(0, 7280)) + // Minimum execution time: 17_343_000 picoseconds. + Weight::from_parts(17_656_000, 7280) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } @@ -721,9 +678,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `383` // Estimated: `3675` - // Minimum execution time: 15_935 nanoseconds. - Weight::from_parts(16_165_000, 0) - .saturating_add(Weight::from_parts(0, 3675)) + // Minimum execution time: 15_902_000 picoseconds. + Weight::from_parts(16_439_000, 3675) .saturating_add(RocksDbWeight::get().reads(1_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } @@ -733,15 +689,12 @@ impl WeightInfo for () { /// Proof: Assets Metadata (max_values: None, max_size: Some(140), added: 2615, mode: MaxEncodedLen) /// The range of component `n` is `[0, 50]`. /// The range of component `s` is `[0, 50]`. - fn set_metadata(n: u32, _s: u32, ) -> Weight { + fn set_metadata(_n: u32, _s: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `383` // Estimated: `7280` - // Minimum execution time: 26_890 nanoseconds. - Weight::from_parts(28_766_510, 0) - .saturating_add(Weight::from_parts(0, 7280)) - // Standard Error: 7_444 - .saturating_add(Weight::from_parts(3_619, 0).saturating_mul(n.into())) + // Minimum execution time: 27_222_000 picoseconds. + Weight::from_parts(29_151_657, 7280) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } @@ -753,9 +706,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `579` // Estimated: `7280` - // Minimum execution time: 27_146 nanoseconds. - Weight::from_parts(27_692_000, 0) - .saturating_add(Weight::from_parts(0, 7280)) + // Minimum execution time: 27_976_000 picoseconds. + Weight::from_parts(28_289_000, 7280) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } @@ -765,13 +717,14 @@ impl WeightInfo for () { /// Proof: Assets Metadata (max_values: None, max_size: Some(140), added: 2615, mode: MaxEncodedLen) /// The range of component `n` is `[0, 50]`. /// The range of component `s` is `[0, 50]`. - fn force_set_metadata(_n: u32, _s: u32, ) -> Weight { + fn force_set_metadata(_n: u32, s: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `190` // Estimated: `7280` - // Minimum execution time: 16_181 nanoseconds. - Weight::from_parts(18_317_178, 0) - .saturating_add(Weight::from_parts(0, 7280)) + // Minimum execution time: 16_162_000 picoseconds. + Weight::from_parts(17_137_043, 7280) + // Standard Error: 982 + .saturating_add(Weight::from_parts(4_180, 0).saturating_mul(s.into())) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } @@ -783,9 +736,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `579` // Estimated: `7280` - // Minimum execution time: 26_962 nanoseconds. - Weight::from_parts(27_896_000, 0) - .saturating_add(Weight::from_parts(0, 7280)) + // Minimum execution time: 27_219_000 picoseconds. + Weight::from_parts(27_931_000, 7280) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } @@ -795,9 +747,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `383` // Estimated: `3675` - // Minimum execution time: 14_394 nanoseconds. - Weight::from_parts(14_917_000, 0) - .saturating_add(Weight::from_parts(0, 3675)) + // Minimum execution time: 15_313_000 picoseconds. + Weight::from_parts(15_775_000, 3675) .saturating_add(RocksDbWeight::get().reads(1_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } @@ -809,9 +760,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `417` // Estimated: `7288` - // Minimum execution time: 30_861 nanoseconds. - Weight::from_parts(31_356_000, 0) - .saturating_add(Weight::from_parts(0, 7288)) + // Minimum execution time: 31_865_000 picoseconds. + Weight::from_parts(32_316_000, 7288) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().writes(2_u64)) } @@ -827,9 +777,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `700` // Estimated: `17025` - // Minimum execution time: 64_510 nanoseconds. - Weight::from_parts(65_676_000, 0) - .saturating_add(Weight::from_parts(0, 17025)) + // Minimum execution time: 67_203_000 picoseconds. + Weight::from_parts(109_742_000, 17025) .saturating_add(RocksDbWeight::get().reads(5_u64)) .saturating_add(RocksDbWeight::get().writes(5_u64)) } @@ -841,9 +790,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `587` // Estimated: `7288` - // Minimum execution time: 32_620 nanoseconds. - Weight::from_parts(33_183_000, 0) - .saturating_add(Weight::from_parts(0, 7288)) + // Minimum execution time: 33_430_000 picoseconds. + Weight::from_parts(33_824_000, 7288) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().writes(2_u64)) } @@ -855,9 +803,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `587` // Estimated: `7288` - // Minimum execution time: 33_277 nanoseconds. - Weight::from_parts(34_438_000, 0) - .saturating_add(Weight::from_parts(0, 7288)) + // Minimum execution time: 33_596_000 picoseconds. + Weight::from_parts(34_226_000, 7288) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().writes(2_u64)) } @@ -867,9 +814,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `383` // Estimated: `3675` - // Minimum execution time: 16_213 nanoseconds. - Weight::from_parts(16_575_000, 0) - .saturating_add(Weight::from_parts(0, 3675)) + // Minimum execution time: 16_367_000 picoseconds. + Weight::from_parts(16_703_000, 3675) .saturating_add(RocksDbWeight::get().reads(1_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } From 2520dedb5521a7fe32ee494ce608b2f0da6179b3 Mon Sep 17 00:00:00 2001 From: Javier Viola Date: Fri, 3 Mar 2023 17:46:02 -0300 Subject: [PATCH 192/558] bump `zombienet` version to v1.3.37 (#13447) * bump zombienet version to v1.3.35 * remove checks, since now is checked by zn * Update .gitlab-ci.yml --------- Co-authored-by: Anton --- .gitlab-ci.yml | 2 +- zombienet/0000-block-building/block-building.zndsl | 3 --- zombienet/0001-basic-warp-sync/test-warp-sync.zndsl | 5 ----- .../test-validators-warp-sync.zndsl | 6 ------ .../test-block-building-warp-sync.zndsl | 5 ----- 5 files changed, 1 insertion(+), 20 deletions(-) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 336e97cdd..94fb3c608 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -55,7 +55,7 @@ variables: RUSTY_CACHIER_COMPRESSION_METHOD: zstd NEXTEST_FAILURE_OUTPUT: immediate-final NEXTEST_SUCCESS_OUTPUT: final - ZOMBIENET_IMAGE: "docker.io/paritytech/zombienet:v1.3.34" + ZOMBIENET_IMAGE: "docker.io/paritytech/zombienet:v1.3.37" .shared-default: &shared-default retry: diff --git a/zombienet/0000-block-building/block-building.zndsl b/zombienet/0000-block-building/block-building.zndsl index 86a547734..6ad5f3d89 100644 --- a/zombienet/0000-block-building/block-building.zndsl +++ b/zombienet/0000-block-building/block-building.zndsl @@ -2,9 +2,6 @@ Description: Block building Network: ./block-building.toml Creds: config -alice: is up within 30 seconds -bob: is up within 30 seconds - alice: reports node_roles is 4 bob: reports node_roles is 4 diff --git a/zombienet/0001-basic-warp-sync/test-warp-sync.zndsl b/zombienet/0001-basic-warp-sync/test-warp-sync.zndsl index 8ceb61c8b..dc84804b7 100644 --- a/zombienet/0001-basic-warp-sync/test-warp-sync.zndsl +++ b/zombienet/0001-basic-warp-sync/test-warp-sync.zndsl @@ -2,11 +2,6 @@ Description: Warp sync Network: ./test-warp-sync.toml Creds: config -alice: is up within 30 seconds -bob: is up within 30 seconds -charlie: is up within 30 seconds -dave: is up within 30 seconds - alice: reports node_roles is 1 bob: reports node_roles is 1 charlie: reports node_roles is 1 diff --git a/zombienet/0002-validators-warp-sync/test-validators-warp-sync.zndsl b/zombienet/0002-validators-warp-sync/test-validators-warp-sync.zndsl index 2110d6373..05c458fbf 100644 --- a/zombienet/0002-validators-warp-sync/test-validators-warp-sync.zndsl +++ b/zombienet/0002-validators-warp-sync/test-validators-warp-sync.zndsl @@ -2,12 +2,6 @@ Description: Warp sync Network: ./test-validators-warp-sync.toml Creds: config -alice: is up within 30 seconds -bob: is up within 30 seconds -charlie: is up within 30 seconds -dave: is up within 30 seconds -eve: is up within 30 seconds - alice: reports node_roles is 4 bob: reports node_roles is 4 charlie: reports node_roles is 1 diff --git a/zombienet/0003-block-building-warp-sync/test-block-building-warp-sync.zndsl b/zombienet/0003-block-building-warp-sync/test-block-building-warp-sync.zndsl index 441fa45a8..a4ba46017 100644 --- a/zombienet/0003-block-building-warp-sync/test-block-building-warp-sync.zndsl +++ b/zombienet/0003-block-building-warp-sync/test-block-building-warp-sync.zndsl @@ -2,11 +2,6 @@ Description: Warp sync Network: ./test-block-building-warp-sync.toml Creds: config -alice: is up within 30 seconds -bob: is up within 30 seconds -charlie: is up within 30 seconds -dave: is up within 30 seconds - alice: reports node_roles is 4 bob: reports node_roles is 4 charlie: reports node_roles is 1 From c2034a822bdb423bfee5f077a4c40f1c2c797f85 Mon Sep 17 00:00:00 2001 From: Gavin Wood Date: Sat, 4 Mar 2023 11:38:22 +0000 Subject: [PATCH 193/558] Just a typo (#13533) * Typo * Update Salary weight Signed-off-by: Oliver Tale-Yazdi * Fix test Signed-off-by: Oliver Tale-Yazdi --------- Signed-off-by: Oliver Tale-Yazdi Co-authored-by: Oliver Tale-Yazdi --- frame/message-queue/src/lib.rs | 2 +- frame/salary/src/tests.rs | 2 +- frame/salary/src/weights.rs | 141 +++++++++++++++------------------ 3 files changed, 65 insertions(+), 80 deletions(-) diff --git a/frame/message-queue/src/lib.rs b/frame/message-queue/src/lib.rs index 6c264be3c..409503d31 100644 --- a/frame/message-queue/src/lib.rs +++ b/frame/message-queue/src/lib.rs @@ -56,7 +56,7 @@ //! **Pagination** //! //! Queues are stored in a *paged* manner by splitting their messages into [`Page`]s. This results -//! in a lot of complexity when implementing the pallet but is completely necessary to archive the +//! in a lot of complexity when implementing the pallet but is completely necessary to achieve the //! second #[Design Goal](design-goals). The problem comes from the fact a message can *possibly* be //! quite large, lets say 64KiB. This then results in a *MEL* of at least 64KiB which results in a //! PoV of at least 64KiB. Now we have the assumption that most messages are much shorter than their diff --git a/frame/salary/src/tests.rs b/frame/salary/src/tests.rs index 864929122..e54b9612b 100644 --- a/frame/salary/src/tests.rs +++ b/frame/salary/src/tests.rs @@ -52,7 +52,7 @@ frame_support::construct_runtime!( parameter_types! { pub BlockWeights: frame_system::limits::BlockWeights = - frame_system::limits::BlockWeights::simple_max(Weight::from_ref_time(1_000_000)); + frame_system::limits::BlockWeights::simple_max(Weight::from_parts(1_000_000, 0)); } impl frame_system::Config for Test { type BaseCallFilter = Everything; diff --git a/frame/salary/src/weights.rs b/frame/salary/src/weights.rs index f678d086d..6b43c58b6 100644 --- a/frame/salary/src/weights.rs +++ b/frame/salary/src/weights.rs @@ -18,27 +18,26 @@ //! Autogenerated weights for pallet_salary //! //! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev -//! DATE: 2023-02-26, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! DATE: 2023-03-03, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` //! WORST CASE MAP SIZE: `1000000` -//! HOSTNAME: `bm3`, CPU: `Intel(R) Core(TM) i7-7700K CPU @ 4.20GHz` +//! HOSTNAME: `bm2`, CPU: `Intel(R) Core(TM) i7-7700K CPU @ 4.20GHz` //! EXECUTION: Some(Wasm), WASM-EXECUTION: Compiled, CHAIN: Some("dev"), DB CACHE: 1024 // Executed Command: -// target/production/substrate +// ./target/production/substrate // benchmark // pallet // --steps=50 // --repeat=20 // --extrinsic=* +// --pallet=pallet_salary // --execution=wasm // --wasm-execution=compiled // --heap-pages=4096 -// --json-file=/var/lib/gitlab-runner/builds/zyw4fam_/0/parity/mirrors/substrate/.git/.artifacts/bench.json -// --pallet=pallet_salary // --chain=dev -// --header=./HEADER-APACHE2 -// --output=./frame/salary/src/weights.rs -// --template=./.maintain/frame-weight-template.hbs +// --header=HEADER-APACHE2 +// --output=frame/salary/src/weights.rs +// --template=.maintain/frame-weight-template.hbs #![cfg_attr(rustfmt, rustfmt_skip)] #![allow(unused_parens)] @@ -65,11 +64,10 @@ impl WeightInfo for SubstrateWeight { /// Proof: Salary Status (max_values: Some(1), max_size: Some(56), added: 551, mode: MaxEncodedLen) fn init() -> Weight { // Proof Size summary in bytes: - // Measured: `1120` - // Estimated: `551` - // Minimum execution time: 21_058 nanoseconds. - Weight::from_ref_time(21_381_000) - .saturating_add(Weight::from_proof_size(551)) + // Measured: `4` + // Estimated: `1541` + // Minimum execution time: 11_793_000 picoseconds. + Weight::from_parts(12_080_000, 1541) .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -77,11 +75,10 @@ impl WeightInfo for SubstrateWeight { /// Proof: Salary Status (max_values: Some(1), max_size: Some(56), added: 551, mode: MaxEncodedLen) fn bump() -> Weight { // Proof Size summary in bytes: - // Measured: `1234` - // Estimated: `551` - // Minimum execution time: 22_272 nanoseconds. - Weight::from_ref_time(22_923_000) - .saturating_add(Weight::from_proof_size(551)) + // Measured: `118` + // Estimated: `1541` + // Minimum execution time: 12_799_000 picoseconds. + Weight::from_parts(13_343_000, 1541) .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -93,11 +90,10 @@ impl WeightInfo for SubstrateWeight { /// Proof: Salary Claimant (max_values: None, max_size: Some(78), added: 2553, mode: MaxEncodedLen) fn induct() -> Weight { // Proof Size summary in bytes: - // Measured: `1510` - // Estimated: `5621` - // Minimum execution time: 32_223 nanoseconds. - Weight::from_ref_time(32_663_000) - .saturating_add(Weight::from_proof_size(5621)) + // Measured: `394` + // Estimated: `8591` + // Minimum execution time: 20_299_000 picoseconds. + Weight::from_parts(20_664_000, 8591) .saturating_add(T::DbWeight::get().reads(3_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -109,11 +105,10 @@ impl WeightInfo for SubstrateWeight { /// Proof: Salary Claimant (max_values: None, max_size: Some(78), added: 2553, mode: MaxEncodedLen) fn register() -> Weight { // Proof Size summary in bytes: - // Measured: `1616` - // Estimated: `5621` - // Minimum execution time: 38_279 nanoseconds. - Weight::from_ref_time(38_996_000) - .saturating_add(Weight::from_proof_size(5621)) + // Measured: `461` + // Estimated: `8591` + // Minimum execution time: 24_195_000 picoseconds. + Weight::from_parts(24_528_000, 8591) .saturating_add(T::DbWeight::get().reads(3_u64)) .saturating_add(T::DbWeight::get().writes(2_u64)) } @@ -125,11 +120,10 @@ impl WeightInfo for SubstrateWeight { /// Proof: RankedCollective Members (max_values: None, max_size: Some(42), added: 2517, mode: MaxEncodedLen) fn payout() -> Weight { // Proof Size summary in bytes: - // Measured: `2241` - // Estimated: `5621` - // Minimum execution time: 68_868 nanoseconds. - Weight::from_ref_time(70_160_000) - .saturating_add(Weight::from_proof_size(5621)) + // Measured: `461` + // Estimated: `8591` + // Minimum execution time: 47_609_000 picoseconds. + Weight::from_parts(48_462_000, 8591) .saturating_add(T::DbWeight::get().reads(3_u64)) .saturating_add(T::DbWeight::get().writes(2_u64)) } @@ -143,11 +137,10 @@ impl WeightInfo for SubstrateWeight { /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) fn payout_other() -> Weight { // Proof Size summary in bytes: - // Measured: `2189` - // Estimated: `8224` - // Minimum execution time: 68_804 nanoseconds. - Weight::from_ref_time(69_223_000) - .saturating_add(Weight::from_proof_size(8224)) + // Measured: `461` + // Estimated: `12184` + // Minimum execution time: 48_513_000 picoseconds. + Weight::from_parts(49_053_000, 12184) .saturating_add(T::DbWeight::get().reads(4_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) } @@ -157,11 +150,10 @@ impl WeightInfo for SubstrateWeight { /// Proof: Salary Claimant (max_values: None, max_size: Some(78), added: 2553, mode: MaxEncodedLen) fn check_payment() -> Weight { // Proof Size summary in bytes: - // Measured: `880` - // Estimated: `3104` - // Minimum execution time: 19_027 nanoseconds. - Weight::from_ref_time(19_360_000) - .saturating_add(Weight::from_proof_size(3104)) + // Measured: `202` + // Estimated: `5084` + // Minimum execution time: 12_663_000 picoseconds. + Weight::from_parts(12_858_000, 5084) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().writes(2_u64)) } @@ -173,11 +165,10 @@ impl WeightInfo for () { /// Proof: Salary Status (max_values: Some(1), max_size: Some(56), added: 551, mode: MaxEncodedLen) fn init() -> Weight { // Proof Size summary in bytes: - // Measured: `1120` - // Estimated: `551` - // Minimum execution time: 21_058 nanoseconds. - Weight::from_ref_time(21_381_000) - .saturating_add(Weight::from_proof_size(551)) + // Measured: `4` + // Estimated: `1541` + // Minimum execution time: 11_793_000 picoseconds. + Weight::from_parts(12_080_000, 1541) .saturating_add(RocksDbWeight::get().reads(1_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } @@ -185,11 +176,10 @@ impl WeightInfo for () { /// Proof: Salary Status (max_values: Some(1), max_size: Some(56), added: 551, mode: MaxEncodedLen) fn bump() -> Weight { // Proof Size summary in bytes: - // Measured: `1234` - // Estimated: `551` - // Minimum execution time: 22_272 nanoseconds. - Weight::from_ref_time(22_923_000) - .saturating_add(Weight::from_proof_size(551)) + // Measured: `118` + // Estimated: `1541` + // Minimum execution time: 12_799_000 picoseconds. + Weight::from_parts(13_343_000, 1541) .saturating_add(RocksDbWeight::get().reads(1_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } @@ -201,11 +191,10 @@ impl WeightInfo for () { /// Proof: Salary Claimant (max_values: None, max_size: Some(78), added: 2553, mode: MaxEncodedLen) fn induct() -> Weight { // Proof Size summary in bytes: - // Measured: `1510` - // Estimated: `5621` - // Minimum execution time: 32_223 nanoseconds. - Weight::from_ref_time(32_663_000) - .saturating_add(Weight::from_proof_size(5621)) + // Measured: `394` + // Estimated: `8591` + // Minimum execution time: 20_299_000 picoseconds. + Weight::from_parts(20_664_000, 8591) .saturating_add(RocksDbWeight::get().reads(3_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } @@ -217,11 +206,10 @@ impl WeightInfo for () { /// Proof: Salary Claimant (max_values: None, max_size: Some(78), added: 2553, mode: MaxEncodedLen) fn register() -> Weight { // Proof Size summary in bytes: - // Measured: `1616` - // Estimated: `5621` - // Minimum execution time: 38_279 nanoseconds. - Weight::from_ref_time(38_996_000) - .saturating_add(Weight::from_proof_size(5621)) + // Measured: `461` + // Estimated: `8591` + // Minimum execution time: 24_195_000 picoseconds. + Weight::from_parts(24_528_000, 8591) .saturating_add(RocksDbWeight::get().reads(3_u64)) .saturating_add(RocksDbWeight::get().writes(2_u64)) } @@ -233,11 +221,10 @@ impl WeightInfo for () { /// Proof: RankedCollective Members (max_values: None, max_size: Some(42), added: 2517, mode: MaxEncodedLen) fn payout() -> Weight { // Proof Size summary in bytes: - // Measured: `2241` - // Estimated: `5621` - // Minimum execution time: 68_868 nanoseconds. - Weight::from_ref_time(70_160_000) - .saturating_add(Weight::from_proof_size(5621)) + // Measured: `461` + // Estimated: `8591` + // Minimum execution time: 47_609_000 picoseconds. + Weight::from_parts(48_462_000, 8591) .saturating_add(RocksDbWeight::get().reads(3_u64)) .saturating_add(RocksDbWeight::get().writes(2_u64)) } @@ -251,11 +238,10 @@ impl WeightInfo for () { /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) fn payout_other() -> Weight { // Proof Size summary in bytes: - // Measured: `2189` - // Estimated: `8224` - // Minimum execution time: 68_804 nanoseconds. - Weight::from_ref_time(69_223_000) - .saturating_add(Weight::from_proof_size(8224)) + // Measured: `461` + // Estimated: `12184` + // Minimum execution time: 48_513_000 picoseconds. + Weight::from_parts(49_053_000, 12184) .saturating_add(RocksDbWeight::get().reads(4_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) } @@ -265,11 +251,10 @@ impl WeightInfo for () { /// Proof: Salary Claimant (max_values: None, max_size: Some(78), added: 2553, mode: MaxEncodedLen) fn check_payment() -> Weight { // Proof Size summary in bytes: - // Measured: `880` - // Estimated: `3104` - // Minimum execution time: 19_027 nanoseconds. - Weight::from_ref_time(19_360_000) - .saturating_add(Weight::from_proof_size(3104)) + // Measured: `202` + // Estimated: `5084` + // Minimum execution time: 12_663_000 picoseconds. + Weight::from_parts(12_858_000, 5084) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().writes(2_u64)) } From f57ff377c13599696772e3c98ddd473717b8f8c2 Mon Sep 17 00:00:00 2001 From: abebeos <110243666+abebeos@users.noreply.github.com> Date: Sat, 4 Mar 2023 15:01:04 +0200 Subject: [PATCH 194/558] FRAME: add a basic readme (#13520) --- frame/README.md | 11 +++++++++++ 1 file changed, 11 insertions(+) create mode 100644 frame/README.md diff --git a/frame/README.md b/frame/README.md new file mode 100644 index 000000000..47a7892c2 --- /dev/null +++ b/frame/README.md @@ -0,0 +1,11 @@ +# FRAME + +The FRAME development environment provides modules (called "pallets") and support libraries that you can use, modify, and extend to build the runtime logic to suit the needs of your blockchain. + +### Documentation + +https://docs.substrate.io/reference/frame-pallets/ + +### Issues + +https://github.com/orgs/paritytech/projects/40 From 7d8aa669e32a544e80ddf807211186b30fedd343 Mon Sep 17 00:00:00 2001 From: Kian Paimani <5588131+kianenigma@users.noreply.github.com> Date: Sun, 5 Mar 2023 15:38:49 -0600 Subject: [PATCH 195/558] Update staking related code-owners (#13462) --- docs/CODEOWNERS | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/docs/CODEOWNERS b/docs/CODEOWNERS index 4377e7ff0..95ee8cc66 100644 --- a/docs/CODEOWNERS +++ b/docs/CODEOWNERS @@ -59,10 +59,11 @@ /frame/contracts/ @athei # NPoS and election -/frame/staking/ @kianenigma -/frame/elections/ @kianenigma -/frame/elections-phragmen/ @kianenigma -/primitives/npos-elections/ @kianenigma +/frame/election-provider-support/ @paritytech/staking-core +/frame/staking/ @paritytech/staking-core +/frame/nomination-pools/ @paritytech/staking-core +/frame/elections-phragmen/ @paritytech/staking-core +/primitives/npos-elections/ @paritytech/staking-core # Fixed point arithmetic /primitives/sp-arithmetic/ @kianenigma From 1d9a139dd96bf5d1583c725bae6fcac5a0805372 Mon Sep 17 00:00:00 2001 From: Sasha Gryaznov Date: Mon, 6 Mar 2023 10:40:03 +0200 Subject: [PATCH 196/558] [contracts] Forbid calling back to contracts after switching to runtime (#13443) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * save: compiles and tests pass * save: added global * done + test * cleanup * changelog update * cleanup * address feedback, step 1 * address feedback, step 2 * address feedback, step 3 * returned updated gas_estimation_call_runtime test * clippy fix * address feedback, step 4 * address feedback, step 5 * move data from context to inputs * docs fix * Apply suggestions from code review Co-authored-by: Alexander Theißen * address feedback, step 6 --------- Co-authored-by: Alexander Theißen --- Cargo.lock | 1 + frame/contracts/CHANGELOG.md | 3 + frame/contracts/Cargo.toml | 2 + frame/contracts/src/lib.rs | 359 +++++++++++++++++++++-------------- frame/contracts/src/tests.rs | 72 ++++++- 5 files changed, 287 insertions(+), 150 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index d67aeda9f..5a215553d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -5598,6 +5598,7 @@ dependencies = [ "assert_matches", "bitflags", "env_logger 0.9.3", + "environmental", "frame-benchmarking", "frame-support", "frame-system", diff --git a/frame/contracts/CHANGELOG.md b/frame/contracts/CHANGELOG.md index ab3998e6d..dcb9d6d4d 100644 --- a/frame/contracts/CHANGELOG.md +++ b/frame/contracts/CHANGELOG.md @@ -20,6 +20,9 @@ In other words: Upgrading this pallet will not break pre-existing contracts. ### Added +- Forbid calling back to contracts after switching to runtime +[#13443](https://github.com/paritytech/substrate/pull/13443) + - Allow contracts to dispatch calls into the runtime (**unstable**) [#9276](https://github.com/paritytech/substrate/pull/9276) diff --git a/frame/contracts/Cargo.toml b/frame/contracts/Cargo.toml index cd41ae2e3..e19326b78 100644 --- a/frame/contracts/Cargo.toml +++ b/frame/contracts/Cargo.toml @@ -35,6 +35,7 @@ rand = { version = "0.8", optional = true, default-features = false } rand_pcg = { version = "0.3", optional = true } # Substrate Dependencies +environmental = { version = "1.1.4", default-features = false } frame-benchmarking = { version = "4.0.0-dev", default-features = false, path = "../benchmarking", optional = true } frame-support = { version = "4.0.0-dev", default-features = false, path = "../support" } frame-system = { version = "4.0.0-dev", default-features = false, path = "../system" } @@ -80,6 +81,7 @@ std = [ "log/std", "rand/std", "wasmparser/std", + "environmental/std", ] runtime-benchmarks = [ "frame-benchmarking/runtime-benchmarks", diff --git a/frame/contracts/src/lib.rs b/frame/contracts/src/lib.rs index a115cf119..4f5d7ba30 100644 --- a/frame/contracts/src/lib.rs +++ b/frame/contracts/src/lib.rs @@ -100,13 +100,14 @@ pub mod weights; mod tests; use crate::{ - exec::{AccountIdOf, ExecError, Executable, Stack as ExecStack}, + exec::{AccountIdOf, ErrorOrigin, ExecError, Executable, Stack as ExecStack}, gas::GasMeter, storage::{meter::Meter as StorageMeter, ContractInfo, DeletedContract}, wasm::{OwnerInfo, PrefabWasmModule, TryInstantiate}, weights::WeightInfo, }; use codec::{Codec, Encode, HasCompact}; +use environmental::*; use frame_support::{ dispatch::{Dispatchable, GetDispatchInfo, Pays, PostDispatchInfo}, ensure, @@ -616,16 +617,16 @@ pub mod pallet { let gas_limit: Weight = gas_limit.into(); let origin = ensure_signed(origin)?; let dest = T::Lookup::lookup(dest)?; - let mut output = Self::internal_call( + let common = CommonInput { origin, - dest, value, - gas_limit, - storage_deposit_limit.map(Into::into), data, - None, - Determinism::Deterministic, - ); + gas_limit, + storage_deposit_limit: storage_deposit_limit.map(Into::into), + debug_message: None, + }; + let mut output = CallInput:: { dest, determinism: Determinism::Deterministic } + .run_guarded(common); if let Ok(retval) = &output.result { if retval.did_revert() { output.result = Err(>::ContractReverted.into()); @@ -678,16 +679,16 @@ pub mod pallet { let code_len = code.len() as u32; let data_len = data.len() as u32; let salt_len = salt.len() as u32; - let mut output = Self::internal_instantiate( + let common = CommonInput { origin, value, - gas_limit, - storage_deposit_limit.map(Into::into), - Code::Upload(code), data, - salt, - None, - ); + gas_limit, + storage_deposit_limit: storage_deposit_limit.map(Into::into), + debug_message: None, + }; + let mut output = + InstantiateInput:: { code: Code::Upload(code), salt }.run_guarded(common); if let Ok(retval) = &output.result { if retval.1.did_revert() { output.result = Err(>::ContractReverted.into()); @@ -720,16 +721,16 @@ pub mod pallet { let origin = ensure_signed(origin)?; let data_len = data.len() as u32; let salt_len = salt.len() as u32; - let mut output = Self::internal_instantiate( + let common = CommonInput { origin, value, - gas_limit, - storage_deposit_limit.map(Into::into), - Code::Existing(code_hash), data, - salt, - None, - ); + gas_limit, + storage_deposit_limit: storage_deposit_limit.map(Into::into), + debug_message: None, + }; + let mut output = + InstantiateInput:: { code: Code::Existing(code_hash), salt }.run_guarded(common); if let Ok(retval) = &output.result { if retval.1.did_revert() { output.result = Err(>::ContractReverted.into()); @@ -872,6 +873,9 @@ pub mod pallet { /// This can be triggered by a call to `seal_terminate`. TerminatedInConstructor, /// A call tried to invoke a contract that is flagged as non-reentrant. + /// The only other cause is that a call from a contract into the runtime tried to call back + /// into `pallet-contracts`. This would make the whole pallet reentrant with regard to + /// contract code execution which is not supported. ReentranceDenied, /// Origin doesn't have enough balance to pay the required storage deposits. StorageDepositNotEnoughFunds, @@ -951,11 +955,27 @@ pub mod pallet { StorageValue<_, BoundedVec, ValueQuery>; } -/// Return type of the private [`Pallet::internal_call`] function. -type InternalCallOutput = InternalOutput; +/// Context of a contract invocation. +struct CommonInput<'a, T: Config> { + origin: T::AccountId, + value: BalanceOf, + data: Vec, + gas_limit: Weight, + storage_deposit_limit: Option>, + debug_message: Option<&'a mut DebugBufferVec>, +} -/// Return type of the private [`Pallet::internal_instantiate`] function. -type InternalInstantiateOutput = InternalOutput, ExecReturnValue)>; +/// Input specific to a call into contract. +struct CallInput { + dest: T::AccountId, + determinism: Determinism, +} + +/// Input specific to a contract instantiation invocation. +struct InstantiateInput { + code: Code>, + salt: Vec, +} /// Return type of private helper functions. struct InternalOutput { @@ -967,6 +987,164 @@ struct InternalOutput { result: Result, } +/// Helper trait to wrap contract execution entry points into a signle function +/// [`Invokable::run_guarded`]. +trait Invokable { + /// What is returned as a result of a successful invocation. + type Output; + + /// Single entry point to contract execution. + /// Downstream execution flow is branched by implementations of [`Invokable`] trait: + /// + /// - [`InstantiateInput::run`] runs contract instantiation, + /// - [`CallInput::run`] runs contract call. + /// + /// We enforce a re-entrancy guard here by initializing and checking a boolean flag through a + /// global reference. + fn run_guarded(&self, common: CommonInput) -> InternalOutput { + // Set up a global reference to the boolean flag used for the re-entrancy guard. + environmental!(executing_contract: bool); + + let gas_limit = common.gas_limit; + executing_contract::using_once(&mut false, || { + executing_contract::with(|f| { + // Fail if already entered contract execution + if *f { + return Err(()) + } + // We are entering contract execution + *f = true; + Ok(()) + }) + .expect("Returns `Ok` if called within `using_once`. It is syntactically obvious that this is the case; qed") + .map_or_else( + |_| InternalOutput { + gas_meter: GasMeter::new(gas_limit), + storage_deposit: Default::default(), + result: Err(ExecError { + error: >::ReentranceDenied.into(), + origin: ErrorOrigin::Caller, + }), + }, + // Enter contract call. + |_| self.run(common, GasMeter::new(gas_limit)), + ) + }) + } + + /// Method that does the actual call to a contract. It can be either a call to a deployed + /// contract or a instantiation of a new one. + /// + /// Called by dispatchables and public functions through the [`Invokable::run_guarded`]. + fn run( + &self, + common: CommonInput, + gas_meter: GasMeter, + ) -> InternalOutput; +} + +impl Invokable for CallInput { + type Output = ExecReturnValue; + + fn run( + &self, + common: CommonInput, + mut gas_meter: GasMeter, + ) -> InternalOutput { + let mut storage_meter = + match StorageMeter::new(&common.origin, common.storage_deposit_limit, common.value) { + Ok(meter) => meter, + Err(err) => + return InternalOutput { + result: Err(err.into()), + gas_meter, + storage_deposit: Default::default(), + }, + }; + let schedule = T::Schedule::get(); + let CallInput { dest, determinism } = self; + let CommonInput { origin, value, data, debug_message, .. } = common; + let result = ExecStack::>::run_call( + origin.clone(), + dest.clone(), + &mut gas_meter, + &mut storage_meter, + &schedule, + value, + data.clone(), + debug_message, + *determinism, + ); + InternalOutput { gas_meter, storage_deposit: storage_meter.into_deposit(&origin), result } + } +} + +impl Invokable for InstantiateInput { + type Output = (AccountIdOf, ExecReturnValue); + + fn run( + &self, + mut common: CommonInput, + mut gas_meter: GasMeter, + ) -> InternalOutput { + let mut storage_deposit = Default::default(); + let try_exec = || { + let schedule = T::Schedule::get(); + let (extra_deposit, executable) = match &self.code { + Code::Upload(binary) => { + let executable = PrefabWasmModule::from_code( + binary.clone(), + &schedule, + common.origin.clone(), + Determinism::Deterministic, + TryInstantiate::Skip, + ) + .map_err(|(err, msg)| { + common + .debug_message + .as_mut() + .map(|buffer| buffer.try_extend(&mut msg.bytes())); + err + })?; + // The open deposit will be charged during execution when the + // uploaded module does not already exist. This deposit is not part of the + // storage meter because it is not transferred to the contract but + // reserved on the uploading account. + (executable.open_deposit(), executable) + }, + Code::Existing(hash) => ( + Default::default(), + PrefabWasmModule::from_storage(*hash, &schedule, &mut gas_meter)?, + ), + }; + let mut storage_meter = StorageMeter::new( + &common.origin, + common.storage_deposit_limit, + common.value.saturating_add(extra_deposit), + )?; + + let InstantiateInput { salt, .. } = self; + let CommonInput { origin, value, data, debug_message, .. } = common; + let result = ExecStack::>::run_instantiate( + origin.clone(), + executable, + &mut gas_meter, + &mut storage_meter, + &schedule, + value, + data.clone(), + &salt, + debug_message, + ); + storage_deposit = storage_meter + .into_deposit(&origin) + .saturating_add(&StorageDeposit::Charge(extra_deposit)); + result + }; + InternalOutput { result: try_exec(), gas_meter, storage_deposit } + } +} + impl Pallet { /// Perform a call to a specified contract. /// @@ -991,16 +1169,15 @@ impl Pallet { determinism: Determinism, ) -> ContractExecResult> { let mut debug_message = if debug { Some(DebugBufferVec::::default()) } else { None }; - let output = Self::internal_call( + let common = CommonInput { origin, - dest, value, + data, gas_limit, storage_deposit_limit, - data, - debug_message.as_mut(), - determinism, - ); + debug_message: debug_message.as_mut(), + }; + let output = CallInput:: { dest, determinism }.run_guarded(common); ContractExecResult { result: output.result.map_err(|r| r.error), gas_consumed: output.gas_meter.gas_consumed(), @@ -1033,16 +1210,15 @@ impl Pallet { debug: bool, ) -> ContractInstantiateResult> { let mut debug_message = if debug { Some(DebugBufferVec::::default()) } else { None }; - let output = Self::internal_instantiate( + let common = CommonInput { origin, value, + data, gas_limit, storage_deposit_limit, - code, - data, - salt, - debug_message.as_mut(), - ); + debug_message: debug_message.as_mut(), + }; + let output = InstantiateInput:: { code, salt }.run_guarded(common); ContractInstantiateResult { result: output .result @@ -1132,113 +1308,6 @@ impl Pallet { self::wasm::reinstrument(module, schedule).map(|_| ()) } - /// Internal function that does the actual call. - /// - /// Called by dispatchables and public functions. - fn internal_call( - origin: T::AccountId, - dest: T::AccountId, - value: BalanceOf, - gas_limit: Weight, - storage_deposit_limit: Option>, - data: Vec, - debug_message: Option<&mut DebugBufferVec>, - determinism: Determinism, - ) -> InternalCallOutput { - let mut gas_meter = GasMeter::new(gas_limit); - let mut storage_meter = match StorageMeter::new(&origin, storage_deposit_limit, value) { - Ok(meter) => meter, - Err(err) => - return InternalCallOutput { - result: Err(err.into()), - gas_meter, - storage_deposit: Default::default(), - }, - }; - let schedule = T::Schedule::get(); - let result = ExecStack::>::run_call( - origin.clone(), - dest, - &mut gas_meter, - &mut storage_meter, - &schedule, - value, - data, - debug_message, - determinism, - ); - InternalCallOutput { - result, - gas_meter, - storage_deposit: storage_meter.into_deposit(&origin), - } - } - - /// Internal function that does the actual instantiation. - /// - /// Called by dispatchables and public functions. - fn internal_instantiate( - origin: T::AccountId, - value: BalanceOf, - gas_limit: Weight, - storage_deposit_limit: Option>, - code: Code>, - data: Vec, - salt: Vec, - mut debug_message: Option<&mut DebugBufferVec>, - ) -> InternalInstantiateOutput { - let mut storage_deposit = Default::default(); - let mut gas_meter = GasMeter::new(gas_limit); - let try_exec = || { - let schedule = T::Schedule::get(); - let (extra_deposit, executable) = match code { - Code::Upload(binary) => { - let executable = PrefabWasmModule::from_code( - binary, - &schedule, - origin.clone(), - Determinism::Deterministic, - TryInstantiate::Skip, - ) - .map_err(|(err, msg)| { - debug_message.as_mut().map(|buffer| buffer.try_extend(&mut msg.bytes())); - err - })?; - // The open deposit will be charged during execution when the - // uploaded module does not already exist. This deposit is not part of the - // storage meter because it is not transferred to the contract but - // reserved on the uploading account. - (executable.open_deposit(), executable) - }, - Code::Existing(hash) => ( - Default::default(), - PrefabWasmModule::from_storage(hash, &schedule, &mut gas_meter)?, - ), - }; - let mut storage_meter = StorageMeter::new( - &origin, - storage_deposit_limit, - value.saturating_add(extra_deposit), - )?; - let result = ExecStack::>::run_instantiate( - origin.clone(), - executable, - &mut gas_meter, - &mut storage_meter, - &schedule, - value, - data, - &salt, - debug_message, - ); - storage_deposit = storage_meter - .into_deposit(&origin) - .saturating_add(&StorageDeposit::Charge(extra_deposit)); - result - }; - InternalInstantiateOutput { result: try_exec(), gas_meter, storage_deposit } - } - /// Deposit a pallet contracts event. Handles the conversion to the overarching event type. fn deposit_event(topics: Vec, event: Event) { >::deposit_event_indexed( diff --git a/frame/contracts/src/tests.rs b/frame/contracts/src/tests.rs index cecac0338..d74b08243 100644 --- a/frame/contracts/src/tests.rs +++ b/frame/contracts/src/tests.rs @@ -2805,12 +2805,9 @@ fn gas_estimation_call_runtime() { // Call something trivial with a huge gas limit so that we can observe the effects // of pre-charging. This should create a difference between consumed and required. - let call = RuntimeCall::Contracts(crate::Call::call { + let call = RuntimeCall::Balances(pallet_balances::Call::transfer { dest: addr_callee, - value: 0, - gas_limit: GAS_LIMIT / 3, - storage_deposit_limit: None, - data: vec![], + value: min_balance * 10, }); let result = Contracts::bare_call( ALICE, @@ -2844,6 +2841,71 @@ fn gas_estimation_call_runtime() { }); } +#[test] +fn gas_call_runtime_reentrancy_guarded() { + let (caller_code, _caller_hash) = compile_module::("call_runtime").unwrap(); + let (callee_code, _callee_hash) = compile_module::("dummy").unwrap(); + ExtBuilder::default().existential_deposit(50).build().execute_with(|| { + let min_balance = ::Currency::minimum_balance(); + let _ = Balances::deposit_creating(&ALICE, 1000 * min_balance); + let _ = Balances::deposit_creating(&CHARLIE, 1000 * min_balance); + + let addr_caller = Contracts::bare_instantiate( + ALICE, + min_balance * 100, + GAS_LIMIT, + None, + Code::Upload(caller_code), + vec![], + vec![0], + false, + ) + .result + .unwrap() + .account_id; + + let addr_callee = Contracts::bare_instantiate( + ALICE, + min_balance * 100, + GAS_LIMIT, + None, + Code::Upload(callee_code), + vec![], + vec![1], + false, + ) + .result + .unwrap() + .account_id; + + // Call pallet_contracts call() dispatchable + let call = RuntimeCall::Contracts(crate::Call::call { + dest: addr_callee, + value: 0, + gas_limit: GAS_LIMIT / 3, + storage_deposit_limit: None, + data: vec![], + }); + + // Call runtime to re-enter back to contracts engine by + // calling dummy contract + let result = Contracts::bare_call( + ALICE, + addr_caller.clone(), + 0, + GAS_LIMIT, + None, + call.encode(), + false, + Determinism::Deterministic, + ) + .result + .unwrap(); + // Call to runtime should fail because of the re-entrancy guard + assert_return_code!(result, RuntimeReturnCode::CallRuntimeFailed); + }); +} + #[test] fn ecdsa_recover() { let (wasm, _code_hash) = compile_module::("ecdsa_recover").unwrap(); From 435446fe0ba4063aa9cedbef8cfb10ef0724a9ce Mon Sep 17 00:00:00 2001 From: yjh Date: Mon, 6 Mar 2023 22:09:07 +0800 Subject: [PATCH 197/558] chore: reduce copy times for bytes in core-hashing (#13519) * chore: reduce copy bytes for core-hashing * improve by suggestions and remove unused `xx_into` * chore: replace sha2 crate by `sp_core::hashing` for pallet-alliance * fix features * use sp-core-hashing directly * add to dev-dep --- Cargo.lock | 2 +- frame/alliance/Cargo.toml | 7 +++-- frame/alliance/src/benchmarking.rs | 7 ++--- frame/alliance/src/mock.rs | 7 ++--- primitives/core/hashing/src/lib.rs | 46 +++++------------------------- 5 files changed, 16 insertions(+), 53 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 5a215553d..e9e33471a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -5285,8 +5285,8 @@ dependencies = [ "pallet-identity", "parity-scale-codec", "scale-info", - "sha2 0.10.6", "sp-core", + "sp-core-hashing", "sp-io", "sp-runtime", "sp-std", diff --git a/frame/alliance/Cargo.toml b/frame/alliance/Cargo.toml index d6a16e2c3..d70dfd6d7 100644 --- a/frame/alliance/Cargo.toml +++ b/frame/alliance/Cargo.toml @@ -14,7 +14,6 @@ targets = ["x86_64-unknown-linux-gnu"] [dependencies] array-bytes = { version = "4.1", optional = true } -sha2 = { version = "0.10.1", default-features = false, optional = true } log = { version = "0.4.14", default-features = false } codec = { package = "parity-scale-codec", version = "3.2.2", default-features = false, features = ["derive"] } @@ -22,6 +21,7 @@ scale-info = { version = "2.0.1", default-features = false, features = ["derive" sp-std = { version = "5.0.0", default-features = false, path = "../../primitives/std" } sp-core = { version = "7.0.0", default-features = false, path = "../../primitives/core" } +sp-core-hashing = { version = "5.0.0", default-features = false, path = "../../primitives/core/hashing", optional = true } sp-io = { version = "7.0.0", default-features = false, path = "../../primitives/io" } sp-runtime = { version = "7.0.0", default-features = false, path = "../../primitives/runtime" } @@ -34,13 +34,14 @@ pallet-collective = { version = "4.0.0-dev", path = "../collective", default-fea [dev-dependencies] array-bytes = "4.1" -sha2 = "0.10.1" +sp-core-hashing = { version = "5.0.0", default-features = false, path = "../../primitives/core/hashing" } pallet-balances = { version = "4.0.0-dev", path = "../balances" } pallet-collective = { version = "4.0.0-dev", path = "../collective" } [features] default = ["std"] std = [ + "sp-core-hashing?/std", "pallet-collective?/std", "frame-benchmarking?/std", "log/std", @@ -56,7 +57,7 @@ std = [ ] runtime-benchmarks = [ "array-bytes", - "sha2", + "sp-core-hashing", "frame-benchmarking/runtime-benchmarks", "sp-runtime/runtime-benchmarks", "frame-support/runtime-benchmarks", diff --git a/frame/alliance/src/benchmarking.rs b/frame/alliance/src/benchmarking.rs index a30b5adce..92bf1ae44 100644 --- a/frame/alliance/src/benchmarking.rs +++ b/frame/alliance/src/benchmarking.rs @@ -40,11 +40,8 @@ fn assert_last_event, I: 'static>(generic_event: >:: } fn cid(input: impl AsRef<[u8]>) -> Cid { - use sha2::{Digest, Sha256}; - let mut hasher = Sha256::new(); - hasher.update(input); - let result = hasher.finalize(); - Cid::new_v0(&*result) + let result = sp_core_hashing::sha2_256(input.as_ref()); + Cid::new_v0(result) } fn rule(input: impl AsRef<[u8]>) -> Cid { diff --git a/frame/alliance/src/mock.rs b/frame/alliance/src/mock.rs index 0925a82fd..b8fd998eb 100644 --- a/frame/alliance/src/mock.rs +++ b/frame/alliance/src/mock.rs @@ -368,11 +368,8 @@ pub fn new_bench_ext() -> sp_io::TestExternalities { } pub fn test_cid() -> Cid { - use sha2::{Digest, Sha256}; - let mut hasher = Sha256::new(); - hasher.update(b"hello world"); - let result = hasher.finalize(); - Cid::new_v0(&*result) + let result = sp_core_hashing::sha2_256(b"hello world"); + Cid::new_v0(result) } pub fn make_remark_proposal(value: u64) -> (RuntimeCall, u32, H256) { diff --git a/primitives/core/hashing/src/lib.rs b/primitives/core/hashing/src/lib.rs index 5bb68586a..26941bb60 100644 --- a/primitives/core/hashing/src/lib.rs +++ b/primitives/core/hashing/src/lib.rs @@ -35,48 +35,22 @@ pub fn blake2_512_into(data: &[u8], dest: &mut [u8; 64]) { /// Do a Blake2 512-bit hash and return result. pub fn blake2_512(data: &[u8]) -> [u8; 64] { - let mut r = [0; 64]; - blake2_512_into(data, &mut r); - r -} - -/// Do a Blake2 256-bit hash and place result in `dest`. -pub fn blake2_256_into(data: &[u8], dest: &mut [u8; 32]) { - type Blake2b256 = blake2::Blake2b; - dest.copy_from_slice(Blake2b256::digest(data).as_slice()); + blake2::Blake2b512::digest(data).into() } /// Do a Blake2 256-bit hash and return result. pub fn blake2_256(data: &[u8]) -> [u8; 32] { - let mut r = [0; 32]; - blake2_256_into(data, &mut r); - r -} - -/// Do a Blake2 128-bit hash and place result in `dest`. -pub fn blake2_128_into(data: &[u8], dest: &mut [u8; 16]) { - type Blake2b128 = blake2::Blake2b; - dest.copy_from_slice(Blake2b128::digest(data).as_slice()); + blake2::Blake2b::::digest(data).into() } /// Do a Blake2 128-bit hash and return result. pub fn blake2_128(data: &[u8]) -> [u8; 16] { - let mut r = [0; 16]; - blake2_128_into(data, &mut r); - r -} - -/// Do a Blake2 64-bit hash and place result in `dest`. -pub fn blake2_64_into(data: &[u8], dest: &mut [u8; 8]) { - type Blake2b64 = blake2::Blake2b; - dest.copy_from_slice(Blake2b64::digest(data).as_slice()); + blake2::Blake2b::::digest(data).into() } /// Do a Blake2 64-bit hash and return result. pub fn blake2_64(data: &[u8]) -> [u8; 8] { - let mut r = [0; 8]; - blake2_64_into(data, &mut r); - r + blake2::Blake2b::::digest(data).into() } /// Do a XX 64-bit hash and place result in `dest`. @@ -128,21 +102,15 @@ pub fn twox_256(data: &[u8]) -> [u8; 32] { /// Do a keccak 256-bit hash and return result. pub fn keccak_256(data: &[u8]) -> [u8; 32] { - let mut output = [0u8; 32]; - output.copy_from_slice(sha3::Keccak256::digest(data).as_slice()); - output + sha3::Keccak256::digest(data).into() } /// Do a keccak 512-bit hash and return result. pub fn keccak_512(data: &[u8]) -> [u8; 64] { - let mut output = [0u8; 64]; - output.copy_from_slice(sha3::Keccak512::digest(data).as_slice()); - output + sha3::Keccak512::digest(data).into() } /// Do a sha2 256-bit hash and return result. pub fn sha2_256(data: &[u8]) -> [u8; 32] { - let mut output = [0u8; 32]; - output.copy_from_slice(sha2::Sha256::digest(data).as_slice()); - output + sha2::Sha256::digest(data).into() } From c4081230565220dff6a7f3a383f81c8eaa27da1b Mon Sep 17 00:00:00 2001 From: Martin Pugh Date: Mon, 6 Mar 2023 15:07:59 +0000 Subject: [PATCH 198/558] [CI] Remove redundant trigger-review-pipeline job (#13547) This job is now redundant since pipelines are run for each commit, so it can be safely removed. Cheers --- .github/workflows/trigger-review-pipeline.yml | 20 ------------------- 1 file changed, 20 deletions(-) delete mode 100644 .github/workflows/trigger-review-pipeline.yml diff --git a/.github/workflows/trigger-review-pipeline.yml b/.github/workflows/trigger-review-pipeline.yml deleted file mode 100644 index af54ec435..000000000 --- a/.github/workflows/trigger-review-pipeline.yml +++ /dev/null @@ -1,20 +0,0 @@ -name: Trigger pipeline for review - -on: - pull_request: - types: [ready_for_review] - -jobs: - trigger: - runs-on: ubuntu-latest - - steps: - - name: Trigger pipeline - run: | - curl -X POST \ - -F token="$TOKEN" \ - -F ref="$REF" \ - https://gitlab.parity.io/api/v4/projects/145/trigger/pipeline - env: - REF: ${{ github.event.number }} - TOKEN: ${{ secrets.GITLAB_TRIGGER_TOKEN }} From e7aa37ec9601fac11f48d227fc06da84b0a5c521 Mon Sep 17 00:00:00 2001 From: Aaro Altonen <48052676+altonen@users.noreply.github.com> Date: Mon, 6 Mar 2023 18:33:38 +0200 Subject: [PATCH 199/558] Extract syncing protocol from `sc-network` (#12828) * Move import queue out of `sc-network` Add supplementary asynchronous API for the import queue which means it can be run as an independent task and communicated with through the `ImportQueueService`. This commit removes removes block and justification imports from `sc-network` and provides `ChainSync` with a handle to import queue so it can import blocks and justifications. Polling of the import queue is moved complete out of `sc-network` and `sc_consensus::Link` is implemented for `ChainSyncInterfaceHandled` so the import queue can still influence the syncing process. * Move stuff to SyncingEngine * Move `ChainSync` instanation to `SyncingEngine` Some of the tests have to be rewritten * Move peer hashmap to `SyncingEngine` * Let `SyncingEngine` to implement `ChainSyncInterface` * Introduce `SyncStatusProvider` * Move `sync_peer_(connected|disconnected)` to `SyncingEngine` * Implement `SyncEventStream` Remove `SyncConnected`/`SyncDisconnected` events from `NetworkEvenStream` and provide those events through `ChainSyncInterface` instead. Modify BEEFY/GRANDPA/transactions protocol and `NetworkGossip` to take `SyncEventStream` object which they listen to for incoming sync peer events. * Introduce `ChainSyncInterface` This interface provides a set of miscellaneous functions that other subsystems can use to query, for example, the syncing status. * Move event stream polling to `SyncingEngine` Subscribe to `NetworkStreamEvent` and poll the incoming notifications and substream events from `SyncingEngine`. The code needs refactoring. * Make `SyncingEngine` into an asynchronous runner This commits removes the last hard dependency of syncing from `sc-network` meaning the protocol now lives completely outside of `sc-network`, ignoring the hardcoded peerset entry which will be addressed in the future. Code needs a lot of refactoring. * Fix warnings * Code refactoring * Use `SyncingService` for BEEFY * Use `SyncingService` for GRANDPA * Remove call delegation from `NetworkService` * Remove `ChainSyncService` * Remove `ChainSync` service tests They were written for the sole purpose of verifying that `NetworWorker` continues to function while the calls are being dispatched to `ChainSync`. * Refactor code * Refactor code * Update client/finality-grandpa/src/communication/tests.rs Co-authored-by: Anton * Fix warnings * Apply review comments * Fix docs * Fix test * cargo-fmt * Update client/network/sync/src/engine.rs Co-authored-by: Anton * Update client/network/sync/src/engine.rs Co-authored-by: Anton * Add missing docs * Refactor code --------- Co-authored-by: Anton --- Cargo.lock | 666 ++++++++----- bin/node-template/node/src/service.rs | 8 +- bin/node/cli/Cargo.toml | 1 + bin/node/cli/src/chain_spec.rs | 3 +- bin/node/cli/src/service.rs | 26 +- client/cli/src/arg_enums.rs | 20 +- client/cli/src/params/network_params.rs | 7 +- client/cli/src/params/node_key_params.rs | 14 +- client/consensus/aura/src/lib.rs | 5 + client/consensus/babe/src/tests.rs | 5 + client/consensus/beefy/Cargo.toml | 1 + client/consensus/beefy/src/lib.rs | 30 +- client/consensus/beefy/src/tests.rs | 8 +- client/consensus/beefy/src/worker.rs | 39 +- .../grandpa/src/communication/mod.rs | 45 +- .../grandpa/src/communication/tests.rs | 40 +- client/consensus/grandpa/src/environment.rs | 31 +- client/consensus/grandpa/src/lib.rs | 35 +- client/consensus/grandpa/src/observer.rs | 19 +- client/consensus/grandpa/src/tests.rs | 71 +- client/informant/src/display.rs | 11 +- client/informant/src/lib.rs | 20 +- client/network-gossip/src/bridge.rs | 123 ++- client/network-gossip/src/lib.rs | 18 +- client/network-gossip/src/state_machine.rs | 4 + client/network/common/Cargo.toml | 6 + client/network/common/src/config.rs | 375 ++++++- client/network/common/src/protocol/event.rs | 14 +- client/network/common/src/service.rs | 35 +- client/network/common/src/sync.rs | 61 +- client/network/src/behaviour.rs | 39 +- client/network/src/config.rs | 384 +------- client/network/src/lib.rs | 20 +- client/network/src/protocol.rs | 766 ++------------- .../src/protocol/notifications/behaviour.rs | 50 +- client/network/src/service.rs | 266 +---- client/network/src/service/metrics.rs | 35 +- client/network/src/service/out_events.rs | 14 - .../network/src/service/tests/chain_sync.rs | 420 -------- client/network/src/service/tests/mod.rs | 138 +-- client/network/src/service/tests/service.rs | 68 +- client/network/sync/Cargo.toml | 1 + client/network/sync/src/engine.rs | 924 ++++++++++++++++++ client/network/sync/src/lib.rs | 71 +- client/network/sync/src/service/chain_sync.rs | 169 +++- client/network/sync/src/service/mock.rs | 15 +- client/network/sync/src/service/network.rs | 30 +- client/network/sync/src/tests.rs | 78 -- client/network/test/src/lib.rs | 215 ++-- client/network/test/src/sync.rs | 27 +- client/network/transactions/src/lib.rs | 76 +- client/service/src/builder.rs | 78 +- client/service/src/config.rs | 9 +- client/service/src/lib.rs | 69 +- client/service/src/metrics.rs | 23 +- client/service/test/Cargo.toml | 1 + client/service/test/src/lib.rs | 18 +- 57 files changed, 2886 insertions(+), 2859 deletions(-) delete mode 100644 client/network/src/service/tests/chain_sync.rs create mode 100644 client/network/sync/src/engine.rs delete mode 100644 client/network/sync/src/tests.rs diff --git a/Cargo.lock b/Cargo.lock index e9e33471a..72b5943d4 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -27,7 +27,7 @@ version = "0.19.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a76fd60b23679b7d19bd066031410fb7e458ccc5e958eb5c325888ce4baedc97" dependencies = [ - "gimli 0.27.1", + "gimli 0.27.2", ] [[package]] @@ -55,6 +55,16 @@ dependencies = [ "rand_core 0.6.4", ] +[[package]] +name = "aead" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c192eb8f11fc081b0fe4259ba5af04217d4e0faddd02417310a927911abd7c8" +dependencies = [ + "crypto-common", + "generic-array 0.14.6", +] + [[package]] name = "aes" version = "0.6.0" @@ -79,17 +89,14 @@ dependencies = [ ] [[package]] -name = "aes-gcm" -version = "0.8.0" +name = "aes" +version = "0.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5278b5fabbb9bd46e24aa69b2fdea62c99088e0a950a9be40e3e0101298f88da" +checksum = "433cfd6710c9986c576a25ca913c39d66a6474107b406f34f91d4a8923395241" dependencies = [ - "aead 0.3.2", - "aes 0.6.0", - "cipher 0.2.5", - "ctr 0.6.0", - "ghash 0.3.1", - "subtle", + "cfg-if", + "cipher 0.4.3", + "cpufeatures", ] [[package]] @@ -106,6 +113,20 @@ dependencies = [ "subtle", ] +[[package]] +name = "aes-gcm" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "82e1366e0c69c9f927b1fa5ce2c7bf9eafc8f9268c0b9800729e8b267612447c" +dependencies = [ + "aead 0.5.1", + "aes 0.8.2", + "cipher 0.4.3", + "ctr 0.9.2", + "ghash 0.5.0", + "subtle", +] + [[package]] name = "aes-soft" version = "0.6.4" @@ -246,7 +267,7 @@ dependencies = [ "num-traits", "rusticata-macros", "thiserror", - "time 0.3.17", + "time 0.3.20", ] [[package]] @@ -262,7 +283,7 @@ dependencies = [ "num-traits", "rusticata-macros", "thiserror", - "time 0.3.17", + "time 0.3.20", ] [[package]] @@ -369,19 +390,20 @@ dependencies = [ [[package]] name = "async-stream" -version = "0.3.3" +version = "0.3.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dad5c83079eae9969be7fadefe640a1c566901f05ff91ab221de4b6f68d9507e" +checksum = "ad445822218ce64be7a341abfb0b1ea43b5c23aa83902542a4542e78309d8e5e" dependencies = [ "async-stream-impl", "futures-core", + "pin-project-lite 0.2.9", ] [[package]] name = "async-stream-impl" -version = "0.3.3" +version = "0.3.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "10f203db73a71dfa2fb6dd22763990fa26f3d2625a6da2da900d23b87d26be27" +checksum = "e4655ae1a7b0cdf149156f780c5bf3f1352bc53cbd9e0a361a7ef7b22947e965" dependencies = [ "proc-macro2", "quote", @@ -482,9 +504,9 @@ checksum = "a4a4ddaa51a5bc52a6948f74c06d20aaaddb71924eab79b8c97a8c556e942d6a" [[package]] name = "base64ct" -version = "1.5.3" +version = "1.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b645a089122eccb6111b4f81cbc1a49f5900ac4666bb93ac027feaecf15607bf" +checksum = "8c3c1a368f70d6cf7302d78f8f7093da241fb8e8807c05cc9e51a125895a6d5b" [[package]] name = "basic-toml" @@ -527,9 +549,9 @@ dependencies = [ [[package]] name = "bindgen" -version = "0.60.1" +version = "0.64.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "062dddbc1ba4aca46de6338e2bf87771414c335f7b2f2036e8f3e9befebf88e6" +checksum = "c4243e6031260db77ede97ad86c27e501d646a27ab57b59a574f725d98ab1fb4" dependencies = [ "bitflags", "cexpr", @@ -542,6 +564,7 @@ dependencies = [ "regex", "rustc-hash", "shlex", + "syn", ] [[package]] @@ -573,24 +596,24 @@ dependencies = [ [[package]] name = "blake2b_simd" -version = "1.0.0" +version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "72936ee4afc7f8f736d1c38383b56480b5497b4617b4a77bdbf1d2ababc76127" +checksum = "3c2f0dc9a68c6317d884f97cc36cf5a3d20ba14ce404227df55e1af708ab04bc" dependencies = [ "arrayref", "arrayvec 0.7.2", - "constant_time_eq 0.1.5", + "constant_time_eq", ] [[package]] name = "blake2s_simd" -version = "1.0.0" +version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "db539cc2b5f6003621f1cd9ef92d7ded8ea5232c7de0f9faa2de251cd98730d4" +checksum = "6637f448b9e61dfadbdcbae9a885fadee1f3eaffb1f8d3c1965d3ade8bdfd44f" dependencies = [ "arrayref", "arrayvec 0.7.2", - "constant_time_eq 0.1.5", + "constant_time_eq", ] [[package]] @@ -603,7 +626,7 @@ dependencies = [ "arrayvec 0.7.2", "cc", "cfg-if", - "constant_time_eq 0.2.4", + "constant_time_eq", ] [[package]] @@ -663,9 +686,9 @@ checksum = "8d696c370c750c948ada61c69a0ee2cbbb9c50b1019ddb86d9317157a99c2cae" [[package]] name = "bounded-collections" -version = "0.1.4" +version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "de2aff4807e40f478132150d80b031f2461d88f061851afcab537d7600c24120" +checksum = "a071c348a5ef6da1d3a87166b408170b46002382b1dda83992b5c2208cefb370" dependencies = [ "log", "parity-scale-codec", @@ -681,9 +704,9 @@ checksum = "771fe0050b883fcc3ea2359b1a96bcfbc090b7116eae7c3c512c7a083fdf23d3" [[package]] name = "bstr" -version = "1.2.0" +version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b7f0778972c64420fdedc63f09919c8a88bda7b25135357fd25a5d9f3257e832" +checksum = "5ffdb39cb703212f3c11973452c2861b972f757b021158f3516ba10f2fa8b2c1" dependencies = [ "memchr", "once_cell", @@ -749,9 +772,9 @@ dependencies = [ [[package]] name = "camino" -version = "1.1.2" +version = "1.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c77df041dc383319cc661b428b6961a005db4d6808d5e12536931b1ca9556055" +checksum = "6031a462f977dd38968b6f23378356512feeace69cef817e1a4475108093cec3" dependencies = [ "serde", ] @@ -865,7 +888,7 @@ name = "chain-spec-builder" version = "2.0.0" dependencies = [ "ansi_term", - "clap 4.1.4", + "clap 4.1.8", "node-cli", "rand 0.8.5", "sc-chain-spec", @@ -924,7 +947,7 @@ checksum = "f6ed9c8b2d17acb8110c46f1da5bf4a696d745e1474a16db0cd2b49cd0249bf2" dependencies = [ "core2", "multibase", - "multihash", + "multihash 0.16.3", "serde", "unsigned-varint", ] @@ -947,6 +970,16 @@ dependencies = [ "generic-array 0.14.6", ] +[[package]] +name = "cipher" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d1873270f8f7942c191139cb8a40fd228da6c3fd2fc376d7e92d47aa14aeb59e" +dependencies = [ + "crypto-common", + "inout", +] + [[package]] name = "ckb-merkle-mountain-range" version = "0.5.2" @@ -958,9 +991,9 @@ dependencies = [ [[package]] name = "clang-sys" -version = "1.4.0" +version = "1.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fa2e27ae6ab525c3d369ded447057bca5438d86dc3a68f6faafb8269ba82ebf3" +checksum = "77ed9a53e5d4d9c573ae844bfac6872b159cb1d1585a83b29e7a64b7eef7332a" dependencies = [ "glob", "libc", @@ -981,13 +1014,13 @@ dependencies = [ [[package]] name = "clap" -version = "4.1.4" +version = "4.1.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f13b9c79b5d1dd500d20ef541215a6423c75829ef43117e1b4d17fd8af0b5d76" +checksum = "c3d7ae14b20b94cb02149ed21a86c423859cbe18dc7ed69845cace50e52b40a5" dependencies = [ "bitflags", "clap_derive", - "clap_lex 0.3.1", + "clap_lex 0.3.2", "is-terminal", "once_cell", "strsim", @@ -996,18 +1029,18 @@ dependencies = [ [[package]] name = "clap_complete" -version = "4.1.1" +version = "4.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3d6540eedc41f8a5a76cf3d8d458057dcdf817be4158a55b5f861f7a5483de75" +checksum = "501ff0a401473ea1d4c3b125ff95506b62c5bc5768d818634195fbb7c4ad5ff4" dependencies = [ - "clap 4.1.4", + "clap 4.1.8", ] [[package]] name = "clap_derive" -version = "4.1.0" +version = "4.1.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "684a277d672e91966334af371f1a7b5833f9aa00b07c84e92fbce95e00208ce8" +checksum = "44bec8e5c9d09e439c4335b1af0abaab56dcf3b94999a936e1bb47b9134288f0" dependencies = [ "heck", "proc-macro-error", @@ -1027,9 +1060,9 @@ dependencies = [ [[package]] name = "clap_lex" -version = "0.3.1" +version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "783fe232adfca04f90f56201b26d79682d4cd2625e0bc7290b95123afe558ade" +checksum = "350b9cf31731f9957399229e9b2adc51eeabdfbe9d71d9a0552275fd12710d09" dependencies = [ "os_str_bytes", ] @@ -1066,15 +1099,9 @@ dependencies = [ [[package]] name = "const-oid" -version = "0.9.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cec318a675afcb6a1ea1d4340e2d377e56e47c266f28043ceccbf4412ddfdd3b" - -[[package]] -name = "constant_time_eq" -version = "0.1.5" +version = "0.9.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "245097e9a4535ee1e3e3931fcfcd55a796a44c643e8596ff6566d68f09b87bbc" +checksum = "520fbf3c07483f94e3e3ca9d0cfd913d7718ef2483d2cfd91c0d9e91474ab913" [[package]] name = "constant_time_eq" @@ -1125,12 +1152,6 @@ dependencies = [ "libc", ] -[[package]] -name = "cpuid-bool" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dcb25d077389e53838a8158c8e99174c5a9d902dee4904320db714f3c653ffba" - [[package]] name = "cranelift-bforest" version = "0.93.0" @@ -1294,9 +1315,9 @@ dependencies = [ [[package]] name = "crossbeam-channel" -version = "0.5.6" +version = "0.5.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c2dd04ddaf88237dc3b8d8f9a3c1004b506b54b3313403944054d23c0870c521" +checksum = "cf2b3e8478797446514c91ef04bafcb59faba183e621ad488df88983cc14128c" dependencies = [ "cfg-if", "crossbeam-utils", @@ -1304,9 +1325,9 @@ dependencies = [ [[package]] name = "crossbeam-deque" -version = "0.8.2" +version = "0.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "715e8152b692bba2d374b53d4875445368fdf21a94751410af607a5ac677d1fc" +checksum = "ce6fd6f855243022dcecf8702fef0c297d4338e226845fe067f6341ad9fa0cef" dependencies = [ "cfg-if", "crossbeam-epoch", @@ -1315,22 +1336,22 @@ dependencies = [ [[package]] name = "crossbeam-epoch" -version = "0.9.13" +version = "0.9.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "01a9af1f4c2ef74bb8aa1f7e19706bc72d03598c8a570bb5de72243c7a9d9d5a" +checksum = "46bd5f3f85273295a9d14aedfb86f6aadbff6d8f5295c4a9edb08e819dcf5695" dependencies = [ "autocfg", "cfg-if", "crossbeam-utils", - "memoffset 0.7.1", + "memoffset 0.8.0", "scopeguard", ] [[package]] name = "crossbeam-utils" -version = "0.8.14" +version = "0.8.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4fb766fa798726286dbbb842f174001dab8abc7b627a1dd86e0b7222a95d929f" +checksum = "3c063cd8cc95f5c377ed0d4b49a4b21f632396ff690e8470c29b3359b346984b" dependencies = [ "cfg-if", ] @@ -1360,6 +1381,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3" dependencies = [ "generic-array 0.14.6", + "rand_core 0.6.4", "typenum", ] @@ -1373,16 +1395,6 @@ dependencies = [ "subtle", ] -[[package]] -name = "crypto-mac" -version = "0.10.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bff07008ec701e8028e2ceb8f83f0e4274ee62bd2dbdc4fefff2e9a91824081a" -dependencies = [ - "generic-array 0.14.6", - "subtle", -] - [[package]] name = "crypto-mac" version = "0.11.1" @@ -1405,20 +1417,20 @@ dependencies = [ [[package]] name = "ctr" -version = "0.6.0" +version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fb4a30d54f7443bf3d6191dcd486aca19e67cb3c49fa7a06a319966346707e7f" +checksum = "049bb91fb4aaf0e3c7efa6cd5ef877dbbbd15b39dad06d9948de4ec8a75761ea" dependencies = [ - "cipher 0.2.5", + "cipher 0.3.0", ] [[package]] name = "ctr" -version = "0.8.0" +version = "0.9.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "049bb91fb4aaf0e3c7efa6cd5ef877dbbbd15b39dad06d9948de4ec8a75761ea" +checksum = "0369ee1ad671834580515889b80f2ea915f23b8be8d0daa4bbaf2ac5c7590835" dependencies = [ - "cipher 0.3.0", + "cipher 0.4.3", ] [[package]] @@ -1463,9 +1475,9 @@ dependencies = [ [[package]] name = "cxx" -version = "1.0.89" +version = "1.0.91" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bc831ee6a32dd495436e317595e639a587aa9907bef96fe6e6abc290ab6204e9" +checksum = "86d3488e7665a7a483b57e25bdd90d0aeb2bc7608c8d0346acf2ad3f1caf1d62" dependencies = [ "cc", "cxxbridge-flags", @@ -1475,9 +1487,9 @@ dependencies = [ [[package]] name = "cxx-build" -version = "1.0.89" +version = "1.0.91" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "94331d54f1b1a8895cd81049f7eaaaef9d05a7dcb4d1fd08bf3ff0806246789d" +checksum = "48fcaf066a053a41a81dfb14d57d99738b767febb8b735c3016e469fac5da690" dependencies = [ "cc", "codespan-reporting", @@ -1490,15 +1502,15 @@ dependencies = [ [[package]] name = "cxxbridge-flags" -version = "1.0.89" +version = "1.0.91" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "48dcd35ba14ca9b40d6e4b4b39961f23d835dbb8eed74565ded361d93e1feb8a" +checksum = "a2ef98b8b717a829ca5603af80e1f9e2e48013ab227b68ef37872ef84ee479bf" [[package]] name = "cxxbridge-macro" -version = "1.0.89" +version = "1.0.91" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "81bbeb29798b407ccd82a3324ade1a7286e0d29851475990b612670f6f5124d2" +checksum = "086c685979a698443656e5cf7856c95c642295a38599f12fb1ff76fb28d19892" dependencies = [ "proc-macro2", "quote", @@ -1804,9 +1816,9 @@ dependencies = [ [[package]] name = "dyn-clone" -version = "1.0.10" +version = "1.0.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c9b0705efd4599c15a38151f4721f7bc388306f61084d3bfd50bd07fbca5cb60" +checksum = "68b0cf012f1230e43cd00ebb729c6bb58707ecfa8ad08b52ef3a4ccd2697fc30" [[package]] name = "ecdsa" @@ -1999,9 +2011,9 @@ checksum = "4443176a9f2c162692bd3d352d745ef9413eec5782a80d8fd6f8a1ac692a07f7" [[package]] name = "fastrand" -version = "1.8.0" +version = "1.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a7a407cfaa3385c4ae6b23e84623d48c2798d06e3e6a1878f7f59f17b3f86499" +checksum = "e51093e27b0797c359783294ca4f0a911c270184cb10f85783b118614a1501be" dependencies = [ "instant", ] @@ -2043,14 +2055,14 @@ dependencies = [ [[package]] name = "filetime" -version = "0.2.19" +version = "0.2.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4e884668cd0c7480504233e951174ddc3b382f7c2666e3b7310b5c4e7b0c37f9" +checksum = "8a3de6e8d11b22ff9edc6d916f890800597d60f8b2da1caf2955c274638d6412" dependencies = [ "cfg-if", "libc", "redox_syscall", - "windows-sys 0.42.0", + "windows-sys 0.45.0", ] [[package]] @@ -2170,7 +2182,7 @@ dependencies = [ "Inflector", "array-bytes", "chrono", - "clap 4.1.4", + "clap 4.1.8", "comfy-table", "frame-benchmarking", "frame-support", @@ -2261,7 +2273,7 @@ dependencies = [ name = "frame-election-solution-type-fuzzer" version = "2.0.0-alpha.5" dependencies = [ - "clap 4.1.4", + "clap 4.1.8", "frame-election-provider-solution-type", "frame-election-provider-support", "frame-support", @@ -2725,22 +2737,22 @@ dependencies = [ [[package]] name = "ghash" -version = "0.3.1" +version = "0.4.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "97304e4cd182c3846f7575ced3890c53012ce534ad9114046b0a9e00bb30a375" +checksum = "1583cc1656d7839fd3732b80cf4f38850336cdb9b8ded1cd399ca62958de3c99" dependencies = [ "opaque-debug 0.3.0", - "polyval 0.4.5", + "polyval 0.5.3", ] [[package]] name = "ghash" -version = "0.4.4" +version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1583cc1656d7839fd3732b80cf4f38850336cdb9b8ded1cd399ca62958de3c99" +checksum = "d930750de5717d2dd0b8c0d42c076c0e884c81a73e6cab859bbd2339c71e3e40" dependencies = [ "opaque-debug 0.3.0", - "polyval 0.5.3", + "polyval 0.6.0", ] [[package]] @@ -2756,9 +2768,9 @@ dependencies = [ [[package]] name = "gimli" -version = "0.27.1" +version = "0.27.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "221996f774192f0f718773def8201c4ae31f02616a54ccfc2d358bb0e5cefdec" +checksum = "ad0a93d233ebf96623465aad4046a8d3aa4da22d4f4beba5388838c8a434bbb4" [[package]] name = "git2" @@ -2805,9 +2817,9 @@ dependencies = [ [[package]] name = "h2" -version = "0.3.15" +version = "0.3.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5f9f29bc9dda355256b2916cf526ab02ce0aeaaaf2bad60d65ef3f12f11dd0f4" +checksum = "5be7b54589b581f624f566bf5d8eb2bab1db736c51528720b6bd36b96b55924d" dependencies = [ "bytes", "fnv", @@ -2901,9 +2913,9 @@ dependencies = [ [[package]] name = "hermit-abi" -version = "0.3.0" +version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "856b5cb0902c2b6d65d5fd97dfa30f9b70c7538e770b98eab5ed52d8db923e01" +checksum = "fed44880c466736ef9a5c5b5facefb5ed0785676d0c02d612db14e54f0d84286" [[package]] name = "hex" @@ -2930,16 +2942,6 @@ dependencies = [ "digest 0.9.0", ] -[[package]] -name = "hmac" -version = "0.10.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c1441c6b1e930e2817404b5046f1f989899143a12bf92de603b69f4e0aee1e15" -dependencies = [ - "crypto-mac 0.10.1", - "digest 0.9.0", -] - [[package]] name = "hmac" version = "0.11.0" @@ -2995,9 +2997,9 @@ dependencies = [ [[package]] name = "http" -version = "0.2.8" +version = "0.2.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "75f43d41e26995c17e71ee126451dd3941010b0514a81a9d11f3b341debc2399" +checksum = "bd6effc99afb63425aff9b05836f029929e345a6148a14b7ecd5ab67af944482" dependencies = [ "bytes", "fnv", @@ -3204,6 +3206,15 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8e04e2fd2b8188ea827b32ef11de88377086d690286ab35747ef7f9bf3ccb590" +[[package]] +name = "inout" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a0c10553d664a4d0bcff9f4215d0aac67a639cc68ef660840afe309b807bc9f5" +dependencies = [ + "generic-array 0.14.6", +] + [[package]] name = "instant" version = "0.1.12" @@ -3283,11 +3294,11 @@ checksum = "30e22bd8629359895450b59ea7a776c850561b96a3b1d31321c1949d9e6c9146" [[package]] name = "is-terminal" -version = "0.4.3" +version = "0.4.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "22e18b0a45d56fe973d6db23972bf5bc46f988a4a2385deac9cc29572f09daef" +checksum = "21b6b32576413a8e69b90e952e4a026476040d81017b80445deda5f2d3921857" dependencies = [ - "hermit-abi 0.3.0", + "hermit-abi 0.3.1", "io-lifetimes 1.0.5", "rustix 0.36.8", "windows-sys 0.45.0", @@ -3685,14 +3696,14 @@ dependencies = [ "futures-timer", "getrandom 0.2.8", "instant", - "libp2p-core", + "libp2p-core 0.38.0", "libp2p-dns", "libp2p-identify", "libp2p-kad", "libp2p-mdns", "libp2p-metrics", "libp2p-mplex", - "libp2p-noise", + "libp2p-noise 0.41.0", "libp2p-ping", "libp2p-quic", "libp2p-request-response", @@ -3702,7 +3713,7 @@ dependencies = [ "libp2p-webrtc", "libp2p-websocket", "libp2p-yamux", - "multiaddr", + "multiaddr 0.16.0", "parking_lot 0.12.1", "pin-project", "smallvec", @@ -3723,8 +3734,42 @@ dependencies = [ "futures-timer", "instant", "log", - "multiaddr", - "multihash", + "multiaddr 0.16.0", + "multihash 0.16.3", + "multistream-select", + "once_cell", + "parking_lot 0.12.1", + "pin-project", + "prost", + "prost-build", + "rand 0.8.5", + "rw-stream-sink", + "sec1", + "sha2 0.10.6", + "smallvec", + "thiserror", + "unsigned-varint", + "void", + "zeroize", +] + +[[package]] +name = "libp2p-core" +version = "0.39.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "881d9a54e97d97cdaa4125d48269d97ca8c40e5fefec6b85b30440dc60cc551f" +dependencies = [ + "asn1_der", + "bs58", + "ed25519-dalek", + "either", + "fnv", + "futures", + "futures-timer", + "instant", + "log", + "multiaddr 0.17.0", + "multihash 0.17.0", "multistream-select", "once_cell", "parking_lot 0.12.1", @@ -3749,7 +3794,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8e42a271c1b49f789b92f7fc87749fa79ce5c7bdc88cbdfacb818a4bca47fec5" dependencies = [ "futures", - "libp2p-core", + "libp2p-core 0.38.0", "log", "parking_lot 0.12.1", "smallvec", @@ -3765,7 +3810,7 @@ dependencies = [ "asynchronous-codec", "futures", "futures-timer", - "libp2p-core", + "libp2p-core 0.38.0", "libp2p-swarm", "log", "lru", @@ -3791,7 +3836,7 @@ dependencies = [ "futures", "futures-timer", "instant", - "libp2p-core", + "libp2p-core 0.38.0", "libp2p-swarm", "log", "prost", @@ -3814,7 +3859,7 @@ dependencies = [ "data-encoding", "futures", "if-watch", - "libp2p-core", + "libp2p-core 0.38.0", "libp2p-swarm", "log", "rand 0.8.5", @@ -3831,7 +3876,7 @@ version = "0.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5ad8a64f29da86005c86a4d2728b8a0719e9b192f4092b609fd8790acb9dec55" dependencies = [ - "libp2p-core", + "libp2p-core 0.38.0", "libp2p-identify", "libp2p-kad", "libp2p-ping", @@ -3848,7 +3893,7 @@ dependencies = [ "asynchronous-codec", "bytes", "futures", - "libp2p-core", + "libp2p-core 0.38.0", "log", "nohash-hasher", "parking_lot 0.12.1", @@ -3866,7 +3911,30 @@ dependencies = [ "bytes", "curve25519-dalek 3.2.0", "futures", - "libp2p-core", + "libp2p-core 0.38.0", + "log", + "once_cell", + "prost", + "prost-build", + "rand 0.8.5", + "sha2 0.10.6", + "snow", + "static_assertions", + "thiserror", + "x25519-dalek 1.1.1", + "zeroize", +] + +[[package]] +name = "libp2p-noise" +version = "0.42.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1216f9ec823ac7a2289b954674c54cbce81c9e45920b4fcf173018ede4295246" +dependencies = [ + "bytes", + "curve25519-dalek 3.2.0", + "futures", + "libp2p-core 0.39.0", "log", "once_cell", "prost", @@ -3889,7 +3957,7 @@ dependencies = [ "futures", "futures-timer", "instant", - "libp2p-core", + "libp2p-core 0.38.0", "libp2p-swarm", "log", "rand 0.8.5", @@ -3898,15 +3966,15 @@ dependencies = [ [[package]] name = "libp2p-quic" -version = "0.7.0-alpha" +version = "0.7.0-alpha.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "01e7c867e95c8130667b24409d236d37598270e6da69b3baf54213ba31ffca59" +checksum = "5971f629ff7519f4d4889a7c981f0dc09c6ad493423cd8a13ee442de241bc8c8" dependencies = [ "bytes", "futures", "futures-timer", "if-watch", - "libp2p-core", + "libp2p-core 0.39.0", "libp2p-tls", "log", "parking_lot 0.12.1", @@ -3927,7 +3995,7 @@ dependencies = [ "bytes", "futures", "instant", - "libp2p-core", + "libp2p-core 0.38.0", "libp2p-swarm", "log", "rand 0.8.5", @@ -3946,7 +4014,7 @@ dependencies = [ "futures", "futures-timer", "instant", - "libp2p-core", + "libp2p-core 0.38.0", "libp2p-swarm-derive", "log", "pin-project", @@ -3978,7 +4046,7 @@ dependencies = [ "futures-timer", "if-watch", "libc", - "libp2p-core", + "libp2p-core 0.38.0", "log", "socket2", "tokio", @@ -3986,13 +4054,13 @@ dependencies = [ [[package]] name = "libp2p-tls" -version = "0.1.0-alpha" +version = "0.1.0-alpha.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f7905ce0d040576634e8a3229a7587cc8beab83f79db6023800f1792895defa8" +checksum = "e9baf6f6292149e124ee737d9a79dbee783f29473fc368c7faad9d157841078a" dependencies = [ "futures", "futures-rustls", - "libp2p-core", + "libp2p-core 0.39.0", "rcgen 0.10.0", "ring", "rustls 0.20.8", @@ -4010,7 +4078,7 @@ checksum = "1bb1a35299860e0d4b3c02a3e74e3b293ad35ae0cee8a056363b0c862d082069" dependencies = [ "futures", "js-sys", - "libp2p-core", + "libp2p-core 0.38.0", "parity-send-wrapper", "wasm-bindgen", "wasm-bindgen-futures", @@ -4018,9 +4086,9 @@ dependencies = [ [[package]] name = "libp2p-webrtc" -version = "0.4.0-alpha" +version = "0.4.0-alpha.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cdb6cd86dd68cba72308ea05de1cebf3ba0ae6e187c40548167955d4e3970f6a" +checksum = "db4401ec550d36f413310ba5d4bf564bb21f89fb1601cadb32b2300f8bc1eb5b" dependencies = [ "async-trait", "asynchronous-codec", @@ -4029,10 +4097,10 @@ dependencies = [ "futures-timer", "hex", "if-watch", - "libp2p-core", - "libp2p-noise", + "libp2p-core 0.39.0", + "libp2p-noise 0.42.0", "log", - "multihash", + "multihash 0.17.0", "prost", "prost-build", "prost-codec", @@ -4056,7 +4124,7 @@ dependencies = [ "either", "futures", "futures-rustls", - "libp2p-core", + "libp2p-core 0.38.0", "log", "parking_lot 0.12.1", "quicksink", @@ -4073,7 +4141,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4f63594a0aa818642d9d4915c791945053877253f08a3626f13416b5cd928a29" dependencies = [ "futures", - "libp2p-core", + "libp2p-core 0.38.0", "log", "parking_lot 0.12.1", "thiserror", @@ -4082,9 +4150,9 @@ dependencies = [ [[package]] name = "librocksdb-sys" -version = "0.8.0+7.4.4" +version = "0.8.3+7.4.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "611804e4666a25136fcc5f8cf425ab4d26c7f74ea245ffe92ea23b85b6420b5d" +checksum = "557b255ff04123fcc176162f56ed0c9cd42d8f357cf55b3fabeb60f7413741b3" dependencies = [ "bindgen", "bzip2-sys", @@ -4374,6 +4442,15 @@ dependencies = [ "autocfg", ] +[[package]] +name = "memoffset" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d61c719bcfbcf5d62b3a09efa6088de8c54bc0bfcd3ea7ae39fcc186108b8de1" +dependencies = [ + "autocfg", +] + [[package]] name = "memory-db" version = "0.31.0" @@ -4419,14 +4496,14 @@ dependencies = [ [[package]] name = "mio" -version = "0.8.5" +version = "0.8.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e5d732bc30207a6423068df043e3d02e0735b155ad7ce1a6f76fe2baa5b158de" +checksum = "5b9d9a46eff5b4ff64b45a9e316a6d1e0bc719ef429cbec4dc630684212bfdf9" dependencies = [ "libc", "log", "wasi 0.11.0+wasi-snapshot-preview1", - "windows-sys 0.42.0", + "windows-sys 0.45.0", ] [[package]] @@ -4505,7 +4582,25 @@ dependencies = [ "byteorder", "data-encoding", "multibase", - "multihash", + "multihash 0.16.3", + "percent-encoding", + "serde", + "static_assertions", + "unsigned-varint", + "url", +] + +[[package]] +name = "multiaddr" +version = "0.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3b53e0cc5907a5c216ba6584bf74be8ab47d6d6289f72793b2dddbf15dc3bf8c" +dependencies = [ + "arrayref", + "byteorder", + "data-encoding", + "multibase", + "multihash 0.17.0", "percent-encoding", "serde", "static_assertions", @@ -4541,6 +4636,19 @@ dependencies = [ "unsigned-varint", ] +[[package]] +name = "multihash" +version = "0.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "835d6ff01d610179fbce3de1694d007e500bf33a7f29689838941d6bf783ae40" +dependencies = [ + "core2", + "digest 0.10.6", + "multihash-derive", + "sha2 0.10.6", + "unsigned-varint", +] + [[package]] name = "multihash-derive" version = "0.8.1" @@ -4708,7 +4816,7 @@ name = "node-bench" version = "0.9.0-dev" dependencies = [ "array-bytes", - "clap 4.1.4", + "clap 4.1.8", "derive_more", "fs_extra", "futures", @@ -4745,7 +4853,7 @@ version = "3.0.0-dev" dependencies = [ "array-bytes", "assert_cmd", - "clap 4.1.4", + "clap 4.1.8", "clap_complete", "criterion", "frame-benchmarking-cli", @@ -4786,6 +4894,7 @@ dependencies = [ "sc-keystore", "sc-network", "sc-network-common", + "sc-network-sync", "sc-rpc", "sc-service", "sc-service-test", @@ -4865,7 +4974,7 @@ dependencies = [ name = "node-inspect" version = "0.9.0-dev" dependencies = [ - "clap 4.1.4", + "clap 4.1.8", "parity-scale-codec", "sc-cli", "sc-client-api", @@ -4924,7 +5033,7 @@ dependencies = [ name = "node-runtime-generate-bags" version = "3.0.0" dependencies = [ - "clap 4.1.4", + "clap 4.1.8", "generate-bags", "kitchensink-runtime", ] @@ -4933,7 +5042,7 @@ dependencies = [ name = "node-template" version = "4.0.0-dev" dependencies = [ - "clap 4.1.4", + "clap 4.1.8", "frame-benchmarking", "frame-benchmarking-cli", "frame-system", @@ -5190,9 +5299,9 @@ dependencies = [ [[package]] name = "once_cell" -version = "1.17.0" +version = "1.17.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6f61fba1741ea2b3d6a1e3178721804bb716a68a6aeba1149b5d52e3d464ea66" +checksum = "b7e5500299e16ebb147ae15a00a942af264cf3688f47923b8fc2cd5858f23ad3" [[package]] name = "oorandom" @@ -6799,9 +6908,9 @@ dependencies = [ [[package]] name = "parity-scale-codec" -version = "3.3.0" +version = "3.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c3840933452adf7b3b9145e27086a5a3376c619dca1a21b1e5a5af0d54979bed" +checksum = "637935964ff85a605d114591d4d2c13c5d1ba2806dae97cea6bf180238a749ac" dependencies = [ "arrayvec 0.7.2", "bitvec", @@ -6946,9 +7055,9 @@ checksum = "478c572c3d73181ff3c2539045f6eb99e5491218eae919370993b890cdbdd98e" [[package]] name = "pest" -version = "2.5.4" +version = "2.5.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4ab62d2fa33726dbe6321cc97ef96d8cde531e3eeaf858a058de53a8a6d40d8f" +checksum = "028accff104c4e513bad663bbcd2ad7cfd5304144404c31ed0a77ac103d00660" dependencies = [ "thiserror", "ucd-trie", @@ -6956,9 +7065,9 @@ dependencies = [ [[package]] name = "pest_derive" -version = "2.5.4" +version = "2.5.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8bf026e2d0581559db66d837fe5242320f525d85c76283c61f4d51a1238d65ea" +checksum = "2ac3922aac69a40733080f53c1ce7f91dcf57e1a5f6c52f421fadec7fbdc4b69" dependencies = [ "pest", "pest_generator", @@ -6966,9 +7075,9 @@ dependencies = [ [[package]] name = "pest_generator" -version = "2.5.4" +version = "2.5.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2b27bd18aa01d91c8ed2b61ea23406a676b42d82609c6e2581fba42f0c15f17f" +checksum = "d06646e185566b5961b4058dd107e0a7f56e77c3f484549fb119867773c0f202" dependencies = [ "pest", "pest_meta", @@ -6979,9 +7088,9 @@ dependencies = [ [[package]] name = "pest_meta" -version = "2.5.4" +version = "2.5.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9f02b677c1859756359fc9983c2e56a0237f18624a3789528804406b7e915e5d" +checksum = "e6f60b2ba541577e2a0c307c8f39d1439108120eb7903adeb6497fa880c59616" dependencies = [ "once_cell", "pest", @@ -7114,30 +7223,31 @@ checksum = "048aeb476be11a4b6ca432ca569e375810de9294ae78f4774e78ea98a9246ede" dependencies = [ "cpufeatures", "opaque-debug 0.3.0", - "universal-hash", + "universal-hash 0.4.1", ] [[package]] name = "polyval" -version = "0.4.5" +version = "0.5.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eebcc4aa140b9abd2bc40d9c3f7ccec842679cd79045ac3a7ac698c1a064b7cd" +checksum = "8419d2b623c7c0896ff2d5d96e2cb4ede590fed28fcc34934f4c33c036e620a1" dependencies = [ - "cpuid-bool", + "cfg-if", + "cpufeatures", "opaque-debug 0.3.0", - "universal-hash", + "universal-hash 0.4.1", ] [[package]] name = "polyval" -version = "0.5.3" +version = "0.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8419d2b623c7c0896ff2d5d96e2cb4ede590fed28fcc34934f4c33c036e620a1" +checksum = "7ef234e08c11dfcb2e56f79fd70f6f2eb7f025c0ce2333e82f4f0518ecad30c6" dependencies = [ "cfg-if", "cpufeatures", "opaque-debug 0.3.0", - "universal-hash", + "universal-hash 0.5.0", ] [[package]] @@ -7293,9 +7403,9 @@ dependencies = [ [[package]] name = "prost" -version = "0.11.6" +version = "0.11.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "21dc42e00223fc37204bd4aa177e69420c604ca4a183209a8f9de30c6d934698" +checksum = "e48e50df39172a3e7eb17e14642445da64996989bc212b583015435d39a58537" dependencies = [ "bytes", "prost-derive", @@ -7303,9 +7413,9 @@ dependencies = [ [[package]] name = "prost-build" -version = "0.11.6" +version = "0.11.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a3f8ad728fb08fe212df3c05169e940fbb6d9d16a877ddde14644a983ba2012e" +checksum = "2c828f93f5ca4826f97fedcbd3f9a536c16b12cff3dbbb4a007f932bbad95b12" dependencies = [ "bytes", "heck", @@ -7338,9 +7448,9 @@ dependencies = [ [[package]] name = "prost-derive" -version = "0.11.6" +version = "0.11.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8bda8c0881ea9f722eb9629376db3d0b903b462477c1aafcb0566610ac28ac5d" +checksum = "4ea9b0f8cbe5e15a8a042d030bd96668db28ecb567ec37d691971ff5731d2b1b" dependencies = [ "anyhow", "itertools", @@ -7351,11 +7461,10 @@ dependencies = [ [[package]] name = "prost-types" -version = "0.11.6" +version = "0.11.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a5e0526209433e96d83d750dd81a99118edbc55739e7e61a46764fd2ad537788" +checksum = "379119666929a1afd7a043aa6cf96fa67a6dce9af60c88095a4686dbce4c9c88" dependencies = [ - "bytes", "prost", ] @@ -7553,7 +7662,7 @@ checksum = "6413f3de1edee53342e6138e75b56d32e7bc6e332b3bd62d497b1929d4cfbcdd" dependencies = [ "pem", "ring", - "time 0.3.17", + "time 0.3.20", "x509-parser 0.13.2", "yasna", ] @@ -7566,7 +7675,7 @@ checksum = "ffbe84efe2f38dea12e9bfc1f65377fdf03e53a18cb3b995faedf7934c7e785b" dependencies = [ "pem", "ring", - "time 0.3.17", + "time 0.3.20", "yasna", ] @@ -7660,15 +7769,6 @@ dependencies = [ "winapi", ] -[[package]] -name = "remove_dir_all" -version = "0.5.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3acd125665422973a33ac9d3dd2df85edad0f4ae9b00dafb1a05e43a9f5ef8e7" -dependencies = [ - "winapi", -] - [[package]] name = "resolv-conf" version = "0.7.0" @@ -8070,7 +8170,7 @@ version = "0.10.0-dev" dependencies = [ "array-bytes", "chrono", - "clap 4.1.4", + "clap 4.1.8", "fdlimit", "futures", "futures-timer", @@ -8323,6 +8423,7 @@ dependencies = [ "sc-network", "sc-network-common", "sc-network-gossip", + "sc-network-sync", "sc-network-test", "sc-utils", "serde", @@ -8734,6 +8835,7 @@ dependencies = [ name = "sc-network-common" version = "0.10.0-dev" dependencies = [ + "array-bytes", "async-trait", "bitflags", "bytes", @@ -8745,6 +8847,7 @@ dependencies = [ "prost-build", "sc-consensus", "sc-peerset", + "sc-utils", "serde", "smallvec", "sp-blockchain", @@ -8752,7 +8855,9 @@ dependencies = [ "sp-consensus-grandpa", "sp-runtime", "substrate-prometheus-endpoint", + "tempfile", "thiserror", + "zeroize", ] [[package]] @@ -8803,6 +8908,7 @@ dependencies = [ "async-trait", "fork-tree", "futures", + "futures-timer", "libp2p", "log", "lru", @@ -9137,6 +9243,7 @@ dependencies = [ "sc-executor", "sc-network", "sc-network-common", + "sc-network-sync", "sc-service", "sc-transaction-pool-api", "sp-api", @@ -9169,7 +9276,7 @@ dependencies = [ name = "sc-storage-monitor" version = "0.1.0" dependencies = [ - "clap 4.1.4", + "clap 4.1.8", "fs4", "futures", "log", @@ -9565,9 +9672,9 @@ dependencies = [ [[package]] name = "serde_json" -version = "1.0.92" +version = "1.0.93" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7434af0dc1cbd59268aa98b4c22c131c0584d2232f6fb166efb993e2832e896a" +checksum = "cad406b69c91885b5107daf2c29572f6c8cdb3c66826821e286c533490c0bc76" dependencies = [ "itoa", "ryu", @@ -9587,6 +9694,17 @@ dependencies = [ "opaque-debug 0.3.0", ] +[[package]] +name = "sha1" +version = "0.10.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f04293dc80c3993519f2d7f6f511707ee7094fe0c6d3406feb330cdb3540eba3" +dependencies = [ + "cfg-if", + "cpufeatures", + "digest 0.10.6", +] + [[package]] name = "sha2" version = "0.8.2" @@ -9650,9 +9768,9 @@ checksum = "43b2853a4d09f215c24cc5489c992ce46052d359b5109343cbafbf26bc62f8a3" [[package]] name = "signal-hook-registry" -version = "1.4.0" +version = "1.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e51e73328dc4ac0c7ccbda3a494dfa03df1de2f46018127f60c693f2648455b0" +checksum = "d8229b473baa5980ac72ef434c4415e70c4b5e71b423043adb4ba059f89c99a1" dependencies = [ "libc", ] @@ -9688,9 +9806,9 @@ checksum = "7bd3e3206899af3f8b12af284fafc038cc1dc2b41d1b89dd17297221c5d225de" [[package]] name = "slab" -version = "0.4.7" +version = "0.4.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4614a76b2a8be0058caa9dbbaf66d988527d86d003c11a94fbd335d7661edcef" +checksum = "6528351c9bc8ab22353f9d776db39a20288e8d6c37ef8cfe3317cf875eecfc2d" dependencies = [ "autocfg", ] @@ -10231,7 +10349,7 @@ dependencies = [ name = "sp-npos-elections-fuzzer" version = "2.0.0-alpha.5" dependencies = [ - "clap 4.1.4", + "clap 4.1.8", "honggfuzz", "parity-scale-codec", "rand 0.8.5", @@ -10588,9 +10706,9 @@ checksum = "6e63cff320ae2c57904679ba7cb63280a3dc4613885beafb148ee7bf9aa9042d" [[package]] name = "spin" -version = "0.9.4" +version = "0.9.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7f6002a767bff9e83f8eeecf883ecb8011875a21ae8da43bffb817a57e78cc09" +checksum = "7dccf47db1b41fa1573ed27ccf5e08e3ca771cb994f776668c5ebda893b248fc" [[package]] name = "spki" @@ -10604,9 +10722,9 @@ dependencies = [ [[package]] name = "ss58-registry" -version = "1.38.0" +version = "1.39.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e40c020d72bc0a9c5660bb71e4a6fdef081493583062c474740a7d59f55f0e7b" +checksum = "ecf0bd63593ef78eca595a7fc25e9a443ca46fe69fd472f8f09f5245cdcd769d" dependencies = [ "Inflector", "num-format", @@ -10708,7 +10826,7 @@ dependencies = [ name = "subkey" version = "3.0.0" dependencies = [ - "clap 4.1.4", + "clap 4.1.8", "sc-cli", ] @@ -10736,7 +10854,7 @@ dependencies = [ name = "substrate-frame-cli" version = "4.0.0-dev" dependencies = [ - "clap 4.1.4", + "clap 4.1.8", "frame-support", "frame-system", "sc-cli", @@ -10997,9 +11115,9 @@ checksum = "6bdef32e8150c2a081110b42772ffe7d7c9032b606bc226c8260fd97e0976601" [[package]] name = "syn" -version = "1.0.107" +version = "1.0.109" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1f4064b5b16e03ae50984a5a8ed5d4f8803e6bc1fd170a3cda91a1be4b18e3f5" +checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" dependencies = [ "proc-macro2", "quote", @@ -11047,22 +11165,21 @@ checksum = "55937e1799185b12863d447f42597ed69d9928686b8d88a1df17376a097d8369" [[package]] name = "target-lexicon" -version = "0.12.5" +version = "0.12.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9410d0f6853b1d94f0e519fb95df60f29d2c1eff2d921ffdf01a4c8a3b54f12d" +checksum = "8ae9980cab1db3fceee2f6c6f643d5d8de2997c58ee8d25fb0cc8a9e9e7348e5" [[package]] name = "tempfile" -version = "3.3.0" +version = "3.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5cdb1ef4eaeeaddc8fbd371e5017057064af0911902ef36b39801f67cc6d79e4" +checksum = "af18f7ae1acd354b992402e9ec5864359d693cd8a79dcbef59f76891701c1e95" dependencies = [ "cfg-if", "fastrand", - "libc", "redox_syscall", - "remove_dir_all", - "winapi", + "rustix 0.36.8", + "windows-sys 0.42.0", ] [[package]] @@ -11114,10 +11231,11 @@ checksum = "3bf63baf9f5039dadc247375c29eb13706706cfde997d0330d05aa63a77d8820" [[package]] name = "thread_local" -version = "1.1.4" +version = "1.1.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5516c27b78311c50bf42c071425c560ac799b11c30b31f87e3081965fe5e0180" +checksum = "3fdd6f064ccff2d6567adcb3873ca630700f00b5ad3f060c25b5dcfd9a4ce152" dependencies = [ + "cfg-if", "once_cell", ] @@ -11153,9 +11271,9 @@ dependencies = [ [[package]] name = "time" -version = "0.3.17" +version = "0.3.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a561bf4617eebd33bca6434b988f39ed798e527f51a1e797d0ee4f61c0a38376" +checksum = "cd0cbfecb4d19b5ea75bb31ad904eb5b9fa13f21079c3b92017ebdf4999a5890" dependencies = [ "itoa", "serde", @@ -11171,9 +11289,9 @@ checksum = "2e153e1f1acaef8acc537e68b44906d2db6436e2b35ac2c6b42640fff91f00fd" [[package]] name = "time-macros" -version = "0.2.6" +version = "0.2.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d967f99f534ca7e495c575c62638eebc2898a8c84c119b89e250477bc4ba16b2" +checksum = "fd80a657e71da814b8e5d60d3374fc6d35045062245d80224748ae522dd76f36" dependencies = [ "time-core", ] @@ -11275,9 +11393,9 @@ dependencies = [ [[package]] name = "tokio-stream" -version = "0.1.11" +version = "0.1.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d660770404473ccd7bc9f8b28494a811bc18542b915c0855c51e8f419d5223ce" +checksum = "8fb52b74f05dbf495a8fba459fdc331812b96aa086d9eb78101fa0d4569c3313" dependencies = [ "futures-core", "pin-project-lite 0.2.9", @@ -11300,9 +11418,9 @@ dependencies = [ [[package]] name = "tokio-util" -version = "0.7.4" +version = "0.7.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0bb2e075f03b3d66d8d8785356224ba688d2906a371015e225beeb65ca92c740" +checksum = "5427d89453009325de0d8f342c9490009f76e999cb7672d77e46267448f7e6b2" dependencies = [ "bytes", "futures-core", @@ -11574,7 +11692,7 @@ name = "try-runtime-cli" version = "0.10.0-dev" dependencies = [ "async-trait", - "clap 4.1.4", + "clap 4.1.8", "frame-remote-externalities", "frame-try-runtime", "hex", @@ -11726,6 +11844,16 @@ dependencies = [ "subtle", ] +[[package]] +name = "universal-hash" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7d3160b73c9a19f7e2939a2fdad446c57c1bbbbf4d919d3213ff1267a580d8b5" +dependencies = [ + "crypto-common", + "subtle", +] + [[package]] name = "unsigned-varint" version = "0.7.1" @@ -11919,9 +12047,9 @@ checksum = "0046fef7e28c3804e5e38bfa31ea2a0f73905319b677e57ebe37e49358989b5d" [[package]] name = "wasm-encoder" -version = "0.22.1" +version = "0.24.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9a584273ccc2d9311f1dd19dc3fb26054661fa3e373d53ede5d1144ba07a9acd" +checksum = "68f7d56227d910901ce12dfd19acc40c12687994dfb3f57c90690f80be946ec5" dependencies = [ "leb128", ] @@ -12017,7 +12145,7 @@ version = "0.20.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "01bf50edb2ea9d922aa75a7bf3c15e26a6c9e2d18c56e862b49737a582901729" dependencies = [ - "spin 0.9.4", + "spin 0.9.5", "wasmi_arena", "wasmi_core 0.5.0", "wasmparser-nostd", @@ -12263,9 +12391,9 @@ dependencies = [ [[package]] name = "wast" -version = "52.0.3" +version = "54.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "15942180f265280eede7bc38b239e9770031d1821c02d905284216c645316430" +checksum = "3d48d9d731d835f4f8dacbb8de7d47be068812cb9877f5c60d408858778d8d2a" dependencies = [ "leb128", "memchr", @@ -12275,9 +12403,9 @@ dependencies = [ [[package]] name = "wat" -version = "1.0.57" +version = "1.0.60" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "37212100d4cbe6f0f6ff6e707f1e5a5b5b675f0451231ed9e4235e234e127ed3" +checksum = "d1db2e3ed05ea31243761439194bec3af6efbbaf87c4c8667fb879e4f23791a0" dependencies = [ "wast", ] @@ -12347,7 +12475,7 @@ dependencies = [ "sha2 0.10.6", "stun", "thiserror", - "time 0.3.17", + "time 0.3.20", "tokio", "turn", "url", @@ -12379,12 +12507,12 @@ dependencies = [ [[package]] name = "webrtc-dtls" -version = "0.7.0" +version = "0.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7021987ae0a2ed6c8cd33f68e98e49bb6e74ffe9543310267b48a1bbe3900e5f" +checksum = "942be5bd85f072c3128396f6e5a9bfb93ca8c1939ded735d177b7bcba9a13d05" dependencies = [ "aes 0.6.0", - "aes-gcm 0.8.0", + "aes-gcm 0.10.1", "async-trait", "bincode", "block-modes", @@ -12394,7 +12522,7 @@ dependencies = [ "der-parser 8.1.0", "elliptic-curve", "hkdf", - "hmac 0.10.1", + "hmac 0.12.1", "log", "oid-registry 0.6.1", "p256", @@ -12406,8 +12534,8 @@ dependencies = [ "rustls 0.19.1", "sec1", "serde", - "sha-1", - "sha2 0.9.9", + "sha1", + "sha2 0.10.6", "signature", "subtle", "thiserror", @@ -12420,9 +12548,9 @@ dependencies = [ [[package]] name = "webrtc-ice" -version = "0.9.0" +version = "0.9.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "494483fbb2f5492620871fdc78b084aed8807377f6e3fe88b2e49f0a9c9c41d7" +checksum = "465a03cc11e9a7d7b4f9f99870558fe37a102b65b93f8045392fef7c67b39e80" dependencies = [ "arc-swap", "async-trait", @@ -12555,9 +12683,9 @@ dependencies = [ [[package]] name = "wide" -version = "0.7.6" +version = "0.7.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "feff0a412894d67223777b6cc8d68c0dab06d52d95e9890d5f2d47f10dd9366c" +checksum = "b689b6c49d6549434bf944e6b0f39238cf63693cb7a147e9d887507fffa3b223" dependencies = [ "bytemuck", "safe_arch", @@ -12780,7 +12908,7 @@ dependencies = [ "ring", "rusticata-macros", "thiserror", - "time 0.3.17", + "time 0.3.20", ] [[package]] @@ -12798,7 +12926,7 @@ dependencies = [ "oid-registry 0.6.1", "rusticata-macros", "thiserror", - "time 0.3.17", + "time 0.3.20", ] [[package]] @@ -12827,7 +12955,7 @@ version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "aed2e7a52e3744ab4d0c05c20aa065258e84c49fd4226f5191b2ed29712710b4" dependencies = [ - "time 0.3.17", + "time 0.3.20", ] [[package]] @@ -12872,9 +13000,9 @@ dependencies = [ [[package]] name = "zstd-sys" -version = "2.0.6+zstd.1.5.2" +version = "2.0.7+zstd.1.5.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "68a3f9792c0c3dc6c165840a75f47ae1f4da402c2d006881129579f6597e801b" +checksum = "94509c3ba2fe55294d752b79842c530ccfab760192521df74a081a78d2b3c7f5" dependencies = [ "cc", "libc", diff --git a/bin/node-template/node/src/service.rs b/bin/node-template/node/src/service.rs index 98485a7ad..34e4e566d 100644 --- a/bin/node-template/node/src/service.rs +++ b/bin/node-template/node/src/service.rs @@ -192,7 +192,7 @@ pub fn new_full(mut config: Configuration) -> Result Vec::default(), )); - let (network, system_rpc_tx, tx_handler_controller, network_starter) = + let (network, system_rpc_tx, tx_handler_controller, network_starter, sync_service) = sc_service::build_network(sc_service::BuildNetworkParams { config: &config, client: client.clone(), @@ -240,6 +240,7 @@ pub fn new_full(mut config: Configuration) -> Result backend, system_rpc_tx, tx_handler_controller, + sync_service: sync_service.clone(), config, telemetry: telemetry.as_mut(), })?; @@ -276,8 +277,8 @@ pub fn new_full(mut config: Configuration) -> Result force_authoring, backoff_authoring_blocks, keystore: keystore_container.sync_keystore(), - sync_oracle: network.clone(), - justification_sync_link: network.clone(), + sync_oracle: sync_service.clone(), + justification_sync_link: sync_service.clone(), block_proposal_slot_portion: SlotProportion::new(2f32 / 3f32), max_block_proposal_slot_portion: None, telemetry: telemetry.as_ref().map(|x| x.handle()), @@ -320,6 +321,7 @@ pub fn new_full(mut config: Configuration) -> Result config: grandpa_config, link: grandpa_link, network, + sync: Arc::new(sync_service), voting_rule: sc_consensus_grandpa::VotingRulesBuilder::default().build(), prometheus_registry, shared_voter_state: SharedVoterState::empty(), diff --git a/bin/node/cli/Cargo.toml b/bin/node/cli/Cargo.toml index b93019b7c..4451935c3 100644 --- a/bin/node/cli/Cargo.toml +++ b/bin/node/cli/Cargo.toml @@ -68,6 +68,7 @@ sc-transaction-pool = { version = "4.0.0-dev", path = "../../../client/transacti sc-transaction-pool-api = { version = "4.0.0-dev", path = "../../../client/transaction-pool/api" } sc-network = { version = "0.10.0-dev", path = "../../../client/network" } sc-network-common = { version = "0.10.0-dev", path = "../../../client/network/common" } +sc-network-sync = { version = "0.10.0-dev", path = "../../../client/network/sync" } sc-consensus-slots = { version = "0.10.0-dev", path = "../../../client/consensus/slots" } sc-consensus-babe = { version = "0.10.0-dev", path = "../../../client/consensus/babe" } grandpa = { version = "0.10.0-dev", package = "sc-consensus-grandpa", path = "../../../client/consensus/grandpa" } diff --git a/bin/node/cli/src/chain_spec.rs b/bin/node/cli/src/chain_spec.rs index 178b97823..4732e12f9 100644 --- a/bin/node/cli/src/chain_spec.rs +++ b/bin/node/cli/src/chain_spec.rs @@ -479,12 +479,13 @@ pub(crate) mod tests { sp_tracing::try_init_simple(); sc_service_test::connectivity(integration_test_config_with_two_authorities(), |config| { - let NewFullBase { task_manager, client, network, transaction_pool, .. } = + let NewFullBase { task_manager, client, network, sync, transaction_pool, .. } = new_full_base(config, false, |_, _| ())?; Ok(sc_service_test::TestNetComponents::new( task_manager, client, network, + sync, transaction_pool, )) }); diff --git a/bin/node/cli/src/service.rs b/bin/node/cli/src/service.rs index 268a170a8..6e000a475 100644 --- a/bin/node/cli/src/service.rs +++ b/bin/node/cli/src/service.rs @@ -35,6 +35,7 @@ use sc_network::NetworkService; use sc_network_common::{ protocol::event::Event, service::NetworkEventStream, sync::warp::WarpSyncParams, }; +use sc_network_sync::SyncingService; use sc_service::{config::Configuration, error::Error as ServiceError, RpcHandlers, TaskManager}; use sc_telemetry::{Telemetry, TelemetryWorker}; use sp_api::ProvideRuntimeApi; @@ -303,6 +304,8 @@ pub struct NewFullBase { pub client: Arc, /// The networking service of the node. pub network: Arc::Hash>>, + /// The syncing service of the node. + pub sync: Arc>, /// The transaction pool of the node. pub transaction_pool: Arc, /// The rpc handlers of the node. @@ -353,7 +356,7 @@ pub fn new_full_base( Vec::default(), )); - let (network, system_rpc_tx, tx_handler_controller, network_starter) = + let (network, system_rpc_tx, tx_handler_controller, network_starter, sync_service) = sc_service::build_network(sc_service::BuildNetworkParams { config: &config, client: client.clone(), @@ -392,6 +395,7 @@ pub fn new_full_base( task_manager: &mut task_manager, system_rpc_tx, tx_handler_controller, + sync_service: sync_service.clone(), telemetry: telemetry.as_mut(), })?; @@ -434,8 +438,8 @@ pub fn new_full_base( select_chain, env: proposer, block_import, - sync_oracle: network.clone(), - justification_sync_link: network.clone(), + sync_oracle: sync_service.clone(), + justification_sync_link: sync_service.clone(), create_inherent_data_providers: move |parent, ()| { let client_clone = client_clone.clone(); async move { @@ -531,6 +535,7 @@ pub fn new_full_base( config, link: grandpa_link, network: network.clone(), + sync: Arc::new(sync_service.clone()), telemetry: telemetry.as_ref().map(|x| x.handle()), voting_rule: grandpa::VotingRulesBuilder::default().build(), prometheus_registry, @@ -547,7 +552,14 @@ pub fn new_full_base( } network_starter.start_network(); - Ok(NewFullBase { task_manager, client, network, transaction_pool, rpc_handlers }) + Ok(NewFullBase { + task_manager, + client, + network, + sync: sync_service, + transaction_pool, + rpc_handlers, + }) } /// Builds a new service for a full client. @@ -627,7 +639,7 @@ mod tests { chain_spec, |config| { let mut setup_handles = None; - let NewFullBase { task_manager, client, network, transaction_pool, .. } = + let NewFullBase { task_manager, client, network, sync, transaction_pool, .. } = new_full_base( config, false, @@ -641,6 +653,7 @@ mod tests { task_manager, client, network, + sync, transaction_pool, ); Ok((node, setup_handles.unwrap())) @@ -807,12 +820,13 @@ mod tests { sc_service_test::consensus( crate::chain_spec::tests::integration_test_config_with_two_authorities(), |config| { - let NewFullBase { task_manager, client, network, transaction_pool, .. } = + let NewFullBase { task_manager, client, network, sync, transaction_pool, .. } = new_full_base(config, false, |_, _| ())?; Ok(sc_service_test::TestNetComponents::new( task_manager, client, network, + sync, transaction_pool, )) }, diff --git a/client/cli/src/arg_enums.rs b/client/cli/src/arg_enums.rs index c3399a896..472c1722f 100644 --- a/client/cli/src/arg_enums.rs +++ b/client/cli/src/arg_enums.rs @@ -251,15 +251,19 @@ pub enum SyncMode { Warp, } -impl Into for SyncMode { - fn into(self) -> sc_network::config::SyncMode { +impl Into for SyncMode { + fn into(self) -> sc_network_common::config::SyncMode { match self { - SyncMode::Full => sc_network::config::SyncMode::Full, - SyncMode::Fast => - sc_network::config::SyncMode::Fast { skip_proofs: false, storage_chain_mode: false }, - SyncMode::FastUnsafe => - sc_network::config::SyncMode::Fast { skip_proofs: true, storage_chain_mode: false }, - SyncMode::Warp => sc_network::config::SyncMode::Warp, + SyncMode::Full => sc_network_common::config::SyncMode::Full, + SyncMode::Fast => sc_network_common::config::SyncMode::Fast { + skip_proofs: false, + storage_chain_mode: false, + }, + SyncMode::FastUnsafe => sc_network_common::config::SyncMode::Fast { + skip_proofs: true, + storage_chain_mode: false, + }, + SyncMode::Warp => sc_network_common::config::SyncMode::Warp, } } } diff --git a/client/cli/src/params/network_params.rs b/client/cli/src/params/network_params.rs index 3aebee778..31b761938 100644 --- a/client/cli/src/params/network_params.rs +++ b/client/cli/src/params/network_params.rs @@ -18,11 +18,8 @@ use crate::{arg_enums::SyncMode, params::node_key_params::NodeKeyParams}; use clap::Args; -use sc_network::{ - config::{NetworkConfiguration, NodeKeyConfig}, - multiaddr::Protocol, -}; -use sc_network_common::config::{NonReservedPeerMode, SetConfig, TransportConfig}; +use sc_network::{config::NetworkConfiguration, multiaddr::Protocol}; +use sc_network_common::config::{NodeKeyConfig, NonReservedPeerMode, SetConfig, TransportConfig}; use sc_service::{ config::{Multiaddr, MultiaddrWithPeerId}, ChainSpec, ChainType, diff --git a/client/cli/src/params/node_key_params.rs b/client/cli/src/params/node_key_params.rs index 074b95bea..d470ef1fa 100644 --- a/client/cli/src/params/node_key_params.rs +++ b/client/cli/src/params/node_key_params.rs @@ -17,7 +17,7 @@ // along with this program. If not, see . use clap::Args; -use sc_network::config::{identity::ed25519, NodeKeyConfig}; +use sc_network_common::config::{identity::ed25519, NodeKeyConfig}; use sp_core::H256; use std::{path::PathBuf, str::FromStr}; @@ -92,7 +92,7 @@ impl NodeKeyParams { let secret = if let Some(node_key) = self.node_key.as_ref() { parse_ed25519_secret(node_key)? } else { - sc_network::config::Secret::File( + sc_network_common::config::Secret::File( self.node_key_file .clone() .unwrap_or_else(|| net_config_dir.join(NODE_KEY_ED25519_FILE)), @@ -111,10 +111,10 @@ fn invalid_node_key(e: impl std::fmt::Display) -> error::Error { } /// Parse a Ed25519 secret key from a hex string into a `sc_network::Secret`. -fn parse_ed25519_secret(hex: &str) -> error::Result { +fn parse_ed25519_secret(hex: &str) -> error::Result { H256::from_str(hex).map_err(invalid_node_key).and_then(|bytes| { ed25519::SecretKey::from_bytes(bytes) - .map(sc_network::config::Secret::Input) + .map(sc_network_common::config::Secret::Input) .map_err(invalid_node_key) }) } @@ -123,7 +123,7 @@ fn parse_ed25519_secret(hex: &str) -> error::Result Ok(()), _ => Err(error::Error::Input("Unexpected node key config".into())), @@ -200,7 +200,7 @@ mod tests { let dir = PathBuf::from(net_config_dir.clone()); let typ = params.node_key_type; params.node_key(net_config_dir).and_then(move |c| match c { - NodeKeyConfig::Ed25519(sc_network::config::Secret::File(ref f)) + NodeKeyConfig::Ed25519(sc_network_common::config::Secret::File(ref f)) if typ == NodeKeyType::Ed25519 && f == &dir.join(NODE_KEY_ED25519_FILE) => Ok(()), _ => Err(error::Error::Input("Unexpected node key config".into())), diff --git a/client/consensus/aura/src/lib.rs b/client/consensus/aura/src/lib.rs index f5f70857c..e90494ddf 100644 --- a/client/consensus/aura/src/lib.rs +++ b/client/consensus/aura/src/lib.rs @@ -766,6 +766,11 @@ mod tests { fn peers(&self) -> &Vec { &self.peers } + + fn peers_mut(&mut self) -> &mut Vec { + &mut self.peers + } + fn mut_peers)>(&mut self, closure: F) { closure(&mut self.peers); } diff --git a/client/consensus/babe/src/tests.rs b/client/consensus/babe/src/tests.rs index 42b8730cd..fcdada94c 100644 --- a/client/consensus/babe/src/tests.rs +++ b/client/consensus/babe/src/tests.rs @@ -349,6 +349,11 @@ impl TestNetFactory for BabeTestNet { &self.peers } + fn peers_mut(&mut self) -> &mut Vec { + trace!(target: "babe", "Retrieving peers, mutable"); + &mut self.peers + } + fn mut_peers)>(&mut self, closure: F) { closure(&mut self.peers); } diff --git a/client/consensus/beefy/Cargo.toml b/client/consensus/beefy/Cargo.toml index d88c07df2..161d53777 100644 --- a/client/consensus/beefy/Cargo.toml +++ b/client/consensus/beefy/Cargo.toml @@ -25,6 +25,7 @@ sc-keystore = { version = "4.0.0-dev", path = "../../keystore" } sc-network = { version = "0.10.0-dev", path = "../../network" } sc-network-common = { version = "0.10.0-dev", path = "../../network/common" } sc-network-gossip = { version = "0.10.0-dev", path = "../../network-gossip" } +sc-network-sync = { version = "0.10.0-dev", path = "../../network/sync" } sc-utils = { version = "4.0.0-dev", path = "../../utils" } sp-api = { version = "4.0.0-dev", path = "../../../primitives/api" } sp-application-crypto = { version = "7.0.0", path = "../../../primitives/application-crypto" } diff --git a/client/consensus/beefy/src/lib.rs b/client/consensus/beefy/src/lib.rs index 9b627e3ff..67758a497 100644 --- a/client/consensus/beefy/src/lib.rs +++ b/client/consensus/beefy/src/lib.rs @@ -40,7 +40,7 @@ use sc_client_api::{Backend, BlockBackend, BlockchainEvents, FinalityNotificatio use sc_consensus::BlockImport; use sc_network::ProtocolName; use sc_network_common::service::NetworkRequest; -use sc_network_gossip::{GossipEngine, Network as GossipNetwork}; +use sc_network_gossip::{GossipEngine, Network as GossipNetwork, Syncing as GossipSyncing}; use sp_api::{HeaderT, NumberFor, ProvideRuntimeApi}; use sp_blockchain::{ Backend as BlockchainBackend, Error as ClientError, HeaderBackend, Result as ClientResult, @@ -172,9 +172,11 @@ where } /// BEEFY gadget network parameters. -pub struct BeefyNetworkParams { +pub struct BeefyNetworkParams { /// Network implementing gossip, requests and sync-oracle. pub network: Arc, + /// Syncing service implementing a sync oracle and an event stream for peers. + pub sync: Arc, /// Chain specific BEEFY gossip protocol name. See /// [`communication::beefy_protocol_name::gossip_protocol_name`]. pub gossip_protocol_name: ProtocolName, @@ -186,7 +188,7 @@ pub struct BeefyNetworkParams { } /// BEEFY gadget initialization parameters. -pub struct BeefyParams { +pub struct BeefyParams { /// BEEFY client pub client: Arc, /// Client Backend @@ -198,7 +200,7 @@ pub struct BeefyParams { /// Local key store pub key_store: Option, /// BEEFY voter network params - pub network_params: BeefyNetworkParams, + pub network_params: BeefyNetworkParams, /// Minimal delta between blocks, BEEFY should vote for pub min_block_delta: u32, /// Prometheus metric registry @@ -212,15 +214,17 @@ pub struct BeefyParams { /// Start the BEEFY gadget. /// /// This is a thin shim around running and awaiting a BEEFY worker. -pub async fn start_beefy_gadget(beefy_params: BeefyParams) -where +pub async fn start_beefy_gadget( + beefy_params: BeefyParams, +) where B: Block, BE: Backend, C: Client + BlockBackend, P: PayloadProvider, R: ProvideRuntimeApi, R::Api: BeefyApi + MmrApi>, - N: GossipNetwork + NetworkRequest + SyncOracle + Send + Sync + 'static, + N: GossipNetwork + NetworkRequest + Send + Sync + 'static, + S: GossipSyncing + SyncOracle + 'static, { let BeefyParams { client, @@ -235,14 +239,20 @@ where on_demand_justifications_handler, } = beefy_params; - let BeefyNetworkParams { network, gossip_protocol_name, justifications_protocol_name, .. } = - network_params; + let BeefyNetworkParams { + network, + sync, + gossip_protocol_name, + justifications_protocol_name, + .. + } = network_params; let known_peers = Arc::new(Mutex::new(KnownPeers::new())); let gossip_validator = Arc::new(communication::gossip::GossipValidator::new(known_peers.clone())); let mut gossip_engine = sc_network_gossip::GossipEngine::new( network.clone(), + sync.clone(), gossip_protocol_name, gossip_validator.clone(), None, @@ -280,7 +290,7 @@ where backend, payload_provider, runtime, - network, + sync, key_store: key_store.into(), gossip_engine, gossip_validator, diff --git a/client/consensus/beefy/src/tests.rs b/client/consensus/beefy/src/tests.rs index e4fe16a2a..fb1c45b90 100644 --- a/client/consensus/beefy/src/tests.rs +++ b/client/consensus/beefy/src/tests.rs @@ -223,6 +223,10 @@ impl TestNetFactory for BeefyTestNet { &self.peers } + fn peers_mut(&mut self) -> &mut Vec { + &mut self.peers + } + fn mut_peers)>(&mut self, closure: F) { closure(&mut self.peers); } @@ -353,6 +357,7 @@ async fn voter_init_setup( Arc::new(crate::communication::gossip::GossipValidator::new(known_peers)); let mut gossip_engine = sc_network_gossip::GossipEngine::new( net.peer(0).network_service().clone(), + net.peer(0).sync_service().clone(), "/beefy/whatever", gossip_validator, None, @@ -389,6 +394,7 @@ where let network_params = crate::BeefyNetworkParams { network: peer.network_service().clone(), + sync: peer.sync_service().clone(), gossip_protocol_name: beefy_gossip_proto_name(), justifications_protocol_name: on_demand_justif_handler.protocol_name(), _phantom: PhantomData, @@ -407,7 +413,7 @@ where prometheus_registry: None, on_demand_justifications_handler: on_demand_justif_handler, }; - let task = crate::start_beefy_gadget::<_, _, _, _, _, _>(beefy_params); + let task = crate::start_beefy_gadget::<_, _, _, _, _, _, _>(beefy_params); fn assert_send(_: &T) {} assert_send(&task); diff --git a/client/consensus/beefy/src/worker.rs b/client/consensus/beefy/src/worker.rs index 7f3114958..3f29dc7ab 100644 --- a/client/consensus/beefy/src/worker.rs +++ b/client/consensus/beefy/src/worker.rs @@ -33,7 +33,6 @@ use codec::{Codec, Decode, Encode}; use futures::{stream::Fuse, FutureExt, StreamExt}; use log::{debug, error, info, log_enabled, trace, warn}; use sc_client_api::{Backend, FinalityNotification, FinalityNotifications, HeaderBackend}; -use sc_network_common::service::{NetworkEventStream, NetworkRequest}; use sc_network_gossip::GossipEngine; use sc_utils::notification::NotificationReceiver; use sp_api::{BlockId, ProvideRuntimeApi}; @@ -244,11 +243,11 @@ impl VoterOracle { } } -pub(crate) struct WorkerParams { +pub(crate) struct WorkerParams { pub backend: Arc, pub payload_provider: P, pub runtime: Arc, - pub network: N, + pub sync: Arc, pub key_store: BeefyKeystore, pub gossip_engine: GossipEngine, pub gossip_validator: Arc>, @@ -296,12 +295,12 @@ impl PersistedState { } /// A BEEFY worker plays the BEEFY protocol -pub(crate) struct BeefyWorker { +pub(crate) struct BeefyWorker { // utilities backend: Arc, payload_provider: P, runtime: Arc, - network: N, + sync: Arc, key_store: BeefyKeystore, // communication @@ -330,14 +329,14 @@ pub(crate) struct BeefyWorker { persisted_state: PersistedState, } -impl BeefyWorker +impl BeefyWorker where B: Block + Codec, BE: Backend, P: PayloadProvider, + S: SyncOracle, R: ProvideRuntimeApi, R::Api: BeefyApi, - N: NetworkEventStream + NetworkRequest + SyncOracle + Send + Sync + Clone + 'static, { /// Return a new BEEFY worker instance. /// @@ -345,13 +344,13 @@ where /// BEEFY pallet has been deployed on-chain. /// /// The BEEFY pallet is needed in order to keep track of the BEEFY authority set. - pub(crate) fn new(worker_params: WorkerParams) -> Self { + pub(crate) fn new(worker_params: WorkerParams) -> Self { let WorkerParams { backend, payload_provider, runtime, key_store, - network, + sync, gossip_engine, gossip_validator, on_demand_justifications, @@ -364,7 +363,7 @@ where backend, payload_provider, runtime, - network, + sync, key_store, gossip_engine, gossip_validator, @@ -836,7 +835,7 @@ where } // Don't bother voting or requesting justifications during major sync. - if !self.network.is_major_syncing() { + if !self.sync.is_major_syncing() { // There were external events, 'state' is changed, author a vote if needed/possible. if let Err(err) = self.try_to_vote() { debug!(target: LOG_TARGET, "🥩 {}", err); @@ -1065,7 +1064,7 @@ pub(crate) mod tests { use futures::{future::poll_fn, task::Poll}; use parking_lot::Mutex; use sc_client_api::{Backend as BackendT, HeaderBackend}; - use sc_network::NetworkService; + use sc_network_sync::SyncingService; use sc_network_test::TestNetFactory; use sp_api::HeaderT; use sp_blockchain::Backend as BlockchainBackendT; @@ -1075,7 +1074,7 @@ pub(crate) mod tests { }; use sp_runtime::traits::One; use substrate_test_runtime_client::{ - runtime::{Block, Digest, DigestItem, Header, H256}, + runtime::{Block, Digest, DigestItem, Header}, Backend, }; @@ -1113,7 +1112,7 @@ pub(crate) mod tests { Backend, MmrRootProvider, TestApi, - Arc>, + Arc>, > { let keystore = create_beefy_keystore(*key); @@ -1137,10 +1136,16 @@ pub(crate) mod tests { let backend = peer.client().as_backend(); let api = Arc::new(TestApi::with_validator_set(&genesis_validator_set)); let network = peer.network_service().clone(); + let sync = peer.sync_service().clone(); let known_peers = Arc::new(Mutex::new(KnownPeers::new())); let gossip_validator = Arc::new(GossipValidator::new(known_peers.clone())); - let gossip_engine = - GossipEngine::new(network.clone(), "/beefy/1", gossip_validator.clone(), None); + let gossip_engine = GossipEngine::new( + network.clone(), + sync.clone(), + "/beefy/1", + gossip_validator.clone(), + None, + ); let metrics = None; let on_demand_justifications = OnDemandJustificationsEngine::new( network.clone(), @@ -1169,7 +1174,7 @@ pub(crate) mod tests { gossip_engine, gossip_validator, metrics, - network, + sync: Arc::new(sync), on_demand_justifications, persisted_state, }; diff --git a/client/consensus/grandpa/src/communication/mod.rs b/client/consensus/grandpa/src/communication/mod.rs index d3ade4bf3..6c7e3ea46 100644 --- a/client/consensus/grandpa/src/communication/mod.rs +++ b/client/consensus/grandpa/src/communication/mod.rs @@ -59,7 +59,10 @@ use crate::{ use gossip::{ FullCatchUpMessage, FullCommitMessage, GossipMessage, GossipValidator, PeerReport, VoteMessage, }; -use sc_network_common::service::{NetworkBlock, NetworkSyncForkRequest}; +use sc_network_common::{ + service::{NetworkBlock, NetworkSyncForkRequest}, + sync::SyncEventStream, +}; use sc_utils::mpsc::TracingUnboundedReceiver; use sp_consensus_grandpa::{AuthorityId, AuthoritySignature, RoundNumber, SetId as SetIdNumber}; @@ -163,24 +166,35 @@ const TELEMETRY_VOTERS_LIMIT: usize = 10; /// A handle to the network. /// -/// Something that provides both the capabilities needed for the `gossip_network::Network` trait as -/// well as the ability to set a fork sync request for a particular block. -pub trait Network: +/// Something that provides the capabilities needed for the `gossip_network::Network` trait. +pub trait Network: GossipNetwork + Clone + Send + 'static {} + +impl Network for T +where + Block: BlockT, + T: GossipNetwork + Clone + Send + 'static, +{ +} + +/// A handle to syncing-related services. +/// +/// Something that provides the ability to set a fork sync request for a particular block. +pub trait Syncing: NetworkSyncForkRequest> + NetworkBlock> - + GossipNetwork + + SyncEventStream + Clone + Send + 'static { } -impl Network for T +impl Syncing for T where Block: BlockT, T: NetworkSyncForkRequest> + NetworkBlock> - + GossipNetwork + + SyncEventStream + Clone + Send + 'static, @@ -198,8 +212,9 @@ pub(crate) fn global_topic(set_id: SetIdNumber) -> B::Hash { } /// Bridge between the underlying network service, gossiping consensus messages and Grandpa -pub(crate) struct NetworkBridge> { +pub(crate) struct NetworkBridge, S: Syncing> { service: N, + sync: S, gossip_engine: Arc>>, validator: Arc>, @@ -225,15 +240,16 @@ pub(crate) struct NetworkBridge> { telemetry: Option, } -impl> Unpin for NetworkBridge {} +impl, S: Syncing> Unpin for NetworkBridge {} -impl> NetworkBridge { +impl, S: Syncing> NetworkBridge { /// Create a new NetworkBridge to the given NetworkService. Returns the service /// handle. /// On creation it will register previous rounds' votes with the gossip /// service taken from the VoterSetState. pub(crate) fn new( service: N, + sync: S, config: crate::Config, set_state: crate::environment::SharedVoterSetState, prometheus_registry: Option<&Registry>, @@ -246,6 +262,7 @@ impl> NetworkBridge { let validator = Arc::new(validator); let gossip_engine = Arc::new(Mutex::new(GossipEngine::new( service.clone(), + sync.clone(), protocol, validator.clone(), prometheus_registry, @@ -290,6 +307,7 @@ impl> NetworkBridge { NetworkBridge { service, + sync, gossip_engine, validator, neighbor_sender: neighbor_packet_sender, @@ -475,11 +493,11 @@ impl> NetworkBridge { hash: B::Hash, number: NumberFor, ) { - self.service.set_sync_fork_request(peers, hash, number) + self.sync.set_sync_fork_request(peers, hash, number) } } -impl> Future for NetworkBridge { +impl, S: Syncing> Future for NetworkBridge { type Output = Result<(), Error>; fn poll(self: Pin<&mut Self>, cx: &mut Context) -> Poll { @@ -661,10 +679,11 @@ fn incoming_global( }) } -impl> Clone for NetworkBridge { +impl, S: Syncing> Clone for NetworkBridge { fn clone(&self) -> Self { NetworkBridge { service: self.service.clone(), + sync: self.sync.clone(), gossip_engine: self.gossip_engine.clone(), validator: Arc::clone(&self.validator), neighbor_sender: self.neighbor_sender.clone(), diff --git a/client/consensus/grandpa/src/communication/tests.rs b/client/consensus/grandpa/src/communication/tests.rs index 21e2e978c..843e54679 100644 --- a/client/consensus/grandpa/src/communication/tests.rs +++ b/client/consensus/grandpa/src/communication/tests.rs @@ -33,6 +33,7 @@ use sc_network_common::{ NetworkBlock, NetworkEventStream, NetworkNotification, NetworkPeers, NetworkSyncForkRequest, NotificationSender, NotificationSenderError, }, + sync::{SyncEvent as SyncStreamEvent, SyncEventStream}, }; use sc_network_gossip::Validator; use sc_network_test::{Block, Hash}; @@ -153,6 +154,10 @@ impl NetworkNotification for TestNetwork { ) -> Result, NotificationSenderError> { unimplemented!(); } + + fn set_notification_handshake(&self, _protocol: ProtocolName, _handshake: Vec) { + unimplemented!(); + } } impl NetworkBlock> for TestNetwork { @@ -186,8 +191,34 @@ impl sc_network_gossip::ValidatorContext for TestNetwork { fn send_topic(&mut self, _: &PeerId, _: Hash, _: bool) {} } +#[derive(Clone)] +pub(crate) struct TestSync; + +impl SyncEventStream for TestSync { + fn event_stream( + &self, + _name: &'static str, + ) -> Pin + Send>> { + Box::pin(futures::stream::pending()) + } +} + +impl NetworkBlock> for TestSync { + fn announce_block(&self, _hash: Hash, _data: Option>) { + unimplemented!(); + } + + fn new_best_block_imported(&self, _hash: Hash, _number: NumberFor) { + unimplemented!(); + } +} + +impl NetworkSyncForkRequest> for TestSync { + fn set_sync_fork_request(&self, _peers: Vec, _hash: Hash, _number: NumberFor) {} +} + pub(crate) struct Tester { - pub(crate) net_handle: super::NetworkBridge, + pub(crate) net_handle: super::NetworkBridge, gossip_validator: Arc>, pub(crate) events: TracingUnboundedReceiver, } @@ -255,6 +286,7 @@ fn voter_set_state() -> SharedVoterSetState { pub(crate) fn make_test_network() -> (impl Future, TestNetwork) { let (tx, rx) = tracing_unbounded("test", 100_000); let net = TestNetwork { sender: tx }; + let sync = TestSync {}; #[derive(Clone)] struct Exit; @@ -267,7 +299,8 @@ pub(crate) fn make_test_network() -> (impl Future, TestNetwork) } } - let bridge = super::NetworkBridge::new(net.clone(), config(), voter_set_state(), None, None); + let bridge = + super::NetworkBridge::new(net.clone(), sync, config(), voter_set_state(), None, None); ( futures::future::ready(Tester { @@ -370,6 +403,7 @@ fn good_commit_leads_to_relay() { protocol: grandpa_protocol_name::NAME.into(), negotiated_fallback: None, role: ObservedRole::Full, + received_handshake: vec![], }); let _ = sender.unbounded_send(NetworkEvent::NotificationsReceived { @@ -387,6 +421,7 @@ fn good_commit_leads_to_relay() { protocol: grandpa_protocol_name::NAME.into(), negotiated_fallback: None, role: ObservedRole::Full, + received_handshake: vec![], }); // Announce its local set has being on the current set id through a neighbor @@ -519,6 +554,7 @@ fn bad_commit_leads_to_report() { protocol: grandpa_protocol_name::NAME.into(), negotiated_fallback: None, role: ObservedRole::Full, + received_handshake: vec![], }); let _ = sender.unbounded_send(NetworkEvent::NotificationsReceived { remote: sender_id, diff --git a/client/consensus/grandpa/src/environment.rs b/client/consensus/grandpa/src/environment.rs index 342542661..67820a59c 100644 --- a/client/consensus/grandpa/src/environment.rs +++ b/client/consensus/grandpa/src/environment.rs @@ -50,7 +50,7 @@ use sp_runtime::traits::{Block as BlockT, Header as HeaderT, NumberFor, Zero}; use crate::{ authorities::{AuthoritySet, SharedAuthoritySet}, - communication::Network as NetworkT, + communication::{Network as NetworkT, Syncing as SyncingT}, justification::GrandpaJustification, local_authority_id, notification::GrandpaJustificationSender, @@ -423,13 +423,21 @@ impl Metrics { } /// The environment we run GRANDPA in. -pub(crate) struct Environment, SC, VR> { +pub(crate) struct Environment< + Backend, + Block: BlockT, + C, + N: NetworkT, + S: SyncingT, + SC, + VR, +> { pub(crate) client: Arc, pub(crate) select_chain: SC, pub(crate) voters: Arc>, pub(crate) config: Config, pub(crate) authority_set: SharedAuthoritySet>, - pub(crate) network: crate::communication::NetworkBridge, + pub(crate) network: crate::communication::NetworkBridge, pub(crate) set_id: SetId, pub(crate) voter_set_state: SharedVoterSetState, pub(crate) voting_rule: VR, @@ -439,7 +447,9 @@ pub(crate) struct Environment, SC, pub(crate) _phantom: PhantomData, } -impl, SC, VR> Environment { +impl, S: SyncingT, SC, VR> + Environment +{ /// Updates the voter set state using the given closure. The write lock is /// held during evaluation of the closure and the environment's voter set /// state is set to its result if successful. @@ -469,13 +479,14 @@ impl, SC, VR> Environment Environment +impl Environment where Block: BlockT, BE: BackendT, C: ClientForGrandpa, C::Api: GrandpaApi, N: NetworkT, + S: SyncingT, SC: SelectChainT, { /// Report the given equivocation to the GRANDPA runtime module. This method @@ -572,13 +583,14 @@ where } } -impl finality_grandpa::Chain> - for Environment +impl finality_grandpa::Chain> + for Environment where Block: BlockT, BE: BackendT, C: ClientForGrandpa, N: NetworkT, + S: SyncingT, SC: SelectChainT, VR: VotingRuleT, NumberFor: BlockNumberOps, @@ -630,14 +642,15 @@ where Ok(tree_route.retracted().iter().skip(1).map(|e| e.hash).collect()) } -impl voter::Environment> - for Environment +impl voter::Environment> + for Environment where Block: BlockT, B: BackendT, C: ClientForGrandpa + 'static, C::Api: GrandpaApi, N: NetworkT, + S: SyncingT, SC: SelectChainT + 'static, VR: VotingRuleT + Clone + 'static, NumberFor: BlockNumberOps, diff --git a/client/consensus/grandpa/src/lib.rs b/client/consensus/grandpa/src/lib.rs index ef4beb51b..7bf48a498 100644 --- a/client/consensus/grandpa/src/lib.rs +++ b/client/consensus/grandpa/src/lib.rs @@ -141,7 +141,7 @@ pub use voting_rule::{ }; use aux_schema::PersistentData; -use communication::{Network as NetworkT, NetworkBridge}; +use communication::{Network as NetworkT, NetworkBridge, Syncing as SyncingT}; use environment::{Environment, VoterSetState}; use until_imported::UntilGlobalMessageBlocksImported; @@ -349,10 +349,11 @@ pub(crate) trait BlockSyncRequester { ); } -impl BlockSyncRequester for NetworkBridge +impl BlockSyncRequester for NetworkBridge where Block: BlockT, Network: NetworkT, + Syncing: SyncingT, { fn set_sync_fork_request( &self, @@ -617,11 +618,11 @@ where )) } -fn global_communication( +fn global_communication( set_id: SetId, voters: &Arc>, client: Arc, - network: &NetworkBridge, + network: &NetworkBridge, keystore: Option<&SyncCryptoStorePtr>, metrics: Option, ) -> ( @@ -640,6 +641,7 @@ where BE: Backend + 'static, C: ClientForGrandpa + 'static, N: NetworkT, + S: SyncingT, NumberFor: BlockNumberOps, { let is_voter = local_authority_id(voters, keystore).is_some(); @@ -665,7 +667,7 @@ where } /// Parameters used to run Grandpa. -pub struct GrandpaParams { +pub struct GrandpaParams { /// Configuration for the GRANDPA service. pub config: Config, /// A link to the block import worker. @@ -676,6 +678,8 @@ pub struct GrandpaParams { /// `sc_network` crate, it is assumed that the Grandpa notifications protocol has been passed /// to the configuration of the networking. See [`grandpa_peers_set_config`]. pub network: N, + /// Event stream for syncing-related events. + pub sync: S, /// A voting rule used to potentially restrict target votes. pub voting_rule: VR, /// The prometheus metrics registry. @@ -710,13 +714,14 @@ pub fn grandpa_peers_set_config( /// Run a GRANDPA voter as a task. Provide configuration and a link to a /// block import worker that has already been instantiated with `block_import`. -pub fn run_grandpa_voter( - grandpa_params: GrandpaParams, +pub fn run_grandpa_voter( + grandpa_params: GrandpaParams, ) -> sp_blockchain::Result + Send> where Block::Hash: Ord, BE: Backend + 'static, N: NetworkT + Sync + 'static, + S: SyncingT + Sync + 'static, SC: SelectChain + 'static, VR: VotingRule + Clone + 'static, NumberFor: BlockNumberOps, @@ -727,6 +732,7 @@ where mut config, link, network, + sync, voting_rule, prometheus_registry, shared_voter_state, @@ -751,6 +757,7 @@ where let network = NetworkBridge::new( network, + sync, config.clone(), persistent_data.set_state.clone(), prometheus_registry.as_ref(), @@ -836,26 +843,27 @@ impl Metrics { /// Future that powers the voter. #[must_use] -struct VoterWork, SC, VR> { +struct VoterWork, S: SyncingT, SC, VR> { voter: Pin< Box>>> + Send>, >, shared_voter_state: SharedVoterState, - env: Arc>, + env: Arc>, voter_commands_rx: TracingUnboundedReceiver>>, - network: NetworkBridge, + network: NetworkBridge, telemetry: Option, /// Prometheus metrics. metrics: Option, } -impl VoterWork +impl VoterWork where Block: BlockT, B: Backend + 'static, C: ClientForGrandpa + 'static, C::Api: GrandpaApi, N: NetworkT + Sync, + S: SyncingT + Sync, NumberFor: BlockNumberOps, SC: SelectChain + 'static, VR: VotingRule + Clone + 'static, @@ -863,7 +871,7 @@ where fn new( client: Arc, config: Config, - network: NetworkBridge, + network: NetworkBridge, select_chain: SC, voting_rule: VR, persistent_data: PersistentData, @@ -1072,11 +1080,12 @@ where } } -impl Future for VoterWork +impl Future for VoterWork where Block: BlockT, B: Backend + 'static, N: NetworkT + Sync, + S: SyncingT + Sync, NumberFor: BlockNumberOps, SC: SelectChain + 'static, C: ClientForGrandpa + 'static, diff --git a/client/consensus/grandpa/src/observer.rs b/client/consensus/grandpa/src/observer.rs index b382430ef..53672c1f0 100644 --- a/client/consensus/grandpa/src/observer.rs +++ b/client/consensus/grandpa/src/observer.rs @@ -39,7 +39,7 @@ use sp_runtime::traits::{Block as BlockT, NumberFor}; use crate::{ authorities::SharedAuthoritySet, aux_schema::PersistentData, - communication::{Network as NetworkT, NetworkBridge}, + communication::{Network as NetworkT, NetworkBridge, Syncing as SyncingT}, environment, global_communication, notification::GrandpaJustificationSender, ClientForGrandpa, CommandOrError, CommunicationIn, Config, Error, LinkHalf, VoterCommand, @@ -163,14 +163,16 @@ where /// already been instantiated with `block_import`. /// NOTE: this is currently not part of the crate's public API since we don't consider /// it stable enough to use on a live network. -pub fn run_grandpa_observer( +pub fn run_grandpa_observer( config: Config, link: LinkHalf, network: N, + sync: S, ) -> sp_blockchain::Result + Send> where BE: Backend + Unpin + 'static, N: NetworkT, + S: SyncingT, SC: SelectChain, NumberFor: BlockNumberOps, Client: ClientForGrandpa + 'static, @@ -186,6 +188,7 @@ where let network = NetworkBridge::new( network, + sync, config.clone(), persistent_data.set_state.clone(), None, @@ -211,11 +214,11 @@ where /// Future that powers the observer. #[must_use] -struct ObserverWork> { +struct ObserverWork, S: SyncingT> { observer: Pin>>> + Send>>, client: Arc, - network: NetworkBridge, + network: NetworkBridge, persistent_data: PersistentData, keystore: Option, voter_commands_rx: TracingUnboundedReceiver>>, @@ -224,17 +227,18 @@ struct ObserverWork> { _phantom: PhantomData, } -impl ObserverWork +impl ObserverWork where B: BlockT, BE: Backend + 'static, Client: ClientForGrandpa + 'static, Network: NetworkT, + Syncing: SyncingT, NumberFor: BlockNumberOps, { fn new( client: Arc, - network: NetworkBridge, + network: NetworkBridge, persistent_data: PersistentData, keystore: Option, voter_commands_rx: TracingUnboundedReceiver>>, @@ -347,12 +351,13 @@ where } } -impl Future for ObserverWork +impl Future for ObserverWork where B: BlockT, BE: Backend + Unpin + 'static, C: ClientForGrandpa + 'static, N: NetworkT, + S: SyncingT, NumberFor: BlockNumberOps, { type Output = Result<(), Error>; diff --git a/client/consensus/grandpa/src/tests.rs b/client/consensus/grandpa/src/tests.rs index 7cc5e75fa..f7747a203 100644 --- a/client/consensus/grandpa/src/tests.rs +++ b/client/consensus/grandpa/src/tests.rs @@ -145,6 +145,10 @@ impl TestNetFactory for GrandpaTestNet { &self.peers } + fn peers_mut(&mut self) -> &mut Vec { + &mut self.peers + } + fn mut_peers)>(&mut self, closure: F) { closure(&mut self.peers); } @@ -310,6 +314,7 @@ fn initialize_grandpa( net.peers[peer_id].data.lock().take().expect("link initialized at startup; qed"); (net.peers[peer_id].network_service().clone(), link) }; + let sync = net.peers[peer_id].sync_service().clone(); let grandpa_params = GrandpaParams { config: Config { @@ -324,6 +329,7 @@ fn initialize_grandpa( }, link, network: net_service, + sync, voting_rule: (), prometheus_registry: None, shared_voter_state: SharedVoterState::empty(), @@ -451,6 +457,7 @@ async fn finalize_3_voters_1_full_observer() { tokio::spawn({ let peer_id = 3; let net_service = net.peers[peer_id].network_service().clone(); + let sync = net.peers[peer_id].sync_service().clone(); let link = net.peers[peer_id].data.lock().take().expect("link initialized at startup; qed"); let grandpa_params = GrandpaParams { @@ -466,6 +473,7 @@ async fn finalize_3_voters_1_full_observer() { }, link, network: net_service, + sync, voting_rule: (), prometheus_registry: None, shared_voter_state: SharedVoterState::empty(), @@ -533,11 +541,15 @@ async fn transition_3_voters_twice_1_full_observer() { for (peer_id, local_key) in all_peers.clone().into_iter().enumerate() { let keystore = create_keystore(local_key); - let (net_service, link) = { + let (net_service, link, sync) = { let net = net.lock(); let link = net.peers[peer_id].data.lock().take().expect("link initialized at startup; qed"); - (net.peers[peer_id].network_service().clone(), link) + ( + net.peers[peer_id].network_service().clone(), + link, + net.peers[peer_id].sync_service().clone(), + ) }; let grandpa_params = GrandpaParams { @@ -553,6 +565,7 @@ async fn transition_3_voters_twice_1_full_observer() { }, link, network: net_service, + sync, voting_rule: (), prometheus_registry: None, shared_voter_state: SharedVoterState::empty(), @@ -999,6 +1012,7 @@ async fn voter_persists_its_votes() { communication::NetworkBridge::new( net.peers[1].network_service().clone(), + net.peers[1].sync_service().clone(), config.clone(), set_state, None, @@ -1016,6 +1030,7 @@ async fn voter_persists_its_votes() { let link = net.peers[0].data.lock().take().expect("link initialized at startup; qed"); (net.peers[0].network_service().clone(), link) }; + let sync = net.peers[0].sync_service().clone(); let grandpa_params = GrandpaParams { config: Config { @@ -1030,6 +1045,7 @@ async fn voter_persists_its_votes() { }, link, network: net_service, + sync, voting_rule: VotingRulesBuilder::default().build(), prometheus_registry: None, shared_voter_state: SharedVoterState::empty(), @@ -1050,6 +1066,7 @@ async fn voter_persists_its_votes() { // the network service of this new peer net.add_authority_peer(); let net_service = net.peers[2].network_service().clone(); + let sync = net.peers[2].sync_service().clone(); // but we'll reuse the client from the first peer (alice_voter1) // since we want to share the same database, so that we can // read the persisted state after aborting alice_voter1. @@ -1071,6 +1088,7 @@ async fn voter_persists_its_votes() { }, link, network: net_service, + sync, voting_rule: VotingRulesBuilder::default().build(), prometheus_registry: None, shared_voter_state: SharedVoterState::empty(), @@ -1232,6 +1250,7 @@ async fn finalize_3_voters_1_light_observer() { }, net.peers[3].data.lock().take().expect("link initialized at startup; qed"), net.peers[3].network_service().clone(), + net.peers[3].sync_service().clone(), ) .unwrap(); net.peer(0).push_blocks(20, false); @@ -1265,6 +1284,7 @@ async fn voter_catches_up_to_latest_round_when_behind() { link, net: Arc>| -> Pin + Send>> { + let mut net = net.lock(); let grandpa_params = GrandpaParams { config: Config { gossip_duration: TEST_GOSSIP_DURATION, @@ -1277,7 +1297,8 @@ async fn voter_catches_up_to_latest_round_when_behind() { protocol_name: grandpa_protocol_name::NAME.into(), }, link, - network: net.lock().peer(peer_id).network_service().clone(), + network: net.peer(peer_id).network_service().clone(), + sync: net.peer(peer_id).sync_service().clone(), voting_rule: (), prometheus_registry: None, shared_voter_state: SharedVoterState::empty(), @@ -1359,18 +1380,20 @@ async fn voter_catches_up_to_latest_round_when_behind() { future::select(test, drive_to_completion).await; } -type TestEnvironment = - Environment; +type TestEnvironment = + Environment; -fn test_environment_with_select_chain( +fn test_environment_with_select_chain( link: &TestLinkHalf, keystore: Option, network_service: N, + sync_service: S, select_chain: SC, voting_rule: VR, -) -> TestEnvironment +) -> TestEnvironment where N: NetworkT, + S: SyncingT, VR: VotingRule, { let PersistentData { ref authority_set, ref set_state, .. } = link.persistent_data; @@ -1386,8 +1409,14 @@ where protocol_name: grandpa_protocol_name::NAME.into(), }; - let network = - NetworkBridge::new(network_service.clone(), config.clone(), set_state.clone(), None, None); + let network = NetworkBridge::new( + network_service.clone(), + sync_service, + config.clone(), + set_state.clone(), + None, + None, + ); Environment { authority_set: authority_set.clone(), @@ -1406,20 +1435,23 @@ where } } -fn test_environment( +fn test_environment( link: &TestLinkHalf, keystore: Option, network_service: N, + sync_service: S, voting_rule: VR, -) -> TestEnvironment, VR> +) -> TestEnvironment, VR> where N: NetworkT, + S: SyncingT, VR: VotingRule, { test_environment_with_select_chain( link, keystore, network_service, + sync_service, link.select_chain.clone(), voting_rule, ) @@ -1435,19 +1467,22 @@ async fn grandpa_environment_respects_voting_rules() { let mut net = GrandpaTestNet::new(TestApi::new(voters), 1, 0); let peer = net.peer(0); let network_service = peer.network_service().clone(); + let sync_service = peer.sync_service().clone(); let link = peer.data.lock().take().unwrap(); // add 21 blocks let hashes = peer.push_blocks(21, false); // create an environment with no voting rule restrictions - let unrestricted_env = test_environment(&link, None, network_service.clone(), ()); + let unrestricted_env = + test_environment(&link, None, network_service.clone(), sync_service.clone(), ()); // another with 3/4 unfinalized chain voting rule restriction let three_quarters_env = test_environment( &link, None, network_service.clone(), + sync_service.clone(), voting_rule::ThreeQuartersOfTheUnfinalizedChain, ); @@ -1457,6 +1492,7 @@ async fn grandpa_environment_respects_voting_rules() { &link, None, network_service.clone(), + sync_service, VotingRulesBuilder::default().build(), ); @@ -1549,6 +1585,7 @@ async fn grandpa_environment_passes_actual_best_block_to_voting_rules() { let mut net = GrandpaTestNet::new(TestApi::new(voters), 1, 0); let peer = net.peer(0); let network_service = peer.network_service().clone(); + let sync_service = peer.sync_service().clone(); let link = peer.data.lock().take().unwrap(); let client = peer.client().as_client().clone(); let select_chain = MockSelectChain::default(); @@ -1562,6 +1599,7 @@ async fn grandpa_environment_passes_actual_best_block_to_voting_rules() { &link, None, network_service.clone(), + sync_service, select_chain.clone(), voting_rule::BeforeBestBlockBy(5), ); @@ -1607,6 +1645,7 @@ async fn grandpa_environment_checks_if_best_block_is_descendent_of_finality_targ let mut net = GrandpaTestNet::new(TestApi::new(voters), 1, 0); let peer = net.peer(0); let network_service = peer.network_service().clone(); + let sync_service = peer.sync_service().clone(); let link = peer.data.lock().take().unwrap(); let client = peer.client().as_client().clone(); let select_chain = MockSelectChain::default(); @@ -1615,6 +1654,7 @@ async fn grandpa_environment_checks_if_best_block_is_descendent_of_finality_targ &link, None, network_service.clone(), + sync_service.clone(), select_chain.clone(), voting_rule.clone(), ); @@ -1717,10 +1757,12 @@ async fn grandpa_environment_never_overwrites_round_voter_state() { let mut net = GrandpaTestNet::new(TestApi::new(voters), 1, 0); let peer = net.peer(0); let network_service = peer.network_service().clone(); + let sync_service = peer.sync_service().clone(); let link = peer.data.lock().take().unwrap(); let keystore = create_keystore(peers[0]); - let environment = test_environment(&link, Some(keystore), network_service.clone(), ()); + let environment = + test_environment(&link, Some(keystore), network_service.clone(), sync_service, ()); let round_state = || finality_grandpa::round::State::genesis(Default::default()); let base = || Default::default(); @@ -1921,9 +1963,10 @@ async fn grandpa_environment_doesnt_send_equivocation_reports_for_itself() { let mut net = GrandpaTestNet::new(TestApi::new(voters), 1, 0); let peer = net.peer(0); let network_service = peer.network_service().clone(); + let sync_service = peer.sync_service().clone(); let link = peer.data.lock().take().unwrap(); let keystore = create_keystore(alice); - test_environment(&link, Some(keystore), network_service.clone(), ()) + test_environment(&link, Some(keystore), network_service.clone(), sync_service, ()) }; let signed_prevote = { diff --git a/client/informant/src/display.rs b/client/informant/src/display.rs index 85be926d9..fc68e5603 100644 --- a/client/informant/src/display.rs +++ b/client/informant/src/display.rs @@ -24,7 +24,7 @@ use sc_network_common::{ service::NetworkStatus, sync::{ warp::{WarpSyncPhase, WarpSyncProgress}, - SyncState, + SyncState, SyncStatus, }, }; use sp_runtime::traits::{Block as BlockT, CheckedDiv, NumberFor, Saturating, Zero}; @@ -69,7 +69,12 @@ impl InformantDisplay { } /// Displays the informant by calling `info!`. - pub fn display(&mut self, info: &ClientInfo, net_status: NetworkStatus) { + pub fn display( + &mut self, + info: &ClientInfo, + net_status: NetworkStatus, + sync_status: SyncStatus, + ) { let best_number = info.chain.best_number; let best_hash = info.chain.best_hash; let finalized_number = info.chain.finalized_number; @@ -94,7 +99,7 @@ impl InformantDisplay { }; let (level, status, target) = - match (net_status.sync_state, net_status.state_sync, net_status.warp_sync) { + match (sync_status.state, sync_status.state_sync, sync_status.warp_sync) { ( _, _, diff --git a/client/informant/src/lib.rs b/client/informant/src/lib.rs index cb30d08c4..dc6aebc2e 100644 --- a/client/informant/src/lib.rs +++ b/client/informant/src/lib.rs @@ -23,7 +23,7 @@ use futures::prelude::*; use futures_timer::Delay; use log::{debug, info, trace}; use sc_client_api::{BlockchainEvents, UsageProvider}; -use sc_network_common::service::NetworkStatusProvider; +use sc_network_common::{service::NetworkStatusProvider, sync::SyncStatusProvider}; use sp_blockchain::HeaderMetadata; use sp_runtime::traits::{Block as BlockT, Header}; use std::{collections::VecDeque, fmt::Display, sync::Arc, time::Duration}; @@ -51,9 +51,10 @@ impl Default for OutputFormat { } /// Builds the informant and returns a `Future` that drives the informant. -pub async fn build(client: Arc, network: N, format: OutputFormat) +pub async fn build(client: Arc, network: N, syncing: S, format: OutputFormat) where - N: NetworkStatusProvider, + N: NetworkStatusProvider, + S: SyncStatusProvider, C: UsageProvider + HeaderMetadata + BlockchainEvents, >::Error: Display, { @@ -63,10 +64,15 @@ where let display_notifications = interval(Duration::from_millis(5000)) .filter_map(|_| async { - let status = network.status().await; - status.ok() + let net_status = network.status().await; + let sync_status = syncing.status().await; + + match (net_status.ok(), sync_status.ok()) { + (Some(net), Some(sync)) => Some((net, sync)), + _ => None, + } }) - .for_each(move |net_status| { + .for_each(move |(net_status, sync_status)| { let info = client_1.usage_info(); if let Some(ref usage) = info.usage { trace!(target: "usage", "Usage statistics: {}", usage); @@ -76,7 +82,7 @@ where "Usage statistics not displayed as backend does not provide it", ) } - display.display(&info, net_status); + display.display(&info, net_status, sync_status); future::ready(()) }); diff --git a/client/network-gossip/src/bridge.rs b/client/network-gossip/src/bridge.rs index 129e18d8a..7368cc770 100644 --- a/client/network-gossip/src/bridge.rs +++ b/client/network-gossip/src/bridge.rs @@ -18,10 +18,13 @@ use crate::{ state_machine::{ConsensusGossip, TopicNotification, PERIODIC_MAINTENANCE_INTERVAL}, - Network, Validator, + Network, Syncing, Validator, }; -use sc_network_common::protocol::{event::Event, ProtocolName}; +use sc_network_common::{ + protocol::{event::Event, ProtocolName}, + sync::SyncEvent, +}; use sc_peerset::ReputationChange; use futures::{ @@ -44,11 +47,14 @@ use std::{ pub struct GossipEngine { state_machine: ConsensusGossip, network: Box + Send>, + sync: Box>, periodic_maintenance_interval: futures_timer::Delay, protocol: ProtocolName, /// Incoming events from the network. network_event_stream: Pin + Send>>, + /// Incoming events from the syncing service. + sync_event_stream: Pin + Send>>, /// Outgoing events to the consumer. message_sinks: HashMap>>, /// Buffered messages (see [`ForwardingState`]). @@ -75,25 +81,31 @@ impl Unpin for GossipEngine {} impl GossipEngine { /// Create a new instance. - pub fn new + Send + Clone + 'static>( + pub fn new( network: N, + sync: S, protocol: impl Into, validator: Arc>, metrics_registry: Option<&Registry>, ) -> Self where B: 'static, + N: Network + Send + Clone + 'static, + S: Syncing + Send + Clone + 'static, { let protocol = protocol.into(); let network_event_stream = network.event_stream("network-gossip"); + let sync_event_stream = sync.event_stream("network-gossip"); GossipEngine { state_machine: ConsensusGossip::new(validator, protocol.clone(), metrics_registry), network: Box::new(network), + sync: Box::new(sync), periodic_maintenance_interval: futures_timer::Delay::new(PERIODIC_MAINTENANCE_INTERVAL), protocol, network_event_stream, + sync_event_stream, message_sinks: HashMap::new(), forwarding_state: ForwardingState::Idle, @@ -162,7 +174,7 @@ impl GossipEngine { /// Note: this method isn't strictly related to gossiping and should eventually be moved /// somewhere else. pub fn announce(&self, block: B::Hash, associated_data: Option>) { - self.network.announce_block(block, associated_data); + self.sync.announce_block(block, associated_data); } } @@ -175,28 +187,24 @@ impl Future for GossipEngine { 'outer: loop { match &mut this.forwarding_state { ForwardingState::Idle => { - match this.network_event_stream.poll_next_unpin(cx) { + let net_event_stream = this.network_event_stream.poll_next_unpin(cx); + let sync_event_stream = this.sync_event_stream.poll_next_unpin(cx); + + if net_event_stream.is_pending() && sync_event_stream.is_pending() { + break + } + + match net_event_stream { Poll::Ready(Some(event)) => match event { - Event::SyncConnected { remote } => { - this.network.add_set_reserved(remote, this.protocol.clone()); - }, - Event::SyncDisconnected { remote } => { - this.network.remove_peers_from_reserved_set( - this.protocol.clone(), - vec![remote], - ); - }, - Event::NotificationStreamOpened { remote, protocol, role, .. } => { - if protocol != this.protocol { - continue - } - this.state_machine.new_peer(&mut *this.network, remote, role); - }, + Event::NotificationStreamOpened { remote, protocol, role, .. } => + if protocol == this.protocol { + this.state_machine.new_peer(&mut *this.network, remote, role); + }, Event::NotificationStreamClosed { remote, protocol } => { - if protocol != this.protocol { - continue + if protocol == this.protocol { + this.state_machine + .peer_disconnected(&mut *this.network, remote); } - this.state_machine.peer_disconnected(&mut *this.network, remote); }, Event::NotificationsReceived { remote, messages } => { let messages = messages @@ -225,7 +233,25 @@ impl Future for GossipEngine { self.is_terminated = true; return Poll::Ready(()) }, - Poll::Pending => break, + Poll::Pending => {}, + } + + match sync_event_stream { + Poll::Ready(Some(event)) => match event { + SyncEvent::PeerConnected(remote) => + this.network.add_set_reserved(remote, this.protocol.clone()), + SyncEvent::PeerDisconnected(remote) => + this.network.remove_peers_from_reserved_set( + this.protocol.clone(), + vec![remote], + ), + }, + // The sync event stream closed. Do the same for [`GossipValidator`]. + Poll::Ready(None) => { + self.is_terminated = true; + return Poll::Ready(()) + }, + Poll::Pending => {}, } }, ForwardingState::Busy(to_forward) => { @@ -321,6 +347,7 @@ mod tests { NetworkBlock, NetworkEventStream, NetworkNotification, NetworkPeers, NotificationSender, NotificationSenderError, }, + sync::SyncEventStream, }; use sp_runtime::{ testing::H256, @@ -433,6 +460,10 @@ mod tests { ) -> Result, NotificationSenderError> { unimplemented!(); } + + fn set_notification_handshake(&self, _protocol: ProtocolName, _handshake: Vec) { + unimplemented!(); + } } impl NetworkBlock<::Hash, NumberFor> for TestNetwork { @@ -449,6 +480,42 @@ mod tests { } } + #[derive(Clone, Default)] + struct TestSync { + inner: Arc>, + } + + #[derive(Clone, Default)] + struct TestSyncInner { + event_senders: Vec>, + } + + impl SyncEventStream for TestSync { + fn event_stream( + &self, + _name: &'static str, + ) -> Pin + Send>> { + let (tx, rx) = unbounded(); + self.inner.lock().unwrap().event_senders.push(tx); + + Box::pin(rx) + } + } + + impl NetworkBlock<::Hash, NumberFor> for TestSync { + fn announce_block(&self, _hash: ::Hash, _data: Option>) { + unimplemented!(); + } + + fn new_best_block_imported( + &self, + _hash: ::Hash, + _number: NumberFor, + ) { + unimplemented!(); + } + } + struct AllowAll; impl Validator for AllowAll { fn validate( @@ -468,8 +535,10 @@ mod tests { #[test] fn returns_when_network_event_stream_closes() { let network = TestNetwork::default(); + let sync = Arc::new(TestSync::default()); let mut gossip_engine = GossipEngine::::new( network.clone(), + sync, "/my_protocol", Arc::new(AllowAll {}), None, @@ -495,9 +564,11 @@ mod tests { let protocol = ProtocolName::from("/my_protocol"); let remote_peer = PeerId::random(); let network = TestNetwork::default(); + let sync = Arc::new(TestSync::default()); let mut gossip_engine = GossipEngine::::new( network.clone(), + sync.clone(), protocol.clone(), Arc::new(AllowAll {}), None, @@ -512,6 +583,7 @@ mod tests { protocol: protocol.clone(), negotiated_fallback: None, role: ObservedRole::Authority, + received_handshake: vec![], }) .expect("Event stream is unbounded; qed."); @@ -614,6 +686,7 @@ mod tests { let protocol = ProtocolName::from("/my_protocol"); let remote_peer = PeerId::random(); let network = TestNetwork::default(); + let sync = Arc::new(TestSync::default()); let num_channels_per_topic = channels.iter().fold( HashMap::new(), @@ -640,6 +713,7 @@ mod tests { let mut gossip_engine = GossipEngine::::new( network.clone(), + sync.clone(), protocol.clone(), Arc::new(TestValidator {}), None, @@ -674,6 +748,7 @@ mod tests { protocol: protocol.clone(), negotiated_fallback: None, role: ObservedRole::Authority, + received_handshake: vec![], }) .expect("Event stream is unbounded; qed."); diff --git a/client/network-gossip/src/lib.rs b/client/network-gossip/src/lib.rs index 859ec3d52..e3448ba01 100644 --- a/client/network-gossip/src/lib.rs +++ b/client/network-gossip/src/lib.rs @@ -71,6 +71,7 @@ use libp2p::{multiaddr, PeerId}; use sc_network_common::{ protocol::ProtocolName, service::{NetworkBlock, NetworkEventStream, NetworkNotification, NetworkPeers}, + sync::SyncEventStream, }; use sp_runtime::traits::{Block as BlockT, NumberFor}; use std::iter; @@ -80,9 +81,7 @@ mod state_machine; mod validator; /// Abstraction over a network. -pub trait Network: - NetworkPeers + NetworkEventStream + NetworkNotification + NetworkBlock> -{ +pub trait Network: NetworkPeers + NetworkEventStream + NetworkNotification { fn add_set_reserved(&self, who: PeerId, protocol: ProtocolName) { let addr = iter::once(multiaddr::Protocol::P2p(who.into())).collect::(); @@ -93,10 +92,9 @@ pub trait Network: } } -impl Network for T where - T: NetworkPeers - + NetworkEventStream - + NetworkNotification - + NetworkBlock> -{ -} +impl Network for T where T: NetworkPeers + NetworkEventStream + NetworkNotification {} + +/// Abstraction over the syncing subsystem. +pub trait Syncing: SyncEventStream + NetworkBlock> {} + +impl Syncing for T where T: SyncEventStream + NetworkBlock> {} diff --git a/client/network-gossip/src/state_machine.rs b/client/network-gossip/src/state_machine.rs index 7cd5b5613..3a0d5fc0d 100644 --- a/client/network-gossip/src/state_machine.rs +++ b/client/network-gossip/src/state_machine.rs @@ -683,6 +683,10 @@ mod tests { ) -> Result, NotificationSenderError> { unimplemented!(); } + + fn set_notification_handshake(&self, _protocol: ProtocolName, _handshake: Vec) { + unimplemented!(); + } } impl NetworkBlock<::Hash, NumberFor> for NoOpNetwork { diff --git a/client/network/common/Cargo.toml b/client/network/common/Cargo.toml index 06ff00ef7..c5f9d6d4c 100644 --- a/client/network/common/Cargo.toml +++ b/client/network/common/Cargo.toml @@ -16,6 +16,7 @@ targets = ["x86_64-unknown-linux-gnu"] prost-build = "0.11" [dependencies] +array-bytes = "4.1" async-trait = "0.1.57" bitflags = "1.3.2" bytes = "1" @@ -30,9 +31,14 @@ prometheus-endpoint = { package = "substrate-prometheus-endpoint", version = "0. smallvec = "1.8.0" sc-consensus = { version = "0.10.0-dev", path = "../../consensus/common" } sc-peerset = { version = "4.0.0-dev", path = "../../peerset" } +sc-utils = { version = "4.0.0-dev", path = "../../utils" } serde = { version = "1.0.136", features = ["derive"] } sp-consensus = { version = "0.10.0-dev", path = "../../../primitives/consensus/common" } sp-consensus-grandpa = { version = "4.0.0-dev", path = "../../../primitives/consensus/grandpa" } sp-runtime = { version = "7.0.0", path = "../../../primitives/runtime" } sp-blockchain = { version = "4.0.0-dev", path = "../../../primitives/blockchain" } thiserror = "1.0" +zeroize = "1.4.3" + +[dev-dependencies] +tempfile = "3.1.0" diff --git a/client/network/common/src/config.rs b/client/network/common/src/config.rs index 782b5a965..6a02129fc 100644 --- a/client/network/common/src/config.rs +++ b/client/network/common/src/config.rs @@ -18,11 +18,33 @@ //! Configuration of the networking layer. -use crate::protocol; +pub use crate::{ + protocol::{self, role::Role}, + request_responses::{ + IncomingRequest, OutgoingResponse, ProtocolConfig as RequestResponseConfig, + }, + sync::warp::WarpSyncProvider, + ExHashT, +}; +pub use libp2p::{build_multiaddr, core::PublicKey, identity}; use codec::Encode; -use libp2p::{multiaddr, Multiaddr, PeerId}; -use std::{fmt, str, str::FromStr}; +use libp2p::{ + identity::{ed25519, Keypair}, + multiaddr, Multiaddr, PeerId, +}; +use zeroize::Zeroize; + +use std::{ + error::Error, + fmt, fs, + io::{self, Write}, + iter, + net::Ipv4Addr, + path::{Path, PathBuf}, + str, + str::FromStr, +}; /// Protocol name prefix, transmitted on the wire for legacy protocol names. /// I.e., `dot` in `/dot/sync/2`. Should be unique for each chain. Always UTF-8. @@ -331,3 +353,350 @@ impl NonReservedPeerMode { } } } + +/// Sync operation mode. +#[derive(Copy, Clone, Debug, Eq, PartialEq)] +pub enum SyncMode { + /// Full block download and verification. + Full, + /// Download blocks and the latest state. + Fast { + /// Skip state proof download and verification. + skip_proofs: bool, + /// Download indexed transactions for recent blocks. + storage_chain_mode: bool, + }, + /// Warp sync - verify authority set transitions and the latest state. + Warp, +} + +impl SyncMode { + /// Returns if `self` is [`Self::Warp`]. + pub fn is_warp(&self) -> bool { + matches!(self, Self::Warp) + } + + /// Returns if `self` is [`Self::Fast`]. + pub fn is_fast(&self) -> bool { + matches!(self, Self::Fast { .. }) + } +} + +impl Default for SyncMode { + fn default() -> Self { + Self::Full + } +} + +/// Network service configuration. +#[derive(Clone, Debug)] +pub struct NetworkConfiguration { + /// Directory path to store network-specific configuration. None means nothing will be saved. + pub net_config_path: Option, + /// Multiaddresses to listen for incoming connections. + pub listen_addresses: Vec, + /// Multiaddresses to advertise. Detected automatically if empty. + pub public_addresses: Vec, + /// List of initial node addresses + pub boot_nodes: Vec, + /// The node key configuration, which determines the node's network identity keypair. + pub node_key: NodeKeyConfig, + /// List of request-response protocols that the node supports. + pub request_response_protocols: Vec, + /// Configuration for the default set of nodes used for block syncing and transactions. + pub default_peers_set: SetConfig, + /// Number of substreams to reserve for full nodes for block syncing and transactions. + /// Any other slot will be dedicated to light nodes. + /// + /// This value is implicitly capped to `default_set.out_peers + default_set.in_peers`. + pub default_peers_set_num_full: u32, + /// Configuration for extra sets of nodes. + pub extra_sets: Vec, + /// Client identifier. Sent over the wire for debugging purposes. + pub client_version: String, + /// Name of the node. Sent over the wire for debugging purposes. + pub node_name: String, + /// Configuration for the transport layer. + pub transport: TransportConfig, + /// Maximum number of peers to ask the same blocks in parallel. + pub max_parallel_downloads: u32, + /// Initial syncing mode. + pub sync_mode: SyncMode, + + /// True if Kademlia random discovery should be enabled. + /// + /// If true, the node will automatically randomly walk the DHT in order to find new peers. + pub enable_dht_random_walk: bool, + + /// Should we insert non-global addresses into the DHT? + pub allow_non_globals_in_dht: bool, + + /// Require iterative Kademlia DHT queries to use disjoint paths for increased resiliency in + /// the presence of potentially adversarial nodes. + pub kademlia_disjoint_query_paths: bool, + /// Enable serving block data over IPFS bitswap. + pub ipfs_server: bool, + + /// Size of Yamux receive window of all substreams. `None` for the default (256kiB). + /// Any value less than 256kiB is invalid. + /// + /// # Context + /// + /// By design, notifications substreams on top of Yamux connections only allow up to `N` bytes + /// to be transferred at a time, where `N` is the Yamux receive window size configurable here. + /// This means, in practice, that every `N` bytes must be acknowledged by the receiver before + /// the sender can send more data. The maximum bandwidth of each notifications substream is + /// therefore `N / round_trip_time`. + /// + /// It is recommended to leave this to `None`, and use a request-response protocol instead if + /// a large amount of data must be transferred. The reason why the value is configurable is + /// that some Substrate users mis-use notification protocols to send large amounts of data. + /// As such, this option isn't designed to stay and will likely get removed in the future. + /// + /// Note that configuring a value here isn't a modification of the Yamux protocol, but rather + /// a modification of the way the implementation works. Different nodes with different + /// configured values remain compatible with each other. + pub yamux_window_size: Option, +} + +impl NetworkConfiguration { + /// Create new default configuration + pub fn new, SV: Into>( + node_name: SN, + client_version: SV, + node_key: NodeKeyConfig, + net_config_path: Option, + ) -> Self { + let default_peers_set = SetConfig::default(); + Self { + net_config_path, + listen_addresses: Vec::new(), + public_addresses: Vec::new(), + boot_nodes: Vec::new(), + node_key, + request_response_protocols: Vec::new(), + default_peers_set_num_full: default_peers_set.in_peers + default_peers_set.out_peers, + default_peers_set, + extra_sets: Vec::new(), + client_version: client_version.into(), + node_name: node_name.into(), + transport: TransportConfig::Normal { enable_mdns: false, allow_private_ip: true }, + max_parallel_downloads: 5, + sync_mode: SyncMode::Full, + enable_dht_random_walk: true, + allow_non_globals_in_dht: false, + kademlia_disjoint_query_paths: false, + yamux_window_size: None, + ipfs_server: false, + } + } + + /// Create new default configuration for localhost-only connection with random port (useful for + /// testing) + pub fn new_local() -> NetworkConfiguration { + let mut config = + NetworkConfiguration::new("test-node", "test-client", Default::default(), None); + + config.listen_addresses = + vec![iter::once(multiaddr::Protocol::Ip4(Ipv4Addr::new(127, 0, 0, 1))) + .chain(iter::once(multiaddr::Protocol::Tcp(0))) + .collect()]; + + config.allow_non_globals_in_dht = true; + config + } + + /// Create new default configuration for localhost-only connection with random port (useful for + /// testing) + pub fn new_memory() -> NetworkConfiguration { + let mut config = + NetworkConfiguration::new("test-node", "test-client", Default::default(), None); + + config.listen_addresses = + vec![iter::once(multiaddr::Protocol::Ip4(Ipv4Addr::new(127, 0, 0, 1))) + .chain(iter::once(multiaddr::Protocol::Tcp(0))) + .collect()]; + + config.allow_non_globals_in_dht = true; + config + } +} + +/// The configuration of a node's secret key, describing the type of key +/// and how it is obtained. A node's identity keypair is the result of +/// the evaluation of the node key configuration. +#[derive(Clone, Debug)] +pub enum NodeKeyConfig { + /// A Ed25519 secret key configuration. + Ed25519(Secret), +} + +impl Default for NodeKeyConfig { + fn default() -> NodeKeyConfig { + Self::Ed25519(Secret::New) + } +} + +/// The options for obtaining a Ed25519 secret key. +pub type Ed25519Secret = Secret; + +/// The configuration options for obtaining a secret key `K`. +#[derive(Clone)] +pub enum Secret { + /// Use the given secret key `K`. + Input(K), + /// Read the secret key from a file. If the file does not exist, + /// it is created with a newly generated secret key `K`. The format + /// of the file is determined by `K`: + /// + /// * `ed25519::SecretKey`: An unencoded 32 bytes Ed25519 secret key. + File(PathBuf), + /// Always generate a new secret key `K`. + New, +} + +impl fmt::Debug for Secret { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + match self { + Self::Input(_) => f.debug_tuple("Secret::Input").finish(), + Self::File(path) => f.debug_tuple("Secret::File").field(path).finish(), + Self::New => f.debug_tuple("Secret::New").finish(), + } + } +} + +impl NodeKeyConfig { + /// Evaluate a `NodeKeyConfig` to obtain an identity `Keypair`: + /// + /// * If the secret is configured as input, the corresponding keypair is returned. + /// + /// * If the secret is configured as a file, it is read from that file, if it exists. Otherwise + /// a new secret is generated and stored. In either case, the keypair obtained from the + /// secret is returned. + /// + /// * If the secret is configured to be new, it is generated and the corresponding keypair is + /// returned. + pub fn into_keypair(self) -> io::Result { + use NodeKeyConfig::*; + match self { + Ed25519(Secret::New) => Ok(Keypair::generate_ed25519()), + + Ed25519(Secret::Input(k)) => Ok(Keypair::Ed25519(k.into())), + + Ed25519(Secret::File(f)) => get_secret( + f, + |mut b| match String::from_utf8(b.to_vec()).ok().and_then(|s| { + if s.len() == 64 { + array_bytes::hex2bytes(&s).ok() + } else { + None + } + }) { + Some(s) => ed25519::SecretKey::from_bytes(s), + _ => ed25519::SecretKey::from_bytes(&mut b), + }, + ed25519::SecretKey::generate, + |b| b.as_ref().to_vec(), + ) + .map(ed25519::Keypair::from) + .map(Keypair::Ed25519), + } + } +} + +/// Load a secret key from a file, if it exists, or generate a +/// new secret key and write it to that file. In either case, +/// the secret key is returned. +fn get_secret(file: P, parse: F, generate: G, serialize: W) -> io::Result +where + P: AsRef, + F: for<'r> FnOnce(&'r mut [u8]) -> Result, + G: FnOnce() -> K, + E: Error + Send + Sync + 'static, + W: Fn(&K) -> Vec, +{ + std::fs::read(&file) + .and_then(|mut sk_bytes| { + parse(&mut sk_bytes).map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e)) + }) + .or_else(|e| { + if e.kind() == io::ErrorKind::NotFound { + file.as_ref().parent().map_or(Ok(()), fs::create_dir_all)?; + let sk = generate(); + let mut sk_vec = serialize(&sk); + write_secret_file(file, &sk_vec)?; + sk_vec.zeroize(); + Ok(sk) + } else { + Err(e) + } + }) +} + +/// Write secret bytes to a file. +fn write_secret_file

(path: P, sk_bytes: &[u8]) -> io::Result<()> +where + P: AsRef, +{ + let mut file = open_secret_file(&path)?; + file.write_all(sk_bytes) +} + +/// Opens a file containing a secret key in write mode. +#[cfg(unix)] +fn open_secret_file

(path: P) -> io::Result +where + P: AsRef, +{ + use std::os::unix::fs::OpenOptionsExt; + fs::OpenOptions::new().write(true).create_new(true).mode(0o600).open(path) +} + +/// Opens a file containing a secret key in write mode. +#[cfg(not(unix))] +fn open_secret_file

(path: P) -> Result +where + P: AsRef, +{ + fs::OpenOptions::new().write(true).create_new(true).open(path) +} + +#[cfg(test)] +mod tests { + use super::*; + use tempfile::TempDir; + + fn tempdir_with_prefix(prefix: &str) -> TempDir { + tempfile::Builder::new().prefix(prefix).tempdir().unwrap() + } + + fn secret_bytes(kp: &Keypair) -> Vec { + let Keypair::Ed25519(p) = kp; + p.secret().as_ref().iter().cloned().collect() + } + + #[test] + fn test_secret_file() { + let tmp = tempdir_with_prefix("x"); + std::fs::remove_dir(tmp.path()).unwrap(); // should be recreated + let file = tmp.path().join("x").to_path_buf(); + let kp1 = NodeKeyConfig::Ed25519(Secret::File(file.clone())).into_keypair().unwrap(); + let kp2 = NodeKeyConfig::Ed25519(Secret::File(file.clone())).into_keypair().unwrap(); + assert!(file.is_file() && secret_bytes(&kp1) == secret_bytes(&kp2)) + } + + #[test] + fn test_secret_input() { + let sk = ed25519::SecretKey::generate(); + let kp1 = NodeKeyConfig::Ed25519(Secret::Input(sk.clone())).into_keypair().unwrap(); + let kp2 = NodeKeyConfig::Ed25519(Secret::Input(sk)).into_keypair().unwrap(); + assert!(secret_bytes(&kp1) == secret_bytes(&kp2)); + } + + #[test] + fn test_secret_new() { + let kp1 = NodeKeyConfig::Ed25519(Secret::New).into_keypair().unwrap(); + let kp2 = NodeKeyConfig::Ed25519(Secret::New).into_keypair().unwrap(); + assert!(secret_bytes(&kp1) != secret_bytes(&kp2)); + } +} diff --git a/client/network/common/src/protocol/event.rs b/client/network/common/src/protocol/event.rs index 7a14a2801..90c38b48c 100644 --- a/client/network/common/src/protocol/event.rs +++ b/client/network/common/src/protocol/event.rs @@ -48,18 +48,6 @@ pub enum Event { /// Event generated by a DHT. Dht(DhtEvent), - /// Now connected to a new peer for syncing purposes. - SyncConnected { - /// Node we are now syncing from. - remote: PeerId, - }, - - /// Now disconnected from a peer for syncing purposes. - SyncDisconnected { - /// Node we are no longer syncing from. - remote: PeerId, - }, - /// Opened a substream with the given node with the given notifications protocol. /// /// The protocol is always one of the notification protocols that have been registered. @@ -79,6 +67,8 @@ pub enum Event { negotiated_fallback: Option, /// Role of the remote. role: ObservedRole, + /// Received handshake. + received_handshake: Vec, }, /// Closed a substream with the given node. Always matches a corresponding previous diff --git a/client/network/common/src/service.rs b/client/network/common/src/service.rs index e96a00c40..d3c5c2f43 100644 --- a/client/network/common/src/service.rs +++ b/client/network/common/src/service.rs @@ -22,14 +22,12 @@ use crate::{ config::MultiaddrWithPeerId, protocol::{event::Event, ProtocolName}, request_responses::{IfDisconnected, RequestFailure}, - sync::{warp::WarpSyncProgress, StateDownloadProgress, SyncState}, }; use futures::{channel::oneshot, Stream}; pub use libp2p::{identity::error::SigningError, kad::record::Key as KademliaKey}; use libp2p::{Multiaddr, PeerId}; use sc_peerset::ReputationChange; pub use signature::Signature; -use sp_runtime::traits::{Block as BlockT, NumberFor}; use std::{collections::HashSet, future::Future, pin::Pin, sync::Arc}; mod signature; @@ -96,45 +94,33 @@ where /// Overview status of the network. #[derive(Clone)] -pub struct NetworkStatus { - /// Current global sync state. - pub sync_state: SyncState>, - /// Target sync block number. - pub best_seen_block: Option>, - /// Number of peers participating in syncing. - pub num_sync_peers: u32, - /// Total number of connected peers +pub struct NetworkStatus { + /// Total number of connected peers. pub num_connected_peers: usize, - /// Total number of active peers. - pub num_active_peers: usize, /// The total number of bytes received. pub total_bytes_inbound: u64, /// The total number of bytes sent. pub total_bytes_outbound: u64, - /// State sync in progress. - pub state_sync: Option, - /// Warp sync in progress. - pub warp_sync: Option>, } /// Provides high-level status information about network. #[async_trait::async_trait] -pub trait NetworkStatusProvider { +pub trait NetworkStatusProvider { /// High-level network status information. /// /// Returns an error if the `NetworkWorker` is no longer running. - async fn status(&self) -> Result, ()>; + async fn status(&self) -> Result; } // Manual implementation to avoid extra boxing here -impl NetworkStatusProvider for Arc +impl NetworkStatusProvider for Arc where T: ?Sized, - T: NetworkStatusProvider, + T: NetworkStatusProvider, { fn status<'life0, 'async_trait>( &'life0 self, - ) -> Pin, ()>> + Send + 'async_trait>> + ) -> Pin> + Send + 'async_trait>> where 'life0: 'async_trait, Self: 'async_trait, @@ -511,6 +497,9 @@ pub trait NetworkNotification { target: PeerId, protocol: ProtocolName, ) -> Result, NotificationSenderError>; + + /// Set handshake for the notification protocol. + fn set_notification_handshake(&self, protocol: ProtocolName, handshake: Vec); } impl NetworkNotification for Arc @@ -529,6 +518,10 @@ where ) -> Result, NotificationSenderError> { T::notification_sender(self, target, protocol) } + + fn set_notification_handshake(&self, protocol: ProtocolName, handshake: Vec) { + T::set_notification_handshake(self, protocol, handshake) + } } /// Provides ability to send network requests. diff --git a/client/network/common/src/sync.rs b/client/network/common/src/sync.rs index 6a98543d4..262da6c20 100644 --- a/client/network/common/src/sync.rs +++ b/client/network/common/src/sync.rs @@ -22,7 +22,11 @@ pub mod message; pub mod metrics; pub mod warp; +use crate::protocol::role::Roles; +use futures::Stream; + use libp2p::PeerId; + use message::{BlockAnnounce, BlockData, BlockRequest, BlockResponse}; use sc_consensus::{import_queue::RuntimeOrigin, IncomingBlock}; use sp_consensus::BlockOrigin; @@ -30,9 +34,10 @@ use sp_runtime::{ traits::{Block as BlockT, NumberFor}, Justifications, }; -use std::{any::Any, fmt, fmt::Formatter, task::Poll}; use warp::WarpSyncProgress; +use std::{any::Any, fmt, fmt::Formatter, pin::Pin, sync::Arc, task::Poll}; + /// The sync status of a peer we are trying to sync with #[derive(Debug)] pub struct PeerInfo { @@ -42,6 +47,17 @@ pub struct PeerInfo { pub best_number: NumberFor, } +/// Info about a peer's known state (both full and light). +#[derive(Clone, Debug)] +pub struct ExtendedPeerInfo { + /// Roles + pub roles: Roles, + /// Peer best block hash + pub best_hash: B::Hash, + /// Peer best block number + pub best_number: NumberFor, +} + /// Reported sync state. #[derive(Clone, Eq, PartialEq, Debug)] pub enum SyncState { @@ -251,6 +267,49 @@ impl fmt::Debug for OpaqueBlockResponse { } } +/// Provides high-level status of syncing. +#[async_trait::async_trait] +pub trait SyncStatusProvider: Send + Sync { + /// Get high-level view of the syncing status. + async fn status(&self) -> Result, ()>; +} + +#[async_trait::async_trait] +impl SyncStatusProvider for Arc +where + T: ?Sized, + T: SyncStatusProvider, + Block: BlockT, +{ + async fn status(&self) -> Result, ()> { + T::status(self).await + } +} + +/// Syncing-related events that other protocols can subscribe to. +pub enum SyncEvent { + /// Peer that the syncing implementation is tracking connected. + PeerConnected(PeerId), + + /// Peer that the syncing implementation was tracking disconnected. + PeerDisconnected(PeerId), +} + +pub trait SyncEventStream: Send + Sync { + /// Subscribe to syncing-related events. + fn event_stream(&self, name: &'static str) -> Pin + Send>>; +} + +impl SyncEventStream for Arc +where + T: ?Sized, + T: SyncEventStream, +{ + fn event_stream(&self, name: &'static str) -> Pin + Send>> { + T::event_stream(self, name) + } +} + /// Something that represents the syncing strategy to download past and future blocks of the chain. pub trait ChainSync: Send { /// Returns the state of the sync of the given peer. diff --git a/client/network/src/behaviour.rs b/client/network/src/behaviour.rs index 1ed8efbcd..559129020 100644 --- a/client/network/src/behaviour.rs +++ b/client/network/src/behaviour.rs @@ -41,7 +41,6 @@ use sc_network_common::{ request_responses::{IfDisconnected, ProtocolConfig, RequestFailure}, }; use sc_peerset::{PeersetHandle, ReputationChange}; -use sp_blockchain::HeaderBackend; use sp_runtime::traits::Block as BlockT; use std::{collections::HashSet, time::Duration}; @@ -50,13 +49,9 @@ pub use crate::request_responses::{InboundFailure, OutboundFailure, RequestId, R /// General behaviour of the network. Combines all protocols together. #[derive(NetworkBehaviour)] #[behaviour(out_event = "BehaviourOut")] -pub struct Behaviour -where - B: BlockT, - Client: HeaderBackend + 'static, -{ +pub struct Behaviour { /// All the substrate-specific protocols. - substrate: Protocol, + substrate: Protocol, /// Periodically pings and identifies the nodes we are connected to, and store information in a /// cache. peer_info: peer_info::PeerInfoBehaviour, @@ -118,6 +113,8 @@ pub enum BehaviourOut { notifications_sink: NotificationsSink, /// Role of the remote. role: ObservedRole, + /// Received handshake. + received_handshake: Vec, }, /// The [`NotificationsSink`] object used to send notifications with the given peer must be @@ -151,12 +148,6 @@ pub enum BehaviourOut { messages: Vec<(ProtocolName, Bytes)>, }, - /// Now connected to a new peer for syncing purposes. - SyncConnected(PeerId), - - /// No longer connected to a peer for syncing purposes. - SyncDisconnected(PeerId), - /// We have obtained identity information from a peer, including the addresses it is listening /// on. PeerIdentify { @@ -177,14 +168,10 @@ pub enum BehaviourOut { None, } -impl Behaviour -where - B: BlockT, - Client: HeaderBackend + 'static, -{ +impl Behaviour { /// Builds a new `Behaviour`. pub fn new( - substrate: Protocol, + substrate: Protocol, user_agent: String, local_public_key: PublicKey, disco_config: DiscoveryConfig, @@ -252,12 +239,12 @@ where } /// Returns a shared reference to the user protocol. - pub fn user_protocol(&self) -> &Protocol { + pub fn user_protocol(&self) -> &Protocol { &self.substrate } /// Returns a mutable reference to the user protocol. - pub fn user_protocol_mut(&mut self) -> &mut Protocol { + pub fn user_protocol_mut(&mut self) -> &mut Protocol { &mut self.substrate } @@ -295,20 +282,22 @@ fn reported_roles_to_observed_role(roles: Roles) -> ObservedRole { } } -impl From> for BehaviourOut { - fn from(event: CustomMessageOutcome) -> Self { +impl From for BehaviourOut { + fn from(event: CustomMessageOutcome) -> Self { match event { CustomMessageOutcome::NotificationStreamOpened { remote, protocol, negotiated_fallback, roles, + received_handshake, notifications_sink, } => BehaviourOut::NotificationStreamOpened { remote, protocol, negotiated_fallback, role: reported_roles_to_observed_role(roles), + received_handshake, notifications_sink, }, CustomMessageOutcome::NotificationStreamReplaced { @@ -320,10 +309,6 @@ impl From> for BehaviourOut { BehaviourOut::NotificationStreamClosed { remote, protocol }, CustomMessageOutcome::NotificationsReceived { remote, messages } => BehaviourOut::NotificationsReceived { remote, messages }, - CustomMessageOutcome::PeerNewBest(_peer_id, _number) => BehaviourOut::None, - CustomMessageOutcome::SyncConnected(peer_id) => BehaviourOut::SyncConnected(peer_id), - CustomMessageOutcome::SyncDisconnected(peer_id) => - BehaviourOut::SyncDisconnected(peer_id), CustomMessageOutcome::None => BehaviourOut::None, } } diff --git a/client/network/src/config.rs b/client/network/src/config.rs index fc5304779..90d02af84 100644 --- a/client/network/src/config.rs +++ b/client/network/src/config.rs @@ -22,7 +22,7 @@ //! See the documentation of [`Params`]. pub use sc_network_common::{ - config::ProtocolId, + config::{NetworkConfiguration, ProtocolId}, protocol::role::Role, request_responses::{ IncomingRequest, OutgoingResponse, ProtocolConfig as RequestResponseConfig, @@ -33,35 +33,12 @@ pub use sc_network_common::{ pub use libp2p::{build_multiaddr, core::PublicKey, identity}; -use crate::ChainSyncInterface; -use core::{fmt, iter}; -use libp2p::{ - identity::{ed25519, Keypair}, - multiaddr, Multiaddr, -}; use prometheus_endpoint::Registry; -use sc_network_common::{ - config::{MultiaddrWithPeerId, NonDefaultSetConfig, SetConfig, TransportConfig}, - sync::ChainSync, -}; -use sp_runtime::traits::Block as BlockT; -use std::{ - error::Error, - fs, - future::Future, - io::{self, Write}, - net::Ipv4Addr, - path::{Path, PathBuf}, - pin::Pin, - sync::Arc, -}; -use zeroize::Zeroize; +use sc_network_common::config::NonDefaultSetConfig; +use std::{future::Future, pin::Pin, sync::Arc}; /// Network initialization parameters. -pub struct Params -where - B: BlockT + 'static, -{ +pub struct Params { /// Assigned role for our node (full, light, ...). pub role: Role, @@ -81,12 +58,6 @@ where /// name on the wire. pub fork_id: Option, - /// Instance of chain sync implementation. - pub chain_sync: Box>, - - /// Interface that can be used to delegate syncing-related function calls to `ChainSync` - pub chain_sync_service: Box>, - /// Registry for recording prometheus metrics to. pub metrics_registry: Option, @@ -96,350 +67,3 @@ where /// Request response protocol configurations pub request_response_protocol_configs: Vec, } - -/// Sync operation mode. -#[derive(Copy, Clone, Debug, Eq, PartialEq)] -pub enum SyncMode { - /// Full block download and verification. - Full, - /// Download blocks and the latest state. - Fast { - /// Skip state proof download and verification. - skip_proofs: bool, - /// Download indexed transactions for recent blocks. - storage_chain_mode: bool, - }, - /// Warp sync - verify authority set transitions and the latest state. - Warp, -} - -impl SyncMode { - /// Returns if `self` is [`Self::Warp`]. - pub fn is_warp(&self) -> bool { - matches!(self, Self::Warp) - } - - /// Returns if `self` is [`Self::Fast`]. - pub fn is_fast(&self) -> bool { - matches!(self, Self::Fast { .. }) - } -} - -impl Default for SyncMode { - fn default() -> Self { - Self::Full - } -} - -/// Network service configuration. -#[derive(Clone, Debug)] -pub struct NetworkConfiguration { - /// Directory path to store network-specific configuration. None means nothing will be saved. - pub net_config_path: Option, - /// Multiaddresses to listen for incoming connections. - pub listen_addresses: Vec, - /// Multiaddresses to advertise. Detected automatically if empty. - pub public_addresses: Vec, - /// List of initial node addresses - pub boot_nodes: Vec, - /// The node key configuration, which determines the node's network identity keypair. - pub node_key: NodeKeyConfig, - /// List of request-response protocols that the node supports. - pub request_response_protocols: Vec, - /// Configuration for the default set of nodes used for block syncing and transactions. - pub default_peers_set: SetConfig, - /// Number of substreams to reserve for full nodes for block syncing and transactions. - /// Any other slot will be dedicated to light nodes. - /// - /// This value is implicitly capped to `default_set.out_peers + default_set.in_peers`. - pub default_peers_set_num_full: u32, - /// Configuration for extra sets of nodes. - pub extra_sets: Vec, - /// Client identifier. Sent over the wire for debugging purposes. - pub client_version: String, - /// Name of the node. Sent over the wire for debugging purposes. - pub node_name: String, - /// Configuration for the transport layer. - pub transport: TransportConfig, - /// Maximum number of peers to ask the same blocks in parallel. - pub max_parallel_downloads: u32, - /// Initial syncing mode. - pub sync_mode: SyncMode, - - /// True if Kademlia random discovery should be enabled. - /// - /// If true, the node will automatically randomly walk the DHT in order to find new peers. - pub enable_dht_random_walk: bool, - - /// Should we insert non-global addresses into the DHT? - pub allow_non_globals_in_dht: bool, - - /// Require iterative Kademlia DHT queries to use disjoint paths for increased resiliency in - /// the presence of potentially adversarial nodes. - pub kademlia_disjoint_query_paths: bool, - /// Enable serving block data over IPFS bitswap. - pub ipfs_server: bool, - - /// Size of Yamux receive window of all substreams. `None` for the default (256kiB). - /// Any value less than 256kiB is invalid. - /// - /// # Context - /// - /// By design, notifications substreams on top of Yamux connections only allow up to `N` bytes - /// to be transferred at a time, where `N` is the Yamux receive window size configurable here. - /// This means, in practice, that every `N` bytes must be acknowledged by the receiver before - /// the sender can send more data. The maximum bandwidth of each notifications substream is - /// therefore `N / round_trip_time`. - /// - /// It is recommended to leave this to `None`, and use a request-response protocol instead if - /// a large amount of data must be transferred. The reason why the value is configurable is - /// that some Substrate users mis-use notification protocols to send large amounts of data. - /// As such, this option isn't designed to stay and will likely get removed in the future. - /// - /// Note that configuring a value here isn't a modification of the Yamux protocol, but rather - /// a modification of the way the implementation works. Different nodes with different - /// configured values remain compatible with each other. - pub yamux_window_size: Option, -} - -impl NetworkConfiguration { - /// Create new default configuration - pub fn new, SV: Into>( - node_name: SN, - client_version: SV, - node_key: NodeKeyConfig, - net_config_path: Option, - ) -> Self { - let default_peers_set = SetConfig::default(); - Self { - net_config_path, - listen_addresses: Vec::new(), - public_addresses: Vec::new(), - boot_nodes: Vec::new(), - node_key, - request_response_protocols: Vec::new(), - default_peers_set_num_full: default_peers_set.in_peers + default_peers_set.out_peers, - default_peers_set, - extra_sets: Vec::new(), - client_version: client_version.into(), - node_name: node_name.into(), - transport: TransportConfig::Normal { enable_mdns: false, allow_private_ip: true }, - max_parallel_downloads: 5, - sync_mode: SyncMode::Full, - enable_dht_random_walk: true, - allow_non_globals_in_dht: false, - kademlia_disjoint_query_paths: false, - yamux_window_size: None, - ipfs_server: false, - } - } - - /// Create new default configuration for localhost-only connection with random port (useful for - /// testing) - pub fn new_local() -> NetworkConfiguration { - let mut config = - NetworkConfiguration::new("test-node", "test-client", Default::default(), None); - - config.listen_addresses = - vec![iter::once(multiaddr::Protocol::Ip4(Ipv4Addr::new(127, 0, 0, 1))) - .chain(iter::once(multiaddr::Protocol::Tcp(0))) - .collect()]; - - config.allow_non_globals_in_dht = true; - config - } - - /// Create new default configuration for localhost-only connection with random port (useful for - /// testing) - pub fn new_memory() -> NetworkConfiguration { - let mut config = - NetworkConfiguration::new("test-node", "test-client", Default::default(), None); - - config.listen_addresses = - vec![iter::once(multiaddr::Protocol::Ip4(Ipv4Addr::new(127, 0, 0, 1))) - .chain(iter::once(multiaddr::Protocol::Tcp(0))) - .collect()]; - - config.allow_non_globals_in_dht = true; - config - } -} - -/// The configuration of a node's secret key, describing the type of key -/// and how it is obtained. A node's identity keypair is the result of -/// the evaluation of the node key configuration. -#[derive(Clone, Debug)] -pub enum NodeKeyConfig { - /// A Ed25519 secret key configuration. - Ed25519(Secret), -} - -impl Default for NodeKeyConfig { - fn default() -> NodeKeyConfig { - Self::Ed25519(Secret::New) - } -} - -/// The options for obtaining a Ed25519 secret key. -pub type Ed25519Secret = Secret; - -/// The configuration options for obtaining a secret key `K`. -#[derive(Clone)] -pub enum Secret { - /// Use the given secret key `K`. - Input(K), - /// Read the secret key from a file. If the file does not exist, - /// it is created with a newly generated secret key `K`. The format - /// of the file is determined by `K`: - /// - /// * `ed25519::SecretKey`: An unencoded 32 bytes Ed25519 secret key. - File(PathBuf), - /// Always generate a new secret key `K`. - New, -} - -impl fmt::Debug for Secret { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - match self { - Self::Input(_) => f.debug_tuple("Secret::Input").finish(), - Self::File(path) => f.debug_tuple("Secret::File").field(path).finish(), - Self::New => f.debug_tuple("Secret::New").finish(), - } - } -} - -impl NodeKeyConfig { - /// Evaluate a `NodeKeyConfig` to obtain an identity `Keypair`: - /// - /// * If the secret is configured as input, the corresponding keypair is returned. - /// - /// * If the secret is configured as a file, it is read from that file, if it exists. Otherwise - /// a new secret is generated and stored. In either case, the keypair obtained from the - /// secret is returned. - /// - /// * If the secret is configured to be new, it is generated and the corresponding keypair is - /// returned. - pub fn into_keypair(self) -> io::Result { - use NodeKeyConfig::*; - match self { - Ed25519(Secret::New) => Ok(Keypair::generate_ed25519()), - - Ed25519(Secret::Input(k)) => Ok(Keypair::Ed25519(k.into())), - - Ed25519(Secret::File(f)) => get_secret( - f, - |mut b| match String::from_utf8(b.to_vec()).ok().and_then(|s| { - if s.len() == 64 { - array_bytes::hex2bytes(&s).ok() - } else { - None - } - }) { - Some(s) => ed25519::SecretKey::from_bytes(s), - _ => ed25519::SecretKey::from_bytes(&mut b), - }, - ed25519::SecretKey::generate, - |b| b.as_ref().to_vec(), - ) - .map(ed25519::Keypair::from) - .map(Keypair::Ed25519), - } - } -} - -/// Load a secret key from a file, if it exists, or generate a -/// new secret key and write it to that file. In either case, -/// the secret key is returned. -fn get_secret(file: P, parse: F, generate: G, serialize: W) -> io::Result -where - P: AsRef, - F: for<'r> FnOnce(&'r mut [u8]) -> Result, - G: FnOnce() -> K, - E: Error + Send + Sync + 'static, - W: Fn(&K) -> Vec, -{ - std::fs::read(&file) - .and_then(|mut sk_bytes| { - parse(&mut sk_bytes).map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e)) - }) - .or_else(|e| { - if e.kind() == io::ErrorKind::NotFound { - file.as_ref().parent().map_or(Ok(()), fs::create_dir_all)?; - let sk = generate(); - let mut sk_vec = serialize(&sk); - write_secret_file(file, &sk_vec)?; - sk_vec.zeroize(); - Ok(sk) - } else { - Err(e) - } - }) -} - -/// Write secret bytes to a file. -fn write_secret_file

(path: P, sk_bytes: &[u8]) -> io::Result<()> -where - P: AsRef, -{ - let mut file = open_secret_file(&path)?; - file.write_all(sk_bytes) -} - -/// Opens a file containing a secret key in write mode. -#[cfg(unix)] -fn open_secret_file

(path: P) -> io::Result -where - P: AsRef, -{ - use std::os::unix::fs::OpenOptionsExt; - fs::OpenOptions::new().write(true).create_new(true).mode(0o600).open(path) -} - -/// Opens a file containing a secret key in write mode. -#[cfg(not(unix))] -fn open_secret_file

(path: P) -> Result -where - P: AsRef, -{ - fs::OpenOptions::new().write(true).create_new(true).open(path) -} - -#[cfg(test)] -mod tests { - use super::*; - use tempfile::TempDir; - - fn tempdir_with_prefix(prefix: &str) -> TempDir { - tempfile::Builder::new().prefix(prefix).tempdir().unwrap() - } - - fn secret_bytes(kp: &Keypair) -> Vec { - let Keypair::Ed25519(p) = kp; - p.secret().as_ref().iter().cloned().collect() - } - - #[test] - fn test_secret_file() { - let tmp = tempdir_with_prefix("x"); - std::fs::remove_dir(tmp.path()).unwrap(); // should be recreated - let file = tmp.path().join("x").to_path_buf(); - let kp1 = NodeKeyConfig::Ed25519(Secret::File(file.clone())).into_keypair().unwrap(); - let kp2 = NodeKeyConfig::Ed25519(Secret::File(file.clone())).into_keypair().unwrap(); - assert!(file.is_file() && secret_bytes(&kp1) == secret_bytes(&kp2)) - } - - #[test] - fn test_secret_input() { - let sk = ed25519::SecretKey::generate(); - let kp1 = NodeKeyConfig::Ed25519(Secret::Input(sk.clone())).into_keypair().unwrap(); - let kp2 = NodeKeyConfig::Ed25519(Secret::Input(sk)).into_keypair().unwrap(); - assert!(secret_bytes(&kp1) == secret_bytes(&kp2)); - } - - #[test] - fn test_secret_new() { - let kp1 = NodeKeyConfig::Ed25519(Secret::New).into_keypair().unwrap(); - let kp2 = NodeKeyConfig::Ed25519(Secret::New).into_keypair().unwrap(); - assert!(secret_bytes(&kp1) != secret_bytes(&kp2)); - } -} diff --git a/client/network/src/lib.rs b/client/network/src/lib.rs index 8714d66f1..f94a71681 100644 --- a/client/network/src/lib.rs +++ b/client/network/src/lib.rs @@ -257,8 +257,6 @@ pub mod network_state; #[doc(inline)] pub use libp2p::{multiaddr, Multiaddr, PeerId}; -pub use protocol::PeerInfo; -use sc_consensus::{JustificationSyncLink, Link}; pub use sc_network_common::{ protocol::{ event::{DhtEvent, Event}, @@ -273,14 +271,13 @@ pub use sc_network_common::{ }, sync::{ warp::{WarpSyncPhase, WarpSyncProgress}, - StateDownloadProgress, SyncState, + ExtendedPeerInfo, StateDownloadProgress, SyncEventStream, SyncState, SyncStatusProvider, }, }; pub use service::{ DecodingError, Keypair, NetworkService, NetworkWorker, NotificationSender, NotificationSenderReady, OutboundFailure, PublicKey, }; -use sp_runtime::traits::{Block as BlockT, NumberFor}; pub use sc_peerset::ReputationChange; @@ -295,18 +292,3 @@ const MAX_CONNECTIONS_PER_PEER: usize = 2; /// The maximum number of concurrent established connections that were incoming. const MAX_CONNECTIONS_ESTABLISHED_INCOMING: u32 = 10_000; - -/// Abstraction over syncing-related services -pub trait ChainSyncInterface: - NetworkSyncForkRequest> + JustificationSyncLink + Link + Send + Sync -{ -} - -impl ChainSyncInterface for T where - T: NetworkSyncForkRequest> - + JustificationSyncLink - + Link - + Send - + Sync -{ -} diff --git a/client/network/src/protocol.rs b/client/network/src/protocol.rs index 2443fce81..8d6041242 100644 --- a/client/network/src/protocol.rs +++ b/client/network/src/protocol.rs @@ -19,8 +19,7 @@ use crate::config; use bytes::Bytes; -use codec::{Decode, DecodeAll, Encode}; -use futures::prelude::*; +use codec::{DecodeAll, Encode}; use libp2p::{ core::connection::ConnectionId, swarm::{ @@ -29,32 +28,20 @@ use libp2p::{ }, Multiaddr, PeerId, }; -use log::{debug, error, log, trace, warn, Level}; -use lru::LruCache; +use log::{debug, error, warn}; use message::{generic::Message as GenericMessage, Message}; use notifications::{Notifications, NotificationsOut}; -use prometheus_endpoint::{register, Gauge, GaugeVec, Opts, PrometheusError, Registry, U64}; -use sc_client_api::HeaderBackend; use sc_network_common::{ config::NonReservedPeerMode, error, protocol::{role::Roles, ProtocolName}, - sync::{ - message::{BlockAnnounce, BlockAnnouncesHandshake, BlockData, BlockResponse, BlockState}, - BadPeer, ChainSync, PollBlockAnnounceValidation, SyncStatus, - }, - utils::{interval, LruHashSet}, + sync::message::BlockAnnouncesHandshake, }; -use sp_arithmetic::traits::SaturatedConversion; -use sp_runtime::traits::{Block as BlockT, CheckedSub, Header as HeaderT, NumberFor, Zero}; +use sp_runtime::traits::Block as BlockT; use std::{ collections::{HashMap, HashSet, VecDeque}, iter, - num::NonZeroUsize, - pin::Pin, - sync::Arc, task::Poll, - time, }; mod notifications; @@ -63,12 +50,6 @@ pub mod message; pub use notifications::{NotificationsSink, NotifsHandlerError, Ready}; -/// Interval at which we perform time based maintenance -const TICK_TIMEOUT: time::Duration = time::Duration::from_millis(1100); - -/// Maximum number of known block hashes to keep for a peer. -const MAX_KNOWN_BLOCKS: usize = 1024; // ~32kb per peer + LruHashSet overhead - /// Maximum size used for notifications in the block announce and transaction protocols. // Must be equal to `max(MAX_BLOCK_ANNOUNCE_SIZE, MAX_TRANSACTIONS_SIZE)`. pub(crate) const BLOCK_ANNOUNCES_TRANSACTIONS_SUBSTREAM_SIZE: u64 = 16 * 1024 * 1024; @@ -79,88 +60,16 @@ const HARDCODED_PEERSETS_SYNC: sc_peerset::SetId = sc_peerset::SetId::from(0); /// superior to this value corresponds to a user-defined protocol. const NUM_HARDCODED_PEERSETS: usize = 1; -/// When light node connects to the full node and the full node is behind light node -/// for at least `LIGHT_MAXIMAL_BLOCKS_DIFFERENCE` blocks, we consider it not useful -/// and disconnect to free connection slot. -const LIGHT_MAXIMAL_BLOCKS_DIFFERENCE: u64 = 8192; - mod rep { use sc_peerset::ReputationChange as Rep; - /// Reputation change when we are a light client and a peer is behind us. - pub const PEER_BEHIND_US_LIGHT: Rep = Rep::new(-(1 << 8), "Useless for a light peer"); /// We received a message that failed to decode. pub const BAD_MESSAGE: Rep = Rep::new(-(1 << 12), "Bad message"); - /// Peer has different genesis. - pub const GENESIS_MISMATCH: Rep = Rep::new_fatal("Genesis mismatch"); - /// Peer role does not match (e.g. light peer connecting to another light peer). - pub const BAD_ROLE: Rep = Rep::new_fatal("Unsupported role"); - /// Peer send us a block announcement that failed at validation. - pub const BAD_BLOCK_ANNOUNCEMENT: Rep = Rep::new(-(1 << 12), "Bad block announcement"); -} - -struct Metrics { - peers: Gauge, - queued_blocks: Gauge, - fork_targets: Gauge, - justifications: GaugeVec, -} - -impl Metrics { - fn register(r: &Registry) -> Result { - Ok(Self { - peers: { - let g = Gauge::new("substrate_sync_peers", "Number of peers we sync with")?; - register(g, r)? - }, - queued_blocks: { - let g = - Gauge::new("substrate_sync_queued_blocks", "Number of blocks in import queue")?; - register(g, r)? - }, - fork_targets: { - let g = Gauge::new("substrate_sync_fork_targets", "Number of fork sync targets")?; - register(g, r)? - }, - justifications: { - let g = GaugeVec::new( - Opts::new( - "substrate_sync_extra_justifications", - "Number of extra justifications requests", - ), - &["status"], - )?; - register(g, r)? - }, - }) - } } // Lock must always be taken in order declared here. -pub struct Protocol { - /// Interval at which we call `tick`. - tick_timeout: Pin + Send>>, +pub struct Protocol { /// Pending list of messages to return from `poll` as a priority. - pending_messages: VecDeque>, - /// Assigned roles. - roles: Roles, - genesis_hash: B::Hash, - /// State machine that handles the list of in-progress requests. Only full node peers are - /// registered. - chain_sync: Box>, - // All connected peers. Contains both full and light node peers. - peers: HashMap>, - chain: Arc, - /// List of nodes for which we perform additional logging because they are important for the - /// user. - important_peers: HashSet, - /// List of nodes that should never occupy peer slots. - default_peers_set_no_slot_peers: HashSet, - /// Actual list of connected no-slot nodes. - default_peers_set_no_slot_connected_peers: HashSet, - /// Value that was passed as part of the configuration. Used to cap the number of full nodes. - default_peers_set_num_full: usize, - /// Number of slots to allocate to light nodes. - default_peers_set_num_light: usize, + pending_messages: VecDeque, /// Used to report reputation changes. peerset_handle: sc_peerset::PeersetHandle, /// Handles opening the unique substream and sending and receiving raw messages. @@ -174,85 +83,18 @@ pub struct Protocol { /// solve this, an entry is added to this map whenever an invalid handshake is received. /// Entries are removed when the corresponding "substream closed" is later received. bad_handshake_substreams: HashSet<(PeerId, sc_peerset::SetId)>, - /// Prometheus metrics. - metrics: Option, - /// The `PeerId`'s of all boot nodes. - boot_node_ids: HashSet, - /// A cache for the data that was associated to a block announcement. - block_announce_data_cache: LruCache>, + /// Connected peers. + peers: HashMap, + _marker: std::marker::PhantomData, } -/// Peer information -#[derive(Debug)] -struct Peer { - info: PeerInfo, - /// Holds a set of blocks known to this peer. - known_blocks: LruHashSet, -} - -/// Info about a peer's known state. -#[derive(Clone, Debug)] -pub struct PeerInfo { - /// Roles - pub roles: Roles, - /// Peer best block hash - pub best_hash: B::Hash, - /// Peer best block number - pub best_number: ::Number, -} - -impl Protocol -where - B: BlockT, - Client: HeaderBackend + 'static, -{ +impl Protocol { /// Create a new instance. pub fn new( roles: Roles, - chain: Arc, network_config: &config::NetworkConfiguration, - metrics_registry: Option<&Registry>, - chain_sync: Box>, block_announces_protocol: sc_network_common::config::NonDefaultSetConfig, ) -> error::Result<(Self, sc_peerset::PeersetHandle, Vec<(PeerId, Multiaddr)>)> { - let info = chain.info(); - - let boot_node_ids = { - let mut list = HashSet::new(); - for node in &network_config.boot_nodes { - list.insert(node.peer_id); - } - list.shrink_to_fit(); - list - }; - - let important_peers = { - let mut imp_p = HashSet::new(); - for reserved in &network_config.default_peers_set.reserved_nodes { - imp_p.insert(reserved.peer_id); - } - for reserved in network_config - .extra_sets - .iter() - .flat_map(|s| s.set_config.reserved_nodes.iter()) - { - imp_p.insert(reserved.peer_id); - } - imp_p.shrink_to_fit(); - imp_p - }; - - let default_peers_set_no_slot_peers = { - let mut no_slot_p: HashSet = network_config - .default_peers_set - .reserved_nodes - .iter() - .map(|reserved| reserved.peer_id) - .collect(); - no_slot_p.shrink_to_fit(); - no_slot_p - }; - let mut known_addresses = Vec::new(); let (peerset, peerset_handle) = { @@ -326,44 +168,17 @@ where ) }; - let cache_capacity = NonZeroUsize::new( - (network_config.default_peers_set.in_peers as usize + - network_config.default_peers_set.out_peers as usize) - .max(1), - ) - .expect("cache capacity is not zero"); - let block_announce_data_cache = LruCache::new(cache_capacity); - let protocol = Self { - tick_timeout: Box::pin(interval(TICK_TIMEOUT)), pending_messages: VecDeque::new(), - roles, - peers: HashMap::new(), - chain, - genesis_hash: info.genesis_hash, - chain_sync, - important_peers, - default_peers_set_no_slot_peers, - default_peers_set_no_slot_connected_peers: HashSet::new(), - default_peers_set_num_full: network_config.default_peers_set_num_full as usize, - default_peers_set_num_light: { - let total = network_config.default_peers_set.out_peers + - network_config.default_peers_set.in_peers; - total.saturating_sub(network_config.default_peers_set_num_full) as usize - }, peerset_handle: peerset_handle.clone(), behaviour, notification_protocols: iter::once(block_announces_protocol.notifications_protocol) .chain(network_config.extra_sets.iter().map(|s| s.notifications_protocol.clone())) .collect(), bad_handshake_substreams: Default::default(), - metrics: if let Some(r) = metrics_registry { - Some(Metrics::register(r)?) - } else { - None - }, - boot_node_ids, - block_announce_data_cache, + peers: HashMap::new(), + // TODO: remove when `BlockAnnouncesHandshake` is moved away from `Protocol` + _marker: Default::default(), }; Ok((protocol, peerset_handle, known_addresses)) @@ -384,6 +199,7 @@ where if let Some(position) = self.notification_protocols.iter().position(|p| *p == protocol_name) { self.behaviour.disconnect_peer(peer_id, sc_peerset::SetId::from(position)); + self.peers.remove(peer_id); } else { warn!(target: "sub-libp2p", "disconnect_peer() with invalid protocol name") } @@ -399,391 +215,23 @@ where self.peers.len() } - /// Returns the number of peers we're connected to and that are being queried. - pub fn num_active_peers(&self) -> usize { - self.chain_sync.num_active_peers() - } - - /// Current global sync state. - pub fn sync_state(&self) -> SyncStatus { - self.chain_sync.status() - } - - /// Target sync block number. - pub fn best_seen_block(&self) -> Option> { - self.chain_sync.status().best_seen_block - } - - /// Number of peers participating in syncing. - pub fn num_sync_peers(&self) -> u32 { - self.chain_sync.status().num_peers - } - - /// Number of blocks in the import queue. - pub fn num_queued_blocks(&self) -> u32 { - self.chain_sync.status().queued_blocks - } - - /// Number of downloaded blocks. - pub fn num_downloaded_blocks(&self) -> usize { - self.chain_sync.num_downloaded_blocks() - } - - /// Number of active sync requests. - pub fn num_sync_requests(&self) -> usize { - self.chain_sync.num_sync_requests() - } - - /// Inform sync about new best imported block. - pub fn new_best_block_imported(&mut self, hash: B::Hash, number: NumberFor) { - debug!(target: "sync", "New best block imported {:?}/#{}", hash, number); - - self.chain_sync.update_chain_info(&hash, number); - - self.behaviour.set_notif_protocol_handshake( - HARDCODED_PEERSETS_SYNC, - BlockAnnouncesHandshake::::build(self.roles, number, hash, self.genesis_hash) - .encode(), - ); - } - - fn update_peer_info(&mut self, who: &PeerId) { - if let Some(info) = self.chain_sync.peer_info(who) { - if let Some(ref mut peer) = self.peers.get_mut(who) { - peer.info.best_hash = info.best_hash; - peer.info.best_number = info.best_number; - } - } - } - - /// Returns information about all the peers we are connected to after the handshake message. - pub fn peers_info(&self) -> impl Iterator)> { - self.peers.iter().map(|(id, peer)| (id, &peer.info)) - } - - /// Called by peer when it is disconnecting. - /// - /// Returns a result if the handshake of this peer was indeed accepted. - pub fn on_sync_peer_disconnected(&mut self, peer: PeerId) -> Result<(), ()> { - if self.important_peers.contains(&peer) { - warn!(target: "sync", "Reserved peer {} disconnected", peer); - } else { - debug!(target: "sync", "{} disconnected", peer); - } - - if let Some(_peer_data) = self.peers.remove(&peer) { - self.chain_sync.peer_disconnected(&peer); - self.default_peers_set_no_slot_connected_peers.remove(&peer); - Ok(()) - } else { - Err(()) - } - } - /// Adjusts the reputation of a node. pub fn report_peer(&self, who: PeerId, reputation: sc_peerset::ReputationChange) { self.peerset_handle.report_peer(who, reputation) } - /// Perform time based maintenance. - /// - /// > **Note**: This method normally doesn't have to be called except for testing purposes. - pub fn tick(&mut self) { - self.report_metrics() - } - - /// Called on the first connection between two peers on the default set, after their exchange - /// of handshake. - /// - /// Returns `Ok` if the handshake is accepted and the peer added to the list of peers we sync - /// from. - fn on_sync_peer_connected( - &mut self, - who: PeerId, - status: BlockAnnouncesHandshake, - ) -> Result<(), ()> { - trace!(target: "sync", "New peer {} {:?}", who, status); - - if self.peers.contains_key(&who) { - error!(target: "sync", "Called on_sync_peer_connected with already connected peer {}", who); - debug_assert!(false); - return Err(()) - } - - if status.genesis_hash != self.genesis_hash { - log!( - target: "sync", - if self.important_peers.contains(&who) { Level::Warn } else { Level::Debug }, - "Peer is on different chain (our genesis: {} theirs: {})", - self.genesis_hash, status.genesis_hash - ); - self.peerset_handle.report_peer(who, rep::GENESIS_MISMATCH); - self.behaviour.disconnect_peer(&who, HARDCODED_PEERSETS_SYNC); - - if self.boot_node_ids.contains(&who) { - error!( - target: "sync", - "Bootnode with peer id `{}` is on a different chain (our genesis: {} theirs: {})", - who, - self.genesis_hash, - status.genesis_hash, - ); - } - - return Err(()) - } - - if self.roles.is_light() { - // we're not interested in light peers - if status.roles.is_light() { - debug!(target: "sync", "Peer {} is unable to serve light requests", who); - self.peerset_handle.report_peer(who, rep::BAD_ROLE); - self.behaviour.disconnect_peer(&who, HARDCODED_PEERSETS_SYNC); - return Err(()) - } - - // we don't interested in peers that are far behind us - let self_best_block = self.chain.info().best_number; - let blocks_difference = self_best_block - .checked_sub(&status.best_number) - .unwrap_or_else(Zero::zero) - .saturated_into::(); - if blocks_difference > LIGHT_MAXIMAL_BLOCKS_DIFFERENCE { - debug!(target: "sync", "Peer {} is far behind us and will unable to serve light requests", who); - self.peerset_handle.report_peer(who, rep::PEER_BEHIND_US_LIGHT); - self.behaviour.disconnect_peer(&who, HARDCODED_PEERSETS_SYNC); - return Err(()) - } - } - - let no_slot_peer = self.default_peers_set_no_slot_peers.contains(&who); - let this_peer_reserved_slot: usize = if no_slot_peer { 1 } else { 0 }; - - if status.roles.is_full() && - self.chain_sync.num_peers() >= - self.default_peers_set_num_full + - self.default_peers_set_no_slot_connected_peers.len() + - this_peer_reserved_slot - { - debug!(target: "sync", "Too many full nodes, rejecting {}", who); - self.behaviour.disconnect_peer(&who, HARDCODED_PEERSETS_SYNC); - return Err(()) - } - - if status.roles.is_light() && - (self.peers.len() - self.chain_sync.num_peers()) >= self.default_peers_set_num_light - { - // Make sure that not all slots are occupied by light clients. - debug!(target: "sync", "Too many light nodes, rejecting {}", who); - self.behaviour.disconnect_peer(&who, HARDCODED_PEERSETS_SYNC); - return Err(()) - } - - let peer = Peer { - info: PeerInfo { - roles: status.roles, - best_hash: status.best_hash, - best_number: status.best_number, - }, - known_blocks: LruHashSet::new( - NonZeroUsize::new(MAX_KNOWN_BLOCKS).expect("Constant is nonzero"), - ), - }; - - let req = if peer.info.roles.is_full() { - match self.chain_sync.new_peer(who, peer.info.best_hash, peer.info.best_number) { - Ok(req) => req, - Err(BadPeer(id, repu)) => { - self.behaviour.disconnect_peer(&id, HARDCODED_PEERSETS_SYNC); - self.peerset_handle.report_peer(id, repu); - return Err(()) - }, - } + /// Set handshake for the notification protocol. + pub fn set_notification_handshake(&mut self, protocol: ProtocolName, handshake: Vec) { + if let Some(index) = self.notification_protocols.iter().position(|p| *p == protocol) { + self.behaviour + .set_notif_protocol_handshake(sc_peerset::SetId::from(index), handshake); } else { - None - }; - - debug!(target: "sync", "Connected {}", who); - - self.peers.insert(who, peer); - if no_slot_peer { - self.default_peers_set_no_slot_connected_peers.insert(who); - } - self.pending_messages - .push_back(CustomMessageOutcome::PeerNewBest(who, status.best_number)); - - if let Some(req) = req { - self.chain_sync.send_block_request(who, req); - } - - Ok(()) - } - - /// Make sure an important block is propagated to peers. - /// - /// In chain-based consensus, we often need to make sure non-best forks are - /// at least temporarily synced. - pub fn announce_block(&mut self, hash: B::Hash, data: Option>) { - let header = match self.chain.header(hash) { - Ok(Some(header)) => header, - Ok(None) => { - warn!("Trying to announce unknown block: {}", hash); - return - }, - Err(e) => { - warn!("Error reading block header {}: {}", hash, e); - return - }, - }; - - // don't announce genesis block since it will be ignored - if header.number().is_zero() { - return - } - - let is_best = self.chain.info().best_hash == hash; - debug!(target: "sync", "Reannouncing block {:?} is_best: {}", hash, is_best); - - let data = data - .or_else(|| self.block_announce_data_cache.get(&hash).cloned()) - .unwrap_or_default(); - - for (who, ref mut peer) in self.peers.iter_mut() { - let inserted = peer.known_blocks.insert(hash); - if inserted { - trace!(target: "sync", "Announcing block {:?} to {}", hash, who); - let message = BlockAnnounce { - header: header.clone(), - state: if is_best { Some(BlockState::Best) } else { Some(BlockState::Normal) }, - data: Some(data.clone()), - }; - - self.behaviour - .write_notification(who, HARDCODED_PEERSETS_SYNC, message.encode()); - } - } - } - - /// Push a block announce validation. - /// - /// It is required that [`ChainSync::poll_block_announce_validation`] is - /// called later to check for finished validations. The result of the validation - /// needs to be passed to [`Protocol::process_block_announce_validation_result`] - /// to finish the processing. - /// - /// # Note - /// - /// This will internally create a future, but this future will not be registered - /// in the task before being polled once. So, it is required to call - /// [`ChainSync::poll_block_announce_validation`] to ensure that the future is - /// registered properly and will wake up the task when being ready. - fn push_block_announce_validation(&mut self, who: PeerId, announce: BlockAnnounce) { - let hash = announce.header.hash(); - - let peer = match self.peers.get_mut(&who) { - Some(p) => p, - None => { - log::error!(target: "sync", "Received block announce from disconnected peer {}", who); - debug_assert!(false); - return - }, - }; - - peer.known_blocks.insert(hash); - - let is_best = match announce.state.unwrap_or(BlockState::Best) { - BlockState::Best => true, - BlockState::Normal => false, - }; - - if peer.info.roles.is_full() { - self.chain_sync.push_block_announce_validation(who, hash, announce, is_best); - } - } - - /// Process the result of the block announce validation. - fn process_block_announce_validation_result( - &mut self, - validation_result: PollBlockAnnounceValidation, - ) -> CustomMessageOutcome { - let (header, is_best, who) = match validation_result { - PollBlockAnnounceValidation::Skip => return CustomMessageOutcome::None, - PollBlockAnnounceValidation::Nothing { is_best, who, announce } => { - self.update_peer_info(&who); - - if let Some(data) = announce.data { - if !data.is_empty() { - self.block_announce_data_cache.put(announce.header.hash(), data); - } - } - - // `on_block_announce` returns `OnBlockAnnounce::ImportHeader` - // when we have all data required to import the block - // in the BlockAnnounce message. This is only when: - // 1) we're on light client; - // AND - // 2) parent block is already imported and not pruned. - if is_best { - return CustomMessageOutcome::PeerNewBest(who, *announce.header.number()) - } else { - return CustomMessageOutcome::None - } - }, - PollBlockAnnounceValidation::ImportHeader { announce, is_best, who } => { - self.update_peer_info(&who); - - if let Some(data) = announce.data { - if !data.is_empty() { - self.block_announce_data_cache.put(announce.header.hash(), data); - } - } - - (announce.header, is_best, who) - }, - PollBlockAnnounceValidation::Failure { who, disconnect } => { - if disconnect { - self.behaviour.disconnect_peer(&who, HARDCODED_PEERSETS_SYNC); - } - - self.report_peer(who, rep::BAD_BLOCK_ANNOUNCEMENT); - return CustomMessageOutcome::None - }, - }; - - let number = *header.number(); - - // to import header from announced block let's construct response to request that normally - // would have been sent over network (but it is not in our case) - let blocks_to_import = self.chain_sync.on_block_data( - &who, - None, - BlockResponse:: { - id: 0, - blocks: vec![BlockData:: { - hash: header.hash(), - header: Some(header), - body: None, - indexed_body: None, - receipt: None, - message_queue: None, - justification: None, - justifications: None, - }], - }, - ); - self.chain_sync.process_block_response_data(blocks_to_import); - - if is_best { - self.pending_messages.push_back(CustomMessageOutcome::PeerNewBest(who, number)); + error!( + target: "sub-libp2p", + "set_notification_handshake with unknown protocol: {}", + protocol + ); } - - CustomMessageOutcome::None - } - - /// Call this when a block has been finalized. The sync layer may have some additional - /// requesting to perform. - pub fn on_block_finalized(&mut self, hash: B::Hash, header: &B::Header) { - self.chain_sync.on_block_finalized(&hash, *header.number()) } /// Set whether the syncing peers set is in reserved-only mode. @@ -884,41 +332,12 @@ where ); } } - - fn report_metrics(&self) { - if let Some(metrics) = &self.metrics { - let n = u64::try_from(self.peers.len()).unwrap_or(std::u64::MAX); - metrics.peers.set(n); - - let m = self.chain_sync.metrics(); - - metrics.fork_targets.set(m.fork_targets.into()); - metrics.queued_blocks.set(m.queued_blocks.into()); - - metrics - .justifications - .with_label_values(&["pending"]) - .set(m.justifications.pending_requests.into()); - metrics - .justifications - .with_label_values(&["active"]) - .set(m.justifications.active_requests.into()); - metrics - .justifications - .with_label_values(&["failed"]) - .set(m.justifications.failed_requests.into()); - metrics - .justifications - .with_label_values(&["importing"]) - .set(m.justifications.importing_requests.into()); - } - } } /// Outcome of an incoming custom message. #[derive(Debug)] #[must_use] -pub enum CustomMessageOutcome { +pub enum CustomMessageOutcome { /// Notification protocols have been opened with a remote. NotificationStreamOpened { remote: PeerId, @@ -926,6 +345,7 @@ pub enum CustomMessageOutcome { /// See [`crate::Event::NotificationStreamOpened::negotiated_fallback`]. negotiated_fallback: Option, roles: Roles, + received_handshake: Vec, notifications_sink: NotificationsSink, }, /// The [`NotificationsSink`] of some notification protocols need an update. @@ -935,31 +355,16 @@ pub enum CustomMessageOutcome { notifications_sink: NotificationsSink, }, /// Notification protocols have been closed with a remote. - NotificationStreamClosed { - remote: PeerId, - protocol: ProtocolName, - }, + NotificationStreamClosed { remote: PeerId, protocol: ProtocolName }, /// Messages have been received on one or more notifications protocols. - NotificationsReceived { - remote: PeerId, - messages: Vec<(ProtocolName, Bytes)>, - }, - /// Peer has a reported a new head of chain. - PeerNewBest(PeerId, NumberFor), + NotificationsReceived { remote: PeerId, messages: Vec<(ProtocolName, Bytes)> }, /// Now connected to a new peer for syncing purposes. - SyncConnected(PeerId), - /// No longer connected to a peer for syncing purposes. - SyncDisconnected(PeerId), None, } -impl NetworkBehaviour for Protocol -where - B: BlockT, - Client: HeaderBackend + 'static, -{ +impl NetworkBehaviour for Protocol { type ConnectionHandler = ::ConnectionHandler; - type OutEvent = CustomMessageOutcome; + type OutEvent = CustomMessageOutcome; fn new_handler(&mut self) -> Self::ConnectionHandler { self.behaviour.new_handler() @@ -994,25 +399,6 @@ where return Poll::Ready(NetworkBehaviourAction::GenerateEvent(message)) } - // Advance the state of `ChainSync` - // - // Process any received requests received from `NetworkService` and - // check if there is any block announcement validation finished. - while let Poll::Ready(result) = self.chain_sync.poll(cx) { - match self.process_block_announce_validation_result(result) { - CustomMessageOutcome::None => {}, - outcome => self.pending_messages.push_back(outcome), - } - } - - while let Poll::Ready(Some(())) = self.tick_timeout.poll_next_unpin(cx) { - self.tick(); - } - - if let Some(message) = self.pending_messages.pop_front() { - return Poll::Ready(NetworkBehaviourAction::GenerateEvent(message)) - } - let event = match self.behaviour.poll(cx, params) { Poll::Pending => return Poll::Pending, Poll::Ready(NetworkBehaviourAction::GenerateEvent(ev)) => ev, @@ -1045,17 +431,22 @@ where // announces substream. match as DecodeAll>::decode_all(&mut &received_handshake[..]) { Ok(GenericMessage::Status(handshake)) => { - let handshake = BlockAnnouncesHandshake { + let roles = handshake.roles; + let handshake = BlockAnnouncesHandshake:: { roles: handshake.roles, best_number: handshake.best_number, best_hash: handshake.best_hash, genesis_hash: handshake.genesis_hash, }; + self.peers.insert(peer_id, roles); - if self.on_sync_peer_connected(peer_id, handshake).is_ok() { - CustomMessageOutcome::SyncConnected(peer_id) - } else { - CustomMessageOutcome::None + CustomMessageOutcome::NotificationStreamOpened { + remote: peer_id, + protocol: self.notification_protocols[usize::from(set_id)].clone(), + negotiated_fallback, + received_handshake: handshake.encode(), + roles, + notifications_sink, } }, Ok(msg) => { @@ -1073,14 +464,21 @@ where &mut &received_handshake[..], ) { Ok(handshake) => { - if self.on_sync_peer_connected(peer_id, handshake).is_ok() { - CustomMessageOutcome::SyncConnected(peer_id) - } else { - CustomMessageOutcome::None + let roles = handshake.roles; + self.peers.insert(peer_id, roles); + + CustomMessageOutcome::NotificationStreamOpened { + remote: peer_id, + protocol: self.notification_protocols[usize::from(set_id)] + .clone(), + negotiated_fallback, + received_handshake, + roles, + notifications_sink, } }, Err(err2) => { - debug!( + log::debug!( target: "sync", "Couldn't decode handshake sent by {}: {:?}: {} & {}", peer_id, @@ -1104,9 +502,10 @@ where protocol: self.notification_protocols[usize::from(set_id)].clone(), negotiated_fallback, roles, + received_handshake, notifications_sink, }, - (Err(_), Some(peer)) if received_handshake.is_empty() => { + (Err(_), Some(roles)) if received_handshake.is_empty() => { // As a convenience, we allow opening substreams for "external" // notification protocols with an empty handshake. This fetches the // roles from the locally-known roles. @@ -1115,7 +514,8 @@ where remote: peer_id, protocol: self.notification_protocols[usize::from(set_id)].clone(), negotiated_fallback, - roles: peer.info.roles, + roles: *roles, + received_handshake, notifications_sink, } }, @@ -1124,15 +524,14 @@ where self.bad_handshake_substreams.insert((peer_id, set_id)); self.behaviour.disconnect_peer(&peer_id, set_id); self.peerset_handle.report_peer(peer_id, rep::BAD_MESSAGE); + self.peers.remove(&peer_id); CustomMessageOutcome::None }, } } }, NotificationsOut::CustomProtocolReplaced { peer_id, notifications_sink, set_id } => - if set_id == HARDCODED_PEERSETS_SYNC || - self.bad_handshake_substreams.contains(&(peer_id, set_id)) - { + if self.bad_handshake_substreams.contains(&(peer_id, set_id)) { CustomMessageOutcome::None } else { CustomMessageOutcome::NotificationStreamReplaced { @@ -1142,19 +541,7 @@ where } }, NotificationsOut::CustomProtocolClosed { peer_id, set_id } => { - // Set number 0 is hardcoded the default set of peers we sync from. - if set_id == HARDCODED_PEERSETS_SYNC { - if self.on_sync_peer_disconnected(peer_id).is_ok() { - CustomMessageOutcome::SyncDisconnected(peer_id) - } else { - log::trace!( - target: "sync", - "Disconnected peer which had earlier been refused by on_sync_peer_connected {}", - peer_id - ); - CustomMessageOutcome::None - } - } else if self.bad_handshake_substreams.remove(&(peer_id, set_id)) { + if self.bad_handshake_substreams.remove(&(peer_id, set_id)) { // The substream that has just been closed had been opened with a bad // handshake. The outer layers have never received an opening event about this // substream, and consequently shouldn't receive a closing event either. @@ -1166,45 +553,20 @@ where } } }, - NotificationsOut::Notification { peer_id, set_id, message } => match set_id { - HARDCODED_PEERSETS_SYNC if self.peers.contains_key(&peer_id) => { - if let Ok(announce) = BlockAnnounce::decode(&mut message.as_ref()) { - self.push_block_announce_validation(peer_id, announce); - - // Make sure that the newly added block announce validation future was - // polled once to be registered in the task. - if let Poll::Ready(res) = self.chain_sync.poll_block_announce_validation(cx) - { - self.process_block_announce_validation_result(res) - } else { - CustomMessageOutcome::None - } - } else { - warn!(target: "sub-libp2p", "Failed to decode block announce"); - CustomMessageOutcome::None - } - }, - HARDCODED_PEERSETS_SYNC => { - trace!( - target: "sync", - "Received sync for peer earlier refused by sync layer: {}", - peer_id - ); + NotificationsOut::Notification { peer_id, set_id, message } => { + if self.bad_handshake_substreams.contains(&(peer_id, set_id)) { CustomMessageOutcome::None - }, - _ if self.bad_handshake_substreams.contains(&(peer_id, set_id)) => - CustomMessageOutcome::None, - _ => { + } else { let protocol_name = self.notification_protocols[usize::from(set_id)].clone(); CustomMessageOutcome::NotificationsReceived { remote: peer_id, messages: vec![(protocol_name, message.freeze())], } - }, + } }, }; - if !matches!(outcome, CustomMessageOutcome::::None) { + if !matches!(outcome, CustomMessageOutcome::None) { return Poll::Ready(NetworkBehaviourAction::GenerateEvent(outcome)) } diff --git a/client/network/src/protocol/notifications/behaviour.rs b/client/network/src/protocol/notifications/behaviour.rs index 027ea0ab5..093d5846d 100644 --- a/client/network/src/protocol/notifications/behaviour.rs +++ b/client/network/src/protocol/notifications/behaviour.rs @@ -538,48 +538,6 @@ impl Notifications { self.peerset.reserved_peers(set_id) } - /// Sends a notification to a peer. - /// - /// Has no effect if the custom protocol is not open with the given peer. - /// - /// Also note that even if we have a valid open substream, it may in fact be already closed - /// without us knowing, in which case the packet will not be received. - /// - /// The `fallback` parameter is used for backwards-compatibility reason if the remote doesn't - /// support our protocol. One needs to pass the equivalent of what would have been passed - /// with `send_packet`. - pub fn write_notification( - &mut self, - target: &PeerId, - set_id: sc_peerset::SetId, - message: impl Into>, - ) { - let notifs_sink = match self.peers.get(&(*target, set_id)).and_then(|p| p.get_open()) { - None => { - trace!( - target: "sub-libp2p", - "Tried to sent notification to {:?} without an open channel.", - target, - ); - return - }, - Some(sink) => sink, - }; - - let message = message.into(); - - trace!( - target: "sub-libp2p", - "External API => Notification({:?}, {:?}, {} bytes)", - target, - set_id, - message.len(), - ); - trace!(target: "sub-libp2p", "Handler({:?}) <= Sync notification", target); - - notifs_sink.send_sync_notification(message); - } - /// Returns the state of the peerset manager, for debugging purposes. pub fn peerset_debug_info(&mut self) -> serde_json::Value { self.peerset.debug_info() @@ -3058,7 +3016,13 @@ mod tests { panic!("invalid state"); } - notif.write_notification(&peer, set_id, vec![1, 3, 3, 7]); + notif + .peers + .get(&(peer, set_id)) + .unwrap() + .get_open() + .unwrap() + .send_sync_notification(vec![1, 3, 3, 7]); assert_eq!(conn_yielder.get_next_event(peer, set_id.into()).await, Some(vec![1, 3, 3, 7])); } diff --git a/client/network/src/service.rs b/client/network/src/service.rs index fd0b965b7..4a0539361 100644 --- a/client/network/src/service.rs +++ b/client/network/src/service.rs @@ -34,8 +34,8 @@ use crate::{ network_state::{ NetworkState, NotConnectedPeer as NetworkStateNotConnectedPeer, Peer as NetworkStatePeer, }, - protocol::{self, NotificationsSink, NotifsHandlerError, PeerInfo, Protocol, Ready}, - transport, ChainSyncInterface, ReputationChange, + protocol::{self, NotificationsSink, NotifsHandlerError, Protocol, Ready}, + transport, ReputationChange, }; use futures::{channel::oneshot, prelude::*}; @@ -65,17 +65,16 @@ use sc_network_common::{ request_responses::{IfDisconnected, RequestFailure}, service::{ NetworkDHTProvider, NetworkEventStream, NetworkNotification, NetworkPeers, NetworkSigner, - NetworkStateInfo, NetworkStatus, NetworkStatusProvider, NetworkSyncForkRequest, + NetworkStateInfo, NetworkStatus, NetworkStatusProvider, NotificationSender as NotificationSenderT, NotificationSenderError, NotificationSenderReady as NotificationSenderReadyT, Signature, SigningError, }, - sync::SyncStatus, ExHashT, }; use sc_peerset::PeersetHandle; use sc_utils::mpsc::{tracing_unbounded, TracingUnboundedReceiver, TracingUnboundedSender}; use sp_blockchain::HeaderBackend; -use sp_runtime::traits::{Block as BlockT, NumberFor, Zero}; +use sp_runtime::traits::{Block as BlockT, Zero}; use std::{ cmp, collections::{HashMap, HashSet}, @@ -85,7 +84,7 @@ use std::{ pin::Pin, str, sync::{ - atomic::{AtomicBool, AtomicUsize, Ordering}, + atomic::{AtomicUsize, Ordering}, Arc, }, }; @@ -98,7 +97,7 @@ mod out_events; mod tests; pub use libp2p::identity::{error::DecodingError, Keypair, PublicKey}; -use sc_network_common::service::{NetworkBlock, NetworkRequest}; +use sc_network_common::service::NetworkRequest; /// Custom error that can be produced by the [`ConnectionHandler`] of the [`NetworkBehaviour`]. /// Used as a template parameter of [`SwarmEvent`] below. @@ -114,8 +113,6 @@ pub struct NetworkService { external_addresses: Arc>>, /// Listen addresses. Do **NOT** include a trailing `/p2p/` with our `PeerId`. listen_addresses: Arc>>, - /// Are we actively catching up with the chain? - is_major_syncing: Arc, /// Local copy of the `PeerId` of the local node. local_peer_id: PeerId, /// The `KeyPair` that defines the `PeerId` of the local node. @@ -126,9 +123,7 @@ pub struct NetworkService { /// nodes it should be connected to or not. peerset: PeersetHandle, /// Channel that sends messages to the actual worker. - to_worker: TracingUnboundedSender>, - /// Interface that can be used to delegate calls to `ChainSync` - chain_sync_service: Box>, + to_worker: TracingUnboundedSender, /// For each peer and protocol combination, an object that allows sending notifications to /// that peer. Updated by the [`NetworkWorker`]. peers_notifications_sinks: Arc>>, @@ -138,20 +133,23 @@ pub struct NetworkService { /// Marker to pin the `H` generic. Serves no purpose except to not break backwards /// compatibility. _marker: PhantomData, + /// Marker for block type + _block: PhantomData, } -impl NetworkWorker +impl NetworkWorker where B: BlockT + 'static, H: ExHashT, - Client: HeaderBackend + 'static, { /// Creates the network service. /// /// Returns a `NetworkWorker` that implements `Future` and must be regularly polled in order /// for the network processing to advance. From it, you can extract a `NetworkService` using /// `worker.service()`. The `NetworkService` can be shared through the codebase. - pub fn new(mut params: Params) -> Result { + pub fn new + 'static>( + mut params: Params, + ) -> Result { // Private and public keys configuration. let local_identity = params.network_config.node_key.clone().into_keypair()?; let local_public = local_identity.public(); @@ -230,10 +228,7 @@ where let (protocol, peerset_handle, mut known_addresses) = Protocol::new( From::from(¶ms.role), - params.chain.clone(), ¶ms.network_config, - params.metrics_registry.as_ref(), - params.chain_sync, params.block_announce_config, )?; @@ -268,10 +263,9 @@ where })?; let num_connected = Arc::new(AtomicUsize::new(0)); - let is_major_syncing = Arc::new(AtomicBool::new(false)); // Build the swarm. - let (mut swarm, bandwidth): (Swarm>, _) = { + let (mut swarm, bandwidth): (Swarm>, _) = { let user_agent = format!( "{} ({})", params.network_config.client_version, params.network_config.node_name @@ -418,7 +412,6 @@ where registry, MetricSources { bandwidth: bandwidth.clone(), - major_syncing: is_major_syncing.clone(), connected_peers: num_connected.clone(), }, )?), @@ -427,14 +420,14 @@ where // Listen on multiaddresses. for addr in ¶ms.network_config.listen_addresses { - if let Err(err) = Swarm::>::listen_on(&mut swarm, addr.clone()) { + if let Err(err) = Swarm::>::listen_on(&mut swarm, addr.clone()) { warn!(target: "sub-libp2p", "Can't listen on {} because: {:?}", addr, err) } } // Add external addresses. for addr in ¶ms.network_config.public_addresses { - Swarm::>::add_external_address( + Swarm::>::add_external_address( &mut swarm, addr.clone(), AddressScore::Infinite, @@ -450,24 +443,22 @@ where external_addresses: external_addresses.clone(), listen_addresses: listen_addresses.clone(), num_connected: num_connected.clone(), - is_major_syncing: is_major_syncing.clone(), peerset: peerset_handle, local_peer_id, local_identity, to_worker, - chain_sync_service: params.chain_sync_service, peers_notifications_sinks: peers_notifications_sinks.clone(), notifications_sizes_metric: metrics .as_ref() .map(|metrics| metrics.notifications_sizes.clone()), _marker: PhantomData, + _block: Default::default(), }); Ok(NetworkWorker { external_addresses, listen_addresses, num_connected, - is_major_syncing, network_service: swarm, service, from_service, @@ -476,22 +467,16 @@ where metrics, boot_node_ids, _marker: Default::default(), + _block: Default::default(), }) } /// High-level network status information. - pub fn status(&self) -> NetworkStatus { - let status = self.sync_state(); + pub fn status(&self) -> NetworkStatus { NetworkStatus { - sync_state: status.state, - best_seen_block: self.best_seen_block(), - num_sync_peers: self.num_sync_peers(), num_connected_peers: self.num_connected_peers(), - num_active_peers: self.num_active_peers(), total_bytes_inbound: self.total_bytes_inbound(), total_bytes_outbound: self.total_bytes_outbound(), - state_sync: status.state_sync, - warp_sync: status.warp_sync, } } @@ -510,42 +495,7 @@ where self.network_service.behaviour().user_protocol().num_connected_peers() } - /// Returns the number of peers we're connected to and that are being queried. - pub fn num_active_peers(&self) -> usize { - self.network_service.behaviour().user_protocol().num_active_peers() - } - - /// Current global sync state. - pub fn sync_state(&self) -> SyncStatus { - self.network_service.behaviour().user_protocol().sync_state() - } - - /// Target sync block number. - pub fn best_seen_block(&self) -> Option> { - self.network_service.behaviour().user_protocol().best_seen_block() - } - - /// Number of peers participating in syncing. - pub fn num_sync_peers(&self) -> u32 { - self.network_service.behaviour().user_protocol().num_sync_peers() - } - - /// Number of blocks in the import queue. - pub fn num_queued_blocks(&self) -> u32 { - self.network_service.behaviour().user_protocol().num_queued_blocks() - } - - /// Returns the number of downloaded blocks. - pub fn num_downloaded_blocks(&self) -> usize { - self.network_service.behaviour().user_protocol().num_downloaded_blocks() - } - - /// Number of active sync requests. - pub fn num_sync_requests(&self) -> usize { - self.network_service.behaviour().user_protocol().num_sync_requests() - } - - /// Adds an address known to a node. + /// Adds an address for a node. pub fn add_known_address(&mut self, peer_id: PeerId, addr: Multiaddr) { self.network_service.behaviour_mut().add_known_address(peer_id, addr); } @@ -556,32 +506,16 @@ where &self.service } - /// You must call this when a new block is finalized by the client. - pub fn on_block_finalized(&mut self, hash: B::Hash, header: B::Header) { - self.network_service - .behaviour_mut() - .user_protocol_mut() - .on_block_finalized(hash, &header); - } - - /// Inform the network service about new best imported block. - pub fn new_best_block_imported(&mut self, hash: B::Hash, number: NumberFor) { - self.network_service - .behaviour_mut() - .user_protocol_mut() - .new_best_block_imported(hash, number); - } - /// Returns the local `PeerId`. pub fn local_peer_id(&self) -> &PeerId { - Swarm::>::local_peer_id(&self.network_service) + Swarm::>::local_peer_id(&self.network_service) } /// Returns the list of addresses we are listening on. /// /// Does **NOT** include a trailing `/p2p/` with our `PeerId`. pub fn listen_addresses(&self) -> impl Iterator { - Swarm::>::listeners(&self.network_service) + Swarm::>::listeners(&self.network_service) } /// Get network state. @@ -661,7 +595,7 @@ where .collect() }; - let peer_id = Swarm::>::local_peer_id(swarm).to_base58(); + let peer_id = Swarm::>::local_peer_id(swarm).to_base58(); let listened_addresses = swarm.listeners().cloned().collect(); let external_addresses = swarm.external_addresses().map(|r| &r.addr).cloned().collect(); @@ -675,16 +609,6 @@ where } } - /// Get currently connected peers. - pub fn peers_debug_info(&mut self) -> Vec<(PeerId, PeerInfo)> { - self.network_service - .behaviour_mut() - .user_protocol_mut() - .peers_info() - .map(|(id, info)| (*id, info.clone())) - .collect() - } - /// Removes a `PeerId` from the list of reserved peers. pub fn remove_reserved_peer(&self, peer: PeerId) { self.service.remove_reserved_peer(peer); @@ -722,20 +646,6 @@ impl NetworkService { } } - /// Get connected peers debug information. - /// - /// Returns an error if the `NetworkWorker` is no longer running. - pub async fn peers_debug_info(&self) -> Result)>, ()> { - let (tx, rx) = oneshot::channel(); - - let _ = self - .to_worker - .unbounded_send(ServiceToWorkerMsg::PeersDebugInfo { pending_response: tx }); - - // The channel can only be closed if the network worker no longer exists. - rx.await.map_err(|_| ()) - } - /// Get the list of reserved peers. /// /// Returns an error if the `NetworkWorker` is no longer running. @@ -779,30 +689,6 @@ impl NetworkService { } } -impl sp_consensus::SyncOracle for NetworkService { - fn is_major_syncing(&self) -> bool { - self.is_major_syncing.load(Ordering::Relaxed) - } - - fn is_offline(&self) -> bool { - self.num_connected.load(Ordering::Relaxed) == 0 - } -} - -impl sc_consensus::JustificationSyncLink for NetworkService { - /// Request a justification for the given block from the network. - /// - /// On success, the justification will be passed to the import queue that was part at - /// initialization as part of the configuration. - fn request_justification(&self, hash: &B::Hash, number: NumberFor) { - let _ = self.chain_sync_service.request_justification(hash, number); - } - - fn clear_justification_requests(&self) { - let _ = self.chain_sync_service.clear_justification_requests(); - } -} - impl NetworkStateInfo for NetworkService where B: sp_runtime::traits::Block, @@ -856,29 +742,13 @@ where } } -impl NetworkSyncForkRequest> for NetworkService -where - B: BlockT + 'static, - H: ExHashT, -{ - /// Configure an explicit fork sync request. - /// Note that this function should not be used for recent blocks. - /// Sync should be able to download all the recent forks normally. - /// `set_sync_fork_request` should only be used if external code detects that there's - /// a stale fork missing. - /// Passing empty `peers` set effectively removes the sync request. - fn set_sync_fork_request(&self, peers: Vec, hash: B::Hash, number: NumberFor) { - self.chain_sync_service.set_sync_fork_request(peers, hash, number); - } -} - #[async_trait::async_trait] -impl NetworkStatusProvider for NetworkService +impl NetworkStatusProvider for NetworkService where B: BlockT + 'static, H: ExHashT, { - async fn status(&self) -> Result, ()> { + async fn status(&self) -> Result { let (tx, rx) = oneshot::channel(); let _ = self @@ -1125,6 +995,12 @@ where Ok(Box::new(NotificationSender { sink, protocol_name: protocol, notification_size_metric })) } + + fn set_notification_handshake(&self, protocol: ProtocolName, handshake: Vec) { + let _ = self + .to_worker + .unbounded_send(ServiceToWorkerMsg::SetNotificationHandshake(protocol, handshake)); + } } #[async_trait::async_trait] @@ -1171,22 +1047,6 @@ where } } -impl NetworkBlock> for NetworkService -where - B: BlockT + 'static, - H: ExHashT, -{ - fn announce_block(&self, hash: B::Hash, data: Option>) { - let _ = self.to_worker.unbounded_send(ServiceToWorkerMsg::AnnounceBlock(hash, data)); - } - - fn new_best_block_imported(&self, hash: B::Hash, number: NumberFor) { - let _ = self - .to_worker - .unbounded_send(ServiceToWorkerMsg::NewBestBlockImported(hash, number)); - } -} - /// A `NotificationSender` allows for sending notifications to a peer with a chosen protocol. #[must_use] pub struct NotificationSender { @@ -1257,8 +1117,7 @@ impl<'a> NotificationSenderReadyT for NotificationSenderReady<'a> { /// Messages sent from the `NetworkService` to the `NetworkWorker`. /// /// Each entry corresponds to a method of `NetworkService`. -enum ServiceToWorkerMsg { - AnnounceBlock(B::Hash, Option>), +enum ServiceToWorkerMsg { GetValue(KademliaKey), PutValue(KademliaKey, Vec), AddKnownAddress(PeerId, Multiaddr), @@ -1280,16 +1139,13 @@ enum ServiceToWorkerMsg { connect: IfDisconnected, }, NetworkStatus { - pending_response: oneshot::Sender, RequestFailure>>, + pending_response: oneshot::Sender>, }, NetworkState { pending_response: oneshot::Sender>, }, DisconnectPeer(PeerId, ProtocolName), - NewBestBlockImported(B::Hash, NumberFor), - PeersDebugInfo { - pending_response: oneshot::Sender)>>, - }, + SetNotificationHandshake(ProtocolName, Vec), ReservedPeers { pending_response: oneshot::Sender>, }, @@ -1299,11 +1155,10 @@ enum ServiceToWorkerMsg { /// /// You are encouraged to poll this in a separate background thread or task. #[must_use = "The NetworkWorker must be polled in order for the network to advance"] -pub struct NetworkWorker +pub struct NetworkWorker where B: BlockT + 'static, H: ExHashT, - Client: HeaderBackend + 'static, { /// Updated by the `NetworkWorker` and loaded by the `NetworkService`. external_addresses: Arc>>, @@ -1311,14 +1166,12 @@ where listen_addresses: Arc>>, /// Updated by the `NetworkWorker` and loaded by the `NetworkService`. num_connected: Arc, - /// Updated by the `NetworkWorker` and loaded by the `NetworkService`. - is_major_syncing: Arc, /// The network service that can be extracted and shared through the codebase. service: Arc>, /// The *actual* network. - network_service: Swarm>, + network_service: Swarm>, /// Messages from the [`NetworkService`] that must be processed. - from_service: TracingUnboundedReceiver>, + from_service: TracingUnboundedReceiver, /// Senders for events that happen on the network. event_streams: out_events::OutChannels, /// Prometheus network metrics. @@ -1331,13 +1184,14 @@ where /// Marker to pin the `H` generic. Serves no purpose except to not break backwards /// compatibility. _marker: PhantomData, + /// Marker for block type + _block: PhantomData, } -impl NetworkWorker +impl NetworkWorker where B: BlockT + 'static, H: ExHashT, - Client: HeaderBackend + 'static, { /// Run the network. pub async fn run(mut self) { @@ -1364,10 +1218,9 @@ where }, }; + // Update the variables shared with the `NetworkService`. let num_connected_peers = self.network_service.behaviour_mut().user_protocol_mut().num_connected_peers(); - - // Update the variables shared with the `NetworkService`. self.num_connected.store(num_connected_peers, Ordering::Relaxed); { let external_addresses = @@ -1379,16 +1232,6 @@ where *self.listen_addresses.lock() = listen_addresses; } - let is_major_syncing = self - .network_service - .behaviour_mut() - .user_protocol_mut() - .sync_state() - .state - .is_major_syncing(); - - self.is_major_syncing.store(is_major_syncing, Ordering::Relaxed); - if let Some(metrics) = self.metrics.as_ref() { if let Some(buckets) = self.network_service.behaviour_mut().num_entries_per_kbucket() { for (lower_ilog2_bucket_bound, num_entries) in buckets { @@ -1420,13 +1263,8 @@ where } /// Process the next message coming from the `NetworkService`. - fn handle_worker_message(&mut self, msg: ServiceToWorkerMsg) { + fn handle_worker_message(&mut self, msg: ServiceToWorkerMsg) { match msg { - ServiceToWorkerMsg::AnnounceBlock(hash, data) => self - .network_service - .behaviour_mut() - .user_protocol_mut() - .announce_block(hash, data), ServiceToWorkerMsg::GetValue(key) => self.network_service.behaviour_mut().get_value(key), ServiceToWorkerMsg::PutValue(key, value) => @@ -1505,14 +1343,11 @@ where .behaviour_mut() .user_protocol_mut() .disconnect_peer(&who, protocol_name), - ServiceToWorkerMsg::NewBestBlockImported(hash, number) => self + ServiceToWorkerMsg::SetNotificationHandshake(protocol, handshake) => self .network_service .behaviour_mut() .user_protocol_mut() - .new_best_block_imported(hash, number), - ServiceToWorkerMsg::PeersDebugInfo { pending_response } => { - let _ = pending_response.send(self.peers_debug_info()); - }, + .set_notification_handshake(protocol, handshake), ServiceToWorkerMsg::ReservedPeers { pending_response } => { let _ = pending_response.send(self.reserved_peers().map(ToOwned::to_owned).collect()); @@ -1523,7 +1358,7 @@ where /// Process the next event coming from `Swarm`. fn handle_swarm_event( &mut self, - event: SwarmEvent>>, + event: SwarmEvent>>, ) { match event { SwarmEvent::Behaviour(BehaviourOut::InboundRequest { protocol, result, .. }) => { @@ -1642,6 +1477,7 @@ where negotiated_fallback, notifications_sink, role, + received_handshake, }) => { if let Some(metrics) = self.metrics.as_ref() { metrics @@ -1660,6 +1496,7 @@ where protocol, negotiated_fallback, role, + received_handshake, }); }, SwarmEvent::Behaviour(BehaviourOut::NotificationStreamReplaced { @@ -1725,12 +1562,6 @@ where } self.event_streams.send(Event::NotificationsReceived { remote, messages }); }, - SwarmEvent::Behaviour(BehaviourOut::SyncConnected(remote)) => { - self.event_streams.send(Event::SyncConnected { remote }); - }, - SwarmEvent::Behaviour(BehaviourOut::SyncDisconnected(remote)) => { - self.event_streams.send(Event::SyncDisconnected { remote }); - }, SwarmEvent::Behaviour(BehaviourOut::Dht(event, duration)) => { if let Some(metrics) = self.metrics.as_ref() { let query_type = match event { @@ -1925,11 +1756,10 @@ where } } -impl Unpin for NetworkWorker +impl Unpin for NetworkWorker where B: BlockT + 'static, H: ExHashT, - Client: HeaderBackend + 'static, { } diff --git a/client/network/src/service/metrics.rs b/client/network/src/service/metrics.rs index ccb440b2c..13bc4b4e7 100644 --- a/client/network/src/service/metrics.rs +++ b/client/network/src/service/metrics.rs @@ -24,7 +24,7 @@ use prometheus_endpoint::{ use std::{ str, sync::{ - atomic::{AtomicBool, AtomicUsize, Ordering}, + atomic::{AtomicUsize, Ordering}, Arc, }, }; @@ -34,7 +34,6 @@ pub use prometheus_endpoint::{Histogram, HistogramVec}; /// Registers all networking metrics with the given registry. pub fn register(registry: &Registry, sources: MetricSources) -> Result { BandwidthCounters::register(registry, sources.bandwidth)?; - MajorSyncingGauge::register(registry, sources.major_syncing)?; NumConnectedGauge::register(registry, sources.connected_peers)?; Metrics::register(registry) } @@ -42,7 +41,6 @@ pub fn register(registry: &Registry, sources: MetricSources) -> Result, - pub major_syncing: Arc, pub connected_peers: Arc, } @@ -266,37 +264,6 @@ impl MetricSource for BandwidthCounters { } } -/// The "major syncing" metric. -#[derive(Clone)] -pub struct MajorSyncingGauge(Arc); - -impl MajorSyncingGauge { - /// Registers the `MajorSyncGauge` metric whose value is - /// obtained from the given `AtomicBool`. - fn register(registry: &Registry, value: Arc) -> Result<(), PrometheusError> { - prometheus::register( - SourcedGauge::new( - &Opts::new( - "substrate_sub_libp2p_is_major_syncing", - "Whether the node is performing a major sync or not.", - ), - MajorSyncingGauge(value), - )?, - registry, - )?; - - Ok(()) - } -} - -impl MetricSource for MajorSyncingGauge { - type N = u64; - - fn collect(&self, mut set: impl FnMut(&[&str], Self::N)) { - set(&[], self.0.load(Ordering::Relaxed) as u64); - } -} - /// The connected peers metric. #[derive(Clone)] pub struct NumConnectedGauge(Arc); diff --git a/client/network/src/service/out_events.rs b/client/network/src/service/out_events.rs index 99ac022c2..3771ea164 100644 --- a/client/network/src/service/out_events.rs +++ b/client/network/src/service/out_events.rs @@ -268,12 +268,6 @@ impl Metrics { Event::Dht(_) => { self.events_total.with_label_values(&["dht", "sent", name]).inc(); }, - Event::SyncConnected { .. } => { - self.events_total.with_label_values(&["sync-connected", "sent", name]).inc(); - }, - Event::SyncDisconnected { .. } => { - self.events_total.with_label_values(&["sync-disconnected", "sent", name]).inc(); - }, Event::NotificationStreamOpened { protocol, .. } => { format_label("notif-open-", protocol, |protocol_label| { self.events_total.with_label_values(&[protocol_label, "sent", name]).inc(); @@ -301,14 +295,6 @@ impl Metrics { Event::Dht(_) => { self.events_total.with_label_values(&["dht", "received", name]).inc(); }, - Event::SyncConnected { .. } => { - self.events_total.with_label_values(&["sync-connected", "received", name]).inc(); - }, - Event::SyncDisconnected { .. } => { - self.events_total - .with_label_values(&["sync-disconnected", "received", name]) - .inc(); - }, Event::NotificationStreamOpened { protocol, .. } => { format_label("notif-open-", protocol, |protocol_label| { self.events_total.with_label_values(&[protocol_label, "received", name]).inc(); diff --git a/client/network/src/service/tests/chain_sync.rs b/client/network/src/service/tests/chain_sync.rs deleted file mode 100644 index a369b7174..000000000 --- a/client/network/src/service/tests/chain_sync.rs +++ /dev/null @@ -1,420 +0,0 @@ -// This file is part of Substrate. - -// Copyright (C) Parity Technologies (UK) Ltd. -// SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 - -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with this program. If not, see . - -use crate::{ - config, - service::tests::{TestNetworkBuilder, BLOCK_ANNOUNCE_PROTO_NAME}, -}; - -use futures::prelude::*; -use libp2p::PeerId; -use sc_block_builder::BlockBuilderProvider; -use sc_client_api::HeaderBackend; -use sc_consensus::JustificationSyncLink; -use sc_network_common::{ - config::{MultiaddrWithPeerId, ProtocolId, SetConfig}, - protocol::{event::Event, role::Roles, ProtocolName}, - service::NetworkSyncForkRequest, - sync::{SyncState, SyncStatus}, -}; -use sc_network_sync::{mock::MockChainSync, service::mock::MockChainSyncInterface, ChainSync}; -use sp_core::H256; -use sp_runtime::traits::{Block as BlockT, Header as _}; -use std::{ - sync::{Arc, RwLock}, - task::Poll, - time::Duration, -}; -use substrate_test_runtime_client::{TestClientBuilder, TestClientBuilderExt as _}; - -fn set_default_expecations_no_peers( - chain_sync: &mut MockChainSync, -) { - chain_sync.expect_poll().returning(|_| Poll::Pending); - chain_sync.expect_status().returning(|| SyncStatus { - state: SyncState::Idle, - best_seen_block: None, - num_peers: 0u32, - queued_blocks: 0u32, - state_sync: None, - warp_sync: None, - }); -} - -#[tokio::test] -async fn normal_network_poll_no_peers() { - // build `ChainSync` and set default expectations for it - let mut chain_sync = - Box::new(MockChainSync::::new()); - set_default_expecations_no_peers(&mut chain_sync); - - // build `ChainSyncInterface` provider and set no expecations for it (i.e., it cannot be - // called) - let chain_sync_service = - Box::new(MockChainSyncInterface::::new()); - - let mut network = TestNetworkBuilder::new() - .with_chain_sync((chain_sync, chain_sync_service)) - .build(); - - // perform one action on network - let _ = network.network().next_action().await; -} - -#[tokio::test] -async fn request_justification() { - let hash = H256::random(); - let number = 1337u64; - - // build `ChainSyncInterface` provider and and expect - // `JustificationSyncLink::request_justification() to be called once - let mut chain_sync_service = - Box::new(MockChainSyncInterface::::new()); - - chain_sync_service - .expect_justification_sync_link_request_justification() - .withf(move |in_hash, in_number| &hash == in_hash && &number == in_number) - .once() - .returning(|_, _| ()); - - // build `ChainSync` and set default expecations for it - let mut chain_sync = MockChainSync::::new(); - - set_default_expecations_no_peers(&mut chain_sync); - let mut network = TestNetworkBuilder::new() - .with_chain_sync((Box::new(chain_sync), chain_sync_service)) - .build(); - - // send "request justifiction" message and poll the network - network.service().request_justification(&hash, number); - - // perform one action on network - let _ = network.network().next_action().await; -} - -#[tokio::test] -async fn clear_justification_requests() { - // build `ChainSyncInterface` provider and expect - // `JustificationSyncLink::clear_justification_requests()` to be called - let mut chain_sync_service = - Box::new(MockChainSyncInterface::::new()); - - chain_sync_service - .expect_justification_sync_link_clear_justification_requests() - .once() - .returning(|| ()); - - // build `ChainSync` and set default expecations for it - let mut chain_sync = - Box::new(MockChainSync::::new()); - - set_default_expecations_no_peers(&mut chain_sync); - let mut network = TestNetworkBuilder::new() - .with_chain_sync((chain_sync, chain_sync_service)) - .build(); - - // send "request justifiction" message and poll the network - network.service().clear_justification_requests(); - - // perform one action on network - let _ = network.network().next_action().await; -} - -#[tokio::test] -async fn set_sync_fork_request() { - // build `ChainSync` and set default expectations for it - let mut chain_sync = - Box::new(MockChainSync::::new()); - set_default_expecations_no_peers(&mut chain_sync); - - // build `ChainSyncInterface` provider and verify that the `set_sync_fork_request()` - // call is delegated to `ChainSyncInterface` (which eventually forwards it to `ChainSync`) - let mut chain_sync_service = - MockChainSyncInterface::::new(); - - let hash = H256::random(); - let number = 1337u64; - let peers = (0..3).map(|_| PeerId::random()).collect::>(); - let copy_peers = peers.clone(); - - chain_sync_service - .expect_set_sync_fork_request() - .withf(move |in_peers, in_hash, in_number| { - &peers == in_peers && &hash == in_hash && &number == in_number - }) - .once() - .returning(|_, _, _| ()); - - let mut network = TestNetworkBuilder::new() - .with_chain_sync((chain_sync, Box::new(chain_sync_service))) - .build(); - - // send "set sync fork request" message and poll the network - network.service().set_sync_fork_request(copy_peers, hash, number); - - // perform one action on network - let _ = network.network().next_action().await; -} - -#[tokio::test] -async fn on_block_finalized() { - let client = Arc::new(TestClientBuilder::with_default_backend().build_with_longest_chain().0); - // build `ChainSyncInterface` provider and set no expecations for it (i.e., it cannot be - // called) - let chain_sync_service = - Box::new(MockChainSyncInterface::::new()); - - // build `ChainSync` and verify that call to `on_block_finalized()` is made - let mut chain_sync = - Box::new(MockChainSync::::new()); - - let at = client.header(client.info().best_hash).unwrap().unwrap().hash(); - let block = client - .new_block_at(at, Default::default(), false) - .unwrap() - .build() - .unwrap() - .block; - let header = block.header.clone(); - let block_number = *header.number(); - let hash = block.hash(); - - chain_sync - .expect_on_block_finalized() - .withf(move |in_hash, in_number| &hash == in_hash && &block_number == in_number) - .once() - .returning(|_, _| ()); - - set_default_expecations_no_peers(&mut chain_sync); - let mut network = TestNetworkBuilder::new() - .with_client(client) - .with_chain_sync((chain_sync, chain_sync_service)) - .build(); - - // send "set sync fork request" message and poll the network - network.network().on_block_finalized(hash, header); - - // perform one action on network - let _ = network.network().next_action().await; -} - -// report from mock import queue that importing a justification was not successful -// and verify that connection to the peer is closed -#[tokio::test] -async fn invalid_justification_imported() { - struct DummyImportQueueHandle; - - impl - sc_consensus::import_queue::ImportQueueService< - substrate_test_runtime_client::runtime::Block, - > for DummyImportQueueHandle - { - fn import_blocks( - &mut self, - _origin: sp_consensus::BlockOrigin, - _blocks: Vec< - sc_consensus::IncomingBlock, - >, - ) { - } - - fn import_justifications( - &mut self, - _who: sc_consensus::import_queue::RuntimeOrigin, - _hash: substrate_test_runtime_client::runtime::Hash, - _number: sp_runtime::traits::NumberFor, - _justifications: sp_runtime::Justifications, - ) { - } - } - - struct DummyImportQueue( - Arc< - RwLock< - Option<( - PeerId, - substrate_test_runtime_client::runtime::Hash, - sp_runtime::traits::NumberFor, - )>, - >, - >, - DummyImportQueueHandle, - ); - - #[async_trait::async_trait] - impl sc_consensus::ImportQueue for DummyImportQueue { - fn poll_actions( - &mut self, - _cx: &mut futures::task::Context, - link: &mut dyn sc_consensus::Link, - ) { - if let Some((peer, hash, number)) = *self.0.read().unwrap() { - link.justification_imported(peer, &hash, number, false); - } - } - - fn service( - &self, - ) -> Box< - dyn sc_consensus::import_queue::ImportQueueService< - substrate_test_runtime_client::runtime::Block, - >, - > { - Box::new(DummyImportQueueHandle {}) - } - - fn service_ref( - &mut self, - ) -> &mut dyn sc_consensus::import_queue::ImportQueueService< - substrate_test_runtime_client::runtime::Block, - > { - &mut self.1 - } - - async fn run( - self, - _link: Box>, - ) { - } - } - - let justification_info = Arc::new(RwLock::new(None)); - let listen_addr = config::build_multiaddr![Memory(rand::random::())]; - - let (service1, mut event_stream1) = TestNetworkBuilder::new() - .with_import_queue(Box::new(DummyImportQueue( - justification_info.clone(), - DummyImportQueueHandle {}, - ))) - .with_listen_addresses(vec![listen_addr.clone()]) - .build() - .start_network(); - - let (service2, mut event_stream2) = TestNetworkBuilder::new() - .with_set_config(SetConfig { - reserved_nodes: vec![MultiaddrWithPeerId { - multiaddr: listen_addr, - peer_id: service1.local_peer_id, - }], - ..Default::default() - }) - .build() - .start_network(); - - async fn wait_for_events(stream: &mut (impl Stream + std::marker::Unpin)) { - let mut notif_received = false; - let mut sync_received = false; - while !notif_received || !sync_received { - match stream.next().await.unwrap() { - Event::NotificationStreamOpened { .. } => notif_received = true, - Event::SyncConnected { .. } => sync_received = true, - _ => {}, - }; - } - } - - wait_for_events(&mut event_stream1).await; - wait_for_events(&mut event_stream2).await; - - { - let mut info = justification_info.write().unwrap(); - *info = Some((service2.local_peer_id, H256::random(), 1337u64)); - } - - let wait_disconnection = async { - while !std::matches!(event_stream1.next().await, Some(Event::SyncDisconnected { .. })) {} - }; - - if tokio::time::timeout(Duration::from_secs(5), wait_disconnection).await.is_err() { - panic!("did not receive disconnection event in time"); - } -} - -#[tokio::test] -async fn disconnect_peer_using_chain_sync_handle() { - let client = Arc::new(TestClientBuilder::with_default_backend().build_with_longest_chain().0); - let listen_addr = config::build_multiaddr![Memory(rand::random::())]; - - let import_queue = Box::new(sc_consensus::import_queue::mock::MockImportQueueHandle::new()); - let (chain_sync_network_provider, chain_sync_network_handle) = - sc_network_sync::service::network::NetworkServiceProvider::new(); - let handle_clone = chain_sync_network_handle.clone(); - - let (chain_sync, chain_sync_service, _) = ChainSync::new( - sc_network_common::sync::SyncMode::Full, - client.clone(), - ProtocolId::from("test-protocol-name"), - &Some(String::from("test-fork-id")), - Roles::from(&config::Role::Full), - Box::new(sp_consensus::block_validation::DefaultBlockAnnounceValidator), - 1u32, - None, - None, - chain_sync_network_handle.clone(), - import_queue, - ProtocolName::from("block-request"), - ProtocolName::from("state-request"), - None, - ) - .unwrap(); - - let (node1, mut event_stream1) = TestNetworkBuilder::new() - .with_listen_addresses(vec![listen_addr.clone()]) - .with_chain_sync((Box::new(chain_sync), Box::new(chain_sync_service))) - .with_chain_sync_network((chain_sync_network_provider, chain_sync_network_handle)) - .with_client(client.clone()) - .build() - .start_network(); - - let (node2, mut event_stream2) = TestNetworkBuilder::new() - .with_set_config(SetConfig { - reserved_nodes: vec![MultiaddrWithPeerId { - multiaddr: listen_addr, - peer_id: node1.local_peer_id, - }], - ..Default::default() - }) - .with_client(client.clone()) - .build() - .start_network(); - - async fn wait_for_events(stream: &mut (impl Stream + std::marker::Unpin)) { - let mut notif_received = false; - let mut sync_received = false; - while !notif_received || !sync_received { - match stream.next().await.unwrap() { - Event::NotificationStreamOpened { .. } => notif_received = true, - Event::SyncConnected { .. } => sync_received = true, - _ => {}, - }; - } - } - - wait_for_events(&mut event_stream1).await; - wait_for_events(&mut event_stream2).await; - - handle_clone.disconnect_peer(node2.local_peer_id, BLOCK_ANNOUNCE_PROTO_NAME.into()); - - let wait_disconnection = async { - while !std::matches!(event_stream1.next().await, Some(Event::SyncDisconnected { .. })) {} - }; - - if tokio::time::timeout(Duration::from_secs(5), wait_disconnection).await.is_err() { - panic!("did not receive disconnection event in time"); - } -} diff --git a/client/network/src/service/tests/mod.rs b/client/network/src/service/tests/mod.rs index 3233b1584..3ac782900 100644 --- a/client/network/src/service/tests/mod.rs +++ b/client/network/src/service/tests/mod.rs @@ -16,44 +16,36 @@ // You should have received a copy of the GNU General Public License // along with this program. If not, see . -use crate::{config, ChainSyncInterface, NetworkService, NetworkWorker}; +use crate::{config, NetworkService, NetworkWorker}; use futures::prelude::*; use libp2p::Multiaddr; -use sc_client_api::{BlockBackend, HeaderBackend}; use sc_consensus::{ImportQueue, Link}; use sc_network_common::{ - config::{ - NonDefaultSetConfig, NonReservedPeerMode, NotificationHandshake, ProtocolId, SetConfig, - TransportConfig, - }, + config::{NonDefaultSetConfig, ProtocolId, SetConfig, TransportConfig}, protocol::{event::Event, role::Roles}, service::NetworkEventStream, - sync::{message::BlockAnnouncesHandshake, ChainSync as ChainSyncT}, }; use sc_network_light::light_client_requests::handler::LightClientRequestHandler; use sc_network_sync::{ block_request_handler::BlockRequestHandler, + engine::SyncingEngine, service::network::{NetworkServiceHandle, NetworkServiceProvider}, state_request_handler::StateRequestHandler, - ChainSync, }; -use sp_runtime::traits::{Block as BlockT, Header as _, Zero}; +use sp_runtime::traits::{Block as BlockT, Header as _}; use std::sync::Arc; use substrate_test_runtime_client::{ runtime::{Block as TestBlock, Hash as TestHash}, - TestClient, TestClientBuilder, TestClientBuilderExt as _, + TestClientBuilder, TestClientBuilderExt as _, }; -#[cfg(test)] -mod chain_sync; #[cfg(test)] mod service; -type TestNetworkWorker = NetworkWorker; +type TestNetworkWorker = NetworkWorker; type TestNetworkService = NetworkService; -const BLOCK_ANNOUNCE_PROTO_NAME: &str = "/block-announces"; const PROTOCOL_NAME: &str = "/foo"; struct TestNetwork { @@ -65,14 +57,6 @@ impl TestNetwork { Self { network } } - pub fn service(&self) -> &Arc { - &self.network.service() - } - - pub fn network(&mut self) -> &mut TestNetworkWorker { - &mut self.network - } - pub fn start_network( self, ) -> (Arc, (impl Stream + std::marker::Unpin)) { @@ -92,7 +76,6 @@ struct TestNetworkBuilder { client: Option>, listen_addresses: Vec, set_config: Option, - chain_sync: Option<(Box>, Box>)>, chain_sync_network: Option<(NetworkServiceProvider, NetworkServiceHandle)>, config: Option, } @@ -105,17 +88,11 @@ impl TestNetworkBuilder { client: None, listen_addresses: Vec::new(), set_config: None, - chain_sync: None, chain_sync_network: None, config: None, } } - pub fn with_client(mut self, client: Arc) -> Self { - self.client = Some(client); - self - } - pub fn with_config(mut self, config: config::NetworkConfiguration) -> Self { self.config = Some(config); self @@ -131,27 +108,6 @@ impl TestNetworkBuilder { self } - pub fn with_chain_sync( - mut self, - chain_sync: (Box>, Box>), - ) -> Self { - self.chain_sync = Some(chain_sync); - self - } - - pub fn with_chain_sync_network( - mut self, - chain_sync_network: (NetworkServiceProvider, NetworkServiceHandle), - ) -> Self { - self.chain_sync_network = Some(chain_sync_network); - self - } - - pub fn with_import_queue(mut self, import_queue: Box>) -> Self { - self.import_queue = Some(import_queue); - self - } - pub fn build(mut self) -> TestNetwork { let client = self.client.as_mut().map_or( Arc::new(TestClientBuilder::with_default_backend().build_with_longest_chain().0), @@ -240,73 +196,29 @@ impl TestNetworkBuilder { protocol_config }; - let block_announce_config = NonDefaultSetConfig { - notifications_protocol: BLOCK_ANNOUNCE_PROTO_NAME.into(), - fallback_names: vec![], - max_notification_size: 1024 * 1024, - handshake: Some(NotificationHandshake::new(BlockAnnouncesHandshake::< - substrate_test_runtime_client::runtime::Block, - >::build( - Roles::from(&config::Role::Full), - client.info().best_number, - client.info().best_hash, - client - .block_hash(Zero::zero()) - .ok() - .flatten() - .expect("Genesis block exists; qed"), - ))), - set_config: SetConfig { - in_peers: 0, - out_peers: 0, - reserved_nodes: Vec::new(), - non_reserved_mode: NonReservedPeerMode::Deny, - }, - }; - let (chain_sync_network_provider, chain_sync_network_handle) = self.chain_sync_network.unwrap_or(NetworkServiceProvider::new()); - let (chain_sync, chain_sync_service) = self.chain_sync.unwrap_or({ - let (chain_sync, chain_sync_service, _) = ChainSync::new( - match network_config.sync_mode { - config::SyncMode::Full => sc_network_common::sync::SyncMode::Full, - config::SyncMode::Fast { skip_proofs, storage_chain_mode } => - sc_network_common::sync::SyncMode::LightState { - skip_proofs, - storage_chain_mode, - }, - config::SyncMode::Warp => sc_network_common::sync::SyncMode::Warp, - }, - client.clone(), - protocol_id.clone(), - &fork_id, - Roles::from(&config::Role::Full), - Box::new(sp_consensus::block_validation::DefaultBlockAnnounceValidator), - network_config.max_parallel_downloads, - None, - None, - chain_sync_network_handle, - import_queue.service(), - block_request_protocol_config.name.clone(), - state_request_protocol_config.name.clone(), - None, - ) - .unwrap(); - - if let None = self.link { - self.link = Some(Box::new(chain_sync_service.clone())); - } - (Box::new(chain_sync), Box::new(chain_sync_service)) - }); - let mut link = self - .link - .unwrap_or(Box::new(sc_network_sync::service::mock::MockChainSyncInterface::new())); - + let (engine, chain_sync_service, block_announce_config) = SyncingEngine::new( + Roles::from(&config::Role::Full), + client.clone(), + None, + &network_config, + protocol_id.clone(), + &None, + Box::new(sp_consensus::block_validation::DefaultBlockAnnounceValidator), + None, + chain_sync_network_handle, + import_queue.service(), + block_request_protocol_config.name.clone(), + state_request_protocol_config.name.clone(), + None, + ) + .unwrap(); + let mut link = self.link.unwrap_or(Box::new(chain_sync_service.clone())); let worker = NetworkWorker::< substrate_test_runtime_client::runtime::Block, substrate_test_runtime_client::runtime::Hash, - substrate_test_runtime_client::TestClient, >::new(config::Params { block_announce_config, role: config::Role::Full, @@ -317,8 +229,6 @@ impl TestNetworkBuilder { chain: client.clone(), protocol_id, fork_id, - chain_sync, - chain_sync_service, metrics_registry: None, request_response_protocol_configs: [ block_request_protocol_config, @@ -343,6 +253,8 @@ impl TestNetworkBuilder { tokio::time::sleep(std::time::Duration::from_millis(250)).await; } }); + let stream = worker.service().event_stream("syncing"); + tokio::spawn(engine.run(stream)); TestNetwork::new(worker) } diff --git a/client/network/src/service/tests/service.rs b/client/network/src/service/tests/service.rs index 1c7b32ff0..9c4c0ad6e 100644 --- a/client/network/src/service/tests/service.rs +++ b/client/network/src/service/tests/service.rs @@ -32,7 +32,6 @@ type TestNetworkService = NetworkService< substrate_test_runtime_client::runtime::Hash, >; -const BLOCK_ANNOUNCE_PROTO_NAME: &str = "/block-announces"; const PROTOCOL_NAME: &str = "/foo"; /// Builds two nodes and their associated events stream. @@ -196,10 +195,6 @@ async fn notifications_state_consistent() { }, // Add new events here. - future::Either::Left(Event::SyncConnected { .. }) => {}, - future::Either::Right(Event::SyncConnected { .. }) => {}, - future::Either::Left(Event::SyncDisconnected { .. }) => {}, - future::Either::Right(Event::SyncDisconnected { .. }) => {}, future::Either::Left(Event::Dht(_)) => {}, future::Either::Right(Event::Dht(_)) => {}, }; @@ -208,6 +203,7 @@ async fn notifications_state_consistent() { #[tokio::test] async fn lots_of_incoming_peers_works() { + sp_tracing::try_init_simple(); let listen_addr = config::build_multiaddr![Memory(rand::random::())]; let (main_node, _) = TestNetworkBuilder::new() @@ -241,6 +237,7 @@ async fn lots_of_incoming_peers_works() { let mut timer = futures_timer::Delay::new(Duration::from_secs(3600 * 24 * 7)).fuse(); let mut event_stream = event_stream.fuse(); + let mut sync_protocol_name = None; loop { futures::select! { _ = timer => { @@ -249,15 +246,21 @@ async fn lots_of_incoming_peers_works() { } ev = event_stream.next() => { match ev.unwrap() { - Event::NotificationStreamOpened { remote, .. } => { + Event::NotificationStreamOpened { protocol, remote, .. } => { + if let None = sync_protocol_name { + sync_protocol_name = Some(protocol.clone()); + } + assert_eq!(remote, main_node_peer_id); // Test succeeds after 5 seconds. This timer is here in order to // detect a potential problem after opening. timer = futures_timer::Delay::new(Duration::from_secs(5)).fuse(); } - Event::NotificationStreamClosed { .. } => { - // Test failed. - panic!(); + Event::NotificationStreamClosed { protocol, .. } => { + if Some(protocol) != sync_protocol_name { + // Test failed. + panic!(); + } } _ => {} } @@ -282,10 +285,19 @@ async fn notifications_back_pressure() { let receiver = tokio::spawn(async move { let mut received_notifications = 0; + let mut sync_protocol_name = None; while received_notifications < TOTAL_NOTIFS { match events_stream2.next().await.unwrap() { - Event::NotificationStreamClosed { .. } => panic!(), + Event::NotificationStreamOpened { protocol, .. } => + if let None = sync_protocol_name { + sync_protocol_name = Some(protocol); + }, + Event::NotificationStreamClosed { protocol, .. } => { + if Some(&protocol) != sync_protocol_name.as_ref() { + panic!() + } + }, Event::NotificationsReceived { messages, .. } => for message in messages { assert_eq!(message.0, PROTOCOL_NAME.into()); @@ -387,42 +399,6 @@ async fn fallback_name_working() { receiver.await.unwrap(); } -// Disconnect peer by calling `Protocol::disconnect_peer()` with the supplied block announcement -// protocol name and verify that `SyncDisconnected` event is emitted -#[tokio::test] -async fn disconnect_sync_peer_using_block_announcement_protocol_name() { - let (node1, mut events_stream1, node2, mut events_stream2) = build_nodes_one_proto(); - - async fn wait_for_events(stream: &mut (impl Stream + std::marker::Unpin)) { - let mut notif_received = false; - let mut sync_received = false; - - while !notif_received || !sync_received { - match stream.next().await.unwrap() { - Event::NotificationStreamOpened { .. } => notif_received = true, - Event::SyncConnected { .. } => sync_received = true, - _ => {}, - }; - } - } - - wait_for_events(&mut events_stream1).await; - wait_for_events(&mut events_stream2).await; - - // disconnect peer using `PROTOCOL_NAME`, verify `NotificationStreamClosed` event is emitted - node2.disconnect_peer(node1.local_peer_id(), PROTOCOL_NAME.into()); - assert!(std::matches!( - events_stream2.next().await, - Some(Event::NotificationStreamClosed { .. }) - )); - let _ = events_stream2.next().await; // ignore the reopen event - - // now disconnect using `BLOCK_ANNOUNCE_PROTO_NAME`, verify that `SyncDisconnected` is - // emitted - node2.disconnect_peer(node1.local_peer_id(), BLOCK_ANNOUNCE_PROTO_NAME.into()); - assert!(std::matches!(events_stream2.next().await, Some(Event::SyncDisconnected { .. }))); -} - #[tokio::test] #[should_panic(expected = "don't match the transport")] async fn ensure_listen_addresses_consistent_with_transport_memory() { diff --git a/client/network/sync/Cargo.toml b/client/network/sync/Cargo.toml index bd51776d6..52ab0d15e 100644 --- a/client/network/sync/Cargo.toml +++ b/client/network/sync/Cargo.toml @@ -20,6 +20,7 @@ array-bytes = "4.1" async-trait = "0.1.58" codec = { package = "parity-scale-codec", version = "3.2.2", features = ["derive"] } futures = "0.3.21" +futures-timer = "3.0.2" libp2p = "0.50.0" log = "0.4.17" lru = "0.8.1" diff --git a/client/network/sync/src/engine.rs b/client/network/sync/src/engine.rs new file mode 100644 index 000000000..25cd3968c --- /dev/null +++ b/client/network/sync/src/engine.rs @@ -0,0 +1,924 @@ +// This file is part of Substrate. + +// Copyright (C) 2017-2023 Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +//! `SyncingEngine` is the actor responsible for syncing Substrate chain +//! to tip and keep the blockchain up to date with network updates. + +use crate::{ + service::{self, chain_sync::ToServiceCommand}, + ChainSync, ClientError, SyncingService, +}; + +use futures::{FutureExt, Stream, StreamExt}; +use libp2p::PeerId; +use lru::LruCache; +use prometheus_endpoint::{ + register, Gauge, GaugeVec, MetricSource, Opts, PrometheusError, Registry, SourcedGauge, U64, +}; + +use codec::{Decode, DecodeAll, Encode}; +use futures_timer::Delay; +use sc_client_api::{BlockBackend, HeaderBackend, ProofProvider}; +use sc_consensus::import_queue::ImportQueueService; +use sc_network_common::{ + config::{ + NetworkConfiguration, NonDefaultSetConfig, ProtocolId, SyncMode as SyncOperationMode, + }, + protocol::{event::Event, role::Roles, ProtocolName}, + sync::{ + message::{ + generic::{BlockData, BlockResponse}, + BlockAnnounce, BlockAnnouncesHandshake, BlockState, + }, + warp::WarpSyncParams, + BadPeer, ChainSync as ChainSyncT, ExtendedPeerInfo, PollBlockAnnounceValidation, SyncEvent, + SyncMode, + }, + utils::LruHashSet, +}; +use sc_utils::mpsc::{tracing_unbounded, TracingUnboundedReceiver, TracingUnboundedSender}; +use sp_blockchain::HeaderMetadata; +use sp_consensus::block_validation::BlockAnnounceValidator; +use sp_runtime::{ + traits::{Block as BlockT, CheckedSub, Header, NumberFor, Zero}, + SaturatedConversion, +}; + +use std::{ + collections::{HashMap, HashSet}, + num::NonZeroUsize, + pin::Pin, + sync::{ + atomic::{AtomicBool, AtomicUsize, Ordering}, + Arc, + }, + task::Poll, +}; + +/// Interval at which we perform time based maintenance +const TICK_TIMEOUT: std::time::Duration = std::time::Duration::from_millis(1100); + +/// When light node connects to the full node and the full node is behind light node +/// for at least `LIGHT_MAXIMAL_BLOCKS_DIFFERENCE` blocks, we consider it not useful +/// and disconnect to free connection slot. +const LIGHT_MAXIMAL_BLOCKS_DIFFERENCE: u64 = 8192; + +/// Maximum number of known block hashes to keep for a peer. +const MAX_KNOWN_BLOCKS: usize = 1024; // ~32kb per peer + LruHashSet overhead + +mod rep { + use sc_peerset::ReputationChange as Rep; + /// Reputation change when we are a light client and a peer is behind us. + pub const PEER_BEHIND_US_LIGHT: Rep = Rep::new(-(1 << 8), "Useless for a light peer"); + /// We received a message that failed to decode. + pub const BAD_MESSAGE: Rep = Rep::new(-(1 << 12), "Bad message"); + /// Peer has different genesis. + pub const GENESIS_MISMATCH: Rep = Rep::new_fatal("Genesis mismatch"); + /// Peer role does not match (e.g. light peer connecting to another light peer). + pub const BAD_ROLE: Rep = Rep::new_fatal("Unsupported role"); + /// Peer send us a block announcement that failed at validation. + pub const BAD_BLOCK_ANNOUNCEMENT: Rep = Rep::new(-(1 << 12), "Bad block announcement"); +} + +struct Metrics { + peers: Gauge, + queued_blocks: Gauge, + fork_targets: Gauge, + justifications: GaugeVec, +} + +impl Metrics { + fn register(r: &Registry, major_syncing: Arc) -> Result { + let _ = MajorSyncingGauge::register(r, major_syncing)?; + Ok(Self { + peers: { + let g = Gauge::new("substrate_sync_peers", "Number of peers we sync with")?; + register(g, r)? + }, + queued_blocks: { + let g = + Gauge::new("substrate_sync_queued_blocks", "Number of blocks in import queue")?; + register(g, r)? + }, + fork_targets: { + let g = Gauge::new("substrate_sync_fork_targets", "Number of fork sync targets")?; + register(g, r)? + }, + justifications: { + let g = GaugeVec::new( + Opts::new( + "substrate_sync_extra_justifications", + "Number of extra justifications requests", + ), + &["status"], + )?; + register(g, r)? + }, + }) + } +} + +/// The "major syncing" metric. +#[derive(Clone)] +pub struct MajorSyncingGauge(Arc); + +impl MajorSyncingGauge { + /// Registers the [`MajorSyncGauge`] metric whose value is + /// obtained from the given `AtomicBool`. + fn register(registry: &Registry, value: Arc) -> Result<(), PrometheusError> { + prometheus_endpoint::register( + SourcedGauge::new( + &Opts::new( + "substrate_sub_libp2p_is_major_syncing", + "Whether the node is performing a major sync or not.", + ), + MajorSyncingGauge(value), + )?, + registry, + )?; + + Ok(()) + } +} + +impl MetricSource for MajorSyncingGauge { + type N = u64; + + fn collect(&self, mut set: impl FnMut(&[&str], Self::N)) { + set(&[], self.0.load(Ordering::Relaxed) as u64); + } +} + +/// Peer information +#[derive(Debug)] +pub struct Peer { + pub info: ExtendedPeerInfo, + /// Holds a set of blocks known to this peer. + pub known_blocks: LruHashSet, +} + +pub struct SyncingEngine { + /// State machine that handles the list of in-progress requests. Only full node peers are + /// registered. + chain_sync: ChainSync, + + /// Blockchain client. + client: Arc, + + /// Number of peers we're connected to. + num_connected: Arc, + + /// Are we actively catching up with the chain? + is_major_syncing: Arc, + + /// Network service. + network_service: service::network::NetworkServiceHandle, + + /// Channel for receiving service commands + service_rx: TracingUnboundedReceiver>, + + /// Assigned roles. + roles: Roles, + + /// Genesis hash. + genesis_hash: B::Hash, + + /// Set of channels for other protocols that have subscribed to syncing events. + event_streams: Vec>, + + /// Interval at which we call `tick`. + tick_timeout: Delay, + + /// All connected peers. Contains both full and light node peers. + peers: HashMap>, + + /// List of nodes for which we perform additional logging because they are important for the + /// user. + important_peers: HashSet, + + /// Actual list of connected no-slot nodes. + default_peers_set_no_slot_connected_peers: HashSet, + + /// List of nodes that should never occupy peer slots. + default_peers_set_no_slot_peers: HashSet, + + /// Value that was passed as part of the configuration. Used to cap the number of full + /// nodes. + default_peers_set_num_full: usize, + + /// Number of slots to allocate to light nodes. + default_peers_set_num_light: usize, + + /// A cache for the data that was associated to a block announcement. + block_announce_data_cache: LruCache>, + + /// The `PeerId`'s of all boot nodes. + boot_node_ids: HashSet, + + /// Protocol name used for block announcements + block_announce_protocol_name: ProtocolName, + + /// Prometheus metrics. + metrics: Option, +} + +impl SyncingEngine +where + B: BlockT, + Client: HeaderBackend + + BlockBackend + + HeaderMetadata + + ProofProvider + + Send + + Sync + + 'static, +{ + pub fn new( + roles: Roles, + client: Arc, + metrics_registry: Option<&Registry>, + network_config: &NetworkConfiguration, + protocol_id: ProtocolId, + fork_id: &Option, + block_announce_validator: Box + Send>, + warp_sync_params: Option>, + network_service: service::network::NetworkServiceHandle, + import_queue: Box>, + block_request_protocol_name: ProtocolName, + state_request_protocol_name: ProtocolName, + warp_sync_protocol_name: Option, + ) -> Result<(Self, SyncingService, NonDefaultSetConfig), ClientError> { + let mode = match network_config.sync_mode { + SyncOperationMode::Full => SyncMode::Full, + SyncOperationMode::Fast { skip_proofs, storage_chain_mode } => + SyncMode::LightState { skip_proofs, storage_chain_mode }, + SyncOperationMode::Warp => SyncMode::Warp, + }; + let max_parallel_downloads = network_config.max_parallel_downloads; + let cache_capacity = NonZeroUsize::new( + (network_config.default_peers_set.in_peers as usize + + network_config.default_peers_set.out_peers as usize) + .max(1), + ) + .expect("cache capacity is not zero"); + let important_peers = { + let mut imp_p = HashSet::new(); + for reserved in &network_config.default_peers_set.reserved_nodes { + imp_p.insert(reserved.peer_id); + } + for reserved in network_config + .extra_sets + .iter() + .flat_map(|s| s.set_config.reserved_nodes.iter()) + { + imp_p.insert(reserved.peer_id); + } + imp_p.shrink_to_fit(); + imp_p + }; + let boot_node_ids = { + let mut list = HashSet::new(); + for node in &network_config.boot_nodes { + list.insert(node.peer_id); + } + list.shrink_to_fit(); + list + }; + let default_peers_set_no_slot_peers = { + let mut no_slot_p: HashSet = network_config + .default_peers_set + .reserved_nodes + .iter() + .map(|reserved| reserved.peer_id) + .collect(); + no_slot_p.shrink_to_fit(); + no_slot_p + }; + let default_peers_set_num_full = network_config.default_peers_set_num_full as usize; + let default_peers_set_num_light = { + let total = network_config.default_peers_set.out_peers + + network_config.default_peers_set.in_peers; + total.saturating_sub(network_config.default_peers_set_num_full) as usize + }; + + let (chain_sync, block_announce_config) = ChainSync::new( + mode, + client.clone(), + protocol_id, + fork_id, + roles, + block_announce_validator, + max_parallel_downloads, + warp_sync_params, + metrics_registry, + network_service.clone(), + import_queue, + block_request_protocol_name, + state_request_protocol_name, + warp_sync_protocol_name, + )?; + + let block_announce_protocol_name = block_announce_config.notifications_protocol.clone(); + let (tx, service_rx) = tracing_unbounded("mpsc_chain_sync", 100_000); + let num_connected = Arc::new(AtomicUsize::new(0)); + let is_major_syncing = Arc::new(AtomicBool::new(false)); + let genesis_hash = client + .block_hash(0u32.into()) + .ok() + .flatten() + .expect("Genesis block exists; qed"); + + Ok(( + Self { + roles, + client, + chain_sync, + network_service, + peers: HashMap::new(), + block_announce_data_cache: LruCache::new(cache_capacity), + block_announce_protocol_name, + num_connected: num_connected.clone(), + is_major_syncing: is_major_syncing.clone(), + service_rx, + genesis_hash, + important_peers, + default_peers_set_no_slot_connected_peers: HashSet::new(), + boot_node_ids, + default_peers_set_no_slot_peers, + default_peers_set_num_full, + default_peers_set_num_light, + event_streams: Vec::new(), + tick_timeout: Delay::new(TICK_TIMEOUT), + metrics: if let Some(r) = metrics_registry { + match Metrics::register(r, is_major_syncing.clone()) { + Ok(metrics) => Some(metrics), + Err(err) => { + log::error!(target: "sync", "Failed to register metrics {err:?}"); + None + }, + } + } else { + None + }, + }, + SyncingService::new(tx, num_connected, is_major_syncing), + block_announce_config, + )) + } + + /// Report Prometheus metrics. + pub fn report_metrics(&self) { + if let Some(metrics) = &self.metrics { + let n = u64::try_from(self.peers.len()).unwrap_or(std::u64::MAX); + metrics.peers.set(n); + + let m = self.chain_sync.metrics(); + + metrics.fork_targets.set(m.fork_targets.into()); + metrics.queued_blocks.set(m.queued_blocks.into()); + + metrics + .justifications + .with_label_values(&["pending"]) + .set(m.justifications.pending_requests.into()); + metrics + .justifications + .with_label_values(&["active"]) + .set(m.justifications.active_requests.into()); + metrics + .justifications + .with_label_values(&["failed"]) + .set(m.justifications.failed_requests.into()); + metrics + .justifications + .with_label_values(&["importing"]) + .set(m.justifications.importing_requests.into()); + } + } + + fn update_peer_info(&mut self, who: &PeerId) { + if let Some(info) = self.chain_sync.peer_info(who) { + if let Some(ref mut peer) = self.peers.get_mut(who) { + peer.info.best_hash = info.best_hash; + peer.info.best_number = info.best_number; + } + } + } + + /// Process the result of the block announce validation. + pub fn process_block_announce_validation_result( + &mut self, + validation_result: PollBlockAnnounceValidation, + ) { + let (header, _is_best, who) = match validation_result { + PollBlockAnnounceValidation::Skip => return, + PollBlockAnnounceValidation::Nothing { is_best: _, who, announce } => { + self.update_peer_info(&who); + + if let Some(data) = announce.data { + if !data.is_empty() { + self.block_announce_data_cache.put(announce.header.hash(), data); + } + } + + return + }, + PollBlockAnnounceValidation::ImportHeader { announce, is_best, who } => { + self.update_peer_info(&who); + + if let Some(data) = announce.data { + if !data.is_empty() { + self.block_announce_data_cache.put(announce.header.hash(), data); + } + } + + (announce.header, is_best, who) + }, + PollBlockAnnounceValidation::Failure { who, disconnect } => { + if disconnect { + self.network_service + .disconnect_peer(who, self.block_announce_protocol_name.clone()); + } + + self.network_service.report_peer(who, rep::BAD_BLOCK_ANNOUNCEMENT); + return + }, + }; + + // to import header from announced block let's construct response to request that normally + // would have been sent over network (but it is not in our case) + let blocks_to_import = self.chain_sync.on_block_data( + &who, + None, + BlockResponse { + id: 0, + blocks: vec![BlockData { + hash: header.hash(), + header: Some(header), + body: None, + indexed_body: None, + receipt: None, + message_queue: None, + justification: None, + justifications: None, + }], + }, + ); + + self.chain_sync.process_block_response_data(blocks_to_import); + } + + /// Push a block announce validation. + /// + /// It is required that [`ChainSync::poll_block_announce_validation`] is + /// called later to check for finished validations. The result of the validation + /// needs to be passed to [`SyncingEngine::process_block_announce_validation_result`] + /// to finish the processing. + /// + /// # Note + /// + /// This will internally create a future, but this future will not be registered + /// in the task before being polled once. So, it is required to call + /// [`ChainSync::poll_block_announce_validation`] to ensure that the future is + /// registered properly and will wake up the task when being ready. + pub fn push_block_announce_validation( + &mut self, + who: PeerId, + announce: BlockAnnounce, + ) { + let hash = announce.header.hash(); + + let peer = match self.peers.get_mut(&who) { + Some(p) => p, + None => { + log::error!(target: "sync", "Received block announce from disconnected peer {}", who); + debug_assert!(false); + return + }, + }; + peer.known_blocks.insert(hash); + + if peer.info.roles.is_full() { + let is_best = match announce.state.unwrap_or(BlockState::Best) { + BlockState::Best => true, + BlockState::Normal => false, + }; + + self.chain_sync.push_block_announce_validation(who, hash, announce, is_best); + } + } + + /// Make sure an important block is propagated to peers. + /// + /// In chain-based consensus, we often need to make sure non-best forks are + /// at least temporarily synced. + pub fn announce_block(&mut self, hash: B::Hash, data: Option>) { + let header = match self.client.header(hash) { + Ok(Some(header)) => header, + Ok(None) => { + log::warn!(target: "sync", "Trying to announce unknown block: {}", hash); + return + }, + Err(e) => { + log::warn!(target: "sync", "Error reading block header {}: {}", hash, e); + return + }, + }; + + // don't announce genesis block since it will be ignored + if header.number().is_zero() { + return + } + + let is_best = self.client.info().best_hash == hash; + log::debug!(target: "sync", "Reannouncing block {:?} is_best: {}", hash, is_best); + + let data = data + .or_else(|| self.block_announce_data_cache.get(&hash).cloned()) + .unwrap_or_default(); + + for (who, ref mut peer) in self.peers.iter_mut() { + let inserted = peer.known_blocks.insert(hash); + if inserted { + log::trace!(target: "sync", "Announcing block {:?} to {}", hash, who); + let message = BlockAnnounce { + header: header.clone(), + state: if is_best { Some(BlockState::Best) } else { Some(BlockState::Normal) }, + data: Some(data.clone()), + }; + + self.network_service.write_notification( + *who, + self.block_announce_protocol_name.clone(), + message.encode(), + ); + } + } + } + + /// Inform sync about new best imported block. + pub fn new_best_block_imported(&mut self, hash: B::Hash, number: NumberFor) { + log::debug!(target: "sync", "New best block imported {:?}/#{}", hash, number); + + self.chain_sync.update_chain_info(&hash, number); + self.network_service.set_notification_handshake( + self.block_announce_protocol_name.clone(), + BlockAnnouncesHandshake::::build(self.roles, number, hash, self.genesis_hash) + .encode(), + ) + } + + pub async fn run(mut self, mut stream: Pin + Send>>) { + loop { + futures::future::poll_fn(|cx| self.poll(cx, &mut stream)).await; + } + } + + pub fn poll( + &mut self, + cx: &mut std::task::Context, + event_stream: &mut Pin + Send>>, + ) -> Poll<()> { + self.num_connected.store(self.peers.len(), Ordering::Relaxed); + self.is_major_syncing + .store(self.chain_sync.status().state.is_major_syncing(), Ordering::Relaxed); + + while let Poll::Ready(()) = self.tick_timeout.poll_unpin(cx) { + self.report_metrics(); + self.tick_timeout.reset(TICK_TIMEOUT); + } + + while let Poll::Ready(Some(event)) = event_stream.poll_next_unpin(cx) { + match event { + Event::NotificationStreamOpened { + remote, protocol, received_handshake, .. + } => { + if protocol != self.block_announce_protocol_name { + continue + } + + match as DecodeAll>::decode_all( + &mut &received_handshake[..], + ) { + Ok(handshake) => { + if self.on_sync_peer_connected(remote, handshake).is_err() { + log::debug!( + target: "sync", + "Failed to register peer {remote:?}: {received_handshake:?}", + ); + } + }, + Err(err) => { + log::debug!( + target: "sync", + "Couldn't decode handshake sent by {}: {:?}: {}", + remote, + received_handshake, + err, + ); + self.network_service.report_peer(remote, rep::BAD_MESSAGE); + }, + } + }, + Event::NotificationStreamClosed { remote, protocol } => { + if protocol != self.block_announce_protocol_name { + continue + } + + if self.on_sync_peer_disconnected(remote).is_err() { + log::trace!( + target: "sync", + "Disconnected peer which had earlier been refused by on_sync_peer_connected {}", + remote + ); + } + }, + Event::NotificationsReceived { remote, messages } => { + for (protocol, message) in messages { + if protocol != self.block_announce_protocol_name { + continue + } + + if self.peers.contains_key(&remote) { + if let Ok(announce) = BlockAnnounce::decode(&mut message.as_ref()) { + self.push_block_announce_validation(remote, announce); + + // Make sure that the newly added block announce validation future + // was polled once to be registered in the task. + if let Poll::Ready(res) = + self.chain_sync.poll_block_announce_validation(cx) + { + self.process_block_announce_validation_result(res) + } + } else { + log::warn!(target: "sub-libp2p", "Failed to decode block announce"); + } + } else { + log::trace!( + target: "sync", + "Received sync for peer earlier refused by sync layer: {}", + remote + ); + } + } + }, + _ => {}, + } + } + + while let Poll::Ready(Some(event)) = self.service_rx.poll_next_unpin(cx) { + match event { + ToServiceCommand::SetSyncForkRequest(peers, hash, number) => { + self.chain_sync.set_sync_fork_request(peers, &hash, number); + }, + ToServiceCommand::EventStream(tx) => self.event_streams.push(tx), + ToServiceCommand::RequestJustification(hash, number) => + self.chain_sync.request_justification(&hash, number), + ToServiceCommand::ClearJustificationRequests => + self.chain_sync.clear_justification_requests(), + ToServiceCommand::BlocksProcessed(imported, count, results) => { + for result in self.chain_sync.on_blocks_processed(imported, count, results) { + match result { + Ok((id, req)) => self.chain_sync.send_block_request(id, req), + Err(BadPeer(id, repu)) => { + self.network_service + .disconnect_peer(id, self.block_announce_protocol_name.clone()); + self.network_service.report_peer(id, repu) + }, + } + } + }, + ToServiceCommand::JustificationImported(peer, hash, number, success) => { + self.chain_sync.on_justification_import(hash, number, success); + if !success { + log::info!(target: "sync", "💔 Invalid justification provided by {} for #{}", peer, hash); + self.network_service + .disconnect_peer(peer, self.block_announce_protocol_name.clone()); + self.network_service.report_peer( + peer, + sc_peerset::ReputationChange::new_fatal("Invalid justification"), + ); + } + }, + ToServiceCommand::AnnounceBlock(hash, data) => self.announce_block(hash, data), + ToServiceCommand::NewBestBlockImported(hash, number) => + self.new_best_block_imported(hash, number), + ToServiceCommand::Status(tx) => { + let _ = tx.send(self.chain_sync.status()); + }, + ToServiceCommand::NumActivePeers(tx) => { + let _ = tx.send(self.chain_sync.num_active_peers()); + }, + ToServiceCommand::SyncState(tx) => { + let _ = tx.send(self.chain_sync.status()); + }, + ToServiceCommand::BestSeenBlock(tx) => { + let _ = tx.send(self.chain_sync.status().best_seen_block); + }, + ToServiceCommand::NumSyncPeers(tx) => { + let _ = tx.send(self.chain_sync.status().num_peers); + }, + ToServiceCommand::NumQueuedBlocks(tx) => { + let _ = tx.send(self.chain_sync.status().queued_blocks); + }, + ToServiceCommand::NumDownloadedBlocks(tx) => { + let _ = tx.send(self.chain_sync.num_downloaded_blocks()); + }, + ToServiceCommand::NumSyncRequests(tx) => { + let _ = tx.send(self.chain_sync.num_sync_requests()); + }, + ToServiceCommand::PeersInfo(tx) => { + let peers_info = + self.peers.iter().map(|(id, peer)| (*id, peer.info.clone())).collect(); + let _ = tx.send(peers_info); + }, + ToServiceCommand::OnBlockFinalized(hash, header) => + self.chain_sync.on_block_finalized(&hash, *header.number()), + } + } + + while let Poll::Ready(result) = self.chain_sync.poll(cx) { + self.process_block_announce_validation_result(result); + } + + Poll::Pending + } + + /// Called by peer when it is disconnecting. + /// + /// Returns a result if the handshake of this peer was indeed accepted. + pub fn on_sync_peer_disconnected(&mut self, peer: PeerId) -> Result<(), ()> { + if self.important_peers.contains(&peer) { + log::warn!(target: "sync", "Reserved peer {} disconnected", peer); + } else { + log::debug!(target: "sync", "{} disconnected", peer); + } + + if self.peers.remove(&peer).is_some() { + self.chain_sync.peer_disconnected(&peer); + self.default_peers_set_no_slot_connected_peers.remove(&peer); + self.event_streams + .retain(|stream| stream.unbounded_send(SyncEvent::PeerDisconnected(peer)).is_ok()); + Ok(()) + } else { + Err(()) + } + } + + /// Called on the first connection between two peers on the default set, after their exchange + /// of handshake. + /// + /// Returns `Ok` if the handshake is accepted and the peer added to the list of peers we sync + /// from. + pub fn on_sync_peer_connected( + &mut self, + who: PeerId, + status: BlockAnnouncesHandshake, + ) -> Result<(), ()> { + log::trace!(target: "sync", "New peer {} {:?}", who, status); + + if self.peers.contains_key(&who) { + log::error!(target: "sync", "Called on_sync_peer_connected with already connected peer {}", who); + debug_assert!(false); + return Err(()) + } + + if status.genesis_hash != self.genesis_hash { + self.network_service.report_peer(who, rep::GENESIS_MISMATCH); + self.network_service + .disconnect_peer(who, self.block_announce_protocol_name.clone()); + + if self.important_peers.contains(&who) { + log::error!( + target: "sync", + "Reserved peer id `{}` is on a different chain (our genesis: {} theirs: {})", + who, + self.genesis_hash, + status.genesis_hash, + ); + } else if self.boot_node_ids.contains(&who) { + log::error!( + target: "sync", + "Bootnode with peer id `{}` is on a different chain (our genesis: {} theirs: {})", + who, + self.genesis_hash, + status.genesis_hash, + ); + } else { + log::debug!( + target: "sync", + "Peer is on different chain (our genesis: {} theirs: {})", + self.genesis_hash, status.genesis_hash + ); + } + + return Err(()) + } + + if self.roles.is_light() { + // we're not interested in light peers + if status.roles.is_light() { + log::debug!(target: "sync", "Peer {} is unable to serve light requests", who); + self.network_service.report_peer(who, rep::BAD_ROLE); + self.network_service + .disconnect_peer(who, self.block_announce_protocol_name.clone()); + return Err(()) + } + + // we don't interested in peers that are far behind us + let self_best_block = self.client.info().best_number; + let blocks_difference = self_best_block + .checked_sub(&status.best_number) + .unwrap_or_else(Zero::zero) + .saturated_into::(); + if blocks_difference > LIGHT_MAXIMAL_BLOCKS_DIFFERENCE { + log::debug!(target: "sync", "Peer {} is far behind us and will unable to serve light requests", who); + self.network_service.report_peer(who, rep::PEER_BEHIND_US_LIGHT); + self.network_service + .disconnect_peer(who, self.block_announce_protocol_name.clone()); + return Err(()) + } + } + + let no_slot_peer = self.default_peers_set_no_slot_peers.contains(&who); + let this_peer_reserved_slot: usize = if no_slot_peer { 1 } else { 0 }; + + if status.roles.is_full() && + self.chain_sync.num_peers() >= + self.default_peers_set_num_full + + self.default_peers_set_no_slot_connected_peers.len() + + this_peer_reserved_slot + { + log::debug!(target: "sync", "Too many full nodes, rejecting {}", who); + self.network_service + .disconnect_peer(who, self.block_announce_protocol_name.clone()); + return Err(()) + } + + if status.roles.is_light() && + (self.peers.len() - self.chain_sync.num_peers()) >= self.default_peers_set_num_light + { + // Make sure that not all slots are occupied by light clients. + log::debug!(target: "sync", "Too many light nodes, rejecting {}", who); + self.network_service + .disconnect_peer(who, self.block_announce_protocol_name.clone()); + return Err(()) + } + + let peer = Peer { + info: ExtendedPeerInfo { + roles: status.roles, + best_hash: status.best_hash, + best_number: status.best_number, + }, + known_blocks: LruHashSet::new( + NonZeroUsize::new(MAX_KNOWN_BLOCKS).expect("Constant is nonzero"), + ), + }; + + let req = if peer.info.roles.is_full() { + match self.chain_sync.new_peer(who, peer.info.best_hash, peer.info.best_number) { + Ok(req) => req, + Err(BadPeer(id, repu)) => { + self.network_service + .disconnect_peer(id, self.block_announce_protocol_name.clone()); + self.network_service.report_peer(id, repu); + return Err(()) + }, + } + } else { + None + }; + + log::debug!(target: "sync", "Connected {}", who); + + self.peers.insert(who, peer); + if no_slot_peer { + self.default_peers_set_no_slot_connected_peers.insert(who); + } + + if let Some(req) = req { + self.chain_sync.send_block_request(who, req); + } + + self.event_streams + .retain(|stream| stream.unbounded_send(SyncEvent::PeerConnected(who)).is_ok()); + + Ok(()) + } +} diff --git a/client/network/sync/src/lib.rs b/client/network/sync/src/lib.rs index f710215e7..23269b02b 100644 --- a/client/network/sync/src/lib.rs +++ b/client/network/sync/src/lib.rs @@ -30,20 +30,18 @@ pub mod block_request_handler; pub mod blocks; +pub mod engine; pub mod mock; mod schema; pub mod service; pub mod state; pub mod state_request_handler; -#[cfg(test)] -mod tests; pub mod warp; pub mod warp_request_handler; use crate::{ blocks::BlockCollection, schema::v1::{StateRequest, StateResponse}, - service::chain_sync::{ChainSyncInterfaceHandle, ToServiceCommand}, state::StateSync, warp::{WarpProofImportResult, WarpSync}, }; @@ -78,7 +76,7 @@ use sc_network_common::{ SyncState, SyncStatus, }, }; -use sc_utils::mpsc::{tracing_unbounded, TracingUnboundedReceiver}; +pub use service::chain_sync::SyncingService; use sp_arithmetic::traits::Saturating; use sp_blockchain::{Error as ClientError, HeaderBackend, HeaderMetadata}; use sp_consensus::{ @@ -327,8 +325,6 @@ pub struct ChainSync { import_existing: bool, /// Gap download process. gap_sync: Option>, - /// Channel for receiving service commands - service_rx: TracingUnboundedReceiver>, /// Handle for communicating with `NetworkService` network_service: service::network::NetworkServiceHandle, /// Protocol name used for block announcements @@ -1326,47 +1322,6 @@ where &mut self, cx: &mut std::task::Context, ) -> Poll> { - while let Poll::Ready(Some(event)) = self.service_rx.poll_next_unpin(cx) { - match event { - ToServiceCommand::SetSyncForkRequest(peers, hash, number) => { - self.set_sync_fork_request(peers, &hash, number); - }, - ToServiceCommand::RequestJustification(hash, number) => - self.request_justification(&hash, number), - ToServiceCommand::ClearJustificationRequests => self.clear_justification_requests(), - ToServiceCommand::BlocksProcessed(imported, count, results) => { - for result in self.on_blocks_processed(imported, count, results) { - match result { - Ok((id, req)) => self.send_block_request(id, req), - Err(BadPeer(id, repu)) => { - self.network_service - .disconnect_peer(id, self.block_announce_protocol_name.clone()); - self.network_service.report_peer(id, repu) - }, - } - } - }, - ToServiceCommand::JustificationImported(peer, hash, number, success) => { - self.on_justification_import(hash, number, success); - if !success { - info!(target: "sync", "💔 Invalid justification provided by {} for #{}", peer, hash); - self.network_service - .disconnect_peer(peer, self.block_announce_protocol_name.clone()); - self.network_service.report_peer( - peer, - sc_peerset::ReputationChange::new_fatal("Invalid justification"), - ); - } - }, - ToServiceCommand::BlockFinalized(hash, number) => { - self.on_block_finalized(&hash, number); - }, - ToServiceCommand::Status { pending_response } => { - let _ = pending_response.send(self.status()); - }, - } - } - // Should be called before `process_outbound_requests` to ensure // that a potential target block is directly leading to requests. if let Some(warp_sync) = &mut self.warp_sync { @@ -1448,8 +1403,7 @@ where block_request_protocol_name: ProtocolName, state_request_protocol_name: ProtocolName, warp_sync_protocol_name: Option, - ) -> Result<(Self, ChainSyncInterfaceHandle, NonDefaultSetConfig), ClientError> { - let (tx, service_rx) = tracing_unbounded("mpsc_chain_sync", 100_000); + ) -> Result<(Self, NonDefaultSetConfig), ClientError> { let block_announce_config = Self::get_block_announce_proto_config( protocol_id, fork_id, @@ -1483,7 +1437,6 @@ where warp_sync: None, import_existing: false, gap_sync: None, - service_rx, network_service, block_request_protocol_name, state_request_protocol_name, @@ -1509,7 +1462,7 @@ where }; sync.reset_sync_start_point()?; - Ok((sync, ChainSyncInterfaceHandle::new(tx), block_announce_config)) + Ok((sync, block_announce_config)) } /// Returns the median seen block number. @@ -3231,7 +3184,7 @@ mod test { let import_queue = Box::new(sc_consensus::import_queue::mock::MockImportQueueHandle::new()); let (_chain_sync_network_provider, chain_sync_network_handle) = NetworkServiceProvider::new(); - let (mut sync, _, _) = ChainSync::new( + let (mut sync, _) = ChainSync::new( SyncMode::Full, client.clone(), ProtocolId::from("test-protocol-name"), @@ -3297,7 +3250,7 @@ mod test { let (_chain_sync_network_provider, chain_sync_network_handle) = NetworkServiceProvider::new(); - let (mut sync, _, _) = ChainSync::new( + let (mut sync, _) = ChainSync::new( SyncMode::Full, client.clone(), ProtocolId::from("test-protocol-name"), @@ -3478,7 +3431,7 @@ mod test { let (_chain_sync_network_provider, chain_sync_network_handle) = NetworkServiceProvider::new(); - let (mut sync, _, _) = ChainSync::new( + let (mut sync, _) = ChainSync::new( SyncMode::Full, client.clone(), ProtocolId::from("test-protocol-name"), @@ -3604,7 +3557,7 @@ mod test { NetworkServiceProvider::new(); let info = client.info(); - let (mut sync, _, _) = ChainSync::new( + let (mut sync, _) = ChainSync::new( SyncMode::Full, client.clone(), ProtocolId::from("test-protocol-name"), @@ -3760,7 +3713,7 @@ mod test { let info = client.info(); - let (mut sync, _, _) = ChainSync::new( + let (mut sync, _) = ChainSync::new( SyncMode::Full, client.clone(), ProtocolId::from("test-protocol-name"), @@ -3901,7 +3854,7 @@ mod test { let info = client.info(); - let (mut sync, _, _) = ChainSync::new( + let (mut sync, _) = ChainSync::new( SyncMode::Full, client.clone(), ProtocolId::from("test-protocol-name"), @@ -4042,7 +3995,7 @@ mod test { let mut client = Arc::new(TestClientBuilder::new().build()); let blocks = (0..3).map(|_| build_block(&mut client, None, false)).collect::>(); - let (mut sync, _, _) = ChainSync::new( + let (mut sync, _) = ChainSync::new( SyncMode::Full, client.clone(), ProtocolId::from("test-protocol-name"), @@ -4087,7 +4040,7 @@ mod test { let empty_client = Arc::new(TestClientBuilder::new().build()); - let (mut sync, _, _) = ChainSync::new( + let (mut sync, _) = ChainSync::new( SyncMode::Full, empty_client.clone(), ProtocolId::from("test-protocol-name"), diff --git a/client/network/sync/src/service/chain_sync.rs b/client/network/sync/src/service/chain_sync.rs index 4d47899a3..99b419774 100644 --- a/client/network/sync/src/service/chain_sync.rs +++ b/client/network/sync/src/service/chain_sync.rs @@ -16,15 +16,26 @@ // You should have received a copy of the GNU General Public License // along with this program. If not, see . -use futures::channel::oneshot; +use futures::{channel::oneshot, Stream}; use libp2p::PeerId; + use sc_consensus::{BlockImportError, BlockImportStatus, JustificationSyncLink, Link}; -use sc_network_common::{service::NetworkSyncForkRequest, sync::SyncStatus}; -use sc_utils::mpsc::TracingUnboundedSender; +use sc_network_common::{ + service::{NetworkBlock, NetworkSyncForkRequest}, + sync::{ExtendedPeerInfo, SyncEvent, SyncEventStream, SyncStatus, SyncStatusProvider}, +}; +use sc_utils::mpsc::{tracing_unbounded, TracingUnboundedSender}; use sp_runtime::traits::{Block as BlockT, NumberFor}; +use std::{ + pin::Pin, + sync::{ + atomic::{AtomicBool, AtomicUsize, Ordering}, + Arc, + }, +}; + /// Commands send to `ChainSync` -#[derive(Debug)] pub enum ToServiceCommand { SetSyncForkRequest(Vec, B::Hash, NumberFor), RequestJustification(B::Hash, NumberFor), @@ -35,27 +46,105 @@ pub enum ToServiceCommand { Vec<(Result>, BlockImportError>, B::Hash)>, ), JustificationImported(PeerId, B::Hash, NumberFor, bool), - BlockFinalized(B::Hash, NumberFor), - Status { - pending_response: oneshot::Sender>, - }, + AnnounceBlock(B::Hash, Option>), + NewBestBlockImported(B::Hash, NumberFor), + EventStream(TracingUnboundedSender), + Status(oneshot::Sender>), + NumActivePeers(oneshot::Sender), + SyncState(oneshot::Sender>), + BestSeenBlock(oneshot::Sender>>), + NumSyncPeers(oneshot::Sender), + NumQueuedBlocks(oneshot::Sender), + NumDownloadedBlocks(oneshot::Sender), + NumSyncRequests(oneshot::Sender), + PeersInfo(oneshot::Sender)>>), + OnBlockFinalized(B::Hash, B::Header), + // Status { + // pending_response: oneshot::Sender>, + // }, } /// Handle for communicating with `ChainSync` asynchronously #[derive(Clone)] -pub struct ChainSyncInterfaceHandle { +pub struct SyncingService { tx: TracingUnboundedSender>, + /// Number of peers we're connected to. + num_connected: Arc, + /// Are we actively catching up with the chain? + is_major_syncing: Arc, } -impl ChainSyncInterfaceHandle { +impl SyncingService { /// Create new handle - pub fn new(tx: TracingUnboundedSender>) -> Self { - Self { tx } + pub fn new( + tx: TracingUnboundedSender>, + num_connected: Arc, + is_major_syncing: Arc, + ) -> Self { + Self { tx, num_connected, is_major_syncing } } - /// Notify ChainSync about finalized block - pub fn on_block_finalized(&self, hash: B::Hash, number: NumberFor) { - let _ = self.tx.unbounded_send(ToServiceCommand::BlockFinalized(hash, number)); + /// Get the number of active peers. + pub async fn num_active_peers(&self) -> Result { + let (tx, rx) = oneshot::channel(); + let _ = self.tx.unbounded_send(ToServiceCommand::NumActivePeers(tx)); + + rx.await + } + + /// Get best seen block. + pub async fn best_seen_block(&self) -> Result>, oneshot::Canceled> { + let (tx, rx) = oneshot::channel(); + let _ = self.tx.unbounded_send(ToServiceCommand::BestSeenBlock(tx)); + + rx.await + } + + /// Get the number of sync peers. + pub async fn num_sync_peers(&self) -> Result { + let (tx, rx) = oneshot::channel(); + let _ = self.tx.unbounded_send(ToServiceCommand::NumSyncPeers(tx)); + + rx.await + } + + /// Get the number of queued blocks. + pub async fn num_queued_blocks(&self) -> Result { + let (tx, rx) = oneshot::channel(); + let _ = self.tx.unbounded_send(ToServiceCommand::NumQueuedBlocks(tx)); + + rx.await + } + + /// Get the number of downloaded blocks. + pub async fn num_downloaded_blocks(&self) -> Result { + let (tx, rx) = oneshot::channel(); + let _ = self.tx.unbounded_send(ToServiceCommand::NumDownloadedBlocks(tx)); + + rx.await + } + + /// Get the number of sync requests. + pub async fn num_sync_requests(&self) -> Result { + let (tx, rx) = oneshot::channel(); + let _ = self.tx.unbounded_send(ToServiceCommand::NumSyncRequests(tx)); + + rx.await + } + + /// Get peer information. + pub async fn peers_info( + &self, + ) -> Result)>, oneshot::Canceled> { + let (tx, rx) = oneshot::channel(); + let _ = self.tx.unbounded_send(ToServiceCommand::PeersInfo(tx)); + + rx.await + } + + /// Notify the `SyncingEngine` that a block has been finalized. + pub fn on_block_finalized(&self, hash: B::Hash, header: B::Header) { + let _ = self.tx.unbounded_send(ToServiceCommand::OnBlockFinalized(hash, header)); } /// Get sync status @@ -63,15 +152,13 @@ impl ChainSyncInterfaceHandle { /// Returns an error if `ChainSync` has terminated. pub async fn status(&self) -> Result, ()> { let (tx, rx) = oneshot::channel(); - let _ = self.tx.unbounded_send(ToServiceCommand::Status { pending_response: tx }); + let _ = self.tx.unbounded_send(ToServiceCommand::Status(tx)); rx.await.map_err(|_| ()) } } -impl NetworkSyncForkRequest> - for ChainSyncInterfaceHandle -{ +impl NetworkSyncForkRequest> for SyncingService { /// Configure an explicit fork sync request. /// /// Note that this function should not be used for recent blocks. @@ -87,7 +174,7 @@ impl NetworkSyncForkRequest> } } -impl JustificationSyncLink for ChainSyncInterfaceHandle { +impl JustificationSyncLink for SyncingService { /// Request a justification for the given block from the network. /// /// On success, the justification will be passed to the import queue that was part at @@ -101,7 +188,18 @@ impl JustificationSyncLink for ChainSyncInterfaceHandle { } } -impl Link for ChainSyncInterfaceHandle { +#[async_trait::async_trait] +impl SyncStatusProvider for SyncingService { + /// Get high-level view of the syncing status. + async fn status(&self) -> Result, ()> { + let (rtx, rrx) = oneshot::channel(); + + let _ = self.tx.unbounded_send(ToServiceCommand::Status(rtx)); + rrx.await.map_err(|_| ()) + } +} + +impl Link for SyncingService { fn blocks_processed( &mut self, imported: usize, @@ -129,3 +227,32 @@ impl Link for ChainSyncInterfaceHandle { let _ = self.tx.unbounded_send(ToServiceCommand::RequestJustification(*hash, number)); } } + +impl SyncEventStream for SyncingService { + /// Get syncing event stream. + fn event_stream(&self, name: &'static str) -> Pin + Send>> { + let (tx, rx) = tracing_unbounded(name, 100_000); + let _ = self.tx.unbounded_send(ToServiceCommand::EventStream(tx)); + Box::pin(rx) + } +} + +impl NetworkBlock> for SyncingService { + fn announce_block(&self, hash: B::Hash, data: Option>) { + let _ = self.tx.unbounded_send(ToServiceCommand::AnnounceBlock(hash, data)); + } + + fn new_best_block_imported(&self, hash: B::Hash, number: NumberFor) { + let _ = self.tx.unbounded_send(ToServiceCommand::NewBestBlockImported(hash, number)); + } +} + +impl sp_consensus::SyncOracle for SyncingService { + fn is_major_syncing(&self) -> bool { + self.is_major_syncing.load(Ordering::Relaxed) + } + + fn is_offline(&self) -> bool { + self.num_connected.load(Ordering::Relaxed) == 0 + } +} diff --git a/client/network/sync/src/service/mock.rs b/client/network/sync/src/service/mock.rs index e66a9e561..2853616ad 100644 --- a/client/network/sync/src/service/mock.rs +++ b/client/network/sync/src/service/mock.rs @@ -23,7 +23,10 @@ use sc_network_common::{ config::MultiaddrWithPeerId, protocol::ProtocolName, request_responses::{IfDisconnected, RequestFailure}, - service::{NetworkPeers, NetworkRequest, NetworkSyncForkRequest}, + service::{ + NetworkNotification, NetworkPeers, NetworkRequest, NetworkSyncForkRequest, + NotificationSender, NotificationSenderError, + }, }; use sc_peerset::ReputationChange; use sp_runtime::traits::{Block as BlockT, NumberFor}; @@ -125,4 +128,14 @@ mockall::mock! { connect: IfDisconnected, ); } + + impl NetworkNotification for Network { + fn write_notification(&self, target: PeerId, protocol: ProtocolName, message: Vec); + fn notification_sender( + &self, + target: PeerId, + protocol: ProtocolName, + ) -> Result, NotificationSenderError>; + fn set_notification_handshake(&self, protocol: ProtocolName, handshake: Vec); + } } diff --git a/client/network/sync/src/service/network.rs b/client/network/sync/src/service/network.rs index de7e255f5..a1f4f27bb 100644 --- a/client/network/sync/src/service/network.rs +++ b/client/network/sync/src/service/network.rs @@ -21,16 +21,16 @@ use libp2p::PeerId; use sc_network_common::{ protocol::ProtocolName, request_responses::{IfDisconnected, RequestFailure}, - service::{NetworkPeers, NetworkRequest}, + service::{NetworkNotification, NetworkPeers, NetworkRequest}, }; use sc_peerset::ReputationChange; use sc_utils::mpsc::{tracing_unbounded, TracingUnboundedReceiver, TracingUnboundedSender}; use std::sync::Arc; /// Network-related services required by `sc-network-sync` -pub trait Network: NetworkPeers + NetworkRequest {} +pub trait Network: NetworkPeers + NetworkRequest + NetworkNotification {} -impl Network for T where T: NetworkPeers + NetworkRequest {} +impl Network for T where T: NetworkPeers + NetworkRequest + NetworkNotification {} /// Network service provider for `ChainSync` /// @@ -56,6 +56,12 @@ pub enum ToServiceCommand { oneshot::Sender, RequestFailure>>, IfDisconnected, ), + + /// Call `NetworkNotification::write_notification()` + WriteNotification(PeerId, ProtocolName, Vec), + + /// Call `NetworkNotification::set_notification_handshake()` + SetNotificationHandshake(ProtocolName, Vec), } /// Handle that is (temporarily) passed to `ChainSync` so it can @@ -94,6 +100,20 @@ impl NetworkServiceHandle { .tx .unbounded_send(ToServiceCommand::StartRequest(who, protocol, request, tx, connect)); } + + /// Send notification to peer + pub fn write_notification(&self, who: PeerId, protocol: ProtocolName, message: Vec) { + let _ = self + .tx + .unbounded_send(ToServiceCommand::WriteNotification(who, protocol, message)); + } + + /// Set handshake for the notification protocol. + pub fn set_notification_handshake(&self, protocol: ProtocolName, handshake: Vec) { + let _ = self + .tx + .unbounded_send(ToServiceCommand::SetNotificationHandshake(protocol, handshake)); + } } impl NetworkServiceProvider { @@ -114,6 +134,10 @@ impl NetworkServiceProvider { service.report_peer(peer, reputation_change), ToServiceCommand::StartRequest(peer, protocol, request, tx, connect) => service.start_request(peer, protocol, request, tx, connect), + ToServiceCommand::WriteNotification(peer, protocol, message) => + service.write_notification(peer, protocol, message), + ToServiceCommand::SetNotificationHandshake(protocol, handshake) => + service.set_notification_handshake(protocol, handshake), } } } diff --git a/client/network/sync/src/tests.rs b/client/network/sync/src/tests.rs deleted file mode 100644 index d56e84093..000000000 --- a/client/network/sync/src/tests.rs +++ /dev/null @@ -1,78 +0,0 @@ -// This file is part of Substrate. - -// Copyright (C) Parity Technologies (UK) Ltd. -// SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 - -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with this program. If not, see . - -use crate::{service::network::NetworkServiceProvider, ChainSync, ForkTarget}; - -use libp2p::PeerId; -use sc_network_common::{ - config::ProtocolId, - protocol::{ - role::{Role, Roles}, - ProtocolName, - }, - service::NetworkSyncForkRequest, - sync::ChainSync as ChainSyncT, -}; -use sp_consensus::block_validation::DefaultBlockAnnounceValidator; -use sp_core::H256; -use std::{sync::Arc, task::Poll}; -use substrate_test_runtime_client::{TestClientBuilder, TestClientBuilderExt as _}; - -// verify that the fork target map is empty, then submit a new sync fork request, -// poll `ChainSync` and verify that a new sync fork request has been registered -#[tokio::test] -async fn delegate_to_chainsync() { - let import_queue = Box::new(sc_consensus::import_queue::mock::MockImportQueueHandle::new()); - let (_chain_sync_network_provider, chain_sync_network_handle) = NetworkServiceProvider::new(); - let (mut chain_sync, chain_sync_service, _) = ChainSync::new( - sc_network_common::sync::SyncMode::Full, - Arc::new(TestClientBuilder::with_default_backend().build_with_longest_chain().0), - ProtocolId::from("test-protocol-name"), - &Some(String::from("test-fork-id")), - Roles::from(&Role::Full), - Box::new(DefaultBlockAnnounceValidator), - 1u32, - None, - None, - chain_sync_network_handle, - import_queue, - ProtocolName::from("block-request"), - ProtocolName::from("state-request"), - None, - ) - .unwrap(); - - let hash = H256::random(); - let in_number = 1337u64; - let peers = (0..3).map(|_| PeerId::random()).collect::>(); - - assert!(chain_sync.fork_targets.is_empty()); - chain_sync_service.set_sync_fork_request(peers, hash, in_number); - - futures::future::poll_fn(|cx| { - let _ = chain_sync.poll(cx); - Poll::Ready(()) - }) - .await; - - if let Some(ForkTarget { number, .. }) = chain_sync.fork_targets.get(&hash) { - assert_eq!(number, &in_number); - } else { - panic!("expected to contain `ForkTarget`"); - } -} diff --git a/client/network/test/src/lib.rs b/client/network/test/src/lib.rs index 544f4c5f6..95381dd7b 100644 --- a/client/network/test/src/lib.rs +++ b/client/network/test/src/lib.rs @@ -46,24 +46,24 @@ use sc_consensus::{ ForkChoiceStrategy, ImportQueue, ImportResult, JustificationImport, JustificationSyncLink, LongestChain, Verifier, }; -use sc_network::{ - config::{NetworkConfiguration, RequestResponseConfig, Role, SyncMode}, - Multiaddr, NetworkService, NetworkWorker, -}; +use sc_network::{Multiaddr, NetworkService, NetworkWorker}; use sc_network_common::{ config::{ - MultiaddrWithPeerId, NonDefaultSetConfig, NonReservedPeerMode, ProtocolId, TransportConfig, + MultiaddrWithPeerId, NetworkConfiguration, NonDefaultSetConfig, NonReservedPeerMode, + ProtocolId, RequestResponseConfig, Role, SyncMode, TransportConfig, }, protocol::{role::Roles, ProtocolName}, - service::{NetworkBlock, NetworkStateInfo, NetworkSyncForkRequest}, + service::{NetworkBlock, NetworkEventStream, NetworkStateInfo, NetworkSyncForkRequest}, sync::warp::{ AuthorityList, EncodedProof, SetId, VerificationResult, WarpSyncParams, WarpSyncProvider, }, }; use sc_network_light::light_client_requests::handler::LightClientRequestHandler; use sc_network_sync::{ - block_request_handler::BlockRequestHandler, service::network::NetworkServiceProvider, - state_request_handler::StateRequestHandler, warp_request_handler, ChainSync, + block_request_handler::BlockRequestHandler, + service::{chain_sync::SyncingService, network::NetworkServiceProvider}, + state_request_handler::StateRequestHandler, + warp_request_handler, }; use sc_service::client::Client; use sp_blockchain::{ @@ -241,7 +241,8 @@ pub struct Peer { block_import: BlockImportAdapter, select_chain: Option>, backend: Option>, - network: NetworkWorker::Hash, PeersFullClient>, + network: NetworkWorker::Hash>, + sync_service: Arc>, imported_blocks_stream: Pin> + Send>>, finality_notification_stream: Pin> + Send>>, listen_addr: Multiaddr, @@ -259,7 +260,7 @@ where /// Returns true if we're major syncing. pub fn is_major_syncing(&self) -> bool { - self.network.service().is_major_syncing() + self.sync_service.is_major_syncing() } // Returns a clone of the local SelectChain, only available on full nodes @@ -275,23 +276,23 @@ where } /// Returns the number of downloaded blocks. - pub fn num_downloaded_blocks(&self) -> usize { - self.network.num_downloaded_blocks() + pub async fn num_downloaded_blocks(&self) -> usize { + self.sync_service.num_downloaded_blocks().await.unwrap() } /// Returns true if we have no peer. pub fn is_offline(&self) -> bool { - self.num_peers() == 0 + self.sync_service.is_offline() } /// Request a justification for the given block. pub fn request_justification(&self, hash: &::Hash, number: NumberFor) { - self.network.service().request_justification(hash, number); + self.sync_service.request_justification(hash, number); } /// Announces an important block on the network. pub fn announce_block(&self, hash: ::Hash, data: Option>) { - self.network.service().announce_block(hash, data); + self.sync_service.announce_block(hash, data); } /// Request explicit fork sync. @@ -301,7 +302,7 @@ where hash: ::Hash, number: NumberFor, ) { - self.network.service().set_sync_fork_request(peers, hash, number); + self.sync_service.set_sync_fork_request(peers, hash, number); } /// Add blocks to the peer -- edit the block before adding @@ -402,14 +403,14 @@ where futures::executor::block_on(self.block_import.import_block(import_block, cache)) .expect("block_import failed"); if announce_block { - self.network.service().announce_block(hash, None); + self.sync_service.announce_block(hash, None); } hashes.push(hash); at = hash; } if inform_sync_about_new_best_block { - self.network.new_best_block_imported( + self.sync_service.new_best_block_imported( at, *full_client.header(at).ok().flatten().unwrap().number(), ); @@ -525,8 +526,12 @@ where self.network.service() } + pub fn sync_service(&self) -> &Arc> { + &self.sync_service + } + /// Get a reference to the network worker. - pub fn network(&self) -> &NetworkWorker::Hash, PeersFullClient> { + pub fn network(&self) -> &NetworkWorker::Hash> { &self.network } @@ -728,13 +733,13 @@ pub struct FullPeerConfig { } #[async_trait::async_trait] -pub trait TestNetFactory: Default + Sized +pub trait TestNetFactory: Default + Sized + Send where >::Transaction: Send, { type Verifier: 'static + Verifier; type BlockImport: BlockImport + Clone + Send + Sync + 'static; - type PeerData: Default; + type PeerData: Default + Send; /// This one needs to be implemented! fn make_verifier(&self, client: PeersClient, peer_data: &Self::PeerData) -> Self::Verifier; @@ -742,6 +747,7 @@ where /// Get reference to peer. fn peer(&mut self, i: usize) -> &mut Peer; fn peers(&self) -> &Vec>; + fn peers_mut(&mut self) -> &mut Vec>; fn mut_peers>)>( &mut self, closure: F, @@ -900,31 +906,25 @@ where let (chain_sync_network_provider, chain_sync_network_handle) = NetworkServiceProvider::new(); - let (chain_sync, chain_sync_service, block_announce_config) = ChainSync::new( - match network_config.sync_mode { - SyncMode::Full => sc_network_common::sync::SyncMode::Full, - SyncMode::Fast { skip_proofs, storage_chain_mode } => - sc_network_common::sync::SyncMode::LightState { - skip_proofs, - storage_chain_mode, - }, - SyncMode::Warp => sc_network_common::sync::SyncMode::Warp, - }, - client.clone(), - protocol_id.clone(), - &fork_id, - Roles::from(if config.is_authority { &Role::Authority } else { &Role::Full }), - block_announce_validator, - network_config.max_parallel_downloads, - Some(warp_sync_params), - None, - chain_sync_network_handle, - import_queue.service(), - block_request_protocol_config.name.clone(), - state_request_protocol_config.name.clone(), - Some(warp_protocol_config.name.clone()), - ) - .unwrap(); + let (engine, sync_service, block_announce_config) = + sc_network_sync::engine::SyncingEngine::new( + Roles::from(if config.is_authority { &Role::Authority } else { &Role::Full }), + client.clone(), + None, + &network_config, + protocol_id.clone(), + &fork_id, + block_announce_validator, + Some(warp_sync_params), + chain_sync_network_handle, + import_queue.service(), + block_request_protocol_config.name.clone(), + state_request_protocol_config.name.clone(), + Some(warp_protocol_config.name.clone()), + ) + .unwrap(); + let sync_service_import_queue = Box::new(sync_service.clone()); + let sync_service = Arc::new(sync_service.clone()); let network = NetworkWorker::new(sc_network::config::Params { role: if config.is_authority { Role::Authority } else { Role::Full }, @@ -935,8 +935,6 @@ where chain: client.clone(), protocol_id, fork_id, - chain_sync: Box::new(chain_sync), - chain_sync_service: Box::new(chain_sync_service.clone()), metrics_registry: None, block_announce_config, request_response_protocol_configs: [ @@ -955,8 +953,14 @@ where tokio::spawn(async move { chain_sync_network_provider.run(service).await; }); + + tokio::spawn(async move { + import_queue.run(sync_service_import_queue).await; + }); + + let service = network.service().clone(); tokio::spawn(async move { - import_queue.run(Box::new(chain_sync_service)).await; + engine.run(service.event_stream("syncing")).await; }); self.mut_peers(move |peers| { @@ -979,6 +983,7 @@ where block_import, verifier, network, + sync_service, listen_addr, }); }); @@ -989,71 +994,75 @@ where tokio::spawn(f); } - /// Polls the testnet until all nodes are in sync. + /// Polls the testnet until all peers are connected to each other. /// /// Must be executed in a task context. - fn poll_until_sync(&mut self, cx: &mut FutureContext) -> Poll<()> { + fn poll_until_connected(&mut self, cx: &mut FutureContext) -> Poll<()> { self.poll(cx); - // Return `NotReady` if there's a mismatch in the highest block number. + let num_peers = self.peers().len(); + if self.peers().iter().all(|p| p.num_peers() == num_peers - 1) { + return Poll::Ready(()) + } + + Poll::Pending + } + + async fn is_in_sync(&mut self) -> bool { let mut highest = None; - for peer in self.peers().iter() { - if peer.is_major_syncing() || peer.network.num_queued_blocks() != 0 { - return Poll::Pending + let peers = self.peers_mut(); + + for peer in peers { + if peer.sync_service.is_major_syncing() || + peer.sync_service.num_queued_blocks().await.unwrap() != 0 + { + return false } - if peer.network.num_sync_requests() != 0 { - return Poll::Pending + if peer.sync_service.num_sync_requests().await.unwrap() != 0 { + return false } match (highest, peer.client.info().best_hash) { (None, b) => highest = Some(b), (Some(ref a), ref b) if a == b => {}, - (Some(_), _) => return Poll::Pending, + (Some(_), _) => return false, } } - Poll::Ready(()) - } - /// Polls the testnet until theres' no activiy of any kind. - /// - /// Must be executed in a task context. - fn poll_until_idle(&mut self, cx: &mut FutureContext) -> Poll<()> { - self.poll(cx); + true + } - for peer in self.peers().iter() { - if peer.is_major_syncing() || peer.network.num_queued_blocks() != 0 { - return Poll::Pending + async fn is_idle(&mut self) -> bool { + let peers = self.peers_mut(); + for peer in peers { + if peer.sync_service.num_queued_blocks().await.unwrap() != 0 { + return false } - if peer.network.num_sync_requests() != 0 { - return Poll::Pending + if peer.sync_service.num_sync_requests().await.unwrap() != 0 { + return false } } - Poll::Ready(()) - } - - /// Polls the testnet until all peers are connected to each other. - /// - /// Must be executed in a task context. - fn poll_until_connected(&mut self, cx: &mut FutureContext) -> Poll<()> { - self.poll(cx); - - let num_peers = self.peers().len(); - if self.peers().iter().all(|p| p.num_peers() == num_peers - 1) { - return Poll::Ready(()) - } - - Poll::Pending + true } - /// Run the network until we are sync'ed. + /// Blocks the current thread until we are sync'ed. + /// Wait until we are sync'ed. /// - /// Calls `poll_until_sync` repeatedly. /// (If we've not synced within 10 mins then panic rather than hang.) async fn run_until_sync(&mut self) { - timeout( - Duration::from_secs(10 * 60), - futures::future::poll_fn::<(), _>(|cx| self.poll_until_sync(cx)), - ) + timeout(Duration::from_secs(10 * 60), async { + loop { + futures::future::poll_fn::<(), _>(|cx| { + self.poll(cx); + Poll::Ready(()) + }) + .await; + + if self.is_in_sync().await { + break + } + } + }) .await .expect("sync didn't happen within 10 mins"); } @@ -1062,7 +1071,17 @@ where /// /// Calls `poll_until_idle` repeatedly with the runtime passed as parameter. async fn run_until_idle(&mut self) { - futures::future::poll_fn::<(), _>(|cx| self.poll_until_idle(cx)).await; + loop { + futures::future::poll_fn::<(), _>(|cx| { + self.poll(cx); + Poll::Ready(()) + }) + .await; + + if self.is_idle().await { + break + } + } } /// Run the network until all peers are connected to each other. @@ -1095,14 +1114,14 @@ where while let Poll::Ready(Some(notification)) = peer.imported_blocks_stream.as_mut().poll_next(cx) { - peer.network.service().announce_block(notification.hash, None); + peer.sync_service.announce_block(notification.hash, None); } // We poll `finality_notification_stream`. while let Poll::Ready(Some(notification)) = peer.finality_notification_stream.as_mut().poll_next(cx) { - peer.network.on_block_finalized(notification.hash, notification.header); + peer.sync_service.on_block_finalized(notification.hash, notification.header); } } }); @@ -1142,6 +1161,10 @@ impl TestNetFactory for TestNet { &self.peers } + fn peers_mut(&mut self) -> &mut Vec> { + &mut self.peers + } + fn mut_peers>)>(&mut self, closure: F) { closure(&mut self.peers); } @@ -1189,6 +1212,10 @@ impl TestNetFactory for JustificationTestNet { self.0.peers() } + fn peers_mut(&mut self) -> &mut Vec> { + self.0.peers_mut() + } + fn mut_peers>)>( &mut self, closure: F, diff --git a/client/network/test/src/sync.rs b/client/network/test/src/sync.rs index 490ec134c..d87b03fb3 100644 --- a/client/network/test/src/sync.rs +++ b/client/network/test/src/sync.rs @@ -652,12 +652,12 @@ async fn imports_stale_once() { // check that NEW block is imported from announce message let new_hash = net.peer(0).push_blocks(1, false).pop().unwrap(); import_with_announce(&mut net, new_hash).await; - assert_eq!(net.peer(1).num_downloaded_blocks(), 1); + assert_eq!(net.peer(1).num_downloaded_blocks().await, 1); // check that KNOWN STALE block is imported from announce message let known_stale_hash = net.peer(0).push_blocks_at(BlockId::Number(0), 1, true).pop().unwrap(); import_with_announce(&mut net, known_stale_hash).await; - assert_eq!(net.peer(1).num_downloaded_blocks(), 2); + assert_eq!(net.peer(1).num_downloaded_blocks().await, 2); } #[tokio::test(flavor = "multi_thread", worker_threads = 2)] @@ -820,7 +820,7 @@ async fn sync_to_tip_requires_that_sync_protocol_is_informed_about_best_block() assert!(!net.peer(1).has_block(block_hash)); // Make sync protocol aware of the best block - net.peer(0).network_service().new_best_block_imported(block_hash, 3); + net.peer(0).sync_service().new_best_block_imported(block_hash, 3); net.run_until_idle().await; // Connect another node that should now sync to the tip @@ -865,8 +865,8 @@ async fn sync_to_tip_when_we_sync_together_with_multiple_peers() { assert!(!net.peer(2).has_block(block_hash)); - net.peer(0).network_service().new_best_block_imported(block_hash, 10_000); - net.peer(0).network_service().announce_block(block_hash, None); + net.peer(0).sync_service().new_best_block_imported(block_hash, 10_000); + net.peer(0).sync_service().announce_block(block_hash, None); while !net.peer(2).has_block(block_hash) && !net.peer(1).has_block(block_hash) { net.run_until_idle().await; @@ -1045,14 +1045,17 @@ async fn syncs_all_forks_from_single_peer() { let branch1 = net.peer(0).push_blocks_at(BlockId::Number(10), 2, true).pop().unwrap(); // Wait till peer 1 starts downloading - futures::future::poll_fn::<(), _>(|cx| { - net.poll(cx); - if net.peer(1).network().best_seen_block() != Some(12) { - return Poll::Pending + loop { + futures::future::poll_fn::<(), _>(|cx| { + net.poll(cx); + Poll::Ready(()) + }) + .await; + + if net.peer(1).sync_service().best_seen_block().await.unwrap() == Some(12) { + break } - Poll::Ready(()) - }) - .await; + } // Peer 0 produces and announces another fork let branch2 = net.peer(0).push_blocks_at(BlockId::Number(10), 2, false).pop().unwrap(); diff --git a/client/network/transactions/src/lib.rs b/client/network/transactions/src/lib.rs index d4d08d2ab..48bb39494 100644 --- a/client/network/transactions/src/lib.rs +++ b/client/network/transactions/src/lib.rs @@ -37,6 +37,7 @@ use sc_network_common::{ error, protocol::{event::Event, role::ObservedRole, ProtocolName}, service::{NetworkEventStream, NetworkNotification, NetworkPeers}, + sync::{SyncEvent, SyncEventStream}, utils::{interval, LruHashSet}, ExHashT, }; @@ -161,14 +162,17 @@ impl TransactionsHandlerPrototype { pub fn build< B: BlockT + 'static, H: ExHashT, - S: NetworkPeers + NetworkEventStream + NetworkNotification + sp_consensus::SyncOracle, + N: NetworkPeers + NetworkEventStream + NetworkNotification, + S: SyncEventStream + sp_consensus::SyncOracle, >( self, - service: S, + network: N, + sync: S, transaction_pool: Arc>, metrics_registry: Option<&Registry>, - ) -> error::Result<(TransactionsHandler, TransactionsHandlerController)> { - let event_stream = service.event_stream("transactions-handler"); + ) -> error::Result<(TransactionsHandler, TransactionsHandlerController)> { + let net_event_stream = network.event_stream("transactions-handler-net"); + let sync_event_stream = sync.event_stream("transactions-handler-sync"); let (to_handler, from_controller) = tracing_unbounded("mpsc_transactions_handler", 100_000); let handler = TransactionsHandler { @@ -178,8 +182,10 @@ impl TransactionsHandlerPrototype { .fuse(), pending_transactions: FuturesUnordered::new(), pending_transactions_peers: HashMap::new(), - service, - event_stream: event_stream.fuse(), + network, + sync, + net_event_stream: net_event_stream.fuse(), + sync_event_stream: sync_event_stream.fuse(), peers: HashMap::new(), transaction_pool, from_controller, @@ -228,7 +234,8 @@ enum ToHandler { pub struct TransactionsHandler< B: BlockT + 'static, H: ExHashT, - S: NetworkPeers + NetworkEventStream + NetworkNotification + sp_consensus::SyncOracle, + N: NetworkPeers + NetworkEventStream + NetworkNotification, + S: SyncEventStream + sp_consensus::SyncOracle, > { protocol_name: ProtocolName, /// Interval at which we call `propagate_transactions`. @@ -241,9 +248,13 @@ pub struct TransactionsHandler< /// multiple times concurrently. pending_transactions_peers: HashMap>, /// Network service to use to send messages and manage peers. - service: S, + network: N, + /// Syncing service. + sync: S, /// Stream of networking events. - event_stream: stream::Fuse + Send>>>, + net_event_stream: stream::Fuse + Send>>>, + /// Receiver for syncing-related events. + sync_event_stream: stream::Fuse + Send>>>, // All connected peers peers: HashMap>, transaction_pool: Arc>, @@ -260,11 +271,12 @@ struct Peer { role: ObservedRole, } -impl TransactionsHandler +impl TransactionsHandler where B: BlockT + 'static, H: ExHashT, - S: NetworkPeers + NetworkEventStream + NetworkNotification + sp_consensus::SyncOracle, + N: NetworkPeers + NetworkEventStream + NetworkNotification, + S: SyncEventStream + sp_consensus::SyncOracle, { /// Turns the [`TransactionsHandler`] into a future that should run forever and not be /// interrupted. @@ -281,7 +293,7 @@ where warn!(target: "sub-libp2p", "Inconsistent state, no peers for pending transaction!"); } }, - network_event = self.event_stream.next() => { + network_event = self.net_event_stream.next() => { if let Some(network_event) = network_event { self.handle_network_event(network_event).await; } else { @@ -289,6 +301,14 @@ where return; } }, + sync_event = self.sync_event_stream.next() => { + if let Some(sync_event) = sync_event { + self.handle_sync_event(sync_event); + } else { + // Syncing has seemingly closed. Closing as well. + return; + } + } message = self.from_controller.select_next_some() => { match message { ToHandler::PropagateTransaction(hash) => self.propagate_transaction(&hash), @@ -299,13 +319,12 @@ where } } - async fn handle_network_event(&mut self, event: Event) { + fn handle_sync_event(&mut self, event: SyncEvent) { match event { - Event::Dht(_) => {}, - Event::SyncConnected { remote } => { + SyncEvent::PeerConnected(remote) => { let addr = iter::once(multiaddr::Protocol::P2p(remote.into())) .collect::(); - let result = self.service.add_peers_to_reserved_set( + let result = self.network.add_peers_to_reserved_set( self.protocol_name.clone(), iter::once(addr).collect(), ); @@ -313,13 +332,18 @@ where log::error!(target: "sync", "Add reserved peer failed: {}", err); } }, - Event::SyncDisconnected { remote } => { - self.service.remove_peers_from_reserved_set( + SyncEvent::PeerDisconnected(remote) => { + self.network.remove_peers_from_reserved_set( self.protocol_name.clone(), iter::once(remote).collect(), ); }, + } + } + async fn handle_network_event(&mut self, event: Event) { + match event { + Event::Dht(_) => {}, Event::NotificationStreamOpened { remote, protocol, role, .. } if protocol == self.protocol_name => { @@ -365,7 +389,7 @@ where /// Called when peer sends us new transactions fn on_transactions(&mut self, who: PeerId, transactions: Transactions) { // Accept transactions only when node is not major syncing - if self.service.is_major_syncing() { + if self.sync.is_major_syncing() { trace!(target: "sync", "{} Ignoring transactions while major syncing", who); return } @@ -385,7 +409,7 @@ where let hash = self.transaction_pool.hash_of(&t); peer.known_transactions.insert(hash.clone()); - self.service.report_peer(who, rep::ANY_TRANSACTION); + self.network.report_peer(who, rep::ANY_TRANSACTION); match self.pending_transactions_peers.entry(hash.clone()) { Entry::Vacant(entry) => { @@ -406,9 +430,9 @@ where fn on_handle_transaction_import(&mut self, who: PeerId, import: TransactionImport) { match import { TransactionImport::KnownGood => - self.service.report_peer(who, rep::ANY_TRANSACTION_REFUND), - TransactionImport::NewGood => self.service.report_peer(who, rep::GOOD_TRANSACTION), - TransactionImport::Bad => self.service.report_peer(who, rep::BAD_TRANSACTION), + self.network.report_peer(who, rep::ANY_TRANSACTION_REFUND), + TransactionImport::NewGood => self.network.report_peer(who, rep::GOOD_TRANSACTION), + TransactionImport::Bad => self.network.report_peer(who, rep::BAD_TRANSACTION), TransactionImport::None => {}, } } @@ -416,7 +440,7 @@ where /// Propagate one transaction. pub fn propagate_transaction(&mut self, hash: &H) { // Accept transactions only when node is not major syncing - if self.service.is_major_syncing() { + if self.sync.is_major_syncing() { return } @@ -453,7 +477,7 @@ where propagated_to.entry(hash).or_default().push(who.to_base58()); } trace!(target: "sync", "Sending {} transactions to {}", to_send.len(), who); - self.service + self.network .write_notification(*who, self.protocol_name.clone(), to_send.encode()); } } @@ -468,7 +492,7 @@ where /// Call when we must propagate ready transactions to peers. fn propagate_transactions(&mut self) { // Accept transactions only when node is not major syncing - if self.service.is_major_syncing() { + if self.sync.is_major_syncing() { return } diff --git a/client/service/src/builder.rs b/client/service/src/builder.rs index 2c690dcd2..ea4b63000 100644 --- a/client/service/src/builder.rs +++ b/client/service/src/builder.rs @@ -38,18 +38,19 @@ use sc_client_db::{Backend, DatabaseSettings}; use sc_consensus::import_queue::ImportQueue; use sc_executor::RuntimeVersionOf; use sc_keystore::LocalKeystore; -use sc_network::{config::SyncMode, NetworkService}; +use sc_network::NetworkService; use sc_network_bitswap::BitswapRequestHandler; use sc_network_common::{ + config::SyncMode, protocol::role::Roles, - service::{NetworkStateInfo, NetworkStatusProvider}, + service::{NetworkEventStream, NetworkStateInfo, NetworkStatusProvider}, sync::warp::WarpSyncParams, }; use sc_network_light::light_client_requests::handler::LightClientRequestHandler; use sc_network_sync::{ - block_request_handler::BlockRequestHandler, service::network::NetworkServiceProvider, - state_request_handler::StateRequestHandler, - warp_request_handler::RequestHandler as WarpSyncRequestHandler, ChainSync, + block_request_handler::BlockRequestHandler, engine::SyncingEngine, + service::network::NetworkServiceProvider, state_request_handler::StateRequestHandler, + warp_request_handler::RequestHandler as WarpSyncRequestHandler, SyncingService, }; use sc_rpc::{ author::AuthorApiServer, @@ -349,12 +350,7 @@ where /// Shared network instance implementing a set of mandatory traits. pub trait SpawnTaskNetwork: - sc_offchain::NetworkProvider - + NetworkStateInfo - + NetworkStatusProvider - + Send - + Sync - + 'static + sc_offchain::NetworkProvider + NetworkStateInfo + NetworkStatusProvider + Send + Sync + 'static { } @@ -363,7 +359,7 @@ where Block: BlockT, T: sc_offchain::NetworkProvider + NetworkStateInfo - + NetworkStatusProvider + + NetworkStatusProvider + Send + Sync + 'static, @@ -394,6 +390,8 @@ pub struct SpawnTasksParams<'a, TBl: BlockT, TCl, TExPool, TRpc, Backend> { /// Controller for transactions handlers pub tx_handler_controller: sc_network_transactions::TransactionsHandlerController<::Hash>, + /// Syncing service. + pub sync_service: Arc>, /// Telemetry instance for this node. pub telemetry: Option<&'a mut Telemetry>, } @@ -471,6 +469,7 @@ where network, system_rpc_tx, tx_handler_controller, + sync_service, telemetry, } = params; @@ -533,7 +532,12 @@ where spawn_handle.spawn( "telemetry-periodic-send", None, - metrics_service.run(client.clone(), transaction_pool.clone(), network.clone()), + metrics_service.run( + client.clone(), + transaction_pool.clone(), + network.clone(), + sync_service.clone(), + ), ); let rpc_id_provider = config.rpc_id_provider.take(); @@ -560,7 +564,12 @@ where spawn_handle.spawn( "informant", None, - sc_informant::build(client.clone(), network, config.informant_output_format), + sc_informant::build( + client.clone(), + network, + sync_service.clone(), + config.informant_output_format, + ), ); task_manager.keep_alive((config.base_path, rpc)); @@ -771,6 +780,7 @@ pub fn build_network( TracingUnboundedSender>, sc_network_transactions::TransactionsHandlerController<::Hash>, NetworkStarter, + Arc>, ), Error, > @@ -876,27 +886,23 @@ where }; let (chain_sync_network_provider, chain_sync_network_handle) = NetworkServiceProvider::new(); - let (chain_sync, chain_sync_service, block_announce_config) = ChainSync::new( - match config.network.sync_mode { - SyncMode::Full => sc_network_common::sync::SyncMode::Full, - SyncMode::Fast { skip_proofs, storage_chain_mode } => - sc_network_common::sync::SyncMode::LightState { skip_proofs, storage_chain_mode }, - SyncMode::Warp => sc_network_common::sync::SyncMode::Warp, - }, + let (engine, sync_service, block_announce_config) = SyncingEngine::new( + Roles::from(&config.role), client.clone(), + config.prometheus_config.as_ref().map(|config| config.registry.clone()).as_ref(), + &config.network, protocol_id.clone(), &config.chain_spec.fork_id().map(ToOwned::to_owned), - Roles::from(&config.role), block_announce_validator, - config.network.max_parallel_downloads, warp_sync_params, - config.prometheus_config.as_ref().map(|config| config.registry.clone()).as_ref(), chain_sync_network_handle, import_queue.service(), block_request_protocol_config.name.clone(), state_request_protocol_config.name.clone(), warp_sync_protocol_config.as_ref().map(|config| config.name.clone()), )?; + let sync_service_import_queue = sync_service.clone(); + let sync_service = Arc::new(sync_service); request_response_protocol_configs.push(config.network.ipfs_server.then(|| { let (handler, protocol_config) = BitswapRequestHandler::new(client.clone()); @@ -916,8 +922,6 @@ where chain: client.clone(), protocol_id: protocol_id.clone(), fork_id: config.chain_spec.fork_id().map(ToOwned::to_owned), - chain_sync: Box::new(chain_sync), - chain_sync_service: Box::new(chain_sync_service.clone()), metrics_registry: config.prometheus_config.as_ref().map(|config| config.registry.clone()), block_announce_config, request_response_protocol_configs: request_response_protocol_configs @@ -953,6 +957,7 @@ where let (tx_handler, tx_handler_controller) = transactions_handler_proto.build( network.clone(), + sync_service.clone(), Arc::new(TransactionPoolAdapter { pool: transaction_pool, client: client.clone() }), config.prometheus_config.as_ref().map(|config| &config.registry), )?; @@ -963,11 +968,10 @@ where Some("networking"), chain_sync_network_provider.run(network.clone()), ); - spawn_handle.spawn( - "import-queue", - None, - import_queue.run(Box::new(chain_sync_service.clone())), - ); + spawn_handle.spawn("import-queue", None, import_queue.run(Box::new(sync_service_import_queue))); + + let event_stream = network.event_stream("syncing"); + spawn_handle.spawn("syncing", None, engine.run(event_stream)); let (system_rpc_tx, system_rpc_rx) = tracing_unbounded("mpsc_system_rpc", 10_000); spawn_handle.spawn( @@ -976,7 +980,7 @@ where build_system_rpc_future( config.role.clone(), network_mut.service().clone(), - chain_sync_service.clone(), + sync_service.clone(), client.clone(), system_rpc_rx, has_bootnodes, @@ -984,7 +988,7 @@ where ); let future = - build_network_future(network_mut, client, chain_sync_service, config.announce_block); + build_network_future(network_mut, client, sync_service.clone(), config.announce_block); // TODO: Normally, one is supposed to pass a list of notifications protocols supported by the // node through the `NetworkConfiguration` struct. But because this function doesn't know in @@ -1022,7 +1026,13 @@ where future.await }); - Ok((network, system_rpc_tx, tx_handler_controller, NetworkStarter(network_start_tx))) + Ok(( + network, + system_rpc_tx, + tx_handler_controller, + NetworkStarter(network_start_tx), + sync_service.clone(), + )) } /// Object used to start the network. diff --git a/client/service/src/config.rs b/client/service/src/config.rs index 7f3a2e3c0..8e843b58f 100644 --- a/client/service/src/config.rs +++ b/client/service/src/config.rs @@ -22,11 +22,14 @@ pub use sc_client_api::execution_extensions::{ExecutionStrategies, ExecutionStra pub use sc_client_db::{BlocksPruning, Database, DatabaseSource, PruningMode}; pub use sc_executor::{WasmExecutionMethod, WasmtimeInstantiationStrategy}; pub use sc_network::{ - config::{NetworkConfiguration, NodeKeyConfig, Role}, + config::{NetworkConfiguration, Role}, Multiaddr, }; pub use sc_network_common::{ - config::{MultiaddrWithPeerId, NonDefaultSetConfig, ProtocolId, SetConfig, TransportConfig}, + config::{ + MultiaddrWithPeerId, NodeKeyConfig, NonDefaultSetConfig, ProtocolId, SetConfig, + TransportConfig, + }, request_responses::{ IncomingRequest, OutgoingResponse, ProtocolConfig as RequestResponseConfig, }, @@ -34,7 +37,7 @@ pub use sc_network_common::{ use prometheus_endpoint::Registry; use sc_chain_spec::ChainSpec; -use sc_network::config::SyncMode; +use sc_network_common::config::SyncMode; pub use sc_telemetry::TelemetryEndpoints; pub use sc_transaction_pool::Options as TransactionPoolOptions; use sp_core::crypto::SecretString; diff --git a/client/service/src/lib.rs b/client/service/src/lib.rs index 6bafa9936..8e674ca44 100644 --- a/client/service/src/lib.rs +++ b/client/service/src/lib.rs @@ -46,7 +46,7 @@ use sc_network_common::{ config::MultiaddrWithPeerId, service::{NetworkBlock, NetworkPeers}, }; -use sc_network_sync::service::chain_sync::ChainSyncInterfaceHandle; +use sc_network_sync::SyncingService; use sc_utils::mpsc::TracingUnboundedReceiver; use sp_blockchain::HeaderMetadata; use sp_consensus::SyncOracle; @@ -158,9 +158,9 @@ async fn build_network_future< + 'static, H: sc_network_common::ExHashT, >( - network: sc_network::NetworkWorker, + network: sc_network::NetworkWorker, client: Arc, - chain_sync_service: ChainSyncInterfaceHandle, + sync_service: Arc>, announce_imported_blocks: bool, ) { let mut imported_blocks_stream = client.import_notification_stream().fuse(); @@ -168,8 +168,6 @@ async fn build_network_future< // Stream of finalized blocks reported by the client. let mut finality_notification_stream = client.finality_notification_stream().fuse(); - let network_service = network.service().clone(); - let network_run = network.run().fuse(); pin_mut!(network_run); @@ -188,11 +186,11 @@ async fn build_network_future< }; if announce_imported_blocks { - network_service.announce_block(notification.hash, None); + sync_service.announce_block(notification.hash, None); } if notification.is_new_best { - network_service.new_best_block_imported( + sync_service.new_best_block_imported( notification.hash, *notification.header.number(), ); @@ -201,7 +199,7 @@ async fn build_network_future< // List of blocks that the client has finalized. notification = finality_notification_stream.select_next_some() => { - chain_sync_service.on_block_finalized(notification.hash, *notification.header.number()); + sync_service.on_block_finalized(notification.hash, notification.header); } // Drive the network. Shut down the network future if `NetworkWorker` has terminated. @@ -228,7 +226,7 @@ async fn build_system_rpc_future< >( role: Role, network_service: Arc>, - chain_sync_service: ChainSyncInterfaceHandle, + sync_service: Arc>, client: Arc, mut rpc_rx: TracingUnboundedReceiver>, should_have_peers: bool, @@ -244,23 +242,21 @@ async fn build_system_rpc_future< }; match req { - sc_rpc::system::Request::Health(sender) => { - let peers = network_service.peers_debug_info().await; - if let Ok(peers) = peers { + sc_rpc::system::Request::Health(sender) => match sync_service.peers_info().await { + Ok(info) => { let _ = sender.send(sc_rpc::system::Health { - peers: peers.len(), - is_syncing: network_service.is_major_syncing(), + peers: info.len(), + is_syncing: sync_service.is_major_syncing(), should_have_peers, }); - } else { - break - } + }, + Err(_) => log::error!("`SyncingEngine` shut down"), }, sc_rpc::system::Request::LocalPeerId(sender) => { let _ = sender.send(network_service.local_peer_id().to_base58()); }, sc_rpc::system::Request::LocalListenAddresses(sender) => { - let peer_id = network_service.local_peer_id().into(); + let peer_id = (network_service.local_peer_id()).into(); let p2p_proto_suffix = sc_network::multiaddr::Protocol::P2p(peer_id); let addresses = network_service .listen_addresses() @@ -269,12 +265,10 @@ async fn build_system_rpc_future< .collect(); let _ = sender.send(addresses); }, - sc_rpc::system::Request::Peers(sender) => { - let peers = network_service.peers_debug_info().await; - if let Ok(peers) = peers { + sc_rpc::system::Request::Peers(sender) => match sync_service.peers_info().await { + Ok(info) => { let _ = sender.send( - peers - .into_iter() + info.into_iter() .map(|(peer_id, p)| sc_rpc::system::PeerInfo { peer_id: peer_id.to_base58(), roles: format!("{:?}", p.roles), @@ -283,9 +277,8 @@ async fn build_system_rpc_future< }) .collect(), ); - } else { - break - } + }, + Err(_) => log::error!("`SyncingEngine` shut down"), }, sc_rpc::system::Request::NetworkState(sender) => { let network_state = network_service.network_state().await; @@ -339,21 +332,21 @@ async fn build_system_rpc_future< sc_rpc::system::Request::SyncState(sender) => { use sc_rpc::system::SyncState; - let best_number = client.info().best_number; - - let Ok(status) = chain_sync_service.status().await else { - debug!("`ChainSync` has terminated, shutting down the system RPC future."); - return - }; - - let _ = sender.send(SyncState { - starting_block, - current_block: best_number, - highest_block: status.best_seen_block.unwrap_or(best_number), - }); + match sync_service.best_seen_block().await { + Ok(best_seen_block) => { + let best_number = client.info().best_number; + let _ = sender.send(SyncState { + starting_block, + current_block: best_number, + highest_block: best_seen_block.unwrap_or(best_number), + }); + }, + Err(_) => log::error!("`SyncingEngine` shut down"), + } }, } } + debug!("`NetworkWorker` has terminated, shutting down the system RPC future."); } diff --git a/client/service/src/metrics.rs b/client/service/src/metrics.rs index 19c64e953..967e3133d 100644 --- a/client/service/src/metrics.rs +++ b/client/service/src/metrics.rs @@ -23,7 +23,10 @@ use futures_timer::Delay; use prometheus_endpoint::{register, Gauge, GaugeVec, Opts, PrometheusError, Registry, U64}; use sc_client_api::{ClientInfo, UsageProvider}; use sc_network::config::Role; -use sc_network_common::service::{NetworkStatus, NetworkStatusProvider}; +use sc_network_common::{ + service::{NetworkStatus, NetworkStatusProvider}, + sync::{SyncStatus, SyncStatusProvider}, +}; use sc_telemetry::{telemetry, TelemetryHandle, SUBSTRATE_INFO}; use sc_transaction_pool_api::{MaintainedTransactionPool, PoolStatus}; use sc_utils::metrics::register_globals; @@ -175,16 +178,18 @@ impl MetricsService { /// Returns a never-ending `Future` that performs the /// metric and telemetry updates with information from /// the given sources. - pub async fn run( + pub async fn run( mut self, client: Arc, transactions: Arc, network: TNet, + syncing: TSync, ) where TBl: Block, TCl: ProvideRuntimeApi + UsageProvider, TExPool: MaintainedTransactionPool::Hash>, - TNet: NetworkStatusProvider, + TNet: NetworkStatusProvider, + TSync: SyncStatusProvider, { let mut timer = Delay::new(Duration::from_secs(0)); let timer_interval = Duration::from_secs(5); @@ -196,8 +201,11 @@ impl MetricsService { // Try to get the latest network information. let net_status = network.status().await.ok(); + // Try to get the latest syncing information. + let sync_status = syncing.status().await.ok(); + // Update / Send the metrics. - self.update(&client.usage_info(), &transactions.status(), net_status); + self.update(&client.usage_info(), &transactions.status(), net_status, sync_status); // Schedule next tick. timer.reset(timer_interval); @@ -208,7 +216,8 @@ impl MetricsService { &mut self, info: &ClientInfo, txpool_status: &PoolStatus, - net_status: Option>, + net_status: Option, + sync_status: Option>, ) { let now = Instant::now(); let elapsed = (now - self.last_update).as_secs(); @@ -273,10 +282,12 @@ impl MetricsService { "bandwidth_download" => avg_bytes_per_sec_inbound, "bandwidth_upload" => avg_bytes_per_sec_outbound, ); + } + if let Some(sync_status) = sync_status { if let Some(metrics) = self.metrics.as_ref() { let best_seen_block: Option = - net_status.best_seen_block.map(|num: NumberFor| { + sync_status.best_seen_block.map(|num: NumberFor| { UniqueSaturatedInto::::unique_saturated_into(num) }); diff --git a/client/service/test/Cargo.toml b/client/service/test/Cargo.toml index 488fe76c3..f58b8c295 100644 --- a/client/service/test/Cargo.toml +++ b/client/service/test/Cargo.toml @@ -27,6 +27,7 @@ sc-consensus = { version = "0.10.0-dev", path = "../../../client/consensus/commo sc-executor = { version = "0.10.0-dev", path = "../../executor" } sc-network = { version = "0.10.0-dev", path = "../../network" } sc-network-common = { version = "0.10.0-dev", path = "../../network/common" } +sc-network-sync = { version = "0.10.0-dev", path = "../../network/sync" } sc-service = { version = "0.10.0-dev", features = ["test-helpers"], path = "../../service" } sc-transaction-pool-api = { version = "4.0.0-dev", path = "../../../client/transaction-pool/api" } sp-api = { version = "4.0.0-dev", path = "../../../primitives/api" } diff --git a/client/service/test/src/lib.rs b/client/service/test/src/lib.rs index 655aff242..b1a09a062 100644 --- a/client/service/test/src/lib.rs +++ b/client/service/test/src/lib.rs @@ -27,6 +27,7 @@ use sc_network_common::{ config::{MultiaddrWithPeerId, TransportConfig}, service::{NetworkBlock, NetworkPeers, NetworkStateInfo}, }; +use sc_network_sync::SyncingService; use sc_service::{ client::Client, config::{BasePath, DatabaseSource, KeystoreConfig}, @@ -79,6 +80,7 @@ pub trait TestNetNode: fn network( &self, ) -> Arc::Hash>>; + fn sync(&self) -> &Arc>; fn spawn_handle(&self) -> SpawnTaskHandle; } @@ -87,6 +89,7 @@ pub struct TestNetComponents { client: Arc>, transaction_pool: Arc, network: Arc::Hash>>, + sync: Arc>, } impl @@ -96,9 +99,16 @@ impl task_manager: TaskManager, client: Arc>, network: Arc::Hash>>, + sync: Arc>, transaction_pool: Arc, ) -> Self { - Self { client, transaction_pool, network, task_manager: Arc::new(Mutex::new(task_manager)) } + Self { + client, + sync, + transaction_pool, + network, + task_manager: Arc::new(Mutex::new(task_manager)), + } } } @@ -111,6 +121,7 @@ impl Clone client: self.client.clone(), transaction_pool: self.transaction_pool.clone(), network: self.network.clone(), + sync: self.sync.clone(), } } } @@ -151,6 +162,9 @@ where ) -> Arc::Hash>> { self.network.clone() } + fn sync(&self) -> &Arc> { + &self.sync + } fn spawn_handle(&self) -> SpawnTaskHandle { self.task_manager.lock().spawn_handle() } @@ -477,7 +491,7 @@ pub fn sync( let info = network.full_nodes[0].1.client().info(); network.full_nodes[0] .1 - .network() + .sync() .new_best_block_imported(info.best_hash, info.best_number); network.full_nodes[0].3.clone() }; From 0f5a22a04d944d6e46877313a929c2cec99d3c88 Mon Sep 17 00:00:00 2001 From: Liu-Cheng Xu Date: Tue, 7 Mar 2023 04:39:32 +0800 Subject: [PATCH 200/558] Expose `new_full_parts_with_genesis_builder` from sc-service (#13539) I forgot this in https://github.com/paritytech/substrate/pull/12291 --- client/service/src/lib.rs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/client/service/src/lib.rs b/client/service/src/lib.rs index 8e674ca44..9dab81a5b 100644 --- a/client/service/src/lib.rs +++ b/client/service/src/lib.rs @@ -58,8 +58,9 @@ use sp_runtime::{ pub use self::{ builder::{ build_network, build_offchain_workers, new_client, new_db_backend, new_full_client, - new_full_parts, spawn_tasks, BuildNetworkParams, KeystoreContainer, NetworkStarter, - SpawnTasksParams, TFullBackend, TFullCallExecutor, TFullClient, + new_full_parts, new_full_parts_with_genesis_builder, spawn_tasks, BuildNetworkParams, + KeystoreContainer, NetworkStarter, SpawnTasksParams, TFullBackend, TFullCallExecutor, + TFullClient, }, client::{ClientConfig, LocalCallExecutor}, error::Error, From 2aa2b4ef88af23e970da55d7b9b420f8f552ba99 Mon Sep 17 00:00:00 2001 From: Dmitry Markin Date: Tue, 7 Mar 2023 10:57:26 +0300 Subject: [PATCH 201/558] Make unbounded channels size warning exact (part 2) (#13504) --- Cargo.lock | 4 +- .../common/src/import_queue/basic_queue.rs | 58 +-- .../common/src/import_queue/buffered_link.rs | 4 +- client/service/src/task_manager/mod.rs | 2 +- client/service/test/Cargo.toml | 1 + client/service/test/src/client/mod.rs | 20 +- client/utils/Cargo.toml | 3 +- client/utils/README.md | 17 +- client/utils/src/lib.rs | 14 +- client/utils/src/metrics.rs | 4 - client/utils/src/mpsc.rs | 389 ++++++------------ client/utils/src/notification.rs | 2 +- client/utils/src/pubsub.rs | 2 +- .../src/pubsub/tests/normal_operation.rs | 3 +- 14 files changed, 196 insertions(+), 327 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 72b5943d4..e0cc2c107 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -9231,6 +9231,7 @@ name = "sc-service-test" version = "2.0.0" dependencies = [ "array-bytes", + "async-channel", "fdlimit", "futures", "log", @@ -9435,13 +9436,14 @@ dependencies = [ name = "sc-utils" version = "4.0.0-dev" dependencies = [ - "backtrace", + "async-channel", "futures", "futures-timer", "lazy_static", "log", "parking_lot 0.12.1", "prometheus", + "sp-arithmetic", "tokio-test", ] diff --git a/client/consensus/common/src/import_queue/basic_queue.rs b/client/consensus/common/src/import_queue/basic_queue.rs index 06122491e..640470815 100644 --- a/client/consensus/common/src/import_queue/basic_queue.rs +++ b/client/consensus/common/src/import_queue/basic_queue.rs @@ -118,8 +118,8 @@ impl BasicQueueHandle { } pub fn close(&mut self) { - self.justification_sender.close_channel(); - self.block_import_sender.close_channel(); + self.justification_sender.close(); + self.block_import_sender.close(); } } @@ -597,11 +597,11 @@ mod tests { fn prioritizes_finality_work_over_block_import() { let (result_sender, mut result_port) = buffered_link::buffered_link(100_000); - let (worker, mut finality_sender, mut block_import_sender) = + let (worker, finality_sender, block_import_sender) = BlockImportWorker::new(result_sender, (), Box::new(()), Some(Box::new(())), None); futures::pin_mut!(worker); - let mut import_block = |n| { + let import_block = |n| { let header = Header { parent_hash: Hash::random(), number: n, @@ -612,35 +612,37 @@ mod tests { let hash = header.hash(); - block_on(block_import_sender.send(worker_messages::ImportBlocks( - BlockOrigin::Own, - vec![IncomingBlock { - hash, - header: Some(header), - body: None, - indexed_body: None, - justifications: None, - origin: None, - allow_missing_state: false, - import_existing: false, - state: None, - skip_execution: false, - }], - ))) - .unwrap(); + block_import_sender + .unbounded_send(worker_messages::ImportBlocks( + BlockOrigin::Own, + vec![IncomingBlock { + hash, + header: Some(header), + body: None, + indexed_body: None, + justifications: None, + origin: None, + allow_missing_state: false, + import_existing: false, + state: None, + skip_execution: false, + }], + )) + .unwrap(); hash }; - let mut import_justification = || { + let import_justification = || { let hash = Hash::random(); - block_on(finality_sender.send(worker_messages::ImportJustification( - libp2p::PeerId::random(), - hash, - 1, - (*b"TEST", Vec::new()), - ))) - .unwrap(); + finality_sender + .unbounded_send(worker_messages::ImportJustification( + libp2p::PeerId::random(), + hash, + 1, + (*b"TEST", Vec::new()), + )) + .unwrap(); hash }; diff --git a/client/consensus/common/src/import_queue/buffered_link.rs b/client/consensus/common/src/import_queue/buffered_link.rs index 5c95cbc42..c23a4b0d5 100644 --- a/client/consensus/common/src/import_queue/buffered_link.rs +++ b/client/consensus/common/src/import_queue/buffered_link.rs @@ -53,7 +53,7 @@ use super::BlockImportResult; /// can be used to buffer commands, and the receiver can be used to poll said commands and transfer /// them to another link. `queue_size_warning` sets the warning threshold of the channel queue size. pub fn buffered_link( - queue_size_warning: i64, + queue_size_warning: usize, ) -> (BufferedLinkSender, BufferedLinkReceiver) { let (tx, rx) = tracing_unbounded("mpsc_buffered_link", queue_size_warning); let tx = BufferedLinkSender { tx }; @@ -166,7 +166,7 @@ impl BufferedLinkReceiver { } /// Close the channel. - pub fn close(&mut self) { + pub fn close(&mut self) -> bool { self.rx.get_mut().close() } } diff --git a/client/service/src/task_manager/mod.rs b/client/service/src/task_manager/mod.rs index 8dc4748b0..afccee903 100644 --- a/client/service/src/task_manager/mod.rs +++ b/client/service/src/task_manager/mod.rs @@ -281,7 +281,7 @@ impl SpawnEssentialTaskHandle { let essential_failed = self.essential_failed_tx.clone(); let essential_task = std::panic::AssertUnwindSafe(task).catch_unwind().map(move |_| { log::error!("Essential task `{}` failed. Shutting down service.", name); - let _ = essential_failed.close_channel(); + let _ = essential_failed.close(); }); let _ = self.inner.spawn_inner(name, group, essential_task, task_type); diff --git a/client/service/test/Cargo.toml b/client/service/test/Cargo.toml index f58b8c295..94a844aa7 100644 --- a/client/service/test/Cargo.toml +++ b/client/service/test/Cargo.toml @@ -12,6 +12,7 @@ repository = "https://github.com/paritytech/substrate/" targets = ["x86_64-unknown-linux-gnu"] [dependencies] +async-channel = "1.8.0" array-bytes = "4.1" fdlimit = "0.2.1" futures = "0.3.21" diff --git a/client/service/test/src/client/mod.rs b/client/service/test/src/client/mod.rs index f8988c5a4..c7b19ca8b 100644 --- a/client/service/test/src/client/mod.rs +++ b/client/service/test/src/client/mod.rs @@ -16,6 +16,7 @@ // You should have received a copy of the GNU General Public License // along with this program. If not, see . +use async_channel::TryRecvError; use futures::executor::block_on; use parity_scale_codec::{Decode, Encode, Joiner}; use sc_block_builder::BlockBuilderProvider; @@ -175,16 +176,17 @@ fn finality_notification_check( finalized: &[Hash], stale_heads: &[Hash], ) { - match notifications.try_next() { - Ok(Some(notif)) => { + match notifications.try_recv() { + Ok(notif) => { let stale_heads_expected: HashSet<_> = stale_heads.iter().collect(); let stale_heads: HashSet<_> = notif.stale_heads.iter().collect(); assert_eq!(notif.tree_route.as_ref(), &finalized[..finalized.len() - 1]); assert_eq!(notif.hash, *finalized.last().unwrap()); assert_eq!(stale_heads, stale_heads_expected); }, - Ok(None) => panic!("unexpected notification result, client send channel was closed"), - Err(_) => assert!(finalized.is_empty()), + Err(TryRecvError::Closed) => + panic!("unexpected notification result, client send channel was closed"), + Err(TryRecvError::Empty) => assert!(finalized.is_empty()), } } @@ -983,7 +985,7 @@ fn import_with_justification() { finality_notification_check(&mut finality_notifications, &[a1.hash(), a2.hash()], &[]); finality_notification_check(&mut finality_notifications, &[a3.hash()], &[]); - assert!(finality_notifications.try_next().is_err()); + assert!(matches!(finality_notifications.try_recv().unwrap_err(), TryRecvError::Empty)); } #[test] @@ -1038,7 +1040,7 @@ fn importing_diverged_finalized_block_should_trigger_reorg() { assert_eq!(client.chain_info().finalized_hash, b1.hash()); finality_notification_check(&mut finality_notifications, &[b1.hash()], &[a2.hash()]); - assert!(finality_notifications.try_next().is_err()); + assert!(matches!(finality_notifications.try_recv().unwrap_err(), TryRecvError::Empty)); } #[test] @@ -1124,7 +1126,7 @@ fn finalizing_diverged_block_should_trigger_reorg() { finality_notification_check(&mut finality_notifications, &[b1.hash()], &[]); finality_notification_check(&mut finality_notifications, &[b2.hash(), b3.hash()], &[a2.hash()]); - assert!(finality_notifications.try_next().is_err()); + assert!(matches!(finality_notifications.try_recv().unwrap_err(), TryRecvError::Empty)); } #[test] @@ -1227,7 +1229,7 @@ fn finality_notifications_content() { finality_notification_check(&mut finality_notifications, &[a1.hash(), a2.hash()], &[c1.hash()]); finality_notification_check(&mut finality_notifications, &[d3.hash(), d4.hash()], &[b2.hash()]); - assert!(finality_notifications.try_next().is_err()); + assert!(matches!(finality_notifications.try_recv().unwrap_err(), TryRecvError::Empty)); } #[test] @@ -1437,7 +1439,7 @@ fn doesnt_import_blocks_that_revert_finality() { finality_notification_check(&mut finality_notifications, &[a3.hash()], &[b2.hash()]); - assert!(finality_notifications.try_next().is_err()); + assert!(matches!(finality_notifications.try_recv().unwrap_err(), TryRecvError::Empty)); } #[test] diff --git a/client/utils/Cargo.toml b/client/utils/Cargo.toml index e80588453..38484285d 100644 --- a/client/utils/Cargo.toml +++ b/client/utils/Cargo.toml @@ -10,13 +10,14 @@ description = "I/O for Substrate runtimes" readme = "README.md" [dependencies] -backtrace = "0.3.67" +async-channel = "1.8.0" futures = "0.3.21" futures-timer = "3.0.2" lazy_static = "1.4.0" log = "0.4" parking_lot = "0.12.1" prometheus = { version = "0.13.0", default-features = false } +sp-arithmetic = { version = "6.0.0", default-features = false, path = "../../primitives/arithmetic" } [features] default = ["metered"] diff --git a/client/utils/README.md b/client/utils/README.md index 2da70f09c..d20fe69ef 100644 --- a/client/utils/README.md +++ b/client/utils/README.md @@ -1,16 +1,11 @@ -Utilities Primitives for Substrate +# Utilities Primitives for Substrate -## Features +This crate provides `mpsc::tracing_unbounded` function that returns wrapper types to +`async_channel::Sender` and `async_channel::Receiver`, which register every +`send`/`received`/`dropped` action happened on the channel. -### metered - -This feature changes the behaviour of the function `mpsc::tracing_unbounded`. -With the disabled feature this function is an alias to `futures::channel::mpsc::unbounded`. -However, when the feature is enabled it creates wrapper types to `UnboundedSender` -and `UnboundedReceiver` to register every `send`/`received`/`dropped` action happened on -the channel. - -Also this feature creates and registers a prometheus vector with name `unbounded_channel_len` and labels: +Also this wrapper creates and registers a prometheus vector with name `unbounded_channel_len` +and labels: | Label | Description | | ------------ | --------------------------------------------- | diff --git a/client/utils/src/lib.rs b/client/utils/src/lib.rs index f0cc1efe6..017fc7620 100644 --- a/client/utils/src/lib.rs +++ b/client/utils/src/lib.rs @@ -18,17 +18,11 @@ //! Utilities Primitives for Substrate //! -//! # Features +//! This crate provides `mpsc::tracing_unbounded` function that returns wrapper types to +//! `async_channel::Sender` and `async_channel::Receiver`, which register every +//! `send`/`received`/`dropped` action happened on the channel. //! -//! ## metered -//! -//! This feature changes the behaviour of the function `mpsc::tracing_unbounded`. -//! With the disabled feature this function is an alias to `futures::channel::mpsc::unbounded`. -//! However, when the feature is enabled it creates wrapper types to `UnboundedSender` -//! and `UnboundedReceiver` to register every `send`/`received`/`dropped` action happened on -//! the channel. -//! -//! Also this feature creates and registers a prometheus vector with name `unbounded_channel_len` +//! Also this wrapper creates and registers a prometheus vector with name `unbounded_channel_len` //! and labels: //! //! | Label | Description | diff --git a/client/utils/src/metrics.rs b/client/utils/src/metrics.rs index c2b10100f..6bbdbe2e2 100644 --- a/client/utils/src/metrics.rs +++ b/client/utils/src/metrics.rs @@ -24,7 +24,6 @@ use prometheus::{ Error as PrometheusError, Registry, }; -#[cfg(feature = "metered")] use prometheus::{core::GenericCounterVec, Opts}; lazy_static! { @@ -36,7 +35,6 @@ lazy_static! { .expect("Creating of statics doesn't fail. qed"); } -#[cfg(feature = "metered")] lazy_static! { pub static ref UNBOUNDED_CHANNELS_COUNTER : GenericCounterVec = GenericCounterVec::new( Opts::new("substrate_unbounded_channel_len", "Items in each mpsc::unbounded instance"), @@ -49,8 +47,6 @@ lazy_static! { pub fn register_globals(registry: &Registry) -> Result<(), PrometheusError> { registry.register(Box::new(TOKIO_THREADS_ALIVE.clone()))?; registry.register(Box::new(TOKIO_THREADS_TOTAL.clone()))?; - - #[cfg(feature = "metered")] registry.register(Box::new(UNBOUNDED_CHANNELS_COUNTER.clone()))?; Ok(()) diff --git a/client/utils/src/mpsc.rs b/client/utils/src/mpsc.rs index df45e33ff..3f783b100 100644 --- a/client/utils/src/mpsc.rs +++ b/client/utils/src/mpsc.rs @@ -16,287 +16,164 @@ // You should have received a copy of the GNU General Public License // along with this program. If not, see . -//! Features to meter unbounded channels - -#[cfg(not(feature = "metered"))] -mod inner { - // just aliased, non performance implications - use futures::channel::mpsc::{self, UnboundedReceiver, UnboundedSender}; - pub type TracingUnboundedSender = UnboundedSender; - pub type TracingUnboundedReceiver = UnboundedReceiver; - - /// Alias `mpsc::unbounded` - pub fn tracing_unbounded( - _key: &'static str, - ) -> (TracingUnboundedSender, TracingUnboundedReceiver) { - mpsc::unbounded() - } +//! Code to meter unbounded channels. + +use crate::metrics::UNBOUNDED_CHANNELS_COUNTER; +use async_channel::{Receiver, Sender, TryRecvError, TrySendError}; +use futures::{ + stream::{FusedStream, Stream}, + task::{Context, Poll}, +}; +use log::error; +use sp_arithmetic::traits::SaturatedConversion; +use std::{ + backtrace::Backtrace, + pin::Pin, + sync::{ + atomic::{AtomicBool, Ordering}, + Arc, + }, +}; + +/// Wrapper Type around [`async_channel::Sender`] that increases the global +/// measure when a message is added. +#[derive(Debug)] +pub struct TracingUnboundedSender { + inner: Sender, + name: &'static str, + queue_size_warning: usize, + warning_fired: Arc, + creation_backtrace: Arc, } -#[cfg(feature = "metered")] -mod inner { - // tracing implementation - use crate::metrics::UNBOUNDED_CHANNELS_COUNTER; - use backtrace::Backtrace; - use futures::{ - channel::mpsc::{ - self, SendError, TryRecvError, TrySendError, UnboundedReceiver, UnboundedSender, - }, - sink::Sink, - stream::{FusedStream, Stream}, - task::{Context, Poll}, - }; - use log::error; - use std::{ - pin::Pin, - sync::{ - atomic::{AtomicBool, AtomicI64, Ordering}, - Arc, - }, - }; - - /// Wrapper Type around `UnboundedSender` that increases the global - /// measure when a message is added - #[derive(Debug)] - pub struct TracingUnboundedSender { - inner: UnboundedSender, - name: &'static str, - // To not bother with ordering and possible underflow errors of the unsigned counter - // we just use `i64` and `Ordering::Relaxed`, and perceive `queue_size` as approximate. - // It can turn < 0 though. - queue_size: Arc, - queue_size_warning: i64, - warning_fired: Arc, - creation_backtrace: Arc, - } - - // Strangely, deriving `Clone` requires that `T` is also `Clone`. - impl Clone for TracingUnboundedSender { - fn clone(&self) -> Self { - Self { - inner: self.inner.clone(), - name: self.name, - queue_size: self.queue_size.clone(), - queue_size_warning: self.queue_size_warning, - warning_fired: self.warning_fired.clone(), - creation_backtrace: self.creation_backtrace.clone(), - } +// Strangely, deriving `Clone` requires that `T` is also `Clone`. +impl Clone for TracingUnboundedSender { + fn clone(&self) -> Self { + Self { + inner: self.inner.clone(), + name: self.name, + queue_size_warning: self.queue_size_warning, + warning_fired: self.warning_fired.clone(), + creation_backtrace: self.creation_backtrace.clone(), } } +} - /// Wrapper Type around `UnboundedReceiver` that decreases the global - /// measure when a message is polled - #[derive(Debug)] - pub struct TracingUnboundedReceiver { - inner: UnboundedReceiver, - name: &'static str, - queue_size: Arc, - } - - /// Wrapper around `mpsc::unbounded` that tracks the in- and outflow via - /// `UNBOUNDED_CHANNELS_COUNTER` and warns if the message queue grows - /// above the warning threshold. - pub fn tracing_unbounded( - name: &'static str, - queue_size_warning: i64, - ) -> (TracingUnboundedSender, TracingUnboundedReceiver) { - let (s, r) = mpsc::unbounded(); - let queue_size = Arc::new(AtomicI64::new(0)); - let sender = TracingUnboundedSender { - inner: s, - name, - queue_size: queue_size.clone(), - queue_size_warning, - warning_fired: Arc::new(AtomicBool::new(false)), - creation_backtrace: Arc::new(Backtrace::new_unresolved()), - }; - let receiver = TracingUnboundedReceiver { inner: r, name, queue_size }; - (sender, receiver) - } - - impl TracingUnboundedSender { - /// Proxy function to mpsc::UnboundedSender - pub fn poll_ready(&self, ctx: &mut Context) -> Poll> { - self.inner.poll_ready(ctx) - } - - /// Proxy function to mpsc::UnboundedSender - pub fn is_closed(&self) -> bool { - self.inner.is_closed() - } - - /// Proxy function to mpsc::UnboundedSender - pub fn close_channel(&self) { - self.inner.close_channel() - } - - /// Proxy function to mpsc::UnboundedSender - pub fn disconnect(&mut self) { - self.inner.disconnect() - } - - pub fn start_send(&mut self, msg: T) -> Result<(), SendError> { - // The underlying implementation of [`UnboundedSender::start_send`] is the same as - // [`UnboundedSender::unbounded_send`], so we just reuse the message counting and - // error reporting code from `unbounded_send`. - self.unbounded_send(msg).map_err(TrySendError::into_send_error) - } - - /// Proxy function to mpsc::UnboundedSender - pub fn unbounded_send(&self, msg: T) -> Result<(), TrySendError> { - self.inner.unbounded_send(msg).map(|s| { - UNBOUNDED_CHANNELS_COUNTER.with_label_values(&[self.name, "send"]).inc(); - - let queue_size = self.queue_size.fetch_add(1, Ordering::Relaxed); - if queue_size == self.queue_size_warning && - self.warning_fired - .compare_exchange(false, true, Ordering::Relaxed, Ordering::Relaxed) - .is_ok() - { - // `warning_fired` and `queue_size` are not synchronized, so it's possible - // that the warning is fired few times before the `warning_fired` is seen - // by all threads. This seems better than introducing a mutex guarding them. - let mut backtrace = (*self.creation_backtrace).clone(); - backtrace.resolve(); - error!( - "The number of unprocessed messages in channel `{}` reached {}.\n\ - The channel was created at:\n{:?}", - self.name, self.queue_size_warning, backtrace, - ); - } +/// Wrapper Type around [`async_channel::Receiver`] that decreases the global +/// measure when a message is polled. +#[derive(Debug)] +pub struct TracingUnboundedReceiver { + inner: Receiver, + name: &'static str, +} - s - }) - } +/// Wrapper around [`async_channel::unbounded`] that tracks the in- and outflow via +/// `UNBOUNDED_CHANNELS_COUNTER` and warns if the message queue grows +/// above the warning threshold. +pub fn tracing_unbounded( + name: &'static str, + queue_size_warning: usize, +) -> (TracingUnboundedSender, TracingUnboundedReceiver) { + let (s, r) = async_channel::unbounded(); + let sender = TracingUnboundedSender { + inner: s, + name, + queue_size_warning, + warning_fired: Arc::new(AtomicBool::new(false)), + creation_backtrace: Arc::new(Backtrace::force_capture()), + }; + let receiver = TracingUnboundedReceiver { inner: r, name }; + (sender, receiver) +} - /// Proxy function to mpsc::UnboundedSender - pub fn same_receiver(&self, other: &UnboundedSender) -> bool { - self.inner.same_receiver(other) - } +impl TracingUnboundedSender { + /// Proxy function to [`async_channel::Sender`]. + pub fn is_closed(&self) -> bool { + self.inner.is_closed() } - impl TracingUnboundedReceiver { - fn consume(&mut self) { - // consume all items, make sure to reflect the updated count - let mut count = 0; - loop { - if self.inner.is_terminated() { - break - } + /// Proxy function to [`async_channel::Sender`]. + pub fn close(&self) -> bool { + self.inner.close() + } - match self.try_next() { - Ok(Some(..)) => count += 1, - _ => break, - } + /// Proxy function to `async_channel::Sender::try_send`. + pub fn unbounded_send(&self, msg: T) -> Result<(), TrySendError> { + self.inner.try_send(msg).map(|s| { + UNBOUNDED_CHANNELS_COUNTER.with_label_values(&[self.name, "send"]).inc(); + + if self.inner.len() >= self.queue_size_warning && + self.warning_fired + .compare_exchange(false, true, Ordering::Relaxed, Ordering::Relaxed) + .is_ok() + { + error!( + "The number of unprocessed messages in channel `{}` exceeded {}.\n\ + The channel was created at:\n{}\n + Last message was sent from:\n{}", + self.name, + self.queue_size_warning, + self.creation_backtrace, + Backtrace::force_capture(), + ); } - // and discount the messages - if count > 0 { - UNBOUNDED_CHANNELS_COUNTER - .with_label_values(&[self.name, "dropped"]) - .inc_by(count); - } - } - /// Proxy function to mpsc::UnboundedReceiver - /// that consumes all messages first and updates the counter - pub fn close(&mut self) { - self.consume(); - self.inner.close() - } - - /// Proxy function to mpsc::UnboundedReceiver - /// that discounts the messages taken out - pub fn try_next(&mut self) -> Result, TryRecvError> { - self.inner.try_next().map(|s| { - if s.is_some() { - let _ = self.queue_size.fetch_sub(1, Ordering::Relaxed); - UNBOUNDED_CHANNELS_COUNTER.with_label_values(&[self.name, "received"]).inc(); - } - s - }) - } + s + }) } +} - impl Drop for TracingUnboundedReceiver { - fn drop(&mut self) { - self.consume(); - } +impl TracingUnboundedReceiver { + /// Proxy function to [`async_channel::Receiver`]. + pub fn close(&mut self) -> bool { + self.inner.close() } - impl Unpin for TracingUnboundedReceiver {} - - impl Stream for TracingUnboundedReceiver { - type Item = T; - - fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { - let s = self.get_mut(); - match Pin::new(&mut s.inner).poll_next(cx) { - Poll::Ready(msg) => { - if msg.is_some() { - let _ = s.queue_size.fetch_sub(1, Ordering::Relaxed); - UNBOUNDED_CHANNELS_COUNTER.with_label_values(&[s.name, "received"]).inc(); - } - Poll::Ready(msg) - }, - Poll::Pending => Poll::Pending, - } - } + /// Proxy function to [`async_channel::Receiver`] + /// that discounts the messages taken out. + pub fn try_recv(&mut self) -> Result { + self.inner.try_recv().map(|s| { + UNBOUNDED_CHANNELS_COUNTER.with_label_values(&[self.name, "received"]).inc(); + s + }) } +} - impl FusedStream for TracingUnboundedReceiver { - fn is_terminated(&self) -> bool { - self.inner.is_terminated() +impl Drop for TracingUnboundedReceiver { + fn drop(&mut self) { + self.close(); + // the number of messages about to be dropped + let count = self.inner.len(); + // discount the messages + if count > 0 { + UNBOUNDED_CHANNELS_COUNTER + .with_label_values(&[self.name, "dropped"]) + .inc_by(count.saturated_into()); } } +} - impl Sink for TracingUnboundedSender { - type Error = SendError; - - fn poll_ready(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { - TracingUnboundedSender::poll_ready(&*self, cx) - } - - fn start_send(mut self: Pin<&mut Self>, msg: T) -> Result<(), Self::Error> { - TracingUnboundedSender::start_send(&mut *self, msg) - } +impl Unpin for TracingUnboundedReceiver {} - fn poll_flush(self: Pin<&mut Self>, _: &mut Context<'_>) -> Poll> { - Poll::Ready(Ok(())) - } +impl Stream for TracingUnboundedReceiver { + type Item = T; - fn poll_close( - mut self: Pin<&mut Self>, - _: &mut Context<'_>, - ) -> Poll> { - self.disconnect(); - Poll::Ready(Ok(())) + fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { + let s = self.get_mut(); + match Pin::new(&mut s.inner).poll_next(cx) { + Poll::Ready(msg) => { + if msg.is_some() { + UNBOUNDED_CHANNELS_COUNTER.with_label_values(&[s.name, "received"]).inc(); + } + Poll::Ready(msg) + }, + Poll::Pending => Poll::Pending, } } +} - impl Sink for &TracingUnboundedSender { - type Error = SendError; - - fn poll_ready(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { - TracingUnboundedSender::poll_ready(*self, cx) - } - - fn start_send(self: Pin<&mut Self>, msg: T) -> Result<(), Self::Error> { - self.unbounded_send(msg).map_err(TrySendError::into_send_error) - } - - fn poll_flush(self: Pin<&mut Self>, _: &mut Context<'_>) -> Poll> { - Poll::Ready(Ok(())) - } - - fn poll_close(self: Pin<&mut Self>, _: &mut Context<'_>) -> Poll> { - // The difference with `TracingUnboundedSender` is intentional. The underlying - // implementation differs for `UnboundedSender` and `&UnboundedSender`: - // the latter closes the channel completely with `close_channel()`, while the former - // only closes this specific sender with `disconnect()`. - self.close_channel(); - Poll::Ready(Ok(())) - } +impl FusedStream for TracingUnboundedReceiver { + fn is_terminated(&self) -> bool { + self.inner.is_terminated() } } - -pub use inner::{tracing_unbounded, TracingUnboundedReceiver, TracingUnboundedSender}; diff --git a/client/utils/src/notification.rs b/client/utils/src/notification.rs index 7d866ffc8..dabb85d61 100644 --- a/client/utils/src/notification.rs +++ b/client/utils/src/notification.rs @@ -79,7 +79,7 @@ impl NotificationStream { } /// Subscribe to a channel through which the generic payload can be received. - pub fn subscribe(&self, queue_size_warning: i64) -> NotificationReceiver { + pub fn subscribe(&self, queue_size_warning: usize) -> NotificationReceiver { let receiver = self.hub.subscribe((), queue_size_warning); NotificationReceiver { receiver } } diff --git a/client/utils/src/pubsub.rs b/client/utils/src/pubsub.rs index 8136aa069..5293fa42e 100644 --- a/client/utils/src/pubsub.rs +++ b/client/utils/src/pubsub.rs @@ -164,7 +164,7 @@ impl Hub { /// Subscribe to this Hub using the `subs_key: K`. /// /// A subscription with a key `K` is possible if the Registry implements `Subscribe`. - pub fn subscribe(&self, subs_key: K, queue_size_warning: i64) -> Receiver + pub fn subscribe(&self, subs_key: K, queue_size_warning: usize) -> Receiver where R: Subscribe + Unsubscribe, { diff --git a/client/utils/src/pubsub/tests/normal_operation.rs b/client/utils/src/pubsub/tests/normal_operation.rs index d4b614d7a..a3ea4f7dd 100644 --- a/client/utils/src/pubsub/tests/normal_operation.rs +++ b/client/utils/src/pubsub/tests/normal_operation.rs @@ -37,9 +37,8 @@ fn positive_rx_receives_relevant_messages_and_terminates_upon_hub_drop() { // Hub is disposed. The rx_01 should be over after that. std::mem::drop(hub); - assert!(!rx_01.is_terminated()); - assert_eq!(None, rx_01.next().await); assert!(rx_01.is_terminated()); + assert_eq!(None, rx_01.next().await); }); } From 6a154768e2491b761d96f53d0a2bd66b9a4ae94f Mon Sep 17 00:00:00 2001 From: Koute Date: Tue, 7 Mar 2023 17:57:16 +0900 Subject: [PATCH 202/558] Switch to the `blake2b_simd` crate in `sp-core-hashing` (#13548) * Switch to the `blake2b_simd` crate in `sp-core-hashing` * ".git/.scripts/commands/bench/bench.sh" pallet dev frame_benchmarking --------- Co-authored-by: command-bot <> --- Cargo.lock | 4 +- frame/benchmarking/src/weights.rs | 69 +++++++++++++++--------------- primitives/core/hashing/Cargo.toml | 4 +- primitives/core/hashing/src/lib.rs | 25 +++++++---- 4 files changed, 55 insertions(+), 47 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index e0cc2c107..53a867543 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -10193,7 +10193,7 @@ dependencies = [ name = "sp-core-hashing" version = "5.0.0" dependencies = [ - "blake2", + "blake2b_simd", "byteorder", "digest 0.10.6", "sha2 0.10.6", @@ -11775,7 +11775,7 @@ checksum = "97fee6b57c6a41524a810daee9286c02d7752c4253064d0b05472833a438f675" dependencies = [ "cfg-if", "digest 0.10.6", - "rand 0.7.3", + "rand 0.8.5", "static_assertions", ] diff --git a/frame/benchmarking/src/weights.rs b/frame/benchmarking/src/weights.rs index fdd4429f2..b05feb590 100644 --- a/frame/benchmarking/src/weights.rs +++ b/frame/benchmarking/src/weights.rs @@ -18,25 +18,26 @@ //! Autogenerated weights for frame_benchmarking //! //! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev -//! DATE: 2023-01-24, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! DATE: 2023-03-06, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` //! WORST CASE MAP SIZE: `1000000` -//! HOSTNAME: `bm2`, CPU: `Intel(R) Core(TM) i7-7700K CPU @ 4.20GHz` +//! HOSTNAME: `bm3`, CPU: `Intel(R) Core(TM) i7-7700K CPU @ 4.20GHz` //! EXECUTION: Some(Wasm), WASM-EXECUTION: Compiled, CHAIN: Some("dev"), DB CACHE: 1024 // Executed Command: -// ./target/production/substrate +// target/production/substrate // benchmark // pallet -// --chain=dev // --steps=50 // --repeat=20 -// --pallet=frame_benchmarking // --extrinsic=* // --execution=wasm // --wasm-execution=compiled // --heap-pages=4096 -// --output=./frame/benchmarking/src/weights.rs +// --json-file=/var/lib/gitlab-runner/builds/zyw4fam_/0/parity/mirrors/substrate/.git/.artifacts/bench.json +// --pallet=frame_benchmarking +// --chain=dev // --header=./HEADER-APACHE2 +// --output=./frame/benchmarking/src/weights.rs // --template=./.maintain/frame-weight-template.hbs #![cfg_attr(rustfmt, rustfmt_skip)] @@ -64,49 +65,49 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 138 nanoseconds. - Weight::from_parts(199_805, 0) + // Minimum execution time: 168_000 picoseconds. + Weight::from_parts(237_577, 0) } /// The range of component `i` is `[0, 1000000]`. fn subtraction(_i: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 142 nanoseconds. - Weight::from_parts(201_435, 0) + // Minimum execution time: 169_000 picoseconds. + Weight::from_parts(224_111, 0) } /// The range of component `i` is `[0, 1000000]`. fn multiplication(_i: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 138 nanoseconds. - Weight::from_parts(207_037, 0) + // Minimum execution time: 175_000 picoseconds. + Weight::from_parts(229_708, 0) } /// The range of component `i` is `[0, 1000000]`. fn division(_i: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 151 nanoseconds. - Weight::from_parts(205_150, 0) + // Minimum execution time: 173_000 picoseconds. + Weight::from_parts(229_855, 0) } fn hashing() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 21_950_884 nanoseconds. - Weight::from_parts(21_994_001_000, 0) + // Minimum execution time: 21_286_627_000 picoseconds. + Weight::from_parts(21_405_011_000, 0) } /// The range of component `i` is `[0, 100]`. fn sr25519_verification(i: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 168 nanoseconds. - Weight::from_parts(1_680_898, 0) - // Standard Error: 10_291 - .saturating_add(Weight::from_parts(46_867_301, 0).saturating_mul(i.into())) + // Minimum execution time: 239_000 picoseconds. + Weight::from_parts(661_987, 0) + // Standard Error: 24_324 + .saturating_add(Weight::from_parts(47_322_399, 0).saturating_mul(i.into())) } } @@ -117,48 +118,48 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 138 nanoseconds. - Weight::from_parts(199_805, 0) + // Minimum execution time: 168_000 picoseconds. + Weight::from_parts(237_577, 0) } /// The range of component `i` is `[0, 1000000]`. fn subtraction(_i: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 142 nanoseconds. - Weight::from_parts(201_435, 0) + // Minimum execution time: 169_000 picoseconds. + Weight::from_parts(224_111, 0) } /// The range of component `i` is `[0, 1000000]`. fn multiplication(_i: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 138 nanoseconds. - Weight::from_parts(207_037, 0) + // Minimum execution time: 175_000 picoseconds. + Weight::from_parts(229_708, 0) } /// The range of component `i` is `[0, 1000000]`. fn division(_i: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 151 nanoseconds. - Weight::from_parts(205_150, 0) + // Minimum execution time: 173_000 picoseconds. + Weight::from_parts(229_855, 0) } fn hashing() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 21_950_884 nanoseconds. - Weight::from_parts(21_994_001_000, 0) + // Minimum execution time: 21_286_627_000 picoseconds. + Weight::from_parts(21_405_011_000, 0) } /// The range of component `i` is `[0, 100]`. fn sr25519_verification(i: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 168 nanoseconds. - Weight::from_parts(1_680_898, 0) - // Standard Error: 10_291 - .saturating_add(Weight::from_parts(46_867_301, 0).saturating_mul(i.into())) + // Minimum execution time: 239_000 picoseconds. + Weight::from_parts(661_987, 0) + // Standard Error: 24_324 + .saturating_add(Weight::from_parts(47_322_399, 0).saturating_mul(i.into())) } } diff --git a/primitives/core/hashing/Cargo.toml b/primitives/core/hashing/Cargo.toml index 1bb67ffff..a77aaaa7a 100644 --- a/primitives/core/hashing/Cargo.toml +++ b/primitives/core/hashing/Cargo.toml @@ -13,7 +13,7 @@ documentation = "https://docs.rs/sp-core-hashing" targets = ["x86_64-unknown-linux-gnu"] [dependencies] -blake2 = { version = "0.10.4", default-features = false } +blake2b_simd = { version = "1.0.1", default-features = false } byteorder = { version = "1.3.2", default-features = false } digest = { version = "0.10.3", default-features = false } sha2 = { version = "0.10.2", default-features = false } @@ -25,7 +25,7 @@ sp-std = { version = "5.0.0", default-features = false, path = "../../std" } default = ["std"] std = [ "digest/std", - "blake2/std", + "blake2b_simd/std", "byteorder/std", "sha2/std", "sha3/std", diff --git a/primitives/core/hashing/src/lib.rs b/primitives/core/hashing/src/lib.rs index 26941bb60..33d777f85 100644 --- a/primitives/core/hashing/src/lib.rs +++ b/primitives/core/hashing/src/lib.rs @@ -23,34 +23,41 @@ use core::hash::Hasher; use byteorder::{ByteOrder, LittleEndian}; -use digest::{ - consts::{U16, U32, U8}, - Digest, -}; +use digest::Digest; + +#[inline(always)] +fn blake2(data: &[u8]) -> [u8; N] { + blake2b_simd::Params::new() + .hash_length(N) + .hash(data) + .as_bytes() + .try_into() + .expect("slice is always the necessary length") +} /// Do a Blake2 512-bit hash and place result in `dest`. pub fn blake2_512_into(data: &[u8], dest: &mut [u8; 64]) { - dest.copy_from_slice(blake2::Blake2b512::digest(data).as_slice()); + *dest = blake2(data); } /// Do a Blake2 512-bit hash and return result. pub fn blake2_512(data: &[u8]) -> [u8; 64] { - blake2::Blake2b512::digest(data).into() + blake2(data) } /// Do a Blake2 256-bit hash and return result. pub fn blake2_256(data: &[u8]) -> [u8; 32] { - blake2::Blake2b::::digest(data).into() + blake2(data) } /// Do a Blake2 128-bit hash and return result. pub fn blake2_128(data: &[u8]) -> [u8; 16] { - blake2::Blake2b::::digest(data).into() + blake2(data) } /// Do a Blake2 64-bit hash and return result. pub fn blake2_64(data: &[u8]) -> [u8; 8] { - blake2::Blake2b::::digest(data).into() + blake2(data) } /// Do a XX 64-bit hash and place result in `dest`. From c09ce08526542a45ff12c6f87b2518094f69f8e1 Mon Sep 17 00:00:00 2001 From: yjh Date: Tue, 7 Mar 2023 16:59:04 +0800 Subject: [PATCH 203/558] remove serde derive for BlockId (#13457) --- primitives/runtime/src/generic/block.rs | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/primitives/runtime/src/generic/block.rs b/primitives/runtime/src/generic/block.rs index 85d0b7ca1..1df747a16 100644 --- a/primitives/runtime/src/generic/block.rs +++ b/primitives/runtime/src/generic/block.rs @@ -33,9 +33,6 @@ use sp_std::prelude::*; /// Something to identify a block. #[derive(PartialEq, Eq, Clone, Encode, Decode, RuntimeDebug)] -#[cfg_attr(feature = "std", derive(Serialize))] -#[cfg_attr(feature = "std", serde(rename_all = "camelCase"))] -#[cfg_attr(feature = "std", serde(deny_unknown_fields))] pub enum BlockId { /// Identify by block header hash. Hash(Block::Hash), @@ -45,12 +42,12 @@ pub enum BlockId { impl BlockId { /// Create a block ID from a hash. - pub fn hash(hash: Block::Hash) -> Self { + pub const fn hash(hash: Block::Hash) -> Self { BlockId::Hash(hash) } /// Create a block ID from a number. - pub fn number(number: NumberFor) -> Self { + pub const fn number(number: NumberFor) -> Self { BlockId::Number(number) } From 4d8f3d543b357a6145b7ba1bea9c63ddeaafdc5a Mon Sep 17 00:00:00 2001 From: Alexandru Vasile <60601340+lexnv@users.noreply.github.com> Date: Tue, 7 Mar 2023 11:56:14 +0200 Subject: [PATCH 204/558] rpc/chainHead: Fix pruned blocks events from forks (#13379) * rpc/chainhead: Test unpin for noncanonical prunned blocks Signed-off-by: Alexandru Vasile * rpc/tests: Ensure fork is not reported by the Finalized event Signed-off-by: Alexandru Vasile * rpc/chainhead: Detect pruned forks to ignore from events Signed-off-by: Alexandru Vasile * rpc/tests: Check unpin can be called on pruned hashes Signed-off-by: Alexandru Vasile * Fix clippy Signed-off-by: Alexandru Vasile * rpc/chain_head: Handle race with memory blocks and notifications Signed-off-by: Alexandru Vasile * rpc/chain_head: Add data config for the `follow` future Signed-off-by: Alexandru Vasile * rpc/chain_head: Address feedback Signed-off-by: Alexandru Vasile * rpc/chain_head: Move best block cache on the data config Signed-off-by: Alexandru Vasile * rpc/chain_head: Send new events from the finalized stream Signed-off-by: Alexandru Vasile * rpc/chian_head: Report all pruned blocks Signed-off-by: Alexandru Vasile * rpc/chain_head: Move `chainHead_follow` logic on dedicated file Signed-off-by: Alexandru Vasile * rpc/chain_head: Delegate follow logic to `chain_head_follow` Signed-off-by: Alexandru Vasile * rpc/chain_head: Remove subscriptions on drop Signed-off-by: Alexandru Vasile * rpc/tests: Ignore pruned blocks for a longer fork Signed-off-by: Alexandru Vasile * rpc/tests: Check all pruned blocks are reported, not just stale heads Signed-off-by: Alexandru Vasile * rpc/tests: Remove println debug and fix indentation Signed-off-by: Alexandru Vasile * rpc/chain_head: Remove unnecessary trait bounds Signed-off-by: Alexandru Vasile * rpc/chain_head: Add debug log for pruned forks Signed-off-by: Alexandru Vasile * Revert "rpc/chain_head: Add debug log for pruned forks" This reverts commit 425d6e7a8b60421bcece12add1941fe58524cf52. * Adjust blockID for testing Signed-off-by: Alexandru Vasile * Update client/rpc-spec-v2/src/chain_head/chain_head_follow.rs Co-authored-by: Davide Galassi * rpc/chain_head: Rename `ChainHeadFollow` to `ChainHeadFollower` Signed-off-by: Alexandru Vasile * rpc/chain_head: Remove subscriptions manually Signed-off-by: Alexandru Vasile * rpc/chain_head: Improve log messages by adding subID and errors Signed-off-by: Alexandru Vasile * rpc/chain_head: Ensure `follow` stops sending events on first error Signed-off-by: Alexandru Vasile * rpc/chain_head: Use default constructor Signed-off-by: Alexandru Vasile * rpc/chain_head: Add `StartupPoint` structure Signed-off-by: Alexandru Vasile * rpc/chain_head: Rename `in_memory_blocks` Signed-off-by: Alexandru Vasile * rpc/chain_head: Fix comment typo Signed-off-by: Alexandru Vasile * rpc/chain_head: Keep unique blocks and remove itertools Signed-off-by: Alexandru Vasile * rpc/chain_head: Make sure `bestBlocks` events are generated in order Signed-off-by: Alexandru Vasile * rpc/chain_head: Maintain order of reported blocks Signed-off-by: Alexandru Vasile * rpc/chain_head: Parent of finalized block could be unpinned Signed-off-by: Alexandru Vasile * rpc/chain_head: Fix warning Signed-off-by: Alexandru Vasile --------- Signed-off-by: Alexandru Vasile Co-authored-by: Davide Galassi --- .../rpc-spec-v2/src/chain_head/chain_head.rs | 399 +---------- .../src/chain_head/chain_head_follow.rs | 644 ++++++++++++++++++ client/rpc-spec-v2/src/chain_head/mod.rs | 1 + .../src/chain_head/subscription.rs | 25 +- client/rpc-spec-v2/src/chain_head/tests.rs | 301 ++++++++ 5 files changed, 989 insertions(+), 381 deletions(-) create mode 100644 client/rpc-spec-v2/src/chain_head/chain_head_follow.rs diff --git a/client/rpc-spec-v2/src/chain_head/chain_head.rs b/client/rpc-spec-v2/src/chain_head/chain_head.rs index 4fa47dc40..c63e874c1 100644 --- a/client/rpc-spec-v2/src/chain_head/chain_head.rs +++ b/client/rpc-spec-v2/src/chain_head/chain_head.rs @@ -21,41 +21,33 @@ use crate::{ chain_head::{ api::ChainHeadApiServer, + chain_head_follow::ChainHeadFollower, error::Error as ChainHeadRpcError, - event::{ - BestBlockChanged, ChainHeadEvent, ChainHeadResult, ErrorEvent, Finalized, FollowEvent, - Initialized, NetworkConfig, NewBlock, RuntimeEvent, RuntimeVersionEvent, - }, - subscription::{SubscriptionHandle, SubscriptionManagement, SubscriptionManagementError}, + event::{ChainHeadEvent, ChainHeadResult, ErrorEvent, FollowEvent, NetworkConfig}, + subscription::SubscriptionManagement, }, SubscriptionTaskExecutor, }; use codec::Encode; -use futures::{ - channel::oneshot, - future::FutureExt, - stream::{self, Stream, StreamExt}, -}; -use futures_util::future::Either; +use futures::future::FutureExt; use jsonrpsee::{ core::{async_trait, RpcResult}, types::{SubscriptionEmptyError, SubscriptionId, SubscriptionResult}, SubscriptionSink, }; -use log::{debug, error}; +use log::debug; use sc_client_api::{ - Backend, BlockBackend, BlockImportNotification, BlockchainEvents, CallExecutor, ChildInfo, - ExecutorProvider, FinalityNotification, StorageKey, StorageProvider, + Backend, BlockBackend, BlockchainEvents, CallExecutor, ChildInfo, ExecutorProvider, StorageKey, + StorageProvider, }; -use serde::Serialize; use sp_api::CallApiAt; -use sp_blockchain::{ - Backend as BlockChainBackend, Error as BlockChainError, HeaderBackend, HeaderMetadata, -}; +use sp_blockchain::{Error as BlockChainError, HeaderBackend, HeaderMetadata}; use sp_core::{hexdisplay::HexDisplay, storage::well_known_keys, traits::CallContext, Bytes}; -use sp_runtime::traits::{Block as BlockT, Header}; +use sp_runtime::traits::Block as BlockT; use std::{marker::PhantomData, sync::Arc}; +pub(crate) const LOG_TARGET: &str = "rpc-spec-v2"; + /// An API for chain head RPC calls. pub struct ChainHead { /// Substrate client. @@ -119,64 +111,6 @@ impl ChainHead { } } -/// Generate the initial events reported by the RPC `follow` method. -/// -/// This includes the "Initialized" event followed by the in-memory -/// blocks via "NewBlock" and the "BestBlockChanged". -fn generate_initial_events( - client: &Arc, - backend: &Arc, - handle: &SubscriptionHandle, - runtime_updates: bool, -) -> Result>, SubscriptionManagementError> -where - Block: BlockT + 'static, - Block::Header: Unpin, - BE: Backend + 'static, - Client: HeaderBackend + CallApiAt + 'static, -{ - // The initialized event is the first one sent. - let finalized_block_hash = client.info().finalized_hash; - handle.pin_block(finalized_block_hash)?; - - let finalized_block_runtime = - generate_runtime_event(&client, runtime_updates, finalized_block_hash, None); - - let initialized_event = FollowEvent::Initialized(Initialized { - finalized_block_hash, - finalized_block_runtime, - runtime_updates, - }); - - let initial_blocks = get_initial_blocks(&backend, finalized_block_hash); - let mut in_memory_blocks = Vec::with_capacity(initial_blocks.len() + 1); - - in_memory_blocks.push(initialized_event); - for (child, parent) in initial_blocks.into_iter() { - handle.pin_block(child)?; - - let new_runtime = generate_runtime_event(&client, runtime_updates, child, Some(parent)); - - let event = FollowEvent::NewBlock(NewBlock { - block_hash: child, - parent_block_hash: parent, - new_runtime, - runtime_updates, - }); - - in_memory_blocks.push(event); - } - - // Generate a new best block event. - let best_block_hash = client.info().best_hash; - if best_block_hash != finalized_block_hash { - let best_block = FollowEvent::BestBlockChanged(BestBlockChanged { best_block_hash }); - in_memory_blocks.push(best_block); - }; - - Ok(in_memory_blocks) -} - /// Parse hex-encoded string parameter as raw bytes. /// /// If the parsing fails, the subscription is rejected. @@ -198,243 +132,6 @@ fn parse_hex_param( } } -/// Conditionally generate the runtime event of the given block. -fn generate_runtime_event( - client: &Arc, - runtime_updates: bool, - block: Block::Hash, - parent: Option, -) -> Option -where - Block: BlockT + 'static, - Client: CallApiAt + 'static, -{ - // No runtime versions should be reported. - if !runtime_updates { - return None - } - - let block_rt = match client.runtime_version_at(block) { - Ok(rt) => rt, - Err(err) => return Some(err.into()), - }; - - let parent = match parent { - Some(parent) => parent, - // Nothing to compare against, always report. - None => return Some(RuntimeEvent::Valid(RuntimeVersionEvent { spec: block_rt })), - }; - - let parent_rt = match client.runtime_version_at(parent) { - Ok(rt) => rt, - Err(err) => return Some(err.into()), - }; - - // Report the runtime version change. - if block_rt != parent_rt { - Some(RuntimeEvent::Valid(RuntimeVersionEvent { spec: block_rt })) - } else { - None - } -} - -/// Get the in-memory blocks of the client, starting from the provided finalized hash. -/// -/// Returns a tuple of block hash with parent hash. -fn get_initial_blocks( - backend: &Arc, - parent_hash: Block::Hash, -) -> Vec<(Block::Hash, Block::Hash)> -where - Block: BlockT + 'static, - BE: Backend + 'static, -{ - let mut result = Vec::new(); - let mut next_hash = Vec::new(); - next_hash.push(parent_hash); - - while let Some(parent_hash) = next_hash.pop() { - let Ok(blocks) = backend.blockchain().children(parent_hash) else { - continue - }; - - for child_hash in blocks { - result.push((child_hash, parent_hash)); - next_hash.push(child_hash); - } - } - - result -} - -/// Submit the events from the provided stream to the RPC client -/// for as long as the `rx_stop` event was not called. -async fn submit_events( - sink: &mut SubscriptionSink, - mut stream: EventStream, - rx_stop: oneshot::Receiver<()>, -) where - EventStream: Stream + Unpin, - T: Serialize, -{ - let mut stream_item = stream.next(); - let mut stop_event = rx_stop; - - while let Either::Left((Some(event), next_stop_event)) = - futures_util::future::select(stream_item, stop_event).await - { - match sink.send(&event) { - Ok(true) => { - stream_item = stream.next(); - stop_event = next_stop_event; - }, - // Client disconnected. - Ok(false) => return, - Err(_) => { - // Failed to submit event. - break - }, - } - } - - let _ = sink.send(&FollowEvent::::Stop); -} - -/// Generate the "NewBlock" event and potentially the "BestBlockChanged" event for -/// every notification. -fn handle_import_blocks( - client: &Arc, - handle: &SubscriptionHandle, - runtime_updates: bool, - notification: BlockImportNotification, -) -> Result<(FollowEvent, Option>), SubscriptionManagementError> -where - Block: BlockT + 'static, - Client: CallApiAt + 'static, -{ - handle.pin_block(notification.hash)?; - - let new_runtime = generate_runtime_event( - &client, - runtime_updates, - notification.hash, - Some(*notification.header.parent_hash()), - ); - - // Note: `Block::Hash` will serialize to hexadecimal encoded string. - let new_block = FollowEvent::NewBlock(NewBlock { - block_hash: notification.hash, - parent_block_hash: *notification.header.parent_hash(), - new_runtime, - runtime_updates, - }); - - if !notification.is_new_best { - return Ok((new_block, None)) - } - - // If this is the new best block, then we need to generate two events. - let best_block_event = - FollowEvent::BestBlockChanged(BestBlockChanged { best_block_hash: notification.hash }); - - let mut best_block_cache = handle.best_block_write(); - match *best_block_cache { - Some(block_cache) => { - // The RPC layer has not reported this block as best before. - // Note: This handles the race with the finalized branch. - if block_cache != notification.hash { - *best_block_cache = Some(notification.hash); - Ok((new_block, Some(best_block_event))) - } else { - Ok((new_block, None)) - } - }, - None => { - *best_block_cache = Some(notification.hash); - Ok((new_block, Some(best_block_event))) - }, - } -} - -/// Generate the "Finalized" event and potentially the "BestBlockChanged" for -/// every notification. -fn handle_finalized_blocks( - client: &Arc, - handle: &SubscriptionHandle, - notification: FinalityNotification, -) -> Result<(FollowEvent, Option>), SubscriptionManagementError> -where - Block: BlockT + 'static, - Client: HeaderBackend + HeaderMetadata + 'static, -{ - let last_finalized = notification.hash; - // We might not receive all new blocks reports, also pin the block here. - handle.pin_block(last_finalized)?; - - // The tree route contains the exclusive path from the last finalized block - // to the block reported by the notification. Ensure the finalized block is - // properly reported to that path. - let mut finalized_block_hashes = notification.tree_route.iter().cloned().collect::>(); - finalized_block_hashes.push(last_finalized); - - let pruned_block_hashes: Vec<_> = notification.stale_heads.iter().cloned().collect(); - - let finalized_event = FollowEvent::Finalized(Finalized { - finalized_block_hashes, - pruned_block_hashes: pruned_block_hashes.clone(), - }); - - let mut best_block_cache = handle.best_block_write(); - match *best_block_cache { - Some(block_cache) => { - // Check if the current best block is also reported as pruned. - let reported_pruned = pruned_block_hashes.iter().find(|&&hash| hash == block_cache); - if reported_pruned.is_none() { - return Ok((finalized_event, None)) - } - - // The best block is reported as pruned. Therefore, we need to signal a new - // best block event before submitting the finalized event. - let best_block_hash = client.info().best_hash; - if best_block_hash == block_cache { - // The client doest not have any new information about the best block. - // The information from `.info()` is updated from the DB as the last - // step of the finalization and it should be up to date. Also, the - // displaced nodes (list of nodes reported) should be reported with - // an offset of 32 blocks for substrate. - // If the info is outdated, there is nothing the RPC can do for now. - error!(target: "rpc-spec-v2", "Client does not contain different best block"); - Ok((finalized_event, None)) - } else { - let ancestor = sp_blockchain::lowest_common_ancestor( - &**client, - last_finalized, - best_block_hash, - ) - .map_err(|_| { - SubscriptionManagementError::Custom("Could not find common ancestor".into()) - })?; - - // The client's best block must be a descendent of the last finalized block. - // In other words, the lowest common ancestor must be the last finalized block. - if ancestor.hash != last_finalized { - return Err(SubscriptionManagementError::Custom( - "The finalized block is not an ancestor of the best block".into(), - )) - } - - // The RPC needs to also submit a new best block changed before the - // finalized event. - *best_block_cache = Some(best_block_hash); - let best_block_event = - FollowEvent::BestBlockChanged(BestBlockChanged { best_block_hash }); - Ok((finalized_event, Some(best_block_event))) - } - }, - None => Ok((finalized_event, None)), - } -} - #[async_trait] impl ChainHeadApiServer for ChainHead where @@ -467,71 +164,28 @@ where let Some((rx_stop, sub_handle)) = self.subscriptions.insert_subscription(sub_id.clone(), runtime_updates, self.max_pinned_blocks) else { // Inserting the subscription can only fail if the JsonRPSee // generated a duplicate subscription ID. - debug!(target: "rpc-spec-v2", "[follow][id={:?}] Subscription already accepted", sub_id); + debug!(target: LOG_TARGET, "[follow][id={:?}] Subscription already accepted", sub_id); let _ = sink.send(&FollowEvent::::Stop); return Ok(()) }; - debug!(target: "rpc-spec-v2", "[follow][id={:?}] Subscription accepted", sub_id); + debug!(target: LOG_TARGET, "[follow][id={:?}] Subscription accepted", sub_id); - let client = self.client.clone(); - let handle = sub_handle.clone(); - let subscription_id = sub_id.clone(); - - let stream_import = self - .client - .import_notification_stream() - .map(move |notification| { - match handle_import_blocks(&client, &handle, runtime_updates, notification) { - Ok((new_block, None)) => stream::iter(vec![new_block]), - Ok((new_block, Some(best_block))) => stream::iter(vec![new_block, best_block]), - Err(_) => { - debug!(target: "rpc-spec-v2", "[follow][id={:?}] Failed to handle block import notification.", subscription_id); - handle.stop(); - stream::iter(vec![]) - }, - } - }) - .flatten(); - - let client = self.client.clone(); - let handle = sub_handle.clone(); - let subscription_id = sub_id.clone(); - - let stream_finalized = self - .client - .finality_notification_stream() - .map(move |notification| { - match handle_finalized_blocks(&client, &handle, notification) { - Ok((finalized_event, None)) => stream::iter(vec![finalized_event]), - Ok((finalized_event, Some(best_block))) => - stream::iter(vec![best_block, finalized_event]), - Err(_) => { - debug!(target: "rpc-spec-v2", "[follow][id={:?}] Failed to import finalized blocks", subscription_id); - handle.stop(); - stream::iter(vec![]) - }, - } - }) - .flatten(); - - let merged = tokio_stream::StreamExt::merge(stream_import, stream_finalized); let subscriptions = self.subscriptions.clone(); - let client = self.client.clone(); let backend = self.backend.clone(); + let client = self.client.clone(); let fut = async move { - let Ok(initial_events) = generate_initial_events(&client, &backend, &sub_handle, runtime_updates) else { - // Stop the subscription if we exceeded the maximum number of blocks pinned. - debug!(target: "rpc-spec-v2", "[follow][id={:?}] Exceeded max pinned blocks from initial events", sub_id); - let _ = sink.send(&FollowEvent::::Stop); - return - }; + let mut chain_head_follow = ChainHeadFollower::new( + client, + backend, + sub_handle, + runtime_updates, + sub_id.clone(), + ); - let stream = stream::iter(initial_events).chain(merged); + chain_head_follow.generate_events(sink, rx_stop).await; - submit_events(&mut sink, stream.boxed(), rx_stop).await; - // The client disconnected or called the unsubscribe method. subscriptions.remove_subscription(&sub_id); - debug!(target: "rpc-spec-v2", "[follow][id={:?}] Subscription removed", sub_id); + debug!(target: LOG_TARGET, "[follow][id={:?}] Subscription removed", sub_id); }; self.executor.spawn("substrate-rpc-subscription", Some("rpc"), fut.boxed()); @@ -569,7 +223,12 @@ where }, Ok(None) => { // The block's body was pruned. This subscription ID has become invalid. - debug!(target: "rpc-spec-v2", "[body][id={:?}] Stopping subscription because hash={:?} was pruned", follow_subscription, hash); + debug!( + target: LOG_TARGET, + "[body][id={:?}] Stopping subscription because hash={:?} was pruned", + follow_subscription, + hash + ); handle.stop(); ChainHeadEvent::::Disjoint }, diff --git a/client/rpc-spec-v2/src/chain_head/chain_head_follow.rs b/client/rpc-spec-v2/src/chain_head/chain_head_follow.rs new file mode 100644 index 000000000..9173b7340 --- /dev/null +++ b/client/rpc-spec-v2/src/chain_head/chain_head_follow.rs @@ -0,0 +1,644 @@ +// This file is part of Substrate. + +// Copyright (C) Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +//! Implementation of the `chainHead_follow` method. + +use crate::chain_head::{ + chain_head::LOG_TARGET, + event::{ + BestBlockChanged, Finalized, FollowEvent, Initialized, NewBlock, RuntimeEvent, + RuntimeVersionEvent, + }, + subscription::{SubscriptionHandle, SubscriptionManagementError}, +}; +use futures::{ + channel::oneshot, + stream::{self, Stream, StreamExt}, +}; +use futures_util::future::Either; +use jsonrpsee::SubscriptionSink; +use log::{debug, error}; +use sc_client_api::{ + Backend, BlockBackend, BlockImportNotification, BlockchainEvents, FinalityNotification, +}; +use sp_api::CallApiAt; +use sp_blockchain::{ + Backend as BlockChainBackend, Error as BlockChainError, HeaderBackend, HeaderMetadata, Info, +}; +use sp_runtime::{ + traits::{Block as BlockT, Header as HeaderT, One}, + Saturating, +}; +use std::{collections::HashSet, sync::Arc}; + +/// Generates the events of the `chainHead_follow` method. +pub struct ChainHeadFollower { + /// Substrate client. + client: Arc, + /// Backend of the chain. + backend: Arc, + /// Subscription handle. + sub_handle: SubscriptionHandle, + /// Subscription was started with the runtime updates flag. + runtime_updates: bool, + /// Subscription ID. + sub_id: String, + /// The best reported block by this subscription. + best_block_cache: Option, +} + +impl ChainHeadFollower { + /// Create a new [`ChainHeadFollower`]. + pub fn new( + client: Arc, + backend: Arc, + sub_handle: SubscriptionHandle, + runtime_updates: bool, + sub_id: String, + ) -> Self { + Self { client, backend, sub_handle, runtime_updates, sub_id, best_block_cache: None } + } +} + +/// A block notification. +enum NotificationType { + /// The initial events generated from the node's memory. + InitialEvents(Vec>), + /// The new block notification obtained from `import_notification_stream`. + NewBlock(BlockImportNotification), + /// The finalized block notification obtained from `finality_notification_stream`. + Finalized(FinalityNotification), +} + +/// The initial blocks that should be reported or ignored by the chainHead. +#[derive(Clone, Debug)] +struct InitialBlocks { + /// Children of the latest finalized block, for which the `NewBlock` + /// event must be generated. + /// + /// It is a tuple of (block hash, parent hash). + finalized_block_descendants: Vec<(Block::Hash, Block::Hash)>, + /// Blocks that should not be reported as pruned by the `Finalized` event. + /// + /// Substrate database will perform the pruning of height N at + /// the finalization N + 1. We could have the following block tree + /// when the user subscribes to the `follow` method: + /// [A] - [A1] - [A2] - [A3] + /// ^^ finalized + /// - [A1] - [B1] + /// + /// When the A3 block is finalized, B1 is reported as pruned, however + /// B1 was never reported as `NewBlock` (and as such was never pinned). + /// This is because the `NewBlock` events are generated for children of + /// the finalized hash. + pruned_forks: HashSet, +} + +/// The startup point from which chainHead started to generate events. +struct StartupPoint { + /// Best block hash. + pub best_hash: Block::Hash, + /// The head of the finalized chain. + pub finalized_hash: Block::Hash, + /// Last finalized block number. + pub finalized_number: <::Header as HeaderT>::Number, +} + +impl From> for StartupPoint { + fn from(info: Info) -> Self { + StartupPoint:: { + best_hash: info.best_hash, + finalized_hash: info.finalized_hash, + finalized_number: info.finalized_number, + } + } +} + +impl ChainHeadFollower +where + Block: BlockT + 'static, + BE: Backend + 'static, + Client: BlockBackend + + HeaderBackend + + HeaderMetadata + + BlockchainEvents + + CallApiAt + + 'static, +{ + /// Conditionally generate the runtime event of the given block. + fn generate_runtime_event( + &self, + block: Block::Hash, + parent: Option, + ) -> Option { + // No runtime versions should be reported. + if !self.runtime_updates { + return None + } + + let block_rt = match self.client.runtime_version_at(block) { + Ok(rt) => rt, + Err(err) => return Some(err.into()), + }; + + let parent = match parent { + Some(parent) => parent, + // Nothing to compare against, always report. + None => return Some(RuntimeEvent::Valid(RuntimeVersionEvent { spec: block_rt })), + }; + + let parent_rt = match self.client.runtime_version_at(parent) { + Ok(rt) => rt, + Err(err) => return Some(err.into()), + }; + + // Report the runtime version change. + if block_rt != parent_rt { + Some(RuntimeEvent::Valid(RuntimeVersionEvent { spec: block_rt })) + } else { + None + } + } + + /// Get the in-memory blocks of the client, starting from the provided finalized hash. + fn get_init_blocks_with_forks( + &self, + startup_point: &StartupPoint, + ) -> Result, SubscriptionManagementError> { + let blockchain = self.backend.blockchain(); + let leaves = blockchain.leaves()?; + let finalized = startup_point.finalized_hash; + let mut pruned_forks = HashSet::new(); + let mut finalized_block_descendants = Vec::new(); + let mut unique_descendants = HashSet::new(); + for leaf in leaves { + let tree_route = sp_blockchain::tree_route(blockchain, finalized, leaf)?; + + let blocks = tree_route.enacted().iter().map(|block| block.hash); + if !tree_route.retracted().is_empty() { + pruned_forks.extend(blocks); + } else { + // Ensure a `NewBlock` event is generated for all children of the + // finalized block. Describe the tree route as (child_node, parent_node) + // Note: the order of elements matters here. + let parents = std::iter::once(finalized).chain(blocks.clone()); + + for pair in blocks.zip(parents) { + if unique_descendants.insert(pair) { + finalized_block_descendants.push(pair); + } + } + } + } + + Ok(InitialBlocks { finalized_block_descendants, pruned_forks }) + } + + /// Generate the initial events reported by the RPC `follow` method. + /// + /// Returns the initial events that should be reported directly, together with pruned + /// block hashes that should be ignored by the `Finalized` event. + fn generate_init_events( + &mut self, + startup_point: &StartupPoint, + ) -> Result<(Vec>, HashSet), SubscriptionManagementError> + { + let init = self.get_init_blocks_with_forks(startup_point)?; + + let initial_blocks = init.finalized_block_descendants; + + // The initialized event is the first one sent. + let finalized_block_hash = startup_point.finalized_hash; + self.sub_handle.pin_block(finalized_block_hash)?; + + let finalized_block_runtime = self.generate_runtime_event(finalized_block_hash, None); + + let initialized_event = FollowEvent::Initialized(Initialized { + finalized_block_hash, + finalized_block_runtime, + runtime_updates: self.runtime_updates, + }); + + let mut finalized_block_descendants = Vec::with_capacity(initial_blocks.len() + 1); + + finalized_block_descendants.push(initialized_event); + for (child, parent) in initial_blocks.into_iter() { + self.sub_handle.pin_block(child)?; + + let new_runtime = self.generate_runtime_event(child, Some(parent)); + + let event = FollowEvent::NewBlock(NewBlock { + block_hash: child, + parent_block_hash: parent, + new_runtime, + runtime_updates: self.runtime_updates, + }); + + finalized_block_descendants.push(event); + } + + // Generate a new best block event. + let best_block_hash = startup_point.best_hash; + if best_block_hash != finalized_block_hash { + let best_block = FollowEvent::BestBlockChanged(BestBlockChanged { best_block_hash }); + self.best_block_cache = Some(best_block_hash); + finalized_block_descendants.push(best_block); + }; + + Ok((finalized_block_descendants, init.pruned_forks)) + } + + /// Generate the "NewBlock" event and potentially the "BestBlockChanged" event for the + /// given block hash. + fn generate_import_events( + &mut self, + block_hash: Block::Hash, + parent_block_hash: Block::Hash, + is_best_block: bool, + ) -> Vec> { + let new_runtime = self.generate_runtime_event(block_hash, Some(parent_block_hash)); + + let new_block = FollowEvent::NewBlock(NewBlock { + block_hash, + parent_block_hash, + new_runtime, + runtime_updates: self.runtime_updates, + }); + + if !is_best_block { + return vec![new_block] + } + + // If this is the new best block, then we need to generate two events. + let best_block_event = + FollowEvent::BestBlockChanged(BestBlockChanged { best_block_hash: block_hash }); + + match self.best_block_cache { + Some(block_cache) => { + // The RPC layer has not reported this block as best before. + // Note: This handles the race with the finalized branch. + if block_cache != block_hash { + self.best_block_cache = Some(block_hash); + vec![new_block, best_block_event] + } else { + vec![new_block] + } + }, + None => { + self.best_block_cache = Some(block_hash); + vec![new_block, best_block_event] + }, + } + } + + /// Handle the import of new blocks by generating the appropriate events. + fn handle_import_blocks( + &mut self, + notification: BlockImportNotification, + startup_point: &StartupPoint, + ) -> Result>, SubscriptionManagementError> { + // The block was already pinned by the initial block events or by the finalized event. + if !self.sub_handle.pin_block(notification.hash)? { + return Ok(Default::default()) + } + + // Ensure we are only reporting blocks after the starting point. + let Some(block_number) = self.client.number(notification.hash)? else { + return Err(SubscriptionManagementError::BlockNumberAbsent) + }; + if block_number < startup_point.finalized_number { + return Ok(Default::default()) + } + + Ok(self.generate_import_events( + notification.hash, + *notification.header.parent_hash(), + notification.is_new_best, + )) + } + + /// Generates new block events from the given finalized hashes. + /// + /// It may be possible that the `Finalized` event fired before the `NewBlock` + /// event. In that case, for each finalized hash that was not reported yet + /// generate the `NewBlock` event. For the final finalized hash we must also + /// generate one `BestBlock` event. + fn generate_finalized_events( + &mut self, + finalized_block_hashes: &[Block::Hash], + ) -> Result>, SubscriptionManagementError> { + let mut events = Vec::new(); + + // Nothing to be done if no finalized hashes are provided. + let Some(first_hash) = finalized_block_hashes.get(0) else { + return Ok(Default::default()) + }; + + // Find the parent hash. + let Some(first_number) = self.client.number(*first_hash)? else { + return Err(SubscriptionManagementError::BlockNumberAbsent) + }; + let Some(parent) = self.client.hash(first_number.saturating_sub(One::one()))? else { + return Err(SubscriptionManagementError::BlockHashAbsent) + }; + + let last_finalized = finalized_block_hashes + .last() + .expect("At least one finalized hash inserted; qed"); + let parents = std::iter::once(&parent).chain(finalized_block_hashes.iter()); + for (hash, parent) in finalized_block_hashes.iter().zip(parents) { + // This block is already reported by the import notification. + if !self.sub_handle.pin_block(*hash)? { + continue + } + + // Generate only the `NewBlock` event for this block. + if hash != last_finalized { + events.extend(self.generate_import_events(*hash, *parent, false)); + continue + } + + match self.best_block_cache { + Some(best_block_hash) => { + // If the best reported block is a children of the last finalized, + // then we had a gap in notification. + let ancestor = sp_blockchain::lowest_common_ancestor( + &*self.client, + *last_finalized, + best_block_hash, + )?; + + // A descendent of the finalized block was already reported + // before the `NewBlock` event containing the finalized block + // is reported. + if ancestor.hash == *last_finalized { + return Err(SubscriptionManagementError::Custom( + "A descendent of the finalized block was already reported".into(), + )) + } + self.best_block_cache = Some(*hash); + }, + // This is the first best block event that we generate. + None => { + self.best_block_cache = Some(*hash); + }, + }; + + // This is the first time we see this block. Generate the `NewBlock` event; if this is + // the last block, also generate the `BestBlock` event. + events.extend(self.generate_import_events(*hash, *parent, true)) + } + + Ok(events) + } + + /// Get all pruned block hashes from the provided stale heads. + /// + /// The result does not include hashes from `to_ignore`. + fn get_pruned_hashes( + &self, + stale_heads: &[Block::Hash], + last_finalized: Block::Hash, + to_ignore: &mut HashSet, + ) -> Result, SubscriptionManagementError> { + let blockchain = self.backend.blockchain(); + let mut pruned = Vec::new(); + + for stale_head in stale_heads { + let tree_route = sp_blockchain::tree_route(blockchain, last_finalized, *stale_head)?; + + // Collect only blocks that are not part of the canonical chain. + pruned.extend(tree_route.enacted().iter().filter_map(|block| { + if !to_ignore.remove(&block.hash) { + Some(block.hash) + } else { + None + } + })) + } + + Ok(pruned) + } + + /// Handle the finalization notification by generating the `Finalized` event. + /// + /// If the block of the notification was not reported yet, this method also + /// generates the events similar to `handle_import_blocks`. + fn handle_finalized_blocks( + &mut self, + notification: FinalityNotification, + to_ignore: &mut HashSet, + startup_point: &StartupPoint, + ) -> Result>, SubscriptionManagementError> { + let last_finalized = notification.hash; + + // Ensure we are only reporting blocks after the starting point. + let Some(block_number) = self.client.number(last_finalized)? else { + return Err(SubscriptionManagementError::BlockNumberAbsent) + }; + if block_number < startup_point.finalized_number { + return Ok(Default::default()) + } + + // The tree route contains the exclusive path from the last finalized block to the block + // reported by the notification. Ensure the finalized block is also reported. + let mut finalized_block_hashes = + notification.tree_route.iter().cloned().collect::>(); + finalized_block_hashes.push(last_finalized); + + // If the finalized hashes were not reported yet, generate the `NewBlock` events. + let mut events = self.generate_finalized_events(&finalized_block_hashes)?; + + // Report all pruned blocks from the notification that are not + // part of the fork we need to ignore. + let pruned_block_hashes = + self.get_pruned_hashes(¬ification.stale_heads, last_finalized, to_ignore)?; + + let finalized_event = FollowEvent::Finalized(Finalized { + finalized_block_hashes, + pruned_block_hashes: pruned_block_hashes.clone(), + }); + + match self.best_block_cache { + Some(block_cache) => { + // Check if the current best block is also reported as pruned. + let reported_pruned = pruned_block_hashes.iter().find(|&&hash| hash == block_cache); + if reported_pruned.is_none() { + events.push(finalized_event); + return Ok(events) + } + + // The best block is reported as pruned. Therefore, we need to signal a new + // best block event before submitting the finalized event. + let best_block_hash = self.client.info().best_hash; + if best_block_hash == block_cache { + // The client doest not have any new information about the best block. + // The information from `.info()` is updated from the DB as the last + // step of the finalization and it should be up to date. + // If the info is outdated, there is nothing the RPC can do for now. + error!( + target: LOG_TARGET, + "[follow][id={:?}] Client does not contain different best block", + self.sub_id, + ); + events.push(finalized_event); + Ok(events) + } else { + let ancestor = sp_blockchain::lowest_common_ancestor( + &*self.client, + last_finalized, + best_block_hash, + )?; + + // The client's best block must be a descendent of the last finalized block. + // In other words, the lowest common ancestor must be the last finalized block. + if ancestor.hash != last_finalized { + return Err(SubscriptionManagementError::Custom( + "The finalized block is not an ancestor of the best block".into(), + )) + } + + // The RPC needs to also submit a new best block changed before the + // finalized event. + self.best_block_cache = Some(best_block_hash); + let best_block_event = + FollowEvent::BestBlockChanged(BestBlockChanged { best_block_hash }); + events.extend([best_block_event, finalized_event]); + Ok(events) + } + }, + None => { + events.push(finalized_event); + Ok(events) + }, + } + } + + /// Submit the events from the provided stream to the RPC client + /// for as long as the `rx_stop` event was not called. + async fn submit_events( + &mut self, + startup_point: &StartupPoint, + mut stream: EventStream, + mut to_ignore: HashSet, + mut sink: SubscriptionSink, + rx_stop: oneshot::Receiver<()>, + ) where + EventStream: Stream> + Unpin, + { + let mut stream_item = stream.next(); + let mut stop_event = rx_stop; + + while let Either::Left((Some(event), next_stop_event)) = + futures_util::future::select(stream_item, stop_event).await + { + let events = match event { + NotificationType::InitialEvents(events) => Ok(events), + NotificationType::NewBlock(notification) => + self.handle_import_blocks(notification, &startup_point), + NotificationType::Finalized(notification) => + self.handle_finalized_blocks(notification, &mut to_ignore, &startup_point), + }; + + let events = match events { + Ok(events) => events, + Err(err) => { + debug!( + target: LOG_TARGET, + "[follow][id={:?}] Failed to handle stream notification {:?}", + self.sub_id, + err + ); + let _ = sink.send(&FollowEvent::::Stop); + return + }, + }; + + for event in events { + let result = sink.send(&event); + + // Migration note: the new version of jsonrpsee returns Result<(), DisconnectError> + // The logic from `Err(err)` should be moved when building the new + // `SubscriptionMessage`. + + // For now, jsonrpsee returns: + // Ok(true): message sent + // Ok(false): client disconnected or subscription closed + // Err(err): serder serialization error of the event + if let Err(err) = result { + // Failed to submit event. + debug!( + target: LOG_TARGET, + "[follow][id={:?}] Failed to send event {:?}", self.sub_id, err + ); + + let _ = sink.send(&FollowEvent::::Stop); + return + } + + if let Ok(false) = result { + // Client disconnected or subscription was closed. + return + } + } + + stream_item = stream.next(); + stop_event = next_stop_event; + } + } + + /// Generate the block events for the `chainHead_follow` method. + pub async fn generate_events( + &mut self, + mut sink: SubscriptionSink, + rx_stop: oneshot::Receiver<()>, + ) { + // Register for the new block and finalized notifications. + let stream_import = self + .client + .import_notification_stream() + .map(|notification| NotificationType::NewBlock(notification)); + + let stream_finalized = self + .client + .finality_notification_stream() + .map(|notification| NotificationType::Finalized(notification)); + + let startup_point = StartupPoint::from(self.client.info()); + let (initial_events, pruned_forks) = match self.generate_init_events(&startup_point) { + Ok(blocks) => blocks, + Err(err) => { + debug!( + target: LOG_TARGET, + "[follow][id={:?}] Failed to generate the initial events {:?}", + self.sub_id, + err + ); + let _ = sink.send(&FollowEvent::::Stop); + return + }, + }; + + let initial = NotificationType::InitialEvents(initial_events); + let merged = tokio_stream::StreamExt::merge(stream_import, stream_finalized); + let stream = stream::once(futures::future::ready(initial)).chain(merged); + + self.submit_events(&startup_point, stream.boxed(), pruned_forks, sink, rx_stop) + .await; + } +} diff --git a/client/rpc-spec-v2/src/chain_head/mod.rs b/client/rpc-spec-v2/src/chain_head/mod.rs index 3d6d5a916..afa8d3b21 100644 --- a/client/rpc-spec-v2/src/chain_head/mod.rs +++ b/client/rpc-spec-v2/src/chain_head/mod.rs @@ -30,6 +30,7 @@ pub mod chain_head; pub mod error; pub mod event; +mod chain_head_follow; mod subscription; pub use api::ChainHeadApiServer; diff --git a/client/rpc-spec-v2/src/chain_head/subscription.rs b/client/rpc-spec-v2/src/chain_head/subscription.rs index e52336097..77d57e747 100644 --- a/client/rpc-spec-v2/src/chain_head/subscription.rs +++ b/client/rpc-spec-v2/src/chain_head/subscription.rs @@ -19,7 +19,8 @@ //! Subscription management for tracking subscription IDs to pinned blocks. use futures::channel::oneshot; -use parking_lot::{RwLock, RwLockWriteGuard}; +use parking_lot::RwLock; +use sp_blockchain::Error; use sp_runtime::traits::Block as BlockT; use std::{ collections::{hash_map::Entry, HashMap, HashSet}, @@ -33,10 +34,22 @@ pub enum SubscriptionManagementError { /// the subscription has exceeded the maximum number /// of blocks pinned. ExceededLimits, + /// Error originated from the blockchain (client or backend). + Blockchain(Error), + /// The database does not contain a block number. + BlockNumberAbsent, + /// The database does not contain a block hash. + BlockHashAbsent, /// Custom error. Custom(String), } +impl From for SubscriptionManagementError { + fn from(err: Error) -> Self { + SubscriptionManagementError::Blockchain(err) + } +} + /// Inner subscription data structure. struct SubscriptionInner { /// The `runtime_updates` parameter flag of the subscription. @@ -53,10 +66,6 @@ struct SubscriptionInner { #[derive(Clone)] pub struct SubscriptionHandle { inner: Arc>>, - /// The best reported block by this subscription. - /// Have this as a separate variable to easily share - /// the write guard with the RPC layer. - best_block: Arc>>, } impl SubscriptionHandle { @@ -69,7 +78,6 @@ impl SubscriptionHandle { blocks: HashSet::new(), max_pinned_blocks, })), - best_block: Arc::new(RwLock::new(None)), } } @@ -125,11 +133,6 @@ impl SubscriptionHandle { let inner = self.inner.read(); inner.runtime_updates } - - /// Get the write guard of the best reported block. - pub fn best_block_write(&self) -> RwLockWriteGuard<'_, Option> { - self.best_block.write() - } } /// Manage block pinning / unpinning for subscription IDs. diff --git a/client/rpc-spec-v2/src/chain_head/tests.rs b/client/rpc-spec-v2/src/chain_head/tests.rs index 79be39136..0886efa94 100644 --- a/client/rpc-spec-v2/src/chain_head/tests.rs +++ b/client/rpc-spec-v2/src/chain_head/tests.rs @@ -1015,4 +1015,305 @@ async fn follow_prune_best_block() { pruned_block_hashes: vec![format!("{:?}", block_2_hash)], }); assert_eq!(event, expected); + + // Pruned hash can be unpinned. + let sub_id = sub.subscription_id(); + let sub_id = serde_json::to_string(&sub_id).unwrap(); + let hash = format!("{:?}", block_2_hash); + let _res: () = api.call("chainHead_unstable_unpin", [&sub_id, &hash]).await.unwrap(); +} + +#[tokio::test] +async fn follow_forks_pruned_block() { + let builder = TestClientBuilder::new(); + let backend = builder.backend(); + let mut client = Arc::new(builder.build()); + + let api = ChainHead::new( + client.clone(), + backend, + Arc::new(TaskExecutor::default()), + CHAIN_GENESIS, + MAX_PINNED_BLOCKS, + ) + .into_rpc(); + + // Block tree before the subscription: + // + // finalized -> block 1 -> block 2 -> block 3 + // ^^^ finalized + // -> block 1 -> block 4 -> block 5 + // + + let block_1 = client.new_block(Default::default()).unwrap().build().unwrap().block; + client.import(BlockOrigin::Own, block_1.clone()).await.unwrap(); + + let block_2 = client.new_block(Default::default()).unwrap().build().unwrap().block; + client.import(BlockOrigin::Own, block_2.clone()).await.unwrap(); + + let block_3 = client.new_block(Default::default()).unwrap().build().unwrap().block; + let block_3_hash = block_3.header.hash(); + client.import(BlockOrigin::Own, block_3.clone()).await.unwrap(); + + // Block 4 with parent Block 1 is not the best imported. + let mut block_builder = + client.new_block_at(block_1.header.hash(), Default::default(), false).unwrap(); + // This push is required as otherwise block 4 has the same hash as block 2 and won't get + // imported + block_builder + .push_transfer(Transfer { + from: AccountKeyring::Alice.into(), + to: AccountKeyring::Ferdie.into(), + amount: 41, + nonce: 0, + }) + .unwrap(); + let block_4 = block_builder.build().unwrap().block; + client.import(BlockOrigin::Own, block_4.clone()).await.unwrap(); + + let mut block_builder = + client.new_block_at(block_4.header.hash(), Default::default(), false).unwrap(); + block_builder + .push_transfer(Transfer { + from: AccountKeyring::Bob.into(), + to: AccountKeyring::Ferdie.into(), + amount: 41, + nonce: 0, + }) + .unwrap(); + let block_5 = block_builder.build().unwrap().block; + client.import(BlockOrigin::Own, block_5.clone()).await.unwrap(); + + // Block 4 and 5 are not pruned, pruning happens at height (N - 1). + client.finalize_block(block_3_hash, None).unwrap(); + + let mut sub = api.subscribe("chainHead_unstable_follow", [false]).await.unwrap(); + + // Initialized must always be reported first. + let event: FollowEvent = get_next_event(&mut sub).await; + let expected = FollowEvent::Initialized(Initialized { + finalized_block_hash: format!("{:?}", block_3_hash), + finalized_block_runtime: None, + runtime_updates: false, + }); + assert_eq!(event, expected); + + // Block tree: + // + // finalized -> block 1 -> block 2 -> block 3 -> block 6 + // ^^^ finalized + // -> block 1 -> block 4 -> block 5 + // + // Mark block 6 as finalized to force block 4 and 5 to get pruned. + + let block_6 = client.new_block(Default::default()).unwrap().build().unwrap().block; + let block_6_hash = block_6.header.hash(); + client.import(BlockOrigin::Own, block_6.clone()).await.unwrap(); + + client.finalize_block(block_6_hash, None).unwrap(); + + // Check block 6. + let event: FollowEvent = get_next_event(&mut sub).await; + let expected = FollowEvent::NewBlock(NewBlock { + block_hash: format!("{:?}", block_6_hash), + parent_block_hash: format!("{:?}", block_3_hash), + new_runtime: None, + runtime_updates: false, + }); + assert_eq!(event, expected); + let event: FollowEvent = get_next_event(&mut sub).await; + let expected = FollowEvent::BestBlockChanged(BestBlockChanged { + best_block_hash: format!("{:?}", block_6_hash), + }); + assert_eq!(event, expected); + + // Block 4 and 5 must not be reported as pruned. + let event: FollowEvent = get_next_event(&mut sub).await; + let expected = FollowEvent::Finalized(Finalized { + finalized_block_hashes: vec![format!("{:?}", block_6_hash)], + pruned_block_hashes: vec![], + }); + assert_eq!(event, expected); +} + +#[tokio::test] +async fn follow_report_multiple_pruned_block() { + let builder = TestClientBuilder::new(); + let backend = builder.backend(); + let mut client = Arc::new(builder.build()); + + let api = ChainHead::new( + client.clone(), + backend, + Arc::new(TaskExecutor::default()), + CHAIN_GENESIS, + MAX_PINNED_BLOCKS, + ) + .into_rpc(); + + // Block tree: + // + // finalized -> block 1 -> block 2 -> block 3 + // ^^^ finalized after subscription + // -> block 1 -> block 4 -> block 5 + + let finalized_hash = client.info().finalized_hash; + + let block_1 = client.new_block(Default::default()).unwrap().build().unwrap().block; + let block_1_hash = block_1.header.hash(); + client.import(BlockOrigin::Own, block_1.clone()).await.unwrap(); + + let block_2 = client.new_block(Default::default()).unwrap().build().unwrap().block; + let block_2_hash = block_2.header.hash(); + client.import(BlockOrigin::Own, block_2.clone()).await.unwrap(); + + let block_3 = client.new_block(Default::default()).unwrap().build().unwrap().block; + let block_3_hash = block_3.header.hash(); + client.import(BlockOrigin::Own, block_3.clone()).await.unwrap(); + + // Block 4 with parent Block 1 is not the best imported. + let mut block_builder = + client.new_block_at(block_1.header.hash(), Default::default(), false).unwrap(); + // This push is required as otherwise block 4 has the same hash as block 2 and won't get + // imported + block_builder + .push_transfer(Transfer { + from: AccountKeyring::Alice.into(), + to: AccountKeyring::Ferdie.into(), + amount: 41, + nonce: 0, + }) + .unwrap(); + let block_4 = block_builder.build().unwrap().block; + let block_4_hash = block_4.header.hash(); + client.import(BlockOrigin::Own, block_4.clone()).await.unwrap(); + + let mut block_builder = + client.new_block_at(block_4.header.hash(), Default::default(), false).unwrap(); + block_builder + .push_transfer(Transfer { + from: AccountKeyring::Bob.into(), + to: AccountKeyring::Ferdie.into(), + amount: 41, + nonce: 0, + }) + .unwrap(); + let block_5 = block_builder.build().unwrap().block; + let block_5_hash = block_5.header.hash(); + client.import(BlockOrigin::Own, block_5.clone()).await.unwrap(); + let mut sub = api.subscribe("chainHead_unstable_follow", [false]).await.unwrap(); + + // Initialized must always be reported first. + let event: FollowEvent = get_next_event(&mut sub).await; + let expected = FollowEvent::Initialized(Initialized { + finalized_block_hash: format!("{:?}", finalized_hash), + finalized_block_runtime: None, + runtime_updates: false, + }); + assert_eq!(event, expected); + + let event: FollowEvent = get_next_event(&mut sub).await; + let expected = FollowEvent::NewBlock(NewBlock { + block_hash: format!("{:?}", block_1_hash), + parent_block_hash: format!("{:?}", finalized_hash), + new_runtime: None, + runtime_updates: false, + }); + assert_eq!(event, expected); + + let event: FollowEvent = get_next_event(&mut sub).await; + let expected = FollowEvent::NewBlock(NewBlock { + block_hash: format!("{:?}", block_2_hash), + parent_block_hash: format!("{:?}", block_1_hash), + new_runtime: None, + runtime_updates: false, + }); + assert_eq!(event, expected); + + let event: FollowEvent = get_next_event(&mut sub).await; + let expected = FollowEvent::NewBlock(NewBlock { + block_hash: format!("{:?}", block_3_hash), + parent_block_hash: format!("{:?}", block_2_hash), + new_runtime: None, + runtime_updates: false, + }); + assert_eq!(event, expected); + + // The fork must also be reported. + let event: FollowEvent = get_next_event(&mut sub).await; + let expected = FollowEvent::NewBlock(NewBlock { + block_hash: format!("{:?}", block_4_hash), + parent_block_hash: format!("{:?}", block_1_hash), + new_runtime: None, + runtime_updates: false, + }); + assert_eq!(event, expected); + + let event: FollowEvent = get_next_event(&mut sub).await; + let expected = FollowEvent::NewBlock(NewBlock { + block_hash: format!("{:?}", block_5_hash), + parent_block_hash: format!("{:?}", block_4_hash), + new_runtime: None, + runtime_updates: false, + }); + assert_eq!(event, expected); + + // The best block of the chain must also be reported. + let event: FollowEvent = get_next_event(&mut sub).await; + let expected = FollowEvent::BestBlockChanged(BestBlockChanged { + best_block_hash: format!("{:?}", block_3_hash), + }); + assert_eq!(event, expected); + + // Block 4 and 5 are not pruned, pruning happens at height (N - 1). + client.finalize_block(block_3_hash, None).unwrap(); + + // Finalizing block 3 directly will also result in block 1 and 2 being finalized. + let event: FollowEvent = get_next_event(&mut sub).await; + let expected = FollowEvent::Finalized(Finalized { + finalized_block_hashes: vec![ + format!("{:?}", block_1_hash), + format!("{:?}", block_2_hash), + format!("{:?}", block_3_hash), + ], + pruned_block_hashes: vec![], + }); + assert_eq!(event, expected); + + // Block tree: + // + // finalized -> block 1 -> block 2 -> block 3 -> block 6 + // ^^^ finalized + // -> block 1 -> block 4 -> block 5 + // + // Mark block 6 as finalized to force block 4 and 5 to get pruned. + + let block_6 = client.new_block(Default::default()).unwrap().build().unwrap().block; + let block_6_hash = block_6.header.hash(); + client.import(BlockOrigin::Own, block_6.clone()).await.unwrap(); + + client.finalize_block(block_6_hash, None).unwrap(); + + // Check block 6. + let event: FollowEvent = get_next_event(&mut sub).await; + let expected = FollowEvent::NewBlock(NewBlock { + block_hash: format!("{:?}", block_6_hash), + parent_block_hash: format!("{:?}", block_3_hash), + new_runtime: None, + runtime_updates: false, + }); + assert_eq!(event, expected); + let event: FollowEvent = get_next_event(&mut sub).await; + let expected = FollowEvent::BestBlockChanged(BestBlockChanged { + best_block_hash: format!("{:?}", block_6_hash), + }); + assert_eq!(event, expected); + + // Block 4 and 5 be reported as pruned, not just the stale head (block 5). + let event: FollowEvent = get_next_event(&mut sub).await; + let expected = FollowEvent::Finalized(Finalized { + finalized_block_hashes: vec![format!("{:?}", block_6_hash)], + pruned_block_hashes: vec![format!("{:?}", block_4_hash), format!("{:?}", block_5_hash)], + }); + assert_eq!(event, expected); } From d90e41ae9bf51319b2579a3c932e7412e4726aa7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Silva?= <123550+andresilva@users.noreply.github.com> Date: Tue, 7 Mar 2023 10:47:13 +0000 Subject: [PATCH 205/558] core: remove unused localized signature type (#13552) --- primitives/core/src/ed25519.rs | 10 ---------- primitives/core/src/sr25519.rs | 11 ----------- 2 files changed, 21 deletions(-) diff --git a/primitives/core/src/ed25519.rs b/primitives/core/src/ed25519.rs index 4eb9401a9..76b3064ad 100644 --- a/primitives/core/src/ed25519.rs +++ b/primitives/core/src/ed25519.rs @@ -332,16 +332,6 @@ impl Signature { } } -/// A localized signature also contains sender information. -#[cfg(feature = "std")] -#[derive(PartialEq, Eq, Clone, Debug, Encode, Decode)] -pub struct LocalizedSignature { - /// The signer of the signature. - pub signer: Public, - /// The signature itself. - pub signature: Signature, -} - impl Public { /// A new instance from the given 32-byte `data`. /// diff --git a/primitives/core/src/sr25519.rs b/primitives/core/src/sr25519.rs index 1206cf5f4..809d83aaf 100644 --- a/primitives/core/src/sr25519.rs +++ b/primitives/core/src/sr25519.rs @@ -307,17 +307,6 @@ impl sp_std::fmt::Debug for Signature { } } -/// A localized signature also contains sender information. -/// NOTE: Encode and Decode traits are supported in ed25519 but not possible for now here. -#[cfg(feature = "std")] -#[derive(PartialEq, Eq, Clone, Debug)] -pub struct LocalizedSignature { - /// The signer of the signature. - pub signer: Public, - /// The signature itself. - pub signature: Signature, -} - impl UncheckedFrom<[u8; 64]> for Signature { fn unchecked_from(data: [u8; 64]) -> Signature { Signature(data) From 8d0b7564d5ba9ee0223a84f72ed15ac64c3be068 Mon Sep 17 00:00:00 2001 From: afm Date: Tue, 7 Mar 2023 08:14:12 -0300 Subject: [PATCH 206/558] Pub enum runtime to pub struct runtime (#13250) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * pub enum Runtime -> pub struct Runtime * changing some more * fmt * updating *.stderr files * re-run trybuild after rust update * keep a test file for `pub enum Runtime` * Delete construct_runtime_2.rs * ".git/.scripts/commands/fmt/fmt.sh" --------- Co-authored-by: Bastian Köcher Co-authored-by: parity-processbot <> --- bin/node/runtime/src/lib.rs | 2 +- frame/bags-list/src/mock.rs | 2 +- frame/election-provider-multi-phase/src/mock.rs | 2 +- frame/election-provider-support/src/onchain.rs | 2 +- frame/executive/src/lib.rs | 2 +- frame/fast-unstake/src/mock.rs | 5 +++-- frame/im-online/src/mock.rs | 2 +- frame/nomination-pools/benchmarking/src/mock.rs | 2 +- frame/nomination-pools/src/mock.rs | 2 +- frame/nomination-pools/test-staking/src/mock.rs | 2 +- frame/offences/src/mock.rs | 2 +- frame/support/test/compile_pass/src/lib.rs | 5 +++-- frame/support/test/tests/construct_runtime.rs | 4 ++-- .../construct_runtime_ui/abundant_where_param.rs | 2 +- .../both_use_and_excluded_parts.rs | 2 +- .../tests/construct_runtime_ui/conflicting_index.rs | 2 +- .../construct_runtime_ui/conflicting_index_2.rs | 2 +- .../construct_runtime_ui/conflicting_module_name.rs | 2 +- .../construct_runtime_ui/double_module_parts.rs | 2 +- .../tests/construct_runtime_ui/duplicate_exclude.rs | 2 +- .../tests/construct_runtime_ui/empty_pallet_path.rs | 2 +- .../tests/construct_runtime_ui/exclude_missspell.rs | 2 +- .../construct_runtime_ui/exclude_undefined_part.rs | 2 +- .../feature_gated_system_pallet.rs | 2 +- .../generics_in_invalid_module.rs | 2 +- .../construct_runtime_ui/invalid_meta_literal.rs | 2 +- .../construct_runtime_ui/invalid_module_details.rs | 2 +- .../invalid_module_details_keyword.rs | 2 +- .../construct_runtime_ui/invalid_module_entry.rs | 2 +- .../invalid_token_after_module.rs | 2 +- .../construct_runtime_ui/invalid_token_after_name.rs | 2 +- .../construct_runtime_ui/invalid_where_param.rs | 2 +- .../missing_event_generic_on_module_with_instance.rs | 2 +- .../construct_runtime_ui/missing_module_instance.rs | 2 +- ...missing_origin_generic_on_module_with_instance.rs | 2 +- .../construct_runtime_ui/missing_system_module.rs | 2 +- .../construct_runtime_ui/missing_where_block.rs | 2 +- .../construct_runtime_ui/missing_where_block.stderr | 6 +++--- .../construct_runtime_ui/missing_where_param.rs | 2 +- .../construct_runtime_ui/more_than_256_modules.rs | 2 +- .../construct_runtime_ui/no_comma_after_where.rs | 2 +- .../construct_runtime_ui/no_std_genesis_config.rs | 2 +- .../no_std_genesis_config.stderr | 6 +++--- .../old_unsupported_pallet_decl.rs | 2 +- .../old_unsupported_pallet_decl.stderr | 8 ++++---- .../construct_runtime_ui/pallet_error_too_large.rs | 2 +- .../pallet_error_too_large.stderr | 2 +- .../construct_runtime_ui/undefined_call_part.rs | 2 +- .../construct_runtime_ui/undefined_call_part.stderr | 2 +- .../construct_runtime_ui/undefined_event_part.rs | 2 +- .../construct_runtime_ui/undefined_event_part.stderr | 4 ++-- .../undefined_genesis_config_part.rs | 2 +- .../undefined_genesis_config_part.stderr | 6 +++--- .../construct_runtime_ui/undefined_inherent_part.rs | 2 +- .../undefined_inherent_part.stderr | 12 ++++++------ .../construct_runtime_ui/undefined_origin_part.rs | 2 +- .../undefined_origin_part.stderr | 6 +++--- .../undefined_validate_unsigned_part.rs | 2 +- .../undefined_validate_unsigned_part.stderr | 8 ++++---- .../unsupported_meta_structure.rs | 2 +- .../construct_runtime_ui/unsupported_pallet_attr.rs | 2 +- .../tests/construct_runtime_ui/use_undefined_part.rs | 2 +- frame/support/test/tests/instance.rs | 2 +- frame/support/test/tests/issue2219.rs | 2 +- frame/support/test/tests/pallet.rs | 2 +- frame/support/test/tests/pallet_compatibility.rs | 2 +- .../test/tests/pallet_compatibility_instance.rs | 2 +- frame/support/test/tests/pallet_instance.rs | 2 +- .../test/tests/pallet_with_name_trait_is_valid.rs | 2 +- frame/support/test/tests/storage_layers.rs | 2 +- frame/system/benches/bench.rs | 2 +- .../transaction-payment/asset-tx-payment/src/mock.rs | 3 ++- frame/transaction-payment/src/mock.rs | 2 +- 73 files changed, 99 insertions(+), 96 deletions(-) diff --git a/bin/node/runtime/src/lib.rs b/bin/node/runtime/src/lib.rs index 0c21fcc90..3d160108f 100644 --- a/bin/node/runtime/src/lib.rs +++ b/bin/node/runtime/src/lib.rs @@ -1740,7 +1740,7 @@ impl frame_benchmarking_pallet_pov::Config for Runtime { } construct_runtime!( - pub enum Runtime where + pub struct Runtime where Block = Block, NodeBlock = node_primitives::Block, UncheckedExtrinsic = UncheckedExtrinsic diff --git a/frame/bags-list/src/mock.rs b/frame/bags-list/src/mock.rs index 2ccc3c438..a48b66b20 100644 --- a/frame/bags-list/src/mock.rs +++ b/frame/bags-list/src/mock.rs @@ -88,7 +88,7 @@ impl bags_list::Config for Runtime { type UncheckedExtrinsic = frame_system::mocking::MockUncheckedExtrinsic; type Block = frame_system::mocking::MockBlock; frame_support::construct_runtime!( - pub enum Runtime where + pub struct Runtime where Block = Block, NodeBlock = Block, UncheckedExtrinsic = UncheckedExtrinsic, diff --git a/frame/election-provider-multi-phase/src/mock.rs b/frame/election-provider-multi-phase/src/mock.rs index a46aa861e..8dee94bb3 100644 --- a/frame/election-provider-multi-phase/src/mock.rs +++ b/frame/election-provider-multi-phase/src/mock.rs @@ -54,7 +54,7 @@ pub type UncheckedExtrinsic = sp_runtime::generic::UncheckedExtrinsic; frame_support::construct_runtime!( - pub enum Runtime where + pub struct Runtime where Block = Block, NodeBlock = Block, UncheckedExtrinsic = UncheckedExtrinsic diff --git a/frame/election-provider-support/src/onchain.rs b/frame/election-provider-support/src/onchain.rs index 296ac976d..a312562d4 100644 --- a/frame/election-provider-support/src/onchain.rs +++ b/frame/election-provider-support/src/onchain.rs @@ -197,7 +197,7 @@ mod tests { pub type Block = sp_runtime::generic::Block; frame_support::construct_runtime!( - pub enum Runtime where + pub struct Runtime where Block = Block, NodeBlock = Block, UncheckedExtrinsic = UncheckedExtrinsic diff --git a/frame/executive/src/lib.rs b/frame/executive/src/lib.rs index 05f3ee4f5..b3a04c4fe 100644 --- a/frame/executive/src/lib.rs +++ b/frame/executive/src/lib.rs @@ -838,7 +838,7 @@ mod tests { } frame_support::construct_runtime!( - pub enum Runtime where + pub struct Runtime where Block = TestBlock, NodeBlock = TestBlock, UncheckedExtrinsic = TestUncheckedExtrinsic diff --git a/frame/fast-unstake/src/mock.rs b/frame/fast-unstake/src/mock.rs index b20ba43ab..c9a1d6d51 100644 --- a/frame/fast-unstake/src/mock.rs +++ b/frame/fast-unstake/src/mock.rs @@ -193,10 +193,11 @@ impl fast_unstake::Config for Runtime { type Block = frame_system::mocking::MockBlock; type UncheckedExtrinsic = frame_system::mocking::MockUncheckedExtrinsic; frame_support::construct_runtime!( - pub enum Runtime where + pub struct Runtime + where Block = Block, NodeBlock = Block, - UncheckedExtrinsic = UncheckedExtrinsic + UncheckedExtrinsic = UncheckedExtrinsic, { System: frame_system, Timestamp: pallet_timestamp, diff --git a/frame/im-online/src/mock.rs b/frame/im-online/src/mock.rs index 97b411e87..64e77b24b 100644 --- a/frame/im-online/src/mock.rs +++ b/frame/im-online/src/mock.rs @@ -43,7 +43,7 @@ type UncheckedExtrinsic = frame_system::mocking::MockUncheckedExtrinsic type Block = frame_system::mocking::MockBlock; frame_support::construct_runtime!( - pub enum Runtime where + pub struct Runtime where Block = Block, NodeBlock = Block, UncheckedExtrinsic = UncheckedExtrinsic, diff --git a/frame/nomination-pools/benchmarking/src/mock.rs b/frame/nomination-pools/benchmarking/src/mock.rs index 4e188ea7e..f6b2022bc 100644 --- a/frame/nomination-pools/benchmarking/src/mock.rs +++ b/frame/nomination-pools/benchmarking/src/mock.rs @@ -173,7 +173,7 @@ impl crate::Config for Runtime {} type Block = frame_system::mocking::MockBlock; type UncheckedExtrinsic = frame_system::mocking::MockUncheckedExtrinsic; frame_support::construct_runtime!( - pub enum Runtime where + pub struct Runtime where Block = Block, NodeBlock = Block, UncheckedExtrinsic = UncheckedExtrinsic diff --git a/frame/nomination-pools/src/mock.rs b/frame/nomination-pools/src/mock.rs index 99d521df3..e8924c0b9 100644 --- a/frame/nomination-pools/src/mock.rs +++ b/frame/nomination-pools/src/mock.rs @@ -236,7 +236,7 @@ impl pools::Config for Runtime { type UncheckedExtrinsic = frame_system::mocking::MockUncheckedExtrinsic; type Block = frame_system::mocking::MockBlock; frame_support::construct_runtime!( - pub enum Runtime where + pub struct Runtime where Block = Block, NodeBlock = Block, UncheckedExtrinsic = UncheckedExtrinsic, diff --git a/frame/nomination-pools/test-staking/src/mock.rs b/frame/nomination-pools/test-staking/src/mock.rs index 3d0ab2c6f..85d0dd6c5 100644 --- a/frame/nomination-pools/test-staking/src/mock.rs +++ b/frame/nomination-pools/test-staking/src/mock.rs @@ -186,7 +186,7 @@ type Block = frame_system::mocking::MockBlock; type UncheckedExtrinsic = frame_system::mocking::MockUncheckedExtrinsic; frame_support::construct_runtime!( - pub enum Runtime where + pub struct Runtime where Block = Block, NodeBlock = Block, UncheckedExtrinsic = UncheckedExtrinsic diff --git a/frame/offences/src/mock.rs b/frame/offences/src/mock.rs index 5fc987ebd..d0fc7377a 100644 --- a/frame/offences/src/mock.rs +++ b/frame/offences/src/mock.rs @@ -70,7 +70,7 @@ type UncheckedExtrinsic = frame_system::mocking::MockUncheckedExtrinsic type Block = frame_system::mocking::MockBlock; frame_support::construct_runtime!( - pub enum Runtime where + pub struct Runtime where Block = Block, NodeBlock = Block, UncheckedExtrinsic = UncheckedExtrinsic, diff --git a/frame/support/test/compile_pass/src/lib.rs b/frame/support/test/compile_pass/src/lib.rs index 1ff72bea5..7e9fdaff2 100644 --- a/frame/support/test/compile_pass/src/lib.rs +++ b/frame/support/test/compile_pass/src/lib.rs @@ -85,10 +85,11 @@ pub type Block = generic::Block; pub type UncheckedExtrinsic = generic::UncheckedExtrinsic; construct_runtime!( - pub enum Runtime where + pub struct Runtime + where Block = Block, NodeBlock = Block, - UncheckedExtrinsic = UncheckedExtrinsic + UncheckedExtrinsic = UncheckedExtrinsic, { System: frame_system, } diff --git a/frame/support/test/tests/construct_runtime.rs b/frame/support/test/tests/construct_runtime.rs index 24f8a278b..d0b31d71a 100644 --- a/frame/support/test/tests/construct_runtime.rs +++ b/frame/support/test/tests/construct_runtime.rs @@ -16,7 +16,7 @@ // limitations under the License. //! General tests for construct_runtime macro, test for: -//! * error declareed with decl_error works +//! * error declared with decl_error works //! * integrity test is generated #![recursion_limit = "128"] @@ -254,7 +254,7 @@ impl system::Config for Runtime { } frame_support::construct_runtime!( - pub enum Runtime where + pub struct Runtime where Block = Block, NodeBlock = Block, UncheckedExtrinsic = UncheckedExtrinsic diff --git a/frame/support/test/tests/construct_runtime_ui/abundant_where_param.rs b/frame/support/test/tests/construct_runtime_ui/abundant_where_param.rs index d5e9f2252..ab55c22e9 100644 --- a/frame/support/test/tests/construct_runtime_ui/abundant_where_param.rs +++ b/frame/support/test/tests/construct_runtime_ui/abundant_where_param.rs @@ -1,7 +1,7 @@ use frame_support::construct_runtime; construct_runtime! { - pub enum Runtime where + pub struct Runtime where Block = Block, NodeBlock = Block, Block = Block1, diff --git a/frame/support/test/tests/construct_runtime_ui/both_use_and_excluded_parts.rs b/frame/support/test/tests/construct_runtime_ui/both_use_and_excluded_parts.rs index 7a074db99..ea468d6de 100644 --- a/frame/support/test/tests/construct_runtime_ui/both_use_and_excluded_parts.rs +++ b/frame/support/test/tests/construct_runtime_ui/both_use_and_excluded_parts.rs @@ -20,7 +20,7 @@ pub type UncheckedExtrinsic = generic::UncheckedExtrinsic tests/construct_runtime_ui/missing_where_block.rs:4:19 + --> tests/construct_runtime_ui/missing_where_block.rs:4:21 | -4 | pub enum Runtime {} - | ^ +4 | pub struct Runtime {} + | ^ diff --git a/frame/support/test/tests/construct_runtime_ui/missing_where_param.rs b/frame/support/test/tests/construct_runtime_ui/missing_where_param.rs index 2e311c5ea..4d2225a4a 100644 --- a/frame/support/test/tests/construct_runtime_ui/missing_where_param.rs +++ b/frame/support/test/tests/construct_runtime_ui/missing_where_param.rs @@ -1,7 +1,7 @@ use frame_support::construct_runtime; construct_runtime! { - pub enum Runtime where + pub struct Runtime where Block = Block, NodeBlock = Block, {} diff --git a/frame/support/test/tests/construct_runtime_ui/more_than_256_modules.rs b/frame/support/test/tests/construct_runtime_ui/more_than_256_modules.rs index 4c8331ae4..7dcbdb9aa 100644 --- a/frame/support/test/tests/construct_runtime_ui/more_than_256_modules.rs +++ b/frame/support/test/tests/construct_runtime_ui/more_than_256_modules.rs @@ -1,7 +1,7 @@ use frame_support::construct_runtime; construct_runtime! { - pub enum Runtime where + pub struct Runtime where UncheckedExtrinsic = UncheckedExtrinsic, Block = Block, NodeBlock = Block, diff --git a/frame/support/test/tests/construct_runtime_ui/no_comma_after_where.rs b/frame/support/test/tests/construct_runtime_ui/no_comma_after_where.rs index 954fadefa..499f9a5cd 100644 --- a/frame/support/test/tests/construct_runtime_ui/no_comma_after_where.rs +++ b/frame/support/test/tests/construct_runtime_ui/no_comma_after_where.rs @@ -1,7 +1,7 @@ use frame_support::construct_runtime; construct_runtime! { - pub enum Runtime where + pub struct Runtime where UncheckedExtrinsic = UncheckedExtrinsic Block = Block, NodeBlock = Block, diff --git a/frame/support/test/tests/construct_runtime_ui/no_std_genesis_config.rs b/frame/support/test/tests/construct_runtime_ui/no_std_genesis_config.rs index 2ca967640..9c9c49c4b 100644 --- a/frame/support/test/tests/construct_runtime_ui/no_std_genesis_config.rs +++ b/frame/support/test/tests/construct_runtime_ui/no_std_genesis_config.rs @@ -38,7 +38,7 @@ impl frame_system::Config for Runtime { } construct_runtime! { - pub enum Runtime where + pub struct Runtime where Block = Block, NodeBlock = Block, UncheckedExtrinsic = UncheckedExtrinsic diff --git a/frame/support/test/tests/construct_runtime_ui/no_std_genesis_config.stderr b/frame/support/test/tests/construct_runtime_ui/no_std_genesis_config.stderr index d35565fb9..2cf451aa6 100644 --- a/frame/support/test/tests/construct_runtime_ui/no_std_genesis_config.stderr +++ b/frame/support/test/tests/construct_runtime_ui/no_std_genesis_config.stderr @@ -2,7 +2,7 @@ error: `Pallet` does not have the std feature enabled, this will cause the `test --> tests/construct_runtime_ui/no_std_genesis_config.rs:40:1 | 40 | / construct_runtime! { -41 | | pub enum Runtime where +41 | | pub struct Runtime where 42 | | Block = Block, 43 | | NodeBlock = Block, ... | @@ -16,7 +16,7 @@ error[E0412]: cannot find type `GenesisConfig` in crate `test_pallet` --> tests/construct_runtime_ui/no_std_genesis_config.rs:40:1 | 40 | / construct_runtime! { -41 | | pub enum Runtime where +41 | | pub struct Runtime where 42 | | Block = Block, 43 | | NodeBlock = Block, ... | @@ -34,7 +34,7 @@ error[E0283]: type annotations needed --> tests/construct_runtime_ui/no_std_genesis_config.rs:40:1 | 40 | / construct_runtime! { -41 | | pub enum Runtime where +41 | | pub struct Runtime where 42 | | Block = Block, 43 | | NodeBlock = Block, ... | diff --git a/frame/support/test/tests/construct_runtime_ui/old_unsupported_pallet_decl.rs b/frame/support/test/tests/construct_runtime_ui/old_unsupported_pallet_decl.rs index 5691549c2..477ca3cc0 100644 --- a/frame/support/test/tests/construct_runtime_ui/old_unsupported_pallet_decl.rs +++ b/frame/support/test/tests/construct_runtime_ui/old_unsupported_pallet_decl.rs @@ -13,7 +13,7 @@ mod pallet_old { } construct_runtime! { - pub enum Runtime where + pub struct Runtime where UncheckedExtrinsic = UncheckedExtrinsic, Block = Block, NodeBlock = Block, diff --git a/frame/support/test/tests/construct_runtime_ui/old_unsupported_pallet_decl.stderr b/frame/support/test/tests/construct_runtime_ui/old_unsupported_pallet_decl.stderr index f8ec07e00..6029e7e6f 100644 --- a/frame/support/test/tests/construct_runtime_ui/old_unsupported_pallet_decl.stderr +++ b/frame/support/test/tests/construct_runtime_ui/old_unsupported_pallet_decl.stderr @@ -1,8 +1,8 @@ error[E0433]: failed to resolve: could not find `tt_default_parts` in `pallet_old` - --> $DIR/old_unsupported_pallet_decl.rs:15:1 + --> tests/construct_runtime_ui/old_unsupported_pallet_decl.rs:15:1 | 15 | / construct_runtime! { -16 | | pub enum Runtime where +16 | | pub struct Runtime where 17 | | UncheckedExtrinsic = UncheckedExtrinsic, 18 | | Block = Block, ... | @@ -13,7 +13,7 @@ error[E0433]: failed to resolve: could not find `tt_default_parts` in `pallet_ol = note: this error originates in the macro `construct_runtime` (in Nightly builds, run with -Z macro-backtrace for more info) error: cannot find macro `decl_storage` in this scope - --> $DIR/old_unsupported_pallet_decl.rs:6:2 + --> tests/construct_runtime_ui/old_unsupported_pallet_decl.rs:6:2 | 6 | decl_storage! { | ^^^^^^^^^^^^ @@ -22,7 +22,7 @@ error: cannot find macro `decl_storage` in this scope frame_support::decl_storage error: cannot find macro `decl_module` in this scope - --> $DIR/old_unsupported_pallet_decl.rs:10:2 + --> tests/construct_runtime_ui/old_unsupported_pallet_decl.rs:10:2 | 10 | decl_module! { | ^^^^^^^^^^^ diff --git a/frame/support/test/tests/construct_runtime_ui/pallet_error_too_large.rs b/frame/support/test/tests/construct_runtime_ui/pallet_error_too_large.rs index b7ccadb5e..866c3f0de 100644 --- a/frame/support/test/tests/construct_runtime_ui/pallet_error_too_large.rs +++ b/frame/support/test/tests/construct_runtime_ui/pallet_error_too_large.rs @@ -72,7 +72,7 @@ impl frame_system::Config for Runtime { } construct_runtime! { - pub enum Runtime where + pub struct Runtime where Block = Block, NodeBlock = Block, UncheckedExtrinsic = UncheckedExtrinsic diff --git a/frame/support/test/tests/construct_runtime_ui/pallet_error_too_large.stderr b/frame/support/test/tests/construct_runtime_ui/pallet_error_too_large.stderr index 99a543eef..b9cec02a2 100644 --- a/frame/support/test/tests/construct_runtime_ui/pallet_error_too_large.stderr +++ b/frame/support/test/tests/construct_runtime_ui/pallet_error_too_large.stderr @@ -2,7 +2,7 @@ error[E0080]: evaluation of constant value failed --> tests/construct_runtime_ui/pallet_error_too_large.rs:74:1 | 74 | / construct_runtime! { -75 | | pub enum Runtime where +75 | | pub struct Runtime where 76 | | Block = Block, 77 | | NodeBlock = Block, ... | diff --git a/frame/support/test/tests/construct_runtime_ui/undefined_call_part.rs b/frame/support/test/tests/construct_runtime_ui/undefined_call_part.rs index abe5c4cfe..0010f5277 100644 --- a/frame/support/test/tests/construct_runtime_ui/undefined_call_part.rs +++ b/frame/support/test/tests/construct_runtime_ui/undefined_call_part.rs @@ -47,7 +47,7 @@ impl frame_system::Config for Runtime { } construct_runtime! { - pub enum Runtime where + pub struct Runtime where Block = Block, NodeBlock = Block, UncheckedExtrinsic = UncheckedExtrinsic diff --git a/frame/support/test/tests/construct_runtime_ui/undefined_call_part.stderr b/frame/support/test/tests/construct_runtime_ui/undefined_call_part.stderr index 6baf01e86..c2092edea 100644 --- a/frame/support/test/tests/construct_runtime_ui/undefined_call_part.stderr +++ b/frame/support/test/tests/construct_runtime_ui/undefined_call_part.stderr @@ -5,7 +5,7 @@ error: `Pallet` does not have #[pallet::call] defined, perhaps you should remove | ^^^^^^^^^^^^^^^^^^^^^^^^ ... 49 | / construct_runtime! { -50 | | pub enum Runtime where +50 | | pub struct Runtime where 51 | | Block = Block, 52 | | NodeBlock = Block, ... | diff --git a/frame/support/test/tests/construct_runtime_ui/undefined_event_part.rs b/frame/support/test/tests/construct_runtime_ui/undefined_event_part.rs index 9a6ac5c62..35212df8f 100644 --- a/frame/support/test/tests/construct_runtime_ui/undefined_event_part.rs +++ b/frame/support/test/tests/construct_runtime_ui/undefined_event_part.rs @@ -47,7 +47,7 @@ impl frame_system::Config for Runtime { } construct_runtime! { - pub enum Runtime where + pub struct Runtime where Block = Block, NodeBlock = Block, UncheckedExtrinsic = UncheckedExtrinsic diff --git a/frame/support/test/tests/construct_runtime_ui/undefined_event_part.stderr b/frame/support/test/tests/construct_runtime_ui/undefined_event_part.stderr index ff8ecf304..1ca64f381 100644 --- a/frame/support/test/tests/construct_runtime_ui/undefined_event_part.stderr +++ b/frame/support/test/tests/construct_runtime_ui/undefined_event_part.stderr @@ -5,7 +5,7 @@ error: `Pallet` does not have #[pallet::event] defined, perhaps you should remov | ^^^^^^^^^^^^^^^^^^^^^^^^ ... 49 | / construct_runtime! { -50 | | pub enum Runtime where +50 | | pub struct Runtime where 51 | | Block = Block, 52 | | NodeBlock = Block, ... | @@ -19,7 +19,7 @@ error[E0412]: cannot find type `Event` in module `pallet` --> tests/construct_runtime_ui/undefined_event_part.rs:49:1 | 49 | / construct_runtime! { -50 | | pub enum Runtime where +50 | | pub struct Runtime where 51 | | Block = Block, 52 | | NodeBlock = Block, ... | diff --git a/frame/support/test/tests/construct_runtime_ui/undefined_genesis_config_part.rs b/frame/support/test/tests/construct_runtime_ui/undefined_genesis_config_part.rs index 4facf85e2..ec753e9a0 100644 --- a/frame/support/test/tests/construct_runtime_ui/undefined_genesis_config_part.rs +++ b/frame/support/test/tests/construct_runtime_ui/undefined_genesis_config_part.rs @@ -47,7 +47,7 @@ impl frame_system::Config for Runtime { } construct_runtime! { - pub enum Runtime where + pub struct Runtime where Block = Block, NodeBlock = Block, UncheckedExtrinsic = UncheckedExtrinsic diff --git a/frame/support/test/tests/construct_runtime_ui/undefined_genesis_config_part.stderr b/frame/support/test/tests/construct_runtime_ui/undefined_genesis_config_part.stderr index 046369e11..72099c1b9 100644 --- a/frame/support/test/tests/construct_runtime_ui/undefined_genesis_config_part.stderr +++ b/frame/support/test/tests/construct_runtime_ui/undefined_genesis_config_part.stderr @@ -5,7 +5,7 @@ error: `Pallet` does not have #[pallet::genesis_config] defined, perhaps you sho | ^^^^^^^^^^^^^^^^^^^^^^^^ ... 49 | / construct_runtime! { -50 | | pub enum Runtime where +50 | | pub struct Runtime where 51 | | Block = Block, 52 | | NodeBlock = Block, ... | @@ -19,7 +19,7 @@ error[E0412]: cannot find type `GenesisConfig` in module `pallet` --> tests/construct_runtime_ui/undefined_genesis_config_part.rs:49:1 | 49 | / construct_runtime! { -50 | | pub enum Runtime where +50 | | pub struct Runtime where 51 | | Block = Block, 52 | | NodeBlock = Block, ... | @@ -37,7 +37,7 @@ error[E0283]: type annotations needed --> tests/construct_runtime_ui/undefined_genesis_config_part.rs:49:1 | 49 | / construct_runtime! { -50 | | pub enum Runtime where +50 | | pub struct Runtime where 51 | | Block = Block, 52 | | NodeBlock = Block, ... | diff --git a/frame/support/test/tests/construct_runtime_ui/undefined_inherent_part.rs b/frame/support/test/tests/construct_runtime_ui/undefined_inherent_part.rs index 322fa2c29..22eaccca4 100644 --- a/frame/support/test/tests/construct_runtime_ui/undefined_inherent_part.rs +++ b/frame/support/test/tests/construct_runtime_ui/undefined_inherent_part.rs @@ -47,7 +47,7 @@ impl frame_system::Config for Runtime { } construct_runtime! { - pub enum Runtime where + pub struct Runtime where Block = Block, NodeBlock = Block, UncheckedExtrinsic = UncheckedExtrinsic diff --git a/frame/support/test/tests/construct_runtime_ui/undefined_inherent_part.stderr b/frame/support/test/tests/construct_runtime_ui/undefined_inherent_part.stderr index 74af0c264..dab651426 100644 --- a/frame/support/test/tests/construct_runtime_ui/undefined_inherent_part.stderr +++ b/frame/support/test/tests/construct_runtime_ui/undefined_inherent_part.stderr @@ -5,7 +5,7 @@ error: `Pallet` does not have #[pallet::inherent] defined, perhaps you should re | ^^^^^^^^^^^^^^^^^^^^^^^^ ... 49 | / construct_runtime! { -50 | | pub enum Runtime where +50 | | pub struct Runtime where 51 | | Block = Block, 52 | | NodeBlock = Block, ... | @@ -22,7 +22,7 @@ error[E0599]: no function or associated item named `create_inherent` found for s | -------------------- function or associated item `create_inherent` not found for this struct ... 49 | / construct_runtime! { -50 | | pub enum Runtime where +50 | | pub struct Runtime where 51 | | Block = Block, 52 | | NodeBlock = Block, ... | @@ -42,7 +42,7 @@ error[E0599]: no function or associated item named `is_inherent` found for struc | -------------------- function or associated item `is_inherent` not found for this struct ... 49 | / construct_runtime! { -50 | | pub enum Runtime where +50 | | pub struct Runtime where 51 | | Block = Block, 52 | | NodeBlock = Block, ... | @@ -62,7 +62,7 @@ error[E0599]: no function or associated item named `check_inherent` found for st | -------------------- function or associated item `check_inherent` not found for this struct ... 49 | / construct_runtime! { -50 | | pub enum Runtime where +50 | | pub struct Runtime where 51 | | Block = Block, 52 | | NodeBlock = Block, ... | @@ -82,7 +82,7 @@ error[E0599]: no associated item named `INHERENT_IDENTIFIER` found for struct `p | -------------------- associated item `INHERENT_IDENTIFIER` not found for this struct ... 49 | / construct_runtime! { -50 | | pub enum Runtime where +50 | | pub struct Runtime where 51 | | Block = Block, 52 | | NodeBlock = Block, ... | @@ -102,7 +102,7 @@ error[E0599]: no function or associated item named `is_inherent_required` found | -------------------- function or associated item `is_inherent_required` not found for this struct ... 49 | / construct_runtime! { -50 | | pub enum Runtime where +50 | | pub struct Runtime where 51 | | Block = Block, 52 | | NodeBlock = Block, ... | diff --git a/frame/support/test/tests/construct_runtime_ui/undefined_origin_part.rs b/frame/support/test/tests/construct_runtime_ui/undefined_origin_part.rs index 55cd5b545..1705fff49 100644 --- a/frame/support/test/tests/construct_runtime_ui/undefined_origin_part.rs +++ b/frame/support/test/tests/construct_runtime_ui/undefined_origin_part.rs @@ -47,7 +47,7 @@ impl frame_system::Config for Runtime { } construct_runtime! { - pub enum Runtime where + pub struct Runtime where Block = Block, NodeBlock = Block, UncheckedExtrinsic = UncheckedExtrinsic diff --git a/frame/support/test/tests/construct_runtime_ui/undefined_origin_part.stderr b/frame/support/test/tests/construct_runtime_ui/undefined_origin_part.stderr index 4907053b1..dd1bdf761 100644 --- a/frame/support/test/tests/construct_runtime_ui/undefined_origin_part.stderr +++ b/frame/support/test/tests/construct_runtime_ui/undefined_origin_part.stderr @@ -5,7 +5,7 @@ error: `Pallet` does not have #[pallet::origin] defined, perhaps you should remo | ^^^^^^^^^^^^^^^^^^^^^^^^ ... 49 | / construct_runtime! { -50 | | pub enum Runtime where +50 | | pub struct Runtime where 51 | | Block = Block, 52 | | NodeBlock = Block, ... | @@ -19,7 +19,7 @@ error[E0412]: cannot find type `Origin` in module `pallet` --> tests/construct_runtime_ui/undefined_origin_part.rs:49:1 | 49 | / construct_runtime! { -50 | | pub enum Runtime where +50 | | pub struct Runtime where 51 | | Block = Block, 52 | | NodeBlock = Block, ... | @@ -37,7 +37,7 @@ error[E0282]: type annotations needed --> tests/construct_runtime_ui/undefined_origin_part.rs:49:1 | 49 | / construct_runtime! { -50 | | pub enum Runtime where +50 | | pub struct Runtime where 51 | | Block = Block, 52 | | NodeBlock = Block, ... | diff --git a/frame/support/test/tests/construct_runtime_ui/undefined_validate_unsigned_part.rs b/frame/support/test/tests/construct_runtime_ui/undefined_validate_unsigned_part.rs index 0cf305a7d..8f64d3094 100644 --- a/frame/support/test/tests/construct_runtime_ui/undefined_validate_unsigned_part.rs +++ b/frame/support/test/tests/construct_runtime_ui/undefined_validate_unsigned_part.rs @@ -47,7 +47,7 @@ impl frame_system::Config for Runtime { } construct_runtime! { - pub enum Runtime where + pub struct Runtime where Block = Block, NodeBlock = Block, UncheckedExtrinsic = UncheckedExtrinsic diff --git a/frame/support/test/tests/construct_runtime_ui/undefined_validate_unsigned_part.stderr b/frame/support/test/tests/construct_runtime_ui/undefined_validate_unsigned_part.stderr index 6f0b13c58..14106ddd0 100644 --- a/frame/support/test/tests/construct_runtime_ui/undefined_validate_unsigned_part.stderr +++ b/frame/support/test/tests/construct_runtime_ui/undefined_validate_unsigned_part.stderr @@ -5,7 +5,7 @@ error: `Pallet` does not have #[pallet::validate_unsigned] defined, perhaps you | ^^^^^^^^^^^^^^^^^^^^^^^^ ... 49 | / construct_runtime! { -50 | | pub enum Runtime where +50 | | pub struct Runtime where 51 | | Block = Block, 52 | | NodeBlock = Block, ... | @@ -19,7 +19,7 @@ error[E0599]: no variant or associated item named `Pallet` found for enum `Runti --> tests/construct_runtime_ui/undefined_validate_unsigned_part.rs:56:3 | 49 | / construct_runtime! { -50 | | pub enum Runtime where +50 | | pub struct Runtime where 51 | | Block = Block, 52 | | NodeBlock = Block, ... | @@ -36,7 +36,7 @@ error[E0599]: no function or associated item named `pre_dispatch` found for stru | -------------------- function or associated item `pre_dispatch` not found for this struct ... 49 | / construct_runtime! { -50 | | pub enum Runtime where +50 | | pub struct Runtime where 51 | | Block = Block, 52 | | NodeBlock = Block, ... | @@ -57,7 +57,7 @@ error[E0599]: no function or associated item named `validate_unsigned` found for | -------------------- function or associated item `validate_unsigned` not found for this struct ... 49 | / construct_runtime! { -50 | | pub enum Runtime where +50 | | pub struct Runtime where 51 | | Block = Block, 52 | | NodeBlock = Block, ... | diff --git a/frame/support/test/tests/construct_runtime_ui/unsupported_meta_structure.rs b/frame/support/test/tests/construct_runtime_ui/unsupported_meta_structure.rs index b93adf9a7..e5fd284dc 100644 --- a/frame/support/test/tests/construct_runtime_ui/unsupported_meta_structure.rs +++ b/frame/support/test/tests/construct_runtime_ui/unsupported_meta_structure.rs @@ -1,7 +1,7 @@ use frame_support::construct_runtime; construct_runtime! { - pub enum Runtime where + pub struct Runtime where Block = Block, NodeBlock = Block, UncheckedExtrinsic = UncheckedExtrinsic diff --git a/frame/support/test/tests/construct_runtime_ui/unsupported_pallet_attr.rs b/frame/support/test/tests/construct_runtime_ui/unsupported_pallet_attr.rs index 3ec8b9db1..03363d30a 100644 --- a/frame/support/test/tests/construct_runtime_ui/unsupported_pallet_attr.rs +++ b/frame/support/test/tests/construct_runtime_ui/unsupported_pallet_attr.rs @@ -1,7 +1,7 @@ use frame_support::construct_runtime; construct_runtime! { - pub enum Runtime where + pub struct Runtime where Block = Block, NodeBlock = Block, UncheckedExtrinsic = UncheckedExtrinsic diff --git a/frame/support/test/tests/construct_runtime_ui/use_undefined_part.rs b/frame/support/test/tests/construct_runtime_ui/use_undefined_part.rs index c74e29bc0..971e2b831 100644 --- a/frame/support/test/tests/construct_runtime_ui/use_undefined_part.rs +++ b/frame/support/test/tests/construct_runtime_ui/use_undefined_part.rs @@ -25,7 +25,7 @@ pub type UncheckedExtrinsic = generic::UncheckedExtrinsic; pub type UncheckedExtrinsic = sp_runtime::generic::UncheckedExtrinsic; frame_support::construct_runtime!( - pub enum Runtime where + pub struct Runtime where Block = Block, NodeBlock = Block, UncheckedExtrinsic = UncheckedExtrinsic diff --git a/frame/support/test/tests/pallet_compatibility.rs b/frame/support/test/tests/pallet_compatibility.rs index bd11d0da3..077b3c96e 100644 --- a/frame/support/test/tests/pallet_compatibility.rs +++ b/frame/support/test/tests/pallet_compatibility.rs @@ -267,7 +267,7 @@ pub type Block = sp_runtime::generic::Block; pub type UncheckedExtrinsic = sp_runtime::generic::UncheckedExtrinsic; frame_support::construct_runtime!( - pub enum Runtime where + pub struct Runtime where Block = Block, NodeBlock = Block, UncheckedExtrinsic = UncheckedExtrinsic diff --git a/frame/support/test/tests/pallet_compatibility_instance.rs b/frame/support/test/tests/pallet_compatibility_instance.rs index 1d2705b12..c641556be 100644 --- a/frame/support/test/tests/pallet_compatibility_instance.rs +++ b/frame/support/test/tests/pallet_compatibility_instance.rs @@ -268,7 +268,7 @@ pub type Block = sp_runtime::generic::Block; pub type UncheckedExtrinsic = sp_runtime::generic::UncheckedExtrinsic; frame_support::construct_runtime!( - pub enum Runtime where + pub struct Runtime where Block = Block, NodeBlock = Block, UncheckedExtrinsic = UncheckedExtrinsic diff --git a/frame/support/test/tests/pallet_instance.rs b/frame/support/test/tests/pallet_instance.rs index 6dc73a6d4..5beb1480a 100644 --- a/frame/support/test/tests/pallet_instance.rs +++ b/frame/support/test/tests/pallet_instance.rs @@ -334,7 +334,7 @@ pub type Block = sp_runtime::generic::Block; pub type UncheckedExtrinsic = sp_runtime::generic::UncheckedExtrinsic; frame_support::construct_runtime!( - pub enum Runtime where + pub struct Runtime where Block = Block, NodeBlock = Block, UncheckedExtrinsic = UncheckedExtrinsic diff --git a/frame/support/test/tests/pallet_with_name_trait_is_valid.rs b/frame/support/test/tests/pallet_with_name_trait_is_valid.rs index 43330dbe2..8cd3c79cc 100644 --- a/frame/support/test/tests/pallet_with_name_trait_is_valid.rs +++ b/frame/support/test/tests/pallet_with_name_trait_is_valid.rs @@ -114,7 +114,7 @@ mod tests { >; frame_support::construct_runtime!( - pub enum Runtime where + pub struct Runtime where Block = TestBlock, NodeBlock = TestBlock, UncheckedExtrinsic = TestUncheckedExtrinsic diff --git a/frame/support/test/tests/storage_layers.rs b/frame/support/test/tests/storage_layers.rs index c5bbbae02..f124aed9a 100644 --- a/frame/support/test/tests/storage_layers.rs +++ b/frame/support/test/tests/storage_layers.rs @@ -114,7 +114,7 @@ impl pallet::Config for Runtime {} impl decl_pallet::Config for Runtime {} frame_support::construct_runtime!( - pub enum Runtime where + pub struct Runtime where Block = Block, NodeBlock = Block, UncheckedExtrinsic = UncheckedExtrinsic diff --git a/frame/system/benches/bench.rs b/frame/system/benches/bench.rs index 51652727c..d2b579814 100644 --- a/frame/system/benches/bench.rs +++ b/frame/system/benches/bench.rs @@ -48,7 +48,7 @@ type UncheckedExtrinsic = frame_system::mocking::MockUncheckedExtrinsic type Block = frame_system::mocking::MockBlock; frame_support::construct_runtime!( - pub enum Runtime where + pub struct Runtime where Block = Block, NodeBlock = Block, UncheckedExtrinsic = UncheckedExtrinsic, diff --git a/frame/transaction-payment/asset-tx-payment/src/mock.rs b/frame/transaction-payment/asset-tx-payment/src/mock.rs index bc994b4dc..cd5147c3a 100644 --- a/frame/transaction-payment/asset-tx-payment/src/mock.rs +++ b/frame/transaction-payment/asset-tx-payment/src/mock.rs @@ -40,7 +40,8 @@ type Balance = u64; type AccountId = u64; frame_support::construct_runtime!( - pub enum Runtime where + pub struct Runtime + where Block = Block, NodeBlock = Block, UncheckedExtrinsic = UncheckedExtrinsic, diff --git a/frame/transaction-payment/src/mock.rs b/frame/transaction-payment/src/mock.rs index 92799bf5b..bfb9a194f 100644 --- a/frame/transaction-payment/src/mock.rs +++ b/frame/transaction-payment/src/mock.rs @@ -37,7 +37,7 @@ type UncheckedExtrinsic = frame_system::mocking::MockUncheckedExtrinsic type Block = frame_system::mocking::MockBlock; frame_support::construct_runtime!( - pub enum Runtime where + pub struct Runtime where Block = Block, NodeBlock = Block, UncheckedExtrinsic = UncheckedExtrinsic, From 8a4746e37c39dfa62552af0a47886dcc4ef1575f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Silva?= <123550+andresilva@users.noreply.github.com> Date: Tue, 7 Mar 2023 11:19:19 +0000 Subject: [PATCH 207/558] consensus: remove caching functionality from block import pipeline (#13551) * consensus: remove caching functionality from block import pipeline * client: update docs on Verifier::verify * node: fix block production benchmark --- bin/node/cli/benches/block_production.rs | 2 +- bin/node/cli/src/service.rs | 2 +- bin/node/testing/src/bench.rs | 6 ++-- client/api/src/backend.rs | 14 +++----- client/api/src/in_mem.rs | 4 +-- client/consensus/aura/src/import_queue.rs | 8 ++--- client/consensus/babe/src/lib.rs | 25 ++++++--------- client/consensus/babe/src/tests.rs | 7 ++-- client/consensus/beefy/src/import.rs | 9 +++--- client/consensus/beefy/src/tests.rs | 15 ++++----- client/consensus/common/src/block_import.rs | 13 ++------ client/consensus/common/src/import_queue.rs | 18 +++++------ .../common/src/import_queue/basic_queue.rs | 8 ++--- client/consensus/grandpa/src/import.rs | 14 ++++---- client/consensus/grandpa/src/tests.rs | 21 ++++-------- .../manual-seal/src/consensus/babe.rs | 5 ++- client/consensus/manual-seal/src/lib.rs | 6 ++-- .../consensus/manual-seal/src/seal_block.rs | 4 +-- client/consensus/pow/src/lib.rs | 11 +++---- client/consensus/pow/src/worker.rs | 3 +- client/consensus/slots/src/lib.rs | 2 +- client/db/src/lib.rs | 8 ++--- client/network/src/service/tests/mod.rs | 27 ++-------------- client/network/test/src/lib.rs | 32 +++++-------------- client/service/src/client/client.rs | 14 +++----- client/service/test/src/client/mod.rs | 4 +-- primitives/blockchain/src/backend.rs | 15 --------- primitives/consensus/common/src/lib.rs | 3 -- test-utils/client/src/client_ext.rs | 17 +++++----- 29 files changed, 103 insertions(+), 214 deletions(-) diff --git a/bin/node/cli/benches/block_production.rs b/bin/node/cli/benches/block_production.rs index e4b48497e..501d69fc2 100644 --- a/bin/node/cli/benches/block_production.rs +++ b/bin/node/cli/benches/block_production.rs @@ -137,7 +137,7 @@ fn import_block( params.state_action = StateAction::ApplyChanges(sc_consensus::StorageChanges::Changes(built.storage_changes)); params.fork_choice = Some(ForkChoiceStrategy::LongestChain); - futures::executor::block_on(client.import_block(params, Default::default())) + futures::executor::block_on(client.import_block(params)) .expect("importing a block doesn't fail"); } diff --git a/bin/node/cli/src/service.rs b/bin/node/cli/src/service.rs index 6e000a475..e9a34b2a5 100644 --- a/bin/node/cli/src/service.rs +++ b/bin/node/cli/src/service.rs @@ -760,7 +760,7 @@ mod tests { ); params.fork_choice = Some(ForkChoiceStrategy::LongestChain); - futures::executor::block_on(block_import.import_block(params, Default::default())) + futures::executor::block_on(block_import.import_block(params)) .expect("error importing test block"); }, |service, _| { diff --git a/bin/node/testing/src/bench.rs b/bin/node/testing/src/bench.rs index ccce6dc23..1a9af1302 100644 --- a/bin/node/testing/src/bench.rs +++ b/bin/node/testing/src/bench.rs @@ -682,10 +682,8 @@ impl BenchContext { assert_eq!(self.client.chain_info().best_number, 0); assert_eq!( - futures::executor::block_on( - self.client.import_block(import_params, Default::default()) - ) - .expect("Failed to import block"), + futures::executor::block_on(self.client.import_block(import_params)) + .expect("Failed to import block"), ImportResult::Imported(ImportedAux { header_only: false, clear_justification_requests: false, diff --git a/client/api/src/backend.rs b/client/api/src/backend.rs index 26a0cf035..b88feafb6 100644 --- a/client/api/src/backend.rs +++ b/client/api/src/backend.rs @@ -18,12 +18,10 @@ //! Substrate Client data backend -use crate::{ - blockchain::{well_known_cache_keys, Backend as BlockchainBackend}, - UsageInfo, -}; +use std::collections::HashSet; + use parking_lot::RwLock; -use sp_blockchain; + use sp_consensus::BlockOrigin; use sp_core::offchain::OffchainStorage; use sp_runtime::{ @@ -35,7 +33,8 @@ use sp_state_machine::{ OffchainChangesCollection, StorageCollection, StorageIterator, }; use sp_storage::{ChildInfo, StorageData, StorageKey}; -use std::collections::{HashMap, HashSet}; + +use crate::{blockchain::Backend as BlockchainBackend, UsageInfo}; pub use sp_state_machine::{Backend as StateBackend, KeyValueStates}; @@ -179,9 +178,6 @@ pub trait BlockImportOperation { state: NewBlockState, ) -> sp_blockchain::Result<()>; - /// Update cached data. - fn update_cache(&mut self, cache: HashMap>); - /// Inject storage data into the database. fn update_db_storage( &mut self, diff --git a/client/api/src/in_mem.rs b/client/api/src/in_mem.rs index 7096910e1..27a74ddd7 100644 --- a/client/api/src/in_mem.rs +++ b/client/api/src/in_mem.rs @@ -40,7 +40,7 @@ use std::{ use crate::{ backend::{self, NewBlockState}, - blockchain::{self, well_known_cache_keys::Id as CacheKeyId, BlockStatus, HeaderBackend}, + blockchain::{self, BlockStatus, HeaderBackend}, leaves::LeafSet, UsageInfo, }; @@ -549,8 +549,6 @@ where Ok(()) } - fn update_cache(&mut self, _cache: HashMap>) {} - fn update_db_storage( &mut self, update: > as StateBackend>>::Transaction, diff --git a/client/consensus/aura/src/import_queue.rs b/client/consensus/aura/src/import_queue.rs index 99b5af081..46e0ccb4e 100644 --- a/client/consensus/aura/src/import_queue.rs +++ b/client/consensus/aura/src/import_queue.rs @@ -34,7 +34,7 @@ use sc_consensus_slots::{check_equivocation, CheckedHeader, InherentDataProvider use sc_telemetry::{telemetry, TelemetryHandle, CONSENSUS_DEBUG, CONSENSUS_TRACE}; use sp_api::{ApiExt, ProvideRuntimeApi}; use sp_block_builder::BlockBuilder as BlockBuilderApi; -use sp_blockchain::{well_known_cache_keys::Id as CacheKeyId, HeaderBackend}; +use sp_blockchain::HeaderBackend; use sp_consensus::Error as ConsensusError; use sp_consensus_aura::{digests::CompatibleDigestItem, inherents::AuraInherentData, AuraApi}; use sp_consensus_slots::Slot; @@ -184,7 +184,7 @@ where async fn verify( &mut self, mut block: BlockImportParams, - ) -> Result<(BlockImportParams, Option)>>), String> { + ) -> Result, String> { // Skip checks that include execution, if being told so or when importing only state. // // This is done for example when gap syncing and it is expected that the block after the gap @@ -194,7 +194,7 @@ where // When we are importing only the state of a block, it will be the best block. block.fork_choice = Some(ForkChoiceStrategy::Custom(block.with_state())); - return Ok((block, Default::default())) + return Ok(block) } let hash = block.header.hash(); @@ -278,7 +278,7 @@ where block.fork_choice = Some(ForkChoiceStrategy::LongestChain); block.post_hash = Some(hash); - Ok((block, None)) + Ok(block) }, CheckedHeader::Deferred(a, b) => { debug!(target: LOG_TARGET, "Checking {:?} failed; {:?}, {:?}.", hash, a, b); diff --git a/client/consensus/babe/src/lib.rs b/client/consensus/babe/src/lib.rs index 271e3f648..1d42057a3 100644 --- a/client/consensus/babe/src/lib.rs +++ b/client/consensus/babe/src/lib.rs @@ -67,7 +67,7 @@ #![warn(missing_docs)] use std::{ - collections::{HashMap, HashSet}, + collections::HashSet, future::Future, pin::Pin, sync::Arc, @@ -114,9 +114,7 @@ use sp_blockchain::{ Backend as _, BlockStatus, Error as ClientError, ForkBackend, HeaderBackend, HeaderMetadata, Result as ClientResult, }; -use sp_consensus::{ - BlockOrigin, CacheKeyId, Environment, Error as ConsensusError, Proposer, SelectChain, -}; +use sp_consensus::{BlockOrigin, Environment, Error as ConsensusError, Proposer, SelectChain}; use sp_consensus_babe::inherents::BabeInherentData; use sp_consensus_slots::Slot; use sp_core::{crypto::ByteArray, ExecutionContext}; @@ -1131,9 +1129,6 @@ where } } -type BlockVerificationResult = - Result<(BlockImportParams, Option)>>), String>; - #[async_trait::async_trait] impl Verifier for BabeVerifier @@ -1153,7 +1148,7 @@ where async fn verify( &mut self, mut block: BlockImportParams, - ) -> BlockVerificationResult { + ) -> Result, String> { trace!( target: LOG_TARGET, "Verifying origin: {:?} header: {:?} justification(s): {:?} body: {:?}", @@ -1177,7 +1172,7 @@ where // read it from the state after import. We also skip all verifications // because there's no parent state and we trust the sync module to verify // that the state is correct and finalized. - return Ok((block, Default::default())) + return Ok(block) } debug!( @@ -1296,7 +1291,7 @@ where ); block.post_hash = Some(hash); - Ok((block, Default::default())) + Ok(block) }, CheckedHeader::Deferred(a, b) => { debug!(target: LOG_TARGET, "Checking {:?} failed; {:?}, {:?}.", hash, a, b); @@ -1368,7 +1363,6 @@ where async fn import_state( &mut self, mut block: BlockImportParams>, - new_cache: HashMap>, ) -> Result { let hash = block.post_hash(); let parent_hash = *block.header.parent_hash(); @@ -1383,7 +1377,7 @@ where }); // First make the client import the state. - let import_result = self.inner.import_block(block, new_cache).await; + let import_result = self.inner.import_block(block).await; let aux = match import_result { Ok(ImportResult::Imported(aux)) => aux, Ok(r) => @@ -1433,7 +1427,6 @@ where async fn import_block( &mut self, mut block: BlockImportParams, - new_cache: HashMap>, ) -> Result { let hash = block.post_hash(); let number = *block.header.number(); @@ -1454,11 +1447,11 @@ where // In case of initial sync intermediates should not be present... let _ = block.remove_intermediate::>(INTERMEDIATE_KEY); block.fork_choice = Some(ForkChoiceStrategy::Custom(false)); - return self.inner.import_block(block, new_cache).await.map_err(Into::into) + return self.inner.import_block(block).await.map_err(Into::into) } if block.with_state() { - return self.import_state(block, new_cache).await + return self.import_state(block).await } let pre_digest = find_pre_digest::(&block.header).expect( @@ -1694,7 +1687,7 @@ where epoch_changes.release_mutex() }; - let import_result = self.inner.import_block(block, new_cache).await; + let import_result = self.inner.import_block(block).await; // revert to the original epoch changes in case there's an error // importing the block diff --git a/client/consensus/babe/src/tests.rs b/client/consensus/babe/src/tests.rs index fcdada94c..25f1f8723 100644 --- a/client/consensus/babe/src/tests.rs +++ b/client/consensus/babe/src/tests.rs @@ -209,9 +209,8 @@ where async fn import_block( &mut self, block: BlockImportParams, - new_cache: HashMap>, ) -> Result { - Ok(self.0.import_block(block, new_cache).await.expect("importing block failed")) + Ok(self.0.import_block(block).await.expect("importing block failed")) } async fn check_block( @@ -258,7 +257,7 @@ impl Verifier for TestVerifier { async fn verify( &mut self, mut block: BlockImportParams, - ) -> Result<(BlockImportParams, Option)>>), String> { + ) -> Result, String> { // apply post-sealing mutations (i.e. stripping seal, if desired). (self.mutator)(&mut block.header, Stage::PostSeal); self.inner.verify(block).await @@ -743,7 +742,7 @@ async fn propose_and_import_block( import .insert_intermediate(INTERMEDIATE_KEY, BabeIntermediate:: { epoch_descriptor }); import.fork_choice = Some(ForkChoiceStrategy::LongestChain); - let import_result = block_import.import_block(import, Default::default()).await.unwrap(); + let import_result = block_import.import_block(import).await.unwrap(); match import_result { ImportResult::Imported(_) => {}, diff --git a/client/consensus/beefy/src/import.rs b/client/consensus/beefy/src/import.rs index 177a99b10..dd2ed92ef 100644 --- a/client/consensus/beefy/src/import.rs +++ b/client/consensus/beefy/src/import.rs @@ -16,13 +16,13 @@ // You should have received a copy of the GNU General Public License // along with this program. If not, see . +use std::sync::Arc; + use log::debug; -use sp_consensus_beefy::{BeefyApi, BEEFY_ENGINE_ID}; -use std::{collections::HashMap, sync::Arc}; use sp_api::{ProvideRuntimeApi, TransactionFor}; -use sp_blockchain::well_known_cache_keys; use sp_consensus::Error as ConsensusError; +use sp_consensus_beefy::{BeefyApi, BEEFY_ENGINE_ID}; use sp_runtime::{ traits::{Block as BlockT, Header as HeaderT, NumberFor}, EncodedJustification, @@ -132,7 +132,6 @@ where async fn import_block( &mut self, mut block: BlockImportParams, - new_cache: HashMap>, ) -> Result { let hash = block.post_hash(); let number = *block.header.number(); @@ -146,7 +145,7 @@ where }); // Run inner block import. - let inner_import_result = self.inner.import_block(block, new_cache).await?; + let inner_import_result = self.inner.import_block(block).await?; match (beefy_encoded, &inner_import_result) { (Some(encoded), ImportResult::Imported(_)) => { diff --git a/client/consensus/beefy/src/tests.rs b/client/consensus/beefy/src/tests.rs index fb1c45b90..44920aaa6 100644 --- a/client/consensus/beefy/src/tests.rs +++ b/client/consensus/beefy/src/tests.rs @@ -61,7 +61,7 @@ use sp_runtime::{ traits::{Header as HeaderT, NumberFor}, BuildStorage, DigestItem, EncodedJustification, Justifications, Storage, }; -use std::{collections::HashMap, marker::PhantomData, sync::Arc, task::Poll}; +use std::{marker::PhantomData, sync::Arc, task::Poll}; use substrate_test_runtime_client::{runtime::Header, ClientExt}; use tokio::time::Duration; @@ -766,14 +766,11 @@ async fn beefy_importing_justifications() { // Import block 1 without justifications. assert_eq!( - block_import - .import_block(params(block.clone(), None), HashMap::new()) - .await - .unwrap(), + block_import.import_block(params(block.clone(), None)).await.unwrap(), ImportResult::Imported(ImportedAux { is_new_best: true, ..Default::default() }), ); assert_eq!( - block_import.import_block(params(block, None), HashMap::new()).await.unwrap(), + block_import.import_block(params(block, None)).await.unwrap(), ImportResult::AlreadyInChain, ); @@ -788,7 +785,7 @@ async fn beefy_importing_justifications() { let encoded = versioned_proof.encode(); let justif = Some(Justifications::from((BEEFY_ENGINE_ID, encoded))); assert_eq!( - block_import.import_block(params(block, justif), HashMap::new()).await.unwrap(), + block_import.import_block(params(block, justif)).await.unwrap(), ImportResult::Imported(ImportedAux { bad_justification: false, is_new_best: true, @@ -820,7 +817,7 @@ async fn beefy_importing_justifications() { let justif = Some(Justifications::from((BEEFY_ENGINE_ID, encoded))); let mut justif_recv = justif_stream.subscribe(100_000); assert_eq!( - block_import.import_block(params(block, justif), HashMap::new()).await.unwrap(), + block_import.import_block(params(block, justif)).await.unwrap(), ImportResult::Imported(ImportedAux { bad_justification: false, is_new_best: true, @@ -856,7 +853,7 @@ async fn beefy_importing_justifications() { let justif = Some(Justifications::from((BEEFY_ENGINE_ID, encoded))); let mut justif_recv = justif_stream.subscribe(100_000); assert_eq!( - block_import.import_block(params(block, justif), HashMap::new()).await.unwrap(), + block_import.import_block(params(block, justif)).await.unwrap(), ImportResult::Imported(ImportedAux { // Still `false` because we don't want to fail import on bad BEEFY justifications. bad_justification: false, diff --git a/client/consensus/common/src/block_import.rs b/client/consensus/common/src/block_import.rs index 98accc1d2..70bf0283a 100644 --- a/client/consensus/common/src/block_import.rs +++ b/client/consensus/common/src/block_import.rs @@ -25,7 +25,7 @@ use sp_runtime::{ }; use std::{any::Any, borrow::Cow, collections::HashMap, sync::Arc}; -use sp_consensus::{BlockOrigin, CacheKeyId, Error}; +use sp_consensus::{BlockOrigin, Error}; /// Block import result. #[derive(Debug, PartialEq, Eq)] @@ -348,12 +348,9 @@ pub trait BlockImport { ) -> Result; /// Import a block. - /// - /// Cached data can be accessed through the blockchain cache. async fn import_block( &mut self, block: BlockImportParams, - cache: HashMap>, ) -> Result; } @@ -374,14 +371,11 @@ where } /// Import a block. - /// - /// Cached data can be accessed through the blockchain cache. async fn import_block( &mut self, block: BlockImportParams, - cache: HashMap>, ) -> Result { - (**self).import_block(block, cache).await + (**self).import_block(block).await } } @@ -405,9 +399,8 @@ where async fn import_block( &mut self, block: BlockImportParams, - cache: HashMap>, ) -> Result { - (&**self).import_block(block, cache).await + (&**self).import_block(block).await } } diff --git a/client/consensus/common/src/import_queue.rs b/client/consensus/common/src/import_queue.rs index 66ff5e6d5..cec9aca47 100644 --- a/client/consensus/common/src/import_queue.rs +++ b/client/consensus/common/src/import_queue.rs @@ -27,9 +27,9 @@ //! instantiated. The `BasicQueue` and `BasicVerifier` traits allow serial //! queues to be instantiated simply. -use std::{collections::HashMap, iter::FromIterator}; - use log::{debug, trace}; + +use sp_consensus::{error::Error as ConsensusError, BlockOrigin}; use sp_runtime::{ traits::{Block as BlockT, Header as _, NumberFor}, Justifications, @@ -42,8 +42,8 @@ use crate::{ }, metrics::Metrics, }; + pub use basic_queue::BasicQueue; -use sp_consensus::{error::Error as ConsensusError, BlockOrigin, CacheKeyId}; const LOG_TARGET: &str = "sync::import-queue"; @@ -96,13 +96,12 @@ pub struct IncomingBlock { /// Verify a justification of a block #[async_trait::async_trait] pub trait Verifier: Send + Sync { - /// Verify the given data and return the BlockImportParams and an optional - /// new set of validators to import. If not, err with an Error-Message - /// presented to the User in the logs. + /// Verify the given block data and return the `BlockImportParams` to + /// continue the block import process. async fn verify( &mut self, block: BlockImportParams, - ) -> Result<(BlockImportParams, Option)>>), String>; + ) -> Result, String>; } /// Blocks import queue API. @@ -328,7 +327,7 @@ pub(crate) async fn import_single_block_metered< import_block.state_action = StateAction::ExecuteIfPossible; } - let (import_block, maybe_keys) = verifier.verify(import_block).await.map_err(|msg| { + let import_block = verifier.verify(import_block).await.map_err(|msg| { if let Some(ref peer) = peer { trace!( target: LOG_TARGET, @@ -351,9 +350,8 @@ pub(crate) async fn import_single_block_metered< metrics.report_verification(true, started.elapsed()); } - let cache = HashMap::from_iter(maybe_keys.unwrap_or_default()); let import_block = import_block.clear_storage_changes_and_mutate(); - let imported = import_handle.import_block(import_block, cache).await; + let imported = import_handle.import_block(import_block).await; if let Some(metrics) = metrics.as_ref() { metrics.report_verification_and_import(started.elapsed()); } diff --git a/client/consensus/common/src/import_queue/basic_queue.rs b/client/consensus/common/src/import_queue/basic_queue.rs index 640470815..653c88321 100644 --- a/client/consensus/common/src/import_queue/basic_queue.rs +++ b/client/consensus/common/src/import_queue/basic_queue.rs @@ -504,19 +504,18 @@ mod tests { block_import::{ BlockCheckParams, BlockImport, BlockImportParams, ImportResult, JustificationImport, }, - import_queue::{CacheKeyId, Verifier}, + import_queue::Verifier, }; use futures::{executor::block_on, Future}; use sp_test_primitives::{Block, BlockNumber, Extrinsic, Hash, Header}; - use std::collections::HashMap; #[async_trait::async_trait] impl Verifier for () { async fn verify( &mut self, block: BlockImportParams, - ) -> Result<(BlockImportParams, Option)>>), String> { - Ok((BlockImportParams::new(block.origin, block.header), None)) + ) -> Result, String> { + Ok(BlockImportParams::new(block.origin, block.header)) } } @@ -535,7 +534,6 @@ mod tests { async fn import_block( &mut self, _block: BlockImportParams, - _cache: HashMap>, ) -> Result { Ok(ImportResult::imported(true)) } diff --git a/client/consensus/grandpa/src/import.rs b/client/consensus/grandpa/src/import.rs index c1f6cb566..cd13f832c 100644 --- a/client/consensus/grandpa/src/import.rs +++ b/client/consensus/grandpa/src/import.rs @@ -29,7 +29,7 @@ use sc_consensus::{ use sc_telemetry::TelemetryHandle; use sc_utils::mpsc::TracingUnboundedSender; use sp_api::{Core, RuntimeApiInfo, TransactionFor}; -use sp_blockchain::{well_known_cache_keys, BlockStatus}; +use sp_blockchain::BlockStatus; use sp_consensus::{BlockOrigin, Error as ConsensusError, SelectChain}; use sp_consensus_grandpa::{ConsensusLog, GrandpaApi, ScheduledChange, SetId, GRANDPA_ENGINE_ID}; use sp_core::hashing::twox_128; @@ -460,13 +460,12 @@ where async fn import_state( &mut self, mut block: BlockImportParams>, - new_cache: HashMap>, ) -> Result { let hash = block.post_hash(); let number = *block.header.number(); // Force imported state finality. block.finalized = true; - let import_result = (&*self.inner).import_block(block, new_cache).await; + let import_result = (&*self.inner).import_block(block).await; match import_result { Ok(ImportResult::Imported(aux)) => { // We've just imported a new state. We trust the sync module has verified @@ -526,7 +525,6 @@ where async fn import_block( &mut self, mut block: BlockImportParams, - new_cache: HashMap>, ) -> Result { let hash = block.post_hash(); let number = *block.header.number(); @@ -537,14 +535,14 @@ where Ok(BlockStatus::InChain) => { // Strip justifications when re-importing an existing block. let _justifications = block.justifications.take(); - return (&*self.inner).import_block(block, new_cache).await + return (&*self.inner).import_block(block).await }, Ok(BlockStatus::Unknown) => {}, Err(e) => return Err(ConsensusError::ClientImport(e.to_string())), } if block.with_state() { - return self.import_state(block, new_cache).await + return self.import_state(block).await } if number <= self.inner.info().finalized_number { @@ -570,7 +568,7 @@ where }, ); } - return (&*self.inner).import_block(block, new_cache).await + return (&*self.inner).import_block(block).await } // on initial sync we will restrict logging under info to avoid spam. @@ -580,7 +578,7 @@ where // we don't want to finalize on `inner.import_block` let mut justifications = block.justifications.take(); - let import_result = (&*self.inner).import_block(block, new_cache).await; + let import_result = (&*self.inner).import_block(block).await; let mut imported_aux = { match import_result { diff --git a/client/consensus/grandpa/src/tests.rs b/client/consensus/grandpa/src/tests.rs index f7747a203..f5e5c45e7 100644 --- a/client/consensus/grandpa/src/tests.rs +++ b/client/consensus/grandpa/src/tests.rs @@ -47,10 +47,7 @@ use sp_runtime::{ traits::{Block as BlockT, Header as HeaderT}, Justifications, }; -use std::{ - collections::{HashMap, HashSet}, - pin::Pin, -}; +use std::{collections::HashSet, pin::Pin}; use substrate_test_runtime_client::runtime::BlockNumber; use tokio::runtime::Handle; @@ -906,7 +903,7 @@ async fn allows_reimporting_change_blocks() { }; assert_eq!( - block_import.import_block(block(), HashMap::new()).await.unwrap(), + block_import.import_block(block()).await.unwrap(), ImportResult::Imported(ImportedAux { needs_justification: true, clear_justification_requests: false, @@ -916,10 +913,7 @@ async fn allows_reimporting_change_blocks() { }), ); - assert_eq!( - block_import.import_block(block(), HashMap::new()).await.unwrap(), - ImportResult::AlreadyInChain - ); + assert_eq!(block_import.import_block(block()).await.unwrap(), ImportResult::AlreadyInChain); } #[tokio::test] @@ -955,7 +949,7 @@ async fn test_bad_justification() { }; assert_eq!( - block_import.import_block(block(), HashMap::new()).await.unwrap(), + block_import.import_block(block()).await.unwrap(), ImportResult::Imported(ImportedAux { needs_justification: true, clear_justification_requests: false, @@ -965,10 +959,7 @@ async fn test_bad_justification() { }), ); - assert_eq!( - block_import.import_block(block(), HashMap::new()).await.unwrap(), - ImportResult::AlreadyInChain - ); + assert_eq!(block_import.import_block(block()).await.unwrap(), ImportResult::AlreadyInChain); } #[tokio::test] @@ -1938,7 +1929,7 @@ async fn imports_justification_for_regular_blocks_on_import() { import.fork_choice = Some(ForkChoiceStrategy::LongestChain); assert_eq!( - block_import.import_block(import, HashMap::new()).await.unwrap(), + block_import.import_block(import).await.unwrap(), ImportResult::Imported(ImportedAux { needs_justification: false, clear_justification_requests: false, diff --git a/client/consensus/manual-seal/src/consensus/babe.rs b/client/consensus/manual-seal/src/consensus/babe.rs index 476d2f0f4..3d21fd73d 100644 --- a/client/consensus/manual-seal/src/consensus/babe.rs +++ b/client/consensus/manual-seal/src/consensus/babe.rs @@ -35,7 +35,6 @@ use std::{marker::PhantomData, sync::Arc}; use sc_consensus::{BlockImportParams, ForkChoiceStrategy, Verifier}; use sp_api::{ProvideRuntimeApi, TransactionFor}; use sp_blockchain::{HeaderBackend, HeaderMetadata}; -use sp_consensus::CacheKeyId; use sp_consensus_babe::{ digests::{NextEpochDescriptor, PreDigest, SecondaryPlainPreDigest}, inherents::BabeInherentData, @@ -99,7 +98,7 @@ where async fn verify( &mut self, mut import_params: BlockImportParams, - ) -> Result<(BlockImportParams, Option)>>), String> { + ) -> Result, String> { import_params.finalized = false; import_params.fork_choice = Some(ForkChoiceStrategy::LongestChain); @@ -128,7 +127,7 @@ where import_params .insert_intermediate(INTERMEDIATE_KEY, BabeIntermediate:: { epoch_descriptor }); - Ok((import_params, None)) + Ok(import_params) } } diff --git a/client/consensus/manual-seal/src/lib.rs b/client/consensus/manual-seal/src/lib.rs index 41f1f4433..b277b3436 100644 --- a/client/consensus/manual-seal/src/lib.rs +++ b/client/consensus/manual-seal/src/lib.rs @@ -27,7 +27,7 @@ use sc_consensus::{ import_queue::{BasicQueue, BoxBlockImport, Verifier}, }; use sp_blockchain::HeaderBackend; -use sp_consensus::{CacheKeyId, Environment, Proposer, SelectChain}; +use sp_consensus::{Environment, Proposer, SelectChain}; use sp_inherents::CreateInherentDataProviders; use sp_runtime::{traits::Block as BlockT, ConsensusEngineId}; use std::{marker::PhantomData, sync::Arc}; @@ -62,10 +62,10 @@ impl Verifier for ManualSealVerifier { async fn verify( &mut self, mut block: BlockImportParams, - ) -> Result<(BlockImportParams, Option)>>), String> { + ) -> Result, String> { block.finalized = false; block.fork_choice = Some(ForkChoiceStrategy::LongestChain); - Ok((block, None)) + Ok(block) } } diff --git a/client/consensus/manual-seal/src/seal_block.rs b/client/consensus/manual-seal/src/seal_block.rs index eda3d91c9..e6133bcca 100644 --- a/client/consensus/manual-seal/src/seal_block.rs +++ b/client/consensus/manual-seal/src/seal_block.rs @@ -27,7 +27,7 @@ use sp_blockchain::HeaderBackend; use sp_consensus::{self, BlockOrigin, Environment, Proposer, SelectChain}; use sp_inherents::{CreateInherentDataProviders, InherentDataProvider}; use sp_runtime::traits::{Block as BlockT, Header as HeaderT}; -use std::{collections::HashMap, sync::Arc, time::Duration}; +use std::{sync::Arc, time::Duration}; /// max duration for creating a proposal in secs pub const MAX_PROPOSAL_DURATION: u64 = 10; @@ -153,7 +153,7 @@ pub async fn seal_block( let mut post_header = header.clone(); post_header.digest_mut().logs.extend(params.post_digests.iter().cloned()); - match block_import.import_block(params, HashMap::new()).await? { + match block_import.import_block(params).await? { ImportResult::Imported(aux) => Ok(CreatedBlock { hash: ::Header::hash(&post_header), aux }), other => Err(other.into()), diff --git a/client/consensus/pow/src/lib.rs b/client/consensus/pow/src/lib.rs index 513386fb2..913686b7b 100644 --- a/client/consensus/pow/src/lib.rs +++ b/client/consensus/pow/src/lib.rs @@ -55,7 +55,7 @@ use sc_consensus::{ }; use sp_api::ProvideRuntimeApi; use sp_block_builder::BlockBuilder as BlockBuilderApi; -use sp_blockchain::{well_known_cache_keys::Id as CacheKeyId, HeaderBackend}; +use sp_blockchain::HeaderBackend; use sp_consensus::{Environment, Error as ConsensusError, Proposer, SelectChain, SyncOracle}; use sp_consensus_pow::{Seal, TotalDifficulty, POW_ENGINE_ID}; use sp_core::ExecutionContext; @@ -65,7 +65,7 @@ use sp_runtime::{ traits::{Block as BlockT, Header as HeaderT}, RuntimeString, }; -use std::{cmp::Ordering, collections::HashMap, marker::PhantomData, sync::Arc, time::Duration}; +use std::{cmp::Ordering, marker::PhantomData, sync::Arc, time::Duration}; const LOG_TARGET: &str = "pow"; @@ -325,7 +325,6 @@ where async fn import_block( &mut self, mut block: BlockImportParams, - new_cache: HashMap>, ) -> Result { let best_header = self .select_chain @@ -399,7 +398,7 @@ where )); } - self.inner.import_block(block, new_cache).await.map_err(Into::into) + self.inner.import_block(block).await.map_err(Into::into) } } @@ -449,7 +448,7 @@ where async fn verify( &mut self, mut block: BlockImportParams, - ) -> Result<(BlockImportParams, Option)>>), String> { + ) -> Result, String> { let hash = block.header.hash(); let (checked_header, seal) = self.check_header(block.header)?; @@ -459,7 +458,7 @@ where block.insert_intermediate(INTERMEDIATE_KEY, intermediate); block.post_hash = Some(hash); - Ok((block, None)) + Ok(block) } } diff --git a/client/consensus/pow/src/worker.rs b/client/consensus/pow/src/worker.rs index 1a4238957..3cb5dfcc0 100644 --- a/client/consensus/pow/src/worker.rs +++ b/client/consensus/pow/src/worker.rs @@ -32,7 +32,6 @@ use sp_runtime::{ DigestItem, }; use std::{ - collections::HashMap, pin::Pin, sync::{ atomic::{AtomicUsize, Ordering}, @@ -203,7 +202,7 @@ where let header = import_block.post_header(); let mut block_import = self.block_import.lock(); - match block_import.import_block(import_block, HashMap::default()).await { + match block_import.import_block(import_block).await { Ok(res) => { res.handle_justification( &header.hash(), diff --git a/client/consensus/slots/src/lib.rs b/client/consensus/slots/src/lib.rs index ec6481a87..c18284de6 100644 --- a/client/consensus/slots/src/lib.rs +++ b/client/consensus/slots/src/lib.rs @@ -424,7 +424,7 @@ pub trait SimpleSlotWorker { ); let header = block_import_params.post_header(); - match self.block_import().import_block(block_import_params, Default::default()).await { + match self.block_import().import_block(block_import_params).await { Ok(res) => { res.handle_justification( &header.hash(), diff --git a/client/db/src/lib.rs b/client/db/src/lib.rs index b78c66228..c62b9ce0e 100644 --- a/client/db/src/lib.rs +++ b/client/db/src/lib.rs @@ -68,8 +68,8 @@ use sc_client_api::{ use sc_state_db::{IsPruned, LastCanonicalized, StateDb}; use sp_arithmetic::traits::Saturating; use sp_blockchain::{ - well_known_cache_keys, Backend as _, CachedHeaderMetadata, Error as ClientError, HeaderBackend, - HeaderMetadata, HeaderMetadataCache, Result as ClientResult, + Backend as _, CachedHeaderMetadata, Error as ClientError, HeaderBackend, HeaderMetadata, + HeaderMetadataCache, Result as ClientResult, }; use sp_core::{ offchain::OffchainOverlayedChange, @@ -913,10 +913,6 @@ impl sc_client_api::backend::BlockImportOperation Ok(()) } - fn update_cache(&mut self, _cache: HashMap>) { - // Currently cache isn't implemented on full nodes. - } - fn update_db_storage(&mut self, update: PrefixedMemoryDB>) -> ClientResult<()> { self.db_updates = update; Ok(()) diff --git a/client/network/src/service/tests/mod.rs b/client/network/src/service/tests/mod.rs index 3ac782900..f29e43e6c 100644 --- a/client/network/src/service/tests/mod.rs +++ b/client/network/src/service/tests/mod.rs @@ -33,7 +33,7 @@ use sc_network_sync::{ service::network::{NetworkServiceHandle, NetworkServiceProvider}, state_request_handler::StateRequestHandler, }; -use sp_runtime::traits::{Block as BlockT, Header as _}; +use sp_runtime::traits::Block as BlockT; use std::sync::Arc; use substrate_test_runtime_client::{ runtime::{Block as TestBlock, Hash as TestHash}, @@ -135,31 +135,10 @@ impl TestNetworkBuilder { async fn verify( &mut self, mut block: sc_consensus::BlockImportParams, - ) -> Result< - ( - sc_consensus::BlockImportParams, - Option)>>, - ), - String, - > { - let maybe_keys = block - .header - .digest() - .log(|l| { - l.try_as_raw(sp_runtime::generic::OpaqueDigestItemId::Consensus(b"aura")) - .or_else(|| { - l.try_as_raw(sp_runtime::generic::OpaqueDigestItemId::Consensus( - b"babe", - )) - }) - }) - .map(|blob| { - vec![(sp_blockchain::well_known_cache_keys::AUTHORITIES, blob.to_vec())] - }); - + ) -> Result, String> { block.finalized = self.0; block.fork_choice = Some(sc_consensus::ForkChoiceStrategy::LongestChain); - Ok((block, maybe_keys)) + Ok(block) } } diff --git a/client/network/test/src/lib.rs b/client/network/test/src/lib.rs index 95381dd7b..a79e17e2f 100644 --- a/client/network/test/src/lib.rs +++ b/client/network/test/src/lib.rs @@ -67,7 +67,6 @@ use sc_network_sync::{ }; use sc_service::client::Client; use sp_blockchain::{ - well_known_cache_keys::{self, Id as CacheKeyId}, Backend as BlockchainBackend, HeaderBackend, Info as BlockchainInfo, Result as ClientResult, }; use sp_consensus::{ @@ -77,7 +76,7 @@ use sp_consensus::{ use sp_core::H256; use sp_runtime::{ codec::{Decode, Encode}, - generic::{BlockId, OpaqueDigestItemId}, + generic::BlockId, traits::{Block as BlockT, Header as HeaderT, NumberFor}, Justification, Justifications, }; @@ -112,20 +111,12 @@ impl Verifier for PassThroughVerifier { async fn verify( &mut self, mut block: BlockImportParams, - ) -> Result<(BlockImportParams, Option)>>), String> { - let maybe_keys = block - .header - .digest() - .log(|l| { - l.try_as_raw(OpaqueDigestItemId::Consensus(b"aura")) - .or_else(|| l.try_as_raw(OpaqueDigestItemId::Consensus(b"babe"))) - }) - .map(|blob| vec![(well_known_cache_keys::AUTHORITIES, blob.to_vec())]); + ) -> Result, String> { if block.fork_choice.is_none() { block.fork_choice = Some(ForkChoiceStrategy::LongestChain); }; block.finalized = self.finalized; - Ok((block, maybe_keys)) + Ok(block) } } @@ -224,9 +215,8 @@ impl BlockImport for PeersClient { async fn import_block( &mut self, block: BlockImportParams, - cache: HashMap>, ) -> Result { - self.client.import_block(block.clear_storage_changes_and_mutate(), cache).await + self.client.import_block(block.clear_storage_changes_and_mutate()).await } } @@ -392,15 +382,10 @@ where let mut import_block = BlockImportParams::new(origin, header.clone()); import_block.body = if headers_only { None } else { Some(block.extrinsics) }; import_block.fork_choice = Some(fork_choice); - let (import_block, cache) = + let import_block = futures::executor::block_on(self.verifier.verify(import_block)).unwrap(); - let cache = if let Some(cache) = cache { - cache.into_iter().collect() - } else { - Default::default() - }; - futures::executor::block_on(self.block_import.import_block(import_block, cache)) + futures::executor::block_on(self.block_import.import_block(import_block)) .expect("block_import failed"); if announce_block { self.sync_service.announce_block(hash, None); @@ -633,9 +618,8 @@ where async fn import_block( &mut self, block: BlockImportParams, - cache: HashMap>, ) -> Result { - self.inner.import_block(block.clear_storage_changes_and_mutate(), cache).await + self.inner.import_block(block.clear_storage_changes_and_mutate()).await } } @@ -650,7 +634,7 @@ impl Verifier for VerifierAdapter { async fn verify( &mut self, block: BlockImportParams, - ) -> Result<(BlockImportParams, Option)>>), String> { + ) -> Result, String> { let hash = block.header.hash(); self.verifier.lock().await.verify(block).await.map_err(|e| { self.failed_verifications.lock().insert(hash, e.clone()); diff --git a/client/service/src/client/client.rs b/client/service/src/client/client.rs index 9a2a376eb..3613cc760 100644 --- a/client/service/src/client/client.rs +++ b/client/service/src/client/client.rs @@ -51,8 +51,8 @@ use sp_api::{ ProvideRuntimeApi, }; use sp_blockchain::{ - self as blockchain, well_known_cache_keys::Id as CacheKeyId, Backend as ChainBackend, - CachedHeaderMetadata, Error, HeaderBackend as ChainHeaderBackend, HeaderMetadata, + self as blockchain, Backend as ChainBackend, CachedHeaderMetadata, Error, + HeaderBackend as ChainHeaderBackend, HeaderMetadata, }; use sp_consensus::{BlockOrigin, BlockStatus, Error as ConsensusError}; @@ -504,7 +504,6 @@ where &self, operation: &mut ClientImportOperation, import_block: BlockImportParams>, - new_cache: HashMap>, storage_changes: Option< sc_consensus::StorageChanges>, >, @@ -559,7 +558,6 @@ where body, indexed_body, storage_changes, - new_cache, finalized, auxiliary, fork_choice, @@ -599,7 +597,6 @@ where storage_changes: Option< sc_consensus::StorageChanges>, >, - new_cache: HashMap>, finalized: bool, aux: Vec<(Vec, Option>)>, fork_choice: ForkChoiceStrategy, @@ -712,7 +709,6 @@ where }, }; - operation.op.update_cache(new_cache); storage_changes }, None => None, @@ -1770,7 +1766,6 @@ where async fn import_block( &mut self, mut import_block: BlockImportParams>, - new_cache: HashMap>, ) -> Result { let span = tracing::span!(tracing::Level::DEBUG, "import_block"); let _enter = span.enter(); @@ -1785,7 +1780,7 @@ where }; self.lock_import_and_run(|operation| { - self.apply_block(operation, import_block, new_cache, storage_changes) + self.apply_block(operation, import_block, storage_changes) }) .map_err(|e| { warn!("Block import error: {}", e); @@ -1875,9 +1870,8 @@ where async fn import_block( &mut self, import_block: BlockImportParams, - new_cache: HashMap>, ) -> Result { - (&*self).import_block(import_block, new_cache).await + (&*self).import_block(import_block).await } async fn check_block( diff --git a/client/service/test/src/client/mod.rs b/client/service/test/src/client/mod.rs index c7b19ca8b..cae69413c 100644 --- a/client/service/test/src/client/mod.rs +++ b/client/service/test/src/client/mod.rs @@ -930,7 +930,7 @@ fn finality_target_with_best_not_on_longest_chain() { let mut import_params = BlockImportParams::new(BlockOrigin::Own, header); import_params.body = Some(extrinsics); import_params.fork_choice = Some(ForkChoiceStrategy::Custom(false)); - block_on(client.import_block(import_params, Default::default())).unwrap(); + block_on(client.import_block(import_params)).unwrap(); // double check that B3 is still the best... assert_eq!(client.info().best_hash, b3.hash()); @@ -1963,7 +1963,7 @@ fn cleans_up_closed_notification_sinks_on_block_import() { let mut import = BlockImportParams::new(origin, header); import.body = Some(extrinsics); import.fork_choice = Some(ForkChoiceStrategy::LongestChain); - block_on(client.import_block(import, Default::default())).unwrap(); + block_on(client.import_block(import)).unwrap(); }; // after importing a block we should still have 4 notification sinks diff --git a/primitives/blockchain/src/backend.rs b/primitives/blockchain/src/backend.rs index 227559239..e9278be1d 100644 --- a/primitives/blockchain/src/backend.rs +++ b/primitives/blockchain/src/backend.rs @@ -288,18 +288,3 @@ pub enum BlockStatus { /// Not in the queue or the blockchain. Unknown, } - -/// A list of all well known keys in the blockchain cache. -pub mod well_known_cache_keys { - /// The type representing cache keys. - pub type Id = sp_consensus::CacheKeyId; - - /// A list of authorities. - pub const AUTHORITIES: Id = *b"auth"; - - /// Current Epoch data. - pub const EPOCH: Id = *b"epch"; - - /// Changes trie configuration. - pub const CHANGES_TRIE_CONFIG: Id = *b"chtr"; -} diff --git a/primitives/consensus/common/src/lib.rs b/primitives/consensus/common/src/lib.rs index e02564654..215b4448b 100644 --- a/primitives/consensus/common/src/lib.rs +++ b/primitives/consensus/common/src/lib.rs @@ -39,9 +39,6 @@ pub use select_chain::SelectChain; pub use sp_inherents::InherentData; pub use sp_state_machine::Backend as StateBackend; -/// Type of keys in the blockchain cache that consensus module could use for its needs. -pub type CacheKeyId = [u8; 4]; - /// Block status. #[derive(Debug, PartialEq, Eq)] pub enum BlockStatus { diff --git a/test-utils/client/src/client_ext.rs b/test-utils/client/src/client_ext.rs index c73ea76a3..a258faa5e 100644 --- a/test-utils/client/src/client_ext.rs +++ b/test-utils/client/src/client_ext.rs @@ -17,7 +17,6 @@ //! Client extension for tests. -use codec::alloc::collections::hash_map::HashMap; use sc_client_api::{backend::Finalizer, client::BlockBackend}; use sc_consensus::{BlockImport, BlockImportParams, ForkChoiceStrategy}; use sc_service::client::Client; @@ -100,7 +99,7 @@ where import.body = Some(extrinsics); import.fork_choice = Some(ForkChoiceStrategy::LongestChain); - BlockImport::import_block(self, import, HashMap::new()).await.map(|_| ()) + BlockImport::import_block(self, import).await.map(|_| ()) } async fn import_as_best( @@ -113,7 +112,7 @@ where import.body = Some(extrinsics); import.fork_choice = Some(ForkChoiceStrategy::Custom(true)); - BlockImport::import_block(self, import, HashMap::new()).await.map(|_| ()) + BlockImport::import_block(self, import).await.map(|_| ()) } async fn import_as_final( @@ -127,7 +126,7 @@ where import.finalized = true; import.fork_choice = Some(ForkChoiceStrategy::Custom(true)); - BlockImport::import_block(self, import, HashMap::new()).await.map(|_| ()) + BlockImport::import_block(self, import).await.map(|_| ()) } async fn import_justified( @@ -143,7 +142,7 @@ where import.finalized = true; import.fork_choice = Some(ForkChoiceStrategy::LongestChain); - BlockImport::import_block(self, import, HashMap::new()).await.map(|_| ()) + BlockImport::import_block(self, import).await.map(|_| ()) } } @@ -162,7 +161,7 @@ where import.body = Some(extrinsics); import.fork_choice = Some(ForkChoiceStrategy::LongestChain); - BlockImport::import_block(self, import, HashMap::new()).await.map(|_| ()) + BlockImport::import_block(self, import).await.map(|_| ()) } async fn import_as_best( @@ -175,7 +174,7 @@ where import.body = Some(extrinsics); import.fork_choice = Some(ForkChoiceStrategy::Custom(true)); - BlockImport::import_block(self, import, HashMap::new()).await.map(|_| ()) + BlockImport::import_block(self, import).await.map(|_| ()) } async fn import_as_final( @@ -189,7 +188,7 @@ where import.finalized = true; import.fork_choice = Some(ForkChoiceStrategy::Custom(true)); - BlockImport::import_block(self, import, HashMap::new()).await.map(|_| ()) + BlockImport::import_block(self, import).await.map(|_| ()) } async fn import_justified( @@ -205,6 +204,6 @@ where import.finalized = true; import.fork_choice = Some(ForkChoiceStrategy::LongestChain); - BlockImport::import_block(self, import, HashMap::new()).await.map(|_| ()) + BlockImport::import_block(self, import).await.map(|_| ()) } } From 306382bce6b21a8ddeaf9a3574557e51cc8a48b6 Mon Sep 17 00:00:00 2001 From: Oliver Tale-Yazdi Date: Tue, 7 Mar 2023 12:31:57 +0100 Subject: [PATCH 208/558] `MessageQueue`: unknit permanently overweight books (#13528) * Unknit permanently overweight books A book with only permanently overweight messages should be unkit from the ready ring. This does currently not happen since perm. overweight messages are not counted as "processed" and therefore not increase the "total_processed" counter. This is only a problem when the next and only message that is processed is overweight. Eventually this should resolve itself when another non-overweight message is enqueued and processed. But for correctness it should be unknitted. Signed-off-by: Oliver Tale-Yazdi * Add tests Signed-off-by: Oliver Tale-Yazdi * fmt Signed-off-by: Oliver Tale-Yazdi * One more tests Signed-off-by: Oliver Tale-Yazdi * ".git/.scripts/commands/bench/bench.sh" pallet dev pallet-message-queue --------- Signed-off-by: Oliver Tale-Yazdi Co-authored-by: command-bot <> --- frame/message-queue/src/lib.rs | 4 +- frame/message-queue/src/mock.rs | 9 ++ frame/message-queue/src/mock_helpers.rs | 4 +- frame/message-queue/src/tests.rs | 115 ++++++++++++++++++++++++ frame/message-queue/src/weights.rs | 104 +++++++++------------ 5 files changed, 170 insertions(+), 66 deletions(-) diff --git a/frame/message-queue/src/lib.rs b/frame/message-queue/src/lib.rs index 409503d31..bed131e5f 100644 --- a/frame/message-queue/src/lib.rs +++ b/frame/message-queue/src/lib.rs @@ -961,11 +961,11 @@ impl Pallet { book_state.begin.saturating_inc(); } let next_ready = book_state.ready_neighbours.as_ref().map(|x| x.next.clone()); - if book_state.begin >= book_state.end && total_processed > 0 { + if book_state.begin >= book_state.end { // No longer ready - unknit. if let Some(neighbours) = book_state.ready_neighbours.take() { Self::ready_ring_unknit(&origin, neighbours); - } else { + } else if total_processed > 0 { defensive!("Freshly processed queue must have been ready"); } } diff --git a/frame/message-queue/src/mock.rs b/frame/message-queue/src/mock.rs index 28a599bcf..a0fe01056 100644 --- a/frame/message-queue/src/mock.rs +++ b/frame/message-queue/src/mock.rs @@ -320,3 +320,12 @@ pub fn knit(queue: &MessageOrigin) { pub fn unknit(queue: &MessageOrigin) { super::mock_helpers::unknit::(queue); } + +pub fn num_overweight_enqueued_events() -> u32 { + frame_system::Pallet::::events() + .into_iter() + .filter(|e| { + matches!(e.event, RuntimeEvent::MessageQueue(crate::Event::OverweightEnqueued { .. })) + }) + .count() as u32 +} diff --git a/frame/message-queue/src/mock_helpers.rs b/frame/message-queue/src/mock_helpers.rs index 716a60782..257691cae 100644 --- a/frame/message-queue/src/mock_helpers.rs +++ b/frame/message-queue/src/mock_helpers.rs @@ -74,11 +74,11 @@ where } /// Create a message from the given data. -pub fn msg>(x: &'static str) -> BoundedSlice { +pub fn msg>(x: &str) -> BoundedSlice { BoundedSlice::defensive_truncate_from(x.as_bytes()) } -pub fn vmsg(x: &'static str) -> Vec { +pub fn vmsg(x: &str) -> Vec { x.as_bytes().to_vec() } diff --git a/frame/message-queue/src/tests.rs b/frame/message-queue/src/tests.rs index d3b0555f2..15bb90573 100644 --- a/frame/message-queue/src/tests.rs +++ b/frame/message-queue/src/tests.rs @@ -1083,6 +1083,121 @@ fn execute_overweight_works() { assert_eq!(consumed, Err(ExecuteOverweightError::NotFound)); assert!(QueueChanges::take().is_empty()); assert!(!Pages::::contains_key(origin, 0), "Page is gone"); + // The book should have been unknit from the ready ring. + assert!(!ServiceHead::::exists(), "No ready book"); + }); +} + +#[test] +fn permanently_overweight_book_unknits() { + use MessageOrigin::*; + + new_test_ext::().execute_with(|| { + set_weight("bump_service_head", 1.into_weight()); + set_weight("service_queue_base", 1.into_weight()); + set_weight("service_page_base_completion", 1.into_weight()); + + MessageQueue::enqueue_messages([msg("weight=9")].into_iter(), Here); + + // It is the only ready book. + assert_ring(&[Here]); + // Mark the message as overweight. + assert_eq!(MessageQueue::service_queues(8.into_weight()), 4.into_weight()); + assert_last_event::( + Event::OverweightEnqueued { + hash: ::Hashing::hash(b"weight=9"), + origin: Here, + message_index: 0, + page_index: 0, + } + .into(), + ); + // The book is not ready anymore. + assert_ring(&[]); + assert_eq!(MessagesProcessed::take().len(), 0); + assert_eq!(BookStateFor::::get(Here).message_count, 1); + // Now if we enqueue another message, it will become ready again. + MessageQueue::enqueue_messages([msg("weight=1")].into_iter(), Here); + assert_ring(&[Here]); + assert_eq!(MessageQueue::service_queues(8.into_weight()), 5.into_weight()); + assert_eq!(MessagesProcessed::take().len(), 1); + assert_ring(&[]); + }); +} + +#[test] +fn permanently_overweight_book_unknits_multiple() { + use MessageOrigin::*; + + new_test_ext::().execute_with(|| { + set_weight("bump_service_head", 1.into_weight()); + set_weight("service_queue_base", 1.into_weight()); + set_weight("service_page_base_completion", 1.into_weight()); + + MessageQueue::enqueue_messages( + [msg("weight=1"), msg("weight=9"), msg("weight=9")].into_iter(), + Here, + ); + + assert_ring(&[Here]); + // Process the first message. + assert_eq!(MessageQueue::service_queues(4.into_weight()), 4.into_weight()); + assert_eq!(num_overweight_enqueued_events(), 0); + assert_eq!(MessagesProcessed::take().len(), 1); + + // Book is still ready since it was not marked as overweight yet. + assert_ring(&[Here]); + assert_eq!(MessageQueue::service_queues(8.into_weight()), 5.into_weight()); + assert_eq!(num_overweight_enqueued_events(), 2); + assert_eq!(MessagesProcessed::take().len(), 0); + // Now it is overweight. + assert_ring(&[]); + // Enqueue another message. + MessageQueue::enqueue_messages([msg("weight=1")].into_iter(), Here); + assert_ring(&[Here]); + assert_eq!(MessageQueue::service_queues(4.into_weight()), 4.into_weight()); + assert_eq!(MessagesProcessed::take().len(), 1); + assert_ring(&[]); + }); +} + +/// We don't want empty books in the ready ring, but if they somehow make their way in there, it +/// should not panic. +#[test] +#[cfg(not(debug_assertions))] // Would trigger a defensive failure otherwise. +fn ready_but_empty_does_not_panic() { + use MessageOrigin::*; + + new_test_ext::().execute_with(|| { + BookStateFor::::insert(Here, empty_book::()); + BookStateFor::::insert(There, empty_book::()); + + knit(&Here); + knit(&There); + assert_ring(&[Here, There]); + + assert_eq!(MessageQueue::service_queues(Weight::MAX), 0.into_weight()); + assert_ring(&[]); + }); +} + +/// We don't want permanently books in the ready ring, but if they somehow make their way in there, +/// it should not panic. +#[test] +#[cfg(not(debug_assertions))] // Would trigger a defensive failure otherwise. +fn ready_but_perm_overweight_does_not_panic() { + use MessageOrigin::*; + + new_test_ext::().execute_with(|| { + MessageQueue::enqueue_message(msg("weight=9"), Here); + assert_eq!(MessageQueue::service_queues(8.into_weight()), 0.into_weight()); + assert_ring(&[]); + // Force it back into the ready ring. + knit(&Here); + assert_ring(&[Here]); + assert_eq!(MessageQueue::service_queues(Weight::MAX), 0.into_weight()); + // Unready again. + assert_ring(&[]); }); } diff --git a/frame/message-queue/src/weights.rs b/frame/message-queue/src/weights.rs index d1ccb338e..fd788f2ba 100644 --- a/frame/message-queue/src/weights.rs +++ b/frame/message-queue/src/weights.rs @@ -18,7 +18,7 @@ //! Autogenerated weights for pallet_message_queue //! //! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev -//! DATE: 2023-02-27, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! DATE: 2023-03-06, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` //! WORST CASE MAP SIZE: `1000000` //! HOSTNAME: `bm3`, CPU: `Intel(R) Core(TM) i7-7700K CPU @ 4.20GHz` //! EXECUTION: Some(Wasm), WASM-EXECUTION: Compiled, CHAIN: Some("dev"), DB CACHE: 1024 @@ -34,7 +34,7 @@ // --wasm-execution=compiled // --heap-pages=4096 // --json-file=/var/lib/gitlab-runner/builds/zyw4fam_/0/parity/mirrors/substrate/.git/.artifacts/bench.json -// --pallet=pallet_message_queue +// --pallet=pallet-message-queue // --chain=dev // --header=./HEADER-APACHE2 // --output=./frame/message-queue/src/weights.rs @@ -72,9 +72,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `295` // Estimated: `7527` - // Minimum execution time: 12_538 nanoseconds. - Weight::from_parts(12_799_000, 0) - .saturating_add(Weight::from_parts(0, 7527)) + // Minimum execution time: 12_283_000 picoseconds. + Weight::from_parts(12_554_000, 7527) .saturating_add(T::DbWeight::get().reads(3_u64)) .saturating_add(T::DbWeight::get().writes(2_u64)) } @@ -86,9 +85,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `295` // Estimated: `7527` - // Minimum execution time: 11_727 nanoseconds. - Weight::from_parts(12_177_000, 0) - .saturating_add(Weight::from_parts(0, 7527)) + // Minimum execution time: 11_484_000 picoseconds. + Weight::from_parts(11_900_000, 7527) .saturating_add(T::DbWeight::get().reads(3_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) } @@ -98,9 +96,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `42` // Estimated: `3514` - // Minimum execution time: 4_983 nanoseconds. - Weight::from_parts(5_174_000, 0) - .saturating_add(Weight::from_parts(0, 3514)) + // Minimum execution time: 4_793_000 picoseconds. + Weight::from_parts(4_990_000, 3514) .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -110,9 +107,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `113` // Estimated: `69049` - // Minimum execution time: 6_299 nanoseconds. - Weight::from_parts(6_670_000, 0) - .saturating_add(Weight::from_parts(0, 69049)) + // Minimum execution time: 6_231_000 picoseconds. + Weight::from_parts(6_442_000, 69049) .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -122,9 +118,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `113` // Estimated: `69049` - // Minimum execution time: 6_762 nanoseconds. - Weight::from_parts(7_059_000, 0) - .saturating_add(Weight::from_parts(0, 69049)) + // Minimum execution time: 6_660_000 picoseconds. + Weight::from_parts(6_825_000, 69049) .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -132,9 +127,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 72_681 nanoseconds. - Weight::from_parts(73_147_000, 0) - .saturating_add(Weight::from_parts(0, 0)) + // Minimum execution time: 72_805_000 picoseconds. + Weight::from_parts(74_650_000, 0) } /// Storage: MessageQueue ServiceHead (r:1 w:1) /// Proof: MessageQueue ServiceHead (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) @@ -144,9 +138,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `172` // Estimated: `5003` - // Minimum execution time: 7_066 nanoseconds. - Weight::from_parts(7_214_000, 0) - .saturating_add(Weight::from_parts(0, 5003)) + // Minimum execution time: 7_078_000 picoseconds. + Weight::from_parts(7_230_000, 5003) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -158,9 +151,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `65742` // Estimated: `72563` - // Minimum execution time: 57_778 nanoseconds. - Weight::from_parts(58_778_000, 0) - .saturating_add(Weight::from_parts(0, 72563)) + // Minimum execution time: 56_799_000 picoseconds. + Weight::from_parts(57_634_000, 72563) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().writes(2_u64)) } @@ -172,9 +164,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `65742` // Estimated: `72563` - // Minimum execution time: 72_144 nanoseconds. - Weight::from_parts(72_942_000, 0) - .saturating_add(Weight::from_parts(0, 72563)) + // Minimum execution time: 72_290_000 picoseconds. + Weight::from_parts(72_754_000, 72563) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().writes(2_u64)) } @@ -186,9 +177,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `65742` // Estimated: `72563` - // Minimum execution time: 84_890 nanoseconds. - Weight::from_parts(86_073_000, 0) - .saturating_add(Weight::from_parts(0, 72563)) + // Minimum execution time: 84_987_000 picoseconds. + Weight::from_parts(85_562_000, 72563) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().writes(2_u64)) } @@ -204,9 +194,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `295` // Estimated: `7527` - // Minimum execution time: 12_538 nanoseconds. - Weight::from_parts(12_799_000, 0) - .saturating_add(Weight::from_parts(0, 7527)) + // Minimum execution time: 12_283_000 picoseconds. + Weight::from_parts(12_554_000, 7527) .saturating_add(RocksDbWeight::get().reads(3_u64)) .saturating_add(RocksDbWeight::get().writes(2_u64)) } @@ -218,9 +207,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `295` // Estimated: `7527` - // Minimum execution time: 11_727 nanoseconds. - Weight::from_parts(12_177_000, 0) - .saturating_add(Weight::from_parts(0, 7527)) + // Minimum execution time: 11_484_000 picoseconds. + Weight::from_parts(11_900_000, 7527) .saturating_add(RocksDbWeight::get().reads(3_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) } @@ -230,9 +218,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `42` // Estimated: `3514` - // Minimum execution time: 4_983 nanoseconds. - Weight::from_parts(5_174_000, 0) - .saturating_add(Weight::from_parts(0, 3514)) + // Minimum execution time: 4_793_000 picoseconds. + Weight::from_parts(4_990_000, 3514) .saturating_add(RocksDbWeight::get().reads(1_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } @@ -242,9 +229,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `113` // Estimated: `69049` - // Minimum execution time: 6_299 nanoseconds. - Weight::from_parts(6_670_000, 0) - .saturating_add(Weight::from_parts(0, 69049)) + // Minimum execution time: 6_231_000 picoseconds. + Weight::from_parts(6_442_000, 69049) .saturating_add(RocksDbWeight::get().reads(1_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } @@ -254,9 +240,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `113` // Estimated: `69049` - // Minimum execution time: 6_762 nanoseconds. - Weight::from_parts(7_059_000, 0) - .saturating_add(Weight::from_parts(0, 69049)) + // Minimum execution time: 6_660_000 picoseconds. + Weight::from_parts(6_825_000, 69049) .saturating_add(RocksDbWeight::get().reads(1_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } @@ -264,9 +249,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 72_681 nanoseconds. - Weight::from_parts(73_147_000, 0) - .saturating_add(Weight::from_parts(0, 0)) + // Minimum execution time: 72_805_000 picoseconds. + Weight::from_parts(74_650_000, 0) } /// Storage: MessageQueue ServiceHead (r:1 w:1) /// Proof: MessageQueue ServiceHead (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) @@ -276,9 +260,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `172` // Estimated: `5003` - // Minimum execution time: 7_066 nanoseconds. - Weight::from_parts(7_214_000, 0) - .saturating_add(Weight::from_parts(0, 5003)) + // Minimum execution time: 7_078_000 picoseconds. + Weight::from_parts(7_230_000, 5003) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } @@ -290,9 +273,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `65742` // Estimated: `72563` - // Minimum execution time: 57_778 nanoseconds. - Weight::from_parts(58_778_000, 0) - .saturating_add(Weight::from_parts(0, 72563)) + // Minimum execution time: 56_799_000 picoseconds. + Weight::from_parts(57_634_000, 72563) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().writes(2_u64)) } @@ -304,9 +286,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `65742` // Estimated: `72563` - // Minimum execution time: 72_144 nanoseconds. - Weight::from_parts(72_942_000, 0) - .saturating_add(Weight::from_parts(0, 72563)) + // Minimum execution time: 72_290_000 picoseconds. + Weight::from_parts(72_754_000, 72563) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().writes(2_u64)) } @@ -318,9 +299,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `65742` // Estimated: `72563` - // Minimum execution time: 84_890 nanoseconds. - Weight::from_parts(86_073_000, 0) - .saturating_add(Weight::from_parts(0, 72563)) + // Minimum execution time: 84_987_000 picoseconds. + Weight::from_parts(85_562_000, 72563) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().writes(2_u64)) } From 851e73a6a1ff749cda0f50d5797eb1ae9804294b Mon Sep 17 00:00:00 2001 From: Davide Galassi Date: Tue, 7 Mar 2023 21:25:55 +0100 Subject: [PATCH 209/558] Offences report system rework (#13425) * Experiments with common equivocation trait * Improved equivocation trait * Fix grandpa equivocation implementation * Remove some cruft * Remove some more cruft * More generic naming * Simplification of offences manipilation * More refactory * Some prograss with the encapsulation of offence report system * Finally unit type works as a universal null report system * Align substrate node code * Further simplification * Fix test utils * Remove not required associated type * Fix benches * Rollback to prev field name * Box big params * Fix typo * Remove new tag computation * Remove default implementations * Better docs * Return 'Result' instead of bool * Change offence report system return types * Some renaming and documentation * Improve documentation * More abstract offence report system * Rename 'consume_evidence' to 'process_evidence' * Further docs refinements * Doc for dummy offence report * Fix rustdoc * Fix after master merge * Apply code review suggestions * Improve docs --- bin/node-template/runtime/src/lib.rs | 15 +- bin/node/runtime/src/lib.rs | 53 +--- frame/babe/src/equivocation.rs | 281 ++++++++--------- frame/babe/src/lib.rs | 131 +++----- frame/babe/src/mock.rs | 17 +- frame/babe/src/tests.rs | 2 +- frame/grandpa/src/equivocation.rs | 384 ++++++++++-------------- frame/grandpa/src/lib.rs | 159 +++------- frame/grandpa/src/mock.rs | 18 +- frame/grandpa/src/tests.rs | 3 +- frame/offences/benchmarking/src/lib.rs | 4 +- frame/offences/src/lib.rs | 6 +- frame/offences/src/mock.rs | 10 +- frame/offences/src/tests.rs | 12 +- primitives/consensus/grandpa/src/lib.rs | 3 + primitives/consensus/slots/src/lib.rs | 2 +- primitives/staking/src/offence.rs | 71 ++++- test-utils/runtime/src/lib.rs | 19 +- 18 files changed, 474 insertions(+), 716 deletions(-) diff --git a/bin/node-template/runtime/src/lib.rs b/bin/node-template/runtime/src/lib.rs index 57e4470c7..ef90517a2 100644 --- a/bin/node-template/runtime/src/lib.rs +++ b/bin/node-template/runtime/src/lib.rs @@ -214,21 +214,12 @@ impl pallet_aura::Config for Runtime { impl pallet_grandpa::Config for Runtime { type RuntimeEvent = RuntimeEvent; - type KeyOwnerProofSystem = (); - - type KeyOwnerProof = - >::Proof; - - type KeyOwnerIdentification = >::IdentificationTuple; - - type HandleEquivocation = (); - type WeightInfo = (); type MaxAuthorities = ConstU32<32>; type MaxSetIdSessionEntries = ConstU64<0>; + + type KeyOwnerProof = sp_core::Void; + type EquivocationReportSystem = (); } impl pallet_timestamp::Config for Runtime { diff --git a/bin/node/runtime/src/lib.rs b/bin/node/runtime/src/lib.rs index 3d160108f..c507c2d8d 100644 --- a/bin/node/runtime/src/lib.rs +++ b/bin/node/runtime/src/lib.rs @@ -113,6 +113,11 @@ pub mod assets_api; #[cfg(feature = "std")] include!(concat!(env!("OUT_DIR"), "/wasm_binary.rs")); +/// Max size for serialized extrinsic params for this testing runtime. +/// This is a quite arbitrary but empirically battle tested value. +#[cfg(test)] +pub const CALL_PARAMS_MAX_SIZE: usize = 208; + /// Wasm binary unwrapped. If built with `SKIP_WASM_BUILD`, the function panics. #[cfg(feature = "std")] pub fn wasm_binary_unwrap() -> &'static [u8] { @@ -399,24 +404,12 @@ impl pallet_babe::Config for Runtime { type ExpectedBlockTime = ExpectedBlockTime; type EpochChangeTrigger = pallet_babe::ExternalTrigger; type DisabledValidators = Session; - - type KeyOwnerProofSystem = Historical; - - type KeyOwnerProof = >::Proof; - - type KeyOwnerIdentification = >::IdentificationTuple; - - type HandleEquivocation = - pallet_babe::EquivocationHandler; - type WeightInfo = (); type MaxAuthorities = MaxAuthorities; + type KeyOwnerProof = + >::Proof; + type EquivocationReportSystem = + pallet_babe::EquivocationReportSystem; } parameter_types! { @@ -1328,26 +1321,12 @@ parameter_types! { impl pallet_grandpa::Config for Runtime { type RuntimeEvent = RuntimeEvent; - - type KeyOwnerProofSystem = Historical; - - type KeyOwnerProof = - >::Proof; - - type KeyOwnerIdentification = >::IdentificationTuple; - - type HandleEquivocation = pallet_grandpa::EquivocationHandler< - Self::KeyOwnerIdentification, - Offences, - ReportLongevity, - >; - type WeightInfo = (); type MaxAuthorities = MaxAuthorities; type MaxSetIdSessionEntries = MaxSetIdSessionEntries; + type KeyOwnerProof = >::Proof; + type EquivocationReportSystem = + pallet_grandpa::EquivocationReportSystem; } parameter_types! { @@ -2483,10 +2462,10 @@ mod tests { fn call_size() { let size = core::mem::size_of::(); assert!( - size <= 208, - "size of RuntimeCall {} is more than 208 bytes: some calls have too big arguments, use Box to reduce the - size of RuntimeCall. - If the limit is too strong, maybe consider increase the limit to 300.", + size <= CALL_PARAMS_MAX_SIZE, + "size of RuntimeCall {} is more than {CALL_PARAMS_MAX_SIZE} bytes. + Some calls have too big arguments, use Box to reduce the size of RuntimeCall. + If the limit is too strong, maybe consider increase the limit.", size, ); } diff --git a/frame/babe/src/equivocation.rs b/frame/babe/src/equivocation.rs index 2a2e2b476..ed98385a7 100644 --- a/frame/babe/src/equivocation.rs +++ b/frame/babe/src/equivocation.rs @@ -34,143 +34,163 @@ //! definition. use frame_support::traits::{Get, KeyOwnerProofSystem}; -use sp_consensus_babe::{EquivocationProof, Slot}; +use log::{error, info}; + +use sp_consensus_babe::{AuthorityId, EquivocationProof, Slot, KEY_TYPE}; use sp_runtime::{ transaction_validity::{ InvalidTransaction, TransactionPriority, TransactionSource, TransactionValidity, TransactionValidityError, ValidTransaction, }, - DispatchResult, Perbill, + DispatchError, KeyTypeId, Perbill, }; +use sp_session::{GetSessionNumber, GetValidatorCount}; use sp_staking::{ - offence::{Kind, Offence, OffenceError, ReportOffence}, + offence::{Kind, Offence, OffenceReportSystem, ReportOffence}, SessionIndex, }; use sp_std::prelude::*; -use crate::{Call, Config, Pallet, LOG_TARGET}; - -/// A trait with utility methods for handling equivocation reports in BABE. -/// The trait provides methods for reporting an offence triggered by a valid -/// equivocation report, checking the current block author (to declare as the -/// reporter), and also for creating and submitting equivocation report -/// extrinsics (useful only in offchain context). -pub trait HandleEquivocation { - /// The longevity, in blocks, that the equivocation report is valid for. When using the staking - /// pallet this should be equal to the bonding duration (in blocks, not eras). - type ReportLongevity: Get; - - /// Report an offence proved by the given reporters. - fn report_offence( - reporters: Vec, - offence: BabeEquivocationOffence, - ) -> Result<(), OffenceError>; - - /// Returns true if all of the offenders at the given time slot have already been reported. - fn is_known_offence(offenders: &[T::KeyOwnerIdentification], time_slot: &Slot) -> bool; - - /// Create and dispatch an equivocation report extrinsic. - fn submit_unsigned_equivocation_report( - equivocation_proof: EquivocationProof, - key_owner_proof: T::KeyOwnerProof, - ) -> DispatchResult; - - /// Fetch the current block author id, if defined. - fn block_author() -> Option; +use crate::{Call, Config, Error, Pallet, LOG_TARGET}; + +/// BABE equivocation offence report. +/// +/// When a validator released two or more blocks at the same slot. +pub struct EquivocationOffence { + /// A babe slot in which this incident happened. + pub slot: Slot, + /// The session index in which the incident happened. + pub session_index: SessionIndex, + /// The size of the validator set at the time of the offence. + pub validator_set_count: u32, + /// The authority that produced the equivocation. + pub offender: Offender, } -impl HandleEquivocation for () { - type ReportLongevity = (); +impl Offence for EquivocationOffence { + const ID: Kind = *b"babe:equivocatio"; + type TimeSlot = Slot; - fn report_offence( - _reporters: Vec, - _offence: BabeEquivocationOffence, - ) -> Result<(), OffenceError> { - Ok(()) + fn offenders(&self) -> Vec { + vec![self.offender.clone()] } - fn is_known_offence(_offenders: &[T::KeyOwnerIdentification], _time_slot: &Slot) -> bool { - true + fn session_index(&self) -> SessionIndex { + self.session_index } - fn submit_unsigned_equivocation_report( - _equivocation_proof: EquivocationProof, - _key_owner_proof: T::KeyOwnerProof, - ) -> DispatchResult { - Ok(()) + fn validator_set_count(&self) -> u32 { + self.validator_set_count } - fn block_author() -> Option { - None + fn time_slot(&self) -> Self::TimeSlot { + self.slot } -} - -/// Generic equivocation handler. This type implements `HandleEquivocation` -/// using existing subsystems that are part of frame (type bounds described -/// below) and will dispatch to them directly, it's only purpose is to wire all -/// subsystems together. -pub struct EquivocationHandler { - _phantom: sp_std::marker::PhantomData<(I, R, L)>, -} -impl Default for EquivocationHandler { - fn default() -> Self { - Self { _phantom: Default::default() } + // The formula is min((3k / n)^2, 1) + // where k = offenders_number and n = validators_number + fn slash_fraction(&self, offenders_count: u32) -> Perbill { + // Perbill type domain is [0, 1] by definition + Perbill::from_rational(3 * offenders_count, self.validator_set_count).square() } } -impl HandleEquivocation for EquivocationHandler +/// Babe equivocation offence system. +/// +/// This type implements `OffenceReportSystem` +pub struct EquivocationReportSystem(sp_std::marker::PhantomData<(T, R, P, L)>); + +// We use the authorship pallet to fetch the current block author and use +// `offchain::SendTransactionTypes` for unsigned extrinsic creation and +// submission. +impl + OffenceReportSystem, (EquivocationProof, T::KeyOwnerProof)> + for EquivocationReportSystem where - // We use the authorship pallet to fetch the current block author and use - // `offchain::SendTransactionTypes` for unsigned extrinsic creation and - // submission. T: Config + pallet_authorship::Config + frame_system::offchain::SendTransactionTypes>, - // A system for reporting offences after valid equivocation reports are - // processed. R: ReportOffence< T::AccountId, - T::KeyOwnerIdentification, - BabeEquivocationOffence, + P::IdentificationTuple, + EquivocationOffence, >, - // The longevity (in blocks) that the equivocation report is valid for. When using the staking - // pallet this should be the bonding duration. + P: KeyOwnerProofSystem<(KeyTypeId, AuthorityId), Proof = T::KeyOwnerProof>, + P::IdentificationTuple: Clone, L: Get, { - type ReportLongevity = L; + type Longevity = L; - fn report_offence( - reporters: Vec, - offence: BabeEquivocationOffence, - ) -> Result<(), OffenceError> { - R::report_offence(reporters, offence) - } - - fn is_known_offence(offenders: &[T::KeyOwnerIdentification], time_slot: &Slot) -> bool { - R::is_known_offence(offenders, time_slot) - } - - fn submit_unsigned_equivocation_report( - equivocation_proof: EquivocationProof, - key_owner_proof: T::KeyOwnerProof, - ) -> DispatchResult { + fn publish_evidence( + evidence: (EquivocationProof, T::KeyOwnerProof), + ) -> Result<(), ()> { use frame_system::offchain::SubmitTransaction; + let (equivocation_proof, key_owner_proof) = evidence; let call = Call::report_equivocation_unsigned { equivocation_proof: Box::new(equivocation_proof), key_owner_proof, }; - - match SubmitTransaction::>::submit_unsigned_transaction(call.into()) { - Ok(()) => log::info!(target: LOG_TARGET, "Submitted BABE equivocation report.",), - Err(e) => - log::error!(target: LOG_TARGET, "Error submitting equivocation report: {:?}", e,), + let res = SubmitTransaction::>::submit_unsigned_transaction(call.into()); + match res { + Ok(()) => info!(target: LOG_TARGET, "Submitted equivocation report."), + Err(e) => error!(target: LOG_TARGET, "Error submitting equivocation report: {:?}", e), } + res + } - Ok(()) + fn check_evidence( + evidence: (EquivocationProof, T::KeyOwnerProof), + ) -> Result<(), TransactionValidityError> { + let (equivocation_proof, key_owner_proof) = evidence; + + // Check the membership proof to extract the offender's id + let key = (sp_consensus_babe::KEY_TYPE, equivocation_proof.offender.clone()); + let offender = + P::check_proof(key, key_owner_proof.clone()).ok_or(InvalidTransaction::BadProof)?; + + // Check if the offence has already been reported, and if so then we can discard the report. + if R::is_known_offence(&[offender], &equivocation_proof.slot) { + Err(InvalidTransaction::Stale.into()) + } else { + Ok(()) + } } - fn block_author() -> Option { - >::author() + fn process_evidence( + reporter: Option, + evidence: (EquivocationProof, T::KeyOwnerProof), + ) -> Result<(), DispatchError> { + let (equivocation_proof, key_owner_proof) = evidence; + let reporter = reporter.or_else(|| >::author()); + let offender = equivocation_proof.offender.clone(); + let slot = equivocation_proof.slot; + + // Validate the equivocation proof (check votes are different and signatures are valid) + if !sp_consensus_babe::check_equivocation_proof(equivocation_proof) { + return Err(Error::::InvalidEquivocationProof.into()) + } + + let validator_set_count = key_owner_proof.validator_count(); + let session_index = key_owner_proof.session(); + + let epoch_index = + *slot.saturating_sub(crate::GenesisSlot::::get()) / T::EpochDuration::get(); + + // Check that the slot number is consistent with the session index + // in the key ownership proof (i.e. slot is for that epoch) + if Pallet::::session_index_for_epoch(epoch_index) != session_index { + return Err(Error::::InvalidKeyOwnershipProof.into()) + } + + // Check the membership proof and extract the offender's id + let offender = P::check_proof((KEY_TYPE, offender), key_owner_proof) + .ok_or(Error::::InvalidKeyOwnershipProof)?; + + let offence = EquivocationOffence { slot, validator_set_count, offender, session_index }; + + R::report_offence(reporter.into_iter().collect(), offence) + .map_err(|_| Error::::DuplicateOffenceReport)?; + + Ok(()) } } @@ -194,11 +214,12 @@ impl Pallet { }, } - // check report staleness - is_known_offence::(equivocation_proof, key_owner_proof)?; + // Check report validity + let evidence = (*equivocation_proof.clone(), key_owner_proof.clone()); + T::EquivocationReportSystem::check_evidence(evidence)?; let longevity = - >::ReportLongevity::get(); + >::Longevity::get(); ValidTransaction::with_tag_prefix("BabeEquivocation") // We assign the maximum priority for any equivocation report. @@ -216,72 +237,10 @@ impl Pallet { pub fn pre_dispatch(call: &Call) -> Result<(), TransactionValidityError> { if let Call::report_equivocation_unsigned { equivocation_proof, key_owner_proof } = call { - is_known_offence::(equivocation_proof, key_owner_proof) + let evidence = (*equivocation_proof.clone(), key_owner_proof.clone()); + T::EquivocationReportSystem::check_evidence(evidence) } else { Err(InvalidTransaction::Call.into()) } } } - -fn is_known_offence( - equivocation_proof: &EquivocationProof, - key_owner_proof: &T::KeyOwnerProof, -) -> Result<(), TransactionValidityError> { - // check the membership proof to extract the offender's id - let key = (sp_consensus_babe::KEY_TYPE, equivocation_proof.offender.clone()); - - let offender = T::KeyOwnerProofSystem::check_proof(key, key_owner_proof.clone()) - .ok_or(InvalidTransaction::BadProof)?; - - // check if the offence has already been reported, - // and if so then we can discard the report. - if T::HandleEquivocation::is_known_offence(&[offender], &equivocation_proof.slot) { - Err(InvalidTransaction::Stale.into()) - } else { - Ok(()) - } -} - -/// A BABE equivocation offence report. -/// -/// When a validator released two or more blocks at the same slot. -pub struct BabeEquivocationOffence { - /// A babe slot in which this incident happened. - pub slot: Slot, - /// The session index in which the incident happened. - pub session_index: SessionIndex, - /// The size of the validator set at the time of the offence. - pub validator_set_count: u32, - /// The authority that produced the equivocation. - pub offender: FullIdentification, -} - -impl Offence - for BabeEquivocationOffence -{ - const ID: Kind = *b"babe:equivocatio"; - type TimeSlot = Slot; - - fn offenders(&self) -> Vec { - vec![self.offender.clone()] - } - - fn session_index(&self) -> SessionIndex { - self.session_index - } - - fn validator_set_count(&self) -> u32 { - self.validator_set_count - } - - fn time_slot(&self) -> Self::TimeSlot { - self.slot - } - - fn slash_fraction(&self, offenders_count: u32) -> Perbill { - // the formula is min((3k / n)^2, 1) - let x = Perbill::from_rational(3 * offenders_count, self.validator_set_count); - // _ ^ 2 - x.square() - } -} diff --git a/frame/babe/src/lib.rs b/frame/babe/src/lib.rs index bb07037ee..903f6249e 100644 --- a/frame/babe/src/lib.rs +++ b/frame/babe/src/lib.rs @@ -25,29 +25,25 @@ use codec::{Decode, Encode}; use frame_support::{ dispatch::{DispatchResultWithPostInfo, Pays}, ensure, - traits::{ - ConstU32, DisabledValidators, FindAuthor, Get, KeyOwnerProofSystem, OnTimestampSet, - OneSessionHandler, - }, + traits::{ConstU32, DisabledValidators, FindAuthor, Get, OnTimestampSet, OneSessionHandler}, weights::Weight, BoundedVec, WeakBoundedVec, }; use sp_application_crypto::ByteArray; -use sp_runtime::{ - generic::DigestItem, - traits::{IsMember, One, SaturatedConversion, Saturating, Zero}, - ConsensusEngineId, KeyTypeId, Permill, -}; -use sp_session::{GetSessionNumber, GetValidatorCount}; -use sp_staking::SessionIndex; -use sp_std::prelude::*; - use sp_consensus_babe::{ digests::{NextConfigDescriptor, NextEpochDescriptor, PreDigest}, AllowedSlots, BabeAuthorityWeight, BabeEpochConfiguration, ConsensusLog, Epoch, EquivocationProof, Slot, BABE_ENGINE_ID, }; use sp_consensus_vrf::schnorrkel; +use sp_runtime::{ + generic::DigestItem, + traits::{IsMember, One, SaturatedConversion, Saturating, Zero}, + ConsensusEngineId, Permill, +}; +use sp_session::{GetSessionNumber, GetValidatorCount}; +use sp_staking::{offence::OffenceReportSystem, SessionIndex}; +use sp_std::prelude::*; pub use sp_consensus_babe::{AuthorityId, PUBLIC_KEY_LENGTH, RANDOMNESS_LENGTH, VRF_OUTPUT_LENGTH}; @@ -64,7 +60,7 @@ mod mock; #[cfg(all(feature = "std", test))] mod tests; -pub use equivocation::{BabeEquivocationOffence, EquivocationHandler, HandleEquivocation}; +pub use equivocation::{EquivocationOffence, EquivocationReportSystem}; #[allow(deprecated)] pub use randomness::CurrentBlockRandomness; pub use randomness::{ @@ -150,35 +146,25 @@ pub mod pallet { /// initialization. type DisabledValidators: DisabledValidators; + /// Helper for weights computations + type WeightInfo: WeightInfo; + + /// Max number of authorities allowed + #[pallet::constant] + type MaxAuthorities: Get; + /// The proof of key ownership, used for validating equivocation reports. /// The proof must include the session index and validator count of the /// session at which the equivocation occurred. type KeyOwnerProof: Parameter + GetSessionNumber + GetValidatorCount; - /// The identification of a key owner, used when reporting equivocations. - type KeyOwnerIdentification: Parameter; - - /// A system for proving ownership of keys, i.e. that a given key was part - /// of a validator set, needed for validating equivocation reports. - type KeyOwnerProofSystem: KeyOwnerProofSystem< - (KeyTypeId, AuthorityId), - Proof = Self::KeyOwnerProof, - IdentificationTuple = Self::KeyOwnerIdentification, + /// The equivocation handling subsystem, defines methods to check/report an + /// offence and for submitting a transaction to report an equivocation + /// (from an offchain context). + type EquivocationReportSystem: OffenceReportSystem< + Option, + (EquivocationProof, Self::KeyOwnerProof), >; - - /// The equivocation handling subsystem, defines methods to report an - /// offence (after the equivocation has been validated) and for submitting a - /// transaction to report an equivocation (from an offchain context). - /// NOTE: when enabling equivocation handling (i.e. this type isn't set to - /// `()`) you must use this pallet's `ValidateUnsigned` in the runtime - /// definition. - type HandleEquivocation: HandleEquivocation; - - type WeightInfo: WeightInfo; - - /// Max number of authorities allowed - #[pallet::constant] - type MaxAuthorities: Get; } #[pallet::error] @@ -429,8 +415,12 @@ pub mod pallet { key_owner_proof: T::KeyOwnerProof, ) -> DispatchResultWithPostInfo { let reporter = ensure_signed(origin)?; - - Self::do_report_equivocation(Some(reporter), *equivocation_proof, key_owner_proof) + T::EquivocationReportSystem::process_evidence( + Some(reporter), + (*equivocation_proof, key_owner_proof), + )?; + // Waive the fee since the report is valid and beneficial + Ok(Pays::No.into()) } /// Report authority equivocation/misbehavior. This method will verify @@ -451,12 +441,11 @@ pub mod pallet { key_owner_proof: T::KeyOwnerProof, ) -> DispatchResultWithPostInfo { ensure_none(origin)?; - - Self::do_report_equivocation( - T::HandleEquivocation::block_author(), - *equivocation_proof, - key_owner_proof, - ) + T::EquivocationReportSystem::process_evidence( + None, + (*equivocation_proof, key_owner_proof), + )?; + Ok(Pays::No.into()) } /// Plan an epoch config change. The epoch config change is recorded and will be enacted on @@ -866,7 +855,7 @@ impl Pallet { /// This function is only well defined for epochs that actually existed, /// e.g. if we skipped from epoch 10 to 20 then a call for epoch 15 (which /// didn't exist) will return an incorrect session index. - fn session_index_for_epoch(epoch_index: u64) -> SessionIndex { + pub(crate) fn session_index_for_epoch(epoch_index: u64) -> SessionIndex { let skipped_epochs = SkippedEpochs::::get(); match skipped_epochs.binary_search_by_key(&epoch_index, |(epoch_index, _)| *epoch_index) { // we have an exact match so we just return the given session index @@ -890,50 +879,6 @@ impl Pallet { } } - fn do_report_equivocation( - reporter: Option, - equivocation_proof: EquivocationProof, - key_owner_proof: T::KeyOwnerProof, - ) -> DispatchResultWithPostInfo { - let offender = equivocation_proof.offender.clone(); - let slot = equivocation_proof.slot; - - // validate the equivocation proof - if !sp_consensus_babe::check_equivocation_proof(equivocation_proof) { - return Err(Error::::InvalidEquivocationProof.into()) - } - - let validator_set_count = key_owner_proof.validator_count(); - let session_index = key_owner_proof.session(); - - let epoch_index = *slot.saturating_sub(GenesisSlot::::get()) / T::EpochDuration::get(); - - // check that the slot number is consistent with the session index - // in the key ownership proof (i.e. slot is for that epoch) - if Self::session_index_for_epoch(epoch_index) != session_index { - return Err(Error::::InvalidKeyOwnershipProof.into()) - } - - // check the membership proof and extract the offender's id - let key = (sp_consensus_babe::KEY_TYPE, offender); - let offender = T::KeyOwnerProofSystem::check_proof(key, key_owner_proof) - .ok_or(Error::::InvalidKeyOwnershipProof)?; - - let offence = - BabeEquivocationOffence { slot, validator_set_count, offender, session_index }; - - let reporters = match reporter { - Some(id) => vec![id], - None => vec![], - }; - - T::HandleEquivocation::report_offence(reporters, offence) - .map_err(|_| Error::::DuplicateOffenceReport)?; - - // waive the fee since the report is valid and beneficial - Ok(Pays::No.into()) - } - /// Submits an extrinsic to report an equivocation. This method will create /// an unsigned extrinsic with a call to `report_equivocation_unsigned` and /// will push the transaction to the pool. Only useful in an offchain @@ -942,11 +887,7 @@ impl Pallet { equivocation_proof: EquivocationProof, key_owner_proof: T::KeyOwnerProof, ) -> Option<()> { - T::HandleEquivocation::submit_unsigned_equivocation_report( - equivocation_proof, - key_owner_proof, - ) - .ok() + T::EquivocationReportSystem::publish_evidence((equivocation_proof, key_owner_proof)).ok() } } diff --git a/frame/babe/src/mock.rs b/frame/babe/src/mock.rs index b13067789..9b832bfff 100644 --- a/frame/babe/src/mock.rs +++ b/frame/babe/src/mock.rs @@ -223,22 +223,11 @@ impl Config for Test { type ExpectedBlockTime = ConstU64<1>; type EpochChangeTrigger = crate::ExternalTrigger; type DisabledValidators = Session; - - type KeyOwnerProofSystem = Historical; - - type KeyOwnerProof = - >::Proof; - - type KeyOwnerIdentification = >::IdentificationTuple; - - type HandleEquivocation = - super::EquivocationHandler; - type WeightInfo = (); type MaxAuthorities = ConstU32<10>; + type KeyOwnerProof = >::Proof; + type EquivocationReportSystem = + super::EquivocationReportSystem; } pub fn go_to_block(n: u64, s: u64) { diff --git a/frame/babe/src/tests.rs b/frame/babe/src/tests.rs index 68426d0a8..8b63f41b3 100644 --- a/frame/babe/src/tests.rs +++ b/frame/babe/src/tests.rs @@ -21,7 +21,7 @@ use super::{Call, *}; use frame_support::{ assert_err, assert_noop, assert_ok, dispatch::{GetDispatchInfo, Pays}, - traits::{Currency, EstimateNextSessionRotation, OnFinalize}, + traits::{Currency, EstimateNextSessionRotation, KeyOwnerProofSystem, OnFinalize}, }; use mock::*; use pallet_session::ShouldEndSession; diff --git a/frame/grandpa/src/equivocation.rs b/frame/grandpa/src/equivocation.rs index 7d8eb6774..6fd12bbdf 100644 --- a/frame/grandpa/src/equivocation.rs +++ b/frame/grandpa/src/equivocation.rs @@ -35,86 +35,73 @@ //! that the `ValidateUnsigned` for the GRANDPA pallet is used in the runtime //! definition. -use sp_std::prelude::*; - use codec::{self as codec, Decode, Encode}; use frame_support::traits::{Get, KeyOwnerProofSystem}; -use sp_consensus_grandpa::{EquivocationProof, RoundNumber, SetId}; +use log::{error, info}; +use sp_consensus_grandpa::{AuthorityId, EquivocationProof, RoundNumber, SetId, KEY_TYPE}; use sp_runtime::{ transaction_validity::{ InvalidTransaction, TransactionPriority, TransactionSource, TransactionValidity, TransactionValidityError, ValidTransaction, }, - DispatchResult, Perbill, + DispatchError, KeyTypeId, Perbill, }; +use sp_session::{GetSessionNumber, GetValidatorCount}; use sp_staking::{ - offence::{Kind, Offence, OffenceError, ReportOffence}, + offence::{Kind, Offence, OffenceReportSystem, ReportOffence}, SessionIndex, }; +use sp_std::prelude::*; -use super::{Call, Config, Pallet, LOG_TARGET}; - -/// A trait with utility methods for handling equivocation reports in GRANDPA. -/// The offence type is generic, and the trait provides , reporting an offence -/// triggered by a valid equivocation report, and also for creating and -/// submitting equivocation report extrinsics (useful only in offchain context). -pub trait HandleEquivocation { - /// The offence type used for reporting offences on valid equivocation reports. - type Offence: GrandpaOffence; - - /// The longevity, in blocks, that the equivocation report is valid for. When using the staking - /// pallet this should be equal to the bonding duration (in blocks, not eras). - type ReportLongevity: Get; - - /// Report an offence proved by the given reporters. - fn report_offence( - reporters: Vec, - offence: Self::Offence, - ) -> Result<(), OffenceError>; - - /// Returns true if all of the offenders at the given time slot have already been reported. - fn is_known_offence( - offenders: &[T::KeyOwnerIdentification], - time_slot: &>::TimeSlot, - ) -> bool; - - /// Create and dispatch an equivocation report extrinsic. - fn submit_unsigned_equivocation_report( - equivocation_proof: EquivocationProof, - key_owner_proof: T::KeyOwnerProof, - ) -> DispatchResult; - - /// Fetch the current block author id, if defined. - fn block_author() -> Option; +use super::{Call, Config, Error, Pallet, LOG_TARGET}; + +/// A round number and set id which point on the time of an offence. +#[derive(Copy, Clone, PartialOrd, Ord, Eq, PartialEq, Encode, Decode)] +pub struct GrandpaTimeSlot { + // The order of these matters for `derive(Ord)`. + /// Grandpa Set ID. + pub set_id: SetId, + /// Round number. + pub round: RoundNumber, } -impl HandleEquivocation for () { - type Offence = GrandpaEquivocationOffence; - type ReportLongevity = (); +/// A GRANDPA equivocation offence report. +pub struct EquivocationOffence { + /// Time slot at which this incident happened. + pub time_slot: GrandpaTimeSlot, + /// The session index in which the incident happened. + pub session_index: SessionIndex, + /// The size of the validator set at the time of the offence. + pub validator_set_count: u32, + /// The authority which produced this equivocation. + pub offender: Offender, +} - fn report_offence( - _reporters: Vec, - _offence: GrandpaEquivocationOffence, - ) -> Result<(), OffenceError> { - Ok(()) +impl Offence for EquivocationOffence { + const ID: Kind = *b"grandpa:equivoca"; + type TimeSlot = GrandpaTimeSlot; + + fn offenders(&self) -> Vec { + vec![self.offender.clone()] } - fn is_known_offence( - _offenders: &[T::KeyOwnerIdentification], - _time_slot: &GrandpaTimeSlot, - ) -> bool { - true + fn session_index(&self) -> SessionIndex { + self.session_index } - fn submit_unsigned_equivocation_report( - _equivocation_proof: EquivocationProof, - _key_owner_proof: T::KeyOwnerProof, - ) -> DispatchResult { - Ok(()) + fn validator_set_count(&self) -> u32 { + self.validator_set_count + } + + fn time_slot(&self) -> Self::TimeSlot { + self.time_slot } - fn block_author() -> Option { - None + // The formula is min((3k / n)^2, 1) + // where k = offenders_number and n = validators_number + fn slash_fraction(&self, offenders_count: u32) -> Perbill { + // Perbill type domain is [0, 1] by definition + Perbill::from_rational(3 * offenders_count, self.validator_set_count).square() } } @@ -122,75 +109,129 @@ impl HandleEquivocation for () { /// using existing subsystems that are part of frame (type bounds described /// below) and will dispatch to them directly, it's only purpose is to wire all /// subsystems together. -pub struct EquivocationHandler> { - _phantom: sp_std::marker::PhantomData<(I, R, L, O)>, -} - -impl Default for EquivocationHandler { - fn default() -> Self { - Self { _phantom: Default::default() } - } -} - -impl HandleEquivocation for EquivocationHandler +pub struct EquivocationReportSystem(sp_std::marker::PhantomData<(T, R, P, L)>); + +// We use the authorship pallet to fetch the current block author and use +// `offchain::SendTransactionTypes` for unsigned extrinsic creation and +// submission. +impl + OffenceReportSystem< + Option, + (EquivocationProof, T::KeyOwnerProof), + > for EquivocationReportSystem where - // We use the authorship pallet to fetch the current block author and use - // `offchain::SendTransactionTypes` for unsigned extrinsic creation and - // submission. T: Config + pallet_authorship::Config + frame_system::offchain::SendTransactionTypes>, - // A system for reporting offences after valid equivocation reports are - // processed. - R: ReportOffence, - // The longevity (in blocks) that the equivocation report is valid for. When using the staking - // pallet this should be the bonding duration. + R: ReportOffence< + T::AccountId, + P::IdentificationTuple, + EquivocationOffence, + >, + P: KeyOwnerProofSystem<(KeyTypeId, AuthorityId), Proof = T::KeyOwnerProof>, + P::IdentificationTuple: Clone, L: Get, - // The offence type that should be used when reporting. - O: GrandpaOffence, { - type Offence = O; - type ReportLongevity = L; - - fn report_offence(reporters: Vec, offence: O) -> Result<(), OffenceError> { - R::report_offence(reporters, offence) - } + type Longevity = L; - fn is_known_offence(offenders: &[T::KeyOwnerIdentification], time_slot: &O::TimeSlot) -> bool { - R::is_known_offence(offenders, time_slot) - } - - fn submit_unsigned_equivocation_report( - equivocation_proof: EquivocationProof, - key_owner_proof: T::KeyOwnerProof, - ) -> DispatchResult { + fn publish_evidence( + evidence: (EquivocationProof, T::KeyOwnerProof), + ) -> Result<(), ()> { use frame_system::offchain::SubmitTransaction; + let (equivocation_proof, key_owner_proof) = evidence; let call = Call::report_equivocation_unsigned { equivocation_proof: Box::new(equivocation_proof), key_owner_proof, }; - - match SubmitTransaction::>::submit_unsigned_transaction(call.into()) { - Ok(()) => log::info!(target: LOG_TARGET, "Submitted GRANDPA equivocation report.",), - Err(e) => - log::error!(target: LOG_TARGET, "Error submitting equivocation report: {:?}", e,), + let res = SubmitTransaction::>::submit_unsigned_transaction(call.into()); + match res { + Ok(()) => info!(target: LOG_TARGET, "Submitted equivocation report."), + Err(e) => error!(target: LOG_TARGET, "Error submitting equivocation report: {:?}", e), } - - Ok(()) + res } - fn block_author() -> Option { - >::author() + fn check_evidence( + evidence: (EquivocationProof, T::KeyOwnerProof), + ) -> Result<(), TransactionValidityError> { + let (equivocation_proof, key_owner_proof) = evidence; + + // Check the membership proof to extract the offender's id + let key = (KEY_TYPE, equivocation_proof.offender().clone()); + let offender = P::check_proof(key, key_owner_proof).ok_or(InvalidTransaction::BadProof)?; + + // Check if the offence has already been reported, and if so then we can discard the report. + let time_slot = GrandpaTimeSlot { + set_id: equivocation_proof.set_id(), + round: equivocation_proof.round(), + }; + if R::is_known_offence(&[offender], &time_slot) { + Err(InvalidTransaction::Stale.into()) + } else { + Ok(()) + } } -} -/// A round number and set id which point on the time of an offence. -#[derive(Copy, Clone, PartialOrd, Ord, Eq, PartialEq, Encode, Decode)] -pub struct GrandpaTimeSlot { - // The order of these matters for `derive(Ord)`. - /// Grandpa Set ID. - pub set_id: SetId, - /// Round number. - pub round: RoundNumber, + fn process_evidence( + reporter: Option, + evidence: (EquivocationProof, T::KeyOwnerProof), + ) -> Result<(), DispatchError> { + let (equivocation_proof, key_owner_proof) = evidence; + let reporter = reporter.or_else(|| >::author()); + let offender = equivocation_proof.offender().clone(); + + // We check the equivocation within the context of its set id (and + // associated session) and round. We also need to know the validator + // set count when the offence since it is required to calculate the + // slash amount. + let set_id = equivocation_proof.set_id(); + let round = equivocation_proof.round(); + let session_index = key_owner_proof.session(); + let validator_set_count = key_owner_proof.validator_count(); + + // Validate equivocation proof (check votes are different and signatures are valid). + if !sp_consensus_grandpa::check_equivocation_proof(equivocation_proof) { + return Err(Error::::InvalidEquivocationProof.into()) + } + + // Validate the key ownership proof extracting the id of the offender. + let offender = P::check_proof((KEY_TYPE, offender), key_owner_proof) + .ok_or(Error::::InvalidKeyOwnershipProof)?; + + // Fetch the current and previous sets last session index. + // For genesis set there's no previous set. + let previous_set_id_session_index = if set_id != 0 { + let idx = crate::SetIdSession::::get(set_id - 1) + .ok_or(Error::::InvalidEquivocationProof)?; + Some(idx) + } else { + None + }; + + let set_id_session_index = + crate::SetIdSession::::get(set_id).ok_or(Error::::InvalidEquivocationProof)?; + + // Check that the session id for the membership proof is within the + // bounds of the set id reported in the equivocation. + if session_index > set_id_session_index || + previous_set_id_session_index + .map(|previous_index| session_index <= previous_index) + .unwrap_or(false) + { + return Err(Error::::InvalidEquivocationProof.into()) + } + + let offence = EquivocationOffence { + time_slot: GrandpaTimeSlot { set_id, round }, + session_index, + offender, + validator_set_count, + }; + + R::report_offence(reporter.into_iter().collect(), offence) + .map_err(|_| Error::::DuplicateOffenceReport)?; + + Ok(()) + } } /// Methods for the `ValidateUnsigned` implementation: @@ -213,11 +254,12 @@ impl Pallet { }, } - // check report staleness - is_known_offence::(equivocation_proof, key_owner_proof)?; + // Check report validity + let evidence = (*equivocation_proof.clone(), key_owner_proof.clone()); + T::EquivocationReportSystem::check_evidence(evidence)?; let longevity = - >::ReportLongevity::get(); + >::Longevity::get(); ValidTransaction::with_tag_prefix("GrandpaEquivocation") // We assign the maximum priority for any equivocation report. @@ -239,118 +281,10 @@ impl Pallet { pub fn pre_dispatch(call: &Call) -> Result<(), TransactionValidityError> { if let Call::report_equivocation_unsigned { equivocation_proof, key_owner_proof } = call { - is_known_offence::(equivocation_proof, key_owner_proof) + let evidence = (*equivocation_proof.clone(), key_owner_proof.clone()); + T::EquivocationReportSystem::check_evidence(evidence) } else { Err(InvalidTransaction::Call.into()) } } } - -fn is_known_offence( - equivocation_proof: &EquivocationProof, - key_owner_proof: &T::KeyOwnerProof, -) -> Result<(), TransactionValidityError> { - // check the membership proof to extract the offender's id - let key = (sp_consensus_grandpa::KEY_TYPE, equivocation_proof.offender().clone()); - - let offender = T::KeyOwnerProofSystem::check_proof(key, key_owner_proof.clone()) - .ok_or(InvalidTransaction::BadProof)?; - - // check if the offence has already been reported, - // and if so then we can discard the report. - let time_slot = >::Offence::new_time_slot( - equivocation_proof.set_id(), - equivocation_proof.round(), - ); - - let is_known_offence = T::HandleEquivocation::is_known_offence(&[offender], &time_slot); - - if is_known_offence { - Err(InvalidTransaction::Stale.into()) - } else { - Ok(()) - } -} - -/// A grandpa equivocation offence report. -#[allow(dead_code)] -pub struct GrandpaEquivocationOffence { - /// Time slot at which this incident happened. - pub time_slot: GrandpaTimeSlot, - /// The session index in which the incident happened. - pub session_index: SessionIndex, - /// The size of the validator set at the time of the offence. - pub validator_set_count: u32, - /// The authority which produced this equivocation. - pub offender: FullIdentification, -} - -/// An interface for types that will be used as GRANDPA offences and must also -/// implement the `Offence` trait. This trait provides a constructor that is -/// provided all available data during processing of GRANDPA equivocations. -pub trait GrandpaOffence: Offence { - /// Create a new GRANDPA offence using the given equivocation details. - fn new( - session_index: SessionIndex, - validator_set_count: u32, - offender: FullIdentification, - set_id: SetId, - round: RoundNumber, - ) -> Self; - - /// Create a new GRANDPA offence time slot. - fn new_time_slot(set_id: SetId, round: RoundNumber) -> Self::TimeSlot; -} - -impl GrandpaOffence - for GrandpaEquivocationOffence -{ - fn new( - session_index: SessionIndex, - validator_set_count: u32, - offender: FullIdentification, - set_id: SetId, - round: RoundNumber, - ) -> Self { - GrandpaEquivocationOffence { - session_index, - validator_set_count, - offender, - time_slot: GrandpaTimeSlot { set_id, round }, - } - } - - fn new_time_slot(set_id: SetId, round: RoundNumber) -> Self::TimeSlot { - GrandpaTimeSlot { set_id, round } - } -} - -impl Offence - for GrandpaEquivocationOffence -{ - const ID: Kind = *b"grandpa:equivoca"; - type TimeSlot = GrandpaTimeSlot; - - fn offenders(&self) -> Vec { - vec![self.offender.clone()] - } - - fn session_index(&self) -> SessionIndex { - self.session_index - } - - fn validator_set_count(&self) -> u32 { - self.validator_set_count - } - - fn time_slot(&self) -> Self::TimeSlot { - self.time_slot - } - - fn slash_fraction(&self, offenders_count: u32) -> Perbill { - // the formula is min((3k / n)^2, 1) - let x = Perbill::from_rational(3 * offenders_count, self.validator_set_count); - // _ ^ 2 - x.square() - } -} diff --git a/frame/grandpa/src/lib.rs b/frame/grandpa/src/lib.rs index 85d116ee7..2106860ea 100644 --- a/frame/grandpa/src/lib.rs +++ b/frame/grandpa/src/lib.rs @@ -28,29 +28,29 @@ #![cfg_attr(not(feature = "std"), no_std)] -// re-export since this is necessary for `impl_apis` in runtime. -pub use sp_consensus_grandpa as fg_primitives; - -use sp_std::prelude::*; +// Re-export since this is necessary for `impl_apis` in runtime. +pub use sp_consensus_grandpa::{ + self as fg_primitives, AuthorityId, AuthorityList, AuthorityWeight, VersionedAuthorityList, +}; use codec::{self as codec, Decode, Encode, MaxEncodedLen}; -pub use fg_primitives::{AuthorityId, AuthorityList, AuthorityWeight, VersionedAuthorityList}; -use fg_primitives::{ - ConsensusLog, EquivocationProof, ScheduledChange, SetId, GRANDPA_AUTHORITIES_KEY, - GRANDPA_ENGINE_ID, RUNTIME_LOG_TARGET as LOG_TARGET, -}; use frame_support::{ dispatch::{DispatchResultWithPostInfo, Pays}, pallet_prelude::Get, storage, - traits::{KeyOwnerProofSystem, OneSessionHandler}, + traits::OneSessionHandler, weights::Weight, WeakBoundedVec, }; use scale_info::TypeInfo; -use sp_runtime::{generic::DigestItem, traits::Zero, DispatchResult, KeyTypeId}; +use sp_consensus_grandpa::{ + ConsensusLog, EquivocationProof, ScheduledChange, SetId, GRANDPA_AUTHORITIES_KEY, + GRANDPA_ENGINE_ID, RUNTIME_LOG_TARGET as LOG_TARGET, +}; +use sp_runtime::{generic::DigestItem, traits::Zero, DispatchResult}; use sp_session::{GetSessionNumber, GetValidatorCount}; -use sp_staking::SessionIndex; +use sp_staking::{offence::OffenceReportSystem, SessionIndex}; +use sp_std::prelude::*; mod default_weights; mod equivocation; @@ -63,10 +63,7 @@ mod mock; #[cfg(all(feature = "std", test))] mod tests; -pub use equivocation::{ - EquivocationHandler, GrandpaEquivocationOffence, GrandpaOffence, GrandpaTimeSlot, - HandleEquivocation, -}; +pub use equivocation::{EquivocationOffence, EquivocationReportSystem, GrandpaTimeSlot}; pub use pallet::*; @@ -91,30 +88,6 @@ pub mod pallet { + Into<::RuntimeEvent> + IsType<::RuntimeEvent>; - /// The proof of key ownership, used for validating equivocation reports - /// The proof must include the session index and validator count of the - /// session at which the equivocation occurred. - type KeyOwnerProof: Parameter + GetSessionNumber + GetValidatorCount; - - /// The identification of a key owner, used when reporting equivocations. - type KeyOwnerIdentification: Parameter; - - /// A system for proving ownership of keys, i.e. that a given key was part - /// of a validator set, needed for validating equivocation reports. - type KeyOwnerProofSystem: KeyOwnerProofSystem< - (KeyTypeId, AuthorityId), - Proof = Self::KeyOwnerProof, - IdentificationTuple = Self::KeyOwnerIdentification, - >; - - /// The equivocation handling subsystem, defines methods to report an - /// offence (after the equivocation has been validated) and for submitting a - /// transaction to report an equivocation (from an offchain context). - /// NOTE: when enabling equivocation handling (i.e. this type isn't set to - /// `()`) you must use this pallet's `ValidateUnsigned` in the runtime - /// definition. - type HandleEquivocation: HandleEquivocation; - /// Weights for this pallet. type WeightInfo: WeightInfo; @@ -130,6 +103,19 @@ pub mod pallet { /// can be zero. #[pallet::constant] type MaxSetIdSessionEntries: Get; + + /// The proof of key ownership, used for validating equivocation reports + /// The proof include the session index and validator count of the + /// session at which the equivocation occurred. + type KeyOwnerProof: Parameter + GetSessionNumber + GetValidatorCount; + + /// The equivocation handling subsystem, defines methods to check/report an + /// offence and for submitting a transaction to report an equivocation + /// (from an offchain context). + type EquivocationReportSystem: OffenceReportSystem< + Option, + (EquivocationProof, Self::KeyOwnerProof), + >; } #[pallet::hooks] @@ -211,7 +197,12 @@ pub mod pallet { ) -> DispatchResultWithPostInfo { let reporter = ensure_signed(origin)?; - Self::do_report_equivocation(Some(reporter), *equivocation_proof, key_owner_proof) + T::EquivocationReportSystem::process_evidence( + Some(reporter), + (*equivocation_proof, key_owner_proof), + )?; + // Waive the fee since the report is valid and beneficial + Ok(Pays::No.into()) } /// Report voter equivocation/misbehavior. This method will verify the @@ -232,11 +223,11 @@ pub mod pallet { ) -> DispatchResultWithPostInfo { ensure_none(origin)?; - Self::do_report_equivocation( - T::HandleEquivocation::block_author(), - *equivocation_proof, - key_owner_proof, - ) + T::EquivocationReportSystem::process_evidence( + None, + (*equivocation_proof, key_owner_proof), + )?; + Ok(Pays::No.into()) } /// Note that the current authority set of the GRANDPA finality gadget has stalled. @@ -352,7 +343,7 @@ pub mod pallet { #[pallet::genesis_build] impl GenesisBuild for GenesisConfig { fn build(&self) { - CurrentSetId::::put(fg_primitives::SetId::default()); + CurrentSetId::::put(SetId::default()); Pallet::::initialize(&self.authorities) } } @@ -532,74 +523,6 @@ impl Pallet { SetIdSession::::insert(0, 0); } - fn do_report_equivocation( - reporter: Option, - equivocation_proof: EquivocationProof, - key_owner_proof: T::KeyOwnerProof, - ) -> DispatchResultWithPostInfo { - // we check the equivocation within the context of its set id (and - // associated session) and round. we also need to know the validator - // set count when the offence since it is required to calculate the - // slash amount. - let set_id = equivocation_proof.set_id(); - let round = equivocation_proof.round(); - let session_index = key_owner_proof.session(); - let validator_count = key_owner_proof.validator_count(); - - // validate the key ownership proof extracting the id of the offender. - let offender = T::KeyOwnerProofSystem::check_proof( - (fg_primitives::KEY_TYPE, equivocation_proof.offender().clone()), - key_owner_proof, - ) - .ok_or(Error::::InvalidKeyOwnershipProof)?; - - // validate equivocation proof (check votes are different and - // signatures are valid). - if !sp_consensus_grandpa::check_equivocation_proof(equivocation_proof) { - return Err(Error::::InvalidEquivocationProof.into()) - } - - // fetch the current and previous sets last session index. on the - // genesis set there's no previous set. - let previous_set_id_session_index = if set_id == 0 { - None - } else { - let session_index = - Self::session_for_set(set_id - 1).ok_or(Error::::InvalidEquivocationProof)?; - - Some(session_index) - }; - - let set_id_session_index = - Self::session_for_set(set_id).ok_or(Error::::InvalidEquivocationProof)?; - - // check that the session id for the membership proof is within the - // bounds of the set id reported in the equivocation. - if session_index > set_id_session_index || - previous_set_id_session_index - .map(|previous_index| session_index <= previous_index) - .unwrap_or(false) - { - return Err(Error::::InvalidEquivocationProof.into()) - } - - // report to the offences module rewarding the sender. - T::HandleEquivocation::report_offence( - reporter.into_iter().collect(), - >::Offence::new( - session_index, - validator_count, - offender, - set_id, - round, - ), - ) - .map_err(|_| Error::::DuplicateOffenceReport)?; - - // waive the fee since the report is valid and beneficial - Ok(Pays::No.into()) - } - /// Submits an extrinsic to report an equivocation. This method will create /// an unsigned extrinsic with a call to `report_equivocation_unsigned` and /// will push the transaction to the pool. Only useful in an offchain @@ -608,11 +531,7 @@ impl Pallet { equivocation_proof: EquivocationProof, key_owner_proof: T::KeyOwnerProof, ) -> Option<()> { - T::HandleEquivocation::submit_unsigned_equivocation_report( - equivocation_proof, - key_owner_proof, - ) - .ok() + T::EquivocationReportSystem::publish_evidence((equivocation_proof, key_owner_proof)).ok() } fn on_stalled(further_wait: T::BlockNumber, median: T::BlockNumber) { diff --git a/frame/grandpa/src/mock.rs b/frame/grandpa/src/mock.rs index a33fbb22d..22ca9c624 100644 --- a/frame/grandpa/src/mock.rs +++ b/frame/grandpa/src/mock.rs @@ -225,22 +225,14 @@ parameter_types! { impl Config for Test { type RuntimeEvent = RuntimeEvent; - type KeyOwnerProofSystem = Historical; - - type KeyOwnerProof = - >::Proof; - - type KeyOwnerIdentification = >::IdentificationTuple; - - type HandleEquivocation = - super::EquivocationHandler; - type WeightInfo = (); type MaxAuthorities = ConstU32<100>; type MaxSetIdSessionEntries = MaxSetIdSessionEntries; + + type KeyOwnerProof = >::Proof; + + type EquivocationReportSystem = + super::EquivocationReportSystem; } pub fn grandpa_log(log: ConsensusLog) -> DigestItem { diff --git a/frame/grandpa/src/tests.rs b/frame/grandpa/src/tests.rs index 0bf3c8ddf..a956ca4bc 100644 --- a/frame/grandpa/src/tests.rs +++ b/frame/grandpa/src/tests.rs @@ -21,12 +21,11 @@ use super::{Call, Event, *}; use crate::mock::*; -use codec::Encode; use fg_primitives::ScheduledChange; use frame_support::{ assert_err, assert_noop, assert_ok, dispatch::{GetDispatchInfo, Pays}, - traits::{Currency, OnFinalize, OneSessionHandler}, + traits::{Currency, KeyOwnerProofSystem, OnFinalize, OneSessionHandler}, }; use frame_system::{EventRecord, Phase}; use sp_core::H256; diff --git a/frame/offences/benchmarking/src/lib.rs b/frame/offences/benchmarking/src/lib.rs index 16d77335e..3db937bb3 100644 --- a/frame/offences/benchmarking/src/lib.rs +++ b/frame/offences/benchmarking/src/lib.rs @@ -36,9 +36,9 @@ use sp_runtime::{ }; use sp_staking::offence::{Offence, ReportOffence}; -use pallet_babe::BabeEquivocationOffence; +use pallet_babe::EquivocationOffence as BabeEquivocationOffence; use pallet_balances::Config as BalancesConfig; -use pallet_grandpa::{GrandpaEquivocationOffence, GrandpaTimeSlot}; +use pallet_grandpa::{EquivocationOffence as GrandpaEquivocationOffence, GrandpaTimeSlot}; use pallet_im_online::{Config as ImOnlineConfig, Pallet as ImOnline, UnresponsivenessOffence}; use pallet_offences::{Config as OffencesConfig, Pallet as Offences}; use pallet_session::{ diff --git a/frame/offences/src/lib.rs b/frame/offences/src/lib.rs index 6ba16161b..8ac1ca0bd 100644 --- a/frame/offences/src/lib.rs +++ b/frame/offences/src/lib.rs @@ -112,10 +112,10 @@ pub mod pallet { } } -impl> - ReportOffence for Pallet +impl ReportOffence for Pallet where - T::IdentificationTuple: Clone, + T: Config, + O: Offence, { fn report_offence(reporters: Vec, offence: O) -> Result<(), OffenceError> { let offenders = offence.offenders(); diff --git a/frame/offences/src/mock.rs b/frame/offences/src/mock.rs index d0fc7377a..27e1da8c4 100644 --- a/frame/offences/src/mock.rs +++ b/frame/offences/src/mock.rs @@ -134,17 +134,17 @@ pub fn offence_reports(kind: Kind, time_slot: u128) -> Vec { +pub struct Offence { pub validator_set_count: u32, - pub offenders: Vec, + pub offenders: Vec, pub time_slot: u128, } -impl offence::Offence for Offence { +impl offence::Offence for Offence { const ID: offence::Kind = KIND; type TimeSlot = u128; - fn offenders(&self) -> Vec { + fn offenders(&self) -> Vec { self.offenders.clone() } @@ -167,5 +167,5 @@ impl offence::Offence for Offence { /// Create the report id for the given `offender` and `time_slot` combination. pub fn report_id(time_slot: u128, offender: u64) -> H256 { - Offences::report_id::>(&time_slot, &offender) + Offences::report_id::(&time_slot, &offender) } diff --git a/frame/offences/src/tests.rs b/frame/offences/src/tests.rs index a5c18020b..81f0f44f1 100644 --- a/frame/offences/src/tests.rs +++ b/frame/offences/src/tests.rs @@ -160,20 +160,18 @@ fn doesnt_deposit_event_for_dups() { #[test] fn reports_if_an_offence_is_dup() { - type TestOffence = Offence; - new_test_ext().execute_with(|| { let time_slot = 42; assert_eq!(offence_reports(KIND, time_slot), vec![]); let offence = - |time_slot, offenders| TestOffence { validator_set_count: 5, time_slot, offenders }; + |time_slot, offenders| Offence { validator_set_count: 5, time_slot, offenders }; let mut test_offence = offence(time_slot, vec![0]); // the report for authority 0 at time slot 42 should not be a known // offence - assert!(!>::is_known_offence( + assert!(!>::is_known_offence( &test_offence.offenders, &test_offence.time_slot )); @@ -182,7 +180,7 @@ fn reports_if_an_offence_is_dup() { Offences::report_offence(vec![], test_offence.clone()).unwrap(); // the same report should be a known offence now - assert!(>::is_known_offence( + assert!(>::is_known_offence( &test_offence.offenders, &test_offence.time_slot )); @@ -197,7 +195,7 @@ fn reports_if_an_offence_is_dup() { test_offence.offenders.push(1); // it should not be a known offence anymore - assert!(!>::is_known_offence( + assert!(!>::is_known_offence( &test_offence.offenders, &test_offence.time_slot )); @@ -208,7 +206,7 @@ fn reports_if_an_offence_is_dup() { // creating a new offence for the same authorities on the next slot // should be considered a new offence and thefore not known let test_offence_next_slot = offence(time_slot + 1, vec![0, 1]); - assert!(!>::is_known_offence( + assert!(!>::is_known_offence( &test_offence_next_slot.offenders, &test_offence_next_slot.time_slot )); diff --git a/primitives/consensus/grandpa/src/lib.rs b/primitives/consensus/grandpa/src/lib.rs index b466dcf37..26cee07b8 100644 --- a/primitives/consensus/grandpa/src/lib.rs +++ b/primitives/consensus/grandpa/src/lib.rs @@ -240,6 +240,9 @@ pub struct EquivocationProof { equivocation: Equivocation, } +// Don't bother the grandpa crate... +impl Eq for EquivocationProof {} + impl EquivocationProof { /// Create a new `EquivocationProof` for the given set id and using the /// given equivocation as proof. diff --git a/primitives/consensus/slots/src/lib.rs b/primitives/consensus/slots/src/lib.rs index d72378527..1f2fa585d 100644 --- a/primitives/consensus/slots/src/lib.rs +++ b/primitives/consensus/slots/src/lib.rs @@ -130,7 +130,7 @@ impl SlotDuration { /// produces more than one block on the same slot. The proof of equivocation /// are the given distinct headers that were signed by the validator and which /// include the slot number. -#[derive(Clone, Debug, Decode, Encode, PartialEq, TypeInfo)] +#[derive(Clone, Debug, Decode, Encode, PartialEq, TypeInfo, Eq)] pub struct EquivocationProof { /// Returns the authority id of the equivocator. pub offender: Id, diff --git a/primitives/staking/src/offence.rs b/primitives/staking/src/offence.rs index dec975668..6694c9055 100644 --- a/primitives/staking/src/offence.rs +++ b/primitives/staking/src/offence.rs @@ -18,10 +18,10 @@ //! Common traits and types that are useful for describing offences for usage in environments //! that use staking. -use sp_std::vec::Vec; - use codec::{Decode, Encode}; -use sp_runtime::Perbill; +use sp_core::Get; +use sp_runtime::{transaction_validity::TransactionValidityError, DispatchError, Perbill}; +use sp_std::vec::Vec; use crate::SessionIndex; @@ -209,3 +209,68 @@ pub struct OffenceDetails { /// particular reporters. pub reporters: Vec, } + +/// An abstract system to publish, check and process offence evidences. +/// +/// Implementation details are left opaque and we don't assume any specific usage +/// scenario for this trait at this level. The main goal is to group together some +/// common actions required during a typical offence report flow. +/// +/// Even though this trait doesn't assume too much, this is a general guideline +/// for a typical usage scenario: +/// +/// 1. An offence is detected and an evidence is submitted on-chain via the +/// [`OffenceReportSystem::publish_evidence`] method. This will construct +/// and submit an extrinsic transaction containing the offence evidence. +/// +/// 2. If the extrinsic is unsigned then the transaction receiver may want to +/// perform some preliminary checks before further processing. This is a good +/// place to call the [`OffenceReportSystem::check_evidence`] method. +/// +/// 3. Finally the report extrinsic is executed on-chain. This is where the user +/// calls the [`OffenceReportSystem::process_evidence`] to consume the offence +/// report and enact any required action. +pub trait OffenceReportSystem { + /// Longevity, in blocks, for the evidence report validity. + /// + /// For example, when using the staking pallet this should be set equal + /// to the bonding duration in blocks, not eras. + type Longevity: Get; + + /// Publish an offence evidence. + /// + /// Common usage: submit the evidence on-chain via some kind of extrinsic. + fn publish_evidence(evidence: Evidence) -> Result<(), ()>; + + /// Check an offence evidence. + /// + /// Common usage: preliminary validity check before execution + /// (e.g. for unsigned extrinsic quick checks). + fn check_evidence(evidence: Evidence) -> Result<(), TransactionValidityError>; + + /// Process an offence evidence. + /// + /// Common usage: enact some form of slashing directly or by forwarding + /// the evidence to a lower level specialized subsystem (e.g. a handler + /// implementing `ReportOffence` trait). + fn process_evidence(reporter: Reporter, evidence: Evidence) -> Result<(), DispatchError>; +} + +/// Dummy offence report system. +/// +/// Doesn't do anything special and returns `Ok(())` for all the actions. +impl OffenceReportSystem for () { + type Longevity = (); + + fn publish_evidence(_evidence: Evidence) -> Result<(), ()> { + Ok(()) + } + + fn check_evidence(_evidence: Evidence) -> Result<(), TransactionValidityError> { + Ok(()) + } + + fn process_evidence(_reporter: Reporter, _evidence: Evidence) -> Result<(), DispatchError> { + Ok(()) + } +} diff --git a/test-utils/runtime/src/lib.rs b/test-utils/runtime/src/lib.rs index 1ea8b8663..83b4e2977 100644 --- a/test-utils/runtime/src/lib.rs +++ b/test-utils/runtime/src/lib.rs @@ -28,7 +28,7 @@ use scale_info::TypeInfo; use sp_std::{marker::PhantomData, prelude::*}; use sp_application_crypto::{ecdsa, ed25519, sr25519, RuntimeAppPublic}; -use sp_core::{offchain::KeyTypeId, OpaqueMetadata, RuntimeDebug}; +use sp_core::{OpaqueMetadata, RuntimeDebug}; use sp_trie::{ trie_types::{TrieDBBuilder, TrieDBMutBuilderV1}, PrefixedMemoryDB, StorageProof, @@ -39,7 +39,7 @@ use cfg_if::cfg_if; use frame_support::{ dispatch::RawOrigin, parameter_types, - traits::{CallerTrait, ConstU32, ConstU64, CrateVersion, KeyOwnerProofSystem}, + traits::{CallerTrait, ConstU32, ConstU64, CrateVersion}, weights::{RuntimeDbWeight, Weight}, }; use frame_system::limits::{BlockLength, BlockWeights}; @@ -659,21 +659,10 @@ impl pallet_babe::Config for Runtime { // pallet_babe::SameAuthoritiesForever. type EpochChangeTrigger = pallet_babe::ExternalTrigger; type DisabledValidators = (); - - type KeyOwnerProofSystem = (); - - type KeyOwnerProof = - >::Proof; - - type KeyOwnerIdentification = >::IdentificationTuple; - - type HandleEquivocation = (); type WeightInfo = (); - type MaxAuthorities = ConstU32<10>; + type KeyOwnerProof = sp_core::Void; + type EquivocationReportSystem = (); } /// Adds one to the given input and returns the final result. From f59f5019333c44b2513852f7ddf30514430ec78f Mon Sep 17 00:00:00 2001 From: Luke Schoen Date: Wed, 8 Mar 2023 07:57:46 +1100 Subject: [PATCH 210/558] fix: Fixes #13071 change order of ldd command and update ./docker/README.md (#13072) * fixes #13071 * remove warning --- docker/README.md | 7 ++++++- docker/substrate_builder.Dockerfile | 4 ++-- 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/docker/README.md b/docker/README.md index ca3c1bde4..b3b806787 100644 --- a/docker/README.md +++ b/docker/README.md @@ -1,6 +1,6 @@ # Substrate Builder Docker Image -The Docker image in this folder is a `builder` image. It is self contained and allow users to build the binaries themselves. +The Docker image in this folder is a `builder` image. It is self contained and allows users to build the binaries themselves. There is no requirement on having Rust or any other toolchain installed but a working Docker environment. Unlike the `parity/polkadot` image which contains a single binary (`polkadot`!) used by default, the image in this folder builds and contains several binaries and you need to provide the name of the binary to be called. @@ -12,6 +12,11 @@ You should refer to the .Dockerfile for the actual list. At the time of editing, - node-template - chain-spec-builder +To generate the latest parity/substrate image. Please first run: +```sh +./build.sh +``` + The image can be used by passing the selected binary followed by the appropriate tags for this binary. Your best guess to get started is to pass the `--help flag`. Here are a few examples: diff --git a/docker/substrate_builder.Dockerfile b/docker/substrate_builder.Dockerfile index d0812c1a8..03b6b46ca 100644 --- a/docker/substrate_builder.Dockerfile +++ b/docker/substrate_builder.Dockerfile @@ -24,10 +24,10 @@ RUN useradd -m -u 1000 -U -s /bin/sh -d /substrate substrate && \ mkdir -p /data /substrate/.local/share/substrate && \ chown -R substrate:substrate /data && \ ln -s /data /substrate/.local/share/substrate && \ -# unclutter and minimize the attack surface - rm -rf /usr/bin /usr/sbin && \ # Sanity checks ldd /usr/local/bin/substrate && \ +# unclutter and minimize the attack surface + rm -rf /usr/bin /usr/sbin && \ /usr/local/bin/substrate --version USER substrate From 7c3d27927577b7fa9ea659aed3197d1f67dd663e Mon Sep 17 00:00:00 2001 From: Luke Schoen Date: Thu, 9 Mar 2023 00:51:43 +1100 Subject: [PATCH 211/558] [docker] Use `BASH_SOURCE` to find project root, add `.dockerignore` (#13472) --- docker/build.sh | 7 +++---- docker/substrate_builder.Dockerfile.dockerignore | 11 +++++++++++ 2 files changed, 14 insertions(+), 4 deletions(-) create mode 100644 docker/substrate_builder.Dockerfile.dockerignore diff --git a/docker/build.sh b/docker/build.sh index f0a4560ff..d4befd864 100755 --- a/docker/build.sh +++ b/docker/build.sh @@ -3,9 +3,8 @@ set -e pushd . -# The following line ensure we run from the project root -PROJECT_ROOT=`git rev-parse --show-toplevel` -cd $PROJECT_ROOT +# Change to the project root and supports calls from symlinks +cd $(dirname "$(dirname "$(realpath "${BASH_SOURCE[0]}")")") # Find the current version from Cargo.toml VERSION=`grep "^version" ./bin/node/cli/Cargo.toml | egrep -o "([0-9\.]+)"` @@ -14,7 +13,7 @@ GITREPO=substrate # Build the image echo "Building ${GITUSER}/${GITREPO}:latest docker image, hang on!" -time docker build -f ./docker/substrate_builder.Dockerfile -t ${GITUSER}/${GITREPO}:latest . +time DOCKER_BUILDKIT=1 docker build -f ./docker/substrate_builder.Dockerfile -t ${GITUSER}/${GITREPO}:latest . docker tag ${GITUSER}/${GITREPO}:latest ${GITUSER}/${GITREPO}:v${VERSION} # Show the list of available images for this repo diff --git a/docker/substrate_builder.Dockerfile.dockerignore b/docker/substrate_builder.Dockerfile.dockerignore new file mode 100644 index 000000000..dccfeb651 --- /dev/null +++ b/docker/substrate_builder.Dockerfile.dockerignore @@ -0,0 +1,11 @@ +doc +**target* +.idea/ +.git/ +.github/ +Dockerfile +.dockerignore +.local +.env* +HEADER-GPL3 +LICENSE-GPL3 From 77c675b15cd805e5a9d61345763655ad8941591e Mon Sep 17 00:00:00 2001 From: Luke Schoen Date: Thu, 9 Mar 2023 00:57:44 +1100 Subject: [PATCH 212/558] Update Docker instructions (#13437) --- .gitignore | 1 + README.md | 1 + bin/node-template/README.md | 29 +++---------------- bin/node-template/docker-compose.yml | 17 ----------- bin/node-template/scripts/docker_run.sh | 10 ------- docker/README.md | 38 +++++++++++++++++++++---- docker/run.sh | 13 +++++++++ 7 files changed, 51 insertions(+), 58 deletions(-) delete mode 100644 bin/node-template/docker-compose.yml delete mode 100755 bin/node-template/scripts/docker_run.sh create mode 100755 docker/run.sh diff --git a/.gitignore b/.gitignore index efb63520e..f30103c62 100644 --- a/.gitignore +++ b/.gitignore @@ -25,4 +25,5 @@ rls*.log *.bin *.iml scripts/ci/node-template-release/Cargo.lock +bin/node-template/Cargo.lock substrate.code-workspace diff --git a/README.md b/README.md index 7d8c7e575..361f410ab 100644 --- a/README.md +++ b/README.md @@ -9,6 +9,7 @@ Substrate is a next-generation framework for blockchain innovation 🚀. Head to [docs.substrate.io](https://docs.substrate.io) and follow the [installation](https://docs.substrate.io/install/) instructions. Then try out one of the [tutorials](https://docs.substrate.io/tutorials/). +Refer to the [Docker instructions](./docker/README.md) to quickly run Substrate, Substrate Node Template, Subkey, or to build a chain spec. ## Community & Support diff --git a/bin/node-template/README.md b/bin/node-template/README.md index c42b58449..7562b6247 100644 --- a/bin/node-template/README.md +++ b/bin/node-template/README.md @@ -2,10 +2,10 @@ A fresh [Substrate](https://substrate.io/) node, ready for hacking :rocket: -A standalone version of this template is available for each release of Polkadot in the [Substrate Developer Hub Parachain Template](https://github.com/substrate-developer-hub/substrate-parachain-template/) repository. -The parachain template is generated directly at each Polkadot release branch form the [Node Template in Substreate](https://github.com/paritytech/substrate/tree/master/bin/node-template) upstream +A standalone version of this template is available for each release of Polkadot in the [Substrate Developer Hub Parachain Template](https://github.com/substrate-developer-hub/substrate-parachain-template/) repository. +The parachain template is generated directly at each Polkadot release branch from the [Node Template in Substrate](https://github.com/paritytech/substrate/tree/master/bin/node-template) upstream -It is usually best to to use the stand-alone version to start a new project. +It is usually best to use the stand-alone version to start a new project. All bugs, suggestions, and feature requests should be made upstream in the [Substrate](https://github.com/paritytech/substrate/tree/master/bin/node-template) repository. ## Getting Started @@ -158,25 +158,4 @@ To get all the correct dependencies, activate direnv `direnv allow` and lorri `l ### Docker -First, install [Docker](https://docs.docker.com/get-docker/) and [Docker Compose](https://docs.docker.com/compose/install/). - -Then run the following command to start a single node development chain. - -```sh -./scripts/docker_run.sh -``` - -This command compiles the code and starts a local development network. -You can also replace the default command (`cargo build --release && ./target/release/node-template --dev --ws-external`) by appending your own. -For example: - -```sh -# Run Substrate node without re-compiling -./scripts/docker_run.sh ./target/release/node-template --dev --ws-external - -# Purge the local dev chain -./scripts/docker_run.sh ./target/release/node-template purge-chain --dev - -# Check whether the code is compilable -./scripts/docker_run.sh cargo check -``` +Please follow the [Substrate Docker instructions here](https://github.com/paritytech/substrate/blob/master/docker/README.md) to build the Docker container with the Substrate Node Template binary. diff --git a/bin/node-template/docker-compose.yml b/bin/node-template/docker-compose.yml deleted file mode 100644 index bc1922f47..000000000 --- a/bin/node-template/docker-compose.yml +++ /dev/null @@ -1,17 +0,0 @@ -version: "3.2" - -services: - dev: - container_name: node-template - image: paritytech/ci-linux:production - working_dir: /var/www/node-template - ports: - - "9944:9944" - environment: - - CARGO_HOME=/var/www/node-template/.cargo - volumes: - - .:/var/www/node-template - - type: bind - source: ./.local - target: /root/.local - command: bash -c "cargo build --release && ./target/release/node-template --dev --ws-external" diff --git a/bin/node-template/scripts/docker_run.sh b/bin/node-template/scripts/docker_run.sh deleted file mode 100755 index 0bac44b4c..000000000 --- a/bin/node-template/scripts/docker_run.sh +++ /dev/null @@ -1,10 +0,0 @@ -#!/usr/bin/env bash -# This script is meant to be run on Unix/Linux based systems -set -e - -echo "*** Start Substrate node template ***" - -cd $(dirname ${BASH_SOURCE[0]})/.. - -docker-compose down --remove-orphans -docker-compose run --rm --service-ports dev $@ diff --git a/docker/README.md b/docker/README.md index b3b806787..71ddb2dff 100644 --- a/docker/README.md +++ b/docker/README.md @@ -5,23 +5,49 @@ There is no requirement on having Rust or any other toolchain installed but a wo Unlike the `parity/polkadot` image which contains a single binary (`polkadot`!) used by default, the image in this folder builds and contains several binaries and you need to provide the name of the binary to be called. -You should refer to the .Dockerfile for the actual list. At the time of editing, the list of included binaries is: +You should refer to the [.Dockerfile](./substrate_builder.Dockerfile) for the actual list. At the time of editing, the list of included binaries is: - substrate - subkey - node-template - chain-spec-builder -To generate the latest parity/substrate image. Please first run: +First, install [Docker](https://docs.docker.com/get-docker/). + +Then to generate the latest parity/substrate image. Please run: ```sh ./build.sh ``` +> If you wish to create a debug build rather than a production build, then you may modify the [.Dockerfile](./substrate_builder.Dockerfile) replacing `cargo build --locked --release` with just `cargo build --locked` and replacing `target/release` with `target/debug`. + +> If you get an error that a tcp port address is already in use then find an available port to use for the host port in the [.Dockerfile](./substrate_builder.Dockerfile). + The image can be used by passing the selected binary followed by the appropriate tags for this binary. Your best guess to get started is to pass the `--help flag`. Here are a few examples: -- `docker run --rm -it parity/substrate substrate --version` -- `docker run --rm -it parity/substrate subkey --help` -- `docker run --rm -it parity/substrate node-template --version` -- `docker run --rm -it parity/substrate chain-spec-builder --help` +- `./run.sh substrate --version` +- `./run.sh subkey --help` +- `./run.sh node-template --version` +- `./run.sh chain-spec-builder --help` + +Then try running the following command to start a single node development chain using the Substrate Node Template binary `node-template`: + +```sh +./run.sh node-template --dev --ws-external +``` + +Note: It is recommended to provide a custom `--base-path` to store the chain database. For example: + +```sh +# Run Substrate Node Template without re-compiling +./run.sh node-template --dev --ws-external --base-path=/data +``` + +> To print logs follow the [Substrate debugging instructions](https://docs.substrate.io/test/debug/). + +```sh +# Purge the local dev chain +./run.sh node-template purge-chain --dev --base-path=/data -y +``` diff --git a/docker/run.sh b/docker/run.sh new file mode 100755 index 000000000..43510bee0 --- /dev/null +++ b/docker/run.sh @@ -0,0 +1,13 @@ +#!/usr/bin/env bash + +args=$@ + +# handle when arguments not provided. run arguments provided to script. +if [ "$args" = "" ] ; then + printf "Note: Please try providing an argument to the script.\n\n" + exit 1 +else + printf "*** Running Substrate Docker container with provided arguments: $args\n\n" + docker run --rm -it parity/substrate $args +fi + From 48e7cb147cb9a27125fd2e82edbcf4d0ed5927c4 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 9 Mar 2023 07:08:00 +0000 Subject: [PATCH 213/558] Bump wasmtime from 6.0.0 to 6.0.1 (#13566) Bumps [wasmtime](https://github.com/bytecodealliance/wasmtime) from 6.0.0 to 6.0.1. - [Release notes](https://github.com/bytecodealliance/wasmtime/releases) - [Changelog](https://github.com/bytecodealliance/wasmtime/blob/main/docs/WASI-some-possible-changes.md) - [Commits](https://github.com/bytecodealliance/wasmtime/compare/v6.0.0...v6.0.1) --- updated-dependencies: - dependency-name: wasmtime dependency-type: direct:production ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- Cargo.lock | 76 ++++++++++++++-------------- client/executor/wasmtime/Cargo.toml | 2 +- primitives/wasm-interface/Cargo.toml | 2 +- 3 files changed, 40 insertions(+), 40 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 53a867543..c0dcc47fd 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1154,18 +1154,18 @@ dependencies = [ [[package]] name = "cranelift-bforest" -version = "0.93.0" +version = "0.93.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "91b18cf92869a6ae85cde3af4bc4beb6154efa8adef03b18db2ad413d5bce3a2" +checksum = "a7379abaacee0f14abf3204a7606118f0465785252169d186337bcb75030815a" dependencies = [ "cranelift-entity", ] [[package]] name = "cranelift-codegen" -version = "0.93.0" +version = "0.93.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "567d9f6e919bac076f39b902a072686eaf9e6d015baa34d10a61b85105b7af59" +checksum = "9489fa336927df749631f1008007ced2871068544f40a202ce6d93fbf2366a7b" dependencies = [ "arrayvec 0.7.2", "bumpalo", @@ -1184,33 +1184,33 @@ dependencies = [ [[package]] name = "cranelift-codegen-meta" -version = "0.93.0" +version = "0.93.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e72b2d5ec8917b2971fe83850187373d0a186db4748a7c23a5f48691b8d92bb" +checksum = "05bbb67da91ec721ed57cef2f7c5ef7728e1cd9bde9ffd3ef8601022e73e3239" dependencies = [ "cranelift-codegen-shared", ] [[package]] name = "cranelift-codegen-shared" -version = "0.93.0" +version = "0.93.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3461c0e0c2ebbeb92533aacb27e219289f60dc84134ef34fbf2d77c9eddf07ef" +checksum = "418ecb2f36032f6665dc1a5e2060a143dbab41d83b784882e97710e890a7a16d" [[package]] name = "cranelift-entity" -version = "0.93.0" +version = "0.93.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "af684f7f7b01427b1942c7102673322a51b9d6f261e9663dc5e5595786775531" +checksum = "7cf583f7b093f291005f9fb1323e2c37f6ee4c7909e39ce016b2e8360d461705" dependencies = [ "serde", ] [[package]] name = "cranelift-frontend" -version = "0.93.0" +version = "0.93.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7d361ed0373cf5f086b49c499aa72227b646a64f899f32e34312f97c0fadff75" +checksum = "0b66bf9e916f57fbbd0f7703ec6286f4624866bf45000111627c70d272c8dda1" dependencies = [ "cranelift-codegen", "log", @@ -1220,15 +1220,15 @@ dependencies = [ [[package]] name = "cranelift-isle" -version = "0.93.0" +version = "0.93.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cef4f8f3984d772c199a48896d2fb766f96301bf71b371e03a2b99f4f3b7b931" +checksum = "649782a39ce99798dd6b4029e2bb318a2fbeaade1b4fa25330763c10c65bc358" [[package]] name = "cranelift-native" -version = "0.93.0" +version = "0.93.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f98e4e99a353703475d5acb402b9c13482d41d8a4008b352559bd560afb90363" +checksum = "937e021e089c51f9749d09e7ad1c4f255c2f8686cb8c3df63a34b3ec9921bc41" dependencies = [ "cranelift-codegen", "libc", @@ -1237,9 +1237,9 @@ dependencies = [ [[package]] name = "cranelift-wasm" -version = "0.93.0" +version = "0.93.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a1e3f4f0779a1b0f286a6ef19835d8665f88326e656a6d7d84fa9a39fa38ca32" +checksum = "d850cf6775477747c9dfda9ae23355dd70512ffebc70cf82b85a5b111ae668b5" dependencies = [ "cranelift-codegen", "cranelift-entity", @@ -12214,9 +12214,9 @@ dependencies = [ [[package]] name = "wasmtime" -version = "6.0.0" +version = "6.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9010891d0b8e367c3be94ca35d7bc25c1de3240463bb1d61bcfc8c2233c4e0d0" +checksum = "f6e89f9819523447330ffd70367ef4a18d8c832e24e8150fe054d1d912841632" dependencies = [ "anyhow", "bincode", @@ -12242,18 +12242,18 @@ dependencies = [ [[package]] name = "wasmtime-asm-macros" -version = "6.0.0" +version = "6.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "65805c663eaa8257b910666f6d4b056b5c7329750da754ba5df54f3af7dbf35c" +checksum = "9bd3a5e46c198032da934469f3a6e48649d1f9142438e4fd4617b68a35644b8a" dependencies = [ "cfg-if", ] [[package]] name = "wasmtime-cache" -version = "6.0.0" +version = "6.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2049ddfc1b10efc3c5591d0e84b9570ca50478f8818f3bfabb1a467918f53fb4" +checksum = "b389ae9b678b9c3851091a4804f4182d688d27aff7abc9aa37fa7be37d8ecffa" dependencies = [ "anyhow", "base64 0.13.1", @@ -12271,9 +12271,9 @@ dependencies = [ [[package]] name = "wasmtime-cranelift" -version = "6.0.0" +version = "6.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3f9065cad6a724fa838ec8497567e0b23acc26417bb2449f8d9d2021925c72f2" +checksum = "59b2c92a08c0db6efffd88fdc97d7aa9c7c63b03edb0971dbca745469f820e8c" dependencies = [ "anyhow", "cranelift-codegen", @@ -12292,9 +12292,9 @@ dependencies = [ [[package]] name = "wasmtime-environ" -version = "6.0.0" +version = "6.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4f964bb0b91fa021b8d1b488c62cc77b346c1dae6e3ebd010050b57c1f2ca657" +checksum = "9a6db9fc52985ba06ca601f2ff0ff1f526c5d724c7ac267b47326304b0c97883" dependencies = [ "anyhow", "cranelift-entity", @@ -12311,9 +12311,9 @@ dependencies = [ [[package]] name = "wasmtime-jit" -version = "6.0.0" +version = "6.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b7a1d06f5d109539e0168fc74fa65e3948ac8dac3bb8cdbd08b62b36a0ae27b8" +checksum = "b77e3a52cd84d0f7f18554afa8060cfe564ccac61e3b0802d3fd4084772fa5f6" dependencies = [ "addr2line 0.17.0", "anyhow", @@ -12335,9 +12335,9 @@ dependencies = [ [[package]] name = "wasmtime-jit-debug" -version = "6.0.0" +version = "6.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f76ef2e410329aaf8555ac6571d6fe07711be0646dcdf7ff3ab750a42ed2e583" +checksum = "d0245e8a9347017c7185a72e215218a802ff561545c242953c11ba00fccc930f" dependencies = [ "object 0.29.0", "once_cell", @@ -12346,9 +12346,9 @@ dependencies = [ [[package]] name = "wasmtime-jit-icache-coherence" -version = "6.0.0" +version = "6.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ec1fd0f0dd79e7cc0f55b102e320d7c77ab76cd272008a8fd98e25b5777e2636" +checksum = "67d412e9340ab1c83867051d8d1d7c90aa8c9afc91da086088068e2734e25064" dependencies = [ "cfg-if", "libc", @@ -12357,9 +12357,9 @@ dependencies = [ [[package]] name = "wasmtime-runtime" -version = "6.0.0" +version = "6.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "271aef9b4ca2e953a866293683f2db33cda46f6933c5e431e68d8373723d4ab6" +checksum = "d594e791b5fdd4dbaf8cf7ae62f2e4ff85018ce90f483ca6f42947688e48827d" dependencies = [ "anyhow", "cc", @@ -12381,9 +12381,9 @@ dependencies = [ [[package]] name = "wasmtime-types" -version = "6.0.0" +version = "6.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b18144b0e45479a830ac9fcebfc71a16d90dc72d8ebd5679700eb3bfe974d7df" +checksum = "a6688d6f96d4dbc1f89fab626c56c1778936d122b5f4ae7a57c2eb42b8d982e2" dependencies = [ "cranelift-entity", "serde", diff --git a/client/executor/wasmtime/Cargo.toml b/client/executor/wasmtime/Cargo.toml index 2e892a98a..bffccd12b 100644 --- a/client/executor/wasmtime/Cargo.toml +++ b/client/executor/wasmtime/Cargo.toml @@ -19,7 +19,7 @@ libc = "0.2.121" # When bumping wasmtime do not forget to also bump rustix # to exactly the same version as used by wasmtime! -wasmtime = { version = "6.0.0", default-features = false, features = [ +wasmtime = { version = "6.0.1", default-features = false, features = [ "cache", "cranelift", "jitdump", diff --git a/primitives/wasm-interface/Cargo.toml b/primitives/wasm-interface/Cargo.toml index 306a47e6d..441d66f0f 100644 --- a/primitives/wasm-interface/Cargo.toml +++ b/primitives/wasm-interface/Cargo.toml @@ -18,7 +18,7 @@ codec = { package = "parity-scale-codec", version = "3.2.2", default-features = impl-trait-for-tuples = "0.2.2" log = { version = "0.4.17", optional = true } wasmi = { version = "0.13.2", optional = true } -wasmtime = { version = "6.0.0", default-features = false, optional = true } +wasmtime = { version = "6.0.1", default-features = false, optional = true } anyhow = { version = "1.0.68", optional = true } sp-std = { version = "5.0.0", default-features = false, path = "../std" } From e46d96cb843b8ae025d2aae8abb46035a6df738c Mon Sep 17 00:00:00 2001 From: Oliver Tale-Yazdi Date: Thu, 9 Mar 2023 13:29:03 +0100 Subject: [PATCH 214/558] Undeploy insecure randomness pallet (#13560) * Undeploy insecure randomness pallet from node-template Signed-off-by: Oliver Tale-Yazdi * Update lockfile Signed-off-by: Oliver Tale-Yazdi * Remove from runtime Signed-off-by: Oliver Tale-Yazdi --------- Signed-off-by: Oliver Tale-Yazdi --- Cargo.lock | 1 - bin/node-template/runtime/Cargo.toml | 3 --- bin/node-template/runtime/src/lib.rs | 3 --- 3 files changed, 7 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index c0dcc47fd..a970be356 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -5096,7 +5096,6 @@ dependencies = [ "pallet-aura", "pallet-balances", "pallet-grandpa", - "pallet-insecure-randomness-collective-flip", "pallet-sudo", "pallet-template", "pallet-timestamp", diff --git a/bin/node-template/runtime/Cargo.toml b/bin/node-template/runtime/Cargo.toml index 55fdea7f5..90fa6269e 100644 --- a/bin/node-template/runtime/Cargo.toml +++ b/bin/node-template/runtime/Cargo.toml @@ -20,7 +20,6 @@ pallet-aura = { version = "4.0.0-dev", default-features = false, path = "../../. pallet-balances = { version = "4.0.0-dev", default-features = false, path = "../../../frame/balances" } frame-support = { version = "4.0.0-dev", default-features = false, path = "../../../frame/support" } pallet-grandpa = { version = "4.0.0-dev", default-features = false, path = "../../../frame/grandpa" } -pallet-insecure-randomness-collective-flip = { version = "4.0.0-dev", default-features = false, path = "../../../frame/insecure-randomness-collective-flip" } pallet-sudo = { version = "4.0.0-dev", default-features = false, path = "../../../frame/sudo" } frame-system = { version = "4.0.0-dev", default-features = false, path = "../../../frame/system" } frame-try-runtime = { version = "0.10.0-dev", default-features = false, path = "../../../frame/try-runtime", optional = true } @@ -70,7 +69,6 @@ std = [ "pallet-aura/std", "pallet-balances/std", "pallet-grandpa/std", - "pallet-insecure-randomness-collective-flip/std", "pallet-sudo/std", "pallet-template/std", "pallet-timestamp/std", @@ -109,7 +107,6 @@ try-runtime = [ "pallet-aura/try-runtime", "pallet-balances/try-runtime", "pallet-grandpa/try-runtime", - "pallet-insecure-randomness-collective-flip/try-runtime", "pallet-sudo/try-runtime", "pallet-template/try-runtime", "pallet-timestamp/try-runtime", diff --git a/bin/node-template/runtime/src/lib.rs b/bin/node-template/runtime/src/lib.rs index ef90517a2..ac01aa95f 100644 --- a/bin/node-template/runtime/src/lib.rs +++ b/bin/node-template/runtime/src/lib.rs @@ -203,8 +203,6 @@ impl frame_system::Config for Runtime { type MaxConsumers = frame_support::traits::ConstU32<16>; } -impl pallet_insecure_randomness_collective_flip::Config for Runtime {} - impl pallet_aura::Config for Runtime { type AuthorityId = AuraId; type DisabledValidators = (); @@ -279,7 +277,6 @@ construct_runtime!( UncheckedExtrinsic = UncheckedExtrinsic, { System: frame_system, - RandomnessCollectiveFlip: pallet_insecure_randomness_collective_flip, Timestamp: pallet_timestamp, Aura: pallet_aura, Grandpa: pallet_grandpa, From 11b932677ea71920c76c4a204947db773cf710f5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Silva?= <123550+andresilva@users.noreply.github.com> Date: Thu, 9 Mar 2023 14:05:04 +0000 Subject: [PATCH 215/558] grandpa: send neighbor packets to light clients on set transition (#13559) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * grandpa: send neighbor packets to light clients on set transition * Apply suggestions from code review Co-authored-by: Bastian Köcher * remove match statement --------- Co-authored-by: Bastian Köcher --- .../grandpa/src/communication/gossip.rs | 175 +++++++++++------- 1 file changed, 109 insertions(+), 66 deletions(-) diff --git a/client/consensus/grandpa/src/communication/gossip.rs b/client/consensus/grandpa/src/communication/gossip.rs index cf476f8e3..0a3029e25 100644 --- a/client/consensus/grandpa/src/communication/gossip.rs +++ b/client/consensus/grandpa/src/communication/gossip.rs @@ -791,68 +791,67 @@ impl Inner { /// Note a round in the current set has started. Does nothing if the last /// call to the function was with the same `round`. fn note_round(&mut self, round: Round) -> MaybeMessage { - { - let local_view = match self.local_view { - None => return None, - Some(ref mut v) => - if v.round == round { - // Do not send neighbor packets out if `round` has not changed --- - // such behavior is punishable. - return None - } else { - v - }, - }; + let local_view = self.local_view.as_mut()?; + if local_view.round == round { + // Do not send neighbor packets out if `round` has not changed --- + // such behavior is punishable. + return None + } - let set_id = local_view.set_id; + let set_id = local_view.set_id; - debug!( - target: LOG_TARGET, - "Voter {} noting beginning of round {:?} to network.", - self.config.name(), - (round, set_id) - ); + debug!( + target: LOG_TARGET, + "Voter {} noting beginning of round {:?} to network.", + self.config.name(), + (round, set_id) + ); - local_view.update_round(round); + local_view.update_round(round); - self.live_topics.push(round, set_id); - self.peers.reshuffle(); - } - self.multicast_neighbor_packet() + self.live_topics.push(round, set_id); + self.peers.reshuffle(); + + self.multicast_neighbor_packet(false) } /// Note that a voter set with given ID has started. Does nothing if the last /// call to the function was with the same `set_id`. fn note_set(&mut self, set_id: SetId, authorities: Vec) -> MaybeMessage { - { - let local_view = match self.local_view { - ref mut x @ None => x.get_or_insert(LocalView::new(set_id, Round(1))), - Some(ref mut v) => - if v.set_id == set_id { - let diff_authorities = self.authorities.iter().collect::>() != - authorities.iter().collect::>(); - - if diff_authorities { - debug!(target: LOG_TARGET, - "Gossip validator noted set {:?} twice with different authorities. \ - Was the authority set hard forked?", - set_id, - ); - self.authorities = authorities; - } - // Do not send neighbor packets out if the `set_id` has not changed --- - // such behavior is punishable. - return None - } else { - v - }, - }; + let local_view = match self.local_view { + ref mut x @ None => x.get_or_insert(LocalView::new(set_id, Round(1))), + Some(ref mut v) => + if v.set_id == set_id { + let diff_authorities = self.authorities.iter().collect::>() != + authorities.iter().collect::>(); + + if diff_authorities { + debug!( + target: LOG_TARGET, + "Gossip validator noted set {:?} twice with different authorities. \ + Was the authority set hard forked?", + set_id, + ); + + self.authorities = authorities; + } - local_view.update_set(set_id); - self.live_topics.push(Round(1), set_id); - self.authorities = authorities; - } - self.multicast_neighbor_packet() + // Do not send neighbor packets out if the `set_id` has not changed --- + // such behavior is punishable. + return None + } else { + v + }, + }; + + local_view.update_set(set_id); + self.live_topics.push(Round(1), set_id); + self.authorities = authorities; + + // when transitioning to a new set we also want to send neighbor packets to light clients, + // this is so that they know who to ask justifications from in order to finalize the last + // block in the previous set. + self.multicast_neighbor_packet(true) } /// Note that we've imported a commit finalizing a given block. Does nothing if the last @@ -864,19 +863,14 @@ impl Inner { set_id: SetId, finalized: NumberFor, ) -> MaybeMessage { - { - match self.local_view { - None => return None, - Some(ref mut v) => - if v.last_commit_height() < Some(&finalized) { - v.last_commit = Some((finalized, round, set_id)); - } else { - return None - }, - }; + let local_view = self.local_view.as_mut()?; + if local_view.last_commit_height() < Some(&finalized) { + local_view.last_commit = Some((finalized, round, set_id)); + } else { + return None } - self.multicast_neighbor_packet() + self.multicast_neighbor_packet(false) } fn consider_vote(&self, round: Round, set_id: SetId) -> Consider { @@ -1193,7 +1187,7 @@ impl Inner { (neighbor_topics, action, catch_up, report) } - fn multicast_neighbor_packet(&self) -> MaybeMessage { + fn multicast_neighbor_packet(&self, force_light: bool) -> MaybeMessage { self.local_view.as_ref().map(|local_view| { let packet = NeighborPacket { round: local_view.round, @@ -1207,8 +1201,9 @@ impl Inner { .iter() .filter_map(|(id, info)| { // light clients don't participate in the full GRANDPA voter protocol - // and therefore don't need to be informed about view updates - if info.roles.is_light() { + // and therefore don't need to be informed about all view updates unless + // we explicitly require it (e.g. when transitioning to a new set) + if info.roles.is_light() && !force_light { None } else { Some(id) @@ -2612,4 +2607,52 @@ mod tests { assert_eq!(val.inner().read().authorities, a2); } + + #[test] + fn sends_neighbor_packets_to_non_light_peers_when_starting_a_new_round() { + let (val, _) = GossipValidator::::new(config(), voter_set_state(), None, None); + + // initialize the validator to a stable set id + val.note_set(SetId(1), Vec::new(), |_, _| {}); + + let authority_peer = PeerId::random(); + let full_peer = PeerId::random(); + let light_peer = PeerId::random(); + + val.inner.write().peers.new_peer(authority_peer, ObservedRole::Authority); + val.inner.write().peers.new_peer(full_peer, ObservedRole::Full); + val.inner.write().peers.new_peer(light_peer, ObservedRole::Light); + + val.note_round(Round(2), |peers, message| { + assert_eq!(peers.len(), 2); + assert!(peers.contains(&authority_peer)); + assert!(peers.contains(&full_peer)); + assert!(!peers.contains(&light_peer)); + assert!(matches!(message, NeighborPacket { set_id: SetId(1), round: Round(2), .. })); + }); + } + + #[test] + fn sends_neighbor_packets_to_all_peers_when_starting_a_new_set() { + let (val, _) = GossipValidator::::new(config(), voter_set_state(), None, None); + + // initialize the validator to a stable set id + val.note_set(SetId(1), Vec::new(), |_, _| {}); + + let authority_peer = PeerId::random(); + let full_peer = PeerId::random(); + let light_peer = PeerId::random(); + + val.inner.write().peers.new_peer(authority_peer, ObservedRole::Authority); + val.inner.write().peers.new_peer(full_peer, ObservedRole::Full); + val.inner.write().peers.new_peer(light_peer, ObservedRole::Light); + + val.note_set(SetId(2), Vec::new(), |peers, message| { + assert_eq!(peers.len(), 3); + assert!(peers.contains(&authority_peer)); + assert!(peers.contains(&full_peer)); + assert!(peers.contains(&light_peer)); + assert!(matches!(message, NeighborPacket { set_id: SetId(2), round: Round(1), .. })); + }); + } } From 48c8875c417a4aebf105112b4442e1343228c7ab Mon Sep 17 00:00:00 2001 From: Jun Jiang Date: Thu, 9 Mar 2023 23:57:02 +0800 Subject: [PATCH 216/558] Make pallet_nfts storages public (#13517) --- frame/nfts/src/lib.rs | 30 +++++++++++++++--------------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/frame/nfts/src/lib.rs b/frame/nfts/src/lib.rs index 681d90842..9314098d3 100644 --- a/frame/nfts/src/lib.rs +++ b/frame/nfts/src/lib.rs @@ -196,7 +196,7 @@ pub mod pallet { /// Details of a collection. #[pallet::storage] - pub(super) type Collection, I: 'static = ()> = StorageMap< + pub type Collection, I: 'static = ()> = StorageMap< _, Blake2_128Concat, T::CollectionId, @@ -205,13 +205,13 @@ pub mod pallet { /// The collection, if any, of which an account is willing to take ownership. #[pallet::storage] - pub(super) type OwnershipAcceptance, I: 'static = ()> = + pub type OwnershipAcceptance, I: 'static = ()> = StorageMap<_, Blake2_128Concat, T::AccountId, T::CollectionId>; /// The items held by any given account; set out this way so that items owned by a single /// account can be enumerated. #[pallet::storage] - pub(super) type Account, I: 'static = ()> = StorageNMap< + pub type Account, I: 'static = ()> = StorageNMap< _, ( NMapKey, // owner @@ -225,7 +225,7 @@ pub mod pallet { /// The collections owned by any given account; set out this way so that collections owned by /// a single account can be enumerated. #[pallet::storage] - pub(super) type CollectionAccount, I: 'static = ()> = StorageDoubleMap< + pub type CollectionAccount, I: 'static = ()> = StorageDoubleMap< _, Blake2_128Concat, T::AccountId, @@ -238,7 +238,7 @@ pub mod pallet { /// The items in existence and their ownership details. #[pallet::storage] /// Stores collection roles as per account. - pub(super) type CollectionRoleOf, I: 'static = ()> = StorageDoubleMap< + pub type CollectionRoleOf, I: 'static = ()> = StorageDoubleMap< _, Blake2_128Concat, T::CollectionId, @@ -250,7 +250,7 @@ pub mod pallet { /// The items in existence and their ownership details. #[pallet::storage] - pub(super) type Item, I: 'static = ()> = StorageDoubleMap< + pub type Item, I: 'static = ()> = StorageDoubleMap< _, Blake2_128Concat, T::CollectionId, @@ -262,7 +262,7 @@ pub mod pallet { /// Metadata of a collection. #[pallet::storage] - pub(super) type CollectionMetadataOf, I: 'static = ()> = StorageMap< + pub type CollectionMetadataOf, I: 'static = ()> = StorageMap< _, Blake2_128Concat, T::CollectionId, @@ -272,7 +272,7 @@ pub mod pallet { /// Metadata of an item. #[pallet::storage] - pub(super) type ItemMetadataOf, I: 'static = ()> = StorageDoubleMap< + pub type ItemMetadataOf, I: 'static = ()> = StorageDoubleMap< _, Blake2_128Concat, T::CollectionId, @@ -284,7 +284,7 @@ pub mod pallet { /// Attributes of a collection. #[pallet::storage] - pub(super) type Attribute, I: 'static = ()> = StorageNMap< + pub type Attribute, I: 'static = ()> = StorageNMap< _, ( NMapKey, @@ -298,7 +298,7 @@ pub mod pallet { /// A price of an item. #[pallet::storage] - pub(super) type ItemPriceOf, I: 'static = ()> = StorageDoubleMap< + pub type ItemPriceOf, I: 'static = ()> = StorageDoubleMap< _, Blake2_128Concat, T::CollectionId, @@ -310,7 +310,7 @@ pub mod pallet { /// Item attribute approvals. #[pallet::storage] - pub(super) type ItemAttributesApprovalsOf, I: 'static = ()> = StorageDoubleMap< + pub type ItemAttributesApprovalsOf, I: 'static = ()> = StorageDoubleMap< _, Blake2_128Concat, T::CollectionId, @@ -323,12 +323,12 @@ pub mod pallet { /// Stores the `CollectionId` that is going to be used for the next collection. /// This gets incremented whenever a new collection is created. #[pallet::storage] - pub(super) type NextCollectionId, I: 'static = ()> = + pub type NextCollectionId, I: 'static = ()> = StorageValue<_, T::CollectionId, OptionQuery>; /// Handles all the pending swaps. #[pallet::storage] - pub(super) type PendingSwapOf, I: 'static = ()> = StorageDoubleMap< + pub type PendingSwapOf, I: 'static = ()> = StorageDoubleMap< _, Blake2_128Concat, T::CollectionId, @@ -345,12 +345,12 @@ pub mod pallet { /// Config of a collection. #[pallet::storage] - pub(super) type CollectionConfigOf, I: 'static = ()> = + pub type CollectionConfigOf, I: 'static = ()> = StorageMap<_, Blake2_128Concat, T::CollectionId, CollectionConfigFor, OptionQuery>; /// Config of an item. #[pallet::storage] - pub(super) type ItemConfigOf, I: 'static = ()> = StorageDoubleMap< + pub type ItemConfigOf, I: 'static = ()> = StorageDoubleMap< _, Blake2_128Concat, T::CollectionId, From 33eee69bf53d45449c1f6b145afe5da872310b6a Mon Sep 17 00:00:00 2001 From: Jun Jiang Date: Fri, 10 Mar 2023 01:44:26 +0800 Subject: [PATCH 217/558] Add amalgamation traits for NFT CollectionId and ItemId (#13514) * Add amalgamation traits for NFT CollectionId, ItemId, and DestroyWitness * Apply @bkchr suggests * ".git/.scripts/commands/fmt/fmt.sh" --------- Co-authored-by: command-bot <> --- frame/support/src/traits/tokens/nonfungible_v2.rs | 7 +++++-- frame/support/src/traits/tokens/nonfungibles_v2.rs | 8 ++++---- 2 files changed, 9 insertions(+), 6 deletions(-) diff --git a/frame/support/src/traits/tokens/nonfungible_v2.rs b/frame/support/src/traits/tokens/nonfungible_v2.rs index 175d94324..c23bf3e40 100644 --- a/frame/support/src/traits/tokens/nonfungible_v2.rs +++ b/frame/support/src/traits/tokens/nonfungible_v2.rs @@ -25,7 +25,10 @@ //! use. use super::nonfungibles_v2 as nonfungibles; -use crate::{dispatch::DispatchResult, traits::Get}; +use crate::{ + dispatch::{DispatchResult, Parameter}, + traits::Get, +}; use codec::{Decode, Encode}; use sp_runtime::TokenError; use sp_std::prelude::*; @@ -33,7 +36,7 @@ use sp_std::prelude::*; /// Trait for providing an interface to a read-only NFT-like item. pub trait Inspect { /// Type for identifying an item. - type ItemId; + type ItemId: Parameter; /// Returns the owner of `item`, or `None` if the item doesn't exist or has no /// owner. diff --git a/frame/support/src/traits/tokens/nonfungibles_v2.rs b/frame/support/src/traits/tokens/nonfungibles_v2.rs index 5deb0c568..9d32f29be 100644 --- a/frame/support/src/traits/tokens/nonfungibles_v2.rs +++ b/frame/support/src/traits/tokens/nonfungibles_v2.rs @@ -27,7 +27,7 @@ //! Implementations of these traits may be converted to implementations of corresponding //! `nonfungible` traits by using the `nonfungible::ItemOf` type adapter. -use crate::dispatch::{DispatchError, DispatchResult}; +use crate::dispatch::{DispatchError, DispatchResult, Parameter}; use codec::{Decode, Encode}; use sp_runtime::TokenError; use sp_std::prelude::*; @@ -35,11 +35,11 @@ use sp_std::prelude::*; /// Trait for providing an interface to many read-only NFT-like sets of items. pub trait Inspect { /// Type for identifying an item. - type ItemId; + type ItemId: Parameter; /// Type for identifying a collection (an identifier for an independent collection of /// items). - type CollectionId; + type CollectionId: Parameter; /// Returns the owner of `item` of `collection`, or `None` if the item doesn't exist /// (or somehow has no owner). @@ -193,7 +193,7 @@ pub trait Create: Inspect { /// Trait for providing the ability to destroy collections of nonfungible items. pub trait Destroy: Inspect { /// The witness data needed to destroy an item. - type DestroyWitness; + type DestroyWitness: Parameter; /// Provide the appropriate witness data needed to destroy an item. fn get_destroy_witness(collection: &Self::CollectionId) -> Option; From 0f3b2a2d8d6c7be3d2ff8e846e7556cf65916cdf Mon Sep 17 00:00:00 2001 From: Jun Jiang Date: Fri, 10 Mar 2023 16:44:23 +0800 Subject: [PATCH 218/558] Nfts: Make ItemConfig members pub (#13575) --- frame/nfts/src/types.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frame/nfts/src/types.rs b/frame/nfts/src/types.rs index 8c43024cd..459aa1cff 100644 --- a/frame/nfts/src/types.rs +++ b/frame/nfts/src/types.rs @@ -399,7 +399,7 @@ impl_codec_bitflags!(ItemSettings, u64, ItemSetting); )] pub struct ItemConfig { /// Item's settings. - pub(super) settings: ItemSettings, + pub settings: ItemSettings, } impl ItemConfig { From 8f9b07445057ab83a057cb61aac56a4a0a671395 Mon Sep 17 00:00:00 2001 From: Jegor Sidorenko <5252494+jsidorenko@users.noreply.github.com> Date: Fri, 10 Mar 2023 11:52:10 +0200 Subject: [PATCH 219/558] Nfts: minor fixes (#13576) * Rename owner_of_item to owned_item * Move AttributeNamespace into the types file --- frame/nfts/src/lib.rs | 10 ++++------ frame/nfts/src/tests.rs | 6 +++--- frame/nfts/src/types.rs | 17 ++++++++++++++++- frame/support/src/traits/tokens.rs | 5 ++--- frame/support/src/traits/tokens/misc.rs | 15 --------------- 5 files changed, 25 insertions(+), 28 deletions(-) diff --git a/frame/nfts/src/lib.rs b/frame/nfts/src/lib.rs index 9314098d3..2d19cf045 100644 --- a/frame/nfts/src/lib.rs +++ b/frame/nfts/src/lib.rs @@ -45,9 +45,7 @@ pub mod weights; use codec::{Decode, Encode}; use frame_support::traits::{ - tokens::{AttributeNamespace, Locker}, - BalanceStatus::Reserved, - Currency, EnsureOriginWithArg, ReservableCurrency, + tokens::Locker, BalanceStatus::Reserved, Currency, EnsureOriginWithArg, ReservableCurrency, }; use frame_system::Config as SystemConfig; use sp_runtime::{ @@ -809,13 +807,13 @@ pub mod pallet { match mint_settings.mint_type { MintType::Issuer => return Err(Error::::NoPermission.into()), MintType::HolderOf(collection_id) => { - let MintWitness { owner_of_item } = + let MintWitness { owned_item } = witness_data.ok_or(Error::::BadWitness)?; let owns_item = Account::::contains_key(( &caller, &collection_id, - &owner_of_item, + &owned_item, )); ensure!(owns_item, Error::::BadWitness); @@ -824,7 +822,7 @@ pub mod pallet { let key = ( &collection_id, - Some(owner_of_item), + Some(owned_item), AttributeNamespace::Pallet, &Self::construct_attribute_key(pallet_attribute.encode())?, ); diff --git a/frame/nfts/src/tests.rs b/frame/nfts/src/tests.rs index 8d937b0f7..e44e4151f 100644 --- a/frame/nfts/src/tests.rs +++ b/frame/nfts/src/tests.rs @@ -335,7 +335,7 @@ fn mint_should_work() { 1, 42, account(2), - Some(MintWitness { owner_of_item: 42 }) + Some(MintWitness { owned_item: 42 }) ), Error::::BadWitness ); @@ -344,7 +344,7 @@ fn mint_should_work() { 1, 42, account(2), - Some(MintWitness { owner_of_item: 43 }) + Some(MintWitness { owned_item: 43 }) )); // can't mint twice @@ -354,7 +354,7 @@ fn mint_should_work() { 1, 46, account(2), - Some(MintWitness { owner_of_item: 43 }) + Some(MintWitness { owned_item: 43 }) ), Error::::AlreadyClaimed ); diff --git a/frame/nfts/src/types.rs b/frame/nfts/src/types.rs index 459aa1cff..f8ab2a355 100644 --- a/frame/nfts/src/types.rs +++ b/frame/nfts/src/types.rs @@ -124,7 +124,7 @@ impl CollectionDetails { #[derive(Clone, Encode, Decode, Eq, PartialEq, RuntimeDebug, TypeInfo)] pub struct MintWitness { /// Provide the id of the item in a required collection. - pub owner_of_item: ItemId, + pub owned_item: ItemId, } /// Information concerning the ownership of a single unique item. @@ -317,6 +317,21 @@ impl Default for MintSettings { + /// An attribute was set by the pallet. + Pallet, + /// An attribute was set by collection's owner. + CollectionOwner, + /// An attribute was set by item's owner. + ItemOwner, + /// An attribute was set by pre-approved account. + Account(AccountId), +} + /// A witness data to cancel attributes approval operation. #[derive(Clone, Encode, Decode, Eq, PartialEq, RuntimeDebug, TypeInfo)] pub struct CancelAttributesApprovalWitness { diff --git a/frame/support/src/traits/tokens.rs b/frame/support/src/traits/tokens.rs index f8dcf6815..e1a96f621 100644 --- a/frame/support/src/traits/tokens.rs +++ b/frame/support/src/traits/tokens.rs @@ -28,7 +28,6 @@ pub mod nonfungibles; pub mod nonfungibles_v2; pub use imbalance::Imbalance; pub use misc::{ - AssetId, AttributeNamespace, Balance, BalanceConversion, BalanceStatus, ConvertRank, - DepositConsequence, ExistenceRequirement, GetSalary, Locker, WithdrawConsequence, - WithdrawReasons, + AssetId, Balance, BalanceConversion, BalanceStatus, ConvertRank, DepositConsequence, + ExistenceRequirement, GetSalary, Locker, WithdrawConsequence, WithdrawReasons, }; diff --git a/frame/support/src/traits/tokens/misc.rs b/frame/support/src/traits/tokens/misc.rs index eff65f362..6113642c8 100644 --- a/frame/support/src/traits/tokens/misc.rs +++ b/frame/support/src/traits/tokens/misc.rs @@ -126,21 +126,6 @@ pub enum BalanceStatus { Reserved, } -/// Attribute namespaces for non-fungible tokens. -#[derive( - Clone, Encode, Decode, Eq, PartialEq, RuntimeDebug, scale_info::TypeInfo, MaxEncodedLen, -)] -pub enum AttributeNamespace { - /// An attribute was set by the pallet. - Pallet, - /// An attribute was set by collection's owner. - CollectionOwner, - /// An attribute was set by item's owner. - ItemOwner, - /// An attribute was set by pre-approved account. - Account(AccountId), -} - bitflags::bitflags! { /// Reasons for moving funds out of an account. #[derive(Encode, Decode, MaxEncodedLen)] From 9d34fb0cb7985b867116cb946f88d76c4fc481df Mon Sep 17 00:00:00 2001 From: Gavin Wood Date: Sat, 11 Mar 2023 13:11:15 +0100 Subject: [PATCH 220/558] Logic for the (Core) Fellowship (#13503) * More drafting * Paymaster pallet * Fix build * More tests * Rename * Rename * Renaming * Revert old changes * Multi-phase payouts to avoid bank-runs * Tests * Tests * Allow payment to be targeted elsewhere * Proper ssync payment failure handling * Test for repayment * Docs * Impl RankedMembers for RankedCollective * Implement Pay for Pot (i.e. basic account). * Benchmarks * Weights * Introduce Salary benchmark into node * Fix warning * ".git/.scripts/commands/bench/bench.sh" pallet dev pallet_salary * Update primitives/arithmetic/src/traits.rs Co-authored-by: Jegor Sidorenko <5252494+jsidorenko@users.noreply.github.com> * Update frame/salary/src/lib.rs Co-authored-by: Jegor Sidorenko <5252494+jsidorenko@users.noreply.github.com> * Update lib.rs * Update frame/salary/src/lib.rs Co-authored-by: joe petrowski <25483142+joepetrowski@users.noreply.github.com> * Docs * Update frame/salary/src/lib.rs Co-authored-by: joe petrowski <25483142+joepetrowski@users.noreply.github.com> * Update frame/salary/src/lib.rs Co-authored-by: joe petrowski <25483142+joepetrowski@users.noreply.github.com> * Fix * Fixes * Fixes * Move some salary traits stuff to a shared location * Initial draft * Comment out bits * Fix * First couple of tests * One more test * Update frame/salary/src/lib.rs Co-authored-by: Oliver Tale-Yazdi * Update frame/salary/src/lib.rs Co-authored-by: Oliver Tale-Yazdi * Mul floor * Tests * Mul floor * Fix warnings * Fix test * Tests * Last tests * Docs * Fix warnings * Benchmarks * Weights * Integrate benchmark * Fixes * Fix * ".git/.scripts/commands/bench/bench.sh" pallet dev pallet_core_fellowship * Better process flow * Fix benchmarks & tests * Docs * Fixes * Fixes * docs * ".git/.scripts/commands/bench/bench.sh" pallet dev pallet_core_fellowship * Docs and allow custom evidence size * ".git/.scripts/commands/bench/bench.sh" pallet dev pallet_core_fellowship * Update frame/core-fellowship/src/lib.rs Co-authored-by: Kian Paimani <5588131+kianenigma@users.noreply.github.com> * Update frame/core-fellowship/src/tests.rs Co-authored-by: Kian Paimani <5588131+kianenigma@users.noreply.github.com> * Update frame/core-fellowship/src/benchmarking.rs * Update frame/core-fellowship/src/benchmarking.rs * Apply suggestions from code review * Rename * Update primitives/arithmetic/src/traits.rs Co-authored-by: joe petrowski <25483142+joepetrowski@users.noreply.github.com> * Reduce magic numbers * Update frame/core-fellowship/src/lib.rs Co-authored-by: Oliver Tale-Yazdi * Update frame/core-fellowship/src/lib.rs Co-authored-by: Oliver Tale-Yazdi * Benchmark result * Remove dependency * set_params should pay * induct should pay * Remove some other free calls --------- Co-authored-by: command-bot <> Co-authored-by: Jegor Sidorenko <5252494+jsidorenko@users.noreply.github.com> Co-authored-by: joe petrowski <25483142+joepetrowski@users.noreply.github.com> Co-authored-by: Oliver Tale-Yazdi Co-authored-by: Kian Paimani <5588131+kianenigma@users.noreply.github.com> --- Cargo.lock | 20 + Cargo.toml | 1 + bin/node/runtime/Cargo.toml | 4 + bin/node/runtime/src/lib.rs | 14 + frame/core-fellowship/Cargo.toml | 55 ++ frame/core-fellowship/README.md | 3 + frame/core-fellowship/src/benchmarking.rs | 216 ++++++++ frame/core-fellowship/src/lib.rs | 594 ++++++++++++++++++++++ frame/core-fellowship/src/tests.rs | 362 +++++++++++++ frame/core-fellowship/src/weights.rs | 393 ++++++++++++++ frame/salary/src/lib.rs | 4 +- frame/system/src/lib.rs | 7 +- primitives/arithmetic/src/traits.rs | 32 ++ primitives/runtime/src/traits.rs | 18 + 14 files changed, 1716 insertions(+), 7 deletions(-) create mode 100644 frame/core-fellowship/Cargo.toml create mode 100644 frame/core-fellowship/README.md create mode 100644 frame/core-fellowship/src/benchmarking.rs create mode 100644 frame/core-fellowship/src/lib.rs create mode 100644 frame/core-fellowship/src/tests.rs create mode 100644 frame/core-fellowship/src/weights.rs diff --git a/Cargo.lock b/Cargo.lock index a970be356..9a63e25b4 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3522,6 +3522,7 @@ dependencies = [ "pallet-contracts", "pallet-contracts-primitives", "pallet-conviction-voting", + "pallet-core-fellowship", "pallet-democracy", "pallet-election-provider-multi-phase", "pallet-election-provider-support-benchmarking", @@ -5777,6 +5778,25 @@ dependencies = [ "sp-std", ] +[[package]] +name = "pallet-core-fellowship" +version = "4.0.0-dev" +dependencies = [ + "frame-benchmarking", + "frame-support", + "frame-system", + "log", + "pallet-ranked-collective", + "pallet-salary", + "parity-scale-codec", + "scale-info", + "sp-arithmetic", + "sp-core", + "sp-io", + "sp-runtime", + "sp-std", +] + [[package]] name = "pallet-democracy" version = "4.0.0-dev" diff --git a/Cargo.toml b/Cargo.toml index 0b490bd17..de562ad79 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -94,6 +94,7 @@ members = [ "frame/contracts/proc-macro", "frame/contracts/primitives", "frame/conviction-voting", + "frame/core-fellowship", "frame/democracy", "frame/fast-unstake", "frame/try-runtime", diff --git a/bin/node/runtime/Cargo.toml b/bin/node/runtime/Cargo.toml index a610bbe5d..16f1d0a4c 100644 --- a/bin/node/runtime/Cargo.toml +++ b/bin/node/runtime/Cargo.toml @@ -65,6 +65,7 @@ pallet-collective = { version = "4.0.0-dev", default-features = false, path = ". pallet-contracts = { version = "4.0.0-dev", default-features = false, path = "../../../frame/contracts" } pallet-contracts-primitives = { version = "7.0.0", default-features = false, path = "../../../frame/contracts/primitives/" } pallet-conviction-voting = { version = "4.0.0-dev", default-features = false, path = "../../../frame/conviction-voting" } +pallet-core-fellowship = { version = "4.0.0-dev", default-features = false, path = "../../../frame/core-fellowship" } pallet-democracy = { version = "4.0.0-dev", default-features = false, path = "../../../frame/democracy" } pallet-election-provider-multi-phase = { version = "4.0.0-dev", default-features = false, path = "../../../frame/election-provider-multi-phase" } pallet-election-provider-support-benchmarking = { version = "4.0.0-dev", default-features = false, path = "../../../frame/election-provider-support/benchmarking", optional = true } @@ -148,6 +149,7 @@ std = [ "pallet-contracts/std", "pallet-contracts-primitives/std", "pallet-conviction-voting/std", + "pallet-core-fellowship/std", "pallet-democracy/std", "pallet-elections-phragmen/std", "pallet-fast-unstake/std", @@ -235,6 +237,7 @@ runtime-benchmarks = [ "pallet-collective/runtime-benchmarks", "pallet-contracts/runtime-benchmarks", "pallet-conviction-voting/runtime-benchmarks", + "pallet-core-fellowship/runtime-benchmarks", "pallet-democracy/runtime-benchmarks", "pallet-election-provider-multi-phase/runtime-benchmarks", "pallet-election-provider-support-benchmarking/runtime-benchmarks", @@ -294,6 +297,7 @@ try-runtime = [ "pallet-collective/try-runtime", "pallet-contracts/try-runtime", "pallet-conviction-voting/try-runtime", + "pallet-core-fellowship/try-runtime", "pallet-democracy/try-runtime", "pallet-election-provider-multi-phase/try-runtime", "pallet-elections-phragmen/try-runtime", diff --git a/bin/node/runtime/src/lib.rs b/bin/node/runtime/src/lib.rs index c507c2d8d..614dbc4d9 100644 --- a/bin/node/runtime/src/lib.rs +++ b/bin/node/runtime/src/lib.rs @@ -1574,6 +1574,18 @@ impl pallet_salary::Config for Runtime { type Budget = Budget; } +impl pallet_core_fellowship::Config for Runtime { + type WeightInfo = (); + type RuntimeEvent = RuntimeEvent; + type Members = RankedCollective; + type Balance = Balance; + type ParamsOrigin = frame_system::EnsureRoot; + type InductOrigin = pallet_core_fellowship::EnsureInducted; + type ApproveOrigin = frame_system::EnsureRootWithSuccess>; + type PromoteOrigin = frame_system::EnsureRootWithSuccess>; + type EvidenceSize = ConstU32<16_384>; +} + parameter_types! { pub Features: PalletFeatures = PalletFeatures::all_enabled(); pub const MaxAttributesPerCall: u32 = 10; @@ -1770,6 +1782,7 @@ construct_runtime!( Uniques: pallet_uniques, Nfts: pallet_nfts, Salary: pallet_salary, + CoreFellowship: pallet_core_fellowship, TransactionStorage: pallet_transaction_storage, VoterList: pallet_bags_list::, StateTrieMigration: pallet_state_trie_migration, @@ -1866,6 +1879,7 @@ mod benches { [pallet_collective, Council] [pallet_conviction_voting, ConvictionVoting] [pallet_contracts, Contracts] + [pallet_core_fellowship, CoreFellowship] [pallet_democracy, Democracy] [pallet_election_provider_multi_phase, ElectionProviderMultiPhase] [pallet_election_provider_support_benchmarking, EPSBench::] diff --git a/frame/core-fellowship/Cargo.toml b/frame/core-fellowship/Cargo.toml new file mode 100644 index 000000000..2785346c7 --- /dev/null +++ b/frame/core-fellowship/Cargo.toml @@ -0,0 +1,55 @@ +[package] +name = "pallet-core-fellowship" +version = "4.0.0-dev" +authors = ["Parity Technologies "] +edition = "2021" +license = "Apache-2.0" +homepage = "https://substrate.io" +repository = "https://github.com/paritytech/substrate/" +description = "Logic as per the description of The Fellowship for core Polkadot technology" +readme = "README.md" + +[package.metadata.docs.rs] +targets = ["x86_64-unknown-linux-gnu"] + +[dependencies] +codec = { package = "parity-scale-codec", version = "3.0.0", default-features = false, features = ["derive"] } +log = { version = "0.4.16", default-features = false } +scale-info = { version = "2.0.1", default-features = false, features = ["derive"] } +frame-benchmarking = { version = "4.0.0-dev", default-features = false, optional = true, path = "../benchmarking" } +frame-support = { version = "4.0.0-dev", default-features = false, path = "../support" } +frame-system = { version = "4.0.0-dev", default-features = false, path = "../system" } +sp-arithmetic = { version = "6.0.0", default-features = false, path = "../../primitives/arithmetic" } +sp-core = { version = "7.0.0", default-features = false, path = "../../primitives/core" } +sp-io = { version = "7.0.0", default-features = false, path = "../../primitives/io" } +sp-runtime = { version = "7.0.0", default-features = false, path = "../../primitives/runtime" } +sp-std = { version = "5.0.0", default-features = false, path = "../../primitives/std" } + +[dev-dependencies] +pallet-ranked-collective = { version = "4.0.0-dev", default-features = false, path = "../ranked-collective" } +pallet-salary = { version = "4.0.0-dev", default-features = false, path = "../salary" } + +[features] +default = ["std"] +std = [ + "codec/std", + "frame-benchmarking?/std", + "frame-support/std", + "frame-system/std", + "log/std", + "scale-info/std", + "sp-arithmetic/std", + "sp-core/std", + "sp-io/std", + "sp-runtime/std", + "sp-std/std", +] +runtime-benchmarks = [ + "frame-benchmarking/runtime-benchmarks", + "frame-support/runtime-benchmarks", + "frame-system/runtime-benchmarks", + "sp-runtime/runtime-benchmarks", + "pallet-ranked-collective/runtime-benchmarks", + "pallet-salary/runtime-benchmarks", +] +try-runtime = ["frame-support/try-runtime"] diff --git a/frame/core-fellowship/README.md b/frame/core-fellowship/README.md new file mode 100644 index 000000000..3c9b1f63e --- /dev/null +++ b/frame/core-fellowship/README.md @@ -0,0 +1,3 @@ +# Core Fellowship + +Logic specific to the core Polkadot Fellowship. \ No newline at end of file diff --git a/frame/core-fellowship/src/benchmarking.rs b/frame/core-fellowship/src/benchmarking.rs new file mode 100644 index 000000000..551ec30c1 --- /dev/null +++ b/frame/core-fellowship/src/benchmarking.rs @@ -0,0 +1,216 @@ +// This file is part of Substrate. + +// Copyright (C) 2020-2022 Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +//! Salary pallet benchmarking. + +#![cfg(feature = "runtime-benchmarks")] + +use super::*; +use crate::Pallet as CoreFellowship; + +use frame_benchmarking::v2::*; +use frame_system::RawOrigin; +use sp_arithmetic::traits::Bounded; + +const SEED: u32 = 0; + +type BenchResult = Result<(), BenchmarkError>; + +#[instance_benchmarks] +mod benchmarks { + use super::*; + + fn ensure_evidence, I: 'static>(who: &T::AccountId) -> BenchResult { + let evidence = BoundedVec::try_from(vec![0; Evidence::::bound()]).unwrap(); + let wish = Wish::Retention; + let origin = RawOrigin::Signed(who.clone()).into(); + CoreFellowship::::submit_evidence(origin, wish, evidence)?; + assert!(MemberEvidence::::contains_key(who)); + Ok(()) + } + + fn make_member, I: 'static>(rank: u16) -> Result { + let member = account("member", 0, SEED); + T::Members::induct(&member)?; + for _ in 0..rank { + T::Members::promote(&member)?; + } + CoreFellowship::::import(RawOrigin::Signed(member.clone()).into())?; + Ok(member) + } + + #[benchmark] + fn set_params() -> Result<(), BenchmarkError> { + let params = ParamsType { + active_salary: [100u32.into(); 9], + passive_salary: [10u32.into(); 9], + demotion_period: [100u32.into(); 9], + min_promotion_period: [100u32.into(); 9], + offboard_timeout: 1u32.into(), + }; + + #[extrinsic_call] + _(RawOrigin::Root, Box::new(params.clone())); + + assert_eq!(Params::::get(), params); + Ok(()) + } + + #[benchmark] + fn bump_offboard() -> Result<(), BenchmarkError> { + let member = make_member::(0)?; + + // Set it to the max value to ensure that any possible auto-demotion period has passed. + frame_system::Pallet::::set_block_number(T::BlockNumber::max_value()); + ensure_evidence::(&member)?; + assert!(Member::::contains_key(&member)); + + #[extrinsic_call] + CoreFellowship::::bump(RawOrigin::Signed(member.clone()), member.clone()); + + assert!(!Member::::contains_key(&member)); + assert!(!MemberEvidence::::contains_key(&member)); + Ok(()) + } + + #[benchmark] + fn bump_demote() -> Result<(), BenchmarkError> { + let member = make_member::(2)?; + + // Set it to the max value to ensure that any possible auto-demotion period has passed. + frame_system::Pallet::::set_block_number(T::BlockNumber::max_value()); + ensure_evidence::(&member)?; + assert!(Member::::contains_key(&member)); + assert_eq!(T::Members::rank_of(&member), Some(2)); + + #[extrinsic_call] + CoreFellowship::::bump(RawOrigin::Signed(member.clone()), member.clone()); + + assert!(Member::::contains_key(&member)); + assert_eq!(T::Members::rank_of(&member), Some(1)); + assert!(!MemberEvidence::::contains_key(&member)); + Ok(()) + } + + #[benchmark] + fn set_active() -> Result<(), BenchmarkError> { + let member = make_member::(1)?; + assert!(Member::::get(&member).unwrap().is_active); + + #[extrinsic_call] + _(RawOrigin::Signed(member.clone()), false); + + assert!(!Member::::get(&member).unwrap().is_active); + Ok(()) + } + + #[benchmark] + fn induct() -> Result<(), BenchmarkError> { + let candidate: T::AccountId = account("candidate", 0, SEED); + + #[extrinsic_call] + _(RawOrigin::Root, candidate.clone()); + + assert_eq!(T::Members::rank_of(&candidate), Some(0)); + assert!(Member::::contains_key(&candidate)); + Ok(()) + } + + #[benchmark] + fn promote() -> Result<(), BenchmarkError> { + let member = make_member::(1)?; + ensure_evidence::(&member)?; + + #[extrinsic_call] + _(RawOrigin::Root, member.clone(), 2u8.into()); + + assert_eq!(T::Members::rank_of(&member), Some(2)); + assert!(!MemberEvidence::::contains_key(&member)); + Ok(()) + } + + #[benchmark] + fn offboard() -> Result<(), BenchmarkError> { + let member = make_member::(0)?; + T::Members::demote(&member)?; + ensure_evidence::(&member)?; + + assert!(T::Members::rank_of(&member).is_none()); + assert!(Member::::contains_key(&member)); + assert!(MemberEvidence::::contains_key(&member)); + + #[extrinsic_call] + _(RawOrigin::Signed(member.clone()), member.clone()); + + assert!(!Member::::contains_key(&member)); + assert!(!MemberEvidence::::contains_key(&member)); + Ok(()) + } + + #[benchmark] + fn import() -> Result<(), BenchmarkError> { + let member = account("member", 0, SEED); + T::Members::induct(&member)?; + T::Members::promote(&member)?; + + assert!(!Member::::contains_key(&member)); + + #[extrinsic_call] + _(RawOrigin::Signed(member.clone())); + + assert!(Member::::contains_key(&member)); + Ok(()) + } + + #[benchmark] + fn approve() -> Result<(), BenchmarkError> { + let member = make_member::(1)?; + let then = frame_system::Pallet::::block_number(); + let now = then.saturating_plus_one(); + frame_system::Pallet::::set_block_number(now); + ensure_evidence::(&member)?; + + assert_eq!(Member::::get(&member).unwrap().last_proof, then); + + #[extrinsic_call] + _(RawOrigin::Root, member.clone(), 1u8.into()); + + assert_eq!(Member::::get(&member).unwrap().last_proof, now); + assert!(!MemberEvidence::::contains_key(&member)); + Ok(()) + } + + #[benchmark] + fn submit_evidence() -> Result<(), BenchmarkError> { + let member = make_member::(1)?; + let evidence = vec![0; Evidence::::bound()].try_into().unwrap(); + + assert!(!MemberEvidence::::contains_key(&member)); + + #[extrinsic_call] + _(RawOrigin::Signed(member.clone()), Wish::Retention, evidence); + + assert!(MemberEvidence::::contains_key(&member)); + Ok(()) + } + + impl_benchmark_test_suite! { + CoreFellowship, + crate::tests::new_test_ext(), + crate::tests::Test, + } +} diff --git a/frame/core-fellowship/src/lib.rs b/frame/core-fellowship/src/lib.rs new file mode 100644 index 000000000..6ba60dbaf --- /dev/null +++ b/frame/core-fellowship/src/lib.rs @@ -0,0 +1,594 @@ +// This file is part of Substrate. + +// Copyright (C) 2017-2022 Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +//! Additional logic for the Core Fellowship. This determines salary, registers activity/passivity +//! and handles promotion and demotion periods. +//! +//! This only handles members of non-zero rank. +//! +//! # Process Flow +//! - Begin with a call to `induct`, where some privileged origin (perhaps a pre-existing member of +//! `rank > 1`) is able to make a candidate from an account and introduce it to be tracked in this +//! pallet in order to allow evidence to be submitted and promotion voted on. +//! - The candidate then calls `submit_evidence` to apply for their promotion to rank 1. +//! - A `PromoteOrigin` of at least rank 1 calls `promote` on the candidate to elevate it to rank 1. +//! - Some time later but before rank 1's `demotion_period` elapses, candidate calls +//! `submit_evidence` with evidence of their efforts to apply for approval to stay at rank 1. +//! - An `ApproveOrigin` of at least rank 1 calls `approve` on the candidate to avoid imminent +//! demotion and keep it at rank 1. +//! - These last two steps continue until the candidate is ready to apply for a promotion, at which +//! point the previous two steps are repeated with a higher rank. +//! - If the member fails to get an approval within the `demotion_period` then anyone may call +//! `bump` to demote the candidate by one rank. +//! - If a candidate fails to be promoted to a member within the `offboard_timeout` period, then +//! anyone may call `bump` to remove the account's candidacy. +//! - Pre-existing members may call `import` to have their rank recognised and be inducted into this +//! pallet (to gain a salary and allow for eventual promotion). +//! - If, externally to this pallet, a member or candidate has their rank removed completely, then +//! `offboard` may be called to remove them entirely from this pallet. +//! +//! Note there is a difference between having a rank of 0 (whereby the account is a *candidate*) and +//! having no rank at all (whereby we consider it *unranked*). An account can be demoted from rank +//! 0 to become unranked. This process is called being offboarded and there is an extrinsic to do +//! this explicitly when external factors to this pallet have caused the tracked account to become +//! unranked. At rank 0, there is not a "demotion" period after which the account may be bumped to +//! become offboarded but rather an "offboard timeout". +//! +//! Candidates may be introduced (i.e. an account to go from unranked to rank of 0) by an origin +//! of a different privilege to that for promotion. This allows the possibility for even a single +//! existing member to introduce a new candidate without payment. +//! +//! Only tracked/ranked accounts may submit evidence for their proof and promotion. Candidates +//! cannot be approved - they must proceed only to promotion prior to the offboard timeout elapsing. + +#![cfg_attr(not(feature = "std"), no_std)] +#![recursion_limit = "128"] + +use codec::{Decode, Encode, MaxEncodedLen}; +use scale_info::TypeInfo; +use sp_arithmetic::traits::{Saturating, Zero}; +use sp_std::{marker::PhantomData, prelude::*}; + +use frame_support::{ + dispatch::DispatchResultWithPostInfo, + ensure, + traits::{tokens::Balance as BalanceTrait, EnsureOrigin, Get, RankedMembers}, + BoundedVec, RuntimeDebug, +}; + +#[cfg(test)] +mod tests; + +#[cfg(feature = "runtime-benchmarks")] +mod benchmarking; +pub mod weights; + +pub use pallet::*; +pub use weights::WeightInfo; + +/// The desired outcome for which evidence is presented. +#[derive(Encode, Decode, Eq, PartialEq, Copy, Clone, TypeInfo, MaxEncodedLen, RuntimeDebug)] +pub enum Wish { + /// Member wishes only to retain their current rank. + Retention, + /// Member wishes to be promoted. + Promotion, +} + +/// A piece of evidence to underpin a [Wish]. +/// +/// From the pallet's perspective, this is just a blob of data without meaning. The fellows can +/// decide how to concretely utilise it. This could be an IPFS hash, a URL or structured data. +pub type Evidence = BoundedVec>::EvidenceSize>; + +/// The status of the pallet instance. +#[derive(Encode, Decode, Eq, PartialEq, Clone, TypeInfo, MaxEncodedLen, RuntimeDebug)] +pub struct ParamsType { + /// The amounts to be paid when a member of a given rank (-1) is active. + active_salary: [Balance; RANKS], + /// The amounts to be paid when a member of a given rank (-1) is passive. + passive_salary: [Balance; RANKS], + /// The period between which unproven members become demoted. + demotion_period: [BlockNumber; RANKS], + /// The period between which members must wait before they may proceed to this rank. + min_promotion_period: [BlockNumber; RANKS], + /// Amount by which an account can remain at rank 0 (candidate before being offboard entirely). + offboard_timeout: BlockNumber, +} + +impl Default + for ParamsType +{ + fn default() -> Self { + Self { + active_salary: [Balance::default(); RANKS], + passive_salary: [Balance::default(); RANKS], + demotion_period: [BlockNumber::default(); RANKS], + min_promotion_period: [BlockNumber::default(); RANKS], + offboard_timeout: BlockNumber::default(), + } + } +} + +/// The status of a single member. +#[derive(Encode, Decode, Eq, PartialEq, Clone, TypeInfo, MaxEncodedLen, RuntimeDebug)] +pub struct MemberStatus { + /// Are they currently active? + is_active: bool, + /// The block number at which we last promoted them. + last_promotion: BlockNumber, + /// The last time a member was demoted, promoted or proved their rank. + last_proof: BlockNumber, +} + +#[frame_support::pallet] +pub mod pallet { + use super::*; + use frame_support::{ + dispatch::Pays, + pallet_prelude::*, + traits::{tokens::GetSalary, EnsureOrigin}, + }; + use frame_system::{ensure_root, pallet_prelude::*}; + + const RANK_COUNT: usize = 9; + + #[pallet::pallet] + #[pallet::generate_store(pub(super) trait Store)] + pub struct Pallet(PhantomData<(T, I)>); + + #[pallet::config] + pub trait Config: frame_system::Config { + /// Weight information for extrinsics in this pallet. + type WeightInfo: WeightInfo; + + /// The runtime event type. + type RuntimeEvent: From> + + IsType<::RuntimeEvent>; + + /// The current membership of the fellowship. + type Members: RankedMembers< + AccountId = ::AccountId, + Rank = u16, + >; + + /// The type in which salaries/budgets are measured. + type Balance: BalanceTrait; + + /// The origin which has permission update the parameters. + type ParamsOrigin: EnsureOrigin; + + /// The origin which has permission to move a candidate into being tracked in this pallet. + /// Generally a very low-permission, such as a pre-existing member of rank 1 or above. + /// + /// This allows the candidate to deposit evidence for their request to be promoted to a + /// member. + type InductOrigin: EnsureOrigin; + + /// The origin which has permission to issue a proof that a member may retain their rank. + /// The `Success` value is the maximum rank of members it is able to prove. + type ApproveOrigin: EnsureOrigin>; + + /// The origin which has permission to promote a member. The `Success` value is the maximum + /// rank to which it can promote. + type PromoteOrigin: EnsureOrigin>; + + /// The maximum size in bytes submitted evidence is allowed to be. + #[pallet::constant] + type EvidenceSize: Get; + } + + pub type ParamsOf = + ParamsType<>::Balance, ::BlockNumber, RANK_COUNT>; + pub type MemberStatusOf = MemberStatus<::BlockNumber>; + pub type RankOf = <>::Members as RankedMembers>::Rank; + + /// The overall status of the system. + #[pallet::storage] + pub(super) type Params, I: 'static = ()> = + StorageValue<_, ParamsOf, ValueQuery>; + + /// The status of a claimant. + #[pallet::storage] + pub(super) type Member, I: 'static = ()> = + StorageMap<_, Twox64Concat, T::AccountId, MemberStatusOf, OptionQuery>; + + /// Some evidence together with the desired outcome for which it was presented. + #[pallet::storage] + pub(super) type MemberEvidence, I: 'static = ()> = + StorageMap<_, Twox64Concat, T::AccountId, (Wish, Evidence), OptionQuery>; + + #[pallet::event] + #[pallet::generate_deposit(pub(super) fn deposit_event)] + pub enum Event, I: 'static = ()> { + /// Parameters for the pallet have changed. + ParamsChanged { params: ParamsOf }, + /// Member activity flag has been set. + ActiveChanged { who: T::AccountId, is_active: bool }, + /// Member has begun being tracked in this pallet. + Inducted { who: T::AccountId }, + /// Member has been removed from being tracked in this pallet (i.e. because rank is now + /// zero). + Offboarded { who: T::AccountId }, + /// Member has been promoted to the given rank. + Promoted { who: T::AccountId, to_rank: RankOf }, + /// Member has been demoted to the given (non-zero) rank. + Demoted { who: T::AccountId, to_rank: RankOf }, + /// Member has been proven at their current rank, postponing auto-demotion. + Proven { who: T::AccountId, at_rank: RankOf }, + /// Member has stated evidence of their efforts their request for rank. + Requested { who: T::AccountId, wish: Wish }, + /// Some submitted evidence was judged and removed. There may or may not have been a change + /// to the rank, but in any case, `last_proof` is reset. + EvidenceJudged { + /// The member/candidate. + who: T::AccountId, + /// The desired outcome for which the evidence was presented. + wish: Wish, + /// The evidence of efforts. + evidence: Evidence, + /// The old rank, prior to this change. + old_rank: u16, + /// New rank. If `None` then candidate record was removed entirely. + new_rank: Option, + }, + /// Pre-ranked account has been inducted at their current rank. + Imported { who: T::AccountId, rank: RankOf }, + } + + #[pallet::error] + pub enum Error { + /// Member's rank is too low. + Unranked, + /// Member's rank is not zero. + Ranked, + /// Member's rank is not as expected - generally means that the rank provided to the call + /// does not agree with the state of the system. + UnexpectedRank, + /// The given rank is invalid - this generally means it's not between 1 and `RANK_COUNT`. + InvalidRank, + /// The origin does not have enough permission to do this operation. + NoPermission, + /// No work needs to be done at present for this member. + NothingDoing, + /// The candidate has already been inducted. This should never happen since it would + /// require a candidate (rank 0) to already be tracked in the pallet. + AlreadyInducted, + /// The candidate has not been inducted, so cannot be offboarded from this pallet. + NotTracked, + /// Operation cannot be done yet since not enough time has passed. + TooSoon, + } + + #[pallet::call] + impl, I: 'static> Pallet { + /// Bump the state of a member. + /// + /// This will demote a member whose `last_proof` is now beyond their rank's + /// `demotion_period`. + /// + /// - `origin`: A `Signed` origin of an account. + /// - `who`: A member account whose state is to be updated. + #[pallet::weight(T::WeightInfo::bump_offboard().max(T::WeightInfo::bump_demote()))] + #[pallet::call_index(0)] + pub fn bump(origin: OriginFor, who: T::AccountId) -> DispatchResultWithPostInfo { + let _ = ensure_signed(origin)?; + let mut member = Member::::get(&who).ok_or(Error::::NotTracked)?; + let rank = T::Members::rank_of(&who).ok_or(Error::::Unranked)?; + + let params = Params::::get(); + let demotion_period = if rank == 0 { + params.offboard_timeout + } else { + let rank_index = Self::rank_to_index(rank).ok_or(Error::::InvalidRank)?; + params.demotion_period[rank_index] + }; + let demotion_block = member.last_proof.saturating_add(demotion_period); + + // Ensure enough time has passed. + let now = frame_system::Pallet::::block_number(); + if now >= demotion_block { + T::Members::demote(&who)?; + let maybe_to_rank = T::Members::rank_of(&who); + Self::dispose_evidence(who.clone(), rank, maybe_to_rank); + let event = if let Some(to_rank) = maybe_to_rank { + member.last_proof = now; + Member::::insert(&who, &member); + Event::::Demoted { who, to_rank } + } else { + Member::::remove(&who); + Event::::Offboarded { who } + }; + Self::deposit_event(event); + return Ok(Pays::No.into()) + } + + Err(Error::::NothingDoing.into()) + } + + /// Set the parameters. + /// + /// - `origin`: A origin complying with `ParamsOrigin` or root. + /// - `params`: The new parameters for the pallet. + #[pallet::weight(T::WeightInfo::set_params())] + #[pallet::call_index(1)] + pub fn set_params(origin: OriginFor, params: Box>) -> DispatchResult { + T::ParamsOrigin::try_origin(origin).map(|_| ()).or_else(|o| ensure_root(o))?; + Params::::put(params.as_ref()); + Self::deposit_event(Event::::ParamsChanged { params: *params }); + Ok(()) + } + + /// Set whether a member is active or not. + /// + /// - `origin`: A `Signed` origin of a member's account. + /// - `is_active`: `true` iff the member is active. + #[pallet::weight(T::WeightInfo::set_active())] + #[pallet::call_index(2)] + pub fn set_active(origin: OriginFor, is_active: bool) -> DispatchResult { + let who = ensure_signed(origin)?; + ensure!( + T::Members::rank_of(&who).map_or(false, |r| !r.is_zero()), + Error::::Unranked + ); + let mut member = Member::::get(&who).ok_or(Error::::NotTracked)?; + member.is_active = is_active; + Member::::insert(&who, &member); + Self::deposit_event(Event::::ActiveChanged { who, is_active }); + Ok(()) + } + + /// Approve a member to continue at their rank. + /// + /// This resets `last_proof` to the current block, thereby delaying any automatic demotion. + /// + /// If `who` is not already tracked by this pallet, then it will become tracked. + /// `last_promotion` will be set to zero. + /// + /// - `origin`: An origin which satisfies `ApproveOrigin` or root. + /// - `who`: A member (i.e. of non-zero rank). + /// - `at_rank`: The rank of member. + #[pallet::weight(T::WeightInfo::approve())] + #[pallet::call_index(3)] + pub fn approve( + origin: OriginFor, + who: T::AccountId, + at_rank: RankOf, + ) -> DispatchResult { + match T::PromoteOrigin::try_origin(origin) { + Ok(allow_rank) => ensure!(allow_rank >= at_rank, Error::::NoPermission), + Err(origin) => ensure_root(origin)?, + } + ensure!(at_rank > 0, Error::::InvalidRank); + let rank = T::Members::rank_of(&who).ok_or(Error::::Unranked)?; + ensure!(rank == at_rank, Error::::UnexpectedRank); + let mut member = Member::::get(&who).ok_or(Error::::NotTracked)?; + + member.last_proof = frame_system::Pallet::::block_number(); + Member::::insert(&who, &member); + + Self::dispose_evidence(who.clone(), at_rank, Some(at_rank)); + Self::deposit_event(Event::::Proven { who, at_rank }); + + Ok(()) + } + + /// Introduce a new and unranked candidate (rank zero). + /// + /// - `origin`: An origin which satisfies `InductOrigin` or root. + /// - `who`: The account ID of the candidate to be inducted and become a member. + #[pallet::weight(T::WeightInfo::induct())] + #[pallet::call_index(4)] + pub fn induct(origin: OriginFor, who: T::AccountId) -> DispatchResult { + match T::InductOrigin::try_origin(origin) { + Ok(_) => {}, + Err(origin) => ensure_root(origin)?, + } + ensure!(!Member::::contains_key(&who), Error::::AlreadyInducted); + ensure!(T::Members::rank_of(&who).is_none(), Error::::Ranked); + + T::Members::induct(&who)?; + let now = frame_system::Pallet::::block_number(); + Member::::insert( + &who, + MemberStatus { is_active: true, last_promotion: now, last_proof: now }, + ); + Self::deposit_event(Event::::Inducted { who }); + Ok(()) + } + + /// Increment the rank of a ranked and tracked account. + /// + /// - `origin`: An origin which satisfies `PromoteOrigin` with a `Success` result of + /// `to_rank` or more or root. + /// - `who`: The account ID of the member to be promoted to `to_rank`. + /// - `to_rank`: One more than the current rank of `who`. + #[pallet::weight(T::WeightInfo::promote())] + #[pallet::call_index(5)] + pub fn promote( + origin: OriginFor, + who: T::AccountId, + to_rank: RankOf, + ) -> DispatchResult { + match T::PromoteOrigin::try_origin(origin) { + Ok(allow_rank) => ensure!(allow_rank >= to_rank, Error::::NoPermission), + Err(origin) => ensure_root(origin)?, + } + let rank = T::Members::rank_of(&who).ok_or(Error::::Unranked)?; + ensure!( + rank.checked_add(1).map_or(false, |i| i == to_rank), + Error::::UnexpectedRank + ); + + let mut member = Member::::get(&who).ok_or(Error::::NotTracked)?; + let now = frame_system::Pallet::::block_number(); + + let params = Params::::get(); + let rank_index = Self::rank_to_index(to_rank).ok_or(Error::::InvalidRank)?; + let min_period = params.min_promotion_period[rank_index]; + // Ensure enough time has passed. + ensure!( + member.last_promotion.saturating_add(min_period) <= now, + Error::::TooSoon, + ); + + T::Members::promote(&who)?; + member.last_promotion = now; + member.last_proof = now; + Member::::insert(&who, &member); + Self::dispose_evidence(who.clone(), rank, Some(to_rank)); + + Self::deposit_event(Event::::Promoted { who, to_rank }); + + Ok(()) + } + + /// Stop tracking a prior member who is now not a ranked member of the collective. + /// + /// - `origin`: A `Signed` origin of an account. + /// - `who`: The ID of an account which was tracked in this pallet but which is now not a + /// ranked member of the collective. + #[pallet::weight(T::WeightInfo::offboard())] + #[pallet::call_index(6)] + pub fn offboard(origin: OriginFor, who: T::AccountId) -> DispatchResultWithPostInfo { + let _ = ensure_signed(origin)?; + ensure!(T::Members::rank_of(&who).is_none(), Error::::Ranked); + ensure!(Member::::contains_key(&who), Error::::NotTracked); + Member::::remove(&who); + MemberEvidence::::remove(&who); + Self::deposit_event(Event::::Offboarded { who }); + Ok(Pays::No.into()) + } + + /// Provide evidence that a rank is deserved. + /// + /// This is free as long as no evidence for the forthcoming judgement is already submitted. + /// Evidence is cleared after an outcome (either demotion, promotion of approval). + /// + /// - `origin`: A `Signed` origin of an inducted and ranked account. + /// - `wish`: The stated desire of the member. + /// - `evidence`: A dump of evidence to be considered. This should generally be either a + /// Markdown-encoded document or a series of 32-byte hashes which can be found on a + /// decentralised content-based-indexing system such as IPFS. + #[pallet::weight(T::WeightInfo::submit_evidence())] + #[pallet::call_index(7)] + pub fn submit_evidence( + origin: OriginFor, + wish: Wish, + evidence: Evidence, + ) -> DispatchResultWithPostInfo { + let who = ensure_signed(origin)?; + ensure!(Member::::contains_key(&who), Error::::NotTracked); + let replaced = MemberEvidence::::contains_key(&who); + MemberEvidence::::insert(&who, (wish, evidence)); + Self::deposit_event(Event::::Requested { who, wish }); + Ok(if replaced { Pays::Yes } else { Pays::No }.into()) + } + + /// Introduce an already-ranked individual of the collective into this pallet. The rank may + /// still be zero. + /// + /// This resets `last_proof` to the current block and `last_promotion` will be set to zero, + /// thereby delaying any automatic demotion but allowing immediate promotion. + /// + /// - `origin`: A signed origin of a ranked, but not tracked, account. + #[pallet::weight(T::WeightInfo::import())] + #[pallet::call_index(8)] + pub fn import(origin: OriginFor) -> DispatchResultWithPostInfo { + let who = ensure_signed(origin)?; + ensure!(!Member::::contains_key(&who), Error::::AlreadyInducted); + let rank = T::Members::rank_of(&who).ok_or(Error::::Unranked)?; + + let now = frame_system::Pallet::::block_number(); + Member::::insert( + &who, + MemberStatus { is_active: true, last_promotion: 0u32.into(), last_proof: now }, + ); + Self::deposit_event(Event::::Imported { who, rank }); + + Ok(Pays::No.into()) + } + } + + impl, I: 'static> Pallet { + /// Convert a rank into a `0..RANK_COUNT` index suitable for the arrays in Params. + /// + /// Rank 1 becomes index 0, rank `RANK_COUNT` becomes index `RANK_COUNT - 1`. Any rank not + /// in the range `1..=RANK_COUNT` is `None`. + pub(crate) fn rank_to_index(rank: RankOf) -> Option { + match TryInto::::try_into(rank) { + Ok(r) if r <= RANK_COUNT && r > 0 => Some(r - 1), + _ => return None, + } + } + + fn dispose_evidence(who: T::AccountId, old_rank: u16, new_rank: Option) { + if let Some((wish, evidence)) = MemberEvidence::::take(&who) { + let e = Event::::EvidenceJudged { who, wish, evidence, old_rank, new_rank }; + Self::deposit_event(e); + } + } + } + + impl, I: 'static> GetSalary, T::AccountId, T::Balance> for Pallet { + fn get_salary(rank: RankOf, who: &T::AccountId) -> T::Balance { + let index = match Self::rank_to_index(rank) { + Some(i) => i, + None => return Zero::zero(), + }; + let member = match Member::::get(who) { + Some(m) => m, + None => return Zero::zero(), + }; + let params = Params::::get(); + let salary = + if member.is_active { params.active_salary } else { params.passive_salary }; + salary[index] + } + } +} + +/// Guard to ensure that the given origin is inducted into this pallet with a given minimum rank. +/// The account ID of the member is the `Success` value. +pub struct EnsureInducted(PhantomData<(T, I)>); +impl, I: 'static, const MIN_RANK: u16> EnsureOrigin + for EnsureInducted +{ + type Success = T::AccountId; + + fn try_origin(o: T::RuntimeOrigin) -> Result { + let who = frame_system::EnsureSigned::try_origin(o)?; + match T::Members::rank_of(&who) { + Some(rank) if rank >= MIN_RANK && Member::::contains_key(&who) => Ok(who), + _ => Err(frame_system::RawOrigin::Signed(who).into()), + } + } + + #[cfg(feature = "runtime-benchmarks")] + fn try_successful_origin() -> Result { + let who = frame_benchmarking::account::("successful_origin", 0, 0); + if T::Members::rank_of(&who).is_none() { + T::Members::induct(&who).map_err(|_| ())?; + } + for _ in 0..MIN_RANK { + if T::Members::rank_of(&who).ok_or(())? < MIN_RANK { + T::Members::promote(&who).map_err(|_| ())?; + } + } + Ok(frame_system::RawOrigin::Signed(who).into()) + } +} diff --git a/frame/core-fellowship/src/tests.rs b/frame/core-fellowship/src/tests.rs new file mode 100644 index 000000000..87c0de112 --- /dev/null +++ b/frame/core-fellowship/src/tests.rs @@ -0,0 +1,362 @@ +// This file is part of Substrate. + +// Copyright (C) 2017-2022 Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +//! The crate's tests. + +use std::collections::BTreeMap; + +use frame_support::{ + assert_noop, assert_ok, ord_parameter_types, + pallet_prelude::Weight, + parameter_types, + traits::{tokens::GetSalary, ConstU32, ConstU64, Everything, IsInVec, TryMapSuccess}, +}; +use frame_system::EnsureSignedBy; +use sp_core::H256; +use sp_runtime::{ + testing::Header, + traits::{BlakeTwo256, IdentityLookup, TryMorphInto}, + DispatchError, DispatchResult, +}; +use sp_std::cell::RefCell; + +use super::*; +use crate as pallet_core_fellowship; + +type UncheckedExtrinsic = frame_system::mocking::MockUncheckedExtrinsic; +type Block = frame_system::mocking::MockBlock; + +frame_support::construct_runtime!( + pub enum Test where + Block = Block, + NodeBlock = Block, + UncheckedExtrinsic = UncheckedExtrinsic, + { + System: frame_system::{Pallet, Call, Config, Storage, Event}, + CoreFellowship: pallet_core_fellowship::{Pallet, Call, Storage, Event}, + } +); + +parameter_types! { + pub BlockWeights: frame_system::limits::BlockWeights = + frame_system::limits::BlockWeights::simple_max(Weight::from_parts(1_000_000, u64::max_value())); +} +impl frame_system::Config for Test { + type BaseCallFilter = Everything; + type BlockWeights = (); + type BlockLength = (); + type DbWeight = (); + type RuntimeOrigin = RuntimeOrigin; + type Index = u64; + type BlockNumber = u64; + type RuntimeCall = RuntimeCall; + type Hash = H256; + type Hashing = BlakeTwo256; + type AccountId = u64; + type Lookup = IdentityLookup; + type Header = Header; + type RuntimeEvent = RuntimeEvent; + type BlockHashCount = ConstU64<250>; + type Version = (); + type PalletInfo = PalletInfo; + type AccountData = (); + type OnNewAccount = (); + type OnKilledAccount = (); + type SystemWeightInfo = (); + type SS58Prefix = (); + type OnSetCode = (); + type MaxConsumers = ConstU32<16>; +} + +thread_local! { + pub static CLUB: RefCell> = RefCell::new(BTreeMap::new()); +} + +pub struct TestClub; +impl RankedMembers for TestClub { + type AccountId = u64; + type Rank = u16; + fn min_rank() -> Self::Rank { + 0 + } + fn rank_of(who: &Self::AccountId) -> Option { + CLUB.with(|club| club.borrow().get(who).cloned()) + } + fn induct(who: &Self::AccountId) -> DispatchResult { + CLUB.with(|club| club.borrow_mut().insert(*who, 0)); + Ok(()) + } + fn promote(who: &Self::AccountId) -> DispatchResult { + CLUB.with(|club| { + club.borrow_mut().entry(*who).and_modify(|r| *r += 1); + }); + Ok(()) + } + fn demote(who: &Self::AccountId) -> DispatchResult { + CLUB.with(|club| match Self::rank_of(who) { + None => Err(sp_runtime::DispatchError::Unavailable), + Some(0) => { + club.borrow_mut().remove(&who); + Ok(()) + }, + Some(_) => { + club.borrow_mut().entry(*who).and_modify(|x| *x -= 1); + Ok(()) + }, + }) + } +} + +fn set_rank(who: u64, rank: u16) { + CLUB.with(|club| club.borrow_mut().insert(who, rank)); +} + +fn unrank(who: u64) { + CLUB.with(|club| club.borrow_mut().remove(&who)); +} + +parameter_types! { + pub ZeroToNine: Vec = vec![0, 1, 2, 3, 4, 5, 6, 7, 8, 9]; +} +ord_parameter_types! { + pub const One: u64 = 1; +} + +impl Config for Test { + type WeightInfo = (); + type RuntimeEvent = RuntimeEvent; + type Members = TestClub; + type Balance = u64; + type ParamsOrigin = EnsureSignedBy; + type InductOrigin = EnsureInducted; + type ApproveOrigin = TryMapSuccess, u64>, TryMorphInto>; + type PromoteOrigin = TryMapSuccess, u64>, TryMorphInto>; + type EvidenceSize = ConstU32<1024>; +} + +pub fn new_test_ext() -> sp_io::TestExternalities { + let t = frame_system::GenesisConfig::default().build_storage::().unwrap(); + let mut ext = sp_io::TestExternalities::new(t); + ext.execute_with(|| { + let params = ParamsType { + active_salary: [10, 20, 30, 40, 50, 60, 70, 80, 90], + passive_salary: [1, 2, 3, 4, 5, 6, 7, 8, 9], + demotion_period: [2, 4, 6, 8, 10, 12, 14, 16, 18], + min_promotion_period: [3, 6, 9, 12, 15, 18, 21, 24, 27], + offboard_timeout: 1, + }; + assert_ok!(CoreFellowship::set_params(signed(1), Box::new(params))); + System::set_block_number(1); + }); + ext +} + +fn next_block() { + System::set_block_number(System::block_number() + 1); +} + +fn run_to(n: u64) { + while System::block_number() < n { + next_block(); + } +} + +fn signed(who: u64) -> RuntimeOrigin { + RuntimeOrigin::signed(who) +} + +fn next_demotion(who: u64) -> u64 { + let member = Member::::get(who).unwrap(); + let demotion_period = Params::::get().demotion_period; + member.last_proof + demotion_period[TestClub::rank_of(&who).unwrap() as usize - 1] +} + +#[test] +fn basic_stuff() { + new_test_ext().execute_with(|| { + assert_eq!(CoreFellowship::rank_to_index(0), None); + assert_eq!(CoreFellowship::rank_to_index(1), Some(0)); + assert_eq!(CoreFellowship::rank_to_index(9), Some(8)); + assert_eq!(CoreFellowship::rank_to_index(10), None); + assert_eq!(CoreFellowship::get_salary(0, &1), 0); + }); +} + +#[test] +fn set_params_works() { + new_test_ext().execute_with(|| { + let params = ParamsType { + active_salary: [10, 20, 30, 40, 50, 60, 70, 80, 90], + passive_salary: [1, 2, 3, 4, 5, 6, 7, 8, 9], + demotion_period: [1, 2, 3, 4, 5, 6, 7, 8, 9], + min_promotion_period: [1, 2, 3, 4, 5, 10, 15, 20, 30], + offboard_timeout: 1, + }; + assert_noop!( + CoreFellowship::set_params(signed(2), Box::new(params.clone())), + DispatchError::BadOrigin + ); + assert_ok!(CoreFellowship::set_params(signed(1), Box::new(params))); + }); +} + +#[test] +fn induct_works() { + new_test_ext().execute_with(|| { + set_rank(0, 0); + assert_ok!(CoreFellowship::import(signed(0))); + set_rank(1, 1); + assert_ok!(CoreFellowship::import(signed(1))); + + assert_noop!(CoreFellowship::induct(signed(10), 10), DispatchError::BadOrigin); + assert_noop!(CoreFellowship::induct(signed(0), 10), DispatchError::BadOrigin); + assert_ok!(CoreFellowship::induct(signed(1), 10)); + assert_noop!(CoreFellowship::induct(signed(1), 10), Error::::AlreadyInducted); + }); +} + +#[test] +fn promote_works() { + new_test_ext().execute_with(|| { + set_rank(1, 1); + assert_ok!(CoreFellowship::import(signed(1))); + assert_noop!(CoreFellowship::promote(signed(1), 10, 1), Error::::Unranked); + + assert_ok!(CoreFellowship::induct(signed(1), 10)); + assert_noop!(CoreFellowship::promote(signed(10), 10, 1), DispatchError::BadOrigin); + assert_noop!(CoreFellowship::promote(signed(0), 10, 1), Error::::NoPermission); + assert_noop!(CoreFellowship::promote(signed(3), 10, 2), Error::::UnexpectedRank); + run_to(3); + assert_noop!(CoreFellowship::promote(signed(1), 10, 1), Error::::TooSoon); + run_to(4); + assert_ok!(CoreFellowship::promote(signed(1), 10, 1)); + set_rank(11, 0); + assert_noop!(CoreFellowship::promote(signed(1), 11, 1), Error::::NotTracked); + }); +} + +#[test] +fn sync_works() { + new_test_ext().execute_with(|| { + set_rank(10, 5); + assert_noop!(CoreFellowship::approve(signed(4), 10, 5), Error::::NoPermission); + assert_noop!(CoreFellowship::approve(signed(6), 10, 6), Error::::UnexpectedRank); + assert_ok!(CoreFellowship::import(signed(10))); + assert!(Member::::contains_key(10)); + assert_eq!(next_demotion(10), 11); + }); +} + +#[test] +fn auto_demote_works() { + new_test_ext().execute_with(|| { + set_rank(10, 5); + assert_ok!(CoreFellowship::import(signed(10))); + + run_to(10); + assert_noop!(CoreFellowship::bump(signed(0), 10), Error::::NothingDoing); + run_to(11); + assert_ok!(CoreFellowship::bump(signed(0), 10)); + assert_eq!(TestClub::rank_of(&10), Some(4)); + assert_noop!(CoreFellowship::bump(signed(0), 10), Error::::NothingDoing); + assert_eq!(next_demotion(10), 19); + }); +} + +#[test] +fn auto_demote_offboard_works() { + new_test_ext().execute_with(|| { + set_rank(10, 1); + assert_ok!(CoreFellowship::import(signed(10))); + + run_to(3); + assert_ok!(CoreFellowship::bump(signed(0), 10)); + assert_eq!(TestClub::rank_of(&10), Some(0)); + assert_noop!(CoreFellowship::bump(signed(0), 10), Error::::NothingDoing); + run_to(4); + assert_ok!(CoreFellowship::bump(signed(0), 10)); + assert_noop!(CoreFellowship::bump(signed(0), 10), Error::::NotTracked); + }); +} + +#[test] +fn offboard_works() { + new_test_ext().execute_with(|| { + assert_noop!(CoreFellowship::offboard(signed(0), 10), Error::::NotTracked); + set_rank(10, 0); + assert_noop!(CoreFellowship::offboard(signed(0), 10), Error::::Ranked); + + assert_ok!(CoreFellowship::import(signed(10))); + assert_noop!(CoreFellowship::offboard(signed(0), 10), Error::::Ranked); + + unrank(10); + assert_ok!(CoreFellowship::offboard(signed(0), 10)); + assert_noop!(CoreFellowship::offboard(signed(0), 10), Error::::NotTracked); + assert_noop!(CoreFellowship::bump(signed(0), 10), Error::::NotTracked); + }); +} + +#[test] +fn proof_postpones_auto_demote() { + new_test_ext().execute_with(|| { + set_rank(10, 5); + assert_ok!(CoreFellowship::import(signed(10))); + + run_to(11); + assert_ok!(CoreFellowship::approve(signed(5), 10, 5)); + assert_eq!(next_demotion(10), 21); + assert_noop!(CoreFellowship::bump(signed(0), 10), Error::::NothingDoing); + }); +} + +#[test] +fn promote_postpones_auto_demote() { + new_test_ext().execute_with(|| { + set_rank(10, 5); + assert_ok!(CoreFellowship::import(signed(10))); + + run_to(19); + assert_ok!(CoreFellowship::promote(signed(6), 10, 6)); + assert_eq!(next_demotion(10), 31); + assert_noop!(CoreFellowship::bump(signed(0), 10), Error::::NothingDoing); + }); +} + +#[test] +fn get_salary_works() { + new_test_ext().execute_with(|| { + for i in 1..=9u64 { + set_rank(10 + i, i as u16); + assert_ok!(CoreFellowship::import(signed(10 + i))); + assert_eq!(CoreFellowship::get_salary(i as u16, &(10 + i)), i * 10); + } + }); +} + +#[test] +fn active_changing_get_salary_works() { + new_test_ext().execute_with(|| { + for i in 1..=9u64 { + set_rank(10 + i, i as u16); + assert_ok!(CoreFellowship::import(signed(10 + i))); + assert_ok!(CoreFellowship::set_active(signed(10 + i), false)); + assert_eq!(CoreFellowship::get_salary(i as u16, &(10 + i)), i); + assert_ok!(CoreFellowship::set_active(signed(10 + i), true)); + assert_eq!(CoreFellowship::get_salary(i as u16, &(10 + i)), i * 10); + } + }); +} diff --git a/frame/core-fellowship/src/weights.rs b/frame/core-fellowship/src/weights.rs new file mode 100644 index 000000000..9f290635e --- /dev/null +++ b/frame/core-fellowship/src/weights.rs @@ -0,0 +1,393 @@ +// This file is part of Substrate. + +// Copyright (C) Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +//! Autogenerated weights for pallet_core_fellowship +//! +//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev +//! DATE: 2023-03-05, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! WORST CASE MAP SIZE: `1000000` +//! HOSTNAME: `bm3`, CPU: `Intel(R) Core(TM) i7-7700K CPU @ 4.20GHz` +//! EXECUTION: Some(Wasm), WASM-EXECUTION: Compiled, CHAIN: Some("dev"), DB CACHE: 1024 + +// Executed Command: +// target/production/substrate +// benchmark +// pallet +// --steps=50 +// --repeat=20 +// --extrinsic=* +// --execution=wasm +// --wasm-execution=compiled +// --heap-pages=4096 +// --json-file=/var/lib/gitlab-runner/builds/zyw4fam_/0/parity/mirrors/substrate/.git/.artifacts/bench.json +// --pallet=pallet_core_fellowship +// --chain=dev +// --header=./HEADER-APACHE2 +// --output=./frame/core-fellowship/src/weights.rs +// --template=./.maintain/frame-weight-template.hbs + +#![cfg_attr(rustfmt, rustfmt_skip)] +#![allow(unused_parens)] +#![allow(unused_imports)] + +use frame_support::{traits::Get, weights::{Weight, constants::RocksDbWeight}}; +use sp_std::marker::PhantomData; + +/// Weight functions needed for pallet_core_fellowship. +pub trait WeightInfo { + fn set_params() -> Weight; + fn bump_offboard() -> Weight; + fn bump_demote() -> Weight; + fn set_active() -> Weight; + fn induct() -> Weight; + fn promote() -> Weight; + fn offboard() -> Weight; + fn import() -> Weight; + fn approve() -> Weight; + fn submit_evidence() -> Weight; +} + +/// Weights for pallet_core_fellowship using the Substrate node and recommended hardware. +pub struct SubstrateWeight(PhantomData); +impl WeightInfo for SubstrateWeight { + /// Storage: CoreFellowship Params (r:0 w:1) + /// Proof: CoreFellowship Params (max_values: Some(1), max_size: Some(364), added: 859, mode: MaxEncodedLen) + fn set_params() -> Weight { + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 10_489_000 picoseconds. + Weight::from_parts(10_873_000, 0) + .saturating_add(T::DbWeight::get().writes(1_u64)) + } + /// Storage: CoreFellowship Member (r:1 w:1) + /// Proof: CoreFellowship Member (max_values: None, max_size: Some(49), added: 2524, mode: MaxEncodedLen) + /// Storage: RankedCollective Members (r:1 w:1) + /// Proof: RankedCollective Members (max_values: None, max_size: Some(42), added: 2517, mode: MaxEncodedLen) + /// Storage: CoreFellowship Params (r:1 w:0) + /// Proof: CoreFellowship Params (max_values: Some(1), max_size: Some(364), added: 859, mode: MaxEncodedLen) + /// Storage: RankedCollective MemberCount (r:1 w:1) + /// Proof: RankedCollective MemberCount (max_values: None, max_size: Some(14), added: 2489, mode: MaxEncodedLen) + /// Storage: RankedCollective IdToIndex (r:1 w:0) + /// Proof: RankedCollective IdToIndex (max_values: None, max_size: Some(54), added: 2529, mode: MaxEncodedLen) + /// Storage: CoreFellowship MemberEvidence (r:1 w:1) + /// Proof: CoreFellowship MemberEvidence (max_values: None, max_size: Some(16429), added: 18904, mode: MaxEncodedLen) + fn bump_offboard() -> Weight { + // Proof Size summary in bytes: + // Measured: `16886` + // Estimated: `35762` + // Minimum execution time: 61_794_000 picoseconds. + Weight::from_parts(62_455_000, 35762) + .saturating_add(T::DbWeight::get().reads(6_u64)) + .saturating_add(T::DbWeight::get().writes(4_u64)) + } + /// Storage: CoreFellowship Member (r:1 w:1) + /// Proof: CoreFellowship Member (max_values: None, max_size: Some(49), added: 2524, mode: MaxEncodedLen) + /// Storage: RankedCollective Members (r:1 w:1) + /// Proof: RankedCollective Members (max_values: None, max_size: Some(42), added: 2517, mode: MaxEncodedLen) + /// Storage: CoreFellowship Params (r:1 w:0) + /// Proof: CoreFellowship Params (max_values: Some(1), max_size: Some(364), added: 859, mode: MaxEncodedLen) + /// Storage: RankedCollective MemberCount (r:1 w:1) + /// Proof: RankedCollective MemberCount (max_values: None, max_size: Some(14), added: 2489, mode: MaxEncodedLen) + /// Storage: RankedCollective IdToIndex (r:1 w:0) + /// Proof: RankedCollective IdToIndex (max_values: None, max_size: Some(54), added: 2529, mode: MaxEncodedLen) + /// Storage: CoreFellowship MemberEvidence (r:1 w:1) + /// Proof: CoreFellowship MemberEvidence (max_values: None, max_size: Some(16429), added: 18904, mode: MaxEncodedLen) + fn bump_demote() -> Weight { + // Proof Size summary in bytes: + // Measured: `16996` + // Estimated: `35762` + // Minimum execution time: 63_921_000 picoseconds. + Weight::from_parts(64_871_000, 35762) + .saturating_add(T::DbWeight::get().reads(6_u64)) + .saturating_add(T::DbWeight::get().writes(4_u64)) + } + /// Storage: RankedCollective Members (r:1 w:0) + /// Proof: RankedCollective Members (max_values: None, max_size: Some(42), added: 2517, mode: MaxEncodedLen) + /// Storage: CoreFellowship Member (r:1 w:1) + /// Proof: CoreFellowship Member (max_values: None, max_size: Some(49), added: 2524, mode: MaxEncodedLen) + fn set_active() -> Weight { + // Proof Size summary in bytes: + // Measured: `355` + // Estimated: `7021` + // Minimum execution time: 19_023_000 picoseconds. + Weight::from_parts(19_270_000, 7021) + .saturating_add(T::DbWeight::get().reads(2_u64)) + .saturating_add(T::DbWeight::get().writes(1_u64)) + } + /// Storage: CoreFellowship Member (r:1 w:1) + /// Proof: CoreFellowship Member (max_values: None, max_size: Some(49), added: 2524, mode: MaxEncodedLen) + /// Storage: RankedCollective Members (r:1 w:1) + /// Proof: RankedCollective Members (max_values: None, max_size: Some(42), added: 2517, mode: MaxEncodedLen) + /// Storage: RankedCollective MemberCount (r:1 w:1) + /// Proof: RankedCollective MemberCount (max_values: None, max_size: Some(14), added: 2489, mode: MaxEncodedLen) + /// Storage: RankedCollective IndexToId (r:0 w:1) + /// Proof: RankedCollective IndexToId (max_values: None, max_size: Some(54), added: 2529, mode: MaxEncodedLen) + /// Storage: RankedCollective IdToIndex (r:0 w:1) + /// Proof: RankedCollective IdToIndex (max_values: None, max_size: Some(54), added: 2529, mode: MaxEncodedLen) + fn induct() -> Weight { + // Proof Size summary in bytes: + // Measured: `113` + // Estimated: `10500` + // Minimum execution time: 29_089_000 picoseconds. + Weight::from_parts(29_718_000, 10500) + .saturating_add(T::DbWeight::get().reads(3_u64)) + .saturating_add(T::DbWeight::get().writes(5_u64)) + } + /// Storage: RankedCollective Members (r:1 w:1) + /// Proof: RankedCollective Members (max_values: None, max_size: Some(42), added: 2517, mode: MaxEncodedLen) + /// Storage: CoreFellowship Member (r:1 w:1) + /// Proof: CoreFellowship Member (max_values: None, max_size: Some(49), added: 2524, mode: MaxEncodedLen) + /// Storage: CoreFellowship Params (r:1 w:0) + /// Proof: CoreFellowship Params (max_values: Some(1), max_size: Some(364), added: 859, mode: MaxEncodedLen) + /// Storage: RankedCollective MemberCount (r:1 w:1) + /// Proof: RankedCollective MemberCount (max_values: None, max_size: Some(14), added: 2489, mode: MaxEncodedLen) + /// Storage: CoreFellowship MemberEvidence (r:1 w:1) + /// Proof: CoreFellowship MemberEvidence (max_values: None, max_size: Some(16429), added: 18904, mode: MaxEncodedLen) + /// Storage: RankedCollective IndexToId (r:0 w:1) + /// Proof: RankedCollective IndexToId (max_values: None, max_size: Some(54), added: 2529, mode: MaxEncodedLen) + /// Storage: RankedCollective IdToIndex (r:0 w:1) + /// Proof: RankedCollective IdToIndex (max_values: None, max_size: Some(54), added: 2529, mode: MaxEncodedLen) + fn promote() -> Weight { + // Proof Size summary in bytes: + // Measured: `16864` + // Estimated: `32243` + // Minimum execution time: 59_529_000 picoseconds. + Weight::from_parts(60_057_000, 32243) + .saturating_add(T::DbWeight::get().reads(5_u64)) + .saturating_add(T::DbWeight::get().writes(6_u64)) + } + /// Storage: RankedCollective Members (r:1 w:0) + /// Proof: RankedCollective Members (max_values: None, max_size: Some(42), added: 2517, mode: MaxEncodedLen) + /// Storage: CoreFellowship Member (r:1 w:1) + /// Proof: CoreFellowship Member (max_values: None, max_size: Some(49), added: 2524, mode: MaxEncodedLen) + fn offboard() -> Weight { + // Proof Size summary in bytes: + // Measured: `292` + // Estimated: `7021` + // Minimum execution time: 17_221_000 picoseconds. + Weight::from_parts(17_585_000, 7021) + .saturating_add(T::DbWeight::get().reads(2_u64)) + .saturating_add(T::DbWeight::get().writes(1_u64)) + } + /// Storage: CoreFellowship Member (r:1 w:1) + /// Proof: CoreFellowship Member (max_values: None, max_size: Some(49), added: 2524, mode: MaxEncodedLen) + /// Storage: RankedCollective Members (r:1 w:0) + /// Proof: RankedCollective Members (max_values: None, max_size: Some(42), added: 2517, mode: MaxEncodedLen) + fn import() -> Weight { + // Proof Size summary in bytes: + // Measured: `280` + // Estimated: `7021` + // Minimum execution time: 18_602_000 picoseconds. + Weight::from_parts(18_813_000, 7021) + .saturating_add(T::DbWeight::get().reads(2_u64)) + .saturating_add(T::DbWeight::get().writes(1_u64)) + } + /// Storage: RankedCollective Members (r:1 w:0) + /// Proof: RankedCollective Members (max_values: None, max_size: Some(42), added: 2517, mode: MaxEncodedLen) + /// Storage: CoreFellowship Member (r:1 w:1) + /// Proof: CoreFellowship Member (max_values: None, max_size: Some(49), added: 2524, mode: MaxEncodedLen) + /// Storage: CoreFellowship MemberEvidence (r:1 w:1) + /// Proof: CoreFellowship MemberEvidence (max_values: None, max_size: Some(16429), added: 18904, mode: MaxEncodedLen) + fn approve() -> Weight { + // Proof Size summary in bytes: + // Measured: `16842` + // Estimated: `26915` + // Minimum execution time: 43_525_000 picoseconds. + Weight::from_parts(43_994_000, 26915) + .saturating_add(T::DbWeight::get().reads(3_u64)) + .saturating_add(T::DbWeight::get().writes(2_u64)) + } + /// Storage: CoreFellowship Member (r:1 w:0) + /// Proof: CoreFellowship Member (max_values: None, max_size: Some(49), added: 2524, mode: MaxEncodedLen) + /// Storage: CoreFellowship MemberEvidence (r:1 w:1) + /// Proof: CoreFellowship MemberEvidence (max_values: None, max_size: Some(16429), added: 18904, mode: MaxEncodedLen) + fn submit_evidence() -> Weight { + // Proof Size summary in bytes: + // Measured: `79` + // Estimated: `23408` + // Minimum execution time: 27_960_000 picoseconds. + Weight::from_parts(28_331_000, 23408) + .saturating_add(T::DbWeight::get().reads(2_u64)) + .saturating_add(T::DbWeight::get().writes(1_u64)) + } +} + +// For backwards compatibility and tests +impl WeightInfo for () { + /// Storage: CoreFellowship Params (r:0 w:1) + /// Proof: CoreFellowship Params (max_values: Some(1), max_size: Some(364), added: 859, mode: MaxEncodedLen) + fn set_params() -> Weight { + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 10_489_000 picoseconds. + Weight::from_parts(10_873_000, 0) + .saturating_add(RocksDbWeight::get().writes(1_u64)) + } + /// Storage: CoreFellowship Member (r:1 w:1) + /// Proof: CoreFellowship Member (max_values: None, max_size: Some(49), added: 2524, mode: MaxEncodedLen) + /// Storage: RankedCollective Members (r:1 w:1) + /// Proof: RankedCollective Members (max_values: None, max_size: Some(42), added: 2517, mode: MaxEncodedLen) + /// Storage: CoreFellowship Params (r:1 w:0) + /// Proof: CoreFellowship Params (max_values: Some(1), max_size: Some(364), added: 859, mode: MaxEncodedLen) + /// Storage: RankedCollective MemberCount (r:1 w:1) + /// Proof: RankedCollective MemberCount (max_values: None, max_size: Some(14), added: 2489, mode: MaxEncodedLen) + /// Storage: RankedCollective IdToIndex (r:1 w:0) + /// Proof: RankedCollective IdToIndex (max_values: None, max_size: Some(54), added: 2529, mode: MaxEncodedLen) + /// Storage: CoreFellowship MemberEvidence (r:1 w:1) + /// Proof: CoreFellowship MemberEvidence (max_values: None, max_size: Some(16429), added: 18904, mode: MaxEncodedLen) + fn bump_offboard() -> Weight { + // Proof Size summary in bytes: + // Measured: `16886` + // Estimated: `35762` + // Minimum execution time: 61_794_000 picoseconds. + Weight::from_parts(62_455_000, 35762) + .saturating_add(RocksDbWeight::get().reads(6_u64)) + .saturating_add(RocksDbWeight::get().writes(4_u64)) + } + /// Storage: CoreFellowship Member (r:1 w:1) + /// Proof: CoreFellowship Member (max_values: None, max_size: Some(49), added: 2524, mode: MaxEncodedLen) + /// Storage: RankedCollective Members (r:1 w:1) + /// Proof: RankedCollective Members (max_values: None, max_size: Some(42), added: 2517, mode: MaxEncodedLen) + /// Storage: CoreFellowship Params (r:1 w:0) + /// Proof: CoreFellowship Params (max_values: Some(1), max_size: Some(364), added: 859, mode: MaxEncodedLen) + /// Storage: RankedCollective MemberCount (r:1 w:1) + /// Proof: RankedCollective MemberCount (max_values: None, max_size: Some(14), added: 2489, mode: MaxEncodedLen) + /// Storage: RankedCollective IdToIndex (r:1 w:0) + /// Proof: RankedCollective IdToIndex (max_values: None, max_size: Some(54), added: 2529, mode: MaxEncodedLen) + /// Storage: CoreFellowship MemberEvidence (r:1 w:1) + /// Proof: CoreFellowship MemberEvidence (max_values: None, max_size: Some(16429), added: 18904, mode: MaxEncodedLen) + fn bump_demote() -> Weight { + // Proof Size summary in bytes: + // Measured: `16996` + // Estimated: `35762` + // Minimum execution time: 63_921_000 picoseconds. + Weight::from_parts(64_871_000, 35762) + .saturating_add(RocksDbWeight::get().reads(6_u64)) + .saturating_add(RocksDbWeight::get().writes(4_u64)) + } + /// Storage: RankedCollective Members (r:1 w:0) + /// Proof: RankedCollective Members (max_values: None, max_size: Some(42), added: 2517, mode: MaxEncodedLen) + /// Storage: CoreFellowship Member (r:1 w:1) + /// Proof: CoreFellowship Member (max_values: None, max_size: Some(49), added: 2524, mode: MaxEncodedLen) + fn set_active() -> Weight { + // Proof Size summary in bytes: + // Measured: `355` + // Estimated: `7021` + // Minimum execution time: 19_023_000 picoseconds. + Weight::from_parts(19_270_000, 7021) + .saturating_add(RocksDbWeight::get().reads(2_u64)) + .saturating_add(RocksDbWeight::get().writes(1_u64)) + } + /// Storage: CoreFellowship Member (r:1 w:1) + /// Proof: CoreFellowship Member (max_values: None, max_size: Some(49), added: 2524, mode: MaxEncodedLen) + /// Storage: RankedCollective Members (r:1 w:1) + /// Proof: RankedCollective Members (max_values: None, max_size: Some(42), added: 2517, mode: MaxEncodedLen) + /// Storage: RankedCollective MemberCount (r:1 w:1) + /// Proof: RankedCollective MemberCount (max_values: None, max_size: Some(14), added: 2489, mode: MaxEncodedLen) + /// Storage: RankedCollective IndexToId (r:0 w:1) + /// Proof: RankedCollective IndexToId (max_values: None, max_size: Some(54), added: 2529, mode: MaxEncodedLen) + /// Storage: RankedCollective IdToIndex (r:0 w:1) + /// Proof: RankedCollective IdToIndex (max_values: None, max_size: Some(54), added: 2529, mode: MaxEncodedLen) + fn induct() -> Weight { + // Proof Size summary in bytes: + // Measured: `113` + // Estimated: `10500` + // Minimum execution time: 29_089_000 picoseconds. + Weight::from_parts(29_718_000, 10500) + .saturating_add(RocksDbWeight::get().reads(3_u64)) + .saturating_add(RocksDbWeight::get().writes(5_u64)) + } + /// Storage: RankedCollective Members (r:1 w:1) + /// Proof: RankedCollective Members (max_values: None, max_size: Some(42), added: 2517, mode: MaxEncodedLen) + /// Storage: CoreFellowship Member (r:1 w:1) + /// Proof: CoreFellowship Member (max_values: None, max_size: Some(49), added: 2524, mode: MaxEncodedLen) + /// Storage: CoreFellowship Params (r:1 w:0) + /// Proof: CoreFellowship Params (max_values: Some(1), max_size: Some(364), added: 859, mode: MaxEncodedLen) + /// Storage: RankedCollective MemberCount (r:1 w:1) + /// Proof: RankedCollective MemberCount (max_values: None, max_size: Some(14), added: 2489, mode: MaxEncodedLen) + /// Storage: CoreFellowship MemberEvidence (r:1 w:1) + /// Proof: CoreFellowship MemberEvidence (max_values: None, max_size: Some(16429), added: 18904, mode: MaxEncodedLen) + /// Storage: RankedCollective IndexToId (r:0 w:1) + /// Proof: RankedCollective IndexToId (max_values: None, max_size: Some(54), added: 2529, mode: MaxEncodedLen) + /// Storage: RankedCollective IdToIndex (r:0 w:1) + /// Proof: RankedCollective IdToIndex (max_values: None, max_size: Some(54), added: 2529, mode: MaxEncodedLen) + fn promote() -> Weight { + // Proof Size summary in bytes: + // Measured: `16864` + // Estimated: `32243` + // Minimum execution time: 59_529_000 picoseconds. + Weight::from_parts(60_057_000, 32243) + .saturating_add(RocksDbWeight::get().reads(5_u64)) + .saturating_add(RocksDbWeight::get().writes(6_u64)) + } + /// Storage: RankedCollective Members (r:1 w:0) + /// Proof: RankedCollective Members (max_values: None, max_size: Some(42), added: 2517, mode: MaxEncodedLen) + /// Storage: CoreFellowship Member (r:1 w:1) + /// Proof: CoreFellowship Member (max_values: None, max_size: Some(49), added: 2524, mode: MaxEncodedLen) + fn offboard() -> Weight { + // Proof Size summary in bytes: + // Measured: `292` + // Estimated: `7021` + // Minimum execution time: 17_221_000 picoseconds. + Weight::from_parts(17_585_000, 7021) + .saturating_add(RocksDbWeight::get().reads(2_u64)) + .saturating_add(RocksDbWeight::get().writes(1_u64)) + } + /// Storage: CoreFellowship Member (r:1 w:1) + /// Proof: CoreFellowship Member (max_values: None, max_size: Some(49), added: 2524, mode: MaxEncodedLen) + /// Storage: RankedCollective Members (r:1 w:0) + /// Proof: RankedCollective Members (max_values: None, max_size: Some(42), added: 2517, mode: MaxEncodedLen) + fn import() -> Weight { + // Proof Size summary in bytes: + // Measured: `280` + // Estimated: `7021` + // Minimum execution time: 18_602_000 picoseconds. + Weight::from_parts(18_813_000, 7021) + .saturating_add(RocksDbWeight::get().reads(2_u64)) + .saturating_add(RocksDbWeight::get().writes(1_u64)) + } + /// Storage: RankedCollective Members (r:1 w:0) + /// Proof: RankedCollective Members (max_values: None, max_size: Some(42), added: 2517, mode: MaxEncodedLen) + /// Storage: CoreFellowship Member (r:1 w:1) + /// Proof: CoreFellowship Member (max_values: None, max_size: Some(49), added: 2524, mode: MaxEncodedLen) + /// Storage: CoreFellowship MemberEvidence (r:1 w:1) + /// Proof: CoreFellowship MemberEvidence (max_values: None, max_size: Some(16429), added: 18904, mode: MaxEncodedLen) + fn approve() -> Weight { + // Proof Size summary in bytes: + // Measured: `16842` + // Estimated: `26915` + // Minimum execution time: 43_525_000 picoseconds. + Weight::from_parts(43_994_000, 26915) + .saturating_add(RocksDbWeight::get().reads(3_u64)) + .saturating_add(RocksDbWeight::get().writes(2_u64)) + } + /// Storage: CoreFellowship Member (r:1 w:0) + /// Proof: CoreFellowship Member (max_values: None, max_size: Some(49), added: 2524, mode: MaxEncodedLen) + /// Storage: CoreFellowship MemberEvidence (r:1 w:1) + /// Proof: CoreFellowship MemberEvidence (max_values: None, max_size: Some(16429), added: 18904, mode: MaxEncodedLen) + fn submit_evidence() -> Weight { + // Proof Size summary in bytes: + // Measured: `79` + // Estimated: `23408` + // Minimum execution time: 27_960_000 picoseconds. + Weight::from_parts(28_331_000, 23408) + .saturating_add(RocksDbWeight::get().reads(2_u64)) + .saturating_add(RocksDbWeight::get().writes(1_u64)) + } +} diff --git a/frame/salary/src/lib.rs b/frame/salary/src/lib.rs index 2169952f1..5a4ff8a66 100644 --- a/frame/salary/src/lib.rs +++ b/frame/salary/src/lib.rs @@ -82,8 +82,8 @@ pub trait Pay { /// after this call. Used in benchmarking code. #[cfg(feature = "runtime-benchmarks")] fn ensure_successful(who: &Self::AccountId, amount: Self::Balance); - /// Ensure that a call to check_payment with the given parameters will return either Success - /// or Failure. + /// Ensure that a call to `check_payment` with the given parameters will return either `Success` + /// or `Failure`. #[cfg(feature = "runtime-benchmarks")] fn ensure_concluded(id: Self::Id); } diff --git a/frame/system/src/lib.rs b/frame/system/src/lib.rs index 5e921ab85..0ec808d4c 100644 --- a/frame/system/src/lib.rs +++ b/frame/system/src/lib.rs @@ -878,12 +878,9 @@ impl< #[cfg(feature = "runtime-benchmarks")] fn try_successful_origin() -> Result { - let zero_account_id = - AccountId::decode(&mut TrailingZeroInput::zeroes()).map_err(|_| ())?; - let members = Who::sorted_members(); - let first_member = match members.get(0) { + let first_member = match Who::sorted_members().first() { Some(account) => account.clone(), - None => zero_account_id, + None => AccountId::decode(&mut TrailingZeroInput::zeroes()).map_err(|_| ())?, }; Ok(O::from(RawOrigin::Signed(first_member))) } diff --git a/primitives/arithmetic/src/traits.rs b/primitives/arithmetic/src/traits.rs index 84e3f60aa..33b4e376a 100644 --- a/primitives/arithmetic/src/traits.rs +++ b/primitives/arithmetic/src/traits.rs @@ -148,6 +148,20 @@ impl< { } +/// A meta trait for arithmetic. +/// +/// Arithmetic types do all the usual stuff you'd expect numbers to do. They are guaranteed to +/// be able to represent at least `u8` values without loss, hence the trait implies `From` +/// and smaller integers. All other conversions are fallible. +pub trait AtLeast8Bit: BaseArithmetic + From {} + +impl> AtLeast8Bit for T {} + +/// A meta trait for arithmetic. Same as [`AtLeast8Bit `], but also bounded to be unsigned. +pub trait AtLeast8BitUnsigned: AtLeast8Bit + Unsigned {} + +impl AtLeast8BitUnsigned for T {} + /// A meta trait for arithmetic. /// /// Arithmetic types do all the usual stuff you'd expect numbers to do. They are guaranteed to @@ -220,6 +234,24 @@ pub trait Saturating { /// instead of overflowing. fn saturating_pow(self, exp: usize) -> Self; + /// Decrement self by one, saturating at zero. + fn saturating_less_one(mut self) -> Self + where + Self: One, + { + self.saturating_dec(); + self + } + + /// Increment self by one, saturating at the numeric bounds instead of overflowing. + fn saturating_plus_one(mut self) -> Self + where + Self: One, + { + self.saturating_inc(); + self + } + /// Increment self by one, saturating. fn saturating_inc(&mut self) where diff --git a/primitives/runtime/src/traits.rs b/primitives/runtime/src/traits.rs index e853cad61..4bba6f6bc 100644 --- a/primitives/runtime/src/traits.rs +++ b/primitives/runtime/src/traits.rs @@ -319,6 +319,24 @@ impl TryMorph for Identity { } } +/// Implementation of `Morph` which converts between types using `Into`. +pub struct MorphInto(sp_std::marker::PhantomData); +impl> Morph for MorphInto { + type Outcome = T; + fn morph(a: A) -> T { + a.into() + } +} + +/// Implementation of `TryMorph` which attmepts to convert between types using `TryInto`. +pub struct TryMorphInto(sp_std::marker::PhantomData); +impl> TryMorph for TryMorphInto { + type Outcome = T; + fn try_morph(a: A) -> Result { + a.try_into().map_err(|_| ()) + } +} + /// Create a `Morph` and/or `TryMorph` impls with a simple closure-like expression. /// /// # Examples From 3c3b223be82d91be85b3f3cc8305e444b2ca37fa Mon Sep 17 00:00:00 2001 From: Mira Ressel Date: Sat, 11 Mar 2023 17:04:54 +0100 Subject: [PATCH 221/558] Fix dependabot labels (#13578) * dependabot: fix label names * dependabot: apply E2-dependencies label for Cargo.lock changes --- .github/dependabot.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/dependabot.yml b/.github/dependabot.yml index cca9219e6..04cf0d1e1 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -2,11 +2,11 @@ version: 2 updates: - package-ecosystem: "cargo" directory: "/" - labels: ["A2-insubstantial", "B0-silent", "C1-low 📌"] + labels: ["A2-insubstantial", "B0-silent", "C1-low", "E2-dependencies"] schedule: interval: "daily" - package-ecosystem: github-actions directory: '/' - labels: ["A2-insubstantial", "B0-silent", "C1-low 📌", "E3-dependencies"] + labels: ["A2-insubstantial", "B0-silent", "C1-low", "E2-dependencies"] schedule: interval: daily From 310fe4095bff177ddcfa660ac0ab06171a25a9c2 Mon Sep 17 00:00:00 2001 From: Davide Galassi Date: Sat, 11 Mar 2023 19:05:55 +0100 Subject: [PATCH 222/558] BEEFY: introduce offence report system (#13564) * Trivial adjustments to beefy and grandpa pallets * Introduce offence report system to beefy pallet * Minor adjustments * Fix beefy-mmr mock * Apply suggestions from code review Co-authored-by: Anton --------- Co-authored-by: Anton --- frame/babe/src/equivocation.rs | 14 +- frame/beefy-mmr/src/mock.rs | 14 +- frame/beefy/src/equivocation.rs | 412 ++++++++++--------------- frame/beefy/src/lib.rs | 130 ++------ frame/beefy/src/mock.rs | 12 +- frame/grandpa/src/equivocation.rs | 33 +- frame/grandpa/src/lib.rs | 3 +- frame/grandpa/src/mock.rs | 3 - frame/grandpa/src/tests.rs | 12 +- frame/offences/benchmarking/src/lib.rs | 4 +- 10 files changed, 233 insertions(+), 404 deletions(-) diff --git a/frame/babe/src/equivocation.rs b/frame/babe/src/equivocation.rs index ed98385a7..3a14cacc9 100644 --- a/frame/babe/src/equivocation.rs +++ b/frame/babe/src/equivocation.rs @@ -95,14 +95,16 @@ impl Offence for EquivocationOffence { } } -/// Babe equivocation offence system. +/// BABE equivocation offence report system. /// -/// This type implements `OffenceReportSystem` +/// This type implements `OffenceReportSystem` such that: +/// - Equivocation reports are published on-chain as unsigned extrinsic via +/// `offchain::SendTransactioinsTypes`. +/// - On-chain validity checks and processing are mostly delegated to the user provided generic +/// types implementing `KeyOwnerProofSystem` and `ReportOffence` traits. +/// - Offence reporter for unsigned transactions is fetched via the the authorship pallet. pub struct EquivocationReportSystem(sp_std::marker::PhantomData<(T, R, P, L)>); -// We use the authorship pallet to fetch the current block author and use -// `offchain::SendTransactionTypes` for unsigned extrinsic creation and -// submission. impl OffenceReportSystem, (EquivocationProof, T::KeyOwnerProof)> for EquivocationReportSystem @@ -131,7 +133,7 @@ where }; let res = SubmitTransaction::>::submit_unsigned_transaction(call.into()); match res { - Ok(()) => info!(target: LOG_TARGET, "Submitted equivocation report."), + Ok(_) => info!(target: LOG_TARGET, "Submitted equivocation report"), Err(e) => error!(target: LOG_TARGET, "Error submitting equivocation report: {:?}", e), } res diff --git a/frame/beefy-mmr/src/mock.rs b/frame/beefy-mmr/src/mock.rs index d31effc9a..8b3bedcb9 100644 --- a/frame/beefy-mmr/src/mock.rs +++ b/frame/beefy-mmr/src/mock.rs @@ -21,11 +21,11 @@ use codec::Encode; use frame_support::{ construct_runtime, parameter_types, sp_io::TestExternalities, - traits::{ConstU16, ConstU32, ConstU64, GenesisBuild, KeyOwnerProofSystem}, + traits::{ConstU16, ConstU32, ConstU64, GenesisBuild}, BasicExternalities, }; use sp_consensus_beefy::mmr::MmrLeafVersion; -use sp_core::{crypto::KeyTypeId, Hasher, H256}; +use sp_core::{Hasher, H256}; use sp_runtime::{ app_crypto::ecdsa::Public, impl_opaque_keys, @@ -124,18 +124,12 @@ impl pallet_mmr::Config for Test { impl pallet_beefy::Config for Test { type BeefyId = BeefyId; - type KeyOwnerProofSystem = (); - type KeyOwnerProof = - >::Proof; - type KeyOwnerIdentification = >::IdentificationTuple; - type HandleEquivocation = (); type MaxAuthorities = ConstU32<100>; type MaxSetIdSessionEntries = ConstU64<100>; type OnNewValidatorSet = BeefyMmr; type WeightInfo = (); + type KeyOwnerProof = sp_core::Void; + type EquivocationReportSystem = (); } parameter_types! { diff --git a/frame/beefy/src/equivocation.rs b/frame/beefy/src/equivocation.rs index cc04f316c..f83b037dc 100644 --- a/frame/beefy/src/equivocation.rs +++ b/frame/beefy/src/equivocation.rs @@ -34,179 +34,205 @@ //! that the `ValidateUnsigned` for the BEEFY pallet is used in the runtime //! definition. -use sp_std::prelude::*; - use codec::{self as codec, Decode, Encode}; use frame_support::{ log, traits::{Get, KeyOwnerProofSystem}, }; -use frame_system::pallet_prelude::BlockNumberFor; -use sp_consensus_beefy::{EquivocationProof, ValidatorSetId}; +use log::{error, info}; +use sp_consensus_beefy::{EquivocationProof, ValidatorSetId, KEY_TYPE}; use sp_runtime::{ transaction_validity::{ InvalidTransaction, TransactionPriority, TransactionSource, TransactionValidity, TransactionValidityError, ValidTransaction, }, - DispatchResult, Perbill, RuntimeAppPublic, + DispatchError, KeyTypeId, Perbill, RuntimeAppPublic, }; +use sp_session::{GetSessionNumber, GetValidatorCount}; use sp_staking::{ - offence::{Kind, Offence, OffenceError, ReportOffence}, + offence::{Kind, Offence, OffenceReportSystem, ReportOffence}, SessionIndex, }; +use sp_std::prelude::*; -use super::{Call, Config, Pallet, LOG_TARGET}; - -/// A trait with utility methods for handling equivocation reports in BEEFY. -/// The offence type is generic, and the trait provides, reporting an offence -/// triggered by a valid equivocation report, and also for creating and -/// submitting equivocation report extrinsics (useful only in offchain context). -pub trait HandleEquivocation { - /// The offence type used for reporting offences on valid equivocation reports. - type Offence: BeefyOffence, T::KeyOwnerIdentification>; - - /// The longevity, in blocks, that the equivocation report is valid for. When using the staking - /// pallet this should be equal to the bonding duration (in blocks, not eras). - type ReportLongevity: Get; - - /// Report an offence proved by the given reporters. - fn report_offence( - reporters: Vec, - offence: Self::Offence, - ) -> Result<(), OffenceError>; - - /// Returns true if all of the offenders at the given time slot have already been reported. - fn is_known_offence( - offenders: &[T::KeyOwnerIdentification], - time_slot: &>::TimeSlot, - ) -> bool; - - /// Create and dispatch an equivocation report extrinsic. - fn submit_unsigned_equivocation_report( - equivocation_proof: EquivocationProof< - BlockNumberFor, - T::BeefyId, - ::Signature, - >, - key_owner_proof: T::KeyOwnerProof, - ) -> DispatchResult; - - /// Fetch the current block author id, if defined. - fn block_author() -> Option; +use super::{Call, Config, Error, Pallet, LOG_TARGET}; + +/// A round number and set id which point on the time of an offence. +#[derive(Copy, Clone, PartialOrd, Ord, Eq, PartialEq, Encode, Decode)] +pub struct TimeSlot { + // The order of these matters for `derive(Ord)`. + /// BEEFY Set ID. + pub set_id: ValidatorSetId, + /// Round number. + pub round: N, } -impl HandleEquivocation for () { - type Offence = BeefyEquivocationOffence, T::KeyOwnerIdentification>; - type ReportLongevity = (); +/// BEEFY equivocation offence report. +pub struct EquivocationOffence +where + N: Copy + Clone + PartialOrd + Ord + Eq + PartialEq + Encode + Decode, +{ + /// Time slot at which this incident happened. + pub time_slot: TimeSlot, + /// The session index in which the incident happened. + pub session_index: SessionIndex, + /// The size of the validator set at the time of the offence. + pub validator_set_count: u32, + /// The authority which produced this equivocation. + pub offender: Offender, +} - fn report_offence( - _reporters: Vec, - _offence: BeefyEquivocationOffence, T::KeyOwnerIdentification>, - ) -> Result<(), OffenceError> { - Ok(()) - } +impl Offence for EquivocationOffence +where + N: Copy + Clone + PartialOrd + Ord + Eq + PartialEq + Encode + Decode, +{ + const ID: Kind = *b"beefy:equivocati"; + type TimeSlot = TimeSlot; - fn is_known_offence( - _offenders: &[T::KeyOwnerIdentification], - _time_slot: &BeefyTimeSlot>, - ) -> bool { - true + fn offenders(&self) -> Vec { + vec![self.offender.clone()] } - fn submit_unsigned_equivocation_report( - _equivocation_proof: EquivocationProof< - BlockNumberFor, - T::BeefyId, - ::Signature, - >, - _key_owner_proof: T::KeyOwnerProof, - ) -> DispatchResult { - Ok(()) + fn session_index(&self) -> SessionIndex { + self.session_index } - fn block_author() -> Option { - None + fn validator_set_count(&self) -> u32 { + self.validator_set_count } -} -/// Generic equivocation handler. This type implements `HandleEquivocation` -/// using existing subsystems that are part of frame (type bounds described -/// below) and will dispatch to them directly, it's only purpose is to wire all -/// subsystems together. -pub struct EquivocationHandler> { - _phantom: sp_std::marker::PhantomData<(N, I, R, L, O)>, -} + fn time_slot(&self) -> Self::TimeSlot { + self.time_slot + } -impl Default for EquivocationHandler { - fn default() -> Self { - Self { _phantom: Default::default() } + // The formula is min((3k / n)^2, 1) + // where k = offenders_number and n = validators_number + fn slash_fraction(&self, offenders_count: u32) -> Perbill { + // Perbill type domain is [0, 1] by definition + Perbill::from_rational(3 * offenders_count, self.validator_set_count).square() } } -impl HandleEquivocation - for EquivocationHandler, T::KeyOwnerIdentification, R, L, O> +/// BEEFY equivocation offence report system. +/// +/// This type implements `OffenceReportSystem` such that: +/// - Equivocation reports are published on-chain as unsigned extrinsic via +/// `offchain::SendTransactioinsTypes`. +/// - On-chain validity checks and processing are mostly delegated to the user provided generic +/// types implementing `KeyOwnerProofSystem` and `ReportOffence` traits. +/// - Offence reporter for unsigned transactions is fetched via the the authorship pallet. +pub struct EquivocationReportSystem(sp_std::marker::PhantomData<(T, R, P, L)>); + +/// Equivocation evidence convenience alias. +pub type EquivocationEvidenceFor = ( + EquivocationProof< + ::BlockNumber, + ::BeefyId, + <::BeefyId as RuntimeAppPublic>::Signature, + >, + ::KeyOwnerProof, +); + +impl OffenceReportSystem, EquivocationEvidenceFor> + for EquivocationReportSystem where - // We use the authorship pallet to fetch the current block author and use - // `offchain::SendTransactionTypes` for unsigned extrinsic creation and - // submission. T: Config + pallet_authorship::Config + frame_system::offchain::SendTransactionTypes>, - // A system for reporting offences after valid equivocation reports are - // processed. - R: ReportOffence, - // The longevity (in blocks) that the equivocation report is valid for. When using the staking - // pallet this should be the bonding duration. + R: ReportOffence< + T::AccountId, + P::IdentificationTuple, + EquivocationOffence, + >, + P: KeyOwnerProofSystem<(KeyTypeId, T::BeefyId), Proof = T::KeyOwnerProof>, + P::IdentificationTuple: Clone, L: Get, - // The offence type that should be used when reporting. - O: BeefyOffence, T::KeyOwnerIdentification>, { - type Offence = O; - type ReportLongevity = L; + type Longevity = L; - fn report_offence(reporters: Vec, offence: O) -> Result<(), OffenceError> { - R::report_offence(reporters, offence) - } - - fn is_known_offence(offenders: &[T::KeyOwnerIdentification], time_slot: &O::TimeSlot) -> bool { - R::is_known_offence(offenders, time_slot) - } - - fn submit_unsigned_equivocation_report( - equivocation_proof: EquivocationProof< - BlockNumberFor, - T::BeefyId, - ::Signature, - >, - key_owner_proof: T::KeyOwnerProof, - ) -> DispatchResult { + fn publish_evidence(evidence: EquivocationEvidenceFor) -> Result<(), ()> { use frame_system::offchain::SubmitTransaction; + let (equivocation_proof, key_owner_proof) = evidence; let call = Call::report_equivocation_unsigned { equivocation_proof: Box::new(equivocation_proof), key_owner_proof, }; - match SubmitTransaction::>::submit_unsigned_transaction(call.into()) { - Ok(()) => log::info!(target: LOG_TARGET, "Submitted BEEFY equivocation report.",), - Err(e) => - log::error!(target: LOG_TARGET, "Error submitting equivocation report: {:?}", e,), + let res = SubmitTransaction::>::submit_unsigned_transaction(call.into()); + match res { + Ok(_) => info!(target: LOG_TARGET, "Submitted equivocation report."), + Err(e) => error!(target: LOG_TARGET, "Error submitting equivocation report: {:?}", e), } - - Ok(()) + res } - fn block_author() -> Option { - >::author() + fn check_evidence( + evidence: EquivocationEvidenceFor, + ) -> Result<(), TransactionValidityError> { + let (equivocation_proof, key_owner_proof) = evidence; + + // Check the membership proof to extract the offender's id + let key = (KEY_TYPE, equivocation_proof.offender_id().clone()); + let offender = P::check_proof(key, key_owner_proof).ok_or(InvalidTransaction::BadProof)?; + + // Check if the offence has already been reported, and if so then we can discard the report. + let time_slot = TimeSlot { + set_id: equivocation_proof.set_id(), + round: *equivocation_proof.round_number(), + }; + + if R::is_known_offence(&[offender], &time_slot) { + Err(InvalidTransaction::Stale.into()) + } else { + Ok(()) + } } -} -/// A round number and set id which point on the time of an offence. -#[derive(Copy, Clone, PartialOrd, Ord, Eq, PartialEq, Encode, Decode)] -pub struct BeefyTimeSlot { - // The order of these matters for `derive(Ord)`. - /// BEEFY Set ID. - pub set_id: ValidatorSetId, - /// Round number. - pub round: N, + fn process_evidence( + reporter: Option, + evidence: EquivocationEvidenceFor, + ) -> Result<(), DispatchError> { + let (equivocation_proof, key_owner_proof) = evidence; + let reporter = reporter.or_else(|| >::author()); + let offender = equivocation_proof.offender_id().clone(); + + // We check the equivocation within the context of its set id (and + // associated session) and round. We also need to know the validator + // set count at the time of the offence since it is required to calculate + // the slash amount. + let set_id = equivocation_proof.set_id(); + let round = *equivocation_proof.round_number(); + let session_index = key_owner_proof.session(); + let validator_set_count = key_owner_proof.validator_count(); + + // Validate the key ownership proof extracting the id of the offender. + let offender = P::check_proof((KEY_TYPE, offender), key_owner_proof) + .ok_or(Error::::InvalidKeyOwnershipProof)?; + + // Validate equivocation proof (check votes are different and signatures are valid). + if !sp_consensus_beefy::check_equivocation_proof(&equivocation_proof) { + return Err(Error::::InvalidEquivocationProof.into()) + } + + // Check that the session id for the membership proof is within the + // bounds of the set id reported in the equivocation. + let set_id_session_index = + crate::SetIdSession::::get(set_id).ok_or(Error::::InvalidEquivocationProof)?; + if session_index != set_id_session_index { + return Err(Error::::InvalidEquivocationProof.into()) + } + + let offence = EquivocationOffence { + time_slot: TimeSlot { set_id, round }, + session_index, + validator_set_count, + offender, + }; + + R::report_offence(reporter.into_iter().collect(), offence) + .map_err(|_| Error::::DuplicateOffenceReport)?; + + Ok(()) + } } /// Methods for the `ValidateUnsigned` implementation: @@ -228,11 +254,11 @@ impl Pallet { }, } - // check report staleness - is_known_offence::(equivocation_proof, key_owner_proof)?; + let evidence = (*equivocation_proof.clone(), key_owner_proof.clone()); + T::EquivocationReportSystem::check_evidence(evidence)?; let longevity = - >::ReportLongevity::get(); + >::Longevity::get(); ValidTransaction::with_tag_prefix("BeefyEquivocation") // We assign the maximum priority for any equivocation report. @@ -254,132 +280,10 @@ impl Pallet { pub fn pre_dispatch(call: &Call) -> Result<(), TransactionValidityError> { if let Call::report_equivocation_unsigned { equivocation_proof, key_owner_proof } = call { - is_known_offence::(equivocation_proof, key_owner_proof) + let evidence = (*equivocation_proof.clone(), key_owner_proof.clone()); + T::EquivocationReportSystem::check_evidence(evidence) } else { Err(InvalidTransaction::Call.into()) } } } - -fn is_known_offence( - equivocation_proof: &EquivocationProof< - BlockNumberFor, - T::BeefyId, - ::Signature, - >, - key_owner_proof: &T::KeyOwnerProof, -) -> Result<(), TransactionValidityError> { - // check the membership proof to extract the offender's id, - // equivocation validity will be fully checked during the call. - let key = (sp_consensus_beefy::KEY_TYPE, equivocation_proof.offender_id().clone()); - - let offender = T::KeyOwnerProofSystem::check_proof(key, key_owner_proof.clone()) - .ok_or(InvalidTransaction::BadProof)?; - - // check if the offence has already been reported, - // and if so then we can discard the report. - let time_slot = >::Offence::new_time_slot( - equivocation_proof.set_id(), - *equivocation_proof.round_number(), - ); - - let is_known_offence = T::HandleEquivocation::is_known_offence(&[offender], &time_slot); - - if is_known_offence { - Err(InvalidTransaction::Stale.into()) - } else { - Ok(()) - } -} - -/// A BEEFY equivocation offence report. -pub struct BeefyEquivocationOffence -where - N: Copy + Clone + PartialOrd + Ord + Eq + PartialEq + Encode + Decode, -{ - /// Time slot at which this incident happened. - pub time_slot: BeefyTimeSlot, - /// The session index in which the incident happened. - pub session_index: SessionIndex, - /// The size of the validator set at the time of the offence. - pub validator_set_count: u32, - /// The authority which produced this equivocation. - pub offender: FullIdentification, -} - -/// An interface for types that will be used as BEEFY offences and must also -/// implement the `Offence` trait. This trait provides a constructor that is -/// provided all available data during processing of BEEFY equivocations. -pub trait BeefyOffence: Offence -where - N: Copy + Clone + PartialOrd + Ord + Eq + PartialEq + Encode + Decode, -{ - /// Create a new BEEFY offence using the given equivocation details. - fn new( - session_index: SessionIndex, - validator_set_count: u32, - offender: FullIdentification, - set_id: ValidatorSetId, - round: N, - ) -> Self; - - /// Create a new BEEFY offence time slot. - fn new_time_slot(set_id: ValidatorSetId, round: N) -> Self::TimeSlot; -} - -impl BeefyOffence - for BeefyEquivocationOffence -where - N: Copy + Clone + PartialOrd + Ord + Eq + PartialEq + Encode + Decode, -{ - fn new( - session_index: SessionIndex, - validator_set_count: u32, - offender: FullIdentification, - set_id: ValidatorSetId, - round: N, - ) -> Self { - BeefyEquivocationOffence { - session_index, - validator_set_count, - offender, - time_slot: BeefyTimeSlot { set_id, round }, - } - } - - fn new_time_slot(set_id: ValidatorSetId, round: N) -> Self::TimeSlot { - BeefyTimeSlot { set_id, round } - } -} - -impl Offence - for BeefyEquivocationOffence -where - N: Copy + Clone + PartialOrd + Ord + Eq + PartialEq + Encode + Decode, -{ - const ID: Kind = *b"beefy:equivocati"; - type TimeSlot = BeefyTimeSlot; - - fn offenders(&self) -> Vec { - vec![self.offender.clone()] - } - - fn session_index(&self) -> SessionIndex { - self.session_index - } - - fn validator_set_count(&self) -> u32 { - self.validator_set_count - } - - fn time_slot(&self) -> Self::TimeSlot { - self.time_slot - } - - fn slash_fraction(&self, offenders_count: u32) -> Perbill { - // the formula is min((3k / n)^2, 1) - let x = Perbill::from_rational(3 * offenders_count, self.validator_set_count); - // _ ^ 2 - x.square() - } -} diff --git a/frame/beefy/src/lib.rs b/frame/beefy/src/lib.rs index 698e6e733..945b32c12 100644 --- a/frame/beefy/src/lib.rs +++ b/frame/beefy/src/lib.rs @@ -23,7 +23,7 @@ use frame_support::{ dispatch::{DispatchResultWithPostInfo, Pays}, log, pallet_prelude::*, - traits::{Get, KeyOwnerProofSystem, OneSessionHandler}, + traits::{Get, OneSessionHandler}, weights::Weight, BoundedSlice, BoundedVec, Parameter, }; @@ -34,10 +34,10 @@ use frame_system::{ use sp_runtime::{ generic::DigestItem, traits::{IsMember, Member}, - KeyTypeId, RuntimeAppPublic, + RuntimeAppPublic, }; use sp_session::{GetSessionNumber, GetValidatorCount}; -use sp_staking::SessionIndex; +use sp_staking::{offence::OffenceReportSystem, SessionIndex}; use sp_std::prelude::*; use sp_consensus_beefy::{ @@ -52,11 +52,11 @@ mod mock; #[cfg(test)] mod tests; -pub use crate::equivocation::{ - BeefyEquivocationOffence, BeefyOffence, BeefyTimeSlot, EquivocationHandler, HandleEquivocation, -}; +pub use crate::equivocation::{EquivocationOffence, EquivocationReportSystem, TimeSlot}; pub use pallet::*; +use crate::equivocation::EquivocationEvidenceFor; + const LOG_TARGET: &str = "runtime::beefy"; #[frame_support::pallet] @@ -74,30 +74,6 @@ pub mod pallet { + MaybeSerializeDeserialize + MaxEncodedLen; - /// A system for proving ownership of keys, i.e. that a given key was part - /// of a validator set, needed for validating equivocation reports. - type KeyOwnerProofSystem: KeyOwnerProofSystem< - (KeyTypeId, Self::BeefyId), - Proof = Self::KeyOwnerProof, - IdentificationTuple = Self::KeyOwnerIdentification, - >; - - /// The proof of key ownership, used for validating equivocation reports - /// The proof must include the session index and validator count of the - /// session at which the equivocation occurred. - type KeyOwnerProof: Parameter + GetSessionNumber + GetValidatorCount; - - /// The identification of a key owner, used when reporting equivocations. - type KeyOwnerIdentification: Parameter; - - /// The equivocation handling subsystem, defines methods to report an - /// offence (after the equivocation has been validated) and for submitting a - /// transaction to report an equivocation (from an offchain context). - /// NOTE: when enabling equivocation handling (i.e. this type isn't set to - /// `()`) you must use this pallet's `ValidateUnsigned` in the runtime - /// definition. - type HandleEquivocation: HandleEquivocation; - /// The maximum number of authorities that can be added. #[pallet::constant] type MaxAuthorities: Get; @@ -120,6 +96,19 @@ pub mod pallet { /// Weights for this pallet. type WeightInfo: WeightInfo; + + /// The proof of key ownership, used for validating equivocation reports + /// The proof must include the session index and validator count of the + /// session at which the equivocation occurred. + type KeyOwnerProof: Parameter + GetSessionNumber + GetValidatorCount; + + /// The equivocation handling subsystem. + /// + /// Defines methods to publish, check and process an equivocation offence. + type EquivocationReportSystem: OffenceReportSystem< + Option, + EquivocationEvidenceFor, + >; } #[pallet::pallet] @@ -229,7 +218,12 @@ pub mod pallet { ) -> DispatchResultWithPostInfo { let reporter = ensure_signed(origin)?; - Self::do_report_equivocation(Some(reporter), *equivocation_proof, key_owner_proof) + T::EquivocationReportSystem::process_evidence( + Some(reporter), + (*equivocation_proof, key_owner_proof), + )?; + // Waive the fee since the report is valid and beneficial + Ok(Pays::No.into()) } /// Report voter equivocation/misbehavior. This method will verify the @@ -256,20 +250,22 @@ pub mod pallet { ) -> DispatchResultWithPostInfo { ensure_none(origin)?; - Self::do_report_equivocation( - T::HandleEquivocation::block_author(), - *equivocation_proof, - key_owner_proof, - ) + T::EquivocationReportSystem::process_evidence( + None, + (*equivocation_proof, key_owner_proof), + )?; + Ok(Pays::No.into()) } } #[pallet::validate_unsigned] impl ValidateUnsigned for Pallet { type Call = Call; + fn pre_dispatch(call: &Self::Call) -> Result<(), TransactionValidityError> { Self::pre_dispatch(call) } + fn validate_unsigned(source: TransactionSource, call: &Self::Call) -> TransactionValidity { Self::validate_unsigned(source, call) } @@ -295,11 +291,7 @@ impl Pallet { >, key_owner_proof: T::KeyOwnerProof, ) -> Option<()> { - T::HandleEquivocation::submit_unsigned_equivocation_report( - equivocation_proof, - key_owner_proof, - ) - .ok() + T::EquivocationReportSystem::publish_evidence((equivocation_proof, key_owner_proof)).ok() } fn change_authorities( @@ -368,62 +360,6 @@ impl Pallet { Ok(()) } - - fn do_report_equivocation( - reporter: Option, - equivocation_proof: EquivocationProof< - BlockNumberFor, - T::BeefyId, - ::Signature, - >, - key_owner_proof: T::KeyOwnerProof, - ) -> DispatchResultWithPostInfo { - // We check the equivocation within the context of its set id (and - // associated session) and round. We also need to know the validator - // set count at the time of the offence since it is required to calculate - // the slash amount. - let set_id = equivocation_proof.set_id(); - let round = *equivocation_proof.round_number(); - let offender_id = equivocation_proof.offender_id().clone(); - let session_index = key_owner_proof.session(); - let validator_count = key_owner_proof.validator_count(); - - // validate the key ownership proof extracting the id of the offender. - let offender = T::KeyOwnerProofSystem::check_proof( - (sp_consensus_beefy::KEY_TYPE, offender_id), - key_owner_proof, - ) - .ok_or(Error::::InvalidKeyOwnershipProof)?; - - // validate equivocation proof (check votes are different and signatures are valid). - if !sp_consensus_beefy::check_equivocation_proof(&equivocation_proof) { - return Err(Error::::InvalidEquivocationProof.into()) - } - - // check that the session id for the membership proof is within the - // bounds of the set id reported in the equivocation. - let set_id_session_index = - Self::session_for_set(set_id).ok_or(Error::::InvalidEquivocationProof)?; - if session_index != set_id_session_index { - return Err(Error::::InvalidEquivocationProof.into()) - } - - // report to the offences module rewarding the sender. - T::HandleEquivocation::report_offence( - reporter.into_iter().collect(), - >::Offence::new( - session_index, - validator_count, - offender, - set_id, - round, - ), - ) - .map_err(|_| Error::::DuplicateOffenceReport)?; - - // waive the fee since the report is valid and beneficial - Ok(Pays::No.into()) - } } impl sp_runtime::BoundToRuntimeAppPublic for Pallet { diff --git a/frame/beefy/src/mock.rs b/frame/beefy/src/mock.rs index 72e3f83df..c92966235 100644 --- a/frame/beefy/src/mock.rs +++ b/frame/beefy/src/mock.rs @@ -116,19 +116,13 @@ parameter_types! { impl pallet_beefy::Config for Test { type BeefyId = BeefyId; - type KeyOwnerProofSystem = Historical; - type KeyOwnerProof = - >::Proof; - type KeyOwnerIdentification = >::IdentificationTuple; - type HandleEquivocation = - super::EquivocationHandler; type MaxAuthorities = ConstU32<100>; type MaxSetIdSessionEntries = MaxSetIdSessionEntries; type OnNewValidatorSet = (); type WeightInfo = (); + type KeyOwnerProof = >::Proof; + type EquivocationReportSystem = + super::EquivocationReportSystem; } parameter_types! { diff --git a/frame/grandpa/src/equivocation.rs b/frame/grandpa/src/equivocation.rs index 6fd12bbdf..44d026637 100644 --- a/frame/grandpa/src/equivocation.rs +++ b/frame/grandpa/src/equivocation.rs @@ -57,7 +57,7 @@ use super::{Call, Config, Error, Pallet, LOG_TARGET}; /// A round number and set id which point on the time of an offence. #[derive(Copy, Clone, PartialOrd, Ord, Eq, PartialEq, Encode, Decode)] -pub struct GrandpaTimeSlot { +pub struct TimeSlot { // The order of these matters for `derive(Ord)`. /// Grandpa Set ID. pub set_id: SetId, @@ -65,10 +65,10 @@ pub struct GrandpaTimeSlot { pub round: RoundNumber, } -/// A GRANDPA equivocation offence report. +/// GRANDPA equivocation offence report. pub struct EquivocationOffence { /// Time slot at which this incident happened. - pub time_slot: GrandpaTimeSlot, + pub time_slot: TimeSlot, /// The session index in which the incident happened. pub session_index: SessionIndex, /// The size of the validator set at the time of the offence. @@ -79,7 +79,7 @@ pub struct EquivocationOffence { impl Offence for EquivocationOffence { const ID: Kind = *b"grandpa:equivoca"; - type TimeSlot = GrandpaTimeSlot; + type TimeSlot = TimeSlot; fn offenders(&self) -> Vec { vec![self.offender.clone()] @@ -105,15 +105,16 @@ impl Offence for EquivocationOffence { } } -/// Generic equivocation handler. This type implements `HandleEquivocation` -/// using existing subsystems that are part of frame (type bounds described -/// below) and will dispatch to them directly, it's only purpose is to wire all -/// subsystems together. +/// GRANDPA equivocation offence report system. +/// +/// This type implements `OffenceReportSystem` such that: +/// - Equivocation reports are published on-chain as unsigned extrinsic via +/// `offchain::SendTransactioinsTypes`. +/// - On-chain validity checks and processing are mostly delegated to the user provided generic +/// types implementing `KeyOwnerProofSystem` and `ReportOffence` traits. +/// - Offence reporter for unsigned transactions is fetched via the the authorship pallet. pub struct EquivocationReportSystem(sp_std::marker::PhantomData<(T, R, P, L)>); -// We use the authorship pallet to fetch the current block author and use -// `offchain::SendTransactionTypes` for unsigned extrinsic creation and -// submission. impl OffenceReportSystem< Option, @@ -144,7 +145,7 @@ where }; let res = SubmitTransaction::>::submit_unsigned_transaction(call.into()); match res { - Ok(()) => info!(target: LOG_TARGET, "Submitted equivocation report."), + Ok(_) => info!(target: LOG_TARGET, "Submitted equivocation report"), Err(e) => error!(target: LOG_TARGET, "Error submitting equivocation report: {:?}", e), } res @@ -160,10 +161,8 @@ where let offender = P::check_proof(key, key_owner_proof).ok_or(InvalidTransaction::BadProof)?; // Check if the offence has already been reported, and if so then we can discard the report. - let time_slot = GrandpaTimeSlot { - set_id: equivocation_proof.set_id(), - round: equivocation_proof.round(), - }; + let time_slot = + TimeSlot { set_id: equivocation_proof.set_id(), round: equivocation_proof.round() }; if R::is_known_offence(&[offender], &time_slot) { Err(InvalidTransaction::Stale.into()) } else { @@ -221,7 +220,7 @@ where } let offence = EquivocationOffence { - time_slot: GrandpaTimeSlot { set_id, round }, + time_slot: TimeSlot { set_id, round }, session_index, offender, validator_set_count, diff --git a/frame/grandpa/src/lib.rs b/frame/grandpa/src/lib.rs index 2106860ea..538cd365b 100644 --- a/frame/grandpa/src/lib.rs +++ b/frame/grandpa/src/lib.rs @@ -63,7 +63,7 @@ mod mock; #[cfg(all(feature = "std", test))] mod tests; -pub use equivocation::{EquivocationOffence, EquivocationReportSystem, GrandpaTimeSlot}; +pub use equivocation::{EquivocationOffence, EquivocationReportSystem, TimeSlot}; pub use pallet::*; @@ -351,6 +351,7 @@ pub mod pallet { #[pallet::validate_unsigned] impl ValidateUnsigned for Pallet { type Call = Call; + fn validate_unsigned(source: TransactionSource, call: &Self::Call) -> TransactionValidity { Self::validate_unsigned(source, call) } diff --git a/frame/grandpa/src/mock.rs b/frame/grandpa/src/mock.rs index 22ca9c624..4e4f2f79d 100644 --- a/frame/grandpa/src/mock.rs +++ b/frame/grandpa/src/mock.rs @@ -224,13 +224,10 @@ parameter_types! { impl Config for Test { type RuntimeEvent = RuntimeEvent; - type WeightInfo = (); type MaxAuthorities = ConstU32<100>; type MaxSetIdSessionEntries = MaxSetIdSessionEntries; - type KeyOwnerProof = >::Proof; - type EquivocationReportSystem = super::EquivocationReportSystem; } diff --git a/frame/grandpa/src/tests.rs b/frame/grandpa/src/tests.rs index a956ca4bc..16d89307b 100644 --- a/frame/grandpa/src/tests.rs +++ b/frame/grandpa/src/tests.rs @@ -296,12 +296,12 @@ fn schedule_resume_only_when_paused() { #[test] fn time_slot_have_sane_ord() { // Ensure that `Ord` implementation is sane. - const FIXTURE: &[GrandpaTimeSlot] = &[ - GrandpaTimeSlot { set_id: 0, round: 0 }, - GrandpaTimeSlot { set_id: 0, round: 1 }, - GrandpaTimeSlot { set_id: 1, round: 0 }, - GrandpaTimeSlot { set_id: 1, round: 1 }, - GrandpaTimeSlot { set_id: 1, round: 2 }, + const FIXTURE: &[TimeSlot] = &[ + TimeSlot { set_id: 0, round: 0 }, + TimeSlot { set_id: 0, round: 1 }, + TimeSlot { set_id: 1, round: 0 }, + TimeSlot { set_id: 1, round: 1 }, + TimeSlot { set_id: 1, round: 2 }, ]; assert!(FIXTURE.windows(2).all(|f| f[0] < f[1])); } diff --git a/frame/offences/benchmarking/src/lib.rs b/frame/offences/benchmarking/src/lib.rs index 3db937bb3..0efbdcd48 100644 --- a/frame/offences/benchmarking/src/lib.rs +++ b/frame/offences/benchmarking/src/lib.rs @@ -38,7 +38,9 @@ use sp_staking::offence::{Offence, ReportOffence}; use pallet_babe::EquivocationOffence as BabeEquivocationOffence; use pallet_balances::Config as BalancesConfig; -use pallet_grandpa::{EquivocationOffence as GrandpaEquivocationOffence, GrandpaTimeSlot}; +use pallet_grandpa::{ + EquivocationOffence as GrandpaEquivocationOffence, TimeSlot as GrandpaTimeSlot, +}; use pallet_im_online::{Config as ImOnlineConfig, Pallet as ImOnline, UnresponsivenessOffence}; use pallet_offences::{Config as OffencesConfig, Pallet as Offences}; use pallet_session::{ From f5c2cecd7efe6dcbc828906794f3c4c41f23c791 Mon Sep 17 00:00:00 2001 From: Jegor Sidorenko <5252494+jsidorenko@users.noreply.github.com> Date: Mon, 13 Mar 2023 10:25:46 +0200 Subject: [PATCH 223/558] [NFTs] Rework permissions model (#13482) * Disallow admin to transfer or burn items he doesn't own * lock_collection should be accessible by collection's owner only * Allow admin to access lock_item_properties() * Fix do_lock_item_properties * Move update_mint_settings() to Issuer * Rename check_owner to check_origin * Typo * Make admin to be in charge of managing the metadata * Make admin the main attributes manager * offchain mint should be signed by Issuer * Remove the special case when the Issuer calls the mint() function * Rework burn and destroy methods * Return back item_metadatas * Don't repatriate the deposit on transfer * A bit more tests * One more test * Add migration * Chore * Clippy * Rename to owned_item * Address comments * Replace .filter_map with .find_map * Improve version validation in pre_upgrade() * ".git/.scripts/commands/bench/bench.sh" pallet dev pallet_nfts --------- Co-authored-by: parity-processbot <> --- frame/nfts/src/benchmarking.rs | 100 +- frame/nfts/src/features/approvals.rs | 12 +- frame/nfts/src/features/attributes.rs | 57 +- .../src/features/create_delete_collection.rs | 22 +- frame/nfts/src/features/create_delete_item.rs | 24 +- frame/nfts/src/features/lock.rs | 17 +- frame/nfts/src/features/metadata.rs | 66 +- frame/nfts/src/features/roles.rs | 15 + frame/nfts/src/features/settings.rs | 11 +- frame/nfts/src/features/transfer.rs | 10 - frame/nfts/src/lib.rs | 140 ++- frame/nfts/src/migration.rs | 117 ++ frame/nfts/src/tests.rs | 145 ++- frame/nfts/src/types.rs | 10 +- frame/nfts/src/weights.rs | 1028 ++++++++--------- 15 files changed, 970 insertions(+), 804 deletions(-) create mode 100644 frame/nfts/src/migration.rs diff --git a/frame/nfts/src/benchmarking.rs b/frame/nfts/src/benchmarking.rs index ae628fbef..d4bbd809c 100644 --- a/frame/nfts/src/benchmarking.rs +++ b/frame/nfts/src/benchmarking.rs @@ -72,6 +72,40 @@ fn add_collection_metadata, I: 'static>() -> (T::AccountId, Account fn mint_item, I: 'static>( index: u16, +) -> (T::ItemId, T::AccountId, AccountIdLookupOf) { + let item = T::Helper::item(index); + let collection = T::Helper::collection(0); + let caller = Collection::::get(collection).unwrap().owner; + if caller != whitelisted_caller() { + whitelist_account!(caller); + } + let caller_lookup = T::Lookup::unlookup(caller.clone()); + let item_exists = Item::::contains_key(&collection, &item); + let item_config = ItemConfigOf::::get(&collection, &item); + if item_exists { + return (item, caller, caller_lookup) + } else if let Some(item_config) = item_config { + assert_ok!(Nfts::::force_mint( + SystemOrigin::Signed(caller.clone()).into(), + collection, + item, + caller_lookup.clone(), + item_config, + )); + } else { + assert_ok!(Nfts::::mint( + SystemOrigin::Signed(caller.clone()).into(), + collection, + item, + caller_lookup.clone(), + None, + )); + } + (item, caller, caller_lookup) +} + +fn lock_item, I: 'static>( + index: u16, ) -> (T::ItemId, T::AccountId, AccountIdLookupOf) { let caller = Collection::::get(T::Helper::collection(0)).unwrap().owner; if caller != whitelisted_caller() { @@ -79,12 +113,27 @@ fn mint_item, I: 'static>( } let caller_lookup = T::Lookup::unlookup(caller.clone()); let item = T::Helper::item(index); - assert_ok!(Nfts::::mint( + assert_ok!(Nfts::::lock_item_transfer( + SystemOrigin::Signed(caller.clone()).into(), + T::Helper::collection(0), + item, + )); + (item, caller, caller_lookup) +} + +fn burn_item, I: 'static>( + index: u16, +) -> (T::ItemId, T::AccountId, AccountIdLookupOf) { + let caller = Collection::::get(T::Helper::collection(0)).unwrap().owner; + if caller != whitelisted_caller() { + whitelist_account!(caller); + } + let caller_lookup = T::Lookup::unlookup(caller.clone()); + let item = T::Helper::item(index); + assert_ok!(Nfts::::burn( SystemOrigin::Signed(caller.clone()).into(), T::Helper::collection(0), item, - caller_lookup.clone(), - None, )); (item, caller, caller_lookup) } @@ -126,6 +175,26 @@ fn add_item_attribute, I: 'static>( (key, caller, caller_lookup) } +fn add_collection_attribute, I: 'static>( + i: u16, +) -> (BoundedVec, T::AccountId, AccountIdLookupOf) { + let caller = Collection::::get(T::Helper::collection(0)).unwrap().owner; + if caller != whitelisted_caller() { + whitelist_account!(caller); + } + let caller_lookup = T::Lookup::unlookup(caller.clone()); + let key: BoundedVec<_, _> = make_filled_vec(i, T::KeyLimit::get() as usize).try_into().unwrap(); + assert_ok!(Nfts::::set_attribute( + SystemOrigin::Signed(caller.clone()).into(), + T::Helper::collection(0), + None, + AttributeNamespace::CollectionOwner, + key.clone(), + vec![0; T::ValueLimit::get() as usize].try_into().unwrap(), + )); + (key, caller, caller_lookup) +} + fn assert_last_event, I: 'static>(generic_event: >::RuntimeEvent) { let events = frame_system::Pallet::::events(); let system_event: ::RuntimeEvent = generic_event.into(); @@ -190,26 +259,25 @@ benchmarks_instance_pallet! { } destroy { - let n in 0 .. 1_000; let m in 0 .. 1_000; + let c in 0 .. 1_000; let a in 0 .. 1_000; let (collection, caller, _) = create_collection::(); add_collection_metadata::(); - for i in 0..n { - mint_item::(i as u16); - } for i in 0..m { - if !Item::::contains_key(collection, T::Helper::item(i as u16)) { - mint_item::(i as u16); - } + mint_item::(i as u16); add_item_metadata::(T::Helper::item(i as u16)); + lock_item::(i as u16); + burn_item::(i as u16); + } + for i in 0..c { + mint_item::(i as u16); + lock_item::(i as u16); + burn_item::(i as u16); } for i in 0..a { - if !Item::::contains_key(collection, T::Helper::item(i as u16)) { - mint_item::(i as u16); - } - add_item_attribute::(T::Helper::item(i as u16)); + add_collection_attribute::(i as u16); } let witness = Collection::::get(collection).unwrap().destroy_witness(); }: _(SystemOrigin::Signed(caller), collection, witness) @@ -234,9 +302,9 @@ benchmarks_instance_pallet! { } burn { - let (collection, caller, caller_lookup) = create_collection::(); + let (collection, caller, _) = create_collection::(); let (item, ..) = mint_item::(0); - }: _(SystemOrigin::Signed(caller.clone()), collection, item, Some(caller_lookup)) + }: _(SystemOrigin::Signed(caller.clone()), collection, item) verify { assert_last_event::(Event::Burned { collection, item, owner: caller }.into()); } diff --git a/frame/nfts/src/features/approvals.rs b/frame/nfts/src/features/approvals.rs index 408c44129..634436a85 100644 --- a/frame/nfts/src/features/approvals.rs +++ b/frame/nfts/src/features/approvals.rs @@ -40,9 +40,7 @@ impl, I: 'static> Pallet { ); if let Some(check_origin) = maybe_check_origin { - let is_admin = Self::has_role(&collection, &check_origin, CollectionRole::Admin); - let permitted = is_admin || check_origin == details.owner; - ensure!(permitted, Error::::NoPermission); + ensure!(check_origin == details.owner, Error::::NoPermission); } let now = frame_system::Pallet::::block_number(); @@ -85,9 +83,7 @@ impl, I: 'static> Pallet { if !is_past_deadline { if let Some(check_origin) = maybe_check_origin { - let is_admin = Self::has_role(&collection, &check_origin, CollectionRole::Admin); - let permitted = is_admin || check_origin == details.owner; - ensure!(permitted, Error::::NoPermission); + ensure!(check_origin == details.owner, Error::::NoPermission); } } @@ -113,9 +109,7 @@ impl, I: 'static> Pallet { Item::::get(&collection, &item).ok_or(Error::::UnknownCollection)?; if let Some(check_origin) = maybe_check_origin { - let is_admin = Self::has_role(&collection, &check_origin, CollectionRole::Admin); - let permitted = is_admin || check_origin == details.owner; - ensure!(permitted, Error::::NoPermission); + ensure!(check_origin == details.owner, Error::::NoPermission); } details.approvals.clear(); diff --git a/frame/nfts/src/features/attributes.rs b/frame/nfts/src/features/attributes.rs index fbd1c9324..9098679fa 100644 --- a/frame/nfts/src/features/attributes.rs +++ b/frame/nfts/src/features/attributes.rs @@ -33,17 +33,8 @@ impl, I: 'static> Pallet { Error::::MethodDisabled ); - let mut collection_details = - Collection::::get(&collection).ok_or(Error::::UnknownCollection)?; - ensure!( - Self::is_valid_namespace( - &origin, - &namespace, - &collection, - &collection_details.owner, - &maybe_item, - )?, + Self::is_valid_namespace(&origin, &namespace, &collection, &maybe_item)?, Error::::NoPermission ); @@ -66,6 +57,9 @@ impl, I: 'static> Pallet { _ => (), } + let mut collection_details = + Collection::::get(&collection).ok_or(Error::::UnknownCollection)?; + let attribute = Attribute::::get((collection, maybe_item, &namespace, &key)); let attribute_exists = attribute.is_some(); if !attribute_exists { @@ -219,7 +213,7 @@ impl, I: 'static> Pallet { } pub(crate) fn do_clear_attribute( - maybe_check_owner: Option, + maybe_check_origin: Option, collection: T::CollectionId, maybe_item: Option, namespace: AttributeNamespace, @@ -227,21 +221,13 @@ impl, I: 'static> Pallet { ) -> DispatchResult { let (_, deposit) = Attribute::::take((collection, maybe_item, &namespace, &key)) .ok_or(Error::::AttributeNotFound)?; - let mut collection_details = - Collection::::get(&collection).ok_or(Error::::UnknownCollection)?; - if let Some(check_owner) = &maybe_check_owner { + if let Some(check_origin) = &maybe_check_origin { // validate the provided namespace when it's not a root call and the caller is not // the same as the `deposit.account` (e.g. the deposit was paid by different account) - if deposit.account != maybe_check_owner { + if deposit.account != maybe_check_origin { ensure!( - Self::is_valid_namespace( - &check_owner, - &namespace, - &collection, - &collection_details.owner, - &maybe_item, - )?, + Self::is_valid_namespace(&check_origin, &namespace, &collection, &maybe_item)?, Error::::NoPermission ); } @@ -264,17 +250,15 @@ impl, I: 'static> Pallet { .map_or(None, |c| { Some(c.has_disabled_setting(ItemSetting::UnlockedAttributes)) }); - match maybe_is_locked { - Some(is_locked) => { - // when item exists, then only the collection's owner can clear that - // attribute - ensure!( - check_owner == &collection_details.owner, - Error::::NoPermission - ); - ensure!(!is_locked, Error::::LockedItemAttributes); - }, - None => (), + if let Some(is_locked) = maybe_is_locked { + ensure!(!is_locked, Error::::LockedItemAttributes); + // Only the collection's admin can clear attributes in that namespace. + // e.g. in off-chain mints, the attribute's depositor will be the item's + // owner, that's why we need to do this extra check. + ensure!( + Self::has_role(&collection, &check_origin, CollectionRole::Admin), + Error::::NoPermission + ); } }, }, @@ -282,6 +266,9 @@ impl, I: 'static> Pallet { }; } + let mut collection_details = + Collection::::get(&collection).ok_or(Error::::UnknownCollection)?; + collection_details.attributes.saturating_dec(); match deposit.account { @@ -372,12 +359,12 @@ impl, I: 'static> Pallet { origin: &T::AccountId, namespace: &AttributeNamespace, collection: &T::CollectionId, - collection_owner: &T::AccountId, maybe_item: &Option, ) -> Result { let mut result = false; match namespace { - AttributeNamespace::CollectionOwner => result = origin == collection_owner, + AttributeNamespace::CollectionOwner => + result = Self::has_role(&collection, &origin, CollectionRole::Admin), AttributeNamespace::ItemOwner => if let Some(item) = maybe_item { let item_details = diff --git a/frame/nfts/src/features/create_delete_collection.rs b/frame/nfts/src/features/create_delete_collection.rs index 8187fff7f..e94347606 100644 --- a/frame/nfts/src/features/create_delete_collection.rs +++ b/frame/nfts/src/features/create_delete_collection.rs @@ -38,6 +38,7 @@ impl, I: 'static> Pallet { owner_deposit: deposit, items: 0, item_metadatas: 0, + item_configs: 0, attributes: 0, }, ); @@ -71,24 +72,23 @@ impl, I: 'static> Pallet { if let Some(check_owner) = maybe_check_owner { ensure!(collection_details.owner == check_owner, Error::::NoPermission); } - ensure!(collection_details.items == witness.items, Error::::BadWitness); + ensure!(collection_details.items == 0, Error::::CollectionNotEmpty); + ensure!(collection_details.attributes == witness.attributes, Error::::BadWitness); ensure!( collection_details.item_metadatas == witness.item_metadatas, Error::::BadWitness ); - ensure!(collection_details.attributes == witness.attributes, Error::::BadWitness); + ensure!( + collection_details.item_configs == witness.item_configs, + Error::::BadWitness + ); - for (item, details) in Item::::drain_prefix(&collection) { - Account::::remove((&details.owner, &collection, &item)); - T::Currency::unreserve(&details.deposit.account, details.deposit.amount); - } for (_, metadata) in ItemMetadataOf::::drain_prefix(&collection) { if let Some(depositor) = metadata.deposit.account { T::Currency::unreserve(&depositor, metadata.deposit.amount); } } - let _ = ItemPriceOf::::clear_prefix(&collection, witness.items, None); - let _ = PendingSwapOf::::clear_prefix(&collection, witness.items, None); + CollectionMetadataOf::::remove(&collection); Self::clear_roles(&collection)?; @@ -103,15 +103,13 @@ impl, I: 'static> Pallet { CollectionAccount::::remove(&collection_details.owner, &collection); T::Currency::unreserve(&collection_details.owner, collection_details.owner_deposit); CollectionConfigOf::::remove(&collection); - let _ = ItemConfigOf::::clear_prefix(&collection, witness.items, None); - let _ = - ItemAttributesApprovalsOf::::clear_prefix(&collection, witness.items, None); + let _ = ItemConfigOf::::clear_prefix(&collection, witness.item_configs, None); Self::deposit_event(Event::Destroyed { collection }); Ok(DestroyWitness { - items: collection_details.items, item_metadatas: collection_details.item_metadatas, + item_configs: collection_details.item_configs, attributes: collection_details.attributes, }) }) diff --git a/frame/nfts/src/features/create_delete_item.rs b/frame/nfts/src/features/create_delete_item.rs index 0319f2c8a..cd96d2844 100644 --- a/frame/nfts/src/features/create_delete_item.rs +++ b/frame/nfts/src/features/create_delete_item.rs @@ -66,6 +66,7 @@ impl, I: 'static> Pallet { ensure!(existing_config == item_config, Error::::InconsistentItemConfig); } else { ItemConfigOf::::insert(&collection, &item, item_config); + collection_details.item_configs.saturating_inc(); } T::Currency::reserve(&deposit_account, deposit_amount)?; @@ -107,7 +108,11 @@ impl, I: 'static> Pallet { let collection_details = Collection::::get(&collection).ok_or(Error::::UnknownCollection)?; - ensure!(collection_details.owner == signer, Error::::NoPermission); + + ensure!( + Self::has_role(&collection, &signer, CollectionRole::Issuer), + Error::::NoPermission + ); let item_config = ItemConfig { settings: Self::get_default_item_settings(&collection)? }; Self::do_mint( @@ -118,9 +123,11 @@ impl, I: 'static> Pallet { item_config, |_, _| Ok(()), )?; + let origin = Self::find_account_by_role(&collection, CollectionRole::Admin) + .unwrap_or(collection_details.owner.clone()); for (key, value) in attributes { Self::do_set_attribute( - collection_details.owner.clone(), + origin.clone(), collection, Some(item), AttributeNamespace::CollectionOwner, @@ -131,7 +138,7 @@ impl, I: 'static> Pallet { } if !metadata.len().is_zero() { Self::do_set_item_metadata( - Some(collection_details.owner.clone()), + Some(origin.clone()), collection, item, metadata, @@ -148,6 +155,9 @@ impl, I: 'static> Pallet { ) -> DispatchResult { ensure!(!T::Locker::is_locked(collection, item), Error::::ItemLocked); let item_config = Self::get_item_config(&collection, &item)?; + // NOTE: if item's settings are not empty (e.g. item's metadata is locked) + // then we keep the config record and don't remove it + let remove_config = !item_config.has_disabled_settings(); let owner = Collection::::try_mutate( &collection, |maybe_collection_details| -> Result { @@ -161,6 +171,10 @@ impl, I: 'static> Pallet { T::Currency::unreserve(&details.deposit.account, details.deposit.amount); collection_details.items.saturating_dec(); + if remove_config { + collection_details.item_configs.saturating_dec(); + } + // Clear the metadata if it's not locked. if item_config.is_setting_enabled(ItemSetting::UnlockedMetadata) { if let Some(metadata) = ItemMetadataOf::::take(&collection, &item) { @@ -188,9 +202,7 @@ impl, I: 'static> Pallet { PendingSwapOf::::remove(&collection, &item); ItemAttributesApprovalsOf::::remove(&collection, &item); - // NOTE: if item's settings are not empty (e.g. item's metadata is locked) - // then we keep the record and don't remove it - if !item_config.has_disabled_settings() { + if remove_config { ItemConfigOf::::remove(&collection, &item); } diff --git a/frame/nfts/src/features/lock.rs b/frame/nfts/src/features/lock.rs index c28fdda98..8b4914bae 100644 --- a/frame/nfts/src/features/lock.rs +++ b/frame/nfts/src/features/lock.rs @@ -24,10 +24,7 @@ impl, I: 'static> Pallet { collection: T::CollectionId, lock_settings: CollectionSettings, ) -> DispatchResult { - ensure!( - Self::has_role(&collection, &origin, CollectionRole::Freezer), - Error::::NoPermission - ); + ensure!(Self::collection_owner(collection) == Some(origin), Error::::NoPermission); ensure!( !lock_settings.is_disabled(CollectionSetting::DepositRequired), Error::::WrongSetting @@ -85,17 +82,17 @@ impl, I: 'static> Pallet { } pub(crate) fn do_lock_item_properties( - maybe_check_owner: Option, + maybe_check_origin: Option, collection: T::CollectionId, item: T::ItemId, lock_metadata: bool, lock_attributes: bool, ) -> DispatchResult { - let collection_details = - Collection::::get(&collection).ok_or(Error::::UnknownCollection)?; - - if let Some(check_owner) = &maybe_check_owner { - ensure!(check_owner == &collection_details.owner, Error::::NoPermission); + if let Some(check_origin) = &maybe_check_origin { + ensure!( + Self::has_role(&collection, &check_origin, CollectionRole::Admin), + Error::::NoPermission + ); } ItemConfigOf::::try_mutate(collection, item, |maybe_config| { diff --git a/frame/nfts/src/features/metadata.rs b/frame/nfts/src/features/metadata.rs index 232262bdb..fde029678 100644 --- a/frame/nfts/src/features/metadata.rs +++ b/frame/nfts/src/features/metadata.rs @@ -21,13 +21,20 @@ use frame_support::pallet_prelude::*; impl, I: 'static> Pallet { /// Note: if `maybe_depositor` is None, that means the depositor will be a collection's owner pub(crate) fn do_set_item_metadata( - maybe_check_owner: Option, + maybe_check_origin: Option, collection: T::CollectionId, item: T::ItemId, data: BoundedVec, maybe_depositor: Option, ) -> DispatchResult { - let is_root = maybe_check_owner.is_none(); + if let Some(check_origin) = &maybe_check_origin { + ensure!( + Self::has_role(&collection, &check_origin, CollectionRole::Admin), + Error::::NoPermission + ); + } + + let is_root = maybe_check_origin.is_none(); let mut collection_details = Collection::::get(&collection).ok_or(Error::::UnknownCollection)?; @@ -37,10 +44,6 @@ impl, I: 'static> Pallet { Error::::LockedItemMetadata ); - if let Some(check_owner) = &maybe_check_owner { - ensure!(check_owner == &collection_details.owner, Error::::NoPermission); - } - let collection_config = Self::get_collection_config(&collection)?; ItemMetadataOf::::try_mutate_exists(collection, item, |metadata| { @@ -89,22 +92,26 @@ impl, I: 'static> Pallet { } pub(crate) fn do_clear_item_metadata( - maybe_check_owner: Option, + maybe_check_origin: Option, collection: T::CollectionId, item: T::ItemId, ) -> DispatchResult { - let is_root = maybe_check_owner.is_none(); + if let Some(check_origin) = &maybe_check_origin { + ensure!( + Self::has_role(&collection, &check_origin, CollectionRole::Admin), + Error::::NoPermission + ); + } + + let is_root = maybe_check_origin.is_none(); let metadata = ItemMetadataOf::::take(collection, item) .ok_or(Error::::MetadataNotFound)?; let mut collection_details = Collection::::get(&collection).ok_or(Error::::UnknownCollection)?; + let depositor_account = metadata.deposit.account.unwrap_or(collection_details.owner.clone()); - if let Some(check_owner) = &maybe_check_owner { - ensure!(check_owner == &collection_details.owner, Error::::NoPermission); - } - // NOTE: if the item was previously burned, the ItemConfigOf record might not exist let is_locked = Self::get_item_config(&collection, &item) .map_or(false, |c| c.has_disabled_setting(ItemSetting::UnlockedMetadata)); @@ -125,29 +132,32 @@ impl, I: 'static> Pallet { } pub(crate) fn do_set_collection_metadata( - maybe_check_owner: Option, + maybe_check_origin: Option, collection: T::CollectionId, data: BoundedVec, ) -> DispatchResult { + if let Some(check_origin) = &maybe_check_origin { + ensure!( + Self::has_role(&collection, &check_origin, CollectionRole::Admin), + Error::::NoPermission + ); + } + + let is_root = maybe_check_origin.is_none(); let collection_config = Self::get_collection_config(&collection)?; ensure!( - maybe_check_owner.is_none() || - collection_config.is_setting_enabled(CollectionSetting::UnlockedMetadata), + is_root || collection_config.is_setting_enabled(CollectionSetting::UnlockedMetadata), Error::::LockedCollectionMetadata ); let mut details = Collection::::get(&collection).ok_or(Error::::UnknownCollection)?; - if let Some(check_owner) = &maybe_check_owner { - ensure!(check_owner == &details.owner, Error::::NoPermission); - } CollectionMetadataOf::::try_mutate_exists(collection, |metadata| { let old_deposit = metadata.take().map_or(Zero::zero(), |m| m.deposit); details.owner_deposit.saturating_reduce(old_deposit); let mut deposit = Zero::zero(); - if maybe_check_owner.is_some() && - collection_config.is_setting_enabled(CollectionSetting::DepositRequired) + if !is_root && collection_config.is_setting_enabled(CollectionSetting::DepositRequired) { deposit = T::DepositPerByte::get() .saturating_mul(((data.len()) as u32).into()) @@ -170,18 +180,22 @@ impl, I: 'static> Pallet { } pub(crate) fn do_clear_collection_metadata( - maybe_check_owner: Option, + maybe_check_origin: Option, collection: T::CollectionId, ) -> DispatchResult { - let details = - Collection::::get(&collection).ok_or(Error::::UnknownCollection)?; - if let Some(check_owner) = &maybe_check_owner { - ensure!(check_owner == &details.owner, Error::::NoPermission); + if let Some(check_origin) = &maybe_check_origin { + ensure!( + Self::has_role(&collection, &check_origin, CollectionRole::Admin), + Error::::NoPermission + ); } + let details = + Collection::::get(&collection).ok_or(Error::::UnknownCollection)?; let collection_config = Self::get_collection_config(&collection)?; + ensure!( - maybe_check_owner.is_none() || + maybe_check_origin.is_none() || collection_config.is_setting_enabled(CollectionSetting::UnlockedMetadata), Error::::LockedCollectionMetadata ); diff --git a/frame/nfts/src/features/roles.rs b/frame/nfts/src/features/roles.rs index f3fa277b1..f91f3ecb6 100644 --- a/frame/nfts/src/features/roles.rs +++ b/frame/nfts/src/features/roles.rs @@ -82,6 +82,21 @@ impl, I: 'static> Pallet { .map_or(false, |roles| roles.has_role(role)) } + /// Finds the account by a provided role within a collection. + /// + /// - `collection_id`: A collection to check the role in. + /// - `role`: A role to find the account for. + /// + /// Returns `Some(T::AccountId)` if the record was found, `None` otherwise. + pub(crate) fn find_account_by_role( + collection_id: &T::CollectionId, + role: CollectionRole, + ) -> Option { + CollectionRoleOf::::iter_prefix(&collection_id).into_iter().find_map( + |(account, roles)| if roles.has_role(role) { Some(account.clone()) } else { None }, + ) + } + /// Groups provided roles by account, given one account could have multiple roles. /// /// - `input`: A vector of (Account, Role) tuples. diff --git a/frame/nfts/src/features/settings.rs b/frame/nfts/src/features/settings.rs index 91bff9dc6..080d7b97f 100644 --- a/frame/nfts/src/features/settings.rs +++ b/frame/nfts/src/features/settings.rs @@ -57,7 +57,7 @@ impl, I: 'static> Pallet { } pub(crate) fn do_update_mint_settings( - maybe_check_owner: Option, + maybe_check_origin: Option, collection: T::CollectionId, mint_settings: MintSettings< BalanceOf, @@ -65,10 +65,11 @@ impl, I: 'static> Pallet { T::CollectionId, >, ) -> DispatchResult { - let details = - Collection::::get(&collection).ok_or(Error::::UnknownCollection)?; - if let Some(check_owner) = &maybe_check_owner { - ensure!(check_owner == &details.owner, Error::::NoPermission); + if let Some(check_origin) = &maybe_check_origin { + ensure!( + Self::has_role(&collection, &check_origin, CollectionRole::Issuer), + Error::::NoPermission + ); } CollectionConfigOf::::try_mutate(collection, |maybe_config| { diff --git a/frame/nfts/src/features/transfer.rs b/frame/nfts/src/features/transfer.rs index 998df81cd..00b5d4e76 100644 --- a/frame/nfts/src/features/transfer.rs +++ b/frame/nfts/src/features/transfer.rs @@ -48,16 +48,6 @@ impl, I: 'static> Pallet { Item::::get(&collection, &item).ok_or(Error::::UnknownItem)?; with_details(&collection_details, &mut details)?; - if details.deposit.account == details.owner { - // Move the deposit to the new owner. - T::Currency::repatriate_reserved( - &details.owner, - &dest, - details.deposit.amount, - Reserved, - )?; - } - Account::::remove((&details.owner, &collection, &item)); Account::::insert((&dest, &collection, &item), ()); let origin = details.owner; diff --git a/frame/nfts/src/lib.rs b/frame/nfts/src/lib.rs index 2d19cf045..4d9547659 100644 --- a/frame/nfts/src/lib.rs +++ b/frame/nfts/src/lib.rs @@ -30,6 +30,7 @@ #[cfg(feature = "runtime-benchmarks")] mod benchmarking; +pub mod migration; #[cfg(test)] pub mod mock; #[cfg(test)] @@ -58,6 +59,9 @@ pub use pallet::*; pub use types::*; pub use weights::WeightInfo; +/// The log target of this pallet. +pub const LOG_TARGET: &'static str = "runtime::nfts"; + type AccountIdLookupOf = <::Lookup as StaticLookup>::Source; #[frame_support::pallet] @@ -67,9 +71,13 @@ pub mod pallet { use frame_system::pallet_prelude::*; use sp_runtime::traits::{IdentifyAccount, Verify}; + /// The current storage version. + const STORAGE_VERSION: StorageVersion = StorageVersion::new(1); + #[pallet::pallet] #[pallet::generate_store(pub(super) trait Store)] - pub struct Pallet(_); + #[pallet::storage_version(STORAGE_VERSION)] + pub struct Pallet(PhantomData<(T, I)>); #[cfg(feature = "runtime-benchmarks")] pub trait BenchmarkHelper { @@ -628,6 +636,8 @@ pub mod pallet { MaxAttributesLimitReached, /// The provided namespace isn't supported in this call. WrongNamespace, + /// Can't delete non-empty collections. + CollectionNotEmpty, } #[pallet::call] @@ -719,20 +729,22 @@ pub mod pallet { /// The origin must conform to `ForceOrigin` or must be `Signed` and the sender must be the /// owner of the `collection`. /// + /// NOTE: The collection must have 0 items to be destroyed. + /// /// - `collection`: The identifier of the collection to be destroyed. /// - `witness`: Information on the items minted in the collection. This must be /// correct. /// /// Emits `Destroyed` event when successful. /// - /// Weight: `O(n + m)` where: - /// - `n = witness.items` + /// Weight: `O(m + c + a)` where: /// - `m = witness.item_metadatas` + /// - `c = witness.item_configs` /// - `a = witness.attributes` #[pallet::call_index(2)] #[pallet::weight(T::WeightInfo::destroy( - witness.items, - witness.item_metadatas, + witness.item_metadatas, + witness.item_configs, witness.attributes, ))] pub fn destroy( @@ -746,8 +758,8 @@ pub mod pallet { let details = Self::do_destroy_collection(collection, witness, maybe_check_owner)?; Ok(Some(T::WeightInfo::destroy( - details.items, details.item_metadatas, + details.item_configs, details.attributes, )) .into()) @@ -755,7 +767,7 @@ pub mod pallet { /// Mint an item of a particular collection. /// - /// The origin must be Signed and the sender must be the Issuer of the `collection`. + /// The origin must be Signed and the sender must comply with the `mint_settings` rules. /// /// - `collection`: The collection of the item to be minted. /// - `item`: An identifier of the new item. @@ -789,11 +801,6 @@ pub mod pallet { mint_to.clone(), item_config, |collection_details, collection_config| { - // Issuer can mint regardless of mint settings - if Self::has_role(&collection, &caller, CollectionRole::Issuer) { - return Ok(()) - } - let mint_settings = collection_config.mint_settings; let now = frame_system::Pallet::::block_number(); @@ -805,7 +812,12 @@ pub mod pallet { } match mint_settings.mint_type { - MintType::Issuer => return Err(Error::::NoPermission.into()), + MintType::Issuer => { + ensure!( + Self::has_role(&collection, &caller, CollectionRole::Issuer), + Error::::NoPermission + ); + }, MintType::HolderOf(collection_id) => { let MintWitness { owned_item } = witness_data.ok_or(Error::::BadWitness)?; @@ -899,38 +911,30 @@ pub mod pallet { /// Destroy a single item. /// - /// Origin must be Signed and the signing account must be either: - /// - the Admin of the `collection`; - /// - the Owner of the `item`; + /// The origin must conform to `ForceOrigin` or must be Signed and the signing account must + /// be the owner of the `item`. /// /// - `collection`: The collection of the item to be burned. /// - `item`: The item to be burned. - /// - `check_owner`: If `Some` then the operation will fail with `WrongOwner` unless the - /// item is owned by this value. /// - /// Emits `Burned` with the actual amount burned. + /// Emits `Burned`. /// /// Weight: `O(1)` - /// Modes: `check_owner.is_some()`. #[pallet::call_index(5)] #[pallet::weight(T::WeightInfo::burn())] pub fn burn( origin: OriginFor, collection: T::CollectionId, item: T::ItemId, - check_owner: Option>, ) -> DispatchResult { - let origin = ensure_signed(origin)?; - let check_owner = check_owner.map(T::Lookup::lookup).transpose()?; + let maybe_check_origin = T::ForceOrigin::try_origin(origin) + .map(|_| None) + .or_else(|origin| ensure_signed(origin).map(Some).map_err(DispatchError::from))?; Self::do_burn(collection, item, |details| { - let is_admin = Self::has_role(&collection, &origin, CollectionRole::Admin); - let is_permitted = is_admin || details.owner == origin; - ensure!(is_permitted, Error::::NoPermission); - ensure!( - check_owner.map_or(true, |o| o == details.owner), - Error::::WrongOwner - ); + if let Some(check_origin) = maybe_check_origin { + ensure!(details.owner == check_origin, Error::::NoPermission); + } Ok(()) }) } @@ -938,7 +942,6 @@ pub mod pallet { /// Move an item from the sender account to another. /// /// Origin must be Signed and the signing account must be either: - /// - the Admin of the `collection`; /// - the Owner of the `item`; /// - the approved delegate for the `item` (in this case, the approval is reset). /// @@ -962,8 +965,7 @@ pub mod pallet { let dest = T::Lookup::lookup(dest)?; Self::do_transfer(collection, item, dest, |_, details| { - let is_admin = Self::has_role(&collection, &origin, CollectionRole::Admin); - if details.owner != origin && !is_admin { + if details.owner != origin { let deadline = details.approvals.get(&origin).ok_or(Error::::NoPermission)?; if let Some(d) = deadline { @@ -1086,12 +1088,13 @@ pub mod pallet { /// Disallows specified settings for the whole collection. /// - /// Origin must be Signed and the sender should be the Freezer of the `collection`. + /// Origin must be Signed and the sender should be the Owner of the `collection`. /// /// - `collection`: The collection to be locked. /// - `lock_settings`: The settings to be locked. /// /// Note: it's possible to only lock(set) the setting, but not to unset it. + /// /// Emits `CollectionLocked`. /// /// Weight: `O(1)` @@ -1243,7 +1246,6 @@ pub mod pallet { /// /// Origin must be either: /// - the `Force` origin; - /// - `Signed` with the signer being the Admin of the `collection`; /// - `Signed` with the signer being the Owner of the `item`; /// /// Arguments: @@ -1273,7 +1275,6 @@ pub mod pallet { /// /// Origin must be either: /// - the `Force` origin; - /// - `Signed` with the signer being the Admin of the `collection`; /// - `Signed` with the signer being the Owner of the `item`; /// /// Arguments: @@ -1298,8 +1299,8 @@ pub mod pallet { /// Disallows changing the metadata or attributes of the item. /// - /// Origin must be either `ForceOrigin` or Signed and the sender should be the Owner of the - /// `collection`. + /// Origin must be either `ForceOrigin` or Signed and the sender should be the Admin + /// of the `collection`. /// /// - `collection`: The collection if the `item`. /// - `item`: An item to be locked. @@ -1307,8 +1308,8 @@ pub mod pallet { /// - `lock_attributes`: Specifies whether the attributes in the `CollectionOwner` namespace /// should be locked. /// - /// Note: `lock_attributes` affects the attributes in the `CollectionOwner` namespace - /// only. When the metadata or attributes are locked, it won't be possible the unlock them. + /// Note: `lock_attributes` affects the attributes in the `CollectionOwner` namespace only. + /// When the metadata or attributes are locked, it won't be possible the unlock them. /// /// Emits `ItemPropertiesLocked`. /// @@ -1322,11 +1323,11 @@ pub mod pallet { lock_metadata: bool, lock_attributes: bool, ) -> DispatchResult { - let maybe_check_owner = T::ForceOrigin::try_origin(origin) + let maybe_check_origin = T::ForceOrigin::try_origin(origin) .map(|_| None) .or_else(|origin| ensure_signed(origin).map(Some).map_err(DispatchError::from))?; Self::do_lock_item_properties( - maybe_check_owner, + maybe_check_origin, collection, item, lock_metadata, @@ -1337,7 +1338,7 @@ pub mod pallet { /// Set an attribute for a collection or item. /// /// Origin must be Signed and must conform to the namespace ruleset: - /// - `CollectionOwner` namespace could be modified by the `collection` owner only; + /// - `CollectionOwner` namespace could be modified by the `collection` Admin only; /// - `ItemOwner` namespace could be modified by the `maybe_item` owner only. `maybe_item` /// should be set in that case; /// - `Account(AccountId)` namespace could be modified only when the `origin` was given a @@ -1367,15 +1368,12 @@ pub mod pallet { value: BoundedVec, ) -> DispatchResult { let origin = ensure_signed(origin)?; - Self::do_set_attribute( - origin.clone(), - collection, - maybe_item, - namespace, - key, - value, - origin, - ) + let depositor = match namespace { + AttributeNamespace::CollectionOwner => + Self::collection_owner(collection).ok_or(Error::::UnknownCollection)?, + _ => origin.clone(), + }; + Self::do_set_attribute(origin, collection, maybe_item, namespace, key, value, depositor) } /// Force-set an attribute for a collection or item. @@ -1490,7 +1488,7 @@ pub mod pallet { /// Set the metadata for an item. /// - /// Origin must be either `ForceOrigin` or Signed and the sender should be the Owner of the + /// Origin must be either `ForceOrigin` or Signed and the sender should be the Admin of the /// `collection`. /// /// If the origin is Signed, then funds of signer are reserved according to the formula: @@ -1512,15 +1510,15 @@ pub mod pallet { item: T::ItemId, data: BoundedVec, ) -> DispatchResult { - let maybe_check_owner = T::ForceOrigin::try_origin(origin) + let maybe_check_origin = T::ForceOrigin::try_origin(origin) .map(|_| None) .or_else(|origin| ensure_signed(origin).map(Some).map_err(DispatchError::from))?; - Self::do_set_item_metadata(maybe_check_owner, collection, item, data, None) + Self::do_set_item_metadata(maybe_check_origin, collection, item, data, None) } /// Clear the metadata for an item. /// - /// Origin must be either `ForceOrigin` or Signed and the sender should be the Owner of the + /// Origin must be either `ForceOrigin` or Signed and the sender should be the Admin of the /// `collection`. /// /// Any deposit is freed for the collection's owner. @@ -1538,15 +1536,15 @@ pub mod pallet { collection: T::CollectionId, item: T::ItemId, ) -> DispatchResult { - let maybe_check_owner = T::ForceOrigin::try_origin(origin) + let maybe_check_origin = T::ForceOrigin::try_origin(origin) .map(|_| None) .or_else(|origin| ensure_signed(origin).map(Some).map_err(DispatchError::from))?; - Self::do_clear_item_metadata(maybe_check_owner, collection, item) + Self::do_clear_item_metadata(maybe_check_origin, collection, item) } /// Set the metadata for a collection. /// - /// Origin must be either `ForceOrigin` or `Signed` and the sender should be the Owner of + /// Origin must be either `ForceOrigin` or `Signed` and the sender should be the Admin of /// the `collection`. /// /// If the origin is `Signed`, then funds of signer are reserved according to the formula: @@ -1566,15 +1564,15 @@ pub mod pallet { collection: T::CollectionId, data: BoundedVec, ) -> DispatchResult { - let maybe_check_owner = T::ForceOrigin::try_origin(origin) + let maybe_check_origin = T::ForceOrigin::try_origin(origin) .map(|_| None) .or_else(|origin| ensure_signed(origin).map(Some).map_err(DispatchError::from))?; - Self::do_set_collection_metadata(maybe_check_owner, collection, data) + Self::do_set_collection_metadata(maybe_check_origin, collection, data) } /// Clear the metadata for a collection. /// - /// Origin must be either `ForceOrigin` or `Signed` and the sender should be the Owner of + /// Origin must be either `ForceOrigin` or `Signed` and the sender should be the Admin of /// the `collection`. /// /// Any deposit is freed for the collection's owner. @@ -1590,10 +1588,10 @@ pub mod pallet { origin: OriginFor, collection: T::CollectionId, ) -> DispatchResult { - let maybe_check_owner = T::ForceOrigin::try_origin(origin) + let maybe_check_origin = T::ForceOrigin::try_origin(origin) .map(|_| None) .or_else(|origin| ensure_signed(origin).map(Some).map_err(DispatchError::from))?; - Self::do_clear_collection_metadata(maybe_check_owner, collection) + Self::do_clear_collection_metadata(maybe_check_origin, collection) } /// Set (or reset) the acceptance of ownership for a particular account. @@ -1640,8 +1638,8 @@ pub mod pallet { /// Update mint settings. /// - /// Origin must be either `ForceOrigin` or `Signed` and the sender should be the Owner of - /// the `collection`. + /// Origin must be either `ForceOrigin` or `Signed` and the sender should be the Issuer + /// of the `collection`. /// /// - `collection`: The identifier of the collection to change. /// - `mint_settings`: The new mint settings. @@ -1658,15 +1656,15 @@ pub mod pallet { T::CollectionId, >, ) -> DispatchResult { - let maybe_check_owner = T::ForceOrigin::try_origin(origin) + let maybe_check_origin = T::ForceOrigin::try_origin(origin) .map(|_| None) .or_else(|origin| ensure_signed(origin).map(Some).map_err(DispatchError::from))?; - Self::do_update_mint_settings(maybe_check_owner, collection, mint_settings) + Self::do_update_mint_settings(maybe_check_origin, collection, mint_settings) } /// Set (or reset) the price for an item. /// - /// Origin must be Signed and must be the owner of the asset `item`. + /// Origin must be Signed and must be the owner of the `item`. /// /// - `collection`: The collection of the item. /// - `item`: The item to set the price for. @@ -1827,7 +1825,7 @@ pub mod pallet { /// its metadata, attributes, who can mint it (`None` for anyone) and until what block /// number. /// - `signature`: The signature of the `data` object. - /// - `signer`: The `data` object's signer. Should be an owner of the collection. + /// - `signer`: The `data` object's signer. Should be an Issuer of the collection. /// /// Emits `Issued` on success. /// Emits `AttributeSet` if the attributes were provided. @@ -1853,7 +1851,7 @@ pub mod pallet { /// - `data`: The pre-signed approval that consists of the information about the item, /// attributes to update and until what block number. /// - `signature`: The signature of the `data` object. - /// - `signer`: The `data` object's signer. Should be an owner of the collection for the + /// - `signer`: The `data` object's signer. Should be an Admin of the collection for the /// `CollectionOwner` namespace. /// /// Emits `AttributeSet` for each provided attribute. diff --git a/frame/nfts/src/migration.rs b/frame/nfts/src/migration.rs new file mode 100644 index 000000000..33ee87e4b --- /dev/null +++ b/frame/nfts/src/migration.rs @@ -0,0 +1,117 @@ +// This file is part of Substrate. + +// Copyright (C) Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +use super::*; +use frame_support::{log, traits::OnRuntimeUpgrade}; + +pub mod v1 { + use frame_support::{pallet_prelude::*, weights::Weight}; + + use super::*; + + #[derive(Decode)] + pub struct OldCollectionDetails { + pub owner: AccountId, + pub owner_deposit: DepositBalance, + pub items: u32, + pub item_metadatas: u32, + pub attributes: u32, + } + + impl OldCollectionDetails { + fn migrate_to_v1(self, item_configs: u32) -> CollectionDetails { + CollectionDetails { + owner: self.owner, + owner_deposit: self.owner_deposit, + items: self.items, + item_metadatas: self.item_metadatas, + item_configs, + attributes: self.attributes, + } + } + } + + pub struct MigrateToV1(sp_std::marker::PhantomData); + impl OnRuntimeUpgrade for MigrateToV1 { + fn on_runtime_upgrade() -> Weight { + let current_version = Pallet::::current_storage_version(); + let onchain_version = Pallet::::on_chain_storage_version(); + + log::info!( + target: LOG_TARGET, + "Running migration with current storage version {:?} / onchain {:?}", + current_version, + onchain_version + ); + + if onchain_version == 0 && current_version == 1 { + let mut translated = 0u64; + let mut configs_iterated = 0u64; + Collection::::translate::< + OldCollectionDetails>, + _, + >(|key, old_value| { + let item_configs = ItemConfigOf::::iter_prefix(&key).count() as u32; + configs_iterated += item_configs as u64; + translated.saturating_inc(); + Some(old_value.migrate_to_v1(item_configs)) + }); + + current_version.put::>(); + + log::info!( + target: LOG_TARGET, + "Upgraded {} records, storage to version {:?}", + translated, + current_version + ); + T::DbWeight::get().reads_writes(translated + configs_iterated + 1, translated + 1) + } else { + log::info!( + target: LOG_TARGET, + "Migration did not execute. This probably should be removed" + ); + T::DbWeight::get().reads(1) + } + } + + #[cfg(feature = "try-runtime")] + fn pre_upgrade() -> Result, &'static str> { + let current_version = Pallet::::current_storage_version(); + let onchain_version = Pallet::::on_chain_storage_version(); + ensure!(onchain_version == 0 && current_version == 1, "migration from version 0 to 1."); + let prev_count = Collection::::iter().count(); + Ok((prev_count as u32).encode()) + } + + #[cfg(feature = "try-runtime")] + fn post_upgrade(prev_count: Vec) -> Result<(), &'static str> { + let prev_count: u32 = Decode::decode(&mut prev_count.as_slice()).expect( + "the state parameter should be something that was generated by pre_upgrade", + ); + let post_count = Collection::::iter().count() as u32; + assert_eq!( + prev_count, post_count, + "the records count before and after the migration should be the same" + ); + + ensure!(Pallet::::on_chain_storage_version() == 1, "wrong storage version"); + + Ok(()) + } + } +} diff --git a/frame/nfts/src/tests.rs b/frame/nfts/src/tests.rs index e44e4151f..694468ace 100644 --- a/frame/nfts/src/tests.rs +++ b/frame/nfts/src/tests.rs @@ -215,21 +215,44 @@ fn lifecycle_should_work() { assert_eq!(items(), vec![(account(1), 0, 70), (account(10), 0, 42), (account(20), 0, 69)]); assert_eq!(Collection::::get(0).unwrap().items, 3); assert_eq!(Collection::::get(0).unwrap().item_metadatas, 0); + assert_eq!(Collection::::get(0).unwrap().item_configs, 3); - assert_eq!(Balances::reserved_balance(&account(2)), 0); + assert_eq!(Balances::reserved_balance(&account(1)), 8); assert_ok!(Nfts::transfer(RuntimeOrigin::signed(account(1)), 0, 70, account(2))); - assert_eq!(Balances::reserved_balance(&account(2)), 1); + assert_eq!(Balances::reserved_balance(&account(1)), 8); + assert_eq!(Balances::reserved_balance(&account(2)), 0); assert_ok!(Nfts::set_metadata(RuntimeOrigin::signed(account(1)), 0, 42, bvec![42, 42])); - assert_eq!(Balances::reserved_balance(&account(1)), 10); + assert_eq!(Balances::reserved_balance(&account(1)), 11); assert!(ItemMetadataOf::::contains_key(0, 42)); assert_ok!(Nfts::set_metadata(RuntimeOrigin::signed(account(1)), 0, 69, bvec![69, 69])); - assert_eq!(Balances::reserved_balance(&account(1)), 13); + assert_eq!(Balances::reserved_balance(&account(1)), 14); assert!(ItemMetadataOf::::contains_key(0, 69)); - + assert!(ItemConfigOf::::contains_key(0, 69)); let w = Nfts::get_destroy_witness(&0).unwrap(); - assert_eq!(w.items, 3); assert_eq!(w.item_metadatas, 2); + assert_eq!(w.item_configs, 3); + assert_noop!( + Nfts::destroy(RuntimeOrigin::signed(account(1)), 0, w), + Error::::CollectionNotEmpty + ); + + assert_ok!(Nfts::set_attribute( + RuntimeOrigin::signed(account(1)), + 0, + Some(69), + AttributeNamespace::CollectionOwner, + bvec![0], + bvec![0], + )); + assert_ok!(Nfts::burn(RuntimeOrigin::signed(account(10)), 0, 42)); + assert_ok!(Nfts::burn(RuntimeOrigin::signed(account(20)), 0, 69)); + assert_ok!(Nfts::burn(RuntimeOrigin::root(), 0, 70)); + + let w = Nfts::get_destroy_witness(&0).unwrap(); + assert_eq!(w.attributes, 1); + assert_eq!(w.item_metadatas, 0); + assert_eq!(w.item_configs, 0); assert_ok!(Nfts::destroy(RuntimeOrigin::signed(account(1)), 0, w)); assert_eq!(Balances::reserved_balance(&account(1)), 0); @@ -240,6 +263,8 @@ fn lifecycle_should_work() { assert!(!CollectionMetadataOf::::contains_key(0)); assert!(!ItemMetadataOf::::contains_key(0, 42)); assert!(!ItemMetadataOf::::contains_key(0, 69)); + assert!(!ItemConfigOf::::contains_key(0, 69)); + assert_eq!(attributes(0), vec![]); assert_eq!(collections(), vec![]); assert_eq!(items(), vec![]); }); @@ -256,14 +281,51 @@ fn destroy_with_bad_witness_should_not_work() { )); let w = Collection::::get(0).unwrap().destroy_witness(); - assert_ok!(Nfts::mint(RuntimeOrigin::signed(account(1)), 0, 42, account(1), None)); assert_noop!( - Nfts::destroy(RuntimeOrigin::signed(account(1)), 0, w), + Nfts::destroy( + RuntimeOrigin::signed(account(1)), + 0, + DestroyWitness { item_configs: 1, ..w } + ), Error::::BadWitness ); }); } +#[test] +fn destroy_should_work() { + new_test_ext().execute_with(|| { + Balances::make_free_balance_be(&account(1), 100); + assert_ok!(Nfts::create( + RuntimeOrigin::signed(account(1)), + account(1), + collection_config_with_all_settings_enabled() + )); + + assert_ok!(Nfts::mint(RuntimeOrigin::signed(account(1)), 0, 42, account(2), None)); + assert_noop!( + Nfts::destroy( + RuntimeOrigin::signed(account(1)), + 0, + Nfts::get_destroy_witness(&0).unwrap() + ), + Error::::CollectionNotEmpty + ); + assert_ok!(Nfts::lock_item_transfer(RuntimeOrigin::signed(account(1)), 0, 42)); + assert_ok!(Nfts::burn(RuntimeOrigin::signed(account(2)), 0, 42)); + assert_eq!(Collection::::get(0).unwrap().item_configs, 1); + assert_eq!(ItemConfigOf::::iter_prefix(0).count() as u32, 1); + assert!(ItemConfigOf::::contains_key(0, 42)); + assert_ok!(Nfts::destroy( + RuntimeOrigin::signed(account(1)), + 0, + Nfts::get_destroy_witness(&0).unwrap() + )); + assert!(!ItemConfigOf::::contains_key(0, 42)); + assert_eq!(ItemConfigOf::::iter_prefix(0).count() as u32, 0); + }); +} + #[test] fn mint_should_work() { new_test_ext().execute_with(|| { @@ -491,8 +553,9 @@ fn origin_guards_should_work() { Nfts::mint(RuntimeOrigin::signed(account(2)), 0, 69, account(2), None), Error::::NoPermission ); + assert_ok!(Nfts::mint(RuntimeOrigin::signed(account(1)), 0, 43, account(2), None)); assert_noop!( - Nfts::burn(RuntimeOrigin::signed(account(2)), 0, 42, None), + Nfts::burn(RuntimeOrigin::signed(account(1)), 0, 43), Error::::NoPermission ); let w = Nfts::get_destroy_witness(&0).unwrap(); @@ -536,13 +599,13 @@ fn transfer_owner_should_work() { // Mint and set metadata now and make sure that deposit gets transferred back. assert_ok!(Nfts::set_collection_metadata( - RuntimeOrigin::signed(account(2)), + RuntimeOrigin::signed(account(1)), 0, bvec![0u8; 20], )); assert_ok!(Nfts::mint(RuntimeOrigin::signed(account(1)), 0, 42, account(1), None)); assert_eq!(Balances::reserved_balance(&account(1)), 1); - assert_ok!(Nfts::set_metadata(RuntimeOrigin::signed(account(2)), 0, 42, bvec![0u8; 20])); + assert_ok!(Nfts::set_metadata(RuntimeOrigin::signed(account(1)), 0, 42, bvec![0u8; 20])); assert_ok!(Nfts::set_accept_ownership(RuntimeOrigin::signed(account(3)), Some(0))); assert_ok!(Nfts::transfer_ownership(RuntimeOrigin::signed(account(2)), 0, account(3))); assert_eq!(collections(), vec![(account(3), 0)]); @@ -552,8 +615,9 @@ fn transfer_owner_should_work() { assert_eq!(Balances::reserved_balance(&account(3)), 44); assert_ok!(Nfts::transfer(RuntimeOrigin::signed(account(1)), 0, 42, account(2))); - assert_eq!(Balances::reserved_balance(&account(1)), 0); - assert_eq!(Balances::reserved_balance(&account(2)), 1); + // reserved_balance of accounts 1 & 2 should be unchanged: + assert_eq!(Balances::reserved_balance(&account(1)), 1); + assert_eq!(Balances::reserved_balance(&account(2)), 0); // 2's acceptance from before is reset when it became an owner, so it cannot be transferred // without a fresh acceptance. @@ -583,8 +647,14 @@ fn set_team_should_work() { assert_ok!(Nfts::mint(RuntimeOrigin::signed(account(2)), 0, 42, account(2), None)); assert_ok!(Nfts::lock_item_transfer(RuntimeOrigin::signed(account(4)), 0, 42)); assert_ok!(Nfts::unlock_item_transfer(RuntimeOrigin::signed(account(4)), 0, 42)); - assert_ok!(Nfts::transfer(RuntimeOrigin::signed(account(3)), 0, 42, account(3))); - assert_ok!(Nfts::burn(RuntimeOrigin::signed(account(3)), 0, 42, None)); + assert_noop!( + Nfts::transfer(RuntimeOrigin::signed(account(3)), 0, 42, account(3)), + Error::::NoPermission + ); + assert_noop!( + Nfts::burn(RuntimeOrigin::signed(account(3)), 0, 42), + Error::::NoPermission + ); }); } @@ -594,7 +664,7 @@ fn set_collection_metadata_should_work() { // Cannot add metadata to unknown item assert_noop!( Nfts::set_collection_metadata(RuntimeOrigin::signed(account(1)), 0, bvec![0u8; 20]), - Error::::NoConfig, + Error::::NoPermission, ); assert_ok!(Nfts::force_create( RuntimeOrigin::root(), @@ -668,7 +738,7 @@ fn set_collection_metadata_should_work() { ); assert_noop!( Nfts::clear_collection_metadata(RuntimeOrigin::signed(account(1)), 1), - Error::::UnknownCollection + Error::::NoPermission ); assert_noop!( Nfts::clear_collection_metadata(RuntimeOrigin::signed(account(1)), 0), @@ -743,7 +813,7 @@ fn set_item_metadata_should_work() { ); assert_noop!( Nfts::clear_metadata(RuntimeOrigin::signed(account(1)), 1, 42), - Error::::MetadataNotFound, + Error::::NoPermission, ); assert_ok!(Nfts::clear_metadata(RuntimeOrigin::root(), 0, 42)); assert!(!ItemMetadataOf::::contains_key(0, 42)); @@ -832,6 +902,7 @@ fn set_collection_owner_attributes_should_work() { ); assert_eq!(Balances::reserved_balance(account(1)), 16); + assert_ok!(Nfts::burn(RuntimeOrigin::root(), 0, 0)); let w = Nfts::get_destroy_witness(&0).unwrap(); assert_ok!(Nfts::destroy(RuntimeOrigin::signed(account(1)), 0, w)); assert_eq!(attributes(0), vec![]); @@ -997,7 +1068,7 @@ fn set_item_owner_attributes_should_work() { assert_eq!(Balances::reserved_balance(account(3)), 13); // validate attributes on item deletion - assert_ok!(Nfts::burn(RuntimeOrigin::signed(account(3)), 0, 0, None)); + assert_ok!(Nfts::burn(RuntimeOrigin::signed(account(3)), 0, 0)); assert_eq!( attributes(0), vec![ @@ -1317,7 +1388,7 @@ fn preserve_config_for_frozen_items() { assert_ok!(Nfts::mint(RuntimeOrigin::signed(account(1)), 0, 1, account(1), None)); // if the item is not locked/frozen then the config gets deleted on item burn - assert_ok!(Nfts::burn(RuntimeOrigin::signed(account(1)), 0, 1, Some(account(1)))); + assert_ok!(Nfts::burn(RuntimeOrigin::signed(account(1)), 0, 1)); assert!(!ItemConfigOf::::contains_key(0, 1)); // lock the item and ensure the config stays unchanged @@ -1329,7 +1400,7 @@ fn preserve_config_for_frozen_items() { let config = ItemConfigOf::::get(0, 0).unwrap(); assert_eq!(config, expect_config); - assert_ok!(Nfts::burn(RuntimeOrigin::signed(account(1)), 0, 0, Some(account(1)))); + assert_ok!(Nfts::burn(RuntimeOrigin::signed(account(1)), 0, 0)); let config = ItemConfigOf::::get(0, 0).unwrap(); assert_eq!(config, expect_config); @@ -1405,6 +1476,7 @@ fn force_update_collection_should_work() { Balances::make_free_balance_be(&account(5), 100); assert_ok!(Nfts::force_collection_owner(RuntimeOrigin::root(), 0, account(5))); + assert_ok!(Nfts::set_team(RuntimeOrigin::root(), 0, account(2), account(5), account(4))); assert_eq!(collections(), vec![(account(5), 0)]); assert_eq!(Balances::reserved_balance(account(1)), 2); assert_eq!(Balances::reserved_balance(account(5)), 63); @@ -1475,7 +1547,7 @@ fn burn_works() { )); assert_noop!( - Nfts::burn(RuntimeOrigin::signed(account(5)), 0, 42, Some(account(5))), + Nfts::burn(RuntimeOrigin::signed(account(5)), 0, 42), Error::::UnknownItem ); @@ -1496,16 +1568,12 @@ fn burn_works() { assert_eq!(Balances::reserved_balance(account(1)), 2); assert_noop!( - Nfts::burn(RuntimeOrigin::signed(account(0)), 0, 42, None), + Nfts::burn(RuntimeOrigin::signed(account(0)), 0, 42), Error::::NoPermission ); - assert_noop!( - Nfts::burn(RuntimeOrigin::signed(account(5)), 0, 42, Some(account(6))), - Error::::WrongOwner - ); - assert_ok!(Nfts::burn(RuntimeOrigin::signed(account(5)), 0, 42, Some(account(5)))); - assert_ok!(Nfts::burn(RuntimeOrigin::signed(account(3)), 0, 69, Some(account(5)))); + assert_ok!(Nfts::burn(RuntimeOrigin::signed(account(5)), 0, 42)); + assert_ok!(Nfts::burn(RuntimeOrigin::signed(account(5)), 0, 69)); assert_eq!(Balances::reserved_balance(account(1)), 0); }); } @@ -1819,21 +1887,21 @@ fn cancel_approval_works_with_admin() { None )); assert_noop!( - Nfts::cancel_approval(RuntimeOrigin::signed(account(1)), 1, 42, account(1)), + Nfts::cancel_approval(RuntimeOrigin::signed(account(2)), 1, 42, account(1)), Error::::UnknownItem ); assert_noop!( - Nfts::cancel_approval(RuntimeOrigin::signed(account(1)), 0, 43, account(1)), + Nfts::cancel_approval(RuntimeOrigin::signed(account(2)), 0, 43, account(1)), Error::::UnknownItem ); assert_noop!( - Nfts::cancel_approval(RuntimeOrigin::signed(account(1)), 0, 42, account(4)), + Nfts::cancel_approval(RuntimeOrigin::signed(account(2)), 0, 42, account(4)), Error::::NotDelegate ); - assert_ok!(Nfts::cancel_approval(RuntimeOrigin::signed(account(1)), 0, 42, account(3))); + assert_ok!(Nfts::cancel_approval(RuntimeOrigin::signed(account(2)), 0, 42, account(3))); assert_noop!( - Nfts::cancel_approval(RuntimeOrigin::signed(account(1)), 0, 42, account(1)), + Nfts::cancel_approval(RuntimeOrigin::signed(account(2)), 0, 42, account(1)), Error::::NotDelegate ); }); @@ -3013,6 +3081,7 @@ fn add_remove_item_attributes_approval_should_work() { #[test] fn pre_signed_mints_should_work() { new_test_ext().execute_with(|| { + let user_0 = account(0); let user_1_pair = sp_core::sr25519::Pair::from_string("//Alice", None).unwrap(); let user_1_signer = MultiSigner::Sr25519(user_1_pair.public()); let user_1 = user_1_signer.clone().into_account(); @@ -3029,10 +3098,10 @@ fn pre_signed_mints_should_work() { let user_2 = account(2); let user_3 = account(3); - Balances::make_free_balance_be(&user_1, 100); + Balances::make_free_balance_be(&user_0, 100); Balances::make_free_balance_be(&user_2, 100); assert_ok!(Nfts::create( - RuntimeOrigin::signed(user_1.clone()), + RuntimeOrigin::signed(user_0.clone()), user_1.clone(), collection_config_with_all_settings_enabled(), )); @@ -3069,7 +3138,7 @@ fn pre_signed_mints_should_work() { assert_eq!(deposit.account, Some(user_2.clone())); assert_eq!(deposit.amount, 3); - assert_eq!(Balances::free_balance(&user_1), 100 - 2); // 2 - collection deposit + assert_eq!(Balances::free_balance(&user_0), 100 - 2); // 2 - collection deposit assert_eq!(Balances::free_balance(&user_2), 100 - 1 - 3 - 6); // 1 - item deposit, 3 - metadata, 6 - attributes assert_noop!( @@ -3082,7 +3151,7 @@ fn pre_signed_mints_should_work() { Error::::AlreadyExists ); - assert_ok!(Nfts::burn(RuntimeOrigin::signed(user_2.clone()), 0, 0, Some(user_2.clone()))); + assert_ok!(Nfts::burn(RuntimeOrigin::signed(user_2.clone()), 0, 0)); assert_eq!(Balances::free_balance(&user_2), 100 - 6); // validate the `only_account` field diff --git a/frame/nfts/src/types.rs b/frame/nfts/src/types.rs index f8ab2a355..fe6d31c12 100644 --- a/frame/nfts/src/types.rs +++ b/frame/nfts/src/types.rs @@ -92,6 +92,8 @@ pub struct CollectionDetails { pub(super) items: u32, /// The total number of outstanding item metadata of this collection. pub(super) item_metadatas: u32, + /// The total number of outstanding item configs of this collection. + pub(super) item_configs: u32, /// The total number of attributes for this collection. pub(super) attributes: u32, } @@ -99,12 +101,12 @@ pub struct CollectionDetails { /// Witness data for the destroy transactions. #[derive(Copy, Clone, Encode, Decode, Eq, PartialEq, RuntimeDebug, TypeInfo, MaxEncodedLen)] pub struct DestroyWitness { - /// The total number of outstanding items of this collection. - #[codec(compact)] - pub items: u32, /// The total number of items in this collection that have outstanding item metadata. #[codec(compact)] pub item_metadatas: u32, + /// The total number of outstanding item configs of this collection. + #[codec(compact)] + pub item_configs: u32, /// The total number of attributes for this collection. #[codec(compact)] pub attributes: u32, @@ -113,8 +115,8 @@ pub struct DestroyWitness { impl CollectionDetails { pub fn destroy_witness(&self) -> DestroyWitness { DestroyWitness { - items: self.items, item_metadatas: self.item_metadatas, + item_configs: self.item_configs, attributes: self.attributes, } } diff --git a/frame/nfts/src/weights.rs b/frame/nfts/src/weights.rs index 858ae2fec..3d447c8d2 100644 --- a/frame/nfts/src/weights.rs +++ b/frame/nfts/src/weights.rs @@ -18,9 +18,9 @@ //! Autogenerated weights for pallet_nfts //! //! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev -//! DATE: 2023-02-15, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! DATE: 2023-03-10, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` //! WORST CASE MAP SIZE: `1000000` -//! HOSTNAME: `runner-osnnfcqu-project-145-concurrent-0`, CPU: `Intel(R) Xeon(R) CPU @ 2.60GHz` +//! HOSTNAME: `bm3`, CPU: `Intel(R) Core(TM) i7-7700K CPU @ 4.20GHz` //! EXECUTION: Some(Wasm), WASM-EXECUTION: Compiled, CHAIN: Some("dev"), DB CACHE: 1024 // Executed Command: @@ -33,7 +33,7 @@ // --execution=wasm // --wasm-execution=compiled // --heap-pages=4096 -// --json-file=/builds/parity/mirrors/substrate/.git/.artifacts/bench.json +// --json-file=/var/lib/gitlab-runner/builds/zyw4fam_/0/parity/mirrors/substrate/.git/.artifacts/bench.json // --pallet=pallet_nfts // --chain=dev // --header=./HEADER-APACHE2 @@ -51,7 +51,7 @@ use sp_std::marker::PhantomData; pub trait WeightInfo { fn create() -> Weight; fn force_create() -> Weight; - fn destroy(n: u32, m: u32, a: u32, ) -> Weight; + fn destroy(m: u32, c: u32, a: u32, ) -> Weight; fn mint() -> Weight; fn force_mint() -> Weight; fn burn() -> Weight; @@ -96,7 +96,7 @@ impl WeightInfo for SubstrateWeight { /// Storage: Nfts NextCollectionId (r:1 w:1) /// Proof: Nfts NextCollectionId (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) /// Storage: Nfts Collection (r:1 w:1) - /// Proof: Nfts Collection (max_values: None, max_size: Some(80), added: 2555, mode: MaxEncodedLen) + /// Proof: Nfts Collection (max_values: None, max_size: Some(84), added: 2559, mode: MaxEncodedLen) /// Storage: Nfts CollectionRoleOf (r:0 w:1) /// Proof: Nfts CollectionRoleOf (max_values: None, max_size: Some(69), added: 2544, mode: MaxEncodedLen) /// Storage: Nfts CollectionConfigOf (r:0 w:1) @@ -106,17 +106,16 @@ impl WeightInfo for SubstrateWeight { fn create() -> Weight { // Proof Size summary in bytes: // Measured: `214` - // Estimated: `3054` - // Minimum execution time: 33_769 nanoseconds. - Weight::from_parts(36_031_000, 0) - .saturating_add(Weight::from_parts(0, 3054)) + // Estimated: `5038` + // Minimum execution time: 37_598_000 picoseconds. + Weight::from_parts(38_703_000, 5038) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().writes(5_u64)) } /// Storage: Nfts NextCollectionId (r:1 w:1) /// Proof: Nfts NextCollectionId (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) /// Storage: Nfts Collection (r:1 w:1) - /// Proof: Nfts Collection (max_values: None, max_size: Some(80), added: 2555, mode: MaxEncodedLen) + /// Proof: Nfts Collection (max_values: None, max_size: Some(84), added: 2559, mode: MaxEncodedLen) /// Storage: Nfts CollectionRoleOf (r:0 w:1) /// Proof: Nfts CollectionRoleOf (max_values: None, max_size: Some(69), added: 2544, mode: MaxEncodedLen) /// Storage: Nfts CollectionConfigOf (r:0 w:1) @@ -126,54 +125,45 @@ impl WeightInfo for SubstrateWeight { fn force_create() -> Weight { // Proof Size summary in bytes: // Measured: `42` - // Estimated: `3054` - // Minimum execution time: 21_767 nanoseconds. - Weight::from_parts(22_565_000, 0) - .saturating_add(Weight::from_parts(0, 3054)) + // Estimated: `5038` + // Minimum execution time: 25_899_000 picoseconds. + Weight::from_parts(26_385_000, 5038) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().writes(5_u64)) } /// Storage: Nfts Collection (r:1 w:1) - /// Proof: Nfts Collection (max_values: None, max_size: Some(80), added: 2555, mode: MaxEncodedLen) - /// Storage: Nfts Item (r:1001 w:1000) - /// Proof: Nfts Item (max_values: None, max_size: Some(861), added: 3336, mode: MaxEncodedLen) - /// Storage: Nfts ItemMetadataOf (r:1001 w:1000) + /// Proof: Nfts Collection (max_values: None, max_size: Some(84), added: 2559, mode: MaxEncodedLen) + /// Storage: Nfts ItemMetadataOf (r:1 w:0) /// Proof: Nfts ItemMetadataOf (max_values: None, max_size: Some(140), added: 2615, mode: MaxEncodedLen) + /// Storage: Nfts CollectionRoleOf (r:1 w:1) + /// Proof: Nfts CollectionRoleOf (max_values: None, max_size: Some(69), added: 2544, mode: MaxEncodedLen) /// Storage: Nfts Attribute (r:1001 w:1000) /// Proof: Nfts Attribute (max_values: None, max_size: Some(446), added: 2921, mode: MaxEncodedLen) - /// Storage: Nfts CollectionRoleOf (r:0 w:1) - /// Proof: Nfts CollectionRoleOf (max_values: None, max_size: Some(69), added: 2544, mode: MaxEncodedLen) + /// Storage: Nfts ItemConfigOf (r:1000 w:1000) + /// Proof: Nfts ItemConfigOf (max_values: None, max_size: Some(48), added: 2523, mode: MaxEncodedLen) /// Storage: Nfts CollectionMetadataOf (r:0 w:1) /// Proof: Nfts CollectionMetadataOf (max_values: None, max_size: Some(87), added: 2562, mode: MaxEncodedLen) /// Storage: Nfts CollectionConfigOf (r:0 w:1) /// Proof: Nfts CollectionConfigOf (max_values: None, max_size: Some(73), added: 2548, mode: MaxEncodedLen) - /// Storage: Nfts ItemConfigOf (r:0 w:1000) - /// Proof: Nfts ItemConfigOf (max_values: None, max_size: Some(48), added: 2523, mode: MaxEncodedLen) - /// Storage: Nfts Account (r:0 w:1000) - /// Proof: Nfts Account (max_values: None, max_size: Some(88), added: 2563, mode: MaxEncodedLen) /// Storage: Nfts CollectionAccount (r:0 w:1) /// Proof: Nfts CollectionAccount (max_values: None, max_size: Some(68), added: 2543, mode: MaxEncodedLen) - /// The range of component `n` is `[0, 1000]`. /// The range of component `m` is `[0, 1000]`. + /// The range of component `c` is `[0, 1000]`. /// The range of component `a` is `[0, 1000]`. - fn destroy(_n: u32, m: u32, a: u32, ) -> Weight { - // Proof Size summary in bytes: - // Measured: `172781 + m * (127 ±0) + a * (402 ±0)` - // Estimated: `3347427 + m * (2615 ±0) + a * (2921 ±0)` - // Minimum execution time: 26_973_627 nanoseconds. - Weight::from_parts(19_692_361_714, 0) - .saturating_add(Weight::from_parts(0, 3347427)) - // Standard Error: 17_036 - .saturating_add(Weight::from_parts(7_797_219, 0).saturating_mul(m.into())) - // Standard Error: 17_036 - .saturating_add(Weight::from_parts(9_504_128, 0).saturating_mul(a.into())) + fn destroy(m: u32, _c: u32, a: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `32218 + a * (364 ±0)` + // Estimated: `2538589 + a * (2921 ±0)` + // Minimum execution time: 1_129_980_000 picoseconds. + Weight::from_parts(1_096_213_543, 2538589) + // Standard Error: 5_210 + .saturating_add(Weight::from_parts(92, 0).saturating_mul(m.into())) + // Standard Error: 5_210 + .saturating_add(Weight::from_parts(5_480_550, 0).saturating_mul(a.into())) .saturating_add(T::DbWeight::get().reads(1004_u64)) - .saturating_add(T::DbWeight::get().reads((1_u64).saturating_mul(m.into()))) .saturating_add(T::DbWeight::get().reads((1_u64).saturating_mul(a.into()))) - .saturating_add(T::DbWeight::get().writes(3005_u64)) - .saturating_add(T::DbWeight::get().writes((1_u64).saturating_mul(m.into()))) + .saturating_add(T::DbWeight::get().writes(1005_u64)) .saturating_add(T::DbWeight::get().writes((1_u64).saturating_mul(a.into()))) - .saturating_add(Weight::from_parts(0, 2615).saturating_mul(m.into())) .saturating_add(Weight::from_parts(0, 2921).saturating_mul(a.into())) } /// Storage: Nfts CollectionConfigOf (r:1 w:0) @@ -181,7 +171,7 @@ impl WeightInfo for SubstrateWeight { /// Storage: Nfts Item (r:1 w:1) /// Proof: Nfts Item (max_values: None, max_size: Some(861), added: 3336, mode: MaxEncodedLen) /// Storage: Nfts Collection (r:1 w:1) - /// Proof: Nfts Collection (max_values: None, max_size: Some(80), added: 2555, mode: MaxEncodedLen) + /// Proof: Nfts Collection (max_values: None, max_size: Some(84), added: 2559, mode: MaxEncodedLen) /// Storage: Nfts CollectionRoleOf (r:1 w:0) /// Proof: Nfts CollectionRoleOf (max_values: None, max_size: Some(69), added: 2544, mode: MaxEncodedLen) /// Storage: Nfts ItemConfigOf (r:1 w:1) @@ -190,11 +180,10 @@ impl WeightInfo for SubstrateWeight { /// Proof: Nfts Account (max_values: None, max_size: Some(88), added: 2563, mode: MaxEncodedLen) fn mint() -> Weight { // Proof Size summary in bytes: - // Measured: `448` - // Estimated: `13506` - // Minimum execution time: 44_837 nanoseconds. - Weight::from_parts(46_794_000, 0) - .saturating_add(Weight::from_parts(0, 13506)) + // Measured: `453` + // Estimated: `18460` + // Minimum execution time: 49_434_000 picoseconds. + Weight::from_parts(50_248_000, 18460) .saturating_add(T::DbWeight::get().reads(5_u64)) .saturating_add(T::DbWeight::get().writes(4_u64)) } @@ -203,7 +192,7 @@ impl WeightInfo for SubstrateWeight { /// Storage: Nfts Item (r:1 w:1) /// Proof: Nfts Item (max_values: None, max_size: Some(861), added: 3336, mode: MaxEncodedLen) /// Storage: Nfts Collection (r:1 w:1) - /// Proof: Nfts Collection (max_values: None, max_size: Some(80), added: 2555, mode: MaxEncodedLen) + /// Proof: Nfts Collection (max_values: None, max_size: Some(84), added: 2559, mode: MaxEncodedLen) /// Storage: Nfts CollectionConfigOf (r:1 w:0) /// Proof: Nfts CollectionConfigOf (max_values: None, max_size: Some(73), added: 2548, mode: MaxEncodedLen) /// Storage: Nfts ItemConfigOf (r:1 w:1) @@ -212,22 +201,19 @@ impl WeightInfo for SubstrateWeight { /// Proof: Nfts Account (max_values: None, max_size: Some(88), added: 2563, mode: MaxEncodedLen) fn force_mint() -> Weight { // Proof Size summary in bytes: - // Measured: `448` - // Estimated: `13506` - // Minimum execution time: 43_976 nanoseconds. - Weight::from_parts(44_831_000, 0) - .saturating_add(Weight::from_parts(0, 13506)) + // Measured: `453` + // Estimated: `18460` + // Minimum execution time: 47_393_000 picoseconds. + Weight::from_parts(47_906_000, 18460) .saturating_add(T::DbWeight::get().reads(5_u64)) .saturating_add(T::DbWeight::get().writes(4_u64)) } /// Storage: Nfts ItemConfigOf (r:1 w:1) /// Proof: Nfts ItemConfigOf (max_values: None, max_size: Some(48), added: 2523, mode: MaxEncodedLen) /// Storage: Nfts Collection (r:1 w:1) - /// Proof: Nfts Collection (max_values: None, max_size: Some(80), added: 2555, mode: MaxEncodedLen) + /// Proof: Nfts Collection (max_values: None, max_size: Some(84), added: 2559, mode: MaxEncodedLen) /// Storage: Nfts Item (r:1 w:1) /// Proof: Nfts Item (max_values: None, max_size: Some(861), added: 3336, mode: MaxEncodedLen) - /// Storage: Nfts CollectionRoleOf (r:1 w:0) - /// Proof: Nfts CollectionRoleOf (max_values: None, max_size: Some(69), added: 2544, mode: MaxEncodedLen) /// Storage: Nfts ItemMetadataOf (r:1 w:0) /// Proof: Nfts ItemMetadataOf (max_values: None, max_size: Some(140), added: 2615, mode: MaxEncodedLen) /// Storage: Nfts Account (r:0 w:1) @@ -240,26 +226,21 @@ impl WeightInfo for SubstrateWeight { /// Proof: Nfts PendingSwapOf (max_values: None, max_size: Some(71), added: 2546, mode: MaxEncodedLen) fn burn() -> Weight { // Proof Size summary in bytes: - // Measured: `647` - // Estimated: `13573` - // Minimum execution time: 48_233 nanoseconds. - Weight::from_parts(50_113_000, 0) - .saturating_add(Weight::from_parts(0, 13573)) - .saturating_add(T::DbWeight::get().reads(5_u64)) + // Measured: `594` + // Estimated: `14993` + // Minimum execution time: 47_188_000 picoseconds. + Weight::from_parts(47_746_000, 14993) + .saturating_add(T::DbWeight::get().reads(4_u64)) .saturating_add(T::DbWeight::get().writes(7_u64)) } /// Storage: Nfts Collection (r:1 w:0) - /// Proof: Nfts Collection (max_values: None, max_size: Some(80), added: 2555, mode: MaxEncodedLen) + /// Proof: Nfts Collection (max_values: None, max_size: Some(84), added: 2559, mode: MaxEncodedLen) /// Storage: Nfts CollectionConfigOf (r:1 w:0) /// Proof: Nfts CollectionConfigOf (max_values: None, max_size: Some(73), added: 2548, mode: MaxEncodedLen) /// Storage: Nfts ItemConfigOf (r:1 w:0) /// Proof: Nfts ItemConfigOf (max_values: None, max_size: Some(48), added: 2523, mode: MaxEncodedLen) /// Storage: Nfts Item (r:1 w:1) /// Proof: Nfts Item (max_values: None, max_size: Some(861), added: 3336, mode: MaxEncodedLen) - /// Storage: Nfts CollectionRoleOf (r:1 w:0) - /// Proof: Nfts CollectionRoleOf (max_values: None, max_size: Some(69), added: 2544, mode: MaxEncodedLen) - /// Storage: System Account (r:1 w:1) - /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) /// Storage: Nfts Account (r:0 w:2) /// Proof: Nfts Account (max_values: None, max_size: Some(88), added: 2563, mode: MaxEncodedLen) /// Storage: Nfts ItemPriceOf (r:0 w:1) @@ -268,16 +249,15 @@ impl WeightInfo for SubstrateWeight { /// Proof: Nfts PendingSwapOf (max_values: None, max_size: Some(71), added: 2546, mode: MaxEncodedLen) fn transfer() -> Weight { // Proof Size summary in bytes: - // Measured: `882` - // Estimated: `16109` - // Minimum execution time: 55_452 nanoseconds. - Weight::from_parts(57_642_000, 0) - .saturating_add(Weight::from_parts(0, 16109)) - .saturating_add(T::DbWeight::get().reads(6_u64)) - .saturating_add(T::DbWeight::get().writes(6_u64)) + // Measured: `623` + // Estimated: `14926` + // Minimum execution time: 37_984_000 picoseconds. + Weight::from_parts(38_446_000, 14926) + .saturating_add(T::DbWeight::get().reads(4_u64)) + .saturating_add(T::DbWeight::get().writes(5_u64)) } /// Storage: Nfts Collection (r:1 w:0) - /// Proof: Nfts Collection (max_values: None, max_size: Some(80), added: 2555, mode: MaxEncodedLen) + /// Proof: Nfts Collection (max_values: None, max_size: Some(84), added: 2559, mode: MaxEncodedLen) /// Storage: Nfts CollectionConfigOf (r:1 w:0) /// Proof: Nfts CollectionConfigOf (max_values: None, max_size: Some(73), added: 2548, mode: MaxEncodedLen) /// Storage: Nfts Item (r:5000 w:5000) @@ -285,13 +265,12 @@ impl WeightInfo for SubstrateWeight { /// The range of component `i` is `[0, 5000]`. fn redeposit(i: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `756 + i * (140 ±0)` - // Estimated: `5103 + i * (3336 ±0)` - // Minimum execution time: 15_598 nanoseconds. - Weight::from_parts(15_926_000, 0) - .saturating_add(Weight::from_parts(0, 5103)) - // Standard Error: 13_692 - .saturating_add(Weight::from_parts(14_040_741, 0).saturating_mul(i.into())) + // Measured: `761 + i * (140 ±0)` + // Estimated: `8077 + i * (3336 ±0)` + // Minimum execution time: 17_297_000 picoseconds. + Weight::from_parts(17_634_000, 8077) + // Standard Error: 17_773 + .saturating_add(Weight::from_parts(14_395_819, 0).saturating_mul(i.into())) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().reads((1_u64).saturating_mul(i.into()))) .saturating_add(T::DbWeight::get().writes((1_u64).saturating_mul(i.into()))) @@ -304,10 +283,9 @@ impl WeightInfo for SubstrateWeight { fn lock_item_transfer() -> Weight { // Proof Size summary in bytes: // Measured: `401` - // Estimated: `5067` - // Minimum execution time: 19_686 nanoseconds. - Weight::from_parts(20_404_000, 0) - .saturating_add(Weight::from_parts(0, 5067)) + // Estimated: `7047` + // Minimum execution time: 21_827_000 picoseconds. + Weight::from_parts(22_134_000, 7047) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -318,101 +296,96 @@ impl WeightInfo for SubstrateWeight { fn unlock_item_transfer() -> Weight { // Proof Size summary in bytes: // Measured: `401` - // Estimated: `5067` - // Minimum execution time: 19_172 nanoseconds. - Weight::from_parts(20_151_000, 0) - .saturating_add(Weight::from_parts(0, 5067)) + // Estimated: `7047` + // Minimum execution time: 21_549_000 picoseconds. + Weight::from_parts(21_987_000, 7047) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } - /// Storage: Nfts CollectionRoleOf (r:1 w:0) - /// Proof: Nfts CollectionRoleOf (max_values: None, max_size: Some(69), added: 2544, mode: MaxEncodedLen) + /// Storage: Nfts Collection (r:1 w:0) + /// Proof: Nfts Collection (max_values: None, max_size: Some(84), added: 2559, mode: MaxEncodedLen) /// Storage: Nfts CollectionConfigOf (r:1 w:1) /// Proof: Nfts CollectionConfigOf (max_values: None, max_size: Some(73), added: 2548, mode: MaxEncodedLen) fn lock_collection() -> Weight { // Proof Size summary in bytes: - // Measured: `289` - // Estimated: `5092` - // Minimum execution time: 17_063 nanoseconds. - Weight::from_parts(17_482_000, 0) - .saturating_add(Weight::from_parts(0, 5092)) + // Measured: `338` + // Estimated: `7087` + // Minimum execution time: 18_930_000 picoseconds. + Weight::from_parts(19_200_000, 7087) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } /// Storage: Nfts OwnershipAcceptance (r:1 w:1) /// Proof: Nfts OwnershipAcceptance (max_values: None, max_size: Some(52), added: 2527, mode: MaxEncodedLen) /// Storage: Nfts Collection (r:1 w:1) - /// Proof: Nfts Collection (max_values: None, max_size: Some(80), added: 2555, mode: MaxEncodedLen) + /// Proof: Nfts Collection (max_values: None, max_size: Some(84), added: 2559, mode: MaxEncodedLen) /// Storage: Nfts CollectionAccount (r:0 w:2) /// Proof: Nfts CollectionAccount (max_values: None, max_size: Some(68), added: 2543, mode: MaxEncodedLen) fn transfer_ownership() -> Weight { // Proof Size summary in bytes: - // Measured: `381` - // Estimated: `5082` - // Minimum execution time: 21_974 nanoseconds. - Weight::from_parts(22_770_000, 0) - .saturating_add(Weight::from_parts(0, 5082)) + // Measured: `386` + // Estimated: `7066` + // Minimum execution time: 24_929_000 picoseconds. + Weight::from_parts(25_786_000, 7066) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().writes(4_u64)) } /// Storage: Nfts Collection (r:1 w:1) - /// Proof: Nfts Collection (max_values: None, max_size: Some(80), added: 2555, mode: MaxEncodedLen) - /// Storage: Nfts CollectionRoleOf (r:0 w:4) + /// Proof: Nfts Collection (max_values: None, max_size: Some(84), added: 2559, mode: MaxEncodedLen) + /// Storage: Nfts CollectionRoleOf (r:1 w:4) /// Proof: Nfts CollectionRoleOf (max_values: None, max_size: Some(69), added: 2544, mode: MaxEncodedLen) fn set_team() -> Weight { // Proof Size summary in bytes: - // Measured: `362` - // Estimated: `2555` - // Minimum execution time: 24_341 nanoseconds. - Weight::from_parts(25_059_000, 0) - .saturating_add(Weight::from_parts(0, 2555)) - .saturating_add(T::DbWeight::get().reads(1_u64)) + // Measured: `367` + // Estimated: `7083` + // Minimum execution time: 28_245_000 picoseconds. + Weight::from_parts(28_490_000, 7083) + .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().writes(5_u64)) } /// Storage: Nfts Collection (r:1 w:1) - /// Proof: Nfts Collection (max_values: None, max_size: Some(80), added: 2555, mode: MaxEncodedLen) + /// Proof: Nfts Collection (max_values: None, max_size: Some(84), added: 2559, mode: MaxEncodedLen) /// Storage: Nfts CollectionAccount (r:0 w:2) /// Proof: Nfts CollectionAccount (max_values: None, max_size: Some(68), added: 2543, mode: MaxEncodedLen) fn force_collection_owner() -> Weight { // Proof Size summary in bytes: - // Measured: `304` - // Estimated: `2555` - // Minimum execution time: 16_897 nanoseconds. - Weight::from_parts(17_560_000, 0) - .saturating_add(Weight::from_parts(0, 2555)) + // Measured: `309` + // Estimated: `3549` + // Minimum execution time: 19_971_000 picoseconds. + Weight::from_parts(20_276_000, 3549) .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) } /// Storage: Nfts Collection (r:1 w:0) - /// Proof: Nfts Collection (max_values: None, max_size: Some(80), added: 2555, mode: MaxEncodedLen) + /// Proof: Nfts Collection (max_values: None, max_size: Some(84), added: 2559, mode: MaxEncodedLen) /// Storage: Nfts CollectionConfigOf (r:0 w:1) /// Proof: Nfts CollectionConfigOf (max_values: None, max_size: Some(73), added: 2548, mode: MaxEncodedLen) fn force_collection_config() -> Weight { // Proof Size summary in bytes: // Measured: `242` - // Estimated: `2555` - // Minimum execution time: 13_239 nanoseconds. - Weight::from_parts(13_963_000, 0) - .saturating_add(Weight::from_parts(0, 2555)) + // Estimated: `3549` + // Minimum execution time: 15_924_000 picoseconds. + Weight::from_parts(16_258_000, 3549) .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } - /// Storage: Nfts Collection (r:1 w:0) - /// Proof: Nfts Collection (max_values: None, max_size: Some(80), added: 2555, mode: MaxEncodedLen) + /// Storage: Nfts CollectionRoleOf (r:1 w:0) + /// Proof: Nfts CollectionRoleOf (max_values: None, max_size: Some(69), added: 2544, mode: MaxEncodedLen) /// Storage: Nfts ItemConfigOf (r:1 w:1) /// Proof: Nfts ItemConfigOf (max_values: None, max_size: Some(48), added: 2523, mode: MaxEncodedLen) fn lock_item_properties() -> Weight { // Proof Size summary in bytes: - // Measured: `445` - // Estimated: `5078` - // Minimum execution time: 17_187 nanoseconds. - Weight::from_parts(17_942_000, 0) - .saturating_add(Weight::from_parts(0, 5078)) + // Measured: `401` + // Estimated: `7047` + // Minimum execution time: 20_780_000 picoseconds. + Weight::from_parts(21_109_000, 7047) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } /// Storage: Nfts Collection (r:1 w:1) - /// Proof: Nfts Collection (max_values: None, max_size: Some(80), added: 2555, mode: MaxEncodedLen) + /// Proof: Nfts Collection (max_values: None, max_size: Some(84), added: 2559, mode: MaxEncodedLen) + /// Storage: Nfts CollectionRoleOf (r:1 w:0) + /// Proof: Nfts CollectionRoleOf (max_values: None, max_size: Some(69), added: 2544, mode: MaxEncodedLen) /// Storage: Nfts CollectionConfigOf (r:1 w:0) /// Proof: Nfts CollectionConfigOf (max_values: None, max_size: Some(73), added: 2548, mode: MaxEncodedLen) /// Storage: Nfts ItemConfigOf (r:1 w:0) @@ -421,42 +394,41 @@ impl WeightInfo for SubstrateWeight { /// Proof: Nfts Attribute (max_values: None, max_size: Some(446), added: 2921, mode: MaxEncodedLen) fn set_attribute() -> Weight { // Proof Size summary in bytes: - // Measured: `474` - // Estimated: `10547` - // Minimum execution time: 40_925 nanoseconds. - Weight::from_parts(42_733_000, 0) - .saturating_add(Weight::from_parts(0, 10547)) - .saturating_add(T::DbWeight::get().reads(4_u64)) + // Measured: `537` + // Estimated: `18045` + // Minimum execution time: 50_817_000 picoseconds. + Weight::from_parts(51_585_000, 18045) + .saturating_add(T::DbWeight::get().reads(5_u64)) .saturating_add(T::DbWeight::get().writes(2_u64)) } /// Storage: Nfts Collection (r:1 w:1) - /// Proof: Nfts Collection (max_values: None, max_size: Some(80), added: 2555, mode: MaxEncodedLen) + /// Proof: Nfts Collection (max_values: None, max_size: Some(84), added: 2559, mode: MaxEncodedLen) /// Storage: Nfts Attribute (r:1 w:1) /// Proof: Nfts Attribute (max_values: None, max_size: Some(446), added: 2921, mode: MaxEncodedLen) fn force_set_attribute() -> Weight { // Proof Size summary in bytes: - // Measured: `337` - // Estimated: `5476` - // Minimum execution time: 24_486 nanoseconds. - Weight::from_parts(25_409_000, 0) - .saturating_add(Weight::from_parts(0, 5476)) + // Measured: `342` + // Estimated: `7460` + // Minimum execution time: 28_821_000 picoseconds. + Weight::from_parts(29_417_000, 7460) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().writes(2_u64)) } /// Storage: Nfts Attribute (r:1 w:1) /// Proof: Nfts Attribute (max_values: None, max_size: Some(446), added: 2921, mode: MaxEncodedLen) - /// Storage: Nfts Collection (r:1 w:1) - /// Proof: Nfts Collection (max_values: None, max_size: Some(80), added: 2555, mode: MaxEncodedLen) + /// Storage: Nfts CollectionRoleOf (r:1 w:0) + /// Proof: Nfts CollectionRoleOf (max_values: None, max_size: Some(69), added: 2544, mode: MaxEncodedLen) /// Storage: Nfts ItemConfigOf (r:1 w:0) /// Proof: Nfts ItemConfigOf (max_values: None, max_size: Some(48), added: 2523, mode: MaxEncodedLen) + /// Storage: Nfts Collection (r:1 w:1) + /// Proof: Nfts Collection (max_values: None, max_size: Some(84), added: 2559, mode: MaxEncodedLen) fn clear_attribute() -> Weight { // Proof Size summary in bytes: - // Measured: `916` - // Estimated: `7999` - // Minimum execution time: 36_643 nanoseconds. - Weight::from_parts(37_805_000, 0) - .saturating_add(Weight::from_parts(0, 7999)) - .saturating_add(T::DbWeight::get().reads(3_u64)) + // Measured: `979` + // Estimated: `14507` + // Minimum execution time: 47_021_000 picoseconds. + Weight::from_parts(47_509_000, 14507) + .saturating_add(T::DbWeight::get().reads(4_u64)) .saturating_add(T::DbWeight::get().writes(2_u64)) } /// Storage: Nfts Item (r:1 w:0) @@ -466,10 +438,9 @@ impl WeightInfo for SubstrateWeight { fn approve_item_attributes() -> Weight { // Proof Size summary in bytes: // Measured: `379` - // Estimated: `6492` - // Minimum execution time: 16_798 nanoseconds. - Weight::from_parts(17_326_000, 0) - .saturating_add(Weight::from_parts(0, 6492)) + // Estimated: `8472` + // Minimum execution time: 19_457_000 picoseconds. + Weight::from_parts(19_738_000, 8472) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -485,20 +456,21 @@ impl WeightInfo for SubstrateWeight { fn cancel_item_attributes_approval(n: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `899 + n * (396 ±0)` - // Estimated: `12016 + n * (2921 ±0)` - // Minimum execution time: 25_524 nanoseconds. - Weight::from_parts(26_107_000, 0) - .saturating_add(Weight::from_parts(0, 12016)) - // Standard Error: 5_460 - .saturating_add(Weight::from_parts(9_030_830, 0).saturating_mul(n.into())) + // Estimated: `15976 + n * (2921 ±0)` + // Minimum execution time: 29_027_000 picoseconds. + Weight::from_parts(29_376_000, 15976) + // Standard Error: 3_500 + .saturating_add(Weight::from_parts(5_568_116, 0).saturating_mul(n.into())) .saturating_add(T::DbWeight::get().reads(4_u64)) .saturating_add(T::DbWeight::get().reads((1_u64).saturating_mul(n.into()))) .saturating_add(T::DbWeight::get().writes(2_u64)) .saturating_add(T::DbWeight::get().writes((1_u64).saturating_mul(n.into()))) .saturating_add(Weight::from_parts(0, 2921).saturating_mul(n.into())) } + /// Storage: Nfts CollectionRoleOf (r:1 w:0) + /// Proof: Nfts CollectionRoleOf (max_values: None, max_size: Some(69), added: 2544, mode: MaxEncodedLen) /// Storage: Nfts Collection (r:1 w:1) - /// Proof: Nfts Collection (max_values: None, max_size: Some(80), added: 2555, mode: MaxEncodedLen) + /// Proof: Nfts Collection (max_values: None, max_size: Some(84), added: 2559, mode: MaxEncodedLen) /// Storage: Nfts ItemConfigOf (r:1 w:0) /// Proof: Nfts ItemConfigOf (max_values: None, max_size: Some(48), added: 2523, mode: MaxEncodedLen) /// Storage: Nfts CollectionConfigOf (r:1 w:0) @@ -507,104 +479,97 @@ impl WeightInfo for SubstrateWeight { /// Proof: Nfts ItemMetadataOf (max_values: None, max_size: Some(140), added: 2615, mode: MaxEncodedLen) fn set_metadata() -> Weight { // Proof Size summary in bytes: - // Measured: `474` - // Estimated: `10241` - // Minimum execution time: 34_400 nanoseconds. - Weight::from_parts(35_469_000, 0) - .saturating_add(Weight::from_parts(0, 10241)) - .saturating_add(T::DbWeight::get().reads(4_u64)) + // Measured: `537` + // Estimated: `17739` + // Minimum execution time: 41_658_000 picoseconds. + Weight::from_parts(42_241_000, 17739) + .saturating_add(T::DbWeight::get().reads(5_u64)) .saturating_add(T::DbWeight::get().writes(2_u64)) } + /// Storage: Nfts CollectionRoleOf (r:1 w:0) + /// Proof: Nfts CollectionRoleOf (max_values: None, max_size: Some(69), added: 2544, mode: MaxEncodedLen) /// Storage: Nfts ItemMetadataOf (r:1 w:1) /// Proof: Nfts ItemMetadataOf (max_values: None, max_size: Some(140), added: 2615, mode: MaxEncodedLen) /// Storage: Nfts Collection (r:1 w:1) - /// Proof: Nfts Collection (max_values: None, max_size: Some(80), added: 2555, mode: MaxEncodedLen) + /// Proof: Nfts Collection (max_values: None, max_size: Some(84), added: 2559, mode: MaxEncodedLen) /// Storage: Nfts ItemConfigOf (r:1 w:0) /// Proof: Nfts ItemConfigOf (max_values: None, max_size: Some(48), added: 2523, mode: MaxEncodedLen) fn clear_metadata() -> Weight { // Proof Size summary in bytes: - // Measured: `609` - // Estimated: `7693` - // Minimum execution time: 31_560 nanoseconds. - Weight::from_parts(33_081_000, 0) - .saturating_add(Weight::from_parts(0, 7693)) - .saturating_add(T::DbWeight::get().reads(3_u64)) + // Measured: `672` + // Estimated: `14201` + // Minimum execution time: 39_838_000 picoseconds. + Weight::from_parts(40_757_000, 14201) + .saturating_add(T::DbWeight::get().reads(4_u64)) .saturating_add(T::DbWeight::get().writes(2_u64)) } + /// Storage: Nfts CollectionRoleOf (r:1 w:0) + /// Proof: Nfts CollectionRoleOf (max_values: None, max_size: Some(69), added: 2544, mode: MaxEncodedLen) /// Storage: Nfts CollectionConfigOf (r:1 w:0) /// Proof: Nfts CollectionConfigOf (max_values: None, max_size: Some(73), added: 2548, mode: MaxEncodedLen) /// Storage: Nfts Collection (r:1 w:1) - /// Proof: Nfts Collection (max_values: None, max_size: Some(80), added: 2555, mode: MaxEncodedLen) + /// Proof: Nfts Collection (max_values: None, max_size: Some(84), added: 2559, mode: MaxEncodedLen) /// Storage: Nfts CollectionMetadataOf (r:1 w:1) /// Proof: Nfts CollectionMetadataOf (max_values: None, max_size: Some(87), added: 2562, mode: MaxEncodedLen) fn set_collection_metadata() -> Weight { // Proof Size summary in bytes: - // Measured: `333` - // Estimated: `7665` - // Minimum execution time: 28_821 nanoseconds. - Weight::from_parts(30_010_000, 0) - .saturating_add(Weight::from_parts(0, 7665)) - .saturating_add(T::DbWeight::get().reads(3_u64)) + // Measured: `396` + // Estimated: `14173` + // Minimum execution time: 37_327_000 picoseconds. + Weight::from_parts(37_874_000, 14173) + .saturating_add(T::DbWeight::get().reads(4_u64)) .saturating_add(T::DbWeight::get().writes(2_u64)) } + /// Storage: Nfts CollectionRoleOf (r:1 w:0) + /// Proof: Nfts CollectionRoleOf (max_values: None, max_size: Some(69), added: 2544, mode: MaxEncodedLen) /// Storage: Nfts Collection (r:1 w:0) - /// Proof: Nfts Collection (max_values: None, max_size: Some(80), added: 2555, mode: MaxEncodedLen) + /// Proof: Nfts Collection (max_values: None, max_size: Some(84), added: 2559, mode: MaxEncodedLen) /// Storage: Nfts CollectionConfigOf (r:1 w:0) /// Proof: Nfts CollectionConfigOf (max_values: None, max_size: Some(73), added: 2548, mode: MaxEncodedLen) /// Storage: Nfts CollectionMetadataOf (r:1 w:1) /// Proof: Nfts CollectionMetadataOf (max_values: None, max_size: Some(87), added: 2562, mode: MaxEncodedLen) fn clear_collection_metadata() -> Weight { // Proof Size summary in bytes: - // Measured: `476` - // Estimated: `7665` - // Minimum execution time: 27_608 nanoseconds. - Weight::from_parts(28_766_000, 0) - .saturating_add(Weight::from_parts(0, 7665)) - .saturating_add(T::DbWeight::get().reads(3_u64)) + // Measured: `539` + // Estimated: `14173` + // Minimum execution time: 35_305_000 picoseconds. + Weight::from_parts(36_213_000, 14173) + .saturating_add(T::DbWeight::get().reads(4_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } /// Storage: Nfts Item (r:1 w:1) /// Proof: Nfts Item (max_values: None, max_size: Some(861), added: 3336, mode: MaxEncodedLen) /// Storage: Nfts CollectionConfigOf (r:1 w:0) /// Proof: Nfts CollectionConfigOf (max_values: None, max_size: Some(73), added: 2548, mode: MaxEncodedLen) - /// Storage: Nfts CollectionRoleOf (r:1 w:0) - /// Proof: Nfts CollectionRoleOf (max_values: None, max_size: Some(69), added: 2544, mode: MaxEncodedLen) fn approve_transfer() -> Weight { // Proof Size summary in bytes: - // Measured: `466` - // Estimated: `8428` - // Minimum execution time: 23_987 nanoseconds. - Weight::from_parts(24_819_000, 0) - .saturating_add(Weight::from_parts(0, 8428)) - .saturating_add(T::DbWeight::get().reads(3_u64)) + // Measured: `408` + // Estimated: `7864` + // Minimum execution time: 22_680_000 picoseconds. + Weight::from_parts(23_126_000, 7864) + .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } /// Storage: Nfts Item (r:1 w:1) /// Proof: Nfts Item (max_values: None, max_size: Some(861), added: 3336, mode: MaxEncodedLen) - /// Storage: Nfts CollectionRoleOf (r:1 w:0) - /// Proof: Nfts CollectionRoleOf (max_values: None, max_size: Some(69), added: 2544, mode: MaxEncodedLen) fn cancel_approval() -> Weight { // Proof Size summary in bytes: - // Measured: `474` - // Estimated: `5880` - // Minimum execution time: 21_254 nanoseconds. - Weight::from_parts(21_826_000, 0) - .saturating_add(Weight::from_parts(0, 5880)) - .saturating_add(T::DbWeight::get().reads(2_u64)) + // Measured: `416` + // Estimated: `4326` + // Minimum execution time: 19_837_000 picoseconds. + Weight::from_parts(20_352_000, 4326) + .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } /// Storage: Nfts Item (r:1 w:1) /// Proof: Nfts Item (max_values: None, max_size: Some(861), added: 3336, mode: MaxEncodedLen) - /// Storage: Nfts CollectionRoleOf (r:1 w:0) - /// Proof: Nfts CollectionRoleOf (max_values: None, max_size: Some(69), added: 2544, mode: MaxEncodedLen) fn clear_all_transfer_approvals() -> Weight { // Proof Size summary in bytes: - // Measured: `474` - // Estimated: `5880` - // Minimum execution time: 20_272 nanoseconds. - Weight::from_parts(20_922_000, 0) - .saturating_add(Weight::from_parts(0, 5880)) - .saturating_add(T::DbWeight::get().reads(2_u64)) + // Measured: `416` + // Estimated: `4326` + // Minimum execution time: 18_911_000 picoseconds. + Weight::from_parts(19_233_000, 4326) + .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } /// Storage: Nfts OwnershipAcceptance (r:1 w:1) @@ -612,38 +577,35 @@ impl WeightInfo for SubstrateWeight { fn set_accept_ownership() -> Weight { // Proof Size summary in bytes: // Measured: `42` - // Estimated: `2527` - // Minimum execution time: 14_287 nanoseconds. - Weight::from_parts(14_960_000, 0) - .saturating_add(Weight::from_parts(0, 2527)) + // Estimated: `3517` + // Minimum execution time: 17_292_000 picoseconds. + Weight::from_parts(17_568_000, 3517) .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } /// Storage: Nfts CollectionConfigOf (r:1 w:1) /// Proof: Nfts CollectionConfigOf (max_values: None, max_size: Some(73), added: 2548, mode: MaxEncodedLen) /// Storage: Nfts Collection (r:1 w:0) - /// Proof: Nfts Collection (max_values: None, max_size: Some(80), added: 2555, mode: MaxEncodedLen) + /// Proof: Nfts Collection (max_values: None, max_size: Some(84), added: 2559, mode: MaxEncodedLen) fn set_collection_max_supply() -> Weight { // Proof Size summary in bytes: - // Measured: `333` - // Estimated: `5103` - // Minimum execution time: 17_948 nanoseconds. - Weight::from_parts(18_780_000, 0) - .saturating_add(Weight::from_parts(0, 5103)) + // Measured: `338` + // Estimated: `7087` + // Minimum execution time: 20_323_000 picoseconds. + Weight::from_parts(20_692_000, 7087) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } - /// Storage: Nfts Collection (r:1 w:0) - /// Proof: Nfts Collection (max_values: None, max_size: Some(80), added: 2555, mode: MaxEncodedLen) + /// Storage: Nfts CollectionRoleOf (r:1 w:0) + /// Proof: Nfts CollectionRoleOf (max_values: None, max_size: Some(69), added: 2544, mode: MaxEncodedLen) /// Storage: Nfts CollectionConfigOf (r:1 w:1) /// Proof: Nfts CollectionConfigOf (max_values: None, max_size: Some(73), added: 2548, mode: MaxEncodedLen) fn update_mint_settings() -> Weight { // Proof Size summary in bytes: - // Measured: `333` - // Estimated: `5103` - // Minimum execution time: 16_616 nanoseconds. - Weight::from_parts(17_155_000, 0) - .saturating_add(Weight::from_parts(0, 5103)) + // Measured: `289` + // Estimated: `7072` + // Minimum execution time: 19_971_000 picoseconds. + Weight::from_parts(20_159_000, 7072) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -658,10 +620,9 @@ impl WeightInfo for SubstrateWeight { fn set_price() -> Weight { // Proof Size summary in bytes: // Measured: `516` - // Estimated: `8407` - // Minimum execution time: 22_777 nanoseconds. - Weight::from_parts(23_955_000, 0) - .saturating_add(Weight::from_parts(0, 8407)) + // Estimated: `11377` + // Minimum execution time: 26_202_000 picoseconds. + Weight::from_parts(26_499_000, 11377) .saturating_add(T::DbWeight::get().reads(3_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -670,37 +631,33 @@ impl WeightInfo for SubstrateWeight { /// Storage: Nfts ItemPriceOf (r:1 w:1) /// Proof: Nfts ItemPriceOf (max_values: None, max_size: Some(89), added: 2564, mode: MaxEncodedLen) /// Storage: Nfts Collection (r:1 w:0) - /// Proof: Nfts Collection (max_values: None, max_size: Some(80), added: 2555, mode: MaxEncodedLen) + /// Proof: Nfts Collection (max_values: None, max_size: Some(84), added: 2559, mode: MaxEncodedLen) /// Storage: Nfts CollectionConfigOf (r:1 w:0) /// Proof: Nfts CollectionConfigOf (max_values: None, max_size: Some(73), added: 2548, mode: MaxEncodedLen) /// Storage: Nfts ItemConfigOf (r:1 w:0) /// Proof: Nfts ItemConfigOf (max_values: None, max_size: Some(48), added: 2523, mode: MaxEncodedLen) - /// Storage: System Account (r:1 w:1) - /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) /// Storage: Nfts Account (r:0 w:2) /// Proof: Nfts Account (max_values: None, max_size: Some(88), added: 2563, mode: MaxEncodedLen) /// Storage: Nfts PendingSwapOf (r:0 w:1) /// Proof: Nfts PendingSwapOf (max_values: None, max_size: Some(71), added: 2546, mode: MaxEncodedLen) fn buy_item() -> Weight { // Proof Size summary in bytes: - // Measured: `934` - // Estimated: `16129` - // Minimum execution time: 61_131 nanoseconds. - Weight::from_parts(62_791_000, 0) - .saturating_add(Weight::from_parts(0, 16129)) - .saturating_add(T::DbWeight::get().reads(6_u64)) - .saturating_add(T::DbWeight::get().writes(6_u64)) + // Measured: `767` + // Estimated: `18480` + // Minimum execution time: 50_601_000 picoseconds. + Weight::from_parts(51_283_000, 18480) + .saturating_add(T::DbWeight::get().reads(5_u64)) + .saturating_add(T::DbWeight::get().writes(5_u64)) } /// The range of component `n` is `[0, 10]`. fn pay_tips(n: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_952 nanoseconds. - Weight::from_parts(3_975_700, 0) - .saturating_add(Weight::from_parts(0, 0)) - // Standard Error: 11_254 - .saturating_add(Weight::from_parts(3_501_698, 0).saturating_mul(n.into())) + // Minimum execution time: 2_733_000 picoseconds. + Weight::from_parts(5_568_590, 0) + // Standard Error: 14_136 + .saturating_add(Weight::from_parts(3_763_928, 0).saturating_mul(n.into())) } /// Storage: Nfts Item (r:2 w:0) /// Proof: Nfts Item (max_values: None, max_size: Some(861), added: 3336, mode: MaxEncodedLen) @@ -709,10 +666,9 @@ impl WeightInfo for SubstrateWeight { fn create_swap() -> Weight { // Proof Size summary in bytes: // Measured: `524` - // Estimated: `6672` - // Minimum execution time: 20_327 nanoseconds. - Weight::from_parts(21_714_000, 0) - .saturating_add(Weight::from_parts(0, 6672)) + // Estimated: `7662` + // Minimum execution time: 22_901_000 picoseconds. + Weight::from_parts(23_673_000, 7662) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -723,10 +679,9 @@ impl WeightInfo for SubstrateWeight { fn cancel_swap() -> Weight { // Proof Size summary in bytes: // Measured: `511` - // Estimated: `5882` - // Minimum execution time: 20_668 nanoseconds. - Weight::from_parts(21_416_000, 0) - .saturating_add(Weight::from_parts(0, 5882)) + // Estimated: `7862` + // Minimum execution time: 22_532_000 picoseconds. + Weight::from_parts(22_831_000, 7862) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -735,29 +690,28 @@ impl WeightInfo for SubstrateWeight { /// Storage: Nfts PendingSwapOf (r:1 w:2) /// Proof: Nfts PendingSwapOf (max_values: None, max_size: Some(71), added: 2546, mode: MaxEncodedLen) /// Storage: Nfts Collection (r:1 w:0) - /// Proof: Nfts Collection (max_values: None, max_size: Some(80), added: 2555, mode: MaxEncodedLen) + /// Proof: Nfts Collection (max_values: None, max_size: Some(84), added: 2559, mode: MaxEncodedLen) /// Storage: Nfts CollectionConfigOf (r:1 w:0) /// Proof: Nfts CollectionConfigOf (max_values: None, max_size: Some(73), added: 2548, mode: MaxEncodedLen) /// Storage: Nfts ItemConfigOf (r:2 w:0) /// Proof: Nfts ItemConfigOf (max_values: None, max_size: Some(48), added: 2523, mode: MaxEncodedLen) - /// Storage: System Account (r:1 w:1) - /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) /// Storage: Nfts Account (r:0 w:4) /// Proof: Nfts Account (max_values: None, max_size: Some(88), added: 2563, mode: MaxEncodedLen) /// Storage: Nfts ItemPriceOf (r:0 w:2) /// Proof: Nfts ItemPriceOf (max_values: None, max_size: Some(89), added: 2564, mode: MaxEncodedLen) fn claim_swap() -> Weight { // Proof Size summary in bytes: - // Measured: `1097` - // Estimated: `21970` - // Minimum execution time: 88_006 nanoseconds. - Weight::from_parts(90_390_000, 0) - .saturating_add(Weight::from_parts(0, 21970)) - .saturating_add(T::DbWeight::get().reads(8_u64)) - .saturating_add(T::DbWeight::get().writes(11_u64)) + // Measured: `896` + // Estimated: `24321` + // Minimum execution time: 77_668_000 picoseconds. + Weight::from_parts(78_407_000, 24321) + .saturating_add(T::DbWeight::get().reads(7_u64)) + .saturating_add(T::DbWeight::get().writes(10_u64)) } /// Storage: Nfts Collection (r:1 w:1) - /// Proof: Nfts Collection (max_values: None, max_size: Some(80), added: 2555, mode: MaxEncodedLen) + /// Proof: Nfts Collection (max_values: None, max_size: Some(84), added: 2559, mode: MaxEncodedLen) + /// Storage: Nfts CollectionRoleOf (r:2 w:0) + /// Proof: Nfts CollectionRoleOf (max_values: None, max_size: Some(69), added: 2544, mode: MaxEncodedLen) /// Storage: Nfts CollectionConfigOf (r:1 w:0) /// Proof: Nfts CollectionConfigOf (max_values: None, max_size: Some(73), added: 2548, mode: MaxEncodedLen) /// Storage: Nfts Item (r:1 w:1) @@ -775,14 +729,13 @@ impl WeightInfo for SubstrateWeight { /// The range of component `n` is `[0, 10]`. fn mint_pre_signed(n: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `596` - // Estimated: `16180 + n * (2921 ±0)` - // Minimum execution time: 124_967 nanoseconds. - Weight::from_parts(131_602_642, 0) - .saturating_add(Weight::from_parts(0, 16180)) - // Standard Error: 36_480 - .saturating_add(Weight::from_parts(25_811_394, 0).saturating_mul(n.into())) - .saturating_add(T::DbWeight::get().reads(6_u64)) + // Measured: `659` + // Estimated: `29192 + n * (2921 ±0)` + // Minimum execution time: 133_147_000 picoseconds. + Weight::from_parts(138_031_439, 29192) + // Standard Error: 28_665 + .saturating_add(Weight::from_parts(28_206_062, 0).saturating_mul(n.into())) + .saturating_add(T::DbWeight::get().reads(8_u64)) .saturating_add(T::DbWeight::get().reads((1_u64).saturating_mul(n.into()))) .saturating_add(T::DbWeight::get().writes(6_u64)) .saturating_add(T::DbWeight::get().writes((1_u64).saturating_mul(n.into()))) @@ -792,10 +745,10 @@ impl WeightInfo for SubstrateWeight { /// Proof: Nfts Item (max_values: None, max_size: Some(861), added: 3336, mode: MaxEncodedLen) /// Storage: Nfts ItemAttributesApprovalsOf (r:1 w:1) /// Proof: Nfts ItemAttributesApprovalsOf (max_values: None, max_size: Some(681), added: 3156, mode: MaxEncodedLen) - /// Storage: Nfts Collection (r:1 w:1) - /// Proof: Nfts Collection (max_values: None, max_size: Some(80), added: 2555, mode: MaxEncodedLen) /// Storage: Nfts CollectionConfigOf (r:1 w:0) /// Proof: Nfts CollectionConfigOf (max_values: None, max_size: Some(73), added: 2548, mode: MaxEncodedLen) + /// Storage: Nfts Collection (r:1 w:1) + /// Proof: Nfts Collection (max_values: None, max_size: Some(84), added: 2559, mode: MaxEncodedLen) /// Storage: Nfts Attribute (r:10 w:10) /// Proof: Nfts Attribute (max_values: None, max_size: Some(446), added: 2921, mode: MaxEncodedLen) /// Storage: System Account (r:1 w:1) @@ -803,13 +756,12 @@ impl WeightInfo for SubstrateWeight { /// The range of component `n` is `[0, 10]`. fn set_attributes_pre_signed(n: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `716` - // Estimated: `14198 + n * (2921 ±0)` - // Minimum execution time: 84_153 nanoseconds. - Weight::from_parts(96_401_623, 0) - .saturating_add(Weight::from_parts(0, 14198)) - // Standard Error: 70_244 - .saturating_add(Weight::from_parts(26_866_222, 0).saturating_mul(n.into())) + // Measured: `721` + // Estimated: `20142 + n * (2921 ±0)` + // Minimum execution time: 78_243_000 picoseconds. + Weight::from_parts(90_947_408, 20142) + // Standard Error: 75_516 + .saturating_add(Weight::from_parts(28_059_623, 0).saturating_mul(n.into())) .saturating_add(T::DbWeight::get().reads(4_u64)) .saturating_add(T::DbWeight::get().reads((1_u64).saturating_mul(n.into()))) .saturating_add(T::DbWeight::get().writes(2_u64)) @@ -823,7 +775,7 @@ impl WeightInfo for () { /// Storage: Nfts NextCollectionId (r:1 w:1) /// Proof: Nfts NextCollectionId (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) /// Storage: Nfts Collection (r:1 w:1) - /// Proof: Nfts Collection (max_values: None, max_size: Some(80), added: 2555, mode: MaxEncodedLen) + /// Proof: Nfts Collection (max_values: None, max_size: Some(84), added: 2559, mode: MaxEncodedLen) /// Storage: Nfts CollectionRoleOf (r:0 w:1) /// Proof: Nfts CollectionRoleOf (max_values: None, max_size: Some(69), added: 2544, mode: MaxEncodedLen) /// Storage: Nfts CollectionConfigOf (r:0 w:1) @@ -833,17 +785,16 @@ impl WeightInfo for () { fn create() -> Weight { // Proof Size summary in bytes: // Measured: `214` - // Estimated: `3054` - // Minimum execution time: 33_769 nanoseconds. - Weight::from_parts(36_031_000, 0) - .saturating_add(Weight::from_parts(0, 3054)) + // Estimated: `5038` + // Minimum execution time: 37_598_000 picoseconds. + Weight::from_parts(38_703_000, 5038) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().writes(5_u64)) } /// Storage: Nfts NextCollectionId (r:1 w:1) /// Proof: Nfts NextCollectionId (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) /// Storage: Nfts Collection (r:1 w:1) - /// Proof: Nfts Collection (max_values: None, max_size: Some(80), added: 2555, mode: MaxEncodedLen) + /// Proof: Nfts Collection (max_values: None, max_size: Some(84), added: 2559, mode: MaxEncodedLen) /// Storage: Nfts CollectionRoleOf (r:0 w:1) /// Proof: Nfts CollectionRoleOf (max_values: None, max_size: Some(69), added: 2544, mode: MaxEncodedLen) /// Storage: Nfts CollectionConfigOf (r:0 w:1) @@ -853,54 +804,45 @@ impl WeightInfo for () { fn force_create() -> Weight { // Proof Size summary in bytes: // Measured: `42` - // Estimated: `3054` - // Minimum execution time: 21_767 nanoseconds. - Weight::from_parts(22_565_000, 0) - .saturating_add(Weight::from_parts(0, 3054)) + // Estimated: `5038` + // Minimum execution time: 25_899_000 picoseconds. + Weight::from_parts(26_385_000, 5038) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().writes(5_u64)) } /// Storage: Nfts Collection (r:1 w:1) - /// Proof: Nfts Collection (max_values: None, max_size: Some(80), added: 2555, mode: MaxEncodedLen) - /// Storage: Nfts Item (r:1001 w:1000) - /// Proof: Nfts Item (max_values: None, max_size: Some(861), added: 3336, mode: MaxEncodedLen) - /// Storage: Nfts ItemMetadataOf (r:1001 w:1000) + /// Proof: Nfts Collection (max_values: None, max_size: Some(84), added: 2559, mode: MaxEncodedLen) + /// Storage: Nfts ItemMetadataOf (r:1 w:0) /// Proof: Nfts ItemMetadataOf (max_values: None, max_size: Some(140), added: 2615, mode: MaxEncodedLen) + /// Storage: Nfts CollectionRoleOf (r:1 w:1) + /// Proof: Nfts CollectionRoleOf (max_values: None, max_size: Some(69), added: 2544, mode: MaxEncodedLen) /// Storage: Nfts Attribute (r:1001 w:1000) /// Proof: Nfts Attribute (max_values: None, max_size: Some(446), added: 2921, mode: MaxEncodedLen) - /// Storage: Nfts CollectionRoleOf (r:0 w:1) - /// Proof: Nfts CollectionRoleOf (max_values: None, max_size: Some(69), added: 2544, mode: MaxEncodedLen) + /// Storage: Nfts ItemConfigOf (r:1000 w:1000) + /// Proof: Nfts ItemConfigOf (max_values: None, max_size: Some(48), added: 2523, mode: MaxEncodedLen) /// Storage: Nfts CollectionMetadataOf (r:0 w:1) /// Proof: Nfts CollectionMetadataOf (max_values: None, max_size: Some(87), added: 2562, mode: MaxEncodedLen) /// Storage: Nfts CollectionConfigOf (r:0 w:1) /// Proof: Nfts CollectionConfigOf (max_values: None, max_size: Some(73), added: 2548, mode: MaxEncodedLen) - /// Storage: Nfts ItemConfigOf (r:0 w:1000) - /// Proof: Nfts ItemConfigOf (max_values: None, max_size: Some(48), added: 2523, mode: MaxEncodedLen) - /// Storage: Nfts Account (r:0 w:1000) - /// Proof: Nfts Account (max_values: None, max_size: Some(88), added: 2563, mode: MaxEncodedLen) /// Storage: Nfts CollectionAccount (r:0 w:1) /// Proof: Nfts CollectionAccount (max_values: None, max_size: Some(68), added: 2543, mode: MaxEncodedLen) - /// The range of component `n` is `[0, 1000]`. /// The range of component `m` is `[0, 1000]`. + /// The range of component `c` is `[0, 1000]`. /// The range of component `a` is `[0, 1000]`. - fn destroy(_n: u32, m: u32, a: u32, ) -> Weight { - // Proof Size summary in bytes: - // Measured: `172781 + m * (127 ±0) + a * (402 ±0)` - // Estimated: `3347427 + m * (2615 ±0) + a * (2921 ±0)` - // Minimum execution time: 26_973_627 nanoseconds. - Weight::from_parts(19_692_361_714, 0) - .saturating_add(Weight::from_parts(0, 3347427)) - // Standard Error: 17_036 - .saturating_add(Weight::from_parts(7_797_219, 0).saturating_mul(m.into())) - // Standard Error: 17_036 - .saturating_add(Weight::from_parts(9_504_128, 0).saturating_mul(a.into())) + fn destroy(m: u32, _c: u32, a: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `32218 + a * (364 ±0)` + // Estimated: `2538589 + a * (2921 ±0)` + // Minimum execution time: 1_129_980_000 picoseconds. + Weight::from_parts(1_096_213_543, 2538589) + // Standard Error: 5_210 + .saturating_add(Weight::from_parts(92, 0).saturating_mul(m.into())) + // Standard Error: 5_210 + .saturating_add(Weight::from_parts(5_480_550, 0).saturating_mul(a.into())) .saturating_add(RocksDbWeight::get().reads(1004_u64)) - .saturating_add(RocksDbWeight::get().reads((1_u64).saturating_mul(m.into()))) .saturating_add(RocksDbWeight::get().reads((1_u64).saturating_mul(a.into()))) - .saturating_add(RocksDbWeight::get().writes(3005_u64)) - .saturating_add(RocksDbWeight::get().writes((1_u64).saturating_mul(m.into()))) + .saturating_add(RocksDbWeight::get().writes(1005_u64)) .saturating_add(RocksDbWeight::get().writes((1_u64).saturating_mul(a.into()))) - .saturating_add(Weight::from_parts(0, 2615).saturating_mul(m.into())) .saturating_add(Weight::from_parts(0, 2921).saturating_mul(a.into())) } /// Storage: Nfts CollectionConfigOf (r:1 w:0) @@ -908,7 +850,7 @@ impl WeightInfo for () { /// Storage: Nfts Item (r:1 w:1) /// Proof: Nfts Item (max_values: None, max_size: Some(861), added: 3336, mode: MaxEncodedLen) /// Storage: Nfts Collection (r:1 w:1) - /// Proof: Nfts Collection (max_values: None, max_size: Some(80), added: 2555, mode: MaxEncodedLen) + /// Proof: Nfts Collection (max_values: None, max_size: Some(84), added: 2559, mode: MaxEncodedLen) /// Storage: Nfts CollectionRoleOf (r:1 w:0) /// Proof: Nfts CollectionRoleOf (max_values: None, max_size: Some(69), added: 2544, mode: MaxEncodedLen) /// Storage: Nfts ItemConfigOf (r:1 w:1) @@ -917,11 +859,10 @@ impl WeightInfo for () { /// Proof: Nfts Account (max_values: None, max_size: Some(88), added: 2563, mode: MaxEncodedLen) fn mint() -> Weight { // Proof Size summary in bytes: - // Measured: `448` - // Estimated: `13506` - // Minimum execution time: 44_837 nanoseconds. - Weight::from_parts(46_794_000, 0) - .saturating_add(Weight::from_parts(0, 13506)) + // Measured: `453` + // Estimated: `18460` + // Minimum execution time: 49_434_000 picoseconds. + Weight::from_parts(50_248_000, 18460) .saturating_add(RocksDbWeight::get().reads(5_u64)) .saturating_add(RocksDbWeight::get().writes(4_u64)) } @@ -930,7 +871,7 @@ impl WeightInfo for () { /// Storage: Nfts Item (r:1 w:1) /// Proof: Nfts Item (max_values: None, max_size: Some(861), added: 3336, mode: MaxEncodedLen) /// Storage: Nfts Collection (r:1 w:1) - /// Proof: Nfts Collection (max_values: None, max_size: Some(80), added: 2555, mode: MaxEncodedLen) + /// Proof: Nfts Collection (max_values: None, max_size: Some(84), added: 2559, mode: MaxEncodedLen) /// Storage: Nfts CollectionConfigOf (r:1 w:0) /// Proof: Nfts CollectionConfigOf (max_values: None, max_size: Some(73), added: 2548, mode: MaxEncodedLen) /// Storage: Nfts ItemConfigOf (r:1 w:1) @@ -939,22 +880,19 @@ impl WeightInfo for () { /// Proof: Nfts Account (max_values: None, max_size: Some(88), added: 2563, mode: MaxEncodedLen) fn force_mint() -> Weight { // Proof Size summary in bytes: - // Measured: `448` - // Estimated: `13506` - // Minimum execution time: 43_976 nanoseconds. - Weight::from_parts(44_831_000, 0) - .saturating_add(Weight::from_parts(0, 13506)) + // Measured: `453` + // Estimated: `18460` + // Minimum execution time: 47_393_000 picoseconds. + Weight::from_parts(47_906_000, 18460) .saturating_add(RocksDbWeight::get().reads(5_u64)) .saturating_add(RocksDbWeight::get().writes(4_u64)) } /// Storage: Nfts ItemConfigOf (r:1 w:1) /// Proof: Nfts ItemConfigOf (max_values: None, max_size: Some(48), added: 2523, mode: MaxEncodedLen) /// Storage: Nfts Collection (r:1 w:1) - /// Proof: Nfts Collection (max_values: None, max_size: Some(80), added: 2555, mode: MaxEncodedLen) + /// Proof: Nfts Collection (max_values: None, max_size: Some(84), added: 2559, mode: MaxEncodedLen) /// Storage: Nfts Item (r:1 w:1) /// Proof: Nfts Item (max_values: None, max_size: Some(861), added: 3336, mode: MaxEncodedLen) - /// Storage: Nfts CollectionRoleOf (r:1 w:0) - /// Proof: Nfts CollectionRoleOf (max_values: None, max_size: Some(69), added: 2544, mode: MaxEncodedLen) /// Storage: Nfts ItemMetadataOf (r:1 w:0) /// Proof: Nfts ItemMetadataOf (max_values: None, max_size: Some(140), added: 2615, mode: MaxEncodedLen) /// Storage: Nfts Account (r:0 w:1) @@ -967,26 +905,21 @@ impl WeightInfo for () { /// Proof: Nfts PendingSwapOf (max_values: None, max_size: Some(71), added: 2546, mode: MaxEncodedLen) fn burn() -> Weight { // Proof Size summary in bytes: - // Measured: `647` - // Estimated: `13573` - // Minimum execution time: 48_233 nanoseconds. - Weight::from_parts(50_113_000, 0) - .saturating_add(Weight::from_parts(0, 13573)) - .saturating_add(RocksDbWeight::get().reads(5_u64)) + // Measured: `594` + // Estimated: `14993` + // Minimum execution time: 47_188_000 picoseconds. + Weight::from_parts(47_746_000, 14993) + .saturating_add(RocksDbWeight::get().reads(4_u64)) .saturating_add(RocksDbWeight::get().writes(7_u64)) } /// Storage: Nfts Collection (r:1 w:0) - /// Proof: Nfts Collection (max_values: None, max_size: Some(80), added: 2555, mode: MaxEncodedLen) + /// Proof: Nfts Collection (max_values: None, max_size: Some(84), added: 2559, mode: MaxEncodedLen) /// Storage: Nfts CollectionConfigOf (r:1 w:0) /// Proof: Nfts CollectionConfigOf (max_values: None, max_size: Some(73), added: 2548, mode: MaxEncodedLen) /// Storage: Nfts ItemConfigOf (r:1 w:0) /// Proof: Nfts ItemConfigOf (max_values: None, max_size: Some(48), added: 2523, mode: MaxEncodedLen) /// Storage: Nfts Item (r:1 w:1) /// Proof: Nfts Item (max_values: None, max_size: Some(861), added: 3336, mode: MaxEncodedLen) - /// Storage: Nfts CollectionRoleOf (r:1 w:0) - /// Proof: Nfts CollectionRoleOf (max_values: None, max_size: Some(69), added: 2544, mode: MaxEncodedLen) - /// Storage: System Account (r:1 w:1) - /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) /// Storage: Nfts Account (r:0 w:2) /// Proof: Nfts Account (max_values: None, max_size: Some(88), added: 2563, mode: MaxEncodedLen) /// Storage: Nfts ItemPriceOf (r:0 w:1) @@ -995,16 +928,15 @@ impl WeightInfo for () { /// Proof: Nfts PendingSwapOf (max_values: None, max_size: Some(71), added: 2546, mode: MaxEncodedLen) fn transfer() -> Weight { // Proof Size summary in bytes: - // Measured: `882` - // Estimated: `16109` - // Minimum execution time: 55_452 nanoseconds. - Weight::from_parts(57_642_000, 0) - .saturating_add(Weight::from_parts(0, 16109)) - .saturating_add(RocksDbWeight::get().reads(6_u64)) - .saturating_add(RocksDbWeight::get().writes(6_u64)) + // Measured: `623` + // Estimated: `14926` + // Minimum execution time: 37_984_000 picoseconds. + Weight::from_parts(38_446_000, 14926) + .saturating_add(RocksDbWeight::get().reads(4_u64)) + .saturating_add(RocksDbWeight::get().writes(5_u64)) } /// Storage: Nfts Collection (r:1 w:0) - /// Proof: Nfts Collection (max_values: None, max_size: Some(80), added: 2555, mode: MaxEncodedLen) + /// Proof: Nfts Collection (max_values: None, max_size: Some(84), added: 2559, mode: MaxEncodedLen) /// Storage: Nfts CollectionConfigOf (r:1 w:0) /// Proof: Nfts CollectionConfigOf (max_values: None, max_size: Some(73), added: 2548, mode: MaxEncodedLen) /// Storage: Nfts Item (r:5000 w:5000) @@ -1012,13 +944,12 @@ impl WeightInfo for () { /// The range of component `i` is `[0, 5000]`. fn redeposit(i: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `756 + i * (140 ±0)` - // Estimated: `5103 + i * (3336 ±0)` - // Minimum execution time: 15_598 nanoseconds. - Weight::from_parts(15_926_000, 0) - .saturating_add(Weight::from_parts(0, 5103)) - // Standard Error: 13_692 - .saturating_add(Weight::from_parts(14_040_741, 0).saturating_mul(i.into())) + // Measured: `761 + i * (140 ±0)` + // Estimated: `8077 + i * (3336 ±0)` + // Minimum execution time: 17_297_000 picoseconds. + Weight::from_parts(17_634_000, 8077) + // Standard Error: 17_773 + .saturating_add(Weight::from_parts(14_395_819, 0).saturating_mul(i.into())) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().reads((1_u64).saturating_mul(i.into()))) .saturating_add(RocksDbWeight::get().writes((1_u64).saturating_mul(i.into()))) @@ -1031,10 +962,9 @@ impl WeightInfo for () { fn lock_item_transfer() -> Weight { // Proof Size summary in bytes: // Measured: `401` - // Estimated: `5067` - // Minimum execution time: 19_686 nanoseconds. - Weight::from_parts(20_404_000, 0) - .saturating_add(Weight::from_parts(0, 5067)) + // Estimated: `7047` + // Minimum execution time: 21_827_000 picoseconds. + Weight::from_parts(22_134_000, 7047) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } @@ -1045,101 +975,96 @@ impl WeightInfo for () { fn unlock_item_transfer() -> Weight { // Proof Size summary in bytes: // Measured: `401` - // Estimated: `5067` - // Minimum execution time: 19_172 nanoseconds. - Weight::from_parts(20_151_000, 0) - .saturating_add(Weight::from_parts(0, 5067)) + // Estimated: `7047` + // Minimum execution time: 21_549_000 picoseconds. + Weight::from_parts(21_987_000, 7047) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } - /// Storage: Nfts CollectionRoleOf (r:1 w:0) - /// Proof: Nfts CollectionRoleOf (max_values: None, max_size: Some(69), added: 2544, mode: MaxEncodedLen) + /// Storage: Nfts Collection (r:1 w:0) + /// Proof: Nfts Collection (max_values: None, max_size: Some(84), added: 2559, mode: MaxEncodedLen) /// Storage: Nfts CollectionConfigOf (r:1 w:1) /// Proof: Nfts CollectionConfigOf (max_values: None, max_size: Some(73), added: 2548, mode: MaxEncodedLen) fn lock_collection() -> Weight { // Proof Size summary in bytes: - // Measured: `289` - // Estimated: `5092` - // Minimum execution time: 17_063 nanoseconds. - Weight::from_parts(17_482_000, 0) - .saturating_add(Weight::from_parts(0, 5092)) + // Measured: `338` + // Estimated: `7087` + // Minimum execution time: 18_930_000 picoseconds. + Weight::from_parts(19_200_000, 7087) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } /// Storage: Nfts OwnershipAcceptance (r:1 w:1) /// Proof: Nfts OwnershipAcceptance (max_values: None, max_size: Some(52), added: 2527, mode: MaxEncodedLen) /// Storage: Nfts Collection (r:1 w:1) - /// Proof: Nfts Collection (max_values: None, max_size: Some(80), added: 2555, mode: MaxEncodedLen) + /// Proof: Nfts Collection (max_values: None, max_size: Some(84), added: 2559, mode: MaxEncodedLen) /// Storage: Nfts CollectionAccount (r:0 w:2) /// Proof: Nfts CollectionAccount (max_values: None, max_size: Some(68), added: 2543, mode: MaxEncodedLen) fn transfer_ownership() -> Weight { // Proof Size summary in bytes: - // Measured: `381` - // Estimated: `5082` - // Minimum execution time: 21_974 nanoseconds. - Weight::from_parts(22_770_000, 0) - .saturating_add(Weight::from_parts(0, 5082)) + // Measured: `386` + // Estimated: `7066` + // Minimum execution time: 24_929_000 picoseconds. + Weight::from_parts(25_786_000, 7066) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().writes(4_u64)) } /// Storage: Nfts Collection (r:1 w:1) - /// Proof: Nfts Collection (max_values: None, max_size: Some(80), added: 2555, mode: MaxEncodedLen) - /// Storage: Nfts CollectionRoleOf (r:0 w:4) + /// Proof: Nfts Collection (max_values: None, max_size: Some(84), added: 2559, mode: MaxEncodedLen) + /// Storage: Nfts CollectionRoleOf (r:1 w:4) /// Proof: Nfts CollectionRoleOf (max_values: None, max_size: Some(69), added: 2544, mode: MaxEncodedLen) fn set_team() -> Weight { // Proof Size summary in bytes: - // Measured: `362` - // Estimated: `2555` - // Minimum execution time: 24_341 nanoseconds. - Weight::from_parts(25_059_000, 0) - .saturating_add(Weight::from_parts(0, 2555)) - .saturating_add(RocksDbWeight::get().reads(1_u64)) + // Measured: `367` + // Estimated: `7083` + // Minimum execution time: 28_245_000 picoseconds. + Weight::from_parts(28_490_000, 7083) + .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().writes(5_u64)) } /// Storage: Nfts Collection (r:1 w:1) - /// Proof: Nfts Collection (max_values: None, max_size: Some(80), added: 2555, mode: MaxEncodedLen) + /// Proof: Nfts Collection (max_values: None, max_size: Some(84), added: 2559, mode: MaxEncodedLen) /// Storage: Nfts CollectionAccount (r:0 w:2) /// Proof: Nfts CollectionAccount (max_values: None, max_size: Some(68), added: 2543, mode: MaxEncodedLen) fn force_collection_owner() -> Weight { // Proof Size summary in bytes: - // Measured: `304` - // Estimated: `2555` - // Minimum execution time: 16_897 nanoseconds. - Weight::from_parts(17_560_000, 0) - .saturating_add(Weight::from_parts(0, 2555)) + // Measured: `309` + // Estimated: `3549` + // Minimum execution time: 19_971_000 picoseconds. + Weight::from_parts(20_276_000, 3549) .saturating_add(RocksDbWeight::get().reads(1_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) } /// Storage: Nfts Collection (r:1 w:0) - /// Proof: Nfts Collection (max_values: None, max_size: Some(80), added: 2555, mode: MaxEncodedLen) + /// Proof: Nfts Collection (max_values: None, max_size: Some(84), added: 2559, mode: MaxEncodedLen) /// Storage: Nfts CollectionConfigOf (r:0 w:1) /// Proof: Nfts CollectionConfigOf (max_values: None, max_size: Some(73), added: 2548, mode: MaxEncodedLen) fn force_collection_config() -> Weight { // Proof Size summary in bytes: // Measured: `242` - // Estimated: `2555` - // Minimum execution time: 13_239 nanoseconds. - Weight::from_parts(13_963_000, 0) - .saturating_add(Weight::from_parts(0, 2555)) + // Estimated: `3549` + // Minimum execution time: 15_924_000 picoseconds. + Weight::from_parts(16_258_000, 3549) .saturating_add(RocksDbWeight::get().reads(1_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } - /// Storage: Nfts Collection (r:1 w:0) - /// Proof: Nfts Collection (max_values: None, max_size: Some(80), added: 2555, mode: MaxEncodedLen) + /// Storage: Nfts CollectionRoleOf (r:1 w:0) + /// Proof: Nfts CollectionRoleOf (max_values: None, max_size: Some(69), added: 2544, mode: MaxEncodedLen) /// Storage: Nfts ItemConfigOf (r:1 w:1) /// Proof: Nfts ItemConfigOf (max_values: None, max_size: Some(48), added: 2523, mode: MaxEncodedLen) fn lock_item_properties() -> Weight { // Proof Size summary in bytes: - // Measured: `445` - // Estimated: `5078` - // Minimum execution time: 17_187 nanoseconds. - Weight::from_parts(17_942_000, 0) - .saturating_add(Weight::from_parts(0, 5078)) + // Measured: `401` + // Estimated: `7047` + // Minimum execution time: 20_780_000 picoseconds. + Weight::from_parts(21_109_000, 7047) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } /// Storage: Nfts Collection (r:1 w:1) - /// Proof: Nfts Collection (max_values: None, max_size: Some(80), added: 2555, mode: MaxEncodedLen) + /// Proof: Nfts Collection (max_values: None, max_size: Some(84), added: 2559, mode: MaxEncodedLen) + /// Storage: Nfts CollectionRoleOf (r:1 w:0) + /// Proof: Nfts CollectionRoleOf (max_values: None, max_size: Some(69), added: 2544, mode: MaxEncodedLen) /// Storage: Nfts CollectionConfigOf (r:1 w:0) /// Proof: Nfts CollectionConfigOf (max_values: None, max_size: Some(73), added: 2548, mode: MaxEncodedLen) /// Storage: Nfts ItemConfigOf (r:1 w:0) @@ -1148,42 +1073,41 @@ impl WeightInfo for () { /// Proof: Nfts Attribute (max_values: None, max_size: Some(446), added: 2921, mode: MaxEncodedLen) fn set_attribute() -> Weight { // Proof Size summary in bytes: - // Measured: `474` - // Estimated: `10547` - // Minimum execution time: 40_925 nanoseconds. - Weight::from_parts(42_733_000, 0) - .saturating_add(Weight::from_parts(0, 10547)) - .saturating_add(RocksDbWeight::get().reads(4_u64)) + // Measured: `537` + // Estimated: `18045` + // Minimum execution time: 50_817_000 picoseconds. + Weight::from_parts(51_585_000, 18045) + .saturating_add(RocksDbWeight::get().reads(5_u64)) .saturating_add(RocksDbWeight::get().writes(2_u64)) } /// Storage: Nfts Collection (r:1 w:1) - /// Proof: Nfts Collection (max_values: None, max_size: Some(80), added: 2555, mode: MaxEncodedLen) + /// Proof: Nfts Collection (max_values: None, max_size: Some(84), added: 2559, mode: MaxEncodedLen) /// Storage: Nfts Attribute (r:1 w:1) /// Proof: Nfts Attribute (max_values: None, max_size: Some(446), added: 2921, mode: MaxEncodedLen) fn force_set_attribute() -> Weight { // Proof Size summary in bytes: - // Measured: `337` - // Estimated: `5476` - // Minimum execution time: 24_486 nanoseconds. - Weight::from_parts(25_409_000, 0) - .saturating_add(Weight::from_parts(0, 5476)) + // Measured: `342` + // Estimated: `7460` + // Minimum execution time: 28_821_000 picoseconds. + Weight::from_parts(29_417_000, 7460) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().writes(2_u64)) } /// Storage: Nfts Attribute (r:1 w:1) /// Proof: Nfts Attribute (max_values: None, max_size: Some(446), added: 2921, mode: MaxEncodedLen) - /// Storage: Nfts Collection (r:1 w:1) - /// Proof: Nfts Collection (max_values: None, max_size: Some(80), added: 2555, mode: MaxEncodedLen) + /// Storage: Nfts CollectionRoleOf (r:1 w:0) + /// Proof: Nfts CollectionRoleOf (max_values: None, max_size: Some(69), added: 2544, mode: MaxEncodedLen) /// Storage: Nfts ItemConfigOf (r:1 w:0) /// Proof: Nfts ItemConfigOf (max_values: None, max_size: Some(48), added: 2523, mode: MaxEncodedLen) + /// Storage: Nfts Collection (r:1 w:1) + /// Proof: Nfts Collection (max_values: None, max_size: Some(84), added: 2559, mode: MaxEncodedLen) fn clear_attribute() -> Weight { // Proof Size summary in bytes: - // Measured: `916` - // Estimated: `7999` - // Minimum execution time: 36_643 nanoseconds. - Weight::from_parts(37_805_000, 0) - .saturating_add(Weight::from_parts(0, 7999)) - .saturating_add(RocksDbWeight::get().reads(3_u64)) + // Measured: `979` + // Estimated: `14507` + // Minimum execution time: 47_021_000 picoseconds. + Weight::from_parts(47_509_000, 14507) + .saturating_add(RocksDbWeight::get().reads(4_u64)) .saturating_add(RocksDbWeight::get().writes(2_u64)) } /// Storage: Nfts Item (r:1 w:0) @@ -1193,10 +1117,9 @@ impl WeightInfo for () { fn approve_item_attributes() -> Weight { // Proof Size summary in bytes: // Measured: `379` - // Estimated: `6492` - // Minimum execution time: 16_798 nanoseconds. - Weight::from_parts(17_326_000, 0) - .saturating_add(Weight::from_parts(0, 6492)) + // Estimated: `8472` + // Minimum execution time: 19_457_000 picoseconds. + Weight::from_parts(19_738_000, 8472) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } @@ -1212,20 +1135,21 @@ impl WeightInfo for () { fn cancel_item_attributes_approval(n: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `899 + n * (396 ±0)` - // Estimated: `12016 + n * (2921 ±0)` - // Minimum execution time: 25_524 nanoseconds. - Weight::from_parts(26_107_000, 0) - .saturating_add(Weight::from_parts(0, 12016)) - // Standard Error: 5_460 - .saturating_add(Weight::from_parts(9_030_830, 0).saturating_mul(n.into())) + // Estimated: `15976 + n * (2921 ±0)` + // Minimum execution time: 29_027_000 picoseconds. + Weight::from_parts(29_376_000, 15976) + // Standard Error: 3_500 + .saturating_add(Weight::from_parts(5_568_116, 0).saturating_mul(n.into())) .saturating_add(RocksDbWeight::get().reads(4_u64)) .saturating_add(RocksDbWeight::get().reads((1_u64).saturating_mul(n.into()))) .saturating_add(RocksDbWeight::get().writes(2_u64)) .saturating_add(RocksDbWeight::get().writes((1_u64).saturating_mul(n.into()))) .saturating_add(Weight::from_parts(0, 2921).saturating_mul(n.into())) } + /// Storage: Nfts CollectionRoleOf (r:1 w:0) + /// Proof: Nfts CollectionRoleOf (max_values: None, max_size: Some(69), added: 2544, mode: MaxEncodedLen) /// Storage: Nfts Collection (r:1 w:1) - /// Proof: Nfts Collection (max_values: None, max_size: Some(80), added: 2555, mode: MaxEncodedLen) + /// Proof: Nfts Collection (max_values: None, max_size: Some(84), added: 2559, mode: MaxEncodedLen) /// Storage: Nfts ItemConfigOf (r:1 w:0) /// Proof: Nfts ItemConfigOf (max_values: None, max_size: Some(48), added: 2523, mode: MaxEncodedLen) /// Storage: Nfts CollectionConfigOf (r:1 w:0) @@ -1234,104 +1158,97 @@ impl WeightInfo for () { /// Proof: Nfts ItemMetadataOf (max_values: None, max_size: Some(140), added: 2615, mode: MaxEncodedLen) fn set_metadata() -> Weight { // Proof Size summary in bytes: - // Measured: `474` - // Estimated: `10241` - // Minimum execution time: 34_400 nanoseconds. - Weight::from_parts(35_469_000, 0) - .saturating_add(Weight::from_parts(0, 10241)) - .saturating_add(RocksDbWeight::get().reads(4_u64)) + // Measured: `537` + // Estimated: `17739` + // Minimum execution time: 41_658_000 picoseconds. + Weight::from_parts(42_241_000, 17739) + .saturating_add(RocksDbWeight::get().reads(5_u64)) .saturating_add(RocksDbWeight::get().writes(2_u64)) } + /// Storage: Nfts CollectionRoleOf (r:1 w:0) + /// Proof: Nfts CollectionRoleOf (max_values: None, max_size: Some(69), added: 2544, mode: MaxEncodedLen) /// Storage: Nfts ItemMetadataOf (r:1 w:1) /// Proof: Nfts ItemMetadataOf (max_values: None, max_size: Some(140), added: 2615, mode: MaxEncodedLen) /// Storage: Nfts Collection (r:1 w:1) - /// Proof: Nfts Collection (max_values: None, max_size: Some(80), added: 2555, mode: MaxEncodedLen) + /// Proof: Nfts Collection (max_values: None, max_size: Some(84), added: 2559, mode: MaxEncodedLen) /// Storage: Nfts ItemConfigOf (r:1 w:0) /// Proof: Nfts ItemConfigOf (max_values: None, max_size: Some(48), added: 2523, mode: MaxEncodedLen) fn clear_metadata() -> Weight { // Proof Size summary in bytes: - // Measured: `609` - // Estimated: `7693` - // Minimum execution time: 31_560 nanoseconds. - Weight::from_parts(33_081_000, 0) - .saturating_add(Weight::from_parts(0, 7693)) - .saturating_add(RocksDbWeight::get().reads(3_u64)) + // Measured: `672` + // Estimated: `14201` + // Minimum execution time: 39_838_000 picoseconds. + Weight::from_parts(40_757_000, 14201) + .saturating_add(RocksDbWeight::get().reads(4_u64)) .saturating_add(RocksDbWeight::get().writes(2_u64)) } + /// Storage: Nfts CollectionRoleOf (r:1 w:0) + /// Proof: Nfts CollectionRoleOf (max_values: None, max_size: Some(69), added: 2544, mode: MaxEncodedLen) /// Storage: Nfts CollectionConfigOf (r:1 w:0) /// Proof: Nfts CollectionConfigOf (max_values: None, max_size: Some(73), added: 2548, mode: MaxEncodedLen) /// Storage: Nfts Collection (r:1 w:1) - /// Proof: Nfts Collection (max_values: None, max_size: Some(80), added: 2555, mode: MaxEncodedLen) + /// Proof: Nfts Collection (max_values: None, max_size: Some(84), added: 2559, mode: MaxEncodedLen) /// Storage: Nfts CollectionMetadataOf (r:1 w:1) /// Proof: Nfts CollectionMetadataOf (max_values: None, max_size: Some(87), added: 2562, mode: MaxEncodedLen) fn set_collection_metadata() -> Weight { // Proof Size summary in bytes: - // Measured: `333` - // Estimated: `7665` - // Minimum execution time: 28_821 nanoseconds. - Weight::from_parts(30_010_000, 0) - .saturating_add(Weight::from_parts(0, 7665)) - .saturating_add(RocksDbWeight::get().reads(3_u64)) + // Measured: `396` + // Estimated: `14173` + // Minimum execution time: 37_327_000 picoseconds. + Weight::from_parts(37_874_000, 14173) + .saturating_add(RocksDbWeight::get().reads(4_u64)) .saturating_add(RocksDbWeight::get().writes(2_u64)) } + /// Storage: Nfts CollectionRoleOf (r:1 w:0) + /// Proof: Nfts CollectionRoleOf (max_values: None, max_size: Some(69), added: 2544, mode: MaxEncodedLen) /// Storage: Nfts Collection (r:1 w:0) - /// Proof: Nfts Collection (max_values: None, max_size: Some(80), added: 2555, mode: MaxEncodedLen) + /// Proof: Nfts Collection (max_values: None, max_size: Some(84), added: 2559, mode: MaxEncodedLen) /// Storage: Nfts CollectionConfigOf (r:1 w:0) /// Proof: Nfts CollectionConfigOf (max_values: None, max_size: Some(73), added: 2548, mode: MaxEncodedLen) /// Storage: Nfts CollectionMetadataOf (r:1 w:1) /// Proof: Nfts CollectionMetadataOf (max_values: None, max_size: Some(87), added: 2562, mode: MaxEncodedLen) fn clear_collection_metadata() -> Weight { // Proof Size summary in bytes: - // Measured: `476` - // Estimated: `7665` - // Minimum execution time: 27_608 nanoseconds. - Weight::from_parts(28_766_000, 0) - .saturating_add(Weight::from_parts(0, 7665)) - .saturating_add(RocksDbWeight::get().reads(3_u64)) + // Measured: `539` + // Estimated: `14173` + // Minimum execution time: 35_305_000 picoseconds. + Weight::from_parts(36_213_000, 14173) + .saturating_add(RocksDbWeight::get().reads(4_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } /// Storage: Nfts Item (r:1 w:1) /// Proof: Nfts Item (max_values: None, max_size: Some(861), added: 3336, mode: MaxEncodedLen) /// Storage: Nfts CollectionConfigOf (r:1 w:0) /// Proof: Nfts CollectionConfigOf (max_values: None, max_size: Some(73), added: 2548, mode: MaxEncodedLen) - /// Storage: Nfts CollectionRoleOf (r:1 w:0) - /// Proof: Nfts CollectionRoleOf (max_values: None, max_size: Some(69), added: 2544, mode: MaxEncodedLen) fn approve_transfer() -> Weight { // Proof Size summary in bytes: - // Measured: `466` - // Estimated: `8428` - // Minimum execution time: 23_987 nanoseconds. - Weight::from_parts(24_819_000, 0) - .saturating_add(Weight::from_parts(0, 8428)) - .saturating_add(RocksDbWeight::get().reads(3_u64)) + // Measured: `408` + // Estimated: `7864` + // Minimum execution time: 22_680_000 picoseconds. + Weight::from_parts(23_126_000, 7864) + .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } /// Storage: Nfts Item (r:1 w:1) /// Proof: Nfts Item (max_values: None, max_size: Some(861), added: 3336, mode: MaxEncodedLen) - /// Storage: Nfts CollectionRoleOf (r:1 w:0) - /// Proof: Nfts CollectionRoleOf (max_values: None, max_size: Some(69), added: 2544, mode: MaxEncodedLen) fn cancel_approval() -> Weight { // Proof Size summary in bytes: - // Measured: `474` - // Estimated: `5880` - // Minimum execution time: 21_254 nanoseconds. - Weight::from_parts(21_826_000, 0) - .saturating_add(Weight::from_parts(0, 5880)) - .saturating_add(RocksDbWeight::get().reads(2_u64)) + // Measured: `416` + // Estimated: `4326` + // Minimum execution time: 19_837_000 picoseconds. + Weight::from_parts(20_352_000, 4326) + .saturating_add(RocksDbWeight::get().reads(1_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } /// Storage: Nfts Item (r:1 w:1) /// Proof: Nfts Item (max_values: None, max_size: Some(861), added: 3336, mode: MaxEncodedLen) - /// Storage: Nfts CollectionRoleOf (r:1 w:0) - /// Proof: Nfts CollectionRoleOf (max_values: None, max_size: Some(69), added: 2544, mode: MaxEncodedLen) fn clear_all_transfer_approvals() -> Weight { // Proof Size summary in bytes: - // Measured: `474` - // Estimated: `5880` - // Minimum execution time: 20_272 nanoseconds. - Weight::from_parts(20_922_000, 0) - .saturating_add(Weight::from_parts(0, 5880)) - .saturating_add(RocksDbWeight::get().reads(2_u64)) + // Measured: `416` + // Estimated: `4326` + // Minimum execution time: 18_911_000 picoseconds. + Weight::from_parts(19_233_000, 4326) + .saturating_add(RocksDbWeight::get().reads(1_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } /// Storage: Nfts OwnershipAcceptance (r:1 w:1) @@ -1339,38 +1256,35 @@ impl WeightInfo for () { fn set_accept_ownership() -> Weight { // Proof Size summary in bytes: // Measured: `42` - // Estimated: `2527` - // Minimum execution time: 14_287 nanoseconds. - Weight::from_parts(14_960_000, 0) - .saturating_add(Weight::from_parts(0, 2527)) + // Estimated: `3517` + // Minimum execution time: 17_292_000 picoseconds. + Weight::from_parts(17_568_000, 3517) .saturating_add(RocksDbWeight::get().reads(1_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } /// Storage: Nfts CollectionConfigOf (r:1 w:1) /// Proof: Nfts CollectionConfigOf (max_values: None, max_size: Some(73), added: 2548, mode: MaxEncodedLen) /// Storage: Nfts Collection (r:1 w:0) - /// Proof: Nfts Collection (max_values: None, max_size: Some(80), added: 2555, mode: MaxEncodedLen) + /// Proof: Nfts Collection (max_values: None, max_size: Some(84), added: 2559, mode: MaxEncodedLen) fn set_collection_max_supply() -> Weight { // Proof Size summary in bytes: - // Measured: `333` - // Estimated: `5103` - // Minimum execution time: 17_948 nanoseconds. - Weight::from_parts(18_780_000, 0) - .saturating_add(Weight::from_parts(0, 5103)) + // Measured: `338` + // Estimated: `7087` + // Minimum execution time: 20_323_000 picoseconds. + Weight::from_parts(20_692_000, 7087) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } - /// Storage: Nfts Collection (r:1 w:0) - /// Proof: Nfts Collection (max_values: None, max_size: Some(80), added: 2555, mode: MaxEncodedLen) + /// Storage: Nfts CollectionRoleOf (r:1 w:0) + /// Proof: Nfts CollectionRoleOf (max_values: None, max_size: Some(69), added: 2544, mode: MaxEncodedLen) /// Storage: Nfts CollectionConfigOf (r:1 w:1) /// Proof: Nfts CollectionConfigOf (max_values: None, max_size: Some(73), added: 2548, mode: MaxEncodedLen) fn update_mint_settings() -> Weight { // Proof Size summary in bytes: - // Measured: `333` - // Estimated: `5103` - // Minimum execution time: 16_616 nanoseconds. - Weight::from_parts(17_155_000, 0) - .saturating_add(Weight::from_parts(0, 5103)) + // Measured: `289` + // Estimated: `7072` + // Minimum execution time: 19_971_000 picoseconds. + Weight::from_parts(20_159_000, 7072) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } @@ -1385,10 +1299,9 @@ impl WeightInfo for () { fn set_price() -> Weight { // Proof Size summary in bytes: // Measured: `516` - // Estimated: `8407` - // Minimum execution time: 22_777 nanoseconds. - Weight::from_parts(23_955_000, 0) - .saturating_add(Weight::from_parts(0, 8407)) + // Estimated: `11377` + // Minimum execution time: 26_202_000 picoseconds. + Weight::from_parts(26_499_000, 11377) .saturating_add(RocksDbWeight::get().reads(3_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } @@ -1397,37 +1310,33 @@ impl WeightInfo for () { /// Storage: Nfts ItemPriceOf (r:1 w:1) /// Proof: Nfts ItemPriceOf (max_values: None, max_size: Some(89), added: 2564, mode: MaxEncodedLen) /// Storage: Nfts Collection (r:1 w:0) - /// Proof: Nfts Collection (max_values: None, max_size: Some(80), added: 2555, mode: MaxEncodedLen) + /// Proof: Nfts Collection (max_values: None, max_size: Some(84), added: 2559, mode: MaxEncodedLen) /// Storage: Nfts CollectionConfigOf (r:1 w:0) /// Proof: Nfts CollectionConfigOf (max_values: None, max_size: Some(73), added: 2548, mode: MaxEncodedLen) /// Storage: Nfts ItemConfigOf (r:1 w:0) /// Proof: Nfts ItemConfigOf (max_values: None, max_size: Some(48), added: 2523, mode: MaxEncodedLen) - /// Storage: System Account (r:1 w:1) - /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) /// Storage: Nfts Account (r:0 w:2) /// Proof: Nfts Account (max_values: None, max_size: Some(88), added: 2563, mode: MaxEncodedLen) /// Storage: Nfts PendingSwapOf (r:0 w:1) /// Proof: Nfts PendingSwapOf (max_values: None, max_size: Some(71), added: 2546, mode: MaxEncodedLen) fn buy_item() -> Weight { // Proof Size summary in bytes: - // Measured: `934` - // Estimated: `16129` - // Minimum execution time: 61_131 nanoseconds. - Weight::from_parts(62_791_000, 0) - .saturating_add(Weight::from_parts(0, 16129)) - .saturating_add(RocksDbWeight::get().reads(6_u64)) - .saturating_add(RocksDbWeight::get().writes(6_u64)) + // Measured: `767` + // Estimated: `18480` + // Minimum execution time: 50_601_000 picoseconds. + Weight::from_parts(51_283_000, 18480) + .saturating_add(RocksDbWeight::get().reads(5_u64)) + .saturating_add(RocksDbWeight::get().writes(5_u64)) } /// The range of component `n` is `[0, 10]`. fn pay_tips(n: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_952 nanoseconds. - Weight::from_parts(3_975_700, 0) - .saturating_add(Weight::from_parts(0, 0)) - // Standard Error: 11_254 - .saturating_add(Weight::from_parts(3_501_698, 0).saturating_mul(n.into())) + // Minimum execution time: 2_733_000 picoseconds. + Weight::from_parts(5_568_590, 0) + // Standard Error: 14_136 + .saturating_add(Weight::from_parts(3_763_928, 0).saturating_mul(n.into())) } /// Storage: Nfts Item (r:2 w:0) /// Proof: Nfts Item (max_values: None, max_size: Some(861), added: 3336, mode: MaxEncodedLen) @@ -1436,10 +1345,9 @@ impl WeightInfo for () { fn create_swap() -> Weight { // Proof Size summary in bytes: // Measured: `524` - // Estimated: `6672` - // Minimum execution time: 20_327 nanoseconds. - Weight::from_parts(21_714_000, 0) - .saturating_add(Weight::from_parts(0, 6672)) + // Estimated: `7662` + // Minimum execution time: 22_901_000 picoseconds. + Weight::from_parts(23_673_000, 7662) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } @@ -1450,10 +1358,9 @@ impl WeightInfo for () { fn cancel_swap() -> Weight { // Proof Size summary in bytes: // Measured: `511` - // Estimated: `5882` - // Minimum execution time: 20_668 nanoseconds. - Weight::from_parts(21_416_000, 0) - .saturating_add(Weight::from_parts(0, 5882)) + // Estimated: `7862` + // Minimum execution time: 22_532_000 picoseconds. + Weight::from_parts(22_831_000, 7862) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } @@ -1462,29 +1369,28 @@ impl WeightInfo for () { /// Storage: Nfts PendingSwapOf (r:1 w:2) /// Proof: Nfts PendingSwapOf (max_values: None, max_size: Some(71), added: 2546, mode: MaxEncodedLen) /// Storage: Nfts Collection (r:1 w:0) - /// Proof: Nfts Collection (max_values: None, max_size: Some(80), added: 2555, mode: MaxEncodedLen) + /// Proof: Nfts Collection (max_values: None, max_size: Some(84), added: 2559, mode: MaxEncodedLen) /// Storage: Nfts CollectionConfigOf (r:1 w:0) /// Proof: Nfts CollectionConfigOf (max_values: None, max_size: Some(73), added: 2548, mode: MaxEncodedLen) /// Storage: Nfts ItemConfigOf (r:2 w:0) /// Proof: Nfts ItemConfigOf (max_values: None, max_size: Some(48), added: 2523, mode: MaxEncodedLen) - /// Storage: System Account (r:1 w:1) - /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) /// Storage: Nfts Account (r:0 w:4) /// Proof: Nfts Account (max_values: None, max_size: Some(88), added: 2563, mode: MaxEncodedLen) /// Storage: Nfts ItemPriceOf (r:0 w:2) /// Proof: Nfts ItemPriceOf (max_values: None, max_size: Some(89), added: 2564, mode: MaxEncodedLen) fn claim_swap() -> Weight { // Proof Size summary in bytes: - // Measured: `1097` - // Estimated: `21970` - // Minimum execution time: 88_006 nanoseconds. - Weight::from_parts(90_390_000, 0) - .saturating_add(Weight::from_parts(0, 21970)) - .saturating_add(RocksDbWeight::get().reads(8_u64)) - .saturating_add(RocksDbWeight::get().writes(11_u64)) + // Measured: `896` + // Estimated: `24321` + // Minimum execution time: 77_668_000 picoseconds. + Weight::from_parts(78_407_000, 24321) + .saturating_add(RocksDbWeight::get().reads(7_u64)) + .saturating_add(RocksDbWeight::get().writes(10_u64)) } /// Storage: Nfts Collection (r:1 w:1) - /// Proof: Nfts Collection (max_values: None, max_size: Some(80), added: 2555, mode: MaxEncodedLen) + /// Proof: Nfts Collection (max_values: None, max_size: Some(84), added: 2559, mode: MaxEncodedLen) + /// Storage: Nfts CollectionRoleOf (r:2 w:0) + /// Proof: Nfts CollectionRoleOf (max_values: None, max_size: Some(69), added: 2544, mode: MaxEncodedLen) /// Storage: Nfts CollectionConfigOf (r:1 w:0) /// Proof: Nfts CollectionConfigOf (max_values: None, max_size: Some(73), added: 2548, mode: MaxEncodedLen) /// Storage: Nfts Item (r:1 w:1) @@ -1502,14 +1408,13 @@ impl WeightInfo for () { /// The range of component `n` is `[0, 10]`. fn mint_pre_signed(n: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `596` - // Estimated: `16180 + n * (2921 ±0)` - // Minimum execution time: 124_967 nanoseconds. - Weight::from_parts(131_602_642, 0) - .saturating_add(Weight::from_parts(0, 16180)) - // Standard Error: 36_480 - .saturating_add(Weight::from_parts(25_811_394, 0).saturating_mul(n.into())) - .saturating_add(RocksDbWeight::get().reads(6_u64)) + // Measured: `659` + // Estimated: `29192 + n * (2921 ±0)` + // Minimum execution time: 133_147_000 picoseconds. + Weight::from_parts(138_031_439, 29192) + // Standard Error: 28_665 + .saturating_add(Weight::from_parts(28_206_062, 0).saturating_mul(n.into())) + .saturating_add(RocksDbWeight::get().reads(8_u64)) .saturating_add(RocksDbWeight::get().reads((1_u64).saturating_mul(n.into()))) .saturating_add(RocksDbWeight::get().writes(6_u64)) .saturating_add(RocksDbWeight::get().writes((1_u64).saturating_mul(n.into()))) @@ -1519,10 +1424,10 @@ impl WeightInfo for () { /// Proof: Nfts Item (max_values: None, max_size: Some(861), added: 3336, mode: MaxEncodedLen) /// Storage: Nfts ItemAttributesApprovalsOf (r:1 w:1) /// Proof: Nfts ItemAttributesApprovalsOf (max_values: None, max_size: Some(681), added: 3156, mode: MaxEncodedLen) - /// Storage: Nfts Collection (r:1 w:1) - /// Proof: Nfts Collection (max_values: None, max_size: Some(80), added: 2555, mode: MaxEncodedLen) /// Storage: Nfts CollectionConfigOf (r:1 w:0) /// Proof: Nfts CollectionConfigOf (max_values: None, max_size: Some(73), added: 2548, mode: MaxEncodedLen) + /// Storage: Nfts Collection (r:1 w:1) + /// Proof: Nfts Collection (max_values: None, max_size: Some(84), added: 2559, mode: MaxEncodedLen) /// Storage: Nfts Attribute (r:10 w:10) /// Proof: Nfts Attribute (max_values: None, max_size: Some(446), added: 2921, mode: MaxEncodedLen) /// Storage: System Account (r:1 w:1) @@ -1530,13 +1435,12 @@ impl WeightInfo for () { /// The range of component `n` is `[0, 10]`. fn set_attributes_pre_signed(n: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `716` - // Estimated: `14198 + n * (2921 ±0)` - // Minimum execution time: 84_153 nanoseconds. - Weight::from_parts(96_401_623, 0) - .saturating_add(Weight::from_parts(0, 14198)) - // Standard Error: 70_244 - .saturating_add(Weight::from_parts(26_866_222, 0).saturating_mul(n.into())) + // Measured: `721` + // Estimated: `20142 + n * (2921 ±0)` + // Minimum execution time: 78_243_000 picoseconds. + Weight::from_parts(90_947_408, 20142) + // Standard Error: 75_516 + .saturating_add(Weight::from_parts(28_059_623, 0).saturating_mul(n.into())) .saturating_add(RocksDbWeight::get().reads(4_u64)) .saturating_add(RocksDbWeight::get().reads((1_u64).saturating_mul(n.into()))) .saturating_add(RocksDbWeight::get().writes(2_u64)) From 1bbde5d7c50e55458ff3925ed0ee911d035719d5 Mon Sep 17 00:00:00 2001 From: Vivek Pandya Date: Mon, 13 Mar 2023 21:00:05 +0530 Subject: [PATCH 224/558] Remove use of trait Store from all pallets and deprecate it. (#13535) * Remove use of trait Store from staking pallet * Remove use of trait Store from bounties pallet * Remove use of trait Store from collective pallet * Remove use of trait Store from babe pallet * Remove use of trait Store from assets pallet * Remove use of trait Store from grandpa pallet * Remove use of trait Store from balances pallet * Remove use of trait Store from authorship pallet * Remove use of trait Store from authority-discovery pallet * Remove use of trait Store from atomic-swap pallet * Remove use of trait Store from sudo pallet * Remove use of trait Store from scheduler pallet * Remove use of trait Store from scored-pool pallet * Remove use of trait Store from society pallet * Remove use of trait Store from lottery pallet * Remove use of trait Store from executive pallet * Remove use of trait Store from democracy pallet * Remove use of trait Store from elections-phragmen pallet * Remove use of trait Store from indices pallet * Remove use of trait Store from identity pallet * Remove use of trait Store from multisig pallet * Remove use of trait Store from merkle-mountain-range pallet * Remove use of trait Store from im-online pallet * Remove use of trait Store from membership pallet * Remove use of trait Store from nicks pallet * Remove use of trait Store from session pallet * Remove use of trait Store from transaction-payment pallet * Remove use of trait Store from utility pallet * Remove use of trait Store from child-bounties pallet * Remove use of trait Store from nis pallet * Remove use of trait Store from nfts pallet * Remove use of trait Store from conviction-voting pallet * Remove use of trait Store from treasury pallet * Remove use of trait Store from vesting pallet * Remove use of trait Store from preimage pallet * Remove use of trait Store from uniques pallet * Remove use of trait Store from ranked-collective pallet * Remove use of trait Store from beefy-mmr pallet * Remove use of trait Store from referenda pallet * Remove use of trait Store from whitelist pallet * Remove use of trait Store from alliance pallet * Remove use of trait Store from nomination-pools pallet * Remove use of trait Store from state-trie-migration pallet * Remove use of trait Store from message-queue pallet * Remove use of trait Store from root-offences pallet * Remove use of trait Store from root-testing pallet * Remove use of trait Store from timestamps pallet * Remove use of trait Store from system pallet * Remove use of trait Store from offences pallet * Remove use of trait Store from recovery pallet * Remove use of trait Store from node-authorization pallet * Remove use of trait Store from proxy pallet * Remove use of trait Store from benchmarking pallet * Remove use of trait Store from bags-list pallet * Add deprecated warning in store_trait * Change warning message * Run cargo fmt * Fix warning and update tests * Remove unnecessary allow deprecated * Remove use of trait Store * Fix mismatch in expected output * Minor update to warning message for deprecation of generate_store with Store trait attribute * Fixes as per review comments * Fixes as per review suggestions * Remove use of Store trait from core-fellowship pallet * Fix type in store_trait.rs * Fixes as pre review comment --- bin/node-template/pallets/template/src/lib.rs | 1 - frame/alliance/src/lib.rs | 1 - frame/assets/src/lib.rs | 1 - frame/atomic-swap/src/lib.rs | 1 - frame/authority-discovery/src/lib.rs | 1 - frame/authorship/src/lib.rs | 1 - frame/babe/src/lib.rs | 1 - frame/bags-list/src/lib.rs | 1 - frame/balances/src/lib.rs | 1 - frame/beefy-mmr/src/lib.rs | 1 - frame/benchmarking/pov/src/lib.rs | 1 - frame/benchmarking/src/tests.rs | 1 - frame/benchmarking/src/tests_instance.rs | 1 - frame/bounties/src/lib.rs | 1 - frame/child-bounties/src/lib.rs | 1 - frame/collective/src/lib.rs | 1 - frame/collective/src/tests.rs | 1 - frame/conviction-voting/src/lib.rs | 1 - frame/core-fellowship/src/lib.rs | 1 - frame/democracy/src/lib.rs | 1 - frame/elections-phragmen/src/lib.rs | 1 - frame/examples/basic/src/lib.rs | 1 - frame/examples/offchain-worker/src/lib.rs | 1 - frame/executive/src/lib.rs | 1 - frame/grandpa/src/lib.rs | 1 - frame/identity/src/lib.rs | 1 - frame/im-online/src/lib.rs | 1 - frame/indices/src/lib.rs | 1 - .../src/lib.rs | 4 +- frame/lottery/src/lib.rs | 1 - frame/membership/src/lib.rs | 1 - frame/merkle-mountain-range/src/lib.rs | 1 - frame/message-queue/src/lib.rs | 3 +- frame/multisig/src/lib.rs | 1 - frame/nfts/src/lib.rs | 1 - frame/nicks/src/lib.rs | 1 - frame/nis/src/lib.rs | 1 - frame/node-authorization/src/lib.rs | 1 - frame/nomination-pools/src/lib.rs | 1 - frame/offences/src/lib.rs | 1 - frame/preimage/src/lib.rs | 1 - frame/proxy/src/lib.rs | 1 - frame/ranked-collective/src/lib.rs | 1 - frame/recovery/src/lib.rs | 1 - frame/referenda/src/lib.rs | 1 - frame/root-offences/src/lib.rs | 1 - frame/root-testing/src/lib.rs | 1 - frame/salary/src/lib.rs | 1 - frame/scheduler/src/lib.rs | 1 - frame/scheduler/src/mock.rs | 1 - frame/scored-pool/src/lib.rs | 1 - frame/session/src/historical/mod.rs | 5 +-- frame/session/src/lib.rs | 1 - frame/society/src/lib.rs | 1 - frame/staking/src/migrations.rs | 2 +- frame/staking/src/pallet/impls.rs | 4 +- frame/staking/src/pallet/mod.rs | 5 +-- frame/staking/src/slashing.rs | 41 +++++++++-------- frame/staking/src/tests.rs | 44 +++++++++---------- frame/state-trie-migration/src/lib.rs | 1 - frame/sudo/src/lib.rs | 1 - frame/sudo/src/mock.rs | 1 - .../src/pallet/expand/store_trait.rs | 13 +++++- .../src/pallet/parse/pallet_struct.rs | 10 ++--- frame/support/test/tests/pallet.rs | 11 ----- frame/support/test/tests/pallet_instance.rs | 2 - .../tests/pallet_ui/deprecated_store_attr.rs | 11 +++++ .../pallet_ui/deprecated_store_attr.stderr | 9 ++++ .../tests/pallet_ui/duplicate_call_attr.rs | 1 - .../pallet_ui/duplicate_call_attr.stderr | 4 +- .../pallet_ui/duplicate_storage_prefix.rs | 1 - .../pallet_ui/duplicate_storage_prefix.stderr | 32 +++++++------- .../pallet_ui/duplicate_store_attr.stderr | 4 +- .../pallet_ui/store_trait_leak_private.stderr | 12 ++++- frame/system/benches/bench.rs | 1 - frame/system/src/lib.rs | 1 - frame/timestamp/src/lib.rs | 1 - frame/tips/src/lib.rs | 1 - .../asset-tx-payment/src/lib.rs | 1 - frame/transaction-payment/src/lib.rs | 1 - frame/transaction-storage/src/lib.rs | 1 - frame/treasury/src/lib.rs | 1 - frame/uniques/src/lib.rs | 1 - frame/utility/src/lib.rs | 1 - frame/utility/src/tests.rs | 1 - frame/vesting/src/lib.rs | 1 - frame/whitelist/src/lib.rs | 1 - test-utils/runtime/src/system.rs | 1 - utils/frame/rpc/support/src/lib.rs | 1 - 89 files changed, 119 insertions(+), 168 deletions(-) create mode 100644 frame/support/test/tests/pallet_ui/deprecated_store_attr.rs create mode 100644 frame/support/test/tests/pallet_ui/deprecated_store_attr.stderr diff --git a/bin/node-template/pallets/template/src/lib.rs b/bin/node-template/pallets/template/src/lib.rs index 4630e344a..9f17623db 100644 --- a/bin/node-template/pallets/template/src/lib.rs +++ b/bin/node-template/pallets/template/src/lib.rs @@ -20,7 +20,6 @@ pub mod pallet { use frame_system::pallet_prelude::*; #[pallet::pallet] - #[pallet::generate_store(pub(super) trait Store)] pub struct Pallet(_); /// Configure the pallet by specifying the parameters and types on which it depends. diff --git a/frame/alliance/src/lib.rs b/frame/alliance/src/lib.rs index c9232a1f2..6cc162f7d 100644 --- a/frame/alliance/src/lib.rs +++ b/frame/alliance/src/lib.rs @@ -222,7 +222,6 @@ pub mod pallet { use super::*; #[pallet::pallet] - #[pallet::generate_store(pub (super) trait Store)] #[pallet::storage_version(migration::STORAGE_VERSION)] pub struct Pallet(PhantomData<(T, I)>); diff --git a/frame/assets/src/lib.rs b/frame/assets/src/lib.rs index 662c20e00..bb0e8ce8b 100644 --- a/frame/assets/src/lib.rs +++ b/frame/assets/src/lib.rs @@ -197,7 +197,6 @@ pub mod pallet { const STORAGE_VERSION: StorageVersion = StorageVersion::new(1); #[pallet::pallet] - #[pallet::generate_store(pub(super) trait Store)] #[pallet::storage_version(STORAGE_VERSION)] pub struct Pallet(_); diff --git a/frame/atomic-swap/src/lib.rs b/frame/atomic-swap/src/lib.rs index 68d254420..ed89a8869 100644 --- a/frame/atomic-swap/src/lib.rs +++ b/frame/atomic-swap/src/lib.rs @@ -184,7 +184,6 @@ pub mod pallet { } #[pallet::pallet] - #[pallet::generate_store(pub(super) trait Store)] pub struct Pallet(PhantomData); #[pallet::storage] diff --git a/frame/authority-discovery/src/lib.rs b/frame/authority-discovery/src/lib.rs index e93deabeb..341646b67 100644 --- a/frame/authority-discovery/src/lib.rs +++ b/frame/authority-discovery/src/lib.rs @@ -38,7 +38,6 @@ pub mod pallet { use frame_support::pallet_prelude::*; #[pallet::pallet] - #[pallet::generate_store(pub(super) trait Store)] pub struct Pallet(_); #[pallet::config] diff --git a/frame/authorship/src/lib.rs b/frame/authorship/src/lib.rs index cb1d777fa..4bb8ba587 100644 --- a/frame/authorship/src/lib.rs +++ b/frame/authorship/src/lib.rs @@ -49,7 +49,6 @@ pub mod pallet { } #[pallet::pallet] - #[pallet::generate_store(pub(super) trait Store)] pub struct Pallet(_); #[pallet::hooks] diff --git a/frame/babe/src/lib.rs b/frame/babe/src/lib.rs index 903f6249e..61284267b 100644 --- a/frame/babe/src/lib.rs +++ b/frame/babe/src/lib.rs @@ -114,7 +114,6 @@ pub mod pallet { /// The BABE Pallet #[pallet::pallet] - #[pallet::generate_store(pub(super) trait Store)] pub struct Pallet(_); #[pallet::config] diff --git a/frame/bags-list/src/lib.rs b/frame/bags-list/src/lib.rs index b65b65c39..87eb2d1b3 100644 --- a/frame/bags-list/src/lib.rs +++ b/frame/bags-list/src/lib.rs @@ -99,7 +99,6 @@ pub mod pallet { use frame_system::pallet_prelude::*; #[pallet::pallet] - #[pallet::generate_store(pub(crate) trait Store)] pub struct Pallet(_); #[pallet::config] diff --git a/frame/balances/src/lib.rs b/frame/balances/src/lib.rs index f02c7f9c0..ecd7e171e 100644 --- a/frame/balances/src/lib.rs +++ b/frame/balances/src/lib.rs @@ -253,7 +253,6 @@ pub mod pallet { frame_support::traits::StorageVersion::new(1); #[pallet::pallet] - #[pallet::generate_store(pub(super) trait Store)] #[pallet::storage_version(STORAGE_VERSION)] pub struct Pallet(PhantomData<(T, I)>); diff --git a/frame/beefy-mmr/src/lib.rs b/frame/beefy-mmr/src/lib.rs index ba52be4f8..b9d4bdfd8 100644 --- a/frame/beefy-mmr/src/lib.rs +++ b/frame/beefy-mmr/src/lib.rs @@ -95,7 +95,6 @@ pub mod pallet { /// BEEFY-MMR pallet. #[pallet::pallet] - #[pallet::generate_store(pub(super) trait Store)] pub struct Pallet(_); /// The module's configuration trait. diff --git a/frame/benchmarking/pov/src/lib.rs b/frame/benchmarking/pov/src/lib.rs index b66b5e417..dccfe72d3 100644 --- a/frame/benchmarking/pov/src/lib.rs +++ b/frame/benchmarking/pov/src/lib.rs @@ -32,7 +32,6 @@ pub mod pallet { use sp_std::prelude::*; #[pallet::pallet] - #[pallet::generate_store(pub(super) trait Store)] pub struct Pallet(PhantomData); #[pallet::config] diff --git a/frame/benchmarking/src/tests.rs b/frame/benchmarking/src/tests.rs index a3e19cde3..453e8e125 100644 --- a/frame/benchmarking/src/tests.rs +++ b/frame/benchmarking/src/tests.rs @@ -35,7 +35,6 @@ mod pallet_test { use frame_system::pallet_prelude::*; #[pallet::pallet] - #[pallet::generate_store(pub(super) trait Store)] pub struct Pallet(PhantomData); #[pallet::config] diff --git a/frame/benchmarking/src/tests_instance.rs b/frame/benchmarking/src/tests_instance.rs index 49f9786d7..d49d7cc81 100644 --- a/frame/benchmarking/src/tests_instance.rs +++ b/frame/benchmarking/src/tests_instance.rs @@ -34,7 +34,6 @@ mod pallet_test { use frame_system::pallet_prelude::*; #[pallet::pallet] - #[pallet::generate_store(pub(super) trait Store)] pub struct Pallet(PhantomData<(T, I)>); pub trait OtherConfig { diff --git a/frame/bounties/src/lib.rs b/frame/bounties/src/lib.rs index 325347502..e7a3e2053 100644 --- a/frame/bounties/src/lib.rs +++ b/frame/bounties/src/lib.rs @@ -188,7 +188,6 @@ pub mod pallet { use super::*; #[pallet::pallet] - #[pallet::generate_store(pub(super) trait Store)] pub struct Pallet(_); #[pallet::config] diff --git a/frame/child-bounties/src/lib.rs b/frame/child-bounties/src/lib.rs index 3046d9700..094b41822 100644 --- a/frame/child-bounties/src/lib.rs +++ b/frame/child-bounties/src/lib.rs @@ -130,7 +130,6 @@ pub mod pallet { use super::*; #[pallet::pallet] - #[pallet::generate_store(pub(super) trait Store)] pub struct Pallet(_); #[pallet::config] diff --git a/frame/collective/src/lib.rs b/frame/collective/src/lib.rs index ae5b22559..c58965d2c 100644 --- a/frame/collective/src/lib.rs +++ b/frame/collective/src/lib.rs @@ -175,7 +175,6 @@ pub mod pallet { const STORAGE_VERSION: StorageVersion = StorageVersion::new(4); #[pallet::pallet] - #[pallet::generate_store(pub(super) trait Store)] #[pallet::storage_version(STORAGE_VERSION)] #[pallet::without_storage_info] pub struct Pallet(PhantomData<(T, I)>); diff --git a/frame/collective/src/tests.rs b/frame/collective/src/tests.rs index 2d9b31a53..79f6cc27f 100644 --- a/frame/collective/src/tests.rs +++ b/frame/collective/src/tests.rs @@ -57,7 +57,6 @@ mod mock_democracy { use frame_system::pallet_prelude::*; #[pallet::pallet] - #[pallet::generate_store(pub(super) trait Store)] pub struct Pallet(_); #[pallet::config] diff --git a/frame/conviction-voting/src/lib.rs b/frame/conviction-voting/src/lib.rs index 6c15c6cf2..f28953c4a 100644 --- a/frame/conviction-voting/src/lib.rs +++ b/frame/conviction-voting/src/lib.rs @@ -92,7 +92,6 @@ pub mod pallet { use frame_system::pallet_prelude::*; #[pallet::pallet] - #[pallet::generate_store(pub(super) trait Store)] pub struct Pallet(_); #[pallet::config] diff --git a/frame/core-fellowship/src/lib.rs b/frame/core-fellowship/src/lib.rs index 6ba60dbaf..93d230244 100644 --- a/frame/core-fellowship/src/lib.rs +++ b/frame/core-fellowship/src/lib.rs @@ -148,7 +148,6 @@ pub mod pallet { const RANK_COUNT: usize = 9; #[pallet::pallet] - #[pallet::generate_store(pub(super) trait Store)] pub struct Pallet(PhantomData<(T, I)>); #[pallet::config] diff --git a/frame/democracy/src/lib.rs b/frame/democracy/src/lib.rs index e1e111e96..a3d7f103a 100644 --- a/frame/democracy/src/lib.rs +++ b/frame/democracy/src/lib.rs @@ -217,7 +217,6 @@ pub mod pallet { const STORAGE_VERSION: StorageVersion = StorageVersion::new(1); #[pallet::pallet] - #[pallet::generate_store(pub(super) trait Store)] #[pallet::storage_version(STORAGE_VERSION)] pub struct Pallet(_); diff --git a/frame/elections-phragmen/src/lib.rs b/frame/elections-phragmen/src/lib.rs index fa63762c5..e3eed6ec4 100644 --- a/frame/elections-phragmen/src/lib.rs +++ b/frame/elections-phragmen/src/lib.rs @@ -187,7 +187,6 @@ pub mod pallet { const STORAGE_VERSION: StorageVersion = StorageVersion::new(4); #[pallet::pallet] - #[pallet::generate_store(pub(super) trait Store)] #[pallet::storage_version(STORAGE_VERSION)] #[pallet::without_storage_info] pub struct Pallet(PhantomData); diff --git a/frame/examples/basic/src/lib.rs b/frame/examples/basic/src/lib.rs index a75a0e17c..6665c3b78 100644 --- a/frame/examples/basic/src/lib.rs +++ b/frame/examples/basic/src/lib.rs @@ -379,7 +379,6 @@ pub mod pallet { // Simple declaration of the `Pallet` type. It is placeholder we use to implement traits and // method. #[pallet::pallet] - #[pallet::generate_store(pub(super) trait Store)] pub struct Pallet(_); // Pallet implements [`Hooks`] trait to define some logic to execute in some context. diff --git a/frame/examples/offchain-worker/src/lib.rs b/frame/examples/offchain-worker/src/lib.rs index c9c72f8e8..418ad5d13 100644 --- a/frame/examples/offchain-worker/src/lib.rs +++ b/frame/examples/offchain-worker/src/lib.rs @@ -158,7 +158,6 @@ pub mod pallet { } #[pallet::pallet] - #[pallet::generate_store(pub(super) trait Store)] pub struct Pallet(_); #[pallet::hooks] diff --git a/frame/executive/src/lib.rs b/frame/executive/src/lib.rs index b3a04c4fe..7a8de4314 100644 --- a/frame/executive/src/lib.rs +++ b/frame/executive/src/lib.rs @@ -706,7 +706,6 @@ mod tests { use frame_system::pallet_prelude::*; #[pallet::pallet] - #[pallet::generate_store(pub(super) trait Store)] pub struct Pallet(_); #[pallet::config] diff --git a/frame/grandpa/src/lib.rs b/frame/grandpa/src/lib.rs index 538cd365b..f01c25e49 100644 --- a/frame/grandpa/src/lib.rs +++ b/frame/grandpa/src/lib.rs @@ -77,7 +77,6 @@ pub mod pallet { const STORAGE_VERSION: StorageVersion = StorageVersion::new(4); #[pallet::pallet] - #[pallet::generate_store(pub(super) trait Store)] #[pallet::storage_version(STORAGE_VERSION)] pub struct Pallet(_); diff --git a/frame/identity/src/lib.rs b/frame/identity/src/lib.rs index d1b764591..f192ee2b4 100644 --- a/frame/identity/src/lib.rs +++ b/frame/identity/src/lib.rs @@ -152,7 +152,6 @@ pub mod pallet { } #[pallet::pallet] - #[pallet::generate_store(pub(super) trait Store)] pub struct Pallet(_); /// Information that is pertinent to identify the entity behind an account. diff --git a/frame/im-online/src/lib.rs b/frame/im-online/src/lib.rs index 39762945c..bc1e541ce 100644 --- a/frame/im-online/src/lib.rs +++ b/frame/im-online/src/lib.rs @@ -313,7 +313,6 @@ pub mod pallet { use frame_system::pallet_prelude::*; #[pallet::pallet] - #[pallet::generate_store(pub(super) trait Store)] pub struct Pallet(_); #[pallet::config] diff --git a/frame/indices/src/lib.rs b/frame/indices/src/lib.rs index 82dc48f7b..74f86abb5 100644 --- a/frame/indices/src/lib.rs +++ b/frame/indices/src/lib.rs @@ -75,7 +75,6 @@ pub mod pallet { } #[pallet::pallet] - #[pallet::generate_store(pub(super) trait Store)] pub struct Pallet(PhantomData); #[pallet::call] diff --git a/frame/insecure-randomness-collective-flip/src/lib.rs b/frame/insecure-randomness-collective-flip/src/lib.rs index c73419efe..ad39c4c4f 100644 --- a/frame/insecure-randomness-collective-flip/src/lib.rs +++ b/frame/insecure-randomness-collective-flip/src/lib.rs @@ -17,7 +17,7 @@ //! # DO NOT USE IN PRODUCTION //! -//! The produced values do not fulfill the cryptographic requirements for random numbers. +//! The produced values do not fulfill the cryptographic requirements for random numbers. //! Should not be used for high-stake production use-cases. //! //! # Randomness Pallet @@ -51,7 +51,6 @@ //! use frame_system::pallet_prelude::*; //! //! #[pallet::pallet] -//! #[pallet::generate_store(pub(super) trait Store)] //! pub struct Pallet(_); //! //! #[pallet::config] @@ -94,7 +93,6 @@ pub mod pallet { use frame_system::pallet_prelude::*; #[pallet::pallet] - #[pallet::generate_store(pub(super) trait Store)] pub struct Pallet(_); #[pallet::config] diff --git a/frame/lottery/src/lib.rs b/frame/lottery/src/lib.rs index bd7ca40fb..178f221a8 100644 --- a/frame/lottery/src/lib.rs +++ b/frame/lottery/src/lib.rs @@ -123,7 +123,6 @@ pub mod pallet { use frame_system::pallet_prelude::*; #[pallet::pallet] - #[pallet::generate_store(pub(super) trait Store)] pub struct Pallet(_); /// The pallet's config trait. diff --git a/frame/membership/src/lib.rs b/frame/membership/src/lib.rs index 04263b93e..0be33a956 100644 --- a/frame/membership/src/lib.rs +++ b/frame/membership/src/lib.rs @@ -50,7 +50,6 @@ pub mod pallet { const STORAGE_VERSION: StorageVersion = StorageVersion::new(4); #[pallet::pallet] - #[pallet::generate_store(pub(super) trait Store)] #[pallet::storage_version(STORAGE_VERSION)] pub struct Pallet(PhantomData<(T, I)>); diff --git a/frame/merkle-mountain-range/src/lib.rs b/frame/merkle-mountain-range/src/lib.rs index 0b4c2f8d9..4ef833e6c 100644 --- a/frame/merkle-mountain-range/src/lib.rs +++ b/frame/merkle-mountain-range/src/lib.rs @@ -121,7 +121,6 @@ pub mod pallet { use frame_system::pallet_prelude::*; #[pallet::pallet] - #[pallet::generate_store(pub(super) trait Store)] pub struct Pallet(PhantomData<(T, I)>); /// This pallet's configuration trait diff --git a/frame/message-queue/src/lib.rs b/frame/message-queue/src/lib.rs index bed131e5f..c8e197610 100644 --- a/frame/message-queue/src/lib.rs +++ b/frame/message-queue/src/lib.rs @@ -39,7 +39,7 @@ //! which queue it will be stored. Messages are stored by being appended to the last [`Page`] of a //! book. Each book keeps track of its pages by indexing `Pages`. The `ReadyRing` contains all //! queues which hold at least one unprocessed message and are thereby *ready* to be serviced. The -//! `ServiceHead` indicates which *ready* queue is the next to be serviced. +//! `ServiceHead` indicates which *ready* queue is the next to be serviced. //! The pallet implements [`frame_support::traits::EnqueueMessage`], //! [`frame_support::traits::ServiceQueues`] and has [`frame_support::traits::ProcessMessage`] and //! [`OnQueueChanged`] hooks to communicate with the outside world. @@ -438,7 +438,6 @@ pub mod pallet { use super::*; #[pallet::pallet] - #[pallet::generate_store(pub(super) trait Store)] pub struct Pallet(_); /// The module configuration trait. diff --git a/frame/multisig/src/lib.rs b/frame/multisig/src/lib.rs index fb10838ce..64058be9c 100644 --- a/frame/multisig/src/lib.rs +++ b/frame/multisig/src/lib.rs @@ -172,7 +172,6 @@ pub mod pallet { const STORAGE_VERSION: StorageVersion = StorageVersion::new(1); #[pallet::pallet] - #[pallet::generate_store(pub(super) trait Store)] #[pallet::storage_version(STORAGE_VERSION)] pub struct Pallet(_); diff --git a/frame/nfts/src/lib.rs b/frame/nfts/src/lib.rs index 4d9547659..254fa7596 100644 --- a/frame/nfts/src/lib.rs +++ b/frame/nfts/src/lib.rs @@ -75,7 +75,6 @@ pub mod pallet { const STORAGE_VERSION: StorageVersion = StorageVersion::new(1); #[pallet::pallet] - #[pallet::generate_store(pub(super) trait Store)] #[pallet::storage_version(STORAGE_VERSION)] pub struct Pallet(PhantomData<(T, I)>); diff --git a/frame/nicks/src/lib.rs b/frame/nicks/src/lib.rs index b457cc299..b74d2e6ee 100644 --- a/frame/nicks/src/lib.rs +++ b/frame/nicks/src/lib.rs @@ -114,7 +114,6 @@ pub mod pallet { StorageMap<_, Twox64Concat, T::AccountId, (BoundedVec, BalanceOf)>; #[pallet::pallet] - #[pallet::generate_store(pub(super) trait Store)] pub struct Pallet(_); #[pallet::call] diff --git a/frame/nis/src/lib.rs b/frame/nis/src/lib.rs index 9cae5e2aa..6fd9c60a8 100644 --- a/frame/nis/src/lib.rs +++ b/frame/nis/src/lib.rs @@ -310,7 +310,6 @@ pub mod pallet { } #[pallet::pallet] - #[pallet::generate_store(pub(super) trait Store)] pub struct Pallet(_); /// A single bid, an item of a *queue* in `Queues`. diff --git a/frame/node-authorization/src/lib.rs b/frame/node-authorization/src/lib.rs index d91a769b3..eaeda3cad 100644 --- a/frame/node-authorization/src/lib.rs +++ b/frame/node-authorization/src/lib.rs @@ -59,7 +59,6 @@ pub mod pallet { use frame_system::pallet_prelude::*; #[pallet::pallet] - #[pallet::generate_store(pub(super) trait Store)] #[pallet::without_storage_info] pub struct Pallet(_); diff --git a/frame/nomination-pools/src/lib.rs b/frame/nomination-pools/src/lib.rs index b296eb048..c1dda69ab 100644 --- a/frame/nomination-pools/src/lib.rs +++ b/frame/nomination-pools/src/lib.rs @@ -1214,7 +1214,6 @@ pub mod pallet { const STORAGE_VERSION: StorageVersion = StorageVersion::new(3); #[pallet::pallet] - #[pallet::generate_store(pub(crate) trait Store)] #[pallet::storage_version(STORAGE_VERSION)] pub struct Pallet(_); diff --git a/frame/offences/src/lib.rs b/frame/offences/src/lib.rs index 8ac1ca0bd..c89d93c12 100644 --- a/frame/offences/src/lib.rs +++ b/frame/offences/src/lib.rs @@ -49,7 +49,6 @@ pub mod pallet { use frame_support::pallet_prelude::*; #[pallet::pallet] - #[pallet::generate_store(pub(super) trait Store)] #[pallet::without_storage_info] pub struct Pallet(_); diff --git a/frame/preimage/src/lib.rs b/frame/preimage/src/lib.rs index 74a3176b1..60208424d 100644 --- a/frame/preimage/src/lib.rs +++ b/frame/preimage/src/lib.rs @@ -107,7 +107,6 @@ pub mod pallet { } #[pallet::pallet] - #[pallet::generate_store(pub(super) trait Store)] #[pallet::storage_version(STORAGE_VERSION)] pub struct Pallet(PhantomData); diff --git a/frame/proxy/src/lib.rs b/frame/proxy/src/lib.rs index c26a53d1a..023d02535 100644 --- a/frame/proxy/src/lib.rs +++ b/frame/proxy/src/lib.rs @@ -102,7 +102,6 @@ pub mod pallet { use frame_system::pallet_prelude::*; #[pallet::pallet] - #[pallet::generate_store(pub(super) trait Store)] pub struct Pallet(_); /// Configuration trait. diff --git a/frame/ranked-collective/src/lib.rs b/frame/ranked-collective/src/lib.rs index 8ed1bbd2e..288fd78d6 100644 --- a/frame/ranked-collective/src/lib.rs +++ b/frame/ranked-collective/src/lib.rs @@ -321,7 +321,6 @@ pub mod pallet { use frame_system::pallet_prelude::*; #[pallet::pallet] - #[pallet::generate_store(pub(super) trait Store)] pub struct Pallet(PhantomData<(T, I)>); #[pallet::config] diff --git a/frame/recovery/src/lib.rs b/frame/recovery/src/lib.rs index b32d19d5d..d66b5725f 100644 --- a/frame/recovery/src/lib.rs +++ b/frame/recovery/src/lib.rs @@ -218,7 +218,6 @@ pub mod pallet { use sp_runtime::ArithmeticError; #[pallet::pallet] - #[pallet::generate_store(pub(super) trait Store)] pub struct Pallet(_); /// Configuration trait. diff --git a/frame/referenda/src/lib.rs b/frame/referenda/src/lib.rs index dccc40112..f69985006 100644 --- a/frame/referenda/src/lib.rs +++ b/frame/referenda/src/lib.rs @@ -145,7 +145,6 @@ pub mod pallet { const STORAGE_VERSION: StorageVersion = StorageVersion::new(1); #[pallet::pallet] - #[pallet::generate_store(pub(super) trait Store)] #[pallet::storage_version(STORAGE_VERSION)] pub struct Pallet(_); diff --git a/frame/root-offences/src/lib.rs b/frame/root-offences/src/lib.rs index 92f3e17b5..a93e7ff84 100644 --- a/frame/root-offences/src/lib.rs +++ b/frame/root-offences/src/lib.rs @@ -57,7 +57,6 @@ pub mod pallet { } #[pallet::pallet] - #[pallet::generate_store(pub(super) trait Store)] pub struct Pallet(_); #[pallet::event] diff --git a/frame/root-testing/src/lib.rs b/frame/root-testing/src/lib.rs index ad0e4cfea..e04c7bfa1 100644 --- a/frame/root-testing/src/lib.rs +++ b/frame/root-testing/src/lib.rs @@ -39,7 +39,6 @@ pub mod pallet { pub trait Config: frame_system::Config {} #[pallet::pallet] - #[pallet::generate_store(pub(super) trait Store)] pub struct Pallet(_); #[pallet::call] diff --git a/frame/salary/src/lib.rs b/frame/salary/src/lib.rs index 5a4ff8a66..6f9e63271 100644 --- a/frame/salary/src/lib.rs +++ b/frame/salary/src/lib.rs @@ -155,7 +155,6 @@ pub mod pallet { use frame_system::pallet_prelude::*; #[pallet::pallet] - #[pallet::generate_store(pub(super) trait Store)] pub struct Pallet(PhantomData<(T, I)>); #[pallet::config] diff --git a/frame/scheduler/src/lib.rs b/frame/scheduler/src/lib.rs index 95bfe8411..09bb35611 100644 --- a/frame/scheduler/src/lib.rs +++ b/frame/scheduler/src/lib.rs @@ -172,7 +172,6 @@ pub mod pallet { const STORAGE_VERSION: StorageVersion = StorageVersion::new(4); #[pallet::pallet] - #[pallet::generate_store(pub(super) trait Store)] #[pallet::storage_version(STORAGE_VERSION)] pub struct Pallet(_); diff --git a/frame/scheduler/src/mock.rs b/frame/scheduler/src/mock.rs index 914a97ab9..adb54fd78 100644 --- a/frame/scheduler/src/mock.rs +++ b/frame/scheduler/src/mock.rs @@ -50,7 +50,6 @@ pub mod logger { } #[pallet::pallet] - #[pallet::generate_store(pub(super) trait Store)] pub struct Pallet(PhantomData); #[pallet::hooks] diff --git a/frame/scored-pool/src/lib.rs b/frame/scored-pool/src/lib.rs index 16d59b660..336a69a79 100644 --- a/frame/scored-pool/src/lib.rs +++ b/frame/scored-pool/src/lib.rs @@ -135,7 +135,6 @@ pub mod pallet { use frame_system::pallet_prelude::*; #[pallet::pallet] - #[pallet::generate_store(pub(super) trait Store)] pub struct Pallet(_); #[pallet::config] diff --git a/frame/session/src/historical/mod.rs b/frame/session/src/historical/mod.rs index d98276794..f00a1c95e 100644 --- a/frame/session/src/historical/mod.rs +++ b/frame/session/src/historical/mod.rs @@ -62,7 +62,6 @@ pub mod pallet { const STORAGE_VERSION: StorageVersion = StorageVersion::new(1); #[pallet::pallet] - #[pallet::generate_store(pub(super) trait Store)] #[pallet::storage_version(STORAGE_VERSION)] pub struct Pallet(_); @@ -97,7 +96,7 @@ impl Pallet { /// Prune historical stored session roots up to (but not including) /// `up_to`. pub fn prune_up_to(up_to: SessionIndex) { - ::StoredRange::mutate(|range| { + StoredRange::::mutate(|range| { let (start, end) = match *range { Some(range) => range, None => return, // nothing to prune. @@ -109,7 +108,7 @@ impl Pallet { return // out of bounds. harmless. } - (start..up_to).for_each(::HistoricalSessions::remove); + (start..up_to).for_each(HistoricalSessions::::remove); let new_start = up_to; *range = if new_start == end { diff --git a/frame/session/src/lib.rs b/frame/session/src/lib.rs index 7d5dd6324..ed5f80e60 100644 --- a/frame/session/src/lib.rs +++ b/frame/session/src/lib.rs @@ -371,7 +371,6 @@ pub mod pallet { const STORAGE_VERSION: StorageVersion = StorageVersion::new(0); #[pallet::pallet] - #[pallet::generate_store(pub(super) trait Store)] #[pallet::storage_version(STORAGE_VERSION)] #[pallet::without_storage_info] pub struct Pallet(_); diff --git a/frame/society/src/lib.rs b/frame/society/src/lib.rs index f3207dabd..899b6005a 100644 --- a/frame/society/src/lib.rs +++ b/frame/society/src/lib.rs @@ -371,7 +371,6 @@ pub mod pallet { use super::*; #[pallet::pallet] - #[pallet::generate_store(pub(super) trait Store)] #[pallet::without_storage_info] pub struct Pallet(_); diff --git a/frame/staking/src/migrations.rs b/frame/staking/src/migrations.rs index 3c33ae2a3..23bcfa439 100644 --- a/frame/staking/src/migrations.rs +++ b/frame/staking/src/migrations.rs @@ -265,7 +265,7 @@ pub mod v10 { impl OnRuntimeUpgrade for MigrateToV10 { fn on_runtime_upgrade() -> frame_support::weights::Weight { if StorageVersion::::get() == ObsoleteReleases::V9_0_0 { - let pending_slashes = as Store>::UnappliedSlashes::iter().take(512); + let pending_slashes = UnappliedSlashes::::iter().take(512); for (era, slashes) in pending_slashes { for slash in slashes { // in the old slashing scheme, the slash era was the key at which we read diff --git a/frame/staking/src/pallet/impls.rs b/frame/staking/src/pallet/impls.rs index 78c9a51bc..760345e8d 100644 --- a/frame/staking/src/pallet/impls.rs +++ b/frame/staking/src/pallet/impls.rs @@ -682,7 +682,7 @@ impl Pallet { /// Apply previously-unapplied slashes on the beginning of a new era, after a delay. fn apply_unapplied_slashes(active_era: EraIndex) { - let era_slashes = ::UnappliedSlashes::take(&active_era); + let era_slashes = UnappliedSlashes::::take(&active_era); log!( debug, "found {} slashes scheduled to be executed in era {:?}", @@ -1369,7 +1369,7 @@ where active_era, slash_era + slash_defer_duration + 1, ); - ::UnappliedSlashes::mutate( + UnappliedSlashes::::mutate( slash_era.saturating_add(slash_defer_duration).saturating_add(One::one()), move |for_later| for_later.push(unapplied), ); diff --git a/frame/staking/src/pallet/mod.rs b/frame/staking/src/pallet/mod.rs index a03053887..d8f1855da 100644 --- a/frame/staking/src/pallet/mod.rs +++ b/frame/staking/src/pallet/mod.rs @@ -68,7 +68,6 @@ pub mod pallet { const STORAGE_VERSION: StorageVersion = StorageVersion::new(13); #[pallet::pallet] - #[pallet::generate_store(pub(crate) trait Store)] #[pallet::storage_version(STORAGE_VERSION)] pub struct Pallet(_); @@ -1452,7 +1451,7 @@ pub mod pallet { ensure!(!slash_indices.is_empty(), Error::::EmptyTargets); ensure!(is_sorted_and_unique(&slash_indices), Error::::NotSortedAndUnique); - let mut unapplied = ::UnappliedSlashes::get(&era); + let mut unapplied = UnappliedSlashes::::get(&era); let last_item = slash_indices[slash_indices.len() - 1]; ensure!((last_item as usize) < unapplied.len(), Error::::InvalidSlashIndex); @@ -1461,7 +1460,7 @@ pub mod pallet { unapplied.remove(index); } - ::UnappliedSlashes::insert(&era, &unapplied); + UnappliedSlashes::::insert(&era, &unapplied); Ok(()) } diff --git a/frame/staking/src/slashing.rs b/frame/staking/src/slashing.rs index f73e3a6f8..bb02da73f 100644 --- a/frame/staking/src/slashing.rs +++ b/frame/staking/src/slashing.rs @@ -50,8 +50,9 @@ //! Based on research at use crate::{ - BalanceOf, Config, Error, Exposure, NegativeImbalanceOf, Pallet, Perbill, SessionInterface, - Store, UnappliedSlash, + BalanceOf, Config, Error, Exposure, NegativeImbalanceOf, NominatorSlashInEra, + OffendingValidators, Pallet, Perbill, SessionInterface, SpanSlash, UnappliedSlash, + ValidatorSlashInEra, }; use codec::{Decode, Encode, MaxEncodedLen}; use frame_support::{ @@ -239,14 +240,13 @@ pub(crate) fn compute_slash( return None } - let prior_slash_p = - as Store>::ValidatorSlashInEra::get(¶ms.slash_era, params.stash) - .map_or(Zero::zero(), |(prior_slash_proportion, _)| prior_slash_proportion); + let prior_slash_p = ValidatorSlashInEra::::get(¶ms.slash_era, params.stash) + .map_or(Zero::zero(), |(prior_slash_proportion, _)| prior_slash_proportion); // compare slash proportions rather than slash values to avoid issues due to rounding // error. if params.slash.deconstruct() > prior_slash_p.deconstruct() { - as Store>::ValidatorSlashInEra::insert( + ValidatorSlashInEra::::insert( ¶ms.slash_era, params.stash, &(params.slash, own_slash), @@ -327,7 +327,7 @@ fn kick_out_if_recent(params: SlashParams) { /// If after adding the validator `OffendingValidatorsThreshold` is reached /// a new era will be forced. fn add_offending_validator(stash: &T::AccountId, disable: bool) { - as Store>::OffendingValidators::mutate(|offending| { + OffendingValidators::::mutate(|offending| { let validators = T::SessionInterface::validators(); let validator_index = match validators.iter().position(|i| i == stash) { Some(index) => index, @@ -388,10 +388,9 @@ fn slash_nominators( let own_slash_difference = own_slash_by_validator.saturating_sub(own_slash_prior); let mut era_slash = - as Store>::NominatorSlashInEra::get(¶ms.slash_era, stash) - .unwrap_or_else(Zero::zero); + NominatorSlashInEra::::get(¶ms.slash_era, stash).unwrap_or_else(Zero::zero); era_slash += own_slash_difference; - as Store>::NominatorSlashInEra::insert(¶ms.slash_era, stash, &era_slash); + NominatorSlashInEra::::insert(¶ms.slash_era, stash, &era_slash); era_slash }; @@ -445,9 +444,9 @@ fn fetch_spans<'a, T: Config + 'a>( slash_of: &'a mut BalanceOf, reward_proportion: Perbill, ) -> InspectingSpans<'a, T> { - let spans = as Store>::SlashingSpans::get(stash).unwrap_or_else(|| { + let spans = crate::SlashingSpans::::get(stash).unwrap_or_else(|| { let spans = SlashingSpans::new(window_start); - as Store>::SlashingSpans::insert(stash, &spans); + crate::SlashingSpans::::insert(stash, &spans); spans }); @@ -496,7 +495,7 @@ impl<'a, T: 'a + Config> InspectingSpans<'a, T> { ) -> Option { let target_span = self.era_span(slash_era)?; let span_slash_key = (self.stash.clone(), target_span.index); - let mut span_record = as Store>::SpanSlash::get(&span_slash_key); + let mut span_record = SpanSlash::::get(&span_slash_key); let mut changed = false; let reward = if span_record.slashed < slash { @@ -527,7 +526,7 @@ impl<'a, T: 'a + Config> InspectingSpans<'a, T> { if changed { self.dirty = true; - as Store>::SpanSlash::insert(&span_slash_key, &span_record); + SpanSlash::::insert(&span_slash_key, &span_record); } Some(target_span.index) @@ -543,20 +542,20 @@ impl<'a, T: 'a + Config> Drop for InspectingSpans<'a, T> { if let Some((start, end)) = self.spans.prune(self.window_start) { for span_index in start..end { - as Store>::SpanSlash::remove(&(self.stash.clone(), span_index)); + SpanSlash::::remove(&(self.stash.clone(), span_index)); } } - as Store>::SlashingSpans::insert(self.stash, &self.spans); + crate::SlashingSpans::::insert(self.stash, &self.spans); } } /// Clear slashing metadata for an obsolete era. pub(crate) fn clear_era_metadata(obsolete_era: EraIndex) { #[allow(deprecated)] - as Store>::ValidatorSlashInEra::remove_prefix(&obsolete_era, None); + ValidatorSlashInEra::::remove_prefix(&obsolete_era, None); #[allow(deprecated)] - as Store>::NominatorSlashInEra::remove_prefix(&obsolete_era, None); + NominatorSlashInEra::::remove_prefix(&obsolete_era, None); } /// Clear slashing metadata for a dead account. @@ -564,7 +563,7 @@ pub(crate) fn clear_stash_metadata( stash: &T::AccountId, num_slashing_spans: u32, ) -> DispatchResult { - let spans = match as Store>::SlashingSpans::get(stash) { + let spans = match crate::SlashingSpans::::get(stash) { None => return Ok(()), Some(s) => s, }; @@ -574,7 +573,7 @@ pub(crate) fn clear_stash_metadata( Error::::IncorrectSlashingSpans ); - as Store>::SlashingSpans::remove(stash); + crate::SlashingSpans::::remove(stash); // kill slashing-span metadata for account. // @@ -582,7 +581,7 @@ pub(crate) fn clear_stash_metadata( // in that case, they may re-bond, but it would count again as span 0. Further ancient // slashes would slash into this new bond, since metadata has now been cleared. for span in spans.iter() { - as Store>::SpanSlash::remove(&(stash.clone(), span.index)); + SpanSlash::::remove(&(stash.clone(), span.index)); } Ok(()) diff --git a/frame/staking/src/tests.rs b/frame/staking/src/tests.rs index 16bafae01..966eae8c1 100644 --- a/frame/staking/src/tests.rs +++ b/frame/staking/src/tests.rs @@ -512,10 +512,10 @@ fn no_candidate_emergency_condition() { // initial validators assert_eq_uvec!(validator_controllers(), vec![10, 20, 30, 40]); let prefs = ValidatorPrefs { commission: Perbill::one(), ..Default::default() }; - ::Validators::insert(11, prefs.clone()); + Validators::::insert(11, prefs.clone()); // set the minimum validator count. - ::MinimumValidatorCount::put(10); + MinimumValidatorCount::::put(10); // try to chill let res = Staking::chill(RuntimeOrigin::signed(10)); @@ -536,7 +536,7 @@ fn no_candidate_emergency_condition() { // changed) assert_eq_uvec!(validator_controllers(), vec![10, 20, 30, 40]); // The chill is still pending. - assert!(!::Validators::contains_key(11)); + assert!(!Validators::::contains_key(11)); // No new era is created. assert_eq!(current_era, CurrentEra::::get()); }); @@ -2432,7 +2432,7 @@ fn slash_in_old_span_does_not_deselect() { ); // the validator doesn't get chilled again - assert!(::Validators::iter().any(|(stash, _)| stash == 11)); + assert!(Validators::::iter().any(|(stash, _)| stash == 11)); // but we are still forcing a new era assert_eq!(Staking::force_era(), Forcing::ForceNew); @@ -2449,7 +2449,7 @@ fn slash_in_old_span_does_not_deselect() { ); // the validator doesn't get chilled again - assert!(::Validators::iter().any(|(stash, _)| stash == 11)); + assert!(Validators::::iter().any(|(stash, _)| stash == 11)); // but it's disabled assert!(is_disabled(10)); @@ -2648,8 +2648,8 @@ fn garbage_collection_after_slashing() { ); assert_eq!(Balances::free_balance(11), 2000 - 200); - assert!(::SlashingSpans::get(&11).is_some()); - assert_eq!(::SpanSlash::get(&(11, 0)).amount(), &200); + assert!(SlashingSpans::::get(&11).is_some()); + assert_eq!(SpanSlash::::get(&(11, 0)).amount(), &200); on_offence_now( &[OffenceDetails { @@ -2665,7 +2665,7 @@ fn garbage_collection_after_slashing() { assert_eq!(Balances::free_balance(11), 2); assert_eq!(Balances::total_balance(&11), 2); - let slashing_spans = ::SlashingSpans::get(&11).unwrap(); + let slashing_spans = SlashingSpans::::get(&11).unwrap(); assert_eq!(slashing_spans.iter().count(), 2); // reap_stash respects num_slashing_spans so that weight is accurate @@ -2675,8 +2675,8 @@ fn garbage_collection_after_slashing() { ); assert_ok!(Staking::reap_stash(RuntimeOrigin::signed(20), 11, 2)); - assert!(::SlashingSpans::get(&11).is_none()); - assert_eq!(::SpanSlash::get(&(11, 0)).amount(), &0); + assert!(SlashingSpans::::get(&11).is_none()); + assert_eq!(SpanSlash::::get(&(11, 0)).amount(), &0); }) } @@ -2702,19 +2702,19 @@ fn garbage_collection_on_window_pruning() { assert_eq!(Balances::free_balance(11), 900); assert_eq!(Balances::free_balance(101), 2000 - (nominated_value / 10)); - assert!(::ValidatorSlashInEra::get(&now, &11).is_some()); - assert!(::NominatorSlashInEra::get(&now, &101).is_some()); + assert!(ValidatorSlashInEra::::get(&now, &11).is_some()); + assert!(NominatorSlashInEra::::get(&now, &101).is_some()); // + 1 because we have to exit the bonding window. for era in (0..(BondingDuration::get() + 1)).map(|offset| offset + now + 1) { - assert!(::ValidatorSlashInEra::get(&now, &11).is_some()); - assert!(::NominatorSlashInEra::get(&now, &101).is_some()); + assert!(ValidatorSlashInEra::::get(&now, &11).is_some()); + assert!(NominatorSlashInEra::::get(&now, &101).is_some()); mock::start_active_era(era); } - assert!(::ValidatorSlashInEra::get(&now, &11).is_none()); - assert!(::NominatorSlashInEra::get(&now, &101).is_none()); + assert!(ValidatorSlashInEra::::get(&now, &11).is_none()); + assert!(NominatorSlashInEra::::get(&now, &101).is_none()); }) } @@ -2755,7 +2755,7 @@ fn slashing_nominators_by_span_max() { slashing::SlashingSpan { index: 0, start: 0, length: Some(4) }, ]; - let get_span = |account| ::SlashingSpans::get(&account).unwrap(); + let get_span = |account| SlashingSpans::::get(&account).unwrap(); assert_eq!(get_span(11).iter().collect::>(), expected_spans); @@ -2817,7 +2817,7 @@ fn slashes_are_summed_across_spans() { assert_eq!(Balances::free_balance(21), 2000); assert_eq!(Staking::slashable_balance_of(&21), 1000); - let get_span = |account| ::SlashingSpans::get(&account).unwrap(); + let get_span = |account| SlashingSpans::::get(&account).unwrap(); on_offence_now( &[OffenceDetails { @@ -3192,7 +3192,7 @@ fn remove_multi_deferred() { &[Perbill::from_percent(25)], ); - assert_eq!(::UnappliedSlashes::get(&4).len(), 5); + assert_eq!(UnappliedSlashes::::get(&4).len(), 5); // fails if list is not sorted assert_noop!( @@ -3212,7 +3212,7 @@ fn remove_multi_deferred() { assert_ok!(Staking::cancel_deferred_slash(RuntimeOrigin::root(), 4, vec![0, 2, 4])); - let slashes = ::UnappliedSlashes::get(&4); + let slashes = UnappliedSlashes::::get(&4); assert_eq!(slashes.len(), 2); assert_eq!(slashes[0].validator, 21); assert_eq!(slashes[1].validator, 42); @@ -3267,7 +3267,7 @@ fn slash_kicks_validators_not_nominators_and_disables_nominator_for_kicked_valid assert_eq!(Balances::free_balance(101), 2000 - nominator_slash_amount_11); // check that validator was chilled. - assert!(::Validators::iter().all(|(stash, _)| stash != 11)); + assert!(Validators::::iter().all(|(stash, _)| stash != 11)); // actually re-bond the slashed validator assert_ok!(Staking::validate(RuntimeOrigin::signed(10), Default::default())); @@ -3612,7 +3612,7 @@ fn zero_slash_keeps_nominators() { assert_eq!(Balances::free_balance(101), 2000); // 11 is still removed.. - assert!(::Validators::iter().all(|(stash, _)| stash != 11)); + assert!(Validators::::iter().all(|(stash, _)| stash != 11)); // but their nominations are kept. assert_eq!(Staking::nominators(101).unwrap().targets, vec![11, 21]); }); diff --git a/frame/state-trie-migration/src/lib.rs b/frame/state-trie-migration/src/lib.rs index 547dfa5cd..5385c6b5f 100644 --- a/frame/state-trie-migration/src/lib.rs +++ b/frame/state-trie-migration/src/lib.rs @@ -439,7 +439,6 @@ pub mod pallet { /// The outer Pallet struct. #[pallet::pallet] - #[pallet::generate_store(pub(crate) trait Store)] pub struct Pallet(_); /// Configurations of this pallet. diff --git a/frame/sudo/src/lib.rs b/frame/sudo/src/lib.rs index 4d5149926..47309833a 100644 --- a/frame/sudo/src/lib.rs +++ b/frame/sudo/src/lib.rs @@ -133,7 +133,6 @@ pub mod pallet { } #[pallet::pallet] - #[pallet::generate_store(pub(super) trait Store)] pub struct Pallet(PhantomData); #[pallet::call] diff --git a/frame/sudo/src/mock.rs b/frame/sudo/src/mock.rs index 59a5cb6cc..6d6043cfd 100644 --- a/frame/sudo/src/mock.rs +++ b/frame/sudo/src/mock.rs @@ -39,7 +39,6 @@ pub mod logger { } #[pallet::pallet] - #[pallet::generate_store(pub(super) trait Store)] pub struct Pallet(PhantomData); #[pallet::call] diff --git a/frame/support/procedural/src/pallet/expand/store_trait.rs b/frame/support/procedural/src/pallet/expand/store_trait.rs index 15a6a223e..251c88f08 100644 --- a/frame/support/procedural/src/pallet/expand/store_trait.rs +++ b/frame/support/procedural/src/pallet/expand/store_trait.rs @@ -22,7 +22,7 @@ use syn::spanned::Spanned; /// * generate Store trait with all storages, /// * implement Store trait for Pallet. pub fn expand_store_trait(def: &mut Def) -> proc_macro2::TokenStream { - let (trait_vis, trait_store) = + let (trait_vis, trait_store, attribute_span) = if let Some(store) = &def.pallet_struct.store { store } else { return Default::default() }; let type_impl_gen = &def.type_impl_generics(trait_store.span()); @@ -36,8 +36,19 @@ pub fn expand_store_trait(def: &mut Def) -> proc_macro2::TokenStream { let storage_names = &def.storages.iter().map(|storage| &storage.ident).collect::>(); let storage_cfg_attrs = &def.storages.iter().map(|storage| &storage.cfg_attrs).collect::>(); + let warnig_struct_name = syn::Ident::new("Store", *attribute_span); + let warning: syn::ItemStruct = syn::parse_quote!( + #[deprecated(note = r" + Use of `#[pallet::generate_store(pub(super) trait Store)]` will be removed soon. + Check https://github.com/paritytech/substrate/pull/13535 for more details.")] + struct #warnig_struct_name; + ); quote::quote_spanned!(trait_store.span() => + const _:() = { + #warning + const _: Option<#warnig_struct_name> = None; + }; #trait_vis trait #trait_store { #( #(#storage_cfg_attrs)* diff --git a/frame/support/procedural/src/pallet/parse/pallet_struct.rs b/frame/support/procedural/src/pallet/parse/pallet_struct.rs index 571298d5a..98312d065 100644 --- a/frame/support/procedural/src/pallet/parse/pallet_struct.rs +++ b/frame/support/procedural/src/pallet/parse/pallet_struct.rs @@ -38,7 +38,7 @@ pub struct PalletStructDef { /// The keyword Pallet used (contains span). pub pallet: keyword::Pallet, /// Whether the trait `Store` must be generated. - pub store: Option<(syn::Visibility, keyword::Store)>, + pub store: Option<(syn::Visibility, keyword::Store, proc_macro2::Span)>, /// The span of the pallet::pallet attribute. pub attr_span: proc_macro2::Span, /// Whether to specify the storages max encoded len when implementing `StorageInfoTrait`. @@ -78,13 +78,13 @@ impl syn::parse::Parse for PalletStructAttr { let lookahead = content.lookahead1(); if lookahead.peek(keyword::generate_store) { - let span = content.parse::()?.span(); - + content.parse::()?; let generate_content; syn::parenthesized!(generate_content in content); let vis = generate_content.parse::()?; generate_content.parse::()?; let keyword = generate_content.parse::()?; + let span = content.span(); Ok(Self::GenerateStore { vis, keyword, span }) } else if lookahead.peek(keyword::without_storage_info) { let span = content.parse::()?.span(); @@ -123,8 +123,8 @@ impl PalletStructDef { let struct_attrs: Vec = helper::take_item_pallet_attrs(&mut item.attrs)?; for attr in struct_attrs { match attr { - PalletStructAttr::GenerateStore { vis, keyword, .. } if store.is_none() => { - store = Some((vis, keyword)); + PalletStructAttr::GenerateStore { vis, keyword, span } if store.is_none() => { + store = Some((vis, keyword, span)); }, PalletStructAttr::WithoutStorageInfoTrait(span) if without_storage_info.is_none() => diff --git a/frame/support/test/tests/pallet.rs b/frame/support/test/tests/pallet.rs index c33d2c8c9..8e77cd025 100644 --- a/frame/support/test/tests/pallet.rs +++ b/frame/support/test/tests/pallet.rs @@ -158,7 +158,6 @@ pub mod pallet { } #[pallet::pallet] - #[pallet::generate_store(pub(crate) trait Store)] #[pallet::storage_version(STORAGE_VERSION)] pub struct Pallet(_); @@ -496,7 +495,6 @@ pub mod pallet2 { } #[pallet::pallet] - #[pallet::generate_store(pub(crate) trait Store)] #[pallet::without_storage_info] pub struct Pallet(_); @@ -946,15 +944,6 @@ fn validate_unsigned_expand() { assert_eq!(validity, ValidTransaction::default()); } -#[test] -fn trait_store_expand() { - TestExternalities::default().execute_with(|| { - as pallet::Store>::Value::get(); - as pallet::Store>::Map::get(1); - as pallet::Store>::DoubleMap::get(1, 2); - }) -} - #[test] fn pallet_expand_deposit_event() { TestExternalities::default().execute_with(|| { diff --git a/frame/support/test/tests/pallet_instance.rs b/frame/support/test/tests/pallet_instance.rs index 5beb1480a..517f920c5 100644 --- a/frame/support/test/tests/pallet_instance.rs +++ b/frame/support/test/tests/pallet_instance.rs @@ -46,7 +46,6 @@ pub mod pallet { } #[pallet::pallet] - #[pallet::generate_store(pub(crate) trait Store)] pub struct Pallet(PhantomData<(T, I)>); #[pallet::hooks] @@ -260,7 +259,6 @@ pub mod pallet2 { } #[pallet::pallet] - #[pallet::generate_store(pub(crate) trait Store)] pub struct Pallet(PhantomData<(T, I)>); #[pallet::event] diff --git a/frame/support/test/tests/pallet_ui/deprecated_store_attr.rs b/frame/support/test/tests/pallet_ui/deprecated_store_attr.rs new file mode 100644 index 000000000..0799a3fce --- /dev/null +++ b/frame/support/test/tests/pallet_ui/deprecated_store_attr.rs @@ -0,0 +1,11 @@ +#[frame_support::pallet] +mod pallet { + #[pallet::config] + pub trait Config: frame_system::Config {} + + #[pallet::pallet] + #[pallet::generate_store(trait Store)] + pub struct Pallet(core::marker::PhantomData); +} + +fn main() {} diff --git a/frame/support/test/tests/pallet_ui/deprecated_store_attr.stderr b/frame/support/test/tests/pallet_ui/deprecated_store_attr.stderr new file mode 100644 index 000000000..bbc4743fc --- /dev/null +++ b/frame/support/test/tests/pallet_ui/deprecated_store_attr.stderr @@ -0,0 +1,9 @@ +error: use of deprecated struct `pallet::_::Store`: + Use of `#[pallet::generate_store(pub(super) trait Store)]` will be removed soon. + Check https://github.com/paritytech/substrate/pull/13535 for more details. + --> tests/pallet_ui/deprecated_store_attr.rs:7:3 + | +7 | #[pallet::generate_store(trait Store)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: `-D deprecated` implied by `-D warnings` diff --git a/frame/support/test/tests/pallet_ui/duplicate_call_attr.rs b/frame/support/test/tests/pallet_ui/duplicate_call_attr.rs index b8a32a0bd..6d781a19e 100644 --- a/frame/support/test/tests/pallet_ui/duplicate_call_attr.rs +++ b/frame/support/test/tests/pallet_ui/duplicate_call_attr.rs @@ -8,7 +8,6 @@ mod pallet { pub trait Config: frame_system::Config {} #[pallet::pallet] - #[pallet::generate_store(trait Store)] pub struct Pallet(core::marker::PhantomData); #[pallet::hooks] diff --git a/frame/support/test/tests/pallet_ui/duplicate_call_attr.stderr b/frame/support/test/tests/pallet_ui/duplicate_call_attr.stderr index c2956717b..2b03f4102 100644 --- a/frame/support/test/tests/pallet_ui/duplicate_call_attr.stderr +++ b/frame/support/test/tests/pallet_ui/duplicate_call_attr.stderr @@ -1,5 +1,5 @@ error: Invalid duplicated attribute - --> $DIR/duplicate_call_attr.rs:23:12 + --> $DIR/duplicate_call_attr.rs:22:12 | -23 | #[pallet::call] +22 | #[pallet::call] | ^^^^ diff --git a/frame/support/test/tests/pallet_ui/duplicate_storage_prefix.rs b/frame/support/test/tests/pallet_ui/duplicate_storage_prefix.rs index 5e99c8405..543c15bd0 100644 --- a/frame/support/test/tests/pallet_ui/duplicate_storage_prefix.rs +++ b/frame/support/test/tests/pallet_ui/duplicate_storage_prefix.rs @@ -6,7 +6,6 @@ mod pallet { pub trait Config: frame_system::Config {} #[pallet::pallet] - #[pallet::generate_store(trait Store)] pub struct Pallet(core::marker::PhantomData); #[pallet::storage] diff --git a/frame/support/test/tests/pallet_ui/duplicate_storage_prefix.stderr b/frame/support/test/tests/pallet_ui/duplicate_storage_prefix.stderr index 716888c9d..75297dc5a 100644 --- a/frame/support/test/tests/pallet_ui/duplicate_storage_prefix.stderr +++ b/frame/support/test/tests/pallet_ui/duplicate_storage_prefix.stderr @@ -1,47 +1,47 @@ error: Duplicate storage prefixes found for `Foo` - --> $DIR/duplicate_storage_prefix.rs:16:29 + --> $DIR/duplicate_storage_prefix.rs:15:29 | -16 | #[pallet::storage_prefix = "Foo"] +15 | #[pallet::storage_prefix = "Foo"] | ^^^^^ error: Duplicate storage prefixes found for `Foo` - --> $DIR/duplicate_storage_prefix.rs:13:7 + --> $DIR/duplicate_storage_prefix.rs:12:7 | -13 | type Foo = StorageValue<_, u8>; +12 | type Foo = StorageValue<_, u8>; | ^^^ error: Duplicate storage prefixes found for `CounterForBar`, used for counter associated to counted storage map - --> $DIR/duplicate_storage_prefix.rs:23:7 + --> $DIR/duplicate_storage_prefix.rs:22:7 | -23 | type Bar = CountedStorageMap<_, Twox64Concat, u16, u16>; +22 | type Bar = CountedStorageMap<_, Twox64Concat, u16, u16>; | ^^^ error: Duplicate storage prefixes found for `CounterForBar` - --> $DIR/duplicate_storage_prefix.rs:20:7 + --> $DIR/duplicate_storage_prefix.rs:19:7 | -20 | type CounterForBar = StorageValue<_, u16>; +19 | type CounterForBar = StorageValue<_, u16>; | ^^^^^^^^^^^^^ error[E0412]: cannot find type `_GeneratedPrefixForStorageFoo` in this scope - --> $DIR/duplicate_storage_prefix.rs:13:7 + --> $DIR/duplicate_storage_prefix.rs:12:7 | -13 | type Foo = StorageValue<_, u8>; +12 | type Foo = StorageValue<_, u8>; | ^^^ not found in this scope error[E0412]: cannot find type `_GeneratedPrefixForStorageNotFoo` in this scope - --> $DIR/duplicate_storage_prefix.rs:17:7 + --> $DIR/duplicate_storage_prefix.rs:16:7 | -17 | type NotFoo = StorageValue<_, u16>; +16 | type NotFoo = StorageValue<_, u16>; | ^^^^^^ not found in this scope error[E0412]: cannot find type `_GeneratedPrefixForStorageCounterForBar` in this scope - --> $DIR/duplicate_storage_prefix.rs:20:7 + --> $DIR/duplicate_storage_prefix.rs:19:7 | -20 | type CounterForBar = StorageValue<_, u16>; +19 | type CounterForBar = StorageValue<_, u16>; | ^^^^^^^^^^^^^ not found in this scope error[E0412]: cannot find type `_GeneratedPrefixForStorageBar` in this scope - --> $DIR/duplicate_storage_prefix.rs:23:7 + --> $DIR/duplicate_storage_prefix.rs:22:7 | -23 | type Bar = CountedStorageMap<_, Twox64Concat, u16, u16>; +22 | type Bar = CountedStorageMap<_, Twox64Concat, u16, u16>; | ^^^ not found in this scope diff --git a/frame/support/test/tests/pallet_ui/duplicate_store_attr.stderr b/frame/support/test/tests/pallet_ui/duplicate_store_attr.stderr index 232144b8d..1c13ee17e 100644 --- a/frame/support/test/tests/pallet_ui/duplicate_store_attr.stderr +++ b/frame/support/test/tests/pallet_ui/duplicate_store_attr.stderr @@ -1,5 +1,5 @@ error: Unexpected duplicated attribute - --> $DIR/duplicate_store_attr.rs:12:12 + --> tests/pallet_ui/duplicate_store_attr.rs:12:3 | 12 | #[pallet::generate_store(trait Store)] - | ^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/frame/support/test/tests/pallet_ui/store_trait_leak_private.stderr b/frame/support/test/tests/pallet_ui/store_trait_leak_private.stderr index d8c62faa3..24fda4ff1 100644 --- a/frame/support/test/tests/pallet_ui/store_trait_leak_private.stderr +++ b/frame/support/test/tests/pallet_ui/store_trait_leak_private.stderr @@ -1,5 +1,15 @@ +error: use of deprecated struct `pallet::_::Store`: + Use of `#[pallet::generate_store(pub(super) trait Store)]` will be removed soon. + Check https://github.com/paritytech/substrate/pull/13535 for more details. + --> tests/pallet_ui/store_trait_leak_private.rs:11:3 + | +11 | #[pallet::generate_store(pub trait Store)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: `-D deprecated` implied by `-D warnings` + error[E0446]: private type `_GeneratedPrefixForStorageFoo` in public interface - --> $DIR/store_trait_leak_private.rs:11:37 + --> tests/pallet_ui/store_trait_leak_private.rs:11:37 | 11 | #[pallet::generate_store(pub trait Store)] | ^^^^^ can't leak private type diff --git a/frame/system/benches/bench.rs b/frame/system/benches/bench.rs index d2b579814..e2fed3e51 100644 --- a/frame/system/benches/bench.rs +++ b/frame/system/benches/bench.rs @@ -29,7 +29,6 @@ mod module { use frame_support::pallet_prelude::*; #[pallet::pallet] - #[pallet::generate_store(pub(super) trait Store)] pub struct Pallet(_); #[pallet::config] diff --git a/frame/system/src/lib.rs b/frame/system/src/lib.rs index 0ec808d4c..faa1ee5d6 100644 --- a/frame/system/src/lib.rs +++ b/frame/system/src/lib.rs @@ -357,7 +357,6 @@ pub mod pallet { } #[pallet::pallet] - #[pallet::generate_store(pub (super) trait Store)] pub struct Pallet(_); #[pallet::hooks] diff --git a/frame/timestamp/src/lib.rs b/frame/timestamp/src/lib.rs index 93af4447d..192c81502 100644 --- a/frame/timestamp/src/lib.rs +++ b/frame/timestamp/src/lib.rs @@ -151,7 +151,6 @@ pub mod pallet { } #[pallet::pallet] - #[pallet::generate_store(pub(super) trait Store)] pub struct Pallet(PhantomData); /// Current time for the current block. diff --git a/frame/tips/src/lib.rs b/frame/tips/src/lib.rs index d82906563..970e2ac15 100644 --- a/frame/tips/src/lib.rs +++ b/frame/tips/src/lib.rs @@ -121,7 +121,6 @@ pub mod pallet { const STORAGE_VERSION: StorageVersion = StorageVersion::new(4); #[pallet::pallet] - #[pallet::generate_store(pub(super) trait Store)] #[pallet::storage_version(STORAGE_VERSION)] #[pallet::without_storage_info] pub struct Pallet(_); diff --git a/frame/transaction-payment/asset-tx-payment/src/lib.rs b/frame/transaction-payment/asset-tx-payment/src/lib.rs index ec0bfcd4f..b9cd6ef99 100644 --- a/frame/transaction-payment/asset-tx-payment/src/lib.rs +++ b/frame/transaction-payment/asset-tx-payment/src/lib.rs @@ -124,7 +124,6 @@ pub mod pallet { } #[pallet::pallet] - #[pallet::generate_store(pub(super) trait Store)] pub struct Pallet(_); #[pallet::event] diff --git a/frame/transaction-payment/src/lib.rs b/frame/transaction-payment/src/lib.rs index 42798c62f..83ff69428 100644 --- a/frame/transaction-payment/src/lib.rs +++ b/frame/transaction-payment/src/lib.rs @@ -295,7 +295,6 @@ pub mod pallet { use frame_system::pallet_prelude::*; #[pallet::pallet] - #[pallet::generate_store(pub(super) trait Store)] pub struct Pallet(_); #[pallet::config] diff --git a/frame/transaction-storage/src/lib.rs b/frame/transaction-storage/src/lib.rs index c4b1a622f..59662ee86 100644 --- a/frame/transaction-storage/src/lib.rs +++ b/frame/transaction-storage/src/lib.rs @@ -141,7 +141,6 @@ pub mod pallet { } #[pallet::pallet] - #[pallet::generate_store(pub(super) trait Store)] pub struct Pallet(_); #[pallet::hooks] diff --git a/frame/treasury/src/lib.rs b/frame/treasury/src/lib.rs index 04e989373..450aee51f 100644 --- a/frame/treasury/src/lib.rs +++ b/frame/treasury/src/lib.rs @@ -140,7 +140,6 @@ pub mod pallet { use frame_system::pallet_prelude::*; #[pallet::pallet] - #[pallet::generate_store(pub(super) trait Store)] pub struct Pallet(PhantomData<(T, I)>); #[pallet::config] diff --git a/frame/uniques/src/lib.rs b/frame/uniques/src/lib.rs index 587f1ae4b..fd94bd3a9 100644 --- a/frame/uniques/src/lib.rs +++ b/frame/uniques/src/lib.rs @@ -71,7 +71,6 @@ pub mod pallet { use frame_system::pallet_prelude::*; #[pallet::pallet] - #[pallet::generate_store(pub(super) trait Store)] pub struct Pallet(_); #[cfg(feature = "runtime-benchmarks")] diff --git a/frame/utility/src/lib.rs b/frame/utility/src/lib.rs index 6cfa8b7e3..af212a31e 100644 --- a/frame/utility/src/lib.rs +++ b/frame/utility/src/lib.rs @@ -76,7 +76,6 @@ pub mod pallet { use frame_system::pallet_prelude::*; #[pallet::pallet] - #[pallet::generate_store(pub(super) trait Store)] pub struct Pallet(_); /// Configuration trait. diff --git a/frame/utility/src/tests.rs b/frame/utility/src/tests.rs index 413315a25..d9ac2ebbc 100644 --- a/frame/utility/src/tests.rs +++ b/frame/utility/src/tests.rs @@ -96,7 +96,6 @@ mod mock_democracy { use frame_system::pallet_prelude::*; #[pallet::pallet] - #[pallet::generate_store(pub(super) trait Store)] pub struct Pallet(_); #[pallet::config] diff --git a/frame/vesting/src/lib.rs b/frame/vesting/src/lib.rs index dbc018796..af3654924 100644 --- a/frame/vesting/src/lib.rs +++ b/frame/vesting/src/lib.rs @@ -211,7 +211,6 @@ pub mod pallet { pub(crate) type StorageVersion = StorageValue<_, Releases, ValueQuery>; #[pallet::pallet] - #[pallet::generate_store(pub(super) trait Store)] pub struct Pallet(_); #[pallet::genesis_config] diff --git a/frame/whitelist/src/lib.rs b/frame/whitelist/src/lib.rs index bfbadd073..decf010b0 100644 --- a/frame/whitelist/src/lib.rs +++ b/frame/whitelist/src/lib.rs @@ -88,7 +88,6 @@ pub mod pallet { } #[pallet::pallet] - #[pallet::generate_store(pub(super) trait Store)] pub struct Pallet(_); #[pallet::event] diff --git a/test-utils/runtime/src/system.rs b/test-utils/runtime/src/system.rs index 657e3c90f..12ebf486b 100644 --- a/test-utils/runtime/src/system.rs +++ b/test-utils/runtime/src/system.rs @@ -47,7 +47,6 @@ mod pallet { use frame_support::pallet_prelude::*; #[pallet::pallet] - #[pallet::generate_store(pub(super) trait Store)] #[pallet::without_storage_info] pub struct Pallet(PhantomData); diff --git a/utils/frame/rpc/support/src/lib.rs b/utils/frame/rpc/support/src/lib.rs index c0ec8befc..eecc80c40 100644 --- a/utils/frame/rpc/support/src/lib.rs +++ b/utils/frame/rpc/support/src/lib.rs @@ -94,7 +94,6 @@ use sp_storage::{StorageData, StorageKey}; /// use frame_support::pallet_prelude::*; /// /// #[pallet::pallet] -/// #[pallet::generate_store(pub(super) trait Store)] /// pub struct Pallet(PhantomData); /// /// #[pallet::config] From 30aa444d2e398c5ca798e306c9c9b04c0e1b5ea9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dino=20Pa=C4=8Dandi?= <3002868+Dinonard@users.noreply.github.com> Date: Mon, 13 Mar 2023 16:44:47 +0100 Subject: [PATCH 225/558] Pallet assets improvements (#13543) --- frame/assets/src/functions.rs | 4 +-- frame/assets/src/lib.rs | 13 +++++++--- frame/assets/src/mock.rs | 40 +++++++++++++++++++++++++++--- frame/assets/src/tests.rs | 46 +++++++++++++++++++++++++++++------ 4 files changed, 85 insertions(+), 18 deletions(-) diff --git a/frame/assets/src/functions.rs b/frame/assets/src/functions.rs index f6e027472..47657ff26 100644 --- a/frame/assets/src/functions.rs +++ b/frame/assets/src/functions.rs @@ -661,8 +661,8 @@ impl, I: 'static> Pallet { status: AssetStatus::Live, }, ); + ensure!(T::CallbackHandle::created(&id, &owner).is_ok(), Error::::CallbackFailed); Self::deposit_event(Event::ForceCreated { asset_id: id, owner: owner.clone() }); - T::CallbackHandle::created(&id, &owner); Ok(()) } @@ -752,7 +752,6 @@ impl, I: 'static> Pallet { approvals_destroyed: removed_approvals as u32, approvals_remaining: details.approvals as u32, }); - T::CallbackHandle::destroyed(&id); Ok(()) })?; Ok(removed_approvals) @@ -767,6 +766,7 @@ impl, I: 'static> Pallet { ensure!(details.status == AssetStatus::Destroying, Error::::IncorrectStatus); ensure!(details.accounts == 0, Error::::InUse); ensure!(details.approvals == 0, Error::::InUse); + ensure!(T::CallbackHandle::destroyed(&id).is_ok(), Error::::CallbackFailed); let metadata = Metadata::::take(&id); T::Currency::unreserve( diff --git a/frame/assets/src/lib.rs b/frame/assets/src/lib.rs index bb0e8ce8b..b36a5cabd 100644 --- a/frame/assets/src/lib.rs +++ b/frame/assets/src/lib.rs @@ -178,10 +178,14 @@ const LOG_TARGET: &str = "runtime::assets"; /// Trait with callbacks that are executed after successfull asset creation or destruction. pub trait AssetsCallback { /// Indicates that asset with `id` was successfully created by the `owner` - fn created(_id: &AssetId, _owner: &AccountId) {} + fn created(_id: &AssetId, _owner: &AccountId) -> Result<(), ()> { + Ok(()) + } /// Indicates that asset with `id` has just been destroyed - fn destroyed(_id: &AssetId) {} + fn destroyed(_id: &AssetId) -> Result<(), ()> { + Ok(()) + } } /// Empty implementation in case no callbacks are required. @@ -560,6 +564,8 @@ pub mod pallet { IncorrectStatus, /// The asset should be frozen before the given operation. NotFrozen, + /// Callback action resulted in error + CallbackFailed, } #[pallet::call] @@ -618,13 +624,12 @@ pub mod pallet { status: AssetStatus::Live, }, ); - + ensure!(T::CallbackHandle::created(&id, &owner).is_ok(), Error::::CallbackFailed); Self::deposit_event(Event::Created { asset_id: id, creator: owner.clone(), owner: admin, }); - T::CallbackHandle::created(&id, &owner); Ok(()) } diff --git a/frame/assets/src/mock.rs b/frame/assets/src/mock.rs index 14795b10a..3f456a7de 100644 --- a/frame/assets/src/mock.rs +++ b/frame/assets/src/mock.rs @@ -91,12 +91,44 @@ impl pallet_balances::Config for Test { pub struct AssetsCallbackHandle; impl AssetsCallback for AssetsCallbackHandle { - fn created(_id: &AssetId, _owner: &AccountId) { - storage::set(b"asset_created", &().encode()); + fn created(_id: &AssetId, _owner: &AccountId) -> Result<(), ()> { + if Self::should_err() { + Err(()) + } else { + storage::set(Self::CREATED.as_bytes(), &().encode()); + Ok(()) + } } - fn destroyed(_id: &AssetId) { - storage::set(b"asset_destroyed", &().encode()); + fn destroyed(_id: &AssetId) -> Result<(), ()> { + if Self::should_err() { + Err(()) + } else { + storage::set(Self::DESTROYED.as_bytes(), &().encode()); + Ok(()) + } + } +} + +impl AssetsCallbackHandle { + pub const CREATED: &'static str = "asset_created"; + pub const DESTROYED: &'static str = "asset_destroyed"; + + const RETURN_ERROR: &'static str = "return_error"; + + // Configures `Self` to return `Ok` when callbacks are invoked + pub fn set_return_ok() { + storage::clear(Self::RETURN_ERROR.as_bytes()); + } + + // Configures `Self` to return `Err` when callbacks are invoked + pub fn set_return_error() { + storage::set(Self::RETURN_ERROR.as_bytes(), &().encode()); + } + + // If `true`, callback should return `Err`, `Ok` otherwise. + fn should_err() -> bool { + storage::exists(Self::RETURN_ERROR.as_bytes()) } } diff --git a/frame/assets/src/tests.rs b/frame/assets/src/tests.rs index a2788f7f4..bc61810a7 100644 --- a/frame/assets/src/tests.rs +++ b/frame/assets/src/tests.rs @@ -1261,28 +1261,58 @@ fn querying_roles_should_work() { #[test] fn normal_asset_create_and_destroy_callbacks_should_work() { new_test_ext().execute_with(|| { - assert!(storage::get(b"asset_created").is_none()); - assert!(storage::get(b"asset_destroyed").is_none()); + assert!(storage::get(AssetsCallbackHandle::CREATED.as_bytes()).is_none()); + assert!(storage::get(AssetsCallbackHandle::DESTROYED.as_bytes()).is_none()); Balances::make_free_balance_be(&1, 100); assert_ok!(Assets::create(RuntimeOrigin::signed(1), 0, 1, 1)); - assert!(storage::get(b"asset_created").is_some()); - assert!(storage::get(b"asset_destroyed").is_none()); + assert!(storage::get(AssetsCallbackHandle::CREATED.as_bytes()).is_some()); + assert!(storage::get(AssetsCallbackHandle::DESTROYED.as_bytes()).is_none()); assert_ok!(Assets::start_destroy(RuntimeOrigin::signed(1), 0)); assert_ok!(Assets::destroy_accounts(RuntimeOrigin::signed(1), 0)); assert_ok!(Assets::destroy_approvals(RuntimeOrigin::signed(1), 0)); + // Callback still hasn't been invoked + assert!(storage::get(AssetsCallbackHandle::DESTROYED.as_bytes()).is_none()); + assert_ok!(Assets::finish_destroy(RuntimeOrigin::signed(1), 0)); - assert!(storage::get(b"asset_destroyed").is_some()); + assert!(storage::get(AssetsCallbackHandle::DESTROYED.as_bytes()).is_some()); }); } #[test] fn root_asset_create_should_work() { new_test_ext().execute_with(|| { - assert!(storage::get(b"asset_created").is_none()); + assert!(storage::get(AssetsCallbackHandle::CREATED.as_bytes()).is_none()); assert_ok!(Assets::force_create(RuntimeOrigin::root(), 0, 1, true, 1)); - assert!(storage::get(b"asset_created").is_some()); - assert!(storage::get(b"asset_destroyed").is_none()); + assert!(storage::get(AssetsCallbackHandle::CREATED.as_bytes()).is_some()); + assert!(storage::get(AssetsCallbackHandle::DESTROYED.as_bytes()).is_none()); + }); +} + +#[test] +fn asset_create_and_destroy_is_reverted_if_callback_fails() { + new_test_ext().execute_with(|| { + // Asset creation fails due to callback failure + AssetsCallbackHandle::set_return_error(); + Balances::make_free_balance_be(&1, 100); + assert_noop!( + Assets::create(RuntimeOrigin::signed(1), 0, 1, 1), + Error::::CallbackFailed + ); + + // Callback succeeds, so asset creation succeeds + AssetsCallbackHandle::set_return_ok(); + assert_ok!(Assets::create(RuntimeOrigin::signed(1), 0, 1, 1)); + + // Asset destroy should fail due to callback failure + AssetsCallbackHandle::set_return_error(); + assert_ok!(Assets::start_destroy(RuntimeOrigin::signed(1), 0)); + assert_ok!(Assets::destroy_accounts(RuntimeOrigin::signed(1), 0)); + assert_ok!(Assets::destroy_approvals(RuntimeOrigin::signed(1), 0)); + assert_noop!( + Assets::finish_destroy(RuntimeOrigin::signed(1), 0), + Error::::CallbackFailed + ); }); } From 959c5b636f2536b883f64e13591aa61825a558f5 Mon Sep 17 00:00:00 2001 From: Alexandru Vasile <60601340+lexnv@users.noreply.github.com> Date: Mon, 13 Mar 2023 21:02:53 +0200 Subject: [PATCH 226/558] Metadata V15: Expose pallet documentation (#13452) * frame/proc: Helpers to parse pallet documentation attributes Signed-off-by: Alexandru Vasile * frame/proc: Expand pallet with runtime metadata documentation Signed-off-by: Alexandru Vasile * frame/dispatch: Implement doc function getter for dispatch Signed-off-by: Alexandru Vasile * frame/tests: Check exposed runtime metadata documentation Signed-off-by: Alexandru Vasile * frame/tests: Add UI tests for `pallet_doc` attribute Signed-off-by: Alexandru Vasile * frame/proc: Document pallet_doc attribute Signed-off-by: Alexandru Vasile * frame/support: Use `derive_syn_parse` Signed-off-by: Alexandru Vasile * Update frame/support/procedural/src/lib.rs Co-authored-by: Niklas Adolfsson * frame/support: Improve documentation Signed-off-by: Alexandru Vasile --------- Signed-off-by: Alexandru Vasile Co-authored-by: parity-processbot <> Co-authored-by: Niklas Adolfsson --- frame/support/procedural/src/lib.rs | 51 ++++ .../src/pallet/expand/documentation.rs | 221 ++++++++++++++++++ .../procedural/src/pallet/expand/mod.rs | 4 + frame/support/src/dispatch.rs | 24 ++ frame/support/test/tests/pallet.rs | 12 + .../pallet_ui/pallet_doc_arg_non_path.rs | 16 ++ .../pallet_ui/pallet_doc_arg_non_path.stderr | 5 + .../test/tests/pallet_ui/pallet_doc_empty.rs | 16 ++ .../tests/pallet_ui/pallet_doc_empty.stderr | 5 + .../tests/pallet_ui/pallet_doc_invalid_arg.rs | 16 ++ .../pallet_ui/pallet_doc_invalid_arg.stderr | 5 + .../pallet_ui/pallet_doc_multiple_args.rs | 16 ++ .../pallet_ui/pallet_doc_multiple_args.stderr | 5 + 13 files changed, 396 insertions(+) create mode 100644 frame/support/procedural/src/pallet/expand/documentation.rs create mode 100644 frame/support/test/tests/pallet_ui/pallet_doc_arg_non_path.rs create mode 100644 frame/support/test/tests/pallet_ui/pallet_doc_arg_non_path.stderr create mode 100644 frame/support/test/tests/pallet_ui/pallet_doc_empty.rs create mode 100644 frame/support/test/tests/pallet_ui/pallet_doc_empty.stderr create mode 100644 frame/support/test/tests/pallet_ui/pallet_doc_invalid_arg.rs create mode 100644 frame/support/test/tests/pallet_ui/pallet_doc_invalid_arg.stderr create mode 100644 frame/support/test/tests/pallet_ui/pallet_doc_multiple_args.rs create mode 100644 frame/support/test/tests/pallet_ui/pallet_doc_multiple_args.stderr diff --git a/frame/support/procedural/src/lib.rs b/frame/support/procedural/src/lib.rs index fec824107..1d5dde20e 100644 --- a/frame/support/procedural/src/lib.rs +++ b/frame/support/procedural/src/lib.rs @@ -481,6 +481,57 @@ pub fn construct_runtime(input: TokenStream) -> TokenStream { /// /// /// See `frame_support::pallet` docs for more info. +/// +/// ## Runtime Metadata Documentation +/// +/// The documentation added to this pallet is included in the runtime metadata. +/// +/// The documentation can be defined in the following ways: +/// +/// ```ignore +/// #[pallet::pallet] +/// /// Documentation for pallet 1 +/// #[doc = "Documentation for pallet 2"] +/// #[doc = include_str!("../README.md")] +/// #[pallet_doc("../doc1.md")] +/// #[pallet_doc("../doc2.md")] +/// pub struct Pallet(_); +/// ``` +/// +/// The runtime metadata for this pallet contains the following +/// - " Documentation for pallet 1" (captured from `///`) +/// - "Documentation for pallet 2" (captured from `#[doc]`) +/// - content of ../README.md (captured from `#[doc]` with `include_str!`) +/// - content of "../doc1.md" (captured from `pallet_doc`) +/// - content of "../doc2.md" (captured from `pallet_doc`) +/// +/// ### `doc` attribute +/// +/// The value of the `doc` attribute is included in the runtime metadata, as well as +/// expanded on the pallet module. The previous example is expanded to: +/// +/// ```ignore +/// /// Documentation for pallet 1 +/// /// Documentation for pallet 2 +/// /// Content of README.md +/// pub struct Pallet(_); +/// ``` +/// +/// If you want to specify the file from which the documentation is loaded, you can use the +/// `include_str` macro. However, if you only want the documentation to be included in the +/// runtime metadata, use the `pallet_doc` attribute. +/// +/// ### `pallet_doc` attribute +/// +/// Unlike the `doc` attribute, the documentation provided to the `pallet_doc` attribute is +/// not inserted on the module. +/// +/// The `pallet_doc` attribute can only be provided with one argument, +/// which is the file path that holds the documentation to be added to the metadata. +/// +/// This approach is beneficial when you use the `include_str` macro at the beginning of the file +/// and want that documentation to extend to the runtime metadata, without reiterating the +/// documentation on the module itself. #[proc_macro_attribute] pub fn pallet(attr: TokenStream, item: TokenStream) -> TokenStream { pallet::pallet(attr, item) diff --git a/frame/support/procedural/src/pallet/expand/documentation.rs b/frame/support/procedural/src/pallet/expand/documentation.rs new file mode 100644 index 000000000..e158448a8 --- /dev/null +++ b/frame/support/procedural/src/pallet/expand/documentation.rs @@ -0,0 +1,221 @@ +// This file is part of Substrate. + +// Copyright (C) Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +use crate::pallet::Def; +use derive_syn_parse::Parse; +use proc_macro2::TokenStream; +use quote::ToTokens; +use syn::{ + parse::{self, Parse, ParseStream}, + spanned::Spanned, + Attribute, Lit, +}; + +const DOC: &'static str = "doc"; +const PALLET_DOC: &'static str = "pallet_doc"; + +mod keywords { + syn::custom_keyword!(include_str); +} + +/// Get the documentation file path from the `pallet_doc` attribute. +/// +/// Supported format: +/// `#[pallet_doc(PATH)]`: The path of the file from which the documentation is loaded +fn parse_pallet_doc_value(attr: &Attribute) -> syn::Result { + let span = attr.span(); + + let meta = attr.parse_meta()?; + let syn::Meta::List(metalist) = meta else { + let msg = "The `pallet_doc` attribute must receive arguments as a list. Supported format: `pallet_doc(PATH)`"; + return Err(syn::Error::new(span, msg)) + }; + + let paths: Vec<_> = metalist + .nested + .into_iter() + .map(|nested| { + let syn::NestedMeta::Lit(lit) = nested else { + let msg = "The `pallet_doc` received an unsupported argument. Supported format: `pallet_doc(PATH)`"; + return Err(syn::Error::new(span, msg)) + }; + + Ok(lit) + }) + .collect::>()?; + + if paths.len() != 1 { + let msg = "The `pallet_doc` attribute must receive only one argument. Supported format: `pallet_doc(PATH)`"; + return Err(syn::Error::new(span, msg)) + } + + Ok(DocMetaValue::Path(paths[0].clone())) +} + +/// Get the value from the `doc` comment attribute: +/// +/// Supported formats: +/// - `#[doc = "A doc string"]`: Documentation as a string literal +/// - `#[doc = include_str!(PATH)]`: Documentation obtained from a path +fn parse_doc_value(attr: &Attribute) -> Option { + let Some(ident) = attr.path.get_ident() else { + return None + }; + if ident != DOC { + return None + } + + let parser = |input: ParseStream| DocParser::parse(input); + let result = parse::Parser::parse2(parser, attr.tokens.clone()).ok()?; + + if let Some(lit) = result.lit { + Some(DocMetaValue::Lit(lit)) + } else if let Some(include_doc) = result.include_doc { + Some(DocMetaValue::Path(include_doc.lit)) + } else { + None + } +} + +/// Parse the include_str attribute. +#[derive(Debug, Parse)] +struct IncludeDocParser { + _include_str: keywords::include_str, + _eq_token: syn::token::Bang, + #[paren] + _paren: syn::token::Paren, + #[inside(_paren)] + lit: Lit, +} + +/// Parse the doc literal. +#[derive(Debug, Parse)] +struct DocParser { + _eq_token: syn::token::Eq, + #[peek(Lit)] + lit: Option, + #[parse_if(lit.is_none())] + include_doc: Option, +} + +/// Supported documentation tokens. +#[derive(Debug)] +enum DocMetaValue { + /// Documentation with string literals. + /// + /// `#[doc = "Lit"]` + Lit(Lit), + /// Documentation with `include_str!` macro. + /// + /// The string literal represents the file `PATH`. + /// + /// `#[doc = include_str!(PATH)]` + Path(Lit), +} + +impl ToTokens for DocMetaValue { + fn to_tokens(&self, tokens: &mut TokenStream) { + match self { + DocMetaValue::Lit(lit) => lit.to_tokens(tokens), + DocMetaValue::Path(path_lit) => { + let decl = quote::quote!(include_str!(#path_lit)); + tokens.extend(decl) + }, + } + } +} + +/// Extract the documentation from the given pallet definition +/// to include in the runtime metadata. +/// +/// Implement a `pallet_documentation_metadata` function to fetch the +/// documentation that is included in the metadata. +/// +/// The documentation is placed at the top of the module similar to: +/// +/// ```ignore +/// #[pallet] +/// /// Documentation for pallet +/// #[doc = "Documentation for pallet"] +/// #[doc = include_str!("../README.md")] +/// #[pallet_doc("../documentation1.md")] +/// #[pallet_doc("../documentation2.md")] +/// pub mod pallet {} +/// ``` +/// +/// # pallet_doc +/// +/// The `pallet_doc` attribute can only be provided with one argument, +/// which is the file path that holds the documentation to be added to the metadata. +/// +/// Unlike the `doc` attribute, the documentation provided to the `proc_macro` attribute is +/// not inserted at the beginning of the module. +pub fn expand_documentation(def: &mut Def) -> proc_macro2::TokenStream { + let frame_support = &def.frame_support; + let type_impl_gen = &def.type_impl_generics(proc_macro2::Span::call_site()); + let type_use_gen = &def.type_use_generics(proc_macro2::Span::call_site()); + let pallet_ident = &def.pallet_struct.pallet; + let where_clauses = &def.config.where_clause; + + // TODO: Use [drain_filter](https://doc.rust-lang.org/std/vec/struct.Vec.html#method.drain_filter) when it is stable. + + // The `pallet_doc` attributes are excluded from the generation of the pallet, + // but they are included in the runtime metadata. + let mut pallet_docs = Vec::with_capacity(def.item.attrs.len()); + let mut index = 0; + while index < def.item.attrs.len() { + let attr = &def.item.attrs[index]; + if let Some(ident) = attr.path.get_ident() { + if ident == PALLET_DOC { + let elem = def.item.attrs.remove(index); + pallet_docs.push(elem); + // Do not increment the index, we have just removed the + // element from the attributes. + continue + } + } + + index += 1; + } + + // Capture the `#[doc = include_str!("../README.md")]` and `#[doc = "Documentation"]`. + let mut docs: Vec<_> = def.item.attrs.iter().filter_map(parse_doc_value).collect(); + + // Capture the `#[pallet_doc("../README.md")]`. + let pallet_docs: Vec<_> = match pallet_docs + .into_iter() + .map(|attr| parse_pallet_doc_value(&attr)) + .collect::>() + { + Ok(docs) => docs, + Err(err) => return err.into_compile_error(), + }; + + docs.extend(pallet_docs); + + quote::quote!( + impl<#type_impl_gen> #pallet_ident<#type_use_gen> #where_clauses{ + + #[doc(hidden)] + pub fn pallet_documentation_metadata() + -> #frame_support::sp_std::vec::Vec<&'static str> + { + #frame_support::sp_std::vec![ #( #docs ),* ] + } + } + ) +} diff --git a/frame/support/procedural/src/pallet/expand/mod.rs b/frame/support/procedural/src/pallet/expand/mod.rs index 3ccbf91b4..09a25a710 100644 --- a/frame/support/procedural/src/pallet/expand/mod.rs +++ b/frame/support/procedural/src/pallet/expand/mod.rs @@ -18,6 +18,7 @@ mod call; mod config; mod constants; +mod documentation; mod error; mod event; mod genesis_build; @@ -52,6 +53,8 @@ pub fn merge_where_clauses(clauses: &[&Option]) -> Option proc_macro2::TokenStream { + // Remove the `pallet_doc` attribute first. + let metadata_docs = documentation::expand_documentation(&mut def); let constants = constants::expand_constants(&mut def); let pallet_struct = pallet_struct::expand_pallet_struct(&mut def); let config = config::expand_config(&mut def); @@ -82,6 +85,7 @@ pub fn expand(mut def: Def) -> proc_macro2::TokenStream { } let new_items = quote::quote!( + #metadata_docs #constants #pallet_struct #config diff --git a/frame/support/src/dispatch.rs b/frame/support/src/dispatch.rs index e2798763d..c5f4cc60c 100644 --- a/frame/support/src/dispatch.rs +++ b/frame/support/src/dispatch.rs @@ -2948,6 +2948,10 @@ macro_rules! decl_module { { $( $other_where_bounds )* } $( $error_type )* } + $crate::__impl_docs_metadata! { + $mod_type<$trait_instance: $trait_name $(, $instance: $instantiable)?> + { $( $other_where_bounds )* } + } $crate::__impl_module_constants_metadata ! { $mod_type<$trait_instance: $trait_name $(, $instance: $instantiable)?> { $( $other_where_bounds )* } @@ -3018,6 +3022,26 @@ macro_rules! __impl_error_metadata { }; } +/// Implement metadata for pallet documentation. +#[macro_export] +#[doc(hidden)] +macro_rules! __impl_docs_metadata { + ( + $mod_type:ident<$trait_instance:ident: $trait_name:ident$(, $instance:ident: $instantiable:path)?> + { $( $other_where_bounds:tt )* } + ) => { + impl<$trait_instance: $trait_name $(, $instance: $instantiable)?> $mod_type<$trait_instance $(, $instance)?> + where $( $other_where_bounds )* + { + #[doc(hidden)] + #[allow(dead_code)] + pub fn pallet_documentation_metadata() -> $crate::sp_std::vec::Vec<&'static str> { + $crate::sp_std::vec![] + } + } + }; +} + /// Implement metadata for module constants. #[macro_export] #[doc(hidden)] diff --git a/frame/support/test/tests/pallet.rs b/frame/support/test/tests/pallet.rs index 8e77cd025..fbebbef45 100644 --- a/frame/support/test/tests/pallet.rs +++ b/frame/support/test/tests/pallet.rs @@ -101,6 +101,10 @@ impl SomeAssociation2 for u64 { } #[frame_support::pallet] +/// Pallet documentation +// Comments should not be included in the pallet documentation +#[pallet_doc("../../README.md")] +#[doc = include_str!("../../README.md")] pub mod pallet { use super::*; use frame_support::pallet_prelude::*; @@ -1589,6 +1593,14 @@ fn metadata() { pretty_assertions::assert_eq!(actual_metadata.pallets, expected_metadata.pallets); } +#[test] +fn test_pallet_runtime_docs() { + let docs = crate::pallet::Pallet::::pallet_documentation_metadata(); + let readme = "Support code for the runtime.\n\nLicense: Apache-2.0"; + let expected = vec![" Pallet documentation", readme, readme]; + assert_eq!(docs, expected); +} + #[test] fn test_pallet_info_access() { assert_eq!(::name(), "System"); diff --git a/frame/support/test/tests/pallet_ui/pallet_doc_arg_non_path.rs b/frame/support/test/tests/pallet_ui/pallet_doc_arg_non_path.rs new file mode 100644 index 000000000..ef3097d23 --- /dev/null +++ b/frame/support/test/tests/pallet_ui/pallet_doc_arg_non_path.rs @@ -0,0 +1,16 @@ +#[frame_support::pallet] +// Must receive a string literal pointing to a path +#[pallet_doc(X)] +mod pallet { + #[pallet::config] + pub trait Config: frame_system::Config + where + ::Index: From, + { + } + + #[pallet::pallet] + pub struct Pallet(core::marker::PhantomData); +} + +fn main() {} diff --git a/frame/support/test/tests/pallet_ui/pallet_doc_arg_non_path.stderr b/frame/support/test/tests/pallet_ui/pallet_doc_arg_non_path.stderr new file mode 100644 index 000000000..ba60479c0 --- /dev/null +++ b/frame/support/test/tests/pallet_ui/pallet_doc_arg_non_path.stderr @@ -0,0 +1,5 @@ +error: The `pallet_doc` received an unsupported argument. Supported format: `pallet_doc(PATH)` + --> tests/pallet_ui/pallet_doc_arg_non_path.rs:3:1 + | +3 | #[pallet_doc(X)] + | ^ diff --git a/frame/support/test/tests/pallet_ui/pallet_doc_empty.rs b/frame/support/test/tests/pallet_ui/pallet_doc_empty.rs new file mode 100644 index 000000000..fe40806d2 --- /dev/null +++ b/frame/support/test/tests/pallet_ui/pallet_doc_empty.rs @@ -0,0 +1,16 @@ +#[frame_support::pallet] +// Expected one argument for the doc path. +#[pallet_doc] +mod pallet { + #[pallet::config] + pub trait Config: frame_system::Config + where + ::Index: From, + { + } + + #[pallet::pallet] + pub struct Pallet(core::marker::PhantomData); +} + +fn main() {} diff --git a/frame/support/test/tests/pallet_ui/pallet_doc_empty.stderr b/frame/support/test/tests/pallet_ui/pallet_doc_empty.stderr new file mode 100644 index 000000000..d6a189d79 --- /dev/null +++ b/frame/support/test/tests/pallet_ui/pallet_doc_empty.stderr @@ -0,0 +1,5 @@ +error: The `pallet_doc` attribute must receive arguments as a list. Supported format: `pallet_doc(PATH)` + --> tests/pallet_ui/pallet_doc_empty.rs:3:1 + | +3 | #[pallet_doc] + | ^ diff --git a/frame/support/test/tests/pallet_ui/pallet_doc_invalid_arg.rs b/frame/support/test/tests/pallet_ui/pallet_doc_invalid_arg.rs new file mode 100644 index 000000000..8f0ccb377 --- /dev/null +++ b/frame/support/test/tests/pallet_ui/pallet_doc_invalid_arg.rs @@ -0,0 +1,16 @@ +#[frame_support::pallet] +// Argument expected as list, not named value. +#[pallet_doc = "invalid"] +mod pallet { + #[pallet::config] + pub trait Config: frame_system::Config + where + ::Index: From, + { + } + + #[pallet::pallet] + pub struct Pallet(core::marker::PhantomData); +} + +fn main() {} diff --git a/frame/support/test/tests/pallet_ui/pallet_doc_invalid_arg.stderr b/frame/support/test/tests/pallet_ui/pallet_doc_invalid_arg.stderr new file mode 100644 index 000000000..9dd039789 --- /dev/null +++ b/frame/support/test/tests/pallet_ui/pallet_doc_invalid_arg.stderr @@ -0,0 +1,5 @@ +error: The `pallet_doc` attribute must receive arguments as a list. Supported format: `pallet_doc(PATH)` + --> tests/pallet_ui/pallet_doc_invalid_arg.rs:3:1 + | +3 | #[pallet_doc = "invalid"] + | ^ diff --git a/frame/support/test/tests/pallet_ui/pallet_doc_multiple_args.rs b/frame/support/test/tests/pallet_ui/pallet_doc_multiple_args.rs new file mode 100644 index 000000000..ffbed9d95 --- /dev/null +++ b/frame/support/test/tests/pallet_ui/pallet_doc_multiple_args.rs @@ -0,0 +1,16 @@ +#[frame_support::pallet] +// Supports only one argument. +#[pallet_doc("A", "B")] +mod pallet { + #[pallet::config] + pub trait Config: frame_system::Config + where + ::Index: From, + { + } + + #[pallet::pallet] + pub struct Pallet(core::marker::PhantomData); +} + +fn main() {} diff --git a/frame/support/test/tests/pallet_ui/pallet_doc_multiple_args.stderr b/frame/support/test/tests/pallet_ui/pallet_doc_multiple_args.stderr new file mode 100644 index 000000000..58ad75a0a --- /dev/null +++ b/frame/support/test/tests/pallet_ui/pallet_doc_multiple_args.stderr @@ -0,0 +1,5 @@ +error: The `pallet_doc` attribute must receive only one argument. Supported format: `pallet_doc(PATH)` + --> tests/pallet_ui/pallet_doc_multiple_args.rs:3:1 + | +3 | #[pallet_doc("A", "B")] + | ^ From 3a94009daac8919db46e3014964fe23866edb996 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bastian=20K=C3=B6cher?= Date: Mon, 13 Mar 2023 22:46:53 +0100 Subject: [PATCH 227/558] sc-slots: Cleanup (#13590) Calculate remaining proposal duration once and also use proposal time as timeout for creating the inherent data. --- client/consensus/slots/src/lib.rs | 24 ++++++++++++++++-------- 1 file changed, 16 insertions(+), 8 deletions(-) diff --git a/client/consensus/slots/src/lib.rs b/client/consensus/slots/src/lib.rs index c18284de6..f1d817ab7 100644 --- a/client/consensus/slots/src/lib.rs +++ b/client/consensus/slots/src/lib.rs @@ -190,7 +190,7 @@ pub trait SimpleSlotWorker { proposer: Self::Proposer, claim: &Self::Claim, slot_info: SlotInfo, - proposing_remaining: Delay, + end_proposing_at: Instant, ) -> Option< Proposal< B, @@ -202,9 +202,11 @@ pub trait SimpleSlotWorker { let telemetry = self.telemetry(); let log_target = self.logging_target(); - let inherent_data = Self::create_inherent_data(&slot_info, &log_target).await?; + let inherent_data = + Self::create_inherent_data(&slot_info, &log_target, end_proposing_at).await?; - let proposing_remaining_duration = self.proposing_remaining_duration(&slot_info); + let proposing_remaining_duration = + end_proposing_at.saturating_duration_since(Instant::now()); let logs = self.pre_digest_data(slot, claim); // deadline our production to 98% of the total time left for proposing. As we deadline @@ -219,7 +221,12 @@ pub trait SimpleSlotWorker { ) .map_err(|e| sp_consensus::Error::ClientImport(e.to_string())); - let proposal = match futures::future::select(proposing, proposing_remaining).await { + let proposal = match futures::future::select( + proposing, + Delay::new(proposing_remaining_duration), + ) + .await + { Either::Left((Ok(p), _)) => p, Either::Left((Err(err), _)) => { warn!(target: log_target, "Proposing failed: {}", err); @@ -255,8 +262,9 @@ pub trait SimpleSlotWorker { async fn create_inherent_data( slot_info: &SlotInfo, logging_target: &str, + end_proposing_at: Instant, ) -> Option { - let remaining_duration = slot_info.ends_at.saturating_duration_since(Instant::now()); + let remaining_duration = end_proposing_at.saturating_duration_since(Instant::now()); let delay = Delay::new(remaining_duration); let cid = slot_info.create_inherent_data.create_inherent_data(); let inherent_data = match futures::future::select(delay, cid).await { @@ -300,7 +308,7 @@ pub trait SimpleSlotWorker { let proposing_remaining_duration = self.proposing_remaining_duration(&slot_info); - let proposing_remaining = if proposing_remaining_duration == Duration::default() { + let end_proposing_at = if proposing_remaining_duration == Duration::default() { debug!( target: logging_target, "Skipping proposal slot {} since there's no time left to propose", slot, @@ -308,7 +316,7 @@ pub trait SimpleSlotWorker { return None } else { - Delay::new(proposing_remaining_duration) + Instant::now() + proposing_remaining_duration }; let aux_data = match self.aux_data(&slot_info.chain_head, slot) { @@ -379,7 +387,7 @@ pub trait SimpleSlotWorker { }, }; - let proposal = self.propose(proposer, &claim, slot_info, proposing_remaining).await?; + let proposal = self.propose(proposer, &claim, slot_info, end_proposing_at).await?; let (block, storage_proof) = (proposal.block, proposal.proof); let (header, body) = block.deconstruct(); From 4f28d2923a825dafc8431825270e78c5ab218ce0 Mon Sep 17 00:00:00 2001 From: Jegor Sidorenko <5252494+jsidorenko@users.noreply.github.com> Date: Tue, 14 Mar 2023 09:55:18 +0200 Subject: [PATCH 228/558] [NFTs] Allow to set the role to None (#13591) * Allow to unset the role * Chore * Array instead of vec --------- Co-authored-by: parity-processbot <> --- frame/nfts/src/benchmarking.rs | 12 +-- frame/nfts/src/features/create_delete_item.rs | 46 +++++----- frame/nfts/src/features/roles.rs | 38 ++++++-- frame/nfts/src/lib.rs | 21 +++-- frame/nfts/src/tests.rs | 88 +++++++++++++++---- 5 files changed, 143 insertions(+), 62 deletions(-) diff --git a/frame/nfts/src/benchmarking.rs b/frame/nfts/src/benchmarking.rs index d4bbd809c..68252ebfc 100644 --- a/frame/nfts/src/benchmarking.rs +++ b/frame/nfts/src/benchmarking.rs @@ -383,16 +383,16 @@ benchmarks_instance_pallet! { set_team { let (collection, caller, _) = create_collection::(); - let target0 = T::Lookup::unlookup(account("target", 0, SEED)); - let target1 = T::Lookup::unlookup(account("target", 1, SEED)); - let target2 = T::Lookup::unlookup(account("target", 2, SEED)); + let target0 = Some(T::Lookup::unlookup(account("target", 0, SEED))); + let target1 = Some(T::Lookup::unlookup(account("target", 1, SEED))); + let target2 = Some(T::Lookup::unlookup(account("target", 2, SEED))); }: _(SystemOrigin::Signed(caller), collection, target0, target1, target2) verify { assert_last_event::(Event::TeamChanged{ collection, - issuer: account("target", 0, SEED), - admin: account("target", 1, SEED), - freezer: account("target", 2, SEED), + issuer: Some(account("target", 0, SEED)), + admin: Some(account("target", 1, SEED)), + freezer: Some(account("target", 2, SEED)), }.into()); } diff --git a/frame/nfts/src/features/create_delete_item.rs b/frame/nfts/src/features/create_delete_item.rs index cd96d2844..2aa27dc06 100644 --- a/frame/nfts/src/features/create_delete_item.rs +++ b/frame/nfts/src/features/create_delete_item.rs @@ -106,9 +106,6 @@ impl, I: 'static> Pallet { let now = frame_system::Pallet::::block_number(); ensure!(deadline >= now, Error::::DeadlineExpired); - let collection_details = - Collection::::get(&collection).ok_or(Error::::UnknownCollection)?; - ensure!( Self::has_role(&collection, &signer, CollectionRole::Issuer), Error::::NoPermission @@ -123,27 +120,28 @@ impl, I: 'static> Pallet { item_config, |_, _| Ok(()), )?; - let origin = Self::find_account_by_role(&collection, CollectionRole::Admin) - .unwrap_or(collection_details.owner.clone()); - for (key, value) in attributes { - Self::do_set_attribute( - origin.clone(), - collection, - Some(item), - AttributeNamespace::CollectionOwner, - Self::construct_attribute_key(key)?, - Self::construct_attribute_value(value)?, - mint_to.clone(), - )?; - } - if !metadata.len().is_zero() { - Self::do_set_item_metadata( - Some(origin.clone()), - collection, - item, - metadata, - Some(mint_to.clone()), - )?; + let admin_account = Self::find_account_by_role(&collection, CollectionRole::Admin); + if let Some(admin_account) = admin_account { + for (key, value) in attributes { + Self::do_set_attribute( + admin_account.clone(), + collection, + Some(item), + AttributeNamespace::CollectionOwner, + Self::construct_attribute_key(key)?, + Self::construct_attribute_value(value)?, + mint_to.clone(), + )?; + } + if !metadata.len().is_zero() { + Self::do_set_item_metadata( + Some(admin_account.clone()), + collection, + item, + metadata, + Some(mint_to.clone()), + )?; + } } Ok(()) } diff --git a/frame/nfts/src/features/roles.rs b/frame/nfts/src/features/roles.rs index f91f3ecb6..3bac00206 100644 --- a/frame/nfts/src/features/roles.rs +++ b/frame/nfts/src/features/roles.rs @@ -23,24 +23,46 @@ impl, I: 'static> Pallet { pub(crate) fn do_set_team( maybe_check_owner: Option, collection: T::CollectionId, - issuer: T::AccountId, - admin: T::AccountId, - freezer: T::AccountId, + issuer: Option, + admin: Option, + freezer: Option, ) -> DispatchResult { Collection::::try_mutate(collection, |maybe_details| { let details = maybe_details.as_mut().ok_or(Error::::UnknownCollection)?; + let is_root = maybe_check_owner.is_none(); if let Some(check_origin) = maybe_check_owner { ensure!(check_origin == details.owner, Error::::NoPermission); } - // delete previous values - Self::clear_roles(&collection)?; - - let account_to_role = Self::group_roles_by_account(vec![ + let roles_map = [ (issuer.clone(), CollectionRole::Issuer), (admin.clone(), CollectionRole::Admin), (freezer.clone(), CollectionRole::Freezer), - ]); + ]; + + // only root can change the role from `None` to `Some(account)` + if !is_root { + for (account, role) in roles_map.iter() { + if account.is_some() { + ensure!( + Self::find_account_by_role(&collection, *role).is_some(), + Error::::NoPermission + ); + } + } + } + + let roles = roles_map + .into_iter() + .filter_map(|(account, role)| account.map(|account| (account, role))) + .collect(); + + let account_to_role = Self::group_roles_by_account(roles); + + // delete the previous records + Self::clear_roles(&collection)?; + + // insert new records for (account, roles) in account_to_role { CollectionRoleOf::::insert(&collection, &account, roles); } diff --git a/frame/nfts/src/lib.rs b/frame/nfts/src/lib.rs index 254fa7596..f4d0e4159 100644 --- a/frame/nfts/src/lib.rs +++ b/frame/nfts/src/lib.rs @@ -403,9 +403,9 @@ pub mod pallet { /// The management team changed. TeamChanged { collection: T::CollectionId, - issuer: T::AccountId, - admin: T::AccountId, - freezer: T::AccountId, + issuer: Option, + admin: Option, + freezer: Option, }, /// An `item` of a `collection` has been approved by the `owner` for transfer by /// a `delegate`. @@ -1136,6 +1136,9 @@ pub mod pallet { /// Origin must be either `ForceOrigin` or Signed and the sender should be the Owner of the /// `collection`. /// + /// Note: by setting the role to `None` only the `ForceOrigin` will be able to change it + /// after to `Some(account)`. + /// /// - `collection`: The collection whose team should be changed. /// - `issuer`: The new Issuer of this collection. /// - `admin`: The new Admin of this collection. @@ -1149,16 +1152,16 @@ pub mod pallet { pub fn set_team( origin: OriginFor, collection: T::CollectionId, - issuer: AccountIdLookupOf, - admin: AccountIdLookupOf, - freezer: AccountIdLookupOf, + issuer: Option>, + admin: Option>, + freezer: Option>, ) -> DispatchResult { let maybe_check_owner = T::ForceOrigin::try_origin(origin) .map(|_| None) .or_else(|origin| ensure_signed(origin).map(Some).map_err(DispatchError::from))?; - let issuer = T::Lookup::lookup(issuer)?; - let admin = T::Lookup::lookup(admin)?; - let freezer = T::Lookup::lookup(freezer)?; + let issuer = issuer.map(T::Lookup::lookup).transpose()?; + let admin = admin.map(T::Lookup::lookup).transpose()?; + let freezer = freezer.map(T::Lookup::lookup).transpose()?; Self::do_set_team(maybe_check_owner, collection, issuer, admin, freezer) } diff --git a/frame/nfts/src/tests.rs b/frame/nfts/src/tests.rs index 694468ace..0fc6fcd15 100644 --- a/frame/nfts/src/tests.rs +++ b/frame/nfts/src/tests.rs @@ -535,9 +535,9 @@ fn origin_guards_should_work() { Nfts::set_team( RuntimeOrigin::signed(account(2)), 0, - account(2), - account(2), - account(2), + Some(account(2)), + Some(account(2)), + Some(account(2)), ), Error::::NoPermission ); @@ -639,14 +639,14 @@ fn set_team_should_work() { assert_ok!(Nfts::set_team( RuntimeOrigin::signed(account(1)), 0, - account(2), - account(3), - account(4), + Some(account(2)), + Some(account(3)), + Some(account(4)), )); assert_ok!(Nfts::mint(RuntimeOrigin::signed(account(2)), 0, 42, account(2), None)); - assert_ok!(Nfts::lock_item_transfer(RuntimeOrigin::signed(account(4)), 0, 42)); - assert_ok!(Nfts::unlock_item_transfer(RuntimeOrigin::signed(account(4)), 0, 42)); + + // admin can't transfer/burn items he doesn't own assert_noop!( Nfts::transfer(RuntimeOrigin::signed(account(3)), 0, 42, account(3)), Error::::NoPermission @@ -655,6 +655,46 @@ fn set_team_should_work() { Nfts::burn(RuntimeOrigin::signed(account(3)), 0, 42), Error::::NoPermission ); + + assert_ok!(Nfts::lock_item_transfer(RuntimeOrigin::signed(account(4)), 0, 42)); + assert_ok!(Nfts::unlock_item_transfer(RuntimeOrigin::signed(account(4)), 0, 42)); + + // validate we can set any role to None + assert_ok!(Nfts::set_team( + RuntimeOrigin::signed(account(1)), + 0, + Some(account(2)), + Some(account(3)), + None, + )); + assert_noop!( + Nfts::lock_item_transfer(RuntimeOrigin::signed(account(4)), 0, 42), + Error::::NoPermission + ); + + // set all the roles to None + assert_ok!(Nfts::set_team(RuntimeOrigin::signed(account(1)), 0, None, None, None,)); + + // validate we can't set the roles back + assert_noop!( + Nfts::set_team( + RuntimeOrigin::signed(account(1)), + 0, + Some(account(2)), + Some(account(3)), + None, + ), + Error::::NoPermission + ); + + // only the root account can change the roles from None to Some() + assert_ok!(Nfts::set_team( + RuntimeOrigin::root(), + 0, + Some(account(2)), + Some(account(3)), + None, + )); }); } @@ -1476,7 +1516,13 @@ fn force_update_collection_should_work() { Balances::make_free_balance_be(&account(5), 100); assert_ok!(Nfts::force_collection_owner(RuntimeOrigin::root(), 0, account(5))); - assert_ok!(Nfts::set_team(RuntimeOrigin::root(), 0, account(2), account(5), account(4))); + assert_ok!(Nfts::set_team( + RuntimeOrigin::root(), + 0, + Some(account(2)), + Some(account(5)), + Some(account(4)), + )); assert_eq!(collections(), vec![(account(5), 0)]); assert_eq!(Balances::reserved_balance(account(1)), 2); assert_eq!(Balances::reserved_balance(account(5)), 63); @@ -1502,7 +1548,13 @@ fn force_update_collection_should_work() { assert_eq!(Balances::reserved_balance(account(5)), 0); // validate new roles - assert_ok!(Nfts::set_team(RuntimeOrigin::root(), 0, account(2), account(3), account(4))); + assert_ok!(Nfts::set_team( + RuntimeOrigin::root(), + 0, + Some(account(2)), + Some(account(3)), + Some(account(4)), + )); assert_eq!( CollectionRoleOf::::get(0, account(2)).unwrap(), CollectionRoles(CollectionRole::Issuer.into()) @@ -1516,7 +1568,13 @@ fn force_update_collection_should_work() { CollectionRoles(CollectionRole::Freezer.into()) ); - assert_ok!(Nfts::set_team(RuntimeOrigin::root(), 0, account(3), account(2), account(3))); + assert_ok!(Nfts::set_team( + RuntimeOrigin::root(), + 0, + Some(account(3)), + Some(account(2)), + Some(account(3)), + )); assert_eq!( CollectionRoleOf::::get(0, account(2)).unwrap(), @@ -1541,9 +1599,9 @@ fn burn_works() { assert_ok!(Nfts::set_team( RuntimeOrigin::signed(account(1)), 0, - account(2), - account(3), - account(4), + Some(account(2)), + Some(account(3)), + Some(account(4)), )); assert_noop!( @@ -3220,7 +3278,7 @@ fn pre_signed_mints_should_work() { signature, user_1.clone(), ), - Error::::UnknownCollection + Error::::NoPermission ); // validate max attributes limit From 897b95d98424a44eb493eb982e5ac88d37f60cb5 Mon Sep 17 00:00:00 2001 From: Aaro Altonen <48052676+altonen@users.noreply.github.com> Date: Tue, 14 Mar 2023 14:06:40 +0200 Subject: [PATCH 229/558] Move code from `sc-network-common` back to `sc-network` (#13592) * Move service tests to `client/network/tests` These tests depend on `sc-network` and `sc-network-sync` so they should live outside the crate. * Move some configs from `sc-network-common` to `sc-network` * Move `NetworkService` traits to `sc-network` * Move request-responses to `sc-network` * Remove more stuff * Remove rest of configs from `sc-network-common` to `sc-network` * Remove more stuff * Fix warnings * Update client/network/src/request_responses.rs Co-authored-by: Dmitry Markin * Fix cargo doc --------- Co-authored-by: Dmitry Markin --- Cargo.lock | 12 +- bin/node/cli/src/service.rs | 6 +- client/authority-discovery/Cargo.toml | 1 + client/authority-discovery/src/lib.rs | 2 +- client/authority-discovery/src/worker.rs | 5 +- .../authority-discovery/src/worker/tests.rs | 9 +- client/chain-spec/Cargo.toml | 2 +- client/chain-spec/src/chain_spec.rs | 2 +- client/chain-spec/src/lib.rs | 2 +- client/cli/src/arg_enums.rs | 20 +- client/cli/src/params/network_params.rs | 8 +- client/cli/src/params/node_key_params.rs | 14 +- .../consensus/beefy/src/communication/mod.rs | 5 +- .../incoming_requests_handler.rs | 6 +- .../outgoing_requests_engine.rs | 5 +- client/consensus/beefy/src/lib.rs | 3 +- .../grandpa/src/communication/gossip.rs | 7 +- .../grandpa/src/communication/mod.rs | 9 +- .../grandpa/src/communication/tests.rs | 16 +- client/consensus/grandpa/src/lib.rs | 10 +- client/informant/Cargo.toml | 1 + client/informant/src/display.rs | 10 +- client/informant/src/lib.rs | 3 +- client/network-gossip/Cargo.toml | 1 + client/network-gossip/src/bridge.rs | 18 +- client/network-gossip/src/lib.rs | 7 +- client/network-gossip/src/state_machine.rs | 14 +- client/network-gossip/src/validator.rs | 2 +- client/network/Cargo.toml | 1 + client/network/bitswap/Cargo.toml | 1 + client/network/bitswap/src/lib.rs | 9 +- client/network/common/Cargo.toml | 1 - client/network/common/src/config.rs | 702 ----------------- client/network/common/src/lib.rs | 7 +- .../network/common/src/request_responses.rs | 155 ---- .../network/common/src/{protocol => }/role.rs | 0 client/network/common/src/sync.rs | 2 +- client/network/common/src/sync/message.rs | 2 +- client/network/light/Cargo.toml | 1 + .../light/src/light_client_requests.rs | 8 +- .../src/light_client_requests/handler.rs | 2 +- client/network/src/behaviour.rs | 13 +- client/network/src/config.rs | 708 +++++++++++++++++- client/network/src/discovery.rs | 5 +- client/network/{common => }/src/error.rs | 3 +- .../{common/src/protocol => src}/event.rs | 6 +- client/network/src/lib.rs | 32 +- client/network/src/peer_info.rs | 4 +- client/network/src/protocol.rs | 26 +- client/network/src/protocol/message.rs | 2 +- .../src/protocol/notifications/behaviour.rs | 8 +- .../src/protocol/notifications/handler.rs | 15 +- .../protocol/notifications/upgrade/collec.rs | 2 +- .../notifications/upgrade/notifications.rs | 8 +- client/network/src/request_responses.rs | 160 +++- client/network/src/service.rs | 54 +- client/network/src/service/out_events.rs | 3 +- .../{common => }/src/service/signature.rs | 4 +- client/network/src/service/tests/mod.rs | 240 ------ .../src/service.rs => src/service/traits.rs} | 11 +- .../{common/src/protocol.rs => src/types.rs} | 9 +- client/network/{common => }/src/utils.rs | 3 + client/network/sync/Cargo.toml | 1 + .../network/sync/src/block_request_handler.rs | 7 +- client/network/sync/src/engine.rs | 14 +- client/network/sync/src/lib.rs | 44 +- client/network/sync/src/service/chain_sync.rs | 6 +- client/network/sync/src/service/mock.rs | 14 +- client/network/sync/src/service/network.rs | 8 +- .../network/sync/src/state_request_handler.rs | 5 +- .../network/sync/src/warp_request_handler.rs | 6 +- client/network/test/src/lib.rs | 16 +- .../service/tests => test/src}/service.rs | 245 +++++- client/network/transactions/Cargo.toml | 1 + client/network/transactions/src/lib.rs | 15 +- client/offchain/Cargo.toml | 1 + client/offchain/src/api.rs | 6 +- client/offchain/src/lib.rs | 4 +- client/rpc/src/system/tests.rs | 2 +- client/service/src/builder.rs | 11 +- client/service/src/config.rs | 10 +- client/service/src/error.rs | 2 +- client/service/src/lib.rs | 6 +- client/service/src/metrics.rs | 7 +- client/service/test/src/lib.rs | 7 +- 85 files changed, 1411 insertions(+), 1434 deletions(-) delete mode 100644 client/network/common/src/config.rs delete mode 100644 client/network/common/src/request_responses.rs rename client/network/common/src/{protocol => }/role.rs (100%) rename client/network/{common => }/src/error.rs (97%) rename client/network/{common/src/protocol => src}/event.rs (97%) rename client/network/{common => }/src/service/signature.rs (96%) delete mode 100644 client/network/src/service/tests/mod.rs rename client/network/{common/src/service.rs => src/service/traits.rs} (99%) rename client/network/{common/src/protocol.rs => src/types.rs} (99%) rename client/network/{common => }/src/utils.rs (98%) rename client/network/{src/service/tests => test/src}/service.rs (72%) diff --git a/Cargo.lock b/Cargo.lock index 9a63e25b4..4aac7f3c7 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -8101,6 +8101,7 @@ dependencies = [ "quickcheck", "rand 0.8.5", "sc-client-api", + "sc-network", "sc-network-common", "sp-api", "sp-authority-discovery", @@ -8163,7 +8164,7 @@ dependencies = [ "sc-chain-spec-derive", "sc-client-api", "sc-executor", - "sc-network-common", + "sc-network", "sc-telemetry", "serde", "serde_json", @@ -8751,6 +8752,7 @@ dependencies = [ "futures-timer", "log", "sc-client-api", + "sc-network", "sc-network-common", "sp-blockchain", "sp-runtime", @@ -8787,6 +8789,7 @@ dependencies = [ "futures-timer", "ip_network", "libp2p", + "linked_hash_set", "log", "lru", "mockall", @@ -8838,6 +8841,7 @@ dependencies = [ "sc-block-builder", "sc-client-api", "sc-consensus", + "sc-network", "sc-network-common", "sp-blockchain", "sp-consensus", @@ -8861,7 +8865,6 @@ dependencies = [ "futures", "futures-timer", "libp2p", - "linked_hash_set", "parity-scale-codec", "prost-build", "sc-consensus", @@ -8890,6 +8893,7 @@ dependencies = [ "log", "lru", "quickcheck", + "sc-network", "sc-network-common", "sc-peerset", "sp-runtime", @@ -8911,6 +8915,7 @@ dependencies = [ "prost", "prost-build", "sc-client-api", + "sc-network", "sc-network-common", "sc-peerset", "sp-blockchain", @@ -8939,6 +8944,7 @@ dependencies = [ "sc-block-builder", "sc-client-api", "sc-consensus", + "sc-network", "sc-network-common", "sc-peerset", "sc-utils", @@ -8997,6 +9003,7 @@ dependencies = [ "log", "parity-scale-codec", "pin-project", + "sc-network", "sc-network-common", "sc-peerset", "sc-utils", @@ -9026,6 +9033,7 @@ dependencies = [ "sc-block-builder", "sc-client-api", "sc-client-db", + "sc-network", "sc-network-common", "sc-peerset", "sc-transaction-pool", diff --git a/bin/node/cli/src/service.rs b/bin/node/cli/src/service.rs index e9a34b2a5..3c9716c08 100644 --- a/bin/node/cli/src/service.rs +++ b/bin/node/cli/src/service.rs @@ -31,10 +31,8 @@ use node_primitives::Block; use sc_client_api::BlockBackend; use sc_consensus_babe::{self, SlotProportion}; use sc_executor::NativeElseWasmExecutor; -use sc_network::NetworkService; -use sc_network_common::{ - protocol::event::Event, service::NetworkEventStream, sync::warp::WarpSyncParams, -}; +use sc_network::{event::Event, NetworkEventStream, NetworkService}; +use sc_network_common::sync::warp::WarpSyncParams; use sc_network_sync::SyncingService; use sc_service::{config::Configuration, error::Error as ServiceError, RpcHandlers, TaskManager}; use sc_telemetry::{Telemetry, TelemetryWorker}; diff --git a/client/authority-discovery/Cargo.toml b/client/authority-discovery/Cargo.toml index b37507662..900d9c59d 100644 --- a/client/authority-discovery/Cargo.toml +++ b/client/authority-discovery/Cargo.toml @@ -28,6 +28,7 @@ rand = "0.8.5" thiserror = "1.0" prometheus-endpoint = { package = "substrate-prometheus-endpoint", version = "0.10.0-dev", path = "../../utils/prometheus" } sc-client-api = { version = "4.0.0-dev", path = "../api" } +sc-network = { version = "0.10.0-dev", path = "../network/" } sc-network-common = { version = "0.10.0-dev", path = "../network/common" } sp-api = { version = "4.0.0-dev", path = "../../primitives/api" } sp-authority-discovery = { version = "4.0.0-dev", path = "../../primitives/authority-discovery" } diff --git a/client/authority-discovery/src/lib.rs b/client/authority-discovery/src/lib.rs index 579b9d8b1..a3c669909 100644 --- a/client/authority-discovery/src/lib.rs +++ b/client/authority-discovery/src/lib.rs @@ -40,7 +40,7 @@ use futures::{ }; use libp2p::{Multiaddr, PeerId}; -use sc_network_common::protocol::event::DhtEvent; +use sc_network::event::DhtEvent; use sp_authority_discovery::AuthorityId; use sp_blockchain::HeaderBackend; use sp_runtime::traits::Block as BlockT; diff --git a/client/authority-discovery/src/worker.rs b/client/authority-discovery/src/worker.rs index bc5fe2848..034d72902 100644 --- a/client/authority-discovery/src/worker.rs +++ b/client/authority-discovery/src/worker.rs @@ -43,9 +43,8 @@ use log::{debug, error, log_enabled}; use prometheus_endpoint::{register, Counter, CounterVec, Gauge, Opts, U64}; use prost::Message; use rand::{seq::SliceRandom, thread_rng}; -use sc_network_common::{ - protocol::event::DhtEvent, - service::{KademliaKey, NetworkDHTProvider, NetworkSigner, NetworkStateInfo, Signature}, +use sc_network::{ + event::DhtEvent, KademliaKey, NetworkDHTProvider, NetworkSigner, NetworkStateInfo, Signature, }; use sp_api::{ApiError, ProvideRuntimeApi}; use sp_authority_discovery::{ diff --git a/client/authority-discovery/src/worker/tests.rs b/client/authority-discovery/src/worker/tests.rs index 22f984d97..febe40657 100644 --- a/client/authority-discovery/src/worker/tests.rs +++ b/client/authority-discovery/src/worker/tests.rs @@ -29,11 +29,16 @@ use futures::{ sink::SinkExt, task::LocalSpawn, }; -use libp2p::{core::multiaddr, identity::Keypair, PeerId}; +use libp2p::{ + core::multiaddr, + identity::{error::SigningError, Keypair}, + kad::record::Key as KademliaKey, + PeerId, +}; use prometheus_endpoint::prometheus::default_registry; use sc_client_api::HeaderBackend; -use sc_network_common::service::{KademliaKey, Signature, SigningError}; +use sc_network::Signature; use sp_api::{ApiRef, ProvideRuntimeApi}; use sp_keystore::{testing::KeyStore, CryptoStore}; use sp_runtime::traits::{Block as BlockT, NumberFor, Zero}; diff --git a/client/chain-spec/Cargo.toml b/client/chain-spec/Cargo.toml index 6168a897c..b1188b3bd 100644 --- a/client/chain-spec/Cargo.toml +++ b/client/chain-spec/Cargo.toml @@ -19,7 +19,7 @@ serde_json = "1.0.85" sc-client-api = { version = "4.0.0-dev", path = "../api" } sc-chain-spec-derive = { version = "4.0.0-dev", path = "./derive" } sc-executor = { version = "0.10.0-dev", path = "../executor" } -sc-network-common = { version = "0.10.0-dev", path = "../network/common" } +sc-network = { version = "0.10.0-dev", path = "../network" } sc-telemetry = { version = "4.0.0-dev", path = "../telemetry" } sp-blockchain = { version = "4.0.0-dev", path = "../../primitives/blockchain" } sp-core = { version = "7.0.0", path = "../../primitives/core" } diff --git a/client/chain-spec/src/chain_spec.rs b/client/chain-spec/src/chain_spec.rs index 9eed5bbbc..96e36d839 100644 --- a/client/chain-spec/src/chain_spec.rs +++ b/client/chain-spec/src/chain_spec.rs @@ -20,7 +20,7 @@ #![warn(missing_docs)] use crate::{extension::GetExtension, ChainType, Properties, RuntimeGenesis}; -use sc_network_common::config::MultiaddrWithPeerId; +use sc_network::config::MultiaddrWithPeerId; use sc_telemetry::TelemetryEndpoints; use serde::{Deserialize, Serialize}; use serde_json as json; diff --git a/client/chain-spec/src/lib.rs b/client/chain-spec/src/lib.rs index e43a24796..6239eb732 100644 --- a/client/chain-spec/src/lib.rs +++ b/client/chain-spec/src/lib.rs @@ -189,7 +189,7 @@ pub use self::{ }; pub use sc_chain_spec_derive::{ChainSpecExtension, ChainSpecGroup}; -use sc_network_common::config::MultiaddrWithPeerId; +use sc_network::config::MultiaddrWithPeerId; use sc_telemetry::TelemetryEndpoints; use serde::{de::DeserializeOwned, Serialize}; use sp_core::storage::Storage; diff --git a/client/cli/src/arg_enums.rs b/client/cli/src/arg_enums.rs index 472c1722f..c3399a896 100644 --- a/client/cli/src/arg_enums.rs +++ b/client/cli/src/arg_enums.rs @@ -251,19 +251,15 @@ pub enum SyncMode { Warp, } -impl Into for SyncMode { - fn into(self) -> sc_network_common::config::SyncMode { +impl Into for SyncMode { + fn into(self) -> sc_network::config::SyncMode { match self { - SyncMode::Full => sc_network_common::config::SyncMode::Full, - SyncMode::Fast => sc_network_common::config::SyncMode::Fast { - skip_proofs: false, - storage_chain_mode: false, - }, - SyncMode::FastUnsafe => sc_network_common::config::SyncMode::Fast { - skip_proofs: true, - storage_chain_mode: false, - }, - SyncMode::Warp => sc_network_common::config::SyncMode::Warp, + SyncMode::Full => sc_network::config::SyncMode::Full, + SyncMode::Fast => + sc_network::config::SyncMode::Fast { skip_proofs: false, storage_chain_mode: false }, + SyncMode::FastUnsafe => + sc_network::config::SyncMode::Fast { skip_proofs: true, storage_chain_mode: false }, + SyncMode::Warp => sc_network::config::SyncMode::Warp, } } } diff --git a/client/cli/src/params/network_params.rs b/client/cli/src/params/network_params.rs index 31b761938..106fba75a 100644 --- a/client/cli/src/params/network_params.rs +++ b/client/cli/src/params/network_params.rs @@ -18,8 +18,12 @@ use crate::{arg_enums::SyncMode, params::node_key_params::NodeKeyParams}; use clap::Args; -use sc_network::{config::NetworkConfiguration, multiaddr::Protocol}; -use sc_network_common::config::{NodeKeyConfig, NonReservedPeerMode, SetConfig, TransportConfig}; +use sc_network::{ + config::{ + NetworkConfiguration, NodeKeyConfig, NonReservedPeerMode, SetConfig, TransportConfig, + }, + multiaddr::Protocol, +}; use sc_service::{ config::{Multiaddr, MultiaddrWithPeerId}, ChainSpec, ChainType, diff --git a/client/cli/src/params/node_key_params.rs b/client/cli/src/params/node_key_params.rs index d470ef1fa..074b95bea 100644 --- a/client/cli/src/params/node_key_params.rs +++ b/client/cli/src/params/node_key_params.rs @@ -17,7 +17,7 @@ // along with this program. If not, see . use clap::Args; -use sc_network_common::config::{identity::ed25519, NodeKeyConfig}; +use sc_network::config::{identity::ed25519, NodeKeyConfig}; use sp_core::H256; use std::{path::PathBuf, str::FromStr}; @@ -92,7 +92,7 @@ impl NodeKeyParams { let secret = if let Some(node_key) = self.node_key.as_ref() { parse_ed25519_secret(node_key)? } else { - sc_network_common::config::Secret::File( + sc_network::config::Secret::File( self.node_key_file .clone() .unwrap_or_else(|| net_config_dir.join(NODE_KEY_ED25519_FILE)), @@ -111,10 +111,10 @@ fn invalid_node_key(e: impl std::fmt::Display) -> error::Error { } /// Parse a Ed25519 secret key from a hex string into a `sc_network::Secret`. -fn parse_ed25519_secret(hex: &str) -> error::Result { +fn parse_ed25519_secret(hex: &str) -> error::Result { H256::from_str(hex).map_err(invalid_node_key).and_then(|bytes| { ed25519::SecretKey::from_bytes(bytes) - .map(sc_network_common::config::Secret::Input) + .map(sc_network::config::Secret::Input) .map_err(invalid_node_key) }) } @@ -123,7 +123,7 @@ fn parse_ed25519_secret(hex: &str) -> error::Result Ok(()), _ => Err(error::Error::Input("Unexpected node key config".into())), @@ -200,7 +200,7 @@ mod tests { let dir = PathBuf::from(net_config_dir.clone()); let typ = params.node_key_type; params.node_key(net_config_dir).and_then(move |c| match c { - NodeKeyConfig::Ed25519(sc_network_common::config::Secret::File(ref f)) + NodeKeyConfig::Ed25519(sc_network::config::Secret::File(ref f)) if typ == NodeKeyType::Ed25519 && f == &dir.join(NODE_KEY_ED25519_FILE) => Ok(()), _ => Err(error::Error::Input("Unexpected node key config".into())), diff --git a/client/consensus/beefy/src/communication/mod.rs b/client/consensus/beefy/src/communication/mod.rs index f91b08bbc..295d549bb 100644 --- a/client/consensus/beefy/src/communication/mod.rs +++ b/client/consensus/beefy/src/communication/mod.rs @@ -67,9 +67,8 @@ pub(crate) mod beefy_protocol_name { /// For standard protocol name see [`beefy_protocol_name::gossip_protocol_name`]. pub fn beefy_peers_set_config( gossip_protocol_name: sc_network::ProtocolName, -) -> sc_network_common::config::NonDefaultSetConfig { - let mut cfg = - sc_network_common::config::NonDefaultSetConfig::new(gossip_protocol_name, 1024 * 1024); +) -> sc_network::config::NonDefaultSetConfig { + let mut cfg = sc_network::config::NonDefaultSetConfig::new(gossip_protocol_name, 1024 * 1024); cfg.allow_non_reserved(25, 25); cfg } diff --git a/client/consensus/beefy/src/communication/request_response/incoming_requests_handler.rs b/client/consensus/beefy/src/communication/request_response/incoming_requests_handler.rs index ab6c21a18..1670e9982 100644 --- a/client/consensus/beefy/src/communication/request_response/incoming_requests_handler.rs +++ b/client/consensus/beefy/src/communication/request_response/incoming_requests_handler.rs @@ -23,8 +23,10 @@ use futures::{ }; use log::{debug, trace}; use sc_client_api::BlockBackend; -use sc_network::{config as netconfig, config::RequestResponseConfig, PeerId, ReputationChange}; -use sc_network_common::protocol::ProtocolName; +use sc_network::{ + config as netconfig, config::RequestResponseConfig, types::ProtocolName, PeerId, + ReputationChange, +}; use sp_consensus_beefy::BEEFY_ENGINE_ID; use sp_runtime::traits::Block; use std::{marker::PhantomData, sync::Arc}; diff --git a/client/consensus/beefy/src/communication/request_response/outgoing_requests_engine.rs b/client/consensus/beefy/src/communication/request_response/outgoing_requests_engine.rs index 7450d4b32..c642e3dfe 100644 --- a/client/consensus/beefy/src/communication/request_response/outgoing_requests_engine.rs +++ b/client/consensus/beefy/src/communication/request_response/outgoing_requests_engine.rs @@ -22,10 +22,9 @@ use codec::Encode; use futures::channel::{oneshot, oneshot::Canceled}; use log::{debug, warn}; use parking_lot::Mutex; -use sc_network::{PeerId, ProtocolName}; -use sc_network_common::{ +use sc_network::{ request_responses::{IfDisconnected, RequestFailure}, - service::NetworkRequest, + NetworkRequest, PeerId, ProtocolName, }; use sp_consensus_beefy::{crypto::AuthorityId, ValidatorSet}; use sp_runtime::traits::{Block, NumberFor}; diff --git a/client/consensus/beefy/src/lib.rs b/client/consensus/beefy/src/lib.rs index 67758a497..eb56a97de 100644 --- a/client/consensus/beefy/src/lib.rs +++ b/client/consensus/beefy/src/lib.rs @@ -38,8 +38,7 @@ use parking_lot::Mutex; use prometheus::Registry; use sc_client_api::{Backend, BlockBackend, BlockchainEvents, FinalityNotifications, Finalizer}; use sc_consensus::BlockImport; -use sc_network::ProtocolName; -use sc_network_common::service::NetworkRequest; +use sc_network::{NetworkRequest, ProtocolName}; use sc_network_gossip::{GossipEngine, Network as GossipNetwork, Syncing as GossipSyncing}; use sp_api::{HeaderT, NumberFor, ProvideRuntimeApi}; use sp_blockchain::{ diff --git a/client/consensus/grandpa/src/communication/gossip.rs b/client/consensus/grandpa/src/communication/gossip.rs index 0a3029e25..2c0fe3d85 100644 --- a/client/consensus/grandpa/src/communication/gossip.rs +++ b/client/consensus/grandpa/src/communication/gossip.rs @@ -91,7 +91,7 @@ use parity_scale_codec::{Decode, Encode}; use prometheus_endpoint::{register, CounterVec, Opts, PrometheusError, Registry, U64}; use rand::seq::SliceRandom; use sc_network::{PeerId, ReputationChange}; -use sc_network_common::protocol::role::ObservedRole; +use sc_network_common::role::ObservedRole; use sc_network_gossip::{MessageIntent, ValidatorContext}; use sc_telemetry::{telemetry, TelemetryHandle, CONSENSUS_DEBUG}; use sc_utils::mpsc::{tracing_unbounded, TracingUnboundedReceiver, TracingUnboundedSender}; @@ -820,7 +820,7 @@ impl Inner { fn note_set(&mut self, set_id: SetId, authorities: Vec) -> MaybeMessage { let local_view = match self.local_view { ref mut x @ None => x.get_or_insert(LocalView::new(set_id, Round(1))), - Some(ref mut v) => + Some(ref mut v) => { if v.set_id == set_id { let diff_authorities = self.authorities.iter().collect::>() != authorities.iter().collect::>(); @@ -841,7 +841,8 @@ impl Inner { return None } else { v - }, + } + }, }; local_view.update_set(set_id); diff --git a/client/consensus/grandpa/src/communication/mod.rs b/client/consensus/grandpa/src/communication/mod.rs index 6c7e3ea46..3896bc360 100644 --- a/client/consensus/grandpa/src/communication/mod.rs +++ b/client/consensus/grandpa/src/communication/mod.rs @@ -46,7 +46,7 @@ use finality_grandpa::{ Message::{Precommit, Prevote, PrimaryPropose}, }; use parity_scale_codec::{Decode, Encode}; -use sc_network::ReputationChange; +use sc_network::{NetworkBlock, NetworkSyncForkRequest, ReputationChange}; use sc_network_gossip::{GossipEngine, Network as GossipNetwork}; use sc_telemetry::{telemetry, TelemetryHandle, CONSENSUS_DEBUG, CONSENSUS_INFO}; use sp_keystore::SyncCryptoStorePtr; @@ -59,10 +59,7 @@ use crate::{ use gossip::{ FullCatchUpMessage, FullCommitMessage, GossipMessage, GossipValidator, PeerReport, VoteMessage, }; -use sc_network_common::{ - service::{NetworkBlock, NetworkSyncForkRequest}, - sync::SyncEventStream, -}; +use sc_network_common::sync::SyncEventStream; use sc_utils::mpsc::TracingUnboundedReceiver; use sp_consensus_grandpa::{AuthorityId, AuthoritySignature, RoundNumber, SetId as SetIdNumber}; @@ -77,7 +74,7 @@ pub(crate) const NEIGHBOR_REBROADCAST_PERIOD: Duration = Duration::from_secs(2 * pub mod grandpa_protocol_name { use sc_chain_spec::ChainSpec; - use sc_network_common::protocol::ProtocolName; + use sc_network::types::ProtocolName; pub(crate) const NAME: &str = "/grandpa/1"; /// Old names for the notifications protocol, used for backward compatibility. diff --git a/client/consensus/grandpa/src/communication/tests.rs b/client/consensus/grandpa/src/communication/tests.rs index 843e54679..f97b1f1e8 100644 --- a/client/consensus/grandpa/src/communication/tests.rs +++ b/client/consensus/grandpa/src/communication/tests.rs @@ -25,14 +25,16 @@ use super::{ use crate::{communication::grandpa_protocol_name, environment::SharedVoterSetState}; use futures::prelude::*; use parity_scale_codec::Encode; -use sc_network::{config::Role, Multiaddr, PeerId, ReputationChange}; +use sc_network::{ + config::{MultiaddrWithPeerId, Role}, + event::Event as NetworkEvent, + types::ProtocolName, + Multiaddr, NetworkBlock, NetworkEventStream, NetworkNotification, NetworkPeers, + NetworkSyncForkRequest, NotificationSenderError, NotificationSenderT as NotificationSender, + PeerId, ReputationChange, +}; use sc_network_common::{ - config::MultiaddrWithPeerId, - protocol::{event::Event as NetworkEvent, role::ObservedRole, ProtocolName}, - service::{ - NetworkBlock, NetworkEventStream, NetworkNotification, NetworkPeers, - NetworkSyncForkRequest, NotificationSender, NotificationSenderError, - }, + role::ObservedRole, sync::{SyncEvent as SyncStreamEvent, SyncEventStream}, }; use sc_network_gossip::Validator; diff --git a/client/consensus/grandpa/src/lib.rs b/client/consensus/grandpa/src/lib.rs index 7bf48a498..2baa13508 100644 --- a/client/consensus/grandpa/src/lib.rs +++ b/client/consensus/grandpa/src/lib.rs @@ -68,7 +68,7 @@ use sc_client_api::{ StorageProvider, TransactionFor, }; use sc_consensus::BlockImport; -use sc_network_common::protocol::ProtocolName; +use sc_network::types::ProtocolName; use sc_telemetry::{telemetry, TelemetryHandle, CONSENSUS_DEBUG, CONSENSUS_INFO}; use sc_utils::mpsc::{tracing_unbounded, TracingUnboundedReceiver}; use sp_api::ProvideRuntimeApi; @@ -695,19 +695,19 @@ pub struct GrandpaParams { /// For standard protocol name see [`crate::protocol_standard_name`]. pub fn grandpa_peers_set_config( protocol_name: ProtocolName, -) -> sc_network_common::config::NonDefaultSetConfig { +) -> sc_network::config::NonDefaultSetConfig { use communication::grandpa_protocol_name; - sc_network_common::config::NonDefaultSetConfig { + sc_network::config::NonDefaultSetConfig { notifications_protocol: protocol_name, fallback_names: grandpa_protocol_name::LEGACY_NAMES.iter().map(|&n| n.into()).collect(), // Notifications reach ~256kiB in size at the time of writing on Kusama and Polkadot. max_notification_size: 1024 * 1024, handshake: None, - set_config: sc_network_common::config::SetConfig { + set_config: sc_network::config::SetConfig { in_peers: 0, out_peers: 0, reserved_nodes: Vec::new(), - non_reserved_mode: sc_network_common::config::NonReservedPeerMode::Deny, + non_reserved_mode: sc_network::config::NonReservedPeerMode::Deny, }, } } diff --git a/client/informant/Cargo.toml b/client/informant/Cargo.toml index ca0ae03af..cd84dcb5a 100644 --- a/client/informant/Cargo.toml +++ b/client/informant/Cargo.toml @@ -19,5 +19,6 @@ futures-timer = "3.0.1" log = "0.4.17" sc-client-api = { version = "4.0.0-dev", path = "../api" } sc-network-common = { version = "0.10.0-dev", path = "../network/common" } +sc-network = { version = "0.10.0-dev", path = "../network" } sp-blockchain = { version = "4.0.0-dev", path = "../../primitives/blockchain" } sp-runtime = { version = "7.0.0", path = "../../primitives/runtime" } diff --git a/client/informant/src/display.rs b/client/informant/src/display.rs index fc68e5603..46e722927 100644 --- a/client/informant/src/display.rs +++ b/client/informant/src/display.rs @@ -20,12 +20,10 @@ use crate::OutputFormat; use ansi_term::Colour; use log::info; use sc_client_api::ClientInfo; -use sc_network_common::{ - service::NetworkStatus, - sync::{ - warp::{WarpSyncPhase, WarpSyncProgress}, - SyncState, SyncStatus, - }, +use sc_network::NetworkStatus; +use sc_network_common::sync::{ + warp::{WarpSyncPhase, WarpSyncProgress}, + SyncState, SyncStatus, }; use sp_runtime::traits::{Block as BlockT, CheckedDiv, NumberFor, Saturating, Zero}; use std::{fmt, time::Instant}; diff --git a/client/informant/src/lib.rs b/client/informant/src/lib.rs index dc6aebc2e..03f907505 100644 --- a/client/informant/src/lib.rs +++ b/client/informant/src/lib.rs @@ -23,7 +23,8 @@ use futures::prelude::*; use futures_timer::Delay; use log::{debug, info, trace}; use sc_client_api::{BlockchainEvents, UsageProvider}; -use sc_network_common::{service::NetworkStatusProvider, sync::SyncStatusProvider}; +use sc_network::NetworkStatusProvider; +use sc_network_common::sync::SyncStatusProvider; use sp_blockchain::HeaderMetadata; use sp_runtime::traits::{Block as BlockT, Header}; use std::{collections::VecDeque, fmt::Display, sync::Arc, time::Duration}; diff --git a/client/network-gossip/Cargo.toml b/client/network-gossip/Cargo.toml index 7811e86c0..5c1bc91f1 100644 --- a/client/network-gossip/Cargo.toml +++ b/client/network-gossip/Cargo.toml @@ -22,6 +22,7 @@ log = "0.4.17" lru = "0.8.1" tracing = "0.1.29" prometheus-endpoint = { package = "substrate-prometheus-endpoint", version = "0.10.0-dev", path = "../../utils/prometheus" } +sc-network = { version = "0.10.0-dev", path = "../network/" } sc-network-common = { version = "0.10.0-dev", path = "../network/common" } sc-peerset = { version = "4.0.0-dev", path = "../peerset" } sp-runtime = { version = "7.0.0", path = "../../primitives/runtime" } diff --git a/client/network-gossip/src/bridge.rs b/client/network-gossip/src/bridge.rs index 7368cc770..4793d7822 100644 --- a/client/network-gossip/src/bridge.rs +++ b/client/network-gossip/src/bridge.rs @@ -21,10 +21,8 @@ use crate::{ Network, Syncing, Validator, }; -use sc_network_common::{ - protocol::{event::Event, ProtocolName}, - sync::SyncEvent, -}; +use sc_network::{event::Event, types::ProtocolName}; +use sc_network_common::sync::SyncEvent; use sc_peerset::ReputationChange; use futures::{ @@ -340,15 +338,11 @@ mod tests { future::poll_fn, }; use quickcheck::{Arbitrary, Gen, QuickCheck}; - use sc_network_common::{ - config::MultiaddrWithPeerId, - protocol::role::ObservedRole, - service::{ - NetworkBlock, NetworkEventStream, NetworkNotification, NetworkPeers, - NotificationSender, NotificationSenderError, - }, - sync::SyncEventStream, + use sc_network::{ + config::MultiaddrWithPeerId, NetworkBlock, NetworkEventStream, NetworkNotification, + NetworkPeers, NotificationSenderError, NotificationSenderT as NotificationSender, }; + use sc_network_common::{role::ObservedRole, sync::SyncEventStream}; use sp_runtime::{ testing::H256, traits::{Block as BlockT, NumberFor}, diff --git a/client/network-gossip/src/lib.rs b/client/network-gossip/src/lib.rs index e3448ba01..ef87dd599 100644 --- a/client/network-gossip/src/lib.rs +++ b/client/network-gossip/src/lib.rs @@ -68,11 +68,10 @@ pub use self::{ }; use libp2p::{multiaddr, PeerId}; -use sc_network_common::{ - protocol::ProtocolName, - service::{NetworkBlock, NetworkEventStream, NetworkNotification, NetworkPeers}, - sync::SyncEventStream, +use sc_network::{ + types::ProtocolName, NetworkBlock, NetworkEventStream, NetworkNotification, NetworkPeers, }; +use sc_network_common::sync::SyncEventStream; use sp_runtime::traits::{Block as BlockT, NumberFor}; use std::iter; diff --git a/client/network-gossip/src/state_machine.rs b/client/network-gossip/src/state_machine.rs index 3a0d5fc0d..e6d2b0e2a 100644 --- a/client/network-gossip/src/state_machine.rs +++ b/client/network-gossip/src/state_machine.rs @@ -22,7 +22,8 @@ use ahash::AHashSet; use libp2p::PeerId; use lru::LruCache; use prometheus_endpoint::{register, Counter, PrometheusError, Registry, U64}; -use sc_network_common::protocol::{role::ObservedRole, ProtocolName}; +use sc_network::types::ProtocolName; +use sc_network_common::role::ObservedRole; use sp_runtime::traits::{Block as BlockT, Hash, HashFor}; use std::{collections::HashMap, iter, num::NonZeroUsize, sync::Arc, time, time::Instant}; @@ -525,13 +526,10 @@ mod tests { use super::*; use crate::multiaddr::Multiaddr; use futures::prelude::*; - use sc_network_common::{ - config::MultiaddrWithPeerId, - protocol::event::Event, - service::{ - NetworkBlock, NetworkEventStream, NetworkNotification, NetworkPeers, - NotificationSender, NotificationSenderError, - }, + use sc_network::{ + config::MultiaddrWithPeerId, event::Event, NetworkBlock, NetworkEventStream, + NetworkNotification, NetworkPeers, NotificationSenderError, + NotificationSenderT as NotificationSender, }; use sc_peerset::ReputationChange; use sp_runtime::{ diff --git a/client/network-gossip/src/validator.rs b/client/network-gossip/src/validator.rs index 26835a5ae..2272efba5 100644 --- a/client/network-gossip/src/validator.rs +++ b/client/network-gossip/src/validator.rs @@ -17,7 +17,7 @@ // along with this program. If not, see . use libp2p::PeerId; -use sc_network_common::protocol::role::ObservedRole; +use sc_network_common::role::ObservedRole; use sp_runtime::traits::Block as BlockT; /// Validates consensus messages. diff --git a/client/network/Cargo.toml b/client/network/Cargo.toml index 5a918bebd..90b5ce871 100644 --- a/client/network/Cargo.toml +++ b/client/network/Cargo.toml @@ -26,6 +26,7 @@ futures = "0.3.21" futures-timer = "3.0.2" ip_network = "0.4.1" libp2p = { version = "0.50.0", features = ["dns", "identify", "kad", "macros", "mdns", "mplex", "noise", "ping", "tcp", "tokio", "yamux", "websocket"] } +linked_hash_set = "0.1.3" log = "0.4.17" lru = "0.8.1" mockall = "0.11.3" diff --git a/client/network/bitswap/Cargo.toml b/client/network/bitswap/Cargo.toml index 0bbb5ff09..ee2e0cfc7 100644 --- a/client/network/bitswap/Cargo.toml +++ b/client/network/bitswap/Cargo.toml @@ -24,6 +24,7 @@ prost = "0.11" thiserror = "1.0" unsigned-varint = { version = "0.7.1", features = ["futures", "asynchronous_codec"] } sc-client-api = { version = "4.0.0-dev", path = "../../api" } +sc-network = { version = "0.10.0-dev", path = "../" } sc-network-common = { version = "0.10.0-dev", path = "../common" } sp-blockchain = { version = "4.0.0-dev", path = "../../../primitives/blockchain" } sp-runtime = { version = "7.0.0", path = "../../../primitives/runtime" } diff --git a/client/network/bitswap/src/lib.rs b/client/network/bitswap/src/lib.rs index dd5fc46f6..5a7a7b513 100644 --- a/client/network/bitswap/src/lib.rs +++ b/client/network/bitswap/src/lib.rs @@ -26,9 +26,9 @@ use libp2p::core::PeerId; use log::{debug, error, trace}; use prost::Message; use sc_client_api::BlockBackend; -use sc_network_common::{ - protocol::ProtocolName, +use sc_network::{ request_responses::{IncomingRequest, OutgoingResponse, ProtocolConfig}, + types::ProtocolName, }; use schema::bitswap::{ message::{wantlist::WantType, Block as MessageBlock, BlockPresence, BlockPresenceType}, @@ -127,8 +127,9 @@ impl BitswapRequestHandler { }; match pending_response.send(response) { - Ok(()) => - trace!(target: LOG_TARGET, "Handled bitswap request from {peer}.",), + Ok(()) => { + trace!(target: LOG_TARGET, "Handled bitswap request from {peer}.",) + }, Err(_) => debug!( target: LOG_TARGET, "Failed to handle light client request from {peer}: {}", diff --git a/client/network/common/Cargo.toml b/client/network/common/Cargo.toml index c5f9d6d4c..983342b01 100644 --- a/client/network/common/Cargo.toml +++ b/client/network/common/Cargo.toml @@ -26,7 +26,6 @@ codec = { package = "parity-scale-codec", version = "3.2.2", features = [ futures = "0.3.21" futures-timer = "3.0.2" libp2p = { version = "0.50.0", features = ["request-response", "kad"] } -linked_hash_set = "0.1.3" prometheus-endpoint = { package = "substrate-prometheus-endpoint", version = "0.10.0-dev", path = "../../../utils/prometheus" } smallvec = "1.8.0" sc-consensus = { version = "0.10.0-dev", path = "../../consensus/common" } diff --git a/client/network/common/src/config.rs b/client/network/common/src/config.rs deleted file mode 100644 index 6a02129fc..000000000 --- a/client/network/common/src/config.rs +++ /dev/null @@ -1,702 +0,0 @@ -// This file is part of Substrate. - -// Copyright (C) Parity Technologies (UK) Ltd. -// SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 - -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with this program. If not, see . - -//! Configuration of the networking layer. - -pub use crate::{ - protocol::{self, role::Role}, - request_responses::{ - IncomingRequest, OutgoingResponse, ProtocolConfig as RequestResponseConfig, - }, - sync::warp::WarpSyncProvider, - ExHashT, -}; -pub use libp2p::{build_multiaddr, core::PublicKey, identity}; - -use codec::Encode; -use libp2p::{ - identity::{ed25519, Keypair}, - multiaddr, Multiaddr, PeerId, -}; -use zeroize::Zeroize; - -use std::{ - error::Error, - fmt, fs, - io::{self, Write}, - iter, - net::Ipv4Addr, - path::{Path, PathBuf}, - str, - str::FromStr, -}; - -/// Protocol name prefix, transmitted on the wire for legacy protocol names. -/// I.e., `dot` in `/dot/sync/2`. Should be unique for each chain. Always UTF-8. -/// Deprecated in favour of genesis hash & fork ID based protocol names. -#[derive(Clone, PartialEq, Eq, Hash)] -pub struct ProtocolId(smallvec::SmallVec<[u8; 6]>); - -impl<'a> From<&'a str> for ProtocolId { - fn from(bytes: &'a str) -> ProtocolId { - Self(bytes.as_bytes().into()) - } -} - -impl AsRef for ProtocolId { - fn as_ref(&self) -> &str { - str::from_utf8(&self.0[..]) - .expect("the only way to build a ProtocolId is through a UTF-8 String; qed") - } -} - -impl fmt::Debug for ProtocolId { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - fmt::Debug::fmt(self.as_ref(), f) - } -} - -/// Parses a string address and splits it into Multiaddress and PeerId, if -/// valid. -/// -/// # Example -/// -/// ``` -/// # use libp2p::{Multiaddr, PeerId}; -/// # use sc_network_common::config::parse_str_addr; -/// let (peer_id, addr) = parse_str_addr( -/// "/ip4/198.51.100.19/tcp/30333/p2p/QmSk5HQbn6LhUwDiNMseVUjuRYhEtYj4aUZ6WfWoGURpdV" -/// ).unwrap(); -/// assert_eq!(peer_id, "QmSk5HQbn6LhUwDiNMseVUjuRYhEtYj4aUZ6WfWoGURpdV".parse::().unwrap()); -/// assert_eq!(addr, "/ip4/198.51.100.19/tcp/30333".parse::().unwrap()); -/// ``` -pub fn parse_str_addr(addr_str: &str) -> Result<(PeerId, Multiaddr), ParseErr> { - let addr: Multiaddr = addr_str.parse()?; - parse_addr(addr) -} - -/// Splits a Multiaddress into a Multiaddress and PeerId. -pub fn parse_addr(mut addr: Multiaddr) -> Result<(PeerId, Multiaddr), ParseErr> { - let who = match addr.pop() { - Some(multiaddr::Protocol::P2p(key)) => - PeerId::from_multihash(key).map_err(|_| ParseErr::InvalidPeerId)?, - _ => return Err(ParseErr::PeerIdMissing), - }; - - Ok((who, addr)) -} - -/// Address of a node, including its identity. -/// -/// This struct represents a decoded version of a multiaddress that ends with `/p2p/`. -/// -/// # Example -/// -/// ``` -/// # use libp2p::{Multiaddr, PeerId}; -/// # use sc_network_common::config::MultiaddrWithPeerId; -/// let addr: MultiaddrWithPeerId = -/// "/ip4/198.51.100.19/tcp/30333/p2p/QmSk5HQbn6LhUwDiNMseVUjuRYhEtYj4aUZ6WfWoGURpdV".parse().unwrap(); -/// assert_eq!(addr.peer_id.to_base58(), "QmSk5HQbn6LhUwDiNMseVUjuRYhEtYj4aUZ6WfWoGURpdV"); -/// assert_eq!(addr.multiaddr.to_string(), "/ip4/198.51.100.19/tcp/30333"); -/// ``` -#[derive(Debug, Clone, serde::Serialize, serde::Deserialize, PartialEq)] -#[serde(try_from = "String", into = "String")] -pub struct MultiaddrWithPeerId { - /// Address of the node. - pub multiaddr: Multiaddr, - /// Its identity. - pub peer_id: PeerId, -} - -impl MultiaddrWithPeerId { - /// Concatenates the multiaddress and peer ID into one multiaddress containing both. - pub fn concat(&self) -> Multiaddr { - let proto = multiaddr::Protocol::P2p(From::from(self.peer_id)); - self.multiaddr.clone().with(proto) - } -} - -impl fmt::Display for MultiaddrWithPeerId { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - fmt::Display::fmt(&self.concat(), f) - } -} - -impl FromStr for MultiaddrWithPeerId { - type Err = ParseErr; - - fn from_str(s: &str) -> Result { - let (peer_id, multiaddr) = parse_str_addr(s)?; - Ok(Self { peer_id, multiaddr }) - } -} - -impl From for String { - fn from(ma: MultiaddrWithPeerId) -> String { - format!("{}", ma) - } -} - -impl TryFrom for MultiaddrWithPeerId { - type Error = ParseErr; - fn try_from(string: String) -> Result { - string.parse() - } -} - -/// Error that can be generated by `parse_str_addr`. -#[derive(Debug)] -pub enum ParseErr { - /// Error while parsing the multiaddress. - MultiaddrParse(multiaddr::Error), - /// Multihash of the peer ID is invalid. - InvalidPeerId, - /// The peer ID is missing from the address. - PeerIdMissing, -} - -impl fmt::Display for ParseErr { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - match self { - Self::MultiaddrParse(err) => write!(f, "{}", err), - Self::InvalidPeerId => write!(f, "Peer id at the end of the address is invalid"), - Self::PeerIdMissing => write!(f, "Peer id is missing from the address"), - } - } -} - -impl std::error::Error for ParseErr { - fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { - match self { - Self::MultiaddrParse(err) => Some(err), - Self::InvalidPeerId => None, - Self::PeerIdMissing => None, - } - } -} - -impl From for ParseErr { - fn from(err: multiaddr::Error) -> ParseErr { - Self::MultiaddrParse(err) - } -} - -/// Configuration for a set of nodes. -#[derive(Clone, Debug)] -pub struct SetConfig { - /// Maximum allowed number of incoming substreams related to this set. - pub in_peers: u32, - /// Number of outgoing substreams related to this set that we're trying to maintain. - pub out_peers: u32, - /// List of reserved node addresses. - pub reserved_nodes: Vec, - /// Whether nodes that aren't in [`SetConfig::reserved_nodes`] are accepted or automatically - /// refused. - pub non_reserved_mode: NonReservedPeerMode, -} - -impl Default for SetConfig { - fn default() -> Self { - Self { - in_peers: 25, - out_peers: 75, - reserved_nodes: Vec::new(), - non_reserved_mode: NonReservedPeerMode::Accept, - } - } -} - -/// Custom handshake for the notification protocol -#[derive(Debug, Clone)] -pub struct NotificationHandshake(Vec); - -impl NotificationHandshake { - /// Create new `NotificationHandshake` from an object that implements `Encode` - pub fn new(handshake: H) -> Self { - Self(handshake.encode()) - } - - /// Create new `NotificationHandshake` from raw bytes - pub fn from_bytes(bytes: Vec) -> Self { - Self(bytes) - } -} - -impl std::ops::Deref for NotificationHandshake { - type Target = Vec; - - fn deref(&self) -> &Self::Target { - &self.0 - } -} - -/// Extension to [`SetConfig`] for sets that aren't the default set. -/// -/// > **Note**: As new fields might be added in the future, please consider using the `new` method -/// > and modifiers instead of creating this struct manually. -#[derive(Clone, Debug)] -pub struct NonDefaultSetConfig { - /// Name of the notifications protocols of this set. A substream on this set will be - /// considered established once this protocol is open. - /// - /// > **Note**: This field isn't present for the default set, as this is handled internally - /// > by the networking code. - pub notifications_protocol: protocol::ProtocolName, - /// If the remote reports that it doesn't support the protocol indicated in the - /// `notifications_protocol` field, then each of these fallback names will be tried one by - /// one. - /// - /// If a fallback is used, it will be reported in - /// `sc_network::protocol::event::Event::NotificationStreamOpened::negotiated_fallback` - pub fallback_names: Vec, - /// Handshake of the protocol - /// - /// NOTE: Currently custom handshakes are not fully supported. See issue #5685 for more - /// details. This field is temporarily used to allow moving the hardcoded block announcement - /// protocol out of `protocol.rs`. - pub handshake: Option, - /// Maximum allowed size of single notifications. - pub max_notification_size: u64, - /// Base configuration. - pub set_config: SetConfig, -} - -impl NonDefaultSetConfig { - /// Creates a new [`NonDefaultSetConfig`]. Zero slots and accepts only reserved nodes. - pub fn new(notifications_protocol: protocol::ProtocolName, max_notification_size: u64) -> Self { - Self { - notifications_protocol, - max_notification_size, - fallback_names: Vec::new(), - handshake: None, - set_config: SetConfig { - in_peers: 0, - out_peers: 0, - reserved_nodes: Vec::new(), - non_reserved_mode: NonReservedPeerMode::Deny, - }, - } - } - - /// Modifies the configuration to allow non-reserved nodes. - pub fn allow_non_reserved(&mut self, in_peers: u32, out_peers: u32) { - self.set_config.in_peers = in_peers; - self.set_config.out_peers = out_peers; - self.set_config.non_reserved_mode = NonReservedPeerMode::Accept; - } - - /// Add a node to the list of reserved nodes. - pub fn add_reserved(&mut self, peer: MultiaddrWithPeerId) { - self.set_config.reserved_nodes.push(peer); - } - - /// Add a list of protocol names used for backward compatibility. - /// - /// See the explanations in [`NonDefaultSetConfig::fallback_names`]. - pub fn add_fallback_names(&mut self, fallback_names: Vec) { - self.fallback_names.extend(fallback_names); - } -} - -/// Configuration for the transport layer. -#[derive(Clone, Debug)] -pub enum TransportConfig { - /// Normal transport mode. - Normal { - /// If true, the network will use mDNS to discover other libp2p nodes on the local network - /// and connect to them if they support the same chain. - enable_mdns: bool, - - /// If true, allow connecting to private IPv4/IPv6 addresses (as defined in - /// [RFC1918](https://tools.ietf.org/html/rfc1918)). Irrelevant for addresses that have - /// been passed in `::sc_network::config::NetworkConfiguration::boot_nodes`. - allow_private_ip: bool, - }, - - /// Only allow connections within the same process. - /// Only addresses of the form `/memory/...` will be supported. - MemoryOnly, -} - -/// The policy for connections to non-reserved peers. -#[derive(Clone, Debug, PartialEq, Eq)] -pub enum NonReservedPeerMode { - /// Accept them. This is the default. - Accept, - /// Deny them. - Deny, -} - -impl NonReservedPeerMode { - /// Attempt to parse the peer mode from a string. - pub fn parse(s: &str) -> Option { - match s { - "accept" => Some(Self::Accept), - "deny" => Some(Self::Deny), - _ => None, - } - } -} - -/// Sync operation mode. -#[derive(Copy, Clone, Debug, Eq, PartialEq)] -pub enum SyncMode { - /// Full block download and verification. - Full, - /// Download blocks and the latest state. - Fast { - /// Skip state proof download and verification. - skip_proofs: bool, - /// Download indexed transactions for recent blocks. - storage_chain_mode: bool, - }, - /// Warp sync - verify authority set transitions and the latest state. - Warp, -} - -impl SyncMode { - /// Returns if `self` is [`Self::Warp`]. - pub fn is_warp(&self) -> bool { - matches!(self, Self::Warp) - } - - /// Returns if `self` is [`Self::Fast`]. - pub fn is_fast(&self) -> bool { - matches!(self, Self::Fast { .. }) - } -} - -impl Default for SyncMode { - fn default() -> Self { - Self::Full - } -} - -/// Network service configuration. -#[derive(Clone, Debug)] -pub struct NetworkConfiguration { - /// Directory path to store network-specific configuration. None means nothing will be saved. - pub net_config_path: Option, - /// Multiaddresses to listen for incoming connections. - pub listen_addresses: Vec, - /// Multiaddresses to advertise. Detected automatically if empty. - pub public_addresses: Vec, - /// List of initial node addresses - pub boot_nodes: Vec, - /// The node key configuration, which determines the node's network identity keypair. - pub node_key: NodeKeyConfig, - /// List of request-response protocols that the node supports. - pub request_response_protocols: Vec, - /// Configuration for the default set of nodes used for block syncing and transactions. - pub default_peers_set: SetConfig, - /// Number of substreams to reserve for full nodes for block syncing and transactions. - /// Any other slot will be dedicated to light nodes. - /// - /// This value is implicitly capped to `default_set.out_peers + default_set.in_peers`. - pub default_peers_set_num_full: u32, - /// Configuration for extra sets of nodes. - pub extra_sets: Vec, - /// Client identifier. Sent over the wire for debugging purposes. - pub client_version: String, - /// Name of the node. Sent over the wire for debugging purposes. - pub node_name: String, - /// Configuration for the transport layer. - pub transport: TransportConfig, - /// Maximum number of peers to ask the same blocks in parallel. - pub max_parallel_downloads: u32, - /// Initial syncing mode. - pub sync_mode: SyncMode, - - /// True if Kademlia random discovery should be enabled. - /// - /// If true, the node will automatically randomly walk the DHT in order to find new peers. - pub enable_dht_random_walk: bool, - - /// Should we insert non-global addresses into the DHT? - pub allow_non_globals_in_dht: bool, - - /// Require iterative Kademlia DHT queries to use disjoint paths for increased resiliency in - /// the presence of potentially adversarial nodes. - pub kademlia_disjoint_query_paths: bool, - /// Enable serving block data over IPFS bitswap. - pub ipfs_server: bool, - - /// Size of Yamux receive window of all substreams. `None` for the default (256kiB). - /// Any value less than 256kiB is invalid. - /// - /// # Context - /// - /// By design, notifications substreams on top of Yamux connections only allow up to `N` bytes - /// to be transferred at a time, where `N` is the Yamux receive window size configurable here. - /// This means, in practice, that every `N` bytes must be acknowledged by the receiver before - /// the sender can send more data. The maximum bandwidth of each notifications substream is - /// therefore `N / round_trip_time`. - /// - /// It is recommended to leave this to `None`, and use a request-response protocol instead if - /// a large amount of data must be transferred. The reason why the value is configurable is - /// that some Substrate users mis-use notification protocols to send large amounts of data. - /// As such, this option isn't designed to stay and will likely get removed in the future. - /// - /// Note that configuring a value here isn't a modification of the Yamux protocol, but rather - /// a modification of the way the implementation works. Different nodes with different - /// configured values remain compatible with each other. - pub yamux_window_size: Option, -} - -impl NetworkConfiguration { - /// Create new default configuration - pub fn new, SV: Into>( - node_name: SN, - client_version: SV, - node_key: NodeKeyConfig, - net_config_path: Option, - ) -> Self { - let default_peers_set = SetConfig::default(); - Self { - net_config_path, - listen_addresses: Vec::new(), - public_addresses: Vec::new(), - boot_nodes: Vec::new(), - node_key, - request_response_protocols: Vec::new(), - default_peers_set_num_full: default_peers_set.in_peers + default_peers_set.out_peers, - default_peers_set, - extra_sets: Vec::new(), - client_version: client_version.into(), - node_name: node_name.into(), - transport: TransportConfig::Normal { enable_mdns: false, allow_private_ip: true }, - max_parallel_downloads: 5, - sync_mode: SyncMode::Full, - enable_dht_random_walk: true, - allow_non_globals_in_dht: false, - kademlia_disjoint_query_paths: false, - yamux_window_size: None, - ipfs_server: false, - } - } - - /// Create new default configuration for localhost-only connection with random port (useful for - /// testing) - pub fn new_local() -> NetworkConfiguration { - let mut config = - NetworkConfiguration::new("test-node", "test-client", Default::default(), None); - - config.listen_addresses = - vec![iter::once(multiaddr::Protocol::Ip4(Ipv4Addr::new(127, 0, 0, 1))) - .chain(iter::once(multiaddr::Protocol::Tcp(0))) - .collect()]; - - config.allow_non_globals_in_dht = true; - config - } - - /// Create new default configuration for localhost-only connection with random port (useful for - /// testing) - pub fn new_memory() -> NetworkConfiguration { - let mut config = - NetworkConfiguration::new("test-node", "test-client", Default::default(), None); - - config.listen_addresses = - vec![iter::once(multiaddr::Protocol::Ip4(Ipv4Addr::new(127, 0, 0, 1))) - .chain(iter::once(multiaddr::Protocol::Tcp(0))) - .collect()]; - - config.allow_non_globals_in_dht = true; - config - } -} - -/// The configuration of a node's secret key, describing the type of key -/// and how it is obtained. A node's identity keypair is the result of -/// the evaluation of the node key configuration. -#[derive(Clone, Debug)] -pub enum NodeKeyConfig { - /// A Ed25519 secret key configuration. - Ed25519(Secret), -} - -impl Default for NodeKeyConfig { - fn default() -> NodeKeyConfig { - Self::Ed25519(Secret::New) - } -} - -/// The options for obtaining a Ed25519 secret key. -pub type Ed25519Secret = Secret; - -/// The configuration options for obtaining a secret key `K`. -#[derive(Clone)] -pub enum Secret { - /// Use the given secret key `K`. - Input(K), - /// Read the secret key from a file. If the file does not exist, - /// it is created with a newly generated secret key `K`. The format - /// of the file is determined by `K`: - /// - /// * `ed25519::SecretKey`: An unencoded 32 bytes Ed25519 secret key. - File(PathBuf), - /// Always generate a new secret key `K`. - New, -} - -impl fmt::Debug for Secret { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - match self { - Self::Input(_) => f.debug_tuple("Secret::Input").finish(), - Self::File(path) => f.debug_tuple("Secret::File").field(path).finish(), - Self::New => f.debug_tuple("Secret::New").finish(), - } - } -} - -impl NodeKeyConfig { - /// Evaluate a `NodeKeyConfig` to obtain an identity `Keypair`: - /// - /// * If the secret is configured as input, the corresponding keypair is returned. - /// - /// * If the secret is configured as a file, it is read from that file, if it exists. Otherwise - /// a new secret is generated and stored. In either case, the keypair obtained from the - /// secret is returned. - /// - /// * If the secret is configured to be new, it is generated and the corresponding keypair is - /// returned. - pub fn into_keypair(self) -> io::Result { - use NodeKeyConfig::*; - match self { - Ed25519(Secret::New) => Ok(Keypair::generate_ed25519()), - - Ed25519(Secret::Input(k)) => Ok(Keypair::Ed25519(k.into())), - - Ed25519(Secret::File(f)) => get_secret( - f, - |mut b| match String::from_utf8(b.to_vec()).ok().and_then(|s| { - if s.len() == 64 { - array_bytes::hex2bytes(&s).ok() - } else { - None - } - }) { - Some(s) => ed25519::SecretKey::from_bytes(s), - _ => ed25519::SecretKey::from_bytes(&mut b), - }, - ed25519::SecretKey::generate, - |b| b.as_ref().to_vec(), - ) - .map(ed25519::Keypair::from) - .map(Keypair::Ed25519), - } - } -} - -/// Load a secret key from a file, if it exists, or generate a -/// new secret key and write it to that file. In either case, -/// the secret key is returned. -fn get_secret(file: P, parse: F, generate: G, serialize: W) -> io::Result -where - P: AsRef, - F: for<'r> FnOnce(&'r mut [u8]) -> Result, - G: FnOnce() -> K, - E: Error + Send + Sync + 'static, - W: Fn(&K) -> Vec, -{ - std::fs::read(&file) - .and_then(|mut sk_bytes| { - parse(&mut sk_bytes).map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e)) - }) - .or_else(|e| { - if e.kind() == io::ErrorKind::NotFound { - file.as_ref().parent().map_or(Ok(()), fs::create_dir_all)?; - let sk = generate(); - let mut sk_vec = serialize(&sk); - write_secret_file(file, &sk_vec)?; - sk_vec.zeroize(); - Ok(sk) - } else { - Err(e) - } - }) -} - -/// Write secret bytes to a file. -fn write_secret_file

(path: P, sk_bytes: &[u8]) -> io::Result<()> -where - P: AsRef, -{ - let mut file = open_secret_file(&path)?; - file.write_all(sk_bytes) -} - -/// Opens a file containing a secret key in write mode. -#[cfg(unix)] -fn open_secret_file

(path: P) -> io::Result -where - P: AsRef, -{ - use std::os::unix::fs::OpenOptionsExt; - fs::OpenOptions::new().write(true).create_new(true).mode(0o600).open(path) -} - -/// Opens a file containing a secret key in write mode. -#[cfg(not(unix))] -fn open_secret_file

(path: P) -> Result -where - P: AsRef, -{ - fs::OpenOptions::new().write(true).create_new(true).open(path) -} - -#[cfg(test)] -mod tests { - use super::*; - use tempfile::TempDir; - - fn tempdir_with_prefix(prefix: &str) -> TempDir { - tempfile::Builder::new().prefix(prefix).tempdir().unwrap() - } - - fn secret_bytes(kp: &Keypair) -> Vec { - let Keypair::Ed25519(p) = kp; - p.secret().as_ref().iter().cloned().collect() - } - - #[test] - fn test_secret_file() { - let tmp = tempdir_with_prefix("x"); - std::fs::remove_dir(tmp.path()).unwrap(); // should be recreated - let file = tmp.path().join("x").to_path_buf(); - let kp1 = NodeKeyConfig::Ed25519(Secret::File(file.clone())).into_keypair().unwrap(); - let kp2 = NodeKeyConfig::Ed25519(Secret::File(file.clone())).into_keypair().unwrap(); - assert!(file.is_file() && secret_bytes(&kp1) == secret_bytes(&kp2)) - } - - #[test] - fn test_secret_input() { - let sk = ed25519::SecretKey::generate(); - let kp1 = NodeKeyConfig::Ed25519(Secret::Input(sk.clone())).into_keypair().unwrap(); - let kp2 = NodeKeyConfig::Ed25519(Secret::Input(sk)).into_keypair().unwrap(); - assert!(secret_bytes(&kp1) == secret_bytes(&kp2)); - } - - #[test] - fn test_secret_new() { - let kp1 = NodeKeyConfig::Ed25519(Secret::New).into_keypair().unwrap(); - let kp2 = NodeKeyConfig::Ed25519(Secret::New).into_keypair().unwrap(); - assert!(secret_bytes(&kp1) != secret_bytes(&kp2)); - } -} diff --git a/client/network/common/src/lib.rs b/client/network/common/src/lib.rs index 10d244938..f53590efd 100644 --- a/client/network/common/src/lib.rs +++ b/client/network/common/src/lib.rs @@ -18,14 +18,9 @@ //! Common data structures of the networking layer. -pub mod config; -pub mod error; pub mod message; -pub mod protocol; -pub mod request_responses; -pub mod service; +pub mod role; pub mod sync; -pub mod utils; /// Minimum Requirements for a Hash within Networking pub trait ExHashT: std::hash::Hash + Eq + std::fmt::Debug + Clone + Send + Sync + 'static {} diff --git a/client/network/common/src/request_responses.rs b/client/network/common/src/request_responses.rs deleted file mode 100644 index 4fa53a736..000000000 --- a/client/network/common/src/request_responses.rs +++ /dev/null @@ -1,155 +0,0 @@ -// This file is part of Substrate. - -// Copyright (C) Parity Technologies (UK) Ltd. -// SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 - -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with this program. If not, see . - -//! Collection of generic data structures for request-response protocols. - -use crate::protocol::ProtocolName; -use futures::channel::{mpsc, oneshot}; -use libp2p::{request_response::OutboundFailure, PeerId}; -use sc_peerset::ReputationChange; -use std::time::Duration; - -/// Configuration for a single request-response protocol. -#[derive(Debug, Clone)] -pub struct ProtocolConfig { - /// Name of the protocol on the wire. Should be something like `/foo/bar`. - pub name: ProtocolName, - - /// Fallback on the wire protocol names to support. - pub fallback_names: Vec, - - /// Maximum allowed size, in bytes, of a request. - /// - /// Any request larger than this value will be declined as a way to avoid allocating too - /// much memory for it. - pub max_request_size: u64, - - /// Maximum allowed size, in bytes, of a response. - /// - /// Any response larger than this value will be declined as a way to avoid allocating too - /// much memory for it. - pub max_response_size: u64, - - /// Duration after which emitted requests are considered timed out. - /// - /// If you expect the response to come back quickly, you should set this to a smaller duration. - pub request_timeout: Duration, - - /// Channel on which the networking service will send incoming requests. - /// - /// Every time a peer sends a request to the local node using this protocol, the networking - /// service will push an element on this channel. The receiving side of this channel then has - /// to pull this element, process the request, and send back the response to send back to the - /// peer. - /// - /// The size of the channel has to be carefully chosen. If the channel is full, the networking - /// service will discard the incoming request send back an error to the peer. Consequently, - /// the channel being full is an indicator that the node is overloaded. - /// - /// You can typically set the size of the channel to `T / d`, where `T` is the - /// `request_timeout` and `d` is the expected average duration of CPU and I/O it takes to - /// build a response. - /// - /// Can be `None` if the local node does not support answering incoming requests. - /// If this is `None`, then the local node will not advertise support for this protocol towards - /// other peers. If this is `Some` but the channel is closed, then the local node will - /// advertise support for this protocol, but any incoming request will lead to an error being - /// sent back. - pub inbound_queue: Option>, -} - -/// A single request received by a peer on a request-response protocol. -#[derive(Debug)] -pub struct IncomingRequest { - /// Who sent the request. - pub peer: PeerId, - - /// Request sent by the remote. Will always be smaller than - /// [`ProtocolConfig::max_request_size`]. - pub payload: Vec, - - /// Channel to send back the response. - /// - /// There are two ways to indicate that handling the request failed: - /// - /// 1. Drop `pending_response` and thus not changing the reputation of the peer. - /// - /// 2. Sending an `Err(())` via `pending_response`, optionally including reputation changes for - /// the given peer. - pub pending_response: oneshot::Sender, -} - -/// Response for an incoming request to be send by a request protocol handler. -#[derive(Debug)] -pub struct OutgoingResponse { - /// The payload of the response. - /// - /// `Err(())` if none is available e.g. due an error while handling the request. - pub result: Result, ()>, - - /// Reputation changes accrued while handling the request. To be applied to the reputation of - /// the peer sending the request. - pub reputation_changes: Vec, - - /// If provided, the `oneshot::Sender` will be notified when the request has been sent to the - /// peer. - /// - /// > **Note**: Operating systems typically maintain a buffer of a few dozen kilobytes of - /// > outgoing data for each TCP socket, and it is not possible for a user - /// > application to inspect this buffer. This channel here is not actually notified - /// > when the response has been fully sent out, but rather when it has fully been - /// > written to the buffer managed by the operating system. - pub sent_feedback: Option>, -} - -/// When sending a request, what to do on a disconnected recipient. -#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)] -pub enum IfDisconnected { - /// Try to connect to the peer. - TryConnect, - /// Just fail if the destination is not yet connected. - ImmediateError, -} - -/// Convenience functions for `IfDisconnected`. -impl IfDisconnected { - /// Shall we connect to a disconnected peer? - pub fn should_connect(self) -> bool { - match self { - Self::TryConnect => true, - Self::ImmediateError => false, - } - } -} - -/// Error in a request. -#[derive(Debug, thiserror::Error)] -#[allow(missing_docs)] -pub enum RequestFailure { - #[error("We are not currently connected to the requested peer.")] - NotConnected, - #[error("Given protocol hasn't been registered.")] - UnknownProtocol, - #[error("Remote has closed the substream before answering, thereby signaling that it considers the request as valid, but refused to answer it.")] - Refused, - #[error("The remote replied, but the local node is no longer interested in the response.")] - Obsolete, - /// Problem on the network. - #[error("Problem on the network: {0}")] - Network(OutboundFailure), -} diff --git a/client/network/common/src/protocol/role.rs b/client/network/common/src/role.rs similarity index 100% rename from client/network/common/src/protocol/role.rs rename to client/network/common/src/role.rs diff --git a/client/network/common/src/sync.rs b/client/network/common/src/sync.rs index 262da6c20..130f354b7 100644 --- a/client/network/common/src/sync.rs +++ b/client/network/common/src/sync.rs @@ -22,7 +22,7 @@ pub mod message; pub mod metrics; pub mod warp; -use crate::protocol::role::Roles; +use crate::role::Roles; use futures::Stream; use libp2p::PeerId; diff --git a/client/network/common/src/sync/message.rs b/client/network/common/src/sync/message.rs index 7b8be18bd..c651660c8 100644 --- a/client/network/common/src/sync/message.rs +++ b/client/network/common/src/sync/message.rs @@ -19,7 +19,7 @@ //! Network packet message types. These get serialized and put into the lower level protocol //! payload. -use crate::protocol::role::Roles; +use crate::role::Roles; use bitflags::bitflags; use codec::{Decode, Encode, Error, Input, Output}; diff --git a/client/network/light/Cargo.toml b/client/network/light/Cargo.toml index f3e94790d..ed2a5d6cb 100644 --- a/client/network/light/Cargo.toml +++ b/client/network/light/Cargo.toml @@ -26,6 +26,7 @@ log = "0.4.16" prost = "0.11" sp-blockchain = { version = "4.0.0-dev", path = "../../../primitives/blockchain" } sc-client-api = { version = "4.0.0-dev", path = "../../api" } +sc-network = { version = "0.10.0-dev", path = "../" } sc-network-common = { version = "0.10.0-dev", path = "../common" } sc-peerset = { version = "4.0.0-dev", path = "../../peerset" } sp-core = { version = "7.0.0", path = "../../../primitives/core" } diff --git a/client/network/light/src/light_client_requests.rs b/client/network/light/src/light_client_requests.rs index 5b6daf174..4d2a301c0 100644 --- a/client/network/light/src/light_client_requests.rs +++ b/client/network/light/src/light_client_requests.rs @@ -18,13 +18,13 @@ //! Helpers for outgoing and incoming light client requests. -/// For incoming light client requests. -pub mod handler; - -use sc_network_common::{config::ProtocolId, request_responses::ProtocolConfig}; +use sc_network::{config::ProtocolId, request_responses::ProtocolConfig}; use std::time::Duration; +/// For incoming light client requests. +pub mod handler; + /// Generate the light client protocol name from the genesis hash and fork id. fn generate_protocol_name>(genesis_hash: Hash, fork_id: Option<&str>) -> String { let genesis_hash = genesis_hash.as_ref(); diff --git a/client/network/light/src/light_client_requests/handler.rs b/client/network/light/src/light_client_requests/handler.rs index 0a1215a1d..db2630b79 100644 --- a/client/network/light/src/light_client_requests/handler.rs +++ b/client/network/light/src/light_client_requests/handler.rs @@ -29,7 +29,7 @@ use libp2p::PeerId; use log::{debug, trace}; use prost::Message; use sc_client_api::{BlockBackend, ProofProvider}; -use sc_network_common::{ +use sc_network::{ config::ProtocolId, request_responses::{IncomingRequest, OutgoingResponse, ProtocolConfig}, }; diff --git a/client/network/src/behaviour.rs b/client/network/src/behaviour.rs index 559129020..f06809992 100644 --- a/client/network/src/behaviour.rs +++ b/client/network/src/behaviour.rs @@ -18,9 +18,11 @@ use crate::{ discovery::{DiscoveryBehaviour, DiscoveryConfig, DiscoveryOut}, + event::DhtEvent, peer_info, protocol::{CustomMessageOutcome, NotificationsSink, Protocol}, - request_responses, + request_responses::{self, IfDisconnected, ProtocolConfig, RequestFailure}, + types::ProtocolName, }; use bytes::Bytes; @@ -32,14 +34,7 @@ use libp2p::{ swarm::NetworkBehaviour, }; -use sc_network_common::{ - protocol::{ - event::DhtEvent, - role::{ObservedRole, Roles}, - ProtocolName, - }, - request_responses::{IfDisconnected, ProtocolConfig, RequestFailure}, -}; +use sc_network_common::role::{ObservedRole, Roles}; use sc_peerset::{PeersetHandle, ReputationChange}; use sp_runtime::traits::Block as BlockT; use std::{collections::HashSet, time::Duration}; diff --git a/client/network/src/config.rs b/client/network/src/config.rs index 90d02af84..925a7795d 100644 --- a/client/network/src/config.rs +++ b/client/network/src/config.rs @@ -21,21 +21,671 @@ //! The [`Params`] struct is the struct that must be passed in order to initialize the networking. //! See the documentation of [`Params`]. -pub use sc_network_common::{ - config::{NetworkConfiguration, ProtocolId}, - protocol::role::Role, +pub use crate::{ request_responses::{ IncomingRequest, OutgoingResponse, ProtocolConfig as RequestResponseConfig, }, - sync::warp::WarpSyncProvider, - ExHashT, + types::ProtocolName, }; -pub use libp2p::{build_multiaddr, core::PublicKey, identity}; - +use codec::Encode; +use libp2p::{identity::Keypair, multiaddr, Multiaddr, PeerId}; use prometheus_endpoint::Registry; -use sc_network_common::config::NonDefaultSetConfig; -use std::{future::Future, pin::Pin, sync::Arc}; +pub use sc_network_common::{role::Role, sync::warp::WarpSyncProvider, ExHashT}; +use zeroize::Zeroize; + +use std::{ + error::Error, + fmt, fs, + future::Future, + io::{self, Write}, + iter, + net::Ipv4Addr, + path::{Path, PathBuf}, + pin::Pin, + str::{self, FromStr}, + sync::Arc, +}; + +pub use libp2p::{ + build_multiaddr, + identity::{self, ed25519}, +}; + +/// Protocol name prefix, transmitted on the wire for legacy protocol names. +/// I.e., `dot` in `/dot/sync/2`. Should be unique for each chain. Always UTF-8. +/// Deprecated in favour of genesis hash & fork ID based protocol names. +#[derive(Clone, PartialEq, Eq, Hash)] +pub struct ProtocolId(smallvec::SmallVec<[u8; 6]>); + +impl<'a> From<&'a str> for ProtocolId { + fn from(bytes: &'a str) -> ProtocolId { + Self(bytes.as_bytes().into()) + } +} + +impl AsRef for ProtocolId { + fn as_ref(&self) -> &str { + str::from_utf8(&self.0[..]) + .expect("the only way to build a ProtocolId is through a UTF-8 String; qed") + } +} + +impl fmt::Debug for ProtocolId { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + fmt::Debug::fmt(self.as_ref(), f) + } +} + +/// Parses a string address and splits it into Multiaddress and PeerId, if +/// valid. +/// +/// # Example +/// +/// ``` +/// # use libp2p::{Multiaddr, PeerId}; +/// use sc_network::config::parse_str_addr; +/// let (peer_id, addr) = parse_str_addr( +/// "/ip4/198.51.100.19/tcp/30333/p2p/QmSk5HQbn6LhUwDiNMseVUjuRYhEtYj4aUZ6WfWoGURpdV" +/// ).unwrap(); +/// assert_eq!(peer_id, "QmSk5HQbn6LhUwDiNMseVUjuRYhEtYj4aUZ6WfWoGURpdV".parse::().unwrap()); +/// assert_eq!(addr, "/ip4/198.51.100.19/tcp/30333".parse::().unwrap()); +/// ``` +pub fn parse_str_addr(addr_str: &str) -> Result<(PeerId, Multiaddr), ParseErr> { + let addr: Multiaddr = addr_str.parse()?; + parse_addr(addr) +} + +/// Splits a Multiaddress into a Multiaddress and PeerId. +pub fn parse_addr(mut addr: Multiaddr) -> Result<(PeerId, Multiaddr), ParseErr> { + let who = match addr.pop() { + Some(multiaddr::Protocol::P2p(key)) => + PeerId::from_multihash(key).map_err(|_| ParseErr::InvalidPeerId)?, + _ => return Err(ParseErr::PeerIdMissing), + }; + + Ok((who, addr)) +} + +/// Address of a node, including its identity. +/// +/// This struct represents a decoded version of a multiaddress that ends with `/p2p/`. +/// +/// # Example +/// +/// ``` +/// # use libp2p::{Multiaddr, PeerId}; +/// use sc_network::config::MultiaddrWithPeerId; +/// let addr: MultiaddrWithPeerId = +/// "/ip4/198.51.100.19/tcp/30333/p2p/QmSk5HQbn6LhUwDiNMseVUjuRYhEtYj4aUZ6WfWoGURpdV".parse().unwrap(); +/// assert_eq!(addr.peer_id.to_base58(), "QmSk5HQbn6LhUwDiNMseVUjuRYhEtYj4aUZ6WfWoGURpdV"); +/// assert_eq!(addr.multiaddr.to_string(), "/ip4/198.51.100.19/tcp/30333"); +/// ``` +#[derive(Debug, Clone, serde::Serialize, serde::Deserialize, PartialEq)] +#[serde(try_from = "String", into = "String")] +pub struct MultiaddrWithPeerId { + /// Address of the node. + pub multiaddr: Multiaddr, + /// Its identity. + pub peer_id: PeerId, +} + +impl MultiaddrWithPeerId { + /// Concatenates the multiaddress and peer ID into one multiaddress containing both. + pub fn concat(&self) -> Multiaddr { + let proto = multiaddr::Protocol::P2p(From::from(self.peer_id)); + self.multiaddr.clone().with(proto) + } +} + +impl fmt::Display for MultiaddrWithPeerId { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + fmt::Display::fmt(&self.concat(), f) + } +} + +impl FromStr for MultiaddrWithPeerId { + type Err = ParseErr; + + fn from_str(s: &str) -> Result { + let (peer_id, multiaddr) = parse_str_addr(s)?; + Ok(Self { peer_id, multiaddr }) + } +} + +impl From for String { + fn from(ma: MultiaddrWithPeerId) -> String { + format!("{}", ma) + } +} + +impl TryFrom for MultiaddrWithPeerId { + type Error = ParseErr; + fn try_from(string: String) -> Result { + string.parse() + } +} + +/// Error that can be generated by `parse_str_addr`. +#[derive(Debug)] +pub enum ParseErr { + /// Error while parsing the multiaddress. + MultiaddrParse(multiaddr::Error), + /// Multihash of the peer ID is invalid. + InvalidPeerId, + /// The peer ID is missing from the address. + PeerIdMissing, +} + +impl fmt::Display for ParseErr { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + Self::MultiaddrParse(err) => write!(f, "{}", err), + Self::InvalidPeerId => write!(f, "Peer id at the end of the address is invalid"), + Self::PeerIdMissing => write!(f, "Peer id is missing from the address"), + } + } +} + +impl std::error::Error for ParseErr { + fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { + match self { + Self::MultiaddrParse(err) => Some(err), + Self::InvalidPeerId => None, + Self::PeerIdMissing => None, + } + } +} + +impl From for ParseErr { + fn from(err: multiaddr::Error) -> ParseErr { + Self::MultiaddrParse(err) + } +} + +/// Custom handshake for the notification protocol +#[derive(Debug, Clone)] +pub struct NotificationHandshake(Vec); + +impl NotificationHandshake { + /// Create new `NotificationHandshake` from an object that implements `Encode` + pub fn new(handshake: H) -> Self { + Self(handshake.encode()) + } + + /// Create new `NotificationHandshake` from raw bytes + pub fn from_bytes(bytes: Vec) -> Self { + Self(bytes) + } +} + +impl std::ops::Deref for NotificationHandshake { + type Target = Vec; + + fn deref(&self) -> &Self::Target { + &self.0 + } +} + +/// Configuration for the transport layer. +#[derive(Clone, Debug)] +pub enum TransportConfig { + /// Normal transport mode. + Normal { + /// If true, the network will use mDNS to discover other libp2p nodes on the local network + /// and connect to them if they support the same chain. + enable_mdns: bool, + + /// If true, allow connecting to private IPv4/IPv6 addresses (as defined in + /// [RFC1918](https://tools.ietf.org/html/rfc1918)). Irrelevant for addresses that have + /// been passed in `::sc_network::config::NetworkConfiguration::boot_nodes`. + allow_private_ip: bool, + }, + + /// Only allow connections within the same process. + /// Only addresses of the form `/memory/...` will be supported. + MemoryOnly, +} + +/// The policy for connections to non-reserved peers. +#[derive(Clone, Debug, PartialEq, Eq)] +pub enum NonReservedPeerMode { + /// Accept them. This is the default. + Accept, + /// Deny them. + Deny, +} + +impl NonReservedPeerMode { + /// Attempt to parse the peer mode from a string. + pub fn parse(s: &str) -> Option { + match s { + "accept" => Some(Self::Accept), + "deny" => Some(Self::Deny), + _ => None, + } + } +} + +/// Sync operation mode. +#[derive(Copy, Clone, Debug, Eq, PartialEq)] +pub enum SyncMode { + /// Full block download and verification. + Full, + /// Download blocks and the latest state. + Fast { + /// Skip state proof download and verification. + skip_proofs: bool, + /// Download indexed transactions for recent blocks. + storage_chain_mode: bool, + }, + /// Warp sync - verify authority set transitions and the latest state. + Warp, +} + +impl SyncMode { + /// Returns if `self` is [`Self::Warp`]. + pub fn is_warp(&self) -> bool { + matches!(self, Self::Warp) + } + + /// Returns if `self` is [`Self::Fast`]. + pub fn is_fast(&self) -> bool { + matches!(self, Self::Fast { .. }) + } +} + +impl Default for SyncMode { + fn default() -> Self { + Self::Full + } +} + +/// The configuration of a node's secret key, describing the type of key +/// and how it is obtained. A node's identity keypair is the result of +/// the evaluation of the node key configuration. +#[derive(Clone, Debug)] +pub enum NodeKeyConfig { + /// A Ed25519 secret key configuration. + Ed25519(Secret), +} + +impl Default for NodeKeyConfig { + fn default() -> NodeKeyConfig { + Self::Ed25519(Secret::New) + } +} + +/// The options for obtaining a Ed25519 secret key. +pub type Ed25519Secret = Secret; + +/// The configuration options for obtaining a secret key `K`. +#[derive(Clone)] +pub enum Secret { + /// Use the given secret key `K`. + Input(K), + /// Read the secret key from a file. If the file does not exist, + /// it is created with a newly generated secret key `K`. The format + /// of the file is determined by `K`: + /// + /// * `ed25519::SecretKey`: An unencoded 32 bytes Ed25519 secret key. + File(PathBuf), + /// Always generate a new secret key `K`. + New, +} + +impl fmt::Debug for Secret { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + match self { + Self::Input(_) => f.debug_tuple("Secret::Input").finish(), + Self::File(path) => f.debug_tuple("Secret::File").field(path).finish(), + Self::New => f.debug_tuple("Secret::New").finish(), + } + } +} + +impl NodeKeyConfig { + /// Evaluate a `NodeKeyConfig` to obtain an identity `Keypair`: + /// + /// * If the secret is configured as input, the corresponding keypair is returned. + /// + /// * If the secret is configured as a file, it is read from that file, if it exists. Otherwise + /// a new secret is generated and stored. In either case, the keypair obtained from the + /// secret is returned. + /// + /// * If the secret is configured to be new, it is generated and the corresponding keypair is + /// returned. + pub fn into_keypair(self) -> io::Result { + use NodeKeyConfig::*; + match self { + Ed25519(Secret::New) => Ok(Keypair::generate_ed25519()), + + Ed25519(Secret::Input(k)) => Ok(Keypair::Ed25519(k.into())), + + Ed25519(Secret::File(f)) => get_secret( + f, + |mut b| match String::from_utf8(b.to_vec()).ok().and_then(|s| { + if s.len() == 64 { + array_bytes::hex2bytes(&s).ok() + } else { + None + } + }) { + Some(s) => ed25519::SecretKey::from_bytes(s), + _ => ed25519::SecretKey::from_bytes(&mut b), + }, + ed25519::SecretKey::generate, + |b| b.as_ref().to_vec(), + ) + .map(ed25519::Keypair::from) + .map(Keypair::Ed25519), + } + } +} + +/// Load a secret key from a file, if it exists, or generate a +/// new secret key and write it to that file. In either case, +/// the secret key is returned. +fn get_secret(file: P, parse: F, generate: G, serialize: W) -> io::Result +where + P: AsRef, + F: for<'r> FnOnce(&'r mut [u8]) -> Result, + G: FnOnce() -> K, + E: Error + Send + Sync + 'static, + W: Fn(&K) -> Vec, +{ + std::fs::read(&file) + .and_then(|mut sk_bytes| { + parse(&mut sk_bytes).map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e)) + }) + .or_else(|e| { + if e.kind() == io::ErrorKind::NotFound { + file.as_ref().parent().map_or(Ok(()), fs::create_dir_all)?; + let sk = generate(); + let mut sk_vec = serialize(&sk); + write_secret_file(file, &sk_vec)?; + sk_vec.zeroize(); + Ok(sk) + } else { + Err(e) + } + }) +} + +/// Write secret bytes to a file. +fn write_secret_file

(path: P, sk_bytes: &[u8]) -> io::Result<()> +where + P: AsRef, +{ + let mut file = open_secret_file(&path)?; + file.write_all(sk_bytes) +} + +/// Opens a file containing a secret key in write mode. +#[cfg(unix)] +fn open_secret_file

(path: P) -> io::Result +where + P: AsRef, +{ + use std::os::unix::fs::OpenOptionsExt; + fs::OpenOptions::new().write(true).create_new(true).mode(0o600).open(path) +} + +/// Opens a file containing a secret key in write mode. +#[cfg(not(unix))] +fn open_secret_file

(path: P) -> Result +where + P: AsRef, +{ + fs::OpenOptions::new().write(true).create_new(true).open(path) +} + +/// Configuration for a set of nodes. +#[derive(Clone, Debug)] +pub struct SetConfig { + /// Maximum allowed number of incoming substreams related to this set. + pub in_peers: u32, + + /// Number of outgoing substreams related to this set that we're trying to maintain. + pub out_peers: u32, + + /// List of reserved node addresses. + pub reserved_nodes: Vec, + + /// Whether nodes that aren't in [`SetConfig::reserved_nodes`] are accepted or automatically + /// refused. + pub non_reserved_mode: NonReservedPeerMode, +} + +impl Default for SetConfig { + fn default() -> Self { + Self { + in_peers: 25, + out_peers: 75, + reserved_nodes: Vec::new(), + non_reserved_mode: NonReservedPeerMode::Accept, + } + } +} + +/// Extension to [`SetConfig`] for sets that aren't the default set. +/// +/// > **Note**: As new fields might be added in the future, please consider using the `new` method +/// > and modifiers instead of creating this struct manually. +#[derive(Clone, Debug)] +pub struct NonDefaultSetConfig { + /// Name of the notifications protocols of this set. A substream on this set will be + /// considered established once this protocol is open. + /// + /// > **Note**: This field isn't present for the default set, as this is handled internally + /// > by the networking code. + pub notifications_protocol: ProtocolName, + + /// If the remote reports that it doesn't support the protocol indicated in the + /// `notifications_protocol` field, then each of these fallback names will be tried one by + /// one. + /// + /// If a fallback is used, it will be reported in + /// `sc_network::protocol::event::Event::NotificationStreamOpened::negotiated_fallback` + pub fallback_names: Vec, + + /// Handshake of the protocol + /// + /// NOTE: Currently custom handshakes are not fully supported. See issue #5685 for more + /// details. This field is temporarily used to allow moving the hardcoded block announcement + /// protocol out of `protocol.rs`. + pub handshake: Option, + + /// Maximum allowed size of single notifications. + pub max_notification_size: u64, + + /// Base configuration. + pub set_config: SetConfig, +} + +impl NonDefaultSetConfig { + /// Creates a new [`NonDefaultSetConfig`]. Zero slots and accepts only reserved nodes. + pub fn new(notifications_protocol: ProtocolName, max_notification_size: u64) -> Self { + Self { + notifications_protocol, + max_notification_size, + fallback_names: Vec::new(), + handshake: None, + set_config: SetConfig { + in_peers: 0, + out_peers: 0, + reserved_nodes: Vec::new(), + non_reserved_mode: NonReservedPeerMode::Deny, + }, + } + } + + /// Modifies the configuration to allow non-reserved nodes. + pub fn allow_non_reserved(&mut self, in_peers: u32, out_peers: u32) { + self.set_config.in_peers = in_peers; + self.set_config.out_peers = out_peers; + self.set_config.non_reserved_mode = NonReservedPeerMode::Accept; + } + + /// Add a node to the list of reserved nodes. + pub fn add_reserved(&mut self, peer: MultiaddrWithPeerId) { + self.set_config.reserved_nodes.push(peer); + } + + /// Add a list of protocol names used for backward compatibility. + /// + /// See the explanations in [`NonDefaultSetConfig::fallback_names`]. + pub fn add_fallback_names(&mut self, fallback_names: Vec) { + self.fallback_names.extend(fallback_names); + } +} + +/// Network service configuration. +#[derive(Clone, Debug)] +pub struct NetworkConfiguration { + /// Directory path to store network-specific configuration. None means nothing will be saved. + pub net_config_path: Option, + + /// Multiaddresses to listen for incoming connections. + pub listen_addresses: Vec, + + /// Multiaddresses to advertise. Detected automatically if empty. + pub public_addresses: Vec, + + /// List of initial node addresses + pub boot_nodes: Vec, + + /// The node key configuration, which determines the node's network identity keypair. + pub node_key: NodeKeyConfig, + + /// List of request-response protocols that the node supports. + pub request_response_protocols: Vec, + /// Configuration for the default set of nodes used for block syncing and transactions. + pub default_peers_set: SetConfig, + + /// Number of substreams to reserve for full nodes for block syncing and transactions. + /// Any other slot will be dedicated to light nodes. + /// + /// This value is implicitly capped to `default_set.out_peers + default_set.in_peers`. + pub default_peers_set_num_full: u32, + + /// Configuration for extra sets of nodes. + pub extra_sets: Vec, + + /// Client identifier. Sent over the wire for debugging purposes. + pub client_version: String, + + /// Name of the node. Sent over the wire for debugging purposes. + pub node_name: String, + + /// Configuration for the transport layer. + pub transport: TransportConfig, + + /// Maximum number of peers to ask the same blocks in parallel. + pub max_parallel_downloads: u32, + + /// Initial syncing mode. + pub sync_mode: SyncMode, + + /// True if Kademlia random discovery should be enabled. + /// + /// If true, the node will automatically randomly walk the DHT in order to find new peers. + pub enable_dht_random_walk: bool, + + /// Should we insert non-global addresses into the DHT? + pub allow_non_globals_in_dht: bool, + + /// Require iterative Kademlia DHT queries to use disjoint paths for increased resiliency in + /// the presence of potentially adversarial nodes. + pub kademlia_disjoint_query_paths: bool, + + /// Enable serving block data over IPFS bitswap. + pub ipfs_server: bool, + + /// Size of Yamux receive window of all substreams. `None` for the default (256kiB). + /// Any value less than 256kiB is invalid. + /// + /// # Context + /// + /// By design, notifications substreams on top of Yamux connections only allow up to `N` bytes + /// to be transferred at a time, where `N` is the Yamux receive window size configurable here. + /// This means, in practice, that every `N` bytes must be acknowledged by the receiver before + /// the sender can send more data. The maximum bandwidth of each notifications substream is + /// therefore `N / round_trip_time`. + /// + /// It is recommended to leave this to `None`, and use a request-response protocol instead if + /// a large amount of data must be transferred. The reason why the value is configurable is + /// that some Substrate users mis-use notification protocols to send large amounts of data. + /// As such, this option isn't designed to stay and will likely get removed in the future. + /// + /// Note that configuring a value here isn't a modification of the Yamux protocol, but rather + /// a modification of the way the implementation works. Different nodes with different + /// configured values remain compatible with each other. + pub yamux_window_size: Option, +} + +impl NetworkConfiguration { + /// Create new default configuration + pub fn new, SV: Into>( + node_name: SN, + client_version: SV, + node_key: NodeKeyConfig, + net_config_path: Option, + ) -> Self { + let default_peers_set = SetConfig::default(); + Self { + net_config_path, + listen_addresses: Vec::new(), + public_addresses: Vec::new(), + boot_nodes: Vec::new(), + node_key, + request_response_protocols: Vec::new(), + default_peers_set_num_full: default_peers_set.in_peers + default_peers_set.out_peers, + default_peers_set, + extra_sets: Vec::new(), + client_version: client_version.into(), + node_name: node_name.into(), + transport: TransportConfig::Normal { enable_mdns: false, allow_private_ip: true }, + max_parallel_downloads: 5, + sync_mode: SyncMode::Full, + enable_dht_random_walk: true, + allow_non_globals_in_dht: false, + kademlia_disjoint_query_paths: false, + yamux_window_size: None, + ipfs_server: false, + } + } + + /// Create new default configuration for localhost-only connection with random port (useful for + /// testing) + pub fn new_local() -> NetworkConfiguration { + let mut config = + NetworkConfiguration::new("test-node", "test-client", Default::default(), None); + + config.listen_addresses = + vec![iter::once(multiaddr::Protocol::Ip4(Ipv4Addr::new(127, 0, 0, 1))) + .chain(iter::once(multiaddr::Protocol::Tcp(0))) + .collect()]; + + config.allow_non_globals_in_dht = true; + config + } + + /// Create new default configuration for localhost-only connection with random port (useful for + /// testing) + pub fn new_memory() -> NetworkConfiguration { + let mut config = + NetworkConfiguration::new("test-node", "test-client", Default::default(), None); + + config.listen_addresses = + vec![iter::once(multiaddr::Protocol::Ip4(Ipv4Addr::new(127, 0, 0, 1))) + .chain(iter::once(multiaddr::Protocol::Tcp(0))) + .collect()]; + + config.allow_non_globals_in_dht = true; + config + } +} /// Network initialization parameters. pub struct Params { @@ -67,3 +717,43 @@ pub struct Params { /// Request response protocol configurations pub request_response_protocol_configs: Vec, } + +#[cfg(test)] +mod tests { + use super::*; + use tempfile::TempDir; + + fn tempdir_with_prefix(prefix: &str) -> TempDir { + tempfile::Builder::new().prefix(prefix).tempdir().unwrap() + } + + fn secret_bytes(kp: &Keypair) -> Vec { + let Keypair::Ed25519(p) = kp; + p.secret().as_ref().iter().cloned().collect() + } + + #[test] + fn test_secret_file() { + let tmp = tempdir_with_prefix("x"); + std::fs::remove_dir(tmp.path()).unwrap(); // should be recreated + let file = tmp.path().join("x").to_path_buf(); + let kp1 = NodeKeyConfig::Ed25519(Secret::File(file.clone())).into_keypair().unwrap(); + let kp2 = NodeKeyConfig::Ed25519(Secret::File(file.clone())).into_keypair().unwrap(); + assert!(file.is_file() && secret_bytes(&kp1) == secret_bytes(&kp2)) + } + + #[test] + fn test_secret_input() { + let sk = ed25519::SecretKey::generate(); + let kp1 = NodeKeyConfig::Ed25519(Secret::Input(sk.clone())).into_keypair().unwrap(); + let kp2 = NodeKeyConfig::Ed25519(Secret::Input(sk)).into_keypair().unwrap(); + assert!(secret_bytes(&kp1) == secret_bytes(&kp2)); + } + + #[test] + fn test_secret_new() { + let kp1 = NodeKeyConfig::Ed25519(Secret::New).into_keypair().unwrap(); + let kp2 = NodeKeyConfig::Ed25519(Secret::New).into_keypair().unwrap(); + assert!(secret_bytes(&kp1) != secret_bytes(&kp2)); + } +} diff --git a/client/network/src/discovery.rs b/client/network/src/discovery.rs index 6a8c777ca..7100c0c70 100644 --- a/client/network/src/discovery.rs +++ b/client/network/src/discovery.rs @@ -46,6 +46,8 @@ //! active mechanism that asks nodes for the addresses they are listening on. Whenever we learn //! of a node's address, you must call `add_self_reported_address`. +use crate::{config::ProtocolId, utils::LruHashSet}; + use array_bytes::bytes2hex; use futures::prelude::*; use futures_timer::Delay; @@ -73,7 +75,6 @@ use libp2p::{ }, }; use log::{debug, info, trace, warn}; -use sc_network_common::{config::ProtocolId, utils::LruHashSet}; use sp_core::hexdisplay::HexDisplay; use std::{ cmp, @@ -904,6 +905,7 @@ mod tests { use super::{ kademlia_protocol_name, legacy_kademlia_protocol_name, DiscoveryConfig, DiscoveryOut, }; + use crate::config::ProtocolId; use futures::prelude::*; use libp2p::{ core::{ @@ -915,7 +917,6 @@ mod tests { swarm::{Executor, Swarm, SwarmEvent}, yamux, Multiaddr, }; - use sc_network_common::config::ProtocolId; use sp_core::hash::H256; use std::{collections::HashSet, pin::Pin, task::Poll}; diff --git a/client/network/common/src/error.rs b/client/network/src/error.rs similarity index 97% rename from client/network/common/src/error.rs rename to client/network/src/error.rs index 712b8c5de..f0828fb82 100644 --- a/client/network/common/src/error.rs +++ b/client/network/src/error.rs @@ -18,7 +18,8 @@ //! Substrate network possible errors. -use crate::{config::TransportConfig, protocol::ProtocolName}; +use crate::{config::TransportConfig, types::ProtocolName}; + use libp2p::{Multiaddr, PeerId}; use std::fmt; diff --git a/client/network/common/src/protocol/event.rs b/client/network/src/event.rs similarity index 97% rename from client/network/common/src/protocol/event.rs rename to client/network/src/event.rs index 90c38b48c..3ecd8f931 100644 --- a/client/network/common/src/protocol/event.rs +++ b/client/network/src/event.rs @@ -19,11 +19,13 @@ //! Network event types. These are are not the part of the protocol, but rather //! events that happen on the network like DHT get/put results received. -use super::ProtocolName; -use crate::protocol::role::ObservedRole; +use crate::types::ProtocolName; + use bytes::Bytes; use libp2p::{core::PeerId, kad::record::Key}; +use sc_network_common::role::ObservedRole; + /// Events generated by DHT as a response to get_value and put_value requests. #[derive(Debug, Clone)] #[must_use] diff --git a/client/network/src/lib.rs b/client/network/src/lib.rs index f94a71681..c290f4b94 100644 --- a/client/network/src/lib.rs +++ b/client/network/src/lib.rs @@ -248,36 +248,40 @@ mod behaviour; mod discovery; mod peer_info; mod protocol; -mod request_responses; mod service; mod transport; pub mod config; +pub mod error; +pub mod event; pub mod network_state; +pub mod request_responses; +pub mod types; +pub mod utils; +pub use event::{DhtEvent, Event}; #[doc(inline)] pub use libp2p::{multiaddr, Multiaddr, PeerId}; +pub use request_responses::{IfDisconnected, RequestFailure, RequestResponseConfig}; pub use sc_network_common::{ - protocol::{ - event::{DhtEvent, Event}, - role::ObservedRole, - ProtocolName, - }, - request_responses::{IfDisconnected, RequestFailure}, - service::{ - KademliaKey, NetworkBlock, NetworkDHTProvider, NetworkRequest, NetworkSigner, - NetworkStateInfo, NetworkStatus, NetworkStatusProvider, NetworkSyncForkRequest, Signature, - SigningError, - }, + role::ObservedRole, sync::{ warp::{WarpSyncPhase, WarpSyncProgress}, ExtendedPeerInfo, StateDownloadProgress, SyncEventStream, SyncState, SyncStatusProvider, }, }; pub use service::{ - DecodingError, Keypair, NetworkService, NetworkWorker, NotificationSender, - NotificationSenderReady, OutboundFailure, PublicKey, + signature::Signature, + traits::{ + KademliaKey, NetworkBlock, NetworkDHTProvider, NetworkEventStream, NetworkNotification, + NetworkPeers, NetworkRequest, NetworkSigner, NetworkStateInfo, NetworkStatus, + NetworkStatusProvider, NetworkSyncForkRequest, NotificationSender as NotificationSenderT, + NotificationSenderError, NotificationSenderReady, + }, + DecodingError, Keypair, NetworkService, NetworkWorker, NotificationSender, OutboundFailure, + PublicKey, }; +pub use types::ProtocolName; pub use sc_peerset::ReputationChange; diff --git a/client/network/src/peer_info.rs b/client/network/src/peer_info.rs index 1cf0c928a..3f769736f 100644 --- a/client/network/src/peer_info.rs +++ b/client/network/src/peer_info.rs @@ -16,6 +16,8 @@ // You should have received a copy of the GNU General Public License // along with this program. If not, see . +use crate::utils::interval; + use fnv::FnvHashMap; use futures::prelude::*; use libp2p::{ @@ -36,8 +38,8 @@ use libp2p::{ Multiaddr, }; use log::{debug, error, trace}; -use sc_network_common::utils::interval; use smallvec::SmallVec; + use std::{ collections::hash_map::Entry, pin::Pin, diff --git a/client/network/src/protocol.rs b/client/network/src/protocol.rs index 8d6041242..06ca02c0c 100644 --- a/client/network/src/protocol.rs +++ b/client/network/src/protocol.rs @@ -16,7 +16,11 @@ // You should have received a copy of the GNU General Public License // along with this program. If not, see . -use crate::config; +use crate::{ + config::{self, NonReservedPeerMode}, + error, + types::ProtocolName, +}; use bytes::Bytes; use codec::{DecodeAll, Encode}; @@ -29,27 +33,25 @@ use libp2p::{ Multiaddr, PeerId, }; use log::{debug, error, warn}; -use message::{generic::Message as GenericMessage, Message}; -use notifications::{Notifications, NotificationsOut}; -use sc_network_common::{ - config::NonReservedPeerMode, - error, - protocol::{role::Roles, ProtocolName}, - sync::message::BlockAnnouncesHandshake, -}; + +use sc_network_common::{role::Roles, sync::message::BlockAnnouncesHandshake}; use sp_runtime::traits::Block as BlockT; + use std::{ collections::{HashMap, HashSet, VecDeque}, iter, task::Poll, }; +use message::{generic::Message as GenericMessage, Message}; +use notifications::{Notifications, NotificationsOut}; + +pub use notifications::{NotificationsSink, NotifsHandlerError, Ready}; + mod notifications; pub mod message; -pub use notifications::{NotificationsSink, NotifsHandlerError, Ready}; - /// Maximum size used for notifications in the block announce and transaction protocols. // Must be equal to `max(MAX_BLOCK_ANNOUNCE_SIZE, MAX_TRANSACTIONS_SIZE)`. pub(crate) const BLOCK_ANNOUNCES_TRANSACTIONS_SUBSTREAM_SIZE: u64 = 16 * 1024 * 1024; @@ -93,7 +95,7 @@ impl Protocol { pub fn new( roles: Roles, network_config: &config::NetworkConfiguration, - block_announces_protocol: sc_network_common::config::NonDefaultSetConfig, + block_announces_protocol: config::NonDefaultSetConfig, ) -> error::Result<(Self, sc_peerset::PeersetHandle, Vec<(PeerId, Multiaddr)>)> { let mut known_addresses = Vec::new(); diff --git a/client/network/src/protocol/message.rs b/client/network/src/protocol/message.rs index f7981f171..66dca2975 100644 --- a/client/network/src/protocol/message.rs +++ b/client/network/src/protocol/message.rs @@ -61,7 +61,7 @@ pub mod generic { use sc_client_api::StorageProof; use sc_network_common::{ message::RequestId, - protocol::role::Roles, + role::Roles, sync::message::{ generic::{BlockRequest, BlockResponse}, BlockAnnounce, diff --git a/client/network/src/protocol/notifications/behaviour.rs b/client/network/src/protocol/notifications/behaviour.rs index 093d5846d..74e27fa17 100644 --- a/client/network/src/protocol/notifications/behaviour.rs +++ b/client/network/src/protocol/notifications/behaviour.rs @@ -16,8 +16,11 @@ // You should have received a copy of the GNU General Public License // along with this program. If not, see . -use crate::protocol::notifications::handler::{ - self, NotificationsSink, NotifsHandlerIn, NotifsHandlerOut, NotifsHandlerProto, +use crate::{ + protocol::notifications::handler::{ + self, NotificationsSink, NotifsHandlerIn, NotifsHandlerOut, NotifsHandlerProto, + }, + types::ProtocolName, }; use bytes::BytesMut; @@ -35,7 +38,6 @@ use libp2p::{ use log::{error, trace, warn}; use parking_lot::RwLock; use rand::distributions::{Distribution as _, Uniform}; -use sc_network_common::protocol::ProtocolName; use sc_peerset::DropReason; use smallvec::SmallVec; use std::{ diff --git a/client/network/src/protocol/notifications/handler.rs b/client/network/src/protocol/notifications/handler.rs index 70157386e..9d8d98fd8 100644 --- a/client/network/src/protocol/notifications/handler.rs +++ b/client/network/src/protocol/notifications/handler.rs @@ -57,9 +57,12 @@ //! It is illegal to send a [`NotifsHandlerIn::Open`] before a previously-emitted //! [`NotifsHandlerIn::Open`] has gotten an answer. -use crate::protocol::notifications::upgrade::{ - NotificationsIn, NotificationsInSubstream, NotificationsOut, NotificationsOutSubstream, - UpgradeCollec, +use crate::{ + protocol::notifications::upgrade::{ + NotificationsIn, NotificationsInSubstream, NotificationsOut, NotificationsOutSubstream, + UpgradeCollec, + }, + types::ProtocolName, }; use bytes::BytesMut; @@ -77,7 +80,6 @@ use libp2p::{ }; use log::error; use parking_lot::{Mutex, RwLock}; -use sc_network_common::protocol::ProtocolName; use std::{ collections::VecDeque, mem, @@ -945,8 +947,9 @@ pub mod tests { Poll::Ready(Some(NotificationsSinkMessage::Notification { message })) => Poll::Ready(Some(message)), Poll::Pending => Poll::Ready(None), - Poll::Ready(Some(NotificationsSinkMessage::ForceClose)) | Poll::Ready(None) => - panic!("sink closed"), + Poll::Ready(Some(NotificationsSinkMessage::ForceClose)) | Poll::Ready(None) => { + panic!("sink closed") + }, }) .await } diff --git a/client/network/src/protocol/notifications/upgrade/collec.rs b/client/network/src/protocol/notifications/upgrade/collec.rs index 89999ff0c..791821b3f 100644 --- a/client/network/src/protocol/notifications/upgrade/collec.rs +++ b/client/network/src/protocol/notifications/upgrade/collec.rs @@ -103,8 +103,8 @@ impl>, O, E> Future for FutWithUsize { #[cfg(test)] mod tests { use super::*; + use crate::types::ProtocolName as ProtoName; use libp2p::core::upgrade::{ProtocolName, UpgradeInfo}; - use sc_network_common::protocol::ProtocolName as ProtoName; // TODO: move to mocks mockall::mock! { diff --git a/client/network/src/protocol/notifications/upgrade/notifications.rs b/client/network/src/protocol/notifications/upgrade/notifications.rs index bfdd9ccfa..4e1c033f3 100644 --- a/client/network/src/protocol/notifications/upgrade/notifications.rs +++ b/client/network/src/protocol/notifications/upgrade/notifications.rs @@ -16,7 +16,6 @@ // You should have received a copy of the GNU General Public License // along with this program. If not, see . -use asynchronous_codec::Framed; /// Notifications protocol. /// /// The Substrate notifications protocol consists in the following: @@ -35,11 +34,15 @@ use asynchronous_codec::Framed; /// /// Notification substreams are unidirectional. If A opens a substream with B, then B is /// encouraged but not required to open a substream to A as well. +use crate::types::ProtocolName; + +use asynchronous_codec::Framed; use bytes::BytesMut; use futures::prelude::*; use libp2p::core::{upgrade, InboundUpgrade, OutboundUpgrade, UpgradeInfo}; use log::{error, warn}; -use sc_network_common::protocol::ProtocolName; +use unsigned_varint::codec::UviBytes; + use std::{ convert::Infallible, io, mem, @@ -47,7 +50,6 @@ use std::{ task::{Context, Poll}, vec, }; -use unsigned_varint::codec::UviBytes; /// Maximum allowed size of the two handshake messages, in bytes. const MAX_HANDSHAKE_SIZE: usize = 1024; diff --git a/client/network/src/request_responses.rs b/client/network/src/request_responses.rs index 37c97e828..4628b0191 100644 --- a/client/network/src/request_responses.rs +++ b/client/network/src/request_responses.rs @@ -34,7 +34,8 @@ //! - If provided, a ["requests processing"](ProtocolConfig::inbound_queue) channel //! is used to handle incoming requests. -use crate::ReputationChange; +use crate::{types::ProtocolName, ReputationChange}; + use futures::{ channel::{mpsc, oneshot}, prelude::*, @@ -43,7 +44,7 @@ use libp2p::{ core::{connection::ConnectionId, Multiaddr, PeerId}, request_response::{ handler::RequestResponseHandler, ProtocolSupport, RequestResponse, RequestResponseCodec, - RequestResponseConfig, RequestResponseEvent, RequestResponseMessage, ResponseChannel, + RequestResponseEvent, RequestResponseMessage, ResponseChannel, }, swarm::{ behaviour::{ConnectionClosed, DialFailure, FromSwarm, ListenFailure}, @@ -52,12 +53,9 @@ use libp2p::{ PollParameters, }, }; -use sc_network_common::{ - protocol::ProtocolName, - request_responses::{ - IfDisconnected, IncomingRequest, OutgoingResponse, ProtocolConfig, RequestFailure, - }, -}; + +use sc_peerset::{PeersetHandle, BANNED_THRESHOLD}; + use std::{ collections::{hash_map::Entry, HashMap}, io, iter, @@ -66,8 +64,138 @@ use std::{ time::{Duration, Instant}, }; -pub use libp2p::request_response::{InboundFailure, OutboundFailure, RequestId}; -use sc_peerset::{PeersetHandle, BANNED_THRESHOLD}; +pub use libp2p::request_response::{ + InboundFailure, OutboundFailure, RequestId, RequestResponseConfig, +}; + +/// Error in a request. +#[derive(Debug, thiserror::Error)] +#[allow(missing_docs)] +pub enum RequestFailure { + #[error("We are not currently connected to the requested peer.")] + NotConnected, + #[error("Given protocol hasn't been registered.")] + UnknownProtocol, + #[error("Remote has closed the substream before answering, thereby signaling that it considers the request as valid, but refused to answer it.")] + Refused, + #[error("The remote replied, but the local node is no longer interested in the response.")] + Obsolete, + #[error("Problem on the network: {0}")] + Network(OutboundFailure), +} + +/// Configuration for a single request-response protocol. +#[derive(Debug, Clone)] +pub struct ProtocolConfig { + /// Name of the protocol on the wire. Should be something like `/foo/bar`. + pub name: ProtocolName, + + /// Fallback on the wire protocol names to support. + pub fallback_names: Vec, + + /// Maximum allowed size, in bytes, of a request. + /// + /// Any request larger than this value will be declined as a way to avoid allocating too + /// much memory for it. + pub max_request_size: u64, + + /// Maximum allowed size, in bytes, of a response. + /// + /// Any response larger than this value will be declined as a way to avoid allocating too + /// much memory for it. + pub max_response_size: u64, + + /// Duration after which emitted requests are considered timed out. + /// + /// If you expect the response to come back quickly, you should set this to a smaller duration. + pub request_timeout: Duration, + + /// Channel on which the networking service will send incoming requests. + /// + /// Every time a peer sends a request to the local node using this protocol, the networking + /// service will push an element on this channel. The receiving side of this channel then has + /// to pull this element, process the request, and send back the response to send back to the + /// peer. + /// + /// The size of the channel has to be carefully chosen. If the channel is full, the networking + /// service will discard the incoming request send back an error to the peer. Consequently, + /// the channel being full is an indicator that the node is overloaded. + /// + /// You can typically set the size of the channel to `T / d`, where `T` is the + /// `request_timeout` and `d` is the expected average duration of CPU and I/O it takes to + /// build a response. + /// + /// Can be `None` if the local node does not support answering incoming requests. + /// If this is `None`, then the local node will not advertise support for this protocol towards + /// other peers. If this is `Some` but the channel is closed, then the local node will + /// advertise support for this protocol, but any incoming request will lead to an error being + /// sent back. + pub inbound_queue: Option>, +} + +/// A single request received by a peer on a request-response protocol. +#[derive(Debug)] +pub struct IncomingRequest { + /// Who sent the request. + pub peer: PeerId, + + /// Request sent by the remote. Will always be smaller than + /// [`ProtocolConfig::max_request_size`]. + pub payload: Vec, + + /// Channel to send back the response. + /// + /// There are two ways to indicate that handling the request failed: + /// + /// 1. Drop `pending_response` and thus not changing the reputation of the peer. + /// + /// 2. Sending an `Err(())` via `pending_response`, optionally including reputation changes for + /// the given peer. + pub pending_response: oneshot::Sender, +} + +/// Response for an incoming request to be send by a request protocol handler. +#[derive(Debug)] +pub struct OutgoingResponse { + /// The payload of the response. + /// + /// `Err(())` if none is available e.g. due an error while handling the request. + pub result: Result, ()>, + + /// Reputation changes accrued while handling the request. To be applied to the reputation of + /// the peer sending the request. + pub reputation_changes: Vec, + + /// If provided, the `oneshot::Sender` will be notified when the request has been sent to the + /// peer. + /// + /// > **Note**: Operating systems typically maintain a buffer of a few dozen kilobytes of + /// > outgoing data for each TCP socket, and it is not possible for a user + /// > application to inspect this buffer. This channel here is not actually notified + /// > when the response has been fully sent out, but rather when it has fully been + /// > written to the buffer managed by the operating system. + pub sent_feedback: Option>, +} + +/// When sending a request, what to do on a disconnected recipient. +#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)] +pub enum IfDisconnected { + /// Try to connect to the peer. + TryConnect, + /// Just fail if the destination is not yet connected. + ImmediateError, +} + +/// Convenience functions for `IfDisconnected`. +impl IfDisconnected { + /// Shall we connect to a disconnected peer? + pub fn should_connect(self) -> bool { + match self { + Self::TryConnect => true, + Self::ImmediateError => false, + } + } +} /// Event generated by the [`RequestResponsesBehaviour`]. #[derive(Debug)] @@ -103,7 +231,12 @@ pub enum Event { }, /// A request protocol handler issued reputation changes for the given peer. - ReputationChanges { peer: PeerId, changes: Vec }, + ReputationChanges { + /// Peer whose reputation needs to be adjust. + peer: PeerId, + /// Reputation changes. + changes: Vec, + }, } /// Combination of a protocol name and a request id. @@ -344,7 +477,7 @@ impl NetworkBehaviour for RequestResponsesBehaviour { ) } }, - FromSwarm::DialFailure(DialFailure { peer_id, error, handler }) => + FromSwarm::DialFailure(DialFailure { peer_id, error, handler }) => { for (p_name, p_handler) in handler.into_iter() { if let Some((proto, _)) = self.protocols.get_mut(p_name.as_str()) { proto.on_swarm_event(FromSwarm::DialFailure(DialFailure { @@ -359,7 +492,8 @@ impl NetworkBehaviour for RequestResponsesBehaviour { p_name, ) } - }, + } + }, FromSwarm::ListenerClosed(e) => for (p, _) in self.protocols.values_mut() { NetworkBehaviour::on_swarm_event(p, FromSwarm::ListenerClosed(e)); diff --git a/client/network/src/service.rs b/client/network/src/service.rs index 4a0539361..6dc00b36c 100644 --- a/client/network/src/service.rs +++ b/client/network/src/service.rs @@ -29,13 +29,27 @@ use crate::{ behaviour::{self, Behaviour, BehaviourOut}, - config::Params, + config::{MultiaddrWithPeerId, Params, TransportConfig}, discovery::DiscoveryConfig, + error::Error, + event::{DhtEvent, Event}, network_state::{ NetworkState, NotConnectedPeer as NetworkStateNotConnectedPeer, Peer as NetworkStatePeer, }, protocol::{self, NotificationsSink, NotifsHandlerError, Protocol, Ready}, - transport, ReputationChange, + request_responses::{IfDisconnected, RequestFailure}, + service::{ + signature::{Signature, SigningError}, + traits::{ + NetworkDHTProvider, NetworkEventStream, NetworkNotification, NetworkPeers, + NetworkRequest, NetworkSigner, NetworkStateInfo, NetworkStatus, NetworkStatusProvider, + NotificationSender as NotificationSenderT, NotificationSenderError, + NotificationSenderReady as NotificationSenderReadyT, + }, + }, + transport, + types::ProtocolName, + ReputationChange, }; use futures::{channel::oneshot, prelude::*}; @@ -55,26 +69,13 @@ use libp2p::{ use log::{debug, error, info, trace, warn}; use metrics::{Histogram, HistogramVec, MetricSources, Metrics}; use parking_lot::Mutex; -use sc_network_common::{ - config::{MultiaddrWithPeerId, TransportConfig}, - error::Error, - protocol::{ - event::{DhtEvent, Event}, - ProtocolName, - }, - request_responses::{IfDisconnected, RequestFailure}, - service::{ - NetworkDHTProvider, NetworkEventStream, NetworkNotification, NetworkPeers, NetworkSigner, - NetworkStateInfo, NetworkStatus, NetworkStatusProvider, - NotificationSender as NotificationSenderT, NotificationSenderError, - NotificationSenderReady as NotificationSenderReadyT, Signature, SigningError, - }, - ExHashT, -}; + +use sc_network_common::ExHashT; use sc_peerset::PeersetHandle; use sc_utils::mpsc::{tracing_unbounded, TracingUnboundedReceiver, TracingUnboundedSender}; use sp_blockchain::HeaderBackend; use sp_runtime::traits::{Block as BlockT, Zero}; + use std::{ cmp, collections::{HashMap, HashSet}, @@ -90,14 +91,13 @@ use std::{ }; pub use behaviour::{InboundFailure, OutboundFailure, ResponseFailure}; +pub use libp2p::identity::{error::DecodingError, Keypair, PublicKey}; mod metrics; mod out_events; -#[cfg(test)] -mod tests; -pub use libp2p::identity::{error::DecodingError, Keypair, PublicKey}; -use sc_network_common::service::NetworkRequest; +pub mod signature; +pub mod traits; /// Custom error that can be produced by the [`ConnectionHandler`] of the [`NetworkBehaviour`]. /// Used as a template parameter of [`SwarmEvent`] below. @@ -1432,10 +1432,11 @@ where }, } }, - SwarmEvent::Behaviour(BehaviourOut::ReputationChanges { peer, changes }) => + SwarmEvent::Behaviour(BehaviourOut::ReputationChanges { peer, changes }) => { for change in changes { self.network_service.behaviour().user_protocol().report_peer(peer, change); - }, + } + }, SwarmEvent::Behaviour(BehaviourOut::PeerIdentify { peer_id, info: @@ -1467,10 +1468,11 @@ where .user_protocol_mut() .add_default_set_discovered_nodes(iter::once(peer_id)); }, - SwarmEvent::Behaviour(BehaviourOut::RandomKademliaStarted) => + SwarmEvent::Behaviour(BehaviourOut::RandomKademliaStarted) => { if let Some(metrics) = self.metrics.as_ref() { metrics.kademlia_random_queries_total.inc(); - }, + } + }, SwarmEvent::Behaviour(BehaviourOut::NotificationStreamOpened { remote, protocol, diff --git a/client/network/src/service/out_events.rs b/client/network/src/service/out_events.rs index 3771ea164..398c26793 100644 --- a/client/network/src/service/out_events.rs +++ b/client/network/src/service/out_events.rs @@ -31,11 +31,12 @@ //! - Send events by calling [`OutChannels::send`]. Events are cloned for each sender in the //! collection. +use crate::event::Event; + use futures::{prelude::*, ready, stream::FusedStream}; use log::error; use parking_lot::Mutex; use prometheus_endpoint::{register, CounterVec, GaugeVec, Opts, PrometheusError, Registry, U64}; -use sc_network_common::protocol::event::Event; use std::{ backtrace::Backtrace, cell::RefCell, diff --git a/client/network/common/src/service/signature.rs b/client/network/src/service/signature.rs similarity index 96% rename from client/network/common/src/service/signature.rs rename to client/network/src/service/signature.rs index 14addafc6..e52dd6b1d 100644 --- a/client/network/common/src/service/signature.rs +++ b/client/network/src/service/signature.rs @@ -19,10 +19,12 @@ // If you read this, you are very thorough, congratulations. use libp2p::{ - identity::{error::SigningError, Keypair, PublicKey}, + identity::{Keypair, PublicKey}, PeerId, }; +pub use libp2p::identity::error::SigningError; + /// A result of signing a message with a network identity. Since `PeerId` is potentially a hash of a /// `PublicKey`, you need to reveal the `PublicKey` next to the signature, so the verifier can check /// if the signature was made by the entity that controls a given `PeerId`. diff --git a/client/network/src/service/tests/mod.rs b/client/network/src/service/tests/mod.rs deleted file mode 100644 index f29e43e6c..000000000 --- a/client/network/src/service/tests/mod.rs +++ /dev/null @@ -1,240 +0,0 @@ -// This file is part of Substrate. - -// Copyright (C) Parity Technologies (UK) Ltd. -// SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 - -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with this program. If not, see . - -use crate::{config, NetworkService, NetworkWorker}; - -use futures::prelude::*; -use libp2p::Multiaddr; -use sc_consensus::{ImportQueue, Link}; -use sc_network_common::{ - config::{NonDefaultSetConfig, ProtocolId, SetConfig, TransportConfig}, - protocol::{event::Event, role::Roles}, - service::NetworkEventStream, -}; -use sc_network_light::light_client_requests::handler::LightClientRequestHandler; -use sc_network_sync::{ - block_request_handler::BlockRequestHandler, - engine::SyncingEngine, - service::network::{NetworkServiceHandle, NetworkServiceProvider}, - state_request_handler::StateRequestHandler, -}; -use sp_runtime::traits::Block as BlockT; -use std::sync::Arc; -use substrate_test_runtime_client::{ - runtime::{Block as TestBlock, Hash as TestHash}, - TestClientBuilder, TestClientBuilderExt as _, -}; - -#[cfg(test)] -mod service; - -type TestNetworkWorker = NetworkWorker; -type TestNetworkService = NetworkService; - -const PROTOCOL_NAME: &str = "/foo"; - -struct TestNetwork { - network: TestNetworkWorker, -} - -impl TestNetwork { - pub fn new(network: TestNetworkWorker) -> Self { - Self { network } - } - - pub fn start_network( - self, - ) -> (Arc, (impl Stream + std::marker::Unpin)) { - let worker = self.network; - let service = worker.service().clone(); - let event_stream = service.event_stream("test"); - - tokio::spawn(worker.run()); - - (service, event_stream) - } -} - -struct TestNetworkBuilder { - import_queue: Option>>, - link: Option>>, - client: Option>, - listen_addresses: Vec, - set_config: Option, - chain_sync_network: Option<(NetworkServiceProvider, NetworkServiceHandle)>, - config: Option, -} - -impl TestNetworkBuilder { - pub fn new() -> Self { - Self { - import_queue: None, - link: None, - client: None, - listen_addresses: Vec::new(), - set_config: None, - chain_sync_network: None, - config: None, - } - } - - pub fn with_config(mut self, config: config::NetworkConfiguration) -> Self { - self.config = Some(config); - self - } - - pub fn with_listen_addresses(mut self, addresses: Vec) -> Self { - self.listen_addresses = addresses; - self - } - - pub fn with_set_config(mut self, set_config: SetConfig) -> Self { - self.set_config = Some(set_config); - self - } - - pub fn build(mut self) -> TestNetwork { - let client = self.client.as_mut().map_or( - Arc::new(TestClientBuilder::with_default_backend().build_with_longest_chain().0), - |v| v.clone(), - ); - - let network_config = self.config.unwrap_or(config::NetworkConfiguration { - extra_sets: vec![NonDefaultSetConfig { - notifications_protocol: PROTOCOL_NAME.into(), - fallback_names: Vec::new(), - max_notification_size: 1024 * 1024, - handshake: None, - set_config: self.set_config.unwrap_or_default(), - }], - listen_addresses: self.listen_addresses, - transport: TransportConfig::MemoryOnly, - ..config::NetworkConfiguration::new_local() - }); - - #[derive(Clone)] - struct PassThroughVerifier(bool); - - #[async_trait::async_trait] - impl sc_consensus::Verifier for PassThroughVerifier { - async fn verify( - &mut self, - mut block: sc_consensus::BlockImportParams, - ) -> Result, String> { - block.finalized = self.0; - block.fork_choice = Some(sc_consensus::ForkChoiceStrategy::LongestChain); - Ok(block) - } - } - - let mut import_queue = - self.import_queue.unwrap_or(Box::new(sc_consensus::BasicQueue::new( - PassThroughVerifier(false), - Box::new(client.clone()), - None, - &sp_core::testing::TaskExecutor::new(), - None, - ))); - - let protocol_id = ProtocolId::from("test-protocol-name"); - let fork_id = Some(String::from("test-fork-id")); - - let block_request_protocol_config = { - let (handler, protocol_config) = - BlockRequestHandler::new(&protocol_id, None, client.clone(), 50); - tokio::spawn(handler.run().boxed()); - protocol_config - }; - - let state_request_protocol_config = { - let (handler, protocol_config) = - StateRequestHandler::new(&protocol_id, None, client.clone(), 50); - tokio::spawn(handler.run().boxed()); - protocol_config - }; - - let light_client_request_protocol_config = { - let (handler, protocol_config) = - LightClientRequestHandler::new(&protocol_id, None, client.clone()); - tokio::spawn(handler.run().boxed()); - protocol_config - }; - - let (chain_sync_network_provider, chain_sync_network_handle) = - self.chain_sync_network.unwrap_or(NetworkServiceProvider::new()); - - let (engine, chain_sync_service, block_announce_config) = SyncingEngine::new( - Roles::from(&config::Role::Full), - client.clone(), - None, - &network_config, - protocol_id.clone(), - &None, - Box::new(sp_consensus::block_validation::DefaultBlockAnnounceValidator), - None, - chain_sync_network_handle, - import_queue.service(), - block_request_protocol_config.name.clone(), - state_request_protocol_config.name.clone(), - None, - ) - .unwrap(); - let mut link = self.link.unwrap_or(Box::new(chain_sync_service.clone())); - let worker = NetworkWorker::< - substrate_test_runtime_client::runtime::Block, - substrate_test_runtime_client::runtime::Hash, - >::new(config::Params { - block_announce_config, - role: config::Role::Full, - executor: Box::new(|f| { - tokio::spawn(f); - }), - network_config, - chain: client.clone(), - protocol_id, - fork_id, - metrics_registry: None, - request_response_protocol_configs: [ - block_request_protocol_config, - state_request_protocol_config, - light_client_request_protocol_config, - ] - .to_vec(), - }) - .unwrap(); - - let service = worker.service().clone(); - tokio::spawn(async move { - let _ = chain_sync_network_provider.run(service).await; - }); - tokio::spawn(async move { - loop { - futures::future::poll_fn(|cx| { - import_queue.poll_actions(cx, &mut *link); - std::task::Poll::Ready(()) - }) - .await; - tokio::time::sleep(std::time::Duration::from_millis(250)).await; - } - }); - let stream = worker.service().event_stream("syncing"); - tokio::spawn(engine.run(stream)); - - TestNetwork::new(worker) - } -} diff --git a/client/network/common/src/service.rs b/client/network/src/service/traits.rs similarity index 99% rename from client/network/common/src/service.rs rename to client/network/src/service/traits.rs index d3c5c2f43..3f9b7e552 100644 --- a/client/network/common/src/service.rs +++ b/client/network/src/service/traits.rs @@ -20,17 +20,20 @@ use crate::{ config::MultiaddrWithPeerId, - protocol::{event::Event, ProtocolName}, + event::Event, request_responses::{IfDisconnected, RequestFailure}, + service::signature::Signature, + types::ProtocolName, }; + use futures::{channel::oneshot, Stream}; -pub use libp2p::{identity::error::SigningError, kad::record::Key as KademliaKey}; use libp2p::{Multiaddr, PeerId}; + use sc_peerset::ReputationChange; -pub use signature::Signature; + use std::{collections::HashSet, future::Future, pin::Pin, sync::Arc}; -mod signature; +pub use libp2p::{identity::error::SigningError, kad::record::Key as KademliaKey}; /// Signer with network identity pub trait NetworkSigner { diff --git a/client/network/common/src/protocol.rs b/client/network/src/types.rs similarity index 99% rename from client/network/common/src/protocol.rs rename to client/network/src/types.rs index bfeb1daf5..b0e32ae10 100644 --- a/client/network/common/src/protocol.rs +++ b/client/network/src/types.rs @@ -16,6 +16,10 @@ // You should have received a copy of the GNU General Public License // along with this program. If not, see . +//! `sc-network` type definitions + +use libp2p::core::upgrade; + use std::{ borrow::Borrow, fmt, @@ -24,11 +28,6 @@ use std::{ sync::Arc, }; -use libp2p::core::upgrade; - -pub mod event; -pub mod role; - /// The protocol name transmitted on the wire. #[derive(Debug, Clone)] pub enum ProtocolName { diff --git a/client/network/common/src/utils.rs b/client/network/src/utils.rs similarity index 98% rename from client/network/common/src/utils.rs rename to client/network/src/utils.rs index c32d264a9..8db2cf4e7 100644 --- a/client/network/common/src/utils.rs +++ b/client/network/src/utils.rs @@ -16,9 +16,12 @@ // You should have received a copy of the GNU General Public License // along with this program. If not, see . +//! `sc-network` utilities + use futures::{stream::unfold, FutureExt, Stream, StreamExt}; use futures_timer::Delay; use linked_hash_set::LinkedHashSet; + use std::{hash::Hash, num::NonZeroUsize, time::Duration}; /// Creates a stream that returns a new value every `duration`. diff --git a/client/network/sync/Cargo.toml b/client/network/sync/Cargo.toml index 52ab0d15e..298cbf180 100644 --- a/client/network/sync/Cargo.toml +++ b/client/network/sync/Cargo.toml @@ -32,6 +32,7 @@ fork-tree = { version = "3.0.0", path = "../../../utils/fork-tree" } prometheus-endpoint = { package = "substrate-prometheus-endpoint", version = "0.10.0-dev", path = "../../../utils/prometheus" } sc-client-api = { version = "4.0.0-dev", path = "../../api" } sc-consensus = { version = "0.10.0-dev", path = "../../consensus/common" } +sc-network = { version = "0.10.0-dev", path = "../" } sc-network-common = { version = "0.10.0-dev", path = "../common" } sc-peerset = { version = "4.0.0-dev", path = "../../peerset" } sc-utils = { version = "4.0.0-dev", path = "../../utils" } diff --git a/client/network/sync/src/block_request_handler.rs b/client/network/sync/src/block_request_handler.rs index 38aca2cde..921efd7de 100644 --- a/client/network/sync/src/block_request_handler.rs +++ b/client/network/sync/src/block_request_handler.rs @@ -18,6 +18,7 @@ //! `crate::request_responses::RequestResponsesBehaviour`. use crate::schema::v1::{block_request::FromBlock, BlockResponse, Direction}; + use codec::{Decode, Encode}; use futures::{ channel::{mpsc, oneshot}, @@ -27,17 +28,19 @@ use libp2p::PeerId; use log::debug; use lru::LruCache; use prost::Message; + use sc_client_api::BlockBackend; -use sc_network_common::{ +use sc_network::{ config::ProtocolId, request_responses::{IncomingRequest, OutgoingResponse, ProtocolConfig}, - sync::message::BlockAttributes, }; +use sc_network_common::sync::message::BlockAttributes; use sp_blockchain::HeaderBackend; use sp_runtime::{ generic::BlockId, traits::{Block as BlockT, Header, One, Zero}, }; + use std::{ cmp::min, hash::{Hash, Hasher}, diff --git a/client/network/sync/src/engine.rs b/client/network/sync/src/engine.rs index 25cd3968c..e6e62101f 100644 --- a/client/network/sync/src/engine.rs +++ b/client/network/sync/src/engine.rs @@ -24,22 +24,27 @@ use crate::{ ChainSync, ClientError, SyncingService, }; +use codec::{Decode, DecodeAll, Encode}; use futures::{FutureExt, Stream, StreamExt}; +use futures_timer::Delay; use libp2p::PeerId; use lru::LruCache; use prometheus_endpoint::{ register, Gauge, GaugeVec, MetricSource, Opts, PrometheusError, Registry, SourcedGauge, U64, }; -use codec::{Decode, DecodeAll, Encode}; -use futures_timer::Delay; use sc_client_api::{BlockBackend, HeaderBackend, ProofProvider}; use sc_consensus::import_queue::ImportQueueService; -use sc_network_common::{ +use sc_network::{ config::{ NetworkConfiguration, NonDefaultSetConfig, ProtocolId, SyncMode as SyncOperationMode, }, - protocol::{event::Event, role::Roles, ProtocolName}, + event::Event, + utils::LruHashSet, + ProtocolName, +}; +use sc_network_common::{ + role::Roles, sync::{ message::{ generic::{BlockData, BlockResponse}, @@ -49,7 +54,6 @@ use sc_network_common::{ BadPeer, ChainSync as ChainSyncT, ExtendedPeerInfo, PollBlockAnnounceValidation, SyncEvent, SyncMode, }, - utils::LruHashSet, }; use sc_utils::mpsc::{tracing_unbounded, TracingUnboundedReceiver, TracingUnboundedSender}; use sp_blockchain::HeaderMetadata; diff --git a/client/network/sync/src/lib.rs b/client/network/sync/src/lib.rs index 23269b02b..45d14ffa7 100644 --- a/client/network/sync/src/lib.rs +++ b/client/network/sync/src/lib.rs @@ -28,23 +28,13 @@ //! the network, or whenever a block has been successfully verified, call the appropriate method in //! order to update it. -pub mod block_request_handler; -pub mod blocks; -pub mod engine; -pub mod mock; -mod schema; -pub mod service; -pub mod state; -pub mod state_request_handler; -pub mod warp; -pub mod warp_request_handler; - use crate::{ blocks::BlockCollection, schema::v1::{StateRequest, StateResponse}, state::StateSync, warp::{WarpProofImportResult, WarpSync}, }; + use codec::{Decode, DecodeAll, Encode}; use extra_requests::ExtraRequests; use futures::{ @@ -52,18 +42,22 @@ use futures::{ }; use libp2p::{request_response::OutboundFailure, PeerId}; use log::{debug, error, info, trace, warn}; -use prometheus_endpoint::{register, Counter, PrometheusError, Registry, U64}; use prost::Message; + +use prometheus_endpoint::{register, Counter, PrometheusError, Registry, U64}; use sc_client_api::{BlockBackend, ProofProvider}; use sc_consensus::{ import_queue::ImportQueueService, BlockImportError, BlockImportStatus, IncomingBlock, }; -use sc_network_common::{ +use sc_network::{ config::{ NonDefaultSetConfig, NonReservedPeerMode, NotificationHandshake, ProtocolId, SetConfig, }, - protocol::{role::Roles, ProtocolName}, request_responses::{IfDisconnected, RequestFailure}, + types::ProtocolName, +}; +use sc_network_common::{ + role::Roles, sync::{ message::{ BlockAnnounce, BlockAnnouncesHandshake, BlockAttributes, BlockData, BlockRequest, @@ -76,7 +70,6 @@ use sc_network_common::{ SyncState, SyncStatus, }, }; -pub use service::chain_sync::SyncingService; use sp_arithmetic::traits::Saturating; use sp_blockchain::{Error as ClientError, HeaderBackend, HeaderMetadata}; use sp_consensus::{ @@ -90,6 +83,7 @@ use sp_runtime::{ }, EncodedJustification, Justifications, }; + use std::{ collections::{hash_map::Entry, HashMap, HashSet}, iter, @@ -97,9 +91,21 @@ use std::{ pin::Pin, sync::Arc, }; -use warp::TargetBlockImportResult; + +pub use service::chain_sync::SyncingService; mod extra_requests; +mod schema; + +pub mod block_request_handler; +pub mod blocks; +pub mod engine; +pub mod mock; +pub mod service; +pub mod state; +pub mod state_request_handler; +pub mod warp; +pub mod warp_request_handler; /// Maximum blocks to request in a single packet. const MAX_BLOCKS_TO_REQUEST: usize = 64; @@ -927,9 +933,9 @@ where match warp_sync.import_target_block( blocks.pop().expect("`blocks` len checked above."), ) { - TargetBlockImportResult::Success => + warp::TargetBlockImportResult::Success => return Ok(OnBlockData::Continue), - TargetBlockImportResult::BadResponse => + warp::TargetBlockImportResult::BadResponse => return Err(BadPeer(*who, rep::VERIFICATION_FAIL)), } } else if blocks.is_empty() { @@ -3160,7 +3166,7 @@ mod test { use futures::{executor::block_on, future::poll_fn}; use sc_block_builder::BlockBuilderProvider; use sc_network_common::{ - protocol::role::Role, + role::Role, sync::message::{BlockData, BlockState, FromBlock}, }; use sp_blockchain::HeaderBackend; diff --git a/client/network/sync/src/service/chain_sync.rs b/client/network/sync/src/service/chain_sync.rs index 99b419774..f9e0e401f 100644 --- a/client/network/sync/src/service/chain_sync.rs +++ b/client/network/sync/src/service/chain_sync.rs @@ -20,9 +20,9 @@ use futures::{channel::oneshot, Stream}; use libp2p::PeerId; use sc_consensus::{BlockImportError, BlockImportStatus, JustificationSyncLink, Link}; -use sc_network_common::{ - service::{NetworkBlock, NetworkSyncForkRequest}, - sync::{ExtendedPeerInfo, SyncEvent, SyncEventStream, SyncStatus, SyncStatusProvider}, +use sc_network::{NetworkBlock, NetworkSyncForkRequest}; +use sc_network_common::sync::{ + ExtendedPeerInfo, SyncEvent, SyncEventStream, SyncStatus, SyncStatusProvider, }; use sc_utils::mpsc::{tracing_unbounded, TracingUnboundedSender}; use sp_runtime::traits::{Block as BlockT, NumberFor}; diff --git a/client/network/sync/src/service/mock.rs b/client/network/sync/src/service/mock.rs index 2853616ad..c88263399 100644 --- a/client/network/sync/src/service/mock.rs +++ b/client/network/sync/src/service/mock.rs @@ -18,18 +18,18 @@ use futures::channel::oneshot; use libp2p::{Multiaddr, PeerId}; + use sc_consensus::{BlockImportError, BlockImportStatus}; -use sc_network_common::{ +use sc_network::{ config::MultiaddrWithPeerId, - protocol::ProtocolName, request_responses::{IfDisconnected, RequestFailure}, - service::{ - NetworkNotification, NetworkPeers, NetworkRequest, NetworkSyncForkRequest, - NotificationSender, NotificationSenderError, - }, + types::ProtocolName, + NetworkNotification, NetworkPeers, NetworkRequest, NetworkSyncForkRequest, + NotificationSenderError, NotificationSenderT, }; use sc_peerset::ReputationChange; use sp_runtime::traits::{Block as BlockT, NumberFor}; + use std::collections::HashSet; mockall::mock! { @@ -135,7 +135,7 @@ mockall::mock! { &self, target: PeerId, protocol: ProtocolName, - ) -> Result, NotificationSenderError>; + ) -> Result, NotificationSenderError>; fn set_notification_handshake(&self, protocol: ProtocolName, handshake: Vec); } } diff --git a/client/network/sync/src/service/network.rs b/client/network/sync/src/service/network.rs index a1f4f27bb..f87de1c4c 100644 --- a/client/network/sync/src/service/network.rs +++ b/client/network/sync/src/service/network.rs @@ -18,13 +18,15 @@ use futures::{channel::oneshot, StreamExt}; use libp2p::PeerId; -use sc_network_common::{ - protocol::ProtocolName, + +use sc_network::{ request_responses::{IfDisconnected, RequestFailure}, - service::{NetworkNotification, NetworkPeers, NetworkRequest}, + types::ProtocolName, + NetworkNotification, NetworkPeers, NetworkRequest, }; use sc_peerset::ReputationChange; use sc_utils::mpsc::{tracing_unbounded, TracingUnboundedReceiver, TracingUnboundedSender}; + use std::sync::Arc; /// Network-related services required by `sc-network-sync` diff --git a/client/network/sync/src/state_request_handler.rs b/client/network/sync/src/state_request_handler.rs index 2f615f4ea..0ce2c541b 100644 --- a/client/network/sync/src/state_request_handler.rs +++ b/client/network/sync/src/state_request_handler.rs @@ -18,6 +18,7 @@ //! `crate::request_responses::RequestResponsesBehaviour`. use crate::schema::v1::{KeyValueStateEntry, StateEntry, StateRequest, StateResponse}; + use codec::{Decode, Encode}; use futures::{ channel::{mpsc, oneshot}, @@ -27,12 +28,14 @@ use libp2p::PeerId; use log::{debug, trace}; use lru::LruCache; use prost::Message; + use sc_client_api::{BlockBackend, ProofProvider}; -use sc_network_common::{ +use sc_network::{ config::ProtocolId, request_responses::{IncomingRequest, OutgoingResponse, ProtocolConfig}, }; use sp_runtime::traits::Block as BlockT; + use std::{ hash::{Hash, Hasher}, num::NonZeroUsize, diff --git a/client/network/sync/src/warp_request_handler.rs b/client/network/sync/src/warp_request_handler.rs index fcb0d116b..7061d6485 100644 --- a/client/network/sync/src/warp_request_handler.rs +++ b/client/network/sync/src/warp_request_handler.rs @@ -22,14 +22,16 @@ use futures::{ stream::StreamExt, }; use log::debug; -use sc_network_common::{ + +use sc_network::{ config::ProtocolId, request_responses::{ IncomingRequest, OutgoingResponse, ProtocolConfig as RequestResponseConfig, }, - sync::warp::{EncodedProof, WarpProofRequest, WarpSyncProvider}, }; +use sc_network_common::sync::warp::{EncodedProof, WarpProofRequest, WarpSyncProvider}; use sp_runtime::traits::Block as BlockT; + use std::{sync::Arc, time::Duration}; const MAX_RESPONSE_SIZE: u64 = 16 * 1024 * 1024; diff --git a/client/network/test/src/lib.rs b/client/network/test/src/lib.rs index a79e17e2f..75b8287b0 100644 --- a/client/network/test/src/lib.rs +++ b/client/network/test/src/lib.rs @@ -20,6 +20,8 @@ #[cfg(test)] mod block_import; #[cfg(test)] +mod service; +#[cfg(test)] mod sync; use std::{ @@ -46,14 +48,18 @@ use sc_consensus::{ ForkChoiceStrategy, ImportQueue, ImportResult, JustificationImport, JustificationSyncLink, LongestChain, Verifier, }; -use sc_network::{Multiaddr, NetworkService, NetworkWorker}; -use sc_network_common::{ +use sc_network::{ config::{ MultiaddrWithPeerId, NetworkConfiguration, NonDefaultSetConfig, NonReservedPeerMode, - ProtocolId, RequestResponseConfig, Role, SyncMode, TransportConfig, + ProtocolId, Role, SyncMode, TransportConfig, }, - protocol::{role::Roles, ProtocolName}, - service::{NetworkBlock, NetworkEventStream, NetworkStateInfo, NetworkSyncForkRequest}, + request_responses::ProtocolConfig as RequestResponseConfig, + types::ProtocolName, + Multiaddr, NetworkBlock, NetworkEventStream, NetworkService, NetworkStateInfo, + NetworkSyncForkRequest, NetworkWorker, +}; +use sc_network_common::{ + role::Roles, sync::warp::{ AuthorityList, EncodedProof, SetId, VerificationResult, WarpSyncParams, WarpSyncProvider, }, diff --git a/client/network/src/service/tests/service.rs b/client/network/test/src/service.rs similarity index 72% rename from client/network/src/service/tests/service.rs rename to client/network/test/src/service.rs index 9c4c0ad6e..b1de2a91e 100644 --- a/client/network/src/service/tests/service.rs +++ b/client/network/test/src/service.rs @@ -16,24 +16,228 @@ // You should have received a copy of the GNU General Public License // along with this program. If not, see . -use crate::{config, service::tests::TestNetworkBuilder, NetworkService}; - use futures::prelude::*; -use libp2p::PeerId; -use sc_network_common::{ - config::{MultiaddrWithPeerId, NonDefaultSetConfig, SetConfig, TransportConfig}, - protocol::event::Event, - service::{NetworkNotification, NetworkPeers, NetworkStateInfo}, +use libp2p::{Multiaddr, PeerId}; + +use sc_consensus::{ImportQueue, Link}; +use sc_network::{ + config::{self, MultiaddrWithPeerId, ProtocolId, TransportConfig}, + event::Event, + NetworkEventStream, NetworkNotification, NetworkPeers, NetworkService, NetworkStateInfo, + NetworkWorker, +}; +use sc_network_common::role::Roles; +use sc_network_light::light_client_requests::handler::LightClientRequestHandler; +use sc_network_sync::{ + block_request_handler::BlockRequestHandler, + engine::SyncingEngine, + service::network::{NetworkServiceHandle, NetworkServiceProvider}, + state_request_handler::StateRequestHandler, }; +use sp_runtime::traits::Block as BlockT; +use substrate_test_runtime_client::{ + runtime::{Block as TestBlock, Hash as TestHash}, + TestClientBuilder, TestClientBuilderExt as _, +}; + use std::{sync::Arc, time::Duration}; -type TestNetworkService = NetworkService< - substrate_test_runtime_client::runtime::Block, - substrate_test_runtime_client::runtime::Hash, ->; +type TestNetworkWorker = NetworkWorker; +type TestNetworkService = NetworkService; const PROTOCOL_NAME: &str = "/foo"; +struct TestNetwork { + network: TestNetworkWorker, +} + +impl TestNetwork { + pub fn new(network: TestNetworkWorker) -> Self { + Self { network } + } + + pub fn start_network( + self, + ) -> (Arc, (impl Stream + std::marker::Unpin)) { + let worker = self.network; + let service = worker.service().clone(); + let event_stream = service.event_stream("test"); + + tokio::spawn(worker.run()); + + (service, event_stream) + } +} + +struct TestNetworkBuilder { + import_queue: Option>>, + link: Option>>, + client: Option>, + listen_addresses: Vec, + set_config: Option, + chain_sync_network: Option<(NetworkServiceProvider, NetworkServiceHandle)>, + config: Option, +} + +impl TestNetworkBuilder { + pub fn new() -> Self { + Self { + import_queue: None, + link: None, + client: None, + listen_addresses: Vec::new(), + set_config: None, + chain_sync_network: None, + config: None, + } + } + + pub fn with_config(mut self, config: config::NetworkConfiguration) -> Self { + self.config = Some(config); + self + } + + pub fn with_listen_addresses(mut self, addresses: Vec) -> Self { + self.listen_addresses = addresses; + self + } + + pub fn with_set_config(mut self, set_config: config::SetConfig) -> Self { + self.set_config = Some(set_config); + self + } + + pub fn build(mut self) -> TestNetwork { + let client = self.client.as_mut().map_or( + Arc::new(TestClientBuilder::with_default_backend().build_with_longest_chain().0), + |v| v.clone(), + ); + + let network_config = self.config.unwrap_or(config::NetworkConfiguration { + extra_sets: vec![config::NonDefaultSetConfig { + notifications_protocol: PROTOCOL_NAME.into(), + fallback_names: Vec::new(), + max_notification_size: 1024 * 1024, + handshake: None, + set_config: self.set_config.unwrap_or_default(), + }], + listen_addresses: self.listen_addresses, + transport: TransportConfig::MemoryOnly, + ..config::NetworkConfiguration::new_local() + }); + + #[derive(Clone)] + struct PassThroughVerifier(bool); + + #[async_trait::async_trait] + impl sc_consensus::Verifier for PassThroughVerifier { + async fn verify( + &mut self, + mut block: sc_consensus::BlockImportParams, + ) -> Result, String> { + block.finalized = self.0; + block.fork_choice = Some(sc_consensus::ForkChoiceStrategy::LongestChain); + Ok(block) + } + } + + let mut import_queue = + self.import_queue.unwrap_or(Box::new(sc_consensus::BasicQueue::new( + PassThroughVerifier(false), + Box::new(client.clone()), + None, + &sp_core::testing::TaskExecutor::new(), + None, + ))); + + let protocol_id = ProtocolId::from("test-protocol-name"); + let fork_id = Some(String::from("test-fork-id")); + + let block_request_protocol_config = { + let (handler, protocol_config) = + BlockRequestHandler::new(&protocol_id, None, client.clone(), 50); + tokio::spawn(handler.run().boxed()); + protocol_config + }; + + let state_request_protocol_config = { + let (handler, protocol_config) = + StateRequestHandler::new(&protocol_id, None, client.clone(), 50); + tokio::spawn(handler.run().boxed()); + protocol_config + }; + + let light_client_request_protocol_config = { + let (handler, protocol_config) = + LightClientRequestHandler::new(&protocol_id, None, client.clone()); + tokio::spawn(handler.run().boxed()); + protocol_config + }; + + let (chain_sync_network_provider, chain_sync_network_handle) = + self.chain_sync_network.unwrap_or(NetworkServiceProvider::new()); + + let (engine, chain_sync_service, block_announce_config) = SyncingEngine::new( + Roles::from(&config::Role::Full), + client.clone(), + None, + &network_config, + protocol_id.clone(), + &None, + Box::new(sp_consensus::block_validation::DefaultBlockAnnounceValidator), + None, + chain_sync_network_handle, + import_queue.service(), + block_request_protocol_config.name.clone(), + state_request_protocol_config.name.clone(), + None, + ) + .unwrap(); + let mut link = self.link.unwrap_or(Box::new(chain_sync_service.clone())); + let worker = NetworkWorker::< + substrate_test_runtime_client::runtime::Block, + substrate_test_runtime_client::runtime::Hash, + >::new(config::Params { + block_announce_config, + role: config::Role::Full, + executor: Box::new(|f| { + tokio::spawn(f); + }), + network_config, + chain: client.clone(), + protocol_id, + fork_id, + metrics_registry: None, + request_response_protocol_configs: [ + block_request_protocol_config, + state_request_protocol_config, + light_client_request_protocol_config, + ] + .to_vec(), + }) + .unwrap(); + + let service = worker.service().clone(); + tokio::spawn(async move { + let _ = chain_sync_network_provider.run(service).await; + }); + tokio::spawn(async move { + loop { + futures::future::poll_fn(|cx| { + import_queue.poll_actions(cx, &mut *link); + std::task::Poll::Ready(()) + }) + .await; + tokio::time::sleep(std::time::Duration::from_millis(250)).await; + } + }); + let stream = worker.service().event_stream("syncing"); + tokio::spawn(engine.run(stream)); + + TestNetwork::new(worker) + } +} + /// Builds two nodes and their associated events stream. /// The nodes are connected together and have the `PROTOCOL_NAME` protocol registered. fn build_nodes_one_proto() -> ( @@ -50,7 +254,7 @@ fn build_nodes_one_proto() -> ( .start_network(); let (node2, events_stream2) = TestNetworkBuilder::new() - .with_set_config(SetConfig { + .with_set_config(config::SetConfig { reserved_nodes: vec![MultiaddrWithPeerId { multiaddr: listen_addr, peer_id: node1.local_peer_id(), @@ -208,7 +412,7 @@ async fn lots_of_incoming_peers_works() { let (main_node, _) = TestNetworkBuilder::new() .with_listen_addresses(vec![listen_addr.clone()]) - .with_set_config(SetConfig { in_peers: u32::MAX, ..Default::default() }) + .with_set_config(config::SetConfig { in_peers: u32::MAX, ..Default::default() }) .build() .start_network(); @@ -220,7 +424,7 @@ async fn lots_of_incoming_peers_works() { for _ in 0..32 { let (_dialing_node, event_stream) = TestNetworkBuilder::new() - .with_set_config(SetConfig { + .with_set_config(config::SetConfig { reserved_nodes: vec![MultiaddrWithPeerId { multiaddr: listen_addr.clone(), peer_id: main_node_peer_id, @@ -289,10 +493,11 @@ async fn notifications_back_pressure() { while received_notifications < TOTAL_NOTIFS { match events_stream2.next().await.unwrap() { - Event::NotificationStreamOpened { protocol, .. } => + Event::NotificationStreamOpened { protocol, .. } => { if let None = sync_protocol_name { sync_protocol_name = Some(protocol); - }, + } + }, Event::NotificationStreamClosed { protocol, .. } => { if Some(&protocol) != sync_protocol_name.as_ref() { panic!() @@ -344,7 +549,7 @@ async fn fallback_name_working() { let listen_addr = config::build_multiaddr![Memory(rand::random::())]; let (node1, mut events_stream1) = TestNetworkBuilder::new() .with_config(config::NetworkConfiguration { - extra_sets: vec![NonDefaultSetConfig { + extra_sets: vec![config::NonDefaultSetConfig { notifications_protocol: NEW_PROTOCOL_NAME.into(), fallback_names: vec![PROTOCOL_NAME.into()], max_notification_size: 1024 * 1024, @@ -359,7 +564,7 @@ async fn fallback_name_working() { .start_network(); let (_, mut events_stream2) = TestNetworkBuilder::new() - .with_set_config(SetConfig { + .with_set_config(config::SetConfig { reserved_nodes: vec![MultiaddrWithPeerId { multiaddr: listen_addr, peer_id: node1.local_peer_id(), @@ -500,7 +705,7 @@ async fn ensure_reserved_node_addresses_consistent_with_transport_memory() { .with_config(config::NetworkConfiguration { listen_addresses: vec![listen_addr.clone()], transport: TransportConfig::MemoryOnly, - default_peers_set: SetConfig { + default_peers_set: config::SetConfig { reserved_nodes: vec![reserved_node], ..Default::default() }, @@ -527,7 +732,7 @@ async fn ensure_reserved_node_addresses_consistent_with_transport_not_memory() { let _ = TestNetworkBuilder::new() .with_config(config::NetworkConfiguration { listen_addresses: vec![listen_addr.clone()], - default_peers_set: SetConfig { + default_peers_set: config::SetConfig { reserved_nodes: vec![reserved_node], ..Default::default() }, diff --git a/client/network/transactions/Cargo.toml b/client/network/transactions/Cargo.toml index cab702fbd..3616473d3 100644 --- a/client/network/transactions/Cargo.toml +++ b/client/network/transactions/Cargo.toml @@ -20,6 +20,7 @@ libp2p = "0.50.0" log = "0.4.17" pin-project = "1.0.12" prometheus-endpoint = { package = "substrate-prometheus-endpoint", version = "0.10.0-dev", path = "../../../utils/prometheus" } +sc-network = { version = "0.10.0-dev", path = "../" } sc-network-common = { version = "0.10.0-dev", path = "../common" } sc-peerset = { version = "4.0.0-dev", path = "../../peerset" } sc-utils = { version = "4.0.0-dev", path = "../../utils" } diff --git a/client/network/transactions/src/lib.rs b/client/network/transactions/src/lib.rs index 48bb39494..381dd654b 100644 --- a/client/network/transactions/src/lib.rs +++ b/client/network/transactions/src/lib.rs @@ -27,22 +27,29 @@ //! `Future` that processes transactions. use crate::config::*; + use codec::{Decode, Encode}; use futures::{prelude::*, stream::FuturesUnordered}; use libp2p::{multiaddr, PeerId}; use log::{debug, trace, warn}; + use prometheus_endpoint::{register, Counter, PrometheusError, Registry, U64}; -use sc_network_common::{ +use sc_network::{ config::{NonDefaultSetConfig, NonReservedPeerMode, ProtocolId, SetConfig}, error, - protocol::{event::Event, role::ObservedRole, ProtocolName}, - service::{NetworkEventStream, NetworkNotification, NetworkPeers}, - sync::{SyncEvent, SyncEventStream}, + event::Event, + types::ProtocolName, utils::{interval, LruHashSet}, + NetworkEventStream, NetworkNotification, NetworkPeers, +}; +use sc_network_common::{ + role::ObservedRole, + sync::{SyncEvent, SyncEventStream}, ExHashT, }; use sc_utils::mpsc::{tracing_unbounded, TracingUnboundedReceiver, TracingUnboundedSender}; use sp_runtime::traits::Block as BlockT; + use std::{ collections::{hash_map::Entry, HashMap}, iter, diff --git a/client/offchain/Cargo.toml b/client/offchain/Cargo.toml index dd6e2e44c..0307e3125 100644 --- a/client/offchain/Cargo.toml +++ b/client/offchain/Cargo.toml @@ -29,6 +29,7 @@ rand = "0.8.5" threadpool = "1.7" tracing = "0.1.29" sc-client-api = { version = "4.0.0-dev", path = "../api" } +sc-network = { version = "0.10.0-dev", path = "../network" } sc-network-common = { version = "0.10.0-dev", path = "../network/common" } sc-peerset = { version = "4.0.0-dev", path = "../peerset" } sc-utils = { version = "4.0.0-dev", path = "../utils" } diff --git a/client/offchain/src/api.rs b/client/offchain/src/api.rs index 6fdd91cda..a15f03bab 100644 --- a/client/offchain/src/api.rs +++ b/client/offchain/src/api.rs @@ -326,10 +326,8 @@ mod tests { use super::*; use libp2p::PeerId; use sc_client_db::offchain::LocalStorage; - use sc_network_common::{ - config::MultiaddrWithPeerId, - protocol::ProtocolName, - service::{NetworkPeers, NetworkStateInfo}, + use sc_network::{ + config::MultiaddrWithPeerId, types::ProtocolName, NetworkPeers, NetworkStateInfo, }; use sc_peerset::ReputationChange; use sp_core::offchain::{DbExternalities, Externalities}; diff --git a/client/offchain/src/lib.rs b/client/offchain/src/lib.rs index c69f01de1..677d89267 100644 --- a/client/offchain/src/lib.rs +++ b/client/offchain/src/lib.rs @@ -42,7 +42,7 @@ use futures::{ prelude::*, }; use parking_lot::Mutex; -use sc_network_common::service::{NetworkPeers, NetworkStateInfo}; +use sc_network::{NetworkPeers, NetworkStateInfo}; use sp_api::{ApiExt, ProvideRuntimeApi}; use sp_core::{offchain, traits::SpawnNamed, ExecutionContext}; use sp_runtime::traits::{self, Header}; @@ -246,7 +246,7 @@ mod tests { use libp2p::{Multiaddr, PeerId}; use sc_block_builder::BlockBuilderProvider as _; use sc_client_api::Backend as _; - use sc_network_common::{config::MultiaddrWithPeerId, protocol::ProtocolName}; + use sc_network::{config::MultiaddrWithPeerId, types::ProtocolName}; use sc_peerset::ReputationChange; use sc_transaction_pool::{BasicPool, FullChainApi}; use sc_transaction_pool_api::{InPoolTransaction, TransactionPool}; diff --git a/client/rpc/src/system/tests.rs b/client/rpc/src/system/tests.rs index c0ee1e8f8..b6bfec773 100644 --- a/client/rpc/src/system/tests.rs +++ b/client/rpc/src/system/tests.rs @@ -99,7 +99,7 @@ fn api>>(sync: T) -> RpcModule> { ); }, Request::NetworkAddReservedPeer(peer, sender) => { - let _ = match sc_network_common::config::parse_str_addr(&peer) { + let _ = match sc_network::config::parse_str_addr(&peer) { Ok(_) => sender.send(Ok(())), Err(s) => sender.send(Err(error::Error::MalformattedPeerArg(s.to_string()))), diff --git a/client/service/src/builder.rs b/client/service/src/builder.rs index ea4b63000..91ef65cf1 100644 --- a/client/service/src/builder.rs +++ b/client/service/src/builder.rs @@ -38,14 +38,11 @@ use sc_client_db::{Backend, DatabaseSettings}; use sc_consensus::import_queue::ImportQueue; use sc_executor::RuntimeVersionOf; use sc_keystore::LocalKeystore; -use sc_network::NetworkService; -use sc_network_bitswap::BitswapRequestHandler; -use sc_network_common::{ - config::SyncMode, - protocol::role::Roles, - service::{NetworkEventStream, NetworkStateInfo, NetworkStatusProvider}, - sync::warp::WarpSyncParams, +use sc_network::{ + config::SyncMode, NetworkEventStream, NetworkService, NetworkStateInfo, NetworkStatusProvider, }; +use sc_network_bitswap::BitswapRequestHandler; +use sc_network_common::{role::Roles, sync::warp::WarpSyncParams}; use sc_network_light::light_client_requests::handler::LightClientRequestHandler; use sc_network_sync::{ block_request_handler::BlockRequestHandler, engine::SyncingEngine, diff --git a/client/service/src/config.rs b/client/service/src/config.rs index 8e843b58f..c7d98a453 100644 --- a/client/service/src/config.rs +++ b/client/service/src/config.rs @@ -22,22 +22,18 @@ pub use sc_client_api::execution_extensions::{ExecutionStrategies, ExecutionStra pub use sc_client_db::{BlocksPruning, Database, DatabaseSource, PruningMode}; pub use sc_executor::{WasmExecutionMethod, WasmtimeInstantiationStrategy}; pub use sc_network::{ - config::{NetworkConfiguration, Role}, - Multiaddr, -}; -pub use sc_network_common::{ config::{ - MultiaddrWithPeerId, NodeKeyConfig, NonDefaultSetConfig, ProtocolId, SetConfig, - TransportConfig, + MultiaddrWithPeerId, NetworkConfiguration, NodeKeyConfig, NonDefaultSetConfig, ProtocolId, + Role, SetConfig, SyncMode, TransportConfig, }, request_responses::{ IncomingRequest, OutgoingResponse, ProtocolConfig as RequestResponseConfig, }, + Multiaddr, }; use prometheus_endpoint::Registry; use sc_chain_spec::ChainSpec; -use sc_network_common::config::SyncMode; pub use sc_telemetry::TelemetryEndpoints; pub use sc_transaction_pool::Options as TransactionPoolOptions; use sp_core::crypto::SecretString; diff --git a/client/service/src/error.rs b/client/service/src/error.rs index 6b71e46b4..c871342c7 100644 --- a/client/service/src/error.rs +++ b/client/service/src/error.rs @@ -40,7 +40,7 @@ pub enum Error { Consensus(#[from] sp_consensus::Error), #[error(transparent)] - Network(#[from] sc_network_common::error::Error), + Network(#[from] sc_network::error::Error), #[error(transparent)] Keystore(#[from] sc_keystore::Error), diff --git a/client/service/src/lib.rs b/client/service/src/lib.rs index 9dab81a5b..54f11ec25 100644 --- a/client/service/src/lib.rs +++ b/client/service/src/lib.rs @@ -41,10 +41,8 @@ use futures::{channel::mpsc, pin_mut, FutureExt, StreamExt}; use jsonrpsee::{core::Error as JsonRpseeError, RpcModule}; use log::{debug, error, warn}; use sc_client_api::{blockchain::HeaderBackend, BlockBackend, BlockchainEvents, ProofProvider}; -use sc_network::{NetworkStateInfo, PeerId}; -use sc_network_common::{ - config::MultiaddrWithPeerId, - service::{NetworkBlock, NetworkPeers}, +use sc_network::{ + config::MultiaddrWithPeerId, NetworkBlock, NetworkPeers, NetworkStateInfo, PeerId, }; use sc_network_sync::SyncingService; use sc_utils::mpsc::TracingUnboundedReceiver; diff --git a/client/service/src/metrics.rs b/client/service/src/metrics.rs index 967e3133d..ece5758be 100644 --- a/client/service/src/metrics.rs +++ b/client/service/src/metrics.rs @@ -22,11 +22,8 @@ use crate::config::Configuration; use futures_timer::Delay; use prometheus_endpoint::{register, Gauge, GaugeVec, Opts, PrometheusError, Registry, U64}; use sc_client_api::{ClientInfo, UsageProvider}; -use sc_network::config::Role; -use sc_network_common::{ - service::{NetworkStatus, NetworkStatusProvider}, - sync::{SyncStatus, SyncStatusProvider}, -}; +use sc_network::{config::Role, NetworkStatus, NetworkStatusProvider}; +use sc_network_common::sync::{SyncStatus, SyncStatusProvider}; use sc_telemetry::{telemetry, TelemetryHandle, SUBSTRATE_INFO}; use sc_transaction_pool_api::{MaintainedTransactionPool, PoolStatus}; use sc_utils::metrics::register_globals; diff --git a/client/service/test/src/lib.rs b/client/service/test/src/lib.rs index b1a09a062..f80446a4d 100644 --- a/client/service/test/src/lib.rs +++ b/client/service/test/src/lib.rs @@ -22,10 +22,9 @@ use futures::{task::Poll, Future, TryFutureExt as _}; use log::{debug, info}; use parking_lot::Mutex; use sc_client_api::{Backend, CallExecutor}; -use sc_network::{config::NetworkConfiguration, multiaddr}; -use sc_network_common::{ - config::{MultiaddrWithPeerId, TransportConfig}, - service::{NetworkBlock, NetworkPeers, NetworkStateInfo}, +use sc_network::{ + config::{MultiaddrWithPeerId, NetworkConfiguration, TransportConfig}, + multiaddr, NetworkBlock, NetworkPeers, NetworkStateInfo, }; use sc_network_sync::SyncingService; use sc_service::{ From 39510ecaa82f07f8671ec4d6d05ef572974ec9fe Mon Sep 17 00:00:00 2001 From: Adrian Catangiu Date: Tue, 14 Mar 2023 16:52:53 +0200 Subject: [PATCH 230/558] sc-consensus-beefy: fix on-demand async state machine (#13599) `futures_util::pending!()` macro only yields to the event loop once, resulting in many calls to the `OnDemandJustificationsEngine::next()` made by the tokio reactor even though the on-demand-engine is idle. Replace it with the correct `futures::future::pending::<()>()` function which forever yields to the event loop, which is what we want when the engine is idle. Signed-off-by: Adrian Catangiu --- .../outgoing_requests_engine.rs | 2 +- client/consensus/beefy/src/tests.rs | 23 ++++++++++++------- client/consensus/beefy/src/worker.rs | 10 ++++---- 3 files changed, 21 insertions(+), 14 deletions(-) diff --git a/client/consensus/beefy/src/communication/request_response/outgoing_requests_engine.rs b/client/consensus/beefy/src/communication/request_response/outgoing_requests_engine.rs index c642e3dfe..fbf464bd6 100644 --- a/client/consensus/beefy/src/communication/request_response/outgoing_requests_engine.rs +++ b/client/consensus/beefy/src/communication/request_response/outgoing_requests_engine.rs @@ -207,7 +207,7 @@ impl OnDemandJustificationsEngine { pub async fn next(&mut self) -> Option> { let (peer, req_info, resp) = match &mut self.state { State::Idle => { - futures::pending!(); + futures::future::pending::<()>().await; return None }, State::AwaitingResponse(peer, req_info, receiver) => { diff --git a/client/consensus/beefy/src/tests.rs b/client/consensus/beefy/src/tests.rs index 44920aaa6..27dc8d819 100644 --- a/client/consensus/beefy/src/tests.rs +++ b/client/consensus/beefy/src/tests.rs @@ -934,7 +934,7 @@ async fn on_demand_beefy_justification_sync() { let dave_index = 3; // push 30 blocks - let hashes = net.generate_blocks_and_sync(35, session_len, &validator_set, false).await; + let mut hashes = net.generate_blocks_and_sync(30, session_len, &validator_set, false).await; let fast_peers = fast_peers.into_iter().enumerate(); let net = Arc::new(Mutex::new(net)); @@ -951,8 +951,16 @@ async fn on_demand_beefy_justification_sync() { // Spawn Dave, they are now way behind voting and can only catch up through on-demand justif // sync. tokio::spawn(dave_task); - // give Dave a chance to spawn and init. - run_for(Duration::from_millis(400), &net).await; + // Dave pushes and syncs 4 more blocks just to make sure he gets included in gossip. + { + let mut net_guard = net.lock(); + let built_hashes = + net_guard + .peer(dave_index) + .generate_blocks(4, BlockOrigin::File, |builder| builder.build().unwrap().block); + hashes.extend(built_hashes); + net_guard.run_until_sync().await; + } let (dave_best_blocks, _) = get_beefy_streams(&mut net.lock(), [(dave_index, BeefyKeyring::Dave)].into_iter()); @@ -965,7 +973,10 @@ async fn on_demand_beefy_justification_sync() { // Have the other peers do some gossip so Dave finds out about their progress. finalize_block_and_wait_for_beefy(&net, fast_peers, &[hashes[25], hashes[29]], &[25, 29]).await; - // Now verify Dave successfully finalized #1 (through on-demand justification request). + // Kick Dave's async loop by finalizing another block. + client.finalize_block(hashes[2], None).unwrap(); + + // And verify Dave successfully finalized #1 (through on-demand justification request). wait_for_best_beefy_blocks(dave_best_blocks, &net, &[1]).await; // Give all tasks some cpu cycles to burn through their events queues, @@ -978,10 +989,6 @@ async fn on_demand_beefy_justification_sync() { &[5, 10, 15, 20, 25], ) .await; - - let all_peers = all_peers.into_iter().enumerate(); - // Now that Dave has caught up, sanity check voting works for all of them. - finalize_block_and_wait_for_beefy(&net, all_peers, &[hashes[30], hashes[34]], &[30]).await; } #[tokio::test] diff --git a/client/consensus/beefy/src/worker.rs b/client/consensus/beefy/src/worker.rs index 3f29dc7ab..0abb38d02 100644 --- a/client/consensus/beefy/src/worker.rs +++ b/client/consensus/beefy/src/worker.rs @@ -887,11 +887,6 @@ where // based on the new resulting 'state'. futures::select_biased! { // Use `select_biased!` to prioritize order below. - // Make sure to pump gossip engine. - _ = gossip_engine => { - error!(target: LOG_TARGET, "🥩 Gossip engine has terminated, closing worker."); - return; - }, // Process finality notifications first since these drive the voter. notification = finality_notifications.next() => { if let Some(notification) = notification { @@ -901,6 +896,11 @@ where return; } }, + // Make sure to pump gossip engine. + _ = gossip_engine => { + error!(target: LOG_TARGET, "🥩 Gossip engine has terminated, closing worker."); + return; + }, // Process incoming justifications as these can make some in-flight votes obsolete. justif = self.on_demand_justifications.next().fuse() => { if let Some(justif) = justif { From 03ea99e4b57d17d3029da87a3b196eb9d5178f5d Mon Sep 17 00:00:00 2001 From: cheme Date: Tue, 14 Mar 2023 22:03:50 +0100 Subject: [PATCH 231/558] update trie deps (#13601) --- Cargo.lock | 29 +++++++++---------- bin/node/bench/Cargo.toml | 2 +- client/db/Cargo.toml | 2 +- primitives/api/Cargo.toml | 2 +- primitives/core/Cargo.toml | 2 +- primitives/state-machine/Cargo.toml | 4 +-- primitives/trie/Cargo.toml | 12 ++++---- test-utils/runtime/Cargo.toml | 4 +-- utils/binary-merkle-tree/Cargo.toml | 2 +- .../rpc/state-trie-migration-rpc/Cargo.toml | 2 +- 10 files changed, 30 insertions(+), 31 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 4aac7f3c7..0d048a99d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2856,9 +2856,9 @@ dependencies = [ [[package]] name = "hash-db" -version = "0.15.2" +version = "0.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d23bd4e7b5eda0d0f3a307e8b381fdc8ba9000f26fbe912250c0a4cc3956364a" +checksum = "8e7d7786361d7425ae2fe4f9e407eb0efaa0840f5212d109cc018c40c35c6ab4" [[package]] name = "hash256-std-hasher" @@ -3484,9 +3484,9 @@ dependencies = [ [[package]] name = "keccak-hasher" -version = "0.15.3" +version = "0.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "711adba9940a039f4374fc5724c0a5eaca84a2d558cce62256bfe26f0dbef05e" +checksum = "19ea4653859ca2266a86419d3f592d3f22e7a854b482f99180d2498507902048" dependencies = [ "hash-db", "hash256-std-hasher", @@ -4454,12 +4454,11 @@ dependencies = [ [[package]] name = "memory-db" -version = "0.31.0" +version = "0.32.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5e0c7cba9ce19ac7ffd2053ac9f49843bbd3f4318feedfd74e85c19d5fb0ba66" +checksum = "808b50db46293432a45e63bc15ea51e0ab4c0a1647b8eb114e31a3e698dd6fbe" dependencies = [ "hash-db", - "hashbrown 0.12.3", ] [[package]] @@ -11618,9 +11617,9 @@ dependencies = [ [[package]] name = "trie-bench" -version = "0.36.0" +version = "0.37.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ac2b7695feb8041efc0adaa09ed3a692ca7b7c997395954fdc838b5b078346f6" +checksum = "4f54b4f9d51d368e62cf7e0730c7c1e18fc658cc84333656bab5b328f44aa964" dependencies = [ "criterion", "hash-db", @@ -11634,9 +11633,9 @@ dependencies = [ [[package]] name = "trie-db" -version = "0.26.0" +version = "0.27.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "879380c0061b165ba1f036325b7f3478bc1a947814d9fc36d22c5d8e960b11bd" +checksum = "634d75c77ea43f2ad8ea9d9c58de49dfc9c3995bdef32b503df7883ff054e7f1" dependencies = [ "hash-db", "hashbrown 0.13.2", @@ -11647,18 +11646,18 @@ dependencies = [ [[package]] name = "trie-root" -version = "0.17.0" +version = "0.18.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9a36c5ca3911ed3c9a5416ee6c679042064b93fc637ded67e25f92e68d783891" +checksum = "d4ed310ef5ab98f5fa467900ed906cb9232dd5376597e00fd4cba2a449d06c0b" dependencies = [ "hash-db", ] [[package]] name = "trie-standardmap" -version = "0.15.2" +version = "0.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c3161ba520ab28cd8e6b68e1126f1009f6e335339d1a73b978139011703264c8" +checksum = "684aafb332fae6f83d7fe10b3fbfdbe39a1b3234c4e2a618f030815838519516" dependencies = [ "hash-db", "keccak-hasher", diff --git a/bin/node/bench/Cargo.toml b/bin/node/bench/Cargo.toml index dfff4db43..be5e94ee4 100644 --- a/bin/node/bench/Cargo.toml +++ b/bin/node/bench/Cargo.toml @@ -33,7 +33,7 @@ sc-basic-authorship = { version = "0.10.0-dev", path = "../../../client/basic-au sp-inherents = { version = "4.0.0-dev", path = "../../../primitives/inherents" } sp-timestamp = { version = "4.0.0-dev", default-features = false, path = "../../../primitives/timestamp" } sp-tracing = { version = "6.0.0", path = "../../../primitives/tracing" } -hash-db = "0.15.2" +hash-db = "0.16.0" tempfile = "3.1.0" fs_extra = "1" rand = { version = "0.8.5", features = ["small_rng"] } diff --git a/client/db/Cargo.toml b/client/db/Cargo.toml index ed26f3737..c96e5c740 100644 --- a/client/db/Cargo.toml +++ b/client/db/Cargo.toml @@ -16,7 +16,7 @@ targets = ["x86_64-unknown-linux-gnu"] codec = { package = "parity-scale-codec", version = "3.2.2", features = [ "derive", ] } -hash-db = "0.15.2" +hash-db = "0.16.0" kvdb = "0.13.0" kvdb-memorydb = "0.13.0" kvdb-rocksdb = { version = "0.17.0", optional = true } diff --git a/primitives/api/Cargo.toml b/primitives/api/Cargo.toml index 75197bcae..ae1b3294c 100644 --- a/primitives/api/Cargo.toml +++ b/primitives/api/Cargo.toml @@ -21,7 +21,7 @@ sp-runtime = { version = "7.0.0", default-features = false, path = "../runtime" sp-version = { version = "5.0.0", default-features = false, path = "../version" } sp-state-machine = { version = "0.13.0", default-features = false, optional = true, path = "../state-machine" } sp-trie = { version = "7.0.0", default-features = false, optional = true, path = "../trie" } -hash-db = { version = "0.15.2", optional = true } +hash-db = { version = "0.16.0", optional = true } thiserror = { version = "1.0.30", optional = true } log = { version = "0.4.17", default-features = false } diff --git a/primitives/core/Cargo.toml b/primitives/core/Cargo.toml index 7c0453775..092baeeda 100644 --- a/primitives/core/Cargo.toml +++ b/primitives/core/Cargo.toml @@ -23,7 +23,7 @@ serde = { version = "1.0.136", optional = true, features = ["derive"] } bounded-collections = { version = "0.1.4", default-features = false } primitive-types = { version = "0.12.0", default-features = false, features = ["codec", "scale-info"] } impl-serde = { version = "0.4.0", optional = true } -hash-db = { version = "0.15.2", default-features = false } +hash-db = { version = "0.16.0", default-features = false } hash256-std-hasher = { version = "0.15.2", default-features = false } base58 = { version = "0.2.0", optional = true } rand = { version = "0.8.5", features = ["small_rng"], optional = true } diff --git a/primitives/state-machine/Cargo.toml b/primitives/state-machine/Cargo.toml index 56fbe0726..9759547d7 100644 --- a/primitives/state-machine/Cargo.toml +++ b/primitives/state-machine/Cargo.toml @@ -15,7 +15,7 @@ targets = ["x86_64-unknown-linux-gnu"] [dependencies] codec = { package = "parity-scale-codec", version = "3.2.2", default-features = false } -hash-db = { version = "0.15.2", default-features = false } +hash-db = { version = "0.16.0", default-features = false } log = { version = "0.4.17", default-features = false } parking_lot = { version = "0.12.1", optional = true } rand = { version = "0.8.5", optional = true } @@ -33,7 +33,7 @@ array-bytes = "4.1" pretty_assertions = "1.2.1" rand = "0.8.5" sp-runtime = { version = "7.0.0", path = "../runtime" } -trie-db = "0.26.0" +trie-db = "0.27.0" assert_matches = "1.5" [features] diff --git a/primitives/trie/Cargo.toml b/primitives/trie/Cargo.toml index fae39ec34..21582296b 100644 --- a/primitives/trie/Cargo.toml +++ b/primitives/trie/Cargo.toml @@ -21,16 +21,16 @@ harness = false ahash = { version = "0.8.2", optional = true } codec = { package = "parity-scale-codec", version = "3.2.2", default-features = false } hashbrown = { version = "0.12.3", optional = true } -hash-db = { version = "0.15.2", default-features = false } +hash-db = { version = "0.16.0", default-features = false } lazy_static = { version = "1.4.0", optional = true } -memory-db = { version = "0.31.0", default-features = false } +memory-db = { version = "0.32.0", default-features = false } nohash-hasher = { version = "0.2.0", optional = true } parking_lot = { version = "0.12.1", optional = true } scale-info = { version = "2.1.1", default-features = false, features = ["derive"] } thiserror = { version = "1.0.30", optional = true } tracing = { version = "0.1.29", optional = true } -trie-db = { version = "0.26.0", default-features = false } -trie-root = { version = "0.17.0", default-features = false } +trie-db = { version = "0.27.0", default-features = false } +trie-root = { version = "0.18.0", default-features = false } sp-core = { version = "7.0.0", default-features = false, path = "../core" } sp-std = { version = "5.0.0", default-features = false, path = "../std" } schnellru = { version = "0.2.1", optional = true } @@ -38,8 +38,8 @@ schnellru = { version = "0.2.1", optional = true } [dev-dependencies] array-bytes = "4.1" criterion = "0.4.0" -trie-bench = "0.36.0" -trie-standardmap = "0.15.2" +trie-bench = "0.37.0" +trie-standardmap = "0.16.0" sp-runtime = { version = "7.0.0", path = "../runtime" } [features] diff --git a/test-utils/runtime/Cargo.toml b/test-utils/runtime/Cargo.toml index e0f414b16..1dcdec0db 100644 --- a/test-utils/runtime/Cargo.toml +++ b/test-utils/runtime/Cargo.toml @@ -23,7 +23,7 @@ codec = { package = "parity-scale-codec", version = "3.2.2", default-features = scale-info = { version = "2.1.1", default-features = false, features = ["derive"] } sp-inherents = { version = "4.0.0-dev", default-features = false, path = "../../primitives/inherents" } sp-keyring = { version = "7.0.0", optional = true, path = "../../primitives/keyring" } -memory-db = { version = "0.31.0", default-features = false } +memory-db = { version = "0.32.0", default-features = false } sp-offchain = { version = "4.0.0-dev", default-features = false, path = "../../primitives/offchain" } sp-core = { version = "7.0.0", default-features = false, path = "../../primitives/core" } sp-std = { version = "5.0.0", default-features = false, path = "../../primitives/std" } @@ -41,7 +41,7 @@ pallet-timestamp = { version = "4.0.0-dev", default-features = false, path = ".. sp-consensus-grandpa = { version = "4.0.0-dev", default-features = false, path = "../../primitives/consensus/grandpa" } sp-trie = { version = "7.0.0", default-features = false, path = "../../primitives/trie" } sp-transaction-pool = { version = "4.0.0-dev", default-features = false, path = "../../primitives/transaction-pool" } -trie-db = { version = "0.26.0", default-features = false } +trie-db = { version = "0.27.0", default-features = false } sc-service = { version = "0.10.0-dev", default-features = false, optional = true, features = ["test-helpers"], path = "../../client/service" } sp-state-machine = { version = "0.13.0", default-features = false, path = "../../primitives/state-machine" } sp-externalities = { version = "0.13.0", default-features = false, path = "../../primitives/externalities" } diff --git a/utils/binary-merkle-tree/Cargo.toml b/utils/binary-merkle-tree/Cargo.toml index a59d27fb0..a54dc4f2d 100644 --- a/utils/binary-merkle-tree/Cargo.toml +++ b/utils/binary-merkle-tree/Cargo.toml @@ -11,7 +11,7 @@ homepage = "https://substrate.io" [dependencies] array-bytes = { version = "4.1", optional = true } log = { version = "0.4", default-features = false, optional = true } -hash-db = { version = "0.15.2", default-features = false } +hash-db = { version = "0.16.0", default-features = false } [dev-dependencies] array-bytes = "4.1" diff --git a/utils/frame/rpc/state-trie-migration-rpc/Cargo.toml b/utils/frame/rpc/state-trie-migration-rpc/Cargo.toml index e6eccc6ff..3689a87da 100644 --- a/utils/frame/rpc/state-trie-migration-rpc/Cargo.toml +++ b/utils/frame/rpc/state-trie-migration-rpc/Cargo.toml @@ -21,7 +21,7 @@ log = { version = "0.4.17", default-features = false } sp-core = { path = "../../../../primitives/core" } sp-state-machine = { path = "../../../../primitives/state-machine" } sp-trie = { path = "../../../../primitives/trie" } -trie-db = "0.26.0" +trie-db = "0.27.0" jsonrpsee = { version = "0.16.2", features = ["client-core", "server", "macros"] } From 9eafc96a62d160cd97f8150b2742d7048e394bba Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bastian=20K=C3=B6cher?= Date: Tue, 14 Mar 2023 22:14:58 +0100 Subject: [PATCH 232/558] sp-api: Support expanding the macro code (#13573) * sp-api: Support expanding the macro code This pr introduces the `expander` crate to expand the generated source code into a file. This gives better error reporting when trying to fix issues in the macro itself as Rustc will point to the line in this file. The feature can be enabled by setting `SP_API_EXPAND=1` at compile time. Besides that the generated code is changed to fix warnings in the exanped version. * Fixes --- Cargo.lock | 21 +++++ primitives/api/proc-macro/Cargo.toml | 4 +- primitives/api/proc-macro/src/common.rs | 3 - .../api/proc-macro/src/decl_runtime_apis.rs | 35 ++++--- .../api/proc-macro/src/impl_runtime_apis.rs | 93 ++++++++++++------- .../proc-macro/src/mock_impl_runtime_apis.rs | 16 +--- primitives/api/proc-macro/src/utils.rs | 39 +++----- primitives/api/test/tests/decl_and_impl.rs | 8 +- .../test/tests/ui/impl_missing_version.stderr | 6 +- 9 files changed, 124 insertions(+), 101 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 0d048a99d..d0aea7729 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1997,6 +1997,19 @@ dependencies = [ "futures", ] +[[package]] +name = "expander" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f360349150728553f92e4c997a16af8915f418d3a0f21b440d34c5632f16ed84" +dependencies = [ + "blake2", + "fs-err", + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "fake-simd" version = "0.1.2" @@ -2507,6 +2520,12 @@ dependencies = [ "sp-std", ] +[[package]] +name = "fs-err" +version = "2.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0845fa252299212f0389d64ba26f34fa32cfe41588355f21ed507c59a0f64541" + [[package]] name = "fs2" version = "0.4.3" @@ -9925,7 +9944,9 @@ dependencies = [ name = "sp-api-proc-macro" version = "4.0.0-dev" dependencies = [ + "Inflector", "blake2", + "expander", "proc-macro-crate", "proc-macro2", "quote", diff --git a/primitives/api/proc-macro/Cargo.toml b/primitives/api/proc-macro/Cargo.toml index 8acc15d6a..ba7c63120 100644 --- a/primitives/api/proc-macro/Cargo.toml +++ b/primitives/api/proc-macro/Cargo.toml @@ -21,8 +21,10 @@ syn = { version = "1.0.98", features = ["full", "fold", "extra-traits", "visit"] proc-macro2 = "1.0.37" blake2 = { version = "0.10.4", default-features = false } proc-macro-crate = "1.1.3" +expander = "1.0.0" +Inflector = "0.11.4" # Required for the doc tests [features] -default = [ "std" ] +default = ["std"] std = [] diff --git a/primitives/api/proc-macro/src/common.rs b/primitives/api/proc-macro/src/common.rs index d29057723..725ad166f 100644 --- a/primitives/api/proc-macro/src/common.rs +++ b/primitives/api/proc-macro/src/common.rs @@ -18,9 +18,6 @@ /// The ident used for the block generic parameter. pub const BLOCK_GENERIC_IDENT: &str = "Block"; -/// Unique identifier used to make the hidden includes unique for this macro. -pub const HIDDEN_INCLUDES_ID: &str = "DECL_RUNTIME_APIS"; - /// The `core_trait` attribute. pub const CORE_TRAIT_ATTRIBUTE: &str = "core_trait"; /// The `api_version` attribute. diff --git a/primitives/api/proc-macro/src/decl_runtime_apis.rs b/primitives/api/proc-macro/src/decl_runtime_apis.rs index d9a72adad..3c3056d34 100644 --- a/primitives/api/proc-macro/src/decl_runtime_apis.rs +++ b/primitives/api/proc-macro/src/decl_runtime_apis.rs @@ -17,14 +17,14 @@ use crate::utils::{ extract_parameter_names_types_and_borrows, fold_fn_decl_for_client_side, generate_crate_access, - generate_hidden_includes, generate_runtime_mod_name_for_trait, parse_runtime_api_version, - prefix_function_with_trait, replace_wild_card_parameter_names, return_type_extract_type, - versioned_trait_name, AllowSelfRefInParameters, + generate_runtime_mod_name_for_trait, parse_runtime_api_version, prefix_function_with_trait, + replace_wild_card_parameter_names, return_type_extract_type, versioned_trait_name, + AllowSelfRefInParameters, }; use crate::common::{ API_VERSION_ATTRIBUTE, BLOCK_GENERIC_IDENT, CHANGED_IN_ATTRIBUTE, CORE_TRAIT_ATTRIBUTE, - HIDDEN_INCLUDES_ID, RENAMED_ATTRIBUTE, SUPPORTED_ATTRIBUTE_NAMES, + RENAMED_ATTRIBUTE, SUPPORTED_ATTRIBUTE_NAMES, }; use proc_macro2::{Span, TokenStream}; @@ -62,7 +62,7 @@ impl Parse for RuntimeApiDecls { /// Extend the given generics with `Block: BlockT` as first generic parameter. fn extend_generics_with_block(generics: &mut Generics) { - let c = generate_crate_access(HIDDEN_INCLUDES_ID); + let c = generate_crate_access(); generics.lt_token = Some(Default::default()); generics.params.insert(0, parse_quote!( Block: #c::BlockT )); @@ -298,7 +298,7 @@ fn generate_runtime_decls(decls: &[ItemTrait]) -> Result { #[allow(dead_code)] #[allow(deprecated)] pub mod #mod_name { - use super::*; + pub use super::*; #( #versioned_api_traits )* @@ -495,10 +495,10 @@ impl<'a> ToClientSideDecl<'a> { __runtime_api_at_param__, #context, __runtime_api_impl_params_encoded__, - &|version| { + &|_version| { #( // Check if we need to call the function by an old name. - if version.apis.iter().any(|(s, v)| { + if _version.apis.iter().any(|(s, v)| { s == &#runtime_mod::ID && *v < #versions }) { return #old_names @@ -569,7 +569,7 @@ fn generate_runtime_api_version(version: u32) -> TokenStream { /// Generates the implementation of `RuntimeApiInfo` for the given trait. fn generate_runtime_info_impl(trait_: &ItemTrait, version: u64) -> TokenStream { let trait_name = &trait_.ident; - let crate_ = generate_crate_access(HIDDEN_INCLUDES_ID); + let crate_ = generate_crate_access(); let id = generate_runtime_api_id(&trait_name.to_string()); let version = generate_runtime_api_version(version as u32); @@ -620,7 +620,7 @@ fn generate_client_side_decls(decls: &[ItemTrait]) -> Result { for decl in decls { let decl = decl.clone(); - let crate_ = generate_crate_access(HIDDEN_INCLUDES_ID); + let crate_ = generate_crate_access(); let block_hash = quote!( ::Hash ); let mut found_attributes = HashMap::new(); let mut errors = Vec::new(); @@ -777,15 +777,20 @@ pub fn decl_runtime_apis_impl(input: proc_macro::TokenStream) -> proc_macro::Tok fn decl_runtime_apis_impl_inner(api_decls: &[ItemTrait]) -> Result { check_trait_decls(api_decls)?; - let hidden_includes = generate_hidden_includes(HIDDEN_INCLUDES_ID); let runtime_decls = generate_runtime_decls(api_decls)?; let client_side_decls = generate_client_side_decls(api_decls)?; - Ok(quote!( - #hidden_includes - + let decl = quote! { #runtime_decls #client_side_decls - )) + }; + + let decl = expander::Expander::new("decl_runtime_apis") + .dry(std::env::var("SP_API_EXPAND").is_err()) + .verbose(true) + .write_to_out_dir(decl) + .expect("Does not fail because of IO in OUT_DIR; qed"); + + Ok(decl) } diff --git a/primitives/api/proc-macro/src/impl_runtime_apis.rs b/primitives/api/proc-macro/src/impl_runtime_apis.rs index f32961f89..5ac07975d 100644 --- a/primitives/api/proc-macro/src/impl_runtime_apis.rs +++ b/primitives/api/proc-macro/src/impl_runtime_apis.rs @@ -17,7 +17,7 @@ use crate::utils::{ extract_all_signature_types, extract_block_type_from_trait_path, extract_impl_trait, - extract_parameter_names_types_and_borrows, generate_crate_access, generate_hidden_includes, + extract_parameter_names_types_and_borrows, generate_crate_access, generate_runtime_mod_name_for_trait, parse_runtime_api_version, prefix_function_with_trait, versioned_trait_name, AllowSelfRefInParameters, RequireQualifiedTraitPath, }; @@ -38,9 +38,6 @@ use syn::{ use std::collections::HashSet; -/// Unique identifier used to make the hidden includes unique for this macro. -const HIDDEN_INCLUDES_ID: &str = "IMPL_RUNTIME_APIS"; - /// The structure used for parsing the runtime api implementations. struct RuntimeApiImpls { impls: Vec, @@ -73,7 +70,7 @@ fn generate_impl_call( let params = extract_parameter_names_types_and_borrows(signature, AllowSelfRefInParameters::No)?; - let c = generate_crate_access(HIDDEN_INCLUDES_ID); + let c = generate_crate_access(); let fn_name = &signature.ident; let fn_name_str = fn_name.to_string(); let pnames = params.iter().map(|v| &v.0); @@ -81,15 +78,33 @@ fn generate_impl_call( let ptypes = params.iter().map(|v| &v.1); let pborrow = params.iter().map(|v| &v.2); + let decode_params = if params.is_empty() { + quote!() + } else { + let let_binding = if params.len() == 1 { + quote! { + let #( #pnames )* : #( #ptypes )* + } + } else { + quote! { + let ( #( #pnames ),* ) : ( #( #ptypes ),* ) + } + }; + + quote!( + #let_binding = + match #c::DecodeLimit::decode_all_with_depth_limit( + #c::MAX_EXTRINSIC_DEPTH, + &mut #input, + ) { + Ok(res) => res, + Err(e) => panic!("Bad input data provided to {}: {}", #fn_name_str, e), + }; + ) + }; + Ok(quote!( - let (#( #pnames ),*) : ( #( #ptypes ),* ) = - match #c::DecodeLimit::decode_all_with_depth_limit( - #c::MAX_EXTRINSIC_DEPTH, - &mut #input, - ) { - Ok(res) => res, - Err(e) => panic!("Bad input data provided to {}: {}", #fn_name_str, e), - }; + #decode_params #[allow(deprecated)] <#runtime as #impl_trait>::#fn_name(#( #pborrow #pnames2 ),*) @@ -134,8 +149,8 @@ fn generate_impl_calls( /// Generate the dispatch function that is used in native to call into the runtime. fn generate_dispatch_function(impls: &[ItemImpl]) -> Result { - let data = Ident::new("__sp_api__input_data", Span::call_site()); - let c = generate_crate_access(HIDDEN_INCLUDES_ID); + let data = Ident::new("_sp_api_input_data_", Span::call_site()); + let c = generate_crate_access(); let impl_calls = generate_impl_calls(impls, &data)? .into_iter() @@ -161,7 +176,7 @@ fn generate_dispatch_function(impls: &[ItemImpl]) -> Result { /// Generate the interface functions that are used to call into the runtime in wasm. fn generate_wasm_interface(impls: &[ItemImpl]) -> Result { let input = Ident::new("input", Span::call_site()); - let c = generate_crate_access(HIDDEN_INCLUDES_ID); + let c = generate_crate_access(); let impl_calls = generate_impl_calls(impls, &input)? @@ -195,7 +210,7 @@ fn generate_wasm_interface(impls: &[ItemImpl]) -> Result { } fn generate_runtime_api_base_structures() -> Result { - let crate_ = generate_crate_access(HIDDEN_INCLUDES_ID); + let crate_ = generate_crate_access(); Ok(quote!( pub struct RuntimeApi {} @@ -414,7 +429,7 @@ impl<'a> ApiRuntimeImplToApiRuntimeApiImpl<'a> { fn process(mut self, input: ItemImpl) -> ItemImpl { let mut input = self.fold_item_impl(input); - let crate_ = generate_crate_access(HIDDEN_INCLUDES_ID); + let crate_ = generate_crate_access(); // Delete all functions, because all of them are default implemented by // `decl_runtime_apis!`. We only need to implement the `__runtime_api_internal_call_api_at` @@ -423,7 +438,7 @@ impl<'a> ApiRuntimeImplToApiRuntimeApiImpl<'a> { input.items.push(parse_quote! { fn __runtime_api_internal_call_api_at( &self, - at: <__SR_API_BLOCK__ as #crate_::BlockT>::Hash, + at: <__SrApiBlock__ as #crate_::BlockT>::Hash, context: #crate_::ExecutionContext, params: std::vec::Vec, fn_name: &dyn Fn(#crate_::RuntimeVersion) -> &'static str, @@ -435,7 +450,7 @@ impl<'a> ApiRuntimeImplToApiRuntimeApiImpl<'a> { } let res = (|| { - let version = #crate_::CallApiAt::<__SR_API_BLOCK__>::runtime_version_at( + let version = #crate_::CallApiAt::<__SrApiBlock__>::runtime_version_at( self.call, at, )?; @@ -450,7 +465,7 @@ impl<'a> ApiRuntimeImplToApiRuntimeApiImpl<'a> { recorder: &self.recorder, }; - #crate_::CallApiAt::<__SR_API_BLOCK__>::call_api_at( + #crate_::CallApiAt::<__SrApiBlock__>::call_api_at( self.call, params, ) @@ -469,7 +484,7 @@ impl<'a> ApiRuntimeImplToApiRuntimeApiImpl<'a> { impl<'a> Fold for ApiRuntimeImplToApiRuntimeApiImpl<'a> { fn fold_type_path(&mut self, input: TypePath) -> TypePath { let new_ty_path = - if input == *self.runtime_block { parse_quote!(__SR_API_BLOCK__) } else { input }; + if input == *self.runtime_block { parse_quote!(__SrApiBlock__) } else { input }; fold::fold_type_path(self, new_ty_path) } @@ -480,25 +495,26 @@ impl<'a> Fold for ApiRuntimeImplToApiRuntimeApiImpl<'a> { // Before we directly had the final block type and rust could determine that it is unwind // safe, but now we just have a generic parameter `Block`. - let crate_ = generate_crate_access(HIDDEN_INCLUDES_ID); + let crate_ = generate_crate_access(); // Implement the trait for the `RuntimeApiImpl` input.self_ty = - Box::new(parse_quote!( RuntimeApiImpl<__SR_API_BLOCK__, RuntimeApiImplCall> )); + Box::new(parse_quote!( RuntimeApiImpl<__SrApiBlock__, RuntimeApiImplCall> )); input.generics.params.push(parse_quote!( - __SR_API_BLOCK__: #crate_::BlockT + std::panic::UnwindSafe + + __SrApiBlock__: #crate_::BlockT + std::panic::UnwindSafe + std::panic::RefUnwindSafe )); - input.generics.params.push( - parse_quote!( RuntimeApiImplCall: #crate_::CallApiAt<__SR_API_BLOCK__> + 'static ), - ); + input + .generics + .params + .push(parse_quote!( RuntimeApiImplCall: #crate_::CallApiAt<__SrApiBlock__> + 'static )); let where_clause = input.generics.make_where_clause(); where_clause.predicates.push(parse_quote! { RuntimeApiImplCall::StateBackend: - #crate_::StateBackend<#crate_::HashFor<__SR_API_BLOCK__>> + #crate_::StateBackend<#crate_::HashFor<__SrApiBlock__>> }); where_clause.predicates.push(parse_quote! { &'static RuntimeApiImplCall: Send }); @@ -511,7 +527,7 @@ impl<'a> Fold for ApiRuntimeImplToApiRuntimeApiImpl<'a> { }); where_clause.predicates.push(parse_quote! { - __SR_API_BLOCK__::Header: std::panic::UnwindSafe + std::panic::RefUnwindSafe + __SrApiBlock__::Header: std::panic::UnwindSafe + std::panic::RefUnwindSafe }); input.attrs = filter_cfg_attrs(&input.attrs); @@ -574,7 +590,7 @@ fn generate_runtime_api_versions(impls: &[ItemImpl]) -> Result { let mut sections = Vec::::with_capacity(impls.len()); let mut processed_traits = HashSet::new(); - let c = generate_crate_access(HIDDEN_INCLUDES_ID); + let c = generate_crate_access(); for impl_ in impls { let api_ver = extract_api_version(&impl_.attrs, impl_.span())?.map(|a| a as u32); @@ -629,14 +645,11 @@ fn impl_runtime_apis_impl_inner(api_impls: &[ItemImpl]) -> Result { let dispatch_impl = generate_dispatch_function(api_impls)?; let api_impls_for_runtime = generate_api_impl_for_runtime(api_impls)?; let base_runtime_api = generate_runtime_api_base_structures()?; - let hidden_includes = generate_hidden_includes(HIDDEN_INCLUDES_ID); let runtime_api_versions = generate_runtime_api_versions(api_impls)?; let wasm_interface = generate_wasm_interface(api_impls)?; let api_impls_for_runtime_api = generate_api_impl_for_runtime_api(api_impls)?; - Ok(quote!( - #hidden_includes - + let impl_ = quote!( #base_runtime_api #api_impls_for_runtime @@ -652,7 +665,15 @@ fn impl_runtime_apis_impl_inner(api_impls: &[ItemImpl]) -> Result { #wasm_interface } - )) + ); + + let impl_ = expander::Expander::new("impl_runtime_apis") + .dry(std::env::var("SP_API_EXPAND").is_err()) + .verbose(true) + .write_to_out_dir(impl_) + .expect("Does not fail because of IO in OUT_DIR; qed"); + + Ok(impl_) } // Filters all attributes except the cfg ones. diff --git a/primitives/api/proc-macro/src/mock_impl_runtime_apis.rs b/primitives/api/proc-macro/src/mock_impl_runtime_apis.rs index 4c79787a1..fc0a754e2 100644 --- a/primitives/api/proc-macro/src/mock_impl_runtime_apis.rs +++ b/primitives/api/proc-macro/src/mock_impl_runtime_apis.rs @@ -17,8 +17,8 @@ use crate::utils::{ extract_block_type_from_trait_path, extract_impl_trait, - extract_parameter_names_types_and_borrows, generate_crate_access, generate_hidden_includes, - return_type_extract_type, AllowSelfRefInParameters, RequireQualifiedTraitPath, + extract_parameter_names_types_and_borrows, generate_crate_access, return_type_extract_type, + AllowSelfRefInParameters, RequireQualifiedTraitPath, }; use proc_macro2::{Span, TokenStream}; @@ -33,9 +33,6 @@ use syn::{ Attribute, ItemImpl, Pat, Type, TypePath, }; -/// Unique identifier used to make the hidden includes unique for this macro. -const HIDDEN_INCLUDES_ID: &str = "MOCK_IMPL_RUNTIME_APIS"; - /// The `advanced` attribute. /// /// If this attribute is given to a function, the function gets access to the `Hash` as first @@ -65,7 +62,7 @@ impl Parse for RuntimeApiImpls { /// Implement the `ApiExt` trait and the `Core` runtime api. fn implement_common_api_traits(block_type: TypePath, self_ty: Type) -> Result { - let crate_ = generate_crate_access(HIDDEN_INCLUDES_ID); + let crate_ = generate_crate_access(); Ok(quote!( impl #crate_::ApiExt<#block_type> for #self_ty { @@ -256,7 +253,7 @@ impl<'a> FoldRuntimeApiImpl<'a> { fn process(mut self, impl_item: syn::ItemImpl) -> syn::ItemImpl { let mut impl_item = self.fold_item_impl(impl_item); - let crate_ = generate_crate_access(HIDDEN_INCLUDES_ID); + let crate_ = generate_crate_access(); // We also need to overwrite all the `_with_context` methods. To do this, // we clone all methods and add them again with the new name plus one more argument. @@ -295,7 +292,7 @@ impl<'a> FoldRuntimeApiImpl<'a> { impl<'a> Fold for FoldRuntimeApiImpl<'a> { fn fold_impl_item_method(&mut self, mut input: syn::ImplItemMethod) -> syn::ImplItemMethod { let block = { - let crate_ = generate_crate_access(HIDDEN_INCLUDES_ID); + let crate_ = generate_crate_access(); let is_advanced = has_advanced_attribute(&mut input.attrs); let mut errors = Vec::new(); @@ -469,14 +466,11 @@ pub fn mock_impl_runtime_apis_impl(input: proc_macro::TokenStream) -> proc_macro } fn mock_impl_runtime_apis_impl_inner(api_impls: &[ItemImpl]) -> Result { - let hidden_includes = generate_hidden_includes(HIDDEN_INCLUDES_ID); let GeneratedRuntimeApiImpls { impls, block_type, self_ty } = generate_runtime_api_impls(api_impls)?; let api_traits = implement_common_api_traits(block_type, self_ty)?; Ok(quote!( - #hidden_includes - #impls #api_traits diff --git a/primitives/api/proc-macro/src/utils.rs b/primitives/api/proc-macro/src/utils.rs index dee29b1a5..4444a2624 100644 --- a/primitives/api/proc-macro/src/utils.rs +++ b/primitives/api/proc-macro/src/utils.rs @@ -24,29 +24,19 @@ use syn::{ use quote::{format_ident, quote}; -use std::env; - use proc_macro_crate::{crate_name, FoundCrate}; use crate::common::API_VERSION_ATTRIBUTE; -fn generate_hidden_includes_mod_name(unique_id: &'static str) -> Ident { - Ident::new(&format!("sp_api_hidden_includes_{}", unique_id), Span::call_site()) -} +use inflector::Inflector; -/// Generates the hidden includes that are required to make the macro independent from its scope. -pub fn generate_hidden_includes(unique_id: &'static str) -> TokenStream { - let mod_name = generate_hidden_includes_mod_name(unique_id); +/// Generates the access to the `sc_client` crate. +pub fn generate_crate_access() -> TokenStream { match crate_name("sp-api") { - Ok(FoundCrate::Itself) => quote!(), - Ok(FoundCrate::Name(client_name)) => { - let client_name = Ident::new(&client_name, Span::call_site()); - quote!( - #[doc(hidden)] - mod #mod_name { - pub extern crate #client_name as sp_api; - } - ) + Ok(FoundCrate::Itself) => quote!(sp_api), + Ok(FoundCrate::Name(renamed_name)) => { + let renamed_name = Ident::new(&renamed_name, Span::call_site()); + quote!(#renamed_name) }, Err(e) => { let err = Error::new(Span::call_site(), e).to_compile_error(); @@ -55,19 +45,12 @@ pub fn generate_hidden_includes(unique_id: &'static str) -> TokenStream { } } -/// Generates the access to the `sc_client` crate. -pub fn generate_crate_access(unique_id: &'static str) -> TokenStream { - if env::var("CARGO_PKG_NAME").unwrap() == "sp-api" { - quote!(sp_api) - } else { - let mod_name = generate_hidden_includes_mod_name(unique_id); - quote!( self::#mod_name::sp_api ) - } -} - /// Generates the name of the module that contains the trait declaration for the runtime. pub fn generate_runtime_mod_name_for_trait(trait_: &Ident) -> Ident { - Ident::new(&format!("runtime_decl_for_{}", trait_), Span::call_site()) + Ident::new( + &format!("runtime_decl_for_{}", trait_.to_string().to_snake_case()), + Span::call_site(), + ) } /// Get the type of a `syn::ReturnType`. diff --git a/primitives/api/test/tests/decl_and_impl.rs b/primitives/api/test/tests/decl_and_impl.rs index 78990d546..f07adbfa7 100644 --- a/primitives/api/test/tests/decl_and_impl.rs +++ b/primitives/api/test/tests/decl_and_impl.rs @@ -166,17 +166,17 @@ fn test_client_side_function_signature() { #[test] fn check_runtime_api_info() { - assert_eq!(&>::ID, &runtime_decl_for_Api::ID); - assert_eq!(>::VERSION, runtime_decl_for_Api::VERSION); + assert_eq!(&>::ID, &runtime_decl_for_api::ID); + assert_eq!(>::VERSION, runtime_decl_for_api::VERSION); assert_eq!(>::VERSION, 1); assert_eq!( >::VERSION, - runtime_decl_for_ApiWithCustomVersion::VERSION, + runtime_decl_for_api_with_custom_version::VERSION, ); assert_eq!( &>::ID, - &runtime_decl_for_ApiWithCustomVersion::ID, + &runtime_decl_for_api_with_custom_version::ID, ); assert_eq!(>::VERSION, 2); diff --git a/primitives/api/test/tests/ui/impl_missing_version.stderr b/primitives/api/test/tests/ui/impl_missing_version.stderr index c0abeffe0..b8ecc466c 100644 --- a/primitives/api/test/tests/ui/impl_missing_version.stderr +++ b/primitives/api/test/tests/ui/impl_missing_version.stderr @@ -1,10 +1,10 @@ -error[E0433]: failed to resolve: could not find `ApiV4` in `runtime_decl_for_Api` +error[E0433]: failed to resolve: could not find `ApiV4` in `runtime_decl_for_api` --> tests/ui/impl_missing_version.rs:21:13 | 21 | impl self::Api for Runtime { - | ^^^ could not find `ApiV4` in `runtime_decl_for_Api` + | ^^^ could not find `ApiV4` in `runtime_decl_for_api` -error[E0405]: cannot find trait `ApiV4` in module `self::runtime_decl_for_Api` +error[E0405]: cannot find trait `ApiV4` in module `self::runtime_decl_for_api` --> tests/ui/impl_missing_version.rs:21:13 | 11 | pub trait Api { From e44038af6440a8f941de64412794707c62e4b4e4 Mon Sep 17 00:00:00 2001 From: Niklas Adolfsson Date: Tue, 14 Mar 2023 23:53:03 +0100 Subject: [PATCH 233/558] frame epm: expose `feasibility_check` in MinerConfig (#13555) * frame epm: expose feasibity_check in miner The goal with this commit is to expose the `feasibity_check` such that anyone that implements the `MinerConfig trait` can utilize it * cleanup * fix tests --- bin/node/runtime/src/lib.rs | 1 + .../election-provider-multi-phase/src/lib.rs | 127 +++++------------- .../election-provider-multi-phase/src/mock.rs | 3 + .../src/signed.rs | 2 +- .../src/unsigned.rs | 98 +++++++++++++- 5 files changed, 134 insertions(+), 97 deletions(-) diff --git a/bin/node/runtime/src/lib.rs b/bin/node/runtime/src/lib.rs index 614dbc4d9..d8426d3b3 100644 --- a/bin/node/runtime/src/lib.rs +++ b/bin/node/runtime/src/lib.rs @@ -702,6 +702,7 @@ impl pallet_election_provider_multi_phase::MinerConfig for Runtime { type Solution = NposSolution16; type MaxVotesPerVoter = <::DataProvider as ElectionDataProvider>::MaxVotesPerVoter; + type MaxWinners = MaxActiveValidators; // The unsigned submissions have to respect the weight of the submit_unsigned call, thus their // weight estimate function is wired to this call's weight. diff --git a/frame/election-provider-multi-phase/src/lib.rs b/frame/election-provider-multi-phase/src/lib.rs index 3ba90f411..80bab6807 100644 --- a/frame/election-provider-multi-phase/src/lib.rs +++ b/frame/election-provider-multi-phase/src/lib.rs @@ -247,10 +247,7 @@ use sp_arithmetic::{ traits::{CheckedAdd, Zero}, UpperOf, }; -use sp_npos_elections::{ - assignment_ratio_to_staked_normalized, BoundedSupports, ElectionScore, EvaluateSupport, - Supports, VoteWeight, -}; +use sp_npos_elections::{BoundedSupports, ElectionScore, IdentifierT, Supports, VoteWeight}; use sp_runtime::{ transaction_validity::{ InvalidTransaction, TransactionPriority, TransactionSource, TransactionValidity, @@ -430,13 +427,17 @@ impl Default for RawSolution { DefaultNoBound, scale_info::TypeInfo, )] -#[scale_info(skip_type_params(T))] -pub struct ReadySolution { +#[scale_info(skip_type_params(AccountId, MaxWinners))] +pub struct ReadySolution +where + AccountId: IdentifierT, + MaxWinners: Get, +{ /// The final supports of the solution. /// /// This is target-major vector, storing each winners, total backing, and each individual /// backer. - pub supports: BoundedSupports, + pub supports: BoundedSupports, /// The score of the solution. /// /// This is needed to potentially challenge the solution. @@ -451,11 +452,11 @@ pub struct ReadySolution { /// These are stored together because they are often accessed together. #[derive(PartialEq, Eq, Clone, Encode, Decode, RuntimeDebug, Default, TypeInfo)] #[scale_info(skip_type_params(T))] -pub struct RoundSnapshot { +pub struct RoundSnapshot { /// All of the voters. - pub voters: Vec>, + pub voters: Vec, /// All of the targets. - pub targets: Vec, + pub targets: Vec, } /// Encodes the length of a solution or a snapshot. @@ -614,6 +615,7 @@ pub mod pallet { type MinerConfig: crate::unsigned::MinerConfig< AccountId = Self::AccountId, MaxVotesPerVoter = ::MaxVotesPerVoter, + MaxWinners = Self::MaxWinners, >; /// Maximum number of signed submissions that can be queued. @@ -733,6 +735,11 @@ pub mod pallet { fn max_votes_per_voter() -> u32 { ::MaxVotesPerVoter::get() } + + #[pallet::constant_name(MinerMaxWinners)] + fn max_winners() -> u32 { + ::MaxWinners::get() + } } #[pallet::hooks] @@ -1247,14 +1254,15 @@ pub mod pallet { /// Current best solution, signed or unsigned, queued to be returned upon `elect`. #[pallet::storage] #[pallet::getter(fn queued_solution)] - pub type QueuedSolution = StorageValue<_, ReadySolution>; + pub type QueuedSolution = + StorageValue<_, ReadySolution>; /// Snapshot data of the round. /// /// This is created at the beginning of the signed phase and cleared upon calling `elect`. #[pallet::storage] #[pallet::getter(fn snapshot)] - pub type Snapshot = StorageValue<_, RoundSnapshot>; + pub type Snapshot = StorageValue<_, RoundSnapshot>>; /// Desired number of targets to elect for this round. /// @@ -1385,7 +1393,7 @@ impl Pallet { // instead of using storage APIs, we do a manual encoding into a fixed-size buffer. // `encoded_size` encodes it without storing it anywhere, this should not cause any // allocation. - let snapshot = RoundSnapshot:: { voters, targets }; + let snapshot = RoundSnapshot::> { voters, targets }; let size = snapshot.encoded_size(); log!(debug, "snapshot pre-calculated size {:?}", size); let mut buffer = Vec::with_capacity(size); @@ -1479,89 +1487,22 @@ impl Pallet { pub fn feasibility_check( raw_solution: RawSolution>, compute: ElectionCompute, - ) -> Result, FeasibilityError> { - let RawSolution { solution, score, round } = raw_solution; - - // First, check round. - ensure!(Self::round() == round, FeasibilityError::InvalidRound); - - // Winners are not directly encoded in the solution. - let winners = solution.unique_targets(); - + ) -> Result, FeasibilityError> { let desired_targets = Self::desired_targets().ok_or(FeasibilityError::SnapshotUnavailable)?; - ensure!(winners.len() as u32 == desired_targets, FeasibilityError::WrongWinnerCount); - // Fail early if targets requested by data provider exceed maximum winners supported. - ensure!( - desired_targets <= ::MaxWinners::get(), - FeasibilityError::TooManyDesiredTargets - ); - - // Ensure that the solution's score can pass absolute min-score. - let submitted_score = raw_solution.score; - ensure!( - Self::minimum_untrusted_score().map_or(true, |min_score| { - submitted_score.strict_threshold_better(min_score, Perbill::zero()) - }), - FeasibilityError::UntrustedScoreTooLow - ); - - // Read the entire snapshot. - let RoundSnapshot { voters: snapshot_voters, targets: snapshot_targets } = - Self::snapshot().ok_or(FeasibilityError::SnapshotUnavailable)?; - - // ----- Start building. First, we need some closures. - let cache = helpers::generate_voter_cache::(&snapshot_voters); - let voter_at = helpers::voter_at_fn::(&snapshot_voters); - let target_at = helpers::target_at_fn::(&snapshot_targets); - let voter_index = helpers::voter_index_fn_usize::(&cache); - - // Then convert solution -> assignment. This will fail if any of the indices are gibberish, - // namely any of the voters or targets. - let assignments = solution - .into_assignment(voter_at, target_at) - .map_err::(Into::into)?; - - // Ensure that assignments is correct. - let _ = assignments.iter().try_for_each(|assignment| { - // Check that assignment.who is actually a voter (defensive-only). - // NOTE: while using the index map from `voter_index` is better than a blind linear - // search, this *still* has room for optimization. Note that we had the index when - // we did `solution -> assignment` and we lost it. Ideal is to keep the index - // around. - - // Defensive-only: must exist in the snapshot. - let snapshot_index = - voter_index(&assignment.who).ok_or(FeasibilityError::InvalidVoter)?; - // Defensive-only: index comes from the snapshot, must exist. - let (_voter, _stake, targets) = - snapshot_voters.get(snapshot_index).ok_or(FeasibilityError::InvalidVoter)?; - - // Check that all of the targets are valid based on the snapshot. - if assignment.distribution.iter().any(|(d, _)| !targets.contains(d)) { - return Err(FeasibilityError::InvalidVote) - } - Ok(()) - })?; - - // ----- Start building support. First, we need one more closure. - let stake_of = helpers::stake_of_fn::(&snapshot_voters, &cache); - - // This might fail if the normalization fails. Very unlikely. See `integrity_test`. - let staked_assignments = assignment_ratio_to_staked_normalized(assignments, stake_of) - .map_err::(Into::into)?; - let supports = sp_npos_elections::to_supports(&staked_assignments); - - // Finally, check that the claimed score was indeed correct. - let known_score = supports.evaluate(); - ensure!(known_score == score, FeasibilityError::InvalidScore); - - // Size of winners in miner solution is equal to `desired_targets` <= `MaxWinners`. - let supports = supports - .try_into() - .defensive_map_err(|_| FeasibilityError::BoundedConversionFailed)?; - Ok(ReadySolution { supports, compute, score }) + let snapshot = Self::snapshot().ok_or(FeasibilityError::SnapshotUnavailable)?; + let round = Self::round(); + let minimum_untrusted_score = Self::minimum_untrusted_score(); + + Miner::::feasibility_check( + raw_solution, + compute, + desired_targets, + snapshot, + round, + minimum_untrusted_score, + ) } /// Perform the tasks to be done after a new `elect` has been triggered: diff --git a/frame/election-provider-multi-phase/src/mock.rs b/frame/election-provider-multi-phase/src/mock.rs index 8dee94bb3..da7a0cf1d 100644 --- a/frame/election-provider-multi-phase/src/mock.rs +++ b/frame/election-provider-multi-phase/src/mock.rs @@ -297,6 +297,8 @@ parameter_types! { pub static MockWeightInfo: MockedWeightInfo = MockedWeightInfo::Real; pub static MaxElectingVoters: VoterIndex = u32::max_value(); pub static MaxElectableTargets: TargetIndex = TargetIndex::max_value(); + + #[derive(Debug)] pub static MaxWinners: u32 = 200; pub static EpochLength: u64 = 30; @@ -359,6 +361,7 @@ impl MinerConfig for Runtime { type MaxLength = MinerMaxLength; type MaxWeight = MinerMaxWeight; type MaxVotesPerVoter = ::MaxVotesPerVoter; + type MaxWinners = MaxWinners; type Solution = TestNposSolution; fn solution_weight(v: u32, t: u32, a: u32, d: u32) -> Weight { diff --git a/frame/election-provider-multi-phase/src/signed.rs b/frame/election-provider-multi-phase/src/signed.rs index 75bbcfcf9..b8a27fdfa 100644 --- a/frame/election-provider-multi-phase/src/signed.rs +++ b/frame/election-provider-multi-phase/src/signed.rs @@ -462,7 +462,7 @@ impl Pallet { /// /// Infallible pub fn finalize_signed_phase_accept_solution( - ready_solution: ReadySolution, + ready_solution: ReadySolution, who: &T::AccountId, deposit: BalanceOf, call_fee: BalanceOf, diff --git a/frame/election-provider-multi-phase/src/unsigned.rs b/frame/election-provider-multi-phase/src/unsigned.rs index 3d80f8f4d..9c09cb48c 100644 --- a/frame/election-provider-multi-phase/src/unsigned.rs +++ b/frame/election-provider-multi-phase/src/unsigned.rs @@ -23,12 +23,17 @@ use crate::{ }; use codec::Encode; use frame_election_provider_support::{NposSolution, NposSolver, PerThing128, VoteWeight}; -use frame_support::{dispatch::DispatchResult, ensure, traits::Get, BoundedVec}; +use frame_support::{ + dispatch::DispatchResult, + ensure, + traits::{DefensiveResult, Get}, + BoundedVec, +}; use frame_system::offchain::SubmitTransaction; use scale_info::TypeInfo; use sp_npos_elections::{ assignment_ratio_to_staked_normalized, assignment_staked_to_ratio_normalized, ElectionResult, - ElectionScore, + ElectionScore, EvaluateSupport, }; use sp_runtime::{ offchain::storage::{MutateStorageError, StorageValueRef}, @@ -351,7 +356,7 @@ impl Pallet { // ensure score is being improved. Panic henceforth. ensure!( - Self::queued_solution().map_or(true, |q: ReadySolution<_>| raw_solution + Self::queued_solution().map_or(true, |q: ReadySolution<_, _>| raw_solution .score .strict_threshold_better(q.score, T::BetterUnsignedThreshold::get())), Error::::PreDispatchWeakSubmission, @@ -387,6 +392,8 @@ pub trait MinerConfig { /// /// The weight is computed using `solution_weight`. type MaxWeight: Get; + /// The maximum number of winners that can be elected. + type MaxWinners: Get; /// Something that can compute the weight of a solution. /// /// This weight estimate is then used to trim the solution, based on [`MinerConfig::MaxWeight`]. @@ -689,6 +696,91 @@ impl Miner { ); final_decision } + + /// Checks the feasibility of a solution. + pub fn feasibility_check( + raw_solution: RawSolution>, + compute: ElectionCompute, + desired_targets: u32, + snapshot: RoundSnapshot>, + current_round: u32, + minimum_untrusted_score: Option, + ) -> Result, FeasibilityError> { + let RawSolution { solution, score, round } = raw_solution; + let RoundSnapshot { voters: snapshot_voters, targets: snapshot_targets } = snapshot; + + // First, check round. + ensure!(current_round == round, FeasibilityError::InvalidRound); + + // Winners are not directly encoded in the solution. + let winners = solution.unique_targets(); + + ensure!(winners.len() as u32 == desired_targets, FeasibilityError::WrongWinnerCount); + // Fail early if targets requested by data provider exceed maximum winners supported. + ensure!(desired_targets <= T::MaxWinners::get(), FeasibilityError::TooManyDesiredTargets); + + // Ensure that the solution's score can pass absolute min-score. + let submitted_score = raw_solution.score; + ensure!( + minimum_untrusted_score.map_or(true, |min_score| { + submitted_score.strict_threshold_better(min_score, sp_runtime::Perbill::zero()) + }), + FeasibilityError::UntrustedScoreTooLow + ); + + // ----- Start building. First, we need some closures. + let cache = helpers::generate_voter_cache::(&snapshot_voters); + let voter_at = helpers::voter_at_fn::(&snapshot_voters); + let target_at = helpers::target_at_fn::(&snapshot_targets); + let voter_index = helpers::voter_index_fn_usize::(&cache); + + // Then convert solution -> assignment. This will fail if any of the indices are gibberish, + // namely any of the voters or targets. + let assignments = solution + .into_assignment(voter_at, target_at) + .map_err::(Into::into)?; + + // Ensure that assignments is correct. + let _ = assignments.iter().try_for_each(|assignment| { + // Check that assignment.who is actually a voter (defensive-only). + // NOTE: while using the index map from `voter_index` is better than a blind linear + // search, this *still* has room for optimization. Note that we had the index when + // we did `solution -> assignment` and we lost it. Ideal is to keep the index + // around. + + // Defensive-only: must exist in the snapshot. + let snapshot_index = + voter_index(&assignment.who).ok_or(FeasibilityError::InvalidVoter)?; + // Defensive-only: index comes from the snapshot, must exist. + let (_voter, _stake, targets) = + snapshot_voters.get(snapshot_index).ok_or(FeasibilityError::InvalidVoter)?; + + // Check that all of the targets are valid based on the snapshot. + if assignment.distribution.iter().any(|(d, _)| !targets.contains(d)) { + return Err(FeasibilityError::InvalidVote) + } + Ok(()) + })?; + + // ----- Start building support. First, we need one more closure. + let stake_of = helpers::stake_of_fn::(&snapshot_voters, &cache); + + // This might fail if the normalization fails. Very unlikely. See `integrity_test`. + let staked_assignments = assignment_ratio_to_staked_normalized(assignments, stake_of) + .map_err::(Into::into)?; + let supports = sp_npos_elections::to_supports(&staked_assignments); + + // Finally, check that the claimed score was indeed correct. + let known_score = supports.evaluate(); + ensure!(known_score == score, FeasibilityError::InvalidScore); + + // Size of winners in miner solution is equal to `desired_targets` <= `MaxWinners`. + let supports = supports + .try_into() + .defensive_map_err(|_| FeasibilityError::BoundedConversionFailed)?; + + Ok(ReadySolution { supports, compute, score }) + } } #[cfg(test)] From ae83a672f2bbe6a4f7391f6a5b2b6fd7ad4f8651 Mon Sep 17 00:00:00 2001 From: Ross Bulat Date: Wed, 15 Mar 2023 12:07:55 +0800 Subject: [PATCH 234/558] Nomination Pool Commission (#13128) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * + nomination pool commission * fmt * use register_update() * Update frame/nomination-pools/src/lib.rs Co-authored-by: Gonçalo Pestana * Update frame/nomination-pools/src/lib.rs Co-authored-by: Gonçalo Pestana * fmt * amend comments * + test for set_commission * fix * Update frame/nomination-pools/fuzzer/src/call.rs Co-authored-by: Kian Paimani <5588131+kianenigma@users.noreply.github.com> * rm comment * use PalletError * some feedback item amendments * update weights * revert PalletError stuff * ".git/.scripts/commands/fmt/fmt.sh" * make pool_events_since_last_call more modular * fmt * fix call indexes + test * add payout teste * add event to max_commisson updating current * begin refactor * some debugging * update * more tests * rewardpol not working * commission refactor * pending rewards returns commission * fmt * add claim_commission call * + claim_commission * fix benchmarks * weight 0 for now * + claim_commission benchmark * fmt * apply commission to benchmarks * ".git/.scripts/commands/bench/bench.sh" pallet dev pallet_nomination_pools * ".git/.scripts/commands/fmt/fmt.sh" * clippy * + pending * add RewardPool.total_rewards_acounted * fixes * println * more logs * Fix plus cleanups * fix assert * tidy up * tests work + tidy up * rm unused * clippy fix * persist reward_pool update * claim_commission_works tests * . * some test formatting * add high level docs * add calls * docs * rename * rename * docs * rename * fmt * use matches! * Update frame/nomination-pools/src/lib.rs Co-authored-by: Gonçalo Pestana * Update frame/nomination-pools/src/lib.rs Co-authored-by: Gonçalo Pestana * Update frame/nomination-pools/src/tests.rs Co-authored-by: Gonçalo Pestana * comment * Update frame/nomination-pools/src/lib.rs Co-authored-by: Gonçalo Pestana * . * weights order * ".git/.scripts/commands/bench/bench.sh" pallet dev pallet_nomination_pools * use from_parts * comment * ".git/.scripts/commands/fmt/fmt.sh" * revert clippy suggestions on old migrations * ".git/.scripts/commands/bench/bench.sh" pallet dev pallet_nomination_pools * add InitialGlobalMaxCommission * fix migration * reward counter comments & explanations * format * add commission implementation note * fmt * revert InitialGlobalMaxCommission * global max commission migration generic * text * 100% commission no payout test * add commission_accumulates_on_multiple_rewards * non-zero fuzzer GlobalMaxCommission * add last_recorded_total_payouts_needs_commission * commission event fix + claim commission test --------- Co-authored-by: Gonçalo Pestana Co-authored-by: Kian Paimani <5588131+kianenigma@users.noreply.github.com> Co-authored-by: command-bot <> Co-authored-by: Bastian Köcher --- .../nomination-pools/benchmarking/src/lib.rs | 161 +- .../nomination-pools/benchmarking/src/mock.rs | 3 +- frame/nomination-pools/fuzzer/src/call.rs | 7 +- frame/nomination-pools/src/lib.rs | 686 ++++++- frame/nomination-pools/src/migration.rs | 101 +- frame/nomination-pools/src/mock.rs | 39 +- frame/nomination-pools/src/tests.rs | 1602 ++++++++++++++++- frame/nomination-pools/src/weights.rs | 590 +++--- .../nomination-pools/test-staking/src/mock.rs | 3 +- 9 files changed, 2730 insertions(+), 462 deletions(-) diff --git a/frame/nomination-pools/benchmarking/src/lib.rs b/frame/nomination-pools/benchmarking/src/lib.rs index 094289ee0..d58bbaf3d 100644 --- a/frame/nomination-pools/benchmarking/src/lib.rs +++ b/frame/nomination-pools/benchmarking/src/lib.rs @@ -31,10 +31,14 @@ use frame_support::{assert_ok, ensure, traits::Get}; use frame_system::RawOrigin as RuntimeOrigin; use pallet_nomination_pools::{ BalanceOf, BondExtra, BondedPoolInner, BondedPools, ClaimPermission, ClaimPermissions, - ConfigOp, MaxPoolMembers, MaxPoolMembersPerPool, MaxPools, Metadata, MinCreateBond, - MinJoinBond, Pallet as Pools, PoolMembers, PoolRoles, PoolState, RewardPools, SubPoolsStorage, + Commission, CommissionChangeRate, ConfigOp, GlobalMaxCommission, MaxPoolMembers, + MaxPoolMembersPerPool, MaxPools, Metadata, MinCreateBond, MinJoinBond, Pallet as Pools, + PoolMembers, PoolRoles, PoolState, RewardPools, SubPoolsStorage, +}; +use sp_runtime::{ + traits::{Bounded, StaticLookup, Zero}, + Perbill, }; -use sp_runtime::traits::{Bounded, StaticLookup, Zero}; use sp_staking::{EraIndex, StakingInterface}; // `frame_benchmarking::benchmarks!` macro needs this use pallet_nomination_pools::Call; @@ -69,6 +73,7 @@ fn create_funded_user_with_balance( fn create_pool_account( n: u32, balance: BalanceOf, + commission: Option, ) -> (T::AccountId, T::AccountId) { let ed = CurrencyOf::::minimum_balance(); let pool_creator: T::AccountId = @@ -84,6 +89,16 @@ fn create_pool_account( ) .unwrap(); + if let Some(c) = commission { + let pool_id = pallet_nomination_pools::LastPoolId::::get(); + Pools::::set_commission( + RuntimeOrigin::Signed(pool_creator.clone()).into(), + pool_id, + Some((c, pool_creator.clone())), + ) + .expect("pool just created, commission can be set by root; qed"); + } + let pool_account = pallet_nomination_pools::BondedPools::::iter() .find(|(_, bonded_pool)| bonded_pool.roles.depositor == pool_creator) .map(|(pool_id, _)| Pools::::create_bonded_account(pool_id)) @@ -134,14 +149,18 @@ impl ListScenario { sp_std::mem::forget(i); // Create accounts with the origin weight - let (pool_creator1, pool_origin1) = create_pool_account::(USER_SEED + 1, origin_weight); + let (pool_creator1, pool_origin1) = + create_pool_account::(USER_SEED + 1, origin_weight, Some(Perbill::from_percent(50))); + T::Staking::nominate( &pool_origin1, // NOTE: these don't really need to be validators. vec![account("random_validator", 0, USER_SEED)], )?; - let (_, pool_origin2) = create_pool_account::(USER_SEED + 2, origin_weight); + let (_, pool_origin2) = + create_pool_account::(USER_SEED + 2, origin_weight, Some(Perbill::from_percent(50))); + T::Staking::nominate( &pool_origin2, vec![account("random_validator", 0, USER_SEED)].clone(), @@ -157,7 +176,9 @@ impl ListScenario { dest_weight_as_vote.try_into().map_err(|_| "could not convert u64 to Balance")?; // Create an account with the worst case destination weight - let (_, pool_dest1) = create_pool_account::(USER_SEED + 3, dest_weight); + let (_, pool_dest1) = + create_pool_account::(USER_SEED + 3, dest_weight, Some(Perbill::from_percent(50))); + T::Staking::nominate(&pool_dest1, vec![account("random_validator", 0, USER_SEED)])?; let weight_of = pallet_staking::Pallet::::weight_of_fn(); @@ -269,18 +290,19 @@ frame_benchmarking::benchmarks! { }: _(RuntimeOrigin::Signed(claimer), T::Lookup::unlookup(scenario.creator1.clone()), BondExtra::Rewards) verify { + // commission of 50% deducted here. assert!( T::Staking::active_stake(&scenario.origin1).unwrap() >= - scenario.dest_weight + scenario.dest_weight / 2u32.into() ); } claim_payout { let claimer: T::AccountId = account("claimer", USER_SEED + 4, 0); - + let commission = Perbill::from_percent(50); let origin_weight = Pools::::depositor_min_bond() * 2u32.into(); let ed = CurrencyOf::::minimum_balance(); - let (depositor, pool_account) = create_pool_account::(0, origin_weight); + let (depositor, pool_account) = create_pool_account::(0, origin_weight, Some(commission)); let reward_account = Pools::::create_reward_account(1); // Send funds to the reward account of the pool @@ -301,11 +323,11 @@ frame_benchmarking::benchmarks! { verify { assert_eq!( CurrencyOf::::free_balance(&depositor), - origin_weight * 2u32.into() + origin_weight + commission * origin_weight ); assert_eq!( CurrencyOf::::free_balance(&reward_account), - ed + Zero::zero() + ed + commission * origin_weight ); } @@ -345,7 +367,7 @@ frame_benchmarking::benchmarks! { let s in 0 .. MAX_SPANS; let min_create_bond = Pools::::depositor_min_bond(); - let (depositor, pool_account) = create_pool_account::(0, min_create_bond); + let (depositor, pool_account) = create_pool_account::(0, min_create_bond, None); // Add a new member let min_join_bond = MinJoinBond::::get().max(CurrencyOf::::minimum_balance()); @@ -387,7 +409,7 @@ frame_benchmarking::benchmarks! { let s in 0 .. MAX_SPANS; let min_create_bond = Pools::::depositor_min_bond(); - let (depositor, pool_account) = create_pool_account::(0, min_create_bond); + let (depositor, pool_account) = create_pool_account::(0, min_create_bond, None); // Add a new member let min_join_bond = MinJoinBond::::get().max(CurrencyOf::::minimum_balance()); @@ -433,7 +455,7 @@ frame_benchmarking::benchmarks! { let s in 0 .. MAX_SPANS; let min_create_bond = Pools::::depositor_min_bond(); - let (depositor, pool_account) = create_pool_account::(0, min_create_bond); + let (depositor, pool_account) = create_pool_account::(0, min_create_bond, None); let depositor_lookup = T::Lookup::unlookup(depositor.clone()); // We set the pool to the destroying state so the depositor can leave @@ -523,15 +545,16 @@ frame_benchmarking::benchmarks! { assert_eq!( new_pool, BondedPoolInner { - points: min_create_bond, - state: PoolState::Open, + commission: Commission::default(), member_counter: 1, + points: min_create_bond, roles: PoolRoles { depositor: depositor.clone(), root: Some(depositor.clone()), nominator: Some(depositor.clone()), bouncer: Some(depositor.clone()), }, + state: PoolState::Open, } ); assert_eq!( @@ -545,7 +568,7 @@ frame_benchmarking::benchmarks! { // Create a pool let min_create_bond = Pools::::depositor_min_bond() * 2u32.into(); - let (depositor, pool_account) = create_pool_account::(0, min_create_bond); + let (depositor, pool_account) = create_pool_account::(0, min_create_bond, None); // Create some accounts to nominate. For the sake of benchmarking they don't need to be // actual validators @@ -562,15 +585,16 @@ frame_benchmarking::benchmarks! { assert_eq!( new_pool, BondedPoolInner { - points: min_create_bond, - state: PoolState::Open, + commission: Commission::default(), member_counter: 1, + points: min_create_bond, roles: PoolRoles { depositor: depositor.clone(), root: Some(depositor.clone()), nominator: Some(depositor.clone()), bouncer: Some(depositor.clone()), - } + }, + state: PoolState::Open, } ); assert_eq!( @@ -582,7 +606,7 @@ frame_benchmarking::benchmarks! { set_state { // Create a pool let min_create_bond = Pools::::depositor_min_bond(); - let (depositor, pool_account) = create_pool_account::(0, min_create_bond); + let (depositor, pool_account) = create_pool_account::(0, min_create_bond, None); BondedPools::::mutate(&1, |maybe_pool| { // Force the pool into an invalid state maybe_pool.as_mut().map(|mut pool| pool.points = min_create_bond * 10u32.into()); @@ -599,7 +623,7 @@ frame_benchmarking::benchmarks! { let n in 1 .. ::MaxMetadataLen::get(); // Create a pool - let (depositor, pool_account) = create_pool_account::(0, Pools::::depositor_min_bond() * 2u32.into()); + let (depositor, pool_account) = create_pool_account::(0, Pools::::depositor_min_bond() * 2u32.into(), None); // Create metadata of the max possible size let metadata: Vec = (0..n).map(|_| 42).collect(); @@ -617,18 +641,20 @@ frame_benchmarking::benchmarks! { ConfigOp::Set(BalanceOf::::max_value()), ConfigOp::Set(u32::MAX), ConfigOp::Set(u32::MAX), - ConfigOp::Set(u32::MAX) + ConfigOp::Set(u32::MAX), + ConfigOp::Set(Perbill::max_value()) ) verify { assert_eq!(MinJoinBond::::get(), BalanceOf::::max_value()); assert_eq!(MinCreateBond::::get(), BalanceOf::::max_value()); assert_eq!(MaxPools::::get(), Some(u32::MAX)); assert_eq!(MaxPoolMembers::::get(), Some(u32::MAX)); assert_eq!(MaxPoolMembersPerPool::::get(), Some(u32::MAX)); + assert_eq!(GlobalMaxCommission::::get(), Some(Perbill::max_value())); } update_roles { let first_id = pallet_nomination_pools::LastPoolId::::get() + 1; - let (root, _) = create_pool_account::(0, Pools::::depositor_min_bond() * 2u32.into()); + let (root, _) = create_pool_account::(0, Pools::::depositor_min_bond() * 2u32.into(), None); let random: T::AccountId = account("but is anything really random in computers..?", 0, USER_SEED); }:_( RuntimeOrigin::Signed(root.clone()), @@ -650,7 +676,7 @@ frame_benchmarking::benchmarks! { chill { // Create a pool - let (depositor, pool_account) = create_pool_account::(0, Pools::::depositor_min_bond() * 2u32.into()); + let (depositor, pool_account) = create_pool_account::(0, Pools::::depositor_min_bond() * 2u32.into(), None); // Nominate with the pool. let validators: Vec<_> = (0..T::MaxNominations::get()) @@ -666,10 +692,68 @@ frame_benchmarking::benchmarks! { assert!(T::Staking::nominations(Pools::::create_bonded_account(1)).is_none()); } + set_commission { + // Create a pool - do not set a commission yet. + let (depositor, pool_account) = create_pool_account::(0, Pools::::depositor_min_bond() * 2u32.into(), None); + // set a max commission + Pools::::set_commission_max(RuntimeOrigin::Signed(depositor.clone()).into(), 1u32.into(), Perbill::from_percent(50)).unwrap(); + // set a change rate + Pools::::set_commission_change_rate(RuntimeOrigin::Signed(depositor.clone()).into(), 1u32.into(), CommissionChangeRate { + max_increase: Perbill::from_percent(20), + min_delay: 0u32.into(), + }).unwrap(); + + }:_(RuntimeOrigin::Signed(depositor.clone()), 1u32.into(), Some((Perbill::from_percent(20), depositor.clone()))) + verify { + assert_eq!(BondedPools::::get(1).unwrap().commission, Commission { + current: Some((Perbill::from_percent(20), depositor)), + max: Some(Perbill::from_percent(50)), + change_rate: Some(CommissionChangeRate { + max_increase: Perbill::from_percent(20), + min_delay: 0u32.into() + }), + throttle_from: Some(1u32.into()), + }); + } + + set_commission_max { + // Create a pool, setting a commission that will update when max commission is set. + let (depositor, pool_account) = create_pool_account::(0, Pools::::depositor_min_bond() * 2u32.into(), Some(Perbill::from_percent(50))); + }:_(RuntimeOrigin::Signed(depositor.clone()), 1u32.into(), Perbill::from_percent(50)) + verify { + assert_eq!( + BondedPools::::get(1).unwrap().commission, Commission { + current: Some((Perbill::from_percent(50), depositor)), + max: Some(Perbill::from_percent(50)), + change_rate: None, + throttle_from: Some(0u32.into()), + }); + } + + set_commission_change_rate { + // Create a pool + let (depositor, pool_account) = create_pool_account::(0, Pools::::depositor_min_bond() * 2u32.into(), None); + }:_(RuntimeOrigin::Signed(depositor.clone()), 1u32.into(), CommissionChangeRate { + max_increase: Perbill::from_percent(50), + min_delay: 1000u32.into(), + }) + verify { + assert_eq!( + BondedPools::::get(1).unwrap().commission, Commission { + current: None, + max: None, + change_rate: Some(CommissionChangeRate { + max_increase: Perbill::from_percent(50), + min_delay: 1000u32.into(), + }), + throttle_from: Some(1_u32.into()), + }); + } + set_claim_permission { // Create a pool let min_create_bond = Pools::::depositor_min_bond(); - let (depositor, pool_account) = create_pool_account::(0, min_create_bond); + let (depositor, pool_account) = create_pool_account::(0, min_create_bond, None); // Join pool let min_join_bond = MinJoinBond::::get().max(CurrencyOf::::minimum_balance()); @@ -688,6 +772,31 @@ frame_benchmarking::benchmarks! { assert_eq!(ClaimPermissions::::get(joiner), ClaimPermission::PermissionlessAll); } + claim_commission { + let claimer: T::AccountId = account("claimer_member", USER_SEED + 4, 0); + let commission = Perbill::from_percent(50); + let origin_weight = Pools::::depositor_min_bond() * 2u32.into(); + let ed = CurrencyOf::::minimum_balance(); + let (depositor, pool_account) = create_pool_account::(0, origin_weight, Some(commission)); + let reward_account = Pools::::create_reward_account(1); + CurrencyOf::::make_free_balance_be(&reward_account, ed + origin_weight); + + // member claims a payout to make some commission available. + let _ = Pools::::claim_payout(RuntimeOrigin::Signed(claimer).into()); + + whitelist_account!(depositor); + }:_(RuntimeOrigin::Signed(depositor.clone()), 1u32.into()) + verify { + assert_eq!( + CurrencyOf::::free_balance(&depositor), + origin_weight + commission * origin_weight + ); + assert_eq!( + CurrencyOf::::free_balance(&reward_account), + ed + commission * origin_weight + ); + } + impl_benchmark_test_suite!( Pallet, crate::mock::new_test_ext(), diff --git a/frame/nomination-pools/benchmarking/src/mock.rs b/frame/nomination-pools/benchmarking/src/mock.rs index f6b2022bc..4a1a52868 100644 --- a/frame/nomination-pools/benchmarking/src/mock.rs +++ b/frame/nomination-pools/benchmarking/src/mock.rs @@ -20,7 +20,7 @@ use frame_election_provider_support::VoteWeight; use frame_support::{pallet_prelude::*, parameter_types, traits::ConstU64, PalletId}; use sp_runtime::{ traits::{Convert, IdentityLookup}, - FixedU128, + FixedU128, Perbill, }; type AccountId = u128; @@ -195,6 +195,7 @@ pub fn new_test_ext() -> sp_io::TestExternalities { max_pools: Some(3), max_members_per_pool: Some(3), max_members: Some(3 * 3), + global_max_commission: Some(Perbill::from_percent(50)), } .assimilate_storage(&mut storage); sp_io::TestExternalities::from(storage) diff --git a/frame/nomination-pools/fuzzer/src/call.rs b/frame/nomination-pools/fuzzer/src/call.rs index 5e8247120..027fb2b69 100644 --- a/frame/nomination-pools/fuzzer/src/call.rs +++ b/frame/nomination-pools/fuzzer/src/call.rs @@ -33,11 +33,11 @@ use pallet_nomination_pools::{ mock::*, pallet as pools, pallet::{BondedPools, Call as PoolsCall, Event as PoolsEvents, PoolMembers}, - BondExtra, BondedPool, LastPoolId, MaxPoolMembers, MaxPoolMembersPerPool, MaxPools, - MinCreateBond, MinJoinBond, PoolId, + BondExtra, BondedPool, GlobalMaxCommission, LastPoolId, MaxPoolMembers, MaxPoolMembersPerPool, + MaxPools, MinCreateBond, MinJoinBond, PoolId, }; use rand::{seq::SliceRandom, Rng}; -use sp_runtime::{assert_eq_error_rate, Perquintill}; +use sp_runtime::{assert_eq_error_rate, Perbill, Perquintill}; const ERA: BlockNumber = 1000; const MAX_ED_MULTIPLE: Balance = 10_000; @@ -224,6 +224,7 @@ fn main() { MaxPoolMembers::::set(Some(10_000)); MaxPoolMembersPerPool::::set(Some(1000)); MaxPools::::set(Some(1_000)); + GlobalMaxCommission::::set(Some(Perbill::from_percent(25))); MinCreateBond::::set(10 * ExistentialDeposit::get()); MinJoinBond::::set(5 * ExistentialDeposit::get()); diff --git a/frame/nomination-pools/src/lib.rs b/frame/nomination-pools/src/lib.rs index c1dda69ab..e73d4b417 100644 --- a/frame/nomination-pools/src/lib.rs +++ b/frame/nomination-pools/src/lib.rs @@ -50,6 +50,11 @@ //! not nominating proper validators. //! * reward account: A similar key-less account, that is set as the `Payee` account for the bonded //! account for all staking rewards. +//! * change rate: The rate at which pool commission can be changed. A change rate consists of a +//! `max_increase` and `min_delay`, dictating the maximum percentage increase that can be applied +//! to the commission per number of blocks. +//! * throttle: An attempted commission increase is throttled if the attempted change falls outside +//! the change rate bounds. //! //! ## Usage //! @@ -125,8 +130,33 @@ //! other members have left. Once they fully withdraw their funds, the pool is destroyed. //! * Nominator: can select which validators the pool nominates. //! * Bouncer: can change the pools state and kick members if the pool is blocked. -//! * Root: can change the nominator, bouncer, or itself and can perform any of the actions the -//! nominator or bouncer can. +//! * Root: can change the nominator, bouncer, or itself, manage and claim commission, and can +//! perform any of the actions the nominator or bouncer can. +//! +//! ## Commission +//! +//! A pool can optionally have a commission configuration, via the `root` role, set with +//! [`Call::set_commission`] and claimed with [`Call::claim_commission`]. A payee account must be +//! supplied with the desired commission percentage. Beyond the commission itself, a pool can have a +//! maximum commission and a change rate. +//! +//! Importantly, both max commission [`Call::set_commission_max`] and change rate +//! [`Call::set_commission_change_rate`] can not be removed once set, and can only be set to more +//! restrictive values (i.e. a lower max commission or a slower change rate) in subsequent updates. +//! +//! If set, a pool's commission is bound to [`GlobalMaxCommission`] at the time it is applied to +//! pending rewards. [`GlobalMaxCommission`] is intended to be updated only via governance. +//! +//! When a pool is dissolved, any outstanding pending commission that has not been claimed will be +//! transferred to the depositor. +//! +//! Implementation note: Commission is analogous to a separate member account of the pool, with its +//! own reward counter in the form of `current_pending_commission`. +//! +//! Crucially, commission is applied to rewards based on the current commission in effect at the +//! time rewards are transferred into the reward pool. This is to prevent the malicious behaviour of +//! changing the commission rate to a very high value after rewards are accumulated, and thus claim +//! an unexpectedly high chunk of the reward. //! //! ### Dismantling //! @@ -232,11 +262,14 @@ //! //! ### Reward pool //! -//! When a pool is first bonded it sets up an deterministic, inaccessible account as its reward -//! destination. +//! When a pool is first bonded it sets up a deterministic, inaccessible account as its reward +//! destination. This reward account combined with `RewardPool` compose a reward pool. //! -//! The reward pool is not really a pool anymore, as it does not track points anymore. Instead, it -//! tracks, a virtual value called `reward_counter`, among a few other values. +//! Reward pools are completely separate entities to bonded pools. Along with its account, a reward +//! pool also tracks its outstanding and claimed rewards as counters, in addition to pending and +//! claimed commission. These counters are updated with `RewardPool::update_records`. The current +//! reward counter of the pool (the total outstanding rewards, in points) is also callable with the +//! `RewardPool::current_reward_counter` method. //! //! See [this link](https://hackmd.io/PFGn6wI5TbCmBYoEA_f2Uw) for an in-depth explanation of the //! reward pool mechanism. @@ -247,13 +280,12 @@ //! //! ### Unbonding sub pools //! -//! When a member unbonds, it's balance is unbonded in the bonded pool's account and tracked in -//! an unbonding pool associated with the active era. If no such pool exists, one is created. To -//! track which unbonding sub pool a member belongs too, a member tracks it's -//! `unbonding_era`. +//! When a member unbonds, it's balance is unbonded in the bonded pool's account and tracked in an +//! unbonding pool associated with the active era. If no such pool exists, one is created. To track +//! which unbonding sub pool a member belongs too, a member tracks it's `unbonding_era`. //! -//! When a member initiates unbonding it's claim on the bonded pool -//! (`balance_to_unbond`) is computed as: +//! When a member initiates unbonding it's claim on the bonded pool (`balance_to_unbond`) is +//! computed as: //! //! ```text //! balance_to_unbond = (bonded_pool.balance / bonded_pool.points) * member.points; @@ -262,8 +294,8 @@ //! If this is the first transfer into an unbonding pool arbitrary amount of points can be issued //! per balance. In this implementation unbonding pools are initialized with a 1 point to 1 balance //! ratio (see [`POINTS_TO_BALANCE_INIT_RATIO`]). Otherwise, the unbonding pools hold the same -//! points to balance ratio properties as the bonded pool, so member points in the -//! unbonding pool are issued based on +//! points to balance ratio properties as the bonded pool, so member points in the unbonding pool +//! are issued based on //! //! ```text //! new_points_issued = (points_before_transfer / balance_before_transfer) * balance_to_unbond; @@ -296,14 +328,14 @@ //! `pallet_staking::StakingLedger::slash`, which passes the information to this pallet via //! [`sp_staking::OnStakerSlash::on_slash`]. //! -//! Unbonding pools need to be slashed to ensure all nominators whom where in the bonded pool -//! while it was backing a validator that equivocated are punished. Without these measures a -//! member could unbond right after a validator equivocated with no consequences. +//! Unbonding pools need to be slashed to ensure all nominators whom where in the bonded pool while +//! it was backing a validator that equivocated are punished. Without these measures a member could +//! unbond right after a validator equivocated with no consequences. //! -//! This strategy is unfair to members who joined after the slash, because they get slashed as -//! well, but spares members who unbond. The latter is much more important for security: if a -//! pool's validators are attacking the network, their members need to unbond fast! Avoiding -//! slashes gives them an incentive to do that if validators get repeatedly slashed. +//! This strategy is unfair to members who joined after the slash, because they get slashed as well, +//! but spares members who unbond. The latter is much more important for security: if a pool's +//! validators are attacking the network, their members need to unbond fast! Avoiding slashes gives +//! them an incentive to do that if validators get repeatedly slashed. //! //! To be fair to joiners, this implementation also need joining pools, which are actively staking, //! in addition to the unbonding pools. For maintenance simplicity these are not implemented. @@ -332,21 +364,22 @@ use frame_support::{ Currency, Defensive, DefensiveOption, DefensiveResult, DefensiveSaturating, ExistenceRequirement, Get, }, - DefaultNoBound, + DefaultNoBound, PalletError, }; use scale_info::TypeInfo; use sp_core::U256; use sp_runtime::{ traits::{ - AccountIdConversion, CheckedAdd, CheckedSub, Convert, Saturating, StaticLookup, Zero, + AccountIdConversion, Bounded, CheckedAdd, CheckedSub, Convert, Saturating, StaticLookup, + Zero, }, - FixedPointNumber, + FixedPointNumber, Perbill, }; use sp_staking::{EraIndex, OnStakerSlash, StakingInterface}; use sp_std::{collections::btree_map::BTreeMap, fmt::Debug, ops::Div, vec::Vec}; /// The log target of this pallet. -pub const LOG_TARGET: &'static str = "runtime::nomination-pools"; +pub const LOG_TARGET: &str = "runtime::nomination-pools"; // syntactic sugar for logging. #[macro_export] @@ -430,19 +463,11 @@ pub enum ClaimPermission { impl ClaimPermission { fn can_bond_extra(&self) -> bool { - match self { - ClaimPermission::PermissionlessAll => true, - ClaimPermission::PermissionlessCompound => true, - _ => false, - } + matches!(self, ClaimPermission::PermissionlessAll | ClaimPermission::PermissionlessCompound) } fn can_claim_payout(&self) -> bool { - match self { - ClaimPermission::PermissionlessAll => true, - ClaimPermission::PermissionlessWithdraw => true, - _ => false, - } + matches!(self, ClaimPermission::PermissionlessAll | ClaimPermission::PermissionlessWithdraw) } } @@ -623,25 +648,232 @@ pub struct PoolRoles { pub bouncer: Option, } +/// Pool commission. +/// +/// The pool `root` can set commission configuration after pool creation. By default, all commission +/// values are `None`. Pool `root` can also set `max` and `change_rate` configurations before +/// setting an initial `current` commission. +/// +/// `current` is a tuple of the commission percentage and payee of commission. `throttle_from` +/// keeps track of which block `current` was last updated. A `max` commission value can only be +/// decreased after the initial value is set, to prevent commission from repeatedly increasing. +/// +/// An optional commission `change_rate` allows the pool to set strict limits to how much commission +/// can change in each update, and how often updates can take place. +#[derive( + Encode, Decode, DefaultNoBound, MaxEncodedLen, TypeInfo, DebugNoBound, PartialEq, Copy, Clone, +)] +#[codec(mel_bound(T: Config))] +#[scale_info(skip_type_params(T))] +pub struct Commission { + /// Optional commission rate of the pool along with the account commission is paid to. + pub current: Option<(Perbill, T::AccountId)>, + /// Optional maximum commission that can be set by the pool `root`. Once set, this value can + /// only be updated to a decreased value. + pub max: Option, + /// Optional configuration around how often commission can be updated, and when the last + /// commission update took place. + pub change_rate: Option>, + /// The block from where throttling should be checked from. This value will be updated on all + /// commission updates and when setting an initial `change_rate`. + pub throttle_from: Option, +} + +impl Commission { + /// Returns true if the current commission updating to `to` would exhaust the change rate + /// limits. + /// + /// A commission update will be throttled (disallowed) if: + /// 1. not enough blocks have passed since the `throttle_from` block, if exists, or + /// 2. the new commission is greater than the maximum allowed increase. + fn throttling(&self, to: &Perbill) -> bool { + if let Some(t) = self.change_rate.as_ref() { + let commission_as_percent = + self.current.as_ref().map(|(x, _)| *x).unwrap_or(Perbill::zero()); + + // do not throttle if `to` is the same or a decrease in commission. + if *to <= commission_as_percent { + return false + } + // Test for `max_increase` throttling. + // + // Throttled if the attempted increase in commission is greater than `max_increase`. + if (*to).saturating_sub(commission_as_percent) > t.max_increase { + return true + } + + // Test for `min_delay` throttling. + // + // Note: matching `None` is defensive only. `throttle_from` should always exist where + // `change_rate` has already been set, so this scenario should never happen. + return self.throttle_from.map_or_else( + || { + defensive!("throttle_from should exist if change_rate is set"); + true + }, + |f| { + // if `min_delay` is zero (no delay), not throttling. + if t.min_delay == Zero::zero() { + false + } else { + // throttling if blocks passed is less than `min_delay`. + let blocks_surpassed = + >::block_number().saturating_sub(f); + blocks_surpassed < t.min_delay + } + }, + ) + } + false + } + + /// Gets the pool's current commission, or returns Perbill::zero if none is set. + /// Bounded to global max if current is greater than `GlobalMaxCommission`. + fn current(&self) -> Perbill { + self.current + .as_ref() + .map_or(Perbill::zero(), |(c, _)| *c) + .min(GlobalMaxCommission::::get().unwrap_or(Bounded::max_value())) + } + + /// Set the pool's commission. + /// + /// Update commission based on `current`. If a `None` is supplied, allow the commission to be + /// removed without any change rate restrictions. Updates `throttle_from` to the current block. + /// If the supplied commission is zero, `None` will be inserted and `payee` will be ignored. + fn try_update_current(&mut self, current: &Option<(Perbill, T::AccountId)>) -> DispatchResult { + self.current = match current { + None => None, + Some((commission, payee)) => { + ensure!(!self.throttling(commission), Error::::CommissionChangeThrottled); + ensure!( + self.max.map_or(true, |m| commission <= &m), + Error::::CommissionExceedsMaximum + ); + if commission.is_zero() { + None + } else { + Some((*commission, payee.clone())) + } + }, + }; + self.register_update(); + Ok(()) + } + + /// Set the pool's maximum commission. + /// + /// The pool's maximum commission can initially be set to any value, and only smaller values + /// thereafter. If larger values are attempted, this function will return a dispatch error. + /// + /// If `current.0` is larger than the updated max commission value, `current.0` will also be + /// updated to the new maximum. This will also register a `throttle_from` update. + /// A `PoolCommissionUpdated` event is triggered if `current.0` is updated. + fn try_update_max(&mut self, pool_id: PoolId, new_max: Perbill) -> DispatchResult { + if let Some(old) = self.max.as_mut() { + if new_max > *old { + return Err(Error::::MaxCommissionRestricted.into()) + } + *old = new_max; + } else { + self.max = Some(new_max) + }; + let updated_current = self + .current + .as_mut() + .map(|(c, _)| { + let u = *c > new_max; + *c = (*c).min(new_max); + u + }) + .unwrap_or(false); + + if updated_current { + if let Some((_, payee)) = self.current.as_ref() { + Pallet::::deposit_event(Event::::PoolCommissionUpdated { + pool_id, + current: Some((new_max, payee.clone())), + }); + } + self.register_update(); + } + Ok(()) + } + + /// Set the pool's commission `change_rate`. + /// + /// Once a change rate configuration has been set, only more restrictive values can be set + /// thereafter. These restrictions translate to increased `min_delay` values and decreased + /// `max_increase` values. + /// + /// Update `throttle_from` to the current block upon setting change rate for the first time, so + /// throttling can be checked from this block. + fn try_update_change_rate( + &mut self, + change_rate: CommissionChangeRate, + ) -> DispatchResult { + ensure!(!&self.less_restrictive(&change_rate), Error::::CommissionChangeRateNotAllowed); + + if self.change_rate.is_none() { + self.register_update(); + } + self.change_rate = Some(change_rate); + Ok(()) + } + + /// Updates a commission's `throttle_from` field to the current block. + fn register_update(&mut self) { + self.throttle_from = Some(>::block_number()); + } + + /// Checks whether a change rate is less restrictive than the current change rate, if any. + /// + /// No change rate will always be less restrictive than some change rate, so where no + /// `change_rate` is currently set, `false` is returned. + fn less_restrictive(&self, new: &CommissionChangeRate) -> bool { + self.change_rate + .as_ref() + .map(|c| new.max_increase > c.max_increase || new.min_delay < c.min_delay) + .unwrap_or(false) + } +} + +/// Pool commission change rate preferences. +/// +/// The pool root is able to set a commission change rate for their pool. A commission change rate +/// consists of 2 values; (1) the maximum allowed commission change, and (2) the minimum amount of +/// blocks that must elapse before commission updates are allowed again. +/// +/// Commission change rates are not applied to decreases in commission. +#[derive(Encode, Decode, MaxEncodedLen, TypeInfo, Debug, PartialEq, Copy, Clone)] +pub struct CommissionChangeRate { + /// The maximum amount the commission can be updated by per `min_delay` period. + pub max_increase: Perbill, + /// How often an update can take place. + pub min_delay: BlockNumber, +} + /// Pool permissions and state #[derive(Encode, Decode, MaxEncodedLen, TypeInfo, DebugNoBound, PartialEq, Clone)] #[codec(mel_bound(T: Config))] #[scale_info(skip_type_params(T))] pub struct BondedPoolInner { - /// Total points of all the members in the pool who are actively bonded. - pub points: BalanceOf, - /// The current state of the pool. - pub state: PoolState, + /// The commission rate of the pool. + pub commission: Commission, /// Count of members that belong to the pool. pub member_counter: u32, + /// Total points of all the members in the pool who are actively bonded. + pub points: BalanceOf, /// See [`PoolRoles`]. pub roles: PoolRoles, + /// The current state of the pool. + pub state: PoolState, } /// A wrapper for bonded pools, with utility functions. /// -/// The main purpose of this is to wrap a [`BondedPoolInner`], with the account + id of the pool, -/// for easier access. +/// The main purpose of this is to wrap a [`BondedPoolInner`], with the account +/// + id of the pool, for easier access. #[derive(RuntimeDebugNoBound)] #[cfg_attr(feature = "std", derive(Clone, PartialEq))] pub struct BondedPool { @@ -670,10 +902,11 @@ impl BondedPool { Self { id, inner: BondedPoolInner { + commission: Commission::default(), + member_counter: Zero::zero(), + points: Zero::zero(), roles, state: PoolState::Open, - points: Zero::zero(), - member_counter: Zero::zero(), }, } } @@ -695,7 +928,7 @@ impl BondedPool { /// Consume self and put into storage. fn put(self) { - BondedPools::::insert(self.id, BondedPoolInner { ..self.inner }); + BondedPools::::insert(self.id, self.inner); } /// Consume self and remove from storage. @@ -800,6 +1033,10 @@ impl BondedPool { self.is_root(who) || self.is_bouncer(who) } + fn can_manage_commission(&self, who: &T::AccountId) -> bool { + self.is_root(who) + } + fn is_destroying(&self) -> bool { matches!(self.state, PoolState::Destroying) } @@ -952,7 +1189,7 @@ impl BondedPool { // Cache the value let bonded_account = self.bonded_account(); T::Currency::transfer( - &who, + who, &bonded_account, amount, match ty { @@ -1011,6 +1248,10 @@ pub struct RewardPool { last_recorded_total_payouts: BalanceOf, /// Total amount that this pool has paid out so far to the members. total_rewards_claimed: BalanceOf, + /// The amount of commission pending to be claimed. + total_commission_pending: BalanceOf, + /// The amount of commission that has been claimed. + total_commission_claimed: BalanceOf, } impl RewardPool { @@ -1024,13 +1265,40 @@ impl RewardPool { self.total_rewards_claimed = self.total_rewards_claimed.saturating_add(reward); } - /// Update the recorded values of the pool. - fn update_records(&mut self, id: PoolId, bonded_points: BalanceOf) -> Result<(), Error> { + /// Update the recorded values of the reward pool. + /// + /// This function MUST be called whenever the points in the bonded pool change, AND whenever the + /// the pools commission is updated. The reason for the former is that a change in pool points + /// will alter the share of the reward balance among pool members, and the reason for the latter + /// is that a change in commission will alter the share of the reward balance among the pool. + fn update_records( + &mut self, + id: PoolId, + bonded_points: BalanceOf, + commission: Perbill, + ) -> Result<(), Error> { let balance = Self::current_balance(id); - self.last_recorded_reward_counter = self.current_reward_counter(id, bonded_points)?; + + let (current_reward_counter, new_pending_commission) = + self.current_reward_counter(id, bonded_points, commission)?; + + // Store the reward counter at the time of this update. This is used in subsequent calls to + // `current_reward_counter`, whereby newly pending rewards (in points) are added to this + // value. + self.last_recorded_reward_counter = current_reward_counter; + + // Add any new pending commission that has been calculated from `current_reward_counter` to + // determine the total pending commission at the time of this update. + self.total_commission_pending = + self.total_commission_pending.saturating_add(new_pending_commission); + + // Store the total payouts at the time of this update. Total payouts are essentially the + // entire historical balance of the reward pool, equating to the current balance + the total + // rewards that have left the pool + the total commission that has left the pool. self.last_recorded_total_payouts = balance - .checked_add(&self.total_rewards_claimed) + .checked_add(&self.total_rewards_claimed.saturating_add(self.total_commission_claimed)) .ok_or(Error::::OverflowRisk)?; + Ok(()) } @@ -1040,15 +1308,27 @@ impl RewardPool { &self, id: PoolId, bonded_points: BalanceOf, - ) -> Result> { + commission: Perbill, + ) -> Result<(T::RewardCounter, BalanceOf), Error> { let balance = Self::current_balance(id); - let payouts_since_last_record = balance + + // Calculate the current payout balance. The first 3 values of this calculation added + // together represent what the balance would be if no payouts were made. The + // `last_recorded_total_payouts` is then subtracted from this value to cancel out previously + // recorded payouts, leaving only the remaining payouts that have not been claimed. + let current_payout_balance = balance .saturating_add(self.total_rewards_claimed) + .saturating_add(self.total_commission_claimed) .saturating_sub(self.last_recorded_total_payouts); + // Split the `current_payout_balance` into claimable rewards and claimable commission + // according to the current commission rate. + let new_pending_commission = commission * current_payout_balance; + let new_pending_rewards = current_payout_balance.saturating_sub(new_pending_commission); + // * accuracy notes regarding the multiplication in `checked_from_rational`: - // `payouts_since_last_record` is a subset of the total_issuance at the very - // worse. `bonded_points` are similarly, in a non-slashed pool, have the same granularity as + // `current_payout_balance` is a subset of the total_issuance at the very worse. + // `bonded_points` are similarly, in a non-slashed pool, have the same granularity as // balance, and are thus below within the range of total_issuance. In the worse case // scenario, for `saturating_from_rational`, we have: // @@ -1066,22 +1346,26 @@ impl RewardPool { // represented as `FixedU128`, which means it is less than `total_issuance * 10^18`. // // * accuracy notes regarding `checked_from_rational` collapsing to zero, meaning that no - // reward can be claimed: + // reward can be claimed: // - // largest `bonded_points`, such that the reward counter is non-zero, with `FixedU128` - // will be when the payout is being computed. This essentially means `payout/bonded_points` - // needs to be more than 1/1^18. Thus, assuming that `bonded_points` will always be less - // than `10 * dot_total_issuance`, if the reward_counter is the smallest possible value, - // the value of the reward being calculated is: + // largest `bonded_points`, such that the reward counter is non-zero, with `FixedU128` will + // be when the payout is being computed. This essentially means `payout/bonded_points` needs + // to be more than 1/1^18. Thus, assuming that `bonded_points` will always be less than `10 + // * dot_total_issuance`, if the reward_counter is the smallest possible value, the value of + // the + // reward being calculated is: // // x / 10^20 = 1/ 10^18 // // x = 100 // // which is basically 10^-8 DOTs. See `smallest_claimable_reward` for an example of this. - T::RewardCounter::checked_from_rational(payouts_since_last_record, bonded_points) - .and_then(|ref r| self.last_recorded_reward_counter.checked_add(r)) - .ok_or(Error::::OverflowRisk) + let current_reward_counter = + T::RewardCounter::checked_from_rational(new_pending_rewards, bonded_points) + .and_then(|ref r| self.last_recorded_reward_counter.checked_add(r)) + .ok_or(Error::::OverflowRisk)?; + + Ok((current_reward_counter, new_pending_commission)) } /// Current free balance of the reward pool. @@ -1209,9 +1493,10 @@ pub mod pallet { use super::*; use frame_support::traits::StorageVersion; use frame_system::{ensure_signed, pallet_prelude::*}; + use sp_runtime::Perbill; /// The current storage version. - const STORAGE_VERSION: StorageVersion = StorageVersion::new(3); + const STORAGE_VERSION: StorageVersion = StorageVersion::new(4); #[pallet::pallet] #[pallet::storage_version(STORAGE_VERSION)] @@ -1313,6 +1598,12 @@ pub mod pallet { #[pallet::storage] pub type MaxPoolMembersPerPool = StorageValue<_, u32, OptionQuery>; + /// The maximum commission that can be charged by a pool. Used on commission payouts to bound + /// pool commissions that are > `GlobalMaxCommission`, necessary if a future + /// `GlobalMaxCommission` is lower than some current pool commissions. + #[pallet::storage] + pub type GlobalMaxCommission = StorageValue<_, Perbill, OptionQuery>; + /// Active members. /// /// TWOX-NOTE: SAFE since `AccountId` is a secure hash. @@ -1326,13 +1617,13 @@ pub mod pallet { pub type BondedPools = CountedStorageMap<_, Twox64Concat, PoolId, BondedPoolInner>; - /// Reward pools. This is where there rewards for each pool accumulate. When a members payout - /// is claimed, the balance comes out fo the reward pool. Keyed by the bonded pools account. + /// Reward pools. This is where there rewards for each pool accumulate. When a members payout is + /// claimed, the balance comes out fo the reward pool. Keyed by the bonded pools account. #[pallet::storage] pub type RewardPools = CountedStorageMap<_, Twox64Concat, PoolId, RewardPool>; - /// Groups of unbonding pools. Each group of unbonding pools belongs to a bonded pool, - /// hence the name sub-pools. Keyed by the bonded pools account. + /// Groups of unbonding pools. Each group of unbonding pools belongs to a + /// bonded pool, hence the name sub-pools. Keyed by the bonded pools account. #[pallet::storage] pub type SubPoolsStorage = CountedStorageMap<_, Twox64Concat, PoolId, SubPools>; @@ -1365,6 +1656,7 @@ pub mod pallet { pub max_pools: Option, pub max_members_per_pool: Option, pub max_members: Option, + pub global_max_commission: Option, } #[cfg(feature = "std")] @@ -1376,6 +1668,7 @@ pub mod pallet { max_pools: Some(16), max_members_per_pool: Some(32), max_members: Some(16 * 32), + global_max_commission: None, } } } @@ -1394,6 +1687,9 @@ pub mod pallet { if let Some(max_members) = self.max_members { MaxPoolMembers::::put(max_members); } + if let Some(global_max_commission) = self.global_max_commission { + GlobalMaxCommission::::put(global_max_commission); + } } } @@ -1456,6 +1752,17 @@ pub mod pallet { PoolSlashed { pool_id: PoolId, balance: BalanceOf }, /// The unbond pool at `era` of pool `pool_id` has been slashed to `balance`. UnbondingPoolSlashed { pool_id: PoolId, era: EraIndex, balance: BalanceOf }, + /// A pool's commission setting has been changed. + PoolCommissionUpdated { pool_id: PoolId, current: Option<(Perbill, T::AccountId)> }, + /// A pool's maximum commission setting has been changed. + PoolMaxCommissionUpdated { pool_id: PoolId, max_commission: Perbill }, + /// A pool's commission `change_rate` has been changed. + PoolCommissionChangeRateUpdated { + pool_id: PoolId, + change_rate: CommissionChangeRate, + }, + /// Pool commission has been claimed. + PoolCommissionClaimed { pool_id: PoolId, commission: BalanceOf }, } #[pallet::error] @@ -1511,6 +1818,18 @@ pub mod pallet { Defensive(DefensiveError), /// Partial unbonding now allowed permissionlessly. PartialUnbondNotAllowedPermissionlessly, + /// The pool's max commission cannot be set higher than the existing value. + MaxCommissionRestricted, + /// The supplied commission exceeds the max allowed commission. + CommissionExceedsMaximum, + /// Not enough blocks have surpassed since the last commission update. + CommissionChangeThrottled, + /// The submitted changes to commission change rate are not allowed. + CommissionChangeRateNotAllowed, + /// There is no pending commission to claim. + NoPendingCommission, + /// No commission current has been set. + NoCommissionCurrentSet, /// Pool id currently in use. PoolIdInUse, /// Pool id provided is not correct/usable. @@ -1519,7 +1838,7 @@ pub mod pallet { BondExtraRestricted, } - #[derive(Encode, Decode, PartialEq, TypeInfo, frame_support::PalletError, RuntimeDebug)] + #[derive(Encode, Decode, PartialEq, TypeInfo, PalletError, RuntimeDebug)] pub enum DefensiveError { /// There isn't enough space in the unbond pool. NotEnoughSpaceInUnbondPool, @@ -1571,7 +1890,11 @@ pub mod pallet { let mut reward_pool = RewardPools::::get(pool_id) .defensive_ok_or::>(DefensiveError::RewardPoolNotFound.into())?; // IMPORTANT: reward pool records must be updated with the old points. - reward_pool.update_records(pool_id, bonded_pool.points)?; + reward_pool.update_records( + pool_id, + bonded_pool.points, + bonded_pool.commission.current(), + )?; bonded_pool.try_inc_members()?; let points_issued = bonded_pool.try_bond_funds(&who, amount, BondType::Later)?; @@ -1622,7 +1945,7 @@ pub mod pallet { } /// A bonded member can use this to claim their payout based on the rewards that the pool - /// has accumulated since their last claimed payout (OR since joining if this is there first + /// has accumulated since their last claimed payout (OR since joining if this is their first /// time claiming rewards). The payout will be transferred to the member's account. /// /// The member will earn rewards pro rata based on the members stake vs the sum of the @@ -1684,7 +2007,11 @@ pub mod pallet { // Claim the the payout prior to unbonding. Once the user is unbonding their points no // longer exist in the bonded pool and thus they can no longer claim their payouts. It // is not strictly necessary to claim the rewards, but we do it here for UX. - let _ = reward_pool.update_records(bonded_pool.id, bonded_pool.points)?; + reward_pool.update_records( + bonded_pool.id, + bonded_pool.points, + bonded_pool.commission.current(), + )?; let _ = Self::do_reward_payout(&who, &mut member, &mut bonded_pool, &mut reward_pool)?; let current_era = T::Staking::current_era(); @@ -1731,7 +2058,7 @@ pub mod pallet { }); // Now that we know everything has worked write the items to storage. - SubPoolsStorage::insert(&member.pool_id, sub_pools); + SubPoolsStorage::insert(member.pool_id, sub_pools); Self::put_member_with_pools(&member_account, member, bonded_pool, reward_pool); Ok(()) } @@ -1820,10 +2147,10 @@ pub mod pallet { .iter() .fold(BalanceOf::::zero(), |accumulator, (era, unlocked_points)| { sum_unlocked_points = sum_unlocked_points.saturating_add(*unlocked_points); - if let Some(era_pool) = sub_pools.with_era.get_mut(&era) { + if let Some(era_pool) = sub_pools.with_era.get_mut(era) { let balance_to_unbond = era_pool.dissolve(*unlocked_points); if era_pool.points.is_zero() { - sub_pools.with_era.remove(&era); + sub_pools.with_era.remove(era); } accumulator.saturating_add(balance_to_unbond) } else { @@ -1872,12 +2199,12 @@ pub mod pallet { None } else { bonded_pool.dec_members().put(); - SubPoolsStorage::::insert(&member.pool_id, sub_pools); + SubPoolsStorage::::insert(member.pool_id, sub_pools); Some(T::WeightInfo::withdraw_unbonded_update(num_slashing_spans)) } } else { // we certainly don't need to delete any pools, because no one is being removed. - SubPoolsStorage::::insert(&member.pool_id, sub_pools); + SubPoolsStorage::::insert(member.pool_id, sub_pools); PoolMembers::::insert(&member_account, member); Some(T::WeightInfo::withdraw_unbonded_update(num_slashing_spans)) }; @@ -2002,8 +2329,8 @@ pub mod pallet { /// Set a new metadata for the pool. /// - /// The dispatch origin of this call must be signed by the bouncer, or the root role - /// of the pool. + /// The dispatch origin of this call must be signed by the bouncer, or the root role of the + /// pool. #[pallet::call_index(10)] #[pallet::weight(T::WeightInfo::set_metadata(metadata.len() as u32))] pub fn set_metadata( @@ -2036,6 +2363,7 @@ pub mod pallet { /// * `max_pools` - Set [`MaxPools`]. /// * `max_members` - Set [`MaxPoolMembers`]. /// * `max_members_per_pool` - Set [`MaxPoolMembersPerPool`]. + /// * `global_max_commission` - Set [`GlobalMaxCommission`]. #[pallet::call_index(11)] #[pallet::weight(T::WeightInfo::set_configs())] pub fn set_configs( @@ -2045,6 +2373,7 @@ pub mod pallet { max_pools: ConfigOp, max_members: ConfigOp, max_members_per_pool: ConfigOp, + global_max_commission: ConfigOp, ) -> DispatchResult { ensure_root(origin)?; @@ -2063,6 +2392,7 @@ pub mod pallet { config_op_exp!(MaxPools::, max_pools); config_op_exp!(MaxPoolMembers::, max_members); config_op_exp!(MaxPoolMembersPerPool::, max_members_per_pool); + config_op_exp!(GlobalMaxCommission::, global_max_commission); Ok(()) } @@ -2195,6 +2525,103 @@ pub mod pallet { let signer = ensure_signed(origin)?; Self::do_claim_payout(signer, other) } + + /// Set the commission of a pool. + // + /// Both a commission percentage and a commission payee must be provided in the `current` + /// tuple. Where a `current` of `None` is provided, any current commission will be removed. + /// + /// - If a `None` is supplied to `new_commission`, existing commission will be removed. + #[pallet::call_index(17)] + #[pallet::weight(T::WeightInfo::set_commission())] + pub fn set_commission( + origin: OriginFor, + pool_id: PoolId, + new_commission: Option<(Perbill, T::AccountId)>, + ) -> DispatchResult { + let who = ensure_signed(origin)?; + let mut bonded_pool = BondedPool::::get(pool_id).ok_or(Error::::PoolNotFound)?; + ensure!(bonded_pool.can_manage_commission(&who), Error::::DoesNotHavePermission); + + let mut reward_pool = RewardPools::::get(pool_id) + .defensive_ok_or::>(DefensiveError::RewardPoolNotFound.into())?; + // IMPORTANT: make sure that everything up to this point is using the current commission + // before it updates. Note that `try_update_current` could still fail at this point. + reward_pool.update_records( + pool_id, + bonded_pool.points, + bonded_pool.commission.current(), + )?; + RewardPools::insert(pool_id, reward_pool); + + bonded_pool.commission.try_update_current(&new_commission)?; + bonded_pool.put(); + Self::deposit_event(Event::::PoolCommissionUpdated { + pool_id, + current: new_commission, + }); + Ok(()) + } + + /// Set the maximum commission of a pool. + /// + /// - Initial max can be set to any `Perbill`, and only smaller values thereafter. + /// - Current commission will be lowered in the event it is higher than a new max + /// commission. + #[pallet::call_index(18)] + #[pallet::weight(T::WeightInfo::set_commission_max())] + pub fn set_commission_max( + origin: OriginFor, + pool_id: PoolId, + max_commission: Perbill, + ) -> DispatchResult { + let who = ensure_signed(origin)?; + let mut bonded_pool = BondedPool::::get(pool_id).ok_or(Error::::PoolNotFound)?; + ensure!(bonded_pool.can_manage_commission(&who), Error::::DoesNotHavePermission); + + bonded_pool.commission.try_update_max(pool_id, max_commission)?; + bonded_pool.put(); + + Self::deposit_event(Event::::PoolMaxCommissionUpdated { pool_id, max_commission }); + Ok(()) + } + + /// Set the commission change rate for a pool. + /// + /// Initial change rate is not bounded, whereas subsequent updates can only be more + /// restrictive than the current. + #[pallet::call_index(19)] + #[pallet::weight(T::WeightInfo::set_commission_change_rate())] + pub fn set_commission_change_rate( + origin: OriginFor, + pool_id: PoolId, + change_rate: CommissionChangeRate, + ) -> DispatchResult { + let who = ensure_signed(origin)?; + let mut bonded_pool = BondedPool::::get(pool_id).ok_or(Error::::PoolNotFound)?; + ensure!(bonded_pool.can_manage_commission(&who), Error::::DoesNotHavePermission); + + bonded_pool.commission.try_update_change_rate(change_rate)?; + bonded_pool.put(); + + Self::deposit_event(Event::::PoolCommissionChangeRateUpdated { + pool_id, + change_rate, + }); + Ok(()) + } + + /// Claim pending commission. + /// + /// The dispatch origin of this call must be signed by the `root` role of the pool. Pending + /// commission is paid out and added to total claimed commission`. Total pending commission + /// is reset to zero. the current. + #[pallet::call_index(20)] + #[pallet::weight(0)] + pub fn claim_commission(origin: OriginFor, pool_id: PoolId) -> DispatchResult { + let who = ensure_signed(origin)?; + Self::do_claim_commission(who, pool_id) + } } #[pallet::hooks] @@ -2259,7 +2686,8 @@ impl Pallet { Zero::zero() ); - // This shouldn't fail, but if it does we don't really care + // This shouldn't fail, but if it does we don't really care. Remaining balance can consist + // of unclaimed pending commission, errorneous transfers to the reward account, etc. let reward_pool_remaining = T::Currency::free_balance(&reward_account); let _ = T::Currency::transfer( &reward_account, @@ -2295,7 +2723,7 @@ impl Pallet { fn get_member_with_pools( who: &T::AccountId, ) -> Result<(PoolMember, BondedPool, RewardPool), Error> { - let member = PoolMembers::::get(&who).ok_or(Error::::PoolMemberNotFound)?; + let member = PoolMembers::::get(who).ok_or(Error::::PoolMemberNotFound)?; let bonded_pool = BondedPool::::get(member.pool_id).defensive_ok_or(DefensiveError::PoolNotFound)?; let reward_pool = @@ -2323,8 +2751,8 @@ impl Pallet { current_points: BalanceOf, new_funds: BalanceOf, ) -> BalanceOf { - let u256 = |x| T::BalanceToU256::convert(x); - let balance = |x| T::U256ToBalance::convert(x); + let u256 = T::BalanceToU256::convert; + let balance = T::U256ToBalance::convert; match (current_balance.is_zero(), current_points.is_zero()) { (_, true) => new_funds.saturating_mul(POINTS_TO_BALANCE_INIT_RATIO.into()), (true, false) => { @@ -2351,8 +2779,8 @@ impl Pallet { current_points: BalanceOf, points: BalanceOf, ) -> BalanceOf { - let u256 = |x| T::BalanceToU256::convert(x); - let balance = |x| T::U256ToBalance::convert(x); + let u256 = T::BalanceToU256::convert; + let balance = T::U256ToBalance::convert; if current_balance.is_zero() || current_points.is_zero() || points.is_zero() { // There is nothing to unbond return Zero::zero() @@ -2378,10 +2806,15 @@ impl Pallet { // a member who has no skin in the game anymore cannot claim any rewards. ensure!(!member.active_points().is_zero(), Error::::FullyUnbonding); - let current_reward_counter = - reward_pool.current_reward_counter(bonded_pool.id, bonded_pool.points)?; - let pending_rewards = member.pending_rewards(current_reward_counter)?; + let (current_reward_counter, _) = reward_pool.current_reward_counter( + bonded_pool.id, + bonded_pool.points, + bonded_pool.commission.current(), + )?; + // Determine the pending rewards. In scenarios where commission is 100%, `pending_rewards` + // will be zero. + let pending_rewards = member.pending_rewards(current_reward_counter)?; if pending_rewards.is_zero() { return Ok(pending_rewards) } @@ -2390,14 +2823,13 @@ impl Pallet { member.last_recorded_reward_counter = current_reward_counter; reward_pool.register_claimed_reward(pending_rewards); - // Transfer payout to the member. T::Currency::transfer( &bonded_pool.reward_account(), - &member_account, + member_account, pending_rewards, // defensive: the depositor has put existential deposit into the pool and it stays // untouched, reward account shall not die. - ExistenceRequirement::AllowDeath, + ExistenceRequirement::KeepAlive, )?; Self::deposit_event(Event::::PaidOut { @@ -2462,6 +2894,8 @@ impl Pallet { last_recorded_reward_counter: Zero::zero(), last_recorded_total_payouts: Zero::zero(), total_rewards_claimed: Zero::zero(), + total_commission_pending: Zero::zero(), + total_commission_claimed: Zero::zero(), }, ); ReversePoolIdLookup::::insert(bonded_pool.bonded_account(), pool_id); @@ -2496,7 +2930,11 @@ impl Pallet { // payout related stuff: we must claim the payouts, and updated recorded payout data // before updating the bonded pool points, similar to that of `join` transaction. - reward_pool.update_records(bonded_pool.id, bonded_pool.points)?; + reward_pool.update_records( + bonded_pool.id, + bonded_pool.points, + bonded_pool.commission.current(), + )?; let claimed = Self::do_reward_payout(&who, &mut member, &mut bonded_pool, &mut reward_pool)?; @@ -2522,6 +2960,51 @@ impl Pallet { Ok(()) } + fn do_claim_commission(who: T::AccountId, pool_id: PoolId) -> DispatchResult { + let bonded_pool = BondedPool::::get(pool_id).ok_or(Error::::PoolNotFound)?; + ensure!(bonded_pool.can_manage_commission(&who), Error::::DoesNotHavePermission); + + let mut reward_pool = RewardPools::::get(pool_id) + .defensive_ok_or::>(DefensiveError::RewardPoolNotFound.into())?; + + // IMPORTANT: make sure that any newly pending commission not yet processed is added to + // `total_commission_pending`. + reward_pool.update_records( + pool_id, + bonded_pool.points, + bonded_pool.commission.current(), + )?; + + let commission = reward_pool.total_commission_pending; + ensure!(!commission.is_zero(), Error::::NoPendingCommission); + + let payee = bonded_pool + .commission + .current + .as_ref() + .map(|(_, p)| p.clone()) + .ok_or(Error::::NoCommissionCurrentSet)?; + + // Payout claimed commission. + T::Currency::transfer( + &bonded_pool.reward_account(), + &payee, + commission, + ExistenceRequirement::KeepAlive, + )?; + + // Add pending commission to total claimed counter. + reward_pool.total_commission_claimed = + reward_pool.total_commission_claimed.saturating_add(commission); + // Reset total pending commission counter to zero. + reward_pool.total_commission_pending = Zero::zero(); + // Commit reward pool updates + RewardPools::::insert(pool_id, reward_pool); + + Self::deposit_event(Event::::PoolCommissionClaimed { pool_id, commission }); + Ok(()) + } + fn do_claim_payout(signer: T::AccountId, who: T::AccountId) -> DispatchResult { if signer != who { ensure!( @@ -2608,16 +3091,18 @@ impl Pallet { let mut all_members = 0u32; PoolMembers::::iter().for_each(|(_, d)| { let bonded_pool = BondedPools::::get(d.pool_id).unwrap(); - assert!(!d.total_points().is_zero(), "no member should have zero points: {:?}", d); + assert!(!d.total_points().is_zero(), "no member should have zero points: {d:?}"); *pools_members.entry(d.pool_id).or_default() += 1; all_members += 1; let reward_pool = RewardPools::::get(d.pool_id).unwrap(); if !bonded_pool.points.is_zero() { - let current_rc = - reward_pool.current_reward_counter(d.pool_id, bonded_pool.points).unwrap(); - *pools_members_pending_rewards.entry(d.pool_id).or_default() += - d.pending_rewards(current_rc).unwrap(); + let commission = bonded_pool.commission.current(); + let (current_rc, _) = reward_pool + .current_reward_counter(d.pool_id, bonded_pool.points, commission) + .unwrap(); + let pending_rewards = d.pending_rewards(current_rc).unwrap(); + *pools_members_pending_rewards.entry(d.pool_id).or_default() += pending_rewards; } // else this pool has been heavily slashed and cannot have any rewards anymore. }); @@ -2633,14 +3118,14 @@ impl Pallet { ); assert!( RewardPool::::current_balance(id) >= - pools_members_pending_rewards.get(&id).map(|x| *x).unwrap_or_default() + pools_members_pending_rewards.get(&id).copied().unwrap_or_default() ) }); BondedPools::::iter().for_each(|(id, inner)| { let bonded_pool = BondedPool { id, inner }; assert_eq!( - pools_members.get(&id).map(|x| *x).unwrap_or_default(), + pools_members.get(&id).copied().unwrap_or_default(), bonded_pool.member_counter ); assert!(MaxPoolMembersPerPool::::get() @@ -2706,8 +3191,9 @@ impl Pallet { if let Some((reward_pool, bonded_pool)) = RewardPools::::get(pool_member.pool_id) .zip(BondedPools::::get(pool_member.pool_id)) { - let current_reward_counter = reward_pool - .current_reward_counter(pool_member.pool_id, bonded_pool.points) + let commission = bonded_pool.commission.current(); + let (current_reward_counter, _) = reward_pool + .current_reward_counter(pool_member.pool_id, bonded_pool.points, commission) .ok()?; return pool_member.pending_rewards(current_reward_counter).ok() } diff --git a/frame/nomination-pools/src/migration.rs b/frame/nomination-pools/src/migration.rs index a16963269..f6f166d7f 100644 --- a/frame/nomination-pools/src/migration.rs +++ b/frame/nomination-pools/src/migration.rs @@ -52,9 +52,12 @@ pub mod v1 { impl OldBondedPoolInner { fn migrate_to_v1(self) -> BondedPoolInner { + // Note: `commission` field not introduced to `BondedPoolInner` until + // migration 4. BondedPoolInner { - member_counter: self.member_counter, points: self.points, + commission: Commission::default(), + member_counter: self.member_counter, state: self.state, roles: self.roles.migrate_to_v1(), } @@ -307,6 +310,8 @@ pub mod v2 { last_recorded_reward_counter: Zero::zero(), last_recorded_total_payouts: Zero::zero(), total_rewards_claimed: Zero::zero(), + total_commission_claimed: Zero::zero(), + total_commission_pending: Zero::zero(), }) }, ); @@ -449,3 +454,97 @@ pub mod v3 { } } } + +pub mod v4 { + use super::*; + + #[derive(Decode)] + pub struct OldBondedPoolInner { + pub points: BalanceOf, + pub state: PoolState, + pub member_counter: u32, + pub roles: PoolRoles, + } + + impl OldBondedPoolInner { + fn migrate_to_v4(self) -> BondedPoolInner { + BondedPoolInner { + commission: Commission::default(), + member_counter: self.member_counter, + points: self.points, + state: self.state, + roles: self.roles, + } + } + } + + /// This migration adds a `commission` field to every `BondedPoolInner`, if + /// any. + pub struct MigrateToV4(sp_std::marker::PhantomData<(T, U)>); + impl> OnRuntimeUpgrade for MigrateToV4 { + fn on_runtime_upgrade() -> Weight { + let current = Pallet::::current_storage_version(); + let onchain = Pallet::::on_chain_storage_version(); + + log!( + info, + "Running migration with current storage version {:?} / onchain {:?}", + current, + onchain + ); + + if current == 4 && onchain == 3 { + let initial_global_max_commission = U::get(); + GlobalMaxCommission::::set(Some(initial_global_max_commission)); + log!( + info, + "Set initial global max commission to {:?}.", + initial_global_max_commission + ); + + let mut translated = 0u64; + BondedPools::::translate::, _>(|_key, old_value| { + translated.saturating_inc(); + Some(old_value.migrate_to_v4()) + }); + + current.put::>(); + log!(info, "Upgraded {} pools, storage to version {:?}", translated, current); + + // reads: translated + onchain version. + // writes: translated + current.put + initial global commission. + T::DbWeight::get().reads_writes(translated + 1, translated + 2) + } else { + log!(info, "Migration did not execute. This probably should be removed"); + T::DbWeight::get().reads(1) + } + } + + #[cfg(feature = "try-runtime")] + fn pre_upgrade() -> Result, &'static str> { + ensure!( + Pallet::::current_storage_version() > Pallet::::on_chain_storage_version(), + "the on_chain version is equal or more than the current one" + ); + Ok(Vec::new()) + } + + #[cfg(feature = "try-runtime")] + fn post_upgrade(_: Vec) -> Result<(), &'static str> { + // ensure all BondedPools items now contain an `inner.commission: Commission` field. + ensure!( + BondedPools::::iter().all(|(_, inner)| inner.commission.current.is_none() && + inner.commission.max.is_none() && + inner.commission.change_rate.is_none() && + inner.commission.throttle_from.is_none()), + "a commission value has been incorrectly set" + ); + ensure!( + GlobalMaxCommission::::get() == Some(U::get()), + "global maximum commission error" + ); + ensure!(Pallet::::on_chain_storage_version() == 4, "wrong storage version"); + Ok(()) + } + } +} diff --git a/frame/nomination-pools/src/mock.rs b/frame/nomination-pools/src/mock.rs index e8924c0b9..c6b094f0a 100644 --- a/frame/nomination-pools/src/mock.rs +++ b/frame/nomination-pools/src/mock.rs @@ -118,8 +118,8 @@ impl sp_staking::StakingInterface for StakingMock { fn stake(who: &Self::AccountId) -> Result, DispatchError> { match ( - UnbondingBalanceMap::get().get(who).map(|v| *v), - BondedBalanceMap::get().get(who).map(|v| *v), + UnbondingBalanceMap::get().get(who).copied(), + BondedBalanceMap::get().get(who).copied(), ) { (None, None) => Err(DispatchError::Other("balance not found")), (Some(v), None) => Ok(Stake { total: v, active: 0, stash: *who }), @@ -251,11 +251,17 @@ pub struct ExtBuilder { members: Vec<(AccountId, Balance)>, max_members: Option, max_members_per_pool: Option, + global_max_commission: Option, } impl Default for ExtBuilder { fn default() -> Self { - Self { members: Default::default(), max_members: Some(4), max_members_per_pool: Some(3) } + Self { + members: Default::default(), + max_members: Some(4), + max_members_per_pool: Some(3), + global_max_commission: Some(Perbill::from_percent(90)), + } } } @@ -297,6 +303,11 @@ impl ExtBuilder { self } + pub fn global_max_commission(mut self, commission: Option) -> Self { + self.global_max_commission = commission; + self + } + pub fn build(self) -> sp_io::TestExternalities { sp_tracing::try_init_simple(); let mut storage = @@ -308,6 +319,7 @@ impl ExtBuilder { max_pools: Some(2), max_members_per_pool: self.max_members_per_pool, max_members: self.max_members, + global_max_commission: self.global_max_commission, } .assimilate_storage(&mut storage); @@ -332,7 +344,7 @@ impl ExtBuilder { ext } - pub fn build_and_execute(self, test: impl FnOnce() -> ()) { + pub fn build_and_execute(self, test: impl FnOnce()) { self.build().execute_with(|| { test(); Pools::do_try_state(CheckLevel::get()).unwrap(); @@ -354,6 +366,23 @@ parameter_types! { storage BalancesEvents: u32 = 0; } +/// Helper to run a specified amount of blocks. +pub fn run_blocks(n: u64) { + let current_block = System::block_number(); + run_to_block(n + current_block); +} + +/// Helper to run to a specific block. +pub fn run_to_block(n: u64) { + let current_block = System::block_number(); + assert!(n > current_block); + while System::block_number() < n { + Pools::on_finalize(System::block_number()); + System::set_block_number(System::block_number() + 1); + Pools::on_initialize(System::block_number()); + } +} + /// All events of this pallet. pub fn pool_events_since_last_call() -> Vec> { let events = System::events() @@ -380,7 +409,7 @@ pub fn balances_events_since_last_call() -> Vec> /// Same as `fully_unbond`, in permissioned setting. pub fn fully_unbond_permissioned(member: AccountId) -> DispatchResult { - let points = PoolMembers::::get(&member) + let points = PoolMembers::::get(member) .map(|d| d.active_points()) .unwrap_or_default(); Pools::unbond(RuntimeOrigin::signed(member), member, points) diff --git a/frame/nomination-pools/src/tests.rs b/frame/nomination-pools/src/tests.rs index 33eee0194..a4fda2e5d 100644 --- a/frame/nomination-pools/src/tests.rs +++ b/frame/nomination-pools/src/tests.rs @@ -3,23 +3,23 @@ // Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at +// Licensed under the Apache License, Version 2.0 (the "License"); you may not +// use this file except in compliance with the License. You may obtain a copy of +// the License at // -// http://www.apache.org/licenses/LICENSE-2.0 +// http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. +// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +// License for the specific language governing permissions and limitations under +// the License. use super::*; use crate::{mock::*, Event}; use frame_support::{assert_err, assert_noop, assert_ok, assert_storage_noop, bounded_btree_map}; use pallet_balances::Event as BEvent; -use sp_runtime::traits::Dispatchable; +use sp_runtime::{traits::Dispatchable, FixedU128}; macro_rules! unbonding_pools_with_era { ($($k:expr => $v:expr),* $(,)?) => {{ @@ -55,10 +55,11 @@ fn test_setup_works() { BondedPool:: { id: last_pool, inner: BondedPoolInner { - state: PoolState::Open, - points: 10, + commission: Commission::default(), member_counter: 1, - roles: DEFAULT_ROLES + points: 10, + roles: DEFAULT_ROLES, + state: PoolState::Open, }, } ); @@ -67,7 +68,9 @@ fn test_setup_works() { RewardPool:: { last_recorded_reward_counter: Zero::zero(), last_recorded_total_payouts: 0, - total_rewards_claimed: 0 + total_rewards_claimed: 0, + total_commission_claimed: 0, + total_commission_pending: 0, } ); assert_eq!( @@ -98,10 +101,11 @@ mod bonded_pool { let mut bonded_pool = BondedPool:: { id: 123123, inner: BondedPoolInner { - state: PoolState::Open, - points: 100, + commission: Commission::default(), member_counter: 1, + points: 100, roles: DEFAULT_ROLES, + state: PoolState::Open, }, }; @@ -153,10 +157,11 @@ mod bonded_pool { let mut bonded_pool = BondedPool:: { id: 123123, inner: BondedPoolInner { - state: PoolState::Open, - points: 100, + commission: Commission::default(), member_counter: 1, + points: 100, roles: DEFAULT_ROLES, + state: PoolState::Open, }, }; @@ -241,10 +246,11 @@ mod bonded_pool { let pool = BondedPool:: { id: 123, inner: BondedPoolInner { - state: PoolState::Open, - points: 100, + commission: Commission::default(), member_counter: 1, + points: 100, roles: DEFAULT_ROLES, + state: PoolState::Open, }, }; @@ -258,7 +264,7 @@ mod bonded_pool { // Simulate a slashed pool at `MaxPointsToBalance` + 1 slashed pool StakingMock::set_bonded_balance( pool.bonded_account(), - max_points_to_balance.saturating_add(1).into(), + max_points_to_balance.saturating_add(1), ); assert_ok!(pool.ok_to_join()); @@ -470,16 +476,17 @@ mod join { let bonded = |points, member_counter| BondedPool:: { id: 1, inner: BondedPoolInner { - state: PoolState::Open, - points, + commission: Commission::default(), member_counter, + points, roles: DEFAULT_ROLES, + state: PoolState::Open, }, }; ExtBuilder::default().with_check(0).build_and_execute(|| { // Given Balances::make_free_balance_be(&11, ExistentialDeposit::get() + 2); - assert!(!PoolMembers::::contains_key(&11)); + assert!(!PoolMembers::::contains_key(11)); // When assert_ok!(Pools::join(RuntimeOrigin::signed(11), 2, 1)); @@ -496,7 +503,7 @@ mod join { ); assert_eq!( - PoolMembers::::get(&11).unwrap(), + PoolMembers::::get(11).unwrap(), PoolMember:: { pool_id: 1, points: 2, ..Default::default() } ); assert_eq!(BondedPool::::get(1).unwrap(), bonded(12, 2)); @@ -507,7 +514,7 @@ mod join { // And Balances::make_free_balance_be(&12, ExistentialDeposit::get() + 12); - assert!(!PoolMembers::::contains_key(&12)); + assert!(!PoolMembers::::contains_key(12)); // When assert_ok!(Pools::join(RuntimeOrigin::signed(12), 12, 1)); @@ -519,7 +526,7 @@ mod join { ); assert_eq!( - PoolMembers::::get(&12).unwrap(), + PoolMembers::::get(12).unwrap(), PoolMember:: { pool_id: 1, points: 24, ..Default::default() } ); assert_eq!(BondedPool::::get(1).unwrap(), bonded(12 + 24, 3)); @@ -530,7 +537,7 @@ mod join { fn join_errors_correctly() { ExtBuilder::default().with_check(0).build_and_execute(|| { // 10 is already part of the default pool created. - assert_eq!(PoolMembers::::get(&10).unwrap().pool_id, 1); + assert_eq!(PoolMembers::::get(10).unwrap().pool_id, 1); assert_noop!( Pools::join(RuntimeOrigin::signed(10), 420, 123), @@ -553,10 +560,11 @@ mod join { BondedPool:: { id: 123, inner: BondedPoolInner { + commission: Commission::default(), member_counter: 1, - state: PoolState::Open, points: 100, roles: DEFAULT_ROLES, + state: PoolState::Open, }, } .put(); @@ -622,10 +630,11 @@ mod join { BondedPool:: { id: 123, inner: BondedPoolInner { - state: PoolState::Open, - points: 100, + commission: Commission::default(), member_counter: 1, + points: 100, roles: DEFAULT_ROLES, + state: PoolState::Open, }, } .put(); @@ -722,6 +731,8 @@ mod claim_payout { last_recorded_reward_counter: last_recorded_reward_counter.into(), last_recorded_total_payouts, total_rewards_claimed, + total_commission_claimed: 0, + total_commission_pending: 0, } } @@ -754,7 +765,7 @@ mod claim_payout { assert_eq!(PoolMembers::::get(10).unwrap(), del(10, 1)); // pool's 'last_recorded_reward_counter' and 'last_recorded_total_payouts' don't // really change unless if someone bonds/unbonds. - assert_eq!(RewardPools::::get(&1).unwrap(), rew(0, 0, 10)); + assert_eq!(RewardPools::::get(1).unwrap(), rew(0, 0, 10)); assert_eq!(Balances::free_balance(&10), 10); assert_eq!(Balances::free_balance(&default_reward_account()), ed + 90); @@ -767,7 +778,7 @@ mod claim_payout { vec![Event::PaidOut { member: 40, pool_id: 1, payout: 40 }] ); assert_eq!(PoolMembers::::get(40).unwrap(), del(40, 1)); - assert_eq!(RewardPools::::get(&1).unwrap(), rew(0, 0, 50)); + assert_eq!(RewardPools::::get(1).unwrap(), rew(0, 0, 50)); assert_eq!(Balances::free_balance(&40), 40); assert_eq!(Balances::free_balance(&default_reward_account()), ed + 50); @@ -780,9 +791,9 @@ mod claim_payout { vec![Event::PaidOut { member: 50, pool_id: 1, payout: 50 }] ); assert_eq!(PoolMembers::::get(50).unwrap(), del(50, 1)); - assert_eq!(RewardPools::::get(&1).unwrap(), rew(0, 0, 100)); + assert_eq!(RewardPools::::get(1).unwrap(), rew(0, 0, 100)); assert_eq!(Balances::free_balance(&50), 50); - assert_eq!(Balances::free_balance(&default_reward_account()), ed + 0); + assert_eq!(Balances::free_balance(&default_reward_account()), ed); // Given the reward pool has some new rewards assert_ok!(Balances::mutate_account(&default_reward_account(), |a| a.free += 50)); @@ -796,7 +807,7 @@ mod claim_payout { vec![Event::PaidOut { member: 10, pool_id: 1, payout: 5 }] ); assert_eq!(PoolMembers::::get(10).unwrap(), del_float(10, 1.5)); - assert_eq!(RewardPools::::get(&1).unwrap(), rew(0, 0, 105)); + assert_eq!(RewardPools::::get(1).unwrap(), rew(0, 0, 105)); assert_eq!(Balances::free_balance(&10), 10 + 5); assert_eq!(Balances::free_balance(&default_reward_account()), ed + 45); @@ -809,7 +820,7 @@ mod claim_payout { vec![Event::PaidOut { member: 40, pool_id: 1, payout: 20 }] ); assert_eq!(PoolMembers::::get(40).unwrap(), del_float(40, 1.5)); - assert_eq!(RewardPools::::get(&1).unwrap(), rew(0, 0, 125)); + assert_eq!(RewardPools::::get(1).unwrap(), rew(0, 0, 125)); assert_eq!(Balances::free_balance(&40), 40 + 20); assert_eq!(Balances::free_balance(&default_reward_account()), ed + 25); @@ -826,7 +837,7 @@ mod claim_payout { vec![Event::PaidOut { member: 50, pool_id: 1, payout: 50 }] ); assert_eq!(PoolMembers::::get(50).unwrap(), del_float(50, 2.0)); - assert_eq!(RewardPools::::get(&1).unwrap(), rew(0, 0, 175)); + assert_eq!(RewardPools::::get(1).unwrap(), rew(0, 0, 175)); assert_eq!(Balances::free_balance(&50), 50 + 50); assert_eq!(Balances::free_balance(&default_reward_account()), ed + 25); @@ -839,7 +850,7 @@ mod claim_payout { vec![Event::PaidOut { member: 10, pool_id: 1, payout: 5 }] ); assert_eq!(PoolMembers::::get(10).unwrap(), del(10, 2)); - assert_eq!(RewardPools::::get(&1).unwrap(), rew(0, 0, 180)); + assert_eq!(RewardPools::::get(1).unwrap(), rew(0, 0, 180)); assert_eq!(Balances::free_balance(&10), 15 + 5); assert_eq!(Balances::free_balance(&default_reward_account()), ed + 20); @@ -858,7 +869,7 @@ mod claim_payout { // We expect a payout of 40 assert_eq!(PoolMembers::::get(10).unwrap(), del(10, 6)); - assert_eq!(RewardPools::::get(&1).unwrap(), rew(0, 0, 220)); + assert_eq!(RewardPools::::get(1).unwrap(), rew(0, 0, 220)); assert_eq!(Balances::free_balance(&10), 20 + 40); assert_eq!(Balances::free_balance(&default_reward_account()), ed + 380); @@ -875,7 +886,7 @@ mod claim_payout { vec![Event::PaidOut { member: 10, pool_id: 1, payout: 2 }] ); assert_eq!(PoolMembers::::get(10).unwrap(), del_float(10, 6.2)); - assert_eq!(RewardPools::::get(&1).unwrap(), rew(0, 0, 222)); + assert_eq!(RewardPools::::get(1).unwrap(), rew(0, 0, 222)); assert_eq!(Balances::free_balance(&10), 60 + 2); assert_eq!(Balances::free_balance(&default_reward_account()), ed + 398); @@ -888,7 +899,7 @@ mod claim_payout { vec![Event::PaidOut { member: 40, pool_id: 1, payout: 188 }] ); assert_eq!(PoolMembers::::get(40).unwrap(), del_float(40, 6.2)); - assert_eq!(RewardPools::::get(&1).unwrap(), rew(0, 0, 410)); + assert_eq!(RewardPools::::get(1).unwrap(), rew(0, 0, 410)); assert_eq!(Balances::free_balance(&40), 60 + 188); assert_eq!(Balances::free_balance(&default_reward_account()), ed + 210); @@ -901,9 +912,9 @@ mod claim_payout { vec![Event::PaidOut { member: 50, pool_id: 1, payout: 210 }] ); assert_eq!(PoolMembers::::get(50).unwrap(), del_float(50, 6.2)); - assert_eq!(RewardPools::::get(&1).unwrap(), rew(0, 0, 620)); + assert_eq!(RewardPools::::get(1).unwrap(), rew(0, 0, 620)); assert_eq!(Balances::free_balance(&50), 100 + 210); - assert_eq!(Balances::free_balance(&default_reward_account()), ed + 0); + assert_eq!(Balances::free_balance(&default_reward_account()), ed); }); } @@ -930,6 +941,56 @@ mod claim_payout { }); } + #[test] + fn claim_payout_bounds_commission_above_global() { + ExtBuilder::default().build_and_execute(|| { + let (mut member, bonded_pool, mut reward_pool) = + Pools::get_member_with_pools(&10).unwrap(); + + // top up commission payee account to existential deposit + let _ = Balances::deposit_creating(&2, 5); + + // Set a commission pool 1 to 75%, with a payee set to `2` + assert_ok!(Pools::set_commission( + RuntimeOrigin::signed(900), + bonded_pool.id, + Some((Perbill::from_percent(75), 2)), + )); + + // re-introduce the global maximum to 50% - 25% lower than the current commission of the + // pool. + GlobalMaxCommission::::set(Some(Perbill::from_percent(50))); + + assert_eq!( + pool_events_since_last_call(), + vec![ + Event::Created { depositor: 10, pool_id: 1 }, + Event::Bonded { member: 10, pool_id: 1, bonded: 10, joined: true }, + Event::PoolCommissionUpdated { + pool_id: 1, + current: Some((Perbill::from_percent(75), 2)) + } + ] + ); + + // The pool earns 10 points + assert_ok!(Balances::mutate_account(&default_reward_account(), |a| a.free += 10)); + + assert_ok!(Pools::do_reward_payout( + &10, + &mut member, + &mut BondedPool::::get(1).unwrap(), + &mut reward_pool + )); + + // commission applied is 50%, not 75%. Has been bounded by `GlobalMaxCommission`. + assert_eq!( + pool_events_since_last_call(), + vec![Event::PaidOut { member: 10, pool_id: 1, payout: 5 },] + ); + }) + } + #[test] fn do_reward_payout_works_with_a_pool_of_1() { let del = |last_recorded_reward_counter| del_float(10, last_recorded_reward_counter); @@ -987,7 +1048,7 @@ mod claim_payout { assert_eq!(member, del(1.5)); // Given the pool has earned no new rewards - Balances::make_free_balance_be(&default_reward_account(), ed + 0); + Balances::make_free_balance_be(&default_reward_account(), ed); // When let payout = @@ -2432,7 +2493,7 @@ mod unbond { assert_eq!( SubPoolsStorage::::get(1).unwrap().with_era, - unbonding_pools_with_era! { 0 + 3 => UnbondPool:: { points: 10, balance: 10 }} + unbonding_pools_with_era! { 3 => UnbondPool:: { points: 10, balance: 10 }} ); assert_eq!( @@ -2440,10 +2501,11 @@ mod unbond { BondedPool { id: 1, inner: BondedPoolInner { - state: PoolState::Destroying, - points: 0, + commission: Commission::default(), member_counter: 1, + points: 0, roles: DEFAULT_ROLES, + state: PoolState::Destroying, } } ); @@ -2469,17 +2531,18 @@ mod unbond { // Then assert_eq!( SubPoolsStorage::::get(1).unwrap().with_era, - unbonding_pools_with_era! { 0 + 3 => UnbondPool { points: 6, balance: 6 }} + unbonding_pools_with_era! { 3 => UnbondPool { points: 6, balance: 6 }} ); assert_eq!( BondedPool::::get(1).unwrap(), BondedPool { id: 1, inner: BondedPoolInner { - state: PoolState::Open, - points: 560, + commission: Commission::default(), member_counter: 3, + points: 560, roles: DEFAULT_ROLES, + state: PoolState::Open, } } ); @@ -2498,7 +2561,7 @@ mod unbond { assert_eq!(StakingMock::active_stake(&default_bonded_account()).unwrap(), 94); assert_eq!( PoolMembers::::get(40).unwrap().unbonding_eras, - member_unbonding_eras!(0 + 3 => 6) + member_unbonding_eras!(3 => 6) ); assert_eq!(Balances::free_balance(&40), 40 + 40); // We claim rewards when unbonding @@ -2508,25 +2571,26 @@ mod unbond { // Then assert_eq!( - SubPoolsStorage::::get(&1).unwrap().with_era, - unbonding_pools_with_era! { 0 + 3 => UnbondPool { points: 98, balance: 98 }} + SubPoolsStorage::::get(1).unwrap().with_era, + unbonding_pools_with_era! { 3 => UnbondPool { points: 98, balance: 98 }} ); assert_eq!( BondedPool::::get(1).unwrap(), BondedPool { id: 1, inner: BondedPoolInner { - state: PoolState::Destroying, - points: 10, + commission: Commission::default(), member_counter: 3, - roles: DEFAULT_ROLES + points: 10, + roles: DEFAULT_ROLES, + state: PoolState::Destroying, } } ); assert_eq!(StakingMock::active_stake(&default_bonded_account()).unwrap(), 2); assert_eq!( PoolMembers::::get(550).unwrap().unbonding_eras, - member_unbonding_eras!(0 + 3 => 92) + member_unbonding_eras!(3 => 92) ); assert_eq!(Balances::free_balance(&550), 550 + 550); assert_eq!( @@ -2559,10 +2623,11 @@ mod unbond { BondedPool { id: 1, inner: BondedPoolInner { - state: PoolState::Destroying, - points: 0, + commission: Commission::default(), member_counter: 1, - roles: DEFAULT_ROLES + points: 0, + roles: DEFAULT_ROLES, + state: PoolState::Destroying, } } ); @@ -2593,7 +2658,7 @@ mod unbond { SubPools { no_era: Default::default(), with_era: unbonding_pools_with_era! { - 0 + 3 => UnbondPool { balance: 10, points: 100 }, + 3 => UnbondPool { balance: 10, points: 100 }, 1 + 3 => UnbondPool { balance: 20, points: 20 }, 2 + 3 => UnbondPool { balance: 101, points: 101} }, @@ -2687,10 +2752,11 @@ mod unbond { BondedPool { id: 1, inner: BondedPoolInner { + commission: Commission::default(), + member_counter: 3, + points: 10, // Only 10 points because 200 + 100 was unbonded roles: DEFAULT_ROLES, state: PoolState::Blocked, - points: 10, // Only 10 points because 200 + 100 was unbonded - member_counter: 3, } } ); @@ -2700,7 +2766,7 @@ mod unbond { SubPools { no_era: Default::default(), with_era: unbonding_pools_with_era! { - 0 + 3 => UnbondPool { points: 100 + 200, balance: 100 + 200 } + 3 => UnbondPool { points: 100 + 200, balance: 100 + 200 } }, } ); @@ -2837,10 +2903,11 @@ mod unbond { BondedPool:: { id: 1, inner: BondedPoolInner { - state: PoolState::Open, - points: 10, + commission: Commission::default(), member_counter: 1, + points: 10, roles: DEFAULT_ROLES, + state: PoolState::Open, }, } .put(); @@ -3356,7 +3423,7 @@ mod withdraw_unbonded { assert_ok!(fully_unbond_permissioned(550)); assert_eq!( - SubPoolsStorage::::get(&1).unwrap().with_era, + SubPoolsStorage::::get(1).unwrap().with_era, unbonding_pools_with_era! { 3 => UnbondPool { points: 550 / 2 + 40 / 2, balance: 550 / 2 + 40 / 2 }} ); @@ -3406,7 +3473,7 @@ mod withdraw_unbonded { ); assert_eq!( - SubPoolsStorage::::get(&1).unwrap().with_era, + SubPoolsStorage::::get(1).unwrap().with_era, unbonding_pools_with_era! { 3 => UnbondPool { points: 550 / 2, balance: 550 / 2 }} ); @@ -3425,7 +3492,7 @@ mod withdraw_unbonded { Event::MemberRemoved { pool_id: 1, member: 550 } ] ); - assert!(SubPoolsStorage::::get(&1).unwrap().with_era.is_empty()); + assert!(SubPoolsStorage::::get(1).unwrap().with_era.is_empty()); // now, finally, the depositor can take out its share. unsafe_set_state(1, PoolState::Destroying); @@ -3433,7 +3500,7 @@ mod withdraw_unbonded { // because everyone else has left, the points assert_eq!( - SubPoolsStorage::::get(&1).unwrap().with_era, + SubPoolsStorage::::get(1).unwrap().with_era, unbonding_pools_with_era! { 6 => UnbondPool { points: 5, balance: 5 }} ); @@ -3487,10 +3554,10 @@ mod withdraw_unbonded { assert_eq!( SubPoolsStorage::::get(1).unwrap().with_era, //------------------------------balance decrease is not account for - unbonding_pools_with_era! { 0 + 3 => UnbondPool { points: 10, balance: 10 } } + unbonding_pools_with_era! { 3 => UnbondPool { points: 10, balance: 10 } } ); - CurrentEra::set(0 + 3); + CurrentEra::set(3); // When assert_ok!(Pools::withdraw_unbonded(RuntimeOrigin::signed(10), 10, 0)); @@ -3507,7 +3574,7 @@ mod withdraw_unbonded { // Insert the sub-pool let sub_pools = SubPools { no_era: Default::default(), - with_era: unbonding_pools_with_era! { 0 + 3 => UnbondPool { points: 10, balance: 10 }}, + with_era: unbonding_pools_with_era! { 3 => UnbondPool { points: 10, balance: 10 }}, }; SubPoolsStorage::::insert(1, sub_pools.clone()); @@ -3520,7 +3587,7 @@ mod withdraw_unbonded { PoolMembers::::insert(11, member.clone()); // Simulate calling `unbond` - member.unbonding_eras = member_unbonding_eras!(3 + 0 => 10); + member.unbonding_eras = member_unbonding_eras!(3 => 10); PoolMembers::::insert(11, member.clone()); // We are still in the bonding duration @@ -3530,7 +3597,7 @@ mod withdraw_unbonded { ); // If we error the member does not get removed - assert_eq!(PoolMembers::::get(&11), Some(member)); + assert_eq!(PoolMembers::::get(11), Some(member)); // and the sub pools do not get updated. assert_eq!(SubPoolsStorage::::get(1).unwrap(), sub_pools) }); @@ -3549,10 +3616,11 @@ mod withdraw_unbonded { BondedPool { id: 1, inner: BondedPoolInner { + commission: Commission::default(), + member_counter: 3, points: 10, + roles: DEFAULT_ROLES, state: PoolState::Open, - member_counter: 3, - roles: DEFAULT_ROLES } } ); @@ -3629,10 +3697,11 @@ mod withdraw_unbonded { BondedPool { id: 1, inner: BondedPoolInner { - points: 10, - state: PoolState::Open, + commission: Commission::default(), member_counter: 2, + points: 10, roles: DEFAULT_ROLES, + state: PoolState::Open, } } ); @@ -4225,15 +4294,16 @@ mod create { BondedPool { id: 2, inner: BondedPoolInner { + commission: Commission::default(), points: StakingMock::minimum_nominator_bond(), member_counter: 1, - state: PoolState::Open, roles: PoolRoles { depositor: 11, root: Some(123), nominator: Some(456), bouncer: Some(789) - } + }, + state: PoolState::Open, } } ); @@ -4289,10 +4359,11 @@ mod create { BondedPool:: { id: 2, inner: BondedPoolInner { - state: PoolState::Open, - points: 10, + commission: Commission::default(), member_counter: 1, + points: 10, roles: DEFAULT_ROLES, + state: PoolState::Open, }, } .put(); @@ -4369,7 +4440,7 @@ fn set_claimable_actor_works() { ExtBuilder::default().build_and_execute(|| { // Given Balances::make_free_balance_be(&11, ExistentialDeposit::get() + 2); - assert!(!PoolMembers::::contains_key(&11)); + assert!(!PoolMembers::::contains_key(11)); // When assert_ok!(Pools::join(RuntimeOrigin::signed(11), 2, 1)); @@ -4574,12 +4645,14 @@ mod set_configs { ConfigOp::Set(3u32), ConfigOp::Set(4u32), ConfigOp::Set(5u32), + ConfigOp::Set(Perbill::from_percent(6)) )); assert_eq!(MinJoinBond::::get(), 1); assert_eq!(MinCreateBond::::get(), 2); assert_eq!(MaxPools::::get(), Some(3)); assert_eq!(MaxPoolMembers::::get(), Some(4)); assert_eq!(MaxPoolMembersPerPool::::get(), Some(5)); + assert_eq!(GlobalMaxCommission::::get(), Some(Perbill::from_percent(6))); // Noop does nothing assert_storage_noop!(assert_ok!(Pools::set_configs( @@ -4589,6 +4662,7 @@ mod set_configs { ConfigOp::Noop, ConfigOp::Noop, ConfigOp::Noop, + ConfigOp::Noop, ))); // Removing works @@ -4599,12 +4673,14 @@ mod set_configs { ConfigOp::Remove, ConfigOp::Remove, ConfigOp::Remove, + ConfigOp::Remove, )); assert_eq!(MinJoinBond::::get(), 0); assert_eq!(MinCreateBond::::get(), 0); assert_eq!(MaxPools::::get(), None); assert_eq!(MaxPoolMembers::::get(), None); assert_eq!(MaxPoolMembersPerPool::::get(), None); + assert_eq!(GlobalMaxCommission::::get(), None); }); } } @@ -4949,8 +5025,6 @@ mod update_roles { } mod reward_counter_precision { - use sp_runtime::FixedU128; - use super::*; const DOT: Balance = 10u128.pow(10u32); @@ -4967,10 +5041,12 @@ mod reward_counter_precision { } fn default_pool_reward_counter() -> FixedU128 { - RewardPools::::get(1) + let bonded_pool = BondedPools::::get(1).unwrap(); + RewardPools::::get(1) .unwrap() - .current_reward_counter(1, BondedPools::::get(1).unwrap().points) + .current_reward_counter(1, bonded_pool.points, bonded_pool.commission.current()) .unwrap() + .0 } fn pending_rewards(of: AccountId) -> Option> { @@ -5292,3 +5368,1353 @@ mod reward_counter_precision { }); } } + +mod commission { + use super::*; + + #[test] + fn set_commission_works() { + ExtBuilder::default().build_and_execute(|| { + let pool_id = 1; + let root = 900; + + // Commission can be set by the `root` role. + + // When: + assert_ok!(Pools::set_commission( + RuntimeOrigin::signed(root), + pool_id, + Some((Perbill::from_percent(50), root)) + )); + + // Then: + assert_eq!( + pool_events_since_last_call(), + vec![ + Event::Created { depositor: 10, pool_id }, + Event::Bonded { member: 10, pool_id, bonded: 10, joined: true }, + Event::PoolCommissionUpdated { + pool_id, + current: Some((Perbill::from_percent(50), root)) + }, + ] + ); + + // Commission can be updated only, while keeping the same payee. + + // When: + assert_ok!(Pools::set_commission( + RuntimeOrigin::signed(root), + 1, + Some((Perbill::from_percent(25), root)) + )); + + // Then: + assert_eq!( + pool_events_since_last_call(), + vec![Event::PoolCommissionUpdated { + pool_id, + current: Some((Perbill::from_percent(25), root)) + },] + ); + + // Payee can be updated only, while keeping the same commission. + + // Given: + let payee = 901; + + // When: + assert_ok!(Pools::set_commission( + RuntimeOrigin::signed(root), + pool_id, + Some((Perbill::from_percent(25), payee)) + )); + + // Then: + assert_eq!( + pool_events_since_last_call(), + vec![Event::PoolCommissionUpdated { + pool_id: 1, + current: Some((Perbill::from_percent(25), payee)) + },] + ); + + // Pool earns 80 points and a payout is triggered. + + // Given: + assert_ok!(Balances::mutate_account(&default_reward_account(), |a| a.free += 80)); + assert_eq!( + PoolMembers::::get(10).unwrap(), + PoolMember:: { pool_id, points: 10, ..Default::default() } + ); + + // When: + assert_ok!(Pools::claim_payout(RuntimeOrigin::signed(10))); + + // Then: + assert_eq!( + pool_events_since_last_call(), + vec![Event::PaidOut { member: 10, pool_id, payout: 60 }] + ); + assert_eq!(RewardPool::::current_balance(pool_id), 20); + + // Pending pool commission can be claimed by the root role. + + // When: + assert_ok!(Pools::claim_commission(RuntimeOrigin::signed(root), pool_id)); + + // Then: + assert_eq!(RewardPool::::current_balance(pool_id), 0); + assert_eq!( + pool_events_since_last_call(), + vec![Event::PoolCommissionClaimed { pool_id: 1, commission: 20 }] + ); + + // Commission can be removed from the pool completely. + + // When: + assert_ok!(Pools::set_commission(RuntimeOrigin::signed(root), pool_id, None)); + + // Then: + assert_eq!( + pool_events_since_last_call(), + vec![Event::PoolCommissionUpdated { pool_id, current: None },] + ); + + // Given a pool now has a reward counter history, additional rewards and payouts can be + // made while maintaining a correct ledger of the reward pool. Pool earns 100 points, + // payout is triggered. + // + // Note that the `total_commission_pending` will not be updated until `update_records` + // is next called, which is not done in this test segment.. + + // Given: + assert_ok!(Balances::mutate_account(&default_reward_account(), |a| a.free += 100)); + + // When: + assert_ok!(Pools::claim_payout(RuntimeOrigin::signed(10))); + + // Then: + assert_eq!( + pool_events_since_last_call(), + vec![Event::PaidOut { member: 10, pool_id, payout: 100 },] + ); + assert_eq!( + RewardPools::::get(pool_id).unwrap(), + RewardPool { + last_recorded_reward_counter: FixedU128::from_float(6.0), + last_recorded_total_payouts: 80, + total_rewards_claimed: 160, + total_commission_pending: 0, + total_commission_claimed: 20 + } + ); + + // When set commission is called again, update_records is called and + // `total_commission_pending` is updated, based on the current reward counter and pool + // balance. + // + // Note that commission is now 0%, so it should not come into play with subsequent + // payouts. + + // When: + assert_ok!(Pools::set_commission( + RuntimeOrigin::signed(root), + 1, + Some((Perbill::from_percent(10), root)) + )); + + // Then: + assert_eq!( + pool_events_since_last_call(), + vec![Event::PoolCommissionUpdated { + pool_id, + current: Some((Perbill::from_percent(10), root)) + },] + ); + assert_eq!( + RewardPools::::get(pool_id).unwrap(), + RewardPool { + last_recorded_reward_counter: FixedU128::from_float(16.0), + last_recorded_total_payouts: 180, + total_rewards_claimed: 160, + total_commission_pending: 0, + total_commission_claimed: 20 + } + ); + + // Supplying a 0% commission along with a payee results in a `None` current value. + + // When: + assert_ok!(Pools::set_commission( + RuntimeOrigin::signed(root), + pool_id, + Some((Perbill::from_percent(0), root)) + )); + + // Then: + assert_eq!( + BondedPool::::get(1).unwrap().commission, + Commission { current: None, max: None, change_rate: None, throttle_from: Some(1) } + ); + assert_eq!( + pool_events_since_last_call(), + vec![Event::PoolCommissionUpdated { + pool_id, + current: Some((Perbill::from_percent(0), root)) + },] + ); + + // The payee can be updated even when commission has reached maximum commission. Both + // commission and max commission are set to 10% to test this. + + // Given: + assert_ok!(Pools::set_commission_max( + RuntimeOrigin::signed(root), + pool_id, + Perbill::from_percent(10) + )); + assert_ok!(Pools::set_commission( + RuntimeOrigin::signed(root), + pool_id, + Some((Perbill::from_percent(10), root)) + )); + + // When: + assert_ok!(Pools::set_commission( + RuntimeOrigin::signed(root), + pool_id, + Some((Perbill::from_percent(10), payee)) + )); + + // Then: + assert_eq!( + pool_events_since_last_call(), + vec![ + Event::PoolMaxCommissionUpdated { + pool_id, + max_commission: Perbill::from_percent(10) + }, + Event::PoolCommissionUpdated { + pool_id, + current: Some((Perbill::from_percent(10), root)) + }, + Event::PoolCommissionUpdated { + pool_id, + current: Some((Perbill::from_percent(10), payee)) + } + ] + ); + }); + } + + #[test] + fn commission_reward_counter_works_one_member() { + ExtBuilder::default().build_and_execute(|| { + let pool_id = 1; + let root = 900; + let member = 10; + + // Set the pool commission to 10% to test commission shares. Pool is topped up 40 points + // and `member` immediately claims their pending rewards. Reward pooll should still have + // 10% share. + + // Given: + assert_ok!(Pools::set_commission( + RuntimeOrigin::signed(root), + 1, + Some((Perbill::from_percent(10), root)), + )); + assert_ok!(Balances::mutate_account(&default_reward_account(), |a| a.free += 40)); + + // When: + assert_ok!(Pools::claim_payout(RuntimeOrigin::signed(10))); + + // Then: + assert_eq!(RewardPool::::current_balance(pool_id), 4); + + // Set pool commission to 20% and repeat the same process. + + // When: + assert_ok!(Pools::set_commission( + RuntimeOrigin::signed(root), + 1, + Some((Perbill::from_percent(20), root)), + )); + + // Then: + assert_eq!( + RewardPools::::get(pool_id).unwrap(), + RewardPool { + last_recorded_reward_counter: FixedU128::from_float(3.6), + last_recorded_total_payouts: 40, + total_rewards_claimed: 36, + total_commission_pending: 4, + total_commission_claimed: 0 + } + ); + + // The current reward counter should yield the correct pending rewards of zero. + + // Given: + let (current_reward_counter, _) = RewardPools::::get(pool_id) + .unwrap() + .current_reward_counter( + pool_id, + BondedPools::::get(pool_id).unwrap().points, + Perbill::from_percent(20), + ) + .unwrap(); + + // Then: + assert_eq!( + PoolMembers::::get(member) + .unwrap() + .pending_rewards(current_reward_counter) + .unwrap(), + 0 + ); + + assert_eq!( + pool_events_since_last_call(), + vec![ + Event::Created { depositor: 10, pool_id: 1 }, + Event::Bonded { member: 10, pool_id: 1, bonded: 10, joined: true }, + Event::PoolCommissionUpdated { + pool_id: 1, + current: Some((Perbill::from_percent(10), 900)) + }, + Event::PaidOut { member: 10, pool_id: 1, payout: 36 }, + Event::PoolCommissionUpdated { + pool_id: 1, + current: Some((Perbill::from_percent(20), 900)) + } + ] + ); + }) + } + + #[test] + fn set_commission_handles_errors() { + ExtBuilder::default().build_and_execute(|| { + assert_eq!( + pool_events_since_last_call(), + vec![ + Event::Created { depositor: 10, pool_id: 1 }, + Event::Bonded { member: 10, pool_id: 1, bonded: 10, joined: true }, + ] + ); + + // Provided pool does not exist. + assert_noop!( + Pools::set_commission( + RuntimeOrigin::signed(900), + 9999, + Some((Perbill::from_percent(1), 900)), + ), + Error::::PoolNotFound + ); + + // Sender does not have permission to set commission. + assert_noop!( + Pools::set_commission( + RuntimeOrigin::signed(1), + 1, + Some((Perbill::from_percent(5), 900)), + ), + Error::::DoesNotHavePermission + ); + + // Commission increases will be throttled if outside of change_rate allowance. + // Commission is set to 5%. + // Change rate is set to 1% max increase, 2 block delay. + + // When: + assert_ok!(Pools::set_commission( + RuntimeOrigin::signed(900), + 1, + Some((Perbill::from_percent(5), 900)), + )); + assert_ok!(Pools::set_commission_change_rate( + RuntimeOrigin::signed(900), + 1, + CommissionChangeRate { max_increase: Perbill::from_percent(1), min_delay: 2_u64 } + )); + assert_eq!( + BondedPool::::get(1).unwrap().commission, + Commission { + current: Some((Perbill::from_percent(5), 900)), + max: None, + change_rate: Some(CommissionChangeRate { + max_increase: Perbill::from_percent(1), + min_delay: 2_u64 + }), + throttle_from: Some(1_u64), + } + ); + assert_eq!( + pool_events_since_last_call(), + vec![ + Event::PoolCommissionUpdated { + pool_id: 1, + current: Some((Perbill::from_percent(5), 900)) + }, + Event::PoolCommissionChangeRateUpdated { + pool_id: 1, + change_rate: CommissionChangeRate { + max_increase: Perbill::from_percent(1), + min_delay: 2 + } + } + ] + ); + + // Now try to increase commission to 10% (5% increase). This should be throttled. + // Then: + assert_noop!( + Pools::set_commission( + RuntimeOrigin::signed(900), + 1, + Some((Perbill::from_percent(10), 900)) + ), + Error::::CommissionChangeThrottled + ); + + run_blocks(2); + + // Increase commission by 1% and provide an initial payee. This should succeed and set + // the `throttle_from` field. + + // When: + assert_ok!(Pools::set_commission( + RuntimeOrigin::signed(900), + 1, + Some((Perbill::from_percent(6), 900)) + )); + assert_eq!( + BondedPool::::get(1).unwrap().commission, + Commission { + current: Some((Perbill::from_percent(6), 900)), + max: None, + change_rate: Some(CommissionChangeRate { + max_increase: Perbill::from_percent(1), + min_delay: 2_u64 + }), + throttle_from: Some(3_u64), + } + ); + assert_eq!( + pool_events_since_last_call(), + vec![Event::PoolCommissionUpdated { + pool_id: 1, + current: Some((Perbill::from_percent(6), 900)) + },] + ); + + // Attempt to increase the commission an additional 1% (now 7%). This will fail as + // `throttle_from` is now the current block. At least 2 blocks need to pass before we + // can set commission again. + + // Then: + assert_noop!( + Pools::set_commission( + RuntimeOrigin::signed(900), + 1, + Some((Perbill::from_percent(7), 900)) + ), + Error::::CommissionChangeThrottled + ); + + run_blocks(2); + + // Can now successfully increase the commission again, to 7%. + + // When: + assert_ok!(Pools::set_commission( + RuntimeOrigin::signed(900), + 1, + Some((Perbill::from_percent(7), 900)), + )); + assert_eq!( + pool_events_since_last_call(), + vec![Event::PoolCommissionUpdated { + pool_id: 1, + current: Some((Perbill::from_percent(7), 900)) + },] + ); + + run_blocks(2); + + // Now surpassed the `min_delay` threshold, but the `max_increase` threshold is + // still at play. An attempted commission change now to 8% (+2% increase) should fail. + + // Then: + assert_noop!( + Pools::set_commission( + RuntimeOrigin::signed(900), + 1, + Some((Perbill::from_percent(9), 900)), + ), + Error::::CommissionChangeThrottled + ); + + // Now set a max commission to the current 5%. This will also update the current + // commission to 5%. + + // When: + assert_ok!(Pools::set_commission_max( + RuntimeOrigin::signed(900), + 1, + Perbill::from_percent(5) + )); + assert_eq!( + BondedPool::::get(1).unwrap().commission, + Commission { + current: Some((Perbill::from_percent(5), 900)), + max: Some(Perbill::from_percent(5)), + change_rate: Some(CommissionChangeRate { + max_increase: Perbill::from_percent(1), + min_delay: 2 + }), + throttle_from: Some(7) + } + ); + assert_eq!( + pool_events_since_last_call(), + vec![ + Event::PoolCommissionUpdated { + pool_id: 1, + current: Some((Perbill::from_percent(5), 900)) + }, + Event::PoolMaxCommissionUpdated { + pool_id: 1, + max_commission: Perbill::from_percent(5) + } + ] + ); + + // Run 2 blocks into the future so we are eligible to update commission again. + run_blocks(2); + + // Now attempt again to increase the commission by 1%, to 6%. This is within the change + // rate allowance, but `max_commission` will now prevent us from going any higher. + + // Then: + assert_noop!( + Pools::set_commission( + RuntimeOrigin::signed(900), + 1, + Some((Perbill::from_percent(6), 900)), + ), + Error::::CommissionExceedsMaximum + ); + }); + } + + #[test] + fn set_commission_max_works_with_error_tests() { + ExtBuilder::default().build_and_execute(|| { + // Provided pool does not exist + assert_noop!( + Pools::set_commission_max( + RuntimeOrigin::signed(900), + 9999, + Perbill::from_percent(1) + ), + Error::::PoolNotFound + ); + // Sender does not have permission to set commission + assert_noop!( + Pools::set_commission_max(RuntimeOrigin::signed(1), 1, Perbill::from_percent(5)), + Error::::DoesNotHavePermission + ); + + // Set a max commission commission pool 1 to 80% + assert_ok!(Pools::set_commission_max( + RuntimeOrigin::signed(900), + 1, + Perbill::from_percent(80) + )); + assert_eq!( + BondedPools::::get(1).unwrap().commission.max, + Some(Perbill::from_percent(80)) + ); + + // We attempt to increase the max commission to 90%, but increasing is + // disallowed due to pool's max commission. + assert_noop!( + Pools::set_commission_max(RuntimeOrigin::signed(900), 1, Perbill::from_percent(90)), + Error::::MaxCommissionRestricted + ); + + // We will now set a commission to 75% and then amend the max commission + // to 50%. The max commission change should decrease the current + // commission to 50%. + assert_ok!(Pools::set_commission( + RuntimeOrigin::signed(900), + 1, + Some((Perbill::from_percent(75), 900)) + )); + assert_ok!(Pools::set_commission_max( + RuntimeOrigin::signed(900), + 1, + Perbill::from_percent(50) + )); + assert_eq!( + BondedPools::::get(1).unwrap().commission, + Commission { + current: Some((Perbill::from_percent(50), 900)), + max: Some(Perbill::from_percent(50)), + change_rate: None, + throttle_from: Some(1), + } + ); + + assert_eq!( + pool_events_since_last_call(), + vec![ + Event::Created { depositor: 10, pool_id: 1 }, + Event::Bonded { member: 10, pool_id: 1, bonded: 10, joined: true }, + Event::PoolMaxCommissionUpdated { + pool_id: 1, + max_commission: Perbill::from_percent(80) + }, + Event::PoolCommissionUpdated { + pool_id: 1, + current: Some((Perbill::from_percent(75), 900)) + }, + Event::PoolCommissionUpdated { + pool_id: 1, + current: Some((Perbill::from_percent(50), 900)) + }, + Event::PoolMaxCommissionUpdated { + pool_id: 1, + max_commission: Perbill::from_percent(50) + } + ] + ); + }); + } + + #[test] + fn set_commission_change_rate_works_with_errors() { + ExtBuilder::default().build_and_execute(|| { + // Provided pool does not exist + assert_noop!( + Pools::set_commission_change_rate( + RuntimeOrigin::signed(900), + 9999, + CommissionChangeRate { + max_increase: Perbill::from_percent(5), + min_delay: 1000_u64 + } + ), + Error::::PoolNotFound + ); + // Sender does not have permission to set commission + assert_noop!( + Pools::set_commission_change_rate( + RuntimeOrigin::signed(1), + 1, + CommissionChangeRate { + max_increase: Perbill::from_percent(5), + min_delay: 1000_u64 + } + ), + Error::::DoesNotHavePermission + ); + + // Set a commission change rate for pool 1 + assert_ok!(Pools::set_commission_change_rate( + RuntimeOrigin::signed(900), + 1, + CommissionChangeRate { max_increase: Perbill::from_percent(5), min_delay: 10_u64 } + )); + assert_eq!( + BondedPools::::get(1).unwrap().commission.change_rate, + Some(CommissionChangeRate { + max_increase: Perbill::from_percent(5), + min_delay: 10_u64 + }) + ); + assert_eq!( + pool_events_since_last_call(), + vec![ + Event::Created { depositor: 10, pool_id: 1 }, + Event::Bonded { member: 10, pool_id: 1, bonded: 10, joined: true }, + Event::PoolCommissionChangeRateUpdated { + pool_id: 1, + change_rate: CommissionChangeRate { + max_increase: Perbill::from_percent(5), + min_delay: 10 + } + }, + ] + ); + + // We now try to half the min_delay - this will be disallowed. A greater delay between + // commission changes is seen as more restrictive. + assert_noop!( + Pools::set_commission_change_rate( + RuntimeOrigin::signed(900), + 1, + CommissionChangeRate { + max_increase: Perbill::from_percent(5), + min_delay: 5_u64 + } + ), + Error::::CommissionChangeRateNotAllowed + ); + + // We now try to increase the allowed max_increase - this will fail. A smaller allowed + // commission change is seen as more restrictive. + assert_noop!( + Pools::set_commission_change_rate( + RuntimeOrigin::signed(900), + 1, + CommissionChangeRate { + max_increase: Perbill::from_percent(10), + min_delay: 10_u64 + } + ), + Error::::CommissionChangeRateNotAllowed + ); + + // Successful more restrictive change of min_delay with the current max_increase + assert_ok!(Pools::set_commission_change_rate( + RuntimeOrigin::signed(900), + 1, + CommissionChangeRate { max_increase: Perbill::from_percent(5), min_delay: 20_u64 } + )); + + // Successful more restrictive change of max_increase with the current min_delay + assert_ok!(Pools::set_commission_change_rate( + RuntimeOrigin::signed(900), + 1, + CommissionChangeRate { max_increase: Perbill::from_percent(4), min_delay: 20_u64 } + )); + + // Successful more restrictive change of both max_increase and min_delay + assert_ok!(Pools::set_commission_change_rate( + RuntimeOrigin::signed(900), + 1, + CommissionChangeRate { max_increase: Perbill::from_percent(3), min_delay: 30_u64 } + )); + + assert_eq!( + pool_events_since_last_call(), + vec![ + Event::PoolCommissionChangeRateUpdated { + pool_id: 1, + change_rate: CommissionChangeRate { + max_increase: Perbill::from_percent(5), + min_delay: 20 + } + }, + Event::PoolCommissionChangeRateUpdated { + pool_id: 1, + change_rate: CommissionChangeRate { + max_increase: Perbill::from_percent(4), + min_delay: 20 + } + }, + Event::PoolCommissionChangeRateUpdated { + pool_id: 1, + change_rate: CommissionChangeRate { + max_increase: Perbill::from_percent(3), + min_delay: 30 + } + } + ] + ); + }); + } + + #[test] + fn change_rate_does_not_apply_to_decreasing_commission() { + ExtBuilder::default().build_and_execute(|| { + // set initial commission of the pool to 10%. + assert_ok!(Pools::set_commission( + RuntimeOrigin::signed(900), + 1, + Some((Perbill::from_percent(10), 900)) + )); + + // Set a commission change rate for pool 1, 1% every 10 blocks + assert_ok!(Pools::set_commission_change_rate( + RuntimeOrigin::signed(900), + 1, + CommissionChangeRate { max_increase: Perbill::from_percent(1), min_delay: 10_u64 } + )); + assert_eq!( + BondedPools::::get(1).unwrap().commission.change_rate, + Some(CommissionChangeRate { + max_increase: Perbill::from_percent(1), + min_delay: 10_u64 + }) + ); + + // run `min_delay` blocks to allow a commission update. + run_blocks(10_u64); + + // Test `max_increase`: attempt to decrease the commission by 5%. Should succeed. + assert_ok!(Pools::set_commission( + RuntimeOrigin::signed(900), + 1, + Some((Perbill::from_percent(5), 900)) + )); + + // Test `min_delay`: *immediately* attempt to decrease the commission by 2%. Should + // succeed. + assert_ok!(Pools::set_commission( + RuntimeOrigin::signed(900), + 1, + Some((Perbill::from_percent(3), 900)) + )); + + // Attempt to *increase* the commission by 5%. Should fail. + assert_noop!( + Pools::set_commission( + RuntimeOrigin::signed(900), + 1, + Some((Perbill::from_percent(8), 900)) + ), + Error::::CommissionChangeThrottled + ); + + // Sanity check: the resulting pool Commission state. + assert_eq!( + BondedPools::::get(1).unwrap().commission, + Commission { + current: Some((Perbill::from_percent(3), 900)), + max: None, + change_rate: Some(CommissionChangeRate { + max_increase: Perbill::from_percent(1), + min_delay: 10_u64 + }), + throttle_from: Some(11), + } + ); + + assert_eq!( + pool_events_since_last_call(), + vec![ + Event::Created { depositor: 10, pool_id: 1 }, + Event::Bonded { member: 10, pool_id: 1, bonded: 10, joined: true }, + Event::PoolCommissionUpdated { + pool_id: 1, + current: Some((Perbill::from_percent(10), 900)) + }, + Event::PoolCommissionChangeRateUpdated { + pool_id: 1, + change_rate: CommissionChangeRate { + max_increase: Perbill::from_percent(1), + min_delay: 10 + } + }, + Event::PoolCommissionUpdated { + pool_id: 1, + current: Some((Perbill::from_percent(5), 900)) + }, + Event::PoolCommissionUpdated { + pool_id: 1, + current: Some((Perbill::from_percent(3), 900)) + } + ] + ); + }); + } + + #[test] + fn set_commission_max_to_zero_works() { + ExtBuilder::default().build_and_execute(|| { + // 0% max commission test. + // set commission max 0%. + assert_ok!(Pools::set_commission_max(RuntimeOrigin::signed(900), 1, Zero::zero())); + + // a max commission of 0% essentially freezes the current commission, even when None. + // All commission update attempts will fail. + assert_noop!( + Pools::set_commission( + RuntimeOrigin::signed(900), + 1, + Some((Perbill::from_percent(1), 900)) + ), + Error::::CommissionExceedsMaximum + ); + }) + } + + #[test] + fn set_commission_change_rate_zero_max_increase_works() { + ExtBuilder::default().build_and_execute(|| { + // set commission change rate to 0% per 10 blocks + assert_ok!(Pools::set_commission_change_rate( + RuntimeOrigin::signed(900), + 1, + CommissionChangeRate { max_increase: Perbill::from_percent(0), min_delay: 10_u64 } + )); + + // even though there is a min delay of 10 blocks, a max increase of 0% essentially + // freezes the commission. All commission update attempts will fail. + assert_noop!( + Pools::set_commission( + RuntimeOrigin::signed(900), + 1, + Some((Perbill::from_percent(1), 900)) + ), + Error::::CommissionChangeThrottled + ); + }) + } + + #[test] + fn set_commission_change_rate_zero_min_delay_works() { + ExtBuilder::default().build_and_execute(|| { + // set commission change rate to 1% with a 0 block `min_delay`. + assert_ok!(Pools::set_commission_change_rate( + RuntimeOrigin::signed(900), + 1, + CommissionChangeRate { max_increase: Perbill::from_percent(1), min_delay: 0_u64 } + )); + assert_eq!( + BondedPools::::get(1).unwrap().commission, + Commission { + current: None, + max: None, + change_rate: Some(CommissionChangeRate { + max_increase: Perbill::from_percent(1), + min_delay: 0 + }), + throttle_from: Some(1) + } + ); + + // since there is no min delay, we should be able to immediately set the commission. + assert_ok!(Pools::set_commission( + RuntimeOrigin::signed(900), + 1, + Some((Perbill::from_percent(1), 900)) + )); + + // sanity check: increasing again to more than +1% will fail. + assert_noop!( + Pools::set_commission( + RuntimeOrigin::signed(900), + 1, + Some((Perbill::from_percent(3), 900)) + ), + Error::::CommissionChangeThrottled + ); + }) + } + + #[test] + fn set_commission_change_rate_zero_value_works() { + ExtBuilder::default().build_and_execute(|| { + // Check zero values play nice. 0 `min_delay` and 0% max_increase test. + // set commission change rate to 0% per 0 blocks. + assert_ok!(Pools::set_commission_change_rate( + RuntimeOrigin::signed(900), + 1, + CommissionChangeRate { max_increase: Perbill::from_percent(0), min_delay: 0_u64 } + )); + + // even though there is no min delay, a max increase of 0% essentially freezes the + // commission. All commission update attempts will fail. + assert_noop!( + Pools::set_commission( + RuntimeOrigin::signed(900), + 1, + Some((Perbill::from_percent(1), 900)) + ), + Error::::CommissionChangeThrottled + ); + + assert_eq!( + pool_events_since_last_call(), + vec![ + Event::Created { depositor: 10, pool_id: 1 }, + Event::Bonded { member: 10, pool_id: 1, bonded: 10, joined: true }, + Event::PoolCommissionChangeRateUpdated { + pool_id: 1, + change_rate: CommissionChangeRate { + max_increase: Perbill::from_percent(0), + min_delay: 0_u64 + } + } + ] + ); + }) + } + + #[test] + fn do_reward_payout_with_various_commissions() { + ExtBuilder::default().build_and_execute(|| { + // turn off GlobalMaxCommission for this test. + GlobalMaxCommission::::set(None); + let pool_id = 1; + + // top up commission payee account to existential deposit + let _ = Balances::deposit_creating(&2, 5); + + // Set a commission pool 1 to 33%, with a payee set to `2` + assert_ok!(Pools::set_commission( + RuntimeOrigin::signed(900), + pool_id, + Some((Perbill::from_percent(33), 2)), + )); + assert_eq!( + pool_events_since_last_call(), + vec![ + Event::Created { depositor: 10, pool_id: 1 }, + Event::Bonded { member: 10, pool_id: 1, bonded: 10, joined: true }, + Event::PoolCommissionUpdated { + pool_id: 1, + current: Some((Perbill::from_percent(33), 2)) + }, + ] + ); + + // The pool earns 10 points + assert_ok!(Balances::mutate_account(&default_reward_account(), |a| a.free += 10)); + assert_ok!(Pools::claim_payout(RuntimeOrigin::signed(10))); + + // Then: + assert_eq!( + pool_events_since_last_call(), + vec![Event::PaidOut { member: 10, pool_id: 1, payout: 7 },] + ); + + // The pool earns 17 points + assert_ok!(Balances::mutate_account(&default_reward_account(), |a| a.free += 17)); + assert_ok!(Pools::claim_payout(RuntimeOrigin::signed(10))); + + // Then: + assert_eq!( + pool_events_since_last_call(), + vec![Event::PaidOut { member: 10, pool_id: 1, payout: 11 },] + ); + + // The pool earns 50 points + assert_ok!(Balances::mutate_account(&default_reward_account(), |a| a.free += 50)); + assert_ok!(Pools::claim_payout(RuntimeOrigin::signed(10))); + + // Then: + assert_eq!( + pool_events_since_last_call(), + vec![Event::PaidOut { member: 10, pool_id: 1, payout: 34 },] + ); + + // The pool earns 10439 points + assert_ok!(Balances::mutate_account(&default_reward_account(), |a| a.free += 10439)); + assert_ok!(Pools::claim_payout(RuntimeOrigin::signed(10))); + + // Then: + assert_eq!( + pool_events_since_last_call(), + vec![Event::PaidOut { member: 10, pool_id: 1, payout: 6994 },] + ); + + // Set the commission to 100% and ensure the following payout to the pool member will + // not happen. + + // When: + assert_ok!(Pools::set_commission( + RuntimeOrigin::signed(900), + pool_id, + Some((Perbill::from_percent(100), 2)), + )); + + // Given: + assert_ok!(Balances::mutate_account(&default_reward_account(), |a| a.free += 200)); + assert_ok!(Pools::claim_payout(RuntimeOrigin::signed(10))); + + // Then: + assert_eq!( + pool_events_since_last_call(), + vec![Event::PoolCommissionUpdated { + pool_id: 1, + current: Some((Perbill::from_percent(100), 2)) + },] + ); + }) + } + + #[test] + fn commission_accumulates_on_multiple_rewards() { + ExtBuilder::default().build_and_execute(|| { + let pool_id = 1; + + // Given: + + // Set initial commission of pool 1 to 10%. + assert_ok!(Pools::set_commission( + RuntimeOrigin::signed(900), + pool_id, + Some((Perbill::from_percent(10), 2)), + )); + assert_eq!( + pool_events_since_last_call(), + vec![ + Event::Created { depositor: 10, pool_id: 1 }, + Event::Bonded { member: 10, pool_id: 1, bonded: 10, joined: true }, + Event::PoolCommissionUpdated { + pool_id: 1, + current: Some((Perbill::from_percent(10), 2)) + }, + ] + ); + + // When: + + // The pool earns 100 points + assert_ok!(Balances::mutate_account(&default_reward_account(), |a| a.free += 100)); + + // Change commission to 20% + assert_ok!(Pools::set_commission( + RuntimeOrigin::signed(900), + pool_id, + Some((Perbill::from_percent(20), 2)), + )); + assert_eq!( + pool_events_since_last_call(), + vec![Event::PoolCommissionUpdated { + pool_id: 1, + current: Some((Perbill::from_percent(20), 2)) + },] + ); + + // The pool earns 100 points + assert_ok!(Balances::mutate_account(&default_reward_account(), |a| a.free += 100)); + + // Then: + + // Claim payout: + assert_ok!(Pools::claim_payout(RuntimeOrigin::signed(10))); + + // Claim commission: + assert_ok!(Pools::claim_commission(RuntimeOrigin::signed(900), pool_id)); + + // Then: + assert_eq!( + pool_events_since_last_call(), + vec![ + Event::PaidOut { member: 10, pool_id: 1, payout: 90 + 80 }, + Event::PoolCommissionClaimed { pool_id: 1, commission: 30 } + ] + ); + }) + } + + #[test] + fn last_recorded_total_payouts_needs_commission() { + ExtBuilder::default().build_and_execute(|| { + let pool_id = 1; + + // Given: + + // Set initial commission of pool 1 to 10%. + assert_ok!(Pools::set_commission( + RuntimeOrigin::signed(900), + pool_id, + Some((Perbill::from_percent(10), 2)), + )); + assert_eq!( + pool_events_since_last_call(), + vec![ + Event::Created { depositor: 10, pool_id: 1 }, + Event::Bonded { member: 10, pool_id: 1, bonded: 10, joined: true }, + Event::PoolCommissionUpdated { + pool_id: 1, + current: Some((Perbill::from_percent(10), 2)) + }, + ] + ); + + // When: + + // The pool earns 100 points + assert_ok!(Balances::mutate_account(&default_reward_account(), |a| a.free += 100)); + + // Claim payout: + assert_ok!(Pools::claim_payout(RuntimeOrigin::signed(10))); + + // Claim commission: + assert_ok!(Pools::claim_commission(RuntimeOrigin::signed(900), pool_id)); + + // Then: + + assert_eq!( + RewardPools::::get(1).unwrap().last_recorded_total_payouts, + 90 + 10 + ); + + assert_eq!( + pool_events_since_last_call(), + vec![ + Event::PaidOut { member: 10, pool_id: 1, payout: 90 }, + Event::PoolCommissionClaimed { pool_id: 1, commission: 10 } + ] + ); + }) + } + + #[test] + fn do_reward_payout_with_100_percent_commission() { + ExtBuilder::default().build_and_execute(|| { + // turn off GlobalMaxCommission for this test. + GlobalMaxCommission::::set(None); + + let (mut member, bonded_pool, mut reward_pool) = + Pools::get_member_with_pools(&10).unwrap(); + + // top up commission payee account to existential deposit + let _ = Balances::deposit_creating(&2, 5); + + // Set a commission pool 1 to 100%, with a payee set to `2` + assert_ok!(Pools::set_commission( + RuntimeOrigin::signed(900), + bonded_pool.id, + Some((Perbill::from_percent(100), 2)), + )); + + assert_eq!( + pool_events_since_last_call(), + vec![ + Event::Created { depositor: 10, pool_id: 1 }, + Event::Bonded { member: 10, pool_id: 1, bonded: 10, joined: true }, + Event::PoolCommissionUpdated { + pool_id: 1, + current: Some((Perbill::from_percent(100), 2)) + } + ] + ); + + // The pool earns 10 points + assert_ok!(Balances::mutate_account(&default_reward_account(), |a| a.free += 10)); + + // execute the payout + assert_ok!(Pools::do_reward_payout( + &10, + &mut member, + &mut BondedPool::::get(1).unwrap(), + &mut reward_pool + )); + }) + } + + #[test] + fn global_max_prevents_100_percent_commission_payout() { + ExtBuilder::default().build_and_execute(|| { + // Note: GlobalMaxCommission is set at 90%. + + let (mut member, bonded_pool, mut reward_pool) = + Pools::get_member_with_pools(&10).unwrap(); + + // top up the commission payee account to existential deposit + let _ = Balances::deposit_creating(&2, 5); + + // Set a commission pool 1 to 100%, with a payee set to `2` + assert_ok!(Pools::set_commission( + RuntimeOrigin::signed(900), + bonded_pool.id, + Some((Perbill::from_percent(100), 2)), + )); + assert_eq!( + pool_events_since_last_call(), + vec![ + Event::Created { depositor: 10, pool_id: 1 }, + Event::Bonded { member: 10, pool_id: 1, bonded: 10, joined: true }, + Event::PoolCommissionUpdated { + pool_id: 1, + current: Some((Perbill::from_percent(100), 2)) + } + ] + ); + + // The pool earns 10 points + assert_ok!(Balances::mutate_account(&default_reward_account(), |a| a.free += 10)); + + // execute the payout + assert_ok!(Pools::do_reward_payout( + &10, + &mut member, + &mut BondedPool::::get(1).unwrap(), + &mut reward_pool + )); + + // Confirm the commission was only 9 points out of 10 points, and the payout was 1 out + // of 10 points, reflecting the 90% global max commission. + assert_eq!( + pool_events_since_last_call(), + vec![Event::PaidOut { member: 10, pool_id: 1, payout: 1 },] + ); + }) + } + + #[test] + fn claim_commission_works() { + ExtBuilder::default().build_and_execute(|| { + let pool_id = 1; + + let _ = Balances::deposit_creating(&900, 5); + assert_ok!(Pools::set_commission( + RuntimeOrigin::signed(900), + pool_id, + Some((Perbill::from_percent(50), 900)) + )); + assert_eq!( + pool_events_since_last_call(), + vec![ + Event::Created { depositor: 10, pool_id }, + Event::Bonded { member: 10, pool_id, bonded: 10, joined: true }, + Event::PoolCommissionUpdated { + pool_id, + current: Some((Perbill::from_percent(50), 900)) + }, + ] + ); + + // Pool earns 80 points, payout is triggered. + assert_ok!(Balances::mutate_account(&default_reward_account(), |a| a.free += 80)); + assert_eq!( + PoolMembers::::get(10).unwrap(), + PoolMember:: { pool_id, points: 10, ..Default::default() } + ); + + assert_ok!(Pools::claim_payout(RuntimeOrigin::signed(10))); + assert_eq!( + pool_events_since_last_call(), + vec![Event::PaidOut { member: 10, pool_id, payout: 40 }] + ); + + // Given: + assert_eq!(RewardPool::::current_balance(pool_id), 40); + + // Pool does not exist + assert_noop!( + Pools::claim_commission(RuntimeOrigin::signed(900), 9999,), + Error::::PoolNotFound + ); + + // Does not have permission. + assert_noop!( + Pools::claim_commission(RuntimeOrigin::signed(10), pool_id,), + Error::::DoesNotHavePermission + ); + + // When: + assert_ok!(Pools::claim_commission(RuntimeOrigin::signed(900), pool_id)); + + // Then: + assert_eq!(RewardPool::::current_balance(pool_id), 0); + + // No more pending commission. + assert_noop!( + Pools::claim_commission(RuntimeOrigin::signed(900), pool_id,), + Error::::NoPendingCommission + ); + }) + } +} diff --git a/frame/nomination-pools/src/weights.rs b/frame/nomination-pools/src/weights.rs index ca7de7be6..cf0048fa4 100644 --- a/frame/nomination-pools/src/weights.rs +++ b/frame/nomination-pools/src/weights.rs @@ -18,9 +18,9 @@ //! Autogenerated weights for pallet_nomination_pools //! //! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev -//! DATE: 2023-02-21, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! DATE: 2023-03-08, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` //! WORST CASE MAP SIZE: `1000000` -//! HOSTNAME: `runner-ehxwxxsd-project-145-concurrent-0`, CPU: `Intel(R) Xeon(R) CPU @ 2.60GHz` +//! HOSTNAME: `bm3`, CPU: `Intel(R) Core(TM) i7-7700K CPU @ 4.20GHz` //! EXECUTION: Some(Wasm), WASM-EXECUTION: Compiled, CHAIN: Some("dev"), DB CACHE: 1024 // Executed Command: @@ -33,7 +33,7 @@ // --execution=wasm // --wasm-execution=compiled // --heap-pages=4096 -// --json-file=/builds/parity/mirrors/substrate/.git/.artifacts/bench.json +// --json-file=/var/lib/gitlab-runner/builds/zyw4fam_/0/parity/mirrors/substrate/.git/.artifacts/bench.json // --pallet=pallet_nomination_pools // --chain=dev // --header=./HEADER-APACHE2 @@ -64,7 +64,11 @@ pub trait WeightInfo { fn set_configs() -> Weight; fn update_roles() -> Weight; fn chill() -> Weight; + fn set_commission() -> Weight; + fn set_commission_max() -> Weight; + fn set_commission_change_rate() -> Weight; fn set_claim_permission() -> Weight; + fn claim_commission() -> Weight; } /// Weights for pallet_nomination_pools using the Substrate node and recommended hardware. @@ -75,13 +79,15 @@ impl WeightInfo for SubstrateWeight { /// Storage: NominationPools PoolMembers (r:1 w:1) /// Proof: NominationPools PoolMembers (max_values: None, max_size: Some(237), added: 2712, mode: MaxEncodedLen) /// Storage: NominationPools BondedPools (r:1 w:1) - /// Proof: NominationPools BondedPools (max_values: None, max_size: Some(164), added: 2639, mode: MaxEncodedLen) + /// Proof: NominationPools BondedPools (max_values: None, max_size: Some(220), added: 2695, mode: MaxEncodedLen) /// Storage: Staking Bonded (r:1 w:0) /// Proof: Staking Bonded (max_values: None, max_size: Some(72), added: 2547, mode: MaxEncodedLen) /// Storage: Staking Ledger (r:1 w:1) /// Proof: Staking Ledger (max_values: None, max_size: Some(1091), added: 3566, mode: MaxEncodedLen) /// Storage: NominationPools RewardPools (r:1 w:1) - /// Proof: NominationPools RewardPools (max_values: None, max_size: Some(60), added: 2535, mode: MaxEncodedLen) + /// Proof: NominationPools RewardPools (max_values: None, max_size: Some(92), added: 2567, mode: MaxEncodedLen) + /// Storage: NominationPools GlobalMaxCommission (r:1 w:0) + /// Proof: NominationPools GlobalMaxCommission (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) /// Storage: System Account (r:2 w:1) /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) /// Storage: NominationPools MaxPoolMembersPerPool (r:1 w:0) @@ -98,20 +104,21 @@ impl WeightInfo for SubstrateWeight { /// Proof: VoterList ListBags (max_values: None, max_size: Some(82), added: 2557, mode: MaxEncodedLen) fn join() -> Weight { // Proof Size summary in bytes: - // Measured: `3573` - // Estimated: `37988` - // Minimum execution time: 169_857 nanoseconds. - Weight::from_parts(173_895_000, 0) - .saturating_add(Weight::from_parts(0, 37988)) - .saturating_add(T::DbWeight::get().reads(17_u64)) + // Measured: `3650` + // Estimated: `52435` + // Minimum execution time: 160_401_000 picoseconds. + Weight::from_parts(161_798_000, 52435) + .saturating_add(T::DbWeight::get().reads(18_u64)) .saturating_add(T::DbWeight::get().writes(12_u64)) } /// Storage: NominationPools PoolMembers (r:1 w:1) /// Proof: NominationPools PoolMembers (max_values: None, max_size: Some(237), added: 2712, mode: MaxEncodedLen) /// Storage: NominationPools BondedPools (r:1 w:1) - /// Proof: NominationPools BondedPools (max_values: None, max_size: Some(164), added: 2639, mode: MaxEncodedLen) + /// Proof: NominationPools BondedPools (max_values: None, max_size: Some(220), added: 2695, mode: MaxEncodedLen) /// Storage: NominationPools RewardPools (r:1 w:1) - /// Proof: NominationPools RewardPools (max_values: None, max_size: Some(60), added: 2535, mode: MaxEncodedLen) + /// Proof: NominationPools RewardPools (max_values: None, max_size: Some(92), added: 2567, mode: MaxEncodedLen) + /// Storage: NominationPools GlobalMaxCommission (r:1 w:0) + /// Proof: NominationPools GlobalMaxCommission (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) /// Storage: System Account (r:3 w:2) /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) /// Storage: Staking Bonded (r:1 w:0) @@ -126,12 +133,11 @@ impl WeightInfo for SubstrateWeight { /// Proof: VoterList ListBags (max_values: None, max_size: Some(82), added: 2557, mode: MaxEncodedLen) fn bond_extra_transfer() -> Weight { // Proof Size summary in bytes: - // Measured: `3615` - // Estimated: `38583` - // Minimum execution time: 167_372 nanoseconds. - Weight::from_parts(168_776_000, 0) - .saturating_add(Weight::from_parts(0, 38583)) - .saturating_add(T::DbWeight::get().reads(14_u64)) + // Measured: `3692` + // Estimated: `49070` + // Minimum execution time: 157_668_000 picoseconds. + Weight::from_parts(161_129_000, 49070) + .saturating_add(T::DbWeight::get().reads(15_u64)) .saturating_add(T::DbWeight::get().writes(12_u64)) } /// Storage: NominationPools ClaimPermissions (r:1 w:0) @@ -139,9 +145,11 @@ impl WeightInfo for SubstrateWeight { /// Storage: NominationPools PoolMembers (r:1 w:1) /// Proof: NominationPools PoolMembers (max_values: None, max_size: Some(237), added: 2712, mode: MaxEncodedLen) /// Storage: NominationPools BondedPools (r:1 w:1) - /// Proof: NominationPools BondedPools (max_values: None, max_size: Some(164), added: 2639, mode: MaxEncodedLen) + /// Proof: NominationPools BondedPools (max_values: None, max_size: Some(220), added: 2695, mode: MaxEncodedLen) /// Storage: NominationPools RewardPools (r:1 w:1) - /// Proof: NominationPools RewardPools (max_values: None, max_size: Some(60), added: 2535, mode: MaxEncodedLen) + /// Proof: NominationPools RewardPools (max_values: None, max_size: Some(92), added: 2567, mode: MaxEncodedLen) + /// Storage: NominationPools GlobalMaxCommission (r:1 w:0) + /// Proof: NominationPools GlobalMaxCommission (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) /// Storage: System Account (r:3 w:3) /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) /// Storage: Staking Bonded (r:1 w:0) @@ -156,12 +164,11 @@ impl WeightInfo for SubstrateWeight { /// Proof: VoterList ListBags (max_values: None, max_size: Some(82), added: 2557, mode: MaxEncodedLen) fn bond_extra_other() -> Weight { // Proof Size summary in bytes: - // Measured: `3680` - // Estimated: `41099` - // Minimum execution time: 186_346 nanoseconds. - Weight::from_parts(191_308_000, 0) - .saturating_add(Weight::from_parts(0, 41099)) - .saturating_add(T::DbWeight::get().reads(15_u64)) + // Measured: `3757` + // Estimated: `52576` + // Minimum execution time: 176_034_000 picoseconds. + Weight::from_parts(176_956_000, 52576) + .saturating_add(T::DbWeight::get().reads(16_u64)) .saturating_add(T::DbWeight::get().writes(13_u64)) } /// Storage: NominationPools ClaimPermissions (r:1 w:0) @@ -169,31 +176,34 @@ impl WeightInfo for SubstrateWeight { /// Storage: NominationPools PoolMembers (r:1 w:1) /// Proof: NominationPools PoolMembers (max_values: None, max_size: Some(237), added: 2712, mode: MaxEncodedLen) /// Storage: NominationPools BondedPools (r:1 w:1) - /// Proof: NominationPools BondedPools (max_values: None, max_size: Some(164), added: 2639, mode: MaxEncodedLen) + /// Proof: NominationPools BondedPools (max_values: None, max_size: Some(220), added: 2695, mode: MaxEncodedLen) /// Storage: NominationPools RewardPools (r:1 w:1) - /// Proof: NominationPools RewardPools (max_values: None, max_size: Some(60), added: 2535, mode: MaxEncodedLen) + /// Proof: NominationPools RewardPools (max_values: None, max_size: Some(92), added: 2567, mode: MaxEncodedLen) + /// Storage: NominationPools GlobalMaxCommission (r:1 w:0) + /// Proof: NominationPools GlobalMaxCommission (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) /// Storage: System Account (r:1 w:1) /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) fn claim_payout() -> Weight { // Proof Size summary in bytes: - // Measured: `1254` - // Estimated: `13005` - // Minimum execution time: 61_423 nanoseconds. - Weight::from_parts(63_219_000, 0) - .saturating_add(Weight::from_parts(0, 13005)) - .saturating_add(T::DbWeight::get().reads(5_u64)) + // Measured: `1331` + // Estimated: `19532` + // Minimum execution time: 61_551_000 picoseconds. + Weight::from_parts(62_201_000, 19532) + .saturating_add(T::DbWeight::get().reads(6_u64)) .saturating_add(T::DbWeight::get().writes(4_u64)) } /// Storage: NominationPools PoolMembers (r:1 w:1) /// Proof: NominationPools PoolMembers (max_values: None, max_size: Some(237), added: 2712, mode: MaxEncodedLen) /// Storage: NominationPools BondedPools (r:1 w:1) - /// Proof: NominationPools BondedPools (max_values: None, max_size: Some(164), added: 2639, mode: MaxEncodedLen) + /// Proof: NominationPools BondedPools (max_values: None, max_size: Some(220), added: 2695, mode: MaxEncodedLen) /// Storage: NominationPools RewardPools (r:1 w:1) - /// Proof: NominationPools RewardPools (max_values: None, max_size: Some(60), added: 2535, mode: MaxEncodedLen) + /// Proof: NominationPools RewardPools (max_values: None, max_size: Some(92), added: 2567, mode: MaxEncodedLen) /// Storage: Staking Bonded (r:1 w:0) /// Proof: Staking Bonded (max_values: None, max_size: Some(72), added: 2547, mode: MaxEncodedLen) /// Storage: Staking Ledger (r:1 w:1) /// Proof: Staking Ledger (max_values: None, max_size: Some(1091), added: 3566, mode: MaxEncodedLen) + /// Storage: NominationPools GlobalMaxCommission (r:1 w:0) + /// Proof: NominationPools GlobalMaxCommission (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) /// Storage: System Account (r:2 w:1) /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) /// Storage: Staking CurrentEra (r:1 w:0) @@ -212,20 +222,17 @@ impl WeightInfo for SubstrateWeight { /// Proof: NominationPools SubPoolsStorage (max_values: None, max_size: Some(24382), added: 26857, mode: MaxEncodedLen) /// Storage: NominationPools CounterForSubPoolsStorage (r:1 w:1) /// Proof: NominationPools CounterForSubPoolsStorage (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) - /// Storage: NominationPools ClaimPermissions (r:0 w:1) - /// Proof: NominationPools ClaimPermissions (max_values: None, max_size: Some(41), added: 2516, mode: MaxEncodedLen) fn unbond() -> Weight { // Proof Size summary in bytes: - // Measured: `3858` - // Estimated: `67379` - // Minimum execution time: 174_532 nanoseconds. - Weight::from_parts(180_032_000, 0) - .saturating_add(Weight::from_parts(0, 67379)) - .saturating_add(T::DbWeight::get().reads(18_u64)) - .saturating_add(T::DbWeight::get().writes(14_u64)) + // Measured: `3935` + // Estimated: `82816` + // Minimum execution time: 162_755_000 picoseconds. + Weight::from_parts(163_518_000, 82816) + .saturating_add(T::DbWeight::get().reads(19_u64)) + .saturating_add(T::DbWeight::get().writes(13_u64)) } /// Storage: NominationPools BondedPools (r:1 w:0) - /// Proof: NominationPools BondedPools (max_values: None, max_size: Some(164), added: 2639, mode: MaxEncodedLen) + /// Proof: NominationPools BondedPools (max_values: None, max_size: Some(220), added: 2695, mode: MaxEncodedLen) /// Storage: Staking Bonded (r:1 w:0) /// Proof: Staking Bonded (max_values: None, max_size: Some(72), added: 2547, mode: MaxEncodedLen) /// Storage: Staking Ledger (r:1 w:1) @@ -237,13 +244,12 @@ impl WeightInfo for SubstrateWeight { /// The range of component `s` is `[0, 100]`. fn pool_withdraw_unbonded(s: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `1779` - // Estimated: `13025` - // Minimum execution time: 55_327 nanoseconds. - Weight::from_parts(58_947_746, 0) - .saturating_add(Weight::from_parts(0, 13025)) - // Standard Error: 1_589 - .saturating_add(Weight::from_parts(40_696, 0).saturating_mul(s.into())) + // Measured: `1783` + // Estimated: `18031` + // Minimum execution time: 54_752_000 picoseconds. + Weight::from_parts(56_248_171, 18031) + // Standard Error: 1_891 + .saturating_add(Weight::from_parts(4_767, 0).saturating_mul(s.into())) .saturating_add(T::DbWeight::get().reads(5_u64)) .saturating_add(T::DbWeight::get().writes(2_u64)) } @@ -252,7 +258,7 @@ impl WeightInfo for SubstrateWeight { /// Storage: Staking CurrentEra (r:1 w:0) /// Proof: Staking CurrentEra (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) /// Storage: NominationPools BondedPools (r:1 w:1) - /// Proof: NominationPools BondedPools (max_values: None, max_size: Some(164), added: 2639, mode: MaxEncodedLen) + /// Proof: NominationPools BondedPools (max_values: None, max_size: Some(220), added: 2695, mode: MaxEncodedLen) /// Storage: NominationPools SubPoolsStorage (r:1 w:1) /// Proof: NominationPools SubPoolsStorage (max_values: None, max_size: Some(24382), added: 26857, mode: MaxEncodedLen) /// Storage: Staking Bonded (r:1 w:0) @@ -265,25 +271,26 @@ impl WeightInfo for SubstrateWeight { /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) /// Storage: NominationPools CounterForPoolMembers (r:1 w:1) /// Proof: NominationPools CounterForPoolMembers (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) + /// Storage: NominationPools ClaimPermissions (r:0 w:1) + /// Proof: NominationPools ClaimPermissions (max_values: None, max_size: Some(41), added: 2516, mode: MaxEncodedLen) /// The range of component `s` is `[0, 100]`. fn withdraw_unbonded_update(s: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `2303` - // Estimated: `45696` - // Minimum execution time: 105_923 nanoseconds. - Weight::from_parts(110_572_476, 0) - .saturating_add(Weight::from_parts(0, 45696)) - // Standard Error: 2_438 - .saturating_add(Weight::from_parts(69_045, 0).saturating_mul(s.into())) + // Measured: `2307` + // Estimated: `54662` + // Minimum execution time: 106_166_000 picoseconds. + Weight::from_parts(107_806_373, 54662) + // Standard Error: 6_985 + .saturating_add(Weight::from_parts(18_449, 0).saturating_mul(s.into())) .saturating_add(T::DbWeight::get().reads(9_u64)) - .saturating_add(T::DbWeight::get().writes(7_u64)) + .saturating_add(T::DbWeight::get().writes(8_u64)) } /// Storage: NominationPools PoolMembers (r:1 w:1) /// Proof: NominationPools PoolMembers (max_values: None, max_size: Some(237), added: 2712, mode: MaxEncodedLen) /// Storage: Staking CurrentEra (r:1 w:0) /// Proof: Staking CurrentEra (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) /// Storage: NominationPools BondedPools (r:1 w:1) - /// Proof: NominationPools BondedPools (max_values: None, max_size: Some(164), added: 2639, mode: MaxEncodedLen) + /// Proof: NominationPools BondedPools (max_values: None, max_size: Some(220), added: 2695, mode: MaxEncodedLen) /// Storage: NominationPools SubPoolsStorage (r:1 w:1) /// Proof: NominationPools SubPoolsStorage (max_values: None, max_size: Some(24382), added: 26857, mode: MaxEncodedLen) /// Storage: Staking Bonded (r:1 w:1) @@ -307,7 +314,7 @@ impl WeightInfo for SubstrateWeight { /// Storage: NominationPools CounterForReversePoolIdLookup (r:1 w:1) /// Proof: NominationPools CounterForReversePoolIdLookup (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) /// Storage: NominationPools RewardPools (r:1 w:1) - /// Proof: NominationPools RewardPools (max_values: None, max_size: Some(60), added: 2535, mode: MaxEncodedLen) + /// Proof: NominationPools RewardPools (max_values: None, max_size: Some(92), added: 2567, mode: MaxEncodedLen) /// Storage: NominationPools CounterForRewardPools (r:1 w:1) /// Proof: NominationPools CounterForRewardPools (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) /// Storage: NominationPools CounterForSubPoolsStorage (r:1 w:1) @@ -318,16 +325,19 @@ impl WeightInfo for SubstrateWeight { /// Proof: NominationPools CounterForBondedPools (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) /// Storage: Staking Payee (r:0 w:1) /// Proof: Staking Payee (max_values: None, max_size: Some(73), added: 2548, mode: MaxEncodedLen) + /// Storage: NominationPools ClaimPermissions (r:0 w:1) + /// Proof: NominationPools ClaimPermissions (max_values: None, max_size: Some(41), added: 2516, mode: MaxEncodedLen) /// The range of component `s` is `[0, 100]`. - fn withdraw_unbonded_kill(_s: u32, ) -> Weight { + fn withdraw_unbonded_kill(s: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `2690` - // Estimated: `68812` - // Minimum execution time: 169_700 nanoseconds. - Weight::from_parts(178_693_541, 0) - .saturating_add(Weight::from_parts(0, 68812)) + // Measured: `2694` + // Estimated: `87714` + // Minimum execution time: 170_047_000 picoseconds. + Weight::from_parts(172_125_770, 87714) + // Standard Error: 2_599 + .saturating_add(Weight::from_parts(9_964, 0).saturating_mul(s.into())) .saturating_add(T::DbWeight::get().reads(20_u64)) - .saturating_add(T::DbWeight::get().writes(17_u64)) + .saturating_add(T::DbWeight::get().writes(18_u64)) } /// Storage: NominationPools LastPoolId (r:1 w:1) /// Proof: NominationPools LastPoolId (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) @@ -360,7 +370,7 @@ impl WeightInfo for SubstrateWeight { /// Storage: Balances Locks (r:1 w:1) /// Proof: Balances Locks (max_values: None, max_size: Some(1299), added: 3774, mode: MaxEncodedLen) /// Storage: NominationPools RewardPools (r:1 w:1) - /// Proof: NominationPools RewardPools (max_values: None, max_size: Some(60), added: 2535, mode: MaxEncodedLen) + /// Proof: NominationPools RewardPools (max_values: None, max_size: Some(92), added: 2567, mode: MaxEncodedLen) /// Storage: NominationPools CounterForRewardPools (r:1 w:1) /// Proof: NominationPools CounterForRewardPools (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) /// Storage: NominationPools ReversePoolIdLookup (r:1 w:1) @@ -368,21 +378,20 @@ impl WeightInfo for SubstrateWeight { /// Storage: NominationPools CounterForReversePoolIdLookup (r:1 w:1) /// Proof: NominationPools CounterForReversePoolIdLookup (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) /// Storage: NominationPools BondedPools (r:1 w:1) - /// Proof: NominationPools BondedPools (max_values: None, max_size: Some(164), added: 2639, mode: MaxEncodedLen) + /// Proof: NominationPools BondedPools (max_values: None, max_size: Some(220), added: 2695, mode: MaxEncodedLen) /// Storage: Staking Payee (r:0 w:1) /// Proof: Staking Payee (max_values: None, max_size: Some(73), added: 2548, mode: MaxEncodedLen) fn create() -> Weight { // Proof Size summary in bytes: // Measured: `1321` - // Estimated: `31522` - // Minimum execution time: 145_976 nanoseconds. - Weight::from_parts(150_664_000, 0) - .saturating_add(Weight::from_parts(0, 31522)) + // Estimated: `51410` + // Minimum execution time: 149_672_000 picoseconds. + Weight::from_parts(153_613_000, 51410) .saturating_add(T::DbWeight::get().reads(21_u64)) .saturating_add(T::DbWeight::get().writes(15_u64)) } /// Storage: NominationPools BondedPools (r:1 w:0) - /// Proof: NominationPools BondedPools (max_values: None, max_size: Some(164), added: 2639, mode: MaxEncodedLen) + /// Proof: NominationPools BondedPools (max_values: None, max_size: Some(220), added: 2695, mode: MaxEncodedLen) /// Storage: Staking Bonded (r:1 w:0) /// Proof: Staking Bonded (max_values: None, max_size: Some(72), added: 2547, mode: MaxEncodedLen) /// Storage: Staking Ledger (r:1 w:0) @@ -408,36 +417,34 @@ impl WeightInfo for SubstrateWeight { /// The range of component `n` is `[1, 16]`. fn nominate(n: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `1909` - // Estimated: `21998 + n * (2520 ±0)` - // Minimum execution time: 69_288 nanoseconds. - Weight::from_parts(71_075_293, 0) - .saturating_add(Weight::from_parts(0, 21998)) - // Standard Error: 10_508 - .saturating_add(Weight::from_parts(1_384_674, 0).saturating_mul(n.into())) + // Measured: `1913` + // Estimated: `33934 + n * (2520 ±0)` + // Minimum execution time: 68_892_000 picoseconds. + Weight::from_parts(69_062_946, 33934) + // Standard Error: 6_448 + .saturating_add(Weight::from_parts(1_422_774, 0).saturating_mul(n.into())) .saturating_add(T::DbWeight::get().reads(12_u64)) .saturating_add(T::DbWeight::get().reads((1_u64).saturating_mul(n.into()))) .saturating_add(T::DbWeight::get().writes(5_u64)) .saturating_add(Weight::from_parts(0, 2520).saturating_mul(n.into())) } /// Storage: NominationPools BondedPools (r:1 w:1) - /// Proof: NominationPools BondedPools (max_values: None, max_size: Some(164), added: 2639, mode: MaxEncodedLen) + /// Proof: NominationPools BondedPools (max_values: None, max_size: Some(220), added: 2695, mode: MaxEncodedLen) /// Storage: Staking Bonded (r:1 w:0) /// Proof: Staking Bonded (max_values: None, max_size: Some(72), added: 2547, mode: MaxEncodedLen) /// Storage: Staking Ledger (r:1 w:0) /// Proof: Staking Ledger (max_values: None, max_size: Some(1091), added: 3566, mode: MaxEncodedLen) fn set_state() -> Weight { // Proof Size summary in bytes: - // Measured: `1498` - // Estimated: `8752` - // Minimum execution time: 36_410 nanoseconds. - Weight::from_parts(37_585_000, 0) - .saturating_add(Weight::from_parts(0, 8752)) + // Measured: `1502` + // Estimated: `11778` + // Minimum execution time: 36_447_000 picoseconds. + Weight::from_parts(36_837_000, 11778) .saturating_add(T::DbWeight::get().reads(3_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } /// Storage: NominationPools BondedPools (r:1 w:0) - /// Proof: NominationPools BondedPools (max_values: None, max_size: Some(164), added: 2639, mode: MaxEncodedLen) + /// Proof: NominationPools BondedPools (max_values: None, max_size: Some(220), added: 2695, mode: MaxEncodedLen) /// Storage: NominationPools Metadata (r:1 w:1) /// Proof: NominationPools Metadata (max_values: None, max_size: Some(270), added: 2745, mode: MaxEncodedLen) /// Storage: NominationPools CounterForMetadata (r:1 w:1) @@ -445,13 +452,12 @@ impl WeightInfo for SubstrateWeight { /// The range of component `n` is `[1, 256]`. fn set_metadata(n: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `559` - // Estimated: `5883` - // Minimum execution time: 14_322 nanoseconds. - Weight::from_parts(15_328_204, 0) - .saturating_add(Weight::from_parts(0, 5883)) - // Standard Error: 161 - .saturating_add(Weight::from_parts(1_406, 0).saturating_mul(n.into())) + // Measured: `563` + // Estimated: `8909` + // Minimum execution time: 15_221_000 picoseconds. + Weight::from_parts(15_632_286, 8909) + // Standard Error: 343 + .saturating_add(Weight::from_parts(2_299, 0).saturating_mul(n.into())) .saturating_add(T::DbWeight::get().reads(3_u64)) .saturating_add(T::DbWeight::get().writes(2_u64)) } @@ -463,31 +469,31 @@ impl WeightInfo for SubstrateWeight { /// Proof: NominationPools MaxPoolMembersPerPool (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) /// Storage: NominationPools MinCreateBond (r:0 w:1) /// Proof: NominationPools MinCreateBond (max_values: Some(1), max_size: Some(16), added: 511, mode: MaxEncodedLen) + /// Storage: NominationPools GlobalMaxCommission (r:0 w:1) + /// Proof: NominationPools GlobalMaxCommission (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) /// Storage: NominationPools MaxPools (r:0 w:1) /// Proof: NominationPools MaxPools (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) fn set_configs() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 5_968 nanoseconds. - Weight::from_parts(6_245_000, 0) - .saturating_add(Weight::from_parts(0, 0)) - .saturating_add(T::DbWeight::get().writes(5_u64)) + // Minimum execution time: 7_409_000 picoseconds. + Weight::from_parts(7_702_000, 0) + .saturating_add(T::DbWeight::get().writes(6_u64)) } /// Storage: NominationPools BondedPools (r:1 w:1) - /// Proof: NominationPools BondedPools (max_values: None, max_size: Some(164), added: 2639, mode: MaxEncodedLen) + /// Proof: NominationPools BondedPools (max_values: None, max_size: Some(220), added: 2695, mode: MaxEncodedLen) fn update_roles() -> Weight { // Proof Size summary in bytes: - // Measured: `559` - // Estimated: `2639` - // Minimum execution time: 18_979 nanoseconds. - Weight::from_parts(19_795_000, 0) - .saturating_add(Weight::from_parts(0, 2639)) + // Measured: `563` + // Estimated: `3685` + // Minimum execution time: 20_451_000 picoseconds. + Weight::from_parts(20_703_000, 3685) .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } /// Storage: NominationPools BondedPools (r:1 w:0) - /// Proof: NominationPools BondedPools (max_values: None, max_size: Some(164), added: 2639, mode: MaxEncodedLen) + /// Proof: NominationPools BondedPools (max_values: None, max_size: Some(220), added: 2695, mode: MaxEncodedLen) /// Storage: Staking Bonded (r:1 w:0) /// Proof: Staking Bonded (max_values: None, max_size: Some(72), added: 2547, mode: MaxEncodedLen) /// Storage: Staking Ledger (r:1 w:0) @@ -506,14 +512,52 @@ impl WeightInfo for SubstrateWeight { /// Proof: VoterList CounterForListNodes (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) fn chill() -> Weight { // Proof Size summary in bytes: - // Measured: `2136` - // Estimated: `20489` - // Minimum execution time: 68_145 nanoseconds. - Weight::from_parts(70_444_000, 0) - .saturating_add(Weight::from_parts(0, 20489)) + // Measured: `2140` + // Estimated: `29455` + // Minimum execution time: 66_001_000 picoseconds. + Weight::from_parts(66_894_000, 29455) .saturating_add(T::DbWeight::get().reads(9_u64)) .saturating_add(T::DbWeight::get().writes(5_u64)) } + /// Storage: NominationPools BondedPools (r:1 w:1) + /// Proof: NominationPools BondedPools (max_values: None, max_size: Some(220), added: 2695, mode: MaxEncodedLen) + /// Storage: NominationPools RewardPools (r:1 w:1) + /// Proof: NominationPools RewardPools (max_values: None, max_size: Some(92), added: 2567, mode: MaxEncodedLen) + /// Storage: NominationPools GlobalMaxCommission (r:1 w:0) + /// Proof: NominationPools GlobalMaxCommission (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) + /// Storage: System Account (r:1 w:0) + /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) + fn set_commission() -> Weight { + // Proof Size summary in bytes: + // Measured: `866` + // Estimated: `12324` + // Minimum execution time: 34_011_000 picoseconds. + Weight::from_parts(34_521_000, 12324) + .saturating_add(T::DbWeight::get().reads(4_u64)) + .saturating_add(T::DbWeight::get().writes(2_u64)) + } + /// Storage: NominationPools BondedPools (r:1 w:1) + /// Proof: NominationPools BondedPools (max_values: None, max_size: Some(220), added: 2695, mode: MaxEncodedLen) + fn set_commission_max() -> Weight { + // Proof Size summary in bytes: + // Measured: `603` + // Estimated: `3685` + // Minimum execution time: 19_524_000 picoseconds. + Weight::from_parts(19_855_000, 3685) + .saturating_add(T::DbWeight::get().reads(1_u64)) + .saturating_add(T::DbWeight::get().writes(1_u64)) + } + /// Storage: NominationPools BondedPools (r:1 w:1) + /// Proof: NominationPools BondedPools (max_values: None, max_size: Some(220), added: 2695, mode: MaxEncodedLen) + fn set_commission_change_rate() -> Weight { + // Proof Size summary in bytes: + // Measured: `563` + // Estimated: `3685` + // Minimum execution time: 20_457_000 picoseconds. + Weight::from_parts(20_698_000, 3685) + .saturating_add(T::DbWeight::get().reads(1_u64)) + .saturating_add(T::DbWeight::get().writes(1_u64)) + } /// Storage: NominationPools PoolMembers (r:1 w:0) /// Proof: NominationPools PoolMembers (max_values: None, max_size: Some(237), added: 2712, mode: MaxEncodedLen) /// Storage: NominationPools ClaimPermissions (r:1 w:1) @@ -521,13 +565,29 @@ impl WeightInfo for SubstrateWeight { fn set_claim_permission() -> Weight { // Proof Size summary in bytes: // Measured: `542` - // Estimated: `5228` - // Minimum execution time: 15_112 nanoseconds. - Weight::from_parts(15_897_000, 0) - .saturating_add(Weight::from_parts(0, 5228)) + // Estimated: `7208` + // Minimum execution time: 15_183_000 picoseconds. + Weight::from_parts(15_597_000, 7208) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } + /// Storage: NominationPools BondedPools (r:1 w:0) + /// Proof: NominationPools BondedPools (max_values: None, max_size: Some(220), added: 2695, mode: MaxEncodedLen) + /// Storage: NominationPools RewardPools (r:1 w:1) + /// Proof: NominationPools RewardPools (max_values: None, max_size: Some(92), added: 2567, mode: MaxEncodedLen) + /// Storage: NominationPools GlobalMaxCommission (r:1 w:0) + /// Proof: NominationPools GlobalMaxCommission (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) + /// Storage: System Account (r:1 w:1) + /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) + fn claim_commission() -> Weight { + // Proof Size summary in bytes: + // Measured: `1096` + // Estimated: `12324` + // Minimum execution time: 48_957_000 picoseconds. + Weight::from_parts(50_207_000, 12324) + .saturating_add(T::DbWeight::get().reads(4_u64)) + .saturating_add(T::DbWeight::get().writes(2_u64)) + } } // For backwards compatibility and tests @@ -537,13 +597,15 @@ impl WeightInfo for () { /// Storage: NominationPools PoolMembers (r:1 w:1) /// Proof: NominationPools PoolMembers (max_values: None, max_size: Some(237), added: 2712, mode: MaxEncodedLen) /// Storage: NominationPools BondedPools (r:1 w:1) - /// Proof: NominationPools BondedPools (max_values: None, max_size: Some(164), added: 2639, mode: MaxEncodedLen) + /// Proof: NominationPools BondedPools (max_values: None, max_size: Some(220), added: 2695, mode: MaxEncodedLen) /// Storage: Staking Bonded (r:1 w:0) /// Proof: Staking Bonded (max_values: None, max_size: Some(72), added: 2547, mode: MaxEncodedLen) /// Storage: Staking Ledger (r:1 w:1) /// Proof: Staking Ledger (max_values: None, max_size: Some(1091), added: 3566, mode: MaxEncodedLen) /// Storage: NominationPools RewardPools (r:1 w:1) - /// Proof: NominationPools RewardPools (max_values: None, max_size: Some(60), added: 2535, mode: MaxEncodedLen) + /// Proof: NominationPools RewardPools (max_values: None, max_size: Some(92), added: 2567, mode: MaxEncodedLen) + /// Storage: NominationPools GlobalMaxCommission (r:1 w:0) + /// Proof: NominationPools GlobalMaxCommission (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) /// Storage: System Account (r:2 w:1) /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) /// Storage: NominationPools MaxPoolMembersPerPool (r:1 w:0) @@ -560,20 +622,21 @@ impl WeightInfo for () { /// Proof: VoterList ListBags (max_values: None, max_size: Some(82), added: 2557, mode: MaxEncodedLen) fn join() -> Weight { // Proof Size summary in bytes: - // Measured: `3573` - // Estimated: `37988` - // Minimum execution time: 169_857 nanoseconds. - Weight::from_parts(173_895_000, 0) - .saturating_add(Weight::from_parts(0, 37988)) - .saturating_add(RocksDbWeight::get().reads(17_u64)) + // Measured: `3650` + // Estimated: `52435` + // Minimum execution time: 160_401_000 picoseconds. + Weight::from_parts(161_798_000, 52435) + .saturating_add(RocksDbWeight::get().reads(18_u64)) .saturating_add(RocksDbWeight::get().writes(12_u64)) } /// Storage: NominationPools PoolMembers (r:1 w:1) /// Proof: NominationPools PoolMembers (max_values: None, max_size: Some(237), added: 2712, mode: MaxEncodedLen) /// Storage: NominationPools BondedPools (r:1 w:1) - /// Proof: NominationPools BondedPools (max_values: None, max_size: Some(164), added: 2639, mode: MaxEncodedLen) + /// Proof: NominationPools BondedPools (max_values: None, max_size: Some(220), added: 2695, mode: MaxEncodedLen) /// Storage: NominationPools RewardPools (r:1 w:1) - /// Proof: NominationPools RewardPools (max_values: None, max_size: Some(60), added: 2535, mode: MaxEncodedLen) + /// Proof: NominationPools RewardPools (max_values: None, max_size: Some(92), added: 2567, mode: MaxEncodedLen) + /// Storage: NominationPools GlobalMaxCommission (r:1 w:0) + /// Proof: NominationPools GlobalMaxCommission (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) /// Storage: System Account (r:3 w:2) /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) /// Storage: Staking Bonded (r:1 w:0) @@ -588,12 +651,11 @@ impl WeightInfo for () { /// Proof: VoterList ListBags (max_values: None, max_size: Some(82), added: 2557, mode: MaxEncodedLen) fn bond_extra_transfer() -> Weight { // Proof Size summary in bytes: - // Measured: `3615` - // Estimated: `38583` - // Minimum execution time: 167_372 nanoseconds. - Weight::from_parts(168_776_000, 0) - .saturating_add(Weight::from_parts(0, 38583)) - .saturating_add(RocksDbWeight::get().reads(14_u64)) + // Measured: `3692` + // Estimated: `49070` + // Minimum execution time: 157_668_000 picoseconds. + Weight::from_parts(161_129_000, 49070) + .saturating_add(RocksDbWeight::get().reads(15_u64)) .saturating_add(RocksDbWeight::get().writes(12_u64)) } /// Storage: NominationPools ClaimPermissions (r:1 w:0) @@ -601,9 +663,11 @@ impl WeightInfo for () { /// Storage: NominationPools PoolMembers (r:1 w:1) /// Proof: NominationPools PoolMembers (max_values: None, max_size: Some(237), added: 2712, mode: MaxEncodedLen) /// Storage: NominationPools BondedPools (r:1 w:1) - /// Proof: NominationPools BondedPools (max_values: None, max_size: Some(164), added: 2639, mode: MaxEncodedLen) + /// Proof: NominationPools BondedPools (max_values: None, max_size: Some(220), added: 2695, mode: MaxEncodedLen) /// Storage: NominationPools RewardPools (r:1 w:1) - /// Proof: NominationPools RewardPools (max_values: None, max_size: Some(60), added: 2535, mode: MaxEncodedLen) + /// Proof: NominationPools RewardPools (max_values: None, max_size: Some(92), added: 2567, mode: MaxEncodedLen) + /// Storage: NominationPools GlobalMaxCommission (r:1 w:0) + /// Proof: NominationPools GlobalMaxCommission (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) /// Storage: System Account (r:3 w:3) /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) /// Storage: Staking Bonded (r:1 w:0) @@ -618,12 +682,11 @@ impl WeightInfo for () { /// Proof: VoterList ListBags (max_values: None, max_size: Some(82), added: 2557, mode: MaxEncodedLen) fn bond_extra_other() -> Weight { // Proof Size summary in bytes: - // Measured: `3680` - // Estimated: `41099` - // Minimum execution time: 186_346 nanoseconds. - Weight::from_parts(191_308_000, 0) - .saturating_add(Weight::from_parts(0, 41099)) - .saturating_add(RocksDbWeight::get().reads(15_u64)) + // Measured: `3757` + // Estimated: `52576` + // Minimum execution time: 176_034_000 picoseconds. + Weight::from_parts(176_956_000, 52576) + .saturating_add(RocksDbWeight::get().reads(16_u64)) .saturating_add(RocksDbWeight::get().writes(13_u64)) } /// Storage: NominationPools ClaimPermissions (r:1 w:0) @@ -631,31 +694,34 @@ impl WeightInfo for () { /// Storage: NominationPools PoolMembers (r:1 w:1) /// Proof: NominationPools PoolMembers (max_values: None, max_size: Some(237), added: 2712, mode: MaxEncodedLen) /// Storage: NominationPools BondedPools (r:1 w:1) - /// Proof: NominationPools BondedPools (max_values: None, max_size: Some(164), added: 2639, mode: MaxEncodedLen) + /// Proof: NominationPools BondedPools (max_values: None, max_size: Some(220), added: 2695, mode: MaxEncodedLen) /// Storage: NominationPools RewardPools (r:1 w:1) - /// Proof: NominationPools RewardPools (max_values: None, max_size: Some(60), added: 2535, mode: MaxEncodedLen) + /// Proof: NominationPools RewardPools (max_values: None, max_size: Some(92), added: 2567, mode: MaxEncodedLen) + /// Storage: NominationPools GlobalMaxCommission (r:1 w:0) + /// Proof: NominationPools GlobalMaxCommission (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) /// Storage: System Account (r:1 w:1) /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) fn claim_payout() -> Weight { // Proof Size summary in bytes: - // Measured: `1254` - // Estimated: `13005` - // Minimum execution time: 61_423 nanoseconds. - Weight::from_parts(63_219_000, 0) - .saturating_add(Weight::from_parts(0, 13005)) - .saturating_add(RocksDbWeight::get().reads(5_u64)) + // Measured: `1331` + // Estimated: `19532` + // Minimum execution time: 61_551_000 picoseconds. + Weight::from_parts(62_201_000, 19532) + .saturating_add(RocksDbWeight::get().reads(6_u64)) .saturating_add(RocksDbWeight::get().writes(4_u64)) } /// Storage: NominationPools PoolMembers (r:1 w:1) /// Proof: NominationPools PoolMembers (max_values: None, max_size: Some(237), added: 2712, mode: MaxEncodedLen) /// Storage: NominationPools BondedPools (r:1 w:1) - /// Proof: NominationPools BondedPools (max_values: None, max_size: Some(164), added: 2639, mode: MaxEncodedLen) + /// Proof: NominationPools BondedPools (max_values: None, max_size: Some(220), added: 2695, mode: MaxEncodedLen) /// Storage: NominationPools RewardPools (r:1 w:1) - /// Proof: NominationPools RewardPools (max_values: None, max_size: Some(60), added: 2535, mode: MaxEncodedLen) + /// Proof: NominationPools RewardPools (max_values: None, max_size: Some(92), added: 2567, mode: MaxEncodedLen) /// Storage: Staking Bonded (r:1 w:0) /// Proof: Staking Bonded (max_values: None, max_size: Some(72), added: 2547, mode: MaxEncodedLen) /// Storage: Staking Ledger (r:1 w:1) /// Proof: Staking Ledger (max_values: None, max_size: Some(1091), added: 3566, mode: MaxEncodedLen) + /// Storage: NominationPools GlobalMaxCommission (r:1 w:0) + /// Proof: NominationPools GlobalMaxCommission (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) /// Storage: System Account (r:2 w:1) /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) /// Storage: Staking CurrentEra (r:1 w:0) @@ -674,20 +740,17 @@ impl WeightInfo for () { /// Proof: NominationPools SubPoolsStorage (max_values: None, max_size: Some(24382), added: 26857, mode: MaxEncodedLen) /// Storage: NominationPools CounterForSubPoolsStorage (r:1 w:1) /// Proof: NominationPools CounterForSubPoolsStorage (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) - /// Storage: NominationPools ClaimPermissions (r:0 w:1) - /// Proof: NominationPools ClaimPermissions (max_values: None, max_size: Some(41), added: 2516, mode: MaxEncodedLen) fn unbond() -> Weight { // Proof Size summary in bytes: - // Measured: `3858` - // Estimated: `67379` - // Minimum execution time: 174_532 nanoseconds. - Weight::from_parts(180_032_000, 0) - .saturating_add(Weight::from_parts(0, 67379)) - .saturating_add(RocksDbWeight::get().reads(18_u64)) - .saturating_add(RocksDbWeight::get().writes(14_u64)) + // Measured: `3935` + // Estimated: `82816` + // Minimum execution time: 162_755_000 picoseconds. + Weight::from_parts(163_518_000, 82816) + .saturating_add(RocksDbWeight::get().reads(19_u64)) + .saturating_add(RocksDbWeight::get().writes(13_u64)) } /// Storage: NominationPools BondedPools (r:1 w:0) - /// Proof: NominationPools BondedPools (max_values: None, max_size: Some(164), added: 2639, mode: MaxEncodedLen) + /// Proof: NominationPools BondedPools (max_values: None, max_size: Some(220), added: 2695, mode: MaxEncodedLen) /// Storage: Staking Bonded (r:1 w:0) /// Proof: Staking Bonded (max_values: None, max_size: Some(72), added: 2547, mode: MaxEncodedLen) /// Storage: Staking Ledger (r:1 w:1) @@ -699,13 +762,12 @@ impl WeightInfo for () { /// The range of component `s` is `[0, 100]`. fn pool_withdraw_unbonded(s: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `1779` - // Estimated: `13025` - // Minimum execution time: 55_327 nanoseconds. - Weight::from_parts(58_947_746, 0) - .saturating_add(Weight::from_parts(0, 13025)) - // Standard Error: 1_589 - .saturating_add(Weight::from_parts(40_696, 0).saturating_mul(s.into())) + // Measured: `1783` + // Estimated: `18031` + // Minimum execution time: 54_752_000 picoseconds. + Weight::from_parts(56_248_171, 18031) + // Standard Error: 1_891 + .saturating_add(Weight::from_parts(4_767, 0).saturating_mul(s.into())) .saturating_add(RocksDbWeight::get().reads(5_u64)) .saturating_add(RocksDbWeight::get().writes(2_u64)) } @@ -714,7 +776,7 @@ impl WeightInfo for () { /// Storage: Staking CurrentEra (r:1 w:0) /// Proof: Staking CurrentEra (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) /// Storage: NominationPools BondedPools (r:1 w:1) - /// Proof: NominationPools BondedPools (max_values: None, max_size: Some(164), added: 2639, mode: MaxEncodedLen) + /// Proof: NominationPools BondedPools (max_values: None, max_size: Some(220), added: 2695, mode: MaxEncodedLen) /// Storage: NominationPools SubPoolsStorage (r:1 w:1) /// Proof: NominationPools SubPoolsStorage (max_values: None, max_size: Some(24382), added: 26857, mode: MaxEncodedLen) /// Storage: Staking Bonded (r:1 w:0) @@ -727,25 +789,26 @@ impl WeightInfo for () { /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) /// Storage: NominationPools CounterForPoolMembers (r:1 w:1) /// Proof: NominationPools CounterForPoolMembers (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) + /// Storage: NominationPools ClaimPermissions (r:0 w:1) + /// Proof: NominationPools ClaimPermissions (max_values: None, max_size: Some(41), added: 2516, mode: MaxEncodedLen) /// The range of component `s` is `[0, 100]`. fn withdraw_unbonded_update(s: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `2303` - // Estimated: `45696` - // Minimum execution time: 105_923 nanoseconds. - Weight::from_parts(110_572_476, 0) - .saturating_add(Weight::from_parts(0, 45696)) - // Standard Error: 2_438 - .saturating_add(Weight::from_parts(69_045, 0).saturating_mul(s.into())) + // Measured: `2307` + // Estimated: `54662` + // Minimum execution time: 106_166_000 picoseconds. + Weight::from_parts(107_806_373, 54662) + // Standard Error: 6_985 + .saturating_add(Weight::from_parts(18_449, 0).saturating_mul(s.into())) .saturating_add(RocksDbWeight::get().reads(9_u64)) - .saturating_add(RocksDbWeight::get().writes(7_u64)) + .saturating_add(RocksDbWeight::get().writes(8_u64)) } /// Storage: NominationPools PoolMembers (r:1 w:1) /// Proof: NominationPools PoolMembers (max_values: None, max_size: Some(237), added: 2712, mode: MaxEncodedLen) /// Storage: Staking CurrentEra (r:1 w:0) /// Proof: Staking CurrentEra (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) /// Storage: NominationPools BondedPools (r:1 w:1) - /// Proof: NominationPools BondedPools (max_values: None, max_size: Some(164), added: 2639, mode: MaxEncodedLen) + /// Proof: NominationPools BondedPools (max_values: None, max_size: Some(220), added: 2695, mode: MaxEncodedLen) /// Storage: NominationPools SubPoolsStorage (r:1 w:1) /// Proof: NominationPools SubPoolsStorage (max_values: None, max_size: Some(24382), added: 26857, mode: MaxEncodedLen) /// Storage: Staking Bonded (r:1 w:1) @@ -769,7 +832,7 @@ impl WeightInfo for () { /// Storage: NominationPools CounterForReversePoolIdLookup (r:1 w:1) /// Proof: NominationPools CounterForReversePoolIdLookup (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) /// Storage: NominationPools RewardPools (r:1 w:1) - /// Proof: NominationPools RewardPools (max_values: None, max_size: Some(60), added: 2535, mode: MaxEncodedLen) + /// Proof: NominationPools RewardPools (max_values: None, max_size: Some(92), added: 2567, mode: MaxEncodedLen) /// Storage: NominationPools CounterForRewardPools (r:1 w:1) /// Proof: NominationPools CounterForRewardPools (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) /// Storage: NominationPools CounterForSubPoolsStorage (r:1 w:1) @@ -780,16 +843,19 @@ impl WeightInfo for () { /// Proof: NominationPools CounterForBondedPools (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) /// Storage: Staking Payee (r:0 w:1) /// Proof: Staking Payee (max_values: None, max_size: Some(73), added: 2548, mode: MaxEncodedLen) + /// Storage: NominationPools ClaimPermissions (r:0 w:1) + /// Proof: NominationPools ClaimPermissions (max_values: None, max_size: Some(41), added: 2516, mode: MaxEncodedLen) /// The range of component `s` is `[0, 100]`. - fn withdraw_unbonded_kill(_s: u32, ) -> Weight { + fn withdraw_unbonded_kill(s: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `2690` - // Estimated: `68812` - // Minimum execution time: 169_700 nanoseconds. - Weight::from_parts(178_693_541, 0) - .saturating_add(Weight::from_parts(0, 68812)) + // Measured: `2694` + // Estimated: `87714` + // Minimum execution time: 170_047_000 picoseconds. + Weight::from_parts(172_125_770, 87714) + // Standard Error: 2_599 + .saturating_add(Weight::from_parts(9_964, 0).saturating_mul(s.into())) .saturating_add(RocksDbWeight::get().reads(20_u64)) - .saturating_add(RocksDbWeight::get().writes(17_u64)) + .saturating_add(RocksDbWeight::get().writes(18_u64)) } /// Storage: NominationPools LastPoolId (r:1 w:1) /// Proof: NominationPools LastPoolId (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) @@ -822,7 +888,7 @@ impl WeightInfo for () { /// Storage: Balances Locks (r:1 w:1) /// Proof: Balances Locks (max_values: None, max_size: Some(1299), added: 3774, mode: MaxEncodedLen) /// Storage: NominationPools RewardPools (r:1 w:1) - /// Proof: NominationPools RewardPools (max_values: None, max_size: Some(60), added: 2535, mode: MaxEncodedLen) + /// Proof: NominationPools RewardPools (max_values: None, max_size: Some(92), added: 2567, mode: MaxEncodedLen) /// Storage: NominationPools CounterForRewardPools (r:1 w:1) /// Proof: NominationPools CounterForRewardPools (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) /// Storage: NominationPools ReversePoolIdLookup (r:1 w:1) @@ -830,21 +896,20 @@ impl WeightInfo for () { /// Storage: NominationPools CounterForReversePoolIdLookup (r:1 w:1) /// Proof: NominationPools CounterForReversePoolIdLookup (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) /// Storage: NominationPools BondedPools (r:1 w:1) - /// Proof: NominationPools BondedPools (max_values: None, max_size: Some(164), added: 2639, mode: MaxEncodedLen) + /// Proof: NominationPools BondedPools (max_values: None, max_size: Some(220), added: 2695, mode: MaxEncodedLen) /// Storage: Staking Payee (r:0 w:1) /// Proof: Staking Payee (max_values: None, max_size: Some(73), added: 2548, mode: MaxEncodedLen) fn create() -> Weight { // Proof Size summary in bytes: // Measured: `1321` - // Estimated: `31522` - // Minimum execution time: 145_976 nanoseconds. - Weight::from_parts(150_664_000, 0) - .saturating_add(Weight::from_parts(0, 31522)) + // Estimated: `51410` + // Minimum execution time: 149_672_000 picoseconds. + Weight::from_parts(153_613_000, 51410) .saturating_add(RocksDbWeight::get().reads(21_u64)) .saturating_add(RocksDbWeight::get().writes(15_u64)) } /// Storage: NominationPools BondedPools (r:1 w:0) - /// Proof: NominationPools BondedPools (max_values: None, max_size: Some(164), added: 2639, mode: MaxEncodedLen) + /// Proof: NominationPools BondedPools (max_values: None, max_size: Some(220), added: 2695, mode: MaxEncodedLen) /// Storage: Staking Bonded (r:1 w:0) /// Proof: Staking Bonded (max_values: None, max_size: Some(72), added: 2547, mode: MaxEncodedLen) /// Storage: Staking Ledger (r:1 w:0) @@ -870,36 +935,34 @@ impl WeightInfo for () { /// The range of component `n` is `[1, 16]`. fn nominate(n: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `1909` - // Estimated: `21998 + n * (2520 ±0)` - // Minimum execution time: 69_288 nanoseconds. - Weight::from_parts(71_075_293, 0) - .saturating_add(Weight::from_parts(0, 21998)) - // Standard Error: 10_508 - .saturating_add(Weight::from_parts(1_384_674, 0).saturating_mul(n.into())) + // Measured: `1913` + // Estimated: `33934 + n * (2520 ±0)` + // Minimum execution time: 68_892_000 picoseconds. + Weight::from_parts(69_062_946, 33934) + // Standard Error: 6_448 + .saturating_add(Weight::from_parts(1_422_774, 0).saturating_mul(n.into())) .saturating_add(RocksDbWeight::get().reads(12_u64)) .saturating_add(RocksDbWeight::get().reads((1_u64).saturating_mul(n.into()))) .saturating_add(RocksDbWeight::get().writes(5_u64)) .saturating_add(Weight::from_parts(0, 2520).saturating_mul(n.into())) } /// Storage: NominationPools BondedPools (r:1 w:1) - /// Proof: NominationPools BondedPools (max_values: None, max_size: Some(164), added: 2639, mode: MaxEncodedLen) + /// Proof: NominationPools BondedPools (max_values: None, max_size: Some(220), added: 2695, mode: MaxEncodedLen) /// Storage: Staking Bonded (r:1 w:0) /// Proof: Staking Bonded (max_values: None, max_size: Some(72), added: 2547, mode: MaxEncodedLen) /// Storage: Staking Ledger (r:1 w:0) /// Proof: Staking Ledger (max_values: None, max_size: Some(1091), added: 3566, mode: MaxEncodedLen) fn set_state() -> Weight { // Proof Size summary in bytes: - // Measured: `1498` - // Estimated: `8752` - // Minimum execution time: 36_410 nanoseconds. - Weight::from_parts(37_585_000, 0) - .saturating_add(Weight::from_parts(0, 8752)) + // Measured: `1502` + // Estimated: `11778` + // Minimum execution time: 36_447_000 picoseconds. + Weight::from_parts(36_837_000, 11778) .saturating_add(RocksDbWeight::get().reads(3_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } /// Storage: NominationPools BondedPools (r:1 w:0) - /// Proof: NominationPools BondedPools (max_values: None, max_size: Some(164), added: 2639, mode: MaxEncodedLen) + /// Proof: NominationPools BondedPools (max_values: None, max_size: Some(220), added: 2695, mode: MaxEncodedLen) /// Storage: NominationPools Metadata (r:1 w:1) /// Proof: NominationPools Metadata (max_values: None, max_size: Some(270), added: 2745, mode: MaxEncodedLen) /// Storage: NominationPools CounterForMetadata (r:1 w:1) @@ -907,13 +970,12 @@ impl WeightInfo for () { /// The range of component `n` is `[1, 256]`. fn set_metadata(n: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `559` - // Estimated: `5883` - // Minimum execution time: 14_322 nanoseconds. - Weight::from_parts(15_328_204, 0) - .saturating_add(Weight::from_parts(0, 5883)) - // Standard Error: 161 - .saturating_add(Weight::from_parts(1_406, 0).saturating_mul(n.into())) + // Measured: `563` + // Estimated: `8909` + // Minimum execution time: 15_221_000 picoseconds. + Weight::from_parts(15_632_286, 8909) + // Standard Error: 343 + .saturating_add(Weight::from_parts(2_299, 0).saturating_mul(n.into())) .saturating_add(RocksDbWeight::get().reads(3_u64)) .saturating_add(RocksDbWeight::get().writes(2_u64)) } @@ -925,31 +987,31 @@ impl WeightInfo for () { /// Proof: NominationPools MaxPoolMembersPerPool (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) /// Storage: NominationPools MinCreateBond (r:0 w:1) /// Proof: NominationPools MinCreateBond (max_values: Some(1), max_size: Some(16), added: 511, mode: MaxEncodedLen) + /// Storage: NominationPools GlobalMaxCommission (r:0 w:1) + /// Proof: NominationPools GlobalMaxCommission (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) /// Storage: NominationPools MaxPools (r:0 w:1) /// Proof: NominationPools MaxPools (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) fn set_configs() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 5_968 nanoseconds. - Weight::from_parts(6_245_000, 0) - .saturating_add(Weight::from_parts(0, 0)) - .saturating_add(RocksDbWeight::get().writes(5_u64)) + // Minimum execution time: 7_409_000 picoseconds. + Weight::from_parts(7_702_000, 0) + .saturating_add(RocksDbWeight::get().writes(6_u64)) } /// Storage: NominationPools BondedPools (r:1 w:1) - /// Proof: NominationPools BondedPools (max_values: None, max_size: Some(164), added: 2639, mode: MaxEncodedLen) + /// Proof: NominationPools BondedPools (max_values: None, max_size: Some(220), added: 2695, mode: MaxEncodedLen) fn update_roles() -> Weight { // Proof Size summary in bytes: - // Measured: `559` - // Estimated: `2639` - // Minimum execution time: 18_979 nanoseconds. - Weight::from_parts(19_795_000, 0) - .saturating_add(Weight::from_parts(0, 2639)) + // Measured: `563` + // Estimated: `3685` + // Minimum execution time: 20_451_000 picoseconds. + Weight::from_parts(20_703_000, 3685) .saturating_add(RocksDbWeight::get().reads(1_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } /// Storage: NominationPools BondedPools (r:1 w:0) - /// Proof: NominationPools BondedPools (max_values: None, max_size: Some(164), added: 2639, mode: MaxEncodedLen) + /// Proof: NominationPools BondedPools (max_values: None, max_size: Some(220), added: 2695, mode: MaxEncodedLen) /// Storage: Staking Bonded (r:1 w:0) /// Proof: Staking Bonded (max_values: None, max_size: Some(72), added: 2547, mode: MaxEncodedLen) /// Storage: Staking Ledger (r:1 w:0) @@ -968,14 +1030,52 @@ impl WeightInfo for () { /// Proof: VoterList CounterForListNodes (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) fn chill() -> Weight { // Proof Size summary in bytes: - // Measured: `2136` - // Estimated: `20489` - // Minimum execution time: 68_145 nanoseconds. - Weight::from_parts(70_444_000, 0) - .saturating_add(Weight::from_parts(0, 20489)) + // Measured: `2140` + // Estimated: `29455` + // Minimum execution time: 66_001_000 picoseconds. + Weight::from_parts(66_894_000, 29455) .saturating_add(RocksDbWeight::get().reads(9_u64)) .saturating_add(RocksDbWeight::get().writes(5_u64)) } + /// Storage: NominationPools BondedPools (r:1 w:1) + /// Proof: NominationPools BondedPools (max_values: None, max_size: Some(220), added: 2695, mode: MaxEncodedLen) + /// Storage: NominationPools RewardPools (r:1 w:1) + /// Proof: NominationPools RewardPools (max_values: None, max_size: Some(92), added: 2567, mode: MaxEncodedLen) + /// Storage: NominationPools GlobalMaxCommission (r:1 w:0) + /// Proof: NominationPools GlobalMaxCommission (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) + /// Storage: System Account (r:1 w:0) + /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) + fn set_commission() -> Weight { + // Proof Size summary in bytes: + // Measured: `866` + // Estimated: `12324` + // Minimum execution time: 34_011_000 picoseconds. + Weight::from_parts(34_521_000, 12324) + .saturating_add(RocksDbWeight::get().reads(4_u64)) + .saturating_add(RocksDbWeight::get().writes(2_u64)) + } + /// Storage: NominationPools BondedPools (r:1 w:1) + /// Proof: NominationPools BondedPools (max_values: None, max_size: Some(220), added: 2695, mode: MaxEncodedLen) + fn set_commission_max() -> Weight { + // Proof Size summary in bytes: + // Measured: `603` + // Estimated: `3685` + // Minimum execution time: 19_524_000 picoseconds. + Weight::from_parts(19_855_000, 3685) + .saturating_add(RocksDbWeight::get().reads(1_u64)) + .saturating_add(RocksDbWeight::get().writes(1_u64)) + } + /// Storage: NominationPools BondedPools (r:1 w:1) + /// Proof: NominationPools BondedPools (max_values: None, max_size: Some(220), added: 2695, mode: MaxEncodedLen) + fn set_commission_change_rate() -> Weight { + // Proof Size summary in bytes: + // Measured: `563` + // Estimated: `3685` + // Minimum execution time: 20_457_000 picoseconds. + Weight::from_parts(20_698_000, 3685) + .saturating_add(RocksDbWeight::get().reads(1_u64)) + .saturating_add(RocksDbWeight::get().writes(1_u64)) + } /// Storage: NominationPools PoolMembers (r:1 w:0) /// Proof: NominationPools PoolMembers (max_values: None, max_size: Some(237), added: 2712, mode: MaxEncodedLen) /// Storage: NominationPools ClaimPermissions (r:1 w:1) @@ -983,11 +1083,27 @@ impl WeightInfo for () { fn set_claim_permission() -> Weight { // Proof Size summary in bytes: // Measured: `542` - // Estimated: `5228` - // Minimum execution time: 15_112 nanoseconds. - Weight::from_parts(15_897_000, 0) - .saturating_add(Weight::from_parts(0, 5228)) + // Estimated: `7208` + // Minimum execution time: 15_183_000 picoseconds. + Weight::from_parts(15_597_000, 7208) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } + /// Storage: NominationPools BondedPools (r:1 w:0) + /// Proof: NominationPools BondedPools (max_values: None, max_size: Some(220), added: 2695, mode: MaxEncodedLen) + /// Storage: NominationPools RewardPools (r:1 w:1) + /// Proof: NominationPools RewardPools (max_values: None, max_size: Some(92), added: 2567, mode: MaxEncodedLen) + /// Storage: NominationPools GlobalMaxCommission (r:1 w:0) + /// Proof: NominationPools GlobalMaxCommission (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) + /// Storage: System Account (r:1 w:1) + /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) + fn claim_commission() -> Weight { + // Proof Size summary in bytes: + // Measured: `1096` + // Estimated: `12324` + // Minimum execution time: 48_957_000 picoseconds. + Weight::from_parts(50_207_000, 12324) + .saturating_add(RocksDbWeight::get().reads(4_u64)) + .saturating_add(RocksDbWeight::get().writes(2_u64)) + } } diff --git a/frame/nomination-pools/test-staking/src/mock.rs b/frame/nomination-pools/test-staking/src/mock.rs index 85d0dd6c5..0550c8e0c 100644 --- a/frame/nomination-pools/test-staking/src/mock.rs +++ b/frame/nomination-pools/test-staking/src/mock.rs @@ -25,7 +25,7 @@ use frame_support::{ }; use sp_runtime::{ traits::{Convert, IdentityLookup}, - FixedU128, + FixedU128, Perbill, }; type AccountId = u128; @@ -209,6 +209,7 @@ pub fn new_test_ext() -> sp_io::TestExternalities { max_pools: Some(3), max_members_per_pool: Some(5), max_members: Some(3 * 5), + global_max_commission: Some(Perbill::from_percent(90)), } .assimilate_storage(&mut storage) .unwrap(); From d3fd5432ae6f6fcec87844b6eeb21f80ab4e31eb Mon Sep 17 00:00:00 2001 From: Kian Paimani <5588131+kianenigma@users.noreply.github.com> Date: Wed, 15 Mar 2023 10:36:23 -0600 Subject: [PATCH 235/558] Pallet dispatchable+storage doc module. (#13341) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * doc-only pallet * cargo fmt * generics fix for dispatchables * use a module instead * add doc comment warning that the dispatchable functions are generated * clean up * fix typo * hide Instance4-Instance16 from `pallet` module docs * revamp Instance1-16 comment * Document storage types Signed-off-by: Oliver Tale-Yazdi * fmt Signed-off-by: Oliver Tale-Yazdi * remove unused variables * crate::Call => Call Co-authored-by: Bastian Köcher * fix indentation Co-authored-by: Bastian Köcher * remove unneeded block Co-authored-by: Bastian Köcher * don't need a Vec Co-authored-by: Bastian Köcher * add "doc only" to coment Co-authored-by: Bastian Köcher * crate::Call => Call Co-authored-by: Bastian Köcher * cargo fmt --------- Signed-off-by: Oliver Tale-Yazdi Co-authored-by: Sam Johnson Co-authored-by: Oliver Tale-Yazdi Co-authored-by: Bastian Köcher Co-authored-by: parity-processbot <> --- .../procedural/src/pallet/expand/doc_only.rs | 79 +++++++++++++++++++ .../procedural/src/pallet/expand/mod.rs | 3 + frame/support/src/instances.rs | 49 ++++++++---- 3 files changed, 115 insertions(+), 16 deletions(-) create mode 100644 frame/support/procedural/src/pallet/expand/doc_only.rs diff --git a/frame/support/procedural/src/pallet/expand/doc_only.rs b/frame/support/procedural/src/pallet/expand/doc_only.rs new file mode 100644 index 000000000..32c9329f2 --- /dev/null +++ b/frame/support/procedural/src/pallet/expand/doc_only.rs @@ -0,0 +1,79 @@ +// This file is part of Substrate. + +// Copyright (C) 2023 Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +use proc_macro2::Span; + +use crate::pallet::Def; + +pub fn expand_doc_only(def: &mut Def) -> proc_macro2::TokenStream { + let storage_names = def.storages.iter().map(|storage| &storage.ident); + let storage_docs = def.storages.iter().map(|storage| &storage.docs); + let dispatchables = if let Some(call_def) = &def.call { + let type_impl_generics = def.type_impl_generics(Span::call_site()); + call_def + .methods + .iter() + .map(|method| { + let name = &method.name; + let args = &method + .args + .iter() + .map(|(_, arg_name, arg_type)| quote::quote!( #arg_name: #arg_type, )) + .collect::(); + let docs = &method.docs; + let line_2 = + format!(" designed to document the [`{}`][`Call::{}`] variant of", name, name); + quote::quote!( + #( #[doc = #docs] )* + /// + /// --- + /// + /// NOTE: This function is an automatically generated, doc only, uncallable stub. + #[ doc = #line_2 ] + /// the pallet [`Call`] enum. You should not attempt to call this function + /// directly. + pub fn #name<#type_impl_generics>(#args) { unreachable!(); } + ) + }) + .collect::() + } else { + quote::quote!() + }; + + quote::quote!( + /// Auto-generated docs-only module listing all defined storage types for this pallet. + /// Note that members of this module cannot be used directly and are only provided for + /// documentation purposes. + #[cfg(doc)] + pub mod storage_types { + use super::*; + #( + #( #[doc = #storage_docs] )* + pub struct #storage_names(); + )* + } + + /// Auto-generated docs-only module listing all defined dispatchables for this pallet. + /// Note that members of this module cannot be used directly and are only provided for + /// documentation purposes. + #[cfg(doc)] + pub mod dispatchables { + use super::*; + #dispatchables + } + ) +} diff --git a/frame/support/procedural/src/pallet/expand/mod.rs b/frame/support/procedural/src/pallet/expand/mod.rs index 09a25a710..926ab0ec8 100644 --- a/frame/support/procedural/src/pallet/expand/mod.rs +++ b/frame/support/procedural/src/pallet/expand/mod.rs @@ -18,6 +18,7 @@ mod call; mod config; mod constants; +mod doc_only; mod documentation; mod error; mod event; @@ -72,6 +73,7 @@ pub fn expand(mut def: Def) -> proc_macro2::TokenStream { let origins = origin::expand_origins(&mut def); let validate_unsigned = validate_unsigned::expand_validate_unsigned(&mut def); let tt_default_parts = tt_default_parts::expand_tt_default_parts(&mut def); + let doc_only = doc_only::expand_doc_only(&mut def); if get_doc_literals(&def.item.attrs).is_empty() { def.item.attrs.push(syn::parse_quote!( @@ -103,6 +105,7 @@ pub fn expand(mut def: Def) -> proc_macro2::TokenStream { #origins #validate_unsigned #tt_default_parts + #doc_only ); def.item diff --git a/frame/support/src/instances.rs b/frame/support/src/instances.rs index 482051c1f..396018d5c 100644 --- a/frame/support/src/instances.rs +++ b/frame/support/src/instances.rs @@ -31,66 +31,83 @@ //! NOTE: [`frame_support::pallet`] will reexport them inside the module, in order to make them //! accessible to [`frame_support::construct_runtime`]. -/// Instance1 to be used for instantiable pallet define with `pallet` macro. +/// `Instance1` to be used for instantiable palllets defined with the +/// [`#[pallet]`](`frame_support::pallet`) macro. Instances 2-16 are also available but are hidden +/// from docs. #[derive(Clone, Copy, PartialEq, Eq, crate::RuntimeDebugNoBound)] pub struct Instance1; -/// Instance2 to be used for instantiable pallet define with `pallet` macro. +/// `Instance2` to be used for instantiable palllets defined with the `#[pallet]` macro. +#[doc(hidden)] #[derive(Clone, Copy, PartialEq, Eq, crate::RuntimeDebugNoBound)] pub struct Instance2; -/// Instance3 to be used for instantiable pallet define with `pallet` macro. +/// `Instance3` to be used for instantiable palllets defined with the `#[pallet]` macro. +#[doc(hidden)] #[derive(Clone, Copy, PartialEq, Eq, crate::RuntimeDebugNoBound)] pub struct Instance3; -/// Instance4 to be used for instantiable pallet define with `pallet` macro. +/// `Instance4` to be used for instantiable palllets defined with the `#[pallet]` macro. +#[doc(hidden)] #[derive(Clone, Copy, PartialEq, Eq, crate::RuntimeDebugNoBound)] pub struct Instance4; -/// Instance5 to be used for instantiable pallet define with `pallet` macro. +/// `Instance5` to be used for instantiable palllets defined with the `#[pallet]` macro. +#[doc(hidden)] #[derive(Clone, Copy, PartialEq, Eq, crate::RuntimeDebugNoBound)] pub struct Instance5; -/// Instance6 to be used for instantiable pallet define with `pallet` macro. +/// `Instance6` to be used for instantiable palllets defined with the `#[pallet]` macro. +#[doc(hidden)] #[derive(Clone, Copy, PartialEq, Eq, crate::RuntimeDebugNoBound)] pub struct Instance6; -/// Instance7 to be used for instantiable pallet define with `pallet` macro. +/// `Instance7` to be used for instantiable palllets defined with the `#[pallet]` macro. +#[doc(hidden)] #[derive(Clone, Copy, PartialEq, Eq, crate::RuntimeDebugNoBound)] pub struct Instance7; -/// Instance8 to be used for instantiable pallet define with `pallet` macro. +/// `Instance8` to be used for instantiable palllets defined with the `#[pallet]` macro. +#[doc(hidden)] #[derive(Clone, Copy, PartialEq, Eq, crate::RuntimeDebugNoBound)] pub struct Instance8; -/// Instance9 to be used for instantiable pallet define with `pallet` macro. +/// `Instance9` to be used for instantiable palllets defined with the `#[pallet]` macro. +#[doc(hidden)] #[derive(Clone, Copy, PartialEq, Eq, crate::RuntimeDebugNoBound)] pub struct Instance9; -/// Instance10 to be used for instantiable pallet define with `pallet` macro. +/// `Instance10` to be used for instantiable palllets defined with the `#[pallet]` macro. +#[doc(hidden)] #[derive(Clone, Copy, PartialEq, Eq, crate::RuntimeDebugNoBound)] pub struct Instance10; -/// Instance11 to be used for instantiable pallet define with `pallet` macro. +/// `Instance11` to be used for instantiable palllets defined with the `#[pallet]` macro. +#[doc(hidden)] #[derive(Clone, Copy, PartialEq, Eq, crate::RuntimeDebugNoBound)] pub struct Instance11; -/// Instance12 to be used for instantiable pallet define with `pallet` macro. +/// `Instance12` to be used for instantiable palllets defined with the `#[pallet]` macro. +#[doc(hidden)] #[derive(Clone, Copy, PartialEq, Eq, crate::RuntimeDebugNoBound)] pub struct Instance12; -/// Instance13 to be used for instantiable pallet define with `pallet` macro. +/// `Instance13` to be used for instantiable palllets defined with the `#[pallet]` macro. +#[doc(hidden)] #[derive(Clone, Copy, PartialEq, Eq, crate::RuntimeDebugNoBound)] pub struct Instance13; -/// Instance14 to be used for instantiable pallet define with `pallet` macro. +/// `Instance14` to be used for instantiable palllets defined with the `#[pallet]` macro. +#[doc(hidden)] #[derive(Clone, Copy, PartialEq, Eq, crate::RuntimeDebugNoBound)] pub struct Instance14; -/// Instance15 to be used for instantiable pallet define with `pallet` macro. +/// `Instance15` to be used for instantiable palllets defined with the `#[pallet]` macro. +#[doc(hidden)] #[derive(Clone, Copy, PartialEq, Eq, crate::RuntimeDebugNoBound)] pub struct Instance15; -/// Instance16 to be used for instantiable pallet define with `pallet` macro. +/// `Instance16` to be used for instantiable palllets defined with the `#[pallet]` macro. +#[doc(hidden)] #[derive(Clone, Copy, PartialEq, Eq, crate::RuntimeDebugNoBound)] pub struct Instance16; From ba87188cce8c0a11c9542d7363cd5ddd46db2740 Mon Sep 17 00:00:00 2001 From: Mira Ressel Date: Wed, 15 Mar 2023 17:37:34 +0100 Subject: [PATCH 236/558] timestamp gitlab ci job outputs (#13605) * timestamp gitlab ci job outputs Based on previous work by @alvicsam in #13047. * inline timestamp script Some of our jobs don't check out the substrate repo. * include .timestamp in pipelines overriding the default before_script Still not including it in the zombienet jobs, they have their own timestamping anyway. * move timestamp.yml to shared pipeline repo https://gitlab.parity.io/parity/infrastructure/ci_cd/shared --- .gitlab-ci.yml | 4 ++++ scripts/ci/gitlab/pipeline/build.yml | 3 +++ scripts/ci/gitlab/pipeline/test.yml | 5 ++++- 3 files changed, 11 insertions(+), 1 deletion(-) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 94fb3c608..44f98db3b 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -113,6 +113,7 @@ variables: .kubernetes-env: image: "${CI_IMAGE}" before_script: + - !reference [.timestamp, before_script] - !reference [.job-switcher, before_script] - !reference [.prepare-env, before_script] tags: @@ -141,6 +142,7 @@ variables: .docker-env: image: "${CI_IMAGE}" before_script: + - !reference [.timestamp, before_script] - !reference [.job-switcher, before_script] - !reference [.prepare-env, before_script] - !reference [.rust-info-script, script] @@ -310,6 +312,8 @@ include: - local: scripts/ci/gitlab/default-pipeline.yml rules: - if: $PIPELINE != "automatic-crate-publishing" + - project: parity/infrastructure/ci_cd/shared + file: /common/timestamp.yml #### stage: notify diff --git a/scripts/ci/gitlab/pipeline/build.yml b/scripts/ci/gitlab/pipeline/build.yml index 02f25a02a..0a36599c7 100644 --- a/scripts/ci/gitlab/pipeline/build.yml +++ b/scripts/ci/gitlab/pipeline/build.yml @@ -62,6 +62,7 @@ build-linux-substrate: - job: test-linux-stable artifacts: false before_script: + - !reference [.timestamp, before_script] - !reference [.job-switcher, before_script] - mkdir -p ./artifacts/substrate/ - !reference [.rusty-cachier, before_script] @@ -95,6 +96,7 @@ build-linux-substrate: # this variable gets overriden by "rusty-cachier environment inject", use the value as default CARGO_TARGET_DIR: "$CI_PROJECT_DIR/target" before_script: + - !reference [.timestamp, before_script] - !reference [.job-switcher, before_script] - mkdir -p ./artifacts/subkey - !reference [.rusty-cachier, before_script] @@ -120,6 +122,7 @@ build-subkey-macos: # duplicating before_script & script sections from .build-subkey hidden job # to overwrite rusty-cachier integration as it doesn't work on macos before_script: + # skip timestamp script, the osx bash doesn't support printf %()T - !reference [.job-switcher, before_script] - mkdir -p ./artifacts/subkey script: diff --git a/scripts/ci/gitlab/pipeline/test.yml b/scripts/ci/gitlab/pipeline/test.yml index 49dbb194f..fd031d9aa 100644 --- a/scripts/ci/gitlab/pipeline/test.yml +++ b/scripts/ci/gitlab/pipeline/test.yml @@ -83,6 +83,7 @@ cargo-check-benches: - .collect-artifacts - .pipeline-stopper-artifacts before_script: + - !reference [.timestamp, before_script] # perform rusty-cachier operations before any further modifications to the git repo to make cargo feel cheated not so much - !reference [.rust-info-script, script] - !reference [.job-switcher, before_script] @@ -145,7 +146,8 @@ node-bench-regression-guard: artifacts: true variables: CI_IMAGE: "paritytech/node-bench-regression-guard:latest" - before_script: [""] + before_script: + - !reference [.timestamp, before_script] script: - echo "------- IMPORTANT -------" - echo "node-bench-regression-guard depends on the results of a cargo-check-benches job" @@ -419,6 +421,7 @@ cargo-check-each-crate-macos: - .collect-artifacts - .pipeline-stopper-artifacts before_script: + # skip timestamp script, the osx bash doesn't support printf %()T - !reference [.job-switcher, before_script] - !reference [.rust-info-script, script] - !reference [.pipeline-stopper-vars, script] From bd52212085051561aee5784ff48b295894b6509d Mon Sep 17 00:00:00 2001 From: Alexandru Vasile <60601340+lexnv@users.noreply.github.com> Date: Wed, 15 Mar 2023 19:49:28 +0200 Subject: [PATCH 237/558] Metadata V15: Expose API to fetch metadata for version (#13287) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * impl_runtime_apis: Generate getters for `metadata_at` functions Signed-off-by: Alexandru Vasile * runtime: Implement new `Metadata` runtime trait Signed-off-by: Alexandru Vasile * runtime: Move `metadata_at` functions to construct_runtime macro Signed-off-by: Alexandru Vasile * contruct_runtime: Use `OpaqueMetadata` from hidden imports Signed-off-by: Alexandru Vasile * Adjust testing Signed-off-by: Alexandru Vasile * frame/tests: Add tests for the new API Signed-off-by: Alexandru Vasile * frame/tests: Adjust metdata naming Signed-off-by: Alexandru Vasile * frame/support: Expose `metadata-v14` feature flag Signed-off-by: Alexandru Vasile * frame/support: Expose metadata only under feature flags Signed-off-by: Alexandru Vasile * frame/support: Expose v14 metadata by default Signed-off-by: Alexandru Vasile * frame/support: Expose metadata feature for testing Signed-off-by: Alexandru Vasile * frame/support: Test metadata under different feature flags Signed-off-by: Alexandru Vasile * Update primitives/api/src/lib.rs Co-authored-by: Bastian Köcher * Update primitives/api/src/lib.rs Co-authored-by: Bastian Köcher * client/tests: Adjust testing to reflect trait Metadata change Signed-off-by: Alexandru Vasile * frame/metadata-ir: Add intermediate representation types for metadata Signed-off-by: Alexandru Vasile * frame/metadata-ir: Convert metadata to V14 Signed-off-by: Alexandru Vasile * frame/metadata-ir: Add API to convert metadata to multiple versions Signed-off-by: Alexandru Vasile * frame/metadata-ir: Expose V14 under feature flag Signed-off-by: Alexandru Vasile * frame/support: Adjust to metadata IR Signed-off-by: Alexandru Vasile * frame/support: More adjustments Signed-off-by: Alexandru Vasile * frame/support: Guard v14 details under feature flag Signed-off-by: Alexandru Vasile * frame/support: Adjust testing Signed-off-by: Alexandru Vasile * CI: Ensure `quick-benchmarks` uses `metadata-v14` Signed-off-by: Alexandru Vasile * frame/support: Use `metadata-v14` for benchmarks Signed-off-by: Alexandru Vasile * Adjust cargo fmt Signed-off-by: Alexandru Vasile * kitchensink-runtime: Add feature flag for `metadata-v14` Signed-off-by: Alexandru Vasile * frame/support/test: Adjust testing Signed-off-by: Alexandru Vasile * frame/support/test: Check crates locally Signed-off-by: Alexandru Vasile * Activate metadata-v14 for pallets Signed-off-by: Alexandru Vasile * Remove metadata-v14 feature flag Signed-off-by: Alexandru Vasile * frame/metadata_ir: Move `api.rs` to `mod.rs` Signed-off-by: Alexandru Vasile * frame/support: Handle latest metadata conversion via IR Signed-off-by: Alexandru Vasile * frame/tests: Add constant for metadata version 14 Signed-off-by: Alexandru Vasile * frame/support/test: Fix merge conflict Signed-off-by: Alexandru Vasile * Update frame/support/Cargo.toml Co-authored-by: Bastian Köcher * Update frame/support/src/metadata_ir/mod.rs Co-authored-by: Bastian Köcher * Update frame/support/test/Cargo.toml Co-authored-by: Bastian Köcher * Update primitives/api/src/lib.rs Co-authored-by: Bastian Köcher * frame/metadata: Collect pallet documentation for MetadataIR Signed-off-by: Alexandru Vasile * frame/tests: Check pallet documentation is propagated to MetadataIR Signed-off-by: Alexandru Vasile * frame/support: Improve documentation Signed-off-by: Alexandru Vasile --------- Signed-off-by: Alexandru Vasile Co-authored-by: parity-processbot <> Co-authored-by: Bastian Köcher --- bin/node-template/runtime/src/lib.rs | 8 + bin/node/runtime/src/lib.rs | 8 + client/rpc-spec-v2/src/chain_head/tests.rs | 2 +- client/rpc/src/state/tests.rs | 2 +- .../src/construct_runtime/expand/metadata.rs | 43 ++- frame/support/procedural/src/lib.rs | 6 +- .../procedural/src/pallet/expand/call.rs | 2 +- .../procedural/src/pallet/expand/constants.rs | 4 +- .../src/pallet/expand/documentation.rs | 4 +- .../src/pallet/expand/pallet_struct.rs | 6 +- .../procedural/src/pallet/expand/storage.rs | 4 +- .../procedural/src/storage/metadata.rs | 26 +- frame/support/procedural/src/storage/mod.rs | 14 +- frame/support/src/dispatch.rs | 16 +- frame/support/src/hash.rs | 18 +- frame/support/src/lib.rs | 92 ++--- frame/support/src/metadata_ir/mod.rs | 82 +++++ frame/support/src/metadata_ir/types.rs | 329 ++++++++++++++++++ frame/support/src/metadata_ir/v14.rs | 158 +++++++++ .../support/src/storage/types/counted_map.rs | 20 +- frame/support/src/storage/types/double_map.rs | 30 +- frame/support/src/storage/types/key.rs | 6 +- frame/support/src/storage/types/map.rs | 26 +- frame/support/src/storage/types/mod.rs | 12 +- frame/support/src/storage/types/nmap.rs | 70 ++-- frame/support/src/storage/types/value.rs | 22 +- frame/support/test/tests/decl_storage.rs | 207 +++++------ frame/support/test/tests/instance.rs | 32 +- frame/support/test/tests/pallet.rs | 40 +++ primitives/api/src/lib.rs | 12 + test-utils/runtime/src/lib.rs | 17 +- 31 files changed, 1002 insertions(+), 316 deletions(-) create mode 100644 frame/support/src/metadata_ir/mod.rs create mode 100644 frame/support/src/metadata_ir/types.rs create mode 100644 frame/support/src/metadata_ir/v14.rs diff --git a/bin/node-template/runtime/src/lib.rs b/bin/node-template/runtime/src/lib.rs index ac01aa95f..41871c06b 100644 --- a/bin/node-template/runtime/src/lib.rs +++ b/bin/node-template/runtime/src/lib.rs @@ -354,6 +354,14 @@ impl_runtime_apis! { fn metadata() -> OpaqueMetadata { OpaqueMetadata::new(Runtime::metadata().into()) } + + fn metadata_at_version(version: u32) -> Option { + Runtime::metadata_at_version(version) + } + + fn metadata_versions() -> sp_std::vec::Vec { + Runtime::metadata_versions() + } } impl sp_block_builder::BlockBuilder for Runtime { diff --git a/bin/node/runtime/src/lib.rs b/bin/node/runtime/src/lib.rs index d8426d3b3..5d1dea02e 100644 --- a/bin/node/runtime/src/lib.rs +++ b/bin/node/runtime/src/lib.rs @@ -1942,6 +1942,14 @@ impl_runtime_apis! { fn metadata() -> OpaqueMetadata { OpaqueMetadata::new(Runtime::metadata().into()) } + + fn metadata_at_version(version: u32) -> Option { + Runtime::metadata_at_version(version) + } + + fn metadata_versions() -> sp_std::vec::Vec { + Runtime::metadata_versions() + } } impl sp_block_builder::BlockBuilder for Runtime { diff --git a/client/rpc-spec-v2/src/chain_head/tests.rs b/client/rpc-spec-v2/src/chain_head/tests.rs index 0886efa94..fcd906dcf 100644 --- a/client/rpc-spec-v2/src/chain_head/tests.rs +++ b/client/rpc-spec-v2/src/chain_head/tests.rs @@ -170,7 +170,7 @@ async fn follow_with_runtime() { let runtime_str = "{\"specName\":\"test\",\"implName\":\"parity-test\",\"authoringVersion\":1,\ \"specVersion\":2,\"implVersion\":2,\"apis\":[[\"0xdf6acb689907609b\",4],\ - [\"0x37e397fc7c91f5e4\",1],[\"0xd2bc9897eed08f15\",3],[\"0x40fe3ad401f8959a\",6],\ + [\"0x37e397fc7c91f5e4\",2],[\"0xd2bc9897eed08f15\",3],[\"0x40fe3ad401f8959a\",6],\ [\"0xc6e9a76309f39b09\",1],[\"0xdd718d5cc53262d4\",1],[\"0xcbca25e39f142387\",2],\ [\"0xf78b278be53f454c\",2],[\"0xab3c0572291feb8b\",1],[\"0xbc9d89904f5b923f\",1]],\ \"transactionVersion\":1,\"stateVersion\":1}"; diff --git a/client/rpc/src/state/tests.rs b/client/rpc/src/state/tests.rs index 53f8f1d48..1ccc609e4 100644 --- a/client/rpc/src/state/tests.rs +++ b/client/rpc/src/state/tests.rs @@ -484,7 +484,7 @@ async fn should_return_runtime_version() { let result = "{\"specName\":\"test\",\"implName\":\"parity-test\",\"authoringVersion\":1,\ \"specVersion\":2,\"implVersion\":2,\"apis\":[[\"0xdf6acb689907609b\",4],\ - [\"0x37e397fc7c91f5e4\",1],[\"0xd2bc9897eed08f15\",3],[\"0x40fe3ad401f8959a\",6],\ + [\"0x37e397fc7c91f5e4\",2],[\"0xd2bc9897eed08f15\",3],[\"0x40fe3ad401f8959a\",6],\ [\"0xc6e9a76309f39b09\",1],[\"0xdd718d5cc53262d4\",1],[\"0xcbca25e39f142387\",2],\ [\"0xf78b278be53f454c\",2],[\"0xab3c0572291feb8b\",1],[\"0xbc9d89904f5b923f\",1]],\ \"transactionVersion\":1,\"stateVersion\":1}"; diff --git a/frame/support/procedural/src/construct_runtime/expand/metadata.rs b/frame/support/procedural/src/construct_runtime/expand/metadata.rs index e99961217..ba6a621af 100644 --- a/frame/support/procedural/src/construct_runtime/expand/metadata.rs +++ b/frame/support/procedural/src/construct_runtime/expand/metadata.rs @@ -48,6 +48,7 @@ pub fn expand_runtime_metadata( let event = expand_pallet_metadata_events(&filtered_names, runtime, scrate, decl); let constants = expand_pallet_metadata_constants(runtime, decl); let errors = expand_pallet_metadata_errors(runtime, decl); + let docs = expand_pallet_metadata_docs(runtime, decl); let attr = decl.cfg_pattern.iter().fold(TokenStream::new(), |acc, pattern| { let attr = TokenStream::from_str(&format!("#[cfg({})]", pattern.original())) .expect("was successfully parsed before; qed"); @@ -59,7 +60,7 @@ pub fn expand_runtime_metadata( quote! { #attr - #scrate::metadata::PalletMetadata { + #scrate::metadata_ir::PalletMetadataIR { name: stringify!(#name), index: #index, storage: #storage, @@ -67,6 +68,7 @@ pub fn expand_runtime_metadata( event: #event, constants: #constants, error: #errors, + docs: #docs, } } }) @@ -74,10 +76,10 @@ pub fn expand_runtime_metadata( quote! { impl #runtime { - pub fn metadata() -> #scrate::metadata::RuntimeMetadataPrefixed { - #scrate::metadata::RuntimeMetadataLastVersion::new( - #scrate::sp_std::vec![ #(#pallets),* ], - #scrate::metadata::ExtrinsicMetadata { + fn metadata_ir() -> #scrate::metadata_ir::MetadataIR { + #scrate::metadata_ir::MetadataIR { + pallets: #scrate::sp_std::vec![ #(#pallets),* ], + extrinsic: #scrate::metadata_ir::ExtrinsicMetadataIR { ty: #scrate::scale_info::meta_type::<#extrinsic>(), version: <#extrinsic as #scrate::sp_runtime::traits::ExtrinsicMetadata>::VERSION, signed_extensions: < @@ -86,15 +88,29 @@ pub fn expand_runtime_metadata( >::SignedExtensions as #scrate::sp_runtime::traits::SignedExtension >::metadata() .into_iter() - .map(|meta| #scrate::metadata::SignedExtensionMetadata { + .map(|meta| #scrate::metadata_ir::SignedExtensionMetadataIR { identifier: meta.identifier, ty: meta.ty, additional_signed: meta.additional_signed, }) .collect(), }, - #scrate::scale_info::meta_type::<#runtime>() - ).into() + ty: #scrate::scale_info::meta_type::<#runtime>() + } + } + + pub fn metadata() -> #scrate::metadata::RuntimeMetadataPrefixed { + #scrate::metadata_ir::into_latest(#runtime::metadata_ir()) + } + + pub fn metadata_at_version(version: u32) -> Option<#scrate::OpaqueMetadata> { + #scrate::metadata_ir::into_version(#runtime::metadata_ir(), version).map(|prefixed| { + #scrate::OpaqueMetadata::new(prefixed.into()) + }) + } + + pub fn metadata_versions() -> #scrate::sp_std::vec::Vec { + #scrate::metadata_ir::supported_versions() } } } @@ -157,7 +173,7 @@ fn expand_pallet_metadata_events( quote! { Some( - #scrate::metadata::PalletEventMetadata { + #scrate::metadata_ir::PalletEventMetadataIR { ty: #scrate::scale_info::meta_type::<#pallet_event>() } ) @@ -184,3 +200,12 @@ fn expand_pallet_metadata_errors(runtime: &Ident, decl: &Pallet) -> TokenStream #path::Pallet::<#runtime #(, #path::#instance)*>::error_metadata() } } + +fn expand_pallet_metadata_docs(runtime: &Ident, decl: &Pallet) -> TokenStream { + let path = &decl.path; + let instance = decl.instance.as_ref().into_iter(); + + quote! { + #path::Pallet::<#runtime #(, #path::#instance)*>::pallet_documentation_metadata() + } +} diff --git a/frame/support/procedural/src/lib.rs b/frame/support/procedural/src/lib.rs index 1d5dde20e..0515a4b7d 100644 --- a/frame/support/procedural/src/lib.rs +++ b/frame/support/procedural/src/lib.rs @@ -495,7 +495,7 @@ pub fn construct_runtime(input: TokenStream) -> TokenStream { /// #[doc = include_str!("../README.md")] /// #[pallet_doc("../doc1.md")] /// #[pallet_doc("../doc2.md")] -/// pub struct Pallet(_); +/// pub mod pallet {} /// ``` /// /// The runtime metadata for this pallet contains the following @@ -514,7 +514,7 @@ pub fn construct_runtime(input: TokenStream) -> TokenStream { /// /// Documentation for pallet 1 /// /// Documentation for pallet 2 /// /// Content of README.md -/// pub struct Pallet(_); +/// pub mod pallet {} /// ``` /// /// If you want to specify the file from which the documentation is loaded, you can use the @@ -531,7 +531,7 @@ pub fn construct_runtime(input: TokenStream) -> TokenStream { /// /// This approach is beneficial when you use the `include_str` macro at the beginning of the file /// and want that documentation to extend to the runtime metadata, without reiterating the -/// documentation on the module itself. +/// documentation on the pallet module itself. #[proc_macro_attribute] pub fn pallet(attr: TokenStream, item: TokenStream) -> TokenStream { pallet::pallet(attr, item) diff --git a/frame/support/procedural/src/pallet/expand/call.rs b/frame/support/procedural/src/pallet/expand/call.rs index 3db454eb6..7672609a9 100644 --- a/frame/support/procedural/src/pallet/expand/call.rs +++ b/frame/support/procedural/src/pallet/expand/call.rs @@ -362,7 +362,7 @@ pub fn expand_call(def: &mut Def) -> proc_macro2::TokenStream { impl<#type_impl_gen> #pallet_ident<#type_use_gen> #where_clause { #[doc(hidden)] - pub fn call_functions() -> #frame_support::metadata::PalletCallMetadata { + pub fn call_functions() -> #frame_support::metadata_ir::PalletCallMetadataIR { #frame_support::scale_info::meta_type::<#call_ident<#type_use_gen>>().into() } } diff --git a/frame/support/procedural/src/pallet/expand/constants.rs b/frame/support/procedural/src/pallet/expand/constants.rs index 21ac1de36..21fa492a7 100644 --- a/frame/support/procedural/src/pallet/expand/constants.rs +++ b/frame/support/procedural/src/pallet/expand/constants.rs @@ -85,7 +85,7 @@ pub fn expand_constants(def: &mut Def) -> proc_macro2::TokenStream { let default_byte_impl = &const_.default_byte_impl; quote::quote!({ - #frame_support::metadata::PalletConstantMetadata { + #frame_support::metadata_ir::PalletConstantMetadataIR { name: #ident_str, ty: #frame_support::scale_info::meta_type::<#const_type>(), value: { #default_byte_impl }, @@ -99,7 +99,7 @@ pub fn expand_constants(def: &mut Def) -> proc_macro2::TokenStream { #[doc(hidden)] pub fn pallet_constants_metadata() - -> #frame_support::sp_std::vec::Vec<#frame_support::metadata::PalletConstantMetadata> + -> #frame_support::sp_std::vec::Vec<#frame_support::metadata_ir::PalletConstantMetadataIR> { #frame_support::sp_std::vec![ #( #consts ),* ] } diff --git a/frame/support/procedural/src/pallet/expand/documentation.rs b/frame/support/procedural/src/pallet/expand/documentation.rs index e158448a8..1aa46cf57 100644 --- a/frame/support/procedural/src/pallet/expand/documentation.rs +++ b/frame/support/procedural/src/pallet/expand/documentation.rs @@ -145,7 +145,7 @@ impl ToTokens for DocMetaValue { /// Implement a `pallet_documentation_metadata` function to fetch the /// documentation that is included in the metadata. /// -/// The documentation is placed at the top of the module similar to: +/// The documentation is placed on the pallet similar to: /// /// ```ignore /// #[pallet] @@ -163,7 +163,7 @@ impl ToTokens for DocMetaValue { /// which is the file path that holds the documentation to be added to the metadata. /// /// Unlike the `doc` attribute, the documentation provided to the `proc_macro` attribute is -/// not inserted at the beginning of the module. +/// not added to the pallet. pub fn expand_documentation(def: &mut Def) -> proc_macro2::TokenStream { let frame_support = &def.frame_support; let type_impl_gen = &def.type_impl_generics(proc_macro2::Span::call_site()); diff --git a/frame/support/procedural/src/pallet/expand/pallet_struct.rs b/frame/support/procedural/src/pallet/expand/pallet_struct.rs index 27fe9d340..7acfb9090 100644 --- a/frame/support/procedural/src/pallet/expand/pallet_struct.rs +++ b/frame/support/procedural/src/pallet/expand/pallet_struct.rs @@ -82,8 +82,8 @@ pub fn expand_pallet_struct(def: &mut Def) -> proc_macro2::TokenStream { quote::quote_spanned!(def.pallet_struct.attr_span => impl<#type_impl_gen> #pallet_ident<#type_use_gen> #config_where_clause { #[doc(hidden)] - pub fn error_metadata() -> Option<#frame_support::metadata::PalletErrorMetadata> { - Some(#frame_support::metadata::PalletErrorMetadata { + pub fn error_metadata() -> Option<#frame_support::metadata_ir::PalletErrorMetadataIR> { + Some(#frame_support::metadata_ir::PalletErrorMetadataIR { ty: #frame_support::scale_info::meta_type::<#error_ident<#type_use_gen>>() }) } @@ -93,7 +93,7 @@ pub fn expand_pallet_struct(def: &mut Def) -> proc_macro2::TokenStream { quote::quote_spanned!(def.pallet_struct.attr_span => impl<#type_impl_gen> #pallet_ident<#type_use_gen> #config_where_clause { #[doc(hidden)] - pub fn error_metadata() -> Option<#frame_support::metadata::PalletErrorMetadata> { + pub fn error_metadata() -> Option<#frame_support::metadata_ir::PalletErrorMetadataIR> { None } } diff --git a/frame/support/procedural/src/pallet/expand/storage.rs b/frame/support/procedural/src/pallet/expand/storage.rs index 05d61bb52..8ba9ee768 100644 --- a/frame/support/procedural/src/pallet/expand/storage.rs +++ b/frame/support/procedural/src/pallet/expand/storage.rs @@ -639,8 +639,8 @@ pub fn expand_storages(def: &mut Def) -> proc_macro2::TokenStream { #completed_where_clause { #[doc(hidden)] - pub fn storage_metadata() -> #frame_support::metadata::PalletStorageMetadata { - #frame_support::metadata::PalletStorageMetadata { + pub fn storage_metadata() -> #frame_support::metadata_ir::PalletStorageMetadataIR { + #frame_support::metadata_ir::PalletStorageMetadataIR { prefix: < ::PalletInfo as #frame_support::traits::PalletInfo diff --git a/frame/support/procedural/src/storage/metadata.rs b/frame/support/procedural/src/storage/metadata.rs index 3e3e0576f..5561d0564 100644 --- a/frame/support/procedural/src/storage/metadata.rs +++ b/frame/support/procedural/src/storage/metadata.rs @@ -27,7 +27,7 @@ fn storage_line_metadata_type(scrate: &TokenStream, line: &StorageLineDefExt) -> match &line.storage_type { StorageLineTypeDef::Simple(_) => { quote! { - #scrate::metadata::StorageEntryType::Plain( + #scrate::metadata_ir::StorageEntryTypeIR::Plain( #scrate::scale_info::meta_type::<#value_type>() ) } @@ -36,8 +36,8 @@ fn storage_line_metadata_type(scrate: &TokenStream, line: &StorageLineDefExt) -> let hasher = map.hasher.into_metadata(); let key = &map.key; quote! { - #scrate::metadata::StorageEntryType::Map { - hashers: #scrate::sp_std::vec! [ #scrate::metadata::#hasher ], + #scrate::metadata_ir::StorageEntryTypeIR::Map { + hashers: #scrate::sp_std::vec! [ #scrate::metadata_ir::#hasher ], key: #scrate::scale_info::meta_type::<#key>(), value: #scrate::scale_info::meta_type::<#value_type>(), } @@ -49,10 +49,10 @@ fn storage_line_metadata_type(scrate: &TokenStream, line: &StorageLineDefExt) -> let key1 = &map.key1; let key2 = &map.key2; quote! { - #scrate::metadata::StorageEntryType::Map { + #scrate::metadata_ir::StorageEntryTypeIR::Map { hashers: #scrate::sp_std::vec! [ - #scrate::metadata::#hasher1, - #scrate::metadata::#hasher2, + #scrate::metadata_ir::#hasher1, + #scrate::metadata_ir::#hasher2, ], key: #scrate::scale_info::meta_type::<(#key1, #key2)>(), value: #scrate::scale_info::meta_type::<#value_type>(), @@ -67,9 +67,9 @@ fn storage_line_metadata_type(scrate: &TokenStream, line: &StorageLineDefExt) -> .map(|hasher| hasher.to_storage_hasher_struct()) .collect::>(); quote! { - #scrate::metadata::StorageEntryType::Map { + #scrate::metadata_ir::StorageEntryTypeIR::Map { hashers: #scrate::sp_std::vec! [ - #( #scrate::metadata::StorageHasher::#hashers, )* + #( #scrate::metadata_ir::StorageHasherIR::#hashers, )* ], key: #scrate::scale_info::meta_type::<#key_tuple>(), value: #scrate::scale_info::meta_type::<#value_type>(), @@ -159,9 +159,9 @@ pub fn impl_metadata(def: &DeclStorageDefExt) -> TokenStream { let str_name = line.name.to_string(); let modifier = if line.is_option { - quote!(#scrate::metadata::StorageEntryModifier::Optional) + quote!(#scrate::metadata_ir::StorageEntryModifierIR::Optional) } else { - quote!(#scrate::metadata::StorageEntryModifier::Default) + quote!(#scrate::metadata_ir::StorageEntryModifierIR::Default) }; let ty = storage_line_metadata_type(scrate, line); @@ -172,7 +172,7 @@ pub fn impl_metadata(def: &DeclStorageDefExt) -> TokenStream { let docs = get_doc_literals(&line.attrs); let entry = quote! { - #scrate::metadata::StorageEntryMetadata { + #scrate::metadata_ir::StorageEntryMetadataIR { name: #str_name, modifier: #modifier, ty: #ty, @@ -194,7 +194,7 @@ pub fn impl_metadata(def: &DeclStorageDefExt) -> TokenStream { }; let store_metadata = quote!( - #scrate::metadata::PalletStorageMetadata { + #scrate::metadata_ir::PalletStorageMetadataIR { prefix: #prefix, entries: #scrate::sp_std::vec![ #entries ], } @@ -209,7 +209,7 @@ pub fn impl_metadata(def: &DeclStorageDefExt) -> TokenStream { impl #module_impl #module_struct #where_clause { #[doc(hidden)] - pub fn storage_metadata() -> #scrate::metadata::PalletStorageMetadata { + pub fn storage_metadata() -> #scrate::metadata_ir::PalletStorageMetadataIR { #store_metadata } } diff --git a/frame/support/procedural/src/storage/mod.rs b/frame/support/procedural/src/storage/mod.rs index 1e48a1fbb..3d3b1443d 100644 --- a/frame/support/procedural/src/storage/mod.rs +++ b/frame/support/procedural/src/storage/mod.rs @@ -454,13 +454,13 @@ impl HasherKind { fn into_metadata(&self) -> proc_macro2::TokenStream { match self { - HasherKind::Blake2_256 => quote!(StorageHasher::Blake2_256), - HasherKind::Blake2_128 => quote!(StorageHasher::Blake2_128), - HasherKind::Blake2_128Concat => quote!(StorageHasher::Blake2_128Concat), - HasherKind::Twox256 => quote!(StorageHasher::Twox256), - HasherKind::Twox128 => quote!(StorageHasher::Twox128), - HasherKind::Twox64Concat => quote!(StorageHasher::Twox64Concat), - HasherKind::Identity => quote!(StorageHasher::Identity), + HasherKind::Blake2_256 => quote!(StorageHasherIR::Blake2_256), + HasherKind::Blake2_128 => quote!(StorageHasherIR::Blake2_128), + HasherKind::Blake2_128Concat => quote!(StorageHasherIR::Blake2_128Concat), + HasherKind::Twox256 => quote!(StorageHasherIR::Twox256), + HasherKind::Twox128 => quote!(StorageHasherIR::Twox128), + HasherKind::Twox64Concat => quote!(StorageHasherIR::Twox64Concat), + HasherKind::Identity => quote!(StorageHasherIR::Identity), } } } diff --git a/frame/support/src/dispatch.rs b/frame/support/src/dispatch.rs index c5f4cc60c..390555b02 100644 --- a/frame/support/src/dispatch.rs +++ b/frame/support/src/dispatch.rs @@ -2977,7 +2977,7 @@ macro_rules! __dispatch_impl_metadata { { #[doc(hidden)] #[allow(dead_code)] - pub fn call_functions() -> $crate::metadata::PalletCallMetadata { + pub fn call_functions() -> $crate::metadata_ir::PalletCallMetadataIR { $crate::scale_info::meta_type::<$call_type<$trait_instance $(, $instance)?>>().into() } } @@ -2998,7 +2998,7 @@ macro_rules! __impl_error_metadata { { #[doc(hidden)] #[allow(dead_code)] - pub fn error_metadata() -> Option<$crate::metadata::PalletErrorMetadata> { + pub fn error_metadata() -> Option<$crate::metadata_ir::PalletErrorMetadataIR> { None } } @@ -3013,8 +3013,8 @@ macro_rules! __impl_error_metadata { { #[doc(hidden)] #[allow(dead_code)] - pub fn error_metadata() -> Option<$crate::metadata::PalletErrorMetadata> { - Some($crate::metadata::PalletErrorMetadata { + pub fn error_metadata() -> Option<$crate::metadata_ir::PalletErrorMetadataIR> { + Some($crate::metadata_ir::PalletErrorMetadataIR { ty: $crate::scale_info::meta_type::<$( $error_type )*>() }) } @@ -3109,7 +3109,7 @@ macro_rules! __impl_module_constants_metadata { { #[doc(hidden)] #[allow(dead_code)] - pub fn pallet_constants_metadata() -> $crate::sp_std::vec::Vec<$crate::metadata::PalletConstantMetadata> { + pub fn pallet_constants_metadata() -> $crate::sp_std::vec::Vec<$crate::metadata_ir::PalletConstantMetadataIR> { // Create the `ByteGetter`s $( #[allow(non_upper_case_types)] @@ -3133,7 +3133,7 @@ macro_rules! __impl_module_constants_metadata { )* $crate::sp_std::vec![ $( - $crate::metadata::PalletConstantMetadata { + $crate::metadata_ir::PalletConstantMetadataIR { name: stringify!($name), ty: $crate::scale_info::meta_type::<$type>(), value: $default_byte_name::<$const_trait_instance $(, $const_instance)?>( @@ -3207,7 +3207,7 @@ mod tests { use super::*; use crate::{ dispatch::{DispatchClass, DispatchInfo, Pays}, - metadata::*, + metadata_ir::*, traits::{ CallerTrait, CrateVersion, Get, GetCallName, IntegrityTest, OnFinalize, OnIdle, OnInitialize, OnRuntimeUpgrade, PalletInfo, @@ -3405,7 +3405,7 @@ mod tests { fn module_json_metadata() { let metadata = Module::::call_functions(); let expected_metadata = - PalletCallMetadata { ty: scale_info::meta_type::>() }; + PalletCallMetadataIR { ty: scale_info::meta_type::>() }; assert_eq!(expected_metadata, metadata); } diff --git a/frame/support/src/hash.rs b/frame/support/src/hash.rs index bf9eb2f88..115ce605d 100644 --- a/frame/support/src/hash.rs +++ b/frame/support/src/hash.rs @@ -17,7 +17,7 @@ //! Hash utilities. -use crate::metadata; +use crate::metadata_ir; use codec::{Codec, MaxEncodedLen}; use sp_io::hashing::{blake2_128, blake2_256, twox_128, twox_256, twox_64}; use sp_std::prelude::Vec; @@ -59,7 +59,7 @@ impl Hashable for T { /// Hasher to use to hash keys to insert to storage. pub trait StorageHasher: 'static { - const METADATA: metadata::StorageHasher; + const METADATA: metadata_ir::StorageHasherIR; type Output: AsRef<[u8]>; fn hash(x: &[u8]) -> Self::Output; @@ -80,7 +80,7 @@ pub trait ReversibleStorageHasher: StorageHasher { /// Store the key directly. pub struct Identity; impl StorageHasher for Identity { - const METADATA: metadata::StorageHasher = metadata::StorageHasher::Identity; + const METADATA: metadata_ir::StorageHasherIR = metadata_ir::StorageHasherIR::Identity; type Output = Vec; fn hash(x: &[u8]) -> Vec { x.to_vec() @@ -98,7 +98,7 @@ impl ReversibleStorageHasher for Identity { /// Hash storage keys with `concat(twox64(key), key)` pub struct Twox64Concat; impl StorageHasher for Twox64Concat { - const METADATA: metadata::StorageHasher = metadata::StorageHasher::Twox64Concat; + const METADATA: metadata_ir::StorageHasherIR = metadata_ir::StorageHasherIR::Twox64Concat; type Output = Vec; fn hash(x: &[u8]) -> Vec { twox_64(x).iter().chain(x.iter()).cloned().collect::>() @@ -120,7 +120,7 @@ impl ReversibleStorageHasher for Twox64Concat { /// Hash storage keys with `concat(blake2_128(key), key)` pub struct Blake2_128Concat; impl StorageHasher for Blake2_128Concat { - const METADATA: metadata::StorageHasher = metadata::StorageHasher::Blake2_128Concat; + const METADATA: metadata_ir::StorageHasherIR = metadata_ir::StorageHasherIR::Blake2_128Concat; type Output = Vec; fn hash(x: &[u8]) -> Vec { blake2_128(x).iter().chain(x.iter()).cloned().collect::>() @@ -142,7 +142,7 @@ impl ReversibleStorageHasher for Blake2_128Concat { /// Hash storage keys with blake2 128 pub struct Blake2_128; impl StorageHasher for Blake2_128 { - const METADATA: metadata::StorageHasher = metadata::StorageHasher::Blake2_128; + const METADATA: metadata_ir::StorageHasherIR = metadata_ir::StorageHasherIR::Blake2_128; type Output = [u8; 16]; fn hash(x: &[u8]) -> [u8; 16] { blake2_128(x) @@ -155,7 +155,7 @@ impl StorageHasher for Blake2_128 { /// Hash storage keys with blake2 256 pub struct Blake2_256; impl StorageHasher for Blake2_256 { - const METADATA: metadata::StorageHasher = metadata::StorageHasher::Blake2_256; + const METADATA: metadata_ir::StorageHasherIR = metadata_ir::StorageHasherIR::Blake2_256; type Output = [u8; 32]; fn hash(x: &[u8]) -> [u8; 32] { blake2_256(x) @@ -168,7 +168,7 @@ impl StorageHasher for Blake2_256 { /// Hash storage keys with twox 128 pub struct Twox128; impl StorageHasher for Twox128 { - const METADATA: metadata::StorageHasher = metadata::StorageHasher::Twox128; + const METADATA: metadata_ir::StorageHasherIR = metadata_ir::StorageHasherIR::Twox128; type Output = [u8; 16]; fn hash(x: &[u8]) -> [u8; 16] { twox_128(x) @@ -181,7 +181,7 @@ impl StorageHasher for Twox128 { /// Hash storage keys with twox 256 pub struct Twox256; impl StorageHasher for Twox256 { - const METADATA: metadata::StorageHasher = metadata::StorageHasher::Twox256; + const METADATA: metadata_ir::StorageHasherIR = metadata_ir::StorageHasherIR::Twox256; type Output = [u8; 32]; fn hash(x: &[u8]) -> [u8; 32] { twox_256(x) diff --git a/frame/support/src/lib.rs b/frame/support/src/lib.rs index 4e9bc32b0..b84582845 100644 --- a/frame/support/src/lib.rs +++ b/frame/support/src/lib.rs @@ -50,7 +50,7 @@ pub use paste; pub use scale_info; #[cfg(feature = "std")] pub use serde; -pub use sp_core::Void; +pub use sp_core::{OpaqueMetadata, Void}; #[doc(hidden)] pub use sp_core_hashing_proc_macro; #[doc(hidden)] @@ -80,10 +80,10 @@ pub mod error; pub mod crypto; pub mod dispatch_context; pub mod instances; +pub mod metadata_ir; pub mod migrations; pub mod traits; pub mod weights; - #[doc(hidden)] pub mod unsigned { #[doc(hidden)] @@ -827,9 +827,9 @@ pub use serde::{Deserialize, Serialize}; #[cfg(test)] pub mod tests { use super::*; - use crate::metadata::{ - PalletStorageMetadata, StorageEntryMetadata, StorageEntryModifier, StorageEntryType, - StorageHasher, + use crate::metadata_ir::{ + PalletStorageMetadataIR, StorageEntryMetadataIR, StorageEntryModifierIR, + StorageEntryTypeIR, StorageHasherIR, }; use codec::{Codec, EncodeLike}; use frame_support::traits::CrateVersion; @@ -1310,101 +1310,107 @@ pub mod tests { }); } - fn expected_metadata() -> PalletStorageMetadata { - PalletStorageMetadata { + fn expected_metadata() -> PalletStorageMetadataIR { + PalletStorageMetadataIR { prefix: "Test", entries: vec![ - StorageEntryMetadata { + StorageEntryMetadataIR { name: "Value", - modifier: StorageEntryModifier::Default, - ty: StorageEntryType::Plain(scale_info::meta_type::()), + modifier: StorageEntryModifierIR::Default, + ty: StorageEntryTypeIR::Plain(scale_info::meta_type::()), default: vec![0, 0, 0, 0, 0, 0, 0, 0], docs: vec![], }, - StorageEntryMetadata { + StorageEntryMetadataIR { name: "Data", - modifier: StorageEntryModifier::Default, - ty: StorageEntryType::Map { - hashers: vec![StorageHasher::Twox64Concat], + modifier: StorageEntryModifierIR::Default, + ty: StorageEntryTypeIR::Map { + hashers: vec![StorageHasherIR::Twox64Concat], key: scale_info::meta_type::(), value: scale_info::meta_type::(), }, default: vec![0, 0, 0, 0, 0, 0, 0, 0], docs: vec![], }, - StorageEntryMetadata { + StorageEntryMetadataIR { name: "OptionLinkedMap", - modifier: StorageEntryModifier::Optional, - ty: StorageEntryType::Map { - hashers: vec![StorageHasher::Blake2_128Concat], + modifier: StorageEntryModifierIR::Optional, + ty: StorageEntryTypeIR::Map { + hashers: vec![StorageHasherIR::Blake2_128Concat], key: scale_info::meta_type::(), value: scale_info::meta_type::(), }, default: vec![0], docs: vec![], }, - StorageEntryMetadata { + StorageEntryMetadataIR { name: "GenericData", - modifier: StorageEntryModifier::Default, - ty: StorageEntryType::Map { - hashers: vec![StorageHasher::Identity], + modifier: StorageEntryModifierIR::Default, + ty: StorageEntryTypeIR::Map { + hashers: vec![StorageHasherIR::Identity], key: scale_info::meta_type::(), value: scale_info::meta_type::(), }, default: vec![0, 0, 0, 0], docs: vec![], }, - StorageEntryMetadata { + StorageEntryMetadataIR { name: "GenericData2", - modifier: StorageEntryModifier::Optional, - ty: StorageEntryType::Map { - hashers: vec![StorageHasher::Blake2_128Concat], + modifier: StorageEntryModifierIR::Optional, + ty: StorageEntryTypeIR::Map { + hashers: vec![StorageHasherIR::Blake2_128Concat], key: scale_info::meta_type::(), value: scale_info::meta_type::(), }, default: vec![0], docs: vec![], }, - StorageEntryMetadata { + StorageEntryMetadataIR { name: "DataDM", - modifier: StorageEntryModifier::Default, - ty: StorageEntryType::Map { - hashers: vec![StorageHasher::Twox64Concat, StorageHasher::Blake2_128Concat], + modifier: StorageEntryModifierIR::Default, + ty: StorageEntryTypeIR::Map { + hashers: vec![ + StorageHasherIR::Twox64Concat, + StorageHasherIR::Blake2_128Concat, + ], key: scale_info::meta_type::<(u32, u32)>(), value: scale_info::meta_type::(), }, default: vec![0, 0, 0, 0, 0, 0, 0, 0], docs: vec![], }, - StorageEntryMetadata { + StorageEntryMetadataIR { name: "GenericDataDM", - modifier: StorageEntryModifier::Default, - ty: StorageEntryType::Map { - hashers: vec![StorageHasher::Blake2_128Concat, StorageHasher::Identity], + modifier: StorageEntryModifierIR::Default, + ty: StorageEntryTypeIR::Map { + hashers: vec![StorageHasherIR::Blake2_128Concat, StorageHasherIR::Identity], key: scale_info::meta_type::<(u32, u32)>(), value: scale_info::meta_type::(), }, default: vec![0, 0, 0, 0], docs: vec![], }, - StorageEntryMetadata { + StorageEntryMetadataIR { name: "GenericData2DM", - modifier: StorageEntryModifier::Optional, - ty: StorageEntryType::Map { - hashers: vec![StorageHasher::Blake2_128Concat, StorageHasher::Twox64Concat], + modifier: StorageEntryModifierIR::Optional, + ty: StorageEntryTypeIR::Map { + hashers: vec![ + StorageHasherIR::Blake2_128Concat, + StorageHasherIR::Twox64Concat, + ], key: scale_info::meta_type::<(u32, u32)>(), value: scale_info::meta_type::(), }, default: vec![0], docs: vec![], }, - StorageEntryMetadata { + StorageEntryMetadataIR { name: "AppendableDM", - modifier: StorageEntryModifier::Default, - ty: StorageEntryType::Map { + modifier: StorageEntryModifierIR::Default, + ty: StorageEntryTypeIR::Map { hashers: vec![ - StorageHasher::Blake2_128Concat, - StorageHasher::Blake2_128Concat, + StorageHasherIR::Blake2_128Concat, + StorageHasherIR::Blake2_128Concat, ], key: scale_info::meta_type::<(u32, u32)>(), value: scale_info::meta_type::>(), diff --git a/frame/support/src/metadata_ir/mod.rs b/frame/support/src/metadata_ir/mod.rs new file mode 100644 index 000000000..bab205d63 --- /dev/null +++ b/frame/support/src/metadata_ir/mod.rs @@ -0,0 +1,82 @@ +// This file is part of Substrate. + +// Copyright (C) Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +//! Intermediate representation of the runtime metadata. + +mod types; +use frame_metadata::{RuntimeMetadataPrefixed, RuntimeMetadataV14}; +pub use types::*; + +mod v14; + +/// Metadata V14. +const V14: u32 = 14; + +/// Transform the IR to the specified version. +/// +/// Use [`supported_versions`] to find supported versions. +pub fn into_version(metadata: MetadataIR, version: u32) -> Option { + match version { + // Latest stable version. + V14 => { + let v14: frame_metadata::v14::RuntimeMetadataV14 = metadata.into(); + Some(v14.into()) + }, + _ => None, + } +} + +/// Returns the supported metadata versions. +pub fn supported_versions() -> sp_std::vec::Vec { + sp_std::vec![V14,] +} + +/// Transform the IR to the latest stable metadata version. +pub fn into_latest(metadata: MetadataIR) -> RuntimeMetadataPrefixed { + let latest: RuntimeMetadataV14 = metadata.into(); + latest.into() +} + +#[cfg(test)] +mod test { + use super::*; + use crate::metadata_ir::ExtrinsicMetadataIR; + use frame_metadata::{v14::META_RESERVED, RuntimeMetadata}; + use scale_info::meta_type; + + fn ir_metadata() -> MetadataIR { + MetadataIR { + pallets: vec![], + extrinsic: ExtrinsicMetadataIR { + ty: meta_type::<()>(), + version: 0, + signed_extensions: vec![], + }, + ty: meta_type::<()>(), + } + } + + #[test] + fn into_version_14() { + let ir = ir_metadata(); + let metadata = into_version(ir, V14).expect("Should return prefixed metadata"); + + assert_eq!(metadata.0, META_RESERVED); + + assert!(matches!(metadata.1, RuntimeMetadata::V14(_))); + } +} diff --git a/frame/support/src/metadata_ir/types.rs b/frame/support/src/metadata_ir/types.rs new file mode 100644 index 000000000..087fd3dca --- /dev/null +++ b/frame/support/src/metadata_ir/types.rs @@ -0,0 +1,329 @@ +// This file is part of Substrate. + +// Copyright (C) Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +use codec::Encode; +use scale_info::{ + form::{Form, MetaForm, PortableForm}, + prelude::vec::Vec, + IntoPortable, MetaType, Registry, +}; + +/// The intermediate representation for the runtime metadata. +/// Contains the needed context that allows conversion to multiple metadata versions. +/// +/// # Note +/// +/// Further fields could be added or removed to ensure proper conversion. +/// When the IR does not contain enough information to generate a specific version +/// of the runtime metadata an appropriate default value is used (ie, empty vector). +pub struct MetadataIR { + /// Pallet metadata. + pub pallets: Vec>, + /// Metadata of the extrinsic. + pub extrinsic: ExtrinsicMetadataIR, + /// The type of the `Runtime`. + pub ty: T::Type, +} + +/// The intermediate representation for a pallet metadata. +#[derive(Clone, PartialEq, Eq, Encode, Debug)] +pub struct PalletMetadataIR { + /// Pallet name. + pub name: T::String, + /// Pallet storage metadata. + pub storage: Option>, + /// Pallet calls metadata. + pub calls: Option>, + /// Pallet event metadata. + pub event: Option>, + /// Pallet constants metadata. + pub constants: Vec>, + /// Pallet error metadata. + pub error: Option>, + /// Define the index of the pallet, this index will be used for the encoding of pallet event, + /// call and origin variants. + pub index: u8, + /// Pallet documentation. + pub docs: Vec, +} + +impl IntoPortable for PalletMetadataIR { + type Output = PalletMetadataIR; + + fn into_portable(self, registry: &mut Registry) -> Self::Output { + PalletMetadataIR { + name: self.name.into_portable(registry), + storage: self.storage.map(|storage| storage.into_portable(registry)), + calls: self.calls.map(|calls| calls.into_portable(registry)), + event: self.event.map(|event| event.into_portable(registry)), + constants: registry.map_into_portable(self.constants), + error: self.error.map(|error| error.into_portable(registry)), + index: self.index, + docs: registry.map_into_portable(self.docs), + } + } +} + +/// Metadata of the extrinsic used by the runtime. +#[derive(Clone, PartialEq, Eq, Encode, Debug)] +pub struct ExtrinsicMetadataIR { + /// The type of the extrinsic. + pub ty: T::Type, + /// Extrinsic version. + pub version: u8, + /// The signed extensions in the order they appear in the extrinsic. + pub signed_extensions: Vec>, +} + +impl IntoPortable for ExtrinsicMetadataIR { + type Output = ExtrinsicMetadataIR; + + fn into_portable(self, registry: &mut Registry) -> Self::Output { + ExtrinsicMetadataIR { + ty: registry.register_type(&self.ty), + version: self.version, + signed_extensions: registry.map_into_portable(self.signed_extensions), + } + } +} + +/// Metadata of an extrinsic's signed extension. +#[derive(Clone, PartialEq, Eq, Encode, Debug)] +pub struct SignedExtensionMetadataIR { + /// The unique signed extension identifier, which may be different from the type name. + pub identifier: T::String, + /// The type of the signed extension, with the data to be included in the extrinsic. + pub ty: T::Type, + /// The type of the additional signed data, with the data to be included in the signed payload + pub additional_signed: T::Type, +} + +impl IntoPortable for SignedExtensionMetadataIR { + type Output = SignedExtensionMetadataIR; + + fn into_portable(self, registry: &mut Registry) -> Self::Output { + SignedExtensionMetadataIR { + identifier: self.identifier.into_portable(registry), + ty: registry.register_type(&self.ty), + additional_signed: registry.register_type(&self.additional_signed), + } + } +} + +/// All metadata of the pallet's storage. +#[derive(Clone, PartialEq, Eq, Encode, Debug)] +/// The common prefix used by all storage entries. +pub struct PalletStorageMetadataIR { + /// The common prefix used by all storage entries. + pub prefix: T::String, + /// Metadata for all storage entries. + pub entries: Vec>, +} + +impl IntoPortable for PalletStorageMetadataIR { + type Output = PalletStorageMetadataIR; + + fn into_portable(self, registry: &mut Registry) -> Self::Output { + PalletStorageMetadataIR { + prefix: self.prefix.into_portable(registry), + entries: registry.map_into_portable(self.entries), + } + } +} + +/// Metadata about one storage entry. +#[derive(Clone, PartialEq, Eq, Encode, Debug)] +pub struct StorageEntryMetadataIR { + /// Variable name of the storage entry. + pub name: T::String, + /// An `Option` modifier of that storage entry. + pub modifier: StorageEntryModifierIR, + /// Type of the value stored in the entry. + pub ty: StorageEntryTypeIR, + /// Default value (SCALE encoded). + pub default: Vec, + /// Storage entry documentation. + pub docs: Vec, +} + +impl IntoPortable for StorageEntryMetadataIR { + type Output = StorageEntryMetadataIR; + + fn into_portable(self, registry: &mut Registry) -> Self::Output { + StorageEntryMetadataIR { + name: self.name.into_portable(registry), + modifier: self.modifier, + ty: self.ty.into_portable(registry), + default: self.default, + docs: registry.map_into_portable(self.docs), + } + } +} + +/// A storage entry modifier indicates how a storage entry is returned when fetched and what the +/// value will be if the key is not present. Specifically this refers to the "return type" when +/// fetching a storage entry, and what the value will be if the key is not present. +/// +/// `Optional` means you should expect an `Option`, with `None` returned if the key is not +/// present. `Default` means you should expect a `T` with the default value of default if the key is +/// not present. +#[derive(Clone, PartialEq, Eq, Encode, Debug)] +pub enum StorageEntryModifierIR { + /// The storage entry returns an `Option`, with `None` if the key is not present. + Optional, + /// The storage entry returns `T::Default` if the key is not present. + Default, +} + +/// Hasher used by storage maps +#[derive(Clone, PartialEq, Eq, Encode, Debug)] +pub enum StorageHasherIR { + /// 128-bit Blake2 hash. + Blake2_128, + /// 256-bit Blake2 hash. + Blake2_256, + /// Multiple 128-bit Blake2 hashes concatenated. + Blake2_128Concat, + /// 128-bit XX hash. + Twox128, + /// 256-bit XX hash. + Twox256, + /// Multiple 64-bit XX hashes concatenated. + Twox64Concat, + /// Identity hashing (no hashing). + Identity, +} + +/// A type of storage value. +#[derive(Clone, PartialEq, Eq, Encode, Debug)] +pub enum StorageEntryTypeIR { + /// Plain storage entry (just the value). + Plain(T::Type), + /// A storage map. + Map { + /// One or more hashers, should be one hasher per key element. + hashers: Vec, + /// The type of the key, can be a tuple with elements for each of the hashers. + key: T::Type, + /// The type of the value. + value: T::Type, + }, +} + +impl IntoPortable for StorageEntryTypeIR { + type Output = StorageEntryTypeIR; + + fn into_portable(self, registry: &mut Registry) -> Self::Output { + match self { + Self::Plain(plain) => StorageEntryTypeIR::Plain(registry.register_type(&plain)), + Self::Map { hashers, key, value } => StorageEntryTypeIR::Map { + hashers, + key: registry.register_type(&key), + value: registry.register_type(&value), + }, + } + } +} + +/// Metadata for all calls in a pallet +#[derive(Clone, PartialEq, Eq, Encode, Debug)] +pub struct PalletCallMetadataIR { + /// The corresponding enum type for the pallet call. + pub ty: T::Type, +} + +impl IntoPortable for PalletCallMetadataIR { + type Output = PalletCallMetadataIR; + + fn into_portable(self, registry: &mut Registry) -> Self::Output { + PalletCallMetadataIR { ty: registry.register_type(&self.ty) } + } +} + +impl From for PalletCallMetadataIR { + fn from(ty: MetaType) -> Self { + Self { ty } + } +} + +/// Metadata about the pallet Event type. +#[derive(Clone, PartialEq, Eq, Encode, Debug)] +pub struct PalletEventMetadataIR { + /// The Event type. + pub ty: T::Type, +} + +impl IntoPortable for PalletEventMetadataIR { + type Output = PalletEventMetadataIR; + + fn into_portable(self, registry: &mut Registry) -> Self::Output { + PalletEventMetadataIR { ty: registry.register_type(&self.ty) } + } +} + +impl From for PalletEventMetadataIR { + fn from(ty: MetaType) -> Self { + Self { ty } + } +} + +/// Metadata about one pallet constant. +#[derive(Clone, PartialEq, Eq, Encode, Debug)] +pub struct PalletConstantMetadataIR { + /// Name of the pallet constant. + pub name: T::String, + /// Type of the pallet constant. + pub ty: T::Type, + /// Value stored in the constant (SCALE encoded). + pub value: Vec, + /// Documentation of the constant. + pub docs: Vec, +} + +impl IntoPortable for PalletConstantMetadataIR { + type Output = PalletConstantMetadataIR; + + fn into_portable(self, registry: &mut Registry) -> Self::Output { + PalletConstantMetadataIR { + name: self.name.into_portable(registry), + ty: registry.register_type(&self.ty), + value: self.value, + docs: registry.map_into_portable(self.docs), + } + } +} + +/// Metadata about a pallet error. +#[derive(Clone, PartialEq, Eq, Encode, Debug)] +pub struct PalletErrorMetadataIR { + /// The error type information. + pub ty: T::Type, +} + +impl IntoPortable for PalletErrorMetadataIR { + type Output = PalletErrorMetadataIR; + + fn into_portable(self, registry: &mut Registry) -> Self::Output { + PalletErrorMetadataIR { ty: registry.register_type(&self.ty) } + } +} + +impl From for PalletErrorMetadataIR { + fn from(ty: MetaType) -> Self { + Self { ty } + } +} diff --git a/frame/support/src/metadata_ir/v14.rs b/frame/support/src/metadata_ir/v14.rs new file mode 100644 index 000000000..e1b7a24f7 --- /dev/null +++ b/frame/support/src/metadata_ir/v14.rs @@ -0,0 +1,158 @@ +// This file is part of Substrate. + +// Copyright (C) Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +//! Convert the IR to V14 metadata. + +use super::types::{ + ExtrinsicMetadataIR, MetadataIR, PalletCallMetadataIR, PalletConstantMetadataIR, + PalletErrorMetadataIR, PalletEventMetadataIR, PalletMetadataIR, PalletStorageMetadataIR, + SignedExtensionMetadataIR, StorageEntryMetadataIR, StorageEntryModifierIR, StorageEntryTypeIR, + StorageHasherIR, +}; + +use frame_metadata::v14::{ + ExtrinsicMetadata, PalletCallMetadata, PalletConstantMetadata, PalletErrorMetadata, + PalletEventMetadata, PalletMetadata, PalletStorageMetadata, RuntimeMetadataV14, + SignedExtensionMetadata, StorageEntryMetadata, StorageEntryModifier, StorageEntryType, + StorageHasher, +}; + +impl From for RuntimeMetadataV14 { + fn from(ir: MetadataIR) -> Self { + RuntimeMetadataV14::new( + ir.pallets.into_iter().map(Into::into).collect(), + ir.extrinsic.into(), + ir.ty, + ) + } +} + +impl From for PalletMetadata { + fn from(ir: PalletMetadataIR) -> Self { + PalletMetadata { + name: ir.name, + storage: ir.storage.map(Into::into), + calls: ir.calls.map(Into::into), + event: ir.event.map(Into::into), + constants: ir.constants.into_iter().map(Into::into).collect(), + error: ir.error.map(Into::into), + index: ir.index, + // Note: ir.docs not part of v14. + } + } +} + +impl From for StorageEntryModifier { + fn from(ir: StorageEntryModifierIR) -> Self { + match ir { + StorageEntryModifierIR::Optional => StorageEntryModifier::Optional, + StorageEntryModifierIR::Default => StorageEntryModifier::Default, + } + } +} + +impl From for StorageHasher { + fn from(ir: StorageHasherIR) -> Self { + match ir { + StorageHasherIR::Blake2_128 => StorageHasher::Blake2_128, + StorageHasherIR::Blake2_256 => StorageHasher::Blake2_256, + StorageHasherIR::Blake2_128Concat => StorageHasher::Blake2_128Concat, + StorageHasherIR::Twox128 => StorageHasher::Twox128, + StorageHasherIR::Twox256 => StorageHasher::Twox256, + StorageHasherIR::Twox64Concat => StorageHasher::Twox64Concat, + StorageHasherIR::Identity => StorageHasher::Identity, + } + } +} + +impl From for StorageEntryType { + fn from(ir: StorageEntryTypeIR) -> Self { + match ir { + StorageEntryTypeIR::Plain(ty) => StorageEntryType::Plain(ty), + StorageEntryTypeIR::Map { hashers, key, value } => StorageEntryType::Map { + hashers: hashers.into_iter().map(Into::into).collect(), + key, + value, + }, + } + } +} + +impl From for StorageEntryMetadata { + fn from(ir: StorageEntryMetadataIR) -> Self { + StorageEntryMetadata { + name: ir.name, + modifier: ir.modifier.into(), + ty: ir.ty.into(), + default: ir.default, + docs: ir.docs, + } + } +} + +impl From for PalletStorageMetadata { + fn from(ir: PalletStorageMetadataIR) -> Self { + PalletStorageMetadata { + prefix: ir.prefix, + entries: ir.entries.into_iter().map(Into::into).collect(), + } + } +} + +impl From for PalletCallMetadata { + fn from(ir: PalletCallMetadataIR) -> Self { + PalletCallMetadata { ty: ir.ty } + } +} + +impl From for PalletEventMetadata { + fn from(ir: PalletEventMetadataIR) -> Self { + PalletEventMetadata { ty: ir.ty } + } +} + +impl From for PalletConstantMetadata { + fn from(ir: PalletConstantMetadataIR) -> Self { + PalletConstantMetadata { name: ir.name, ty: ir.ty, value: ir.value, docs: ir.docs } + } +} + +impl From for PalletErrorMetadata { + fn from(ir: PalletErrorMetadataIR) -> Self { + PalletErrorMetadata { ty: ir.ty } + } +} + +impl From for SignedExtensionMetadata { + fn from(ir: SignedExtensionMetadataIR) -> Self { + SignedExtensionMetadata { + identifier: ir.identifier, + ty: ir.ty, + additional_signed: ir.additional_signed, + } + } +} + +impl From for ExtrinsicMetadata { + fn from(ir: ExtrinsicMetadataIR) -> Self { + ExtrinsicMetadata { + ty: ir.ty, + version: ir.version, + signed_extensions: ir.signed_extensions.into_iter().map(Into::into).collect(), + } + } +} diff --git a/frame/support/src/storage/types/counted_map.rs b/frame/support/src/storage/types/counted_map.rs index 24b00be48..e57942cbe 100644 --- a/frame/support/src/storage/types/counted_map.rs +++ b/frame/support/src/storage/types/counted_map.rs @@ -18,7 +18,7 @@ //! Storage counted map type. use crate::{ - metadata::StorageEntryMetadata, + metadata_ir::StorageEntryMetadataIR, storage::{ generator::StorageMap as _, types::{ @@ -459,7 +459,7 @@ where OnEmpty: Get + 'static, MaxValues: Get>, { - fn build_metadata(docs: Vec<&'static str>, entries: &mut Vec) { + fn build_metadata(docs: Vec<&'static str>, entries: &mut Vec) { ::Map::build_metadata(docs, entries); CounterFor::::build_metadata( if cfg!(feature = "no-metadata-docs") { @@ -512,7 +512,7 @@ mod test { use super::*; use crate::{ hash::*, - metadata::{StorageEntryModifier, StorageEntryType, StorageHasher}, + metadata_ir::{StorageEntryModifierIR, StorageEntryTypeIR, StorageHasherIR}, storage::{bounded_vec::BoundedVec, types::ValueQuery}, traits::ConstU32, }; @@ -1147,21 +1147,21 @@ mod test { assert_eq!( entries, vec![ - StorageEntryMetadata { + StorageEntryMetadataIR { name: "foo", - modifier: StorageEntryModifier::Default, - ty: StorageEntryType::Map { - hashers: vec![StorageHasher::Twox64Concat], + modifier: StorageEntryModifierIR::Default, + ty: StorageEntryTypeIR::Map { + hashers: vec![StorageHasherIR::Twox64Concat], key: scale_info::meta_type::(), value: scale_info::meta_type::(), }, default: 97u32.encode(), docs: vec![], }, - StorageEntryMetadata { + StorageEntryMetadataIR { name: "counter_for_foo", - modifier: StorageEntryModifier::Default, - ty: StorageEntryType::Plain(scale_info::meta_type::()), + modifier: StorageEntryModifierIR::Default, + ty: StorageEntryTypeIR::Plain(scale_info::meta_type::()), default: vec![0, 0, 0, 0], docs: if cfg!(feature = "no-metadata-docs") { vec![] diff --git a/frame/support/src/storage/types/double_map.rs b/frame/support/src/storage/types/double_map.rs index 6a4bdc1e6..08ac1709c 100644 --- a/frame/support/src/storage/types/double_map.rs +++ b/frame/support/src/storage/types/double_map.rs @@ -19,7 +19,7 @@ //! StoragePrefixedDoubleMap traits and their methods directly. use crate::{ - metadata::{StorageEntryMetadata, StorageEntryType}, + metadata_ir::{StorageEntryMetadataIR, StorageEntryTypeIR}, storage::{ types::{OptionQuery, QueryKindTrait, StorageEntryMetadataBuilder}, KeyLenOf, StorageAppend, StorageDecodeLength, StoragePrefixedMap, StorageTryAppend, @@ -656,13 +656,13 @@ where OnEmpty: Get + 'static, MaxValues: Get>, { - fn build_metadata(docs: Vec<&'static str>, entries: &mut Vec) { + fn build_metadata(docs: Vec<&'static str>, entries: &mut Vec) { let docs = if cfg!(feature = "no-metadata-docs") { vec![] } else { docs }; - let entry = StorageEntryMetadata { + let entry = StorageEntryMetadataIR { name: Prefix::STORAGE_PREFIX, modifier: QueryKind::METADATA, - ty: StorageEntryType::Map { + ty: StorageEntryTypeIR::Map { hashers: vec![Hasher1::METADATA, Hasher2::METADATA], key: scale_info::meta_type::<(Key1, Key2)>(), value: scale_info::meta_type::(), @@ -736,7 +736,7 @@ mod test { use super::*; use crate::{ hash::*, - metadata::{StorageEntryModifier, StorageEntryType, StorageHasher}, + metadata_ir::{StorageEntryModifierIR, StorageEntryTypeIR, StorageHasherIR}, storage::types::ValueQuery, }; use sp_io::{hashing::twox_128, TestExternalities}; @@ -916,13 +916,13 @@ mod test { assert_eq!( entries, vec![ - StorageEntryMetadata { + StorageEntryMetadataIR { name: "foo", - modifier: StorageEntryModifier::Optional, - ty: StorageEntryType::Map { + modifier: StorageEntryModifierIR::Optional, + ty: StorageEntryTypeIR::Map { hashers: vec![ - StorageHasher::Blake2_128Concat, - StorageHasher::Twox64Concat + StorageHasherIR::Blake2_128Concat, + StorageHasherIR::Twox64Concat ], key: scale_info::meta_type::<(u16, u8)>(), value: scale_info::meta_type::(), @@ -930,13 +930,13 @@ mod test { default: Option::::None.encode(), docs: vec![], }, - StorageEntryMetadata { + StorageEntryMetadataIR { name: "foo", - modifier: StorageEntryModifier::Default, - ty: StorageEntryType::Map { + modifier: StorageEntryModifierIR::Default, + ty: StorageEntryTypeIR::Map { hashers: vec![ - StorageHasher::Blake2_128Concat, - StorageHasher::Twox64Concat + StorageHasherIR::Blake2_128Concat, + StorageHasherIR::Twox64Concat ], key: scale_info::meta_type::<(u16, u8)>(), value: scale_info::meta_type::(), diff --git a/frame/support/src/storage/types/key.rs b/frame/support/src/storage/types/key.rs index 901bdb8b0..bf87e593b 100755 --- a/frame/support/src/storage/types/key.rs +++ b/frame/support/src/storage/types/key.rs @@ -41,7 +41,7 @@ pub trait KeyGenerator { type HashFn: FnOnce(&[u8]) -> Vec; type HArg; - const HASHER_METADATA: &'static [crate::metadata::StorageHasher]; + const HASHER_METADATA: &'static [crate::metadata_ir::StorageHasherIR]; /// Given a `key` tuple, calculate the final key by encoding each element individually and /// hashing them using the corresponding hasher in the `KeyGenerator`. @@ -74,7 +74,7 @@ impl KeyGenerator for Key type HashFn = Box Vec>; type HArg = (Self::HashFn,); - const HASHER_METADATA: &'static [crate::metadata::StorageHasher] = &[H::METADATA]; + const HASHER_METADATA: &'static [crate::metadata_ir::StorageHasherIR] = &[H::METADATA]; fn final_key + TupleToEncodedIter>(key: KArg) -> Vec { H::hash(&key.to_encoded_iter().next().expect("should have at least one element!")) @@ -114,7 +114,7 @@ impl KeyGenerator for Tuple { for_tuples!( type HArg = ( #(Tuple::HashFn),* ); ); type HashFn = Box Vec>; - const HASHER_METADATA: &'static [crate::metadata::StorageHasher] = + const HASHER_METADATA: &'static [crate::metadata_ir::StorageHasherIR] = &[for_tuples!( #(Tuple::Hasher::METADATA),* )]; fn final_key + TupleToEncodedIter>(key: KArg) -> Vec { diff --git a/frame/support/src/storage/types/map.rs b/frame/support/src/storage/types/map.rs index 53cf74d26..2110732b2 100644 --- a/frame/support/src/storage/types/map.rs +++ b/frame/support/src/storage/types/map.rs @@ -19,7 +19,7 @@ //! methods directly. use crate::{ - metadata::{StorageEntryMetadata, StorageEntryType}, + metadata_ir::{StorageEntryMetadataIR, StorageEntryTypeIR}, storage::{ types::{OptionQuery, QueryKindTrait, StorageEntryMetadataBuilder}, KeyLenOf, StorageAppend, StorageDecodeLength, StoragePrefixedMap, StorageTryAppend, @@ -409,13 +409,13 @@ where OnEmpty: Get + 'static, MaxValues: Get>, { - fn build_metadata(docs: Vec<&'static str>, entries: &mut Vec) { + fn build_metadata(docs: Vec<&'static str>, entries: &mut Vec) { let docs = if cfg!(feature = "no-metadata-docs") { vec![] } else { docs }; - let entry = StorageEntryMetadata { + let entry = StorageEntryMetadataIR { name: Prefix::STORAGE_PREFIX, modifier: QueryKind::METADATA, - ty: StorageEntryType::Map { + ty: StorageEntryTypeIR::Map { hashers: vec![Hasher::METADATA], key: scale_info::meta_type::(), value: scale_info::meta_type::(), @@ -483,7 +483,7 @@ mod test { use super::*; use crate::{ hash::*, - metadata::{StorageEntryModifier, StorageEntryType, StorageHasher}, + metadata_ir::{StorageEntryModifierIR, StorageEntryTypeIR, StorageHasherIR}, storage::types::ValueQuery, }; use sp_io::{hashing::twox_128, TestExternalities}; @@ -706,22 +706,22 @@ mod test { assert_eq!( entries, vec![ - StorageEntryMetadata { + StorageEntryMetadataIR { name: "foo", - modifier: StorageEntryModifier::Optional, - ty: StorageEntryType::Map { - hashers: vec![StorageHasher::Blake2_128Concat], + modifier: StorageEntryModifierIR::Optional, + ty: StorageEntryTypeIR::Map { + hashers: vec![StorageHasherIR::Blake2_128Concat], key: scale_info::meta_type::(), value: scale_info::meta_type::(), }, default: Option::::None.encode(), docs: vec![], }, - StorageEntryMetadata { + StorageEntryMetadataIR { name: "foo", - modifier: StorageEntryModifier::Default, - ty: StorageEntryType::Map { - hashers: vec![StorageHasher::Blake2_128Concat], + modifier: StorageEntryModifierIR::Default, + ty: StorageEntryTypeIR::Map { + hashers: vec![StorageHasherIR::Blake2_128Concat], key: scale_info::meta_type::(), value: scale_info::meta_type::(), }, diff --git a/frame/support/src/storage/types/mod.rs b/frame/support/src/storage/types/mod.rs index 9a6f15d1e..3a5bae2e6 100644 --- a/frame/support/src/storage/types/mod.rs +++ b/frame/support/src/storage/types/mod.rs @@ -18,7 +18,7 @@ //! Storage types to build abstraction on storage, they implements storage traits such as //! StorageMap and others. -use crate::metadata::{StorageEntryMetadata, StorageEntryModifier}; +use crate::metadata_ir::{StorageEntryMetadataIR, StorageEntryModifierIR}; use codec::FullCodec; use sp_std::prelude::*; @@ -50,7 +50,7 @@ pub use value::StorageValue; /// value. pub trait QueryKindTrait { /// Metadata for the storage kind. - const METADATA: StorageEntryModifier; + const METADATA: StorageEntryModifierIR; /// Type returned on query type Query: FullCodec + 'static; @@ -73,7 +73,7 @@ impl QueryKindTrait for OptionQuery where Value: FullCodec + 'static, { - const METADATA: StorageEntryModifier = StorageEntryModifier::Optional; + const METADATA: StorageEntryModifierIR = StorageEntryModifierIR::Optional; type Query = Option; @@ -95,7 +95,7 @@ where Error: FullCodec + 'static, OnEmpty: crate::traits::Get>, { - const METADATA: StorageEntryModifier = StorageEntryModifier::Optional; + const METADATA: StorageEntryModifierIR = StorageEntryModifierIR::Optional; type Query = Result; @@ -118,7 +118,7 @@ where Value: FullCodec + 'static, OnEmpty: crate::traits::Get, { - const METADATA: StorageEntryModifier = StorageEntryModifier::Default; + const METADATA: StorageEntryModifierIR = StorageEntryModifierIR::Default; type Query = Value; @@ -136,5 +136,5 @@ where /// Implemented by each of the storage types: value, map, countedmap, doublemap and nmap. pub trait StorageEntryMetadataBuilder { /// Build into `entries` the storage metadata entries of a storage given some `docs`. - fn build_metadata(doc: Vec<&'static str>, entries: &mut Vec); + fn build_metadata(doc: Vec<&'static str>, entries: &mut Vec); } diff --git a/frame/support/src/storage/types/nmap.rs b/frame/support/src/storage/types/nmap.rs index d97103596..9b63ca7b0 100755 --- a/frame/support/src/storage/types/nmap.rs +++ b/frame/support/src/storage/types/nmap.rs @@ -19,7 +19,7 @@ //! StoragePrefixedDoubleMap traits and their methods directly. use crate::{ - metadata::{StorageEntryMetadata, StorageEntryType}, + metadata_ir::{StorageEntryMetadataIR, StorageEntryTypeIR}, storage::{ types::{ EncodeLikeTuple, HasKeyPrefix, HasReversibleKeyPrefix, OptionQuery, QueryKindTrait, @@ -550,13 +550,13 @@ where OnEmpty: Get + 'static, MaxValues: Get>, { - fn build_metadata(docs: Vec<&'static str>, entries: &mut Vec) { + fn build_metadata(docs: Vec<&'static str>, entries: &mut Vec) { let docs = if cfg!(feature = "no-metadata-docs") { vec![] } else { docs }; - let entry = StorageEntryMetadata { + let entry = StorageEntryMetadataIR { name: Prefix::STORAGE_PREFIX, modifier: QueryKind::METADATA, - ty: StorageEntryType::Map { + ty: StorageEntryTypeIR::Map { key: scale_info::meta_type::(), hashers: Key::HASHER_METADATA.to_vec(), value: scale_info::meta_type::(), @@ -620,7 +620,7 @@ mod test { use super::*; use crate::{ hash::{StorageHasher as _, *}, - metadata::{StorageEntryModifier, StorageHasher}, + metadata_ir::{StorageEntryModifierIR, StorageHasherIR}, storage::types::{Key as NMapKey, ValueQuery}, }; use sp_io::{hashing::twox_128, TestExternalities}; @@ -791,22 +791,22 @@ mod test { assert_eq!( entries, vec![ - StorageEntryMetadata { + StorageEntryMetadataIR { name: "Foo", - modifier: StorageEntryModifier::Optional, - ty: StorageEntryType::Map { - hashers: vec![StorageHasher::Blake2_128Concat], + modifier: StorageEntryModifierIR::Optional, + ty: StorageEntryTypeIR::Map { + hashers: vec![StorageHasherIR::Blake2_128Concat], key: scale_info::meta_type::(), value: scale_info::meta_type::(), }, default: Option::::None.encode(), docs: vec![], }, - StorageEntryMetadata { + StorageEntryMetadataIR { name: "Foo", - modifier: StorageEntryModifier::Default, - ty: StorageEntryType::Map { - hashers: vec![StorageHasher::Blake2_128Concat], + modifier: StorageEntryModifierIR::Default, + ty: StorageEntryTypeIR::Map { + hashers: vec![StorageHasherIR::Blake2_128Concat], key: scale_info::meta_type::(), value: scale_info::meta_type::(), }, @@ -991,13 +991,13 @@ mod test { assert_eq!( entries, vec![ - StorageEntryMetadata { + StorageEntryMetadataIR { name: "Foo", - modifier: StorageEntryModifier::Optional, - ty: StorageEntryType::Map { + modifier: StorageEntryModifierIR::Optional, + ty: StorageEntryTypeIR::Map { hashers: vec![ - StorageHasher::Blake2_128Concat, - StorageHasher::Twox64Concat + StorageHasherIR::Blake2_128Concat, + StorageHasherIR::Twox64Concat ], key: scale_info::meta_type::<(u16, u8)>(), value: scale_info::meta_type::(), @@ -1005,13 +1005,13 @@ mod test { default: Option::::None.encode(), docs: vec![], }, - StorageEntryMetadata { + StorageEntryMetadataIR { name: "Foo", - modifier: StorageEntryModifier::Default, - ty: StorageEntryType::Map { + modifier: StorageEntryModifierIR::Default, + ty: StorageEntryTypeIR::Map { hashers: vec![ - StorageHasher::Blake2_128Concat, - StorageHasher::Twox64Concat + StorageHasherIR::Blake2_128Concat, + StorageHasherIR::Twox64Concat ], key: scale_info::meta_type::<(u16, u8)>(), value: scale_info::meta_type::(), @@ -1232,14 +1232,14 @@ mod test { assert_eq!( entries, vec![ - StorageEntryMetadata { + StorageEntryMetadataIR { name: "Foo", - modifier: StorageEntryModifier::Optional, - ty: StorageEntryType::Map { + modifier: StorageEntryModifierIR::Optional, + ty: StorageEntryTypeIR::Map { hashers: vec![ - StorageHasher::Blake2_128Concat, - StorageHasher::Blake2_128Concat, - StorageHasher::Twox64Concat + StorageHasherIR::Blake2_128Concat, + StorageHasherIR::Blake2_128Concat, + StorageHasherIR::Twox64Concat ], key: scale_info::meta_type::<(u16, u16, u16)>(), value: scale_info::meta_type::(), @@ -1247,14 +1247,14 @@ mod test { default: Option::::None.encode(), docs: vec![], }, - StorageEntryMetadata { + StorageEntryMetadataIR { name: "Foo", - modifier: StorageEntryModifier::Default, - ty: StorageEntryType::Map { + modifier: StorageEntryModifierIR::Default, + ty: StorageEntryTypeIR::Map { hashers: vec![ - StorageHasher::Blake2_128Concat, - StorageHasher::Blake2_128Concat, - StorageHasher::Twox64Concat + StorageHasherIR::Blake2_128Concat, + StorageHasherIR::Blake2_128Concat, + StorageHasherIR::Twox64Concat ], key: scale_info::meta_type::<(u16, u16, u16)>(), value: scale_info::meta_type::(), diff --git a/frame/support/src/storage/types/value.rs b/frame/support/src/storage/types/value.rs index 64fb432b2..d5fbb0656 100644 --- a/frame/support/src/storage/types/value.rs +++ b/frame/support/src/storage/types/value.rs @@ -18,7 +18,7 @@ //! Storage value type. Implements StorageValue trait and its method directly. use crate::{ - metadata::{StorageEntryMetadata, StorageEntryType}, + metadata_ir::{StorageEntryMetadataIR, StorageEntryTypeIR}, storage::{ generator::StorageValue as StorageValueT, types::{OptionQuery, QueryKindTrait, StorageEntryMetadataBuilder}, @@ -221,13 +221,13 @@ where QueryKind: QueryKindTrait, OnEmpty: crate::traits::Get + 'static, { - fn build_metadata(docs: Vec<&'static str>, entries: &mut Vec) { + fn build_metadata(docs: Vec<&'static str>, entries: &mut Vec) { let docs = if cfg!(feature = "no-metadata-docs") { vec![] } else { docs }; - let entry = StorageEntryMetadata { + let entry = StorageEntryMetadataIR { name: Prefix::STORAGE_PREFIX, modifier: QueryKind::METADATA, - ty: StorageEntryType::Plain(scale_info::meta_type::()), + ty: StorageEntryTypeIR::Plain(scale_info::meta_type::()), default: OnEmpty::get().encode(), docs, }; @@ -278,7 +278,7 @@ where #[cfg(test)] mod test { use super::*; - use crate::{metadata::StorageEntryModifier, storage::types::ValueQuery}; + use crate::{metadata_ir::StorageEntryModifierIR, storage::types::ValueQuery}; use sp_io::{hashing::twox_128, TestExternalities}; struct Prefix; @@ -363,17 +363,17 @@ mod test { assert_eq!( entries, vec![ - StorageEntryMetadata { + StorageEntryMetadataIR { name: "foo", - modifier: StorageEntryModifier::Optional, - ty: StorageEntryType::Plain(scale_info::meta_type::()), + modifier: StorageEntryModifierIR::Optional, + ty: StorageEntryTypeIR::Plain(scale_info::meta_type::()), default: Option::::None.encode(), docs: vec![], }, - StorageEntryMetadata { + StorageEntryMetadataIR { name: "foo", - modifier: StorageEntryModifier::Default, - ty: StorageEntryType::Plain(scale_info::meta_type::()), + modifier: StorageEntryModifierIR::Default, + ty: StorageEntryTypeIR::Plain(scale_info::meta_type::()), default: 97u32.encode(), docs: vec![], } diff --git a/frame/support/test/tests/decl_storage.rs b/frame/support/test/tests/decl_storage.rs index c2ee77424..bee1dc83d 100644 --- a/frame/support/test/tests/decl_storage.rs +++ b/frame/support/test/tests/decl_storage.rs @@ -19,7 +19,7 @@ // Do not complain about unused `dispatch` and `dispatch_aux`. #[allow(dead_code)] mod tests { - use frame_support::metadata::*; + use frame_support::metadata_ir::*; use sp_io::TestExternalities; frame_support::decl_module! { @@ -104,195 +104,195 @@ mod tests { type Origin2 = u32; } - fn expected_metadata() -> PalletStorageMetadata { - PalletStorageMetadata { + fn expected_metadata() -> PalletStorageMetadataIR { + PalletStorageMetadataIR { prefix: "TestStorage", entries: vec![ - StorageEntryMetadata { + StorageEntryMetadataIR { name: "U32", - modifier: StorageEntryModifier::Optional, - ty: StorageEntryType::Plain(scale_info::meta_type::()), + modifier: StorageEntryModifierIR::Optional, + ty: StorageEntryTypeIR::Plain(scale_info::meta_type::()), default: vec![0], docs: vec![" Hello, this is doc!"], }, - StorageEntryMetadata { + StorageEntryMetadataIR { name: "PUBU32", - modifier: StorageEntryModifier::Optional, - ty: StorageEntryType::Plain(scale_info::meta_type::()), + modifier: StorageEntryModifierIR::Optional, + ty: StorageEntryTypeIR::Plain(scale_info::meta_type::()), default: vec![0], docs: vec![], }, - StorageEntryMetadata { + StorageEntryMetadataIR { name: "U32MYDEF", - modifier: StorageEntryModifier::Optional, - ty: StorageEntryType::Plain(scale_info::meta_type::()), + modifier: StorageEntryModifierIR::Optional, + ty: StorageEntryTypeIR::Plain(scale_info::meta_type::()), default: vec![0], docs: vec![], }, - StorageEntryMetadata { + StorageEntryMetadataIR { name: "PUBU32MYDEF", - modifier: StorageEntryModifier::Optional, - ty: StorageEntryType::Plain(scale_info::meta_type::()), + modifier: StorageEntryModifierIR::Optional, + ty: StorageEntryTypeIR::Plain(scale_info::meta_type::()), default: vec![0], docs: vec![], }, - StorageEntryMetadata { + StorageEntryMetadataIR { name: "GETU32", - modifier: StorageEntryModifier::Default, - ty: StorageEntryType::Plain(scale_info::meta_type::()), + modifier: StorageEntryModifierIR::Default, + ty: StorageEntryTypeIR::Plain(scale_info::meta_type::()), default: vec![0, 0, 0, 0], docs: vec![], }, - StorageEntryMetadata { + StorageEntryMetadataIR { name: "PUBGETU32", - modifier: StorageEntryModifier::Default, - ty: StorageEntryType::Plain(scale_info::meta_type::()), + modifier: StorageEntryModifierIR::Default, + ty: StorageEntryTypeIR::Plain(scale_info::meta_type::()), default: vec![0, 0, 0, 0], docs: vec![], }, - StorageEntryMetadata { + StorageEntryMetadataIR { name: "GETU32WITHCONFIG", - modifier: StorageEntryModifier::Default, - ty: StorageEntryType::Plain(scale_info::meta_type::()), + modifier: StorageEntryModifierIR::Default, + ty: StorageEntryTypeIR::Plain(scale_info::meta_type::()), default: vec![0, 0, 0, 0], docs: vec![], }, - StorageEntryMetadata { + StorageEntryMetadataIR { name: "PUBGETU32WITHCONFIG", - modifier: StorageEntryModifier::Default, - ty: StorageEntryType::Plain(scale_info::meta_type::()), + modifier: StorageEntryModifierIR::Default, + ty: StorageEntryTypeIR::Plain(scale_info::meta_type::()), default: vec![0, 0, 0, 0], docs: vec![], }, - StorageEntryMetadata { + StorageEntryMetadataIR { name: "GETU32MYDEF", - modifier: StorageEntryModifier::Optional, - ty: StorageEntryType::Plain(scale_info::meta_type::()), + modifier: StorageEntryModifierIR::Optional, + ty: StorageEntryTypeIR::Plain(scale_info::meta_type::()), default: vec![0], docs: vec![], }, - StorageEntryMetadata { + StorageEntryMetadataIR { name: "PUBGETU32MYDEF", - modifier: StorageEntryModifier::Default, - ty: StorageEntryType::Plain(scale_info::meta_type::()), + modifier: StorageEntryModifierIR::Default, + ty: StorageEntryTypeIR::Plain(scale_info::meta_type::()), default: vec![3, 0, 0, 0], docs: vec![], }, - StorageEntryMetadata { + StorageEntryMetadataIR { name: "GETU32WITHCONFIGMYDEF", - modifier: StorageEntryModifier::Default, - ty: StorageEntryType::Plain(scale_info::meta_type::()), + modifier: StorageEntryModifierIR::Default, + ty: StorageEntryTypeIR::Plain(scale_info::meta_type::()), default: vec![2, 0, 0, 0], docs: vec![], }, - StorageEntryMetadata { + StorageEntryMetadataIR { name: "PUBGETU32WITHCONFIGMYDEF", - modifier: StorageEntryModifier::Default, - ty: StorageEntryType::Plain(scale_info::meta_type::()), + modifier: StorageEntryModifierIR::Default, + ty: StorageEntryTypeIR::Plain(scale_info::meta_type::()), default: vec![1, 0, 0, 0], docs: vec![], }, - StorageEntryMetadata { + StorageEntryMetadataIR { name: "PUBGETU32WITHCONFIGMYDEFOPT", - modifier: StorageEntryModifier::Optional, - ty: StorageEntryType::Plain(scale_info::meta_type::()), + modifier: StorageEntryModifierIR::Optional, + ty: StorageEntryTypeIR::Plain(scale_info::meta_type::()), default: vec![0], docs: vec![], }, - StorageEntryMetadata { + StorageEntryMetadataIR { name: "GetU32WithBuilder", - modifier: StorageEntryModifier::Default, - ty: StorageEntryType::Plain(scale_info::meta_type::()), + modifier: StorageEntryModifierIR::Default, + ty: StorageEntryTypeIR::Plain(scale_info::meta_type::()), default: vec![0, 0, 0, 0], docs: vec![], }, - StorageEntryMetadata { + StorageEntryMetadataIR { name: "GetOptU32WithBuilderSome", - modifier: StorageEntryModifier::Optional, - ty: StorageEntryType::Plain(scale_info::meta_type::()), + modifier: StorageEntryModifierIR::Optional, + ty: StorageEntryTypeIR::Plain(scale_info::meta_type::()), default: vec![0], docs: vec![], }, - StorageEntryMetadata { + StorageEntryMetadataIR { name: "GetOptU32WithBuilderNone", - modifier: StorageEntryModifier::Optional, - ty: StorageEntryType::Plain(scale_info::meta_type::()), + modifier: StorageEntryModifierIR::Optional, + ty: StorageEntryTypeIR::Plain(scale_info::meta_type::()), default: vec![0], docs: vec![], }, - StorageEntryMetadata { + StorageEntryMetadataIR { name: "MAPU32", - modifier: StorageEntryModifier::Optional, - ty: StorageEntryType::Map { - hashers: vec![StorageHasher::Blake2_128Concat], + modifier: StorageEntryModifierIR::Optional, + ty: StorageEntryTypeIR::Map { + hashers: vec![StorageHasherIR::Blake2_128Concat], key: scale_info::meta_type::(), value: scale_info::meta_type::<[u8; 4]>(), }, default: vec![0], docs: vec![], }, - StorageEntryMetadata { + StorageEntryMetadataIR { name: "PUBMAPU32", - modifier: StorageEntryModifier::Optional, - ty: StorageEntryType::Map { - hashers: vec![StorageHasher::Blake2_128Concat], + modifier: StorageEntryModifierIR::Optional, + ty: StorageEntryTypeIR::Map { + hashers: vec![StorageHasherIR::Blake2_128Concat], key: scale_info::meta_type::(), value: scale_info::meta_type::<[u8; 4]>(), }, default: vec![0], docs: vec![], }, - StorageEntryMetadata { + StorageEntryMetadataIR { name: "GETMAPU32", - modifier: StorageEntryModifier::Default, - ty: StorageEntryType::Map { - hashers: vec![StorageHasher::Blake2_128Concat], + modifier: StorageEntryModifierIR::Default, + ty: StorageEntryTypeIR::Map { + hashers: vec![StorageHasherIR::Blake2_128Concat], key: scale_info::meta_type::(), value: scale_info::meta_type::<[u8; 4]>(), }, default: vec![0, 0, 0, 0], docs: vec![], }, - StorageEntryMetadata { + StorageEntryMetadataIR { name: "PUBGETMAPU32", - modifier: StorageEntryModifier::Default, - ty: StorageEntryType::Map { - hashers: vec![StorageHasher::Blake2_128Concat], + modifier: StorageEntryModifierIR::Default, + ty: StorageEntryTypeIR::Map { + hashers: vec![StorageHasherIR::Blake2_128Concat], key: scale_info::meta_type::(), value: scale_info::meta_type::<[u8; 4]>(), }, default: vec![0, 0, 0, 0], docs: vec![], }, - StorageEntryMetadata { + StorageEntryMetadataIR { name: "GETMAPU32MYDEF", - modifier: StorageEntryModifier::Default, - ty: StorageEntryType::Map { - hashers: vec![StorageHasher::Blake2_128Concat], + modifier: StorageEntryModifierIR::Default, + ty: StorageEntryTypeIR::Map { + hashers: vec![StorageHasherIR::Blake2_128Concat], key: scale_info::meta_type::(), value: scale_info::meta_type::<[u8; 4]>(), }, default: vec![109, 97, 112, 100], // "map" docs: vec![], }, - StorageEntryMetadata { + StorageEntryMetadataIR { name: "PUBGETMAPU32MYDEF", - modifier: StorageEntryModifier::Default, - ty: StorageEntryType::Map { - hashers: vec![StorageHasher::Blake2_128Concat], + modifier: StorageEntryModifierIR::Default, + ty: StorageEntryTypeIR::Map { + hashers: vec![StorageHasherIR::Blake2_128Concat], key: scale_info::meta_type::(), value: scale_info::meta_type::<[u8; 4]>(), }, default: vec![112, 117, 98, 109], // "pubmap" docs: vec![], }, - StorageEntryMetadata { + StorageEntryMetadataIR { name: "DOUBLEMAP", - modifier: StorageEntryModifier::Optional, - ty: StorageEntryType::Map { + modifier: StorageEntryModifierIR::Optional, + ty: StorageEntryTypeIR::Map { hashers: vec![ - StorageHasher::Blake2_128Concat, - StorageHasher::Blake2_128Concat, + StorageHasherIR::Blake2_128Concat, + StorageHasherIR::Blake2_128Concat, ], key: scale_info::meta_type::<(u32, u32)>(), value: scale_info::meta_type::<[u8; 4]>(), @@ -300,13 +300,13 @@ mod tests { default: vec![0], docs: vec![], }, - StorageEntryMetadata { + StorageEntryMetadataIR { name: "DOUBLEMAP2", - modifier: StorageEntryModifier::Optional, - ty: StorageEntryType::Map { + modifier: StorageEntryModifierIR::Optional, + ty: StorageEntryTypeIR::Map { hashers: vec![ - StorageHasher::Blake2_128Concat, - StorageHasher::Blake2_128Concat, + StorageHasherIR::Blake2_128Concat, + StorageHasherIR::Blake2_128Concat, ], key: scale_info::meta_type::<(u32, u32)>(), value: scale_info::meta_type::<[u8; 4]>(), @@ -314,47 +314,50 @@ mod tests { default: vec![0], docs: vec![], }, - StorageEntryMetadata { + StorageEntryMetadataIR { name: "COMPLEXTYPE1", - modifier: StorageEntryModifier::Default, - ty: StorageEntryType::Plain(scale_info::meta_type::<(Option,)>()), + modifier: StorageEntryModifierIR::Default, + ty: StorageEntryTypeIR::Plain(scale_info::meta_type::<(Option,)>()), default: vec![0], docs: vec![], }, - StorageEntryMetadata { + StorageEntryMetadataIR { name: "COMPLEXTYPE2", - modifier: StorageEntryModifier::Default, - ty: StorageEntryType::Plain(scale_info::meta_type::<( + modifier: StorageEntryModifierIR::Default, + ty: StorageEntryTypeIR::Plain(scale_info::meta_type::<( [[(u16, Option<()>); 32]; 12], u32, )>()), default: [0u8; 1156].to_vec(), docs: vec![], }, - StorageEntryMetadata { + StorageEntryMetadataIR { name: "COMPLEXTYPE3", - modifier: StorageEntryModifier::Default, - ty: StorageEntryType::Plain(scale_info::meta_type::<[u32; 25]>()), + modifier: StorageEntryModifierIR::Default, + ty: StorageEntryTypeIR::Plain(scale_info::meta_type::<[u32; 25]>()), default: [0u8; 100].to_vec(), docs: vec![], }, - StorageEntryMetadata { + StorageEntryMetadataIR { name: "NMAP", - modifier: StorageEntryModifier::Default, - ty: StorageEntryType::Map { + modifier: StorageEntryModifierIR::Default, + ty: StorageEntryTypeIR::Map { key: scale_info::meta_type::<(u32, u16)>(), - hashers: vec![StorageHasher::Blake2_128Concat, StorageHasher::Twox64Concat], + hashers: vec![ + StorageHasherIR::Blake2_128Concat, + StorageHasherIR::Twox64Concat, + ], value: scale_info::meta_type::(), }, default: vec![0], docs: vec![], }, - StorageEntryMetadata { + StorageEntryMetadataIR { name: "NMAP2", - modifier: StorageEntryModifier::Default, - ty: StorageEntryType::Map { + modifier: StorageEntryModifierIR::Default, + ty: StorageEntryTypeIR::Map { key: scale_info::meta_type::(), - hashers: vec![StorageHasher::Blake2_128Concat], + hashers: vec![StorageHasherIR::Blake2_128Concat], value: scale_info::meta_type::(), }, default: vec![0], diff --git a/frame/support/test/tests/instance.rs b/frame/support/test/tests/instance.rs index 8780e1840..9b08e175b 100644 --- a/frame/support/test/tests/instance.rs +++ b/frame/support/test/tests/instance.rs @@ -20,9 +20,9 @@ use codec::{Codec, Decode, Encode, EncodeLike, MaxEncodedLen}; use frame_support::{ inherent::{InherentData, InherentIdentifier, MakeFatalError, ProvideInherent}, - metadata::{ - PalletStorageMetadata, StorageEntryMetadata, StorageEntryModifier, StorageEntryType, - StorageHasher, + metadata_ir::{ + PalletStorageMetadataIR, StorageEntryMetadataIR, StorageEntryModifierIR, + StorageEntryTypeIR, StorageHasherIR, }, traits::{ConstU32, Get}, Parameter, StorageDoubleMap, StorageMap, StorageValue, @@ -410,33 +410,33 @@ fn storage_with_instance_basic_operation() { }); } -fn expected_metadata() -> PalletStorageMetadata { - PalletStorageMetadata { +fn expected_metadata() -> PalletStorageMetadataIR { + PalletStorageMetadataIR { prefix: "Instance2Module2", entries: vec![ - StorageEntryMetadata { + StorageEntryMetadataIR { name: "Value", - modifier: StorageEntryModifier::Default, - ty: StorageEntryType::Plain(scale_info::meta_type::()), + modifier: StorageEntryModifierIR::Default, + ty: StorageEntryTypeIR::Plain(scale_info::meta_type::()), default: vec![0, 0, 0, 0], docs: vec![], }, - StorageEntryMetadata { + StorageEntryMetadataIR { name: "Map", - modifier: StorageEntryModifier::Default, - ty: StorageEntryType::Map { - hashers: vec![StorageHasher::Identity], + modifier: StorageEntryModifierIR::Default, + ty: StorageEntryTypeIR::Map { + hashers: vec![StorageHasherIR::Identity], key: scale_info::meta_type::(), value: scale_info::meta_type::(), }, default: [0u8; 8].to_vec(), docs: vec![], }, - StorageEntryMetadata { + StorageEntryMetadataIR { name: "DoubleMap", - modifier: StorageEntryModifier::Default, - ty: StorageEntryType::Map { - hashers: vec![StorageHasher::Identity, StorageHasher::Identity], + modifier: StorageEntryModifierIR::Default, + ty: StorageEntryTypeIR::Map { + hashers: vec![StorageHasherIR::Identity, StorageHasherIR::Identity], key: scale_info::meta_type::<(u64, u64)>(), value: scale_info::meta_type::(), }, diff --git a/frame/support/test/tests/pallet.rs b/frame/support/test/tests/pallet.rs index fbebbef45..b9e531fb4 100644 --- a/frame/support/test/tests/pallet.rs +++ b/frame/support/test/tests/pallet.rs @@ -37,6 +37,9 @@ use sp_io::{ }; use sp_runtime::{DispatchError, ModuleError}; +/// Latest stable metadata version used for testing. +const LATEST_METADATA_VERSION: u32 = 14; + pub struct SomeType1; impl From for u64 { fn from(_t: SomeType1) -> Self { @@ -1593,6 +1596,43 @@ fn metadata() { pretty_assertions::assert_eq!(actual_metadata.pallets, expected_metadata.pallets); } +#[test] +fn metadata_at_version() { + use frame_support::metadata::*; + use sp_core::Decode; + + let metadata = Runtime::metadata(); + let at_metadata = match Runtime::metadata_at_version(LATEST_METADATA_VERSION) { + Some(opaque) => { + let bytes = &*opaque; + let metadata: RuntimeMetadataPrefixed = Decode::decode(&mut &bytes[..]).unwrap(); + metadata + }, + _ => panic!("metadata has been bumped, test needs to be updated"), + }; + + assert_eq!(metadata, at_metadata); +} + +#[test] +fn metadata_versions() { + assert_eq!(vec![LATEST_METADATA_VERSION], Runtime::metadata_versions()); +} + +#[test] +fn metadata_ir_pallet_runtime_docs() { + let ir = Runtime::metadata_ir(); + let pallet = ir + .pallets + .iter() + .find(|pallet| pallet.name == "Example") + .expect("Pallet should be present"); + + let readme = "Support code for the runtime.\n\nLicense: Apache-2.0"; + let expected = vec![" Pallet documentation", readme, readme]; + assert_eq!(pallet.docs, expected); +} + #[test] fn test_pallet_runtime_docs() { let docs = crate::pallet::Pallet::::pallet_documentation_metadata(); diff --git a/primitives/api/src/lib.rs b/primitives/api/src/lib.rs index 7542ca3f2..ff101c3ad 100644 --- a/primitives/api/src/lib.rs +++ b/primitives/api/src/lib.rs @@ -729,8 +729,20 @@ decl_runtime_apis! { } /// The `Metadata` api trait that returns metadata for the runtime. + #[api_version(2)] pub trait Metadata { /// Returns the metadata of a runtime. fn metadata() -> OpaqueMetadata; + + /// Returns the metadata at a given version. + /// + /// If the given `version` isn't supported, this will return `None`. + /// Use [`Self::metadata_versions`] to find out about supported metadata version of the runtime. + fn metadata_at_version(version: u32) -> Option; + + /// Returns the supported metadata versions. + /// + /// This can be used to call `metadata_at_version`. + fn metadata_versions() -> sp_std::vec::Vec; } } diff --git a/test-utils/runtime/src/lib.rs b/test-utils/runtime/src/lib.rs index 83b4e2977..c9a0ac04d 100644 --- a/test-utils/runtime/src/lib.rs +++ b/test-utils/runtime/src/lib.rs @@ -437,7 +437,6 @@ cfg_if! { #[derive(Clone, Eq, PartialEq, TypeInfo)] pub struct Runtime; - impl GetNodeBlockType for Runtime { type NodeBlock = Block; } @@ -729,6 +728,14 @@ cfg_if! { fn metadata() -> OpaqueMetadata { unimplemented!() } + + fn metadata_at_version(_version: u32) -> Option { + unimplemented!() + } + + fn metadata_versions() -> sp_std::vec::Vec { + unimplemented!() + } } impl sp_transaction_pool::runtime_api::TaggedTransactionQueue for Runtime { @@ -1021,6 +1028,14 @@ cfg_if! { fn metadata() -> OpaqueMetadata { unimplemented!() } + + fn metadata_at_version(_version: u32) -> Option { + unimplemented!() + } + + fn metadata_versions() -> sp_std::vec::Vec { + unimplemented!() + } } impl sp_transaction_pool::runtime_api::TaggedTransactionQueue for Runtime { From 36ed84069343c55ec4280b2d3cc49c49e3b019d2 Mon Sep 17 00:00:00 2001 From: Mira Ressel Date: Wed, 15 Mar 2023 19:27:18 +0100 Subject: [PATCH 238/558] pin specific version of timestamp script (#13613) --- .gitlab-ci.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 44f98db3b..6cf062736 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -313,6 +313,7 @@ include: rules: - if: $PIPELINE != "automatic-crate-publishing" - project: parity/infrastructure/ci_cd/shared + ref: v0.1 file: /common/timestamp.yml #### stage: notify From 8b5306e1a607a14e08e13fd521f747319ecb6846 Mon Sep 17 00:00:00 2001 From: Oliver Tale-Yazdi Date: Wed, 15 Mar 2023 21:43:22 +0100 Subject: [PATCH 239/558] Add dedicated `FeePolynomial` struct (#13612) * Add FeePolynomial struct Signed-off-by: Oliver Tale-Yazdi * Add Weight::without_{ref_time, proof_size} Signed-off-by: Oliver Tale-Yazdi * Docs Signed-off-by: Oliver Tale-Yazdi * Cleanup code Signed-off-by: Oliver Tale-Yazdi * Add docs Signed-off-by: Oliver Tale-Yazdi * doc Signed-off-by: Oliver Tale-Yazdi * Fix docs Signed-off-by: Oliver Tale-Yazdi * docs Signed-off-by: Oliver Tale-Yazdi --------- Signed-off-by: Oliver Tale-Yazdi --- primitives/weights/src/lib.rs | 95 ++++++++++++++++++++++------- primitives/weights/src/weight_v2.rs | 10 +++ 2 files changed, 82 insertions(+), 23 deletions(-) diff --git a/primitives/weights/src/lib.rs b/primitives/weights/src/lib.rs index 8a8423073..faa641a0f 100644 --- a/primitives/weights/src/lib.rs +++ b/primitives/weights/src/lib.rs @@ -30,7 +30,7 @@ use scale_info::TypeInfo; use serde::{Deserialize, Serialize}; use smallvec::SmallVec; use sp_arithmetic::{ - traits::{BaseArithmetic, SaturatedConversion, Saturating, Unsigned}, + traits::{BaseArithmetic, SaturatedConversion, Unsigned}, Perbill, }; use sp_core::Get; @@ -118,9 +118,77 @@ pub struct WeightToFeeCoefficient { pub degree: u8, } -/// A list of coefficients that represent one polynomial. +impl WeightToFeeCoefficient +where + Balance: BaseArithmetic + From + Copy + Unsigned, +{ + /// Evaluate the term at `x` and saturatingly amalgamate into `result`. + /// + /// The unsigned value for the term is calculated as: + /// ```ignore + /// (frac * x^(degree) + integer * x^(degree)) + /// ``` + /// Depending on the value of `negative`, it is added or subtracted from the `result`. + pub fn saturating_eval(&self, mut result: Balance, x: Balance) -> Balance { + let power = x.saturating_pow(self.degree.into()); + + let frac = self.coeff_frac * power; // Overflow safe. + let integer = self.coeff_integer.saturating_mul(power); + // Do not add them together here to avoid an underflow. + + if self.negative { + result = result.saturating_sub(frac); + result = result.saturating_sub(integer); + } else { + result = result.saturating_add(frac); + result = result.saturating_add(integer); + } + + result + } +} + +/// A list of coefficients that represent a polynomial. pub type WeightToFeeCoefficients = SmallVec<[WeightToFeeCoefficient; 4]>; +/// A list of coefficients that represent a polynomial. +/// +/// Can be [eval](Self::eval)uated at a specific `u64` to get the fee. The evaluations happens by +/// summing up all term [results](`WeightToFeeCoefficient::saturating_eval`). The order of the +/// coefficients matters since it uses saturating arithmetic. This struct does therefore not model a +/// polynomial in the mathematical sense (polynomial ring). +/// +/// For visualization purposes, the formulas of the unsigned terms look like: +/// +/// ```ignore +/// (c[0].frac * x^(c[0].degree) + c[0].integer * x^(c[0].degree)) +/// (c[1].frac * x^(c[1].degree) + c[1].integer * x^(c[1].degree)) +/// ... +/// ``` +/// Depending on the value of `c[i].negative`, each term is added or subtracted from the result. +/// The result is initialized as zero. +pub struct FeePolynomial { + coefficients: SmallVec<[WeightToFeeCoefficient; 4]>, +} + +impl From> for FeePolynomial { + fn from(coefficients: WeightToFeeCoefficients) -> Self { + Self { coefficients } + } +} + +impl FeePolynomial +where + Balance: BaseArithmetic + From + Copy + Unsigned, +{ + /// Evaluate the polynomial at a specific `x`. + pub fn eval(&self, x: u64) -> Balance { + self.coefficients.iter().fold(Balance::zero(), |acc, term| { + term.saturating_eval(acc, Balance::saturated_from(x)) + }) + } +} + /// A trait that describes the weight to fee calculation. pub trait WeightToFee { /// The type that is returned as result from calculation. @@ -157,27 +225,8 @@ where /// This should not be overridden in most circumstances. Calculation is done in the /// `Balance` type and never overflows. All evaluation is saturating. fn weight_to_fee(weight: &Weight) -> Self::Balance { - Self::polynomial() - .iter() - .fold(Self::Balance::saturated_from(0u32), |mut acc, args| { - let w = Self::Balance::saturated_from(weight.ref_time()) - .saturating_pow(args.degree.into()); - - // The sum could get negative. Therefore we only sum with the accumulator. - // The Perbill Mul implementation is non overflowing. - let frac = args.coeff_frac * w; - let integer = args.coeff_integer.saturating_mul(w); - - if args.negative { - acc = acc.saturating_sub(frac); - acc = acc.saturating_sub(integer); - } else { - acc = acc.saturating_add(frac); - acc = acc.saturating_add(integer); - } - - acc - }) + let poly: FeePolynomial = Self::polynomial().into(); + poly.eval(weight.ref_time()) } } diff --git a/primitives/weights/src/weight_v2.rs b/primitives/weights/src/weight_v2.rs index ca1371459..76c40c16c 100644 --- a/primitives/weights/src/weight_v2.rs +++ b/primitives/weights/src/weight_v2.rs @@ -74,6 +74,16 @@ impl Weight { &mut self.proof_size } + /// Return self but discard any reference time. + pub const fn without_ref_time(&self) -> Self { + Self { ref_time: 0, proof_size: self.proof_size } + } + + /// Return self but discard any proof size. + pub const fn without_proof_size(&self) -> Self { + Self { ref_time: self.ref_time, proof_size: 0 } + } + pub const MAX: Self = Self { ref_time: u64::MAX, proof_size: u64::MAX }; /// Get the conservative min of `self` and `other` weight. From 86731af2629fdedc39a31bd9666fdd1b5df5ed2d Mon Sep 17 00:00:00 2001 From: Francisco Aguirre Date: Wed, 15 Mar 2023 18:04:28 -0300 Subject: [PATCH 240/558] Make Pay trait from salaries pallet more generic (#13609) * Make Pay trait from salaries pallet more generic * Rename and add missing * Update frame/support/src/traits/tokens/pay.rs * Update pay.rs * Update pay.rs * Update pay.rs * Add better documentation for the AssetKind associated type --------- Co-authored-by: Gavin Wood Co-authored-by: parity-processbot <> --- bin/node/runtime/src/lib.rs | 6 +- frame/salary/src/lib.rs | 74 ++---------------- frame/salary/src/tests.rs | 11 ++- frame/support/src/traits/tokens.rs | 2 + frame/support/src/traits/tokens/pay.rs | 103 +++++++++++++++++++++++++ 5 files changed, 122 insertions(+), 74 deletions(-) create mode 100644 frame/support/src/traits/tokens/pay.rs diff --git a/bin/node/runtime/src/lib.rs b/bin/node/runtime/src/lib.rs index 5d1dea02e..9f4ad2ed2 100644 --- a/bin/node/runtime/src/lib.rs +++ b/bin/node/runtime/src/lib.rs @@ -33,7 +33,7 @@ use frame_support::{ parameter_types, traits::{ fungible::ItemOf, - tokens::{nonfungibles_v2::Inspect, GetSalary}, + tokens::{nonfungibles_v2::Inspect, GetSalary, PayFromAccount}, AsEnsureOriginWithArg, ConstBool, ConstU128, ConstU16, ConstU32, Currency, EitherOfDiverse, EqualPrivilegeOnly, Everything, Imbalance, InstanceFilter, KeyOwnerProofSystem, LockIdentifier, Nothing, OnUnbalanced, U128CurrencyToVote, WithdrawReasons, @@ -56,7 +56,7 @@ use pallet_election_provider_multi_phase::SolutionAccuracyOf; use pallet_im_online::sr25519::AuthorityId as ImOnlineId; use pallet_nfts::PalletFeatures; use pallet_nis::WithMaximumOf; -use pallet_session::historical::{self as pallet_session_historical}; +use pallet_session::historical as pallet_session_historical; pub use pallet_transaction_payment::{CurrencyAdapter, Multiplier, TargetedFeeAdjustment}; use pallet_transaction_payment::{FeeDetails, RuntimeDispatchInfo}; use sp_api::impl_runtime_apis; @@ -1567,7 +1567,7 @@ impl GetSalary for SalaryForRank { impl pallet_salary::Config for Runtime { type WeightInfo = (); type RuntimeEvent = RuntimeEvent; - type Paymaster = pallet_salary::PayFromAccount; + type Paymaster = PayFromAccount; type Members = RankedCollective; type Salary = SalaryForRank; type RegistrationPeriod = ConstU32<200>; diff --git a/frame/salary/src/lib.rs b/frame/salary/src/lib.rs index 6f9e63271..0b2b4b47d 100644 --- a/frame/salary/src/lib.rs +++ b/frame/salary/src/lib.rs @@ -20,18 +20,17 @@ #![cfg_attr(not(feature = "std"), no_std)] #![recursion_limit = "128"] -use codec::{Decode, Encode, FullCodec, MaxEncodedLen}; +use codec::{Decode, Encode, MaxEncodedLen}; use scale_info::TypeInfo; use sp_arithmetic::traits::{Saturating, Zero}; -use sp_core::TypedGet; use sp_runtime::Perbill; -use sp_std::{fmt::Debug, marker::PhantomData, prelude::*}; +use sp_std::{marker::PhantomData, prelude::*}; use frame_support::{ dispatch::DispatchResultWithPostInfo, ensure, traits::{ - tokens::{fungible, Balance, GetSalary}, + tokens::{GetSalary, Pay, PaymentStatus}, RankedMembers, }, RuntimeDebug, @@ -50,67 +49,6 @@ pub use weights::WeightInfo; /// Payroll cycle. pub type Cycle = u32; -/// Status for making a payment via the `Pay::pay` trait function. -#[derive(Encode, Decode, Eq, PartialEq, Clone, TypeInfo, MaxEncodedLen, RuntimeDebug)] -pub enum PaymentStatus { - /// Payment is in progress. Nothing to report yet. - InProgress, - /// Payment status is unknowable. It will never be reported successful or failed. - Unknown, - /// Payment happened successfully. - Success, - /// Payment failed. It may safely be retried. - Failure, -} - -/// Can be implemented by `PayFromAccount` using a `fungible` impl, but can also be implemented with -/// XCM/MultiAsset and made generic over assets. -pub trait Pay { - /// The type by which we measure units of the currency in which we make payments. - type Balance: Balance; - /// The type by which we identify the individuals to whom a payment may be made. - type AccountId; - /// An identifier given to an individual payment. - type Id: FullCodec + MaxEncodedLen + TypeInfo + Clone + Eq + PartialEq + Debug + Copy; - /// Make a payment and return an identifier for later evaluation of success in some off-chain - /// mechanism (likely an event, but possibly not on this chain). - fn pay(who: &Self::AccountId, amount: Self::Balance) -> Result; - /// Check how a payment has proceeded. `id` must have been a previously returned by `pay` for - /// the result of this call to be meaningful. - fn check_payment(id: Self::Id) -> PaymentStatus; - /// Ensure that a call to pay with the given parameters will be successful if done immediately - /// after this call. Used in benchmarking code. - #[cfg(feature = "runtime-benchmarks")] - fn ensure_successful(who: &Self::AccountId, amount: Self::Balance); - /// Ensure that a call to `check_payment` with the given parameters will return either `Success` - /// or `Failure`. - #[cfg(feature = "runtime-benchmarks")] - fn ensure_concluded(id: Self::Id); -} - -/// Simple implementation of `Pay` which makes a payment from a "pot" - i.e. a single account. -pub struct PayFromAccount(sp_std::marker::PhantomData<(F, A)>); -impl + fungible::Mutate> Pay - for PayFromAccount -{ - type Balance = F::Balance; - type AccountId = A::Type; - type Id = (); - fn pay(who: &Self::AccountId, amount: Self::Balance) -> Result { - >::transfer(&A::get(), who, amount, false).map_err(|_| ())?; - Ok(()) - } - fn check_payment(_: ()) -> PaymentStatus { - PaymentStatus::Success - } - #[cfg(feature = "runtime-benchmarks")] - fn ensure_successful(_: &Self::AccountId, amount: Self::Balance) { - >::mint_into(&A::get(), amount).unwrap(); - } - #[cfg(feature = "runtime-benchmarks")] - fn ensure_concluded(_: Self::Id) {} -} - /// The status of the pallet instance. #[derive(Encode, Decode, Eq, PartialEq, Clone, TypeInfo, MaxEncodedLen, RuntimeDebug)] pub struct StatusType { @@ -168,7 +106,7 @@ pub mod pallet { /// Means by which we can make payments to accounts. This also defines the currency and the /// balance which we use to denote that currency. - type Paymaster: Pay::AccountId>; + type Paymaster: Pay::AccountId, AssetKind = ()>; /// The current membership of payees. type Members: RankedMembers::AccountId>; @@ -498,8 +436,8 @@ pub mod pallet { claimant.last_active = status.cycle_index; - let id = - T::Paymaster::pay(&beneficiary, payout).map_err(|()| Error::::PayError)?; + let id = T::Paymaster::pay(&beneficiary, (), payout) + .map_err(|()| Error::::PayError)?; claimant.status = Attempted { registered, id, amount: payout }; diff --git a/frame/salary/src/tests.rs b/frame/salary/src/tests.rs index e54b9612b..1b7bc6cbb 100644 --- a/frame/salary/src/tests.rs +++ b/frame/salary/src/tests.rs @@ -99,11 +99,16 @@ fn set_status(id: u64, s: PaymentStatus) { pub struct TestPay; impl Pay for TestPay { - type AccountId = u64; + type Beneficiary = u64; type Balance = u64; type Id = u64; + type AssetKind = (); - fn pay(who: &Self::AccountId, amount: Self::Balance) -> Result { + fn pay( + who: &Self::Beneficiary, + _: Self::AssetKind, + amount: Self::Balance, + ) -> Result { PAID.with(|paid| *paid.borrow_mut().entry(*who).or_default() += amount); Ok(LAST_ID.with(|lid| { let x = *lid.borrow(); @@ -115,7 +120,7 @@ impl Pay for TestPay { STATUS.with(|s| s.borrow().get(&id).cloned().unwrap_or(PaymentStatus::Unknown)) } #[cfg(feature = "runtime-benchmarks")] - fn ensure_successful(_: &Self::AccountId, _: Self::Balance) {} + fn ensure_successful(_: &Self::Beneficiary, _: Self::Balance) {} #[cfg(feature = "runtime-benchmarks")] fn ensure_concluded(id: Self::Id) { set_status(id, PaymentStatus::Failure) diff --git a/frame/support/src/traits/tokens.rs b/frame/support/src/traits/tokens.rs index e1a96f621..2d2bd63ad 100644 --- a/frame/support/src/traits/tokens.rs +++ b/frame/support/src/traits/tokens.rs @@ -27,7 +27,9 @@ pub mod nonfungible_v2; pub mod nonfungibles; pub mod nonfungibles_v2; pub use imbalance::Imbalance; +pub mod pay; pub use misc::{ AssetId, Balance, BalanceConversion, BalanceStatus, ConvertRank, DepositConsequence, ExistenceRequirement, GetSalary, Locker, WithdrawConsequence, WithdrawReasons, }; +pub use pay::{Pay, PayFromAccount, PaymentStatus}; diff --git a/frame/support/src/traits/tokens/pay.rs b/frame/support/src/traits/tokens/pay.rs new file mode 100644 index 000000000..1c6a147b5 --- /dev/null +++ b/frame/support/src/traits/tokens/pay.rs @@ -0,0 +1,103 @@ +// This file is part of Substrate. + +// Copyright (C) Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +//! The Pay trait and associated types. + +use codec::{Decode, Encode, FullCodec, MaxEncodedLen}; +use scale_info::TypeInfo; +use sp_core::{RuntimeDebug, TypedGet}; +use sp_std::fmt::Debug; + +use super::{fungible, Balance}; + +/// Can be implemented by `PayFromAccount` using a `fungible` impl, but can also be implemented with +/// XCM/MultiAsset and made generic over assets. +pub trait Pay { + /// The type by which we measure units of the currency in which we make payments. + type Balance: Balance; + /// The type by which we identify the beneficiaries to whom a payment may be made. + type Beneficiary; + /// The type for the kinds of asset that are going to be paid. + /// + /// The unit type can be used here to indicate there's only one kind of asset to do payments + /// with. When implementing, it should be clear from the context what that asset is. + type AssetKind; + /// An identifier given to an individual payment. + type Id: FullCodec + MaxEncodedLen + TypeInfo + Clone + Eq + PartialEq + Debug + Copy; + /// Make a payment and return an identifier for later evaluation of success in some off-chain + /// mechanism (likely an event, but possibly not on this chain). + fn pay( + who: &Self::Beneficiary, + asset_kind: Self::AssetKind, + amount: Self::Balance, + ) -> Result; + /// Check how a payment has proceeded. `id` must have been previously returned by `pay` for + /// the result of this call to be meaningful. Once this returns anything other than + /// `InProgress` for some `id` it must return `Unknown` rather than the actual result + /// value. + fn check_payment(id: Self::Id) -> PaymentStatus; + /// Ensure that a call to pay with the given parameters will be successful if done immediately + /// after this call. Used in benchmarking code. + #[cfg(feature = "runtime-benchmarks")] + fn ensure_successful(who: &Self::Beneficiary, amount: Self::Balance); + /// Ensure that a call to `check_payment` with the given parameters will return either `Success` + /// or `Failure`. + #[cfg(feature = "runtime-benchmarks")] + fn ensure_concluded(id: Self::Id); +} + +/// Status for making a payment via the `Pay::pay` trait function. +#[derive(Encode, Decode, Eq, PartialEq, Clone, TypeInfo, MaxEncodedLen, RuntimeDebug)] +pub enum PaymentStatus { + /// Payment is in progress. Nothing to report yet. + InProgress, + /// Payment status is unknowable. It may already have reported the result, or if not then + /// it will never be reported successful or failed. + Unknown, + /// Payment happened successfully. + Success, + /// Payment failed. It may safely be retried. + Failure, +} + +/// Simple implementation of `Pay` which makes a payment from a "pot" - i.e. a single account. +pub struct PayFromAccount(sp_std::marker::PhantomData<(F, A)>); +impl + fungible::Mutate> Pay + for PayFromAccount +{ + type Balance = F::Balance; + type Beneficiary = A::Type; + type AssetKind = (); + type Id = (); + fn pay( + who: &Self::Beneficiary, + _: Self::AssetKind, + amount: Self::Balance, + ) -> Result { + >::transfer(&A::get(), who, amount, false).map_err(|_| ())?; + Ok(()) + } + fn check_payment(_: ()) -> PaymentStatus { + PaymentStatus::Success + } + #[cfg(feature = "runtime-benchmarks")] + fn ensure_successful(_: &Self::Beneficiary, amount: Self::Balance) { + >::mint_into(&A::get(), amount).unwrap(); + } + #[cfg(feature = "runtime-benchmarks")] + fn ensure_concluded(_: Self::Id) {} +} From 9f933c4d87a4682233328daaafbc9bbe2cc8e778 Mon Sep 17 00:00:00 2001 From: Sacha Lansky Date: Thu, 16 Mar 2023 08:29:20 +0000 Subject: [PATCH 241/558] Fix typos (#13616) --- frame/examples/offchain-worker/src/lib.rs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/frame/examples/offchain-worker/src/lib.rs b/frame/examples/offchain-worker/src/lib.rs index 418ad5d13..1a63955c0 100644 --- a/frame/examples/offchain-worker/src/lib.rs +++ b/frame/examples/offchain-worker/src/lib.rs @@ -166,7 +166,7 @@ pub mod pallet { /// /// By implementing `fn offchain_worker` you declare a new offchain worker. /// This function will be called when the node is fully synced and a new best block is - /// succesfuly imported. + /// successfully imported. /// Note that it's not guaranteed for offchain workers to run on EVERY block, there might /// be cases where some blocks are skipped, or for some the worker runs twice (re-orgs), /// so the code should be able to handle that. @@ -224,7 +224,7 @@ pub mod pallet { /// The transaction needs to be signed (see `ensure_signed`) check, so that the caller /// pays a fee to execute it. /// This makes sure that it's not easy (or rather cheap) to attack the chain by submitting - /// excesive transactions, but note that it doesn't ensure the price oracle is actually + /// excessive transactions, but note that it doesn't ensure the price oracle is actually /// working and receives (and provides) meaningful data. /// This example is not focused on correctness of the oracle itself, but rather its /// purpose is to showcase offchain worker capabilities. @@ -336,7 +336,7 @@ pub mod pallet { /// Defines the block when next unsigned transaction will be accepted. /// - /// To prevent spam of unsigned (and unpayed!) transactions on the network, + /// To prevent spam of unsigned (and unpaid!) transactions on the network, /// we only allow one transaction every `T::UnsignedInterval` blocks. /// This storage entry defines when new transaction is going to be accepted. #[pallet::storage] @@ -570,12 +570,12 @@ impl Pallet { fn fetch_price() -> Result { // We want to keep the offchain worker execution time reasonable, so we set a hard-coded // deadline to 2s to complete the external call. - // You can also wait idefinitely for the response, however you may still get a timeout + // You can also wait indefinitely for the response, however you may still get a timeout // coming from the host machine. let deadline = sp_io::offchain::timestamp().add(Duration::from_millis(2_000)); // Initiate an external HTTP GET request. // This is using high-level wrappers from `sp_runtime`, for the low-level calls that - // you can find in `sp_io`. The API is trying to be similar to `reqwest`, but + // you can find in `sp_io`. The API is trying to be similar to `request`, but // since we are running in a custom WASM execution environment we can't simply // import the library here. let request = From b0700a6d3e9a9dfa3897971ee62a06134bda17d3 Mon Sep 17 00:00:00 2001 From: Adrian Catangiu Date: Thu, 16 Mar 2023 11:02:39 +0200 Subject: [PATCH 242/558] sc-consensus-beefy: improve beefy gossip validator (#13606) * sc-consensus-beefy: improve beefy gossip validator Old gossip validator was pretty dumb, being very permissive with incoming votes - only condition it had was to be newer than best finalized. New filter conditions: - voter rounds are initialized (discarding votes until voter is actually active), - only votes for current active set id are accepted, - only votes for rounds in the current voting session are accepted, - only votes for GRANDPA finalized blocks are accepted, - when BEEFY voter reaches mandatory round, only votes for said mandatory round are accepted. New validator uses the VoterOracle to easily implement above conditions and only allow through votes that are immediately useful to the voter. After every GRANDPA or BEEFY finality, the gossip validator filter is updated. * sc-consensus-beefy: remove votes enqueueing Since gossip validator will simply disallow votes for future rounds, and only allow votes that the voter can immediately process, there is no need for the voter to enqueue votes. It will see these "future" votes later in rebroadcasts, when voter will also be able to process them. Only at that point does gossip accept and consume them. * sc-consensus-beefy: refactor persistent state Move best-beefy and best-grandpa into VoterOracle instead of passing them around as params. VoterOracle ultimately needs to know best-beefy and/or best-grandpa for most of its functions. * sc-consensus-beefy: further restrict gossip validator Assuming mandatory done in current session: Instead of allowing votes for any round in the current session, only accept votes for rounds equal or better than best BEEFY finalized. * sc-consensus-beefy: add a couple of comments * sc-consensus-beefy: fix tests involving multiple tasks Finalize blocks one a time in tests where we want gossip to happen in a certain round. Otherwise, some tasks may be left behind in terms of gossip round numbers because once "scheduled" a task will greedily process as much as possible. This change should be in line with the real-world scenario where voters run "in parallel" across nodes, the only points of synchronization being the finality notifications. * sc-consensus-beefy: address review comments --------- Signed-off-by: acatangiu --- client/consensus/beefy/src/aux_schema.rs | 6 +- .../beefy/src/communication/gossip.rs | 204 +++---- client/consensus/beefy/src/lib.rs | 10 + client/consensus/beefy/src/metrics.rs | 24 - client/consensus/beefy/src/tests.rs | 183 ++---- client/consensus/beefy/src/worker.rs | 532 ++++++++---------- 6 files changed, 385 insertions(+), 574 deletions(-) diff --git a/client/consensus/beefy/src/aux_schema.rs b/client/consensus/beefy/src/aux_schema.rs index 2e99b4cc4..84186140b 100644 --- a/client/consensus/beefy/src/aux_schema.rs +++ b/client/consensus/beefy/src/aux_schema.rs @@ -28,7 +28,7 @@ use sp_runtime::traits::Block as BlockT; const VERSION_KEY: &[u8] = b"beefy_auxschema_version"; const WORKER_STATE_KEY: &[u8] = b"beefy_voter_state"; -const CURRENT_VERSION: u32 = 2; +const CURRENT_VERSION: u32 = 3; pub(crate) fn write_current_version(backend: &BE) -> ClientResult<()> { info!(target: LOG_TARGET, "🥩 write aux schema version {:?}", CURRENT_VERSION); @@ -63,8 +63,8 @@ where match version { None => (), - Some(1) => (), // version 1 is totally obsolete and should be simply ignored - Some(2) => return load_decode::<_, PersistedState>(backend, WORKER_STATE_KEY), + Some(1) | Some(2) => (), // versions 1 & 2 are obsolete and should be simply ignored + Some(3) => return load_decode::<_, PersistedState>(backend, WORKER_STATE_KEY), other => return Err(ClientError::Backend(format!("Unsupported BEEFY DB version: {:?}", other))), } diff --git a/client/consensus/beefy/src/communication/gossip.rs b/client/consensus/beefy/src/communication/gossip.rs index 219203ee4..e49382251 100644 --- a/client/consensus/beefy/src/communication/gossip.rs +++ b/client/consensus/beefy/src/communication/gossip.rs @@ -31,11 +31,14 @@ use wasm_timer::Instant; use crate::{communication::peers::KnownPeers, keystore::BeefyKeystore, LOG_TARGET}; use sp_consensus_beefy::{ crypto::{Public, Signature}, - VoteMessage, + ValidatorSetId, VoteMessage, }; // Timeout for rebroadcasting messages. -const REBROADCAST_AFTER: Duration = Duration::from_secs(60 * 5); +#[cfg(not(test))] +const REBROADCAST_AFTER: Duration = Duration::from_secs(60); +#[cfg(test)] +const REBROADCAST_AFTER: Duration = Duration::from_secs(5); /// Gossip engine messages topic pub(crate) fn topic() -> B::Hash @@ -45,45 +48,51 @@ where <::Hashing as Hash>::hash(b"beefy") } +#[derive(Debug)] +pub(crate) struct GossipVoteFilter { + pub start: NumberFor, + pub end: NumberFor, + pub validator_set_id: ValidatorSetId, +} + /// A type that represents hash of the message. pub type MessageHash = [u8; 8]; -struct KnownVotes { - last_done: Option>, +struct VotesFilter { + filter: Option>, live: BTreeMap, fnv::FnvHashSet>, } -impl KnownVotes { +impl VotesFilter { pub fn new() -> Self { - Self { last_done: None, live: BTreeMap::new() } - } - - /// Create new round votes set if not already present. - fn insert(&mut self, round: NumberFor) { - self.live.entry(round).or_default(); + Self { filter: None, live: BTreeMap::new() } } - /// Remove `round` and older from live set, update `last_done` accordingly. - fn conclude(&mut self, round: NumberFor) { - self.live.retain(|&number, _| number > round); - self.last_done = self.last_done.max(Some(round)); + /// Update filter to new `start` and `set_id`. + fn update(&mut self, filter: GossipVoteFilter) { + self.live.retain(|&round, _| round >= filter.start && round <= filter.end); + self.filter = Some(filter); } - /// Return true if `round` is newer than previously concluded rounds. + /// Return true if `round` is >= than `max(session_start, best_beefy)`, + /// and vote set id matches session set id. /// /// Latest concluded round is still considered alive to allow proper gossiping for it. - fn is_live(&self, round: &NumberFor) -> bool { - Some(*round) >= self.last_done + fn is_live(&self, round: NumberFor, set_id: ValidatorSetId) -> bool { + self.filter + .as_ref() + .map(|f| set_id == f.validator_set_id && round >= f.start && round <= f.end) + .unwrap_or(false) } /// Add new _known_ `hash` to the round's known votes. - fn add_known(&mut self, round: &NumberFor, hash: MessageHash) { - self.live.get_mut(round).map(|known| known.insert(hash)); + fn add_known(&mut self, round: NumberFor, hash: MessageHash) { + self.live.entry(round).or_default().insert(hash); } /// Check if `hash` is already part of round's known votes. - fn is_known(&self, round: &NumberFor, hash: &MessageHash) -> bool { - self.live.get(round).map(|known| known.contains(hash)).unwrap_or(false) + fn is_known(&self, round: NumberFor, hash: &MessageHash) -> bool { + self.live.get(&round).map(|known| known.contains(hash)).unwrap_or(false) } } @@ -100,7 +109,7 @@ where B: Block, { topic: B::Hash, - known_votes: RwLock>, + votes_filter: RwLock>, next_rebroadcast: Mutex, known_peers: Arc>>, } @@ -112,26 +121,18 @@ where pub fn new(known_peers: Arc>>) -> GossipValidator { GossipValidator { topic: topic::(), - known_votes: RwLock::new(KnownVotes::new()), + votes_filter: RwLock::new(VotesFilter::new()), next_rebroadcast: Mutex::new(Instant::now() + REBROADCAST_AFTER), known_peers, } } - /// Note a voting round. - /// - /// Noting round will track gossiped votes for `round`. - pub(crate) fn note_round(&self, round: NumberFor) { - debug!(target: LOG_TARGET, "🥩 About to note gossip round #{}", round); - self.known_votes.write().insert(round); - } - - /// Conclude a voting round. + /// Update gossip validator filter. /// - /// This can be called once round is complete so we stop gossiping for it. - pub(crate) fn conclude_round(&self, round: NumberFor) { - debug!(target: LOG_TARGET, "🥩 About to drop gossip round #{}", round); - self.known_votes.write().conclude(round); + /// Only votes for `set_id` and rounds `start <= round <= end` will be accepted. + pub(crate) fn update_filter(&self, filter: GossipVoteFilter) { + debug!(target: LOG_TARGET, "🥩 New gossip filter {:?}", filter); + self.votes_filter.write().update(filter); } } @@ -152,25 +153,26 @@ where if let Ok(msg) = VoteMessage::, Public, Signature>::decode(&mut data) { let msg_hash = twox_64(data); let round = msg.commitment.block_number; + let set_id = msg.commitment.validator_set_id; + self.known_peers.lock().note_vote_for(*sender, round); // Verify general usefulness of the message. // We are going to discard old votes right away (without verification) // Also we keep track of already received votes to avoid verifying duplicates. { - let known_votes = self.known_votes.read(); + let filter = self.votes_filter.read(); - if !known_votes.is_live(&round) { + if !filter.is_live(round, set_id) { return ValidationResult::Discard } - if known_votes.is_known(&round, &msg_hash) { + if filter.is_known(round, &msg_hash) { return ValidationResult::ProcessAndKeep(self.topic) } } if BeefyKeystore::verify(&msg.id, &msg.signature, &msg.commitment.encode()) { - self.known_votes.write().add_known(&round, msg_hash); - self.known_peers.lock().note_vote_for(*sender, round); + self.votes_filter.write().add_known(round, msg_hash); return ValidationResult::ProcessAndKeep(self.topic) } else { // TODO: report peer @@ -185,7 +187,7 @@ where } fn message_expired<'a>(&'a self) -> Box bool + 'a> { - let known_votes = self.known_votes.read(); + let filter = self.votes_filter.read(); Box::new(move |_topic, mut data| { let msg = match VoteMessage::, Public, Signature>::decode(&mut data) { Ok(vote) => vote, @@ -193,7 +195,8 @@ where }; let round = msg.commitment.block_number; - let expired = !known_votes.is_live(&round); + let set_id = msg.commitment.validator_set_id; + let expired = !filter.is_live(round, set_id); trace!(target: LOG_TARGET, "🥩 Message for round #{} expired: {}", round, expired); @@ -208,6 +211,7 @@ where let now = Instant::now(); let mut next_rebroadcast = self.next_rebroadcast.lock(); if now >= *next_rebroadcast { + trace!(target: LOG_TARGET, "🥩 Gossip rebroadcast"); *next_rebroadcast = now + REBROADCAST_AFTER; true } else { @@ -215,7 +219,7 @@ where } }; - let known_votes = self.known_votes.read(); + let filter = self.votes_filter.read(); Box::new(move |_who, intent, _topic, mut data| { if let MessageIntent::PeriodicRebroadcast = intent { return do_rebroadcast @@ -227,7 +231,8 @@ where }; let round = msg.commitment.block_number; - let allowed = known_votes.is_live(&round); + let set_id = msg.commitment.validator_set_id; + let allowed = filter.is_live(round, set_id); trace!(target: LOG_TARGET, "🥩 Message for round #{} allowed: {}", round, allowed); @@ -252,81 +257,35 @@ mod tests { #[test] fn known_votes_insert_remove() { - let mut kv = KnownVotes::::new(); + let mut kv = VotesFilter::::new(); + let msg_hash = twox_64(b"data"); - kv.insert(1); - kv.insert(1); - kv.insert(2); + kv.add_known(1, msg_hash); + kv.add_known(1, msg_hash); + kv.add_known(2, msg_hash); assert_eq!(kv.live.len(), 2); - let mut kv = KnownVotes::::new(); - kv.insert(1); - kv.insert(2); - kv.insert(3); + kv.add_known(3, msg_hash); + assert!(kv.is_known(3, &msg_hash)); + assert!(!kv.is_known(3, &twox_64(b"other"))); + assert!(!kv.is_known(4, &msg_hash)); + assert_eq!(kv.live.len(), 3); - assert!(kv.last_done.is_none()); - kv.conclude(2); - assert_eq!(kv.live.len(), 1); - assert!(!kv.live.contains_key(&2)); - assert_eq!(kv.last_done, Some(2)); + assert!(kv.filter.is_none()); + assert!(!kv.is_live(1, 1)); - kv.conclude(1); - assert_eq!(kv.last_done, Some(2)); + kv.update(GossipVoteFilter { start: 3, end: 10, validator_set_id: 1 }); + assert_eq!(kv.live.len(), 1); + assert!(kv.live.contains_key(&3)); + assert!(!kv.is_live(2, 1)); + assert!(kv.is_live(3, 1)); + assert!(kv.is_live(4, 1)); + assert!(!kv.is_live(4, 2)); - kv.conclude(3); - assert_eq!(kv.last_done, Some(3)); + kv.update(GossipVoteFilter { start: 5, end: 10, validator_set_id: 2 }); assert!(kv.live.is_empty()); } - #[test] - fn note_and_drop_round_works() { - let gv = GossipValidator::::new(Arc::new(Mutex::new(KnownPeers::new()))); - - gv.note_round(1u64); - - assert!(gv.known_votes.read().is_live(&1u64)); - - gv.note_round(3u64); - gv.note_round(7u64); - gv.note_round(10u64); - - assert_eq!(gv.known_votes.read().live.len(), 4); - - gv.conclude_round(7u64); - - let votes = gv.known_votes.read(); - - // rounds 1 and 3 are outdated, don't gossip anymore - assert!(!votes.is_live(&1u64)); - assert!(!votes.is_live(&3u64)); - // latest concluded round is still gossiped - assert!(votes.is_live(&7u64)); - // round 10 is alive and in-progress - assert!(votes.is_live(&10u64)); - } - - #[test] - fn note_same_round_twice() { - let gv = GossipValidator::::new(Arc::new(Mutex::new(KnownPeers::new()))); - - gv.note_round(3u64); - gv.note_round(7u64); - gv.note_round(10u64); - - assert_eq!(gv.known_votes.read().live.len(), 3); - - // note round #7 again -> should not change anything - gv.note_round(7u64); - - let votes = gv.known_votes.read(); - - assert_eq!(votes.live.len(), 3); - - assert!(votes.is_live(&3u64)); - assert!(votes.is_live(&7u64)); - assert!(votes.is_live(&10u64)); - } - struct TestContext; impl ValidatorContext for TestContext { fn broadcast_topic(&mut self, _topic: B::Hash, _force: bool) { @@ -368,21 +327,18 @@ mod tests { #[test] fn should_avoid_verifying_signatures_twice() { let gv = GossipValidator::::new(Arc::new(Mutex::new(KnownPeers::new()))); + gv.update_filter(GossipVoteFilter { start: 0, end: 10, validator_set_id: 0 }); let sender = sc_network::PeerId::random(); let mut context = TestContext; let vote = dummy_vote(3); - gv.note_round(3u64); - gv.note_round(7u64); - gv.note_round(10u64); - // first time the cache should be populated let res = gv.validate(&mut context, &sender, &vote.encode()); assert!(matches!(res, ValidationResult::ProcessAndKeep(_))); assert_eq!( - gv.known_votes.read().live.get(&vote.commitment.block_number).map(|x| x.len()), + gv.votes_filter.read().live.get(&vote.commitment.block_number).map(|x| x.len()), Some(1) ); @@ -392,9 +348,11 @@ mod tests { assert!(matches!(res, ValidationResult::ProcessAndKeep(_))); // next we should quickly reject if the round is not live - gv.conclude_round(7_u64); + gv.update_filter(GossipVoteFilter { start: 7, end: 10, validator_set_id: 0 }); - assert!(!gv.known_votes.read().is_live(&vote.commitment.block_number)); + let number = vote.commitment.block_number; + let set_id = vote.commitment.validator_set_id; + assert!(!gv.votes_filter.read().is_live(number, set_id)); let res = gv.validate(&mut context, &sender, &vote.encode()); @@ -404,14 +362,13 @@ mod tests { #[test] fn messages_allowed_and_expired() { let gv = GossipValidator::::new(Arc::new(Mutex::new(KnownPeers::new()))); + gv.update_filter(GossipVoteFilter { start: 0, end: 10, validator_set_id: 0 }); let sender = sc_network::PeerId::random(); let topic = Default::default(); let intent = MessageIntent::Broadcast; - // note round 2 and 3, then conclude 2 - gv.note_round(2u64); - gv.note_round(3u64); - gv.conclude_round(2u64); + // conclude 2 + gv.update_filter(GossipVoteFilter { start: 2, end: 10, validator_set_id: 0 }); let mut allowed = gv.message_allowed(); let mut expired = gv.message_expired(); @@ -447,6 +404,7 @@ mod tests { #[test] fn messages_rebroadcast() { let gv = GossipValidator::::new(Arc::new(Mutex::new(KnownPeers::new()))); + gv.update_filter(GossipVoteFilter { start: 0, end: 10, validator_set_id: 0 }); let sender = sc_network::PeerId::random(); let topic = Default::default(); diff --git a/client/consensus/beefy/src/lib.rs b/client/consensus/beefy/src/lib.rs index eb56a97de..d632f5833 100644 --- a/client/consensus/beefy/src/lib.rs +++ b/client/consensus/beefy/src/lib.rs @@ -247,6 +247,8 @@ pub async fn start_beefy_gadget( } = network_params; let known_peers = Arc::new(Mutex::new(KnownPeers::new())); + // Default votes filter is to discard everything. + // Validator is updated later with correct starting round and set id. let gossip_validator = Arc::new(communication::gossip::GossipValidator::new(known_peers.clone())); let mut gossip_engine = sc_network_gossip::GossipEngine::new( @@ -284,6 +286,14 @@ pub async fn start_beefy_gadget( return }, }; + // Update the gossip validator with the right starting round and set id. + if let Err(e) = persisted_state + .current_gossip_filter() + .map(|f| gossip_validator.update_filter(f)) + { + error!(target: LOG_TARGET, "Error: {:?}. Terminating.", e); + return + } let worker_params = worker::WorkerParams { backend, diff --git a/client/consensus/beefy/src/metrics.rs b/client/consensus/beefy/src/metrics.rs index 0ce48e60e..6653763fc 100644 --- a/client/consensus/beefy/src/metrics.rs +++ b/client/consensus/beefy/src/metrics.rs @@ -45,10 +45,6 @@ pub struct VoterMetrics { pub beefy_lagging_sessions: Counter, /// Number of times no Authority public key found in store pub beefy_no_authority_found_in_store: Counter, - /// Number of currently buffered votes - pub beefy_buffered_votes: Gauge, - /// Number of votes dropped due to full buffers - pub beefy_buffered_votes_dropped: Counter, /// Number of good votes successfully handled pub beefy_good_votes_processed: Counter, /// Number of equivocation votes received @@ -65,8 +61,6 @@ pub struct VoterMetrics { pub beefy_imported_justifications: Counter, /// Number of justifications dropped due to full buffers pub beefy_buffered_justifications_dropped: Counter, - /// Trying to set Best Beefy block to old block - pub beefy_best_block_set_last_failure: Gauge, } impl PrometheusRegister for VoterMetrics { @@ -110,17 +104,6 @@ impl PrometheusRegister for VoterMetrics { )?, registry, )?, - beefy_buffered_votes: register( - Gauge::new("substrate_beefy_buffered_votes", "Number of currently buffered votes")?, - registry, - )?, - beefy_buffered_votes_dropped: register( - Counter::new( - "substrate_beefy_buffered_votes_dropped", - "Number of votes dropped due to full buffers", - )?, - registry, - )?, beefy_good_votes_processed: register( Counter::new( "substrate_beefy_successful_handled_votes", @@ -174,13 +157,6 @@ impl PrometheusRegister for VoterMetrics { )?, registry, )?, - beefy_best_block_set_last_failure: register( - Gauge::new( - "substrate_beefy_best_block_to_old_block", - "Trying to set Best Beefy block to old block", - )?, - registry, - )?, }) } } diff --git a/client/consensus/beefy/src/tests.rs b/client/consensus/beefy/src/tests.rs index 27dc8d819..629f14424 100644 --- a/client/consensus/beefy/src/tests.rs +++ b/client/consensus/beefy/src/tests.rs @@ -526,17 +526,15 @@ async fn finalize_block_and_wait_for_beefy( net: &Arc>, // peer index and key peers: impl Iterator + Clone, - finalize_targets: &[H256], + finalize_target: &H256, expected_beefy: &[u64], ) { let (best_blocks, versioned_finality_proof) = get_beefy_streams(&mut net.lock(), peers.clone()); - for block in finalize_targets { - peers.clone().for_each(|(index, _)| { - let client = net.lock().peer(index).client().as_client(); - client.finalize_block(*block, None).unwrap(); - }) - } + peers.clone().for_each(|(index, _)| { + let client = net.lock().peer(index).client().as_client(); + client.finalize_block(*finalize_target, None).unwrap(); + }); if expected_beefy.is_empty() { // run for quarter second then verify no new best beefy block available @@ -574,31 +572,32 @@ async fn beefy_finalizing_blocks() { let peers = peers.into_iter().enumerate(); // finalize block #5 -> BEEFY should finalize #1 (mandatory) and #5 from diff-power-of-two rule. - finalize_block_and_wait_for_beefy(&net, peers.clone(), &[hashes[1], hashes[5]], &[1, 5]).await; + finalize_block_and_wait_for_beefy(&net, peers.clone(), &hashes[1], &[1]).await; + finalize_block_and_wait_for_beefy(&net, peers.clone(), &hashes[5], &[5]).await; // GRANDPA finalize #10 -> BEEFY finalize #10 (mandatory) - finalize_block_and_wait_for_beefy(&net, peers.clone(), &[hashes[10]], &[10]).await; + finalize_block_and_wait_for_beefy(&net, peers.clone(), &hashes[10], &[10]).await; // GRANDPA finalize #18 -> BEEFY finalize #14, then #18 (diff-power-of-two rule) - finalize_block_and_wait_for_beefy(&net, peers.clone(), &[hashes[18]], &[14, 18]).await; + finalize_block_and_wait_for_beefy(&net, peers.clone(), &hashes[18], &[14, 18]).await; // GRANDPA finalize #20 -> BEEFY finalize #20 (mandatory) - finalize_block_and_wait_for_beefy(&net, peers.clone(), &[hashes[20]], &[20]).await; + finalize_block_and_wait_for_beefy(&net, peers.clone(), &hashes[20], &[20]).await; // GRANDPA finalize #21 -> BEEFY finalize nothing (yet) because min delta is 4 - finalize_block_and_wait_for_beefy(&net, peers, &[hashes[21]], &[]).await; + finalize_block_and_wait_for_beefy(&net, peers, &hashes[21], &[]).await; } #[tokio::test] async fn lagging_validators() { sp_tracing::try_init_simple(); - let peers = [BeefyKeyring::Alice, BeefyKeyring::Bob]; + let peers = [BeefyKeyring::Alice, BeefyKeyring::Bob, BeefyKeyring::Charlie]; let validator_set = ValidatorSet::new(make_beefy_ids(&peers), 0).unwrap(); let session_len = 30; let min_block_delta = 1; - let mut net = BeefyTestNet::new(2); + let mut net = BeefyTestNet::new(3); let api = Arc::new(TestApi::with_validator_set(&validator_set)); let beefy_peers = peers.iter().enumerate().map(|(id, key)| (id, key, api.clone())).collect(); tokio::spawn(initialize_beefy(&mut net, beefy_peers, min_block_delta)); @@ -611,55 +610,47 @@ async fn lagging_validators() { let peers = peers.into_iter().enumerate(); // finalize block #15 -> BEEFY should finalize #1 (mandatory) and #9, #13, #14, #15 from // diff-power-of-two rule. - finalize_block_and_wait_for_beefy( - &net, - peers.clone(), - &[hashes[1], hashes[15]], - &[1, 9, 13, 14, 15], - ) - .await; - - // Alice finalizes #25, Bob lags behind + finalize_block_and_wait_for_beefy(&net, peers.clone(), &hashes[1], &[1]).await; + finalize_block_and_wait_for_beefy(&net, peers.clone(), &hashes[15], &[9, 13, 14, 15]).await; + + // Alice and Bob finalize #25, Charlie lags behind let finalize = hashes[25]; let (best_blocks, versioned_finality_proof) = get_beefy_streams(&mut net.lock(), peers.clone()); net.lock().peer(0).client().as_client().finalize_block(finalize, None).unwrap(); + net.lock().peer(1).client().as_client().finalize_block(finalize, None).unwrap(); // verify nothing gets finalized by BEEFY - let timeout = Some(Duration::from_millis(250)); + let timeout = Some(Duration::from_millis(100)); streams_empty_after_timeout(best_blocks, &net, timeout).await; streams_empty_after_timeout(versioned_finality_proof, &net, None).await; - // Bob catches up and also finalizes #25 + // Charlie catches up and also finalizes #25 let (best_blocks, versioned_finality_proof) = get_beefy_streams(&mut net.lock(), peers.clone()); - net.lock().peer(1).client().as_client().finalize_block(finalize, None).unwrap(); - // expected beefy finalizes block #17 from diff-power-of-two + net.lock().peer(2).client().as_client().finalize_block(finalize, None).unwrap(); + // expected beefy finalizes blocks 23, 24, 25 from diff-power-of-two wait_for_best_beefy_blocks(best_blocks, &net, &[23, 24, 25]).await; wait_for_beefy_signed_commitments(versioned_finality_proof, &net, &[23, 24, 25]).await; // Both finalize #30 (mandatory session) and #32 -> BEEFY finalize #30 (mandatory), #31, #32 - finalize_block_and_wait_for_beefy( - &net, - peers.clone(), - &[hashes[30], hashes[32]], - &[30, 31, 32], - ) - .await; + finalize_block_and_wait_for_beefy(&net, peers.clone(), &hashes[30], &[30]).await; + finalize_block_and_wait_for_beefy(&net, peers.clone(), &hashes[32], &[31, 32]).await; // Verify that session-boundary votes get buffered by client and only processed once // session-boundary block is GRANDPA-finalized (this guarantees authenticity for the new session // validator set). - // Alice finalizes session-boundary mandatory block #60, Bob lags behind + // Alice and Bob finalize session-boundary mandatory block #60, Charlie lags behind let (best_blocks, versioned_finality_proof) = get_beefy_streams(&mut net.lock(), peers.clone()); let finalize = hashes[60]; net.lock().peer(0).client().as_client().finalize_block(finalize, None).unwrap(); + net.lock().peer(1).client().as_client().finalize_block(finalize, None).unwrap(); // verify nothing gets finalized by BEEFY - let timeout = Some(Duration::from_millis(250)); + let timeout = Some(Duration::from_millis(100)); streams_empty_after_timeout(best_blocks, &net, timeout).await; streams_empty_after_timeout(versioned_finality_proof, &net, None).await; - // Bob catches up and also finalizes #60 (and should have buffered Alice's vote on #60) + // Charlie catches up and also finalizes #60 (and should have buffered Alice's vote on #60) let (best_blocks, versioned_finality_proof) = get_beefy_streams(&mut net.lock(), peers); - net.lock().peer(1).client().as_client().finalize_block(finalize, None).unwrap(); + net.lock().peer(2).client().as_client().finalize_block(finalize, None).unwrap(); // verify beefy skips intermediary votes, and successfully finalizes mandatory block #60 wait_for_best_beefy_blocks(best_blocks, &net, &[60]).await; wait_for_beefy_signed_commitments(versioned_finality_proof, &net, &[60]).await; @@ -696,7 +687,8 @@ async fn correct_beefy_payload() { let net = Arc::new(Mutex::new(net)); let peers = peers.into_iter().enumerate(); // with 3 good voters and 1 bad one, consensus should happen and best blocks produced. - finalize_block_and_wait_for_beefy(&net, peers, &[hashes[1], hashes[10]], &[1, 9]).await; + finalize_block_and_wait_for_beefy(&net, peers.clone(), &hashes[1], &[1]).await; + finalize_block_and_wait_for_beefy(&net, peers, &hashes[10], &[9]).await; let (best_blocks, versioned_finality_proof) = get_beefy_streams(&mut net.lock(), [(0, BeefyKeyring::Alice)].into_iter()); @@ -708,7 +700,7 @@ async fn correct_beefy_payload() { net.lock().peer(3).client().as_client().finalize_block(hashof11, None).unwrap(); // verify consensus is _not_ reached - let timeout = Some(Duration::from_millis(250)); + let timeout = Some(Duration::from_millis(100)); streams_empty_after_timeout(best_blocks, &net, timeout).await; streams_empty_after_timeout(versioned_finality_proof, &net, None).await; @@ -874,39 +866,6 @@ async fn beefy_importing_justifications() { } } -#[tokio::test] -async fn voter_initialization() { - sp_tracing::try_init_simple(); - // Regression test for voter initialization where finality notifications were dropped - // after waiting for BEEFY pallet availability. - - let peers = [BeefyKeyring::Alice, BeefyKeyring::Bob]; - let validator_set = ValidatorSet::new(make_beefy_ids(&peers), 0).unwrap(); - let session_len = 5; - // Should vote on all mandatory blocks no matter the `min_block_delta`. - let min_block_delta = 10; - - let mut net = BeefyTestNet::new(2); - let api = Arc::new(TestApi::with_validator_set(&validator_set)); - let beefy_peers = peers.iter().enumerate().map(|(id, key)| (id, key, api.clone())).collect(); - tokio::spawn(initialize_beefy(&mut net, beefy_peers, min_block_delta)); - - // push 26 blocks - let hashes = net.generate_blocks_and_sync(26, session_len, &validator_set, false).await; - let net = Arc::new(Mutex::new(net)); - - // Finalize multiple blocks at once to get a burst of finality notifications right from start. - // Need to finalize at least one block in each session, choose randomly. - // Expect voters to pick up all of them and BEEFY-finalize the mandatory blocks of each session. - finalize_block_and_wait_for_beefy( - &net, - peers.into_iter().enumerate(), - &[hashes[1], hashes[6], hashes[10], hashes[17], hashes[24], hashes[26]], - &[1, 5, 10, 15, 20, 25], - ) - .await; -} - #[tokio::test] async fn on_demand_beefy_justification_sync() { sp_tracing::try_init_simple(); @@ -940,13 +899,11 @@ async fn on_demand_beefy_justification_sync() { let net = Arc::new(Mutex::new(net)); // With 3 active voters and one inactive, consensus should happen and blocks BEEFY-finalized. // Need to finalize at least one block in each session, choose randomly. - finalize_block_and_wait_for_beefy( - &net, - fast_peers.clone(), - &[hashes[1], hashes[6], hashes[10], hashes[17], hashes[23]], - &[1, 5, 10, 15, 20], - ) - .await; + finalize_block_and_wait_for_beefy(&net, fast_peers.clone(), &hashes[1], &[1]).await; + finalize_block_and_wait_for_beefy(&net, fast_peers.clone(), &hashes[6], &[5]).await; + finalize_block_and_wait_for_beefy(&net, fast_peers.clone(), &hashes[10], &[10]).await; + finalize_block_and_wait_for_beefy(&net, fast_peers.clone(), &hashes[17], &[15]).await; + finalize_block_and_wait_for_beefy(&net, fast_peers.clone(), &hashes[24], &[20]).await; // Spawn Dave, they are now way behind voting and can only catch up through on-demand justif // sync. @@ -971,7 +928,8 @@ async fn on_demand_beefy_justification_sync() { // freshly spun up Dave now needs to listen for gossip to figure out the state of their peers. // Have the other peers do some gossip so Dave finds out about their progress. - finalize_block_and_wait_for_beefy(&net, fast_peers, &[hashes[25], hashes[29]], &[25, 29]).await; + finalize_block_and_wait_for_beefy(&net, fast_peers.clone(), &hashes[25], &[25]).await; + finalize_block_and_wait_for_beefy(&net, fast_peers, &hashes[29], &[29]).await; // Kick Dave's async loop by finalizing another block. client.finalize_block(hashes[2], None).unwrap(); @@ -982,13 +940,14 @@ async fn on_demand_beefy_justification_sync() { // Give all tasks some cpu cycles to burn through their events queues, run_for(Duration::from_millis(100), &net).await; // then verify Dave catches up through on-demand justification requests. - finalize_block_and_wait_for_beefy( - &net, - [(dave_index, BeefyKeyring::Dave)].into_iter(), - &[hashes[6], hashes[10], hashes[17], hashes[24], hashes[26]], - &[5, 10, 15, 20, 25], - ) - .await; + let (dave_best_blocks, _) = + get_beefy_streams(&mut net.lock(), [(dave_index, BeefyKeyring::Dave)].into_iter()); + client.finalize_block(hashes[6], None).unwrap(); + client.finalize_block(hashes[10], None).unwrap(); + client.finalize_block(hashes[17], None).unwrap(); + client.finalize_block(hashes[24], None).unwrap(); + client.finalize_block(hashes[26], None).unwrap(); + wait_for_best_beefy_blocks(dave_best_blocks, &net, &[5, 10, 15, 20, 25]).await; } #[tokio::test] @@ -1022,13 +981,8 @@ async fn should_initialize_voter_at_genesis() { // verify next vote target is mandatory block 1 assert_eq!(persisted_state.best_beefy_block(), 0); - assert_eq!(persisted_state.best_grandpa_block(), 13); - assert_eq!( - persisted_state - .voting_oracle() - .voting_target(persisted_state.best_beefy_block(), 13), - Some(1) - ); + assert_eq!(persisted_state.best_grandpa_number(), 13); + assert_eq!(persisted_state.voting_oracle().voting_target(), Some(1)); // verify state also saved to db assert!(verify_persisted_version(&*backend)); @@ -1070,13 +1024,8 @@ async fn should_initialize_voter_at_custom_genesis() { // verify next vote target is mandatory block 7 assert_eq!(persisted_state.best_beefy_block(), 0); - assert_eq!(persisted_state.best_grandpa_block(), 8); - assert_eq!( - persisted_state - .voting_oracle() - .voting_target(persisted_state.best_beefy_block(), 13), - Some(custom_pallet_genesis) - ); + assert_eq!(persisted_state.best_grandpa_number(), 8); + assert_eq!(persisted_state.voting_oracle().voting_target(), Some(custom_pallet_genesis)); // verify state also saved to db assert!(verify_persisted_version(&*backend)); @@ -1128,14 +1077,9 @@ async fn should_initialize_voter_when_last_final_is_session_boundary() { // verify block 10 is correctly marked as finalized assert_eq!(persisted_state.best_beefy_block(), 10); - assert_eq!(persisted_state.best_grandpa_block(), 13); + assert_eq!(persisted_state.best_grandpa_number(), 13); // verify next vote target is diff-power-of-two block 12 - assert_eq!( - persisted_state - .voting_oracle() - .voting_target(persisted_state.best_beefy_block(), 13), - Some(12) - ); + assert_eq!(persisted_state.voting_oracle().voting_target(), Some(12)); // verify state also saved to db assert!(verify_persisted_version(&*backend)); @@ -1186,13 +1130,8 @@ async fn should_initialize_voter_at_latest_finalized() { // verify next vote target is 13 assert_eq!(persisted_state.best_beefy_block(), 12); - assert_eq!(persisted_state.best_grandpa_block(), 13); - assert_eq!( - persisted_state - .voting_oracle() - .voting_target(persisted_state.best_beefy_block(), 13), - Some(13) - ); + assert_eq!(persisted_state.best_grandpa_number(), 13); + assert_eq!(persisted_state.voting_oracle().voting_target(), Some(13)); // verify state also saved to db assert!(verify_persisted_version(&*backend)); @@ -1225,13 +1164,13 @@ async fn beefy_finalizing_after_pallet_genesis() { // Minimum BEEFY block delta is 1. // GRANDPA finalize blocks leading up to BEEFY pallet genesis -> BEEFY should finalize nothing. - finalize_block_and_wait_for_beefy(&net, peers.clone(), &hashes[1..14], &[]).await; + finalize_block_and_wait_for_beefy(&net, peers.clone(), &hashes[14], &[]).await; // GRANDPA finalize block #16 -> BEEFY should finalize #15 (genesis mandatory) and #16. - finalize_block_and_wait_for_beefy(&net, peers.clone(), &[hashes[16]], &[15, 16]).await; + finalize_block_and_wait_for_beefy(&net, peers.clone(), &hashes[16], &[15, 16]).await; // GRANDPA finalize #21 -> BEEFY finalize #20 (mandatory) and #21 - finalize_block_and_wait_for_beefy(&net, peers.clone(), &[hashes[21]], &[20, 21]).await; + finalize_block_and_wait_for_beefy(&net, peers.clone(), &hashes[21], &[20, 21]).await; } #[tokio::test] @@ -1249,14 +1188,14 @@ async fn beefy_reports_equivocations() { let mut api_alice = TestApi::with_validator_set(&validator_set); api_alice.allow_equivocations(); let api_alice = Arc::new(api_alice); - let alice = (0, &peers[0], api_alice.clone()); + let alice = (0, &BeefyKeyring::Alice, api_alice.clone()); tokio::spawn(initialize_beefy(&mut net, vec![alice], min_block_delta)); // Bob votes on bad MMR roots, equivocations are allowed/expected. let mut api_bob = TestApi::new(1, &validator_set, BAD_MMR_ROOT); api_bob.allow_equivocations(); let api_bob = Arc::new(api_bob); - let bob = (1, &peers[1], api_bob.clone()); + let bob = (1, &BeefyKeyring::Bob, api_bob.clone()); tokio::spawn(initialize_beefy(&mut net, vec![bob], min_block_delta)); // We spawn another node voting with Bob key, on alternate bad MMR roots (equivocating). @@ -1276,7 +1215,7 @@ async fn beefy_reports_equivocations() { let peers = peers.into_iter().enumerate(); // finalize block #1 -> BEEFY should not finalize anything (each node votes on different MMR). - finalize_block_and_wait_for_beefy(&net, peers, &[hashes[1]], &[]).await; + finalize_block_and_wait_for_beefy(&net, peers, &hashes[1], &[]).await; // Verify neither Bob or Bob_Prime report themselves as equivocating. assert!(api_bob.reported_equivocations.as_ref().unwrap().lock().is_empty()); diff --git a/client/consensus/beefy/src/worker.rs b/client/consensus/beefy/src/worker.rs index 0abb38d02..0260d7693 100644 --- a/client/consensus/beefy/src/worker.rs +++ b/client/consensus/beefy/src/worker.rs @@ -18,13 +18,13 @@ use crate::{ communication::{ - gossip::{topic, GossipValidator}, + gossip::{topic, GossipValidator, GossipVoteFilter}, request_response::outgoing_requests_engine::OnDemandJustificationsEngine, }, error::Error, justification::BeefyVersionedFinalityProof, keystore::{BeefyKeystore, BeefySignatureHasher}, - metric_get, metric_inc, metric_set, + metric_inc, metric_set, metrics::VoterMetrics, round::{Rounds, VoteImportResult}, BeefyVoterLinks, LOG_TARGET, @@ -42,24 +42,19 @@ use sp_consensus_beefy::{ check_equivocation_proof, crypto::{AuthorityId, Signature}, BeefyApi, Commitment, ConsensusLog, EquivocationProof, PayloadProvider, ValidatorSet, - VersionedFinalityProof, VoteMessage, BEEFY_ENGINE_ID, + ValidatorSetId, VersionedFinalityProof, VoteMessage, BEEFY_ENGINE_ID, }; use sp_runtime::{ generic::OpaqueDigestItemId, - traits::{Block, ConstU32, Header, NumberFor, Zero}, - BoundedVec, SaturatedConversion, + traits::{Block, Header, NumberFor, Zero}, + SaturatedConversion, }; use std::{ collections::{BTreeMap, BTreeSet, VecDeque}, fmt::Debug, - marker::PhantomData, sync::Arc, }; -/// Bound for the number of buffered future voting rounds. -const MAX_BUFFERED_VOTE_ROUNDS: usize = 600; -/// Bound for the number of buffered votes per round number. -const MAX_BUFFERED_VOTES_PER_ROUND: u32 = 1000; /// Bound for the number of pending justifications - use 2400 - the max number /// of justifications possible in a single session. const MAX_BUFFERED_JUSTIFICATIONS: usize = 2400; @@ -87,23 +82,34 @@ pub(crate) struct VoterOracle { sessions: VecDeque>, /// Min delta in block numbers between two blocks, BEEFY should vote on. min_block_delta: u32, + /// Best block we received a GRANDPA finality for. + best_grandpa_block_header: ::Header, + /// Best block a BEEFY voting round has been concluded for. + best_beefy_block: NumberFor, } impl VoterOracle { /// Verify provided `sessions` satisfies requirements, then build `VoterOracle`. - pub fn checked_new(sessions: VecDeque>, min_block_delta: u32) -> Option { + pub fn checked_new( + sessions: VecDeque>, + min_block_delta: u32, + grandpa_header: ::Header, + best_beefy: NumberFor, + ) -> Option { let mut prev_start = Zero::zero(); let mut prev_validator_id = None; // verifies the let mut validate = || -> bool { - if sessions.is_empty() { + let best_grandpa = *grandpa_header.number(); + if sessions.is_empty() || best_beefy > best_grandpa { return false } for (idx, session) in sessions.iter().enumerate() { + let start = session.session_start(); if session.validators().is_empty() { return false } - if session.session_start() <= prev_start { + if start > best_grandpa || start <= prev_start { return false } #[cfg(not(test))] @@ -125,23 +131,35 @@ impl VoterOracle { sessions, // Always target at least one block better than current best beefy. min_block_delta: min_block_delta.max(1), + best_grandpa_block_header: grandpa_header, + best_beefy_block: best_beefy, }) } else { - error!(target: LOG_TARGET, "🥩 Invalid sessions queue: {:?}.", sessions); + error!( + target: LOG_TARGET, + "🥩 Invalid sessions queue: {:?}; best-beefy {:?} best-grandpa-header {:?}.", + sessions, + best_beefy, + grandpa_header + ); None } } // Return reference to rounds pertaining to first session in the queue. // Voting will always happen at the head of the queue. - fn active_rounds(&self) -> Option<&Rounds> { - self.sessions.front() + fn active_rounds(&self) -> Result<&Rounds, Error> { + self.sessions.front().ok_or(Error::UninitSession) } // Return mutable reference to rounds pertaining to first session in the queue. // Voting will always happen at the head of the queue. - fn active_rounds_mut(&mut self) -> Option<&mut Rounds> { - self.sessions.front_mut() + fn active_rounds_mut(&mut self) -> Result<&mut Rounds, Error> { + self.sessions.front_mut().ok_or(Error::UninitSession) + } + + fn current_validator_set_id(&self) -> Result { + self.active_rounds().map(|r| r.validator_set_id()) } // Prune the sessions queue to keep the Oracle in one of the expected three states. @@ -164,7 +182,7 @@ impl VoterOracle { /// Finalize a particular block. pub fn finalize(&mut self, block: NumberFor) -> Result<(), Error> { // Conclude voting round for this block. - self.active_rounds_mut().ok_or(Error::UninitSession)?.conclude(block); + self.active_rounds_mut()?.conclude(block); // Prune any now "finalized" sessions from queue. self.try_prune(); Ok(()) @@ -182,30 +200,26 @@ impl VoterOracle { } /// Return `(A, B)` tuple representing inclusive [A, B] interval of votes to accept. - pub fn accepted_interval( - &self, - best_grandpa: NumberFor, - ) -> Result<(NumberFor, NumberFor), Error> { + pub fn accepted_interval(&self) -> Result<(NumberFor, NumberFor), Error> { let rounds = self.sessions.front().ok_or(Error::UninitSession)?; if rounds.mandatory_done() { // There's only one session active and its mandatory is done. - // Accept any GRANDPA finalized vote. - Ok((rounds.session_start(), best_grandpa.into())) + // Accept any vote for a GRANDPA finalized block in a better round. + Ok(( + rounds.session_start().max(self.best_beefy_block), + (*self.best_grandpa_block_header.number()).into(), + )) } else { - // There's at least one session with mandatory not done. + // Current session has mandatory not done. // Only accept votes for the mandatory block in the front of queue. Ok((rounds.session_start(), rounds.session_start())) } } /// Utility function to quickly decide what to do for each round. - pub fn triage_round( - &self, - round: NumberFor, - best_grandpa: NumberFor, - ) -> Result { - let (start, end) = self.accepted_interval(best_grandpa)?; + pub fn triage_round(&self, round: NumberFor) -> Result { + let (start, end) = self.accepted_interval()?; if start <= round && round <= end { Ok(RoundAction::Process) } else if round > end { @@ -217,17 +231,15 @@ impl VoterOracle { /// Return `Some(number)` if we should be voting on block `number`, /// return `None` if there is no block we should vote on. - pub fn voting_target( - &self, - best_beefy: NumberFor, - best_grandpa: NumberFor, - ) -> Option> { + pub fn voting_target(&self) -> Option> { let rounds = if let Some(r) = self.sessions.front() { r } else { debug!(target: LOG_TARGET, "🥩 No voting round started"); return None }; + let best_grandpa = *self.best_grandpa_block_header.number(); + let best_beefy = self.best_beefy_block; // `target` is guaranteed > `best_beefy` since `min_block_delta` is at least `1`. let target = @@ -259,10 +271,6 @@ pub(crate) struct WorkerParams { #[derive(Debug, Decode, Encode, PartialEq)] pub(crate) struct PersistedState { - /// Best block we received a GRANDPA finality for. - best_grandpa_block_header: ::Header, - /// Best block a BEEFY voting round has been concluded for. - best_beefy_block: NumberFor, /// Best block we voted on. best_voted: NumberFor, /// Chooses which incoming votes to accept and which votes to generate. @@ -277,20 +285,26 @@ impl PersistedState { sessions: VecDeque>, min_block_delta: u32, ) -> Option { - VoterOracle::checked_new(sessions, min_block_delta).map(|voting_oracle| PersistedState { - best_grandpa_block_header: grandpa_header, - best_beefy_block: best_beefy, - best_voted: Zero::zero(), - voting_oracle, - }) + VoterOracle::checked_new(sessions, min_block_delta, grandpa_header, best_beefy) + .map(|voting_oracle| PersistedState { best_voted: Zero::zero(), voting_oracle }) } pub(crate) fn set_min_block_delta(&mut self, min_block_delta: u32) { self.voting_oracle.min_block_delta = min_block_delta.max(1); } + pub(crate) fn set_best_beefy(&mut self, best_beefy: NumberFor) { + self.voting_oracle.best_beefy_block = best_beefy; + } + pub(crate) fn set_best_grandpa(&mut self, best_grandpa: ::Header) { - self.best_grandpa_block_header = best_grandpa; + self.voting_oracle.best_grandpa_block_header = best_grandpa; + } + + pub(crate) fn current_gossip_filter(&self) -> Result, Error> { + let (start, end) = self.voting_oracle.accepted_interval()?; + let validator_set_id = self.voting_oracle.current_validator_set_id()?; + Ok(GossipVoteFilter { start, end, validator_set_id }) } } @@ -315,14 +329,6 @@ pub(crate) struct BeefyWorker { // voter state /// BEEFY client metrics. metrics: Option, - /// Buffer holding votes for future processing. - pending_votes: BTreeMap< - NumberFor, - BoundedVec< - VoteMessage, AuthorityId, Signature>, - ConstU32, - >, - >, /// Buffer holding justifications for future processing. pending_justifications: BTreeMap, BeefyVersionedFinalityProof>, /// Persisted voter state. @@ -370,25 +376,20 @@ where on_demand_justifications, links, metrics, - pending_votes: BTreeMap::new(), pending_justifications: BTreeMap::new(), persisted_state, } } fn best_grandpa_block(&self) -> NumberFor { - *self.persisted_state.best_grandpa_block_header.number() - } - - fn best_beefy_block(&self) -> NumberFor { - self.persisted_state.best_beefy_block + *self.persisted_state.voting_oracle.best_grandpa_block_header.number() } fn voting_oracle(&self) -> &VoterOracle { &self.persisted_state.voting_oracle } - fn active_rounds(&mut self) -> Option<&Rounds> { + fn active_rounds(&mut self) -> Result<&Rounds, Error> { self.persisted_state.voting_oracle.active_rounds() } @@ -429,7 +430,7 @@ where debug!(target: LOG_TARGET, "🥩 New active validator set: {:?}", validator_set); // BEEFY should finalize a mandatory block during each session. - if let Some(active_session) = self.active_rounds() { + if let Ok(active_session) = self.active_rounds() { if !active_session.mandatory_done() { debug!( target: LOG_TARGET, @@ -460,12 +461,17 @@ where } fn handle_finality_notification(&mut self, notification: &FinalityNotification) { - debug!(target: LOG_TARGET, "🥩 Finality notification: {:?}", notification); + debug!( + target: LOG_TARGET, + "🥩 Finality notification: header {:?} tree_route {:?}", + notification.header, + notification.tree_route, + ); let header = ¬ification.header; if *header.number() > self.best_grandpa_block() { // update best GRANDPA finalized block we have seen - self.persisted_state.best_grandpa_block_header = header.clone(); + self.persisted_state.set_best_grandpa(header.clone()); // Check all (newly) finalized blocks for new session(s). let backend = self.backend.clone(); @@ -484,38 +490,28 @@ where self.init_session_at(new_validator_set, *header.number()); } } + + // Update gossip validator votes filter. + if let Err(e) = self + .persisted_state + .current_gossip_filter() + .map(|filter| self.gossip_validator.update_filter(filter)) + { + error!(target: LOG_TARGET, "🥩 Voter error: {:?}", e); + } } } - /// Based on [VoterOracle] this vote is either processed here or enqueued for later. + /// Based on [VoterOracle] this vote is either processed here or discarded. fn triage_incoming_vote( &mut self, vote: VoteMessage, AuthorityId, Signature>, ) -> Result<(), Error> { let block_num = vote.commitment.block_number; - let best_grandpa = self.best_grandpa_block(); - self.gossip_validator.note_round(block_num); - match self.voting_oracle().triage_round(block_num, best_grandpa)? { + match self.voting_oracle().triage_round(block_num)? { RoundAction::Process => self.handle_vote(vote)?, - RoundAction::Enqueue => { - debug!(target: LOG_TARGET, "🥩 Buffer vote for round: {:?}.", block_num); - if self.pending_votes.len() < MAX_BUFFERED_VOTE_ROUNDS { - let votes_vec = self.pending_votes.entry(block_num).or_default(); - if votes_vec.try_push(vote).is_ok() { - metric_inc!(self, beefy_buffered_votes); - } else { - warn!( - target: LOG_TARGET, - "🥩 Buffer vote dropped for round: {:?}", block_num - ); - metric_inc!(self, beefy_buffered_votes_dropped); - } - } else { - warn!(target: LOG_TARGET, "🥩 Buffer vote dropped for round: {:?}.", block_num); - metric_inc!(self, beefy_buffered_votes_dropped); - } - }, RoundAction::Drop => metric_inc!(self, beefy_stale_votes), + RoundAction::Enqueue => error!(target: LOG_TARGET, "🥩 unexpected vote: {:?}.", vote), }; Ok(()) } @@ -531,8 +527,7 @@ where VersionedFinalityProof::V1(ref sc) => sc, }; let block_num = signed_commitment.commitment.block_number; - let best_grandpa = self.best_grandpa_block(); - match self.voting_oracle().triage_round(block_num, best_grandpa)? { + match self.voting_oracle().triage_round(block_num)? { RoundAction::Process => { debug!(target: LOG_TARGET, "🥩 Process justification for round: {:?}.", block_num); metric_inc!(self, beefy_imported_justifications); @@ -560,11 +555,7 @@ where &mut self, vote: VoteMessage, AuthorityId, Signature>, ) -> Result<(), Error> { - let rounds = self - .persisted_state - .voting_oracle - .active_rounds_mut() - .ok_or(Error::UninitSession)?; + let rounds = self.persisted_state.voting_oracle.active_rounds_mut()?; let block_number = vote.commitment.block_number; match rounds.add_vote(vote) { @@ -608,7 +599,7 @@ where /// 3. Persist voter state, /// 4. Send best block hash and `finality_proof` to RPC worker. /// - /// Expects `finality proof` to be valid. + /// Expects `finality proof` to be valid and for a block > current-best-beefy. fn finalize(&mut self, finality_proof: BeefyVersionedFinalityProof) -> Result<(), Error> { let block_num = match finality_proof { VersionedFinalityProof::V1(ref sc) => sc.commitment.block_number, @@ -616,74 +607,62 @@ where // Finalize inner round and update voting_oracle state. self.persisted_state.voting_oracle.finalize(block_num)?; - self.gossip_validator.conclude_round(block_num); - if block_num > self.best_beefy_block() { - // Set new best BEEFY block number. - self.persisted_state.best_beefy_block = block_num; - crate::aux_schema::write_voter_state(&*self.backend, &self.persisted_state) - .map_err(|e| Error::Backend(e.to_string()))?; + // Set new best BEEFY block number. + self.persisted_state.set_best_beefy(block_num); + crate::aux_schema::write_voter_state(&*self.backend, &self.persisted_state) + .map_err(|e| Error::Backend(e.to_string()))?; - metric_set!(self, beefy_best_block, block_num); + metric_set!(self, beefy_best_block, block_num); - self.on_demand_justifications.cancel_requests_older_than(block_num); + self.on_demand_justifications.cancel_requests_older_than(block_num); - if let Err(e) = self - .backend - .blockchain() - .expect_block_hash_from_id(&BlockId::Number(block_num)) - .and_then(|hash| { - self.links - .to_rpc_best_block_sender - .notify(|| Ok::<_, ()>(hash)) - .expect("forwards closure result; the closure always returns Ok; qed."); - - self.backend - .append_justification(hash, (BEEFY_ENGINE_ID, finality_proof.encode())) - }) { - error!( - target: LOG_TARGET, - "🥩 Error {:?} on appending justification: {:?}", e, finality_proof - ); - } - - self.links - .to_rpc_justif_sender - .notify(|| Ok::<_, ()>(finality_proof)) - .expect("forwards closure result; the closure always returns Ok; qed."); - } else { - debug!(target: LOG_TARGET, "🥩 Can't set best beefy to old: {}", block_num); - metric_set!(self, beefy_best_block_set_last_failure, block_num); + if let Err(e) = self + .backend + .blockchain() + .expect_block_hash_from_id(&BlockId::Number(block_num)) + .and_then(|hash| { + self.links + .to_rpc_best_block_sender + .notify(|| Ok::<_, ()>(hash)) + .expect("forwards closure result; the closure always returns Ok; qed."); + + self.backend + .append_justification(hash, (BEEFY_ENGINE_ID, finality_proof.encode())) + }) { + error!( + target: LOG_TARGET, + "🥩 Error {:?} on appending justification: {:?}", e, finality_proof + ); } + + self.links + .to_rpc_justif_sender + .notify(|| Ok::<_, ()>(finality_proof)) + .expect("forwards closure result; the closure always returns Ok; qed."); + + // Update gossip validator votes filter. + self.persisted_state + .current_gossip_filter() + .map(|filter| self.gossip_validator.update_filter(filter))?; Ok(()) } - /// Handle previously buffered justifications and votes that now land in the voting interval. - fn try_pending_justif_and_votes(&mut self) -> Result<(), Error> { - let best_grandpa = self.best_grandpa_block(); - let _ph = PhantomData::::default(); - - fn to_process_for( - pending: &mut BTreeMap, T>, - (start, end): (NumberFor, NumberFor), - _: PhantomData, - ) -> BTreeMap, T> { + /// Handle previously buffered justifications, that now land in the voting interval. + fn try_pending_justififactions(&mut self) -> Result<(), Error> { + // Interval of blocks for which we can process justifications and votes right now. + let (start, end) = self.voting_oracle().accepted_interval()?; + // Process pending justifications. + if !self.pending_justifications.is_empty() { // These are still pending. - let still_pending = pending.split_off(&end.saturating_add(1u32.into())); + let still_pending = + self.pending_justifications.split_off(&end.saturating_add(1u32.into())); // These can be processed. - let to_handle = pending.split_off(&start); + let justifs_to_process = self.pending_justifications.split_off(&start); // The rest can be dropped. - *pending = still_pending; - // Return ones to process. - to_handle - } - // Interval of blocks for which we can process justifications and votes right now. - let mut interval = self.voting_oracle().accepted_interval(best_grandpa)?; + self.pending_justifications = still_pending; - // Process pending justifications. - if !self.pending_justifications.is_empty() { - let justifs_to_handle = to_process_for(&mut self.pending_justifications, interval, _ph); - for (num, justification) in justifs_to_handle.into_iter() { + for (num, justification) in justifs_to_process.into_iter() { debug!(target: LOG_TARGET, "🥩 Handle buffered justification for: {:?}.", num); metric_inc!(self, beefy_imported_justifications); if let Err(err) = self.finalize(justification) { @@ -691,27 +670,6 @@ where } } metric_set!(self, beefy_buffered_justifications, self.pending_justifications.len()); - // Possibly new interval after processing justifications. - interval = self.voting_oracle().accepted_interval(best_grandpa)?; - } - - // Process pending votes. - if !self.pending_votes.is_empty() { - let mut processed = 0u64; - let votes_to_handle = to_process_for(&mut self.pending_votes, interval, _ph); - for (num, votes) in votes_to_handle.into_iter() { - debug!(target: LOG_TARGET, "🥩 Handle buffered votes for: {:?}.", num); - processed += votes.len() as u64; - for v in votes.into_iter() { - if let Err(err) = self.handle_vote(v) { - error!(target: LOG_TARGET, "🥩 Error handling buffered vote: {}", err); - }; - } - } - if let Some(previous) = metric_get!(self, beefy_buffered_votes) { - previous.sub(processed); - metric_set!(self, beefy_buffered_votes, previous.get()); - } } Ok(()) } @@ -719,10 +677,7 @@ where /// Decide if should vote, then vote.. or don't.. fn try_to_vote(&mut self) -> Result<(), Error> { // Vote if there's now a new vote target. - if let Some(target) = self - .voting_oracle() - .voting_target(self.best_beefy_block(), self.best_grandpa_block()) - { + if let Some(target) = self.voting_oracle().voting_target() { metric_set!(self, beefy_should_vote_on, target); if target > self.persisted_state.best_voted { self.do_vote(target)?; @@ -740,7 +695,7 @@ where // Most of the time we get here, `target` is actually `best_grandpa`, // avoid getting header from backend in that case. let target_header = if target_number == self.best_grandpa_block() { - self.persisted_state.best_grandpa_block_header.clone() + self.persisted_state.voting_oracle.best_grandpa_block_header.clone() } else { let hash = self .backend @@ -771,11 +726,7 @@ where return Ok(()) }; - let rounds = self - .persisted_state - .voting_oracle - .active_rounds_mut() - .ok_or(Error::UninitSession)?; + let rounds = self.persisted_state.voting_oracle.active_rounds_mut()?; let (validators, validator_set_id) = (rounds.validators(), rounds.validator_set_id()); let authority_id = if let Some(id) = self.key_store.authority_id(validators) { @@ -830,7 +781,7 @@ where fn process_new_state(&mut self) { // Handle pending justifications and/or votes for now GRANDPA finalized blocks. - if let Err(err) = self.try_pending_justif_and_votes() { + if let Err(err) = self.try_pending_justififactions() { debug!(target: LOG_TARGET, "🥩 {}", err); } @@ -867,12 +818,12 @@ where self.gossip_engine .messages_for(topic::()) .filter_map(|notification| async move { - trace!(target: LOG_TARGET, "🥩 Got vote message: {:?}", notification); - - VoteMessage::, AuthorityId, Signature>::decode( + let vote = VoteMessage::, AuthorityId, Signature>::decode( &mut ¬ification.message[..], ) - .ok() + .ok(); + trace!(target: LOG_TARGET, "🥩 Got vote message: {:?}", vote); + vote }) .fuse(), ); @@ -946,8 +897,7 @@ where &self, proof: EquivocationProof, AuthorityId, Signature>, ) -> Result<(), Error> { - let rounds = - self.persisted_state.voting_oracle.active_rounds().ok_or(Error::UninitSession)?; + let rounds = self.persisted_state.voting_oracle.active_rounds()?; let (validators, validator_set_id) = (rounds.validators(), rounds.validator_set_id()); let offender_id = proof.offender_id().clone(); @@ -1083,16 +1033,16 @@ pub(crate) mod tests { &self.voting_oracle } - pub fn active_round(&self) -> Option<&Rounds> { + pub fn active_round(&self) -> Result<&Rounds, Error> { self.voting_oracle.active_rounds() } pub fn best_beefy_block(&self) -> NumberFor { - self.best_beefy_block + self.voting_oracle.best_beefy_block } - pub fn best_grandpa_block(&self) -> NumberFor { - *self.best_grandpa_block_header.number() + pub fn best_grandpa_number(&self) -> NumberFor { + *self.voting_oracle.best_grandpa_block_header.number() } } @@ -1103,7 +1053,7 @@ pub(crate) mod tests { } fn create_beefy_worker( - peer: &BeefyPeer, + peer: &mut BeefyPeer, key: &Keyring, min_block_delta: u32, genesis_validator_set: ValidatorSet, @@ -1153,12 +1103,15 @@ pub(crate) mod tests { known_peers, None, ); - let genesis_header = backend + // Push 1 block - will start first session. + let hashes = peer.push_blocks(1, false); + backend.finalize_block(hashes[0], None).unwrap(); + let first_header = backend .blockchain() - .expect_header(backend.blockchain().info().genesis_hash) + .expect_header(backend.blockchain().info().best_hash) .unwrap(); let persisted_state = PersistedState::checked_new( - genesis_header, + first_header, Zero::zero(), vec![Rounds::new(One::one(), genesis_validator_set)].into(), min_block_delta, @@ -1275,10 +1228,30 @@ pub(crate) mod tests { #[test] fn should_vote_target() { - let mut oracle = VoterOracle:: { min_block_delta: 1, sessions: VecDeque::new() }; + let header = Header::new( + 1u32.into(), + Default::default(), + Default::default(), + Default::default(), + Digest::default(), + ); + let mut oracle = VoterOracle:: { + best_beefy_block: 0, + best_grandpa_block_header: header, + min_block_delta: 1, + sessions: VecDeque::new(), + }; + let voting_target_with = |oracle: &mut VoterOracle, + best_beefy: NumberFor, + best_grandpa: NumberFor| + -> Option> { + oracle.best_beefy_block = best_beefy; + oracle.best_grandpa_block_header.number = best_grandpa; + oracle.voting_target() + }; // rounds not initialized -> should vote: `None` - assert_eq!(oracle.voting_target(0, 1), None); + assert_eq!(voting_target_with(&mut oracle, 0, 1), None); let keys = &[Keyring::Alice]; let validator_set = ValidatorSet::new(make_beefy_ids(keys), 0).unwrap(); @@ -1287,29 +1260,29 @@ pub(crate) mod tests { // under min delta oracle.min_block_delta = 4; - assert_eq!(oracle.voting_target(1, 1), None); - assert_eq!(oracle.voting_target(2, 5), None); + assert_eq!(voting_target_with(&mut oracle, 1, 1), None); + assert_eq!(voting_target_with(&mut oracle, 2, 5), None); // vote on min delta - assert_eq!(oracle.voting_target(4, 9), Some(8)); + assert_eq!(voting_target_with(&mut oracle, 4, 9), Some(8)); oracle.min_block_delta = 8; - assert_eq!(oracle.voting_target(10, 18), Some(18)); + assert_eq!(voting_target_with(&mut oracle, 10, 18), Some(18)); // vote on power of two oracle.min_block_delta = 1; - assert_eq!(oracle.voting_target(1000, 1008), Some(1004)); - assert_eq!(oracle.voting_target(1000, 1016), Some(1008)); + assert_eq!(voting_target_with(&mut oracle, 1000, 1008), Some(1004)); + assert_eq!(voting_target_with(&mut oracle, 1000, 1016), Some(1008)); // nothing new to vote on - assert_eq!(oracle.voting_target(1000, 1000), None); + assert_eq!(voting_target_with(&mut oracle, 1000, 1000), None); // vote on mandatory oracle.sessions.clear(); oracle.add_session(Rounds::new(1000, validator_set.clone())); - assert_eq!(oracle.voting_target(0, 1008), Some(1000)); + assert_eq!(voting_target_with(&mut oracle, 0, 1008), Some(1000)); oracle.sessions.clear(); oracle.add_session(Rounds::new(1001, validator_set.clone())); - assert_eq!(oracle.voting_target(1000, 1008), Some(1001)); + assert_eq!(voting_target_with(&mut oracle, 1000, 1008), Some(1001)); } #[test] @@ -1317,16 +1290,34 @@ pub(crate) mod tests { let keys = &[Keyring::Alice]; let validator_set = ValidatorSet::new(make_beefy_ids(keys), 0).unwrap(); - let mut oracle = VoterOracle:: { min_block_delta: 1, sessions: VecDeque::new() }; + let header = Header::new( + 1u32.into(), + Default::default(), + Default::default(), + Default::default(), + Digest::default(), + ); + let mut oracle = VoterOracle:: { + best_beefy_block: 0, + best_grandpa_block_header: header, + min_block_delta: 1, + sessions: VecDeque::new(), + }; + let accepted_interval_with = |oracle: &mut VoterOracle, + best_grandpa: NumberFor| + -> Result<(NumberFor, NumberFor), Error> { + oracle.best_grandpa_block_header.number = best_grandpa; + oracle.accepted_interval() + }; // rounds not initialized -> should accept votes: `None` - assert!(oracle.accepted_interval(1).is_err()); + assert!(accepted_interval_with(&mut oracle, 1).is_err()); let session_one = 1; oracle.add_session(Rounds::new(session_one, validator_set.clone())); // mandatory not done, only accept mandatory for i in 0..15 { - assert_eq!(oracle.accepted_interval(i), Ok((session_one, session_one))); + assert_eq!(accepted_interval_with(&mut oracle, i), Ok((session_one, session_one))); } // add more sessions, nothing changes @@ -1336,7 +1327,7 @@ pub(crate) mod tests { oracle.add_session(Rounds::new(session_three, validator_set.clone())); // mandatory not done, should accept mandatory for session_one for i in session_three..session_three + 15 { - assert_eq!(oracle.accepted_interval(i), Ok((session_one, session_one))); + assert_eq!(accepted_interval_with(&mut oracle, i), Ok((session_one, session_one))); } // simulate finish mandatory for session one, prune oracle @@ -1344,7 +1335,7 @@ pub(crate) mod tests { oracle.try_prune(); // session_one pruned, should accept mandatory for session_two for i in session_three..session_three + 15 { - assert_eq!(oracle.accepted_interval(i), Ok((session_two, session_two))); + assert_eq!(accepted_interval_with(&mut oracle, i), Ok((session_two, session_two))); } // simulate finish mandatory for session two, prune oracle @@ -1352,26 +1343,29 @@ pub(crate) mod tests { oracle.try_prune(); // session_two pruned, should accept mandatory for session_three for i in session_three..session_three + 15 { - assert_eq!(oracle.accepted_interval(i), Ok((session_three, session_three))); + assert_eq!(accepted_interval_with(&mut oracle, i), Ok((session_three, session_three))); } // simulate finish mandatory for session three oracle.sessions.front_mut().unwrap().test_set_mandatory_done(true); // verify all other blocks in this session are now open to voting for i in session_three..session_three + 15 { - assert_eq!(oracle.accepted_interval(i), Ok((session_three, i))); + assert_eq!(accepted_interval_with(&mut oracle, i), Ok((session_three, i))); } // pruning does nothing in this case oracle.try_prune(); for i in session_three..session_three + 15 { - assert_eq!(oracle.accepted_interval(i), Ok((session_three, i))); + assert_eq!(accepted_interval_with(&mut oracle, i), Ok((session_three, i))); } // adding new session automatically prunes "finalized" previous session let session_four = 31; oracle.add_session(Rounds::new(session_four, validator_set.clone())); assert_eq!(oracle.sessions.front().unwrap().session_start(), session_four); - assert_eq!(oracle.accepted_interval(session_four + 10), Ok((session_four, session_four))); + assert_eq!( + accepted_interval_with(&mut oracle, session_four + 10), + Ok((session_four, session_four)) + ); } #[test] @@ -1405,7 +1399,7 @@ pub(crate) mod tests { let keys = &[Keyring::Alice]; let validator_set = ValidatorSet::new(make_beefy_ids(keys), 0).unwrap(); let mut net = BeefyTestNet::new(1); - let mut worker = create_beefy_worker(&net.peer(0), &keys[0], 1, validator_set.clone()); + let mut worker = create_beefy_worker(net.peer(0), &keys[0], 1, validator_set.clone()); // keystore doesn't contain other keys than validators' assert_eq!(worker.verify_validator_set(&1, &validator_set), Ok(())); @@ -1429,7 +1423,7 @@ pub(crate) mod tests { let validator_set = ValidatorSet::new(make_beefy_ids(&keys), 0).unwrap(); let mut net = BeefyTestNet::new(1); let backend = net.peer(0).client().as_backend(); - let mut worker = create_beefy_worker(&net.peer(0), &keys[0], 1, validator_set.clone()); + let mut worker = create_beefy_worker(net.peer(0), &keys[0], 1, validator_set.clone()); // remove default session, will manually add custom one. worker.persisted_state.voting_oracle.sessions.clear(); @@ -1449,7 +1443,7 @@ pub(crate) mod tests { }; // no 'best beefy block' or finality proofs - assert_eq!(worker.best_beefy_block(), 0); + assert_eq!(worker.persisted_state.best_beefy_block(), 0); poll_fn(move |cx| { assert_eq!(best_block_stream.poll_next_unpin(cx), Poll::Pending); assert_eq!(finality_proof.poll_next_unpin(cx), Poll::Pending); @@ -1457,6 +1451,7 @@ pub(crate) mod tests { }) .await; + let client = net.peer(0).client().as_client(); // unknown hash for block #1 let (mut best_block_streams, mut finality_proofs) = get_beefy_streams(&mut net, keys.clone()); @@ -1471,10 +1466,16 @@ pub(crate) mod tests { // try to finalize block #1 worker.finalize(justif.clone()).unwrap(); // verify block finalized - assert_eq!(worker.best_beefy_block(), 1); + assert_eq!(worker.persisted_state.best_beefy_block(), 1); poll_fn(move |cx| { - // unknown hash -> nothing streamed - assert_eq!(best_block_stream.poll_next_unpin(cx), Poll::Pending); + // expect Some(hash-of-block-1) + match best_block_stream.poll_next_unpin(cx) { + Poll::Ready(Some(hash)) => { + let block_num = client.number(hash).unwrap(); + assert_eq!(block_num, Some(1)); + }, + v => panic!("unexpected value: {:?}", v), + } // commitment streamed match finality_proof.poll_next_unpin(cx) { // expect justification @@ -1488,11 +1489,9 @@ pub(crate) mod tests { // generate 2 blocks, try again expect success let (mut best_block_streams, _) = get_beefy_streams(&mut net, keys); let mut best_block_stream = best_block_streams.drain(..).next().unwrap(); - let hashes = net.peer(0).push_blocks(2, false); + let hashes = net.peer(0).push_blocks(1, false); // finalize 1 and 2 without justifications (hashes does not contain genesis) - let hashof1 = hashes[0]; - let hashof2 = hashes[1]; - backend.finalize_block(hashof1, None).unwrap(); + let hashof2 = hashes[0]; backend.finalize_block(hashof2, None).unwrap(); let justif = create_finality_proof(2); @@ -1504,7 +1503,7 @@ pub(crate) mod tests { // new session starting at #2 is in front assert_eq!(worker.active_rounds().unwrap().session_start(), 2); // verify block finalized - assert_eq!(worker.best_beefy_block(), 2); + assert_eq!(worker.persisted_state.best_beefy_block(), 2); poll_fn(move |cx| { match best_block_stream.poll_next_unpin(cx) { // expect Some(hash-of-block-2) @@ -1528,7 +1527,7 @@ pub(crate) mod tests { let keys = &[Keyring::Alice, Keyring::Bob]; let validator_set = ValidatorSet::new(make_beefy_ids(keys), 0).unwrap(); let mut net = BeefyTestNet::new(1); - let mut worker = create_beefy_worker(&net.peer(0), &keys[0], 1, validator_set.clone()); + let mut worker = create_beefy_worker(net.peer(0), &keys[0], 1, validator_set.clone()); let worker_rounds = worker.active_rounds().unwrap(); assert_eq!(worker_rounds.session_start(), 1); @@ -1554,77 +1553,6 @@ pub(crate) mod tests { assert_eq!(rounds.validator_set_id(), new_validator_set.id()); } - #[tokio::test] - async fn should_triage_votes_and_process_later() { - let keys = &[Keyring::Alice, Keyring::Bob]; - let validator_set = ValidatorSet::new(make_beefy_ids(keys), 0).unwrap(); - let mut net = BeefyTestNet::new(1); - let mut worker = create_beefy_worker(&net.peer(0), &keys[0], 1, validator_set.clone()); - // remove default session, will manually add custom one. - worker.persisted_state.voting_oracle.sessions.clear(); - - fn new_vote( - block_number: NumberFor, - ) -> VoteMessage, AuthorityId, Signature> { - let commitment = Commitment { - payload: Payload::from_single_entry(*b"BF", vec![]), - block_number, - validator_set_id: 0, - }; - VoteMessage { - commitment, - id: Keyring::Alice.public(), - signature: Keyring::Alice.sign(b"I am committed"), - } - } - - // best grandpa is 20 - let best_grandpa_header = Header::new( - 20u32.into(), - Default::default(), - Default::default(), - Default::default(), - Digest::default(), - ); - - worker - .persisted_state - .voting_oracle - .add_session(Rounds::new(10, validator_set.clone())); - worker.persisted_state.best_grandpa_block_header = best_grandpa_header; - - // triage votes for blocks 10..13 - worker.triage_incoming_vote(new_vote(10)).unwrap(); - worker.triage_incoming_vote(new_vote(11)).unwrap(); - worker.triage_incoming_vote(new_vote(12)).unwrap(); - // triage votes for blocks 20..23 - worker.triage_incoming_vote(new_vote(20)).unwrap(); - worker.triage_incoming_vote(new_vote(21)).unwrap(); - worker.triage_incoming_vote(new_vote(22)).unwrap(); - - // vote for 10 should have been handled, while the rest buffered for later processing - let mut votes = worker.pending_votes.values(); - assert_eq!(votes.next().unwrap().first().unwrap().commitment.block_number, 11); - assert_eq!(votes.next().unwrap().first().unwrap().commitment.block_number, 12); - assert_eq!(votes.next().unwrap().first().unwrap().commitment.block_number, 20); - assert_eq!(votes.next().unwrap().first().unwrap().commitment.block_number, 21); - assert_eq!(votes.next().unwrap().first().unwrap().commitment.block_number, 22); - assert!(votes.next().is_none()); - - // simulate mandatory done, and retry buffered votes - worker - .persisted_state - .voting_oracle - .active_rounds_mut() - .unwrap() - .test_set_mandatory_done(true); - worker.try_pending_justif_and_votes().unwrap(); - // all blocks <= grandpa finalized should have been handled, rest still buffered - let mut votes = worker.pending_votes.values(); - assert_eq!(votes.next().unwrap().first().unwrap().commitment.block_number, 21); - assert_eq!(votes.next().unwrap().first().unwrap().commitment.block_number, 22); - } - #[tokio::test] async fn should_not_report_bad_old_or_self_equivocations() { let block_num = 1; @@ -1637,7 +1565,7 @@ pub(crate) mod tests { let api_alice = Arc::new(api_alice); let mut net = BeefyTestNet::new(1); - let mut worker = create_beefy_worker(&net.peer(0), &keys[0], 1, validator_set.clone()); + let mut worker = create_beefy_worker(net.peer(0), &keys[0], 1, validator_set.clone()); worker.runtime = api_alice.clone(); // let there be a block with num = 1: From 8a9f48bcf0c9f92949082535d77c12166522bb2f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bastian=20K=C3=B6cher?= Date: Thu, 16 Mar 2023 12:24:20 +0100 Subject: [PATCH 243/558] Try to fix flaky `temp-base-path-work` test (#13505) * Try to fix flaky `temp-base-path-work` test The test is most of the time failing when checking if the database path was deleted. The assumption is that it takes a little bit more time by the OS to actually clean up the temp path under high load. The pr tries to fix this by checking multiple times if the path was deleted. Besides that it also ensures that the tests that require the benchmark feature don't fail when compiled without the feature. * ".git/.scripts/commands/fmt/fmt.sh" * Capture signals earlier * Rewrite tests to let them having one big timeout * Remove unneeded dep * Update bin/node/cli/tests/common.rs Co-authored-by: Koute * Review feedback * Update bin/node/cli/tests/common.rs Co-authored-by: Anton --------- Co-authored-by: command-bot <> Co-authored-by: Koute Co-authored-by: Anton --- bin/node/cli/tests/benchmark_pallet_works.rs | 2 + bin/node/cli/tests/benchmark_storage_works.rs | 2 + bin/node/cli/tests/common.rs | 132 ++++++++-------- .../tests/running_the_node_and_interrupt.rs | 143 ++++++++---------- bin/node/cli/tests/telemetry.rs | 131 ++++++++-------- bin/node/cli/tests/temp_base_path_works.rs | 63 ++++---- client/cli/src/lib.rs | 14 +- client/cli/src/runner.rs | 97 +++++++----- 8 files changed, 296 insertions(+), 288 deletions(-) diff --git a/bin/node/cli/tests/benchmark_pallet_works.rs b/bin/node/cli/tests/benchmark_pallet_works.rs index 1a278f985..053516b95 100644 --- a/bin/node/cli/tests/benchmark_pallet_works.rs +++ b/bin/node/cli/tests/benchmark_pallet_works.rs @@ -16,6 +16,8 @@ // You should have received a copy of the GNU General Public License // along with this program. If not, see . +#![cfg(feature = "runtime-benchmarks")] + use assert_cmd::cargo::cargo_bin; use std::process::Command; diff --git a/bin/node/cli/tests/benchmark_storage_works.rs b/bin/node/cli/tests/benchmark_storage_works.rs index 1f1181218..953c07ca7 100644 --- a/bin/node/cli/tests/benchmark_storage_works.rs +++ b/bin/node/cli/tests/benchmark_storage_works.rs @@ -16,6 +16,8 @@ // You should have received a copy of the GNU General Public License // along with this program. If not, see . +#![cfg(feature = "runtime-benchmarks")] + use assert_cmd::cargo::cargo_bin; use std::{ path::Path, diff --git a/bin/node/cli/tests/common.rs b/bin/node/cli/tests/common.rs index a0de7300f..4c4824391 100644 --- a/bin/node/cli/tests/common.rs +++ b/bin/node/cli/tests/common.rs @@ -20,54 +20,26 @@ use assert_cmd::cargo::cargo_bin; use nix::{ - sys::signal::{kill, Signal::SIGINT}, + sys::signal::{kill, Signal, Signal::SIGINT}, unistd::Pid, }; use node_primitives::{Hash, Header}; +use regex::Regex; use std::{ io::{BufRead, BufReader, Read}, ops::{Deref, DerefMut}, - path::Path, - process::{self, Child, Command, ExitStatus}, + path::{Path, PathBuf}, + process::{self, Child, Command}, time::Duration, }; -use tokio::time::timeout; -/// Wait for the given `child` the given number of `secs`. -/// -/// Returns the `Some(exit status)` or `None` if the process did not finish in the given time. -pub fn wait_for(child: &mut Child, secs: u64) -> Result { - let result = wait_timeout::ChildExt::wait_timeout(child, Duration::from_secs(5.min(secs))) - .map_err(|_| ())?; - if let Some(exit_status) = result { - Ok(exit_status) - } else { - if secs > 5 { - eprintln!("Child process taking over 5 seconds to exit gracefully"); - let result = wait_timeout::ChildExt::wait_timeout(child, Duration::from_secs(secs - 5)) - .map_err(|_| ())?; - if let Some(exit_status) = result { - return Ok(exit_status) - } - } - eprintln!("Took too long to exit (> {} seconds). Killing...", secs); - let _ = child.kill(); - child.wait().unwrap(); - Err(()) - } -} - -/// Wait for at least n blocks to be finalized within a specified time. -pub async fn wait_n_finalized_blocks( - n: usize, - timeout_secs: u64, - url: &str, -) -> Result<(), tokio::time::error::Elapsed> { - timeout(Duration::from_secs(timeout_secs), wait_n_finalized_blocks_from(n, url)).await +/// Run the given `future` and panic if the `timeout` is hit. +pub async fn run_with_timeout(timeout: Duration, future: impl futures::Future) { + tokio::time::timeout(timeout, future).await.expect("Hit timeout"); } /// Wait for at least n blocks to be finalized from a specified node -pub async fn wait_n_finalized_blocks_from(n: usize, url: &str) { +pub async fn wait_n_finalized_blocks(n: usize, url: &str) { use substrate_rpc_client::{ws_client, ChainApi}; let mut built_blocks = std::collections::HashSet::new(); @@ -87,46 +59,54 @@ pub async fn wait_n_finalized_blocks_from(n: usize, url: &str) { /// Run the node for a while (3 blocks) pub async fn run_node_for_a_while(base_path: &Path, args: &[&str]) { - let mut cmd = Command::new(cargo_bin("substrate")) - .stdout(process::Stdio::piped()) - .stderr(process::Stdio::piped()) - .args(args) - .arg("-d") - .arg(base_path) - .spawn() - .unwrap(); + run_with_timeout(Duration::from_secs(60 * 10), async move { + let mut cmd = Command::new(cargo_bin("substrate")) + .stdout(process::Stdio::piped()) + .stderr(process::Stdio::piped()) + .args(args) + .arg("-d") + .arg(base_path) + .spawn() + .unwrap(); - let stderr = cmd.stderr.take().unwrap(); + let stderr = cmd.stderr.take().unwrap(); - let mut child = KillChildOnDrop(cmd); + let mut child = KillChildOnDrop(cmd); - let (ws_url, _) = find_ws_url_from_output(stderr); + let ws_url = extract_info_from_output(stderr).0.ws_url; - // Let it produce some blocks. - let _ = wait_n_finalized_blocks(3, 30, &ws_url).await; + // Let it produce some blocks. + wait_n_finalized_blocks(3, &ws_url).await; - assert!(child.try_wait().unwrap().is_none(), "the process should still be running"); + child.assert_still_running(); - // Stop the process - kill(Pid::from_raw(child.id().try_into().unwrap()), SIGINT).unwrap(); - assert!(wait_for(&mut child, 40).map(|x| x.success()).unwrap()); + // Stop the process + child.stop(); + }) + .await } -/// Run the node asserting that it fails with an error -pub fn run_node_assert_fail(base_path: &Path, args: &[&str]) { - let mut cmd = Command::new(cargo_bin("substrate")); +pub struct KillChildOnDrop(pub Child); - let mut child = KillChildOnDrop(cmd.args(args).arg("-d").arg(base_path).spawn().unwrap()); +impl KillChildOnDrop { + /// Stop the child and wait until it is finished. + /// + /// Asserts if the exit status isn't success. + pub fn stop(&mut self) { + self.stop_with_signal(SIGINT); + } - // Let it produce some blocks, but it should die within 10 seconds. - assert_ne!( - wait_timeout::ChildExt::wait_timeout(&mut *child, Duration::from_secs(10)).unwrap(), - None, - "the process should not be running anymore" - ); -} + /// Same as [`Self::stop`] but takes the `signal` that is sent to stop the child. + pub fn stop_with_signal(&mut self, signal: Signal) { + kill(Pid::from_raw(self.id().try_into().unwrap()), signal).unwrap(); + assert!(self.wait().unwrap().success()); + } -pub struct KillChildOnDrop(pub Child); + /// Asserts that the child is still running. + pub fn assert_still_running(&mut self) { + assert!(self.try_wait().unwrap().is_none(), "the process should still be running"); + } +} impl Drop for KillChildOnDrop { fn drop(&mut self) { @@ -148,18 +128,22 @@ impl DerefMut for KillChildOnDrop { } } -/// Read the WS address from the output. +/// Information extracted from a running node. +pub struct NodeInfo { + pub ws_url: String, + pub db_path: PathBuf, +} + +/// Extract [`NodeInfo`] from a running node by parsing its output. /// -/// This is hack to get the actual binded sockaddr because -/// substrate assigns a random port if the specified port was already binded. -pub fn find_ws_url_from_output(read: impl Read + Send) -> (String, String) { +/// Returns the [`NodeInfo`] and all the read data. +pub fn extract_info_from_output(read: impl Read + Send) -> (NodeInfo, String) { let mut data = String::new(); let ws_url = BufReader::new(read) .lines() .find_map(|line| { - let line = - line.expect("failed to obtain next line from stdout for WS address discovery"); + let line = line.expect("failed to obtain next line while extracting node info"); data.push_str(&line); data.push_str("\n"); @@ -176,5 +160,9 @@ pub fn find_ws_url_from_output(read: impl Read + Send) -> (String, String) { panic!("We should get a WebSocket address") }); - (ws_url, data) + // Database path is printed before the ws url! + let re = Regex::new(r"Database: .+ at (\S+)").unwrap(); + let db_path = PathBuf::from(re.captures(data.as_str()).unwrap().get(1).unwrap().as_str()); + + (NodeInfo { ws_url, db_path }, data) } diff --git a/bin/node/cli/tests/running_the_node_and_interrupt.rs b/bin/node/cli/tests/running_the_node_and_interrupt.rs index fc0bf69a0..3d5598f3f 100644 --- a/bin/node/cli/tests/running_the_node_and_interrupt.rs +++ b/bin/node/cli/tests/running_the_node_and_interrupt.rs @@ -18,94 +18,81 @@ #![cfg(unix)] use assert_cmd::cargo::cargo_bin; -use nix::{ - sys::signal::{ - kill, - Signal::{self, SIGINT, SIGTERM}, - }, - unistd::Pid, +use nix::sys::signal::Signal::{self, SIGINT, SIGTERM}; +use std::{ + process::{self, Child, Command}, + time::Duration, }; -use std::process::{self, Child, Command}; use tempfile::tempdir; pub mod common; #[tokio::test] async fn running_the_node_works_and_can_be_interrupted() { - async fn run_command_and_kill(signal: Signal) { - let base_path = tempdir().expect("could not create a temp dir"); - let mut cmd = common::KillChildOnDrop( + common::run_with_timeout(Duration::from_secs(60 * 10), async move { + async fn run_command_and_kill(signal: Signal) { + let base_path = tempdir().expect("could not create a temp dir"); + let mut cmd = common::KillChildOnDrop( + Command::new(cargo_bin("substrate")) + .stdout(process::Stdio::piped()) + .stderr(process::Stdio::piped()) + .args(&["--dev", "-d"]) + .arg(base_path.path()) + .arg("--db=paritydb") + .arg("--no-hardware-benchmarks") + .spawn() + .unwrap(), + ); + + let stderr = cmd.stderr.take().unwrap(); + + let ws_url = common::extract_info_from_output(stderr).0.ws_url; + + common::wait_n_finalized_blocks(3, &ws_url).await; + + cmd.assert_still_running(); + + cmd.stop_with_signal(signal); + + // Check if the database was closed gracefully. If it was not, + // there may exist a ref cycle that prevents the Client from being dropped properly. + // + // parity-db only writes the stats file on clean shutdown. + let stats_file = base_path.path().join("chains/dev/paritydb/full/stats.txt"); + assert!(std::path::Path::exists(&stats_file)); + } + + run_command_and_kill(SIGINT).await; + run_command_and_kill(SIGTERM).await; + }) + .await; +} + +#[tokio::test] +async fn running_two_nodes_with_the_same_ws_port_should_work() { + common::run_with_timeout(Duration::from_secs(60 * 10), async move { + fn start_node() -> Child { Command::new(cargo_bin("substrate")) .stdout(process::Stdio::piped()) .stderr(process::Stdio::piped()) - .args(&["--dev", "-d"]) - .arg(base_path.path()) - .arg("--db=paritydb") - .arg("--no-hardware-benchmarks") + .args(&["--dev", "--tmp", "--ws-port=45789", "--no-hardware-benchmarks"]) .spawn() - .unwrap(), - ); - - let stderr = cmd.stderr.take().unwrap(); - - let (ws_url, _) = common::find_ws_url_from_output(stderr); - - common::wait_n_finalized_blocks(3, 30, &ws_url) - .await - .expect("Blocks are produced in time"); - assert!(cmd.try_wait().unwrap().is_none(), "the process should still be running"); - kill(Pid::from_raw(cmd.id().try_into().unwrap()), signal).unwrap(); - assert_eq!( - common::wait_for(&mut cmd, 30).map(|x| x.success()), - Ok(true), - "the process must exit gracefully after signal {}", - signal, - ); - // Check if the database was closed gracefully. If it was not, - // there may exist a ref cycle that prevents the Client from being dropped properly. - // - // parity-db only writes the stats file on clean shutdown. - let stats_file = base_path.path().join("chains/dev/paritydb/full/stats.txt"); - assert!(std::path::Path::exists(&stats_file)); - } - - run_command_and_kill(SIGINT).await; - run_command_and_kill(SIGTERM).await; -} + .unwrap() + } -#[tokio::test] -async fn running_two_nodes_with_the_same_ws_port_should_work() { - fn start_node() -> Child { - Command::new(cargo_bin("substrate")) - .stdout(process::Stdio::piped()) - .stderr(process::Stdio::piped()) - .args(&["--dev", "--tmp", "--ws-port=45789", "--no-hardware-benchmarks"]) - .spawn() - .unwrap() - } - - let mut first_node = common::KillChildOnDrop(start_node()); - let mut second_node = common::KillChildOnDrop(start_node()); - - let stderr = first_node.stderr.take().unwrap(); - let (ws_url, _) = common::find_ws_url_from_output(stderr); - - common::wait_n_finalized_blocks(3, 30, &ws_url).await.unwrap(); - - assert!(first_node.try_wait().unwrap().is_none(), "The first node should still be running"); - assert!(second_node.try_wait().unwrap().is_none(), "The second node should still be running"); - - kill(Pid::from_raw(first_node.id().try_into().unwrap()), SIGINT).unwrap(); - kill(Pid::from_raw(second_node.id().try_into().unwrap()), SIGINT).unwrap(); - - assert_eq!( - common::wait_for(&mut first_node, 30).map(|x| x.success()), - Ok(true), - "The first node must exit gracefully", - ); - assert_eq!( - common::wait_for(&mut second_node, 30).map(|x| x.success()), - Ok(true), - "The second node must exit gracefully", - ); + let mut first_node = common::KillChildOnDrop(start_node()); + let mut second_node = common::KillChildOnDrop(start_node()); + + let stderr = first_node.stderr.take().unwrap(); + let ws_url = common::extract_info_from_output(stderr).0.ws_url; + + common::wait_n_finalized_blocks(3, &ws_url).await; + + first_node.assert_still_running(); + second_node.assert_still_running(); + + first_node.stop(); + second_node.stop(); + }) + .await; } diff --git a/bin/node/cli/tests/telemetry.rs b/bin/node/cli/tests/telemetry.rs index 633cc996c..a68746a2c 100644 --- a/bin/node/cli/tests/telemetry.rs +++ b/bin/node/cli/tests/telemetry.rs @@ -17,78 +17,75 @@ // along with this program. If not, see . use assert_cmd::cargo::cargo_bin; -use nix::{ - sys::signal::{kill, Signal::SIGINT}, - unistd::Pid, -}; -use std::process; +use std::{process, time::Duration}; + +use crate::common::KillChildOnDrop; pub mod common; pub mod websocket_server; #[tokio::test] async fn telemetry_works() { - let config = websocket_server::Config { - capacity: 1, - max_frame_size: 1048 * 1024, - send_buffer_len: 32, - bind_address: "127.0.0.1:0".parse().unwrap(), - }; - let mut server = websocket_server::WsServer::new(config).await.unwrap(); - - let addr = server.local_addr().unwrap(); - - let server_task = tokio::spawn(async move { - loop { - use websocket_server::Event; - match server.next_event().await { - // New connection on the listener. - Event::ConnectionOpen { address } => { - println!("New connection from {:?}", address); - server.accept(); - }, - - // Received a message from a connection. - Event::BinaryFrame { message, .. } => { - let json: serde_json::Value = serde_json::from_slice(&message).unwrap(); - let object = - json.as_object().unwrap().get("payload").unwrap().as_object().unwrap(); - if matches!(object.get("best"), Some(serde_json::Value::String(_))) { - break - } - }, - - Event::TextFrame { .. } => panic!("Got a TextFrame over the socket, this is a bug"), - - // Connection has been closed. - Event::ConnectionError { .. } => {}, + common::run_with_timeout(Duration::from_secs(60 * 10), async move { + let config = websocket_server::Config { + capacity: 1, + max_frame_size: 1048 * 1024, + send_buffer_len: 32, + bind_address: "127.0.0.1:0".parse().unwrap(), + }; + let mut server = websocket_server::WsServer::new(config).await.unwrap(); + + let addr = server.local_addr().unwrap(); + + let server_task = tokio::spawn(async move { + loop { + use websocket_server::Event; + match server.next_event().await { + // New connection on the listener. + Event::ConnectionOpen { address } => { + println!("New connection from {:?}", address); + server.accept(); + }, + + // Received a message from a connection. + Event::BinaryFrame { message, .. } => { + let json: serde_json::Value = serde_json::from_slice(&message).unwrap(); + let object = + json.as_object().unwrap().get("payload").unwrap().as_object().unwrap(); + if matches!(object.get("best"), Some(serde_json::Value::String(_))) { + break + } + }, + + Event::TextFrame { .. } => + panic!("Got a TextFrame over the socket, this is a bug"), + + // Connection has been closed. + Event::ConnectionError { .. } => {}, + } } - } - }); - - let mut substrate = process::Command::new(cargo_bin("substrate")); - - let mut substrate = substrate - .args(&["--dev", "--tmp", "--telemetry-url"]) - .arg(format!("ws://{} 10", addr)) - .arg("--no-hardware-benchmarks") - .stdout(process::Stdio::piped()) - .stderr(process::Stdio::piped()) - .stdin(process::Stdio::null()) - .spawn() - .unwrap(); - - server_task.await.expect("server task panicked"); - - assert!(substrate.try_wait().unwrap().is_none(), "the process should still be running"); - - // Stop the process - kill(Pid::from_raw(substrate.id().try_into().unwrap()), SIGINT).unwrap(); - assert!(common::wait_for(&mut substrate, 40).map(|x| x.success()).unwrap_or_default()); - - let output = substrate.wait_with_output().unwrap(); - - println!("{}", String::from_utf8(output.stdout).unwrap()); - eprintln!("{}", String::from_utf8(output.stderr).unwrap()); - assert!(output.status.success()); + }); + + let mut substrate = process::Command::new(cargo_bin("substrate")); + + let mut substrate = KillChildOnDrop( + substrate + .args(&["--dev", "--tmp", "--telemetry-url"]) + .arg(format!("ws://{} 10", addr)) + .arg("--no-hardware-benchmarks") + .stdout(process::Stdio::piped()) + .stderr(process::Stdio::piped()) + .stdin(process::Stdio::null()) + .spawn() + .unwrap(), + ); + + server_task.await.expect("server task panicked"); + + substrate.assert_still_running(); + + // Stop the process + substrate.stop(); + }) + .await; } diff --git a/bin/node/cli/tests/temp_base_path_works.rs b/bin/node/cli/tests/temp_base_path_works.rs index 4e743f2d3..89e901c00 100644 --- a/bin/node/cli/tests/temp_base_path_works.rs +++ b/bin/node/cli/tests/temp_base_path_works.rs @@ -19,45 +19,42 @@ #![cfg(unix)] use assert_cmd::cargo::cargo_bin; -use nix::{ - sys::signal::{kill, Signal::SIGINT}, - unistd::Pid, -}; -use regex::Regex; use std::{ - io::Read, - path::PathBuf, process::{Command, Stdio}, + time::Duration, }; pub mod common; #[tokio::test] async fn temp_base_path_works() { - let mut cmd = Command::new(cargo_bin("substrate")); - let mut child = common::KillChildOnDrop( - cmd.args(&["--dev", "--tmp", "--no-hardware-benchmarks"]) - .stdout(Stdio::piped()) - .stderr(Stdio::piped()) - .spawn() - .unwrap(), - ); - - let mut stderr = child.stderr.take().unwrap(); - let (ws_url, mut data) = common::find_ws_url_from_output(&mut stderr); - - // Let it produce some blocks. - common::wait_n_finalized_blocks(3, 30, &ws_url).await.unwrap(); - assert!(child.try_wait().unwrap().is_none(), "the process should still be running"); - - // Stop the process - kill(Pid::from_raw(child.id().try_into().unwrap()), SIGINT).unwrap(); - assert!(common::wait_for(&mut child, 40).map(|x| x.success()).unwrap_or_default()); - - // Ensure the database has been deleted - stderr.read_to_string(&mut data).unwrap(); - let re = Regex::new(r"Database: .+ at (\S+)").unwrap(); - let db_path = PathBuf::from(re.captures(data.as_str()).unwrap().get(1).unwrap().as_str()); - - assert!(!db_path.exists()); + common::run_with_timeout(Duration::from_secs(60 * 10), async move { + let mut cmd = Command::new(cargo_bin("substrate")); + let mut child = common::KillChildOnDrop( + cmd.args(&["--dev", "--tmp", "--no-hardware-benchmarks"]) + .stdout(Stdio::piped()) + .stderr(Stdio::piped()) + .spawn() + .unwrap(), + ); + + let mut stderr = child.stderr.take().unwrap(); + let node_info = common::extract_info_from_output(&mut stderr).0; + + // Let it produce some blocks. + common::wait_n_finalized_blocks(3, &node_info.ws_url).await; + + // Ensure the db path exists while the node is running + assert!(node_info.db_path.exists()); + + child.assert_still_running(); + + // Stop the process + child.stop(); + + if node_info.db_path.exists() { + panic!("Database path `{}` wasn't deleted!", node_info.db_path.display()); + } + }) + .await; } diff --git a/client/cli/src/lib.rs b/client/cli/src/lib.rs index adbb29931..e73321ecc 100644 --- a/client/cli/src/lib.rs +++ b/client/cli/src/lib.rs @@ -197,10 +197,15 @@ pub trait SubstrateCli: Sized { command: &T, ) -> error::Result> { let tokio_runtime = build_runtime()?; + + // `capture` needs to be called in a tokio context. + // Also capture them as early as possible. + let signals = tokio_runtime.block_on(async { Signals::capture() })?; + let config = command.create_configuration(self, tokio_runtime.handle().clone())?; command.init(&Self::support_url(), &Self::impl_version(), |_, _| {}, &config)?; - Runner::new(config, tokio_runtime) + Runner::new(config, tokio_runtime, signals) } /// Create a runner for the command provided in argument. The `logger_hook` can be used to setup @@ -231,10 +236,15 @@ pub trait SubstrateCli: Sized { F: FnOnce(&mut LoggerBuilder, &Configuration), { let tokio_runtime = build_runtime()?; + + // `capture` needs to be called in a tokio context. + // Also capture them as early as possible. + let signals = tokio_runtime.block_on(async { Signals::capture() })?; + let config = command.create_configuration(self, tokio_runtime.handle().clone())?; command.init(&Self::support_url(), &Self::impl_version(), logger_hook, &config)?; - Runner::new(config, tokio_runtime) + Runner::new(config, tokio_runtime, signals) } /// Native runtime version. fn native_runtime_version(chain_spec: &Box) -> &'static RuntimeVersion; diff --git a/client/cli/src/runner.rs b/client/cli/src/runner.rs index 8917a37c4..47adfcf89 100644 --- a/client/cli/src/runner.rs +++ b/client/cli/src/runner.rs @@ -18,54 +18,72 @@ use crate::{error::Error as CliError, Result, SubstrateCli}; use chrono::prelude::*; -use futures::{future, future::FutureExt, pin_mut, select, Future}; +use futures::{ + future::{self, BoxFuture, FutureExt}, + pin_mut, select, Future, +}; use log::info; use sc_service::{Configuration, Error as ServiceError, TaskManager}; use sc_utils::metrics::{TOKIO_THREADS_ALIVE, TOKIO_THREADS_TOTAL}; use std::{marker::PhantomData, time::Duration}; -#[cfg(target_family = "unix")] -async fn main(func: F) -> std::result::Result<(), E> -where - F: Future> + future::FusedFuture, - E: std::error::Error + Send + Sync + 'static + From, -{ - use tokio::signal::unix::{signal, SignalKind}; +/// Abstraction over OS signals to handle the shutdown of the node smoothly. +/// +/// On `unix` this represents `SigInt` and `SigTerm`. +pub struct Signals(BoxFuture<'static, ()>); - let mut stream_int = signal(SignalKind::interrupt()).map_err(ServiceError::Io)?; - let mut stream_term = signal(SignalKind::terminate()).map_err(ServiceError::Io)?; - - let t1 = stream_int.recv().fuse(); - let t2 = stream_term.recv().fuse(); - let t3 = func; - - pin_mut!(t1, t2, t3); +impl Signals { + /// Capture the relevant signals to handle shutdown of the node smoothly. + /// + /// Needs to be called in a Tokio context to have access to the tokio reactor. + #[cfg(target_family = "unix")] + pub fn capture() -> std::result::Result { + use tokio::signal::unix::{signal, SignalKind}; + + let mut stream_int = signal(SignalKind::interrupt()).map_err(ServiceError::Io)?; + let mut stream_term = signal(SignalKind::terminate()).map_err(ServiceError::Io)?; + + Ok(Signals( + async move { + future::select(stream_int.recv().boxed(), stream_term.recv().boxed()).await; + } + .boxed(), + )) + } - select! { - _ = t1 => {}, - _ = t2 => {}, - res = t3 => res?, + /// Capture the relevant signals to handle shutdown of the node smoothly. + /// + /// Needs to be called in a Tokio context to have access to the tokio reactor. + #[cfg(not(unix))] + pub fn capture() -> std::result::Result { + use tokio::signal::ctrl_c; + + Ok(Signals( + async move { + let _ = ctrl_c().await; + } + .boxed(), + )) } - Ok(()) + /// A dummy signal that never returns. + pub fn dummy() -> Self { + Self(future::pending().boxed()) + } } -#[cfg(not(unix))] -async fn main(func: F) -> std::result::Result<(), E> +async fn main(func: F, signals: impl Future) -> std::result::Result<(), E> where F: Future> + future::FusedFuture, - E: std::error::Error + Send + Sync + 'static + From, + E: std::error::Error + Send + Sync + 'static, { - use tokio::signal::ctrl_c; - - let t1 = ctrl_c().fuse(); - let t2 = func; + let signals = signals.fuse(); - pin_mut!(t1, t2); + pin_mut!(func, signals); select! { - _ = t1 => {}, - res = t2 => res?, + _ = signals => {}, + res = func => res?, } Ok(()) @@ -89,6 +107,7 @@ fn run_until_exit( tokio_runtime: tokio::runtime::Runtime, future: F, task_manager: TaskManager, + signals: impl Future, ) -> std::result::Result<(), E> where F: Future> + future::Future, @@ -97,7 +116,7 @@ where let f = future.fuse(); pin_mut!(f); - tokio_runtime.block_on(main(f))?; + tokio_runtime.block_on(main(f, signals))?; drop(task_manager); Ok(()) @@ -107,13 +126,18 @@ where pub struct Runner { config: Configuration, tokio_runtime: tokio::runtime::Runtime, + signals: Signals, phantom: PhantomData, } impl Runner { /// Create a new runtime with the command provided in argument - pub fn new(config: Configuration, tokio_runtime: tokio::runtime::Runtime) -> Result> { - Ok(Runner { config, tokio_runtime, phantom: PhantomData }) + pub fn new( + config: Configuration, + tokio_runtime: tokio::runtime::Runtime, + signals: Signals, + ) -> Result> { + Ok(Runner { config, tokio_runtime, signals, phantom: PhantomData }) } /// Log information about the node itself. @@ -147,7 +171,7 @@ impl Runner { self.print_node_infos(); let mut task_manager = self.tokio_runtime.block_on(initialize(self.config))?; - let res = self.tokio_runtime.block_on(main(task_manager.future().fuse())); + let res = self.tokio_runtime.block_on(main(task_manager.future().fuse(), self.signals.0)); // We need to drop the task manager here to inform all tasks that they should shut down. // // This is important to be done before we instruct the tokio runtime to shutdown. Otherwise @@ -210,7 +234,7 @@ impl Runner { E: std::error::Error + Send + Sync + 'static + From + From, { let (future, task_manager) = runner(self.config)?; - run_until_exit::<_, E>(self.tokio_runtime, future, task_manager) + run_until_exit::<_, E>(self.tokio_runtime, future, task_manager, self.signals.0) } /// Get an immutable reference to the node Configuration @@ -369,6 +393,7 @@ mod tests { runtime_cache_size: 2, }, runtime, + Signals::dummy(), ) .unwrap(); From 22cca143a2c8e4a03ae770d71e94329eb518cca8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gon=C3=A7alo=20Pestana?= Date: Thu, 16 Mar 2023 12:09:50 +0000 Subject: [PATCH 244/558] Integration tests for staking + election-provider-multi-phase (#12972) * EPM and staking pallets: Adds new crate for integration tests a * Adds ExtBuilder and helpers with initial conditions assertions * removes account helpers; adds staking, session, etc genesis * Adds kusama incident test case * Prepare for slashing test * Adds solution submission * slash_through_offending_threshold * Renames e2e integration tests dir and crate * consistently slash 10% of validator set * finishes continous_slashes_below_offending_threshold test * Update frame/election-provider-multi-phase/test-staking-e2e/src/lib.rs Co-authored-by: Ankan <10196091+Ank4n@users.noreply.github.com> * Update frame/election-provider-multi-phase/test-staking-e2e/src/lib.rs Co-authored-by: Ankan <10196091+Ank4n@users.noreply.github.com> * Update frame/election-provider-multi-phase/test-staking-e2e/src/lib.rs Co-authored-by: Kian Paimani <5588131+kianenigma@users.noreply.github.com> * Update frame/election-provider-multi-phase/test-staking-e2e/src/mock.rs Co-authored-by: Kian Paimani <5588131+kianenigma@users.noreply.github.com> * Update frame/election-provider-multi-phase/test-staking-e2e/src/mock.rs Co-authored-by: Kian Paimani <5588131+kianenigma@users.noreply.github.com> * Update frame/election-provider-multi-phase/test-staking-e2e/src/mock.rs Co-authored-by: Kian Paimani <5588131+kianenigma@users.noreply.github.com> * mock fixes * Additional checks to delayed solution eras and mock fixes * nits and addresses review comments; splits ext_builder into one per pallet * helper to set balances ext builder * bring up mock.rs to master * integration test fixes and additions --------- Co-authored-by: Ankan <10196091+Ank4n@users.noreply.github.com> Co-authored-by: Kian Paimani <5588131+kianenigma@users.noreply.github.com> --- .../test-staking-e2e/Cargo.lock | 3609 +++++++++++++++++ .../test-staking-e2e/Cargo.toml | 40 + .../test-staking-e2e/src/lib.rs | 206 + .../test-staking-e2e/src/mock.rs | 779 ++++ 4 files changed, 4634 insertions(+) create mode 100644 frame/election-provider-multi-phase/test-staking-e2e/Cargo.lock create mode 100644 frame/election-provider-multi-phase/test-staking-e2e/Cargo.toml create mode 100644 frame/election-provider-multi-phase/test-staking-e2e/src/lib.rs create mode 100644 frame/election-provider-multi-phase/test-staking-e2e/src/mock.rs diff --git a/frame/election-provider-multi-phase/test-staking-e2e/Cargo.lock b/frame/election-provider-multi-phase/test-staking-e2e/Cargo.lock new file mode 100644 index 000000000..b179ab705 --- /dev/null +++ b/frame/election-provider-multi-phase/test-staking-e2e/Cargo.lock @@ -0,0 +1,3609 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "Inflector" +version = "0.11.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fe438c63458706e03479442743baae6c88256498e6431708f6dfc520a26515d3" +dependencies = [ + "lazy_static", + "regex", +] + +[[package]] +name = "addr2line" +version = "0.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b9ecd88a8c8378ca913a680cd98f0f13ac67383d35993f86c90a70e3f137816b" +dependencies = [ + "gimli", +] + +[[package]] +name = "adler" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" + +[[package]] +name = "ahash" +version = "0.7.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fcb51a0695d8f838b1ee009b3fbf66bda078cd64590202a864a8f3e8c4315c47" +dependencies = [ + "getrandom 0.2.8", + "once_cell", + "version_check", +] + +[[package]] +name = "ahash" +version = "0.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2c99f64d1e06488f620f932677e24bc6e2897582980441ae90a671415bd7ec2f" +dependencies = [ + "cfg-if", + "getrandom 0.2.8", + "once_cell", + "version_check", +] + +[[package]] +name = "aho-corasick" +version = "0.7.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cc936419f96fa211c1b9166887b38e5e40b19958e5b895be7c1f93adec7071ac" +dependencies = [ + "memchr", +] + +[[package]] +name = "android_system_properties" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "819e7219dbd41043ac279b19830f2efc897156490d7fd6ea916720117ee66311" +dependencies = [ + "libc", +] + +[[package]] +name = "ansi_term" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d52a9bb7ec0cf484c551830a7ce27bd20d67eac647e1befb56b0be4ee39a55d2" +dependencies = [ + "winapi", +] + +[[package]] +name = "anyhow" +version = "1.0.69" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "224afbd727c3d6e4b90103ece64b8d1b67fbb1973b1046c2281eed3f3803f800" + +[[package]] +name = "approx" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cab112f0a86d568ea0e627cc1d6be74a1e9cd55214684db5561995f6dad897c6" +dependencies = [ + "num-traits", +] + +[[package]] +name = "array-bytes" +version = "4.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f52f63c5c1316a16a4b35eaac8b76a98248961a533f061684cb2a7cb0eafb6c6" + +[[package]] +name = "arrayref" +version = "0.3.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a4c527152e37cf757a3f78aae5a06fbeefdb07ccc535c980a3208ee3060dd544" + +[[package]] +name = "arrayvec" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "23b62fc65de8e4e7f52534fb52b0f3ed04746ae267519eef2a83941e8085068b" + +[[package]] +name = "arrayvec" +version = "0.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8da52d66c7071e2e3fa2a1e5c6d088fec47b593032b254f5e980de8ea54454d6" + +[[package]] +name = "async-trait" +version = "0.1.59" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "31e6e93155431f3931513b243d371981bb2770112b370c82745a1d19d2f99364" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "autocfg" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" + +[[package]] +name = "backtrace" +version = "0.3.66" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cab84319d616cfb654d03394f38ab7e6f0919e181b1b57e1fd15e7fb4077d9a7" +dependencies = [ + "addr2line", + "cc", + "cfg-if", + "libc", + "miniz_oxide", + "object", + "rustc-demangle", +] + +[[package]] +name = "base16ct" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "349a06037c7bf932dd7e7d1f653678b2038b9ad46a74102f1fc7bd7872678cce" + +[[package]] +name = "base58" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6107fe1be6682a68940da878d9e9f5e90ca5745b3dec9fd1bb393c8777d4f581" + +[[package]] +name = "base64" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9e1b586273c5702936fe7b7d6896644d8be71e6314cfe09d3167c95f712589e8" + +[[package]] +name = "base64ct" +version = "1.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b645a089122eccb6111b4f81cbc1a49f5900ac4666bb93ac027feaecf15607bf" + +[[package]] +name = "bincode" +version = "1.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b1f45e9417d87227c7a56d22e471c6206462cba514c7590c09aff4cf6d1ddcad" +dependencies = [ + "serde", +] + +[[package]] +name = "bitflags" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" + +[[package]] +name = "bitvec" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1bc2832c24239b0141d5674bb9174f9d68a8b5b3f2753311927c172ca46f7e9c" +dependencies = [ + "funty", + "radium", + "tap", + "wyz", +] + +[[package]] +name = "blake2" +version = "0.10.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b12e5fd123190ce1c2e559308a94c9bacad77907d4c6005d9e58fe1a0689e55e" +dependencies = [ + "digest 0.10.6", +] + +[[package]] +name = "blake2b_simd" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3c2f0dc9a68c6317d884f97cc36cf5a3d20ba14ce404227df55e1af708ab04bc" +dependencies = [ + "arrayref", + "arrayvec 0.7.2", + "constant_time_eq", +] + +[[package]] +name = "block-buffer" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c0940dc441f31689269e10ac70eb1002a3a1d3ad1390e030043662eb7fe4688b" +dependencies = [ + "block-padding", + "byte-tools", + "byteorder", + "generic-array 0.12.4", +] + +[[package]] +name = "block-buffer" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4152116fd6e9dadb291ae18fc1ec3575ed6d84c29642d97890f4b4a3417297e4" +dependencies = [ + "generic-array 0.14.6", +] + +[[package]] +name = "block-buffer" +version = "0.10.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "69cce20737498f97b993470a6e536b8523f0af7892a4f928cceb1ac5e52ebe7e" +dependencies = [ + "generic-array 0.14.6", +] + +[[package]] +name = "block-padding" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fa79dedbb091f449f1f39e53edf88d5dbe95f895dae6135a8d7b881fb5af73f5" +dependencies = [ + "byte-tools", +] + +[[package]] +name = "bounded-collections" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a071c348a5ef6da1d3a87166b408170b46002382b1dda83992b5c2208cefb370" +dependencies = [ + "log", + "parity-scale-codec", + "scale-info", + "serde", +] + +[[package]] +name = "bumpalo" +version = "3.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "572f695136211188308f16ad2ca5c851a712c464060ae6974944458eb83880ba" + +[[package]] +name = "byte-slice-cast" +version = "1.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c3ac9f8b63eca6fd385229b3675f6cc0dc5c8a5c8a54a59d4f52ffd670d87b0c" + +[[package]] +name = "byte-tools" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3b5ca7a04898ad4bcd41c90c5285445ff5b791899bb1b0abdd2a2aa791211d7" + +[[package]] +name = "bytemuck" +version = "1.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "17febce684fd15d89027105661fec94afb475cb995fbc59d2865198446ba2eea" + +[[package]] +name = "byteorder" +version = "1.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610" + +[[package]] +name = "bytes" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dfb24e866b15a1af2a1b663f10c6b6b8f397a84aadb828f12e5b289ec23a3a3c" + +[[package]] +name = "cc" +version = "1.0.77" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e9f73505338f7d905b19d18738976aae232eb46b8efc15554ffc56deb5d9ebe4" + +[[package]] +name = "cfg-expr" +version = "0.10.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0aacacf4d96c24b2ad6eb8ee6df040e4f27b0d0b39a5710c30091baa830485db" +dependencies = [ + "smallvec", +] + +[[package]] +name = "cfg-if" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" + +[[package]] +name = "chrono" +version = "0.4.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "16b0a3d9ed01224b22057780a37bb8c5dbfe1be8ba48678e7bf57ec4b385411f" +dependencies = [ + "iana-time-zone", + "num-integer", + "num-traits", + "winapi", +] + +[[package]] +name = "codespan-reporting" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3538270d33cc669650c4b093848450d380def10c331d38c768e34cac80576e6e" +dependencies = [ + "termcolor", + "unicode-width", +] + +[[package]] +name = "const-oid" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cec318a675afcb6a1ea1d4340e2d377e56e47c266f28043ceccbf4412ddfdd3b" + +[[package]] +name = "constant_time_eq" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "13418e745008f7349ec7e449155f419a61b92b58a99cc3616942b926825ec76b" + +[[package]] +name = "core-foundation-sys" +version = "0.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5827cebf4670468b8772dd191856768aedcb1b0278a04f989f7766351917b9dc" + +[[package]] +name = "cpp_demangle" +version = "0.3.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eeaa953eaad386a53111e47172c2fedba671e5684c8dd601a5f474f4f118710f" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "cpufeatures" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "28d997bd5e24a5928dd43e46dc529867e207907fe0b239c3477d924f7f2ca320" +dependencies = [ + "libc", +] + +[[package]] +name = "cranelift-entity" +version = "0.93.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7cf583f7b093f291005f9fb1323e2c37f6ee4c7909e39ce016b2e8360d461705" +dependencies = [ + "serde", +] + +[[package]] +name = "crc32fast" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b540bd8bc810d3885c6ea91e2018302f68baba2129ab3e88f32389ee9370880d" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "crunchy" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a81dae078cea95a014a339291cec439d2f232ebe854a9d672b796c6afafa9b7" + +[[package]] +name = "crypto-bigint" +version = "0.4.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ef2b4b23cddf68b89b8f8069890e8c270d54e2d5fe1b143820234805e4cb17ef" +dependencies = [ + "generic-array 0.14.6", + "rand_core 0.6.4", + "subtle", + "zeroize", +] + +[[package]] +name = "crypto-common" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3" +dependencies = [ + "generic-array 0.14.6", + "typenum", +] + +[[package]] +name = "crypto-mac" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b584a330336237c1eecd3e94266efb216c56ed91225d634cb2991c5f3fd1aeab" +dependencies = [ + "generic-array 0.14.6", + "subtle", +] + +[[package]] +name = "crypto-mac" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b1d1a86f49236c215f271d40892d5fc950490551400b02ef360692c29815c714" +dependencies = [ + "generic-array 0.14.6", + "subtle", +] + +[[package]] +name = "curve25519-dalek" +version = "2.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4a9b85542f99a2dfa2a1b8e192662741c9859a846b296bef1c92ef9b58b5a216" +dependencies = [ + "byteorder", + "digest 0.8.1", + "rand_core 0.5.1", + "subtle", + "zeroize", +] + +[[package]] +name = "curve25519-dalek" +version = "3.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b9fdf9972b2bd6af2d913799d9ebc165ea4d2e65878e329d9c6b372c4491b61" +dependencies = [ + "byteorder", + "digest 0.9.0", + "rand_core 0.5.1", + "subtle", + "zeroize", +] + +[[package]] +name = "cxx" +version = "1.0.83" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bdf07d07d6531bfcdbe9b8b739b104610c6508dcc4d63b410585faf338241daf" +dependencies = [ + "cc", + "cxxbridge-flags", + "cxxbridge-macro", + "link-cplusplus", +] + +[[package]] +name = "cxx-build" +version = "1.0.83" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d2eb5b96ecdc99f72657332953d4d9c50135af1bac34277801cc3937906ebd39" +dependencies = [ + "cc", + "codespan-reporting", + "once_cell", + "proc-macro2", + "quote", + "scratch", + "syn", +] + +[[package]] +name = "cxxbridge-flags" +version = "1.0.83" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac040a39517fd1674e0f32177648334b0f4074625b5588a64519804ba0553b12" + +[[package]] +name = "cxxbridge-macro" +version = "1.0.83" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1362b0ddcfc4eb0a1f57b68bd77dd99f0e826958a96abd0ae9bd092e114ffed6" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "der" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f1a467a65c5e759bce6e65eaf91cc29f466cdc57cb65777bd646872a8a1fd4de" +dependencies = [ + "const-oid", + "zeroize", +] + +[[package]] +name = "derive-syn-parse" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e79116f119dd1dba1abf1f3405f03b9b0e79a27a3883864bfebded8a3dc768cd" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "derive_more" +version = "0.99.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4fb810d30a7c1953f91334de7244731fc3f3c10d7fe163338a35b9f640960321" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "digest" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f3d0c8c8752312f9713efd397ff63acb9f85585afbf179282e720e7704954dd5" +dependencies = [ + "generic-array 0.12.4", +] + +[[package]] +name = "digest" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3dd60d1080a57a05ab032377049e0591415d2b31afd7028356dbf3cc6dcb066" +dependencies = [ + "generic-array 0.14.6", +] + +[[package]] +name = "digest" +version = "0.10.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8168378f4e5023e7218c89c891c0fd8ecdb5e5e4f18cb78f38cf245dd021e76f" +dependencies = [ + "block-buffer 0.10.3", + "crypto-common", + "subtle", +] + +[[package]] +name = "downcast-rs" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ea835d29036a4087793836fa931b08837ad5e957da9e23886b29586fb9b6650" + +[[package]] +name = "dyn-clonable" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4e9232f0e607a262ceb9bd5141a3dfb3e4db6994b31989bbfd845878cba59fd4" +dependencies = [ + "dyn-clonable-impl", + "dyn-clone", +] + +[[package]] +name = "dyn-clonable-impl" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "558e40ea573c374cf53507fd240b7ee2f5477df7cfebdb97323ec61c719399c5" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "dyn-clone" +version = "1.0.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4f94fa09c2aeea5b8839e414b7b841bf429fd25b9c522116ac97ee87856d88b2" + +[[package]] +name = "ecdsa" +version = "0.14.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "413301934810f597c1d19ca71c8710e99a3f1ba28a0d2ebc01551a2daeea3c5c" +dependencies = [ + "der", + "elliptic-curve", + "rfc6979", + "signature", +] + +[[package]] +name = "ed25519" +version = "1.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e9c280362032ea4203659fc489832d0204ef09f247a0506f170dafcac08c369" +dependencies = [ + "signature", +] + +[[package]] +name = "ed25519-dalek" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c762bae6dcaf24c4c84667b8579785430908723d5c889f469d76a41d59cc7a9d" +dependencies = [ + "curve25519-dalek 3.2.0", + "ed25519", + "sha2 0.9.9", + "zeroize", +] + +[[package]] +name = "ed25519-zebra" +version = "3.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7c24f403d068ad0b359e577a77f92392118be3f3c927538f2bb544a5ecd828c6" +dependencies = [ + "curve25519-dalek 3.2.0", + "hashbrown 0.12.3", + "hex", + "rand_core 0.6.4", + "sha2 0.9.9", + "zeroize", +] + +[[package]] +name = "either" +version = "1.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "90e5c1c8368803113bf0c9584fc495a58b86dc8a29edbf8fe877d21d9507e797" + +[[package]] +name = "elliptic-curve" +version = "0.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e7bb888ab5300a19b8e5bceef25ac745ad065f3c9f7efc6de1b91958110891d3" +dependencies = [ + "base16ct", + "crypto-bigint", + "der", + "digest 0.10.6", + "ff", + "generic-array 0.14.6", + "group", + "rand_core 0.6.4", + "sec1", + "subtle", + "zeroize", +] + +[[package]] +name = "environmental" +version = "1.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e48c92028aaa870e83d51c64e5d4e0b6981b360c522198c23959f219a4e1b15b" + +[[package]] +name = "errno" +version = "0.2.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f639046355ee4f37944e44f60642c6f3a7efa3cf6b78c78a0d989a8ce6c396a1" +dependencies = [ + "errno-dragonfly", + "libc", + "winapi", +] + +[[package]] +name = "errno-dragonfly" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aa68f1b12764fab894d2755d2518754e71b4fd80ecfb822714a1206c2aab39bf" +dependencies = [ + "cc", + "libc", +] + +[[package]] +name = "expander" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f360349150728553f92e4c997a16af8915f418d3a0f21b440d34c5632f16ed84" +dependencies = [ + "blake2", + "fs-err", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "fake-simd" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e88a8acf291dafb59c2d96e8f59828f3838bb1a70398823ade51a84de6a6deed" + +[[package]] +name = "fallible-iterator" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4443176a9f2c162692bd3d352d745ef9413eec5782a80d8fd6f8a1ac692a07f7" + +[[package]] +name = "ff" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d013fc25338cc558c5c2cfbad646908fb23591e2404481826742b651c9af7160" +dependencies = [ + "rand_core 0.6.4", + "subtle", +] + +[[package]] +name = "fixed-hash" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "835c052cb0c08c1acf6ffd71c022172e18723949c8282f2b9f27efbc51e64534" +dependencies = [ + "byteorder", + "rand 0.8.5", + "rustc-hex", + "static_assertions", +] + +[[package]] +name = "form_urlencoded" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a9c384f161156f5260c24a097c56119f9be8c798586aecc13afbcbe7b7e26bf8" +dependencies = [ + "percent-encoding", +] + +[[package]] +name = "frame-benchmarking" +version = "4.0.0-dev" +dependencies = [ + "frame-support", + "frame-support-procedural", + "frame-system", + "linregress", + "log", + "parity-scale-codec", + "paste", + "scale-info", + "serde", + "sp-api", + "sp-application-crypto", + "sp-core", + "sp-io", + "sp-runtime", + "sp-runtime-interface", + "sp-std", + "sp-storage", + "static_assertions", +] + +[[package]] +name = "frame-election-provider-solution-type" +version = "4.0.0-dev" +dependencies = [ + "proc-macro-crate", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "frame-election-provider-support" +version = "4.0.0-dev" +dependencies = [ + "frame-election-provider-solution-type", + "frame-support", + "frame-system", + "parity-scale-codec", + "scale-info", + "sp-arithmetic", + "sp-core", + "sp-npos-elections", + "sp-runtime", + "sp-std", +] + +[[package]] +name = "frame-metadata" +version = "15.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df6bb8542ef006ef0de09a5c4420787d79823c0ed7924225822362fd2bf2ff2d" +dependencies = [ + "cfg-if", + "parity-scale-codec", + "scale-info", + "serde", +] + +[[package]] +name = "frame-support" +version = "4.0.0-dev" +dependencies = [ + "bitflags", + "environmental", + "frame-metadata", + "frame-support-procedural", + "impl-trait-for-tuples", + "k256", + "log", + "once_cell", + "parity-scale-codec", + "paste", + "scale-info", + "serde", + "smallvec", + "sp-api", + "sp-arithmetic", + "sp-core", + "sp-core-hashing-proc-macro", + "sp-inherents", + "sp-io", + "sp-runtime", + "sp-staking", + "sp-state-machine", + "sp-std", + "sp-tracing", + "sp-weights", + "tt-call", +] + +[[package]] +name = "frame-support-procedural" +version = "4.0.0-dev" +dependencies = [ + "Inflector", + "cfg-expr", + "derive-syn-parse", + "frame-support-procedural-tools", + "itertools", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "frame-support-procedural-tools" +version = "4.0.0-dev" +dependencies = [ + "frame-support-procedural-tools-derive", + "proc-macro-crate", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "frame-support-procedural-tools-derive" +version = "3.0.0" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "frame-system" +version = "4.0.0-dev" +dependencies = [ + "frame-support", + "log", + "parity-scale-codec", + "scale-info", + "serde", + "sp-core", + "sp-io", + "sp-runtime", + "sp-std", + "sp-version", + "sp-weights", +] + +[[package]] +name = "fs-err" +version = "2.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0845fa252299212f0389d64ba26f34fa32cfe41588355f21ed507c59a0f64541" + +[[package]] +name = "funty" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6d5a32815ae3f33302d95fdcb2ce17862f8c65363dcfd29360480ba1001fc9c" + +[[package]] +name = "futures" +version = "0.3.25" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "38390104763dc37a5145a53c29c63c1290b5d316d6086ec32c293f6736051bb0" +dependencies = [ + "futures-channel", + "futures-core", + "futures-executor", + "futures-io", + "futures-sink", + "futures-task", + "futures-util", +] + +[[package]] +name = "futures-channel" +version = "0.3.25" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "52ba265a92256105f45b719605a571ffe2d1f0fea3807304b522c1d778f79eed" +dependencies = [ + "futures-core", + "futures-sink", +] + +[[package]] +name = "futures-core" +version = "0.3.25" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "04909a7a7e4633ae6c4a9ab280aeb86da1236243a77b694a49eacd659a4bd3ac" + +[[package]] +name = "futures-executor" +version = "0.3.25" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7acc85df6714c176ab5edf386123fafe217be88c0840ec11f199441134a074e2" +dependencies = [ + "futures-core", + "futures-task", + "futures-util", + "num_cpus", +] + +[[package]] +name = "futures-io" +version = "0.3.25" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "00f5fb52a06bdcadeb54e8d3671f8888a39697dcb0b81b23b55174030427f4eb" + +[[package]] +name = "futures-macro" +version = "0.3.25" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bdfb8ce053d86b91919aad980c220b1fb8401a9394410e1c289ed7e66b61835d" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "futures-sink" +version = "0.3.25" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "39c15cf1a4aa79df40f1bb462fb39676d0ad9e366c2a33b590d7c66f4f81fcf9" + +[[package]] +name = "futures-task" +version = "0.3.25" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2ffb393ac5d9a6eaa9d3fdf37ae2776656b706e200c8e16b1bdb227f5198e6ea" + +[[package]] +name = "futures-timer" +version = "3.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e64b03909df88034c26dc1547e8970b91f98bdb65165d6a4e9110d94263dbb2c" + +[[package]] +name = "futures-util" +version = "0.3.25" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "197676987abd2f9cadff84926f410af1c183608d36641465df73ae8211dc65d6" +dependencies = [ + "futures-channel", + "futures-core", + "futures-io", + "futures-macro", + "futures-sink", + "futures-task", + "memchr", + "pin-project-lite", + "pin-utils", + "slab", +] + +[[package]] +name = "generic-array" +version = "0.12.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ffdf9f34f1447443d37393cc6c2b8313aebddcd96906caf34e54c68d8e57d7bd" +dependencies = [ + "typenum", +] + +[[package]] +name = "generic-array" +version = "0.14.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bff49e947297f3312447abdca79f45f4738097cc82b06e72054d2223f601f1b9" +dependencies = [ + "typenum", + "version_check", +] + +[[package]] +name = "getrandom" +version = "0.1.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8fc3cb4d91f53b50155bdcfd23f6a4c39ae1969c2ae85982b135750cccaf5fce" +dependencies = [ + "cfg-if", + "libc", + "wasi 0.9.0+wasi-snapshot-preview1", +] + +[[package]] +name = "getrandom" +version = "0.2.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c05aeb6a22b8f62540c194aac980f2115af067bfe15a0734d7277a768d396b31" +dependencies = [ + "cfg-if", + "libc", + "wasi 0.11.0+wasi-snapshot-preview1", +] + +[[package]] +name = "gimli" +version = "0.26.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "22030e2c5a68ec659fde1e949a745124b48e6fa8b045b7ed5bd1fe4ccc5c4e5d" +dependencies = [ + "fallible-iterator", + "stable_deref_trait", +] + +[[package]] +name = "group" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5dfbfb3a6cfbd390d5c9564ab283a0349b9b9fcd46a706c1eb10e0db70bfbac7" +dependencies = [ + "ff", + "rand_core 0.6.4", + "subtle", +] + +[[package]] +name = "hash-db" +version = "0.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e7d7786361d7425ae2fe4f9e407eb0efaa0840f5212d109cc018c40c35c6ab4" + +[[package]] +name = "hash256-std-hasher" +version = "0.15.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "92c171d55b98633f4ed3860808f004099b36c1cc29c42cfc53aa8591b21efcf2" +dependencies = [ + "crunchy", +] + +[[package]] +name = "hashbrown" +version = "0.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" +dependencies = [ + "ahash 0.7.6", +] + +[[package]] +name = "hashbrown" +version = "0.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "43a3c133739dddd0d2990f9a4bdf8eb4b21ef50e4851ca85ab661199821d510e" +dependencies = [ + "ahash 0.8.3", +] + +[[package]] +name = "heck" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2540771e65fc8cb83cd6e8a237f70c319bd5c29f78ed1084ba5d50eeac86f7f9" + +[[package]] +name = "hermit-abi" +version = "0.1.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "62b467343b94ba476dcb2500d242dadbb39557df889310ac77c5d99100aaac33" +dependencies = [ + "libc", +] + +[[package]] +name = "hex" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" + +[[package]] +name = "hmac" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "126888268dcc288495a26bf004b38c5fdbb31682f992c84ceb046a1f0fe38840" +dependencies = [ + "crypto-mac 0.8.0", + "digest 0.9.0", +] + +[[package]] +name = "hmac" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2a2a2320eb7ec0ebe8da8f744d7812d9fc4cb4d09344ac01898dbcb6a20ae69b" +dependencies = [ + "crypto-mac 0.11.1", + "digest 0.9.0", +] + +[[package]] +name = "hmac" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c49c37c09c17a53d937dfbb742eb3a961d65a994e6bcdcf37e7399d0cc8ab5e" +dependencies = [ + "digest 0.10.6", +] + +[[package]] +name = "hmac-drbg" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "17ea0a1394df5b6574da6e0c1ade9e78868c9fb0a4e5ef4428e32da4676b85b1" +dependencies = [ + "digest 0.9.0", + "generic-array 0.14.6", + "hmac 0.8.1", +] + +[[package]] +name = "iana-time-zone" +version = "0.1.53" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "64c122667b287044802d6ce17ee2ddf13207ed924c712de9a66a5814d5b64765" +dependencies = [ + "android_system_properties", + "core-foundation-sys", + "iana-time-zone-haiku", + "js-sys", + "wasm-bindgen", + "winapi", +] + +[[package]] +name = "iana-time-zone-haiku" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0703ae284fc167426161c2e3f1da3ea71d94b21bedbcc9494e92b28e334e3dca" +dependencies = [ + "cxx", + "cxx-build", +] + +[[package]] +name = "idna" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e14ddfc70884202db2244c223200c204c2bda1bc6e0998d11b5e024d657209e6" +dependencies = [ + "unicode-bidi", + "unicode-normalization", +] + +[[package]] +name = "impl-codec" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba6a270039626615617f3f36d15fc827041df3b78c439da2cadfa47455a77f2f" +dependencies = [ + "parity-scale-codec", +] + +[[package]] +name = "impl-serde" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ebc88fc67028ae3db0c853baa36269d398d5f45b6982f95549ff5def78c935cd" +dependencies = [ + "serde", +] + +[[package]] +name = "impl-trait-for-tuples" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "11d7a9f6330b71fea57921c9b61c47ee6e84f72d394754eff6163ae67e7395eb" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "indexmap" +version = "1.9.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1885e79c1fc4b10f0e172c475f458b7f7b93061064d98c3293e98c5ba0c8b399" +dependencies = [ + "autocfg", + "hashbrown 0.12.3", + "serde", +] + +[[package]] +name = "integer-sqrt" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "276ec31bcb4a9ee45f58bec6f9ec700ae4cf4f4f8f2fa7e06cb406bd5ffdd770" +dependencies = [ + "num-traits", +] + +[[package]] +name = "io-lifetimes" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cfa919a82ea574332e2de6e74b4c36e74d41982b335080fa59d4ef31be20fdf3" +dependencies = [ + "libc", + "windows-sys 0.45.0", +] + +[[package]] +name = "itertools" +version = "0.10.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b0fd2260e829bddf4cb6ea802289de2f86d6a7a690192fbe91b3f46e0f2c8473" +dependencies = [ + "either", +] + +[[package]] +name = "itoa" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4217ad341ebadf8d8e724e264f13e593e0648f5b3e94b3896a5df283be015ecc" + +[[package]] +name = "js-sys" +version = "0.3.60" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49409df3e3bf0856b916e2ceaca09ee28e6871cf7d9ce97a692cacfdb2a25a47" +dependencies = [ + "wasm-bindgen", +] + +[[package]] +name = "k256" +version = "0.11.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72c1e0b51e7ec0a97369623508396067a486bd0cbed95a2659a4b863d28cfc8b" +dependencies = [ + "cfg-if", + "ecdsa", + "elliptic-curve", + "sha2 0.10.6", +] + +[[package]] +name = "keccak" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3afef3b6eff9ce9d8ff9b3601125eec7f0c8cbac7abd14f355d053fa56c98768" +dependencies = [ + "cpufeatures", +] + +[[package]] +name = "lazy_static" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" + +[[package]] +name = "libc" +version = "0.2.138" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "db6d7e329c562c5dfab7a46a2afabc8b987ab9a4834c9d1ca04dc54c1546cef8" + +[[package]] +name = "libm" +version = "0.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "348108ab3fba42ec82ff6e9564fc4ca0247bdccdc68dd8af9764bbc79c3c8ffb" + +[[package]] +name = "libsecp256k1" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "95b09eff1b35ed3b33b877ced3a691fc7a481919c7e29c53c906226fcf55e2a1" +dependencies = [ + "arrayref", + "base64", + "digest 0.9.0", + "hmac-drbg", + "libsecp256k1-core", + "libsecp256k1-gen-ecmult", + "libsecp256k1-gen-genmult", + "rand 0.8.5", + "serde", + "sha2 0.9.9", + "typenum", +] + +[[package]] +name = "libsecp256k1-core" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5be9b9bb642d8522a44d533eab56c16c738301965504753b03ad1de3425d5451" +dependencies = [ + "crunchy", + "digest 0.9.0", + "subtle", +] + +[[package]] +name = "libsecp256k1-gen-ecmult" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3038c808c55c87e8a172643a7d87187fc6c4174468159cb3090659d55bcb4809" +dependencies = [ + "libsecp256k1-core", +] + +[[package]] +name = "libsecp256k1-gen-genmult" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3db8d6ba2cec9eacc40e6e8ccc98931840301f1006e95647ceb2dd5c3aa06f7c" +dependencies = [ + "libsecp256k1-core", +] + +[[package]] +name = "link-cplusplus" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9272ab7b96c9046fbc5bc56c06c117cb639fe2d509df0c421cad82d2915cf369" +dependencies = [ + "cc", +] + +[[package]] +name = "linregress" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "475015a7f8f017edb28d2e69813be23500ad4b32cfe3421c4148efc97324ee52" +dependencies = [ + "nalgebra", +] + +[[package]] +name = "linux-raw-sys" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f051f77a7c8e6957c0696eac88f26b0117e54f52d3fc682ab19397a8812846a4" + +[[package]] +name = "lock_api" +version = "0.4.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "435011366fe56583b16cf956f9df0095b405b82d76425bc8981c0e22e60ec4df" +dependencies = [ + "autocfg", + "scopeguard", +] + +[[package]] +name = "log" +version = "0.4.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "abb12e687cfb44aa40f41fc3978ef76448f9b6038cad6aef4259d3c095a2382e" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "mach" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b823e83b2affd8f40a9ee8c29dbc56404c1e34cd2710921f2801e2cf29527afa" +dependencies = [ + "libc", +] + +[[package]] +name = "matchers" +version = "0.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f099785f7595cc4b4553a174ce30dd7589ef93391ff414dbb67f62392b9e0ce1" +dependencies = [ + "regex-automata", +] + +[[package]] +name = "matrixmultiply" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "add85d4dd35074e6fedc608f8c8f513a3548619a9024b751949ef0e8e45a4d84" +dependencies = [ + "rawpointer", +] + +[[package]] +name = "memchr" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d" + +[[package]] +name = "memfd" +version = "0.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b20a59d985586e4a5aef64564ac77299f8586d8be6cf9106a5a40207e8908efb" +dependencies = [ + "rustix", +] + +[[package]] +name = "memoffset" +version = "0.6.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5aa361d4faea93603064a027415f07bd8e1d5c88c9fbf68bf56a285428fd79ce" +dependencies = [ + "autocfg", +] + +[[package]] +name = "memory-db" +version = "0.32.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "808b50db46293432a45e63bc15ea51e0ab4c0a1647b8eb114e31a3e698dd6fbe" +dependencies = [ + "hash-db", +] + +[[package]] +name = "memory_units" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8452105ba047068f40ff7093dd1d9da90898e63dd61736462e9cdda6a90ad3c3" + +[[package]] +name = "merlin" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4e261cf0f8b3c42ded9f7d2bb59dea03aa52bc8a1cbc7482f9fc3fd1229d3b42" +dependencies = [ + "byteorder", + "keccak", + "rand_core 0.5.1", + "zeroize", +] + +[[package]] +name = "miniz_oxide" +version = "0.5.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "96590ba8f175222643a85693f33d26e9c8a015f599c216509b1a6894af675d34" +dependencies = [ + "adler", +] + +[[package]] +name = "nalgebra" +version = "0.32.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d68d47bba83f9e2006d117a9a33af1524e655516b8919caac694427a6fb1e511" +dependencies = [ + "approx", + "matrixmultiply", + "nalgebra-macros", + "num-complex", + "num-rational", + "num-traits", + "simba", + "typenum", +] + +[[package]] +name = "nalgebra-macros" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d232c68884c0c99810a5a4d333ef7e47689cfd0edc85efc9e54e1e6bf5212766" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "nohash-hasher" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2bf50223579dc7cdcfb3bfcacf7069ff68243f8c363f62ffa99cf000a6b9c451" + +[[package]] +name = "num-bigint" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f93ab6289c7b344a8a9f60f88d80aa20032336fe78da341afc91c8a2341fc75f" +dependencies = [ + "autocfg", + "num-integer", + "num-traits", +] + +[[package]] +name = "num-complex" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7ae39348c8bc5fbd7f40c727a9925f03517afd2ab27d46702108b6a7e5414c19" +dependencies = [ + "num-traits", +] + +[[package]] +name = "num-format" +version = "0.4.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a652d9771a63711fd3c3deb670acfbe5c30a4072e664d7a3bf5a9e1056ac72c3" +dependencies = [ + "arrayvec 0.7.2", + "itoa", +] + +[[package]] +name = "num-integer" +version = "0.1.45" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "225d3389fb3509a24c93f5c29eb6bde2586b98d9f016636dff58d7c6f7569cd9" +dependencies = [ + "autocfg", + "num-traits", +] + +[[package]] +name = "num-rational" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0638a1c9d0a3c0914158145bc76cff373a75a627e6ecbfb71cbe6f453a5a19b0" +dependencies = [ + "autocfg", + "num-bigint", + "num-integer", + "num-traits", +] + +[[package]] +name = "num-traits" +version = "0.2.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "578ede34cf02f8924ab9447f50c28075b4d3e5b269972345e7e0372b38c6cdcd" +dependencies = [ + "autocfg", +] + +[[package]] +name = "num_cpus" +version = "1.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f6058e64324c71e02bc2b150e4f3bc8286db6c83092132ffa3f6b1eab0f9def5" +dependencies = [ + "hermit-abi", + "libc", +] + +[[package]] +name = "object" +version = "0.29.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "21158b2c33aa6d4561f1c0a6ea283ca92bc54802a93b263e910746d679a7eb53" +dependencies = [ + "crc32fast", + "hashbrown 0.12.3", + "indexmap", + "memchr", +] + +[[package]] +name = "once_cell" +version = "1.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "86f0b0d4bf799edbc74508c1e8bf170ff5f41238e5f8225603ca7caaae2b7860" + +[[package]] +name = "opaque-debug" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2839e79665f131bdb5782e51f2c6c9599c133c6098982a54c794358bf432529c" + +[[package]] +name = "opaque-debug" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "624a8340c38c1b80fd549087862da4ba43e08858af025b236e509b6649fc13d5" + +[[package]] +name = "pallet-authorship" +version = "4.0.0-dev" +dependencies = [ + "frame-support", + "frame-system", + "impl-trait-for-tuples", + "parity-scale-codec", + "scale-info", + "sp-runtime", + "sp-std", +] + +[[package]] +name = "pallet-bags-list" +version = "4.0.0-dev" +dependencies = [ + "frame-benchmarking", + "frame-election-provider-support", + "frame-support", + "frame-system", + "log", + "pallet-balances", + "parity-scale-codec", + "scale-info", + "sp-core", + "sp-io", + "sp-runtime", + "sp-std", + "sp-tracing", +] + +[[package]] +name = "pallet-balances" +version = "4.0.0-dev" +dependencies = [ + "frame-benchmarking", + "frame-support", + "frame-system", + "log", + "parity-scale-codec", + "scale-info", + "sp-runtime", + "sp-std", +] + +[[package]] +name = "pallet-election-provider-e2e-test" +version = "1.0.0" +dependencies = [ + "frame-election-provider-support", + "frame-support", + "frame-system", + "log", + "pallet-bags-list", + "pallet-balances", + "pallet-election-provider-multi-phase", + "pallet-session", + "pallet-staking", + "pallet-timestamp", + "parity-scale-codec", + "scale-info", + "sp-core", + "sp-io", + "sp-npos-elections", + "sp-runtime", + "sp-staking", + "sp-std", + "sp-tracing", +] + +[[package]] +name = "pallet-election-provider-multi-phase" +version = "4.0.0-dev" +dependencies = [ + "frame-benchmarking", + "frame-election-provider-support", + "frame-support", + "frame-system", + "log", + "pallet-election-provider-support-benchmarking", + "parity-scale-codec", + "rand 0.8.5", + "scale-info", + "sp-arithmetic", + "sp-core", + "sp-io", + "sp-npos-elections", + "sp-runtime", + "sp-std", + "strum", +] + +[[package]] +name = "pallet-election-provider-support-benchmarking" +version = "4.0.0-dev" +dependencies = [ + "frame-benchmarking", + "frame-election-provider-support", + "frame-system", + "parity-scale-codec", + "sp-npos-elections", + "sp-runtime", +] + +[[package]] +name = "pallet-session" +version = "4.0.0-dev" +dependencies = [ + "frame-support", + "frame-system", + "impl-trait-for-tuples", + "log", + "pallet-timestamp", + "parity-scale-codec", + "scale-info", + "sp-core", + "sp-io", + "sp-runtime", + "sp-session", + "sp-staking", + "sp-std", + "sp-trie", +] + +[[package]] +name = "pallet-staking" +version = "4.0.0-dev" +dependencies = [ + "frame-benchmarking", + "frame-election-provider-support", + "frame-support", + "frame-system", + "log", + "pallet-authorship", + "pallet-session", + "parity-scale-codec", + "scale-info", + "serde", + "sp-application-crypto", + "sp-io", + "sp-runtime", + "sp-staking", + "sp-std", +] + +[[package]] +name = "pallet-timestamp" +version = "4.0.0-dev" +dependencies = [ + "frame-benchmarking", + "frame-support", + "frame-system", + "log", + "parity-scale-codec", + "scale-info", + "sp-inherents", + "sp-io", + "sp-runtime", + "sp-std", + "sp-timestamp", +] + +[[package]] +name = "parity-scale-codec" +version = "3.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "637935964ff85a605d114591d4d2c13c5d1ba2806dae97cea6bf180238a749ac" +dependencies = [ + "arrayvec 0.7.2", + "bitvec", + "byte-slice-cast", + "bytes", + "impl-trait-for-tuples", + "parity-scale-codec-derive", + "serde", +] + +[[package]] +name = "parity-scale-codec-derive" +version = "3.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "86b26a931f824dd4eca30b3e43bb4f31cd5f0d3a403c5f5ff27106b805bfde7b" +dependencies = [ + "proc-macro-crate", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "parity-wasm" +version = "0.45.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e1ad0aff30c1da14b1254fcb2af73e1fa9a28670e584a626f53a369d0e157304" + +[[package]] +name = "parking_lot" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3742b2c103b9f06bc9fff0a37ff4912935851bee6d36f3c02bcc755bcfec228f" +dependencies = [ + "lock_api", + "parking_lot_core", +] + +[[package]] +name = "parking_lot_core" +version = "0.9.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7ff9f3fef3968a3ec5945535ed654cb38ff72d7495a25619e2247fb15a2ed9ba" +dependencies = [ + "cfg-if", + "libc", + "redox_syscall", + "smallvec", + "windows-sys 0.42.0", +] + +[[package]] +name = "paste" +version = "1.0.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cf1c2c742266c2f1041c914ba65355a83ae8747b05f208319784083583494b4b" + +[[package]] +name = "pbkdf2" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d95f5254224e617595d2cc3cc73ff0a5eaf2637519e25f03388154e9378b6ffa" +dependencies = [ + "crypto-mac 0.11.1", +] + +[[package]] +name = "pbkdf2" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "83a0692ec44e4cf1ef28ca317f14f8f07da2d95ec3fa01f86e4467b725e60917" +dependencies = [ + "digest 0.10.6", +] + +[[package]] +name = "percent-encoding" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "478c572c3d73181ff3c2539045f6eb99e5491218eae919370993b890cdbdd98e" + +[[package]] +name = "pin-project-lite" +version = "0.2.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e0a7ae3ac2f1173085d398531c705756c94a4c56843785df85a60c1a0afac116" + +[[package]] +name = "pin-utils" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" + +[[package]] +name = "pkcs8" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9eca2c590a5f85da82668fa685c09ce2888b9430e83299debf1f34b65fd4a4ba" +dependencies = [ + "der", + "spki", +] + +[[package]] +name = "ppv-lite86" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de" + +[[package]] +name = "primitive-types" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9f3486ccba82358b11a77516035647c34ba167dfa53312630de83b12bd4f3d66" +dependencies = [ + "fixed-hash", + "impl-codec", + "impl-serde", + "scale-info", + "uint", +] + +[[package]] +name = "proc-macro-crate" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eda0fc3b0fb7c975631757e14d9049da17374063edb6ebbcbc54d880d4fe94e9" +dependencies = [ + "once_cell", + "thiserror", + "toml", +] + +[[package]] +name = "proc-macro2" +version = "1.0.47" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5ea3d908b0e36316caf9e9e2c4625cdde190a7e6f440d794667ed17a1855e725" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "psm" +version = "0.1.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5787f7cda34e3033a72192c018bc5883100330f362ef279a8cbccfce8bb4e874" +dependencies = [ + "cc", +] + +[[package]] +name = "quote" +version = "1.0.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bbe448f377a7d6961e30f5955f9b8d106c3f5e449d493ee1b125c1d43c2b5179" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "radium" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc33ff2d4973d518d823d61aa239014831e521c75da58e3df4840d3f47749d09" + +[[package]] +name = "rand" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6a6b1679d49b24bbfe0c803429aa1874472f50d9b363131f0e89fc356b544d03" +dependencies = [ + "getrandom 0.1.16", + "libc", + "rand_chacha 0.2.2", + "rand_core 0.5.1", + "rand_hc", +] + +[[package]] +name = "rand" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" +dependencies = [ + "libc", + "rand_chacha 0.3.1", + "rand_core 0.6.4", +] + +[[package]] +name = "rand_chacha" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f4c8ed856279c9737206bf725bf36935d8666ead7aa69b52be55af369d193402" +dependencies = [ + "ppv-lite86", + "rand_core 0.5.1", +] + +[[package]] +name = "rand_chacha" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" +dependencies = [ + "ppv-lite86", + "rand_core 0.6.4", +] + +[[package]] +name = "rand_core" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "90bde5296fc891b0cef12a6d03ddccc162ce7b2aff54160af9338f8d40df6d19" +dependencies = [ + "getrandom 0.1.16", +] + +[[package]] +name = "rand_core" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" +dependencies = [ + "getrandom 0.2.8", +] + +[[package]] +name = "rand_hc" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ca3129af7b92a17112d59ad498c6f81eaf463253766b90396d39ea7a39d6613c" +dependencies = [ + "rand_core 0.5.1", +] + +[[package]] +name = "rawpointer" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "60a357793950651c4ed0f3f52338f53b2f809f32d83a07f72909fa13e4c6c1e3" + +[[package]] +name = "redox_syscall" +version = "0.2.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fb5a58c1855b4b6819d59012155603f0b22ad30cad752600aadfcb695265519a" +dependencies = [ + "bitflags", +] + +[[package]] +name = "ref-cast" +version = "1.0.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "53b15debb4f9d60d767cd8ca9ef7abb2452922f3214671ff052defc7f3502c44" +dependencies = [ + "ref-cast-impl", +] + +[[package]] +name = "ref-cast-impl" +version = "1.0.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "abfa8511e9e94fd3de6585a3d3cd00e01ed556dc9814829280af0e8dc72a8f36" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "regex" +version = "1.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e076559ef8e241f2ae3479e36f97bd5741c0330689e217ad51ce2c76808b868a" +dependencies = [ + "aho-corasick", + "memchr", + "regex-syntax", +] + +[[package]] +name = "regex-automata" +version = "0.1.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c230d73fb8d8c1b9c0b3135c5142a8acee3a0558fb8db5cf1cb65f8d7862132" +dependencies = [ + "regex-syntax", +] + +[[package]] +name = "regex-syntax" +version = "0.6.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "456c603be3e8d448b072f410900c09faf164fbce2d480456f50eea6e25f9c848" + +[[package]] +name = "rfc6979" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7743f17af12fa0b03b803ba12cd6a8d9483a587e89c69445e3909655c0b9fabb" +dependencies = [ + "crypto-bigint", + "hmac 0.12.1", + "zeroize", +] + +[[package]] +name = "rustc-demangle" +version = "0.1.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7ef03e0a2b150c7a90d01faf6254c9c48a41e95fb2a8c2ac1c6f0d2b9aefc342" + +[[package]] +name = "rustc-hash" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2" + +[[package]] +name = "rustc-hex" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3e75f6a532d0fd9f7f13144f392b6ad56a32696bfcd9c78f797f16bbb6f072d6" + +[[package]] +name = "rustix" +version = "0.36.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fd5c6ff11fecd55b40746d1995a02f2eb375bf8c00d192d521ee09f42bef37bc" +dependencies = [ + "bitflags", + "errno", + "io-lifetimes", + "libc", + "linux-raw-sys", + "windows-sys 0.45.0", +] + +[[package]] +name = "rustversion" +version = "1.0.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "97477e48b4cf8603ad5f7aaf897467cf42ab4218a38ef76fb14c2d6773a6d6a8" + +[[package]] +name = "ryu" +version = "1.0.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4501abdff3ae82a1c1b477a17252eb69cee9e66eb915c1abaa4f44d873df9f09" + +[[package]] +name = "safe_arch" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "794821e4ccb0d9f979512f9c1973480123f9bd62a90d74ab0f9426fcf8f4a529" +dependencies = [ + "bytemuck", +] + +[[package]] +name = "scale-info" +version = "2.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "001cf62ece89779fd16105b5f515ad0e5cedcd5440d3dd806bb067978e7c3608" +dependencies = [ + "bitvec", + "cfg-if", + "derive_more", + "parity-scale-codec", + "scale-info-derive", + "serde", +] + +[[package]] +name = "scale-info-derive" +version = "2.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "303959cf613a6f6efd19ed4b4ad5bf79966a13352716299ad532cfb115f4205c" +dependencies = [ + "proc-macro-crate", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "schnellru" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "772575a524feeb803e5b0fcbc6dd9f367e579488197c94c6e4023aad2305774d" +dependencies = [ + "ahash 0.8.3", + "cfg-if", + "hashbrown 0.13.2", +] + +[[package]] +name = "schnorrkel" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "021b403afe70d81eea68f6ea12f6b3c9588e5d536a94c3bf80f15e7faa267862" +dependencies = [ + "arrayref", + "arrayvec 0.5.2", + "curve25519-dalek 2.1.3", + "getrandom 0.1.16", + "merlin", + "rand 0.7.3", + "rand_core 0.5.1", + "sha2 0.8.2", + "subtle", + "zeroize", +] + +[[package]] +name = "scopeguard" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd" + +[[package]] +name = "scratch" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c8132065adcfd6e02db789d9285a0deb2f3fcb04002865ab67d5fb103533898" + +[[package]] +name = "sec1" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3be24c1842290c45df0a7bf069e0c268a747ad05a192f2fd7dcfdbc1cba40928" +dependencies = [ + "base16ct", + "der", + "generic-array 0.14.6", + "pkcs8", + "subtle", + "zeroize", +] + +[[package]] +name = "secp256k1" +version = "0.24.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d9512ffd81e3a3503ed401f79c33168b9148c75038956039166cd750eaa037c3" +dependencies = [ + "secp256k1-sys", +] + +[[package]] +name = "secp256k1-sys" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "83080e2c2fc1006e625be82e5d1eb6a43b7fd9578b617fcc55814daf286bba4b" +dependencies = [ + "cc", +] + +[[package]] +name = "secrecy" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9bd1c54ea06cfd2f6b63219704de0b9b4f72dcc2b8fdef820be6cd799780e91e" +dependencies = [ + "zeroize", +] + +[[package]] +name = "serde" +version = "1.0.150" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e326c9ec8042f1b5da33252c8a37e9ffbd2c9bef0155215b6e6c80c790e05f91" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_derive" +version = "1.0.150" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "42a3df25b0713732468deadad63ab9da1f1fd75a48a15024b50363f128db627e" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "serde_json" +version = "1.0.89" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "020ff22c755c2ed3f8cf162dbb41a7268d934702f3ed3631656ea597e08fc3db" +dependencies = [ + "itoa", + "ryu", + "serde", +] + +[[package]] +name = "sha2" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a256f46ea78a0c0d9ff00077504903ac881a1dafdc20da66545699e7776b3e69" +dependencies = [ + "block-buffer 0.7.3", + "digest 0.8.1", + "fake-simd", + "opaque-debug 0.2.3", +] + +[[package]] +name = "sha2" +version = "0.9.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4d58a1e1bf39749807d89cf2d98ac2dfa0ff1cb3faa38fbb64dd88ac8013d800" +dependencies = [ + "block-buffer 0.9.0", + "cfg-if", + "cpufeatures", + "digest 0.9.0", + "opaque-debug 0.3.0", +] + +[[package]] +name = "sha2" +version = "0.10.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "82e6b795fe2e3b1e845bafcb27aa35405c4d47cdfc92af5fc8d3002f76cebdc0" +dependencies = [ + "cfg-if", + "cpufeatures", + "digest 0.10.6", +] + +[[package]] +name = "sha3" +version = "0.10.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bdf0c33fae925bdc080598b84bc15c55e7b9a4a43b3c704da051f977469691c9" +dependencies = [ + "digest 0.10.6", + "keccak", +] + +[[package]] +name = "sharded-slab" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "900fba806f70c630b0a382d0d825e17a0f19fcd059a2ade1ff237bcddf446b31" +dependencies = [ + "lazy_static", +] + +[[package]] +name = "signature" +version = "1.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "74233d3b3b2f6d4b006dc19dee745e73e2a6bfb6f93607cd3b02bd5b00797d7c" +dependencies = [ + "digest 0.10.6", + "rand_core 0.6.4", +] + +[[package]] +name = "simba" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "50582927ed6f77e4ac020c057f37a268fc6aebc29225050365aacbb9deeeddc4" +dependencies = [ + "approx", + "num-complex", + "num-traits", + "paste", + "wide", +] + +[[package]] +name = "slab" +version = "0.4.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4614a76b2a8be0058caa9dbbaf66d988527d86d003c11a94fbd335d7661edcef" +dependencies = [ + "autocfg", +] + +[[package]] +name = "smallvec" +version = "1.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a507befe795404456341dfab10cef66ead4c041f62b8b11bbb92bffe5d0953e0" + +[[package]] +name = "sp-api" +version = "4.0.0-dev" +dependencies = [ + "hash-db", + "log", + "parity-scale-codec", + "sp-api-proc-macro", + "sp-core", + "sp-runtime", + "sp-state-machine", + "sp-std", + "sp-trie", + "sp-version", + "thiserror", +] + +[[package]] +name = "sp-api-proc-macro" +version = "4.0.0-dev" +dependencies = [ + "Inflector", + "blake2", + "expander", + "proc-macro-crate", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "sp-application-crypto" +version = "7.0.0" +dependencies = [ + "parity-scale-codec", + "scale-info", + "serde", + "sp-core", + "sp-io", + "sp-std", +] + +[[package]] +name = "sp-arithmetic" +version = "6.0.0" +dependencies = [ + "integer-sqrt", + "num-traits", + "parity-scale-codec", + "scale-info", + "serde", + "sp-std", + "static_assertions", +] + +[[package]] +name = "sp-core" +version = "7.0.0" +dependencies = [ + "array-bytes", + "base58", + "bitflags", + "blake2", + "bounded-collections", + "dyn-clonable", + "ed25519-zebra", + "futures", + "hash-db", + "hash256-std-hasher", + "impl-serde", + "lazy_static", + "libsecp256k1", + "log", + "merlin", + "parity-scale-codec", + "parking_lot", + "primitive-types", + "rand 0.8.5", + "regex", + "scale-info", + "schnorrkel", + "secp256k1", + "secrecy", + "serde", + "sp-core-hashing", + "sp-debug-derive", + "sp-externalities", + "sp-runtime-interface", + "sp-std", + "sp-storage", + "ss58-registry", + "substrate-bip39", + "thiserror", + "tiny-bip39", + "zeroize", +] + +[[package]] +name = "sp-core-hashing" +version = "5.0.0" +dependencies = [ + "blake2b_simd", + "byteorder", + "digest 0.10.6", + "sha2 0.10.6", + "sha3", + "sp-std", + "twox-hash", +] + +[[package]] +name = "sp-core-hashing-proc-macro" +version = "5.0.0" +dependencies = [ + "proc-macro2", + "quote", + "sp-core-hashing", + "syn", +] + +[[package]] +name = "sp-debug-derive" +version = "5.0.0" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "sp-externalities" +version = "0.13.0" +dependencies = [ + "environmental", + "parity-scale-codec", + "sp-std", + "sp-storage", +] + +[[package]] +name = "sp-inherents" +version = "4.0.0-dev" +dependencies = [ + "async-trait", + "impl-trait-for-tuples", + "parity-scale-codec", + "scale-info", + "sp-core", + "sp-runtime", + "sp-std", + "thiserror", +] + +[[package]] +name = "sp-io" +version = "7.0.0" +dependencies = [ + "bytes", + "ed25519", + "ed25519-dalek", + "futures", + "libsecp256k1", + "log", + "parity-scale-codec", + "secp256k1", + "sp-core", + "sp-externalities", + "sp-keystore", + "sp-runtime-interface", + "sp-state-machine", + "sp-std", + "sp-tracing", + "sp-trie", + "tracing", + "tracing-core", +] + +[[package]] +name = "sp-keystore" +version = "0.13.0" +dependencies = [ + "async-trait", + "futures", + "merlin", + "parity-scale-codec", + "parking_lot", + "schnorrkel", + "sp-core", + "sp-externalities", + "thiserror", +] + +[[package]] +name = "sp-npos-elections" +version = "4.0.0-dev" +dependencies = [ + "parity-scale-codec", + "scale-info", + "serde", + "sp-arithmetic", + "sp-core", + "sp-runtime", + "sp-std", +] + +[[package]] +name = "sp-panic-handler" +version = "5.0.0" +dependencies = [ + "backtrace", + "lazy_static", + "regex", +] + +[[package]] +name = "sp-runtime" +version = "7.0.0" +dependencies = [ + "either", + "hash256-std-hasher", + "impl-trait-for-tuples", + "log", + "parity-scale-codec", + "paste", + "rand 0.8.5", + "scale-info", + "serde", + "sp-application-crypto", + "sp-arithmetic", + "sp-core", + "sp-io", + "sp-std", + "sp-weights", +] + +[[package]] +name = "sp-runtime-interface" +version = "7.0.0" +dependencies = [ + "bytes", + "impl-trait-for-tuples", + "parity-scale-codec", + "primitive-types", + "sp-externalities", + "sp-runtime-interface-proc-macro", + "sp-std", + "sp-storage", + "sp-tracing", + "sp-wasm-interface", + "static_assertions", +] + +[[package]] +name = "sp-runtime-interface-proc-macro" +version = "6.0.0" +dependencies = [ + "Inflector", + "proc-macro-crate", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "sp-session" +version = "4.0.0-dev" +dependencies = [ + "parity-scale-codec", + "scale-info", + "sp-api", + "sp-core", + "sp-runtime", + "sp-staking", + "sp-std", +] + +[[package]] +name = "sp-staking" +version = "4.0.0-dev" +dependencies = [ + "parity-scale-codec", + "scale-info", + "sp-core", + "sp-runtime", + "sp-std", +] + +[[package]] +name = "sp-state-machine" +version = "0.13.0" +dependencies = [ + "hash-db", + "log", + "parity-scale-codec", + "parking_lot", + "rand 0.8.5", + "smallvec", + "sp-core", + "sp-externalities", + "sp-panic-handler", + "sp-std", + "sp-trie", + "thiserror", + "tracing", +] + +[[package]] +name = "sp-std" +version = "5.0.0" + +[[package]] +name = "sp-storage" +version = "7.0.0" +dependencies = [ + "impl-serde", + "parity-scale-codec", + "ref-cast", + "serde", + "sp-debug-derive", + "sp-std", +] + +[[package]] +name = "sp-timestamp" +version = "4.0.0-dev" +dependencies = [ + "async-trait", + "futures-timer", + "log", + "parity-scale-codec", + "sp-inherents", + "sp-runtime", + "sp-std", + "thiserror", +] + +[[package]] +name = "sp-tracing" +version = "6.0.0" +dependencies = [ + "parity-scale-codec", + "sp-std", + "tracing", + "tracing-core", + "tracing-subscriber", +] + +[[package]] +name = "sp-trie" +version = "7.0.0" +dependencies = [ + "ahash 0.8.3", + "hash-db", + "hashbrown 0.12.3", + "lazy_static", + "memory-db", + "nohash-hasher", + "parity-scale-codec", + "parking_lot", + "scale-info", + "schnellru", + "sp-core", + "sp-std", + "thiserror", + "tracing", + "trie-db", + "trie-root", +] + +[[package]] +name = "sp-version" +version = "5.0.0" +dependencies = [ + "impl-serde", + "parity-scale-codec", + "parity-wasm", + "scale-info", + "serde", + "sp-core-hashing-proc-macro", + "sp-runtime", + "sp-std", + "sp-version-proc-macro", + "thiserror", +] + +[[package]] +name = "sp-version-proc-macro" +version = "4.0.0-dev" +dependencies = [ + "parity-scale-codec", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "sp-wasm-interface" +version = "7.0.0" +dependencies = [ + "anyhow", + "impl-trait-for-tuples", + "log", + "parity-scale-codec", + "sp-std", + "wasmi", + "wasmtime", +] + +[[package]] +name = "sp-weights" +version = "4.0.0" +dependencies = [ + "parity-scale-codec", + "scale-info", + "serde", + "smallvec", + "sp-arithmetic", + "sp-core", + "sp-debug-derive", + "sp-std", +] + +[[package]] +name = "spki" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "67cf02bbac7a337dc36e4f5a693db6c21e7863f45070f7064577eb4367a3212b" +dependencies = [ + "base64ct", + "der", +] + +[[package]] +name = "ss58-registry" +version = "1.36.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "23d92659e7d18d82b803824a9ba5a6022cff101c3491d027c1c1d8d30e749284" +dependencies = [ + "Inflector", + "num-format", + "proc-macro2", + "quote", + "serde", + "serde_json", + "unicode-xid", +] + +[[package]] +name = "stable_deref_trait" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3" + +[[package]] +name = "static_assertions" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" + +[[package]] +name = "strum" +version = "0.24.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "063e6045c0e62079840579a7e47a355ae92f60eb74daaf156fb1e84ba164e63f" +dependencies = [ + "strum_macros", +] + +[[package]] +name = "strum_macros" +version = "0.24.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e385be0d24f186b4ce2f9982191e7101bb737312ad61c1f2f984f34bcf85d59" +dependencies = [ + "heck", + "proc-macro2", + "quote", + "rustversion", + "syn", +] + +[[package]] +name = "substrate-bip39" +version = "0.4.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49eee6965196b32f882dd2ee85a92b1dbead41b04e53907f269de3b0dc04733c" +dependencies = [ + "hmac 0.11.0", + "pbkdf2 0.8.0", + "schnorrkel", + "sha2 0.9.9", + "zeroize", +] + +[[package]] +name = "subtle" +version = "2.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6bdef32e8150c2a081110b42772ffe7d7c9032b606bc226c8260fd97e0976601" + +[[package]] +name = "syn" +version = "1.0.105" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "60b9b43d45702de4c839cb9b51d9f529c5dd26a4aff255b42b1ebc03e88ee908" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "synstructure" +version = "0.12.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f36bdaa60a83aca3921b5259d5400cbf5e90fc51931376a9bd4a0eb79aa7210f" +dependencies = [ + "proc-macro2", + "quote", + "syn", + "unicode-xid", +] + +[[package]] +name = "tap" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "55937e1799185b12863d447f42597ed69d9928686b8d88a1df17376a097d8369" + +[[package]] +name = "target-lexicon" +version = "0.12.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9410d0f6853b1d94f0e519fb95df60f29d2c1eff2d921ffdf01a4c8a3b54f12d" + +[[package]] +name = "termcolor" +version = "1.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bab24d30b911b2376f3a13cc2cd443142f0c81dda04c118693e35b3835757755" +dependencies = [ + "winapi-util", +] + +[[package]] +name = "thiserror" +version = "1.0.37" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "10deb33631e3c9018b9baf9dcbbc4f737320d2b576bac10f6aefa048fa407e3e" +dependencies = [ + "thiserror-impl", +] + +[[package]] +name = "thiserror-impl" +version = "1.0.37" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "982d17546b47146b28f7c22e3d08465f6b8903d0ea13c1660d9d84a6e7adcdbb" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "thread_local" +version = "1.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5516c27b78311c50bf42c071425c560ac799b11c30b31f87e3081965fe5e0180" +dependencies = [ + "once_cell", +] + +[[package]] +name = "tiny-bip39" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "62cc94d358b5a1e84a5cb9109f559aa3c4d634d2b1b4de3d0fa4adc7c78e2861" +dependencies = [ + "anyhow", + "hmac 0.12.1", + "once_cell", + "pbkdf2 0.11.0", + "rand 0.8.5", + "rustc-hash", + "sha2 0.10.6", + "thiserror", + "unicode-normalization", + "wasm-bindgen", + "zeroize", +] + +[[package]] +name = "tinyvec" +version = "1.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87cc5ceb3875bb20c2890005a4e226a4651264a5c75edb2421b52861a0a0cb50" +dependencies = [ + "tinyvec_macros", +] + +[[package]] +name = "tinyvec_macros" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cda74da7e1a664f795bb1f8a87ec406fb89a02522cf6e50620d016add6dbbf5c" + +[[package]] +name = "toml" +version = "0.5.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8d82e1a7758622a465f8cee077614c73484dac5b836c02ff6a40d5d1010324d7" +dependencies = [ + "serde", +] + +[[package]] +name = "tracing" +version = "0.1.37" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ce8c33a8d48bd45d624a6e523445fd21ec13d3653cd51f681abf67418f54eb8" +dependencies = [ + "cfg-if", + "pin-project-lite", + "tracing-attributes", + "tracing-core", +] + +[[package]] +name = "tracing-attributes" +version = "0.1.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4017f8f45139870ca7e672686113917c71c7a6e02d4924eda67186083c03081a" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "tracing-core" +version = "0.1.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "24eb03ba0eab1fd845050058ce5e616558e8f8d8fca633e6b163fe25c797213a" +dependencies = [ + "once_cell", + "valuable", +] + +[[package]] +name = "tracing-log" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "78ddad33d2d10b1ed7eb9d1f518a5674713876e97e5bb9b7345a7984fbb4f922" +dependencies = [ + "lazy_static", + "log", + "tracing-core", +] + +[[package]] +name = "tracing-serde" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bc6b213177105856957181934e4920de57730fc69bf42c37ee5bb664d406d9e1" +dependencies = [ + "serde", + "tracing-core", +] + +[[package]] +name = "tracing-subscriber" +version = "0.2.25" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0e0d2eaa99c3c2e41547cfa109e910a68ea03823cccad4a0525dcbc9b01e8c71" +dependencies = [ + "ansi_term", + "chrono", + "lazy_static", + "matchers", + "regex", + "serde", + "serde_json", + "sharded-slab", + "smallvec", + "thread_local", + "tracing", + "tracing-core", + "tracing-log", + "tracing-serde", +] + +[[package]] +name = "trie-db" +version = "0.27.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "634d75c77ea43f2ad8ea9d9c58de49dfc9c3995bdef32b503df7883ff054e7f1" +dependencies = [ + "hash-db", + "hashbrown 0.13.2", + "log", + "rustc-hex", + "smallvec", +] + +[[package]] +name = "trie-root" +version = "0.18.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d4ed310ef5ab98f5fa467900ed906cb9232dd5376597e00fd4cba2a449d06c0b" +dependencies = [ + "hash-db", +] + +[[package]] +name = "tt-call" +version = "1.0.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5e66dcbec4290c69dd03c57e76c2469ea5c7ce109c6dd4351c13055cf71ea055" + +[[package]] +name = "twox-hash" +version = "1.6.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "97fee6b57c6a41524a810daee9286c02d7752c4253064d0b05472833a438f675" +dependencies = [ + "cfg-if", + "digest 0.10.6", + "rand 0.8.5", + "static_assertions", +] + +[[package]] +name = "typenum" +version = "1.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "497961ef93d974e23eb6f433eb5fe1b7930b659f06d12dec6fc44a8f554c0bba" + +[[package]] +name = "uint" +version = "0.9.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "76f64bba2c53b04fcab63c01a7d7427eadc821e3bc48c34dc9ba29c501164b52" +dependencies = [ + "byteorder", + "crunchy", + "hex", + "static_assertions", +] + +[[package]] +name = "unicode-bidi" +version = "0.3.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "524b68aca1d05e03fdf03fcdce2c6c94b6daf6d16861ddaa7e4f2b6638a9052c" + +[[package]] +name = "unicode-ident" +version = "1.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6ceab39d59e4c9499d4e5a8ee0e2735b891bb7308ac83dfb4e80cad195c9f6f3" + +[[package]] +name = "unicode-normalization" +version = "0.1.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c5713f0fc4b5db668a2ac63cdb7bb4469d8c9fed047b1d0292cc7b0ce2ba921" +dependencies = [ + "tinyvec", +] + +[[package]] +name = "unicode-width" +version = "0.1.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c0edd1e5b14653f783770bce4a4dabb4a5108a5370a5f5d8cfe8710c361f6c8b" + +[[package]] +name = "unicode-xid" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f962df74c8c05a667b5ee8bcf162993134c104e96440b663c8daa176dc772d8c" + +[[package]] +name = "url" +version = "2.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0d68c799ae75762b8c3fe375feb6600ef5602c883c5d21eb51c09f22b83c4643" +dependencies = [ + "form_urlencoded", + "idna", + "percent-encoding", +] + +[[package]] +name = "valuable" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "830b7e5d4d90034032940e4ace0d9a9a057e7a45cd94e6c007832e39edb82f6d" + +[[package]] +name = "version_check" +version = "0.9.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" + +[[package]] +name = "wasi" +version = "0.9.0+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cccddf32554fecc6acb585f82a32a72e28b48f8c4c1883ddfeeeaa96f7d8e519" + +[[package]] +name = "wasi" +version = "0.11.0+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" + +[[package]] +name = "wasm-bindgen" +version = "0.2.83" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eaf9f5aceeec8be17c128b2e93e031fb8a4d469bb9c4ae2d7dc1888b26887268" +dependencies = [ + "cfg-if", + "wasm-bindgen-macro", +] + +[[package]] +name = "wasm-bindgen-backend" +version = "0.2.83" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4c8ffb332579b0557b52d268b91feab8df3615f265d5270fec2a8c95b17c1142" +dependencies = [ + "bumpalo", + "log", + "once_cell", + "proc-macro2", + "quote", + "syn", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-macro" +version = "0.2.83" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "052be0f94026e6cbc75cdefc9bae13fd6052cdcaf532fa6c45e7ae33a1e6c810" +dependencies = [ + "quote", + "wasm-bindgen-macro-support", +] + +[[package]] +name = "wasm-bindgen-macro-support" +version = "0.2.83" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "07bc0c051dc5f23e307b13285f9d75df86bfdf816c5721e573dec1f9b8aa193c" +dependencies = [ + "proc-macro2", + "quote", + "syn", + "wasm-bindgen-backend", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-shared" +version = "0.2.83" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1c38c045535d93ec4f0b4defec448e4291638ee608530863b1e2ba115d4fff7f" + +[[package]] +name = "wasmi" +version = "0.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "06c326c93fbf86419608361a2c925a31754cf109da1b8b55737070b4d6669422" +dependencies = [ + "parity-wasm", + "wasmi-validation", + "wasmi_core", +] + +[[package]] +name = "wasmi-validation" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "91ff416ad1ff0c42e5a926ed5d5fab74c0f098749aa0ad8b2a34b982ce0e867b" +dependencies = [ + "parity-wasm", +] + +[[package]] +name = "wasmi_core" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "57d20cb3c59b788653d99541c646c561c9dd26506f25c0cebfe810659c54c6d7" +dependencies = [ + "downcast-rs", + "libm", + "memory_units", + "num-rational", + "num-traits", +] + +[[package]] +name = "wasmparser" +version = "0.100.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "64b20236ab624147dfbb62cf12a19aaf66af0e41b8398838b66e997d07d269d4" +dependencies = [ + "indexmap", + "url", +] + +[[package]] +name = "wasmtime" +version = "6.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f6e89f9819523447330ffd70367ef4a18d8c832e24e8150fe054d1d912841632" +dependencies = [ + "anyhow", + "bincode", + "cfg-if", + "indexmap", + "libc", + "log", + "object", + "once_cell", + "paste", + "psm", + "serde", + "target-lexicon", + "wasmparser", + "wasmtime-environ", + "wasmtime-jit", + "wasmtime-runtime", + "windows-sys 0.42.0", +] + +[[package]] +name = "wasmtime-asm-macros" +version = "6.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9bd3a5e46c198032da934469f3a6e48649d1f9142438e4fd4617b68a35644b8a" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "wasmtime-environ" +version = "6.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a6db9fc52985ba06ca601f2ff0ff1f526c5d724c7ac267b47326304b0c97883" +dependencies = [ + "anyhow", + "cranelift-entity", + "gimli", + "indexmap", + "log", + "object", + "serde", + "target-lexicon", + "thiserror", + "wasmparser", + "wasmtime-types", +] + +[[package]] +name = "wasmtime-jit" +version = "6.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b77e3a52cd84d0f7f18554afa8060cfe564ccac61e3b0802d3fd4084772fa5f6" +dependencies = [ + "addr2line", + "anyhow", + "bincode", + "cfg-if", + "cpp_demangle", + "gimli", + "log", + "object", + "rustc-demangle", + "serde", + "target-lexicon", + "wasmtime-environ", + "wasmtime-jit-icache-coherence", + "wasmtime-runtime", + "windows-sys 0.42.0", +] + +[[package]] +name = "wasmtime-jit-debug" +version = "6.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d0245e8a9347017c7185a72e215218a802ff561545c242953c11ba00fccc930f" +dependencies = [ + "once_cell", +] + +[[package]] +name = "wasmtime-jit-icache-coherence" +version = "6.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "67d412e9340ab1c83867051d8d1d7c90aa8c9afc91da086088068e2734e25064" +dependencies = [ + "cfg-if", + "libc", + "windows-sys 0.42.0", +] + +[[package]] +name = "wasmtime-runtime" +version = "6.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d594e791b5fdd4dbaf8cf7ae62f2e4ff85018ce90f483ca6f42947688e48827d" +dependencies = [ + "anyhow", + "cc", + "cfg-if", + "indexmap", + "libc", + "log", + "mach", + "memfd", + "memoffset", + "paste", + "rand 0.8.5", + "rustix", + "wasmtime-asm-macros", + "wasmtime-environ", + "wasmtime-jit-debug", + "windows-sys 0.42.0", +] + +[[package]] +name = "wasmtime-types" +version = "6.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a6688d6f96d4dbc1f89fab626c56c1778936d122b5f4ae7a57c2eb42b8d982e2" +dependencies = [ + "cranelift-entity", + "serde", + "thiserror", + "wasmparser", +] + +[[package]] +name = "wide" +version = "0.7.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b689b6c49d6549434bf944e6b0f39238cf63693cb7a147e9d887507fffa3b223" +dependencies = [ + "bytemuck", + "safe_arch", +] + +[[package]] +name = "winapi" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" +dependencies = [ + "winapi-i686-pc-windows-gnu", + "winapi-x86_64-pc-windows-gnu", +] + +[[package]] +name = "winapi-i686-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" + +[[package]] +name = "winapi-util" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "70ec6ce85bb158151cae5e5c87f95a8e97d2c0c4b001223f33a334e3ce5de178" +dependencies = [ + "winapi", +] + +[[package]] +name = "winapi-x86_64-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" + +[[package]] +name = "windows-sys" +version = "0.42.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a3e1820f08b8513f676f7ab6c1f99ff312fb97b553d30ff4dd86f9f15728aa7" +dependencies = [ + "windows_aarch64_gnullvm", + "windows_aarch64_msvc", + "windows_i686_gnu", + "windows_i686_msvc", + "windows_x86_64_gnu", + "windows_x86_64_gnullvm", + "windows_x86_64_msvc", +] + +[[package]] +name = "windows-sys" +version = "0.45.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "75283be5efb2831d37ea142365f009c02ec203cd29a3ebecbc093d52315b66d0" +dependencies = [ + "windows-targets", +] + +[[package]] +name = "windows-targets" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e5180c00cd44c9b1c88adb3693291f1cd93605ded80c250a75d472756b4d071" +dependencies = [ + "windows_aarch64_gnullvm", + "windows_aarch64_msvc", + "windows_i686_gnu", + "windows_i686_msvc", + "windows_x86_64_gnu", + "windows_x86_64_gnullvm", + "windows_x86_64_msvc", +] + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "597a5118570b68bc08d8d59125332c54f1ba9d9adeedeef5b99b02ba2b0698f8" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e08e8864a60f06ef0d0ff4ba04124db8b0fb3be5776a5cd47641e942e58c4d43" + +[[package]] +name = "windows_i686_gnu" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c61d927d8da41da96a81f029489353e68739737d3beca43145c8afec9a31a84f" + +[[package]] +name = "windows_i686_msvc" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "44d840b6ec649f480a41c8d80f9c65108b92d89345dd94027bfe06ac444d1060" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8de912b8b8feb55c064867cf047dda097f92d51efad5b491dfb98f6bbb70cb36" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "26d41b46a36d453748aedef1486d5c7a85db22e56aff34643984ea85514e94a3" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9aec5da331524158c6d1a4ac0ab1541149c0b9505fde06423b02f5ef0106b9f0" + +[[package]] +name = "wyz" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05f360fc0b24296329c78fda852a1e9ae82de9cf7b27dae4b7f62f118f77b9ed" +dependencies = [ + "tap", +] + +[[package]] +name = "zeroize" +version = "1.5.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c394b5bd0c6f669e7275d9c20aa90ae064cb22e75a1cad54e1b34088034b149f" +dependencies = [ + "zeroize_derive", +] + +[[package]] +name = "zeroize_derive" +version = "1.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "44bf07cb3e50ea2003396695d58bf46bc9887a1f362260446fad6bc4e79bd36c" +dependencies = [ + "proc-macro2", + "quote", + "syn", + "synstructure", +] diff --git a/frame/election-provider-multi-phase/test-staking-e2e/Cargo.toml b/frame/election-provider-multi-phase/test-staking-e2e/Cargo.toml new file mode 100644 index 000000000..432b732b4 --- /dev/null +++ b/frame/election-provider-multi-phase/test-staking-e2e/Cargo.toml @@ -0,0 +1,40 @@ +[package] +name = "pallet-election-provider-e2e-test" +version = "1.0.0" +authors = ["Parity Technologies "] +edition = "2021" +license = "Apache-2.0" +homepage = "https://substrate.io" +repository = "https://github.com/paritytech/substrate/" +description = "FRAME election provider multi phase pallet tests with staking pallet, bags-list and session pallets" +publish = false + +[package.metadata.docs.rs] +targets = ["x86_64-unknown-linux-gnu"] + +[workspace] + +[dev-dependencies] +codec = { package = "parity-scale-codec", version = "3.0.0", features = ["derive"] } +scale-info = { version = "2.0.1", features = ["derive"] } +log = { version = "0.4.17", default-features = false } + +sp-runtime = { version = "7.0.0", path = "../../../primitives/runtime" } +sp-io = { version = "7.0.0", path = "../../../primitives/io" } +sp-std = { version = "5.0.0", path = "../../../primitives/std" } +sp-staking = { version = "4.0.0-dev", path = "../../../primitives/staking" } +sp-core = { version = "7.0.0", path = "../../../primitives/core" } +sp-npos-elections = { version = "4.0.0-dev", default-features = false, path = "../../../primitives/npos-elections" } +sp-tracing = { version = "6.0.0", path = "../../../primitives/tracing" } + +frame-system = { version = "4.0.0-dev", path = "../../system" } +frame-support = { version = "4.0.0-dev", path = "../../support" } +frame-election-provider-support = { version = "4.0.0-dev", path = "../../election-provider-support" } + +pallet-election-provider-multi-phase = { version = "4.0.0-dev", path = "../../election-provider-multi-phase" } +pallet-staking = { version = "4.0.0-dev", path = "../../staking" } +pallet-bags-list = { version = "4.0.0-dev", path = "../../bags-list" } +pallet-balances = { version = "4.0.0-dev", path = "../../balances" } +pallet-timestamp = { version = "4.0.0-dev", path = "../../timestamp" } +pallet-session = { version = "4.0.0-dev", path = "../../session" } + diff --git a/frame/election-provider-multi-phase/test-staking-e2e/src/lib.rs b/frame/election-provider-multi-phase/test-staking-e2e/src/lib.rs new file mode 100644 index 000000000..c8f7d48a8 --- /dev/null +++ b/frame/election-provider-multi-phase/test-staking-e2e/src/lib.rs @@ -0,0 +1,206 @@ +// This file is part of Substrate. + +// Copyright (C) Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#![cfg(test)] +mod mock; + +pub(crate) const LOG_TARGET: &str = "tests::e2e-epm"; + +use mock::*; +use sp_core::Get; +use sp_npos_elections::{to_supports, StakedAssignment}; +use sp_runtime::Perbill; + +use crate::mock::RuntimeOrigin; + +// syntactic sugar for logging. +#[macro_export] +macro_rules! log { + ($level:tt, $patter:expr $(, $values:expr)* $(,)?) => { + log::$level!( + target: crate::LOG_TARGET, + concat!("🛠️ ", $patter) $(, $values)* + ) + }; +} + +fn log_current_time() { + log!( + trace, + "block: {:?}, session: {:?}, era: {:?}, EPM phase: {:?} ts: {:?}", + System::block_number(), + Session::current_index(), + Staking::current_era(), + ElectionProviderMultiPhase::current_phase(), + Timestamp::now() + ); +} + +#[test] +fn block_progression_works() { + ExtBuilder::default().build_and_execute(|| { + assert_eq!(active_era(), 0); + assert_eq!(Session::current_index(), 0); + assert!(ElectionProviderMultiPhase::current_phase().is_off()); + + assert!(start_next_active_era().is_ok()); + assert_eq!(active_era(), 1); + assert_eq!(Session::current_index(), >::get()); + + assert!(ElectionProviderMultiPhase::current_phase().is_off()); + + roll_to_epm_signed(); + assert!(ElectionProviderMultiPhase::current_phase().is_signed()); + }); + + ExtBuilder::default().build_and_execute(|| { + assert_eq!(active_era(), 0); + assert_eq!(Session::current_index(), 0); + assert!(ElectionProviderMultiPhase::current_phase().is_off()); + + assert!(start_next_active_era_delayed_solution().is_ok()); + // if the solution is delayed, EPM will end up in emergency mode.. + assert!(ElectionProviderMultiPhase::current_phase().is_emergency()); + // .. era won't progress.. + assert_eq!(active_era(), 0); + // .. but session does. + assert_eq!(Session::current_index(), 2); + }) +} + +#[test] +/// Replicates the Kusama incident of 8th Dec 2022 and its resolution through the governance +/// fallback. +/// +/// After enough slashes exceeded the `Staking::OffendingValidatorsThreshold`, the staking pallet +/// set `Forcing::ForceNew`. When a new session starts, staking will start to force a new era and +/// calls ::elect(). If at this point EPM and the staking miners did not +/// have enough time to queue a new solution (snapshot + solution submission), the election request +/// fails. If there is no election fallback mechanism in place, EPM enters in emergency mode. +/// Recovery: Once EPM is in emergency mode, subsequent calls to `elect()` will fail until a new +/// solution is added to EPM's `QueuedSolution` queue. This can be achieved through +/// `Call::set_emergency_election_result` or `Call::governance_fallback` dispatchables. Once a new +/// solution is added to the queue, EPM phase transitions to `Phase::Off` and the election flow +/// restarts. Note that in this test case, the emergency throttling is disabled. +fn enters_emergency_phase_after_forcing_before_elect() { + let epm_builder = EpmExtBuilder::default().disable_emergency_throttling(); + + ExtBuilder::default().epm(epm_builder).build_and_execute(|| { + log!( + trace, + "current validators (staking): {:?}", + >::validators() + ); + let session_validators_before = Session::validators(); + + roll_to_epm_off(); + assert!(ElectionProviderMultiPhase::current_phase().is_off()); + + assert_eq!(pallet_staking::ForceEra::::get(), pallet_staking::Forcing::NotForcing); + // slashes so that staking goes into `Forcing::ForceNew`. + slash_through_offending_threshold(); + + assert_eq!(pallet_staking::ForceEra::::get(), pallet_staking::Forcing::ForceNew); + + advance_session_delayed_solution(); + assert!(ElectionProviderMultiPhase::current_phase().is_emergency()); + log_current_time(); + + let era_before_delayed_next = Staking::current_era(); + // try to advance 2 eras. + assert!(start_next_active_era_delayed_solution().is_ok()); + assert_eq!(Staking::current_era(), era_before_delayed_next); + assert!(start_next_active_era().is_err()); + assert_eq!(Staking::current_era(), era_before_delayed_next); + + // EPM is still in emergency phase. + assert!(ElectionProviderMultiPhase::current_phase().is_emergency()); + + // session validator set remains the same. + assert_eq!(Session::validators(), session_validators_before); + + // performs recovery through the set emergency result. + let supports = to_supports(&vec![ + StakedAssignment { who: 21, distribution: vec![(21, 10)] }, + StakedAssignment { who: 31, distribution: vec![(21, 10), (31, 10)] }, + StakedAssignment { who: 41, distribution: vec![(41, 10)] }, + ]); + assert!(ElectionProviderMultiPhase::set_emergency_election_result( + RuntimeOrigin::root(), + supports + ) + .is_ok()); + + // EPM can now roll to signed phase to proceed with elections. The validator set is the + // expected (ie. set through `set_emergency_election_result`). + roll_to_epm_signed(); + //assert!(ElectionProviderMultiPhase::current_phase().is_signed()); + assert_eq!(Session::validators(), vec![21, 31, 41]); + assert_eq!(Staking::current_era(), era_before_delayed_next.map(|e| e + 1)); + }); +} + +#[test] +/// Continuously slash 10% of the active validators per era. +/// +/// Since the `OffendingValidatorsThreshold` is only checked per era staking does not force a new +/// era even as the number of active validators is decreasing across eras. When processing a new +/// slash, staking calculates the offending threshold based on the length of the current list of +/// active validators. Thus, slashing a percentage of the current validators that is lower than +/// `OffendingValidatorsThreshold` will never force a new era. However, as the slashes progress, if +/// the subsequent elections do not meet the minimum election untrusted score, the election will +/// fail and enter in emenergency mode. +fn continous_slashes_below_offending_threshold() { + let staking_builder = StakingExtBuilder::default().validator_count(10); + let epm_builder = EpmExtBuilder::default().disable_emergency_throttling(); + + ExtBuilder::default() + .staking(staking_builder) + .epm(epm_builder) + .build_and_execute(|| { + assert_eq!(Session::validators().len(), 10); + let mut active_validator_set = Session::validators(); + + roll_to_epm_signed(); + + // set a minimum election score. + assert!(set_minimum_election_score(500, 1000, 500).is_ok()); + + // slash 10% of the active validators and progress era until the minimum trusted score + // is reached. + while active_validator_set.len() > 0 { + let slashed = slash_percentage(Perbill::from_percent(10)); + assert_eq!(slashed.len(), 1); + + // break loop when era does not progress; EPM is in emergency phase as election + // failed due to election minimum score. + if start_next_active_era().is_err() { + assert!(ElectionProviderMultiPhase::current_phase().is_emergency()); + break + } + + active_validator_set = Session::validators(); + + log!( + trace, + "slashed 10% of active validators ({:?}). After slash: {:?}", + slashed, + active_validator_set + ); + } + }); +} diff --git a/frame/election-provider-multi-phase/test-staking-e2e/src/mock.rs b/frame/election-provider-multi-phase/test-staking-e2e/src/mock.rs new file mode 100644 index 000000000..19f568737 --- /dev/null +++ b/frame/election-provider-multi-phase/test-staking-e2e/src/mock.rs @@ -0,0 +1,779 @@ +// This file is part of Substrate. + +// Copyright (C) 2022 Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#![allow(dead_code)] + +use _feps::ExtendedBalance; +use frame_support::{ + parameter_types, traits, + traits::{GenesisBuild, Hooks}, + weights::constants, +}; +use frame_system::EnsureRoot; +use sp_core::{ConstU32, Get, H256}; +use sp_npos_elections::{ElectionScore, VoteWeight}; +use sp_runtime::{ + testing, + traits::{IdentityLookup, Zero}, + transaction_validity, PerU16, Perbill, +}; +use sp_staking::{ + offence::{DisableStrategy, OffenceDetails, OnOffenceHandler}, + EraIndex, SessionIndex, +}; +use sp_std::prelude::*; +use std::collections::BTreeMap; + +use frame_election_provider_support::{onchain, ElectionDataProvider, SequentialPhragmen, Weight}; +use pallet_election_provider_multi_phase::{ + unsigned::MinerConfig, ElectionCompute, QueuedSolution, SolutionAccuracyOf, +}; +use pallet_staking::StakerStatus; + +use crate::{log, log_current_time}; + +pub const INIT_TIMESTAMP: u64 = 30_000; +pub const BLOCK_TIME: u64 = 1000; + +type Block = frame_system::mocking::MockBlock; +type UncheckedExtrinsic = frame_system::mocking::MockUncheckedExtrinsic; +type Extrinsic = testing::TestXt; + +frame_support::construct_runtime!( + pub enum Runtime where + Block = Block, + NodeBlock = Block, + UncheckedExtrinsic = UncheckedExtrinsic + { + System: frame_system, + ElectionProviderMultiPhase: pallet_election_provider_multi_phase, + Staking: pallet_staking, + Balances: pallet_balances, + BagsList: pallet_bags_list, + Session: pallet_session, + Historical: pallet_session::historical, + Timestamp: pallet_timestamp, + } +); + +pub(crate) type AccountId = u128; +pub(crate) type AccountIndex = u32; +pub(crate) type BlockNumber = u64; +pub(crate) type Balance = u64; +pub(crate) type VoterIndex = u32; +pub(crate) type TargetIndex = u16; +pub(crate) type Moment = u64; + +impl frame_system::Config for Runtime { + type BaseCallFilter = traits::Everything; + type BlockWeights = BlockWeights; + type BlockLength = (); + type DbWeight = (); + type RuntimeOrigin = RuntimeOrigin; + type Index = AccountIndex; + type BlockNumber = BlockNumber; + type RuntimeCall = RuntimeCall; + type Hash = H256; + type Hashing = sp_runtime::traits::BlakeTwo256; + type AccountId = AccountId; + type Lookup = IdentityLookup; + type Header = sp_runtime::testing::Header; + type RuntimeEvent = RuntimeEvent; + type BlockHashCount = (); + type Version = (); + type PalletInfo = PalletInfo; + type AccountData = pallet_balances::AccountData; + type OnNewAccount = (); + type OnKilledAccount = (); + type SystemWeightInfo = (); + type SS58Prefix = (); + type OnSetCode = (); + type MaxConsumers = traits::ConstU32<16>; +} + +const NORMAL_DISPATCH_RATIO: Perbill = Perbill::from_percent(75); +parameter_types! { + pub static ExistentialDeposit: Balance = 1; + pub BlockWeights: frame_system::limits::BlockWeights = frame_system::limits::BlockWeights + ::with_sensible_defaults( + Weight::from_parts(2u64 * constants::WEIGHT_REF_TIME_PER_SECOND, u64::MAX), + NORMAL_DISPATCH_RATIO, + ); +} + +impl pallet_balances::Config for Runtime { + type MaxLocks = traits::ConstU32<1024>; + type MaxReserves = (); + type ReserveIdentifier = [u8; 8]; + type Balance = Balance; + type RuntimeEvent = RuntimeEvent; + type DustRemoval = (); + type ExistentialDeposit = ExistentialDeposit; + type AccountStore = System; + type WeightInfo = (); +} + +impl pallet_timestamp::Config for Runtime { + type Moment = Moment; + type OnTimestampSet = (); + type MinimumPeriod = traits::ConstU64<5>; + type WeightInfo = (); +} + +parameter_types! { + pub static Period: BlockNumber = 30; + pub static Offset: BlockNumber = 0; +} + +sp_runtime::impl_opaque_keys! { + pub struct SessionKeys { + pub other: OtherSessionHandler, + } +} + +impl pallet_session::Config for Runtime { + type SessionManager = pallet_session::historical::NoteHistoricalRoot; + type Keys = SessionKeys; + type ShouldEndSession = pallet_session::PeriodicSessions; + type NextSessionRotation = pallet_session::PeriodicSessions; + type SessionHandler = (OtherSessionHandler,); + type RuntimeEvent = RuntimeEvent; + type ValidatorId = AccountId; + type ValidatorIdOf = pallet_staking::StashOf; + type WeightInfo = (); +} +impl pallet_session::historical::Config for Runtime { + type FullIdentification = pallet_staking::Exposure; + type FullIdentificationOf = pallet_staking::ExposureOf; +} + +frame_election_provider_support::generate_solution_type!( + #[compact] + pub struct MockNposSolution::< + VoterIndex = VoterIndex, + TargetIndex = TargetIndex, + Accuracy = PerU16, + MaxVoters = ConstU32::<2_000> + >(16) +); + +parameter_types! { + pub static SignedPhase: BlockNumber = 10; + pub static UnsignedPhase: BlockNumber = 10; + // we expect a minimum of 3 blocks in signed phase and unsigned phases before trying + // enetering in emergency phase after the election failed. + pub static MinBlocksBeforeEmergency: BlockNumber = 3; + pub static MaxElectingVoters: VoterIndex = 1000; + pub static MaxElectableTargets: TargetIndex = 1000; + pub static MaxActiveValidators: u32 = 1000; + pub static OffchainRepeat: u32 = 5; + pub static MinerMaxLength: u32 = 256; + pub static MinerMaxWeight: Weight = BlockWeights::get().max_block; + pub static TransactionPriority: transaction_validity::TransactionPriority = 1; + pub static MaxWinners: u32 = 100; + pub static MaxVotesPerVoter: u32 = 16; + pub static MaxNominations: u32 = 16; +} + +impl pallet_election_provider_multi_phase::Config for Runtime { + type RuntimeEvent = RuntimeEvent; + type Currency = Balances; + type EstimateCallFee = frame_support::traits::ConstU32<8>; + type SignedPhase = SignedPhase; + type UnsignedPhase = UnsignedPhase; + type BetterSignedThreshold = (); + type BetterUnsignedThreshold = (); + type OffchainRepeat = OffchainRepeat; + type MinerTxPriority = TransactionPriority; + type MinerConfig = Self; + type SignedMaxSubmissions = ConstU32<10>; + type SignedRewardBase = (); + type SignedDepositBase = (); + type SignedDepositByte = (); + type SignedMaxRefunds = ConstU32<3>; + type SignedDepositWeight = (); + type SignedMaxWeight = (); + type SlashHandler = (); + type RewardHandler = (); + type DataProvider = Staking; + type Fallback = + frame_election_provider_support::NoElection<(AccountId, BlockNumber, Staking, MaxWinners)>; + type GovernanceFallback = onchain::OnChainExecution; + type Solver = SequentialPhragmen, ()>; + type ForceOrigin = EnsureRoot; + type MaxElectableTargets = MaxElectableTargets; + type MaxElectingVoters = MaxElectingVoters; + type MaxWinners = MaxWinners; + type BenchmarkingConfig = NoopElectionProviderBenchmarkConfig; + type WeightInfo = (); +} + +impl MinerConfig for Runtime { + type AccountId = AccountId; + type Solution = MockNposSolution; + type MaxVotesPerVoter = + <::DataProvider as ElectionDataProvider>::MaxVotesPerVoter; + type MaxLength = MinerMaxLength; + type MaxWeight = MinerMaxWeight; + type MaxWinners = MaxWinners; + + fn solution_weight(_v: u32, _t: u32, _a: u32, _d: u32) -> Weight { + Weight::zero() + } +} + +const THRESHOLDS: [VoteWeight; 9] = [10, 20, 30, 40, 50, 60, 1_000, 2_000, 10_000]; + +parameter_types! { + pub static BagThresholds: &'static [sp_npos_elections::VoteWeight] = &THRESHOLDS; + pub const SessionsPerEra: sp_staking::SessionIndex = 2; + pub const BondingDuration: sp_staking::EraIndex = 28; + pub const SlashDeferDuration: sp_staking::EraIndex = 7; // 1/4 the bonding duration. + pub const MaxNominatorRewardedPerValidator: u32 = 256; + pub const OffendingValidatorsThreshold: Perbill = Perbill::from_percent(40); + pub HistoryDepth: u32 = 84; +} + +impl pallet_bags_list::Config for Runtime { + type RuntimeEvent = RuntimeEvent; + type WeightInfo = (); + type ScoreProvider = Staking; + type BagThresholds = BagThresholds; + type Score = VoteWeight; +} + +impl pallet_staking::Config for Runtime { + type MaxNominations = MaxNominations; + type Currency = Balances; + type CurrencyBalance = Balance; + type UnixTime = Timestamp; + type CurrencyToVote = traits::SaturatingCurrencyToVote; + type RewardRemainder = (); + type RuntimeEvent = RuntimeEvent; + type Slash = (); // burn slashes + type Reward = (); // rewards are minted from the void + type SessionsPerEra = SessionsPerEra; + type BondingDuration = BondingDuration; + type SlashDeferDuration = SlashDeferDuration; + type AdminOrigin = EnsureRoot; // root can cancel slashes + type SessionInterface = Self; + type EraPayout = (); + type NextNewSession = Session; + type MaxNominatorRewardedPerValidator = MaxNominatorRewardedPerValidator; + type OffendingValidatorsThreshold = OffendingValidatorsThreshold; + type ElectionProvider = ElectionProviderMultiPhase; + type GenesisElectionProvider = onchain::OnChainExecution; + type VoterList = BagsList; + type TargetList = pallet_staking::UseValidatorsMap; + type MaxUnlockingChunks = ConstU32<32>; + type HistoryDepth = HistoryDepth; + type OnStakerSlash = (); + type WeightInfo = pallet_staking::weights::SubstrateWeight; + type BenchmarkingConfig = pallet_staking::TestBenchmarkingConfig; +} + +impl frame_system::offchain::SendTransactionTypes for Runtime +where + RuntimeCall: From, +{ + type OverarchingCall = RuntimeCall; + type Extrinsic = Extrinsic; +} + +pub struct OnChainSeqPhragmen; + +parameter_types! { + pub static VotersBound: u32 = 600; + pub static TargetsBound: u32 = 400; +} + +impl onchain::Config for OnChainSeqPhragmen { + type System = Runtime; + type Solver = SequentialPhragmen< + AccountId, + pallet_election_provider_multi_phase::SolutionAccuracyOf, + >; + type DataProvider = Staking; + type WeightInfo = (); + type MaxWinners = MaxWinners; + type VotersBound = VotersBound; + type TargetsBound = TargetsBound; +} + +pub struct NoopElectionProviderBenchmarkConfig; + +impl pallet_election_provider_multi_phase::BenchmarkingConfig + for NoopElectionProviderBenchmarkConfig +{ + const VOTERS: [u32; 2] = [0, 0]; + const TARGETS: [u32; 2] = [0, 0]; + const ACTIVE_VOTERS: [u32; 2] = [0, 0]; + const DESIRED_TARGETS: [u32; 2] = [0, 0]; + const SNAPSHOT_MAXIMUM_VOTERS: u32 = 0; + const MINER_MAXIMUM_VOTERS: u32 = 0; + const MAXIMUM_TARGETS: u32 = 0; +} + +pub struct OtherSessionHandler; +impl traits::OneSessionHandler for OtherSessionHandler { + type Key = testing::UintAuthorityId; + + fn on_genesis_session<'a, I: 'a>(_: I) + where + I: Iterator, + AccountId: 'a, + { + } + + fn on_new_session<'a, I: 'a>(_: bool, _: I, _: I) + where + I: Iterator, + AccountId: 'a, + { + } + + fn on_disabled(_validator_index: u32) {} +} + +impl sp_runtime::BoundToRuntimeAppPublic for OtherSessionHandler { + type Public = testing::UintAuthorityId; +} + +pub struct StakingExtBuilder { + validator_count: u32, + minimum_validator_count: u32, + min_nominator_bond: Balance, + min_validator_bond: Balance, + status: BTreeMap>, + stakes: BTreeMap, + stakers: Vec<(AccountId, AccountId, Balance, StakerStatus)>, +} + +impl Default for StakingExtBuilder { + fn default() -> Self { + let stakers = vec![ + // (stash, ctrl, stake, status) + // these two will be elected in the default test where we elect 2. + (11, 10, 1000, StakerStatus::::Validator), + (21, 20, 1000, StakerStatus::::Validator), + // loser validatos if validator_count() is default. + (31, 30, 500, StakerStatus::::Validator), + (41, 40, 500, StakerStatus::::Validator), + (51, 50, 500, StakerStatus::::Validator), + (61, 60, 500, StakerStatus::::Validator), + (71, 70, 500, StakerStatus::::Validator), + (81, 80, 500, StakerStatus::::Validator), + (91, 90, 500, StakerStatus::::Validator), + (101, 100, 500, StakerStatus::::Validator), + // an idle validator + (201, 200, 1000, StakerStatus::::Idle), + ]; + + Self { + validator_count: 2, + minimum_validator_count: 0, + min_nominator_bond: ExistentialDeposit::get(), + min_validator_bond: ExistentialDeposit::get(), + status: Default::default(), + stakes: Default::default(), + stakers, + } + } +} + +impl StakingExtBuilder { + pub fn validator_count(mut self, n: u32) -> Self { + self.validator_count = n; + self + } +} + +pub struct EpmExtBuilder {} + +impl Default for EpmExtBuilder { + fn default() -> Self { + EpmExtBuilder {} + } +} + +impl EpmExtBuilder { + pub fn disable_emergency_throttling(self) -> Self { + ::set(0); + self + } + + pub fn phases(self, signed: BlockNumber, unsigned: BlockNumber) -> Self { + ::set(signed); + ::set(unsigned); + self + } +} + +pub struct BalancesExtBuilder { + balances: Vec<(AccountId, Balance)>, +} + +impl Default for BalancesExtBuilder { + fn default() -> Self { + let balances = vec![ + // (account_id, balance) + (1, 10), + (2, 20), + (3, 300), + (4, 400), + // controllers + (10, 100), + (20, 100), + (30, 100), + (40, 100), + (50, 100), + (60, 100), + (70, 100), + (80, 100), + (90, 100), + (100, 100), + (200, 100), + // stashes + (11, 1000), + (21, 2000), + (31, 3000), + (41, 4000), + (51, 5000), + (61, 6000), + (71, 7000), + (81, 8000), + (91, 9000), + (101, 10000), + (201, 20000), + // This allows us to have a total_payout different from 0. + (999, 1_000_000_000_000), + ]; + Self { balances } + } +} + +pub struct ExtBuilder { + staking_builder: StakingExtBuilder, + epm_builder: EpmExtBuilder, + balances_builder: BalancesExtBuilder, +} + +impl Default for ExtBuilder { + fn default() -> Self { + Self { + staking_builder: StakingExtBuilder::default(), + epm_builder: EpmExtBuilder::default(), + balances_builder: BalancesExtBuilder::default(), + } + } +} + +impl ExtBuilder { + pub fn build(self) -> sp_io::TestExternalities { + sp_tracing::try_init_simple(); + let mut storage = + frame_system::GenesisConfig::default().build_storage::().unwrap(); + + let _ = + pallet_balances::GenesisConfig:: { balances: self.balances_builder.balances } + .assimilate_storage(&mut storage); + + let mut stakers = self.staking_builder.stakers.clone(); + self.staking_builder.status.into_iter().for_each(|(stash, status)| { + let (_, _, _, ref mut prev_status) = stakers + .iter_mut() + .find(|s| s.0 == stash) + .expect("set_status staker should exist; qed"); + *prev_status = status; + }); + // replaced any of the stakes if needed. + self.staking_builder.stakes.into_iter().for_each(|(stash, stake)| { + let (_, _, ref mut prev_stake, _) = stakers + .iter_mut() + .find(|s| s.0 == stash) + .expect("set_stake staker should exits; qed."); + *prev_stake = stake; + }); + + let _ = pallet_staking::GenesisConfig:: { + stakers: stakers.clone(), + validator_count: self.staking_builder.validator_count, + minimum_validator_count: self.staking_builder.minimum_validator_count, + slash_reward_fraction: Perbill::from_percent(10), + min_nominator_bond: self.staking_builder.min_nominator_bond, + min_validator_bond: self.staking_builder.min_validator_bond, + ..Default::default() + } + .assimilate_storage(&mut storage); + + let _ = pallet_session::GenesisConfig:: { + // set the keys for the first session. + keys: stakers + .into_iter() + .map(|(id, ..)| (id, id, SessionKeys { other: (id as u64).into() })) + .collect(), + } + .assimilate_storage(&mut storage); + + let mut ext = sp_io::TestExternalities::from(storage); + + // We consider all test to start after timestamp is initialized This must be ensured by + // having `timestamp::on_initialize` called before `staking::on_initialize`. + ext.execute_with(|| { + System::set_block_number(1); + Session::on_initialize(1); + >::on_initialize(1); + Timestamp::set_timestamp(INIT_TIMESTAMP); + }); + + ext + } + pub fn staking(mut self, builder: StakingExtBuilder) -> Self { + self.staking_builder = builder; + self + } + + pub fn epm(mut self, builder: EpmExtBuilder) -> Self { + self.epm_builder = builder; + self + } + + pub fn balances(mut self, builder: BalancesExtBuilder) -> Self { + self.balances_builder = builder; + self + } + + pub fn build_and_execute(self, test: impl FnOnce() -> ()) { + self.build().execute_with(test) + } +} + +// Progress to given block, triggering session and era changes as we progress and ensuring that +// there is a solution queued when expected. +pub fn roll_to(n: BlockNumber, delay_solution: bool) { + for b in (System::block_number()) + 1..=n { + System::set_block_number(b); + Session::on_initialize(b); + Timestamp::set_timestamp(System::block_number() * BLOCK_TIME + INIT_TIMESTAMP); + + // TODO(gpestana): implement a realistic OCW worker insted of simulating it + // https://github.com/paritytech/substrate/issues/13589 + // if there's no solution queued and the solution should not be delayed, try mining and + // queue a solution. + if ElectionProviderMultiPhase::current_phase().is_signed() && !delay_solution { + let _ = try_queue_solution(ElectionCompute::Signed).map_err(|e| { + log!(info, "failed to mine/queue solution: {:?}", e); + }); + } + ElectionProviderMultiPhase::on_initialize(b); + + Staking::on_initialize(b); + if b != n { + Staking::on_finalize(System::block_number()); + } + + log_current_time(); + } +} + +/// Progresses from the current block number (whatever that may be) to the block where the session +/// `session_index` starts. +pub(crate) fn start_session(session_index: SessionIndex, delay_solution: bool) { + let end: u64 = if Offset::get().is_zero() { + Period::get() * (session_index as u64) + } else { + Offset::get() * (session_index as u64) + Period::get() * (session_index as u64) + }; + + assert!(end >= System::block_number()); + + roll_to(end, delay_solution); + + // session must have progressed properly. + assert_eq!( + Session::current_index(), + session_index, + "current session index = {}, expected = {}", + Session::current_index(), + session_index, + ); +} + +/// Go one session forward. +pub(crate) fn advance_session() { + let current_index = Session::current_index(); + start_session(current_index + 1, false); +} + +pub(crate) fn advance_session_delayed_solution() { + let current_index = Session::current_index(); + start_session(current_index + 1, true); +} + +pub(crate) fn start_next_active_era() -> Result<(), ()> { + start_active_era(active_era() + 1, false) +} + +pub(crate) fn start_next_active_era_delayed_solution() -> Result<(), ()> { + start_active_era(active_era() + 1, true) +} + +/// Progress until the given era. +pub(crate) fn start_active_era(era_index: EraIndex, delay_solution: bool) -> Result<(), ()> { + let era_before = current_era(); + + start_session((era_index * >::get()).into(), delay_solution); + + log!( + info, + "start_active_era - era_before: {}, current era: {} -> progress to: {} -> after era: {}", + era_before, + active_era(), + era_index, + current_era(), + ); + + // if the solution was not delayed, era should have progressed. + if !delay_solution && (active_era() != era_index || current_era() != active_era()) { + Err(()) + } else { + Ok(()) + } +} + +pub(crate) fn active_era() -> EraIndex { + Staking::active_era().unwrap().index +} + +pub(crate) fn current_era() -> EraIndex { + Staking::current_era().unwrap() +} + +// Fast forward until EPM signed phase. +pub fn roll_to_epm_signed() { + while !matches!( + ElectionProviderMultiPhase::current_phase(), + pallet_election_provider_multi_phase::Phase::Signed + ) { + roll_to(System::block_number() + 1, false); + } +} + +// Fast forward until EPM unsigned phase. +pub fn roll_to_epm_unsigned() { + while !matches!( + ElectionProviderMultiPhase::current_phase(), + pallet_election_provider_multi_phase::Phase::Unsigned(_) + ) { + roll_to(System::block_number() + 1, false); + } +} + +// Fast forward until EPM off. +pub fn roll_to_epm_off() { + while !matches!( + ElectionProviderMultiPhase::current_phase(), + pallet_election_provider_multi_phase::Phase::Off + ) { + roll_to(System::block_number() + 1, false); + } +} + +// Queue a solution based on the current snapshot. +pub(crate) fn try_queue_solution(when: ElectionCompute) -> Result<(), String> { + let raw_solution = ElectionProviderMultiPhase::mine_solution() + .map_err(|e| format!("error mining solution: {:?}", e))?; + + ElectionProviderMultiPhase::feasibility_check(raw_solution.0, when) + .map(|ready| { + QueuedSolution::::put(ready); + }) + .map_err(|e| format!("error in solution feasibility: {:?}", e)) +} + +pub(crate) fn on_offence_now( + offenders: &[OffenceDetails< + AccountId, + pallet_session::historical::IdentificationTuple, + >], + slash_fraction: &[Perbill], +) { + let now = Staking::active_era().unwrap().index; + let _ = Staking::on_offence( + offenders, + slash_fraction, + Staking::eras_start_session_index(now).unwrap(), + DisableStrategy::WhenSlashed, + ); +} + +// Add offence to validator, slash it. +pub(crate) fn add_slash(who: &AccountId) { + on_offence_now( + &[OffenceDetails { + offender: (*who, Staking::eras_stakers(active_era(), *who)), + reporters: vec![], + }], + &[Perbill::from_percent(10)], + ); +} + +// Slashes enough validators to cross the `Staking::OffendingValidatorsThreshold`. +pub(crate) fn slash_through_offending_threshold() { + let validators = Session::validators(); + let mut remaining_slashes = + ::OffendingValidatorsThreshold::get() * + validators.len() as u32; + + for v in validators.into_iter() { + if remaining_slashes != 0 { + add_slash(&v); + remaining_slashes -= 1; + } + } +} + +// Slashes a percentage of the active nominators that haven't been slashed yet, with +// a minimum of 1 validator slash. +pub(crate) fn slash_percentage(percentage: Perbill) -> Vec { + let validators = Session::validators(); + let mut remaining_slashes = (percentage * validators.len() as u32).max(1); + let mut slashed = vec![]; + + for v in validators.into_iter() { + if remaining_slashes != 0 { + add_slash(&v); + slashed.push(v); + remaining_slashes -= 1; + } + } + slashed +} + +pub(crate) fn set_minimum_election_score( + minimal_stake: ExtendedBalance, + sum_stake: ExtendedBalance, + sum_stake_squared: ExtendedBalance, +) -> Result<(), ()> { + let election_score = ElectionScore { minimal_stake, sum_stake, sum_stake_squared }; + ElectionProviderMultiPhase::set_minimum_untrusted_score( + RuntimeOrigin::root(), + Some(election_score), + ) + .map(|_| ()) + .map_err(|_| ()) +} From e359d8b3d14ae3c5b012704dd406ed593fcfcca2 Mon Sep 17 00:00:00 2001 From: Mira Ressel Date: Thu, 16 Mar 2023 13:41:43 +0100 Subject: [PATCH 245/558] fix BASE lookup in cargo-check-benches --- scripts/ci/gitlab/pipeline/test.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/ci/gitlab/pipeline/test.yml b/scripts/ci/gitlab/pipeline/test.yml index fd031d9aa..c13f2f361 100644 --- a/scripts/ci/gitlab/pipeline/test.yml +++ b/scripts/ci/gitlab/pipeline/test.yml @@ -91,7 +91,7 @@ cargo-check-benches: - !reference [.pipeline-stopper-vars, script] # merges in the master branch on PRs - | - export BASE=$(curl -s -H "Authorization: Bearer ${GITHUB_PR_TOKEN}" https://api.github.com/repos/paritytech/substrate/pulls/${$CI_COMMIT_REF_NAME} | jq .base.ref) + export BASE=$(curl -s -H "Authorization: Bearer ${GITHUB_PR_TOKEN}" https://api.github.com/repos/paritytech/substrate/pulls/${CI_COMMIT_REF_NAME} | jq .base.ref) - if [ $CI_COMMIT_REF_NAME != "master" ]; then git fetch origin +${BASE}:${BASE}; git fetch origin +$CI_COMMIT_REF_NAME:$CI_COMMIT_REF_NAME; From 283106b19b85bc8deb232cfa624eed195f143541 Mon Sep 17 00:00:00 2001 From: Mira Ressel Date: Thu, 16 Mar 2023 14:47:28 +0100 Subject: [PATCH 246/558] refactor cargo-check-benches logic * fix fetch refspec * assume base is master if the api call fails * merge master into pr branch rather than the other way around; same effect but imo cleaner and more readable * don't re-fetch pr branch, we want to test the version our pipeline is running on --- scripts/ci/gitlab/pipeline/test.yml | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/scripts/ci/gitlab/pipeline/test.yml b/scripts/ci/gitlab/pipeline/test.yml index c13f2f361..7c5f43c29 100644 --- a/scripts/ci/gitlab/pipeline/test.yml +++ b/scripts/ci/gitlab/pipeline/test.yml @@ -90,15 +90,13 @@ cargo-check-benches: - !reference [.rusty-cachier, before_script] - !reference [.pipeline-stopper-vars, script] # merges in the master branch on PRs - - | - export BASE=$(curl -s -H "Authorization: Bearer ${GITHUB_PR_TOKEN}" https://api.github.com/repos/paritytech/substrate/pulls/${CI_COMMIT_REF_NAME} | jq .base.ref) - - if [ $CI_COMMIT_REF_NAME != "master" ]; then - git fetch origin +${BASE}:${BASE}; - git fetch origin +$CI_COMMIT_REF_NAME:$CI_COMMIT_REF_NAME; - git checkout ${BASE}; + - 'if [ $CI_COMMIT_REF_NAME != "master" ]; then + BASE=$(curl -s -H "Authorization: Bearer ${GITHUB_PR_TOKEN}" https://api.github.com/repos/paritytech/substrate/pulls/${CI_COMMIT_REF_NAME} | jq -r .base.ref); + printf "Merging base branch %s\n" "${BASE:=master}"; git config user.email "ci@gitlab.parity.io"; - git merge $CI_COMMIT_REF_NAME --verbose --no-edit; - fi + git fetch origin "refs/heads/${BASE}"; + git merge --verbose --no-edit FETCH_HEAD; + fi' parallel: 2 script: - rusty-cachier snapshot create From be9e305d2476fa68b4278fdd7eb00decb5b80331 Mon Sep 17 00:00:00 2001 From: Oliver Tale-Yazdi Date: Thu, 16 Mar 2023 17:57:45 +0100 Subject: [PATCH 247/558] Update all weights (#13614) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Empty commit * ".git/.scripts/commands/bench/bench.sh" all --------- Co-authored-by: Alexander Theißen Co-authored-by: command-bot <> --- frame/alliance/src/weights.rs | 523 ++-- frame/assets/src/weights.rs | 354 +-- frame/bags-list/src/weights.rs | 55 +- frame/balances/src/weights.rs | 100 +- frame/benchmarking/src/weights.rs | 70 +- frame/bounties/src/weights.rs | 199 +- frame/child-bounties/src/weights.rs | 143 +- frame/collective/src/weights.rs | 343 ++- frame/contracts/src/weights.rs | 2736 ++++++++--------- frame/conviction-voting/src/weights.rs | 143 +- frame/core-fellowship/src/weights.rs | 122 +- frame/democracy/src/weights.rs | 480 +-- .../src/weights.rs | 227 +- frame/elections-phragmen/src/weights.rs | 268 +- frame/fast-unstake/src/weights.rs | 399 ++- frame/glutton/src/weights.rs | 148 +- frame/identity/src/weights.rs | 455 ++- frame/im-online/src/weights.rs | 43 +- frame/indices/src/weights.rs | 83 +- frame/lottery/src/weights.rs | 95 +- frame/membership/src/weights.rs | 167 +- frame/message-queue/src/weights.rs | 118 +- frame/multisig/src/weights.rs | 183 +- frame/nfts/src/weights.rs | 526 ++-- frame/nis/src/weights.rs | 187 +- frame/nomination-pools/src/weights.rs | 290 +- frame/preimage/src/weights.rs | 175 +- frame/proxy/src/weights.rs | 275 +- frame/ranked-collective/src/weights.rs | 127 +- frame/recovery/src/weights.rs | 167 +- frame/referenda/src/weights.rs | 492 +-- frame/remark/src/weights.rs | 23 +- frame/salary/src/weights.rs | 97 +- frame/scheduler/src/weights.rs | 207 +- frame/session/src/weights.rs | 39 +- frame/staking/src/weights.rs | 555 ++-- frame/state-trie-migration/src/weights.rs | 99 +- frame/support/src/weights/block_weights.rs | 20 +- .../support/src/weights/extrinsic_weights.rs | 20 +- frame/system/src/weights.rs | 93 +- frame/timestamp/src/weights.rs | 27 +- frame/tips/src/weights.rs | 147 +- frame/transaction-storage/src/weights.rs | 55 +- frame/treasury/src/weights.rs | 111 +- frame/uniques/src/weights.rs | 471 +-- frame/utility/src/weights.rs | 71 +- frame/vesting/src/weights.rs | 263 +- frame/whitelist/src/weights.rs | 71 +- 48 files changed, 6019 insertions(+), 6043 deletions(-) diff --git a/frame/alliance/src/weights.rs b/frame/alliance/src/weights.rs index 8ff4bad31..a9756c2d2 100644 --- a/frame/alliance/src/weights.rs +++ b/frame/alliance/src/weights.rs @@ -18,9 +18,9 @@ //! Autogenerated weights for pallet_alliance //! //! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev -//! DATE: 2023-01-24, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! DATE: 2023-03-15, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` //! WORST CASE MAP SIZE: `1000000` -//! HOSTNAME: `bm2`, CPU: `Intel(R) Core(TM) i7-7700K CPU @ 4.20GHz` +//! HOSTNAME: `bm3`, CPU: `Intel(R) Core(TM) i7-7700K CPU @ 4.20GHz` //! EXECUTION: Some(Wasm), WASM-EXECUTION: Compiled, CHAIN: Some("dev"), DB CACHE: 1024 // Executed Command: @@ -31,6 +31,9 @@ // --steps=50 // --repeat=20 // --pallet=pallet_alliance +// --no-storage-info +// --no-median-slopes +// --no-min-squares // --extrinsic=* // --execution=wasm // --wasm-execution=compiled @@ -88,20 +91,20 @@ impl WeightInfo for SubstrateWeight { /// The range of component `p` is `[1, 100]`. fn propose_proposed(b: u32, m: u32, p: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `692 + m * (32 ±0) + p * (36 ±0)` - // Estimated: `11659 + m * (132 ±0) + p * (144 ±0)` - // Minimum execution time: 28_849 nanoseconds. - Weight::from_parts(29_823_933, 11659) - // Standard Error: 74 - .saturating_add(Weight::from_parts(830, 0).saturating_mul(b.into())) - // Standard Error: 775 - .saturating_add(Weight::from_parts(22_980, 0).saturating_mul(m.into())) - // Standard Error: 765 - .saturating_add(Weight::from_parts(90_520, 0).saturating_mul(p.into())) + // Measured: `618 + m * (32 ±0) + p * (36 ±0)` + // Estimated: `15463 + m * (124 ±0) + p * (148 ±0)` + // Minimum execution time: 32_788_000 picoseconds. + Weight::from_parts(36_725_375, 15463) + // Standard Error: 138 + .saturating_add(Weight::from_parts(1_252, 0).saturating_mul(b.into())) + // Standard Error: 1_445 + .saturating_add(Weight::from_parts(9_861, 0).saturating_mul(m.into())) + // Standard Error: 1_427 + .saturating_add(Weight::from_parts(127_304, 0).saturating_mul(p.into())) .saturating_add(T::DbWeight::get().reads(4_u64)) .saturating_add(T::DbWeight::get().writes(4_u64)) - .saturating_add(Weight::from_parts(0, 132).saturating_mul(m.into())) - .saturating_add(Weight::from_parts(0, 144).saturating_mul(p.into())) + .saturating_add(Weight::from_parts(0, 124).saturating_mul(m.into())) + .saturating_add(Weight::from_parts(0, 148).saturating_mul(p.into())) } /// Storage: Alliance Members (r:1 w:0) /// Proof: Alliance Members (max_values: None, max_size: Some(3211), added: 5686, mode: MaxEncodedLen) @@ -110,12 +113,12 @@ impl WeightInfo for SubstrateWeight { /// The range of component `m` is `[5, 100]`. fn vote(m: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `1177 + m * (64 ±0)` - // Estimated: `9337 + m * (64 ±0)` - // Minimum execution time: 23_570 nanoseconds. - Weight::from_parts(25_473_196, 9337) - // Standard Error: 824 - .saturating_add(Weight::from_parts(54_603, 0).saturating_mul(m.into())) + // Measured: `1042 + m * (64 ±0)` + // Estimated: `11182 + m * (64 ±0)` + // Minimum execution time: 26_632_000 picoseconds. + Weight::from_parts(28_918_089, 11182) + // Standard Error: 947 + .saturating_add(Weight::from_parts(56_565, 0).saturating_mul(m.into())) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) .saturating_add(Weight::from_parts(0, 64).saturating_mul(m.into())) @@ -134,17 +137,17 @@ impl WeightInfo for SubstrateWeight { /// The range of component `p` is `[1, 100]`. fn close_early_disapproved(m: u32, p: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `730 + m * (96 ±0) + p * (36 ±0)` - // Estimated: `11979 + m * (388 ±0) + p * (148 ±0)` - // Minimum execution time: 35_373 nanoseconds. - Weight::from_parts(32_763_656, 11979) - // Standard Error: 2_041 - .saturating_add(Weight::from_parts(52_915, 0).saturating_mul(m.into())) - // Standard Error: 1_991 - .saturating_add(Weight::from_parts(78_594, 0).saturating_mul(p.into())) + // Measured: `622 + m * (96 ±0) + p * (37 ±0)` + // Estimated: `15551 + m * (384 ±0) + p * (148 ±0)` + // Minimum execution time: 40_937_000 picoseconds. + Weight::from_parts(39_361_123, 15551) + // Standard Error: 1_609 + .saturating_add(Weight::from_parts(43_225, 0).saturating_mul(m.into())) + // Standard Error: 1_569 + .saturating_add(Weight::from_parts(112_935, 0).saturating_mul(p.into())) .saturating_add(T::DbWeight::get().reads(4_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) - .saturating_add(Weight::from_parts(0, 388).saturating_mul(m.into())) + .saturating_add(Weight::from_parts(0, 384).saturating_mul(m.into())) .saturating_add(Weight::from_parts(0, 148).saturating_mul(p.into())) } /// Storage: Alliance Members (r:1 w:0) @@ -162,20 +165,20 @@ impl WeightInfo for SubstrateWeight { /// The range of component `p` is `[1, 100]`. fn close_early_approved(b: u32, m: u32, p: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `1139 + m * (96 ±0) + p * (41 ±0)` - // Estimated: `15730 + m * (388 ±0) + p * (164 ±0)` - // Minimum execution time: 44_590 nanoseconds. - Weight::from_parts(43_367_913, 15730) - // Standard Error: 71 - .saturating_add(Weight::from_parts(819, 0).saturating_mul(b.into())) - // Standard Error: 751 - .saturating_add(Weight::from_parts(39_124, 0).saturating_mul(m.into())) - // Standard Error: 732 - .saturating_add(Weight::from_parts(90_469, 0).saturating_mul(p.into())) + // Measured: `903 + m * (96 ±0) + p * (41 ±0)` + // Estimated: `20072 + m * (392 ±0) + p * (160 ±0)` + // Minimum execution time: 51_569_000 picoseconds. + Weight::from_parts(50_722_448, 20072) + // Standard Error: 120 + .saturating_add(Weight::from_parts(474, 0).saturating_mul(b.into())) + // Standard Error: 1_271 + .saturating_add(Weight::from_parts(48_855, 0).saturating_mul(m.into())) + // Standard Error: 1_239 + .saturating_add(Weight::from_parts(139_022, 0).saturating_mul(p.into())) .saturating_add(T::DbWeight::get().reads(5_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) - .saturating_add(Weight::from_parts(0, 388).saturating_mul(m.into())) - .saturating_add(Weight::from_parts(0, 164).saturating_mul(p.into())) + .saturating_add(Weight::from_parts(0, 392).saturating_mul(m.into())) + .saturating_add(Weight::from_parts(0, 160).saturating_mul(p.into())) } /// Storage: Alliance Members (r:1 w:0) /// Proof: Alliance Members (max_values: None, max_size: Some(3211), added: 5686, mode: MaxEncodedLen) @@ -193,17 +196,17 @@ impl WeightInfo for SubstrateWeight { /// The range of component `p` is `[1, 100]`. fn close_disapproved(m: u32, p: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `730 + m * (96 ±0) + p * (36 ±0)` - // Estimated: `13201 + m * (485 ±0) + p * (185 ±0)` - // Minimum execution time: 36_796 nanoseconds. - Weight::from_parts(34_578_765, 13201) - // Standard Error: 1_037 - .saturating_add(Weight::from_parts(43_508, 0).saturating_mul(m.into())) - // Standard Error: 1_024 - .saturating_add(Weight::from_parts(77_084, 0).saturating_mul(p.into())) + // Measured: `622 + m * (96 ±0) + p * (37 ±0)` + // Estimated: `17666 + m * (480 ±0) + p * (185 ±0)` + // Minimum execution time: 42_084_000 picoseconds. + Weight::from_parts(39_907_823, 17666) + // Standard Error: 899 + .saturating_add(Weight::from_parts(46_642, 0).saturating_mul(m.into())) + // Standard Error: 888 + .saturating_add(Weight::from_parts(114_161, 0).saturating_mul(p.into())) .saturating_add(T::DbWeight::get().reads(5_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) - .saturating_add(Weight::from_parts(0, 485).saturating_mul(m.into())) + .saturating_add(Weight::from_parts(0, 480).saturating_mul(m.into())) .saturating_add(Weight::from_parts(0, 185).saturating_mul(p.into())) } /// Storage: Alliance Members (r:1 w:0) @@ -223,20 +226,20 @@ impl WeightInfo for SubstrateWeight { /// The range of component `p` is `[1, 100]`. fn close_approved(b: u32, m: u32, p: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `757 + m * (96 ±0) + p * (36 ±0)` - // Estimated: `13146 + m * (480 ±0) + p * (185 ±0)` - // Minimum execution time: 36_897 nanoseconds. - Weight::from_parts(34_169_666, 13146) - // Standard Error: 47 - .saturating_add(Weight::from_parts(972, 0).saturating_mul(b.into())) - // Standard Error: 510 - .saturating_add(Weight::from_parts(38_084, 0).saturating_mul(m.into())) - // Standard Error: 492 - .saturating_add(Weight::from_parts(78_576, 0).saturating_mul(p.into())) + // Measured: `591 + m * (96 ±0) + p * (36 ±0)` + // Estimated: `17471 + m * (485 ±0) + p * (180 ±0)` + // Minimum execution time: 42_322_000 picoseconds. + Weight::from_parts(38_753_429, 17471) + // Standard Error: 78 + .saturating_add(Weight::from_parts(1_053, 0).saturating_mul(b.into())) + // Standard Error: 841 + .saturating_add(Weight::from_parts(51_720, 0).saturating_mul(m.into())) + // Standard Error: 811 + .saturating_add(Weight::from_parts(113_343, 0).saturating_mul(p.into())) .saturating_add(T::DbWeight::get().reads(5_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) - .saturating_add(Weight::from_parts(0, 480).saturating_mul(m.into())) - .saturating_add(Weight::from_parts(0, 185).saturating_mul(p.into())) + .saturating_add(Weight::from_parts(0, 485).saturating_mul(m.into())) + .saturating_add(Weight::from_parts(0, 180).saturating_mul(p.into())) } /// Storage: Alliance Members (r:2 w:2) /// Proof: Alliance Members (max_values: None, max_size: Some(3211), added: 5686, mode: MaxEncodedLen) @@ -247,13 +250,13 @@ impl WeightInfo for SubstrateWeight { fn init_members(m: u32, z: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `217` - // Estimated: `12084` - // Minimum execution time: 29_313 nanoseconds. - Weight::from_parts(20_502_244, 12084) - // Standard Error: 304 - .saturating_add(Weight::from_parts(107_994, 0).saturating_mul(m.into())) - // Standard Error: 300 - .saturating_add(Weight::from_parts(92_645, 0).saturating_mul(z.into())) + // Estimated: `14064` + // Minimum execution time: 33_217_000 picoseconds. + Weight::from_parts(24_495_505, 14064) + // Standard Error: 412 + .saturating_add(Weight::from_parts(108_405, 0).saturating_mul(m.into())) + // Standard Error: 407 + .saturating_add(Weight::from_parts(95_707, 0).saturating_mul(z.into())) .saturating_add(T::DbWeight::get().reads(3_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) } @@ -274,16 +277,16 @@ impl WeightInfo for SubstrateWeight { /// The range of component `z` is `[0, 50]`. fn disband(x: u32, y: u32, z: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `0 + x * (50 ±0) + y * (51 ±0) + z * (283 ±0)` - // Estimated: `32201 + x * (2587 ±0) + y * (2590 ±0) + z * (3209 ±1)` - // Minimum execution time: 232_895 nanoseconds. - Weight::from_parts(233_860_000, 32201) - // Standard Error: 19_092 - .saturating_add(Weight::from_parts(445_664, 0).saturating_mul(x.into())) - // Standard Error: 19_000 - .saturating_add(Weight::from_parts(432_571, 0).saturating_mul(y.into())) - // Standard Error: 37_965 - .saturating_add(Weight::from_parts(9_386_151, 0).saturating_mul(z.into())) + // Measured: `0 + x * (50 ±0) + y * (51 ±0) + z * (251 ±0)` + // Estimated: `35975 + x * (2587 ±0) + y * (2590 ±0) + z * (3113 ±1)` + // Minimum execution time: 278_029_000 picoseconds. + Weight::from_parts(279_353_000, 35975) + // Standard Error: 23_655 + .saturating_add(Weight::from_parts(519_330, 0).saturating_mul(x.into())) + // Standard Error: 23_541 + .saturating_add(Weight::from_parts(547_382, 0).saturating_mul(y.into())) + // Standard Error: 47_040 + .saturating_add(Weight::from_parts(10_939_685, 0).saturating_mul(z.into())) .saturating_add(T::DbWeight::get().reads(3_u64)) .saturating_add(T::DbWeight::get().reads((1_u64).saturating_mul(x.into()))) .saturating_add(T::DbWeight::get().reads((1_u64).saturating_mul(y.into()))) @@ -292,7 +295,7 @@ impl WeightInfo for SubstrateWeight { .saturating_add(T::DbWeight::get().writes((2_u64).saturating_mul(z.into()))) .saturating_add(Weight::from_parts(0, 2587).saturating_mul(x.into())) .saturating_add(Weight::from_parts(0, 2590).saturating_mul(y.into())) - .saturating_add(Weight::from_parts(0, 3209).saturating_mul(z.into())) + .saturating_add(Weight::from_parts(0, 3113).saturating_mul(z.into())) } /// Storage: Alliance Rule (r:0 w:1) /// Proof: Alliance Rule (max_values: Some(1), max_size: Some(87), added: 582, mode: MaxEncodedLen) @@ -300,8 +303,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 9_156 nanoseconds. - Weight::from_parts(9_376_000, 0) + // Minimum execution time: 10_129_000 picoseconds. + Weight::from_parts(10_370_000, 0) .saturating_add(T::DbWeight::get().writes(1_u64)) } /// Storage: Alliance Announcements (r:1 w:1) @@ -309,9 +312,9 @@ impl WeightInfo for SubstrateWeight { fn announce() -> Weight { // Proof Size summary in bytes: // Measured: `246` - // Estimated: `9197` - // Minimum execution time: 12_163 nanoseconds. - Weight::from_parts(12_397_000, 9197) + // Estimated: `10187` + // Minimum execution time: 13_601_000 picoseconds. + Weight::from_parts(13_823_000, 10187) .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -319,10 +322,10 @@ impl WeightInfo for SubstrateWeight { /// Proof: Alliance Announcements (max_values: Some(1), max_size: Some(8702), added: 9197, mode: MaxEncodedLen) fn remove_announcement() -> Weight { // Proof Size summary in bytes: - // Measured: `351` - // Estimated: `9197` - // Minimum execution time: 12_778 nanoseconds. - Weight::from_parts(12_991_000, 9197) + // Measured: `319` + // Estimated: `10187` + // Minimum execution time: 14_953_000 picoseconds. + Weight::from_parts(15_239_000, 10187) .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -336,10 +339,10 @@ impl WeightInfo for SubstrateWeight { /// Proof: Alliance DepositOf (max_values: None, max_size: Some(64), added: 2539, mode: MaxEncodedLen) fn join_alliance() -> Weight { // Proof Size summary in bytes: - // Measured: `562` - // Estimated: `23358` - // Minimum execution time: 40_216 nanoseconds. - Weight::from_parts(40_535_000, 23358) + // Measured: `468` + // Estimated: `26328` + // Minimum execution time: 42_947_000 picoseconds. + Weight::from_parts(43_602_000, 26328) .saturating_add(T::DbWeight::get().reads(5_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) } @@ -349,10 +352,10 @@ impl WeightInfo for SubstrateWeight { /// Proof: Alliance UnscrupulousAccounts (max_values: Some(1), max_size: Some(3202), added: 3697, mode: MaxEncodedLen) fn nominate_ally() -> Weight { // Proof Size summary in bytes: - // Measured: `429` - // Estimated: `20755` - // Minimum execution time: 28_027 nanoseconds. - Weight::from_parts(28_392_000, 20755) + // Measured: `367` + // Estimated: `22735` + // Minimum execution time: 28_787_000 picoseconds. + Weight::from_parts(29_286_000, 22735) .saturating_add(T::DbWeight::get().reads(4_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -366,10 +369,10 @@ impl WeightInfo for SubstrateWeight { /// Proof Skipped: AllianceMotion Prime (max_values: Some(1), max_size: None, mode: Measured) fn elevate_ally() -> Weight { // Proof Size summary in bytes: - // Measured: `505` - // Estimated: `13382` - // Minimum execution time: 24_427 nanoseconds. - Weight::from_parts(24_952_000, 13382) + // Measured: `443` + // Estimated: `15176` + // Minimum execution time: 28_476_000 picoseconds. + Weight::from_parts(28_972_000, 15176) .saturating_add(T::DbWeight::get().reads(3_u64)) .saturating_add(T::DbWeight::get().writes(4_u64)) } @@ -385,10 +388,10 @@ impl WeightInfo for SubstrateWeight { /// Proof: Alliance RetiringMembers (max_values: None, max_size: Some(52), added: 2527, mode: MaxEncodedLen) fn give_retirement_notice() -> Weight { // Proof Size summary in bytes: - // Measured: `505` - // Estimated: `24754` - // Minimum execution time: 33_163 nanoseconds. - Weight::from_parts(33_556_000, 24754) + // Measured: `443` + // Estimated: `26548` + // Minimum execution time: 36_773_000 picoseconds. + Weight::from_parts(38_123_000, 26548) .saturating_add(T::DbWeight::get().reads(5_u64)) .saturating_add(T::DbWeight::get().writes(5_u64)) } @@ -402,10 +405,10 @@ impl WeightInfo for SubstrateWeight { /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) fn retire() -> Weight { // Proof Size summary in bytes: - // Measured: `750` - // Estimated: `13355` - // Minimum execution time: 32_980 nanoseconds. - Weight::from_parts(33_452_000, 13355) + // Measured: `687` + // Estimated: `17315` + // Minimum execution time: 37_814_000 picoseconds. + Weight::from_parts(38_353_000, 17315) .saturating_add(T::DbWeight::get().reads(4_u64)) .saturating_add(T::DbWeight::get().writes(4_u64)) } @@ -423,10 +426,10 @@ impl WeightInfo for SubstrateWeight { /// Proof Skipped: AllianceMotion Prime (max_values: Some(1), max_size: None, mode: Measured) fn kick_member() -> Weight { // Proof Size summary in bytes: - // Measured: `801` - // Estimated: `25098` - // Minimum execution time: 54_660 nanoseconds. - Weight::from_parts(55_186_000, 25098) + // Measured: `707` + // Estimated: `28776` + // Minimum execution time: 62_194_000 picoseconds. + Weight::from_parts(63_925_000, 28776) .saturating_add(T::DbWeight::get().reads(6_u64)) .saturating_add(T::DbWeight::get().writes(5_u64)) } @@ -439,13 +442,13 @@ impl WeightInfo for SubstrateWeight { fn add_unscrupulous_items(n: u32, l: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `246` - // Estimated: `29894` - // Minimum execution time: 7_709 nanoseconds. - Weight::from_parts(7_773_000, 29894) - // Standard Error: 2_645 - .saturating_add(Weight::from_parts(1_266_755, 0).saturating_mul(n.into())) - // Standard Error: 1_036 - .saturating_add(Weight::from_parts(67_359, 0).saturating_mul(l.into())) + // Estimated: `31874` + // Minimum execution time: 8_369_000 picoseconds. + Weight::from_parts(8_462_000, 31874) + // Standard Error: 3_347 + .saturating_add(Weight::from_parts(1_567_757, 0).saturating_mul(n.into())) + // Standard Error: 1_310 + .saturating_add(Weight::from_parts(72_697, 0).saturating_mul(l.into())) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().writes(2_u64)) } @@ -458,13 +461,13 @@ impl WeightInfo for SubstrateWeight { fn remove_unscrupulous_items(n: u32, l: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0 + n * (289 ±0) + l * (100 ±0)` - // Estimated: `29894` - // Minimum execution time: 7_438 nanoseconds. - Weight::from_parts(7_570_000, 29894) - // Standard Error: 165_072 - .saturating_add(Weight::from_parts(13_026_975, 0).saturating_mul(n.into())) - // Standard Error: 64_649 - .saturating_add(Weight::from_parts(485_565, 0).saturating_mul(l.into())) + // Estimated: `31874` + // Minimum execution time: 8_309_000 picoseconds. + Weight::from_parts(8_450_000, 31874) + // Standard Error: 185_163 + .saturating_add(Weight::from_parts(16_348_419, 0).saturating_mul(n.into())) + // Standard Error: 72_518 + .saturating_add(Weight::from_parts(337_571, 0).saturating_mul(l.into())) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().writes(2_u64)) } @@ -478,10 +481,10 @@ impl WeightInfo for SubstrateWeight { /// Proof Skipped: AllianceMotion Prime (max_values: Some(1), max_size: None, mode: Measured) fn abdicate_fellow_status() -> Weight { // Proof Size summary in bytes: - // Measured: `505` - // Estimated: `19068` - // Minimum execution time: 31_326 nanoseconds. - Weight::from_parts(31_768_000, 19068) + // Measured: `443` + // Estimated: `20862` + // Minimum execution time: 35_363_000 picoseconds. + Weight::from_parts(35_665_000, 20862) .saturating_add(T::DbWeight::get().reads(4_u64)) .saturating_add(T::DbWeight::get().writes(4_u64)) } @@ -504,20 +507,20 @@ impl WeightInfo for () { /// The range of component `p` is `[1, 100]`. fn propose_proposed(b: u32, m: u32, p: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `692 + m * (32 ±0) + p * (36 ±0)` - // Estimated: `11659 + m * (132 ±0) + p * (144 ±0)` - // Minimum execution time: 28_849 nanoseconds. - Weight::from_parts(29_823_933, 11659) - // Standard Error: 74 - .saturating_add(Weight::from_parts(830, 0).saturating_mul(b.into())) - // Standard Error: 775 - .saturating_add(Weight::from_parts(22_980, 0).saturating_mul(m.into())) - // Standard Error: 765 - .saturating_add(Weight::from_parts(90_520, 0).saturating_mul(p.into())) + // Measured: `618 + m * (32 ±0) + p * (36 ±0)` + // Estimated: `15463 + m * (124 ±0) + p * (148 ±0)` + // Minimum execution time: 32_788_000 picoseconds. + Weight::from_parts(36_725_375, 15463) + // Standard Error: 138 + .saturating_add(Weight::from_parts(1_252, 0).saturating_mul(b.into())) + // Standard Error: 1_445 + .saturating_add(Weight::from_parts(9_861, 0).saturating_mul(m.into())) + // Standard Error: 1_427 + .saturating_add(Weight::from_parts(127_304, 0).saturating_mul(p.into())) .saturating_add(RocksDbWeight::get().reads(4_u64)) .saturating_add(RocksDbWeight::get().writes(4_u64)) - .saturating_add(Weight::from_parts(0, 132).saturating_mul(m.into())) - .saturating_add(Weight::from_parts(0, 144).saturating_mul(p.into())) + .saturating_add(Weight::from_parts(0, 124).saturating_mul(m.into())) + .saturating_add(Weight::from_parts(0, 148).saturating_mul(p.into())) } /// Storage: Alliance Members (r:1 w:0) /// Proof: Alliance Members (max_values: None, max_size: Some(3211), added: 5686, mode: MaxEncodedLen) @@ -526,12 +529,12 @@ impl WeightInfo for () { /// The range of component `m` is `[5, 100]`. fn vote(m: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `1177 + m * (64 ±0)` - // Estimated: `9337 + m * (64 ±0)` - // Minimum execution time: 23_570 nanoseconds. - Weight::from_parts(25_473_196, 9337) - // Standard Error: 824 - .saturating_add(Weight::from_parts(54_603, 0).saturating_mul(m.into())) + // Measured: `1042 + m * (64 ±0)` + // Estimated: `11182 + m * (64 ±0)` + // Minimum execution time: 26_632_000 picoseconds. + Weight::from_parts(28_918_089, 11182) + // Standard Error: 947 + .saturating_add(Weight::from_parts(56_565, 0).saturating_mul(m.into())) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) .saturating_add(Weight::from_parts(0, 64).saturating_mul(m.into())) @@ -550,17 +553,17 @@ impl WeightInfo for () { /// The range of component `p` is `[1, 100]`. fn close_early_disapproved(m: u32, p: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `730 + m * (96 ±0) + p * (36 ±0)` - // Estimated: `11979 + m * (388 ±0) + p * (148 ±0)` - // Minimum execution time: 35_373 nanoseconds. - Weight::from_parts(32_763_656, 11979) - // Standard Error: 2_041 - .saturating_add(Weight::from_parts(52_915, 0).saturating_mul(m.into())) - // Standard Error: 1_991 - .saturating_add(Weight::from_parts(78_594, 0).saturating_mul(p.into())) + // Measured: `622 + m * (96 ±0) + p * (37 ±0)` + // Estimated: `15551 + m * (384 ±0) + p * (148 ±0)` + // Minimum execution time: 40_937_000 picoseconds. + Weight::from_parts(39_361_123, 15551) + // Standard Error: 1_609 + .saturating_add(Weight::from_parts(43_225, 0).saturating_mul(m.into())) + // Standard Error: 1_569 + .saturating_add(Weight::from_parts(112_935, 0).saturating_mul(p.into())) .saturating_add(RocksDbWeight::get().reads(4_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) - .saturating_add(Weight::from_parts(0, 388).saturating_mul(m.into())) + .saturating_add(Weight::from_parts(0, 384).saturating_mul(m.into())) .saturating_add(Weight::from_parts(0, 148).saturating_mul(p.into())) } /// Storage: Alliance Members (r:1 w:0) @@ -578,20 +581,20 @@ impl WeightInfo for () { /// The range of component `p` is `[1, 100]`. fn close_early_approved(b: u32, m: u32, p: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `1139 + m * (96 ±0) + p * (41 ±0)` - // Estimated: `15730 + m * (388 ±0) + p * (164 ±0)` - // Minimum execution time: 44_590 nanoseconds. - Weight::from_parts(43_367_913, 15730) - // Standard Error: 71 - .saturating_add(Weight::from_parts(819, 0).saturating_mul(b.into())) - // Standard Error: 751 - .saturating_add(Weight::from_parts(39_124, 0).saturating_mul(m.into())) - // Standard Error: 732 - .saturating_add(Weight::from_parts(90_469, 0).saturating_mul(p.into())) + // Measured: `903 + m * (96 ±0) + p * (41 ±0)` + // Estimated: `20072 + m * (392 ±0) + p * (160 ±0)` + // Minimum execution time: 51_569_000 picoseconds. + Weight::from_parts(50_722_448, 20072) + // Standard Error: 120 + .saturating_add(Weight::from_parts(474, 0).saturating_mul(b.into())) + // Standard Error: 1_271 + .saturating_add(Weight::from_parts(48_855, 0).saturating_mul(m.into())) + // Standard Error: 1_239 + .saturating_add(Weight::from_parts(139_022, 0).saturating_mul(p.into())) .saturating_add(RocksDbWeight::get().reads(5_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) - .saturating_add(Weight::from_parts(0, 388).saturating_mul(m.into())) - .saturating_add(Weight::from_parts(0, 164).saturating_mul(p.into())) + .saturating_add(Weight::from_parts(0, 392).saturating_mul(m.into())) + .saturating_add(Weight::from_parts(0, 160).saturating_mul(p.into())) } /// Storage: Alliance Members (r:1 w:0) /// Proof: Alliance Members (max_values: None, max_size: Some(3211), added: 5686, mode: MaxEncodedLen) @@ -609,17 +612,17 @@ impl WeightInfo for () { /// The range of component `p` is `[1, 100]`. fn close_disapproved(m: u32, p: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `730 + m * (96 ±0) + p * (36 ±0)` - // Estimated: `13201 + m * (485 ±0) + p * (185 ±0)` - // Minimum execution time: 36_796 nanoseconds. - Weight::from_parts(34_578_765, 13201) - // Standard Error: 1_037 - .saturating_add(Weight::from_parts(43_508, 0).saturating_mul(m.into())) - // Standard Error: 1_024 - .saturating_add(Weight::from_parts(77_084, 0).saturating_mul(p.into())) + // Measured: `622 + m * (96 ±0) + p * (37 ±0)` + // Estimated: `17666 + m * (480 ±0) + p * (185 ±0)` + // Minimum execution time: 42_084_000 picoseconds. + Weight::from_parts(39_907_823, 17666) + // Standard Error: 899 + .saturating_add(Weight::from_parts(46_642, 0).saturating_mul(m.into())) + // Standard Error: 888 + .saturating_add(Weight::from_parts(114_161, 0).saturating_mul(p.into())) .saturating_add(RocksDbWeight::get().reads(5_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) - .saturating_add(Weight::from_parts(0, 485).saturating_mul(m.into())) + .saturating_add(Weight::from_parts(0, 480).saturating_mul(m.into())) .saturating_add(Weight::from_parts(0, 185).saturating_mul(p.into())) } /// Storage: Alliance Members (r:1 w:0) @@ -639,20 +642,20 @@ impl WeightInfo for () { /// The range of component `p` is `[1, 100]`. fn close_approved(b: u32, m: u32, p: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `757 + m * (96 ±0) + p * (36 ±0)` - // Estimated: `13146 + m * (480 ±0) + p * (185 ±0)` - // Minimum execution time: 36_897 nanoseconds. - Weight::from_parts(34_169_666, 13146) - // Standard Error: 47 - .saturating_add(Weight::from_parts(972, 0).saturating_mul(b.into())) - // Standard Error: 510 - .saturating_add(Weight::from_parts(38_084, 0).saturating_mul(m.into())) - // Standard Error: 492 - .saturating_add(Weight::from_parts(78_576, 0).saturating_mul(p.into())) + // Measured: `591 + m * (96 ±0) + p * (36 ±0)` + // Estimated: `17471 + m * (485 ±0) + p * (180 ±0)` + // Minimum execution time: 42_322_000 picoseconds. + Weight::from_parts(38_753_429, 17471) + // Standard Error: 78 + .saturating_add(Weight::from_parts(1_053, 0).saturating_mul(b.into())) + // Standard Error: 841 + .saturating_add(Weight::from_parts(51_720, 0).saturating_mul(m.into())) + // Standard Error: 811 + .saturating_add(Weight::from_parts(113_343, 0).saturating_mul(p.into())) .saturating_add(RocksDbWeight::get().reads(5_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) - .saturating_add(Weight::from_parts(0, 480).saturating_mul(m.into())) - .saturating_add(Weight::from_parts(0, 185).saturating_mul(p.into())) + .saturating_add(Weight::from_parts(0, 485).saturating_mul(m.into())) + .saturating_add(Weight::from_parts(0, 180).saturating_mul(p.into())) } /// Storage: Alliance Members (r:2 w:2) /// Proof: Alliance Members (max_values: None, max_size: Some(3211), added: 5686, mode: MaxEncodedLen) @@ -663,13 +666,13 @@ impl WeightInfo for () { fn init_members(m: u32, z: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `217` - // Estimated: `12084` - // Minimum execution time: 29_313 nanoseconds. - Weight::from_parts(20_502_244, 12084) - // Standard Error: 304 - .saturating_add(Weight::from_parts(107_994, 0).saturating_mul(m.into())) - // Standard Error: 300 - .saturating_add(Weight::from_parts(92_645, 0).saturating_mul(z.into())) + // Estimated: `14064` + // Minimum execution time: 33_217_000 picoseconds. + Weight::from_parts(24_495_505, 14064) + // Standard Error: 412 + .saturating_add(Weight::from_parts(108_405, 0).saturating_mul(m.into())) + // Standard Error: 407 + .saturating_add(Weight::from_parts(95_707, 0).saturating_mul(z.into())) .saturating_add(RocksDbWeight::get().reads(3_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) } @@ -690,16 +693,16 @@ impl WeightInfo for () { /// The range of component `z` is `[0, 50]`. fn disband(x: u32, y: u32, z: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `0 + x * (50 ±0) + y * (51 ±0) + z * (283 ±0)` - // Estimated: `32201 + x * (2587 ±0) + y * (2590 ±0) + z * (3209 ±1)` - // Minimum execution time: 232_895 nanoseconds. - Weight::from_parts(233_860_000, 32201) - // Standard Error: 19_092 - .saturating_add(Weight::from_parts(445_664, 0).saturating_mul(x.into())) - // Standard Error: 19_000 - .saturating_add(Weight::from_parts(432_571, 0).saturating_mul(y.into())) - // Standard Error: 37_965 - .saturating_add(Weight::from_parts(9_386_151, 0).saturating_mul(z.into())) + // Measured: `0 + x * (50 ±0) + y * (51 ±0) + z * (251 ±0)` + // Estimated: `35975 + x * (2587 ±0) + y * (2590 ±0) + z * (3113 ±1)` + // Minimum execution time: 278_029_000 picoseconds. + Weight::from_parts(279_353_000, 35975) + // Standard Error: 23_655 + .saturating_add(Weight::from_parts(519_330, 0).saturating_mul(x.into())) + // Standard Error: 23_541 + .saturating_add(Weight::from_parts(547_382, 0).saturating_mul(y.into())) + // Standard Error: 47_040 + .saturating_add(Weight::from_parts(10_939_685, 0).saturating_mul(z.into())) .saturating_add(RocksDbWeight::get().reads(3_u64)) .saturating_add(RocksDbWeight::get().reads((1_u64).saturating_mul(x.into()))) .saturating_add(RocksDbWeight::get().reads((1_u64).saturating_mul(y.into()))) @@ -708,7 +711,7 @@ impl WeightInfo for () { .saturating_add(RocksDbWeight::get().writes((2_u64).saturating_mul(z.into()))) .saturating_add(Weight::from_parts(0, 2587).saturating_mul(x.into())) .saturating_add(Weight::from_parts(0, 2590).saturating_mul(y.into())) - .saturating_add(Weight::from_parts(0, 3209).saturating_mul(z.into())) + .saturating_add(Weight::from_parts(0, 3113).saturating_mul(z.into())) } /// Storage: Alliance Rule (r:0 w:1) /// Proof: Alliance Rule (max_values: Some(1), max_size: Some(87), added: 582, mode: MaxEncodedLen) @@ -716,8 +719,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 9_156 nanoseconds. - Weight::from_parts(9_376_000, 0) + // Minimum execution time: 10_129_000 picoseconds. + Weight::from_parts(10_370_000, 0) .saturating_add(RocksDbWeight::get().writes(1_u64)) } /// Storage: Alliance Announcements (r:1 w:1) @@ -725,9 +728,9 @@ impl WeightInfo for () { fn announce() -> Weight { // Proof Size summary in bytes: // Measured: `246` - // Estimated: `9197` - // Minimum execution time: 12_163 nanoseconds. - Weight::from_parts(12_397_000, 9197) + // Estimated: `10187` + // Minimum execution time: 13_601_000 picoseconds. + Weight::from_parts(13_823_000, 10187) .saturating_add(RocksDbWeight::get().reads(1_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } @@ -735,10 +738,10 @@ impl WeightInfo for () { /// Proof: Alliance Announcements (max_values: Some(1), max_size: Some(8702), added: 9197, mode: MaxEncodedLen) fn remove_announcement() -> Weight { // Proof Size summary in bytes: - // Measured: `351` - // Estimated: `9197` - // Minimum execution time: 12_778 nanoseconds. - Weight::from_parts(12_991_000, 9197) + // Measured: `319` + // Estimated: `10187` + // Minimum execution time: 14_953_000 picoseconds. + Weight::from_parts(15_239_000, 10187) .saturating_add(RocksDbWeight::get().reads(1_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } @@ -752,10 +755,10 @@ impl WeightInfo for () { /// Proof: Alliance DepositOf (max_values: None, max_size: Some(64), added: 2539, mode: MaxEncodedLen) fn join_alliance() -> Weight { // Proof Size summary in bytes: - // Measured: `562` - // Estimated: `23358` - // Minimum execution time: 40_216 nanoseconds. - Weight::from_parts(40_535_000, 23358) + // Measured: `468` + // Estimated: `26328` + // Minimum execution time: 42_947_000 picoseconds. + Weight::from_parts(43_602_000, 26328) .saturating_add(RocksDbWeight::get().reads(5_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) } @@ -765,10 +768,10 @@ impl WeightInfo for () { /// Proof: Alliance UnscrupulousAccounts (max_values: Some(1), max_size: Some(3202), added: 3697, mode: MaxEncodedLen) fn nominate_ally() -> Weight { // Proof Size summary in bytes: - // Measured: `429` - // Estimated: `20755` - // Minimum execution time: 28_027 nanoseconds. - Weight::from_parts(28_392_000, 20755) + // Measured: `367` + // Estimated: `22735` + // Minimum execution time: 28_787_000 picoseconds. + Weight::from_parts(29_286_000, 22735) .saturating_add(RocksDbWeight::get().reads(4_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } @@ -782,10 +785,10 @@ impl WeightInfo for () { /// Proof Skipped: AllianceMotion Prime (max_values: Some(1), max_size: None, mode: Measured) fn elevate_ally() -> Weight { // Proof Size summary in bytes: - // Measured: `505` - // Estimated: `13382` - // Minimum execution time: 24_427 nanoseconds. - Weight::from_parts(24_952_000, 13382) + // Measured: `443` + // Estimated: `15176` + // Minimum execution time: 28_476_000 picoseconds. + Weight::from_parts(28_972_000, 15176) .saturating_add(RocksDbWeight::get().reads(3_u64)) .saturating_add(RocksDbWeight::get().writes(4_u64)) } @@ -801,10 +804,10 @@ impl WeightInfo for () { /// Proof: Alliance RetiringMembers (max_values: None, max_size: Some(52), added: 2527, mode: MaxEncodedLen) fn give_retirement_notice() -> Weight { // Proof Size summary in bytes: - // Measured: `505` - // Estimated: `24754` - // Minimum execution time: 33_163 nanoseconds. - Weight::from_parts(33_556_000, 24754) + // Measured: `443` + // Estimated: `26548` + // Minimum execution time: 36_773_000 picoseconds. + Weight::from_parts(38_123_000, 26548) .saturating_add(RocksDbWeight::get().reads(5_u64)) .saturating_add(RocksDbWeight::get().writes(5_u64)) } @@ -818,10 +821,10 @@ impl WeightInfo for () { /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) fn retire() -> Weight { // Proof Size summary in bytes: - // Measured: `750` - // Estimated: `13355` - // Minimum execution time: 32_980 nanoseconds. - Weight::from_parts(33_452_000, 13355) + // Measured: `687` + // Estimated: `17315` + // Minimum execution time: 37_814_000 picoseconds. + Weight::from_parts(38_353_000, 17315) .saturating_add(RocksDbWeight::get().reads(4_u64)) .saturating_add(RocksDbWeight::get().writes(4_u64)) } @@ -839,10 +842,10 @@ impl WeightInfo for () { /// Proof Skipped: AllianceMotion Prime (max_values: Some(1), max_size: None, mode: Measured) fn kick_member() -> Weight { // Proof Size summary in bytes: - // Measured: `801` - // Estimated: `25098` - // Minimum execution time: 54_660 nanoseconds. - Weight::from_parts(55_186_000, 25098) + // Measured: `707` + // Estimated: `28776` + // Minimum execution time: 62_194_000 picoseconds. + Weight::from_parts(63_925_000, 28776) .saturating_add(RocksDbWeight::get().reads(6_u64)) .saturating_add(RocksDbWeight::get().writes(5_u64)) } @@ -855,13 +858,13 @@ impl WeightInfo for () { fn add_unscrupulous_items(n: u32, l: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `246` - // Estimated: `29894` - // Minimum execution time: 7_709 nanoseconds. - Weight::from_parts(7_773_000, 29894) - // Standard Error: 2_645 - .saturating_add(Weight::from_parts(1_266_755, 0).saturating_mul(n.into())) - // Standard Error: 1_036 - .saturating_add(Weight::from_parts(67_359, 0).saturating_mul(l.into())) + // Estimated: `31874` + // Minimum execution time: 8_369_000 picoseconds. + Weight::from_parts(8_462_000, 31874) + // Standard Error: 3_347 + .saturating_add(Weight::from_parts(1_567_757, 0).saturating_mul(n.into())) + // Standard Error: 1_310 + .saturating_add(Weight::from_parts(72_697, 0).saturating_mul(l.into())) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().writes(2_u64)) } @@ -874,13 +877,13 @@ impl WeightInfo for () { fn remove_unscrupulous_items(n: u32, l: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0 + n * (289 ±0) + l * (100 ±0)` - // Estimated: `29894` - // Minimum execution time: 7_438 nanoseconds. - Weight::from_parts(7_570_000, 29894) - // Standard Error: 165_072 - .saturating_add(Weight::from_parts(13_026_975, 0).saturating_mul(n.into())) - // Standard Error: 64_649 - .saturating_add(Weight::from_parts(485_565, 0).saturating_mul(l.into())) + // Estimated: `31874` + // Minimum execution time: 8_309_000 picoseconds. + Weight::from_parts(8_450_000, 31874) + // Standard Error: 185_163 + .saturating_add(Weight::from_parts(16_348_419, 0).saturating_mul(n.into())) + // Standard Error: 72_518 + .saturating_add(Weight::from_parts(337_571, 0).saturating_mul(l.into())) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().writes(2_u64)) } @@ -894,10 +897,10 @@ impl WeightInfo for () { /// Proof Skipped: AllianceMotion Prime (max_values: Some(1), max_size: None, mode: Measured) fn abdicate_fellow_status() -> Weight { // Proof Size summary in bytes: - // Measured: `505` - // Estimated: `19068` - // Minimum execution time: 31_326 nanoseconds. - Weight::from_parts(31_768_000, 19068) + // Measured: `443` + // Estimated: `20862` + // Minimum execution time: 35_363_000 picoseconds. + Weight::from_parts(35_665_000, 20862) .saturating_add(RocksDbWeight::get().reads(4_u64)) .saturating_add(RocksDbWeight::get().writes(4_u64)) } diff --git a/frame/assets/src/weights.rs b/frame/assets/src/weights.rs index b9c3ab21d..0d6c41b45 100644 --- a/frame/assets/src/weights.rs +++ b/frame/assets/src/weights.rs @@ -18,26 +18,28 @@ //! Autogenerated weights for pallet_assets //! //! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev -//! DATE: 2023-03-03, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! DATE: 2023-03-15, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` //! WORST CASE MAP SIZE: `1000000` //! HOSTNAME: `bm3`, CPU: `Intel(R) Core(TM) i7-7700K CPU @ 4.20GHz` //! EXECUTION: Some(Wasm), WASM-EXECUTION: Compiled, CHAIN: Some("dev"), DB CACHE: 1024 // Executed Command: -// target/production/substrate +// ./target/production/substrate // benchmark // pallet +// --chain=dev // --steps=50 // --repeat=20 +// --pallet=pallet_assets +// --no-storage-info +// --no-median-slopes +// --no-min-squares // --extrinsic=* // --execution=wasm // --wasm-execution=compiled // --heap-pages=4096 -// --json-file=/var/lib/gitlab-runner/builds/zyw4fam_/0/parity/mirrors/substrate/.git/.artifacts/bench.json -// --pallet=pallet_assets -// --chain=dev -// --header=./HEADER-APACHE2 // --output=./frame/assets/src/weights.rs +// --header=./HEADER-APACHE2 // --template=./.maintain/frame-weight-template.hbs #![cfg_attr(rustfmt, rustfmt_skip)] @@ -87,10 +89,10 @@ impl WeightInfo for SubstrateWeight { /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) fn create() -> Weight { // Proof Size summary in bytes: - // Measured: `325` + // Measured: `293` // Estimated: `7268` - // Minimum execution time: 28_265_000 picoseconds. - Weight::from_parts(28_764_000, 7268) + // Minimum execution time: 27_756_000 picoseconds. + Weight::from_parts(28_637_000, 7268) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().writes(2_u64)) } @@ -100,8 +102,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `153` // Estimated: `3675` - // Minimum execution time: 15_125_000 picoseconds. - Weight::from_parts(15_468_000, 3675) + // Minimum execution time: 15_402_000 picoseconds. + Weight::from_parts(15_803_000, 3675) .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -109,10 +111,10 @@ impl WeightInfo for SubstrateWeight { /// Proof: Assets Asset (max_values: None, max_size: Some(210), added: 2685, mode: MaxEncodedLen) fn start_destroy() -> Weight { // Proof Size summary in bytes: - // Measured: `417` + // Measured: `385` // Estimated: `3675` - // Minimum execution time: 15_368_000 picoseconds. - Weight::from_parts(15_625_000, 3675) + // Minimum execution time: 16_027_000 picoseconds. + Weight::from_parts(16_372_000, 3675) .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -125,12 +127,12 @@ impl WeightInfo for SubstrateWeight { /// The range of component `c` is `[0, 1000]`. fn destroy_accounts(c: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `25 + c * (240 ±0)` + // Measured: `0 + c * (208 ±0)` // Estimated: `8232 + c * (5180 ±0)` - // Minimum execution time: 20_816_000 picoseconds. - Weight::from_parts(21_045_000, 8232) - // Standard Error: 7_118 - .saturating_add(Weight::from_parts(12_723_454, 0).saturating_mul(c.into())) + // Minimum execution time: 20_667_000 picoseconds. + Weight::from_parts(20_930_000, 8232) + // Standard Error: 7_226 + .saturating_add(Weight::from_parts(12_759_091, 0).saturating_mul(c.into())) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().reads((2_u64).saturating_mul(c.into()))) .saturating_add(T::DbWeight::get().writes(1_u64)) @@ -144,12 +146,12 @@ impl WeightInfo for SubstrateWeight { /// The range of component `a` is `[0, 1000]`. fn destroy_approvals(a: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `554 + a * (86 ±0)` + // Measured: `522 + a * (86 ±0)` // Estimated: `7288 + a * (2623 ±0)` - // Minimum execution time: 20_923_000 picoseconds. - Weight::from_parts(21_229_000, 7288) - // Standard Error: 7_215 - .saturating_add(Weight::from_parts(12_915_292, 0).saturating_mul(a.into())) + // Minimum execution time: 20_798_000 picoseconds. + Weight::from_parts(21_129_000, 7288) + // Standard Error: 7_484 + .saturating_add(Weight::from_parts(12_761_996, 0).saturating_mul(a.into())) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().reads((1_u64).saturating_mul(a.into()))) .saturating_add(T::DbWeight::get().writes(1_u64)) @@ -162,10 +164,10 @@ impl WeightInfo for SubstrateWeight { /// Proof: Assets Metadata (max_values: None, max_size: Some(140), added: 2615, mode: MaxEncodedLen) fn finish_destroy() -> Weight { // Proof Size summary in bytes: - // Measured: `383` + // Measured: `351` // Estimated: `7280` - // Minimum execution time: 15_764_000 picoseconds. - Weight::from_parts(16_245_000, 7280) + // Minimum execution time: 15_870_000 picoseconds. + Weight::from_parts(16_280_000, 7280) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -175,10 +177,10 @@ impl WeightInfo for SubstrateWeight { /// Proof: Assets Account (max_values: None, max_size: Some(102), added: 2577, mode: MaxEncodedLen) fn mint() -> Weight { // Proof Size summary in bytes: - // Measured: `383` + // Measured: `351` // Estimated: `7242` - // Minimum execution time: 28_814_000 picoseconds. - Weight::from_parts(29_407_000, 7242) + // Minimum execution time: 28_556_000 picoseconds. + Weight::from_parts(28_972_000, 7242) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().writes(2_u64)) } @@ -188,10 +190,10 @@ impl WeightInfo for SubstrateWeight { /// Proof: Assets Account (max_values: None, max_size: Some(102), added: 2577, mode: MaxEncodedLen) fn burn() -> Weight { // Proof Size summary in bytes: - // Measured: `491` + // Measured: `459` // Estimated: `7242` - // Minimum execution time: 34_784_000 picoseconds. - Weight::from_parts(35_402_000, 7242) + // Minimum execution time: 35_379_000 picoseconds. + Weight::from_parts(35_826_000, 7242) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().writes(2_u64)) } @@ -203,10 +205,10 @@ impl WeightInfo for SubstrateWeight { /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) fn transfer() -> Weight { // Proof Size summary in bytes: - // Measured: `530` + // Measured: `498` // Estimated: `13412` - // Minimum execution time: 49_110_000 picoseconds. - Weight::from_parts(50_483_000, 13412) + // Minimum execution time: 48_837_000 picoseconds. + Weight::from_parts(49_305_000, 13412) .saturating_add(T::DbWeight::get().reads(4_u64)) .saturating_add(T::DbWeight::get().writes(4_u64)) } @@ -218,10 +220,10 @@ impl WeightInfo for SubstrateWeight { /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) fn transfer_keep_alive() -> Weight { // Proof Size summary in bytes: - // Measured: `530` + // Measured: `498` // Estimated: `13412` - // Minimum execution time: 43_585_000 picoseconds. - Weight::from_parts(44_207_000, 13412) + // Minimum execution time: 43_603_000 picoseconds. + Weight::from_parts(44_142_000, 13412) .saturating_add(T::DbWeight::get().reads(4_u64)) .saturating_add(T::DbWeight::get().writes(4_u64)) } @@ -233,10 +235,10 @@ impl WeightInfo for SubstrateWeight { /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) fn force_transfer() -> Weight { // Proof Size summary in bytes: - // Measured: `530` + // Measured: `498` // Estimated: `13412` - // Minimum execution time: 49_238_000 picoseconds. - Weight::from_parts(77_664_000, 13412) + // Minimum execution time: 49_691_000 picoseconds. + Weight::from_parts(50_402_000, 13412) .saturating_add(T::DbWeight::get().reads(4_u64)) .saturating_add(T::DbWeight::get().writes(4_u64)) } @@ -246,10 +248,10 @@ impl WeightInfo for SubstrateWeight { /// Proof: Assets Account (max_values: None, max_size: Some(102), added: 2577, mode: MaxEncodedLen) fn freeze() -> Weight { // Proof Size summary in bytes: - // Measured: `491` + // Measured: `459` // Estimated: `7242` - // Minimum execution time: 19_198_000 picoseconds. - Weight::from_parts(19_585_000, 7242) + // Minimum execution time: 19_600_000 picoseconds. + Weight::from_parts(20_435_000, 7242) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -259,10 +261,10 @@ impl WeightInfo for SubstrateWeight { /// Proof: Assets Account (max_values: None, max_size: Some(102), added: 2577, mode: MaxEncodedLen) fn thaw() -> Weight { // Proof Size summary in bytes: - // Measured: `491` + // Measured: `459` // Estimated: `7242` - // Minimum execution time: 19_659_000 picoseconds. - Weight::from_parts(20_079_000, 7242) + // Minimum execution time: 19_396_000 picoseconds. + Weight::from_parts(19_745_000, 7242) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -270,10 +272,10 @@ impl WeightInfo for SubstrateWeight { /// Proof: Assets Asset (max_values: None, max_size: Some(210), added: 2685, mode: MaxEncodedLen) fn freeze_asset() -> Weight { // Proof Size summary in bytes: - // Measured: `417` + // Measured: `385` // Estimated: `3675` - // Minimum execution time: 15_035_000 picoseconds. - Weight::from_parts(15_594_000, 3675) + // Minimum execution time: 15_503_000 picoseconds. + Weight::from_parts(15_788_000, 3675) .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -281,10 +283,10 @@ impl WeightInfo for SubstrateWeight { /// Proof: Assets Asset (max_values: None, max_size: Some(210), added: 2685, mode: MaxEncodedLen) fn thaw_asset() -> Weight { // Proof Size summary in bytes: - // Measured: `417` + // Measured: `385` // Estimated: `3675` - // Minimum execution time: 15_073_000 picoseconds. - Weight::from_parts(15_862_000, 3675) + // Minimum execution time: 15_874_000 picoseconds. + Weight::from_parts(16_190_000, 3675) .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -294,10 +296,10 @@ impl WeightInfo for SubstrateWeight { /// Proof: Assets Metadata (max_values: None, max_size: Some(140), added: 2615, mode: MaxEncodedLen) fn transfer_ownership() -> Weight { // Proof Size summary in bytes: - // Measured: `383` + // Measured: `351` // Estimated: `7280` - // Minimum execution time: 17_343_000 picoseconds. - Weight::from_parts(17_656_000, 7280) + // Minimum execution time: 17_067_000 picoseconds. + Weight::from_parts(17_355_000, 7280) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -305,10 +307,10 @@ impl WeightInfo for SubstrateWeight { /// Proof: Assets Asset (max_values: None, max_size: Some(210), added: 2685, mode: MaxEncodedLen) fn set_team() -> Weight { // Proof Size summary in bytes: - // Measured: `383` + // Measured: `351` // Estimated: `3675` - // Minimum execution time: 15_902_000 picoseconds. - Weight::from_parts(16_439_000, 3675) + // Minimum execution time: 16_155_000 picoseconds. + Weight::from_parts(16_442_000, 3675) .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -320,10 +322,10 @@ impl WeightInfo for SubstrateWeight { /// The range of component `s` is `[0, 50]`. fn set_metadata(_n: u32, _s: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `383` + // Measured: `351` // Estimated: `7280` - // Minimum execution time: 27_222_000 picoseconds. - Weight::from_parts(29_151_657, 7280) + // Minimum execution time: 27_270_000 picoseconds. + Weight::from_parts(28_634_060, 7280) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -333,10 +335,10 @@ impl WeightInfo for SubstrateWeight { /// Proof: Assets Metadata (max_values: None, max_size: Some(140), added: 2615, mode: MaxEncodedLen) fn clear_metadata() -> Weight { // Proof Size summary in bytes: - // Measured: `579` + // Measured: `515` // Estimated: `7280` - // Minimum execution time: 27_976_000 picoseconds. - Weight::from_parts(28_289_000, 7280) + // Minimum execution time: 27_469_000 picoseconds. + Weight::from_parts(27_911_000, 7280) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -350,10 +352,10 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `190` // Estimated: `7280` - // Minimum execution time: 16_162_000 picoseconds. - Weight::from_parts(17_137_043, 7280) - // Standard Error: 982 - .saturating_add(Weight::from_parts(4_180, 0).saturating_mul(s.into())) + // Minimum execution time: 16_008_000 picoseconds. + Weight::from_parts(17_007_654, 7280) + // Standard Error: 527 + .saturating_add(Weight::from_parts(4_158, 0).saturating_mul(s.into())) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -363,10 +365,10 @@ impl WeightInfo for SubstrateWeight { /// Proof: Assets Metadata (max_values: None, max_size: Some(140), added: 2615, mode: MaxEncodedLen) fn force_clear_metadata() -> Weight { // Proof Size summary in bytes: - // Measured: `579` + // Measured: `515` // Estimated: `7280` - // Minimum execution time: 27_219_000 picoseconds. - Weight::from_parts(27_931_000, 7280) + // Minimum execution time: 27_118_000 picoseconds. + Weight::from_parts(27_776_000, 7280) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -374,10 +376,10 @@ impl WeightInfo for SubstrateWeight { /// Proof: Assets Asset (max_values: None, max_size: Some(210), added: 2685, mode: MaxEncodedLen) fn force_asset_status() -> Weight { // Proof Size summary in bytes: - // Measured: `383` + // Measured: `351` // Estimated: `3675` - // Minimum execution time: 15_313_000 picoseconds. - Weight::from_parts(15_775_000, 3675) + // Minimum execution time: 14_776_000 picoseconds. + Weight::from_parts(15_281_000, 3675) .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -387,10 +389,10 @@ impl WeightInfo for SubstrateWeight { /// Proof: Assets Approvals (max_values: None, max_size: Some(148), added: 2623, mode: MaxEncodedLen) fn approve_transfer() -> Weight { // Proof Size summary in bytes: - // Measured: `417` + // Measured: `385` // Estimated: `7288` - // Minimum execution time: 31_865_000 picoseconds. - Weight::from_parts(32_316_000, 7288) + // Minimum execution time: 31_324_000 picoseconds. + Weight::from_parts(32_161_000, 7288) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().writes(2_u64)) } @@ -404,10 +406,10 @@ impl WeightInfo for SubstrateWeight { /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) fn transfer_approved() -> Weight { // Proof Size summary in bytes: - // Measured: `700` + // Measured: `668` // Estimated: `17025` - // Minimum execution time: 67_203_000 picoseconds. - Weight::from_parts(109_742_000, 17025) + // Minimum execution time: 63_750_000 picoseconds. + Weight::from_parts(64_828_000, 17025) .saturating_add(T::DbWeight::get().reads(5_u64)) .saturating_add(T::DbWeight::get().writes(5_u64)) } @@ -417,10 +419,10 @@ impl WeightInfo for SubstrateWeight { /// Proof: Assets Approvals (max_values: None, max_size: Some(148), added: 2623, mode: MaxEncodedLen) fn cancel_approval() -> Weight { // Proof Size summary in bytes: - // Measured: `587` + // Measured: `555` // Estimated: `7288` - // Minimum execution time: 33_430_000 picoseconds. - Weight::from_parts(33_824_000, 7288) + // Minimum execution time: 32_698_000 picoseconds. + Weight::from_parts(33_309_000, 7288) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().writes(2_u64)) } @@ -430,10 +432,10 @@ impl WeightInfo for SubstrateWeight { /// Proof: Assets Approvals (max_values: None, max_size: Some(148), added: 2623, mode: MaxEncodedLen) fn force_cancel_approval() -> Weight { // Proof Size summary in bytes: - // Measured: `587` + // Measured: `555` // Estimated: `7288` - // Minimum execution time: 33_596_000 picoseconds. - Weight::from_parts(34_226_000, 7288) + // Minimum execution time: 33_147_000 picoseconds. + Weight::from_parts(33_620_000, 7288) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().writes(2_u64)) } @@ -441,10 +443,10 @@ impl WeightInfo for SubstrateWeight { /// Proof: Assets Asset (max_values: None, max_size: Some(210), added: 2685, mode: MaxEncodedLen) fn set_min_balance() -> Weight { // Proof Size summary in bytes: - // Measured: `383` + // Measured: `351` // Estimated: `3675` - // Minimum execution time: 16_367_000 picoseconds. - Weight::from_parts(16_703_000, 3675) + // Minimum execution time: 15_910_000 picoseconds. + Weight::from_parts(16_315_000, 3675) .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -458,10 +460,10 @@ impl WeightInfo for () { /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) fn create() -> Weight { // Proof Size summary in bytes: - // Measured: `325` + // Measured: `293` // Estimated: `7268` - // Minimum execution time: 28_265_000 picoseconds. - Weight::from_parts(28_764_000, 7268) + // Minimum execution time: 27_756_000 picoseconds. + Weight::from_parts(28_637_000, 7268) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().writes(2_u64)) } @@ -471,8 +473,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `153` // Estimated: `3675` - // Minimum execution time: 15_125_000 picoseconds. - Weight::from_parts(15_468_000, 3675) + // Minimum execution time: 15_402_000 picoseconds. + Weight::from_parts(15_803_000, 3675) .saturating_add(RocksDbWeight::get().reads(1_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } @@ -480,10 +482,10 @@ impl WeightInfo for () { /// Proof: Assets Asset (max_values: None, max_size: Some(210), added: 2685, mode: MaxEncodedLen) fn start_destroy() -> Weight { // Proof Size summary in bytes: - // Measured: `417` + // Measured: `385` // Estimated: `3675` - // Minimum execution time: 15_368_000 picoseconds. - Weight::from_parts(15_625_000, 3675) + // Minimum execution time: 16_027_000 picoseconds. + Weight::from_parts(16_372_000, 3675) .saturating_add(RocksDbWeight::get().reads(1_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } @@ -496,12 +498,12 @@ impl WeightInfo for () { /// The range of component `c` is `[0, 1000]`. fn destroy_accounts(c: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `25 + c * (240 ±0)` + // Measured: `0 + c * (208 ±0)` // Estimated: `8232 + c * (5180 ±0)` - // Minimum execution time: 20_816_000 picoseconds. - Weight::from_parts(21_045_000, 8232) - // Standard Error: 7_118 - .saturating_add(Weight::from_parts(12_723_454, 0).saturating_mul(c.into())) + // Minimum execution time: 20_667_000 picoseconds. + Weight::from_parts(20_930_000, 8232) + // Standard Error: 7_226 + .saturating_add(Weight::from_parts(12_759_091, 0).saturating_mul(c.into())) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().reads((2_u64).saturating_mul(c.into()))) .saturating_add(RocksDbWeight::get().writes(1_u64)) @@ -515,12 +517,12 @@ impl WeightInfo for () { /// The range of component `a` is `[0, 1000]`. fn destroy_approvals(a: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `554 + a * (86 ±0)` + // Measured: `522 + a * (86 ±0)` // Estimated: `7288 + a * (2623 ±0)` - // Minimum execution time: 20_923_000 picoseconds. - Weight::from_parts(21_229_000, 7288) - // Standard Error: 7_215 - .saturating_add(Weight::from_parts(12_915_292, 0).saturating_mul(a.into())) + // Minimum execution time: 20_798_000 picoseconds. + Weight::from_parts(21_129_000, 7288) + // Standard Error: 7_484 + .saturating_add(Weight::from_parts(12_761_996, 0).saturating_mul(a.into())) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().reads((1_u64).saturating_mul(a.into()))) .saturating_add(RocksDbWeight::get().writes(1_u64)) @@ -533,10 +535,10 @@ impl WeightInfo for () { /// Proof: Assets Metadata (max_values: None, max_size: Some(140), added: 2615, mode: MaxEncodedLen) fn finish_destroy() -> Weight { // Proof Size summary in bytes: - // Measured: `383` + // Measured: `351` // Estimated: `7280` - // Minimum execution time: 15_764_000 picoseconds. - Weight::from_parts(16_245_000, 7280) + // Minimum execution time: 15_870_000 picoseconds. + Weight::from_parts(16_280_000, 7280) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } @@ -546,10 +548,10 @@ impl WeightInfo for () { /// Proof: Assets Account (max_values: None, max_size: Some(102), added: 2577, mode: MaxEncodedLen) fn mint() -> Weight { // Proof Size summary in bytes: - // Measured: `383` + // Measured: `351` // Estimated: `7242` - // Minimum execution time: 28_814_000 picoseconds. - Weight::from_parts(29_407_000, 7242) + // Minimum execution time: 28_556_000 picoseconds. + Weight::from_parts(28_972_000, 7242) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().writes(2_u64)) } @@ -559,10 +561,10 @@ impl WeightInfo for () { /// Proof: Assets Account (max_values: None, max_size: Some(102), added: 2577, mode: MaxEncodedLen) fn burn() -> Weight { // Proof Size summary in bytes: - // Measured: `491` + // Measured: `459` // Estimated: `7242` - // Minimum execution time: 34_784_000 picoseconds. - Weight::from_parts(35_402_000, 7242) + // Minimum execution time: 35_379_000 picoseconds. + Weight::from_parts(35_826_000, 7242) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().writes(2_u64)) } @@ -574,10 +576,10 @@ impl WeightInfo for () { /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) fn transfer() -> Weight { // Proof Size summary in bytes: - // Measured: `530` + // Measured: `498` // Estimated: `13412` - // Minimum execution time: 49_110_000 picoseconds. - Weight::from_parts(50_483_000, 13412) + // Minimum execution time: 48_837_000 picoseconds. + Weight::from_parts(49_305_000, 13412) .saturating_add(RocksDbWeight::get().reads(4_u64)) .saturating_add(RocksDbWeight::get().writes(4_u64)) } @@ -589,10 +591,10 @@ impl WeightInfo for () { /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) fn transfer_keep_alive() -> Weight { // Proof Size summary in bytes: - // Measured: `530` + // Measured: `498` // Estimated: `13412` - // Minimum execution time: 43_585_000 picoseconds. - Weight::from_parts(44_207_000, 13412) + // Minimum execution time: 43_603_000 picoseconds. + Weight::from_parts(44_142_000, 13412) .saturating_add(RocksDbWeight::get().reads(4_u64)) .saturating_add(RocksDbWeight::get().writes(4_u64)) } @@ -604,10 +606,10 @@ impl WeightInfo for () { /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) fn force_transfer() -> Weight { // Proof Size summary in bytes: - // Measured: `530` + // Measured: `498` // Estimated: `13412` - // Minimum execution time: 49_238_000 picoseconds. - Weight::from_parts(77_664_000, 13412) + // Minimum execution time: 49_691_000 picoseconds. + Weight::from_parts(50_402_000, 13412) .saturating_add(RocksDbWeight::get().reads(4_u64)) .saturating_add(RocksDbWeight::get().writes(4_u64)) } @@ -617,10 +619,10 @@ impl WeightInfo for () { /// Proof: Assets Account (max_values: None, max_size: Some(102), added: 2577, mode: MaxEncodedLen) fn freeze() -> Weight { // Proof Size summary in bytes: - // Measured: `491` + // Measured: `459` // Estimated: `7242` - // Minimum execution time: 19_198_000 picoseconds. - Weight::from_parts(19_585_000, 7242) + // Minimum execution time: 19_600_000 picoseconds. + Weight::from_parts(20_435_000, 7242) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } @@ -630,10 +632,10 @@ impl WeightInfo for () { /// Proof: Assets Account (max_values: None, max_size: Some(102), added: 2577, mode: MaxEncodedLen) fn thaw() -> Weight { // Proof Size summary in bytes: - // Measured: `491` + // Measured: `459` // Estimated: `7242` - // Minimum execution time: 19_659_000 picoseconds. - Weight::from_parts(20_079_000, 7242) + // Minimum execution time: 19_396_000 picoseconds. + Weight::from_parts(19_745_000, 7242) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } @@ -641,10 +643,10 @@ impl WeightInfo for () { /// Proof: Assets Asset (max_values: None, max_size: Some(210), added: 2685, mode: MaxEncodedLen) fn freeze_asset() -> Weight { // Proof Size summary in bytes: - // Measured: `417` + // Measured: `385` // Estimated: `3675` - // Minimum execution time: 15_035_000 picoseconds. - Weight::from_parts(15_594_000, 3675) + // Minimum execution time: 15_503_000 picoseconds. + Weight::from_parts(15_788_000, 3675) .saturating_add(RocksDbWeight::get().reads(1_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } @@ -652,10 +654,10 @@ impl WeightInfo for () { /// Proof: Assets Asset (max_values: None, max_size: Some(210), added: 2685, mode: MaxEncodedLen) fn thaw_asset() -> Weight { // Proof Size summary in bytes: - // Measured: `417` + // Measured: `385` // Estimated: `3675` - // Minimum execution time: 15_073_000 picoseconds. - Weight::from_parts(15_862_000, 3675) + // Minimum execution time: 15_874_000 picoseconds. + Weight::from_parts(16_190_000, 3675) .saturating_add(RocksDbWeight::get().reads(1_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } @@ -665,10 +667,10 @@ impl WeightInfo for () { /// Proof: Assets Metadata (max_values: None, max_size: Some(140), added: 2615, mode: MaxEncodedLen) fn transfer_ownership() -> Weight { // Proof Size summary in bytes: - // Measured: `383` + // Measured: `351` // Estimated: `7280` - // Minimum execution time: 17_343_000 picoseconds. - Weight::from_parts(17_656_000, 7280) + // Minimum execution time: 17_067_000 picoseconds. + Weight::from_parts(17_355_000, 7280) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } @@ -676,10 +678,10 @@ impl WeightInfo for () { /// Proof: Assets Asset (max_values: None, max_size: Some(210), added: 2685, mode: MaxEncodedLen) fn set_team() -> Weight { // Proof Size summary in bytes: - // Measured: `383` + // Measured: `351` // Estimated: `3675` - // Minimum execution time: 15_902_000 picoseconds. - Weight::from_parts(16_439_000, 3675) + // Minimum execution time: 16_155_000 picoseconds. + Weight::from_parts(16_442_000, 3675) .saturating_add(RocksDbWeight::get().reads(1_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } @@ -691,10 +693,10 @@ impl WeightInfo for () { /// The range of component `s` is `[0, 50]`. fn set_metadata(_n: u32, _s: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `383` + // Measured: `351` // Estimated: `7280` - // Minimum execution time: 27_222_000 picoseconds. - Weight::from_parts(29_151_657, 7280) + // Minimum execution time: 27_270_000 picoseconds. + Weight::from_parts(28_634_060, 7280) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } @@ -704,10 +706,10 @@ impl WeightInfo for () { /// Proof: Assets Metadata (max_values: None, max_size: Some(140), added: 2615, mode: MaxEncodedLen) fn clear_metadata() -> Weight { // Proof Size summary in bytes: - // Measured: `579` + // Measured: `515` // Estimated: `7280` - // Minimum execution time: 27_976_000 picoseconds. - Weight::from_parts(28_289_000, 7280) + // Minimum execution time: 27_469_000 picoseconds. + Weight::from_parts(27_911_000, 7280) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } @@ -721,10 +723,10 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `190` // Estimated: `7280` - // Minimum execution time: 16_162_000 picoseconds. - Weight::from_parts(17_137_043, 7280) - // Standard Error: 982 - .saturating_add(Weight::from_parts(4_180, 0).saturating_mul(s.into())) + // Minimum execution time: 16_008_000 picoseconds. + Weight::from_parts(17_007_654, 7280) + // Standard Error: 527 + .saturating_add(Weight::from_parts(4_158, 0).saturating_mul(s.into())) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } @@ -734,10 +736,10 @@ impl WeightInfo for () { /// Proof: Assets Metadata (max_values: None, max_size: Some(140), added: 2615, mode: MaxEncodedLen) fn force_clear_metadata() -> Weight { // Proof Size summary in bytes: - // Measured: `579` + // Measured: `515` // Estimated: `7280` - // Minimum execution time: 27_219_000 picoseconds. - Weight::from_parts(27_931_000, 7280) + // Minimum execution time: 27_118_000 picoseconds. + Weight::from_parts(27_776_000, 7280) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } @@ -745,10 +747,10 @@ impl WeightInfo for () { /// Proof: Assets Asset (max_values: None, max_size: Some(210), added: 2685, mode: MaxEncodedLen) fn force_asset_status() -> Weight { // Proof Size summary in bytes: - // Measured: `383` + // Measured: `351` // Estimated: `3675` - // Minimum execution time: 15_313_000 picoseconds. - Weight::from_parts(15_775_000, 3675) + // Minimum execution time: 14_776_000 picoseconds. + Weight::from_parts(15_281_000, 3675) .saturating_add(RocksDbWeight::get().reads(1_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } @@ -758,10 +760,10 @@ impl WeightInfo for () { /// Proof: Assets Approvals (max_values: None, max_size: Some(148), added: 2623, mode: MaxEncodedLen) fn approve_transfer() -> Weight { // Proof Size summary in bytes: - // Measured: `417` + // Measured: `385` // Estimated: `7288` - // Minimum execution time: 31_865_000 picoseconds. - Weight::from_parts(32_316_000, 7288) + // Minimum execution time: 31_324_000 picoseconds. + Weight::from_parts(32_161_000, 7288) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().writes(2_u64)) } @@ -775,10 +777,10 @@ impl WeightInfo for () { /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) fn transfer_approved() -> Weight { // Proof Size summary in bytes: - // Measured: `700` + // Measured: `668` // Estimated: `17025` - // Minimum execution time: 67_203_000 picoseconds. - Weight::from_parts(109_742_000, 17025) + // Minimum execution time: 63_750_000 picoseconds. + Weight::from_parts(64_828_000, 17025) .saturating_add(RocksDbWeight::get().reads(5_u64)) .saturating_add(RocksDbWeight::get().writes(5_u64)) } @@ -788,10 +790,10 @@ impl WeightInfo for () { /// Proof: Assets Approvals (max_values: None, max_size: Some(148), added: 2623, mode: MaxEncodedLen) fn cancel_approval() -> Weight { // Proof Size summary in bytes: - // Measured: `587` + // Measured: `555` // Estimated: `7288` - // Minimum execution time: 33_430_000 picoseconds. - Weight::from_parts(33_824_000, 7288) + // Minimum execution time: 32_698_000 picoseconds. + Weight::from_parts(33_309_000, 7288) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().writes(2_u64)) } @@ -801,10 +803,10 @@ impl WeightInfo for () { /// Proof: Assets Approvals (max_values: None, max_size: Some(148), added: 2623, mode: MaxEncodedLen) fn force_cancel_approval() -> Weight { // Proof Size summary in bytes: - // Measured: `587` + // Measured: `555` // Estimated: `7288` - // Minimum execution time: 33_596_000 picoseconds. - Weight::from_parts(34_226_000, 7288) + // Minimum execution time: 33_147_000 picoseconds. + Weight::from_parts(33_620_000, 7288) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().writes(2_u64)) } @@ -812,10 +814,10 @@ impl WeightInfo for () { /// Proof: Assets Asset (max_values: None, max_size: Some(210), added: 2685, mode: MaxEncodedLen) fn set_min_balance() -> Weight { // Proof Size summary in bytes: - // Measured: `383` + // Measured: `351` // Estimated: `3675` - // Minimum execution time: 16_367_000 picoseconds. - Weight::from_parts(16_703_000, 3675) + // Minimum execution time: 15_910_000 picoseconds. + Weight::from_parts(16_315_000, 3675) .saturating_add(RocksDbWeight::get().reads(1_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } diff --git a/frame/bags-list/src/weights.rs b/frame/bags-list/src/weights.rs index 65d27c5ae..30efe1ef0 100644 --- a/frame/bags-list/src/weights.rs +++ b/frame/bags-list/src/weights.rs @@ -18,9 +18,9 @@ //! Autogenerated weights for pallet_bags_list //! //! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev -//! DATE: 2023-01-24, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! DATE: 2023-03-15, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` //! WORST CASE MAP SIZE: `1000000` -//! HOSTNAME: `bm2`, CPU: `Intel(R) Core(TM) i7-7700K CPU @ 4.20GHz` +//! HOSTNAME: `bm3`, CPU: `Intel(R) Core(TM) i7-7700K CPU @ 4.20GHz` //! EXECUTION: Some(Wasm), WASM-EXECUTION: Compiled, CHAIN: Some("dev"), DB CACHE: 1024 // Executed Command: @@ -31,6 +31,9 @@ // --steps=50 // --repeat=20 // --pallet=pallet_bags_list +// --no-storage-info +// --no-median-slopes +// --no-min-squares // --extrinsic=* // --execution=wasm // --wasm-execution=compiled @@ -66,10 +69,10 @@ impl WeightInfo for SubstrateWeight { /// Proof: VoterList ListBags (max_values: None, max_size: Some(82), added: 2557, mode: MaxEncodedLen) fn rebag_non_terminal() -> Weight { // Proof Size summary in bytes: - // Measured: `1916` - // Estimated: `19186` - // Minimum execution time: 54_348 nanoseconds. - Weight::from_parts(55_024_000, 19186) + // Measured: `1724` + // Estimated: `23146` + // Minimum execution time: 63_526_000 picoseconds. + Weight::from_parts(64_125_000, 23146) .saturating_add(T::DbWeight::get().reads(7_u64)) .saturating_add(T::DbWeight::get().writes(5_u64)) } @@ -83,10 +86,10 @@ impl WeightInfo for SubstrateWeight { /// Proof: VoterList ListBags (max_values: None, max_size: Some(82), added: 2557, mode: MaxEncodedLen) fn rebag_terminal() -> Weight { // Proof Size summary in bytes: - // Measured: `1810` - // Estimated: `19114` - // Minimum execution time: 53_306 nanoseconds. - Weight::from_parts(54_263_000, 19114) + // Measured: `1618` + // Estimated: `23074` + // Minimum execution time: 62_404_000 picoseconds. + Weight::from_parts(63_178_000, 23074) .saturating_add(T::DbWeight::get().reads(7_u64)) .saturating_add(T::DbWeight::get().writes(5_u64)) } @@ -102,10 +105,10 @@ impl WeightInfo for SubstrateWeight { /// Proof: VoterList ListBags (max_values: None, max_size: Some(82), added: 2557, mode: MaxEncodedLen) fn put_in_front_of() -> Weight { // Proof Size summary in bytes: - // Measured: `2154` - // Estimated: `25798` - // Minimum execution time: 59_513 nanoseconds. - Weight::from_parts(60_717_000, 25798) + // Measured: `1930` + // Estimated: `30748` + // Minimum execution time: 69_938_000 picoseconds. + Weight::from_parts(70_723_000, 30748) .saturating_add(T::DbWeight::get().reads(10_u64)) .saturating_add(T::DbWeight::get().writes(6_u64)) } @@ -123,10 +126,10 @@ impl WeightInfo for () { /// Proof: VoterList ListBags (max_values: None, max_size: Some(82), added: 2557, mode: MaxEncodedLen) fn rebag_non_terminal() -> Weight { // Proof Size summary in bytes: - // Measured: `1916` - // Estimated: `19186` - // Minimum execution time: 54_348 nanoseconds. - Weight::from_parts(55_024_000, 19186) + // Measured: `1724` + // Estimated: `23146` + // Minimum execution time: 63_526_000 picoseconds. + Weight::from_parts(64_125_000, 23146) .saturating_add(RocksDbWeight::get().reads(7_u64)) .saturating_add(RocksDbWeight::get().writes(5_u64)) } @@ -140,10 +143,10 @@ impl WeightInfo for () { /// Proof: VoterList ListBags (max_values: None, max_size: Some(82), added: 2557, mode: MaxEncodedLen) fn rebag_terminal() -> Weight { // Proof Size summary in bytes: - // Measured: `1810` - // Estimated: `19114` - // Minimum execution time: 53_306 nanoseconds. - Weight::from_parts(54_263_000, 19114) + // Measured: `1618` + // Estimated: `23074` + // Minimum execution time: 62_404_000 picoseconds. + Weight::from_parts(63_178_000, 23074) .saturating_add(RocksDbWeight::get().reads(7_u64)) .saturating_add(RocksDbWeight::get().writes(5_u64)) } @@ -159,10 +162,10 @@ impl WeightInfo for () { /// Proof: VoterList ListBags (max_values: None, max_size: Some(82), added: 2557, mode: MaxEncodedLen) fn put_in_front_of() -> Weight { // Proof Size summary in bytes: - // Measured: `2154` - // Estimated: `25798` - // Minimum execution time: 59_513 nanoseconds. - Weight::from_parts(60_717_000, 25798) + // Measured: `1930` + // Estimated: `30748` + // Minimum execution time: 69_938_000 picoseconds. + Weight::from_parts(70_723_000, 30748) .saturating_add(RocksDbWeight::get().reads(10_u64)) .saturating_add(RocksDbWeight::get().writes(6_u64)) } diff --git a/frame/balances/src/weights.rs b/frame/balances/src/weights.rs index cdc657ce1..500eb3371 100644 --- a/frame/balances/src/weights.rs +++ b/frame/balances/src/weights.rs @@ -18,26 +18,28 @@ //! Autogenerated weights for pallet_balances //! //! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev -//! DATE: 2023-02-27, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! DATE: 2023-03-15, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` //! WORST CASE MAP SIZE: `1000000` //! HOSTNAME: `bm3`, CPU: `Intel(R) Core(TM) i7-7700K CPU @ 4.20GHz` //! EXECUTION: Some(Wasm), WASM-EXECUTION: Compiled, CHAIN: Some("dev"), DB CACHE: 1024 // Executed Command: -// target/production/substrate +// ./target/production/substrate // benchmark // pallet +// --chain=dev // --steps=50 // --repeat=20 +// --pallet=pallet_balances +// --no-storage-info +// --no-median-slopes +// --no-min-squares // --extrinsic=* // --execution=wasm // --wasm-execution=compiled // --heap-pages=4096 -// --json-file=/var/lib/gitlab-runner/builds/zyw4fam_/0/parity/mirrors/substrate/.git/.artifacts/bench.json -// --pallet=pallet_balances -// --chain=dev -// --header=./HEADER-APACHE2 // --output=./frame/balances/src/weights.rs +// --header=./HEADER-APACHE2 // --template=./.maintain/frame-weight-template.hbs #![cfg_attr(rustfmt, rustfmt_skip)] @@ -67,9 +69,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `3593` - // Minimum execution time: 37_815 nanoseconds. - Weight::from_parts(38_109_000, 0) - .saturating_add(Weight::from_parts(0, 3593)) + // Minimum execution time: 37_266_000 picoseconds. + Weight::from_parts(37_757_000, 3593) .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -79,9 +80,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `3593` - // Minimum execution time: 28_184 nanoseconds. - Weight::from_parts(49_250_000, 0) - .saturating_add(Weight::from_parts(0, 3593)) + // Minimum execution time: 28_782_000 picoseconds. + Weight::from_parts(29_185_000, 3593) .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -89,11 +89,10 @@ impl WeightInfo for SubstrateWeight { /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) fn set_balance_creating() -> Weight { // Proof Size summary in bytes: - // Measured: `206` + // Measured: `174` // Estimated: `3593` - // Minimum execution time: 17_474 nanoseconds. - Weight::from_parts(17_777_000, 0) - .saturating_add(Weight::from_parts(0, 3593)) + // Minimum execution time: 17_406_000 picoseconds. + Weight::from_parts(17_781_000, 3593) .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -101,11 +100,10 @@ impl WeightInfo for SubstrateWeight { /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) fn set_balance_killing() -> Weight { // Proof Size summary in bytes: - // Measured: `206` + // Measured: `174` // Estimated: `3593` - // Minimum execution time: 20_962 nanoseconds. - Weight::from_parts(21_419_000, 0) - .saturating_add(Weight::from_parts(0, 3593)) + // Minimum execution time: 20_942_000 picoseconds. + Weight::from_parts(21_270_000, 3593) .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -113,11 +111,10 @@ impl WeightInfo for SubstrateWeight { /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) fn force_transfer() -> Weight { // Proof Size summary in bytes: - // Measured: `135` + // Measured: `103` // Estimated: `6196` - // Minimum execution time: 39_713 nanoseconds. - Weight::from_parts(40_360_000, 0) - .saturating_add(Weight::from_parts(0, 6196)) + // Minimum execution time: 41_706_000 picoseconds. + Weight::from_parts(42_176_000, 6196) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().writes(2_u64)) } @@ -127,9 +124,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `3593` - // Minimum execution time: 34_878 nanoseconds. - Weight::from_parts(35_121_000, 0) - .saturating_add(Weight::from_parts(0, 3593)) + // Minimum execution time: 35_622_000 picoseconds. + Weight::from_parts(35_943_000, 3593) .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -137,11 +133,10 @@ impl WeightInfo for SubstrateWeight { /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) fn force_unreserve() -> Weight { // Proof Size summary in bytes: - // Measured: `206` + // Measured: `174` // Estimated: `3593` - // Minimum execution time: 16_790 nanoseconds. - Weight::from_parts(17_029_000, 0) - .saturating_add(Weight::from_parts(0, 3593)) + // Minimum execution time: 16_586_000 picoseconds. + Weight::from_parts(17_352_000, 3593) .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -155,9 +150,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `0` // Estimated: `3593` - // Minimum execution time: 37_815 nanoseconds. - Weight::from_parts(38_109_000, 0) - .saturating_add(Weight::from_parts(0, 3593)) + // Minimum execution time: 37_266_000 picoseconds. + Weight::from_parts(37_757_000, 3593) .saturating_add(RocksDbWeight::get().reads(1_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } @@ -167,9 +161,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `0` // Estimated: `3593` - // Minimum execution time: 28_184 nanoseconds. - Weight::from_parts(49_250_000, 0) - .saturating_add(Weight::from_parts(0, 3593)) + // Minimum execution time: 28_782_000 picoseconds. + Weight::from_parts(29_185_000, 3593) .saturating_add(RocksDbWeight::get().reads(1_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } @@ -177,11 +170,10 @@ impl WeightInfo for () { /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) fn set_balance_creating() -> Weight { // Proof Size summary in bytes: - // Measured: `206` + // Measured: `174` // Estimated: `3593` - // Minimum execution time: 17_474 nanoseconds. - Weight::from_parts(17_777_000, 0) - .saturating_add(Weight::from_parts(0, 3593)) + // Minimum execution time: 17_406_000 picoseconds. + Weight::from_parts(17_781_000, 3593) .saturating_add(RocksDbWeight::get().reads(1_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } @@ -189,11 +181,10 @@ impl WeightInfo for () { /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) fn set_balance_killing() -> Weight { // Proof Size summary in bytes: - // Measured: `206` + // Measured: `174` // Estimated: `3593` - // Minimum execution time: 20_962 nanoseconds. - Weight::from_parts(21_419_000, 0) - .saturating_add(Weight::from_parts(0, 3593)) + // Minimum execution time: 20_942_000 picoseconds. + Weight::from_parts(21_270_000, 3593) .saturating_add(RocksDbWeight::get().reads(1_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } @@ -201,11 +192,10 @@ impl WeightInfo for () { /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) fn force_transfer() -> Weight { // Proof Size summary in bytes: - // Measured: `135` + // Measured: `103` // Estimated: `6196` - // Minimum execution time: 39_713 nanoseconds. - Weight::from_parts(40_360_000, 0) - .saturating_add(Weight::from_parts(0, 6196)) + // Minimum execution time: 41_706_000 picoseconds. + Weight::from_parts(42_176_000, 6196) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().writes(2_u64)) } @@ -215,9 +205,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `0` // Estimated: `3593` - // Minimum execution time: 34_878 nanoseconds. - Weight::from_parts(35_121_000, 0) - .saturating_add(Weight::from_parts(0, 3593)) + // Minimum execution time: 35_622_000 picoseconds. + Weight::from_parts(35_943_000, 3593) .saturating_add(RocksDbWeight::get().reads(1_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } @@ -225,11 +214,10 @@ impl WeightInfo for () { /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) fn force_unreserve() -> Weight { // Proof Size summary in bytes: - // Measured: `206` + // Measured: `174` // Estimated: `3593` - // Minimum execution time: 16_790 nanoseconds. - Weight::from_parts(17_029_000, 0) - .saturating_add(Weight::from_parts(0, 3593)) + // Minimum execution time: 16_586_000 picoseconds. + Weight::from_parts(17_352_000, 3593) .saturating_add(RocksDbWeight::get().reads(1_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } diff --git a/frame/benchmarking/src/weights.rs b/frame/benchmarking/src/weights.rs index b05feb590..aa24b99ac 100644 --- a/frame/benchmarking/src/weights.rs +++ b/frame/benchmarking/src/weights.rs @@ -18,26 +18,28 @@ //! Autogenerated weights for frame_benchmarking //! //! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev -//! DATE: 2023-03-06, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! DATE: 2023-03-15, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` //! WORST CASE MAP SIZE: `1000000` //! HOSTNAME: `bm3`, CPU: `Intel(R) Core(TM) i7-7700K CPU @ 4.20GHz` //! EXECUTION: Some(Wasm), WASM-EXECUTION: Compiled, CHAIN: Some("dev"), DB CACHE: 1024 // Executed Command: -// target/production/substrate +// ./target/production/substrate // benchmark // pallet +// --chain=dev // --steps=50 // --repeat=20 +// --pallet=frame_benchmarking +// --no-storage-info +// --no-median-slopes +// --no-min-squares // --extrinsic=* // --execution=wasm // --wasm-execution=compiled // --heap-pages=4096 -// --json-file=/var/lib/gitlab-runner/builds/zyw4fam_/0/parity/mirrors/substrate/.git/.artifacts/bench.json -// --pallet=frame_benchmarking -// --chain=dev -// --header=./HEADER-APACHE2 // --output=./frame/benchmarking/src/weights.rs +// --header=./HEADER-APACHE2 // --template=./.maintain/frame-weight-template.hbs #![cfg_attr(rustfmt, rustfmt_skip)] @@ -65,49 +67,49 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 168_000 picoseconds. - Weight::from_parts(237_577, 0) + // Minimum execution time: 205_000 picoseconds. + Weight::from_parts(263_483, 0) } /// The range of component `i` is `[0, 1000000]`. fn subtraction(_i: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 169_000 picoseconds. - Weight::from_parts(224_111, 0) + // Minimum execution time: 186_000 picoseconds. + Weight::from_parts(257_588, 0) } /// The range of component `i` is `[0, 1000000]`. fn multiplication(_i: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 175_000 picoseconds. - Weight::from_parts(229_708, 0) + // Minimum execution time: 192_000 picoseconds. + Weight::from_parts(264_271, 0) } /// The range of component `i` is `[0, 1000000]`. fn division(_i: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 173_000 picoseconds. - Weight::from_parts(229_855, 0) + // Minimum execution time: 191_000 picoseconds. + Weight::from_parts(250_785, 0) } fn hashing() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 21_286_627_000 picoseconds. - Weight::from_parts(21_405_011_000, 0) + // Minimum execution time: 21_405_207_000 picoseconds. + Weight::from_parts(21_534_243_000, 0) } /// The range of component `i` is `[0, 100]`. fn sr25519_verification(i: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 239_000 picoseconds. - Weight::from_parts(661_987, 0) - // Standard Error: 24_324 - .saturating_add(Weight::from_parts(47_322_399, 0).saturating_mul(i.into())) + // Minimum execution time: 255_000 picoseconds. + Weight::from_parts(271_000, 0) + // Standard Error: 12_788 + .saturating_add(Weight::from_parts(47_469_387, 0).saturating_mul(i.into())) } } @@ -118,48 +120,48 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 168_000 picoseconds. - Weight::from_parts(237_577, 0) + // Minimum execution time: 205_000 picoseconds. + Weight::from_parts(263_483, 0) } /// The range of component `i` is `[0, 1000000]`. fn subtraction(_i: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 169_000 picoseconds. - Weight::from_parts(224_111, 0) + // Minimum execution time: 186_000 picoseconds. + Weight::from_parts(257_588, 0) } /// The range of component `i` is `[0, 1000000]`. fn multiplication(_i: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 175_000 picoseconds. - Weight::from_parts(229_708, 0) + // Minimum execution time: 192_000 picoseconds. + Weight::from_parts(264_271, 0) } /// The range of component `i` is `[0, 1000000]`. fn division(_i: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 173_000 picoseconds. - Weight::from_parts(229_855, 0) + // Minimum execution time: 191_000 picoseconds. + Weight::from_parts(250_785, 0) } fn hashing() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 21_286_627_000 picoseconds. - Weight::from_parts(21_405_011_000, 0) + // Minimum execution time: 21_405_207_000 picoseconds. + Weight::from_parts(21_534_243_000, 0) } /// The range of component `i` is `[0, 100]`. fn sr25519_verification(i: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 239_000 picoseconds. - Weight::from_parts(661_987, 0) - // Standard Error: 24_324 - .saturating_add(Weight::from_parts(47_322_399, 0).saturating_mul(i.into())) + // Minimum execution time: 255_000 picoseconds. + Weight::from_parts(271_000, 0) + // Standard Error: 12_788 + .saturating_add(Weight::from_parts(47_469_387, 0).saturating_mul(i.into())) } } diff --git a/frame/bounties/src/weights.rs b/frame/bounties/src/weights.rs index f0ea78b5f..b0459fee6 100644 --- a/frame/bounties/src/weights.rs +++ b/frame/bounties/src/weights.rs @@ -18,9 +18,9 @@ //! Autogenerated weights for pallet_bounties //! //! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev -//! DATE: 2023-01-24, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! DATE: 2023-03-15, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` //! WORST CASE MAP SIZE: `1000000` -//! HOSTNAME: `bm2`, CPU: `Intel(R) Core(TM) i7-7700K CPU @ 4.20GHz` +//! HOSTNAME: `bm3`, CPU: `Intel(R) Core(TM) i7-7700K CPU @ 4.20GHz` //! EXECUTION: Some(Wasm), WASM-EXECUTION: Compiled, CHAIN: Some("dev"), DB CACHE: 1024 // Executed Command: @@ -31,6 +31,9 @@ // --steps=50 // --repeat=20 // --pallet=pallet_bounties +// --no-storage-info +// --no-median-slopes +// --no-min-squares // --extrinsic=* // --execution=wasm // --wasm-execution=compiled @@ -73,14 +76,12 @@ impl WeightInfo for SubstrateWeight { /// Storage: Bounties Bounties (r:0 w:1) /// Proof: Bounties Bounties (max_values: None, max_size: Some(177), added: 2652, mode: MaxEncodedLen) /// The range of component `d` is `[0, 300]`. - fn propose_bounty(d: u32, ) -> Weight { + fn propose_bounty(_d: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `308` - // Estimated: `3102` - // Minimum execution time: 22_787 nanoseconds. - Weight::from_parts(23_898_632, 3102) - // Standard Error: 141 - .saturating_add(Weight::from_parts(568, 0).saturating_mul(d.into())) + // Measured: `276` + // Estimated: `5082` + // Minimum execution time: 26_271_000 picoseconds. + Weight::from_parts(28_048_901, 5082) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().writes(4_u64)) } @@ -90,10 +91,10 @@ impl WeightInfo for SubstrateWeight { /// Proof: Bounties BountyApprovals (max_values: Some(1), max_size: Some(402), added: 897, mode: MaxEncodedLen) fn approve_bounty() -> Weight { // Proof Size summary in bytes: - // Measured: `400` - // Estimated: `3549` - // Minimum execution time: 10_526 nanoseconds. - Weight::from_parts(10_729_000, 3549) + // Measured: `368` + // Estimated: `5529` + // Minimum execution time: 12_573_000 picoseconds. + Weight::from_parts(12_827_000, 5529) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().writes(2_u64)) } @@ -101,10 +102,10 @@ impl WeightInfo for SubstrateWeight { /// Proof: Bounties Bounties (max_values: None, max_size: Some(177), added: 2652, mode: MaxEncodedLen) fn propose_curator() -> Weight { // Proof Size summary in bytes: - // Measured: `420` - // Estimated: `2652` - // Minimum execution time: 9_193 nanoseconds. - Weight::from_parts(9_455_000, 2652) + // Measured: `388` + // Estimated: `3642` + // Minimum execution time: 10_555_000 picoseconds. + Weight::from_parts(10_857_000, 3642) .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -114,10 +115,10 @@ impl WeightInfo for SubstrateWeight { /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) fn unassign_curator() -> Weight { // Proof Size summary in bytes: - // Measured: `628` - // Estimated: `5255` - // Minimum execution time: 22_592 nanoseconds. - Weight::from_parts(22_952_000, 5255) + // Measured: `564` + // Estimated: `7235` + // Minimum execution time: 26_003_000 picoseconds. + Weight::from_parts(26_291_000, 7235) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().writes(2_u64)) } @@ -127,10 +128,10 @@ impl WeightInfo for SubstrateWeight { /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) fn accept_curator() -> Weight { // Proof Size summary in bytes: - // Measured: `624` - // Estimated: `5255` - // Minimum execution time: 20_920 nanoseconds. - Weight::from_parts(21_369_000, 5255) + // Measured: `560` + // Estimated: `7235` + // Minimum execution time: 24_371_000 picoseconds. + Weight::from_parts(24_781_000, 7235) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().writes(2_u64)) } @@ -140,10 +141,10 @@ impl WeightInfo for SubstrateWeight { /// Proof: ChildBounties ParentChildBounties (max_values: None, max_size: Some(16), added: 2491, mode: MaxEncodedLen) fn award_bounty() -> Weight { // Proof Size summary in bytes: - // Measured: `570` - // Estimated: `5143` - // Minimum execution time: 17_853 nanoseconds. - Weight::from_parts(18_280_000, 5143) + // Measured: `538` + // Estimated: `7123` + // Minimum execution time: 20_688_000 picoseconds. + Weight::from_parts(20_909_000, 7123) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -157,10 +158,10 @@ impl WeightInfo for SubstrateWeight { /// Proof: Bounties BountyDescriptions (max_values: None, max_size: Some(314), added: 2789, mode: MaxEncodedLen) fn claim_bounty() -> Weight { // Proof Size summary in bytes: - // Measured: `998` - // Estimated: `12964` - // Minimum execution time: 67_538 nanoseconds. - Weight::from_parts(67_974_000, 12964) + // Measured: `902` + // Estimated: `15934` + // Minimum execution time: 78_933_000 picoseconds. + Weight::from_parts(79_884_000, 15934) .saturating_add(T::DbWeight::get().reads(5_u64)) .saturating_add(T::DbWeight::get().writes(6_u64)) } @@ -174,10 +175,10 @@ impl WeightInfo for SubstrateWeight { /// Proof: Bounties BountyDescriptions (max_values: None, max_size: Some(314), added: 2789, mode: MaxEncodedLen) fn close_bounty_proposed() -> Weight { // Proof Size summary in bytes: - // Measured: `646` - // Estimated: `7746` - // Minimum execution time: 28_380 nanoseconds. - Weight::from_parts(28_859_000, 7746) + // Measured: `582` + // Estimated: `10716` + // Minimum execution time: 32_332_000 picoseconds. + Weight::from_parts(32_833_000, 10716) .saturating_add(T::DbWeight::get().reads(3_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) } @@ -191,10 +192,10 @@ impl WeightInfo for SubstrateWeight { /// Proof: Bounties BountyDescriptions (max_values: None, max_size: Some(314), added: 2789, mode: MaxEncodedLen) fn close_bounty_active() -> Weight { // Proof Size summary in bytes: - // Measured: `914` - // Estimated: `10349` - // Minimum execution time: 47_739 nanoseconds. - Weight::from_parts(48_388_000, 10349) + // Measured: `818` + // Estimated: `13319` + // Minimum execution time: 55_686_000 picoseconds. + Weight::from_parts(56_271_000, 13319) .saturating_add(T::DbWeight::get().reads(4_u64)) .saturating_add(T::DbWeight::get().writes(4_u64)) } @@ -202,10 +203,10 @@ impl WeightInfo for SubstrateWeight { /// Proof: Bounties Bounties (max_values: None, max_size: Some(177), added: 2652, mode: MaxEncodedLen) fn extend_bounty_expiry() -> Weight { // Proof Size summary in bytes: - // Measured: `456` - // Estimated: `2652` - // Minimum execution time: 14_188 nanoseconds. - Weight::from_parts(14_801_000, 2652) + // Measured: `424` + // Estimated: `3642` + // Minimum execution time: 16_414_000 picoseconds. + Weight::from_parts(16_681_000, 3642) .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -218,12 +219,12 @@ impl WeightInfo for SubstrateWeight { /// The range of component `b` is `[0, 100]`. fn spend_funds(b: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `31 + b * (360 ±0)` - // Estimated: `897 + b * (7858 ±0)` - // Minimum execution time: 4_685 nanoseconds. - Weight::from_parts(9_932_840, 897) - // Standard Error: 14_301 - .saturating_add(Weight::from_parts(27_178_347, 0).saturating_mul(b.into())) + // Measured: `4 + b * (297 ±0)` + // Estimated: `3867 + b * (7858 ±0)` + // Minimum execution time: 5_398_000 picoseconds. + Weight::from_parts(4_478_947, 3867) + // Standard Error: 41_860 + .saturating_add(Weight::from_parts(33_082_606, 0).saturating_mul(b.into())) .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(T::DbWeight::get().reads((3_u64).saturating_mul(b.into()))) .saturating_add(T::DbWeight::get().writes(1_u64)) @@ -243,14 +244,12 @@ impl WeightInfo for () { /// Storage: Bounties Bounties (r:0 w:1) /// Proof: Bounties Bounties (max_values: None, max_size: Some(177), added: 2652, mode: MaxEncodedLen) /// The range of component `d` is `[0, 300]`. - fn propose_bounty(d: u32, ) -> Weight { + fn propose_bounty(_d: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `308` - // Estimated: `3102` - // Minimum execution time: 22_787 nanoseconds. - Weight::from_parts(23_898_632, 3102) - // Standard Error: 141 - .saturating_add(Weight::from_parts(568, 0).saturating_mul(d.into())) + // Measured: `276` + // Estimated: `5082` + // Minimum execution time: 26_271_000 picoseconds. + Weight::from_parts(28_048_901, 5082) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().writes(4_u64)) } @@ -260,10 +259,10 @@ impl WeightInfo for () { /// Proof: Bounties BountyApprovals (max_values: Some(1), max_size: Some(402), added: 897, mode: MaxEncodedLen) fn approve_bounty() -> Weight { // Proof Size summary in bytes: - // Measured: `400` - // Estimated: `3549` - // Minimum execution time: 10_526 nanoseconds. - Weight::from_parts(10_729_000, 3549) + // Measured: `368` + // Estimated: `5529` + // Minimum execution time: 12_573_000 picoseconds. + Weight::from_parts(12_827_000, 5529) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().writes(2_u64)) } @@ -271,10 +270,10 @@ impl WeightInfo for () { /// Proof: Bounties Bounties (max_values: None, max_size: Some(177), added: 2652, mode: MaxEncodedLen) fn propose_curator() -> Weight { // Proof Size summary in bytes: - // Measured: `420` - // Estimated: `2652` - // Minimum execution time: 9_193 nanoseconds. - Weight::from_parts(9_455_000, 2652) + // Measured: `388` + // Estimated: `3642` + // Minimum execution time: 10_555_000 picoseconds. + Weight::from_parts(10_857_000, 3642) .saturating_add(RocksDbWeight::get().reads(1_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } @@ -284,10 +283,10 @@ impl WeightInfo for () { /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) fn unassign_curator() -> Weight { // Proof Size summary in bytes: - // Measured: `628` - // Estimated: `5255` - // Minimum execution time: 22_592 nanoseconds. - Weight::from_parts(22_952_000, 5255) + // Measured: `564` + // Estimated: `7235` + // Minimum execution time: 26_003_000 picoseconds. + Weight::from_parts(26_291_000, 7235) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().writes(2_u64)) } @@ -297,10 +296,10 @@ impl WeightInfo for () { /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) fn accept_curator() -> Weight { // Proof Size summary in bytes: - // Measured: `624` - // Estimated: `5255` - // Minimum execution time: 20_920 nanoseconds. - Weight::from_parts(21_369_000, 5255) + // Measured: `560` + // Estimated: `7235` + // Minimum execution time: 24_371_000 picoseconds. + Weight::from_parts(24_781_000, 7235) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().writes(2_u64)) } @@ -310,10 +309,10 @@ impl WeightInfo for () { /// Proof: ChildBounties ParentChildBounties (max_values: None, max_size: Some(16), added: 2491, mode: MaxEncodedLen) fn award_bounty() -> Weight { // Proof Size summary in bytes: - // Measured: `570` - // Estimated: `5143` - // Minimum execution time: 17_853 nanoseconds. - Weight::from_parts(18_280_000, 5143) + // Measured: `538` + // Estimated: `7123` + // Minimum execution time: 20_688_000 picoseconds. + Weight::from_parts(20_909_000, 7123) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } @@ -327,10 +326,10 @@ impl WeightInfo for () { /// Proof: Bounties BountyDescriptions (max_values: None, max_size: Some(314), added: 2789, mode: MaxEncodedLen) fn claim_bounty() -> Weight { // Proof Size summary in bytes: - // Measured: `998` - // Estimated: `12964` - // Minimum execution time: 67_538 nanoseconds. - Weight::from_parts(67_974_000, 12964) + // Measured: `902` + // Estimated: `15934` + // Minimum execution time: 78_933_000 picoseconds. + Weight::from_parts(79_884_000, 15934) .saturating_add(RocksDbWeight::get().reads(5_u64)) .saturating_add(RocksDbWeight::get().writes(6_u64)) } @@ -344,10 +343,10 @@ impl WeightInfo for () { /// Proof: Bounties BountyDescriptions (max_values: None, max_size: Some(314), added: 2789, mode: MaxEncodedLen) fn close_bounty_proposed() -> Weight { // Proof Size summary in bytes: - // Measured: `646` - // Estimated: `7746` - // Minimum execution time: 28_380 nanoseconds. - Weight::from_parts(28_859_000, 7746) + // Measured: `582` + // Estimated: `10716` + // Minimum execution time: 32_332_000 picoseconds. + Weight::from_parts(32_833_000, 10716) .saturating_add(RocksDbWeight::get().reads(3_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) } @@ -361,10 +360,10 @@ impl WeightInfo for () { /// Proof: Bounties BountyDescriptions (max_values: None, max_size: Some(314), added: 2789, mode: MaxEncodedLen) fn close_bounty_active() -> Weight { // Proof Size summary in bytes: - // Measured: `914` - // Estimated: `10349` - // Minimum execution time: 47_739 nanoseconds. - Weight::from_parts(48_388_000, 10349) + // Measured: `818` + // Estimated: `13319` + // Minimum execution time: 55_686_000 picoseconds. + Weight::from_parts(56_271_000, 13319) .saturating_add(RocksDbWeight::get().reads(4_u64)) .saturating_add(RocksDbWeight::get().writes(4_u64)) } @@ -372,10 +371,10 @@ impl WeightInfo for () { /// Proof: Bounties Bounties (max_values: None, max_size: Some(177), added: 2652, mode: MaxEncodedLen) fn extend_bounty_expiry() -> Weight { // Proof Size summary in bytes: - // Measured: `456` - // Estimated: `2652` - // Minimum execution time: 14_188 nanoseconds. - Weight::from_parts(14_801_000, 2652) + // Measured: `424` + // Estimated: `3642` + // Minimum execution time: 16_414_000 picoseconds. + Weight::from_parts(16_681_000, 3642) .saturating_add(RocksDbWeight::get().reads(1_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } @@ -388,12 +387,12 @@ impl WeightInfo for () { /// The range of component `b` is `[0, 100]`. fn spend_funds(b: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `31 + b * (360 ±0)` - // Estimated: `897 + b * (7858 ±0)` - // Minimum execution time: 4_685 nanoseconds. - Weight::from_parts(9_932_840, 897) - // Standard Error: 14_301 - .saturating_add(Weight::from_parts(27_178_347, 0).saturating_mul(b.into())) + // Measured: `4 + b * (297 ±0)` + // Estimated: `3867 + b * (7858 ±0)` + // Minimum execution time: 5_398_000 picoseconds. + Weight::from_parts(4_478_947, 3867) + // Standard Error: 41_860 + .saturating_add(Weight::from_parts(33_082_606, 0).saturating_mul(b.into())) .saturating_add(RocksDbWeight::get().reads(1_u64)) .saturating_add(RocksDbWeight::get().reads((3_u64).saturating_mul(b.into()))) .saturating_add(RocksDbWeight::get().writes(1_u64)) diff --git a/frame/child-bounties/src/weights.rs b/frame/child-bounties/src/weights.rs index eeb144196..92be77c49 100644 --- a/frame/child-bounties/src/weights.rs +++ b/frame/child-bounties/src/weights.rs @@ -18,9 +18,9 @@ //! Autogenerated weights for pallet_child_bounties //! //! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev -//! DATE: 2023-01-24, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! DATE: 2023-03-15, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` //! WORST CASE MAP SIZE: `1000000` -//! HOSTNAME: `bm2`, CPU: `Intel(R) Core(TM) i7-7700K CPU @ 4.20GHz` +//! HOSTNAME: `bm3`, CPU: `Intel(R) Core(TM) i7-7700K CPU @ 4.20GHz` //! EXECUTION: Some(Wasm), WASM-EXECUTION: Compiled, CHAIN: Some("dev"), DB CACHE: 1024 // Executed Command: @@ -31,6 +31,9 @@ // --steps=50 // --repeat=20 // --pallet=pallet_child_bounties +// --no-storage-info +// --no-median-slopes +// --no-min-squares // --extrinsic=* // --execution=wasm // --wasm-execution=compiled @@ -76,12 +79,12 @@ impl WeightInfo for SubstrateWeight { /// The range of component `d` is `[0, 300]`. fn add_child_bounty(d: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `742` - // Estimated: `10848` - // Minimum execution time: 46_743 nanoseconds. - Weight::from_parts(47_762_924, 10848) - // Standard Error: 135 - .saturating_add(Weight::from_parts(599, 0).saturating_mul(d.into())) + // Measured: `678` + // Estimated: `14808` + // Minimum execution time: 54_017_000 picoseconds. + Weight::from_parts(55_142_443, 14808) + // Standard Error: 310 + .saturating_add(Weight::from_parts(1_199, 0).saturating_mul(d.into())) .saturating_add(T::DbWeight::get().reads(5_u64)) .saturating_add(T::DbWeight::get().writes(6_u64)) } @@ -93,10 +96,10 @@ impl WeightInfo for SubstrateWeight { /// Proof: ChildBounties ChildrenCuratorFees (max_values: None, max_size: Some(28), added: 2503, mode: MaxEncodedLen) fn propose_curator() -> Weight { // Proof Size summary in bytes: - // Measured: `796` - // Estimated: `7775` - // Minimum execution time: 16_417 nanoseconds. - Weight::from_parts(16_712_000, 7775) + // Measured: `732` + // Estimated: `10745` + // Minimum execution time: 19_222_000 picoseconds. + Weight::from_parts(19_446_000, 10745) .saturating_add(T::DbWeight::get().reads(3_u64)) .saturating_add(T::DbWeight::get().writes(2_u64)) } @@ -108,10 +111,10 @@ impl WeightInfo for SubstrateWeight { /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) fn accept_curator() -> Weight { // Proof Size summary in bytes: - // Measured: `974` - // Estimated: `7875` - // Minimum execution time: 25_548 nanoseconds. - Weight::from_parts(25_919_000, 7875) + // Measured: `878` + // Estimated: `10845` + // Minimum execution time: 31_049_000 picoseconds. + Weight::from_parts(31_546_000, 10845) .saturating_add(T::DbWeight::get().reads(3_u64)) .saturating_add(T::DbWeight::get().writes(2_u64)) } @@ -123,10 +126,10 @@ impl WeightInfo for SubstrateWeight { /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) fn unassign_curator() -> Weight { // Proof Size summary in bytes: - // Measured: `974` - // Estimated: `7875` - // Minimum execution time: 27_645 nanoseconds. - Weight::from_parts(27_947_000, 7875) + // Measured: `878` + // Estimated: `10845` + // Minimum execution time: 33_860_000 picoseconds. + Weight::from_parts(34_897_000, 10845) .saturating_add(T::DbWeight::get().reads(3_u64)) .saturating_add(T::DbWeight::get().writes(2_u64)) } @@ -136,10 +139,10 @@ impl WeightInfo for SubstrateWeight { /// Proof: ChildBounties ChildBounties (max_values: None, max_size: Some(145), added: 2620, mode: MaxEncodedLen) fn award_child_bounty() -> Weight { // Proof Size summary in bytes: - // Measured: `839` - // Estimated: `5272` - // Minimum execution time: 20_002 nanoseconds. - Weight::from_parts(20_512_000, 5272) + // Measured: `775` + // Estimated: `7252` + // Minimum execution time: 23_178_000 picoseconds. + Weight::from_parts(23_412_000, 7252) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -153,10 +156,10 @@ impl WeightInfo for SubstrateWeight { /// Proof: ChildBounties ChildBountyDescriptions (max_values: None, max_size: Some(314), added: 2789, mode: MaxEncodedLen) fn claim_child_bounty() -> Weight { // Proof Size summary in bytes: - // Measured: `744` - // Estimated: `12920` - // Minimum execution time: 63_752 nanoseconds. - Weight::from_parts(64_179_000, 12920) + // Measured: `648` + // Estimated: `15890` + // Minimum execution time: 76_189_000 picoseconds. + Weight::from_parts(76_770_000, 15890) .saturating_add(T::DbWeight::get().reads(5_u64)) .saturating_add(T::DbWeight::get().writes(6_u64)) } @@ -174,10 +177,10 @@ impl WeightInfo for SubstrateWeight { /// Proof: ChildBounties ChildBountyDescriptions (max_values: None, max_size: Some(314), added: 2789, mode: MaxEncodedLen) fn close_child_bounty_added() -> Weight { // Proof Size summary in bytes: - // Measured: `1106` - // Estimated: `15472` - // Minimum execution time: 47_388 nanoseconds. - Weight::from_parts(47_946_000, 15472) + // Measured: `978` + // Estimated: `20422` + // Minimum execution time: 55_161_000 picoseconds. + Weight::from_parts(55_578_000, 20422) .saturating_add(T::DbWeight::get().reads(6_u64)) .saturating_add(T::DbWeight::get().writes(6_u64)) } @@ -195,10 +198,10 @@ impl WeightInfo for SubstrateWeight { /// Proof: ChildBounties ChildBountyDescriptions (max_values: None, max_size: Some(314), added: 2789, mode: MaxEncodedLen) fn close_child_bounty_active() -> Weight { // Proof Size summary in bytes: - // Measured: `1325` - // Estimated: `18075` - // Minimum execution time: 58_008 nanoseconds. - Weight::from_parts(58_586_000, 18075) + // Measured: `1165` + // Estimated: `23025` + // Minimum execution time: 67_815_000 picoseconds. + Weight::from_parts(68_620_000, 23025) .saturating_add(T::DbWeight::get().reads(7_u64)) .saturating_add(T::DbWeight::get().writes(7_u64)) } @@ -221,12 +224,12 @@ impl WeightInfo for () { /// The range of component `d` is `[0, 300]`. fn add_child_bounty(d: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `742` - // Estimated: `10848` - // Minimum execution time: 46_743 nanoseconds. - Weight::from_parts(47_762_924, 10848) - // Standard Error: 135 - .saturating_add(Weight::from_parts(599, 0).saturating_mul(d.into())) + // Measured: `678` + // Estimated: `14808` + // Minimum execution time: 54_017_000 picoseconds. + Weight::from_parts(55_142_443, 14808) + // Standard Error: 310 + .saturating_add(Weight::from_parts(1_199, 0).saturating_mul(d.into())) .saturating_add(RocksDbWeight::get().reads(5_u64)) .saturating_add(RocksDbWeight::get().writes(6_u64)) } @@ -238,10 +241,10 @@ impl WeightInfo for () { /// Proof: ChildBounties ChildrenCuratorFees (max_values: None, max_size: Some(28), added: 2503, mode: MaxEncodedLen) fn propose_curator() -> Weight { // Proof Size summary in bytes: - // Measured: `796` - // Estimated: `7775` - // Minimum execution time: 16_417 nanoseconds. - Weight::from_parts(16_712_000, 7775) + // Measured: `732` + // Estimated: `10745` + // Minimum execution time: 19_222_000 picoseconds. + Weight::from_parts(19_446_000, 10745) .saturating_add(RocksDbWeight::get().reads(3_u64)) .saturating_add(RocksDbWeight::get().writes(2_u64)) } @@ -253,10 +256,10 @@ impl WeightInfo for () { /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) fn accept_curator() -> Weight { // Proof Size summary in bytes: - // Measured: `974` - // Estimated: `7875` - // Minimum execution time: 25_548 nanoseconds. - Weight::from_parts(25_919_000, 7875) + // Measured: `878` + // Estimated: `10845` + // Minimum execution time: 31_049_000 picoseconds. + Weight::from_parts(31_546_000, 10845) .saturating_add(RocksDbWeight::get().reads(3_u64)) .saturating_add(RocksDbWeight::get().writes(2_u64)) } @@ -268,10 +271,10 @@ impl WeightInfo for () { /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) fn unassign_curator() -> Weight { // Proof Size summary in bytes: - // Measured: `974` - // Estimated: `7875` - // Minimum execution time: 27_645 nanoseconds. - Weight::from_parts(27_947_000, 7875) + // Measured: `878` + // Estimated: `10845` + // Minimum execution time: 33_860_000 picoseconds. + Weight::from_parts(34_897_000, 10845) .saturating_add(RocksDbWeight::get().reads(3_u64)) .saturating_add(RocksDbWeight::get().writes(2_u64)) } @@ -281,10 +284,10 @@ impl WeightInfo for () { /// Proof: ChildBounties ChildBounties (max_values: None, max_size: Some(145), added: 2620, mode: MaxEncodedLen) fn award_child_bounty() -> Weight { // Proof Size summary in bytes: - // Measured: `839` - // Estimated: `5272` - // Minimum execution time: 20_002 nanoseconds. - Weight::from_parts(20_512_000, 5272) + // Measured: `775` + // Estimated: `7252` + // Minimum execution time: 23_178_000 picoseconds. + Weight::from_parts(23_412_000, 7252) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } @@ -298,10 +301,10 @@ impl WeightInfo for () { /// Proof: ChildBounties ChildBountyDescriptions (max_values: None, max_size: Some(314), added: 2789, mode: MaxEncodedLen) fn claim_child_bounty() -> Weight { // Proof Size summary in bytes: - // Measured: `744` - // Estimated: `12920` - // Minimum execution time: 63_752 nanoseconds. - Weight::from_parts(64_179_000, 12920) + // Measured: `648` + // Estimated: `15890` + // Minimum execution time: 76_189_000 picoseconds. + Weight::from_parts(76_770_000, 15890) .saturating_add(RocksDbWeight::get().reads(5_u64)) .saturating_add(RocksDbWeight::get().writes(6_u64)) } @@ -319,10 +322,10 @@ impl WeightInfo for () { /// Proof: ChildBounties ChildBountyDescriptions (max_values: None, max_size: Some(314), added: 2789, mode: MaxEncodedLen) fn close_child_bounty_added() -> Weight { // Proof Size summary in bytes: - // Measured: `1106` - // Estimated: `15472` - // Minimum execution time: 47_388 nanoseconds. - Weight::from_parts(47_946_000, 15472) + // Measured: `978` + // Estimated: `20422` + // Minimum execution time: 55_161_000 picoseconds. + Weight::from_parts(55_578_000, 20422) .saturating_add(RocksDbWeight::get().reads(6_u64)) .saturating_add(RocksDbWeight::get().writes(6_u64)) } @@ -340,10 +343,10 @@ impl WeightInfo for () { /// Proof: ChildBounties ChildBountyDescriptions (max_values: None, max_size: Some(314), added: 2789, mode: MaxEncodedLen) fn close_child_bounty_active() -> Weight { // Proof Size summary in bytes: - // Measured: `1325` - // Estimated: `18075` - // Minimum execution time: 58_008 nanoseconds. - Weight::from_parts(58_586_000, 18075) + // Measured: `1165` + // Estimated: `23025` + // Minimum execution time: 67_815_000 picoseconds. + Weight::from_parts(68_620_000, 23025) .saturating_add(RocksDbWeight::get().reads(7_u64)) .saturating_add(RocksDbWeight::get().writes(7_u64)) } diff --git a/frame/collective/src/weights.rs b/frame/collective/src/weights.rs index df233fc24..2199c0205 100644 --- a/frame/collective/src/weights.rs +++ b/frame/collective/src/weights.rs @@ -18,9 +18,9 @@ //! Autogenerated weights for pallet_collective //! //! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev -//! DATE: 2023-01-24, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! DATE: 2023-03-15, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` //! WORST CASE MAP SIZE: `1000000` -//! HOSTNAME: `bm2`, CPU: `Intel(R) Core(TM) i7-7700K CPU @ 4.20GHz` +//! HOSTNAME: `bm3`, CPU: `Intel(R) Core(TM) i7-7700K CPU @ 4.20GHz` //! EXECUTION: Some(Wasm), WASM-EXECUTION: Compiled, CHAIN: Some("dev"), DB CACHE: 1024 // Executed Command: @@ -31,6 +31,9 @@ // --steps=50 // --repeat=20 // --pallet=pallet_collective +// --no-storage-info +// --no-median-slopes +// --no-min-squares // --extrinsic=* // --execution=wasm // --wasm-execution=compiled @@ -76,20 +79,20 @@ impl WeightInfo for SubstrateWeight { /// The range of component `p` is `[0, 100]`. fn set_members(m: u32, _n: u32, p: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `0 + m * (3233 ±0) + p * (3223 ±0)` - // Estimated: `16586 + m * (7809 ±24) + p * (10238 ±24)` - // Minimum execution time: 17_093 nanoseconds. - Weight::from_parts(17_284_000, 16586) - // Standard Error: 64_700 - .saturating_add(Weight::from_parts(5_143_145, 0).saturating_mul(m.into())) - // Standard Error: 64_700 - .saturating_add(Weight::from_parts(7_480_941, 0).saturating_mul(p.into())) + // Measured: `0 + m * (3232 ±0) + p * (3190 ±0)` + // Estimated: `19428 + m * (7799 ±24) + p * (10110 ±24)` + // Minimum execution time: 18_772_000 picoseconds. + Weight::from_parts(18_931_000, 19428) + // Standard Error: 65_247 + .saturating_add(Weight::from_parts(5_050_136, 0).saturating_mul(m.into())) + // Standard Error: 65_247 + .saturating_add(Weight::from_parts(8_235_069, 0).saturating_mul(p.into())) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().reads((1_u64).saturating_mul(p.into()))) .saturating_add(T::DbWeight::get().writes(2_u64)) .saturating_add(T::DbWeight::get().writes((1_u64).saturating_mul(p.into()))) - .saturating_add(Weight::from_parts(0, 7809).saturating_mul(m.into())) - .saturating_add(Weight::from_parts(0, 10238).saturating_mul(p.into())) + .saturating_add(Weight::from_parts(0, 7799).saturating_mul(m.into())) + .saturating_add(Weight::from_parts(0, 10110).saturating_mul(p.into())) } /// Storage: Council Members (r:1 w:0) /// Proof Skipped: Council Members (max_values: Some(1), max_size: None, mode: Measured) @@ -97,14 +100,14 @@ impl WeightInfo for SubstrateWeight { /// The range of component `m` is `[1, 100]`. fn execute(b: u32, m: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `234 + m * (32 ±0)` - // Estimated: `730 + m * (32 ±0)` - // Minimum execution time: 15_972 nanoseconds. - Weight::from_parts(14_971_445, 730) - // Standard Error: 32 - .saturating_add(Weight::from_parts(1_775, 0).saturating_mul(b.into())) - // Standard Error: 334 - .saturating_add(Weight::from_parts(17_052, 0).saturating_mul(m.into())) + // Measured: `202 + m * (32 ±0)` + // Estimated: `1688 + m * (32 ±0)` + // Minimum execution time: 17_741_000 picoseconds. + Weight::from_parts(17_580_031, 1688) + // Standard Error: 113 + .saturating_add(Weight::from_parts(941, 0).saturating_mul(b.into())) + // Standard Error: 1_170 + .saturating_add(Weight::from_parts(17_329, 0).saturating_mul(m.into())) .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(Weight::from_parts(0, 32).saturating_mul(m.into())) } @@ -116,14 +119,14 @@ impl WeightInfo for SubstrateWeight { /// The range of component `m` is `[1, 100]`. fn propose_execute(b: u32, m: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `234 + m * (32 ±0)` - // Estimated: `3440 + m * (64 ±0)` - // Minimum execution time: 17_950 nanoseconds. - Weight::from_parts(17_019_558, 3440) - // Standard Error: 41 - .saturating_add(Weight::from_parts(1_807, 0).saturating_mul(b.into())) - // Standard Error: 432 - .saturating_add(Weight::from_parts(27_986, 0).saturating_mul(m.into())) + // Measured: `202 + m * (32 ±0)` + // Estimated: `5356 + m * (64 ±0)` + // Minimum execution time: 20_214_000 picoseconds. + Weight::from_parts(19_508_816, 5356) + // Standard Error: 46 + .saturating_add(Weight::from_parts(1_646, 0).saturating_mul(b.into())) + // Standard Error: 475 + .saturating_add(Weight::from_parts(27_145, 0).saturating_mul(m.into())) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(Weight::from_parts(0, 64).saturating_mul(m.into())) } @@ -142,16 +145,16 @@ impl WeightInfo for SubstrateWeight { /// The range of component `p` is `[1, 100]`. fn propose_proposed(b: u32, m: u32, p: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `556 + m * (32 ±0) + p * (36 ±0)` - // Estimated: `6355 + m * (165 ±0) + p * (180 ±0)` - // Minimum execution time: 24_817 nanoseconds. - Weight::from_parts(24_778_955, 6355) - // Standard Error: 73 - .saturating_add(Weight::from_parts(2_355, 0).saturating_mul(b.into())) - // Standard Error: 765 - .saturating_add(Weight::from_parts(20_518, 0).saturating_mul(m.into())) - // Standard Error: 755 - .saturating_add(Weight::from_parts(85_670, 0).saturating_mul(p.into())) + // Measured: `492 + m * (32 ±0) + p * (36 ±0)` + // Estimated: `10015 + m * (165 ±0) + p * (180 ±0)` + // Minimum execution time: 27_872_000 picoseconds. + Weight::from_parts(28_010_227, 10015) + // Standard Error: 90 + .saturating_add(Weight::from_parts(2_643, 0).saturating_mul(b.into())) + // Standard Error: 944 + .saturating_add(Weight::from_parts(17_897, 0).saturating_mul(m.into())) + // Standard Error: 932 + .saturating_add(Weight::from_parts(122_368, 0).saturating_mul(p.into())) .saturating_add(T::DbWeight::get().reads(4_u64)) .saturating_add(T::DbWeight::get().writes(4_u64)) .saturating_add(Weight::from_parts(0, 165).saturating_mul(m.into())) @@ -164,12 +167,12 @@ impl WeightInfo for SubstrateWeight { /// The range of component `m` is `[5, 100]`. fn vote(m: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `1006 + m * (64 ±0)` - // Estimated: `4980 + m * (128 ±0)` - // Minimum execution time: 19_790 nanoseconds. - Weight::from_parts(20_528_275, 4980) - // Standard Error: 651 - .saturating_add(Weight::from_parts(48_856, 0).saturating_mul(m.into())) + // Measured: `941 + m * (64 ±0)` + // Estimated: `6830 + m * (128 ±0)` + // Minimum execution time: 22_518_000 picoseconds. + Weight::from_parts(23_281_512, 6830) + // Standard Error: 647 + .saturating_add(Weight::from_parts(53_974, 0).saturating_mul(m.into())) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) .saturating_add(Weight::from_parts(0, 128).saturating_mul(m.into())) @@ -186,14 +189,14 @@ impl WeightInfo for SubstrateWeight { /// The range of component `p` is `[1, 100]`. fn close_early_disapproved(m: u32, p: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `626 + m * (64 ±0) + p * (36 ±0)` - // Estimated: `5893 + m * (260 ±0) + p * (144 ±0)` - // Minimum execution time: 25_564 nanoseconds. - Weight::from_parts(25_535_497, 5893) - // Standard Error: 610 - .saturating_add(Weight::from_parts(27_956, 0).saturating_mul(m.into())) - // Standard Error: 595 - .saturating_add(Weight::from_parts(84_835, 0).saturating_mul(p.into())) + // Measured: `530 + m * (64 ±0) + p * (36 ±0)` + // Estimated: `8475 + m * (260 ±0) + p * (144 ±0)` + // Minimum execution time: 29_020_000 picoseconds. + Weight::from_parts(29_009_937, 8475) + // Standard Error: 809 + .saturating_add(Weight::from_parts(31_952, 0).saturating_mul(m.into())) + // Standard Error: 789 + .saturating_add(Weight::from_parts(124_282, 0).saturating_mul(p.into())) .saturating_add(T::DbWeight::get().reads(3_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) .saturating_add(Weight::from_parts(0, 260).saturating_mul(m.into())) @@ -212,16 +215,16 @@ impl WeightInfo for SubstrateWeight { /// The range of component `p` is `[1, 100]`. fn close_early_approved(b: u32, m: u32, p: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `962 + b * (1 ±0) + m * (64 ±0) + p * (40 ±0)` - // Estimated: `9164 + b * (4 ±0) + m * (264 ±0) + p * (160 ±0)` - // Minimum execution time: 36_515 nanoseconds. - Weight::from_parts(36_626_648, 9164) - // Standard Error: 98 - .saturating_add(Weight::from_parts(2_295, 0).saturating_mul(b.into())) - // Standard Error: 1_036 - .saturating_add(Weight::from_parts(22_182, 0).saturating_mul(m.into())) - // Standard Error: 1_010 - .saturating_add(Weight::from_parts(100_034, 0).saturating_mul(p.into())) + // Measured: `832 + b * (1 ±0) + m * (64 ±0) + p * (40 ±0)` + // Estimated: `12636 + b * (4 ±0) + m * (264 ±0) + p * (160 ±0)` + // Minimum execution time: 41_541_000 picoseconds. + Weight::from_parts(43_640_322, 12636) + // Standard Error: 104 + .saturating_add(Weight::from_parts(2_178, 0).saturating_mul(b.into())) + // Standard Error: 1_101 + .saturating_add(Weight::from_parts(20_512, 0).saturating_mul(m.into())) + // Standard Error: 1_073 + .saturating_add(Weight::from_parts(133_112, 0).saturating_mul(p.into())) .saturating_add(T::DbWeight::get().reads(4_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) .saturating_add(Weight::from_parts(0, 4).saturating_mul(b.into())) @@ -242,14 +245,14 @@ impl WeightInfo for SubstrateWeight { /// The range of component `p` is `[1, 100]`. fn close_disapproved(m: u32, p: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `646 + m * (64 ±0) + p * (36 ±0)` - // Estimated: `7095 + m * (325 ±0) + p * (180 ±0)` - // Minimum execution time: 28_858 nanoseconds. - Weight::from_parts(28_050_047, 7095) - // Standard Error: 614 - .saturating_add(Weight::from_parts(34_031, 0).saturating_mul(m.into())) - // Standard Error: 599 - .saturating_add(Weight::from_parts(85_744, 0).saturating_mul(p.into())) + // Measured: `550 + m * (64 ±0) + p * (36 ±0)` + // Estimated: `10570 + m * (325 ±0) + p * (180 ±0)` + // Minimum execution time: 32_773_000 picoseconds. + Weight::from_parts(33_031_550, 10570) + // Standard Error: 1_205 + .saturating_add(Weight::from_parts(26_143, 0).saturating_mul(m.into())) + // Standard Error: 1_175 + .saturating_add(Weight::from_parts(137_503, 0).saturating_mul(p.into())) .saturating_add(T::DbWeight::get().reads(4_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) .saturating_add(Weight::from_parts(0, 325).saturating_mul(m.into())) @@ -270,16 +273,16 @@ impl WeightInfo for SubstrateWeight { /// The range of component `p` is `[1, 100]`. fn close_approved(b: u32, m: u32, p: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `982 + b * (1 ±0) + m * (64 ±0) + p * (40 ±0)` - // Estimated: `10565 + b * (5 ±0) + m * (330 ±0) + p * (200 ±0)` - // Minimum execution time: 38_608 nanoseconds. - Weight::from_parts(39_948_329, 10565) - // Standard Error: 84 - .saturating_add(Weight::from_parts(2_045, 0).saturating_mul(b.into())) - // Standard Error: 895 - .saturating_add(Weight::from_parts(22_669, 0).saturating_mul(m.into())) - // Standard Error: 872 - .saturating_add(Weight::from_parts(95_525, 0).saturating_mul(p.into())) + // Measured: `852 + b * (1 ±0) + m * (64 ±0) + p * (40 ±0)` + // Estimated: `14905 + b * (5 ±0) + m * (330 ±0) + p * (200 ±0)` + // Minimum execution time: 44_855_000 picoseconds. + Weight::from_parts(47_307_460, 14905) + // Standard Error: 100 + .saturating_add(Weight::from_parts(1_536, 0).saturating_mul(b.into())) + // Standard Error: 1_065 + .saturating_add(Weight::from_parts(20_549, 0).saturating_mul(m.into())) + // Standard Error: 1_038 + .saturating_add(Weight::from_parts(133_143, 0).saturating_mul(p.into())) .saturating_add(T::DbWeight::get().reads(5_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) .saturating_add(Weight::from_parts(0, 5).saturating_mul(b.into())) @@ -295,12 +298,12 @@ impl WeightInfo for SubstrateWeight { /// The range of component `p` is `[1, 100]`. fn disapprove_proposal(p: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `391 + p * (32 ±0)` - // Estimated: `1668 + p * (96 ±0)` - // Minimum execution time: 14_785 nanoseconds. - Weight::from_parts(16_393_818, 1668) - // Standard Error: 612 - .saturating_add(Weight::from_parts(76_786, 0).saturating_mul(p.into())) + // Measured: `359 + p * (32 ±0)` + // Estimated: `2562 + p * (96 ±0)` + // Minimum execution time: 16_420_000 picoseconds. + Weight::from_parts(18_236_525, 2562) + // Standard Error: 894 + .saturating_add(Weight::from_parts(115_590, 0).saturating_mul(p.into())) .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) .saturating_add(Weight::from_parts(0, 96).saturating_mul(p.into())) @@ -322,20 +325,20 @@ impl WeightInfo for () { /// The range of component `p` is `[0, 100]`. fn set_members(m: u32, _n: u32, p: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `0 + m * (3233 ±0) + p * (3223 ±0)` - // Estimated: `16586 + m * (7809 ±24) + p * (10238 ±24)` - // Minimum execution time: 17_093 nanoseconds. - Weight::from_parts(17_284_000, 16586) - // Standard Error: 64_700 - .saturating_add(Weight::from_parts(5_143_145, 0).saturating_mul(m.into())) - // Standard Error: 64_700 - .saturating_add(Weight::from_parts(7_480_941, 0).saturating_mul(p.into())) + // Measured: `0 + m * (3232 ±0) + p * (3190 ±0)` + // Estimated: `19428 + m * (7799 ±24) + p * (10110 ±24)` + // Minimum execution time: 18_772_000 picoseconds. + Weight::from_parts(18_931_000, 19428) + // Standard Error: 65_247 + .saturating_add(Weight::from_parts(5_050_136, 0).saturating_mul(m.into())) + // Standard Error: 65_247 + .saturating_add(Weight::from_parts(8_235_069, 0).saturating_mul(p.into())) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().reads((1_u64).saturating_mul(p.into()))) .saturating_add(RocksDbWeight::get().writes(2_u64)) .saturating_add(RocksDbWeight::get().writes((1_u64).saturating_mul(p.into()))) - .saturating_add(Weight::from_parts(0, 7809).saturating_mul(m.into())) - .saturating_add(Weight::from_parts(0, 10238).saturating_mul(p.into())) + .saturating_add(Weight::from_parts(0, 7799).saturating_mul(m.into())) + .saturating_add(Weight::from_parts(0, 10110).saturating_mul(p.into())) } /// Storage: Council Members (r:1 w:0) /// Proof Skipped: Council Members (max_values: Some(1), max_size: None, mode: Measured) @@ -343,14 +346,14 @@ impl WeightInfo for () { /// The range of component `m` is `[1, 100]`. fn execute(b: u32, m: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `234 + m * (32 ±0)` - // Estimated: `730 + m * (32 ±0)` - // Minimum execution time: 15_972 nanoseconds. - Weight::from_parts(14_971_445, 730) - // Standard Error: 32 - .saturating_add(Weight::from_parts(1_775, 0).saturating_mul(b.into())) - // Standard Error: 334 - .saturating_add(Weight::from_parts(17_052, 0).saturating_mul(m.into())) + // Measured: `202 + m * (32 ±0)` + // Estimated: `1688 + m * (32 ±0)` + // Minimum execution time: 17_741_000 picoseconds. + Weight::from_parts(17_580_031, 1688) + // Standard Error: 113 + .saturating_add(Weight::from_parts(941, 0).saturating_mul(b.into())) + // Standard Error: 1_170 + .saturating_add(Weight::from_parts(17_329, 0).saturating_mul(m.into())) .saturating_add(RocksDbWeight::get().reads(1_u64)) .saturating_add(Weight::from_parts(0, 32).saturating_mul(m.into())) } @@ -362,14 +365,14 @@ impl WeightInfo for () { /// The range of component `m` is `[1, 100]`. fn propose_execute(b: u32, m: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `234 + m * (32 ±0)` - // Estimated: `3440 + m * (64 ±0)` - // Minimum execution time: 17_950 nanoseconds. - Weight::from_parts(17_019_558, 3440) - // Standard Error: 41 - .saturating_add(Weight::from_parts(1_807, 0).saturating_mul(b.into())) - // Standard Error: 432 - .saturating_add(Weight::from_parts(27_986, 0).saturating_mul(m.into())) + // Measured: `202 + m * (32 ±0)` + // Estimated: `5356 + m * (64 ±0)` + // Minimum execution time: 20_214_000 picoseconds. + Weight::from_parts(19_508_816, 5356) + // Standard Error: 46 + .saturating_add(Weight::from_parts(1_646, 0).saturating_mul(b.into())) + // Standard Error: 475 + .saturating_add(Weight::from_parts(27_145, 0).saturating_mul(m.into())) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(Weight::from_parts(0, 64).saturating_mul(m.into())) } @@ -388,16 +391,16 @@ impl WeightInfo for () { /// The range of component `p` is `[1, 100]`. fn propose_proposed(b: u32, m: u32, p: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `556 + m * (32 ±0) + p * (36 ±0)` - // Estimated: `6355 + m * (165 ±0) + p * (180 ±0)` - // Minimum execution time: 24_817 nanoseconds. - Weight::from_parts(24_778_955, 6355) - // Standard Error: 73 - .saturating_add(Weight::from_parts(2_355, 0).saturating_mul(b.into())) - // Standard Error: 765 - .saturating_add(Weight::from_parts(20_518, 0).saturating_mul(m.into())) - // Standard Error: 755 - .saturating_add(Weight::from_parts(85_670, 0).saturating_mul(p.into())) + // Measured: `492 + m * (32 ±0) + p * (36 ±0)` + // Estimated: `10015 + m * (165 ±0) + p * (180 ±0)` + // Minimum execution time: 27_872_000 picoseconds. + Weight::from_parts(28_010_227, 10015) + // Standard Error: 90 + .saturating_add(Weight::from_parts(2_643, 0).saturating_mul(b.into())) + // Standard Error: 944 + .saturating_add(Weight::from_parts(17_897, 0).saturating_mul(m.into())) + // Standard Error: 932 + .saturating_add(Weight::from_parts(122_368, 0).saturating_mul(p.into())) .saturating_add(RocksDbWeight::get().reads(4_u64)) .saturating_add(RocksDbWeight::get().writes(4_u64)) .saturating_add(Weight::from_parts(0, 165).saturating_mul(m.into())) @@ -410,12 +413,12 @@ impl WeightInfo for () { /// The range of component `m` is `[5, 100]`. fn vote(m: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `1006 + m * (64 ±0)` - // Estimated: `4980 + m * (128 ±0)` - // Minimum execution time: 19_790 nanoseconds. - Weight::from_parts(20_528_275, 4980) - // Standard Error: 651 - .saturating_add(Weight::from_parts(48_856, 0).saturating_mul(m.into())) + // Measured: `941 + m * (64 ±0)` + // Estimated: `6830 + m * (128 ±0)` + // Minimum execution time: 22_518_000 picoseconds. + Weight::from_parts(23_281_512, 6830) + // Standard Error: 647 + .saturating_add(Weight::from_parts(53_974, 0).saturating_mul(m.into())) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) .saturating_add(Weight::from_parts(0, 128).saturating_mul(m.into())) @@ -432,14 +435,14 @@ impl WeightInfo for () { /// The range of component `p` is `[1, 100]`. fn close_early_disapproved(m: u32, p: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `626 + m * (64 ±0) + p * (36 ±0)` - // Estimated: `5893 + m * (260 ±0) + p * (144 ±0)` - // Minimum execution time: 25_564 nanoseconds. - Weight::from_parts(25_535_497, 5893) - // Standard Error: 610 - .saturating_add(Weight::from_parts(27_956, 0).saturating_mul(m.into())) - // Standard Error: 595 - .saturating_add(Weight::from_parts(84_835, 0).saturating_mul(p.into())) + // Measured: `530 + m * (64 ±0) + p * (36 ±0)` + // Estimated: `8475 + m * (260 ±0) + p * (144 ±0)` + // Minimum execution time: 29_020_000 picoseconds. + Weight::from_parts(29_009_937, 8475) + // Standard Error: 809 + .saturating_add(Weight::from_parts(31_952, 0).saturating_mul(m.into())) + // Standard Error: 789 + .saturating_add(Weight::from_parts(124_282, 0).saturating_mul(p.into())) .saturating_add(RocksDbWeight::get().reads(3_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) .saturating_add(Weight::from_parts(0, 260).saturating_mul(m.into())) @@ -458,16 +461,16 @@ impl WeightInfo for () { /// The range of component `p` is `[1, 100]`. fn close_early_approved(b: u32, m: u32, p: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `962 + b * (1 ±0) + m * (64 ±0) + p * (40 ±0)` - // Estimated: `9164 + b * (4 ±0) + m * (264 ±0) + p * (160 ±0)` - // Minimum execution time: 36_515 nanoseconds. - Weight::from_parts(36_626_648, 9164) - // Standard Error: 98 - .saturating_add(Weight::from_parts(2_295, 0).saturating_mul(b.into())) - // Standard Error: 1_036 - .saturating_add(Weight::from_parts(22_182, 0).saturating_mul(m.into())) - // Standard Error: 1_010 - .saturating_add(Weight::from_parts(100_034, 0).saturating_mul(p.into())) + // Measured: `832 + b * (1 ±0) + m * (64 ±0) + p * (40 ±0)` + // Estimated: `12636 + b * (4 ±0) + m * (264 ±0) + p * (160 ±0)` + // Minimum execution time: 41_541_000 picoseconds. + Weight::from_parts(43_640_322, 12636) + // Standard Error: 104 + .saturating_add(Weight::from_parts(2_178, 0).saturating_mul(b.into())) + // Standard Error: 1_101 + .saturating_add(Weight::from_parts(20_512, 0).saturating_mul(m.into())) + // Standard Error: 1_073 + .saturating_add(Weight::from_parts(133_112, 0).saturating_mul(p.into())) .saturating_add(RocksDbWeight::get().reads(4_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) .saturating_add(Weight::from_parts(0, 4).saturating_mul(b.into())) @@ -488,14 +491,14 @@ impl WeightInfo for () { /// The range of component `p` is `[1, 100]`. fn close_disapproved(m: u32, p: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `646 + m * (64 ±0) + p * (36 ±0)` - // Estimated: `7095 + m * (325 ±0) + p * (180 ±0)` - // Minimum execution time: 28_858 nanoseconds. - Weight::from_parts(28_050_047, 7095) - // Standard Error: 614 - .saturating_add(Weight::from_parts(34_031, 0).saturating_mul(m.into())) - // Standard Error: 599 - .saturating_add(Weight::from_parts(85_744, 0).saturating_mul(p.into())) + // Measured: `550 + m * (64 ±0) + p * (36 ±0)` + // Estimated: `10570 + m * (325 ±0) + p * (180 ±0)` + // Minimum execution time: 32_773_000 picoseconds. + Weight::from_parts(33_031_550, 10570) + // Standard Error: 1_205 + .saturating_add(Weight::from_parts(26_143, 0).saturating_mul(m.into())) + // Standard Error: 1_175 + .saturating_add(Weight::from_parts(137_503, 0).saturating_mul(p.into())) .saturating_add(RocksDbWeight::get().reads(4_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) .saturating_add(Weight::from_parts(0, 325).saturating_mul(m.into())) @@ -516,16 +519,16 @@ impl WeightInfo for () { /// The range of component `p` is `[1, 100]`. fn close_approved(b: u32, m: u32, p: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `982 + b * (1 ±0) + m * (64 ±0) + p * (40 ±0)` - // Estimated: `10565 + b * (5 ±0) + m * (330 ±0) + p * (200 ±0)` - // Minimum execution time: 38_608 nanoseconds. - Weight::from_parts(39_948_329, 10565) - // Standard Error: 84 - .saturating_add(Weight::from_parts(2_045, 0).saturating_mul(b.into())) - // Standard Error: 895 - .saturating_add(Weight::from_parts(22_669, 0).saturating_mul(m.into())) - // Standard Error: 872 - .saturating_add(Weight::from_parts(95_525, 0).saturating_mul(p.into())) + // Measured: `852 + b * (1 ±0) + m * (64 ±0) + p * (40 ±0)` + // Estimated: `14905 + b * (5 ±0) + m * (330 ±0) + p * (200 ±0)` + // Minimum execution time: 44_855_000 picoseconds. + Weight::from_parts(47_307_460, 14905) + // Standard Error: 100 + .saturating_add(Weight::from_parts(1_536, 0).saturating_mul(b.into())) + // Standard Error: 1_065 + .saturating_add(Weight::from_parts(20_549, 0).saturating_mul(m.into())) + // Standard Error: 1_038 + .saturating_add(Weight::from_parts(133_143, 0).saturating_mul(p.into())) .saturating_add(RocksDbWeight::get().reads(5_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) .saturating_add(Weight::from_parts(0, 5).saturating_mul(b.into())) @@ -541,12 +544,12 @@ impl WeightInfo for () { /// The range of component `p` is `[1, 100]`. fn disapprove_proposal(p: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `391 + p * (32 ±0)` - // Estimated: `1668 + p * (96 ±0)` - // Minimum execution time: 14_785 nanoseconds. - Weight::from_parts(16_393_818, 1668) - // Standard Error: 612 - .saturating_add(Weight::from_parts(76_786, 0).saturating_mul(p.into())) + // Measured: `359 + p * (32 ±0)` + // Estimated: `2562 + p * (96 ±0)` + // Minimum execution time: 16_420_000 picoseconds. + Weight::from_parts(18_236_525, 2562) + // Standard Error: 894 + .saturating_add(Weight::from_parts(115_590, 0).saturating_mul(p.into())) .saturating_add(RocksDbWeight::get().reads(1_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) .saturating_add(Weight::from_parts(0, 96).saturating_mul(p.into())) diff --git a/frame/contracts/src/weights.rs b/frame/contracts/src/weights.rs index 14e680fb6..ae8638eb7 100644 --- a/frame/contracts/src/weights.rs +++ b/frame/contracts/src/weights.rs @@ -18,26 +18,28 @@ //! Autogenerated weights for pallet_contracts //! //! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev -//! DATE: 2023-02-16, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! DATE: 2023-03-16, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` //! WORST CASE MAP SIZE: `1000000` //! HOSTNAME: `bm3`, CPU: `Intel(R) Core(TM) i7-7700K CPU @ 4.20GHz` //! EXECUTION: Some(Wasm), WASM-EXECUTION: Compiled, CHAIN: Some("dev"), DB CACHE: 1024 // Executed Command: -// /home/benchbot/cargo_target_dir/production/substrate +// ./target/production/substrate // benchmark // pallet +// --chain=dev // --steps=50 // --repeat=20 +// --pallet=pallet_contracts +// --no-storage-info +// --no-median-slopes +// --no-min-squares // --extrinsic=* // --execution=wasm // --wasm-execution=compiled // --heap-pages=4096 -// --json-file=/var/lib/gitlab-runner/builds/zyw4fam_/0/parity/mirrors/substrate/.git/.artifacts/bench.json -// --pallet=pallet_contracts -// --chain=dev -// --header=./HEADER-APACHE2 // --output=./frame/contracts/src/weights.rs +// --header=./HEADER-APACHE2 // --template=./.maintain/frame-weight-template.hbs #![cfg_attr(rustfmt, rustfmt_skip)] @@ -177,10 +179,9 @@ impl WeightInfo for SubstrateWeight { fn on_process_deletion_queue_batch() -> Weight { // Proof Size summary in bytes: // Measured: `109` - // Estimated: `604` - // Minimum execution time: 2_591 nanoseconds. - Weight::from_parts(2_817_000, 0) - .saturating_add(Weight::from_parts(0, 604)) + // Estimated: `1594` + // Minimum execution time: 2_679_000 picoseconds. + Weight::from_parts(2_907_000, 1594) .saturating_add(T::DbWeight::get().reads(1_u64)) } /// Storage: Skipped Metadata (r:0 w:0) @@ -188,14 +189,14 @@ impl WeightInfo for SubstrateWeight { /// The range of component `k` is `[0, 1024]`. fn on_initialize_per_trie_key(k: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `481 + k * (69 ±0)` - // Estimated: `471 + k * (70 ±0)` - // Minimum execution time: 10_190 nanoseconds. - Weight::from_parts(6_642_117, 0) - .saturating_add(Weight::from_parts(0, 471)) - // Standard Error: 992 - .saturating_add(Weight::from_parts(919_828, 0).saturating_mul(k.into())) + // Measured: `450 + k * (69 ±0)` + // Estimated: `440 + k * (70 ±0)` + // Minimum execution time: 11_162_000 picoseconds. + Weight::from_parts(5_734_923, 440) + // Standard Error: 1_097 + .saturating_add(Weight::from_parts(976_647, 0).saturating_mul(k.into())) .saturating_add(T::DbWeight::get().reads(1_u64)) + .saturating_add(T::DbWeight::get().reads((1_u64).saturating_mul(k.into()))) .saturating_add(T::DbWeight::get().writes(1_u64)) .saturating_add(T::DbWeight::get().writes((1_u64).saturating_mul(k.into()))) .saturating_add(Weight::from_parts(0, 70).saturating_mul(k.into())) @@ -205,13 +206,12 @@ impl WeightInfo for SubstrateWeight { /// The range of component `q` is `[0, 128]`. fn on_initialize_per_queue_item(q: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `281 + q * (33 ±0)` - // Estimated: `763 + q * (33 ±0)` - // Minimum execution time: 2_598 nanoseconds. - Weight::from_parts(10_288_252, 0) - .saturating_add(Weight::from_parts(0, 763)) - // Standard Error: 2_886 - .saturating_add(Weight::from_parts(1_092_420, 0).saturating_mul(q.into())) + // Measured: `250 + q * (33 ±0)` + // Estimated: `1725 + q * (33 ±0)` + // Minimum execution time: 2_626_000 picoseconds. + Weight::from_parts(10_626_314, 1725) + // Standard Error: 4_006 + .saturating_add(Weight::from_parts(1_298_864, 0).saturating_mul(q.into())) .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) .saturating_add(Weight::from_parts(0, 33).saturating_mul(q.into())) @@ -223,13 +223,12 @@ impl WeightInfo for SubstrateWeight { /// The range of component `c` is `[0, 61717]`. fn reinstrument(c: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `270 + c * (1 ±0)` - // Estimated: `3025 + c * (2 ±0)` - // Minimum execution time: 34_338 nanoseconds. - Weight::from_parts(32_159_677, 0) - .saturating_add(Weight::from_parts(0, 3025)) - // Standard Error: 53 - .saturating_add(Weight::from_parts(51_034, 0).saturating_mul(c.into())) + // Measured: `238 + c * (1 ±0)` + // Estimated: `3951 + c * (2 ±0)` + // Minimum execution time: 31_434_000 picoseconds. + Weight::from_parts(29_558_961, 3951) + // Standard Error: 55 + .saturating_add(Weight::from_parts(50_774, 0).saturating_mul(c.into())) .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) .saturating_add(Weight::from_parts(0, 2).saturating_mul(c.into())) @@ -247,13 +246,12 @@ impl WeightInfo for SubstrateWeight { /// The range of component `c` is `[0, 125952]`. fn call_with_code_per_byte(c: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `803` - // Estimated: `16930 + c * (5 ±0)` - // Minimum execution time: 385_587 nanoseconds. - Weight::from_parts(395_545_811, 0) - .saturating_add(Weight::from_parts(0, 16930)) - // Standard Error: 27 - .saturating_add(Weight::from_parts(31_342, 0).saturating_mul(c.into())) + // Measured: `707` + // Estimated: `21400 + c * (5 ±0)` + // Minimum execution time: 309_015_000 picoseconds. + Weight::from_parts(318_740_885, 21400) + // Standard Error: 29 + .saturating_add(Weight::from_parts(30_993, 0).saturating_mul(c.into())) .saturating_add(T::DbWeight::get().reads(6_u64)) .saturating_add(T::DbWeight::get().writes(4_u64)) .saturating_add(Weight::from_parts(0, 5).saturating_mul(c.into())) @@ -280,16 +278,15 @@ impl WeightInfo for SubstrateWeight { fn instantiate_with_code(c: u32, i: u32, s: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `270` - // Estimated: `20267` - // Minimum execution time: 3_799_742 nanoseconds. - Weight::from_parts(670_115_588, 0) - .saturating_add(Weight::from_parts(0, 20267)) - // Standard Error: 287 - .saturating_add(Weight::from_parts(93_885, 0).saturating_mul(c.into())) - // Standard Error: 16 - .saturating_add(Weight::from_parts(1_367, 0).saturating_mul(i.into())) - // Standard Error: 16 - .saturating_add(Weight::from_parts(1_781, 0).saturating_mul(s.into())) + // Estimated: `26207` + // Minimum execution time: 3_126_495_000 picoseconds. + Weight::from_parts(636_857_878, 26207) + // Standard Error: 294 + .saturating_add(Weight::from_parts(92_930, 0).saturating_mul(c.into())) + // Standard Error: 17 + .saturating_add(Weight::from_parts(1_128, 0).saturating_mul(i.into())) + // Standard Error: 17 + .saturating_add(Weight::from_parts(1_415, 0).saturating_mul(s.into())) .saturating_add(T::DbWeight::get().reads(9_u64)) .saturating_add(T::DbWeight::get().writes(10_u64)) } @@ -311,15 +308,14 @@ impl WeightInfo for SubstrateWeight { /// The range of component `s` is `[0, 1048576]`. fn instantiate(i: u32, s: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `546` - // Estimated: `22039` - // Minimum execution time: 1_949_008 nanoseconds. - Weight::from_parts(214_033_418, 0) - .saturating_add(Weight::from_parts(0, 22039)) + // Measured: `482` + // Estimated: `28521` + // Minimum execution time: 1_591_415_000 picoseconds. + Weight::from_parts(228_633_516, 28521) // Standard Error: 8 - .saturating_add(Weight::from_parts(1_666, 0).saturating_mul(i.into())) + .saturating_add(Weight::from_parts(1_449, 0).saturating_mul(i.into())) // Standard Error: 8 - .saturating_add(Weight::from_parts(1_801, 0).saturating_mul(s.into())) + .saturating_add(Weight::from_parts(1_444, 0).saturating_mul(s.into())) .saturating_add(T::DbWeight::get().reads(9_u64)) .saturating_add(T::DbWeight::get().writes(7_u64)) } @@ -335,11 +331,10 @@ impl WeightInfo for SubstrateWeight { /// Proof Skipped: System EventTopics (max_values: None, max_size: None, mode: Measured) fn call() -> Weight { // Proof Size summary in bytes: - // Measured: `855` - // Estimated: `17145` - // Minimum execution time: 146_654 nanoseconds. - Weight::from_parts(147_528_000, 0) - .saturating_add(Weight::from_parts(0, 17145)) + // Measured: `759` + // Estimated: `21615` + // Minimum execution time: 167_905_000 picoseconds. + Weight::from_parts(169_247_000, 21615) .saturating_add(T::DbWeight::get().reads(6_u64)) .saturating_add(T::DbWeight::get().writes(4_u64)) } @@ -355,12 +350,11 @@ impl WeightInfo for SubstrateWeight { fn upload_code(c: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `109` - // Estimated: `5386` - // Minimum execution time: 387_889 nanoseconds. - Weight::from_parts(391_379_335, 0) - .saturating_add(Weight::from_parts(0, 5386)) - // Standard Error: 89 - .saturating_add(Weight::from_parts(94_810, 0).saturating_mul(c.into())) + // Estimated: `7366` + // Minimum execution time: 305_128_000 picoseconds. + Weight::from_parts(310_911_395, 7366) + // Standard Error: 70 + .saturating_add(Weight::from_parts(94_067, 0).saturating_mul(c.into())) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().writes(4_u64)) } @@ -374,11 +368,10 @@ impl WeightInfo for SubstrateWeight { /// Proof: Contracts PristineCode (max_values: None, max_size: Some(125988), added: 128463, mode: Measured) fn remove_code() -> Weight { // Proof Size summary in bytes: - // Measured: `287` - // Estimated: `6098` - // Minimum execution time: 26_014 nanoseconds. - Weight::from_parts(26_510_000, 0) - .saturating_add(Weight::from_parts(0, 6098)) + // Measured: `255` + // Estimated: `7950` + // Minimum execution time: 29_605_000 picoseconds. + Weight::from_parts(29_986_000, 7950) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().writes(4_u64)) } @@ -390,11 +383,10 @@ impl WeightInfo for SubstrateWeight { /// Proof Skipped: System EventTopics (max_values: None, max_size: None, mode: Measured) fn set_code() -> Weight { // Proof Size summary in bytes: - // Measured: `666` - // Estimated: `16848` - // Minimum execution time: 30_177 nanoseconds. - Weight::from_parts(30_639_000, 0) - .saturating_add(Weight::from_parts(0, 16848)) + // Measured: `570` + // Estimated: `19530` + // Minimum execution time: 33_824_000 picoseconds. + Weight::from_parts(34_388_000, 19530) .saturating_add(T::DbWeight::get().reads(6_u64)) .saturating_add(T::DbWeight::get().writes(6_u64)) } @@ -411,13 +403,12 @@ impl WeightInfo for SubstrateWeight { /// The range of component `r` is `[0, 20]`. fn seal_caller(r: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `877 + r * (480 ±0)` - // Estimated: `17295 + r * (2400 ±0)` - // Minimum execution time: 373_786 nanoseconds. - Weight::from_parts(377_332_691, 0) - .saturating_add(Weight::from_parts(0, 17295)) - // Standard Error: 51_211 - .saturating_add(Weight::from_parts(17_715_615, 0).saturating_mul(r.into())) + // Measured: `781 + r * (480 ±0)` + // Estimated: `21765 + r * (2400 ±0)` + // Minimum execution time: 295_283_000 picoseconds. + Weight::from_parts(295_895_211, 21765) + // Standard Error: 44_860 + .saturating_add(Weight::from_parts(22_913_078, 0).saturating_mul(r.into())) .saturating_add(T::DbWeight::get().reads(6_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) .saturating_add(Weight::from_parts(0, 2400).saturating_mul(r.into())) @@ -435,17 +426,16 @@ impl WeightInfo for SubstrateWeight { /// The range of component `r` is `[0, 20]`. fn seal_is_contract(r: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `917 + r * (21778 ±0)` - // Estimated: `17295 + r * (306895 ±0)` - // Minimum execution time: 374_009 nanoseconds. - Weight::from_parts(238_991_986, 0) - .saturating_add(Weight::from_parts(0, 17295)) - // Standard Error: 464_711 - .saturating_add(Weight::from_parts(249_099_538, 0).saturating_mul(r.into())) + // Measured: `821 + r * (19218 ±0)` + // Estimated: `21765 + r * (294095 ±0)` + // Minimum execution time: 295_760_000 picoseconds. + Weight::from_parts(151_512_811, 21765) + // Standard Error: 522_974 + .saturating_add(Weight::from_parts(263_028_353, 0).saturating_mul(r.into())) .saturating_add(T::DbWeight::get().reads(6_u64)) .saturating_add(T::DbWeight::get().reads((80_u64).saturating_mul(r.into()))) .saturating_add(T::DbWeight::get().writes(3_u64)) - .saturating_add(Weight::from_parts(0, 306895).saturating_mul(r.into())) + .saturating_add(Weight::from_parts(0, 294095).saturating_mul(r.into())) } /// Storage: System Account (r:1 w:0) /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: Measured) @@ -460,17 +450,16 @@ impl WeightInfo for SubstrateWeight { /// The range of component `r` is `[0, 20]`. fn seal_code_hash(r: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `921 + r * (22099 ±0)` - // Estimated: `17340 + r * (308500 ±0)` - // Minimum execution time: 375_058 nanoseconds. - Weight::from_parts(238_765_068, 0) - .saturating_add(Weight::from_parts(0, 17340)) - // Standard Error: 662_617 - .saturating_add(Weight::from_parts(302_175_089, 0).saturating_mul(r.into())) + // Measured: `825 + r * (19539 ±0)` + // Estimated: `21810 + r * (295700 ±0)` + // Minimum execution time: 295_950_000 picoseconds. + Weight::from_parts(164_516_076, 21810) + // Standard Error: 469_540 + .saturating_add(Weight::from_parts(321_138_601, 0).saturating_mul(r.into())) .saturating_add(T::DbWeight::get().reads(6_u64)) .saturating_add(T::DbWeight::get().reads((80_u64).saturating_mul(r.into()))) .saturating_add(T::DbWeight::get().writes(3_u64)) - .saturating_add(Weight::from_parts(0, 308500).saturating_mul(r.into())) + .saturating_add(Weight::from_parts(0, 295700).saturating_mul(r.into())) } /// Storage: System Account (r:1 w:0) /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: Measured) @@ -485,13 +474,12 @@ impl WeightInfo for SubstrateWeight { /// The range of component `r` is `[0, 20]`. fn seal_own_code_hash(r: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `884 + r * (480 ±0)` - // Estimated: `17330 + r * (2400 ±0)` - // Minimum execution time: 374_747 nanoseconds. - Weight::from_parts(376_482_380, 0) - .saturating_add(Weight::from_parts(0, 17330)) - // Standard Error: 61_919 - .saturating_add(Weight::from_parts(22_376_795, 0).saturating_mul(r.into())) + // Measured: `788 + r * (480 ±0)` + // Estimated: `21800 + r * (2400 ±0)` + // Minimum execution time: 295_916_000 picoseconds. + Weight::from_parts(299_964_913, 21800) + // Standard Error: 78_211 + .saturating_add(Weight::from_parts(28_933_603, 0).saturating_mul(r.into())) .saturating_add(T::DbWeight::get().reads(6_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) .saturating_add(Weight::from_parts(0, 2400).saturating_mul(r.into())) @@ -509,13 +497,12 @@ impl WeightInfo for SubstrateWeight { /// The range of component `r` is `[0, 20]`. fn seal_caller_is_origin(r: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `874 + r * (240 ±0)` - // Estimated: `17265 + r * (1200 ±0)` - // Minimum execution time: 372_287 nanoseconds. - Weight::from_parts(376_250_858, 0) - .saturating_add(Weight::from_parts(0, 17265)) - // Standard Error: 40_119 - .saturating_add(Weight::from_parts(11_359_647, 0).saturating_mul(r.into())) + // Measured: `778 + r * (240 ±0)` + // Estimated: `21735 + r * (1200 ±0)` + // Minimum execution time: 293_588_000 picoseconds. + Weight::from_parts(297_568_984, 21735) + // Standard Error: 35_513 + .saturating_add(Weight::from_parts(11_074_880, 0).saturating_mul(r.into())) .saturating_add(T::DbWeight::get().reads(6_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) .saturating_add(Weight::from_parts(0, 1200).saturating_mul(r.into())) @@ -533,13 +520,12 @@ impl WeightInfo for SubstrateWeight { /// The range of component `r` is `[0, 20]`. fn seal_address(r: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `878 + r * (480 ±0)` - // Estimated: `17260 + r * (2400 ±0)` - // Minimum execution time: 374_445 nanoseconds. - Weight::from_parts(377_243_521, 0) - .saturating_add(Weight::from_parts(0, 17260)) - // Standard Error: 53_032 - .saturating_add(Weight::from_parts(17_684_246, 0).saturating_mul(r.into())) + // Measured: `782 + r * (480 ±0)` + // Estimated: `21730 + r * (2400 ±0)` + // Minimum execution time: 294_929_000 picoseconds. + Weight::from_parts(295_916_817, 21730) + // Standard Error: 64_902 + .saturating_add(Weight::from_parts(22_944_018, 0).saturating_mul(r.into())) .saturating_add(T::DbWeight::get().reads(6_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) .saturating_add(Weight::from_parts(0, 2400).saturating_mul(r.into())) @@ -557,13 +543,12 @@ impl WeightInfo for SubstrateWeight { /// The range of component `r` is `[0, 20]`. fn seal_gas_left(r: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `879 + r * (480 ±0)` - // Estimated: `17250 + r * (2405 ±0)` - // Minimum execution time: 374_029 nanoseconds. - Weight::from_parts(380_415_186, 0) - .saturating_add(Weight::from_parts(0, 17250)) - // Standard Error: 60_562 - .saturating_add(Weight::from_parts(17_152_599, 0).saturating_mul(r.into())) + // Measured: `783 + r * (480 ±0)` + // Estimated: `21720 + r * (2405 ±0)` + // Minimum execution time: 296_783_000 picoseconds. + Weight::from_parts(299_399_332, 21720) + // Standard Error: 50_982 + .saturating_add(Weight::from_parts(22_173_249, 0).saturating_mul(r.into())) .saturating_add(T::DbWeight::get().reads(6_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) .saturating_add(Weight::from_parts(0, 2405).saturating_mul(r.into())) @@ -581,16 +566,15 @@ impl WeightInfo for SubstrateWeight { /// The range of component `r` is `[0, 20]`. fn seal_balance(r: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `1049 + r * (480 ±0)` - // Estimated: `19849 + r * (2456 ±0)` - // Minimum execution time: 373_999 nanoseconds. - Weight::from_parts(381_757_033, 0) - .saturating_add(Weight::from_parts(0, 19849)) - // Standard Error: 97_983 - .saturating_add(Weight::from_parts(98_290_984, 0).saturating_mul(r.into())) + // Measured: `921 + r * (480 ±0)` + // Estimated: `24193 + r * (2451 ±0)` + // Minimum execution time: 294_120_000 picoseconds. + Weight::from_parts(305_889_989, 24193) + // Standard Error: 116_244 + .saturating_add(Weight::from_parts(114_781_094, 0).saturating_mul(r.into())) .saturating_add(T::DbWeight::get().reads(7_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) - .saturating_add(Weight::from_parts(0, 2456).saturating_mul(r.into())) + .saturating_add(Weight::from_parts(0, 2451).saturating_mul(r.into())) } /// Storage: System Account (r:1 w:0) /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: Measured) @@ -605,13 +589,12 @@ impl WeightInfo for SubstrateWeight { /// The range of component `r` is `[0, 20]`. fn seal_value_transferred(r: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `888 + r * (480 ±0)` - // Estimated: `17360 + r * (2400 ±0)` - // Minimum execution time: 374_197 nanoseconds. - Weight::from_parts(377_755_896, 0) - .saturating_add(Weight::from_parts(0, 17360)) - // Standard Error: 60_542 - .saturating_add(Weight::from_parts(17_442_065, 0).saturating_mul(r.into())) + // Measured: `792 + r * (480 ±0)` + // Estimated: `21830 + r * (2400 ±0)` + // Minimum execution time: 295_569_000 picoseconds. + Weight::from_parts(299_774_774, 21830) + // Standard Error: 46_252 + .saturating_add(Weight::from_parts(21_753_842, 0).saturating_mul(r.into())) .saturating_add(T::DbWeight::get().reads(6_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) .saturating_add(Weight::from_parts(0, 2400).saturating_mul(r.into())) @@ -629,13 +612,12 @@ impl WeightInfo for SubstrateWeight { /// The range of component `r` is `[0, 20]`. fn seal_minimum_balance(r: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `886 + r * (480 ±0)` - // Estimated: `17290 + r * (2400 ±0)` - // Minimum execution time: 373_888 nanoseconds. - Weight::from_parts(377_825_771, 0) - .saturating_add(Weight::from_parts(0, 17290)) - // Standard Error: 38_026 - .saturating_add(Weight::from_parts(17_147_903, 0).saturating_mul(r.into())) + // Measured: `790 + r * (480 ±0)` + // Estimated: `21760 + r * (2400 ±0)` + // Minimum execution time: 295_400_000 picoseconds. + Weight::from_parts(300_637_266, 21760) + // Standard Error: 41_263 + .saturating_add(Weight::from_parts(21_565_466, 0).saturating_mul(r.into())) .saturating_add(T::DbWeight::get().reads(6_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) .saturating_add(Weight::from_parts(0, 2400).saturating_mul(r.into())) @@ -653,13 +635,12 @@ impl WeightInfo for SubstrateWeight { /// The range of component `r` is `[0, 20]`. fn seal_block_number(r: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `883 + r * (480 ±0)` - // Estimated: `17315 + r * (2400 ±0)` - // Minimum execution time: 373_904 nanoseconds. - Weight::from_parts(378_652_372, 0) - .saturating_add(Weight::from_parts(0, 17315)) - // Standard Error: 43_833 - .saturating_add(Weight::from_parts(16_936_781, 0).saturating_mul(r.into())) + // Measured: `787 + r * (480 ±0)` + // Estimated: `21785 + r * (2400 ±0)` + // Minimum execution time: 294_696_000 picoseconds. + Weight::from_parts(297_196_796, 21785) + // Standard Error: 61_173 + .saturating_add(Weight::from_parts(21_903_191, 0).saturating_mul(r.into())) .saturating_add(T::DbWeight::get().reads(6_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) .saturating_add(Weight::from_parts(0, 2400).saturating_mul(r.into())) @@ -677,13 +658,12 @@ impl WeightInfo for SubstrateWeight { /// The range of component `r` is `[0, 20]`. fn seal_now(r: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `874 + r * (480 ±0)` - // Estimated: `17245 + r * (2400 ±0)` - // Minimum execution time: 373_473 nanoseconds. - Weight::from_parts(376_386_312, 0) - .saturating_add(Weight::from_parts(0, 17245)) - // Standard Error: 46_945 - .saturating_add(Weight::from_parts(17_336_462, 0).saturating_mul(r.into())) + // Measured: `778 + r * (480 ±0)` + // Estimated: `21715 + r * (2400 ±0)` + // Minimum execution time: 297_714_000 picoseconds. + Weight::from_parts(306_030_237, 21715) + // Standard Error: 69_984 + .saturating_add(Weight::from_parts(21_744_413, 0).saturating_mul(r.into())) .saturating_add(T::DbWeight::get().reads(6_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) .saturating_add(Weight::from_parts(0, 2400).saturating_mul(r.into())) @@ -703,13 +683,12 @@ impl WeightInfo for SubstrateWeight { /// The range of component `r` is `[0, 20]`. fn seal_weight_to_fee(r: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `951 + r * (800 ±0)` - // Estimated: `19046 + r * (4805 ±0)` - // Minimum execution time: 373_661 nanoseconds. - Weight::from_parts(385_824_015, 0) - .saturating_add(Weight::from_parts(0, 19046)) - // Standard Error: 75_964 - .saturating_add(Weight::from_parts(88_530_074, 0).saturating_mul(r.into())) + // Measured: `856 + r * (800 ±0)` + // Estimated: `24416 + r * (4805 ±0)` + // Minimum execution time: 295_180_000 picoseconds. + Weight::from_parts(308_851_025, 24416) + // Standard Error: 115_126 + .saturating_add(Weight::from_parts(98_678_404, 0).saturating_mul(r.into())) .saturating_add(T::DbWeight::get().reads(7_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) .saturating_add(Weight::from_parts(0, 4805).saturating_mul(r.into())) @@ -727,13 +706,12 @@ impl WeightInfo for SubstrateWeight { /// The range of component `r` is `[0, 20]`. fn seal_gas(r: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `841 + r * (320 ±0)` - // Estimated: `17120 + r * (1600 ±0)` - // Minimum execution time: 133_849 nanoseconds. - Weight::from_parts(137_283_391, 0) - .saturating_add(Weight::from_parts(0, 17120)) - // Standard Error: 13_312 - .saturating_add(Weight::from_parts(8_055_328, 0).saturating_mul(r.into())) + // Measured: `745 + r * (320 ±0)` + // Estimated: `21590 + r * (1600 ±0)` + // Minimum execution time: 151_587_000 picoseconds. + Weight::from_parts(155_713_096, 21590) + // Standard Error: 32_464 + .saturating_add(Weight::from_parts(8_332_778, 0).saturating_mul(r.into())) .saturating_add(T::DbWeight::get().reads(6_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) .saturating_add(Weight::from_parts(0, 1600).saturating_mul(r.into())) @@ -751,13 +729,12 @@ impl WeightInfo for SubstrateWeight { /// The range of component `r` is `[0, 20]`. fn seal_input(r: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `876 + r * (480 ±0)` - // Estimated: `17245 + r * (2400 ±0)` - // Minimum execution time: 373_468 nanoseconds. - Weight::from_parts(376_121_093, 0) - .saturating_add(Weight::from_parts(0, 17245)) - // Standard Error: 61_857 - .saturating_add(Weight::from_parts(15_868_414, 0).saturating_mul(r.into())) + // Measured: `780 + r * (480 ±0)` + // Estimated: `21715 + r * (2400 ±0)` + // Minimum execution time: 295_008_000 picoseconds. + Weight::from_parts(297_982_800, 21715) + // Standard Error: 37_480 + .saturating_add(Weight::from_parts(17_970_520, 0).saturating_mul(r.into())) .saturating_add(T::DbWeight::get().reads(6_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) .saturating_add(Weight::from_parts(0, 2400).saturating_mul(r.into())) @@ -775,13 +752,12 @@ impl WeightInfo for SubstrateWeight { /// The range of component `n` is `[0, 1024]`. fn seal_input_per_kb(n: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `1356` - // Estimated: `19650` - // Minimum execution time: 390_668 nanoseconds. - Weight::from_parts(419_608_449, 0) - .saturating_add(Weight::from_parts(0, 19650)) - // Standard Error: 4_890 - .saturating_add(Weight::from_parts(9_672_288, 0).saturating_mul(n.into())) + // Measured: `1260` + // Estimated: `24120` + // Minimum execution time: 314_970_000 picoseconds. + Weight::from_parts(345_339_508, 24120) + // Standard Error: 4_112 + .saturating_add(Weight::from_parts(9_746_966, 0).saturating_mul(n.into())) .saturating_add(T::DbWeight::get().reads(6_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) } @@ -798,13 +774,12 @@ impl WeightInfo for SubstrateWeight { /// The range of component `r` is `[0, 1]`. fn seal_return(r: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `864 + r * (45 ±0)` - // Estimated: `17190 + r * (225 ±0)` - // Minimum execution time: 371_309 nanoseconds. - Weight::from_parts(373_625_402, 0) - .saturating_add(Weight::from_parts(0, 17190)) - // Standard Error: 419_605 - .saturating_add(Weight::from_parts(1_737_397, 0).saturating_mul(r.into())) + // Measured: `768 + r * (45 ±0)` + // Estimated: `21660 + r * (225 ±0)` + // Minimum execution time: 293_072_000 picoseconds. + Weight::from_parts(295_694_824, 21660) + // Standard Error: 219_523 + .saturating_add(Weight::from_parts(783_975, 0).saturating_mul(r.into())) .saturating_add(T::DbWeight::get().reads(6_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) .saturating_add(Weight::from_parts(0, 225).saturating_mul(r.into())) @@ -822,13 +797,12 @@ impl WeightInfo for SubstrateWeight { /// The range of component `n` is `[0, 1024]`. fn seal_return_per_kb(n: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `874` - // Estimated: `17285` - // Minimum execution time: 374_094 nanoseconds. - Weight::from_parts(375_965_200, 0) - .saturating_add(Weight::from_parts(0, 17285)) - // Standard Error: 1_127 - .saturating_add(Weight::from_parts(232_645, 0).saturating_mul(n.into())) + // Measured: `778` + // Estimated: `21755` + // Minimum execution time: 295_959_000 picoseconds. + Weight::from_parts(299_004_761, 21755) + // Standard Error: 3_988 + .saturating_add(Weight::from_parts(194_268, 0).saturating_mul(n.into())) .saturating_add(T::DbWeight::get().reads(6_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) } @@ -849,18 +823,17 @@ impl WeightInfo for SubstrateWeight { /// The range of component `r` is `[0, 1]`. fn seal_terminate(r: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `906 + r * (452 ±0)` - // Estimated: `20242 + r * (15004 ±0)` - // Minimum execution time: 373_123 nanoseconds. - Weight::from_parts(374_924_634, 0) - .saturating_add(Weight::from_parts(0, 20242)) - // Standard Error: 378_010 - .saturating_add(Weight::from_parts(70_441_665, 0).saturating_mul(r.into())) + // Measured: `810 + r * (356 ±0)` + // Estimated: `25511 + r * (15321 ±0)` + // Minimum execution time: 294_626_000 picoseconds. + Weight::from_parts(297_839_179, 25511) + // Standard Error: 707_428 + .saturating_add(Weight::from_parts(81_507_620, 0).saturating_mul(r.into())) .saturating_add(T::DbWeight::get().reads(6_u64)) .saturating_add(T::DbWeight::get().reads((6_u64).saturating_mul(r.into()))) .saturating_add(T::DbWeight::get().writes(3_u64)) .saturating_add(T::DbWeight::get().writes((7_u64).saturating_mul(r.into()))) - .saturating_add(Weight::from_parts(0, 15004).saturating_mul(r.into())) + .saturating_add(Weight::from_parts(0, 15321).saturating_mul(r.into())) } /// Storage: System Account (r:1 w:0) /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: Measured) @@ -877,13 +850,12 @@ impl WeightInfo for SubstrateWeight { /// The range of component `r` is `[0, 20]`. fn seal_random(r: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `921 + r * (800 ±0)` - // Estimated: `18835 + r * (4805 ±0)` - // Minimum execution time: 373_291 nanoseconds. - Weight::from_parts(385_684_344, 0) - .saturating_add(Weight::from_parts(0, 18835)) - // Standard Error: 99_025 - .saturating_add(Weight::from_parts(111_308_793, 0).saturating_mul(r.into())) + // Measured: `825 + r * (800 ±0)` + // Estimated: `24199 + r * (4805 ±0)` + // Minimum execution time: 295_431_000 picoseconds. + Weight::from_parts(310_428_531, 24199) + // Standard Error: 143_501 + .saturating_add(Weight::from_parts(136_529_376, 0).saturating_mul(r.into())) .saturating_add(T::DbWeight::get().reads(7_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) .saturating_add(Weight::from_parts(0, 4805).saturating_mul(r.into())) @@ -901,13 +873,12 @@ impl WeightInfo for SubstrateWeight { /// The range of component `r` is `[0, 20]`. fn seal_deposit_event(r: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `874 + r * (800 ±0)` - // Estimated: `17250 + r * (4000 ±0)` - // Minimum execution time: 371_900 nanoseconds. - Weight::from_parts(384_166_626, 0) - .saturating_add(Weight::from_parts(0, 17250)) - // Standard Error: 205_255 - .saturating_add(Weight::from_parts(229_214_157, 0).saturating_mul(r.into())) + // Measured: `778 + r * (800 ±0)` + // Estimated: `21720 + r * (4000 ±0)` + // Minimum execution time: 293_778_000 picoseconds. + Weight::from_parts(323_404_081, 21720) + // Standard Error: 243_095 + .saturating_add(Weight::from_parts(269_012_482, 0).saturating_mul(r.into())) .saturating_add(T::DbWeight::get().reads(6_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) .saturating_add(Weight::from_parts(0, 4000).saturating_mul(r.into())) @@ -926,15 +897,14 @@ impl WeightInfo for SubstrateWeight { /// The range of component `n` is `[0, 16]`. fn seal_deposit_event_per_topic_and_kb(t: u32, n: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `1821 + t * (2608 ±0) + n * (7 ±0)` - // Estimated: `21870 + t * (211030 ±0) + n * (50 ±0)` - // Minimum execution time: 1_289_873 nanoseconds. - Weight::from_parts(581_702_206, 0) - .saturating_add(Weight::from_parts(0, 21870)) - // Standard Error: 665_638 - .saturating_add(Weight::from_parts(181_470_553, 0).saturating_mul(t.into())) - // Standard Error: 182_816 - .saturating_add(Weight::from_parts(71_635_250, 0).saturating_mul(n.into())) + // Measured: `1725 + t * (2608 ±0) + n * (7 ±0)` + // Estimated: `26340 + t * (211030 ±0) + n * (50 ±0)` + // Minimum execution time: 1_317_129_000 picoseconds. + Weight::from_parts(566_722_295, 26340) + // Standard Error: 619_390 + .saturating_add(Weight::from_parts(194_334_643, 0).saturating_mul(t.into())) + // Standard Error: 170_114 + .saturating_add(Weight::from_parts(67_458_751, 0).saturating_mul(n.into())) .saturating_add(T::DbWeight::get().reads(6_u64)) .saturating_add(T::DbWeight::get().reads((80_u64).saturating_mul(t.into()))) .saturating_add(T::DbWeight::get().writes(3_u64)) @@ -955,13 +925,12 @@ impl WeightInfo for SubstrateWeight { /// The range of component `r` is `[0, 20]`. fn seal_debug_message(r: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `873 + r * (560 ±0)` - // Estimated: `17240 + r * (2800 ±0)` - // Minimum execution time: 148_635 nanoseconds. - Weight::from_parts(154_095_712, 0) - .saturating_add(Weight::from_parts(0, 17240)) - // Standard Error: 77_790 - .saturating_add(Weight::from_parts(14_837_085, 0).saturating_mul(r.into())) + // Measured: `777 + r * (560 ±0)` + // Estimated: `21710 + r * (2800 ±0)` + // Minimum execution time: 159_087_000 picoseconds. + Weight::from_parts(163_703_981, 21710) + // Standard Error: 26_067 + .saturating_add(Weight::from_parts(14_810_256, 0).saturating_mul(r.into())) .saturating_add(T::DbWeight::get().reads(6_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) .saturating_add(Weight::from_parts(0, 2800).saturating_mul(r.into())) @@ -979,13 +948,12 @@ impl WeightInfo for SubstrateWeight { /// The range of component `i` is `[0, 1024]`. fn seal_debug_message_per_kb(i: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `125824` - // Estimated: `265128` - // Minimum execution time: 501_014 nanoseconds. - Weight::from_parts(505_634_218, 0) - .saturating_add(Weight::from_parts(0, 265128)) - // Standard Error: 2_441 - .saturating_add(Weight::from_parts(819_257, 0).saturating_mul(i.into())) + // Measured: `125728` + // Estimated: `269982` + // Minimum execution time: 413_391_000 picoseconds. + Weight::from_parts(414_927_640, 269982) + // Standard Error: 1_740 + .saturating_add(Weight::from_parts(763_359, 0).saturating_mul(i.into())) .saturating_add(T::DbWeight::get().reads(6_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) } @@ -994,13 +962,12 @@ impl WeightInfo for SubstrateWeight { /// The range of component `r` is `[0, 10]`. fn seal_set_storage(r: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `911 + r * (23420 ±0)` - // Estimated: `911 + r * (23418 ±0)` - // Minimum execution time: 375_301 nanoseconds. - Weight::from_parts(291_498_841, 0) - .saturating_add(Weight::from_parts(0, 911)) - // Standard Error: 809_989 - .saturating_add(Weight::from_parts(464_550_291, 0).saturating_mul(r.into())) + // Measured: `815 + r * (23420 ±0)` + // Estimated: `815 + r * (23418 ±0)` + // Minimum execution time: 296_108_000 picoseconds. + Weight::from_parts(208_214_040, 815) + // Standard Error: 860_573 + .saturating_add(Weight::from_parts(480_342_550, 0).saturating_mul(r.into())) .saturating_add(T::DbWeight::get().reads(6_u64)) .saturating_add(T::DbWeight::get().reads((80_u64).saturating_mul(r.into()))) .saturating_add(T::DbWeight::get().writes(3_u64)) @@ -1012,13 +979,12 @@ impl WeightInfo for SubstrateWeight { /// The range of component `n` is `[0, 8]`. fn seal_set_storage_per_new_kb(n: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `12672 + n * (11945 ±0)` - // Estimated: `8529 + n * (12814 ±61)` - // Minimum execution time: 506_318 nanoseconds. - Weight::from_parts(676_935_313, 0) - .saturating_add(Weight::from_parts(0, 8529)) - // Standard Error: 1_589_291 - .saturating_add(Weight::from_parts(97_839_399, 0).saturating_mul(n.into())) + // Measured: `12544 + n * (11945 ±0)` + // Estimated: `8401 + n * (12814 ±61)` + // Minimum execution time: 442_286_000 picoseconds. + Weight::from_parts(620_395_299, 8401) + // Standard Error: 1_661_554 + .saturating_add(Weight::from_parts(89_393_169, 0).saturating_mul(n.into())) .saturating_add(T::DbWeight::get().reads(52_u64)) .saturating_add(T::DbWeight::get().reads((7_u64).saturating_mul(n.into()))) .saturating_add(T::DbWeight::get().writes(50_u64)) @@ -1030,31 +996,29 @@ impl WeightInfo for SubstrateWeight { /// The range of component `n` is `[0, 8]`. fn seal_set_storage_per_old_kb(n: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `15170 + n * (175775 ±0)` - // Estimated: `9914 + n * (176858 ±74)` - // Minimum execution time: 506_148 nanoseconds. - Weight::from_parts(648_278_778, 0) - .saturating_add(Weight::from_parts(0, 9914)) - // Standard Error: 1_343_586 - .saturating_add(Weight::from_parts(65_789_595, 0).saturating_mul(n.into())) + // Measured: `12532 + n * (175784 ±0)` + // Estimated: `8396 + n * (176649 ±61)` + // Minimum execution time: 440_882_000 picoseconds. + Weight::from_parts(587_317_981, 8396) + // Standard Error: 1_374_991 + .saturating_add(Weight::from_parts(66_271_084, 0).saturating_mul(n.into())) .saturating_add(T::DbWeight::get().reads(51_u64)) .saturating_add(T::DbWeight::get().reads((7_u64).saturating_mul(n.into()))) .saturating_add(T::DbWeight::get().writes(49_u64)) .saturating_add(T::DbWeight::get().writes((7_u64).saturating_mul(n.into()))) - .saturating_add(Weight::from_parts(0, 176858).saturating_mul(n.into())) + .saturating_add(Weight::from_parts(0, 176649).saturating_mul(n.into())) } /// Storage: Skipped Metadata (r:0 w:0) /// Proof Skipped: Skipped Metadata (max_values: None, max_size: None, mode: Measured) /// The range of component `r` is `[0, 10]`. fn seal_clear_storage(r: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `903 + r * (23099 ±0)` - // Estimated: `908 + r * (23099 ±0)` - // Minimum execution time: 374_344 nanoseconds. - Weight::from_parts(293_272_061, 0) - .saturating_add(Weight::from_parts(0, 908)) - // Standard Error: 810_412 - .saturating_add(Weight::from_parts(453_315_956, 0).saturating_mul(r.into())) + // Measured: `807 + r * (23099 ±0)` + // Estimated: `812 + r * (23099 ±0)` + // Minimum execution time: 296_023_000 picoseconds. + Weight::from_parts(221_471_559, 812) + // Standard Error: 814_503 + .saturating_add(Weight::from_parts(465_928_089, 0).saturating_mul(r.into())) .saturating_add(T::DbWeight::get().reads(6_u64)) .saturating_add(T::DbWeight::get().reads((80_u64).saturating_mul(r.into()))) .saturating_add(T::DbWeight::get().writes(3_u64)) @@ -1066,31 +1030,29 @@ impl WeightInfo for SubstrateWeight { /// The range of component `n` is `[0, 8]`. fn seal_clear_storage_per_kb(n: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `14895 + n * (175768 ±0)` - // Estimated: `9551 + n * (176867 ±75)` - // Minimum execution time: 478_564 nanoseconds. - Weight::from_parts(630_839_142, 0) - .saturating_add(Weight::from_parts(0, 9551)) - // Standard Error: 1_427_520 - .saturating_add(Weight::from_parts(66_813_592, 0).saturating_mul(n.into())) + // Measured: `12256 + n * (175776 ±0)` + // Estimated: `8047 + n * (176655 ±62)` + // Minimum execution time: 412_212_000 picoseconds. + Weight::from_parts(569_213_147, 8047) + // Standard Error: 1_463_771 + .saturating_add(Weight::from_parts(67_932_081, 0).saturating_mul(n.into())) .saturating_add(T::DbWeight::get().reads(51_u64)) .saturating_add(T::DbWeight::get().reads((7_u64).saturating_mul(n.into()))) .saturating_add(T::DbWeight::get().writes(48_u64)) .saturating_add(T::DbWeight::get().writes((7_u64).saturating_mul(n.into()))) - .saturating_add(Weight::from_parts(0, 176867).saturating_mul(n.into())) + .saturating_add(Weight::from_parts(0, 176655).saturating_mul(n.into())) } /// Storage: Skipped Metadata (r:0 w:0) /// Proof Skipped: Skipped Metadata (max_values: None, max_size: None, mode: Measured) /// The range of component `r` is `[0, 10]`. fn seal_get_storage(r: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `896 + r * (23744 ±0)` - // Estimated: `909 + r * (23740 ±0)` - // Minimum execution time: 374_479 nanoseconds. - Weight::from_parts(311_839_315, 0) - .saturating_add(Weight::from_parts(0, 909)) - // Standard Error: 666_553 - .saturating_add(Weight::from_parts(371_213_042, 0).saturating_mul(r.into())) + // Measured: `800 + r * (23744 ±0)` + // Estimated: `813 + r * (23740 ±0)` + // Minimum execution time: 296_007_000 picoseconds. + Weight::from_parts(229_100_700, 813) + // Standard Error: 684_970 + .saturating_add(Weight::from_parts(394_982_991, 0).saturating_mul(r.into())) .saturating_add(T::DbWeight::get().reads(6_u64)) .saturating_add(T::DbWeight::get().reads((80_u64).saturating_mul(r.into()))) .saturating_add(T::DbWeight::get().writes(3_u64)) @@ -1101,30 +1063,28 @@ impl WeightInfo for SubstrateWeight { /// The range of component `n` is `[0, 8]`. fn seal_get_storage_per_kb(n: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `15501 + n * (175775 ±0)` - // Estimated: `10042 + n * (176900 ±76)` - // Minimum execution time: 460_639 nanoseconds. - Weight::from_parts(591_187_094, 0) - .saturating_add(Weight::from_parts(0, 10042)) - // Standard Error: 1_233_792 - .saturating_add(Weight::from_parts(160_874_477, 0).saturating_mul(n.into())) + // Measured: `12863 + n * (175784 ±0)` + // Estimated: `8538 + n * (176688 ±63)` + // Minimum execution time: 392_306_000 picoseconds. + Weight::from_parts(539_188_004, 8538) + // Standard Error: 1_371_839 + .saturating_add(Weight::from_parts(155_130_373, 0).saturating_mul(n.into())) .saturating_add(T::DbWeight::get().reads(51_u64)) .saturating_add(T::DbWeight::get().reads((7_u64).saturating_mul(n.into()))) .saturating_add(T::DbWeight::get().writes(3_u64)) - .saturating_add(Weight::from_parts(0, 176900).saturating_mul(n.into())) + .saturating_add(Weight::from_parts(0, 176688).saturating_mul(n.into())) } /// Storage: Skipped Metadata (r:0 w:0) /// Proof Skipped: Skipped Metadata (max_values: None, max_size: None, mode: Measured) /// The range of component `r` is `[0, 10]`. fn seal_contains_storage(r: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `914 + r * (23098 ±0)` - // Estimated: `920 + r * (23098 ±0)` - // Minimum execution time: 374_272 nanoseconds. - Weight::from_parts(311_446_269, 0) - .saturating_add(Weight::from_parts(0, 920)) - // Standard Error: 630_307 - .saturating_add(Weight::from_parts(357_134_931, 0).saturating_mul(r.into())) + // Measured: `818 + r * (23098 ±0)` + // Estimated: `824 + r * (23098 ±0)` + // Minimum execution time: 296_038_000 picoseconds. + Weight::from_parts(229_462_798, 824) + // Standard Error: 655_463 + .saturating_add(Weight::from_parts(372_593_685, 0).saturating_mul(r.into())) .saturating_add(T::DbWeight::get().reads(6_u64)) .saturating_add(T::DbWeight::get().reads((80_u64).saturating_mul(r.into()))) .saturating_add(T::DbWeight::get().writes(3_u64)) @@ -1135,30 +1095,28 @@ impl WeightInfo for SubstrateWeight { /// The range of component `n` is `[0, 8]`. fn seal_contains_storage_per_kb(n: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `14839 + n * (175789 ±0)` - // Estimated: `9532 + n * (176874 ±75)` - // Minimum execution time: 456_013 nanoseconds. - Weight::from_parts(575_116_352, 0) - .saturating_add(Weight::from_parts(0, 9532)) - // Standard Error: 1_122_298 - .saturating_add(Weight::from_parts(61_786_107, 0).saturating_mul(n.into())) + // Measured: `12200 + n * (175798 ±0)` + // Estimated: `8028 + n * (176661 ±62)` + // Minimum execution time: 386_782_000 picoseconds. + Weight::from_parts(512_965_975, 8028) + // Standard Error: 1_168_132 + .saturating_add(Weight::from_parts(63_307_325, 0).saturating_mul(n.into())) .saturating_add(T::DbWeight::get().reads(51_u64)) .saturating_add(T::DbWeight::get().reads((7_u64).saturating_mul(n.into()))) .saturating_add(T::DbWeight::get().writes(3_u64)) - .saturating_add(Weight::from_parts(0, 176874).saturating_mul(n.into())) + .saturating_add(Weight::from_parts(0, 176661).saturating_mul(n.into())) } /// Storage: Skipped Metadata (r:0 w:0) /// Proof Skipped: Skipped Metadata (max_values: None, max_size: None, mode: Measured) /// The range of component `r` is `[0, 10]`. fn seal_take_storage(r: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `911 + r * (23740 ±0)` - // Estimated: `913 + r * (23739 ±0)` - // Minimum execution time: 374_621 nanoseconds. - Weight::from_parts(299_689_489, 0) - .saturating_add(Weight::from_parts(0, 913)) - // Standard Error: 757_735 - .saturating_add(Weight::from_parts(465_213_246, 0).saturating_mul(r.into())) + // Measured: `815 + r * (23740 ±0)` + // Estimated: `817 + r * (23739 ±0)` + // Minimum execution time: 296_352_000 picoseconds. + Weight::from_parts(212_661_296, 817) + // Standard Error: 879_297 + .saturating_add(Weight::from_parts(494_182_672, 0).saturating_mul(r.into())) .saturating_add(T::DbWeight::get().reads(6_u64)) .saturating_add(T::DbWeight::get().reads((80_u64).saturating_mul(r.into()))) .saturating_add(T::DbWeight::get().writes(3_u64)) @@ -1170,18 +1128,17 @@ impl WeightInfo for SubstrateWeight { /// The range of component `n` is `[0, 8]`. fn seal_take_storage_per_kb(n: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `15502 + n * (175775 ±0)` - // Estimated: `10042 + n * (176898 ±76)` - // Minimum execution time: 481_980 nanoseconds. - Weight::from_parts(647_289_053, 0) - .saturating_add(Weight::from_parts(0, 10042)) - // Standard Error: 1_556_155 - .saturating_add(Weight::from_parts(166_592_657, 0).saturating_mul(n.into())) + // Measured: `12864 + n * (175784 ±0)` + // Estimated: `8539 + n * (176686 ±63)` + // Minimum execution time: 414_693_000 picoseconds. + Weight::from_parts(595_693_859, 8539) + // Standard Error: 1_673_819 + .saturating_add(Weight::from_parts(163_090_316, 0).saturating_mul(n.into())) .saturating_add(T::DbWeight::get().reads(51_u64)) .saturating_add(T::DbWeight::get().reads((7_u64).saturating_mul(n.into()))) .saturating_add(T::DbWeight::get().writes(48_u64)) .saturating_add(T::DbWeight::get().writes((7_u64).saturating_mul(n.into()))) - .saturating_add(Weight::from_parts(0, 176898).saturating_mul(n.into())) + .saturating_add(Weight::from_parts(0, 176686).saturating_mul(n.into())) } /// Storage: System Account (r:1602 w:1601) /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: Measured) @@ -1196,18 +1153,17 @@ impl WeightInfo for SubstrateWeight { /// The range of component `r` is `[0, 20]`. fn seal_transfer(r: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `1457 + r * (3604 ±0)` - // Estimated: `21583 + r * (216101 ±0)` - // Minimum execution time: 374_962 nanoseconds. - Weight::from_parts(313_416_386, 0) - .saturating_add(Weight::from_parts(0, 21583)) - // Standard Error: 710_675 - .saturating_add(Weight::from_parts(1_396_551_156, 0).saturating_mul(r.into())) + // Measured: `1329 + r * (3604 ±0)` + // Estimated: `25928 + r * (216096 ±4)` + // Minimum execution time: 299_205_000 picoseconds. + Weight::from_parts(221_142_217, 25928) + // Standard Error: 1_369_909 + .saturating_add(Weight::from_parts(1_665_917_241, 0).saturating_mul(r.into())) .saturating_add(T::DbWeight::get().reads(7_u64)) .saturating_add(T::DbWeight::get().reads((80_u64).saturating_mul(r.into()))) .saturating_add(T::DbWeight::get().writes(4_u64)) .saturating_add(T::DbWeight::get().writes((80_u64).saturating_mul(r.into()))) - .saturating_add(Weight::from_parts(0, 216101).saturating_mul(r.into())) + .saturating_add(Weight::from_parts(0, 216096).saturating_mul(r.into())) } /// Storage: System Account (r:1 w:0) /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: Measured) @@ -1222,18 +1178,17 @@ impl WeightInfo for SubstrateWeight { /// The range of component `r` is `[0, 20]`. fn seal_call(r: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `1609 + r * (23073 ±0)` - // Estimated: `22098 + r * (511456 ±1)` - // Minimum execution time: 375_916 nanoseconds. - Weight::from_parts(376_468_000, 0) - .saturating_add(Weight::from_parts(0, 22098)) - // Standard Error: 7_246_855 - .saturating_add(Weight::from_parts(28_982_425_139, 0).saturating_mul(r.into())) + // Measured: `1479 + r * (20514 ±0)` + // Estimated: `26429 + r * (498656 ±1)` + // Minimum execution time: 297_501_000 picoseconds. + Weight::from_parts(298_400_000, 26429) + // Standard Error: 9_372_890 + .saturating_add(Weight::from_parts(22_507_984_433, 0).saturating_mul(r.into())) .saturating_add(T::DbWeight::get().reads(7_u64)) .saturating_add(T::DbWeight::get().reads((160_u64).saturating_mul(r.into()))) .saturating_add(T::DbWeight::get().writes(3_u64)) .saturating_add(T::DbWeight::get().writes((160_u64).saturating_mul(r.into()))) - .saturating_add(Weight::from_parts(0, 511456).saturating_mul(r.into())) + .saturating_add(Weight::from_parts(0, 498656).saturating_mul(r.into())) } /// Storage: System Account (r:1 w:0) /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: Measured) @@ -1248,18 +1203,17 @@ impl WeightInfo for SubstrateWeight { /// The range of component `r` is `[0, 20]`. fn seal_delegate_call(r: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `0 + r * (71670 ±0)` - // Estimated: `17285 + r * (659930 ±563)` - // Minimum execution time: 375_412 nanoseconds. - Weight::from_parts(376_493_000, 0) - .saturating_add(Weight::from_parts(0, 17285)) - // Standard Error: 8_239_575 - .saturating_add(Weight::from_parts(28_716_347_183, 0).saturating_mul(r.into())) + // Measured: `0 + r * (69030 ±0)` + // Estimated: `21755 + r * (647542 ±560)` + // Minimum execution time: 297_432_000 picoseconds. + Weight::from_parts(298_220_000, 21755) + // Standard Error: 9_420_834 + .saturating_add(Weight::from_parts(22_261_152_353, 0).saturating_mul(r.into())) .saturating_add(T::DbWeight::get().reads(6_u64)) .saturating_add(T::DbWeight::get().reads((150_u64).saturating_mul(r.into()))) .saturating_add(T::DbWeight::get().writes(3_u64)) .saturating_add(T::DbWeight::get().writes((75_u64).saturating_mul(r.into()))) - .saturating_add(Weight::from_parts(0, 659930).saturating_mul(r.into())) + .saturating_add(Weight::from_parts(0, 647542).saturating_mul(r.into())) } /// Storage: System Account (r:82 w:81) /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: Measured) @@ -1275,20 +1229,19 @@ impl WeightInfo for SubstrateWeight { /// The range of component `c` is `[0, 1024]`. fn seal_call_per_transfer_clone_kb(t: u32, c: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `24269 + t * (16910 ±0)` - // Estimated: `532690 + t * (285025 ±0)` - // Minimum execution time: 10_443_315 nanoseconds. - Weight::from_parts(9_342_574_069, 0) - .saturating_add(Weight::from_parts(0, 532690)) - // Standard Error: 7_237_279 - .saturating_add(Weight::from_parts(1_390_221_936, 0).saturating_mul(t.into())) - // Standard Error: 10_851 - .saturating_add(Weight::from_parts(9_842_151, 0).saturating_mul(c.into())) + // Measured: `21581 + t * (14318 ±0)` + // Estimated: `524200 + t * (272065 ±0)` + // Minimum execution time: 11_799_802_000 picoseconds. + Weight::from_parts(10_575_328_547, 524200) + // Standard Error: 11_952_810 + .saturating_add(Weight::from_parts(1_603_077_083, 0).saturating_mul(t.into())) + // Standard Error: 17_922 + .saturating_add(Weight::from_parts(9_876_752, 0).saturating_mul(c.into())) .saturating_add(T::DbWeight::get().reads(167_u64)) .saturating_add(T::DbWeight::get().reads((81_u64).saturating_mul(t.into()))) .saturating_add(T::DbWeight::get().writes(163_u64)) .saturating_add(T::DbWeight::get().writes((81_u64).saturating_mul(t.into()))) - .saturating_add(Weight::from_parts(0, 285025).saturating_mul(t.into())) + .saturating_add(Weight::from_parts(0, 272065).saturating_mul(t.into())) } /// Storage: System Account (r:3202 w:3202) /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: Measured) @@ -1307,18 +1260,17 @@ impl WeightInfo for SubstrateWeight { /// The range of component `r` is `[0, 20]`. fn seal_instantiate(r: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `1775 + r * (25568 ±0)` - // Estimated: `26563 + r * (1367114 ±2)` - // Minimum execution time: 376_418 nanoseconds. - Weight::from_parts(377_292_000, 0) - .saturating_add(Weight::from_parts(0, 26563)) - // Standard Error: 32_312_545 - .saturating_add(Weight::from_parts(35_904_826_312, 0).saturating_mul(r.into())) + // Measured: `1646 + r * (20288 ±0)` + // Estimated: `32625 + r * (1330150 ±2)` + // Minimum execution time: 298_155_000 picoseconds. + Weight::from_parts(299_212_000, 32625) + // Standard Error: 32_410_313 + .saturating_add(Weight::from_parts(30_417_025_178, 0).saturating_mul(r.into())) .saturating_add(T::DbWeight::get().reads(8_u64)) .saturating_add(T::DbWeight::get().reads((480_u64).saturating_mul(r.into()))) .saturating_add(T::DbWeight::get().writes(5_u64)) .saturating_add(T::DbWeight::get().writes((400_u64).saturating_mul(r.into()))) - .saturating_add(Weight::from_parts(0, 1367114).saturating_mul(r.into())) + .saturating_add(Weight::from_parts(0, 1330150).saturating_mul(r.into())) } /// Storage: System Account (r:162 w:162) /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: Measured) @@ -1339,22 +1291,21 @@ impl WeightInfo for SubstrateWeight { /// The range of component `s` is `[0, 960]`. fn seal_instantiate_per_transfer_input_salt_kb(t: u32, i: u32, s: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `5785 + t * (33 ±0)` - // Estimated: `850985 + t * (2671 ±3)` - // Minimum execution time: 132_157_340 nanoseconds. - Weight::from_parts(11_329_968_948, 0) - .saturating_add(Weight::from_parts(0, 850985)) - // Standard Error: 99_102_968 - .saturating_add(Weight::from_parts(84_719_458, 0).saturating_mul(t.into())) - // Standard Error: 161_609 - .saturating_add(Weight::from_parts(126_156_627, 0).saturating_mul(i.into())) - // Standard Error: 161_609 - .saturating_add(Weight::from_parts(126_628_313, 0).saturating_mul(s.into())) + // Measured: `5625 + t * (1 ±0)` + // Estimated: `856795 + t * (2471 ±3)` + // Minimum execution time: 105_914_928_000 picoseconds. + Weight::from_parts(13_229_760_432, 856795) + // Standard Error: 96_394_437 + .saturating_add(Weight::from_parts(398_413_888, 0).saturating_mul(t.into())) + // Standard Error: 157_192 + .saturating_add(Weight::from_parts(97_104_978, 0).saturating_mul(i.into())) + // Standard Error: 157_192 + .saturating_add(Weight::from_parts(97_146_130, 0).saturating_mul(s.into())) .saturating_add(T::DbWeight::get().reads(329_u64)) .saturating_add(T::DbWeight::get().reads((1_u64).saturating_mul(t.into()))) .saturating_add(T::DbWeight::get().writes(326_u64)) .saturating_add(T::DbWeight::get().writes((1_u64).saturating_mul(t.into()))) - .saturating_add(Weight::from_parts(0, 2671).saturating_mul(t.into())) + .saturating_add(Weight::from_parts(0, 2471).saturating_mul(t.into())) } /// Storage: System Account (r:1 w:0) /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: Measured) @@ -1369,13 +1320,12 @@ impl WeightInfo for SubstrateWeight { /// The range of component `r` is `[0, 1]`. fn seal_hash_sha2_256(r: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `871 + r * (642 ±0)` - // Estimated: `17225 + r * (3210 ±0)` - // Minimum execution time: 373_559 nanoseconds. - Weight::from_parts(375_166_904, 0) - .saturating_add(Weight::from_parts(0, 17225)) - // Standard Error: 125_024 - .saturating_add(Weight::from_parts(42_291_595, 0).saturating_mul(r.into())) + // Measured: `775 + r * (642 ±0)` + // Estimated: `21695 + r * (3210 ±0)` + // Minimum execution time: 294_309_000 picoseconds. + Weight::from_parts(296_241_318, 21695) + // Standard Error: 127_872 + .saturating_add(Weight::from_parts(45_806_281, 0).saturating_mul(r.into())) .saturating_add(T::DbWeight::get().reads(6_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) .saturating_add(Weight::from_parts(0, 3210).saturating_mul(r.into())) @@ -1393,13 +1343,12 @@ impl WeightInfo for SubstrateWeight { /// The range of component `n` is `[0, 1024]`. fn seal_hash_sha2_256_per_kb(n: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `1673` - // Estimated: `21160` - // Minimum execution time: 416_233 nanoseconds. - Weight::from_parts(416_785_000, 0) - .saturating_add(Weight::from_parts(0, 21160)) - // Standard Error: 56_223 - .saturating_add(Weight::from_parts(324_513_835, 0).saturating_mul(n.into())) + // Measured: `1577` + // Estimated: `25630` + // Minimum execution time: 341_248_000 picoseconds. + Weight::from_parts(341_607_000, 25630) + // Standard Error: 66_687 + .saturating_add(Weight::from_parts(322_250_398, 0).saturating_mul(n.into())) .saturating_add(T::DbWeight::get().reads(6_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) } @@ -1416,13 +1365,12 @@ impl WeightInfo for SubstrateWeight { /// The range of component `r` is `[0, 1]`. fn seal_hash_keccak_256(r: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `873 + r * (642 ±0)` - // Estimated: `17235 + r * (3210 ±0)` - // Minimum execution time: 371_735 nanoseconds. - Weight::from_parts(375_979_430, 0) - .saturating_add(Weight::from_parts(0, 17235)) - // Standard Error: 968_037 - .saturating_add(Weight::from_parts(57_780_769, 0).saturating_mul(r.into())) + // Measured: `777 + r * (642 ±0)` + // Estimated: `21705 + r * (3210 ±0)` + // Minimum execution time: 293_810_000 picoseconds. + Weight::from_parts(296_719_171, 21705) + // Standard Error: 450_807 + .saturating_add(Weight::from_parts(61_627_228, 0).saturating_mul(r.into())) .saturating_add(T::DbWeight::get().reads(6_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) .saturating_add(Weight::from_parts(0, 3210).saturating_mul(r.into())) @@ -1440,13 +1388,12 @@ impl WeightInfo for SubstrateWeight { /// The range of component `n` is `[0, 1024]`. fn seal_hash_keccak_256_per_kb(n: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `1675` - // Estimated: `21205` - // Minimum execution time: 428_196 nanoseconds. - Weight::from_parts(429_438_000, 0) - .saturating_add(Weight::from_parts(0, 21205)) - // Standard Error: 57_860 - .saturating_add(Weight::from_parts(260_917_896, 0).saturating_mul(n.into())) + // Measured: `1579` + // Estimated: `25675` + // Minimum execution time: 354_550_000 picoseconds. + Weight::from_parts(355_136_000, 25675) + // Standard Error: 61_761 + .saturating_add(Weight::from_parts(257_021_566, 0).saturating_mul(n.into())) .saturating_add(T::DbWeight::get().reads(6_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) } @@ -1463,13 +1410,12 @@ impl WeightInfo for SubstrateWeight { /// The range of component `r` is `[0, 1]`. fn seal_hash_blake2_256(r: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `873 + r * (642 ±0)` - // Estimated: `17235 + r * (3210 ±0)` - // Minimum execution time: 371_412 nanoseconds. - Weight::from_parts(373_635_818, 0) - .saturating_add(Weight::from_parts(0, 17235)) - // Standard Error: 222_973 - .saturating_add(Weight::from_parts(33_347_181, 0).saturating_mul(r.into())) + // Measured: `777 + r * (642 ±0)` + // Estimated: `21705 + r * (3210 ±0)` + // Minimum execution time: 293_369_000 picoseconds. + Weight::from_parts(295_990_893, 21705) + // Standard Error: 431_269 + .saturating_add(Weight::from_parts(32_213_406, 0).saturating_mul(r.into())) .saturating_add(T::DbWeight::get().reads(6_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) .saturating_add(Weight::from_parts(0, 3210).saturating_mul(r.into())) @@ -1487,13 +1433,12 @@ impl WeightInfo for SubstrateWeight { /// The range of component `n` is `[0, 1024]`. fn seal_hash_blake2_256_per_kb(n: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `1675` - // Estimated: `21180` - // Minimum execution time: 405_858 nanoseconds. - Weight::from_parts(406_498_000, 0) - .saturating_add(Weight::from_parts(0, 21180)) - // Standard Error: 48_388 - .saturating_add(Weight::from_parts(103_283_157, 0).saturating_mul(n.into())) + // Measured: `1579` + // Estimated: `25650` + // Minimum execution time: 326_595_000 picoseconds. + Weight::from_parts(327_188_000, 25650) + // Standard Error: 53_385 + .saturating_add(Weight::from_parts(74_046_074, 0).saturating_mul(n.into())) .saturating_add(T::DbWeight::get().reads(6_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) } @@ -1510,13 +1455,12 @@ impl WeightInfo for SubstrateWeight { /// The range of component `r` is `[0, 1]`. fn seal_hash_blake2_128(r: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `873 + r * (679 ±0)` - // Estimated: `17235 + r * (3395 ±0)` - // Minimum execution time: 371_746 nanoseconds. - Weight::from_parts(373_538_171, 0) - .saturating_add(Weight::from_parts(0, 17235)) - // Standard Error: 387_332 - .saturating_add(Weight::from_parts(35_933_528, 0).saturating_mul(r.into())) + // Measured: `777 + r * (679 ±0)` + // Estimated: `21705 + r * (3395 ±0)` + // Minimum execution time: 292_388_000 picoseconds. + Weight::from_parts(295_090_179, 21705) + // Standard Error: 170_487 + .saturating_add(Weight::from_parts(32_577_820, 0).saturating_mul(r.into())) .saturating_add(T::DbWeight::get().reads(6_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) .saturating_add(Weight::from_parts(0, 3395).saturating_mul(r.into())) @@ -1534,13 +1478,12 @@ impl WeightInfo for SubstrateWeight { /// The range of component `n` is `[0, 1024]`. fn seal_hash_blake2_128_per_kb(n: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `1675` - // Estimated: `21225` - // Minimum execution time: 405_752 nanoseconds. - Weight::from_parts(406_417_000, 0) - .saturating_add(Weight::from_parts(0, 21225)) - // Standard Error: 47_051 - .saturating_add(Weight::from_parts(103_325_027, 0).saturating_mul(n.into())) + // Measured: `1579` + // Estimated: `25695` + // Minimum execution time: 326_862_000 picoseconds. + Weight::from_parts(327_449_000, 25695) + // Standard Error: 54_539 + .saturating_add(Weight::from_parts(74_042_337, 0).saturating_mul(n.into())) .saturating_add(T::DbWeight::get().reads(6_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) } @@ -1557,13 +1500,12 @@ impl WeightInfo for SubstrateWeight { /// The range of component `r` is `[0, 1]`. fn seal_ecdsa_recover(r: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `917 + r * (6083 ±0)` - // Estimated: `17455 + r * (30415 ±0)` - // Minimum execution time: 373_882 nanoseconds. - Weight::from_parts(376_553_787, 0) - .saturating_add(Weight::from_parts(0, 17455)) - // Standard Error: 912_833 - .saturating_add(Weight::from_parts(3_021_100_412, 0).saturating_mul(r.into())) + // Measured: `821 + r * (6083 ±0)` + // Estimated: `21925 + r * (30415 ±0)` + // Minimum execution time: 295_597_000 picoseconds. + Weight::from_parts(298_267_200, 21925) + // Standard Error: 699_824 + .saturating_add(Weight::from_parts(3_021_734_700, 0).saturating_mul(r.into())) .saturating_add(T::DbWeight::get().reads(6_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) .saturating_add(Weight::from_parts(0, 30415).saturating_mul(r.into())) @@ -1581,13 +1523,12 @@ impl WeightInfo for SubstrateWeight { /// The range of component `r` is `[0, 1]`. fn seal_ecdsa_to_eth_address(r: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `886 + r * (3362 ±0)` - // Estimated: `17300 + r * (16810 ±0)` - // Minimum execution time: 373_673 nanoseconds. - Weight::from_parts(375_712_961, 0) - .saturating_add(Weight::from_parts(0, 17300)) - // Standard Error: 596_297 - .saturating_add(Weight::from_parts(738_257_638, 0).saturating_mul(r.into())) + // Measured: `790 + r * (3362 ±0)` + // Estimated: `21770 + r * (16810 ±0)` + // Minimum execution time: 295_539_000 picoseconds. + Weight::from_parts(297_844_822, 21770) + // Standard Error: 490_532 + .saturating_add(Weight::from_parts(747_592_977, 0).saturating_mul(r.into())) .saturating_add(T::DbWeight::get().reads(6_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) .saturating_add(Weight::from_parts(0, 16810).saturating_mul(r.into())) @@ -1607,18 +1548,17 @@ impl WeightInfo for SubstrateWeight { /// The range of component `r` is `[0, 20]`. fn seal_set_code_hash(r: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `0 + r * (79300 ±0)` - // Estimated: `64844 + r * (942952 ±833)` - // Minimum execution time: 374_097 nanoseconds. - Weight::from_parts(374_985_000, 0) - .saturating_add(Weight::from_parts(0, 64844)) - // Standard Error: 3_772_336 - .saturating_add(Weight::from_parts(1_546_402_854, 0).saturating_mul(r.into())) + // Measured: `0 + r * (74020 ±0)` + // Estimated: `69192 + r * (913289 ±821)` + // Minimum execution time: 295_164_000 picoseconds. + Weight::from_parts(296_597_000, 69192) + // Standard Error: 3_646_577 + .saturating_add(Weight::from_parts(1_725_796_807, 0).saturating_mul(r.into())) .saturating_add(T::DbWeight::get().reads(6_u64)) .saturating_add(T::DbWeight::get().reads((225_u64).saturating_mul(r.into()))) .saturating_add(T::DbWeight::get().writes(3_u64)) .saturating_add(T::DbWeight::get().writes((150_u64).saturating_mul(r.into()))) - .saturating_add(Weight::from_parts(0, 942952).saturating_mul(r.into())) + .saturating_add(Weight::from_parts(0, 913289).saturating_mul(r.into())) } /// Storage: System Account (r:1 w:0) /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: Measured) @@ -1633,13 +1573,12 @@ impl WeightInfo for SubstrateWeight { /// The range of component `r` is `[0, 20]`. fn seal_reentrance_count(r: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `869 + r * (240 ±0)` - // Estimated: `17215 + r * (1200 ±0)` - // Minimum execution time: 374_249 nanoseconds. - Weight::from_parts(377_990_998, 0) - .saturating_add(Weight::from_parts(0, 17215)) - // Standard Error: 38_133 - .saturating_add(Weight::from_parts(11_483_273, 0).saturating_mul(r.into())) + // Measured: `773 + r * (240 ±0)` + // Estimated: `21685 + r * (1200 ±0)` + // Minimum execution time: 296_287_000 picoseconds. + Weight::from_parts(300_696_694, 21685) + // Standard Error: 27_891 + .saturating_add(Weight::from_parts(10_943_994, 0).saturating_mul(r.into())) .saturating_add(T::DbWeight::get().reads(6_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) .saturating_add(Weight::from_parts(0, 1200).saturating_mul(r.into())) @@ -1657,13 +1596,12 @@ impl WeightInfo for SubstrateWeight { /// The range of component `r` is `[0, 20]`. fn seal_account_reentrance_count(r: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `2102 + r * (3154 ±0)` - // Estimated: `21980 + r * (15875 ±2)` - // Minimum execution time: 375_552 nanoseconds. - Weight::from_parts(400_624_032, 0) - .saturating_add(Weight::from_parts(0, 21980)) - // Standard Error: 82_523 - .saturating_add(Weight::from_parts(18_057_327, 0).saturating_mul(r.into())) + // Measured: `2006 + r * (3154 ±0)` + // Estimated: `26435 + r * (15875 ±2)` + // Minimum execution time: 298_000_000 picoseconds. + Weight::from_parts(328_282_119, 26435) + // Standard Error: 104_874 + .saturating_add(Weight::from_parts(18_428_267, 0).saturating_mul(r.into())) .saturating_add(T::DbWeight::get().reads(6_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) .saturating_add(Weight::from_parts(0, 15875).saturating_mul(r.into())) @@ -1683,13 +1621,12 @@ impl WeightInfo for SubstrateWeight { /// The range of component `r` is `[0, 20]`. fn seal_instantiation_nonce(r: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `872 + r * (240 ±0)` - // Estimated: `18598 + r * (1440 ±0)` - // Minimum execution time: 373_899 nanoseconds. - Weight::from_parts(379_733_943, 0) - .saturating_add(Weight::from_parts(0, 18598)) - // Standard Error: 32_022 - .saturating_add(Weight::from_parts(9_381_180, 0).saturating_mul(r.into())) + // Measured: `776 + r * (240 ±0)` + // Estimated: `23962 + r * (1440 ±0)` + // Minimum execution time: 295_104_000 picoseconds. + Weight::from_parts(299_334_585, 23962) + // Standard Error: 25_840 + .saturating_add(Weight::from_parts(9_744_866, 0).saturating_mul(r.into())) .saturating_add(T::DbWeight::get().reads(7_u64)) .saturating_add(T::DbWeight::get().writes(4_u64)) .saturating_add(Weight::from_parts(0, 1440).saturating_mul(r.into())) @@ -1699,572 +1636,520 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 834 nanoseconds. - Weight::from_parts(1_009_646, 0) - .saturating_add(Weight::from_parts(0, 0)) - // Standard Error: 388 - .saturating_add(Weight::from_parts(411_979, 0).saturating_mul(r.into())) + // Minimum execution time: 1_132_000 picoseconds. + Weight::from_parts(1_100_159, 0) + // Standard Error: 10_131 + .saturating_add(Weight::from_parts(436_462, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 50]`. fn instr_i64load(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 882 nanoseconds. - Weight::from_parts(1_416_377, 0) - .saturating_add(Weight::from_parts(0, 0)) - // Standard Error: 1_133 - .saturating_add(Weight::from_parts(1_075_838, 0).saturating_mul(r.into())) + // Minimum execution time: 1_368_000 picoseconds. + Weight::from_parts(1_974_308, 0) + // Standard Error: 811 + .saturating_add(Weight::from_parts(1_079_620, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 50]`. fn instr_i64store(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 878 nanoseconds. - Weight::from_parts(1_343_056, 0) - .saturating_add(Weight::from_parts(0, 0)) - // Standard Error: 426 - .saturating_add(Weight::from_parts(1_001_214, 0).saturating_mul(r.into())) + // Minimum execution time: 1_354_000 picoseconds. + Weight::from_parts(2_057_834, 0) + // Standard Error: 4_277 + .saturating_add(Weight::from_parts(1_008_797, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 50]`. fn instr_select(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 796 nanoseconds. - Weight::from_parts(1_079_086, 0) - .saturating_add(Weight::from_parts(0, 0)) - // Standard Error: 409 - .saturating_add(Weight::from_parts(1_149_188, 0).saturating_mul(r.into())) + // Minimum execution time: 1_218_000 picoseconds. + Weight::from_parts(1_594_528, 0) + // Standard Error: 723 + .saturating_add(Weight::from_parts(1_147_486, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 50]`. fn instr_if(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 800 nanoseconds. - Weight::from_parts(1_044_184, 0) - .saturating_add(Weight::from_parts(0, 0)) - // Standard Error: 707 - .saturating_add(Weight::from_parts(1_315_686, 0).saturating_mul(r.into())) + // Minimum execution time: 1_156_000 picoseconds. + Weight::from_parts(1_460_611, 0) + // Standard Error: 419 + .saturating_add(Weight::from_parts(1_314_741, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 50]`. fn instr_br(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 807 nanoseconds. - Weight::from_parts(1_049_633, 0) - .saturating_add(Weight::from_parts(0, 0)) - // Standard Error: 361 - .saturating_add(Weight::from_parts(640_530, 0).saturating_mul(r.into())) + // Minimum execution time: 1_166_000 picoseconds. + Weight::from_parts(1_454_460, 0) + // Standard Error: 493 + .saturating_add(Weight::from_parts(642_481, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 50]`. fn instr_br_if(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 774 nanoseconds. - Weight::from_parts(1_124_053, 0) - .saturating_add(Weight::from_parts(0, 0)) - // Standard Error: 784 - .saturating_add(Weight::from_parts(949_398, 0).saturating_mul(r.into())) + // Minimum execution time: 1_144_000 picoseconds. + Weight::from_parts(1_410_515, 0) + // Standard Error: 894 + .saturating_add(Weight::from_parts(958_765, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 50]`. fn instr_br_table(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 810 nanoseconds. - Weight::from_parts(676_581, 0) - .saturating_add(Weight::from_parts(0, 0)) - // Standard Error: 2_356 - .saturating_add(Weight::from_parts(1_163_465, 0).saturating_mul(r.into())) + // Minimum execution time: 1_190_000 picoseconds. + Weight::from_parts(1_143_236, 0) + // Standard Error: 2_773 + .saturating_add(Weight::from_parts(1_164_764, 0).saturating_mul(r.into())) } /// The range of component `e` is `[1, 256]`. fn instr_br_table_per_entry(e: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 2_580 nanoseconds. - Weight::from_parts(2_835_656, 0) - .saturating_add(Weight::from_parts(0, 0)) - // Standard Error: 71 - .saturating_add(Weight::from_parts(4_686, 0).saturating_mul(e.into())) + // Minimum execution time: 2_993_000 picoseconds. + Weight::from_parts(3_270_525, 0) + // Standard Error: 65 + .saturating_add(Weight::from_parts(4_418, 0).saturating_mul(e.into())) } /// The range of component `r` is `[0, 50]`. fn instr_call(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 826 nanoseconds. - Weight::from_parts(1_625_698, 0) - .saturating_add(Weight::from_parts(0, 0)) - // Standard Error: 1_740 - .saturating_add(Weight::from_parts(2_332_187, 0).saturating_mul(r.into())) + // Minimum execution time: 1_163_000 picoseconds. + Weight::from_parts(1_999_458, 0) + // Standard Error: 1_418 + .saturating_add(Weight::from_parts(2_371_979, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 50]`. fn instr_call_indirect(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 901 nanoseconds. - Weight::from_parts(2_338_620, 0) - .saturating_add(Weight::from_parts(0, 0)) - // Standard Error: 1_642 - .saturating_add(Weight::from_parts(2_924_090, 0).saturating_mul(r.into())) + // Minimum execution time: 1_288_000 picoseconds. + Weight::from_parts(2_340_227, 0) + // Standard Error: 3_234 + .saturating_add(Weight::from_parts(3_038_523, 0).saturating_mul(r.into())) } /// The range of component `p` is `[0, 128]`. fn instr_call_indirect_per_param(p: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 4_670 nanoseconds. - Weight::from_parts(5_556_246, 0) - .saturating_add(Weight::from_parts(0, 0)) - // Standard Error: 1_491 - .saturating_add(Weight::from_parts(228_965, 0).saturating_mul(p.into())) + // Minimum execution time: 5_270_000 picoseconds. + Weight::from_parts(6_360_011, 0) + // Standard Error: 2_585 + .saturating_add(Weight::from_parts(226_149, 0).saturating_mul(p.into())) } /// The range of component `l` is `[0, 1024]`. fn instr_call_per_local(l: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 3_099 nanoseconds. - Weight::from_parts(3_896_177, 0) - .saturating_add(Weight::from_parts(0, 0)) - // Standard Error: 99 - .saturating_add(Weight::from_parts(91_304, 0).saturating_mul(l.into())) + // Minimum execution time: 3_580_000 picoseconds. + Weight::from_parts(5_183_658, 0) + // Standard Error: 80 + .saturating_add(Weight::from_parts(46_038, 0).saturating_mul(l.into())) } /// The range of component `r` is `[0, 50]`. fn instr_local_get(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 3_042 nanoseconds. - Weight::from_parts(3_334_621, 0) - .saturating_add(Weight::from_parts(0, 0)) - // Standard Error: 793 - .saturating_add(Weight::from_parts(459_346, 0).saturating_mul(r.into())) + // Minimum execution time: 2_559_000 picoseconds. + Weight::from_parts(2_846_350, 0) + // Standard Error: 2_809 + .saturating_add(Weight::from_parts(462_084, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 50]`. fn instr_local_set(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 2_968 nanoseconds. - Weight::from_parts(3_235_286, 0) - .saturating_add(Weight::from_parts(0, 0)) - // Standard Error: 427 - .saturating_add(Weight::from_parts(485_454, 0).saturating_mul(r.into())) + // Minimum execution time: 2_530_000 picoseconds. + Weight::from_parts(2_835_072, 0) + // Standard Error: 479 + .saturating_add(Weight::from_parts(483_451, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 50]`. fn instr_local_tee(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 3_012 nanoseconds. - Weight::from_parts(3_303_555, 0) - .saturating_add(Weight::from_parts(0, 0)) - // Standard Error: 371 - .saturating_add(Weight::from_parts(657_811, 0).saturating_mul(r.into())) + // Minimum execution time: 2_510_000 picoseconds. + Weight::from_parts(2_740_081, 0) + // Standard Error: 901 + .saturating_add(Weight::from_parts(664_354, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 50]`. fn instr_global_get(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 865 nanoseconds. - Weight::from_parts(1_249_987, 0) - .saturating_add(Weight::from_parts(0, 0)) - // Standard Error: 417 - .saturating_add(Weight::from_parts(896_704, 0).saturating_mul(r.into())) + // Minimum execution time: 1_295_000 picoseconds. + Weight::from_parts(1_798_174, 0) + // Standard Error: 464 + .saturating_add(Weight::from_parts(896_289, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 50]`. fn instr_global_set(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 866 nanoseconds. - Weight::from_parts(1_216_218, 0) - .saturating_add(Weight::from_parts(0, 0)) - // Standard Error: 503 - .saturating_add(Weight::from_parts(919_719, 0).saturating_mul(r.into())) + // Minimum execution time: 1_287_000 picoseconds. + Weight::from_parts(1_636_548, 0) + // Standard Error: 658 + .saturating_add(Weight::from_parts(923_295, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 50]`. fn instr_memory_current(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 921 nanoseconds. - Weight::from_parts(1_228_408, 0) - .saturating_add(Weight::from_parts(0, 0)) - // Standard Error: 309 - .saturating_add(Weight::from_parts(813_007, 0).saturating_mul(r.into())) + // Minimum execution time: 1_353_000 picoseconds. + Weight::from_parts(1_672_309, 0) + // Standard Error: 403 + .saturating_add(Weight::from_parts(818_273, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 1]`. fn instr_memory_grow(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 820 nanoseconds. - Weight::from_parts(914_830, 0) - .saturating_add(Weight::from_parts(0, 0)) - // Standard Error: 6_018 - .saturating_add(Weight::from_parts(237_062_769, 0).saturating_mul(r.into())) + // Minimum execution time: 1_180_000 picoseconds. + Weight::from_parts(1_304_714, 0) + // Standard Error: 88_180 + .saturating_add(Weight::from_parts(183_952_985, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 50]`. fn instr_i64clz(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 812 nanoseconds. - Weight::from_parts(1_554_406, 0) - .saturating_add(Weight::from_parts(0, 0)) - // Standard Error: 9_979 - .saturating_add(Weight::from_parts(625_434, 0).saturating_mul(r.into())) + // Minimum execution time: 1_158_000 picoseconds. + Weight::from_parts(1_800_065, 0) + // Standard Error: 2_104 + .saturating_add(Weight::from_parts(625_410, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 50]`. fn instr_i64ctz(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 798 nanoseconds. - Weight::from_parts(1_095_113, 0) - .saturating_add(Weight::from_parts(0, 0)) - // Standard Error: 243 - .saturating_add(Weight::from_parts(634_204, 0).saturating_mul(r.into())) + // Minimum execution time: 1_208_000 picoseconds. + Weight::from_parts(1_483_908, 0) + // Standard Error: 319 + .saturating_add(Weight::from_parts(635_095, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 50]`. fn instr_i64popcnt(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 792 nanoseconds. - Weight::from_parts(1_109_845, 0) - .saturating_add(Weight::from_parts(0, 0)) - // Standard Error: 14_944 - .saturating_add(Weight::from_parts(658_834, 0).saturating_mul(r.into())) + // Minimum execution time: 1_164_000 picoseconds. + Weight::from_parts(1_506_871, 0) + // Standard Error: 264 + .saturating_add(Weight::from_parts(633_660, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 50]`. fn instr_i64eqz(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 815 nanoseconds. - Weight::from_parts(1_068_916, 0) - .saturating_add(Weight::from_parts(0, 0)) - // Standard Error: 327 - .saturating_add(Weight::from_parts(652_897, 0).saturating_mul(r.into())) + // Minimum execution time: 1_141_000 picoseconds. + Weight::from_parts(1_467_219, 0) + // Standard Error: 191 + .saturating_add(Weight::from_parts(651_156, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 50]`. fn instr_i64extendsi32(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 798 nanoseconds. - Weight::from_parts(1_069_745, 0) - .saturating_add(Weight::from_parts(0, 0)) - // Standard Error: 306 - .saturating_add(Weight::from_parts(618_481, 0).saturating_mul(r.into())) + // Minimum execution time: 1_170_000 picoseconds. + Weight::from_parts(1_474_718, 0) + // Standard Error: 224 + .saturating_add(Weight::from_parts(617_317, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 50]`. fn instr_i64extendui32(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 799 nanoseconds. - Weight::from_parts(1_398_001, 0) - .saturating_add(Weight::from_parts(0, 0)) - // Standard Error: 6_234 - .saturating_add(Weight::from_parts(611_399, 0).saturating_mul(r.into())) + // Minimum execution time: 1_195_000 picoseconds. + Weight::from_parts(1_501_421, 0) + // Standard Error: 221 + .saturating_add(Weight::from_parts(616_559, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 50]`. fn instr_i32wrapi64(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 811 nanoseconds. - Weight::from_parts(1_098_083, 0) - .saturating_add(Weight::from_parts(0, 0)) - // Standard Error: 297 - .saturating_add(Weight::from_parts(617_692, 0).saturating_mul(r.into())) + // Minimum execution time: 1_172_000 picoseconds. + Weight::from_parts(1_482_402, 0) + // Standard Error: 2_163 + .saturating_add(Weight::from_parts(619_230, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 50]`. fn instr_i64eq(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 815 nanoseconds. - Weight::from_parts(1_046_922, 0) - .saturating_add(Weight::from_parts(0, 0)) - // Standard Error: 335 - .saturating_add(Weight::from_parts(909_196, 0).saturating_mul(r.into())) + // Minimum execution time: 1_149_000 picoseconds. + Weight::from_parts(2_021_801, 0) + // Standard Error: 20_804 + .saturating_add(Weight::from_parts(913_888, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 50]`. fn instr_i64ne(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 805 nanoseconds. - Weight::from_parts(1_093_667, 0) - .saturating_add(Weight::from_parts(0, 0)) - // Standard Error: 233 - .saturating_add(Weight::from_parts(907_378, 0).saturating_mul(r.into())) + // Minimum execution time: 1_187_000 picoseconds. + Weight::from_parts(1_358_437, 0) + // Standard Error: 1_946 + .saturating_add(Weight::from_parts(916_378, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 50]`. fn instr_i64lts(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 805 nanoseconds. - Weight::from_parts(1_290_591, 0) - .saturating_add(Weight::from_parts(0, 0)) - // Standard Error: 3_201 - .saturating_add(Weight::from_parts(902_006, 0).saturating_mul(r.into())) + // Minimum execution time: 1_175_000 picoseconds. + Weight::from_parts(1_527_336, 0) + // Standard Error: 243 + .saturating_add(Weight::from_parts(907_580, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 50]`. fn instr_i64ltu(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 783 nanoseconds. - Weight::from_parts(1_159_977, 0) - .saturating_add(Weight::from_parts(0, 0)) - // Standard Error: 2_310 - .saturating_add(Weight::from_parts(906_489, 0).saturating_mul(r.into())) + // Minimum execution time: 1_220_000 picoseconds. + Weight::from_parts(1_530_715, 0) + // Standard Error: 224 + .saturating_add(Weight::from_parts(906_096, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 50]`. fn instr_i64gts(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 790 nanoseconds. - Weight::from_parts(1_109_719, 0) - .saturating_add(Weight::from_parts(0, 0)) - // Standard Error: 261 - .saturating_add(Weight::from_parts(906_614, 0).saturating_mul(r.into())) + // Minimum execution time: 1_107_000 picoseconds. + Weight::from_parts(1_496_553, 0) + // Standard Error: 299 + .saturating_add(Weight::from_parts(907_963, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 50]`. fn instr_i64gtu(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 776 nanoseconds. - Weight::from_parts(1_076_567, 0) - .saturating_add(Weight::from_parts(0, 0)) - // Standard Error: 348 - .saturating_add(Weight::from_parts(919_374, 0).saturating_mul(r.into())) + // Minimum execution time: 1_153_000 picoseconds. + Weight::from_parts(1_482_710, 0) + // Standard Error: 182 + .saturating_add(Weight::from_parts(918_439, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 50]`. fn instr_i64les(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 780 nanoseconds. - Weight::from_parts(1_069_663, 0) - .saturating_add(Weight::from_parts(0, 0)) - // Standard Error: 265 - .saturating_add(Weight::from_parts(908_037, 0).saturating_mul(r.into())) + // Minimum execution time: 1_142_000 picoseconds. + Weight::from_parts(1_522_114, 0) + // Standard Error: 297 + .saturating_add(Weight::from_parts(906_633, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 50]`. fn instr_i64leu(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 832 nanoseconds. - Weight::from_parts(930_920, 0) - .saturating_add(Weight::from_parts(0, 0)) - // Standard Error: 2_170 - .saturating_add(Weight::from_parts(929_811, 0).saturating_mul(r.into())) + // Minimum execution time: 1_119_000 picoseconds. + Weight::from_parts(2_199_831, 0) + // Standard Error: 4_234 + .saturating_add(Weight::from_parts(898_161, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 50]`. fn instr_i64ges(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 787 nanoseconds. - Weight::from_parts(1_087_325, 0) - .saturating_add(Weight::from_parts(0, 0)) - // Standard Error: 315 - .saturating_add(Weight::from_parts(908_321, 0).saturating_mul(r.into())) + // Minimum execution time: 1_163_000 picoseconds. + Weight::from_parts(1_489_404, 0) + // Standard Error: 416 + .saturating_add(Weight::from_parts(908_450, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 50]`. fn instr_i64geu(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 798 nanoseconds. - Weight::from_parts(1_029_132, 0) - .saturating_add(Weight::from_parts(0, 0)) - // Standard Error: 2_095 - .saturating_add(Weight::from_parts(913_553, 0).saturating_mul(r.into())) + // Minimum execution time: 1_204_000 picoseconds. + Weight::from_parts(2_266_296, 0) + // Standard Error: 13_013 + .saturating_add(Weight::from_parts(899_225, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 50]`. fn instr_i64add(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 791 nanoseconds. - Weight::from_parts(1_086_314, 0) - .saturating_add(Weight::from_parts(0, 0)) - // Standard Error: 197 - .saturating_add(Weight::from_parts(896_392, 0).saturating_mul(r.into())) + // Minimum execution time: 1_160_000 picoseconds. + Weight::from_parts(1_470_799, 0) + // Standard Error: 422 + .saturating_add(Weight::from_parts(897_325, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 50]`. fn instr_i64sub(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 793 nanoseconds. - Weight::from_parts(1_078_172, 0) - .saturating_add(Weight::from_parts(0, 0)) - // Standard Error: 404 - .saturating_add(Weight::from_parts(886_329, 0).saturating_mul(r.into())) + // Minimum execution time: 1_125_000 picoseconds. + Weight::from_parts(1_521_508, 0) + // Standard Error: 594 + .saturating_add(Weight::from_parts(886_145, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 50]`. fn instr_i64mul(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 799 nanoseconds. - Weight::from_parts(1_095_010, 0) - .saturating_add(Weight::from_parts(0, 0)) - // Standard Error: 431 - .saturating_add(Weight::from_parts(886_513, 0).saturating_mul(r.into())) + // Minimum execution time: 1_139_000 picoseconds. + Weight::from_parts(1_490_535, 0) + // Standard Error: 211 + .saturating_add(Weight::from_parts(884_929, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 50]`. fn instr_i64divs(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 810 nanoseconds. - Weight::from_parts(1_114_325, 0) - .saturating_add(Weight::from_parts(0, 0)) - // Standard Error: 452 - .saturating_add(Weight::from_parts(1_521_849, 0).saturating_mul(r.into())) + // Minimum execution time: 1_180_000 picoseconds. + Weight::from_parts(2_221_481, 0) + // Standard Error: 9_884 + .saturating_add(Weight::from_parts(1_507_511, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 50]`. fn instr_i64divu(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 784 nanoseconds. - Weight::from_parts(1_123_153, 0) - .saturating_add(Weight::from_parts(0, 0)) - // Standard Error: 475 - .saturating_add(Weight::from_parts(1_457_746, 0).saturating_mul(r.into())) + // Minimum execution time: 1_131_000 picoseconds. + Weight::from_parts(1_498_652, 0) + // Standard Error: 531 + .saturating_add(Weight::from_parts(1_451_498, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 50]`. fn instr_i64rems(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 809 nanoseconds. - Weight::from_parts(1_145_906, 0) - .saturating_add(Weight::from_parts(0, 0)) - // Standard Error: 718 - .saturating_add(Weight::from_parts(1_549_964, 0).saturating_mul(r.into())) + // Minimum execution time: 1_145_000 picoseconds. + Weight::from_parts(1_504_118, 0) + // Standard Error: 439 + .saturating_add(Weight::from_parts(1_521_895, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 50]`. fn instr_i64remu(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 803 nanoseconds. - Weight::from_parts(1_110_328, 0) - .saturating_add(Weight::from_parts(0, 0)) - // Standard Error: 627 - .saturating_add(Weight::from_parts(1_453_013, 0).saturating_mul(r.into())) + // Minimum execution time: 1_124_000 picoseconds. + Weight::from_parts(1_497_209, 0) + // Standard Error: 198 + .saturating_add(Weight::from_parts(1_449_843, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 50]`. fn instr_i64and(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 786 nanoseconds. - Weight::from_parts(1_108_792, 0) - .saturating_add(Weight::from_parts(0, 0)) - // Standard Error: 286 - .saturating_add(Weight::from_parts(897_035, 0).saturating_mul(r.into())) + // Minimum execution time: 1_169_000 picoseconds. + Weight::from_parts(1_534_491, 0) + // Standard Error: 283 + .saturating_add(Weight::from_parts(894_703, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 50]`. fn instr_i64or(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 787 nanoseconds. - Weight::from_parts(830_000, 0) - .saturating_add(Weight::from_parts(0, 0)) - // Standard Error: 15_995 - .saturating_add(Weight::from_parts(963_344, 0).saturating_mul(r.into())) + // Minimum execution time: 1_178_000 picoseconds. + Weight::from_parts(1_485_473, 0) + // Standard Error: 552 + .saturating_add(Weight::from_parts(897_878, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 50]`. fn instr_i64xor(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 773 nanoseconds. - Weight::from_parts(1_082_459, 0) - .saturating_add(Weight::from_parts(0, 0)) - // Standard Error: 330 - .saturating_add(Weight::from_parts(897_077, 0).saturating_mul(r.into())) + // Minimum execution time: 1_214_000 picoseconds. + Weight::from_parts(1_580_999, 0) + // Standard Error: 358 + .saturating_add(Weight::from_parts(894_207, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 50]`. fn instr_i64shl(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 810 nanoseconds. - Weight::from_parts(1_325_815, 0) - .saturating_add(Weight::from_parts(0, 0)) - // Standard Error: 3_352 - .saturating_add(Weight::from_parts(899_555, 0).saturating_mul(r.into())) + // Minimum execution time: 1_176_000 picoseconds. + Weight::from_parts(1_490_470, 0) + // Standard Error: 738 + .saturating_add(Weight::from_parts(904_900, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 50]`. fn instr_i64shrs(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 808 nanoseconds. - Weight::from_parts(1_085_903, 0) - .saturating_add(Weight::from_parts(0, 0)) - // Standard Error: 430 - .saturating_add(Weight::from_parts(903_249, 0).saturating_mul(r.into())) + // Minimum execution time: 1_197_000 picoseconds. + Weight::from_parts(1_519_703, 0) + // Standard Error: 336 + .saturating_add(Weight::from_parts(901_277, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 50]`. fn instr_i64shru(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 792 nanoseconds. - Weight::from_parts(1_091_261, 0) - .saturating_add(Weight::from_parts(0, 0)) - // Standard Error: 312 - .saturating_add(Weight::from_parts(902_245, 0).saturating_mul(r.into())) + // Minimum execution time: 1_166_000 picoseconds. + Weight::from_parts(1_507_499, 0) + // Standard Error: 298 + .saturating_add(Weight::from_parts(901_153, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 50]`. fn instr_i64rotl(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 807 nanoseconds. - Weight::from_parts(1_121_052, 0) - .saturating_add(Weight::from_parts(0, 0)) - // Standard Error: 506 - .saturating_add(Weight::from_parts(902_772, 0).saturating_mul(r.into())) + // Minimum execution time: 1_158_000 picoseconds. + Weight::from_parts(1_491_176, 0) + // Standard Error: 341 + .saturating_add(Weight::from_parts(901_880, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 50]`. fn instr_i64rotr(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 823 nanoseconds. - Weight::from_parts(1_317_597, 0) - .saturating_add(Weight::from_parts(0, 0)) - // Standard Error: 6_219 - .saturating_add(Weight::from_parts(896_692, 0).saturating_mul(r.into())) + // Minimum execution time: 1_207_000 picoseconds. + Weight::from_parts(1_477_261, 0) + // Standard Error: 287 + .saturating_add(Weight::from_parts(902_622, 0).saturating_mul(r.into())) } } @@ -2275,10 +2160,9 @@ impl WeightInfo for () { fn on_process_deletion_queue_batch() -> Weight { // Proof Size summary in bytes: // Measured: `109` - // Estimated: `604` - // Minimum execution time: 2_591 nanoseconds. - Weight::from_parts(2_817_000, 0) - .saturating_add(Weight::from_parts(0, 604)) + // Estimated: `1594` + // Minimum execution time: 2_679_000 picoseconds. + Weight::from_parts(2_907_000, 1594) .saturating_add(RocksDbWeight::get().reads(1_u64)) } /// Storage: Skipped Metadata (r:0 w:0) @@ -2286,14 +2170,14 @@ impl WeightInfo for () { /// The range of component `k` is `[0, 1024]`. fn on_initialize_per_trie_key(k: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `481 + k * (69 ±0)` - // Estimated: `471 + k * (70 ±0)` - // Minimum execution time: 10_190 nanoseconds. - Weight::from_parts(6_642_117, 0) - .saturating_add(Weight::from_parts(0, 471)) - // Standard Error: 992 - .saturating_add(Weight::from_parts(919_828, 0).saturating_mul(k.into())) + // Measured: `450 + k * (69 ±0)` + // Estimated: `440 + k * (70 ±0)` + // Minimum execution time: 11_162_000 picoseconds. + Weight::from_parts(5_734_923, 440) + // Standard Error: 1_097 + .saturating_add(Weight::from_parts(976_647, 0).saturating_mul(k.into())) .saturating_add(RocksDbWeight::get().reads(1_u64)) + .saturating_add(RocksDbWeight::get().reads((1_u64).saturating_mul(k.into()))) .saturating_add(RocksDbWeight::get().writes(1_u64)) .saturating_add(RocksDbWeight::get().writes((1_u64).saturating_mul(k.into()))) .saturating_add(Weight::from_parts(0, 70).saturating_mul(k.into())) @@ -2303,13 +2187,12 @@ impl WeightInfo for () { /// The range of component `q` is `[0, 128]`. fn on_initialize_per_queue_item(q: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `281 + q * (33 ±0)` - // Estimated: `763 + q * (33 ±0)` - // Minimum execution time: 2_598 nanoseconds. - Weight::from_parts(10_288_252, 0) - .saturating_add(Weight::from_parts(0, 763)) - // Standard Error: 2_886 - .saturating_add(Weight::from_parts(1_092_420, 0).saturating_mul(q.into())) + // Measured: `250 + q * (33 ±0)` + // Estimated: `1725 + q * (33 ±0)` + // Minimum execution time: 2_626_000 picoseconds. + Weight::from_parts(10_626_314, 1725) + // Standard Error: 4_006 + .saturating_add(Weight::from_parts(1_298_864, 0).saturating_mul(q.into())) .saturating_add(RocksDbWeight::get().reads(1_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) .saturating_add(Weight::from_parts(0, 33).saturating_mul(q.into())) @@ -2321,13 +2204,12 @@ impl WeightInfo for () { /// The range of component `c` is `[0, 61717]`. fn reinstrument(c: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `270 + c * (1 ±0)` - // Estimated: `3025 + c * (2 ±0)` - // Minimum execution time: 34_338 nanoseconds. - Weight::from_parts(32_159_677, 0) - .saturating_add(Weight::from_parts(0, 3025)) - // Standard Error: 53 - .saturating_add(Weight::from_parts(51_034, 0).saturating_mul(c.into())) + // Measured: `238 + c * (1 ±0)` + // Estimated: `3951 + c * (2 ±0)` + // Minimum execution time: 31_434_000 picoseconds. + Weight::from_parts(29_558_961, 3951) + // Standard Error: 55 + .saturating_add(Weight::from_parts(50_774, 0).saturating_mul(c.into())) .saturating_add(RocksDbWeight::get().reads(1_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) .saturating_add(Weight::from_parts(0, 2).saturating_mul(c.into())) @@ -2345,13 +2227,12 @@ impl WeightInfo for () { /// The range of component `c` is `[0, 125952]`. fn call_with_code_per_byte(c: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `803` - // Estimated: `16930 + c * (5 ±0)` - // Minimum execution time: 385_587 nanoseconds. - Weight::from_parts(395_545_811, 0) - .saturating_add(Weight::from_parts(0, 16930)) - // Standard Error: 27 - .saturating_add(Weight::from_parts(31_342, 0).saturating_mul(c.into())) + // Measured: `707` + // Estimated: `21400 + c * (5 ±0)` + // Minimum execution time: 309_015_000 picoseconds. + Weight::from_parts(318_740_885, 21400) + // Standard Error: 29 + .saturating_add(Weight::from_parts(30_993, 0).saturating_mul(c.into())) .saturating_add(RocksDbWeight::get().reads(6_u64)) .saturating_add(RocksDbWeight::get().writes(4_u64)) .saturating_add(Weight::from_parts(0, 5).saturating_mul(c.into())) @@ -2378,16 +2259,15 @@ impl WeightInfo for () { fn instantiate_with_code(c: u32, i: u32, s: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `270` - // Estimated: `20267` - // Minimum execution time: 3_799_742 nanoseconds. - Weight::from_parts(670_115_588, 0) - .saturating_add(Weight::from_parts(0, 20267)) - // Standard Error: 287 - .saturating_add(Weight::from_parts(93_885, 0).saturating_mul(c.into())) - // Standard Error: 16 - .saturating_add(Weight::from_parts(1_367, 0).saturating_mul(i.into())) - // Standard Error: 16 - .saturating_add(Weight::from_parts(1_781, 0).saturating_mul(s.into())) + // Estimated: `26207` + // Minimum execution time: 3_126_495_000 picoseconds. + Weight::from_parts(636_857_878, 26207) + // Standard Error: 294 + .saturating_add(Weight::from_parts(92_930, 0).saturating_mul(c.into())) + // Standard Error: 17 + .saturating_add(Weight::from_parts(1_128, 0).saturating_mul(i.into())) + // Standard Error: 17 + .saturating_add(Weight::from_parts(1_415, 0).saturating_mul(s.into())) .saturating_add(RocksDbWeight::get().reads(9_u64)) .saturating_add(RocksDbWeight::get().writes(10_u64)) } @@ -2409,15 +2289,14 @@ impl WeightInfo for () { /// The range of component `s` is `[0, 1048576]`. fn instantiate(i: u32, s: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `546` - // Estimated: `22039` - // Minimum execution time: 1_949_008 nanoseconds. - Weight::from_parts(214_033_418, 0) - .saturating_add(Weight::from_parts(0, 22039)) + // Measured: `482` + // Estimated: `28521` + // Minimum execution time: 1_591_415_000 picoseconds. + Weight::from_parts(228_633_516, 28521) // Standard Error: 8 - .saturating_add(Weight::from_parts(1_666, 0).saturating_mul(i.into())) + .saturating_add(Weight::from_parts(1_449, 0).saturating_mul(i.into())) // Standard Error: 8 - .saturating_add(Weight::from_parts(1_801, 0).saturating_mul(s.into())) + .saturating_add(Weight::from_parts(1_444, 0).saturating_mul(s.into())) .saturating_add(RocksDbWeight::get().reads(9_u64)) .saturating_add(RocksDbWeight::get().writes(7_u64)) } @@ -2433,11 +2312,10 @@ impl WeightInfo for () { /// Proof Skipped: System EventTopics (max_values: None, max_size: None, mode: Measured) fn call() -> Weight { // Proof Size summary in bytes: - // Measured: `855` - // Estimated: `17145` - // Minimum execution time: 146_654 nanoseconds. - Weight::from_parts(147_528_000, 0) - .saturating_add(Weight::from_parts(0, 17145)) + // Measured: `759` + // Estimated: `21615` + // Minimum execution time: 167_905_000 picoseconds. + Weight::from_parts(169_247_000, 21615) .saturating_add(RocksDbWeight::get().reads(6_u64)) .saturating_add(RocksDbWeight::get().writes(4_u64)) } @@ -2453,12 +2331,11 @@ impl WeightInfo for () { fn upload_code(c: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `109` - // Estimated: `5386` - // Minimum execution time: 387_889 nanoseconds. - Weight::from_parts(391_379_335, 0) - .saturating_add(Weight::from_parts(0, 5386)) - // Standard Error: 89 - .saturating_add(Weight::from_parts(94_810, 0).saturating_mul(c.into())) + // Estimated: `7366` + // Minimum execution time: 305_128_000 picoseconds. + Weight::from_parts(310_911_395, 7366) + // Standard Error: 70 + .saturating_add(Weight::from_parts(94_067, 0).saturating_mul(c.into())) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().writes(4_u64)) } @@ -2472,11 +2349,10 @@ impl WeightInfo for () { /// Proof: Contracts PristineCode (max_values: None, max_size: Some(125988), added: 128463, mode: Measured) fn remove_code() -> Weight { // Proof Size summary in bytes: - // Measured: `287` - // Estimated: `6098` - // Minimum execution time: 26_014 nanoseconds. - Weight::from_parts(26_510_000, 0) - .saturating_add(Weight::from_parts(0, 6098)) + // Measured: `255` + // Estimated: `7950` + // Minimum execution time: 29_605_000 picoseconds. + Weight::from_parts(29_986_000, 7950) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().writes(4_u64)) } @@ -2488,11 +2364,10 @@ impl WeightInfo for () { /// Proof Skipped: System EventTopics (max_values: None, max_size: None, mode: Measured) fn set_code() -> Weight { // Proof Size summary in bytes: - // Measured: `666` - // Estimated: `16848` - // Minimum execution time: 30_177 nanoseconds. - Weight::from_parts(30_639_000, 0) - .saturating_add(Weight::from_parts(0, 16848)) + // Measured: `570` + // Estimated: `19530` + // Minimum execution time: 33_824_000 picoseconds. + Weight::from_parts(34_388_000, 19530) .saturating_add(RocksDbWeight::get().reads(6_u64)) .saturating_add(RocksDbWeight::get().writes(6_u64)) } @@ -2509,13 +2384,12 @@ impl WeightInfo for () { /// The range of component `r` is `[0, 20]`. fn seal_caller(r: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `877 + r * (480 ±0)` - // Estimated: `17295 + r * (2400 ±0)` - // Minimum execution time: 373_786 nanoseconds. - Weight::from_parts(377_332_691, 0) - .saturating_add(Weight::from_parts(0, 17295)) - // Standard Error: 51_211 - .saturating_add(Weight::from_parts(17_715_615, 0).saturating_mul(r.into())) + // Measured: `781 + r * (480 ±0)` + // Estimated: `21765 + r * (2400 ±0)` + // Minimum execution time: 295_283_000 picoseconds. + Weight::from_parts(295_895_211, 21765) + // Standard Error: 44_860 + .saturating_add(Weight::from_parts(22_913_078, 0).saturating_mul(r.into())) .saturating_add(RocksDbWeight::get().reads(6_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) .saturating_add(Weight::from_parts(0, 2400).saturating_mul(r.into())) @@ -2533,17 +2407,16 @@ impl WeightInfo for () { /// The range of component `r` is `[0, 20]`. fn seal_is_contract(r: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `917 + r * (21778 ±0)` - // Estimated: `17295 + r * (306895 ±0)` - // Minimum execution time: 374_009 nanoseconds. - Weight::from_parts(238_991_986, 0) - .saturating_add(Weight::from_parts(0, 17295)) - // Standard Error: 464_711 - .saturating_add(Weight::from_parts(249_099_538, 0).saturating_mul(r.into())) + // Measured: `821 + r * (19218 ±0)` + // Estimated: `21765 + r * (294095 ±0)` + // Minimum execution time: 295_760_000 picoseconds. + Weight::from_parts(151_512_811, 21765) + // Standard Error: 522_974 + .saturating_add(Weight::from_parts(263_028_353, 0).saturating_mul(r.into())) .saturating_add(RocksDbWeight::get().reads(6_u64)) .saturating_add(RocksDbWeight::get().reads((80_u64).saturating_mul(r.into()))) .saturating_add(RocksDbWeight::get().writes(3_u64)) - .saturating_add(Weight::from_parts(0, 306895).saturating_mul(r.into())) + .saturating_add(Weight::from_parts(0, 294095).saturating_mul(r.into())) } /// Storage: System Account (r:1 w:0) /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: Measured) @@ -2558,17 +2431,16 @@ impl WeightInfo for () { /// The range of component `r` is `[0, 20]`. fn seal_code_hash(r: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `921 + r * (22099 ±0)` - // Estimated: `17340 + r * (308500 ±0)` - // Minimum execution time: 375_058 nanoseconds. - Weight::from_parts(238_765_068, 0) - .saturating_add(Weight::from_parts(0, 17340)) - // Standard Error: 662_617 - .saturating_add(Weight::from_parts(302_175_089, 0).saturating_mul(r.into())) + // Measured: `825 + r * (19539 ±0)` + // Estimated: `21810 + r * (295700 ±0)` + // Minimum execution time: 295_950_000 picoseconds. + Weight::from_parts(164_516_076, 21810) + // Standard Error: 469_540 + .saturating_add(Weight::from_parts(321_138_601, 0).saturating_mul(r.into())) .saturating_add(RocksDbWeight::get().reads(6_u64)) .saturating_add(RocksDbWeight::get().reads((80_u64).saturating_mul(r.into()))) .saturating_add(RocksDbWeight::get().writes(3_u64)) - .saturating_add(Weight::from_parts(0, 308500).saturating_mul(r.into())) + .saturating_add(Weight::from_parts(0, 295700).saturating_mul(r.into())) } /// Storage: System Account (r:1 w:0) /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: Measured) @@ -2583,13 +2455,12 @@ impl WeightInfo for () { /// The range of component `r` is `[0, 20]`. fn seal_own_code_hash(r: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `884 + r * (480 ±0)` - // Estimated: `17330 + r * (2400 ±0)` - // Minimum execution time: 374_747 nanoseconds. - Weight::from_parts(376_482_380, 0) - .saturating_add(Weight::from_parts(0, 17330)) - // Standard Error: 61_919 - .saturating_add(Weight::from_parts(22_376_795, 0).saturating_mul(r.into())) + // Measured: `788 + r * (480 ±0)` + // Estimated: `21800 + r * (2400 ±0)` + // Minimum execution time: 295_916_000 picoseconds. + Weight::from_parts(299_964_913, 21800) + // Standard Error: 78_211 + .saturating_add(Weight::from_parts(28_933_603, 0).saturating_mul(r.into())) .saturating_add(RocksDbWeight::get().reads(6_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) .saturating_add(Weight::from_parts(0, 2400).saturating_mul(r.into())) @@ -2607,13 +2478,12 @@ impl WeightInfo for () { /// The range of component `r` is `[0, 20]`. fn seal_caller_is_origin(r: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `874 + r * (240 ±0)` - // Estimated: `17265 + r * (1200 ±0)` - // Minimum execution time: 372_287 nanoseconds. - Weight::from_parts(376_250_858, 0) - .saturating_add(Weight::from_parts(0, 17265)) - // Standard Error: 40_119 - .saturating_add(Weight::from_parts(11_359_647, 0).saturating_mul(r.into())) + // Measured: `778 + r * (240 ±0)` + // Estimated: `21735 + r * (1200 ±0)` + // Minimum execution time: 293_588_000 picoseconds. + Weight::from_parts(297_568_984, 21735) + // Standard Error: 35_513 + .saturating_add(Weight::from_parts(11_074_880, 0).saturating_mul(r.into())) .saturating_add(RocksDbWeight::get().reads(6_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) .saturating_add(Weight::from_parts(0, 1200).saturating_mul(r.into())) @@ -2631,13 +2501,12 @@ impl WeightInfo for () { /// The range of component `r` is `[0, 20]`. fn seal_address(r: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `878 + r * (480 ±0)` - // Estimated: `17260 + r * (2400 ±0)` - // Minimum execution time: 374_445 nanoseconds. - Weight::from_parts(377_243_521, 0) - .saturating_add(Weight::from_parts(0, 17260)) - // Standard Error: 53_032 - .saturating_add(Weight::from_parts(17_684_246, 0).saturating_mul(r.into())) + // Measured: `782 + r * (480 ±0)` + // Estimated: `21730 + r * (2400 ±0)` + // Minimum execution time: 294_929_000 picoseconds. + Weight::from_parts(295_916_817, 21730) + // Standard Error: 64_902 + .saturating_add(Weight::from_parts(22_944_018, 0).saturating_mul(r.into())) .saturating_add(RocksDbWeight::get().reads(6_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) .saturating_add(Weight::from_parts(0, 2400).saturating_mul(r.into())) @@ -2655,13 +2524,12 @@ impl WeightInfo for () { /// The range of component `r` is `[0, 20]`. fn seal_gas_left(r: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `879 + r * (480 ±0)` - // Estimated: `17250 + r * (2405 ±0)` - // Minimum execution time: 374_029 nanoseconds. - Weight::from_parts(380_415_186, 0) - .saturating_add(Weight::from_parts(0, 17250)) - // Standard Error: 60_562 - .saturating_add(Weight::from_parts(17_152_599, 0).saturating_mul(r.into())) + // Measured: `783 + r * (480 ±0)` + // Estimated: `21720 + r * (2405 ±0)` + // Minimum execution time: 296_783_000 picoseconds. + Weight::from_parts(299_399_332, 21720) + // Standard Error: 50_982 + .saturating_add(Weight::from_parts(22_173_249, 0).saturating_mul(r.into())) .saturating_add(RocksDbWeight::get().reads(6_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) .saturating_add(Weight::from_parts(0, 2405).saturating_mul(r.into())) @@ -2679,16 +2547,15 @@ impl WeightInfo for () { /// The range of component `r` is `[0, 20]`. fn seal_balance(r: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `1049 + r * (480 ±0)` - // Estimated: `19849 + r * (2456 ±0)` - // Minimum execution time: 373_999 nanoseconds. - Weight::from_parts(381_757_033, 0) - .saturating_add(Weight::from_parts(0, 19849)) - // Standard Error: 97_983 - .saturating_add(Weight::from_parts(98_290_984, 0).saturating_mul(r.into())) + // Measured: `921 + r * (480 ±0)` + // Estimated: `24193 + r * (2451 ±0)` + // Minimum execution time: 294_120_000 picoseconds. + Weight::from_parts(305_889_989, 24193) + // Standard Error: 116_244 + .saturating_add(Weight::from_parts(114_781_094, 0).saturating_mul(r.into())) .saturating_add(RocksDbWeight::get().reads(7_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) - .saturating_add(Weight::from_parts(0, 2456).saturating_mul(r.into())) + .saturating_add(Weight::from_parts(0, 2451).saturating_mul(r.into())) } /// Storage: System Account (r:1 w:0) /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: Measured) @@ -2703,13 +2570,12 @@ impl WeightInfo for () { /// The range of component `r` is `[0, 20]`. fn seal_value_transferred(r: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `888 + r * (480 ±0)` - // Estimated: `17360 + r * (2400 ±0)` - // Minimum execution time: 374_197 nanoseconds. - Weight::from_parts(377_755_896, 0) - .saturating_add(Weight::from_parts(0, 17360)) - // Standard Error: 60_542 - .saturating_add(Weight::from_parts(17_442_065, 0).saturating_mul(r.into())) + // Measured: `792 + r * (480 ±0)` + // Estimated: `21830 + r * (2400 ±0)` + // Minimum execution time: 295_569_000 picoseconds. + Weight::from_parts(299_774_774, 21830) + // Standard Error: 46_252 + .saturating_add(Weight::from_parts(21_753_842, 0).saturating_mul(r.into())) .saturating_add(RocksDbWeight::get().reads(6_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) .saturating_add(Weight::from_parts(0, 2400).saturating_mul(r.into())) @@ -2727,13 +2593,12 @@ impl WeightInfo for () { /// The range of component `r` is `[0, 20]`. fn seal_minimum_balance(r: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `886 + r * (480 ±0)` - // Estimated: `17290 + r * (2400 ±0)` - // Minimum execution time: 373_888 nanoseconds. - Weight::from_parts(377_825_771, 0) - .saturating_add(Weight::from_parts(0, 17290)) - // Standard Error: 38_026 - .saturating_add(Weight::from_parts(17_147_903, 0).saturating_mul(r.into())) + // Measured: `790 + r * (480 ±0)` + // Estimated: `21760 + r * (2400 ±0)` + // Minimum execution time: 295_400_000 picoseconds. + Weight::from_parts(300_637_266, 21760) + // Standard Error: 41_263 + .saturating_add(Weight::from_parts(21_565_466, 0).saturating_mul(r.into())) .saturating_add(RocksDbWeight::get().reads(6_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) .saturating_add(Weight::from_parts(0, 2400).saturating_mul(r.into())) @@ -2751,13 +2616,12 @@ impl WeightInfo for () { /// The range of component `r` is `[0, 20]`. fn seal_block_number(r: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `883 + r * (480 ±0)` - // Estimated: `17315 + r * (2400 ±0)` - // Minimum execution time: 373_904 nanoseconds. - Weight::from_parts(378_652_372, 0) - .saturating_add(Weight::from_parts(0, 17315)) - // Standard Error: 43_833 - .saturating_add(Weight::from_parts(16_936_781, 0).saturating_mul(r.into())) + // Measured: `787 + r * (480 ±0)` + // Estimated: `21785 + r * (2400 ±0)` + // Minimum execution time: 294_696_000 picoseconds. + Weight::from_parts(297_196_796, 21785) + // Standard Error: 61_173 + .saturating_add(Weight::from_parts(21_903_191, 0).saturating_mul(r.into())) .saturating_add(RocksDbWeight::get().reads(6_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) .saturating_add(Weight::from_parts(0, 2400).saturating_mul(r.into())) @@ -2775,13 +2639,12 @@ impl WeightInfo for () { /// The range of component `r` is `[0, 20]`. fn seal_now(r: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `874 + r * (480 ±0)` - // Estimated: `17245 + r * (2400 ±0)` - // Minimum execution time: 373_473 nanoseconds. - Weight::from_parts(376_386_312, 0) - .saturating_add(Weight::from_parts(0, 17245)) - // Standard Error: 46_945 - .saturating_add(Weight::from_parts(17_336_462, 0).saturating_mul(r.into())) + // Measured: `778 + r * (480 ±0)` + // Estimated: `21715 + r * (2400 ±0)` + // Minimum execution time: 297_714_000 picoseconds. + Weight::from_parts(306_030_237, 21715) + // Standard Error: 69_984 + .saturating_add(Weight::from_parts(21_744_413, 0).saturating_mul(r.into())) .saturating_add(RocksDbWeight::get().reads(6_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) .saturating_add(Weight::from_parts(0, 2400).saturating_mul(r.into())) @@ -2801,13 +2664,12 @@ impl WeightInfo for () { /// The range of component `r` is `[0, 20]`. fn seal_weight_to_fee(r: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `951 + r * (800 ±0)` - // Estimated: `19046 + r * (4805 ±0)` - // Minimum execution time: 373_661 nanoseconds. - Weight::from_parts(385_824_015, 0) - .saturating_add(Weight::from_parts(0, 19046)) - // Standard Error: 75_964 - .saturating_add(Weight::from_parts(88_530_074, 0).saturating_mul(r.into())) + // Measured: `856 + r * (800 ±0)` + // Estimated: `24416 + r * (4805 ±0)` + // Minimum execution time: 295_180_000 picoseconds. + Weight::from_parts(308_851_025, 24416) + // Standard Error: 115_126 + .saturating_add(Weight::from_parts(98_678_404, 0).saturating_mul(r.into())) .saturating_add(RocksDbWeight::get().reads(7_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) .saturating_add(Weight::from_parts(0, 4805).saturating_mul(r.into())) @@ -2825,13 +2687,12 @@ impl WeightInfo for () { /// The range of component `r` is `[0, 20]`. fn seal_gas(r: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `841 + r * (320 ±0)` - // Estimated: `17120 + r * (1600 ±0)` - // Minimum execution time: 133_849 nanoseconds. - Weight::from_parts(137_283_391, 0) - .saturating_add(Weight::from_parts(0, 17120)) - // Standard Error: 13_312 - .saturating_add(Weight::from_parts(8_055_328, 0).saturating_mul(r.into())) + // Measured: `745 + r * (320 ±0)` + // Estimated: `21590 + r * (1600 ±0)` + // Minimum execution time: 151_587_000 picoseconds. + Weight::from_parts(155_713_096, 21590) + // Standard Error: 32_464 + .saturating_add(Weight::from_parts(8_332_778, 0).saturating_mul(r.into())) .saturating_add(RocksDbWeight::get().reads(6_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) .saturating_add(Weight::from_parts(0, 1600).saturating_mul(r.into())) @@ -2849,13 +2710,12 @@ impl WeightInfo for () { /// The range of component `r` is `[0, 20]`. fn seal_input(r: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `876 + r * (480 ±0)` - // Estimated: `17245 + r * (2400 ±0)` - // Minimum execution time: 373_468 nanoseconds. - Weight::from_parts(376_121_093, 0) - .saturating_add(Weight::from_parts(0, 17245)) - // Standard Error: 61_857 - .saturating_add(Weight::from_parts(15_868_414, 0).saturating_mul(r.into())) + // Measured: `780 + r * (480 ±0)` + // Estimated: `21715 + r * (2400 ±0)` + // Minimum execution time: 295_008_000 picoseconds. + Weight::from_parts(297_982_800, 21715) + // Standard Error: 37_480 + .saturating_add(Weight::from_parts(17_970_520, 0).saturating_mul(r.into())) .saturating_add(RocksDbWeight::get().reads(6_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) .saturating_add(Weight::from_parts(0, 2400).saturating_mul(r.into())) @@ -2873,13 +2733,12 @@ impl WeightInfo for () { /// The range of component `n` is `[0, 1024]`. fn seal_input_per_kb(n: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `1356` - // Estimated: `19650` - // Minimum execution time: 390_668 nanoseconds. - Weight::from_parts(419_608_449, 0) - .saturating_add(Weight::from_parts(0, 19650)) - // Standard Error: 4_890 - .saturating_add(Weight::from_parts(9_672_288, 0).saturating_mul(n.into())) + // Measured: `1260` + // Estimated: `24120` + // Minimum execution time: 314_970_000 picoseconds. + Weight::from_parts(345_339_508, 24120) + // Standard Error: 4_112 + .saturating_add(Weight::from_parts(9_746_966, 0).saturating_mul(n.into())) .saturating_add(RocksDbWeight::get().reads(6_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) } @@ -2896,13 +2755,12 @@ impl WeightInfo for () { /// The range of component `r` is `[0, 1]`. fn seal_return(r: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `864 + r * (45 ±0)` - // Estimated: `17190 + r * (225 ±0)` - // Minimum execution time: 371_309 nanoseconds. - Weight::from_parts(373_625_402, 0) - .saturating_add(Weight::from_parts(0, 17190)) - // Standard Error: 419_605 - .saturating_add(Weight::from_parts(1_737_397, 0).saturating_mul(r.into())) + // Measured: `768 + r * (45 ±0)` + // Estimated: `21660 + r * (225 ±0)` + // Minimum execution time: 293_072_000 picoseconds. + Weight::from_parts(295_694_824, 21660) + // Standard Error: 219_523 + .saturating_add(Weight::from_parts(783_975, 0).saturating_mul(r.into())) .saturating_add(RocksDbWeight::get().reads(6_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) .saturating_add(Weight::from_parts(0, 225).saturating_mul(r.into())) @@ -2920,13 +2778,12 @@ impl WeightInfo for () { /// The range of component `n` is `[0, 1024]`. fn seal_return_per_kb(n: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `874` - // Estimated: `17285` - // Minimum execution time: 374_094 nanoseconds. - Weight::from_parts(375_965_200, 0) - .saturating_add(Weight::from_parts(0, 17285)) - // Standard Error: 1_127 - .saturating_add(Weight::from_parts(232_645, 0).saturating_mul(n.into())) + // Measured: `778` + // Estimated: `21755` + // Minimum execution time: 295_959_000 picoseconds. + Weight::from_parts(299_004_761, 21755) + // Standard Error: 3_988 + .saturating_add(Weight::from_parts(194_268, 0).saturating_mul(n.into())) .saturating_add(RocksDbWeight::get().reads(6_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) } @@ -2947,18 +2804,17 @@ impl WeightInfo for () { /// The range of component `r` is `[0, 1]`. fn seal_terminate(r: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `906 + r * (452 ±0)` - // Estimated: `20242 + r * (15004 ±0)` - // Minimum execution time: 373_123 nanoseconds. - Weight::from_parts(374_924_634, 0) - .saturating_add(Weight::from_parts(0, 20242)) - // Standard Error: 378_010 - .saturating_add(Weight::from_parts(70_441_665, 0).saturating_mul(r.into())) + // Measured: `810 + r * (356 ±0)` + // Estimated: `25511 + r * (15321 ±0)` + // Minimum execution time: 294_626_000 picoseconds. + Weight::from_parts(297_839_179, 25511) + // Standard Error: 707_428 + .saturating_add(Weight::from_parts(81_507_620, 0).saturating_mul(r.into())) .saturating_add(RocksDbWeight::get().reads(6_u64)) .saturating_add(RocksDbWeight::get().reads((6_u64).saturating_mul(r.into()))) .saturating_add(RocksDbWeight::get().writes(3_u64)) .saturating_add(RocksDbWeight::get().writes((7_u64).saturating_mul(r.into()))) - .saturating_add(Weight::from_parts(0, 15004).saturating_mul(r.into())) + .saturating_add(Weight::from_parts(0, 15321).saturating_mul(r.into())) } /// Storage: System Account (r:1 w:0) /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: Measured) @@ -2975,13 +2831,12 @@ impl WeightInfo for () { /// The range of component `r` is `[0, 20]`. fn seal_random(r: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `921 + r * (800 ±0)` - // Estimated: `18835 + r * (4805 ±0)` - // Minimum execution time: 373_291 nanoseconds. - Weight::from_parts(385_684_344, 0) - .saturating_add(Weight::from_parts(0, 18835)) - // Standard Error: 99_025 - .saturating_add(Weight::from_parts(111_308_793, 0).saturating_mul(r.into())) + // Measured: `825 + r * (800 ±0)` + // Estimated: `24199 + r * (4805 ±0)` + // Minimum execution time: 295_431_000 picoseconds. + Weight::from_parts(310_428_531, 24199) + // Standard Error: 143_501 + .saturating_add(Weight::from_parts(136_529_376, 0).saturating_mul(r.into())) .saturating_add(RocksDbWeight::get().reads(7_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) .saturating_add(Weight::from_parts(0, 4805).saturating_mul(r.into())) @@ -2999,13 +2854,12 @@ impl WeightInfo for () { /// The range of component `r` is `[0, 20]`. fn seal_deposit_event(r: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `874 + r * (800 ±0)` - // Estimated: `17250 + r * (4000 ±0)` - // Minimum execution time: 371_900 nanoseconds. - Weight::from_parts(384_166_626, 0) - .saturating_add(Weight::from_parts(0, 17250)) - // Standard Error: 205_255 - .saturating_add(Weight::from_parts(229_214_157, 0).saturating_mul(r.into())) + // Measured: `778 + r * (800 ±0)` + // Estimated: `21720 + r * (4000 ±0)` + // Minimum execution time: 293_778_000 picoseconds. + Weight::from_parts(323_404_081, 21720) + // Standard Error: 243_095 + .saturating_add(Weight::from_parts(269_012_482, 0).saturating_mul(r.into())) .saturating_add(RocksDbWeight::get().reads(6_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) .saturating_add(Weight::from_parts(0, 4000).saturating_mul(r.into())) @@ -3024,15 +2878,14 @@ impl WeightInfo for () { /// The range of component `n` is `[0, 16]`. fn seal_deposit_event_per_topic_and_kb(t: u32, n: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `1821 + t * (2608 ±0) + n * (7 ±0)` - // Estimated: `21870 + t * (211030 ±0) + n * (50 ±0)` - // Minimum execution time: 1_289_873 nanoseconds. - Weight::from_parts(581_702_206, 0) - .saturating_add(Weight::from_parts(0, 21870)) - // Standard Error: 665_638 - .saturating_add(Weight::from_parts(181_470_553, 0).saturating_mul(t.into())) - // Standard Error: 182_816 - .saturating_add(Weight::from_parts(71_635_250, 0).saturating_mul(n.into())) + // Measured: `1725 + t * (2608 ±0) + n * (7 ±0)` + // Estimated: `26340 + t * (211030 ±0) + n * (50 ±0)` + // Minimum execution time: 1_317_129_000 picoseconds. + Weight::from_parts(566_722_295, 26340) + // Standard Error: 619_390 + .saturating_add(Weight::from_parts(194_334_643, 0).saturating_mul(t.into())) + // Standard Error: 170_114 + .saturating_add(Weight::from_parts(67_458_751, 0).saturating_mul(n.into())) .saturating_add(RocksDbWeight::get().reads(6_u64)) .saturating_add(RocksDbWeight::get().reads((80_u64).saturating_mul(t.into()))) .saturating_add(RocksDbWeight::get().writes(3_u64)) @@ -3053,13 +2906,12 @@ impl WeightInfo for () { /// The range of component `r` is `[0, 20]`. fn seal_debug_message(r: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `873 + r * (560 ±0)` - // Estimated: `17240 + r * (2800 ±0)` - // Minimum execution time: 148_635 nanoseconds. - Weight::from_parts(154_095_712, 0) - .saturating_add(Weight::from_parts(0, 17240)) - // Standard Error: 77_790 - .saturating_add(Weight::from_parts(14_837_085, 0).saturating_mul(r.into())) + // Measured: `777 + r * (560 ±0)` + // Estimated: `21710 + r * (2800 ±0)` + // Minimum execution time: 159_087_000 picoseconds. + Weight::from_parts(163_703_981, 21710) + // Standard Error: 26_067 + .saturating_add(Weight::from_parts(14_810_256, 0).saturating_mul(r.into())) .saturating_add(RocksDbWeight::get().reads(6_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) .saturating_add(Weight::from_parts(0, 2800).saturating_mul(r.into())) @@ -3077,13 +2929,12 @@ impl WeightInfo for () { /// The range of component `i` is `[0, 1024]`. fn seal_debug_message_per_kb(i: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `125824` - // Estimated: `265128` - // Minimum execution time: 501_014 nanoseconds. - Weight::from_parts(505_634_218, 0) - .saturating_add(Weight::from_parts(0, 265128)) - // Standard Error: 2_441 - .saturating_add(Weight::from_parts(819_257, 0).saturating_mul(i.into())) + // Measured: `125728` + // Estimated: `269982` + // Minimum execution time: 413_391_000 picoseconds. + Weight::from_parts(414_927_640, 269982) + // Standard Error: 1_740 + .saturating_add(Weight::from_parts(763_359, 0).saturating_mul(i.into())) .saturating_add(RocksDbWeight::get().reads(6_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) } @@ -3092,13 +2943,12 @@ impl WeightInfo for () { /// The range of component `r` is `[0, 10]`. fn seal_set_storage(r: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `911 + r * (23420 ±0)` - // Estimated: `911 + r * (23418 ±0)` - // Minimum execution time: 375_301 nanoseconds. - Weight::from_parts(291_498_841, 0) - .saturating_add(Weight::from_parts(0, 911)) - // Standard Error: 809_989 - .saturating_add(Weight::from_parts(464_550_291, 0).saturating_mul(r.into())) + // Measured: `815 + r * (23420 ±0)` + // Estimated: `815 + r * (23418 ±0)` + // Minimum execution time: 296_108_000 picoseconds. + Weight::from_parts(208_214_040, 815) + // Standard Error: 860_573 + .saturating_add(Weight::from_parts(480_342_550, 0).saturating_mul(r.into())) .saturating_add(RocksDbWeight::get().reads(6_u64)) .saturating_add(RocksDbWeight::get().reads((80_u64).saturating_mul(r.into()))) .saturating_add(RocksDbWeight::get().writes(3_u64)) @@ -3110,13 +2960,12 @@ impl WeightInfo for () { /// The range of component `n` is `[0, 8]`. fn seal_set_storage_per_new_kb(n: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `12672 + n * (11945 ±0)` - // Estimated: `8529 + n * (12814 ±61)` - // Minimum execution time: 506_318 nanoseconds. - Weight::from_parts(676_935_313, 0) - .saturating_add(Weight::from_parts(0, 8529)) - // Standard Error: 1_589_291 - .saturating_add(Weight::from_parts(97_839_399, 0).saturating_mul(n.into())) + // Measured: `12544 + n * (11945 ±0)` + // Estimated: `8401 + n * (12814 ±61)` + // Minimum execution time: 442_286_000 picoseconds. + Weight::from_parts(620_395_299, 8401) + // Standard Error: 1_661_554 + .saturating_add(Weight::from_parts(89_393_169, 0).saturating_mul(n.into())) .saturating_add(RocksDbWeight::get().reads(52_u64)) .saturating_add(RocksDbWeight::get().reads((7_u64).saturating_mul(n.into()))) .saturating_add(RocksDbWeight::get().writes(50_u64)) @@ -3128,31 +2977,29 @@ impl WeightInfo for () { /// The range of component `n` is `[0, 8]`. fn seal_set_storage_per_old_kb(n: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `15170 + n * (175775 ±0)` - // Estimated: `9914 + n * (176858 ±74)` - // Minimum execution time: 506_148 nanoseconds. - Weight::from_parts(648_278_778, 0) - .saturating_add(Weight::from_parts(0, 9914)) - // Standard Error: 1_343_586 - .saturating_add(Weight::from_parts(65_789_595, 0).saturating_mul(n.into())) + // Measured: `12532 + n * (175784 ±0)` + // Estimated: `8396 + n * (176649 ±61)` + // Minimum execution time: 440_882_000 picoseconds. + Weight::from_parts(587_317_981, 8396) + // Standard Error: 1_374_991 + .saturating_add(Weight::from_parts(66_271_084, 0).saturating_mul(n.into())) .saturating_add(RocksDbWeight::get().reads(51_u64)) .saturating_add(RocksDbWeight::get().reads((7_u64).saturating_mul(n.into()))) .saturating_add(RocksDbWeight::get().writes(49_u64)) .saturating_add(RocksDbWeight::get().writes((7_u64).saturating_mul(n.into()))) - .saturating_add(Weight::from_parts(0, 176858).saturating_mul(n.into())) + .saturating_add(Weight::from_parts(0, 176649).saturating_mul(n.into())) } /// Storage: Skipped Metadata (r:0 w:0) /// Proof Skipped: Skipped Metadata (max_values: None, max_size: None, mode: Measured) /// The range of component `r` is `[0, 10]`. fn seal_clear_storage(r: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `903 + r * (23099 ±0)` - // Estimated: `908 + r * (23099 ±0)` - // Minimum execution time: 374_344 nanoseconds. - Weight::from_parts(293_272_061, 0) - .saturating_add(Weight::from_parts(0, 908)) - // Standard Error: 810_412 - .saturating_add(Weight::from_parts(453_315_956, 0).saturating_mul(r.into())) + // Measured: `807 + r * (23099 ±0)` + // Estimated: `812 + r * (23099 ±0)` + // Minimum execution time: 296_023_000 picoseconds. + Weight::from_parts(221_471_559, 812) + // Standard Error: 814_503 + .saturating_add(Weight::from_parts(465_928_089, 0).saturating_mul(r.into())) .saturating_add(RocksDbWeight::get().reads(6_u64)) .saturating_add(RocksDbWeight::get().reads((80_u64).saturating_mul(r.into()))) .saturating_add(RocksDbWeight::get().writes(3_u64)) @@ -3164,31 +3011,29 @@ impl WeightInfo for () { /// The range of component `n` is `[0, 8]`. fn seal_clear_storage_per_kb(n: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `14895 + n * (175768 ±0)` - // Estimated: `9551 + n * (176867 ±75)` - // Minimum execution time: 478_564 nanoseconds. - Weight::from_parts(630_839_142, 0) - .saturating_add(Weight::from_parts(0, 9551)) - // Standard Error: 1_427_520 - .saturating_add(Weight::from_parts(66_813_592, 0).saturating_mul(n.into())) + // Measured: `12256 + n * (175776 ±0)` + // Estimated: `8047 + n * (176655 ±62)` + // Minimum execution time: 412_212_000 picoseconds. + Weight::from_parts(569_213_147, 8047) + // Standard Error: 1_463_771 + .saturating_add(Weight::from_parts(67_932_081, 0).saturating_mul(n.into())) .saturating_add(RocksDbWeight::get().reads(51_u64)) .saturating_add(RocksDbWeight::get().reads((7_u64).saturating_mul(n.into()))) .saturating_add(RocksDbWeight::get().writes(48_u64)) .saturating_add(RocksDbWeight::get().writes((7_u64).saturating_mul(n.into()))) - .saturating_add(Weight::from_parts(0, 176867).saturating_mul(n.into())) + .saturating_add(Weight::from_parts(0, 176655).saturating_mul(n.into())) } /// Storage: Skipped Metadata (r:0 w:0) /// Proof Skipped: Skipped Metadata (max_values: None, max_size: None, mode: Measured) /// The range of component `r` is `[0, 10]`. fn seal_get_storage(r: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `896 + r * (23744 ±0)` - // Estimated: `909 + r * (23740 ±0)` - // Minimum execution time: 374_479 nanoseconds. - Weight::from_parts(311_839_315, 0) - .saturating_add(Weight::from_parts(0, 909)) - // Standard Error: 666_553 - .saturating_add(Weight::from_parts(371_213_042, 0).saturating_mul(r.into())) + // Measured: `800 + r * (23744 ±0)` + // Estimated: `813 + r * (23740 ±0)` + // Minimum execution time: 296_007_000 picoseconds. + Weight::from_parts(229_100_700, 813) + // Standard Error: 684_970 + .saturating_add(Weight::from_parts(394_982_991, 0).saturating_mul(r.into())) .saturating_add(RocksDbWeight::get().reads(6_u64)) .saturating_add(RocksDbWeight::get().reads((80_u64).saturating_mul(r.into()))) .saturating_add(RocksDbWeight::get().writes(3_u64)) @@ -3199,30 +3044,28 @@ impl WeightInfo for () { /// The range of component `n` is `[0, 8]`. fn seal_get_storage_per_kb(n: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `15501 + n * (175775 ±0)` - // Estimated: `10042 + n * (176900 ±76)` - // Minimum execution time: 460_639 nanoseconds. - Weight::from_parts(591_187_094, 0) - .saturating_add(Weight::from_parts(0, 10042)) - // Standard Error: 1_233_792 - .saturating_add(Weight::from_parts(160_874_477, 0).saturating_mul(n.into())) + // Measured: `12863 + n * (175784 ±0)` + // Estimated: `8538 + n * (176688 ±63)` + // Minimum execution time: 392_306_000 picoseconds. + Weight::from_parts(539_188_004, 8538) + // Standard Error: 1_371_839 + .saturating_add(Weight::from_parts(155_130_373, 0).saturating_mul(n.into())) .saturating_add(RocksDbWeight::get().reads(51_u64)) .saturating_add(RocksDbWeight::get().reads((7_u64).saturating_mul(n.into()))) .saturating_add(RocksDbWeight::get().writes(3_u64)) - .saturating_add(Weight::from_parts(0, 176900).saturating_mul(n.into())) + .saturating_add(Weight::from_parts(0, 176688).saturating_mul(n.into())) } /// Storage: Skipped Metadata (r:0 w:0) /// Proof Skipped: Skipped Metadata (max_values: None, max_size: None, mode: Measured) /// The range of component `r` is `[0, 10]`. fn seal_contains_storage(r: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `914 + r * (23098 ±0)` - // Estimated: `920 + r * (23098 ±0)` - // Minimum execution time: 374_272 nanoseconds. - Weight::from_parts(311_446_269, 0) - .saturating_add(Weight::from_parts(0, 920)) - // Standard Error: 630_307 - .saturating_add(Weight::from_parts(357_134_931, 0).saturating_mul(r.into())) + // Measured: `818 + r * (23098 ±0)` + // Estimated: `824 + r * (23098 ±0)` + // Minimum execution time: 296_038_000 picoseconds. + Weight::from_parts(229_462_798, 824) + // Standard Error: 655_463 + .saturating_add(Weight::from_parts(372_593_685, 0).saturating_mul(r.into())) .saturating_add(RocksDbWeight::get().reads(6_u64)) .saturating_add(RocksDbWeight::get().reads((80_u64).saturating_mul(r.into()))) .saturating_add(RocksDbWeight::get().writes(3_u64)) @@ -3233,30 +3076,28 @@ impl WeightInfo for () { /// The range of component `n` is `[0, 8]`. fn seal_contains_storage_per_kb(n: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `14839 + n * (175789 ±0)` - // Estimated: `9532 + n * (176874 ±75)` - // Minimum execution time: 456_013 nanoseconds. - Weight::from_parts(575_116_352, 0) - .saturating_add(Weight::from_parts(0, 9532)) - // Standard Error: 1_122_298 - .saturating_add(Weight::from_parts(61_786_107, 0).saturating_mul(n.into())) + // Measured: `12200 + n * (175798 ±0)` + // Estimated: `8028 + n * (176661 ±62)` + // Minimum execution time: 386_782_000 picoseconds. + Weight::from_parts(512_965_975, 8028) + // Standard Error: 1_168_132 + .saturating_add(Weight::from_parts(63_307_325, 0).saturating_mul(n.into())) .saturating_add(RocksDbWeight::get().reads(51_u64)) .saturating_add(RocksDbWeight::get().reads((7_u64).saturating_mul(n.into()))) .saturating_add(RocksDbWeight::get().writes(3_u64)) - .saturating_add(Weight::from_parts(0, 176874).saturating_mul(n.into())) + .saturating_add(Weight::from_parts(0, 176661).saturating_mul(n.into())) } /// Storage: Skipped Metadata (r:0 w:0) /// Proof Skipped: Skipped Metadata (max_values: None, max_size: None, mode: Measured) /// The range of component `r` is `[0, 10]`. fn seal_take_storage(r: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `911 + r * (23740 ±0)` - // Estimated: `913 + r * (23739 ±0)` - // Minimum execution time: 374_621 nanoseconds. - Weight::from_parts(299_689_489, 0) - .saturating_add(Weight::from_parts(0, 913)) - // Standard Error: 757_735 - .saturating_add(Weight::from_parts(465_213_246, 0).saturating_mul(r.into())) + // Measured: `815 + r * (23740 ±0)` + // Estimated: `817 + r * (23739 ±0)` + // Minimum execution time: 296_352_000 picoseconds. + Weight::from_parts(212_661_296, 817) + // Standard Error: 879_297 + .saturating_add(Weight::from_parts(494_182_672, 0).saturating_mul(r.into())) .saturating_add(RocksDbWeight::get().reads(6_u64)) .saturating_add(RocksDbWeight::get().reads((80_u64).saturating_mul(r.into()))) .saturating_add(RocksDbWeight::get().writes(3_u64)) @@ -3268,18 +3109,17 @@ impl WeightInfo for () { /// The range of component `n` is `[0, 8]`. fn seal_take_storage_per_kb(n: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `15502 + n * (175775 ±0)` - // Estimated: `10042 + n * (176898 ±76)` - // Minimum execution time: 481_980 nanoseconds. - Weight::from_parts(647_289_053, 0) - .saturating_add(Weight::from_parts(0, 10042)) - // Standard Error: 1_556_155 - .saturating_add(Weight::from_parts(166_592_657, 0).saturating_mul(n.into())) + // Measured: `12864 + n * (175784 ±0)` + // Estimated: `8539 + n * (176686 ±63)` + // Minimum execution time: 414_693_000 picoseconds. + Weight::from_parts(595_693_859, 8539) + // Standard Error: 1_673_819 + .saturating_add(Weight::from_parts(163_090_316, 0).saturating_mul(n.into())) .saturating_add(RocksDbWeight::get().reads(51_u64)) .saturating_add(RocksDbWeight::get().reads((7_u64).saturating_mul(n.into()))) .saturating_add(RocksDbWeight::get().writes(48_u64)) .saturating_add(RocksDbWeight::get().writes((7_u64).saturating_mul(n.into()))) - .saturating_add(Weight::from_parts(0, 176898).saturating_mul(n.into())) + .saturating_add(Weight::from_parts(0, 176686).saturating_mul(n.into())) } /// Storage: System Account (r:1602 w:1601) /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: Measured) @@ -3294,18 +3134,17 @@ impl WeightInfo for () { /// The range of component `r` is `[0, 20]`. fn seal_transfer(r: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `1457 + r * (3604 ±0)` - // Estimated: `21583 + r * (216101 ±0)` - // Minimum execution time: 374_962 nanoseconds. - Weight::from_parts(313_416_386, 0) - .saturating_add(Weight::from_parts(0, 21583)) - // Standard Error: 710_675 - .saturating_add(Weight::from_parts(1_396_551_156, 0).saturating_mul(r.into())) + // Measured: `1329 + r * (3604 ±0)` + // Estimated: `25928 + r * (216096 ±4)` + // Minimum execution time: 299_205_000 picoseconds. + Weight::from_parts(221_142_217, 25928) + // Standard Error: 1_369_909 + .saturating_add(Weight::from_parts(1_665_917_241, 0).saturating_mul(r.into())) .saturating_add(RocksDbWeight::get().reads(7_u64)) .saturating_add(RocksDbWeight::get().reads((80_u64).saturating_mul(r.into()))) .saturating_add(RocksDbWeight::get().writes(4_u64)) .saturating_add(RocksDbWeight::get().writes((80_u64).saturating_mul(r.into()))) - .saturating_add(Weight::from_parts(0, 216101).saturating_mul(r.into())) + .saturating_add(Weight::from_parts(0, 216096).saturating_mul(r.into())) } /// Storage: System Account (r:1 w:0) /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: Measured) @@ -3320,18 +3159,17 @@ impl WeightInfo for () { /// The range of component `r` is `[0, 20]`. fn seal_call(r: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `1609 + r * (23073 ±0)` - // Estimated: `22098 + r * (511456 ±1)` - // Minimum execution time: 375_916 nanoseconds. - Weight::from_parts(376_468_000, 0) - .saturating_add(Weight::from_parts(0, 22098)) - // Standard Error: 7_246_855 - .saturating_add(Weight::from_parts(28_982_425_139, 0).saturating_mul(r.into())) + // Measured: `1479 + r * (20514 ±0)` + // Estimated: `26429 + r * (498656 ±1)` + // Minimum execution time: 297_501_000 picoseconds. + Weight::from_parts(298_400_000, 26429) + // Standard Error: 9_372_890 + .saturating_add(Weight::from_parts(22_507_984_433, 0).saturating_mul(r.into())) .saturating_add(RocksDbWeight::get().reads(7_u64)) .saturating_add(RocksDbWeight::get().reads((160_u64).saturating_mul(r.into()))) .saturating_add(RocksDbWeight::get().writes(3_u64)) .saturating_add(RocksDbWeight::get().writes((160_u64).saturating_mul(r.into()))) - .saturating_add(Weight::from_parts(0, 511456).saturating_mul(r.into())) + .saturating_add(Weight::from_parts(0, 498656).saturating_mul(r.into())) } /// Storage: System Account (r:1 w:0) /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: Measured) @@ -3346,18 +3184,17 @@ impl WeightInfo for () { /// The range of component `r` is `[0, 20]`. fn seal_delegate_call(r: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `0 + r * (71670 ±0)` - // Estimated: `17285 + r * (659930 ±563)` - // Minimum execution time: 375_412 nanoseconds. - Weight::from_parts(376_493_000, 0) - .saturating_add(Weight::from_parts(0, 17285)) - // Standard Error: 8_239_575 - .saturating_add(Weight::from_parts(28_716_347_183, 0).saturating_mul(r.into())) + // Measured: `0 + r * (69030 ±0)` + // Estimated: `21755 + r * (647542 ±560)` + // Minimum execution time: 297_432_000 picoseconds. + Weight::from_parts(298_220_000, 21755) + // Standard Error: 9_420_834 + .saturating_add(Weight::from_parts(22_261_152_353, 0).saturating_mul(r.into())) .saturating_add(RocksDbWeight::get().reads(6_u64)) .saturating_add(RocksDbWeight::get().reads((150_u64).saturating_mul(r.into()))) .saturating_add(RocksDbWeight::get().writes(3_u64)) .saturating_add(RocksDbWeight::get().writes((75_u64).saturating_mul(r.into()))) - .saturating_add(Weight::from_parts(0, 659930).saturating_mul(r.into())) + .saturating_add(Weight::from_parts(0, 647542).saturating_mul(r.into())) } /// Storage: System Account (r:82 w:81) /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: Measured) @@ -3373,20 +3210,19 @@ impl WeightInfo for () { /// The range of component `c` is `[0, 1024]`. fn seal_call_per_transfer_clone_kb(t: u32, c: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `24269 + t * (16910 ±0)` - // Estimated: `532690 + t * (285025 ±0)` - // Minimum execution time: 10_443_315 nanoseconds. - Weight::from_parts(9_342_574_069, 0) - .saturating_add(Weight::from_parts(0, 532690)) - // Standard Error: 7_237_279 - .saturating_add(Weight::from_parts(1_390_221_936, 0).saturating_mul(t.into())) - // Standard Error: 10_851 - .saturating_add(Weight::from_parts(9_842_151, 0).saturating_mul(c.into())) + // Measured: `21581 + t * (14318 ±0)` + // Estimated: `524200 + t * (272065 ±0)` + // Minimum execution time: 11_799_802_000 picoseconds. + Weight::from_parts(10_575_328_547, 524200) + // Standard Error: 11_952_810 + .saturating_add(Weight::from_parts(1_603_077_083, 0).saturating_mul(t.into())) + // Standard Error: 17_922 + .saturating_add(Weight::from_parts(9_876_752, 0).saturating_mul(c.into())) .saturating_add(RocksDbWeight::get().reads(167_u64)) .saturating_add(RocksDbWeight::get().reads((81_u64).saturating_mul(t.into()))) .saturating_add(RocksDbWeight::get().writes(163_u64)) .saturating_add(RocksDbWeight::get().writes((81_u64).saturating_mul(t.into()))) - .saturating_add(Weight::from_parts(0, 285025).saturating_mul(t.into())) + .saturating_add(Weight::from_parts(0, 272065).saturating_mul(t.into())) } /// Storage: System Account (r:3202 w:3202) /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: Measured) @@ -3405,18 +3241,17 @@ impl WeightInfo for () { /// The range of component `r` is `[0, 20]`. fn seal_instantiate(r: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `1775 + r * (25568 ±0)` - // Estimated: `26563 + r * (1367114 ±2)` - // Minimum execution time: 376_418 nanoseconds. - Weight::from_parts(377_292_000, 0) - .saturating_add(Weight::from_parts(0, 26563)) - // Standard Error: 32_312_545 - .saturating_add(Weight::from_parts(35_904_826_312, 0).saturating_mul(r.into())) + // Measured: `1646 + r * (20288 ±0)` + // Estimated: `32625 + r * (1330150 ±2)` + // Minimum execution time: 298_155_000 picoseconds. + Weight::from_parts(299_212_000, 32625) + // Standard Error: 32_410_313 + .saturating_add(Weight::from_parts(30_417_025_178, 0).saturating_mul(r.into())) .saturating_add(RocksDbWeight::get().reads(8_u64)) .saturating_add(RocksDbWeight::get().reads((480_u64).saturating_mul(r.into()))) .saturating_add(RocksDbWeight::get().writes(5_u64)) .saturating_add(RocksDbWeight::get().writes((400_u64).saturating_mul(r.into()))) - .saturating_add(Weight::from_parts(0, 1367114).saturating_mul(r.into())) + .saturating_add(Weight::from_parts(0, 1330150).saturating_mul(r.into())) } /// Storage: System Account (r:162 w:162) /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: Measured) @@ -3437,22 +3272,21 @@ impl WeightInfo for () { /// The range of component `s` is `[0, 960]`. fn seal_instantiate_per_transfer_input_salt_kb(t: u32, i: u32, s: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `5785 + t * (33 ±0)` - // Estimated: `850985 + t * (2671 ±3)` - // Minimum execution time: 132_157_340 nanoseconds. - Weight::from_parts(11_329_968_948, 0) - .saturating_add(Weight::from_parts(0, 850985)) - // Standard Error: 99_102_968 - .saturating_add(Weight::from_parts(84_719_458, 0).saturating_mul(t.into())) - // Standard Error: 161_609 - .saturating_add(Weight::from_parts(126_156_627, 0).saturating_mul(i.into())) - // Standard Error: 161_609 - .saturating_add(Weight::from_parts(126_628_313, 0).saturating_mul(s.into())) + // Measured: `5625 + t * (1 ±0)` + // Estimated: `856795 + t * (2471 ±3)` + // Minimum execution time: 105_914_928_000 picoseconds. + Weight::from_parts(13_229_760_432, 856795) + // Standard Error: 96_394_437 + .saturating_add(Weight::from_parts(398_413_888, 0).saturating_mul(t.into())) + // Standard Error: 157_192 + .saturating_add(Weight::from_parts(97_104_978, 0).saturating_mul(i.into())) + // Standard Error: 157_192 + .saturating_add(Weight::from_parts(97_146_130, 0).saturating_mul(s.into())) .saturating_add(RocksDbWeight::get().reads(329_u64)) .saturating_add(RocksDbWeight::get().reads((1_u64).saturating_mul(t.into()))) .saturating_add(RocksDbWeight::get().writes(326_u64)) .saturating_add(RocksDbWeight::get().writes((1_u64).saturating_mul(t.into()))) - .saturating_add(Weight::from_parts(0, 2671).saturating_mul(t.into())) + .saturating_add(Weight::from_parts(0, 2471).saturating_mul(t.into())) } /// Storage: System Account (r:1 w:0) /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: Measured) @@ -3467,13 +3301,12 @@ impl WeightInfo for () { /// The range of component `r` is `[0, 1]`. fn seal_hash_sha2_256(r: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `871 + r * (642 ±0)` - // Estimated: `17225 + r * (3210 ±0)` - // Minimum execution time: 373_559 nanoseconds. - Weight::from_parts(375_166_904, 0) - .saturating_add(Weight::from_parts(0, 17225)) - // Standard Error: 125_024 - .saturating_add(Weight::from_parts(42_291_595, 0).saturating_mul(r.into())) + // Measured: `775 + r * (642 ±0)` + // Estimated: `21695 + r * (3210 ±0)` + // Minimum execution time: 294_309_000 picoseconds. + Weight::from_parts(296_241_318, 21695) + // Standard Error: 127_872 + .saturating_add(Weight::from_parts(45_806_281, 0).saturating_mul(r.into())) .saturating_add(RocksDbWeight::get().reads(6_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) .saturating_add(Weight::from_parts(0, 3210).saturating_mul(r.into())) @@ -3491,13 +3324,12 @@ impl WeightInfo for () { /// The range of component `n` is `[0, 1024]`. fn seal_hash_sha2_256_per_kb(n: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `1673` - // Estimated: `21160` - // Minimum execution time: 416_233 nanoseconds. - Weight::from_parts(416_785_000, 0) - .saturating_add(Weight::from_parts(0, 21160)) - // Standard Error: 56_223 - .saturating_add(Weight::from_parts(324_513_835, 0).saturating_mul(n.into())) + // Measured: `1577` + // Estimated: `25630` + // Minimum execution time: 341_248_000 picoseconds. + Weight::from_parts(341_607_000, 25630) + // Standard Error: 66_687 + .saturating_add(Weight::from_parts(322_250_398, 0).saturating_mul(n.into())) .saturating_add(RocksDbWeight::get().reads(6_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) } @@ -3514,13 +3346,12 @@ impl WeightInfo for () { /// The range of component `r` is `[0, 1]`. fn seal_hash_keccak_256(r: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `873 + r * (642 ±0)` - // Estimated: `17235 + r * (3210 ±0)` - // Minimum execution time: 371_735 nanoseconds. - Weight::from_parts(375_979_430, 0) - .saturating_add(Weight::from_parts(0, 17235)) - // Standard Error: 968_037 - .saturating_add(Weight::from_parts(57_780_769, 0).saturating_mul(r.into())) + // Measured: `777 + r * (642 ±0)` + // Estimated: `21705 + r * (3210 ±0)` + // Minimum execution time: 293_810_000 picoseconds. + Weight::from_parts(296_719_171, 21705) + // Standard Error: 450_807 + .saturating_add(Weight::from_parts(61_627_228, 0).saturating_mul(r.into())) .saturating_add(RocksDbWeight::get().reads(6_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) .saturating_add(Weight::from_parts(0, 3210).saturating_mul(r.into())) @@ -3538,13 +3369,12 @@ impl WeightInfo for () { /// The range of component `n` is `[0, 1024]`. fn seal_hash_keccak_256_per_kb(n: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `1675` - // Estimated: `21205` - // Minimum execution time: 428_196 nanoseconds. - Weight::from_parts(429_438_000, 0) - .saturating_add(Weight::from_parts(0, 21205)) - // Standard Error: 57_860 - .saturating_add(Weight::from_parts(260_917_896, 0).saturating_mul(n.into())) + // Measured: `1579` + // Estimated: `25675` + // Minimum execution time: 354_550_000 picoseconds. + Weight::from_parts(355_136_000, 25675) + // Standard Error: 61_761 + .saturating_add(Weight::from_parts(257_021_566, 0).saturating_mul(n.into())) .saturating_add(RocksDbWeight::get().reads(6_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) } @@ -3561,13 +3391,12 @@ impl WeightInfo for () { /// The range of component `r` is `[0, 1]`. fn seal_hash_blake2_256(r: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `873 + r * (642 ±0)` - // Estimated: `17235 + r * (3210 ±0)` - // Minimum execution time: 371_412 nanoseconds. - Weight::from_parts(373_635_818, 0) - .saturating_add(Weight::from_parts(0, 17235)) - // Standard Error: 222_973 - .saturating_add(Weight::from_parts(33_347_181, 0).saturating_mul(r.into())) + // Measured: `777 + r * (642 ±0)` + // Estimated: `21705 + r * (3210 ±0)` + // Minimum execution time: 293_369_000 picoseconds. + Weight::from_parts(295_990_893, 21705) + // Standard Error: 431_269 + .saturating_add(Weight::from_parts(32_213_406, 0).saturating_mul(r.into())) .saturating_add(RocksDbWeight::get().reads(6_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) .saturating_add(Weight::from_parts(0, 3210).saturating_mul(r.into())) @@ -3585,13 +3414,12 @@ impl WeightInfo for () { /// The range of component `n` is `[0, 1024]`. fn seal_hash_blake2_256_per_kb(n: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `1675` - // Estimated: `21180` - // Minimum execution time: 405_858 nanoseconds. - Weight::from_parts(406_498_000, 0) - .saturating_add(Weight::from_parts(0, 21180)) - // Standard Error: 48_388 - .saturating_add(Weight::from_parts(103_283_157, 0).saturating_mul(n.into())) + // Measured: `1579` + // Estimated: `25650` + // Minimum execution time: 326_595_000 picoseconds. + Weight::from_parts(327_188_000, 25650) + // Standard Error: 53_385 + .saturating_add(Weight::from_parts(74_046_074, 0).saturating_mul(n.into())) .saturating_add(RocksDbWeight::get().reads(6_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) } @@ -3608,13 +3436,12 @@ impl WeightInfo for () { /// The range of component `r` is `[0, 1]`. fn seal_hash_blake2_128(r: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `873 + r * (679 ±0)` - // Estimated: `17235 + r * (3395 ±0)` - // Minimum execution time: 371_746 nanoseconds. - Weight::from_parts(373_538_171, 0) - .saturating_add(Weight::from_parts(0, 17235)) - // Standard Error: 387_332 - .saturating_add(Weight::from_parts(35_933_528, 0).saturating_mul(r.into())) + // Measured: `777 + r * (679 ±0)` + // Estimated: `21705 + r * (3395 ±0)` + // Minimum execution time: 292_388_000 picoseconds. + Weight::from_parts(295_090_179, 21705) + // Standard Error: 170_487 + .saturating_add(Weight::from_parts(32_577_820, 0).saturating_mul(r.into())) .saturating_add(RocksDbWeight::get().reads(6_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) .saturating_add(Weight::from_parts(0, 3395).saturating_mul(r.into())) @@ -3632,13 +3459,12 @@ impl WeightInfo for () { /// The range of component `n` is `[0, 1024]`. fn seal_hash_blake2_128_per_kb(n: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `1675` - // Estimated: `21225` - // Minimum execution time: 405_752 nanoseconds. - Weight::from_parts(406_417_000, 0) - .saturating_add(Weight::from_parts(0, 21225)) - // Standard Error: 47_051 - .saturating_add(Weight::from_parts(103_325_027, 0).saturating_mul(n.into())) + // Measured: `1579` + // Estimated: `25695` + // Minimum execution time: 326_862_000 picoseconds. + Weight::from_parts(327_449_000, 25695) + // Standard Error: 54_539 + .saturating_add(Weight::from_parts(74_042_337, 0).saturating_mul(n.into())) .saturating_add(RocksDbWeight::get().reads(6_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) } @@ -3655,13 +3481,12 @@ impl WeightInfo for () { /// The range of component `r` is `[0, 1]`. fn seal_ecdsa_recover(r: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `917 + r * (6083 ±0)` - // Estimated: `17455 + r * (30415 ±0)` - // Minimum execution time: 373_882 nanoseconds. - Weight::from_parts(376_553_787, 0) - .saturating_add(Weight::from_parts(0, 17455)) - // Standard Error: 912_833 - .saturating_add(Weight::from_parts(3_021_100_412, 0).saturating_mul(r.into())) + // Measured: `821 + r * (6083 ±0)` + // Estimated: `21925 + r * (30415 ±0)` + // Minimum execution time: 295_597_000 picoseconds. + Weight::from_parts(298_267_200, 21925) + // Standard Error: 699_824 + .saturating_add(Weight::from_parts(3_021_734_700, 0).saturating_mul(r.into())) .saturating_add(RocksDbWeight::get().reads(6_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) .saturating_add(Weight::from_parts(0, 30415).saturating_mul(r.into())) @@ -3679,13 +3504,12 @@ impl WeightInfo for () { /// The range of component `r` is `[0, 1]`. fn seal_ecdsa_to_eth_address(r: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `886 + r * (3362 ±0)` - // Estimated: `17300 + r * (16810 ±0)` - // Minimum execution time: 373_673 nanoseconds. - Weight::from_parts(375_712_961, 0) - .saturating_add(Weight::from_parts(0, 17300)) - // Standard Error: 596_297 - .saturating_add(Weight::from_parts(738_257_638, 0).saturating_mul(r.into())) + // Measured: `790 + r * (3362 ±0)` + // Estimated: `21770 + r * (16810 ±0)` + // Minimum execution time: 295_539_000 picoseconds. + Weight::from_parts(297_844_822, 21770) + // Standard Error: 490_532 + .saturating_add(Weight::from_parts(747_592_977, 0).saturating_mul(r.into())) .saturating_add(RocksDbWeight::get().reads(6_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) .saturating_add(Weight::from_parts(0, 16810).saturating_mul(r.into())) @@ -3705,18 +3529,17 @@ impl WeightInfo for () { /// The range of component `r` is `[0, 20]`. fn seal_set_code_hash(r: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `0 + r * (79300 ±0)` - // Estimated: `64844 + r * (942952 ±833)` - // Minimum execution time: 374_097 nanoseconds. - Weight::from_parts(374_985_000, 0) - .saturating_add(Weight::from_parts(0, 64844)) - // Standard Error: 3_772_336 - .saturating_add(Weight::from_parts(1_546_402_854, 0).saturating_mul(r.into())) + // Measured: `0 + r * (74020 ±0)` + // Estimated: `69192 + r * (913289 ±821)` + // Minimum execution time: 295_164_000 picoseconds. + Weight::from_parts(296_597_000, 69192) + // Standard Error: 3_646_577 + .saturating_add(Weight::from_parts(1_725_796_807, 0).saturating_mul(r.into())) .saturating_add(RocksDbWeight::get().reads(6_u64)) .saturating_add(RocksDbWeight::get().reads((225_u64).saturating_mul(r.into()))) .saturating_add(RocksDbWeight::get().writes(3_u64)) .saturating_add(RocksDbWeight::get().writes((150_u64).saturating_mul(r.into()))) - .saturating_add(Weight::from_parts(0, 942952).saturating_mul(r.into())) + .saturating_add(Weight::from_parts(0, 913289).saturating_mul(r.into())) } /// Storage: System Account (r:1 w:0) /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: Measured) @@ -3731,13 +3554,12 @@ impl WeightInfo for () { /// The range of component `r` is `[0, 20]`. fn seal_reentrance_count(r: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `869 + r * (240 ±0)` - // Estimated: `17215 + r * (1200 ±0)` - // Minimum execution time: 374_249 nanoseconds. - Weight::from_parts(377_990_998, 0) - .saturating_add(Weight::from_parts(0, 17215)) - // Standard Error: 38_133 - .saturating_add(Weight::from_parts(11_483_273, 0).saturating_mul(r.into())) + // Measured: `773 + r * (240 ±0)` + // Estimated: `21685 + r * (1200 ±0)` + // Minimum execution time: 296_287_000 picoseconds. + Weight::from_parts(300_696_694, 21685) + // Standard Error: 27_891 + .saturating_add(Weight::from_parts(10_943_994, 0).saturating_mul(r.into())) .saturating_add(RocksDbWeight::get().reads(6_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) .saturating_add(Weight::from_parts(0, 1200).saturating_mul(r.into())) @@ -3755,13 +3577,12 @@ impl WeightInfo for () { /// The range of component `r` is `[0, 20]`. fn seal_account_reentrance_count(r: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `2102 + r * (3154 ±0)` - // Estimated: `21980 + r * (15875 ±2)` - // Minimum execution time: 375_552 nanoseconds. - Weight::from_parts(400_624_032, 0) - .saturating_add(Weight::from_parts(0, 21980)) - // Standard Error: 82_523 - .saturating_add(Weight::from_parts(18_057_327, 0).saturating_mul(r.into())) + // Measured: `2006 + r * (3154 ±0)` + // Estimated: `26435 + r * (15875 ±2)` + // Minimum execution time: 298_000_000 picoseconds. + Weight::from_parts(328_282_119, 26435) + // Standard Error: 104_874 + .saturating_add(Weight::from_parts(18_428_267, 0).saturating_mul(r.into())) .saturating_add(RocksDbWeight::get().reads(6_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) .saturating_add(Weight::from_parts(0, 15875).saturating_mul(r.into())) @@ -3781,13 +3602,12 @@ impl WeightInfo for () { /// The range of component `r` is `[0, 20]`. fn seal_instantiation_nonce(r: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `872 + r * (240 ±0)` - // Estimated: `18598 + r * (1440 ±0)` - // Minimum execution time: 373_899 nanoseconds. - Weight::from_parts(379_733_943, 0) - .saturating_add(Weight::from_parts(0, 18598)) - // Standard Error: 32_022 - .saturating_add(Weight::from_parts(9_381_180, 0).saturating_mul(r.into())) + // Measured: `776 + r * (240 ±0)` + // Estimated: `23962 + r * (1440 ±0)` + // Minimum execution time: 295_104_000 picoseconds. + Weight::from_parts(299_334_585, 23962) + // Standard Error: 25_840 + .saturating_add(Weight::from_parts(9_744_866, 0).saturating_mul(r.into())) .saturating_add(RocksDbWeight::get().reads(7_u64)) .saturating_add(RocksDbWeight::get().writes(4_u64)) .saturating_add(Weight::from_parts(0, 1440).saturating_mul(r.into())) @@ -3797,571 +3617,519 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 834 nanoseconds. - Weight::from_parts(1_009_646, 0) - .saturating_add(Weight::from_parts(0, 0)) - // Standard Error: 388 - .saturating_add(Weight::from_parts(411_979, 0).saturating_mul(r.into())) + // Minimum execution time: 1_132_000 picoseconds. + Weight::from_parts(1_100_159, 0) + // Standard Error: 10_131 + .saturating_add(Weight::from_parts(436_462, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 50]`. fn instr_i64load(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 882 nanoseconds. - Weight::from_parts(1_416_377, 0) - .saturating_add(Weight::from_parts(0, 0)) - // Standard Error: 1_133 - .saturating_add(Weight::from_parts(1_075_838, 0).saturating_mul(r.into())) + // Minimum execution time: 1_368_000 picoseconds. + Weight::from_parts(1_974_308, 0) + // Standard Error: 811 + .saturating_add(Weight::from_parts(1_079_620, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 50]`. fn instr_i64store(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 878 nanoseconds. - Weight::from_parts(1_343_056, 0) - .saturating_add(Weight::from_parts(0, 0)) - // Standard Error: 426 - .saturating_add(Weight::from_parts(1_001_214, 0).saturating_mul(r.into())) + // Minimum execution time: 1_354_000 picoseconds. + Weight::from_parts(2_057_834, 0) + // Standard Error: 4_277 + .saturating_add(Weight::from_parts(1_008_797, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 50]`. fn instr_select(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 796 nanoseconds. - Weight::from_parts(1_079_086, 0) - .saturating_add(Weight::from_parts(0, 0)) - // Standard Error: 409 - .saturating_add(Weight::from_parts(1_149_188, 0).saturating_mul(r.into())) + // Minimum execution time: 1_218_000 picoseconds. + Weight::from_parts(1_594_528, 0) + // Standard Error: 723 + .saturating_add(Weight::from_parts(1_147_486, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 50]`. fn instr_if(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 800 nanoseconds. - Weight::from_parts(1_044_184, 0) - .saturating_add(Weight::from_parts(0, 0)) - // Standard Error: 707 - .saturating_add(Weight::from_parts(1_315_686, 0).saturating_mul(r.into())) + // Minimum execution time: 1_156_000 picoseconds. + Weight::from_parts(1_460_611, 0) + // Standard Error: 419 + .saturating_add(Weight::from_parts(1_314_741, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 50]`. fn instr_br(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 807 nanoseconds. - Weight::from_parts(1_049_633, 0) - .saturating_add(Weight::from_parts(0, 0)) - // Standard Error: 361 - .saturating_add(Weight::from_parts(640_530, 0).saturating_mul(r.into())) + // Minimum execution time: 1_166_000 picoseconds. + Weight::from_parts(1_454_460, 0) + // Standard Error: 493 + .saturating_add(Weight::from_parts(642_481, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 50]`. fn instr_br_if(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 774 nanoseconds. - Weight::from_parts(1_124_053, 0) - .saturating_add(Weight::from_parts(0, 0)) - // Standard Error: 784 - .saturating_add(Weight::from_parts(949_398, 0).saturating_mul(r.into())) + // Minimum execution time: 1_144_000 picoseconds. + Weight::from_parts(1_410_515, 0) + // Standard Error: 894 + .saturating_add(Weight::from_parts(958_765, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 50]`. fn instr_br_table(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 810 nanoseconds. - Weight::from_parts(676_581, 0) - .saturating_add(Weight::from_parts(0, 0)) - // Standard Error: 2_356 - .saturating_add(Weight::from_parts(1_163_465, 0).saturating_mul(r.into())) + // Minimum execution time: 1_190_000 picoseconds. + Weight::from_parts(1_143_236, 0) + // Standard Error: 2_773 + .saturating_add(Weight::from_parts(1_164_764, 0).saturating_mul(r.into())) } /// The range of component `e` is `[1, 256]`. fn instr_br_table_per_entry(e: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 2_580 nanoseconds. - Weight::from_parts(2_835_656, 0) - .saturating_add(Weight::from_parts(0, 0)) - // Standard Error: 71 - .saturating_add(Weight::from_parts(4_686, 0).saturating_mul(e.into())) + // Minimum execution time: 2_993_000 picoseconds. + Weight::from_parts(3_270_525, 0) + // Standard Error: 65 + .saturating_add(Weight::from_parts(4_418, 0).saturating_mul(e.into())) } /// The range of component `r` is `[0, 50]`. fn instr_call(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 826 nanoseconds. - Weight::from_parts(1_625_698, 0) - .saturating_add(Weight::from_parts(0, 0)) - // Standard Error: 1_740 - .saturating_add(Weight::from_parts(2_332_187, 0).saturating_mul(r.into())) + // Minimum execution time: 1_163_000 picoseconds. + Weight::from_parts(1_999_458, 0) + // Standard Error: 1_418 + .saturating_add(Weight::from_parts(2_371_979, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 50]`. fn instr_call_indirect(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 901 nanoseconds. - Weight::from_parts(2_338_620, 0) - .saturating_add(Weight::from_parts(0, 0)) - // Standard Error: 1_642 - .saturating_add(Weight::from_parts(2_924_090, 0).saturating_mul(r.into())) + // Minimum execution time: 1_288_000 picoseconds. + Weight::from_parts(2_340_227, 0) + // Standard Error: 3_234 + .saturating_add(Weight::from_parts(3_038_523, 0).saturating_mul(r.into())) } /// The range of component `p` is `[0, 128]`. fn instr_call_indirect_per_param(p: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 4_670 nanoseconds. - Weight::from_parts(5_556_246, 0) - .saturating_add(Weight::from_parts(0, 0)) - // Standard Error: 1_491 - .saturating_add(Weight::from_parts(228_965, 0).saturating_mul(p.into())) + // Minimum execution time: 5_270_000 picoseconds. + Weight::from_parts(6_360_011, 0) + // Standard Error: 2_585 + .saturating_add(Weight::from_parts(226_149, 0).saturating_mul(p.into())) } /// The range of component `l` is `[0, 1024]`. fn instr_call_per_local(l: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 3_099 nanoseconds. - Weight::from_parts(3_896_177, 0) - .saturating_add(Weight::from_parts(0, 0)) - // Standard Error: 99 - .saturating_add(Weight::from_parts(91_304, 0).saturating_mul(l.into())) + // Minimum execution time: 3_580_000 picoseconds. + Weight::from_parts(5_183_658, 0) + // Standard Error: 80 + .saturating_add(Weight::from_parts(46_038, 0).saturating_mul(l.into())) } /// The range of component `r` is `[0, 50]`. fn instr_local_get(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 3_042 nanoseconds. - Weight::from_parts(3_334_621, 0) - .saturating_add(Weight::from_parts(0, 0)) - // Standard Error: 793 - .saturating_add(Weight::from_parts(459_346, 0).saturating_mul(r.into())) + // Minimum execution time: 2_559_000 picoseconds. + Weight::from_parts(2_846_350, 0) + // Standard Error: 2_809 + .saturating_add(Weight::from_parts(462_084, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 50]`. fn instr_local_set(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 2_968 nanoseconds. - Weight::from_parts(3_235_286, 0) - .saturating_add(Weight::from_parts(0, 0)) - // Standard Error: 427 - .saturating_add(Weight::from_parts(485_454, 0).saturating_mul(r.into())) + // Minimum execution time: 2_530_000 picoseconds. + Weight::from_parts(2_835_072, 0) + // Standard Error: 479 + .saturating_add(Weight::from_parts(483_451, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 50]`. fn instr_local_tee(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 3_012 nanoseconds. - Weight::from_parts(3_303_555, 0) - .saturating_add(Weight::from_parts(0, 0)) - // Standard Error: 371 - .saturating_add(Weight::from_parts(657_811, 0).saturating_mul(r.into())) + // Minimum execution time: 2_510_000 picoseconds. + Weight::from_parts(2_740_081, 0) + // Standard Error: 901 + .saturating_add(Weight::from_parts(664_354, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 50]`. fn instr_global_get(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 865 nanoseconds. - Weight::from_parts(1_249_987, 0) - .saturating_add(Weight::from_parts(0, 0)) - // Standard Error: 417 - .saturating_add(Weight::from_parts(896_704, 0).saturating_mul(r.into())) + // Minimum execution time: 1_295_000 picoseconds. + Weight::from_parts(1_798_174, 0) + // Standard Error: 464 + .saturating_add(Weight::from_parts(896_289, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 50]`. fn instr_global_set(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 866 nanoseconds. - Weight::from_parts(1_216_218, 0) - .saturating_add(Weight::from_parts(0, 0)) - // Standard Error: 503 - .saturating_add(Weight::from_parts(919_719, 0).saturating_mul(r.into())) + // Minimum execution time: 1_287_000 picoseconds. + Weight::from_parts(1_636_548, 0) + // Standard Error: 658 + .saturating_add(Weight::from_parts(923_295, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 50]`. fn instr_memory_current(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 921 nanoseconds. - Weight::from_parts(1_228_408, 0) - .saturating_add(Weight::from_parts(0, 0)) - // Standard Error: 309 - .saturating_add(Weight::from_parts(813_007, 0).saturating_mul(r.into())) + // Minimum execution time: 1_353_000 picoseconds. + Weight::from_parts(1_672_309, 0) + // Standard Error: 403 + .saturating_add(Weight::from_parts(818_273, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 1]`. fn instr_memory_grow(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 820 nanoseconds. - Weight::from_parts(914_830, 0) - .saturating_add(Weight::from_parts(0, 0)) - // Standard Error: 6_018 - .saturating_add(Weight::from_parts(237_062_769, 0).saturating_mul(r.into())) + // Minimum execution time: 1_180_000 picoseconds. + Weight::from_parts(1_304_714, 0) + // Standard Error: 88_180 + .saturating_add(Weight::from_parts(183_952_985, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 50]`. fn instr_i64clz(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 812 nanoseconds. - Weight::from_parts(1_554_406, 0) - .saturating_add(Weight::from_parts(0, 0)) - // Standard Error: 9_979 - .saturating_add(Weight::from_parts(625_434, 0).saturating_mul(r.into())) + // Minimum execution time: 1_158_000 picoseconds. + Weight::from_parts(1_800_065, 0) + // Standard Error: 2_104 + .saturating_add(Weight::from_parts(625_410, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 50]`. fn instr_i64ctz(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 798 nanoseconds. - Weight::from_parts(1_095_113, 0) - .saturating_add(Weight::from_parts(0, 0)) - // Standard Error: 243 - .saturating_add(Weight::from_parts(634_204, 0).saturating_mul(r.into())) + // Minimum execution time: 1_208_000 picoseconds. + Weight::from_parts(1_483_908, 0) + // Standard Error: 319 + .saturating_add(Weight::from_parts(635_095, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 50]`. fn instr_i64popcnt(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 792 nanoseconds. - Weight::from_parts(1_109_845, 0) - .saturating_add(Weight::from_parts(0, 0)) - // Standard Error: 14_944 - .saturating_add(Weight::from_parts(658_834, 0).saturating_mul(r.into())) + // Minimum execution time: 1_164_000 picoseconds. + Weight::from_parts(1_506_871, 0) + // Standard Error: 264 + .saturating_add(Weight::from_parts(633_660, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 50]`. fn instr_i64eqz(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 815 nanoseconds. - Weight::from_parts(1_068_916, 0) - .saturating_add(Weight::from_parts(0, 0)) - // Standard Error: 327 - .saturating_add(Weight::from_parts(652_897, 0).saturating_mul(r.into())) + // Minimum execution time: 1_141_000 picoseconds. + Weight::from_parts(1_467_219, 0) + // Standard Error: 191 + .saturating_add(Weight::from_parts(651_156, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 50]`. fn instr_i64extendsi32(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 798 nanoseconds. - Weight::from_parts(1_069_745, 0) - .saturating_add(Weight::from_parts(0, 0)) - // Standard Error: 306 - .saturating_add(Weight::from_parts(618_481, 0).saturating_mul(r.into())) + // Minimum execution time: 1_170_000 picoseconds. + Weight::from_parts(1_474_718, 0) + // Standard Error: 224 + .saturating_add(Weight::from_parts(617_317, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 50]`. fn instr_i64extendui32(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 799 nanoseconds. - Weight::from_parts(1_398_001, 0) - .saturating_add(Weight::from_parts(0, 0)) - // Standard Error: 6_234 - .saturating_add(Weight::from_parts(611_399, 0).saturating_mul(r.into())) + // Minimum execution time: 1_195_000 picoseconds. + Weight::from_parts(1_501_421, 0) + // Standard Error: 221 + .saturating_add(Weight::from_parts(616_559, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 50]`. fn instr_i32wrapi64(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 811 nanoseconds. - Weight::from_parts(1_098_083, 0) - .saturating_add(Weight::from_parts(0, 0)) - // Standard Error: 297 - .saturating_add(Weight::from_parts(617_692, 0).saturating_mul(r.into())) + // Minimum execution time: 1_172_000 picoseconds. + Weight::from_parts(1_482_402, 0) + // Standard Error: 2_163 + .saturating_add(Weight::from_parts(619_230, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 50]`. fn instr_i64eq(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 815 nanoseconds. - Weight::from_parts(1_046_922, 0) - .saturating_add(Weight::from_parts(0, 0)) - // Standard Error: 335 - .saturating_add(Weight::from_parts(909_196, 0).saturating_mul(r.into())) + // Minimum execution time: 1_149_000 picoseconds. + Weight::from_parts(2_021_801, 0) + // Standard Error: 20_804 + .saturating_add(Weight::from_parts(913_888, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 50]`. fn instr_i64ne(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 805 nanoseconds. - Weight::from_parts(1_093_667, 0) - .saturating_add(Weight::from_parts(0, 0)) - // Standard Error: 233 - .saturating_add(Weight::from_parts(907_378, 0).saturating_mul(r.into())) + // Minimum execution time: 1_187_000 picoseconds. + Weight::from_parts(1_358_437, 0) + // Standard Error: 1_946 + .saturating_add(Weight::from_parts(916_378, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 50]`. fn instr_i64lts(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 805 nanoseconds. - Weight::from_parts(1_290_591, 0) - .saturating_add(Weight::from_parts(0, 0)) - // Standard Error: 3_201 - .saturating_add(Weight::from_parts(902_006, 0).saturating_mul(r.into())) + // Minimum execution time: 1_175_000 picoseconds. + Weight::from_parts(1_527_336, 0) + // Standard Error: 243 + .saturating_add(Weight::from_parts(907_580, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 50]`. fn instr_i64ltu(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 783 nanoseconds. - Weight::from_parts(1_159_977, 0) - .saturating_add(Weight::from_parts(0, 0)) - // Standard Error: 2_310 - .saturating_add(Weight::from_parts(906_489, 0).saturating_mul(r.into())) + // Minimum execution time: 1_220_000 picoseconds. + Weight::from_parts(1_530_715, 0) + // Standard Error: 224 + .saturating_add(Weight::from_parts(906_096, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 50]`. fn instr_i64gts(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 790 nanoseconds. - Weight::from_parts(1_109_719, 0) - .saturating_add(Weight::from_parts(0, 0)) - // Standard Error: 261 - .saturating_add(Weight::from_parts(906_614, 0).saturating_mul(r.into())) + // Minimum execution time: 1_107_000 picoseconds. + Weight::from_parts(1_496_553, 0) + // Standard Error: 299 + .saturating_add(Weight::from_parts(907_963, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 50]`. fn instr_i64gtu(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 776 nanoseconds. - Weight::from_parts(1_076_567, 0) - .saturating_add(Weight::from_parts(0, 0)) - // Standard Error: 348 - .saturating_add(Weight::from_parts(919_374, 0).saturating_mul(r.into())) + // Minimum execution time: 1_153_000 picoseconds. + Weight::from_parts(1_482_710, 0) + // Standard Error: 182 + .saturating_add(Weight::from_parts(918_439, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 50]`. fn instr_i64les(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 780 nanoseconds. - Weight::from_parts(1_069_663, 0) - .saturating_add(Weight::from_parts(0, 0)) - // Standard Error: 265 - .saturating_add(Weight::from_parts(908_037, 0).saturating_mul(r.into())) + // Minimum execution time: 1_142_000 picoseconds. + Weight::from_parts(1_522_114, 0) + // Standard Error: 297 + .saturating_add(Weight::from_parts(906_633, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 50]`. fn instr_i64leu(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 832 nanoseconds. - Weight::from_parts(930_920, 0) - .saturating_add(Weight::from_parts(0, 0)) - // Standard Error: 2_170 - .saturating_add(Weight::from_parts(929_811, 0).saturating_mul(r.into())) + // Minimum execution time: 1_119_000 picoseconds. + Weight::from_parts(2_199_831, 0) + // Standard Error: 4_234 + .saturating_add(Weight::from_parts(898_161, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 50]`. fn instr_i64ges(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 787 nanoseconds. - Weight::from_parts(1_087_325, 0) - .saturating_add(Weight::from_parts(0, 0)) - // Standard Error: 315 - .saturating_add(Weight::from_parts(908_321, 0).saturating_mul(r.into())) + // Minimum execution time: 1_163_000 picoseconds. + Weight::from_parts(1_489_404, 0) + // Standard Error: 416 + .saturating_add(Weight::from_parts(908_450, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 50]`. fn instr_i64geu(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 798 nanoseconds. - Weight::from_parts(1_029_132, 0) - .saturating_add(Weight::from_parts(0, 0)) - // Standard Error: 2_095 - .saturating_add(Weight::from_parts(913_553, 0).saturating_mul(r.into())) + // Minimum execution time: 1_204_000 picoseconds. + Weight::from_parts(2_266_296, 0) + // Standard Error: 13_013 + .saturating_add(Weight::from_parts(899_225, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 50]`. fn instr_i64add(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 791 nanoseconds. - Weight::from_parts(1_086_314, 0) - .saturating_add(Weight::from_parts(0, 0)) - // Standard Error: 197 - .saturating_add(Weight::from_parts(896_392, 0).saturating_mul(r.into())) + // Minimum execution time: 1_160_000 picoseconds. + Weight::from_parts(1_470_799, 0) + // Standard Error: 422 + .saturating_add(Weight::from_parts(897_325, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 50]`. fn instr_i64sub(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 793 nanoseconds. - Weight::from_parts(1_078_172, 0) - .saturating_add(Weight::from_parts(0, 0)) - // Standard Error: 404 - .saturating_add(Weight::from_parts(886_329, 0).saturating_mul(r.into())) + // Minimum execution time: 1_125_000 picoseconds. + Weight::from_parts(1_521_508, 0) + // Standard Error: 594 + .saturating_add(Weight::from_parts(886_145, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 50]`. fn instr_i64mul(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 799 nanoseconds. - Weight::from_parts(1_095_010, 0) - .saturating_add(Weight::from_parts(0, 0)) - // Standard Error: 431 - .saturating_add(Weight::from_parts(886_513, 0).saturating_mul(r.into())) + // Minimum execution time: 1_139_000 picoseconds. + Weight::from_parts(1_490_535, 0) + // Standard Error: 211 + .saturating_add(Weight::from_parts(884_929, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 50]`. fn instr_i64divs(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 810 nanoseconds. - Weight::from_parts(1_114_325, 0) - .saturating_add(Weight::from_parts(0, 0)) - // Standard Error: 452 - .saturating_add(Weight::from_parts(1_521_849, 0).saturating_mul(r.into())) + // Minimum execution time: 1_180_000 picoseconds. + Weight::from_parts(2_221_481, 0) + // Standard Error: 9_884 + .saturating_add(Weight::from_parts(1_507_511, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 50]`. fn instr_i64divu(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 784 nanoseconds. - Weight::from_parts(1_123_153, 0) - .saturating_add(Weight::from_parts(0, 0)) - // Standard Error: 475 - .saturating_add(Weight::from_parts(1_457_746, 0).saturating_mul(r.into())) + // Minimum execution time: 1_131_000 picoseconds. + Weight::from_parts(1_498_652, 0) + // Standard Error: 531 + .saturating_add(Weight::from_parts(1_451_498, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 50]`. fn instr_i64rems(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 809 nanoseconds. - Weight::from_parts(1_145_906, 0) - .saturating_add(Weight::from_parts(0, 0)) - // Standard Error: 718 - .saturating_add(Weight::from_parts(1_549_964, 0).saturating_mul(r.into())) + // Minimum execution time: 1_145_000 picoseconds. + Weight::from_parts(1_504_118, 0) + // Standard Error: 439 + .saturating_add(Weight::from_parts(1_521_895, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 50]`. fn instr_i64remu(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 803 nanoseconds. - Weight::from_parts(1_110_328, 0) - .saturating_add(Weight::from_parts(0, 0)) - // Standard Error: 627 - .saturating_add(Weight::from_parts(1_453_013, 0).saturating_mul(r.into())) + // Minimum execution time: 1_124_000 picoseconds. + Weight::from_parts(1_497_209, 0) + // Standard Error: 198 + .saturating_add(Weight::from_parts(1_449_843, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 50]`. fn instr_i64and(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 786 nanoseconds. - Weight::from_parts(1_108_792, 0) - .saturating_add(Weight::from_parts(0, 0)) - // Standard Error: 286 - .saturating_add(Weight::from_parts(897_035, 0).saturating_mul(r.into())) + // Minimum execution time: 1_169_000 picoseconds. + Weight::from_parts(1_534_491, 0) + // Standard Error: 283 + .saturating_add(Weight::from_parts(894_703, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 50]`. fn instr_i64or(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 787 nanoseconds. - Weight::from_parts(830_000, 0) - .saturating_add(Weight::from_parts(0, 0)) - // Standard Error: 15_995 - .saturating_add(Weight::from_parts(963_344, 0).saturating_mul(r.into())) + // Minimum execution time: 1_178_000 picoseconds. + Weight::from_parts(1_485_473, 0) + // Standard Error: 552 + .saturating_add(Weight::from_parts(897_878, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 50]`. fn instr_i64xor(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 773 nanoseconds. - Weight::from_parts(1_082_459, 0) - .saturating_add(Weight::from_parts(0, 0)) - // Standard Error: 330 - .saturating_add(Weight::from_parts(897_077, 0).saturating_mul(r.into())) + // Minimum execution time: 1_214_000 picoseconds. + Weight::from_parts(1_580_999, 0) + // Standard Error: 358 + .saturating_add(Weight::from_parts(894_207, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 50]`. fn instr_i64shl(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 810 nanoseconds. - Weight::from_parts(1_325_815, 0) - .saturating_add(Weight::from_parts(0, 0)) - // Standard Error: 3_352 - .saturating_add(Weight::from_parts(899_555, 0).saturating_mul(r.into())) + // Minimum execution time: 1_176_000 picoseconds. + Weight::from_parts(1_490_470, 0) + // Standard Error: 738 + .saturating_add(Weight::from_parts(904_900, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 50]`. fn instr_i64shrs(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 808 nanoseconds. - Weight::from_parts(1_085_903, 0) - .saturating_add(Weight::from_parts(0, 0)) - // Standard Error: 430 - .saturating_add(Weight::from_parts(903_249, 0).saturating_mul(r.into())) + // Minimum execution time: 1_197_000 picoseconds. + Weight::from_parts(1_519_703, 0) + // Standard Error: 336 + .saturating_add(Weight::from_parts(901_277, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 50]`. fn instr_i64shru(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 792 nanoseconds. - Weight::from_parts(1_091_261, 0) - .saturating_add(Weight::from_parts(0, 0)) - // Standard Error: 312 - .saturating_add(Weight::from_parts(902_245, 0).saturating_mul(r.into())) + // Minimum execution time: 1_166_000 picoseconds. + Weight::from_parts(1_507_499, 0) + // Standard Error: 298 + .saturating_add(Weight::from_parts(901_153, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 50]`. fn instr_i64rotl(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 807 nanoseconds. - Weight::from_parts(1_121_052, 0) - .saturating_add(Weight::from_parts(0, 0)) - // Standard Error: 506 - .saturating_add(Weight::from_parts(902_772, 0).saturating_mul(r.into())) + // Minimum execution time: 1_158_000 picoseconds. + Weight::from_parts(1_491_176, 0) + // Standard Error: 341 + .saturating_add(Weight::from_parts(901_880, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 50]`. fn instr_i64rotr(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 823 nanoseconds. - Weight::from_parts(1_317_597, 0) - .saturating_add(Weight::from_parts(0, 0)) - // Standard Error: 6_219 - .saturating_add(Weight::from_parts(896_692, 0).saturating_mul(r.into())) + // Minimum execution time: 1_207_000 picoseconds. + Weight::from_parts(1_477_261, 0) + // Standard Error: 287 + .saturating_add(Weight::from_parts(902_622, 0).saturating_mul(r.into())) } } diff --git a/frame/conviction-voting/src/weights.rs b/frame/conviction-voting/src/weights.rs index 91c4c0130..765a4276a 100644 --- a/frame/conviction-voting/src/weights.rs +++ b/frame/conviction-voting/src/weights.rs @@ -18,9 +18,9 @@ //! Autogenerated weights for pallet_conviction_voting //! //! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev -//! DATE: 2023-01-24, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! DATE: 2023-03-16, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` //! WORST CASE MAP SIZE: `1000000` -//! HOSTNAME: `bm2`, CPU: `Intel(R) Core(TM) i7-7700K CPU @ 4.20GHz` +//! HOSTNAME: `bm3`, CPU: `Intel(R) Core(TM) i7-7700K CPU @ 4.20GHz` //! EXECUTION: Some(Wasm), WASM-EXECUTION: Compiled, CHAIN: Some("dev"), DB CACHE: 1024 // Executed Command: @@ -31,6 +31,9 @@ // --steps=50 // --repeat=20 // --pallet=pallet_conviction_voting +// --no-storage-info +// --no-median-slopes +// --no-min-squares // --extrinsic=* // --execution=wasm // --wasm-execution=compiled @@ -72,10 +75,10 @@ impl WeightInfo for SubstrateWeight { /// Proof: Scheduler Agenda (max_values: None, max_size: Some(107022), added: 109497, mode: MaxEncodedLen) fn vote_new() -> Weight { // Proof Size summary in bytes: - // Measured: `13168` - // Estimated: `257859` - // Minimum execution time: 85_569 nanoseconds. - Weight::from_parts(86_492_000, 257859) + // Measured: `13074` + // Estimated: `262809` + // Minimum execution time: 94_493_000 picoseconds. + Weight::from_parts(96_510_000, 262809) .saturating_add(T::DbWeight::get().reads(6_u64)) .saturating_add(T::DbWeight::get().writes(6_u64)) } @@ -91,10 +94,10 @@ impl WeightInfo for SubstrateWeight { /// Proof: Scheduler Agenda (max_values: None, max_size: Some(107022), added: 109497, mode: MaxEncodedLen) fn vote_existing() -> Weight { // Proof Size summary in bytes: - // Measured: `20342` - // Estimated: `257859` - // Minimum execution time: 212_727 nanoseconds. - Weight::from_parts(213_741_000, 257859) + // Measured: `20216` + // Estimated: `262809` + // Minimum execution time: 252_717_000 picoseconds. + Weight::from_parts(255_200_000, 262809) .saturating_add(T::DbWeight::get().reads(6_u64)) .saturating_add(T::DbWeight::get().writes(6_u64)) } @@ -106,10 +109,10 @@ impl WeightInfo for SubstrateWeight { /// Proof: Scheduler Agenda (max_values: None, max_size: Some(107022), added: 109497, mode: MaxEncodedLen) fn remove_vote() -> Weight { // Proof Size summary in bytes: - // Measured: `20062` - // Estimated: `251551` - // Minimum execution time: 200_513 nanoseconds. - Weight::from_parts(201_589_000, 251551) + // Measured: `19968` + // Estimated: `254521` + // Minimum execution time: 244_844_000 picoseconds. + Weight::from_parts(247_433_000, 254521) .saturating_add(T::DbWeight::get().reads(4_u64)) .saturating_add(T::DbWeight::get().writes(4_u64)) } @@ -119,10 +122,10 @@ impl WeightInfo for SubstrateWeight { /// Proof: Referenda ReferendumInfoFor (max_values: None, max_size: Some(366), added: 2841, mode: MaxEncodedLen) fn remove_other_vote() -> Weight { // Proof Size summary in bytes: - // Measured: `12738` - // Estimated: `32557` - // Minimum execution time: 43_083 nanoseconds. - Weight::from_parts(43_763_000, 32557) + // Measured: `12675` + // Estimated: `34537` + // Minimum execution time: 46_271_000 picoseconds. + Weight::from_parts(47_639_000, 34537) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -139,17 +142,17 @@ impl WeightInfo for SubstrateWeight { /// The range of component `r` is `[0, 1]`. fn delegate(r: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `272 + r * (1689 ±0)` - // Estimated: `176657 + r * (110917 ±0)` - // Minimum execution time: 33_246 nanoseconds. - Weight::from_parts(34_560_391, 176657) - // Standard Error: 63_925 - .saturating_add(Weight::from_parts(34_500_408, 0).saturating_mul(r.into())) + // Measured: `240 + r * (1627 ±0)` + // Estimated: `180617 + r * (111908 ±0)` + // Minimum execution time: 37_578_000 picoseconds. + Weight::from_parts(39_035_446, 180617) + // Standard Error: 160_560 + .saturating_add(Weight::from_parts(39_642_553, 0).saturating_mul(r.into())) .saturating_add(T::DbWeight::get().reads(4_u64)) .saturating_add(T::DbWeight::get().reads((3_u64).saturating_mul(r.into()))) .saturating_add(T::DbWeight::get().writes(4_u64)) .saturating_add(T::DbWeight::get().writes((3_u64).saturating_mul(r.into()))) - .saturating_add(Weight::from_parts(0, 110917).saturating_mul(r.into())) + .saturating_add(Weight::from_parts(0, 111908).saturating_mul(r.into())) } /// Storage: ConvictionVoting VotingFor (r:2 w:2) /// Proof: ConvictionVoting VotingFor (max_values: None, max_size: Some(27241), added: 29716, mode: MaxEncodedLen) @@ -160,17 +163,17 @@ impl WeightInfo for SubstrateWeight { /// The range of component `r` is `[0, 1]`. fn undelegate(r: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `470 + r * (1407 ±0)` - // Estimated: `170349 + r * (110917 ±0)` - // Minimum execution time: 20_508 nanoseconds. - Weight::from_parts(21_240_024, 170349) - // Standard Error: 37_314 - .saturating_add(Weight::from_parts(30_890_875, 0).saturating_mul(r.into())) + // Measured: `406 + r * (1376 ±0)` + // Estimated: `172329 + r * (111908 ±0)` + // Minimum execution time: 22_997_000 picoseconds. + Weight::from_parts(24_180_222, 172329) + // Standard Error: 87_723 + .saturating_add(Weight::from_parts(36_189_677, 0).saturating_mul(r.into())) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().reads((3_u64).saturating_mul(r.into()))) .saturating_add(T::DbWeight::get().writes(2_u64)) .saturating_add(T::DbWeight::get().writes((3_u64).saturating_mul(r.into()))) - .saturating_add(Weight::from_parts(0, 110917).saturating_mul(r.into())) + .saturating_add(Weight::from_parts(0, 111908).saturating_mul(r.into())) } /// Storage: ConvictionVoting VotingFor (r:1 w:1) /// Proof: ConvictionVoting VotingFor (max_values: None, max_size: Some(27241), added: 29716, mode: MaxEncodedLen) @@ -180,10 +183,10 @@ impl WeightInfo for SubstrateWeight { /// Proof: Balances Locks (max_values: None, max_size: Some(1299), added: 3774, mode: MaxEncodedLen) fn unlock() -> Weight { // Proof Size summary in bytes: - // Measured: `11797` - // Estimated: `36024` - // Minimum execution time: 50_305 nanoseconds. - Weight::from_parts(51_226_000, 36024) + // Measured: `11734` + // Estimated: `38994` + // Minimum execution time: 55_906_000 picoseconds. + Weight::from_parts(56_819_000, 38994) .saturating_add(T::DbWeight::get().reads(3_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) } @@ -203,10 +206,10 @@ impl WeightInfo for () { /// Proof: Scheduler Agenda (max_values: None, max_size: Some(107022), added: 109497, mode: MaxEncodedLen) fn vote_new() -> Weight { // Proof Size summary in bytes: - // Measured: `13168` - // Estimated: `257859` - // Minimum execution time: 85_569 nanoseconds. - Weight::from_parts(86_492_000, 257859) + // Measured: `13074` + // Estimated: `262809` + // Minimum execution time: 94_493_000 picoseconds. + Weight::from_parts(96_510_000, 262809) .saturating_add(RocksDbWeight::get().reads(6_u64)) .saturating_add(RocksDbWeight::get().writes(6_u64)) } @@ -222,10 +225,10 @@ impl WeightInfo for () { /// Proof: Scheduler Agenda (max_values: None, max_size: Some(107022), added: 109497, mode: MaxEncodedLen) fn vote_existing() -> Weight { // Proof Size summary in bytes: - // Measured: `20342` - // Estimated: `257859` - // Minimum execution time: 212_727 nanoseconds. - Weight::from_parts(213_741_000, 257859) + // Measured: `20216` + // Estimated: `262809` + // Minimum execution time: 252_717_000 picoseconds. + Weight::from_parts(255_200_000, 262809) .saturating_add(RocksDbWeight::get().reads(6_u64)) .saturating_add(RocksDbWeight::get().writes(6_u64)) } @@ -237,10 +240,10 @@ impl WeightInfo for () { /// Proof: Scheduler Agenda (max_values: None, max_size: Some(107022), added: 109497, mode: MaxEncodedLen) fn remove_vote() -> Weight { // Proof Size summary in bytes: - // Measured: `20062` - // Estimated: `251551` - // Minimum execution time: 200_513 nanoseconds. - Weight::from_parts(201_589_000, 251551) + // Measured: `19968` + // Estimated: `254521` + // Minimum execution time: 244_844_000 picoseconds. + Weight::from_parts(247_433_000, 254521) .saturating_add(RocksDbWeight::get().reads(4_u64)) .saturating_add(RocksDbWeight::get().writes(4_u64)) } @@ -250,10 +253,10 @@ impl WeightInfo for () { /// Proof: Referenda ReferendumInfoFor (max_values: None, max_size: Some(366), added: 2841, mode: MaxEncodedLen) fn remove_other_vote() -> Weight { // Proof Size summary in bytes: - // Measured: `12738` - // Estimated: `32557` - // Minimum execution time: 43_083 nanoseconds. - Weight::from_parts(43_763_000, 32557) + // Measured: `12675` + // Estimated: `34537` + // Minimum execution time: 46_271_000 picoseconds. + Weight::from_parts(47_639_000, 34537) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } @@ -270,17 +273,17 @@ impl WeightInfo for () { /// The range of component `r` is `[0, 1]`. fn delegate(r: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `272 + r * (1689 ±0)` - // Estimated: `176657 + r * (110917 ±0)` - // Minimum execution time: 33_246 nanoseconds. - Weight::from_parts(34_560_391, 176657) - // Standard Error: 63_925 - .saturating_add(Weight::from_parts(34_500_408, 0).saturating_mul(r.into())) + // Measured: `240 + r * (1627 ±0)` + // Estimated: `180617 + r * (111908 ±0)` + // Minimum execution time: 37_578_000 picoseconds. + Weight::from_parts(39_035_446, 180617) + // Standard Error: 160_560 + .saturating_add(Weight::from_parts(39_642_553, 0).saturating_mul(r.into())) .saturating_add(RocksDbWeight::get().reads(4_u64)) .saturating_add(RocksDbWeight::get().reads((3_u64).saturating_mul(r.into()))) .saturating_add(RocksDbWeight::get().writes(4_u64)) .saturating_add(RocksDbWeight::get().writes((3_u64).saturating_mul(r.into()))) - .saturating_add(Weight::from_parts(0, 110917).saturating_mul(r.into())) + .saturating_add(Weight::from_parts(0, 111908).saturating_mul(r.into())) } /// Storage: ConvictionVoting VotingFor (r:2 w:2) /// Proof: ConvictionVoting VotingFor (max_values: None, max_size: Some(27241), added: 29716, mode: MaxEncodedLen) @@ -291,17 +294,17 @@ impl WeightInfo for () { /// The range of component `r` is `[0, 1]`. fn undelegate(r: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `470 + r * (1407 ±0)` - // Estimated: `170349 + r * (110917 ±0)` - // Minimum execution time: 20_508 nanoseconds. - Weight::from_parts(21_240_024, 170349) - // Standard Error: 37_314 - .saturating_add(Weight::from_parts(30_890_875, 0).saturating_mul(r.into())) + // Measured: `406 + r * (1376 ±0)` + // Estimated: `172329 + r * (111908 ±0)` + // Minimum execution time: 22_997_000 picoseconds. + Weight::from_parts(24_180_222, 172329) + // Standard Error: 87_723 + .saturating_add(Weight::from_parts(36_189_677, 0).saturating_mul(r.into())) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().reads((3_u64).saturating_mul(r.into()))) .saturating_add(RocksDbWeight::get().writes(2_u64)) .saturating_add(RocksDbWeight::get().writes((3_u64).saturating_mul(r.into()))) - .saturating_add(Weight::from_parts(0, 110917).saturating_mul(r.into())) + .saturating_add(Weight::from_parts(0, 111908).saturating_mul(r.into())) } /// Storage: ConvictionVoting VotingFor (r:1 w:1) /// Proof: ConvictionVoting VotingFor (max_values: None, max_size: Some(27241), added: 29716, mode: MaxEncodedLen) @@ -311,10 +314,10 @@ impl WeightInfo for () { /// Proof: Balances Locks (max_values: None, max_size: Some(1299), added: 3774, mode: MaxEncodedLen) fn unlock() -> Weight { // Proof Size summary in bytes: - // Measured: `11797` - // Estimated: `36024` - // Minimum execution time: 50_305 nanoseconds. - Weight::from_parts(51_226_000, 36024) + // Measured: `11734` + // Estimated: `38994` + // Minimum execution time: 55_906_000 picoseconds. + Weight::from_parts(56_819_000, 38994) .saturating_add(RocksDbWeight::get().reads(3_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) } diff --git a/frame/core-fellowship/src/weights.rs b/frame/core-fellowship/src/weights.rs index 9f290635e..d43682666 100644 --- a/frame/core-fellowship/src/weights.rs +++ b/frame/core-fellowship/src/weights.rs @@ -18,26 +18,28 @@ //! Autogenerated weights for pallet_core_fellowship //! //! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev -//! DATE: 2023-03-05, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! DATE: 2023-03-16, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` //! WORST CASE MAP SIZE: `1000000` //! HOSTNAME: `bm3`, CPU: `Intel(R) Core(TM) i7-7700K CPU @ 4.20GHz` //! EXECUTION: Some(Wasm), WASM-EXECUTION: Compiled, CHAIN: Some("dev"), DB CACHE: 1024 // Executed Command: -// target/production/substrate +// ./target/production/substrate // benchmark // pallet +// --chain=dev // --steps=50 // --repeat=20 +// --pallet=pallet_core_fellowship +// --no-storage-info +// --no-median-slopes +// --no-min-squares // --extrinsic=* // --execution=wasm // --wasm-execution=compiled // --heap-pages=4096 -// --json-file=/var/lib/gitlab-runner/builds/zyw4fam_/0/parity/mirrors/substrate/.git/.artifacts/bench.json -// --pallet=pallet_core_fellowship -// --chain=dev -// --header=./HEADER-APACHE2 // --output=./frame/core-fellowship/src/weights.rs +// --header=./HEADER-APACHE2 // --template=./.maintain/frame-weight-template.hbs #![cfg_attr(rustfmt, rustfmt_skip)] @@ -70,8 +72,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 10_489_000 picoseconds. - Weight::from_parts(10_873_000, 0) + // Minimum execution time: 10_384_000 picoseconds. + Weight::from_parts(10_787_000, 0) .saturating_add(T::DbWeight::get().writes(1_u64)) } /// Storage: CoreFellowship Member (r:1 w:1) @@ -88,10 +90,10 @@ impl WeightInfo for SubstrateWeight { /// Proof: CoreFellowship MemberEvidence (max_values: None, max_size: Some(16429), added: 18904, mode: MaxEncodedLen) fn bump_offboard() -> Weight { // Proof Size summary in bytes: - // Measured: `16886` + // Measured: `16854` // Estimated: `35762` - // Minimum execution time: 61_794_000 picoseconds. - Weight::from_parts(62_455_000, 35762) + // Minimum execution time: 61_847_000 picoseconds. + Weight::from_parts(62_453_000, 35762) .saturating_add(T::DbWeight::get().reads(6_u64)) .saturating_add(T::DbWeight::get().writes(4_u64)) } @@ -109,10 +111,10 @@ impl WeightInfo for SubstrateWeight { /// Proof: CoreFellowship MemberEvidence (max_values: None, max_size: Some(16429), added: 18904, mode: MaxEncodedLen) fn bump_demote() -> Weight { // Proof Size summary in bytes: - // Measured: `16996` + // Measured: `16964` // Estimated: `35762` - // Minimum execution time: 63_921_000 picoseconds. - Weight::from_parts(64_871_000, 35762) + // Minimum execution time: 64_449_000 picoseconds. + Weight::from_parts(65_298_000, 35762) .saturating_add(T::DbWeight::get().reads(6_u64)) .saturating_add(T::DbWeight::get().writes(4_u64)) } @@ -124,8 +126,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `355` // Estimated: `7021` - // Minimum execution time: 19_023_000 picoseconds. - Weight::from_parts(19_270_000, 7021) + // Minimum execution time: 19_308_000 picoseconds. + Weight::from_parts(19_520_000, 7021) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -143,8 +145,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `113` // Estimated: `10500` - // Minimum execution time: 29_089_000 picoseconds. - Weight::from_parts(29_718_000, 10500) + // Minimum execution time: 29_228_000 picoseconds. + Weight::from_parts(29_683_000, 10500) .saturating_add(T::DbWeight::get().reads(3_u64)) .saturating_add(T::DbWeight::get().writes(5_u64)) } @@ -164,10 +166,10 @@ impl WeightInfo for SubstrateWeight { /// Proof: RankedCollective IdToIndex (max_values: None, max_size: Some(54), added: 2529, mode: MaxEncodedLen) fn promote() -> Weight { // Proof Size summary in bytes: - // Measured: `16864` + // Measured: `16832` // Estimated: `32243` - // Minimum execution time: 59_529_000 picoseconds. - Weight::from_parts(60_057_000, 32243) + // Minimum execution time: 59_745_000 picoseconds. + Weight::from_parts(60_290_000, 32243) .saturating_add(T::DbWeight::get().reads(5_u64)) .saturating_add(T::DbWeight::get().writes(6_u64)) } @@ -175,14 +177,16 @@ impl WeightInfo for SubstrateWeight { /// Proof: RankedCollective Members (max_values: None, max_size: Some(42), added: 2517, mode: MaxEncodedLen) /// Storage: CoreFellowship Member (r:1 w:1) /// Proof: CoreFellowship Member (max_values: None, max_size: Some(49), added: 2524, mode: MaxEncodedLen) + /// Storage: CoreFellowship MemberEvidence (r:0 w:1) + /// Proof: CoreFellowship MemberEvidence (max_values: None, max_size: Some(16429), added: 18904, mode: MaxEncodedLen) fn offboard() -> Weight { // Proof Size summary in bytes: - // Measured: `292` + // Measured: `326` // Estimated: `7021` - // Minimum execution time: 17_221_000 picoseconds. - Weight::from_parts(17_585_000, 7021) + // Minimum execution time: 18_964_000 picoseconds. + Weight::from_parts(19_603_000, 7021) .saturating_add(T::DbWeight::get().reads(2_u64)) - .saturating_add(T::DbWeight::get().writes(1_u64)) + .saturating_add(T::DbWeight::get().writes(2_u64)) } /// Storage: CoreFellowship Member (r:1 w:1) /// Proof: CoreFellowship Member (max_values: None, max_size: Some(49), added: 2524, mode: MaxEncodedLen) @@ -192,8 +196,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `280` // Estimated: `7021` - // Minimum execution time: 18_602_000 picoseconds. - Weight::from_parts(18_813_000, 7021) + // Minimum execution time: 18_752_000 picoseconds. + Weight::from_parts(19_035_000, 7021) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -205,10 +209,10 @@ impl WeightInfo for SubstrateWeight { /// Proof: CoreFellowship MemberEvidence (max_values: None, max_size: Some(16429), added: 18904, mode: MaxEncodedLen) fn approve() -> Weight { // Proof Size summary in bytes: - // Measured: `16842` + // Measured: `16810` // Estimated: `26915` - // Minimum execution time: 43_525_000 picoseconds. - Weight::from_parts(43_994_000, 26915) + // Minimum execution time: 44_972_000 picoseconds. + Weight::from_parts(45_391_000, 26915) .saturating_add(T::DbWeight::get().reads(3_u64)) .saturating_add(T::DbWeight::get().writes(2_u64)) } @@ -220,8 +224,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `79` // Estimated: `23408` - // Minimum execution time: 27_960_000 picoseconds. - Weight::from_parts(28_331_000, 23408) + // Minimum execution time: 27_820_000 picoseconds. + Weight::from_parts(28_992_000, 23408) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -235,8 +239,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 10_489_000 picoseconds. - Weight::from_parts(10_873_000, 0) + // Minimum execution time: 10_384_000 picoseconds. + Weight::from_parts(10_787_000, 0) .saturating_add(RocksDbWeight::get().writes(1_u64)) } /// Storage: CoreFellowship Member (r:1 w:1) @@ -253,10 +257,10 @@ impl WeightInfo for () { /// Proof: CoreFellowship MemberEvidence (max_values: None, max_size: Some(16429), added: 18904, mode: MaxEncodedLen) fn bump_offboard() -> Weight { // Proof Size summary in bytes: - // Measured: `16886` + // Measured: `16854` // Estimated: `35762` - // Minimum execution time: 61_794_000 picoseconds. - Weight::from_parts(62_455_000, 35762) + // Minimum execution time: 61_847_000 picoseconds. + Weight::from_parts(62_453_000, 35762) .saturating_add(RocksDbWeight::get().reads(6_u64)) .saturating_add(RocksDbWeight::get().writes(4_u64)) } @@ -274,10 +278,10 @@ impl WeightInfo for () { /// Proof: CoreFellowship MemberEvidence (max_values: None, max_size: Some(16429), added: 18904, mode: MaxEncodedLen) fn bump_demote() -> Weight { // Proof Size summary in bytes: - // Measured: `16996` + // Measured: `16964` // Estimated: `35762` - // Minimum execution time: 63_921_000 picoseconds. - Weight::from_parts(64_871_000, 35762) + // Minimum execution time: 64_449_000 picoseconds. + Weight::from_parts(65_298_000, 35762) .saturating_add(RocksDbWeight::get().reads(6_u64)) .saturating_add(RocksDbWeight::get().writes(4_u64)) } @@ -289,8 +293,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `355` // Estimated: `7021` - // Minimum execution time: 19_023_000 picoseconds. - Weight::from_parts(19_270_000, 7021) + // Minimum execution time: 19_308_000 picoseconds. + Weight::from_parts(19_520_000, 7021) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } @@ -308,8 +312,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `113` // Estimated: `10500` - // Minimum execution time: 29_089_000 picoseconds. - Weight::from_parts(29_718_000, 10500) + // Minimum execution time: 29_228_000 picoseconds. + Weight::from_parts(29_683_000, 10500) .saturating_add(RocksDbWeight::get().reads(3_u64)) .saturating_add(RocksDbWeight::get().writes(5_u64)) } @@ -329,10 +333,10 @@ impl WeightInfo for () { /// Proof: RankedCollective IdToIndex (max_values: None, max_size: Some(54), added: 2529, mode: MaxEncodedLen) fn promote() -> Weight { // Proof Size summary in bytes: - // Measured: `16864` + // Measured: `16832` // Estimated: `32243` - // Minimum execution time: 59_529_000 picoseconds. - Weight::from_parts(60_057_000, 32243) + // Minimum execution time: 59_745_000 picoseconds. + Weight::from_parts(60_290_000, 32243) .saturating_add(RocksDbWeight::get().reads(5_u64)) .saturating_add(RocksDbWeight::get().writes(6_u64)) } @@ -340,14 +344,16 @@ impl WeightInfo for () { /// Proof: RankedCollective Members (max_values: None, max_size: Some(42), added: 2517, mode: MaxEncodedLen) /// Storage: CoreFellowship Member (r:1 w:1) /// Proof: CoreFellowship Member (max_values: None, max_size: Some(49), added: 2524, mode: MaxEncodedLen) + /// Storage: CoreFellowship MemberEvidence (r:0 w:1) + /// Proof: CoreFellowship MemberEvidence (max_values: None, max_size: Some(16429), added: 18904, mode: MaxEncodedLen) fn offboard() -> Weight { // Proof Size summary in bytes: - // Measured: `292` + // Measured: `326` // Estimated: `7021` - // Minimum execution time: 17_221_000 picoseconds. - Weight::from_parts(17_585_000, 7021) + // Minimum execution time: 18_964_000 picoseconds. + Weight::from_parts(19_603_000, 7021) .saturating_add(RocksDbWeight::get().reads(2_u64)) - .saturating_add(RocksDbWeight::get().writes(1_u64)) + .saturating_add(RocksDbWeight::get().writes(2_u64)) } /// Storage: CoreFellowship Member (r:1 w:1) /// Proof: CoreFellowship Member (max_values: None, max_size: Some(49), added: 2524, mode: MaxEncodedLen) @@ -357,8 +363,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `280` // Estimated: `7021` - // Minimum execution time: 18_602_000 picoseconds. - Weight::from_parts(18_813_000, 7021) + // Minimum execution time: 18_752_000 picoseconds. + Weight::from_parts(19_035_000, 7021) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } @@ -370,10 +376,10 @@ impl WeightInfo for () { /// Proof: CoreFellowship MemberEvidence (max_values: None, max_size: Some(16429), added: 18904, mode: MaxEncodedLen) fn approve() -> Weight { // Proof Size summary in bytes: - // Measured: `16842` + // Measured: `16810` // Estimated: `26915` - // Minimum execution time: 43_525_000 picoseconds. - Weight::from_parts(43_994_000, 26915) + // Minimum execution time: 44_972_000 picoseconds. + Weight::from_parts(45_391_000, 26915) .saturating_add(RocksDbWeight::get().reads(3_u64)) .saturating_add(RocksDbWeight::get().writes(2_u64)) } @@ -385,8 +391,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `79` // Estimated: `23408` - // Minimum execution time: 27_960_000 picoseconds. - Weight::from_parts(28_331_000, 23408) + // Minimum execution time: 27_820_000 picoseconds. + Weight::from_parts(28_992_000, 23408) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } diff --git a/frame/democracy/src/weights.rs b/frame/democracy/src/weights.rs index 9241b27bb..e0c970a34 100644 --- a/frame/democracy/src/weights.rs +++ b/frame/democracy/src/weights.rs @@ -18,26 +18,28 @@ //! Autogenerated weights for pallet_democracy //! //! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev -//! DATE: 2023-01-27, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! DATE: 2023-03-16, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` //! WORST CASE MAP SIZE: `1000000` -//! HOSTNAME: `runner-b3zmxxc-project-145-concurrent-0`, CPU: `Intel(R) Xeon(R) CPU @ 2.60GHz` +//! HOSTNAME: `bm3`, CPU: `Intel(R) Core(TM) i7-7700K CPU @ 4.20GHz` //! EXECUTION: Some(Wasm), WASM-EXECUTION: Compiled, CHAIN: Some("dev"), DB CACHE: 1024 // Executed Command: -// target/production/substrate +// ./target/production/substrate // benchmark // pallet +// --chain=dev // --steps=50 // --repeat=20 +// --pallet=pallet_democracy +// --no-storage-info +// --no-median-slopes +// --no-min-squares // --extrinsic=* // --execution=wasm // --wasm-execution=compiled // --heap-pages=4096 -// --json-file=/builds/parity/mirrors/substrate/.git/.artifacts/bench.json -// --pallet=pallet_democracy -// --chain=dev -// --header=./HEADER-APACHE2 // --output=./frame/democracy/src/weights.rs +// --header=./HEADER-APACHE2 // --template=./.maintain/frame-weight-template.hbs #![cfg_attr(rustfmt, rustfmt_skip)] @@ -92,10 +94,10 @@ impl WeightInfo for SubstrateWeight { /// Proof: Democracy DepositOf (max_values: None, max_size: Some(3230), added: 5705, mode: MaxEncodedLen) fn propose() -> Weight { // Proof Size summary in bytes: - // Measured: `4864` - // Estimated: `23409` - // Minimum execution time: 42_939 nanoseconds. - Weight::from_parts(43_543_000, 23409) + // Measured: `4801` + // Estimated: `26379` + // Minimum execution time: 39_171_000 picoseconds. + Weight::from_parts(39_779_000, 26379) .saturating_add(T::DbWeight::get().reads(3_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) } @@ -103,10 +105,10 @@ impl WeightInfo for SubstrateWeight { /// Proof: Democracy DepositOf (max_values: None, max_size: Some(3230), added: 5705, mode: MaxEncodedLen) fn second() -> Weight { // Proof Size summary in bytes: - // Measured: `3620` - // Estimated: `5705` - // Minimum execution time: 36_475 nanoseconds. - Weight::from_parts(37_863_000, 5705) + // Measured: `3556` + // Estimated: `6695` + // Minimum execution time: 35_694_000 picoseconds. + Weight::from_parts(36_181_000, 6695) .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -118,10 +120,10 @@ impl WeightInfo for SubstrateWeight { /// Proof: Balances Locks (max_values: None, max_size: Some(1299), added: 3774, mode: MaxEncodedLen) fn vote_new() -> Weight { // Proof Size summary in bytes: - // Measured: `3565` - // Estimated: `12720` - // Minimum execution time: 56_372 nanoseconds. - Weight::from_parts(57_483_000, 12720) + // Measured: `3470` + // Estimated: `15690` + // Minimum execution time: 50_274_000 picoseconds. + Weight::from_parts(51_216_000, 15690) .saturating_add(T::DbWeight::get().reads(3_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) } @@ -133,10 +135,10 @@ impl WeightInfo for SubstrateWeight { /// Proof: Balances Locks (max_values: None, max_size: Some(1299), added: 3774, mode: MaxEncodedLen) fn vote_existing() -> Weight { // Proof Size summary in bytes: - // Measured: `3587` - // Estimated: `12720` - // Minimum execution time: 56_789 nanoseconds. - Weight::from_parts(57_737_000, 12720) + // Measured: `3492` + // Estimated: `15690` + // Minimum execution time: 50_559_000 picoseconds. + Weight::from_parts(51_030_000, 15690) .saturating_add(T::DbWeight::get().reads(3_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) } @@ -148,10 +150,10 @@ impl WeightInfo for SubstrateWeight { /// Proof: Democracy MetadataOf (max_values: None, max_size: Some(53), added: 2528, mode: MaxEncodedLen) fn emergency_cancel() -> Weight { // Proof Size summary in bytes: - // Measured: `398` - // Estimated: `7712` - // Minimum execution time: 24_379 nanoseconds. - Weight::from_parts(25_302_000, 7712) + // Measured: `366` + // Estimated: `10682` + // Minimum execution time: 27_539_000 picoseconds. + Weight::from_parts(28_040_000, 10682) .saturating_add(T::DbWeight::get().reads(3_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) } @@ -171,10 +173,10 @@ impl WeightInfo for SubstrateWeight { /// Proof: Democracy Blacklist (max_values: None, max_size: Some(3238), added: 5713, mode: MaxEncodedLen) fn blacklist() -> Weight { // Proof Size summary in bytes: - // Measured: `6036` - // Estimated: `36392` - // Minimum execution time: 100_345 nanoseconds. - Weight::from_parts(102_233_000, 36392) + // Measured: `5910` + // Estimated: `42332` + // Minimum execution time: 93_053_000 picoseconds. + Weight::from_parts(94_193_000, 42332) .saturating_add(T::DbWeight::get().reads(8_u64)) .saturating_add(T::DbWeight::get().writes(7_u64)) } @@ -184,10 +186,10 @@ impl WeightInfo for SubstrateWeight { /// Proof: Democracy Blacklist (max_values: None, max_size: Some(3238), added: 5713, mode: MaxEncodedLen) fn external_propose() -> Weight { // Proof Size summary in bytes: - // Measured: `3448` - // Estimated: `6340` - // Minimum execution time: 13_155 nanoseconds. - Weight::from_parts(14_158_000, 6340) + // Measured: `3416` + // Estimated: `8320` + // Minimum execution time: 14_664_000 picoseconds. + Weight::from_parts(15_064_000, 8320) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -197,8 +199,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 2_961 nanoseconds. - Weight::from_parts(3_139_000, 0) + // Minimum execution time: 4_073_000 picoseconds. + Weight::from_parts(4_242_000, 0) .saturating_add(T::DbWeight::get().writes(1_u64)) } /// Storage: Democracy NextExternal (r:0 w:1) @@ -207,8 +209,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 3_040 nanoseconds. - Weight::from_parts(3_261_000, 0) + // Minimum execution time: 3_779_000 picoseconds. + Weight::from_parts(4_091_000, 0) .saturating_add(T::DbWeight::get().writes(1_u64)) } /// Storage: Democracy NextExternal (r:1 w:1) @@ -222,9 +224,9 @@ impl WeightInfo for SubstrateWeight { fn fast_track() -> Weight { // Proof Size summary in bytes: // Measured: `286` - // Estimated: `3654` - // Minimum execution time: 26_666 nanoseconds. - Weight::from_parts(28_014_000, 3654) + // Estimated: `6624` + // Minimum execution time: 29_596_000 picoseconds. + Weight::from_parts(30_083_000, 6624) .saturating_add(T::DbWeight::get().reads(3_u64)) .saturating_add(T::DbWeight::get().writes(5_u64)) } @@ -236,10 +238,10 @@ impl WeightInfo for SubstrateWeight { /// Proof: Democracy MetadataOf (max_values: None, max_size: Some(53), added: 2528, mode: MaxEncodedLen) fn veto_external() -> Weight { // Proof Size summary in bytes: - // Measured: `3551` - // Estimated: `8868` - // Minimum execution time: 30_180 nanoseconds. - Weight::from_parts(31_593_000, 8868) + // Measured: `3519` + // Estimated: `11838` + // Minimum execution time: 34_721_000 picoseconds. + Weight::from_parts(34_989_000, 11838) .saturating_add(T::DbWeight::get().reads(3_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) } @@ -253,10 +255,10 @@ impl WeightInfo for SubstrateWeight { /// Proof: Democracy MetadataOf (max_values: None, max_size: Some(53), added: 2528, mode: MaxEncodedLen) fn cancel_proposal() -> Weight { // Proof Size summary in bytes: - // Measured: `5915` - // Estimated: `28033` - // Minimum execution time: 80_780 nanoseconds. - Weight::from_parts(82_070_000, 28033) + // Measured: `5821` + // Estimated: `31993` + // Minimum execution time: 74_835_000 picoseconds. + Weight::from_parts(75_807_000, 31993) .saturating_add(T::DbWeight::get().reads(4_u64)) .saturating_add(T::DbWeight::get().writes(4_u64)) } @@ -267,9 +269,9 @@ impl WeightInfo for SubstrateWeight { fn cancel_referendum() -> Weight { // Proof Size summary in bytes: // Measured: `271` - // Estimated: `2528` - // Minimum execution time: 18_117 nanoseconds. - Weight::from_parts(19_027_000, 2528) + // Estimated: `3518` + // Minimum execution time: 20_995_000 picoseconds. + Weight::from_parts(21_425_000, 3518) .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(T::DbWeight::get().writes(2_u64)) } @@ -282,12 +284,12 @@ impl WeightInfo for SubstrateWeight { /// The range of component `r` is `[0, 99]`. fn on_initialize_base(r: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `244 + r * (117 ±0)` - // Estimated: `998 + r * (2676 ±0)` - // Minimum execution time: 7_093 nanoseconds. - Weight::from_parts(8_792_955, 998) - // Standard Error: 6_630 - .saturating_add(Weight::from_parts(3_091_565, 0).saturating_mul(r.into())) + // Measured: `244 + r * (86 ±0)` + // Estimated: `3968 + r * (2676 ±0)` + // Minimum execution time: 7_002_000 picoseconds. + Weight::from_parts(10_543_534, 3968) + // Standard Error: 7_286 + .saturating_add(Weight::from_parts(2_911_122, 0).saturating_mul(r.into())) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().reads((1_u64).saturating_mul(r.into()))) .saturating_add(T::DbWeight::get().writes(1_u64)) @@ -308,12 +310,12 @@ impl WeightInfo for SubstrateWeight { /// The range of component `r` is `[0, 99]`. fn on_initialize_base_with_launch_period(r: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `244 + r * (117 ±0)` - // Estimated: `19318 + r * (2676 ±0)` - // Minimum execution time: 10_372 nanoseconds. - Weight::from_parts(10_961_165, 19318) - // Standard Error: 7_284 - .saturating_add(Weight::from_parts(3_112_573, 0).saturating_mul(r.into())) + // Measured: `244 + r * (86 ±0)` + // Estimated: `25258 + r * (2676 ±0)` + // Minimum execution time: 10_824_000 picoseconds. + Weight::from_parts(14_187_527, 25258) + // Standard Error: 7_148 + .saturating_add(Weight::from_parts(2_886_910, 0).saturating_mul(r.into())) .saturating_add(T::DbWeight::get().reads(5_u64)) .saturating_add(T::DbWeight::get().reads((1_u64).saturating_mul(r.into()))) .saturating_add(T::DbWeight::get().writes(1_u64)) @@ -328,12 +330,12 @@ impl WeightInfo for SubstrateWeight { /// The range of component `r` is `[0, 99]`. fn delegate(r: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `958 + r * (139 ±0)` - // Estimated: `22584 + r * (2676 ±0)` - // Minimum execution time: 36_618 nanoseconds. - Weight::from_parts(42_803_184, 22584) - // Standard Error: 7_268 - .saturating_add(Weight::from_parts(4_537_902, 0).saturating_mul(r.into())) + // Measured: `830 + r * (108 ±0)` + // Estimated: `25554 + r * (2676 ±0)` + // Minimum execution time: 42_940_000 picoseconds. + Weight::from_parts(45_288_082, 25554) + // Standard Error: 6_380 + .saturating_add(Weight::from_parts(4_219_163, 0).saturating_mul(r.into())) .saturating_add(T::DbWeight::get().reads(4_u64)) .saturating_add(T::DbWeight::get().reads((1_u64).saturating_mul(r.into()))) .saturating_add(T::DbWeight::get().writes(4_u64)) @@ -347,12 +349,12 @@ impl WeightInfo for SubstrateWeight { /// The range of component `r` is `[0, 99]`. fn undelegate(r: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `557 + r * (139 ±0)` - // Estimated: `12540 + r * (2676 ±0)` - // Minimum execution time: 19_758 nanoseconds. - Weight::from_parts(21_641_793, 12540) - // Standard Error: 6_889 - .saturating_add(Weight::from_parts(4_478_884, 0).saturating_mul(r.into())) + // Measured: `493 + r * (108 ±0)` + // Estimated: `14520 + r * (2676 ±0)` + // Minimum execution time: 22_203_000 picoseconds. + Weight::from_parts(22_307_372, 14520) + // Standard Error: 7_095 + .saturating_add(Weight::from_parts(4_202_995, 0).saturating_mul(r.into())) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().reads((1_u64).saturating_mul(r.into()))) .saturating_add(T::DbWeight::get().writes(2_u64)) @@ -365,8 +367,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 2_844 nanoseconds. - Weight::from_parts(3_017_000, 0) + // Minimum execution time: 4_046_000 picoseconds. + Weight::from_parts(4_191_000, 0) .saturating_add(T::DbWeight::get().writes(1_u64)) } /// Storage: Democracy VotingOf (r:1 w:1) @@ -378,12 +380,12 @@ impl WeightInfo for SubstrateWeight { /// The range of component `r` is `[0, 99]`. fn unlock_remove(r: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `627` - // Estimated: `12647` - // Minimum execution time: 20_380 nanoseconds. - Weight::from_parts(28_295_875, 12647) - // Standard Error: 2_331 - .saturating_add(Weight::from_parts(67_348, 0).saturating_mul(r.into())) + // Measured: `563` + // Estimated: `15617` + // Minimum execution time: 22_585_000 picoseconds. + Weight::from_parts(28_461_437, 15617) + // Standard Error: 1_636 + .saturating_add(Weight::from_parts(19_140, 0).saturating_mul(r.into())) .saturating_add(T::DbWeight::get().reads(3_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) } @@ -396,12 +398,12 @@ impl WeightInfo for SubstrateWeight { /// The range of component `r` is `[0, 99]`. fn unlock_set(r: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `628 + r * (22 ±0)` - // Estimated: `12647` - // Minimum execution time: 24_475 nanoseconds. - Weight::from_parts(27_102_576, 12647) - // Standard Error: 1_464 - .saturating_add(Weight::from_parts(128_921, 0).saturating_mul(r.into())) + // Measured: `564 + r * (22 ±0)` + // Estimated: `15617` + // Minimum execution time: 26_674_000 picoseconds. + Weight::from_parts(27_815_260, 15617) + // Standard Error: 490 + .saturating_add(Weight::from_parts(68_478, 0).saturating_mul(r.into())) .saturating_add(T::DbWeight::get().reads(3_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) } @@ -412,12 +414,12 @@ impl WeightInfo for SubstrateWeight { /// The range of component `r` is `[1, 100]`. fn remove_vote(r: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `791 + r * (26 ±0)` - // Estimated: `8946` - // Minimum execution time: 15_039 nanoseconds. - Weight::from_parts(19_252_498, 8946) - // Standard Error: 1_798 - .saturating_add(Weight::from_parts(131_855, 0).saturating_mul(r.into())) + // Measured: `728 + r * (26 ±0)` + // Estimated: `10926` + // Minimum execution time: 17_381_000 picoseconds. + Weight::from_parts(19_728_665, 10926) + // Standard Error: 1_065 + .saturating_add(Weight::from_parts(84_481, 0).saturating_mul(r.into())) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().writes(2_u64)) } @@ -428,12 +430,12 @@ impl WeightInfo for SubstrateWeight { /// The range of component `r` is `[1, 100]`. fn remove_other_vote(r: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `791 + r * (26 ±0)` - // Estimated: `8946` - // Minimum execution time: 14_837 nanoseconds. - Weight::from_parts(19_144_929, 8946) - // Standard Error: 1_875 - .saturating_add(Weight::from_parts(136_819, 0).saturating_mul(r.into())) + // Measured: `728 + r * (26 ±0)` + // Estimated: `10926` + // Minimum execution time: 17_496_000 picoseconds. + Weight::from_parts(19_845_941, 10926) + // Standard Error: 1_169 + .saturating_add(Weight::from_parts(85_004, 0).saturating_mul(r.into())) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().writes(2_u64)) } @@ -446,9 +448,9 @@ impl WeightInfo for SubstrateWeight { fn set_external_metadata() -> Weight { // Proof Size summary in bytes: // Measured: `356` - // Estimated: `3193` - // Minimum execution time: 17_338 nanoseconds. - Weight::from_parts(17_946_000, 3193) + // Estimated: `5173` + // Minimum execution time: 19_076_000 picoseconds. + Weight::from_parts(19_650_000, 5173) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -459,9 +461,9 @@ impl WeightInfo for SubstrateWeight { fn clear_external_metadata() -> Weight { // Proof Size summary in bytes: // Measured: `286` - // Estimated: `3155` - // Minimum execution time: 15_364 nanoseconds. - Weight::from_parts(15_990_000, 3155) + // Estimated: `5135` + // Minimum execution time: 17_588_000 picoseconds. + Weight::from_parts(17_781_000, 5135) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -473,10 +475,10 @@ impl WeightInfo for SubstrateWeight { /// Proof: Democracy MetadataOf (max_values: None, max_size: Some(53), added: 2528, mode: MaxEncodedLen) fn set_proposal_metadata() -> Weight { // Proof Size summary in bytes: - // Measured: `4919` - // Estimated: `19763` - // Minimum execution time: 37_147 nanoseconds. - Weight::from_parts(37_778_000, 19763) + // Measured: `4888` + // Estimated: `21743` + // Minimum execution time: 34_828_000 picoseconds. + Weight::from_parts(35_364_000, 21743) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -486,10 +488,10 @@ impl WeightInfo for SubstrateWeight { /// Proof: Democracy MetadataOf (max_values: None, max_size: Some(53), added: 2528, mode: MaxEncodedLen) fn clear_proposal_metadata() -> Weight { // Proof Size summary in bytes: - // Measured: `4853` - // Estimated: `19725` - // Minimum execution time: 34_118 nanoseconds. - Weight::from_parts(34_737_000, 19725) + // Measured: `4822` + // Estimated: `21705` + // Minimum execution time: 32_035_000 picoseconds. + Weight::from_parts(32_554_000, 21705) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -500,9 +502,9 @@ impl WeightInfo for SubstrateWeight { fn set_referendum_metadata() -> Weight { // Proof Size summary in bytes: // Measured: `144` - // Estimated: `2566` - // Minimum execution time: 12_787 nanoseconds. - Weight::from_parts(13_463_000, 2566) + // Estimated: `3556` + // Minimum execution time: 15_279_000 picoseconds. + Weight::from_parts(15_556_000, 3556) .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -513,9 +515,9 @@ impl WeightInfo for SubstrateWeight { fn clear_referendum_metadata() -> Weight { // Proof Size summary in bytes: // Measured: `302` - // Estimated: `5204` - // Minimum execution time: 17_636 nanoseconds. - Weight::from_parts(18_399_000, 5204) + // Estimated: `7184` + // Minimum execution time: 19_622_000 picoseconds. + Weight::from_parts(19_978_000, 7184) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -533,10 +535,10 @@ impl WeightInfo for () { /// Proof: Democracy DepositOf (max_values: None, max_size: Some(3230), added: 5705, mode: MaxEncodedLen) fn propose() -> Weight { // Proof Size summary in bytes: - // Measured: `4864` - // Estimated: `23409` - // Minimum execution time: 42_939 nanoseconds. - Weight::from_parts(43_543_000, 23409) + // Measured: `4801` + // Estimated: `26379` + // Minimum execution time: 39_171_000 picoseconds. + Weight::from_parts(39_779_000, 26379) .saturating_add(RocksDbWeight::get().reads(3_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) } @@ -544,10 +546,10 @@ impl WeightInfo for () { /// Proof: Democracy DepositOf (max_values: None, max_size: Some(3230), added: 5705, mode: MaxEncodedLen) fn second() -> Weight { // Proof Size summary in bytes: - // Measured: `3620` - // Estimated: `5705` - // Minimum execution time: 36_475 nanoseconds. - Weight::from_parts(37_863_000, 5705) + // Measured: `3556` + // Estimated: `6695` + // Minimum execution time: 35_694_000 picoseconds. + Weight::from_parts(36_181_000, 6695) .saturating_add(RocksDbWeight::get().reads(1_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } @@ -559,10 +561,10 @@ impl WeightInfo for () { /// Proof: Balances Locks (max_values: None, max_size: Some(1299), added: 3774, mode: MaxEncodedLen) fn vote_new() -> Weight { // Proof Size summary in bytes: - // Measured: `3565` - // Estimated: `12720` - // Minimum execution time: 56_372 nanoseconds. - Weight::from_parts(57_483_000, 12720) + // Measured: `3470` + // Estimated: `15690` + // Minimum execution time: 50_274_000 picoseconds. + Weight::from_parts(51_216_000, 15690) .saturating_add(RocksDbWeight::get().reads(3_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) } @@ -574,10 +576,10 @@ impl WeightInfo for () { /// Proof: Balances Locks (max_values: None, max_size: Some(1299), added: 3774, mode: MaxEncodedLen) fn vote_existing() -> Weight { // Proof Size summary in bytes: - // Measured: `3587` - // Estimated: `12720` - // Minimum execution time: 56_789 nanoseconds. - Weight::from_parts(57_737_000, 12720) + // Measured: `3492` + // Estimated: `15690` + // Minimum execution time: 50_559_000 picoseconds. + Weight::from_parts(51_030_000, 15690) .saturating_add(RocksDbWeight::get().reads(3_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) } @@ -589,10 +591,10 @@ impl WeightInfo for () { /// Proof: Democracy MetadataOf (max_values: None, max_size: Some(53), added: 2528, mode: MaxEncodedLen) fn emergency_cancel() -> Weight { // Proof Size summary in bytes: - // Measured: `398` - // Estimated: `7712` - // Minimum execution time: 24_379 nanoseconds. - Weight::from_parts(25_302_000, 7712) + // Measured: `366` + // Estimated: `10682` + // Minimum execution time: 27_539_000 picoseconds. + Weight::from_parts(28_040_000, 10682) .saturating_add(RocksDbWeight::get().reads(3_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) } @@ -612,10 +614,10 @@ impl WeightInfo for () { /// Proof: Democracy Blacklist (max_values: None, max_size: Some(3238), added: 5713, mode: MaxEncodedLen) fn blacklist() -> Weight { // Proof Size summary in bytes: - // Measured: `6036` - // Estimated: `36392` - // Minimum execution time: 100_345 nanoseconds. - Weight::from_parts(102_233_000, 36392) + // Measured: `5910` + // Estimated: `42332` + // Minimum execution time: 93_053_000 picoseconds. + Weight::from_parts(94_193_000, 42332) .saturating_add(RocksDbWeight::get().reads(8_u64)) .saturating_add(RocksDbWeight::get().writes(7_u64)) } @@ -625,10 +627,10 @@ impl WeightInfo for () { /// Proof: Democracy Blacklist (max_values: None, max_size: Some(3238), added: 5713, mode: MaxEncodedLen) fn external_propose() -> Weight { // Proof Size summary in bytes: - // Measured: `3448` - // Estimated: `6340` - // Minimum execution time: 13_155 nanoseconds. - Weight::from_parts(14_158_000, 6340) + // Measured: `3416` + // Estimated: `8320` + // Minimum execution time: 14_664_000 picoseconds. + Weight::from_parts(15_064_000, 8320) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } @@ -638,8 +640,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 2_961 nanoseconds. - Weight::from_parts(3_139_000, 0) + // Minimum execution time: 4_073_000 picoseconds. + Weight::from_parts(4_242_000, 0) .saturating_add(RocksDbWeight::get().writes(1_u64)) } /// Storage: Democracy NextExternal (r:0 w:1) @@ -648,8 +650,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 3_040 nanoseconds. - Weight::from_parts(3_261_000, 0) + // Minimum execution time: 3_779_000 picoseconds. + Weight::from_parts(4_091_000, 0) .saturating_add(RocksDbWeight::get().writes(1_u64)) } /// Storage: Democracy NextExternal (r:1 w:1) @@ -663,9 +665,9 @@ impl WeightInfo for () { fn fast_track() -> Weight { // Proof Size summary in bytes: // Measured: `286` - // Estimated: `3654` - // Minimum execution time: 26_666 nanoseconds. - Weight::from_parts(28_014_000, 3654) + // Estimated: `6624` + // Minimum execution time: 29_596_000 picoseconds. + Weight::from_parts(30_083_000, 6624) .saturating_add(RocksDbWeight::get().reads(3_u64)) .saturating_add(RocksDbWeight::get().writes(5_u64)) } @@ -677,10 +679,10 @@ impl WeightInfo for () { /// Proof: Democracy MetadataOf (max_values: None, max_size: Some(53), added: 2528, mode: MaxEncodedLen) fn veto_external() -> Weight { // Proof Size summary in bytes: - // Measured: `3551` - // Estimated: `8868` - // Minimum execution time: 30_180 nanoseconds. - Weight::from_parts(31_593_000, 8868) + // Measured: `3519` + // Estimated: `11838` + // Minimum execution time: 34_721_000 picoseconds. + Weight::from_parts(34_989_000, 11838) .saturating_add(RocksDbWeight::get().reads(3_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) } @@ -694,10 +696,10 @@ impl WeightInfo for () { /// Proof: Democracy MetadataOf (max_values: None, max_size: Some(53), added: 2528, mode: MaxEncodedLen) fn cancel_proposal() -> Weight { // Proof Size summary in bytes: - // Measured: `5915` - // Estimated: `28033` - // Minimum execution time: 80_780 nanoseconds. - Weight::from_parts(82_070_000, 28033) + // Measured: `5821` + // Estimated: `31993` + // Minimum execution time: 74_835_000 picoseconds. + Weight::from_parts(75_807_000, 31993) .saturating_add(RocksDbWeight::get().reads(4_u64)) .saturating_add(RocksDbWeight::get().writes(4_u64)) } @@ -708,9 +710,9 @@ impl WeightInfo for () { fn cancel_referendum() -> Weight { // Proof Size summary in bytes: // Measured: `271` - // Estimated: `2528` - // Minimum execution time: 18_117 nanoseconds. - Weight::from_parts(19_027_000, 2528) + // Estimated: `3518` + // Minimum execution time: 20_995_000 picoseconds. + Weight::from_parts(21_425_000, 3518) .saturating_add(RocksDbWeight::get().reads(1_u64)) .saturating_add(RocksDbWeight::get().writes(2_u64)) } @@ -723,12 +725,12 @@ impl WeightInfo for () { /// The range of component `r` is `[0, 99]`. fn on_initialize_base(r: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `244 + r * (117 ±0)` - // Estimated: `998 + r * (2676 ±0)` - // Minimum execution time: 7_093 nanoseconds. - Weight::from_parts(8_792_955, 998) - // Standard Error: 6_630 - .saturating_add(Weight::from_parts(3_091_565, 0).saturating_mul(r.into())) + // Measured: `244 + r * (86 ±0)` + // Estimated: `3968 + r * (2676 ±0)` + // Minimum execution time: 7_002_000 picoseconds. + Weight::from_parts(10_543_534, 3968) + // Standard Error: 7_286 + .saturating_add(Weight::from_parts(2_911_122, 0).saturating_mul(r.into())) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().reads((1_u64).saturating_mul(r.into()))) .saturating_add(RocksDbWeight::get().writes(1_u64)) @@ -749,12 +751,12 @@ impl WeightInfo for () { /// The range of component `r` is `[0, 99]`. fn on_initialize_base_with_launch_period(r: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `244 + r * (117 ±0)` - // Estimated: `19318 + r * (2676 ±0)` - // Minimum execution time: 10_372 nanoseconds. - Weight::from_parts(10_961_165, 19318) - // Standard Error: 7_284 - .saturating_add(Weight::from_parts(3_112_573, 0).saturating_mul(r.into())) + // Measured: `244 + r * (86 ±0)` + // Estimated: `25258 + r * (2676 ±0)` + // Minimum execution time: 10_824_000 picoseconds. + Weight::from_parts(14_187_527, 25258) + // Standard Error: 7_148 + .saturating_add(Weight::from_parts(2_886_910, 0).saturating_mul(r.into())) .saturating_add(RocksDbWeight::get().reads(5_u64)) .saturating_add(RocksDbWeight::get().reads((1_u64).saturating_mul(r.into()))) .saturating_add(RocksDbWeight::get().writes(1_u64)) @@ -769,12 +771,12 @@ impl WeightInfo for () { /// The range of component `r` is `[0, 99]`. fn delegate(r: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `958 + r * (139 ±0)` - // Estimated: `22584 + r * (2676 ±0)` - // Minimum execution time: 36_618 nanoseconds. - Weight::from_parts(42_803_184, 22584) - // Standard Error: 7_268 - .saturating_add(Weight::from_parts(4_537_902, 0).saturating_mul(r.into())) + // Measured: `830 + r * (108 ±0)` + // Estimated: `25554 + r * (2676 ±0)` + // Minimum execution time: 42_940_000 picoseconds. + Weight::from_parts(45_288_082, 25554) + // Standard Error: 6_380 + .saturating_add(Weight::from_parts(4_219_163, 0).saturating_mul(r.into())) .saturating_add(RocksDbWeight::get().reads(4_u64)) .saturating_add(RocksDbWeight::get().reads((1_u64).saturating_mul(r.into()))) .saturating_add(RocksDbWeight::get().writes(4_u64)) @@ -788,12 +790,12 @@ impl WeightInfo for () { /// The range of component `r` is `[0, 99]`. fn undelegate(r: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `557 + r * (139 ±0)` - // Estimated: `12540 + r * (2676 ±0)` - // Minimum execution time: 19_758 nanoseconds. - Weight::from_parts(21_641_793, 12540) - // Standard Error: 6_889 - .saturating_add(Weight::from_parts(4_478_884, 0).saturating_mul(r.into())) + // Measured: `493 + r * (108 ±0)` + // Estimated: `14520 + r * (2676 ±0)` + // Minimum execution time: 22_203_000 picoseconds. + Weight::from_parts(22_307_372, 14520) + // Standard Error: 7_095 + .saturating_add(Weight::from_parts(4_202_995, 0).saturating_mul(r.into())) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().reads((1_u64).saturating_mul(r.into()))) .saturating_add(RocksDbWeight::get().writes(2_u64)) @@ -806,8 +808,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 2_844 nanoseconds. - Weight::from_parts(3_017_000, 0) + // Minimum execution time: 4_046_000 picoseconds. + Weight::from_parts(4_191_000, 0) .saturating_add(RocksDbWeight::get().writes(1_u64)) } /// Storage: Democracy VotingOf (r:1 w:1) @@ -819,12 +821,12 @@ impl WeightInfo for () { /// The range of component `r` is `[0, 99]`. fn unlock_remove(r: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `627` - // Estimated: `12647` - // Minimum execution time: 20_380 nanoseconds. - Weight::from_parts(28_295_875, 12647) - // Standard Error: 2_331 - .saturating_add(Weight::from_parts(67_348, 0).saturating_mul(r.into())) + // Measured: `563` + // Estimated: `15617` + // Minimum execution time: 22_585_000 picoseconds. + Weight::from_parts(28_461_437, 15617) + // Standard Error: 1_636 + .saturating_add(Weight::from_parts(19_140, 0).saturating_mul(r.into())) .saturating_add(RocksDbWeight::get().reads(3_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) } @@ -837,12 +839,12 @@ impl WeightInfo for () { /// The range of component `r` is `[0, 99]`. fn unlock_set(r: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `628 + r * (22 ±0)` - // Estimated: `12647` - // Minimum execution time: 24_475 nanoseconds. - Weight::from_parts(27_102_576, 12647) - // Standard Error: 1_464 - .saturating_add(Weight::from_parts(128_921, 0).saturating_mul(r.into())) + // Measured: `564 + r * (22 ±0)` + // Estimated: `15617` + // Minimum execution time: 26_674_000 picoseconds. + Weight::from_parts(27_815_260, 15617) + // Standard Error: 490 + .saturating_add(Weight::from_parts(68_478, 0).saturating_mul(r.into())) .saturating_add(RocksDbWeight::get().reads(3_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) } @@ -853,12 +855,12 @@ impl WeightInfo for () { /// The range of component `r` is `[1, 100]`. fn remove_vote(r: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `791 + r * (26 ±0)` - // Estimated: `8946` - // Minimum execution time: 15_039 nanoseconds. - Weight::from_parts(19_252_498, 8946) - // Standard Error: 1_798 - .saturating_add(Weight::from_parts(131_855, 0).saturating_mul(r.into())) + // Measured: `728 + r * (26 ±0)` + // Estimated: `10926` + // Minimum execution time: 17_381_000 picoseconds. + Weight::from_parts(19_728_665, 10926) + // Standard Error: 1_065 + .saturating_add(Weight::from_parts(84_481, 0).saturating_mul(r.into())) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().writes(2_u64)) } @@ -869,12 +871,12 @@ impl WeightInfo for () { /// The range of component `r` is `[1, 100]`. fn remove_other_vote(r: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `791 + r * (26 ±0)` - // Estimated: `8946` - // Minimum execution time: 14_837 nanoseconds. - Weight::from_parts(19_144_929, 8946) - // Standard Error: 1_875 - .saturating_add(Weight::from_parts(136_819, 0).saturating_mul(r.into())) + // Measured: `728 + r * (26 ±0)` + // Estimated: `10926` + // Minimum execution time: 17_496_000 picoseconds. + Weight::from_parts(19_845_941, 10926) + // Standard Error: 1_169 + .saturating_add(Weight::from_parts(85_004, 0).saturating_mul(r.into())) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().writes(2_u64)) } @@ -887,9 +889,9 @@ impl WeightInfo for () { fn set_external_metadata() -> Weight { // Proof Size summary in bytes: // Measured: `356` - // Estimated: `3193` - // Minimum execution time: 17_338 nanoseconds. - Weight::from_parts(17_946_000, 3193) + // Estimated: `5173` + // Minimum execution time: 19_076_000 picoseconds. + Weight::from_parts(19_650_000, 5173) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } @@ -900,9 +902,9 @@ impl WeightInfo for () { fn clear_external_metadata() -> Weight { // Proof Size summary in bytes: // Measured: `286` - // Estimated: `3155` - // Minimum execution time: 15_364 nanoseconds. - Weight::from_parts(15_990_000, 3155) + // Estimated: `5135` + // Minimum execution time: 17_588_000 picoseconds. + Weight::from_parts(17_781_000, 5135) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } @@ -914,10 +916,10 @@ impl WeightInfo for () { /// Proof: Democracy MetadataOf (max_values: None, max_size: Some(53), added: 2528, mode: MaxEncodedLen) fn set_proposal_metadata() -> Weight { // Proof Size summary in bytes: - // Measured: `4919` - // Estimated: `19763` - // Minimum execution time: 37_147 nanoseconds. - Weight::from_parts(37_778_000, 19763) + // Measured: `4888` + // Estimated: `21743` + // Minimum execution time: 34_828_000 picoseconds. + Weight::from_parts(35_364_000, 21743) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } @@ -927,10 +929,10 @@ impl WeightInfo for () { /// Proof: Democracy MetadataOf (max_values: None, max_size: Some(53), added: 2528, mode: MaxEncodedLen) fn clear_proposal_metadata() -> Weight { // Proof Size summary in bytes: - // Measured: `4853` - // Estimated: `19725` - // Minimum execution time: 34_118 nanoseconds. - Weight::from_parts(34_737_000, 19725) + // Measured: `4822` + // Estimated: `21705` + // Minimum execution time: 32_035_000 picoseconds. + Weight::from_parts(32_554_000, 21705) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } @@ -941,9 +943,9 @@ impl WeightInfo for () { fn set_referendum_metadata() -> Weight { // Proof Size summary in bytes: // Measured: `144` - // Estimated: `2566` - // Minimum execution time: 12_787 nanoseconds. - Weight::from_parts(13_463_000, 2566) + // Estimated: `3556` + // Minimum execution time: 15_279_000 picoseconds. + Weight::from_parts(15_556_000, 3556) .saturating_add(RocksDbWeight::get().reads(1_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } @@ -954,9 +956,9 @@ impl WeightInfo for () { fn clear_referendum_metadata() -> Weight { // Proof Size summary in bytes: // Measured: `302` - // Estimated: `5204` - // Minimum execution time: 17_636 nanoseconds. - Weight::from_parts(18_399_000, 5204) + // Estimated: `7184` + // Minimum execution time: 19_622_000 picoseconds. + Weight::from_parts(19_978_000, 7184) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } diff --git a/frame/election-provider-multi-phase/src/weights.rs b/frame/election-provider-multi-phase/src/weights.rs index 90be98ece..f9dfdfc19 100644 --- a/frame/election-provider-multi-phase/src/weights.rs +++ b/frame/election-provider-multi-phase/src/weights.rs @@ -18,9 +18,9 @@ //! Autogenerated weights for pallet_election_provider_multi_phase //! //! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev -//! DATE: 2023-01-24, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! DATE: 2023-03-16, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` //! WORST CASE MAP SIZE: `1000000` -//! HOSTNAME: `bm2`, CPU: `Intel(R) Core(TM) i7-7700K CPU @ 4.20GHz` +//! HOSTNAME: `bm3`, CPU: `Intel(R) Core(TM) i7-7700K CPU @ 4.20GHz` //! EXECUTION: Some(Wasm), WASM-EXECUTION: Compiled, CHAIN: Some("dev"), DB CACHE: 1024 // Executed Command: @@ -31,6 +31,9 @@ // --steps=50 // --repeat=20 // --pallet=pallet_election_provider_multi_phase +// --no-storage-info +// --no-median-slopes +// --no-min-squares // --extrinsic=* // --execution=wasm // --wasm-execution=compiled @@ -82,9 +85,9 @@ impl WeightInfo for SubstrateWeight { fn on_initialize_nothing() -> Weight { // Proof Size summary in bytes: // Measured: `994` - // Estimated: `6983` - // Minimum execution time: 17_801 nanoseconds. - Weight::from_parts(18_364_000, 6983) + // Estimated: `14903` + // Minimum execution time: 21_538_000 picoseconds. + Weight::from_parts(22_045_000, 14903) .saturating_add(T::DbWeight::get().reads(8_u64)) } /// Storage: ElectionProviderMultiPhase Round (r:1 w:0) @@ -94,9 +97,9 @@ impl WeightInfo for SubstrateWeight { fn on_initialize_open_signed() -> Weight { // Proof Size summary in bytes: // Measured: `114` - // Estimated: `1218` - // Minimum execution time: 12_814 nanoseconds. - Weight::from_parts(13_154_000, 1218) + // Estimated: `3198` + // Minimum execution time: 12_696_000 picoseconds. + Weight::from_parts(12_950_000, 3198) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -107,9 +110,9 @@ impl WeightInfo for SubstrateWeight { fn on_initialize_open_unsigned() -> Weight { // Proof Size summary in bytes: // Measured: `114` - // Estimated: `1218` - // Minimum execution time: 14_565 nanoseconds. - Weight::from_parts(15_097_000, 1218) + // Estimated: `3198` + // Minimum execution time: 14_029_000 picoseconds. + Weight::from_parts(14_288_000, 3198) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -119,10 +122,10 @@ impl WeightInfo for SubstrateWeight { /// Proof Skipped: ElectionProviderMultiPhase QueuedSolution (max_values: Some(1), max_size: None, mode: Measured) fn finalize_signed_phase_accept_solution() -> Weight { // Proof Size summary in bytes: - // Measured: `206` - // Estimated: `2809` - // Minimum execution time: 23_341 nanoseconds. - Weight::from_parts(23_770_000, 2809) + // Measured: `174` + // Estimated: `3767` + // Minimum execution time: 26_468_000 picoseconds. + Weight::from_parts(26_790_000, 3767) .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(T::DbWeight::get().writes(2_u64)) } @@ -130,10 +133,10 @@ impl WeightInfo for SubstrateWeight { /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) fn finalize_signed_phase_reject_solution() -> Weight { // Proof Size summary in bytes: - // Measured: `206` - // Estimated: `2603` - // Minimum execution time: 16_662 nanoseconds. - Weight::from_parts(16_898_000, 2603) + // Measured: `174` + // Estimated: `3593` + // Minimum execution time: 18_401_000 picoseconds. + Weight::from_parts(18_686_000, 3593) .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -149,10 +152,10 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 215_168 nanoseconds. - Weight::from_parts(219_887_000, 0) - // Standard Error: 1_444 - .saturating_add(Weight::from_parts(146_388, 0).saturating_mul(v.into())) + // Minimum execution time: 266_901_000 picoseconds. + Weight::from_parts(270_251_000, 0) + // Standard Error: 1_758 + .saturating_add(Weight::from_parts(167_310, 0).saturating_mul(v.into())) .saturating_add(T::DbWeight::get().writes(3_u64)) } /// Storage: ElectionProviderMultiPhase SignedSubmissionIndices (r:1 w:1) @@ -177,14 +180,14 @@ impl WeightInfo for SubstrateWeight { /// The range of component `d` is `[200, 400]`. fn elect_queued(a: u32, d: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `368 + a * (768 ±0) + d * (48 ±0)` - // Estimated: `9540 + a * (6912 ±0) + d * (441 ±0)` - // Minimum execution time: 268_021 nanoseconds. - Weight::from_parts(72_491_937, 9540) - // Standard Error: 2_910 - .saturating_add(Weight::from_parts(303_955, 0).saturating_mul(a.into())) - // Standard Error: 4_363 - .saturating_add(Weight::from_parts(167_369, 0).saturating_mul(d.into())) + // Measured: `337 + a * (768 ±0) + d * (48 ±0)` + // Estimated: `16191 + a * (6912 ±0) + d * (441 ±0)` + // Minimum execution time: 286_523_000 picoseconds. + Weight::from_parts(19_101_753, 16191) + // Standard Error: 4_194 + .saturating_add(Weight::from_parts(398_102, 0).saturating_mul(a.into())) + // Standard Error: 6_287 + .saturating_add(Weight::from_parts(188_928, 0).saturating_mul(d.into())) .saturating_add(T::DbWeight::get().reads(7_u64)) .saturating_add(T::DbWeight::get().writes(8_u64)) .saturating_add(Weight::from_parts(0, 6912).saturating_mul(a.into())) @@ -204,10 +207,10 @@ impl WeightInfo for SubstrateWeight { /// Proof Skipped: ElectionProviderMultiPhase SignedSubmissionsMap (max_values: None, max_size: None, mode: Measured) fn submit() -> Weight { // Proof Size summary in bytes: - // Measured: `924` - // Estimated: `7111` - // Minimum execution time: 44_177 nanoseconds. - Weight::from_parts(44_663_000, 7111) + // Measured: `893` + // Estimated: `11906` + // Minimum execution time: 47_876_000 picoseconds. + Weight::from_parts(48_464_000, 11906) .saturating_add(T::DbWeight::get().reads(5_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) } @@ -221,51 +224,51 @@ impl WeightInfo for SubstrateWeight { /// Proof Skipped: ElectionProviderMultiPhase QueuedSolution (max_values: Some(1), max_size: None, mode: Measured) /// Storage: ElectionProviderMultiPhase SnapshotMetadata (r:1 w:0) /// Proof Skipped: ElectionProviderMultiPhase SnapshotMetadata (max_values: Some(1), max_size: None, mode: Measured) - /// Storage: ElectionProviderMultiPhase MinimumUntrustedScore (r:1 w:0) - /// Proof Skipped: ElectionProviderMultiPhase MinimumUntrustedScore (max_values: Some(1), max_size: None, mode: Measured) /// Storage: ElectionProviderMultiPhase Snapshot (r:1 w:0) /// Proof Skipped: ElectionProviderMultiPhase Snapshot (max_values: Some(1), max_size: None, mode: Measured) + /// Storage: ElectionProviderMultiPhase MinimumUntrustedScore (r:1 w:0) + /// Proof Skipped: ElectionProviderMultiPhase MinimumUntrustedScore (max_values: Some(1), max_size: None, mode: Measured) /// The range of component `v` is `[1000, 2000]`. /// The range of component `t` is `[500, 1000]`. /// The range of component `a` is `[500, 800]`. /// The range of component `d` is `[200, 400]`. fn submit_unsigned(v: u32, t: u32, a: u32, _d: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `251 + v * (553 ±0) + t * (32 ±0)` - // Estimated: `5222 + v * (3871 ±0) + t * (224 ±0)` - // Minimum execution time: 4_425_457 nanoseconds. - Weight::from_parts(4_445_889_000, 5222) - // Standard Error: 13_250 - .saturating_add(Weight::from_parts(48_844, 0).saturating_mul(v.into())) - // Standard Error: 39_266 - .saturating_add(Weight::from_parts(4_144_034, 0).saturating_mul(a.into())) + // Measured: `219 + v * (553 ±0) + t * (32 ±0)` + // Estimated: `11928 + v * (3871 ±0) + t * (224 ±0)` + // Minimum execution time: 4_702_825_000 picoseconds. + Weight::from_parts(4_752_015_000, 11928) + // Standard Error: 14_906 + .saturating_add(Weight::from_parts(131_585, 0).saturating_mul(v.into())) + // Standard Error: 44_174 + .saturating_add(Weight::from_parts(4_167_676, 0).saturating_mul(a.into())) .saturating_add(T::DbWeight::get().reads(7_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) .saturating_add(Weight::from_parts(0, 3871).saturating_mul(v.into())) .saturating_add(Weight::from_parts(0, 224).saturating_mul(t.into())) } - /// Storage: ElectionProviderMultiPhase Round (r:1 w:0) - /// Proof Skipped: ElectionProviderMultiPhase Round (max_values: Some(1), max_size: None, mode: Measured) /// Storage: ElectionProviderMultiPhase DesiredTargets (r:1 w:0) /// Proof Skipped: ElectionProviderMultiPhase DesiredTargets (max_values: Some(1), max_size: None, mode: Measured) - /// Storage: ElectionProviderMultiPhase MinimumUntrustedScore (r:1 w:0) - /// Proof Skipped: ElectionProviderMultiPhase MinimumUntrustedScore (max_values: Some(1), max_size: None, mode: Measured) /// Storage: ElectionProviderMultiPhase Snapshot (r:1 w:0) /// Proof Skipped: ElectionProviderMultiPhase Snapshot (max_values: Some(1), max_size: None, mode: Measured) + /// Storage: ElectionProviderMultiPhase Round (r:1 w:0) + /// Proof Skipped: ElectionProviderMultiPhase Round (max_values: Some(1), max_size: None, mode: Measured) + /// Storage: ElectionProviderMultiPhase MinimumUntrustedScore (r:1 w:0) + /// Proof Skipped: ElectionProviderMultiPhase MinimumUntrustedScore (max_values: Some(1), max_size: None, mode: Measured) /// The range of component `v` is `[1000, 2000]`. /// The range of component `t` is `[500, 1000]`. /// The range of component `a` is `[500, 800]`. /// The range of component `d` is `[200, 400]`. fn feasibility_check(v: u32, t: u32, a: u32, _d: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `226 + v * (553 ±0) + t * (32 ±0)` - // Estimated: `2884 + v * (2212 ±0) + t * (128 ±0)` - // Minimum execution time: 3_812_071 nanoseconds. - Weight::from_parts(3_826_375_000, 2884) - // Standard Error: 11_601 - .saturating_add(Weight::from_parts(145_309, 0).saturating_mul(v.into())) - // Standard Error: 34_378 - .saturating_add(Weight::from_parts(3_223_977, 0).saturating_mul(a.into())) + // Measured: `194 + v * (553 ±0) + t * (32 ±0)` + // Estimated: `6716 + v * (2212 ±0) + t * (128 ±0)` + // Minimum execution time: 4_069_075_000 picoseconds. + Weight::from_parts(4_105_635_000, 6716) + // Standard Error: 12_539 + .saturating_add(Weight::from_parts(163_040, 0).saturating_mul(v.into())) + // Standard Error: 37_160 + .saturating_add(Weight::from_parts(3_343_858, 0).saturating_mul(a.into())) .saturating_add(T::DbWeight::get().reads(4_u64)) .saturating_add(Weight::from_parts(0, 2212).saturating_mul(v.into())) .saturating_add(Weight::from_parts(0, 128).saturating_mul(t.into())) @@ -293,9 +296,9 @@ impl WeightInfo for () { fn on_initialize_nothing() -> Weight { // Proof Size summary in bytes: // Measured: `994` - // Estimated: `6983` - // Minimum execution time: 17_801 nanoseconds. - Weight::from_parts(18_364_000, 6983) + // Estimated: `14903` + // Minimum execution time: 21_538_000 picoseconds. + Weight::from_parts(22_045_000, 14903) .saturating_add(RocksDbWeight::get().reads(8_u64)) } /// Storage: ElectionProviderMultiPhase Round (r:1 w:0) @@ -305,9 +308,9 @@ impl WeightInfo for () { fn on_initialize_open_signed() -> Weight { // Proof Size summary in bytes: // Measured: `114` - // Estimated: `1218` - // Minimum execution time: 12_814 nanoseconds. - Weight::from_parts(13_154_000, 1218) + // Estimated: `3198` + // Minimum execution time: 12_696_000 picoseconds. + Weight::from_parts(12_950_000, 3198) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } @@ -318,9 +321,9 @@ impl WeightInfo for () { fn on_initialize_open_unsigned() -> Weight { // Proof Size summary in bytes: // Measured: `114` - // Estimated: `1218` - // Minimum execution time: 14_565 nanoseconds. - Weight::from_parts(15_097_000, 1218) + // Estimated: `3198` + // Minimum execution time: 14_029_000 picoseconds. + Weight::from_parts(14_288_000, 3198) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } @@ -330,10 +333,10 @@ impl WeightInfo for () { /// Proof Skipped: ElectionProviderMultiPhase QueuedSolution (max_values: Some(1), max_size: None, mode: Measured) fn finalize_signed_phase_accept_solution() -> Weight { // Proof Size summary in bytes: - // Measured: `206` - // Estimated: `2809` - // Minimum execution time: 23_341 nanoseconds. - Weight::from_parts(23_770_000, 2809) + // Measured: `174` + // Estimated: `3767` + // Minimum execution time: 26_468_000 picoseconds. + Weight::from_parts(26_790_000, 3767) .saturating_add(RocksDbWeight::get().reads(1_u64)) .saturating_add(RocksDbWeight::get().writes(2_u64)) } @@ -341,10 +344,10 @@ impl WeightInfo for () { /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) fn finalize_signed_phase_reject_solution() -> Weight { // Proof Size summary in bytes: - // Measured: `206` - // Estimated: `2603` - // Minimum execution time: 16_662 nanoseconds. - Weight::from_parts(16_898_000, 2603) + // Measured: `174` + // Estimated: `3593` + // Minimum execution time: 18_401_000 picoseconds. + Weight::from_parts(18_686_000, 3593) .saturating_add(RocksDbWeight::get().reads(1_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } @@ -360,10 +363,10 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 215_168 nanoseconds. - Weight::from_parts(219_887_000, 0) - // Standard Error: 1_444 - .saturating_add(Weight::from_parts(146_388, 0).saturating_mul(v.into())) + // Minimum execution time: 266_901_000 picoseconds. + Weight::from_parts(270_251_000, 0) + // Standard Error: 1_758 + .saturating_add(Weight::from_parts(167_310, 0).saturating_mul(v.into())) .saturating_add(RocksDbWeight::get().writes(3_u64)) } /// Storage: ElectionProviderMultiPhase SignedSubmissionIndices (r:1 w:1) @@ -388,14 +391,14 @@ impl WeightInfo for () { /// The range of component `d` is `[200, 400]`. fn elect_queued(a: u32, d: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `368 + a * (768 ±0) + d * (48 ±0)` - // Estimated: `9540 + a * (6912 ±0) + d * (441 ±0)` - // Minimum execution time: 268_021 nanoseconds. - Weight::from_parts(72_491_937, 9540) - // Standard Error: 2_910 - .saturating_add(Weight::from_parts(303_955, 0).saturating_mul(a.into())) - // Standard Error: 4_363 - .saturating_add(Weight::from_parts(167_369, 0).saturating_mul(d.into())) + // Measured: `337 + a * (768 ±0) + d * (48 ±0)` + // Estimated: `16191 + a * (6912 ±0) + d * (441 ±0)` + // Minimum execution time: 286_523_000 picoseconds. + Weight::from_parts(19_101_753, 16191) + // Standard Error: 4_194 + .saturating_add(Weight::from_parts(398_102, 0).saturating_mul(a.into())) + // Standard Error: 6_287 + .saturating_add(Weight::from_parts(188_928, 0).saturating_mul(d.into())) .saturating_add(RocksDbWeight::get().reads(7_u64)) .saturating_add(RocksDbWeight::get().writes(8_u64)) .saturating_add(Weight::from_parts(0, 6912).saturating_mul(a.into())) @@ -415,10 +418,10 @@ impl WeightInfo for () { /// Proof Skipped: ElectionProviderMultiPhase SignedSubmissionsMap (max_values: None, max_size: None, mode: Measured) fn submit() -> Weight { // Proof Size summary in bytes: - // Measured: `924` - // Estimated: `7111` - // Minimum execution time: 44_177 nanoseconds. - Weight::from_parts(44_663_000, 7111) + // Measured: `893` + // Estimated: `11906` + // Minimum execution time: 47_876_000 picoseconds. + Weight::from_parts(48_464_000, 11906) .saturating_add(RocksDbWeight::get().reads(5_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) } @@ -432,51 +435,51 @@ impl WeightInfo for () { /// Proof Skipped: ElectionProviderMultiPhase QueuedSolution (max_values: Some(1), max_size: None, mode: Measured) /// Storage: ElectionProviderMultiPhase SnapshotMetadata (r:1 w:0) /// Proof Skipped: ElectionProviderMultiPhase SnapshotMetadata (max_values: Some(1), max_size: None, mode: Measured) - /// Storage: ElectionProviderMultiPhase MinimumUntrustedScore (r:1 w:0) - /// Proof Skipped: ElectionProviderMultiPhase MinimumUntrustedScore (max_values: Some(1), max_size: None, mode: Measured) /// Storage: ElectionProviderMultiPhase Snapshot (r:1 w:0) /// Proof Skipped: ElectionProviderMultiPhase Snapshot (max_values: Some(1), max_size: None, mode: Measured) + /// Storage: ElectionProviderMultiPhase MinimumUntrustedScore (r:1 w:0) + /// Proof Skipped: ElectionProviderMultiPhase MinimumUntrustedScore (max_values: Some(1), max_size: None, mode: Measured) /// The range of component `v` is `[1000, 2000]`. /// The range of component `t` is `[500, 1000]`. /// The range of component `a` is `[500, 800]`. /// The range of component `d` is `[200, 400]`. fn submit_unsigned(v: u32, t: u32, a: u32, _d: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `251 + v * (553 ±0) + t * (32 ±0)` - // Estimated: `5222 + v * (3871 ±0) + t * (224 ±0)` - // Minimum execution time: 4_425_457 nanoseconds. - Weight::from_parts(4_445_889_000, 5222) - // Standard Error: 13_250 - .saturating_add(Weight::from_parts(48_844, 0).saturating_mul(v.into())) - // Standard Error: 39_266 - .saturating_add(Weight::from_parts(4_144_034, 0).saturating_mul(a.into())) + // Measured: `219 + v * (553 ±0) + t * (32 ±0)` + // Estimated: `11928 + v * (3871 ±0) + t * (224 ±0)` + // Minimum execution time: 4_702_825_000 picoseconds. + Weight::from_parts(4_752_015_000, 11928) + // Standard Error: 14_906 + .saturating_add(Weight::from_parts(131_585, 0).saturating_mul(v.into())) + // Standard Error: 44_174 + .saturating_add(Weight::from_parts(4_167_676, 0).saturating_mul(a.into())) .saturating_add(RocksDbWeight::get().reads(7_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) .saturating_add(Weight::from_parts(0, 3871).saturating_mul(v.into())) .saturating_add(Weight::from_parts(0, 224).saturating_mul(t.into())) } - /// Storage: ElectionProviderMultiPhase Round (r:1 w:0) - /// Proof Skipped: ElectionProviderMultiPhase Round (max_values: Some(1), max_size: None, mode: Measured) /// Storage: ElectionProviderMultiPhase DesiredTargets (r:1 w:0) /// Proof Skipped: ElectionProviderMultiPhase DesiredTargets (max_values: Some(1), max_size: None, mode: Measured) - /// Storage: ElectionProviderMultiPhase MinimumUntrustedScore (r:1 w:0) - /// Proof Skipped: ElectionProviderMultiPhase MinimumUntrustedScore (max_values: Some(1), max_size: None, mode: Measured) /// Storage: ElectionProviderMultiPhase Snapshot (r:1 w:0) /// Proof Skipped: ElectionProviderMultiPhase Snapshot (max_values: Some(1), max_size: None, mode: Measured) + /// Storage: ElectionProviderMultiPhase Round (r:1 w:0) + /// Proof Skipped: ElectionProviderMultiPhase Round (max_values: Some(1), max_size: None, mode: Measured) + /// Storage: ElectionProviderMultiPhase MinimumUntrustedScore (r:1 w:0) + /// Proof Skipped: ElectionProviderMultiPhase MinimumUntrustedScore (max_values: Some(1), max_size: None, mode: Measured) /// The range of component `v` is `[1000, 2000]`. /// The range of component `t` is `[500, 1000]`. /// The range of component `a` is `[500, 800]`. /// The range of component `d` is `[200, 400]`. fn feasibility_check(v: u32, t: u32, a: u32, _d: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `226 + v * (553 ±0) + t * (32 ±0)` - // Estimated: `2884 + v * (2212 ±0) + t * (128 ±0)` - // Minimum execution time: 3_812_071 nanoseconds. - Weight::from_parts(3_826_375_000, 2884) - // Standard Error: 11_601 - .saturating_add(Weight::from_parts(145_309, 0).saturating_mul(v.into())) - // Standard Error: 34_378 - .saturating_add(Weight::from_parts(3_223_977, 0).saturating_mul(a.into())) + // Measured: `194 + v * (553 ±0) + t * (32 ±0)` + // Estimated: `6716 + v * (2212 ±0) + t * (128 ±0)` + // Minimum execution time: 4_069_075_000 picoseconds. + Weight::from_parts(4_105_635_000, 6716) + // Standard Error: 12_539 + .saturating_add(Weight::from_parts(163_040, 0).saturating_mul(v.into())) + // Standard Error: 37_160 + .saturating_add(Weight::from_parts(3_343_858, 0).saturating_mul(a.into())) .saturating_add(RocksDbWeight::get().reads(4_u64)) .saturating_add(Weight::from_parts(0, 2212).saturating_mul(v.into())) .saturating_add(Weight::from_parts(0, 128).saturating_mul(t.into())) diff --git a/frame/elections-phragmen/src/weights.rs b/frame/elections-phragmen/src/weights.rs index bbe66d529..0ff6ecf29 100644 --- a/frame/elections-phragmen/src/weights.rs +++ b/frame/elections-phragmen/src/weights.rs @@ -18,26 +18,28 @@ //! Autogenerated weights for pallet_elections_phragmen //! //! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev -//! DATE: 2023-02-03, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! DATE: 2023-03-16, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` //! WORST CASE MAP SIZE: `1000000` -//! HOSTNAME: `runner-b3zmxxc-project-145-concurrent-0`, CPU: `Intel(R) Xeon(R) CPU @ 2.60GHz` +//! HOSTNAME: `bm3`, CPU: `Intel(R) Core(TM) i7-7700K CPU @ 4.20GHz` //! EXECUTION: Some(Wasm), WASM-EXECUTION: Compiled, CHAIN: Some("dev"), DB CACHE: 1024 // Executed Command: -// target/production/substrate +// ./target/production/substrate // benchmark // pallet +// --chain=dev // --steps=50 // --repeat=20 +// --pallet=pallet_elections_phragmen +// --no-storage-info +// --no-median-slopes +// --no-min-squares // --extrinsic=* // --execution=wasm // --wasm-execution=compiled // --heap-pages=4096 -// --json-file=/builds/parity/mirrors/substrate/.git/.artifacts/bench.json -// --pallet=pallet_elections_phragmen -// --chain=dev -// --header=./HEADER-APACHE2 // --output=./frame/elections-phragmen/src/weights.rs +// --header=./HEADER-APACHE2 // --template=./.maintain/frame-weight-template.hbs #![cfg_attr(rustfmt, rustfmt_skip)] @@ -79,12 +81,12 @@ impl WeightInfo for SubstrateWeight { /// The range of component `v` is `[1, 16]`. fn vote_equal(v: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `499 + v * (80 ±0)` - // Estimated: `9726 + v * (320 ±0)` - // Minimum execution time: 27_362 nanoseconds. - Weight::from_parts(28_497_963, 9726) - // Standard Error: 3_968 - .saturating_add(Weight::from_parts(176_840, 0).saturating_mul(v.into())) + // Measured: `403 + v * (80 ±0)` + // Estimated: `14292 + v * (320 ±0)` + // Minimum execution time: 30_206_000 picoseconds. + Weight::from_parts(30_696_455, 14292) + // Standard Error: 1_864 + .saturating_add(Weight::from_parts(124_941, 0).saturating_mul(v.into())) .saturating_add(T::DbWeight::get().reads(5_u64)) .saturating_add(T::DbWeight::get().writes(2_u64)) .saturating_add(Weight::from_parts(0, 320).saturating_mul(v.into())) @@ -102,12 +104,12 @@ impl WeightInfo for SubstrateWeight { /// The range of component `v` is `[2, 16]`. fn vote_more(v: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `467 + v * (80 ±0)` - // Estimated: `9598 + v * (320 ±0)` - // Minimum execution time: 37_120 nanoseconds. - Weight::from_parts(38_455_302, 9598) - // Standard Error: 5_478 - .saturating_add(Weight::from_parts(219_678, 0).saturating_mul(v.into())) + // Measured: `371 + v * (80 ±0)` + // Estimated: `14164 + v * (320 ±0)` + // Minimum execution time: 40_331_000 picoseconds. + Weight::from_parts(40_936_971, 14164) + // Standard Error: 2_377 + .saturating_add(Weight::from_parts(142_332, 0).saturating_mul(v.into())) .saturating_add(T::DbWeight::get().reads(5_u64)) .saturating_add(T::DbWeight::get().writes(2_u64)) .saturating_add(Weight::from_parts(0, 320).saturating_mul(v.into())) @@ -125,12 +127,12 @@ impl WeightInfo for SubstrateWeight { /// The range of component `v` is `[2, 16]`. fn vote_less(v: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `499 + v * (80 ±0)` - // Estimated: `9726 + v * (320 ±0)` - // Minimum execution time: 36_928 nanoseconds. - Weight::from_parts(38_334_669, 9726) - // Standard Error: 5_271 - .saturating_add(Weight::from_parts(232_355, 0).saturating_mul(v.into())) + // Measured: `403 + v * (80 ±0)` + // Estimated: `14292 + v * (320 ±0)` + // Minimum execution time: 40_343_000 picoseconds. + Weight::from_parts(41_055_673, 14292) + // Standard Error: 3_150 + .saturating_add(Weight::from_parts(129_894, 0).saturating_mul(v.into())) .saturating_add(T::DbWeight::get().reads(5_u64)) .saturating_add(T::DbWeight::get().writes(2_u64)) .saturating_add(Weight::from_parts(0, 320).saturating_mul(v.into())) @@ -141,10 +143,10 @@ impl WeightInfo for SubstrateWeight { /// Proof: Balances Locks (max_values: None, max_size: Some(1299), added: 3774, mode: MaxEncodedLen) fn remove_voter() -> Weight { // Proof Size summary in bytes: - // Measured: `989` - // Estimated: `7238` - // Minimum execution time: 34_338 nanoseconds. - Weight::from_parts(35_672_000, 7238) + // Measured: `925` + // Estimated: `9154` + // Minimum execution time: 36_348_000 picoseconds. + Weight::from_parts(36_677_000, 9154) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().writes(2_u64)) } @@ -157,12 +159,12 @@ impl WeightInfo for SubstrateWeight { /// The range of component `c` is `[1, 64]`. fn submit_candidacy(c: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `1697 + c * (48 ±0)` - // Estimated: `6576 + c * (144 ±0)` - // Minimum execution time: 31_864 nanoseconds. - Weight::from_parts(33_490_161, 6576) - // Standard Error: 2_643 - .saturating_add(Weight::from_parts(158_386, 0).saturating_mul(c.into())) + // Measured: `1570 + c * (48 ±0)` + // Estimated: `9165 + c * (144 ±0)` + // Minimum execution time: 32_963_000 picoseconds. + Weight::from_parts(33_778_406, 9165) + // Standard Error: 2_792 + .saturating_add(Weight::from_parts(71_214, 0).saturating_mul(c.into())) .saturating_add(T::DbWeight::get().reads(3_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) .saturating_add(Weight::from_parts(0, 144).saturating_mul(c.into())) @@ -172,12 +174,12 @@ impl WeightInfo for SubstrateWeight { /// The range of component `c` is `[1, 64]`. fn renounce_candidacy_candidate(c: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `349 + c * (48 ±0)` - // Estimated: `844 + c * (48 ±0)` - // Minimum execution time: 27_292 nanoseconds. - Weight::from_parts(28_364_955, 844) - // Standard Error: 1_335 - .saturating_add(Weight::from_parts(78_086, 0).saturating_mul(c.into())) + // Measured: `285 + c * (48 ±0)` + // Estimated: `1770 + c * (48 ±0)` + // Minimum execution time: 26_462_000 picoseconds. + Weight::from_parts(27_289_268, 1770) + // Standard Error: 915 + .saturating_add(Weight::from_parts(45_079, 0).saturating_mul(c.into())) .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) .saturating_add(Weight::from_parts(0, 48).saturating_mul(c.into())) @@ -194,10 +196,10 @@ impl WeightInfo for SubstrateWeight { /// Proof Skipped: Council Members (max_values: Some(1), max_size: None, mode: Measured) fn renounce_candidacy_members() -> Weight { // Proof Size summary in bytes: - // Measured: `2027` - // Estimated: `12115` - // Minimum execution time: 45_975 nanoseconds. - Weight::from_parts(47_103_000, 12115) + // Measured: `1900` + // Estimated: `15440` + // Minimum execution time: 44_414_000 picoseconds. + Weight::from_parts(44_900_000, 15440) .saturating_add(T::DbWeight::get().reads(4_u64)) .saturating_add(T::DbWeight::get().writes(4_u64)) } @@ -205,10 +207,10 @@ impl WeightInfo for SubstrateWeight { /// Proof Skipped: Elections RunnersUp (max_values: Some(1), max_size: None, mode: Measured) fn renounce_candidacy_runners_up() -> Weight { // Proof Size summary in bytes: - // Measured: `975` - // Estimated: `1470` - // Minimum execution time: 29_243 nanoseconds. - Weight::from_parts(30_582_000, 1470) + // Measured: `880` + // Estimated: `2365` + // Minimum execution time: 28_688_000 picoseconds. + Weight::from_parts(29_027_000, 2365) .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -218,7 +220,7 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 2_000_000_000 nanoseconds. + // Minimum execution time: 2_000_000_000_000 picoseconds. Weight::from_parts(2_000_000_000_000, 0) } /// Storage: Elections Members (r:1 w:1) @@ -235,10 +237,10 @@ impl WeightInfo for SubstrateWeight { /// Proof Skipped: Council Members (max_values: Some(1), max_size: None, mode: Measured) fn remove_member_with_replacement() -> Weight { // Proof Size summary in bytes: - // Measured: `2027` - // Estimated: `14718` - // Minimum execution time: 52_527 nanoseconds. - Weight::from_parts(53_538_000, 14718) + // Measured: `1900` + // Estimated: `19033` + // Minimum execution time: 50_280_000 picoseconds. + Weight::from_parts(50_906_000, 19033) .saturating_add(T::DbWeight::get().reads(5_u64)) .saturating_add(T::DbWeight::get().writes(5_u64)) } @@ -258,16 +260,16 @@ impl WeightInfo for SubstrateWeight { /// The range of component `d` is `[0, 256]`. fn clean_defunct_voters(v: u32, _d: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `1115 + v * (875 ±0)` - // Estimated: `8448 + v * (12352 ±0)` - // Minimum execution time: 14_934_185 nanoseconds. - Weight::from_parts(15_014_057_000, 8448) - // Standard Error: 245_588 - .saturating_add(Weight::from_parts(35_586_946, 0).saturating_mul(v.into())) + // Measured: `1115 + v * (811 ±0)` + // Estimated: `14388 + v * (12096 ±0)` + // Minimum execution time: 14_785_043_000 picoseconds. + Weight::from_parts(14_842_583_000, 14388) + // Standard Error: 242_757 + .saturating_add(Weight::from_parts(36_168_971, 0).saturating_mul(v.into())) .saturating_add(T::DbWeight::get().reads(4_u64)) .saturating_add(T::DbWeight::get().reads((3_u64).saturating_mul(v.into()))) .saturating_add(T::DbWeight::get().writes((3_u64).saturating_mul(v.into()))) - .saturating_add(Weight::from_parts(0, 12352).saturating_mul(v.into())) + .saturating_add(Weight::from_parts(0, 12096).saturating_mul(v.into())) } /// Storage: Elections Candidates (r:1 w:1) /// Proof Skipped: Elections Candidates (max_values: Some(1), max_size: None, mode: Measured) @@ -292,20 +294,20 @@ impl WeightInfo for SubstrateWeight { /// The range of component `e` is `[512, 8192]`. fn election_phragmen(c: u32, v: u32, e: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `0 + v * (638 ±0) + e * (28 ±0)` - // Estimated: `330033 + v * (5229 ±6) + e * (89 ±0) + c * (2135 ±7)` - // Minimum execution time: 1_273_671 nanoseconds. - Weight::from_parts(1_279_716_000, 330033) - // Standard Error: 543_277 - .saturating_add(Weight::from_parts(20_613_753, 0).saturating_mul(v.into())) - // Standard Error: 34_857 - .saturating_add(Weight::from_parts(688_354, 0).saturating_mul(e.into())) + // Measured: `0 + v * (606 ±0) + e * (28 ±0)` + // Estimated: `309059 + v * (5005 ±8) + e * (89 ±0) + c * (2135 ±7)` + // Minimum execution time: 1_074_961_000 picoseconds. + Weight::from_parts(1_079_460_000, 309059) + // Standard Error: 598_993 + .saturating_add(Weight::from_parts(17_097_981, 0).saturating_mul(v.into())) + // Standard Error: 38_432 + .saturating_add(Weight::from_parts(820_141, 0).saturating_mul(e.into())) .saturating_add(T::DbWeight::get().reads(21_u64)) .saturating_add(T::DbWeight::get().reads((1_u64).saturating_mul(c.into()))) .saturating_add(T::DbWeight::get().reads((1_u64).saturating_mul(v.into()))) .saturating_add(T::DbWeight::get().writes(6_u64)) .saturating_add(T::DbWeight::get().writes((1_u64).saturating_mul(c.into()))) - .saturating_add(Weight::from_parts(0, 5229).saturating_mul(v.into())) + .saturating_add(Weight::from_parts(0, 5005).saturating_mul(v.into())) .saturating_add(Weight::from_parts(0, 89).saturating_mul(e.into())) .saturating_add(Weight::from_parts(0, 2135).saturating_mul(c.into())) } @@ -326,12 +328,12 @@ impl WeightInfo for () { /// The range of component `v` is `[1, 16]`. fn vote_equal(v: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `499 + v * (80 ±0)` - // Estimated: `9726 + v * (320 ±0)` - // Minimum execution time: 27_362 nanoseconds. - Weight::from_parts(28_497_963, 9726) - // Standard Error: 3_968 - .saturating_add(Weight::from_parts(176_840, 0).saturating_mul(v.into())) + // Measured: `403 + v * (80 ±0)` + // Estimated: `14292 + v * (320 ±0)` + // Minimum execution time: 30_206_000 picoseconds. + Weight::from_parts(30_696_455, 14292) + // Standard Error: 1_864 + .saturating_add(Weight::from_parts(124_941, 0).saturating_mul(v.into())) .saturating_add(RocksDbWeight::get().reads(5_u64)) .saturating_add(RocksDbWeight::get().writes(2_u64)) .saturating_add(Weight::from_parts(0, 320).saturating_mul(v.into())) @@ -349,12 +351,12 @@ impl WeightInfo for () { /// The range of component `v` is `[2, 16]`. fn vote_more(v: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `467 + v * (80 ±0)` - // Estimated: `9598 + v * (320 ±0)` - // Minimum execution time: 37_120 nanoseconds. - Weight::from_parts(38_455_302, 9598) - // Standard Error: 5_478 - .saturating_add(Weight::from_parts(219_678, 0).saturating_mul(v.into())) + // Measured: `371 + v * (80 ±0)` + // Estimated: `14164 + v * (320 ±0)` + // Minimum execution time: 40_331_000 picoseconds. + Weight::from_parts(40_936_971, 14164) + // Standard Error: 2_377 + .saturating_add(Weight::from_parts(142_332, 0).saturating_mul(v.into())) .saturating_add(RocksDbWeight::get().reads(5_u64)) .saturating_add(RocksDbWeight::get().writes(2_u64)) .saturating_add(Weight::from_parts(0, 320).saturating_mul(v.into())) @@ -372,12 +374,12 @@ impl WeightInfo for () { /// The range of component `v` is `[2, 16]`. fn vote_less(v: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `499 + v * (80 ±0)` - // Estimated: `9726 + v * (320 ±0)` - // Minimum execution time: 36_928 nanoseconds. - Weight::from_parts(38_334_669, 9726) - // Standard Error: 5_271 - .saturating_add(Weight::from_parts(232_355, 0).saturating_mul(v.into())) + // Measured: `403 + v * (80 ±0)` + // Estimated: `14292 + v * (320 ±0)` + // Minimum execution time: 40_343_000 picoseconds. + Weight::from_parts(41_055_673, 14292) + // Standard Error: 3_150 + .saturating_add(Weight::from_parts(129_894, 0).saturating_mul(v.into())) .saturating_add(RocksDbWeight::get().reads(5_u64)) .saturating_add(RocksDbWeight::get().writes(2_u64)) .saturating_add(Weight::from_parts(0, 320).saturating_mul(v.into())) @@ -388,10 +390,10 @@ impl WeightInfo for () { /// Proof: Balances Locks (max_values: None, max_size: Some(1299), added: 3774, mode: MaxEncodedLen) fn remove_voter() -> Weight { // Proof Size summary in bytes: - // Measured: `989` - // Estimated: `7238` - // Minimum execution time: 34_338 nanoseconds. - Weight::from_parts(35_672_000, 7238) + // Measured: `925` + // Estimated: `9154` + // Minimum execution time: 36_348_000 picoseconds. + Weight::from_parts(36_677_000, 9154) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().writes(2_u64)) } @@ -404,12 +406,12 @@ impl WeightInfo for () { /// The range of component `c` is `[1, 64]`. fn submit_candidacy(c: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `1697 + c * (48 ±0)` - // Estimated: `6576 + c * (144 ±0)` - // Minimum execution time: 31_864 nanoseconds. - Weight::from_parts(33_490_161, 6576) - // Standard Error: 2_643 - .saturating_add(Weight::from_parts(158_386, 0).saturating_mul(c.into())) + // Measured: `1570 + c * (48 ±0)` + // Estimated: `9165 + c * (144 ±0)` + // Minimum execution time: 32_963_000 picoseconds. + Weight::from_parts(33_778_406, 9165) + // Standard Error: 2_792 + .saturating_add(Weight::from_parts(71_214, 0).saturating_mul(c.into())) .saturating_add(RocksDbWeight::get().reads(3_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) .saturating_add(Weight::from_parts(0, 144).saturating_mul(c.into())) @@ -419,12 +421,12 @@ impl WeightInfo for () { /// The range of component `c` is `[1, 64]`. fn renounce_candidacy_candidate(c: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `349 + c * (48 ±0)` - // Estimated: `844 + c * (48 ±0)` - // Minimum execution time: 27_292 nanoseconds. - Weight::from_parts(28_364_955, 844) - // Standard Error: 1_335 - .saturating_add(Weight::from_parts(78_086, 0).saturating_mul(c.into())) + // Measured: `285 + c * (48 ±0)` + // Estimated: `1770 + c * (48 ±0)` + // Minimum execution time: 26_462_000 picoseconds. + Weight::from_parts(27_289_268, 1770) + // Standard Error: 915 + .saturating_add(Weight::from_parts(45_079, 0).saturating_mul(c.into())) .saturating_add(RocksDbWeight::get().reads(1_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) .saturating_add(Weight::from_parts(0, 48).saturating_mul(c.into())) @@ -441,10 +443,10 @@ impl WeightInfo for () { /// Proof Skipped: Council Members (max_values: Some(1), max_size: None, mode: Measured) fn renounce_candidacy_members() -> Weight { // Proof Size summary in bytes: - // Measured: `2027` - // Estimated: `12115` - // Minimum execution time: 45_975 nanoseconds. - Weight::from_parts(47_103_000, 12115) + // Measured: `1900` + // Estimated: `15440` + // Minimum execution time: 44_414_000 picoseconds. + Weight::from_parts(44_900_000, 15440) .saturating_add(RocksDbWeight::get().reads(4_u64)) .saturating_add(RocksDbWeight::get().writes(4_u64)) } @@ -452,10 +454,10 @@ impl WeightInfo for () { /// Proof Skipped: Elections RunnersUp (max_values: Some(1), max_size: None, mode: Measured) fn renounce_candidacy_runners_up() -> Weight { // Proof Size summary in bytes: - // Measured: `975` - // Estimated: `1470` - // Minimum execution time: 29_243 nanoseconds. - Weight::from_parts(30_582_000, 1470) + // Measured: `880` + // Estimated: `2365` + // Minimum execution time: 28_688_000 picoseconds. + Weight::from_parts(29_027_000, 2365) .saturating_add(RocksDbWeight::get().reads(1_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } @@ -465,7 +467,7 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 2_000_000_000 nanoseconds. + // Minimum execution time: 2_000_000_000_000 picoseconds. Weight::from_parts(2_000_000_000_000, 0) } /// Storage: Elections Members (r:1 w:1) @@ -482,10 +484,10 @@ impl WeightInfo for () { /// Proof Skipped: Council Members (max_values: Some(1), max_size: None, mode: Measured) fn remove_member_with_replacement() -> Weight { // Proof Size summary in bytes: - // Measured: `2027` - // Estimated: `14718` - // Minimum execution time: 52_527 nanoseconds. - Weight::from_parts(53_538_000, 14718) + // Measured: `1900` + // Estimated: `19033` + // Minimum execution time: 50_280_000 picoseconds. + Weight::from_parts(50_906_000, 19033) .saturating_add(RocksDbWeight::get().reads(5_u64)) .saturating_add(RocksDbWeight::get().writes(5_u64)) } @@ -505,16 +507,16 @@ impl WeightInfo for () { /// The range of component `d` is `[0, 256]`. fn clean_defunct_voters(v: u32, _d: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `1115 + v * (875 ±0)` - // Estimated: `8448 + v * (12352 ±0)` - // Minimum execution time: 14_934_185 nanoseconds. - Weight::from_parts(15_014_057_000, 8448) - // Standard Error: 245_588 - .saturating_add(Weight::from_parts(35_586_946, 0).saturating_mul(v.into())) + // Measured: `1115 + v * (811 ±0)` + // Estimated: `14388 + v * (12096 ±0)` + // Minimum execution time: 14_785_043_000 picoseconds. + Weight::from_parts(14_842_583_000, 14388) + // Standard Error: 242_757 + .saturating_add(Weight::from_parts(36_168_971, 0).saturating_mul(v.into())) .saturating_add(RocksDbWeight::get().reads(4_u64)) .saturating_add(RocksDbWeight::get().reads((3_u64).saturating_mul(v.into()))) .saturating_add(RocksDbWeight::get().writes((3_u64).saturating_mul(v.into()))) - .saturating_add(Weight::from_parts(0, 12352).saturating_mul(v.into())) + .saturating_add(Weight::from_parts(0, 12096).saturating_mul(v.into())) } /// Storage: Elections Candidates (r:1 w:1) /// Proof Skipped: Elections Candidates (max_values: Some(1), max_size: None, mode: Measured) @@ -539,20 +541,20 @@ impl WeightInfo for () { /// The range of component `e` is `[512, 8192]`. fn election_phragmen(c: u32, v: u32, e: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `0 + v * (638 ±0) + e * (28 ±0)` - // Estimated: `330033 + v * (5229 ±6) + e * (89 ±0) + c * (2135 ±7)` - // Minimum execution time: 1_273_671 nanoseconds. - Weight::from_parts(1_279_716_000, 330033) - // Standard Error: 543_277 - .saturating_add(Weight::from_parts(20_613_753, 0).saturating_mul(v.into())) - // Standard Error: 34_857 - .saturating_add(Weight::from_parts(688_354, 0).saturating_mul(e.into())) + // Measured: `0 + v * (606 ±0) + e * (28 ±0)` + // Estimated: `309059 + v * (5005 ±8) + e * (89 ±0) + c * (2135 ±7)` + // Minimum execution time: 1_074_961_000 picoseconds. + Weight::from_parts(1_079_460_000, 309059) + // Standard Error: 598_993 + .saturating_add(Weight::from_parts(17_097_981, 0).saturating_mul(v.into())) + // Standard Error: 38_432 + .saturating_add(Weight::from_parts(820_141, 0).saturating_mul(e.into())) .saturating_add(RocksDbWeight::get().reads(21_u64)) .saturating_add(RocksDbWeight::get().reads((1_u64).saturating_mul(c.into()))) .saturating_add(RocksDbWeight::get().reads((1_u64).saturating_mul(v.into()))) .saturating_add(RocksDbWeight::get().writes(6_u64)) .saturating_add(RocksDbWeight::get().writes((1_u64).saturating_mul(c.into()))) - .saturating_add(Weight::from_parts(0, 5229).saturating_mul(v.into())) + .saturating_add(Weight::from_parts(0, 5005).saturating_mul(v.into())) .saturating_add(Weight::from_parts(0, 89).saturating_mul(e.into())) .saturating_add(Weight::from_parts(0, 2135).saturating_mul(c.into())) } diff --git a/frame/fast-unstake/src/weights.rs b/frame/fast-unstake/src/weights.rs index ced7acb95..b47d4f056 100644 --- a/frame/fast-unstake/src/weights.rs +++ b/frame/fast-unstake/src/weights.rs @@ -18,25 +18,28 @@ //! Autogenerated weights for pallet_fast_unstake //! //! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev -//! DATE: 2023-01-25, STEPS: `50`, REPEAT: 20, LOW RANGE: `[]`, HIGH RANGE: `[]` -//! HOSTNAME: `runner-b3zmxxc-project-145-concurrent-0`, CPU: `Intel(R) Xeon(R) CPU @ 2.60GHz` +//! DATE: 2023-03-16, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! WORST CASE MAP SIZE: `1000000` +//! HOSTNAME: `bm3`, CPU: `Intel(R) Core(TM) i7-7700K CPU @ 4.20GHz` //! EXECUTION: Some(Wasm), WASM-EXECUTION: Compiled, CHAIN: Some("dev"), DB CACHE: 1024 // Executed Command: -// target/production/substrate +// ./target/production/substrate // benchmark // pallet +// --chain=dev // --steps=50 // --repeat=20 +// --pallet=pallet_fast_unstake +// --no-storage-info +// --no-median-slopes +// --no-min-squares // --extrinsic=* // --execution=wasm // --wasm-execution=compiled // --heap-pages=4096 -// --json-file=/builds/parity/mirrors/substrate/.git/.artifacts/bench.json -// --pallet=pallet_fast_unstake -// --chain=dev -// --header=./HEADER-APACHE2 // --output=./frame/fast-unstake/src/weights.rs +// --header=./HEADER-APACHE2 // --template=./.maintain/frame-weight-template.hbs #![cfg_attr(rustfmt, rustfmt_skip)] @@ -58,172 +61,290 @@ pub trait WeightInfo { /// Weights for pallet_fast_unstake using the Substrate node and recommended hardware. pub struct SubstrateWeight(PhantomData); impl WeightInfo for SubstrateWeight { - // Storage: FastUnstake ErasToCheckPerBlock (r:1 w:0) - // Storage: Staking ValidatorCount (r:1 w:0) - // Storage: FastUnstake Head (r:1 w:1) - // Storage: FastUnstake CounterForQueue (r:1 w:0) - // Storage: ElectionProviderMultiPhase CurrentPhase (r:1 w:0) - // Storage: Staking CurrentEra (r:1 w:0) - // Storage: Staking SlashingSpans (r:1 w:0) - // Storage: Staking Bonded (r:1 w:1) - // Storage: Staking Validators (r:1 w:0) - // Storage: Staking Nominators (r:1 w:0) - // Storage: System Account (r:1 w:1) - // Storage: Balances Locks (r:1 w:1) - // Storage: Staking Ledger (r:0 w:1) - // Storage: Staking Payee (r:0 w:1) + /// Storage: FastUnstake ErasToCheckPerBlock (r:1 w:0) + /// Proof: FastUnstake ErasToCheckPerBlock (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) + /// Storage: Staking ValidatorCount (r:1 w:0) + /// Proof: Staking ValidatorCount (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) + /// Storage: FastUnstake Head (r:1 w:1) + /// Proof: FastUnstake Head (max_values: Some(1), max_size: Some(5768), added: 6263, mode: MaxEncodedLen) + /// Storage: FastUnstake CounterForQueue (r:1 w:0) + /// Proof: FastUnstake CounterForQueue (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) + /// Storage: ElectionProviderMultiPhase CurrentPhase (r:1 w:0) + /// Proof Skipped: ElectionProviderMultiPhase CurrentPhase (max_values: Some(1), max_size: None, mode: Measured) + /// Storage: Staking CurrentEra (r:1 w:0) + /// Proof: Staking CurrentEra (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) + /// Storage: Staking SlashingSpans (r:64 w:0) + /// Proof Skipped: Staking SlashingSpans (max_values: None, max_size: None, mode: Measured) + /// Storage: Staking Bonded (r:64 w:64) + /// Proof: Staking Bonded (max_values: None, max_size: Some(72), added: 2547, mode: MaxEncodedLen) + /// Storage: Staking Validators (r:64 w:0) + /// Proof: Staking Validators (max_values: None, max_size: Some(45), added: 2520, mode: MaxEncodedLen) + /// Storage: Staking Nominators (r:64 w:0) + /// Proof: Staking Nominators (max_values: None, max_size: Some(558), added: 3033, mode: MaxEncodedLen) + /// Storage: System Account (r:64 w:64) + /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) + /// Storage: Balances Locks (r:64 w:64) + /// Proof: Balances Locks (max_values: None, max_size: Some(1299), added: 3774, mode: MaxEncodedLen) + /// Storage: Staking Ledger (r:0 w:64) + /// Proof: Staking Ledger (max_values: None, max_size: Some(1091), added: 3566, mode: MaxEncodedLen) + /// Storage: Staking Payee (r:0 w:64) + /// Proof: Staking Payee (max_values: None, max_size: Some(73), added: 2548, mode: MaxEncodedLen) /// The range of component `b` is `[1, 64]`. fn on_idle_unstake(b: u32, ) -> Weight { - // Minimum execution time: 92_833 nanoseconds. - Weight::from_parts(62_136_346, 0) - // Standard Error: 25_541 - .saturating_add(Weight::from_parts(42_904_859, 0).saturating_mul(b.into())) - .saturating_add(T::DbWeight::get().reads(6)) + // Proof Size summary in bytes: + // Measured: `1344 + b * (343 ±0)` + // Estimated: `23254 + b * (17642 ±0)` + // Minimum execution time: 78_484_000 picoseconds. + Weight::from_parts(39_186_541, 23254) + // Standard Error: 37_606 + .saturating_add(Weight::from_parts(42_433_199, 0).saturating_mul(b.into())) + .saturating_add(T::DbWeight::get().reads(6_u64)) .saturating_add(T::DbWeight::get().reads((6_u64).saturating_mul(b.into()))) - .saturating_add(T::DbWeight::get().writes(1)) + .saturating_add(T::DbWeight::get().writes(1_u64)) .saturating_add(T::DbWeight::get().writes((5_u64).saturating_mul(b.into()))) + .saturating_add(Weight::from_parts(0, 17642).saturating_mul(b.into())) } - // Storage: FastUnstake ErasToCheckPerBlock (r:1 w:0) - // Storage: Staking ValidatorCount (r:1 w:0) - // Storage: FastUnstake Head (r:1 w:1) - // Storage: FastUnstake CounterForQueue (r:1 w:0) - // Storage: ElectionProviderMultiPhase CurrentPhase (r:1 w:0) - // Storage: Staking CurrentEra (r:1 w:0) - // Storage: Staking ErasStakers (r:2 w:0) + /// Storage: FastUnstake ErasToCheckPerBlock (r:1 w:0) + /// Proof: FastUnstake ErasToCheckPerBlock (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) + /// Storage: Staking ValidatorCount (r:1 w:0) + /// Proof: Staking ValidatorCount (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) + /// Storage: FastUnstake Head (r:1 w:1) + /// Proof: FastUnstake Head (max_values: Some(1), max_size: Some(5768), added: 6263, mode: MaxEncodedLen) + /// Storage: FastUnstake CounterForQueue (r:1 w:0) + /// Proof: FastUnstake CounterForQueue (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) + /// Storage: ElectionProviderMultiPhase CurrentPhase (r:1 w:0) + /// Proof Skipped: ElectionProviderMultiPhase CurrentPhase (max_values: Some(1), max_size: None, mode: Measured) + /// Storage: Staking CurrentEra (r:1 w:0) + /// Proof: Staking CurrentEra (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) + /// Storage: Staking ErasStakers (r:257 w:0) + /// Proof Skipped: Staking ErasStakers (max_values: None, max_size: None, mode: Measured) /// The range of component `v` is `[1, 256]`. /// The range of component `b` is `[1, 64]`. fn on_idle_check(v: u32, b: u32, ) -> Weight { - // Minimum execution time: 1_775_293 nanoseconds. - Weight::from_parts(1_787_133_000, 0) - // Standard Error: 17_109_142 - .saturating_add(Weight::from_parts(546_766_552, 0).saturating_mul(v.into())) - // Standard Error: 68_455_625 - .saturating_add(Weight::from_parts(2_135_980_830, 0).saturating_mul(b.into())) - .saturating_add(T::DbWeight::get().reads(7)) + // Proof Size summary in bytes: + // Measured: `1512 + v * (10037 ±0) + b * (48 ±0)` + // Estimated: `20889 + v * (22551 ±0) + b * (98 ±0)` + // Minimum execution time: 1_538_727_000 picoseconds. + Weight::from_parts(1_549_586_000, 20889) + // Standard Error: 14_184_223 + .saturating_add(Weight::from_parts(452_966_006, 0).saturating_mul(v.into())) + // Standard Error: 56_752_692 + .saturating_add(Weight::from_parts(1_775_601_329, 0).saturating_mul(b.into())) + .saturating_add(T::DbWeight::get().reads(7_u64)) .saturating_add(T::DbWeight::get().reads((1_u64).saturating_mul(v.into()))) - .saturating_add(T::DbWeight::get().writes(1)) + .saturating_add(T::DbWeight::get().writes(1_u64)) + .saturating_add(Weight::from_parts(0, 22551).saturating_mul(v.into())) + .saturating_add(Weight::from_parts(0, 98).saturating_mul(b.into())) } - // Storage: FastUnstake ErasToCheckPerBlock (r:1 w:0) - // Storage: Staking Ledger (r:1 w:1) - // Storage: FastUnstake Queue (r:1 w:1) - // Storage: FastUnstake Head (r:1 w:0) - // Storage: Staking Bonded (r:1 w:0) - // Storage: Staking Validators (r:1 w:0) - // Storage: Staking Nominators (r:1 w:1) - // Storage: Staking CounterForNominators (r:1 w:1) - // Storage: VoterList ListNodes (r:1 w:1) - // Storage: VoterList ListBags (r:1 w:1) - // Storage: VoterList CounterForListNodes (r:1 w:1) - // Storage: Staking CurrentEra (r:1 w:0) - // Storage: Balances Locks (r:1 w:1) - // Storage: FastUnstake CounterForQueue (r:1 w:1) + /// Storage: FastUnstake ErasToCheckPerBlock (r:1 w:0) + /// Proof: FastUnstake ErasToCheckPerBlock (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) + /// Storage: Staking Ledger (r:1 w:1) + /// Proof: Staking Ledger (max_values: None, max_size: Some(1091), added: 3566, mode: MaxEncodedLen) + /// Storage: FastUnstake Queue (r:1 w:1) + /// Proof: FastUnstake Queue (max_values: None, max_size: Some(56), added: 2531, mode: MaxEncodedLen) + /// Storage: FastUnstake Head (r:1 w:0) + /// Proof: FastUnstake Head (max_values: Some(1), max_size: Some(5768), added: 6263, mode: MaxEncodedLen) + /// Storage: Staking Bonded (r:1 w:0) + /// Proof: Staking Bonded (max_values: None, max_size: Some(72), added: 2547, mode: MaxEncodedLen) + /// Storage: Staking Validators (r:1 w:0) + /// Proof: Staking Validators (max_values: None, max_size: Some(45), added: 2520, mode: MaxEncodedLen) + /// Storage: Staking Nominators (r:1 w:1) + /// Proof: Staking Nominators (max_values: None, max_size: Some(558), added: 3033, mode: MaxEncodedLen) + /// Storage: Staking CounterForNominators (r:1 w:1) + /// Proof: Staking CounterForNominators (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) + /// Storage: VoterList ListNodes (r:1 w:1) + /// Proof: VoterList ListNodes (max_values: None, max_size: Some(154), added: 2629, mode: MaxEncodedLen) + /// Storage: VoterList ListBags (r:1 w:1) + /// Proof: VoterList ListBags (max_values: None, max_size: Some(82), added: 2557, mode: MaxEncodedLen) + /// Storage: VoterList CounterForListNodes (r:1 w:1) + /// Proof: VoterList CounterForListNodes (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) + /// Storage: Staking CurrentEra (r:1 w:0) + /// Proof: Staking CurrentEra (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) + /// Storage: Balances Locks (r:1 w:1) + /// Proof: Balances Locks (max_values: None, max_size: Some(1299), added: 3774, mode: MaxEncodedLen) + /// Storage: FastUnstake CounterForQueue (r:1 w:1) + /// Proof: FastUnstake CounterForQueue (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) fn register_fast_unstake() -> Weight { - // Minimum execution time: 124_849 nanoseconds. - Weight::from_parts(128_176_000, 0) - .saturating_add(T::DbWeight::get().reads(14)) - .saturating_add(T::DbWeight::get().writes(9)) + // Proof Size summary in bytes: + // Measured: `1964` + // Estimated: `45775` + // Minimum execution time: 117_552_000 picoseconds. + Weight::from_parts(118_303_000, 45775) + .saturating_add(T::DbWeight::get().reads(14_u64)) + .saturating_add(T::DbWeight::get().writes(9_u64)) } - // Storage: FastUnstake ErasToCheckPerBlock (r:1 w:0) - // Storage: Staking Ledger (r:1 w:0) - // Storage: FastUnstake Queue (r:1 w:1) - // Storage: FastUnstake Head (r:1 w:0) - // Storage: FastUnstake CounterForQueue (r:1 w:1) + /// Storage: FastUnstake ErasToCheckPerBlock (r:1 w:0) + /// Proof: FastUnstake ErasToCheckPerBlock (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) + /// Storage: Staking Ledger (r:1 w:0) + /// Proof: Staking Ledger (max_values: None, max_size: Some(1091), added: 3566, mode: MaxEncodedLen) + /// Storage: FastUnstake Queue (r:1 w:1) + /// Proof: FastUnstake Queue (max_values: None, max_size: Some(56), added: 2531, mode: MaxEncodedLen) + /// Storage: FastUnstake Head (r:1 w:0) + /// Proof: FastUnstake Head (max_values: Some(1), max_size: Some(5768), added: 6263, mode: MaxEncodedLen) + /// Storage: FastUnstake CounterForQueue (r:1 w:1) + /// Proof: FastUnstake CounterForQueue (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) fn deregister() -> Weight { - // Minimum execution time: 48_246 nanoseconds. - Weight::from_parts(49_720_000, 0) - .saturating_add(T::DbWeight::get().reads(5)) - .saturating_add(T::DbWeight::get().writes(2)) + // Proof Size summary in bytes: + // Measured: `1223` + // Estimated: `18308` + // Minimum execution time: 42_291_000 picoseconds. + Weight::from_parts(42_835_000, 18308) + .saturating_add(T::DbWeight::get().reads(5_u64)) + .saturating_add(T::DbWeight::get().writes(2_u64)) } - // Storage: FastUnstake ErasToCheckPerBlock (r:0 w:1) + /// Storage: FastUnstake ErasToCheckPerBlock (r:0 w:1) + /// Proof: FastUnstake ErasToCheckPerBlock (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) fn control() -> Weight { - // Minimum execution time: 4_611 nanoseconds. - Weight::from_parts(4_844_000, 0) - .saturating_add(T::DbWeight::get().writes(1)) + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 3_551_000 picoseconds. + Weight::from_parts(3_632_000, 0) + .saturating_add(T::DbWeight::get().writes(1_u64)) } } // For backwards compatibility and tests impl WeightInfo for () { - // Storage: FastUnstake ErasToCheckPerBlock (r:1 w:0) - // Storage: Staking ValidatorCount (r:1 w:0) - // Storage: FastUnstake Head (r:1 w:1) - // Storage: FastUnstake CounterForQueue (r:1 w:0) - // Storage: ElectionProviderMultiPhase CurrentPhase (r:1 w:0) - // Storage: Staking CurrentEra (r:1 w:0) - // Storage: Staking SlashingSpans (r:1 w:0) - // Storage: Staking Bonded (r:1 w:1) - // Storage: Staking Validators (r:1 w:0) - // Storage: Staking Nominators (r:1 w:0) - // Storage: System Account (r:1 w:1) - // Storage: Balances Locks (r:1 w:1) - // Storage: Staking Ledger (r:0 w:1) - // Storage: Staking Payee (r:0 w:1) + /// Storage: FastUnstake ErasToCheckPerBlock (r:1 w:0) + /// Proof: FastUnstake ErasToCheckPerBlock (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) + /// Storage: Staking ValidatorCount (r:1 w:0) + /// Proof: Staking ValidatorCount (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) + /// Storage: FastUnstake Head (r:1 w:1) + /// Proof: FastUnstake Head (max_values: Some(1), max_size: Some(5768), added: 6263, mode: MaxEncodedLen) + /// Storage: FastUnstake CounterForQueue (r:1 w:0) + /// Proof: FastUnstake CounterForQueue (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) + /// Storage: ElectionProviderMultiPhase CurrentPhase (r:1 w:0) + /// Proof Skipped: ElectionProviderMultiPhase CurrentPhase (max_values: Some(1), max_size: None, mode: Measured) + /// Storage: Staking CurrentEra (r:1 w:0) + /// Proof: Staking CurrentEra (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) + /// Storage: Staking SlashingSpans (r:64 w:0) + /// Proof Skipped: Staking SlashingSpans (max_values: None, max_size: None, mode: Measured) + /// Storage: Staking Bonded (r:64 w:64) + /// Proof: Staking Bonded (max_values: None, max_size: Some(72), added: 2547, mode: MaxEncodedLen) + /// Storage: Staking Validators (r:64 w:0) + /// Proof: Staking Validators (max_values: None, max_size: Some(45), added: 2520, mode: MaxEncodedLen) + /// Storage: Staking Nominators (r:64 w:0) + /// Proof: Staking Nominators (max_values: None, max_size: Some(558), added: 3033, mode: MaxEncodedLen) + /// Storage: System Account (r:64 w:64) + /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) + /// Storage: Balances Locks (r:64 w:64) + /// Proof: Balances Locks (max_values: None, max_size: Some(1299), added: 3774, mode: MaxEncodedLen) + /// Storage: Staking Ledger (r:0 w:64) + /// Proof: Staking Ledger (max_values: None, max_size: Some(1091), added: 3566, mode: MaxEncodedLen) + /// Storage: Staking Payee (r:0 w:64) + /// Proof: Staking Payee (max_values: None, max_size: Some(73), added: 2548, mode: MaxEncodedLen) /// The range of component `b` is `[1, 64]`. fn on_idle_unstake(b: u32, ) -> Weight { - // Minimum execution time: 92_833 nanoseconds. - Weight::from_parts(62_136_346, 0) - // Standard Error: 25_541 - .saturating_add(Weight::from_parts(42_904_859, 0).saturating_mul(b.into())) - .saturating_add(RocksDbWeight::get().reads(6)) + // Proof Size summary in bytes: + // Measured: `1344 + b * (343 ±0)` + // Estimated: `23254 + b * (17642 ±0)` + // Minimum execution time: 78_484_000 picoseconds. + Weight::from_parts(39_186_541, 23254) + // Standard Error: 37_606 + .saturating_add(Weight::from_parts(42_433_199, 0).saturating_mul(b.into())) + .saturating_add(RocksDbWeight::get().reads(6_u64)) .saturating_add(RocksDbWeight::get().reads((6_u64).saturating_mul(b.into()))) - .saturating_add(RocksDbWeight::get().writes(1)) + .saturating_add(RocksDbWeight::get().writes(1_u64)) .saturating_add(RocksDbWeight::get().writes((5_u64).saturating_mul(b.into()))) + .saturating_add(Weight::from_parts(0, 17642).saturating_mul(b.into())) } - // Storage: FastUnstake ErasToCheckPerBlock (r:1 w:0) - // Storage: Staking ValidatorCount (r:1 w:0) - // Storage: FastUnstake Head (r:1 w:1) - // Storage: FastUnstake CounterForQueue (r:1 w:0) - // Storage: ElectionProviderMultiPhase CurrentPhase (r:1 w:0) - // Storage: Staking CurrentEra (r:1 w:0) - // Storage: Staking ErasStakers (r:2 w:0) + /// Storage: FastUnstake ErasToCheckPerBlock (r:1 w:0) + /// Proof: FastUnstake ErasToCheckPerBlock (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) + /// Storage: Staking ValidatorCount (r:1 w:0) + /// Proof: Staking ValidatorCount (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) + /// Storage: FastUnstake Head (r:1 w:1) + /// Proof: FastUnstake Head (max_values: Some(1), max_size: Some(5768), added: 6263, mode: MaxEncodedLen) + /// Storage: FastUnstake CounterForQueue (r:1 w:0) + /// Proof: FastUnstake CounterForQueue (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) + /// Storage: ElectionProviderMultiPhase CurrentPhase (r:1 w:0) + /// Proof Skipped: ElectionProviderMultiPhase CurrentPhase (max_values: Some(1), max_size: None, mode: Measured) + /// Storage: Staking CurrentEra (r:1 w:0) + /// Proof: Staking CurrentEra (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) + /// Storage: Staking ErasStakers (r:257 w:0) + /// Proof Skipped: Staking ErasStakers (max_values: None, max_size: None, mode: Measured) /// The range of component `v` is `[1, 256]`. /// The range of component `b` is `[1, 64]`. fn on_idle_check(v: u32, b: u32, ) -> Weight { - // Minimum execution time: 1_775_293 nanoseconds. - Weight::from_parts(1_787_133_000, 0) - // Standard Error: 17_109_142 - .saturating_add(Weight::from_parts(546_766_552, 0).saturating_mul(v.into())) - // Standard Error: 68_455_625 - .saturating_add(Weight::from_parts(2_135_980_830, 0).saturating_mul(b.into())) - .saturating_add(RocksDbWeight::get().reads(7)) + // Proof Size summary in bytes: + // Measured: `1512 + v * (10037 ±0) + b * (48 ±0)` + // Estimated: `20889 + v * (22551 ±0) + b * (98 ±0)` + // Minimum execution time: 1_538_727_000 picoseconds. + Weight::from_parts(1_549_586_000, 20889) + // Standard Error: 14_184_223 + .saturating_add(Weight::from_parts(452_966_006, 0).saturating_mul(v.into())) + // Standard Error: 56_752_692 + .saturating_add(Weight::from_parts(1_775_601_329, 0).saturating_mul(b.into())) + .saturating_add(RocksDbWeight::get().reads(7_u64)) .saturating_add(RocksDbWeight::get().reads((1_u64).saturating_mul(v.into()))) - .saturating_add(RocksDbWeight::get().writes(1)) + .saturating_add(RocksDbWeight::get().writes(1_u64)) + .saturating_add(Weight::from_parts(0, 22551).saturating_mul(v.into())) + .saturating_add(Weight::from_parts(0, 98).saturating_mul(b.into())) } - // Storage: FastUnstake ErasToCheckPerBlock (r:1 w:0) - // Storage: Staking Ledger (r:1 w:1) - // Storage: FastUnstake Queue (r:1 w:1) - // Storage: FastUnstake Head (r:1 w:0) - // Storage: Staking Bonded (r:1 w:0) - // Storage: Staking Validators (r:1 w:0) - // Storage: Staking Nominators (r:1 w:1) - // Storage: Staking CounterForNominators (r:1 w:1) - // Storage: VoterList ListNodes (r:1 w:1) - // Storage: VoterList ListBags (r:1 w:1) - // Storage: VoterList CounterForListNodes (r:1 w:1) - // Storage: Staking CurrentEra (r:1 w:0) - // Storage: Balances Locks (r:1 w:1) - // Storage: FastUnstake CounterForQueue (r:1 w:1) + /// Storage: FastUnstake ErasToCheckPerBlock (r:1 w:0) + /// Proof: FastUnstake ErasToCheckPerBlock (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) + /// Storage: Staking Ledger (r:1 w:1) + /// Proof: Staking Ledger (max_values: None, max_size: Some(1091), added: 3566, mode: MaxEncodedLen) + /// Storage: FastUnstake Queue (r:1 w:1) + /// Proof: FastUnstake Queue (max_values: None, max_size: Some(56), added: 2531, mode: MaxEncodedLen) + /// Storage: FastUnstake Head (r:1 w:0) + /// Proof: FastUnstake Head (max_values: Some(1), max_size: Some(5768), added: 6263, mode: MaxEncodedLen) + /// Storage: Staking Bonded (r:1 w:0) + /// Proof: Staking Bonded (max_values: None, max_size: Some(72), added: 2547, mode: MaxEncodedLen) + /// Storage: Staking Validators (r:1 w:0) + /// Proof: Staking Validators (max_values: None, max_size: Some(45), added: 2520, mode: MaxEncodedLen) + /// Storage: Staking Nominators (r:1 w:1) + /// Proof: Staking Nominators (max_values: None, max_size: Some(558), added: 3033, mode: MaxEncodedLen) + /// Storage: Staking CounterForNominators (r:1 w:1) + /// Proof: Staking CounterForNominators (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) + /// Storage: VoterList ListNodes (r:1 w:1) + /// Proof: VoterList ListNodes (max_values: None, max_size: Some(154), added: 2629, mode: MaxEncodedLen) + /// Storage: VoterList ListBags (r:1 w:1) + /// Proof: VoterList ListBags (max_values: None, max_size: Some(82), added: 2557, mode: MaxEncodedLen) + /// Storage: VoterList CounterForListNodes (r:1 w:1) + /// Proof: VoterList CounterForListNodes (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) + /// Storage: Staking CurrentEra (r:1 w:0) + /// Proof: Staking CurrentEra (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) + /// Storage: Balances Locks (r:1 w:1) + /// Proof: Balances Locks (max_values: None, max_size: Some(1299), added: 3774, mode: MaxEncodedLen) + /// Storage: FastUnstake CounterForQueue (r:1 w:1) + /// Proof: FastUnstake CounterForQueue (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) fn register_fast_unstake() -> Weight { - // Minimum execution time: 124_849 nanoseconds. - Weight::from_parts(128_176_000, 0) - .saturating_add(RocksDbWeight::get().reads(14)) - .saturating_add(RocksDbWeight::get().writes(9)) + // Proof Size summary in bytes: + // Measured: `1964` + // Estimated: `45775` + // Minimum execution time: 117_552_000 picoseconds. + Weight::from_parts(118_303_000, 45775) + .saturating_add(RocksDbWeight::get().reads(14_u64)) + .saturating_add(RocksDbWeight::get().writes(9_u64)) } - // Storage: FastUnstake ErasToCheckPerBlock (r:1 w:0) - // Storage: Staking Ledger (r:1 w:0) - // Storage: FastUnstake Queue (r:1 w:1) - // Storage: FastUnstake Head (r:1 w:0) - // Storage: FastUnstake CounterForQueue (r:1 w:1) + /// Storage: FastUnstake ErasToCheckPerBlock (r:1 w:0) + /// Proof: FastUnstake ErasToCheckPerBlock (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) + /// Storage: Staking Ledger (r:1 w:0) + /// Proof: Staking Ledger (max_values: None, max_size: Some(1091), added: 3566, mode: MaxEncodedLen) + /// Storage: FastUnstake Queue (r:1 w:1) + /// Proof: FastUnstake Queue (max_values: None, max_size: Some(56), added: 2531, mode: MaxEncodedLen) + /// Storage: FastUnstake Head (r:1 w:0) + /// Proof: FastUnstake Head (max_values: Some(1), max_size: Some(5768), added: 6263, mode: MaxEncodedLen) + /// Storage: FastUnstake CounterForQueue (r:1 w:1) + /// Proof: FastUnstake CounterForQueue (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) fn deregister() -> Weight { - // Minimum execution time: 48_246 nanoseconds. - Weight::from_parts(49_720_000, 0) - .saturating_add(RocksDbWeight::get().reads(5)) - .saturating_add(RocksDbWeight::get().writes(2)) + // Proof Size summary in bytes: + // Measured: `1223` + // Estimated: `18308` + // Minimum execution time: 42_291_000 picoseconds. + Weight::from_parts(42_835_000, 18308) + .saturating_add(RocksDbWeight::get().reads(5_u64)) + .saturating_add(RocksDbWeight::get().writes(2_u64)) } - // Storage: FastUnstake ErasToCheckPerBlock (r:0 w:1) + /// Storage: FastUnstake ErasToCheckPerBlock (r:0 w:1) + /// Proof: FastUnstake ErasToCheckPerBlock (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) fn control() -> Weight { - // Minimum execution time: 4_611 nanoseconds. - Weight::from_parts(4_844_000, 0) - .saturating_add(RocksDbWeight::get().writes(1)) + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 3_551_000 picoseconds. + Weight::from_parts(3_632_000, 0) + .saturating_add(RocksDbWeight::get().writes(1_u64)) } } diff --git a/frame/glutton/src/weights.rs b/frame/glutton/src/weights.rs index 1a7020c17..bd409645a 100644 --- a/frame/glutton/src/weights.rs +++ b/frame/glutton/src/weights.rs @@ -18,26 +18,28 @@ //! Autogenerated weights for pallet_glutton //! //! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev -//! DATE: 2023-02-28, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! DATE: 2023-03-16, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` //! WORST CASE MAP SIZE: `1000000` //! HOSTNAME: `bm3`, CPU: `Intel(R) Core(TM) i7-7700K CPU @ 4.20GHz` //! EXECUTION: Some(Wasm), WASM-EXECUTION: Compiled, CHAIN: Some("dev"), DB CACHE: 1024 // Executed Command: -// target/production/substrate +// ./target/production/substrate // benchmark // pallet +// --chain=dev // --steps=50 // --repeat=20 +// --pallet=pallet_glutton +// --no-storage-info +// --no-median-slopes +// --no-min-squares // --extrinsic=* // --execution=wasm // --wasm-execution=compiled // --heap-pages=4096 -// --json-file=/var/lib/gitlab-runner/builds/zyw4fam_/0/parity/mirrors/substrate/.git/.artifacts/bench.json -// --pallet=pallet_glutton -// --chain=dev -// --header=./HEADER-APACHE2 // --output=./frame/glutton/src/weights.rs +// --header=./HEADER-APACHE2 // --template=./.maintain/frame-weight-template.hbs #![cfg_attr(rustfmt, rustfmt_skip)] @@ -72,11 +74,10 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `4` // Estimated: `1489` - // Minimum execution time: 10_218 nanoseconds. - Weight::from_parts(10_510_000, 0) - .saturating_add(Weight::from_parts(0, 1489)) - // Standard Error: 1_582 - .saturating_add(Weight::from_parts(1_577_660, 0).saturating_mul(n.into())) + // Minimum execution time: 10_384_000 picoseconds. + Weight::from_parts(10_598_000, 1489) + // Standard Error: 1_369 + .saturating_add(Weight::from_parts(1_580_155, 0).saturating_mul(n.into())) .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) .saturating_add(T::DbWeight::get().writes((1_u64).saturating_mul(n.into()))) @@ -90,11 +91,10 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `65` // Estimated: `1489` - // Minimum execution time: 10_993 nanoseconds. - Weight::from_parts(11_208_000, 0) - .saturating_add(Weight::from_parts(0, 1489)) - // Standard Error: 1_386 - .saturating_add(Weight::from_parts(1_072_330, 0).saturating_mul(n.into())) + // Minimum execution time: 11_022_000 picoseconds. + Weight::from_parts(11_319_000, 1489) + // Standard Error: 1_108 + .saturating_add(Weight::from_parts(1_048_682, 0).saturating_mul(n.into())) .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) .saturating_add(T::DbWeight::get().writes((1_u64).saturating_mul(n.into()))) @@ -104,24 +104,22 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 740 nanoseconds. - Weight::from_parts(770_000, 0) - .saturating_add(Weight::from_parts(0, 0)) - // Standard Error: 24 - .saturating_add(Weight::from_parts(96_434, 0).saturating_mul(i.into())) + // Minimum execution time: 660_000 picoseconds. + Weight::from_parts(7_814_200, 0) + // Standard Error: 33 + .saturating_add(Weight::from_parts(94_390, 0).saturating_mul(i.into())) } /// Storage: Glutton TrashData (r:5000 w:0) /// Proof: Glutton TrashData (max_values: Some(65000), max_size: Some(1036), added: 3016, mode: MaxEncodedLen) /// The range of component `i` is `[0, 5000]`. fn waste_proof_size_some(i: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `119036 + i * (1053 ±0)` + // Measured: `119036 + i * (1022 ±0)` // Estimated: `990 + i * (3016 ±0)` - // Minimum execution time: 630 nanoseconds. - Weight::from_parts(712_000, 0) - .saturating_add(Weight::from_parts(0, 990)) - // Standard Error: 4_326 - .saturating_add(Weight::from_parts(5_500_880, 0).saturating_mul(i.into())) + // Minimum execution time: 712_000 picoseconds. + Weight::from_parts(814_000, 990) + // Standard Error: 1_805 + .saturating_add(Weight::from_parts(5_407_690, 0).saturating_mul(i.into())) .saturating_add(T::DbWeight::get().reads((1_u64).saturating_mul(i.into()))) .saturating_add(Weight::from_parts(0, 3016).saturating_mul(i.into())) } @@ -133,11 +131,10 @@ impl WeightInfo for SubstrateWeight { /// Proof: Glutton TrashData (max_values: Some(65000), max_size: Some(1036), added: 3016, mode: MaxEncodedLen) fn on_idle_high_proof_waste() -> Weight { // Proof Size summary in bytes: - // Measured: `1954313` + // Measured: `1900466` // Estimated: `5242760` - // Minimum execution time: 56_743_236 nanoseconds. - Weight::from_parts(57_088_040_000, 0) - .saturating_add(Weight::from_parts(0, 5242760)) + // Minimum execution time: 54_903_045_000 picoseconds. + Weight::from_parts(55_194_691_000, 5242760) .saturating_add(T::DbWeight::get().reads(1739_u64)) } /// Storage: Glutton Storage (r:1 w:0) @@ -148,11 +145,10 @@ impl WeightInfo for SubstrateWeight { /// Proof: Glutton TrashData (max_values: Some(65000), max_size: Some(1036), added: 3016, mode: MaxEncodedLen) fn on_idle_low_proof_waste() -> Weight { // Proof Size summary in bytes: - // Measured: `9671` + // Measured: `9516` // Estimated: `19048` - // Minimum execution time: 100_387_042 nanoseconds. - Weight::from_parts(100_987_577_000, 0) - .saturating_add(Weight::from_parts(0, 19048)) + // Minimum execution time: 97_295_331_000 picoseconds. + Weight::from_parts(97_853_502_000, 19048) .saturating_add(T::DbWeight::get().reads(7_u64)) } /// Storage: Glutton Storage (r:1 w:0) @@ -163,9 +159,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `4` // Estimated: `2978` - // Minimum execution time: 4_256 nanoseconds. - Weight::from_parts(4_447_000, 0) - .saturating_add(Weight::from_parts(0, 2978)) + // Minimum execution time: 4_280_000 picoseconds. + Weight::from_parts(4_381_000, 2978) .saturating_add(T::DbWeight::get().reads(2_u64)) } /// Storage: Glutton Compute (r:0 w:1) @@ -174,9 +169,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 8_663 nanoseconds. - Weight::from_parts(8_864_000, 0) - .saturating_add(Weight::from_parts(0, 0)) + // Minimum execution time: 8_758_000 picoseconds. + Weight::from_parts(8_988_000, 0) .saturating_add(T::DbWeight::get().writes(1_u64)) } /// Storage: Glutton Storage (r:0 w:1) @@ -185,9 +179,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 8_653 nanoseconds. - Weight::from_parts(8_998_000, 0) - .saturating_add(Weight::from_parts(0, 0)) + // Minimum execution time: 8_901_000 picoseconds. + Weight::from_parts(9_291_000, 0) .saturating_add(T::DbWeight::get().writes(1_u64)) } } @@ -203,11 +196,10 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `4` // Estimated: `1489` - // Minimum execution time: 10_218 nanoseconds. - Weight::from_parts(10_510_000, 0) - .saturating_add(Weight::from_parts(0, 1489)) - // Standard Error: 1_582 - .saturating_add(Weight::from_parts(1_577_660, 0).saturating_mul(n.into())) + // Minimum execution time: 10_384_000 picoseconds. + Weight::from_parts(10_598_000, 1489) + // Standard Error: 1_369 + .saturating_add(Weight::from_parts(1_580_155, 0).saturating_mul(n.into())) .saturating_add(RocksDbWeight::get().reads(1_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) .saturating_add(RocksDbWeight::get().writes((1_u64).saturating_mul(n.into()))) @@ -221,11 +213,10 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `65` // Estimated: `1489` - // Minimum execution time: 10_993 nanoseconds. - Weight::from_parts(11_208_000, 0) - .saturating_add(Weight::from_parts(0, 1489)) - // Standard Error: 1_386 - .saturating_add(Weight::from_parts(1_072_330, 0).saturating_mul(n.into())) + // Minimum execution time: 11_022_000 picoseconds. + Weight::from_parts(11_319_000, 1489) + // Standard Error: 1_108 + .saturating_add(Weight::from_parts(1_048_682, 0).saturating_mul(n.into())) .saturating_add(RocksDbWeight::get().reads(1_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) .saturating_add(RocksDbWeight::get().writes((1_u64).saturating_mul(n.into()))) @@ -235,24 +226,22 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 740 nanoseconds. - Weight::from_parts(770_000, 0) - .saturating_add(Weight::from_parts(0, 0)) - // Standard Error: 24 - .saturating_add(Weight::from_parts(96_434, 0).saturating_mul(i.into())) + // Minimum execution time: 660_000 picoseconds. + Weight::from_parts(7_814_200, 0) + // Standard Error: 33 + .saturating_add(Weight::from_parts(94_390, 0).saturating_mul(i.into())) } /// Storage: Glutton TrashData (r:5000 w:0) /// Proof: Glutton TrashData (max_values: Some(65000), max_size: Some(1036), added: 3016, mode: MaxEncodedLen) /// The range of component `i` is `[0, 5000]`. fn waste_proof_size_some(i: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `119036 + i * (1053 ±0)` + // Measured: `119036 + i * (1022 ±0)` // Estimated: `990 + i * (3016 ±0)` - // Minimum execution time: 630 nanoseconds. - Weight::from_parts(712_000, 0) - .saturating_add(Weight::from_parts(0, 990)) - // Standard Error: 4_326 - .saturating_add(Weight::from_parts(5_500_880, 0).saturating_mul(i.into())) + // Minimum execution time: 712_000 picoseconds. + Weight::from_parts(814_000, 990) + // Standard Error: 1_805 + .saturating_add(Weight::from_parts(5_407_690, 0).saturating_mul(i.into())) .saturating_add(RocksDbWeight::get().reads((1_u64).saturating_mul(i.into()))) .saturating_add(Weight::from_parts(0, 3016).saturating_mul(i.into())) } @@ -264,11 +253,10 @@ impl WeightInfo for () { /// Proof: Glutton TrashData (max_values: Some(65000), max_size: Some(1036), added: 3016, mode: MaxEncodedLen) fn on_idle_high_proof_waste() -> Weight { // Proof Size summary in bytes: - // Measured: `1954313` + // Measured: `1900466` // Estimated: `5242760` - // Minimum execution time: 56_743_236 nanoseconds. - Weight::from_parts(57_088_040_000, 0) - .saturating_add(Weight::from_parts(0, 5242760)) + // Minimum execution time: 54_903_045_000 picoseconds. + Weight::from_parts(55_194_691_000, 5242760) .saturating_add(RocksDbWeight::get().reads(1739_u64)) } /// Storage: Glutton Storage (r:1 w:0) @@ -279,11 +267,10 @@ impl WeightInfo for () { /// Proof: Glutton TrashData (max_values: Some(65000), max_size: Some(1036), added: 3016, mode: MaxEncodedLen) fn on_idle_low_proof_waste() -> Weight { // Proof Size summary in bytes: - // Measured: `9671` + // Measured: `9516` // Estimated: `19048` - // Minimum execution time: 100_387_042 nanoseconds. - Weight::from_parts(100_987_577_000, 0) - .saturating_add(Weight::from_parts(0, 19048)) + // Minimum execution time: 97_295_331_000 picoseconds. + Weight::from_parts(97_853_502_000, 19048) .saturating_add(RocksDbWeight::get().reads(7_u64)) } /// Storage: Glutton Storage (r:1 w:0) @@ -294,9 +281,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `4` // Estimated: `2978` - // Minimum execution time: 4_256 nanoseconds. - Weight::from_parts(4_447_000, 0) - .saturating_add(Weight::from_parts(0, 2978)) + // Minimum execution time: 4_280_000 picoseconds. + Weight::from_parts(4_381_000, 2978) .saturating_add(RocksDbWeight::get().reads(2_u64)) } /// Storage: Glutton Compute (r:0 w:1) @@ -305,9 +291,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 8_663 nanoseconds. - Weight::from_parts(8_864_000, 0) - .saturating_add(Weight::from_parts(0, 0)) + // Minimum execution time: 8_758_000 picoseconds. + Weight::from_parts(8_988_000, 0) .saturating_add(RocksDbWeight::get().writes(1_u64)) } /// Storage: Glutton Storage (r:0 w:1) @@ -316,9 +301,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 8_653 nanoseconds. - Weight::from_parts(8_998_000, 0) - .saturating_add(Weight::from_parts(0, 0)) + // Minimum execution time: 8_901_000 picoseconds. + Weight::from_parts(9_291_000, 0) .saturating_add(RocksDbWeight::get().writes(1_u64)) } } diff --git a/frame/identity/src/weights.rs b/frame/identity/src/weights.rs index 65fe2b17b..bad6aa768 100644 --- a/frame/identity/src/weights.rs +++ b/frame/identity/src/weights.rs @@ -18,9 +18,9 @@ //! Autogenerated weights for pallet_identity //! //! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev -//! DATE: 2023-01-24, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! DATE: 2023-03-16, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` //! WORST CASE MAP SIZE: `1000000` -//! HOSTNAME: `bm2`, CPU: `Intel(R) Core(TM) i7-7700K CPU @ 4.20GHz` +//! HOSTNAME: `bm3`, CPU: `Intel(R) Core(TM) i7-7700K CPU @ 4.20GHz` //! EXECUTION: Some(Wasm), WASM-EXECUTION: Compiled, CHAIN: Some("dev"), DB CACHE: 1024 // Executed Command: @@ -31,6 +31,9 @@ // --steps=50 // --repeat=20 // --pallet=pallet_identity +// --no-storage-info +// --no-median-slopes +// --no-min-squares // --extrinsic=* // --execution=wasm // --wasm-execution=compiled @@ -74,12 +77,12 @@ impl WeightInfo for SubstrateWeight { /// The range of component `r` is `[1, 19]`. fn add_registrar(r: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `64 + r * (57 ±0)` - // Estimated: `1636` - // Minimum execution time: 10_964 nanoseconds. - Weight::from_parts(11_800_935, 1636) - // Standard Error: 1_334 - .saturating_add(Weight::from_parts(96_038, 0).saturating_mul(r.into())) + // Measured: `32 + r * (57 ±0)` + // Estimated: `2626` + // Minimum execution time: 13_092_000 picoseconds. + Weight::from_parts(13_798_719, 2626) + // Standard Error: 1_553 + .saturating_add(Weight::from_parts(109_215, 0).saturating_mul(r.into())) .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -89,14 +92,14 @@ impl WeightInfo for SubstrateWeight { /// The range of component `x` is `[0, 100]`. fn set_identity(r: u32, x: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `474 + r * (5 ±0)` - // Estimated: `10013` - // Minimum execution time: 26_400 nanoseconds. - Weight::from_parts(26_060_549, 10013) - // Standard Error: 1_561 - .saturating_add(Weight::from_parts(72_083, 0).saturating_mul(r.into())) - // Standard Error: 304 - .saturating_add(Weight::from_parts(306_994, 0).saturating_mul(x.into())) + // Measured: `442 + r * (5 ±0)` + // Estimated: `11003` + // Minimum execution time: 30_800_000 picoseconds. + Weight::from_parts(31_140_040, 11003) + // Standard Error: 4_896 + .saturating_add(Weight::from_parts(69_348, 0).saturating_mul(r.into())) + // Standard Error: 955 + .saturating_add(Weight::from_parts(430_082, 0).saturating_mul(x.into())) .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -110,11 +113,11 @@ impl WeightInfo for SubstrateWeight { fn set_subs_new(s: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `101` - // Estimated: `15746 + s * (2589 ±0)` - // Minimum execution time: 8_492 nanoseconds. - Weight::from_parts(21_645_924, 15746) - // Standard Error: 3_452 - .saturating_add(Weight::from_parts(2_442_604, 0).saturating_mul(s.into())) + // Estimated: `18716 + s * (2589 ±0)` + // Minimum execution time: 10_301_000 picoseconds. + Weight::from_parts(23_255_636, 18716) + // Standard Error: 5_562 + .saturating_add(Weight::from_parts(2_978_765, 0).saturating_mul(s.into())) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().reads((1_u64).saturating_mul(s.into()))) .saturating_add(T::DbWeight::get().writes(1_u64)) @@ -130,12 +133,12 @@ impl WeightInfo for SubstrateWeight { /// The range of component `p` is `[0, 100]`. fn set_subs_old(p: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `226 + p * (32 ±0)` - // Estimated: `15746` - // Minimum execution time: 8_488 nanoseconds. - Weight::from_parts(20_202_601, 15746) - // Standard Error: 2_834 - .saturating_add(Weight::from_parts(1_082_941, 0).saturating_mul(p.into())) + // Measured: `194 + p * (32 ±0)` + // Estimated: `17726` + // Minimum execution time: 10_095_000 picoseconds. + Weight::from_parts(23_197_533, 17726) + // Standard Error: 3_460 + .saturating_add(Weight::from_parts(1_227_498, 0).saturating_mul(p.into())) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) .saturating_add(T::DbWeight::get().writes((1_u64).saturating_mul(p.into()))) @@ -149,18 +152,16 @@ impl WeightInfo for SubstrateWeight { /// The range of component `r` is `[1, 20]`. /// The range of component `s` is `[0, 100]`. /// The range of component `x` is `[0, 100]`. - fn clear_identity(r: u32, s: u32, x: u32, ) -> Weight { - // Proof Size summary in bytes: - // Measured: `533 + r * (5 ±0) + s * (32 ±0) + x * (66 ±0)` - // Estimated: `15746` - // Minimum execution time: 41_319 nanoseconds. - Weight::from_parts(25_850_055, 15746) - // Standard Error: 4_144 - .saturating_add(Weight::from_parts(59_619, 0).saturating_mul(r.into())) - // Standard Error: 809 - .saturating_add(Weight::from_parts(1_076_550, 0).saturating_mul(s.into())) - // Standard Error: 809 - .saturating_add(Weight::from_parts(163_191, 0).saturating_mul(x.into())) + fn clear_identity(_r: u32, s: u32, x: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `469 + r * (5 ±0) + s * (32 ±0) + x * (66 ±0)` + // Estimated: `17726` + // Minimum execution time: 51_054_000 picoseconds. + Weight::from_parts(30_184_047, 17726) + // Standard Error: 2_441 + .saturating_add(Weight::from_parts(1_221_983, 0).saturating_mul(s.into())) + // Standard Error: 2_441 + .saturating_add(Weight::from_parts(234_084, 0).saturating_mul(x.into())) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().writes(2_u64)) .saturating_add(T::DbWeight::get().writes((1_u64).saturating_mul(s.into()))) @@ -173,14 +174,14 @@ impl WeightInfo for SubstrateWeight { /// The range of component `x` is `[0, 100]`. fn request_judgement(r: u32, x: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `431 + r * (57 ±0) + x * (66 ±0)` - // Estimated: `11649` - // Minimum execution time: 28_118 nanoseconds. - Weight::from_parts(27_359_471, 11649) - // Standard Error: 2_707 - .saturating_add(Weight::from_parts(107_279, 0).saturating_mul(r.into())) - // Standard Error: 528 - .saturating_add(Weight::from_parts(325_165, 0).saturating_mul(x.into())) + // Measured: `367 + r * (57 ±0) + x * (66 ±0)` + // Estimated: `13629` + // Minimum execution time: 32_747_000 picoseconds. + Weight::from_parts(29_557_659, 13629) + // Standard Error: 4_616 + .saturating_add(Weight::from_parts(200_494, 0).saturating_mul(r.into())) + // Standard Error: 900 + .saturating_add(Weight::from_parts(468_381, 0).saturating_mul(x.into())) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -190,14 +191,14 @@ impl WeightInfo for SubstrateWeight { /// The range of component `x` is `[0, 100]`. fn cancel_request(r: u32, x: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `430 + x * (66 ±0)` - // Estimated: `10013` - // Minimum execution time: 24_817 nanoseconds. - Weight::from_parts(24_749_808, 10013) - // Standard Error: 1_938 - .saturating_add(Weight::from_parts(63_396, 0).saturating_mul(r.into())) - // Standard Error: 378 - .saturating_add(Weight::from_parts(327_083, 0).saturating_mul(x.into())) + // Measured: `398 + x * (66 ±0)` + // Estimated: `11003` + // Minimum execution time: 28_853_000 picoseconds. + Weight::from_parts(26_811_119, 11003) + // Standard Error: 15_667 + .saturating_add(Weight::from_parts(240_647, 0).saturating_mul(r.into())) + // Standard Error: 3_056 + .saturating_add(Weight::from_parts(462_536, 0).saturating_mul(x.into())) .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -206,12 +207,12 @@ impl WeightInfo for SubstrateWeight { /// The range of component `r` is `[1, 19]`. fn set_fee(r: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `121 + r * (57 ±0)` - // Estimated: `1636` - // Minimum execution time: 6_664 nanoseconds. - Weight::from_parts(7_286_307, 1636) - // Standard Error: 1_560 - .saturating_add(Weight::from_parts(96_416, 0).saturating_mul(r.into())) + // Measured: `89 + r * (57 ±0)` + // Estimated: `2626` + // Minimum execution time: 8_413_000 picoseconds. + Weight::from_parts(8_846_436, 2626) + // Standard Error: 1_470 + .saturating_add(Weight::from_parts(113_780, 0).saturating_mul(r.into())) .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -220,12 +221,12 @@ impl WeightInfo for SubstrateWeight { /// The range of component `r` is `[1, 19]`. fn set_account_id(r: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `121 + r * (57 ±0)` - // Estimated: `1636` - // Minimum execution time: 7_054 nanoseconds. - Weight::from_parts(7_382_954, 1636) - // Standard Error: 1_621 - .saturating_add(Weight::from_parts(101_595, 0).saturating_mul(r.into())) + // Measured: `89 + r * (57 ±0)` + // Estimated: `2626` + // Minimum execution time: 8_753_000 picoseconds. + Weight::from_parts(9_143_938, 2626) + // Standard Error: 1_490 + .saturating_add(Weight::from_parts(113_406, 0).saturating_mul(r.into())) .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -234,12 +235,12 @@ impl WeightInfo for SubstrateWeight { /// The range of component `r` is `[1, 19]`. fn set_fields(r: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `121 + r * (57 ±0)` - // Estimated: `1636` - // Minimum execution time: 6_659 nanoseconds. - Weight::from_parts(7_188_883, 1636) - // Standard Error: 1_377 - .saturating_add(Weight::from_parts(98_965, 0).saturating_mul(r.into())) + // Measured: `89 + r * (57 ±0)` + // Estimated: `2626` + // Minimum execution time: 8_320_000 picoseconds. + Weight::from_parts(8_800_306, 2626) + // Standard Error: 1_345 + .saturating_add(Weight::from_parts(125_258, 0).saturating_mul(r.into())) .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -251,14 +252,14 @@ impl WeightInfo for SubstrateWeight { /// The range of component `x` is `[0, 100]`. fn provide_judgement(r: u32, x: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `509 + r * (57 ±0) + x * (66 ±0)` - // Estimated: `11649` - // Minimum execution time: 21_567 nanoseconds. - Weight::from_parts(21_015_310, 11649) - // Standard Error: 2_516 - .saturating_add(Weight::from_parts(123_992, 0).saturating_mul(r.into())) - // Standard Error: 465 - .saturating_add(Weight::from_parts(552_116, 0).saturating_mul(x.into())) + // Measured: `445 + r * (57 ±0) + x * (66 ±0)` + // Estimated: `13629` + // Minimum execution time: 25_313_000 picoseconds. + Weight::from_parts(23_968_548, 13629) + // Standard Error: 4_143 + .saturating_add(Weight::from_parts(164_678, 0).saturating_mul(r.into())) + // Standard Error: 766 + .saturating_add(Weight::from_parts(713_576, 0).saturating_mul(x.into())) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -275,16 +276,16 @@ impl WeightInfo for SubstrateWeight { /// The range of component `x` is `[0, 100]`. fn kill_identity(r: u32, s: u32, x: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `772 + r * (5 ±0) + s * (32 ±0) + x * (66 ±0)` - // Estimated: `18349` - // Minimum execution time: 52_881 nanoseconds. - Weight::from_parts(38_504_388, 18349) - // Standard Error: 3_909 - .saturating_add(Weight::from_parts(51_452, 0).saturating_mul(r.into())) - // Standard Error: 763 - .saturating_add(Weight::from_parts(1_069_924, 0).saturating_mul(s.into())) - // Standard Error: 763 - .saturating_add(Weight::from_parts(164_906, 0).saturating_mul(x.into())) + // Measured: `676 + r * (5 ±0) + s * (32 ±0) + x * (66 ±0)` + // Estimated: `21319` + // Minimum execution time: 64_638_000 picoseconds. + Weight::from_parts(43_317_654, 21319) + // Standard Error: 10_182 + .saturating_add(Weight::from_parts(61_967, 0).saturating_mul(r.into())) + // Standard Error: 1_988 + .saturating_add(Weight::from_parts(1_224_202, 0).saturating_mul(s.into())) + // Standard Error: 1_988 + .saturating_add(Weight::from_parts(233_897, 0).saturating_mul(x.into())) .saturating_add(T::DbWeight::get().reads(3_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) .saturating_add(T::DbWeight::get().writes((1_u64).saturating_mul(s.into()))) @@ -298,12 +299,12 @@ impl WeightInfo for SubstrateWeight { /// The range of component `s` is `[0, 99]`. fn add_sub(s: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `507 + s * (36 ±0)` - // Estimated: `18335` - // Minimum execution time: 24_556 nanoseconds. - Weight::from_parts(28_641_160, 18335) - // Standard Error: 1_327 - .saturating_add(Weight::from_parts(66_150, 0).saturating_mul(s.into())) + // Measured: `475 + s * (36 ±0)` + // Estimated: `21305` + // Minimum execution time: 28_661_000 picoseconds. + Weight::from_parts(33_432_641, 21305) + // Standard Error: 1_547 + .saturating_add(Weight::from_parts(71_314, 0).saturating_mul(s.into())) .saturating_add(T::DbWeight::get().reads(3_u64)) .saturating_add(T::DbWeight::get().writes(2_u64)) } @@ -314,12 +315,12 @@ impl WeightInfo for SubstrateWeight { /// The range of component `s` is `[1, 100]`. fn rename_sub(s: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `623 + s * (3 ±0)` - // Estimated: `12602` - // Minimum execution time: 11_347 nanoseconds. - Weight::from_parts(13_299_367, 12602) - // Standard Error: 525 - .saturating_add(Weight::from_parts(16_472, 0).saturating_mul(s.into())) + // Measured: `591 + s * (3 ±0)` + // Estimated: `14582` + // Minimum execution time: 13_885_000 picoseconds. + Weight::from_parts(16_105_165, 14582) + // Standard Error: 562 + .saturating_add(Weight::from_parts(17_087, 0).saturating_mul(s.into())) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -332,12 +333,12 @@ impl WeightInfo for SubstrateWeight { /// The range of component `s` is `[1, 100]`. fn remove_sub(s: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `702 + s * (35 ±0)` - // Estimated: `18335` - // Minimum execution time: 27_810 nanoseconds. - Weight::from_parts(30_347_763, 18335) - // Standard Error: 928 - .saturating_add(Weight::from_parts(55_342, 0).saturating_mul(s.into())) + // Measured: `638 + s * (35 ±0)` + // Estimated: `21305` + // Minimum execution time: 32_192_000 picoseconds. + Weight::from_parts(35_393_444, 21305) + // Standard Error: 2_198 + .saturating_add(Weight::from_parts(62_316, 0).saturating_mul(s.into())) .saturating_add(T::DbWeight::get().reads(3_u64)) .saturating_add(T::DbWeight::get().writes(2_u64)) } @@ -348,12 +349,12 @@ impl WeightInfo for SubstrateWeight { /// The range of component `s` is `[0, 99]`. fn quit_sub(s: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `628 + s * (37 ±0)` - // Estimated: `8322` - // Minimum execution time: 17_601 nanoseconds. - Weight::from_parts(19_794_971, 8322) - // Standard Error: 934 - .saturating_add(Weight::from_parts(59_289, 0).saturating_mul(s.into())) + // Measured: `564 + s * (37 ±0)` + // Estimated: `10302` + // Minimum execution time: 20_803_000 picoseconds. + Weight::from_parts(23_462_393, 10302) + // Standard Error: 1_619 + .saturating_add(Weight::from_parts(59_938, 0).saturating_mul(s.into())) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().writes(2_u64)) } @@ -366,12 +367,12 @@ impl WeightInfo for () { /// The range of component `r` is `[1, 19]`. fn add_registrar(r: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `64 + r * (57 ±0)` - // Estimated: `1636` - // Minimum execution time: 10_964 nanoseconds. - Weight::from_parts(11_800_935, 1636) - // Standard Error: 1_334 - .saturating_add(Weight::from_parts(96_038, 0).saturating_mul(r.into())) + // Measured: `32 + r * (57 ±0)` + // Estimated: `2626` + // Minimum execution time: 13_092_000 picoseconds. + Weight::from_parts(13_798_719, 2626) + // Standard Error: 1_553 + .saturating_add(Weight::from_parts(109_215, 0).saturating_mul(r.into())) .saturating_add(RocksDbWeight::get().reads(1_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } @@ -381,14 +382,14 @@ impl WeightInfo for () { /// The range of component `x` is `[0, 100]`. fn set_identity(r: u32, x: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `474 + r * (5 ±0)` - // Estimated: `10013` - // Minimum execution time: 26_400 nanoseconds. - Weight::from_parts(26_060_549, 10013) - // Standard Error: 1_561 - .saturating_add(Weight::from_parts(72_083, 0).saturating_mul(r.into())) - // Standard Error: 304 - .saturating_add(Weight::from_parts(306_994, 0).saturating_mul(x.into())) + // Measured: `442 + r * (5 ±0)` + // Estimated: `11003` + // Minimum execution time: 30_800_000 picoseconds. + Weight::from_parts(31_140_040, 11003) + // Standard Error: 4_896 + .saturating_add(Weight::from_parts(69_348, 0).saturating_mul(r.into())) + // Standard Error: 955 + .saturating_add(Weight::from_parts(430_082, 0).saturating_mul(x.into())) .saturating_add(RocksDbWeight::get().reads(1_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } @@ -402,11 +403,11 @@ impl WeightInfo for () { fn set_subs_new(s: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `101` - // Estimated: `15746 + s * (2589 ±0)` - // Minimum execution time: 8_492 nanoseconds. - Weight::from_parts(21_645_924, 15746) - // Standard Error: 3_452 - .saturating_add(Weight::from_parts(2_442_604, 0).saturating_mul(s.into())) + // Estimated: `18716 + s * (2589 ±0)` + // Minimum execution time: 10_301_000 picoseconds. + Weight::from_parts(23_255_636, 18716) + // Standard Error: 5_562 + .saturating_add(Weight::from_parts(2_978_765, 0).saturating_mul(s.into())) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().reads((1_u64).saturating_mul(s.into()))) .saturating_add(RocksDbWeight::get().writes(1_u64)) @@ -422,12 +423,12 @@ impl WeightInfo for () { /// The range of component `p` is `[0, 100]`. fn set_subs_old(p: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `226 + p * (32 ±0)` - // Estimated: `15746` - // Minimum execution time: 8_488 nanoseconds. - Weight::from_parts(20_202_601, 15746) - // Standard Error: 2_834 - .saturating_add(Weight::from_parts(1_082_941, 0).saturating_mul(p.into())) + // Measured: `194 + p * (32 ±0)` + // Estimated: `17726` + // Minimum execution time: 10_095_000 picoseconds. + Weight::from_parts(23_197_533, 17726) + // Standard Error: 3_460 + .saturating_add(Weight::from_parts(1_227_498, 0).saturating_mul(p.into())) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) .saturating_add(RocksDbWeight::get().writes((1_u64).saturating_mul(p.into()))) @@ -441,18 +442,16 @@ impl WeightInfo for () { /// The range of component `r` is `[1, 20]`. /// The range of component `s` is `[0, 100]`. /// The range of component `x` is `[0, 100]`. - fn clear_identity(r: u32, s: u32, x: u32, ) -> Weight { - // Proof Size summary in bytes: - // Measured: `533 + r * (5 ±0) + s * (32 ±0) + x * (66 ±0)` - // Estimated: `15746` - // Minimum execution time: 41_319 nanoseconds. - Weight::from_parts(25_850_055, 15746) - // Standard Error: 4_144 - .saturating_add(Weight::from_parts(59_619, 0).saturating_mul(r.into())) - // Standard Error: 809 - .saturating_add(Weight::from_parts(1_076_550, 0).saturating_mul(s.into())) - // Standard Error: 809 - .saturating_add(Weight::from_parts(163_191, 0).saturating_mul(x.into())) + fn clear_identity(_r: u32, s: u32, x: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `469 + r * (5 ±0) + s * (32 ±0) + x * (66 ±0)` + // Estimated: `17726` + // Minimum execution time: 51_054_000 picoseconds. + Weight::from_parts(30_184_047, 17726) + // Standard Error: 2_441 + .saturating_add(Weight::from_parts(1_221_983, 0).saturating_mul(s.into())) + // Standard Error: 2_441 + .saturating_add(Weight::from_parts(234_084, 0).saturating_mul(x.into())) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().writes(2_u64)) .saturating_add(RocksDbWeight::get().writes((1_u64).saturating_mul(s.into()))) @@ -465,14 +464,14 @@ impl WeightInfo for () { /// The range of component `x` is `[0, 100]`. fn request_judgement(r: u32, x: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `431 + r * (57 ±0) + x * (66 ±0)` - // Estimated: `11649` - // Minimum execution time: 28_118 nanoseconds. - Weight::from_parts(27_359_471, 11649) - // Standard Error: 2_707 - .saturating_add(Weight::from_parts(107_279, 0).saturating_mul(r.into())) - // Standard Error: 528 - .saturating_add(Weight::from_parts(325_165, 0).saturating_mul(x.into())) + // Measured: `367 + r * (57 ±0) + x * (66 ±0)` + // Estimated: `13629` + // Minimum execution time: 32_747_000 picoseconds. + Weight::from_parts(29_557_659, 13629) + // Standard Error: 4_616 + .saturating_add(Weight::from_parts(200_494, 0).saturating_mul(r.into())) + // Standard Error: 900 + .saturating_add(Weight::from_parts(468_381, 0).saturating_mul(x.into())) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } @@ -482,14 +481,14 @@ impl WeightInfo for () { /// The range of component `x` is `[0, 100]`. fn cancel_request(r: u32, x: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `430 + x * (66 ±0)` - // Estimated: `10013` - // Minimum execution time: 24_817 nanoseconds. - Weight::from_parts(24_749_808, 10013) - // Standard Error: 1_938 - .saturating_add(Weight::from_parts(63_396, 0).saturating_mul(r.into())) - // Standard Error: 378 - .saturating_add(Weight::from_parts(327_083, 0).saturating_mul(x.into())) + // Measured: `398 + x * (66 ±0)` + // Estimated: `11003` + // Minimum execution time: 28_853_000 picoseconds. + Weight::from_parts(26_811_119, 11003) + // Standard Error: 15_667 + .saturating_add(Weight::from_parts(240_647, 0).saturating_mul(r.into())) + // Standard Error: 3_056 + .saturating_add(Weight::from_parts(462_536, 0).saturating_mul(x.into())) .saturating_add(RocksDbWeight::get().reads(1_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } @@ -498,12 +497,12 @@ impl WeightInfo for () { /// The range of component `r` is `[1, 19]`. fn set_fee(r: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `121 + r * (57 ±0)` - // Estimated: `1636` - // Minimum execution time: 6_664 nanoseconds. - Weight::from_parts(7_286_307, 1636) - // Standard Error: 1_560 - .saturating_add(Weight::from_parts(96_416, 0).saturating_mul(r.into())) + // Measured: `89 + r * (57 ±0)` + // Estimated: `2626` + // Minimum execution time: 8_413_000 picoseconds. + Weight::from_parts(8_846_436, 2626) + // Standard Error: 1_470 + .saturating_add(Weight::from_parts(113_780, 0).saturating_mul(r.into())) .saturating_add(RocksDbWeight::get().reads(1_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } @@ -512,12 +511,12 @@ impl WeightInfo for () { /// The range of component `r` is `[1, 19]`. fn set_account_id(r: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `121 + r * (57 ±0)` - // Estimated: `1636` - // Minimum execution time: 7_054 nanoseconds. - Weight::from_parts(7_382_954, 1636) - // Standard Error: 1_621 - .saturating_add(Weight::from_parts(101_595, 0).saturating_mul(r.into())) + // Measured: `89 + r * (57 ±0)` + // Estimated: `2626` + // Minimum execution time: 8_753_000 picoseconds. + Weight::from_parts(9_143_938, 2626) + // Standard Error: 1_490 + .saturating_add(Weight::from_parts(113_406, 0).saturating_mul(r.into())) .saturating_add(RocksDbWeight::get().reads(1_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } @@ -526,12 +525,12 @@ impl WeightInfo for () { /// The range of component `r` is `[1, 19]`. fn set_fields(r: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `121 + r * (57 ±0)` - // Estimated: `1636` - // Minimum execution time: 6_659 nanoseconds. - Weight::from_parts(7_188_883, 1636) - // Standard Error: 1_377 - .saturating_add(Weight::from_parts(98_965, 0).saturating_mul(r.into())) + // Measured: `89 + r * (57 ±0)` + // Estimated: `2626` + // Minimum execution time: 8_320_000 picoseconds. + Weight::from_parts(8_800_306, 2626) + // Standard Error: 1_345 + .saturating_add(Weight::from_parts(125_258, 0).saturating_mul(r.into())) .saturating_add(RocksDbWeight::get().reads(1_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } @@ -543,14 +542,14 @@ impl WeightInfo for () { /// The range of component `x` is `[0, 100]`. fn provide_judgement(r: u32, x: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `509 + r * (57 ±0) + x * (66 ±0)` - // Estimated: `11649` - // Minimum execution time: 21_567 nanoseconds. - Weight::from_parts(21_015_310, 11649) - // Standard Error: 2_516 - .saturating_add(Weight::from_parts(123_992, 0).saturating_mul(r.into())) - // Standard Error: 465 - .saturating_add(Weight::from_parts(552_116, 0).saturating_mul(x.into())) + // Measured: `445 + r * (57 ±0) + x * (66 ±0)` + // Estimated: `13629` + // Minimum execution time: 25_313_000 picoseconds. + Weight::from_parts(23_968_548, 13629) + // Standard Error: 4_143 + .saturating_add(Weight::from_parts(164_678, 0).saturating_mul(r.into())) + // Standard Error: 766 + .saturating_add(Weight::from_parts(713_576, 0).saturating_mul(x.into())) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } @@ -567,16 +566,16 @@ impl WeightInfo for () { /// The range of component `x` is `[0, 100]`. fn kill_identity(r: u32, s: u32, x: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `772 + r * (5 ±0) + s * (32 ±0) + x * (66 ±0)` - // Estimated: `18349` - // Minimum execution time: 52_881 nanoseconds. - Weight::from_parts(38_504_388, 18349) - // Standard Error: 3_909 - .saturating_add(Weight::from_parts(51_452, 0).saturating_mul(r.into())) - // Standard Error: 763 - .saturating_add(Weight::from_parts(1_069_924, 0).saturating_mul(s.into())) - // Standard Error: 763 - .saturating_add(Weight::from_parts(164_906, 0).saturating_mul(x.into())) + // Measured: `676 + r * (5 ±0) + s * (32 ±0) + x * (66 ±0)` + // Estimated: `21319` + // Minimum execution time: 64_638_000 picoseconds. + Weight::from_parts(43_317_654, 21319) + // Standard Error: 10_182 + .saturating_add(Weight::from_parts(61_967, 0).saturating_mul(r.into())) + // Standard Error: 1_988 + .saturating_add(Weight::from_parts(1_224_202, 0).saturating_mul(s.into())) + // Standard Error: 1_988 + .saturating_add(Weight::from_parts(233_897, 0).saturating_mul(x.into())) .saturating_add(RocksDbWeight::get().reads(3_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) .saturating_add(RocksDbWeight::get().writes((1_u64).saturating_mul(s.into()))) @@ -590,12 +589,12 @@ impl WeightInfo for () { /// The range of component `s` is `[0, 99]`. fn add_sub(s: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `507 + s * (36 ±0)` - // Estimated: `18335` - // Minimum execution time: 24_556 nanoseconds. - Weight::from_parts(28_641_160, 18335) - // Standard Error: 1_327 - .saturating_add(Weight::from_parts(66_150, 0).saturating_mul(s.into())) + // Measured: `475 + s * (36 ±0)` + // Estimated: `21305` + // Minimum execution time: 28_661_000 picoseconds. + Weight::from_parts(33_432_641, 21305) + // Standard Error: 1_547 + .saturating_add(Weight::from_parts(71_314, 0).saturating_mul(s.into())) .saturating_add(RocksDbWeight::get().reads(3_u64)) .saturating_add(RocksDbWeight::get().writes(2_u64)) } @@ -606,12 +605,12 @@ impl WeightInfo for () { /// The range of component `s` is `[1, 100]`. fn rename_sub(s: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `623 + s * (3 ±0)` - // Estimated: `12602` - // Minimum execution time: 11_347 nanoseconds. - Weight::from_parts(13_299_367, 12602) - // Standard Error: 525 - .saturating_add(Weight::from_parts(16_472, 0).saturating_mul(s.into())) + // Measured: `591 + s * (3 ±0)` + // Estimated: `14582` + // Minimum execution time: 13_885_000 picoseconds. + Weight::from_parts(16_105_165, 14582) + // Standard Error: 562 + .saturating_add(Weight::from_parts(17_087, 0).saturating_mul(s.into())) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } @@ -624,12 +623,12 @@ impl WeightInfo for () { /// The range of component `s` is `[1, 100]`. fn remove_sub(s: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `702 + s * (35 ±0)` - // Estimated: `18335` - // Minimum execution time: 27_810 nanoseconds. - Weight::from_parts(30_347_763, 18335) - // Standard Error: 928 - .saturating_add(Weight::from_parts(55_342, 0).saturating_mul(s.into())) + // Measured: `638 + s * (35 ±0)` + // Estimated: `21305` + // Minimum execution time: 32_192_000 picoseconds. + Weight::from_parts(35_393_444, 21305) + // Standard Error: 2_198 + .saturating_add(Weight::from_parts(62_316, 0).saturating_mul(s.into())) .saturating_add(RocksDbWeight::get().reads(3_u64)) .saturating_add(RocksDbWeight::get().writes(2_u64)) } @@ -640,12 +639,12 @@ impl WeightInfo for () { /// The range of component `s` is `[0, 99]`. fn quit_sub(s: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `628 + s * (37 ±0)` - // Estimated: `8322` - // Minimum execution time: 17_601 nanoseconds. - Weight::from_parts(19_794_971, 8322) - // Standard Error: 934 - .saturating_add(Weight::from_parts(59_289, 0).saturating_mul(s.into())) + // Measured: `564 + s * (37 ±0)` + // Estimated: `10302` + // Minimum execution time: 20_803_000 picoseconds. + Weight::from_parts(23_462_393, 10302) + // Standard Error: 1_619 + .saturating_add(Weight::from_parts(59_938, 0).saturating_mul(s.into())) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().writes(2_u64)) } diff --git a/frame/im-online/src/weights.rs b/frame/im-online/src/weights.rs index 675feb075..f97df1c36 100644 --- a/frame/im-online/src/weights.rs +++ b/frame/im-online/src/weights.rs @@ -18,9 +18,9 @@ //! Autogenerated weights for pallet_im_online //! //! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev -//! DATE: 2023-01-24, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! DATE: 2023-03-16, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` //! WORST CASE MAP SIZE: `1000000` -//! HOSTNAME: `bm2`, CPU: `Intel(R) Core(TM) i7-7700K CPU @ 4.20GHz` +//! HOSTNAME: `bm3`, CPU: `Intel(R) Core(TM) i7-7700K CPU @ 4.20GHz` //! EXECUTION: Some(Wasm), WASM-EXECUTION: Compiled, CHAIN: Some("dev"), DB CACHE: 1024 // Executed Command: @@ -31,6 +31,9 @@ // --steps=50 // --repeat=20 // --pallet=pallet_im_online +// --no-storage-info +// --no-median-slopes +// --no-min-squares // --extrinsic=* // --execution=wasm // --wasm-execution=compiled @@ -68,17 +71,17 @@ impl WeightInfo for SubstrateWeight { /// The range of component `e` is `[1, 100]`. fn validate_unsigned_and_then_heartbeat(k: u32, e: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `359 + k * (32 ±0)` - // Estimated: `10345712 + e * (25 ±0) + k * (64 ±0)` - // Minimum execution time: 91_116 nanoseconds. - Weight::from_parts(72_526_877, 10345712) - // Standard Error: 95 - .saturating_add(Weight::from_parts(20_461, 0).saturating_mul(k.into())) - // Standard Error: 967 - .saturating_add(Weight::from_parts(307_869, 0).saturating_mul(e.into())) + // Measured: `295 + k * (32 ±0)` + // Estimated: `10349544 + e * (35 ±0) + k * (64 ±0)` + // Minimum execution time: 106_224_000 picoseconds. + Weight::from_parts(79_172_319, 10349544) + // Standard Error: 259 + .saturating_add(Weight::from_parts(29_576, 0).saturating_mul(k.into())) + // Standard Error: 2_611 + .saturating_add(Weight::from_parts(422_369, 0).saturating_mul(e.into())) .saturating_add(T::DbWeight::get().reads(4_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) - .saturating_add(Weight::from_parts(0, 25).saturating_mul(e.into())) + .saturating_add(Weight::from_parts(0, 35).saturating_mul(e.into())) .saturating_add(Weight::from_parts(0, 64).saturating_mul(k.into())) } } @@ -99,17 +102,17 @@ impl WeightInfo for () { /// The range of component `e` is `[1, 100]`. fn validate_unsigned_and_then_heartbeat(k: u32, e: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `359 + k * (32 ±0)` - // Estimated: `10345712 + e * (25 ±0) + k * (64 ±0)` - // Minimum execution time: 91_116 nanoseconds. - Weight::from_parts(72_526_877, 10345712) - // Standard Error: 95 - .saturating_add(Weight::from_parts(20_461, 0).saturating_mul(k.into())) - // Standard Error: 967 - .saturating_add(Weight::from_parts(307_869, 0).saturating_mul(e.into())) + // Measured: `295 + k * (32 ±0)` + // Estimated: `10349544 + e * (35 ±0) + k * (64 ±0)` + // Minimum execution time: 106_224_000 picoseconds. + Weight::from_parts(79_172_319, 10349544) + // Standard Error: 259 + .saturating_add(Weight::from_parts(29_576, 0).saturating_mul(k.into())) + // Standard Error: 2_611 + .saturating_add(Weight::from_parts(422_369, 0).saturating_mul(e.into())) .saturating_add(RocksDbWeight::get().reads(4_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) - .saturating_add(Weight::from_parts(0, 25).saturating_mul(e.into())) + .saturating_add(Weight::from_parts(0, 35).saturating_mul(e.into())) .saturating_add(Weight::from_parts(0, 64).saturating_mul(k.into())) } } diff --git a/frame/indices/src/weights.rs b/frame/indices/src/weights.rs index fcb71177a..87160ccfb 100644 --- a/frame/indices/src/weights.rs +++ b/frame/indices/src/weights.rs @@ -18,9 +18,9 @@ //! Autogenerated weights for pallet_indices //! //! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev -//! DATE: 2023-01-24, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! DATE: 2023-03-16, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` //! WORST CASE MAP SIZE: `1000000` -//! HOSTNAME: `bm2`, CPU: `Intel(R) Core(TM) i7-7700K CPU @ 4.20GHz` +//! HOSTNAME: `bm3`, CPU: `Intel(R) Core(TM) i7-7700K CPU @ 4.20GHz` //! EXECUTION: Some(Wasm), WASM-EXECUTION: Compiled, CHAIN: Some("dev"), DB CACHE: 1024 // Executed Command: @@ -31,6 +31,9 @@ // --steps=50 // --repeat=20 // --pallet=pallet_indices +// --no-storage-info +// --no-median-slopes +// --no-min-squares // --extrinsic=* // --execution=wasm // --wasm-execution=compiled @@ -63,9 +66,9 @@ impl WeightInfo for SubstrateWeight { fn claim() -> Weight { // Proof Size summary in bytes: // Measured: `76` - // Estimated: `2544` - // Minimum execution time: 19_738 nanoseconds. - Weight::from_parts(20_029_000, 2544) + // Estimated: `3534` + // Minimum execution time: 22_365_000 picoseconds. + Weight::from_parts(22_713_000, 3534) .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -75,10 +78,10 @@ impl WeightInfo for SubstrateWeight { /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) fn transfer() -> Weight { // Proof Size summary in bytes: - // Measured: `339` - // Estimated: `5147` - // Minimum execution time: 24_494 nanoseconds. - Weight::from_parts(24_794_000, 5147) + // Measured: `275` + // Estimated: `7127` + // Minimum execution time: 28_380_000 picoseconds. + Weight::from_parts(29_251_000, 7127) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().writes(2_u64)) } @@ -86,10 +89,10 @@ impl WeightInfo for SubstrateWeight { /// Proof: Indices Accounts (max_values: None, max_size: Some(69), added: 2544, mode: MaxEncodedLen) fn free() -> Weight { // Proof Size summary in bytes: - // Measured: `204` - // Estimated: `2544` - // Minimum execution time: 20_041 nanoseconds. - Weight::from_parts(20_473_000, 2544) + // Measured: `172` + // Estimated: `3534` + // Minimum execution time: 22_932_000 picoseconds. + Weight::from_parts(23_442_000, 3534) .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -99,10 +102,10 @@ impl WeightInfo for SubstrateWeight { /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) fn force_transfer() -> Weight { // Proof Size summary in bytes: - // Measured: `339` - // Estimated: `5147` - // Minimum execution time: 21_874 nanoseconds. - Weight::from_parts(22_438_000, 5147) + // Measured: `275` + // Estimated: `7127` + // Minimum execution time: 25_223_000 picoseconds. + Weight::from_parts(25_519_000, 7127) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().writes(2_u64)) } @@ -110,10 +113,10 @@ impl WeightInfo for SubstrateWeight { /// Proof: Indices Accounts (max_values: None, max_size: Some(69), added: 2544, mode: MaxEncodedLen) fn freeze() -> Weight { // Proof Size summary in bytes: - // Measured: `204` - // Estimated: `2544` - // Minimum execution time: 22_407 nanoseconds. - Weight::from_parts(22_768_000, 2544) + // Measured: `172` + // Estimated: `3534` + // Minimum execution time: 25_223_000 picoseconds. + Weight::from_parts(26_127_000, 3534) .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -126,9 +129,9 @@ impl WeightInfo for () { fn claim() -> Weight { // Proof Size summary in bytes: // Measured: `76` - // Estimated: `2544` - // Minimum execution time: 19_738 nanoseconds. - Weight::from_parts(20_029_000, 2544) + // Estimated: `3534` + // Minimum execution time: 22_365_000 picoseconds. + Weight::from_parts(22_713_000, 3534) .saturating_add(RocksDbWeight::get().reads(1_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } @@ -138,10 +141,10 @@ impl WeightInfo for () { /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) fn transfer() -> Weight { // Proof Size summary in bytes: - // Measured: `339` - // Estimated: `5147` - // Minimum execution time: 24_494 nanoseconds. - Weight::from_parts(24_794_000, 5147) + // Measured: `275` + // Estimated: `7127` + // Minimum execution time: 28_380_000 picoseconds. + Weight::from_parts(29_251_000, 7127) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().writes(2_u64)) } @@ -149,10 +152,10 @@ impl WeightInfo for () { /// Proof: Indices Accounts (max_values: None, max_size: Some(69), added: 2544, mode: MaxEncodedLen) fn free() -> Weight { // Proof Size summary in bytes: - // Measured: `204` - // Estimated: `2544` - // Minimum execution time: 20_041 nanoseconds. - Weight::from_parts(20_473_000, 2544) + // Measured: `172` + // Estimated: `3534` + // Minimum execution time: 22_932_000 picoseconds. + Weight::from_parts(23_442_000, 3534) .saturating_add(RocksDbWeight::get().reads(1_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } @@ -162,10 +165,10 @@ impl WeightInfo for () { /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) fn force_transfer() -> Weight { // Proof Size summary in bytes: - // Measured: `339` - // Estimated: `5147` - // Minimum execution time: 21_874 nanoseconds. - Weight::from_parts(22_438_000, 5147) + // Measured: `275` + // Estimated: `7127` + // Minimum execution time: 25_223_000 picoseconds. + Weight::from_parts(25_519_000, 7127) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().writes(2_u64)) } @@ -173,10 +176,10 @@ impl WeightInfo for () { /// Proof: Indices Accounts (max_values: None, max_size: Some(69), added: 2544, mode: MaxEncodedLen) fn freeze() -> Weight { // Proof Size summary in bytes: - // Measured: `204` - // Estimated: `2544` - // Minimum execution time: 22_407 nanoseconds. - Weight::from_parts(22_768_000, 2544) + // Measured: `172` + // Estimated: `3534` + // Minimum execution time: 25_223_000 picoseconds. + Weight::from_parts(26_127_000, 3534) .saturating_add(RocksDbWeight::get().reads(1_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } diff --git a/frame/lottery/src/weights.rs b/frame/lottery/src/weights.rs index 0038db621..c8c373ad6 100644 --- a/frame/lottery/src/weights.rs +++ b/frame/lottery/src/weights.rs @@ -18,9 +18,9 @@ //! Autogenerated weights for pallet_lottery //! //! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev -//! DATE: 2023-01-24, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! DATE: 2023-03-16, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` //! WORST CASE MAP SIZE: `1000000` -//! HOSTNAME: `bm2`, CPU: `Intel(R) Core(TM) i7-7700K CPU @ 4.20GHz` +//! HOSTNAME: `bm3`, CPU: `Intel(R) Core(TM) i7-7700K CPU @ 4.20GHz` //! EXECUTION: Some(Wasm), WASM-EXECUTION: Compiled, CHAIN: Some("dev"), DB CACHE: 1024 // Executed Command: @@ -31,6 +31,9 @@ // --steps=50 // --repeat=20 // --pallet=pallet_lottery +// --no-storage-info +// --no-median-slopes +// --no-min-squares // --extrinsic=* // --execution=wasm // --wasm-execution=compiled @@ -75,10 +78,10 @@ impl WeightInfo for SubstrateWeight { /// Proof: Lottery Tickets (max_values: None, max_size: Some(44), added: 2519, mode: MaxEncodedLen) fn buy_ticket() -> Weight { // Proof Size summary in bytes: - // Measured: `484` - // Estimated: `7181` - // Minimum execution time: 62_125 nanoseconds. - Weight::from_parts(63_145_000, 7181) + // Measured: `452` + // Estimated: `13121` + // Minimum execution time: 43_076_000 picoseconds. + Weight::from_parts(43_902_000, 13121) .saturating_add(T::DbWeight::get().reads(6_u64)) .saturating_add(T::DbWeight::get().writes(4_u64)) } @@ -89,10 +92,10 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 7_650 nanoseconds. - Weight::from_parts(8_344_960, 0) - // Standard Error: 2_629 - .saturating_add(Weight::from_parts(268_557, 0).saturating_mul(n.into())) + // Minimum execution time: 8_222_000 picoseconds. + Weight::from_parts(9_166_238, 0) + // Standard Error: 3_489 + .saturating_add(Weight::from_parts(328_564, 0).saturating_mul(n.into())) .saturating_add(T::DbWeight::get().writes(1_u64)) } /// Storage: Lottery Lottery (r:1 w:1) @@ -104,9 +107,9 @@ impl WeightInfo for SubstrateWeight { fn start_lottery() -> Weight { // Proof Size summary in bytes: // Measured: `161` - // Estimated: `3626` - // Minimum execution time: 31_324 nanoseconds. - Weight::from_parts(31_985_000, 3626) + // Estimated: `6596` + // Minimum execution time: 36_555_000 picoseconds. + Weight::from_parts(37_008_000, 6596) .saturating_add(T::DbWeight::get().reads(3_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) } @@ -115,9 +118,9 @@ impl WeightInfo for SubstrateWeight { fn stop_repeat() -> Weight { // Proof Size summary in bytes: // Measured: `219` - // Estimated: `524` - // Minimum execution time: 7_124 nanoseconds. - Weight::from_parts(7_285_000, 524) + // Estimated: `1514` + // Minimum execution time: 8_230_000 picoseconds. + Weight::from_parts(8_355_000, 1514) .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -133,10 +136,10 @@ impl WeightInfo for SubstrateWeight { /// Proof: Lottery Tickets (max_values: None, max_size: Some(44), added: 2519, mode: MaxEncodedLen) fn on_initialize_end() -> Weight { // Proof Size summary in bytes: - // Measured: `556` - // Estimated: `11837` - // Minimum execution time: 50_745 nanoseconds. - Weight::from_parts(52_232_000, 11837) + // Measured: `524` + // Estimated: `16787` + // Minimum execution time: 60_097_000 picoseconds. + Weight::from_parts(62_055_000, 16787) .saturating_add(T::DbWeight::get().reads(6_u64)) .saturating_add(T::DbWeight::get().writes(4_u64)) } @@ -154,10 +157,10 @@ impl WeightInfo for SubstrateWeight { /// Proof: Lottery LotteryIndex (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) fn on_initialize_repeat() -> Weight { // Proof Size summary in bytes: - // Measured: `556` - // Estimated: `12336` - // Minimum execution time: 52_437 nanoseconds. - Weight::from_parts(53_063_000, 12336) + // Measured: `524` + // Estimated: `18276` + // Minimum execution time: 61_354_000 picoseconds. + Weight::from_parts(62_429_000, 18276) .saturating_add(T::DbWeight::get().reads(7_u64)) .saturating_add(T::DbWeight::get().writes(5_u64)) } @@ -181,10 +184,10 @@ impl WeightInfo for () { /// Proof: Lottery Tickets (max_values: None, max_size: Some(44), added: 2519, mode: MaxEncodedLen) fn buy_ticket() -> Weight { // Proof Size summary in bytes: - // Measured: `484` - // Estimated: `7181` - // Minimum execution time: 62_125 nanoseconds. - Weight::from_parts(63_145_000, 7181) + // Measured: `452` + // Estimated: `13121` + // Minimum execution time: 43_076_000 picoseconds. + Weight::from_parts(43_902_000, 13121) .saturating_add(RocksDbWeight::get().reads(6_u64)) .saturating_add(RocksDbWeight::get().writes(4_u64)) } @@ -195,10 +198,10 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 7_650 nanoseconds. - Weight::from_parts(8_344_960, 0) - // Standard Error: 2_629 - .saturating_add(Weight::from_parts(268_557, 0).saturating_mul(n.into())) + // Minimum execution time: 8_222_000 picoseconds. + Weight::from_parts(9_166_238, 0) + // Standard Error: 3_489 + .saturating_add(Weight::from_parts(328_564, 0).saturating_mul(n.into())) .saturating_add(RocksDbWeight::get().writes(1_u64)) } /// Storage: Lottery Lottery (r:1 w:1) @@ -210,9 +213,9 @@ impl WeightInfo for () { fn start_lottery() -> Weight { // Proof Size summary in bytes: // Measured: `161` - // Estimated: `3626` - // Minimum execution time: 31_324 nanoseconds. - Weight::from_parts(31_985_000, 3626) + // Estimated: `6596` + // Minimum execution time: 36_555_000 picoseconds. + Weight::from_parts(37_008_000, 6596) .saturating_add(RocksDbWeight::get().reads(3_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) } @@ -221,9 +224,9 @@ impl WeightInfo for () { fn stop_repeat() -> Weight { // Proof Size summary in bytes: // Measured: `219` - // Estimated: `524` - // Minimum execution time: 7_124 nanoseconds. - Weight::from_parts(7_285_000, 524) + // Estimated: `1514` + // Minimum execution time: 8_230_000 picoseconds. + Weight::from_parts(8_355_000, 1514) .saturating_add(RocksDbWeight::get().reads(1_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } @@ -239,10 +242,10 @@ impl WeightInfo for () { /// Proof: Lottery Tickets (max_values: None, max_size: Some(44), added: 2519, mode: MaxEncodedLen) fn on_initialize_end() -> Weight { // Proof Size summary in bytes: - // Measured: `556` - // Estimated: `11837` - // Minimum execution time: 50_745 nanoseconds. - Weight::from_parts(52_232_000, 11837) + // Measured: `524` + // Estimated: `16787` + // Minimum execution time: 60_097_000 picoseconds. + Weight::from_parts(62_055_000, 16787) .saturating_add(RocksDbWeight::get().reads(6_u64)) .saturating_add(RocksDbWeight::get().writes(4_u64)) } @@ -260,10 +263,10 @@ impl WeightInfo for () { /// Proof: Lottery LotteryIndex (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) fn on_initialize_repeat() -> Weight { // Proof Size summary in bytes: - // Measured: `556` - // Estimated: `12336` - // Minimum execution time: 52_437 nanoseconds. - Weight::from_parts(53_063_000, 12336) + // Measured: `524` + // Estimated: `18276` + // Minimum execution time: 61_354_000 picoseconds. + Weight::from_parts(62_429_000, 18276) .saturating_add(RocksDbWeight::get().reads(7_u64)) .saturating_add(RocksDbWeight::get().writes(5_u64)) } diff --git a/frame/membership/src/weights.rs b/frame/membership/src/weights.rs index f080f842c..31c359b44 100644 --- a/frame/membership/src/weights.rs +++ b/frame/membership/src/weights.rs @@ -18,9 +18,9 @@ //! Autogenerated weights for pallet_membership //! //! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev -//! DATE: 2023-01-24, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! DATE: 2023-03-16, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` //! WORST CASE MAP SIZE: `1000000` -//! HOSTNAME: `bm2`, CPU: `Intel(R) Core(TM) i7-7700K CPU @ 4.20GHz` +//! HOSTNAME: `bm3`, CPU: `Intel(R) Core(TM) i7-7700K CPU @ 4.20GHz` //! EXECUTION: Some(Wasm), WASM-EXECUTION: Compiled, CHAIN: Some("dev"), DB CACHE: 1024 // Executed Command: @@ -31,6 +31,9 @@ // --steps=50 // --repeat=20 // --pallet=pallet_membership +// --no-storage-info +// --no-median-slopes +// --no-min-squares // --extrinsic=* // --execution=wasm // --wasm-execution=compiled @@ -71,12 +74,12 @@ impl WeightInfo for SubstrateWeight { /// The range of component `m` is `[1, 99]`. fn add_member(m: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `238 + m * (64 ±0)` - // Estimated: `4903 + m * (192 ±0)` - // Minimum execution time: 15_673 nanoseconds. - Weight::from_parts(16_830_288, 4903) - // Standard Error: 570 - .saturating_add(Weight::from_parts(41_959, 0).saturating_mul(m.into())) + // Measured: `174 + m * (64 ±0)` + // Estimated: `6691 + m * (192 ±0)` + // Minimum execution time: 17_587_000 picoseconds. + Weight::from_parts(18_658_163, 6691) + // Standard Error: 710 + .saturating_add(Weight::from_parts(46_294, 0).saturating_mul(m.into())) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) .saturating_add(Weight::from_parts(0, 192).saturating_mul(m.into())) @@ -94,12 +97,12 @@ impl WeightInfo for SubstrateWeight { /// The range of component `m` is `[2, 100]`. fn remove_member(m: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `342 + m * (64 ±0)` - // Estimated: `5742 + m * (192 ±0)` - // Minimum execution time: 18_231 nanoseconds. - Weight::from_parts(19_081_297, 5742) - // Standard Error: 571 - .saturating_add(Weight::from_parts(41_331, 0).saturating_mul(m.into())) + // Measured: `278 + m * (64 ±0)` + // Estimated: `8520 + m * (192 ±0)` + // Minimum execution time: 20_402_000 picoseconds. + Weight::from_parts(21_165_819, 8520) + // Standard Error: 643 + .saturating_add(Weight::from_parts(45_481, 0).saturating_mul(m.into())) .saturating_add(T::DbWeight::get().reads(3_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) .saturating_add(Weight::from_parts(0, 192).saturating_mul(m.into())) @@ -117,12 +120,12 @@ impl WeightInfo for SubstrateWeight { /// The range of component `m` is `[2, 100]`. fn swap_member(m: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `342 + m * (64 ±0)` - // Estimated: `5742 + m * (192 ±0)` - // Minimum execution time: 18_517 nanoseconds. - Weight::from_parts(19_388_310, 5742) - // Standard Error: 625 - .saturating_add(Weight::from_parts(51_422, 0).saturating_mul(m.into())) + // Measured: `278 + m * (64 ±0)` + // Estimated: `8520 + m * (192 ±0)` + // Minimum execution time: 20_380_000 picoseconds. + Weight::from_parts(21_633_260, 8520) + // Standard Error: 770 + .saturating_add(Weight::from_parts(55_504, 0).saturating_mul(m.into())) .saturating_add(T::DbWeight::get().reads(3_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) .saturating_add(Weight::from_parts(0, 192).saturating_mul(m.into())) @@ -140,12 +143,12 @@ impl WeightInfo for SubstrateWeight { /// The range of component `m` is `[1, 100]`. fn reset_member(m: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `342 + m * (64 ±0)` - // Estimated: `5742 + m * (192 ±0)` - // Minimum execution time: 17_628 nanoseconds. - Weight::from_parts(19_258_882, 5742) - // Standard Error: 820 - .saturating_add(Weight::from_parts(153_956, 0).saturating_mul(m.into())) + // Measured: `278 + m * (64 ±0)` + // Estimated: `8520 + m * (192 ±0)` + // Minimum execution time: 19_989_000 picoseconds. + Weight::from_parts(22_352_059, 8520) + // Standard Error: 2_878 + .saturating_add(Weight::from_parts(156_367, 0).saturating_mul(m.into())) .saturating_add(T::DbWeight::get().reads(3_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) .saturating_add(Weight::from_parts(0, 192).saturating_mul(m.into())) @@ -163,12 +166,12 @@ impl WeightInfo for SubstrateWeight { /// The range of component `m` is `[1, 100]`. fn change_key(m: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `342 + m * (64 ±0)` - // Estimated: `5742 + m * (192 ±0)` - // Minimum execution time: 19_031 nanoseconds. - Weight::from_parts(20_264_948, 5742) - // Standard Error: 707 - .saturating_add(Weight::from_parts(51_060, 0).saturating_mul(m.into())) + // Measured: `278 + m * (64 ±0)` + // Estimated: `8520 + m * (192 ±0)` + // Minimum execution time: 21_275_000 picoseconds. + Weight::from_parts(23_344_594, 8520) + // Standard Error: 2_750 + .saturating_add(Weight::from_parts(46_736, 0).saturating_mul(m.into())) .saturating_add(T::DbWeight::get().reads(3_u64)) .saturating_add(T::DbWeight::get().writes(4_u64)) .saturating_add(Weight::from_parts(0, 192).saturating_mul(m.into())) @@ -182,12 +185,12 @@ impl WeightInfo for SubstrateWeight { /// The range of component `m` is `[1, 100]`. fn set_prime(m: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `64 + m * (32 ±0)` - // Estimated: `3761 + m * (32 ±0)` - // Minimum execution time: 6_897 nanoseconds. - Weight::from_parts(7_455_387, 3761) - // Standard Error: 326 - .saturating_add(Weight::from_parts(16_653, 0).saturating_mul(m.into())) + // Measured: `32 + m * (32 ±0)` + // Estimated: `4719 + m * (32 ±0)` + // Minimum execution time: 8_087_000 picoseconds. + Weight::from_parts(8_909_627, 4719) + // Standard Error: 1_572 + .saturating_add(Weight::from_parts(17_186, 0).saturating_mul(m.into())) .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(T::DbWeight::get().writes(2_u64)) .saturating_add(Weight::from_parts(0, 32).saturating_mul(m.into())) @@ -201,10 +204,10 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 3_400 nanoseconds. - Weight::from_parts(3_703_421, 0) - // Standard Error: 119 - .saturating_add(Weight::from_parts(915, 0).saturating_mul(m.into())) + // Minimum execution time: 3_752_000 picoseconds. + Weight::from_parts(4_081_144, 0) + // Standard Error: 229 + .saturating_add(Weight::from_parts(1_298, 0).saturating_mul(m.into())) .saturating_add(T::DbWeight::get().writes(2_u64)) } } @@ -222,12 +225,12 @@ impl WeightInfo for () { /// The range of component `m` is `[1, 99]`. fn add_member(m: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `238 + m * (64 ±0)` - // Estimated: `4903 + m * (192 ±0)` - // Minimum execution time: 15_673 nanoseconds. - Weight::from_parts(16_830_288, 4903) - // Standard Error: 570 - .saturating_add(Weight::from_parts(41_959, 0).saturating_mul(m.into())) + // Measured: `174 + m * (64 ±0)` + // Estimated: `6691 + m * (192 ±0)` + // Minimum execution time: 17_587_000 picoseconds. + Weight::from_parts(18_658_163, 6691) + // Standard Error: 710 + .saturating_add(Weight::from_parts(46_294, 0).saturating_mul(m.into())) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) .saturating_add(Weight::from_parts(0, 192).saturating_mul(m.into())) @@ -245,12 +248,12 @@ impl WeightInfo for () { /// The range of component `m` is `[2, 100]`. fn remove_member(m: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `342 + m * (64 ±0)` - // Estimated: `5742 + m * (192 ±0)` - // Minimum execution time: 18_231 nanoseconds. - Weight::from_parts(19_081_297, 5742) - // Standard Error: 571 - .saturating_add(Weight::from_parts(41_331, 0).saturating_mul(m.into())) + // Measured: `278 + m * (64 ±0)` + // Estimated: `8520 + m * (192 ±0)` + // Minimum execution time: 20_402_000 picoseconds. + Weight::from_parts(21_165_819, 8520) + // Standard Error: 643 + .saturating_add(Weight::from_parts(45_481, 0).saturating_mul(m.into())) .saturating_add(RocksDbWeight::get().reads(3_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) .saturating_add(Weight::from_parts(0, 192).saturating_mul(m.into())) @@ -268,12 +271,12 @@ impl WeightInfo for () { /// The range of component `m` is `[2, 100]`. fn swap_member(m: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `342 + m * (64 ±0)` - // Estimated: `5742 + m * (192 ±0)` - // Minimum execution time: 18_517 nanoseconds. - Weight::from_parts(19_388_310, 5742) - // Standard Error: 625 - .saturating_add(Weight::from_parts(51_422, 0).saturating_mul(m.into())) + // Measured: `278 + m * (64 ±0)` + // Estimated: `8520 + m * (192 ±0)` + // Minimum execution time: 20_380_000 picoseconds. + Weight::from_parts(21_633_260, 8520) + // Standard Error: 770 + .saturating_add(Weight::from_parts(55_504, 0).saturating_mul(m.into())) .saturating_add(RocksDbWeight::get().reads(3_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) .saturating_add(Weight::from_parts(0, 192).saturating_mul(m.into())) @@ -291,12 +294,12 @@ impl WeightInfo for () { /// The range of component `m` is `[1, 100]`. fn reset_member(m: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `342 + m * (64 ±0)` - // Estimated: `5742 + m * (192 ±0)` - // Minimum execution time: 17_628 nanoseconds. - Weight::from_parts(19_258_882, 5742) - // Standard Error: 820 - .saturating_add(Weight::from_parts(153_956, 0).saturating_mul(m.into())) + // Measured: `278 + m * (64 ±0)` + // Estimated: `8520 + m * (192 ±0)` + // Minimum execution time: 19_989_000 picoseconds. + Weight::from_parts(22_352_059, 8520) + // Standard Error: 2_878 + .saturating_add(Weight::from_parts(156_367, 0).saturating_mul(m.into())) .saturating_add(RocksDbWeight::get().reads(3_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) .saturating_add(Weight::from_parts(0, 192).saturating_mul(m.into())) @@ -314,12 +317,12 @@ impl WeightInfo for () { /// The range of component `m` is `[1, 100]`. fn change_key(m: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `342 + m * (64 ±0)` - // Estimated: `5742 + m * (192 ±0)` - // Minimum execution time: 19_031 nanoseconds. - Weight::from_parts(20_264_948, 5742) - // Standard Error: 707 - .saturating_add(Weight::from_parts(51_060, 0).saturating_mul(m.into())) + // Measured: `278 + m * (64 ±0)` + // Estimated: `8520 + m * (192 ±0)` + // Minimum execution time: 21_275_000 picoseconds. + Weight::from_parts(23_344_594, 8520) + // Standard Error: 2_750 + .saturating_add(Weight::from_parts(46_736, 0).saturating_mul(m.into())) .saturating_add(RocksDbWeight::get().reads(3_u64)) .saturating_add(RocksDbWeight::get().writes(4_u64)) .saturating_add(Weight::from_parts(0, 192).saturating_mul(m.into())) @@ -333,12 +336,12 @@ impl WeightInfo for () { /// The range of component `m` is `[1, 100]`. fn set_prime(m: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `64 + m * (32 ±0)` - // Estimated: `3761 + m * (32 ±0)` - // Minimum execution time: 6_897 nanoseconds. - Weight::from_parts(7_455_387, 3761) - // Standard Error: 326 - .saturating_add(Weight::from_parts(16_653, 0).saturating_mul(m.into())) + // Measured: `32 + m * (32 ±0)` + // Estimated: `4719 + m * (32 ±0)` + // Minimum execution time: 8_087_000 picoseconds. + Weight::from_parts(8_909_627, 4719) + // Standard Error: 1_572 + .saturating_add(Weight::from_parts(17_186, 0).saturating_mul(m.into())) .saturating_add(RocksDbWeight::get().reads(1_u64)) .saturating_add(RocksDbWeight::get().writes(2_u64)) .saturating_add(Weight::from_parts(0, 32).saturating_mul(m.into())) @@ -352,10 +355,10 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 3_400 nanoseconds. - Weight::from_parts(3_703_421, 0) - // Standard Error: 119 - .saturating_add(Weight::from_parts(915, 0).saturating_mul(m.into())) + // Minimum execution time: 3_752_000 picoseconds. + Weight::from_parts(4_081_144, 0) + // Standard Error: 229 + .saturating_add(Weight::from_parts(1_298, 0).saturating_mul(m.into())) .saturating_add(RocksDbWeight::get().writes(2_u64)) } } diff --git a/frame/message-queue/src/weights.rs b/frame/message-queue/src/weights.rs index fd788f2ba..fc44456db 100644 --- a/frame/message-queue/src/weights.rs +++ b/frame/message-queue/src/weights.rs @@ -18,26 +18,28 @@ //! Autogenerated weights for pallet_message_queue //! //! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev -//! DATE: 2023-03-06, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! DATE: 2023-03-16, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` //! WORST CASE MAP SIZE: `1000000` //! HOSTNAME: `bm3`, CPU: `Intel(R) Core(TM) i7-7700K CPU @ 4.20GHz` //! EXECUTION: Some(Wasm), WASM-EXECUTION: Compiled, CHAIN: Some("dev"), DB CACHE: 1024 // Executed Command: -// target/production/substrate +// ./target/production/substrate // benchmark // pallet +// --chain=dev // --steps=50 // --repeat=20 +// --pallet=pallet_message_queue +// --no-storage-info +// --no-median-slopes +// --no-min-squares // --extrinsic=* // --execution=wasm // --wasm-execution=compiled // --heap-pages=4096 -// --json-file=/var/lib/gitlab-runner/builds/zyw4fam_/0/parity/mirrors/substrate/.git/.artifacts/bench.json -// --pallet=pallet-message-queue -// --chain=dev -// --header=./HEADER-APACHE2 // --output=./frame/message-queue/src/weights.rs +// --header=./HEADER-APACHE2 // --template=./.maintain/frame-weight-template.hbs #![cfg_attr(rustfmt, rustfmt_skip)] @@ -70,10 +72,10 @@ impl WeightInfo for SubstrateWeight { /// Proof: MessageQueue BookStateFor (max_values: None, max_size: Some(49), added: 2524, mode: MaxEncodedLen) fn ready_ring_knit() -> Weight { // Proof Size summary in bytes: - // Measured: `295` + // Measured: `233` // Estimated: `7527` - // Minimum execution time: 12_283_000 picoseconds. - Weight::from_parts(12_554_000, 7527) + // Minimum execution time: 12_561_000 picoseconds. + Weight::from_parts(12_758_000, 7527) .saturating_add(T::DbWeight::get().reads(3_u64)) .saturating_add(T::DbWeight::get().writes(2_u64)) } @@ -83,10 +85,10 @@ impl WeightInfo for SubstrateWeight { /// Proof: MessageQueue ServiceHead (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) fn ready_ring_unknit() -> Weight { // Proof Size summary in bytes: - // Measured: `295` + // Measured: `233` // Estimated: `7527` - // Minimum execution time: 11_484_000 picoseconds. - Weight::from_parts(11_900_000, 7527) + // Minimum execution time: 11_854_000 picoseconds. + Weight::from_parts(12_178_000, 7527) .saturating_add(T::DbWeight::get().reads(3_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) } @@ -96,8 +98,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `42` // Estimated: `3514` - // Minimum execution time: 4_793_000 picoseconds. - Weight::from_parts(4_990_000, 3514) + // Minimum execution time: 7_900_000 picoseconds. + Weight::from_parts(8_046_000, 3514) .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -107,8 +109,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `113` // Estimated: `69049` - // Minimum execution time: 6_231_000 picoseconds. - Weight::from_parts(6_442_000, 69049) + // Minimum execution time: 6_284_000 picoseconds. + Weight::from_parts(6_433_000, 69049) .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -118,8 +120,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `113` // Estimated: `69049` - // Minimum execution time: 6_660_000 picoseconds. - Weight::from_parts(6_825_000, 69049) + // Minimum execution time: 6_418_000 picoseconds. + Weight::from_parts(6_633_000, 69049) .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -127,8 +129,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 72_805_000 picoseconds. - Weight::from_parts(74_650_000, 0) + // Minimum execution time: 52_661_000 picoseconds. + Weight::from_parts(52_994_000, 0) } /// Storage: MessageQueue ServiceHead (r:1 w:1) /// Proof: MessageQueue ServiceHead (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) @@ -136,10 +138,10 @@ impl WeightInfo for SubstrateWeight { /// Proof: MessageQueue BookStateFor (max_values: None, max_size: Some(49), added: 2524, mode: MaxEncodedLen) fn bump_service_head() -> Weight { // Proof Size summary in bytes: - // Measured: `172` + // Measured: `140` // Estimated: `5003` - // Minimum execution time: 7_078_000 picoseconds. - Weight::from_parts(7_230_000, 5003) + // Minimum execution time: 7_132_000 picoseconds. + Weight::from_parts(7_386_000, 5003) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -149,10 +151,10 @@ impl WeightInfo for SubstrateWeight { /// Proof: MessageQueue Pages (max_values: None, max_size: Some(65584), added: 68059, mode: MaxEncodedLen) fn reap_page() -> Weight { // Proof Size summary in bytes: - // Measured: `65742` + // Measured: `65710` // Estimated: `72563` - // Minimum execution time: 56_799_000 picoseconds. - Weight::from_parts(57_634_000, 72563) + // Minimum execution time: 54_377_000 picoseconds. + Weight::from_parts(54_804_000, 72563) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().writes(2_u64)) } @@ -162,10 +164,10 @@ impl WeightInfo for SubstrateWeight { /// Proof: MessageQueue Pages (max_values: None, max_size: Some(65584), added: 68059, mode: MaxEncodedLen) fn execute_overweight_page_removed() -> Weight { // Proof Size summary in bytes: - // Measured: `65742` + // Measured: `65710` // Estimated: `72563` - // Minimum execution time: 72_290_000 picoseconds. - Weight::from_parts(72_754_000, 72563) + // Minimum execution time: 69_461_000 picoseconds. + Weight::from_parts(70_016_000, 72563) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().writes(2_u64)) } @@ -175,10 +177,10 @@ impl WeightInfo for SubstrateWeight { /// Proof: MessageQueue Pages (max_values: None, max_size: Some(65584), added: 68059, mode: MaxEncodedLen) fn execute_overweight_page_updated() -> Weight { // Proof Size summary in bytes: - // Measured: `65742` + // Measured: `65710` // Estimated: `72563` - // Minimum execution time: 84_987_000 picoseconds. - Weight::from_parts(85_562_000, 72563) + // Minimum execution time: 81_787_000 picoseconds. + Weight::from_parts(83_100_000, 72563) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().writes(2_u64)) } @@ -192,10 +194,10 @@ impl WeightInfo for () { /// Proof: MessageQueue BookStateFor (max_values: None, max_size: Some(49), added: 2524, mode: MaxEncodedLen) fn ready_ring_knit() -> Weight { // Proof Size summary in bytes: - // Measured: `295` + // Measured: `233` // Estimated: `7527` - // Minimum execution time: 12_283_000 picoseconds. - Weight::from_parts(12_554_000, 7527) + // Minimum execution time: 12_561_000 picoseconds. + Weight::from_parts(12_758_000, 7527) .saturating_add(RocksDbWeight::get().reads(3_u64)) .saturating_add(RocksDbWeight::get().writes(2_u64)) } @@ -205,10 +207,10 @@ impl WeightInfo for () { /// Proof: MessageQueue ServiceHead (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) fn ready_ring_unknit() -> Weight { // Proof Size summary in bytes: - // Measured: `295` + // Measured: `233` // Estimated: `7527` - // Minimum execution time: 11_484_000 picoseconds. - Weight::from_parts(11_900_000, 7527) + // Minimum execution time: 11_854_000 picoseconds. + Weight::from_parts(12_178_000, 7527) .saturating_add(RocksDbWeight::get().reads(3_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) } @@ -218,8 +220,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `42` // Estimated: `3514` - // Minimum execution time: 4_793_000 picoseconds. - Weight::from_parts(4_990_000, 3514) + // Minimum execution time: 7_900_000 picoseconds. + Weight::from_parts(8_046_000, 3514) .saturating_add(RocksDbWeight::get().reads(1_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } @@ -229,8 +231,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `113` // Estimated: `69049` - // Minimum execution time: 6_231_000 picoseconds. - Weight::from_parts(6_442_000, 69049) + // Minimum execution time: 6_284_000 picoseconds. + Weight::from_parts(6_433_000, 69049) .saturating_add(RocksDbWeight::get().reads(1_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } @@ -240,8 +242,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `113` // Estimated: `69049` - // Minimum execution time: 6_660_000 picoseconds. - Weight::from_parts(6_825_000, 69049) + // Minimum execution time: 6_418_000 picoseconds. + Weight::from_parts(6_633_000, 69049) .saturating_add(RocksDbWeight::get().reads(1_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } @@ -249,8 +251,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 72_805_000 picoseconds. - Weight::from_parts(74_650_000, 0) + // Minimum execution time: 52_661_000 picoseconds. + Weight::from_parts(52_994_000, 0) } /// Storage: MessageQueue ServiceHead (r:1 w:1) /// Proof: MessageQueue ServiceHead (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) @@ -258,10 +260,10 @@ impl WeightInfo for () { /// Proof: MessageQueue BookStateFor (max_values: None, max_size: Some(49), added: 2524, mode: MaxEncodedLen) fn bump_service_head() -> Weight { // Proof Size summary in bytes: - // Measured: `172` + // Measured: `140` // Estimated: `5003` - // Minimum execution time: 7_078_000 picoseconds. - Weight::from_parts(7_230_000, 5003) + // Minimum execution time: 7_132_000 picoseconds. + Weight::from_parts(7_386_000, 5003) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } @@ -271,10 +273,10 @@ impl WeightInfo for () { /// Proof: MessageQueue Pages (max_values: None, max_size: Some(65584), added: 68059, mode: MaxEncodedLen) fn reap_page() -> Weight { // Proof Size summary in bytes: - // Measured: `65742` + // Measured: `65710` // Estimated: `72563` - // Minimum execution time: 56_799_000 picoseconds. - Weight::from_parts(57_634_000, 72563) + // Minimum execution time: 54_377_000 picoseconds. + Weight::from_parts(54_804_000, 72563) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().writes(2_u64)) } @@ -284,10 +286,10 @@ impl WeightInfo for () { /// Proof: MessageQueue Pages (max_values: None, max_size: Some(65584), added: 68059, mode: MaxEncodedLen) fn execute_overweight_page_removed() -> Weight { // Proof Size summary in bytes: - // Measured: `65742` + // Measured: `65710` // Estimated: `72563` - // Minimum execution time: 72_290_000 picoseconds. - Weight::from_parts(72_754_000, 72563) + // Minimum execution time: 69_461_000 picoseconds. + Weight::from_parts(70_016_000, 72563) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().writes(2_u64)) } @@ -297,10 +299,10 @@ impl WeightInfo for () { /// Proof: MessageQueue Pages (max_values: None, max_size: Some(65584), added: 68059, mode: MaxEncodedLen) fn execute_overweight_page_updated() -> Weight { // Proof Size summary in bytes: - // Measured: `65742` + // Measured: `65710` // Estimated: `72563` - // Minimum execution time: 84_987_000 picoseconds. - Weight::from_parts(85_562_000, 72563) + // Minimum execution time: 81_787_000 picoseconds. + Weight::from_parts(83_100_000, 72563) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().writes(2_u64)) } diff --git a/frame/multisig/src/weights.rs b/frame/multisig/src/weights.rs index fb155c97f..8941ce44f 100644 --- a/frame/multisig/src/weights.rs +++ b/frame/multisig/src/weights.rs @@ -18,9 +18,9 @@ //! Autogenerated weights for pallet_multisig //! //! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev -//! DATE: 2023-01-24, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! DATE: 2023-03-16, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` //! WORST CASE MAP SIZE: `1000000` -//! HOSTNAME: `bm2`, CPU: `Intel(R) Core(TM) i7-7700K CPU @ 4.20GHz` +//! HOSTNAME: `bm3`, CPU: `Intel(R) Core(TM) i7-7700K CPU @ 4.20GHz` //! EXECUTION: Some(Wasm), WASM-EXECUTION: Compiled, CHAIN: Some("dev"), DB CACHE: 1024 // Executed Command: @@ -31,6 +31,9 @@ // --steps=50 // --repeat=20 // --pallet=pallet_multisig +// --no-storage-info +// --no-median-slopes +// --no-min-squares // --extrinsic=* // --execution=wasm // --wasm-execution=compiled @@ -65,8 +68,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 12_086 nanoseconds. - Weight::from_parts(12_464_828, 0) + // Minimum execution time: 12_096_000 picoseconds. + Weight::from_parts(12_442_958, 0) // Standard Error: 1 .saturating_add(Weight::from_parts(494, 0).saturating_mul(z.into())) } @@ -76,14 +79,14 @@ impl WeightInfo for SubstrateWeight { /// The range of component `z` is `[0, 10000]`. fn as_multi_create(s: u32, z: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `352 + s * (2 ±0)` - // Estimated: `5821` - // Minimum execution time: 35_377 nanoseconds. - Weight::from_parts(29_088_956, 5821) - // Standard Error: 335 - .saturating_add(Weight::from_parts(67_846, 0).saturating_mul(s.into())) - // Standard Error: 3 - .saturating_add(Weight::from_parts(1_523, 0).saturating_mul(z.into())) + // Measured: `301 + s * (2 ±0)` + // Estimated: `6811` + // Minimum execution time: 37_609_000 picoseconds. + Weight::from_parts(32_693_100, 6811) + // Standard Error: 449 + .saturating_add(Weight::from_parts(56_623, 0).saturating_mul(s.into())) + // Standard Error: 4 + .saturating_add(Weight::from_parts(1_203, 0).saturating_mul(z.into())) .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -93,14 +96,14 @@ impl WeightInfo for SubstrateWeight { /// The range of component `z` is `[0, 10000]`. fn as_multi_approve(s: u32, z: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `351` - // Estimated: `5821` - // Minimum execution time: 26_138 nanoseconds. - Weight::from_parts(20_479_380, 5821) - // Standard Error: 259 - .saturating_add(Weight::from_parts(64_116, 0).saturating_mul(s.into())) - // Standard Error: 2 - .saturating_add(Weight::from_parts(1_520, 0).saturating_mul(z.into())) + // Measured: `320` + // Estimated: `6811` + // Minimum execution time: 27_434_000 picoseconds. + Weight::from_parts(22_659_090, 6811) + // Standard Error: 328 + .saturating_add(Weight::from_parts(53_508, 0).saturating_mul(s.into())) + // Standard Error: 3 + .saturating_add(Weight::from_parts(1_213, 0).saturating_mul(z.into())) .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -112,14 +115,14 @@ impl WeightInfo for SubstrateWeight { /// The range of component `z` is `[0, 10000]`. fn as_multi_complete(s: u32, z: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `489 + s * (33 ±0)` - // Estimated: `8424` - // Minimum execution time: 40_323 nanoseconds. - Weight::from_parts(32_311_615, 8424) - // Standard Error: 401 - .saturating_add(Weight::from_parts(85_999, 0).saturating_mul(s.into())) - // Standard Error: 3 - .saturating_add(Weight::from_parts(1_534, 0).saturating_mul(z.into())) + // Measured: `426 + s * (33 ±0)` + // Estimated: `10404` + // Minimum execution time: 43_592_000 picoseconds. + Weight::from_parts(36_465_266, 10404) + // Standard Error: 1_049 + .saturating_add(Weight::from_parts(78_833, 0).saturating_mul(s.into())) + // Standard Error: 10 + .saturating_add(Weight::from_parts(1_238, 0).saturating_mul(z.into())) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().writes(2_u64)) } @@ -128,12 +131,12 @@ impl WeightInfo for SubstrateWeight { /// The range of component `s` is `[2, 100]`. fn approve_as_multi_create(s: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `359 + s * (2 ±0)` - // Estimated: `5821` - // Minimum execution time: 26_938 nanoseconds. - Weight::from_parts(27_802_216, 5821) - // Standard Error: 342 - .saturating_add(Weight::from_parts(69_282, 0).saturating_mul(s.into())) + // Measured: `301 + s * (2 ±0)` + // Estimated: `6811` + // Minimum execution time: 30_216_000 picoseconds. + Weight::from_parts(31_430_373, 6811) + // Standard Error: 967 + .saturating_add(Weight::from_parts(57_035, 0).saturating_mul(s.into())) .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -142,12 +145,12 @@ impl WeightInfo for SubstrateWeight { /// The range of component `s` is `[2, 100]`. fn approve_as_multi_approve(s: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `351` - // Estimated: `5821` - // Minimum execution time: 18_050 nanoseconds. - Weight::from_parts(19_095_404, 5821) - // Standard Error: 419 - .saturating_add(Weight::from_parts(66_914, 0).saturating_mul(s.into())) + // Measured: `320` + // Estimated: `6811` + // Minimum execution time: 19_960_000 picoseconds. + Weight::from_parts(21_281_659, 6811) + // Standard Error: 435 + .saturating_add(Weight::from_parts(56_445, 0).saturating_mul(s.into())) .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -156,12 +159,12 @@ impl WeightInfo for SubstrateWeight { /// The range of component `s` is `[2, 100]`. fn cancel_as_multi(s: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `555 + s * (1 ±0)` - // Estimated: `5821` - // Minimum execution time: 27_508 nanoseconds. - Weight::from_parts(28_702_686, 5821) - // Standard Error: 466 - .saturating_add(Weight::from_parts(69_419, 0).saturating_mul(s.into())) + // Measured: `492 + s * (1 ±0)` + // Estimated: `6811` + // Minimum execution time: 31_219_000 picoseconds. + Weight::from_parts(32_395_963, 6811) + // Standard Error: 500 + .saturating_add(Weight::from_parts(56_853, 0).saturating_mul(s.into())) .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -174,8 +177,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 12_086 nanoseconds. - Weight::from_parts(12_464_828, 0) + // Minimum execution time: 12_096_000 picoseconds. + Weight::from_parts(12_442_958, 0) // Standard Error: 1 .saturating_add(Weight::from_parts(494, 0).saturating_mul(z.into())) } @@ -185,14 +188,14 @@ impl WeightInfo for () { /// The range of component `z` is `[0, 10000]`. fn as_multi_create(s: u32, z: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `352 + s * (2 ±0)` - // Estimated: `5821` - // Minimum execution time: 35_377 nanoseconds. - Weight::from_parts(29_088_956, 5821) - // Standard Error: 335 - .saturating_add(Weight::from_parts(67_846, 0).saturating_mul(s.into())) - // Standard Error: 3 - .saturating_add(Weight::from_parts(1_523, 0).saturating_mul(z.into())) + // Measured: `301 + s * (2 ±0)` + // Estimated: `6811` + // Minimum execution time: 37_609_000 picoseconds. + Weight::from_parts(32_693_100, 6811) + // Standard Error: 449 + .saturating_add(Weight::from_parts(56_623, 0).saturating_mul(s.into())) + // Standard Error: 4 + .saturating_add(Weight::from_parts(1_203, 0).saturating_mul(z.into())) .saturating_add(RocksDbWeight::get().reads(1_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } @@ -202,14 +205,14 @@ impl WeightInfo for () { /// The range of component `z` is `[0, 10000]`. fn as_multi_approve(s: u32, z: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `351` - // Estimated: `5821` - // Minimum execution time: 26_138 nanoseconds. - Weight::from_parts(20_479_380, 5821) - // Standard Error: 259 - .saturating_add(Weight::from_parts(64_116, 0).saturating_mul(s.into())) - // Standard Error: 2 - .saturating_add(Weight::from_parts(1_520, 0).saturating_mul(z.into())) + // Measured: `320` + // Estimated: `6811` + // Minimum execution time: 27_434_000 picoseconds. + Weight::from_parts(22_659_090, 6811) + // Standard Error: 328 + .saturating_add(Weight::from_parts(53_508, 0).saturating_mul(s.into())) + // Standard Error: 3 + .saturating_add(Weight::from_parts(1_213, 0).saturating_mul(z.into())) .saturating_add(RocksDbWeight::get().reads(1_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } @@ -221,14 +224,14 @@ impl WeightInfo for () { /// The range of component `z` is `[0, 10000]`. fn as_multi_complete(s: u32, z: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `489 + s * (33 ±0)` - // Estimated: `8424` - // Minimum execution time: 40_323 nanoseconds. - Weight::from_parts(32_311_615, 8424) - // Standard Error: 401 - .saturating_add(Weight::from_parts(85_999, 0).saturating_mul(s.into())) - // Standard Error: 3 - .saturating_add(Weight::from_parts(1_534, 0).saturating_mul(z.into())) + // Measured: `426 + s * (33 ±0)` + // Estimated: `10404` + // Minimum execution time: 43_592_000 picoseconds. + Weight::from_parts(36_465_266, 10404) + // Standard Error: 1_049 + .saturating_add(Weight::from_parts(78_833, 0).saturating_mul(s.into())) + // Standard Error: 10 + .saturating_add(Weight::from_parts(1_238, 0).saturating_mul(z.into())) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().writes(2_u64)) } @@ -237,12 +240,12 @@ impl WeightInfo for () { /// The range of component `s` is `[2, 100]`. fn approve_as_multi_create(s: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `359 + s * (2 ±0)` - // Estimated: `5821` - // Minimum execution time: 26_938 nanoseconds. - Weight::from_parts(27_802_216, 5821) - // Standard Error: 342 - .saturating_add(Weight::from_parts(69_282, 0).saturating_mul(s.into())) + // Measured: `301 + s * (2 ±0)` + // Estimated: `6811` + // Minimum execution time: 30_216_000 picoseconds. + Weight::from_parts(31_430_373, 6811) + // Standard Error: 967 + .saturating_add(Weight::from_parts(57_035, 0).saturating_mul(s.into())) .saturating_add(RocksDbWeight::get().reads(1_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } @@ -251,12 +254,12 @@ impl WeightInfo for () { /// The range of component `s` is `[2, 100]`. fn approve_as_multi_approve(s: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `351` - // Estimated: `5821` - // Minimum execution time: 18_050 nanoseconds. - Weight::from_parts(19_095_404, 5821) - // Standard Error: 419 - .saturating_add(Weight::from_parts(66_914, 0).saturating_mul(s.into())) + // Measured: `320` + // Estimated: `6811` + // Minimum execution time: 19_960_000 picoseconds. + Weight::from_parts(21_281_659, 6811) + // Standard Error: 435 + .saturating_add(Weight::from_parts(56_445, 0).saturating_mul(s.into())) .saturating_add(RocksDbWeight::get().reads(1_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } @@ -265,12 +268,12 @@ impl WeightInfo for () { /// The range of component `s` is `[2, 100]`. fn cancel_as_multi(s: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `555 + s * (1 ±0)` - // Estimated: `5821` - // Minimum execution time: 27_508 nanoseconds. - Weight::from_parts(28_702_686, 5821) - // Standard Error: 466 - .saturating_add(Weight::from_parts(69_419, 0).saturating_mul(s.into())) + // Measured: `492 + s * (1 ±0)` + // Estimated: `6811` + // Minimum execution time: 31_219_000 picoseconds. + Weight::from_parts(32_395_963, 6811) + // Standard Error: 500 + .saturating_add(Weight::from_parts(56_853, 0).saturating_mul(s.into())) .saturating_add(RocksDbWeight::get().reads(1_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } diff --git a/frame/nfts/src/weights.rs b/frame/nfts/src/weights.rs index 3d447c8d2..c5010a73c 100644 --- a/frame/nfts/src/weights.rs +++ b/frame/nfts/src/weights.rs @@ -18,26 +18,28 @@ //! Autogenerated weights for pallet_nfts //! //! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev -//! DATE: 2023-03-10, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! DATE: 2023-03-16, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` //! WORST CASE MAP SIZE: `1000000` //! HOSTNAME: `bm3`, CPU: `Intel(R) Core(TM) i7-7700K CPU @ 4.20GHz` //! EXECUTION: Some(Wasm), WASM-EXECUTION: Compiled, CHAIN: Some("dev"), DB CACHE: 1024 // Executed Command: -// target/production/substrate +// ./target/production/substrate // benchmark // pallet +// --chain=dev // --steps=50 // --repeat=20 +// --pallet=pallet_nfts +// --no-storage-info +// --no-median-slopes +// --no-min-squares // --extrinsic=* // --execution=wasm // --wasm-execution=compiled // --heap-pages=4096 -// --json-file=/var/lib/gitlab-runner/builds/zyw4fam_/0/parity/mirrors/substrate/.git/.artifacts/bench.json -// --pallet=pallet_nfts -// --chain=dev -// --header=./HEADER-APACHE2 // --output=./frame/nfts/src/weights.rs +// --header=./HEADER-APACHE2 // --template=./.maintain/frame-weight-template.hbs #![cfg_attr(rustfmt, rustfmt_skip)] @@ -105,10 +107,10 @@ impl WeightInfo for SubstrateWeight { /// Proof: Nfts CollectionAccount (max_values: None, max_size: Some(68), added: 2543, mode: MaxEncodedLen) fn create() -> Weight { // Proof Size summary in bytes: - // Measured: `214` + // Measured: `182` // Estimated: `5038` - // Minimum execution time: 37_598_000 picoseconds. - Weight::from_parts(38_703_000, 5038) + // Minimum execution time: 36_780_000 picoseconds. + Weight::from_parts(37_508_000, 5038) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().writes(5_u64)) } @@ -126,8 +128,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `42` // Estimated: `5038` - // Minimum execution time: 25_899_000 picoseconds. - Weight::from_parts(26_385_000, 5038) + // Minimum execution time: 25_144_000 picoseconds. + Weight::from_parts(25_800_000, 5038) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().writes(5_u64)) } @@ -152,14 +154,14 @@ impl WeightInfo for SubstrateWeight { /// The range of component `a` is `[0, 1000]`. fn destroy(m: u32, _c: u32, a: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `32218 + a * (364 ±0)` + // Measured: `32186 + a * (332 ±0)` // Estimated: `2538589 + a * (2921 ±0)` - // Minimum execution time: 1_129_980_000 picoseconds. - Weight::from_parts(1_096_213_543, 2538589) - // Standard Error: 5_210 - .saturating_add(Weight::from_parts(92, 0).saturating_mul(m.into())) - // Standard Error: 5_210 - .saturating_add(Weight::from_parts(5_480_550, 0).saturating_mul(a.into())) + // Minimum execution time: 1_122_858_000 picoseconds. + Weight::from_parts(1_094_711_913, 2538589) + // Standard Error: 4_485 + .saturating_add(Weight::from_parts(3_481, 0).saturating_mul(m.into())) + // Standard Error: 4_485 + .saturating_add(Weight::from_parts(5_520_602, 0).saturating_mul(a.into())) .saturating_add(T::DbWeight::get().reads(1004_u64)) .saturating_add(T::DbWeight::get().reads((1_u64).saturating_mul(a.into()))) .saturating_add(T::DbWeight::get().writes(1005_u64)) @@ -180,10 +182,10 @@ impl WeightInfo for SubstrateWeight { /// Proof: Nfts Account (max_values: None, max_size: Some(88), added: 2563, mode: MaxEncodedLen) fn mint() -> Weight { // Proof Size summary in bytes: - // Measured: `453` + // Measured: `421` // Estimated: `18460` - // Minimum execution time: 49_434_000 picoseconds. - Weight::from_parts(50_248_000, 18460) + // Minimum execution time: 48_590_000 picoseconds. + Weight::from_parts(49_260_000, 18460) .saturating_add(T::DbWeight::get().reads(5_u64)) .saturating_add(T::DbWeight::get().writes(4_u64)) } @@ -201,10 +203,10 @@ impl WeightInfo for SubstrateWeight { /// Proof: Nfts Account (max_values: None, max_size: Some(88), added: 2563, mode: MaxEncodedLen) fn force_mint() -> Weight { // Proof Size summary in bytes: - // Measured: `453` + // Measured: `421` // Estimated: `18460` - // Minimum execution time: 47_393_000 picoseconds. - Weight::from_parts(47_906_000, 18460) + // Minimum execution time: 46_646_000 picoseconds. + Weight::from_parts(47_331_000, 18460) .saturating_add(T::DbWeight::get().reads(5_u64)) .saturating_add(T::DbWeight::get().writes(4_u64)) } @@ -226,10 +228,10 @@ impl WeightInfo for SubstrateWeight { /// Proof: Nfts PendingSwapOf (max_values: None, max_size: Some(71), added: 2546, mode: MaxEncodedLen) fn burn() -> Weight { // Proof Size summary in bytes: - // Measured: `594` + // Measured: `530` // Estimated: `14993` - // Minimum execution time: 47_188_000 picoseconds. - Weight::from_parts(47_746_000, 14993) + // Minimum execution time: 46_976_000 picoseconds. + Weight::from_parts(47_606_000, 14993) .saturating_add(T::DbWeight::get().reads(4_u64)) .saturating_add(T::DbWeight::get().writes(7_u64)) } @@ -249,10 +251,10 @@ impl WeightInfo for SubstrateWeight { /// Proof: Nfts PendingSwapOf (max_values: None, max_size: Some(71), added: 2546, mode: MaxEncodedLen) fn transfer() -> Weight { // Proof Size summary in bytes: - // Measured: `623` + // Measured: `559` // Estimated: `14926` - // Minimum execution time: 37_984_000 picoseconds. - Weight::from_parts(38_446_000, 14926) + // Minimum execution time: 38_013_000 picoseconds. + Weight::from_parts(38_420_000, 14926) .saturating_add(T::DbWeight::get().reads(4_u64)) .saturating_add(T::DbWeight::get().writes(5_u64)) } @@ -265,12 +267,12 @@ impl WeightInfo for SubstrateWeight { /// The range of component `i` is `[0, 5000]`. fn redeposit(i: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `761 + i * (140 ±0)` + // Measured: `729 + i * (108 ±0)` // Estimated: `8077 + i * (3336 ±0)` - // Minimum execution time: 17_297_000 picoseconds. - Weight::from_parts(17_634_000, 8077) - // Standard Error: 17_773 - .saturating_add(Weight::from_parts(14_395_819, 0).saturating_mul(i.into())) + // Minimum execution time: 17_428_000 picoseconds. + Weight::from_parts(17_664_000, 8077) + // Standard Error: 16_385 + .saturating_add(Weight::from_parts(14_150_405, 0).saturating_mul(i.into())) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().reads((1_u64).saturating_mul(i.into()))) .saturating_add(T::DbWeight::get().writes((1_u64).saturating_mul(i.into()))) @@ -284,8 +286,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `401` // Estimated: `7047` - // Minimum execution time: 21_827_000 picoseconds. - Weight::from_parts(22_134_000, 7047) + // Minimum execution time: 21_760_000 picoseconds. + Weight::from_parts(22_063_000, 7047) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -297,8 +299,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `401` // Estimated: `7047` - // Minimum execution time: 21_549_000 picoseconds. - Weight::from_parts(21_987_000, 7047) + // Minimum execution time: 21_622_000 picoseconds. + Weight::from_parts(22_056_000, 7047) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -308,10 +310,10 @@ impl WeightInfo for SubstrateWeight { /// Proof: Nfts CollectionConfigOf (max_values: None, max_size: Some(73), added: 2548, mode: MaxEncodedLen) fn lock_collection() -> Weight { // Proof Size summary in bytes: - // Measured: `338` + // Measured: `306` // Estimated: `7087` - // Minimum execution time: 18_930_000 picoseconds. - Weight::from_parts(19_200_000, 7087) + // Minimum execution time: 18_820_000 picoseconds. + Weight::from_parts(19_212_000, 7087) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -323,24 +325,24 @@ impl WeightInfo for SubstrateWeight { /// Proof: Nfts CollectionAccount (max_values: None, max_size: Some(68), added: 2543, mode: MaxEncodedLen) fn transfer_ownership() -> Weight { // Proof Size summary in bytes: - // Measured: `386` + // Measured: `354` // Estimated: `7066` - // Minimum execution time: 24_929_000 picoseconds. - Weight::from_parts(25_786_000, 7066) + // Minimum execution time: 25_433_000 picoseconds. + Weight::from_parts(25_793_000, 7066) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().writes(4_u64)) } /// Storage: Nfts Collection (r:1 w:1) /// Proof: Nfts Collection (max_values: None, max_size: Some(84), added: 2559, mode: MaxEncodedLen) - /// Storage: Nfts CollectionRoleOf (r:1 w:4) + /// Storage: Nfts CollectionRoleOf (r:2 w:4) /// Proof: Nfts CollectionRoleOf (max_values: None, max_size: Some(69), added: 2544, mode: MaxEncodedLen) fn set_team() -> Weight { // Proof Size summary in bytes: - // Measured: `367` - // Estimated: `7083` - // Minimum execution time: 28_245_000 picoseconds. - Weight::from_parts(28_490_000, 7083) - .saturating_add(T::DbWeight::get().reads(2_u64)) + // Measured: `335` + // Estimated: `9627` + // Minimum execution time: 43_368_000 picoseconds. + Weight::from_parts(43_974_000, 9627) + .saturating_add(T::DbWeight::get().reads(3_u64)) .saturating_add(T::DbWeight::get().writes(5_u64)) } /// Storage: Nfts Collection (r:1 w:1) @@ -349,10 +351,10 @@ impl WeightInfo for SubstrateWeight { /// Proof: Nfts CollectionAccount (max_values: None, max_size: Some(68), added: 2543, mode: MaxEncodedLen) fn force_collection_owner() -> Weight { // Proof Size summary in bytes: - // Measured: `309` + // Measured: `277` // Estimated: `3549` - // Minimum execution time: 19_971_000 picoseconds. - Weight::from_parts(20_276_000, 3549) + // Minimum execution time: 20_009_000 picoseconds. + Weight::from_parts(20_220_000, 3549) .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) } @@ -364,8 +366,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `242` // Estimated: `3549` - // Minimum execution time: 15_924_000 picoseconds. - Weight::from_parts(16_258_000, 3549) + // Minimum execution time: 16_048_000 picoseconds. + Weight::from_parts(16_353_000, 3549) .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -377,8 +379,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `401` // Estimated: `7047` - // Minimum execution time: 20_780_000 picoseconds. - Weight::from_parts(21_109_000, 7047) + // Minimum execution time: 20_890_000 picoseconds. + Weight::from_parts(21_237_000, 7047) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -394,10 +396,10 @@ impl WeightInfo for SubstrateWeight { /// Proof: Nfts Attribute (max_values: None, max_size: Some(446), added: 2921, mode: MaxEncodedLen) fn set_attribute() -> Weight { // Proof Size summary in bytes: - // Measured: `537` + // Measured: `505` // Estimated: `18045` - // Minimum execution time: 50_817_000 picoseconds. - Weight::from_parts(51_585_000, 18045) + // Minimum execution time: 50_686_000 picoseconds. + Weight::from_parts(50_981_000, 18045) .saturating_add(T::DbWeight::get().reads(5_u64)) .saturating_add(T::DbWeight::get().writes(2_u64)) } @@ -407,10 +409,10 @@ impl WeightInfo for SubstrateWeight { /// Proof: Nfts Attribute (max_values: None, max_size: Some(446), added: 2921, mode: MaxEncodedLen) fn force_set_attribute() -> Weight { // Proof Size summary in bytes: - // Measured: `342` + // Measured: `310` // Estimated: `7460` - // Minimum execution time: 28_821_000 picoseconds. - Weight::from_parts(29_417_000, 7460) + // Minimum execution time: 28_714_000 picoseconds. + Weight::from_parts(29_044_000, 7460) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().writes(2_u64)) } @@ -424,10 +426,10 @@ impl WeightInfo for SubstrateWeight { /// Proof: Nfts Collection (max_values: None, max_size: Some(84), added: 2559, mode: MaxEncodedLen) fn clear_attribute() -> Weight { // Proof Size summary in bytes: - // Measured: `979` + // Measured: `916` // Estimated: `14507` - // Minimum execution time: 47_021_000 picoseconds. - Weight::from_parts(47_509_000, 14507) + // Minimum execution time: 46_232_000 picoseconds. + Weight::from_parts(46_738_000, 14507) .saturating_add(T::DbWeight::get().reads(4_u64)) .saturating_add(T::DbWeight::get().writes(2_u64)) } @@ -437,10 +439,10 @@ impl WeightInfo for SubstrateWeight { /// Proof: Nfts ItemAttributesApprovalsOf (max_values: None, max_size: Some(681), added: 3156, mode: MaxEncodedLen) fn approve_item_attributes() -> Weight { // Proof Size summary in bytes: - // Measured: `379` + // Measured: `347` // Estimated: `8472` - // Minimum execution time: 19_457_000 picoseconds. - Weight::from_parts(19_738_000, 8472) + // Minimum execution time: 19_332_000 picoseconds. + Weight::from_parts(19_765_000, 8472) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -455,12 +457,12 @@ impl WeightInfo for SubstrateWeight { /// The range of component `n` is `[0, 1000]`. fn cancel_item_attributes_approval(n: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `899 + n * (396 ±0)` + // Measured: `803 + n * (364 ±0)` // Estimated: `15976 + n * (2921 ±0)` - // Minimum execution time: 29_027_000 picoseconds. - Weight::from_parts(29_376_000, 15976) - // Standard Error: 3_500 - .saturating_add(Weight::from_parts(5_568_116, 0).saturating_mul(n.into())) + // Minimum execution time: 29_227_000 picoseconds. + Weight::from_parts(29_661_000, 15976) + // Standard Error: 3_574 + .saturating_add(Weight::from_parts(5_442_937, 0).saturating_mul(n.into())) .saturating_add(T::DbWeight::get().reads(4_u64)) .saturating_add(T::DbWeight::get().reads((1_u64).saturating_mul(n.into()))) .saturating_add(T::DbWeight::get().writes(2_u64)) @@ -479,10 +481,10 @@ impl WeightInfo for SubstrateWeight { /// Proof: Nfts ItemMetadataOf (max_values: None, max_size: Some(140), added: 2615, mode: MaxEncodedLen) fn set_metadata() -> Weight { // Proof Size summary in bytes: - // Measured: `537` + // Measured: `505` // Estimated: `17739` - // Minimum execution time: 41_658_000 picoseconds. - Weight::from_parts(42_241_000, 17739) + // Minimum execution time: 41_297_000 picoseconds. + Weight::from_parts(41_902_000, 17739) .saturating_add(T::DbWeight::get().reads(5_u64)) .saturating_add(T::DbWeight::get().writes(2_u64)) } @@ -496,10 +498,10 @@ impl WeightInfo for SubstrateWeight { /// Proof: Nfts ItemConfigOf (max_values: None, max_size: Some(48), added: 2523, mode: MaxEncodedLen) fn clear_metadata() -> Weight { // Proof Size summary in bytes: - // Measured: `672` + // Measured: `608` // Estimated: `14201` - // Minimum execution time: 39_838_000 picoseconds. - Weight::from_parts(40_757_000, 14201) + // Minimum execution time: 39_312_000 picoseconds. + Weight::from_parts(40_048_000, 14201) .saturating_add(T::DbWeight::get().reads(4_u64)) .saturating_add(T::DbWeight::get().writes(2_u64)) } @@ -513,10 +515,10 @@ impl WeightInfo for SubstrateWeight { /// Proof: Nfts CollectionMetadataOf (max_values: None, max_size: Some(87), added: 2562, mode: MaxEncodedLen) fn set_collection_metadata() -> Weight { // Proof Size summary in bytes: - // Measured: `396` + // Measured: `364` // Estimated: `14173` - // Minimum execution time: 37_327_000 picoseconds. - Weight::from_parts(37_874_000, 14173) + // Minimum execution time: 36_932_000 picoseconds. + Weight::from_parts(38_207_000, 14173) .saturating_add(T::DbWeight::get().reads(4_u64)) .saturating_add(T::DbWeight::get().writes(2_u64)) } @@ -530,10 +532,10 @@ impl WeightInfo for SubstrateWeight { /// Proof: Nfts CollectionMetadataOf (max_values: None, max_size: Some(87), added: 2562, mode: MaxEncodedLen) fn clear_collection_metadata() -> Weight { // Proof Size summary in bytes: - // Measured: `539` + // Measured: `475` // Estimated: `14173` - // Minimum execution time: 35_305_000 picoseconds. - Weight::from_parts(36_213_000, 14173) + // Minimum execution time: 35_264_000 picoseconds. + Weight::from_parts(35_829_000, 14173) .saturating_add(T::DbWeight::get().reads(4_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -543,10 +545,10 @@ impl WeightInfo for SubstrateWeight { /// Proof: Nfts CollectionConfigOf (max_values: None, max_size: Some(73), added: 2548, mode: MaxEncodedLen) fn approve_transfer() -> Weight { // Proof Size summary in bytes: - // Measured: `408` + // Measured: `376` // Estimated: `7864` - // Minimum execution time: 22_680_000 picoseconds. - Weight::from_parts(23_126_000, 7864) + // Minimum execution time: 22_819_000 picoseconds. + Weight::from_parts(23_121_000, 7864) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -554,10 +556,10 @@ impl WeightInfo for SubstrateWeight { /// Proof: Nfts Item (max_values: None, max_size: Some(861), added: 3336, mode: MaxEncodedLen) fn cancel_approval() -> Weight { // Proof Size summary in bytes: - // Measured: `416` + // Measured: `384` // Estimated: `4326` - // Minimum execution time: 19_837_000 picoseconds. - Weight::from_parts(20_352_000, 4326) + // Minimum execution time: 20_377_000 picoseconds. + Weight::from_parts(20_625_000, 4326) .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -565,10 +567,10 @@ impl WeightInfo for SubstrateWeight { /// Proof: Nfts Item (max_values: None, max_size: Some(861), added: 3336, mode: MaxEncodedLen) fn clear_all_transfer_approvals() -> Weight { // Proof Size summary in bytes: - // Measured: `416` + // Measured: `384` // Estimated: `4326` - // Minimum execution time: 18_911_000 picoseconds. - Weight::from_parts(19_233_000, 4326) + // Minimum execution time: 19_180_000 picoseconds. + Weight::from_parts(19_468_000, 4326) .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -578,8 +580,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `42` // Estimated: `3517` - // Minimum execution time: 17_292_000 picoseconds. - Weight::from_parts(17_568_000, 3517) + // Minimum execution time: 17_381_000 picoseconds. + Weight::from_parts(17_718_000, 3517) .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -589,10 +591,10 @@ impl WeightInfo for SubstrateWeight { /// Proof: Nfts Collection (max_values: None, max_size: Some(84), added: 2559, mode: MaxEncodedLen) fn set_collection_max_supply() -> Weight { // Proof Size summary in bytes: - // Measured: `338` + // Measured: `306` // Estimated: `7087` - // Minimum execution time: 20_323_000 picoseconds. - Weight::from_parts(20_692_000, 7087) + // Minimum execution time: 20_373_000 picoseconds. + Weight::from_parts(20_662_000, 7087) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -604,8 +606,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `289` // Estimated: `7072` - // Minimum execution time: 19_971_000 picoseconds. - Weight::from_parts(20_159_000, 7072) + // Minimum execution time: 20_154_000 picoseconds. + Weight::from_parts(20_592_000, 7072) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -619,10 +621,10 @@ impl WeightInfo for SubstrateWeight { /// Proof: Nfts ItemPriceOf (max_values: None, max_size: Some(89), added: 2564, mode: MaxEncodedLen) fn set_price() -> Weight { // Proof Size summary in bytes: - // Measured: `516` + // Measured: `484` // Estimated: `11377` - // Minimum execution time: 26_202_000 picoseconds. - Weight::from_parts(26_499_000, 11377) + // Minimum execution time: 25_644_000 picoseconds. + Weight::from_parts(25_999_000, 11377) .saturating_add(T::DbWeight::get().reads(3_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -642,10 +644,10 @@ impl WeightInfo for SubstrateWeight { /// Proof: Nfts PendingSwapOf (max_values: None, max_size: Some(71), added: 2546, mode: MaxEncodedLen) fn buy_item() -> Weight { // Proof Size summary in bytes: - // Measured: `767` + // Measured: `671` // Estimated: `18480` - // Minimum execution time: 50_601_000 picoseconds. - Weight::from_parts(51_283_000, 18480) + // Minimum execution time: 51_542_000 picoseconds. + Weight::from_parts(52_203_000, 18480) .saturating_add(T::DbWeight::get().reads(5_u64)) .saturating_add(T::DbWeight::get().writes(5_u64)) } @@ -654,10 +656,10 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 2_733_000 picoseconds. - Weight::from_parts(5_568_590, 0) - // Standard Error: 14_136 - .saturating_add(Weight::from_parts(3_763_928, 0).saturating_mul(n.into())) + // Minimum execution time: 2_796_000 picoseconds. + Weight::from_parts(5_517_617, 0) + // Standard Error: 14_247 + .saturating_add(Weight::from_parts(3_757_796, 0).saturating_mul(n.into())) } /// Storage: Nfts Item (r:2 w:0) /// Proof: Nfts Item (max_values: None, max_size: Some(861), added: 3336, mode: MaxEncodedLen) @@ -665,10 +667,10 @@ impl WeightInfo for SubstrateWeight { /// Proof: Nfts PendingSwapOf (max_values: None, max_size: Some(71), added: 2546, mode: MaxEncodedLen) fn create_swap() -> Weight { // Proof Size summary in bytes: - // Measured: `524` + // Measured: `460` // Estimated: `7662` - // Minimum execution time: 22_901_000 picoseconds. - Weight::from_parts(23_673_000, 7662) + // Minimum execution time: 23_218_000 picoseconds. + Weight::from_parts(23_590_000, 7662) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -678,10 +680,10 @@ impl WeightInfo for SubstrateWeight { /// Proof: Nfts Item (max_values: None, max_size: Some(861), added: 3336, mode: MaxEncodedLen) fn cancel_swap() -> Weight { // Proof Size summary in bytes: - // Measured: `511` + // Measured: `479` // Estimated: `7862` - // Minimum execution time: 22_532_000 picoseconds. - Weight::from_parts(22_831_000, 7862) + // Minimum execution time: 22_536_000 picoseconds. + Weight::from_parts(22_874_000, 7862) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -701,21 +703,21 @@ impl WeightInfo for SubstrateWeight { /// Proof: Nfts ItemPriceOf (max_values: None, max_size: Some(89), added: 2564, mode: MaxEncodedLen) fn claim_swap() -> Weight { // Proof Size summary in bytes: - // Measured: `896` + // Measured: `800` // Estimated: `24321` - // Minimum execution time: 77_668_000 picoseconds. - Weight::from_parts(78_407_000, 24321) + // Minimum execution time: 77_372_000 picoseconds. + Weight::from_parts(78_109_000, 24321) .saturating_add(T::DbWeight::get().reads(7_u64)) .saturating_add(T::DbWeight::get().writes(10_u64)) } - /// Storage: Nfts Collection (r:1 w:1) - /// Proof: Nfts Collection (max_values: None, max_size: Some(84), added: 2559, mode: MaxEncodedLen) /// Storage: Nfts CollectionRoleOf (r:2 w:0) /// Proof: Nfts CollectionRoleOf (max_values: None, max_size: Some(69), added: 2544, mode: MaxEncodedLen) /// Storage: Nfts CollectionConfigOf (r:1 w:0) /// Proof: Nfts CollectionConfigOf (max_values: None, max_size: Some(73), added: 2548, mode: MaxEncodedLen) /// Storage: Nfts Item (r:1 w:1) /// Proof: Nfts Item (max_values: None, max_size: Some(861), added: 3336, mode: MaxEncodedLen) + /// Storage: Nfts Collection (r:1 w:1) + /// Proof: Nfts Collection (max_values: None, max_size: Some(84), added: 2559, mode: MaxEncodedLen) /// Storage: Nfts ItemConfigOf (r:1 w:1) /// Proof: Nfts ItemConfigOf (max_values: None, max_size: Some(48), added: 2523, mode: MaxEncodedLen) /// Storage: System Account (r:1 w:1) @@ -729,12 +731,12 @@ impl WeightInfo for SubstrateWeight { /// The range of component `n` is `[0, 10]`. fn mint_pre_signed(n: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `659` + // Measured: `595` // Estimated: `29192 + n * (2921 ±0)` - // Minimum execution time: 133_147_000 picoseconds. - Weight::from_parts(138_031_439, 29192) - // Standard Error: 28_665 - .saturating_add(Weight::from_parts(28_206_062, 0).saturating_mul(n.into())) + // Minimum execution time: 131_935_000 picoseconds. + Weight::from_parts(137_455_891, 29192) + // Standard Error: 31_617 + .saturating_add(Weight::from_parts(27_550_750, 0).saturating_mul(n.into())) .saturating_add(T::DbWeight::get().reads(8_u64)) .saturating_add(T::DbWeight::get().reads((1_u64).saturating_mul(n.into()))) .saturating_add(T::DbWeight::get().writes(6_u64)) @@ -756,12 +758,12 @@ impl WeightInfo for SubstrateWeight { /// The range of component `n` is `[0, 10]`. fn set_attributes_pre_signed(n: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `721` + // Measured: `625` // Estimated: `20142 + n * (2921 ±0)` - // Minimum execution time: 78_243_000 picoseconds. - Weight::from_parts(90_947_408, 20142) - // Standard Error: 75_516 - .saturating_add(Weight::from_parts(28_059_623, 0).saturating_mul(n.into())) + // Minimum execution time: 77_694_000 picoseconds. + Weight::from_parts(90_089_732, 20142) + // Standard Error: 71_327 + .saturating_add(Weight::from_parts(27_832_189, 0).saturating_mul(n.into())) .saturating_add(T::DbWeight::get().reads(4_u64)) .saturating_add(T::DbWeight::get().reads((1_u64).saturating_mul(n.into()))) .saturating_add(T::DbWeight::get().writes(2_u64)) @@ -784,10 +786,10 @@ impl WeightInfo for () { /// Proof: Nfts CollectionAccount (max_values: None, max_size: Some(68), added: 2543, mode: MaxEncodedLen) fn create() -> Weight { // Proof Size summary in bytes: - // Measured: `214` + // Measured: `182` // Estimated: `5038` - // Minimum execution time: 37_598_000 picoseconds. - Weight::from_parts(38_703_000, 5038) + // Minimum execution time: 36_780_000 picoseconds. + Weight::from_parts(37_508_000, 5038) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().writes(5_u64)) } @@ -805,8 +807,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `42` // Estimated: `5038` - // Minimum execution time: 25_899_000 picoseconds. - Weight::from_parts(26_385_000, 5038) + // Minimum execution time: 25_144_000 picoseconds. + Weight::from_parts(25_800_000, 5038) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().writes(5_u64)) } @@ -831,14 +833,14 @@ impl WeightInfo for () { /// The range of component `a` is `[0, 1000]`. fn destroy(m: u32, _c: u32, a: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `32218 + a * (364 ±0)` + // Measured: `32186 + a * (332 ±0)` // Estimated: `2538589 + a * (2921 ±0)` - // Minimum execution time: 1_129_980_000 picoseconds. - Weight::from_parts(1_096_213_543, 2538589) - // Standard Error: 5_210 - .saturating_add(Weight::from_parts(92, 0).saturating_mul(m.into())) - // Standard Error: 5_210 - .saturating_add(Weight::from_parts(5_480_550, 0).saturating_mul(a.into())) + // Minimum execution time: 1_122_858_000 picoseconds. + Weight::from_parts(1_094_711_913, 2538589) + // Standard Error: 4_485 + .saturating_add(Weight::from_parts(3_481, 0).saturating_mul(m.into())) + // Standard Error: 4_485 + .saturating_add(Weight::from_parts(5_520_602, 0).saturating_mul(a.into())) .saturating_add(RocksDbWeight::get().reads(1004_u64)) .saturating_add(RocksDbWeight::get().reads((1_u64).saturating_mul(a.into()))) .saturating_add(RocksDbWeight::get().writes(1005_u64)) @@ -859,10 +861,10 @@ impl WeightInfo for () { /// Proof: Nfts Account (max_values: None, max_size: Some(88), added: 2563, mode: MaxEncodedLen) fn mint() -> Weight { // Proof Size summary in bytes: - // Measured: `453` + // Measured: `421` // Estimated: `18460` - // Minimum execution time: 49_434_000 picoseconds. - Weight::from_parts(50_248_000, 18460) + // Minimum execution time: 48_590_000 picoseconds. + Weight::from_parts(49_260_000, 18460) .saturating_add(RocksDbWeight::get().reads(5_u64)) .saturating_add(RocksDbWeight::get().writes(4_u64)) } @@ -880,10 +882,10 @@ impl WeightInfo for () { /// Proof: Nfts Account (max_values: None, max_size: Some(88), added: 2563, mode: MaxEncodedLen) fn force_mint() -> Weight { // Proof Size summary in bytes: - // Measured: `453` + // Measured: `421` // Estimated: `18460` - // Minimum execution time: 47_393_000 picoseconds. - Weight::from_parts(47_906_000, 18460) + // Minimum execution time: 46_646_000 picoseconds. + Weight::from_parts(47_331_000, 18460) .saturating_add(RocksDbWeight::get().reads(5_u64)) .saturating_add(RocksDbWeight::get().writes(4_u64)) } @@ -905,10 +907,10 @@ impl WeightInfo for () { /// Proof: Nfts PendingSwapOf (max_values: None, max_size: Some(71), added: 2546, mode: MaxEncodedLen) fn burn() -> Weight { // Proof Size summary in bytes: - // Measured: `594` + // Measured: `530` // Estimated: `14993` - // Minimum execution time: 47_188_000 picoseconds. - Weight::from_parts(47_746_000, 14993) + // Minimum execution time: 46_976_000 picoseconds. + Weight::from_parts(47_606_000, 14993) .saturating_add(RocksDbWeight::get().reads(4_u64)) .saturating_add(RocksDbWeight::get().writes(7_u64)) } @@ -928,10 +930,10 @@ impl WeightInfo for () { /// Proof: Nfts PendingSwapOf (max_values: None, max_size: Some(71), added: 2546, mode: MaxEncodedLen) fn transfer() -> Weight { // Proof Size summary in bytes: - // Measured: `623` + // Measured: `559` // Estimated: `14926` - // Minimum execution time: 37_984_000 picoseconds. - Weight::from_parts(38_446_000, 14926) + // Minimum execution time: 38_013_000 picoseconds. + Weight::from_parts(38_420_000, 14926) .saturating_add(RocksDbWeight::get().reads(4_u64)) .saturating_add(RocksDbWeight::get().writes(5_u64)) } @@ -944,12 +946,12 @@ impl WeightInfo for () { /// The range of component `i` is `[0, 5000]`. fn redeposit(i: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `761 + i * (140 ±0)` + // Measured: `729 + i * (108 ±0)` // Estimated: `8077 + i * (3336 ±0)` - // Minimum execution time: 17_297_000 picoseconds. - Weight::from_parts(17_634_000, 8077) - // Standard Error: 17_773 - .saturating_add(Weight::from_parts(14_395_819, 0).saturating_mul(i.into())) + // Minimum execution time: 17_428_000 picoseconds. + Weight::from_parts(17_664_000, 8077) + // Standard Error: 16_385 + .saturating_add(Weight::from_parts(14_150_405, 0).saturating_mul(i.into())) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().reads((1_u64).saturating_mul(i.into()))) .saturating_add(RocksDbWeight::get().writes((1_u64).saturating_mul(i.into()))) @@ -963,8 +965,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `401` // Estimated: `7047` - // Minimum execution time: 21_827_000 picoseconds. - Weight::from_parts(22_134_000, 7047) + // Minimum execution time: 21_760_000 picoseconds. + Weight::from_parts(22_063_000, 7047) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } @@ -976,8 +978,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `401` // Estimated: `7047` - // Minimum execution time: 21_549_000 picoseconds. - Weight::from_parts(21_987_000, 7047) + // Minimum execution time: 21_622_000 picoseconds. + Weight::from_parts(22_056_000, 7047) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } @@ -987,10 +989,10 @@ impl WeightInfo for () { /// Proof: Nfts CollectionConfigOf (max_values: None, max_size: Some(73), added: 2548, mode: MaxEncodedLen) fn lock_collection() -> Weight { // Proof Size summary in bytes: - // Measured: `338` + // Measured: `306` // Estimated: `7087` - // Minimum execution time: 18_930_000 picoseconds. - Weight::from_parts(19_200_000, 7087) + // Minimum execution time: 18_820_000 picoseconds. + Weight::from_parts(19_212_000, 7087) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } @@ -1002,24 +1004,24 @@ impl WeightInfo for () { /// Proof: Nfts CollectionAccount (max_values: None, max_size: Some(68), added: 2543, mode: MaxEncodedLen) fn transfer_ownership() -> Weight { // Proof Size summary in bytes: - // Measured: `386` + // Measured: `354` // Estimated: `7066` - // Minimum execution time: 24_929_000 picoseconds. - Weight::from_parts(25_786_000, 7066) + // Minimum execution time: 25_433_000 picoseconds. + Weight::from_parts(25_793_000, 7066) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().writes(4_u64)) } /// Storage: Nfts Collection (r:1 w:1) /// Proof: Nfts Collection (max_values: None, max_size: Some(84), added: 2559, mode: MaxEncodedLen) - /// Storage: Nfts CollectionRoleOf (r:1 w:4) + /// Storage: Nfts CollectionRoleOf (r:2 w:4) /// Proof: Nfts CollectionRoleOf (max_values: None, max_size: Some(69), added: 2544, mode: MaxEncodedLen) fn set_team() -> Weight { // Proof Size summary in bytes: - // Measured: `367` - // Estimated: `7083` - // Minimum execution time: 28_245_000 picoseconds. - Weight::from_parts(28_490_000, 7083) - .saturating_add(RocksDbWeight::get().reads(2_u64)) + // Measured: `335` + // Estimated: `9627` + // Minimum execution time: 43_368_000 picoseconds. + Weight::from_parts(43_974_000, 9627) + .saturating_add(RocksDbWeight::get().reads(3_u64)) .saturating_add(RocksDbWeight::get().writes(5_u64)) } /// Storage: Nfts Collection (r:1 w:1) @@ -1028,10 +1030,10 @@ impl WeightInfo for () { /// Proof: Nfts CollectionAccount (max_values: None, max_size: Some(68), added: 2543, mode: MaxEncodedLen) fn force_collection_owner() -> Weight { // Proof Size summary in bytes: - // Measured: `309` + // Measured: `277` // Estimated: `3549` - // Minimum execution time: 19_971_000 picoseconds. - Weight::from_parts(20_276_000, 3549) + // Minimum execution time: 20_009_000 picoseconds. + Weight::from_parts(20_220_000, 3549) .saturating_add(RocksDbWeight::get().reads(1_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) } @@ -1043,8 +1045,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `242` // Estimated: `3549` - // Minimum execution time: 15_924_000 picoseconds. - Weight::from_parts(16_258_000, 3549) + // Minimum execution time: 16_048_000 picoseconds. + Weight::from_parts(16_353_000, 3549) .saturating_add(RocksDbWeight::get().reads(1_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } @@ -1056,8 +1058,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `401` // Estimated: `7047` - // Minimum execution time: 20_780_000 picoseconds. - Weight::from_parts(21_109_000, 7047) + // Minimum execution time: 20_890_000 picoseconds. + Weight::from_parts(21_237_000, 7047) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } @@ -1073,10 +1075,10 @@ impl WeightInfo for () { /// Proof: Nfts Attribute (max_values: None, max_size: Some(446), added: 2921, mode: MaxEncodedLen) fn set_attribute() -> Weight { // Proof Size summary in bytes: - // Measured: `537` + // Measured: `505` // Estimated: `18045` - // Minimum execution time: 50_817_000 picoseconds. - Weight::from_parts(51_585_000, 18045) + // Minimum execution time: 50_686_000 picoseconds. + Weight::from_parts(50_981_000, 18045) .saturating_add(RocksDbWeight::get().reads(5_u64)) .saturating_add(RocksDbWeight::get().writes(2_u64)) } @@ -1086,10 +1088,10 @@ impl WeightInfo for () { /// Proof: Nfts Attribute (max_values: None, max_size: Some(446), added: 2921, mode: MaxEncodedLen) fn force_set_attribute() -> Weight { // Proof Size summary in bytes: - // Measured: `342` + // Measured: `310` // Estimated: `7460` - // Minimum execution time: 28_821_000 picoseconds. - Weight::from_parts(29_417_000, 7460) + // Minimum execution time: 28_714_000 picoseconds. + Weight::from_parts(29_044_000, 7460) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().writes(2_u64)) } @@ -1103,10 +1105,10 @@ impl WeightInfo for () { /// Proof: Nfts Collection (max_values: None, max_size: Some(84), added: 2559, mode: MaxEncodedLen) fn clear_attribute() -> Weight { // Proof Size summary in bytes: - // Measured: `979` + // Measured: `916` // Estimated: `14507` - // Minimum execution time: 47_021_000 picoseconds. - Weight::from_parts(47_509_000, 14507) + // Minimum execution time: 46_232_000 picoseconds. + Weight::from_parts(46_738_000, 14507) .saturating_add(RocksDbWeight::get().reads(4_u64)) .saturating_add(RocksDbWeight::get().writes(2_u64)) } @@ -1116,10 +1118,10 @@ impl WeightInfo for () { /// Proof: Nfts ItemAttributesApprovalsOf (max_values: None, max_size: Some(681), added: 3156, mode: MaxEncodedLen) fn approve_item_attributes() -> Weight { // Proof Size summary in bytes: - // Measured: `379` + // Measured: `347` // Estimated: `8472` - // Minimum execution time: 19_457_000 picoseconds. - Weight::from_parts(19_738_000, 8472) + // Minimum execution time: 19_332_000 picoseconds. + Weight::from_parts(19_765_000, 8472) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } @@ -1134,12 +1136,12 @@ impl WeightInfo for () { /// The range of component `n` is `[0, 1000]`. fn cancel_item_attributes_approval(n: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `899 + n * (396 ±0)` + // Measured: `803 + n * (364 ±0)` // Estimated: `15976 + n * (2921 ±0)` - // Minimum execution time: 29_027_000 picoseconds. - Weight::from_parts(29_376_000, 15976) - // Standard Error: 3_500 - .saturating_add(Weight::from_parts(5_568_116, 0).saturating_mul(n.into())) + // Minimum execution time: 29_227_000 picoseconds. + Weight::from_parts(29_661_000, 15976) + // Standard Error: 3_574 + .saturating_add(Weight::from_parts(5_442_937, 0).saturating_mul(n.into())) .saturating_add(RocksDbWeight::get().reads(4_u64)) .saturating_add(RocksDbWeight::get().reads((1_u64).saturating_mul(n.into()))) .saturating_add(RocksDbWeight::get().writes(2_u64)) @@ -1158,10 +1160,10 @@ impl WeightInfo for () { /// Proof: Nfts ItemMetadataOf (max_values: None, max_size: Some(140), added: 2615, mode: MaxEncodedLen) fn set_metadata() -> Weight { // Proof Size summary in bytes: - // Measured: `537` + // Measured: `505` // Estimated: `17739` - // Minimum execution time: 41_658_000 picoseconds. - Weight::from_parts(42_241_000, 17739) + // Minimum execution time: 41_297_000 picoseconds. + Weight::from_parts(41_902_000, 17739) .saturating_add(RocksDbWeight::get().reads(5_u64)) .saturating_add(RocksDbWeight::get().writes(2_u64)) } @@ -1175,10 +1177,10 @@ impl WeightInfo for () { /// Proof: Nfts ItemConfigOf (max_values: None, max_size: Some(48), added: 2523, mode: MaxEncodedLen) fn clear_metadata() -> Weight { // Proof Size summary in bytes: - // Measured: `672` + // Measured: `608` // Estimated: `14201` - // Minimum execution time: 39_838_000 picoseconds. - Weight::from_parts(40_757_000, 14201) + // Minimum execution time: 39_312_000 picoseconds. + Weight::from_parts(40_048_000, 14201) .saturating_add(RocksDbWeight::get().reads(4_u64)) .saturating_add(RocksDbWeight::get().writes(2_u64)) } @@ -1192,10 +1194,10 @@ impl WeightInfo for () { /// Proof: Nfts CollectionMetadataOf (max_values: None, max_size: Some(87), added: 2562, mode: MaxEncodedLen) fn set_collection_metadata() -> Weight { // Proof Size summary in bytes: - // Measured: `396` + // Measured: `364` // Estimated: `14173` - // Minimum execution time: 37_327_000 picoseconds. - Weight::from_parts(37_874_000, 14173) + // Minimum execution time: 36_932_000 picoseconds. + Weight::from_parts(38_207_000, 14173) .saturating_add(RocksDbWeight::get().reads(4_u64)) .saturating_add(RocksDbWeight::get().writes(2_u64)) } @@ -1209,10 +1211,10 @@ impl WeightInfo for () { /// Proof: Nfts CollectionMetadataOf (max_values: None, max_size: Some(87), added: 2562, mode: MaxEncodedLen) fn clear_collection_metadata() -> Weight { // Proof Size summary in bytes: - // Measured: `539` + // Measured: `475` // Estimated: `14173` - // Minimum execution time: 35_305_000 picoseconds. - Weight::from_parts(36_213_000, 14173) + // Minimum execution time: 35_264_000 picoseconds. + Weight::from_parts(35_829_000, 14173) .saturating_add(RocksDbWeight::get().reads(4_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } @@ -1222,10 +1224,10 @@ impl WeightInfo for () { /// Proof: Nfts CollectionConfigOf (max_values: None, max_size: Some(73), added: 2548, mode: MaxEncodedLen) fn approve_transfer() -> Weight { // Proof Size summary in bytes: - // Measured: `408` + // Measured: `376` // Estimated: `7864` - // Minimum execution time: 22_680_000 picoseconds. - Weight::from_parts(23_126_000, 7864) + // Minimum execution time: 22_819_000 picoseconds. + Weight::from_parts(23_121_000, 7864) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } @@ -1233,10 +1235,10 @@ impl WeightInfo for () { /// Proof: Nfts Item (max_values: None, max_size: Some(861), added: 3336, mode: MaxEncodedLen) fn cancel_approval() -> Weight { // Proof Size summary in bytes: - // Measured: `416` + // Measured: `384` // Estimated: `4326` - // Minimum execution time: 19_837_000 picoseconds. - Weight::from_parts(20_352_000, 4326) + // Minimum execution time: 20_377_000 picoseconds. + Weight::from_parts(20_625_000, 4326) .saturating_add(RocksDbWeight::get().reads(1_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } @@ -1244,10 +1246,10 @@ impl WeightInfo for () { /// Proof: Nfts Item (max_values: None, max_size: Some(861), added: 3336, mode: MaxEncodedLen) fn clear_all_transfer_approvals() -> Weight { // Proof Size summary in bytes: - // Measured: `416` + // Measured: `384` // Estimated: `4326` - // Minimum execution time: 18_911_000 picoseconds. - Weight::from_parts(19_233_000, 4326) + // Minimum execution time: 19_180_000 picoseconds. + Weight::from_parts(19_468_000, 4326) .saturating_add(RocksDbWeight::get().reads(1_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } @@ -1257,8 +1259,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `42` // Estimated: `3517` - // Minimum execution time: 17_292_000 picoseconds. - Weight::from_parts(17_568_000, 3517) + // Minimum execution time: 17_381_000 picoseconds. + Weight::from_parts(17_718_000, 3517) .saturating_add(RocksDbWeight::get().reads(1_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } @@ -1268,10 +1270,10 @@ impl WeightInfo for () { /// Proof: Nfts Collection (max_values: None, max_size: Some(84), added: 2559, mode: MaxEncodedLen) fn set_collection_max_supply() -> Weight { // Proof Size summary in bytes: - // Measured: `338` + // Measured: `306` // Estimated: `7087` - // Minimum execution time: 20_323_000 picoseconds. - Weight::from_parts(20_692_000, 7087) + // Minimum execution time: 20_373_000 picoseconds. + Weight::from_parts(20_662_000, 7087) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } @@ -1283,8 +1285,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `289` // Estimated: `7072` - // Minimum execution time: 19_971_000 picoseconds. - Weight::from_parts(20_159_000, 7072) + // Minimum execution time: 20_154_000 picoseconds. + Weight::from_parts(20_592_000, 7072) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } @@ -1298,10 +1300,10 @@ impl WeightInfo for () { /// Proof: Nfts ItemPriceOf (max_values: None, max_size: Some(89), added: 2564, mode: MaxEncodedLen) fn set_price() -> Weight { // Proof Size summary in bytes: - // Measured: `516` + // Measured: `484` // Estimated: `11377` - // Minimum execution time: 26_202_000 picoseconds. - Weight::from_parts(26_499_000, 11377) + // Minimum execution time: 25_644_000 picoseconds. + Weight::from_parts(25_999_000, 11377) .saturating_add(RocksDbWeight::get().reads(3_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } @@ -1321,10 +1323,10 @@ impl WeightInfo for () { /// Proof: Nfts PendingSwapOf (max_values: None, max_size: Some(71), added: 2546, mode: MaxEncodedLen) fn buy_item() -> Weight { // Proof Size summary in bytes: - // Measured: `767` + // Measured: `671` // Estimated: `18480` - // Minimum execution time: 50_601_000 picoseconds. - Weight::from_parts(51_283_000, 18480) + // Minimum execution time: 51_542_000 picoseconds. + Weight::from_parts(52_203_000, 18480) .saturating_add(RocksDbWeight::get().reads(5_u64)) .saturating_add(RocksDbWeight::get().writes(5_u64)) } @@ -1333,10 +1335,10 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 2_733_000 picoseconds. - Weight::from_parts(5_568_590, 0) - // Standard Error: 14_136 - .saturating_add(Weight::from_parts(3_763_928, 0).saturating_mul(n.into())) + // Minimum execution time: 2_796_000 picoseconds. + Weight::from_parts(5_517_617, 0) + // Standard Error: 14_247 + .saturating_add(Weight::from_parts(3_757_796, 0).saturating_mul(n.into())) } /// Storage: Nfts Item (r:2 w:0) /// Proof: Nfts Item (max_values: None, max_size: Some(861), added: 3336, mode: MaxEncodedLen) @@ -1344,10 +1346,10 @@ impl WeightInfo for () { /// Proof: Nfts PendingSwapOf (max_values: None, max_size: Some(71), added: 2546, mode: MaxEncodedLen) fn create_swap() -> Weight { // Proof Size summary in bytes: - // Measured: `524` + // Measured: `460` // Estimated: `7662` - // Minimum execution time: 22_901_000 picoseconds. - Weight::from_parts(23_673_000, 7662) + // Minimum execution time: 23_218_000 picoseconds. + Weight::from_parts(23_590_000, 7662) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } @@ -1357,10 +1359,10 @@ impl WeightInfo for () { /// Proof: Nfts Item (max_values: None, max_size: Some(861), added: 3336, mode: MaxEncodedLen) fn cancel_swap() -> Weight { // Proof Size summary in bytes: - // Measured: `511` + // Measured: `479` // Estimated: `7862` - // Minimum execution time: 22_532_000 picoseconds. - Weight::from_parts(22_831_000, 7862) + // Minimum execution time: 22_536_000 picoseconds. + Weight::from_parts(22_874_000, 7862) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } @@ -1380,21 +1382,21 @@ impl WeightInfo for () { /// Proof: Nfts ItemPriceOf (max_values: None, max_size: Some(89), added: 2564, mode: MaxEncodedLen) fn claim_swap() -> Weight { // Proof Size summary in bytes: - // Measured: `896` + // Measured: `800` // Estimated: `24321` - // Minimum execution time: 77_668_000 picoseconds. - Weight::from_parts(78_407_000, 24321) + // Minimum execution time: 77_372_000 picoseconds. + Weight::from_parts(78_109_000, 24321) .saturating_add(RocksDbWeight::get().reads(7_u64)) .saturating_add(RocksDbWeight::get().writes(10_u64)) } - /// Storage: Nfts Collection (r:1 w:1) - /// Proof: Nfts Collection (max_values: None, max_size: Some(84), added: 2559, mode: MaxEncodedLen) /// Storage: Nfts CollectionRoleOf (r:2 w:0) /// Proof: Nfts CollectionRoleOf (max_values: None, max_size: Some(69), added: 2544, mode: MaxEncodedLen) /// Storage: Nfts CollectionConfigOf (r:1 w:0) /// Proof: Nfts CollectionConfigOf (max_values: None, max_size: Some(73), added: 2548, mode: MaxEncodedLen) /// Storage: Nfts Item (r:1 w:1) /// Proof: Nfts Item (max_values: None, max_size: Some(861), added: 3336, mode: MaxEncodedLen) + /// Storage: Nfts Collection (r:1 w:1) + /// Proof: Nfts Collection (max_values: None, max_size: Some(84), added: 2559, mode: MaxEncodedLen) /// Storage: Nfts ItemConfigOf (r:1 w:1) /// Proof: Nfts ItemConfigOf (max_values: None, max_size: Some(48), added: 2523, mode: MaxEncodedLen) /// Storage: System Account (r:1 w:1) @@ -1408,12 +1410,12 @@ impl WeightInfo for () { /// The range of component `n` is `[0, 10]`. fn mint_pre_signed(n: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `659` + // Measured: `595` // Estimated: `29192 + n * (2921 ±0)` - // Minimum execution time: 133_147_000 picoseconds. - Weight::from_parts(138_031_439, 29192) - // Standard Error: 28_665 - .saturating_add(Weight::from_parts(28_206_062, 0).saturating_mul(n.into())) + // Minimum execution time: 131_935_000 picoseconds. + Weight::from_parts(137_455_891, 29192) + // Standard Error: 31_617 + .saturating_add(Weight::from_parts(27_550_750, 0).saturating_mul(n.into())) .saturating_add(RocksDbWeight::get().reads(8_u64)) .saturating_add(RocksDbWeight::get().reads((1_u64).saturating_mul(n.into()))) .saturating_add(RocksDbWeight::get().writes(6_u64)) @@ -1435,12 +1437,12 @@ impl WeightInfo for () { /// The range of component `n` is `[0, 10]`. fn set_attributes_pre_signed(n: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `721` + // Measured: `625` // Estimated: `20142 + n * (2921 ±0)` - // Minimum execution time: 78_243_000 picoseconds. - Weight::from_parts(90_947_408, 20142) - // Standard Error: 75_516 - .saturating_add(Weight::from_parts(28_059_623, 0).saturating_mul(n.into())) + // Minimum execution time: 77_694_000 picoseconds. + Weight::from_parts(90_089_732, 20142) + // Standard Error: 71_327 + .saturating_add(Weight::from_parts(27_832_189, 0).saturating_mul(n.into())) .saturating_add(RocksDbWeight::get().reads(4_u64)) .saturating_add(RocksDbWeight::get().reads((1_u64).saturating_mul(n.into()))) .saturating_add(RocksDbWeight::get().writes(2_u64)) diff --git a/frame/nis/src/weights.rs b/frame/nis/src/weights.rs index 27c2f23e0..14b7b4d9c 100644 --- a/frame/nis/src/weights.rs +++ b/frame/nis/src/weights.rs @@ -18,9 +18,9 @@ //! Autogenerated weights for pallet_nis //! //! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev -//! DATE: 2023-01-24, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! DATE: 2023-03-16, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` //! WORST CASE MAP SIZE: `1000000` -//! HOSTNAME: `bm2`, CPU: `Intel(R) Core(TM) i7-7700K CPU @ 4.20GHz` +//! HOSTNAME: `bm3`, CPU: `Intel(R) Core(TM) i7-7700K CPU @ 4.20GHz` //! EXECUTION: Some(Wasm), WASM-EXECUTION: Compiled, CHAIN: Some("dev"), DB CACHE: 1024 // Executed Command: @@ -31,6 +31,9 @@ // --steps=50 // --repeat=20 // --pallet=pallet_nis +// --no-storage-info +// --no-median-slopes +// --no-min-squares // --extrinsic=* // --execution=wasm // --wasm-execution=compiled @@ -73,12 +76,12 @@ impl WeightInfo for SubstrateWeight { /// The range of component `l` is `[0, 999]`. fn place_bid(l: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `6245 + l * (48 ±0)` - // Estimated: `60718` - // Minimum execution time: 28_814 nanoseconds. - Weight::from_parts(35_245_917, 60718) - // Standard Error: 189 - .saturating_add(Weight::from_parts(45_322, 0).saturating_mul(l.into())) + // Measured: `6182 + l * (48 ±0)` + // Estimated: `63688` + // Minimum execution time: 31_160_000 picoseconds. + Weight::from_parts(39_023_414, 63688) + // Standard Error: 214 + .saturating_add(Weight::from_parts(46_106, 0).saturating_mul(l.into())) .saturating_add(T::DbWeight::get().reads(3_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) } @@ -90,10 +93,10 @@ impl WeightInfo for SubstrateWeight { /// Proof: Nis QueueTotals (max_values: Some(1), max_size: Some(6002), added: 6497, mode: MaxEncodedLen) fn place_bid_max() -> Weight { // Proof Size summary in bytes: - // Measured: `54247` - // Estimated: `60718` - // Minimum execution time: 80_332 nanoseconds. - Weight::from_parts(81_050_000, 60718) + // Measured: `54184` + // Estimated: `63688` + // Minimum execution time: 85_703_000 picoseconds. + Weight::from_parts(86_613_000, 63688) .saturating_add(T::DbWeight::get().reads(3_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) } @@ -106,12 +109,12 @@ impl WeightInfo for SubstrateWeight { /// The range of component `l` is `[1, 1000]`. fn retract_bid(l: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `6245 + l * (48 ±0)` - // Estimated: `60718` - // Minimum execution time: 34_426 nanoseconds. - Weight::from_parts(36_434_166, 60718) - // Standard Error: 135 - .saturating_add(Weight::from_parts(33_923, 0).saturating_mul(l.into())) + // Measured: `6182 + l * (48 ±0)` + // Estimated: `63688` + // Minimum execution time: 37_637_000 picoseconds. + Weight::from_parts(39_977_788, 63688) + // Standard Error: 152 + .saturating_add(Weight::from_parts(34_595, 0).saturating_mul(l.into())) .saturating_add(T::DbWeight::get().reads(3_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) } @@ -121,10 +124,10 @@ impl WeightInfo for SubstrateWeight { /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) fn fund_deficit() -> Weight { // Proof Size summary in bytes: - // Measured: `222` - // Estimated: `3138` - // Minimum execution time: 32_566 nanoseconds. - Weight::from_parts(32_880_000, 3138) + // Measured: `191` + // Estimated: `5118` + // Minimum execution time: 35_807_000 picoseconds. + Weight::from_parts(36_329_000, 5118) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -138,10 +141,10 @@ impl WeightInfo for SubstrateWeight { /// Proof: Balances Reserves (max_values: None, max_size: Some(1249), added: 3724, mode: MaxEncodedLen) fn thaw_private() -> Weight { // Proof Size summary in bytes: - // Measured: `423` - // Estimated: `9418` - // Minimum execution time: 46_212 nanoseconds. - Weight::from_parts(46_748_000, 9418) + // Measured: `360` + // Estimated: `13378` + // Minimum execution time: 51_342_000 picoseconds. + Weight::from_parts(51_976_000, 13378) .saturating_add(T::DbWeight::get().reads(4_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) } @@ -157,10 +160,10 @@ impl WeightInfo for SubstrateWeight { /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) fn thaw_communal() -> Weight { // Proof Size summary in bytes: - // Measured: `868` - // Estimated: `10956` - // Minimum execution time: 68_791 nanoseconds. - Weight::from_parts(69_504_000, 10956) + // Measured: `773` + // Estimated: `15906` + // Minimum execution time: 76_535_000 picoseconds. + Weight::from_parts(78_073_000, 15906) .saturating_add(T::DbWeight::get().reads(5_u64)) .saturating_add(T::DbWeight::get().writes(5_u64)) } @@ -178,10 +181,10 @@ impl WeightInfo for SubstrateWeight { /// Proof: Balances Reserves (max_values: None, max_size: Some(1249), added: 3724, mode: MaxEncodedLen) fn privatize() -> Weight { // Proof Size summary in bytes: - // Measured: `930` - // Estimated: `14680` - // Minimum execution time: 76_784 nanoseconds. - Weight::from_parts(77_575_000, 14680) + // Measured: `835` + // Estimated: `20620` + // Minimum execution time: 84_680_000 picoseconds. + Weight::from_parts(85_661_000, 20620) .saturating_add(T::DbWeight::get().reads(6_u64)) .saturating_add(T::DbWeight::get().writes(6_u64)) } @@ -199,10 +202,10 @@ impl WeightInfo for SubstrateWeight { /// Proof: Assets Account (max_values: None, max_size: Some(102), added: 2577, mode: MaxEncodedLen) fn communify() -> Weight { // Proof Size summary in bytes: - // Measured: `769` - // Estimated: `14680` - // Minimum execution time: 64_543 nanoseconds. - Weight::from_parts(65_258_000, 14680) + // Measured: `674` + // Estimated: `20620` + // Minimum execution time: 72_828_000 picoseconds. + Weight::from_parts(73_553_000, 20620) .saturating_add(T::DbWeight::get().reads(6_u64)) .saturating_add(T::DbWeight::get().writes(6_u64)) } @@ -214,10 +217,10 @@ impl WeightInfo for SubstrateWeight { /// Proof: Nis QueueTotals (max_values: Some(1), max_size: Some(6002), added: 6497, mode: MaxEncodedLen) fn process_queues() -> Weight { // Proof Size summary in bytes: - // Measured: `6655` - // Estimated: `9635` - // Minimum execution time: 21_379 nanoseconds. - Weight::from_parts(21_736_000, 9635) + // Measured: `6624` + // Estimated: `12605` + // Minimum execution time: 23_311_000 picoseconds. + Weight::from_parts(23_855_000, 12605) .saturating_add(T::DbWeight::get().reads(3_u64)) .saturating_add(T::DbWeight::get().writes(2_u64)) } @@ -226,9 +229,9 @@ impl WeightInfo for SubstrateWeight { fn process_queue() -> Weight { // Proof Size summary in bytes: // Measured: `42` - // Estimated: `50497` - // Minimum execution time: 4_302 nanoseconds. - Weight::from_parts(4_440_000, 50497) + // Estimated: `51487` + // Minimum execution time: 4_485_000 picoseconds. + Weight::from_parts(4_717_000, 51487) .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -238,8 +241,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 6_758 nanoseconds. - Weight::from_parts(6_911_000, 0) + // Minimum execution time: 7_837_000 picoseconds. + Weight::from_parts(12_913_000, 0) .saturating_add(T::DbWeight::get().writes(1_u64)) } } @@ -255,12 +258,12 @@ impl WeightInfo for () { /// The range of component `l` is `[0, 999]`. fn place_bid(l: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `6245 + l * (48 ±0)` - // Estimated: `60718` - // Minimum execution time: 28_814 nanoseconds. - Weight::from_parts(35_245_917, 60718) - // Standard Error: 189 - .saturating_add(Weight::from_parts(45_322, 0).saturating_mul(l.into())) + // Measured: `6182 + l * (48 ±0)` + // Estimated: `63688` + // Minimum execution time: 31_160_000 picoseconds. + Weight::from_parts(39_023_414, 63688) + // Standard Error: 214 + .saturating_add(Weight::from_parts(46_106, 0).saturating_mul(l.into())) .saturating_add(RocksDbWeight::get().reads(3_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) } @@ -272,10 +275,10 @@ impl WeightInfo for () { /// Proof: Nis QueueTotals (max_values: Some(1), max_size: Some(6002), added: 6497, mode: MaxEncodedLen) fn place_bid_max() -> Weight { // Proof Size summary in bytes: - // Measured: `54247` - // Estimated: `60718` - // Minimum execution time: 80_332 nanoseconds. - Weight::from_parts(81_050_000, 60718) + // Measured: `54184` + // Estimated: `63688` + // Minimum execution time: 85_703_000 picoseconds. + Weight::from_parts(86_613_000, 63688) .saturating_add(RocksDbWeight::get().reads(3_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) } @@ -288,12 +291,12 @@ impl WeightInfo for () { /// The range of component `l` is `[1, 1000]`. fn retract_bid(l: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `6245 + l * (48 ±0)` - // Estimated: `60718` - // Minimum execution time: 34_426 nanoseconds. - Weight::from_parts(36_434_166, 60718) - // Standard Error: 135 - .saturating_add(Weight::from_parts(33_923, 0).saturating_mul(l.into())) + // Measured: `6182 + l * (48 ±0)` + // Estimated: `63688` + // Minimum execution time: 37_637_000 picoseconds. + Weight::from_parts(39_977_788, 63688) + // Standard Error: 152 + .saturating_add(Weight::from_parts(34_595, 0).saturating_mul(l.into())) .saturating_add(RocksDbWeight::get().reads(3_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) } @@ -303,10 +306,10 @@ impl WeightInfo for () { /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) fn fund_deficit() -> Weight { // Proof Size summary in bytes: - // Measured: `222` - // Estimated: `3138` - // Minimum execution time: 32_566 nanoseconds. - Weight::from_parts(32_880_000, 3138) + // Measured: `191` + // Estimated: `5118` + // Minimum execution time: 35_807_000 picoseconds. + Weight::from_parts(36_329_000, 5118) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } @@ -320,10 +323,10 @@ impl WeightInfo for () { /// Proof: Balances Reserves (max_values: None, max_size: Some(1249), added: 3724, mode: MaxEncodedLen) fn thaw_private() -> Weight { // Proof Size summary in bytes: - // Measured: `423` - // Estimated: `9418` - // Minimum execution time: 46_212 nanoseconds. - Weight::from_parts(46_748_000, 9418) + // Measured: `360` + // Estimated: `13378` + // Minimum execution time: 51_342_000 picoseconds. + Weight::from_parts(51_976_000, 13378) .saturating_add(RocksDbWeight::get().reads(4_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) } @@ -339,10 +342,10 @@ impl WeightInfo for () { /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) fn thaw_communal() -> Weight { // Proof Size summary in bytes: - // Measured: `868` - // Estimated: `10956` - // Minimum execution time: 68_791 nanoseconds. - Weight::from_parts(69_504_000, 10956) + // Measured: `773` + // Estimated: `15906` + // Minimum execution time: 76_535_000 picoseconds. + Weight::from_parts(78_073_000, 15906) .saturating_add(RocksDbWeight::get().reads(5_u64)) .saturating_add(RocksDbWeight::get().writes(5_u64)) } @@ -360,10 +363,10 @@ impl WeightInfo for () { /// Proof: Balances Reserves (max_values: None, max_size: Some(1249), added: 3724, mode: MaxEncodedLen) fn privatize() -> Weight { // Proof Size summary in bytes: - // Measured: `930` - // Estimated: `14680` - // Minimum execution time: 76_784 nanoseconds. - Weight::from_parts(77_575_000, 14680) + // Measured: `835` + // Estimated: `20620` + // Minimum execution time: 84_680_000 picoseconds. + Weight::from_parts(85_661_000, 20620) .saturating_add(RocksDbWeight::get().reads(6_u64)) .saturating_add(RocksDbWeight::get().writes(6_u64)) } @@ -381,10 +384,10 @@ impl WeightInfo for () { /// Proof: Assets Account (max_values: None, max_size: Some(102), added: 2577, mode: MaxEncodedLen) fn communify() -> Weight { // Proof Size summary in bytes: - // Measured: `769` - // Estimated: `14680` - // Minimum execution time: 64_543 nanoseconds. - Weight::from_parts(65_258_000, 14680) + // Measured: `674` + // Estimated: `20620` + // Minimum execution time: 72_828_000 picoseconds. + Weight::from_parts(73_553_000, 20620) .saturating_add(RocksDbWeight::get().reads(6_u64)) .saturating_add(RocksDbWeight::get().writes(6_u64)) } @@ -396,10 +399,10 @@ impl WeightInfo for () { /// Proof: Nis QueueTotals (max_values: Some(1), max_size: Some(6002), added: 6497, mode: MaxEncodedLen) fn process_queues() -> Weight { // Proof Size summary in bytes: - // Measured: `6655` - // Estimated: `9635` - // Minimum execution time: 21_379 nanoseconds. - Weight::from_parts(21_736_000, 9635) + // Measured: `6624` + // Estimated: `12605` + // Minimum execution time: 23_311_000 picoseconds. + Weight::from_parts(23_855_000, 12605) .saturating_add(RocksDbWeight::get().reads(3_u64)) .saturating_add(RocksDbWeight::get().writes(2_u64)) } @@ -408,9 +411,9 @@ impl WeightInfo for () { fn process_queue() -> Weight { // Proof Size summary in bytes: // Measured: `42` - // Estimated: `50497` - // Minimum execution time: 4_302 nanoseconds. - Weight::from_parts(4_440_000, 50497) + // Estimated: `51487` + // Minimum execution time: 4_485_000 picoseconds. + Weight::from_parts(4_717_000, 51487) .saturating_add(RocksDbWeight::get().reads(1_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } @@ -420,8 +423,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 6_758 nanoseconds. - Weight::from_parts(6_911_000, 0) + // Minimum execution time: 7_837_000 picoseconds. + Weight::from_parts(12_913_000, 0) .saturating_add(RocksDbWeight::get().writes(1_u64)) } } diff --git a/frame/nomination-pools/src/weights.rs b/frame/nomination-pools/src/weights.rs index cf0048fa4..003ba93eb 100644 --- a/frame/nomination-pools/src/weights.rs +++ b/frame/nomination-pools/src/weights.rs @@ -18,26 +18,28 @@ //! Autogenerated weights for pallet_nomination_pools //! //! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev -//! DATE: 2023-03-08, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! DATE: 2023-03-16, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` //! WORST CASE MAP SIZE: `1000000` //! HOSTNAME: `bm3`, CPU: `Intel(R) Core(TM) i7-7700K CPU @ 4.20GHz` //! EXECUTION: Some(Wasm), WASM-EXECUTION: Compiled, CHAIN: Some("dev"), DB CACHE: 1024 // Executed Command: -// target/production/substrate +// ./target/production/substrate // benchmark // pallet +// --chain=dev // --steps=50 // --repeat=20 +// --pallet=pallet_nomination_pools +// --no-storage-info +// --no-median-slopes +// --no-min-squares // --extrinsic=* // --execution=wasm // --wasm-execution=compiled // --heap-pages=4096 -// --json-file=/var/lib/gitlab-runner/builds/zyw4fam_/0/parity/mirrors/substrate/.git/.artifacts/bench.json -// --pallet=pallet_nomination_pools -// --chain=dev -// --header=./HEADER-APACHE2 // --output=./frame/nomination-pools/src/weights.rs +// --header=./HEADER-APACHE2 // --template=./.maintain/frame-weight-template.hbs #![cfg_attr(rustfmt, rustfmt_skip)] @@ -104,10 +106,10 @@ impl WeightInfo for SubstrateWeight { /// Proof: VoterList ListBags (max_values: None, max_size: Some(82), added: 2557, mode: MaxEncodedLen) fn join() -> Weight { // Proof Size summary in bytes: - // Measured: `3650` + // Measured: `3300` // Estimated: `52435` - // Minimum execution time: 160_401_000 picoseconds. - Weight::from_parts(161_798_000, 52435) + // Minimum execution time: 160_675_000 picoseconds. + Weight::from_parts(162_605_000, 52435) .saturating_add(T::DbWeight::get().reads(18_u64)) .saturating_add(T::DbWeight::get().writes(12_u64)) } @@ -133,10 +135,10 @@ impl WeightInfo for SubstrateWeight { /// Proof: VoterList ListBags (max_values: None, max_size: Some(82), added: 2557, mode: MaxEncodedLen) fn bond_extra_transfer() -> Weight { // Proof Size summary in bytes: - // Measured: `3692` + // Measured: `3310` // Estimated: `49070` - // Minimum execution time: 157_668_000 picoseconds. - Weight::from_parts(161_129_000, 49070) + // Minimum execution time: 156_820_000 picoseconds. + Weight::from_parts(159_798_000, 49070) .saturating_add(T::DbWeight::get().reads(15_u64)) .saturating_add(T::DbWeight::get().writes(12_u64)) } @@ -164,10 +166,10 @@ impl WeightInfo for SubstrateWeight { /// Proof: VoterList ListBags (max_values: None, max_size: Some(82), added: 2557, mode: MaxEncodedLen) fn bond_extra_other() -> Weight { // Proof Size summary in bytes: - // Measured: `3757` + // Measured: `3375` // Estimated: `52576` - // Minimum execution time: 176_034_000 picoseconds. - Weight::from_parts(176_956_000, 52576) + // Minimum execution time: 175_820_000 picoseconds. + Weight::from_parts(177_378_000, 52576) .saturating_add(T::DbWeight::get().reads(16_u64)) .saturating_add(T::DbWeight::get().writes(13_u64)) } @@ -185,10 +187,10 @@ impl WeightInfo for SubstrateWeight { /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) fn claim_payout() -> Weight { // Proof Size summary in bytes: - // Measured: `1331` + // Measured: `1171` // Estimated: `19532` - // Minimum execution time: 61_551_000 picoseconds. - Weight::from_parts(62_201_000, 19532) + // Minimum execution time: 62_011_000 picoseconds. + Weight::from_parts(62_680_000, 19532) .saturating_add(T::DbWeight::get().reads(6_u64)) .saturating_add(T::DbWeight::get().writes(4_u64)) } @@ -224,10 +226,10 @@ impl WeightInfo for SubstrateWeight { /// Proof: NominationPools CounterForSubPoolsStorage (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) fn unbond() -> Weight { // Proof Size summary in bytes: - // Measured: `3935` + // Measured: `3586` // Estimated: `82816` - // Minimum execution time: 162_755_000 picoseconds. - Weight::from_parts(163_518_000, 82816) + // Minimum execution time: 163_294_000 picoseconds. + Weight::from_parts(164_375_000, 82816) .saturating_add(T::DbWeight::get().reads(19_u64)) .saturating_add(T::DbWeight::get().writes(13_u64)) } @@ -244,12 +246,12 @@ impl WeightInfo for SubstrateWeight { /// The range of component `s` is `[0, 100]`. fn pool_withdraw_unbonded(s: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `1783` + // Measured: `1687` // Estimated: `18031` - // Minimum execution time: 54_752_000 picoseconds. - Weight::from_parts(56_248_171, 18031) - // Standard Error: 1_891 - .saturating_add(Weight::from_parts(4_767, 0).saturating_mul(s.into())) + // Minimum execution time: 54_775_000 picoseconds. + Weight::from_parts(55_724_944, 18031) + // Standard Error: 536 + .saturating_add(Weight::from_parts(10_059, 0).saturating_mul(s.into())) .saturating_add(T::DbWeight::get().reads(5_u64)) .saturating_add(T::DbWeight::get().writes(2_u64)) } @@ -276,12 +278,12 @@ impl WeightInfo for SubstrateWeight { /// The range of component `s` is `[0, 100]`. fn withdraw_unbonded_update(s: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `2307` + // Measured: `2115` // Estimated: `54662` - // Minimum execution time: 106_166_000 picoseconds. - Weight::from_parts(107_806_373, 54662) - // Standard Error: 6_985 - .saturating_add(Weight::from_parts(18_449, 0).saturating_mul(s.into())) + // Minimum execution time: 106_820_000 picoseconds. + Weight::from_parts(109_870_849, 54662) + // Standard Error: 11_111 + .saturating_add(Weight::from_parts(2_006, 0).saturating_mul(s.into())) .saturating_add(T::DbWeight::get().reads(9_u64)) .saturating_add(T::DbWeight::get().writes(8_u64)) } @@ -330,12 +332,12 @@ impl WeightInfo for SubstrateWeight { /// The range of component `s` is `[0, 100]`. fn withdraw_unbonded_kill(s: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `2694` - // Estimated: `87714` - // Minimum execution time: 170_047_000 picoseconds. - Weight::from_parts(172_125_770, 87714) - // Standard Error: 2_599 - .saturating_add(Weight::from_parts(9_964, 0).saturating_mul(s.into())) + // Measured: `2470` + // Estimated: `87490` + // Minimum execution time: 170_908_000 picoseconds. + Weight::from_parts(173_616_555, 87490) + // Standard Error: 2_324 + .saturating_add(Weight::from_parts(4_397, 0).saturating_mul(s.into())) .saturating_add(T::DbWeight::get().reads(20_u64)) .saturating_add(T::DbWeight::get().writes(18_u64)) } @@ -383,10 +385,10 @@ impl WeightInfo for SubstrateWeight { /// Proof: Staking Payee (max_values: None, max_size: Some(73), added: 2548, mode: MaxEncodedLen) fn create() -> Weight { // Proof Size summary in bytes: - // Measured: `1321` + // Measured: `1289` // Estimated: `51410` - // Minimum execution time: 149_672_000 picoseconds. - Weight::from_parts(153_613_000, 51410) + // Minimum execution time: 148_934_000 picoseconds. + Weight::from_parts(149_702_000, 51410) .saturating_add(T::DbWeight::get().reads(21_u64)) .saturating_add(T::DbWeight::get().writes(15_u64)) } @@ -417,12 +419,12 @@ impl WeightInfo for SubstrateWeight { /// The range of component `n` is `[1, 16]`. fn nominate(n: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `1913` + // Measured: `1849` // Estimated: `33934 + n * (2520 ±0)` - // Minimum execution time: 68_892_000 picoseconds. - Weight::from_parts(69_062_946, 33934) - // Standard Error: 6_448 - .saturating_add(Weight::from_parts(1_422_774, 0).saturating_mul(n.into())) + // Minimum execution time: 68_986_000 picoseconds. + Weight::from_parts(69_474_269, 33934) + // Standard Error: 6_602 + .saturating_add(Weight::from_parts(1_416_294, 0).saturating_mul(n.into())) .saturating_add(T::DbWeight::get().reads(12_u64)) .saturating_add(T::DbWeight::get().reads((1_u64).saturating_mul(n.into()))) .saturating_add(T::DbWeight::get().writes(5_u64)) @@ -436,10 +438,10 @@ impl WeightInfo for SubstrateWeight { /// Proof: Staking Ledger (max_values: None, max_size: Some(1091), added: 3566, mode: MaxEncodedLen) fn set_state() -> Weight { // Proof Size summary in bytes: - // Measured: `1502` + // Measured: `1438` // Estimated: `11778` - // Minimum execution time: 36_447_000 picoseconds. - Weight::from_parts(36_837_000, 11778) + // Minimum execution time: 36_300_000 picoseconds. + Weight::from_parts(36_713_000, 11778) .saturating_add(T::DbWeight::get().reads(3_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -452,12 +454,12 @@ impl WeightInfo for SubstrateWeight { /// The range of component `n` is `[1, 256]`. fn set_metadata(n: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `563` + // Measured: `531` // Estimated: `8909` - // Minimum execution time: 15_221_000 picoseconds. - Weight::from_parts(15_632_286, 8909) - // Standard Error: 343 - .saturating_add(Weight::from_parts(2_299, 0).saturating_mul(n.into())) + // Minimum execution time: 14_976_000 picoseconds. + Weight::from_parts(15_538_088, 8909) + // Standard Error: 71 + .saturating_add(Weight::from_parts(1_548, 0).saturating_mul(n.into())) .saturating_add(T::DbWeight::get().reads(3_u64)) .saturating_add(T::DbWeight::get().writes(2_u64)) } @@ -477,18 +479,18 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 7_409_000 picoseconds. - Weight::from_parts(7_702_000, 0) + // Minimum execution time: 7_259_000 picoseconds. + Weight::from_parts(7_499_000, 0) .saturating_add(T::DbWeight::get().writes(6_u64)) } /// Storage: NominationPools BondedPools (r:1 w:1) /// Proof: NominationPools BondedPools (max_values: None, max_size: Some(220), added: 2695, mode: MaxEncodedLen) fn update_roles() -> Weight { // Proof Size summary in bytes: - // Measured: `563` + // Measured: `531` // Estimated: `3685` - // Minimum execution time: 20_451_000 picoseconds. - Weight::from_parts(20_703_000, 3685) + // Minimum execution time: 20_454_000 picoseconds. + Weight::from_parts(20_771_000, 3685) .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -512,10 +514,10 @@ impl WeightInfo for SubstrateWeight { /// Proof: VoterList CounterForListNodes (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) fn chill() -> Weight { // Proof Size summary in bytes: - // Measured: `2140` + // Measured: `2012` // Estimated: `29455` - // Minimum execution time: 66_001_000 picoseconds. - Weight::from_parts(66_894_000, 29455) + // Minimum execution time: 66_404_000 picoseconds. + Weight::from_parts(66_872_000, 29455) .saturating_add(T::DbWeight::get().reads(9_u64)) .saturating_add(T::DbWeight::get().writes(5_u64)) } @@ -529,10 +531,10 @@ impl WeightInfo for SubstrateWeight { /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) fn set_commission() -> Weight { // Proof Size summary in bytes: - // Measured: `866` + // Measured: `770` // Estimated: `12324` - // Minimum execution time: 34_011_000 picoseconds. - Weight::from_parts(34_521_000, 12324) + // Minimum execution time: 34_240_000 picoseconds. + Weight::from_parts(34_797_000, 12324) .saturating_add(T::DbWeight::get().reads(4_u64)) .saturating_add(T::DbWeight::get().writes(2_u64)) } @@ -540,10 +542,10 @@ impl WeightInfo for SubstrateWeight { /// Proof: NominationPools BondedPools (max_values: None, max_size: Some(220), added: 2695, mode: MaxEncodedLen) fn set_commission_max() -> Weight { // Proof Size summary in bytes: - // Measured: `603` + // Measured: `571` // Estimated: `3685` - // Minimum execution time: 19_524_000 picoseconds. - Weight::from_parts(19_855_000, 3685) + // Minimum execution time: 19_469_000 picoseconds. + Weight::from_parts(19_865_000, 3685) .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -551,10 +553,10 @@ impl WeightInfo for SubstrateWeight { /// Proof: NominationPools BondedPools (max_values: None, max_size: Some(220), added: 2695, mode: MaxEncodedLen) fn set_commission_change_rate() -> Weight { // Proof Size summary in bytes: - // Measured: `563` + // Measured: `531` // Estimated: `3685` - // Minimum execution time: 20_457_000 picoseconds. - Weight::from_parts(20_698_000, 3685) + // Minimum execution time: 20_423_000 picoseconds. + Weight::from_parts(20_620_000, 3685) .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -566,8 +568,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `542` // Estimated: `7208` - // Minimum execution time: 15_183_000 picoseconds. - Weight::from_parts(15_597_000, 7208) + // Minimum execution time: 15_346_000 picoseconds. + Weight::from_parts(15_613_000, 7208) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -581,10 +583,10 @@ impl WeightInfo for SubstrateWeight { /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) fn claim_commission() -> Weight { // Proof Size summary in bytes: - // Measured: `1096` + // Measured: `968` // Estimated: `12324` - // Minimum execution time: 48_957_000 picoseconds. - Weight::from_parts(50_207_000, 12324) + // Minimum execution time: 48_749_000 picoseconds. + Weight::from_parts(49_131_000, 12324) .saturating_add(T::DbWeight::get().reads(4_u64)) .saturating_add(T::DbWeight::get().writes(2_u64)) } @@ -622,10 +624,10 @@ impl WeightInfo for () { /// Proof: VoterList ListBags (max_values: None, max_size: Some(82), added: 2557, mode: MaxEncodedLen) fn join() -> Weight { // Proof Size summary in bytes: - // Measured: `3650` + // Measured: `3300` // Estimated: `52435` - // Minimum execution time: 160_401_000 picoseconds. - Weight::from_parts(161_798_000, 52435) + // Minimum execution time: 160_675_000 picoseconds. + Weight::from_parts(162_605_000, 52435) .saturating_add(RocksDbWeight::get().reads(18_u64)) .saturating_add(RocksDbWeight::get().writes(12_u64)) } @@ -651,10 +653,10 @@ impl WeightInfo for () { /// Proof: VoterList ListBags (max_values: None, max_size: Some(82), added: 2557, mode: MaxEncodedLen) fn bond_extra_transfer() -> Weight { // Proof Size summary in bytes: - // Measured: `3692` + // Measured: `3310` // Estimated: `49070` - // Minimum execution time: 157_668_000 picoseconds. - Weight::from_parts(161_129_000, 49070) + // Minimum execution time: 156_820_000 picoseconds. + Weight::from_parts(159_798_000, 49070) .saturating_add(RocksDbWeight::get().reads(15_u64)) .saturating_add(RocksDbWeight::get().writes(12_u64)) } @@ -682,10 +684,10 @@ impl WeightInfo for () { /// Proof: VoterList ListBags (max_values: None, max_size: Some(82), added: 2557, mode: MaxEncodedLen) fn bond_extra_other() -> Weight { // Proof Size summary in bytes: - // Measured: `3757` + // Measured: `3375` // Estimated: `52576` - // Minimum execution time: 176_034_000 picoseconds. - Weight::from_parts(176_956_000, 52576) + // Minimum execution time: 175_820_000 picoseconds. + Weight::from_parts(177_378_000, 52576) .saturating_add(RocksDbWeight::get().reads(16_u64)) .saturating_add(RocksDbWeight::get().writes(13_u64)) } @@ -703,10 +705,10 @@ impl WeightInfo for () { /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) fn claim_payout() -> Weight { // Proof Size summary in bytes: - // Measured: `1331` + // Measured: `1171` // Estimated: `19532` - // Minimum execution time: 61_551_000 picoseconds. - Weight::from_parts(62_201_000, 19532) + // Minimum execution time: 62_011_000 picoseconds. + Weight::from_parts(62_680_000, 19532) .saturating_add(RocksDbWeight::get().reads(6_u64)) .saturating_add(RocksDbWeight::get().writes(4_u64)) } @@ -742,10 +744,10 @@ impl WeightInfo for () { /// Proof: NominationPools CounterForSubPoolsStorage (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) fn unbond() -> Weight { // Proof Size summary in bytes: - // Measured: `3935` + // Measured: `3586` // Estimated: `82816` - // Minimum execution time: 162_755_000 picoseconds. - Weight::from_parts(163_518_000, 82816) + // Minimum execution time: 163_294_000 picoseconds. + Weight::from_parts(164_375_000, 82816) .saturating_add(RocksDbWeight::get().reads(19_u64)) .saturating_add(RocksDbWeight::get().writes(13_u64)) } @@ -762,12 +764,12 @@ impl WeightInfo for () { /// The range of component `s` is `[0, 100]`. fn pool_withdraw_unbonded(s: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `1783` + // Measured: `1687` // Estimated: `18031` - // Minimum execution time: 54_752_000 picoseconds. - Weight::from_parts(56_248_171, 18031) - // Standard Error: 1_891 - .saturating_add(Weight::from_parts(4_767, 0).saturating_mul(s.into())) + // Minimum execution time: 54_775_000 picoseconds. + Weight::from_parts(55_724_944, 18031) + // Standard Error: 536 + .saturating_add(Weight::from_parts(10_059, 0).saturating_mul(s.into())) .saturating_add(RocksDbWeight::get().reads(5_u64)) .saturating_add(RocksDbWeight::get().writes(2_u64)) } @@ -794,12 +796,12 @@ impl WeightInfo for () { /// The range of component `s` is `[0, 100]`. fn withdraw_unbonded_update(s: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `2307` + // Measured: `2115` // Estimated: `54662` - // Minimum execution time: 106_166_000 picoseconds. - Weight::from_parts(107_806_373, 54662) - // Standard Error: 6_985 - .saturating_add(Weight::from_parts(18_449, 0).saturating_mul(s.into())) + // Minimum execution time: 106_820_000 picoseconds. + Weight::from_parts(109_870_849, 54662) + // Standard Error: 11_111 + .saturating_add(Weight::from_parts(2_006, 0).saturating_mul(s.into())) .saturating_add(RocksDbWeight::get().reads(9_u64)) .saturating_add(RocksDbWeight::get().writes(8_u64)) } @@ -848,12 +850,12 @@ impl WeightInfo for () { /// The range of component `s` is `[0, 100]`. fn withdraw_unbonded_kill(s: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `2694` - // Estimated: `87714` - // Minimum execution time: 170_047_000 picoseconds. - Weight::from_parts(172_125_770, 87714) - // Standard Error: 2_599 - .saturating_add(Weight::from_parts(9_964, 0).saturating_mul(s.into())) + // Measured: `2470` + // Estimated: `87490` + // Minimum execution time: 170_908_000 picoseconds. + Weight::from_parts(173_616_555, 87490) + // Standard Error: 2_324 + .saturating_add(Weight::from_parts(4_397, 0).saturating_mul(s.into())) .saturating_add(RocksDbWeight::get().reads(20_u64)) .saturating_add(RocksDbWeight::get().writes(18_u64)) } @@ -901,10 +903,10 @@ impl WeightInfo for () { /// Proof: Staking Payee (max_values: None, max_size: Some(73), added: 2548, mode: MaxEncodedLen) fn create() -> Weight { // Proof Size summary in bytes: - // Measured: `1321` + // Measured: `1289` // Estimated: `51410` - // Minimum execution time: 149_672_000 picoseconds. - Weight::from_parts(153_613_000, 51410) + // Minimum execution time: 148_934_000 picoseconds. + Weight::from_parts(149_702_000, 51410) .saturating_add(RocksDbWeight::get().reads(21_u64)) .saturating_add(RocksDbWeight::get().writes(15_u64)) } @@ -935,12 +937,12 @@ impl WeightInfo for () { /// The range of component `n` is `[1, 16]`. fn nominate(n: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `1913` + // Measured: `1849` // Estimated: `33934 + n * (2520 ±0)` - // Minimum execution time: 68_892_000 picoseconds. - Weight::from_parts(69_062_946, 33934) - // Standard Error: 6_448 - .saturating_add(Weight::from_parts(1_422_774, 0).saturating_mul(n.into())) + // Minimum execution time: 68_986_000 picoseconds. + Weight::from_parts(69_474_269, 33934) + // Standard Error: 6_602 + .saturating_add(Weight::from_parts(1_416_294, 0).saturating_mul(n.into())) .saturating_add(RocksDbWeight::get().reads(12_u64)) .saturating_add(RocksDbWeight::get().reads((1_u64).saturating_mul(n.into()))) .saturating_add(RocksDbWeight::get().writes(5_u64)) @@ -954,10 +956,10 @@ impl WeightInfo for () { /// Proof: Staking Ledger (max_values: None, max_size: Some(1091), added: 3566, mode: MaxEncodedLen) fn set_state() -> Weight { // Proof Size summary in bytes: - // Measured: `1502` + // Measured: `1438` // Estimated: `11778` - // Minimum execution time: 36_447_000 picoseconds. - Weight::from_parts(36_837_000, 11778) + // Minimum execution time: 36_300_000 picoseconds. + Weight::from_parts(36_713_000, 11778) .saturating_add(RocksDbWeight::get().reads(3_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } @@ -970,12 +972,12 @@ impl WeightInfo for () { /// The range of component `n` is `[1, 256]`. fn set_metadata(n: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `563` + // Measured: `531` // Estimated: `8909` - // Minimum execution time: 15_221_000 picoseconds. - Weight::from_parts(15_632_286, 8909) - // Standard Error: 343 - .saturating_add(Weight::from_parts(2_299, 0).saturating_mul(n.into())) + // Minimum execution time: 14_976_000 picoseconds. + Weight::from_parts(15_538_088, 8909) + // Standard Error: 71 + .saturating_add(Weight::from_parts(1_548, 0).saturating_mul(n.into())) .saturating_add(RocksDbWeight::get().reads(3_u64)) .saturating_add(RocksDbWeight::get().writes(2_u64)) } @@ -995,18 +997,18 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 7_409_000 picoseconds. - Weight::from_parts(7_702_000, 0) + // Minimum execution time: 7_259_000 picoseconds. + Weight::from_parts(7_499_000, 0) .saturating_add(RocksDbWeight::get().writes(6_u64)) } /// Storage: NominationPools BondedPools (r:1 w:1) /// Proof: NominationPools BondedPools (max_values: None, max_size: Some(220), added: 2695, mode: MaxEncodedLen) fn update_roles() -> Weight { // Proof Size summary in bytes: - // Measured: `563` + // Measured: `531` // Estimated: `3685` - // Minimum execution time: 20_451_000 picoseconds. - Weight::from_parts(20_703_000, 3685) + // Minimum execution time: 20_454_000 picoseconds. + Weight::from_parts(20_771_000, 3685) .saturating_add(RocksDbWeight::get().reads(1_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } @@ -1030,10 +1032,10 @@ impl WeightInfo for () { /// Proof: VoterList CounterForListNodes (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) fn chill() -> Weight { // Proof Size summary in bytes: - // Measured: `2140` + // Measured: `2012` // Estimated: `29455` - // Minimum execution time: 66_001_000 picoseconds. - Weight::from_parts(66_894_000, 29455) + // Minimum execution time: 66_404_000 picoseconds. + Weight::from_parts(66_872_000, 29455) .saturating_add(RocksDbWeight::get().reads(9_u64)) .saturating_add(RocksDbWeight::get().writes(5_u64)) } @@ -1047,10 +1049,10 @@ impl WeightInfo for () { /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) fn set_commission() -> Weight { // Proof Size summary in bytes: - // Measured: `866` + // Measured: `770` // Estimated: `12324` - // Minimum execution time: 34_011_000 picoseconds. - Weight::from_parts(34_521_000, 12324) + // Minimum execution time: 34_240_000 picoseconds. + Weight::from_parts(34_797_000, 12324) .saturating_add(RocksDbWeight::get().reads(4_u64)) .saturating_add(RocksDbWeight::get().writes(2_u64)) } @@ -1058,10 +1060,10 @@ impl WeightInfo for () { /// Proof: NominationPools BondedPools (max_values: None, max_size: Some(220), added: 2695, mode: MaxEncodedLen) fn set_commission_max() -> Weight { // Proof Size summary in bytes: - // Measured: `603` + // Measured: `571` // Estimated: `3685` - // Minimum execution time: 19_524_000 picoseconds. - Weight::from_parts(19_855_000, 3685) + // Minimum execution time: 19_469_000 picoseconds. + Weight::from_parts(19_865_000, 3685) .saturating_add(RocksDbWeight::get().reads(1_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } @@ -1069,10 +1071,10 @@ impl WeightInfo for () { /// Proof: NominationPools BondedPools (max_values: None, max_size: Some(220), added: 2695, mode: MaxEncodedLen) fn set_commission_change_rate() -> Weight { // Proof Size summary in bytes: - // Measured: `563` + // Measured: `531` // Estimated: `3685` - // Minimum execution time: 20_457_000 picoseconds. - Weight::from_parts(20_698_000, 3685) + // Minimum execution time: 20_423_000 picoseconds. + Weight::from_parts(20_620_000, 3685) .saturating_add(RocksDbWeight::get().reads(1_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } @@ -1084,8 +1086,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `542` // Estimated: `7208` - // Minimum execution time: 15_183_000 picoseconds. - Weight::from_parts(15_597_000, 7208) + // Minimum execution time: 15_346_000 picoseconds. + Weight::from_parts(15_613_000, 7208) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } @@ -1099,10 +1101,10 @@ impl WeightInfo for () { /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) fn claim_commission() -> Weight { // Proof Size summary in bytes: - // Measured: `1096` + // Measured: `968` // Estimated: `12324` - // Minimum execution time: 48_957_000 picoseconds. - Weight::from_parts(50_207_000, 12324) + // Minimum execution time: 48_749_000 picoseconds. + Weight::from_parts(49_131_000, 12324) .saturating_add(RocksDbWeight::get().reads(4_u64)) .saturating_add(RocksDbWeight::get().writes(2_u64)) } diff --git a/frame/preimage/src/weights.rs b/frame/preimage/src/weights.rs index fa6bdc972..168cb634c 100644 --- a/frame/preimage/src/weights.rs +++ b/frame/preimage/src/weights.rs @@ -18,9 +18,9 @@ //! Autogenerated weights for pallet_preimage //! //! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev -//! DATE: 2023-01-24, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! DATE: 2023-03-16, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` //! WORST CASE MAP SIZE: `1000000` -//! HOSTNAME: `bm2`, CPU: `Intel(R) Core(TM) i7-7700K CPU @ 4.20GHz` +//! HOSTNAME: `bm3`, CPU: `Intel(R) Core(TM) i7-7700K CPU @ 4.20GHz` //! EXECUTION: Some(Wasm), WASM-EXECUTION: Compiled, CHAIN: Some("dev"), DB CACHE: 1024 // Executed Command: @@ -31,6 +31,9 @@ // --steps=50 // --repeat=20 // --pallet=pallet_preimage +// --no-storage-info +// --no-median-slopes +// --no-min-squares // --extrinsic=* // --execution=wasm // --wasm-execution=compiled @@ -72,12 +75,12 @@ impl WeightInfo for SubstrateWeight { /// The range of component `s` is `[0, 4194304]`. fn note_preimage(s: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `175` - // Estimated: `2566` - // Minimum execution time: 23_484 nanoseconds. - Weight::from_parts(23_828_000, 2566) + // Measured: `143` + // Estimated: `3556` + // Minimum execution time: 27_010_000 picoseconds. + Weight::from_parts(27_665_000, 3556) // Standard Error: 0 - .saturating_add(Weight::from_parts(1_705, 0).saturating_mul(s.into())) + .saturating_add(Weight::from_parts(1_414, 0).saturating_mul(s.into())) .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(T::DbWeight::get().writes(2_u64)) } @@ -89,11 +92,11 @@ impl WeightInfo for SubstrateWeight { fn note_requested_preimage(s: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `106` - // Estimated: `2566` - // Minimum execution time: 14_812 nanoseconds. - Weight::from_parts(14_949_000, 2566) + // Estimated: `3556` + // Minimum execution time: 16_984_000 picoseconds. + Weight::from_parts(17_455_000, 3556) // Standard Error: 0 - .saturating_add(Weight::from_parts(1_707, 0).saturating_mul(s.into())) + .saturating_add(Weight::from_parts(1_414, 0).saturating_mul(s.into())) .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(T::DbWeight::get().writes(2_u64)) } @@ -105,11 +108,11 @@ impl WeightInfo for SubstrateWeight { fn note_no_deposit_preimage(s: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `106` - // Estimated: `2566` - // Minimum execution time: 14_185 nanoseconds. - Weight::from_parts(14_398_000, 2566) + // Estimated: `3556` + // Minimum execution time: 16_579_000 picoseconds. + Weight::from_parts(16_849_000, 3556) // Standard Error: 0 - .saturating_add(Weight::from_parts(1_709, 0).saturating_mul(s.into())) + .saturating_add(Weight::from_parts(1_414, 0).saturating_mul(s.into())) .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(T::DbWeight::get().writes(2_u64)) } @@ -119,10 +122,10 @@ impl WeightInfo for SubstrateWeight { /// Proof: Preimage PreimageFor (max_values: None, max_size: Some(4194344), added: 4196819, mode: MaxEncodedLen) fn unnote_preimage() -> Weight { // Proof Size summary in bytes: - // Measured: `353` - // Estimated: `2566` - // Minimum execution time: 29_917 nanoseconds. - Weight::from_parts(30_691_000, 2566) + // Measured: `289` + // Estimated: `3556` + // Minimum execution time: 33_552_000 picoseconds. + Weight::from_parts(35_134_000, 3556) .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(T::DbWeight::get().writes(2_u64)) } @@ -133,9 +136,9 @@ impl WeightInfo for SubstrateWeight { fn unnote_no_deposit_preimage() -> Weight { // Proof Size summary in bytes: // Measured: `144` - // Estimated: `2566` - // Minimum execution time: 19_281 nanoseconds. - Weight::from_parts(20_121_000, 2566) + // Estimated: `3556` + // Minimum execution time: 22_102_000 picoseconds. + Weight::from_parts(22_860_000, 3556) .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(T::DbWeight::get().writes(2_u64)) } @@ -143,10 +146,10 @@ impl WeightInfo for SubstrateWeight { /// Proof: Preimage StatusFor (max_values: None, max_size: Some(91), added: 2566, mode: MaxEncodedLen) fn request_preimage() -> Weight { // Proof Size summary in bytes: - // Measured: `220` - // Estimated: `2566` - // Minimum execution time: 17_192 nanoseconds. - Weight::from_parts(18_637_000, 2566) + // Measured: `188` + // Estimated: `3556` + // Minimum execution time: 19_590_000 picoseconds. + Weight::from_parts(20_638_000, 3556) .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -155,9 +158,9 @@ impl WeightInfo for SubstrateWeight { fn request_no_deposit_preimage() -> Weight { // Proof Size summary in bytes: // Measured: `144` - // Estimated: `2566` - // Minimum execution time: 10_139 nanoseconds. - Weight::from_parts(10_773_000, 2566) + // Estimated: `3556` + // Minimum execution time: 11_689_000 picoseconds. + Weight::from_parts(12_577_000, 3556) .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -166,9 +169,9 @@ impl WeightInfo for SubstrateWeight { fn request_unnoted_preimage() -> Weight { // Proof Size summary in bytes: // Measured: `42` - // Estimated: `2566` - // Minimum execution time: 11_721 nanoseconds. - Weight::from_parts(12_313_000, 2566) + // Estimated: `3556` + // Minimum execution time: 13_673_000 picoseconds. + Weight::from_parts(14_210_000, 3556) .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -177,9 +180,9 @@ impl WeightInfo for SubstrateWeight { fn request_requested_preimage() -> Weight { // Proof Size summary in bytes: // Measured: `106` - // Estimated: `2566` - // Minimum execution time: 7_263 nanoseconds. - Weight::from_parts(7_547_000, 2566) + // Estimated: `3556` + // Minimum execution time: 8_810_000 picoseconds. + Weight::from_parts(9_044_000, 3556) .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -190,9 +193,9 @@ impl WeightInfo for SubstrateWeight { fn unrequest_preimage() -> Weight { // Proof Size summary in bytes: // Measured: `144` - // Estimated: `2566` - // Minimum execution time: 17_785 nanoseconds. - Weight::from_parts(18_501_000, 2566) + // Estimated: `3556` + // Minimum execution time: 21_236_000 picoseconds. + Weight::from_parts(21_684_000, 3556) .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(T::DbWeight::get().writes(2_u64)) } @@ -201,9 +204,9 @@ impl WeightInfo for SubstrateWeight { fn unrequest_unnoted_preimage() -> Weight { // Proof Size summary in bytes: // Measured: `106` - // Estimated: `2566` - // Minimum execution time: 7_090 nanoseconds. - Weight::from_parts(7_319_000, 2566) + // Estimated: `3556` + // Minimum execution time: 8_709_000 picoseconds. + Weight::from_parts(8_970_000, 3556) .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -212,9 +215,9 @@ impl WeightInfo for SubstrateWeight { fn unrequest_multi_referenced_preimage() -> Weight { // Proof Size summary in bytes: // Measured: `106` - // Estimated: `2566` - // Minimum execution time: 7_253 nanoseconds. - Weight::from_parts(7_508_000, 2566) + // Estimated: `3556` + // Minimum execution time: 8_976_000 picoseconds. + Weight::from_parts(9_164_000, 3556) .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -229,12 +232,12 @@ impl WeightInfo for () { /// The range of component `s` is `[0, 4194304]`. fn note_preimage(s: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `175` - // Estimated: `2566` - // Minimum execution time: 23_484 nanoseconds. - Weight::from_parts(23_828_000, 2566) + // Measured: `143` + // Estimated: `3556` + // Minimum execution time: 27_010_000 picoseconds. + Weight::from_parts(27_665_000, 3556) // Standard Error: 0 - .saturating_add(Weight::from_parts(1_705, 0).saturating_mul(s.into())) + .saturating_add(Weight::from_parts(1_414, 0).saturating_mul(s.into())) .saturating_add(RocksDbWeight::get().reads(1_u64)) .saturating_add(RocksDbWeight::get().writes(2_u64)) } @@ -246,11 +249,11 @@ impl WeightInfo for () { fn note_requested_preimage(s: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `106` - // Estimated: `2566` - // Minimum execution time: 14_812 nanoseconds. - Weight::from_parts(14_949_000, 2566) + // Estimated: `3556` + // Minimum execution time: 16_984_000 picoseconds. + Weight::from_parts(17_455_000, 3556) // Standard Error: 0 - .saturating_add(Weight::from_parts(1_707, 0).saturating_mul(s.into())) + .saturating_add(Weight::from_parts(1_414, 0).saturating_mul(s.into())) .saturating_add(RocksDbWeight::get().reads(1_u64)) .saturating_add(RocksDbWeight::get().writes(2_u64)) } @@ -262,11 +265,11 @@ impl WeightInfo for () { fn note_no_deposit_preimage(s: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `106` - // Estimated: `2566` - // Minimum execution time: 14_185 nanoseconds. - Weight::from_parts(14_398_000, 2566) + // Estimated: `3556` + // Minimum execution time: 16_579_000 picoseconds. + Weight::from_parts(16_849_000, 3556) // Standard Error: 0 - .saturating_add(Weight::from_parts(1_709, 0).saturating_mul(s.into())) + .saturating_add(Weight::from_parts(1_414, 0).saturating_mul(s.into())) .saturating_add(RocksDbWeight::get().reads(1_u64)) .saturating_add(RocksDbWeight::get().writes(2_u64)) } @@ -276,10 +279,10 @@ impl WeightInfo for () { /// Proof: Preimage PreimageFor (max_values: None, max_size: Some(4194344), added: 4196819, mode: MaxEncodedLen) fn unnote_preimage() -> Weight { // Proof Size summary in bytes: - // Measured: `353` - // Estimated: `2566` - // Minimum execution time: 29_917 nanoseconds. - Weight::from_parts(30_691_000, 2566) + // Measured: `289` + // Estimated: `3556` + // Minimum execution time: 33_552_000 picoseconds. + Weight::from_parts(35_134_000, 3556) .saturating_add(RocksDbWeight::get().reads(1_u64)) .saturating_add(RocksDbWeight::get().writes(2_u64)) } @@ -290,9 +293,9 @@ impl WeightInfo for () { fn unnote_no_deposit_preimage() -> Weight { // Proof Size summary in bytes: // Measured: `144` - // Estimated: `2566` - // Minimum execution time: 19_281 nanoseconds. - Weight::from_parts(20_121_000, 2566) + // Estimated: `3556` + // Minimum execution time: 22_102_000 picoseconds. + Weight::from_parts(22_860_000, 3556) .saturating_add(RocksDbWeight::get().reads(1_u64)) .saturating_add(RocksDbWeight::get().writes(2_u64)) } @@ -300,10 +303,10 @@ impl WeightInfo for () { /// Proof: Preimage StatusFor (max_values: None, max_size: Some(91), added: 2566, mode: MaxEncodedLen) fn request_preimage() -> Weight { // Proof Size summary in bytes: - // Measured: `220` - // Estimated: `2566` - // Minimum execution time: 17_192 nanoseconds. - Weight::from_parts(18_637_000, 2566) + // Measured: `188` + // Estimated: `3556` + // Minimum execution time: 19_590_000 picoseconds. + Weight::from_parts(20_638_000, 3556) .saturating_add(RocksDbWeight::get().reads(1_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } @@ -312,9 +315,9 @@ impl WeightInfo for () { fn request_no_deposit_preimage() -> Weight { // Proof Size summary in bytes: // Measured: `144` - // Estimated: `2566` - // Minimum execution time: 10_139 nanoseconds. - Weight::from_parts(10_773_000, 2566) + // Estimated: `3556` + // Minimum execution time: 11_689_000 picoseconds. + Weight::from_parts(12_577_000, 3556) .saturating_add(RocksDbWeight::get().reads(1_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } @@ -323,9 +326,9 @@ impl WeightInfo for () { fn request_unnoted_preimage() -> Weight { // Proof Size summary in bytes: // Measured: `42` - // Estimated: `2566` - // Minimum execution time: 11_721 nanoseconds. - Weight::from_parts(12_313_000, 2566) + // Estimated: `3556` + // Minimum execution time: 13_673_000 picoseconds. + Weight::from_parts(14_210_000, 3556) .saturating_add(RocksDbWeight::get().reads(1_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } @@ -334,9 +337,9 @@ impl WeightInfo for () { fn request_requested_preimage() -> Weight { // Proof Size summary in bytes: // Measured: `106` - // Estimated: `2566` - // Minimum execution time: 7_263 nanoseconds. - Weight::from_parts(7_547_000, 2566) + // Estimated: `3556` + // Minimum execution time: 8_810_000 picoseconds. + Weight::from_parts(9_044_000, 3556) .saturating_add(RocksDbWeight::get().reads(1_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } @@ -347,9 +350,9 @@ impl WeightInfo for () { fn unrequest_preimage() -> Weight { // Proof Size summary in bytes: // Measured: `144` - // Estimated: `2566` - // Minimum execution time: 17_785 nanoseconds. - Weight::from_parts(18_501_000, 2566) + // Estimated: `3556` + // Minimum execution time: 21_236_000 picoseconds. + Weight::from_parts(21_684_000, 3556) .saturating_add(RocksDbWeight::get().reads(1_u64)) .saturating_add(RocksDbWeight::get().writes(2_u64)) } @@ -358,9 +361,9 @@ impl WeightInfo for () { fn unrequest_unnoted_preimage() -> Weight { // Proof Size summary in bytes: // Measured: `106` - // Estimated: `2566` - // Minimum execution time: 7_090 nanoseconds. - Weight::from_parts(7_319_000, 2566) + // Estimated: `3556` + // Minimum execution time: 8_709_000 picoseconds. + Weight::from_parts(8_970_000, 3556) .saturating_add(RocksDbWeight::get().reads(1_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } @@ -369,9 +372,9 @@ impl WeightInfo for () { fn unrequest_multi_referenced_preimage() -> Weight { // Proof Size summary in bytes: // Measured: `106` - // Estimated: `2566` - // Minimum execution time: 7_253 nanoseconds. - Weight::from_parts(7_508_000, 2566) + // Estimated: `3556` + // Minimum execution time: 8_976_000 picoseconds. + Weight::from_parts(9_164_000, 3556) .saturating_add(RocksDbWeight::get().reads(1_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } diff --git a/frame/proxy/src/weights.rs b/frame/proxy/src/weights.rs index 5b70b61e5..28e24c468 100644 --- a/frame/proxy/src/weights.rs +++ b/frame/proxy/src/weights.rs @@ -18,9 +18,9 @@ //! Autogenerated weights for pallet_proxy //! //! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev -//! DATE: 2023-01-24, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! DATE: 2023-03-16, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` //! WORST CASE MAP SIZE: `1000000` -//! HOSTNAME: `bm2`, CPU: `Intel(R) Core(TM) i7-7700K CPU @ 4.20GHz` +//! HOSTNAME: `bm3`, CPU: `Intel(R) Core(TM) i7-7700K CPU @ 4.20GHz` //! EXECUTION: Some(Wasm), WASM-EXECUTION: Compiled, CHAIN: Some("dev"), DB CACHE: 1024 // Executed Command: @@ -31,6 +31,9 @@ // --steps=50 // --repeat=20 // --pallet=pallet_proxy +// --no-storage-info +// --no-median-slopes +// --no-min-squares // --extrinsic=* // --execution=wasm // --wasm-execution=compiled @@ -68,12 +71,12 @@ impl WeightInfo for SubstrateWeight { /// The range of component `p` is `[1, 31]`. fn proxy(p: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `193 + p * (37 ±0)` - // Estimated: `3716` - // Minimum execution time: 14_461 nanoseconds. - Weight::from_parts(14_913_927, 3716) - // Standard Error: 1_174 - .saturating_add(Weight::from_parts(36_087, 0).saturating_mul(p.into())) + // Measured: `161 + p * (37 ±0)` + // Estimated: `4706` + // Minimum execution time: 16_407_000 picoseconds. + Weight::from_parts(17_044_509, 4706) + // Standard Error: 1_177 + .saturating_add(Weight::from_parts(36_533, 0).saturating_mul(p.into())) .saturating_add(T::DbWeight::get().reads(1_u64)) } /// Storage: Proxy Proxies (r:1 w:0) @@ -86,14 +89,14 @@ impl WeightInfo for SubstrateWeight { /// The range of component `p` is `[1, 31]`. fn proxy_announced(a: u32, p: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `584 + a * (68 ±0) + p * (37 ±0)` - // Estimated: `11027` - // Minimum execution time: 31_523 nanoseconds. - Weight::from_parts(31_116_270, 11027) - // Standard Error: 1_789 - .saturating_add(Weight::from_parts(135_656, 0).saturating_mul(a.into())) - // Standard Error: 1_849 - .saturating_add(Weight::from_parts(53_893, 0).saturating_mul(p.into())) + // Measured: `488 + a * (68 ±0) + p * (37 ±0)` + // Estimated: `13997` + // Minimum execution time: 38_523_000 picoseconds. + Weight::from_parts(39_227_232, 13997) + // Standard Error: 5_703 + .saturating_add(Weight::from_parts(136_003, 0).saturating_mul(a.into())) + // Standard Error: 5_893 + .saturating_add(Weight::from_parts(42_397, 0).saturating_mul(p.into())) .saturating_add(T::DbWeight::get().reads(3_u64)) .saturating_add(T::DbWeight::get().writes(2_u64)) } @@ -105,14 +108,14 @@ impl WeightInfo for SubstrateWeight { /// The range of component `p` is `[1, 31]`. fn remove_announcement(a: u32, p: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `467 + a * (68 ±0)` - // Estimated: `7311` - // Minimum execution time: 19_363 nanoseconds. - Weight::from_parts(20_282_191, 7311) - // Standard Error: 1_084 - .saturating_add(Weight::from_parts(133_825, 0).saturating_mul(a.into())) - // Standard Error: 1_120 - .saturating_add(Weight::from_parts(3_434, 0).saturating_mul(p.into())) + // Measured: `403 + a * (68 ±0)` + // Estimated: `9291` + // Minimum execution time: 22_541_000 picoseconds. + Weight::from_parts(23_402_268, 9291) + // Standard Error: 3_242 + .saturating_add(Weight::from_parts(178_017, 0).saturating_mul(a.into())) + // Standard Error: 3_349 + .saturating_add(Weight::from_parts(6_230, 0).saturating_mul(p.into())) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().writes(2_u64)) } @@ -124,14 +127,14 @@ impl WeightInfo for SubstrateWeight { /// The range of component `p` is `[1, 31]`. fn reject_announcement(a: u32, p: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `467 + a * (68 ±0)` - // Estimated: `7311` - // Minimum execution time: 19_363 nanoseconds. - Weight::from_parts(20_211_584, 7311) - // Standard Error: 1_171 - .saturating_add(Weight::from_parts(136_984, 0).saturating_mul(a.into())) - // Standard Error: 1_210 - .saturating_add(Weight::from_parts(3_686, 0).saturating_mul(p.into())) + // Measured: `403 + a * (68 ±0)` + // Estimated: `9291` + // Minimum execution time: 22_539_000 picoseconds. + Weight::from_parts(23_479_286, 9291) + // Standard Error: 1_329 + .saturating_add(Weight::from_parts(174_021, 0).saturating_mul(a.into())) + // Standard Error: 1_373 + .saturating_add(Weight::from_parts(6_465, 0).saturating_mul(p.into())) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().writes(2_u64)) } @@ -145,14 +148,14 @@ impl WeightInfo for SubstrateWeight { /// The range of component `p` is `[1, 31]`. fn announce(a: u32, p: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `516 + a * (68 ±0) + p * (37 ±0)` - // Estimated: `11027` - // Minimum execution time: 27_811 nanoseconds. - Weight::from_parts(27_965_813, 11027) - // Standard Error: 1_987 - .saturating_add(Weight::from_parts(124_133, 0).saturating_mul(a.into())) - // Standard Error: 2_053 - .saturating_add(Weight::from_parts(54_692, 0).saturating_mul(p.into())) + // Measured: `420 + a * (68 ±0) + p * (37 ±0)` + // Estimated: `13997` + // Minimum execution time: 32_049_000 picoseconds. + Weight::from_parts(35_352_453, 13997) + // Standard Error: 4_389 + .saturating_add(Weight::from_parts(144_573, 0).saturating_mul(a.into())) + // Standard Error: 4_535 + .saturating_add(Weight::from_parts(41_849, 0).saturating_mul(p.into())) .saturating_add(T::DbWeight::get().reads(3_u64)) .saturating_add(T::DbWeight::get().writes(2_u64)) } @@ -161,12 +164,12 @@ impl WeightInfo for SubstrateWeight { /// The range of component `p` is `[1, 31]`. fn add_proxy(p: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `193 + p * (37 ±0)` - // Estimated: `3716` - // Minimum execution time: 20_922 nanoseconds. - Weight::from_parts(21_551_797, 3716) - // Standard Error: 1_425 - .saturating_add(Weight::from_parts(58_434, 0).saturating_mul(p.into())) + // Measured: `161 + p * (37 ±0)` + // Estimated: `4706` + // Minimum execution time: 24_491_000 picoseconds. + Weight::from_parts(25_291_582, 4706) + // Standard Error: 11_080 + .saturating_add(Weight::from_parts(68_024, 0).saturating_mul(p.into())) .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -175,12 +178,12 @@ impl WeightInfo for SubstrateWeight { /// The range of component `p` is `[1, 31]`. fn remove_proxy(p: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `193 + p * (37 ±0)` - // Estimated: `3716` - // Minimum execution time: 20_812 nanoseconds. - Weight::from_parts(21_660_732, 3716) - // Standard Error: 1_438 - .saturating_add(Weight::from_parts(68_740, 0).saturating_mul(p.into())) + // Measured: `161 + p * (37 ±0)` + // Estimated: `4706` + // Minimum execution time: 23_849_000 picoseconds. + Weight::from_parts(25_370_806, 4706) + // Standard Error: 8_763 + .saturating_add(Weight::from_parts(63_413, 0).saturating_mul(p.into())) .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -189,12 +192,12 @@ impl WeightInfo for SubstrateWeight { /// The range of component `p` is `[1, 31]`. fn remove_proxies(p: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `193 + p * (37 ±0)` - // Estimated: `3716` - // Minimum execution time: 16_786 nanoseconds. - Weight::from_parts(17_249_958, 3716) - // Standard Error: 1_007 - .saturating_add(Weight::from_parts(37_546, 0).saturating_mul(p.into())) + // Measured: `161 + p * (37 ±0)` + // Estimated: `4706` + // Minimum execution time: 19_488_000 picoseconds. + Weight::from_parts(20_219_817, 4706) + // Standard Error: 1_385 + .saturating_add(Weight::from_parts(30_354, 0).saturating_mul(p.into())) .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -204,11 +207,11 @@ impl WeightInfo for SubstrateWeight { fn create_pure(p: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `173` - // Estimated: `3716` - // Minimum execution time: 22_764 nanoseconds. - Weight::from_parts(23_539_039, 3716) - // Standard Error: 814 - .saturating_add(Weight::from_parts(144, 0).saturating_mul(p.into())) + // Estimated: `4706` + // Minimum execution time: 25_864_000 picoseconds. + Weight::from_parts(26_712_232, 4706) + // Standard Error: 1_331 + .saturating_add(Weight::from_parts(4_401, 0).saturating_mul(p.into())) .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -217,12 +220,12 @@ impl WeightInfo for SubstrateWeight { /// The range of component `p` is `[0, 30]`. fn kill_pure(p: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `230 + p * (37 ±0)` - // Estimated: `3716` - // Minimum execution time: 17_720 nanoseconds. - Weight::from_parts(18_428_849, 3716) - // Standard Error: 1_093 - .saturating_add(Weight::from_parts(34_600, 0).saturating_mul(p.into())) + // Measured: `198 + p * (37 ±0)` + // Estimated: `4706` + // Minimum execution time: 20_488_000 picoseconds. + Weight::from_parts(21_135_155, 4706) + // Standard Error: 1_255 + .saturating_add(Weight::from_parts(36_312, 0).saturating_mul(p.into())) .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -235,12 +238,12 @@ impl WeightInfo for () { /// The range of component `p` is `[1, 31]`. fn proxy(p: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `193 + p * (37 ±0)` - // Estimated: `3716` - // Minimum execution time: 14_461 nanoseconds. - Weight::from_parts(14_913_927, 3716) - // Standard Error: 1_174 - .saturating_add(Weight::from_parts(36_087, 0).saturating_mul(p.into())) + // Measured: `161 + p * (37 ±0)` + // Estimated: `4706` + // Minimum execution time: 16_407_000 picoseconds. + Weight::from_parts(17_044_509, 4706) + // Standard Error: 1_177 + .saturating_add(Weight::from_parts(36_533, 0).saturating_mul(p.into())) .saturating_add(RocksDbWeight::get().reads(1_u64)) } /// Storage: Proxy Proxies (r:1 w:0) @@ -253,14 +256,14 @@ impl WeightInfo for () { /// The range of component `p` is `[1, 31]`. fn proxy_announced(a: u32, p: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `584 + a * (68 ±0) + p * (37 ±0)` - // Estimated: `11027` - // Minimum execution time: 31_523 nanoseconds. - Weight::from_parts(31_116_270, 11027) - // Standard Error: 1_789 - .saturating_add(Weight::from_parts(135_656, 0).saturating_mul(a.into())) - // Standard Error: 1_849 - .saturating_add(Weight::from_parts(53_893, 0).saturating_mul(p.into())) + // Measured: `488 + a * (68 ±0) + p * (37 ±0)` + // Estimated: `13997` + // Minimum execution time: 38_523_000 picoseconds. + Weight::from_parts(39_227_232, 13997) + // Standard Error: 5_703 + .saturating_add(Weight::from_parts(136_003, 0).saturating_mul(a.into())) + // Standard Error: 5_893 + .saturating_add(Weight::from_parts(42_397, 0).saturating_mul(p.into())) .saturating_add(RocksDbWeight::get().reads(3_u64)) .saturating_add(RocksDbWeight::get().writes(2_u64)) } @@ -272,14 +275,14 @@ impl WeightInfo for () { /// The range of component `p` is `[1, 31]`. fn remove_announcement(a: u32, p: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `467 + a * (68 ±0)` - // Estimated: `7311` - // Minimum execution time: 19_363 nanoseconds. - Weight::from_parts(20_282_191, 7311) - // Standard Error: 1_084 - .saturating_add(Weight::from_parts(133_825, 0).saturating_mul(a.into())) - // Standard Error: 1_120 - .saturating_add(Weight::from_parts(3_434, 0).saturating_mul(p.into())) + // Measured: `403 + a * (68 ±0)` + // Estimated: `9291` + // Minimum execution time: 22_541_000 picoseconds. + Weight::from_parts(23_402_268, 9291) + // Standard Error: 3_242 + .saturating_add(Weight::from_parts(178_017, 0).saturating_mul(a.into())) + // Standard Error: 3_349 + .saturating_add(Weight::from_parts(6_230, 0).saturating_mul(p.into())) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().writes(2_u64)) } @@ -291,14 +294,14 @@ impl WeightInfo for () { /// The range of component `p` is `[1, 31]`. fn reject_announcement(a: u32, p: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `467 + a * (68 ±0)` - // Estimated: `7311` - // Minimum execution time: 19_363 nanoseconds. - Weight::from_parts(20_211_584, 7311) - // Standard Error: 1_171 - .saturating_add(Weight::from_parts(136_984, 0).saturating_mul(a.into())) - // Standard Error: 1_210 - .saturating_add(Weight::from_parts(3_686, 0).saturating_mul(p.into())) + // Measured: `403 + a * (68 ±0)` + // Estimated: `9291` + // Minimum execution time: 22_539_000 picoseconds. + Weight::from_parts(23_479_286, 9291) + // Standard Error: 1_329 + .saturating_add(Weight::from_parts(174_021, 0).saturating_mul(a.into())) + // Standard Error: 1_373 + .saturating_add(Weight::from_parts(6_465, 0).saturating_mul(p.into())) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().writes(2_u64)) } @@ -312,14 +315,14 @@ impl WeightInfo for () { /// The range of component `p` is `[1, 31]`. fn announce(a: u32, p: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `516 + a * (68 ±0) + p * (37 ±0)` - // Estimated: `11027` - // Minimum execution time: 27_811 nanoseconds. - Weight::from_parts(27_965_813, 11027) - // Standard Error: 1_987 - .saturating_add(Weight::from_parts(124_133, 0).saturating_mul(a.into())) - // Standard Error: 2_053 - .saturating_add(Weight::from_parts(54_692, 0).saturating_mul(p.into())) + // Measured: `420 + a * (68 ±0) + p * (37 ±0)` + // Estimated: `13997` + // Minimum execution time: 32_049_000 picoseconds. + Weight::from_parts(35_352_453, 13997) + // Standard Error: 4_389 + .saturating_add(Weight::from_parts(144_573, 0).saturating_mul(a.into())) + // Standard Error: 4_535 + .saturating_add(Weight::from_parts(41_849, 0).saturating_mul(p.into())) .saturating_add(RocksDbWeight::get().reads(3_u64)) .saturating_add(RocksDbWeight::get().writes(2_u64)) } @@ -328,12 +331,12 @@ impl WeightInfo for () { /// The range of component `p` is `[1, 31]`. fn add_proxy(p: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `193 + p * (37 ±0)` - // Estimated: `3716` - // Minimum execution time: 20_922 nanoseconds. - Weight::from_parts(21_551_797, 3716) - // Standard Error: 1_425 - .saturating_add(Weight::from_parts(58_434, 0).saturating_mul(p.into())) + // Measured: `161 + p * (37 ±0)` + // Estimated: `4706` + // Minimum execution time: 24_491_000 picoseconds. + Weight::from_parts(25_291_582, 4706) + // Standard Error: 11_080 + .saturating_add(Weight::from_parts(68_024, 0).saturating_mul(p.into())) .saturating_add(RocksDbWeight::get().reads(1_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } @@ -342,12 +345,12 @@ impl WeightInfo for () { /// The range of component `p` is `[1, 31]`. fn remove_proxy(p: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `193 + p * (37 ±0)` - // Estimated: `3716` - // Minimum execution time: 20_812 nanoseconds. - Weight::from_parts(21_660_732, 3716) - // Standard Error: 1_438 - .saturating_add(Weight::from_parts(68_740, 0).saturating_mul(p.into())) + // Measured: `161 + p * (37 ±0)` + // Estimated: `4706` + // Minimum execution time: 23_849_000 picoseconds. + Weight::from_parts(25_370_806, 4706) + // Standard Error: 8_763 + .saturating_add(Weight::from_parts(63_413, 0).saturating_mul(p.into())) .saturating_add(RocksDbWeight::get().reads(1_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } @@ -356,12 +359,12 @@ impl WeightInfo for () { /// The range of component `p` is `[1, 31]`. fn remove_proxies(p: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `193 + p * (37 ±0)` - // Estimated: `3716` - // Minimum execution time: 16_786 nanoseconds. - Weight::from_parts(17_249_958, 3716) - // Standard Error: 1_007 - .saturating_add(Weight::from_parts(37_546, 0).saturating_mul(p.into())) + // Measured: `161 + p * (37 ±0)` + // Estimated: `4706` + // Minimum execution time: 19_488_000 picoseconds. + Weight::from_parts(20_219_817, 4706) + // Standard Error: 1_385 + .saturating_add(Weight::from_parts(30_354, 0).saturating_mul(p.into())) .saturating_add(RocksDbWeight::get().reads(1_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } @@ -371,11 +374,11 @@ impl WeightInfo for () { fn create_pure(p: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `173` - // Estimated: `3716` - // Minimum execution time: 22_764 nanoseconds. - Weight::from_parts(23_539_039, 3716) - // Standard Error: 814 - .saturating_add(Weight::from_parts(144, 0).saturating_mul(p.into())) + // Estimated: `4706` + // Minimum execution time: 25_864_000 picoseconds. + Weight::from_parts(26_712_232, 4706) + // Standard Error: 1_331 + .saturating_add(Weight::from_parts(4_401, 0).saturating_mul(p.into())) .saturating_add(RocksDbWeight::get().reads(1_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } @@ -384,12 +387,12 @@ impl WeightInfo for () { /// The range of component `p` is `[0, 30]`. fn kill_pure(p: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `230 + p * (37 ±0)` - // Estimated: `3716` - // Minimum execution time: 17_720 nanoseconds. - Weight::from_parts(18_428_849, 3716) - // Standard Error: 1_093 - .saturating_add(Weight::from_parts(34_600, 0).saturating_mul(p.into())) + // Measured: `198 + p * (37 ±0)` + // Estimated: `4706` + // Minimum execution time: 20_488_000 picoseconds. + Weight::from_parts(21_135_155, 4706) + // Standard Error: 1_255 + .saturating_add(Weight::from_parts(36_312, 0).saturating_mul(p.into())) .saturating_add(RocksDbWeight::get().reads(1_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } diff --git a/frame/ranked-collective/src/weights.rs b/frame/ranked-collective/src/weights.rs index 8d6c1620d..e25737a1d 100644 --- a/frame/ranked-collective/src/weights.rs +++ b/frame/ranked-collective/src/weights.rs @@ -18,9 +18,9 @@ //! Autogenerated weights for pallet_ranked_collective //! //! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev -//! DATE: 2023-01-24, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! DATE: 2023-03-16, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` //! WORST CASE MAP SIZE: `1000000` -//! HOSTNAME: `bm2`, CPU: `Intel(R) Core(TM) i7-7700K CPU @ 4.20GHz` +//! HOSTNAME: `bm3`, CPU: `Intel(R) Core(TM) i7-7700K CPU @ 4.20GHz` //! EXECUTION: Some(Wasm), WASM-EXECUTION: Compiled, CHAIN: Some("dev"), DB CACHE: 1024 // Executed Command: @@ -31,6 +31,9 @@ // --steps=50 // --repeat=20 // --pallet=pallet_ranked_collective +// --no-storage-info +// --no-median-slopes +// --no-min-squares // --extrinsic=* // --execution=wasm // --wasm-execution=compiled @@ -70,9 +73,9 @@ impl WeightInfo for SubstrateWeight { fn add_member() -> Weight { // Proof Size summary in bytes: // Measured: `109` - // Estimated: `5006` - // Minimum execution time: 16_334 nanoseconds. - Weight::from_parts(16_784_000, 5006) + // Estimated: `6986` + // Minimum execution time: 19_174_000 picoseconds. + Weight::from_parts(19_391_000, 6986) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().writes(4_u64)) } @@ -88,11 +91,11 @@ impl WeightInfo for SubstrateWeight { fn remove_member(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `583 + r * (281 ±0)` - // Estimated: `10064 + r * (7547 ±0)` - // Minimum execution time: 26_177 nanoseconds. - Weight::from_parts(29_245_248, 10064) - // Standard Error: 18_611 - .saturating_add(Weight::from_parts(10_916_516, 0).saturating_mul(r.into())) + // Estimated: `14024 + r * (7547 ±0)` + // Minimum execution time: 30_972_000 picoseconds. + Weight::from_parts(35_526_205, 14024) + // Standard Error: 47_832 + .saturating_add(Weight::from_parts(12_838_739, 0).saturating_mul(r.into())) .saturating_add(T::DbWeight::get().reads(4_u64)) .saturating_add(T::DbWeight::get().reads((3_u64).saturating_mul(r.into()))) .saturating_add(T::DbWeight::get().writes(4_u64)) @@ -111,11 +114,11 @@ impl WeightInfo for SubstrateWeight { fn promote_member(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `281 + r * (17 ±0)` - // Estimated: `5006` - // Minimum execution time: 18_953 nanoseconds. - Weight::from_parts(19_570_567, 5006) - // Standard Error: 4_156 - .saturating_add(Weight::from_parts(263_843, 0).saturating_mul(r.into())) + // Estimated: `6986` + // Minimum execution time: 21_573_000 picoseconds. + Weight::from_parts(22_184_210, 6986) + // Standard Error: 10_584 + .saturating_add(Weight::from_parts(289_535, 0).saturating_mul(r.into())) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().writes(4_u64)) } @@ -131,11 +134,11 @@ impl WeightInfo for SubstrateWeight { fn demote_member(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `599 + r * (72 ±0)` - // Estimated: `10064` - // Minimum execution time: 26_243 nanoseconds. - Weight::from_parts(28_532_816, 10064) - // Standard Error: 22_689 - .saturating_add(Weight::from_parts(614_464, 0).saturating_mul(r.into())) + // Estimated: `14024` + // Minimum execution time: 30_249_000 picoseconds. + Weight::from_parts(32_702_502, 14024) + // Standard Error: 26_725 + .saturating_add(Weight::from_parts(651_139, 0).saturating_mul(r.into())) .saturating_add(T::DbWeight::get().reads(4_u64)) .saturating_add(T::DbWeight::get().writes(4_u64)) } @@ -149,10 +152,10 @@ impl WeightInfo for SubstrateWeight { /// Proof: Scheduler Agenda (max_values: None, max_size: Some(107022), added: 109497, mode: MaxEncodedLen) fn vote() -> Weight { // Proof Size summary in bytes: - // Measured: `626` - // Estimated: `226856` - // Minimum execution time: 41_121 nanoseconds. - Weight::from_parts(41_606_000, 226856) + // Measured: `595` + // Estimated: `230816` + // Minimum execution time: 46_972_000 picoseconds. + Weight::from_parts(47_617_000, 230816) .saturating_add(T::DbWeight::get().reads(5_u64)) .saturating_add(T::DbWeight::get().writes(4_u64)) } @@ -160,19 +163,21 @@ impl WeightInfo for SubstrateWeight { /// Proof: RankedPolls ReferendumInfoFor (max_values: None, max_size: Some(330), added: 2805, mode: MaxEncodedLen) /// Storage: RankedCollective VotingCleanup (r:1 w:0) /// Proof: RankedCollective VotingCleanup (max_values: None, max_size: Some(114), added: 2589, mode: MaxEncodedLen) - /// Storage: RankedCollective Voting (r:0 w:100) + /// Storage: RankedCollective Voting (r:100 w:100) /// Proof: RankedCollective Voting (max_values: None, max_size: Some(65), added: 2540, mode: MaxEncodedLen) /// The range of component `n` is `[0, 100]`. fn cleanup_poll(n: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `461 + n * (50 ±0)` - // Estimated: `5394` - // Minimum execution time: 13_245 nanoseconds. - Weight::from_parts(17_420_271, 5394) - // Standard Error: 1_503 - .saturating_add(Weight::from_parts(952_500, 0).saturating_mul(n.into())) + // Measured: `429 + n * (50 ±0)` + // Estimated: `8364 + n * (2540 ±0)` + // Minimum execution time: 15_301_000 picoseconds. + Weight::from_parts(19_308_613, 8364) + // Standard Error: 1_792 + .saturating_add(Weight::from_parts(1_015_251, 0).saturating_mul(n.into())) .saturating_add(T::DbWeight::get().reads(2_u64)) + .saturating_add(T::DbWeight::get().reads((1_u64).saturating_mul(n.into()))) .saturating_add(T::DbWeight::get().writes((1_u64).saturating_mul(n.into()))) + .saturating_add(Weight::from_parts(0, 2540).saturating_mul(n.into())) } } @@ -189,9 +194,9 @@ impl WeightInfo for () { fn add_member() -> Weight { // Proof Size summary in bytes: // Measured: `109` - // Estimated: `5006` - // Minimum execution time: 16_334 nanoseconds. - Weight::from_parts(16_784_000, 5006) + // Estimated: `6986` + // Minimum execution time: 19_174_000 picoseconds. + Weight::from_parts(19_391_000, 6986) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().writes(4_u64)) } @@ -207,11 +212,11 @@ impl WeightInfo for () { fn remove_member(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `583 + r * (281 ±0)` - // Estimated: `10064 + r * (7547 ±0)` - // Minimum execution time: 26_177 nanoseconds. - Weight::from_parts(29_245_248, 10064) - // Standard Error: 18_611 - .saturating_add(Weight::from_parts(10_916_516, 0).saturating_mul(r.into())) + // Estimated: `14024 + r * (7547 ±0)` + // Minimum execution time: 30_972_000 picoseconds. + Weight::from_parts(35_526_205, 14024) + // Standard Error: 47_832 + .saturating_add(Weight::from_parts(12_838_739, 0).saturating_mul(r.into())) .saturating_add(RocksDbWeight::get().reads(4_u64)) .saturating_add(RocksDbWeight::get().reads((3_u64).saturating_mul(r.into()))) .saturating_add(RocksDbWeight::get().writes(4_u64)) @@ -230,11 +235,11 @@ impl WeightInfo for () { fn promote_member(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `281 + r * (17 ±0)` - // Estimated: `5006` - // Minimum execution time: 18_953 nanoseconds. - Weight::from_parts(19_570_567, 5006) - // Standard Error: 4_156 - .saturating_add(Weight::from_parts(263_843, 0).saturating_mul(r.into())) + // Estimated: `6986` + // Minimum execution time: 21_573_000 picoseconds. + Weight::from_parts(22_184_210, 6986) + // Standard Error: 10_584 + .saturating_add(Weight::from_parts(289_535, 0).saturating_mul(r.into())) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().writes(4_u64)) } @@ -250,11 +255,11 @@ impl WeightInfo for () { fn demote_member(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `599 + r * (72 ±0)` - // Estimated: `10064` - // Minimum execution time: 26_243 nanoseconds. - Weight::from_parts(28_532_816, 10064) - // Standard Error: 22_689 - .saturating_add(Weight::from_parts(614_464, 0).saturating_mul(r.into())) + // Estimated: `14024` + // Minimum execution time: 30_249_000 picoseconds. + Weight::from_parts(32_702_502, 14024) + // Standard Error: 26_725 + .saturating_add(Weight::from_parts(651_139, 0).saturating_mul(r.into())) .saturating_add(RocksDbWeight::get().reads(4_u64)) .saturating_add(RocksDbWeight::get().writes(4_u64)) } @@ -268,10 +273,10 @@ impl WeightInfo for () { /// Proof: Scheduler Agenda (max_values: None, max_size: Some(107022), added: 109497, mode: MaxEncodedLen) fn vote() -> Weight { // Proof Size summary in bytes: - // Measured: `626` - // Estimated: `226856` - // Minimum execution time: 41_121 nanoseconds. - Weight::from_parts(41_606_000, 226856) + // Measured: `595` + // Estimated: `230816` + // Minimum execution time: 46_972_000 picoseconds. + Weight::from_parts(47_617_000, 230816) .saturating_add(RocksDbWeight::get().reads(5_u64)) .saturating_add(RocksDbWeight::get().writes(4_u64)) } @@ -279,18 +284,20 @@ impl WeightInfo for () { /// Proof: RankedPolls ReferendumInfoFor (max_values: None, max_size: Some(330), added: 2805, mode: MaxEncodedLen) /// Storage: RankedCollective VotingCleanup (r:1 w:0) /// Proof: RankedCollective VotingCleanup (max_values: None, max_size: Some(114), added: 2589, mode: MaxEncodedLen) - /// Storage: RankedCollective Voting (r:0 w:100) + /// Storage: RankedCollective Voting (r:100 w:100) /// Proof: RankedCollective Voting (max_values: None, max_size: Some(65), added: 2540, mode: MaxEncodedLen) /// The range of component `n` is `[0, 100]`. fn cleanup_poll(n: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `461 + n * (50 ±0)` - // Estimated: `5394` - // Minimum execution time: 13_245 nanoseconds. - Weight::from_parts(17_420_271, 5394) - // Standard Error: 1_503 - .saturating_add(Weight::from_parts(952_500, 0).saturating_mul(n.into())) + // Measured: `429 + n * (50 ±0)` + // Estimated: `8364 + n * (2540 ±0)` + // Minimum execution time: 15_301_000 picoseconds. + Weight::from_parts(19_308_613, 8364) + // Standard Error: 1_792 + .saturating_add(Weight::from_parts(1_015_251, 0).saturating_mul(n.into())) .saturating_add(RocksDbWeight::get().reads(2_u64)) + .saturating_add(RocksDbWeight::get().reads((1_u64).saturating_mul(n.into()))) .saturating_add(RocksDbWeight::get().writes((1_u64).saturating_mul(n.into()))) + .saturating_add(Weight::from_parts(0, 2540).saturating_mul(n.into())) } } diff --git a/frame/recovery/src/weights.rs b/frame/recovery/src/weights.rs index 6f8818f81..8245b9606 100644 --- a/frame/recovery/src/weights.rs +++ b/frame/recovery/src/weights.rs @@ -18,9 +18,9 @@ //! Autogenerated weights for pallet_recovery //! //! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev -//! DATE: 2023-01-24, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! DATE: 2023-03-16, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` //! WORST CASE MAP SIZE: `1000000` -//! HOSTNAME: `bm2`, CPU: `Intel(R) Core(TM) i7-7700K CPU @ 4.20GHz` +//! HOSTNAME: `bm3`, CPU: `Intel(R) Core(TM) i7-7700K CPU @ 4.20GHz` //! EXECUTION: Some(Wasm), WASM-EXECUTION: Compiled, CHAIN: Some("dev"), DB CACHE: 1024 // Executed Command: @@ -31,6 +31,9 @@ // --steps=50 // --repeat=20 // --pallet=pallet_recovery +// --no-storage-info +// --no-median-slopes +// --no-min-squares // --extrinsic=* // --execution=wasm // --wasm-execution=compiled @@ -67,9 +70,9 @@ impl WeightInfo for SubstrateWeight { fn as_recovered() -> Weight { // Proof Size summary in bytes: // Measured: `281` - // Estimated: `2555` - // Minimum execution time: 8_866 nanoseconds. - Weight::from_parts(9_065_000, 2555) + // Estimated: `3545` + // Minimum execution time: 9_937_000 picoseconds. + Weight::from_parts(10_213_000, 3545) .saturating_add(T::DbWeight::get().reads(1_u64)) } /// Storage: Recovery Proxy (r:0 w:1) @@ -78,8 +81,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 8_893 nanoseconds. - Weight::from_parts(9_177_000, 0) + // Minimum execution time: 9_704_000 picoseconds. + Weight::from_parts(9_997_000, 0) .saturating_add(T::DbWeight::get().writes(1_u64)) } /// Storage: Recovery Recoverable (r:1 w:1) @@ -88,11 +91,11 @@ impl WeightInfo for SubstrateWeight { fn create_recovery(n: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `175` - // Estimated: `2826` - // Minimum execution time: 20_662 nanoseconds. - Weight::from_parts(21_378_064, 2826) - // Standard Error: 3_350 - .saturating_add(Weight::from_parts(83_738, 0).saturating_mul(n.into())) + // Estimated: `3816` + // Minimum execution time: 23_260_000 picoseconds. + Weight::from_parts(24_408_081, 3816) + // Standard Error: 3_086 + .saturating_add(Weight::from_parts(41_628, 0).saturating_mul(n.into())) .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -103,9 +106,9 @@ impl WeightInfo for SubstrateWeight { fn initiate_recovery() -> Weight { // Proof Size summary in bytes: // Measured: `272` - // Estimated: `5690` - // Minimum execution time: 24_805 nanoseconds. - Weight::from_parts(25_273_000, 5690) + // Estimated: `7670` + // Minimum execution time: 28_148_000 picoseconds. + Weight::from_parts(28_577_000, 7670) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -116,12 +119,12 @@ impl WeightInfo for SubstrateWeight { /// The range of component `n` is `[1, 9]`. fn vouch_recovery(n: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `423 + n * (64 ±0)` - // Estimated: `5690` - // Minimum execution time: 17_837 nanoseconds. - Weight::from_parts(18_429_664, 5690) - // Standard Error: 3_187 - .saturating_add(Weight::from_parts(143_648, 0).saturating_mul(n.into())) + // Measured: `360 + n * (64 ±0)` + // Estimated: `7670` + // Minimum execution time: 20_395_000 picoseconds. + Weight::from_parts(21_287_972, 7670) + // Standard Error: 4_464 + .saturating_add(Weight::from_parts(170_214, 0).saturating_mul(n.into())) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -134,12 +137,12 @@ impl WeightInfo for SubstrateWeight { /// The range of component `n` is `[1, 9]`. fn claim_recovery(n: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `455 + n * (64 ±0)` - // Estimated: `8245` - // Minimum execution time: 21_960 nanoseconds. - Weight::from_parts(22_529_644, 8245) - // Standard Error: 2_945 - .saturating_add(Weight::from_parts(85_604, 0).saturating_mul(n.into())) + // Measured: `392 + n * (64 ±0)` + // Estimated: `11215` + // Minimum execution time: 24_902_000 picoseconds. + Weight::from_parts(25_662_621, 11215) + // Standard Error: 3_101 + .saturating_add(Weight::from_parts(60_748, 0).saturating_mul(n.into())) .saturating_add(T::DbWeight::get().reads(3_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -150,12 +153,12 @@ impl WeightInfo for SubstrateWeight { /// The range of component `n` is `[1, 9]`. fn close_recovery(n: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `576 + n * (32 ±0)` - // Estimated: `5467` - // Minimum execution time: 26_054 nanoseconds. - Weight::from_parts(26_724_866, 5467) - // Standard Error: 2_645 - .saturating_add(Weight::from_parts(104_301, 0).saturating_mul(n.into())) + // Measured: `513 + n * (32 ±0)` + // Estimated: `7447` + // Minimum execution time: 30_071_000 picoseconds. + Weight::from_parts(30_727_760, 7447) + // Standard Error: 4_442 + .saturating_add(Weight::from_parts(98_153, 0).saturating_mul(n.into())) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().writes(2_u64)) } @@ -164,14 +167,12 @@ impl WeightInfo for SubstrateWeight { /// Storage: Recovery Recoverable (r:1 w:1) /// Proof: Recovery Recoverable (max_values: None, max_size: Some(351), added: 2826, mode: MaxEncodedLen) /// The range of component `n` is `[1, 9]`. - fn remove_recovery(n: u32, ) -> Weight { + fn remove_recovery(_n: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `302 + n * (32 ±0)` - // Estimated: `5690` - // Minimum execution time: 25_110 nanoseconds. - Weight::from_parts(25_805_837, 5690) - // Standard Error: 2_732 - .saturating_add(Weight::from_parts(73_458, 0).saturating_mul(n.into())) + // Measured: `270 + n * (32 ±0)` + // Estimated: `7670` + // Minimum execution time: 29_088_000 picoseconds. + Weight::from_parts(30_535_744, 7670) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -180,9 +181,9 @@ impl WeightInfo for SubstrateWeight { fn cancel_recovered() -> Weight { // Proof Size summary in bytes: // Measured: `281` - // Estimated: `2555` - // Minimum execution time: 11_061 nanoseconds. - Weight::from_parts(11_291_000, 2555) + // Estimated: `3545` + // Minimum execution time: 12_712_000 picoseconds. + Weight::from_parts(12_980_000, 3545) .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -195,9 +196,9 @@ impl WeightInfo for () { fn as_recovered() -> Weight { // Proof Size summary in bytes: // Measured: `281` - // Estimated: `2555` - // Minimum execution time: 8_866 nanoseconds. - Weight::from_parts(9_065_000, 2555) + // Estimated: `3545` + // Minimum execution time: 9_937_000 picoseconds. + Weight::from_parts(10_213_000, 3545) .saturating_add(RocksDbWeight::get().reads(1_u64)) } /// Storage: Recovery Proxy (r:0 w:1) @@ -206,8 +207,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 8_893 nanoseconds. - Weight::from_parts(9_177_000, 0) + // Minimum execution time: 9_704_000 picoseconds. + Weight::from_parts(9_997_000, 0) .saturating_add(RocksDbWeight::get().writes(1_u64)) } /// Storage: Recovery Recoverable (r:1 w:1) @@ -216,11 +217,11 @@ impl WeightInfo for () { fn create_recovery(n: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `175` - // Estimated: `2826` - // Minimum execution time: 20_662 nanoseconds. - Weight::from_parts(21_378_064, 2826) - // Standard Error: 3_350 - .saturating_add(Weight::from_parts(83_738, 0).saturating_mul(n.into())) + // Estimated: `3816` + // Minimum execution time: 23_260_000 picoseconds. + Weight::from_parts(24_408_081, 3816) + // Standard Error: 3_086 + .saturating_add(Weight::from_parts(41_628, 0).saturating_mul(n.into())) .saturating_add(RocksDbWeight::get().reads(1_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } @@ -231,9 +232,9 @@ impl WeightInfo for () { fn initiate_recovery() -> Weight { // Proof Size summary in bytes: // Measured: `272` - // Estimated: `5690` - // Minimum execution time: 24_805 nanoseconds. - Weight::from_parts(25_273_000, 5690) + // Estimated: `7670` + // Minimum execution time: 28_148_000 picoseconds. + Weight::from_parts(28_577_000, 7670) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } @@ -244,12 +245,12 @@ impl WeightInfo for () { /// The range of component `n` is `[1, 9]`. fn vouch_recovery(n: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `423 + n * (64 ±0)` - // Estimated: `5690` - // Minimum execution time: 17_837 nanoseconds. - Weight::from_parts(18_429_664, 5690) - // Standard Error: 3_187 - .saturating_add(Weight::from_parts(143_648, 0).saturating_mul(n.into())) + // Measured: `360 + n * (64 ±0)` + // Estimated: `7670` + // Minimum execution time: 20_395_000 picoseconds. + Weight::from_parts(21_287_972, 7670) + // Standard Error: 4_464 + .saturating_add(Weight::from_parts(170_214, 0).saturating_mul(n.into())) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } @@ -262,12 +263,12 @@ impl WeightInfo for () { /// The range of component `n` is `[1, 9]`. fn claim_recovery(n: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `455 + n * (64 ±0)` - // Estimated: `8245` - // Minimum execution time: 21_960 nanoseconds. - Weight::from_parts(22_529_644, 8245) - // Standard Error: 2_945 - .saturating_add(Weight::from_parts(85_604, 0).saturating_mul(n.into())) + // Measured: `392 + n * (64 ±0)` + // Estimated: `11215` + // Minimum execution time: 24_902_000 picoseconds. + Weight::from_parts(25_662_621, 11215) + // Standard Error: 3_101 + .saturating_add(Weight::from_parts(60_748, 0).saturating_mul(n.into())) .saturating_add(RocksDbWeight::get().reads(3_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } @@ -278,12 +279,12 @@ impl WeightInfo for () { /// The range of component `n` is `[1, 9]`. fn close_recovery(n: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `576 + n * (32 ±0)` - // Estimated: `5467` - // Minimum execution time: 26_054 nanoseconds. - Weight::from_parts(26_724_866, 5467) - // Standard Error: 2_645 - .saturating_add(Weight::from_parts(104_301, 0).saturating_mul(n.into())) + // Measured: `513 + n * (32 ±0)` + // Estimated: `7447` + // Minimum execution time: 30_071_000 picoseconds. + Weight::from_parts(30_727_760, 7447) + // Standard Error: 4_442 + .saturating_add(Weight::from_parts(98_153, 0).saturating_mul(n.into())) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().writes(2_u64)) } @@ -292,14 +293,12 @@ impl WeightInfo for () { /// Storage: Recovery Recoverable (r:1 w:1) /// Proof: Recovery Recoverable (max_values: None, max_size: Some(351), added: 2826, mode: MaxEncodedLen) /// The range of component `n` is `[1, 9]`. - fn remove_recovery(n: u32, ) -> Weight { + fn remove_recovery(_n: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `302 + n * (32 ±0)` - // Estimated: `5690` - // Minimum execution time: 25_110 nanoseconds. - Weight::from_parts(25_805_837, 5690) - // Standard Error: 2_732 - .saturating_add(Weight::from_parts(73_458, 0).saturating_mul(n.into())) + // Measured: `270 + n * (32 ±0)` + // Estimated: `7670` + // Minimum execution time: 29_088_000 picoseconds. + Weight::from_parts(30_535_744, 7670) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } @@ -308,9 +307,9 @@ impl WeightInfo for () { fn cancel_recovered() -> Weight { // Proof Size summary in bytes: // Measured: `281` - // Estimated: `2555` - // Minimum execution time: 11_061 nanoseconds. - Weight::from_parts(11_291_000, 2555) + // Estimated: `3545` + // Minimum execution time: 12_712_000 picoseconds. + Weight::from_parts(12_980_000, 3545) .saturating_add(RocksDbWeight::get().reads(1_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } diff --git a/frame/referenda/src/weights.rs b/frame/referenda/src/weights.rs index 817e0d88d..3ac0f54ed 100644 --- a/frame/referenda/src/weights.rs +++ b/frame/referenda/src/weights.rs @@ -18,26 +18,28 @@ //! Autogenerated weights for pallet_referenda //! //! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev -//! DATE: 2023-01-27, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! DATE: 2023-03-16, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` //! WORST CASE MAP SIZE: `1000000` -//! HOSTNAME: `runner-b3zmxxc-project-145-concurrent-0`, CPU: `Intel(R) Xeon(R) CPU @ 2.60GHz` +//! HOSTNAME: `bm3`, CPU: `Intel(R) Core(TM) i7-7700K CPU @ 4.20GHz` //! EXECUTION: Some(Wasm), WASM-EXECUTION: Compiled, CHAIN: Some("dev"), DB CACHE: 1024 // Executed Command: -// target/production/substrate +// ./target/production/substrate // benchmark // pallet +// --chain=dev // --steps=50 // --repeat=20 +// --pallet=pallet_referenda +// --no-storage-info +// --no-median-slopes +// --no-min-squares // --extrinsic=* // --execution=wasm // --wasm-execution=compiled // --heap-pages=4096 -// --json-file=/builds/parity/mirrors/substrate/.git/.artifacts/bench.json -// --pallet=pallet_referenda -// --chain=dev -// --header=./HEADER-APACHE2 // --output=./frame/referenda/src/weights.rs +// --header=./HEADER-APACHE2 // --template=./.maintain/frame-weight-template.hbs #![cfg_attr(rustfmt, rustfmt_skip)] @@ -92,10 +94,10 @@ impl WeightInfo for SubstrateWeight { /// Proof: Referenda ReferendumInfoFor (max_values: None, max_size: Some(366), added: 2841, mode: MaxEncodedLen) fn submit() -> Weight { // Proof Size summary in bytes: - // Measured: `251` - // Estimated: `109996` - // Minimum execution time: 34_540 nanoseconds. - Weight::from_parts(36_144_000, 109996) + // Measured: `220` + // Estimated: `111976` + // Minimum execution time: 36_851_000 picoseconds. + Weight::from_parts(37_183_000, 111976) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) } @@ -105,10 +107,10 @@ impl WeightInfo for SubstrateWeight { /// Proof: Scheduler Agenda (max_values: None, max_size: Some(107022), added: 109497, mode: MaxEncodedLen) fn place_decision_deposit_preparing() -> Weight { // Proof Size summary in bytes: - // Measured: `536` - // Estimated: `221835` - // Minimum execution time: 46_963 nanoseconds. - Weight::from_parts(48_459_000, 221835) + // Measured: `473` + // Estimated: `223815` + // Minimum execution time: 49_435_000 picoseconds. + Weight::from_parts(50_056_000, 223815) .saturating_add(T::DbWeight::get().reads(3_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) } @@ -120,10 +122,10 @@ impl WeightInfo for SubstrateWeight { /// Proof: Referenda TrackQueue (max_values: None, max_size: Some(2012), added: 4487, mode: MaxEncodedLen) fn place_decision_deposit_queued() -> Weight { // Proof Size summary in bytes: - // Measured: `3203` - // Estimated: `9817` - // Minimum execution time: 55_798 nanoseconds. - Weight::from_parts(58_260_000, 9817) + // Measured: `3107` + // Estimated: `12787` + // Minimum execution time: 47_061_000 picoseconds. + Weight::from_parts(47_579_000, 12787) .saturating_add(T::DbWeight::get().reads(3_u64)) .saturating_add(T::DbWeight::get().writes(2_u64)) } @@ -135,10 +137,10 @@ impl WeightInfo for SubstrateWeight { /// Proof: Referenda TrackQueue (max_values: None, max_size: Some(2012), added: 4487, mode: MaxEncodedLen) fn place_decision_deposit_not_queued() -> Weight { // Proof Size summary in bytes: - // Measured: `3223` - // Estimated: `9817` - // Minimum execution time: 53_888 nanoseconds. - Weight::from_parts(57_919_000, 9817) + // Measured: `3127` + // Estimated: `12787` + // Minimum execution time: 46_513_000 picoseconds. + Weight::from_parts(47_212_000, 12787) .saturating_add(T::DbWeight::get().reads(3_u64)) .saturating_add(T::DbWeight::get().writes(2_u64)) } @@ -150,10 +152,10 @@ impl WeightInfo for SubstrateWeight { /// Proof: Scheduler Agenda (max_values: None, max_size: Some(107022), added: 109497, mode: MaxEncodedLen) fn place_decision_deposit_passing() -> Weight { // Proof Size summary in bytes: - // Measured: `536` - // Estimated: `224324` - // Minimum execution time: 56_121 nanoseconds. - Weight::from_parts(58_301_000, 224324) + // Measured: `473` + // Estimated: `227294` + // Minimum execution time: 59_255_000 picoseconds. + Weight::from_parts(60_114_000, 227294) .saturating_add(T::DbWeight::get().reads(4_u64)) .saturating_add(T::DbWeight::get().writes(4_u64)) } @@ -165,10 +167,10 @@ impl WeightInfo for SubstrateWeight { /// Proof: Scheduler Agenda (max_values: None, max_size: Some(107022), added: 109497, mode: MaxEncodedLen) fn place_decision_deposit_failing() -> Weight { // Proof Size summary in bytes: - // Measured: `536` - // Estimated: `224324` - // Minimum execution time: 54_237 nanoseconds. - Weight::from_parts(55_681_000, 224324) + // Measured: `473` + // Estimated: `227294` + // Minimum execution time: 57_927_000 picoseconds. + Weight::from_parts(58_625_000, 227294) .saturating_add(T::DbWeight::get().reads(4_u64)) .saturating_add(T::DbWeight::get().writes(4_u64)) } @@ -176,10 +178,10 @@ impl WeightInfo for SubstrateWeight { /// Proof: Referenda ReferendumInfoFor (max_values: None, max_size: Some(366), added: 2841, mode: MaxEncodedLen) fn refund_decision_deposit() -> Weight { // Proof Size summary in bytes: - // Measured: `415` - // Estimated: `2841` - // Minimum execution time: 25_734 nanoseconds. - Weight::from_parts(26_429_000, 2841) + // Measured: `351` + // Estimated: `3831` + // Minimum execution time: 27_186_000 picoseconds. + Weight::from_parts(27_551_000, 3831) .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -187,10 +189,10 @@ impl WeightInfo for SubstrateWeight { /// Proof: Referenda ReferendumInfoFor (max_values: None, max_size: Some(366), added: 2841, mode: MaxEncodedLen) fn refund_submission_deposit() -> Weight { // Proof Size summary in bytes: - // Measured: `405` - // Estimated: `2841` - // Minimum execution time: 26_000 nanoseconds. - Weight::from_parts(26_786_000, 2841) + // Measured: `341` + // Estimated: `3831` + // Minimum execution time: 27_339_000 picoseconds. + Weight::from_parts(27_828_000, 3831) .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -200,10 +202,10 @@ impl WeightInfo for SubstrateWeight { /// Proof: Scheduler Agenda (max_values: None, max_size: Some(107022), added: 109497, mode: MaxEncodedLen) fn cancel() -> Weight { // Proof Size summary in bytes: - // Measured: `412` - // Estimated: `221835` - // Minimum execution time: 34_567 nanoseconds. - Weight::from_parts(35_939_000, 221835) + // Measured: `381` + // Estimated: `223815` + // Minimum execution time: 37_081_000 picoseconds. + Weight::from_parts(38_110_000, 223815) .saturating_add(T::DbWeight::get().reads(3_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) } @@ -215,10 +217,10 @@ impl WeightInfo for SubstrateWeight { /// Proof: Referenda MetadataOf (max_values: None, max_size: Some(52), added: 2527, mode: MaxEncodedLen) fn kill() -> Weight { // Proof Size summary in bytes: - // Measured: `717` - // Estimated: `224362` - // Minimum execution time: 67_744 nanoseconds. - Weight::from_parts(70_047_000, 224362) + // Measured: `622` + // Estimated: `227332` + // Minimum execution time: 70_195_000 picoseconds. + Weight::from_parts(71_451_000, 227332) .saturating_add(T::DbWeight::get().reads(4_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) } @@ -229,9 +231,9 @@ impl WeightInfo for SubstrateWeight { fn one_fewer_deciding_queue_empty() -> Weight { // Proof Size summary in bytes: // Measured: `174` - // Estimated: `6976` - // Minimum execution time: 9_886 nanoseconds. - Weight::from_parts(10_406_000, 6976) + // Estimated: `8956` + // Minimum execution time: 10_748_000 picoseconds. + Weight::from_parts(10_912_000, 8956) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -243,10 +245,10 @@ impl WeightInfo for SubstrateWeight { /// Proof: Scheduler Agenda (max_values: None, max_size: Some(107022), added: 109497, mode: MaxEncodedLen) fn one_fewer_deciding_failing() -> Weight { // Proof Size summary in bytes: - // Measured: `4661` - // Estimated: `226322` - // Minimum execution time: 100_449 nanoseconds. - Weight::from_parts(101_812_000, 226322) + // Measured: `4567` + // Estimated: `229292` + // Minimum execution time: 89_144_000 picoseconds. + Weight::from_parts(90_552_000, 229292) .saturating_add(T::DbWeight::get().reads(4_u64)) .saturating_add(T::DbWeight::get().writes(4_u64)) } @@ -258,10 +260,10 @@ impl WeightInfo for SubstrateWeight { /// Proof: Scheduler Agenda (max_values: None, max_size: Some(107022), added: 109497, mode: MaxEncodedLen) fn one_fewer_deciding_passing() -> Weight { // Proof Size summary in bytes: - // Measured: `4661` - // Estimated: `226322` - // Minimum execution time: 101_430 nanoseconds. - Weight::from_parts(103_704_000, 226322) + // Measured: `4567` + // Estimated: `229292` + // Minimum execution time: 92_164_000 picoseconds. + Weight::from_parts(92_947_000, 229292) .saturating_add(T::DbWeight::get().reads(4_u64)) .saturating_add(T::DbWeight::get().writes(4_u64)) } @@ -273,10 +275,10 @@ impl WeightInfo for SubstrateWeight { /// Proof: Scheduler Agenda (max_values: None, max_size: Some(107022), added: 109497, mode: MaxEncodedLen) fn nudge_referendum_requeued_insertion() -> Weight { // Proof Size summary in bytes: - // Measured: `4682` - // Estimated: `116825` - // Minimum execution time: 67_224 nanoseconds. - Weight::from_parts(70_596_000, 116825) + // Measured: `4588` + // Estimated: `119795` + // Minimum execution time: 59_212_000 picoseconds. + Weight::from_parts(59_843_000, 119795) .saturating_add(T::DbWeight::get().reads(3_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) } @@ -288,10 +290,10 @@ impl WeightInfo for SubstrateWeight { /// Proof: Scheduler Agenda (max_values: None, max_size: Some(107022), added: 109497, mode: MaxEncodedLen) fn nudge_referendum_requeued_slide() -> Weight { // Proof Size summary in bytes: - // Measured: `4668` - // Estimated: `116825` - // Minimum execution time: 65_461 nanoseconds. - Weight::from_parts(69_624_000, 116825) + // Measured: `4574` + // Estimated: `119795` + // Minimum execution time: 59_049_000 picoseconds. + Weight::from_parts(59_760_000, 119795) .saturating_add(T::DbWeight::get().reads(3_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) } @@ -305,10 +307,10 @@ impl WeightInfo for SubstrateWeight { /// Proof: Scheduler Agenda (max_values: None, max_size: Some(107022), added: 109497, mode: MaxEncodedLen) fn nudge_referendum_queued() -> Weight { // Proof Size summary in bytes: - // Measured: `4642` - // Estimated: `119314` - // Minimum execution time: 69_848 nanoseconds. - Weight::from_parts(74_480_000, 119314) + // Measured: `4548` + // Estimated: `123274` + // Minimum execution time: 61_774_000 picoseconds. + Weight::from_parts(62_480_000, 123274) .saturating_add(T::DbWeight::get().reads(4_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) } @@ -322,10 +324,10 @@ impl WeightInfo for SubstrateWeight { /// Proof: Scheduler Agenda (max_values: None, max_size: Some(107022), added: 109497, mode: MaxEncodedLen) fn nudge_referendum_not_queued() -> Weight { // Proof Size summary in bytes: - // Measured: `4676` - // Estimated: `119314` - // Minimum execution time: 70_042 nanoseconds. - Weight::from_parts(72_912_000, 119314) + // Measured: `4582` + // Estimated: `123274` + // Minimum execution time: 61_605_000 picoseconds. + Weight::from_parts(62_385_000, 123274) .saturating_add(T::DbWeight::get().reads(4_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) } @@ -335,10 +337,10 @@ impl WeightInfo for SubstrateWeight { /// Proof: Scheduler Agenda (max_values: None, max_size: Some(107022), added: 109497, mode: MaxEncodedLen) fn nudge_referendum_no_deposit() -> Weight { // Proof Size summary in bytes: - // Measured: `364` - // Estimated: `112338` - // Minimum execution time: 23_008 nanoseconds. - Weight::from_parts(23_767_000, 112338) + // Measured: `333` + // Estimated: `114318` + // Minimum execution time: 24_423_000 picoseconds. + Weight::from_parts(24_747_000, 114318) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().writes(2_u64)) } @@ -348,10 +350,10 @@ impl WeightInfo for SubstrateWeight { /// Proof: Scheduler Agenda (max_values: None, max_size: Some(107022), added: 109497, mode: MaxEncodedLen) fn nudge_referendum_preparing() -> Weight { // Proof Size summary in bytes: - // Measured: `412` - // Estimated: `112338` - // Minimum execution time: 23_550 nanoseconds. - Weight::from_parts(24_081_000, 112338) + // Measured: `381` + // Estimated: `114318` + // Minimum execution time: 24_487_000 picoseconds. + Weight::from_parts(24_941_000, 114318) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().writes(2_u64)) } @@ -359,10 +361,10 @@ impl WeightInfo for SubstrateWeight { /// Proof: Referenda ReferendumInfoFor (max_values: None, max_size: Some(366), added: 2841, mode: MaxEncodedLen) fn nudge_referendum_timed_out() -> Weight { // Proof Size summary in bytes: - // Measured: `310` - // Estimated: `2841` - // Minimum execution time: 15_850 nanoseconds. - Weight::from_parts(16_773_000, 2841) + // Measured: `278` + // Estimated: `3831` + // Minimum execution time: 17_454_000 picoseconds. + Weight::from_parts(17_697_000, 3831) .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -374,10 +376,10 @@ impl WeightInfo for SubstrateWeight { /// Proof: Scheduler Agenda (max_values: None, max_size: Some(107022), added: 109497, mode: MaxEncodedLen) fn nudge_referendum_begin_deciding_failing() -> Weight { // Proof Size summary in bytes: - // Measured: `412` - // Estimated: `114827` - // Minimum execution time: 32_126 nanoseconds. - Weight::from_parts(33_313_000, 114827) + // Measured: `381` + // Estimated: `117797` + // Minimum execution time: 34_721_000 picoseconds. + Weight::from_parts(35_295_000, 117797) .saturating_add(T::DbWeight::get().reads(3_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) } @@ -389,10 +391,10 @@ impl WeightInfo for SubstrateWeight { /// Proof: Scheduler Agenda (max_values: None, max_size: Some(107022), added: 109497, mode: MaxEncodedLen) fn nudge_referendum_begin_deciding_passing() -> Weight { // Proof Size summary in bytes: - // Measured: `412` - // Estimated: `114827` - // Minimum execution time: 34_698 nanoseconds. - Weight::from_parts(35_802_000, 114827) + // Measured: `381` + // Estimated: `117797` + // Minimum execution time: 36_587_000 picoseconds. + Weight::from_parts(37_095_000, 117797) .saturating_add(T::DbWeight::get().reads(3_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) } @@ -402,10 +404,10 @@ impl WeightInfo for SubstrateWeight { /// Proof: Scheduler Agenda (max_values: None, max_size: Some(107022), added: 109497, mode: MaxEncodedLen) fn nudge_referendum_begin_confirming() -> Weight { // Proof Size summary in bytes: - // Measured: `465` - // Estimated: `112338` - // Minimum execution time: 28_710 nanoseconds. - Weight::from_parts(29_574_000, 112338) + // Measured: `434` + // Estimated: `114318` + // Minimum execution time: 30_003_000 picoseconds. + Weight::from_parts(30_541_000, 114318) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().writes(2_u64)) } @@ -415,10 +417,10 @@ impl WeightInfo for SubstrateWeight { /// Proof: Scheduler Agenda (max_values: None, max_size: Some(107022), added: 109497, mode: MaxEncodedLen) fn nudge_referendum_end_confirming() -> Weight { // Proof Size summary in bytes: - // Measured: `448` - // Estimated: `112338` - // Minimum execution time: 29_030 nanoseconds. - Weight::from_parts(30_308_000, 112338) + // Measured: `417` + // Estimated: `114318` + // Minimum execution time: 30_479_000 picoseconds. + Weight::from_parts(30_900_000, 114318) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().writes(2_u64)) } @@ -428,10 +430,10 @@ impl WeightInfo for SubstrateWeight { /// Proof: Scheduler Agenda (max_values: None, max_size: Some(107022), added: 109497, mode: MaxEncodedLen) fn nudge_referendum_continue_not_confirming() -> Weight { // Proof Size summary in bytes: - // Measured: `465` - // Estimated: `112338` - // Minimum execution time: 26_382 nanoseconds. - Weight::from_parts(27_219_000, 112338) + // Measured: `434` + // Estimated: `114318` + // Minimum execution time: 27_657_000 picoseconds. + Weight::from_parts(28_054_000, 114318) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().writes(2_u64)) } @@ -441,10 +443,10 @@ impl WeightInfo for SubstrateWeight { /// Proof: Scheduler Agenda (max_values: None, max_size: Some(107022), added: 109497, mode: MaxEncodedLen) fn nudge_referendum_continue_confirming() -> Weight { // Proof Size summary in bytes: - // Measured: `469` - // Estimated: `112338` - // Minimum execution time: 25_445 nanoseconds. - Weight::from_parts(26_010_000, 112338) + // Measured: `438` + // Estimated: `114318` + // Minimum execution time: 26_713_000 picoseconds. + Weight::from_parts(27_284_000, 114318) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().writes(2_u64)) } @@ -456,10 +458,10 @@ impl WeightInfo for SubstrateWeight { /// Proof: Scheduler Lookup (max_values: None, max_size: Some(48), added: 2523, mode: MaxEncodedLen) fn nudge_referendum_approved() -> Weight { // Proof Size summary in bytes: - // Measured: `469` - // Estimated: `224358` - // Minimum execution time: 41_064 nanoseconds. - Weight::from_parts(42_895_000, 224358) + // Measured: `438` + // Estimated: `227328` + // Minimum execution time: 42_374_000 picoseconds. + Weight::from_parts(43_142_000, 227328) .saturating_add(T::DbWeight::get().reads(4_u64)) .saturating_add(T::DbWeight::get().writes(4_u64)) } @@ -469,10 +471,10 @@ impl WeightInfo for SubstrateWeight { /// Proof: Scheduler Agenda (max_values: None, max_size: Some(107022), added: 109497, mode: MaxEncodedLen) fn nudge_referendum_rejected() -> Weight { // Proof Size summary in bytes: - // Measured: `465` - // Estimated: `112338` - // Minimum execution time: 29_472 nanoseconds. - Weight::from_parts(30_011_000, 112338) + // Measured: `434` + // Estimated: `114318` + // Minimum execution time: 30_213_000 picoseconds. + Weight::from_parts(30_633_000, 114318) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().writes(2_u64)) } @@ -484,10 +486,10 @@ impl WeightInfo for SubstrateWeight { /// Proof: Referenda MetadataOf (max_values: None, max_size: Some(52), added: 2527, mode: MaxEncodedLen) fn set_some_metadata() -> Weight { // Proof Size summary in bytes: - // Measured: `454` - // Estimated: `5407` - // Minimum execution time: 19_389 nanoseconds. - Weight::from_parts(20_490_000, 5407) + // Measured: `422` + // Estimated: `7387` + // Minimum execution time: 20_887_000 picoseconds. + Weight::from_parts(21_242_000, 7387) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -497,10 +499,10 @@ impl WeightInfo for SubstrateWeight { /// Proof: Referenda MetadataOf (max_values: None, max_size: Some(52), added: 2527, mode: MaxEncodedLen) fn clear_metadata() -> Weight { // Proof Size summary in bytes: - // Measured: `387` - // Estimated: `5368` - // Minimum execution time: 18_195 nanoseconds. - Weight::from_parts(19_917_000, 5368) + // Measured: `355` + // Estimated: `7348` + // Minimum execution time: 18_702_000 picoseconds. + Weight::from_parts(18_880_000, 7348) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -516,10 +518,10 @@ impl WeightInfo for () { /// Proof: Referenda ReferendumInfoFor (max_values: None, max_size: Some(366), added: 2841, mode: MaxEncodedLen) fn submit() -> Weight { // Proof Size summary in bytes: - // Measured: `251` - // Estimated: `109996` - // Minimum execution time: 34_540 nanoseconds. - Weight::from_parts(36_144_000, 109996) + // Measured: `220` + // Estimated: `111976` + // Minimum execution time: 36_851_000 picoseconds. + Weight::from_parts(37_183_000, 111976) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) } @@ -529,10 +531,10 @@ impl WeightInfo for () { /// Proof: Scheduler Agenda (max_values: None, max_size: Some(107022), added: 109497, mode: MaxEncodedLen) fn place_decision_deposit_preparing() -> Weight { // Proof Size summary in bytes: - // Measured: `536` - // Estimated: `221835` - // Minimum execution time: 46_963 nanoseconds. - Weight::from_parts(48_459_000, 221835) + // Measured: `473` + // Estimated: `223815` + // Minimum execution time: 49_435_000 picoseconds. + Weight::from_parts(50_056_000, 223815) .saturating_add(RocksDbWeight::get().reads(3_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) } @@ -544,10 +546,10 @@ impl WeightInfo for () { /// Proof: Referenda TrackQueue (max_values: None, max_size: Some(2012), added: 4487, mode: MaxEncodedLen) fn place_decision_deposit_queued() -> Weight { // Proof Size summary in bytes: - // Measured: `3203` - // Estimated: `9817` - // Minimum execution time: 55_798 nanoseconds. - Weight::from_parts(58_260_000, 9817) + // Measured: `3107` + // Estimated: `12787` + // Minimum execution time: 47_061_000 picoseconds. + Weight::from_parts(47_579_000, 12787) .saturating_add(RocksDbWeight::get().reads(3_u64)) .saturating_add(RocksDbWeight::get().writes(2_u64)) } @@ -559,10 +561,10 @@ impl WeightInfo for () { /// Proof: Referenda TrackQueue (max_values: None, max_size: Some(2012), added: 4487, mode: MaxEncodedLen) fn place_decision_deposit_not_queued() -> Weight { // Proof Size summary in bytes: - // Measured: `3223` - // Estimated: `9817` - // Minimum execution time: 53_888 nanoseconds. - Weight::from_parts(57_919_000, 9817) + // Measured: `3127` + // Estimated: `12787` + // Minimum execution time: 46_513_000 picoseconds. + Weight::from_parts(47_212_000, 12787) .saturating_add(RocksDbWeight::get().reads(3_u64)) .saturating_add(RocksDbWeight::get().writes(2_u64)) } @@ -574,10 +576,10 @@ impl WeightInfo for () { /// Proof: Scheduler Agenda (max_values: None, max_size: Some(107022), added: 109497, mode: MaxEncodedLen) fn place_decision_deposit_passing() -> Weight { // Proof Size summary in bytes: - // Measured: `536` - // Estimated: `224324` - // Minimum execution time: 56_121 nanoseconds. - Weight::from_parts(58_301_000, 224324) + // Measured: `473` + // Estimated: `227294` + // Minimum execution time: 59_255_000 picoseconds. + Weight::from_parts(60_114_000, 227294) .saturating_add(RocksDbWeight::get().reads(4_u64)) .saturating_add(RocksDbWeight::get().writes(4_u64)) } @@ -589,10 +591,10 @@ impl WeightInfo for () { /// Proof: Scheduler Agenda (max_values: None, max_size: Some(107022), added: 109497, mode: MaxEncodedLen) fn place_decision_deposit_failing() -> Weight { // Proof Size summary in bytes: - // Measured: `536` - // Estimated: `224324` - // Minimum execution time: 54_237 nanoseconds. - Weight::from_parts(55_681_000, 224324) + // Measured: `473` + // Estimated: `227294` + // Minimum execution time: 57_927_000 picoseconds. + Weight::from_parts(58_625_000, 227294) .saturating_add(RocksDbWeight::get().reads(4_u64)) .saturating_add(RocksDbWeight::get().writes(4_u64)) } @@ -600,10 +602,10 @@ impl WeightInfo for () { /// Proof: Referenda ReferendumInfoFor (max_values: None, max_size: Some(366), added: 2841, mode: MaxEncodedLen) fn refund_decision_deposit() -> Weight { // Proof Size summary in bytes: - // Measured: `415` - // Estimated: `2841` - // Minimum execution time: 25_734 nanoseconds. - Weight::from_parts(26_429_000, 2841) + // Measured: `351` + // Estimated: `3831` + // Minimum execution time: 27_186_000 picoseconds. + Weight::from_parts(27_551_000, 3831) .saturating_add(RocksDbWeight::get().reads(1_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } @@ -611,10 +613,10 @@ impl WeightInfo for () { /// Proof: Referenda ReferendumInfoFor (max_values: None, max_size: Some(366), added: 2841, mode: MaxEncodedLen) fn refund_submission_deposit() -> Weight { // Proof Size summary in bytes: - // Measured: `405` - // Estimated: `2841` - // Minimum execution time: 26_000 nanoseconds. - Weight::from_parts(26_786_000, 2841) + // Measured: `341` + // Estimated: `3831` + // Minimum execution time: 27_339_000 picoseconds. + Weight::from_parts(27_828_000, 3831) .saturating_add(RocksDbWeight::get().reads(1_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } @@ -624,10 +626,10 @@ impl WeightInfo for () { /// Proof: Scheduler Agenda (max_values: None, max_size: Some(107022), added: 109497, mode: MaxEncodedLen) fn cancel() -> Weight { // Proof Size summary in bytes: - // Measured: `412` - // Estimated: `221835` - // Minimum execution time: 34_567 nanoseconds. - Weight::from_parts(35_939_000, 221835) + // Measured: `381` + // Estimated: `223815` + // Minimum execution time: 37_081_000 picoseconds. + Weight::from_parts(38_110_000, 223815) .saturating_add(RocksDbWeight::get().reads(3_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) } @@ -639,10 +641,10 @@ impl WeightInfo for () { /// Proof: Referenda MetadataOf (max_values: None, max_size: Some(52), added: 2527, mode: MaxEncodedLen) fn kill() -> Weight { // Proof Size summary in bytes: - // Measured: `717` - // Estimated: `224362` - // Minimum execution time: 67_744 nanoseconds. - Weight::from_parts(70_047_000, 224362) + // Measured: `622` + // Estimated: `227332` + // Minimum execution time: 70_195_000 picoseconds. + Weight::from_parts(71_451_000, 227332) .saturating_add(RocksDbWeight::get().reads(4_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) } @@ -653,9 +655,9 @@ impl WeightInfo for () { fn one_fewer_deciding_queue_empty() -> Weight { // Proof Size summary in bytes: // Measured: `174` - // Estimated: `6976` - // Minimum execution time: 9_886 nanoseconds. - Weight::from_parts(10_406_000, 6976) + // Estimated: `8956` + // Minimum execution time: 10_748_000 picoseconds. + Weight::from_parts(10_912_000, 8956) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } @@ -667,10 +669,10 @@ impl WeightInfo for () { /// Proof: Scheduler Agenda (max_values: None, max_size: Some(107022), added: 109497, mode: MaxEncodedLen) fn one_fewer_deciding_failing() -> Weight { // Proof Size summary in bytes: - // Measured: `4661` - // Estimated: `226322` - // Minimum execution time: 100_449 nanoseconds. - Weight::from_parts(101_812_000, 226322) + // Measured: `4567` + // Estimated: `229292` + // Minimum execution time: 89_144_000 picoseconds. + Weight::from_parts(90_552_000, 229292) .saturating_add(RocksDbWeight::get().reads(4_u64)) .saturating_add(RocksDbWeight::get().writes(4_u64)) } @@ -682,10 +684,10 @@ impl WeightInfo for () { /// Proof: Scheduler Agenda (max_values: None, max_size: Some(107022), added: 109497, mode: MaxEncodedLen) fn one_fewer_deciding_passing() -> Weight { // Proof Size summary in bytes: - // Measured: `4661` - // Estimated: `226322` - // Minimum execution time: 101_430 nanoseconds. - Weight::from_parts(103_704_000, 226322) + // Measured: `4567` + // Estimated: `229292` + // Minimum execution time: 92_164_000 picoseconds. + Weight::from_parts(92_947_000, 229292) .saturating_add(RocksDbWeight::get().reads(4_u64)) .saturating_add(RocksDbWeight::get().writes(4_u64)) } @@ -697,10 +699,10 @@ impl WeightInfo for () { /// Proof: Scheduler Agenda (max_values: None, max_size: Some(107022), added: 109497, mode: MaxEncodedLen) fn nudge_referendum_requeued_insertion() -> Weight { // Proof Size summary in bytes: - // Measured: `4682` - // Estimated: `116825` - // Minimum execution time: 67_224 nanoseconds. - Weight::from_parts(70_596_000, 116825) + // Measured: `4588` + // Estimated: `119795` + // Minimum execution time: 59_212_000 picoseconds. + Weight::from_parts(59_843_000, 119795) .saturating_add(RocksDbWeight::get().reads(3_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) } @@ -712,10 +714,10 @@ impl WeightInfo for () { /// Proof: Scheduler Agenda (max_values: None, max_size: Some(107022), added: 109497, mode: MaxEncodedLen) fn nudge_referendum_requeued_slide() -> Weight { // Proof Size summary in bytes: - // Measured: `4668` - // Estimated: `116825` - // Minimum execution time: 65_461 nanoseconds. - Weight::from_parts(69_624_000, 116825) + // Measured: `4574` + // Estimated: `119795` + // Minimum execution time: 59_049_000 picoseconds. + Weight::from_parts(59_760_000, 119795) .saturating_add(RocksDbWeight::get().reads(3_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) } @@ -729,10 +731,10 @@ impl WeightInfo for () { /// Proof: Scheduler Agenda (max_values: None, max_size: Some(107022), added: 109497, mode: MaxEncodedLen) fn nudge_referendum_queued() -> Weight { // Proof Size summary in bytes: - // Measured: `4642` - // Estimated: `119314` - // Minimum execution time: 69_848 nanoseconds. - Weight::from_parts(74_480_000, 119314) + // Measured: `4548` + // Estimated: `123274` + // Minimum execution time: 61_774_000 picoseconds. + Weight::from_parts(62_480_000, 123274) .saturating_add(RocksDbWeight::get().reads(4_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) } @@ -746,10 +748,10 @@ impl WeightInfo for () { /// Proof: Scheduler Agenda (max_values: None, max_size: Some(107022), added: 109497, mode: MaxEncodedLen) fn nudge_referendum_not_queued() -> Weight { // Proof Size summary in bytes: - // Measured: `4676` - // Estimated: `119314` - // Minimum execution time: 70_042 nanoseconds. - Weight::from_parts(72_912_000, 119314) + // Measured: `4582` + // Estimated: `123274` + // Minimum execution time: 61_605_000 picoseconds. + Weight::from_parts(62_385_000, 123274) .saturating_add(RocksDbWeight::get().reads(4_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) } @@ -759,10 +761,10 @@ impl WeightInfo for () { /// Proof: Scheduler Agenda (max_values: None, max_size: Some(107022), added: 109497, mode: MaxEncodedLen) fn nudge_referendum_no_deposit() -> Weight { // Proof Size summary in bytes: - // Measured: `364` - // Estimated: `112338` - // Minimum execution time: 23_008 nanoseconds. - Weight::from_parts(23_767_000, 112338) + // Measured: `333` + // Estimated: `114318` + // Minimum execution time: 24_423_000 picoseconds. + Weight::from_parts(24_747_000, 114318) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().writes(2_u64)) } @@ -772,10 +774,10 @@ impl WeightInfo for () { /// Proof: Scheduler Agenda (max_values: None, max_size: Some(107022), added: 109497, mode: MaxEncodedLen) fn nudge_referendum_preparing() -> Weight { // Proof Size summary in bytes: - // Measured: `412` - // Estimated: `112338` - // Minimum execution time: 23_550 nanoseconds. - Weight::from_parts(24_081_000, 112338) + // Measured: `381` + // Estimated: `114318` + // Minimum execution time: 24_487_000 picoseconds. + Weight::from_parts(24_941_000, 114318) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().writes(2_u64)) } @@ -783,10 +785,10 @@ impl WeightInfo for () { /// Proof: Referenda ReferendumInfoFor (max_values: None, max_size: Some(366), added: 2841, mode: MaxEncodedLen) fn nudge_referendum_timed_out() -> Weight { // Proof Size summary in bytes: - // Measured: `310` - // Estimated: `2841` - // Minimum execution time: 15_850 nanoseconds. - Weight::from_parts(16_773_000, 2841) + // Measured: `278` + // Estimated: `3831` + // Minimum execution time: 17_454_000 picoseconds. + Weight::from_parts(17_697_000, 3831) .saturating_add(RocksDbWeight::get().reads(1_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } @@ -798,10 +800,10 @@ impl WeightInfo for () { /// Proof: Scheduler Agenda (max_values: None, max_size: Some(107022), added: 109497, mode: MaxEncodedLen) fn nudge_referendum_begin_deciding_failing() -> Weight { // Proof Size summary in bytes: - // Measured: `412` - // Estimated: `114827` - // Minimum execution time: 32_126 nanoseconds. - Weight::from_parts(33_313_000, 114827) + // Measured: `381` + // Estimated: `117797` + // Minimum execution time: 34_721_000 picoseconds. + Weight::from_parts(35_295_000, 117797) .saturating_add(RocksDbWeight::get().reads(3_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) } @@ -813,10 +815,10 @@ impl WeightInfo for () { /// Proof: Scheduler Agenda (max_values: None, max_size: Some(107022), added: 109497, mode: MaxEncodedLen) fn nudge_referendum_begin_deciding_passing() -> Weight { // Proof Size summary in bytes: - // Measured: `412` - // Estimated: `114827` - // Minimum execution time: 34_698 nanoseconds. - Weight::from_parts(35_802_000, 114827) + // Measured: `381` + // Estimated: `117797` + // Minimum execution time: 36_587_000 picoseconds. + Weight::from_parts(37_095_000, 117797) .saturating_add(RocksDbWeight::get().reads(3_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) } @@ -826,10 +828,10 @@ impl WeightInfo for () { /// Proof: Scheduler Agenda (max_values: None, max_size: Some(107022), added: 109497, mode: MaxEncodedLen) fn nudge_referendum_begin_confirming() -> Weight { // Proof Size summary in bytes: - // Measured: `465` - // Estimated: `112338` - // Minimum execution time: 28_710 nanoseconds. - Weight::from_parts(29_574_000, 112338) + // Measured: `434` + // Estimated: `114318` + // Minimum execution time: 30_003_000 picoseconds. + Weight::from_parts(30_541_000, 114318) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().writes(2_u64)) } @@ -839,10 +841,10 @@ impl WeightInfo for () { /// Proof: Scheduler Agenda (max_values: None, max_size: Some(107022), added: 109497, mode: MaxEncodedLen) fn nudge_referendum_end_confirming() -> Weight { // Proof Size summary in bytes: - // Measured: `448` - // Estimated: `112338` - // Minimum execution time: 29_030 nanoseconds. - Weight::from_parts(30_308_000, 112338) + // Measured: `417` + // Estimated: `114318` + // Minimum execution time: 30_479_000 picoseconds. + Weight::from_parts(30_900_000, 114318) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().writes(2_u64)) } @@ -852,10 +854,10 @@ impl WeightInfo for () { /// Proof: Scheduler Agenda (max_values: None, max_size: Some(107022), added: 109497, mode: MaxEncodedLen) fn nudge_referendum_continue_not_confirming() -> Weight { // Proof Size summary in bytes: - // Measured: `465` - // Estimated: `112338` - // Minimum execution time: 26_382 nanoseconds. - Weight::from_parts(27_219_000, 112338) + // Measured: `434` + // Estimated: `114318` + // Minimum execution time: 27_657_000 picoseconds. + Weight::from_parts(28_054_000, 114318) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().writes(2_u64)) } @@ -865,10 +867,10 @@ impl WeightInfo for () { /// Proof: Scheduler Agenda (max_values: None, max_size: Some(107022), added: 109497, mode: MaxEncodedLen) fn nudge_referendum_continue_confirming() -> Weight { // Proof Size summary in bytes: - // Measured: `469` - // Estimated: `112338` - // Minimum execution time: 25_445 nanoseconds. - Weight::from_parts(26_010_000, 112338) + // Measured: `438` + // Estimated: `114318` + // Minimum execution time: 26_713_000 picoseconds. + Weight::from_parts(27_284_000, 114318) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().writes(2_u64)) } @@ -880,10 +882,10 @@ impl WeightInfo for () { /// Proof: Scheduler Lookup (max_values: None, max_size: Some(48), added: 2523, mode: MaxEncodedLen) fn nudge_referendum_approved() -> Weight { // Proof Size summary in bytes: - // Measured: `469` - // Estimated: `224358` - // Minimum execution time: 41_064 nanoseconds. - Weight::from_parts(42_895_000, 224358) + // Measured: `438` + // Estimated: `227328` + // Minimum execution time: 42_374_000 picoseconds. + Weight::from_parts(43_142_000, 227328) .saturating_add(RocksDbWeight::get().reads(4_u64)) .saturating_add(RocksDbWeight::get().writes(4_u64)) } @@ -893,10 +895,10 @@ impl WeightInfo for () { /// Proof: Scheduler Agenda (max_values: None, max_size: Some(107022), added: 109497, mode: MaxEncodedLen) fn nudge_referendum_rejected() -> Weight { // Proof Size summary in bytes: - // Measured: `465` - // Estimated: `112338` - // Minimum execution time: 29_472 nanoseconds. - Weight::from_parts(30_011_000, 112338) + // Measured: `434` + // Estimated: `114318` + // Minimum execution time: 30_213_000 picoseconds. + Weight::from_parts(30_633_000, 114318) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().writes(2_u64)) } @@ -908,10 +910,10 @@ impl WeightInfo for () { /// Proof: Referenda MetadataOf (max_values: None, max_size: Some(52), added: 2527, mode: MaxEncodedLen) fn set_some_metadata() -> Weight { // Proof Size summary in bytes: - // Measured: `454` - // Estimated: `5407` - // Minimum execution time: 19_389 nanoseconds. - Weight::from_parts(20_490_000, 5407) + // Measured: `422` + // Estimated: `7387` + // Minimum execution time: 20_887_000 picoseconds. + Weight::from_parts(21_242_000, 7387) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } @@ -921,10 +923,10 @@ impl WeightInfo for () { /// Proof: Referenda MetadataOf (max_values: None, max_size: Some(52), added: 2527, mode: MaxEncodedLen) fn clear_metadata() -> Weight { // Proof Size summary in bytes: - // Measured: `387` - // Estimated: `5368` - // Minimum execution time: 18_195 nanoseconds. - Weight::from_parts(19_917_000, 5368) + // Measured: `355` + // Estimated: `7348` + // Minimum execution time: 18_702_000 picoseconds. + Weight::from_parts(18_880_000, 7348) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } diff --git a/frame/remark/src/weights.rs b/frame/remark/src/weights.rs index f9fcb7380..bb2e91b5d 100644 --- a/frame/remark/src/weights.rs +++ b/frame/remark/src/weights.rs @@ -18,9 +18,9 @@ //! Autogenerated weights for pallet_remark //! //! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev -//! DATE: 2023-01-24, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! DATE: 2023-03-16, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` //! WORST CASE MAP SIZE: `1000000` -//! HOSTNAME: `bm2`, CPU: `Intel(R) Core(TM) i7-7700K CPU @ 4.20GHz` +//! HOSTNAME: `bm3`, CPU: `Intel(R) Core(TM) i7-7700K CPU @ 4.20GHz` //! EXECUTION: Some(Wasm), WASM-EXECUTION: Compiled, CHAIN: Some("dev"), DB CACHE: 1024 // Executed Command: @@ -31,6 +31,9 @@ // --steps=50 // --repeat=20 // --pallet=pallet_remark +// --no-storage-info +// --no-median-slopes +// --no-min-squares // --extrinsic=* // --execution=wasm // --wasm-execution=compiled @@ -59,10 +62,10 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 8_404 nanoseconds. - Weight::from_parts(343_031, 0) - // Standard Error: 1 - .saturating_add(Weight::from_parts(1_404, 0).saturating_mul(l.into())) + // Minimum execution time: 9_284_000 picoseconds. + Weight::from_parts(4_054_843, 0) + // Standard Error: 2 + .saturating_add(Weight::from_parts(1_094, 0).saturating_mul(l.into())) } } @@ -73,9 +76,9 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 8_404 nanoseconds. - Weight::from_parts(343_031, 0) - // Standard Error: 1 - .saturating_add(Weight::from_parts(1_404, 0).saturating_mul(l.into())) + // Minimum execution time: 9_284_000 picoseconds. + Weight::from_parts(4_054_843, 0) + // Standard Error: 2 + .saturating_add(Weight::from_parts(1_094, 0).saturating_mul(l.into())) } } diff --git a/frame/salary/src/weights.rs b/frame/salary/src/weights.rs index 6b43c58b6..7195a144a 100644 --- a/frame/salary/src/weights.rs +++ b/frame/salary/src/weights.rs @@ -18,26 +18,29 @@ //! Autogenerated weights for pallet_salary //! //! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev -//! DATE: 2023-03-03, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! DATE: 2023-03-16, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` //! WORST CASE MAP SIZE: `1000000` -//! HOSTNAME: `bm2`, CPU: `Intel(R) Core(TM) i7-7700K CPU @ 4.20GHz` +//! HOSTNAME: `bm3`, CPU: `Intel(R) Core(TM) i7-7700K CPU @ 4.20GHz` //! EXECUTION: Some(Wasm), WASM-EXECUTION: Compiled, CHAIN: Some("dev"), DB CACHE: 1024 // Executed Command: // ./target/production/substrate // benchmark // pallet +// --chain=dev // --steps=50 // --repeat=20 -// --extrinsic=* // --pallet=pallet_salary +// --no-storage-info +// --no-median-slopes +// --no-min-squares +// --extrinsic=* // --execution=wasm // --wasm-execution=compiled // --heap-pages=4096 -// --chain=dev -// --header=HEADER-APACHE2 -// --output=frame/salary/src/weights.rs -// --template=.maintain/frame-weight-template.hbs +// --output=./frame/salary/src/weights.rs +// --header=./HEADER-APACHE2 +// --template=./.maintain/frame-weight-template.hbs #![cfg_attr(rustfmt, rustfmt_skip)] #![allow(unused_parens)] @@ -66,8 +69,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `4` // Estimated: `1541` - // Minimum execution time: 11_793_000 picoseconds. - Weight::from_parts(12_080_000, 1541) + // Minimum execution time: 11_732_000 picoseconds. + Weight::from_parts(12_162_000, 1541) .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -75,10 +78,10 @@ impl WeightInfo for SubstrateWeight { /// Proof: Salary Status (max_values: Some(1), max_size: Some(56), added: 551, mode: MaxEncodedLen) fn bump() -> Weight { // Proof Size summary in bytes: - // Measured: `118` + // Measured: `86` // Estimated: `1541` - // Minimum execution time: 12_799_000 picoseconds. - Weight::from_parts(13_343_000, 1541) + // Minimum execution time: 12_966_000 picoseconds. + Weight::from_parts(13_443_000, 1541) .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -90,10 +93,10 @@ impl WeightInfo for SubstrateWeight { /// Proof: Salary Claimant (max_values: None, max_size: Some(78), added: 2553, mode: MaxEncodedLen) fn induct() -> Weight { // Proof Size summary in bytes: - // Measured: `394` + // Measured: `362` // Estimated: `8591` - // Minimum execution time: 20_299_000 picoseconds. - Weight::from_parts(20_664_000, 8591) + // Minimum execution time: 20_467_000 picoseconds. + Weight::from_parts(20_782_000, 8591) .saturating_add(T::DbWeight::get().reads(3_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -105,10 +108,10 @@ impl WeightInfo for SubstrateWeight { /// Proof: Salary Claimant (max_values: None, max_size: Some(78), added: 2553, mode: MaxEncodedLen) fn register() -> Weight { // Proof Size summary in bytes: - // Measured: `461` + // Measured: `429` // Estimated: `8591` - // Minimum execution time: 24_195_000 picoseconds. - Weight::from_parts(24_528_000, 8591) + // Minimum execution time: 23_547_000 picoseconds. + Weight::from_parts(23_911_000, 8591) .saturating_add(T::DbWeight::get().reads(3_u64)) .saturating_add(T::DbWeight::get().writes(2_u64)) } @@ -120,10 +123,10 @@ impl WeightInfo for SubstrateWeight { /// Proof: RankedCollective Members (max_values: None, max_size: Some(42), added: 2517, mode: MaxEncodedLen) fn payout() -> Weight { // Proof Size summary in bytes: - // Measured: `461` + // Measured: `429` // Estimated: `8591` - // Minimum execution time: 47_609_000 picoseconds. - Weight::from_parts(48_462_000, 8591) + // Minimum execution time: 47_145_000 picoseconds. + Weight::from_parts(47_778_000, 8591) .saturating_add(T::DbWeight::get().reads(3_u64)) .saturating_add(T::DbWeight::get().writes(2_u64)) } @@ -137,10 +140,10 @@ impl WeightInfo for SubstrateWeight { /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) fn payout_other() -> Weight { // Proof Size summary in bytes: - // Measured: `461` + // Measured: `429` // Estimated: `12184` - // Minimum execution time: 48_513_000 picoseconds. - Weight::from_parts(49_053_000, 12184) + // Minimum execution time: 48_763_000 picoseconds. + Weight::from_parts(49_853_000, 12184) .saturating_add(T::DbWeight::get().reads(4_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) } @@ -150,10 +153,10 @@ impl WeightInfo for SubstrateWeight { /// Proof: Salary Claimant (max_values: None, max_size: Some(78), added: 2553, mode: MaxEncodedLen) fn check_payment() -> Weight { // Proof Size summary in bytes: - // Measured: `202` + // Measured: `170` // Estimated: `5084` - // Minimum execution time: 12_663_000 picoseconds. - Weight::from_parts(12_858_000, 5084) + // Minimum execution time: 12_825_000 picoseconds. + Weight::from_parts(13_143_000, 5084) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().writes(2_u64)) } @@ -167,8 +170,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `4` // Estimated: `1541` - // Minimum execution time: 11_793_000 picoseconds. - Weight::from_parts(12_080_000, 1541) + // Minimum execution time: 11_732_000 picoseconds. + Weight::from_parts(12_162_000, 1541) .saturating_add(RocksDbWeight::get().reads(1_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } @@ -176,10 +179,10 @@ impl WeightInfo for () { /// Proof: Salary Status (max_values: Some(1), max_size: Some(56), added: 551, mode: MaxEncodedLen) fn bump() -> Weight { // Proof Size summary in bytes: - // Measured: `118` + // Measured: `86` // Estimated: `1541` - // Minimum execution time: 12_799_000 picoseconds. - Weight::from_parts(13_343_000, 1541) + // Minimum execution time: 12_966_000 picoseconds. + Weight::from_parts(13_443_000, 1541) .saturating_add(RocksDbWeight::get().reads(1_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } @@ -191,10 +194,10 @@ impl WeightInfo for () { /// Proof: Salary Claimant (max_values: None, max_size: Some(78), added: 2553, mode: MaxEncodedLen) fn induct() -> Weight { // Proof Size summary in bytes: - // Measured: `394` + // Measured: `362` // Estimated: `8591` - // Minimum execution time: 20_299_000 picoseconds. - Weight::from_parts(20_664_000, 8591) + // Minimum execution time: 20_467_000 picoseconds. + Weight::from_parts(20_782_000, 8591) .saturating_add(RocksDbWeight::get().reads(3_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } @@ -206,10 +209,10 @@ impl WeightInfo for () { /// Proof: Salary Claimant (max_values: None, max_size: Some(78), added: 2553, mode: MaxEncodedLen) fn register() -> Weight { // Proof Size summary in bytes: - // Measured: `461` + // Measured: `429` // Estimated: `8591` - // Minimum execution time: 24_195_000 picoseconds. - Weight::from_parts(24_528_000, 8591) + // Minimum execution time: 23_547_000 picoseconds. + Weight::from_parts(23_911_000, 8591) .saturating_add(RocksDbWeight::get().reads(3_u64)) .saturating_add(RocksDbWeight::get().writes(2_u64)) } @@ -221,10 +224,10 @@ impl WeightInfo for () { /// Proof: RankedCollective Members (max_values: None, max_size: Some(42), added: 2517, mode: MaxEncodedLen) fn payout() -> Weight { // Proof Size summary in bytes: - // Measured: `461` + // Measured: `429` // Estimated: `8591` - // Minimum execution time: 47_609_000 picoseconds. - Weight::from_parts(48_462_000, 8591) + // Minimum execution time: 47_145_000 picoseconds. + Weight::from_parts(47_778_000, 8591) .saturating_add(RocksDbWeight::get().reads(3_u64)) .saturating_add(RocksDbWeight::get().writes(2_u64)) } @@ -238,10 +241,10 @@ impl WeightInfo for () { /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) fn payout_other() -> Weight { // Proof Size summary in bytes: - // Measured: `461` + // Measured: `429` // Estimated: `12184` - // Minimum execution time: 48_513_000 picoseconds. - Weight::from_parts(49_053_000, 12184) + // Minimum execution time: 48_763_000 picoseconds. + Weight::from_parts(49_853_000, 12184) .saturating_add(RocksDbWeight::get().reads(4_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) } @@ -251,10 +254,10 @@ impl WeightInfo for () { /// Proof: Salary Claimant (max_values: None, max_size: Some(78), added: 2553, mode: MaxEncodedLen) fn check_payment() -> Weight { // Proof Size summary in bytes: - // Measured: `202` + // Measured: `170` // Estimated: `5084` - // Minimum execution time: 12_663_000 picoseconds. - Weight::from_parts(12_858_000, 5084) + // Minimum execution time: 12_825_000 picoseconds. + Weight::from_parts(13_143_000, 5084) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().writes(2_u64)) } diff --git a/frame/scheduler/src/weights.rs b/frame/scheduler/src/weights.rs index 577e5b0bc..bccb50265 100644 --- a/frame/scheduler/src/weights.rs +++ b/frame/scheduler/src/weights.rs @@ -18,9 +18,9 @@ //! Autogenerated weights for pallet_scheduler //! //! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev -//! DATE: 2023-01-24, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! DATE: 2023-03-16, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` //! WORST CASE MAP SIZE: `1000000` -//! HOSTNAME: `bm2`, CPU: `Intel(R) Core(TM) i7-7700K CPU @ 4.20GHz` +//! HOSTNAME: `bm3`, CPU: `Intel(R) Core(TM) i7-7700K CPU @ 4.20GHz` //! EXECUTION: Some(Wasm), WASM-EXECUTION: Compiled, CHAIN: Some("dev"), DB CACHE: 1024 // Executed Command: @@ -31,6 +31,9 @@ // --steps=50 // --repeat=20 // --pallet=pallet_scheduler +// --no-storage-info +// --no-median-slopes +// --no-min-squares // --extrinsic=* // --execution=wasm // --wasm-execution=compiled @@ -69,10 +72,10 @@ impl WeightInfo for SubstrateWeight { /// Proof: Scheduler IncompleteSince (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) fn service_agendas_base() -> Weight { // Proof Size summary in bytes: - // Measured: `30` - // Estimated: `499` - // Minimum execution time: 3_670 nanoseconds. - Weight::from_parts(3_838_000, 499) + // Measured: `31` + // Estimated: `1489` + // Minimum execution time: 3_965_000 picoseconds. + Weight::from_parts(4_126_000, 1489) .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -81,12 +84,12 @@ impl WeightInfo for SubstrateWeight { /// The range of component `s` is `[0, 512]`. fn service_agenda_base(s: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `112 + s * (177 ±0)` - // Estimated: `109497` - // Minimum execution time: 3_079 nanoseconds. - Weight::from_parts(7_087_647, 109497) - // Standard Error: 658 - .saturating_add(Weight::from_parts(279_320, 0).saturating_mul(s.into())) + // Measured: `81 + s * (177 ±0)` + // Estimated: `110487` + // Minimum execution time: 3_662_000 picoseconds. + Weight::from_parts(8_746_597, 110487) + // Standard Error: 754 + .saturating_add(Weight::from_parts(309_350, 0).saturating_mul(s.into())) .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -94,8 +97,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 5_192 nanoseconds. - Weight::from_parts(5_528_000, 0) + // Minimum execution time: 5_807_000 picoseconds. + Weight::from_parts(5_963_000, 0) } /// Storage: Preimage PreimageFor (r:1 w:1) /// Proof: Preimage PreimageFor (max_values: None, max_size: Some(4194344), added: 4196819, mode: Measured) @@ -104,12 +107,12 @@ impl WeightInfo for SubstrateWeight { /// The range of component `s` is `[128, 4194304]`. fn service_task_fetched(s: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `211 + s * (1 ±0)` - // Estimated: `5252 + s * (1 ±0)` - // Minimum execution time: 17_284 nanoseconds. - Weight::from_parts(17_574_000, 5252) - // Standard Error: 0 - .saturating_add(Weight::from_parts(1_126, 0).saturating_mul(s.into())) + // Measured: `179 + s * (1 ±0)` + // Estimated: `7200 + s * (1 ±0)` + // Minimum execution time: 20_188_000 picoseconds. + Weight::from_parts(20_422_000, 7200) + // Standard Error: 1 + .saturating_add(Weight::from_parts(1_143, 0).saturating_mul(s.into())) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().writes(2_u64)) .saturating_add(Weight::from_parts(0, 1).saturating_mul(s.into())) @@ -120,42 +123,42 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 7_020 nanoseconds. - Weight::from_parts(7_262_000, 0) + // Minimum execution time: 7_787_000 picoseconds. + Weight::from_parts(8_046_000, 0) .saturating_add(T::DbWeight::get().writes(1_u64)) } fn service_task_periodic() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 5_187 nanoseconds. - Weight::from_parts(5_368_000, 0) + // Minimum execution time: 5_730_000 picoseconds. + Weight::from_parts(5_901_000, 0) } fn execute_dispatch_signed() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 2_313 nanoseconds. - Weight::from_parts(2_404_000, 0) + // Minimum execution time: 2_569_000 picoseconds. + Weight::from_parts(2_710_000, 0) } fn execute_dispatch_unsigned() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 2_187 nanoseconds. - Weight::from_parts(2_362_000, 0) + // Minimum execution time: 2_611_000 picoseconds. + Weight::from_parts(2_765_000, 0) } /// Storage: Scheduler Agenda (r:1 w:1) /// Proof: Scheduler Agenda (max_values: None, max_size: Some(107022), added: 109497, mode: MaxEncodedLen) /// The range of component `s` is `[0, 511]`. fn schedule(s: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `112 + s * (177 ±0)` - // Estimated: `109497` - // Minimum execution time: 11_971 nanoseconds. - Weight::from_parts(16_060_361, 109497) - // Standard Error: 665 - .saturating_add(Weight::from_parts(286_324, 0).saturating_mul(s.into())) + // Measured: `81 + s * (177 ±0)` + // Estimated: `110487` + // Minimum execution time: 14_237_000 picoseconds. + Weight::from_parts(18_553_929, 110487) + // Standard Error: 757 + .saturating_add(Weight::from_parts(327_347, 0).saturating_mul(s.into())) .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -166,12 +169,12 @@ impl WeightInfo for SubstrateWeight { /// The range of component `s` is `[1, 512]`. fn cancel(s: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `112 + s * (177 ±0)` - // Estimated: `109497` - // Minimum execution time: 15_594 nanoseconds. - Weight::from_parts(17_191_501, 109497) - // Standard Error: 626 - .saturating_add(Weight::from_parts(425_572, 0).saturating_mul(s.into())) + // Measured: `81 + s * (177 ±0)` + // Estimated: `110487` + // Minimum execution time: 18_003_000 picoseconds. + Weight::from_parts(19_521_283, 110487) + // Standard Error: 901 + .saturating_add(Weight::from_parts(496_401, 0).saturating_mul(s.into())) .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(T::DbWeight::get().writes(2_u64)) } @@ -182,12 +185,12 @@ impl WeightInfo for SubstrateWeight { /// The range of component `s` is `[0, 511]`. fn schedule_named(s: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `627 + s * (178 ±0)` - // Estimated: `112020` - // Minimum execution time: 15_127 nanoseconds. - Weight::from_parts(20_932_642, 112020) - // Standard Error: 692 - .saturating_add(Weight::from_parts(288_344, 0).saturating_mul(s.into())) + // Measured: `596 + s * (178 ±0)` + // Estimated: `114000` + // Minimum execution time: 17_713_000 picoseconds. + Weight::from_parts(24_442_945, 114000) + // Standard Error: 764 + .saturating_add(Weight::from_parts(330_307, 0).saturating_mul(s.into())) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().writes(2_u64)) } @@ -198,12 +201,12 @@ impl WeightInfo for SubstrateWeight { /// The range of component `s` is `[1, 512]`. fn cancel_named(s: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `740 + s * (177 ±0)` - // Estimated: `112020` - // Minimum execution time: 16_859 nanoseconds. - Weight::from_parts(19_736_937, 112020) - // Standard Error: 676 - .saturating_add(Weight::from_parts(429_770, 0).saturating_mul(s.into())) + // Measured: `709 + s * (177 ±0)` + // Estimated: `114000` + // Minimum execution time: 19_808_000 picoseconds. + Weight::from_parts(22_601_896, 114000) + // Standard Error: 991 + .saturating_add(Weight::from_parts(496_702, 0).saturating_mul(s.into())) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().writes(2_u64)) } @@ -215,10 +218,10 @@ impl WeightInfo for () { /// Proof: Scheduler IncompleteSince (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) fn service_agendas_base() -> Weight { // Proof Size summary in bytes: - // Measured: `30` - // Estimated: `499` - // Minimum execution time: 3_670 nanoseconds. - Weight::from_parts(3_838_000, 499) + // Measured: `31` + // Estimated: `1489` + // Minimum execution time: 3_965_000 picoseconds. + Weight::from_parts(4_126_000, 1489) .saturating_add(RocksDbWeight::get().reads(1_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } @@ -227,12 +230,12 @@ impl WeightInfo for () { /// The range of component `s` is `[0, 512]`. fn service_agenda_base(s: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `112 + s * (177 ±0)` - // Estimated: `109497` - // Minimum execution time: 3_079 nanoseconds. - Weight::from_parts(7_087_647, 109497) - // Standard Error: 658 - .saturating_add(Weight::from_parts(279_320, 0).saturating_mul(s.into())) + // Measured: `81 + s * (177 ±0)` + // Estimated: `110487` + // Minimum execution time: 3_662_000 picoseconds. + Weight::from_parts(8_746_597, 110487) + // Standard Error: 754 + .saturating_add(Weight::from_parts(309_350, 0).saturating_mul(s.into())) .saturating_add(RocksDbWeight::get().reads(1_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } @@ -240,8 +243,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 5_192 nanoseconds. - Weight::from_parts(5_528_000, 0) + // Minimum execution time: 5_807_000 picoseconds. + Weight::from_parts(5_963_000, 0) } /// Storage: Preimage PreimageFor (r:1 w:1) /// Proof: Preimage PreimageFor (max_values: None, max_size: Some(4194344), added: 4196819, mode: Measured) @@ -250,12 +253,12 @@ impl WeightInfo for () { /// The range of component `s` is `[128, 4194304]`. fn service_task_fetched(s: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `211 + s * (1 ±0)` - // Estimated: `5252 + s * (1 ±0)` - // Minimum execution time: 17_284 nanoseconds. - Weight::from_parts(17_574_000, 5252) - // Standard Error: 0 - .saturating_add(Weight::from_parts(1_126, 0).saturating_mul(s.into())) + // Measured: `179 + s * (1 ±0)` + // Estimated: `7200 + s * (1 ±0)` + // Minimum execution time: 20_188_000 picoseconds. + Weight::from_parts(20_422_000, 7200) + // Standard Error: 1 + .saturating_add(Weight::from_parts(1_143, 0).saturating_mul(s.into())) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().writes(2_u64)) .saturating_add(Weight::from_parts(0, 1).saturating_mul(s.into())) @@ -266,42 +269,42 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 7_020 nanoseconds. - Weight::from_parts(7_262_000, 0) + // Minimum execution time: 7_787_000 picoseconds. + Weight::from_parts(8_046_000, 0) .saturating_add(RocksDbWeight::get().writes(1_u64)) } fn service_task_periodic() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 5_187 nanoseconds. - Weight::from_parts(5_368_000, 0) + // Minimum execution time: 5_730_000 picoseconds. + Weight::from_parts(5_901_000, 0) } fn execute_dispatch_signed() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 2_313 nanoseconds. - Weight::from_parts(2_404_000, 0) + // Minimum execution time: 2_569_000 picoseconds. + Weight::from_parts(2_710_000, 0) } fn execute_dispatch_unsigned() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 2_187 nanoseconds. - Weight::from_parts(2_362_000, 0) + // Minimum execution time: 2_611_000 picoseconds. + Weight::from_parts(2_765_000, 0) } /// Storage: Scheduler Agenda (r:1 w:1) /// Proof: Scheduler Agenda (max_values: None, max_size: Some(107022), added: 109497, mode: MaxEncodedLen) /// The range of component `s` is `[0, 511]`. fn schedule(s: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `112 + s * (177 ±0)` - // Estimated: `109497` - // Minimum execution time: 11_971 nanoseconds. - Weight::from_parts(16_060_361, 109497) - // Standard Error: 665 - .saturating_add(Weight::from_parts(286_324, 0).saturating_mul(s.into())) + // Measured: `81 + s * (177 ±0)` + // Estimated: `110487` + // Minimum execution time: 14_237_000 picoseconds. + Weight::from_parts(18_553_929, 110487) + // Standard Error: 757 + .saturating_add(Weight::from_parts(327_347, 0).saturating_mul(s.into())) .saturating_add(RocksDbWeight::get().reads(1_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } @@ -312,12 +315,12 @@ impl WeightInfo for () { /// The range of component `s` is `[1, 512]`. fn cancel(s: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `112 + s * (177 ±0)` - // Estimated: `109497` - // Minimum execution time: 15_594 nanoseconds. - Weight::from_parts(17_191_501, 109497) - // Standard Error: 626 - .saturating_add(Weight::from_parts(425_572, 0).saturating_mul(s.into())) + // Measured: `81 + s * (177 ±0)` + // Estimated: `110487` + // Minimum execution time: 18_003_000 picoseconds. + Weight::from_parts(19_521_283, 110487) + // Standard Error: 901 + .saturating_add(Weight::from_parts(496_401, 0).saturating_mul(s.into())) .saturating_add(RocksDbWeight::get().reads(1_u64)) .saturating_add(RocksDbWeight::get().writes(2_u64)) } @@ -328,12 +331,12 @@ impl WeightInfo for () { /// The range of component `s` is `[0, 511]`. fn schedule_named(s: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `627 + s * (178 ±0)` - // Estimated: `112020` - // Minimum execution time: 15_127 nanoseconds. - Weight::from_parts(20_932_642, 112020) - // Standard Error: 692 - .saturating_add(Weight::from_parts(288_344, 0).saturating_mul(s.into())) + // Measured: `596 + s * (178 ±0)` + // Estimated: `114000` + // Minimum execution time: 17_713_000 picoseconds. + Weight::from_parts(24_442_945, 114000) + // Standard Error: 764 + .saturating_add(Weight::from_parts(330_307, 0).saturating_mul(s.into())) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().writes(2_u64)) } @@ -344,12 +347,12 @@ impl WeightInfo for () { /// The range of component `s` is `[1, 512]`. fn cancel_named(s: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `740 + s * (177 ±0)` - // Estimated: `112020` - // Minimum execution time: 16_859 nanoseconds. - Weight::from_parts(19_736_937, 112020) - // Standard Error: 676 - .saturating_add(Weight::from_parts(429_770, 0).saturating_mul(s.into())) + // Measured: `709 + s * (177 ±0)` + // Estimated: `114000` + // Minimum execution time: 19_808_000 picoseconds. + Weight::from_parts(22_601_896, 114000) + // Standard Error: 991 + .saturating_add(Weight::from_parts(496_702, 0).saturating_mul(s.into())) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().writes(2_u64)) } diff --git a/frame/session/src/weights.rs b/frame/session/src/weights.rs index c01799ce5..3515ff446 100644 --- a/frame/session/src/weights.rs +++ b/frame/session/src/weights.rs @@ -18,9 +18,9 @@ //! Autogenerated weights for pallet_session //! //! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev -//! DATE: 2023-01-24, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! DATE: 2023-03-16, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` //! WORST CASE MAP SIZE: `1000000` -//! HOSTNAME: `bm2`, CPU: `Intel(R) Core(TM) i7-7700K CPU @ 4.20GHz` +//! HOSTNAME: `bm3`, CPU: `Intel(R) Core(TM) i7-7700K CPU @ 4.20GHz` //! EXECUTION: Some(Wasm), WASM-EXECUTION: Compiled, CHAIN: Some("dev"), DB CACHE: 1024 // Executed Command: @@ -31,6 +31,9 @@ // --steps=50 // --repeat=20 // --pallet=pallet_session +// --no-storage-info +// --no-median-slopes +// --no-min-squares // --extrinsic=* // --execution=wasm // --wasm-execution=compiled @@ -63,10 +66,10 @@ impl WeightInfo for SubstrateWeight { /// Proof Skipped: Session KeyOwner (max_values: None, max_size: None, mode: Measured) fn set_keys() -> Weight { // Proof Size summary in bytes: - // Measured: `1955` - // Estimated: `19851` - // Minimum execution time: 40_867 nanoseconds. - Weight::from_parts(41_319_000, 19851) + // Measured: `1891` + // Estimated: `22693` + // Minimum execution time: 48_165_000 picoseconds. + Weight::from_parts(48_967_000, 22693) .saturating_add(T::DbWeight::get().reads(6_u64)) .saturating_add(T::DbWeight::get().writes(5_u64)) } @@ -78,10 +81,10 @@ impl WeightInfo for SubstrateWeight { /// Proof Skipped: Session KeyOwner (max_values: None, max_size: None, mode: Measured) fn purge_keys() -> Weight { // Proof Size summary in bytes: - // Measured: `1854` - // Estimated: `9749` - // Minimum execution time: 30_286 nanoseconds. - Weight::from_parts(30_620_000, 9749) + // Measured: `1758` + // Estimated: `11537` + // Minimum execution time: 35_824_000 picoseconds. + Weight::from_parts(36_267_000, 11537) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().writes(5_u64)) } @@ -97,10 +100,10 @@ impl WeightInfo for () { /// Proof Skipped: Session KeyOwner (max_values: None, max_size: None, mode: Measured) fn set_keys() -> Weight { // Proof Size summary in bytes: - // Measured: `1955` - // Estimated: `19851` - // Minimum execution time: 40_867 nanoseconds. - Weight::from_parts(41_319_000, 19851) + // Measured: `1891` + // Estimated: `22693` + // Minimum execution time: 48_165_000 picoseconds. + Weight::from_parts(48_967_000, 22693) .saturating_add(RocksDbWeight::get().reads(6_u64)) .saturating_add(RocksDbWeight::get().writes(5_u64)) } @@ -112,10 +115,10 @@ impl WeightInfo for () { /// Proof Skipped: Session KeyOwner (max_values: None, max_size: None, mode: Measured) fn purge_keys() -> Weight { // Proof Size summary in bytes: - // Measured: `1854` - // Estimated: `9749` - // Minimum execution time: 30_286 nanoseconds. - Weight::from_parts(30_620_000, 9749) + // Measured: `1758` + // Estimated: `11537` + // Minimum execution time: 35_824_000 picoseconds. + Weight::from_parts(36_267_000, 11537) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().writes(5_u64)) } diff --git a/frame/staking/src/weights.rs b/frame/staking/src/weights.rs index d1814d89d..01d874d55 100644 --- a/frame/staking/src/weights.rs +++ b/frame/staking/src/weights.rs @@ -18,9 +18,9 @@ //! Autogenerated weights for pallet_staking //! //! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev -//! DATE: 2023-01-25, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! DATE: 2023-03-16, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` //! WORST CASE MAP SIZE: `1000000` -//! HOSTNAME: `bm2`, CPU: `Intel(R) Core(TM) i7-7700K CPU @ 4.20GHz` +//! HOSTNAME: `bm3`, CPU: `Intel(R) Core(TM) i7-7700K CPU @ 4.20GHz` //! EXECUTION: Some(Wasm), WASM-EXECUTION: Compiled, CHAIN: Some("dev"), DB CACHE: 1024 // Executed Command: @@ -31,6 +31,9 @@ // --steps=50 // --repeat=20 // --pallet=pallet_staking +// --no-storage-info +// --no-median-slopes +// --no-min-squares // --extrinsic=* // --execution=wasm // --wasm-execution=compiled @@ -95,10 +98,10 @@ impl WeightInfo for SubstrateWeight { /// Proof: Staking Payee (max_values: None, max_size: Some(73), added: 2548, mode: MaxEncodedLen) fn bond() -> Weight { // Proof Size summary in bytes: - // Measured: `1079` - // Estimated: `10386` - // Minimum execution time: 40_015 nanoseconds. - Weight::from_parts(40_601_000, 10386) + // Measured: `1047` + // Estimated: `14346` + // Minimum execution time: 46_401_000 picoseconds. + Weight::from_parts(46_987_000, 14346) .saturating_add(T::DbWeight::get().reads(4_u64)) .saturating_add(T::DbWeight::get().writes(4_u64)) } @@ -114,10 +117,10 @@ impl WeightInfo for SubstrateWeight { /// Proof: VoterList ListBags (max_values: None, max_size: Some(82), added: 2557, mode: MaxEncodedLen) fn bond_extra() -> Weight { // Proof Size summary in bytes: - // Measured: `2252` - // Estimated: `22888` - // Minimum execution time: 74_781 nanoseconds. - Weight::from_parts(75_188_000, 22888) + // Measured: `2028` + // Estimated: `27838` + // Minimum execution time: 87_755_000 picoseconds. + Weight::from_parts(88_802_000, 27838) .saturating_add(T::DbWeight::get().reads(8_u64)) .saturating_add(T::DbWeight::get().writes(7_u64)) } @@ -141,10 +144,10 @@ impl WeightInfo for SubstrateWeight { /// Proof: VoterList ListBags (max_values: None, max_size: Some(82), added: 2557, mode: MaxEncodedLen) fn unbond() -> Weight { // Proof Size summary in bytes: - // Measured: `2457` - // Estimated: `29534` - // Minimum execution time: 81_299 nanoseconds. - Weight::from_parts(82_242_000, 29534) + // Measured: `2233` + // Estimated: `38444` + // Minimum execution time: 95_698_000 picoseconds. + Weight::from_parts(96_971_000, 38444) .saturating_add(T::DbWeight::get().reads(12_u64)) .saturating_add(T::DbWeight::get().writes(8_u64)) } @@ -159,12 +162,12 @@ impl WeightInfo for SubstrateWeight { /// The range of component `s` is `[0, 100]`. fn withdraw_unbonded_update(s: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `1085` - // Estimated: `10442` - // Minimum execution time: 31_479 nanoseconds. - Weight::from_parts(32_410_035, 10442) - // Standard Error: 313 - .saturating_add(Weight::from_parts(9_090, 0).saturating_mul(s.into())) + // Measured: `1021` + // Estimated: `14402` + // Minimum execution time: 38_239_000 picoseconds. + Weight::from_parts(39_615_452, 14402) + // Standard Error: 447 + .saturating_add(Weight::from_parts(9_199, 0).saturating_mul(s.into())) .saturating_add(T::DbWeight::get().reads(4_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) } @@ -199,12 +202,12 @@ impl WeightInfo for SubstrateWeight { /// The range of component `s` is `[0, 100]`. fn withdraw_unbonded_kill(s: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `2486 + s * (4 ±0)` - // Estimated: `32303 + s * (4 ±0)` - // Minimum execution time: 71_968 nanoseconds. - Weight::from_parts(76_631_804, 32303) - // Standard Error: 1_613 - .saturating_add(Weight::from_parts(1_058_968, 0).saturating_mul(s.into())) + // Measured: `2294 + s * (4 ±0)` + // Estimated: `43999 + s * (4 ±0)` + // Minimum execution time: 84_251_000 picoseconds. + Weight::from_parts(90_113_319, 43999) + // Standard Error: 2_177 + .saturating_add(Weight::from_parts(1_278_840, 0).saturating_mul(s.into())) .saturating_add(T::DbWeight::get().reads(13_u64)) .saturating_add(T::DbWeight::get().writes(12_u64)) .saturating_add(T::DbWeight::get().writes((1_u64).saturating_mul(s.into()))) @@ -234,10 +237,10 @@ impl WeightInfo for SubstrateWeight { /// Proof: Staking CounterForValidators (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) fn validate() -> Weight { // Proof Size summary in bytes: - // Measured: `1446` - // Estimated: `19359` - // Minimum execution time: 51_963 nanoseconds. - Weight::from_parts(52_418_000, 19359) + // Measured: `1414` + // Estimated: `30249` + // Minimum execution time: 60_509_000 picoseconds. + Weight::from_parts(61_305_000, 30249) .saturating_add(T::DbWeight::get().reads(11_u64)) .saturating_add(T::DbWeight::get().writes(5_u64)) } @@ -248,12 +251,12 @@ impl WeightInfo for SubstrateWeight { /// The range of component `k` is `[1, 128]`. fn kick(k: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `1292 + k * (601 ±0)` - // Estimated: `3566 + k * (3033 ±0)` - // Minimum execution time: 25_685 nanoseconds. - Weight::from_parts(25_290_286, 3566) - // Standard Error: 5_164 - .saturating_add(Weight::from_parts(6_445_608, 0).saturating_mul(k.into())) + // Measured: `1260 + k * (569 ±0)` + // Estimated: `5546 + k * (3033 ±0)` + // Minimum execution time: 30_063_000 picoseconds. + Weight::from_parts(31_774_698, 5546) + // Standard Error: 11_088 + .saturating_add(Weight::from_parts(7_869_325, 0).saturating_mul(k.into())) .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(T::DbWeight::get().reads((1_u64).saturating_mul(k.into()))) .saturating_add(T::DbWeight::get().writes((1_u64).saturating_mul(k.into()))) @@ -284,12 +287,12 @@ impl WeightInfo for SubstrateWeight { /// The range of component `n` is `[1, 16]`. fn nominate(n: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `1984 + n * (105 ±0)` - // Estimated: `21988 + n * (2520 ±0)` - // Minimum execution time: 59_542 nanoseconds. - Weight::from_parts(57_558_678, 21988) - // Standard Error: 10_364 - .saturating_add(Weight::from_parts(2_759_713, 0).saturating_mul(n.into())) + // Measured: `1888 + n * (105 ±0)` + // Estimated: `32878 + n * (2520 ±0)` + // Minimum execution time: 70_091_000 picoseconds. + Weight::from_parts(67_744_973, 32878) + // Standard Error: 12_799 + .saturating_add(Weight::from_parts(3_238_224, 0).saturating_mul(n.into())) .saturating_add(T::DbWeight::get().reads(12_u64)) .saturating_add(T::DbWeight::get().reads((1_u64).saturating_mul(n.into()))) .saturating_add(T::DbWeight::get().writes(6_u64)) @@ -311,10 +314,10 @@ impl WeightInfo for SubstrateWeight { /// Proof: VoterList CounterForListNodes (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) fn chill() -> Weight { // Proof Size summary in bytes: - // Measured: `1876` - // Estimated: `17932` - // Minimum execution time: 52_132 nanoseconds. - Weight::from_parts(52_648_000, 17932) + // Measured: `1748` + // Estimated: `24862` + // Minimum execution time: 60_582_000 picoseconds. + Weight::from_parts(61_208_000, 24862) .saturating_add(T::DbWeight::get().reads(8_u64)) .saturating_add(T::DbWeight::get().writes(6_u64)) } @@ -324,10 +327,10 @@ impl WeightInfo for SubstrateWeight { /// Proof: Staking Payee (max_values: None, max_size: Some(73), added: 2548, mode: MaxEncodedLen) fn set_payee() -> Weight { // Proof Size summary in bytes: - // Measured: `840` - // Estimated: `3566` - // Minimum execution time: 13_399 nanoseconds. - Weight::from_parts(13_567_000, 3566) + // Measured: `808` + // Estimated: `4556` + // Minimum execution time: 15_630_000 picoseconds. + Weight::from_parts(16_018_000, 4556) .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -337,10 +340,10 @@ impl WeightInfo for SubstrateWeight { /// Proof: Staking Ledger (max_values: None, max_size: Some(1091), added: 3566, mode: MaxEncodedLen) fn set_controller() -> Weight { // Proof Size summary in bytes: - // Measured: `939` - // Estimated: `9679` - // Minimum execution time: 20_425 nanoseconds. - Weight::from_parts(20_713_000, 9679) + // Measured: `907` + // Estimated: `11659` + // Minimum execution time: 23_895_000 picoseconds. + Weight::from_parts(24_306_000, 11659) .saturating_add(T::DbWeight::get().reads(3_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) } @@ -350,8 +353,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 3_069 nanoseconds. - Weight::from_parts(3_176_000, 0) + // Minimum execution time: 3_694_000 picoseconds. + Weight::from_parts(3_820_000, 0) .saturating_add(T::DbWeight::get().writes(1_u64)) } /// Storage: Staking ForceEra (r:0 w:1) @@ -360,8 +363,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 11_386 nanoseconds. - Weight::from_parts(11_672_000, 0) + // Minimum execution time: 10_729_000 picoseconds. + Weight::from_parts(11_086_000, 0) .saturating_add(T::DbWeight::get().writes(1_u64)) } /// Storage: Staking ForceEra (r:0 w:1) @@ -370,8 +373,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 11_591 nanoseconds. - Weight::from_parts(11_799_000, 0) + // Minimum execution time: 10_959_000 picoseconds. + Weight::from_parts(11_371_000, 0) .saturating_add(T::DbWeight::get().writes(1_u64)) } /// Storage: Staking ForceEra (r:0 w:1) @@ -380,8 +383,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 11_553 nanoseconds. - Weight::from_parts(11_871_000, 0) + // Minimum execution time: 11_101_000 picoseconds. + Weight::from_parts(11_506_000, 0) .saturating_add(T::DbWeight::get().writes(1_u64)) } /// Storage: Staking Invulnerables (r:0 w:1) @@ -391,10 +394,10 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 3_292 nanoseconds. - Weight::from_parts(3_754_352, 0) - // Standard Error: 40 - .saturating_add(Weight::from_parts(9_838, 0).saturating_mul(v.into())) + // Minimum execution time: 4_016_000 picoseconds. + Weight::from_parts(4_454_020, 0) + // Standard Error: 82 + .saturating_add(Weight::from_parts(10_703, 0).saturating_mul(v.into())) .saturating_add(T::DbWeight::get().writes(1_u64)) } /// Storage: Staking Bonded (r:1 w:1) @@ -426,12 +429,12 @@ impl WeightInfo for SubstrateWeight { /// The range of component `s` is `[0, 100]`. fn force_unstake(s: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `2178 + s * (4 ±0)` - // Estimated: `27930 + s * (4 ±0)` - // Minimum execution time: 65_307 nanoseconds. - Weight::from_parts(70_227_980, 27930) - // Standard Error: 2_113 - .saturating_add(Weight::from_parts(1_059_856, 0).saturating_mul(s.into())) + // Measured: `2018 + s * (4 ±0)` + // Estimated: `37678 + s * (4 ±0)` + // Minimum execution time: 76_338_000 picoseconds. + Weight::from_parts(82_426_098, 37678) + // Standard Error: 3_761 + .saturating_add(Weight::from_parts(1_279_343, 0).saturating_mul(s.into())) .saturating_add(T::DbWeight::get().reads(11_u64)) .saturating_add(T::DbWeight::get().writes(12_u64)) .saturating_add(T::DbWeight::get().writes((1_u64).saturating_mul(s.into()))) @@ -442,12 +445,12 @@ impl WeightInfo for SubstrateWeight { /// The range of component `s` is `[1, 1000]`. fn cancel_deferred_slash(s: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `66671` - // Estimated: `69146` - // Minimum execution time: 89_123 nanoseconds. - Weight::from_parts(890_989_741, 69146) - // Standard Error: 58_282 - .saturating_add(Weight::from_parts(4_920_413, 0).saturating_mul(s.into())) + // Measured: `66639` + // Estimated: `70104` + // Minimum execution time: 91_160_000 picoseconds. + Weight::from_parts(794_662_495, 70104) + // Standard Error: 51_479 + .saturating_add(Weight::from_parts(4_346_694, 0).saturating_mul(s.into())) .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -472,12 +475,12 @@ impl WeightInfo for SubstrateWeight { /// The range of component `n` is `[0, 256]`. fn payout_stakers_dead_controller(n: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `20345 + n * (143 ±0)` - // Estimated: `54756 + n * (8024 ±1)` - // Minimum execution time: 73_652 nanoseconds. - Weight::from_parts(127_839_483, 54756) - // Standard Error: 14_195 - .saturating_add(Weight::from_parts(21_932_079, 0).saturating_mul(n.into())) + // Measured: `20217 + n * (143 ±0)` + // Estimated: `63416 + n * (8024 ±1)` + // Minimum execution time: 77_845_000 picoseconds. + Weight::from_parts(86_178_186, 63416) + // Standard Error: 21_971 + .saturating_add(Weight::from_parts(26_966_654, 0).saturating_mul(n.into())) .saturating_add(T::DbWeight::get().reads(9_u64)) .saturating_add(T::DbWeight::get().reads((3_u64).saturating_mul(n.into()))) .saturating_add(T::DbWeight::get().writes(2_u64)) @@ -507,17 +510,17 @@ impl WeightInfo for SubstrateWeight { /// The range of component `n` is `[0, 256]`. fn payout_stakers_alive_staked(n: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `35099 + n * (465 ±0)` - // Estimated: `83594 + n * (16026 ±0)` - // Minimum execution time: 94_560 nanoseconds. - Weight::from_parts(154_033_219, 83594) - // Standard Error: 26_663 - .saturating_add(Weight::from_parts(31_269_223, 0).saturating_mul(n.into())) + // Measured: `34971 + n * (401 ±0)` + // Estimated: `93244 + n * (15898 ±0)` + // Minimum execution time: 98_209_000 picoseconds. + Weight::from_parts(102_121_432, 93244) + // Standard Error: 25_633 + .saturating_add(Weight::from_parts(39_244_200, 0).saturating_mul(n.into())) .saturating_add(T::DbWeight::get().reads(10_u64)) .saturating_add(T::DbWeight::get().reads((5_u64).saturating_mul(n.into()))) .saturating_add(T::DbWeight::get().writes(3_u64)) .saturating_add(T::DbWeight::get().writes((3_u64).saturating_mul(n.into()))) - .saturating_add(Weight::from_parts(0, 16026).saturating_mul(n.into())) + .saturating_add(Weight::from_parts(0, 15898).saturating_mul(n.into())) } /// Storage: Staking Ledger (r:1 w:1) /// Proof: Staking Ledger (max_values: None, max_size: Some(1091), added: 3566, mode: MaxEncodedLen) @@ -534,12 +537,12 @@ impl WeightInfo for SubstrateWeight { /// The range of component `l` is `[1, 32]`. fn rebond(l: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `2253 + l * (7 ±0)` - // Estimated: `25491` - // Minimum execution time: 74_764 nanoseconds. - Weight::from_parts(75_814_067, 25491) - // Standard Error: 1_217 - .saturating_add(Weight::from_parts(64_725, 0).saturating_mul(l.into())) + // Measured: `2029 + l * (7 ±0)` + // Estimated: `31431` + // Minimum execution time: 87_067_000 picoseconds. + Weight::from_parts(87_971_483, 31431) + // Standard Error: 1_668 + .saturating_add(Weight::from_parts(56_024, 0).saturating_mul(l.into())) .saturating_add(T::DbWeight::get().reads(9_u64)) .saturating_add(T::DbWeight::get().writes(8_u64)) } @@ -572,12 +575,12 @@ impl WeightInfo for SubstrateWeight { /// The range of component `s` is `[1, 100]`. fn reap_stash(s: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `2486 + s * (4 ±0)` - // Estimated: `31810 + s * (4 ±0)` - // Minimum execution time: 77_611 nanoseconds. - Weight::from_parts(79_760_034, 31810) - // Standard Error: 1_597 - .saturating_add(Weight::from_parts(1_039_268, 0).saturating_mul(s.into())) + // Measured: `2294 + s * (4 ±0)` + // Estimated: `42515 + s * (4 ±0)` + // Minimum execution time: 90_421_000 picoseconds. + Weight::from_parts(91_873_866, 42515) + // Standard Error: 1_867 + .saturating_add(Weight::from_parts(1_275_581, 0).saturating_mul(s.into())) .saturating_add(T::DbWeight::get().reads(12_u64)) .saturating_add(T::DbWeight::get().writes(12_u64)) .saturating_add(T::DbWeight::get().writes((1_u64).saturating_mul(s.into()))) @@ -621,21 +624,21 @@ impl WeightInfo for SubstrateWeight { /// The range of component `n` is `[0, 100]`. fn new_era(v: u32, n: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `0 + v * (3662 ±0) + n * (816 ±0)` - // Estimated: `528203 + v * (16743 ±0) + n * (12947 ±0)` - // Minimum execution time: 489_824 nanoseconds. - Weight::from_parts(491_687_000, 528203) - // Standard Error: 1_787_577 - .saturating_add(Weight::from_parts(58_719_498, 0).saturating_mul(v.into())) - // Standard Error: 178_122 - .saturating_add(Weight::from_parts(13_273_555, 0).saturating_mul(n.into())) + // Measured: `0 + v * (3598 ±0) + n * (720 ±0)` + // Estimated: `537749 + v * (16699 ±39) + n * (12763 ±3)` + // Minimum execution time: 552_452_000 picoseconds. + Weight::from_parts(555_250_000, 537749) + // Standard Error: 1_893_951 + .saturating_add(Weight::from_parts(61_059_451, 0).saturating_mul(v.into())) + // Standard Error: 188_721 + .saturating_add(Weight::from_parts(17_220_679, 0).saturating_mul(n.into())) .saturating_add(T::DbWeight::get().reads(206_u64)) .saturating_add(T::DbWeight::get().reads((5_u64).saturating_mul(v.into()))) .saturating_add(T::DbWeight::get().reads((4_u64).saturating_mul(n.into()))) .saturating_add(T::DbWeight::get().writes(4_u64)) .saturating_add(T::DbWeight::get().writes((3_u64).saturating_mul(v.into()))) - .saturating_add(Weight::from_parts(0, 16743).saturating_mul(v.into())) - .saturating_add(Weight::from_parts(0, 12947).saturating_mul(n.into())) + .saturating_add(Weight::from_parts(0, 16699).saturating_mul(v.into())) + .saturating_add(Weight::from_parts(0, 12763).saturating_mul(n.into())) } /// Storage: VoterList CounterForListNodes (r:1 w:0) /// Proof: VoterList CounterForListNodes (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) @@ -657,14 +660,14 @@ impl WeightInfo for SubstrateWeight { /// The range of component `n` is `[500, 1000]`. fn get_npos_voters(v: u32, n: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `3167 + v * (459 ±0) + n * (1007 ±0)` - // Estimated: `511899 + v * (14295 ±0) + n * (11775 ±0)` - // Minimum execution time: 23_373_467 nanoseconds. - Weight::from_parts(23_497_257_000, 511899) - // Standard Error: 299_205 - .saturating_add(Weight::from_parts(3_434_000, 0).saturating_mul(v.into())) - // Standard Error: 299_205 - .saturating_add(Weight::from_parts(2_568_954, 0).saturating_mul(n.into())) + // Measured: `3135 + v * (395 ±0) + n * (911 ±0)` + // Estimated: `518829 + v * (14295 ±0) + n * (11775 ±0)` + // Minimum execution time: 32_870_065_000 picoseconds. + Weight::from_parts(33_295_716_000, 518829) + // Standard Error: 371_824 + .saturating_add(Weight::from_parts(5_148_979, 0).saturating_mul(v.into())) + // Standard Error: 371_824 + .saturating_add(Weight::from_parts(3_376_262, 0).saturating_mul(n.into())) .saturating_add(T::DbWeight::get().reads(201_u64)) .saturating_add(T::DbWeight::get().reads((5_u64).saturating_mul(v.into()))) .saturating_add(T::DbWeight::get().reads((4_u64).saturating_mul(n.into()))) @@ -680,11 +683,11 @@ impl WeightInfo for SubstrateWeight { fn get_npos_targets(v: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `983 + v * (50 ±0)` - // Estimated: `3019 + v * (2520 ±0)` - // Minimum execution time: 3_882_120 nanoseconds. - Weight::from_parts(3_951_993_000, 3019) - // Standard Error: 46_729 - .saturating_add(Weight::from_parts(2_856_043, 0).saturating_mul(v.into())) + // Estimated: `4999 + v * (2520 ±0)` + // Minimum execution time: 2_321_389_000 picoseconds. + Weight::from_parts(70_122_933, 4999) + // Standard Error: 7_249 + .saturating_add(Weight::from_parts(4_632_666, 0).saturating_mul(v.into())) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().reads((1_u64).saturating_mul(v.into()))) .saturating_add(Weight::from_parts(0, 2520).saturating_mul(v.into())) @@ -705,8 +708,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 8_427 nanoseconds. - Weight::from_parts(8_794_000, 0) + // Minimum execution time: 9_581_000 picoseconds. + Weight::from_parts(9_802_000, 0) .saturating_add(T::DbWeight::get().writes(6_u64)) } /// Storage: Staking MinCommission (r:0 w:1) @@ -725,8 +728,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 7_620 nanoseconds. - Weight::from_parts(7_901_000, 0) + // Minimum execution time: 8_883_000 picoseconds. + Weight::from_parts(9_151_000, 0) .saturating_add(T::DbWeight::get().writes(6_u64)) } /// Storage: Staking Ledger (r:1 w:0) @@ -751,10 +754,10 @@ impl WeightInfo for SubstrateWeight { /// Proof: VoterList CounterForListNodes (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) fn chill_other() -> Weight { // Proof Size summary in bytes: - // Measured: `2031` - // Estimated: `19438` - // Minimum execution time: 66_188 nanoseconds. - Weight::from_parts(66_767_000, 19438) + // Measured: `1871` + // Estimated: `29338` + // Minimum execution time: 77_916_000 picoseconds. + Weight::from_parts(78_877_000, 29338) .saturating_add(T::DbWeight::get().reads(11_u64)) .saturating_add(T::DbWeight::get().writes(6_u64)) } @@ -765,9 +768,9 @@ impl WeightInfo for SubstrateWeight { fn force_apply_min_commission() -> Weight { // Proof Size summary in bytes: // Measured: `694` - // Estimated: `3019` - // Minimum execution time: 14_703 nanoseconds. - Weight::from_parts(15_031_000, 3019) + // Estimated: `4999` + // Minimum execution time: 16_658_000 picoseconds. + Weight::from_parts(16_968_000, 4999) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -777,8 +780,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 4_518 nanoseconds. - Weight::from_parts(4_656_000, 0) + // Minimum execution time: 4_982_000 picoseconds. + Weight::from_parts(5_232_000, 0) .saturating_add(T::DbWeight::get().writes(1_u64)) } } @@ -797,10 +800,10 @@ impl WeightInfo for () { /// Proof: Staking Payee (max_values: None, max_size: Some(73), added: 2548, mode: MaxEncodedLen) fn bond() -> Weight { // Proof Size summary in bytes: - // Measured: `1079` - // Estimated: `10386` - // Minimum execution time: 40_015 nanoseconds. - Weight::from_parts(40_601_000, 10386) + // Measured: `1047` + // Estimated: `14346` + // Minimum execution time: 46_401_000 picoseconds. + Weight::from_parts(46_987_000, 14346) .saturating_add(RocksDbWeight::get().reads(4_u64)) .saturating_add(RocksDbWeight::get().writes(4_u64)) } @@ -816,10 +819,10 @@ impl WeightInfo for () { /// Proof: VoterList ListBags (max_values: None, max_size: Some(82), added: 2557, mode: MaxEncodedLen) fn bond_extra() -> Weight { // Proof Size summary in bytes: - // Measured: `2252` - // Estimated: `22888` - // Minimum execution time: 74_781 nanoseconds. - Weight::from_parts(75_188_000, 22888) + // Measured: `2028` + // Estimated: `27838` + // Minimum execution time: 87_755_000 picoseconds. + Weight::from_parts(88_802_000, 27838) .saturating_add(RocksDbWeight::get().reads(8_u64)) .saturating_add(RocksDbWeight::get().writes(7_u64)) } @@ -843,10 +846,10 @@ impl WeightInfo for () { /// Proof: VoterList ListBags (max_values: None, max_size: Some(82), added: 2557, mode: MaxEncodedLen) fn unbond() -> Weight { // Proof Size summary in bytes: - // Measured: `2457` - // Estimated: `29534` - // Minimum execution time: 81_299 nanoseconds. - Weight::from_parts(82_242_000, 29534) + // Measured: `2233` + // Estimated: `38444` + // Minimum execution time: 95_698_000 picoseconds. + Weight::from_parts(96_971_000, 38444) .saturating_add(RocksDbWeight::get().reads(12_u64)) .saturating_add(RocksDbWeight::get().writes(8_u64)) } @@ -861,12 +864,12 @@ impl WeightInfo for () { /// The range of component `s` is `[0, 100]`. fn withdraw_unbonded_update(s: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `1085` - // Estimated: `10442` - // Minimum execution time: 31_479 nanoseconds. - Weight::from_parts(32_410_035, 10442) - // Standard Error: 313 - .saturating_add(Weight::from_parts(9_090, 0).saturating_mul(s.into())) + // Measured: `1021` + // Estimated: `14402` + // Minimum execution time: 38_239_000 picoseconds. + Weight::from_parts(39_615_452, 14402) + // Standard Error: 447 + .saturating_add(Weight::from_parts(9_199, 0).saturating_mul(s.into())) .saturating_add(RocksDbWeight::get().reads(4_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) } @@ -901,12 +904,12 @@ impl WeightInfo for () { /// The range of component `s` is `[0, 100]`. fn withdraw_unbonded_kill(s: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `2486 + s * (4 ±0)` - // Estimated: `32303 + s * (4 ±0)` - // Minimum execution time: 71_968 nanoseconds. - Weight::from_parts(76_631_804, 32303) - // Standard Error: 1_613 - .saturating_add(Weight::from_parts(1_058_968, 0).saturating_mul(s.into())) + // Measured: `2294 + s * (4 ±0)` + // Estimated: `43999 + s * (4 ±0)` + // Minimum execution time: 84_251_000 picoseconds. + Weight::from_parts(90_113_319, 43999) + // Standard Error: 2_177 + .saturating_add(Weight::from_parts(1_278_840, 0).saturating_mul(s.into())) .saturating_add(RocksDbWeight::get().reads(13_u64)) .saturating_add(RocksDbWeight::get().writes(12_u64)) .saturating_add(RocksDbWeight::get().writes((1_u64).saturating_mul(s.into()))) @@ -936,10 +939,10 @@ impl WeightInfo for () { /// Proof: Staking CounterForValidators (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) fn validate() -> Weight { // Proof Size summary in bytes: - // Measured: `1446` - // Estimated: `19359` - // Minimum execution time: 51_963 nanoseconds. - Weight::from_parts(52_418_000, 19359) + // Measured: `1414` + // Estimated: `30249` + // Minimum execution time: 60_509_000 picoseconds. + Weight::from_parts(61_305_000, 30249) .saturating_add(RocksDbWeight::get().reads(11_u64)) .saturating_add(RocksDbWeight::get().writes(5_u64)) } @@ -950,12 +953,12 @@ impl WeightInfo for () { /// The range of component `k` is `[1, 128]`. fn kick(k: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `1292 + k * (601 ±0)` - // Estimated: `3566 + k * (3033 ±0)` - // Minimum execution time: 25_685 nanoseconds. - Weight::from_parts(25_290_286, 3566) - // Standard Error: 5_164 - .saturating_add(Weight::from_parts(6_445_608, 0).saturating_mul(k.into())) + // Measured: `1260 + k * (569 ±0)` + // Estimated: `5546 + k * (3033 ±0)` + // Minimum execution time: 30_063_000 picoseconds. + Weight::from_parts(31_774_698, 5546) + // Standard Error: 11_088 + .saturating_add(Weight::from_parts(7_869_325, 0).saturating_mul(k.into())) .saturating_add(RocksDbWeight::get().reads(1_u64)) .saturating_add(RocksDbWeight::get().reads((1_u64).saturating_mul(k.into()))) .saturating_add(RocksDbWeight::get().writes((1_u64).saturating_mul(k.into()))) @@ -986,12 +989,12 @@ impl WeightInfo for () { /// The range of component `n` is `[1, 16]`. fn nominate(n: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `1984 + n * (105 ±0)` - // Estimated: `21988 + n * (2520 ±0)` - // Minimum execution time: 59_542 nanoseconds. - Weight::from_parts(57_558_678, 21988) - // Standard Error: 10_364 - .saturating_add(Weight::from_parts(2_759_713, 0).saturating_mul(n.into())) + // Measured: `1888 + n * (105 ±0)` + // Estimated: `32878 + n * (2520 ±0)` + // Minimum execution time: 70_091_000 picoseconds. + Weight::from_parts(67_744_973, 32878) + // Standard Error: 12_799 + .saturating_add(Weight::from_parts(3_238_224, 0).saturating_mul(n.into())) .saturating_add(RocksDbWeight::get().reads(12_u64)) .saturating_add(RocksDbWeight::get().reads((1_u64).saturating_mul(n.into()))) .saturating_add(RocksDbWeight::get().writes(6_u64)) @@ -1013,10 +1016,10 @@ impl WeightInfo for () { /// Proof: VoterList CounterForListNodes (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) fn chill() -> Weight { // Proof Size summary in bytes: - // Measured: `1876` - // Estimated: `17932` - // Minimum execution time: 52_132 nanoseconds. - Weight::from_parts(52_648_000, 17932) + // Measured: `1748` + // Estimated: `24862` + // Minimum execution time: 60_582_000 picoseconds. + Weight::from_parts(61_208_000, 24862) .saturating_add(RocksDbWeight::get().reads(8_u64)) .saturating_add(RocksDbWeight::get().writes(6_u64)) } @@ -1026,10 +1029,10 @@ impl WeightInfo for () { /// Proof: Staking Payee (max_values: None, max_size: Some(73), added: 2548, mode: MaxEncodedLen) fn set_payee() -> Weight { // Proof Size summary in bytes: - // Measured: `840` - // Estimated: `3566` - // Minimum execution time: 13_399 nanoseconds. - Weight::from_parts(13_567_000, 3566) + // Measured: `808` + // Estimated: `4556` + // Minimum execution time: 15_630_000 picoseconds. + Weight::from_parts(16_018_000, 4556) .saturating_add(RocksDbWeight::get().reads(1_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } @@ -1039,10 +1042,10 @@ impl WeightInfo for () { /// Proof: Staking Ledger (max_values: None, max_size: Some(1091), added: 3566, mode: MaxEncodedLen) fn set_controller() -> Weight { // Proof Size summary in bytes: - // Measured: `939` - // Estimated: `9679` - // Minimum execution time: 20_425 nanoseconds. - Weight::from_parts(20_713_000, 9679) + // Measured: `907` + // Estimated: `11659` + // Minimum execution time: 23_895_000 picoseconds. + Weight::from_parts(24_306_000, 11659) .saturating_add(RocksDbWeight::get().reads(3_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) } @@ -1052,8 +1055,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 3_069 nanoseconds. - Weight::from_parts(3_176_000, 0) + // Minimum execution time: 3_694_000 picoseconds. + Weight::from_parts(3_820_000, 0) .saturating_add(RocksDbWeight::get().writes(1_u64)) } /// Storage: Staking ForceEra (r:0 w:1) @@ -1062,8 +1065,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 11_386 nanoseconds. - Weight::from_parts(11_672_000, 0) + // Minimum execution time: 10_729_000 picoseconds. + Weight::from_parts(11_086_000, 0) .saturating_add(RocksDbWeight::get().writes(1_u64)) } /// Storage: Staking ForceEra (r:0 w:1) @@ -1072,8 +1075,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 11_591 nanoseconds. - Weight::from_parts(11_799_000, 0) + // Minimum execution time: 10_959_000 picoseconds. + Weight::from_parts(11_371_000, 0) .saturating_add(RocksDbWeight::get().writes(1_u64)) } /// Storage: Staking ForceEra (r:0 w:1) @@ -1082,8 +1085,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 11_553 nanoseconds. - Weight::from_parts(11_871_000, 0) + // Minimum execution time: 11_101_000 picoseconds. + Weight::from_parts(11_506_000, 0) .saturating_add(RocksDbWeight::get().writes(1_u64)) } /// Storage: Staking Invulnerables (r:0 w:1) @@ -1093,10 +1096,10 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 3_292 nanoseconds. - Weight::from_parts(3_754_352, 0) - // Standard Error: 40 - .saturating_add(Weight::from_parts(9_838, 0).saturating_mul(v.into())) + // Minimum execution time: 4_016_000 picoseconds. + Weight::from_parts(4_454_020, 0) + // Standard Error: 82 + .saturating_add(Weight::from_parts(10_703, 0).saturating_mul(v.into())) .saturating_add(RocksDbWeight::get().writes(1_u64)) } /// Storage: Staking Bonded (r:1 w:1) @@ -1128,12 +1131,12 @@ impl WeightInfo for () { /// The range of component `s` is `[0, 100]`. fn force_unstake(s: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `2178 + s * (4 ±0)` - // Estimated: `27930 + s * (4 ±0)` - // Minimum execution time: 65_307 nanoseconds. - Weight::from_parts(70_227_980, 27930) - // Standard Error: 2_113 - .saturating_add(Weight::from_parts(1_059_856, 0).saturating_mul(s.into())) + // Measured: `2018 + s * (4 ±0)` + // Estimated: `37678 + s * (4 ±0)` + // Minimum execution time: 76_338_000 picoseconds. + Weight::from_parts(82_426_098, 37678) + // Standard Error: 3_761 + .saturating_add(Weight::from_parts(1_279_343, 0).saturating_mul(s.into())) .saturating_add(RocksDbWeight::get().reads(11_u64)) .saturating_add(RocksDbWeight::get().writes(12_u64)) .saturating_add(RocksDbWeight::get().writes((1_u64).saturating_mul(s.into()))) @@ -1144,12 +1147,12 @@ impl WeightInfo for () { /// The range of component `s` is `[1, 1000]`. fn cancel_deferred_slash(s: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `66671` - // Estimated: `69146` - // Minimum execution time: 89_123 nanoseconds. - Weight::from_parts(890_989_741, 69146) - // Standard Error: 58_282 - .saturating_add(Weight::from_parts(4_920_413, 0).saturating_mul(s.into())) + // Measured: `66639` + // Estimated: `70104` + // Minimum execution time: 91_160_000 picoseconds. + Weight::from_parts(794_662_495, 70104) + // Standard Error: 51_479 + .saturating_add(Weight::from_parts(4_346_694, 0).saturating_mul(s.into())) .saturating_add(RocksDbWeight::get().reads(1_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } @@ -1174,12 +1177,12 @@ impl WeightInfo for () { /// The range of component `n` is `[0, 256]`. fn payout_stakers_dead_controller(n: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `20345 + n * (143 ±0)` - // Estimated: `54756 + n * (8024 ±1)` - // Minimum execution time: 73_652 nanoseconds. - Weight::from_parts(127_839_483, 54756) - // Standard Error: 14_195 - .saturating_add(Weight::from_parts(21_932_079, 0).saturating_mul(n.into())) + // Measured: `20217 + n * (143 ±0)` + // Estimated: `63416 + n * (8024 ±1)` + // Minimum execution time: 77_845_000 picoseconds. + Weight::from_parts(86_178_186, 63416) + // Standard Error: 21_971 + .saturating_add(Weight::from_parts(26_966_654, 0).saturating_mul(n.into())) .saturating_add(RocksDbWeight::get().reads(9_u64)) .saturating_add(RocksDbWeight::get().reads((3_u64).saturating_mul(n.into()))) .saturating_add(RocksDbWeight::get().writes(2_u64)) @@ -1209,17 +1212,17 @@ impl WeightInfo for () { /// The range of component `n` is `[0, 256]`. fn payout_stakers_alive_staked(n: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `35099 + n * (465 ±0)` - // Estimated: `83594 + n * (16026 ±0)` - // Minimum execution time: 94_560 nanoseconds. - Weight::from_parts(154_033_219, 83594) - // Standard Error: 26_663 - .saturating_add(Weight::from_parts(31_269_223, 0).saturating_mul(n.into())) + // Measured: `34971 + n * (401 ±0)` + // Estimated: `93244 + n * (15898 ±0)` + // Minimum execution time: 98_209_000 picoseconds. + Weight::from_parts(102_121_432, 93244) + // Standard Error: 25_633 + .saturating_add(Weight::from_parts(39_244_200, 0).saturating_mul(n.into())) .saturating_add(RocksDbWeight::get().reads(10_u64)) .saturating_add(RocksDbWeight::get().reads((5_u64).saturating_mul(n.into()))) .saturating_add(RocksDbWeight::get().writes(3_u64)) .saturating_add(RocksDbWeight::get().writes((3_u64).saturating_mul(n.into()))) - .saturating_add(Weight::from_parts(0, 16026).saturating_mul(n.into())) + .saturating_add(Weight::from_parts(0, 15898).saturating_mul(n.into())) } /// Storage: Staking Ledger (r:1 w:1) /// Proof: Staking Ledger (max_values: None, max_size: Some(1091), added: 3566, mode: MaxEncodedLen) @@ -1236,12 +1239,12 @@ impl WeightInfo for () { /// The range of component `l` is `[1, 32]`. fn rebond(l: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `2253 + l * (7 ±0)` - // Estimated: `25491` - // Minimum execution time: 74_764 nanoseconds. - Weight::from_parts(75_814_067, 25491) - // Standard Error: 1_217 - .saturating_add(Weight::from_parts(64_725, 0).saturating_mul(l.into())) + // Measured: `2029 + l * (7 ±0)` + // Estimated: `31431` + // Minimum execution time: 87_067_000 picoseconds. + Weight::from_parts(87_971_483, 31431) + // Standard Error: 1_668 + .saturating_add(Weight::from_parts(56_024, 0).saturating_mul(l.into())) .saturating_add(RocksDbWeight::get().reads(9_u64)) .saturating_add(RocksDbWeight::get().writes(8_u64)) } @@ -1274,12 +1277,12 @@ impl WeightInfo for () { /// The range of component `s` is `[1, 100]`. fn reap_stash(s: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `2486 + s * (4 ±0)` - // Estimated: `31810 + s * (4 ±0)` - // Minimum execution time: 77_611 nanoseconds. - Weight::from_parts(79_760_034, 31810) - // Standard Error: 1_597 - .saturating_add(Weight::from_parts(1_039_268, 0).saturating_mul(s.into())) + // Measured: `2294 + s * (4 ±0)` + // Estimated: `42515 + s * (4 ±0)` + // Minimum execution time: 90_421_000 picoseconds. + Weight::from_parts(91_873_866, 42515) + // Standard Error: 1_867 + .saturating_add(Weight::from_parts(1_275_581, 0).saturating_mul(s.into())) .saturating_add(RocksDbWeight::get().reads(12_u64)) .saturating_add(RocksDbWeight::get().writes(12_u64)) .saturating_add(RocksDbWeight::get().writes((1_u64).saturating_mul(s.into()))) @@ -1323,21 +1326,21 @@ impl WeightInfo for () { /// The range of component `n` is `[0, 100]`. fn new_era(v: u32, n: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `0 + v * (3662 ±0) + n * (816 ±0)` - // Estimated: `528203 + v * (16743 ±0) + n * (12947 ±0)` - // Minimum execution time: 489_824 nanoseconds. - Weight::from_parts(491_687_000, 528203) - // Standard Error: 1_787_577 - .saturating_add(Weight::from_parts(58_719_498, 0).saturating_mul(v.into())) - // Standard Error: 178_122 - .saturating_add(Weight::from_parts(13_273_555, 0).saturating_mul(n.into())) + // Measured: `0 + v * (3598 ±0) + n * (720 ±0)` + // Estimated: `537749 + v * (16699 ±39) + n * (12763 ±3)` + // Minimum execution time: 552_452_000 picoseconds. + Weight::from_parts(555_250_000, 537749) + // Standard Error: 1_893_951 + .saturating_add(Weight::from_parts(61_059_451, 0).saturating_mul(v.into())) + // Standard Error: 188_721 + .saturating_add(Weight::from_parts(17_220_679, 0).saturating_mul(n.into())) .saturating_add(RocksDbWeight::get().reads(206_u64)) .saturating_add(RocksDbWeight::get().reads((5_u64).saturating_mul(v.into()))) .saturating_add(RocksDbWeight::get().reads((4_u64).saturating_mul(n.into()))) .saturating_add(RocksDbWeight::get().writes(4_u64)) .saturating_add(RocksDbWeight::get().writes((3_u64).saturating_mul(v.into()))) - .saturating_add(Weight::from_parts(0, 16743).saturating_mul(v.into())) - .saturating_add(Weight::from_parts(0, 12947).saturating_mul(n.into())) + .saturating_add(Weight::from_parts(0, 16699).saturating_mul(v.into())) + .saturating_add(Weight::from_parts(0, 12763).saturating_mul(n.into())) } /// Storage: VoterList CounterForListNodes (r:1 w:0) /// Proof: VoterList CounterForListNodes (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) @@ -1359,14 +1362,14 @@ impl WeightInfo for () { /// The range of component `n` is `[500, 1000]`. fn get_npos_voters(v: u32, n: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `3167 + v * (459 ±0) + n * (1007 ±0)` - // Estimated: `511899 + v * (14295 ±0) + n * (11775 ±0)` - // Minimum execution time: 23_373_467 nanoseconds. - Weight::from_parts(23_497_257_000, 511899) - // Standard Error: 299_205 - .saturating_add(Weight::from_parts(3_434_000, 0).saturating_mul(v.into())) - // Standard Error: 299_205 - .saturating_add(Weight::from_parts(2_568_954, 0).saturating_mul(n.into())) + // Measured: `3135 + v * (395 ±0) + n * (911 ±0)` + // Estimated: `518829 + v * (14295 ±0) + n * (11775 ±0)` + // Minimum execution time: 32_870_065_000 picoseconds. + Weight::from_parts(33_295_716_000, 518829) + // Standard Error: 371_824 + .saturating_add(Weight::from_parts(5_148_979, 0).saturating_mul(v.into())) + // Standard Error: 371_824 + .saturating_add(Weight::from_parts(3_376_262, 0).saturating_mul(n.into())) .saturating_add(RocksDbWeight::get().reads(201_u64)) .saturating_add(RocksDbWeight::get().reads((5_u64).saturating_mul(v.into()))) .saturating_add(RocksDbWeight::get().reads((4_u64).saturating_mul(n.into()))) @@ -1382,11 +1385,11 @@ impl WeightInfo for () { fn get_npos_targets(v: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `983 + v * (50 ±0)` - // Estimated: `3019 + v * (2520 ±0)` - // Minimum execution time: 3_882_120 nanoseconds. - Weight::from_parts(3_951_993_000, 3019) - // Standard Error: 46_729 - .saturating_add(Weight::from_parts(2_856_043, 0).saturating_mul(v.into())) + // Estimated: `4999 + v * (2520 ±0)` + // Minimum execution time: 2_321_389_000 picoseconds. + Weight::from_parts(70_122_933, 4999) + // Standard Error: 7_249 + .saturating_add(Weight::from_parts(4_632_666, 0).saturating_mul(v.into())) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().reads((1_u64).saturating_mul(v.into()))) .saturating_add(Weight::from_parts(0, 2520).saturating_mul(v.into())) @@ -1407,8 +1410,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 8_427 nanoseconds. - Weight::from_parts(8_794_000, 0) + // Minimum execution time: 9_581_000 picoseconds. + Weight::from_parts(9_802_000, 0) .saturating_add(RocksDbWeight::get().writes(6_u64)) } /// Storage: Staking MinCommission (r:0 w:1) @@ -1427,8 +1430,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 7_620 nanoseconds. - Weight::from_parts(7_901_000, 0) + // Minimum execution time: 8_883_000 picoseconds. + Weight::from_parts(9_151_000, 0) .saturating_add(RocksDbWeight::get().writes(6_u64)) } /// Storage: Staking Ledger (r:1 w:0) @@ -1453,10 +1456,10 @@ impl WeightInfo for () { /// Proof: VoterList CounterForListNodes (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) fn chill_other() -> Weight { // Proof Size summary in bytes: - // Measured: `2031` - // Estimated: `19438` - // Minimum execution time: 66_188 nanoseconds. - Weight::from_parts(66_767_000, 19438) + // Measured: `1871` + // Estimated: `29338` + // Minimum execution time: 77_916_000 picoseconds. + Weight::from_parts(78_877_000, 29338) .saturating_add(RocksDbWeight::get().reads(11_u64)) .saturating_add(RocksDbWeight::get().writes(6_u64)) } @@ -1467,9 +1470,9 @@ impl WeightInfo for () { fn force_apply_min_commission() -> Weight { // Proof Size summary in bytes: // Measured: `694` - // Estimated: `3019` - // Minimum execution time: 14_703 nanoseconds. - Weight::from_parts(15_031_000, 3019) + // Estimated: `4999` + // Minimum execution time: 16_658_000 picoseconds. + Weight::from_parts(16_968_000, 4999) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } @@ -1479,8 +1482,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 4_518 nanoseconds. - Weight::from_parts(4_656_000, 0) + // Minimum execution time: 4_982_000 picoseconds. + Weight::from_parts(5_232_000, 0) .saturating_add(RocksDbWeight::get().writes(1_u64)) } } diff --git a/frame/state-trie-migration/src/weights.rs b/frame/state-trie-migration/src/weights.rs index e087ba185..961cec7bc 100644 --- a/frame/state-trie-migration/src/weights.rs +++ b/frame/state-trie-migration/src/weights.rs @@ -18,9 +18,9 @@ //! Autogenerated weights for pallet_state_trie_migration //! //! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev -//! DATE: 2023-01-25, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! DATE: 2023-03-16, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` //! WORST CASE MAP SIZE: `1000000` -//! HOSTNAME: `bm2`, CPU: `Intel(R) Core(TM) i7-7700K CPU @ 4.20GHz` +//! HOSTNAME: `bm3`, CPU: `Intel(R) Core(TM) i7-7700K CPU @ 4.20GHz` //! EXECUTION: Some(Wasm), WASM-EXECUTION: Compiled, CHAIN: Some("dev"), DB CACHE: 1024 // Executed Command: @@ -31,6 +31,9 @@ // --steps=50 // --repeat=20 // --pallet=pallet_state_trie_migration +// --no-storage-info +// --no-median-slopes +// --no-min-squares // --extrinsic=* // --execution=wasm // --wasm-execution=compiled @@ -67,9 +70,9 @@ impl WeightInfo for SubstrateWeight { fn continue_migrate() -> Weight { // Proof Size summary in bytes: // Measured: `108` - // Estimated: `2040` - // Minimum execution time: 15_563 nanoseconds. - Weight::from_parts(15_783_000, 2040) + // Estimated: `4020` + // Minimum execution time: 15_449_000 picoseconds. + Weight::from_parts(16_040_000, 4020) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -78,26 +81,26 @@ impl WeightInfo for SubstrateWeight { fn continue_migrate_wrong_witness() -> Weight { // Proof Size summary in bytes: // Measured: `76` - // Estimated: `503` - // Minimum execution time: 4_347 nanoseconds. - Weight::from_parts(4_558_000, 503) + // Estimated: `1493` + // Minimum execution time: 4_632_000 picoseconds. + Weight::from_parts(4_768_000, 1493) .saturating_add(T::DbWeight::get().reads(1_u64)) } fn migrate_custom_top_success() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 8_817 nanoseconds. - Weight::from_parts(9_027_000, 0) + // Minimum execution time: 9_596_000 picoseconds. + Weight::from_parts(9_866_000, 0) } /// Storage: unknown `0x666f6f` (r:1 w:1) /// Proof Skipped: unknown `0x666f6f` (r:1 w:1) fn migrate_custom_top_fail() -> Weight { // Proof Size summary in bytes: - // Measured: `144` - // Estimated: `2619` - // Minimum execution time: 23_854 nanoseconds. - Weight::from_parts(24_850_000, 2619) + // Measured: `113` + // Estimated: `3578` + // Minimum execution time: 27_352_000 picoseconds. + Weight::from_parts(28_089_000, 3578) .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -105,17 +108,17 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 9_020 nanoseconds. - Weight::from_parts(9_234_000, 0) + // Minimum execution time: 10_286_000 picoseconds. + Weight::from_parts(10_761_000, 0) } /// Storage: unknown `0x666f6f` (r:1 w:1) /// Proof Skipped: unknown `0x666f6f` (r:1 w:1) fn migrate_custom_child_fail() -> Weight { // Proof Size summary in bytes: - // Measured: `136` - // Estimated: `2611` - // Minimum execution time: 24_136 nanoseconds. - Weight::from_parts(24_810_000, 2611) + // Measured: `105` + // Estimated: `3570` + // Minimum execution time: 27_355_000 picoseconds. + Weight::from_parts(28_092_000, 3570) .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -124,12 +127,12 @@ impl WeightInfo for SubstrateWeight { /// The range of component `v` is `[1, 4194304]`. fn process_top_key(v: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `228 + v * (1 ±0)` - // Estimated: `2700 + v * (1 ±0)` - // Minimum execution time: 5_279 nanoseconds. - Weight::from_parts(5_517_000, 2700) + // Measured: `197 + v * (1 ±0)` + // Estimated: `3662 + v * (1 ±0)` + // Minimum execution time: 6_004_000 picoseconds. + Weight::from_parts(6_188_000, 3662) // Standard Error: 1 - .saturating_add(Weight::from_parts(1_230, 0).saturating_mul(v.into())) + .saturating_add(Weight::from_parts(1_073, 0).saturating_mul(v.into())) .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) .saturating_add(Weight::from_parts(0, 1).saturating_mul(v.into())) @@ -145,9 +148,9 @@ impl WeightInfo for () { fn continue_migrate() -> Weight { // Proof Size summary in bytes: // Measured: `108` - // Estimated: `2040` - // Minimum execution time: 15_563 nanoseconds. - Weight::from_parts(15_783_000, 2040) + // Estimated: `4020` + // Minimum execution time: 15_449_000 picoseconds. + Weight::from_parts(16_040_000, 4020) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } @@ -156,26 +159,26 @@ impl WeightInfo for () { fn continue_migrate_wrong_witness() -> Weight { // Proof Size summary in bytes: // Measured: `76` - // Estimated: `503` - // Minimum execution time: 4_347 nanoseconds. - Weight::from_parts(4_558_000, 503) + // Estimated: `1493` + // Minimum execution time: 4_632_000 picoseconds. + Weight::from_parts(4_768_000, 1493) .saturating_add(RocksDbWeight::get().reads(1_u64)) } fn migrate_custom_top_success() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 8_817 nanoseconds. - Weight::from_parts(9_027_000, 0) + // Minimum execution time: 9_596_000 picoseconds. + Weight::from_parts(9_866_000, 0) } /// Storage: unknown `0x666f6f` (r:1 w:1) /// Proof Skipped: unknown `0x666f6f` (r:1 w:1) fn migrate_custom_top_fail() -> Weight { // Proof Size summary in bytes: - // Measured: `144` - // Estimated: `2619` - // Minimum execution time: 23_854 nanoseconds. - Weight::from_parts(24_850_000, 2619) + // Measured: `113` + // Estimated: `3578` + // Minimum execution time: 27_352_000 picoseconds. + Weight::from_parts(28_089_000, 3578) .saturating_add(RocksDbWeight::get().reads(1_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } @@ -183,17 +186,17 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 9_020 nanoseconds. - Weight::from_parts(9_234_000, 0) + // Minimum execution time: 10_286_000 picoseconds. + Weight::from_parts(10_761_000, 0) } /// Storage: unknown `0x666f6f` (r:1 w:1) /// Proof Skipped: unknown `0x666f6f` (r:1 w:1) fn migrate_custom_child_fail() -> Weight { // Proof Size summary in bytes: - // Measured: `136` - // Estimated: `2611` - // Minimum execution time: 24_136 nanoseconds. - Weight::from_parts(24_810_000, 2611) + // Measured: `105` + // Estimated: `3570` + // Minimum execution time: 27_355_000 picoseconds. + Weight::from_parts(28_092_000, 3570) .saturating_add(RocksDbWeight::get().reads(1_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } @@ -202,12 +205,12 @@ impl WeightInfo for () { /// The range of component `v` is `[1, 4194304]`. fn process_top_key(v: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `228 + v * (1 ±0)` - // Estimated: `2700 + v * (1 ±0)` - // Minimum execution time: 5_279 nanoseconds. - Weight::from_parts(5_517_000, 2700) + // Measured: `197 + v * (1 ±0)` + // Estimated: `3662 + v * (1 ±0)` + // Minimum execution time: 6_004_000 picoseconds. + Weight::from_parts(6_188_000, 3662) // Standard Error: 1 - .saturating_add(Weight::from_parts(1_230, 0).saturating_mul(v.into())) + .saturating_add(Weight::from_parts(1_073, 0).saturating_mul(v.into())) .saturating_add(RocksDbWeight::get().reads(1_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) .saturating_add(Weight::from_parts(0, 1).saturating_mul(v.into())) diff --git a/frame/support/src/weights/block_weights.rs b/frame/support/src/weights/block_weights.rs index 2389c51d9..b8ac9a038 100644 --- a/frame/support/src/weights/block_weights.rs +++ b/frame/support/src/weights/block_weights.rs @@ -16,8 +16,8 @@ // limitations under the License. //! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev -//! DATE: 2023-01-25 (Y/M/D) -//! HOSTNAME: `bm2`, CPU: `Intel(R) Core(TM) i7-7700K CPU @ 4.20GHz` +//! DATE: 2023-03-15 (Y/M/D) +//! HOSTNAME: `bm3`, CPU: `Intel(R) Core(TM) i7-7700K CPU @ 4.20GHz` //! //! SHORT-NAME: `block`, LONG-NAME: `BlockExecution`, RUNTIME: `Development` //! WARMUPS: `10`, REPEAT: `100` @@ -44,17 +44,17 @@ parameter_types! { /// Calculated by multiplying the *Average* with `1.0` and adding `0`. /// /// Stats nanoseconds: - /// Min, Max: 377_722, 414_752 - /// Average: 381_015 - /// Median: 379_751 - /// Std-Dev: 5462.64 + /// Min, Max: 402_748, 458_228 + /// Average: 412_772 + /// Median: 406_151 + /// Std-Dev: 13480.33 /// /// Percentiles nanoseconds: - /// 99th: 413_074 - /// 95th: 384_876 - /// 75th: 380_642 + /// 99th: 450_080 + /// 95th: 445_111 + /// 75th: 414_170 pub const BlockExecutionWeight: Weight = - Weight::from_parts(WEIGHT_REF_TIME_PER_NANOS.saturating_mul(381_015), 0); + Weight::from_parts(WEIGHT_REF_TIME_PER_NANOS.saturating_mul(412_772), 0); } #[cfg(test)] diff --git a/frame/support/src/weights/extrinsic_weights.rs b/frame/support/src/weights/extrinsic_weights.rs index 78798d2e3..44b1b94ae 100644 --- a/frame/support/src/weights/extrinsic_weights.rs +++ b/frame/support/src/weights/extrinsic_weights.rs @@ -16,8 +16,8 @@ // limitations under the License. //! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev -//! DATE: 2023-01-25 (Y/M/D) -//! HOSTNAME: `bm2`, CPU: `Intel(R) Core(TM) i7-7700K CPU @ 4.20GHz` +//! DATE: 2023-03-15 (Y/M/D) +//! HOSTNAME: `bm3`, CPU: `Intel(R) Core(TM) i7-7700K CPU @ 4.20GHz` //! //! SHORT-NAME: `extrinsic`, LONG-NAME: `ExtrinsicBase`, RUNTIME: `Development` //! WARMUPS: `10`, REPEAT: `100` @@ -44,17 +44,17 @@ parameter_types! { /// Calculated by multiplying the *Average* with `1.0` and adding `0`. /// /// Stats nanoseconds: - /// Min, Max: 99_481, 103_304 - /// Average: 99_840 - /// Median: 99_795 - /// Std-Dev: 376.17 + /// Min, Max: 109_595, 114_170 + /// Average: 110_536 + /// Median: 110_233 + /// Std-Dev: 933.39 /// /// Percentiles nanoseconds: - /// 99th: 100_078 - /// 95th: 100_051 - /// 75th: 99_916 + /// 99th: 114_120 + /// 95th: 112_680 + /// 75th: 110_858 pub const ExtrinsicBaseWeight: Weight = - Weight::from_parts(WEIGHT_REF_TIME_PER_NANOS.saturating_mul(99_840), 0); + Weight::from_parts(WEIGHT_REF_TIME_PER_NANOS.saturating_mul(110_536), 0); } #[cfg(test)] diff --git a/frame/system/src/weights.rs b/frame/system/src/weights.rs index 812f2d091..34c0b62dc 100644 --- a/frame/system/src/weights.rs +++ b/frame/system/src/weights.rs @@ -18,9 +18,9 @@ //! Autogenerated weights for frame_system //! //! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev -//! DATE: 2023-01-24, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! DATE: 2023-03-15, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` //! WORST CASE MAP SIZE: `1000000` -//! HOSTNAME: `bm2`, CPU: `Intel(R) Core(TM) i7-7700K CPU @ 4.20GHz` +//! HOSTNAME: `bm3`, CPU: `Intel(R) Core(TM) i7-7700K CPU @ 4.20GHz` //! EXECUTION: Some(Wasm), WASM-EXECUTION: Compiled, CHAIN: Some("dev"), DB CACHE: 1024 // Executed Command: @@ -31,6 +31,9 @@ // --steps=50 // --repeat=20 // --pallet=frame_system +// --no-storage-info +// --no-median-slopes +// --no-min-squares // --extrinsic=* // --execution=wasm // --wasm-execution=compiled @@ -64,20 +67,20 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 2_018 nanoseconds. - Weight::from_parts(2_091_000, 0) + // Minimum execution time: 2_519_000 picoseconds. + Weight::from_parts(2_576_000, 0) // Standard Error: 0 - .saturating_add(Weight::from_parts(362, 0).saturating_mul(b.into())) + .saturating_add(Weight::from_parts(365, 0).saturating_mul(b.into())) } /// The range of component `b` is `[0, 3932160]`. fn remark_with_event(b: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 7_449 nanoseconds. - Weight::from_parts(7_748_000, 0) + // Minimum execution time: 8_687_000 picoseconds. + Weight::from_parts(8_875_000, 0) // Standard Error: 0 - .saturating_add(Weight::from_parts(1_423, 0).saturating_mul(b.into())) + .saturating_add(Weight::from_parts(1_121, 0).saturating_mul(b.into())) } /// Storage: System Digest (r:1 w:1) /// Proof Skipped: System Digest (max_values: Some(1), max_size: None, mode: Measured) @@ -86,9 +89,9 @@ impl WeightInfo for SubstrateWeight { fn set_heap_pages() -> Weight { // Proof Size summary in bytes: // Measured: `0` - // Estimated: `495` - // Minimum execution time: 4_440 nanoseconds. - Weight::from_parts(4_605_000, 495) + // Estimated: `1485` + // Minimum execution time: 5_281_000 picoseconds. + Weight::from_parts(5_383_000, 1485) .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(T::DbWeight::get().writes(2_u64)) } @@ -99,10 +102,10 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_981 nanoseconds. - Weight::from_parts(2_114_000, 0) - // Standard Error: 804 - .saturating_add(Weight::from_parts(631_438, 0).saturating_mul(i.into())) + // Minimum execution time: 2_346_000 picoseconds. + Weight::from_parts(2_491_000, 0) + // Standard Error: 815 + .saturating_add(Weight::from_parts(731_217, 0).saturating_mul(i.into())) .saturating_add(T::DbWeight::get().writes((1_u64).saturating_mul(i.into()))) } /// Storage: Skipped Metadata (r:0 w:0) @@ -112,10 +115,10 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 2_061 nanoseconds. - Weight::from_parts(2_153_000, 0) - // Standard Error: 952 - .saturating_add(Weight::from_parts(502_629, 0).saturating_mul(i.into())) + // Minimum execution time: 2_394_000 picoseconds. + Weight::from_parts(2_567_000, 0) + // Standard Error: 935 + .saturating_add(Weight::from_parts(554_023, 0).saturating_mul(i.into())) .saturating_add(T::DbWeight::get().writes((1_u64).saturating_mul(i.into()))) } /// Storage: Skipped Metadata (r:0 w:0) @@ -125,10 +128,11 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `116 + p * (69 ±0)` // Estimated: `121 + p * (70 ±0)` - // Minimum execution time: 4_026 nanoseconds. - Weight::from_parts(4_174_000, 121) - // Standard Error: 1_148 - .saturating_add(Weight::from_parts(1_093_099, 0).saturating_mul(p.into())) + // Minimum execution time: 4_706_000 picoseconds. + Weight::from_parts(4_858_000, 121) + // Standard Error: 1_291 + .saturating_add(Weight::from_parts(1_142_198, 0).saturating_mul(p.into())) + .saturating_add(T::DbWeight::get().reads((1_u64).saturating_mul(p.into()))) .saturating_add(T::DbWeight::get().writes((1_u64).saturating_mul(p.into()))) .saturating_add(Weight::from_parts(0, 70).saturating_mul(p.into())) } @@ -141,20 +145,20 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 2_018 nanoseconds. - Weight::from_parts(2_091_000, 0) + // Minimum execution time: 2_519_000 picoseconds. + Weight::from_parts(2_576_000, 0) // Standard Error: 0 - .saturating_add(Weight::from_parts(362, 0).saturating_mul(b.into())) + .saturating_add(Weight::from_parts(365, 0).saturating_mul(b.into())) } /// The range of component `b` is `[0, 3932160]`. fn remark_with_event(b: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 7_449 nanoseconds. - Weight::from_parts(7_748_000, 0) + // Minimum execution time: 8_687_000 picoseconds. + Weight::from_parts(8_875_000, 0) // Standard Error: 0 - .saturating_add(Weight::from_parts(1_423, 0).saturating_mul(b.into())) + .saturating_add(Weight::from_parts(1_121, 0).saturating_mul(b.into())) } /// Storage: System Digest (r:1 w:1) /// Proof Skipped: System Digest (max_values: Some(1), max_size: None, mode: Measured) @@ -163,9 +167,9 @@ impl WeightInfo for () { fn set_heap_pages() -> Weight { // Proof Size summary in bytes: // Measured: `0` - // Estimated: `495` - // Minimum execution time: 4_440 nanoseconds. - Weight::from_parts(4_605_000, 495) + // Estimated: `1485` + // Minimum execution time: 5_281_000 picoseconds. + Weight::from_parts(5_383_000, 1485) .saturating_add(RocksDbWeight::get().reads(1_u64)) .saturating_add(RocksDbWeight::get().writes(2_u64)) } @@ -176,10 +180,10 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_981 nanoseconds. - Weight::from_parts(2_114_000, 0) - // Standard Error: 804 - .saturating_add(Weight::from_parts(631_438, 0).saturating_mul(i.into())) + // Minimum execution time: 2_346_000 picoseconds. + Weight::from_parts(2_491_000, 0) + // Standard Error: 815 + .saturating_add(Weight::from_parts(731_217, 0).saturating_mul(i.into())) .saturating_add(RocksDbWeight::get().writes((1_u64).saturating_mul(i.into()))) } /// Storage: Skipped Metadata (r:0 w:0) @@ -189,10 +193,10 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 2_061 nanoseconds. - Weight::from_parts(2_153_000, 0) - // Standard Error: 952 - .saturating_add(Weight::from_parts(502_629, 0).saturating_mul(i.into())) + // Minimum execution time: 2_394_000 picoseconds. + Weight::from_parts(2_567_000, 0) + // Standard Error: 935 + .saturating_add(Weight::from_parts(554_023, 0).saturating_mul(i.into())) .saturating_add(RocksDbWeight::get().writes((1_u64).saturating_mul(i.into()))) } /// Storage: Skipped Metadata (r:0 w:0) @@ -202,10 +206,11 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `116 + p * (69 ±0)` // Estimated: `121 + p * (70 ±0)` - // Minimum execution time: 4_026 nanoseconds. - Weight::from_parts(4_174_000, 121) - // Standard Error: 1_148 - .saturating_add(Weight::from_parts(1_093_099, 0).saturating_mul(p.into())) + // Minimum execution time: 4_706_000 picoseconds. + Weight::from_parts(4_858_000, 121) + // Standard Error: 1_291 + .saturating_add(Weight::from_parts(1_142_198, 0).saturating_mul(p.into())) + .saturating_add(RocksDbWeight::get().reads((1_u64).saturating_mul(p.into()))) .saturating_add(RocksDbWeight::get().writes((1_u64).saturating_mul(p.into()))) .saturating_add(Weight::from_parts(0, 70).saturating_mul(p.into())) } diff --git a/frame/timestamp/src/weights.rs b/frame/timestamp/src/weights.rs index 943f941d5..e1ada6f63 100644 --- a/frame/timestamp/src/weights.rs +++ b/frame/timestamp/src/weights.rs @@ -18,9 +18,9 @@ //! Autogenerated weights for pallet_timestamp //! //! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev -//! DATE: 2023-01-25, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! DATE: 2023-03-16, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` //! WORST CASE MAP SIZE: `1000000` -//! HOSTNAME: `bm2`, CPU: `Intel(R) Core(TM) i7-7700K CPU @ 4.20GHz` +//! HOSTNAME: `bm3`, CPU: `Intel(R) Core(TM) i7-7700K CPU @ 4.20GHz` //! EXECUTION: Some(Wasm), WASM-EXECUTION: Compiled, CHAIN: Some("dev"), DB CACHE: 1024 // Executed Command: @@ -31,6 +31,9 @@ // --steps=50 // --repeat=20 // --pallet=pallet_timestamp +// --no-storage-info +// --no-median-slopes +// --no-min-squares // --extrinsic=* // --execution=wasm // --wasm-execution=compiled @@ -62,9 +65,9 @@ impl WeightInfo for SubstrateWeight { fn set() -> Weight { // Proof Size summary in bytes: // Measured: `312` - // Estimated: `1006` - // Minimum execution time: 9_106 nanoseconds. - Weight::from_parts(9_258_000, 1006) + // Estimated: `2986` + // Minimum execution time: 10_913_000 picoseconds. + Weight::from_parts(11_453_000, 2986) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -72,8 +75,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `161` // Estimated: `0` - // Minimum execution time: 3_927 nanoseconds. - Weight::from_parts(4_078_000, 0) + // Minimum execution time: 4_400_000 picoseconds. + Weight::from_parts(4_550_000, 0) } } @@ -86,9 +89,9 @@ impl WeightInfo for () { fn set() -> Weight { // Proof Size summary in bytes: // Measured: `312` - // Estimated: `1006` - // Minimum execution time: 9_106 nanoseconds. - Weight::from_parts(9_258_000, 1006) + // Estimated: `2986` + // Minimum execution time: 10_913_000 picoseconds. + Weight::from_parts(11_453_000, 2986) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } @@ -96,7 +99,7 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `161` // Estimated: `0` - // Minimum execution time: 3_927 nanoseconds. - Weight::from_parts(4_078_000, 0) + // Minimum execution time: 4_400_000 picoseconds. + Weight::from_parts(4_550_000, 0) } } diff --git a/frame/tips/src/weights.rs b/frame/tips/src/weights.rs index 2b265d787..205413d52 100644 --- a/frame/tips/src/weights.rs +++ b/frame/tips/src/weights.rs @@ -18,9 +18,9 @@ //! Autogenerated weights for pallet_tips //! //! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev -//! DATE: 2023-01-25, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! DATE: 2023-03-16, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` //! WORST CASE MAP SIZE: `1000000` -//! HOSTNAME: `bm2`, CPU: `Intel(R) Core(TM) i7-7700K CPU @ 4.20GHz` +//! HOSTNAME: `bm3`, CPU: `Intel(R) Core(TM) i7-7700K CPU @ 4.20GHz` //! EXECUTION: Some(Wasm), WASM-EXECUTION: Compiled, CHAIN: Some("dev"), DB CACHE: 1024 // Executed Command: @@ -31,6 +31,9 @@ // --steps=50 // --repeat=20 // --pallet=pallet_tips +// --no-storage-info +// --no-median-slopes +// --no-min-squares // --extrinsic=* // --execution=wasm // --wasm-execution=compiled @@ -67,11 +70,11 @@ impl WeightInfo for SubstrateWeight { fn report_awesome(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `4` - // Estimated: `4958` - // Minimum execution time: 23_262 nanoseconds. - Weight::from_parts(24_104_224, 4958) - // Standard Error: 148 - .saturating_add(Weight::from_parts(1_963, 0).saturating_mul(r.into())) + // Estimated: `6938` + // Minimum execution time: 26_789_000 picoseconds. + Weight::from_parts(27_619_925, 6938) + // Standard Error: 168 + .saturating_add(Weight::from_parts(1_352, 0).saturating_mul(r.into())) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().writes(2_u64)) } @@ -81,10 +84,10 @@ impl WeightInfo for SubstrateWeight { /// Proof Skipped: Tips Reasons (max_values: None, max_size: None, mode: Measured) fn retract_tip() -> Weight { // Proof Size summary in bytes: - // Measured: `253` - // Estimated: `2981` - // Minimum execution time: 22_282 nanoseconds. - Weight::from_parts(22_737_000, 2981) + // Measured: `221` + // Estimated: `3907` + // Minimum execution time: 25_322_000 picoseconds. + Weight::from_parts(26_107_000, 3907) .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(T::DbWeight::get().writes(2_u64)) } @@ -96,16 +99,14 @@ impl WeightInfo for SubstrateWeight { /// Proof Skipped: Tips Tips (max_values: None, max_size: None, mode: Measured) /// The range of component `r` is `[0, 300]`. /// The range of component `t` is `[1, 13]`. - fn tip_new(r: u32, t: u32, ) -> Weight { + fn tip_new(_r: u32, t: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `558 + t * (64 ±0)` - // Estimated: `4644 + t * (192 ±0)` - // Minimum execution time: 18_382 nanoseconds. - Weight::from_parts(18_195_288, 4644) - // Standard Error: 103 - .saturating_add(Weight::from_parts(2_096, 0).saturating_mul(r.into())) - // Standard Error: 2_469 - .saturating_add(Weight::from_parts(97_092, 0).saturating_mul(t.into())) + // Measured: `526 + t * (64 ±0)` + // Estimated: `6528 + t * (192 ±0)` + // Minimum execution time: 21_112_000 picoseconds. + Weight::from_parts(21_825_317, 6528) + // Standard Error: 11_230 + .saturating_add(Weight::from_parts(86_081, 0).saturating_mul(t.into())) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().writes(2_u64)) .saturating_add(Weight::from_parts(0, 192).saturating_mul(t.into())) @@ -117,12 +118,12 @@ impl WeightInfo for SubstrateWeight { /// The range of component `t` is `[1, 13]`. fn tip(t: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `811 + t * (112 ±0)` - // Estimated: `4592 + t * (224 ±0)` - // Minimum execution time: 13_564 nanoseconds. - Weight::from_parts(13_867_280, 4592) - // Standard Error: 4_245 - .saturating_add(Weight::from_parts(206_587, 0).saturating_mul(t.into())) + // Measured: `747 + t * (112 ±0)` + // Estimated: `6444 + t * (224 ±0)` + // Minimum execution time: 16_703_000 picoseconds. + Weight::from_parts(16_679_429, 6444) + // Standard Error: 18_088 + .saturating_add(Weight::from_parts(281_667, 0).saturating_mul(t.into())) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) .saturating_add(Weight::from_parts(0, 224).saturating_mul(t.into())) @@ -138,12 +139,12 @@ impl WeightInfo for SubstrateWeight { /// The range of component `t` is `[1, 13]`. fn close_tip(t: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `850 + t * (112 ±0)` - // Estimated: `8096 + t * (336 ±0)` - // Minimum execution time: 39_902 nanoseconds. - Weight::from_parts(40_747_650, 8096) - // Standard Error: 5_322 - .saturating_add(Weight::from_parts(144_298, 0).saturating_mul(t.into())) + // Measured: `786 + t * (112 ±0)` + // Estimated: `10874 + t * (336 ±0)` + // Minimum execution time: 46_106_000 picoseconds. + Weight::from_parts(47_844_269, 10874) + // Standard Error: 9_247 + .saturating_add(Weight::from_parts(87_804, 0).saturating_mul(t.into())) .saturating_add(T::DbWeight::get().reads(3_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) .saturating_add(Weight::from_parts(0, 336).saturating_mul(t.into())) @@ -153,14 +154,12 @@ impl WeightInfo for SubstrateWeight { /// Storage: Tips Reasons (r:0 w:1) /// Proof Skipped: Tips Reasons (max_values: None, max_size: None, mode: Measured) /// The range of component `t` is `[1, 13]`. - fn slash_tip(t: u32, ) -> Weight { + fn slash_tip(_t: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `301` - // Estimated: `3077` - // Minimum execution time: 13_511 nanoseconds. - Weight::from_parts(14_114_101, 3077) - // Standard Error: 1_815 - .saturating_add(Weight::from_parts(7_825, 0).saturating_mul(t.into())) + // Measured: `269` + // Estimated: `4003` + // Minimum execution time: 15_508_000 picoseconds. + Weight::from_parts(16_207_284, 4003) .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(T::DbWeight::get().writes(2_u64)) } @@ -176,11 +175,11 @@ impl WeightInfo for () { fn report_awesome(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `4` - // Estimated: `4958` - // Minimum execution time: 23_262 nanoseconds. - Weight::from_parts(24_104_224, 4958) - // Standard Error: 148 - .saturating_add(Weight::from_parts(1_963, 0).saturating_mul(r.into())) + // Estimated: `6938` + // Minimum execution time: 26_789_000 picoseconds. + Weight::from_parts(27_619_925, 6938) + // Standard Error: 168 + .saturating_add(Weight::from_parts(1_352, 0).saturating_mul(r.into())) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().writes(2_u64)) } @@ -190,10 +189,10 @@ impl WeightInfo for () { /// Proof Skipped: Tips Reasons (max_values: None, max_size: None, mode: Measured) fn retract_tip() -> Weight { // Proof Size summary in bytes: - // Measured: `253` - // Estimated: `2981` - // Minimum execution time: 22_282 nanoseconds. - Weight::from_parts(22_737_000, 2981) + // Measured: `221` + // Estimated: `3907` + // Minimum execution time: 25_322_000 picoseconds. + Weight::from_parts(26_107_000, 3907) .saturating_add(RocksDbWeight::get().reads(1_u64)) .saturating_add(RocksDbWeight::get().writes(2_u64)) } @@ -205,16 +204,14 @@ impl WeightInfo for () { /// Proof Skipped: Tips Tips (max_values: None, max_size: None, mode: Measured) /// The range of component `r` is `[0, 300]`. /// The range of component `t` is `[1, 13]`. - fn tip_new(r: u32, t: u32, ) -> Weight { + fn tip_new(_r: u32, t: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `558 + t * (64 ±0)` - // Estimated: `4644 + t * (192 ±0)` - // Minimum execution time: 18_382 nanoseconds. - Weight::from_parts(18_195_288, 4644) - // Standard Error: 103 - .saturating_add(Weight::from_parts(2_096, 0).saturating_mul(r.into())) - // Standard Error: 2_469 - .saturating_add(Weight::from_parts(97_092, 0).saturating_mul(t.into())) + // Measured: `526 + t * (64 ±0)` + // Estimated: `6528 + t * (192 ±0)` + // Minimum execution time: 21_112_000 picoseconds. + Weight::from_parts(21_825_317, 6528) + // Standard Error: 11_230 + .saturating_add(Weight::from_parts(86_081, 0).saturating_mul(t.into())) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().writes(2_u64)) .saturating_add(Weight::from_parts(0, 192).saturating_mul(t.into())) @@ -226,12 +223,12 @@ impl WeightInfo for () { /// The range of component `t` is `[1, 13]`. fn tip(t: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `811 + t * (112 ±0)` - // Estimated: `4592 + t * (224 ±0)` - // Minimum execution time: 13_564 nanoseconds. - Weight::from_parts(13_867_280, 4592) - // Standard Error: 4_245 - .saturating_add(Weight::from_parts(206_587, 0).saturating_mul(t.into())) + // Measured: `747 + t * (112 ±0)` + // Estimated: `6444 + t * (224 ±0)` + // Minimum execution time: 16_703_000 picoseconds. + Weight::from_parts(16_679_429, 6444) + // Standard Error: 18_088 + .saturating_add(Weight::from_parts(281_667, 0).saturating_mul(t.into())) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) .saturating_add(Weight::from_parts(0, 224).saturating_mul(t.into())) @@ -247,12 +244,12 @@ impl WeightInfo for () { /// The range of component `t` is `[1, 13]`. fn close_tip(t: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `850 + t * (112 ±0)` - // Estimated: `8096 + t * (336 ±0)` - // Minimum execution time: 39_902 nanoseconds. - Weight::from_parts(40_747_650, 8096) - // Standard Error: 5_322 - .saturating_add(Weight::from_parts(144_298, 0).saturating_mul(t.into())) + // Measured: `786 + t * (112 ±0)` + // Estimated: `10874 + t * (336 ±0)` + // Minimum execution time: 46_106_000 picoseconds. + Weight::from_parts(47_844_269, 10874) + // Standard Error: 9_247 + .saturating_add(Weight::from_parts(87_804, 0).saturating_mul(t.into())) .saturating_add(RocksDbWeight::get().reads(3_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) .saturating_add(Weight::from_parts(0, 336).saturating_mul(t.into())) @@ -262,14 +259,12 @@ impl WeightInfo for () { /// Storage: Tips Reasons (r:0 w:1) /// Proof Skipped: Tips Reasons (max_values: None, max_size: None, mode: Measured) /// The range of component `t` is `[1, 13]`. - fn slash_tip(t: u32, ) -> Weight { + fn slash_tip(_t: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `301` - // Estimated: `3077` - // Minimum execution time: 13_511 nanoseconds. - Weight::from_parts(14_114_101, 3077) - // Standard Error: 1_815 - .saturating_add(Weight::from_parts(7_825, 0).saturating_mul(t.into())) + // Measured: `269` + // Estimated: `4003` + // Minimum execution time: 15_508_000 picoseconds. + Weight::from_parts(16_207_284, 4003) .saturating_add(RocksDbWeight::get().reads(1_u64)) .saturating_add(RocksDbWeight::get().writes(2_u64)) } diff --git a/frame/transaction-storage/src/weights.rs b/frame/transaction-storage/src/weights.rs index 896e1ebab..2845136b7 100644 --- a/frame/transaction-storage/src/weights.rs +++ b/frame/transaction-storage/src/weights.rs @@ -18,9 +18,9 @@ //! Autogenerated weights for pallet_transaction_storage //! //! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev -//! DATE: 2023-01-25, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! DATE: 2023-03-16, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` //! WORST CASE MAP SIZE: `1000000` -//! HOSTNAME: `bm2`, CPU: `Intel(R) Core(TM) i7-7700K CPU @ 4.20GHz` +//! HOSTNAME: `bm3`, CPU: `Intel(R) Core(TM) i7-7700K CPU @ 4.20GHz` //! EXECUTION: Some(Wasm), WASM-EXECUTION: Compiled, CHAIN: Some("dev"), DB CACHE: 1024 // Executed Command: @@ -31,6 +31,9 @@ // --steps=50 // --repeat=20 // --pallet=pallet_transaction_storage +// --no-storage-info +// --no-median-slopes +// --no-min-squares // --extrinsic=* // --execution=wasm // --wasm-execution=compiled @@ -66,11 +69,11 @@ impl WeightInfo for SubstrateWeight { fn store(l: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `176` - // Estimated: `38383` - // Minimum execution time: 28_702 nanoseconds. - Weight::from_parts(29_164_000, 38383) + // Estimated: `41353` + // Minimum execution time: 33_286_000 picoseconds. + Weight::from_parts(33_596_000, 41353) // Standard Error: 2 - .saturating_add(Weight::from_parts(5_601, 0).saturating_mul(l.into())) + .saturating_add(Weight::from_parts(4_954, 0).saturating_mul(l.into())) .saturating_add(T::DbWeight::get().reads(3_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -84,10 +87,10 @@ impl WeightInfo for SubstrateWeight { /// Proof: TransactionStorage BlockTransactions (max_values: Some(1), max_size: Some(36866), added: 37361, mode: MaxEncodedLen) fn renew() -> Weight { // Proof Size summary in bytes: - // Measured: `358` - // Estimated: `77744` - // Minimum execution time: 36_219 nanoseconds. - Weight::from_parts(36_979_000, 77744) + // Measured: `326` + // Estimated: `81704` + // Minimum execution time: 41_892_000 picoseconds. + Weight::from_parts(42_802_000, 81704) .saturating_add(T::DbWeight::get().reads(4_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -103,10 +106,10 @@ impl WeightInfo for SubstrateWeight { /// Proof: TransactionStorage Transactions (max_values: None, max_size: Some(36886), added: 39361, mode: MaxEncodedLen) fn check_proof_max() -> Weight { // Proof Size summary in bytes: - // Measured: `37177` - // Estimated: `43382` - // Minimum execution time: 55_793 nanoseconds. - Weight::from_parts(57_128_000, 43382) + // Measured: `37145` + // Estimated: `48332` + // Minimum execution time: 63_799_000 picoseconds. + Weight::from_parts(66_145_000, 48332) .saturating_add(T::DbWeight::get().reads(5_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -124,11 +127,11 @@ impl WeightInfo for () { fn store(l: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `176` - // Estimated: `38383` - // Minimum execution time: 28_702 nanoseconds. - Weight::from_parts(29_164_000, 38383) + // Estimated: `41353` + // Minimum execution time: 33_286_000 picoseconds. + Weight::from_parts(33_596_000, 41353) // Standard Error: 2 - .saturating_add(Weight::from_parts(5_601, 0).saturating_mul(l.into())) + .saturating_add(Weight::from_parts(4_954, 0).saturating_mul(l.into())) .saturating_add(RocksDbWeight::get().reads(3_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } @@ -142,10 +145,10 @@ impl WeightInfo for () { /// Proof: TransactionStorage BlockTransactions (max_values: Some(1), max_size: Some(36866), added: 37361, mode: MaxEncodedLen) fn renew() -> Weight { // Proof Size summary in bytes: - // Measured: `358` - // Estimated: `77744` - // Minimum execution time: 36_219 nanoseconds. - Weight::from_parts(36_979_000, 77744) + // Measured: `326` + // Estimated: `81704` + // Minimum execution time: 41_892_000 picoseconds. + Weight::from_parts(42_802_000, 81704) .saturating_add(RocksDbWeight::get().reads(4_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } @@ -161,10 +164,10 @@ impl WeightInfo for () { /// Proof: TransactionStorage Transactions (max_values: None, max_size: Some(36886), added: 39361, mode: MaxEncodedLen) fn check_proof_max() -> Weight { // Proof Size summary in bytes: - // Measured: `37177` - // Estimated: `43382` - // Minimum execution time: 55_793 nanoseconds. - Weight::from_parts(57_128_000, 43382) + // Measured: `37145` + // Estimated: `48332` + // Minimum execution time: 63_799_000 picoseconds. + Weight::from_parts(66_145_000, 48332) .saturating_add(RocksDbWeight::get().reads(5_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } diff --git a/frame/treasury/src/weights.rs b/frame/treasury/src/weights.rs index abf461c62..a7c093a80 100644 --- a/frame/treasury/src/weights.rs +++ b/frame/treasury/src/weights.rs @@ -18,9 +18,9 @@ //! Autogenerated weights for pallet_treasury //! //! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev -//! DATE: 2023-01-25, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! DATE: 2023-03-16, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` //! WORST CASE MAP SIZE: `1000000` -//! HOSTNAME: `bm2`, CPU: `Intel(R) Core(TM) i7-7700K CPU @ 4.20GHz` +//! HOSTNAME: `bm3`, CPU: `Intel(R) Core(TM) i7-7700K CPU @ 4.20GHz` //! EXECUTION: Some(Wasm), WASM-EXECUTION: Compiled, CHAIN: Some("dev"), DB CACHE: 1024 // Executed Command: @@ -31,6 +31,9 @@ // --steps=50 // --repeat=20 // --pallet=pallet_treasury +// --no-storage-info +// --no-median-slopes +// --no-min-squares // --extrinsic=* // --execution=wasm // --wasm-execution=compiled @@ -68,9 +71,9 @@ impl WeightInfo for SubstrateWeight { fn spend() -> Weight { // Proof Size summary in bytes: // Measured: `42` - // Estimated: `1396` - // Minimum execution time: 14_277 nanoseconds. - Weight::from_parts(14_749_000, 1396) + // Estimated: `3376` + // Minimum execution time: 17_010_000 picoseconds. + Weight::from_parts(17_247_000, 3376) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) } @@ -80,10 +83,10 @@ impl WeightInfo for SubstrateWeight { /// Proof: Treasury Proposals (max_values: None, max_size: Some(108), added: 2583, mode: MaxEncodedLen) fn propose_spend() -> Weight { // Proof Size summary in bytes: - // Measured: `175` - // Estimated: `499` - // Minimum execution time: 23_297 nanoseconds. - Weight::from_parts(23_585_000, 499) + // Measured: `143` + // Estimated: `1489` + // Minimum execution time: 25_780_000 picoseconds. + Weight::from_parts(41_064_000, 1489) .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(T::DbWeight::get().writes(2_u64)) } @@ -93,10 +96,10 @@ impl WeightInfo for SubstrateWeight { /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) fn reject_proposal() -> Weight { // Proof Size summary in bytes: - // Measured: `365` - // Estimated: `5186` - // Minimum execution time: 23_996 nanoseconds. - Weight::from_parts(24_548_000, 5186) + // Measured: `301` + // Estimated: `7166` + // Minimum execution time: 27_805_000 picoseconds. + Weight::from_parts(28_322_000, 7166) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().writes(2_u64)) } @@ -107,12 +110,12 @@ impl WeightInfo for SubstrateWeight { /// The range of component `p` is `[0, 99]`. fn approve_proposal(p: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `501 + p * (8 ±0)` - // Estimated: `3480` - // Minimum execution time: 9_366 nanoseconds. - Weight::from_parts(11_731_455, 3480) - // Standard Error: 761 - .saturating_add(Weight::from_parts(21_665, 0).saturating_mul(p.into())) + // Measured: `470 + p * (8 ±0)` + // Estimated: `5460` + // Minimum execution time: 10_939_000 picoseconds. + Weight::from_parts(13_667_341, 5460) + // Standard Error: 907 + .saturating_add(Weight::from_parts(26_648, 0).saturating_mul(p.into())) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -121,9 +124,9 @@ impl WeightInfo for SubstrateWeight { fn remove_approval() -> Weight { // Proof Size summary in bytes: // Measured: `127` - // Estimated: `897` - // Minimum execution time: 7_012 nanoseconds. - Weight::from_parts(7_270_000, 897) + // Estimated: `1887` + // Minimum execution time: 8_261_000 picoseconds. + Weight::from_parts(8_399_000, 1887) .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -140,12 +143,12 @@ impl WeightInfo for SubstrateWeight { /// The range of component `p` is `[0, 100]`. fn on_initialize_proposals(p: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `415 + p * (314 ±0)` - // Estimated: `2305 + p * (7789 ±0)` - // Minimum execution time: 37_834 nanoseconds. - Weight::from_parts(47_496_917, 2305) - // Standard Error: 12_505 - .saturating_add(Weight::from_parts(26_902_474, 0).saturating_mul(p.into())) + // Measured: `387 + p * (251 ±0)` + // Estimated: `7255 + p * (7789 ±0)` + // Minimum execution time: 43_781_000 picoseconds. + Weight::from_parts(68_521_487, 7255) + // Standard Error: 58_804 + .saturating_add(Weight::from_parts(33_271_211, 0).saturating_mul(p.into())) .saturating_add(T::DbWeight::get().reads(3_u64)) .saturating_add(T::DbWeight::get().reads((3_u64).saturating_mul(p.into()))) .saturating_add(T::DbWeight::get().writes(3_u64)) @@ -165,9 +168,9 @@ impl WeightInfo for () { fn spend() -> Weight { // Proof Size summary in bytes: // Measured: `42` - // Estimated: `1396` - // Minimum execution time: 14_277 nanoseconds. - Weight::from_parts(14_749_000, 1396) + // Estimated: `3376` + // Minimum execution time: 17_010_000 picoseconds. + Weight::from_parts(17_247_000, 3376) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) } @@ -177,10 +180,10 @@ impl WeightInfo for () { /// Proof: Treasury Proposals (max_values: None, max_size: Some(108), added: 2583, mode: MaxEncodedLen) fn propose_spend() -> Weight { // Proof Size summary in bytes: - // Measured: `175` - // Estimated: `499` - // Minimum execution time: 23_297 nanoseconds. - Weight::from_parts(23_585_000, 499) + // Measured: `143` + // Estimated: `1489` + // Minimum execution time: 25_780_000 picoseconds. + Weight::from_parts(41_064_000, 1489) .saturating_add(RocksDbWeight::get().reads(1_u64)) .saturating_add(RocksDbWeight::get().writes(2_u64)) } @@ -190,10 +193,10 @@ impl WeightInfo for () { /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) fn reject_proposal() -> Weight { // Proof Size summary in bytes: - // Measured: `365` - // Estimated: `5186` - // Minimum execution time: 23_996 nanoseconds. - Weight::from_parts(24_548_000, 5186) + // Measured: `301` + // Estimated: `7166` + // Minimum execution time: 27_805_000 picoseconds. + Weight::from_parts(28_322_000, 7166) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().writes(2_u64)) } @@ -204,12 +207,12 @@ impl WeightInfo for () { /// The range of component `p` is `[0, 99]`. fn approve_proposal(p: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `501 + p * (8 ±0)` - // Estimated: `3480` - // Minimum execution time: 9_366 nanoseconds. - Weight::from_parts(11_731_455, 3480) - // Standard Error: 761 - .saturating_add(Weight::from_parts(21_665, 0).saturating_mul(p.into())) + // Measured: `470 + p * (8 ±0)` + // Estimated: `5460` + // Minimum execution time: 10_939_000 picoseconds. + Weight::from_parts(13_667_341, 5460) + // Standard Error: 907 + .saturating_add(Weight::from_parts(26_648, 0).saturating_mul(p.into())) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } @@ -218,9 +221,9 @@ impl WeightInfo for () { fn remove_approval() -> Weight { // Proof Size summary in bytes: // Measured: `127` - // Estimated: `897` - // Minimum execution time: 7_012 nanoseconds. - Weight::from_parts(7_270_000, 897) + // Estimated: `1887` + // Minimum execution time: 8_261_000 picoseconds. + Weight::from_parts(8_399_000, 1887) .saturating_add(RocksDbWeight::get().reads(1_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } @@ -237,12 +240,12 @@ impl WeightInfo for () { /// The range of component `p` is `[0, 100]`. fn on_initialize_proposals(p: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `415 + p * (314 ±0)` - // Estimated: `2305 + p * (7789 ±0)` - // Minimum execution time: 37_834 nanoseconds. - Weight::from_parts(47_496_917, 2305) - // Standard Error: 12_505 - .saturating_add(Weight::from_parts(26_902_474, 0).saturating_mul(p.into())) + // Measured: `387 + p * (251 ±0)` + // Estimated: `7255 + p * (7789 ±0)` + // Minimum execution time: 43_781_000 picoseconds. + Weight::from_parts(68_521_487, 7255) + // Standard Error: 58_804 + .saturating_add(Weight::from_parts(33_271_211, 0).saturating_mul(p.into())) .saturating_add(RocksDbWeight::get().reads(3_u64)) .saturating_add(RocksDbWeight::get().reads((3_u64).saturating_mul(p.into()))) .saturating_add(RocksDbWeight::get().writes(3_u64)) diff --git a/frame/uniques/src/weights.rs b/frame/uniques/src/weights.rs index edf3bd35d..1f0e37726 100644 --- a/frame/uniques/src/weights.rs +++ b/frame/uniques/src/weights.rs @@ -18,9 +18,9 @@ //! Autogenerated weights for pallet_uniques //! //! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev -//! DATE: 2023-01-25, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! DATE: 2023-03-16, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` //! WORST CASE MAP SIZE: `1000000` -//! HOSTNAME: `bm2`, CPU: `Intel(R) Core(TM) i7-7700K CPU @ 4.20GHz` +//! HOSTNAME: `bm3`, CPU: `Intel(R) Core(TM) i7-7700K CPU @ 4.20GHz` //! EXECUTION: Some(Wasm), WASM-EXECUTION: Compiled, CHAIN: Some("dev"), DB CACHE: 1024 // Executed Command: @@ -31,6 +31,9 @@ // --steps=50 // --repeat=20 // --pallet=pallet_uniques +// --no-storage-info +// --no-median-slopes +// --no-min-squares // --extrinsic=* // --execution=wasm // --wasm-execution=compiled @@ -85,10 +88,10 @@ impl WeightInfo for SubstrateWeight { /// Proof: Uniques ClassAccount (max_values: None, max_size: Some(68), added: 2543, mode: MaxEncodedLen) fn create() -> Weight { // Proof Size summary in bytes: - // Measured: `281` - // Estimated: `2653` - // Minimum execution time: 24_242 nanoseconds. - Weight::from_parts(24_682_000, 2653) + // Measured: `249` + // Estimated: `3643` + // Minimum execution time: 27_805_000 picoseconds. + Weight::from_parts(28_303_000, 3643) .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(T::DbWeight::get().writes(2_u64)) } @@ -99,9 +102,9 @@ impl WeightInfo for SubstrateWeight { fn force_create() -> Weight { // Proof Size summary in bytes: // Measured: `109` - // Estimated: `2653` - // Minimum execution time: 14_145 nanoseconds. - Weight::from_parts(14_598_000, 2653) + // Estimated: `3643` + // Minimum execution time: 16_257_000 picoseconds. + Weight::from_parts(16_626_000, 3643) .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(T::DbWeight::get().writes(2_u64)) } @@ -109,14 +112,14 @@ impl WeightInfo for SubstrateWeight { /// Proof: Uniques Class (max_values: None, max_size: Some(178), added: 2653, mode: MaxEncodedLen) /// Storage: Uniques Asset (r:1001 w:1000) /// Proof: Uniques Asset (max_values: None, max_size: Some(122), added: 2597, mode: MaxEncodedLen) + /// Storage: Uniques InstanceMetadataOf (r:1000 w:1000) + /// Proof: Uniques InstanceMetadataOf (max_values: None, max_size: Some(108), added: 2583, mode: MaxEncodedLen) + /// Storage: Uniques Attribute (r:1000 w:1000) + /// Proof: Uniques Attribute (max_values: None, max_size: Some(364), added: 2839, mode: MaxEncodedLen) /// Storage: Uniques ClassAccount (r:0 w:1) /// Proof: Uniques ClassAccount (max_values: None, max_size: Some(68), added: 2543, mode: MaxEncodedLen) - /// Storage: Uniques Attribute (r:0 w:1000) - /// Proof: Uniques Attribute (max_values: None, max_size: Some(364), added: 2839, mode: MaxEncodedLen) /// Storage: Uniques ClassMetadataOf (r:0 w:1) /// Proof: Uniques ClassMetadataOf (max_values: None, max_size: Some(88), added: 2563, mode: MaxEncodedLen) - /// Storage: Uniques InstanceMetadataOf (r:0 w:1000) - /// Proof: Uniques InstanceMetadataOf (max_values: None, max_size: Some(108), added: 2583, mode: MaxEncodedLen) /// Storage: Uniques Account (r:0 w:1000) /// Proof: Uniques Account (max_values: None, max_size: Some(88), added: 2563, mode: MaxEncodedLen) /// Storage: Uniques CollectionMaxSupply (r:0 w:1) @@ -126,23 +129,27 @@ impl WeightInfo for SubstrateWeight { /// The range of component `a` is `[0, 1000]`. fn destroy(n: u32, m: u32, a: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `450 + n * (108 ±0) + m * (56 ±0) + a * (107 ±0)` - // Estimated: `5250 + n * (2597 ±0)` - // Minimum execution time: 2_404_081 nanoseconds. - Weight::from_parts(2_419_004_000, 5250) - // Standard Error: 27_175 - .saturating_add(Weight::from_parts(8_616_904, 0).saturating_mul(n.into())) - // Standard Error: 27_175 - .saturating_add(Weight::from_parts(334_249, 0).saturating_mul(m.into())) - // Standard Error: 27_175 - .saturating_add(Weight::from_parts(213_038, 0).saturating_mul(a.into())) + // Measured: `418 + n * (76 ±0) + m * (56 ±0) + a * (107 ±0)` + // Estimated: `9210 + n * (2597 ±0) + a * (2839 ±0) + m * (2583 ±0)` + // Minimum execution time: 2_510_772_000 picoseconds. + Weight::from_parts(2_522_511_000, 9210) + // Standard Error: 27_455 + .saturating_add(Weight::from_parts(6_708_827, 0).saturating_mul(n.into())) + // Standard Error: 27_455 + .saturating_add(Weight::from_parts(375_591, 0).saturating_mul(m.into())) + // Standard Error: 27_455 + .saturating_add(Weight::from_parts(251_697, 0).saturating_mul(a.into())) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().reads((1_u64).saturating_mul(n.into()))) + .saturating_add(T::DbWeight::get().reads((1_u64).saturating_mul(m.into()))) + .saturating_add(T::DbWeight::get().reads((1_u64).saturating_mul(a.into()))) .saturating_add(T::DbWeight::get().writes(4_u64)) .saturating_add(T::DbWeight::get().writes((2_u64).saturating_mul(n.into()))) .saturating_add(T::DbWeight::get().writes((1_u64).saturating_mul(m.into()))) .saturating_add(T::DbWeight::get().writes((1_u64).saturating_mul(a.into()))) .saturating_add(Weight::from_parts(0, 2597).saturating_mul(n.into())) + .saturating_add(Weight::from_parts(0, 2839).saturating_mul(a.into())) + .saturating_add(Weight::from_parts(0, 2583).saturating_mul(m.into())) } /// Storage: Uniques Asset (r:1 w:1) /// Proof: Uniques Asset (max_values: None, max_size: Some(122), added: 2597, mode: MaxEncodedLen) @@ -154,10 +161,10 @@ impl WeightInfo for SubstrateWeight { /// Proof: Uniques Account (max_values: None, max_size: Some(88), added: 2563, mode: MaxEncodedLen) fn mint() -> Weight { // Proof Size summary in bytes: - // Measured: `381` - // Estimated: `7749` - // Minimum execution time: 29_326 nanoseconds. - Weight::from_parts(29_671_000, 7749) + // Measured: `349` + // Estimated: `10719` + // Minimum execution time: 33_959_000 picoseconds. + Weight::from_parts(34_380_000, 10719) .saturating_add(T::DbWeight::get().reads(3_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) } @@ -171,10 +178,10 @@ impl WeightInfo for SubstrateWeight { /// Proof: Uniques ItemPriceOf (max_values: None, max_size: Some(89), added: 2564, mode: MaxEncodedLen) fn burn() -> Weight { // Proof Size summary in bytes: - // Measured: `559` - // Estimated: `5250` - // Minimum execution time: 30_497 nanoseconds. - Weight::from_parts(30_714_000, 5250) + // Measured: `495` + // Estimated: `7230` + // Minimum execution time: 34_194_000 picoseconds. + Weight::from_parts(34_808_000, 7230) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().writes(4_u64)) } @@ -188,10 +195,10 @@ impl WeightInfo for SubstrateWeight { /// Proof: Uniques ItemPriceOf (max_values: None, max_size: Some(89), added: 2564, mode: MaxEncodedLen) fn transfer() -> Weight { // Proof Size summary in bytes: - // Measured: `559` - // Estimated: `5250` - // Minimum execution time: 24_212 nanoseconds. - Weight::from_parts(24_681_000, 5250) + // Measured: `495` + // Estimated: `7230` + // Minimum execution time: 27_841_000 picoseconds. + Weight::from_parts(28_263_000, 7230) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().writes(4_u64)) } @@ -202,12 +209,12 @@ impl WeightInfo for SubstrateWeight { /// The range of component `i` is `[0, 5000]`. fn redeposit(i: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `837 + i * (108 ±0)` - // Estimated: `2653 + i * (2597 ±0)` - // Minimum execution time: 13_633 nanoseconds. - Weight::from_parts(13_797_000, 2653) - // Standard Error: 9_293 - .saturating_add(Weight::from_parts(11_163_914, 0).saturating_mul(i.into())) + // Measured: `805 + i * (76 ±0)` + // Estimated: `4633 + i * (2597 ±0)` + // Minimum execution time: 15_577_000 picoseconds. + Weight::from_parts(15_706_000, 4633) + // Standard Error: 17_679 + .saturating_add(Weight::from_parts(14_173_779, 0).saturating_mul(i.into())) .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(T::DbWeight::get().reads((1_u64).saturating_mul(i.into()))) .saturating_add(T::DbWeight::get().writes(1_u64)) @@ -220,10 +227,10 @@ impl WeightInfo for SubstrateWeight { /// Proof: Uniques Class (max_values: None, max_size: Some(178), added: 2653, mode: MaxEncodedLen) fn freeze() -> Weight { // Proof Size summary in bytes: - // Measured: `559` - // Estimated: `5250` - // Minimum execution time: 17_126 nanoseconds. - Weight::from_parts(17_572_000, 5250) + // Measured: `495` + // Estimated: `7230` + // Minimum execution time: 19_474_000 picoseconds. + Weight::from_parts(19_807_000, 7230) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -233,10 +240,10 @@ impl WeightInfo for SubstrateWeight { /// Proof: Uniques Class (max_values: None, max_size: Some(178), added: 2653, mode: MaxEncodedLen) fn thaw() -> Weight { // Proof Size summary in bytes: - // Measured: `559` - // Estimated: `5250` - // Minimum execution time: 17_209 nanoseconds. - Weight::from_parts(17_411_000, 5250) + // Measured: `495` + // Estimated: `7230` + // Minimum execution time: 19_526_000 picoseconds. + Weight::from_parts(19_818_000, 7230) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -244,10 +251,10 @@ impl WeightInfo for SubstrateWeight { /// Proof: Uniques Class (max_values: None, max_size: Some(178), added: 2653, mode: MaxEncodedLen) fn freeze_collection() -> Weight { // Proof Size summary in bytes: - // Measured: `381` - // Estimated: `2653` - // Minimum execution time: 13_048 nanoseconds. - Weight::from_parts(13_589_000, 2653) + // Measured: `349` + // Estimated: `3643` + // Minimum execution time: 15_324_000 picoseconds. + Weight::from_parts(15_599_000, 3643) .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -255,10 +262,10 @@ impl WeightInfo for SubstrateWeight { /// Proof: Uniques Class (max_values: None, max_size: Some(178), added: 2653, mode: MaxEncodedLen) fn thaw_collection() -> Weight { // Proof Size summary in bytes: - // Measured: `381` - // Estimated: `2653` - // Minimum execution time: 12_908 nanoseconds. - Weight::from_parts(13_098_000, 2653) + // Measured: `349` + // Estimated: `3643` + // Minimum execution time: 15_294_000 picoseconds. + Weight::from_parts(15_504_000, 3643) .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -270,10 +277,10 @@ impl WeightInfo for SubstrateWeight { /// Proof: Uniques ClassAccount (max_values: None, max_size: Some(68), added: 2543, mode: MaxEncodedLen) fn transfer_ownership() -> Weight { // Proof Size summary in bytes: - // Measured: `455` - // Estimated: `5180` - // Minimum execution time: 20_629 nanoseconds. - Weight::from_parts(21_448_000, 5180) + // Measured: `423` + // Estimated: `7160` + // Minimum execution time: 24_007_000 picoseconds. + Weight::from_parts(24_555_000, 7160) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().writes(4_u64)) } @@ -281,10 +288,10 @@ impl WeightInfo for SubstrateWeight { /// Proof: Uniques Class (max_values: None, max_size: Some(178), added: 2653, mode: MaxEncodedLen) fn set_team() -> Weight { // Proof Size summary in bytes: - // Measured: `381` - // Estimated: `2653` - // Minimum execution time: 13_740 nanoseconds. - Weight::from_parts(14_020_000, 2653) + // Measured: `349` + // Estimated: `3643` + // Minimum execution time: 15_616_000 picoseconds. + Weight::from_parts(15_897_000, 3643) .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -294,10 +301,10 @@ impl WeightInfo for SubstrateWeight { /// Proof: Uniques ClassAccount (max_values: None, max_size: Some(68), added: 2543, mode: MaxEncodedLen) fn force_item_status() -> Weight { // Proof Size summary in bytes: - // Measured: `381` - // Estimated: `2653` - // Minimum execution time: 16_293 nanoseconds. - Weight::from_parts(16_509_000, 2653) + // Measured: `349` + // Estimated: `3643` + // Minimum execution time: 18_459_000 picoseconds. + Weight::from_parts(18_705_000, 3643) .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(T::DbWeight::get().writes(2_u64)) } @@ -309,10 +316,10 @@ impl WeightInfo for SubstrateWeight { /// Proof: Uniques Attribute (max_values: None, max_size: Some(364), added: 2839, mode: MaxEncodedLen) fn set_attribute() -> Weight { // Proof Size summary in bytes: - // Measured: `611` - // Estimated: `8075` - // Minimum execution time: 33_560 nanoseconds. - Weight::from_parts(34_263_000, 8075) + // Measured: `547` + // Estimated: `11045` + // Minimum execution time: 39_056_000 picoseconds. + Weight::from_parts(39_513_000, 11045) .saturating_add(T::DbWeight::get().reads(3_u64)) .saturating_add(T::DbWeight::get().writes(2_u64)) } @@ -324,10 +331,10 @@ impl WeightInfo for SubstrateWeight { /// Proof: Uniques Attribute (max_values: None, max_size: Some(364), added: 2839, mode: MaxEncodedLen) fn clear_attribute() -> Weight { // Proof Size summary in bytes: - // Measured: `1031` - // Estimated: `8075` - // Minimum execution time: 31_955 nanoseconds. - Weight::from_parts(32_447_000, 8075) + // Measured: `936` + // Estimated: `11045` + // Minimum execution time: 37_441_000 picoseconds. + Weight::from_parts(37_859_000, 11045) .saturating_add(T::DbWeight::get().reads(3_u64)) .saturating_add(T::DbWeight::get().writes(2_u64)) } @@ -337,10 +344,10 @@ impl WeightInfo for SubstrateWeight { /// Proof: Uniques InstanceMetadataOf (max_values: None, max_size: Some(108), added: 2583, mode: MaxEncodedLen) fn set_metadata() -> Weight { // Proof Size summary in bytes: - // Measured: `447` - // Estimated: `5236` - // Minimum execution time: 25_520 nanoseconds. - Weight::from_parts(25_843_000, 5236) + // Measured: `415` + // Estimated: `7216` + // Minimum execution time: 29_456_000 picoseconds. + Weight::from_parts(29_930_000, 7216) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().writes(2_u64)) } @@ -350,10 +357,10 @@ impl WeightInfo for SubstrateWeight { /// Proof: Uniques InstanceMetadataOf (max_values: None, max_size: Some(108), added: 2583, mode: MaxEncodedLen) fn clear_metadata() -> Weight { // Proof Size summary in bytes: - // Measured: `611` - // Estimated: `5236` - // Minimum execution time: 25_812 nanoseconds. - Weight::from_parts(26_141_000, 5236) + // Measured: `547` + // Estimated: `7216` + // Minimum execution time: 29_817_000 picoseconds. + Weight::from_parts(30_364_000, 7216) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().writes(2_u64)) } @@ -363,10 +370,10 @@ impl WeightInfo for SubstrateWeight { /// Proof: Uniques ClassMetadataOf (max_values: None, max_size: Some(88), added: 2563, mode: MaxEncodedLen) fn set_collection_metadata() -> Weight { // Proof Size summary in bytes: - // Measured: `381` - // Estimated: `5216` - // Minimum execution time: 25_055 nanoseconds. - Weight::from_parts(25_244_000, 5216) + // Measured: `349` + // Estimated: `7196` + // Minimum execution time: 29_392_000 picoseconds. + Weight::from_parts(29_878_000, 7196) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().writes(2_u64)) } @@ -376,10 +383,10 @@ impl WeightInfo for SubstrateWeight { /// Proof: Uniques ClassMetadataOf (max_values: None, max_size: Some(88), added: 2563, mode: MaxEncodedLen) fn clear_collection_metadata() -> Weight { // Proof Size summary in bytes: - // Measured: `525` - // Estimated: `5216` - // Minimum execution time: 23_311 nanoseconds. - Weight::from_parts(23_682_000, 5216) + // Measured: `461` + // Estimated: `7196` + // Minimum execution time: 27_234_000 picoseconds. + Weight::from_parts(27_664_000, 7196) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -389,10 +396,10 @@ impl WeightInfo for SubstrateWeight { /// Proof: Uniques Asset (max_values: None, max_size: Some(122), added: 2597, mode: MaxEncodedLen) fn approve_transfer() -> Weight { // Proof Size summary in bytes: - // Measured: `559` - // Estimated: `5250` - // Minimum execution time: 17_709 nanoseconds. - Weight::from_parts(18_308_000, 5250) + // Measured: `495` + // Estimated: `7230` + // Minimum execution time: 20_447_000 picoseconds. + Weight::from_parts(20_886_000, 7230) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -402,10 +409,10 @@ impl WeightInfo for SubstrateWeight { /// Proof: Uniques Asset (max_values: None, max_size: Some(122), added: 2597, mode: MaxEncodedLen) fn cancel_approval() -> Weight { // Proof Size summary in bytes: - // Measured: `592` - // Estimated: `5250` - // Minimum execution time: 17_656 nanoseconds. - Weight::from_parts(18_039_000, 5250) + // Measured: `528` + // Estimated: `7230` + // Minimum execution time: 20_934_000 picoseconds. + Weight::from_parts(21_271_000, 7230) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -414,9 +421,9 @@ impl WeightInfo for SubstrateWeight { fn set_accept_ownership() -> Weight { // Proof Size summary in bytes: // Measured: `109` - // Estimated: `2527` - // Minimum execution time: 14_615 nanoseconds. - Weight::from_parts(14_931_000, 2527) + // Estimated: `3517` + // Minimum execution time: 17_004_000 picoseconds. + Weight::from_parts(17_401_000, 3517) .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -426,10 +433,10 @@ impl WeightInfo for SubstrateWeight { /// Proof: Uniques Class (max_values: None, max_size: Some(178), added: 2653, mode: MaxEncodedLen) fn set_collection_max_supply() -> Weight { // Proof Size summary in bytes: - // Measured: `381` - // Estimated: `5152` - // Minimum execution time: 14_974 nanoseconds. - Weight::from_parts(15_314_000, 5152) + // Measured: `349` + // Estimated: `7132` + // Minimum execution time: 17_371_000 picoseconds. + Weight::from_parts(18_103_000, 7132) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -439,10 +446,10 @@ impl WeightInfo for SubstrateWeight { /// Proof: Uniques ItemPriceOf (max_values: None, max_size: Some(89), added: 2564, mode: MaxEncodedLen) fn set_price() -> Weight { // Proof Size summary in bytes: - // Measured: `358` - // Estimated: `2597` - // Minimum execution time: 15_444 nanoseconds. - Weight::from_parts(15_886_000, 2597) + // Measured: `326` + // Estimated: `3587` + // Minimum execution time: 17_624_000 picoseconds. + Weight::from_parts(17_866_000, 3587) .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -456,10 +463,10 @@ impl WeightInfo for SubstrateWeight { /// Proof: Uniques Account (max_values: None, max_size: Some(88), added: 2563, mode: MaxEncodedLen) fn buy_item() -> Weight { // Proof Size summary in bytes: - // Measured: `703` - // Estimated: `7814` - // Minimum execution time: 35_628 nanoseconds. - Weight::from_parts(35_886_000, 7814) + // Measured: `607` + // Estimated: `10784` + // Minimum execution time: 39_736_000 picoseconds. + Weight::from_parts(40_855_000, 10784) .saturating_add(T::DbWeight::get().reads(3_u64)) .saturating_add(T::DbWeight::get().writes(4_u64)) } @@ -473,10 +480,10 @@ impl WeightInfo for () { /// Proof: Uniques ClassAccount (max_values: None, max_size: Some(68), added: 2543, mode: MaxEncodedLen) fn create() -> Weight { // Proof Size summary in bytes: - // Measured: `281` - // Estimated: `2653` - // Minimum execution time: 24_242 nanoseconds. - Weight::from_parts(24_682_000, 2653) + // Measured: `249` + // Estimated: `3643` + // Minimum execution time: 27_805_000 picoseconds. + Weight::from_parts(28_303_000, 3643) .saturating_add(RocksDbWeight::get().reads(1_u64)) .saturating_add(RocksDbWeight::get().writes(2_u64)) } @@ -487,9 +494,9 @@ impl WeightInfo for () { fn force_create() -> Weight { // Proof Size summary in bytes: // Measured: `109` - // Estimated: `2653` - // Minimum execution time: 14_145 nanoseconds. - Weight::from_parts(14_598_000, 2653) + // Estimated: `3643` + // Minimum execution time: 16_257_000 picoseconds. + Weight::from_parts(16_626_000, 3643) .saturating_add(RocksDbWeight::get().reads(1_u64)) .saturating_add(RocksDbWeight::get().writes(2_u64)) } @@ -497,14 +504,14 @@ impl WeightInfo for () { /// Proof: Uniques Class (max_values: None, max_size: Some(178), added: 2653, mode: MaxEncodedLen) /// Storage: Uniques Asset (r:1001 w:1000) /// Proof: Uniques Asset (max_values: None, max_size: Some(122), added: 2597, mode: MaxEncodedLen) + /// Storage: Uniques InstanceMetadataOf (r:1000 w:1000) + /// Proof: Uniques InstanceMetadataOf (max_values: None, max_size: Some(108), added: 2583, mode: MaxEncodedLen) + /// Storage: Uniques Attribute (r:1000 w:1000) + /// Proof: Uniques Attribute (max_values: None, max_size: Some(364), added: 2839, mode: MaxEncodedLen) /// Storage: Uniques ClassAccount (r:0 w:1) /// Proof: Uniques ClassAccount (max_values: None, max_size: Some(68), added: 2543, mode: MaxEncodedLen) - /// Storage: Uniques Attribute (r:0 w:1000) - /// Proof: Uniques Attribute (max_values: None, max_size: Some(364), added: 2839, mode: MaxEncodedLen) /// Storage: Uniques ClassMetadataOf (r:0 w:1) /// Proof: Uniques ClassMetadataOf (max_values: None, max_size: Some(88), added: 2563, mode: MaxEncodedLen) - /// Storage: Uniques InstanceMetadataOf (r:0 w:1000) - /// Proof: Uniques InstanceMetadataOf (max_values: None, max_size: Some(108), added: 2583, mode: MaxEncodedLen) /// Storage: Uniques Account (r:0 w:1000) /// Proof: Uniques Account (max_values: None, max_size: Some(88), added: 2563, mode: MaxEncodedLen) /// Storage: Uniques CollectionMaxSupply (r:0 w:1) @@ -514,23 +521,27 @@ impl WeightInfo for () { /// The range of component `a` is `[0, 1000]`. fn destroy(n: u32, m: u32, a: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `450 + n * (108 ±0) + m * (56 ±0) + a * (107 ±0)` - // Estimated: `5250 + n * (2597 ±0)` - // Minimum execution time: 2_404_081 nanoseconds. - Weight::from_parts(2_419_004_000, 5250) - // Standard Error: 27_175 - .saturating_add(Weight::from_parts(8_616_904, 0).saturating_mul(n.into())) - // Standard Error: 27_175 - .saturating_add(Weight::from_parts(334_249, 0).saturating_mul(m.into())) - // Standard Error: 27_175 - .saturating_add(Weight::from_parts(213_038, 0).saturating_mul(a.into())) + // Measured: `418 + n * (76 ±0) + m * (56 ±0) + a * (107 ±0)` + // Estimated: `9210 + n * (2597 ±0) + a * (2839 ±0) + m * (2583 ±0)` + // Minimum execution time: 2_510_772_000 picoseconds. + Weight::from_parts(2_522_511_000, 9210) + // Standard Error: 27_455 + .saturating_add(Weight::from_parts(6_708_827, 0).saturating_mul(n.into())) + // Standard Error: 27_455 + .saturating_add(Weight::from_parts(375_591, 0).saturating_mul(m.into())) + // Standard Error: 27_455 + .saturating_add(Weight::from_parts(251_697, 0).saturating_mul(a.into())) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().reads((1_u64).saturating_mul(n.into()))) + .saturating_add(RocksDbWeight::get().reads((1_u64).saturating_mul(m.into()))) + .saturating_add(RocksDbWeight::get().reads((1_u64).saturating_mul(a.into()))) .saturating_add(RocksDbWeight::get().writes(4_u64)) .saturating_add(RocksDbWeight::get().writes((2_u64).saturating_mul(n.into()))) .saturating_add(RocksDbWeight::get().writes((1_u64).saturating_mul(m.into()))) .saturating_add(RocksDbWeight::get().writes((1_u64).saturating_mul(a.into()))) .saturating_add(Weight::from_parts(0, 2597).saturating_mul(n.into())) + .saturating_add(Weight::from_parts(0, 2839).saturating_mul(a.into())) + .saturating_add(Weight::from_parts(0, 2583).saturating_mul(m.into())) } /// Storage: Uniques Asset (r:1 w:1) /// Proof: Uniques Asset (max_values: None, max_size: Some(122), added: 2597, mode: MaxEncodedLen) @@ -542,10 +553,10 @@ impl WeightInfo for () { /// Proof: Uniques Account (max_values: None, max_size: Some(88), added: 2563, mode: MaxEncodedLen) fn mint() -> Weight { // Proof Size summary in bytes: - // Measured: `381` - // Estimated: `7749` - // Minimum execution time: 29_326 nanoseconds. - Weight::from_parts(29_671_000, 7749) + // Measured: `349` + // Estimated: `10719` + // Minimum execution time: 33_959_000 picoseconds. + Weight::from_parts(34_380_000, 10719) .saturating_add(RocksDbWeight::get().reads(3_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) } @@ -559,10 +570,10 @@ impl WeightInfo for () { /// Proof: Uniques ItemPriceOf (max_values: None, max_size: Some(89), added: 2564, mode: MaxEncodedLen) fn burn() -> Weight { // Proof Size summary in bytes: - // Measured: `559` - // Estimated: `5250` - // Minimum execution time: 30_497 nanoseconds. - Weight::from_parts(30_714_000, 5250) + // Measured: `495` + // Estimated: `7230` + // Minimum execution time: 34_194_000 picoseconds. + Weight::from_parts(34_808_000, 7230) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().writes(4_u64)) } @@ -576,10 +587,10 @@ impl WeightInfo for () { /// Proof: Uniques ItemPriceOf (max_values: None, max_size: Some(89), added: 2564, mode: MaxEncodedLen) fn transfer() -> Weight { // Proof Size summary in bytes: - // Measured: `559` - // Estimated: `5250` - // Minimum execution time: 24_212 nanoseconds. - Weight::from_parts(24_681_000, 5250) + // Measured: `495` + // Estimated: `7230` + // Minimum execution time: 27_841_000 picoseconds. + Weight::from_parts(28_263_000, 7230) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().writes(4_u64)) } @@ -590,12 +601,12 @@ impl WeightInfo for () { /// The range of component `i` is `[0, 5000]`. fn redeposit(i: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `837 + i * (108 ±0)` - // Estimated: `2653 + i * (2597 ±0)` - // Minimum execution time: 13_633 nanoseconds. - Weight::from_parts(13_797_000, 2653) - // Standard Error: 9_293 - .saturating_add(Weight::from_parts(11_163_914, 0).saturating_mul(i.into())) + // Measured: `805 + i * (76 ±0)` + // Estimated: `4633 + i * (2597 ±0)` + // Minimum execution time: 15_577_000 picoseconds. + Weight::from_parts(15_706_000, 4633) + // Standard Error: 17_679 + .saturating_add(Weight::from_parts(14_173_779, 0).saturating_mul(i.into())) .saturating_add(RocksDbWeight::get().reads(1_u64)) .saturating_add(RocksDbWeight::get().reads((1_u64).saturating_mul(i.into()))) .saturating_add(RocksDbWeight::get().writes(1_u64)) @@ -608,10 +619,10 @@ impl WeightInfo for () { /// Proof: Uniques Class (max_values: None, max_size: Some(178), added: 2653, mode: MaxEncodedLen) fn freeze() -> Weight { // Proof Size summary in bytes: - // Measured: `559` - // Estimated: `5250` - // Minimum execution time: 17_126 nanoseconds. - Weight::from_parts(17_572_000, 5250) + // Measured: `495` + // Estimated: `7230` + // Minimum execution time: 19_474_000 picoseconds. + Weight::from_parts(19_807_000, 7230) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } @@ -621,10 +632,10 @@ impl WeightInfo for () { /// Proof: Uniques Class (max_values: None, max_size: Some(178), added: 2653, mode: MaxEncodedLen) fn thaw() -> Weight { // Proof Size summary in bytes: - // Measured: `559` - // Estimated: `5250` - // Minimum execution time: 17_209 nanoseconds. - Weight::from_parts(17_411_000, 5250) + // Measured: `495` + // Estimated: `7230` + // Minimum execution time: 19_526_000 picoseconds. + Weight::from_parts(19_818_000, 7230) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } @@ -632,10 +643,10 @@ impl WeightInfo for () { /// Proof: Uniques Class (max_values: None, max_size: Some(178), added: 2653, mode: MaxEncodedLen) fn freeze_collection() -> Weight { // Proof Size summary in bytes: - // Measured: `381` - // Estimated: `2653` - // Minimum execution time: 13_048 nanoseconds. - Weight::from_parts(13_589_000, 2653) + // Measured: `349` + // Estimated: `3643` + // Minimum execution time: 15_324_000 picoseconds. + Weight::from_parts(15_599_000, 3643) .saturating_add(RocksDbWeight::get().reads(1_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } @@ -643,10 +654,10 @@ impl WeightInfo for () { /// Proof: Uniques Class (max_values: None, max_size: Some(178), added: 2653, mode: MaxEncodedLen) fn thaw_collection() -> Weight { // Proof Size summary in bytes: - // Measured: `381` - // Estimated: `2653` - // Minimum execution time: 12_908 nanoseconds. - Weight::from_parts(13_098_000, 2653) + // Measured: `349` + // Estimated: `3643` + // Minimum execution time: 15_294_000 picoseconds. + Weight::from_parts(15_504_000, 3643) .saturating_add(RocksDbWeight::get().reads(1_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } @@ -658,10 +669,10 @@ impl WeightInfo for () { /// Proof: Uniques ClassAccount (max_values: None, max_size: Some(68), added: 2543, mode: MaxEncodedLen) fn transfer_ownership() -> Weight { // Proof Size summary in bytes: - // Measured: `455` - // Estimated: `5180` - // Minimum execution time: 20_629 nanoseconds. - Weight::from_parts(21_448_000, 5180) + // Measured: `423` + // Estimated: `7160` + // Minimum execution time: 24_007_000 picoseconds. + Weight::from_parts(24_555_000, 7160) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().writes(4_u64)) } @@ -669,10 +680,10 @@ impl WeightInfo for () { /// Proof: Uniques Class (max_values: None, max_size: Some(178), added: 2653, mode: MaxEncodedLen) fn set_team() -> Weight { // Proof Size summary in bytes: - // Measured: `381` - // Estimated: `2653` - // Minimum execution time: 13_740 nanoseconds. - Weight::from_parts(14_020_000, 2653) + // Measured: `349` + // Estimated: `3643` + // Minimum execution time: 15_616_000 picoseconds. + Weight::from_parts(15_897_000, 3643) .saturating_add(RocksDbWeight::get().reads(1_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } @@ -682,10 +693,10 @@ impl WeightInfo for () { /// Proof: Uniques ClassAccount (max_values: None, max_size: Some(68), added: 2543, mode: MaxEncodedLen) fn force_item_status() -> Weight { // Proof Size summary in bytes: - // Measured: `381` - // Estimated: `2653` - // Minimum execution time: 16_293 nanoseconds. - Weight::from_parts(16_509_000, 2653) + // Measured: `349` + // Estimated: `3643` + // Minimum execution time: 18_459_000 picoseconds. + Weight::from_parts(18_705_000, 3643) .saturating_add(RocksDbWeight::get().reads(1_u64)) .saturating_add(RocksDbWeight::get().writes(2_u64)) } @@ -697,10 +708,10 @@ impl WeightInfo for () { /// Proof: Uniques Attribute (max_values: None, max_size: Some(364), added: 2839, mode: MaxEncodedLen) fn set_attribute() -> Weight { // Proof Size summary in bytes: - // Measured: `611` - // Estimated: `8075` - // Minimum execution time: 33_560 nanoseconds. - Weight::from_parts(34_263_000, 8075) + // Measured: `547` + // Estimated: `11045` + // Minimum execution time: 39_056_000 picoseconds. + Weight::from_parts(39_513_000, 11045) .saturating_add(RocksDbWeight::get().reads(3_u64)) .saturating_add(RocksDbWeight::get().writes(2_u64)) } @@ -712,10 +723,10 @@ impl WeightInfo for () { /// Proof: Uniques Attribute (max_values: None, max_size: Some(364), added: 2839, mode: MaxEncodedLen) fn clear_attribute() -> Weight { // Proof Size summary in bytes: - // Measured: `1031` - // Estimated: `8075` - // Minimum execution time: 31_955 nanoseconds. - Weight::from_parts(32_447_000, 8075) + // Measured: `936` + // Estimated: `11045` + // Minimum execution time: 37_441_000 picoseconds. + Weight::from_parts(37_859_000, 11045) .saturating_add(RocksDbWeight::get().reads(3_u64)) .saturating_add(RocksDbWeight::get().writes(2_u64)) } @@ -725,10 +736,10 @@ impl WeightInfo for () { /// Proof: Uniques InstanceMetadataOf (max_values: None, max_size: Some(108), added: 2583, mode: MaxEncodedLen) fn set_metadata() -> Weight { // Proof Size summary in bytes: - // Measured: `447` - // Estimated: `5236` - // Minimum execution time: 25_520 nanoseconds. - Weight::from_parts(25_843_000, 5236) + // Measured: `415` + // Estimated: `7216` + // Minimum execution time: 29_456_000 picoseconds. + Weight::from_parts(29_930_000, 7216) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().writes(2_u64)) } @@ -738,10 +749,10 @@ impl WeightInfo for () { /// Proof: Uniques InstanceMetadataOf (max_values: None, max_size: Some(108), added: 2583, mode: MaxEncodedLen) fn clear_metadata() -> Weight { // Proof Size summary in bytes: - // Measured: `611` - // Estimated: `5236` - // Minimum execution time: 25_812 nanoseconds. - Weight::from_parts(26_141_000, 5236) + // Measured: `547` + // Estimated: `7216` + // Minimum execution time: 29_817_000 picoseconds. + Weight::from_parts(30_364_000, 7216) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().writes(2_u64)) } @@ -751,10 +762,10 @@ impl WeightInfo for () { /// Proof: Uniques ClassMetadataOf (max_values: None, max_size: Some(88), added: 2563, mode: MaxEncodedLen) fn set_collection_metadata() -> Weight { // Proof Size summary in bytes: - // Measured: `381` - // Estimated: `5216` - // Minimum execution time: 25_055 nanoseconds. - Weight::from_parts(25_244_000, 5216) + // Measured: `349` + // Estimated: `7196` + // Minimum execution time: 29_392_000 picoseconds. + Weight::from_parts(29_878_000, 7196) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().writes(2_u64)) } @@ -764,10 +775,10 @@ impl WeightInfo for () { /// Proof: Uniques ClassMetadataOf (max_values: None, max_size: Some(88), added: 2563, mode: MaxEncodedLen) fn clear_collection_metadata() -> Weight { // Proof Size summary in bytes: - // Measured: `525` - // Estimated: `5216` - // Minimum execution time: 23_311 nanoseconds. - Weight::from_parts(23_682_000, 5216) + // Measured: `461` + // Estimated: `7196` + // Minimum execution time: 27_234_000 picoseconds. + Weight::from_parts(27_664_000, 7196) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } @@ -777,10 +788,10 @@ impl WeightInfo for () { /// Proof: Uniques Asset (max_values: None, max_size: Some(122), added: 2597, mode: MaxEncodedLen) fn approve_transfer() -> Weight { // Proof Size summary in bytes: - // Measured: `559` - // Estimated: `5250` - // Minimum execution time: 17_709 nanoseconds. - Weight::from_parts(18_308_000, 5250) + // Measured: `495` + // Estimated: `7230` + // Minimum execution time: 20_447_000 picoseconds. + Weight::from_parts(20_886_000, 7230) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } @@ -790,10 +801,10 @@ impl WeightInfo for () { /// Proof: Uniques Asset (max_values: None, max_size: Some(122), added: 2597, mode: MaxEncodedLen) fn cancel_approval() -> Weight { // Proof Size summary in bytes: - // Measured: `592` - // Estimated: `5250` - // Minimum execution time: 17_656 nanoseconds. - Weight::from_parts(18_039_000, 5250) + // Measured: `528` + // Estimated: `7230` + // Minimum execution time: 20_934_000 picoseconds. + Weight::from_parts(21_271_000, 7230) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } @@ -802,9 +813,9 @@ impl WeightInfo for () { fn set_accept_ownership() -> Weight { // Proof Size summary in bytes: // Measured: `109` - // Estimated: `2527` - // Minimum execution time: 14_615 nanoseconds. - Weight::from_parts(14_931_000, 2527) + // Estimated: `3517` + // Minimum execution time: 17_004_000 picoseconds. + Weight::from_parts(17_401_000, 3517) .saturating_add(RocksDbWeight::get().reads(1_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } @@ -814,10 +825,10 @@ impl WeightInfo for () { /// Proof: Uniques Class (max_values: None, max_size: Some(178), added: 2653, mode: MaxEncodedLen) fn set_collection_max_supply() -> Weight { // Proof Size summary in bytes: - // Measured: `381` - // Estimated: `5152` - // Minimum execution time: 14_974 nanoseconds. - Weight::from_parts(15_314_000, 5152) + // Measured: `349` + // Estimated: `7132` + // Minimum execution time: 17_371_000 picoseconds. + Weight::from_parts(18_103_000, 7132) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } @@ -827,10 +838,10 @@ impl WeightInfo for () { /// Proof: Uniques ItemPriceOf (max_values: None, max_size: Some(89), added: 2564, mode: MaxEncodedLen) fn set_price() -> Weight { // Proof Size summary in bytes: - // Measured: `358` - // Estimated: `2597` - // Minimum execution time: 15_444 nanoseconds. - Weight::from_parts(15_886_000, 2597) + // Measured: `326` + // Estimated: `3587` + // Minimum execution time: 17_624_000 picoseconds. + Weight::from_parts(17_866_000, 3587) .saturating_add(RocksDbWeight::get().reads(1_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } @@ -844,10 +855,10 @@ impl WeightInfo for () { /// Proof: Uniques Account (max_values: None, max_size: Some(88), added: 2563, mode: MaxEncodedLen) fn buy_item() -> Weight { // Proof Size summary in bytes: - // Measured: `703` - // Estimated: `7814` - // Minimum execution time: 35_628 nanoseconds. - Weight::from_parts(35_886_000, 7814) + // Measured: `607` + // Estimated: `10784` + // Minimum execution time: 39_736_000 picoseconds. + Weight::from_parts(40_855_000, 10784) .saturating_add(RocksDbWeight::get().reads(3_u64)) .saturating_add(RocksDbWeight::get().writes(4_u64)) } diff --git a/frame/utility/src/weights.rs b/frame/utility/src/weights.rs index c680c9ff0..0c50de4f5 100644 --- a/frame/utility/src/weights.rs +++ b/frame/utility/src/weights.rs @@ -18,9 +18,9 @@ //! Autogenerated weights for pallet_utility //! //! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev -//! DATE: 2023-01-25, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! DATE: 2023-03-16, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` //! WORST CASE MAP SIZE: `1000000` -//! HOSTNAME: `bm2`, CPU: `Intel(R) Core(TM) i7-7700K CPU @ 4.20GHz` +//! HOSTNAME: `bm3`, CPU: `Intel(R) Core(TM) i7-7700K CPU @ 4.20GHz` //! EXECUTION: Some(Wasm), WASM-EXECUTION: Compiled, CHAIN: Some("dev"), DB CACHE: 1024 // Executed Command: @@ -31,6 +31,9 @@ // --steps=50 // --repeat=20 // --pallet=pallet_utility +// --no-storage-info +// --no-median-slopes +// --no-min-squares // --extrinsic=* // --execution=wasm // --wasm-execution=compiled @@ -63,44 +66,44 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 6_742 nanoseconds. - Weight::from_parts(16_087_635, 0) - // Standard Error: 2_411 - .saturating_add(Weight::from_parts(3_665_506, 0).saturating_mul(c.into())) + // Minimum execution time: 7_828_000 picoseconds. + Weight::from_parts(13_806_712, 0) + // Standard Error: 2_750 + .saturating_add(Weight::from_parts(4_169_287, 0).saturating_mul(c.into())) } fn as_derivative() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 4_802 nanoseconds. - Weight::from_parts(5_269_000, 0) + // Minimum execution time: 5_835_000 picoseconds. + Weight::from_parts(6_305_000, 0) } /// The range of component `c` is `[0, 1000]`. fn batch_all(c: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 7_100 nanoseconds. - Weight::from_parts(14_090_381, 0) - // Standard Error: 1_917 - .saturating_add(Weight::from_parts(3_744_891, 0).saturating_mul(c.into())) + // Minimum execution time: 8_010_000 picoseconds. + Weight::from_parts(10_539_696, 0) + // Standard Error: 2_681 + .saturating_add(Weight::from_parts(4_368_716, 0).saturating_mul(c.into())) } fn dispatch_as() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 8_840 nanoseconds. - Weight::from_parts(9_280_000, 0) + // Minimum execution time: 10_139_000 picoseconds. + Weight::from_parts(10_679_000, 0) } /// The range of component `c` is `[0, 1000]`. fn force_batch(c: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 7_245 nanoseconds. - Weight::from_parts(14_292_923, 0) - // Standard Error: 1_803 - .saturating_add(Weight::from_parts(3_645_950, 0).saturating_mul(c.into())) + // Minimum execution time: 7_960_000 picoseconds. + Weight::from_parts(16_874_350, 0) + // Standard Error: 3_040 + .saturating_add(Weight::from_parts(4_151_507, 0).saturating_mul(c.into())) } } @@ -111,43 +114,43 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 6_742 nanoseconds. - Weight::from_parts(16_087_635, 0) - // Standard Error: 2_411 - .saturating_add(Weight::from_parts(3_665_506, 0).saturating_mul(c.into())) + // Minimum execution time: 7_828_000 picoseconds. + Weight::from_parts(13_806_712, 0) + // Standard Error: 2_750 + .saturating_add(Weight::from_parts(4_169_287, 0).saturating_mul(c.into())) } fn as_derivative() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 4_802 nanoseconds. - Weight::from_parts(5_269_000, 0) + // Minimum execution time: 5_835_000 picoseconds. + Weight::from_parts(6_305_000, 0) } /// The range of component `c` is `[0, 1000]`. fn batch_all(c: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 7_100 nanoseconds. - Weight::from_parts(14_090_381, 0) - // Standard Error: 1_917 - .saturating_add(Weight::from_parts(3_744_891, 0).saturating_mul(c.into())) + // Minimum execution time: 8_010_000 picoseconds. + Weight::from_parts(10_539_696, 0) + // Standard Error: 2_681 + .saturating_add(Weight::from_parts(4_368_716, 0).saturating_mul(c.into())) } fn dispatch_as() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 8_840 nanoseconds. - Weight::from_parts(9_280_000, 0) + // Minimum execution time: 10_139_000 picoseconds. + Weight::from_parts(10_679_000, 0) } /// The range of component `c` is `[0, 1000]`. fn force_batch(c: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 7_245 nanoseconds. - Weight::from_parts(14_292_923, 0) - // Standard Error: 1_803 - .saturating_add(Weight::from_parts(3_645_950, 0).saturating_mul(c.into())) + // Minimum execution time: 7_960_000 picoseconds. + Weight::from_parts(16_874_350, 0) + // Standard Error: 3_040 + .saturating_add(Weight::from_parts(4_151_507, 0).saturating_mul(c.into())) } } diff --git a/frame/vesting/src/weights.rs b/frame/vesting/src/weights.rs index de3260fa1..400984f06 100644 --- a/frame/vesting/src/weights.rs +++ b/frame/vesting/src/weights.rs @@ -18,9 +18,9 @@ //! Autogenerated weights for pallet_vesting //! //! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev -//! DATE: 2023-01-25, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! DATE: 2023-03-16, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` //! WORST CASE MAP SIZE: `1000000` -//! HOSTNAME: `bm2`, CPU: `Intel(R) Core(TM) i7-7700K CPU @ 4.20GHz` +//! HOSTNAME: `bm3`, CPU: `Intel(R) Core(TM) i7-7700K CPU @ 4.20GHz` //! EXECUTION: Some(Wasm), WASM-EXECUTION: Compiled, CHAIN: Some("dev"), DB CACHE: 1024 // Executed Command: @@ -31,6 +31,9 @@ // --steps=50 // --repeat=20 // --pallet=pallet_vesting +// --no-storage-info +// --no-median-slopes +// --no-min-squares // --extrinsic=* // --execution=wasm // --wasm-execution=compiled @@ -69,14 +72,14 @@ impl WeightInfo for SubstrateWeight { /// The range of component `s` is `[1, 28]`. fn vest_locked(l: u32, s: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `444 + l * (25 ±0) + s * (36 ±0)` - // Estimated: `7306` - // Minimum execution time: 28_062 nanoseconds. - Weight::from_parts(26_857_563, 7306) - // Standard Error: 623 - .saturating_add(Weight::from_parts(55_988, 0).saturating_mul(l.into())) - // Standard Error: 1_109 - .saturating_add(Weight::from_parts(59_714, 0).saturating_mul(s.into())) + // Measured: `381 + l * (25 ±0) + s * (36 ±0)` + // Estimated: `9286` + // Minimum execution time: 31_657_000 picoseconds. + Weight::from_parts(30_569_947, 9286) + // Standard Error: 794 + .saturating_add(Weight::from_parts(63_114, 0).saturating_mul(l.into())) + // Standard Error: 1_413 + .saturating_add(Weight::from_parts(58_636, 0).saturating_mul(s.into())) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().writes(2_u64)) } @@ -88,14 +91,14 @@ impl WeightInfo for SubstrateWeight { /// The range of component `s` is `[1, 28]`. fn vest_unlocked(l: u32, s: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `444 + l * (25 ±0) + s * (36 ±0)` - // Estimated: `7306` - // Minimum execution time: 27_027 nanoseconds. - Weight::from_parts(26_509_364, 7306) - // Standard Error: 815 - .saturating_add(Weight::from_parts(54_711, 0).saturating_mul(l.into())) - // Standard Error: 1_451 - .saturating_add(Weight::from_parts(32_792, 0).saturating_mul(s.into())) + // Measured: `381 + l * (25 ±0) + s * (36 ±0)` + // Estimated: `9286` + // Minimum execution time: 30_474_000 picoseconds. + Weight::from_parts(30_227_344, 9286) + // Standard Error: 1_005 + .saturating_add(Weight::from_parts(56_742, 0).saturating_mul(l.into())) + // Standard Error: 1_788 + .saturating_add(Weight::from_parts(33_890, 0).saturating_mul(s.into())) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().writes(2_u64)) } @@ -109,14 +112,14 @@ impl WeightInfo for SubstrateWeight { /// The range of component `s` is `[1, 28]`. fn vest_other_locked(l: u32, s: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `579 + l * (25 ±0) + s * (36 ±0)` - // Estimated: `9909` - // Minimum execution time: 29_554 nanoseconds. - Weight::from_parts(28_269_203, 9909) - // Standard Error: 623 - .saturating_add(Weight::from_parts(59_058, 0).saturating_mul(l.into())) - // Standard Error: 1_108 - .saturating_add(Weight::from_parts(63_429, 0).saturating_mul(s.into())) + // Measured: `484 + l * (25 ±0) + s * (36 ±0)` + // Estimated: `12879` + // Minimum execution time: 33_681_000 picoseconds. + Weight::from_parts(32_540_534, 12879) + // Standard Error: 2_642 + .saturating_add(Weight::from_parts(62_200, 0).saturating_mul(l.into())) + // Standard Error: 4_701 + .saturating_add(Weight::from_parts(69_703, 0).saturating_mul(s.into())) .saturating_add(T::DbWeight::get().reads(3_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) } @@ -130,14 +133,14 @@ impl WeightInfo for SubstrateWeight { /// The range of component `s` is `[1, 28]`. fn vest_other_unlocked(l: u32, s: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `579 + l * (25 ±0) + s * (36 ±0)` - // Estimated: `9909` - // Minimum execution time: 28_546 nanoseconds. - Weight::from_parts(28_299_251, 9909) - // Standard Error: 786 - .saturating_add(Weight::from_parts(53_401, 0).saturating_mul(l.into())) - // Standard Error: 1_399 - .saturating_add(Weight::from_parts(29_713, 0).saturating_mul(s.into())) + // Measured: `484 + l * (25 ±0) + s * (36 ±0)` + // Estimated: `12879` + // Minimum execution time: 32_255_000 picoseconds. + Weight::from_parts(31_637_918, 12879) + // Standard Error: 3_135 + .saturating_add(Weight::from_parts(62_121, 0).saturating_mul(l.into())) + // Standard Error: 5_579 + .saturating_add(Weight::from_parts(61_055, 0).saturating_mul(s.into())) .saturating_add(T::DbWeight::get().reads(3_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) } @@ -151,14 +154,14 @@ impl WeightInfo for SubstrateWeight { /// The range of component `s` is `[0, 27]`. fn vested_transfer(l: u32, s: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `650 + l * (25 ±0) + s * (36 ±0)` - // Estimated: `9909` - // Minimum execution time: 43_436 nanoseconds. - Weight::from_parts(44_885_707, 9909) - // Standard Error: 1_516 - .saturating_add(Weight::from_parts(59_066, 0).saturating_mul(l.into())) - // Standard Error: 2_698 - .saturating_add(Weight::from_parts(32_053, 0).saturating_mul(s.into())) + // Measured: `555 + l * (25 ±0) + s * (36 ±0)` + // Estimated: `12879` + // Minimum execution time: 51_697_000 picoseconds. + Weight::from_parts(52_048_055, 12879) + // Standard Error: 1_598 + .saturating_add(Weight::from_parts(60_508, 0).saturating_mul(l.into())) + // Standard Error: 2_843 + .saturating_add(Weight::from_parts(37_870, 0).saturating_mul(s.into())) .saturating_add(T::DbWeight::get().reads(3_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) } @@ -172,14 +175,14 @@ impl WeightInfo for SubstrateWeight { /// The range of component `s` is `[0, 27]`. fn force_vested_transfer(l: u32, s: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `785 + l * (25 ±0) + s * (36 ±0)` - // Estimated: `12512` - // Minimum execution time: 45_805 nanoseconds. - Weight::from_parts(46_869_490, 12512) - // Standard Error: 1_445 - .saturating_add(Weight::from_parts(52_654, 0).saturating_mul(l.into())) - // Standard Error: 2_571 - .saturating_add(Weight::from_parts(34_202, 0).saturating_mul(s.into())) + // Measured: `658 + l * (25 ±0) + s * (36 ±0)` + // Estimated: `15482` + // Minimum execution time: 54_585_000 picoseconds. + Weight::from_parts(54_492_070, 15482) + // Standard Error: 1_694 + .saturating_add(Weight::from_parts(52_633, 0).saturating_mul(l.into())) + // Standard Error: 3_014 + .saturating_add(Weight::from_parts(45_485, 0).saturating_mul(s.into())) .saturating_add(T::DbWeight::get().reads(4_u64)) .saturating_add(T::DbWeight::get().writes(4_u64)) } @@ -193,14 +196,14 @@ impl WeightInfo for SubstrateWeight { /// The range of component `s` is `[2, 28]`. fn not_unlocking_merge_schedules(l: u32, s: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `577 + l * (25 ±0) + s * (36 ±0)` - // Estimated: `9909` - // Minimum execution time: 30_460 nanoseconds. - Weight::from_parts(29_407_637, 9909) - // Standard Error: 794 - .saturating_add(Weight::from_parts(63_757, 0).saturating_mul(l.into())) - // Standard Error: 1_466 - .saturating_add(Weight::from_parts(56_032, 0).saturating_mul(s.into())) + // Measured: `482 + l * (25 ±0) + s * (36 ±0)` + // Estimated: `12879` + // Minimum execution time: 34_312_000 picoseconds. + Weight::from_parts(33_740_101, 12879) + // Standard Error: 996 + .saturating_add(Weight::from_parts(62_123, 0).saturating_mul(l.into())) + // Standard Error: 1_841 + .saturating_add(Weight::from_parts(56_463, 0).saturating_mul(s.into())) .saturating_add(T::DbWeight::get().reads(3_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) } @@ -214,14 +217,14 @@ impl WeightInfo for SubstrateWeight { /// The range of component `s` is `[2, 28]`. fn unlocking_merge_schedules(l: u32, s: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `577 + l * (25 ±0) + s * (36 ±0)` - // Estimated: `9909` - // Minimum execution time: 30_413 nanoseconds. - Weight::from_parts(29_350_467, 9909) - // Standard Error: 724 - .saturating_add(Weight::from_parts(65_366, 0).saturating_mul(l.into())) - // Standard Error: 1_337 - .saturating_add(Weight::from_parts(53_799, 0).saturating_mul(s.into())) + // Measured: `482 + l * (25 ±0) + s * (36 ±0)` + // Estimated: `12879` + // Minimum execution time: 34_965_000 picoseconds. + Weight::from_parts(33_831_484, 12879) + // Standard Error: 1_530 + .saturating_add(Weight::from_parts(59_136, 0).saturating_mul(l.into())) + // Standard Error: 2_827 + .saturating_add(Weight::from_parts(58_493, 0).saturating_mul(s.into())) .saturating_add(T::DbWeight::get().reads(3_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) } @@ -237,14 +240,14 @@ impl WeightInfo for () { /// The range of component `s` is `[1, 28]`. fn vest_locked(l: u32, s: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `444 + l * (25 ±0) + s * (36 ±0)` - // Estimated: `7306` - // Minimum execution time: 28_062 nanoseconds. - Weight::from_parts(26_857_563, 7306) - // Standard Error: 623 - .saturating_add(Weight::from_parts(55_988, 0).saturating_mul(l.into())) - // Standard Error: 1_109 - .saturating_add(Weight::from_parts(59_714, 0).saturating_mul(s.into())) + // Measured: `381 + l * (25 ±0) + s * (36 ±0)` + // Estimated: `9286` + // Minimum execution time: 31_657_000 picoseconds. + Weight::from_parts(30_569_947, 9286) + // Standard Error: 794 + .saturating_add(Weight::from_parts(63_114, 0).saturating_mul(l.into())) + // Standard Error: 1_413 + .saturating_add(Weight::from_parts(58_636, 0).saturating_mul(s.into())) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().writes(2_u64)) } @@ -256,14 +259,14 @@ impl WeightInfo for () { /// The range of component `s` is `[1, 28]`. fn vest_unlocked(l: u32, s: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `444 + l * (25 ±0) + s * (36 ±0)` - // Estimated: `7306` - // Minimum execution time: 27_027 nanoseconds. - Weight::from_parts(26_509_364, 7306) - // Standard Error: 815 - .saturating_add(Weight::from_parts(54_711, 0).saturating_mul(l.into())) - // Standard Error: 1_451 - .saturating_add(Weight::from_parts(32_792, 0).saturating_mul(s.into())) + // Measured: `381 + l * (25 ±0) + s * (36 ±0)` + // Estimated: `9286` + // Minimum execution time: 30_474_000 picoseconds. + Weight::from_parts(30_227_344, 9286) + // Standard Error: 1_005 + .saturating_add(Weight::from_parts(56_742, 0).saturating_mul(l.into())) + // Standard Error: 1_788 + .saturating_add(Weight::from_parts(33_890, 0).saturating_mul(s.into())) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().writes(2_u64)) } @@ -277,14 +280,14 @@ impl WeightInfo for () { /// The range of component `s` is `[1, 28]`. fn vest_other_locked(l: u32, s: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `579 + l * (25 ±0) + s * (36 ±0)` - // Estimated: `9909` - // Minimum execution time: 29_554 nanoseconds. - Weight::from_parts(28_269_203, 9909) - // Standard Error: 623 - .saturating_add(Weight::from_parts(59_058, 0).saturating_mul(l.into())) - // Standard Error: 1_108 - .saturating_add(Weight::from_parts(63_429, 0).saturating_mul(s.into())) + // Measured: `484 + l * (25 ±0) + s * (36 ±0)` + // Estimated: `12879` + // Minimum execution time: 33_681_000 picoseconds. + Weight::from_parts(32_540_534, 12879) + // Standard Error: 2_642 + .saturating_add(Weight::from_parts(62_200, 0).saturating_mul(l.into())) + // Standard Error: 4_701 + .saturating_add(Weight::from_parts(69_703, 0).saturating_mul(s.into())) .saturating_add(RocksDbWeight::get().reads(3_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) } @@ -298,14 +301,14 @@ impl WeightInfo for () { /// The range of component `s` is `[1, 28]`. fn vest_other_unlocked(l: u32, s: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `579 + l * (25 ±0) + s * (36 ±0)` - // Estimated: `9909` - // Minimum execution time: 28_546 nanoseconds. - Weight::from_parts(28_299_251, 9909) - // Standard Error: 786 - .saturating_add(Weight::from_parts(53_401, 0).saturating_mul(l.into())) - // Standard Error: 1_399 - .saturating_add(Weight::from_parts(29_713, 0).saturating_mul(s.into())) + // Measured: `484 + l * (25 ±0) + s * (36 ±0)` + // Estimated: `12879` + // Minimum execution time: 32_255_000 picoseconds. + Weight::from_parts(31_637_918, 12879) + // Standard Error: 3_135 + .saturating_add(Weight::from_parts(62_121, 0).saturating_mul(l.into())) + // Standard Error: 5_579 + .saturating_add(Weight::from_parts(61_055, 0).saturating_mul(s.into())) .saturating_add(RocksDbWeight::get().reads(3_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) } @@ -319,14 +322,14 @@ impl WeightInfo for () { /// The range of component `s` is `[0, 27]`. fn vested_transfer(l: u32, s: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `650 + l * (25 ±0) + s * (36 ±0)` - // Estimated: `9909` - // Minimum execution time: 43_436 nanoseconds. - Weight::from_parts(44_885_707, 9909) - // Standard Error: 1_516 - .saturating_add(Weight::from_parts(59_066, 0).saturating_mul(l.into())) - // Standard Error: 2_698 - .saturating_add(Weight::from_parts(32_053, 0).saturating_mul(s.into())) + // Measured: `555 + l * (25 ±0) + s * (36 ±0)` + // Estimated: `12879` + // Minimum execution time: 51_697_000 picoseconds. + Weight::from_parts(52_048_055, 12879) + // Standard Error: 1_598 + .saturating_add(Weight::from_parts(60_508, 0).saturating_mul(l.into())) + // Standard Error: 2_843 + .saturating_add(Weight::from_parts(37_870, 0).saturating_mul(s.into())) .saturating_add(RocksDbWeight::get().reads(3_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) } @@ -340,14 +343,14 @@ impl WeightInfo for () { /// The range of component `s` is `[0, 27]`. fn force_vested_transfer(l: u32, s: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `785 + l * (25 ±0) + s * (36 ±0)` - // Estimated: `12512` - // Minimum execution time: 45_805 nanoseconds. - Weight::from_parts(46_869_490, 12512) - // Standard Error: 1_445 - .saturating_add(Weight::from_parts(52_654, 0).saturating_mul(l.into())) - // Standard Error: 2_571 - .saturating_add(Weight::from_parts(34_202, 0).saturating_mul(s.into())) + // Measured: `658 + l * (25 ±0) + s * (36 ±0)` + // Estimated: `15482` + // Minimum execution time: 54_585_000 picoseconds. + Weight::from_parts(54_492_070, 15482) + // Standard Error: 1_694 + .saturating_add(Weight::from_parts(52_633, 0).saturating_mul(l.into())) + // Standard Error: 3_014 + .saturating_add(Weight::from_parts(45_485, 0).saturating_mul(s.into())) .saturating_add(RocksDbWeight::get().reads(4_u64)) .saturating_add(RocksDbWeight::get().writes(4_u64)) } @@ -361,14 +364,14 @@ impl WeightInfo for () { /// The range of component `s` is `[2, 28]`. fn not_unlocking_merge_schedules(l: u32, s: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `577 + l * (25 ±0) + s * (36 ±0)` - // Estimated: `9909` - // Minimum execution time: 30_460 nanoseconds. - Weight::from_parts(29_407_637, 9909) - // Standard Error: 794 - .saturating_add(Weight::from_parts(63_757, 0).saturating_mul(l.into())) - // Standard Error: 1_466 - .saturating_add(Weight::from_parts(56_032, 0).saturating_mul(s.into())) + // Measured: `482 + l * (25 ±0) + s * (36 ±0)` + // Estimated: `12879` + // Minimum execution time: 34_312_000 picoseconds. + Weight::from_parts(33_740_101, 12879) + // Standard Error: 996 + .saturating_add(Weight::from_parts(62_123, 0).saturating_mul(l.into())) + // Standard Error: 1_841 + .saturating_add(Weight::from_parts(56_463, 0).saturating_mul(s.into())) .saturating_add(RocksDbWeight::get().reads(3_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) } @@ -382,14 +385,14 @@ impl WeightInfo for () { /// The range of component `s` is `[2, 28]`. fn unlocking_merge_schedules(l: u32, s: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `577 + l * (25 ±0) + s * (36 ±0)` - // Estimated: `9909` - // Minimum execution time: 30_413 nanoseconds. - Weight::from_parts(29_350_467, 9909) - // Standard Error: 724 - .saturating_add(Weight::from_parts(65_366, 0).saturating_mul(l.into())) - // Standard Error: 1_337 - .saturating_add(Weight::from_parts(53_799, 0).saturating_mul(s.into())) + // Measured: `482 + l * (25 ±0) + s * (36 ±0)` + // Estimated: `12879` + // Minimum execution time: 34_965_000 picoseconds. + Weight::from_parts(33_831_484, 12879) + // Standard Error: 1_530 + .saturating_add(Weight::from_parts(59_136, 0).saturating_mul(l.into())) + // Standard Error: 2_827 + .saturating_add(Weight::from_parts(58_493, 0).saturating_mul(s.into())) .saturating_add(RocksDbWeight::get().reads(3_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) } diff --git a/frame/whitelist/src/weights.rs b/frame/whitelist/src/weights.rs index 667d602a3..536cd1887 100644 --- a/frame/whitelist/src/weights.rs +++ b/frame/whitelist/src/weights.rs @@ -18,9 +18,9 @@ //! Autogenerated weights for pallet_whitelist //! //! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev -//! DATE: 2023-01-25, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! DATE: 2023-03-16, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` //! WORST CASE MAP SIZE: `1000000` -//! HOSTNAME: `bm2`, CPU: `Intel(R) Core(TM) i7-7700K CPU @ 4.20GHz` +//! HOSTNAME: `bm3`, CPU: `Intel(R) Core(TM) i7-7700K CPU @ 4.20GHz` //! EXECUTION: Some(Wasm), WASM-EXECUTION: Compiled, CHAIN: Some("dev"), DB CACHE: 1024 // Executed Command: @@ -31,6 +31,9 @@ // --steps=50 // --repeat=20 // --pallet=pallet_whitelist +// --no-storage-info +// --no-median-slopes +// --no-min-squares // --extrinsic=* // --execution=wasm // --wasm-execution=compiled @@ -64,9 +67,9 @@ impl WeightInfo for SubstrateWeight { fn whitelist_call() -> Weight { // Proof Size summary in bytes: // Measured: `217` - // Estimated: `5081` - // Minimum execution time: 18_618 nanoseconds. - Weight::from_parts(19_133_000, 5081) + // Estimated: `7061` + // Minimum execution time: 21_530_000 picoseconds. + Weight::from_parts(21_986_000, 7061) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().writes(2_u64)) } @@ -77,9 +80,9 @@ impl WeightInfo for SubstrateWeight { fn remove_whitelisted_call() -> Weight { // Proof Size summary in bytes: // Measured: `346` - // Estimated: `5081` - // Minimum execution time: 16_685 nanoseconds. - Weight::from_parts(17_325_000, 5081) + // Estimated: `7061` + // Minimum execution time: 18_980_000 picoseconds. + Weight::from_parts(19_474_000, 7061) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().writes(2_u64)) } @@ -92,12 +95,12 @@ impl WeightInfo for SubstrateWeight { /// The range of component `n` is `[1, 4194294]`. fn dispatch_whitelisted_call(n: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `454 + n * (1 ±0)` - // Estimated: `8007 + n * (1 ±0)` - // Minimum execution time: 27_539 nanoseconds. - Weight::from_parts(27_950_000, 8007) + // Measured: `422 + n * (1 ±0)` + // Estimated: `10947 + n * (1 ±0)` + // Minimum execution time: 32_377_000 picoseconds. + Weight::from_parts(32_643_000, 10947) // Standard Error: 0 - .saturating_add(Weight::from_parts(1_134, 0).saturating_mul(n.into())) + .saturating_add(Weight::from_parts(1_147, 0).saturating_mul(n.into())) .saturating_add(T::DbWeight::get().reads(3_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) .saturating_add(Weight::from_parts(0, 1).saturating_mul(n.into())) @@ -110,11 +113,11 @@ impl WeightInfo for SubstrateWeight { fn dispatch_whitelisted_call_with_preimage(n: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `346` - // Estimated: `5081` - // Minimum execution time: 20_581 nanoseconds. - Weight::from_parts(21_762_318, 5081) - // Standard Error: 4 - .saturating_add(Weight::from_parts(1_480, 0).saturating_mul(n.into())) + // Estimated: `7061` + // Minimum execution time: 23_421_000 picoseconds. + Weight::from_parts(24_488_523, 7061) + // Standard Error: 5 + .saturating_add(Weight::from_parts(1_217, 0).saturating_mul(n.into())) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().writes(2_u64)) } @@ -129,9 +132,9 @@ impl WeightInfo for () { fn whitelist_call() -> Weight { // Proof Size summary in bytes: // Measured: `217` - // Estimated: `5081` - // Minimum execution time: 18_618 nanoseconds. - Weight::from_parts(19_133_000, 5081) + // Estimated: `7061` + // Minimum execution time: 21_530_000 picoseconds. + Weight::from_parts(21_986_000, 7061) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().writes(2_u64)) } @@ -142,9 +145,9 @@ impl WeightInfo for () { fn remove_whitelisted_call() -> Weight { // Proof Size summary in bytes: // Measured: `346` - // Estimated: `5081` - // Minimum execution time: 16_685 nanoseconds. - Weight::from_parts(17_325_000, 5081) + // Estimated: `7061` + // Minimum execution time: 18_980_000 picoseconds. + Weight::from_parts(19_474_000, 7061) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().writes(2_u64)) } @@ -157,12 +160,12 @@ impl WeightInfo for () { /// The range of component `n` is `[1, 4194294]`. fn dispatch_whitelisted_call(n: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `454 + n * (1 ±0)` - // Estimated: `8007 + n * (1 ±0)` - // Minimum execution time: 27_539 nanoseconds. - Weight::from_parts(27_950_000, 8007) + // Measured: `422 + n * (1 ±0)` + // Estimated: `10947 + n * (1 ±0)` + // Minimum execution time: 32_377_000 picoseconds. + Weight::from_parts(32_643_000, 10947) // Standard Error: 0 - .saturating_add(Weight::from_parts(1_134, 0).saturating_mul(n.into())) + .saturating_add(Weight::from_parts(1_147, 0).saturating_mul(n.into())) .saturating_add(RocksDbWeight::get().reads(3_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) .saturating_add(Weight::from_parts(0, 1).saturating_mul(n.into())) @@ -175,11 +178,11 @@ impl WeightInfo for () { fn dispatch_whitelisted_call_with_preimage(n: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `346` - // Estimated: `5081` - // Minimum execution time: 20_581 nanoseconds. - Weight::from_parts(21_762_318, 5081) - // Standard Error: 4 - .saturating_add(Weight::from_parts(1_480, 0).saturating_mul(n.into())) + // Estimated: `7061` + // Minimum execution time: 23_421_000 picoseconds. + Weight::from_parts(24_488_523, 7061) + // Standard Error: 5 + .saturating_add(Weight::from_parts(1_217, 0).saturating_mul(n.into())) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().writes(2_u64)) } From aaa9a3631d29f757552ffcc8b97aa7091c0b27b0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alexander=20Thei=C3=9Fen?= Date: Thu, 16 Mar 2023 20:19:48 +0100 Subject: [PATCH 248/558] contracts: Simplify benchmarks (#13595) * Remove batching * Benchmark in bytes not kilobytes * ".git/.scripts/commands/bench/bench.sh" pallet dev pallet_contracts * ".git/.scripts/commands/bench/bench.sh" pallet dev pallet_contracts * ".git/.scripts/commands/bench/bench.sh" pallet dev pallet_contracts * ".git/.scripts/commands/bench/bench.sh" pallet dev pallet_contracts * Add rationale for picking batch numbers --------- Co-authored-by: command-bot <> --- frame/contracts/src/benchmarking/code.rs | 2 +- frame/contracts/src/benchmarking/mod.rs | 789 +++-- frame/contracts/src/schedule.rs | 222 +- frame/contracts/src/wasm/prepare.rs | 2 +- frame/contracts/src/weights.rs | 3383 +++++++++++----------- 5 files changed, 2092 insertions(+), 2306 deletions(-) diff --git a/frame/contracts/src/benchmarking/code.rs b/frame/contracts/src/benchmarking/code.rs index aa44cc397..96eb5e501 100644 --- a/frame/contracts/src/benchmarking/code.rs +++ b/frame/contracts/src/benchmarking/code.rs @@ -512,7 +512,7 @@ pub fn max_pages() -> u32 { fn inject_gas_metering(module: Module) -> Module { let schedule = T::Schedule::get(); - let gas_rules = schedule.rules(&module, Determinism::Deterministic); + let gas_rules = schedule.rules(Determinism::Deterministic); let backend = gas_metering::host_function::Injector::new("seal0", "gas"); gas_metering::inject(module, backend, &gas_rules).unwrap() } diff --git a/frame/contracts/src/benchmarking/mod.rs b/frame/contracts/src/benchmarking/mod.rs index c18d9ffb4..873314981 100644 --- a/frame/contracts/src/benchmarking/mod.rs +++ b/frame/contracts/src/benchmarking/mod.rs @@ -31,7 +31,6 @@ use self::{ }; use crate::{ exec::{AccountIdOf, FixSizedKey, VarSizedKey}, - schedule::{API_BENCHMARK_BATCH_SIZE, INSTR_BENCHMARK_BATCH_SIZE}, wasm::CallFlags, Pallet as Contracts, *, }; @@ -47,10 +46,17 @@ use sp_std::prelude::*; use wasm_instrument::parity_wasm::elements::{BlockType, BrTableData, Instruction, ValueType}; /// How many batches we do per API benchmark. -const API_BENCHMARK_BATCHES: u32 = 20; +/// +/// This is picked more or less arbitrary. We experimented with different numbers until +/// the results appeared to be stable. Reducing the number would speed up the benchmarks +/// but might make the results less precise. +const API_BENCHMARK_BATCHES: u32 = 1600; -/// How many batches we do per Instruction benchmark. -const INSTR_BENCHMARK_BATCHES: u32 = 50; +/// How many batches we do per instruction benchmark. +/// +/// Same rationale as for [`API_BENCHMARK_BATCHES`]. The number is bigger because instruction +/// benchmarks are faster. +const INSTR_BENCHMARK_BATCHES: u32 = 5000; /// An instantiated and deployed contract. struct Contract { @@ -244,7 +250,7 @@ benchmarks! { // the sandbox. This does **not** include the actual execution for which the gas meter // is responsible. This is achieved by generating all code to the `deploy` function // which is in the wasm module but not executed on `call`. - // The results are supposed to be used as `call_with_code_kb(c) - call_with_code_kb(0)`. + // The results are supposed to be used as `call_with_code_per_byte(c) - call_with_code_per_byte(0)`. #[pov_mode = Measured] call_with_code_per_byte { let c in 0 .. T::MaxCodeLen::get(); @@ -263,9 +269,9 @@ benchmarks! { // we don't benchmark the actual execution of this code but merely what it takes to load // a code of that size into the sandbox. // - // `c`: Size of the code in kilobytes. - // `i`: Size of the input in kilobytes. - // `s`: Size of the salt in kilobytes. + // `c`: Size of the code in bytes. + // `i`: Size of the input in bytes. + // `s`: Size of the salt in bytes. // // # Note // @@ -299,8 +305,8 @@ benchmarks! { } // Instantiate uses a dummy contract constructor to measure the overhead of the instantiate. - // `i`: Size of the input in kilobytes. - // `s`: Size of the salt in kilobytes. + // `i`: Size of the input in bytes. + // `s`: Size of the salt in bytes. #[pov_mode = Measured] instantiate { let i in 0 .. code::max_pages::() * 64 * 1024; @@ -333,7 +339,7 @@ benchmarks! { // The dummy contract used here does not do this. The costs for the data copy is billed as // part of `seal_input`. The costs for invoking a contract of a specific size are not part // of this benchmark because we cannot know the size of the contract when issuing a call - // transaction. See `invoke_per_code_kb` for this. + // transaction. See `call_with_code_per_byte` for this. #[pov_mode = Measured] call { let data = vec![42u8; 1024]; @@ -362,7 +368,7 @@ benchmarks! { // This constructs a contract that is maximal expensive to instrument. // It creates a maximum number of metering blocks per byte. - // `c`: Size of the code in kilobytes. + // `c`: Size of the code in bytes. // // # Note // @@ -421,7 +427,7 @@ benchmarks! { seal_caller { let r in 0 .. API_BENCHMARK_BATCHES; let instance = Contract::::new(WasmModule::getter( - "seal0", "seal_caller", r * API_BENCHMARK_BATCH_SIZE + "seal0", "seal_caller", r ), vec![])?; let origin = RawOrigin::Signed(instance.caller.clone()); }: call(origin, instance.addr, 0u32.into(), Weight::MAX, None, vec![]) @@ -429,7 +435,7 @@ benchmarks! { #[pov_mode = Measured] seal_is_contract { let r in 0 .. API_BENCHMARK_BATCHES; - let accounts = (0 .. r * API_BENCHMARK_BATCH_SIZE) + let accounts = (0 .. r) .map(|n| account::("account", n, 0)) .collect::>(); let account_len = accounts.get(0).map(|i| i.encode().len()).unwrap_or(0); @@ -448,7 +454,7 @@ benchmarks! { value: accounts_bytes }, ], - call_body: Some(body::repeated_dyn(r * API_BENCHMARK_BATCH_SIZE, vec![ + call_body: Some(body::repeated_dyn(r, vec![ Counter(0, account_len as u32), // address_ptr Regular(Instruction::Call(0)), Regular(Instruction::Drop), @@ -467,7 +473,7 @@ benchmarks! { #[pov_mode = Measured] seal_code_hash { let r in 0 .. API_BENCHMARK_BATCHES; - let accounts = (0 .. r * API_BENCHMARK_BATCH_SIZE) + let accounts = (0 .. r) .map(|n| account::("account", n, 0)) .collect::>(); let account_len = accounts.get(0).map(|i| i.encode().len()).unwrap_or(0); @@ -492,7 +498,7 @@ benchmarks! { value: accounts_bytes, }, ], - call_body: Some(body::repeated_dyn(r * API_BENCHMARK_BATCH_SIZE, vec![ + call_body: Some(body::repeated_dyn(r, vec![ Counter(36, account_len as u32), // address_ptr Regular(Instruction::I32Const(4)), // ptr to output data Regular(Instruction::I32Const(0)), // ptr to output length @@ -514,7 +520,7 @@ benchmarks! { seal_own_code_hash { let r in 0 .. API_BENCHMARK_BATCHES; let instance = Contract::::new(WasmModule::getter( - "seal0", "seal_own_code_hash", r * API_BENCHMARK_BATCH_SIZE + "seal0", "seal_own_code_hash", r ), vec![])?; let origin = RawOrigin::Signed(instance.caller.clone()); }: call(origin, instance.addr, 0u32.into(), Weight::MAX, None, vec![]) @@ -530,7 +536,7 @@ benchmarks! { params: vec![], return_type: Some(ValueType::I32), }], - call_body: Some(body::repeated(r * API_BENCHMARK_BATCH_SIZE, &[ + call_body: Some(body::repeated(r, &[ Instruction::Call(0), Instruction::Drop, ])), @@ -544,7 +550,7 @@ benchmarks! { seal_address { let r in 0 .. API_BENCHMARK_BATCHES; let instance = Contract::::new(WasmModule::getter( - "seal0", "seal_address", r * API_BENCHMARK_BATCH_SIZE + "seal0", "seal_address", r ), vec![])?; let origin = RawOrigin::Signed(instance.caller.clone()); }: call(origin, instance.addr, 0u32.into(), Weight::MAX, None, vec![]) @@ -553,7 +559,7 @@ benchmarks! { seal_gas_left { let r in 0 .. API_BENCHMARK_BATCHES; let instance = Contract::::new(WasmModule::getter( - "seal0", "seal_gas_left", r * API_BENCHMARK_BATCH_SIZE + "seal0", "seal_gas_left", r ), vec![])?; let origin = RawOrigin::Signed(instance.caller.clone()); }: call(origin, instance.addr, 0u32.into(), Weight::MAX, None, vec![]) @@ -562,7 +568,7 @@ benchmarks! { seal_balance { let r in 0 .. API_BENCHMARK_BATCHES; let instance = Contract::::new(WasmModule::getter( - "seal0", "seal_balance", r * API_BENCHMARK_BATCH_SIZE + "seal0", "seal_balance", r ), vec![])?; let origin = RawOrigin::Signed(instance.caller.clone()); }: call(origin, instance.addr, 0u32.into(), Weight::MAX, None, vec![]) @@ -571,7 +577,7 @@ benchmarks! { seal_value_transferred { let r in 0 .. API_BENCHMARK_BATCHES; let instance = Contract::::new(WasmModule::getter( - "seal0", "seal_value_transferred", r * API_BENCHMARK_BATCH_SIZE + "seal0", "seal_value_transferred", r ), vec![])?; let origin = RawOrigin::Signed(instance.caller.clone()); }: call(origin, instance.addr, 0u32.into(), Weight::MAX, None, vec![]) @@ -580,7 +586,7 @@ benchmarks! { seal_minimum_balance { let r in 0 .. API_BENCHMARK_BATCHES; let instance = Contract::::new(WasmModule::getter( - "seal0", "seal_minimum_balance", r * API_BENCHMARK_BATCH_SIZE + "seal0", "seal_minimum_balance", r ), vec![])?; let origin = RawOrigin::Signed(instance.caller.clone()); }: call(origin, instance.addr, 0u32.into(), Weight::MAX, None, vec![]) @@ -589,7 +595,7 @@ benchmarks! { seal_block_number { let r in 0 .. API_BENCHMARK_BATCHES; let instance = Contract::::new(WasmModule::getter( - "seal0", "seal_block_number", r * API_BENCHMARK_BATCH_SIZE + "seal0", "seal_block_number", r ), vec![])?; let origin = RawOrigin::Signed(instance.caller.clone()); }: call(origin, instance.addr, 0u32.into(), Weight::MAX, None, vec![]) @@ -598,7 +604,7 @@ benchmarks! { seal_now { let r in 0 .. API_BENCHMARK_BATCHES; let instance = Contract::::new(WasmModule::getter( - "seal0", "seal_now", r * API_BENCHMARK_BATCH_SIZE + "seal0", "seal_now", r ), vec![])?; let origin = RawOrigin::Signed(instance.caller.clone()); }: call(origin, instance.addr, 0u32.into(), Weight::MAX, None, vec![]) @@ -619,7 +625,7 @@ benchmarks! { offset: 0, value: (pages * 64 * 1024 - 4).to_le_bytes().to_vec(), }], - call_body: Some(body::repeated(r * API_BENCHMARK_BATCH_SIZE, &[ + call_body: Some(body::repeated(r, &[ Instruction::I64Const(500_000), Instruction::I32Const(4), Instruction::I32Const(0), @@ -641,7 +647,7 @@ benchmarks! { params: vec![ValueType::I64], return_type: None, }], - call_body: Some(body::repeated(r * API_BENCHMARK_BATCH_SIZE, &[ + call_body: Some(body::repeated(r, &[ Instruction::I64Const(42), Instruction::Call(0), ])), @@ -669,7 +675,7 @@ benchmarks! { value: 0u32.to_le_bytes().to_vec(), }, ], - call_body: Some(body::repeated(r * API_BENCHMARK_BATCH_SIZE, &[ + call_body: Some(body::repeated(r, &[ Instruction::I32Const(4), // ptr where to store output Instruction::I32Const(0), // ptr to length Instruction::Call(0), @@ -681,10 +687,9 @@ benchmarks! { }: call(origin, instance.addr, 0u32.into(), Weight::MAX, None, vec![]) #[pov_mode = Measured] - seal_input_per_kb { - let n in 0 .. code::max_pages::() * 64; - let pages = code::max_pages::(); - let buffer_size = pages * 64 * 1024 - 4; + seal_input_per_byte { + let n in 0 .. code::max_pages::() * 64 * 1024; + let buffer_size = code::max_pages::() * 64 * 1024 - 4; let code = WasmModule::::from(ModuleDefinition { memory: Some(ImportedMemory::max::()), imported_functions: vec![ImportedFunction { @@ -699,15 +704,16 @@ benchmarks! { value: buffer_size.to_le_bytes().to_vec(), }, ], - call_body: Some(body::repeated(API_BENCHMARK_BATCH_SIZE, &[ + call_body: Some(body::plain(vec![ Instruction::I32Const(4), // ptr where to store output Instruction::I32Const(0), // ptr to length Instruction::Call(0), + Instruction::End, ])), .. Default::default() }); let instance = Contract::::new(code, vec![])?; - let data = vec![42u8; (n * 1024).min(buffer_size) as usize]; + let data = vec![42u8; n.min(buffer_size) as usize]; let origin = RawOrigin::Signed(instance.caller.clone()); }: call(origin, instance.addr, 0u32.into(), Weight::MAX, None, data) @@ -738,8 +744,8 @@ benchmarks! { }: call(origin, instance.addr, 0u32.into(), Weight::MAX, None, vec![]) #[pov_mode = Measured] - seal_return_per_kb { - let n in 0 .. code::max_pages::() * 64; + seal_return_per_byte { + let n in 0 .. code::max_pages::() * 64 * 1024; let code = WasmModule::::from(ModuleDefinition { memory: Some(ImportedMemory::max::()), imported_functions: vec![ImportedFunction { @@ -751,7 +757,7 @@ benchmarks! { call_body: Some(body::plain(vec![ Instruction::I32Const(0), // flags Instruction::I32Const(0), // data_ptr - Instruction::I32Const((n * 1024) as i32), // data_len + Instruction::I32Const(n as i32), // data_len Instruction::Call(0), Instruction::End, ])), @@ -827,7 +833,7 @@ benchmarks! { value: (pages * 64 * 1024 - subject_len - 4).to_le_bytes().to_vec(), }, ], - call_body: Some(body::repeated(r * API_BENCHMARK_BATCH_SIZE, &[ + call_body: Some(body::repeated(r, &[ Instruction::I32Const(4), // subject_ptr Instruction::I32Const(subject_len as i32), // subject_len Instruction::I32Const((subject_len + 4) as i32), // out_ptr @@ -853,7 +859,7 @@ benchmarks! { params: vec![ValueType::I32, ValueType::I32, ValueType::I32, ValueType::I32], return_type: None, }], - call_body: Some(body::repeated(r * API_BENCHMARK_BATCH_SIZE, &[ + call_body: Some(body::repeated(r, &[ Instruction::I32Const(0), // topics_ptr Instruction::I32Const(0), // topics_len Instruction::I32Const(0), // data_ptr @@ -868,16 +874,13 @@ benchmarks! { // Benchmark the overhead that topics generate. // `t`: Number of topics - // `n`: Size of event payload in kb + // `n`: Size of event payload in bytes #[pov_mode = Measured] - seal_deposit_event_per_topic_and_kb { + seal_deposit_event_per_topic_and_byte { let t in 0 .. T::Schedule::get().limits.event_topics; - let n in 0 .. T::Schedule::get().limits.payload_len / 1024; - let mut topics = (0..API_BENCHMARK_BATCH_SIZE) - .map(|n| (n * t..n * t + t).map(|i| T::Hashing::hash_of(&i)).collect::>().encode()) - .peekable(); - let topics_len = topics.peek().map(|i| i.len()).unwrap_or(0); - let topics = topics.flatten().collect(); + let n in 0 .. T::Schedule::get().limits.payload_len; + let topics = (0..t).map(|i| T::Hashing::hash_of(&i)).collect::>().encode(); + let topics_len = topics.len(); let code = WasmModule::::from(ModuleDefinition { memory: Some(ImportedMemory::max::()), imported_functions: vec![ImportedFunction { @@ -892,12 +895,13 @@ benchmarks! { value: topics, }, ], - call_body: Some(body::repeated_dyn(API_BENCHMARK_BATCH_SIZE, vec![ - Counter(0, topics_len as u32), // topics_ptr - Regular(Instruction::I32Const(topics_len as i32)), // topics_len - Regular(Instruction::I32Const(0)), // data_ptr - Regular(Instruction::I32Const((n * 1024) as i32)), // data_len - Regular(Instruction::Call(0)), + call_body: Some(body::plain(vec![ + Instruction::I32Const(0), // topics_ptr + Instruction::I32Const(topics_len as i32), // topics_len + Instruction::I32Const(0), // data_ptr + Instruction::I32Const(n as i32), // data_len + Instruction::Call(0), + Instruction::End, ])), .. Default::default() }); @@ -919,7 +923,7 @@ benchmarks! { params: vec![ValueType::I32, ValueType::I32], return_type: Some(ValueType::I32), }], - call_body: Some(body::repeated(r * API_BENCHMARK_BATCH_SIZE, &[ + call_body: Some(body::repeated(r, &[ Instruction::I32Const(0), // value_ptr Instruction::I32Const(0), // value_len Instruction::Call(0), @@ -942,10 +946,10 @@ benchmarks! { .result?; } - seal_debug_message_per_kb { - // Vary size of input in kilobytes up to maximum allowed contract memory + seal_debug_message_per_byte { + // Vary size of input in bytes up to maximum allowed contract memory // or maximum allowed debug buffer size, whichever is less. - let i in 0 .. (T::Schedule::get().limits.memory_pages * 64).min(T::MaxDebugBufferLen::get() / 1024); + let i in 0 .. (T::Schedule::get().limits.memory_pages * 64 * 1024).min(T::MaxDebugBufferLen::get()); // We benchmark versus messages containing printable ASCII codes. // About 1Kb goes to the instrumented contract code instructions, // whereas all the space left we use for the initialization of the debug messages data. @@ -969,7 +973,7 @@ benchmarks! { ], call_body: Some(body::plain(vec![ Instruction::I32Const(0), // value_ptr - Instruction::I32Const((i * 1024) as i32), // value_len increments by i Kb + Instruction::I32Const(i as i32), // value_len Instruction::Call(0), Instruction::Drop, Instruction::End, @@ -994,15 +998,20 @@ benchmarks! { // Only the overhead of calling the function itself with minimal arguments. // The contract is a bit more complex because it needs to use different keys in order // to generate unique storage accesses. However, it is still dominated by the storage - // accesses. We store all the keys that we are about to write at beforehand + // accesses. We store something at all the keys that we are about to write to // because re-writing at an existing key is always more expensive than writing - // it at a virgin key. + // to an key with no data behind it. + // + // # Note + // + // We need to use a smaller `r` because the keys are big and writing them all into the wasm + // might exceed the code size. #[skip_meta] #[pov_mode = Measured] seal_set_storage { let r in 0 .. API_BENCHMARK_BATCHES/2; let max_key_len = T::MaxStorageKeyLen::get(); - let keys = (0 .. r * API_BENCHMARK_BATCH_SIZE) + let keys = (0 .. r) .map(|n| { let mut h = T::Hashing::hash_of(&n).as_ref().to_vec(); h.resize(max_key_len.try_into().unwrap(), n.to_le_bytes()[0]); h }) .collect::>(); @@ -1021,7 +1030,7 @@ benchmarks! { value: keys_bytes, }, ], - call_body: Some(body::repeated_dyn(r * API_BENCHMARK_BATCH_SIZE, vec![ + call_body: Some(body::repeated_dyn(r, vec![ Counter(0, max_key_len as u32), // key_ptr Regular(Instruction::I32Const(max_key_len as i32)), // key_len Regular(Instruction::I32Const(0)), // value_ptr @@ -1047,14 +1056,10 @@ benchmarks! { #[skip_meta] #[pov_mode = Measured] - seal_set_storage_per_new_kb { - let n in 0 .. T::Schedule::get().limits.payload_len / 2048; // half of the max payload_len in kb + seal_set_storage_per_new_byte { + let n in 0 .. T::Schedule::get().limits.payload_len; let max_key_len = T::MaxStorageKeyLen::get(); - let keys = (0 .. n * API_BENCHMARK_BATCH_SIZE) - .map(|n| { let mut h = T::Hashing::hash_of(&n).as_ref().to_vec(); - h.resize(max_key_len.try_into().unwrap(), n.to_le_bytes()[0]); h }) - .collect::>(); - let key_bytes = keys.iter().flatten().cloned().collect::>(); + let key = vec![0u8; max_key_len as usize]; let code = WasmModule::::from(ModuleDefinition { memory: Some(ImportedMemory::max::()), imported_functions: vec![ImportedFunction { @@ -1066,43 +1071,38 @@ benchmarks! { data_segments: vec![ DataSegment { offset: 0, - value: key_bytes, + value: key.clone(), }, ], - call_body: Some(body::repeated_dyn(API_BENCHMARK_BATCH_SIZE, vec![ - Counter(0, max_key_len as u32), // key_ptr - Regular(Instruction::I32Const(max_key_len as i32)), // key_len - Regular(Instruction::I32Const(0)), // value_ptr - Regular(Instruction::I32Const((n * 2048) as i32)), // value_len increments by 2kb up to max payload_len - Regular(Instruction::Call(0)), - Regular(Instruction::Drop), + call_body: Some(body::plain(vec![ + Instruction::I32Const(0), // key_ptr + Instruction::I32Const(max_key_len as i32), // key_len + Instruction::I32Const(0), // value_ptr + Instruction::I32Const(n as i32), // value_len + Instruction::Call(0), + Instruction::Drop, + Instruction::End, ])), .. Default::default() }); let instance = Contract::::new(code, vec![])?; let info = instance.info()?; - for key in keys { - info.write( - &VarSizedKey::::try_from(key).map_err(|e| "Key has wrong length")?, - Some(vec![]), - None, - false, - ) - .map_err(|_| "Failed to write to storage during setup.")?; - } + info.write( + &VarSizedKey::::try_from(key).map_err(|e| "Key has wrong length")?, + Some(vec![]), + None, + false, + ) + .map_err(|_| "Failed to write to storage during setup.")?; let origin = RawOrigin::Signed(instance.caller.clone()); }: call(origin, instance.addr, 0u32.into(), Weight::MAX, None, vec![]) #[skip_meta] #[pov_mode = Measured] - seal_set_storage_per_old_kb { - let n in 0 .. T::Schedule::get().limits.payload_len / 2048; // half of the max payload_len in kb + seal_set_storage_per_old_byte { + let n in 0 .. T::Schedule::get().limits.payload_len; let max_key_len = T::MaxStorageKeyLen::get(); - let keys = (0 .. n * API_BENCHMARK_BATCH_SIZE) - .map(|n| { let mut h = T::Hashing::hash_of(&n).as_ref().to_vec(); - h.resize(max_key_len.try_into().unwrap(), n.to_le_bytes()[0]); h }) - .collect::>(); - let key_bytes = keys.iter().flatten().cloned().collect::>(); + let key = vec![0u8; max_key_len as usize]; let code = WasmModule::::from(ModuleDefinition { memory: Some(ImportedMemory::max::()), imported_functions: vec![ImportedFunction { @@ -1114,30 +1114,29 @@ benchmarks! { data_segments: vec![ DataSegment { offset: 0, - value: key_bytes, + value: key.clone(), }, ], - call_body: Some(body::repeated_dyn(API_BENCHMARK_BATCH_SIZE, vec![ - Counter(0, max_key_len as u32), // key_ptr - Regular(Instruction::I32Const(max_key_len as i32)), // key_len - Regular(Instruction::I32Const(0)), // value_ptr - Regular(Instruction::I32Const(0)), // value_len is 0 as testing vs pre-existing value len - Regular(Instruction::Call(0)), - Regular(Instruction::Drop), + call_body: Some(body::plain(vec![ + Instruction::I32Const(0), // key_ptr + Instruction::I32Const(max_key_len as i32), // key_len + Instruction::I32Const(0), // value_ptr + Instruction::I32Const(0), // value_len is 0 as testing vs pre-existing value len + Instruction::Call(0), + Instruction::Drop, + Instruction::End, ])), .. Default::default() }); let instance = Contract::::new(code, vec![])?; let info = instance.info()?; - for key in keys { - info.write( - &VarSizedKey::::try_from(key).map_err(|e| "Key has wrong length")?, - Some(vec![42u8; (n * 2048) as usize]), // value_len increments by 2kb up to max payload_len - None, - false, - ) - .map_err(|_| "Failed to write to storage during setup.")?; - } + info.write( + &VarSizedKey::::try_from(key).map_err(|e| "Key has wrong length")?, + Some(vec![42u8; n as usize]), + None, + false, + ) + .map_err(|_| "Failed to write to storage during setup.")?; let origin = RawOrigin::Signed(instance.caller.clone()); }: call(origin, instance.addr, 0u32.into(), Weight::MAX, None, vec![]) @@ -1150,7 +1149,7 @@ benchmarks! { seal_clear_storage { let r in 0 .. API_BENCHMARK_BATCHES/2; let max_key_len = T::MaxStorageKeyLen::get(); - let keys = (0 .. r * API_BENCHMARK_BATCH_SIZE) + let keys = (0 .. r) .map(|n| { let mut h = T::Hashing::hash_of(&n).as_ref().to_vec(); h.resize(max_key_len.try_into().unwrap(), n.to_le_bytes()[0]); h }) .collect::>(); @@ -1169,7 +1168,7 @@ benchmarks! { value: key_bytes, }, ], - call_body: Some(body::repeated_dyn(r * API_BENCHMARK_BATCH_SIZE, vec![ + call_body: Some(body::repeated_dyn(r, vec![ Counter(0, max_key_len as u32), // key_ptr Regular(Instruction::I32Const(max_key_len as i32)), // key_len Regular(Instruction::Call(0)), @@ -1194,14 +1193,10 @@ benchmarks! { #[skip_meta] #[pov_mode = Measured] - seal_clear_storage_per_kb { - let n in 0 .. T::Schedule::get().limits.payload_len / 2048; // half of the max payload_len in kb + seal_clear_storage_per_byte { + let n in 0 .. T::Schedule::get().limits.payload_len; let max_key_len = T::MaxStorageKeyLen::get(); - let keys = (0 .. n * API_BENCHMARK_BATCH_SIZE) - .map(|n| { let mut h = T::Hashing::hash_of(&n).as_ref().to_vec(); - h.resize(max_key_len.try_into().unwrap(), n.to_le_bytes()[0]); h }) - .collect::>(); - let key_bytes = keys.iter().flatten().cloned().collect::>(); + let key = vec![0u8; max_key_len as usize]; let code = WasmModule::::from(ModuleDefinition { memory: Some(ImportedMemory::max::()), imported_functions: vec![ImportedFunction { @@ -1213,28 +1208,27 @@ benchmarks! { data_segments: vec![ DataSegment { offset: 0, - value: key_bytes, + value: key.clone(), }, ], - call_body: Some(body::repeated_dyn(API_BENCHMARK_BATCH_SIZE, vec![ - Counter(0, max_key_len as u32), // key_ptr - Regular(Instruction::I32Const(max_key_len as i32)), // key_len - Regular(Instruction::Call(0)), - Regular(Instruction::Drop), + call_body: Some(body::plain(vec![ + Instruction::I32Const(0), // key_ptr + Instruction::I32Const(max_key_len as i32), // key_len + Instruction::Call(0), + Instruction::Drop, + Instruction::End, ])), .. Default::default() }); let instance = Contract::::new(code, vec![])?; let info = instance.info()?; - for key in keys { - info.write( - &VarSizedKey::::try_from(key).map_err(|e| "Key has wrong length")?, - Some(vec![42u8; (n * 2048) as usize]), // value_len increments by 2kb up to max payload_len - None, - false, - ) - .map_err(|_| "Failed to write to storage during setup.")?; - } + info.write( + &VarSizedKey::::try_from(key).map_err(|e| "Key has wrong length")?, + Some(vec![42u8; n as usize]), + None, + false, + ) + .map_err(|_| "Failed to write to storage during setup.")?; let origin = RawOrigin::Signed(instance.caller.clone()); }: call(origin, instance.addr, 0u32.into(), Weight::MAX, None, vec![]) @@ -1244,7 +1238,7 @@ benchmarks! { seal_get_storage { let r in 0 .. API_BENCHMARK_BATCHES/2; let max_key_len = T::MaxStorageKeyLen::get(); - let keys = (0 .. r * API_BENCHMARK_BATCH_SIZE) + let keys = (0 .. r) .map(|n| { let mut h = T::Hashing::hash_of(&n).as_ref().to_vec(); h.resize(max_key_len.try_into().unwrap(), n.to_le_bytes()[0]); h }) .collect::>(); @@ -1268,8 +1262,8 @@ benchmarks! { value: T::Schedule::get().limits.payload_len.to_le_bytes().into(), }, ], - call_body: Some(body::repeated_dyn(r * API_BENCHMARK_BATCH_SIZE, vec![ - Counter(0, max_key_len as u32), // key_ptr + call_body: Some(body::repeated_dyn(r, vec![ + Counter(0, max_key_len), // key_ptr Regular(Instruction::I32Const(max_key_len as i32)), // key_len Regular(Instruction::I32Const((key_bytes_len + 4) as i32)), // out_ptr Regular(Instruction::I32Const(key_bytes_len as i32)), // out_len_ptr @@ -1295,15 +1289,10 @@ benchmarks! { #[skip_meta] #[pov_mode = Measured] - seal_get_storage_per_kb { - let n in 0 .. T::Schedule::get().limits.payload_len / 2048; // half of the max payload_len in kb + seal_get_storage_per_byte { + let n in 0 .. T::Schedule::get().limits.payload_len; let max_key_len = T::MaxStorageKeyLen::get(); - let keys = (0 .. n * API_BENCHMARK_BATCH_SIZE) - .map(|n| { let mut h = T::Hashing::hash_of(&n).as_ref().to_vec(); - h.resize(max_key_len.try_into().unwrap(), n.to_le_bytes()[0]); h }) - .collect::>(); - let key_bytes = keys.iter().flatten().cloned().collect::>(); - let key_bytes_len = key_bytes.len(); + let key = vec![0u8; max_key_len as usize]; let code = WasmModule::::from(ModuleDefinition { memory: Some(ImportedMemory::max::()), imported_functions: vec![ImportedFunction { @@ -1315,34 +1304,33 @@ benchmarks! { data_segments: vec![ DataSegment { offset: 0, - value: key_bytes, + value: key.clone(), }, DataSegment { - offset: key_bytes_len as u32, + offset: max_key_len, value: T::Schedule::get().limits.payload_len.to_le_bytes().into(), }, ], - call_body: Some(body::repeated_dyn(API_BENCHMARK_BATCH_SIZE, vec![ - Counter(0, max_key_len as u32), // key_ptr - Regular(Instruction::I32Const(max_key_len as i32)), // key_len - Regular(Instruction::I32Const((key_bytes_len + 4) as i32)), // out_ptr - Regular(Instruction::I32Const(key_bytes_len as i32)), // out_len_ptr - Regular(Instruction::Call(0)), - Regular(Instruction::Drop), + call_body: Some(body::plain(vec![ + Instruction::I32Const(0), // key_ptr + Instruction::I32Const(max_key_len as i32), // key_len + Instruction::I32Const((max_key_len + 4) as i32), // out_ptr + Instruction::I32Const(max_key_len as i32), // out_len_ptr + Instruction::Call(0), + Instruction::Drop, + Instruction::End, ])), .. Default::default() }); let instance = Contract::::new(code, vec![])?; let info = instance.info()?; - for key in keys { - info.write( - &VarSizedKey::::try_from(key).map_err(|e| "Key has wrong length")?, - Some(vec![42u8; (n * 2048) as usize]), // value_len increments by 2kb up to max payload_len - None, - false, - ) - .map_err(|_| "Failed to write to storage during setup.")?; - } + info.write( + &VarSizedKey::::try_from(key).map_err(|e| "Key has wrong length")?, + Some(vec![42u8; n as usize]), + None, + false, + ) + .map_err(|_| "Failed to write to storage during setup.")?; >::insert(&instance.account_id, info); let origin = RawOrigin::Signed(instance.caller.clone()); }: call(origin, instance.addr, 0u32.into(), Weight::MAX, None, vec![]) @@ -1353,7 +1341,7 @@ benchmarks! { seal_contains_storage { let r in 0 .. API_BENCHMARK_BATCHES/2; let max_key_len = T::MaxStorageKeyLen::get(); - let keys = (0 .. r * API_BENCHMARK_BATCH_SIZE) + let keys = (0 .. r) .map(|n| { let mut h = T::Hashing::hash_of(&n).as_ref().to_vec(); h.resize(max_key_len.try_into().unwrap(), n.to_le_bytes()[0]); h }) .collect::>(); @@ -1373,7 +1361,7 @@ benchmarks! { value: key_bytes, }, ], - call_body: Some(body::repeated_dyn(r * API_BENCHMARK_BATCH_SIZE, vec![ + call_body: Some(body::repeated_dyn(r, vec![ Counter(0, max_key_len as u32), // key_ptr Regular(Instruction::I32Const(max_key_len as i32)), // key_len Regular(Instruction::Call(0)), @@ -1398,14 +1386,10 @@ benchmarks! { #[skip_meta] #[pov_mode = Measured] - seal_contains_storage_per_kb { - let n in 0 .. T::Schedule::get().limits.payload_len / 2048; // half of the max payload_len in kb + seal_contains_storage_per_byte { + let n in 0 .. T::Schedule::get().limits.payload_len; let max_key_len = T::MaxStorageKeyLen::get(); - let keys = (0 .. n * API_BENCHMARK_BATCH_SIZE) - .map(|n| { let mut h = T::Hashing::hash_of(&n).as_ref().to_vec(); - h.resize(max_key_len.try_into().unwrap(), n.to_le_bytes()[0]); h }) - .collect::>(); - let key_bytes = keys.iter().flatten().cloned().collect::>(); + let key = vec![0u8; max_key_len as usize]; let code = WasmModule::::from(ModuleDefinition { memory: Some(ImportedMemory::max::()), imported_functions: vec![ImportedFunction { @@ -1417,28 +1401,27 @@ benchmarks! { data_segments: vec![ DataSegment { offset: 0, - value: key_bytes, + value: key.clone(), }, ], - call_body: Some(body::repeated_dyn(API_BENCHMARK_BATCH_SIZE, vec![ - Counter(0, max_key_len as u32), // key_ptr - Regular(Instruction::I32Const(max_key_len as i32)), // key_len - Regular(Instruction::Call(0)), - Regular(Instruction::Drop), + call_body: Some(body::plain(vec![ + Instruction::I32Const(0), // key_ptr + Instruction::I32Const(max_key_len as i32), // key_len + Instruction::Call(0), + Instruction::Drop, + Instruction::End, ])), .. Default::default() }); let instance = Contract::::new(code, vec![])?; let info = instance.info()?; - for key in keys { - info.write( - &VarSizedKey::::try_from(key).map_err(|e| "Key has wrong length")?, - Some(vec![42u8; (n * 2048) as usize]), // value_len increments by 2kb up to max payload_len - None, - false, - ) - .map_err(|_| "Failed to write to storage during setup.")?; - } + info.write( + &VarSizedKey::::try_from(key).map_err(|e| "Key has wrong length")?, + Some(vec![42u8; n as usize]), + None, + false, + ) + .map_err(|_| "Failed to write to storage during setup.")?; >::insert(&instance.account_id, info); let origin = RawOrigin::Signed(instance.caller.clone()); }: call(origin, instance.addr, 0u32.into(), Weight::MAX, None, vec![]) @@ -1448,7 +1431,7 @@ benchmarks! { seal_take_storage { let r in 0 .. API_BENCHMARK_BATCHES/2; let max_key_len = T::MaxStorageKeyLen::get(); - let keys = (0 .. r * API_BENCHMARK_BATCH_SIZE) + let keys = (0 .. r) .map(|n| { let mut h = T::Hashing::hash_of(&n).as_ref().to_vec(); h.resize(max_key_len.try_into().unwrap(), n.to_le_bytes()[0]); h }) .collect::>(); @@ -1472,7 +1455,7 @@ benchmarks! { value: T::Schedule::get().limits.payload_len.to_le_bytes().into(), }, ], - call_body: Some(body::repeated_dyn(r * API_BENCHMARK_BATCH_SIZE, vec![ + call_body: Some(body::repeated_dyn(r, vec![ Counter(0, max_key_len as u32), // key_ptr Regular(Instruction::I32Const(max_key_len as i32)), // key_len Regular(Instruction::I32Const((key_bytes_len + 4) as i32)), // out_ptr @@ -1499,15 +1482,10 @@ benchmarks! { #[skip_meta] #[pov_mode = Measured] - seal_take_storage_per_kb { - let n in 0 .. T::Schedule::get().limits.payload_len / 2048; // half of the max payload_len in kb + seal_take_storage_per_byte { + let n in 0 .. T::Schedule::get().limits.payload_len; let max_key_len = T::MaxStorageKeyLen::get(); - let keys = (0 .. n * API_BENCHMARK_BATCH_SIZE) - .map(|n| { let mut h = T::Hashing::hash_of(&n).as_ref().to_vec(); - h.resize(max_key_len.try_into().unwrap(), n.to_le_bytes()[0]); h }) - .collect::>(); - let key_bytes = keys.iter().flatten().cloned().collect::>(); - let key_bytes_len = key_bytes.len(); + let key = vec![0u8; max_key_len as usize]; let code = WasmModule::::from(ModuleDefinition { memory: Some(ImportedMemory::max::()), imported_functions: vec![ImportedFunction { @@ -1519,34 +1497,33 @@ benchmarks! { data_segments: vec![ DataSegment { offset: 0, - value: key_bytes, + value: key.clone(), }, DataSegment { - offset: key_bytes_len as u32, + offset: max_key_len, value: T::Schedule::get().limits.payload_len.to_le_bytes().into(), }, ], - call_body: Some(body::repeated_dyn(API_BENCHMARK_BATCH_SIZE, vec![ - Counter(0, max_key_len as u32), // key_ptr - Regular(Instruction::I32Const(max_key_len as i32)), // key_len - Regular(Instruction::I32Const((key_bytes_len + 4) as i32)), // out_ptr - Regular(Instruction::I32Const(key_bytes_len as i32)), // out_len_ptr - Regular(Instruction::Call(0)), - Regular(Instruction::Drop), + call_body: Some(body::plain(vec![ + Instruction::I32Const(0), // key_ptr + Instruction::I32Const(max_key_len as i32), // key_len + Instruction::I32Const((max_key_len + 4) as i32), // out_ptr + Instruction::I32Const(max_key_len as i32), // out_len_ptr + Instruction::Call(0), + Instruction::Drop, + Instruction::End, ])), .. Default::default() }); let instance = Contract::::new(code, vec![])?; let info = instance.info()?; - for key in keys { - info.write( - &VarSizedKey::::try_from(key).map_err(|e| "Key has wrong length")?, - Some(vec![42u8; (n * 2048) as usize]), // value_len increments by 2kb up to max payload_len - None, - false, - ) - .map_err(|_| "Failed to write to storage during setup.")?; - } + info.write( + &VarSizedKey::::try_from(key).map_err(|e| "Key has wrong length")?, + Some(vec![42u8; n as usize]), + None, + false, + ) + .map_err(|_| "Failed to write to storage during setup.")?; >::insert(&instance.account_id, info); let origin = RawOrigin::Signed(instance.caller.clone()); }: call(origin, instance.addr, 0u32.into(), Weight::MAX, None, vec![]) @@ -1555,7 +1532,7 @@ benchmarks! { #[pov_mode = Measured] seal_transfer { let r in 0 .. API_BENCHMARK_BATCHES; - let accounts = (0..r * API_BENCHMARK_BATCH_SIZE) + let accounts = (0..r) .map(|i| account::("receiver", i, 0)) .collect::>(); let account_len = accounts.get(0).map(|i| i.encode().len()).unwrap_or(0); @@ -1582,7 +1559,7 @@ benchmarks! { value: account_bytes, }, ], - call_body: Some(body::repeated_dyn(r * API_BENCHMARK_BATCH_SIZE, vec![ + call_body: Some(body::repeated_dyn(r, vec![ Counter(value_len as u32, account_len as u32), // account_ptr Regular(Instruction::I32Const(account_len as i32)), // account_len Regular(Instruction::I32Const(0)), // value_ptr @@ -1593,7 +1570,7 @@ benchmarks! { .. Default::default() }); let instance = Contract::::new(code, vec![])?; - instance.set_balance(value * (r * API_BENCHMARK_BATCH_SIZE + 1).into()); + instance.set_balance(value * (r + 1).into()); let origin = RawOrigin::Signed(instance.caller.clone()); for account in &accounts { assert_eq!(T::Currency::total_balance(account), 0u32.into()); @@ -1610,7 +1587,7 @@ benchmarks! { seal_call { let r in 0 .. API_BENCHMARK_BATCHES; let dummy_code = WasmModule::::dummy_with_bytes(0); - let callees = (0..r * API_BENCHMARK_BATCH_SIZE) + let callees = (0..r) .map(|i| Contract::with_index(i + 1, dummy_code.clone(), vec![])) .collect::, _>>()?; let callee_len = callees.get(0).map(|i| i.account_id.encode().len()).unwrap_or(0); @@ -1646,7 +1623,7 @@ benchmarks! { value: callee_bytes, }, ], - call_body: Some(body::repeated_dyn(r * API_BENCHMARK_BATCH_SIZE, vec![ + call_body: Some(body::repeated_dyn(r, vec![ Counter(value_len as u32, callee_len as u32), // callee_ptr Regular(Instruction::I32Const(callee_len as i32)), // callee_len Regular(Instruction::I64Const(0)), // gas @@ -1668,7 +1645,7 @@ benchmarks! { #[pov_mode = Measured] seal_delegate_call { let r in 0 .. API_BENCHMARK_BATCHES; - let hashes = (0..r * API_BENCHMARK_BATCH_SIZE) + let hashes = (0..r) .map(|i| { let code = WasmModule::::dummy_with_bytes(i); Contracts::::store_code_raw(code.code, whitelisted_caller())?; @@ -1701,7 +1678,7 @@ benchmarks! { value: hashes_bytes, }, ], - call_body: Some(body::repeated_dyn(r * API_BENCHMARK_BATCH_SIZE, vec![ + call_body: Some(body::repeated_dyn(r, vec![ Regular(Instruction::I32Const(0)), // flags Counter(hashes_offset as u32, hash_len as u32), // code_hash_ptr Regular(Instruction::I32Const(0)), // input_data_ptr @@ -1719,14 +1696,10 @@ benchmarks! { }: call(origin, callee, 0u32.into(), Weight::MAX, None, vec![]) #[pov_mode = Measured] - seal_call_per_transfer_clone_kb { + seal_call_per_transfer_clone_byte { let t in 0 .. 1; - let c in 0 .. code::max_pages::() * 64; - let callees = (0..API_BENCHMARK_BATCH_SIZE) - .map(|i| Contract::with_index(i + 1, >::dummy(), vec![])) - .collect::, _>>()?; - let callee_len = callees.get(0).map(|i| i.account_id.encode().len()).unwrap_or(0); - let callee_bytes = callees.iter().flat_map(|x| x.account_id.encode()).collect::>(); + let c in 0 .. code::max_pages::() * 64 * 1024; + let callee = Contract::with_index(5, >::dummy(), vec![])?; let value: BalanceOf = t.into(); let value_bytes = value.encode(); let value_len = value_bytes.len(); @@ -1754,33 +1727,34 @@ benchmarks! { }, DataSegment { offset: value_len as u32, - value: callee_bytes, + value: callee.account_id.encode(), }, ], - call_body: Some(body::repeated_dyn(API_BENCHMARK_BATCH_SIZE, vec![ - Regular(Instruction::I32Const(CallFlags::CLONE_INPUT.bits() as i32)), // flags - Counter(value_len as u32, callee_len as u32), // callee_ptr - Regular(Instruction::I64Const(0)), // gas - Regular(Instruction::I32Const(0)), // value_ptr - Regular(Instruction::I32Const(0)), // input_data_ptr - Regular(Instruction::I32Const(0)), // input_data_len - Regular(Instruction::I32Const(SENTINEL as i32)), // output_ptr - Regular(Instruction::I32Const(0)), // output_len_ptr - Regular(Instruction::Call(0)), - Regular(Instruction::Drop), + call_body: Some(body::plain(vec![ + Instruction::I32Const(CallFlags::CLONE_INPUT.bits() as i32), // flags + Instruction::I32Const(value_len as i32), // callee_ptr + Instruction::I64Const(0), // gas + Instruction::I32Const(0), // value_ptr + Instruction::I32Const(0), // input_data_ptr + Instruction::I32Const(0), // input_data_len + Instruction::I32Const(SENTINEL as i32), // output_ptr + Instruction::I32Const(0), // output_len_ptr + Instruction::Call(0), + Instruction::Drop, + Instruction::End, ])), .. Default::default() }); let instance = Contract::::new(code, vec![])?; let origin = RawOrigin::Signed(instance.caller.clone()); - let bytes = vec![42; (c * 1024) as usize]; + let bytes = vec![42; c as usize]; }: call(origin, instance.addr, 0u32.into(), Weight::MAX, None, bytes) // We assume that every instantiate sends at least the minimum balance. #[pov_mode = Measured] seal_instantiate { let r in 0 .. API_BENCHMARK_BATCHES; - let hashes = (0..r * API_BENCHMARK_BATCH_SIZE) + let hashes = (0..r) .map(|i| { let code = WasmModule::::from(ModuleDefinition { memory: Some(ImportedMemory::max::()), @@ -1848,7 +1822,7 @@ benchmarks! { value: addr_len.to_le_bytes().into(), }, ], - call_body: Some(body::repeated_dyn(r * API_BENCHMARK_BATCH_SIZE, vec![ + call_body: Some(body::repeated_dyn(r, vec![ Counter(hashes_offset as u32, hash_len as u32), // code_hash_ptr Regular(Instruction::I32Const(hash_len as i32)), // code_hash_len Regular(Instruction::I64Const(0)), // gas @@ -1868,7 +1842,7 @@ benchmarks! { .. Default::default() }); let instance = Contract::::new(code, vec![])?; - instance.set_balance((value + Pallet::::min_balance()) * (r * API_BENCHMARK_BATCH_SIZE + 1).into()); + instance.set_balance((value + Pallet::::min_balance()) * (r + 1).into()); let origin = RawOrigin::Signed(instance.caller.clone()); let callee = instance.addr.clone(); let addresses = hashes @@ -1892,37 +1866,24 @@ benchmarks! { } #[pov_mode = Measured] - seal_instantiate_per_transfer_input_salt_kb { + seal_instantiate_per_transfer_input_salt_byte { let t in 0 .. 1; - let i in 0 .. (code::max_pages::() - 1) * 64; - let s in 0 .. (code::max_pages::() - 1) * 64; + let i in 0 .. (code::max_pages::() - 1) * 64 * 1024; + let s in 0 .. (code::max_pages::() - 1) * 64 * 1024; let callee_code = WasmModule::::dummy(); let hash = callee_code.hash; let hash_bytes = callee_code.hash.encode(); let hash_len = hash_bytes.len(); Contracts::::store_code_raw(callee_code.code, whitelisted_caller())?; - let salts = (0..API_BENCHMARK_BATCH_SIZE).map(|x| x.encode()).collect::>(); - let salt_len = salts.get(0).map(|x| x.len()).unwrap_or(0); - let salt_bytes = salts.iter().cloned().flatten().collect::>(); - let salts_len = salt_bytes.len(); - let value: BalanceOf = t.into(); + let value: BalanceOf = t.into(); let value_bytes = value.encode(); - let value_len = value_bytes.len(); - let addr_len = T::AccountId::max_encoded_len(); - - // offsets where to place static data in contract memory - let salt_offset = 0; - let value_offset = salts_len; - let hash_offset = value_offset + value_len; - let addr_len_offset = hash_offset + hash_len; let code = WasmModule::::from(ModuleDefinition { memory: Some(ImportedMemory::max::()), imported_functions: vec![ImportedFunction { - module: "seal0", + module: "seal1", name: "seal_instantiate", params: vec![ - ValueType::I32, ValueType::I32, ValueType::I64, ValueType::I32, @@ -1934,73 +1895,63 @@ benchmarks! { ValueType::I32, ValueType::I32, ValueType::I32, - ValueType::I32, ], return_type: Some(ValueType::I32), }], data_segments: vec![ DataSegment { - offset: salt_offset as u32, - value: salt_bytes, - }, - DataSegment { - offset: value_offset as u32, - value: value_bytes, - }, - DataSegment { - offset: hash_offset as u32, + offset: 0, value: hash_bytes, }, DataSegment { - offset: addr_len_offset as u32, - value: (addr_len as u32).to_le_bytes().into(), + offset: hash_len as u32, + value: value_bytes, }, ], - call_body: Some(body::repeated_dyn(API_BENCHMARK_BATCH_SIZE, vec![ - Regular(Instruction::I32Const(hash_offset as i32)), // code_hash_ptr - Regular(Instruction::I32Const(hash_len as i32)), // code_hash_len - Regular(Instruction::I64Const(0)), // gas - Regular(Instruction::I32Const(value_offset as i32)), // value_ptr - Regular(Instruction::I32Const(value_len as i32)), // value_len - Counter(salt_offset as u32, salt_len as u32), // input_data_ptr - Regular(Instruction::I32Const((i * 1024) as i32)), // input_data_len - Regular(Instruction::I32Const((addr_len_offset + addr_len) as i32)), // address_ptr - Regular(Instruction::I32Const(addr_len_offset as i32)), // address_len_ptr - Regular(Instruction::I32Const(SENTINEL as i32)), // output_ptr - Regular(Instruction::I32Const(0)), // output_len_ptr - Counter(salt_offset as u32, salt_len as u32), // salt_ptr - Regular(Instruction::I32Const((s * 1024) as i32)), // salt_len - Regular(Instruction::Call(0)), - Regular(Instruction::I32Eqz), - Regular(Instruction::If(BlockType::NoResult)), - Regular(Instruction::Nop), - Regular(Instruction::Else), - Regular(Instruction::Unreachable), - Regular(Instruction::End), + call_body: Some(body::plain(vec![ + Instruction::I32Const(0 as i32), // code_hash_ptr + Instruction::I64Const(0), // gas + Instruction::I32Const(hash_len as i32), // value_ptr + Instruction::I32Const(0 as i32), // input_data_ptr + Instruction::I32Const(i as i32), // input_data_len + Instruction::I32Const(SENTINEL as i32), // address_ptr + Instruction::I32Const(0), // address_len_ptr + Instruction::I32Const(SENTINEL as i32), // output_ptr + Instruction::I32Const(0), // output_len_ptr + Instruction::I32Const(0 as i32), // salt_ptr + Instruction::I32Const(s as i32), // salt_len + Instruction::Call(0), + Instruction::I32Eqz, + Instruction::If(BlockType::NoResult), + Instruction::Nop, + Instruction::Else, + Instruction::Unreachable, + Instruction::End, + Instruction::End, ])), .. Default::default() }); let instance = Contract::::new(code, vec![])?; - instance.set_balance((value + Pallet::::min_balance()) * (API_BENCHMARK_BATCH_SIZE + 1).into()); + instance.set_balance(value + (Pallet::::min_balance() * 2u32.into())); let origin = RawOrigin::Signed(instance.caller.clone()); }: call(origin, instance.addr, 0u32.into(), Weight::MAX, None, vec![]) // Only the overhead of calling the function itself with minimal arguments. #[pov_mode = Measured] seal_hash_sha2_256 { - let r in 0 .. 1; + let r in 0 .. API_BENCHMARK_BATCHES; let instance = Contract::::new(WasmModule::hasher( - "seal_hash_sha2_256", r * API_BENCHMARK_BATCH_SIZE, 0, + "seal_hash_sha2_256", r, 0, ), vec![])?; let origin = RawOrigin::Signed(instance.caller.clone()); }: call(origin, instance.addr, 0u32.into(), Weight::MAX, None, vec![]) - // `n`: Input to hash in kilobytes + // `n`: Input to hash in bytes #[pov_mode = Measured] - seal_hash_sha2_256_per_kb { - let n in 0 .. code::max_pages::() * 64; + seal_hash_sha2_256_per_byte { + let n in 0 .. code::max_pages::() * 64 * 1024; let instance = Contract::::new(WasmModule::hasher( - "seal_hash_sha2_256", API_BENCHMARK_BATCH_SIZE, n * 1024, + "seal_hash_sha2_256", 1, n, ), vec![])?; let origin = RawOrigin::Signed(instance.caller.clone()); }: call(origin, instance.addr, 0u32.into(), Weight::MAX, None, vec![]) @@ -2008,19 +1959,19 @@ benchmarks! { // Only the overhead of calling the function itself with minimal arguments. #[pov_mode = Measured] seal_hash_keccak_256 { - let r in 0 .. 1; + let r in 0 .. API_BENCHMARK_BATCHES; let instance = Contract::::new(WasmModule::hasher( - "seal_hash_keccak_256", r * API_BENCHMARK_BATCH_SIZE, 0, + "seal_hash_keccak_256", r, 0, ), vec![])?; let origin = RawOrigin::Signed(instance.caller.clone()); }: call(origin, instance.addr, 0u32.into(), Weight::MAX, None, vec![]) - // `n`: Input to hash in kilobytes + // `n`: Input to hash in bytes #[pov_mode = Measured] - seal_hash_keccak_256_per_kb { - let n in 0 .. code::max_pages::() * 64; + seal_hash_keccak_256_per_byte { + let n in 0 .. code::max_pages::() * 64 * 1024; let instance = Contract::::new(WasmModule::hasher( - "seal_hash_keccak_256", API_BENCHMARK_BATCH_SIZE, n * 1024, + "seal_hash_keccak_256", 1, n, ), vec![])?; let origin = RawOrigin::Signed(instance.caller.clone()); }: call(origin, instance.addr, 0u32.into(), Weight::MAX, None, vec![]) @@ -2028,19 +1979,19 @@ benchmarks! { // Only the overhead of calling the function itself with minimal arguments. #[pov_mode = Measured] seal_hash_blake2_256 { - let r in 0 .. 1; + let r in 0 .. API_BENCHMARK_BATCHES; let instance = Contract::::new(WasmModule::hasher( - "seal_hash_blake2_256", r * API_BENCHMARK_BATCH_SIZE, 0, + "seal_hash_blake2_256", r, 0, ), vec![])?; let origin = RawOrigin::Signed(instance.caller.clone()); }: call(origin, instance.addr, 0u32.into(), Weight::MAX, None, vec![]) - // `n`: Input to hash in kilobytes + // `n`: Input to hash in bytes #[pov_mode = Measured] - seal_hash_blake2_256_per_kb { - let n in 0 .. code::max_pages::() * 64; + seal_hash_blake2_256_per_byte { + let n in 0 .. code::max_pages::() * 64 * 1024; let instance = Contract::::new(WasmModule::hasher( - "seal_hash_blake2_256", API_BENCHMARK_BATCH_SIZE, n * 1024, + "seal_hash_blake2_256", 1, n, ), vec![])?; let origin = RawOrigin::Signed(instance.caller.clone()); }: call(origin, instance.addr, 0u32.into(), Weight::MAX, None, vec![]) @@ -2048,19 +1999,19 @@ benchmarks! { // Only the overhead of calling the function itself with minimal arguments. #[pov_mode = Measured] seal_hash_blake2_128 { - let r in 0 .. 1; + let r in 0 .. API_BENCHMARK_BATCHES; let instance = Contract::::new(WasmModule::hasher( - "seal_hash_blake2_128", r * API_BENCHMARK_BATCH_SIZE, 0, + "seal_hash_blake2_128", r, 0, ), vec![])?; let origin = RawOrigin::Signed(instance.caller.clone()); }: call(origin, instance.addr, 0u32.into(), Weight::MAX, None, vec![]) - // `n`: Input to hash in kilobytes + // `n`: Input to hash in bytes #[pov_mode = Measured] - seal_hash_blake2_128_per_kb { - let n in 0 .. code::max_pages::() * 64; + seal_hash_blake2_128_per_byte { + let n in 0 .. code::max_pages::() * 64 * 1024; let instance = Contract::::new(WasmModule::hasher( - "seal_hash_blake2_128", API_BENCHMARK_BATCH_SIZE, n * 1024, + "seal_hash_blake2_128", 1, n, ), vec![])?; let origin = RawOrigin::Signed(instance.caller.clone()); }: call(origin, instance.addr, 0u32.into(), Weight::MAX, None, vec![]) @@ -2069,11 +2020,11 @@ benchmarks! { // It generates different private keys and signatures for the message "Hello world". #[pov_mode = Measured] seal_ecdsa_recover { - let r in 0 .. 1; + let r in 0 .. API_BENCHMARK_BATCHES; let message_hash = sp_io::hashing::blake2_256("Hello world".as_bytes()); let key_type = sp_core::crypto::KeyTypeId(*b"code"); - let signatures = (0..r * API_BENCHMARK_BATCH_SIZE) + let signatures = (0..r) .map(|i| { let pub_key = sp_io::crypto::ecdsa_generate(key_type, None); let sig = sp_io::crypto::ecdsa_sign_prehashed(key_type, &pub_key, &message_hash).expect("Generates signature"); @@ -2101,7 +2052,7 @@ benchmarks! { value: signatures, }, ], - call_body: Some(body::repeated_dyn(r * API_BENCHMARK_BATCH_SIZE, vec![ + call_body: Some(body::repeated_dyn(r, vec![ Counter(32, 65), // signature_ptr Regular(Instruction::I32Const(0)), // message_hash_ptr Regular(Instruction::I32Const(signatures_bytes_len + 32)), // output_len_ptr @@ -2118,9 +2069,9 @@ benchmarks! { // generated different ECDSA keys. #[pov_mode = Measured] seal_ecdsa_to_eth_address { - let r in 0 .. 1; + let r in 0 .. API_BENCHMARK_BATCHES; let key_type = sp_core::crypto::KeyTypeId(*b"code"); - let pub_keys_bytes = (0..r * API_BENCHMARK_BATCH_SIZE) + let pub_keys_bytes = (0..r) .flat_map(|_| { sp_io::crypto::ecdsa_generate(key_type, None).0 }) @@ -2140,7 +2091,7 @@ benchmarks! { value: pub_keys_bytes, }, ], - call_body: Some(body::repeated_dyn(r * API_BENCHMARK_BATCH_SIZE, vec![ + call_body: Some(body::repeated_dyn(r, vec![ Counter(0, 33), // pub_key_ptr Regular(Instruction::I32Const(pub_keys_bytes_len)), // out_ptr Regular(Instruction::Call(0)), @@ -2155,7 +2106,7 @@ benchmarks! { #[pov_mode = Measured] seal_set_code_hash { let r in 0 .. API_BENCHMARK_BATCHES; - let code_hashes = (0..r * API_BENCHMARK_BATCH_SIZE) + let code_hashes = (0..r) .map(|i| { let new_code = WasmModule::::dummy_with_bytes(i); Contracts::::store_code_raw(new_code.code, whitelisted_caller())?; @@ -2182,7 +2133,7 @@ benchmarks! { value: code_hashes_bytes, }, ], - call_body: Some(body::repeated_dyn(r * API_BENCHMARK_BATCH_SIZE, vec![ + call_body: Some(body::repeated_dyn(r, vec![ Counter(0, code_hash_len as u32), // code_hash_ptr Regular(Instruction::Call(0)), Regular(Instruction::Drop), @@ -2204,7 +2155,7 @@ benchmarks! { params: vec![], return_type: Some(ValueType::I32), }], - call_body: Some(body::repeated(r * API_BENCHMARK_BATCH_SIZE, &[ + call_body: Some(body::repeated(r, &[ Instruction::Call(0), Instruction::Drop, ])), @@ -2218,7 +2169,7 @@ benchmarks! { seal_account_reentrance_count { let r in 0 .. API_BENCHMARK_BATCHES; let dummy_code = WasmModule::::dummy_with_bytes(0); - let accounts = (0..r * API_BENCHMARK_BATCH_SIZE) + let accounts = (0..r) .map(|i| Contract::with_index(i + 1, dummy_code.clone(), vec![])) .collect::, _>>()?; let account_id_len = accounts.get(0).map(|i| i.account_id.encode().len()).unwrap_or(0); @@ -2237,7 +2188,7 @@ benchmarks! { value: account_id_bytes, }, ], - call_body: Some(body::repeated_dyn(r * API_BENCHMARK_BATCH_SIZE, vec![ + call_body: Some(body::repeated_dyn(r, vec![ Counter(0, account_id_len as u32), // account_ptr Regular(Instruction::Call(0)), Regular(Instruction::Drop), @@ -2259,7 +2210,7 @@ benchmarks! { params: vec![], return_type: Some(ValueType::I64), }], - call_body: Some(body::repeated(r * API_BENCHMARK_BATCH_SIZE, &[ + call_body: Some(body::repeated(r, &[ Instruction::Call(0), Instruction::Drop, ])), @@ -2283,7 +2234,7 @@ benchmarks! { instr_i64const { let r in 0 .. INSTR_BENCHMARK_BATCHES; let mut sbox = Sandbox::from(&WasmModule::::from(ModuleDefinition { - call_body: Some(body::repeated_dyn(r * INSTR_BENCHMARK_BATCH_SIZE, vec![ + call_body: Some(body::repeated_dyn(r, vec![ RandomI64Repeated(1), Regular(Instruction::Drop), ])), @@ -2299,7 +2250,7 @@ benchmarks! { let r in 0 .. INSTR_BENCHMARK_BATCHES; let mut sbox = Sandbox::from(&WasmModule::::from(ModuleDefinition { memory: Some(ImportedMemory::max::()), - call_body: Some(body::repeated_dyn(r * INSTR_BENCHMARK_BATCH_SIZE, vec![ + call_body: Some(body::repeated_dyn(r, vec![ RandomUnaligned(0, code::max_pages::() * 64 * 1024 - 8), Regular(Instruction::I64Load(3, 0)), Regular(Instruction::Drop), @@ -2316,7 +2267,7 @@ benchmarks! { let r in 0 .. INSTR_BENCHMARK_BATCHES; let mut sbox = Sandbox::from(&WasmModule::::from(ModuleDefinition { memory: Some(ImportedMemory::max::()), - call_body: Some(body::repeated_dyn(r * INSTR_BENCHMARK_BATCH_SIZE, vec![ + call_body: Some(body::repeated_dyn(r, vec![ RandomUnaligned(0, code::max_pages::() * 64 * 1024 - 8), RandomI64Repeated(1), Regular(Instruction::I64Store(3, 0)), @@ -2332,7 +2283,7 @@ benchmarks! { instr_select { let r in 0 .. INSTR_BENCHMARK_BATCHES; let mut sbox = Sandbox::from(&WasmModule::::from(ModuleDefinition { - call_body: Some(body::repeated_dyn(r * INSTR_BENCHMARK_BATCH_SIZE, vec![ + call_body: Some(body::repeated_dyn(r, vec![ RandomI64Repeated(1), RandomI64Repeated(1), RandomI32(0, 2), @@ -2350,7 +2301,7 @@ benchmarks! { instr_if { let r in 0 .. INSTR_BENCHMARK_BATCHES; let mut sbox = Sandbox::from(&WasmModule::::from(ModuleDefinition { - call_body: Some(body::repeated_dyn(r * INSTR_BENCHMARK_BATCH_SIZE, vec![ + call_body: Some(body::repeated_dyn(r, vec![ RandomI32(0, 2), Regular(Instruction::If(BlockType::Value(ValueType::I64))), RandomI64Repeated(1), @@ -2371,7 +2322,7 @@ benchmarks! { instr_br { let r in 0 .. INSTR_BENCHMARK_BATCHES; let mut sbox = Sandbox::from(&WasmModule::::from(ModuleDefinition { - call_body: Some(body::repeated_dyn(r * INSTR_BENCHMARK_BATCH_SIZE, vec![ + call_body: Some(body::repeated_dyn(r, vec![ Regular(Instruction::Block(BlockType::NoResult)), Regular(Instruction::Block(BlockType::NoResult)), Regular(Instruction::Block(BlockType::NoResult)), @@ -2398,7 +2349,7 @@ benchmarks! { instr_br_if { let r in 0 .. INSTR_BENCHMARK_BATCHES; let mut sbox = Sandbox::from(&WasmModule::::from(ModuleDefinition { - call_body: Some(body::repeated_dyn(r * INSTR_BENCHMARK_BATCH_SIZE, vec![ + call_body: Some(body::repeated_dyn(r, vec![ Regular(Instruction::Block(BlockType::NoResult)), Regular(Instruction::Block(BlockType::NoResult)), Regular(Instruction::Block(BlockType::NoResult)), @@ -2430,7 +2381,7 @@ benchmarks! { default: 1, }); let mut sbox = Sandbox::from(&WasmModule::::from(ModuleDefinition { - call_body: Some(body::repeated_dyn(r * INSTR_BENCHMARK_BATCH_SIZE, vec![ + call_body: Some(body::repeated_dyn(r, vec![ Regular(Instruction::Block(BlockType::NoResult)), Regular(Instruction::Block(BlockType::NoResult)), Regular(Instruction::Block(BlockType::NoResult)), @@ -2465,21 +2416,22 @@ benchmarks! { default: 0, }); let mut sbox = Sandbox::from(&WasmModule::::from(ModuleDefinition { - call_body: Some(body::repeated_dyn(INSTR_BENCHMARK_BATCH_SIZE, vec![ - Regular(Instruction::Block(BlockType::NoResult)), - Regular(Instruction::Block(BlockType::NoResult)), - Regular(Instruction::Block(BlockType::NoResult)), - RandomI32(0, (e + 1) as i32), // Make sure the default entry is also used - Regular(Instruction::BrTable(table)), - RandomI64Repeated(1), - Regular(Instruction::Drop), - Regular(Instruction::End), - RandomI64Repeated(1), - Regular(Instruction::Drop), - Regular(Instruction::End), - RandomI64Repeated(1), - Regular(Instruction::Drop), - Regular(Instruction::End), + call_body: Some(body::plain(vec![ + Instruction::Block(BlockType::NoResult), + Instruction::Block(BlockType::NoResult), + Instruction::Block(BlockType::NoResult), + Instruction::I32Const((e / 2) as i32), + Instruction::BrTable(table), + Instruction::I64Const(42), + Instruction::Drop, + Instruction::End, + Instruction::I64Const(42), + Instruction::Drop, + Instruction::End, + Instruction::I64Const(42), + Instruction::Drop, + Instruction::End, + Instruction::End, ])), .. Default::default() })); @@ -2499,7 +2451,7 @@ benchmarks! { Instruction::Drop, Instruction::End, ])), - call_body: Some(body::repeated(r * INSTR_BENCHMARK_BATCH_SIZE, &[ + call_body: Some(body::repeated(r, &[ Instruction::Call(2), // call aux ])), .. Default::default() @@ -2522,7 +2474,7 @@ benchmarks! { Instruction::Drop, Instruction::End, ])), - call_body: Some(body::repeated_dyn(r * INSTR_BENCHMARK_BATCH_SIZE, vec![ + call_body: Some(body::repeated_dyn(r, vec![ RandomI32(0, num_elements as i32), Regular(Instruction::CallIndirect(0, 0)), // we only have one sig: 0 ])), @@ -2536,39 +2488,6 @@ benchmarks! { sbox.invoke(); } - // w_instr_call_indirect_per_param = w_bench - 1 * w_param - // Calling a function indirectly causes it to go through a thunk function whose runtime - // linearly depend on the amount of parameters to this function. - // Please note that this is not necessary with a direct call. - #[pov_mode = Ignored] - instr_call_indirect_per_param { - let p in 0 .. T::Schedule::get().limits.parameters; - let num_elements = T::Schedule::get().limits.table_size; - use self::code::TableSegment; - let mut sbox = Sandbox::from(&WasmModule::::from(ModuleDefinition { - // We need to make use of the stack here in order to trigger stack height - // instrumentation. - aux_body: Some(body::plain(vec![ - Instruction::I64Const(42), - Instruction::Drop, - Instruction::End, - ])), - aux_arg_num: p, - call_body: Some(body::repeated_dyn(INSTR_BENCHMARK_BATCH_SIZE, vec![ - RandomI64Repeated(p as usize), - RandomI32(0, num_elements as i32), - Regular(Instruction::CallIndirect(p.min(1), 0)), // aux signature: 1 or 0 - ])), - table: Some(TableSegment { - num_elements, - function_index: 2, // aux - }), - .. Default::default() - })); - }: { - sbox.invoke(); - } - // w_per_local = w_bench #[pov_mode = Ignored] instr_call_per_local { @@ -2579,8 +2498,9 @@ benchmarks! { body::inject_locals(&mut aux_body, l); let mut sbox = Sandbox::from(&WasmModule::::from(ModuleDefinition { aux_body: Some(aux_body), - call_body: Some(body::repeated(INSTR_BENCHMARK_BATCH_SIZE, &[ + call_body: Some(body::plain(vec![ Instruction::Call(2), // call aux + Instruction::End, ])), .. Default::default() })); @@ -2593,7 +2513,7 @@ benchmarks! { instr_local_get { let r in 0 .. INSTR_BENCHMARK_BATCHES; let max_locals = T::Schedule::get().limits.locals; - let mut call_body = body::repeated_dyn(r * INSTR_BENCHMARK_BATCH_SIZE, vec![ + let mut call_body = body::repeated_dyn(r, vec![ RandomGetLocal(0, max_locals), Regular(Instruction::Drop), ]); @@ -2611,7 +2531,7 @@ benchmarks! { instr_local_set { let r in 0 .. INSTR_BENCHMARK_BATCHES; let max_locals = T::Schedule::get().limits.locals; - let mut call_body = body::repeated_dyn(r * INSTR_BENCHMARK_BATCH_SIZE, vec![ + let mut call_body = body::repeated_dyn(r, vec![ RandomI64Repeated(1), RandomSetLocal(0, max_locals), ]); @@ -2629,7 +2549,7 @@ benchmarks! { instr_local_tee { let r in 0 .. INSTR_BENCHMARK_BATCHES; let max_locals = T::Schedule::get().limits.locals; - let mut call_body = body::repeated_dyn(r * INSTR_BENCHMARK_BATCH_SIZE, vec![ + let mut call_body = body::repeated_dyn(r, vec![ RandomI64Repeated(1), RandomTeeLocal(0, max_locals), Regular(Instruction::Drop), @@ -2649,7 +2569,7 @@ benchmarks! { let r in 0 .. INSTR_BENCHMARK_BATCHES; let max_globals = T::Schedule::get().limits.globals; let mut sbox = Sandbox::from(&WasmModule::::from(ModuleDefinition { - call_body: Some(body::repeated_dyn(r * INSTR_BENCHMARK_BATCH_SIZE, vec![ + call_body: Some(body::repeated_dyn(r, vec![ RandomGetGlobal(0, max_globals), Regular(Instruction::Drop), ])), @@ -2666,7 +2586,7 @@ benchmarks! { let r in 0 .. INSTR_BENCHMARK_BATCHES; let max_globals = T::Schedule::get().limits.globals; let mut sbox = Sandbox::from(&WasmModule::::from(ModuleDefinition { - call_body: Some(body::repeated_dyn(r * INSTR_BENCHMARK_BATCH_SIZE, vec![ + call_body: Some(body::repeated_dyn(r, vec![ RandomI64Repeated(1), RandomSetGlobal(0, max_globals), ])), @@ -2683,7 +2603,7 @@ benchmarks! { let r in 0 .. INSTR_BENCHMARK_BATCHES; let mut sbox = Sandbox::from(&WasmModule::::from(ModuleDefinition { memory: Some(ImportedMemory::max::()), - call_body: Some(body::repeated(r * INSTR_BENCHMARK_BATCH_SIZE, &[ + call_body: Some(body::repeated(r, &[ Instruction::CurrentMemory(0), Instruction::Drop ])), @@ -2700,14 +2620,13 @@ benchmarks! { // depends on how much memory is already allocated. #[pov_mode = Ignored] instr_memory_grow { - let r in 0 .. 1; - let max_pages = ImportedMemory::max::().max_pages; + let r in 0 .. ImportedMemory::max::().max_pages; let mut sbox = Sandbox::from(&WasmModule::::from(ModuleDefinition { memory: Some(ImportedMemory { min_pages: 0, - max_pages, + max_pages: ImportedMemory::max::().max_pages, }), - call_body: Some(body::repeated(r * max_pages, &[ + call_body: Some(body::repeated(r, &[ Instruction::I32Const(1), Instruction::GrowMemory(0), Instruction::Drop, @@ -2726,7 +2645,7 @@ benchmarks! { let r in 0 .. INSTR_BENCHMARK_BATCHES; let mut sbox = Sandbox::from(&WasmModule::::unary_instr( Instruction::I64Clz, - r * INSTR_BENCHMARK_BATCH_SIZE, + r, )); }: { sbox.invoke(); @@ -2737,7 +2656,7 @@ benchmarks! { let r in 0 .. INSTR_BENCHMARK_BATCHES; let mut sbox = Sandbox::from(&WasmModule::::unary_instr( Instruction::I64Ctz, - r * INSTR_BENCHMARK_BATCH_SIZE, + r, )); }: { sbox.invoke(); @@ -2748,7 +2667,7 @@ benchmarks! { let r in 0 .. INSTR_BENCHMARK_BATCHES; let mut sbox = Sandbox::from(&WasmModule::::unary_instr( Instruction::I64Popcnt, - r * INSTR_BENCHMARK_BATCH_SIZE, + r, )); }: { sbox.invoke(); @@ -2759,7 +2678,7 @@ benchmarks! { let r in 0 .. INSTR_BENCHMARK_BATCHES; let mut sbox = Sandbox::from(&WasmModule::::unary_instr( Instruction::I64Eqz, - r * INSTR_BENCHMARK_BATCH_SIZE, + r, )); }: { sbox.invoke(); @@ -2769,7 +2688,7 @@ benchmarks! { instr_i64extendsi32 { let r in 0 .. INSTR_BENCHMARK_BATCHES; let mut sbox = Sandbox::from(&WasmModule::::from(ModuleDefinition { - call_body: Some(body::repeated_dyn(r * INSTR_BENCHMARK_BATCH_SIZE, vec![ + call_body: Some(body::repeated_dyn(r, vec![ RandomI32Repeated(1), Regular(Instruction::I64ExtendSI32), Regular(Instruction::Drop), @@ -2784,7 +2703,7 @@ benchmarks! { instr_i64extendui32 { let r in 0 .. INSTR_BENCHMARK_BATCHES; let mut sbox = Sandbox::from(&WasmModule::::from(ModuleDefinition { - call_body: Some(body::repeated_dyn(r * INSTR_BENCHMARK_BATCH_SIZE, vec![ + call_body: Some(body::repeated_dyn(r, vec![ RandomI32Repeated(1), Regular(Instruction::I64ExtendUI32), Regular(Instruction::Drop), @@ -2800,7 +2719,7 @@ benchmarks! { let r in 0 .. INSTR_BENCHMARK_BATCHES; let mut sbox = Sandbox::from(&WasmModule::::unary_instr( Instruction::I32WrapI64, - r * INSTR_BENCHMARK_BATCH_SIZE, + r, )); }: { sbox.invoke(); @@ -2814,7 +2733,7 @@ benchmarks! { let r in 0 .. INSTR_BENCHMARK_BATCHES; let mut sbox = Sandbox::from(&WasmModule::::binary_instr( Instruction::I64Eq, - r * INSTR_BENCHMARK_BATCH_SIZE, + r, )); }: { sbox.invoke(); @@ -2825,7 +2744,7 @@ benchmarks! { let r in 0 .. INSTR_BENCHMARK_BATCHES; let mut sbox = Sandbox::from(&WasmModule::::binary_instr( Instruction::I64Ne, - r * INSTR_BENCHMARK_BATCH_SIZE, + r, )); }: { sbox.invoke(); @@ -2836,7 +2755,7 @@ benchmarks! { let r in 0 .. INSTR_BENCHMARK_BATCHES; let mut sbox = Sandbox::from(&WasmModule::::binary_instr( Instruction::I64LtS, - r * INSTR_BENCHMARK_BATCH_SIZE, + r, )); }: { sbox.invoke(); @@ -2847,7 +2766,7 @@ benchmarks! { let r in 0 .. INSTR_BENCHMARK_BATCHES; let mut sbox = Sandbox::from(&WasmModule::::binary_instr( Instruction::I64LtU, - r * INSTR_BENCHMARK_BATCH_SIZE, + r, )); }: { sbox.invoke(); @@ -2858,7 +2777,7 @@ benchmarks! { let r in 0 .. INSTR_BENCHMARK_BATCHES; let mut sbox = Sandbox::from(&WasmModule::::binary_instr( Instruction::I64GtS, - r * INSTR_BENCHMARK_BATCH_SIZE, + r, )); }: { sbox.invoke(); @@ -2869,7 +2788,7 @@ benchmarks! { let r in 0 .. INSTR_BENCHMARK_BATCHES; let mut sbox = Sandbox::from(&WasmModule::::binary_instr( Instruction::I64GtU, - r * INSTR_BENCHMARK_BATCH_SIZE, + r, )); }: { sbox.invoke(); @@ -2880,7 +2799,7 @@ benchmarks! { let r in 0 .. INSTR_BENCHMARK_BATCHES; let mut sbox = Sandbox::from(&WasmModule::::binary_instr( Instruction::I64LeS, - r * INSTR_BENCHMARK_BATCH_SIZE, + r, )); }: { sbox.invoke(); @@ -2891,7 +2810,7 @@ benchmarks! { let r in 0 .. INSTR_BENCHMARK_BATCHES; let mut sbox = Sandbox::from(&WasmModule::::binary_instr( Instruction::I64LeU, - r * INSTR_BENCHMARK_BATCH_SIZE, + r, )); }: { sbox.invoke(); @@ -2902,7 +2821,7 @@ benchmarks! { let r in 0 .. INSTR_BENCHMARK_BATCHES; let mut sbox = Sandbox::from(&WasmModule::::binary_instr( Instruction::I64GeS, - r * INSTR_BENCHMARK_BATCH_SIZE, + r, )); }: { sbox.invoke(); @@ -2913,7 +2832,7 @@ benchmarks! { let r in 0 .. INSTR_BENCHMARK_BATCHES; let mut sbox = Sandbox::from(&WasmModule::::binary_instr( Instruction::I64GeU, - r * INSTR_BENCHMARK_BATCH_SIZE, + r, )); }: { sbox.invoke(); @@ -2924,7 +2843,7 @@ benchmarks! { let r in 0 .. INSTR_BENCHMARK_BATCHES; let mut sbox = Sandbox::from(&WasmModule::::binary_instr( Instruction::I64Add, - r * INSTR_BENCHMARK_BATCH_SIZE, + r, )); }: { sbox.invoke(); @@ -2935,7 +2854,7 @@ benchmarks! { let r in 0 .. INSTR_BENCHMARK_BATCHES; let mut sbox = Sandbox::from(&WasmModule::::binary_instr( Instruction::I64Sub, - r * INSTR_BENCHMARK_BATCH_SIZE, + r, )); }: { sbox.invoke(); @@ -2946,7 +2865,7 @@ benchmarks! { let r in 0 .. INSTR_BENCHMARK_BATCHES; let mut sbox = Sandbox::from(&WasmModule::::binary_instr( Instruction::I64Mul, - r * INSTR_BENCHMARK_BATCH_SIZE, + r, )); }: { sbox.invoke(); @@ -2957,7 +2876,7 @@ benchmarks! { let r in 0 .. INSTR_BENCHMARK_BATCHES; let mut sbox = Sandbox::from(&WasmModule::::binary_instr( Instruction::I64DivS, - r * INSTR_BENCHMARK_BATCH_SIZE, + r, )); }: { sbox.invoke(); @@ -2968,7 +2887,7 @@ benchmarks! { let r in 0 .. INSTR_BENCHMARK_BATCHES; let mut sbox = Sandbox::from(&WasmModule::::binary_instr( Instruction::I64DivU, - r * INSTR_BENCHMARK_BATCH_SIZE, + r, )); }: { sbox.invoke(); @@ -2979,7 +2898,7 @@ benchmarks! { let r in 0 .. INSTR_BENCHMARK_BATCHES; let mut sbox = Sandbox::from(&WasmModule::::binary_instr( Instruction::I64RemS, - r * INSTR_BENCHMARK_BATCH_SIZE, + r, )); }: { sbox.invoke(); @@ -2990,7 +2909,7 @@ benchmarks! { let r in 0 .. INSTR_BENCHMARK_BATCHES; let mut sbox = Sandbox::from(&WasmModule::::binary_instr( Instruction::I64RemU, - r * INSTR_BENCHMARK_BATCH_SIZE, + r, )); }: { sbox.invoke(); @@ -3001,7 +2920,7 @@ benchmarks! { let r in 0 .. INSTR_BENCHMARK_BATCHES; let mut sbox = Sandbox::from(&WasmModule::::binary_instr( Instruction::I64And, - r * INSTR_BENCHMARK_BATCH_SIZE, + r, )); }: { sbox.invoke(); @@ -3012,7 +2931,7 @@ benchmarks! { let r in 0 .. INSTR_BENCHMARK_BATCHES; let mut sbox = Sandbox::from(&WasmModule::::binary_instr( Instruction::I64Or, - r * INSTR_BENCHMARK_BATCH_SIZE, + r, )); }: { sbox.invoke(); @@ -3023,7 +2942,7 @@ benchmarks! { let r in 0 .. INSTR_BENCHMARK_BATCHES; let mut sbox = Sandbox::from(&WasmModule::::binary_instr( Instruction::I64Xor, - r * INSTR_BENCHMARK_BATCH_SIZE, + r, )); }: { sbox.invoke(); @@ -3034,7 +2953,7 @@ benchmarks! { let r in 0 .. INSTR_BENCHMARK_BATCHES; let mut sbox = Sandbox::from(&WasmModule::::binary_instr( Instruction::I64Shl, - r * INSTR_BENCHMARK_BATCH_SIZE, + r, )); }: { sbox.invoke(); @@ -3045,7 +2964,7 @@ benchmarks! { let r in 0 .. INSTR_BENCHMARK_BATCHES; let mut sbox = Sandbox::from(&WasmModule::::binary_instr( Instruction::I64ShrS, - r * INSTR_BENCHMARK_BATCH_SIZE, + r, )); }: { sbox.invoke(); @@ -3056,7 +2975,7 @@ benchmarks! { let r in 0 .. INSTR_BENCHMARK_BATCHES; let mut sbox = Sandbox::from(&WasmModule::::binary_instr( Instruction::I64ShrU, - r * INSTR_BENCHMARK_BATCH_SIZE, + r, )); }: { sbox.invoke(); @@ -3067,7 +2986,7 @@ benchmarks! { let r in 0 .. INSTR_BENCHMARK_BATCHES; let mut sbox = Sandbox::from(&WasmModule::::binary_instr( Instruction::I64Rotl, - r * INSTR_BENCHMARK_BATCH_SIZE, + r, )); }: { sbox.invoke(); @@ -3078,7 +2997,7 @@ benchmarks! { let r in 0 .. INSTR_BENCHMARK_BATCHES; let mut sbox = Sandbox::from(&WasmModule::::binary_instr( Instruction::I64Rotr, - r * INSTR_BENCHMARK_BATCH_SIZE, + r, )); }: { sbox.invoke(); diff --git a/frame/contracts/src/schedule.rs b/frame/contracts/src/schedule.rs index 71faf08c2..796a7f6d3 100644 --- a/frame/contracts/src/schedule.rs +++ b/frame/contracts/src/schedule.rs @@ -27,18 +27,9 @@ use scale_info::TypeInfo; #[cfg(feature = "std")] use serde::{Deserialize, Serialize}; use sp_runtime::RuntimeDebug; -use sp_std::{marker::PhantomData, vec::Vec}; +use sp_std::marker::PhantomData; use wasm_instrument::{gas_metering, parity_wasm::elements}; -/// How many API calls are executed in a single batch. The reason for increasing the amount -/// of API calls in batches (per benchmark component increase) is so that the linear regression -/// has an easier time determining the contribution of that component. -pub const API_BENCHMARK_BATCH_SIZE: u32 = 80; - -/// How many instructions are executed in a single batch. The reasoning is the same -/// as for `API_BENCHMARK_BATCH_SIZE`. -pub const INSTR_BENCHMARK_BATCH_SIZE: u32 = 100; - /// Definition of the cost schedule and other parameterizations for the wasm vm. /// /// Its [`Default`] implementation is the designated way to initialize this type. It uses @@ -200,7 +191,6 @@ pub struct InstructionWeights { pub br_table_per_entry: u32, pub call: u32, pub call_indirect: u32, - pub call_indirect_per_param: u32, pub call_per_local: u32, pub local_get: u32, pub local_set: u32, @@ -451,66 +441,22 @@ macro_rules! cost_args { } } -macro_rules! cost_batched_args { - ($name:ident, $( $arg: expr ),+) => { - cost_args!($name, $( $arg ),+) / u64::from(API_BENCHMARK_BATCH_SIZE) - } -} - -macro_rules! cost_instr_no_params_with_batch_size { - ($name:ident, $batch_size:expr) => { - (cost_args!($name, 1).ref_time() / u64::from($batch_size)) as u32 - }; -} - -macro_rules! cost_instr_with_batch_size { - ($name:ident, $num_params:expr, $batch_size:expr) => { - cost_instr_no_params_with_batch_size!($name, $batch_size).saturating_sub( - (cost_instr_no_params_with_batch_size!(instr_i64const, $batch_size) / 2) - .saturating_mul($num_params), - ) - }; -} - -macro_rules! cost_instr { - ($name:ident, $num_params:expr) => { - cost_instr_with_batch_size!($name, $num_params, INSTR_BENCHMARK_BATCH_SIZE) +macro_rules! cost_instr_no_params { + ($name:ident) => { + cost_args!($name, 1).ref_time() as u32 }; } -macro_rules! cost_byte_args { - ($name:ident, $( $arg: expr ),+) => { - cost_args!($name, $( $arg ),+) / 1024 - } -} - -macro_rules! cost_byte_batched_args { - ($name:ident, $( $arg: expr ),+) => { - cost_batched_args!($name, $( $arg ),+) / 1024 - } -} - macro_rules! cost { ($name:ident) => { cost_args!($name, 1) }; } -macro_rules! cost_batched { - ($name:ident) => { - cost_batched_args!($name, 1) - }; -} - -macro_rules! cost_byte { - ($name:ident) => { - cost_byte_args!($name, 1) - }; -} - -macro_rules! cost_byte_batched { - ($name:ident) => { - cost_byte_batched_args!($name, 1) +macro_rules! cost_instr { + ($name:ident, $num_params:expr) => { + cost_instr_no_params!($name) + .saturating_sub((cost_instr_no_params!(instr_i64const) / 2).saturating_mul($num_params)) }; } @@ -533,7 +479,6 @@ impl Default for Limits { impl Default for InstructionWeights { fn default() -> Self { - let max_pages = Limits::default().memory_pages; Self { version: 4, fallback: 0, @@ -548,7 +493,6 @@ impl Default for InstructionWeights { br_table_per_entry: cost_instr!(instr_br_table_per_entry, 0), call: cost_instr!(instr_call, 2), call_indirect: cost_instr!(instr_call_indirect, 3), - call_indirect_per_param: cost_instr!(instr_call_indirect_per_param, 0), call_per_local: cost_instr!(instr_call_per_local, 0), local_get: cost_instr!(instr_local_get, 1), local_set: cost_instr!(instr_local_set, 1), @@ -556,7 +500,7 @@ impl Default for InstructionWeights { global_get: cost_instr!(instr_global_get, 1), global_set: cost_instr!(instr_global_set, 1), memory_current: cost_instr!(instr_memory_current, 1), - memory_grow: cost_instr_with_batch_size!(instr_memory_grow, 1, max_pages), + memory_grow: cost_instr!(instr_memory_grow, 1), i64clz: cost_instr!(instr_i64clz, 2), i64ctz: cost_instr!(instr_i64ctz, 2), i64popcnt: cost_instr!(instr_i64popcnt, 2), @@ -597,89 +541,85 @@ impl Default for InstructionWeights { impl Default for HostFnWeights { fn default() -> Self { Self { - caller: cost_batched!(seal_caller), - is_contract: cost_batched!(seal_is_contract), - code_hash: cost_batched!(seal_code_hash), - own_code_hash: cost_batched!(seal_own_code_hash), - caller_is_origin: cost_batched!(seal_caller_is_origin), - address: cost_batched!(seal_address), - gas_left: cost_batched!(seal_gas_left), - balance: cost_batched!(seal_balance), - value_transferred: cost_batched!(seal_value_transferred), - minimum_balance: cost_batched!(seal_minimum_balance), - block_number: cost_batched!(seal_block_number), - now: cost_batched!(seal_now), - weight_to_fee: cost_batched!(seal_weight_to_fee), + caller: cost!(seal_caller), + is_contract: cost!(seal_is_contract), + code_hash: cost!(seal_code_hash), + own_code_hash: cost!(seal_own_code_hash), + caller_is_origin: cost!(seal_caller_is_origin), + address: cost!(seal_address), + gas_left: cost!(seal_gas_left), + balance: cost!(seal_balance), + value_transferred: cost!(seal_value_transferred), + minimum_balance: cost!(seal_minimum_balance), + block_number: cost!(seal_block_number), + now: cost!(seal_now), + weight_to_fee: cost!(seal_weight_to_fee), // Manually remove proof size from basic block cost. // // Due to imperfect benchmarking some host functions incur a small // amount of proof size. Usually this is ok. However, charging a basic block is such // a frequent operation that this would be a vast overestimation. - gas: cost_batched!(seal_gas).set_proof_size(0), - input: cost_batched!(seal_input), - input_per_byte: cost_byte_batched!(seal_input_per_kb), + gas: cost!(seal_gas).set_proof_size(0), + input: cost!(seal_input), + input_per_byte: cost!(seal_input_per_byte), r#return: cost!(seal_return), - return_per_byte: cost_byte!(seal_return_per_kb), + return_per_byte: cost!(seal_return_per_byte), terminate: cost!(seal_terminate), - random: cost_batched!(seal_random), - deposit_event: cost_batched!(seal_deposit_event), - deposit_event_per_topic: cost_batched_args!(seal_deposit_event_per_topic_and_kb, 1, 0), - deposit_event_per_byte: cost_byte_batched_args!( - seal_deposit_event_per_topic_and_kb, - 0, - 1 - ), - debug_message: cost_batched!(seal_debug_message), - debug_message_per_byte: cost_byte!(seal_debug_message_per_kb), - set_storage: cost_batched!(seal_set_storage), - set_code_hash: cost_batched!(seal_set_code_hash), - set_storage_per_new_byte: cost_byte_batched!(seal_set_storage_per_new_kb), - set_storage_per_old_byte: cost_byte_batched!(seal_set_storage_per_old_kb), - clear_storage: cost_batched!(seal_clear_storage), - clear_storage_per_byte: cost_byte_batched!(seal_clear_storage_per_kb), - contains_storage: cost_batched!(seal_contains_storage), - contains_storage_per_byte: cost_byte_batched!(seal_contains_storage_per_kb), - get_storage: cost_batched!(seal_get_storage), - get_storage_per_byte: cost_byte_batched!(seal_get_storage_per_kb), - take_storage: cost_batched!(seal_take_storage), - take_storage_per_byte: cost_byte_batched!(seal_take_storage_per_kb), - transfer: cost_batched!(seal_transfer), - call: cost_batched!(seal_call), - delegate_call: cost_batched!(seal_delegate_call), - call_transfer_surcharge: cost_batched_args!(seal_call_per_transfer_clone_kb, 1, 0), - call_per_cloned_byte: cost_byte_batched_args!(seal_call_per_transfer_clone_kb, 0, 1), - instantiate: cost_batched!(seal_instantiate), - instantiate_transfer_surcharge: cost_batched_args!( - seal_instantiate_per_transfer_input_salt_kb, + random: cost!(seal_random), + deposit_event: cost!(seal_deposit_event), + deposit_event_per_topic: cost_args!(seal_deposit_event_per_topic_and_byte, 1, 0), + deposit_event_per_byte: cost_args!(seal_deposit_event_per_topic_and_byte, 0, 1), + debug_message: cost!(seal_debug_message), + debug_message_per_byte: cost!(seal_debug_message_per_byte), + set_storage: cost!(seal_set_storage), + set_code_hash: cost!(seal_set_code_hash), + set_storage_per_new_byte: cost!(seal_set_storage_per_new_byte), + set_storage_per_old_byte: cost!(seal_set_storage_per_old_byte), + clear_storage: cost!(seal_clear_storage), + clear_storage_per_byte: cost!(seal_clear_storage_per_byte), + contains_storage: cost!(seal_contains_storage), + contains_storage_per_byte: cost!(seal_contains_storage_per_byte), + get_storage: cost!(seal_get_storage), + get_storage_per_byte: cost!(seal_get_storage_per_byte), + take_storage: cost!(seal_take_storage), + take_storage_per_byte: cost!(seal_take_storage_per_byte), + transfer: cost!(seal_transfer), + call: cost!(seal_call), + delegate_call: cost!(seal_delegate_call), + call_transfer_surcharge: cost_args!(seal_call_per_transfer_clone_byte, 1, 0), + call_per_cloned_byte: cost_args!(seal_call_per_transfer_clone_byte, 0, 1), + instantiate: cost!(seal_instantiate), + instantiate_transfer_surcharge: cost_args!( + seal_instantiate_per_transfer_input_salt_byte, 1, 0, 0 ), - instantiate_per_input_byte: cost_byte_batched_args!( - seal_instantiate_per_transfer_input_salt_kb, + instantiate_per_input_byte: cost_args!( + seal_instantiate_per_transfer_input_salt_byte, 0, 1, 0 ), - instantiate_per_salt_byte: cost_byte_batched_args!( - seal_instantiate_per_transfer_input_salt_kb, + instantiate_per_salt_byte: cost_args!( + seal_instantiate_per_transfer_input_salt_byte, 0, 0, 1 ), - hash_sha2_256: cost_batched!(seal_hash_sha2_256), - hash_sha2_256_per_byte: cost_byte_batched!(seal_hash_sha2_256_per_kb), - hash_keccak_256: cost_batched!(seal_hash_keccak_256), - hash_keccak_256_per_byte: cost_byte_batched!(seal_hash_keccak_256_per_kb), - hash_blake2_256: cost_batched!(seal_hash_blake2_256), - hash_blake2_256_per_byte: cost_byte_batched!(seal_hash_blake2_256_per_kb), - hash_blake2_128: cost_batched!(seal_hash_blake2_128), - hash_blake2_128_per_byte: cost_byte_batched!(seal_hash_blake2_128_per_kb), - ecdsa_recover: cost_batched!(seal_ecdsa_recover), - ecdsa_to_eth_address: cost_batched!(seal_ecdsa_to_eth_address), - reentrance_count: cost_batched!(seal_reentrance_count), - account_reentrance_count: cost_batched!(seal_account_reentrance_count), - instantiation_nonce: cost_batched!(seal_instantiation_nonce), + hash_sha2_256: cost!(seal_hash_sha2_256), + hash_sha2_256_per_byte: cost!(seal_hash_sha2_256_per_byte), + hash_keccak_256: cost!(seal_hash_keccak_256), + hash_keccak_256_per_byte: cost!(seal_hash_keccak_256_per_byte), + hash_blake2_256: cost!(seal_hash_blake2_256), + hash_blake2_256_per_byte: cost!(seal_hash_blake2_256_per_byte), + hash_blake2_128: cost!(seal_hash_blake2_128), + hash_blake2_128_per_byte: cost!(seal_hash_blake2_128_per_byte), + ecdsa_recover: cost!(seal_ecdsa_recover), + ecdsa_to_eth_address: cost!(seal_ecdsa_to_eth_address), + reentrance_count: cost!(seal_reentrance_count), + account_reentrance_count: cost!(seal_account_reentrance_count), + instantiation_nonce: cost!(seal_instantiation_nonce), _phantom: PhantomData, } } @@ -687,29 +627,12 @@ impl Default for HostFnWeights { struct ScheduleRules<'a, T: Config> { schedule: &'a Schedule, - params: Vec, determinism: Determinism, } impl Schedule { - pub(crate) fn rules( - &self, - module: &elements::Module, - determinism: Determinism, - ) -> impl gas_metering::Rules + '_ { - ScheduleRules { - schedule: self, - params: module - .type_section() - .iter() - .flat_map(|section| section.types()) - .map(|func| { - let elements::Type::Function(func) = func; - func.params().len() as u32 - }) - .collect(), - determinism, - } + pub(crate) fn rules(&self, determinism: Determinism) -> impl gas_metering::Rules + '_ { + ScheduleRules { schedule: self, determinism } } } @@ -717,7 +640,6 @@ impl<'a, T: Config> gas_metering::Rules for ScheduleRules<'a, T> { fn instruction_cost(&self, instruction: &elements::Instruction) -> Option { use self::elements::Instruction::*; let w = &self.schedule.instruction_weights; - let max_params = self.schedule.limits.parameters; let weight = match *instruction { End | Unreachable | Return | Else => 0, @@ -753,7 +675,7 @@ impl<'a, T: Config> gas_metering::Rules for ScheduleRules<'a, T> { SetGlobal(_) => w.global_set, CurrentMemory(_) => w.memory_current, GrowMemory(_) => w.memory_grow, - CallIndirect(idx, _) => *self.params.get(idx as usize).unwrap_or(&max_params), + CallIndirect(_, _) => w.call_indirect, BrTable(ref data) => w .br_table .saturating_add(w.br_table_per_entry.saturating_mul(data.table.len() as u32)), diff --git a/frame/contracts/src/wasm/prepare.rs b/frame/contracts/src/wasm/prepare.rs index 3c69fc2a7..eed60891d 100644 --- a/frame/contracts/src/wasm/prepare.rs +++ b/frame/contracts/src/wasm/prepare.rs @@ -219,7 +219,7 @@ impl<'a, T: Config> ContractModule<'a, T> { } fn inject_gas_metering(self, determinism: Determinism) -> Result { - let gas_rules = self.schedule.rules(&self.module, determinism); + let gas_rules = self.schedule.rules(determinism); let backend = gas_metering::host_function::Injector::new("seal0", "gas"); let contract_module = gas_metering::inject(self.module, backend, &gas_rules) .map_err(|_| "gas instrumentation failed")?; diff --git a/frame/contracts/src/weights.rs b/frame/contracts/src/weights.rs index ae8638eb7..b16f11eb7 100644 --- a/frame/contracts/src/weights.rs +++ b/frame/contracts/src/weights.rs @@ -18,28 +18,26 @@ //! Autogenerated weights for pallet_contracts //! //! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev -//! DATE: 2023-03-16, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! DATE: 2023-03-14, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` //! WORST CASE MAP SIZE: `1000000` //! HOSTNAME: `bm3`, CPU: `Intel(R) Core(TM) i7-7700K CPU @ 4.20GHz` //! EXECUTION: Some(Wasm), WASM-EXECUTION: Compiled, CHAIN: Some("dev"), DB CACHE: 1024 // Executed Command: -// ./target/production/substrate +// target/production/substrate // benchmark // pallet -// --chain=dev // --steps=50 // --repeat=20 -// --pallet=pallet_contracts -// --no-storage-info -// --no-median-slopes -// --no-min-squares // --extrinsic=* // --execution=wasm // --wasm-execution=compiled // --heap-pages=4096 -// --output=./frame/contracts/src/weights.rs +// --json-file=/var/lib/gitlab-runner/builds/zyw4fam_/0/parity/mirrors/substrate/.git/.artifacts/bench.json +// --pallet=pallet_contracts +// --chain=dev // --header=./HEADER-APACHE2 +// --output=./frame/contracts/src/weights.rs // --template=./.maintain/frame-weight-template.hbs #![cfg_attr(rustfmt, rustfmt_skip)] @@ -77,40 +75,40 @@ pub trait WeightInfo { fn seal_weight_to_fee(r: u32, ) -> Weight; fn seal_gas(r: u32, ) -> Weight; fn seal_input(r: u32, ) -> Weight; - fn seal_input_per_kb(n: u32, ) -> Weight; + fn seal_input_per_byte(n: u32, ) -> Weight; fn seal_return(r: u32, ) -> Weight; - fn seal_return_per_kb(n: u32, ) -> Weight; + fn seal_return_per_byte(n: u32, ) -> Weight; fn seal_terminate(r: u32, ) -> Weight; fn seal_random(r: u32, ) -> Weight; fn seal_deposit_event(r: u32, ) -> Weight; - fn seal_deposit_event_per_topic_and_kb(t: u32, n: u32, ) -> Weight; + fn seal_deposit_event_per_topic_and_byte(t: u32, n: u32, ) -> Weight; fn seal_debug_message(r: u32, ) -> Weight; - fn seal_debug_message_per_kb(i: u32, ) -> Weight; + fn seal_debug_message_per_byte(i: u32, ) -> Weight; fn seal_set_storage(r: u32, ) -> Weight; - fn seal_set_storage_per_new_kb(n: u32, ) -> Weight; - fn seal_set_storage_per_old_kb(n: u32, ) -> Weight; + fn seal_set_storage_per_new_byte(n: u32, ) -> Weight; + fn seal_set_storage_per_old_byte(n: u32, ) -> Weight; fn seal_clear_storage(r: u32, ) -> Weight; - fn seal_clear_storage_per_kb(n: u32, ) -> Weight; + fn seal_clear_storage_per_byte(n: u32, ) -> Weight; fn seal_get_storage(r: u32, ) -> Weight; - fn seal_get_storage_per_kb(n: u32, ) -> Weight; + fn seal_get_storage_per_byte(n: u32, ) -> Weight; fn seal_contains_storage(r: u32, ) -> Weight; - fn seal_contains_storage_per_kb(n: u32, ) -> Weight; + fn seal_contains_storage_per_byte(n: u32, ) -> Weight; fn seal_take_storage(r: u32, ) -> Weight; - fn seal_take_storage_per_kb(n: u32, ) -> Weight; + fn seal_take_storage_per_byte(n: u32, ) -> Weight; fn seal_transfer(r: u32, ) -> Weight; fn seal_call(r: u32, ) -> Weight; fn seal_delegate_call(r: u32, ) -> Weight; - fn seal_call_per_transfer_clone_kb(t: u32, c: u32, ) -> Weight; + fn seal_call_per_transfer_clone_byte(t: u32, c: u32, ) -> Weight; fn seal_instantiate(r: u32, ) -> Weight; - fn seal_instantiate_per_transfer_input_salt_kb(t: u32, i: u32, s: u32, ) -> Weight; + fn seal_instantiate_per_transfer_input_salt_byte(t: u32, i: u32, s: u32, ) -> Weight; fn seal_hash_sha2_256(r: u32, ) -> Weight; - fn seal_hash_sha2_256_per_kb(n: u32, ) -> Weight; + fn seal_hash_sha2_256_per_byte(n: u32, ) -> Weight; fn seal_hash_keccak_256(r: u32, ) -> Weight; - fn seal_hash_keccak_256_per_kb(n: u32, ) -> Weight; + fn seal_hash_keccak_256_per_byte(n: u32, ) -> Weight; fn seal_hash_blake2_256(r: u32, ) -> Weight; - fn seal_hash_blake2_256_per_kb(n: u32, ) -> Weight; + fn seal_hash_blake2_256_per_byte(n: u32, ) -> Weight; fn seal_hash_blake2_128(r: u32, ) -> Weight; - fn seal_hash_blake2_128_per_kb(n: u32, ) -> Weight; + fn seal_hash_blake2_128_per_byte(n: u32, ) -> Weight; fn seal_ecdsa_recover(r: u32, ) -> Weight; fn seal_ecdsa_to_eth_address(r: u32, ) -> Weight; fn seal_set_code_hash(r: u32, ) -> Weight; @@ -128,7 +126,6 @@ pub trait WeightInfo { fn instr_br_table_per_entry(e: u32, ) -> Weight; fn instr_call(r: u32, ) -> Weight; fn instr_call_indirect(r: u32, ) -> Weight; - fn instr_call_indirect_per_param(p: u32, ) -> Weight; fn instr_call_per_local(l: u32, ) -> Weight; fn instr_local_get(r: u32, ) -> Weight; fn instr_local_set(r: u32, ) -> Weight; @@ -180,8 +177,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `109` // Estimated: `1594` - // Minimum execution time: 2_679_000 picoseconds. - Weight::from_parts(2_907_000, 1594) + // Minimum execution time: 2_736_000 picoseconds. + Weight::from_parts(2_931_000, 1594) .saturating_add(T::DbWeight::get().reads(1_u64)) } /// Storage: Skipped Metadata (r:0 w:0) @@ -189,12 +186,12 @@ impl WeightInfo for SubstrateWeight { /// The range of component `k` is `[0, 1024]`. fn on_initialize_per_trie_key(k: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `450 + k * (69 ±0)` - // Estimated: `440 + k * (70 ±0)` - // Minimum execution time: 11_162_000 picoseconds. - Weight::from_parts(5_734_923, 440) - // Standard Error: 1_097 - .saturating_add(Weight::from_parts(976_647, 0).saturating_mul(k.into())) + // Measured: `481 + k * (69 ±0)` + // Estimated: `471 + k * (70 ±0)` + // Minimum execution time: 10_759_000 picoseconds. + Weight::from_parts(6_965_683, 471) + // Standard Error: 1_019 + .saturating_add(Weight::from_parts(961_947, 0).saturating_mul(k.into())) .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(T::DbWeight::get().reads((1_u64).saturating_mul(k.into()))) .saturating_add(T::DbWeight::get().writes(1_u64)) @@ -206,12 +203,12 @@ impl WeightInfo for SubstrateWeight { /// The range of component `q` is `[0, 128]`. fn on_initialize_per_queue_item(q: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `250 + q * (33 ±0)` - // Estimated: `1725 + q * (33 ±0)` - // Minimum execution time: 2_626_000 picoseconds. - Weight::from_parts(10_626_314, 1725) - // Standard Error: 4_006 - .saturating_add(Weight::from_parts(1_298_864, 0).saturating_mul(q.into())) + // Measured: `281 + q * (33 ±0)` + // Estimated: `1753 + q * (33 ±0)` + // Minimum execution time: 2_633_000 picoseconds. + Weight::from_parts(10_668_837, 1753) + // Standard Error: 3_538 + .saturating_add(Weight::from_parts(1_277_168, 0).saturating_mul(q.into())) .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) .saturating_add(Weight::from_parts(0, 33).saturating_mul(q.into())) @@ -223,12 +220,12 @@ impl WeightInfo for SubstrateWeight { /// The range of component `c` is `[0, 61717]`. fn reinstrument(c: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `238 + c * (1 ±0)` - // Estimated: `3951 + c * (2 ±0)` - // Minimum execution time: 31_434_000 picoseconds. - Weight::from_parts(29_558_961, 3951) - // Standard Error: 55 - .saturating_add(Weight::from_parts(50_774, 0).saturating_mul(c.into())) + // Measured: `270 + c * (1 ±0)` + // Estimated: `4015 + c * (2 ±0)` + // Minimum execution time: 30_264_000 picoseconds. + Weight::from_parts(27_106_554, 4015) + // Standard Error: 53 + .saturating_add(Weight::from_parts(49_705, 0).saturating_mul(c.into())) .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) .saturating_add(Weight::from_parts(0, 2).saturating_mul(c.into())) @@ -246,12 +243,12 @@ impl WeightInfo for SubstrateWeight { /// The range of component `c` is `[0, 125952]`. fn call_with_code_per_byte(c: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `707` - // Estimated: `21400 + c * (5 ±0)` - // Minimum execution time: 309_015_000 picoseconds. - Weight::from_parts(318_740_885, 21400) - // Standard Error: 29 - .saturating_add(Weight::from_parts(30_993, 0).saturating_mul(c.into())) + // Measured: `803` + // Estimated: `21880 + c * (5 ±0)` + // Minimum execution time: 309_249_000 picoseconds. + Weight::from_parts(323_353_590, 21880) + // Standard Error: 25 + .saturating_add(Weight::from_parts(31_359, 0).saturating_mul(c.into())) .saturating_add(T::DbWeight::get().reads(6_u64)) .saturating_add(T::DbWeight::get().writes(4_u64)) .saturating_add(Weight::from_parts(0, 5).saturating_mul(c.into())) @@ -279,14 +276,14 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `270` // Estimated: `26207` - // Minimum execution time: 3_126_495_000 picoseconds. - Weight::from_parts(636_857_878, 26207) - // Standard Error: 294 - .saturating_add(Weight::from_parts(92_930, 0).saturating_mul(c.into())) - // Standard Error: 17 - .saturating_add(Weight::from_parts(1_128, 0).saturating_mul(i.into())) - // Standard Error: 17 - .saturating_add(Weight::from_parts(1_415, 0).saturating_mul(s.into())) + // Minimum execution time: 3_136_707_000 picoseconds. + Weight::from_parts(564_055_378, 26207) + // Standard Error: 286 + .saturating_add(Weight::from_parts(93_308, 0).saturating_mul(c.into())) + // Standard Error: 16 + .saturating_add(Weight::from_parts(1_165, 0).saturating_mul(i.into())) + // Standard Error: 16 + .saturating_add(Weight::from_parts(1_435, 0).saturating_mul(s.into())) .saturating_add(T::DbWeight::get().reads(9_u64)) .saturating_add(T::DbWeight::get().writes(10_u64)) } @@ -308,14 +305,14 @@ impl WeightInfo for SubstrateWeight { /// The range of component `s` is `[0, 1048576]`. fn instantiate(i: u32, s: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `482` - // Estimated: `28521` - // Minimum execution time: 1_591_415_000 picoseconds. - Weight::from_parts(228_633_516, 28521) + // Measured: `546` + // Estimated: `28969` + // Minimum execution time: 1_631_314_000 picoseconds. + Weight::from_parts(236_693_159, 28969) // Standard Error: 8 - .saturating_add(Weight::from_parts(1_449, 0).saturating_mul(i.into())) + .saturating_add(Weight::from_parts(1_434, 0).saturating_mul(i.into())) // Standard Error: 8 - .saturating_add(Weight::from_parts(1_444, 0).saturating_mul(s.into())) + .saturating_add(Weight::from_parts(1_445, 0).saturating_mul(s.into())) .saturating_add(T::DbWeight::get().reads(9_u64)) .saturating_add(T::DbWeight::get().writes(7_u64)) } @@ -331,10 +328,10 @@ impl WeightInfo for SubstrateWeight { /// Proof Skipped: System EventTopics (max_values: None, max_size: None, mode: Measured) fn call() -> Weight { // Proof Size summary in bytes: - // Measured: `759` - // Estimated: `21615` - // Minimum execution time: 167_905_000 picoseconds. - Weight::from_parts(169_247_000, 21615) + // Measured: `855` + // Estimated: `22095` + // Minimum execution time: 167_139_000 picoseconds. + Weight::from_parts(168_034_000, 22095) .saturating_add(T::DbWeight::get().reads(6_u64)) .saturating_add(T::DbWeight::get().writes(4_u64)) } @@ -351,10 +348,10 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `109` // Estimated: `7366` - // Minimum execution time: 305_128_000 picoseconds. - Weight::from_parts(310_911_395, 7366) - // Standard Error: 70 - .saturating_add(Weight::from_parts(94_067, 0).saturating_mul(c.into())) + // Minimum execution time: 306_518_000 picoseconds. + Weight::from_parts(325_652_407, 7366) + // Standard Error: 134 + .saturating_add(Weight::from_parts(93_502, 0).saturating_mul(c.into())) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().writes(4_u64)) } @@ -368,10 +365,10 @@ impl WeightInfo for SubstrateWeight { /// Proof: Contracts PristineCode (max_values: None, max_size: Some(125988), added: 128463, mode: Measured) fn remove_code() -> Weight { // Proof Size summary in bytes: - // Measured: `255` - // Estimated: `7950` - // Minimum execution time: 29_605_000 picoseconds. - Weight::from_parts(29_986_000, 7950) + // Measured: `287` + // Estimated: `8078` + // Minimum execution time: 29_084_000 picoseconds. + Weight::from_parts(29_350_000, 8078) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().writes(4_u64)) } @@ -383,10 +380,10 @@ impl WeightInfo for SubstrateWeight { /// Proof Skipped: System EventTopics (max_values: None, max_size: None, mode: Measured) fn set_code() -> Weight { // Proof Size summary in bytes: - // Measured: `570` - // Estimated: `19530` - // Minimum execution time: 33_824_000 picoseconds. - Weight::from_parts(34_388_000, 19530) + // Measured: `666` + // Estimated: `19818` + // Minimum execution time: 32_996_000 picoseconds. + Weight::from_parts(33_365_000, 19818) .saturating_add(T::DbWeight::get().reads(6_u64)) .saturating_add(T::DbWeight::get().writes(6_u64)) } @@ -400,18 +397,18 @@ impl WeightInfo for SubstrateWeight { /// Proof: Timestamp Now (max_values: Some(1), max_size: Some(8), added: 503, mode: Measured) /// Storage: System EventTopics (r:2 w:2) /// Proof Skipped: System EventTopics (max_values: None, max_size: None, mode: Measured) - /// The range of component `r` is `[0, 20]`. + /// The range of component `r` is `[0, 1600]`. fn seal_caller(r: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `781 + r * (480 ±0)` - // Estimated: `21765 + r * (2400 ±0)` - // Minimum execution time: 295_283_000 picoseconds. - Weight::from_parts(295_895_211, 21765) - // Standard Error: 44_860 - .saturating_add(Weight::from_parts(22_913_078, 0).saturating_mul(r.into())) + // Measured: `877 + r * (6 ±0)` + // Estimated: `22210 + r * (30 ±0)` + // Minimum execution time: 298_315_000 picoseconds. + Weight::from_parts(304_612_433, 22210) + // Standard Error: 904 + .saturating_add(Weight::from_parts(285_473, 0).saturating_mul(r.into())) .saturating_add(T::DbWeight::get().reads(6_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) - .saturating_add(Weight::from_parts(0, 2400).saturating_mul(r.into())) + .saturating_add(Weight::from_parts(0, 30).saturating_mul(r.into())) } /// Storage: System Account (r:1 w:0) /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: Measured) @@ -423,19 +420,19 @@ impl WeightInfo for SubstrateWeight { /// Proof: Timestamp Now (max_values: Some(1), max_size: Some(8), added: 503, mode: Measured) /// Storage: System EventTopics (r:2 w:2) /// Proof Skipped: System EventTopics (max_values: None, max_size: None, mode: Measured) - /// The range of component `r` is `[0, 20]`. + /// The range of component `r` is `[0, 1600]`. fn seal_is_contract(r: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `821 + r * (19218 ±0)` - // Estimated: `21765 + r * (294095 ±0)` - // Minimum execution time: 295_760_000 picoseconds. - Weight::from_parts(151_512_811, 21765) - // Standard Error: 522_974 - .saturating_add(Weight::from_parts(263_028_353, 0).saturating_mul(r.into())) + // Measured: `935 + r * (272 ±0)` + // Estimated: `22315 + r * (3835 ±0)` + // Minimum execution time: 299_169_000 picoseconds. + Weight::from_parts(138_157_704, 22315) + // Standard Error: 6_115 + .saturating_add(Weight::from_parts(3_244_482, 0).saturating_mul(r.into())) .saturating_add(T::DbWeight::get().reads(6_u64)) - .saturating_add(T::DbWeight::get().reads((80_u64).saturating_mul(r.into()))) + .saturating_add(T::DbWeight::get().reads((1_u64).saturating_mul(r.into()))) .saturating_add(T::DbWeight::get().writes(3_u64)) - .saturating_add(Weight::from_parts(0, 294095).saturating_mul(r.into())) + .saturating_add(Weight::from_parts(0, 3835).saturating_mul(r.into())) } /// Storage: System Account (r:1 w:0) /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: Measured) @@ -447,19 +444,19 @@ impl WeightInfo for SubstrateWeight { /// Proof: Timestamp Now (max_values: Some(1), max_size: Some(8), added: 503, mode: Measured) /// Storage: System EventTopics (r:2 w:2) /// Proof Skipped: System EventTopics (max_values: None, max_size: None, mode: Measured) - /// The range of component `r` is `[0, 20]`. + /// The range of component `r` is `[0, 1600]`. fn seal_code_hash(r: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `825 + r * (19539 ±0)` - // Estimated: `21810 + r * (295700 ±0)` - // Minimum execution time: 295_950_000 picoseconds. - Weight::from_parts(164_516_076, 21810) - // Standard Error: 469_540 - .saturating_add(Weight::from_parts(321_138_601, 0).saturating_mul(r.into())) + // Measured: `927 + r * (276 ±0)` + // Estimated: `22335 + r * (3855 ±0)` + // Minimum execution time: 299_262_000 picoseconds. + Weight::from_parts(157_105_627, 22335) + // Standard Error: 5_634 + .saturating_add(Weight::from_parts(3_995_666, 0).saturating_mul(r.into())) .saturating_add(T::DbWeight::get().reads(6_u64)) - .saturating_add(T::DbWeight::get().reads((80_u64).saturating_mul(r.into()))) + .saturating_add(T::DbWeight::get().reads((1_u64).saturating_mul(r.into()))) .saturating_add(T::DbWeight::get().writes(3_u64)) - .saturating_add(Weight::from_parts(0, 295700).saturating_mul(r.into())) + .saturating_add(Weight::from_parts(0, 3855).saturating_mul(r.into())) } /// Storage: System Account (r:1 w:0) /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: Measured) @@ -471,18 +468,18 @@ impl WeightInfo for SubstrateWeight { /// Proof: Timestamp Now (max_values: Some(1), max_size: Some(8), added: 503, mode: Measured) /// Storage: System EventTopics (r:2 w:2) /// Proof Skipped: System EventTopics (max_values: None, max_size: None, mode: Measured) - /// The range of component `r` is `[0, 20]`. + /// The range of component `r` is `[0, 1600]`. fn seal_own_code_hash(r: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `788 + r * (480 ±0)` - // Estimated: `21800 + r * (2400 ±0)` - // Minimum execution time: 295_916_000 picoseconds. - Weight::from_parts(299_964_913, 21800) - // Standard Error: 78_211 - .saturating_add(Weight::from_parts(28_933_603, 0).saturating_mul(r.into())) + // Measured: `884 + r * (6 ±0)` + // Estimated: `22250 + r * (30 ±0)` + // Minimum execution time: 298_259_000 picoseconds. + Weight::from_parts(303_498_245, 22250) + // Standard Error: 874 + .saturating_add(Weight::from_parts(352_839, 0).saturating_mul(r.into())) .saturating_add(T::DbWeight::get().reads(6_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) - .saturating_add(Weight::from_parts(0, 2400).saturating_mul(r.into())) + .saturating_add(Weight::from_parts(0, 30).saturating_mul(r.into())) } /// Storage: System Account (r:1 w:0) /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: Measured) @@ -494,18 +491,18 @@ impl WeightInfo for SubstrateWeight { /// Proof: Timestamp Now (max_values: Some(1), max_size: Some(8), added: 503, mode: Measured) /// Storage: System EventTopics (r:2 w:2) /// Proof Skipped: System EventTopics (max_values: None, max_size: None, mode: Measured) - /// The range of component `r` is `[0, 20]`. + /// The range of component `r` is `[0, 1600]`. fn seal_caller_is_origin(r: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `778 + r * (240 ±0)` - // Estimated: `21735 + r * (1200 ±0)` - // Minimum execution time: 293_588_000 picoseconds. - Weight::from_parts(297_568_984, 21735) - // Standard Error: 35_513 - .saturating_add(Weight::from_parts(11_074_880, 0).saturating_mul(r.into())) + // Measured: `874 + r * (3 ±0)` + // Estimated: `22215 + r * (15 ±0)` + // Minimum execution time: 295_689_000 picoseconds. + Weight::from_parts(314_004_541, 22215) + // Standard Error: 1_803 + .saturating_add(Weight::from_parts(131_102, 0).saturating_mul(r.into())) .saturating_add(T::DbWeight::get().reads(6_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) - .saturating_add(Weight::from_parts(0, 1200).saturating_mul(r.into())) + .saturating_add(Weight::from_parts(0, 15).saturating_mul(r.into())) } /// Storage: System Account (r:1 w:0) /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: Measured) @@ -517,18 +514,18 @@ impl WeightInfo for SubstrateWeight { /// Proof: Timestamp Now (max_values: Some(1), max_size: Some(8), added: 503, mode: Measured) /// Storage: System EventTopics (r:2 w:2) /// Proof Skipped: System EventTopics (max_values: None, max_size: None, mode: Measured) - /// The range of component `r` is `[0, 20]`. + /// The range of component `r` is `[0, 1600]`. fn seal_address(r: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `782 + r * (480 ±0)` - // Estimated: `21730 + r * (2400 ±0)` - // Minimum execution time: 294_929_000 picoseconds. - Weight::from_parts(295_916_817, 21730) - // Standard Error: 64_902 - .saturating_add(Weight::from_parts(22_944_018, 0).saturating_mul(r.into())) + // Measured: `878 + r * (6 ±0)` + // Estimated: `22220 + r * (30 ±0)` + // Minimum execution time: 297_579_000 picoseconds. + Weight::from_parts(299_326_920, 22220) + // Standard Error: 990 + .saturating_add(Weight::from_parts(284_789, 0).saturating_mul(r.into())) .saturating_add(T::DbWeight::get().reads(6_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) - .saturating_add(Weight::from_parts(0, 2400).saturating_mul(r.into())) + .saturating_add(Weight::from_parts(0, 30).saturating_mul(r.into())) } /// Storage: System Account (r:1 w:0) /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: Measured) @@ -540,18 +537,18 @@ impl WeightInfo for SubstrateWeight { /// Proof: Timestamp Now (max_values: Some(1), max_size: Some(8), added: 503, mode: Measured) /// Storage: System EventTopics (r:2 w:2) /// Proof Skipped: System EventTopics (max_values: None, max_size: None, mode: Measured) - /// The range of component `r` is `[0, 20]`. + /// The range of component `r` is `[0, 1600]`. fn seal_gas_left(r: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `783 + r * (480 ±0)` - // Estimated: `21720 + r * (2405 ±0)` - // Minimum execution time: 296_783_000 picoseconds. - Weight::from_parts(299_399_332, 21720) - // Standard Error: 50_982 - .saturating_add(Weight::from_parts(22_173_249, 0).saturating_mul(r.into())) + // Measured: `879 + r * (6 ±0)` + // Estimated: `22205 + r * (30 ±0)` + // Minimum execution time: 297_651_000 picoseconds. + Weight::from_parts(304_465_467, 22205) + // Standard Error: 736 + .saturating_add(Weight::from_parts(272_149, 0).saturating_mul(r.into())) .saturating_add(T::DbWeight::get().reads(6_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) - .saturating_add(Weight::from_parts(0, 2405).saturating_mul(r.into())) + .saturating_add(Weight::from_parts(0, 30).saturating_mul(r.into())) } /// Storage: System Account (r:2 w:0) /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: Measured) @@ -563,18 +560,18 @@ impl WeightInfo for SubstrateWeight { /// Proof: Timestamp Now (max_values: Some(1), max_size: Some(8), added: 503, mode: Measured) /// Storage: System EventTopics (r:2 w:2) /// Proof Skipped: System EventTopics (max_values: None, max_size: None, mode: Measured) - /// The range of component `r` is `[0, 20]`. + /// The range of component `r` is `[0, 1600]`. fn seal_balance(r: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `921 + r * (480 ±0)` - // Estimated: `24193 + r * (2451 ±0)` - // Minimum execution time: 294_120_000 picoseconds. - Weight::from_parts(305_889_989, 24193) - // Standard Error: 116_244 - .saturating_add(Weight::from_parts(114_781_094, 0).saturating_mul(r.into())) + // Measured: `1050 + r * (6 ±0)` + // Estimated: `25258 + r * (30 ±0)` + // Minimum execution time: 296_957_000 picoseconds. + Weight::from_parts(308_680_068, 25258) + // Standard Error: 1_377 + .saturating_add(Weight::from_parts(1_419_294, 0).saturating_mul(r.into())) .saturating_add(T::DbWeight::get().reads(7_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) - .saturating_add(Weight::from_parts(0, 2451).saturating_mul(r.into())) + .saturating_add(Weight::from_parts(0, 30).saturating_mul(r.into())) } /// Storage: System Account (r:1 w:0) /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: Measured) @@ -586,18 +583,18 @@ impl WeightInfo for SubstrateWeight { /// Proof: Timestamp Now (max_values: Some(1), max_size: Some(8), added: 503, mode: Measured) /// Storage: System EventTopics (r:2 w:2) /// Proof Skipped: System EventTopics (max_values: None, max_size: None, mode: Measured) - /// The range of component `r` is `[0, 20]`. + /// The range of component `r` is `[0, 1600]`. fn seal_value_transferred(r: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `792 + r * (480 ±0)` - // Estimated: `21830 + r * (2400 ±0)` - // Minimum execution time: 295_569_000 picoseconds. - Weight::from_parts(299_774_774, 21830) - // Standard Error: 46_252 - .saturating_add(Weight::from_parts(21_753_842, 0).saturating_mul(r.into())) + // Measured: `888 + r * (6 ±0)` + // Estimated: `22305 + r * (30 ±0)` + // Minimum execution time: 297_735_000 picoseconds. + Weight::from_parts(301_533_807, 22305) + // Standard Error: 1_119 + .saturating_add(Weight::from_parts(277_289, 0).saturating_mul(r.into())) .saturating_add(T::DbWeight::get().reads(6_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) - .saturating_add(Weight::from_parts(0, 2400).saturating_mul(r.into())) + .saturating_add(Weight::from_parts(0, 30).saturating_mul(r.into())) } /// Storage: System Account (r:1 w:0) /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: Measured) @@ -609,18 +606,18 @@ impl WeightInfo for SubstrateWeight { /// Proof: Timestamp Now (max_values: Some(1), max_size: Some(8), added: 503, mode: Measured) /// Storage: System EventTopics (r:2 w:2) /// Proof Skipped: System EventTopics (max_values: None, max_size: None, mode: Measured) - /// The range of component `r` is `[0, 20]`. + /// The range of component `r` is `[0, 1600]`. fn seal_minimum_balance(r: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `790 + r * (480 ±0)` - // Estimated: `21760 + r * (2400 ±0)` - // Minimum execution time: 295_400_000 picoseconds. - Weight::from_parts(300_637_266, 21760) - // Standard Error: 41_263 - .saturating_add(Weight::from_parts(21_565_466, 0).saturating_mul(r.into())) + // Measured: `886 + r * (6 ±0)` + // Estimated: `22295 + r * (30 ±0)` + // Minimum execution time: 299_599_000 picoseconds. + Weight::from_parts(303_664_496, 22295) + // Standard Error: 1_009 + .saturating_add(Weight::from_parts(280_353, 0).saturating_mul(r.into())) .saturating_add(T::DbWeight::get().reads(6_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) - .saturating_add(Weight::from_parts(0, 2400).saturating_mul(r.into())) + .saturating_add(Weight::from_parts(0, 30).saturating_mul(r.into())) } /// Storage: System Account (r:1 w:0) /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: Measured) @@ -632,18 +629,18 @@ impl WeightInfo for SubstrateWeight { /// Proof: Timestamp Now (max_values: Some(1), max_size: Some(8), added: 503, mode: Measured) /// Storage: System EventTopics (r:2 w:2) /// Proof Skipped: System EventTopics (max_values: None, max_size: None, mode: Measured) - /// The range of component `r` is `[0, 20]`. + /// The range of component `r` is `[0, 1600]`. fn seal_block_number(r: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `787 + r * (480 ±0)` - // Estimated: `21785 + r * (2400 ±0)` - // Minimum execution time: 294_696_000 picoseconds. - Weight::from_parts(297_196_796, 21785) - // Standard Error: 61_173 - .saturating_add(Weight::from_parts(21_903_191, 0).saturating_mul(r.into())) + // Measured: `883 + r * (6 ±0)` + // Estimated: `22285 + r * (30 ±0)` + // Minimum execution time: 297_641_000 picoseconds. + Weight::from_parts(302_437_979, 22285) + // Standard Error: 669 + .saturating_add(Weight::from_parts(270_403, 0).saturating_mul(r.into())) .saturating_add(T::DbWeight::get().reads(6_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) - .saturating_add(Weight::from_parts(0, 2400).saturating_mul(r.into())) + .saturating_add(Weight::from_parts(0, 30).saturating_mul(r.into())) } /// Storage: System Account (r:1 w:0) /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: Measured) @@ -655,18 +652,18 @@ impl WeightInfo for SubstrateWeight { /// Proof: Timestamp Now (max_values: Some(1), max_size: Some(8), added: 503, mode: Measured) /// Storage: System EventTopics (r:2 w:2) /// Proof Skipped: System EventTopics (max_values: None, max_size: None, mode: Measured) - /// The range of component `r` is `[0, 20]`. + /// The range of component `r` is `[0, 1600]`. fn seal_now(r: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `778 + r * (480 ±0)` - // Estimated: `21715 + r * (2400 ±0)` - // Minimum execution time: 297_714_000 picoseconds. - Weight::from_parts(306_030_237, 21715) - // Standard Error: 69_984 - .saturating_add(Weight::from_parts(21_744_413, 0).saturating_mul(r.into())) + // Measured: `874 + r * (6 ±0)` + // Estimated: `22215 + r * (30 ±0)` + // Minimum execution time: 297_217_000 picoseconds. + Weight::from_parts(299_538_157, 22215) + // Standard Error: 732 + .saturating_add(Weight::from_parts(280_800, 0).saturating_mul(r.into())) .saturating_add(T::DbWeight::get().reads(6_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) - .saturating_add(Weight::from_parts(0, 2400).saturating_mul(r.into())) + .saturating_add(Weight::from_parts(0, 30).saturating_mul(r.into())) } /// Storage: System Account (r:1 w:0) /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: Measured) @@ -680,18 +677,18 @@ impl WeightInfo for SubstrateWeight { /// Proof: TransactionPayment NextFeeMultiplier (max_values: Some(1), max_size: Some(16), added: 511, mode: Measured) /// Storage: System EventTopics (r:2 w:2) /// Proof Skipped: System EventTopics (max_values: None, max_size: None, mode: Measured) - /// The range of component `r` is `[0, 20]`. + /// The range of component `r` is `[0, 1600]`. fn seal_weight_to_fee(r: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `856 + r * (800 ±0)` - // Estimated: `24416 + r * (4805 ±0)` - // Minimum execution time: 295_180_000 picoseconds. - Weight::from_parts(308_851_025, 24416) - // Standard Error: 115_126 - .saturating_add(Weight::from_parts(98_678_404, 0).saturating_mul(r.into())) + // Measured: `952 + r * (10 ±0)` + // Estimated: `25022 + r * (60 ±0)` + // Minimum execution time: 297_286_000 picoseconds. + Weight::from_parts(311_436_352, 25022) + // Standard Error: 6_811 + .saturating_add(Weight::from_parts(1_291_941, 0).saturating_mul(r.into())) .saturating_add(T::DbWeight::get().reads(7_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) - .saturating_add(Weight::from_parts(0, 4805).saturating_mul(r.into())) + .saturating_add(Weight::from_parts(0, 60).saturating_mul(r.into())) } /// Storage: System Account (r:1 w:0) /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: Measured) @@ -703,18 +700,18 @@ impl WeightInfo for SubstrateWeight { /// Proof: Timestamp Now (max_values: Some(1), max_size: Some(8), added: 503, mode: Measured) /// Storage: System EventTopics (r:2 w:2) /// Proof Skipped: System EventTopics (max_values: None, max_size: None, mode: Measured) - /// The range of component `r` is `[0, 20]`. + /// The range of component `r` is `[0, 1600]`. fn seal_gas(r: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `745 + r * (320 ±0)` - // Estimated: `21590 + r * (1600 ±0)` - // Minimum execution time: 151_587_000 picoseconds. - Weight::from_parts(155_713_096, 21590) - // Standard Error: 32_464 - .saturating_add(Weight::from_parts(8_332_778, 0).saturating_mul(r.into())) + // Measured: `841 + r * (4 ±0)` + // Estimated: `22035 + r * (20 ±0)` + // Minimum execution time: 154_772_000 picoseconds. + Weight::from_parts(159_224_457, 22035) + // Standard Error: 212 + .saturating_add(Weight::from_parts(102_264, 0).saturating_mul(r.into())) .saturating_add(T::DbWeight::get().reads(6_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) - .saturating_add(Weight::from_parts(0, 1600).saturating_mul(r.into())) + .saturating_add(Weight::from_parts(0, 20).saturating_mul(r.into())) } /// Storage: System Account (r:1 w:0) /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: Measured) @@ -726,18 +723,18 @@ impl WeightInfo for SubstrateWeight { /// Proof: Timestamp Now (max_values: Some(1), max_size: Some(8), added: 503, mode: Measured) /// Storage: System EventTopics (r:2 w:2) /// Proof Skipped: System EventTopics (max_values: None, max_size: None, mode: Measured) - /// The range of component `r` is `[0, 20]`. + /// The range of component `r` is `[0, 1600]`. fn seal_input(r: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `780 + r * (480 ±0)` - // Estimated: `21715 + r * (2400 ±0)` - // Minimum execution time: 295_008_000 picoseconds. - Weight::from_parts(297_982_800, 21715) - // Standard Error: 37_480 - .saturating_add(Weight::from_parts(17_970_520, 0).saturating_mul(r.into())) + // Measured: `876 + r * (6 ±0)` + // Estimated: `22220 + r * (30 ±0)` + // Minimum execution time: 297_248_000 picoseconds. + Weight::from_parts(303_805_584, 22220) + // Standard Error: 858 + .saturating_add(Weight::from_parts(223_587, 0).saturating_mul(r.into())) .saturating_add(T::DbWeight::get().reads(6_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) - .saturating_add(Weight::from_parts(0, 2400).saturating_mul(r.into())) + .saturating_add(Weight::from_parts(0, 30).saturating_mul(r.into())) } /// Storage: System Account (r:1 w:0) /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: Measured) @@ -749,15 +746,15 @@ impl WeightInfo for SubstrateWeight { /// Proof: Timestamp Now (max_values: Some(1), max_size: Some(8), added: 503, mode: Measured) /// Storage: System EventTopics (r:2 w:2) /// Proof Skipped: System EventTopics (max_values: None, max_size: None, mode: Measured) - /// The range of component `n` is `[0, 1024]`. - fn seal_input_per_kb(n: u32, ) -> Weight { + /// The range of component `n` is `[0, 1048576]`. + fn seal_input_per_byte(n: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `1260` - // Estimated: `24120` - // Minimum execution time: 314_970_000 picoseconds. - Weight::from_parts(345_339_508, 24120) - // Standard Error: 4_112 - .saturating_add(Weight::from_parts(9_746_966, 0).saturating_mul(n.into())) + // Measured: `880` + // Estimated: `22220` + // Minimum execution time: 298_464_000 picoseconds. + Weight::from_parts(300_996_431, 22220) + // Standard Error: 1 + .saturating_add(Weight::from_parts(600, 0).saturating_mul(n.into())) .saturating_add(T::DbWeight::get().reads(6_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) } @@ -774,12 +771,12 @@ impl WeightInfo for SubstrateWeight { /// The range of component `r` is `[0, 1]`. fn seal_return(r: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `768 + r * (45 ±0)` - // Estimated: `21660 + r * (225 ±0)` - // Minimum execution time: 293_072_000 picoseconds. - Weight::from_parts(295_694_824, 21660) - // Standard Error: 219_523 - .saturating_add(Weight::from_parts(783_975, 0).saturating_mul(r.into())) + // Measured: `864 + r * (45 ±0)` + // Estimated: `22140 + r * (225 ±0)` + // Minimum execution time: 294_347_000 picoseconds. + Weight::from_parts(296_793_034, 22140) + // Standard Error: 277_251 + .saturating_add(Weight::from_parts(1_711_665, 0).saturating_mul(r.into())) .saturating_add(T::DbWeight::get().reads(6_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) .saturating_add(Weight::from_parts(0, 225).saturating_mul(r.into())) @@ -794,15 +791,15 @@ impl WeightInfo for SubstrateWeight { /// Proof: Timestamp Now (max_values: Some(1), max_size: Some(8), added: 503, mode: Measured) /// Storage: System EventTopics (r:2 w:2) /// Proof Skipped: System EventTopics (max_values: None, max_size: None, mode: Measured) - /// The range of component `n` is `[0, 1024]`. - fn seal_return_per_kb(n: u32, ) -> Weight { - // Proof Size summary in bytes: - // Measured: `778` - // Estimated: `21755` - // Minimum execution time: 295_959_000 picoseconds. - Weight::from_parts(299_004_761, 21755) - // Standard Error: 3_988 - .saturating_add(Weight::from_parts(194_268, 0).saturating_mul(n.into())) + /// The range of component `n` is `[0, 1048576]`. + fn seal_return_per_byte(n: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `874` + // Estimated: `22255` + // Minimum execution time: 297_257_000 picoseconds. + Weight::from_parts(300_088_288, 22255) + // Standard Error: 1 + .saturating_add(Weight::from_parts(181, 0).saturating_mul(n.into())) .saturating_add(T::DbWeight::get().reads(6_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) } @@ -823,17 +820,17 @@ impl WeightInfo for SubstrateWeight { /// The range of component `r` is `[0, 1]`. fn seal_terminate(r: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `810 + r * (356 ±0)` - // Estimated: `25511 + r * (15321 ±0)` - // Minimum execution time: 294_626_000 picoseconds. - Weight::from_parts(297_839_179, 25511) - // Standard Error: 707_428 - .saturating_add(Weight::from_parts(81_507_620, 0).saturating_mul(r.into())) + // Measured: `906 + r * (452 ±0)` + // Estimated: `26183 + r * (15994 ±0)` + // Minimum execution time: 296_510_000 picoseconds. + Weight::from_parts(299_169_757, 26183) + // Standard Error: 205_313 + .saturating_add(Weight::from_parts(78_059_642, 0).saturating_mul(r.into())) .saturating_add(T::DbWeight::get().reads(6_u64)) .saturating_add(T::DbWeight::get().reads((6_u64).saturating_mul(r.into()))) .saturating_add(T::DbWeight::get().writes(3_u64)) .saturating_add(T::DbWeight::get().writes((7_u64).saturating_mul(r.into()))) - .saturating_add(Weight::from_parts(0, 15321).saturating_mul(r.into())) + .saturating_add(Weight::from_parts(0, 15994).saturating_mul(r.into())) } /// Storage: System Account (r:1 w:0) /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: Measured) @@ -847,18 +844,18 @@ impl WeightInfo for SubstrateWeight { /// Proof: RandomnessCollectiveFlip RandomMaterial (max_values: Some(1), max_size: Some(2594), added: 3089, mode: Measured) /// Storage: System EventTopics (r:2 w:2) /// Proof Skipped: System EventTopics (max_values: None, max_size: None, mode: Measured) - /// The range of component `r` is `[0, 20]`. + /// The range of component `r` is `[0, 1600]`. fn seal_random(r: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `825 + r * (800 ±0)` - // Estimated: `24199 + r * (4805 ±0)` - // Minimum execution time: 295_431_000 picoseconds. - Weight::from_parts(310_428_531, 24199) - // Standard Error: 143_501 - .saturating_add(Weight::from_parts(136_529_376, 0).saturating_mul(r.into())) + // Measured: `921 + r * (10 ±0)` + // Estimated: `24859 + r * (60 ±0)` + // Minimum execution time: 299_299_000 picoseconds. + Weight::from_parts(314_487_015, 24859) + // Standard Error: 1_552 + .saturating_add(Weight::from_parts(1_753_960, 0).saturating_mul(r.into())) .saturating_add(T::DbWeight::get().reads(7_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) - .saturating_add(Weight::from_parts(0, 4805).saturating_mul(r.into())) + .saturating_add(Weight::from_parts(0, 60).saturating_mul(r.into())) } /// Storage: System Account (r:1 w:0) /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: Measured) @@ -870,18 +867,18 @@ impl WeightInfo for SubstrateWeight { /// Proof: Timestamp Now (max_values: Some(1), max_size: Some(8), added: 503, mode: Measured) /// Storage: System EventTopics (r:2 w:2) /// Proof Skipped: System EventTopics (max_values: None, max_size: None, mode: Measured) - /// The range of component `r` is `[0, 20]`. + /// The range of component `r` is `[0, 1600]`. fn seal_deposit_event(r: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `778 + r * (800 ±0)` - // Estimated: `21720 + r * (4000 ±0)` - // Minimum execution time: 293_778_000 picoseconds. - Weight::from_parts(323_404_081, 21720) - // Standard Error: 243_095 - .saturating_add(Weight::from_parts(269_012_482, 0).saturating_mul(r.into())) + // Measured: `874 + r * (10 ±0)` + // Estimated: `22215 + r * (50 ±0)` + // Minimum execution time: 296_188_000 picoseconds. + Weight::from_parts(305_901_539, 22215) + // Standard Error: 2_782 + .saturating_add(Weight::from_parts(3_456_054, 0).saturating_mul(r.into())) .saturating_add(T::DbWeight::get().reads(6_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) - .saturating_add(Weight::from_parts(0, 4000).saturating_mul(r.into())) + .saturating_add(Weight::from_parts(0, 50).saturating_mul(r.into())) } /// Storage: System Account (r:1 w:0) /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: Measured) @@ -891,26 +888,25 @@ impl WeightInfo for SubstrateWeight { /// Proof: Contracts CodeStorage (max_values: None, max_size: Some(126001), added: 128476, mode: Measured) /// Storage: Timestamp Now (r:1 w:0) /// Proof: Timestamp Now (max_values: Some(1), max_size: Some(8), added: 503, mode: Measured) - /// Storage: System EventTopics (r:322 w:322) + /// Storage: System EventTopics (r:6 w:6) /// Proof Skipped: System EventTopics (max_values: None, max_size: None, mode: Measured) /// The range of component `t` is `[0, 4]`. - /// The range of component `n` is `[0, 16]`. - fn seal_deposit_event_per_topic_and_kb(t: u32, n: u32, ) -> Weight { - // Proof Size summary in bytes: - // Measured: `1725 + t * (2608 ±0) + n * (7 ±0)` - // Estimated: `26340 + t * (211030 ±0) + n * (50 ±0)` - // Minimum execution time: 1_317_129_000 picoseconds. - Weight::from_parts(566_722_295, 26340) - // Standard Error: 619_390 - .saturating_add(Weight::from_parts(194_334_643, 0).saturating_mul(t.into())) - // Standard Error: 170_114 - .saturating_add(Weight::from_parts(67_458_751, 0).saturating_mul(n.into())) + /// The range of component `n` is `[0, 16384]`. + fn seal_deposit_event_per_topic_and_byte(t: u32, n: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `893 + t * (32 ±0)` + // Estimated: `22320 + t * (2640 ±0)` + // Minimum execution time: 313_860_000 picoseconds. + Weight::from_parts(312_473_092, 22320) + // Standard Error: 250_852 + .saturating_add(Weight::from_parts(2_258_502, 0).saturating_mul(t.into())) + // Standard Error: 70 + .saturating_add(Weight::from_parts(312, 0).saturating_mul(n.into())) .saturating_add(T::DbWeight::get().reads(6_u64)) - .saturating_add(T::DbWeight::get().reads((80_u64).saturating_mul(t.into()))) + .saturating_add(T::DbWeight::get().reads((1_u64).saturating_mul(t.into()))) .saturating_add(T::DbWeight::get().writes(3_u64)) - .saturating_add(T::DbWeight::get().writes((80_u64).saturating_mul(t.into()))) - .saturating_add(Weight::from_parts(0, 211030).saturating_mul(t.into())) - .saturating_add(Weight::from_parts(0, 50).saturating_mul(n.into())) + .saturating_add(T::DbWeight::get().writes((1_u64).saturating_mul(t.into()))) + .saturating_add(Weight::from_parts(0, 2640).saturating_mul(t.into())) } /// Storage: System Account (r:1 w:0) /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: Measured) @@ -922,18 +918,18 @@ impl WeightInfo for SubstrateWeight { /// Proof: Timestamp Now (max_values: Some(1), max_size: Some(8), added: 503, mode: Measured) /// Storage: System EventTopics (r:2 w:2) /// Proof Skipped: System EventTopics (max_values: None, max_size: None, mode: Measured) - /// The range of component `r` is `[0, 20]`. + /// The range of component `r` is `[0, 1600]`. fn seal_debug_message(r: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `777 + r * (560 ±0)` - // Estimated: `21710 + r * (2800 ±0)` - // Minimum execution time: 159_087_000 picoseconds. - Weight::from_parts(163_703_981, 21710) - // Standard Error: 26_067 - .saturating_add(Weight::from_parts(14_810_256, 0).saturating_mul(r.into())) + // Measured: `873 + r * (7 ±0)` + // Estimated: `22205 + r * (35 ±0)` + // Minimum execution time: 162_043_000 picoseconds. + Weight::from_parts(166_132_332, 22205) + // Standard Error: 716 + .saturating_add(Weight::from_parts(184_981, 0).saturating_mul(r.into())) .saturating_add(T::DbWeight::get().reads(6_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) - .saturating_add(Weight::from_parts(0, 2800).saturating_mul(r.into())) + .saturating_add(Weight::from_parts(0, 35).saturating_mul(r.into())) } /// Storage: System Account (r:1 w:0) /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) @@ -945,200 +941,187 @@ impl WeightInfo for SubstrateWeight { /// Proof: Timestamp Now (max_values: Some(1), max_size: Some(8), added: 503, mode: MaxEncodedLen) /// Storage: System EventTopics (r:2 w:2) /// Proof Skipped: System EventTopics (max_values: None, max_size: None, mode: Measured) - /// The range of component `i` is `[0, 1024]`. - fn seal_debug_message_per_kb(i: u32, ) -> Weight { - // Proof Size summary in bytes: - // Measured: `125728` - // Estimated: `269982` - // Minimum execution time: 413_391_000 picoseconds. - Weight::from_parts(414_927_640, 269982) - // Standard Error: 1_740 - .saturating_add(Weight::from_parts(763_359, 0).saturating_mul(i.into())) + /// The range of component `i` is `[0, 1048576]`. + fn seal_debug_message_per_byte(i: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `125824` + // Estimated: `270073` + // Minimum execution time: 414_433_000 picoseconds. + Weight::from_parts(417_483_627, 270073) + // Standard Error: 1 + .saturating_add(Weight::from_parts(748, 0).saturating_mul(i.into())) .saturating_add(T::DbWeight::get().reads(6_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) } /// Storage: Skipped Metadata (r:0 w:0) /// Proof Skipped: Skipped Metadata (max_values: None, max_size: None, mode: Measured) - /// The range of component `r` is `[0, 10]`. + /// The range of component `r` is `[0, 800]`. fn seal_set_storage(r: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `815 + r * (23420 ±0)` - // Estimated: `815 + r * (23418 ±0)` - // Minimum execution time: 296_108_000 picoseconds. - Weight::from_parts(208_214_040, 815) - // Standard Error: 860_573 - .saturating_add(Weight::from_parts(480_342_550, 0).saturating_mul(r.into())) + // Measured: `941 + r * (292 ±0)` + // Estimated: `939 + r * (293 ±0)` + // Minimum execution time: 299_500_000 picoseconds. + Weight::from_parts(194_466_413, 939) + // Standard Error: 9_986 + .saturating_add(Weight::from_parts(6_010_112, 0).saturating_mul(r.into())) .saturating_add(T::DbWeight::get().reads(6_u64)) - .saturating_add(T::DbWeight::get().reads((80_u64).saturating_mul(r.into()))) + .saturating_add(T::DbWeight::get().reads((1_u64).saturating_mul(r.into()))) .saturating_add(T::DbWeight::get().writes(3_u64)) - .saturating_add(T::DbWeight::get().writes((80_u64).saturating_mul(r.into()))) - .saturating_add(Weight::from_parts(0, 23418).saturating_mul(r.into())) + .saturating_add(T::DbWeight::get().writes((1_u64).saturating_mul(r.into()))) + .saturating_add(Weight::from_parts(0, 293).saturating_mul(r.into())) } /// Storage: Skipped Metadata (r:0 w:0) /// Proof Skipped: Skipped Metadata (max_values: None, max_size: None, mode: Measured) - /// The range of component `n` is `[0, 8]`. - fn seal_set_storage_per_new_kb(n: u32, ) -> Weight { - // Proof Size summary in bytes: - // Measured: `12544 + n * (11945 ±0)` - // Estimated: `8401 + n * (12814 ±61)` - // Minimum execution time: 442_286_000 picoseconds. - Weight::from_parts(620_395_299, 8401) - // Standard Error: 1_661_554 - .saturating_add(Weight::from_parts(89_393_169, 0).saturating_mul(n.into())) - .saturating_add(T::DbWeight::get().reads(52_u64)) - .saturating_add(T::DbWeight::get().reads((7_u64).saturating_mul(n.into()))) - .saturating_add(T::DbWeight::get().writes(50_u64)) - .saturating_add(T::DbWeight::get().writes((7_u64).saturating_mul(n.into()))) - .saturating_add(Weight::from_parts(0, 12814).saturating_mul(n.into())) + /// The range of component `n` is `[0, 16384]`. + fn seal_set_storage_per_new_byte(n: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `1432` + // Estimated: `1405` + // Minimum execution time: 314_171_000 picoseconds. + Weight::from_parts(335_595_397, 1405) + // Standard Error: 67 + .saturating_add(Weight::from_parts(90, 0).saturating_mul(n.into())) + .saturating_add(T::DbWeight::get().reads(8_u64)) + .saturating_add(T::DbWeight::get().writes(6_u64)) } /// Storage: Skipped Metadata (r:0 w:0) /// Proof Skipped: Skipped Metadata (max_values: None, max_size: None, mode: Measured) - /// The range of component `n` is `[0, 8]`. - fn seal_set_storage_per_old_kb(n: u32, ) -> Weight { - // Proof Size summary in bytes: - // Measured: `12532 + n * (175784 ±0)` - // Estimated: `8396 + n * (176649 ±61)` - // Minimum execution time: 440_882_000 picoseconds. - Weight::from_parts(587_317_981, 8396) - // Standard Error: 1_374_991 - .saturating_add(Weight::from_parts(66_271_084, 0).saturating_mul(n.into())) - .saturating_add(T::DbWeight::get().reads(51_u64)) - .saturating_add(T::DbWeight::get().reads((7_u64).saturating_mul(n.into()))) - .saturating_add(T::DbWeight::get().writes(49_u64)) - .saturating_add(T::DbWeight::get().writes((7_u64).saturating_mul(n.into()))) - .saturating_add(Weight::from_parts(0, 176649).saturating_mul(n.into())) + /// The range of component `n` is `[0, 16384]`. + fn seal_set_storage_per_old_byte(n: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `1295 + n * (1 ±0)` + // Estimated: `1292 + n * (1 ±0)` + // Minimum execution time: 313_479_000 picoseconds. + Weight::from_parts(317_435_100, 1292) + // Standard Error: 41 + .saturating_add(Weight::from_parts(106, 0).saturating_mul(n.into())) + .saturating_add(T::DbWeight::get().reads(7_u64)) + .saturating_add(T::DbWeight::get().writes(4_u64)) + .saturating_add(Weight::from_parts(0, 1).saturating_mul(n.into())) } /// Storage: Skipped Metadata (r:0 w:0) /// Proof Skipped: Skipped Metadata (max_values: None, max_size: None, mode: Measured) - /// The range of component `r` is `[0, 10]`. + /// The range of component `r` is `[0, 800]`. fn seal_clear_storage(r: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `807 + r * (23099 ±0)` - // Estimated: `812 + r * (23099 ±0)` - // Minimum execution time: 296_023_000 picoseconds. - Weight::from_parts(221_471_559, 812) - // Standard Error: 814_503 - .saturating_add(Weight::from_parts(465_928_089, 0).saturating_mul(r.into())) + // Measured: `937 + r * (288 ±0)` + // Estimated: `941 + r * (289 ±0)` + // Minimum execution time: 297_831_000 picoseconds. + Weight::from_parts(196_983_778, 941) + // Standard Error: 9_899 + .saturating_add(Weight::from_parts(5_904_642, 0).saturating_mul(r.into())) .saturating_add(T::DbWeight::get().reads(6_u64)) - .saturating_add(T::DbWeight::get().reads((80_u64).saturating_mul(r.into()))) + .saturating_add(T::DbWeight::get().reads((1_u64).saturating_mul(r.into()))) .saturating_add(T::DbWeight::get().writes(3_u64)) - .saturating_add(T::DbWeight::get().writes((80_u64).saturating_mul(r.into()))) - .saturating_add(Weight::from_parts(0, 23099).saturating_mul(r.into())) + .saturating_add(T::DbWeight::get().writes((1_u64).saturating_mul(r.into()))) + .saturating_add(Weight::from_parts(0, 289).saturating_mul(r.into())) } /// Storage: Skipped Metadata (r:0 w:0) /// Proof Skipped: Skipped Metadata (max_values: None, max_size: None, mode: Measured) - /// The range of component `n` is `[0, 8]`. - fn seal_clear_storage_per_kb(n: u32, ) -> Weight { - // Proof Size summary in bytes: - // Measured: `12256 + n * (175776 ±0)` - // Estimated: `8047 + n * (176655 ±62)` - // Minimum execution time: 412_212_000 picoseconds. - Weight::from_parts(569_213_147, 8047) - // Standard Error: 1_463_771 - .saturating_add(Weight::from_parts(67_932_081, 0).saturating_mul(n.into())) - .saturating_add(T::DbWeight::get().reads(51_u64)) - .saturating_add(T::DbWeight::get().reads((7_u64).saturating_mul(n.into()))) - .saturating_add(T::DbWeight::get().writes(48_u64)) - .saturating_add(T::DbWeight::get().writes((7_u64).saturating_mul(n.into()))) - .saturating_add(Weight::from_parts(0, 176655).saturating_mul(n.into())) + /// The range of component `n` is `[0, 16384]`. + fn seal_clear_storage_per_byte(n: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `1291 + n * (1 ±0)` + // Estimated: `1288 + n * (1 ±0)` + // Minimum execution time: 320_156_000 picoseconds. + Weight::from_parts(327_504_368, 1288) + .saturating_add(T::DbWeight::get().reads(7_u64)) + .saturating_add(T::DbWeight::get().writes(4_u64)) + .saturating_add(Weight::from_parts(0, 1).saturating_mul(n.into())) } /// Storage: Skipped Metadata (r:0 w:0) /// Proof Skipped: Skipped Metadata (max_values: None, max_size: None, mode: Measured) - /// The range of component `r` is `[0, 10]`. + /// The range of component `r` is `[0, 800]`. fn seal_get_storage(r: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `800 + r * (23744 ±0)` - // Estimated: `813 + r * (23740 ±0)` - // Minimum execution time: 296_007_000 picoseconds. - Weight::from_parts(229_100_700, 813) - // Standard Error: 684_970 - .saturating_add(Weight::from_parts(394_982_991, 0).saturating_mul(r.into())) + // Measured: `931 + r * (296 ±0)` + // Estimated: `936 + r * (297 ±0)` + // Minimum execution time: 305_849_000 picoseconds. + Weight::from_parts(219_649_351, 936) + // Standard Error: 9_157 + .saturating_add(Weight::from_parts(4_846_108, 0).saturating_mul(r.into())) .saturating_add(T::DbWeight::get().reads(6_u64)) - .saturating_add(T::DbWeight::get().reads((80_u64).saturating_mul(r.into()))) + .saturating_add(T::DbWeight::get().reads((1_u64).saturating_mul(r.into()))) .saturating_add(T::DbWeight::get().writes(3_u64)) - .saturating_add(Weight::from_parts(0, 23740).saturating_mul(r.into())) + .saturating_add(Weight::from_parts(0, 297).saturating_mul(r.into())) } /// Storage: Skipped Metadata (r:0 w:0) /// Proof Skipped: Skipped Metadata (max_values: None, max_size: None, mode: Measured) - /// The range of component `n` is `[0, 8]`. - fn seal_get_storage_per_kb(n: u32, ) -> Weight { - // Proof Size summary in bytes: - // Measured: `12863 + n * (175784 ±0)` - // Estimated: `8538 + n * (176688 ±63)` - // Minimum execution time: 392_306_000 picoseconds. - Weight::from_parts(539_188_004, 8538) - // Standard Error: 1_371_839 - .saturating_add(Weight::from_parts(155_130_373, 0).saturating_mul(n.into())) - .saturating_add(T::DbWeight::get().reads(51_u64)) - .saturating_add(T::DbWeight::get().reads((7_u64).saturating_mul(n.into()))) + /// The range of component `n` is `[0, 16384]`. + fn seal_get_storage_per_byte(n: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `1307 + n * (1 ±0)` + // Estimated: `1304 + n * (1 ±0)` + // Minimum execution time: 312_106_000 picoseconds. + Weight::from_parts(315_905_779, 1304) + // Standard Error: 44 + .saturating_add(Weight::from_parts(674, 0).saturating_mul(n.into())) + .saturating_add(T::DbWeight::get().reads(7_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) - .saturating_add(Weight::from_parts(0, 176688).saturating_mul(n.into())) + .saturating_add(Weight::from_parts(0, 1).saturating_mul(n.into())) } /// Storage: Skipped Metadata (r:0 w:0) /// Proof Skipped: Skipped Metadata (max_values: None, max_size: None, mode: Measured) - /// The range of component `r` is `[0, 10]`. + /// The range of component `r` is `[0, 800]`. fn seal_contains_storage(r: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `818 + r * (23098 ±0)` - // Estimated: `824 + r * (23098 ±0)` - // Minimum execution time: 296_038_000 picoseconds. - Weight::from_parts(229_462_798, 824) - // Standard Error: 655_463 - .saturating_add(Weight::from_parts(372_593_685, 0).saturating_mul(r.into())) + // Measured: `952 + r * (288 ±0)` + // Estimated: `953 + r * (289 ±0)` + // Minimum execution time: 299_372_000 picoseconds. + Weight::from_parts(211_293_493, 953) + // Standard Error: 8_509 + .saturating_add(Weight::from_parts(4_688_993, 0).saturating_mul(r.into())) .saturating_add(T::DbWeight::get().reads(6_u64)) - .saturating_add(T::DbWeight::get().reads((80_u64).saturating_mul(r.into()))) + .saturating_add(T::DbWeight::get().reads((1_u64).saturating_mul(r.into()))) .saturating_add(T::DbWeight::get().writes(3_u64)) - .saturating_add(Weight::from_parts(0, 23098).saturating_mul(r.into())) + .saturating_add(Weight::from_parts(0, 289).saturating_mul(r.into())) } /// Storage: Skipped Metadata (r:0 w:0) /// Proof Skipped: Skipped Metadata (max_values: None, max_size: None, mode: Measured) - /// The range of component `n` is `[0, 8]`. - fn seal_contains_storage_per_kb(n: u32, ) -> Weight { - // Proof Size summary in bytes: - // Measured: `12200 + n * (175798 ±0)` - // Estimated: `8028 + n * (176661 ±62)` - // Minimum execution time: 386_782_000 picoseconds. - Weight::from_parts(512_965_975, 8028) - // Standard Error: 1_168_132 - .saturating_add(Weight::from_parts(63_307_325, 0).saturating_mul(n.into())) - .saturating_add(T::DbWeight::get().reads(51_u64)) - .saturating_add(T::DbWeight::get().reads((7_u64).saturating_mul(n.into()))) + /// The range of component `n` is `[0, 16384]`. + fn seal_contains_storage_per_byte(n: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `1294 + n * (1 ±0)` + // Estimated: `1291 + n * (1 ±0)` + // Minimum execution time: 311_605_000 picoseconds. + Weight::from_parts(315_473_850, 1291) + // Standard Error: 37 + .saturating_add(Weight::from_parts(3, 0).saturating_mul(n.into())) + .saturating_add(T::DbWeight::get().reads(7_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) - .saturating_add(Weight::from_parts(0, 176661).saturating_mul(n.into())) + .saturating_add(Weight::from_parts(0, 1).saturating_mul(n.into())) } /// Storage: Skipped Metadata (r:0 w:0) /// Proof Skipped: Skipped Metadata (max_values: None, max_size: None, mode: Measured) - /// The range of component `r` is `[0, 10]`. + /// The range of component `r` is `[0, 800]`. fn seal_take_storage(r: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `815 + r * (23740 ±0)` - // Estimated: `817 + r * (23739 ±0)` - // Minimum execution time: 296_352_000 picoseconds. - Weight::from_parts(212_661_296, 817) - // Standard Error: 879_297 - .saturating_add(Weight::from_parts(494_182_672, 0).saturating_mul(r.into())) + // Measured: `925 + r * (296 ±0)` + // Estimated: `932 + r * (297 ±0)` + // Minimum execution time: 298_231_000 picoseconds. + Weight::from_parts(200_178_698, 932) + // Standard Error: 10_452 + .saturating_add(Weight::from_parts(6_107_653, 0).saturating_mul(r.into())) .saturating_add(T::DbWeight::get().reads(6_u64)) - .saturating_add(T::DbWeight::get().reads((80_u64).saturating_mul(r.into()))) + .saturating_add(T::DbWeight::get().reads((1_u64).saturating_mul(r.into()))) .saturating_add(T::DbWeight::get().writes(3_u64)) - .saturating_add(T::DbWeight::get().writes((80_u64).saturating_mul(r.into()))) - .saturating_add(Weight::from_parts(0, 23739).saturating_mul(r.into())) + .saturating_add(T::DbWeight::get().writes((1_u64).saturating_mul(r.into()))) + .saturating_add(Weight::from_parts(0, 297).saturating_mul(r.into())) } /// Storage: Skipped Metadata (r:0 w:0) /// Proof Skipped: Skipped Metadata (max_values: None, max_size: None, mode: Measured) - /// The range of component `n` is `[0, 8]`. - fn seal_take_storage_per_kb(n: u32, ) -> Weight { - // Proof Size summary in bytes: - // Measured: `12864 + n * (175784 ±0)` - // Estimated: `8539 + n * (176686 ±63)` - // Minimum execution time: 414_693_000 picoseconds. - Weight::from_parts(595_693_859, 8539) - // Standard Error: 1_673_819 - .saturating_add(Weight::from_parts(163_090_316, 0).saturating_mul(n.into())) - .saturating_add(T::DbWeight::get().reads(51_u64)) - .saturating_add(T::DbWeight::get().reads((7_u64).saturating_mul(n.into()))) - .saturating_add(T::DbWeight::get().writes(48_u64)) - .saturating_add(T::DbWeight::get().writes((7_u64).saturating_mul(n.into()))) - .saturating_add(Weight::from_parts(0, 176686).saturating_mul(n.into())) + /// The range of component `n` is `[0, 16384]`. + fn seal_take_storage_per_byte(n: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `1308 + n * (1 ±0)` + // Estimated: `1305 + n * (1 ±0)` + // Minimum execution time: 314_970_000 picoseconds. + Weight::from_parts(318_135_821, 1305) + // Standard Error: 26 + .saturating_add(Weight::from_parts(630, 0).saturating_mul(n.into())) + .saturating_add(T::DbWeight::get().reads(7_u64)) + .saturating_add(T::DbWeight::get().writes(4_u64)) + .saturating_add(Weight::from_parts(0, 1).saturating_mul(n.into())) } /// Storage: System Account (r:1602 w:1601) /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: Measured) @@ -1150,20 +1133,20 @@ impl WeightInfo for SubstrateWeight { /// Proof: Timestamp Now (max_values: Some(1), max_size: Some(8), added: 503, mode: Measured) /// Storage: System EventTopics (r:2 w:2) /// Proof Skipped: System EventTopics (max_values: None, max_size: None, mode: Measured) - /// The range of component `r` is `[0, 20]`. + /// The range of component `r` is `[0, 1600]`. fn seal_transfer(r: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `1329 + r * (3604 ±0)` - // Estimated: `25928 + r * (216096 ±4)` - // Minimum execution time: 299_205_000 picoseconds. - Weight::from_parts(221_142_217, 25928) - // Standard Error: 1_369_909 - .saturating_add(Weight::from_parts(1_665_917_241, 0).saturating_mul(r.into())) + // Measured: `1501 + r * (45 ±0)` + // Estimated: `27383 + r * (2700 ±0)` + // Minimum execution time: 299_629_000 picoseconds. + Weight::from_parts(150_915_187, 27383) + // Standard Error: 25_604 + .saturating_add(Weight::from_parts(20_859_844, 0).saturating_mul(r.into())) .saturating_add(T::DbWeight::get().reads(7_u64)) - .saturating_add(T::DbWeight::get().reads((80_u64).saturating_mul(r.into()))) + .saturating_add(T::DbWeight::get().reads((1_u64).saturating_mul(r.into()))) .saturating_add(T::DbWeight::get().writes(4_u64)) - .saturating_add(T::DbWeight::get().writes((80_u64).saturating_mul(r.into()))) - .saturating_add(Weight::from_parts(0, 216096).saturating_mul(r.into())) + .saturating_add(T::DbWeight::get().writes((1_u64).saturating_mul(r.into()))) + .saturating_add(Weight::from_parts(0, 2700).saturating_mul(r.into())) } /// Storage: System Account (r:1 w:0) /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: Measured) @@ -1175,20 +1158,20 @@ impl WeightInfo for SubstrateWeight { /// Proof: Timestamp Now (max_values: Some(1), max_size: Some(8), added: 503, mode: Measured) /// Storage: System EventTopics (r:1602 w:1602) /// Proof Skipped: System EventTopics (max_values: None, max_size: None, mode: Measured) - /// The range of component `r` is `[0, 20]`. + /// The range of component `r` is `[0, 1600]`. fn seal_call(r: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `1479 + r * (20514 ±0)` - // Estimated: `26429 + r * (498656 ±1)` - // Minimum execution time: 297_501_000 picoseconds. - Weight::from_parts(298_400_000, 26429) - // Standard Error: 9_372_890 - .saturating_add(Weight::from_parts(22_507_984_433, 0).saturating_mul(r.into())) + // Measured: `1670 + r * (288 ±0)` + // Estimated: `27813 + r * (6391 ±0)` + // Minimum execution time: 299_578_000 picoseconds. + Weight::from_parts(300_036_000, 27813) + // Standard Error: 102_709 + .saturating_add(Weight::from_parts(283_767_316, 0).saturating_mul(r.into())) .saturating_add(T::DbWeight::get().reads(7_u64)) - .saturating_add(T::DbWeight::get().reads((160_u64).saturating_mul(r.into()))) + .saturating_add(T::DbWeight::get().reads((2_u64).saturating_mul(r.into()))) .saturating_add(T::DbWeight::get().writes(3_u64)) - .saturating_add(T::DbWeight::get().writes((160_u64).saturating_mul(r.into()))) - .saturating_add(Weight::from_parts(0, 498656).saturating_mul(r.into())) + .saturating_add(T::DbWeight::get().writes((2_u64).saturating_mul(r.into()))) + .saturating_add(Weight::from_parts(0, 6391).saturating_mul(r.into())) } /// Storage: System Account (r:1 w:0) /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: Measured) @@ -1200,48 +1183,48 @@ impl WeightInfo for SubstrateWeight { /// Proof: Timestamp Now (max_values: Some(1), max_size: Some(8), added: 503, mode: Measured) /// Storage: System EventTopics (r:1537 w:1537) /// Proof Skipped: System EventTopics (max_values: None, max_size: None, mode: Measured) - /// The range of component `r` is `[0, 20]`. + /// The range of component `r` is `[0, 1600]`. fn seal_delegate_call(r: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `0 + r * (69030 ±0)` - // Estimated: `21755 + r * (647542 ±560)` - // Minimum execution time: 297_432_000 picoseconds. - Weight::from_parts(298_220_000, 21755) - // Standard Error: 9_420_834 - .saturating_add(Weight::from_parts(22_261_152_353, 0).saturating_mul(r.into())) + // Measured: `0 + r * (935 ±0)` + // Estimated: `22235 + r * (8322 ±10)` + // Minimum execution time: 298_943_000 picoseconds. + Weight::from_parts(299_619_000, 22235) + // Standard Error: 117_493 + .saturating_add(Weight::from_parts(280_555_517, 0).saturating_mul(r.into())) .saturating_add(T::DbWeight::get().reads(6_u64)) - .saturating_add(T::DbWeight::get().reads((150_u64).saturating_mul(r.into()))) + .saturating_add(T::DbWeight::get().reads((2_u64).saturating_mul(r.into()))) .saturating_add(T::DbWeight::get().writes(3_u64)) - .saturating_add(T::DbWeight::get().writes((75_u64).saturating_mul(r.into()))) - .saturating_add(Weight::from_parts(0, 647542).saturating_mul(r.into())) + .saturating_add(T::DbWeight::get().writes((1_u64).saturating_mul(r.into()))) + .saturating_add(Weight::from_parts(0, 8322).saturating_mul(r.into())) } - /// Storage: System Account (r:82 w:81) + /// Storage: System Account (r:3 w:2) /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: Measured) - /// Storage: Contracts ContractInfoOf (r:81 w:81) + /// Storage: Contracts ContractInfoOf (r:2 w:2) /// Proof: Contracts ContractInfoOf (max_values: None, max_size: Some(290), added: 2765, mode: Measured) /// Storage: Contracts CodeStorage (r:2 w:0) /// Proof: Contracts CodeStorage (max_values: None, max_size: Some(126001), added: 128476, mode: Measured) /// Storage: Timestamp Now (r:1 w:0) /// Proof: Timestamp Now (max_values: Some(1), max_size: Some(8), added: 503, mode: Measured) - /// Storage: System EventTopics (r:82 w:82) + /// Storage: System EventTopics (r:3 w:3) /// Proof Skipped: System EventTopics (max_values: None, max_size: None, mode: Measured) /// The range of component `t` is `[0, 1]`. - /// The range of component `c` is `[0, 1024]`. - fn seal_call_per_transfer_clone_kb(t: u32, c: u32, ) -> Weight { - // Proof Size summary in bytes: - // Measured: `21581 + t * (14318 ±0)` - // Estimated: `524200 + t * (272065 ±0)` - // Minimum execution time: 11_799_802_000 picoseconds. - Weight::from_parts(10_575_328_547, 524200) - // Standard Error: 11_952_810 - .saturating_add(Weight::from_parts(1_603_077_083, 0).saturating_mul(t.into())) - // Standard Error: 17_922 - .saturating_add(Weight::from_parts(9_876_752, 0).saturating_mul(c.into())) - .saturating_add(T::DbWeight::get().reads(167_u64)) - .saturating_add(T::DbWeight::get().reads((81_u64).saturating_mul(t.into()))) - .saturating_add(T::DbWeight::get().writes(163_u64)) - .saturating_add(T::DbWeight::get().writes((81_u64).saturating_mul(t.into()))) - .saturating_add(Weight::from_parts(0, 272065).saturating_mul(t.into())) + /// The range of component `c` is `[0, 1048576]`. + fn seal_call_per_transfer_clone_byte(t: u32, c: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `1314 + t * (268 ±0)` + // Estimated: `31815 + t * (6290 ±0)` + // Minimum execution time: 453_013_000 picoseconds. + Weight::from_parts(442_536_283, 31815) + // Standard Error: 1_339_541 + .saturating_add(Weight::from_parts(17_062_445, 0).saturating_mul(t.into())) + // Standard Error: 1 + .saturating_add(Weight::from_parts(604, 0).saturating_mul(c.into())) + .saturating_add(T::DbWeight::get().reads(9_u64)) + .saturating_add(T::DbWeight::get().reads((2_u64).saturating_mul(t.into()))) + .saturating_add(T::DbWeight::get().writes(5_u64)) + .saturating_add(T::DbWeight::get().writes((2_u64).saturating_mul(t.into()))) + .saturating_add(Weight::from_parts(0, 6290).saturating_mul(t.into())) } /// Storage: System Account (r:3202 w:3202) /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: Measured) @@ -1257,24 +1240,24 @@ impl WeightInfo for SubstrateWeight { /// Proof: Contracts OwnerInfoOf (max_values: None, max_size: Some(88), added: 2563, mode: Measured) /// Storage: System EventTopics (r:1602 w:1602) /// Proof Skipped: System EventTopics (max_values: None, max_size: None, mode: Measured) - /// The range of component `r` is `[0, 20]`. + /// The range of component `r` is `[0, 1600]`. fn seal_instantiate(r: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `1646 + r * (20288 ±0)` - // Estimated: `32625 + r * (1330150 ±2)` - // Minimum execution time: 298_155_000 picoseconds. - Weight::from_parts(299_212_000, 32625) - // Standard Error: 32_410_313 - .saturating_add(Weight::from_parts(30_417_025_178, 0).saturating_mul(r.into())) + // Measured: `1988 + r * (319 ±0)` + // Estimated: `34745 + r * (17090 ±0)` + // Minimum execution time: 300_123_000 picoseconds. + Weight::from_parts(300_406_000, 34745) + // Standard Error: 420_997 + .saturating_add(Weight::from_parts(382_704_025, 0).saturating_mul(r.into())) .saturating_add(T::DbWeight::get().reads(8_u64)) - .saturating_add(T::DbWeight::get().reads((480_u64).saturating_mul(r.into()))) - .saturating_add(T::DbWeight::get().writes(5_u64)) - .saturating_add(T::DbWeight::get().writes((400_u64).saturating_mul(r.into()))) - .saturating_add(Weight::from_parts(0, 1330150).saturating_mul(r.into())) + .saturating_add(T::DbWeight::get().reads((6_u64).saturating_mul(r.into()))) + .saturating_add(T::DbWeight::get().writes(6_u64)) + .saturating_add(T::DbWeight::get().writes((5_u64).saturating_mul(r.into()))) + .saturating_add(Weight::from_parts(0, 17090).saturating_mul(r.into())) } - /// Storage: System Account (r:162 w:162) + /// Storage: System Account (r:4 w:4) /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: Measured) - /// Storage: Contracts ContractInfoOf (r:81 w:81) + /// Storage: Contracts ContractInfoOf (r:2 w:2) /// Proof: Contracts ContractInfoOf (max_values: None, max_size: Some(290), added: 2765, mode: Measured) /// Storage: Contracts CodeStorage (r:2 w:0) /// Proof: Contracts CodeStorage (max_values: None, max_size: Some(126001), added: 128476, mode: Measured) @@ -1284,28 +1267,28 @@ impl WeightInfo for SubstrateWeight { /// Proof: Contracts Nonce (max_values: Some(1), max_size: Some(8), added: 503, mode: Measured) /// Storage: Contracts OwnerInfoOf (r:1 w:1) /// Proof: Contracts OwnerInfoOf (max_values: None, max_size: Some(88), added: 2563, mode: Measured) - /// Storage: System EventTopics (r:82 w:82) + /// Storage: System EventTopics (r:3 w:3) /// Proof Skipped: System EventTopics (max_values: None, max_size: None, mode: Measured) /// The range of component `t` is `[0, 1]`. - /// The range of component `i` is `[0, 960]`. - /// The range of component `s` is `[0, 960]`. - fn seal_instantiate_per_transfer_input_salt_kb(t: u32, i: u32, s: u32, ) -> Weight { - // Proof Size summary in bytes: - // Measured: `5625 + t * (1 ±0)` - // Estimated: `856795 + t * (2471 ±3)` - // Minimum execution time: 105_914_928_000 picoseconds. - Weight::from_parts(13_229_760_432, 856795) - // Standard Error: 96_394_437 - .saturating_add(Weight::from_parts(398_413_888, 0).saturating_mul(t.into())) - // Standard Error: 157_192 - .saturating_add(Weight::from_parts(97_104_978, 0).saturating_mul(i.into())) - // Standard Error: 157_192 - .saturating_add(Weight::from_parts(97_146_130, 0).saturating_mul(s.into())) - .saturating_add(T::DbWeight::get().reads(329_u64)) + /// The range of component `i` is `[0, 983040]`. + /// The range of component `s` is `[0, 983040]`. + fn seal_instantiate_per_transfer_input_salt_byte(t: u32, i: u32, s: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `1231 + t * (219 ±0)` + // Estimated: `43797 + t * (3812 ±2)` + // Minimum execution time: 1_636_322_000 picoseconds. + Weight::from_parts(360_859_331, 43797) + // Standard Error: 4_816_923 + .saturating_add(Weight::from_parts(109_179_023, 0).saturating_mul(t.into())) + // Standard Error: 7 + .saturating_add(Weight::from_parts(1_180, 0).saturating_mul(i.into())) + // Standard Error: 7 + .saturating_add(Weight::from_parts(1_344, 0).saturating_mul(s.into())) + .saturating_add(T::DbWeight::get().reads(13_u64)) .saturating_add(T::DbWeight::get().reads((1_u64).saturating_mul(t.into()))) - .saturating_add(T::DbWeight::get().writes(326_u64)) + .saturating_add(T::DbWeight::get().writes(10_u64)) .saturating_add(T::DbWeight::get().writes((1_u64).saturating_mul(t.into()))) - .saturating_add(Weight::from_parts(0, 2471).saturating_mul(t.into())) + .saturating_add(Weight::from_parts(0, 3812).saturating_mul(t.into())) } /// Storage: System Account (r:1 w:0) /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: Measured) @@ -1317,18 +1300,18 @@ impl WeightInfo for SubstrateWeight { /// Proof: Timestamp Now (max_values: Some(1), max_size: Some(8), added: 503, mode: Measured) /// Storage: System EventTopics (r:2 w:2) /// Proof Skipped: System EventTopics (max_values: None, max_size: None, mode: Measured) - /// The range of component `r` is `[0, 1]`. + /// The range of component `r` is `[0, 1600]`. fn seal_hash_sha2_256(r: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `775 + r * (642 ±0)` - // Estimated: `21695 + r * (3210 ±0)` - // Minimum execution time: 294_309_000 picoseconds. - Weight::from_parts(296_241_318, 21695) - // Standard Error: 127_872 - .saturating_add(Weight::from_parts(45_806_281, 0).saturating_mul(r.into())) + // Measured: `873 + r * (8 ±0)` + // Estimated: `22190 + r * (40 ±0)` + // Minimum execution time: 297_521_000 picoseconds. + Weight::from_parts(303_523_260, 22190) + // Standard Error: 1_162 + .saturating_add(Weight::from_parts(542_201, 0).saturating_mul(r.into())) .saturating_add(T::DbWeight::get().reads(6_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) - .saturating_add(Weight::from_parts(0, 3210).saturating_mul(r.into())) + .saturating_add(Weight::from_parts(0, 40).saturating_mul(r.into())) } /// Storage: System Account (r:1 w:0) /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: Measured) @@ -1340,15 +1323,15 @@ impl WeightInfo for SubstrateWeight { /// Proof: Timestamp Now (max_values: Some(1), max_size: Some(8), added: 503, mode: Measured) /// Storage: System EventTopics (r:2 w:2) /// Proof Skipped: System EventTopics (max_values: None, max_size: None, mode: Measured) - /// The range of component `n` is `[0, 1024]`. - fn seal_hash_sha2_256_per_kb(n: u32, ) -> Weight { - // Proof Size summary in bytes: - // Measured: `1577` - // Estimated: `25630` - // Minimum execution time: 341_248_000 picoseconds. - Weight::from_parts(341_607_000, 25630) - // Standard Error: 66_687 - .saturating_add(Weight::from_parts(322_250_398, 0).saturating_mul(n.into())) + /// The range of component `n` is `[0, 1048576]`. + fn seal_hash_sha2_256_per_byte(n: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `881` + // Estimated: `22225` + // Minimum execution time: 299_877_000 picoseconds. + Weight::from_parts(293_538_014, 22225) + // Standard Error: 2 + .saturating_add(Weight::from_parts(3_967, 0).saturating_mul(n.into())) .saturating_add(T::DbWeight::get().reads(6_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) } @@ -1362,18 +1345,18 @@ impl WeightInfo for SubstrateWeight { /// Proof: Timestamp Now (max_values: Some(1), max_size: Some(8), added: 503, mode: Measured) /// Storage: System EventTopics (r:2 w:2) /// Proof Skipped: System EventTopics (max_values: None, max_size: None, mode: Measured) - /// The range of component `r` is `[0, 1]`. + /// The range of component `r` is `[0, 1600]`. fn seal_hash_keccak_256(r: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `777 + r * (642 ±0)` - // Estimated: `21705 + r * (3210 ±0)` - // Minimum execution time: 293_810_000 picoseconds. - Weight::from_parts(296_719_171, 21705) - // Standard Error: 450_807 - .saturating_add(Weight::from_parts(61_627_228, 0).saturating_mul(r.into())) + // Measured: `875 + r * (8 ±0)` + // Estimated: `22205 + r * (40 ±0)` + // Minimum execution time: 297_672_000 picoseconds. + Weight::from_parts(299_933_312, 22205) + // Standard Error: 1_138 + .saturating_add(Weight::from_parts(713_189, 0).saturating_mul(r.into())) .saturating_add(T::DbWeight::get().reads(6_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) - .saturating_add(Weight::from_parts(0, 3210).saturating_mul(r.into())) + .saturating_add(Weight::from_parts(0, 40).saturating_mul(r.into())) } /// Storage: System Account (r:1 w:0) /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: Measured) @@ -1385,15 +1368,15 @@ impl WeightInfo for SubstrateWeight { /// Proof: Timestamp Now (max_values: Some(1), max_size: Some(8), added: 503, mode: Measured) /// Storage: System EventTopics (r:2 w:2) /// Proof Skipped: System EventTopics (max_values: None, max_size: None, mode: Measured) - /// The range of component `n` is `[0, 1024]`. - fn seal_hash_keccak_256_per_kb(n: u32, ) -> Weight { - // Proof Size summary in bytes: - // Measured: `1579` - // Estimated: `25675` - // Minimum execution time: 354_550_000 picoseconds. - Weight::from_parts(355_136_000, 25675) - // Standard Error: 61_761 - .saturating_add(Weight::from_parts(257_021_566, 0).saturating_mul(n.into())) + /// The range of component `n` is `[0, 1048576]`. + fn seal_hash_keccak_256_per_byte(n: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `883` + // Estimated: `22245` + // Minimum execution time: 299_048_000 picoseconds. + Weight::from_parts(293_055_982, 22245) + // Standard Error: 2 + .saturating_add(Weight::from_parts(3_179, 0).saturating_mul(n.into())) .saturating_add(T::DbWeight::get().reads(6_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) } @@ -1407,18 +1390,18 @@ impl WeightInfo for SubstrateWeight { /// Proof: Timestamp Now (max_values: Some(1), max_size: Some(8), added: 503, mode: Measured) /// Storage: System EventTopics (r:2 w:2) /// Proof Skipped: System EventTopics (max_values: None, max_size: None, mode: Measured) - /// The range of component `r` is `[0, 1]`. + /// The range of component `r` is `[0, 1600]`. fn seal_hash_blake2_256(r: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `777 + r * (642 ±0)` - // Estimated: `21705 + r * (3210 ±0)` - // Minimum execution time: 293_369_000 picoseconds. - Weight::from_parts(295_990_893, 21705) - // Standard Error: 431_269 - .saturating_add(Weight::from_parts(32_213_406, 0).saturating_mul(r.into())) + // Measured: `875 + r * (8 ±0)` + // Estimated: `22220 + r * (40 ±0)` + // Minimum execution time: 301_991_000 picoseconds. + Weight::from_parts(300_027_441, 22220) + // Standard Error: 981 + .saturating_add(Weight::from_parts(391_319, 0).saturating_mul(r.into())) .saturating_add(T::DbWeight::get().reads(6_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) - .saturating_add(Weight::from_parts(0, 3210).saturating_mul(r.into())) + .saturating_add(Weight::from_parts(0, 40).saturating_mul(r.into())) } /// Storage: System Account (r:1 w:0) /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: Measured) @@ -1430,15 +1413,15 @@ impl WeightInfo for SubstrateWeight { /// Proof: Timestamp Now (max_values: Some(1), max_size: Some(8), added: 503, mode: Measured) /// Storage: System EventTopics (r:2 w:2) /// Proof Skipped: System EventTopics (max_values: None, max_size: None, mode: Measured) - /// The range of component `n` is `[0, 1024]`. - fn seal_hash_blake2_256_per_kb(n: u32, ) -> Weight { - // Proof Size summary in bytes: - // Measured: `1579` - // Estimated: `25650` - // Minimum execution time: 326_595_000 picoseconds. - Weight::from_parts(327_188_000, 25650) - // Standard Error: 53_385 - .saturating_add(Weight::from_parts(74_046_074, 0).saturating_mul(n.into())) + /// The range of component `n` is `[0, 1048576]`. + fn seal_hash_blake2_256_per_byte(n: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `883` + // Estimated: `22265` + // Minimum execution time: 296_522_000 picoseconds. + Weight::from_parts(296_121_638, 22265) + // Standard Error: 2 + .saturating_add(Weight::from_parts(916, 0).saturating_mul(n.into())) .saturating_add(T::DbWeight::get().reads(6_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) } @@ -1452,18 +1435,18 @@ impl WeightInfo for SubstrateWeight { /// Proof: Timestamp Now (max_values: Some(1), max_size: Some(8), added: 503, mode: Measured) /// Storage: System EventTopics (r:2 w:2) /// Proof Skipped: System EventTopics (max_values: None, max_size: None, mode: Measured) - /// The range of component `r` is `[0, 1]`. + /// The range of component `r` is `[0, 1600]`. fn seal_hash_blake2_128(r: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `777 + r * (679 ±0)` - // Estimated: `21705 + r * (3395 ±0)` - // Minimum execution time: 292_388_000 picoseconds. - Weight::from_parts(295_090_179, 21705) - // Standard Error: 170_487 - .saturating_add(Weight::from_parts(32_577_820, 0).saturating_mul(r.into())) + // Measured: `875 + r * (8 ±0)` + // Estimated: `22225 + r * (40 ±0)` + // Minimum execution time: 296_228_000 picoseconds. + Weight::from_parts(301_472_299, 22225) + // Standard Error: 875 + .saturating_add(Weight::from_parts(381_027, 0).saturating_mul(r.into())) .saturating_add(T::DbWeight::get().reads(6_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) - .saturating_add(Weight::from_parts(0, 3395).saturating_mul(r.into())) + .saturating_add(Weight::from_parts(0, 40).saturating_mul(r.into())) } /// Storage: System Account (r:1 w:0) /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: Measured) @@ -1475,15 +1458,15 @@ impl WeightInfo for SubstrateWeight { /// Proof: Timestamp Now (max_values: Some(1), max_size: Some(8), added: 503, mode: Measured) /// Storage: System EventTopics (r:2 w:2) /// Proof Skipped: System EventTopics (max_values: None, max_size: None, mode: Measured) - /// The range of component `n` is `[0, 1024]`. - fn seal_hash_blake2_128_per_kb(n: u32, ) -> Weight { - // Proof Size summary in bytes: - // Measured: `1579` - // Estimated: `25695` - // Minimum execution time: 326_862_000 picoseconds. - Weight::from_parts(327_449_000, 25695) - // Standard Error: 54_539 - .saturating_add(Weight::from_parts(74_042_337, 0).saturating_mul(n.into())) + /// The range of component `n` is `[0, 1048576]`. + fn seal_hash_blake2_128_per_byte(n: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `883` + // Estimated: `22235` + // Minimum execution time: 296_644_000 picoseconds. + Weight::from_parts(289_879_744, 22235) + // Standard Error: 2 + .saturating_add(Weight::from_parts(925, 0).saturating_mul(n.into())) .saturating_add(T::DbWeight::get().reads(6_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) } @@ -1497,18 +1480,18 @@ impl WeightInfo for SubstrateWeight { /// Proof: Timestamp Now (max_values: Some(1), max_size: Some(8), added: 503, mode: Measured) /// Storage: System EventTopics (r:2 w:2) /// Proof Skipped: System EventTopics (max_values: None, max_size: None, mode: Measured) - /// The range of component `r` is `[0, 1]`. + /// The range of component `r` is `[0, 1600]`. fn seal_ecdsa_recover(r: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `821 + r * (6083 ±0)` - // Estimated: `21925 + r * (30415 ±0)` - // Minimum execution time: 295_597_000 picoseconds. - Weight::from_parts(298_267_200, 21925) - // Standard Error: 699_824 - .saturating_add(Weight::from_parts(3_021_734_700, 0).saturating_mul(r.into())) + // Measured: `800 + r * (78 ±0)` + // Estimated: `21845 + r * (390 ±0)` + // Minimum execution time: 297_804_000 picoseconds. + Weight::from_parts(471_994_534, 21845) + // Standard Error: 9_479 + .saturating_add(Weight::from_parts(36_886_028, 0).saturating_mul(r.into())) .saturating_add(T::DbWeight::get().reads(6_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) - .saturating_add(Weight::from_parts(0, 30415).saturating_mul(r.into())) + .saturating_add(Weight::from_parts(0, 390).saturating_mul(r.into())) } /// Storage: System Account (r:1 w:0) /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: Measured) @@ -1520,18 +1503,18 @@ impl WeightInfo for SubstrateWeight { /// Proof: Timestamp Now (max_values: Some(1), max_size: Some(8), added: 503, mode: Measured) /// Storage: System EventTopics (r:2 w:2) /// Proof Skipped: System EventTopics (max_values: None, max_size: None, mode: Measured) - /// The range of component `r` is `[0, 1]`. + /// The range of component `r` is `[0, 1600]`. fn seal_ecdsa_to_eth_address(r: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `790 + r * (3362 ±0)` - // Estimated: `21770 + r * (16810 ±0)` - // Minimum execution time: 295_539_000 picoseconds. - Weight::from_parts(297_844_822, 21770) - // Standard Error: 490_532 - .saturating_add(Weight::from_parts(747_592_977, 0).saturating_mul(r.into())) + // Measured: `645 + r * (44 ±0)` + // Estimated: `21110 + r * (220 ±0)` + // Minimum execution time: 298_623_000 picoseconds. + Weight::from_parts(322_192_102, 21110) + // Standard Error: 3_207 + .saturating_add(Weight::from_parts(9_243_653, 0).saturating_mul(r.into())) .saturating_add(T::DbWeight::get().reads(6_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) - .saturating_add(Weight::from_parts(0, 16810).saturating_mul(r.into())) + .saturating_add(Weight::from_parts(0, 220).saturating_mul(r.into())) } /// Storage: System Account (r:1 w:0) /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: Measured) @@ -1545,20 +1528,20 @@ impl WeightInfo for SubstrateWeight { /// Proof: Contracts OwnerInfoOf (max_values: None, max_size: Some(88), added: 2563, mode: Measured) /// Storage: System EventTopics (r:1538 w:1538) /// Proof Skipped: System EventTopics (max_values: None, max_size: None, mode: Measured) - /// The range of component `r` is `[0, 20]`. + /// The range of component `r` is `[0, 1600]`. fn seal_set_code_hash(r: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `0 + r * (74020 ±0)` - // Estimated: `69192 + r * (913289 ±821)` - // Minimum execution time: 295_164_000 picoseconds. - Weight::from_parts(296_597_000, 69192) - // Standard Error: 3_646_577 - .saturating_add(Weight::from_parts(1_725_796_807, 0).saturating_mul(r.into())) + // Measured: `0 + r * (1030 ±0)` + // Estimated: `30592 + r * (11919 ±7)` + // Minimum execution time: 298_574_000 picoseconds. + Weight::from_parts(299_383_000, 30592) + // Standard Error: 44_061 + .saturating_add(Weight::from_parts(21_625_366, 0).saturating_mul(r.into())) .saturating_add(T::DbWeight::get().reads(6_u64)) - .saturating_add(T::DbWeight::get().reads((225_u64).saturating_mul(r.into()))) + .saturating_add(T::DbWeight::get().reads((3_u64).saturating_mul(r.into()))) .saturating_add(T::DbWeight::get().writes(3_u64)) - .saturating_add(T::DbWeight::get().writes((150_u64).saturating_mul(r.into()))) - .saturating_add(Weight::from_parts(0, 913289).saturating_mul(r.into())) + .saturating_add(T::DbWeight::get().writes((2_u64).saturating_mul(r.into()))) + .saturating_add(Weight::from_parts(0, 11919).saturating_mul(r.into())) } /// Storage: System Account (r:1 w:0) /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: Measured) @@ -1570,18 +1553,18 @@ impl WeightInfo for SubstrateWeight { /// Proof: Timestamp Now (max_values: Some(1), max_size: Some(8), added: 503, mode: Measured) /// Storage: System EventTopics (r:2 w:2) /// Proof Skipped: System EventTopics (max_values: None, max_size: None, mode: Measured) - /// The range of component `r` is `[0, 20]`. + /// The range of component `r` is `[0, 1600]`. fn seal_reentrance_count(r: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `773 + r * (240 ±0)` - // Estimated: `21685 + r * (1200 ±0)` - // Minimum execution time: 296_287_000 picoseconds. - Weight::from_parts(300_696_694, 21685) - // Standard Error: 27_891 - .saturating_add(Weight::from_parts(10_943_994, 0).saturating_mul(r.into())) + // Measured: `869 + r * (3 ±0)` + // Estimated: `22215 + r * (15 ±0)` + // Minimum execution time: 297_014_000 picoseconds. + Weight::from_parts(301_226_615, 22215) + // Standard Error: 439 + .saturating_add(Weight::from_parts(143_017, 0).saturating_mul(r.into())) .saturating_add(T::DbWeight::get().reads(6_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) - .saturating_add(Weight::from_parts(0, 1200).saturating_mul(r.into())) + .saturating_add(Weight::from_parts(0, 15).saturating_mul(r.into())) } /// Storage: System Account (r:1 w:0) /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: Measured) @@ -1593,18 +1576,18 @@ impl WeightInfo for SubstrateWeight { /// Proof: Timestamp Now (max_values: Some(1), max_size: Some(8), added: 503, mode: Measured) /// Storage: System EventTopics (r:2 w:2) /// Proof Skipped: System EventTopics (max_values: None, max_size: None, mode: Measured) - /// The range of component `r` is `[0, 20]`. + /// The range of component `r` is `[0, 1600]`. fn seal_account_reentrance_count(r: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `2006 + r * (3154 ±0)` - // Estimated: `26435 + r * (15875 ±2)` - // Minimum execution time: 298_000_000 picoseconds. - Weight::from_parts(328_282_119, 26435) - // Standard Error: 104_874 - .saturating_add(Weight::from_parts(18_428_267, 0).saturating_mul(r.into())) + // Measured: `2072 + r * (39 ±0)` + // Estimated: `27645 + r * (200 ±0)` + // Minimum execution time: 299_074_000 picoseconds. + Weight::from_parts(336_979_016, 27645) + // Standard Error: 1_163 + .saturating_add(Weight::from_parts(227_998, 0).saturating_mul(r.into())) .saturating_add(T::DbWeight::get().reads(6_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) - .saturating_add(Weight::from_parts(0, 15875).saturating_mul(r.into())) + .saturating_add(Weight::from_parts(0, 200).saturating_mul(r.into())) } /// Storage: System Account (r:1 w:0) /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: Measured) @@ -1618,538 +1601,526 @@ impl WeightInfo for SubstrateWeight { /// Proof: Contracts Nonce (max_values: Some(1), max_size: Some(8), added: 503, mode: Measured) /// Storage: System EventTopics (r:2 w:2) /// Proof Skipped: System EventTopics (max_values: None, max_size: None, mode: Measured) - /// The range of component `r` is `[0, 20]`. + /// The range of component `r` is `[0, 1600]`. fn seal_instantiation_nonce(r: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `776 + r * (240 ±0)` - // Estimated: `23962 + r * (1440 ±0)` - // Minimum execution time: 295_104_000 picoseconds. - Weight::from_parts(299_334_585, 23962) - // Standard Error: 25_840 - .saturating_add(Weight::from_parts(9_744_866, 0).saturating_mul(r.into())) + // Measured: `872 + r * (3 ±0)` + // Estimated: `24580 + r * (18 ±0)` + // Minimum execution time: 296_959_000 picoseconds. + Weight::from_parts(303_796_839, 24580) + // Standard Error: 534 + .saturating_add(Weight::from_parts(118_978, 0).saturating_mul(r.into())) .saturating_add(T::DbWeight::get().reads(7_u64)) .saturating_add(T::DbWeight::get().writes(4_u64)) - .saturating_add(Weight::from_parts(0, 1440).saturating_mul(r.into())) + .saturating_add(Weight::from_parts(0, 18).saturating_mul(r.into())) } - /// The range of component `r` is `[0, 50]`. + /// The range of component `r` is `[0, 5000]`. fn instr_i64const(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_132_000 picoseconds. - Weight::from_parts(1_100_159, 0) - // Standard Error: 10_131 - .saturating_add(Weight::from_parts(436_462, 0).saturating_mul(r.into())) + // Minimum execution time: 1_020_000 picoseconds. + Weight::from_parts(1_355_107, 0) + // Standard Error: 1 + .saturating_add(Weight::from_parts(4_110, 0).saturating_mul(r.into())) } - /// The range of component `r` is `[0, 50]`. + /// The range of component `r` is `[0, 5000]`. fn instr_i64load(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_368_000 picoseconds. - Weight::from_parts(1_974_308, 0) - // Standard Error: 811 - .saturating_add(Weight::from_parts(1_079_620, 0).saturating_mul(r.into())) + // Minimum execution time: 1_224_000 picoseconds. + Weight::from_parts(1_819_284, 0) + // Standard Error: 10 + .saturating_add(Weight::from_parts(10_803, 0).saturating_mul(r.into())) } - /// The range of component `r` is `[0, 50]`. + /// The range of component `r` is `[0, 5000]`. fn instr_i64store(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_354_000 picoseconds. - Weight::from_parts(2_057_834, 0) - // Standard Error: 4_277 - .saturating_add(Weight::from_parts(1_008_797, 0).saturating_mul(r.into())) + // Minimum execution time: 1_216_000 picoseconds. + Weight::from_parts(1_795_011, 0) + // Standard Error: 5 + .saturating_add(Weight::from_parts(10_000, 0).saturating_mul(r.into())) } - /// The range of component `r` is `[0, 50]`. + /// The range of component `r` is `[0, 5000]`. fn instr_select(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_218_000 picoseconds. - Weight::from_parts(1_594_528, 0) - // Standard Error: 723 - .saturating_add(Weight::from_parts(1_147_486, 0).saturating_mul(r.into())) + // Minimum execution time: 1_127_000 picoseconds. + Weight::from_parts(1_491_730, 0) + // Standard Error: 4 + .saturating_add(Weight::from_parts(11_471, 0).saturating_mul(r.into())) } - /// The range of component `r` is `[0, 50]`. + /// The range of component `r` is `[0, 5000]`. fn instr_if(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_156_000 picoseconds. - Weight::from_parts(1_460_611, 0) - // Standard Error: 419 - .saturating_add(Weight::from_parts(1_314_741, 0).saturating_mul(r.into())) + // Minimum execution time: 1_044_000 picoseconds. + Weight::from_parts(2_330_852, 0) + // Standard Error: 69 + .saturating_add(Weight::from_parts(12_866, 0).saturating_mul(r.into())) } - /// The range of component `r` is `[0, 50]`. + /// The range of component `r` is `[0, 5000]`. fn instr_br(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_166_000 picoseconds. - Weight::from_parts(1_454_460, 0) - // Standard Error: 493 - .saturating_add(Weight::from_parts(642_481, 0).saturating_mul(r.into())) + // Minimum execution time: 1_067_000 picoseconds. + Weight::from_parts(1_399_626, 0) + // Standard Error: 5 + .saturating_add(Weight::from_parts(6_430, 0).saturating_mul(r.into())) } - /// The range of component `r` is `[0, 50]`. + /// The range of component `r` is `[0, 5000]`. fn instr_br_if(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_144_000 picoseconds. - Weight::from_parts(1_410_515, 0) - // Standard Error: 894 - .saturating_add(Weight::from_parts(958_765, 0).saturating_mul(r.into())) + // Minimum execution time: 1_087_000 picoseconds. + Weight::from_parts(1_463_592, 0) + // Standard Error: 12 + .saturating_add(Weight::from_parts(9_707, 0).saturating_mul(r.into())) } - /// The range of component `r` is `[0, 50]`. + /// The range of component `r` is `[0, 5000]`. fn instr_br_table(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_190_000 picoseconds. - Weight::from_parts(1_143_236, 0) - // Standard Error: 2_773 - .saturating_add(Weight::from_parts(1_164_764, 0).saturating_mul(r.into())) + // Minimum execution time: 1_110_000 picoseconds. + Weight::from_parts(1_058_258, 0) + // Standard Error: 22 + .saturating_add(Weight::from_parts(11_713, 0).saturating_mul(r.into())) } /// The range of component `e` is `[1, 256]`. - fn instr_br_table_per_entry(e: u32, ) -> Weight { + fn instr_br_table_per_entry(_e: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 2_993_000 picoseconds. - Weight::from_parts(3_270_525, 0) - // Standard Error: 65 - .saturating_add(Weight::from_parts(4_418, 0).saturating_mul(e.into())) + // Minimum execution time: 1_189_000 picoseconds. + Weight::from_parts(1_416_188, 0) } - /// The range of component `r` is `[0, 50]`. + /// The range of component `r` is `[0, 5000]`. fn instr_call(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_163_000 picoseconds. - Weight::from_parts(1_999_458, 0) - // Standard Error: 1_418 - .saturating_add(Weight::from_parts(2_371_979, 0).saturating_mul(r.into())) + // Minimum execution time: 2_201_000 picoseconds. + Weight::from_parts(3_375_851, 0) + // Standard Error: 96 + .saturating_add(Weight::from_parts(22_970, 0).saturating_mul(r.into())) } - /// The range of component `r` is `[0, 50]`. + /// The range of component `r` is `[0, 5000]`. fn instr_call_indirect(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_288_000 picoseconds. - Weight::from_parts(2_340_227, 0) - // Standard Error: 3_234 - .saturating_add(Weight::from_parts(3_038_523, 0).saturating_mul(r.into())) - } - /// The range of component `p` is `[0, 128]`. - fn instr_call_indirect_per_param(p: u32, ) -> Weight { - // Proof Size summary in bytes: - // Measured: `0` - // Estimated: `0` - // Minimum execution time: 5_270_000 picoseconds. - Weight::from_parts(6_360_011, 0) - // Standard Error: 2_585 - .saturating_add(Weight::from_parts(226_149, 0).saturating_mul(p.into())) + // Minimum execution time: 1_286_000 picoseconds. + Weight::from_parts(2_817_725, 0) + // Standard Error: 55 + .saturating_add(Weight::from_parts(29_437, 0).saturating_mul(r.into())) } /// The range of component `l` is `[0, 1024]`. fn instr_call_per_local(l: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 3_580_000 picoseconds. - Weight::from_parts(5_183_658, 0) - // Standard Error: 80 - .saturating_add(Weight::from_parts(46_038, 0).saturating_mul(l.into())) + // Minimum execution time: 1_319_000 picoseconds. + Weight::from_parts(1_636_286, 0) + // Standard Error: 31 + .saturating_add(Weight::from_parts(1_240, 0).saturating_mul(l.into())) } - /// The range of component `r` is `[0, 50]`. + /// The range of component `r` is `[0, 5000]`. fn instr_local_get(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 2_559_000 picoseconds. - Weight::from_parts(2_846_350, 0) - // Standard Error: 2_809 - .saturating_add(Weight::from_parts(462_084, 0).saturating_mul(r.into())) + // Minimum execution time: 2_507_000 picoseconds. + Weight::from_parts(2_785_119, 0) + // Standard Error: 1 + .saturating_add(Weight::from_parts(4_601, 0).saturating_mul(r.into())) } - /// The range of component `r` is `[0, 50]`. + /// The range of component `r` is `[0, 5000]`. fn instr_local_set(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 2_530_000 picoseconds. - Weight::from_parts(2_835_072, 0) - // Standard Error: 479 - .saturating_add(Weight::from_parts(483_451, 0).saturating_mul(r.into())) + // Minimum execution time: 2_494_000 picoseconds. + Weight::from_parts(2_948_015, 0) + // Standard Error: 14 + .saturating_add(Weight::from_parts(4_788, 0).saturating_mul(r.into())) } - /// The range of component `r` is `[0, 50]`. + /// The range of component `r` is `[0, 5000]`. fn instr_local_tee(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 2_510_000 picoseconds. - Weight::from_parts(2_740_081, 0) - // Standard Error: 901 - .saturating_add(Weight::from_parts(664_354, 0).saturating_mul(r.into())) + // Minimum execution time: 2_443_000 picoseconds. + Weight::from_parts(3_065_273, 0) + // Standard Error: 15 + .saturating_add(Weight::from_parts(6_489, 0).saturating_mul(r.into())) } - /// The range of component `r` is `[0, 50]`. + /// The range of component `r` is `[0, 5000]`. fn instr_global_get(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_295_000 picoseconds. - Weight::from_parts(1_798_174, 0) - // Standard Error: 464 - .saturating_add(Weight::from_parts(896_289, 0).saturating_mul(r.into())) + // Minimum execution time: 1_214_000 picoseconds. + Weight::from_parts(1_634_049, 0) + // Standard Error: 3 + .saturating_add(Weight::from_parts(8_960, 0).saturating_mul(r.into())) } - /// The range of component `r` is `[0, 50]`. + /// The range of component `r` is `[0, 5000]`. fn instr_global_set(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_287_000 picoseconds. - Weight::from_parts(1_636_548, 0) - // Standard Error: 658 - .saturating_add(Weight::from_parts(923_295, 0).saturating_mul(r.into())) + // Minimum execution time: 1_180_000 picoseconds. + Weight::from_parts(1_555_599, 0) + // Standard Error: 4 + .saturating_add(Weight::from_parts(9_205, 0).saturating_mul(r.into())) } - /// The range of component `r` is `[0, 50]`. + /// The range of component `r` is `[0, 5000]`. fn instr_memory_current(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_353_000 picoseconds. - Weight::from_parts(1_672_309, 0) - // Standard Error: 403 - .saturating_add(Weight::from_parts(818_273, 0).saturating_mul(r.into())) + // Minimum execution time: 1_223_000 picoseconds. + Weight::from_parts(1_626_002, 0) + // Standard Error: 5 + .saturating_add(Weight::from_parts(8_181, 0).saturating_mul(r.into())) } - /// The range of component `r` is `[0, 1]`. + /// The range of component `r` is `[0, 16]`. fn instr_memory_grow(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_180_000 picoseconds. - Weight::from_parts(1_304_714, 0) - // Standard Error: 88_180 - .saturating_add(Weight::from_parts(183_952_985, 0).saturating_mul(r.into())) + // Minimum execution time: 1_148_000 picoseconds. + Weight::from_parts(307_155, 0) + // Standard Error: 138_541 + .saturating_add(Weight::from_parts(13_291_570, 0).saturating_mul(r.into())) } - /// The range of component `r` is `[0, 50]`. + /// The range of component `r` is `[0, 5000]`. fn instr_i64clz(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_158_000 picoseconds. - Weight::from_parts(1_800_065, 0) - // Standard Error: 2_104 - .saturating_add(Weight::from_parts(625_410, 0).saturating_mul(r.into())) + // Minimum execution time: 1_079_000 picoseconds. + Weight::from_parts(1_372_409, 0) + // Standard Error: 50 + .saturating_add(Weight::from_parts(6_427, 0).saturating_mul(r.into())) } - /// The range of component `r` is `[0, 50]`. + /// The range of component `r` is `[0, 5000]`. fn instr_i64ctz(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_208_000 picoseconds. - Weight::from_parts(1_483_908, 0) - // Standard Error: 319 - .saturating_add(Weight::from_parts(635_095, 0).saturating_mul(r.into())) + // Minimum execution time: 1_116_000 picoseconds. + Weight::from_parts(1_415_180, 0) + // Standard Error: 2 + .saturating_add(Weight::from_parts(6_353, 0).saturating_mul(r.into())) } - /// The range of component `r` is `[0, 50]`. + /// The range of component `r` is `[0, 5000]`. fn instr_i64popcnt(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_164_000 picoseconds. - Weight::from_parts(1_506_871, 0) - // Standard Error: 264 - .saturating_add(Weight::from_parts(633_660, 0).saturating_mul(r.into())) + // Minimum execution time: 1_058_000 picoseconds. + Weight::from_parts(1_314_206, 0) + // Standard Error: 35 + .saturating_add(Weight::from_parts(6_431, 0).saturating_mul(r.into())) } - /// The range of component `r` is `[0, 50]`. + /// The range of component `r` is `[0, 5000]`. fn instr_i64eqz(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_141_000 picoseconds. - Weight::from_parts(1_467_219, 0) - // Standard Error: 191 - .saturating_add(Weight::from_parts(651_156, 0).saturating_mul(r.into())) + // Minimum execution time: 1_059_000 picoseconds. + Weight::from_parts(1_455_331, 0) + // Standard Error: 2 + .saturating_add(Weight::from_parts(6_507, 0).saturating_mul(r.into())) } - /// The range of component `r` is `[0, 50]`. + /// The range of component `r` is `[0, 5000]`. fn instr_i64extendsi32(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_170_000 picoseconds. - Weight::from_parts(1_474_718, 0) - // Standard Error: 224 - .saturating_add(Weight::from_parts(617_317, 0).saturating_mul(r.into())) + // Minimum execution time: 1_057_000 picoseconds. + Weight::from_parts(1_421_085, 0) + // Standard Error: 2 + .saturating_add(Weight::from_parts(6_177, 0).saturating_mul(r.into())) } - /// The range of component `r` is `[0, 50]`. + /// The range of component `r` is `[0, 5000]`. fn instr_i64extendui32(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_195_000 picoseconds. - Weight::from_parts(1_501_421, 0) - // Standard Error: 221 - .saturating_add(Weight::from_parts(616_559, 0).saturating_mul(r.into())) + // Minimum execution time: 1_079_000 picoseconds. + Weight::from_parts(1_429_275, 0) + // Standard Error: 2 + .saturating_add(Weight::from_parts(6_175, 0).saturating_mul(r.into())) } - /// The range of component `r` is `[0, 50]`. + /// The range of component `r` is `[0, 5000]`. fn instr_i32wrapi64(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_172_000 picoseconds. - Weight::from_parts(1_482_402, 0) - // Standard Error: 2_163 - .saturating_add(Weight::from_parts(619_230, 0).saturating_mul(r.into())) + // Minimum execution time: 1_065_000 picoseconds. + Weight::from_parts(1_403_813, 0) + // Standard Error: 3 + .saturating_add(Weight::from_parts(6_192, 0).saturating_mul(r.into())) } - /// The range of component `r` is `[0, 50]`. + /// The range of component `r` is `[0, 5000]`. fn instr_i64eq(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_149_000 picoseconds. - Weight::from_parts(2_021_801, 0) - // Standard Error: 20_804 - .saturating_add(Weight::from_parts(913_888, 0).saturating_mul(r.into())) + // Minimum execution time: 1_061_000 picoseconds. + Weight::from_parts(1_421_984, 0) + // Standard Error: 4 + .saturating_add(Weight::from_parts(9_094, 0).saturating_mul(r.into())) } - /// The range of component `r` is `[0, 50]`. + /// The range of component `r` is `[0, 5000]`. fn instr_i64ne(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_187_000 picoseconds. - Weight::from_parts(1_358_437, 0) - // Standard Error: 1_946 - .saturating_add(Weight::from_parts(916_378, 0).saturating_mul(r.into())) + // Minimum execution time: 1_075_000 picoseconds. + Weight::from_parts(1_431_453, 0) + // Standard Error: 2 + .saturating_add(Weight::from_parts(9_084, 0).saturating_mul(r.into())) } - /// The range of component `r` is `[0, 50]`. + /// The range of component `r` is `[0, 5000]`. fn instr_i64lts(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_175_000 picoseconds. - Weight::from_parts(1_527_336, 0) - // Standard Error: 243 - .saturating_add(Weight::from_parts(907_580, 0).saturating_mul(r.into())) + // Minimum execution time: 1_100_000 picoseconds. + Weight::from_parts(1_452_384, 0) + // Standard Error: 3 + .saturating_add(Weight::from_parts(9_081, 0).saturating_mul(r.into())) } - /// The range of component `r` is `[0, 50]`. + /// The range of component `r` is `[0, 5000]`. fn instr_i64ltu(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_220_000 picoseconds. - Weight::from_parts(1_530_715, 0) - // Standard Error: 224 - .saturating_add(Weight::from_parts(906_096, 0).saturating_mul(r.into())) + // Minimum execution time: 1_090_000 picoseconds. + Weight::from_parts(1_466_416, 0) + // Standard Error: 17 + .saturating_add(Weight::from_parts(9_091, 0).saturating_mul(r.into())) } - /// The range of component `r` is `[0, 50]`. + /// The range of component `r` is `[0, 5000]`. fn instr_i64gts(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_107_000 picoseconds. - Weight::from_parts(1_496_553, 0) - // Standard Error: 299 - .saturating_add(Weight::from_parts(907_963, 0).saturating_mul(r.into())) + // Minimum execution time: 1_056_000 picoseconds. + Weight::from_parts(1_676_091, 0) + // Standard Error: 47 + .saturating_add(Weight::from_parts(9_082, 0).saturating_mul(r.into())) } - /// The range of component `r` is `[0, 50]`. + /// The range of component `r` is `[0, 5000]`. fn instr_i64gtu(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_153_000 picoseconds. - Weight::from_parts(1_482_710, 0) - // Standard Error: 182 - .saturating_add(Weight::from_parts(918_439, 0).saturating_mul(r.into())) + // Minimum execution time: 1_053_000 picoseconds. + Weight::from_parts(1_232_790, 0) + // Standard Error: 43 + .saturating_add(Weight::from_parts(9_329, 0).saturating_mul(r.into())) } - /// The range of component `r` is `[0, 50]`. + /// The range of component `r` is `[0, 5000]`. fn instr_i64les(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_142_000 picoseconds. - Weight::from_parts(1_522_114, 0) - // Standard Error: 297 - .saturating_add(Weight::from_parts(906_633, 0).saturating_mul(r.into())) + // Minimum execution time: 1_091_000 picoseconds. + Weight::from_parts(1_476_212, 0) + // Standard Error: 4 + .saturating_add(Weight::from_parts(9_074, 0).saturating_mul(r.into())) } - /// The range of component `r` is `[0, 50]`. + /// The range of component `r` is `[0, 5000]`. fn instr_i64leu(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_119_000 picoseconds. - Weight::from_parts(2_199_831, 0) - // Standard Error: 4_234 - .saturating_add(Weight::from_parts(898_161, 0).saturating_mul(r.into())) + // Minimum execution time: 1_083_000 picoseconds. + Weight::from_parts(1_484_966, 0) + // Standard Error: 3 + .saturating_add(Weight::from_parts(9_184, 0).saturating_mul(r.into())) } - /// The range of component `r` is `[0, 50]`. + /// The range of component `r` is `[0, 5000]`. fn instr_i64ges(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_163_000 picoseconds. - Weight::from_parts(1_489_404, 0) - // Standard Error: 416 - .saturating_add(Weight::from_parts(908_450, 0).saturating_mul(r.into())) + // Minimum execution time: 1_089_000 picoseconds. + Weight::from_parts(1_516_766, 0) + // Standard Error: 4 + .saturating_add(Weight::from_parts(9_063, 0).saturating_mul(r.into())) } - /// The range of component `r` is `[0, 50]`. + /// The range of component `r` is `[0, 5000]`. fn instr_i64geu(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_204_000 picoseconds. - Weight::from_parts(2_266_296, 0) - // Standard Error: 13_013 - .saturating_add(Weight::from_parts(899_225, 0).saturating_mul(r.into())) + // Minimum execution time: 996_000 picoseconds. + Weight::from_parts(1_460_638, 0) + // Standard Error: 2 + .saturating_add(Weight::from_parts(9_076, 0).saturating_mul(r.into())) } - /// The range of component `r` is `[0, 50]`. + /// The range of component `r` is `[0, 5000]`. fn instr_i64add(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_160_000 picoseconds. - Weight::from_parts(1_470_799, 0) - // Standard Error: 422 - .saturating_add(Weight::from_parts(897_325, 0).saturating_mul(r.into())) + // Minimum execution time: 1_095_000 picoseconds. + Weight::from_parts(1_448_412, 0) + // Standard Error: 3 + .saturating_add(Weight::from_parts(8_977, 0).saturating_mul(r.into())) } - /// The range of component `r` is `[0, 50]`. + /// The range of component `r` is `[0, 5000]`. fn instr_i64sub(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_125_000 picoseconds. - Weight::from_parts(1_521_508, 0) - // Standard Error: 594 - .saturating_add(Weight::from_parts(886_145, 0).saturating_mul(r.into())) + // Minimum execution time: 1_071_000 picoseconds. + Weight::from_parts(1_459_165, 0) + // Standard Error: 2 + .saturating_add(Weight::from_parts(8_852, 0).saturating_mul(r.into())) } - /// The range of component `r` is `[0, 50]`. + /// The range of component `r` is `[0, 5000]`. fn instr_i64mul(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_139_000 picoseconds. - Weight::from_parts(1_490_535, 0) - // Standard Error: 211 - .saturating_add(Weight::from_parts(884_929, 0).saturating_mul(r.into())) + // Minimum execution time: 1_082_000 picoseconds. + Weight::from_parts(1_450_718, 0) + // Standard Error: 3 + .saturating_add(Weight::from_parts(8_860, 0).saturating_mul(r.into())) } - /// The range of component `r` is `[0, 50]`. + /// The range of component `r` is `[0, 5000]`. fn instr_i64divs(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_180_000 picoseconds. - Weight::from_parts(2_221_481, 0) - // Standard Error: 9_884 - .saturating_add(Weight::from_parts(1_507_511, 0).saturating_mul(r.into())) + // Minimum execution time: 1_065_000 picoseconds. + Weight::from_parts(1_436_871, 0) + // Standard Error: 9 + .saturating_add(Weight::from_parts(15_241, 0).saturating_mul(r.into())) } - /// The range of component `r` is `[0, 50]`. + /// The range of component `r` is `[0, 5000]`. fn instr_i64divu(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_131_000 picoseconds. - Weight::from_parts(1_498_652, 0) - // Standard Error: 531 - .saturating_add(Weight::from_parts(1_451_498, 0).saturating_mul(r.into())) + // Minimum execution time: 1_065_000 picoseconds. + Weight::from_parts(1_423_806, 0) + // Standard Error: 17 + .saturating_add(Weight::from_parts(14_706, 0).saturating_mul(r.into())) } - /// The range of component `r` is `[0, 50]`. + /// The range of component `r` is `[0, 5000]`. fn instr_i64rems(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_145_000 picoseconds. - Weight::from_parts(1_504_118, 0) - // Standard Error: 439 - .saturating_add(Weight::from_parts(1_521_895, 0).saturating_mul(r.into())) + // Minimum execution time: 1_059_000 picoseconds. + Weight::from_parts(1_735_816, 0) + // Standard Error: 66 + .saturating_add(Weight::from_parts(15_230, 0).saturating_mul(r.into())) } - /// The range of component `r` is `[0, 50]`. + /// The range of component `r` is `[0, 5000]`. fn instr_i64remu(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_124_000 picoseconds. - Weight::from_parts(1_497_209, 0) - // Standard Error: 198 - .saturating_add(Weight::from_parts(1_449_843, 0).saturating_mul(r.into())) + // Minimum execution time: 1_076_000 picoseconds. + Weight::from_parts(1_451_290, 0) + // Standard Error: 6 + .saturating_add(Weight::from_parts(14_530, 0).saturating_mul(r.into())) } - /// The range of component `r` is `[0, 50]`. + /// The range of component `r` is `[0, 5000]`. fn instr_i64and(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_169_000 picoseconds. - Weight::from_parts(1_534_491, 0) - // Standard Error: 283 - .saturating_add(Weight::from_parts(894_703, 0).saturating_mul(r.into())) + // Minimum execution time: 1_079_000 picoseconds. + Weight::from_parts(1_457_537, 0) + // Standard Error: 3 + .saturating_add(Weight::from_parts(8_963, 0).saturating_mul(r.into())) } - /// The range of component `r` is `[0, 50]`. + /// The range of component `r` is `[0, 5000]`. fn instr_i64or(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_178_000 picoseconds. - Weight::from_parts(1_485_473, 0) - // Standard Error: 552 - .saturating_add(Weight::from_parts(897_878, 0).saturating_mul(r.into())) + // Minimum execution time: 1_032_000 picoseconds. + Weight::from_parts(1_475_315, 0) + // Standard Error: 4 + .saturating_add(Weight::from_parts(8_956, 0).saturating_mul(r.into())) } - /// The range of component `r` is `[0, 50]`. + /// The range of component `r` is `[0, 5000]`. fn instr_i64xor(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_214_000 picoseconds. - Weight::from_parts(1_580_999, 0) - // Standard Error: 358 - .saturating_add(Weight::from_parts(894_207, 0).saturating_mul(r.into())) + // Minimum execution time: 1_056_000 picoseconds. + Weight::from_parts(1_450_071, 0) + // Standard Error: 2 + .saturating_add(Weight::from_parts(8_960, 0).saturating_mul(r.into())) } - /// The range of component `r` is `[0, 50]`. + /// The range of component `r` is `[0, 5000]`. fn instr_i64shl(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_176_000 picoseconds. - Weight::from_parts(1_490_470, 0) - // Standard Error: 738 - .saturating_add(Weight::from_parts(904_900, 0).saturating_mul(r.into())) + // Minimum execution time: 1_116_000 picoseconds. + Weight::from_parts(1_429_705, 0) + // Standard Error: 2 + .saturating_add(Weight::from_parts(9_027, 0).saturating_mul(r.into())) } - /// The range of component `r` is `[0, 50]`. + /// The range of component `r` is `[0, 5000]`. fn instr_i64shrs(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_197_000 picoseconds. - Weight::from_parts(1_519_703, 0) - // Standard Error: 336 - .saturating_add(Weight::from_parts(901_277, 0).saturating_mul(r.into())) + // Minimum execution time: 1_059_000 picoseconds. + Weight::from_parts(1_429_307, 0) + // Standard Error: 4 + .saturating_add(Weight::from_parts(9_048, 0).saturating_mul(r.into())) } - /// The range of component `r` is `[0, 50]`. + /// The range of component `r` is `[0, 5000]`. fn instr_i64shru(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_166_000 picoseconds. - Weight::from_parts(1_507_499, 0) - // Standard Error: 298 - .saturating_add(Weight::from_parts(901_153, 0).saturating_mul(r.into())) + // Minimum execution time: 1_093_000 picoseconds. + Weight::from_parts(1_411_827, 0) + // Standard Error: 4 + .saturating_add(Weight::from_parts(9_528, 0).saturating_mul(r.into())) } - /// The range of component `r` is `[0, 50]`. + /// The range of component `r` is `[0, 5000]`. fn instr_i64rotl(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_158_000 picoseconds. - Weight::from_parts(1_491_176, 0) - // Standard Error: 341 - .saturating_add(Weight::from_parts(901_880, 0).saturating_mul(r.into())) + // Minimum execution time: 1_115_000 picoseconds. + Weight::from_parts(1_441_025, 0) + // Standard Error: 4 + .saturating_add(Weight::from_parts(9_043, 0).saturating_mul(r.into())) } - /// The range of component `r` is `[0, 50]`. + /// The range of component `r` is `[0, 5000]`. fn instr_i64rotr(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_207_000 picoseconds. - Weight::from_parts(1_477_261, 0) - // Standard Error: 287 - .saturating_add(Weight::from_parts(902_622, 0).saturating_mul(r.into())) + // Minimum execution time: 1_077_000 picoseconds. + Weight::from_parts(1_480_666, 0) + // Standard Error: 4 + .saturating_add(Weight::from_parts(9_031, 0).saturating_mul(r.into())) } } @@ -2161,8 +2132,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `109` // Estimated: `1594` - // Minimum execution time: 2_679_000 picoseconds. - Weight::from_parts(2_907_000, 1594) + // Minimum execution time: 2_736_000 picoseconds. + Weight::from_parts(2_931_000, 1594) .saturating_add(RocksDbWeight::get().reads(1_u64)) } /// Storage: Skipped Metadata (r:0 w:0) @@ -2170,12 +2141,12 @@ impl WeightInfo for () { /// The range of component `k` is `[0, 1024]`. fn on_initialize_per_trie_key(k: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `450 + k * (69 ±0)` - // Estimated: `440 + k * (70 ±0)` - // Minimum execution time: 11_162_000 picoseconds. - Weight::from_parts(5_734_923, 440) - // Standard Error: 1_097 - .saturating_add(Weight::from_parts(976_647, 0).saturating_mul(k.into())) + // Measured: `481 + k * (69 ±0)` + // Estimated: `471 + k * (70 ±0)` + // Minimum execution time: 10_759_000 picoseconds. + Weight::from_parts(6_965_683, 471) + // Standard Error: 1_019 + .saturating_add(Weight::from_parts(961_947, 0).saturating_mul(k.into())) .saturating_add(RocksDbWeight::get().reads(1_u64)) .saturating_add(RocksDbWeight::get().reads((1_u64).saturating_mul(k.into()))) .saturating_add(RocksDbWeight::get().writes(1_u64)) @@ -2187,12 +2158,12 @@ impl WeightInfo for () { /// The range of component `q` is `[0, 128]`. fn on_initialize_per_queue_item(q: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `250 + q * (33 ±0)` - // Estimated: `1725 + q * (33 ±0)` - // Minimum execution time: 2_626_000 picoseconds. - Weight::from_parts(10_626_314, 1725) - // Standard Error: 4_006 - .saturating_add(Weight::from_parts(1_298_864, 0).saturating_mul(q.into())) + // Measured: `281 + q * (33 ±0)` + // Estimated: `1753 + q * (33 ±0)` + // Minimum execution time: 2_633_000 picoseconds. + Weight::from_parts(10_668_837, 1753) + // Standard Error: 3_538 + .saturating_add(Weight::from_parts(1_277_168, 0).saturating_mul(q.into())) .saturating_add(RocksDbWeight::get().reads(1_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) .saturating_add(Weight::from_parts(0, 33).saturating_mul(q.into())) @@ -2204,12 +2175,12 @@ impl WeightInfo for () { /// The range of component `c` is `[0, 61717]`. fn reinstrument(c: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `238 + c * (1 ±0)` - // Estimated: `3951 + c * (2 ±0)` - // Minimum execution time: 31_434_000 picoseconds. - Weight::from_parts(29_558_961, 3951) - // Standard Error: 55 - .saturating_add(Weight::from_parts(50_774, 0).saturating_mul(c.into())) + // Measured: `270 + c * (1 ±0)` + // Estimated: `4015 + c * (2 ±0)` + // Minimum execution time: 30_264_000 picoseconds. + Weight::from_parts(27_106_554, 4015) + // Standard Error: 53 + .saturating_add(Weight::from_parts(49_705, 0).saturating_mul(c.into())) .saturating_add(RocksDbWeight::get().reads(1_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) .saturating_add(Weight::from_parts(0, 2).saturating_mul(c.into())) @@ -2227,12 +2198,12 @@ impl WeightInfo for () { /// The range of component `c` is `[0, 125952]`. fn call_with_code_per_byte(c: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `707` - // Estimated: `21400 + c * (5 ±0)` - // Minimum execution time: 309_015_000 picoseconds. - Weight::from_parts(318_740_885, 21400) - // Standard Error: 29 - .saturating_add(Weight::from_parts(30_993, 0).saturating_mul(c.into())) + // Measured: `803` + // Estimated: `21880 + c * (5 ±0)` + // Minimum execution time: 309_249_000 picoseconds. + Weight::from_parts(323_353_590, 21880) + // Standard Error: 25 + .saturating_add(Weight::from_parts(31_359, 0).saturating_mul(c.into())) .saturating_add(RocksDbWeight::get().reads(6_u64)) .saturating_add(RocksDbWeight::get().writes(4_u64)) .saturating_add(Weight::from_parts(0, 5).saturating_mul(c.into())) @@ -2260,14 +2231,14 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `270` // Estimated: `26207` - // Minimum execution time: 3_126_495_000 picoseconds. - Weight::from_parts(636_857_878, 26207) - // Standard Error: 294 - .saturating_add(Weight::from_parts(92_930, 0).saturating_mul(c.into())) - // Standard Error: 17 - .saturating_add(Weight::from_parts(1_128, 0).saturating_mul(i.into())) - // Standard Error: 17 - .saturating_add(Weight::from_parts(1_415, 0).saturating_mul(s.into())) + // Minimum execution time: 3_136_707_000 picoseconds. + Weight::from_parts(564_055_378, 26207) + // Standard Error: 286 + .saturating_add(Weight::from_parts(93_308, 0).saturating_mul(c.into())) + // Standard Error: 16 + .saturating_add(Weight::from_parts(1_165, 0).saturating_mul(i.into())) + // Standard Error: 16 + .saturating_add(Weight::from_parts(1_435, 0).saturating_mul(s.into())) .saturating_add(RocksDbWeight::get().reads(9_u64)) .saturating_add(RocksDbWeight::get().writes(10_u64)) } @@ -2289,14 +2260,14 @@ impl WeightInfo for () { /// The range of component `s` is `[0, 1048576]`. fn instantiate(i: u32, s: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `482` - // Estimated: `28521` - // Minimum execution time: 1_591_415_000 picoseconds. - Weight::from_parts(228_633_516, 28521) + // Measured: `546` + // Estimated: `28969` + // Minimum execution time: 1_631_314_000 picoseconds. + Weight::from_parts(236_693_159, 28969) // Standard Error: 8 - .saturating_add(Weight::from_parts(1_449, 0).saturating_mul(i.into())) + .saturating_add(Weight::from_parts(1_434, 0).saturating_mul(i.into())) // Standard Error: 8 - .saturating_add(Weight::from_parts(1_444, 0).saturating_mul(s.into())) + .saturating_add(Weight::from_parts(1_445, 0).saturating_mul(s.into())) .saturating_add(RocksDbWeight::get().reads(9_u64)) .saturating_add(RocksDbWeight::get().writes(7_u64)) } @@ -2312,10 +2283,10 @@ impl WeightInfo for () { /// Proof Skipped: System EventTopics (max_values: None, max_size: None, mode: Measured) fn call() -> Weight { // Proof Size summary in bytes: - // Measured: `759` - // Estimated: `21615` - // Minimum execution time: 167_905_000 picoseconds. - Weight::from_parts(169_247_000, 21615) + // Measured: `855` + // Estimated: `22095` + // Minimum execution time: 167_139_000 picoseconds. + Weight::from_parts(168_034_000, 22095) .saturating_add(RocksDbWeight::get().reads(6_u64)) .saturating_add(RocksDbWeight::get().writes(4_u64)) } @@ -2332,10 +2303,10 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `109` // Estimated: `7366` - // Minimum execution time: 305_128_000 picoseconds. - Weight::from_parts(310_911_395, 7366) - // Standard Error: 70 - .saturating_add(Weight::from_parts(94_067, 0).saturating_mul(c.into())) + // Minimum execution time: 306_518_000 picoseconds. + Weight::from_parts(325_652_407, 7366) + // Standard Error: 134 + .saturating_add(Weight::from_parts(93_502, 0).saturating_mul(c.into())) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().writes(4_u64)) } @@ -2349,10 +2320,10 @@ impl WeightInfo for () { /// Proof: Contracts PristineCode (max_values: None, max_size: Some(125988), added: 128463, mode: Measured) fn remove_code() -> Weight { // Proof Size summary in bytes: - // Measured: `255` - // Estimated: `7950` - // Minimum execution time: 29_605_000 picoseconds. - Weight::from_parts(29_986_000, 7950) + // Measured: `287` + // Estimated: `8078` + // Minimum execution time: 29_084_000 picoseconds. + Weight::from_parts(29_350_000, 8078) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().writes(4_u64)) } @@ -2364,10 +2335,10 @@ impl WeightInfo for () { /// Proof Skipped: System EventTopics (max_values: None, max_size: None, mode: Measured) fn set_code() -> Weight { // Proof Size summary in bytes: - // Measured: `570` - // Estimated: `19530` - // Minimum execution time: 33_824_000 picoseconds. - Weight::from_parts(34_388_000, 19530) + // Measured: `666` + // Estimated: `19818` + // Minimum execution time: 32_996_000 picoseconds. + Weight::from_parts(33_365_000, 19818) .saturating_add(RocksDbWeight::get().reads(6_u64)) .saturating_add(RocksDbWeight::get().writes(6_u64)) } @@ -2381,18 +2352,18 @@ impl WeightInfo for () { /// Proof: Timestamp Now (max_values: Some(1), max_size: Some(8), added: 503, mode: Measured) /// Storage: System EventTopics (r:2 w:2) /// Proof Skipped: System EventTopics (max_values: None, max_size: None, mode: Measured) - /// The range of component `r` is `[0, 20]`. + /// The range of component `r` is `[0, 1600]`. fn seal_caller(r: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `781 + r * (480 ±0)` - // Estimated: `21765 + r * (2400 ±0)` - // Minimum execution time: 295_283_000 picoseconds. - Weight::from_parts(295_895_211, 21765) - // Standard Error: 44_860 - .saturating_add(Weight::from_parts(22_913_078, 0).saturating_mul(r.into())) + // Measured: `877 + r * (6 ±0)` + // Estimated: `22210 + r * (30 ±0)` + // Minimum execution time: 298_315_000 picoseconds. + Weight::from_parts(304_612_433, 22210) + // Standard Error: 904 + .saturating_add(Weight::from_parts(285_473, 0).saturating_mul(r.into())) .saturating_add(RocksDbWeight::get().reads(6_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) - .saturating_add(Weight::from_parts(0, 2400).saturating_mul(r.into())) + .saturating_add(Weight::from_parts(0, 30).saturating_mul(r.into())) } /// Storage: System Account (r:1 w:0) /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: Measured) @@ -2404,19 +2375,19 @@ impl WeightInfo for () { /// Proof: Timestamp Now (max_values: Some(1), max_size: Some(8), added: 503, mode: Measured) /// Storage: System EventTopics (r:2 w:2) /// Proof Skipped: System EventTopics (max_values: None, max_size: None, mode: Measured) - /// The range of component `r` is `[0, 20]`. + /// The range of component `r` is `[0, 1600]`. fn seal_is_contract(r: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `821 + r * (19218 ±0)` - // Estimated: `21765 + r * (294095 ±0)` - // Minimum execution time: 295_760_000 picoseconds. - Weight::from_parts(151_512_811, 21765) - // Standard Error: 522_974 - .saturating_add(Weight::from_parts(263_028_353, 0).saturating_mul(r.into())) + // Measured: `935 + r * (272 ±0)` + // Estimated: `22315 + r * (3835 ±0)` + // Minimum execution time: 299_169_000 picoseconds. + Weight::from_parts(138_157_704, 22315) + // Standard Error: 6_115 + .saturating_add(Weight::from_parts(3_244_482, 0).saturating_mul(r.into())) .saturating_add(RocksDbWeight::get().reads(6_u64)) - .saturating_add(RocksDbWeight::get().reads((80_u64).saturating_mul(r.into()))) + .saturating_add(RocksDbWeight::get().reads((1_u64).saturating_mul(r.into()))) .saturating_add(RocksDbWeight::get().writes(3_u64)) - .saturating_add(Weight::from_parts(0, 294095).saturating_mul(r.into())) + .saturating_add(Weight::from_parts(0, 3835).saturating_mul(r.into())) } /// Storage: System Account (r:1 w:0) /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: Measured) @@ -2428,19 +2399,19 @@ impl WeightInfo for () { /// Proof: Timestamp Now (max_values: Some(1), max_size: Some(8), added: 503, mode: Measured) /// Storage: System EventTopics (r:2 w:2) /// Proof Skipped: System EventTopics (max_values: None, max_size: None, mode: Measured) - /// The range of component `r` is `[0, 20]`. + /// The range of component `r` is `[0, 1600]`. fn seal_code_hash(r: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `825 + r * (19539 ±0)` - // Estimated: `21810 + r * (295700 ±0)` - // Minimum execution time: 295_950_000 picoseconds. - Weight::from_parts(164_516_076, 21810) - // Standard Error: 469_540 - .saturating_add(Weight::from_parts(321_138_601, 0).saturating_mul(r.into())) + // Measured: `927 + r * (276 ±0)` + // Estimated: `22335 + r * (3855 ±0)` + // Minimum execution time: 299_262_000 picoseconds. + Weight::from_parts(157_105_627, 22335) + // Standard Error: 5_634 + .saturating_add(Weight::from_parts(3_995_666, 0).saturating_mul(r.into())) .saturating_add(RocksDbWeight::get().reads(6_u64)) - .saturating_add(RocksDbWeight::get().reads((80_u64).saturating_mul(r.into()))) + .saturating_add(RocksDbWeight::get().reads((1_u64).saturating_mul(r.into()))) .saturating_add(RocksDbWeight::get().writes(3_u64)) - .saturating_add(Weight::from_parts(0, 295700).saturating_mul(r.into())) + .saturating_add(Weight::from_parts(0, 3855).saturating_mul(r.into())) } /// Storage: System Account (r:1 w:0) /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: Measured) @@ -2452,18 +2423,18 @@ impl WeightInfo for () { /// Proof: Timestamp Now (max_values: Some(1), max_size: Some(8), added: 503, mode: Measured) /// Storage: System EventTopics (r:2 w:2) /// Proof Skipped: System EventTopics (max_values: None, max_size: None, mode: Measured) - /// The range of component `r` is `[0, 20]`. + /// The range of component `r` is `[0, 1600]`. fn seal_own_code_hash(r: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `788 + r * (480 ±0)` - // Estimated: `21800 + r * (2400 ±0)` - // Minimum execution time: 295_916_000 picoseconds. - Weight::from_parts(299_964_913, 21800) - // Standard Error: 78_211 - .saturating_add(Weight::from_parts(28_933_603, 0).saturating_mul(r.into())) + // Measured: `884 + r * (6 ±0)` + // Estimated: `22250 + r * (30 ±0)` + // Minimum execution time: 298_259_000 picoseconds. + Weight::from_parts(303_498_245, 22250) + // Standard Error: 874 + .saturating_add(Weight::from_parts(352_839, 0).saturating_mul(r.into())) .saturating_add(RocksDbWeight::get().reads(6_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) - .saturating_add(Weight::from_parts(0, 2400).saturating_mul(r.into())) + .saturating_add(Weight::from_parts(0, 30).saturating_mul(r.into())) } /// Storage: System Account (r:1 w:0) /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: Measured) @@ -2475,18 +2446,18 @@ impl WeightInfo for () { /// Proof: Timestamp Now (max_values: Some(1), max_size: Some(8), added: 503, mode: Measured) /// Storage: System EventTopics (r:2 w:2) /// Proof Skipped: System EventTopics (max_values: None, max_size: None, mode: Measured) - /// The range of component `r` is `[0, 20]`. + /// The range of component `r` is `[0, 1600]`. fn seal_caller_is_origin(r: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `778 + r * (240 ±0)` - // Estimated: `21735 + r * (1200 ±0)` - // Minimum execution time: 293_588_000 picoseconds. - Weight::from_parts(297_568_984, 21735) - // Standard Error: 35_513 - .saturating_add(Weight::from_parts(11_074_880, 0).saturating_mul(r.into())) + // Measured: `874 + r * (3 ±0)` + // Estimated: `22215 + r * (15 ±0)` + // Minimum execution time: 295_689_000 picoseconds. + Weight::from_parts(314_004_541, 22215) + // Standard Error: 1_803 + .saturating_add(Weight::from_parts(131_102, 0).saturating_mul(r.into())) .saturating_add(RocksDbWeight::get().reads(6_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) - .saturating_add(Weight::from_parts(0, 1200).saturating_mul(r.into())) + .saturating_add(Weight::from_parts(0, 15).saturating_mul(r.into())) } /// Storage: System Account (r:1 w:0) /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: Measured) @@ -2498,18 +2469,18 @@ impl WeightInfo for () { /// Proof: Timestamp Now (max_values: Some(1), max_size: Some(8), added: 503, mode: Measured) /// Storage: System EventTopics (r:2 w:2) /// Proof Skipped: System EventTopics (max_values: None, max_size: None, mode: Measured) - /// The range of component `r` is `[0, 20]`. + /// The range of component `r` is `[0, 1600]`. fn seal_address(r: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `782 + r * (480 ±0)` - // Estimated: `21730 + r * (2400 ±0)` - // Minimum execution time: 294_929_000 picoseconds. - Weight::from_parts(295_916_817, 21730) - // Standard Error: 64_902 - .saturating_add(Weight::from_parts(22_944_018, 0).saturating_mul(r.into())) + // Measured: `878 + r * (6 ±0)` + // Estimated: `22220 + r * (30 ±0)` + // Minimum execution time: 297_579_000 picoseconds. + Weight::from_parts(299_326_920, 22220) + // Standard Error: 990 + .saturating_add(Weight::from_parts(284_789, 0).saturating_mul(r.into())) .saturating_add(RocksDbWeight::get().reads(6_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) - .saturating_add(Weight::from_parts(0, 2400).saturating_mul(r.into())) + .saturating_add(Weight::from_parts(0, 30).saturating_mul(r.into())) } /// Storage: System Account (r:1 w:0) /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: Measured) @@ -2521,18 +2492,18 @@ impl WeightInfo for () { /// Proof: Timestamp Now (max_values: Some(1), max_size: Some(8), added: 503, mode: Measured) /// Storage: System EventTopics (r:2 w:2) /// Proof Skipped: System EventTopics (max_values: None, max_size: None, mode: Measured) - /// The range of component `r` is `[0, 20]`. + /// The range of component `r` is `[0, 1600]`. fn seal_gas_left(r: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `783 + r * (480 ±0)` - // Estimated: `21720 + r * (2405 ±0)` - // Minimum execution time: 296_783_000 picoseconds. - Weight::from_parts(299_399_332, 21720) - // Standard Error: 50_982 - .saturating_add(Weight::from_parts(22_173_249, 0).saturating_mul(r.into())) + // Measured: `879 + r * (6 ±0)` + // Estimated: `22205 + r * (30 ±0)` + // Minimum execution time: 297_651_000 picoseconds. + Weight::from_parts(304_465_467, 22205) + // Standard Error: 736 + .saturating_add(Weight::from_parts(272_149, 0).saturating_mul(r.into())) .saturating_add(RocksDbWeight::get().reads(6_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) - .saturating_add(Weight::from_parts(0, 2405).saturating_mul(r.into())) + .saturating_add(Weight::from_parts(0, 30).saturating_mul(r.into())) } /// Storage: System Account (r:2 w:0) /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: Measured) @@ -2544,18 +2515,18 @@ impl WeightInfo for () { /// Proof: Timestamp Now (max_values: Some(1), max_size: Some(8), added: 503, mode: Measured) /// Storage: System EventTopics (r:2 w:2) /// Proof Skipped: System EventTopics (max_values: None, max_size: None, mode: Measured) - /// The range of component `r` is `[0, 20]`. + /// The range of component `r` is `[0, 1600]`. fn seal_balance(r: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `921 + r * (480 ±0)` - // Estimated: `24193 + r * (2451 ±0)` - // Minimum execution time: 294_120_000 picoseconds. - Weight::from_parts(305_889_989, 24193) - // Standard Error: 116_244 - .saturating_add(Weight::from_parts(114_781_094, 0).saturating_mul(r.into())) + // Measured: `1050 + r * (6 ±0)` + // Estimated: `25258 + r * (30 ±0)` + // Minimum execution time: 296_957_000 picoseconds. + Weight::from_parts(308_680_068, 25258) + // Standard Error: 1_377 + .saturating_add(Weight::from_parts(1_419_294, 0).saturating_mul(r.into())) .saturating_add(RocksDbWeight::get().reads(7_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) - .saturating_add(Weight::from_parts(0, 2451).saturating_mul(r.into())) + .saturating_add(Weight::from_parts(0, 30).saturating_mul(r.into())) } /// Storage: System Account (r:1 w:0) /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: Measured) @@ -2567,18 +2538,18 @@ impl WeightInfo for () { /// Proof: Timestamp Now (max_values: Some(1), max_size: Some(8), added: 503, mode: Measured) /// Storage: System EventTopics (r:2 w:2) /// Proof Skipped: System EventTopics (max_values: None, max_size: None, mode: Measured) - /// The range of component `r` is `[0, 20]`. + /// The range of component `r` is `[0, 1600]`. fn seal_value_transferred(r: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `792 + r * (480 ±0)` - // Estimated: `21830 + r * (2400 ±0)` - // Minimum execution time: 295_569_000 picoseconds. - Weight::from_parts(299_774_774, 21830) - // Standard Error: 46_252 - .saturating_add(Weight::from_parts(21_753_842, 0).saturating_mul(r.into())) + // Measured: `888 + r * (6 ±0)` + // Estimated: `22305 + r * (30 ±0)` + // Minimum execution time: 297_735_000 picoseconds. + Weight::from_parts(301_533_807, 22305) + // Standard Error: 1_119 + .saturating_add(Weight::from_parts(277_289, 0).saturating_mul(r.into())) .saturating_add(RocksDbWeight::get().reads(6_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) - .saturating_add(Weight::from_parts(0, 2400).saturating_mul(r.into())) + .saturating_add(Weight::from_parts(0, 30).saturating_mul(r.into())) } /// Storage: System Account (r:1 w:0) /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: Measured) @@ -2590,18 +2561,18 @@ impl WeightInfo for () { /// Proof: Timestamp Now (max_values: Some(1), max_size: Some(8), added: 503, mode: Measured) /// Storage: System EventTopics (r:2 w:2) /// Proof Skipped: System EventTopics (max_values: None, max_size: None, mode: Measured) - /// The range of component `r` is `[0, 20]`. + /// The range of component `r` is `[0, 1600]`. fn seal_minimum_balance(r: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `790 + r * (480 ±0)` - // Estimated: `21760 + r * (2400 ±0)` - // Minimum execution time: 295_400_000 picoseconds. - Weight::from_parts(300_637_266, 21760) - // Standard Error: 41_263 - .saturating_add(Weight::from_parts(21_565_466, 0).saturating_mul(r.into())) + // Measured: `886 + r * (6 ±0)` + // Estimated: `22295 + r * (30 ±0)` + // Minimum execution time: 299_599_000 picoseconds. + Weight::from_parts(303_664_496, 22295) + // Standard Error: 1_009 + .saturating_add(Weight::from_parts(280_353, 0).saturating_mul(r.into())) .saturating_add(RocksDbWeight::get().reads(6_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) - .saturating_add(Weight::from_parts(0, 2400).saturating_mul(r.into())) + .saturating_add(Weight::from_parts(0, 30).saturating_mul(r.into())) } /// Storage: System Account (r:1 w:0) /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: Measured) @@ -2613,18 +2584,18 @@ impl WeightInfo for () { /// Proof: Timestamp Now (max_values: Some(1), max_size: Some(8), added: 503, mode: Measured) /// Storage: System EventTopics (r:2 w:2) /// Proof Skipped: System EventTopics (max_values: None, max_size: None, mode: Measured) - /// The range of component `r` is `[0, 20]`. + /// The range of component `r` is `[0, 1600]`. fn seal_block_number(r: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `787 + r * (480 ±0)` - // Estimated: `21785 + r * (2400 ±0)` - // Minimum execution time: 294_696_000 picoseconds. - Weight::from_parts(297_196_796, 21785) - // Standard Error: 61_173 - .saturating_add(Weight::from_parts(21_903_191, 0).saturating_mul(r.into())) + // Measured: `883 + r * (6 ±0)` + // Estimated: `22285 + r * (30 ±0)` + // Minimum execution time: 297_641_000 picoseconds. + Weight::from_parts(302_437_979, 22285) + // Standard Error: 669 + .saturating_add(Weight::from_parts(270_403, 0).saturating_mul(r.into())) .saturating_add(RocksDbWeight::get().reads(6_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) - .saturating_add(Weight::from_parts(0, 2400).saturating_mul(r.into())) + .saturating_add(Weight::from_parts(0, 30).saturating_mul(r.into())) } /// Storage: System Account (r:1 w:0) /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: Measured) @@ -2636,18 +2607,18 @@ impl WeightInfo for () { /// Proof: Timestamp Now (max_values: Some(1), max_size: Some(8), added: 503, mode: Measured) /// Storage: System EventTopics (r:2 w:2) /// Proof Skipped: System EventTopics (max_values: None, max_size: None, mode: Measured) - /// The range of component `r` is `[0, 20]`. + /// The range of component `r` is `[0, 1600]`. fn seal_now(r: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `778 + r * (480 ±0)` - // Estimated: `21715 + r * (2400 ±0)` - // Minimum execution time: 297_714_000 picoseconds. - Weight::from_parts(306_030_237, 21715) - // Standard Error: 69_984 - .saturating_add(Weight::from_parts(21_744_413, 0).saturating_mul(r.into())) + // Measured: `874 + r * (6 ±0)` + // Estimated: `22215 + r * (30 ±0)` + // Minimum execution time: 297_217_000 picoseconds. + Weight::from_parts(299_538_157, 22215) + // Standard Error: 732 + .saturating_add(Weight::from_parts(280_800, 0).saturating_mul(r.into())) .saturating_add(RocksDbWeight::get().reads(6_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) - .saturating_add(Weight::from_parts(0, 2400).saturating_mul(r.into())) + .saturating_add(Weight::from_parts(0, 30).saturating_mul(r.into())) } /// Storage: System Account (r:1 w:0) /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: Measured) @@ -2661,18 +2632,18 @@ impl WeightInfo for () { /// Proof: TransactionPayment NextFeeMultiplier (max_values: Some(1), max_size: Some(16), added: 511, mode: Measured) /// Storage: System EventTopics (r:2 w:2) /// Proof Skipped: System EventTopics (max_values: None, max_size: None, mode: Measured) - /// The range of component `r` is `[0, 20]`. + /// The range of component `r` is `[0, 1600]`. fn seal_weight_to_fee(r: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `856 + r * (800 ±0)` - // Estimated: `24416 + r * (4805 ±0)` - // Minimum execution time: 295_180_000 picoseconds. - Weight::from_parts(308_851_025, 24416) - // Standard Error: 115_126 - .saturating_add(Weight::from_parts(98_678_404, 0).saturating_mul(r.into())) + // Measured: `952 + r * (10 ±0)` + // Estimated: `25022 + r * (60 ±0)` + // Minimum execution time: 297_286_000 picoseconds. + Weight::from_parts(311_436_352, 25022) + // Standard Error: 6_811 + .saturating_add(Weight::from_parts(1_291_941, 0).saturating_mul(r.into())) .saturating_add(RocksDbWeight::get().reads(7_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) - .saturating_add(Weight::from_parts(0, 4805).saturating_mul(r.into())) + .saturating_add(Weight::from_parts(0, 60).saturating_mul(r.into())) } /// Storage: System Account (r:1 w:0) /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: Measured) @@ -2684,18 +2655,18 @@ impl WeightInfo for () { /// Proof: Timestamp Now (max_values: Some(1), max_size: Some(8), added: 503, mode: Measured) /// Storage: System EventTopics (r:2 w:2) /// Proof Skipped: System EventTopics (max_values: None, max_size: None, mode: Measured) - /// The range of component `r` is `[0, 20]`. + /// The range of component `r` is `[0, 1600]`. fn seal_gas(r: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `745 + r * (320 ±0)` - // Estimated: `21590 + r * (1600 ±0)` - // Minimum execution time: 151_587_000 picoseconds. - Weight::from_parts(155_713_096, 21590) - // Standard Error: 32_464 - .saturating_add(Weight::from_parts(8_332_778, 0).saturating_mul(r.into())) + // Measured: `841 + r * (4 ±0)` + // Estimated: `22035 + r * (20 ±0)` + // Minimum execution time: 154_772_000 picoseconds. + Weight::from_parts(159_224_457, 22035) + // Standard Error: 212 + .saturating_add(Weight::from_parts(102_264, 0).saturating_mul(r.into())) .saturating_add(RocksDbWeight::get().reads(6_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) - .saturating_add(Weight::from_parts(0, 1600).saturating_mul(r.into())) + .saturating_add(Weight::from_parts(0, 20).saturating_mul(r.into())) } /// Storage: System Account (r:1 w:0) /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: Measured) @@ -2707,18 +2678,18 @@ impl WeightInfo for () { /// Proof: Timestamp Now (max_values: Some(1), max_size: Some(8), added: 503, mode: Measured) /// Storage: System EventTopics (r:2 w:2) /// Proof Skipped: System EventTopics (max_values: None, max_size: None, mode: Measured) - /// The range of component `r` is `[0, 20]`. + /// The range of component `r` is `[0, 1600]`. fn seal_input(r: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `780 + r * (480 ±0)` - // Estimated: `21715 + r * (2400 ±0)` - // Minimum execution time: 295_008_000 picoseconds. - Weight::from_parts(297_982_800, 21715) - // Standard Error: 37_480 - .saturating_add(Weight::from_parts(17_970_520, 0).saturating_mul(r.into())) + // Measured: `876 + r * (6 ±0)` + // Estimated: `22220 + r * (30 ±0)` + // Minimum execution time: 297_248_000 picoseconds. + Weight::from_parts(303_805_584, 22220) + // Standard Error: 858 + .saturating_add(Weight::from_parts(223_587, 0).saturating_mul(r.into())) .saturating_add(RocksDbWeight::get().reads(6_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) - .saturating_add(Weight::from_parts(0, 2400).saturating_mul(r.into())) + .saturating_add(Weight::from_parts(0, 30).saturating_mul(r.into())) } /// Storage: System Account (r:1 w:0) /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: Measured) @@ -2730,15 +2701,15 @@ impl WeightInfo for () { /// Proof: Timestamp Now (max_values: Some(1), max_size: Some(8), added: 503, mode: Measured) /// Storage: System EventTopics (r:2 w:2) /// Proof Skipped: System EventTopics (max_values: None, max_size: None, mode: Measured) - /// The range of component `n` is `[0, 1024]`. - fn seal_input_per_kb(n: u32, ) -> Weight { + /// The range of component `n` is `[0, 1048576]`. + fn seal_input_per_byte(n: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `1260` - // Estimated: `24120` - // Minimum execution time: 314_970_000 picoseconds. - Weight::from_parts(345_339_508, 24120) - // Standard Error: 4_112 - .saturating_add(Weight::from_parts(9_746_966, 0).saturating_mul(n.into())) + // Measured: `880` + // Estimated: `22220` + // Minimum execution time: 298_464_000 picoseconds. + Weight::from_parts(300_996_431, 22220) + // Standard Error: 1 + .saturating_add(Weight::from_parts(600, 0).saturating_mul(n.into())) .saturating_add(RocksDbWeight::get().reads(6_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) } @@ -2755,12 +2726,12 @@ impl WeightInfo for () { /// The range of component `r` is `[0, 1]`. fn seal_return(r: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `768 + r * (45 ±0)` - // Estimated: `21660 + r * (225 ±0)` - // Minimum execution time: 293_072_000 picoseconds. - Weight::from_parts(295_694_824, 21660) - // Standard Error: 219_523 - .saturating_add(Weight::from_parts(783_975, 0).saturating_mul(r.into())) + // Measured: `864 + r * (45 ±0)` + // Estimated: `22140 + r * (225 ±0)` + // Minimum execution time: 294_347_000 picoseconds. + Weight::from_parts(296_793_034, 22140) + // Standard Error: 277_251 + .saturating_add(Weight::from_parts(1_711_665, 0).saturating_mul(r.into())) .saturating_add(RocksDbWeight::get().reads(6_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) .saturating_add(Weight::from_parts(0, 225).saturating_mul(r.into())) @@ -2775,15 +2746,15 @@ impl WeightInfo for () { /// Proof: Timestamp Now (max_values: Some(1), max_size: Some(8), added: 503, mode: Measured) /// Storage: System EventTopics (r:2 w:2) /// Proof Skipped: System EventTopics (max_values: None, max_size: None, mode: Measured) - /// The range of component `n` is `[0, 1024]`. - fn seal_return_per_kb(n: u32, ) -> Weight { - // Proof Size summary in bytes: - // Measured: `778` - // Estimated: `21755` - // Minimum execution time: 295_959_000 picoseconds. - Weight::from_parts(299_004_761, 21755) - // Standard Error: 3_988 - .saturating_add(Weight::from_parts(194_268, 0).saturating_mul(n.into())) + /// The range of component `n` is `[0, 1048576]`. + fn seal_return_per_byte(n: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `874` + // Estimated: `22255` + // Minimum execution time: 297_257_000 picoseconds. + Weight::from_parts(300_088_288, 22255) + // Standard Error: 1 + .saturating_add(Weight::from_parts(181, 0).saturating_mul(n.into())) .saturating_add(RocksDbWeight::get().reads(6_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) } @@ -2804,17 +2775,17 @@ impl WeightInfo for () { /// The range of component `r` is `[0, 1]`. fn seal_terminate(r: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `810 + r * (356 ±0)` - // Estimated: `25511 + r * (15321 ±0)` - // Minimum execution time: 294_626_000 picoseconds. - Weight::from_parts(297_839_179, 25511) - // Standard Error: 707_428 - .saturating_add(Weight::from_parts(81_507_620, 0).saturating_mul(r.into())) + // Measured: `906 + r * (452 ±0)` + // Estimated: `26183 + r * (15994 ±0)` + // Minimum execution time: 296_510_000 picoseconds. + Weight::from_parts(299_169_757, 26183) + // Standard Error: 205_313 + .saturating_add(Weight::from_parts(78_059_642, 0).saturating_mul(r.into())) .saturating_add(RocksDbWeight::get().reads(6_u64)) .saturating_add(RocksDbWeight::get().reads((6_u64).saturating_mul(r.into()))) .saturating_add(RocksDbWeight::get().writes(3_u64)) .saturating_add(RocksDbWeight::get().writes((7_u64).saturating_mul(r.into()))) - .saturating_add(Weight::from_parts(0, 15321).saturating_mul(r.into())) + .saturating_add(Weight::from_parts(0, 15994).saturating_mul(r.into())) } /// Storage: System Account (r:1 w:0) /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: Measured) @@ -2828,18 +2799,18 @@ impl WeightInfo for () { /// Proof: RandomnessCollectiveFlip RandomMaterial (max_values: Some(1), max_size: Some(2594), added: 3089, mode: Measured) /// Storage: System EventTopics (r:2 w:2) /// Proof Skipped: System EventTopics (max_values: None, max_size: None, mode: Measured) - /// The range of component `r` is `[0, 20]`. + /// The range of component `r` is `[0, 1600]`. fn seal_random(r: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `825 + r * (800 ±0)` - // Estimated: `24199 + r * (4805 ±0)` - // Minimum execution time: 295_431_000 picoseconds. - Weight::from_parts(310_428_531, 24199) - // Standard Error: 143_501 - .saturating_add(Weight::from_parts(136_529_376, 0).saturating_mul(r.into())) + // Measured: `921 + r * (10 ±0)` + // Estimated: `24859 + r * (60 ±0)` + // Minimum execution time: 299_299_000 picoseconds. + Weight::from_parts(314_487_015, 24859) + // Standard Error: 1_552 + .saturating_add(Weight::from_parts(1_753_960, 0).saturating_mul(r.into())) .saturating_add(RocksDbWeight::get().reads(7_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) - .saturating_add(Weight::from_parts(0, 4805).saturating_mul(r.into())) + .saturating_add(Weight::from_parts(0, 60).saturating_mul(r.into())) } /// Storage: System Account (r:1 w:0) /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: Measured) @@ -2851,18 +2822,18 @@ impl WeightInfo for () { /// Proof: Timestamp Now (max_values: Some(1), max_size: Some(8), added: 503, mode: Measured) /// Storage: System EventTopics (r:2 w:2) /// Proof Skipped: System EventTopics (max_values: None, max_size: None, mode: Measured) - /// The range of component `r` is `[0, 20]`. + /// The range of component `r` is `[0, 1600]`. fn seal_deposit_event(r: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `778 + r * (800 ±0)` - // Estimated: `21720 + r * (4000 ±0)` - // Minimum execution time: 293_778_000 picoseconds. - Weight::from_parts(323_404_081, 21720) - // Standard Error: 243_095 - .saturating_add(Weight::from_parts(269_012_482, 0).saturating_mul(r.into())) + // Measured: `874 + r * (10 ±0)` + // Estimated: `22215 + r * (50 ±0)` + // Minimum execution time: 296_188_000 picoseconds. + Weight::from_parts(305_901_539, 22215) + // Standard Error: 2_782 + .saturating_add(Weight::from_parts(3_456_054, 0).saturating_mul(r.into())) .saturating_add(RocksDbWeight::get().reads(6_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) - .saturating_add(Weight::from_parts(0, 4000).saturating_mul(r.into())) + .saturating_add(Weight::from_parts(0, 50).saturating_mul(r.into())) } /// Storage: System Account (r:1 w:0) /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: Measured) @@ -2872,26 +2843,25 @@ impl WeightInfo for () { /// Proof: Contracts CodeStorage (max_values: None, max_size: Some(126001), added: 128476, mode: Measured) /// Storage: Timestamp Now (r:1 w:0) /// Proof: Timestamp Now (max_values: Some(1), max_size: Some(8), added: 503, mode: Measured) - /// Storage: System EventTopics (r:322 w:322) + /// Storage: System EventTopics (r:6 w:6) /// Proof Skipped: System EventTopics (max_values: None, max_size: None, mode: Measured) /// The range of component `t` is `[0, 4]`. - /// The range of component `n` is `[0, 16]`. - fn seal_deposit_event_per_topic_and_kb(t: u32, n: u32, ) -> Weight { - // Proof Size summary in bytes: - // Measured: `1725 + t * (2608 ±0) + n * (7 ±0)` - // Estimated: `26340 + t * (211030 ±0) + n * (50 ±0)` - // Minimum execution time: 1_317_129_000 picoseconds. - Weight::from_parts(566_722_295, 26340) - // Standard Error: 619_390 - .saturating_add(Weight::from_parts(194_334_643, 0).saturating_mul(t.into())) - // Standard Error: 170_114 - .saturating_add(Weight::from_parts(67_458_751, 0).saturating_mul(n.into())) + /// The range of component `n` is `[0, 16384]`. + fn seal_deposit_event_per_topic_and_byte(t: u32, n: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `893 + t * (32 ±0)` + // Estimated: `22320 + t * (2640 ±0)` + // Minimum execution time: 313_860_000 picoseconds. + Weight::from_parts(312_473_092, 22320) + // Standard Error: 250_852 + .saturating_add(Weight::from_parts(2_258_502, 0).saturating_mul(t.into())) + // Standard Error: 70 + .saturating_add(Weight::from_parts(312, 0).saturating_mul(n.into())) .saturating_add(RocksDbWeight::get().reads(6_u64)) - .saturating_add(RocksDbWeight::get().reads((80_u64).saturating_mul(t.into()))) + .saturating_add(RocksDbWeight::get().reads((1_u64).saturating_mul(t.into()))) .saturating_add(RocksDbWeight::get().writes(3_u64)) - .saturating_add(RocksDbWeight::get().writes((80_u64).saturating_mul(t.into()))) - .saturating_add(Weight::from_parts(0, 211030).saturating_mul(t.into())) - .saturating_add(Weight::from_parts(0, 50).saturating_mul(n.into())) + .saturating_add(RocksDbWeight::get().writes((1_u64).saturating_mul(t.into()))) + .saturating_add(Weight::from_parts(0, 2640).saturating_mul(t.into())) } /// Storage: System Account (r:1 w:0) /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: Measured) @@ -2903,18 +2873,18 @@ impl WeightInfo for () { /// Proof: Timestamp Now (max_values: Some(1), max_size: Some(8), added: 503, mode: Measured) /// Storage: System EventTopics (r:2 w:2) /// Proof Skipped: System EventTopics (max_values: None, max_size: None, mode: Measured) - /// The range of component `r` is `[0, 20]`. + /// The range of component `r` is `[0, 1600]`. fn seal_debug_message(r: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `777 + r * (560 ±0)` - // Estimated: `21710 + r * (2800 ±0)` - // Minimum execution time: 159_087_000 picoseconds. - Weight::from_parts(163_703_981, 21710) - // Standard Error: 26_067 - .saturating_add(Weight::from_parts(14_810_256, 0).saturating_mul(r.into())) + // Measured: `873 + r * (7 ±0)` + // Estimated: `22205 + r * (35 ±0)` + // Minimum execution time: 162_043_000 picoseconds. + Weight::from_parts(166_132_332, 22205) + // Standard Error: 716 + .saturating_add(Weight::from_parts(184_981, 0).saturating_mul(r.into())) .saturating_add(RocksDbWeight::get().reads(6_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) - .saturating_add(Weight::from_parts(0, 2800).saturating_mul(r.into())) + .saturating_add(Weight::from_parts(0, 35).saturating_mul(r.into())) } /// Storage: System Account (r:1 w:0) /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) @@ -2926,200 +2896,187 @@ impl WeightInfo for () { /// Proof: Timestamp Now (max_values: Some(1), max_size: Some(8), added: 503, mode: MaxEncodedLen) /// Storage: System EventTopics (r:2 w:2) /// Proof Skipped: System EventTopics (max_values: None, max_size: None, mode: Measured) - /// The range of component `i` is `[0, 1024]`. - fn seal_debug_message_per_kb(i: u32, ) -> Weight { - // Proof Size summary in bytes: - // Measured: `125728` - // Estimated: `269982` - // Minimum execution time: 413_391_000 picoseconds. - Weight::from_parts(414_927_640, 269982) - // Standard Error: 1_740 - .saturating_add(Weight::from_parts(763_359, 0).saturating_mul(i.into())) + /// The range of component `i` is `[0, 1048576]`. + fn seal_debug_message_per_byte(i: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `125824` + // Estimated: `270073` + // Minimum execution time: 414_433_000 picoseconds. + Weight::from_parts(417_483_627, 270073) + // Standard Error: 1 + .saturating_add(Weight::from_parts(748, 0).saturating_mul(i.into())) .saturating_add(RocksDbWeight::get().reads(6_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) } /// Storage: Skipped Metadata (r:0 w:0) /// Proof Skipped: Skipped Metadata (max_values: None, max_size: None, mode: Measured) - /// The range of component `r` is `[0, 10]`. + /// The range of component `r` is `[0, 800]`. fn seal_set_storage(r: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `815 + r * (23420 ±0)` - // Estimated: `815 + r * (23418 ±0)` - // Minimum execution time: 296_108_000 picoseconds. - Weight::from_parts(208_214_040, 815) - // Standard Error: 860_573 - .saturating_add(Weight::from_parts(480_342_550, 0).saturating_mul(r.into())) + // Measured: `941 + r * (292 ±0)` + // Estimated: `939 + r * (293 ±0)` + // Minimum execution time: 299_500_000 picoseconds. + Weight::from_parts(194_466_413, 939) + // Standard Error: 9_986 + .saturating_add(Weight::from_parts(6_010_112, 0).saturating_mul(r.into())) .saturating_add(RocksDbWeight::get().reads(6_u64)) - .saturating_add(RocksDbWeight::get().reads((80_u64).saturating_mul(r.into()))) + .saturating_add(RocksDbWeight::get().reads((1_u64).saturating_mul(r.into()))) .saturating_add(RocksDbWeight::get().writes(3_u64)) - .saturating_add(RocksDbWeight::get().writes((80_u64).saturating_mul(r.into()))) - .saturating_add(Weight::from_parts(0, 23418).saturating_mul(r.into())) + .saturating_add(RocksDbWeight::get().writes((1_u64).saturating_mul(r.into()))) + .saturating_add(Weight::from_parts(0, 293).saturating_mul(r.into())) } /// Storage: Skipped Metadata (r:0 w:0) /// Proof Skipped: Skipped Metadata (max_values: None, max_size: None, mode: Measured) - /// The range of component `n` is `[0, 8]`. - fn seal_set_storage_per_new_kb(n: u32, ) -> Weight { - // Proof Size summary in bytes: - // Measured: `12544 + n * (11945 ±0)` - // Estimated: `8401 + n * (12814 ±61)` - // Minimum execution time: 442_286_000 picoseconds. - Weight::from_parts(620_395_299, 8401) - // Standard Error: 1_661_554 - .saturating_add(Weight::from_parts(89_393_169, 0).saturating_mul(n.into())) - .saturating_add(RocksDbWeight::get().reads(52_u64)) - .saturating_add(RocksDbWeight::get().reads((7_u64).saturating_mul(n.into()))) - .saturating_add(RocksDbWeight::get().writes(50_u64)) - .saturating_add(RocksDbWeight::get().writes((7_u64).saturating_mul(n.into()))) - .saturating_add(Weight::from_parts(0, 12814).saturating_mul(n.into())) + /// The range of component `n` is `[0, 16384]`. + fn seal_set_storage_per_new_byte(n: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `1432` + // Estimated: `1405` + // Minimum execution time: 314_171_000 picoseconds. + Weight::from_parts(335_595_397, 1405) + // Standard Error: 67 + .saturating_add(Weight::from_parts(90, 0).saturating_mul(n.into())) + .saturating_add(RocksDbWeight::get().reads(8_u64)) + .saturating_add(RocksDbWeight::get().writes(6_u64)) } /// Storage: Skipped Metadata (r:0 w:0) /// Proof Skipped: Skipped Metadata (max_values: None, max_size: None, mode: Measured) - /// The range of component `n` is `[0, 8]`. - fn seal_set_storage_per_old_kb(n: u32, ) -> Weight { - // Proof Size summary in bytes: - // Measured: `12532 + n * (175784 ±0)` - // Estimated: `8396 + n * (176649 ±61)` - // Minimum execution time: 440_882_000 picoseconds. - Weight::from_parts(587_317_981, 8396) - // Standard Error: 1_374_991 - .saturating_add(Weight::from_parts(66_271_084, 0).saturating_mul(n.into())) - .saturating_add(RocksDbWeight::get().reads(51_u64)) - .saturating_add(RocksDbWeight::get().reads((7_u64).saturating_mul(n.into()))) - .saturating_add(RocksDbWeight::get().writes(49_u64)) - .saturating_add(RocksDbWeight::get().writes((7_u64).saturating_mul(n.into()))) - .saturating_add(Weight::from_parts(0, 176649).saturating_mul(n.into())) + /// The range of component `n` is `[0, 16384]`. + fn seal_set_storage_per_old_byte(n: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `1295 + n * (1 ±0)` + // Estimated: `1292 + n * (1 ±0)` + // Minimum execution time: 313_479_000 picoseconds. + Weight::from_parts(317_435_100, 1292) + // Standard Error: 41 + .saturating_add(Weight::from_parts(106, 0).saturating_mul(n.into())) + .saturating_add(RocksDbWeight::get().reads(7_u64)) + .saturating_add(RocksDbWeight::get().writes(4_u64)) + .saturating_add(Weight::from_parts(0, 1).saturating_mul(n.into())) } /// Storage: Skipped Metadata (r:0 w:0) /// Proof Skipped: Skipped Metadata (max_values: None, max_size: None, mode: Measured) - /// The range of component `r` is `[0, 10]`. + /// The range of component `r` is `[0, 800]`. fn seal_clear_storage(r: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `807 + r * (23099 ±0)` - // Estimated: `812 + r * (23099 ±0)` - // Minimum execution time: 296_023_000 picoseconds. - Weight::from_parts(221_471_559, 812) - // Standard Error: 814_503 - .saturating_add(Weight::from_parts(465_928_089, 0).saturating_mul(r.into())) + // Measured: `937 + r * (288 ±0)` + // Estimated: `941 + r * (289 ±0)` + // Minimum execution time: 297_831_000 picoseconds. + Weight::from_parts(196_983_778, 941) + // Standard Error: 9_899 + .saturating_add(Weight::from_parts(5_904_642, 0).saturating_mul(r.into())) .saturating_add(RocksDbWeight::get().reads(6_u64)) - .saturating_add(RocksDbWeight::get().reads((80_u64).saturating_mul(r.into()))) + .saturating_add(RocksDbWeight::get().reads((1_u64).saturating_mul(r.into()))) .saturating_add(RocksDbWeight::get().writes(3_u64)) - .saturating_add(RocksDbWeight::get().writes((80_u64).saturating_mul(r.into()))) - .saturating_add(Weight::from_parts(0, 23099).saturating_mul(r.into())) + .saturating_add(RocksDbWeight::get().writes((1_u64).saturating_mul(r.into()))) + .saturating_add(Weight::from_parts(0, 289).saturating_mul(r.into())) } /// Storage: Skipped Metadata (r:0 w:0) /// Proof Skipped: Skipped Metadata (max_values: None, max_size: None, mode: Measured) - /// The range of component `n` is `[0, 8]`. - fn seal_clear_storage_per_kb(n: u32, ) -> Weight { - // Proof Size summary in bytes: - // Measured: `12256 + n * (175776 ±0)` - // Estimated: `8047 + n * (176655 ±62)` - // Minimum execution time: 412_212_000 picoseconds. - Weight::from_parts(569_213_147, 8047) - // Standard Error: 1_463_771 - .saturating_add(Weight::from_parts(67_932_081, 0).saturating_mul(n.into())) - .saturating_add(RocksDbWeight::get().reads(51_u64)) - .saturating_add(RocksDbWeight::get().reads((7_u64).saturating_mul(n.into()))) - .saturating_add(RocksDbWeight::get().writes(48_u64)) - .saturating_add(RocksDbWeight::get().writes((7_u64).saturating_mul(n.into()))) - .saturating_add(Weight::from_parts(0, 176655).saturating_mul(n.into())) + /// The range of component `n` is `[0, 16384]`. + fn seal_clear_storage_per_byte(n: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `1291 + n * (1 ±0)` + // Estimated: `1288 + n * (1 ±0)` + // Minimum execution time: 320_156_000 picoseconds. + Weight::from_parts(327_504_368, 1288) + .saturating_add(RocksDbWeight::get().reads(7_u64)) + .saturating_add(RocksDbWeight::get().writes(4_u64)) + .saturating_add(Weight::from_parts(0, 1).saturating_mul(n.into())) } /// Storage: Skipped Metadata (r:0 w:0) /// Proof Skipped: Skipped Metadata (max_values: None, max_size: None, mode: Measured) - /// The range of component `r` is `[0, 10]`. + /// The range of component `r` is `[0, 800]`. fn seal_get_storage(r: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `800 + r * (23744 ±0)` - // Estimated: `813 + r * (23740 ±0)` - // Minimum execution time: 296_007_000 picoseconds. - Weight::from_parts(229_100_700, 813) - // Standard Error: 684_970 - .saturating_add(Weight::from_parts(394_982_991, 0).saturating_mul(r.into())) + // Measured: `931 + r * (296 ±0)` + // Estimated: `936 + r * (297 ±0)` + // Minimum execution time: 305_849_000 picoseconds. + Weight::from_parts(219_649_351, 936) + // Standard Error: 9_157 + .saturating_add(Weight::from_parts(4_846_108, 0).saturating_mul(r.into())) .saturating_add(RocksDbWeight::get().reads(6_u64)) - .saturating_add(RocksDbWeight::get().reads((80_u64).saturating_mul(r.into()))) + .saturating_add(RocksDbWeight::get().reads((1_u64).saturating_mul(r.into()))) .saturating_add(RocksDbWeight::get().writes(3_u64)) - .saturating_add(Weight::from_parts(0, 23740).saturating_mul(r.into())) + .saturating_add(Weight::from_parts(0, 297).saturating_mul(r.into())) } /// Storage: Skipped Metadata (r:0 w:0) /// Proof Skipped: Skipped Metadata (max_values: None, max_size: None, mode: Measured) - /// The range of component `n` is `[0, 8]`. - fn seal_get_storage_per_kb(n: u32, ) -> Weight { - // Proof Size summary in bytes: - // Measured: `12863 + n * (175784 ±0)` - // Estimated: `8538 + n * (176688 ±63)` - // Minimum execution time: 392_306_000 picoseconds. - Weight::from_parts(539_188_004, 8538) - // Standard Error: 1_371_839 - .saturating_add(Weight::from_parts(155_130_373, 0).saturating_mul(n.into())) - .saturating_add(RocksDbWeight::get().reads(51_u64)) - .saturating_add(RocksDbWeight::get().reads((7_u64).saturating_mul(n.into()))) + /// The range of component `n` is `[0, 16384]`. + fn seal_get_storage_per_byte(n: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `1307 + n * (1 ±0)` + // Estimated: `1304 + n * (1 ±0)` + // Minimum execution time: 312_106_000 picoseconds. + Weight::from_parts(315_905_779, 1304) + // Standard Error: 44 + .saturating_add(Weight::from_parts(674, 0).saturating_mul(n.into())) + .saturating_add(RocksDbWeight::get().reads(7_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) - .saturating_add(Weight::from_parts(0, 176688).saturating_mul(n.into())) + .saturating_add(Weight::from_parts(0, 1).saturating_mul(n.into())) } /// Storage: Skipped Metadata (r:0 w:0) /// Proof Skipped: Skipped Metadata (max_values: None, max_size: None, mode: Measured) - /// The range of component `r` is `[0, 10]`. + /// The range of component `r` is `[0, 800]`. fn seal_contains_storage(r: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `818 + r * (23098 ±0)` - // Estimated: `824 + r * (23098 ±0)` - // Minimum execution time: 296_038_000 picoseconds. - Weight::from_parts(229_462_798, 824) - // Standard Error: 655_463 - .saturating_add(Weight::from_parts(372_593_685, 0).saturating_mul(r.into())) + // Measured: `952 + r * (288 ±0)` + // Estimated: `953 + r * (289 ±0)` + // Minimum execution time: 299_372_000 picoseconds. + Weight::from_parts(211_293_493, 953) + // Standard Error: 8_509 + .saturating_add(Weight::from_parts(4_688_993, 0).saturating_mul(r.into())) .saturating_add(RocksDbWeight::get().reads(6_u64)) - .saturating_add(RocksDbWeight::get().reads((80_u64).saturating_mul(r.into()))) + .saturating_add(RocksDbWeight::get().reads((1_u64).saturating_mul(r.into()))) .saturating_add(RocksDbWeight::get().writes(3_u64)) - .saturating_add(Weight::from_parts(0, 23098).saturating_mul(r.into())) + .saturating_add(Weight::from_parts(0, 289).saturating_mul(r.into())) } /// Storage: Skipped Metadata (r:0 w:0) /// Proof Skipped: Skipped Metadata (max_values: None, max_size: None, mode: Measured) - /// The range of component `n` is `[0, 8]`. - fn seal_contains_storage_per_kb(n: u32, ) -> Weight { - // Proof Size summary in bytes: - // Measured: `12200 + n * (175798 ±0)` - // Estimated: `8028 + n * (176661 ±62)` - // Minimum execution time: 386_782_000 picoseconds. - Weight::from_parts(512_965_975, 8028) - // Standard Error: 1_168_132 - .saturating_add(Weight::from_parts(63_307_325, 0).saturating_mul(n.into())) - .saturating_add(RocksDbWeight::get().reads(51_u64)) - .saturating_add(RocksDbWeight::get().reads((7_u64).saturating_mul(n.into()))) + /// The range of component `n` is `[0, 16384]`. + fn seal_contains_storage_per_byte(n: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `1294 + n * (1 ±0)` + // Estimated: `1291 + n * (1 ±0)` + // Minimum execution time: 311_605_000 picoseconds. + Weight::from_parts(315_473_850, 1291) + // Standard Error: 37 + .saturating_add(Weight::from_parts(3, 0).saturating_mul(n.into())) + .saturating_add(RocksDbWeight::get().reads(7_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) - .saturating_add(Weight::from_parts(0, 176661).saturating_mul(n.into())) + .saturating_add(Weight::from_parts(0, 1).saturating_mul(n.into())) } /// Storage: Skipped Metadata (r:0 w:0) /// Proof Skipped: Skipped Metadata (max_values: None, max_size: None, mode: Measured) - /// The range of component `r` is `[0, 10]`. + /// The range of component `r` is `[0, 800]`. fn seal_take_storage(r: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `815 + r * (23740 ±0)` - // Estimated: `817 + r * (23739 ±0)` - // Minimum execution time: 296_352_000 picoseconds. - Weight::from_parts(212_661_296, 817) - // Standard Error: 879_297 - .saturating_add(Weight::from_parts(494_182_672, 0).saturating_mul(r.into())) + // Measured: `925 + r * (296 ±0)` + // Estimated: `932 + r * (297 ±0)` + // Minimum execution time: 298_231_000 picoseconds. + Weight::from_parts(200_178_698, 932) + // Standard Error: 10_452 + .saturating_add(Weight::from_parts(6_107_653, 0).saturating_mul(r.into())) .saturating_add(RocksDbWeight::get().reads(6_u64)) - .saturating_add(RocksDbWeight::get().reads((80_u64).saturating_mul(r.into()))) + .saturating_add(RocksDbWeight::get().reads((1_u64).saturating_mul(r.into()))) .saturating_add(RocksDbWeight::get().writes(3_u64)) - .saturating_add(RocksDbWeight::get().writes((80_u64).saturating_mul(r.into()))) - .saturating_add(Weight::from_parts(0, 23739).saturating_mul(r.into())) + .saturating_add(RocksDbWeight::get().writes((1_u64).saturating_mul(r.into()))) + .saturating_add(Weight::from_parts(0, 297).saturating_mul(r.into())) } /// Storage: Skipped Metadata (r:0 w:0) /// Proof Skipped: Skipped Metadata (max_values: None, max_size: None, mode: Measured) - /// The range of component `n` is `[0, 8]`. - fn seal_take_storage_per_kb(n: u32, ) -> Weight { - // Proof Size summary in bytes: - // Measured: `12864 + n * (175784 ±0)` - // Estimated: `8539 + n * (176686 ±63)` - // Minimum execution time: 414_693_000 picoseconds. - Weight::from_parts(595_693_859, 8539) - // Standard Error: 1_673_819 - .saturating_add(Weight::from_parts(163_090_316, 0).saturating_mul(n.into())) - .saturating_add(RocksDbWeight::get().reads(51_u64)) - .saturating_add(RocksDbWeight::get().reads((7_u64).saturating_mul(n.into()))) - .saturating_add(RocksDbWeight::get().writes(48_u64)) - .saturating_add(RocksDbWeight::get().writes((7_u64).saturating_mul(n.into()))) - .saturating_add(Weight::from_parts(0, 176686).saturating_mul(n.into())) + /// The range of component `n` is `[0, 16384]`. + fn seal_take_storage_per_byte(n: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `1308 + n * (1 ±0)` + // Estimated: `1305 + n * (1 ±0)` + // Minimum execution time: 314_970_000 picoseconds. + Weight::from_parts(318_135_821, 1305) + // Standard Error: 26 + .saturating_add(Weight::from_parts(630, 0).saturating_mul(n.into())) + .saturating_add(RocksDbWeight::get().reads(7_u64)) + .saturating_add(RocksDbWeight::get().writes(4_u64)) + .saturating_add(Weight::from_parts(0, 1).saturating_mul(n.into())) } /// Storage: System Account (r:1602 w:1601) /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: Measured) @@ -3131,20 +3088,20 @@ impl WeightInfo for () { /// Proof: Timestamp Now (max_values: Some(1), max_size: Some(8), added: 503, mode: Measured) /// Storage: System EventTopics (r:2 w:2) /// Proof Skipped: System EventTopics (max_values: None, max_size: None, mode: Measured) - /// The range of component `r` is `[0, 20]`. + /// The range of component `r` is `[0, 1600]`. fn seal_transfer(r: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `1329 + r * (3604 ±0)` - // Estimated: `25928 + r * (216096 ±4)` - // Minimum execution time: 299_205_000 picoseconds. - Weight::from_parts(221_142_217, 25928) - // Standard Error: 1_369_909 - .saturating_add(Weight::from_parts(1_665_917_241, 0).saturating_mul(r.into())) + // Measured: `1501 + r * (45 ±0)` + // Estimated: `27383 + r * (2700 ±0)` + // Minimum execution time: 299_629_000 picoseconds. + Weight::from_parts(150_915_187, 27383) + // Standard Error: 25_604 + .saturating_add(Weight::from_parts(20_859_844, 0).saturating_mul(r.into())) .saturating_add(RocksDbWeight::get().reads(7_u64)) - .saturating_add(RocksDbWeight::get().reads((80_u64).saturating_mul(r.into()))) + .saturating_add(RocksDbWeight::get().reads((1_u64).saturating_mul(r.into()))) .saturating_add(RocksDbWeight::get().writes(4_u64)) - .saturating_add(RocksDbWeight::get().writes((80_u64).saturating_mul(r.into()))) - .saturating_add(Weight::from_parts(0, 216096).saturating_mul(r.into())) + .saturating_add(RocksDbWeight::get().writes((1_u64).saturating_mul(r.into()))) + .saturating_add(Weight::from_parts(0, 2700).saturating_mul(r.into())) } /// Storage: System Account (r:1 w:0) /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: Measured) @@ -3156,20 +3113,20 @@ impl WeightInfo for () { /// Proof: Timestamp Now (max_values: Some(1), max_size: Some(8), added: 503, mode: Measured) /// Storage: System EventTopics (r:1602 w:1602) /// Proof Skipped: System EventTopics (max_values: None, max_size: None, mode: Measured) - /// The range of component `r` is `[0, 20]`. + /// The range of component `r` is `[0, 1600]`. fn seal_call(r: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `1479 + r * (20514 ±0)` - // Estimated: `26429 + r * (498656 ±1)` - // Minimum execution time: 297_501_000 picoseconds. - Weight::from_parts(298_400_000, 26429) - // Standard Error: 9_372_890 - .saturating_add(Weight::from_parts(22_507_984_433, 0).saturating_mul(r.into())) + // Measured: `1670 + r * (288 ±0)` + // Estimated: `27813 + r * (6391 ±0)` + // Minimum execution time: 299_578_000 picoseconds. + Weight::from_parts(300_036_000, 27813) + // Standard Error: 102_709 + .saturating_add(Weight::from_parts(283_767_316, 0).saturating_mul(r.into())) .saturating_add(RocksDbWeight::get().reads(7_u64)) - .saturating_add(RocksDbWeight::get().reads((160_u64).saturating_mul(r.into()))) + .saturating_add(RocksDbWeight::get().reads((2_u64).saturating_mul(r.into()))) .saturating_add(RocksDbWeight::get().writes(3_u64)) - .saturating_add(RocksDbWeight::get().writes((160_u64).saturating_mul(r.into()))) - .saturating_add(Weight::from_parts(0, 498656).saturating_mul(r.into())) + .saturating_add(RocksDbWeight::get().writes((2_u64).saturating_mul(r.into()))) + .saturating_add(Weight::from_parts(0, 6391).saturating_mul(r.into())) } /// Storage: System Account (r:1 w:0) /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: Measured) @@ -3181,48 +3138,48 @@ impl WeightInfo for () { /// Proof: Timestamp Now (max_values: Some(1), max_size: Some(8), added: 503, mode: Measured) /// Storage: System EventTopics (r:1537 w:1537) /// Proof Skipped: System EventTopics (max_values: None, max_size: None, mode: Measured) - /// The range of component `r` is `[0, 20]`. + /// The range of component `r` is `[0, 1600]`. fn seal_delegate_call(r: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `0 + r * (69030 ±0)` - // Estimated: `21755 + r * (647542 ±560)` - // Minimum execution time: 297_432_000 picoseconds. - Weight::from_parts(298_220_000, 21755) - // Standard Error: 9_420_834 - .saturating_add(Weight::from_parts(22_261_152_353, 0).saturating_mul(r.into())) + // Measured: `0 + r * (935 ±0)` + // Estimated: `22235 + r * (8322 ±10)` + // Minimum execution time: 298_943_000 picoseconds. + Weight::from_parts(299_619_000, 22235) + // Standard Error: 117_493 + .saturating_add(Weight::from_parts(280_555_517, 0).saturating_mul(r.into())) .saturating_add(RocksDbWeight::get().reads(6_u64)) - .saturating_add(RocksDbWeight::get().reads((150_u64).saturating_mul(r.into()))) + .saturating_add(RocksDbWeight::get().reads((2_u64).saturating_mul(r.into()))) .saturating_add(RocksDbWeight::get().writes(3_u64)) - .saturating_add(RocksDbWeight::get().writes((75_u64).saturating_mul(r.into()))) - .saturating_add(Weight::from_parts(0, 647542).saturating_mul(r.into())) + .saturating_add(RocksDbWeight::get().writes((1_u64).saturating_mul(r.into()))) + .saturating_add(Weight::from_parts(0, 8322).saturating_mul(r.into())) } - /// Storage: System Account (r:82 w:81) + /// Storage: System Account (r:3 w:2) /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: Measured) - /// Storage: Contracts ContractInfoOf (r:81 w:81) + /// Storage: Contracts ContractInfoOf (r:2 w:2) /// Proof: Contracts ContractInfoOf (max_values: None, max_size: Some(290), added: 2765, mode: Measured) /// Storage: Contracts CodeStorage (r:2 w:0) /// Proof: Contracts CodeStorage (max_values: None, max_size: Some(126001), added: 128476, mode: Measured) /// Storage: Timestamp Now (r:1 w:0) /// Proof: Timestamp Now (max_values: Some(1), max_size: Some(8), added: 503, mode: Measured) - /// Storage: System EventTopics (r:82 w:82) + /// Storage: System EventTopics (r:3 w:3) /// Proof Skipped: System EventTopics (max_values: None, max_size: None, mode: Measured) /// The range of component `t` is `[0, 1]`. - /// The range of component `c` is `[0, 1024]`. - fn seal_call_per_transfer_clone_kb(t: u32, c: u32, ) -> Weight { - // Proof Size summary in bytes: - // Measured: `21581 + t * (14318 ±0)` - // Estimated: `524200 + t * (272065 ±0)` - // Minimum execution time: 11_799_802_000 picoseconds. - Weight::from_parts(10_575_328_547, 524200) - // Standard Error: 11_952_810 - .saturating_add(Weight::from_parts(1_603_077_083, 0).saturating_mul(t.into())) - // Standard Error: 17_922 - .saturating_add(Weight::from_parts(9_876_752, 0).saturating_mul(c.into())) - .saturating_add(RocksDbWeight::get().reads(167_u64)) - .saturating_add(RocksDbWeight::get().reads((81_u64).saturating_mul(t.into()))) - .saturating_add(RocksDbWeight::get().writes(163_u64)) - .saturating_add(RocksDbWeight::get().writes((81_u64).saturating_mul(t.into()))) - .saturating_add(Weight::from_parts(0, 272065).saturating_mul(t.into())) + /// The range of component `c` is `[0, 1048576]`. + fn seal_call_per_transfer_clone_byte(t: u32, c: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `1314 + t * (268 ±0)` + // Estimated: `31815 + t * (6290 ±0)` + // Minimum execution time: 453_013_000 picoseconds. + Weight::from_parts(442_536_283, 31815) + // Standard Error: 1_339_541 + .saturating_add(Weight::from_parts(17_062_445, 0).saturating_mul(t.into())) + // Standard Error: 1 + .saturating_add(Weight::from_parts(604, 0).saturating_mul(c.into())) + .saturating_add(RocksDbWeight::get().reads(9_u64)) + .saturating_add(RocksDbWeight::get().reads((2_u64).saturating_mul(t.into()))) + .saturating_add(RocksDbWeight::get().writes(5_u64)) + .saturating_add(RocksDbWeight::get().writes((2_u64).saturating_mul(t.into()))) + .saturating_add(Weight::from_parts(0, 6290).saturating_mul(t.into())) } /// Storage: System Account (r:3202 w:3202) /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: Measured) @@ -3238,24 +3195,24 @@ impl WeightInfo for () { /// Proof: Contracts OwnerInfoOf (max_values: None, max_size: Some(88), added: 2563, mode: Measured) /// Storage: System EventTopics (r:1602 w:1602) /// Proof Skipped: System EventTopics (max_values: None, max_size: None, mode: Measured) - /// The range of component `r` is `[0, 20]`. + /// The range of component `r` is `[0, 1600]`. fn seal_instantiate(r: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `1646 + r * (20288 ±0)` - // Estimated: `32625 + r * (1330150 ±2)` - // Minimum execution time: 298_155_000 picoseconds. - Weight::from_parts(299_212_000, 32625) - // Standard Error: 32_410_313 - .saturating_add(Weight::from_parts(30_417_025_178, 0).saturating_mul(r.into())) + // Measured: `1988 + r * (319 ±0)` + // Estimated: `34745 + r * (17090 ±0)` + // Minimum execution time: 300_123_000 picoseconds. + Weight::from_parts(300_406_000, 34745) + // Standard Error: 420_997 + .saturating_add(Weight::from_parts(382_704_025, 0).saturating_mul(r.into())) .saturating_add(RocksDbWeight::get().reads(8_u64)) - .saturating_add(RocksDbWeight::get().reads((480_u64).saturating_mul(r.into()))) - .saturating_add(RocksDbWeight::get().writes(5_u64)) - .saturating_add(RocksDbWeight::get().writes((400_u64).saturating_mul(r.into()))) - .saturating_add(Weight::from_parts(0, 1330150).saturating_mul(r.into())) + .saturating_add(RocksDbWeight::get().reads((6_u64).saturating_mul(r.into()))) + .saturating_add(RocksDbWeight::get().writes(6_u64)) + .saturating_add(RocksDbWeight::get().writes((5_u64).saturating_mul(r.into()))) + .saturating_add(Weight::from_parts(0, 17090).saturating_mul(r.into())) } - /// Storage: System Account (r:162 w:162) + /// Storage: System Account (r:4 w:4) /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: Measured) - /// Storage: Contracts ContractInfoOf (r:81 w:81) + /// Storage: Contracts ContractInfoOf (r:2 w:2) /// Proof: Contracts ContractInfoOf (max_values: None, max_size: Some(290), added: 2765, mode: Measured) /// Storage: Contracts CodeStorage (r:2 w:0) /// Proof: Contracts CodeStorage (max_values: None, max_size: Some(126001), added: 128476, mode: Measured) @@ -3265,28 +3222,28 @@ impl WeightInfo for () { /// Proof: Contracts Nonce (max_values: Some(1), max_size: Some(8), added: 503, mode: Measured) /// Storage: Contracts OwnerInfoOf (r:1 w:1) /// Proof: Contracts OwnerInfoOf (max_values: None, max_size: Some(88), added: 2563, mode: Measured) - /// Storage: System EventTopics (r:82 w:82) + /// Storage: System EventTopics (r:3 w:3) /// Proof Skipped: System EventTopics (max_values: None, max_size: None, mode: Measured) /// The range of component `t` is `[0, 1]`. - /// The range of component `i` is `[0, 960]`. - /// The range of component `s` is `[0, 960]`. - fn seal_instantiate_per_transfer_input_salt_kb(t: u32, i: u32, s: u32, ) -> Weight { - // Proof Size summary in bytes: - // Measured: `5625 + t * (1 ±0)` - // Estimated: `856795 + t * (2471 ±3)` - // Minimum execution time: 105_914_928_000 picoseconds. - Weight::from_parts(13_229_760_432, 856795) - // Standard Error: 96_394_437 - .saturating_add(Weight::from_parts(398_413_888, 0).saturating_mul(t.into())) - // Standard Error: 157_192 - .saturating_add(Weight::from_parts(97_104_978, 0).saturating_mul(i.into())) - // Standard Error: 157_192 - .saturating_add(Weight::from_parts(97_146_130, 0).saturating_mul(s.into())) - .saturating_add(RocksDbWeight::get().reads(329_u64)) + /// The range of component `i` is `[0, 983040]`. + /// The range of component `s` is `[0, 983040]`. + fn seal_instantiate_per_transfer_input_salt_byte(t: u32, i: u32, s: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `1231 + t * (219 ±0)` + // Estimated: `43797 + t * (3812 ±2)` + // Minimum execution time: 1_636_322_000 picoseconds. + Weight::from_parts(360_859_331, 43797) + // Standard Error: 4_816_923 + .saturating_add(Weight::from_parts(109_179_023, 0).saturating_mul(t.into())) + // Standard Error: 7 + .saturating_add(Weight::from_parts(1_180, 0).saturating_mul(i.into())) + // Standard Error: 7 + .saturating_add(Weight::from_parts(1_344, 0).saturating_mul(s.into())) + .saturating_add(RocksDbWeight::get().reads(13_u64)) .saturating_add(RocksDbWeight::get().reads((1_u64).saturating_mul(t.into()))) - .saturating_add(RocksDbWeight::get().writes(326_u64)) + .saturating_add(RocksDbWeight::get().writes(10_u64)) .saturating_add(RocksDbWeight::get().writes((1_u64).saturating_mul(t.into()))) - .saturating_add(Weight::from_parts(0, 2471).saturating_mul(t.into())) + .saturating_add(Weight::from_parts(0, 3812).saturating_mul(t.into())) } /// Storage: System Account (r:1 w:0) /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: Measured) @@ -3298,18 +3255,18 @@ impl WeightInfo for () { /// Proof: Timestamp Now (max_values: Some(1), max_size: Some(8), added: 503, mode: Measured) /// Storage: System EventTopics (r:2 w:2) /// Proof Skipped: System EventTopics (max_values: None, max_size: None, mode: Measured) - /// The range of component `r` is `[0, 1]`. + /// The range of component `r` is `[0, 1600]`. fn seal_hash_sha2_256(r: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `775 + r * (642 ±0)` - // Estimated: `21695 + r * (3210 ±0)` - // Minimum execution time: 294_309_000 picoseconds. - Weight::from_parts(296_241_318, 21695) - // Standard Error: 127_872 - .saturating_add(Weight::from_parts(45_806_281, 0).saturating_mul(r.into())) + // Measured: `873 + r * (8 ±0)` + // Estimated: `22190 + r * (40 ±0)` + // Minimum execution time: 297_521_000 picoseconds. + Weight::from_parts(303_523_260, 22190) + // Standard Error: 1_162 + .saturating_add(Weight::from_parts(542_201, 0).saturating_mul(r.into())) .saturating_add(RocksDbWeight::get().reads(6_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) - .saturating_add(Weight::from_parts(0, 3210).saturating_mul(r.into())) + .saturating_add(Weight::from_parts(0, 40).saturating_mul(r.into())) } /// Storage: System Account (r:1 w:0) /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: Measured) @@ -3321,15 +3278,15 @@ impl WeightInfo for () { /// Proof: Timestamp Now (max_values: Some(1), max_size: Some(8), added: 503, mode: Measured) /// Storage: System EventTopics (r:2 w:2) /// Proof Skipped: System EventTopics (max_values: None, max_size: None, mode: Measured) - /// The range of component `n` is `[0, 1024]`. - fn seal_hash_sha2_256_per_kb(n: u32, ) -> Weight { - // Proof Size summary in bytes: - // Measured: `1577` - // Estimated: `25630` - // Minimum execution time: 341_248_000 picoseconds. - Weight::from_parts(341_607_000, 25630) - // Standard Error: 66_687 - .saturating_add(Weight::from_parts(322_250_398, 0).saturating_mul(n.into())) + /// The range of component `n` is `[0, 1048576]`. + fn seal_hash_sha2_256_per_byte(n: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `881` + // Estimated: `22225` + // Minimum execution time: 299_877_000 picoseconds. + Weight::from_parts(293_538_014, 22225) + // Standard Error: 2 + .saturating_add(Weight::from_parts(3_967, 0).saturating_mul(n.into())) .saturating_add(RocksDbWeight::get().reads(6_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) } @@ -3343,18 +3300,18 @@ impl WeightInfo for () { /// Proof: Timestamp Now (max_values: Some(1), max_size: Some(8), added: 503, mode: Measured) /// Storage: System EventTopics (r:2 w:2) /// Proof Skipped: System EventTopics (max_values: None, max_size: None, mode: Measured) - /// The range of component `r` is `[0, 1]`. + /// The range of component `r` is `[0, 1600]`. fn seal_hash_keccak_256(r: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `777 + r * (642 ±0)` - // Estimated: `21705 + r * (3210 ±0)` - // Minimum execution time: 293_810_000 picoseconds. - Weight::from_parts(296_719_171, 21705) - // Standard Error: 450_807 - .saturating_add(Weight::from_parts(61_627_228, 0).saturating_mul(r.into())) + // Measured: `875 + r * (8 ±0)` + // Estimated: `22205 + r * (40 ±0)` + // Minimum execution time: 297_672_000 picoseconds. + Weight::from_parts(299_933_312, 22205) + // Standard Error: 1_138 + .saturating_add(Weight::from_parts(713_189, 0).saturating_mul(r.into())) .saturating_add(RocksDbWeight::get().reads(6_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) - .saturating_add(Weight::from_parts(0, 3210).saturating_mul(r.into())) + .saturating_add(Weight::from_parts(0, 40).saturating_mul(r.into())) } /// Storage: System Account (r:1 w:0) /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: Measured) @@ -3366,15 +3323,15 @@ impl WeightInfo for () { /// Proof: Timestamp Now (max_values: Some(1), max_size: Some(8), added: 503, mode: Measured) /// Storage: System EventTopics (r:2 w:2) /// Proof Skipped: System EventTopics (max_values: None, max_size: None, mode: Measured) - /// The range of component `n` is `[0, 1024]`. - fn seal_hash_keccak_256_per_kb(n: u32, ) -> Weight { - // Proof Size summary in bytes: - // Measured: `1579` - // Estimated: `25675` - // Minimum execution time: 354_550_000 picoseconds. - Weight::from_parts(355_136_000, 25675) - // Standard Error: 61_761 - .saturating_add(Weight::from_parts(257_021_566, 0).saturating_mul(n.into())) + /// The range of component `n` is `[0, 1048576]`. + fn seal_hash_keccak_256_per_byte(n: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `883` + // Estimated: `22245` + // Minimum execution time: 299_048_000 picoseconds. + Weight::from_parts(293_055_982, 22245) + // Standard Error: 2 + .saturating_add(Weight::from_parts(3_179, 0).saturating_mul(n.into())) .saturating_add(RocksDbWeight::get().reads(6_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) } @@ -3388,18 +3345,18 @@ impl WeightInfo for () { /// Proof: Timestamp Now (max_values: Some(1), max_size: Some(8), added: 503, mode: Measured) /// Storage: System EventTopics (r:2 w:2) /// Proof Skipped: System EventTopics (max_values: None, max_size: None, mode: Measured) - /// The range of component `r` is `[0, 1]`. + /// The range of component `r` is `[0, 1600]`. fn seal_hash_blake2_256(r: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `777 + r * (642 ±0)` - // Estimated: `21705 + r * (3210 ±0)` - // Minimum execution time: 293_369_000 picoseconds. - Weight::from_parts(295_990_893, 21705) - // Standard Error: 431_269 - .saturating_add(Weight::from_parts(32_213_406, 0).saturating_mul(r.into())) + // Measured: `875 + r * (8 ±0)` + // Estimated: `22220 + r * (40 ±0)` + // Minimum execution time: 301_991_000 picoseconds. + Weight::from_parts(300_027_441, 22220) + // Standard Error: 981 + .saturating_add(Weight::from_parts(391_319, 0).saturating_mul(r.into())) .saturating_add(RocksDbWeight::get().reads(6_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) - .saturating_add(Weight::from_parts(0, 3210).saturating_mul(r.into())) + .saturating_add(Weight::from_parts(0, 40).saturating_mul(r.into())) } /// Storage: System Account (r:1 w:0) /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: Measured) @@ -3411,15 +3368,15 @@ impl WeightInfo for () { /// Proof: Timestamp Now (max_values: Some(1), max_size: Some(8), added: 503, mode: Measured) /// Storage: System EventTopics (r:2 w:2) /// Proof Skipped: System EventTopics (max_values: None, max_size: None, mode: Measured) - /// The range of component `n` is `[0, 1024]`. - fn seal_hash_blake2_256_per_kb(n: u32, ) -> Weight { - // Proof Size summary in bytes: - // Measured: `1579` - // Estimated: `25650` - // Minimum execution time: 326_595_000 picoseconds. - Weight::from_parts(327_188_000, 25650) - // Standard Error: 53_385 - .saturating_add(Weight::from_parts(74_046_074, 0).saturating_mul(n.into())) + /// The range of component `n` is `[0, 1048576]`. + fn seal_hash_blake2_256_per_byte(n: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `883` + // Estimated: `22265` + // Minimum execution time: 296_522_000 picoseconds. + Weight::from_parts(296_121_638, 22265) + // Standard Error: 2 + .saturating_add(Weight::from_parts(916, 0).saturating_mul(n.into())) .saturating_add(RocksDbWeight::get().reads(6_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) } @@ -3433,18 +3390,18 @@ impl WeightInfo for () { /// Proof: Timestamp Now (max_values: Some(1), max_size: Some(8), added: 503, mode: Measured) /// Storage: System EventTopics (r:2 w:2) /// Proof Skipped: System EventTopics (max_values: None, max_size: None, mode: Measured) - /// The range of component `r` is `[0, 1]`. + /// The range of component `r` is `[0, 1600]`. fn seal_hash_blake2_128(r: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `777 + r * (679 ±0)` - // Estimated: `21705 + r * (3395 ±0)` - // Minimum execution time: 292_388_000 picoseconds. - Weight::from_parts(295_090_179, 21705) - // Standard Error: 170_487 - .saturating_add(Weight::from_parts(32_577_820, 0).saturating_mul(r.into())) + // Measured: `875 + r * (8 ±0)` + // Estimated: `22225 + r * (40 ±0)` + // Minimum execution time: 296_228_000 picoseconds. + Weight::from_parts(301_472_299, 22225) + // Standard Error: 875 + .saturating_add(Weight::from_parts(381_027, 0).saturating_mul(r.into())) .saturating_add(RocksDbWeight::get().reads(6_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) - .saturating_add(Weight::from_parts(0, 3395).saturating_mul(r.into())) + .saturating_add(Weight::from_parts(0, 40).saturating_mul(r.into())) } /// Storage: System Account (r:1 w:0) /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: Measured) @@ -3456,15 +3413,15 @@ impl WeightInfo for () { /// Proof: Timestamp Now (max_values: Some(1), max_size: Some(8), added: 503, mode: Measured) /// Storage: System EventTopics (r:2 w:2) /// Proof Skipped: System EventTopics (max_values: None, max_size: None, mode: Measured) - /// The range of component `n` is `[0, 1024]`. - fn seal_hash_blake2_128_per_kb(n: u32, ) -> Weight { - // Proof Size summary in bytes: - // Measured: `1579` - // Estimated: `25695` - // Minimum execution time: 326_862_000 picoseconds. - Weight::from_parts(327_449_000, 25695) - // Standard Error: 54_539 - .saturating_add(Weight::from_parts(74_042_337, 0).saturating_mul(n.into())) + /// The range of component `n` is `[0, 1048576]`. + fn seal_hash_blake2_128_per_byte(n: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `883` + // Estimated: `22235` + // Minimum execution time: 296_644_000 picoseconds. + Weight::from_parts(289_879_744, 22235) + // Standard Error: 2 + .saturating_add(Weight::from_parts(925, 0).saturating_mul(n.into())) .saturating_add(RocksDbWeight::get().reads(6_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) } @@ -3478,18 +3435,18 @@ impl WeightInfo for () { /// Proof: Timestamp Now (max_values: Some(1), max_size: Some(8), added: 503, mode: Measured) /// Storage: System EventTopics (r:2 w:2) /// Proof Skipped: System EventTopics (max_values: None, max_size: None, mode: Measured) - /// The range of component `r` is `[0, 1]`. + /// The range of component `r` is `[0, 1600]`. fn seal_ecdsa_recover(r: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `821 + r * (6083 ±0)` - // Estimated: `21925 + r * (30415 ±0)` - // Minimum execution time: 295_597_000 picoseconds. - Weight::from_parts(298_267_200, 21925) - // Standard Error: 699_824 - .saturating_add(Weight::from_parts(3_021_734_700, 0).saturating_mul(r.into())) + // Measured: `800 + r * (78 ±0)` + // Estimated: `21845 + r * (390 ±0)` + // Minimum execution time: 297_804_000 picoseconds. + Weight::from_parts(471_994_534, 21845) + // Standard Error: 9_479 + .saturating_add(Weight::from_parts(36_886_028, 0).saturating_mul(r.into())) .saturating_add(RocksDbWeight::get().reads(6_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) - .saturating_add(Weight::from_parts(0, 30415).saturating_mul(r.into())) + .saturating_add(Weight::from_parts(0, 390).saturating_mul(r.into())) } /// Storage: System Account (r:1 w:0) /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: Measured) @@ -3501,18 +3458,18 @@ impl WeightInfo for () { /// Proof: Timestamp Now (max_values: Some(1), max_size: Some(8), added: 503, mode: Measured) /// Storage: System EventTopics (r:2 w:2) /// Proof Skipped: System EventTopics (max_values: None, max_size: None, mode: Measured) - /// The range of component `r` is `[0, 1]`. + /// The range of component `r` is `[0, 1600]`. fn seal_ecdsa_to_eth_address(r: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `790 + r * (3362 ±0)` - // Estimated: `21770 + r * (16810 ±0)` - // Minimum execution time: 295_539_000 picoseconds. - Weight::from_parts(297_844_822, 21770) - // Standard Error: 490_532 - .saturating_add(Weight::from_parts(747_592_977, 0).saturating_mul(r.into())) + // Measured: `645 + r * (44 ±0)` + // Estimated: `21110 + r * (220 ±0)` + // Minimum execution time: 298_623_000 picoseconds. + Weight::from_parts(322_192_102, 21110) + // Standard Error: 3_207 + .saturating_add(Weight::from_parts(9_243_653, 0).saturating_mul(r.into())) .saturating_add(RocksDbWeight::get().reads(6_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) - .saturating_add(Weight::from_parts(0, 16810).saturating_mul(r.into())) + .saturating_add(Weight::from_parts(0, 220).saturating_mul(r.into())) } /// Storage: System Account (r:1 w:0) /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: Measured) @@ -3526,20 +3483,20 @@ impl WeightInfo for () { /// Proof: Contracts OwnerInfoOf (max_values: None, max_size: Some(88), added: 2563, mode: Measured) /// Storage: System EventTopics (r:1538 w:1538) /// Proof Skipped: System EventTopics (max_values: None, max_size: None, mode: Measured) - /// The range of component `r` is `[0, 20]`. + /// The range of component `r` is `[0, 1600]`. fn seal_set_code_hash(r: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `0 + r * (74020 ±0)` - // Estimated: `69192 + r * (913289 ±821)` - // Minimum execution time: 295_164_000 picoseconds. - Weight::from_parts(296_597_000, 69192) - // Standard Error: 3_646_577 - .saturating_add(Weight::from_parts(1_725_796_807, 0).saturating_mul(r.into())) + // Measured: `0 + r * (1030 ±0)` + // Estimated: `30592 + r * (11919 ±7)` + // Minimum execution time: 298_574_000 picoseconds. + Weight::from_parts(299_383_000, 30592) + // Standard Error: 44_061 + .saturating_add(Weight::from_parts(21_625_366, 0).saturating_mul(r.into())) .saturating_add(RocksDbWeight::get().reads(6_u64)) - .saturating_add(RocksDbWeight::get().reads((225_u64).saturating_mul(r.into()))) + .saturating_add(RocksDbWeight::get().reads((3_u64).saturating_mul(r.into()))) .saturating_add(RocksDbWeight::get().writes(3_u64)) - .saturating_add(RocksDbWeight::get().writes((150_u64).saturating_mul(r.into()))) - .saturating_add(Weight::from_parts(0, 913289).saturating_mul(r.into())) + .saturating_add(RocksDbWeight::get().writes((2_u64).saturating_mul(r.into()))) + .saturating_add(Weight::from_parts(0, 11919).saturating_mul(r.into())) } /// Storage: System Account (r:1 w:0) /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: Measured) @@ -3551,18 +3508,18 @@ impl WeightInfo for () { /// Proof: Timestamp Now (max_values: Some(1), max_size: Some(8), added: 503, mode: Measured) /// Storage: System EventTopics (r:2 w:2) /// Proof Skipped: System EventTopics (max_values: None, max_size: None, mode: Measured) - /// The range of component `r` is `[0, 20]`. + /// The range of component `r` is `[0, 1600]`. fn seal_reentrance_count(r: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `773 + r * (240 ±0)` - // Estimated: `21685 + r * (1200 ±0)` - // Minimum execution time: 296_287_000 picoseconds. - Weight::from_parts(300_696_694, 21685) - // Standard Error: 27_891 - .saturating_add(Weight::from_parts(10_943_994, 0).saturating_mul(r.into())) + // Measured: `869 + r * (3 ±0)` + // Estimated: `22215 + r * (15 ±0)` + // Minimum execution time: 297_014_000 picoseconds. + Weight::from_parts(301_226_615, 22215) + // Standard Error: 439 + .saturating_add(Weight::from_parts(143_017, 0).saturating_mul(r.into())) .saturating_add(RocksDbWeight::get().reads(6_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) - .saturating_add(Weight::from_parts(0, 1200).saturating_mul(r.into())) + .saturating_add(Weight::from_parts(0, 15).saturating_mul(r.into())) } /// Storage: System Account (r:1 w:0) /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: Measured) @@ -3574,18 +3531,18 @@ impl WeightInfo for () { /// Proof: Timestamp Now (max_values: Some(1), max_size: Some(8), added: 503, mode: Measured) /// Storage: System EventTopics (r:2 w:2) /// Proof Skipped: System EventTopics (max_values: None, max_size: None, mode: Measured) - /// The range of component `r` is `[0, 20]`. + /// The range of component `r` is `[0, 1600]`. fn seal_account_reentrance_count(r: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `2006 + r * (3154 ±0)` - // Estimated: `26435 + r * (15875 ±2)` - // Minimum execution time: 298_000_000 picoseconds. - Weight::from_parts(328_282_119, 26435) - // Standard Error: 104_874 - .saturating_add(Weight::from_parts(18_428_267, 0).saturating_mul(r.into())) + // Measured: `2072 + r * (39 ±0)` + // Estimated: `27645 + r * (200 ±0)` + // Minimum execution time: 299_074_000 picoseconds. + Weight::from_parts(336_979_016, 27645) + // Standard Error: 1_163 + .saturating_add(Weight::from_parts(227_998, 0).saturating_mul(r.into())) .saturating_add(RocksDbWeight::get().reads(6_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) - .saturating_add(Weight::from_parts(0, 15875).saturating_mul(r.into())) + .saturating_add(Weight::from_parts(0, 200).saturating_mul(r.into())) } /// Storage: System Account (r:1 w:0) /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: Measured) @@ -3599,537 +3556,525 @@ impl WeightInfo for () { /// Proof: Contracts Nonce (max_values: Some(1), max_size: Some(8), added: 503, mode: Measured) /// Storage: System EventTopics (r:2 w:2) /// Proof Skipped: System EventTopics (max_values: None, max_size: None, mode: Measured) - /// The range of component `r` is `[0, 20]`. + /// The range of component `r` is `[0, 1600]`. fn seal_instantiation_nonce(r: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `776 + r * (240 ±0)` - // Estimated: `23962 + r * (1440 ±0)` - // Minimum execution time: 295_104_000 picoseconds. - Weight::from_parts(299_334_585, 23962) - // Standard Error: 25_840 - .saturating_add(Weight::from_parts(9_744_866, 0).saturating_mul(r.into())) + // Measured: `872 + r * (3 ±0)` + // Estimated: `24580 + r * (18 ±0)` + // Minimum execution time: 296_959_000 picoseconds. + Weight::from_parts(303_796_839, 24580) + // Standard Error: 534 + .saturating_add(Weight::from_parts(118_978, 0).saturating_mul(r.into())) .saturating_add(RocksDbWeight::get().reads(7_u64)) .saturating_add(RocksDbWeight::get().writes(4_u64)) - .saturating_add(Weight::from_parts(0, 1440).saturating_mul(r.into())) + .saturating_add(Weight::from_parts(0, 18).saturating_mul(r.into())) } - /// The range of component `r` is `[0, 50]`. + /// The range of component `r` is `[0, 5000]`. fn instr_i64const(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_132_000 picoseconds. - Weight::from_parts(1_100_159, 0) - // Standard Error: 10_131 - .saturating_add(Weight::from_parts(436_462, 0).saturating_mul(r.into())) + // Minimum execution time: 1_020_000 picoseconds. + Weight::from_parts(1_355_107, 0) + // Standard Error: 1 + .saturating_add(Weight::from_parts(4_110, 0).saturating_mul(r.into())) } - /// The range of component `r` is `[0, 50]`. + /// The range of component `r` is `[0, 5000]`. fn instr_i64load(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_368_000 picoseconds. - Weight::from_parts(1_974_308, 0) - // Standard Error: 811 - .saturating_add(Weight::from_parts(1_079_620, 0).saturating_mul(r.into())) + // Minimum execution time: 1_224_000 picoseconds. + Weight::from_parts(1_819_284, 0) + // Standard Error: 10 + .saturating_add(Weight::from_parts(10_803, 0).saturating_mul(r.into())) } - /// The range of component `r` is `[0, 50]`. + /// The range of component `r` is `[0, 5000]`. fn instr_i64store(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_354_000 picoseconds. - Weight::from_parts(2_057_834, 0) - // Standard Error: 4_277 - .saturating_add(Weight::from_parts(1_008_797, 0).saturating_mul(r.into())) + // Minimum execution time: 1_216_000 picoseconds. + Weight::from_parts(1_795_011, 0) + // Standard Error: 5 + .saturating_add(Weight::from_parts(10_000, 0).saturating_mul(r.into())) } - /// The range of component `r` is `[0, 50]`. + /// The range of component `r` is `[0, 5000]`. fn instr_select(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_218_000 picoseconds. - Weight::from_parts(1_594_528, 0) - // Standard Error: 723 - .saturating_add(Weight::from_parts(1_147_486, 0).saturating_mul(r.into())) + // Minimum execution time: 1_127_000 picoseconds. + Weight::from_parts(1_491_730, 0) + // Standard Error: 4 + .saturating_add(Weight::from_parts(11_471, 0).saturating_mul(r.into())) } - /// The range of component `r` is `[0, 50]`. + /// The range of component `r` is `[0, 5000]`. fn instr_if(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_156_000 picoseconds. - Weight::from_parts(1_460_611, 0) - // Standard Error: 419 - .saturating_add(Weight::from_parts(1_314_741, 0).saturating_mul(r.into())) + // Minimum execution time: 1_044_000 picoseconds. + Weight::from_parts(2_330_852, 0) + // Standard Error: 69 + .saturating_add(Weight::from_parts(12_866, 0).saturating_mul(r.into())) } - /// The range of component `r` is `[0, 50]`. + /// The range of component `r` is `[0, 5000]`. fn instr_br(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_166_000 picoseconds. - Weight::from_parts(1_454_460, 0) - // Standard Error: 493 - .saturating_add(Weight::from_parts(642_481, 0).saturating_mul(r.into())) + // Minimum execution time: 1_067_000 picoseconds. + Weight::from_parts(1_399_626, 0) + // Standard Error: 5 + .saturating_add(Weight::from_parts(6_430, 0).saturating_mul(r.into())) } - /// The range of component `r` is `[0, 50]`. + /// The range of component `r` is `[0, 5000]`. fn instr_br_if(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_144_000 picoseconds. - Weight::from_parts(1_410_515, 0) - // Standard Error: 894 - .saturating_add(Weight::from_parts(958_765, 0).saturating_mul(r.into())) + // Minimum execution time: 1_087_000 picoseconds. + Weight::from_parts(1_463_592, 0) + // Standard Error: 12 + .saturating_add(Weight::from_parts(9_707, 0).saturating_mul(r.into())) } - /// The range of component `r` is `[0, 50]`. + /// The range of component `r` is `[0, 5000]`. fn instr_br_table(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_190_000 picoseconds. - Weight::from_parts(1_143_236, 0) - // Standard Error: 2_773 - .saturating_add(Weight::from_parts(1_164_764, 0).saturating_mul(r.into())) + // Minimum execution time: 1_110_000 picoseconds. + Weight::from_parts(1_058_258, 0) + // Standard Error: 22 + .saturating_add(Weight::from_parts(11_713, 0).saturating_mul(r.into())) } /// The range of component `e` is `[1, 256]`. - fn instr_br_table_per_entry(e: u32, ) -> Weight { + fn instr_br_table_per_entry(_e: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 2_993_000 picoseconds. - Weight::from_parts(3_270_525, 0) - // Standard Error: 65 - .saturating_add(Weight::from_parts(4_418, 0).saturating_mul(e.into())) + // Minimum execution time: 1_189_000 picoseconds. + Weight::from_parts(1_416_188, 0) } - /// The range of component `r` is `[0, 50]`. + /// The range of component `r` is `[0, 5000]`. fn instr_call(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_163_000 picoseconds. - Weight::from_parts(1_999_458, 0) - // Standard Error: 1_418 - .saturating_add(Weight::from_parts(2_371_979, 0).saturating_mul(r.into())) + // Minimum execution time: 2_201_000 picoseconds. + Weight::from_parts(3_375_851, 0) + // Standard Error: 96 + .saturating_add(Weight::from_parts(22_970, 0).saturating_mul(r.into())) } - /// The range of component `r` is `[0, 50]`. + /// The range of component `r` is `[0, 5000]`. fn instr_call_indirect(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_288_000 picoseconds. - Weight::from_parts(2_340_227, 0) - // Standard Error: 3_234 - .saturating_add(Weight::from_parts(3_038_523, 0).saturating_mul(r.into())) - } - /// The range of component `p` is `[0, 128]`. - fn instr_call_indirect_per_param(p: u32, ) -> Weight { - // Proof Size summary in bytes: - // Measured: `0` - // Estimated: `0` - // Minimum execution time: 5_270_000 picoseconds. - Weight::from_parts(6_360_011, 0) - // Standard Error: 2_585 - .saturating_add(Weight::from_parts(226_149, 0).saturating_mul(p.into())) + // Minimum execution time: 1_286_000 picoseconds. + Weight::from_parts(2_817_725, 0) + // Standard Error: 55 + .saturating_add(Weight::from_parts(29_437, 0).saturating_mul(r.into())) } /// The range of component `l` is `[0, 1024]`. fn instr_call_per_local(l: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 3_580_000 picoseconds. - Weight::from_parts(5_183_658, 0) - // Standard Error: 80 - .saturating_add(Weight::from_parts(46_038, 0).saturating_mul(l.into())) + // Minimum execution time: 1_319_000 picoseconds. + Weight::from_parts(1_636_286, 0) + // Standard Error: 31 + .saturating_add(Weight::from_parts(1_240, 0).saturating_mul(l.into())) } - /// The range of component `r` is `[0, 50]`. + /// The range of component `r` is `[0, 5000]`. fn instr_local_get(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 2_559_000 picoseconds. - Weight::from_parts(2_846_350, 0) - // Standard Error: 2_809 - .saturating_add(Weight::from_parts(462_084, 0).saturating_mul(r.into())) + // Minimum execution time: 2_507_000 picoseconds. + Weight::from_parts(2_785_119, 0) + // Standard Error: 1 + .saturating_add(Weight::from_parts(4_601, 0).saturating_mul(r.into())) } - /// The range of component `r` is `[0, 50]`. + /// The range of component `r` is `[0, 5000]`. fn instr_local_set(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 2_530_000 picoseconds. - Weight::from_parts(2_835_072, 0) - // Standard Error: 479 - .saturating_add(Weight::from_parts(483_451, 0).saturating_mul(r.into())) + // Minimum execution time: 2_494_000 picoseconds. + Weight::from_parts(2_948_015, 0) + // Standard Error: 14 + .saturating_add(Weight::from_parts(4_788, 0).saturating_mul(r.into())) } - /// The range of component `r` is `[0, 50]`. + /// The range of component `r` is `[0, 5000]`. fn instr_local_tee(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 2_510_000 picoseconds. - Weight::from_parts(2_740_081, 0) - // Standard Error: 901 - .saturating_add(Weight::from_parts(664_354, 0).saturating_mul(r.into())) + // Minimum execution time: 2_443_000 picoseconds. + Weight::from_parts(3_065_273, 0) + // Standard Error: 15 + .saturating_add(Weight::from_parts(6_489, 0).saturating_mul(r.into())) } - /// The range of component `r` is `[0, 50]`. + /// The range of component `r` is `[0, 5000]`. fn instr_global_get(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_295_000 picoseconds. - Weight::from_parts(1_798_174, 0) - // Standard Error: 464 - .saturating_add(Weight::from_parts(896_289, 0).saturating_mul(r.into())) + // Minimum execution time: 1_214_000 picoseconds. + Weight::from_parts(1_634_049, 0) + // Standard Error: 3 + .saturating_add(Weight::from_parts(8_960, 0).saturating_mul(r.into())) } - /// The range of component `r` is `[0, 50]`. + /// The range of component `r` is `[0, 5000]`. fn instr_global_set(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_287_000 picoseconds. - Weight::from_parts(1_636_548, 0) - // Standard Error: 658 - .saturating_add(Weight::from_parts(923_295, 0).saturating_mul(r.into())) + // Minimum execution time: 1_180_000 picoseconds. + Weight::from_parts(1_555_599, 0) + // Standard Error: 4 + .saturating_add(Weight::from_parts(9_205, 0).saturating_mul(r.into())) } - /// The range of component `r` is `[0, 50]`. + /// The range of component `r` is `[0, 5000]`. fn instr_memory_current(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_353_000 picoseconds. - Weight::from_parts(1_672_309, 0) - // Standard Error: 403 - .saturating_add(Weight::from_parts(818_273, 0).saturating_mul(r.into())) + // Minimum execution time: 1_223_000 picoseconds. + Weight::from_parts(1_626_002, 0) + // Standard Error: 5 + .saturating_add(Weight::from_parts(8_181, 0).saturating_mul(r.into())) } - /// The range of component `r` is `[0, 1]`. + /// The range of component `r` is `[0, 16]`. fn instr_memory_grow(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_180_000 picoseconds. - Weight::from_parts(1_304_714, 0) - // Standard Error: 88_180 - .saturating_add(Weight::from_parts(183_952_985, 0).saturating_mul(r.into())) + // Minimum execution time: 1_148_000 picoseconds. + Weight::from_parts(307_155, 0) + // Standard Error: 138_541 + .saturating_add(Weight::from_parts(13_291_570, 0).saturating_mul(r.into())) } - /// The range of component `r` is `[0, 50]`. + /// The range of component `r` is `[0, 5000]`. fn instr_i64clz(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_158_000 picoseconds. - Weight::from_parts(1_800_065, 0) - // Standard Error: 2_104 - .saturating_add(Weight::from_parts(625_410, 0).saturating_mul(r.into())) + // Minimum execution time: 1_079_000 picoseconds. + Weight::from_parts(1_372_409, 0) + // Standard Error: 50 + .saturating_add(Weight::from_parts(6_427, 0).saturating_mul(r.into())) } - /// The range of component `r` is `[0, 50]`. + /// The range of component `r` is `[0, 5000]`. fn instr_i64ctz(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_208_000 picoseconds. - Weight::from_parts(1_483_908, 0) - // Standard Error: 319 - .saturating_add(Weight::from_parts(635_095, 0).saturating_mul(r.into())) + // Minimum execution time: 1_116_000 picoseconds. + Weight::from_parts(1_415_180, 0) + // Standard Error: 2 + .saturating_add(Weight::from_parts(6_353, 0).saturating_mul(r.into())) } - /// The range of component `r` is `[0, 50]`. + /// The range of component `r` is `[0, 5000]`. fn instr_i64popcnt(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_164_000 picoseconds. - Weight::from_parts(1_506_871, 0) - // Standard Error: 264 - .saturating_add(Weight::from_parts(633_660, 0).saturating_mul(r.into())) + // Minimum execution time: 1_058_000 picoseconds. + Weight::from_parts(1_314_206, 0) + // Standard Error: 35 + .saturating_add(Weight::from_parts(6_431, 0).saturating_mul(r.into())) } - /// The range of component `r` is `[0, 50]`. + /// The range of component `r` is `[0, 5000]`. fn instr_i64eqz(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_141_000 picoseconds. - Weight::from_parts(1_467_219, 0) - // Standard Error: 191 - .saturating_add(Weight::from_parts(651_156, 0).saturating_mul(r.into())) + // Minimum execution time: 1_059_000 picoseconds. + Weight::from_parts(1_455_331, 0) + // Standard Error: 2 + .saturating_add(Weight::from_parts(6_507, 0).saturating_mul(r.into())) } - /// The range of component `r` is `[0, 50]`. + /// The range of component `r` is `[0, 5000]`. fn instr_i64extendsi32(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_170_000 picoseconds. - Weight::from_parts(1_474_718, 0) - // Standard Error: 224 - .saturating_add(Weight::from_parts(617_317, 0).saturating_mul(r.into())) + // Minimum execution time: 1_057_000 picoseconds. + Weight::from_parts(1_421_085, 0) + // Standard Error: 2 + .saturating_add(Weight::from_parts(6_177, 0).saturating_mul(r.into())) } - /// The range of component `r` is `[0, 50]`. + /// The range of component `r` is `[0, 5000]`. fn instr_i64extendui32(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_195_000 picoseconds. - Weight::from_parts(1_501_421, 0) - // Standard Error: 221 - .saturating_add(Weight::from_parts(616_559, 0).saturating_mul(r.into())) + // Minimum execution time: 1_079_000 picoseconds. + Weight::from_parts(1_429_275, 0) + // Standard Error: 2 + .saturating_add(Weight::from_parts(6_175, 0).saturating_mul(r.into())) } - /// The range of component `r` is `[0, 50]`. + /// The range of component `r` is `[0, 5000]`. fn instr_i32wrapi64(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_172_000 picoseconds. - Weight::from_parts(1_482_402, 0) - // Standard Error: 2_163 - .saturating_add(Weight::from_parts(619_230, 0).saturating_mul(r.into())) + // Minimum execution time: 1_065_000 picoseconds. + Weight::from_parts(1_403_813, 0) + // Standard Error: 3 + .saturating_add(Weight::from_parts(6_192, 0).saturating_mul(r.into())) } - /// The range of component `r` is `[0, 50]`. + /// The range of component `r` is `[0, 5000]`. fn instr_i64eq(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_149_000 picoseconds. - Weight::from_parts(2_021_801, 0) - // Standard Error: 20_804 - .saturating_add(Weight::from_parts(913_888, 0).saturating_mul(r.into())) + // Minimum execution time: 1_061_000 picoseconds. + Weight::from_parts(1_421_984, 0) + // Standard Error: 4 + .saturating_add(Weight::from_parts(9_094, 0).saturating_mul(r.into())) } - /// The range of component `r` is `[0, 50]`. + /// The range of component `r` is `[0, 5000]`. fn instr_i64ne(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_187_000 picoseconds. - Weight::from_parts(1_358_437, 0) - // Standard Error: 1_946 - .saturating_add(Weight::from_parts(916_378, 0).saturating_mul(r.into())) + // Minimum execution time: 1_075_000 picoseconds. + Weight::from_parts(1_431_453, 0) + // Standard Error: 2 + .saturating_add(Weight::from_parts(9_084, 0).saturating_mul(r.into())) } - /// The range of component `r` is `[0, 50]`. + /// The range of component `r` is `[0, 5000]`. fn instr_i64lts(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_175_000 picoseconds. - Weight::from_parts(1_527_336, 0) - // Standard Error: 243 - .saturating_add(Weight::from_parts(907_580, 0).saturating_mul(r.into())) + // Minimum execution time: 1_100_000 picoseconds. + Weight::from_parts(1_452_384, 0) + // Standard Error: 3 + .saturating_add(Weight::from_parts(9_081, 0).saturating_mul(r.into())) } - /// The range of component `r` is `[0, 50]`. + /// The range of component `r` is `[0, 5000]`. fn instr_i64ltu(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_220_000 picoseconds. - Weight::from_parts(1_530_715, 0) - // Standard Error: 224 - .saturating_add(Weight::from_parts(906_096, 0).saturating_mul(r.into())) + // Minimum execution time: 1_090_000 picoseconds. + Weight::from_parts(1_466_416, 0) + // Standard Error: 17 + .saturating_add(Weight::from_parts(9_091, 0).saturating_mul(r.into())) } - /// The range of component `r` is `[0, 50]`. + /// The range of component `r` is `[0, 5000]`. fn instr_i64gts(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_107_000 picoseconds. - Weight::from_parts(1_496_553, 0) - // Standard Error: 299 - .saturating_add(Weight::from_parts(907_963, 0).saturating_mul(r.into())) + // Minimum execution time: 1_056_000 picoseconds. + Weight::from_parts(1_676_091, 0) + // Standard Error: 47 + .saturating_add(Weight::from_parts(9_082, 0).saturating_mul(r.into())) } - /// The range of component `r` is `[0, 50]`. + /// The range of component `r` is `[0, 5000]`. fn instr_i64gtu(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_153_000 picoseconds. - Weight::from_parts(1_482_710, 0) - // Standard Error: 182 - .saturating_add(Weight::from_parts(918_439, 0).saturating_mul(r.into())) + // Minimum execution time: 1_053_000 picoseconds. + Weight::from_parts(1_232_790, 0) + // Standard Error: 43 + .saturating_add(Weight::from_parts(9_329, 0).saturating_mul(r.into())) } - /// The range of component `r` is `[0, 50]`. + /// The range of component `r` is `[0, 5000]`. fn instr_i64les(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_142_000 picoseconds. - Weight::from_parts(1_522_114, 0) - // Standard Error: 297 - .saturating_add(Weight::from_parts(906_633, 0).saturating_mul(r.into())) + // Minimum execution time: 1_091_000 picoseconds. + Weight::from_parts(1_476_212, 0) + // Standard Error: 4 + .saturating_add(Weight::from_parts(9_074, 0).saturating_mul(r.into())) } - /// The range of component `r` is `[0, 50]`. + /// The range of component `r` is `[0, 5000]`. fn instr_i64leu(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_119_000 picoseconds. - Weight::from_parts(2_199_831, 0) - // Standard Error: 4_234 - .saturating_add(Weight::from_parts(898_161, 0).saturating_mul(r.into())) + // Minimum execution time: 1_083_000 picoseconds. + Weight::from_parts(1_484_966, 0) + // Standard Error: 3 + .saturating_add(Weight::from_parts(9_184, 0).saturating_mul(r.into())) } - /// The range of component `r` is `[0, 50]`. + /// The range of component `r` is `[0, 5000]`. fn instr_i64ges(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_163_000 picoseconds. - Weight::from_parts(1_489_404, 0) - // Standard Error: 416 - .saturating_add(Weight::from_parts(908_450, 0).saturating_mul(r.into())) + // Minimum execution time: 1_089_000 picoseconds. + Weight::from_parts(1_516_766, 0) + // Standard Error: 4 + .saturating_add(Weight::from_parts(9_063, 0).saturating_mul(r.into())) } - /// The range of component `r` is `[0, 50]`. + /// The range of component `r` is `[0, 5000]`. fn instr_i64geu(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_204_000 picoseconds. - Weight::from_parts(2_266_296, 0) - // Standard Error: 13_013 - .saturating_add(Weight::from_parts(899_225, 0).saturating_mul(r.into())) + // Minimum execution time: 996_000 picoseconds. + Weight::from_parts(1_460_638, 0) + // Standard Error: 2 + .saturating_add(Weight::from_parts(9_076, 0).saturating_mul(r.into())) } - /// The range of component `r` is `[0, 50]`. + /// The range of component `r` is `[0, 5000]`. fn instr_i64add(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_160_000 picoseconds. - Weight::from_parts(1_470_799, 0) - // Standard Error: 422 - .saturating_add(Weight::from_parts(897_325, 0).saturating_mul(r.into())) + // Minimum execution time: 1_095_000 picoseconds. + Weight::from_parts(1_448_412, 0) + // Standard Error: 3 + .saturating_add(Weight::from_parts(8_977, 0).saturating_mul(r.into())) } - /// The range of component `r` is `[0, 50]`. + /// The range of component `r` is `[0, 5000]`. fn instr_i64sub(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_125_000 picoseconds. - Weight::from_parts(1_521_508, 0) - // Standard Error: 594 - .saturating_add(Weight::from_parts(886_145, 0).saturating_mul(r.into())) + // Minimum execution time: 1_071_000 picoseconds. + Weight::from_parts(1_459_165, 0) + // Standard Error: 2 + .saturating_add(Weight::from_parts(8_852, 0).saturating_mul(r.into())) } - /// The range of component `r` is `[0, 50]`. + /// The range of component `r` is `[0, 5000]`. fn instr_i64mul(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_139_000 picoseconds. - Weight::from_parts(1_490_535, 0) - // Standard Error: 211 - .saturating_add(Weight::from_parts(884_929, 0).saturating_mul(r.into())) + // Minimum execution time: 1_082_000 picoseconds. + Weight::from_parts(1_450_718, 0) + // Standard Error: 3 + .saturating_add(Weight::from_parts(8_860, 0).saturating_mul(r.into())) } - /// The range of component `r` is `[0, 50]`. + /// The range of component `r` is `[0, 5000]`. fn instr_i64divs(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_180_000 picoseconds. - Weight::from_parts(2_221_481, 0) - // Standard Error: 9_884 - .saturating_add(Weight::from_parts(1_507_511, 0).saturating_mul(r.into())) + // Minimum execution time: 1_065_000 picoseconds. + Weight::from_parts(1_436_871, 0) + // Standard Error: 9 + .saturating_add(Weight::from_parts(15_241, 0).saturating_mul(r.into())) } - /// The range of component `r` is `[0, 50]`. + /// The range of component `r` is `[0, 5000]`. fn instr_i64divu(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_131_000 picoseconds. - Weight::from_parts(1_498_652, 0) - // Standard Error: 531 - .saturating_add(Weight::from_parts(1_451_498, 0).saturating_mul(r.into())) + // Minimum execution time: 1_065_000 picoseconds. + Weight::from_parts(1_423_806, 0) + // Standard Error: 17 + .saturating_add(Weight::from_parts(14_706, 0).saturating_mul(r.into())) } - /// The range of component `r` is `[0, 50]`. + /// The range of component `r` is `[0, 5000]`. fn instr_i64rems(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_145_000 picoseconds. - Weight::from_parts(1_504_118, 0) - // Standard Error: 439 - .saturating_add(Weight::from_parts(1_521_895, 0).saturating_mul(r.into())) + // Minimum execution time: 1_059_000 picoseconds. + Weight::from_parts(1_735_816, 0) + // Standard Error: 66 + .saturating_add(Weight::from_parts(15_230, 0).saturating_mul(r.into())) } - /// The range of component `r` is `[0, 50]`. + /// The range of component `r` is `[0, 5000]`. fn instr_i64remu(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_124_000 picoseconds. - Weight::from_parts(1_497_209, 0) - // Standard Error: 198 - .saturating_add(Weight::from_parts(1_449_843, 0).saturating_mul(r.into())) + // Minimum execution time: 1_076_000 picoseconds. + Weight::from_parts(1_451_290, 0) + // Standard Error: 6 + .saturating_add(Weight::from_parts(14_530, 0).saturating_mul(r.into())) } - /// The range of component `r` is `[0, 50]`. + /// The range of component `r` is `[0, 5000]`. fn instr_i64and(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_169_000 picoseconds. - Weight::from_parts(1_534_491, 0) - // Standard Error: 283 - .saturating_add(Weight::from_parts(894_703, 0).saturating_mul(r.into())) + // Minimum execution time: 1_079_000 picoseconds. + Weight::from_parts(1_457_537, 0) + // Standard Error: 3 + .saturating_add(Weight::from_parts(8_963, 0).saturating_mul(r.into())) } - /// The range of component `r` is `[0, 50]`. + /// The range of component `r` is `[0, 5000]`. fn instr_i64or(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_178_000 picoseconds. - Weight::from_parts(1_485_473, 0) - // Standard Error: 552 - .saturating_add(Weight::from_parts(897_878, 0).saturating_mul(r.into())) + // Minimum execution time: 1_032_000 picoseconds. + Weight::from_parts(1_475_315, 0) + // Standard Error: 4 + .saturating_add(Weight::from_parts(8_956, 0).saturating_mul(r.into())) } - /// The range of component `r` is `[0, 50]`. + /// The range of component `r` is `[0, 5000]`. fn instr_i64xor(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_214_000 picoseconds. - Weight::from_parts(1_580_999, 0) - // Standard Error: 358 - .saturating_add(Weight::from_parts(894_207, 0).saturating_mul(r.into())) + // Minimum execution time: 1_056_000 picoseconds. + Weight::from_parts(1_450_071, 0) + // Standard Error: 2 + .saturating_add(Weight::from_parts(8_960, 0).saturating_mul(r.into())) } - /// The range of component `r` is `[0, 50]`. + /// The range of component `r` is `[0, 5000]`. fn instr_i64shl(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_176_000 picoseconds. - Weight::from_parts(1_490_470, 0) - // Standard Error: 738 - .saturating_add(Weight::from_parts(904_900, 0).saturating_mul(r.into())) + // Minimum execution time: 1_116_000 picoseconds. + Weight::from_parts(1_429_705, 0) + // Standard Error: 2 + .saturating_add(Weight::from_parts(9_027, 0).saturating_mul(r.into())) } - /// The range of component `r` is `[0, 50]`. + /// The range of component `r` is `[0, 5000]`. fn instr_i64shrs(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_197_000 picoseconds. - Weight::from_parts(1_519_703, 0) - // Standard Error: 336 - .saturating_add(Weight::from_parts(901_277, 0).saturating_mul(r.into())) + // Minimum execution time: 1_059_000 picoseconds. + Weight::from_parts(1_429_307, 0) + // Standard Error: 4 + .saturating_add(Weight::from_parts(9_048, 0).saturating_mul(r.into())) } - /// The range of component `r` is `[0, 50]`. + /// The range of component `r` is `[0, 5000]`. fn instr_i64shru(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_166_000 picoseconds. - Weight::from_parts(1_507_499, 0) - // Standard Error: 298 - .saturating_add(Weight::from_parts(901_153, 0).saturating_mul(r.into())) + // Minimum execution time: 1_093_000 picoseconds. + Weight::from_parts(1_411_827, 0) + // Standard Error: 4 + .saturating_add(Weight::from_parts(9_528, 0).saturating_mul(r.into())) } - /// The range of component `r` is `[0, 50]`. + /// The range of component `r` is `[0, 5000]`. fn instr_i64rotl(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_158_000 picoseconds. - Weight::from_parts(1_491_176, 0) - // Standard Error: 341 - .saturating_add(Weight::from_parts(901_880, 0).saturating_mul(r.into())) + // Minimum execution time: 1_115_000 picoseconds. + Weight::from_parts(1_441_025, 0) + // Standard Error: 4 + .saturating_add(Weight::from_parts(9_043, 0).saturating_mul(r.into())) } - /// The range of component `r` is `[0, 50]`. + /// The range of component `r` is `[0, 5000]`. fn instr_i64rotr(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_207_000 picoseconds. - Weight::from_parts(1_477_261, 0) - // Standard Error: 287 - .saturating_add(Weight::from_parts(902_622, 0).saturating_mul(r.into())) + // Minimum execution time: 1_077_000 picoseconds. + Weight::from_parts(1_480_666, 0) + // Standard Error: 4 + .saturating_add(Weight::from_parts(9_031, 0).saturating_mul(r.into())) } } From a2d4f7dfa97c1496f964c6fe833d5128f8319c77 Mon Sep 17 00:00:00 2001 From: Davide Galassi Date: Fri, 17 Mar 2023 12:24:14 +0100 Subject: [PATCH 249/558] Keystore overhaul (#13615) * Remove 'supported_keys' 'sign_with_any' and 'sign_with_all' from keystore trait * Remove the aync keystore * Renaming: - SyncCryptoStore -> Keystore - SyncCryptoStorePtr -> KeystorePtr - KeyStore -> MemoryKeystore * Fix authority discovery worker and tests * Rename 'insert_unknown' to 'insert' * Remove leftover --- Cargo.lock | 1 - bin/node-template/node/src/service.rs | 9 +- bin/node/cli/src/service.rs | 17 +- bin/node/executor/tests/submit_transaction.rs | 22 +- bin/node/rpc/src/lib.rs | 4 +- bin/utils/chain-spec-builder/src/main.rs | 6 +- client/api/src/execution_extensions.rs | 6 +- client/authority-discovery/src/tests.rs | 5 +- client/authority-discovery/src/worker.rs | 38 ++- .../authority-discovery/src/worker/tests.rs | 86 +++--- client/cli/src/commands/insert_key.rs | 8 +- client/cli/src/error.rs | 2 +- client/consensus/aura/src/lib.rs | 18 +- client/consensus/babe/rpc/src/lib.rs | 17 +- client/consensus/babe/src/authorship.rs | 20 +- client/consensus/babe/src/lib.rs | 8 +- client/consensus/babe/src/tests.rs | 17 +- .../beefy/src/communication/gossip.rs | 6 +- client/consensus/beefy/src/keystore.rs | 33 ++- client/consensus/beefy/src/lib.rs | 4 +- client/consensus/beefy/src/tests.rs | 8 +- .../grandpa/src/communication/mod.rs | 10 +- client/consensus/grandpa/src/lib.rs | 12 +- client/consensus/grandpa/src/observer.rs | 6 +- client/consensus/grandpa/src/tests.rs | 12 +- .../manual-seal/src/consensus/babe.rs | 6 +- client/keystore/src/local.rs | 158 ++--------- client/rpc-api/src/author/error.rs | 2 +- client/rpc/src/author/mod.rs | 14 +- client/rpc/src/author/tests.rs | 12 +- client/service/src/builder.rs | 39 +-- client/service/src/client/client.rs | 6 +- frame/benchmarking/src/baseline.rs | 4 +- frame/contracts/src/tests.rs | 4 +- frame/examples/offchain-worker/src/tests.rs | 20 +- frame/nfts/src/mock.rs | 4 +- .../application-crypto/test/src/ecdsa.rs | 6 +- .../application-crypto/test/src/ed25519.rs | 6 +- .../application-crypto/test/src/sr25519.rs | 6 +- primitives/consensus/beefy/src/commitment.rs | 12 +- primitives/consensus/beefy/src/witness.rs | 12 +- primitives/consensus/grandpa/src/lib.rs | 6 +- primitives/io/src/lib.rs | 24 +- primitives/keystore/Cargo.toml | 1 - primitives/keystore/src/lib.rs | 250 +----------------- primitives/keystore/src/testing.rs | 156 ++--------- test-utils/client/src/lib.rs | 6 +- .../benchmarking-cli/src/pallet/command.rs | 4 +- utils/frame/try-runtime/cli/src/lib.rs | 4 +- 49 files changed, 317 insertions(+), 820 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index d0aea7729..9121a8591 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -10339,7 +10339,6 @@ dependencies = [ name = "sp-keystore" version = "0.13.0" dependencies = [ - "async-trait", "futures", "merlin", "parity-scale-codec", diff --git a/bin/node-template/node/src/service.rs b/bin/node-template/node/src/service.rs index 34e4e566d..40ad80c7b 100644 --- a/bin/node-template/node/src/service.rs +++ b/bin/node-template/node/src/service.rs @@ -150,7 +150,7 @@ pub fn new_partial( fn remote_keystore(_url: &String) -> Result, &'static str> { // FIXME: here would the concrete keystore be built, // must return a concrete type (NOT `LocalKeystore`) that - // implements `CryptoStore` and `SyncCryptoStore` + // implements `Keystore` Err("Remote Keystore not supported.") } @@ -233,7 +233,7 @@ pub fn new_full(mut config: Configuration) -> Result let _rpc_handlers = sc_service::spawn_tasks(sc_service::SpawnTasksParams { network: network.clone(), client: client.clone(), - keystore: keystore_container.sync_keystore(), + keystore: keystore_container.keystore(), task_manager: &mut task_manager, transaction_pool: transaction_pool.clone(), rpc_builder: rpc_extensions_builder, @@ -276,7 +276,7 @@ pub fn new_full(mut config: Configuration) -> Result }, force_authoring, backoff_authoring_blocks, - keystore: keystore_container.sync_keystore(), + keystore: keystore_container.keystore(), sync_oracle: sync_service.clone(), justification_sync_link: sync_service.clone(), block_proposal_slot_portion: SlotProportion::new(2f32 / 3f32), @@ -296,8 +296,7 @@ pub fn new_full(mut config: Configuration) -> Result if enable_grandpa { // if the node isn't actively participating in consensus then it doesn't // need a keystore, regardless of which protocol we use below. - let keystore = - if role.is_authority() { Some(keystore_container.sync_keystore()) } else { None }; + let keystore = if role.is_authority() { Some(keystore_container.keystore()) } else { None }; let grandpa_config = sc_consensus_grandpa::Config { // FIXME #1578 make this available through chainspec diff --git a/bin/node/cli/src/service.rs b/bin/node/cli/src/service.rs index 3c9716c08..a2d66cd63 100644 --- a/bin/node/cli/src/service.rs +++ b/bin/node/cli/src/service.rs @@ -251,7 +251,7 @@ pub fn new_partial( let client = client.clone(); let pool = transaction_pool.clone(); let select_chain = select_chain.clone(); - let keystore = keystore_container.sync_keystore(); + let keystore = keystore_container.keystore(); let chain_spec = config.chain_spec.cloned_box(); let rpc_backend = backend.clone(); @@ -386,7 +386,7 @@ pub fn new_full_base( config, backend, client: client.clone(), - keystore: keystore_container.sync_keystore(), + keystore: keystore_container.keystore(), network: network.clone(), rpc_builder: Box::new(rpc_builder), transaction_pool: transaction_pool.clone(), @@ -431,7 +431,7 @@ pub fn new_full_base( let client_clone = client.clone(); let slot_duration = babe_link.config().slot_duration(); let babe_config = sc_consensus_babe::BabeParams { - keystore: keystore_container.sync_keystore(), + keystore: keystore_container.keystore(), client: client.clone(), select_chain, env: proposer, @@ -507,8 +507,7 @@ pub fn new_full_base( // if the node isn't actively participating in consensus then it doesn't // need a keystore, regardless of which protocol we use below. - let keystore = - if role.is_authority() { Some(keystore_container.sync_keystore()) } else { None }; + let keystore = if role.is_authority() { Some(keystore_container.keystore()) } else { None }; let config = grandpa::Config { // FIXME #1578 make this available through chainspec @@ -596,7 +595,7 @@ mod tests { use sp_core::{crypto::Pair as CryptoPair, Public}; use sp_inherents::InherentDataProvider; use sp_keyring::AccountKeyring; - use sp_keystore::{SyncCryptoStore, SyncCryptoStorePtr}; + use sp_keystore::{Keystore, KeystorePtr}; use sp_runtime::{ generic::{Digest, Era, SignedPayload}, key_types::BABE, @@ -616,10 +615,10 @@ mod tests { sp_tracing::try_init_simple(); let keystore_path = tempfile::tempdir().expect("Creates keystore path"); - let keystore: SyncCryptoStorePtr = + let keystore: KeystorePtr = Arc::new(LocalKeystore::open(keystore_path.path(), None).expect("Creates keystore")); let alice: sp_consensus_babe::AuthorityId = - SyncCryptoStore::sr25519_generate_new(&*keystore, BABE, Some("//Alice")) + Keystore::sr25519_generate_new(&*keystore, BABE, Some("//Alice")) .expect("Creates authority pair") .into(); @@ -736,7 +735,7 @@ mod tests { // sign the pre-sealed hash of the block and then // add it to a digest item. let to_sign = pre_hash.encode(); - let signature = SyncCryptoStore::sign_with( + let signature = Keystore::sign_with( &*keystore, sp_consensus_babe::AuthorityId::ID, &alice.to_public_crypto_pair(), diff --git a/bin/node/executor/tests/submit_transaction.rs b/bin/node/executor/tests/submit_transaction.rs index ab5df62a2..32eacd78b 100644 --- a/bin/node/executor/tests/submit_transaction.rs +++ b/bin/node/executor/tests/submit_transaction.rs @@ -21,7 +21,7 @@ use kitchensink_runtime::{Executive, Indices, Runtime, UncheckedExtrinsic}; use sp_application_crypto::AppKey; use sp_core::offchain::{testing::TestTransactionPoolExt, TransactionPoolExt}; use sp_keyring::sr25519::Keyring::Alice; -use sp_keystore::{testing::KeyStore, KeystoreExt, SyncCryptoStore}; +use sp_keystore::{testing::MemoryKeystore, Keystore, KeystoreExt}; use std::sync::Arc; pub mod common; @@ -62,20 +62,20 @@ fn should_submit_signed_transaction() { let (pool, state) = TestTransactionPoolExt::new(); t.register_extension(TransactionPoolExt::new(pool)); - let keystore = KeyStore::new(); - SyncCryptoStore::sr25519_generate_new( + let keystore = MemoryKeystore::new(); + Keystore::sr25519_generate_new( &keystore, sr25519::AuthorityId::ID, Some(&format!("{}/hunter1", PHRASE)), ) .unwrap(); - SyncCryptoStore::sr25519_generate_new( + Keystore::sr25519_generate_new( &keystore, sr25519::AuthorityId::ID, Some(&format!("{}/hunter2", PHRASE)), ) .unwrap(); - SyncCryptoStore::sr25519_generate_new( + Keystore::sr25519_generate_new( &keystore, sr25519::AuthorityId::ID, Some(&format!("{}/hunter3", PHRASE)), @@ -105,14 +105,14 @@ fn should_submit_signed_twice_from_the_same_account() { let (pool, state) = TestTransactionPoolExt::new(); t.register_extension(TransactionPoolExt::new(pool)); - let keystore = KeyStore::new(); - SyncCryptoStore::sr25519_generate_new( + let keystore = MemoryKeystore::new(); + Keystore::sr25519_generate_new( &keystore, sr25519::AuthorityId::ID, Some(&format!("{}/hunter1", PHRASE)), ) .unwrap(); - SyncCryptoStore::sr25519_generate_new( + Keystore::sr25519_generate_new( &keystore, sr25519::AuthorityId::ID, Some(&format!("{}/hunter2", PHRASE)), @@ -162,7 +162,7 @@ fn should_submit_signed_twice_from_all_accounts() { let (pool, state) = TestTransactionPoolExt::new(); t.register_extension(TransactionPoolExt::new(pool)); - let keystore = KeyStore::new(); + let keystore = MemoryKeystore::new(); keystore .sr25519_generate_new(sr25519::AuthorityId::ID, Some(&format!("{}/hunter1", PHRASE))) .unwrap(); @@ -226,8 +226,8 @@ fn submitted_transaction_should_be_valid() { let (pool, state) = TestTransactionPoolExt::new(); t.register_extension(TransactionPoolExt::new(pool)); - let keystore = KeyStore::new(); - SyncCryptoStore::sr25519_generate_new( + let keystore = MemoryKeystore::new(); + Keystore::sr25519_generate_new( &keystore, sr25519::AuthorityId::ID, Some(&format!("{}/hunter1", PHRASE)), diff --git a/bin/node/rpc/src/lib.rs b/bin/node/rpc/src/lib.rs index 7c2f7c15e..9dcdf0f21 100644 --- a/bin/node/rpc/src/lib.rs +++ b/bin/node/rpc/src/lib.rs @@ -49,7 +49,7 @@ use sp_block_builder::BlockBuilder; use sp_blockchain::{Error as BlockChainError, HeaderBackend, HeaderMetadata}; use sp_consensus::SelectChain; use sp_consensus_babe::BabeApi; -use sp_keystore::SyncCryptoStorePtr; +use sp_keystore::KeystorePtr; /// Extra dependencies for BABE. pub struct BabeDeps { @@ -58,7 +58,7 @@ pub struct BabeDeps { /// BABE pending epoch changes. pub shared_epoch_changes: SharedEpochChanges, /// The keystore that manages the keys of the node. - pub keystore: SyncCryptoStorePtr, + pub keystore: KeystorePtr, } /// Extra dependencies for GRANDPA diff --git a/bin/utils/chain-spec-builder/src/main.rs b/bin/utils/chain-spec-builder/src/main.rs index b3f28b269..f7b7d94c5 100644 --- a/bin/utils/chain-spec-builder/src/main.rs +++ b/bin/utils/chain-spec-builder/src/main.rs @@ -32,7 +32,7 @@ use sp_core::{ crypto::{ByteArray, Ss58Codec}, sr25519, }; -use sp_keystore::{SyncCryptoStore, SyncCryptoStorePtr}; +use sp_keystore::{Keystore, KeystorePtr}; /// A utility to easily create a testnet chain spec definition with a given set /// of authorities and endowed accounts and/or generate random accounts. @@ -164,7 +164,7 @@ fn generate_chain_spec( fn generate_authority_keys_and_store(seeds: &[String], keystore_path: &Path) -> Result<(), String> { for (n, seed) in seeds.iter().enumerate() { - let keystore: SyncCryptoStorePtr = Arc::new( + let keystore: KeystorePtr = Arc::new( LocalKeystore::open(keystore_path.join(format!("auth-{}", n)), None) .map_err(|err| err.to_string())?, ); @@ -173,7 +173,7 @@ fn generate_authority_keys_and_store(seeds: &[String], keystore_path: &Path) -> chain_spec::authority_keys_from_seed(seed); let insert_key = |key_type, public| { - SyncCryptoStore::insert_unknown(&*keystore, key_type, &format!("//{}", seed), public) + Keystore::insert(&*keystore, key_type, &format!("//{}", seed), public) .map_err(|_| format!("Failed to insert key: {}", grandpa)) }; diff --git a/client/api/src/execution_extensions.rs b/client/api/src/execution_extensions.rs index b491d7672..ffa670f7b 100644 --- a/client/api/src/execution_extensions.rs +++ b/client/api/src/execution_extensions.rs @@ -30,7 +30,7 @@ use sp_core::{ ExecutionContext, }; use sp_externalities::{Extension, Extensions}; -use sp_keystore::{KeystoreExt, SyncCryptoStorePtr}; +use sp_keystore::{KeystoreExt, KeystorePtr}; use sp_runtime::{ generic::BlockId, traits::{Block as BlockT, NumberFor}, @@ -163,7 +163,7 @@ impl DbExternaliti /// for each call, based on required `Capabilities`. pub struct ExecutionExtensions { strategies: ExecutionStrategies, - keystore: Option, + keystore: Option, offchain_db: Option>, // FIXME: these two are only RwLock because of https://github.com/paritytech/substrate/issues/4587 // remove when fixed. @@ -191,7 +191,7 @@ impl ExecutionExtensions { /// Create new `ExecutionExtensions` given a `keystore` and `ExecutionStrategies`. pub fn new( strategies: ExecutionStrategies, - keystore: Option, + keystore: Option, offchain_db: Option>, ) -> Self { let transaction_pool = RwLock::new(None); diff --git a/client/authority-discovery/src/tests.rs b/client/authority-discovery/src/tests.rs index 982c3fc04..d354f6a96 100644 --- a/client/authority-discovery/src/tests.rs +++ b/client/authority-discovery/src/tests.rs @@ -33,7 +33,7 @@ use std::{collections::HashSet, sync::Arc}; use sp_authority_discovery::AuthorityId; use sp_core::crypto::key_types; -use sp_keystore::{testing::KeyStore, CryptoStore}; +use sp_keystore::{testing::MemoryKeystore, Keystore}; #[test] fn get_addresses_and_authority_id() { @@ -42,12 +42,11 @@ fn get_addresses_and_authority_id() { let mut pool = LocalPool::new(); - let key_store = KeyStore::new(); + let key_store = MemoryKeystore::new(); let remote_authority_id: AuthorityId = pool.run_until(async { key_store .sr25519_generate_new(key_types::AUTHORITY_DISCOVERY, None) - .await .unwrap() .into() }); diff --git a/client/authority-discovery/src/worker.rs b/client/authority-discovery/src/worker.rs index 034d72902..5d7d21a5b 100644 --- a/client/authority-discovery/src/worker.rs +++ b/client/authority-discovery/src/worker.rs @@ -53,7 +53,7 @@ use sp_authority_discovery::{ use sp_blockchain::HeaderBackend; use sp_core::crypto::{key_types, CryptoTypePublicPair, Pair}; -use sp_keystore::CryptoStore; +use sp_keystore::{Keystore, KeystorePtr}; use sp_runtime::traits::Block as BlockT; mod addr_cache; @@ -78,7 +78,7 @@ const MAX_IN_FLIGHT_LOOKUPS: usize = 8; /// Role an authority discovery [`Worker`] can run as. pub enum Role { /// Publish own addresses and discover addresses of others. - PublishAndDiscover(Arc), + PublishAndDiscover(KeystorePtr), /// Discover addresses of others. Discover, } @@ -364,8 +364,7 @@ where Some(peer_signature), key_store.as_ref(), keys_vec, - ) - .await?; + )?; for (key, value) in kv_pairs.into_iter() { self.network.put_value(key, value); @@ -382,7 +381,6 @@ where let local_keys = match &self.role { Role::PublishAndDiscover(key_store) => key_store .sr25519_public_keys(key_types::AUTHORITY_DISCOVERY) - .await .into_iter() .collect::>(), Role::Discover => HashSet::new(), @@ -588,12 +586,11 @@ where // next authority set with two keys. The function does not return all of the local authority // discovery public keys, but only the ones intersecting with the current or next authority set. async fn get_own_public_keys_within_authority_set( - key_store: Arc, + key_store: KeystorePtr, client: &Client, ) -> Result> { let local_pub_keys = key_store .sr25519_public_keys(key_types::AUTHORITY_DISCOVERY) - .await .into_iter() .collect::>(); @@ -663,33 +660,28 @@ fn sign_record_with_peer_id( Ok(schema::PeerSignature { signature, public_key }) } -async fn sign_record_with_authority_ids( +fn sign_record_with_authority_ids( serialized_record: Vec, peer_signature: Option, - key_store: &dyn CryptoStore, + key_store: &dyn Keystore, keys: Vec, ) -> Result)>> { - let signatures = key_store - .sign_with_all(key_types::AUTHORITY_DISCOVERY, keys.clone(), &serialized_record) - .await - .map_err(|_| Error::Signing)?; + let mut result = Vec::with_capacity(keys.len()); - let mut result = vec![]; - for (sign_result, key) in signatures.into_iter().zip(keys.iter()) { - let mut signed_record = vec![]; + for key in keys.iter() { + let auth_signature = key_store + .sign_with(key_types::AUTHORITY_DISCOVERY, key, &serialized_record) + .map_err(|_| Error::Signing)? + .ok_or_else(|| Error::MissingSignature(key.clone()))?; - // Verify that all signatures exist for all provided keys. - let auth_signature = - sign_result.ok().flatten().ok_or_else(|| Error::MissingSignature(key.clone()))?; - schema::SignedAuthorityRecord { + let signed_record = schema::SignedAuthorityRecord { record: serialized_record.clone(), auth_signature, peer_signature: peer_signature.clone(), } - .encode(&mut signed_record) - .map_err(Error::EncodingProto)?; + .encode_to_vec(); - result.push((hash_authority_id(key.1.as_ref()), signed_record)); + result.push((hash_authority_id(&key.1), signed_record)); } Ok(result) diff --git a/client/authority-discovery/src/worker/tests.rs b/client/authority-discovery/src/worker/tests.rs index febe40657..c918d7993 100644 --- a/client/authority-discovery/src/worker/tests.rs +++ b/client/authority-discovery/src/worker/tests.rs @@ -40,7 +40,7 @@ use prometheus_endpoint::prometheus::default_registry; use sc_client_api::HeaderBackend; use sc_network::Signature; use sp_api::{ApiRef, ProvideRuntimeApi}; -use sp_keystore::{testing::KeyStore, CryptoStore}; +use sp_keystore::{testing::MemoryKeystore, Keystore}; use sp_runtime::traits::{Block as BlockT, NumberFor, Zero}; use substrate_test_runtime_client::runtime::Block; @@ -208,10 +208,10 @@ impl<'a> NetworkSigner for TestSigner<'a> { } } -async fn build_dht_event( +fn build_dht_event( addresses: Vec, public_key: AuthorityId, - key_store: &dyn CryptoStore, + key_store: &MemoryKeystore, network: Option<&Signer>, ) -> Vec<(KademliaKey, Vec)> { let serialized_record = @@ -224,7 +224,6 @@ async fn build_dht_event( key_store, vec![public_key.into()], ) - .await .unwrap(); // There is always a single item in it, because we signed it with a single key kv_pairs @@ -234,7 +233,7 @@ async fn build_dht_event( fn new_registers_metrics() { let (_dht_event_tx, dht_event_rx) = mpsc::channel(1000); let network: Arc = Arc::new(Default::default()); - let key_store = KeyStore::new(); + let key_store = MemoryKeystore::new(); let test_api = Arc::new(TestApi { authorities: vec![] }); let registry = prometheus_endpoint::Registry::new(); @@ -266,7 +265,7 @@ fn triggers_dht_get_query() { let test_api = Arc::new(TestApi { authorities: authorities.clone() }); let network = Arc::new(TestNetwork::default()); - let key_store = KeyStore::new(); + let key_store = MemoryKeystore::new(); let (_to_worker, from_service) = mpsc::channel(0); let mut worker = Worker::new( @@ -298,14 +297,12 @@ fn publish_discover_cycle() { let network: Arc = Arc::new(Default::default()); - let key_store = KeyStore::new(); + let key_store = MemoryKeystore::new(); let _ = pool.spawner().spawn_local_obj( async move { - let node_a_public = key_store - .sr25519_generate_new(key_types::AUTHORITY_DISCOVERY, None) - .await - .unwrap(); + let node_a_public = + key_store.sr25519_generate_new(key_types::AUTHORITY_DISCOVERY, None).unwrap(); let test_api = Arc::new(TestApi { authorities: vec![node_a_public.into()] }); let (_to_worker, from_service) = mpsc::channel(0); @@ -337,7 +334,7 @@ fn publish_discover_cycle() { authorities: vec![node_a_public.into()], }); let network: Arc = Arc::new(Default::default()); - let key_store = KeyStore::new(); + let key_store = MemoryKeystore::new(); let (_to_worker, from_service) = mpsc::channel(0); let mut worker = Worker::new( @@ -371,7 +368,7 @@ fn publish_discover_cycle() { fn terminate_when_event_stream_terminates() { let (dht_event_tx, dht_event_rx) = channel(1000); let network: Arc = Arc::new(Default::default()); - let key_store = KeyStore::new(); + let key_store = MemoryKeystore::new(); let test_api = Arc::new(TestApi { authorities: vec![] }); let (to_worker, from_service) = mpsc::channel(0); @@ -420,11 +417,11 @@ fn dont_stop_polling_dht_event_stream_after_bogus_event() { address.with(multiaddr::Protocol::P2p(peer_id.into())) }; - let remote_key_store = KeyStore::new(); - let remote_public_key: AuthorityId = - block_on(remote_key_store.sr25519_generate_new(key_types::AUTHORITY_DISCOVERY, None)) - .unwrap() - .into(); + let remote_key_store = MemoryKeystore::new(); + let remote_public_key: AuthorityId = remote_key_store + .sr25519_generate_new(key_types::AUTHORITY_DISCOVERY, None) + .unwrap() + .into(); let (mut dht_event_tx, dht_event_rx) = channel(1); let (network, mut network_events) = { @@ -433,7 +430,7 @@ fn dont_stop_polling_dht_event_stream_after_bogus_event() { (Arc::new(n), r) }; - let key_store = KeyStore::new(); + let key_store = MemoryKeystore::new(); let test_api = Arc::new(TestApi { authorities: vec![remote_public_key.clone()] }); let mut pool = LocalPool::new(); @@ -480,8 +477,7 @@ fn dont_stop_polling_dht_event_stream_after_bogus_event() { remote_public_key.clone(), &remote_key_store, None, - ) - .await; + ); DhtEvent::ValueFound(kv_pairs) }; dht_event_tx.send(dht_event).await.expect("Channel has capacity of 1."); @@ -498,7 +494,7 @@ fn dont_stop_polling_dht_event_stream_after_bogus_event() { } struct DhtValueFoundTester { - pub remote_key_store: KeyStore, + pub remote_key_store: MemoryKeystore, pub remote_authority_public: sp_core::sr25519::Public, pub remote_node_key: Keypair, pub local_worker: Option< @@ -516,10 +512,10 @@ struct DhtValueFoundTester { impl DhtValueFoundTester { fn new() -> Self { - let remote_key_store = KeyStore::new(); - let remote_authority_public = - block_on(remote_key_store.sr25519_generate_new(key_types::AUTHORITY_DISCOVERY, None)) - .unwrap(); + let remote_key_store = MemoryKeystore::new(); + let remote_authority_public = remote_key_store + .sr25519_generate_new(key_types::AUTHORITY_DISCOVERY, None) + .unwrap(); let remote_node_key = Keypair::generate_ed25519(); Self { remote_key_store, remote_authority_public, remote_node_key, local_worker: None } @@ -542,7 +538,7 @@ impl DhtValueFoundTester { let local_test_api = Arc::new(TestApi { authorities: vec![self.remote_authority_public.into()] }); let local_network: Arc = Arc::new(Default::default()); - let local_key_store = KeyStore::new(); + let local_key_store = MemoryKeystore::new(); let (_to_worker, from_service) = mpsc::channel(0); let mut local_worker = Worker::new( @@ -576,12 +572,12 @@ fn limit_number_of_addresses_added_to_cache_per_authority() { let mut tester = DhtValueFoundTester::new(); assert!(MAX_ADDRESSES_PER_AUTHORITY < 100); let addresses = (1..100).map(|i| tester.multiaddr_with_peer_id(i)).collect(); - let kv_pairs = block_on(build_dht_event::( + let kv_pairs = build_dht_event::( addresses, tester.remote_authority_public.into(), &tester.remote_key_store, None, - )); + ); let cached_remote_addresses = tester.process_value_found(false, kv_pairs); assert_eq!(MAX_ADDRESSES_PER_AUTHORITY, cached_remote_addresses.unwrap().len()); @@ -591,12 +587,12 @@ fn limit_number_of_addresses_added_to_cache_per_authority() { fn strict_accept_address_with_peer_signature() { let mut tester = DhtValueFoundTester::new(); let addr = tester.multiaddr_with_peer_id(1); - let kv_pairs = block_on(build_dht_event( + let kv_pairs = build_dht_event( vec![addr.clone()], tester.remote_authority_public.into(), &tester.remote_key_store, Some(&TestSigner { keypair: &tester.remote_node_key }), - )); + ); let cached_remote_addresses = tester.process_value_found(true, kv_pairs); @@ -611,12 +607,12 @@ fn strict_accept_address_with_peer_signature() { fn reject_address_with_rogue_peer_signature() { let mut tester = DhtValueFoundTester::new(); let rogue_remote_node_key = Keypair::generate_ed25519(); - let kv_pairs = block_on(build_dht_event( + let kv_pairs = build_dht_event( vec![tester.multiaddr_with_peer_id(1)], tester.remote_authority_public.into(), &tester.remote_key_store, Some(&TestSigner { keypair: &rogue_remote_node_key }), - )); + ); let cached_remote_addresses = tester.process_value_found(false, kv_pairs); @@ -629,12 +625,12 @@ fn reject_address_with_rogue_peer_signature() { #[test] fn reject_address_with_invalid_peer_signature() { let mut tester = DhtValueFoundTester::new(); - let mut kv_pairs = block_on(build_dht_event( + let mut kv_pairs = build_dht_event( vec![tester.multiaddr_with_peer_id(1)], tester.remote_authority_public.into(), &tester.remote_key_store, Some(&TestSigner { keypair: &tester.remote_node_key }), - )); + ); // tamper with the signature let mut record = schema::SignedAuthorityRecord::decode(kv_pairs[0].1.as_slice()).unwrap(); record.peer_signature.as_mut().map(|p| p.signature[1] = !p.signature[1]); @@ -651,12 +647,12 @@ fn reject_address_with_invalid_peer_signature() { #[test] fn reject_address_without_peer_signature() { let mut tester = DhtValueFoundTester::new(); - let kv_pairs = block_on(build_dht_event::( + let kv_pairs = build_dht_event::( vec![tester.multiaddr_with_peer_id(1)], tester.remote_authority_public.into(), &tester.remote_key_store, None, - )); + ); let cached_remote_addresses = tester.process_value_found(true, kv_pairs); @@ -669,12 +665,12 @@ fn do_not_cache_addresses_without_peer_id() { let multiaddr_with_peer_id = tester.multiaddr_with_peer_id(1); let multiaddr_without_peer_id: Multiaddr = "/ip6/2001:db8:0:0:0:0:0:2/tcp/30333".parse().unwrap(); - let kv_pairs = block_on(build_dht_event::( + let kv_pairs = build_dht_event::( vec![multiaddr_with_peer_id.clone(), multiaddr_without_peer_id], tester.remote_authority_public.into(), &tester.remote_key_store, None, - )); + ); let cached_remote_addresses = tester.process_value_found(false, kv_pairs); @@ -701,7 +697,7 @@ fn addresses_to_publish_adds_p2p() { Arc::new(TestApi { authorities: vec![] }), network.clone(), Box::pin(dht_event_rx), - Role::PublishAndDiscover(Arc::new(KeyStore::new())), + Role::PublishAndDiscover(Arc::new(MemoryKeystore::new())), Some(prometheus_endpoint::Registry::new()), Default::default(), ); @@ -735,7 +731,7 @@ fn addresses_to_publish_respects_existing_p2p_protocol() { Arc::new(TestApi { authorities: vec![] }), network.clone(), Box::pin(dht_event_rx), - Role::PublishAndDiscover(Arc::new(KeyStore::new())), + Role::PublishAndDiscover(Arc::new(MemoryKeystore::new())), Some(prometheus_endpoint::Registry::new()), Default::default(), ); @@ -755,10 +751,11 @@ fn lookup_throttling() { address.with(multiaddr::Protocol::P2p(peer_id.into())) }; - let remote_key_store = KeyStore::new(); + let remote_key_store = MemoryKeystore::new(); let remote_public_keys: Vec = (0..20) .map(|_| { - block_on(remote_key_store.sr25519_generate_new(key_types::AUTHORITY_DISCOVERY, None)) + remote_key_store + .sr25519_generate_new(key_types::AUTHORITY_DISCOVERY, None) .unwrap() .into() }) @@ -818,8 +815,7 @@ fn lookup_throttling() { remote_key, &remote_key_store, None, - ) - .await; + ); DhtEvent::ValueFound(kv_pairs) }; dht_event_tx.send(dht_event).await.expect("Channel has capacity of 1."); diff --git a/client/cli/src/commands/insert_key.rs b/client/cli/src/commands/insert_key.rs index 77d0f5778..b02c0181d 100644 --- a/client/cli/src/commands/insert_key.rs +++ b/client/cli/src/commands/insert_key.rs @@ -24,7 +24,7 @@ use clap::Parser; use sc_keystore::LocalKeystore; use sc_service::config::{BasePath, KeystoreConfig}; use sp_core::crypto::{KeyTypeId, SecretString}; -use sp_keystore::{SyncCryptoStore, SyncCryptoStorePtr}; +use sp_keystore::{Keystore, KeystorePtr}; use std::sync::Arc; /// The `insert` command @@ -69,7 +69,7 @@ impl InsertKeyCmd { let (keystore, public) = match self.keystore_params.keystore_config(&config_dir)? { (_, KeystoreConfig::Path { path, password }) => { let public = with_crypto_scheme!(self.scheme, to_vec(&suri, password.clone()))?; - let keystore: SyncCryptoStorePtr = Arc::new(LocalKeystore::open(path, password)?); + let keystore: KeystorePtr = Arc::new(LocalKeystore::open(path, password)?); (keystore, public) }, _ => unreachable!("keystore_config always returns path and password; qed"), @@ -78,8 +78,8 @@ impl InsertKeyCmd { let key_type = KeyTypeId::try_from(self.key_type.as_str()).map_err(|_| Error::KeyTypeInvalid)?; - SyncCryptoStore::insert_unknown(&*keystore, key_type, &suri, &public[..]) - .map_err(|_| Error::KeyStoreOperation)?; + Keystore::insert(&*keystore, key_type, &suri, &public[..]) + .map_err(|_| Error::KeystoreOperation)?; Ok(()) } diff --git a/client/cli/src/error.rs b/client/cli/src/error.rs index 3b30ccb3e..6c0cfca49 100644 --- a/client/cli/src/error.rs +++ b/client/cli/src/error.rs @@ -64,7 +64,7 @@ pub enum Error { SignatureInvalid, #[error("Key store operation failed")] - KeyStoreOperation, + KeystoreOperation, #[error("Key storage issue encountered")] KeyStorage(#[from] sc_keystore::Error), diff --git a/client/consensus/aura/src/lib.rs b/client/consensus/aura/src/lib.rs index e90494ddf..8cdad148d 100644 --- a/client/consensus/aura/src/lib.rs +++ b/client/consensus/aura/src/lib.rs @@ -51,7 +51,7 @@ use sp_consensus::{BlockOrigin, Environment, Error as ConsensusError, Proposer, use sp_consensus_slots::Slot; use sp_core::crypto::{ByteArray, Pair, Public}; use sp_inherents::CreateInherentDataProviders; -use sp_keystore::{SyncCryptoStore, SyncCryptoStorePtr}; +use sp_keystore::{Keystore, KeystorePtr}; use sp_runtime::{ traits::{Block as BlockT, Header, Member, NumberFor, Zero}, DigestItem, @@ -168,7 +168,7 @@ pub struct StartAuraParams { /// The backoff strategy when we miss slots. pub backoff_authoring_blocks: Option, /// The keystore used by the node. - pub keystore: SyncCryptoStorePtr, + pub keystore: KeystorePtr, /// The proportion of the slot dedicated to proposing. /// /// The block proposing will be limited to this proportion of the slot from the starting of the @@ -265,7 +265,7 @@ pub struct BuildAuraWorkerParams { /// The backoff strategy when we miss slots. pub backoff_authoring_blocks: Option, /// The keystore used by the node. - pub keystore: SyncCryptoStorePtr, + pub keystore: KeystorePtr, /// The proportion of the slot dedicated to proposing. /// /// The block proposing will be limited to this proportion of the slot from the starting of the @@ -346,7 +346,7 @@ struct AuraWorker { client: Arc, block_import: I, env: E, - keystore: SyncCryptoStorePtr, + keystore: KeystorePtr, sync_oracle: SO, justification_sync_link: L, force_authoring: bool, @@ -418,7 +418,7 @@ where ) -> Option { let expected_author = slot_author::

(slot, epoch_data); expected_author.and_then(|p| { - if SyncCryptoStore::has_keys( + if Keystore::has_keys( &*self.keystore, &[(p.to_raw_vec(), sp_application_crypto::key_types::AURA)], ) { @@ -449,7 +449,7 @@ where // add it to a digest item. let public_type_pair = public.to_public_crypto_pair(); let public = public.to_raw_vec(); - let signature = SyncCryptoStore::sign_with( + let signature = Keystore::sign_with( &*self.keystore, as AppKey>::ID, &public_type_pair, @@ -798,7 +798,7 @@ mod tests { LocalKeystore::open(keystore_path.path(), None).expect("Creates keystore."), ); - SyncCryptoStore::sr25519_generate_new(&*keystore, AURA, Some(&key.to_seed())) + Keystore::sr25519_generate_new(&*keystore, AURA, Some(&key.to_seed())) .expect("Creates authority key"); keystore_paths.push(keystore_path); @@ -883,7 +883,7 @@ mod tests { let keystore_path = tempfile::tempdir().expect("Creates keystore path"); let keystore = LocalKeystore::open(keystore_path.path(), None).expect("Creates keystore."); - let public = SyncCryptoStore::sr25519_generate_new(&keystore, AuthorityPair::ID, None) + let public = Keystore::sr25519_generate_new(&keystore, AuthorityPair::ID, None) .expect("Key should be created"); authorities.push(public.into()); @@ -933,7 +933,7 @@ mod tests { let keystore_path = tempfile::tempdir().expect("Creates keystore path"); let keystore = LocalKeystore::open(keystore_path.path(), None).expect("Creates keystore."); - SyncCryptoStore::sr25519_generate_new( + Keystore::sr25519_generate_new( &keystore, AuthorityPair::ID, Some(&Keyring::Alice.to_seed()), diff --git a/client/consensus/babe/rpc/src/lib.rs b/client/consensus/babe/rpc/src/lib.rs index 677a4ad92..bc4d1c17e 100644 --- a/client/consensus/babe/rpc/src/lib.rs +++ b/client/consensus/babe/rpc/src/lib.rs @@ -37,7 +37,7 @@ use sp_consensus_babe::{ digests::PreDigest, AuthorityId, BabeApi as BabeRuntimeApi, BabeConfiguration, }; use sp_core::crypto::ByteArray; -use sp_keystore::{SyncCryptoStore, SyncCryptoStorePtr}; +use sp_keystore::{Keystore, KeystorePtr}; use sp_runtime::traits::{Block as BlockT, Header as _}; use std::{collections::HashMap, sync::Arc}; @@ -57,7 +57,7 @@ pub struct Babe { /// shared reference to EpochChanges shared_epoch_changes: SharedEpochChanges, /// shared reference to the Keystore - keystore: SyncCryptoStorePtr, + keystore: KeystorePtr, /// config (actually holds the slot duration) babe_config: BabeConfiguration, /// The SelectChain strategy @@ -71,7 +71,7 @@ impl Babe { pub fn new( client: Arc, shared_epoch_changes: SharedEpochChanges, - keystore: SyncCryptoStorePtr, + keystore: KeystorePtr, babe_config: BabeConfiguration, select_chain: SC, deny_unsafe: DenyUnsafe, @@ -117,10 +117,7 @@ where .iter() .enumerate() .filter_map(|(i, a)| { - if SyncCryptoStore::has_keys( - &*self.keystore, - &[(a.0.to_raw_vec(), AuthorityId::ID)], - ) { + if Keystore::has_keys(&*self.keystore, &[(a.0.to_raw_vec(), AuthorityId::ID)]) { Some((a.0.clone(), i)) } else { None @@ -217,7 +214,7 @@ mod tests { use sp_application_crypto::AppPair; use sp_core::crypto::key_types::BABE; use sp_keyring::Sr25519Keyring; - use sp_keystore::{SyncCryptoStore, SyncCryptoStorePtr}; + use sp_keystore::{Keystore, KeystorePtr}; use substrate_test_runtime_client::{ runtime::Block, Backend, DefaultTestClientBuilderExt, TestClient, TestClientBuilder, TestClientBuilderExt, @@ -229,11 +226,11 @@ mod tests { /// creates keystore backed by a temp file fn create_temp_keystore( authority: Sr25519Keyring, - ) -> (SyncCryptoStorePtr, tempfile::TempDir) { + ) -> (KeystorePtr, tempfile::TempDir) { let keystore_path = tempfile::tempdir().expect("Creates keystore path"); let keystore = Arc::new(LocalKeystore::open(keystore_path.path(), None).expect("Creates keystore")); - SyncCryptoStore::sr25519_generate_new(&*keystore, BABE, Some(&authority.to_seed())) + Keystore::sr25519_generate_new(&*keystore, BABE, Some(&authority.to_seed())) .expect("Creates authority key"); (keystore, keystore_path) diff --git a/client/consensus/babe/src/authorship.rs b/client/consensus/babe/src/authorship.rs index 195a19b3d..956e886d1 100644 --- a/client/consensus/babe/src/authorship.rs +++ b/client/consensus/babe/src/authorship.rs @@ -29,7 +29,7 @@ use sp_consensus_babe::{ }; use sp_consensus_vrf::schnorrkel::{VRFOutput, VRFProof}; use sp_core::{blake2_256, crypto::ByteArray, U256}; -use sp_keystore::{SyncCryptoStore, SyncCryptoStorePtr}; +use sp_keystore::{Keystore, KeystorePtr}; /// Calculates the primary selection threshold for a given authority, taking /// into account `c` (`1 - c` represents the probability of a slot being empty). @@ -133,7 +133,7 @@ fn claim_secondary_slot( slot: Slot, epoch: &Epoch, keys: &[(AuthorityId, usize)], - keystore: &SyncCryptoStorePtr, + keystore: &KeystorePtr, author_secondary_vrf: bool, ) -> Option<(PreDigest, AuthorityId)> { let Epoch { authorities, randomness, mut epoch_index, .. } = epoch; @@ -153,7 +153,7 @@ fn claim_secondary_slot( if authority_id == expected_author { let pre_digest = if author_secondary_vrf { let transcript_data = make_transcript_data(randomness, slot, epoch_index); - let result = SyncCryptoStore::sr25519_vrf_sign( + let result = Keystore::sr25519_vrf_sign( &**keystore, AuthorityId::ID, authority_id.as_ref(), @@ -169,7 +169,7 @@ fn claim_secondary_slot( } else { None } - } else if SyncCryptoStore::has_keys( + } else if Keystore::has_keys( &**keystore, &[(authority_id.to_raw_vec(), AuthorityId::ID)], ) { @@ -197,7 +197,7 @@ fn claim_secondary_slot( pub fn claim_slot( slot: Slot, epoch: &Epoch, - keystore: &SyncCryptoStorePtr, + keystore: &KeystorePtr, ) -> Option<(PreDigest, AuthorityId)> { let authorities = epoch .authorities @@ -213,7 +213,7 @@ pub fn claim_slot( pub fn claim_slot_using_keys( slot: Slot, epoch: &Epoch, - keystore: &SyncCryptoStorePtr, + keystore: &KeystorePtr, keys: &[(AuthorityId, usize)], ) -> Option<(PreDigest, AuthorityId)> { claim_primary_slot(slot, epoch, epoch.config.c, keystore, keys).or_else(|| { @@ -241,7 +241,7 @@ fn claim_primary_slot( slot: Slot, epoch: &Epoch, c: (u64, u64), - keystore: &SyncCryptoStorePtr, + keystore: &KeystorePtr, keys: &[(AuthorityId, usize)], ) -> Option<(PreDigest, AuthorityId)> { let Epoch { authorities, randomness, mut epoch_index, .. } = epoch; @@ -254,7 +254,7 @@ fn claim_primary_slot( for (authority_id, authority_index) in keys { let transcript = make_transcript(randomness, slot, epoch_index); let transcript_data = make_transcript_data(randomness, slot, epoch_index); - let result = SyncCryptoStore::sr25519_vrf_sign( + let result = Keystore::sr25519_vrf_sign( &**keystore, AuthorityId::ID, authority_id.as_ref(), @@ -294,8 +294,8 @@ mod tests { #[test] fn claim_secondary_plain_slot_works() { - let keystore: SyncCryptoStorePtr = Arc::new(LocalKeystore::in_memory()); - let valid_public_key = SyncCryptoStore::sr25519_generate_new( + let keystore: KeystorePtr = Arc::new(LocalKeystore::in_memory()); + let valid_public_key = Keystore::sr25519_generate_new( &*keystore, AuthorityId::ID, Some(sp_core::crypto::DEV_PHRASE), diff --git a/client/consensus/babe/src/lib.rs b/client/consensus/babe/src/lib.rs index 1d42057a3..621aac391 100644 --- a/client/consensus/babe/src/lib.rs +++ b/client/consensus/babe/src/lib.rs @@ -119,7 +119,7 @@ use sp_consensus_babe::inherents::BabeInherentData; use sp_consensus_slots::Slot; use sp_core::{crypto::ByteArray, ExecutionContext}; use sp_inherents::{CreateInherentDataProviders, InherentData, InherentDataProvider}; -use sp_keystore::{SyncCryptoStore, SyncCryptoStorePtr}; +use sp_keystore::{Keystore, KeystorePtr}; use sp_runtime::{ generic::OpaqueDigestItemId, traits::{Block as BlockT, Header, NumberFor, SaturatedConversion, Zero}, @@ -404,7 +404,7 @@ where /// Parameters for BABE. pub struct BabeParams { /// The keystore that manages the keys of the node. - pub keystore: SyncCryptoStorePtr, + pub keystore: KeystorePtr, /// The client to use pub client: Arc, @@ -711,7 +711,7 @@ struct BabeSlotWorker { justification_sync_link: L, force_authoring: bool, backoff_authoring_blocks: Option, - keystore: SyncCryptoStorePtr, + keystore: KeystorePtr, epoch_changes: SharedEpochChanges, slot_notification_sinks: SlotNotificationSinks, config: BabeConfiguration, @@ -834,7 +834,7 @@ where // add it to a digest item. let public_type_pair = public.clone().into(); let public = public.to_raw_vec(); - let signature = SyncCryptoStore::sign_with( + let signature = Keystore::sign_with( &*self.keystore, ::ID, &public_type_pair, diff --git a/client/consensus/babe/src/tests.rs b/client/consensus/babe/src/tests.rs index 25f1f8723..4ac15bf26 100644 --- a/client/consensus/babe/src/tests.rs +++ b/client/consensus/babe/src/tests.rs @@ -41,8 +41,7 @@ use sp_consensus_vrf::schnorrkel::VRFOutput; use sp_core::crypto::Pair; use sp_keyring::Sr25519Keyring; use sp_keystore::{ - testing::KeyStore as TestKeyStore, vrf::make_transcript as transcript_from_data, - SyncCryptoStore, + testing::MemoryKeystore, vrf::make_transcript as transcript_from_data, Keystore, }; use sp_runtime::{ generic::{Digest, DigestItem}, @@ -369,9 +368,9 @@ async fn rejects_empty_block() { }) } -fn create_keystore(authority: Sr25519Keyring) -> SyncCryptoStorePtr { - let keystore = Arc::new(TestKeyStore::new()); - SyncCryptoStore::sr25519_generate_new(&*keystore, BABE, Some(&authority.to_seed())) +fn create_keystore(authority: Sr25519Keyring) -> KeystorePtr { + let keystore = Arc::new(MemoryKeystore::new()); + Keystore::sr25519_generate_new(&*keystore, BABE, Some(&authority.to_seed())) .expect("Generates authority key"); keystore } @@ -638,7 +637,7 @@ fn claim_vrf_check() { v => panic!("Unexpected pre-digest variant {:?}", v), }; let transcript = make_transcript_data(&epoch.randomness.clone(), 0.into(), epoch.epoch_index); - let sign = SyncCryptoStore::sr25519_vrf_sign(&*keystore, AuthorityId::ID, &public, transcript) + let sign = Keystore::sr25519_vrf_sign(&*keystore, AuthorityId::ID, &public, transcript) .unwrap() .unwrap(); assert_eq!(pre_digest.vrf_output, VRFOutput(sign.output)); @@ -649,7 +648,7 @@ fn claim_vrf_check() { v => panic!("Unexpected pre-digest variant {:?}", v), }; let transcript = make_transcript_data(&epoch.randomness.clone(), 1.into(), epoch.epoch_index); - let sign = SyncCryptoStore::sr25519_vrf_sign(&*keystore, AuthorityId::ID, &public, transcript) + let sign = Keystore::sr25519_vrf_sign(&*keystore, AuthorityId::ID, &public, transcript) .unwrap() .unwrap(); assert_eq!(pre_digest.vrf_output, VRFOutput(sign.output)); @@ -662,7 +661,7 @@ fn claim_vrf_check() { }; let fixed_epoch = epoch.clone_for_slot(slot); let transcript = make_transcript_data(&epoch.randomness.clone(), slot, fixed_epoch.epoch_index); - let sign = SyncCryptoStore::sr25519_vrf_sign(&*keystore, AuthorityId::ID, &public, transcript) + let sign = Keystore::sr25519_vrf_sign(&*keystore, AuthorityId::ID, &public, transcript) .unwrap() .unwrap(); assert_eq!(fixed_epoch.epoch_index, 11); @@ -676,7 +675,7 @@ fn claim_vrf_check() { }; let fixed_epoch = epoch.clone_for_slot(slot); let transcript = make_transcript_data(&epoch.randomness.clone(), slot, fixed_epoch.epoch_index); - let sign = SyncCryptoStore::sr25519_vrf_sign(&*keystore, AuthorityId::ID, &public, transcript) + let sign = Keystore::sr25519_vrf_sign(&*keystore, AuthorityId::ID, &public, transcript) .unwrap() .unwrap(); assert_eq!(fixed_epoch.epoch_index, 11); diff --git a/client/consensus/beefy/src/communication/gossip.rs b/client/consensus/beefy/src/communication/gossip.rs index e49382251..4bbd7a7f6 100644 --- a/client/consensus/beefy/src/communication/gossip.rs +++ b/client/consensus/beefy/src/communication/gossip.rs @@ -245,7 +245,7 @@ where mod tests { use sc_keystore::LocalKeystore; use sc_network_test::Block; - use sp_keystore::{SyncCryptoStore, SyncCryptoStorePtr}; + use sp_keystore::{Keystore, KeystorePtr}; use crate::keystore::BeefyKeystore; use sp_consensus_beefy::{ @@ -306,8 +306,8 @@ mod tests { } fn sign_commitment(who: &Keyring, commitment: &Commitment) -> Signature { - let store: SyncCryptoStorePtr = std::sync::Arc::new(LocalKeystore::in_memory()); - SyncCryptoStore::ecdsa_generate_new(&*store, KEY_TYPE, Some(&who.to_seed())).unwrap(); + let store: KeystorePtr = std::sync::Arc::new(LocalKeystore::in_memory()); + Keystore::ecdsa_generate_new(&*store, KEY_TYPE, Some(&who.to_seed())).unwrap(); let beefy_keystore: BeefyKeystore = Some(store).into(); beefy_keystore.sign(&who.public(), &commitment.encode()).unwrap() diff --git a/client/consensus/beefy/src/keystore.rs b/client/consensus/beefy/src/keystore.rs index 421f71490..596e8f2eb 100644 --- a/client/consensus/beefy/src/keystore.rs +++ b/client/consensus/beefy/src/keystore.rs @@ -18,7 +18,7 @@ use sp_application_crypto::RuntimeAppPublic; use sp_core::keccak_256; -use sp_keystore::{SyncCryptoStore, SyncCryptoStorePtr}; +use sp_keystore::{Keystore, KeystorePtr}; use log::warn; @@ -33,9 +33,9 @@ use crate::{error, LOG_TARGET}; pub(crate) type BeefySignatureHasher = sp_runtime::traits::Keccak256; /// A BEEFY specific keystore implemented as a `Newtype`. This is basically a -/// wrapper around [`sp_keystore::SyncCryptoStore`] and allows to customize +/// wrapper around [`sp_keystore::Keystore`] and allows to customize /// common cryptographic functionality. -pub(crate) struct BeefyKeystore(Option); +pub(crate) struct BeefyKeystore(Option); impl BeefyKeystore { /// Check if the keystore contains a private key for one of the public keys @@ -50,7 +50,7 @@ impl BeefyKeystore { // we do check for multiple private keys as a key store sanity check. let public: Vec = keys .iter() - .filter(|k| SyncCryptoStore::has_keys(&*store, &[(k.to_raw_vec(), KEY_TYPE)])) + .filter(|k| Keystore::has_keys(&*store, &[(k.to_raw_vec(), KEY_TYPE)])) .cloned() .collect(); @@ -77,7 +77,7 @@ impl BeefyKeystore { let msg = keccak_256(message); let public = public.as_ref(); - let sig = SyncCryptoStore::ecdsa_sign_prehashed(&*store, KEY_TYPE, public, &msg) + let sig = Keystore::ecdsa_sign_prehashed(&*store, KEY_TYPE, public, &msg) .map_err(|e| error::Error::Keystore(e.to_string()))? .ok_or_else(|| error::Error::Signature("ecdsa_sign_prehashed() failed".to_string()))?; @@ -94,7 +94,7 @@ impl BeefyKeystore { pub fn public_keys(&self) -> Result, error::Error> { let store = self.0.clone().ok_or_else(|| error::Error::Keystore("no Keystore".into()))?; - let pk: Vec = SyncCryptoStore::ecdsa_public_keys(&*store, KEY_TYPE) + let pk: Vec = Keystore::ecdsa_public_keys(&*store, KEY_TYPE) .drain(..) .map(Public::from) .collect(); @@ -110,8 +110,8 @@ impl BeefyKeystore { } } -impl From> for BeefyKeystore { - fn from(store: Option) -> BeefyKeystore { +impl From> for BeefyKeystore { + fn from(store: Option) -> BeefyKeystore { BeefyKeystore(store) } } @@ -128,7 +128,7 @@ pub mod tests { use super::*; use crate::error::Error; - fn keystore() -> SyncCryptoStorePtr { + fn keystore() -> KeystorePtr { Arc::new(LocalKeystore::in_memory()) } @@ -198,7 +198,7 @@ pub mod tests { let store = keystore(); let alice: crypto::Public = - SyncCryptoStore::ecdsa_generate_new(&*store, KEY_TYPE, Some(&Keyring::Alice.to_seed())) + Keystore::ecdsa_generate_new(&*store, KEY_TYPE, Some(&Keyring::Alice.to_seed())) .ok() .unwrap() .into(); @@ -224,7 +224,7 @@ pub mod tests { let store = keystore(); let alice: crypto::Public = - SyncCryptoStore::ecdsa_generate_new(&*store, KEY_TYPE, Some(&Keyring::Alice.to_seed())) + Keystore::ecdsa_generate_new(&*store, KEY_TYPE, Some(&Keyring::Alice.to_seed())) .ok() .unwrap() .into(); @@ -243,10 +243,9 @@ pub mod tests { fn sign_error() { let store = keystore(); - let _ = - SyncCryptoStore::ecdsa_generate_new(&*store, KEY_TYPE, Some(&Keyring::Bob.to_seed())) - .ok() - .unwrap(); + let _ = Keystore::ecdsa_generate_new(&*store, KEY_TYPE, Some(&Keyring::Bob.to_seed())) + .ok() + .unwrap(); let store: BeefyKeystore = Some(store).into(); @@ -276,7 +275,7 @@ pub mod tests { let store = keystore(); let alice: crypto::Public = - SyncCryptoStore::ecdsa_generate_new(&*store, KEY_TYPE, Some(&Keyring::Alice.to_seed())) + Keystore::ecdsa_generate_new(&*store, KEY_TYPE, Some(&Keyring::Alice.to_seed())) .ok() .unwrap() .into(); @@ -302,7 +301,7 @@ pub mod tests { let store = keystore(); let add_key = |key_type, seed: Option<&str>| { - SyncCryptoStore::ecdsa_generate_new(&*store, key_type, seed).unwrap() + Keystore::ecdsa_generate_new(&*store, key_type, seed).unwrap() }; // test keys diff --git a/client/consensus/beefy/src/lib.rs b/client/consensus/beefy/src/lib.rs index d632f5833..b84fa45e7 100644 --- a/client/consensus/beefy/src/lib.rs +++ b/client/consensus/beefy/src/lib.rs @@ -49,7 +49,7 @@ use sp_consensus_beefy::{ crypto::AuthorityId, BeefyApi, MmrRootHash, PayloadProvider, ValidatorSet, BEEFY_ENGINE_ID, GENESIS_AUTHORITY_SET_ID, }; -use sp_keystore::SyncCryptoStorePtr; +use sp_keystore::KeystorePtr; use sp_mmr_primitives::MmrApi; use sp_runtime::traits::{Block, Zero}; use std::{collections::VecDeque, marker::PhantomData, sync::Arc}; @@ -197,7 +197,7 @@ pub struct BeefyParams { /// Runtime Api Provider pub runtime: Arc, /// Local key store - pub key_store: Option, + pub key_store: Option, /// BEEFY voter network params pub network_params: BeefyNetworkParams, /// Minimal delta between blocks, BEEFY should vote for diff --git a/client/consensus/beefy/src/tests.rs b/client/consensus/beefy/src/tests.rs index 629f14424..9a3653f7c 100644 --- a/client/consensus/beefy/src/tests.rs +++ b/client/consensus/beefy/src/tests.rs @@ -54,7 +54,7 @@ use sp_consensus_beefy::{ VersionedFinalityProof, BEEFY_ENGINE_ID, KEY_TYPE as BeefyKeyType, }; use sp_core::H256; -use sp_keystore::{testing::KeyStore as TestKeystore, SyncCryptoStore, SyncCryptoStorePtr}; +use sp_keystore::{testing::MemoryKeystore, Keystore, KeystorePtr}; use sp_mmr_primitives::{Error as MmrError, MmrApi}; use sp_runtime::{ codec::Encode, @@ -339,9 +339,9 @@ pub(crate) fn make_beefy_ids(keys: &[BeefyKeyring]) -> Vec { keys.iter().map(|&key| key.public().into()).collect() } -pub(crate) fn create_beefy_keystore(authority: BeefyKeyring) -> SyncCryptoStorePtr { - let keystore = Arc::new(TestKeystore::new()); - SyncCryptoStore::ecdsa_generate_new(&*keystore, BeefyKeyType, Some(&authority.to_seed())) +pub(crate) fn create_beefy_keystore(authority: BeefyKeyring) -> KeystorePtr { + let keystore = Arc::new(MemoryKeystore::new()); + Keystore::ecdsa_generate_new(&*keystore, BeefyKeyType, Some(&authority.to_seed())) .expect("Creates authority key"); keystore } diff --git a/client/consensus/grandpa/src/communication/mod.rs b/client/consensus/grandpa/src/communication/mod.rs index 3896bc360..81365c20b 100644 --- a/client/consensus/grandpa/src/communication/mod.rs +++ b/client/consensus/grandpa/src/communication/mod.rs @@ -49,7 +49,7 @@ use parity_scale_codec::{Decode, Encode}; use sc_network::{NetworkBlock, NetworkSyncForkRequest, ReputationChange}; use sc_network_gossip::{GossipEngine, Network as GossipNetwork}; use sc_telemetry::{telemetry, TelemetryHandle, CONSENSUS_DEBUG, CONSENSUS_INFO}; -use sp_keystore::SyncCryptoStorePtr; +use sp_keystore::KeystorePtr; use sp_runtime::traits::{Block as BlockT, Hash as HashT, Header as HeaderT, NumberFor}; use crate::{ @@ -136,7 +136,7 @@ mod benefit { /// A type that ties together our local authority id and a keystore where it is /// available for signing. -pub struct LocalIdKeystore((AuthorityId, SyncCryptoStorePtr)); +pub struct LocalIdKeystore((AuthorityId, KeystorePtr)); impl LocalIdKeystore { /// Returns a reference to our local authority id. @@ -145,13 +145,13 @@ impl LocalIdKeystore { } /// Returns a reference to the keystore. - fn keystore(&self) -> SyncCryptoStorePtr { + fn keystore(&self) -> KeystorePtr { (self.0).1.clone() } } -impl From<(AuthorityId, SyncCryptoStorePtr)> for LocalIdKeystore { - fn from(inner: (AuthorityId, SyncCryptoStorePtr)) -> LocalIdKeystore { +impl From<(AuthorityId, KeystorePtr)> for LocalIdKeystore { + fn from(inner: (AuthorityId, KeystorePtr)) -> LocalIdKeystore { LocalIdKeystore(inner) } } diff --git a/client/consensus/grandpa/src/lib.rs b/client/consensus/grandpa/src/lib.rs index 2baa13508..6b3730fc7 100644 --- a/client/consensus/grandpa/src/lib.rs +++ b/client/consensus/grandpa/src/lib.rs @@ -79,7 +79,7 @@ use sp_consensus_grandpa::{ AuthorityList, AuthoritySignature, SetId, CLIENT_LOG_TARGET as LOG_TARGET, }; use sp_core::{crypto::ByteArray, traits::CallContext}; -use sp_keystore::{SyncCryptoStore, SyncCryptoStorePtr}; +use sp_keystore::{Keystore, KeystorePtr}; use sp_runtime::{ generic::BlockId, traits::{Block as BlockT, NumberFor, Zero}, @@ -228,7 +228,7 @@ pub struct Config { /// Some local identifier of the voter. pub name: Option, /// The keystore that manages the keys of this node. - pub keystore: Option, + pub keystore: Option, /// TelemetryHandle instance. pub telemetry: Option, /// Chain specific GRANDPA protocol name. See [`crate::protocol_standard_name`]. @@ -623,7 +623,7 @@ fn global_communication( voters: &Arc>, client: Arc, network: &NetworkBridge, - keystore: Option<&SyncCryptoStorePtr>, + keystore: Option<&KeystorePtr>, metrics: Option, ) -> ( impl Stream< @@ -1136,14 +1136,12 @@ where /// available. fn local_authority_id( voters: &VoterSet, - keystore: Option<&SyncCryptoStorePtr>, + keystore: Option<&KeystorePtr>, ) -> Option { keystore.and_then(|keystore| { voters .iter() - .find(|(p, _)| { - SyncCryptoStore::has_keys(&**keystore, &[(p.to_raw_vec(), AuthorityId::ID)]) - }) + .find(|(p, _)| Keystore::has_keys(&**keystore, &[(p.to_raw_vec(), AuthorityId::ID)])) .map(|(p, _)| p.clone()) }) } diff --git a/client/consensus/grandpa/src/observer.rs b/client/consensus/grandpa/src/observer.rs index 53672c1f0..8541baa82 100644 --- a/client/consensus/grandpa/src/observer.rs +++ b/client/consensus/grandpa/src/observer.rs @@ -33,7 +33,7 @@ use sc_utils::mpsc::TracingUnboundedReceiver; use sp_blockchain::HeaderMetadata; use sp_consensus::SelectChain; use sp_consensus_grandpa::AuthorityId; -use sp_keystore::SyncCryptoStorePtr; +use sp_keystore::KeystorePtr; use sp_runtime::traits::{Block as BlockT, NumberFor}; use crate::{ @@ -220,7 +220,7 @@ struct ObserverWork, S: SyncingT> { client: Arc, network: NetworkBridge, persistent_data: PersistentData, - keystore: Option, + keystore: Option, voter_commands_rx: TracingUnboundedReceiver>>, justification_sender: Option>, telemetry: Option, @@ -240,7 +240,7 @@ where client: Arc, network: NetworkBridge, persistent_data: PersistentData, - keystore: Option, + keystore: Option, voter_commands_rx: TracingUnboundedReceiver>>, justification_sender: Option>, telemetry: Option, diff --git a/client/consensus/grandpa/src/tests.rs b/client/consensus/grandpa/src/tests.rs index f5e5c45e7..6f19ca739 100644 --- a/client/consensus/grandpa/src/tests.rs +++ b/client/consensus/grandpa/src/tests.rs @@ -40,7 +40,7 @@ use sp_consensus_grandpa::{ }; use sp_core::H256; use sp_keyring::Ed25519Keyring; -use sp_keystore::{testing::KeyStore as TestKeyStore, SyncCryptoStore, SyncCryptoStorePtr}; +use sp_keystore::{testing::MemoryKeystore, Keystore, KeystorePtr}; use sp_runtime::{ codec::Encode, generic::{BlockId, DigestItem}, @@ -280,9 +280,9 @@ fn make_ids(keys: &[Ed25519Keyring]) -> AuthorityList { keys.iter().map(|&key| key.public().into()).map(|id| (id, 1)).collect() } -fn create_keystore(authority: Ed25519Keyring) -> SyncCryptoStorePtr { - let keystore = Arc::new(TestKeyStore::new()); - SyncCryptoStore::ed25519_generate_new(&*keystore, GRANDPA, Some(&authority.to_seed())) +fn create_keystore(authority: Ed25519Keyring) -> KeystorePtr { + let keystore = Arc::new(MemoryKeystore::new()); + Keystore::ed25519_generate_new(&*keystore, GRANDPA, Some(&authority.to_seed())) .expect("Creates authority key"); keystore } @@ -1376,7 +1376,7 @@ type TestEnvironment = fn test_environment_with_select_chain( link: &TestLinkHalf, - keystore: Option, + keystore: Option, network_service: N, sync_service: S, select_chain: SC, @@ -1428,7 +1428,7 @@ where fn test_environment( link: &TestLinkHalf, - keystore: Option, + keystore: Option, network_service: N, sync_service: S, voting_rule: VR, diff --git a/client/consensus/manual-seal/src/consensus/babe.rs b/client/consensus/manual-seal/src/consensus/babe.rs index 3d21fd73d..2485bd603 100644 --- a/client/consensus/manual-seal/src/consensus/babe.rs +++ b/client/consensus/manual-seal/src/consensus/babe.rs @@ -29,7 +29,7 @@ use sc_consensus_babe::{ use sc_consensus_epochs::{ descendent_query, EpochHeader, SharedEpochChanges, ViableEpochDescriptor, }; -use sp_keystore::SyncCryptoStorePtr; +use sp_keystore::KeystorePtr; use std::{marker::PhantomData, sync::Arc}; use sc_consensus::{BlockImportParams, ForkChoiceStrategy, Verifier}; @@ -53,7 +53,7 @@ use sp_timestamp::TimestampInherentData; /// Intended for use with BABE runtimes. pub struct BabeConsensusDataProvider { /// shared reference to keystore - keystore: SyncCryptoStorePtr, + keystore: KeystorePtr, /// Shared reference to the client. client: Arc, @@ -143,7 +143,7 @@ where { pub fn new( client: Arc, - keystore: SyncCryptoStorePtr, + keystore: KeystorePtr, epoch_changes: SharedEpochChanges, authorities: Vec<(AuthorityId, BabeAuthorityWeight)>, ) -> Result { diff --git a/client/keystore/src/local.rs b/client/keystore/src/local.rs index cd55739a9..18ee58b87 100644 --- a/client/keystore/src/local.rs +++ b/client/keystore/src/local.rs @@ -17,7 +17,6 @@ // //! Local keystore implementation -use async_trait::async_trait; use parking_lot::RwLock; use sp_application_crypto::{ecdsa, ed25519, sr25519, AppKey, AppPair, IsWrappedBy}; use sp_core::{ @@ -29,10 +28,10 @@ use sp_core::{ }; use sp_keystore::{ vrf::{make_transcript, VRFSignature, VRFTranscriptData}, - CryptoStore, Error as TraitError, SyncCryptoStore, SyncCryptoStorePtr, + Error as TraitError, Keystore, KeystorePtr, }; use std::{ - collections::{HashMap, HashSet}, + collections::HashMap, fs::{self, File}, io::Write, path::PathBuf, @@ -69,101 +68,7 @@ impl LocalKeystore { } } -#[async_trait] -impl CryptoStore for LocalKeystore { - async fn keys( - &self, - id: KeyTypeId, - ) -> std::result::Result, TraitError> { - SyncCryptoStore::keys(self, id) - } - - async fn sr25519_public_keys(&self, id: KeyTypeId) -> Vec { - SyncCryptoStore::sr25519_public_keys(self, id) - } - - async fn sr25519_generate_new( - &self, - id: KeyTypeId, - seed: Option<&str>, - ) -> std::result::Result { - SyncCryptoStore::sr25519_generate_new(self, id, seed) - } - - async fn ed25519_public_keys(&self, id: KeyTypeId) -> Vec { - SyncCryptoStore::ed25519_public_keys(self, id) - } - - async fn ed25519_generate_new( - &self, - id: KeyTypeId, - seed: Option<&str>, - ) -> std::result::Result { - SyncCryptoStore::ed25519_generate_new(self, id, seed) - } - - async fn ecdsa_public_keys(&self, id: KeyTypeId) -> Vec { - SyncCryptoStore::ecdsa_public_keys(self, id) - } - - async fn ecdsa_generate_new( - &self, - id: KeyTypeId, - seed: Option<&str>, - ) -> std::result::Result { - SyncCryptoStore::ecdsa_generate_new(self, id, seed) - } - - async fn insert_unknown( - &self, - id: KeyTypeId, - suri: &str, - public: &[u8], - ) -> std::result::Result<(), ()> { - SyncCryptoStore::insert_unknown(self, id, suri, public) - } - - async fn has_keys(&self, public_keys: &[(Vec, KeyTypeId)]) -> bool { - SyncCryptoStore::has_keys(self, public_keys) - } - - async fn supported_keys( - &self, - id: KeyTypeId, - keys: Vec, - ) -> std::result::Result, TraitError> { - SyncCryptoStore::supported_keys(self, id, keys) - } - - async fn sign_with( - &self, - id: KeyTypeId, - key: &CryptoTypePublicPair, - msg: &[u8], - ) -> std::result::Result>, TraitError> { - SyncCryptoStore::sign_with(self, id, key, msg) - } - - async fn sr25519_vrf_sign( - &self, - key_type: KeyTypeId, - public: &sr25519::Public, - transcript_data: VRFTranscriptData, - ) -> std::result::Result, TraitError> { - SyncCryptoStore::sr25519_vrf_sign(self, key_type, public, transcript_data) - } - - async fn ecdsa_sign_prehashed( - &self, - id: KeyTypeId, - public: &ecdsa::Public, - msg: &[u8; 32], - ) -> std::result::Result, TraitError> { - SyncCryptoStore::ecdsa_sign_prehashed(self, id, public, msg) - } -} - -impl SyncCryptoStore for LocalKeystore { +impl Keystore for LocalKeystore { fn keys(&self, id: KeyTypeId) -> std::result::Result, TraitError> { let raw_keys = self.0.read().raw_public_keys(id)?; Ok(raw_keys.into_iter().fold(Vec::new(), |mut v, k| { @@ -174,15 +79,6 @@ impl SyncCryptoStore for LocalKeystore { })) } - fn supported_keys( - &self, - id: KeyTypeId, - keys: Vec, - ) -> std::result::Result, TraitError> { - let all_keys = SyncCryptoStore::keys(self, id)?.into_iter().collect::>(); - Ok(keys.into_iter().filter(|key| all_keys.contains(key)).collect::>()) - } - fn sign_with( &self, id: KeyTypeId, @@ -308,13 +204,13 @@ impl SyncCryptoStore for LocalKeystore { Ok(pair.public()) } - fn insert_unknown( + fn insert( &self, key_type: KeyTypeId, suri: &str, public: &[u8], ) -> std::result::Result<(), ()> { - self.0.write().insert_unknown(key_type, suri, public).map_err(|_| ()) + self.0.write().insert(key_type, suri, public).map_err(|_| ()) } fn has_keys(&self, public_keys: &[(Vec, KeyTypeId)]) -> bool { @@ -352,14 +248,8 @@ impl SyncCryptoStore for LocalKeystore { } } -impl Into for LocalKeystore { - fn into(self) -> SyncCryptoStorePtr { - Arc::new(self) - } -} - -impl Into> for LocalKeystore { - fn into(self) -> Arc { +impl Into for LocalKeystore { + fn into(self) -> KeystorePtr { Arc::new(self) } } @@ -414,7 +304,7 @@ impl KeystoreInner { /// Insert a new key with anonymous crypto. /// /// Places it into the file system store, if a path is configured. - fn insert_unknown(&self, key_type: KeyTypeId, suri: &str, public: &[u8]) -> Result<()> { + fn insert(&self, key_type: KeyTypeId, suri: &str, public: &[u8]) -> Result<()> { if let Some(path) = self.key_file_path(public, key_type) { Self::write_to_file(path, suri)?; } @@ -614,12 +504,9 @@ mod tests { let key: ed25519::AppPair = store.0.write().generate().unwrap(); let key2 = ed25519::Pair::generate().0; - assert!(!SyncCryptoStore::has_keys( - &store, - &[(key2.public().to_vec(), ed25519::AppPublic::ID)] - )); + assert!(!Keystore::has_keys(&store, &[(key2.public().to_vec(), ed25519::AppPublic::ID)])); - assert!(!SyncCryptoStore::has_keys( + assert!(!Keystore::has_keys( &store, &[ (key2.public().to_vec(), ed25519::AppPublic::ID), @@ -627,10 +514,7 @@ mod tests { ], )); - assert!(SyncCryptoStore::has_keys( - &store, - &[(key.public().to_raw_vec(), ed25519::AppPublic::ID)] - )); + assert!(Keystore::has_keys(&store, &[(key.public().to_raw_vec(), ed25519::AppPublic::ID)])); } #[test] @@ -723,7 +607,7 @@ mod tests { let key_pair = sr25519::AppPair::from_string(secret_uri, None).expect("Generates key pair"); store - .insert_unknown(SR25519, secret_uri, key_pair.public().as_ref()) + .insert(SR25519, secret_uri, key_pair.public().as_ref()) .expect("Inserts unknown key"); let store_key_pair = store @@ -742,7 +626,7 @@ mod tests { let file_name = temp_dir.path().join(array_bytes::bytes2hex("", &SR25519.0[..2])); fs::write(file_name, "test").expect("Invalid file is written"); - assert!(SyncCryptoStore::sr25519_public_keys(&store, SR25519).is_empty()); + assert!(Keystore::sr25519_public_keys(&store, SR25519).is_empty()); } #[test] @@ -750,23 +634,23 @@ mod tests { let temp_dir = TempDir::new().unwrap(); let store = LocalKeystore::open(temp_dir.path(), None).unwrap(); let _alice_tmp_key = - SyncCryptoStore::sr25519_generate_new(&store, TEST_KEY_TYPE, Some("//Alice")).unwrap(); + Keystore::sr25519_generate_new(&store, TEST_KEY_TYPE, Some("//Alice")).unwrap(); - assert_eq!(SyncCryptoStore::sr25519_public_keys(&store, TEST_KEY_TYPE).len(), 1); + assert_eq!(Keystore::sr25519_public_keys(&store, TEST_KEY_TYPE).len(), 1); drop(store); let store = LocalKeystore::open(temp_dir.path(), None).unwrap(); - assert_eq!(SyncCryptoStore::sr25519_public_keys(&store, TEST_KEY_TYPE).len(), 0); + assert_eq!(Keystore::sr25519_public_keys(&store, TEST_KEY_TYPE).len(), 0); } #[test] fn generate_can_be_fetched_in_memory() { let store = LocalKeystore::in_memory(); - SyncCryptoStore::sr25519_generate_new(&store, TEST_KEY_TYPE, Some("//Alice")).unwrap(); + Keystore::sr25519_generate_new(&store, TEST_KEY_TYPE, Some("//Alice")).unwrap(); - assert_eq!(SyncCryptoStore::sr25519_public_keys(&store, TEST_KEY_TYPE).len(), 1); - SyncCryptoStore::sr25519_generate_new(&store, TEST_KEY_TYPE, None).unwrap(); - assert_eq!(SyncCryptoStore::sr25519_public_keys(&store, TEST_KEY_TYPE).len(), 2); + assert_eq!(Keystore::sr25519_public_keys(&store, TEST_KEY_TYPE).len(), 1); + Keystore::sr25519_generate_new(&store, TEST_KEY_TYPE, None).unwrap(); + assert_eq!(Keystore::sr25519_public_keys(&store, TEST_KEY_TYPE).len(), 2); } #[test] @@ -777,7 +661,7 @@ mod tests { let temp_dir = TempDir::new().unwrap(); let store = LocalKeystore::open(temp_dir.path(), None).unwrap(); - let public = SyncCryptoStore::sr25519_generate_new(&store, TEST_KEY_TYPE, None).unwrap(); + let public = Keystore::sr25519_generate_new(&store, TEST_KEY_TYPE, None).unwrap(); let path = store.0.read().key_file_path(public.as_ref(), TEST_KEY_TYPE).unwrap(); let permissions = File::open(path).unwrap().metadata().unwrap().permissions(); diff --git a/client/rpc-api/src/author/error.rs b/client/rpc-api/src/author/error.rs index 7ca96bbab..8149a1f8d 100644 --- a/client/rpc-api/src/author/error.rs +++ b/client/rpc-api/src/author/error.rs @@ -47,7 +47,7 @@ pub enum Error { BadKeyType, /// Some random issue with the key store. Shouldn't happen. #[error("The key store is unavailable")] - KeyStoreUnavailable, + KeystoreUnavailable, /// Invalid session keys encoding. #[error("Session keys are not encoded correctly")] InvalidSessionKeys, diff --git a/client/rpc/src/author/mod.rs b/client/rpc/src/author/mod.rs index 2bb88352e..9752a32b1 100644 --- a/client/rpc/src/author/mod.rs +++ b/client/rpc/src/author/mod.rs @@ -40,7 +40,7 @@ use sc_transaction_pool_api::{ use sp_api::ProvideRuntimeApi; use sp_blockchain::HeaderBackend; use sp_core::Bytes; -use sp_keystore::{SyncCryptoStore, SyncCryptoStorePtr}; +use sp_keystore::{Keystore, KeystorePtr}; use sp_runtime::{generic, traits::Block as BlockT}; use sp_session::SessionKeys; @@ -55,7 +55,7 @@ pub struct Author { /// Transactions pool pool: Arc

, /// The key store. - keystore: SyncCryptoStorePtr, + keystore: KeystorePtr, /// Whether to deny unsafe calls deny_unsafe: DenyUnsafe, /// Executor to spawn subscriptions. @@ -67,7 +67,7 @@ impl Author { pub fn new( client: Arc, pool: Arc

, - keystore: SyncCryptoStorePtr, + keystore: KeystorePtr, deny_unsafe: DenyUnsafe, executor: SubscriptionTaskExecutor, ) -> Self { @@ -112,8 +112,8 @@ where self.deny_unsafe.check_if_safe()?; let key_type = key_type.as_str().try_into().map_err(|_| Error::BadKeyType)?; - SyncCryptoStore::insert_unknown(&*self.keystore, key_type, &suri, &public[..]) - .map_err(|_| Error::KeyStoreUnavailable)?; + Keystore::insert(&*self.keystore, key_type, &suri, &public[..]) + .map_err(|_| Error::KeystoreUnavailable)?; Ok(()) } @@ -139,14 +139,14 @@ where .map_err(|e| Error::Client(Box::new(e)))? .ok_or(Error::InvalidSessionKeys)?; - Ok(SyncCryptoStore::has_keys(&*self.keystore, &keys)) + Ok(Keystore::has_keys(&*self.keystore, &keys)) } fn has_key(&self, public_key: Bytes, key_type: String) -> RpcResult { self.deny_unsafe.check_if_safe()?; let key_type = key_type.as_str().try_into().map_err(|_| Error::BadKeyType)?; - Ok(SyncCryptoStore::has_keys(&*self.keystore, &[(public_key.to_vec(), key_type)])) + Ok(Keystore::has_keys(&*self.keystore, &[(public_key.to_vec(), key_type)])) } fn pending_extrinsics(&self) -> RpcResult> { diff --git a/client/rpc/src/author/tests.rs b/client/rpc/src/author/tests.rs index bf880cfbc..209673fce 100644 --- a/client/rpc/src/author/tests.rs +++ b/client/rpc/src/author/tests.rs @@ -36,7 +36,7 @@ use sp_core::{ testing::{ED25519, SR25519}, H256, }; -use sp_keystore::testing::KeyStore; +use sp_keystore::testing::MemoryKeystore; use std::sync::Arc; use substrate_test_runtime_client::{ self, @@ -58,13 +58,13 @@ type FullTransactionPool = BasicPool, Block>, Block struct TestSetup { pub client: Arc>, - pub keystore: Arc, + pub keystore: Arc, pub pool: Arc, } impl Default for TestSetup { fn default() -> Self { - let keystore = Arc::new(KeyStore::new()); + let keystore = Arc::new(MemoryKeystore::new()); let client_builder = substrate_test_runtime_client::TestClientBuilder::new(); let client = Arc::new(client_builder.set_keystore(keystore.clone()).build()); @@ -225,7 +225,7 @@ async fn author_should_insert_key() { keypair.public().0.to_vec().into(), ); api.call::<_, ()>("author_insertKey", params).await.unwrap(); - let pubkeys = SyncCryptoStore::keys(&*setup.keystore, ED25519).unwrap(); + let pubkeys = Keystore::keys(&*setup.keystore, ED25519).unwrap(); assert!( pubkeys.contains(&CryptoTypePublicPair(ed25519::CRYPTO_ID, keypair.public().to_raw_vec())) @@ -240,8 +240,8 @@ async fn author_should_rotate_keys() { let new_pubkeys: Bytes = api.call("author_rotateKeys", EmptyParams::new()).await.unwrap(); let session_keys = SessionKeys::decode(&mut &new_pubkeys[..]).expect("SessionKeys decode successfully"); - let ed25519_pubkeys = SyncCryptoStore::keys(&*setup.keystore, ED25519).unwrap(); - let sr25519_pubkeys = SyncCryptoStore::keys(&*setup.keystore, SR25519).unwrap(); + let ed25519_pubkeys = Keystore::keys(&*setup.keystore, ED25519).unwrap(); + let sr25519_pubkeys = Keystore::keys(&*setup.keystore, SR25519).unwrap(); assert!(ed25519_pubkeys .contains(&CryptoTypePublicPair(ed25519::CRYPTO_ID, session_keys.ed25519.to_raw_vec()))); assert!(sr25519_pubkeys diff --git a/client/service/src/builder.rs b/client/service/src/builder.rs index 91ef65cf1..034d056d6 100644 --- a/client/service/src/builder.rs +++ b/client/service/src/builder.rs @@ -67,7 +67,7 @@ use sp_consensus::block_validation::{ BlockAnnounceValidator, Chain, DefaultBlockAnnounceValidator, }; use sp_core::traits::{CodeExecutor, SpawnNamed}; -use sp_keystore::{CryptoStore, SyncCryptoStore, SyncCryptoStorePtr}; +use sp_keystore::{Keystore, KeystorePtr}; use sp_runtime::traits::{Block as BlockT, BlockIdTo, NumberFor, Zero}; use std::{str::FromStr, sync::Arc, time::SystemTime}; @@ -85,26 +85,22 @@ pub type TFullCallExecutor = type TFullParts = (TFullClient, Arc>, KeystoreContainer, TaskManager); -trait AsCryptoStoreRef { - fn keystore_ref(&self) -> Arc; - fn sync_keystore_ref(&self) -> Arc; +trait AsKeystoreRef { + fn keystore_ref(&self) -> Arc; } -impl AsCryptoStoreRef for Arc +impl AsKeystoreRef for Arc where - T: CryptoStore + SyncCryptoStore + 'static, + T: Keystore + 'static, { - fn keystore_ref(&self) -> Arc { - self.clone() - } - fn sync_keystore_ref(&self) -> Arc { + fn keystore_ref(&self) -> Arc { self.clone() } } /// Construct and hold different layers of Keystore wrappers pub struct KeystoreContainer { - remote: Option>, + remote: Option>, local: Arc, } @@ -127,13 +123,13 @@ impl KeystoreContainer { /// stick around. pub fn set_remote_keystore(&mut self, remote: Arc) where - T: CryptoStore + SyncCryptoStore + 'static, + T: Keystore + 'static, { self.remote = Some(Box::new(remote)) } - /// Returns an adapter to the asynchronous keystore that implements `CryptoStore` - pub fn keystore(&self) -> Arc { + /// Returns an adapter to a `Keystore` implementation. + pub fn keystore(&self) -> Arc { if let Some(c) = self.remote.as_ref() { c.keystore_ref() } else { @@ -141,15 +137,6 @@ impl KeystoreContainer { } } - /// Returns the synchronous keystore wrapper - pub fn sync_keystore(&self) -> SyncCryptoStorePtr { - if let Some(c) = self.remote.as_ref() { - c.sync_keystore_ref() - } else { - self.local.clone() as SyncCryptoStorePtr - } - } - /// Returns the local keystore if available /// /// The function will return None if the available keystore is not a local keystore. @@ -234,7 +221,7 @@ where let client = { let extensions = sc_client_api::execution_extensions::ExecutionExtensions::new( config.execution_strategies.clone(), - Some(keystore_container.sync_keystore()), + Some(keystore_container.keystore()), sc_offchain::OffchainDb::factory_from_backend(&*backend), ); @@ -374,7 +361,7 @@ pub struct SpawnTasksParams<'a, TBl: BlockT, TCl, TExPool, TRpc, Backend> { /// A task manager returned by `new_full_parts`. pub task_manager: &'a mut TaskManager, /// A shared keystore returned by `new_full_parts`. - pub keystore: SyncCryptoStorePtr, + pub keystore: KeystorePtr, /// A shared transaction pool. pub transaction_pool: Arc, /// Builds additional [`RpcModule`]s that should be added to the server @@ -645,7 +632,7 @@ fn gen_rpc_module( spawn_handle: SpawnTaskHandle, client: Arc, transaction_pool: Arc, - keystore: SyncCryptoStorePtr, + keystore: KeystorePtr, system_rpc_tx: TracingUnboundedSender>, config: &Configuration, backend: Arc, diff --git a/client/service/src/client/client.rs b/client/service/src/client/client.rs index 3613cc760..d5212021f 100644 --- a/client/service/src/client/client.rs +++ b/client/service/src/client/client.rs @@ -65,7 +65,7 @@ use sp_core::{ traits::SpawnNamed, }; #[cfg(feature = "test-helpers")] -use sp_keystore::SyncCryptoStorePtr; +use sp_keystore::KeystorePtr; use sp_runtime::{ generic::{BlockId, SignedBlock}, traits::{ @@ -161,7 +161,7 @@ pub fn new_in_mem( backend: Arc>, executor: E, genesis_block_builder: G, - keystore: Option, + keystore: Option, prometheus_registry: Option, telemetry: Option, spawn_handle: Box, @@ -224,7 +224,7 @@ pub fn new_with_backend( backend: Arc, executor: E, genesis_block_builder: G, - keystore: Option, + keystore: Option, spawn_handle: Box, prometheus_registry: Option, telemetry: Option, diff --git a/frame/benchmarking/src/baseline.rs b/frame/benchmarking/src/baseline.rs index 11d4ba501..1f6e9c5a6 100644 --- a/frame/benchmarking/src/baseline.rs +++ b/frame/benchmarking/src/baseline.rs @@ -160,12 +160,12 @@ pub mod mock { impl super::Config for Test {} pub fn new_test_ext() -> sp_io::TestExternalities { - use sp_keystore::{testing::KeyStore, KeystoreExt, SyncCryptoStorePtr}; + use sp_keystore::{testing::MemoryKeystore, KeystoreExt, KeystorePtr}; use sp_std::sync::Arc; let t = frame_system::GenesisConfig::default().build_storage::().unwrap(); let mut ext = sp_io::TestExternalities::new(t); - ext.register_extension(KeystoreExt(Arc::new(KeyStore::new()) as SyncCryptoStorePtr)); + ext.register_extension(KeystoreExt(Arc::new(MemoryKeystore::new()) as KeystorePtr)); ext } diff --git a/frame/contracts/src/tests.rs b/frame/contracts/src/tests.rs index d74b08243..ba9873f07 100644 --- a/frame/contracts/src/tests.rs +++ b/frame/contracts/src/tests.rs @@ -44,7 +44,7 @@ use frame_support::{ use frame_system::{self as system, EventRecord, Phase}; use pretty_assertions::{assert_eq, assert_ne}; use sp_io::hashing::blake2_256; -use sp_keystore::{testing::KeyStore, KeystoreExt}; +use sp_keystore::{testing::MemoryKeystore, KeystoreExt}; use sp_runtime::{ testing::{Header, H256}, traits::{BlakeTwo256, Convert, Hash, IdentityLookup}, @@ -440,7 +440,7 @@ impl ExtBuilder { .assimilate_storage(&mut t) .unwrap(); let mut ext = sp_io::TestExternalities::new(t); - ext.register_extension(KeystoreExt(Arc::new(KeyStore::new()))); + ext.register_extension(KeystoreExt(Arc::new(MemoryKeystore::new()))); ext.execute_with(|| System::set_block_number(1)); ext } diff --git a/frame/examples/offchain-worker/src/tests.rs b/frame/examples/offchain-worker/src/tests.rs index 75a42e872..c51359043 100644 --- a/frame/examples/offchain-worker/src/tests.rs +++ b/frame/examples/offchain-worker/src/tests.rs @@ -29,7 +29,7 @@ use sp_core::{ }; use std::sync::Arc; -use sp_keystore::{testing::KeyStore, KeystoreExt, SyncCryptoStore}; +use sp_keystore::{testing::MemoryKeystore, Keystore, KeystoreExt}; use sp_runtime::{ testing::{Header, TestXt}, traits::{BlakeTwo256, Extrinsic as ExtrinsicT, IdentifyAccount, IdentityLookup, Verify}, @@ -205,8 +205,8 @@ fn should_submit_signed_transaction_on_chain() { let (offchain, offchain_state) = testing::TestOffchainExt::new(); let (pool, pool_state) = testing::TestTransactionPoolExt::new(); - let keystore = KeyStore::new(); - SyncCryptoStore::sr25519_generate_new( + let keystore = MemoryKeystore::new(); + Keystore::sr25519_generate_new( &keystore, crate::crypto::Public::ID, Some(&format!("{}/hunter1", PHRASE)), @@ -239,16 +239,16 @@ fn should_submit_unsigned_transaction_on_chain_for_any_account() { let (offchain, offchain_state) = testing::TestOffchainExt::new(); let (pool, pool_state) = testing::TestTransactionPoolExt::new(); - let keystore = KeyStore::new(); + let keystore = MemoryKeystore::new(); - SyncCryptoStore::sr25519_generate_new( + Keystore::sr25519_generate_new( &keystore, crate::crypto::Public::ID, Some(&format!("{}/hunter1", PHRASE)), ) .unwrap(); - let public_key = *SyncCryptoStore::sr25519_public_keys(&keystore, crate::crypto::Public::ID) + let public_key = *Keystore::sr25519_public_keys(&keystore, crate::crypto::Public::ID) .get(0) .unwrap(); @@ -298,16 +298,16 @@ fn should_submit_unsigned_transaction_on_chain_for_all_accounts() { let (offchain, offchain_state) = testing::TestOffchainExt::new(); let (pool, pool_state) = testing::TestTransactionPoolExt::new(); - let keystore = KeyStore::new(); + let keystore = MemoryKeystore::new(); - SyncCryptoStore::sr25519_generate_new( + Keystore::sr25519_generate_new( &keystore, crate::crypto::Public::ID, Some(&format!("{}/hunter1", PHRASE)), ) .unwrap(); - let public_key = *SyncCryptoStore::sr25519_public_keys(&keystore, crate::crypto::Public::ID) + let public_key = *Keystore::sr25519_public_keys(&keystore, crate::crypto::Public::ID) .get(0) .unwrap(); @@ -355,7 +355,7 @@ fn should_submit_raw_unsigned_transaction_on_chain() { let (offchain, offchain_state) = testing::TestOffchainExt::new(); let (pool, pool_state) = testing::TestTransactionPoolExt::new(); - let keystore = KeyStore::new(); + let keystore = MemoryKeystore::new(); let mut t = sp_io::TestExternalities::default(); t.register_extension(OffchainWorkerExt::new(offchain)); diff --git a/frame/nfts/src/mock.rs b/frame/nfts/src/mock.rs index a9db1a62c..f21547b72 100644 --- a/frame/nfts/src/mock.rs +++ b/frame/nfts/src/mock.rs @@ -25,7 +25,7 @@ use frame_support::{ traits::{AsEnsureOriginWithArg, ConstU32, ConstU64}, }; use sp_core::H256; -use sp_keystore::{testing::KeyStore, KeystoreExt}; +use sp_keystore::{testing::MemoryKeystore, KeystoreExt}; use sp_runtime::{ testing::Header, traits::{BlakeTwo256, IdentifyAccount, IdentityLookup, Verify}, @@ -130,7 +130,7 @@ impl Config for Test { pub(crate) fn new_test_ext() -> sp_io::TestExternalities { let t = frame_system::GenesisConfig::default().build_storage::().unwrap(); - let keystore = KeyStore::new(); + let keystore = MemoryKeystore::new(); let mut ext = sp_io::TestExternalities::new(t); ext.register_extension(KeystoreExt(Arc::new(keystore))); ext.execute_with(|| System::set_block_number(1)); diff --git a/primitives/application-crypto/test/src/ecdsa.rs b/primitives/application-crypto/test/src/ecdsa.rs index df3be6f93..7b96b42d5 100644 --- a/primitives/application-crypto/test/src/ecdsa.rs +++ b/primitives/application-crypto/test/src/ecdsa.rs @@ -19,7 +19,7 @@ use sp_api::ProvideRuntimeApi; use sp_application_crypto::ecdsa::{AppPair, AppPublic}; use sp_core::{crypto::Pair, testing::ECDSA}; -use sp_keystore::{testing::KeyStore, SyncCryptoStore}; +use sp_keystore::{testing::MemoryKeystore, Keystore}; use std::sync::Arc; use substrate_test_runtime_client::{ runtime::TestAPI, DefaultTestClientBuilderExt, TestClientBuilder, TestClientBuilderExt, @@ -27,14 +27,14 @@ use substrate_test_runtime_client::{ #[test] fn ecdsa_works_in_runtime() { - let keystore = Arc::new(KeyStore::new()); + let keystore = Arc::new(MemoryKeystore::new()); let test_client = TestClientBuilder::new().set_keystore(keystore.clone()).build(); let (signature, public) = test_client .runtime_api() .test_ecdsa_crypto(test_client.chain_info().genesis_hash) .expect("Tests `ecdsa` crypto."); - let supported_keys = SyncCryptoStore::keys(&*keystore, ECDSA).unwrap(); + let supported_keys = Keystore::keys(&*keystore, ECDSA).unwrap(); assert!(supported_keys.contains(&public.clone().into())); assert!(AppPair::verify(&signature, "ecdsa", &AppPublic::from(public))); } diff --git a/primitives/application-crypto/test/src/ed25519.rs b/primitives/application-crypto/test/src/ed25519.rs index 8ce08717a..1cf2b574b 100644 --- a/primitives/application-crypto/test/src/ed25519.rs +++ b/primitives/application-crypto/test/src/ed25519.rs @@ -20,7 +20,7 @@ use sp_api::ProvideRuntimeApi; use sp_application_crypto::ed25519::{AppPair, AppPublic}; use sp_core::{crypto::Pair, testing::ED25519}; -use sp_keystore::{testing::KeyStore, SyncCryptoStore}; +use sp_keystore::{testing::MemoryKeystore, Keystore}; use std::sync::Arc; use substrate_test_runtime_client::{ runtime::TestAPI, DefaultTestClientBuilderExt, TestClientBuilder, TestClientBuilderExt, @@ -28,14 +28,14 @@ use substrate_test_runtime_client::{ #[test] fn ed25519_works_in_runtime() { - let keystore = Arc::new(KeyStore::new()); + let keystore = Arc::new(MemoryKeystore::new()); let test_client = TestClientBuilder::new().set_keystore(keystore.clone()).build(); let (signature, public) = test_client .runtime_api() .test_ed25519_crypto(test_client.chain_info().genesis_hash) .expect("Tests `ed25519` crypto."); - let supported_keys = SyncCryptoStore::keys(&*keystore, ED25519).unwrap(); + let supported_keys = Keystore::keys(&*keystore, ED25519).unwrap(); assert!(supported_keys.contains(&public.clone().into())); assert!(AppPair::verify(&signature, "ed25519", &AppPublic::from(public))); } diff --git a/primitives/application-crypto/test/src/sr25519.rs b/primitives/application-crypto/test/src/sr25519.rs index 4c1d0f1ef..aa8f75c0d 100644 --- a/primitives/application-crypto/test/src/sr25519.rs +++ b/primitives/application-crypto/test/src/sr25519.rs @@ -20,7 +20,7 @@ use sp_api::ProvideRuntimeApi; use sp_application_crypto::sr25519::{AppPair, AppPublic}; use sp_core::{crypto::Pair, testing::SR25519}; -use sp_keystore::{testing::KeyStore, SyncCryptoStore}; +use sp_keystore::{testing::MemoryKeystore, Keystore}; use std::sync::Arc; use substrate_test_runtime_client::{ runtime::TestAPI, DefaultTestClientBuilderExt, TestClientBuilder, TestClientBuilderExt, @@ -28,14 +28,14 @@ use substrate_test_runtime_client::{ #[test] fn sr25519_works_in_runtime() { - let keystore = Arc::new(KeyStore::new()); + let keystore = Arc::new(MemoryKeystore::new()); let test_client = TestClientBuilder::new().set_keystore(keystore.clone()).build(); let (signature, public) = test_client .runtime_api() .test_sr25519_crypto(test_client.chain_info().genesis_hash) .expect("Tests `sr25519` crypto."); - let supported_keys = SyncCryptoStore::keys(&*keystore, SR25519).unwrap(); + let supported_keys = Keystore::keys(&*keystore, SR25519).unwrap(); assert!(supported_keys.contains(&public.clone().into())); assert!(AppPair::verify(&signature, "sr25519", &AppPublic::from(public))); } diff --git a/primitives/consensus/beefy/src/commitment.rs b/primitives/consensus/beefy/src/commitment.rs index f824c90e2..6ae17b06d 100644 --- a/primitives/consensus/beefy/src/commitment.rs +++ b/primitives/consensus/beefy/src/commitment.rs @@ -253,7 +253,7 @@ mod tests { use crate::{crypto, known_payloads, KEY_TYPE}; use codec::Decode; use sp_core::{keccak_256, Pair}; - use sp_keystore::{testing::KeyStore, SyncCryptoStore, SyncCryptoStorePtr}; + use sp_keystore::{testing::MemoryKeystore, Keystore, KeystorePtr}; type TestCommitment = Commitment; type TestSignedCommitment = SignedCommitment; @@ -263,20 +263,18 @@ mod tests { // The mock signatures are equivalent to the ones produced by the BEEFY keystore fn mock_signatures() -> (crypto::Signature, crypto::Signature) { - let store: SyncCryptoStorePtr = KeyStore::new().into(); + let store: KeystorePtr = MemoryKeystore::new().into(); let alice = sp_core::ecdsa::Pair::from_string("//Alice", None).unwrap(); - let _ = - SyncCryptoStore::insert_unknown(&*store, KEY_TYPE, "//Alice", alice.public().as_ref()) - .unwrap(); + let _ = Keystore::insert(&*store, KEY_TYPE, "//Alice", alice.public().as_ref()).unwrap(); let msg = keccak_256(b"This is the first message"); - let sig1 = SyncCryptoStore::ecdsa_sign_prehashed(&*store, KEY_TYPE, &alice.public(), &msg) + let sig1 = Keystore::ecdsa_sign_prehashed(&*store, KEY_TYPE, &alice.public(), &msg) .unwrap() .unwrap(); let msg = keccak_256(b"This is the second message"); - let sig2 = SyncCryptoStore::ecdsa_sign_prehashed(&*store, KEY_TYPE, &alice.public(), &msg) + let sig2 = Keystore::ecdsa_sign_prehashed(&*store, KEY_TYPE, &alice.public(), &msg) .unwrap() .unwrap(); diff --git a/primitives/consensus/beefy/src/witness.rs b/primitives/consensus/beefy/src/witness.rs index 9cc31d54b..70b924c0a 100644 --- a/primitives/consensus/beefy/src/witness.rs +++ b/primitives/consensus/beefy/src/witness.rs @@ -76,7 +76,7 @@ impl SignedCommitmentWitness (crypto::Signature, crypto::Signature) { - let store: SyncCryptoStorePtr = KeyStore::new().into(); + let store: KeystorePtr = MemoryKeystore::new().into(); let alice = sp_core::ecdsa::Pair::from_string("//Alice", None).unwrap(); - let _ = - SyncCryptoStore::insert_unknown(&*store, KEY_TYPE, "//Alice", alice.public().as_ref()) - .unwrap(); + let _ = Keystore::insert(&*store, KEY_TYPE, "//Alice", alice.public().as_ref()).unwrap(); let msg = keccak_256(b"This is the first message"); - let sig1 = SyncCryptoStore::ecdsa_sign_prehashed(&*store, KEY_TYPE, &alice.public(), &msg) + let sig1 = Keystore::ecdsa_sign_prehashed(&*store, KEY_TYPE, &alice.public(), &msg) .unwrap() .unwrap(); let msg = keccak_256(b"This is the second message"); - let sig2 = SyncCryptoStore::ecdsa_sign_prehashed(&*store, KEY_TYPE, &alice.public(), &msg) + let sig2 = Keystore::ecdsa_sign_prehashed(&*store, KEY_TYPE, &alice.public(), &msg) .unwrap() .unwrap(); diff --git a/primitives/consensus/grandpa/src/lib.rs b/primitives/consensus/grandpa/src/lib.rs index 26cee07b8..32fc563b1 100644 --- a/primitives/consensus/grandpa/src/lib.rs +++ b/primitives/consensus/grandpa/src/lib.rs @@ -28,7 +28,7 @@ use serde::Serialize; use codec::{Codec, Decode, Encode, Input}; use scale_info::TypeInfo; #[cfg(feature = "std")] -use sp_keystore::{SyncCryptoStore, SyncCryptoStorePtr}; +use sp_keystore::{Keystore, KeystorePtr}; use sp_runtime::{ traits::{Header as HeaderT, NumberFor}, ConsensusEngineId, RuntimeDebug, @@ -444,7 +444,7 @@ where /// Localizes the message to the given set and round and signs the payload. #[cfg(feature = "std")] pub fn sign_message( - keystore: SyncCryptoStorePtr, + keystore: KeystorePtr, message: grandpa::Message, public: AuthorityId, round: RoundNumber, @@ -458,7 +458,7 @@ where use sp_core::crypto::Public; let encoded = localized_payload(round, set_id, &message); - let signature = SyncCryptoStore::sign_with( + let signature = Keystore::sign_with( &*keystore, AuthorityId::ID, &public.to_public_crypto_pair(), diff --git a/primitives/io/src/lib.rs b/primitives/io/src/lib.rs index 8c3cdc668..65c583330 100644 --- a/primitives/io/src/lib.rs +++ b/primitives/io/src/lib.rs @@ -43,7 +43,7 @@ use sp_core::{ traits::TaskExecutorExt, }; #[cfg(feature = "std")] -use sp_keystore::{KeystoreExt, SyncCryptoStore}; +use sp_keystore::{Keystore, KeystoreExt}; use sp_core::{ crypto::KeyTypeId, @@ -734,7 +734,7 @@ pub trait Crypto { let keystore = &***self .extension::() .expect("No `keystore` associated for the current context!"); - SyncCryptoStore::ed25519_public_keys(keystore, id) + Keystore::ed25519_public_keys(keystore, id) } /// Generate an `ed22519` key for the given key type using an optional `seed` and @@ -748,8 +748,7 @@ pub trait Crypto { let keystore = &***self .extension::() .expect("No `keystore` associated for the current context!"); - SyncCryptoStore::ed25519_generate_new(keystore, id, seed) - .expect("`ed25519_generate` failed") + Keystore::ed25519_generate_new(keystore, id, seed).expect("`ed25519_generate` failed") } /// Sign the given `msg` with the `ed25519` key that corresponds to the given public key and @@ -765,7 +764,7 @@ pub trait Crypto { let keystore = &***self .extension::() .expect("No `keystore` associated for the current context!"); - SyncCryptoStore::sign_with(keystore, id, &pub_key.into(), msg) + Keystore::sign_with(keystore, id, &pub_key.into(), msg) .ok() .flatten() .and_then(|sig| ed25519::Signature::from_slice(&sig)) @@ -877,7 +876,7 @@ pub trait Crypto { let keystore = &***self .extension::() .expect("No `keystore` associated for the current context!"); - SyncCryptoStore::sr25519_public_keys(keystore, id) + Keystore::sr25519_public_keys(keystore, id) } /// Generate an `sr22519` key for the given key type using an optional seed and @@ -891,8 +890,7 @@ pub trait Crypto { let keystore = &***self .extension::() .expect("No `keystore` associated for the current context!"); - SyncCryptoStore::sr25519_generate_new(keystore, id, seed) - .expect("`sr25519_generate` failed") + Keystore::sr25519_generate_new(keystore, id, seed).expect("`sr25519_generate` failed") } /// Sign the given `msg` with the `sr25519` key that corresponds to the given public key and @@ -908,7 +906,7 @@ pub trait Crypto { let keystore = &***self .extension::() .expect("No `keystore` associated for the current context!"); - SyncCryptoStore::sign_with(keystore, id, &pub_key.into(), msg) + Keystore::sign_with(keystore, id, &pub_key.into(), msg) .ok() .flatten() .and_then(|sig| sr25519::Signature::from_slice(&sig)) @@ -927,7 +925,7 @@ pub trait Crypto { let keystore = &***self .extension::() .expect("No `keystore` associated for the current context!"); - SyncCryptoStore::ecdsa_public_keys(keystore, id) + Keystore::ecdsa_public_keys(keystore, id) } /// Generate an `ecdsa` key for the given key type using an optional `seed` and @@ -941,7 +939,7 @@ pub trait Crypto { let keystore = &***self .extension::() .expect("No `keystore` associated for the current context!"); - SyncCryptoStore::ecdsa_generate_new(keystore, id, seed).expect("`ecdsa_generate` failed") + Keystore::ecdsa_generate_new(keystore, id, seed).expect("`ecdsa_generate` failed") } /// Sign the given `msg` with the `ecdsa` key that corresponds to the given public key and @@ -957,7 +955,7 @@ pub trait Crypto { let keystore = &***self .extension::() .expect("No `keystore` associated for the current context!"); - SyncCryptoStore::sign_with(keystore, id, &pub_key.into(), msg) + Keystore::sign_with(keystore, id, &pub_key.into(), msg) .ok() .flatten() .and_then(|sig| ecdsa::Signature::from_slice(&sig)) @@ -976,7 +974,7 @@ pub trait Crypto { let keystore = &***self .extension::() .expect("No `keystore` associated for the current context!"); - SyncCryptoStore::ecdsa_sign_prehashed(keystore, id, pub_key, msg).ok().flatten() + Keystore::ecdsa_sign_prehashed(keystore, id, pub_key, msg).ok().flatten() } /// Verify `ecdsa` signature. diff --git a/primitives/keystore/Cargo.toml b/primitives/keystore/Cargo.toml index 9386cb5d1..543f01059 100644 --- a/primitives/keystore/Cargo.toml +++ b/primitives/keystore/Cargo.toml @@ -13,7 +13,6 @@ documentation = "https://docs.rs/sp-core" targets = ["x86_64-unknown-linux-gnu"] [dependencies] -async-trait = "0.1.57" codec = { package = "parity-scale-codec", version = "3.2.2", default-features = false, features = ["derive"] } futures = "0.3.21" merlin = { version = "2.0", default-features = false } diff --git a/primitives/keystore/src/lib.rs b/primitives/keystore/src/lib.rs index 17c435483..e0c00967b 100644 --- a/primitives/keystore/src/lib.rs +++ b/primitives/keystore/src/lib.rs @@ -20,15 +20,13 @@ pub mod testing; pub mod vrf; use crate::vrf::{VRFSignature, VRFTranscriptData}; -use async_trait::async_trait; -use futures::{executor::block_on, future::join_all}; use sp_core::{ crypto::{CryptoTypePublicPair, KeyTypeId}, ecdsa, ed25519, sr25519, }; use std::sync::Arc; -/// CryptoStore error +/// Keystore error #[derive(Debug, thiserror::Error)] pub enum Error { /// Public key type is not supported @@ -45,179 +43,8 @@ pub enum Error { Other(String), } -/// Something that generates, stores and provides access to keys. -#[async_trait] -pub trait CryptoStore: Send + Sync { - /// Returns all sr25519 public keys for the given key type. - async fn sr25519_public_keys(&self, id: KeyTypeId) -> Vec; - /// Generate a new sr25519 key pair for the given key type and an optional seed. - /// - /// If the given seed is `Some(_)`, the key pair will only be stored in memory. - /// - /// Returns the public key of the generated key pair. - async fn sr25519_generate_new( - &self, - id: KeyTypeId, - seed: Option<&str>, - ) -> Result; - /// Returns all ed25519 public keys for the given key type. - async fn ed25519_public_keys(&self, id: KeyTypeId) -> Vec; - /// Generate a new ed25519 key pair for the given key type and an optional seed. - /// - /// If the given seed is `Some(_)`, the key pair will only be stored in memory. - /// - /// Returns the public key of the generated key pair. - async fn ed25519_generate_new( - &self, - id: KeyTypeId, - seed: Option<&str>, - ) -> Result; - /// Returns all ecdsa public keys for the given key type. - async fn ecdsa_public_keys(&self, id: KeyTypeId) -> Vec; - /// Generate a new ecdsa key pair for the given key type and an optional seed. - /// - /// If the given seed is `Some(_)`, the key pair will only be stored in memory. - /// - /// Returns the public key of the generated key pair. - async fn ecdsa_generate_new( - &self, - id: KeyTypeId, - seed: Option<&str>, - ) -> Result; - - /// Insert a new key. This doesn't require any known of the crypto; but a public key must be - /// manually provided. - /// - /// Places it into the file system store. - /// - /// `Err` if there's some sort of weird filesystem error, but should generally be `Ok`. - async fn insert_unknown(&self, id: KeyTypeId, suri: &str, public: &[u8]) -> Result<(), ()>; - - /// Find intersection between provided keys and supported keys - /// - /// Provided a list of (CryptoTypeId,[u8]) pairs, this would return - /// a filtered set of public keys which are supported by the keystore. - async fn supported_keys( - &self, - id: KeyTypeId, - keys: Vec, - ) -> Result, Error>; - /// List all supported keys - /// - /// Returns a set of public keys the signer supports. - async fn keys(&self, id: KeyTypeId) -> Result, Error>; - - /// Checks if the private keys for the given public key and key type combinations exist. - /// - /// Returns `true` iff all private keys could be found. - async fn has_keys(&self, public_keys: &[(Vec, KeyTypeId)]) -> bool; - - /// Sign with key - /// - /// Signs a message with the private key that matches - /// the public key passed. - /// - /// Returns the SCALE encoded signature if key is found and supported, `None` if the key doesn't - /// exist or an error when something failed. - async fn sign_with( - &self, - id: KeyTypeId, - key: &CryptoTypePublicPair, - msg: &[u8], - ) -> Result>, Error>; - - /// Sign with any key - /// - /// Given a list of public keys, find the first supported key and - /// sign the provided message with that key. - /// - /// Returns a tuple of the used key and the SCALE encoded signature or `None` if no key could - /// be found to sign. - async fn sign_with_any( - &self, - id: KeyTypeId, - keys: Vec, - msg: &[u8], - ) -> Result)>, Error> { - if keys.len() == 1 { - return Ok(self.sign_with(id, &keys[0], msg).await?.map(|s| (keys[0].clone(), s))) - } else { - for k in self.supported_keys(id, keys).await? { - if let Ok(Some(sign)) = self.sign_with(id, &k, msg).await { - return Ok(Some((k, sign))) - } - } - } - - Ok(None) - } - - /// Sign with all keys - /// - /// Provided a list of public keys, sign a message with - /// each key given that the key is supported. - /// - /// Returns a list of `Result`s each representing the SCALE encoded - /// signature of each key, `None` if the key doesn't exist or a error when something failed. - async fn sign_with_all( - &self, - id: KeyTypeId, - keys: Vec, - msg: &[u8], - ) -> Result>, Error>>, ()> { - let futs = keys.iter().map(|k| self.sign_with(id, k, msg)); - - Ok(join_all(futs).await) - } - - /// Generate VRF signature for given transcript data. - /// - /// Receives KeyTypeId and Public key to be able to map - /// them to a private key that exists in the keystore which - /// is, in turn, used for signing the provided transcript. - /// - /// Returns a result containing the signature data. - /// Namely, VRFOutput and VRFProof which are returned - /// inside the `VRFSignature` container struct. - /// - /// This function will return `None` if the given `key_type` and `public` combination - /// doesn't exist in the keystore or an `Err` when something failed. - async fn sr25519_vrf_sign( - &self, - key_type: KeyTypeId, - public: &sr25519::Public, - transcript_data: VRFTranscriptData, - ) -> Result, Error>; - - /// Generate an ECDSA signature for a given pre-hashed message. - /// - /// Receives [`KeyTypeId`] and an [`ecdsa::Public`] key to be able to map - /// them to a private key that exists in the keystore. This private key is, - /// in turn, used for signing the provided pre-hashed message. - /// - /// The `msg` argument provided should be a hashed message for which an - /// ECDSA signature should be generated. - /// - /// Returns an [`ecdsa::Signature`] or `None` in case the given `id` and - /// `public` combination doesn't exist in the keystore. An `Err` will be - /// returned if generating the signature itself failed. - async fn ecdsa_sign_prehashed( - &self, - id: KeyTypeId, - public: &ecdsa::Public, - msg: &[u8; 32], - ) -> Result, Error>; -} - -/// Sync version of the CryptoStore -/// -/// Some parts of Substrate still rely on a sync version of the `CryptoStore`. -/// To make the transition easier this auto trait wraps any async `CryptoStore` and -/// exposes a `sync` interface using `block_on`. Usage of this is deprecated and it -/// will be removed as soon as the internal usage has transitioned successfully. -/// If you are starting out building something new **do not use this**, -/// instead, use [`CryptoStore`]. -pub trait SyncCryptoStore: CryptoStore + Send + Sync { +/// Something that generates, stores and provides access to secret keys. +pub trait Keystore: Send + Sync { /// Returns all sr25519 public keys for the given key type. fn sr25519_public_keys(&self, id: KeyTypeId) -> Vec; @@ -257,30 +84,13 @@ pub trait SyncCryptoStore: CryptoStore + Send + Sync { fn ecdsa_generate_new(&self, id: KeyTypeId, seed: Option<&str>) -> Result; - /// Insert a new key. This doesn't require any known of the crypto; but a public key must be - /// manually provided. - /// - /// Places it into the file system store. - /// - /// `Err` if there's some sort of weird filesystem error, but should generally be `Ok`. - fn insert_unknown(&self, key_type: KeyTypeId, suri: &str, public: &[u8]) -> Result<(), ()>; - - /// Find intersection between provided keys and supported keys - /// - /// Provided a list of (CryptoTypeId,[u8]) pairs, this would return - /// a filtered set of public keys which are supported by the keystore. - fn supported_keys( - &self, - id: KeyTypeId, - keys: Vec, - ) -> Result, Error>; + /// Insert a new secret key. + fn insert(&self, key_type: KeyTypeId, suri: &str, public: &[u8]) -> Result<(), ()>; /// List all supported keys /// /// Returns a set of public keys the signer supports. - fn keys(&self, id: KeyTypeId) -> Result, Error> { - block_on(CryptoStore::keys(self, id)) - } + fn keys(&self, id: KeyTypeId) -> Result, Error>; /// Checks if the private keys for the given public key and key type combinations exist. /// @@ -301,50 +111,6 @@ pub trait SyncCryptoStore: CryptoStore + Send + Sync { msg: &[u8], ) -> Result>, Error>; - /// Sign with any key - /// - /// Given a list of public keys, find the first supported key and - /// sign the provided message with that key. - /// - /// Returns a tuple of the used key and the SCALE encoded signature or `None` if no key could - /// be found to sign. - fn sign_with_any( - &self, - id: KeyTypeId, - keys: Vec, - msg: &[u8], - ) -> Result)>, Error> { - if keys.len() == 1 { - return Ok( - SyncCryptoStore::sign_with(self, id, &keys[0], msg)?.map(|s| (keys[0].clone(), s)) - ) - } else { - for k in SyncCryptoStore::supported_keys(self, id, keys)? { - if let Ok(Some(sign)) = SyncCryptoStore::sign_with(self, id, &k, msg) { - return Ok(Some((k, sign))) - } - } - } - - Ok(None) - } - - /// Sign with all keys - /// - /// Provided a list of public keys, sign a message with - /// each key given that the key is supported. - /// - /// Returns a list of `Result`s each representing the SCALE encoded - /// signature of each key, `None` if the key doesn't exist or an error when something failed. - fn sign_with_all( - &self, - id: KeyTypeId, - keys: Vec, - msg: &[u8], - ) -> Result>, Error>>, ()> { - Ok(keys.iter().map(|k| SyncCryptoStore::sign_with(self, id, k, msg)).collect()) - } - /// Generate VRF signature for given transcript data. /// /// Receives KeyTypeId and Public key to be able to map @@ -385,9 +151,9 @@ pub trait SyncCryptoStore: CryptoStore + Send + Sync { } /// A pointer to a keystore. -pub type SyncCryptoStorePtr = Arc; +pub type KeystorePtr = Arc; sp_externalities::decl_extension! { /// The keystore extension to register/retrieve from the externalities. - pub struct KeystoreExt(SyncCryptoStorePtr); + pub struct KeystoreExt(KeystorePtr); } diff --git a/primitives/keystore/src/testing.rs b/primitives/keystore/src/testing.rs index 5058a0650..fecc11342 100644 --- a/primitives/keystore/src/testing.rs +++ b/primitives/keystore/src/testing.rs @@ -24,23 +24,19 @@ use sp_core::{ use crate::{ vrf::{make_transcript, VRFSignature, VRFTranscriptData}, - CryptoStore, Error, SyncCryptoStore, SyncCryptoStorePtr, + Error, Keystore, KeystorePtr, }; -use async_trait::async_trait; use parking_lot::RwLock; -use std::{ - collections::{HashMap, HashSet}, - sync::Arc, -}; +use std::{collections::HashMap, sync::Arc}; /// A keystore implementation usable in tests. #[derive(Default)] -pub struct KeyStore { +pub struct MemoryKeystore { /// `KeyTypeId` maps to public keys and public keys map to private keys. keys: Arc, String>>>>, } -impl KeyStore { +impl MemoryKeystore { /// Creates a new instance of `Self`. pub fn new() -> Self { Self::default() @@ -71,93 +67,7 @@ impl KeyStore { } } -#[async_trait] -impl CryptoStore for KeyStore { - async fn keys(&self, id: KeyTypeId) -> Result, Error> { - SyncCryptoStore::keys(self, id) - } - - async fn sr25519_public_keys(&self, id: KeyTypeId) -> Vec { - SyncCryptoStore::sr25519_public_keys(self, id) - } - - async fn sr25519_generate_new( - &self, - id: KeyTypeId, - seed: Option<&str>, - ) -> Result { - SyncCryptoStore::sr25519_generate_new(self, id, seed) - } - - async fn ed25519_public_keys(&self, id: KeyTypeId) -> Vec { - SyncCryptoStore::ed25519_public_keys(self, id) - } - - async fn ed25519_generate_new( - &self, - id: KeyTypeId, - seed: Option<&str>, - ) -> Result { - SyncCryptoStore::ed25519_generate_new(self, id, seed) - } - - async fn ecdsa_public_keys(&self, id: KeyTypeId) -> Vec { - SyncCryptoStore::ecdsa_public_keys(self, id) - } - - async fn ecdsa_generate_new( - &self, - id: KeyTypeId, - seed: Option<&str>, - ) -> Result { - SyncCryptoStore::ecdsa_generate_new(self, id, seed) - } - - async fn insert_unknown(&self, id: KeyTypeId, suri: &str, public: &[u8]) -> Result<(), ()> { - SyncCryptoStore::insert_unknown(self, id, suri, public) - } - - async fn has_keys(&self, public_keys: &[(Vec, KeyTypeId)]) -> bool { - SyncCryptoStore::has_keys(self, public_keys) - } - - async fn supported_keys( - &self, - id: KeyTypeId, - keys: Vec, - ) -> std::result::Result, Error> { - SyncCryptoStore::supported_keys(self, id, keys) - } - - async fn sign_with( - &self, - id: KeyTypeId, - key: &CryptoTypePublicPair, - msg: &[u8], - ) -> Result>, Error> { - SyncCryptoStore::sign_with(self, id, key, msg) - } - - async fn sr25519_vrf_sign( - &self, - key_type: KeyTypeId, - public: &sr25519::Public, - transcript_data: VRFTranscriptData, - ) -> Result, Error> { - SyncCryptoStore::sr25519_vrf_sign(self, key_type, public, transcript_data) - } - - async fn ecdsa_sign_prehashed( - &self, - id: KeyTypeId, - public: &ecdsa::Public, - msg: &[u8; 32], - ) -> Result, Error> { - SyncCryptoStore::ecdsa_sign_prehashed(self, id, public, msg) - } -} - -impl SyncCryptoStore for KeyStore { +impl Keystore for MemoryKeystore { fn keys(&self, id: KeyTypeId) -> Result, Error> { self.keys .read() @@ -304,7 +214,7 @@ impl SyncCryptoStore for KeyStore { } } - fn insert_unknown(&self, id: KeyTypeId, suri: &str, public: &[u8]) -> Result<(), ()> { + fn insert(&self, id: KeyTypeId, suri: &str, public: &[u8]) -> Result<(), ()> { self.keys .write() .entry(id) @@ -319,17 +229,6 @@ impl SyncCryptoStore for KeyStore { .all(|(k, t)| self.keys.read().get(t).and_then(|s| s.get(k)).is_some()) } - fn supported_keys( - &self, - id: KeyTypeId, - keys: Vec, - ) -> std::result::Result, Error> { - let provided_keys = keys.into_iter().collect::>(); - let all_keys = SyncCryptoStore::keys(self, id)?.into_iter().collect::>(); - - Ok(provided_keys.intersection(&all_keys).cloned().collect()) - } - fn sign_with( &self, id: KeyTypeId, @@ -386,14 +285,8 @@ impl SyncCryptoStore for KeyStore { } } -impl Into for KeyStore { - fn into(self) -> SyncCryptoStorePtr { - Arc::new(self) - } -} - -impl Into> for KeyStore { - fn into(self) -> Arc { +impl Into for MemoryKeystore { + fn into(self) -> KeystorePtr { Arc::new(self) } } @@ -401,7 +294,7 @@ impl Into> for KeyStore { #[cfg(test)] mod tests { use super::*; - use crate::{vrf::VRFTranscriptValue, SyncCryptoStore}; + use crate::vrf::VRFTranscriptValue; use sp_core::{ sr25519, testing::{ECDSA, ED25519, SR25519}, @@ -409,34 +302,33 @@ mod tests { #[test] fn store_key_and_extract() { - let store = KeyStore::new(); + let store = MemoryKeystore::new(); - let public = - SyncCryptoStore::ed25519_generate_new(&store, ED25519, None).expect("Generates key"); + let public = Keystore::ed25519_generate_new(&store, ED25519, None).expect("Generates key"); - let public_keys = SyncCryptoStore::keys(&store, ED25519).unwrap(); + let public_keys = Keystore::keys(&store, ED25519).unwrap(); assert!(public_keys.contains(&public.into())); } #[test] fn store_unknown_and_extract_it() { - let store = KeyStore::new(); + let store = MemoryKeystore::new(); let secret_uri = "//Alice"; let key_pair = sr25519::Pair::from_string(secret_uri, None).expect("Generates key pair"); - SyncCryptoStore::insert_unknown(&store, SR25519, secret_uri, key_pair.public().as_ref()) + Keystore::insert(&store, SR25519, secret_uri, key_pair.public().as_ref()) .expect("Inserts unknown key"); - let public_keys = SyncCryptoStore::keys(&store, SR25519).unwrap(); + let public_keys = Keystore::keys(&store, SR25519).unwrap(); assert!(public_keys.contains(&key_pair.public().into())); } #[test] fn vrf_sign() { - let store = KeyStore::new(); + let store = MemoryKeystore::new(); let secret_uri = "//Alice"; let key_pair = sr25519::Pair::from_string(secret_uri, None).expect("Generates key pair"); @@ -450,7 +342,7 @@ mod tests { ], }; - let result = SyncCryptoStore::sr25519_vrf_sign( + let result = Keystore::sr25519_vrf_sign( &store, SR25519, &key_pair.public(), @@ -458,18 +350,18 @@ mod tests { ); assert!(result.unwrap().is_none()); - SyncCryptoStore::insert_unknown(&store, SR25519, secret_uri, key_pair.public().as_ref()) + Keystore::insert(&store, SR25519, secret_uri, key_pair.public().as_ref()) .expect("Inserts unknown key"); let result = - SyncCryptoStore::sr25519_vrf_sign(&store, SR25519, &key_pair.public(), transcript_data); + Keystore::sr25519_vrf_sign(&store, SR25519, &key_pair.public(), transcript_data); assert!(result.unwrap().is_some()); } #[test] fn ecdsa_sign_prehashed_works() { - let store = KeyStore::new(); + let store = MemoryKeystore::new(); let suri = "//Alice"; let pair = ecdsa::Pair::from_string(suri, None).unwrap(); @@ -477,15 +369,13 @@ mod tests { let msg = sp_core::keccak_256(b"this should be a hashed message"); // no key in key store - let res = - SyncCryptoStore::ecdsa_sign_prehashed(&store, ECDSA, &pair.public(), &msg).unwrap(); + let res = Keystore::ecdsa_sign_prehashed(&store, ECDSA, &pair.public(), &msg).unwrap(); assert!(res.is_none()); // insert key, sign again - SyncCryptoStore::insert_unknown(&store, ECDSA, suri, pair.public().as_ref()).unwrap(); + Keystore::insert(&store, ECDSA, suri, pair.public().as_ref()).unwrap(); - let res = - SyncCryptoStore::ecdsa_sign_prehashed(&store, ECDSA, &pair.public(), &msg).unwrap(); + let res = Keystore::ecdsa_sign_prehashed(&store, ECDSA, &pair.public(), &msg).unwrap(); assert!(res.is_some()); } } diff --git a/test-utils/client/src/lib.rs b/test-utils/client/src/lib.rs index a27178792..a91aa9992 100644 --- a/test-utils/client/src/lib.rs +++ b/test-utils/client/src/lib.rs @@ -33,7 +33,7 @@ pub use sp_consensus; pub use sp_keyring::{ ed25519::Keyring as Ed25519Keyring, sr25519::Keyring as Sr25519Keyring, AccountKeyring, }; -pub use sp_keystore::{SyncCryptoStore, SyncCryptoStorePtr}; +pub use sp_keystore::{Keystore, KeystorePtr}; pub use sp_runtime::{Storage, StorageChild}; pub use sp_state_machine::ExecutionStrategy; @@ -70,7 +70,7 @@ pub struct TestClientBuilder, StorageChild>, backend: Arc, _executor: std::marker::PhantomData, - keystore: Option, + keystore: Option, fork_blocks: ForkBlocks, bad_blocks: BadBlocks, enable_offchain_indexing_api: bool, @@ -128,7 +128,7 @@ impl } /// Set the keystore that should be used by the externalities. - pub fn set_keystore(mut self, keystore: SyncCryptoStorePtr) -> Self { + pub fn set_keystore(mut self, keystore: KeystorePtr) -> Self { self.keystore = Some(keystore); self } diff --git a/utils/frame/benchmarking-cli/src/pallet/command.rs b/utils/frame/benchmarking-cli/src/pallet/command.rs index 60f078142..f11e6bd92 100644 --- a/utils/frame/benchmarking-cli/src/pallet/command.rs +++ b/utils/frame/benchmarking-cli/src/pallet/command.rs @@ -38,7 +38,7 @@ use sp_core::{ traits::CallContext, }; use sp_externalities::Extensions; -use sp_keystore::{testing::KeyStore, KeystoreExt, SyncCryptoStorePtr}; +use sp_keystore::{testing::MemoryKeystore, KeystoreExt, KeystorePtr}; use sp_runtime::traits::{Block as BlockT, Header as HeaderT}; use sp_state_machine::StateMachine; use std::{collections::HashMap, fmt::Debug, fs, str::FromStr, sync::Arc, time}; @@ -218,7 +218,7 @@ impl PalletCmd { let extensions = || -> Extensions { let mut extensions = Extensions::default(); - extensions.register(KeystoreExt(Arc::new(KeyStore::new()) as SyncCryptoStorePtr)); + extensions.register(KeystoreExt(Arc::new(MemoryKeystore::new()) as KeystorePtr)); let (offchain, _) = TestOffchainExt::new(); let (pool, _) = TestTransactionPoolExt::new(); extensions.register(OffchainWorkerExt::new(offchain.clone())); diff --git a/utils/frame/try-runtime/cli/src/lib.rs b/utils/frame/try-runtime/cli/src/lib.rs index 6fb4b4439..e1d75064a 100644 --- a/utils/frame/try-runtime/cli/src/lib.rs +++ b/utils/frame/try-runtime/cli/src/lib.rs @@ -383,7 +383,7 @@ use sp_core::{ }; use sp_externalities::Extensions; use sp_inherents::InherentData; -use sp_keystore::{testing::KeyStore, KeystoreExt}; +use sp_keystore::{testing::MemoryKeystore, KeystoreExt}; use sp_runtime::{ traits::{BlakeTwo256, Block as BlockT, NumberFor}, DeserializeOwned, Digest, @@ -820,7 +820,7 @@ pub(crate) fn full_extensions() -> Extensions { let (pool, _pool_state) = TestTransactionPoolExt::new(); extensions.register(OffchainDbExt::new(offchain.clone())); extensions.register(OffchainWorkerExt::new(offchain)); - extensions.register(KeystoreExt(std::sync::Arc::new(KeyStore::new()))); + extensions.register(KeystoreExt(std::sync::Arc::new(MemoryKeystore::new()))); extensions.register(TransactionPoolExt::new(pool)); extensions From a87aa29b4b8796afe50ffbda4ad6f7692cc9a65e Mon Sep 17 00:00:00 2001 From: Davide Galassi Date: Fri, 17 Mar 2023 14:21:02 +0100 Subject: [PATCH 250/558] Bump grandpa to 0.16.2 (#13622) --- Cargo.lock | 4 ++-- client/consensus/grandpa/Cargo.toml | 4 ++-- client/consensus/grandpa/rpc/Cargo.toml | 2 +- frame/grandpa/Cargo.toml | 2 +- primitives/consensus/grandpa/Cargo.toml | 2 +- primitives/consensus/grandpa/src/lib.rs | 7 ++----- 6 files changed, 9 insertions(+), 12 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 9121a8591..df0e54a3d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2080,9 +2080,9 @@ dependencies = [ [[package]] name = "finality-grandpa" -version = "0.16.1" +version = "0.16.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e24e6c429951433ccb7c87fd528c60084834dcd14763182c1f83291bcde24c34" +checksum = "36530797b9bf31cd4ff126dcfee8170f86b00cfdcea3269d73133cc0415945c3" dependencies = [ "either", "futures", diff --git a/client/consensus/grandpa/Cargo.toml b/client/consensus/grandpa/Cargo.toml index 511e546aa..67c58d37a 100644 --- a/client/consensus/grandpa/Cargo.toml +++ b/client/consensus/grandpa/Cargo.toml @@ -18,7 +18,7 @@ ahash = "0.8.2" array-bytes = "4.1" async-trait = "0.1.57" dyn-clone = "1.0" -finality-grandpa = { version = "0.16.1", features = ["derive-codec"] } +finality-grandpa = { version = "0.16.2", features = ["derive-codec"] } futures = "0.3.21" futures-timer = "3.0.1" log = "0.4.17" @@ -50,7 +50,7 @@ sp-runtime = { version = "7.0.0", path = "../../../primitives/runtime" } [dev-dependencies] assert_matches = "1.3.0" -finality-grandpa = { version = "0.16.1", features = ["derive-codec", "test-helpers"] } +finality-grandpa = { version = "0.16.2", features = ["derive-codec", "test-helpers"] } serde = "1.0.136" tokio = "1.22.0" sc-network = { version = "0.10.0-dev", path = "../../network" } diff --git a/client/consensus/grandpa/rpc/Cargo.toml b/client/consensus/grandpa/rpc/Cargo.toml index 4880b50d3..76a06ad29 100644 --- a/client/consensus/grandpa/rpc/Cargo.toml +++ b/client/consensus/grandpa/rpc/Cargo.toml @@ -10,7 +10,7 @@ readme = "README.md" homepage = "https://substrate.io" [dependencies] -finality-grandpa = { version = "0.16.1", features = ["derive-codec"] } +finality-grandpa = { version = "0.16.2", features = ["derive-codec"] } futures = "0.3.16" jsonrpsee = { version = "0.16.2", features = ["client-core", "server", "macros"] } log = "0.4.8" diff --git a/frame/grandpa/Cargo.toml b/frame/grandpa/Cargo.toml index 3ffb51642..4eb796cf0 100644 --- a/frame/grandpa/Cargo.toml +++ b/frame/grandpa/Cargo.toml @@ -31,7 +31,7 @@ sp-staking = { version = "4.0.0-dev", default-features = false, path = "../../pr sp-std = { version = "5.0.0", default-features = false, path = "../../primitives/std" } [dev-dependencies] -grandpa = { package = "finality-grandpa", version = "0.16.1", features = ["derive-codec"] } +grandpa = { package = "finality-grandpa", version = "0.16.2", features = ["derive-codec"] } frame-benchmarking = { version = "4.0.0-dev", path = "../benchmarking" } frame-election-provider-support = { version = "4.0.0-dev", path = "../election-provider-support" } pallet-balances = { version = "4.0.0-dev", path = "../balances" } diff --git a/primitives/consensus/grandpa/Cargo.toml b/primitives/consensus/grandpa/Cargo.toml index 7146873e0..5536cf735 100644 --- a/primitives/consensus/grandpa/Cargo.toml +++ b/primitives/consensus/grandpa/Cargo.toml @@ -15,7 +15,7 @@ targets = ["x86_64-unknown-linux-gnu"] [dependencies] codec = { package = "parity-scale-codec", version = "3.2.2", default-features = false, features = ["derive"] } -grandpa = { package = "finality-grandpa", version = "0.16.1", default-features = false, features = ["derive-codec"] } +grandpa = { package = "finality-grandpa", version = "0.16.2", default-features = false, features = ["derive-codec"] } log = { version = "0.4.17", default-features = false } scale-info = { version = "2.1.1", default-features = false, features = ["derive"] } serde = { version = "1.0.136", features = ["derive"], optional = true } diff --git a/primitives/consensus/grandpa/src/lib.rs b/primitives/consensus/grandpa/src/lib.rs index 32fc563b1..28c7de067 100644 --- a/primitives/consensus/grandpa/src/lib.rs +++ b/primitives/consensus/grandpa/src/lib.rs @@ -234,15 +234,12 @@ impl ConsensusLog { /// GRANDPA happens when a voter votes on the same round (either at prevote or /// precommit stage) for different blocks. Proving is achieved by collecting the /// signed messages of conflicting votes. -#[derive(Clone, Debug, Decode, Encode, PartialEq, TypeInfo)] +#[derive(Clone, Debug, Decode, Encode, PartialEq, Eq, TypeInfo)] pub struct EquivocationProof { set_id: SetId, equivocation: Equivocation, } -// Don't bother the grandpa crate... -impl Eq for EquivocationProof {} - impl EquivocationProof { /// Create a new `EquivocationProof` for the given set id and using the /// given equivocation as proof. @@ -271,7 +268,7 @@ impl EquivocationProof { /// Wrapper object for GRANDPA equivocation proofs, useful for unifying prevote /// and precommit equivocations under a common type. -#[derive(Clone, Debug, Decode, Encode, PartialEq, TypeInfo)] +#[derive(Clone, Debug, Decode, Encode, PartialEq, Eq, TypeInfo)] pub enum Equivocation { /// Proof of equivocation at prevote stage. Prevote(grandpa::Equivocation, AuthoritySignature>), From 0506da00297757babe63b7bb62e487c657066117 Mon Sep 17 00:00:00 2001 From: Oleg Plakida <112385193+oleg-plakida@users.noreply.github.com> Date: Fri, 17 Mar 2023 14:14:13 +0000 Subject: [PATCH 251/558] Fix `test-linux-stable` excessive run time (#13597) * ci-cd#754:Fix slow unit tests - Add nextest config - Add slow tests to mutual exclusion list * Update .config/nextest.toml Co-authored-by: Alexander Samusev <41779041+alvicsam@users.noreply.github.com> --------- Co-authored-by: Alexander Samusev <41779041+alvicsam@users.noreply.github.com> --- .config/nextest.toml | 124 +++++++++++++++++++++++++++++++++++ .github/pr-custom-review.yml | 4 +- 2 files changed, 126 insertions(+), 2 deletions(-) create mode 100644 .config/nextest.toml diff --git a/.config/nextest.toml b/.config/nextest.toml new file mode 100644 index 000000000..c111b0abe --- /dev/null +++ b/.config/nextest.toml @@ -0,0 +1,124 @@ +# This is the default config used by nextest. It is embedded in the binary at +# build time. It may be used as a template for .config/nextest.toml. + +[store] +# The directory under the workspace root at which nextest-related files are +# written. Profile-specific storage is currently written to dir/. +dir = "target/nextest" + +# This section defines the default nextest profile. Custom profiles are layered +# on top of the default profile. +[profile.default] +# "retries" defines the number of times a test should be retried. If set to a +# non-zero value, tests that succeed on a subsequent attempt will be marked as +# non-flaky. Can be overridden through the `--retries` option. +# Examples +# * retries = 3 +# * retries = { backoff = "fixed", count = 2, delay = "1s" } +# * retries = { backoff = "exponential", count = 10, delay = "1s", jitter = true, max-delay = "10s" } +retries = 0 + +# The number of threads to run tests with. Supported values are either an integer or +# the string "num-cpus". Can be overridden through the `--test-threads` option. +test-threads = "num-cpus" + +# The number of threads required for each test. This is generally used in overrides to +# mark certain tests as heavier than others. However, it can also be set as a global parameter. +threads-required = 1 + +# Show these test statuses in the output. +# +# The possible values this can take are: +# * none: no output +# * fail: show failed (including exec-failed) tests +# * retry: show flaky and retried tests +# * slow: show slow tests +# * pass: show passed tests +# * skip: show skipped tests (most useful for CI) +# * all: all of the above +# +# Each value includes all the values above it; for example, "slow" includes +# failed and retried tests. +# +# Can be overridden through the `--status-level` flag. +status-level = "pass" + +# Similar to status-level, show these test statuses at the end of the run. +final-status-level = "flaky" + +# "failure-output" defines when standard output and standard error for failing tests are produced. +# Accepted values are +# * "immediate": output failures as soon as they happen +# * "final": output failures at the end of the test run +# * "immediate-final": output failures as soon as they happen and at the end of +# the test run; combination of "immediate" and "final" +# * "never": don't output failures at all +# +# For large test suites and CI it is generally useful to use "immediate-final". +# +# Can be overridden through the `--failure-output` option. +failure-output = "immediate" + +# "success-output" controls production of standard output and standard error on success. This should +# generally be set to "never". +success-output = "never" + +# Cancel the test run on the first failure. For CI runs, consider setting this +# to false. +fail-fast = true + +# Treat a test that takes longer than the configured 'period' as slow, and print a message. +# See for more information. +# +# Optional: specify the parameter 'terminate-after' with a non-zero integer, +# which will cause slow tests to be terminated after the specified number of +# periods have passed. +# Example: slow-timeout = { period = "60s", terminate-after = 2 } +slow-timeout = { period = "60s" } + +# Treat a test as leaky if after the process is shut down, standard output and standard error +# aren't closed within this duration. +# +# This usually happens in case of a test that creates a child process and lets it inherit those +# handles, but doesn't clean the child process up (especially when it fails). +# +# See for more information. +leak-timeout = "100ms" + +[profile.default.junit] +# Output a JUnit report into the given file inside 'store.dir/'. +# If unspecified, JUnit is not written out. + +# path = "junit.xml" + +# The name of the top-level "report" element in JUnit report. If aggregating +# reports across different test runs, it may be useful to provide separate names +# for each report. +report-name = "nextest-run" + +# Whether standard output and standard error for passing tests should be stored in the JUnit report. +# Output is stored in the and elements of the element. +store-success-output = false + +# Whether standard output and standard error for failing tests should be stored in the JUnit report. +# Output is stored in the and elements of the element. +# +# Note that if a description can be extracted from the output, it is always stored in the +# element. +store-failure-output = true + +# This profile is activated if MIRI_SYSROOT is set. +[profile.default-miri] +# Miri tests take up a lot of memory, so only run 1 test at a time by default. +test-threads = 1 + +# Mutual exclusion of tests with `cargo build` invocation as a lock to avoid multiple +# simultaneous invocations clobbering each other. +[test-groups] +serial-integration = { max-threads = 1 } + +# Running UI tests sequentially +# More info can be found here: https://github.com/paritytech/ci_cd/issues/754 +[[profile.default.overrides]] +filter = 'test(/(^ui$|_ui|ui_)/)' +test-group = 'serial-integration' diff --git a/.github/pr-custom-review.yml b/.github/pr-custom-review.yml index 769ac1e11..3620f7e77 100644 --- a/.github/pr-custom-review.yml +++ b/.github/pr-custom-review.yml @@ -9,7 +9,7 @@ rules: condition: include: .* # excluding files from 'CI team' rules - exclude: ^\.gitlab-ci\.yml|^scripts/ci/.*|^\.github/.* + exclude: ^\.gitlab-ci\.yml|^scripts/ci/.*|^\.github/.*|^\.config/nextest.toml min_approvals: 2 teams: - core-devs @@ -17,7 +17,7 @@ rules: - name: CI team check_type: changed_files condition: - include: ^\.gitlab-ci\.yml|^scripts/ci/.*|^\.github/.* + include: ^\.gitlab-ci\.yml|^scripts/ci/.*|^\.github/.*|^\.config/nextest.toml min_approvals: 2 teams: - ci From acdbf010f5c81497edfe65377a46ba0d6e50baa5 Mon Sep 17 00:00:00 2001 From: PG Herveou Date: Fri, 17 Mar 2023 16:31:51 +0100 Subject: [PATCH 252/558] Fix typos in pallet-contracts (#13629) * Fix typos in pallet-contracts --- client/state-db/src/lib.rs | 4 ++-- frame/contracts/README.md | 2 +- frame/contracts/primitives/src/lib.rs | 4 ++-- frame/contracts/proc-macro/src/lib.rs | 6 +++--- frame/contracts/src/address.rs | 2 +- frame/contracts/src/benchmarking/code.rs | 2 +- frame/contracts/src/benchmarking/mod.rs | 8 ++++---- frame/contracts/src/chain_extension.rs | 2 +- frame/contracts/src/exec.rs | 10 +++++----- frame/contracts/src/lib.rs | 6 +++--- frame/contracts/src/storage/meter.rs | 2 +- frame/contracts/src/wasm/mod.rs | 4 ++-- frame/contracts/src/wasm/prepare.rs | 6 +++--- frame/contracts/src/wasm/runtime.rs | 16 ++++++++-------- 14 files changed, 37 insertions(+), 37 deletions(-) diff --git a/client/state-db/src/lib.rs b/client/state-db/src/lib.rs index 14b4622ac..5ebfbaad6 100644 --- a/client/state-db/src/lib.rs +++ b/client/state-db/src/lib.rs @@ -594,7 +594,7 @@ impl StateDb { } /// Prevents pruning of specified block and its descendants. - /// `hint` used for futher checking if the given block exists + /// `hint` used for further checking if the given block exists pub fn pin(&self, hash: &BlockHash, number: u64, hint: F) -> Result<(), PinError> where F: Fn() -> bool, @@ -665,7 +665,7 @@ pub enum IsPruned { Pruned, /// Definitely not pruned NotPruned, - /// May or may not pruned, need futher checking + /// May or may not pruned, need further checking MaybePruned, } diff --git a/frame/contracts/README.md b/frame/contracts/README.md index 4df7d9449..9b1d3f7d6 100644 --- a/frame/contracts/README.md +++ b/frame/contracts/README.md @@ -64,7 +64,7 @@ When setting up the `Schedule` for your runtime make sure to set `InstructionWei to a non zero value. The default is `0` and prevents the upload of any non deterministic code. An indeterministic code can be deployed on-chain by passing `Determinism::AllowIndeterministic` -to `upload_code`. A determinstic contract can then delegate call into it if and only if it +to `upload_code`. A deterministic contract can then delegate call into it if and only if it is ran by using `bare_call` and passing `Determinism::AllowIndeterministic` to it. **Never use this argument when the contract is called from an on-chain transaction.** diff --git a/frame/contracts/primitives/src/lib.rs b/frame/contracts/primitives/src/lib.rs index ee415cb82..e73ceb031 100644 --- a/frame/contracts/primitives/src/lib.rs +++ b/frame/contracts/primitives/src/lib.rs @@ -131,7 +131,7 @@ pub struct InstantiateReturnValue { pub account_id: AccountId, } -/// The result of succesfully uploading a contract. +/// The result of successfully uploading a contract. #[derive(PartialEq, Eq, Encode, Decode, RuntimeDebug, TypeInfo)] pub struct CodeUploadReturnValue { /// The key under which the new code is stored. @@ -239,7 +239,7 @@ where } } - /// If the amount of deposit (this type) is constrained by a `limit` this calcuates how + /// If the amount of deposit (this type) is constrained by a `limit` this calculates how /// much balance (if any) is still available from this limit. /// /// # Note diff --git a/frame/contracts/proc-macro/src/lib.rs b/frame/contracts/proc-macro/src/lib.rs index 1b530a409..c3e61dae5 100644 --- a/frame/contracts/proc-macro/src/lib.rs +++ b/frame/contracts/proc-macro/src/lib.rs @@ -15,7 +15,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -//! Procedural macroses used in the contracts module. +//! Procedural macros used in the contracts module. //! //! Most likely you should use the [`#[define_env]`][`macro@define_env`] attribute macro which hides //! boilerplate of defining external environment for a wasm module. @@ -527,7 +527,7 @@ fn expand_docs(def: &EnvDef) -> TokenStream2 { } } -/// Expands environment definiton. +/// Expands environment definition. /// Should generate source code for: /// - implementations of the host functions to be added to the wasm runtime environment (see /// `expand_impls()`). @@ -778,7 +778,7 @@ fn expand_functions(def: &EnvDef, expand_blocks: bool, host_state: TokenStream2) #[proc_macro_attribute] pub fn define_env(attr: TokenStream, item: TokenStream) -> TokenStream { if !attr.is_empty() && !(attr.to_string() == "doc".to_string()) { - let msg = r#"Invalid `define_env` attribute macro: expected either no attributes or a single `doc` attibute: + let msg = r#"Invalid `define_env` attribute macro: expected either no attributes or a single `doc` attribute: - `#[define_env]` - `#[define_env(doc)]`"#; let span = TokenStream2::from(attr).span(); diff --git a/frame/contracts/src/address.rs b/frame/contracts/src/address.rs index 1c25af21e..e36fc6fff 100644 --- a/frame/contracts/src/address.rs +++ b/frame/contracts/src/address.rs @@ -32,7 +32,7 @@ use sp_runtime::traits::{Hash, TrailingZeroInput}; pub trait AddressGenerator { /// The address of a contract based on the given instantiate parameters. /// - /// Changing the formular for an already deployed chain is fine as long as no collisons + /// Changing the formular for an already deployed chain is fine as long as no collisions /// with the old formular. Changes only affect existing contracts. fn contract_address( deploying_address: &T::AccountId, diff --git a/frame/contracts/src/benchmarking/code.rs b/frame/contracts/src/benchmarking/code.rs index 96eb5e501..c2d623ae4 100644 --- a/frame/contracts/src/benchmarking/code.rs +++ b/frame/contracts/src/benchmarking/code.rs @@ -39,7 +39,7 @@ use wasm_instrument::{ }, }; -/// The location where to put the genrated code. +/// The location where to put the generated code. pub enum Location { /// Generate all code into the `call` exported function. Call, diff --git a/frame/contracts/src/benchmarking/mod.rs b/frame/contracts/src/benchmarking/mod.rs index 873314981..96368ebb3 100644 --- a/frame/contracts/src/benchmarking/mod.rs +++ b/frame/contracts/src/benchmarking/mod.rs @@ -355,7 +355,7 @@ benchmarks! { }: _(origin, callee, value, Weight::MAX, None, data) verify { let deposit = T::Currency::free_balance(&deposit_account); - // value and value transfered via call should be removed from the caller + // value and value transferred via call should be removed from the caller assert_eq!( T::Currency::free_balance(&instance.caller), caller_funding::() - instance.value - value - deposit - Pallet::::min_balance(), @@ -2224,7 +2224,7 @@ benchmarks! { // the same amount of time. We follow that `t.load` and `drop` both have the weight // of this benchmark / 2. We need to make this assumption because there is no way // to measure them on their own using a valid wasm module. We need their individual - // values to derive the weight of individual instructions (by substraction) from + // values to derive the weight of individual instructions (by subtraction) from // benchmarks that include those for parameter pushing and return type dropping. // We call the weight of `t.load` and `drop`: `w_param`. // The weight that would result from the respective benchmark we call: `w_bench`. @@ -3003,9 +3003,9 @@ benchmarks! { sbox.invoke(); } - // This is no benchmark. It merely exist to have an easy way to pretty print the curently + // This is no benchmark. It merely exist to have an easy way to pretty print the currently // configured `Schedule` during benchmark development. - // It can be outputed using the following command: + // It can be outputted using the following command: // cargo run --manifest-path=bin/node/cli/Cargo.toml \ // --features runtime-benchmarks -- benchmark pallet --extra --dev --execution=native \ // -p pallet_contracts -e print_schedule --no-median-slopes --no-min-squares diff --git a/frame/contracts/src/chain_extension.rs b/frame/contracts/src/chain_extension.rs index 56d41a759..2c3780708 100644 --- a/frame/contracts/src/chain_extension.rs +++ b/frame/contracts/src/chain_extension.rs @@ -138,7 +138,7 @@ pub trait ChainExtension { /// /// # Note /// -/// Currently, we support tuples of up to ten registred chain extensions. If more chain extensions +/// Currently, we support tuples of up to ten registered chain extensions. If more chain extensions /// are needed consider opening an issue. pub trait RegisteredChainExtension: ChainExtension { /// The extensions globally unique identifier. diff --git a/frame/contracts/src/exec.rs b/frame/contracts/src/exec.rs index 37be12d50..35ef43da5 100644 --- a/frame/contracts/src/exec.rs +++ b/frame/contracts/src/exec.rs @@ -84,14 +84,14 @@ where pub enum ErrorOrigin { /// Caller error origin. /// - /// The error happened in the current exeuction context rather than in the one + /// The error happened in the current execution context rather than in the one /// of the contract that is called into. Caller, /// The error happened during execution of the called contract. Callee, } -/// Error returned by contract exection. +/// Error returned by contract execution. #[cfg_attr(test, derive(Debug, PartialEq))] pub struct ExecError { /// The reason why the execution failed. @@ -2455,7 +2455,7 @@ mod tests { #[test] fn in_memory_changes_not_discarded() { // Call stack: BOB -> CHARLIE (trap) -> BOB' (success) - // This tests verfies some edge case of the contract info cache: + // This tests verifies some edge case of the contract info cache: // We change some value in our contract info before calling into a contract // that calls into ourself. This triggers a case where BOBs contract info // is written to storage and invalidated by the successful execution of BOB'. @@ -3405,7 +3405,7 @@ mod tests { let code_hash = MockLoader::insert(Call, move |ctx, _| { // It is set to one when this contract was instantiated by `place_contract` assert_eq!(ctx.ext.nonce(), 1); - // Should not change without any instantation in-between + // Should not change without any instantiation in-between assert_eq!(ctx.ext.nonce(), 1); // Should not change with a failed instantiation assert_err!( @@ -3416,7 +3416,7 @@ mod tests { } ); assert_eq!(ctx.ext.nonce(), 1); - // Successful instantation increments + // Successful instantiation increments ctx.ext.instantiate(Weight::zero(), success_code, 0, vec![], &[]).unwrap(); assert_eq!(ctx.ext.nonce(), 2); exec_success() diff --git a/frame/contracts/src/lib.rs b/frame/contracts/src/lib.rs index 4f5d7ba30..0aff18fd6 100644 --- a/frame/contracts/src/lib.rs +++ b/frame/contracts/src/lib.rs @@ -403,7 +403,7 @@ pub mod pallet { T::MaxCodeLen::get(), ); - // Debug buffer should at least be large enough to accomodate a simple error message + // Debug buffer should at least be large enough to accommodate a simple error message const MIN_DEBUG_BUF_SIZE: u32 = 256; assert!( T::MaxDebugBufferLen::get() > MIN_DEBUG_BUF_SIZE, @@ -891,7 +891,7 @@ pub mod pallet { /// The contract's code was found to be invalid during validation or instrumentation. /// /// The most likely cause of this is that an API was used which is not supported by the - /// node. This hapens if an older node is used with a new version of ink!. Try updating + /// node. This happens if an older node is used with a new version of ink!. Try updating /// your node to the newest available version. /// /// A more detailed error can be found on the node console if debug messages are enabled @@ -987,7 +987,7 @@ struct InternalOutput { result: Result, } -/// Helper trait to wrap contract execution entry points into a signle function +/// Helper trait to wrap contract execution entry points into a single function /// [`Invokable::run_guarded`]. trait Invokable { /// What is returned as a result of a successful invocation. diff --git a/frame/contracts/src/storage/meter.rs b/frame/contracts/src/storage/meter.rs index ef6fb3277..b4fc443fe 100644 --- a/frame/contracts/src/storage/meter.rs +++ b/frame/contracts/src/storage/meter.rs @@ -513,7 +513,7 @@ impl Ext for ReservingExt { // we make sure to leave at least the ed in the free balance. // // The sender always has enough balance because we track it in the `ContractInfo` and - // never send more back than we have. Noone has access to the deposit account. Hence no + // never send more back than we have. No one has access to the deposit account. Hence no // other interaction with this account takes place. Deposit::Refund(amount) => { if terminated { diff --git a/frame/contracts/src/wasm/mod.rs b/frame/contracts/src/wasm/mod.rs index f8c5a6ed9..f74df2c36 100644 --- a/frame/contracts/src/wasm/mod.rs +++ b/frame/contracts/src/wasm/mod.rs @@ -186,7 +186,7 @@ impl PrefabWasmModule { code_cache::try_remove::(origin, code_hash) } - /// Returns whether there is a deposit to be payed for this module. + /// Returns whether there is a deposit to be paid for this module. /// /// Returns `0` if the module is already in storage and hence no deposit will /// be charged when storing it. @@ -689,7 +689,7 @@ mod tests { executable.execute(ext.borrow_mut(), entry_point, input_data) } - /// Execute the suppplied code. + /// Execute the supplied code. fn execute>(wat: &str, input_data: Vec, ext: E) -> ExecResult { execute_internal(wat, input_data, ext, &ExportedFunction::Call, true, false) } diff --git a/frame/contracts/src/wasm/prepare.rs b/frame/contracts/src/wasm/prepare.rs index eed60891d..6a2eb6a76 100644 --- a/frame/contracts/src/wasm/prepare.rs +++ b/frame/contracts/src/wasm/prepare.rs @@ -50,7 +50,7 @@ pub enum TryInstantiate { Instantiate, /// Skip the instantiation during preparation. /// - /// This makes sense when the preparation takes place as part of an instantation. Then + /// This makes sense when the preparation takes place as part of an instantiation. Then /// this instantiation would fail the whole transaction and an extra check is not /// necessary. Skip, @@ -376,7 +376,7 @@ fn get_memory_limits( }, } } else { - // If none memory imported then just crate an empty placeholder. + // If none memory imported then just create an empty placeholder. // Any access to it will lead to out of bounds trap. Ok((0, 0)) } @@ -412,7 +412,7 @@ where extended_const: false, component_model: false, // This is not our only defense: We check for float types later in the preparation - // process. Additionally, all instructions explictily need to have weights assigned + // process. Additionally, all instructions explicitly need to have weights assigned // or the deployment will fail. We have none assigned for float instructions. deterministic_only: matches!(determinism, Determinism::Deterministic), mutable_global: false, diff --git a/frame/contracts/src/wasm/runtime.rs b/frame/contracts/src/wasm/runtime.rs index e80e2cde3..b30bad38b 100644 --- a/frame/contracts/src/wasm/runtime.rs +++ b/frame/contracts/src/wasm/runtime.rs @@ -1336,7 +1336,7 @@ pub mod env { /// /// # Parameters /// - /// - `flags`: See `crate::wasm::runtime::CallFlags` for a documenation of the supported flags. + /// - `flags`: See `crate::wasm::runtime::CallFlags` for a documentation of the supported flags. /// - `callee_ptr`: a pointer to the address of the callee contract. Should be decodable as an /// `T::AccountId`. Traps otherwise. /// - `gas`: how much gas to devote to the execution. @@ -2038,7 +2038,7 @@ pub mod env { /// # Note /// /// The state rent functionality was removed. This is stub only exists for - /// backwards compatiblity + /// backwards compatibility #[prefixed_alias] #[deprecated] fn restore_to( @@ -2062,7 +2062,7 @@ pub mod env { /// # Note /// /// The state rent functionality was removed. This is stub only exists for - /// backwards compatiblity + /// backwards compatibility #[version(1)] #[prefixed_alias] #[deprecated] @@ -2084,7 +2084,7 @@ pub mod env { /// # Note /// /// The state rent functionality was removed. This is stub only exists for - /// backwards compatiblity. + /// backwards compatibility. #[prefixed_alias] #[deprecated] fn set_rent_allowance( @@ -2102,7 +2102,7 @@ pub mod env { /// # Note /// /// The state rent functionality was removed. This is stub only exists for - /// backwards compatiblity. + /// backwards compatibility. #[version(1)] #[prefixed_alias] #[deprecated] @@ -2116,7 +2116,7 @@ pub mod env { /// # Note /// /// The state rent functionality was removed. This is stub only exists for - /// backwards compatiblity. + /// backwards compatibility. #[prefixed_alias] #[deprecated] fn rent_allowance(ctx: _, memory: _, out_ptr: u32, out_len_ptr: u32) -> Result<(), TrapReason> { @@ -2409,7 +2409,7 @@ pub mod env { /// /// # Return Value /// - /// Returns `ReturnCode::Success` when the dispatchable was succesfully executed and + /// Returns `ReturnCode::Success` when the dispatchable was successfully executed and /// returned `Ok`. When the dispatchable was exeuted but returned an error /// `ReturnCode::CallRuntimeFailed` is returned. The full error is not /// provided because it is not guaranteed to be stable. @@ -2603,7 +2603,7 @@ pub mod env { /// Returns a nonce that is unique per contract instantiation. /// - /// The nonce is incremented for each succesful contract instantiation. This is a + /// The nonce is incremented for each successful contract instantiation. This is a /// sensible default salt for contract instantiations. #[unstable] fn instantiation_nonce(ctx: _, _memory: _) -> Result { From 44c5c99880346d5b9d4874725500c03b30994774 Mon Sep 17 00:00:00 2001 From: Oliver Tale-Yazdi Date: Fri, 17 Mar 2023 17:06:40 +0100 Subject: [PATCH 253/558] Ignore flaky test (#13631) * Ignore flaky test Signed-off-by: Oliver Tale-Yazdi * Re-enable running_the_node_works_and_can_be_interrupted Signed-off-by: Oliver Tale-Yazdi * Re-enable notifications_back_pressure Signed-off-by: Oliver Tale-Yazdi * Unused import Signed-off-by: Oliver Tale-Yazdi --------- Signed-off-by: Oliver Tale-Yazdi --- client/rpc-spec-v2/src/chain_head/tests.rs | 6 ++++++ client/service/test/src/client/mod.rs | 6 +++++- 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/client/rpc-spec-v2/src/chain_head/tests.rs b/client/rpc-spec-v2/src/chain_head/tests.rs index fcd906dcf..0110c97a0 100644 --- a/client/rpc-spec-v2/src/chain_head/tests.rs +++ b/client/rpc-spec-v2/src/chain_head/tests.rs @@ -1024,6 +1024,9 @@ async fn follow_prune_best_block() { } #[tokio::test] +#[cfg(disable_flaky)] +#[allow(dead_code)] +// FIXME: https://github.com/paritytech/substrate/issues/11321 async fn follow_forks_pruned_block() { let builder = TestClientBuilder::new(); let backend = builder.backend(); @@ -1137,6 +1140,9 @@ async fn follow_forks_pruned_block() { } #[tokio::test] +#[cfg(disable_flaky)] +#[allow(dead_code)] +// FIXME: https://github.com/paritytech/substrate/issues/11321 async fn follow_report_multiple_pruned_block() { let builder = TestClientBuilder::new(); let backend = builder.backend(); diff --git a/client/service/test/src/client/mod.rs b/client/service/test/src/client/mod.rs index cae69413c..96a0a8fe2 100644 --- a/client/service/test/src/client/mod.rs +++ b/client/service/test/src/client/mod.rs @@ -30,7 +30,7 @@ use sc_consensus::{ }; use sc_service::client::{new_in_mem, Client, LocalCallExecutor}; use sp_api::ProvideRuntimeApi; -use sp_consensus::{BlockOrigin, BlockStatus, Error as ConsensusError, SelectChain}; +use sp_consensus::{BlockOrigin, Error as ConsensusError, SelectChain}; use sp_core::{testing::TaskExecutor, traits::CallContext, H256}; use sp_runtime::{ generic::BlockId, @@ -1567,7 +1567,11 @@ fn respects_block_rules() { } #[test] +#[cfg(disable_flaky)] +#[allow(dead_code)] +// FIXME: https://github.com/paritytech/substrate/issues/11321 fn returns_status_for_pruned_blocks() { + use sc_consensus::BlockStatus; sp_tracing::try_init_simple(); let tmp = tempfile::tempdir().unwrap(); From bab32f81dd5f9261605b572595de6eb9c40cb0be Mon Sep 17 00:00:00 2001 From: Koute Date: Sat, 18 Mar 2023 01:09:28 +0900 Subject: [PATCH 254/558] Bump `trie-db` to 0.27.1 (fix storage iteration bug + change seek behavior with prefix) (#13630) --- Cargo.lock | 4 +- primitives/state-machine/Cargo.toml | 2 +- primitives/state-machine/src/trie_backend.rs | 119 ++++++++++++++++++- 3 files changed, 121 insertions(+), 4 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index df0e54a3d..da100658b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -11653,9 +11653,9 @@ dependencies = [ [[package]] name = "trie-db" -version = "0.27.0" +version = "0.27.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "634d75c77ea43f2ad8ea9d9c58de49dfc9c3995bdef32b503df7883ff054e7f1" +checksum = "767abe6ffed88a1889671a102c2861ae742726f52e0a5a425b92c9fbfa7e9c85" dependencies = [ "hash-db", "hashbrown 0.13.2", diff --git a/primitives/state-machine/Cargo.toml b/primitives/state-machine/Cargo.toml index 9759547d7..cc780e51a 100644 --- a/primitives/state-machine/Cargo.toml +++ b/primitives/state-machine/Cargo.toml @@ -33,7 +33,7 @@ array-bytes = "4.1" pretty_assertions = "1.2.1" rand = "0.8.5" sp-runtime = { version = "7.0.0", path = "../runtime" } -trie-db = "0.27.0" +trie-db = "0.27.1" assert_matches = "1.5" [features] diff --git a/primitives/state-machine/src/trie_backend.rs b/primitives/state-machine/src/trie_backend.rs index 10e2dfd16..afbe6cbbe 100644 --- a/primitives/state-machine/src/trie_backend.rs +++ b/primitives/state-machine/src/trie_backend.rs @@ -536,6 +536,29 @@ pub mod tests { (mdb, root) } + pub(crate) fn test_db_with_hex_keys( + state_version: StateVersion, + keys: &[&str], + ) -> (PrefixedMemoryDB, H256) { + let mut root = H256::default(); + let mut mdb = PrefixedMemoryDB::::default(); + match state_version { + StateVersion::V0 => { + let mut trie = TrieDBMutBuilderV0::new(&mut mdb, &mut root).build(); + for (index, key) in keys.iter().enumerate() { + trie.insert(&array_bytes::hex2bytes(key).unwrap(), &[index as u8]).unwrap(); + } + }, + StateVersion::V1 => { + let mut trie = TrieDBMutBuilderV1::new(&mut mdb, &mut root).build(); + for (index, key) in keys.iter().enumerate() { + trie.insert(&array_bytes::hex2bytes(key).unwrap(), &[index as u8]).unwrap(); + } + }, + }; + (mdb, root) + } + pub(crate) fn test_trie( hashed_value: StateVersion, cache: Option, @@ -549,6 +572,20 @@ pub mod tests { .build() } + pub(crate) fn test_trie_with_hex_keys( + hashed_value: StateVersion, + cache: Option, + recorder: Option, + keys: &[&str], + ) -> TrieBackend, BlakeTwo256> { + let (mdb, root) = test_db_with_hex_keys(hashed_value, keys); + + TrieBackendBuilder::new(mdb, root) + .with_optional_cache(cache) + .with_optional_recorder(recorder) + .build() + } + parameterized_test!(read_from_storage_returns_some, read_from_storage_returns_some_inner); fn read_from_storage_returns_some_inner( state_version: StateVersion, @@ -697,10 +734,25 @@ pub mod tests { ); // Fetch starting at a given key and with prefix which doesn't match that key. + // (Start *before* the prefix.) + assert_eq!( + trie.keys(IterArgs { + prefix: Some(b"value"), + start_at: Some(b"key"), + ..IterArgs::default() + }) + .unwrap() + .map(|result| result.unwrap()) + .collect::>(), + vec![b"value1".to_vec(), b"value2".to_vec(),] + ); + + // Fetch starting at a given key and with prefix which doesn't match that key. + // (Start *after* the prefix.) assert!(trie .keys(IterArgs { prefix: Some(b"value"), - start_at: Some(b"key"), + start_at: Some(b"vblue"), ..IterArgs::default() }) .unwrap() @@ -722,6 +774,71 @@ pub mod tests { ); } + // This test reproduces an actual real-world issue: https://github.com/polkadot-js/apps/issues/9103 + parameterized_test!( + storage_iter_does_not_return_out_of_prefix_keys, + storage_iter_does_not_return_out_of_prefix_keys_inner + ); + fn storage_iter_does_not_return_out_of_prefix_keys_inner( + state_version: StateVersion, + cache: Option, + recorder: Option, + ) { + let trie = test_trie_with_hex_keys(state_version, cache, recorder, &[ + "6cf4040bbce30824850f1a4823d8c65faeefaa25a5bae16a431719647c1d99da", + "6cf4040bbce30824850f1a4823d8c65ff536928ca5ba50039bc2766a48ddbbab", + "70f943199f1a2dde80afdaf3f447db834e7b9012096b41c4eb3aaf947f6ea429", + "7474449cca95dc5d0c00e71735a6d17d3cd15a3fd6e04e47bee3922dbfa92c8d007fc7effcb0c044a0c41fd8a77eb55d2133058a86d1f4d6f8e45612cd271eefd77f91caeaacfe011b8f41540e0a793b0fd51b245dae19382b45386570f2b545fab75e3277910f7324b55f47c29f9965e8298371404e50ac", + "7474449cca95dc5d0c00e71735a6d17d3cd15a3fd6e04e47bee3922dbfa92c8d0179c23cd593c770fde9fc7aa8f84b3e401e654b8986c67728844da0080ec9ee222b41a85708a471a511548302870b53f40813d8354b6d2969e1b7ca9e083ecf96f9647e004ecb41c7f26f0110f778bdb3d9da31bef323d9", + "7474449cca95dc5d0c00e71735a6d17d3cd15a3fd6e04e47bee3922dbfa92c8d024de296f88310247001277477f4ace4d0aa5685ea2928d518a807956e4806a656520d6520b8ac259f684aa0d91961d76f697716f04e6c997338d03560ab7d703829fe7b9d0e6d7eff8d8412fc428364c2f474a67b36586d", + "7474449cca95dc5d0c00e71735a6d17d3cd15a3fd6e04e47bee3922dbfa92c8d13dc5d83f2361c14d05933eb3182a92ac14665718569703baf1da25c7d571843b6489f03d8549c87bfa5709836ba729443c319659e83ad5ee133e6f11af51d883e56216e9e1bbb1e2920c7c6120cbb55cd469b1f95b61601", + "7474449cca95dc5d0c00e71735a6d17d3cd15a3fd6e04e47bee3922dbfa92c8d1786d20bbb4b91eb1f5765432d750bd0111a0807c8d04f05110ffaf73f4fa7b360422c13bc97efc3a2324d9fa8f954b424c0bcfce7236a2e8107dd31c2042a9860a964f8472fda49749dec3f146e81470b55aa0f3930d854", + "7474449cca95dc5d0c00e71735a6d17d3cd15a3fd6e04e47bee3922dbfa92c8d18c246484ec5335a40903e7cd05771be7c0b8459333f1ae2925c3669fc3e5accd0f38c4711a15544bfa5709836ba729443c319659e83ad5ee133e6f11af51d883e56216e9e1bbb1e2920c7c6120cbb55cd469b1f95b61601", + "7474449cca95dc5d0c00e71735a6d17d3cd15a3fd6e04e47bee3922dbfa92c8d1aca749033252ce75245528397430d14cb8e8c09248d81ee5de00b6ae93ee880b6d19a595e6dc106bfa5709836ba729443c319659e83ad5ee133e6f11af51d883e56216e9e1bbb1e2920c7c6120cbb55cd469b1f95b61601", + "7474449cca95dc5d0c00e71735a6d17d3cd15a3fd6e04e47bee3922dbfa92c8d1d6bceb91bc07973e7b3296f83af9f1c4300ce9198cc3b44c54dafddb58f4a43aee44a9bef1a2e9dbfa5709836ba729443c319659e83ad5ee133e6f11af51d883e56216e9e1bbb1e2920c7c6120cbb55cd469b1f95b61601", + "7474449cca95dc5d0c00e71735a6d17d3cd15a3fd6e04e47bee3922dbfa92c8d203383772f45721232139e1a8863b0f2f8d480bdc15bcc1f2033cf467e137059558da743838f6b58bfa5709836ba729443c319659e83ad5ee133e6f11af51d883e56216e9e1bbb1e2920c7c6120cbb55cd469b1f95b61601", + "7474449cca95dc5d0c00e71735a6d17d3cd15a3fd6e04e47bee3922dbfa92c8d2197cc5c3eb3a6a67538e0dc3eaaf8c820d71310d377499c4a5d276381789e0a234475e69cddf709d207458083d6146d3a36fce7f1fe05b232702bf154096e5e3a8c378bdc237d7a27909acd663563917f0f70bb0e8e61a3", + "7474449cca95dc5d0c00e71735a6d17d3cd15a3fd6e04e47bee3922dbfa92c8d4f19c117f2ea36100f753c4885aa8d63b4d65a0dc32106f829f89eeabd52c37105c9bdb75f752469729fa3f0e7d907c1d949192c8e264a1a510c32abe3a05ed50be2262d5bfb981673ec80a07fd2ce28c7f27cd0043a788c", + "7474449cca95dc5d0c00e71735a6d17d3cd15a3fd6e04e47bee3922dbfa92c8d547d5aaa651bafa63d077560dfe823ac75665ebf1dcfd96a06e45499f03dda31282977706918d4821b8f41540e0a793b0fd51b245dae19382b45386570f2b545fab75e3277910f7324b55f47c29f9965e8298371404e50ac", + "7474449cca95dc5d0c00e71735a6d17d3cd15a3fd6e04e47bee3922dbfa92c8d6037207d54d69a082ea225ab4a412e4b87d6f5612053b07c405cf05ea25e482a4908c0713be2998abfa5709836ba729443c319659e83ad5ee133e6f11af51d883e56216e9e1bbb1e2920c7c6120cbb55cd469b1f95b61601", + "7474449cca95dc5d0c00e71735a6d17d3cd15a3fd6e04e47bee3922dbfa92c8d63d0920de0c7315ebaed1d639d926961d28af89461c31eca890441e449147d23bb7c9d4fc42d7c16bfa5709836ba729443c319659e83ad5ee133e6f11af51d883e56216e9e1bbb1e2920c7c6120cbb55cd469b1f95b61601", + "7474449cca95dc5d0c00e71735a6d17d3cd15a3fd6e04e47bee3922dbfa92c8d7912c66be82a5972e5bc11c8d10551a296ba9aaff8ca6ab22a8cd1987974b87a97121c871f786d2e17e0a629acf01c38947f170b7e02a9ebb4ee60f83779acb99b71114c01a4f0a60694611a1502c399c77214ffa26e955b", + "7474449cca95dc5d0c00e71735a6d17d3cd15a3fd6e04e47bee3922dbfa92c8d7aa00f217f3a374a2f1ca0f388719f84099e8157a8a83c5ccf54eae1617f93933fa976baa629e6febfa5709836ba729443c319659e83ad5ee133e6f11af51d883e56216e9e1bbb1e2920c7c6120cbb55cd469b1f95b61601", + "7474449cca95dc5d0c00e71735a6d17d3cd15a3fd6e04e47bee3922dbfa92c8d9e1c3c8ab41943cf377b1aa724d7f518a3cfc96a732bdc4658155d09ed2bfc31b5ccbc6d8646b59f1b8f41540e0a793b0fd51b245dae19382b45386570f2b545fab75e3277910f7324b55f47c29f9965e8298371404e50ac", + "7474449cca95dc5d0c00e71735a6d17d3cd15a3fd6e04e47bee3922dbfa92c8d9fb8d6d95d5214a3305a4fa07e344eb99fad4be3565d646c8ac5af85514d9c96702c9c207be234958dbdb9185f467d2be3b84e8b2f529f7ec3844b378a889afd6bd31a9b5ed22ffee2019ad82c6692f1736dd41c8bb85726", + "7474449cca95dc5d0c00e71735a6d17d3cd15a3fd6e04e47bee3922dbfa92c8d9fb8d6d95d5214a3305a4fa07e344eb99fad4be3565d646c8ac5af85514d9c96702c9c207be23495ec1caa509591a36a8403684384ce40838c9bd7fc49d933a10d3b26e979273e2f17ebf0bf41cd90e4287e126a59d5a243", + "7474449cca95dc5d0c00e71735a6d17d3cd15a3fd6e04e47bee3922dbfa92c8da7fc066aae2ffe03b36e9a72f9a39cb2befac7e47f320309f31f1c1676288d9596045807304b3d79bfa5709836ba729443c319659e83ad5ee133e6f11af51d883e56216e9e1bbb1e2920c7c6120cbb55cd469b1f95b61601", + "7474449cca95dc5d0c00e71735a6d17d3cd15a3fd6e04e47bee3922dbfa92c8daf3c377b0fddf7c7ad6d390fab0ab45ac16c21645be880af5cab2fbbeb04820401a4c9f766c17bef9fc14a2e16ade86fe26ee81d4497dc6aab81cc5f5bb0458d6149a763ecb09aefec06950dd61db1ba025401d2a04e3b9d", + "7474449cca95dc5d0c00e71735a6d17d3cd15a3fd6e04e47bee3922dbfa92c8daf3c377b0fddf7c7ad6d390fab0ab45ac16c21645be880af5cab2fbbeb04820401a4c9f766c17befbfa5709836ba729443c319659e83ad5ee133e6f11af51d883e56216e9e1bbb1e2920c7c6120cbb55cd469b1f95b61601", + "7474449cca95dc5d0c00e71735a6d17d3cd15a3fd6e04e47bee3922dbfa92c8db60505ba8b77ef03ed805436d3242f26dc828084b12aaf4bcb96af468816a182b5360149398aad6b1dafe949b0918138ceef924f6393d1818a04842301294604972da17b24b31b155e4409a01273733b8d21a156c2e7eb71", + "7474449cca95dc5d0c00e71735a6d17d3cd15a3fd6e04e47bee3922dbfa92c8dbd27136a6e028656073cc840bfabb48fe935880c4c4c990ee98458b2fed308e9765f7f7f717dd3b2862fa5361d3b55afa6040e582687403c852b2d065b24f253276cc581226991f8e1818a78fc64c39da7f0b383c6726e0f", + "7474449cca95dc5d0c00e71735a6d17d3cd15a3fd6e04e47bee3922dbfa92c8dca40d91320edd326500f9e8b5a0b23a8bdf21549f98f0e014f66b6a18bdd78e337a6c05d670c80c88a55d4c7bb6fbae546e2d03ac9ab16e85fe11dad6adfd6a20618905477b831d7d48ca32d0bfd2bdc8dbeba26ffe2c710", + "7474449cca95dc5d0c00e71735a6d17d3cd15a3fd6e04e47bee3922dbfa92c8dd27478512243ed62c1c1f7066021798a464d4cf9099546d5d9907b3369f1b9d7a5aa5d60ca845619bfa5709836ba729443c319659e83ad5ee133e6f11af51d883e56216e9e1bbb1e2920c7c6120cbb55cd469b1f95b61601", + "7474449cca95dc5d0c00e71735a6d17d3cd15a3fd6e04e47bee3922dbfa92c8de6da5659cbbe1489abbe99c4d3a474f4d1e78edb55a9be68d8f52c6fe730388a298e6f6325db3da7bfa5709836ba729443c319659e83ad5ee133e6f11af51d883e56216e9e1bbb1e2920c7c6120cbb55cd469b1f95b61601", + "7474449cca95dc5d0c00e71735a6d17d3cd15a3fd6e04e47bee3922dbfa92c8de6da5659cbbe1489abbe99c4d3a474f4d1e78edb55a9be68d8f52c6fe730388a298e6f6325db3da7e94ca3e8c297d82f71e232a2892992d1f6480475fb797ce64e58f773d8fafd9fbcee4bdf4b14f2a71b6d3a428cf9f24b", + "7474449cca95dc5d0c00e71735a6d17d3cd15a3fd6e04e47bee3922dbfa92c8decdd1760c61ff7234f2876dbe817af803170233320d778b92043b2359e3de6d16c9e5359f6302da31c84d6f551ad2a831263ef956f0cdb3b4810cefcb2d0b57bcce7b82007016ae4fe752c31d1a01b589a7966cea03ec65c", + "7474449cca95dc5d0c00e71735a6d17d3cd15a3fd6e04e47bee3922dbfa92c8df9981ee6b69eb7af2153af34f39ffc06e2daa5272c99798c8849091284dc8905f2a76b65754c2089bfa5709836ba729443c319659e83ad5ee133e6f11af51d883e56216e9e1bbb1e2920c7c6120cbb55cd469b1f95b61601", + "7474449cca95dc5d0c00e71735a6d17d4e7b9012096b41c4eb3aaf947f6ea429", + "89d139e01a5eb2256f222e5fc5dbe6b33c9c1284130706f5aea0c8b3d4c54d89", + "89d139e01a5eb2256f222e5fc5dbe6b36254e9d55588784fa2a62b726696e2b1" + ]); + + let key = array_bytes::hex2bytes("7474449cca95dc5d0c00e71735a6d17d3cd15a3fd6e04e47bee3922dbfa92c8da7dad55cf08ffe8194efa962146801b0503092b1ed6a3fa6aee9107334aefd7965bbe568c3d24c6d").unwrap(); + + assert_eq!( + trie.keys(IterArgs { + prefix: Some(&key), + start_at: Some(&key), + start_at_exclusive: true, + ..IterArgs::default() + }) + .unwrap() + .map(|result| result.unwrap()) + .collect::>(), + Vec::>::new() + ); + } + parameterized_test!(storage_root_is_non_default, storage_root_is_non_default_inner); fn storage_root_is_non_default_inner( state_version: StateVersion, From d574cb75ce70148f5e082201beaef24b5a00741e Mon Sep 17 00:00:00 2001 From: PG Herveou Date: Fri, 17 Mar 2023 23:17:21 +0100 Subject: [PATCH 255/558] contracts: Refactor `trait Ext::*_storage_transparent` functions (#13600) * Refactor _transparent methods rewrote commits, stashed the typo changes to remove some diff noise fixed my unverified email commit * remove type alias * Get rid of From impl blocks * Get rid of KeyType impl block * remove unnecessary Key export * Update frame/contracts/src/exec.rs Co-authored-by: Sasha Gryaznov * PR review comment --------- Co-authored-by: Sasha Gryaznov --- bin/node/executor/tests/basic.rs | 6 +- frame/contracts/src/benchmarking/mod.rs | 28 +-- frame/contracts/src/exec.rs | 267 ++++++++++-------------- frame/contracts/src/lib.rs | 8 +- frame/contracts/src/storage.rs | 10 +- frame/contracts/src/tests.rs | 6 +- frame/contracts/src/wasm/mod.rs | 68 ++---- frame/contracts/src/wasm/runtime.rs | 103 ++++----- 8 files changed, 200 insertions(+), 296 deletions(-) diff --git a/bin/node/executor/tests/basic.rs b/bin/node/executor/tests/basic.rs index ecfe02aa4..9200fe735 100644 --- a/bin/node/executor/tests/basic.rs +++ b/bin/node/executor/tests/basic.rs @@ -710,11 +710,7 @@ fn deploying_wasm_contract_should_work() { t.execute_with(|| { // Verify that the contract does exist by querying some of its storage items // It does not matter that the storage item itself does not exist. - assert!(&pallet_contracts::Pallet::::get_storage( - addr, - pallet_contracts::StorageKey::::default().to_vec() - ) - .is_ok()); + assert!(&pallet_contracts::Pallet::::get_storage(addr, vec![]).is_ok()); }); } diff --git a/frame/contracts/src/benchmarking/mod.rs b/frame/contracts/src/benchmarking/mod.rs index 96368ebb3..2fb033cf8 100644 --- a/frame/contracts/src/benchmarking/mod.rs +++ b/frame/contracts/src/benchmarking/mod.rs @@ -30,7 +30,7 @@ use self::{ sandbox::Sandbox, }; use crate::{ - exec::{AccountIdOf, FixSizedKey, VarSizedKey}, + exec::{AccountIdOf, Key}, wasm::CallFlags, Pallet as Contracts, *, }; @@ -135,10 +135,10 @@ where } /// Store the supplied storage items into this contracts storage. - fn store(&self, items: &Vec<(FixSizedKey, Vec)>) -> Result<(), &'static str> { + fn store(&self, items: &Vec<([u8; 32], Vec)>) -> Result<(), &'static str> { let info = self.info()?; for item in items { - info.write(&item.0 as &FixSizedKey, Some(item.1.clone()), None, false) + info.write(&Key::Fix(item.0), Some(item.1.clone()), None, false) .map_err(|_| "Failed to write storage to restoration dest")?; } >::insert(&self.account_id, info); @@ -1044,7 +1044,7 @@ benchmarks! { let info = instance.info()?; for key in keys { info.write( - &VarSizedKey::::try_from(key).map_err(|e| "Key has wrong length")?, + &Key::::try_from_var(key).map_err(|e| "Key has wrong length")?, Some(vec![]), None, false, @@ -1088,7 +1088,7 @@ benchmarks! { let instance = Contract::::new(code, vec![])?; let info = instance.info()?; info.write( - &VarSizedKey::::try_from(key).map_err(|e| "Key has wrong length")?, + &Key::::try_from_var(key).map_err(|e| "Key has wrong length")?, Some(vec![]), None, false, @@ -1131,7 +1131,7 @@ benchmarks! { let instance = Contract::::new(code, vec![])?; let info = instance.info()?; info.write( - &VarSizedKey::::try_from(key).map_err(|e| "Key has wrong length")?, + &Key::::try_from_var(key).map_err(|e| "Key has wrong length")?, Some(vec![42u8; n as usize]), None, false, @@ -1180,7 +1180,7 @@ benchmarks! { let info = instance.info()?; for key in keys { info.write( - &VarSizedKey::::try_from(key).map_err(|e| "Key has wrong length")?, + &Key::::try_from_var(key).map_err(|e| "Key has wrong length")?, Some(vec![]), None, false, @@ -1223,7 +1223,7 @@ benchmarks! { let instance = Contract::::new(code, vec![])?; let info = instance.info()?; info.write( - &VarSizedKey::::try_from(key).map_err(|e| "Key has wrong length")?, + &Key::::try_from_var(key).map_err(|e| "Key has wrong length")?, Some(vec![42u8; n as usize]), None, false, @@ -1276,7 +1276,7 @@ benchmarks! { let info = instance.info()?; for key in keys { info.write( - &VarSizedKey::::try_from(key).map_err(|e| "Key has wrong length")?, + &Key::::try_from_var(key).map_err(|e| "Key has wrong length")?, Some(vec![]), None, false, @@ -1325,7 +1325,7 @@ benchmarks! { let instance = Contract::::new(code, vec![])?; let info = instance.info()?; info.write( - &VarSizedKey::::try_from(key).map_err(|e| "Key has wrong length")?, + &Key::::try_from_var(key).map_err(|e| "Key has wrong length")?, Some(vec![42u8; n as usize]), None, false, @@ -1373,7 +1373,7 @@ benchmarks! { let info = instance.info()?; for key in keys { info.write( - &VarSizedKey::::try_from(key).map_err(|e| "Key has wrong length")?, + &Key::::try_from_var(key).map_err(|e| "Key has wrong length")?, Some(vec![]), None, false, @@ -1416,7 +1416,7 @@ benchmarks! { let instance = Contract::::new(code, vec![])?; let info = instance.info()?; info.write( - &VarSizedKey::::try_from(key).map_err(|e| "Key has wrong length")?, + &Key::::try_from_var(key).map_err(|e| "Key has wrong length")?, Some(vec![42u8; n as usize]), None, false, @@ -1469,7 +1469,7 @@ benchmarks! { let info = instance.info()?; for key in keys { info.write( - &VarSizedKey::::try_from(key).map_err(|e| "Key has wrong length")?, + &Key::::try_from_var(key).map_err(|e| "Key has wrong length")?, Some(vec![]), None, false, @@ -1518,7 +1518,7 @@ benchmarks! { let instance = Contract::::new(code, vec![])?; let info = instance.info()?; info.write( - &VarSizedKey::::try_from(key).map_err(|e| "Key has wrong length")?, + &Key::::try_from_var(key).map_err(|e| "Key has wrong length")?, Some(vec![42u8; n as usize]), None, false, diff --git a/frame/contracts/src/exec.rs b/frame/contracts/src/exec.rs index 35ef43da5..bd63c524c 100644 --- a/frame/contracts/src/exec.rs +++ b/frame/contracts/src/exec.rs @@ -35,7 +35,7 @@ use smallvec::{Array, SmallVec}; use sp_core::ecdsa::Public as ECDSAPublic; use sp_io::{crypto::secp256k1_ecdsa_recover_compressed, hashing::blake2_256}; use sp_runtime::traits::{Convert, Hash}; -use sp_std::{marker::PhantomData, mem, prelude::*}; +use sp_std::{marker::PhantomData, mem, prelude::*, vec::Vec}; pub type AccountIdOf = ::AccountId; pub type MomentOf = <::Time as Time>::Moment; @@ -46,32 +46,39 @@ pub type ExecResult = Result; /// A type that represents a topic of an event. At the moment a hash is used. pub type TopicOf = ::Hash; -/// Type for fix sized storage key. -pub type FixSizedKey = [u8; 32]; - /// Type for variable sized storage key. Used for transparent hashing. -pub type VarSizedKey = BoundedVec::MaxStorageKeyLen>; - -/// Trait for hashing storage keys. -pub trait StorageKey -where - T: Config, -{ - fn hash(&self) -> Vec; +type VarSizedKey = BoundedVec::MaxStorageKeyLen>; + +/// Combined key type for both fixed and variable sized storage keys. +pub enum Key { + /// Variant for fixed sized keys. + Fix([u8; 32]), + /// Variant for variable sized keys. + Var(VarSizedKey), } -impl StorageKey for FixSizedKey { - fn hash(&self) -> Vec { - blake2_256(self.as_slice()).to_vec() +impl Key { + /// Copies self into a new vec. + pub fn to_vec(&self) -> Vec { + match self { + Key::Fix(v) => v.to_vec(), + Key::Var(v) => v.to_vec(), + } } -} -impl StorageKey for VarSizedKey -where - T: Config, -{ - fn hash(&self) -> Vec { - Blake2_128Concat::hash(self.as_slice()) + pub fn hash(&self) -> Vec { + match self { + Key::Fix(v) => blake2_256(v.as_slice()).to_vec(), + Key::Var(v) => Blake2_128Concat::hash(v.as_slice()), + } + } + + pub fn try_from_fix(v: Vec) -> Result> { + <[u8; 32]>::try_from(v).map(Self::Fix) + } + + pub fn try_from_var(v: Vec) -> Result> { + VarSizedKey::::try_from(v).map(Self::Var) } } @@ -169,44 +176,19 @@ pub trait Ext: sealing::Sealed { /// /// Returns `None` if the `key` wasn't previously set by `set_storage` or /// was deleted. - fn get_storage(&mut self, key: &FixSizedKey) -> Option>; - - /// This is a variation of `get_storage()` to be used with transparent hashing. - /// These two will be merged into a single function after some refactoring is done. - /// Returns the storage entry of the executing account by the given `key`. - /// - /// Returns `None` if the `key` wasn't previously set by `set_storage` or - /// was deleted. - fn get_storage_transparent(&mut self, key: &VarSizedKey) -> Option>; + fn get_storage(&mut self, key: &Key) -> Option>; /// Returns `Some(len)` (in bytes) if a storage item exists at `key`. /// /// Returns `None` if the `key` wasn't previously set by `set_storage` or /// was deleted. - fn get_storage_size(&mut self, key: &FixSizedKey) -> Option; - - /// This is the variation of `get_storage_size()` to be used with transparent hashing. - /// These two will be merged into a single function after some refactoring is done. - /// Returns `Some(len)` (in bytes) if a storage item exists at `key`. - /// - /// Returns `None` if the `key` wasn't previously set by `set_storage` or - /// was deleted. - fn get_storage_size_transparent(&mut self, key: &VarSizedKey) -> Option; + fn get_storage_size(&mut self, key: &Key) -> Option; /// Sets the storage entry by the given key to the specified value. If `value` is `None` then /// the storage entry is deleted. fn set_storage( &mut self, - key: &FixSizedKey, - value: Option>, - take_old: bool, - ) -> Result; - - /// This is the variation of `set_storage()` to be used with transparent hashing. - /// These two will be merged into a single function after some refactoring is done. - fn set_storage_transparent( - &mut self, - key: &VarSizedKey, + key: &Key, value: Option>, take_old: bool, ) -> Result; @@ -1236,46 +1218,23 @@ where Self::transfer(ExistenceRequirement::KeepAlive, &self.top_frame().account_id, to, value) } - fn get_storage(&mut self, key: &FixSizedKey) -> Option> { - self.top_frame_mut().contract_info().read(key) - } - - fn get_storage_transparent(&mut self, key: &VarSizedKey) -> Option> { + fn get_storage(&mut self, key: &Key) -> Option> { self.top_frame_mut().contract_info().read(key) } - fn get_storage_size(&mut self, key: &FixSizedKey) -> Option { - self.top_frame_mut().contract_info().size(key) - } - - fn get_storage_size_transparent(&mut self, key: &VarSizedKey) -> Option { - self.top_frame_mut().contract_info().size(key) + fn get_storage_size(&mut self, key: &Key) -> Option { + self.top_frame_mut().contract_info().size(key.into()) } fn set_storage( &mut self, - key: &FixSizedKey, + key: &Key, value: Option>, take_old: bool, ) -> Result { let frame = self.top_frame_mut(); frame.contract_info.get(&frame.account_id).write( - key, - value, - Some(&mut frame.nested_storage), - take_old, - ) - } - - fn set_storage_transparent( - &mut self, - key: &VarSizedKey, - value: Option>, - take_old: bool, - ) -> Result { - let frame = self.top_frame_mut(); - frame.contract_info.get(&frame.account_id).write( - key, + key.into(), value, Some(&mut frame.nested_storage), take_old, @@ -1688,7 +1647,7 @@ mod tests { place_contract(&dest, success_ch); set_balance(&origin, 100); let balance = get_balance(&dest); - let mut storage_meter = storage::meter::Meter::new(&origin, Some(0), 55).unwrap(); + let mut storage_meter = storage::meter::Meter::new(&origin, Some(0), value).unwrap(); let _ = MockStack::run_call( origin.clone(), @@ -2986,35 +2945,41 @@ mod tests { let code_hash = MockLoader::insert(Call, |ctx, _| { // Write assert_eq!( - ctx.ext.set_storage(&[1; 32], Some(vec![1, 2, 3]), false), + ctx.ext.set_storage(&Key::Fix([1; 32]), Some(vec![1, 2, 3]), false), + Ok(WriteOutcome::New) + ); + assert_eq!( + ctx.ext.set_storage(&Key::Fix([2; 32]), Some(vec![4, 5, 6]), true), + Ok(WriteOutcome::New) + ); + assert_eq!(ctx.ext.set_storage(&Key::Fix([3; 32]), None, false), Ok(WriteOutcome::New)); + assert_eq!(ctx.ext.set_storage(&Key::Fix([4; 32]), None, true), Ok(WriteOutcome::New)); + assert_eq!( + ctx.ext.set_storage(&Key::Fix([5; 32]), Some(vec![]), false), Ok(WriteOutcome::New) ); assert_eq!( - ctx.ext.set_storage(&[2; 32], Some(vec![4, 5, 6]), true), + ctx.ext.set_storage(&Key::Fix([6; 32]), Some(vec![]), true), Ok(WriteOutcome::New) ); - assert_eq!(ctx.ext.set_storage(&[3; 32], None, false), Ok(WriteOutcome::New)); - assert_eq!(ctx.ext.set_storage(&[4; 32], None, true), Ok(WriteOutcome::New)); - assert_eq!(ctx.ext.set_storage(&[5; 32], Some(vec![]), false), Ok(WriteOutcome::New)); - assert_eq!(ctx.ext.set_storage(&[6; 32], Some(vec![]), true), Ok(WriteOutcome::New)); // Overwrite assert_eq!( - ctx.ext.set_storage(&[1; 32], Some(vec![42]), false), + ctx.ext.set_storage(&Key::Fix([1; 32]), Some(vec![42]), false), Ok(WriteOutcome::Overwritten(3)) ); assert_eq!( - ctx.ext.set_storage(&[2; 32], Some(vec![48]), true), + ctx.ext.set_storage(&Key::Fix([2; 32]), Some(vec![48]), true), Ok(WriteOutcome::Taken(vec![4, 5, 6])) ); - assert_eq!(ctx.ext.set_storage(&[3; 32], None, false), Ok(WriteOutcome::New)); - assert_eq!(ctx.ext.set_storage(&[4; 32], None, true), Ok(WriteOutcome::New)); + assert_eq!(ctx.ext.set_storage(&Key::Fix([3; 32]), None, false), Ok(WriteOutcome::New)); + assert_eq!(ctx.ext.set_storage(&Key::Fix([4; 32]), None, true), Ok(WriteOutcome::New)); assert_eq!( - ctx.ext.set_storage(&[5; 32], Some(vec![]), false), + ctx.ext.set_storage(&Key::Fix([5; 32]), Some(vec![]), false), Ok(WriteOutcome::Overwritten(0)) ); assert_eq!( - ctx.ext.set_storage(&[6; 32], Some(vec![]), true), + ctx.ext.set_storage(&Key::Fix([6; 32]), Some(vec![]), true), Ok(WriteOutcome::Taken(vec![])) ); @@ -3043,52 +3008,52 @@ mod tests { } #[test] - fn set_storage_transparent_works() { + fn set_storage_varsized_key_works() { let code_hash = MockLoader::insert(Call, |ctx, _| { // Write assert_eq!( - ctx.ext.set_storage_transparent( - &VarSizedKey::::try_from([1; 64].to_vec()).unwrap(), + ctx.ext.set_storage( + &Key::::try_from_var([1; 64].to_vec()).unwrap(), Some(vec![1, 2, 3]), false ), Ok(WriteOutcome::New) ); assert_eq!( - ctx.ext.set_storage_transparent( - &VarSizedKey::::try_from([2; 19].to_vec()).unwrap(), + ctx.ext.set_storage( + &Key::::try_from_var([2; 19].to_vec()).unwrap(), Some(vec![4, 5, 6]), true ), Ok(WriteOutcome::New) ); assert_eq!( - ctx.ext.set_storage_transparent( - &VarSizedKey::::try_from([3; 19].to_vec()).unwrap(), + ctx.ext.set_storage( + &Key::::try_from_var([3; 19].to_vec()).unwrap(), None, false ), Ok(WriteOutcome::New) ); assert_eq!( - ctx.ext.set_storage_transparent( - &VarSizedKey::::try_from([4; 64].to_vec()).unwrap(), + ctx.ext.set_storage( + &Key::::try_from_var([4; 64].to_vec()).unwrap(), None, true ), Ok(WriteOutcome::New) ); assert_eq!( - ctx.ext.set_storage_transparent( - &VarSizedKey::::try_from([5; 30].to_vec()).unwrap(), + ctx.ext.set_storage( + &Key::::try_from_var([5; 30].to_vec()).unwrap(), Some(vec![]), false ), Ok(WriteOutcome::New) ); assert_eq!( - ctx.ext.set_storage_transparent( - &VarSizedKey::::try_from([6; 128].to_vec()).unwrap(), + ctx.ext.set_storage( + &Key::::try_from_var([6; 128].to_vec()).unwrap(), Some(vec![]), true ), @@ -3097,48 +3062,48 @@ mod tests { // Overwrite assert_eq!( - ctx.ext.set_storage_transparent( - &VarSizedKey::::try_from([1; 64].to_vec()).unwrap(), + ctx.ext.set_storage( + &Key::::try_from_var([1; 64].to_vec()).unwrap(), Some(vec![42, 43, 44]), false ), Ok(WriteOutcome::Overwritten(3)) ); assert_eq!( - ctx.ext.set_storage_transparent( - &VarSizedKey::::try_from([2; 19].to_vec()).unwrap(), + ctx.ext.set_storage( + &Key::::try_from_var([2; 19].to_vec()).unwrap(), Some(vec![48]), true ), Ok(WriteOutcome::Taken(vec![4, 5, 6])) ); assert_eq!( - ctx.ext.set_storage_transparent( - &VarSizedKey::::try_from([3; 19].to_vec()).unwrap(), + ctx.ext.set_storage( + &Key::::try_from_var([3; 19].to_vec()).unwrap(), None, false ), Ok(WriteOutcome::New) ); assert_eq!( - ctx.ext.set_storage_transparent( - &VarSizedKey::::try_from([4; 64].to_vec()).unwrap(), + ctx.ext.set_storage( + &Key::::try_from_var([4; 64].to_vec()).unwrap(), None, true ), Ok(WriteOutcome::New) ); assert_eq!( - ctx.ext.set_storage_transparent( - &VarSizedKey::::try_from([5; 30].to_vec()).unwrap(), + ctx.ext.set_storage( + &Key::::try_from_var([5; 30].to_vec()).unwrap(), Some(vec![]), false ), Ok(WriteOutcome::Overwritten(0)) ); assert_eq!( - ctx.ext.set_storage_transparent( - &VarSizedKey::::try_from([6; 128].to_vec()).unwrap(), + ctx.ext.set_storage( + &Key::::try_from_var([6; 128].to_vec()).unwrap(), Some(vec![]), true ), @@ -3173,13 +3138,16 @@ mod tests { fn get_storage_works() { let code_hash = MockLoader::insert(Call, |ctx, _| { assert_eq!( - ctx.ext.set_storage(&[1; 32], Some(vec![1, 2, 3]), false), + ctx.ext.set_storage(&Key::Fix([1; 32]), Some(vec![1, 2, 3]), false), Ok(WriteOutcome::New) ); - assert_eq!(ctx.ext.set_storage(&[2; 32], Some(vec![]), false), Ok(WriteOutcome::New)); - assert_eq!(ctx.ext.get_storage(&[1; 32]), Some(vec![1, 2, 3])); - assert_eq!(ctx.ext.get_storage(&[2; 32]), Some(vec![])); - assert_eq!(ctx.ext.get_storage(&[3; 32]), None); + assert_eq!( + ctx.ext.set_storage(&Key::Fix([2; 32]), Some(vec![]), false), + Ok(WriteOutcome::New) + ); + assert_eq!(ctx.ext.get_storage(&Key::Fix([1; 32])), Some(vec![1, 2, 3])); + assert_eq!(ctx.ext.get_storage(&Key::Fix([2; 32])), Some(vec![])); + assert_eq!(ctx.ext.get_storage(&Key::Fix([3; 32])), None); exec_success() }); @@ -3209,13 +3177,16 @@ mod tests { fn get_storage_size_works() { let code_hash = MockLoader::insert(Call, |ctx, _| { assert_eq!( - ctx.ext.set_storage(&[1; 32], Some(vec![1, 2, 3]), false), + ctx.ext.set_storage(&Key::Fix([1; 32]), Some(vec![1, 2, 3]), false), Ok(WriteOutcome::New) ); - assert_eq!(ctx.ext.set_storage(&[2; 32], Some(vec![]), false), Ok(WriteOutcome::New)); - assert_eq!(ctx.ext.get_storage_size(&[1; 32]), Some(3)); - assert_eq!(ctx.ext.get_storage_size(&[2; 32]), Some(0)); - assert_eq!(ctx.ext.get_storage_size(&[3; 32]), None); + assert_eq!( + ctx.ext.set_storage(&Key::Fix([2; 32]), Some(vec![]), false), + Ok(WriteOutcome::New) + ); + assert_eq!(ctx.ext.get_storage_size(&Key::Fix([1; 32])), Some(3)); + assert_eq!(ctx.ext.get_storage_size(&Key::Fix([2; 32])), Some(0)); + assert_eq!(ctx.ext.get_storage_size(&Key::Fix([3; 32])), None); exec_success() }); @@ -3242,40 +3213,34 @@ mod tests { } #[test] - fn get_storage_transparent_works() { + fn get_storage_varsized_key_works() { let code_hash = MockLoader::insert(Call, |ctx, _| { assert_eq!( - ctx.ext.set_storage_transparent( - &VarSizedKey::::try_from([1; 19].to_vec()).unwrap(), + ctx.ext.set_storage( + &Key::::try_from_var([1; 19].to_vec()).unwrap(), Some(vec![1, 2, 3]), false ), Ok(WriteOutcome::New) ); assert_eq!( - ctx.ext.set_storage_transparent( - &VarSizedKey::::try_from([2; 16].to_vec()).unwrap(), + ctx.ext.set_storage( + &Key::::try_from_var([2; 16].to_vec()).unwrap(), Some(vec![]), false ), Ok(WriteOutcome::New) ); assert_eq!( - ctx.ext.get_storage_transparent( - &VarSizedKey::::try_from([1; 19].to_vec()).unwrap() - ), + ctx.ext.get_storage(&Key::::try_from_var([1; 19].to_vec()).unwrap()), Some(vec![1, 2, 3]) ); assert_eq!( - ctx.ext.get_storage_transparent( - &VarSizedKey::::try_from([2; 16].to_vec()).unwrap() - ), + ctx.ext.get_storage(&Key::::try_from_var([2; 16].to_vec()).unwrap()), Some(vec![]) ); assert_eq!( - ctx.ext.get_storage_transparent( - &VarSizedKey::::try_from([3; 8].to_vec()).unwrap() - ), + ctx.ext.get_storage(&Key::::try_from_var([3; 8].to_vec()).unwrap()), None ); @@ -3304,40 +3269,34 @@ mod tests { } #[test] - fn get_storage_size_transparent_works() { + fn get_storage_size_varsized_key_works() { let code_hash = MockLoader::insert(Call, |ctx, _| { assert_eq!( - ctx.ext.set_storage_transparent( - &VarSizedKey::::try_from([1; 19].to_vec()).unwrap(), + ctx.ext.set_storage( + &Key::::try_from_var([1; 19].to_vec()).unwrap(), Some(vec![1, 2, 3]), false ), Ok(WriteOutcome::New) ); assert_eq!( - ctx.ext.set_storage_transparent( - &VarSizedKey::::try_from([2; 16].to_vec()).unwrap(), + ctx.ext.set_storage( + &Key::::try_from_var([2; 16].to_vec()).unwrap(), Some(vec![]), false ), Ok(WriteOutcome::New) ); assert_eq!( - ctx.ext.get_storage_size_transparent( - &VarSizedKey::::try_from([1; 19].to_vec()).unwrap() - ), + ctx.ext.get_storage_size(&Key::::try_from_var([1; 19].to_vec()).unwrap()), Some(3) ); assert_eq!( - ctx.ext.get_storage_size_transparent( - &VarSizedKey::::try_from([2; 16].to_vec()).unwrap() - ), + ctx.ext.get_storage_size(&Key::::try_from_var([2; 16].to_vec()).unwrap()), Some(0) ); assert_eq!( - ctx.ext.get_storage_size_transparent( - &VarSizedKey::::try_from([3; 8].to_vec()).unwrap() - ), + ctx.ext.get_storage_size(&Key::::try_from_var([3; 8].to_vec()).unwrap()), None ); diff --git a/frame/contracts/src/lib.rs b/frame/contracts/src/lib.rs index 0aff18fd6..7aac94d11 100644 --- a/frame/contracts/src/lib.rs +++ b/frame/contracts/src/lib.rs @@ -100,7 +100,7 @@ pub mod weights; mod tests; use crate::{ - exec::{AccountIdOf, ErrorOrigin, ExecError, Executable, Stack as ExecStack}, + exec::{AccountIdOf, ErrorOrigin, ExecError, Executable, Key, Stack as ExecStack}, gas::GasMeter, storage::{meter::Meter as StorageMeter, ContractInfo, DeletedContract}, wasm::{OwnerInfo, PrefabWasmModule, TryInstantiate}, @@ -131,7 +131,7 @@ use sp_std::{fmt::Debug, marker::PhantomData, prelude::*}; pub use crate::{ address::{AddressGenerator, DefaultAddressGenerator}, - exec::{Frame, VarSizedKey as StorageKey}, + exec::Frame, migration::Migration, pallet::*, schedule::{HostFnWeights, InstructionWeights, Limits, Schedule}, @@ -1265,7 +1265,9 @@ impl Pallet { ContractInfoOf::::get(&address).ok_or(ContractAccessError::DoesntExist)?; let maybe_value = contract_info.read( - &StorageKey::::try_from(key).map_err(|_| ContractAccessError::KeyDecodingFailed)?, + &Key::::try_from_var(key) + .map_err(|_| ContractAccessError::KeyDecodingFailed)? + .into(), ); Ok(maybe_value) } diff --git a/frame/contracts/src/storage.rs b/frame/contracts/src/storage.rs index 26162b55c..19c5f391d 100644 --- a/frame/contracts/src/storage.rs +++ b/frame/contracts/src/storage.rs @@ -20,7 +20,7 @@ pub mod meter; use crate::{ - exec::{AccountIdOf, StorageKey}, + exec::{AccountIdOf, Key}, weights::WeightInfo, AddressGenerator, BalanceOf, CodeHash, Config, ContractInfoOf, DeletionQueue, Error, Pallet, TrieId, SENTINEL, @@ -132,7 +132,7 @@ impl ContractInfo { /// /// The read is performed from the `trie_id` only. The `address` is not necessary. If the /// contract doesn't store under the given `key` `None` is returned. - pub fn read>(&self, key: &K) -> Option> { + pub fn read(&self, key: &Key) -> Option> { child::get_raw(&self.child_trie_info(), key.hash().as_slice()) } @@ -140,7 +140,7 @@ impl ContractInfo { /// /// Returns `None` if the `key` wasn't previously set by `set_storage` or /// was deleted. - pub fn size>(&self, key: &K) -> Option { + pub fn size(&self, key: &Key) -> Option { child::len(&self.child_trie_info(), key.hash().as_slice()) } @@ -151,9 +151,9 @@ impl ContractInfo { /// /// This function also records how much storage was created or removed if a `storage_meter` /// is supplied. It should only be absent for testing or benchmarking code. - pub fn write>( + pub fn write( &self, - key: &K, + key: &Key, new_value: Option>, storage_meter: Option<&mut meter::NestedMeter>, take: bool, diff --git a/frame/contracts/src/tests.rs b/frame/contracts/src/tests.rs index ba9873f07..30a39eebf 100644 --- a/frame/contracts/src/tests.rs +++ b/frame/contracts/src/tests.rs @@ -21,7 +21,7 @@ use crate::{ ChainExtension, Environment, Ext, InitState, RegisteredChainExtension, Result as ExtensionResult, RetVal, ReturnFlags, SysConfig, }, - exec::{FixSizedKey, Frame}, + exec::{Frame, Key}, tests::test_utils::{get_contract, get_contract_checked}, wasm::{Determinism, PrefabWasmModule, ReturnCode as RuntimeReturnCode}, weights::WeightInfo, @@ -2145,7 +2145,7 @@ fn lazy_removal_partial_remove_works() { // Put value into the contracts child trie for val in &vals { - info.write(&val.0 as &FixSizedKey, Some(val.2.clone()), None, false).unwrap(); + info.write(&Key::Fix(val.0), Some(val.2.clone()), None, false).unwrap(); } >::insert(&addr, info.clone()); @@ -2330,7 +2330,7 @@ fn lazy_removal_does_not_use_all_weight() { // Put value into the contracts child trie for val in &vals { - info.write(&val.0 as &FixSizedKey, Some(val.2.clone()), None, false).unwrap(); + info.write(&Key::Fix(val.0), Some(val.2.clone()), None, false).unwrap(); } >::insert(&addr, info.clone()); diff --git a/frame/contracts/src/wasm/mod.rs b/frame/contracts/src/wasm/mod.rs index f74df2c36..cfda9c376 100644 --- a/frame/contracts/src/wasm/mod.rs +++ b/frame/contracts/src/wasm/mod.rs @@ -366,10 +366,7 @@ impl Executable for PrefabWasmModule { mod tests { use super::*; use crate::{ - exec::{ - AccountIdOf, BlockNumberOf, ErrorOrigin, ExecError, Executable, Ext, FixSizedKey, - SeedOf, VarSizedKey, - }, + exec::{AccountIdOf, BlockNumberOf, ErrorOrigin, ExecError, Executable, Ext, Key, SeedOf}, gas::GasMeter, storage::WriteOutcome, tests::{RuntimeCall, Test, ALICE, BOB}, @@ -521,40 +518,15 @@ mod tests { self.terminations.push(TerminationEntry { beneficiary: beneficiary.clone() }); Ok(()) } - fn get_storage(&mut self, key: &FixSizedKey) -> Option> { + fn get_storage(&mut self, key: &Key) -> Option> { self.storage.get(&key.to_vec()).cloned() } - fn get_storage_transparent(&mut self, key: &VarSizedKey) -> Option> { - self.storage.get(&key.to_vec()).cloned() - } - fn get_storage_size(&mut self, key: &FixSizedKey) -> Option { - self.storage.get(&key.to_vec()).map(|val| val.len() as u32) - } - fn get_storage_size_transparent(&mut self, key: &VarSizedKey) -> Option { + fn get_storage_size(&mut self, key: &Key) -> Option { self.storage.get(&key.to_vec()).map(|val| val.len() as u32) } fn set_storage( &mut self, - key: &FixSizedKey, - value: Option>, - take_old: bool, - ) -> Result { - let key = key.to_vec(); - let entry = self.storage.entry(key.clone()); - let result = match (entry, take_old) { - (Entry::Vacant(_), _) => WriteOutcome::New, - (Entry::Occupied(entry), false) => - WriteOutcome::Overwritten(entry.remove().len() as u32), - (Entry::Occupied(entry), true) => WriteOutcome::Taken(entry.remove()), - }; - if let Some(value) = value { - self.storage.insert(key, value); - } - Ok(result) - } - fn set_storage_transparent( - &mut self, - key: &VarSizedKey, + key: &Key, value: Option>, take_old: bool, ) -> Result { @@ -1071,14 +1043,14 @@ mod tests { "#; let mut ext = MockExt::default(); - ext.set_storage_transparent( - &VarSizedKey::::try_from([1u8; 64].to_vec()).unwrap(), + ext.set_storage( + &Key::::try_from_var([1u8; 64].to_vec()).unwrap(), Some(vec![42u8]), false, ) .unwrap(); - ext.set_storage_transparent( - &VarSizedKey::::try_from([2u8; 19].to_vec()).unwrap(), + ext.set_storage( + &Key::::try_from_var([2u8; 19].to_vec()).unwrap(), Some(vec![]), false, ) @@ -2547,15 +2519,15 @@ mod tests { let mut ext = MockExt::default(); - ext.set_storage_transparent( - &VarSizedKey::::try_from([1u8; 64].to_vec()).unwrap(), + ext.set_storage( + &Key::::try_from_var([1u8; 64].to_vec()).unwrap(), Some(vec![42u8]), false, ) .unwrap(); - ext.set_storage_transparent( - &VarSizedKey::::try_from([2u8; 19].to_vec()).unwrap(), + ext.set_storage( + &Key::::try_from_var([2u8; 19].to_vec()).unwrap(), Some(vec![]), false, ) @@ -2631,14 +2603,14 @@ mod tests { let mut ext = MockExt::default(); - ext.set_storage_transparent( - &VarSizedKey::::try_from([1u8; 64].to_vec()).unwrap(), + ext.set_storage( + &Key::::try_from_var([1u8; 64].to_vec()).unwrap(), Some(vec![42u8]), false, ) .unwrap(); - ext.set_storage_transparent( - &VarSizedKey::::try_from([2u8; 19].to_vec()).unwrap(), + ext.set_storage( + &Key::::try_from_var([2u8; 19].to_vec()).unwrap(), Some(vec![]), false, ) @@ -2728,15 +2700,15 @@ mod tests { let mut ext = MockExt::default(); - ext.set_storage_transparent( - &VarSizedKey::::try_from([1u8; 64].to_vec()).unwrap(), + ext.set_storage( + &Key::::try_from_var([1u8; 64].to_vec()).unwrap(), Some(vec![42u8]), false, ) .unwrap(); - ext.set_storage_transparent( - &VarSizedKey::::try_from([2u8; 19].to_vec()).unwrap(), + ext.set_storage( + &Key::::try_from_var([2u8; 19].to_vec()).unwrap(), Some(vec![]), false, ) diff --git a/frame/contracts/src/wasm/runtime.rs b/frame/contracts/src/wasm/runtime.rs index b30bad38b..a8a19e6f9 100644 --- a/frame/contracts/src/wasm/runtime.rs +++ b/frame/contracts/src/wasm/runtime.rs @@ -18,7 +18,7 @@ //! Environment definition of the wasm smart-contract runtime. use crate::{ - exec::{ExecError, ExecResult, Ext, FixSizedKey, TopicOf, VarSizedKey}, + exec::{ExecError, ExecResult, Ext, Key, TopicOf}, gas::{ChargedAmount, Token}, schedule::HostFnWeights, BalanceOf, CodeHash, Config, DebugBufferVec, Error, SENTINEL, @@ -73,19 +73,7 @@ enum KeyType { Fix, /// Variable sized key used in transparent hashing, /// cannot be larger than MaxStorageKeyLen. - Variable(u32), -} - -impl KeyType { - fn len(&self) -> Result { - match self { - KeyType::Fix => Ok(32u32), - KeyType::Variable(len) => { - ensure!(len <= &::MaxStorageKeyLen::get(), Error::::DecodingFailed); - Ok(*len) - }, - } - } + Var(u32), } /// Every error that can be returned to a contract when it calls any of the host functions. @@ -752,6 +740,29 @@ impl<'a, E: Ext + 'a> Runtime<'a, E> { (err, _) => Self::err_into_return_code(err), } } + fn decode_key( + &self, + memory: &[u8], + key_type: KeyType, + key_ptr: u32, + ) -> Result, TrapReason> { + let res = match key_type { + KeyType::Fix => { + let key = self.read_sandbox_memory(memory, key_ptr, 32u32)?; + Key::try_from_fix(key) + }, + KeyType::Var(len) => { + ensure!( + len <= <::T as Config>::MaxStorageKeyLen::get(), + Error::::DecodingFailed + ); + let key = self.read_sandbox_memory(memory, key_ptr, len)?; + Key::try_from_var(key) + }, + }; + + res.map_err(|_| Error::::DecodingFailed.into()) + } fn set_storage( &mut self, @@ -767,20 +778,9 @@ impl<'a, E: Ext + 'a> Runtime<'a, E> { if value_len > max_size { return Err(Error::::ValueTooLarge.into()) } - let key = self.read_sandbox_memory(memory, key_ptr, key_type.len::()?)?; + let key = self.decode_key(memory, key_type, key_ptr)?; let value = Some(self.read_sandbox_memory(memory, value_ptr, value_len)?); - let write_outcome = match key_type { - KeyType::Fix => self.ext.set_storage( - &FixSizedKey::try_from(key).map_err(|_| Error::::DecodingFailed)?, - value, - false, - )?, - KeyType::Variable(_) => self.ext.set_storage_transparent( - &VarSizedKey::::try_from(key).map_err(|_| Error::::DecodingFailed)?, - value, - false, - )?, - }; + let write_outcome = self.ext.set_storage(&key, value, false)?; self.adjust_gas( charged, @@ -796,19 +796,8 @@ impl<'a, E: Ext + 'a> Runtime<'a, E> { key_ptr: u32, ) -> Result { let charged = self.charge_gas(RuntimeCosts::ClearStorage(self.ext.max_value_size()))?; - let key = self.read_sandbox_memory(memory, key_ptr, key_type.len::()?)?; - let outcome = match key_type { - KeyType::Fix => self.ext.set_storage( - &FixSizedKey::try_from(key).map_err(|_| Error::::DecodingFailed)?, - None, - false, - )?, - KeyType::Variable(_) => self.ext.set_storage_transparent( - &VarSizedKey::::try_from(key).map_err(|_| Error::::DecodingFailed)?, - None, - false, - )?, - }; + let key = self.decode_key(memory, key_type, key_ptr)?; + let outcome = self.ext.set_storage(&key, None, false)?; self.adjust_gas(charged, RuntimeCosts::ClearStorage(outcome.old_len())); Ok(outcome.old_len_with_sentinel()) @@ -823,15 +812,8 @@ impl<'a, E: Ext + 'a> Runtime<'a, E> { out_len_ptr: u32, ) -> Result { let charged = self.charge_gas(RuntimeCosts::GetStorage(self.ext.max_value_size()))?; - let key = self.read_sandbox_memory(memory, key_ptr, key_type.len::()?)?; - let outcome = match key_type { - KeyType::Fix => self.ext.get_storage( - &FixSizedKey::try_from(key).map_err(|_| Error::::DecodingFailed)?, - ), - KeyType::Variable(_) => self.ext.get_storage_transparent( - &VarSizedKey::::try_from(key).map_err(|_| Error::::DecodingFailed)?, - ), - }; + let key = self.decode_key(memory, key_type, key_ptr)?; + let outcome = self.ext.get_storage(&key); if let Some(value) = outcome { self.adjust_gas(charged, RuntimeCosts::GetStorage(value.len() as u32)); @@ -857,15 +839,8 @@ impl<'a, E: Ext + 'a> Runtime<'a, E> { key_ptr: u32, ) -> Result { let charged = self.charge_gas(RuntimeCosts::ContainsStorage(self.ext.max_value_size()))?; - let key = self.read_sandbox_memory(memory, key_ptr, key_type.len::()?)?; - let outcome = match key_type { - KeyType::Fix => self.ext.get_storage_size( - &FixSizedKey::try_from(key).map_err(|_| Error::::DecodingFailed)?, - ), - KeyType::Variable(_) => self.ext.get_storage_size_transparent( - &VarSizedKey::::try_from(key).map_err(|_| Error::::DecodingFailed)?, - ), - }; + let key = self.decode_key(memory, key_type, key_ptr)?; + let outcome = self.ext.get_storage_size(&key); self.adjust_gas(charged, RuntimeCosts::ClearStorage(outcome.unwrap_or(0))); Ok(outcome.unwrap_or(SENTINEL)) @@ -1092,7 +1067,7 @@ pub mod env { value_ptr: u32, value_len: u32, ) -> Result { - ctx.set_storage(memory, KeyType::Variable(key_len), key_ptr, value_ptr, value_len) + ctx.set_storage(memory, KeyType::Var(key_len), key_ptr, value_ptr, value_len) } /// Clear the value at the given key in the contract storage. @@ -1118,7 +1093,7 @@ pub mod env { #[version(1)] #[prefixed_alias] fn clear_storage(ctx: _, memory: _, key_ptr: u32, key_len: u32) -> Result { - ctx.clear_storage(memory, KeyType::Variable(key_len), key_ptr) + ctx.clear_storage(memory, KeyType::Var(key_len), key_ptr) } /// Retrieve the value under the given key from storage. @@ -1175,7 +1150,7 @@ pub mod env { out_ptr: u32, out_len_ptr: u32, ) -> Result { - ctx.get_storage(memory, KeyType::Variable(key_len), key_ptr, out_ptr, out_len_ptr) + ctx.get_storage(memory, KeyType::Var(key_len), key_ptr, out_ptr, out_len_ptr) } /// Checks whether there is a value stored under the given key. @@ -1212,7 +1187,7 @@ pub mod env { #[version(1)] #[prefixed_alias] fn contains_storage(ctx: _, memory: _, key_ptr: u32, key_len: u32) -> Result { - ctx.contains_storage(memory, KeyType::Variable(key_len), key_ptr) + ctx.contains_storage(memory, KeyType::Var(key_len), key_ptr) } /// Retrieve and remove the value under the given key from storage. @@ -1239,8 +1214,8 @@ pub mod env { ) -> Result { let charged = ctx.charge_gas(RuntimeCosts::TakeStorage(ctx.ext.max_value_size()))?; let key = ctx.read_sandbox_memory(memory, key_ptr, key_len)?; - if let crate::storage::WriteOutcome::Taken(value) = ctx.ext.set_storage_transparent( - &VarSizedKey::::try_from(key).map_err(|_| Error::::DecodingFailed)?, + if let crate::storage::WriteOutcome::Taken(value) = ctx.ext.set_storage( + &Key::::try_from_var(key).map_err(|_| Error::::DecodingFailed)?, None, true, )? { From 29466db4b711730f5fa95f3fe1f289871b2c7c5c Mon Sep 17 00:00:00 2001 From: PG Herveou Date: Sat, 18 Mar 2023 08:44:46 +0100 Subject: [PATCH 256/558] Add linguist-generated to gitattributes (#13628) --- .gitattributes | 1 + frame/balances/src/weights.rs | 70 +++++++++++++++++------------------ 2 files changed, 35 insertions(+), 36 deletions(-) diff --git a/.gitattributes b/.gitattributes index 1762f1a04..a77c52fcc 100644 --- a/.gitattributes +++ b/.gitattributes @@ -1,3 +1,4 @@ Cargo.lock linguist-generated=true /.gitlab-ci.yml filter=ci-prettier /scripts/ci/gitlab/pipeline/*.yml filter=ci-prettier +frame/**/src/weights.rs linguist-generated=true diff --git a/frame/balances/src/weights.rs b/frame/balances/src/weights.rs index 500eb3371..e7fd539c0 100644 --- a/frame/balances/src/weights.rs +++ b/frame/balances/src/weights.rs @@ -18,28 +18,26 @@ //! Autogenerated weights for pallet_balances //! //! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev -//! DATE: 2023-03-15, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! DATE: 2023-03-17, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` //! WORST CASE MAP SIZE: `1000000` //! HOSTNAME: `bm3`, CPU: `Intel(R) Core(TM) i7-7700K CPU @ 4.20GHz` //! EXECUTION: Some(Wasm), WASM-EXECUTION: Compiled, CHAIN: Some("dev"), DB CACHE: 1024 // Executed Command: -// ./target/production/substrate +// target/production/substrate // benchmark // pallet -// --chain=dev // --steps=50 // --repeat=20 -// --pallet=pallet_balances -// --no-storage-info -// --no-median-slopes -// --no-min-squares // --extrinsic=* // --execution=wasm // --wasm-execution=compiled // --heap-pages=4096 -// --output=./frame/balances/src/weights.rs +// --json-file=/var/lib/gitlab-runner/builds/zyw4fam_/0/parity/mirrors/substrate/.git/.artifacts/bench.json +// --pallet=pallet_balances +// --chain=dev // --header=./HEADER-APACHE2 +// --output=./frame/balances/src/weights.rs // --template=./.maintain/frame-weight-template.hbs #![cfg_attr(rustfmt, rustfmt_skip)] @@ -69,8 +67,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `3593` - // Minimum execution time: 37_266_000 picoseconds. - Weight::from_parts(37_757_000, 3593) + // Minimum execution time: 36_551_000 picoseconds. + Weight::from_parts(37_150_000, 3593) .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -80,8 +78,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `3593` - // Minimum execution time: 28_782_000 picoseconds. - Weight::from_parts(29_185_000, 3593) + // Minimum execution time: 28_246_000 picoseconds. + Weight::from_parts(28_647_000, 3593) .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -91,8 +89,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `174` // Estimated: `3593` - // Minimum execution time: 17_406_000 picoseconds. - Weight::from_parts(17_781_000, 3593) + // Minimum execution time: 17_407_000 picoseconds. + Weight::from_parts(17_827_000, 3593) .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -102,8 +100,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `174` // Estimated: `3593` - // Minimum execution time: 20_942_000 picoseconds. - Weight::from_parts(21_270_000, 3593) + // Minimum execution time: 20_941_000 picoseconds. + Weight::from_parts(21_383_000, 3593) .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -113,8 +111,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `103` // Estimated: `6196` - // Minimum execution time: 41_706_000 picoseconds. - Weight::from_parts(42_176_000, 6196) + // Minimum execution time: 38_741_000 picoseconds. + Weight::from_parts(39_201_000, 6196) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().writes(2_u64)) } @@ -124,8 +122,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `3593` - // Minimum execution time: 35_622_000 picoseconds. - Weight::from_parts(35_943_000, 3593) + // Minimum execution time: 33_791_000 picoseconds. + Weight::from_parts(34_500_000, 3593) .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -135,8 +133,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `174` // Estimated: `3593` - // Minimum execution time: 16_586_000 picoseconds. - Weight::from_parts(17_352_000, 3593) + // Minimum execution time: 16_538_000 picoseconds. + Weight::from_parts(16_859_000, 3593) .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -150,8 +148,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `0` // Estimated: `3593` - // Minimum execution time: 37_266_000 picoseconds. - Weight::from_parts(37_757_000, 3593) + // Minimum execution time: 36_551_000 picoseconds. + Weight::from_parts(37_150_000, 3593) .saturating_add(RocksDbWeight::get().reads(1_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } @@ -161,8 +159,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `0` // Estimated: `3593` - // Minimum execution time: 28_782_000 picoseconds. - Weight::from_parts(29_185_000, 3593) + // Minimum execution time: 28_246_000 picoseconds. + Weight::from_parts(28_647_000, 3593) .saturating_add(RocksDbWeight::get().reads(1_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } @@ -172,8 +170,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `174` // Estimated: `3593` - // Minimum execution time: 17_406_000 picoseconds. - Weight::from_parts(17_781_000, 3593) + // Minimum execution time: 17_407_000 picoseconds. + Weight::from_parts(17_827_000, 3593) .saturating_add(RocksDbWeight::get().reads(1_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } @@ -183,8 +181,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `174` // Estimated: `3593` - // Minimum execution time: 20_942_000 picoseconds. - Weight::from_parts(21_270_000, 3593) + // Minimum execution time: 20_941_000 picoseconds. + Weight::from_parts(21_383_000, 3593) .saturating_add(RocksDbWeight::get().reads(1_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } @@ -194,8 +192,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `103` // Estimated: `6196` - // Minimum execution time: 41_706_000 picoseconds. - Weight::from_parts(42_176_000, 6196) + // Minimum execution time: 38_741_000 picoseconds. + Weight::from_parts(39_201_000, 6196) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().writes(2_u64)) } @@ -205,8 +203,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `0` // Estimated: `3593` - // Minimum execution time: 35_622_000 picoseconds. - Weight::from_parts(35_943_000, 3593) + // Minimum execution time: 33_791_000 picoseconds. + Weight::from_parts(34_500_000, 3593) .saturating_add(RocksDbWeight::get().reads(1_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } @@ -216,8 +214,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `174` // Estimated: `3593` - // Minimum execution time: 16_586_000 picoseconds. - Weight::from_parts(17_352_000, 3593) + // Minimum execution time: 16_538_000 picoseconds. + Weight::from_parts(16_859_000, 3593) .saturating_add(RocksDbWeight::get().reads(1_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } From f8a1dd4c836d7d29382ba66fc2fce40fc1364546 Mon Sep 17 00:00:00 2001 From: Gavin Wood Date: Sat, 18 Mar 2023 14:47:55 +0000 Subject: [PATCH 257/558] Deprecate `Currency`; introduce holds and freezing into `fungible` traits (#12951) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * First reworking of fungibles API * New API and docs * More fungible::* API improvements * New ref-counting logic for old API * Missing files * Fixes * Use the new transfer logic * Use fungibles for the dispatchables * Use shelve/restore names * Locking works with total balance. * repotting and removal * Separate Holds from Reserves * Introduce freezes * Missing files * Tests for freezing * Fix hold+freeze combo * More tests * Fee-free dispatchable for upgrading accounts * Benchmarks and a few fixes * Another test * Docs and refactor to avoid blanket impls * Repot * Fit out ItemOf fully * Add events to Balanced traits * Introduced events into Hold traits * Fix Assets pallet tests * Assets benchmarks pass * Missing files and fixes * Fixes * Fixes * Benchmarks fixes * Fix balance benchmarks * Formatting * Expose fungible sub modules * Move NIS to fungible API * Fix broken impl and add test * Fix tests * API for `transfer_and_hold` * Use composite APIs * Formatting * Upgraded event * Fixes * Fixes * Fixes * Fixes * Repot tests and some fixed * Fix some bits * Fix dust tests * Rename `set_balance` - `Balances::set_balance` becomes `Balances::force_set_balance` - `Unbalanced::set_balance` becomes `Unbalances::write_balance` * becomes * Move dust handling to fungibles API * Formatting * Fixes and more refactoring * Fixes * Fixes * Fixes * Fixes * Fixes * Fixes * Fixes * Fixes * Fixes * Use reducible_balance for better correctness on fees * Reducing hold to zero should remove entry. * Add test * Docs * Update frame/support/src/traits/tokens/fungibles/hold.rs Co-authored-by: Muharem Ismailov * Update frame/support/src/traits/tokens/fungibles/regular.rs Co-authored-by: Muharem Ismailov * Update frame/support/src/traits/tokens/fungible/hold.rs Co-authored-by: Muharem Ismailov * Update frame/support/src/traits/tokens/fungible/regular.rs Co-authored-by: Muharem Ismailov * Docs * Docs * Docs * Fix NIS benchmarks * Doc comment * Remove post_mutation * Fix some tests * Fix some grumbles * Enumify bool args to fungible(s) functions * Fix up assets and balances * Formatting * Fix contracts * Fix tests & benchmarks build * Typify minted boolean arg * Typify on_hold boolean arg; renames * Fix numerous tests * Fix dependency issue * Privatize dangerous API mutate_account * Fix contracts (@alext - please check this commit) * Remove println * Fix tests for contracts * Fix broken rename * Fix broken rename * Fix broken rename * Docs * Update frame/support/src/traits/tokens/fungible/hold.rs Co-authored-by: Anthony Alaribe * remove from_ref_time * Update frame/executive/src/lib.rs Co-authored-by: Anthony Alaribe * Update frame/executive/src/lib.rs Co-authored-by: Anthony Alaribe * Reenable test * Update frame/support/src/traits/tokens/fungibles/hold.rs Co-authored-by: Anthony Alaribe * Update frame/support/src/traits/tokens/fungible/hold.rs Co-authored-by: Anthony Alaribe * Update frame/support/src/traits/tokens/fungible/hold.rs Co-authored-by: Anthony Alaribe * Update frame/support/src/traits/tokens/fungible/hold.rs Co-authored-by: Anthony Alaribe * Update frame/support/src/traits/tokens/currency.rs Co-authored-by: Anthony Alaribe * Update frame/lottery/src/tests.rs Co-authored-by: Anthony Alaribe * Update frame/support/src/traits/tokens/fungible/mod.rs Co-authored-by: Anthony Alaribe * Update frame/support/src/traits/tokens/fungible/regular.rs Co-authored-by: Anthony Alaribe * Update frame/support/src/traits/tokens/fungibles/freeze.rs Co-authored-by: Anthony Alaribe * Update frame/support/src/traits/tokens/fungible/regular.rs Co-authored-by: Anthony Alaribe * Update frame/support/src/traits/tokens/fungibles/hold.rs Co-authored-by: Anthony Alaribe * Update frame/support/src/traits/tokens/fungibles/hold.rs Co-authored-by: Anthony Alaribe * Update frame/support/src/traits/tokens/fungibles/hold.rs Co-authored-by: Anthony Alaribe * Rename UnwantedRemoval to UnwantedAccountRemoval * Docs * Formatting * Update frame/balances/src/lib.rs Co-authored-by: Oliver Tale-Yazdi * Update primitives/runtime/src/lib.rs Co-authored-by: Keith Yeung * handle_raw_dust oes nothing * Formatting * Fixes * Grumble * Fixes * Add test * Add test * Tests for reducible_balance * Fixes * Fix Salary * Fixes * Disable broken test * Disable nicely * Fixes * Fixes * Fixes * Rename some events * Fix nomination pools breakage * Add compatibility stub for transfer tx * Reinstate a safely compatible version of Balances set_balance * Fixes * Grumble * Update frame/nis/src/lib.rs Co-authored-by: Kian Paimani <5588131+kianenigma@users.noreply.github.com> * ".git/.scripts/commands/bench/bench.sh" pallet dev pallet_balances * disable flakey tests * Update frame/balances/src/lib.rs Co-authored-by: Kian Paimani <5588131+kianenigma@users.noreply.github.com> * Grumbles * Grumble --------- Co-authored-by: Muharem Ismailov Co-authored-by: Alexander Theißen Co-authored-by: Anthony Alaribe Co-authored-by: Oliver Tale-Yazdi Co-authored-by: Keith Yeung Co-authored-by: Kian Paimani <5588131+kianenigma@users.noreply.github.com> Co-authored-by: command-bot <> --- bin/node-template/runtime/src/lib.rs | 4 + bin/node/cli/benches/block_production.rs | 2 +- bin/node/cli/benches/transaction_pool.rs | 8 +- bin/node/cli/src/service.rs | 2 +- bin/node/cli/tests/temp_base_path_works.rs | 4 +- bin/node/executor/benches/bench.rs | 2 +- bin/node/executor/tests/basic.rs | 57 +- bin/node/executor/tests/common.rs | 5 +- bin/node/executor/tests/fees.rs | 6 +- bin/node/executor/tests/submit_transaction.rs | 12 +- bin/node/runtime/src/impls.rs | 4 +- bin/node/runtime/src/lib.rs | 21 +- bin/node/testing/src/bench.rs | 2 +- frame/alliance/src/mock.rs | 4 + frame/assets/src/benchmarking.rs | 8 +- frame/assets/src/functions.rs | 15 +- frame/assets/src/impl_fungibles.rs | 126 +- frame/assets/src/lib.rs | 6 +- frame/assets/src/mock.rs | 6 +- frame/assets/src/tests.rs | 30 +- frame/atomic-swap/src/tests.rs | 4 + frame/babe/src/mock.rs | 4 + frame/balances/README.md | 2 +- frame/balances/src/benchmarking.rs | 74 +- frame/balances/src/impl_currency.rs | 898 +++++++ frame/balances/src/impl_fungible.rs | 358 +++ frame/balances/src/lib.rs | 2387 +++++------------ frame/balances/src/tests.rs | 1460 ---------- frame/balances/src/tests/currency_tests.rs | 1231 +++++++++ .../balances/src/tests/dispatchable_tests.rs | 224 ++ frame/balances/src/tests/fungible_tests.rs | 399 +++ frame/balances/src/tests/mod.rs | 296 ++ frame/balances/src/tests/reentrancy_tests.rs | 195 ++ frame/balances/src/tests_composite.rs | 149 - frame/balances/src/tests_local.rs | 191 -- frame/balances/src/tests_reentrancy.rs | 264 -- frame/balances/src/types.rs | 157 ++ frame/balances/src/weights.rs | 49 +- frame/beefy/src/mock.rs | 4 + frame/bounties/src/benchmarking.rs | 7 +- frame/bounties/src/tests.rs | 8 +- frame/child-bounties/src/benchmarking.rs | 12 +- frame/child-bounties/src/tests.rs | 8 +- frame/contracts/src/benchmarking/mod.rs | 12 +- frame/contracts/src/exec.rs | 15 +- frame/contracts/src/lib.rs | 2 +- frame/contracts/src/storage/meter.rs | 7 +- frame/contracts/src/tests.rs | 73 +- frame/conviction-voting/src/benchmarking.rs | 12 +- frame/conviction-voting/src/lib.rs | 7 +- frame/conviction-voting/src/tests.rs | 6 +- frame/democracy/src/tests.rs | 8 +- .../src/benchmarking.rs | 10 +- .../election-provider-multi-phase/src/mock.rs | 4 + frame/elections-phragmen/src/lib.rs | 7 +- frame/examples/basic/src/tests.rs | 4 + frame/executive/src/lib.rs | 100 +- frame/fast-unstake/src/mock.rs | 4 + frame/grandpa/src/mock.rs | 4 + frame/identity/src/tests.rs | 4 + frame/indices/src/mock.rs | 4 + frame/lottery/src/mock.rs | 4 + frame/lottery/src/tests.rs | 77 +- frame/multisig/src/tests.rs | 72 +- frame/nfts/src/mock.rs | 4 + frame/nicks/src/lib.rs | 4 + frame/nis/README.md | 5 +- frame/nis/src/benchmarking.rs | 102 +- frame/nis/src/lib.rs | 240 +- frame/nis/src/mock.rs | 50 +- frame/nis/src/tests.rs | 110 +- .../nomination-pools/benchmarking/src/mock.rs | 4 + frame/nomination-pools/src/mock.rs | 4 + frame/nomination-pools/src/tests.rs | 177 +- .../nomination-pools/test-staking/src/mock.rs | 4 + frame/offences/benchmarking/src/mock.rs | 4 + frame/preimage/src/mock.rs | 4 + frame/proxy/src/tests.rs | 28 +- frame/recovery/src/mock.rs | 4 + frame/recovery/src/tests.rs | 10 +- frame/referenda/src/migration.rs | 2 +- frame/referenda/src/mock.rs | 17 +- frame/root-offences/src/mock.rs | 4 + frame/scored-pool/src/mock.rs | 4 + frame/session/benchmarking/src/mock.rs | 4 + frame/society/src/mock.rs | 4 + frame/staking/src/mock.rs | 4 + frame/staking/src/tests.rs | 22 +- frame/state-trie-migration/src/lib.rs | 4 + frame/support/src/traits/stored_map.rs | 35 +- frame/support/src/traits/tokens.rs | 3 +- frame/support/src/traits/tokens/fungible.rs | 367 --- .../src/traits/tokens/fungible/balanced.rs | 354 --- .../src/traits/tokens/fungible/freeze.rs | 68 + .../src/traits/tokens/fungible/hold.rs | 393 +++ .../src/traits/tokens/fungible/imbalance.rs | 15 +- .../src/traits/tokens/fungible/item_of.rs | 451 ++++ .../support/src/traits/tokens/fungible/mod.rs | 56 + .../src/traits/tokens/fungible/regular.rs | 506 ++++ frame/support/src/traits/tokens/fungibles.rs | 332 --- .../src/traits/tokens/fungibles/balanced.rs | 394 --- .../src/traits/tokens/fungibles/enumerable.rs | 4 +- .../src/traits/tokens/fungibles/freeze.rs | 78 + .../src/traits/tokens/fungibles/hold.rs | 457 ++++ .../src/traits/tokens/fungibles/imbalance.rs | 13 +- .../src/traits/tokens/fungibles/lifetime.rs | 84 + .../src/traits/tokens/fungibles/mod.rs | 40 + .../src/traits/tokens/fungibles/regular.rs | 571 ++++ frame/support/src/traits/tokens/misc.rs | 62 +- frame/support/src/traits/tokens/pay.rs | 8 +- frame/system/src/lib.rs | 23 +- frame/system/src/tests.rs | 2 + frame/tips/src/tests.rs | 4 + .../asset-tx-payment/src/lib.rs | 8 +- .../asset-tx-payment/src/mock.rs | 6 +- .../asset-tx-payment/src/payment.rs | 23 +- .../asset-tx-payment/src/tests.rs | 2 +- frame/transaction-payment/src/mock.rs | 6 +- frame/transaction-payment/src/tests.rs | 10 +- frame/transaction-storage/src/mock.rs | 4 + frame/treasury/src/tests.rs | 4 + frame/uniques/src/mock.rs | 4 + frame/utility/Cargo.toml | 1 + frame/utility/src/tests.rs | 17 +- frame/vesting/src/benchmarking.rs | 12 +- frame/vesting/src/mock.rs | 4 + frame/vesting/src/tests.rs | 37 +- frame/whitelist/src/mock.rs | 4 + primitives/runtime/src/lib.rs | 22 +- 129 files changed, 8325 insertions(+), 6119 deletions(-) create mode 100644 frame/balances/src/impl_currency.rs create mode 100644 frame/balances/src/impl_fungible.rs delete mode 100644 frame/balances/src/tests.rs create mode 100644 frame/balances/src/tests/currency_tests.rs create mode 100644 frame/balances/src/tests/dispatchable_tests.rs create mode 100644 frame/balances/src/tests/fungible_tests.rs create mode 100644 frame/balances/src/tests/mod.rs create mode 100644 frame/balances/src/tests/reentrancy_tests.rs delete mode 100644 frame/balances/src/tests_composite.rs delete mode 100644 frame/balances/src/tests_local.rs delete mode 100644 frame/balances/src/tests_reentrancy.rs create mode 100644 frame/balances/src/types.rs delete mode 100644 frame/support/src/traits/tokens/fungible.rs delete mode 100644 frame/support/src/traits/tokens/fungible/balanced.rs create mode 100644 frame/support/src/traits/tokens/fungible/freeze.rs create mode 100644 frame/support/src/traits/tokens/fungible/hold.rs create mode 100644 frame/support/src/traits/tokens/fungible/item_of.rs create mode 100644 frame/support/src/traits/tokens/fungible/mod.rs create mode 100644 frame/support/src/traits/tokens/fungible/regular.rs delete mode 100644 frame/support/src/traits/tokens/fungibles.rs delete mode 100644 frame/support/src/traits/tokens/fungibles/balanced.rs create mode 100644 frame/support/src/traits/tokens/fungibles/freeze.rs create mode 100644 frame/support/src/traits/tokens/fungibles/hold.rs create mode 100644 frame/support/src/traits/tokens/fungibles/lifetime.rs create mode 100644 frame/support/src/traits/tokens/fungibles/mod.rs create mode 100644 frame/support/src/traits/tokens/fungibles/regular.rs diff --git a/bin/node-template/runtime/src/lib.rs b/bin/node-template/runtime/src/lib.rs index 41871c06b..50bcb67cb 100644 --- a/bin/node-template/runtime/src/lib.rs +++ b/bin/node-template/runtime/src/lib.rs @@ -243,6 +243,10 @@ impl pallet_balances::Config for Runtime { type ExistentialDeposit = ConstU128; type AccountStore = System; type WeightInfo = pallet_balances::weights::SubstrateWeight; + type FreezeIdentifier = (); + type MaxFreezes = (); + type HoldIdentifier = (); + type MaxHolds = (); } parameter_types! { diff --git a/bin/node/cli/benches/block_production.rs b/bin/node/cli/benches/block_production.rs index 501d69fc2..f1c65955e 100644 --- a/bin/node/cli/benches/block_production.rs +++ b/bin/node/cli/benches/block_production.rs @@ -162,7 +162,7 @@ fn prepare_benchmark(client: &FullClient) -> (usize, Vec) { let extrinsic: OpaqueExtrinsic = create_extrinsic( client, src.clone(), - BalancesCall::transfer { dest: dst.clone(), value: 1 * DOLLARS }, + BalancesCall::transfer_allow_death { dest: dst.clone(), value: 1 * DOLLARS }, Some(nonce), ) .into(); diff --git a/bin/node/cli/benches/transaction_pool.rs b/bin/node/cli/benches/transaction_pool.rs index 165c67f60..72c1c8160 100644 --- a/bin/node/cli/benches/transaction_pool.rs +++ b/bin/node/cli/benches/transaction_pool.rs @@ -140,10 +140,9 @@ fn create_account_extrinsics( Sr25519Keyring::Alice.pair(), SudoCall::sudo { call: Box::new( - BalancesCall::set_balance { + BalancesCall::force_set_balance { who: AccountId::from(a.public()).into(), new_free: 0, - new_reserved: 0, } .into(), ), @@ -156,10 +155,9 @@ fn create_account_extrinsics( Sr25519Keyring::Alice.pair(), SudoCall::sudo { call: Box::new( - BalancesCall::set_balance { + BalancesCall::force_set_balance { who: AccountId::from(a.public()).into(), new_free: 1_000_000 * DOLLARS, - new_reserved: 0, } .into(), ), @@ -184,7 +182,7 @@ fn create_benchmark_extrinsics( create_extrinsic( client, account.clone(), - BalancesCall::transfer { + BalancesCall::transfer_allow_death { dest: Sr25519Keyring::Bob.to_account_id().into(), value: 1 * DOLLARS, }, diff --git a/bin/node/cli/src/service.rs b/bin/node/cli/src/service.rs index a2d66cd63..d32223339 100644 --- a/bin/node/cli/src/service.rs +++ b/bin/node/cli/src/service.rs @@ -772,7 +772,7 @@ mod tests { }; let signer = charlie.clone(); - let function = RuntimeCall::Balances(BalancesCall::transfer { + let function = RuntimeCall::Balances(BalancesCall::transfer_allow_death { dest: to.into(), value: amount, }); diff --git a/bin/node/cli/tests/temp_base_path_works.rs b/bin/node/cli/tests/temp_base_path_works.rs index 89e901c00..bfc9f8822 100644 --- a/bin/node/cli/tests/temp_base_path_works.rs +++ b/bin/node/cli/tests/temp_base_path_works.rs @@ -26,7 +26,9 @@ use std::{ pub mod common; -#[tokio::test] +#[allow(dead_code)] +// Apparently `#[ignore]` doesn't actually work to disable this one. +//#[tokio::test] async fn temp_base_path_works() { common::run_with_timeout(Duration::from_secs(60 * 10), async move { let mut cmd = Command::new(cargo_bin("substrate")); diff --git a/bin/node/executor/benches/bench.rs b/bin/node/executor/benches/bench.rs index 19e7b158a..a8c31068e 100644 --- a/bin/node/executor/benches/bench.rs +++ b/bin/node/executor/benches/bench.rs @@ -167,7 +167,7 @@ fn test_blocks( }]; block1_extrinsics.extend((0..20).map(|i| CheckedExtrinsic { signed: Some((alice(), signed_extra(i, 0))), - function: RuntimeCall::Balances(pallet_balances::Call::transfer { + function: RuntimeCall::Balances(pallet_balances::Call::transfer_allow_death { dest: bob().into(), value: 1 * DOLLARS, }), diff --git a/bin/node/executor/tests/basic.rs b/bin/node/executor/tests/basic.rs index 9200fe735..d301aa06f 100644 --- a/bin/node/executor/tests/basic.rs +++ b/bin/node/executor/tests/basic.rs @@ -89,7 +89,7 @@ fn changes_trie_block() -> (Vec, Hash) { }, CheckedExtrinsic { signed: Some((alice(), signed_extra(0, 0))), - function: RuntimeCall::Balances(pallet_balances::Call::transfer { + function: RuntimeCall::Balances(pallet_balances::Call::transfer_allow_death { dest: bob().into(), value: 69 * DOLLARS, }), @@ -116,7 +116,7 @@ fn blocks() -> ((Vec, Hash), (Vec, Hash)) { }, CheckedExtrinsic { signed: Some((alice(), signed_extra(0, 0))), - function: RuntimeCall::Balances(pallet_balances::Call::transfer { + function: RuntimeCall::Balances(pallet_balances::Call::transfer_allow_death { dest: bob().into(), value: 69 * DOLLARS, }), @@ -136,14 +136,14 @@ fn blocks() -> ((Vec, Hash), (Vec, Hash)) { }, CheckedExtrinsic { signed: Some((bob(), signed_extra(0, 0))), - function: RuntimeCall::Balances(pallet_balances::Call::transfer { + function: RuntimeCall::Balances(pallet_balances::Call::transfer_allow_death { dest: alice().into(), value: 5 * DOLLARS, }), }, CheckedExtrinsic { signed: Some((alice(), signed_extra(1, 0))), - function: RuntimeCall::Balances(pallet_balances::Call::transfer { + function: RuntimeCall::Balances(pallet_balances::Call::transfer_allow_death { dest: bob().into(), value: 15 * DOLLARS, }), @@ -183,7 +183,12 @@ fn panic_execution_with_foreign_code_gives_error() { let mut t = new_test_ext(bloaty_code_unwrap()); t.insert( >::hashed_key_for(alice()), - (69u128, 0u32, 0u128, 0u128, 0u128).encode(), + AccountInfo::<::Index, _> { + providers: 1, + data: (69u128, 0u128, 0u128, 1u128 << 127), + ..Default::default() + } + .encode(), ); t.insert(>::hashed_key().to_vec(), 69_u128.encode()); t.insert(>::hashed_key_for(0), vec![0u8; 32]); @@ -204,9 +209,14 @@ fn bad_extrinsic_with_native_equivalent_code_gives_error() { let mut t = new_test_ext(compact_code_unwrap()); t.insert( >::hashed_key_for(alice()), - (0u32, 0u32, 0u32, 69u128, 0u128, 0u128, 0u128).encode(), + AccountInfo::<::Index, _> { + providers: 1, + data: (69u128, 0u128, 0u128, 1u128 << 127), + ..Default::default() + } + .encode(), ); - t.insert(>::hashed_key().to_vec(), 69_u128.encode()); + t.insert(>::hashed_key().to_vec(), 69u128.encode()); t.insert(>::hashed_key_for(0), vec![0u8; 32]); let r = @@ -226,17 +236,18 @@ fn successful_execution_with_native_equivalent_code_gives_ok() { t.insert( >::hashed_key_for(alice()), AccountInfo::<::Index, _> { - data: (111 * DOLLARS, 0u128, 0u128, 0u128), + providers: 1, + data: (111 * DOLLARS, 0u128, 0u128, 1u128 << 127), ..Default::default() } .encode(), ); t.insert( >::hashed_key_for(bob()), - AccountInfo::<::Index, _> { - data: (0 * DOLLARS, 0u128, 0u128, 0u128), - ..Default::default() - } + AccountInfo::< + ::Index, + ::AccountData, + >::default() .encode(), ); t.insert( @@ -267,17 +278,18 @@ fn successful_execution_with_foreign_code_gives_ok() { t.insert( >::hashed_key_for(alice()), AccountInfo::<::Index, _> { - data: (111 * DOLLARS, 0u128, 0u128, 0u128), + providers: 1, + data: (111 * DOLLARS, 0u128, 0u128, 1u128 << 127), ..Default::default() } .encode(), ); t.insert( >::hashed_key_for(bob()), - AccountInfo::<::Index, _> { - data: (0 * DOLLARS, 0u128, 0u128, 0u128), - ..Default::default() - } + AccountInfo::< + ::Index, + ::AccountData, + >::default() .encode(), ); t.insert( @@ -784,17 +796,18 @@ fn successful_execution_gives_ok() { t.insert( >::hashed_key_for(alice()), AccountInfo::<::Index, _> { - data: (111 * DOLLARS, 0u128, 0u128, 0u128), + providers: 1, + data: (111 * DOLLARS, 0u128, 0u128, 1u128 << 127), ..Default::default() } .encode(), ); t.insert( >::hashed_key_for(bob()), - AccountInfo::<::Index, _> { - data: (0 * DOLLARS, 0u128, 0u128, 0u128), - ..Default::default() - } + AccountInfo::< + ::Index, + ::AccountData, + >::default() .encode(), ); t.insert( diff --git a/bin/node/executor/tests/common.rs b/bin/node/executor/tests/common.rs index 036528f8d..2f92423ff 100644 --- a/bin/node/executor/tests/common.rs +++ b/bin/node/executor/tests/common.rs @@ -87,7 +87,10 @@ pub fn sign(xt: CheckedExtrinsic) -> UncheckedExtrinsic { } pub fn default_transfer_call() -> pallet_balances::Call { - pallet_balances::Call::::transfer { dest: bob().into(), value: 69 * DOLLARS } + pallet_balances::Call::::transfer_allow_death { + dest: bob().into(), + value: 69 * DOLLARS, + } } pub fn from_block_number(n: u32) -> Header { diff --git a/bin/node/executor/tests/fees.rs b/bin/node/executor/tests/fees.rs index cbf0fdce9..970d790a8 100644 --- a/bin/node/executor/tests/fees.rs +++ b/bin/node/executor/tests/fees.rs @@ -120,9 +120,9 @@ fn new_account_info(free_dollars: u128) -> Vec { frame_system::AccountInfo { nonce: 0u32, consumers: 0, - providers: 0, + providers: 1, sufficients: 0, - data: (free_dollars * DOLLARS, 0 * DOLLARS, 0 * DOLLARS, 0 * DOLLARS), + data: (free_dollars * DOLLARS, 0 * DOLLARS, 0 * DOLLARS, 1u128 << 127), } .encode() } @@ -214,7 +214,7 @@ fn block_weight_capacity_report() { let mut xts = (0..num_transfers) .map(|i| CheckedExtrinsic { signed: Some((charlie(), signed_extra(nonce + i as Index, 0))), - function: RuntimeCall::Balances(pallet_balances::Call::transfer { + function: RuntimeCall::Balances(pallet_balances::Call::transfer_allow_death { dest: bob().into(), value: 0, }), diff --git a/bin/node/executor/tests/submit_transaction.rs b/bin/node/executor/tests/submit_transaction.rs index 32eacd78b..a3d6681fb 100644 --- a/bin/node/executor/tests/submit_transaction.rs +++ b/bin/node/executor/tests/submit_transaction.rs @@ -86,7 +86,7 @@ fn should_submit_signed_transaction() { t.execute_with(|| { let results = Signer::::all_accounts().send_signed_transaction(|_| { - pallet_balances::Call::transfer { + pallet_balances::Call::transfer_allow_death { dest: Alice.to_account_id().into(), value: Default::default(), } @@ -123,7 +123,7 @@ fn should_submit_signed_twice_from_the_same_account() { t.execute_with(|| { let result = Signer::::any_account().send_signed_transaction(|_| { - pallet_balances::Call::transfer { + pallet_balances::Call::transfer_allow_death { dest: Alice.to_account_id().into(), value: Default::default(), } @@ -135,7 +135,7 @@ fn should_submit_signed_twice_from_the_same_account() { // submit another one from the same account. The nonce should be incremented. let result = Signer::::any_account().send_signed_transaction(|_| { - pallet_balances::Call::transfer { + pallet_balances::Call::transfer_allow_death { dest: Alice.to_account_id().into(), value: Default::default(), } @@ -174,7 +174,7 @@ fn should_submit_signed_twice_from_all_accounts() { t.execute_with(|| { let results = Signer::::all_accounts() .send_signed_transaction(|_| { - pallet_balances::Call::transfer { dest: Alice.to_account_id().into(), value: Default::default() } + pallet_balances::Call::transfer_allow_death { dest: Alice.to_account_id().into(), value: Default::default() } }); let len = results.len(); @@ -185,7 +185,7 @@ fn should_submit_signed_twice_from_all_accounts() { // submit another one from the same account. The nonce should be incremented. let results = Signer::::all_accounts() .send_signed_transaction(|_| { - pallet_balances::Call::transfer { dest: Alice.to_account_id().into(), value: Default::default() } + pallet_balances::Call::transfer_allow_death { dest: Alice.to_account_id().into(), value: Default::default() } }); let len = results.len(); @@ -238,7 +238,7 @@ fn submitted_transaction_should_be_valid() { t.execute_with(|| { let results = Signer::::all_accounts().send_signed_transaction(|_| { - pallet_balances::Call::transfer { + pallet_balances::Call::transfer_allow_death { dest: Alice.to_account_id().into(), value: Default::default(), } diff --git a/bin/node/runtime/src/impls.rs b/bin/node/runtime/src/impls.rs index 09f7b6a29..033549549 100644 --- a/bin/node/runtime/src/impls.rs +++ b/bin/node/runtime/src/impls.rs @@ -24,7 +24,7 @@ use crate::{ use frame_support::{ pallet_prelude::*, traits::{ - fungibles::{Balanced, CreditOf}, + fungibles::{Balanced, Credit}, Currency, OnUnbalanced, }, }; @@ -45,7 +45,7 @@ impl OnUnbalanced for Author { /// Will drop and burn the assets in case the transfer fails. pub struct CreditToBlockAuthor; impl HandleCredit for CreditToBlockAuthor { - fn handle_credit(credit: CreditOf) { + fn handle_credit(credit: Credit) { if let Some(author) = pallet_authorship::Pallet::::author() { // Drop the result which will trigger the `OnDrop` of the imbalance in case of error. let _ = Assets::resolve(&author, credit); diff --git a/bin/node/runtime/src/lib.rs b/bin/node/runtime/src/lib.rs index 9f4ad2ed2..3f0ea0712 100644 --- a/bin/node/runtime/src/lib.rs +++ b/bin/node/runtime/src/lib.rs @@ -59,6 +59,7 @@ use pallet_nis::WithMaximumOf; use pallet_session::historical as pallet_session_historical; pub use pallet_transaction_payment::{CurrencyAdapter, Multiplier, TargetedFeeAdjustment}; use pallet_transaction_payment::{FeeDetails, RuntimeDispatchInfo}; +use scale_info::TypeInfo; use sp_api::impl_runtime_apis; use sp_authority_discovery::AuthorityId as AuthorityDiscoveryId; use sp_consensus_grandpa::AuthorityId as GrandpaId; @@ -432,6 +433,15 @@ parameter_types! { pub const MaxReserves: u32 = 50; } +/// A reason for placing a hold on funds. +#[derive( + Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Encode, Decode, MaxEncodedLen, Debug, TypeInfo, +)] +pub enum HoldReason { + /// The NIS Pallet has reserved it for a non-fungible receipt. + Nis, +} + impl pallet_balances::Config for Runtime { type MaxLocks = MaxLocks; type MaxReserves = MaxReserves; @@ -442,6 +452,10 @@ impl pallet_balances::Config for Runtime { type ExistentialDeposit = ExistentialDeposit; type AccountStore = frame_system::Pallet; type WeightInfo = pallet_balances::weights::SubstrateWeight; + type FreezeIdentifier = (); + type MaxFreezes = (); + type HoldIdentifier = HoldReason; + type MaxHolds = ConstU32<1>; } parameter_types! { @@ -1481,7 +1495,6 @@ impl pallet_assets::Config for Runtime { } parameter_types! { - pub IgnoredIssuance: Balance = Treasury::pot(); pub const QueueCount: u32 = 300; pub const MaxQueueLen: u32 = 1000; pub const FifoQueueLen: u32 = 500; @@ -1493,7 +1506,7 @@ parameter_types! { pub const ThawThrottle: (Perquintill, BlockNumber) = (Perquintill::from_percent(25), 5); pub Target: Perquintill = Perquintill::zero(); pub const NisPalletId: PalletId = PalletId(*b"py/nis "); - pub const NisReserveId: [u8; 8] = *b"py/nis "; + pub const NisHoldReason: HoldReason = HoldReason::Nis; } impl pallet_nis::Config for Runtime { @@ -1505,7 +1518,7 @@ impl pallet_nis::Config for Runtime { type Counterpart = ItemOf, AccountId>; type CounterpartAmount = WithMaximumOf>; type Deficit = (); - type IgnoredIssuance = IgnoredIssuance; + type IgnoredIssuance = (); type Target = Target; type PalletId = NisPalletId; type QueueCount = QueueCount; @@ -1517,7 +1530,7 @@ impl pallet_nis::Config for Runtime { type IntakePeriod = IntakePeriod; type MaxIntakeWeight = MaxIntakeWeight; type ThawThrottle = ThawThrottle; - type ReserveId = NisReserveId; + type HoldReason = NisHoldReason; } parameter_types! { diff --git a/bin/node/testing/src/bench.rs b/bin/node/testing/src/bench.rs index 1a9af1302..d6bcf6e25 100644 --- a/bin/node/testing/src/bench.rs +++ b/bin/node/testing/src/bench.rs @@ -308,7 +308,7 @@ impl<'a> Iterator for BlockContentIterator<'a> { value: kitchensink_runtime::ExistentialDeposit::get() + 1, }), BlockType::RandomTransfersReaping => { - RuntimeCall::Balances(BalancesCall::transfer { + RuntimeCall::Balances(BalancesCall::transfer_allow_death { dest: sp_runtime::MultiAddress::Id(receiver), // Transfer so that ending balance would be 1 less than existential // deposit so that we kill the sender account. diff --git a/frame/alliance/src/mock.rs b/frame/alliance/src/mock.rs index b8fd998eb..848f6b2a0 100644 --- a/frame/alliance/src/mock.rs +++ b/frame/alliance/src/mock.rs @@ -85,6 +85,10 @@ impl pallet_balances::Config for Test { type MaxLocks = MaxLocks; type MaxReserves = (); type ReserveIdentifier = [u8; 8]; + type FreezeIdentifier = (); + type MaxFreezes = (); + type HoldIdentifier = (); + type MaxHolds = (); } const MOTION_DURATION_IN_BLOCKS: BlockNumber = 3; diff --git a/frame/assets/src/benchmarking.rs b/frame/assets/src/benchmarking.rs index 5b4f1489d..047ede2ce 100644 --- a/frame/assets/src/benchmarking.rs +++ b/frame/assets/src/benchmarking.rs @@ -105,14 +105,18 @@ fn add_sufficients, I: 'static>(minter: T::AccountId, n: u32) { fn add_approvals, I: 'static>(minter: T::AccountId, n: u32) { let asset_id = default_asset_id::(); - T::Currency::deposit_creating(&minter, T::ApprovalDeposit::get() * n.into()); + T::Currency::deposit_creating( + &minter, + T::ApprovalDeposit::get() * n.into() + T::Currency::minimum_balance(), + ); let minter_lookup = T::Lookup::unlookup(minter.clone()); let origin = SystemOrigin::Signed(minter); Assets::::mint(origin.clone().into(), asset_id, minter_lookup, (100 * (n + 1)).into()) .unwrap(); + let enough = T::Currency::minimum_balance(); for i in 0..n { let target = account("approval", i, SEED); - T::Currency::make_free_balance_be(&target, T::Currency::minimum_balance()); + T::Currency::make_free_balance_be(&target, enough); let target_lookup = T::Lookup::unlookup(target); Assets::::approve_transfer( origin.clone().into(), diff --git a/frame/assets/src/functions.rs b/frame/assets/src/functions.rs index 47657ff26..af9e269ac 100644 --- a/frame/assets/src/functions.rs +++ b/frame/assets/src/functions.rs @@ -76,7 +76,14 @@ impl, I: 'static> Pallet { d.sufficients += 1; ExistenceReason::Sufficient } else { - frame_system::Pallet::::inc_consumers(who).map_err(|_| Error::::NoProvider)?; + frame_system::Pallet::::inc_consumers(who) + .map_err(|_| Error::::UnavailableConsumer)?; + // We ensure that we can still increment consumers once more because we could otherwise + // allow accidental usage of all consumer references which could cause grief. + if !frame_system::Pallet::::can_inc_consumer(who) { + frame_system::Pallet::::dec_consumers(who); + return Err(Error::::UnavailableConsumer.into()) + } ExistenceReason::Consumer }; d.accounts = accounts; @@ -165,7 +172,7 @@ impl, I: 'static> Pallet { } let account = match Account::::get(id, who) { Some(a) => a, - None => return NoFunds, + None => return BalanceLow, }; if account.is_frozen { return Frozen @@ -193,7 +200,7 @@ impl, I: 'static> Pallet { Success } } else { - NoFunds + BalanceLow } } @@ -254,7 +261,7 @@ impl, I: 'static> Pallet { ensure!(f.best_effort || actual >= amount, Error::::BalanceLow); let conseq = Self::can_decrease(id, target, actual, f.keep_alive); - let actual = match conseq.into_result() { + let actual = match conseq.into_result(f.keep_alive) { Ok(dust) => actual.saturating_add(dust), //< guaranteed by reducible_balance Err(e) => { debug_assert!(false, "passed from reducible_balance; qed"); diff --git a/frame/assets/src/impl_fungibles.rs b/frame/assets/src/impl_fungibles.rs index f420ea02c..7bec884f4 100644 --- a/frame/assets/src/impl_fungibles.rs +++ b/frame/assets/src/impl_fungibles.rs @@ -17,6 +17,16 @@ //! Implementations for fungibles trait. +use frame_support::{ + defensive, + traits::tokens::{ + Fortitude, + Precision::{self, BestEffort}, + Preservation::{self, Expendable}, + Provenance::{self, Minted}, + }, +}; + use super::*; impl, I: 'static> fungibles::Inspect<::AccountId> for Pallet { @@ -35,21 +45,27 @@ impl, I: 'static> fungibles::Inspect<::AccountId Pallet::::balance(asset, who) } + fn total_balance(asset: Self::AssetId, who: &::AccountId) -> Self::Balance { + Pallet::::balance(asset, who) + } + fn reducible_balance( asset: Self::AssetId, who: &::AccountId, - keep_alive: bool, + preservation: Preservation, + _: Fortitude, ) -> Self::Balance { - Pallet::::reducible_balance(asset, who, keep_alive).unwrap_or(Zero::zero()) + Pallet::::reducible_balance(asset, who, !matches!(preservation, Expendable)) + .unwrap_or(Zero::zero()) } fn can_deposit( asset: Self::AssetId, who: &::AccountId, amount: Self::Balance, - mint: bool, + provenance: Provenance, ) -> DepositConsequence { - Pallet::::can_increase(asset, who, amount, mint) + Pallet::::can_increase(asset, who, amount, provenance == Minted) } fn can_withdraw( @@ -65,69 +81,26 @@ impl, I: 'static> fungibles::Inspect<::AccountId } } -impl, I: 'static> fungibles::InspectMetadata<::AccountId> +impl, I: 'static> fungibles::Mutate<::AccountId> for Pallet {} +impl, I: 'static> fungibles::Balanced<::AccountId> for Pallet { - /// Return the name of an asset. - fn name(asset: &Self::AssetId) -> Vec { - Metadata::::get(asset).name.to_vec() - } - - /// Return the symbol of an asset. - fn symbol(asset: &Self::AssetId) -> Vec { - Metadata::::get(asset).symbol.to_vec() - } - - /// Return the decimals of an asset. - fn decimals(asset: &Self::AssetId) -> u8 { - Metadata::::get(asset).decimals - } -} - -impl, I: 'static> fungibles::Mutate<::AccountId> for Pallet { - fn mint_into( - asset: Self::AssetId, - who: &::AccountId, - amount: Self::Balance, - ) -> DispatchResult { - Self::do_mint(asset, who, amount, None) - } - - fn burn_from( - asset: Self::AssetId, - who: &::AccountId, - amount: Self::Balance, - ) -> Result { - let f = DebitFlags { keep_alive: false, best_effort: false }; - Self::do_burn(asset, who, amount, None, f) - } - - fn slash( - asset: Self::AssetId, - who: &::AccountId, - amount: Self::Balance, - ) -> Result { - let f = DebitFlags { keep_alive: false, best_effort: true }; - Self::do_burn(asset, who, amount, None, f) - } -} - -impl, I: 'static> fungibles::Transfer for Pallet { - fn transfer( - asset: Self::AssetId, - source: &T::AccountId, - dest: &T::AccountId, - amount: T::Balance, - keep_alive: bool, - ) -> Result { - let f = TransferFlags { keep_alive, best_effort: false, burn_dust: false }; - Self::do_transfer(asset, source, dest, amount, None, f) - } + type OnDropCredit = fungibles::DecreaseIssuance; + type OnDropDebt = fungibles::IncreaseIssuance; } impl, I: 'static> fungibles::Unbalanced for Pallet { - fn set_balance(_: Self::AssetId, _: &T::AccountId, _: Self::Balance) -> DispatchResult { - unreachable!("set_balance is not used if other functions are impl'd"); + fn handle_raw_dust(_: Self::AssetId, _: Self::Balance) {} + fn handle_dust(_: fungibles::Dust) { + defensive!("`decrease_balance` and `increase_balance` have non-default impls; nothing else calls this; qed"); + } + fn write_balance( + _: Self::AssetId, + _: &T::AccountId, + _: Self::Balance, + ) -> Result, DispatchError> { + defensive!("write_balance is not used if other functions are impl'd"); + Err(DispatchError::Unavailable) } fn set_total_issuance(id: T::AssetId, amount: Self::Balance) { Asset::::mutate_exists(id, |maybe_asset| { @@ -140,36 +113,27 @@ impl, I: 'static> fungibles::Unbalanced for Pallet Result { - let f = DebitFlags { keep_alive: false, best_effort: false }; + let f = DebitFlags { + keep_alive: preservation != Expendable, + best_effort: precision == BestEffort, + }; Self::decrease_balance(asset, who, amount, f, |_, _| Ok(())) } - fn decrease_balance_at_most( - asset: T::AssetId, - who: &T::AccountId, - amount: Self::Balance, - ) -> Self::Balance { - let f = DebitFlags { keep_alive: false, best_effort: true }; - Self::decrease_balance(asset, who, amount, f, |_, _| Ok(())).unwrap_or(Zero::zero()) - } fn increase_balance( asset: T::AssetId, who: &T::AccountId, amount: Self::Balance, + _: Precision, ) -> Result { Self::increase_balance(asset, who, amount, |_| Ok(()))?; Ok(amount) } - fn increase_balance_at_most( - asset: T::AssetId, - who: &T::AccountId, - amount: Self::Balance, - ) -> Self::Balance { - match Self::increase_balance(asset, who, amount, |_| Ok(())) { - Ok(()) => amount, - Err(_) => Zero::zero(), - } - } + + // TODO: #13196 implement deactivate/reactivate once we have inactive balance tracking. } impl, I: 'static> fungibles::Create for Pallet { diff --git a/frame/assets/src/lib.rs b/frame/assets/src/lib.rs index b36a5cabd..859a13243 100644 --- a/frame/assets/src/lib.rs +++ b/frame/assets/src/lib.rs @@ -540,9 +540,9 @@ pub mod pallet { /// Minimum balance should be non-zero. MinBalanceZero, /// Unable to increment the consumer reference counters on the account. Either no provider - /// reference exists to allow a non-zero balance of a non-self-sufficient asset, or the - /// maximum number of consumers has been reached. - NoProvider, + /// reference exists to allow a non-zero balance of a non-self-sufficient asset, or one + /// fewer then the maximum number of consumers has been reached. + UnavailableConsumer, /// Invalid metadata given. BadMetadata, /// No approval exists that would allow the transfer. diff --git a/frame/assets/src/mock.rs b/frame/assets/src/mock.rs index 3f456a7de..3926d2fa8 100644 --- a/frame/assets/src/mock.rs +++ b/frame/assets/src/mock.rs @@ -74,7 +74,7 @@ impl frame_system::Config for Test { type SystemWeightInfo = (); type SS58Prefix = (); type OnSetCode = (); - type MaxConsumers = ConstU32<2>; + type MaxConsumers = ConstU32<3>; } impl pallet_balances::Config for Test { @@ -87,6 +87,10 @@ impl pallet_balances::Config for Test { type MaxLocks = (); type MaxReserves = (); type ReserveIdentifier = [u8; 8]; + type HoldIdentifier = (); + type FreezeIdentifier = (); + type MaxHolds = (); + type MaxFreezes = (); } pub struct AssetsCallbackHandle; diff --git a/frame/assets/src/tests.rs b/frame/assets/src/tests.rs index bc61810a7..d4a49ac5a 100644 --- a/frame/assets/src/tests.rs +++ b/frame/assets/src/tests.rs @@ -57,7 +57,10 @@ fn minting_too_many_insufficient_assets_fails() { Balances::make_free_balance_be(&1, 100); assert_ok!(Assets::mint(RuntimeOrigin::signed(1), 0, 1, 100)); assert_ok!(Assets::mint(RuntimeOrigin::signed(1), 1, 1, 100)); - assert_noop!(Assets::mint(RuntimeOrigin::signed(1), 2, 1, 100), TokenError::CannotCreate); + assert_noop!( + Assets::mint(RuntimeOrigin::signed(1), 2, 1, 100), + Error::::UnavailableConsumer + ); Balances::make_free_balance_be(&2, 1); assert_ok!(Assets::transfer(RuntimeOrigin::signed(1), 0, 2, 100)); @@ -75,7 +78,10 @@ fn minting_insufficient_asset_with_deposit_should_work_when_consumers_exhausted( Balances::make_free_balance_be(&1, 100); assert_ok!(Assets::mint(RuntimeOrigin::signed(1), 0, 1, 100)); assert_ok!(Assets::mint(RuntimeOrigin::signed(1), 1, 1, 100)); - assert_noop!(Assets::mint(RuntimeOrigin::signed(1), 2, 1, 100), TokenError::CannotCreate); + assert_noop!( + Assets::mint(RuntimeOrigin::signed(1), 2, 1, 100), + Error::::UnavailableConsumer + ); assert_ok!(Assets::touch(RuntimeOrigin::signed(1), 2)); assert_eq!(Balances::reserved_balance(&1), 10); @@ -93,7 +99,7 @@ fn minting_insufficient_assets_with_deposit_without_consumer_should_work() { assert_ok!(Assets::touch(RuntimeOrigin::signed(1), 0)); assert_ok!(Assets::mint(RuntimeOrigin::signed(1), 0, 1, 100)); assert_eq!(Balances::reserved_balance(&1), 10); - assert_eq!(System::consumers(&1), 0); + assert_eq!(System::consumers(&1), 1); }); } @@ -167,7 +173,7 @@ fn approval_lifecycle_works() { // so we create it :) assert_ok!(Assets::force_create(RuntimeOrigin::root(), 0, 1, true, 1)); assert_ok!(Assets::mint(RuntimeOrigin::signed(1), 0, 1, 100)); - Balances::make_free_balance_be(&1, 1); + Balances::make_free_balance_be(&1, 2); assert_ok!(Assets::approve_transfer(RuntimeOrigin::signed(1), 0, 2, 50)); assert_eq!(Asset::::get(0).unwrap().approvals, 1); assert_eq!(Balances::reserved_balance(&1), 1); @@ -193,7 +199,7 @@ fn transfer_approved_all_funds() { // so we create it :) assert_ok!(Assets::force_create(RuntimeOrigin::root(), 0, 1, true, 1)); assert_ok!(Assets::mint(RuntimeOrigin::signed(1), 0, 1, 100)); - Balances::make_free_balance_be(&1, 1); + Balances::make_free_balance_be(&1, 2); assert_ok!(Assets::approve_transfer(RuntimeOrigin::signed(1), 0, 2, 50)); assert_eq!(Asset::::get(0).unwrap().approvals, 1); assert_eq!(Balances::reserved_balance(&1), 1); @@ -215,7 +221,7 @@ fn approval_deposits_work() { let e = BalancesError::::InsufficientBalance; assert_noop!(Assets::approve_transfer(RuntimeOrigin::signed(1), 0, 2, 50), e); - Balances::make_free_balance_be(&1, 1); + Balances::make_free_balance_be(&1, 2); assert_ok!(Assets::approve_transfer(RuntimeOrigin::signed(1), 0, 2, 50)); assert_eq!(Balances::reserved_balance(&1), 1); @@ -233,7 +239,7 @@ fn cannot_transfer_more_than_approved() { new_test_ext().execute_with(|| { assert_ok!(Assets::force_create(RuntimeOrigin::root(), 0, 1, true, 1)); assert_ok!(Assets::mint(RuntimeOrigin::signed(1), 0, 1, 100)); - Balances::make_free_balance_be(&1, 1); + Balances::make_free_balance_be(&1, 2); assert_ok!(Assets::approve_transfer(RuntimeOrigin::signed(1), 0, 2, 50)); let e = Error::::Unapproved; assert_noop!(Assets::transfer_approved(RuntimeOrigin::signed(2), 0, 1, 3, 51), e); @@ -245,7 +251,7 @@ fn cannot_transfer_more_than_exists() { new_test_ext().execute_with(|| { assert_ok!(Assets::force_create(RuntimeOrigin::root(), 0, 1, true, 1)); assert_ok!(Assets::mint(RuntimeOrigin::signed(1), 0, 1, 100)); - Balances::make_free_balance_be(&1, 1); + Balances::make_free_balance_be(&1, 2); assert_ok!(Assets::approve_transfer(RuntimeOrigin::signed(1), 0, 2, 101)); let e = Error::::BalanceLow; assert_noop!(Assets::transfer_approved(RuntimeOrigin::signed(2), 0, 1, 3, 101), e); @@ -257,7 +263,7 @@ fn cancel_approval_works() { new_test_ext().execute_with(|| { assert_ok!(Assets::force_create(RuntimeOrigin::root(), 0, 1, true, 1)); assert_ok!(Assets::mint(RuntimeOrigin::signed(1), 0, 1, 100)); - Balances::make_free_balance_be(&1, 1); + Balances::make_free_balance_be(&1, 2); assert_ok!(Assets::approve_transfer(RuntimeOrigin::signed(1), 0, 2, 50)); assert_eq!(Asset::::get(0).unwrap().approvals, 1); assert_noop!( @@ -287,7 +293,7 @@ fn force_cancel_approval_works() { new_test_ext().execute_with(|| { assert_ok!(Assets::force_create(RuntimeOrigin::root(), 0, 1, true, 1)); assert_ok!(Assets::mint(RuntimeOrigin::signed(1), 0, 1, 100)); - Balances::make_free_balance_be(&1, 1); + Balances::make_free_balance_be(&1, 2); assert_ok!(Assets::approve_transfer(RuntimeOrigin::signed(1), 0, 2, 50)); assert_eq!(Asset::::get(0).unwrap().approvals, 1); let e = Error::::NoPermission; @@ -516,7 +522,7 @@ fn min_balance_should_work() { // Death by `transfer_approved`. assert_ok!(Assets::mint(RuntimeOrigin::signed(1), 0, 1, 100)); - Balances::make_free_balance_be(&1, 1); + Balances::make_free_balance_be(&1, 2); assert_ok!(Assets::approve_transfer(RuntimeOrigin::signed(1), 0, 2, 100)); assert_ok!(Assets::transfer_approved(RuntimeOrigin::signed(2), 0, 1, 3, 91)); assert_eq!(take_hooks(), vec![Hook::Died(0, 1)]); @@ -1217,7 +1223,7 @@ fn querying_allowance_should_work() { use frame_support::traits::tokens::fungibles::approvals::{Inspect, Mutate}; assert_ok!(Assets::force_create(RuntimeOrigin::root(), 0, 1, true, 1)); assert_ok!(Assets::mint(RuntimeOrigin::signed(1), 0, 1, 100)); - Balances::make_free_balance_be(&1, 1); + Balances::make_free_balance_be(&1, 2); assert_ok!(Assets::approve(0, &1, &2, 50)); assert_eq!(Assets::allowance(0, &1, &2), 50); // Transfer asset 0, from owner 1 and delegate 2 to destination 3 diff --git a/frame/atomic-swap/src/tests.rs b/frame/atomic-swap/src/tests.rs index 081a449ec..7437d62a9 100644 --- a/frame/atomic-swap/src/tests.rs +++ b/frame/atomic-swap/src/tests.rs @@ -62,6 +62,10 @@ impl pallet_balances::Config for Test { type ExistentialDeposit = ConstU64<1>; type AccountStore = System; type WeightInfo = (); + type FreezeIdentifier = (); + type MaxFreezes = (); + type HoldIdentifier = (); + type MaxHolds = (); } impl Config for Test { diff --git a/frame/babe/src/mock.rs b/frame/babe/src/mock.rs index 9b832bfff..94e748d0b 100644 --- a/frame/babe/src/mock.rs +++ b/frame/babe/src/mock.rs @@ -144,6 +144,10 @@ impl pallet_balances::Config for Test { type ExistentialDeposit = ConstU128<1>; type AccountStore = System; type WeightInfo = (); + type FreezeIdentifier = (); + type MaxFreezes = (); + type HoldIdentifier = (); + type MaxHolds = (); } pallet_staking_reward_curve::build! { diff --git a/frame/balances/README.md b/frame/balances/README.md index 93e424a89..dd56ab3fa 100644 --- a/frame/balances/README.md +++ b/frame/balances/README.md @@ -70,7 +70,7 @@ given account is unused. ### Dispatchable Functions - `transfer` - Transfer some liquid free balance to another account. -- `set_balance` - Set the balances of a given account. The origin of this call must be root. +- `force_set_balance` - Set the balances of a given account. The origin of this call must be root. ## Usage diff --git a/frame/balances/src/benchmarking.rs b/frame/balances/src/benchmarking.rs index b36fe1e34..5641c6851 100644 --- a/frame/balances/src/benchmarking.rs +++ b/frame/balances/src/benchmarking.rs @@ -24,6 +24,8 @@ use crate::Pallet as Balances; use frame_benchmarking::v2::*; use frame_system::RawOrigin; +use sp_runtime::traits::Bounded; +use types::ExtraFlags; const SEED: u32 = 0; // existential deposit multiplier @@ -37,7 +39,7 @@ mod benchmarks { // * Transfer will kill the sender account. // * Transfer will create the recipient account. #[benchmark] - fn transfer() { + fn transfer_allow_death() { let existential_deposit = T::ExistentialDeposit::get(); let caller = whitelisted_caller(); @@ -79,7 +81,7 @@ mod benchmarks { let transfer_amount = existential_deposit.saturating_mul(ED_MULTIPLIER.into()); #[extrinsic_call] - transfer(RawOrigin::Signed(caller.clone()), recipient_lookup, transfer_amount); + transfer_allow_death(RawOrigin::Signed(caller.clone()), recipient_lookup, transfer_amount); assert!(!Balances::::free_balance(&caller).is_zero()); assert!(!Balances::::free_balance(&recipient).is_zero()); @@ -106,9 +108,9 @@ mod benchmarks { assert_eq!(Balances::::free_balance(&recipient), transfer_amount); } - // Benchmark `set_balance` coming from ROOT account. This always creates an account. + // Benchmark `force_set_balance` coming from ROOT account. This always creates an account. #[benchmark] - fn set_balance_creating() { + fn force_set_balance_creating() { let user: T::AccountId = account("user", 0, SEED); let user_lookup = T::Lookup::unlookup(user.clone()); @@ -118,15 +120,14 @@ mod benchmarks { let _ = as Currency<_>>::make_free_balance_be(&user, balance_amount); #[extrinsic_call] - set_balance(RawOrigin::Root, user_lookup, balance_amount, balance_amount); + force_set_balance(RawOrigin::Root, user_lookup, balance_amount); assert_eq!(Balances::::free_balance(&user), balance_amount); - assert_eq!(Balances::::reserved_balance(&user), balance_amount); } - // Benchmark `set_balance` coming from ROOT account. This always kills an account. + // Benchmark `force_set_balance` coming from ROOT account. This always kills an account. #[benchmark] - fn set_balance_killing() { + fn force_set_balance_killing() { let user: T::AccountId = account("user", 0, SEED); let user_lookup = T::Lookup::unlookup(user.clone()); @@ -136,7 +137,7 @@ mod benchmarks { let _ = as Currency<_>>::make_free_balance_be(&user, balance_amount); #[extrinsic_call] - set_balance(RawOrigin::Root, user_lookup, Zero::zero(), Zero::zero()); + force_set_balance(RawOrigin::Root, user_lookup, Zero::zero()); assert!(Balances::::free_balance(&user).is_zero()); } @@ -197,7 +198,7 @@ mod benchmarks { } #[extrinsic_call] - transfer(RawOrigin::Signed(caller.clone()), recipient_lookup, transfer_amount); + transfer_allow_death(RawOrigin::Signed(caller.clone()), recipient_lookup, transfer_amount); assert_eq!(Balances::::free_balance(&caller), Zero::zero()); assert_eq!(Balances::::free_balance(&recipient), transfer_amount); @@ -230,27 +231,64 @@ mod benchmarks { let user_lookup = T::Lookup::unlookup(user.clone()); // Give some multiple of the existential deposit - let existential_deposit = T::ExistentialDeposit::get(); - let balance = existential_deposit.saturating_mul(ED_MULTIPLIER.into()); + let ed = T::ExistentialDeposit::get(); + let balance = ed + ed; let _ = as Currency<_>>::make_free_balance_be(&user, balance); // Reserve the balance - as ReservableCurrency<_>>::reserve(&user, balance)?; - assert_eq!(Balances::::reserved_balance(&user), balance); - assert!(Balances::::free_balance(&user).is_zero()); + as ReservableCurrency<_>>::reserve(&user, ed)?; + assert_eq!(Balances::::reserved_balance(&user), ed); + assert_eq!(Balances::::free_balance(&user), ed); #[extrinsic_call] _(RawOrigin::Root, user_lookup, balance); assert!(Balances::::reserved_balance(&user).is_zero()); - assert_eq!(Balances::::free_balance(&user), balance); + assert_eq!(Balances::::free_balance(&user), ed + ed); Ok(()) } + #[benchmark] + fn upgrade_accounts(u: Linear<1, 1_000>) { + let caller: T::AccountId = whitelisted_caller(); + let who = (0..u) + .into_iter() + .map(|i| -> T::AccountId { + let user = account("old_user", i, SEED); + let account = AccountData { + free: T::ExistentialDeposit::get(), + reserved: T::ExistentialDeposit::get(), + frozen: Zero::zero(), + flags: ExtraFlags::old_logic(), + }; + frame_system::Pallet::::inc_providers(&user); + assert!(T::AccountStore::try_mutate_exists(&user, |a| -> DispatchResult { + *a = Some(account); + Ok(()) + }) + .is_ok()); + assert!(!Balances::::account(&user).flags.is_new_logic()); + assert_eq!(frame_system::Pallet::::providers(&user), 1); + assert_eq!(frame_system::Pallet::::consumers(&user), 0); + user + }) + .collect(); + + #[extrinsic_call] + _(RawOrigin::Signed(caller.clone()), who); + + for i in 0..u { + let user: T::AccountId = account("old_user", i, SEED); + assert!(Balances::::account(&user).flags.is_new_logic()); + assert_eq!(frame_system::Pallet::::providers(&user), 1); + assert_eq!(frame_system::Pallet::::consumers(&user), 1); + } + } + impl_benchmark_test_suite! { Balances, - crate::tests_composite::ExtBuilder::default().build(), - crate::tests_composite::Test, + crate::tests::ExtBuilder::default().build(), + crate::tests::Test, } } diff --git a/frame/balances/src/impl_currency.rs b/frame/balances/src/impl_currency.rs new file mode 100644 index 000000000..790a29f00 --- /dev/null +++ b/frame/balances/src/impl_currency.rs @@ -0,0 +1,898 @@ +// This file is part of Substrate. + +// Copyright (C) 2017-2022 Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +//! Implementations for the `Currency` family of traits. + +use super::*; +use frame_support::{ + ensure, + pallet_prelude::DispatchResult, + traits::{ + tokens::{fungible, BalanceStatus as Status}, + Currency, DefensiveSaturating, ExistenceRequirement, + ExistenceRequirement::AllowDeath, + Get, Imbalance, LockIdentifier, LockableCurrency, NamedReservableCurrency, + ReservableCurrency, SignedImbalance, TryDrop, WithdrawReasons, + }, +}; +pub use imbalances::{NegativeImbalance, PositiveImbalance}; + +// wrapping these imbalances in a private module is necessary to ensure absolute privacy +// of the inner member. +mod imbalances { + use super::{result, Config, Imbalance, RuntimeDebug, Saturating, TryDrop, Zero}; + use frame_support::traits::SameOrOther; + use sp_std::mem; + + /// Opaque, move-only struct with private fields that serves as a token denoting that + /// funds have been created without any equal and opposite accounting. + #[must_use] + #[derive(RuntimeDebug, PartialEq, Eq)] + pub struct PositiveImbalance, I: 'static = ()>(T::Balance); + + impl, I: 'static> PositiveImbalance { + /// Create a new positive imbalance from a balance. + pub fn new(amount: T::Balance) -> Self { + PositiveImbalance(amount) + } + } + + /// Opaque, move-only struct with private fields that serves as a token denoting that + /// funds have been destroyed without any equal and opposite accounting. + #[must_use] + #[derive(RuntimeDebug, PartialEq, Eq)] + pub struct NegativeImbalance, I: 'static = ()>(T::Balance); + + impl, I: 'static> NegativeImbalance { + /// Create a new negative imbalance from a balance. + pub fn new(amount: T::Balance) -> Self { + NegativeImbalance(amount) + } + } + + impl, I: 'static> TryDrop for PositiveImbalance { + fn try_drop(self) -> result::Result<(), Self> { + self.drop_zero() + } + } + + impl, I: 'static> Default for PositiveImbalance { + fn default() -> Self { + Self::zero() + } + } + + impl, I: 'static> Imbalance for PositiveImbalance { + type Opposite = NegativeImbalance; + + fn zero() -> Self { + Self(Zero::zero()) + } + fn drop_zero(self) -> result::Result<(), Self> { + if self.0.is_zero() { + Ok(()) + } else { + Err(self) + } + } + fn split(self, amount: T::Balance) -> (Self, Self) { + let first = self.0.min(amount); + let second = self.0 - first; + + mem::forget(self); + (Self(first), Self(second)) + } + fn merge(mut self, other: Self) -> Self { + self.0 = self.0.saturating_add(other.0); + mem::forget(other); + + self + } + fn subsume(&mut self, other: Self) { + self.0 = self.0.saturating_add(other.0); + mem::forget(other); + } + fn offset(self, other: Self::Opposite) -> SameOrOther { + let (a, b) = (self.0, other.0); + mem::forget((self, other)); + + if a > b { + SameOrOther::Same(Self(a - b)) + } else if b > a { + SameOrOther::Other(NegativeImbalance::new(b - a)) + } else { + SameOrOther::None + } + } + fn peek(&self) -> T::Balance { + self.0 + } + } + + impl, I: 'static> TryDrop for NegativeImbalance { + fn try_drop(self) -> result::Result<(), Self> { + self.drop_zero() + } + } + + impl, I: 'static> Default for NegativeImbalance { + fn default() -> Self { + Self::zero() + } + } + + impl, I: 'static> Imbalance for NegativeImbalance { + type Opposite = PositiveImbalance; + + fn zero() -> Self { + Self(Zero::zero()) + } + fn drop_zero(self) -> result::Result<(), Self> { + if self.0.is_zero() { + Ok(()) + } else { + Err(self) + } + } + fn split(self, amount: T::Balance) -> (Self, Self) { + let first = self.0.min(amount); + let second = self.0 - first; + + mem::forget(self); + (Self(first), Self(second)) + } + fn merge(mut self, other: Self) -> Self { + self.0 = self.0.saturating_add(other.0); + mem::forget(other); + + self + } + fn subsume(&mut self, other: Self) { + self.0 = self.0.saturating_add(other.0); + mem::forget(other); + } + fn offset(self, other: Self::Opposite) -> SameOrOther { + let (a, b) = (self.0, other.0); + mem::forget((self, other)); + + if a > b { + SameOrOther::Same(Self(a - b)) + } else if b > a { + SameOrOther::Other(PositiveImbalance::new(b - a)) + } else { + SameOrOther::None + } + } + fn peek(&self) -> T::Balance { + self.0 + } + } + + impl, I: 'static> Drop for PositiveImbalance { + /// Basic drop handler will just square up the total issuance. + fn drop(&mut self) { + >::mutate(|v| *v = v.saturating_add(self.0)); + } + } + + impl, I: 'static> Drop for NegativeImbalance { + /// Basic drop handler will just square up the total issuance. + fn drop(&mut self) { + >::mutate(|v| *v = v.saturating_sub(self.0)); + } + } +} + +impl, I: 'static> Currency for Pallet +where + T::Balance: MaybeSerializeDeserialize + Debug, +{ + type Balance = T::Balance; + type PositiveImbalance = PositiveImbalance; + type NegativeImbalance = NegativeImbalance; + + fn total_balance(who: &T::AccountId) -> Self::Balance { + Self::account(who).total() + } + + // Check if `value` amount of free balance can be slashed from `who`. + fn can_slash(who: &T::AccountId, value: Self::Balance) -> bool { + if value.is_zero() { + return true + } + Self::free_balance(who) >= value + } + + fn total_issuance() -> Self::Balance { + TotalIssuance::::get() + } + + fn active_issuance() -> Self::Balance { + >::active_issuance() + } + + fn deactivate(amount: Self::Balance) { + >::deactivate(amount); + } + + fn reactivate(amount: Self::Balance) { + >::reactivate(amount); + } + + fn minimum_balance() -> Self::Balance { + T::ExistentialDeposit::get() + } + + // Burn funds from the total issuance, returning a positive imbalance for the amount burned. + // Is a no-op if amount to be burned is zero. + fn burn(mut amount: Self::Balance) -> Self::PositiveImbalance { + if amount.is_zero() { + return PositiveImbalance::zero() + } + >::mutate(|issued| { + *issued = issued.checked_sub(&amount).unwrap_or_else(|| { + amount = *issued; + Zero::zero() + }); + }); + PositiveImbalance::new(amount) + } + + // Create new funds into the total issuance, returning a negative imbalance + // for the amount issued. + // Is a no-op if amount to be issued it zero. + fn issue(mut amount: Self::Balance) -> Self::NegativeImbalance { + if amount.is_zero() { + return NegativeImbalance::zero() + } + >::mutate(|issued| { + *issued = issued.checked_add(&amount).unwrap_or_else(|| { + amount = Self::Balance::max_value() - *issued; + Self::Balance::max_value() + }) + }); + NegativeImbalance::new(amount) + } + + fn free_balance(who: &T::AccountId) -> Self::Balance { + Self::account(who).free + } + + // Ensure that an account can withdraw from their free balance given any existing withdrawal + // restrictions like locks and vesting balance. + // Is a no-op if amount to be withdrawn is zero. + fn ensure_can_withdraw( + who: &T::AccountId, + amount: T::Balance, + _reasons: WithdrawReasons, + new_balance: T::Balance, + ) -> DispatchResult { + if amount.is_zero() { + return Ok(()) + } + ensure!(new_balance >= Self::account(who).frozen, Error::::LiquidityRestrictions); + Ok(()) + } + + // Transfer some free balance from `transactor` to `dest`, respecting existence requirements. + // Is a no-op if value to be transferred is zero or the `transactor` is the same as `dest`. + fn transfer( + transactor: &T::AccountId, + dest: &T::AccountId, + value: Self::Balance, + existence_requirement: ExistenceRequirement, + ) -> DispatchResult { + if value.is_zero() || transactor == dest { + return Ok(()) + } + let keep_alive = match existence_requirement { + ExistenceRequirement::KeepAlive => Preserve, + ExistenceRequirement::AllowDeath => Expendable, + }; + >::transfer(transactor, dest, value, keep_alive)?; + Ok(()) + } + + /// Slash a target account `who`, returning the negative imbalance created and any left over + /// amount that could not be slashed. + /// + /// Is a no-op if `value` to be slashed is zero or the account does not exist. + /// + /// NOTE: `slash()` prefers free balance, but assumes that reserve balance can be drawn + /// from in extreme circumstances. `can_slash()` should be used prior to `slash()` to avoid + /// having to draw from reserved funds, however we err on the side of punishment if things are + /// inconsistent or `can_slash` wasn't used appropriately. + fn slash(who: &T::AccountId, value: Self::Balance) -> (Self::NegativeImbalance, Self::Balance) { + if value.is_zero() { + return (NegativeImbalance::zero(), Zero::zero()) + } + if Self::total_balance(who).is_zero() { + return (NegativeImbalance::zero(), value) + } + + let result = match Self::try_mutate_account_handling_dust( + who, + |account, _is_new| -> Result<(Self::NegativeImbalance, Self::Balance), DispatchError> { + // Best value is the most amount we can slash following liveness rules. + let ed = T::ExistentialDeposit::get(); + let actual = match system::Pallet::::can_dec_provider(who) { + true => value.min(account.free), + false => value.min(account.free.saturating_sub(ed)), + }; + account.free.saturating_reduce(actual); + let remaining = value.saturating_sub(actual); + Ok((NegativeImbalance::new(actual), remaining)) + }, + ) { + Ok((imbalance, remaining)) => { + Self::deposit_event(Event::Slashed { + who: who.clone(), + amount: value.saturating_sub(remaining), + }); + (imbalance, remaining) + }, + Err(_) => (Self::NegativeImbalance::zero(), value), + }; + result + } + + /// Deposit some `value` into the free balance of an existing target account `who`. + /// + /// Is a no-op if the `value` to be deposited is zero. + fn deposit_into_existing( + who: &T::AccountId, + value: Self::Balance, + ) -> Result { + if value.is_zero() { + return Ok(PositiveImbalance::zero()) + } + + Self::try_mutate_account_handling_dust( + who, + |account, is_new| -> Result { + ensure!(!is_new, Error::::DeadAccount); + account.free = account.free.checked_add(&value).ok_or(ArithmeticError::Overflow)?; + Self::deposit_event(Event::Deposit { who: who.clone(), amount: value }); + Ok(PositiveImbalance::new(value)) + }, + ) + } + + /// Deposit some `value` into the free balance of `who`, possibly creating a new account. + /// + /// This function is a no-op if: + /// - the `value` to be deposited is zero; or + /// - the `value` to be deposited is less than the required ED and the account does not yet + /// exist; or + /// - the deposit would necessitate the account to exist and there are no provider references; + /// or + /// - `value` is so large it would cause the balance of `who` to overflow. + fn deposit_creating(who: &T::AccountId, value: Self::Balance) -> Self::PositiveImbalance { + if value.is_zero() { + return Self::PositiveImbalance::zero() + } + + Self::try_mutate_account_handling_dust( + who, + |account, is_new| -> Result { + let ed = T::ExistentialDeposit::get(); + ensure!(value >= ed || !is_new, Error::::ExistentialDeposit); + + // defensive only: overflow should never happen, however in case it does, then this + // operation is a no-op. + account.free = match account.free.checked_add(&value) { + Some(x) => x, + None => return Ok(Self::PositiveImbalance::zero()), + }; + + Self::deposit_event(Event::Deposit { who: who.clone(), amount: value }); + Ok(PositiveImbalance::new(value)) + }, + ) + .unwrap_or_else(|_| Self::PositiveImbalance::zero()) + } + + /// Withdraw some free balance from an account, respecting existence requirements. + /// + /// Is a no-op if value to be withdrawn is zero. + fn withdraw( + who: &T::AccountId, + value: Self::Balance, + reasons: WithdrawReasons, + liveness: ExistenceRequirement, + ) -> result::Result { + if value.is_zero() { + return Ok(NegativeImbalance::zero()) + } + + Self::try_mutate_account_handling_dust( + who, + |account, _| -> Result { + let new_free_account = + account.free.checked_sub(&value).ok_or(Error::::InsufficientBalance)?; + + // bail if we need to keep the account alive and this would kill it. + let ed = T::ExistentialDeposit::get(); + let would_be_dead = new_free_account < ed; + let would_kill = would_be_dead && account.free >= ed; + ensure!(liveness == AllowDeath || !would_kill, Error::::Expendability); + + Self::ensure_can_withdraw(who, value, reasons, new_free_account)?; + + account.free = new_free_account; + + Self::deposit_event(Event::Withdraw { who: who.clone(), amount: value }); + Ok(NegativeImbalance::new(value)) + }, + ) + } + + /// Force the new free balance of a target account `who` to some new value `balance`. + fn make_free_balance_be( + who: &T::AccountId, + value: Self::Balance, + ) -> SignedImbalance { + Self::try_mutate_account_handling_dust( + who, + |account, + is_new| + -> Result, DispatchError> { + let ed = T::ExistentialDeposit::get(); + // If we're attempting to set an existing account to less than ED, then + // bypass the entire operation. It's a no-op if you follow it through, but + // since this is an instance where we might account for a negative imbalance + // (in the dust cleaner of set_account) before we account for its actual + // equal and opposite cause (returned as an Imbalance), then in the + // instance that there's no other accounts on the system at all, we might + // underflow the issuance and our arithmetic will be off. + ensure!(value >= ed || !is_new, Error::::ExistentialDeposit); + + let imbalance = if account.free <= value { + SignedImbalance::Positive(PositiveImbalance::new(value - account.free)) + } else { + SignedImbalance::Negative(NegativeImbalance::new(account.free - value)) + }; + account.free = value; + Self::deposit_event(Event::BalanceSet { who: who.clone(), free: account.free }); + Ok(imbalance) + }, + ) + .unwrap_or_else(|_| SignedImbalance::Positive(Self::PositiveImbalance::zero())) + } +} + +impl, I: 'static> ReservableCurrency for Pallet +where + T::Balance: MaybeSerializeDeserialize + Debug, +{ + /// Check if `who` can reserve `value` from their free balance. + /// + /// Always `true` if value to be reserved is zero. + fn can_reserve(who: &T::AccountId, value: Self::Balance) -> bool { + if value.is_zero() { + return true + } + Self::account(who).free.checked_sub(&value).map_or(false, |new_balance| { + Self::ensure_can_withdraw(who, value, WithdrawReasons::RESERVE, new_balance).is_ok() + }) + } + + fn reserved_balance(who: &T::AccountId) -> Self::Balance { + Self::account(who).reserved + } + + /// Move `value` from the free balance from `who` to their reserved balance. + /// + /// Is a no-op if value to be reserved is zero. + fn reserve(who: &T::AccountId, value: Self::Balance) -> DispatchResult { + if value.is_zero() { + return Ok(()) + } + + Self::try_mutate_account_handling_dust(who, |account, _| -> DispatchResult { + account.free = + account.free.checked_sub(&value).ok_or(Error::::InsufficientBalance)?; + account.reserved = + account.reserved.checked_add(&value).ok_or(ArithmeticError::Overflow)?; + Self::ensure_can_withdraw(&who, value, WithdrawReasons::RESERVE, account.free) + })?; + + Self::deposit_event(Event::Reserved { who: who.clone(), amount: value }); + Ok(()) + } + + /// Unreserve some funds, returning any amount that was unable to be unreserved. + /// + /// Is a no-op if the value to be unreserved is zero or the account does not exist. + /// + /// NOTE: returns amount value which wasn't successfully unreserved. + fn unreserve(who: &T::AccountId, value: Self::Balance) -> Self::Balance { + if value.is_zero() { + return Zero::zero() + } + if Self::total_balance(who).is_zero() { + return value + } + + let actual = match Self::mutate_account_handling_dust(who, |account| { + let actual = cmp::min(account.reserved, value); + account.reserved -= actual; + // defensive only: this can never fail since total issuance which is at least + // free+reserved fits into the same data type. + account.free = account.free.defensive_saturating_add(actual); + actual + }) { + Ok(x) => x, + Err(_) => { + // This should never happen since we don't alter the total amount in the account. + // If it ever does, then we should fail gracefully though, indicating that nothing + // could be done. + return value + }, + }; + + Self::deposit_event(Event::Unreserved { who: who.clone(), amount: actual }); + value - actual + } + + /// Slash from reserved balance, returning the negative imbalance created, + /// and any amount that was unable to be slashed. + /// + /// Is a no-op if the value to be slashed is zero or the account does not exist. + fn slash_reserved( + who: &T::AccountId, + value: Self::Balance, + ) -> (Self::NegativeImbalance, Self::Balance) { + if value.is_zero() { + return (NegativeImbalance::zero(), Zero::zero()) + } + if Self::total_balance(who).is_zero() { + return (NegativeImbalance::zero(), value) + } + + // NOTE: `mutate_account` may fail if it attempts to reduce the balance to the point that an + // account is attempted to be illegally destroyed. + + match Self::mutate_account_handling_dust(who, |account| { + let actual = value.min(account.reserved); + account.reserved.saturating_reduce(actual); + + // underflow should never happen, but it if does, there's nothing to be done here. + (NegativeImbalance::new(actual), value.saturating_sub(actual)) + }) { + Ok((imbalance, not_slashed)) => { + Self::deposit_event(Event::Slashed { + who: who.clone(), + amount: value.saturating_sub(not_slashed), + }); + (imbalance, not_slashed) + }, + Err(_) => (Self::NegativeImbalance::zero(), value), + } + } + + /// Move the reserved balance of one account into the balance of another, according to `status`. + /// + /// Is a no-op if: + /// - the value to be moved is zero; or + /// - the `slashed` id equal to `beneficiary` and the `status` is `Reserved`. + fn repatriate_reserved( + slashed: &T::AccountId, + beneficiary: &T::AccountId, + value: Self::Balance, + status: Status, + ) -> Result { + let actual = Self::do_transfer_reserved(slashed, beneficiary, value, true, status)?; + Ok(value.saturating_sub(actual)) + } +} + +impl, I: 'static> NamedReservableCurrency for Pallet +where + T::Balance: MaybeSerializeDeserialize + Debug, +{ + type ReserveIdentifier = T::ReserveIdentifier; + + fn reserved_balance_named(id: &Self::ReserveIdentifier, who: &T::AccountId) -> Self::Balance { + let reserves = Self::reserves(who); + reserves + .binary_search_by_key(id, |data| data.id) + .map(|index| reserves[index].amount) + .unwrap_or_default() + } + + /// Move `value` from the free balance from `who` to a named reserve balance. + /// + /// Is a no-op if value to be reserved is zero. + fn reserve_named( + id: &Self::ReserveIdentifier, + who: &T::AccountId, + value: Self::Balance, + ) -> DispatchResult { + if value.is_zero() { + return Ok(()) + } + + Reserves::::try_mutate(who, |reserves| -> DispatchResult { + match reserves.binary_search_by_key(id, |data| data.id) { + Ok(index) => { + // this add can't overflow but just to be defensive. + reserves[index].amount = reserves[index].amount.defensive_saturating_add(value); + }, + Err(index) => { + reserves + .try_insert(index, ReserveData { id: *id, amount: value }) + .map_err(|_| Error::::TooManyReserves)?; + }, + }; + >::reserve(who, value)?; + Ok(()) + }) + } + + /// Unreserve some funds, returning any amount that was unable to be unreserved. + /// + /// Is a no-op if the value to be unreserved is zero. + fn unreserve_named( + id: &Self::ReserveIdentifier, + who: &T::AccountId, + value: Self::Balance, + ) -> Self::Balance { + if value.is_zero() { + return Zero::zero() + } + + Reserves::::mutate_exists(who, |maybe_reserves| -> Self::Balance { + if let Some(reserves) = maybe_reserves.as_mut() { + match reserves.binary_search_by_key(id, |data| data.id) { + Ok(index) => { + let to_change = cmp::min(reserves[index].amount, value); + + let remain = >::unreserve(who, to_change); + + // remain should always be zero but just to be defensive here. + let actual = to_change.defensive_saturating_sub(remain); + + // `actual <= to_change` and `to_change <= amount`; qed; + reserves[index].amount -= actual; + + if reserves[index].amount.is_zero() { + if reserves.len() == 1 { + // no more named reserves + *maybe_reserves = None; + } else { + // remove this named reserve + reserves.remove(index); + } + } + + value - actual + }, + Err(_) => value, + } + } else { + value + } + }) + } + + /// Slash from reserved balance, returning the negative imbalance created, + /// and any amount that was unable to be slashed. + /// + /// Is a no-op if the value to be slashed is zero. + fn slash_reserved_named( + id: &Self::ReserveIdentifier, + who: &T::AccountId, + value: Self::Balance, + ) -> (Self::NegativeImbalance, Self::Balance) { + if value.is_zero() { + return (NegativeImbalance::zero(), Zero::zero()) + } + + Reserves::::mutate(who, |reserves| -> (Self::NegativeImbalance, Self::Balance) { + match reserves.binary_search_by_key(id, |data| data.id) { + Ok(index) => { + let to_change = cmp::min(reserves[index].amount, value); + + let (imb, remain) = + >::slash_reserved(who, to_change); + + // remain should always be zero but just to be defensive here. + let actual = to_change.defensive_saturating_sub(remain); + + // `actual <= to_change` and `to_change <= amount`; qed; + reserves[index].amount -= actual; + + Self::deposit_event(Event::Slashed { who: who.clone(), amount: actual }); + (imb, value - actual) + }, + Err(_) => (NegativeImbalance::zero(), value), + } + }) + } + + /// Move the reserved balance of one account into the balance of another, according to `status`. + /// If `status` is `Reserved`, the balance will be reserved with given `id`. + /// + /// Is a no-op if: + /// - the value to be moved is zero; or + /// - the `slashed` id equal to `beneficiary` and the `status` is `Reserved`. + fn repatriate_reserved_named( + id: &Self::ReserveIdentifier, + slashed: &T::AccountId, + beneficiary: &T::AccountId, + value: Self::Balance, + status: Status, + ) -> Result { + if value.is_zero() { + return Ok(Zero::zero()) + } + + if slashed == beneficiary { + return match status { + Status::Free => Ok(Self::unreserve_named(id, slashed, value)), + Status::Reserved => + Ok(value.saturating_sub(Self::reserved_balance_named(id, slashed))), + } + } + + Reserves::::try_mutate(slashed, |reserves| -> Result { + match reserves.binary_search_by_key(id, |data| data.id) { + Ok(index) => { + let to_change = cmp::min(reserves[index].amount, value); + + let actual = if status == Status::Reserved { + // make it the reserved under same identifier + Reserves::::try_mutate( + beneficiary, + |reserves| -> Result { + match reserves.binary_search_by_key(id, |data| data.id) { + Ok(index) => { + let remain = + >::repatriate_reserved( + slashed, + beneficiary, + to_change, + status, + )?; + + // remain should always be zero but just to be defensive + // here. + let actual = to_change.defensive_saturating_sub(remain); + + // this add can't overflow but just to be defensive. + reserves[index].amount = + reserves[index].amount.defensive_saturating_add(actual); + + Ok(actual) + }, + Err(index) => { + let remain = + >::repatriate_reserved( + slashed, + beneficiary, + to_change, + status, + )?; + + // remain should always be zero but just to be defensive + // here + let actual = to_change.defensive_saturating_sub(remain); + + reserves + .try_insert( + index, + ReserveData { id: *id, amount: actual }, + ) + .map_err(|_| Error::::TooManyReserves)?; + + Ok(actual) + }, + } + }, + )? + } else { + let remain = >::repatriate_reserved( + slashed, + beneficiary, + to_change, + status, + )?; + + // remain should always be zero but just to be defensive here + to_change.defensive_saturating_sub(remain) + }; + + // `actual <= to_change` and `to_change <= amount`; qed; + reserves[index].amount -= actual; + + Ok(value - actual) + }, + Err(_) => Ok(value), + } + }) + } +} + +impl, I: 'static> LockableCurrency for Pallet +where + T::Balance: MaybeSerializeDeserialize + Debug, +{ + type Moment = T::BlockNumber; + + type MaxLocks = T::MaxLocks; + + // Set a lock on the balance of `who`. + // Is a no-op if lock amount is zero or `reasons` `is_none()`. + fn set_lock( + id: LockIdentifier, + who: &T::AccountId, + amount: T::Balance, + reasons: WithdrawReasons, + ) { + if amount.is_zero() || reasons.is_empty() { + return + } + let mut new_lock = Some(BalanceLock { id, amount, reasons: reasons.into() }); + let mut locks = Self::locks(who) + .into_iter() + .filter_map(|l| if l.id == id { new_lock.take() } else { Some(l) }) + .collect::>(); + if let Some(lock) = new_lock { + locks.push(lock) + } + Self::update_locks(who, &locks[..]); + } + + // Extend a lock on the balance of `who`. + // Is a no-op if lock amount is zero or `reasons` `is_none()`. + fn extend_lock( + id: LockIdentifier, + who: &T::AccountId, + amount: T::Balance, + reasons: WithdrawReasons, + ) { + if amount.is_zero() || reasons.is_empty() { + return + } + let mut new_lock = Some(BalanceLock { id, amount, reasons: reasons.into() }); + let mut locks = Self::locks(who) + .into_iter() + .filter_map(|l| { + if l.id == id { + new_lock.take().map(|nl| BalanceLock { + id: l.id, + amount: l.amount.max(nl.amount), + reasons: l.reasons | nl.reasons, + }) + } else { + Some(l) + } + }) + .collect::>(); + if let Some(lock) = new_lock { + locks.push(lock) + } + Self::update_locks(who, &locks[..]); + } + + fn remove_lock(id: LockIdentifier, who: &T::AccountId) { + let mut locks = Self::locks(who); + locks.retain(|l| l.id != id); + Self::update_locks(who, &locks[..]); + } +} diff --git a/frame/balances/src/impl_fungible.rs b/frame/balances/src/impl_fungible.rs new file mode 100644 index 000000000..f8f8fe17a --- /dev/null +++ b/frame/balances/src/impl_fungible.rs @@ -0,0 +1,358 @@ +// This file is part of Substrate. + +// Copyright (C) 2017-2022 Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +//! Implementation of `fungible` traits for Balances pallet. +use super::*; +use frame_support::traits::tokens::{ + Fortitude, + Preservation::{self, Preserve, Protect}, + Provenance::{self, Minted}, +}; + +impl, I: 'static> fungible::Inspect for Pallet { + type Balance = T::Balance; + + fn total_issuance() -> Self::Balance { + TotalIssuance::::get() + } + fn active_issuance() -> Self::Balance { + TotalIssuance::::get().saturating_sub(InactiveIssuance::::get()) + } + fn minimum_balance() -> Self::Balance { + T::ExistentialDeposit::get() + } + fn total_balance(who: &T::AccountId) -> Self::Balance { + Self::account(who).total() + } + fn balance(who: &T::AccountId) -> Self::Balance { + Self::account(who).free + } + fn reducible_balance( + who: &T::AccountId, + preservation: Preservation, + force: Fortitude, + ) -> Self::Balance { + let a = Self::account(who); + let mut untouchable = Zero::zero(); + if force == Polite { + // Frozen balance applies to total. Anything on hold therefore gets discounted from the + // limit given by the freezes. + untouchable = a.frozen.saturating_sub(a.reserved); + } + // If we want to keep our provider ref.. + if preservation == Preserve + // ..or we don't want the account to die and our provider ref is needed for it to live.. + || preservation == Protect && !a.free.is_zero() && + frame_system::Pallet::::providers(who) == 1 + // ..or we don't care about the account dying but our provider ref is required.. + || preservation == Expendable && !a.free.is_zero() && + !frame_system::Pallet::::can_dec_provider(who) + { + // ..then the ED needed.. + untouchable = untouchable.max(T::ExistentialDeposit::get()); + } + // Liquid balance is what is neither on hold nor frozen/required for provider. + a.free.saturating_sub(untouchable) + } + fn can_deposit( + who: &T::AccountId, + amount: Self::Balance, + provenance: Provenance, + ) -> DepositConsequence { + if amount.is_zero() { + return DepositConsequence::Success + } + + if provenance == Minted && TotalIssuance::::get().checked_add(&amount).is_none() { + return DepositConsequence::Overflow + } + + let account = Self::account(who); + let new_free = match account.free.checked_add(&amount) { + None => return DepositConsequence::Overflow, + Some(x) if x < T::ExistentialDeposit::get() => return DepositConsequence::BelowMinimum, + Some(x) => x, + }; + + match account.reserved.checked_add(&new_free) { + Some(_) => {}, + None => return DepositConsequence::Overflow, + }; + + // NOTE: We assume that we are a provider, so don't need to do any checks in the + // case of account creation. + + DepositConsequence::Success + } + fn can_withdraw( + who: &T::AccountId, + amount: Self::Balance, + ) -> WithdrawConsequence { + if amount.is_zero() { + return WithdrawConsequence::Success + } + + if TotalIssuance::::get().checked_sub(&amount).is_none() { + return WithdrawConsequence::Underflow + } + + let account = Self::account(who); + let new_free_balance = match account.free.checked_sub(&amount) { + Some(x) => x, + None => return WithdrawConsequence::BalanceLow, + }; + + let liquid = Self::reducible_balance(who, Expendable, Polite); + if amount > liquid { + return WithdrawConsequence::Frozen + } + + // Provider restriction - total account balance cannot be reduced to zero if it cannot + // sustain the loss of a provider reference. + // NOTE: This assumes that the pallet is a provider (which is true). Is this ever changes, + // then this will need to adapt accordingly. + let ed = T::ExistentialDeposit::get(); + let success = if new_free_balance < ed { + if frame_system::Pallet::::can_dec_provider(who) { + WithdrawConsequence::ReducedToZero(new_free_balance) + } else { + return WithdrawConsequence::WouldDie + } + } else { + WithdrawConsequence::Success + }; + + let new_total_balance = new_free_balance.saturating_add(account.reserved); + + // Eventual free funds must be no less than the frozen balance. + if new_total_balance < account.frozen { + return WithdrawConsequence::Frozen + } + + success + } +} + +impl, I: 'static> fungible::Unbalanced for Pallet { + fn handle_dust(dust: fungible::Dust) { + T::DustRemoval::on_unbalanced(dust.into_credit()); + } + fn write_balance( + who: &T::AccountId, + amount: Self::Balance, + ) -> Result, DispatchError> { + let max_reduction = + >::reducible_balance(who, Expendable, Force); + let (result, maybe_dust) = Self::mutate_account(who, |account| -> DispatchResult { + // Make sure the reduction (if there is one) is no more than the maximum allowed. + let reduction = account.free.saturating_sub(amount); + ensure!(reduction <= max_reduction, Error::::InsufficientBalance); + + account.free = amount; + Ok(()) + })?; + result?; + Ok(maybe_dust) + } + + fn set_total_issuance(amount: Self::Balance) { + TotalIssuance::::mutate(|t| *t = amount); + } + + fn deactivate(amount: Self::Balance) { + InactiveIssuance::::mutate(|b| b.saturating_accrue(amount)); + } + + fn reactivate(amount: Self::Balance) { + InactiveIssuance::::mutate(|b| b.saturating_reduce(amount)); + } +} + +impl, I: 'static> fungible::Mutate for Pallet { + fn done_mint_into(who: &T::AccountId, amount: Self::Balance) { + Self::deposit_event(Event::::Minted { who: who.clone(), amount }); + } + fn done_burn_from(who: &T::AccountId, amount: Self::Balance) { + Self::deposit_event(Event::::Burned { who: who.clone(), amount }); + } + fn done_shelve(who: &T::AccountId, amount: Self::Balance) { + Self::deposit_event(Event::::Suspended { who: who.clone(), amount }); + } + fn done_restore(who: &T::AccountId, amount: Self::Balance) { + Self::deposit_event(Event::::Restored { who: who.clone(), amount }); + } + fn done_transfer(source: &T::AccountId, dest: &T::AccountId, amount: Self::Balance) { + Self::deposit_event(Event::::Transfer { + from: source.clone(), + to: dest.clone(), + amount, + }); + } +} + +impl, I: 'static> fungible::MutateHold for Pallet {} + +impl, I: 'static> fungible::InspectHold for Pallet { + type Reason = T::HoldIdentifier; + + fn total_balance_on_hold(who: &T::AccountId) -> T::Balance { + Self::account(who).reserved + } + fn reducible_total_balance_on_hold(who: &T::AccountId, force: Fortitude) -> Self::Balance { + // The total balance must never drop below the freeze requirements if we're not forcing: + let a = Self::account(who); + let unavailable = if force == Force { + Self::Balance::zero() + } else { + // The freeze lock applies to the total balance, so we can discount the free balance + // from the amount which the total reserved balance must provide to satisfy it. + a.frozen.saturating_sub(a.free) + }; + a.reserved.saturating_sub(unavailable) + } + fn balance_on_hold(reason: &Self::Reason, who: &T::AccountId) -> T::Balance { + Holds::::get(who) + .iter() + .find(|x| &x.id == reason) + .map_or_else(Zero::zero, |x| x.amount) + } + fn hold_available(reason: &Self::Reason, who: &T::AccountId) -> bool { + if frame_system::Pallet::::providers(who) == 0 { + return false + } + let holds = Holds::::get(who); + if holds.is_full() && !holds.iter().any(|x| &x.id == reason) { + return false + } + true + } +} + +impl, I: 'static> fungible::UnbalancedHold for Pallet { + fn set_balance_on_hold( + reason: &Self::Reason, + who: &T::AccountId, + amount: Self::Balance, + ) -> DispatchResult { + let mut new_account = Self::account(who); + let mut holds = Holds::::get(who); + let mut increase = true; + let mut delta = amount; + + if let Some(item) = holds.iter_mut().find(|x| &x.id == reason) { + delta = item.amount.max(amount) - item.amount.min(amount); + increase = amount > item.amount; + item.amount = amount; + holds.retain(|x| !x.amount.is_zero()); + } else { + if !amount.is_zero() { + holds + .try_push(IdAmount { id: *reason, amount }) + .map_err(|_| Error::::TooManyHolds)?; + } + } + + new_account.reserved = if increase { + new_account.reserved.checked_add(&delta).ok_or(ArithmeticError::Overflow)? + } else { + new_account.reserved.checked_sub(&delta).ok_or(ArithmeticError::Underflow)? + }; + + let (result, maybe_dust) = Self::try_mutate_account(who, |a, _| -> DispatchResult { + *a = new_account; + Ok(()) + })?; + debug_assert!( + maybe_dust.is_none(), + "Does not alter main balance; dust only happens when it is altered; qed" + ); + Holds::::insert(who, holds); + Ok(result) + } +} + +impl, I: 'static> fungible::InspectFreeze for Pallet { + type Id = T::FreezeIdentifier; + + fn balance_frozen(id: &Self::Id, who: &T::AccountId) -> Self::Balance { + let locks = Freezes::::get(who); + locks.into_iter().find(|l| &l.id == id).map_or(Zero::zero(), |l| l.amount) + } + + fn can_freeze(id: &Self::Id, who: &T::AccountId) -> bool { + let l = Freezes::::get(who); + !l.is_full() || l.iter().any(|x| &x.id == id) + } +} + +impl, I: 'static> fungible::MutateFreeze for Pallet { + fn set_freeze(id: &Self::Id, who: &T::AccountId, amount: Self::Balance) -> DispatchResult { + if amount.is_zero() { + return Self::thaw(id, who) + } + let mut locks = Freezes::::get(who); + if let Some(i) = locks.iter_mut().find(|x| &x.id == id) { + i.amount = amount; + } else { + locks + .try_push(IdAmount { id: *id, amount }) + .map_err(|_| Error::::TooManyFreezes)?; + } + Self::update_freezes(who, locks.as_bounded_slice()) + } + + fn extend_freeze(id: &Self::Id, who: &T::AccountId, amount: Self::Balance) -> DispatchResult { + if amount.is_zero() { + return Ok(()) + } + let mut locks = Freezes::::get(who); + if let Some(i) = locks.iter_mut().find(|x| &x.id == id) { + i.amount = i.amount.max(amount); + } else { + locks + .try_push(IdAmount { id: *id, amount }) + .map_err(|_| Error::::TooManyFreezes)?; + } + Self::update_freezes(who, locks.as_bounded_slice()) + } + + fn thaw(id: &Self::Id, who: &T::AccountId) -> DispatchResult { + let mut locks = Freezes::::get(who); + locks.retain(|l| &l.id != id); + Self::update_freezes(who, locks.as_bounded_slice()) + } +} + +impl, I: 'static> fungible::Balanced for Pallet { + type OnDropCredit = fungible::DecreaseIssuance; + type OnDropDebt = fungible::IncreaseIssuance; + + fn done_deposit(who: &T::AccountId, amount: Self::Balance) { + Self::deposit_event(Event::::Deposit { who: who.clone(), amount }); + } + fn done_withdraw(who: &T::AccountId, amount: Self::Balance) { + Self::deposit_event(Event::::Withdraw { who: who.clone(), amount }); + } + fn done_issue(amount: Self::Balance) { + Self::deposit_event(Event::::Issued { amount }); + } + fn done_rescind(amount: Self::Balance) { + Self::deposit_event(Event::::Rescinded { amount }); + } +} + +impl, I: 'static> fungible::BalancedHold for Pallet {} diff --git a/frame/balances/src/lib.rs b/frame/balances/src/lib.rs index ecd7e171e..ca8e86ef2 100644 --- a/frame/balances/src/lib.rs +++ b/frame/balances/src/lib.rs @@ -89,8 +89,9 @@ //! //! ### Dispatchable Functions //! -//! - `transfer` - Transfer some liquid free balance to another account. -//! - `set_balance` - Set the balances of a given account. The origin of this call must be root. +//! - `transfer_allow_death` - Transfer some liquid free balance to another account. +//! - `force_set_balance` - Set the balances of a given account. The origin of this call must be +//! root. //! //! ## Usage //! @@ -152,43 +153,43 @@ //! * Total issued balanced of all accounts should be less than `Config::Balance::max_value()`. #![cfg_attr(not(feature = "std"), no_std)] - -#[macro_use] -mod tests; mod benchmarking; +mod impl_currency; +mod impl_fungible; pub mod migration; -mod tests_composite; -mod tests_local; -#[cfg(test)] -mod tests_reentrancy; +mod tests; +mod types; pub mod weights; -pub use self::imbalances::{NegativeImbalance, PositiveImbalance}; -use codec::{Codec, Decode, Encode, MaxEncodedLen}; +use codec::{Codec, MaxEncodedLen}; #[cfg(feature = "std")] use frame_support::traits::GenesisBuild; use frame_support::{ ensure, pallet_prelude::DispatchResult, traits::{ - tokens::{fungible, BalanceStatus as Status, DepositConsequence, WithdrawConsequence}, - Currency, DefensiveSaturating, ExistenceRequirement, - ExistenceRequirement::{AllowDeath, KeepAlive}, - Get, Imbalance, LockIdentifier, LockableCurrency, NamedReservableCurrency, OnUnbalanced, - ReservableCurrency, SignedImbalance, StoredMap, TryDrop, WithdrawReasons, + tokens::{ + fungible, BalanceStatus as Status, DepositConsequence, + Fortitude::{self, Force, Polite}, + Preservation::{Expendable, Preserve, Protect}, + WithdrawConsequence, + }, + Currency, Defensive, Get, OnUnbalanced, ReservableCurrency, StoredMap, }, - WeakBoundedVec, + BoundedSlice, WeakBoundedVec, }; use frame_system as system; +pub use impl_currency::{NegativeImbalance, PositiveImbalance}; use scale_info::TypeInfo; use sp_runtime::{ traits::{ AtLeast32BitUnsigned, Bounded, CheckedAdd, CheckedSub, MaybeSerializeDeserialize, Saturating, StaticLookup, Zero, }, - ArithmeticError, DispatchError, FixedPointOperand, RuntimeDebug, + ArithmeticError, DispatchError, FixedPointOperand, Perbill, RuntimeDebug, TokenError, }; -use sp_std::{cmp, fmt::Debug, mem, ops::BitOr, prelude::*, result}; +use sp_std::{cmp, fmt::Debug, mem, prelude::*, result}; +pub use types::{AccountData, BalanceLock, DustCleaner, IdAmount, Reasons, ReserveData}; pub use weights::WeightInfo; pub use pallet::*; @@ -200,11 +201,20 @@ type AccountIdLookupOf = <::Lookup as StaticLookup #[frame_support::pallet] pub mod pallet { use super::*; - use frame_support::pallet_prelude::*; + use frame_support::{pallet_prelude::*, traits::fungible::Credit}; use frame_system::pallet_prelude::*; + pub type CreditOf = Credit<::AccountId, Pallet>; + #[pallet::config] pub trait Config: frame_system::Config { + /// The overarching event type. + type RuntimeEvent: From> + + IsType<::RuntimeEvent>; + + /// Weight information for extrinsics in this pallet. + type WeightInfo: WeightInfo; + /// The balance of an account. type Balance: Parameter + Member @@ -219,11 +229,7 @@ pub mod pallet { + FixedPointOperand; /// Handler for the unbalanced reduction when removing a dust account. - type DustRemoval: OnUnbalanced>; - - /// The overarching event type. - type RuntimeEvent: From> - + IsType<::RuntimeEvent>; + type DustRemoval: OnUnbalanced>; /// The minimum amount required to keep an account open. #[pallet::constant] @@ -232,8 +238,16 @@ pub mod pallet { /// The means of storing the balances of an account. type AccountStore: StoredMap>; - /// Weight information for extrinsics in this pallet. - type WeightInfo: WeightInfo; + /// The ID type for reserves. + /// + /// Use of reserves is deprecated in favour of holds. See `https://github.com/paritytech/substrate/pull/12951/` + type ReserveIdentifier: Parameter + Member + MaxEncodedLen + Ord + Copy; + + /// The ID type for holds. + type HoldIdentifier: Parameter + Member + MaxEncodedLen + Ord + Copy; + + /// The ID type for freezes. + type FreezeIdentifier: Parameter + Member + MaxEncodedLen + Ord + Copy; /// The maximum number of locks that should exist on an account. /// Not strictly enforced, but used for weight estimation. @@ -244,8 +258,13 @@ pub mod pallet { #[pallet::constant] type MaxReserves: Get; - /// The id type for named reserves. - type ReserveIdentifier: Parameter + Member + MaxEncodedLen + Ord + Copy; + /// The maximum number of holds that can exist on an account at any time. + #[pallet::constant] + type MaxHolds: Get; + + /// The maximum number of individual freeze locks that can exist on an account at any time. + #[pallet::constant] + type MaxFreezes: Get; } /// The current storage version. @@ -256,197 +275,6 @@ pub mod pallet { #[pallet::storage_version(STORAGE_VERSION)] pub struct Pallet(PhantomData<(T, I)>); - #[pallet::call] - impl, I: 'static> Pallet { - /// Transfer some liquid free balance to another account. - /// - /// `transfer` will set the `FreeBalance` of the sender and receiver. - /// If the sender's account is below the existential deposit as a result - /// of the transfer, the account will be reaped. - /// - /// The dispatch origin for this call must be `Signed` by the transactor. - /// - /// ## Complexity - /// - Dependent on arguments but not critical, given proper implementations for input config - /// types. See related functions below. - /// - It contains a limited number of reads and writes internally and no complex - /// computation. - /// - /// Related functions: - /// - /// - `ensure_can_withdraw` is always called internally but has a bounded complexity. - /// - Transferring balances to accounts that did not exist before will cause - /// `T::OnNewAccount::on_new_account` to be called. - /// - Removing enough funds from an account will trigger `T::DustRemoval::on_unbalanced`. - /// - `transfer_keep_alive` works the same way as `transfer`, but has an additional check - /// that the transfer will not kill the origin account. - #[pallet::call_index(0)] - #[pallet::weight(T::WeightInfo::transfer())] - pub fn transfer( - origin: OriginFor, - dest: AccountIdLookupOf, - #[pallet::compact] value: T::Balance, - ) -> DispatchResultWithPostInfo { - let transactor = ensure_signed(origin)?; - let dest = T::Lookup::lookup(dest)?; - >::transfer( - &transactor, - &dest, - value, - ExistenceRequirement::AllowDeath, - )?; - Ok(().into()) - } - - /// Set the balances of a given account. - /// - /// This will alter `FreeBalance` and `ReservedBalance` in storage. it will - /// also alter the total issuance of the system (`TotalIssuance`) appropriately. - /// If the new free or reserved balance is below the existential deposit, - /// it will reset the account nonce (`frame_system::AccountNonce`). - /// - /// The dispatch origin for this call is `root`. - #[pallet::call_index(1)] - #[pallet::weight( - T::WeightInfo::set_balance_creating() // Creates a new account. - .max(T::WeightInfo::set_balance_killing()) // Kills an existing account. - )] - pub fn set_balance( - origin: OriginFor, - who: AccountIdLookupOf, - #[pallet::compact] new_free: T::Balance, - #[pallet::compact] new_reserved: T::Balance, - ) -> DispatchResultWithPostInfo { - ensure_root(origin)?; - let who = T::Lookup::lookup(who)?; - let existential_deposit = T::ExistentialDeposit::get(); - - let wipeout = new_free + new_reserved < existential_deposit; - let new_free = if wipeout { Zero::zero() } else { new_free }; - let new_reserved = if wipeout { Zero::zero() } else { new_reserved }; - - // First we try to modify the account's balance to the forced balance. - let (old_free, old_reserved) = Self::mutate_account(&who, |account| { - let old_free = account.free; - let old_reserved = account.reserved; - - account.free = new_free; - account.reserved = new_reserved; - - (old_free, old_reserved) - })?; - - // This will adjust the total issuance, which was not done by the `mutate_account` - // above. - if new_free > old_free { - mem::drop(PositiveImbalance::::new(new_free - old_free)); - } else if new_free < old_free { - mem::drop(NegativeImbalance::::new(old_free - new_free)); - } - - if new_reserved > old_reserved { - mem::drop(PositiveImbalance::::new(new_reserved - old_reserved)); - } else if new_reserved < old_reserved { - mem::drop(NegativeImbalance::::new(old_reserved - new_reserved)); - } - - Self::deposit_event(Event::BalanceSet { who, free: new_free, reserved: new_reserved }); - Ok(().into()) - } - - /// Exactly as `transfer`, except the origin must be root and the source account may be - /// specified. - /// ## Complexity - /// - Same as transfer, but additional read and write because the source account is not - /// assumed to be in the overlay. - #[pallet::call_index(2)] - #[pallet::weight(T::WeightInfo::force_transfer())] - pub fn force_transfer( - origin: OriginFor, - source: AccountIdLookupOf, - dest: AccountIdLookupOf, - #[pallet::compact] value: T::Balance, - ) -> DispatchResultWithPostInfo { - ensure_root(origin)?; - let source = T::Lookup::lookup(source)?; - let dest = T::Lookup::lookup(dest)?; - >::transfer( - &source, - &dest, - value, - ExistenceRequirement::AllowDeath, - )?; - Ok(().into()) - } - - /// Same as the [`transfer`] call, but with a check that the transfer will not kill the - /// origin account. - /// - /// 99% of the time you want [`transfer`] instead. - /// - /// [`transfer`]: struct.Pallet.html#method.transfer - #[pallet::call_index(3)] - #[pallet::weight(T::WeightInfo::transfer_keep_alive())] - pub fn transfer_keep_alive( - origin: OriginFor, - dest: AccountIdLookupOf, - #[pallet::compact] value: T::Balance, - ) -> DispatchResultWithPostInfo { - let transactor = ensure_signed(origin)?; - let dest = T::Lookup::lookup(dest)?; - >::transfer(&transactor, &dest, value, KeepAlive)?; - Ok(().into()) - } - - /// Transfer the entire transferable balance from the caller account. - /// - /// NOTE: This function only attempts to transfer _transferable_ balances. This means that - /// any locked, reserved, or existential deposits (when `keep_alive` is `true`), will not be - /// transferred by this function. To ensure that this function results in a killed account, - /// you might need to prepare the account by removing any reference counters, storage - /// deposits, etc... - /// - /// The dispatch origin of this call must be Signed. - /// - /// - `dest`: The recipient of the transfer. - /// - `keep_alive`: A boolean to determine if the `transfer_all` operation should send all - /// of the funds the account has, causing the sender account to be killed (false), or - /// transfer everything except at least the existential deposit, which will guarantee to - /// keep the sender account alive (true). ## Complexity - /// - O(1). Just like transfer, but reading the user's transferable balance first. - #[pallet::call_index(4)] - #[pallet::weight(T::WeightInfo::transfer_all())] - pub fn transfer_all( - origin: OriginFor, - dest: AccountIdLookupOf, - keep_alive: bool, - ) -> DispatchResult { - use fungible::Inspect; - let transactor = ensure_signed(origin)?; - let reducible_balance = Self::reducible_balance(&transactor, keep_alive); - let dest = T::Lookup::lookup(dest)?; - let keep_alive = if keep_alive { KeepAlive } else { AllowDeath }; - >::transfer(&transactor, &dest, reducible_balance, keep_alive)?; - Ok(()) - } - - /// Unreserve some balance from a user by force. - /// - /// Can only be called by ROOT. - #[pallet::call_index(5)] - #[pallet::weight(T::WeightInfo::force_unreserve())] - pub fn force_unreserve( - origin: OriginFor, - who: AccountIdLookupOf, - amount: T::Balance, - ) -> DispatchResult { - ensure_root(origin)?; - let who = T::Lookup::lookup(who)?; - let _leftover = >::unreserve(&who, amount); - Ok(()) - } - } - #[pallet::event] #[pallet::generate_deposit(pub(super) fn deposit_event)] pub enum Event, I: 'static = ()> { @@ -458,7 +286,7 @@ pub mod pallet { /// Transfer succeeded. Transfer { from: T::AccountId, to: T::AccountId, amount: T::Balance }, /// A balance was set by root. - BalanceSet { who: T::AccountId, free: T::Balance, reserved: T::Balance }, + BalanceSet { who: T::AccountId, free: T::Balance }, /// Some balance was reserved (moved from free to reserved). Reserved { who: T::AccountId, amount: T::Balance }, /// Some balance was unreserved (moved from reserved to free). @@ -477,26 +305,44 @@ pub mod pallet { Withdraw { who: T::AccountId, amount: T::Balance }, /// Some amount was removed from the account (e.g. for misbehavior). Slashed { who: T::AccountId, amount: T::Balance }, + /// Some amount was minted into an account. + Minted { who: T::AccountId, amount: T::Balance }, + /// Some amount was burned from an account. + Burned { who: T::AccountId, amount: T::Balance }, + /// Some amount was suspended from an account (it can be restored later). + Suspended { who: T::AccountId, amount: T::Balance }, + /// Some amount was restored into an account. + Restored { who: T::AccountId, amount: T::Balance }, + /// An account was upgraded. + Upgraded { who: T::AccountId }, + /// Total issuance was increased by `amount`, creating a credit to be balanced. + Issued { amount: T::Balance }, + /// Total issuance was decreased by `amount`, creating a debt to be balanced. + Rescinded { amount: T::Balance }, } #[pallet::error] pub enum Error { - /// Vesting balance too high to send value + /// Vesting balance too high to send value. VestingBalance, - /// Account liquidity restrictions prevent withdrawal + /// Account liquidity restrictions prevent withdrawal. LiquidityRestrictions, /// Balance too low to send value. InsufficientBalance, - /// Value too low to create account due to existential deposit + /// Value too low to create account due to existential deposit. ExistentialDeposit, - /// Transfer/payment would kill account - KeepAlive, - /// A vesting schedule already exists for this account + /// Transfer/payment would kill account. + Expendability, + /// A vesting schedule already exists for this account. ExistingVestingSchedule, - /// Beneficiary account must pre-exist + /// Beneficiary account must pre-exist. DeadAccount, - /// Number of named reserves exceed MaxReserves + /// Number of named reserves exceed `MaxReserves`. TooManyReserves, + /// Number of holds exceed `MaxHolds`. + TooManyHolds, + /// Number of freezes exceed `MaxFreezes`. + TooManyFreezes, } /// The total units issued in the system. @@ -563,6 +409,26 @@ pub mod pallet { ValueQuery, >; + /// Holds on account balances. + #[pallet::storage] + pub type Holds, I: 'static = ()> = StorageMap< + _, + Blake2_128Concat, + T::AccountId, + BoundedVec, T::MaxHolds>, + ValueQuery, + >; + + /// Freeze locks on account balances. + #[pallet::storage] + pub type Freezes, I: 'static = ()> = StorageMap< + _, + Blake2_128Concat, + T::AccountId, + BoundedVec, T::MaxFreezes>, + ValueQuery, + >; + #[pallet::genesis_config] pub struct GenesisConfig, I: 'static = ()> { pub balances: Vec<(T::AccountId, T::Balance)>, @@ -602,1601 +468,646 @@ pub mod pallet { ); for &(ref who, free) in self.balances.iter() { + frame_system::Pallet::::inc_providers(who); assert!(T::AccountStore::insert(who, AccountData { free, ..Default::default() }) .is_ok()); } } } -} - -#[cfg(feature = "std")] -impl, I: 'static> GenesisConfig { - /// Direct implementation of `GenesisBuild::build_storage`. - /// - /// Kept in order not to break dependency. - pub fn build_storage(&self) -> Result { - >::build_storage(self) - } - - /// Direct implementation of `GenesisBuild::assimilate_storage`. - /// - /// Kept in order not to break dependency. - pub fn assimilate_storage(&self, storage: &mut sp_runtime::Storage) -> Result<(), String> { - >::assimilate_storage(self, storage) - } -} - -/// Simplified reasons for withdrawing balance. -#[derive(Encode, Decode, Clone, Copy, PartialEq, Eq, RuntimeDebug, MaxEncodedLen, TypeInfo)] -pub enum Reasons { - /// Paying system transaction fees. - Fee = 0, - /// Any reason other than paying system transaction fees. - Misc = 1, - /// Any reason at all. - All = 2, -} - -impl From for Reasons { - fn from(r: WithdrawReasons) -> Reasons { - if r == WithdrawReasons::TRANSACTION_PAYMENT { - Reasons::Fee - } else if r.contains(WithdrawReasons::TRANSACTION_PAYMENT) { - Reasons::All - } else { - Reasons::Misc - } - } -} -impl BitOr for Reasons { - type Output = Reasons; - fn bitor(self, other: Reasons) -> Reasons { - if self == other { - return self + #[cfg(feature = "std")] + impl, I: 'static> GenesisConfig { + /// Direct implementation of `GenesisBuild::build_storage`. + /// + /// Kept in order not to break dependency. + pub fn build_storage(&self) -> Result { + >::build_storage(self) } - Reasons::All - } -} - -/// A single lock on a balance. There can be many of these on an account and they "overlap", so the -/// same balance is frozen by multiple locks. -#[derive(Encode, Decode, Clone, PartialEq, Eq, RuntimeDebug, MaxEncodedLen, TypeInfo)] -pub struct BalanceLock { - /// An identifier for this lock. Only one lock may be in existence for each identifier. - pub id: LockIdentifier, - /// The amount which the free balance may not drop below when this lock is in effect. - pub amount: Balance, - /// If true, then the lock remains in effect even for payment of transaction fees. - pub reasons: Reasons, -} - -/// Store named reserved balance. -#[derive(Encode, Decode, Clone, PartialEq, Eq, RuntimeDebug, MaxEncodedLen, TypeInfo)] -pub struct ReserveData { - /// The identifier for the named reserve. - pub id: ReserveIdentifier, - /// The amount of the named reserve. - pub amount: Balance, -} - -/// All balance information for an account. -#[derive(Encode, Decode, Clone, PartialEq, Eq, Default, RuntimeDebug, MaxEncodedLen, TypeInfo)] -pub struct AccountData { - /// Non-reserved part of the balance. There may still be restrictions on this, but it is the - /// total pool what may in principle be transferred, reserved and used for tipping. - /// - /// This is the only balance that matters in terms of most operations on tokens. It - /// alone is used to determine the balance when in the contract execution environment. - pub free: Balance, - /// Balance which is reserved and may not be used at all. - /// - /// This can still get slashed, but gets slashed last of all. - /// - /// This balance is a 'reserve' balance that other subsystems use in order to set aside tokens - /// that are still 'owned' by the account holder, but which are suspendable. - /// This includes named reserve and unnamed reserve. - pub reserved: Balance, - /// The amount that `free` may not drop below when withdrawing for *anything except transaction - /// fee payment*. - pub misc_frozen: Balance, - /// The amount that `free` may not drop below when withdrawing specifically for transaction - /// fee payment. - pub fee_frozen: Balance, -} -impl AccountData { - /// How much this account's balance can be reduced for the given `reasons`. - fn usable(&self, reasons: Reasons) -> Balance { - self.free.saturating_sub(self.frozen(reasons)) - } - /// The amount that this account's free balance may not be reduced beyond for the given - /// `reasons`. - fn frozen(&self, reasons: Reasons) -> Balance { - match reasons { - Reasons::All => self.misc_frozen.max(self.fee_frozen), - Reasons::Misc => self.misc_frozen, - Reasons::Fee => self.fee_frozen, + /// Direct implementation of `GenesisBuild::assimilate_storage`. + /// + /// Kept in order not to break dependency. + pub fn assimilate_storage(&self, storage: &mut sp_runtime::Storage) -> Result<(), String> { + >::assimilate_storage(self, storage) } } - /// The total balance in this account including any that is reserved and ignoring any frozen. - fn total(&self) -> Balance { - self.free.saturating_add(self.reserved) - } -} -pub struct DustCleaner, I: 'static = ()>( - Option<(T::AccountId, NegativeImbalance)>, -); - -impl, I: 'static> Drop for DustCleaner { - fn drop(&mut self) { - if let Some((who, dust)) = self.0.take() { - Pallet::::deposit_event(Event::DustLost { account: who, amount: dust.peek() }); - T::DustRemoval::on_unbalanced(dust); + #[pallet::call] + impl, I: 'static> Pallet { + /// Transfer some liquid free balance to another account. + /// + /// `transfer_allow_death` will set the `FreeBalance` of the sender and receiver. + /// If the sender's account is below the existential deposit as a result + /// of the transfer, the account will be reaped. + /// + /// The dispatch origin for this call must be `Signed` by the transactor. + #[pallet::call_index(0)] + #[pallet::weight(T::WeightInfo::transfer_allow_death())] + pub fn transfer_allow_death( + origin: OriginFor, + dest: AccountIdLookupOf, + #[pallet::compact] value: T::Balance, + ) -> DispatchResultWithPostInfo { + let source = ensure_signed(origin)?; + let dest = T::Lookup::lookup(dest)?; + >::transfer(&source, &dest, value, Expendable)?; + Ok(().into()) } - } -} -impl, I: 'static> Pallet { - /// Get the free balance of an account. - pub fn free_balance(who: impl sp_std::borrow::Borrow) -> T::Balance { - Self::account(who.borrow()).free - } + /// Set the regular balance of a given account; it also takes a reserved balance but this + /// must be the same as the account's current reserved balance. + /// + /// The dispatch origin for this call is `root`. + /// + /// WARNING: This call is DEPRECATED! Use `force_set_balance` instead. + #[pallet::call_index(1)] + #[pallet::weight( + T::WeightInfo::force_set_balance_creating() // Creates a new account. + .max(T::WeightInfo::force_set_balance_killing()) // Kills an existing account. + )] + pub fn set_balance_deprecated( + origin: OriginFor, + who: AccountIdLookupOf, + #[pallet::compact] new_free: T::Balance, + #[pallet::compact] old_reserved: T::Balance, + ) -> DispatchResultWithPostInfo { + ensure_root(origin)?; + let who = T::Lookup::lookup(who)?; + let existential_deposit = T::ExistentialDeposit::get(); - /// Get the balance of an account that can be used for transfers, reservations, or any other - /// non-locking, non-transaction-fee activity. Will be at most `free_balance`. - pub fn usable_balance(who: impl sp_std::borrow::Borrow) -> T::Balance { - Self::account(who.borrow()).usable(Reasons::Misc) - } + let wipeout = new_free < existential_deposit; + let new_free = if wipeout { Zero::zero() } else { new_free }; - /// Get the balance of an account that can be used for paying transaction fees (not tipping, - /// or any other kind of fees, though). Will be at most `free_balance`. - pub fn usable_balance_for_fees(who: impl sp_std::borrow::Borrow) -> T::Balance { - Self::account(who.borrow()).usable(Reasons::Fee) - } + // First we try to modify the account's balance to the forced balance. + let old_free = Self::try_mutate_account_handling_dust( + &who, + |account, _is_new| -> Result { + let old_free = account.free; + ensure!(account.reserved == old_reserved, TokenError::Unsupported); + account.free = new_free; + Ok(old_free) + }, + )?; - /// Get the reserved balance of an account. - pub fn reserved_balance(who: impl sp_std::borrow::Borrow) -> T::Balance { - Self::account(who.borrow()).reserved - } + // This will adjust the total issuance, which was not done by the `mutate_account` + // above. + if new_free > old_free { + mem::drop(PositiveImbalance::::new(new_free - old_free)); + } else if new_free < old_free { + mem::drop(NegativeImbalance::::new(old_free - new_free)); + } - /// Get both the free and reserved balances of an account. - fn account(who: &T::AccountId) -> AccountData { - T::AccountStore::get(who) - } + Self::deposit_event(Event::BalanceSet { who, free: new_free }); + Ok(().into()) + } - /// Handles any steps needed after mutating an account. - /// - /// This includes DustRemoval unbalancing, in the case than the `new` account's total balance - /// is non-zero but below ED. - /// - /// Returns two values: - /// - `Some` containing the the `new` account, iff the account has sufficient balance. - /// - `Some` containing the dust to be dropped, iff some dust should be dropped. - fn post_mutation( - _who: &T::AccountId, - new: AccountData, - ) -> (Option>, Option>) { - let total = new.total(); - if total < T::ExistentialDeposit::get() { - if total.is_zero() { - (None, None) - } else { - (None, Some(NegativeImbalance::new(total))) - } - } else { - (Some(new), None) - } - } - - fn deposit_consequence( - _who: &T::AccountId, - amount: T::Balance, - account: &AccountData, - mint: bool, - ) -> DepositConsequence { - if amount.is_zero() { - return DepositConsequence::Success - } - - if mint && TotalIssuance::::get().checked_add(&amount).is_none() { - return DepositConsequence::Overflow - } - - let new_total_balance = match account.total().checked_add(&amount) { - Some(x) => x, - None => return DepositConsequence::Overflow, - }; - - if new_total_balance < T::ExistentialDeposit::get() { - return DepositConsequence::BelowMinimum - } - - // NOTE: We assume that we are a provider, so don't need to do any checks in the - // case of account creation. - - DepositConsequence::Success - } - - fn withdraw_consequence( - who: &T::AccountId, - amount: T::Balance, - account: &AccountData, - ) -> WithdrawConsequence { - if amount.is_zero() { - return WithdrawConsequence::Success - } - - if TotalIssuance::::get().checked_sub(&amount).is_none() { - return WithdrawConsequence::Underflow + /// Exactly as `transfer_allow_death`, except the origin must be root and the source account + /// may be specified. + #[pallet::call_index(2)] + #[pallet::weight(T::WeightInfo::force_transfer())] + pub fn force_transfer( + origin: OriginFor, + source: AccountIdLookupOf, + dest: AccountIdLookupOf, + #[pallet::compact] value: T::Balance, + ) -> DispatchResultWithPostInfo { + ensure_root(origin)?; + let source = T::Lookup::lookup(source)?; + let dest = T::Lookup::lookup(dest)?; + >::transfer(&source, &dest, value, Expendable)?; + Ok(().into()) } - let new_total_balance = match account.total().checked_sub(&amount) { - Some(x) => x, - None => return WithdrawConsequence::NoFunds, - }; - - // Provider restriction - total account balance cannot be reduced to zero if it cannot - // sustain the loss of a provider reference. - // NOTE: This assumes that the pallet is a provider (which is true). Is this ever changes, - // then this will need to adapt accordingly. - let ed = T::ExistentialDeposit::get(); - let success = if new_total_balance < ed { - if frame_system::Pallet::::can_dec_provider(who) { - WithdrawConsequence::ReducedToZero(new_total_balance) - } else { - return WithdrawConsequence::WouldDie - } - } else { - WithdrawConsequence::Success - }; - - // Enough free funds to have them be reduced. - let new_free_balance = match account.free.checked_sub(&amount) { - Some(b) => b, - None => return WithdrawConsequence::NoFunds, - }; - - // Eventual free funds must be no less than the frozen balance. - let min_balance = account.frozen(Reasons::All); - if new_free_balance < min_balance { - return WithdrawConsequence::Frozen + /// Same as the [`transfer_allow_death`] call, but with a check that the transfer will not + /// kill the origin account. + /// + /// 99% of the time you want [`transfer_allow_death`] instead. + /// + /// [`transfer_allow_death`]: struct.Pallet.html#method.transfer + #[pallet::call_index(3)] + #[pallet::weight(T::WeightInfo::transfer_keep_alive())] + pub fn transfer_keep_alive( + origin: OriginFor, + dest: AccountIdLookupOf, + #[pallet::compact] value: T::Balance, + ) -> DispatchResultWithPostInfo { + let source = ensure_signed(origin)?; + let dest = T::Lookup::lookup(dest)?; + >::transfer(&source, &dest, value, Preserve)?; + Ok(().into()) } - success - } - - /// Mutate an account to some new value, or delete it entirely with `None`. Will enforce - /// `ExistentialDeposit` law, annulling the account as needed. - /// - /// NOTE: Doesn't do any preparatory work for creating a new account, so should only be used - /// when it is known that the account already exists. - /// - /// NOTE: LOW-LEVEL: This will not attempt to maintain total issuance. It is expected that - /// the caller will do this. - pub fn mutate_account( - who: &T::AccountId, - f: impl FnOnce(&mut AccountData) -> R, - ) -> Result { - Self::try_mutate_account(who, |a, _| -> Result { Ok(f(a)) }) - } - - /// Mutate an account to some new value, or delete it entirely with `None`. Will enforce - /// `ExistentialDeposit` law, annulling the account as needed. This will do nothing if the - /// result of `f` is an `Err`. - /// - /// NOTE: Doesn't do any preparatory work for creating a new account, so should only be used - /// when it is known that the account already exists. - /// - /// NOTE: LOW-LEVEL: This will not attempt to maintain total issuance. It is expected that - /// the caller will do this. - fn try_mutate_account>( - who: &T::AccountId, - f: impl FnOnce(&mut AccountData, bool) -> Result, - ) -> Result { - Self::try_mutate_account_with_dust(who, f).map(|(result, dust_cleaner)| { - drop(dust_cleaner); - result - }) - } - - /// Mutate an account to some new value, or delete it entirely with `None`. Will enforce - /// `ExistentialDeposit` law, annulling the account as needed. This will do nothing if the - /// result of `f` is an `Err`. - /// - /// It returns both the result from the closure, and an optional `DustCleaner` instance which - /// should be dropped once it is known that all nested mutates that could affect storage items - /// what the dust handler touches have completed. - /// - /// NOTE: Doesn't do any preparatory work for creating a new account, so should only be used - /// when it is known that the account already exists. - /// - /// NOTE: LOW-LEVEL: This will not attempt to maintain total issuance. It is expected that - /// the caller will do this. - fn try_mutate_account_with_dust>( - who: &T::AccountId, - f: impl FnOnce(&mut AccountData, bool) -> Result, - ) -> Result<(R, DustCleaner), E> { - let result = T::AccountStore::try_mutate_exists(who, |maybe_account| { - let is_new = maybe_account.is_none(); - let mut account = maybe_account.take().unwrap_or_default(); - f(&mut account, is_new).map(move |result| { - let maybe_endowed = if is_new { Some(account.free) } else { None }; - let maybe_account_maybe_dust = Self::post_mutation(who, account); - *maybe_account = maybe_account_maybe_dust.0; - (maybe_endowed, maybe_account_maybe_dust.1, result) - }) - }); - result.map(|(maybe_endowed, maybe_dust, result)| { - if let Some(endowed) = maybe_endowed { - Self::deposit_event(Event::Endowed { account: who.clone(), free_balance: endowed }); - } - let dust_cleaner = DustCleaner(maybe_dust.map(|dust| (who.clone(), dust))); - (result, dust_cleaner) - }) - } - - /// Update the account entry for `who`, given the locks. - fn update_locks(who: &T::AccountId, locks: &[BalanceLock]) { - let bounded_locks = WeakBoundedVec::<_, T::MaxLocks>::force_from( - locks.to_vec(), - Some("Balances Update Locks"), - ); - - if locks.len() as u32 > T::MaxLocks::get() { - log::warn!( - target: LOG_TARGET, - "Warning: A user has more currency locks than expected. \ - A runtime configuration adjustment may be needed." + /// Transfer the entire transferable balance from the caller account. + /// + /// NOTE: This function only attempts to transfer _transferable_ balances. This means that + /// any locked, reserved, or existential deposits (when `keep_alive` is `true`), will not be + /// transferred by this function. To ensure that this function results in a killed account, + /// you might need to prepare the account by removing any reference counters, storage + /// deposits, etc... + /// + /// The dispatch origin of this call must be Signed. + /// + /// - `dest`: The recipient of the transfer. + /// - `keep_alive`: A boolean to determine if the `transfer_all` operation should send all + /// of the funds the account has, causing the sender account to be killed (false), or + /// transfer everything except at least the existential deposit, which will guarantee to + /// keep the sender account alive (true). + #[pallet::call_index(4)] + #[pallet::weight(T::WeightInfo::transfer_all())] + pub fn transfer_all( + origin: OriginFor, + dest: AccountIdLookupOf, + keep_alive: bool, + ) -> DispatchResult { + let transactor = ensure_signed(origin)?; + let keep_alive = if keep_alive { Preserve } else { Expendable }; + let reducible_balance = >::reducible_balance( + &transactor, + keep_alive, + Fortitude::Polite, ); - } - // No way this can fail since we do not alter the existential balances. - let res = Self::mutate_account(who, |b| { - b.misc_frozen = Zero::zero(); - b.fee_frozen = Zero::zero(); - for l in locks.iter() { - if l.reasons == Reasons::All || l.reasons == Reasons::Misc { - b.misc_frozen = b.misc_frozen.max(l.amount); - } - if l.reasons == Reasons::All || l.reasons == Reasons::Fee { - b.fee_frozen = b.fee_frozen.max(l.amount); - } - } - }); - debug_assert!(res.is_ok()); - - let existed = Locks::::contains_key(who); - if locks.is_empty() { - Locks::::remove(who); - if existed { - // TODO: use Locks::::hashed_key - // https://github.com/paritytech/substrate/issues/4969 - system::Pallet::::dec_consumers(who); - } - } else { - Locks::::insert(who, bounded_locks); - if !existed && system::Pallet::::inc_consumers_without_limit(who).is_err() { - // No providers for the locks. This is impossible under normal circumstances - // since the funds that are under the lock will themselves be stored in the - // account and therefore will need a reference. - log::warn!( - target: LOG_TARGET, - "Warning: Attempt to introduce lock consumer reference, yet no providers. \ - This is unexpected but should be safe." - ); - } - } - } - - /// Move the reserved balance of one account into the balance of another, according to `status`. - /// - /// Is a no-op if: - /// - the value to be moved is zero; or - /// - the `slashed` id equal to `beneficiary` and the `status` is `Reserved`. - /// - /// NOTE: returns actual amount of transferred value in `Ok` case. - fn do_transfer_reserved( - slashed: &T::AccountId, - beneficiary: &T::AccountId, - value: T::Balance, - best_effort: bool, - status: Status, - ) -> Result { - if value.is_zero() { - return Ok(Zero::zero()) - } - - if slashed == beneficiary { - return match status { - Status::Free => Ok(value.saturating_sub(Self::unreserve(slashed, value))), - Status::Reserved => Ok(value.saturating_sub(Self::reserved_balance(slashed))), - } - } - - let ((actual, _maybe_one_dust), _maybe_other_dust) = Self::try_mutate_account_with_dust( - beneficiary, - |to_account, is_new| -> Result<(T::Balance, DustCleaner), DispatchError> { - ensure!(!is_new, Error::::DeadAccount); - Self::try_mutate_account_with_dust( - slashed, - |from_account, _| -> Result { - let actual = cmp::min(from_account.reserved, value); - ensure!(best_effort || actual == value, Error::::InsufficientBalance); - match status { - Status::Free => - to_account.free = to_account - .free - .checked_add(&actual) - .ok_or(ArithmeticError::Overflow)?, - Status::Reserved => - to_account.reserved = to_account - .reserved - .checked_add(&actual) - .ok_or(ArithmeticError::Overflow)?, - } - from_account.reserved -= actual; - Ok(actual) - }, - ) - }, - )?; - - Self::deposit_event(Event::ReserveRepatriated { - from: slashed.clone(), - to: beneficiary.clone(), - amount: actual, - destination_status: status, - }); - Ok(actual) - } -} - -impl, I: 'static> fungible::Inspect for Pallet { - type Balance = T::Balance; - - fn total_issuance() -> Self::Balance { - TotalIssuance::::get() - } - fn active_issuance() -> Self::Balance { - TotalIssuance::::get().saturating_sub(InactiveIssuance::::get()) - } - fn minimum_balance() -> Self::Balance { - T::ExistentialDeposit::get() - } - fn balance(who: &T::AccountId) -> Self::Balance { - Self::account(who).total() - } - fn reducible_balance(who: &T::AccountId, keep_alive: bool) -> Self::Balance { - let a = Self::account(who); - // Liquid balance is what is neither reserved nor locked/frozen. - let liquid = a.free.saturating_sub(a.fee_frozen.max(a.misc_frozen)); - if frame_system::Pallet::::can_dec_provider(who) && !keep_alive { - liquid - } else { - // `must_remain_to_exist` is the part of liquid balance which must remain to keep total - // over ED. - let must_remain_to_exist = - T::ExistentialDeposit::get().saturating_sub(a.total() - liquid); - liquid.saturating_sub(must_remain_to_exist) - } - } - fn can_deposit(who: &T::AccountId, amount: Self::Balance, mint: bool) -> DepositConsequence { - Self::deposit_consequence(who, amount, &Self::account(who), mint) - } - fn can_withdraw( - who: &T::AccountId, - amount: Self::Balance, - ) -> WithdrawConsequence { - Self::withdraw_consequence(who, amount, &Self::account(who)) - } -} - -impl, I: 'static> fungible::Mutate for Pallet { - fn mint_into(who: &T::AccountId, amount: Self::Balance) -> DispatchResult { - if amount.is_zero() { - return Ok(()) - } - Self::try_mutate_account(who, |account, _is_new| -> DispatchResult { - Self::deposit_consequence(who, amount, account, true).into_result()?; - account.free += amount; + let dest = T::Lookup::lookup(dest)?; + >::transfer( + &transactor, + &dest, + reducible_balance, + keep_alive, + )?; Ok(()) - })?; - TotalIssuance::::mutate(|t| *t += amount); - Self::deposit_event(Event::Deposit { who: who.clone(), amount }); - Ok(()) - } - - fn burn_from( - who: &T::AccountId, - amount: Self::Balance, - ) -> Result { - if amount.is_zero() { - return Ok(Self::Balance::zero()) } - let actual = Self::try_mutate_account( - who, - |account, _is_new| -> Result { - let extra = Self::withdraw_consequence(who, amount, account).into_result()?; - let actual = amount + extra; - account.free -= actual; - Ok(actual) - }, - )?; - TotalIssuance::::mutate(|t| *t -= actual); - Self::deposit_event(Event::Withdraw { who: who.clone(), amount }); - Ok(actual) - } -} - -impl, I: 'static> fungible::Transfer for Pallet { - fn transfer( - source: &T::AccountId, - dest: &T::AccountId, - amount: T::Balance, - keep_alive: bool, - ) -> Result { - let er = if keep_alive { KeepAlive } else { AllowDeath }; - >::transfer(source, dest, amount, er).map(|_| amount) - } - - fn deactivate(amount: Self::Balance) { - InactiveIssuance::::mutate(|b| b.saturating_accrue(amount)); - } - - fn reactivate(amount: Self::Balance) { - InactiveIssuance::::mutate(|b| b.saturating_reduce(amount)); - } -} - -impl, I: 'static> fungible::Unbalanced for Pallet { - fn set_balance(who: &T::AccountId, amount: Self::Balance) -> DispatchResult { - Self::mutate_account(who, |account| -> DispatchResult { - // fungibles::Unbalanced::decrease_balance didn't check account.reserved - // free = new_balance - reserved - account.free = - amount.checked_sub(&account.reserved).ok_or(ArithmeticError::Underflow)?; - Self::deposit_event(Event::BalanceSet { - who: who.clone(), - free: account.free, - reserved: account.reserved, - }); + /// Unreserve some balance from a user by force. + /// + /// Can only be called by ROOT. + #[pallet::call_index(5)] + #[pallet::weight(T::WeightInfo::force_unreserve())] + pub fn force_unreserve( + origin: OriginFor, + who: AccountIdLookupOf, + amount: T::Balance, + ) -> DispatchResult { + ensure_root(origin)?; + let who = T::Lookup::lookup(who)?; + let _leftover = >::unreserve(&who, amount); Ok(()) - })? - } - - fn set_total_issuance(amount: Self::Balance) { - TotalIssuance::::mutate(|t| *t = amount); - } -} - -impl, I: 'static> fungible::InspectHold for Pallet { - fn balance_on_hold(who: &T::AccountId) -> T::Balance { - Self::account(who).reserved - } - fn can_hold(who: &T::AccountId, amount: T::Balance) -> bool { - let a = Self::account(who); - let min_balance = T::ExistentialDeposit::get().max(a.frozen(Reasons::All)); - if a.reserved.checked_add(&amount).is_none() { - return false - } - // We require it to be min_balance + amount to ensure that the full reserved funds may be - // slashed without compromising locked funds or destroying the account. - let required_free = match min_balance.checked_add(&amount) { - Some(x) => x, - None => return false, - }; - a.free >= required_free - } -} -impl, I: 'static> fungible::MutateHold for Pallet { - fn hold(who: &T::AccountId, amount: Self::Balance) -> DispatchResult { - if amount.is_zero() { - return Ok(()) - } - ensure!(Self::can_reserve(who, amount), Error::::InsufficientBalance); - Self::mutate_account(who, |a| { - a.free -= amount; - a.reserved += amount; - })?; - Ok(()) - } - fn release( - who: &T::AccountId, - amount: Self::Balance, - best_effort: bool, - ) -> Result { - if amount.is_zero() { - return Ok(amount) } - // Done on a best-effort basis. - Self::try_mutate_account(who, |a, _| { - let new_free = a.free.saturating_add(amount.min(a.reserved)); - let actual = new_free - a.free; - ensure!(best_effort || actual == amount, Error::::InsufficientBalance); - // ^^^ Guaranteed to be <= amount and <= a.reserved - a.free = new_free; - a.reserved = a.reserved.saturating_sub(actual); - Ok(actual) - }) - } - fn transfer_held( - source: &T::AccountId, - dest: &T::AccountId, - amount: Self::Balance, - best_effort: bool, - on_hold: bool, - ) -> Result { - let status = if on_hold { Status::Reserved } else { Status::Free }; - Self::do_transfer_reserved(source, dest, amount, best_effort, status) - } -} - -// wrapping these imbalances in a private module is necessary to ensure absolute privacy -// of the inner member. -mod imbalances { - use super::{result, Config, Imbalance, RuntimeDebug, Saturating, TryDrop, Zero}; - use frame_support::traits::SameOrOther; - use sp_std::mem; - - /// Opaque, move-only struct with private fields that serves as a token denoting that - /// funds have been created without any equal and opposite accounting. - #[must_use] - #[derive(RuntimeDebug, PartialEq, Eq)] - pub struct PositiveImbalance, I: 'static = ()>(T::Balance); - - impl, I: 'static> PositiveImbalance { - /// Create a new positive imbalance from a balance. - pub fn new(amount: T::Balance) -> Self { - PositiveImbalance(amount) - } - } - /// Opaque, move-only struct with private fields that serves as a token denoting that - /// funds have been destroyed without any equal and opposite accounting. - #[must_use] - #[derive(RuntimeDebug, PartialEq, Eq)] - pub struct NegativeImbalance, I: 'static = ()>(T::Balance); - - impl, I: 'static> NegativeImbalance { - /// Create a new negative imbalance from a balance. - pub fn new(amount: T::Balance) -> Self { - NegativeImbalance(amount) - } - } - - impl, I: 'static> TryDrop for PositiveImbalance { - fn try_drop(self) -> result::Result<(), Self> { - self.drop_zero() - } - } - - impl, I: 'static> Default for PositiveImbalance { - fn default() -> Self { - Self::zero() - } - } - - impl, I: 'static> Imbalance for PositiveImbalance { - type Opposite = NegativeImbalance; - - fn zero() -> Self { - Self(Zero::zero()) - } - fn drop_zero(self) -> result::Result<(), Self> { - if self.0.is_zero() { - Ok(()) - } else { - Err(self) + /// Upgrade a specified account. + /// + /// - `origin`: Must be `Signed`. + /// - `who`: The account to be upgraded. + /// + /// This will waive the transaction fee if at least all but 10% of the accounts needed to + /// be upgraded. (We let some not have to be upgraded just in order to allow for the + /// possibililty of churn). + #[pallet::call_index(6)] + #[pallet::weight(T::WeightInfo::upgrade_accounts(who.len() as u32))] + pub fn upgrade_accounts( + origin: OriginFor, + who: Vec, + ) -> DispatchResultWithPostInfo { + ensure_signed(origin)?; + if who.is_empty() { + return Ok(Pays::Yes.into()) } - } - fn split(self, amount: T::Balance) -> (Self, Self) { - let first = self.0.min(amount); - let second = self.0 - first; - - mem::forget(self); - (Self(first), Self(second)) - } - fn merge(mut self, other: Self) -> Self { - self.0 = self.0.saturating_add(other.0); - mem::forget(other); - - self - } - fn subsume(&mut self, other: Self) { - self.0 = self.0.saturating_add(other.0); - mem::forget(other); - } - fn offset(self, other: Self::Opposite) -> SameOrOther { - let (a, b) = (self.0, other.0); - mem::forget((self, other)); - - if a > b { - SameOrOther::Same(Self(a - b)) - } else if b > a { - SameOrOther::Other(NegativeImbalance::new(b - a)) + let mut upgrade_count = 0; + for i in &who { + let upgraded = Self::ensure_upgraded(i); + if upgraded { + upgrade_count.saturating_inc(); + } + } + let proportion_upgraded = Perbill::from_rational(upgrade_count, who.len() as u32); + if proportion_upgraded >= Perbill::from_percent(90) { + Ok(Pays::No.into()) } else { - SameOrOther::None + Ok(Pays::Yes.into()) } } - fn peek(&self) -> T::Balance { - self.0 - } - } - - impl, I: 'static> TryDrop for NegativeImbalance { - fn try_drop(self) -> result::Result<(), Self> { - self.drop_zero() - } - } - impl, I: 'static> Default for NegativeImbalance { - fn default() -> Self { - Self::zero() + /// Alias for `transfer_allow_death`, provided only for name-wise compatibility. + /// + /// WARNING: DEPRECATED! Will be released in approximately 3 months. + #[pallet::call_index(7)] + #[pallet::weight(T::WeightInfo::transfer_allow_death())] + pub fn transfer( + origin: OriginFor, + dest: AccountIdLookupOf, + #[pallet::compact] value: T::Balance, + ) -> DispatchResultWithPostInfo { + let source = ensure_signed(origin)?; + let dest = T::Lookup::lookup(dest)?; + >::transfer(&source, &dest, value, Expendable)?; + Ok(().into()) } - } - impl, I: 'static> Imbalance for NegativeImbalance { - type Opposite = PositiveImbalance; + /// Set the regular balance of a given account. + /// + /// The dispatch origin for this call is `root`. + #[pallet::call_index(8)] + #[pallet::weight( + T::WeightInfo::force_set_balance_creating() // Creates a new account. + .max(T::WeightInfo::force_set_balance_killing()) // Kills an existing account. + )] + pub fn force_set_balance( + origin: OriginFor, + who: AccountIdLookupOf, + #[pallet::compact] new_free: T::Balance, + ) -> DispatchResultWithPostInfo { + ensure_root(origin)?; + let who = T::Lookup::lookup(who)?; + let existential_deposit = T::ExistentialDeposit::get(); - fn zero() -> Self { - Self(Zero::zero()) - } - fn drop_zero(self) -> result::Result<(), Self> { - if self.0.is_zero() { - Ok(()) - } else { - Err(self) - } - } - fn split(self, amount: T::Balance) -> (Self, Self) { - let first = self.0.min(amount); - let second = self.0 - first; + let wipeout = new_free < existential_deposit; + let new_free = if wipeout { Zero::zero() } else { new_free }; - mem::forget(self); - (Self(first), Self(second)) - } - fn merge(mut self, other: Self) -> Self { - self.0 = self.0.saturating_add(other.0); - mem::forget(other); + // First we try to modify the account's balance to the forced balance. + let old_free = Self::mutate_account_handling_dust(&who, |account| { + let old_free = account.free; + account.free = new_free; + old_free + })?; - self - } - fn subsume(&mut self, other: Self) { - self.0 = self.0.saturating_add(other.0); - mem::forget(other); - } - fn offset(self, other: Self::Opposite) -> SameOrOther { - let (a, b) = (self.0, other.0); - mem::forget((self, other)); - - if a > b { - SameOrOther::Same(Self(a - b)) - } else if b > a { - SameOrOther::Other(PositiveImbalance::new(b - a)) - } else { - SameOrOther::None + // This will adjust the total issuance, which was not done by the `mutate_account` + // above. + if new_free > old_free { + mem::drop(PositiveImbalance::::new(new_free - old_free)); + } else if new_free < old_free { + mem::drop(NegativeImbalance::::new(old_free - new_free)); } - } - fn peek(&self) -> T::Balance { - self.0 - } - } - impl, I: 'static> Drop for PositiveImbalance { - /// Basic drop handler will just square up the total issuance. - fn drop(&mut self) { - >::mutate(|v| *v = v.saturating_add(self.0)); - } - } - - impl, I: 'static> Drop for NegativeImbalance { - /// Basic drop handler will just square up the total issuance. - fn drop(&mut self) { - >::mutate(|v| *v = v.saturating_sub(self.0)); + Self::deposit_event(Event::BalanceSet { who, free: new_free }); + Ok(().into()) } } -} - -impl, I: 'static> Currency for Pallet -where - T::Balance: MaybeSerializeDeserialize + Debug, -{ - type Balance = T::Balance; - type PositiveImbalance = PositiveImbalance; - type NegativeImbalance = NegativeImbalance; - - fn total_balance(who: &T::AccountId) -> Self::Balance { - Self::account(who).total() - } - // Check if `value` amount of free balance can be slashed from `who`. - fn can_slash(who: &T::AccountId, value: Self::Balance) -> bool { - if value.is_zero() { + impl, I: 'static> Pallet { + /// Ensure the account `who` is using the new logic. + /// + /// Returns `true` if the account did get upgraded, `false` if it didn't need upgrading. + pub fn ensure_upgraded(who: &T::AccountId) -> bool { + let mut a = T::AccountStore::get(who); + if a.flags.is_new_logic() { + return false + } + a.flags.set_new_logic(); + if !a.reserved.is_zero() || !a.frozen.is_zero() { + if !system::Pallet::::can_inc_consumer(who) { + // Gah!! We have a non-zero reserve balance but no provider refs :( + // This shouldn't practically happen, but we need a failsafe anyway: let's give + // them enough for an ED. + a.free = a.free.min(T::ExistentialDeposit::get()); + system::Pallet::::inc_providers(who); + } + let _ = system::Pallet::::inc_consumers(who).defensive(); + } + // Should never fail - we're only setting a bit. + let _ = T::AccountStore::try_mutate_exists(who, |account| -> DispatchResult { + *account = Some(a); + Ok(()) + }); + Self::deposit_event(Event::Upgraded { who: who.clone() }); return true } - Self::free_balance(who) >= value - } - - fn total_issuance() -> Self::Balance { - TotalIssuance::::get() - } - - fn active_issuance() -> Self::Balance { - >::active_issuance() - } - - fn deactivate(amount: Self::Balance) { - >::deactivate(amount); - } - - fn reactivate(amount: Self::Balance) { - >::reactivate(amount); - } - fn minimum_balance() -> Self::Balance { - T::ExistentialDeposit::get() - } - - // Burn funds from the total issuance, returning a positive imbalance for the amount burned. - // Is a no-op if amount to be burned is zero. - fn burn(mut amount: Self::Balance) -> Self::PositiveImbalance { - if amount.is_zero() { - return PositiveImbalance::zero() + /// Get the free balance of an account. + pub fn free_balance(who: impl sp_std::borrow::Borrow) -> T::Balance { + Self::account(who.borrow()).free } - >::mutate(|issued| { - *issued = issued.checked_sub(&amount).unwrap_or_else(|| { - amount = *issued; - Zero::zero() - }); - }); - PositiveImbalance::new(amount) - } - // Create new funds into the total issuance, returning a negative imbalance - // for the amount issued. - // Is a no-op if amount to be issued it zero. - fn issue(mut amount: Self::Balance) -> Self::NegativeImbalance { - if amount.is_zero() { - return NegativeImbalance::zero() + /// Get the balance of an account that can be used for transfers, reservations, or any other + /// non-locking, non-transaction-fee activity. Will be at most `free_balance`. + pub fn usable_balance(who: impl sp_std::borrow::Borrow) -> T::Balance { + >::reducible_balance(who.borrow(), Expendable, Polite) } - >::mutate(|issued| { - *issued = issued.checked_add(&amount).unwrap_or_else(|| { - amount = Self::Balance::max_value() - *issued; - Self::Balance::max_value() - }) - }); - NegativeImbalance::new(amount) - } - - fn free_balance(who: &T::AccountId) -> Self::Balance { - Self::account(who).free - } - // Ensure that an account can withdraw from their free balance given any existing withdrawal - // restrictions like locks and vesting balance. - // Is a no-op if amount to be withdrawn is zero. - // - // ## Complexity - // Despite iterating over a list of locks, they are limited by the number of - // lock IDs, which means the number of runtime pallets that intend to use and create locks. - fn ensure_can_withdraw( - who: &T::AccountId, - amount: T::Balance, - reasons: WithdrawReasons, - new_balance: T::Balance, - ) -> DispatchResult { - if amount.is_zero() { - return Ok(()) + /// Get the balance of an account that can be used for paying transaction fees (not tipping, + /// or any other kind of fees, though). Will be at most `free_balance`. + /// + /// This requires that the account stays alive. + pub fn usable_balance_for_fees( + who: impl sp_std::borrow::Borrow, + ) -> T::Balance { + >::reducible_balance(who.borrow(), Protect, Polite) } - let min_balance = Self::account(who).frozen(reasons.into()); - ensure!(new_balance >= min_balance, Error::::LiquidityRestrictions); - Ok(()) - } - // Transfer some free balance from `transactor` to `dest`, respecting existence requirements. - // Is a no-op if value to be transferred is zero or the `transactor` is the same as `dest`. - fn transfer( - transactor: &T::AccountId, - dest: &T::AccountId, - value: Self::Balance, - existence_requirement: ExistenceRequirement, - ) -> DispatchResult { - if value.is_zero() || transactor == dest { - return Ok(()) + /// Get the reserved balance of an account. + pub fn reserved_balance(who: impl sp_std::borrow::Borrow) -> T::Balance { + Self::account(who.borrow()).reserved } - Self::try_mutate_account_with_dust( - dest, - |to_account, _| -> Result, DispatchError> { - Self::try_mutate_account_with_dust( - transactor, - |from_account, _| -> DispatchResult { - from_account.free = from_account - .free - .checked_sub(&value) - .ok_or(Error::::InsufficientBalance)?; - - // NOTE: total stake being stored in the same type means that this could - // never overflow but better to be safe than sorry. - to_account.free = - to_account.free.checked_add(&value).ok_or(ArithmeticError::Overflow)?; - - let ed = T::ExistentialDeposit::get(); - ensure!(to_account.total() >= ed, Error::::ExistentialDeposit); - - Self::ensure_can_withdraw( - transactor, - value, - WithdrawReasons::TRANSFER, - from_account.free, - ) - .map_err(|_| Error::::LiquidityRestrictions)?; - - // TODO: This is over-conservative. There may now be other providers, and - // this pallet may not even be a provider. - let allow_death = existence_requirement == ExistenceRequirement::AllowDeath; - let allow_death = - allow_death && system::Pallet::::can_dec_provider(transactor); - ensure!( - allow_death || from_account.total() >= ed, - Error::::KeepAlive - ); - - Ok(()) - }, - ) - .map(|(_, maybe_dust_cleaner)| maybe_dust_cleaner) - }, - )?; - - // Emit transfer event. - Self::deposit_event(Event::Transfer { - from: transactor.clone(), - to: dest.clone(), - amount: value, - }); - - Ok(()) - } - - /// Slash a target account `who`, returning the negative imbalance created and any left over - /// amount that could not be slashed. - /// - /// Is a no-op if `value` to be slashed is zero or the account does not exist. - /// - /// NOTE: `slash()` prefers free balance, but assumes that reserve balance can be drawn - /// from in extreme circumstances. `can_slash()` should be used prior to `slash()` to avoid - /// having to draw from reserved funds, however we err on the side of punishment if things are - /// inconsistent or `can_slash` wasn't used appropriately. - fn slash(who: &T::AccountId, value: Self::Balance) -> (Self::NegativeImbalance, Self::Balance) { - if value.is_zero() { - return (NegativeImbalance::zero(), Zero::zero()) - } - if Self::total_balance(who).is_zero() { - return (NegativeImbalance::zero(), value) + /// Get both the free and reserved balances of an account. + pub(crate) fn account(who: &T::AccountId) -> AccountData { + T::AccountStore::get(who) } - for attempt in 0..2 { - match Self::try_mutate_account( - who, - |account, - _is_new| - -> Result<(Self::NegativeImbalance, Self::Balance), DispatchError> { - // Best value is the most amount we can slash following liveness rules. - let best_value = match attempt { - // First attempt we try to slash the full amount, and see if liveness issues - // happen. - 0 => value, - // If acting as a critical provider (i.e. first attempt failed), then slash - // as much as possible while leaving at least at ED. - _ => value.min( - (account.free + account.reserved) - .saturating_sub(T::ExistentialDeposit::get()), - ), - }; - - let free_slash = cmp::min(account.free, best_value); - account.free -= free_slash; // Safe because of above check - let remaining_slash = best_value - free_slash; // Safe because of above check - - if !remaining_slash.is_zero() { - // If we have remaining slash, take it from reserved balance. - let reserved_slash = cmp::min(account.reserved, remaining_slash); - account.reserved -= reserved_slash; // Safe because of above check - Ok(( - NegativeImbalance::new(free_slash + reserved_slash), - value - free_slash - reserved_slash, /* Safe because value is gt or - * eq total slashed */ - )) - } else { - // Else we are done! - Ok(( - NegativeImbalance::new(free_slash), - value - free_slash, // Safe because value is gt or eq to total slashed - )) - } - }, - ) { - Ok((imbalance, not_slashed)) => { - Self::deposit_event(Event::Slashed { - who: who.clone(), - amount: value.saturating_sub(not_slashed), - }); - return (imbalance, not_slashed) - }, - Err(_) => (), + /// Mutate an account to some new value, or delete it entirely with `None`. Will enforce + /// `ExistentialDeposit` law, annulling the account as needed. + /// + /// It returns the result from the closure. Any dust is handled through the low-level + /// `fungible::Unbalanced` trap-door for legacy dust management. + /// + /// NOTE: Doesn't do any preparatory work for creating a new account, so should only be used + /// when it is known that the account already exists. + /// + /// NOTE: LOW-LEVEL: This will not attempt to maintain total issuance. It is expected that + /// the caller will do this. + pub(crate) fn mutate_account_handling_dust( + who: &T::AccountId, + f: impl FnOnce(&mut AccountData) -> R, + ) -> Result { + let (r, maybe_dust) = Self::mutate_account(who, f)?; + if let Some(dust) = maybe_dust { + >::handle_raw_dust(dust); } + Ok(r) } - // Should never get here. But we'll be defensive anyway. - (Self::NegativeImbalance::zero(), value) - } - - /// Deposit some `value` into the free balance of an existing target account `who`. - /// - /// Is a no-op if the `value` to be deposited is zero. - fn deposit_into_existing( - who: &T::AccountId, - value: Self::Balance, - ) -> Result { - if value.is_zero() { - return Ok(PositiveImbalance::zero()) - } - - Self::try_mutate_account( - who, - |account, is_new| -> Result { - ensure!(!is_new, Error::::DeadAccount); - account.free = account.free.checked_add(&value).ok_or(ArithmeticError::Overflow)?; - Self::deposit_event(Event::Deposit { who: who.clone(), amount: value }); - Ok(PositiveImbalance::new(value)) - }, - ) - } - - /// Deposit some `value` into the free balance of `who`, possibly creating a new account. - /// - /// This function is a no-op if: - /// - the `value` to be deposited is zero; or - /// - the `value` to be deposited is less than the required ED and the account does not yet - /// exist; or - /// - the deposit would necessitate the account to exist and there are no provider references; - /// or - /// - `value` is so large it would cause the balance of `who` to overflow. - fn deposit_creating(who: &T::AccountId, value: Self::Balance) -> Self::PositiveImbalance { - if value.is_zero() { - return Self::PositiveImbalance::zero() - } - - Self::try_mutate_account( - who, - |account, is_new| -> Result { - let ed = T::ExistentialDeposit::get(); - ensure!(value >= ed || !is_new, Error::::ExistentialDeposit); - - // defensive only: overflow should never happen, however in case it does, then this - // operation is a no-op. - account.free = match account.free.checked_add(&value) { - Some(x) => x, - None => return Ok(Self::PositiveImbalance::zero()), - }; - - Self::deposit_event(Event::Deposit { who: who.clone(), amount: value }); - Ok(PositiveImbalance::new(value)) - }, - ) - .unwrap_or_else(|_| Self::PositiveImbalance::zero()) - } - - /// Withdraw some free balance from an account, respecting existence requirements. - /// - /// Is a no-op if value to be withdrawn is zero. - fn withdraw( - who: &T::AccountId, - value: Self::Balance, - reasons: WithdrawReasons, - liveness: ExistenceRequirement, - ) -> result::Result { - if value.is_zero() { - return Ok(NegativeImbalance::zero()) + /// Mutate an account to some new value, or delete it entirely with `None`. Will enforce + /// `ExistentialDeposit` law, annulling the account as needed. + /// + /// It returns the result from the closure. Any dust is handled through the low-level + /// `fungible::Unbalanced` trap-door for legacy dust management. + /// + /// NOTE: Doesn't do any preparatory work for creating a new account, so should only be used + /// when it is known that the account already exists. + /// + /// NOTE: LOW-LEVEL: This will not attempt to maintain total issuance. It is expected that + /// the caller will do this. + pub(crate) fn try_mutate_account_handling_dust>( + who: &T::AccountId, + f: impl FnOnce(&mut AccountData, bool) -> Result, + ) -> Result { + let (r, maybe_dust) = Self::try_mutate_account(who, f)?; + if let Some(dust) = maybe_dust { + >::handle_raw_dust(dust); + } + Ok(r) } - Self::try_mutate_account( - who, - |account, _| -> Result { - let new_free_account = - account.free.checked_sub(&value).ok_or(Error::::InsufficientBalance)?; - - // bail if we need to keep the account alive and this would kill it. - let ed = T::ExistentialDeposit::get(); - let would_be_dead = new_free_account + account.reserved < ed; - let would_kill = would_be_dead && account.free + account.reserved >= ed; - ensure!(liveness == AllowDeath || !would_kill, Error::::KeepAlive); - - Self::ensure_can_withdraw(who, value, reasons, new_free_account)?; - - account.free = new_free_account; + /// Mutate an account to some new value, or delete it entirely with `None`. Will enforce + /// `ExistentialDeposit` law, annulling the account as needed. + /// + /// It returns both the result from the closure, and an optional amount of dust + /// which should be handled once it is known that all nested mutates that could affect + /// storage items what the dust handler touches have completed. + /// + /// NOTE: Doesn't do any preparatory work for creating a new account, so should only be used + /// when it is known that the account already exists. + /// + /// NOTE: LOW-LEVEL: This will not attempt to maintain total issuance. It is expected that + /// the caller will do this. + pub(crate) fn mutate_account( + who: &T::AccountId, + f: impl FnOnce(&mut AccountData) -> R, + ) -> Result<(R, Option), DispatchError> { + Self::try_mutate_account(who, |a, _| -> Result { Ok(f(a)) }) + } + + /// Mutate an account to some new value, or delete it entirely with `None`. Will enforce + /// `ExistentialDeposit` law, annulling the account as needed. This will do nothing if the + /// result of `f` is an `Err`. + /// + /// It returns both the result from the closure, and an optional amount of dust + /// which should be handled once it is known that all nested mutates that could affect + /// storage items what the dust handler touches have completed. + /// + /// NOTE: Doesn't do any preparatory work for creating a new account, so should only be used + /// when it is known that the account already exists. + /// + /// NOTE: LOW-LEVEL: This will not attempt to maintain total issuance. It is expected that + /// the caller will do this. + pub(crate) fn try_mutate_account>( + who: &T::AccountId, + f: impl FnOnce(&mut AccountData, bool) -> Result, + ) -> Result<(R, Option), E> { + Self::ensure_upgraded(who); + let result = T::AccountStore::try_mutate_exists(who, |maybe_account| { + let is_new = maybe_account.is_none(); + let mut account = maybe_account.take().unwrap_or_default(); + let did_provide = account.free >= T::ExistentialDeposit::get(); + let did_consume = + !is_new && (!account.reserved.is_zero() || !account.frozen.is_zero()); + + let result = f(&mut account, is_new)?; + + let does_provide = account.free >= T::ExistentialDeposit::get(); + let does_consume = !account.reserved.is_zero() || !account.frozen.is_zero(); + + if !did_provide && does_provide { + frame_system::Pallet::::inc_providers(who); + } + if did_consume && !does_consume { + frame_system::Pallet::::dec_consumers(who); + } + if !did_consume && does_consume { + frame_system::Pallet::::inc_consumers(who)?; + } + if did_provide && !does_provide { + // This could reap the account so must go last. + frame_system::Pallet::::dec_providers(who).map_err(|r| { + if did_consume && !does_consume { + // best-effort revert consumer change. + let _ = frame_system::Pallet::::inc_consumers(who).defensive(); + } + if !did_consume && does_consume { + let _ = frame_system::Pallet::::dec_consumers(who); + } + r + })?; + } - Self::deposit_event(Event::Withdraw { who: who.clone(), amount: value }); - Ok(NegativeImbalance::new(value)) - }, - ) - } + let maybe_endowed = if is_new { Some(account.free) } else { None }; - /// Force the new free balance of a target account `who` to some new value `balance`. - fn make_free_balance_be( - who: &T::AccountId, - value: Self::Balance, - ) -> SignedImbalance { - Self::try_mutate_account( - who, - |account, - is_new| - -> Result, DispatchError> { + // Handle any steps needed after mutating an account. + // + // This includes DustRemoval unbalancing, in the case than the `new` account's total + // balance is non-zero but below ED. + // + // Updates `maybe_account` to `Some` iff the account has sufficient balance. + // Evaluates `maybe_dust`, which is `Some` containing the dust to be dropped, iff + // some dust should be dropped. + // + // We should never be dropping if reserved is non-zero. Reserved being non-zero + // should imply that we have a consumer ref, so this is economically safe. let ed = T::ExistentialDeposit::get(); - let total = value.saturating_add(account.reserved); - // If we're attempting to set an existing account to less than ED, then - // bypass the entire operation. It's a no-op if you follow it through, but - // since this is an instance where we might account for a negative imbalance - // (in the dust cleaner of set_account) before we account for its actual - // equal and opposite cause (returned as an Imbalance), then in the - // instance that there's no other accounts on the system at all, we might - // underflow the issuance and our arithmetic will be off. - ensure!(total >= ed || !is_new, Error::::ExistentialDeposit); - - let imbalance = if account.free <= value { - SignedImbalance::Positive(PositiveImbalance::new(value - account.free)) + let maybe_dust = if account.free < ed && account.reserved.is_zero() { + if account.free.is_zero() { + None + } else { + Some(account.free) + } } else { - SignedImbalance::Negative(NegativeImbalance::new(account.free - value)) - }; - account.free = value; - Self::deposit_event(Event::BalanceSet { - who: who.clone(), - free: account.free, - reserved: account.reserved, - }); - Ok(imbalance) - }, - ) - .unwrap_or_else(|_| SignedImbalance::Positive(Self::PositiveImbalance::zero())) - } -} - -impl, I: 'static> ReservableCurrency for Pallet -where - T::Balance: MaybeSerializeDeserialize + Debug, -{ - /// Check if `who` can reserve `value` from their free balance. - /// - /// Always `true` if value to be reserved is zero. - fn can_reserve(who: &T::AccountId, value: Self::Balance) -> bool { - if value.is_zero() { - return true - } - Self::account(who).free.checked_sub(&value).map_or(false, |new_balance| { - Self::ensure_can_withdraw(who, value, WithdrawReasons::RESERVE, new_balance).is_ok() - }) - } - - fn reserved_balance(who: &T::AccountId) -> Self::Balance { - Self::account(who).reserved - } - - /// Move `value` from the free balance from `who` to their reserved balance. - /// - /// Is a no-op if value to be reserved is zero. - fn reserve(who: &T::AccountId, value: Self::Balance) -> DispatchResult { - if value.is_zero() { - return Ok(()) - } - - Self::try_mutate_account(who, |account, _| -> DispatchResult { - account.free = - account.free.checked_sub(&value).ok_or(Error::::InsufficientBalance)?; - account.reserved = - account.reserved.checked_add(&value).ok_or(ArithmeticError::Overflow)?; - Self::ensure_can_withdraw(&who, value, WithdrawReasons::RESERVE, account.free) - })?; - - Self::deposit_event(Event::Reserved { who: who.clone(), amount: value }); - Ok(()) - } - - /// Unreserve some funds, returning any amount that was unable to be unreserved. - /// - /// Is a no-op if the value to be unreserved is zero or the account does not exist. - /// - /// NOTE: returns amount value which wasn't successfully unreserved. - fn unreserve(who: &T::AccountId, value: Self::Balance) -> Self::Balance { - if value.is_zero() { - return Zero::zero() - } - if Self::total_balance(who).is_zero() { - return value - } - - let actual = match Self::mutate_account(who, |account| { - let actual = cmp::min(account.reserved, value); - account.reserved -= actual; - // defensive only: this can never fail since total issuance which is at least - // free+reserved fits into the same data type. - account.free = account.free.defensive_saturating_add(actual); - actual - }) { - Ok(x) => x, - Err(_) => { - // This should never happen since we don't alter the total amount in the account. - // If it ever does, then we should fail gracefully though, indicating that nothing - // could be done. - return value - }, - }; - - Self::deposit_event(Event::Unreserved { who: who.clone(), amount: actual }); - value - actual - } - - /// Slash from reserved balance, returning the negative imbalance created, - /// and any amount that was unable to be slashed. - /// - /// Is a no-op if the value to be slashed is zero or the account does not exist. - fn slash_reserved( - who: &T::AccountId, - value: Self::Balance, - ) -> (Self::NegativeImbalance, Self::Balance) { - if value.is_zero() { - return (NegativeImbalance::zero(), Zero::zero()) - } - if Self::total_balance(who).is_zero() { - return (NegativeImbalance::zero(), value) - } - - // NOTE: `mutate_account` may fail if it attempts to reduce the balance to the point that an - // account is attempted to be illegally destroyed. - - for attempt in 0..2 { - match Self::mutate_account(who, |account| { - let best_value = match attempt { - 0 => value, - // If acting as a critical provider (i.e. first attempt failed), then ensure - // slash leaves at least the ED. - _ => value.min( - (account.free + account.reserved) - .saturating_sub(T::ExistentialDeposit::get()), - ), + assert!( + account.free.is_zero() || account.free >= ed || !account.reserved.is_zero() + ); + *maybe_account = Some(account); + None }; - - let actual = cmp::min(account.reserved, best_value); - account.reserved -= actual; - - // underflow should never happen, but it if does, there's nothing to be done here. - (NegativeImbalance::new(actual), value - actual) - }) { - Ok((imbalance, not_slashed)) => { - Self::deposit_event(Event::Slashed { - who: who.clone(), - amount: value.saturating_sub(not_slashed), + Ok((maybe_endowed, maybe_dust, result)) + }); + result.map(|(maybe_endowed, maybe_dust, result)| { + if let Some(endowed) = maybe_endowed { + Self::deposit_event(Event::Endowed { + account: who.clone(), + free_balance: endowed, }); - return (imbalance, not_slashed) - }, - Err(_) => (), - } - } - // Should never get here as we ensure that ED is left in the second attempt. - // In case we do, though, then we fail gracefully. - (Self::NegativeImbalance::zero(), value) - } - - /// Move the reserved balance of one account into the balance of another, according to `status`. - /// - /// Is a no-op if: - /// - the value to be moved is zero; or - /// - the `slashed` id equal to `beneficiary` and the `status` is `Reserved`. - fn repatriate_reserved( - slashed: &T::AccountId, - beneficiary: &T::AccountId, - value: Self::Balance, - status: Status, - ) -> Result { - let actual = Self::do_transfer_reserved(slashed, beneficiary, value, true, status)?; - Ok(value.saturating_sub(actual)) - } -} - -impl, I: 'static> NamedReservableCurrency for Pallet -where - T::Balance: MaybeSerializeDeserialize + Debug, -{ - type ReserveIdentifier = T::ReserveIdentifier; - - fn reserved_balance_named(id: &Self::ReserveIdentifier, who: &T::AccountId) -> Self::Balance { - let reserves = Self::reserves(who); - reserves - .binary_search_by_key(id, |data| data.id) - .map(|index| reserves[index].amount) - .unwrap_or_default() - } - - /// Move `value` from the free balance from `who` to a named reserve balance. - /// - /// Is a no-op if value to be reserved is zero. - fn reserve_named( - id: &Self::ReserveIdentifier, - who: &T::AccountId, - value: Self::Balance, - ) -> DispatchResult { - if value.is_zero() { - return Ok(()) - } - - Reserves::::try_mutate(who, |reserves| -> DispatchResult { - match reserves.binary_search_by_key(id, |data| data.id) { - Ok(index) => { - // this add can't overflow but just to be defensive. - reserves[index].amount = reserves[index].amount.defensive_saturating_add(value); - }, - Err(index) => { - reserves - .try_insert(index, ReserveData { id: *id, amount: value }) - .map_err(|_| Error::::TooManyReserves)?; - }, - }; - >::reserve(who, value)?; - Ok(()) - }) - } - - /// Unreserve some funds, returning any amount that was unable to be unreserved. - /// - /// Is a no-op if the value to be unreserved is zero. - fn unreserve_named( - id: &Self::ReserveIdentifier, - who: &T::AccountId, - value: Self::Balance, - ) -> Self::Balance { - if value.is_zero() { - return Zero::zero() + } + if let Some(amount) = maybe_dust { + Pallet::::deposit_event(Event::DustLost { account: who.clone(), amount }); + } + (result, maybe_dust) + }) } - Reserves::::mutate_exists(who, |maybe_reserves| -> Self::Balance { - if let Some(reserves) = maybe_reserves.as_mut() { - match reserves.binary_search_by_key(id, |data| data.id) { - Ok(index) => { - let to_change = cmp::min(reserves[index].amount, value); - - let remain = >::unreserve(who, to_change); - - // remain should always be zero but just to be defensive here. - let actual = to_change.defensive_saturating_sub(remain); - - // `actual <= to_change` and `to_change <= amount`; qed; - reserves[index].amount -= actual; + /// Update the account entry for `who`, given the locks. + pub(crate) fn update_locks(who: &T::AccountId, locks: &[BalanceLock]) { + let bounded_locks = WeakBoundedVec::<_, T::MaxLocks>::force_from( + locks.to_vec(), + Some("Balances Update Locks"), + ); - if reserves[index].amount.is_zero() { - if reserves.len() == 1 { - // no more named reserves - *maybe_reserves = None; - } else { - // remove this named reserve - reserves.remove(index); - } - } + if locks.len() as u32 > T::MaxLocks::get() { + log::warn!( + target: LOG_TARGET, + "Warning: A user has more currency locks than expected. \ + A runtime configuration adjustment may be needed." + ); + } + let freezes = Freezes::::get(who); + // TODO: Revisit this assumption. We no manipulate consumer/provider refs. + // No way this can fail since we do not alter the existential balances. + let res = Self::mutate_account(who, |b| { + b.frozen = Zero::zero(); + for l in locks.iter() { + b.frozen = b.frozen.max(l.amount); + } + for l in freezes.iter() { + b.frozen = b.frozen.max(l.amount); + } + }); + debug_assert!(res.is_ok()); + if let Ok((_, maybe_dust)) = res { + debug_assert!(maybe_dust.is_none(), "Not altering main balance; qed"); + } - value - actual - }, - Err(_) => value, + let existed = Locks::::contains_key(who); + if locks.is_empty() { + Locks::::remove(who); + if existed { + // TODO: use Locks::::hashed_key + // https://github.com/paritytech/substrate/issues/4969 + system::Pallet::::dec_consumers(who); } } else { - value + Locks::::insert(who, bounded_locks); + if !existed && system::Pallet::::inc_consumers_without_limit(who).is_err() { + // No providers for the locks. This is impossible under normal circumstances + // since the funds that are under the lock will themselves be stored in the + // account and therefore will need a reference. + log::warn!( + target: LOG_TARGET, + "Warning: Attempt to introduce lock consumer reference, yet no providers. \ + This is unexpected but should be safe." + ); + } } - }) - } - - /// Slash from reserved balance, returning the negative imbalance created, - /// and any amount that was unable to be slashed. - /// - /// Is a no-op if the value to be slashed is zero. - fn slash_reserved_named( - id: &Self::ReserveIdentifier, - who: &T::AccountId, - value: Self::Balance, - ) -> (Self::NegativeImbalance, Self::Balance) { - if value.is_zero() { - return (NegativeImbalance::zero(), Zero::zero()) } - Reserves::::mutate(who, |reserves| -> (Self::NegativeImbalance, Self::Balance) { - match reserves.binary_search_by_key(id, |data| data.id) { - Ok(index) => { - let to_change = cmp::min(reserves[index].amount, value); - - let (imb, remain) = - >::slash_reserved(who, to_change); - - // remain should always be zero but just to be defensive here. - let actual = to_change.defensive_saturating_sub(remain); - - // `actual <= to_change` and `to_change <= amount`; qed; - reserves[index].amount -= actual; - - Self::deposit_event(Event::Slashed { who: who.clone(), amount: actual }); - (imb, value - actual) - }, - Err(_) => (NegativeImbalance::zero(), value), + /// Update the account entry for `who`, given the locks. + pub(crate) fn update_freezes( + who: &T::AccountId, + freezes: BoundedSlice, T::MaxFreezes>, + ) -> DispatchResult { + let (_, maybe_dust) = Self::mutate_account(who, |b| { + b.frozen = Zero::zero(); + for l in Locks::::get(who).iter() { + b.frozen = b.frozen.max(l.amount); + } + for l in freezes.iter() { + b.frozen = b.frozen.max(l.amount); + } + })?; + debug_assert!(maybe_dust.is_none(), "Not altering main balance; qed"); + if freezes.is_empty() { + Freezes::::remove(who); + } else { + Freezes::::insert(who, freezes); } - }) - } - - /// Move the reserved balance of one account into the balance of another, according to `status`. - /// If `status` is `Reserved`, the balance will be reserved with given `id`. - /// - /// Is a no-op if: - /// - the value to be moved is zero; or - /// - the `slashed` id equal to `beneficiary` and the `status` is `Reserved`. - fn repatriate_reserved_named( - id: &Self::ReserveIdentifier, - slashed: &T::AccountId, - beneficiary: &T::AccountId, - value: Self::Balance, - status: Status, - ) -> Result { - if value.is_zero() { - return Ok(Zero::zero()) + Ok(()) } - if slashed == beneficiary { - return match status { - Status::Free => Ok(Self::unreserve_named(id, slashed, value)), - Status::Reserved => - Ok(value.saturating_sub(Self::reserved_balance_named(id, slashed))), + /// Move the reserved balance of one account into the balance of another, according to + /// `status`. + /// + /// Is a no-op if: + /// - the value to be moved is zero; or + /// - the `slashed` id equal to `beneficiary` and the `status` is `Reserved`. + /// + /// NOTE: returns actual amount of transferred value in `Ok` case. + pub(crate) fn do_transfer_reserved( + slashed: &T::AccountId, + beneficiary: &T::AccountId, + value: T::Balance, + best_effort: bool, + status: Status, + ) -> Result { + if value.is_zero() { + return Ok(Zero::zero()) } - } - - Reserves::::try_mutate(slashed, |reserves| -> Result { - match reserves.binary_search_by_key(id, |data| data.id) { - Ok(index) => { - let to_change = cmp::min(reserves[index].amount, value); - - let actual = if status == Status::Reserved { - // make it the reserved under same identifier - Reserves::::try_mutate( - beneficiary, - |reserves| -> Result { - match reserves.binary_search_by_key(id, |data| data.id) { - Ok(index) => { - let remain = - >::repatriate_reserved( - slashed, - beneficiary, - to_change, - status, - )?; - - // remain should always be zero but just to be defensive - // here. - let actual = to_change.defensive_saturating_sub(remain); - - // this add can't overflow but just to be defensive. - reserves[index].amount = - reserves[index].amount.defensive_saturating_add(actual); - - Ok(actual) - }, - Err(index) => { - let remain = - >::repatriate_reserved( - slashed, - beneficiary, - to_change, - status, - )?; - - // remain should always be zero but just to be defensive - // here - let actual = to_change.defensive_saturating_sub(remain); - - reserves - .try_insert( - index, - ReserveData { id: *id, amount: actual }, - ) - .map_err(|_| Error::::TooManyReserves)?; - - Ok(actual) - }, - } - }, - )? - } else { - let remain = >::repatriate_reserved( - slashed, - beneficiary, - to_change, - status, - )?; - - // remain should always be zero but just to be defensive here - to_change.defensive_saturating_sub(remain) - }; - // `actual <= to_change` and `to_change <= amount`; qed; - reserves[index].amount -= actual; + if slashed == beneficiary { + return match status { + Status::Free => Ok(value.saturating_sub(Self::unreserve(slashed, value))), + Status::Reserved => Ok(value.saturating_sub(Self::reserved_balance(slashed))), + } + } - Ok(value - actual) + let ((actual, maybe_dust_1), maybe_dust_2) = Self::try_mutate_account( + beneficiary, + |to_account, is_new| -> Result<(T::Balance, Option), DispatchError> { + ensure!(!is_new, Error::::DeadAccount); + Self::try_mutate_account( + slashed, + |from_account, _| -> Result { + let actual = cmp::min(from_account.reserved, value); + ensure!( + best_effort || actual == value, + Error::::InsufficientBalance + ); + match status { + Status::Free => + to_account.free = to_account + .free + .checked_add(&actual) + .ok_or(ArithmeticError::Overflow)?, + Status::Reserved => + to_account.reserved = to_account + .reserved + .checked_add(&actual) + .ok_or(ArithmeticError::Overflow)?, + } + from_account.reserved -= actual; + Ok(actual) + }, + ) }, - Err(_) => Ok(value), - } - }) - } -} + )?; -impl, I: 'static> LockableCurrency for Pallet -where - T::Balance: MaybeSerializeDeserialize + Debug, -{ - type Moment = T::BlockNumber; - - type MaxLocks = T::MaxLocks; - - // Set a lock on the balance of `who`. - // Is a no-op if lock amount is zero or `reasons` `is_none()`. - fn set_lock( - id: LockIdentifier, - who: &T::AccountId, - amount: T::Balance, - reasons: WithdrawReasons, - ) { - if amount.is_zero() || reasons.is_empty() { - return - } - let mut new_lock = Some(BalanceLock { id, amount, reasons: reasons.into() }); - let mut locks = Self::locks(who) - .into_iter() - .filter_map(|l| if l.id == id { new_lock.take() } else { Some(l) }) - .collect::>(); - if let Some(lock) = new_lock { - locks.push(lock) - } - Self::update_locks(who, &locks[..]); - } + if let Some(dust) = maybe_dust_1 { + >::handle_raw_dust(dust); + } + if let Some(dust) = maybe_dust_2 { + >::handle_raw_dust(dust); + } - // Extend a lock on the balance of `who`. - // Is a no-op if lock amount is zero or `reasons` `is_none()`. - fn extend_lock( - id: LockIdentifier, - who: &T::AccountId, - amount: T::Balance, - reasons: WithdrawReasons, - ) { - if amount.is_zero() || reasons.is_empty() { - return - } - let mut new_lock = Some(BalanceLock { id, amount, reasons: reasons.into() }); - let mut locks = Self::locks(who) - .into_iter() - .filter_map(|l| { - if l.id == id { - new_lock.take().map(|nl| BalanceLock { - id: l.id, - amount: l.amount.max(nl.amount), - reasons: l.reasons | nl.reasons, - }) - } else { - Some(l) - } - }) - .collect::>(); - if let Some(lock) = new_lock { - locks.push(lock) + Self::deposit_event(Event::ReserveRepatriated { + from: slashed.clone(), + to: beneficiary.clone(), + amount: actual, + destination_status: status, + }); + Ok(actual) } - Self::update_locks(who, &locks[..]); - } - - fn remove_lock(id: LockIdentifier, who: &T::AccountId) { - let mut locks = Self::locks(who); - locks.retain(|l| l.id != id); - Self::update_locks(who, &locks[..]); } } diff --git a/frame/balances/src/tests.rs b/frame/balances/src/tests.rs deleted file mode 100644 index b4233a6c3..000000000 --- a/frame/balances/src/tests.rs +++ /dev/null @@ -1,1460 +0,0 @@ -// This file is part of Substrate. - -// Copyright (C) Parity Technologies (UK) Ltd. -// SPDX-License-Identifier: Apache-2.0 - -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -//! Macro for creating the tests for the module. - -#![cfg(test)] - -#[macro_export] -macro_rules! decl_tests { - ($test:ty, $ext_builder:ty, $existential_deposit:expr) => { - - use crate::*; - use sp_runtime::{ArithmeticError, TokenError, FixedPointNumber, traits::{SignedExtension, BadOrigin}}; - use frame_support::{ - assert_noop, assert_storage_noop, assert_ok, assert_err, - traits::{ - LockableCurrency, LockIdentifier, WithdrawReasons, - Currency, ReservableCurrency, ExistenceRequirement::AllowDeath - } - }; - use pallet_transaction_payment::{ChargeTransactionPayment, Multiplier}; - use frame_system::RawOrigin; - - const ID_1: LockIdentifier = *b"1 "; - const ID_2: LockIdentifier = *b"2 "; - - pub const CALL: &<$test as frame_system::Config>::RuntimeCall = - &RuntimeCall::Balances(pallet_balances::Call::transfer { dest: 0, value: 0 }); - - /// create a transaction info struct from weight. Handy to avoid building the whole struct. - pub fn info_from_weight(w: Weight) -> DispatchInfo { - DispatchInfo { weight: w, ..Default::default() } - } - - fn events() -> Vec { - let evt = System::events().into_iter().map(|evt| evt.event).collect::>(); - - System::reset_events(); - - evt - } - - #[test] - fn basic_locking_should_work() { - <$ext_builder>::default().existential_deposit(1).monied(true).build().execute_with(|| { - assert_eq!(Balances::free_balance(1), 10); - Balances::set_lock(ID_1, &1, 9, WithdrawReasons::all()); - assert_noop!( - >::transfer(&1, &2, 5, AllowDeath), - Error::<$test, _>::LiquidityRestrictions - ); - }); - } - - #[test] - fn account_should_be_reaped() { - <$ext_builder>::default().existential_deposit(1).monied(true).build().execute_with(|| { - assert_eq!(Balances::free_balance(1), 10); - assert_ok!(>::transfer(&1, &2, 10, AllowDeath)); - // Check that the account is dead. - assert!(!frame_system::Account::::contains_key(&1)); - }); - } - - #[test] - fn reap_failed_due_to_provider_and_consumer() { - <$ext_builder>::default().existential_deposit(1).monied(true).build().execute_with(|| { - // SCENARIO: only one provider and there are remaining consumers. - assert_ok!(System::inc_consumers(&1)); - assert!(!System::can_dec_provider(&1)); - assert_noop!( - >::transfer(&1, &2, 10, AllowDeath), - Error::<$test, _>::KeepAlive - ); - assert!(System::account_exists(&1)); - assert_eq!(Balances::free_balance(1), 10); - - // SCENARIO: more than one provider, but will not kill account due to other provider. - assert_eq!(System::inc_providers(&1), frame_system::IncRefStatus::Existed); - assert_eq!(System::providers(&1), 2); - assert!(System::can_dec_provider(&1)); - assert_ok!(>::transfer(&1, &2, 10, AllowDeath)); - assert_eq!(System::providers(&1), 1); - assert!(System::account_exists(&1)); - assert_eq!(Balances::free_balance(1), 0); - }); - } - - #[test] - fn partial_locking_should_work() { - <$ext_builder>::default().existential_deposit(1).monied(true).build().execute_with(|| { - Balances::set_lock(ID_1, &1, 5, WithdrawReasons::all()); - assert_ok!(>::transfer(&1, &2, 1, AllowDeath)); - }); - } - - #[test] - fn lock_removal_should_work() { - <$ext_builder>::default().existential_deposit(1).monied(true).build().execute_with(|| { - Balances::set_lock(ID_1, &1, u64::MAX, WithdrawReasons::all()); - Balances::remove_lock(ID_1, &1); - assert_ok!(>::transfer(&1, &2, 1, AllowDeath)); - }); - } - - #[test] - fn lock_replacement_should_work() { - <$ext_builder>::default().existential_deposit(1).monied(true).build().execute_with(|| { - Balances::set_lock(ID_1, &1, u64::MAX, WithdrawReasons::all()); - Balances::set_lock(ID_1, &1, 5, WithdrawReasons::all()); - assert_ok!(>::transfer(&1, &2, 1, AllowDeath)); - }); - } - - #[test] - fn double_locking_should_work() { - <$ext_builder>::default().existential_deposit(1).monied(true).build().execute_with(|| { - Balances::set_lock(ID_1, &1, 5, WithdrawReasons::all()); - Balances::set_lock(ID_2, &1, 5, WithdrawReasons::all()); - assert_ok!(>::transfer(&1, &2, 1, AllowDeath)); - }); - } - - #[test] - fn combination_locking_should_work() { - <$ext_builder>::default().existential_deposit(1).monied(true).build().execute_with(|| { - Balances::set_lock(ID_1, &1, u64::MAX, WithdrawReasons::empty()); - Balances::set_lock(ID_2, &1, 0, WithdrawReasons::all()); - assert_ok!(>::transfer(&1, &2, 1, AllowDeath)); - }); - } - - #[test] - fn lock_value_extension_should_work() { - <$ext_builder>::default().existential_deposit(1).monied(true).build().execute_with(|| { - Balances::set_lock(ID_1, &1, 5, WithdrawReasons::all()); - assert_noop!( - >::transfer(&1, &2, 6, AllowDeath), - Error::<$test, _>::LiquidityRestrictions - ); - Balances::extend_lock(ID_1, &1, 2, WithdrawReasons::all()); - assert_noop!( - >::transfer(&1, &2, 6, AllowDeath), - Error::<$test, _>::LiquidityRestrictions - ); - Balances::extend_lock(ID_1, &1, 8, WithdrawReasons::all()); - assert_noop!( - >::transfer(&1, &2, 3, AllowDeath), - Error::<$test, _>::LiquidityRestrictions - ); - }); - } - - #[test] - fn lock_reasons_should_work() { - <$ext_builder>::default() - .existential_deposit(1) - .monied(true) - .build() - .execute_with(|| { - pallet_transaction_payment::NextFeeMultiplier::<$test>::put( - Multiplier::saturating_from_integer(1) - ); - Balances::set_lock(ID_1, &1, 10, WithdrawReasons::RESERVE); - assert_noop!( - >::transfer(&1, &2, 1, AllowDeath), - Error::<$test, _>::LiquidityRestrictions - ); - assert_noop!( - >::reserve(&1, 1), - Error::<$test, _>::LiquidityRestrictions, - ); - assert!( as SignedExtension>::pre_dispatch( - ChargeTransactionPayment::from(1), - &1, - CALL, - &info_from_weight(Weight::from_parts(1, 0)), - 1, - ).is_err()); - assert_ok!( as SignedExtension>::pre_dispatch( - ChargeTransactionPayment::from(0), - &1, - CALL, - &info_from_weight(Weight::from_parts(1, 0)), - 1, - )); - - Balances::set_lock(ID_1, &1, 10, WithdrawReasons::TRANSACTION_PAYMENT); - assert_ok!(>::transfer(&1, &2, 1, AllowDeath)); - assert_ok!(>::reserve(&1, 1)); - assert!( as SignedExtension>::pre_dispatch( - ChargeTransactionPayment::from(1), - &1, - CALL, - &info_from_weight(Weight::from_parts(1, 0)), - 1, - ).is_err()); - assert!( as SignedExtension>::pre_dispatch( - ChargeTransactionPayment::from(0), - &1, - CALL, - &info_from_weight(Weight::from_parts(1, 0)), - 1, - ).is_err()); - }); - } - - #[test] - fn lock_block_number_extension_should_work() { - <$ext_builder>::default().existential_deposit(1).monied(true).build().execute_with(|| { - Balances::set_lock(ID_1, &1, 10, WithdrawReasons::all()); - assert_noop!( - >::transfer(&1, &2, 6, AllowDeath), - Error::<$test, _>::LiquidityRestrictions - ); - Balances::extend_lock(ID_1, &1, 10, WithdrawReasons::all()); - assert_noop!( - >::transfer(&1, &2, 6, AllowDeath), - Error::<$test, _>::LiquidityRestrictions - ); - System::set_block_number(2); - Balances::extend_lock(ID_1, &1, 10, WithdrawReasons::all()); - assert_noop!( - >::transfer(&1, &2, 3, AllowDeath), - Error::<$test, _>::LiquidityRestrictions - ); - }); - } - - #[test] - fn lock_reasons_extension_should_work() { - <$ext_builder>::default().existential_deposit(1).monied(true).build().execute_with(|| { - Balances::set_lock(ID_1, &1, 10, WithdrawReasons::TRANSFER); - assert_noop!( - >::transfer(&1, &2, 6, AllowDeath), - Error::<$test, _>::LiquidityRestrictions - ); - Balances::extend_lock(ID_1, &1, 10, WithdrawReasons::empty()); - assert_noop!( - >::transfer(&1, &2, 6, AllowDeath), - Error::<$test, _>::LiquidityRestrictions - ); - Balances::extend_lock(ID_1, &1, 10, WithdrawReasons::RESERVE); - assert_noop!( - >::transfer(&1, &2, 6, AllowDeath), - Error::<$test, _>::LiquidityRestrictions - ); - }); - } - - #[test] - fn default_indexing_on_new_accounts_should_not_work2() { - <$ext_builder>::default() - .existential_deposit(10) - .monied(true) - .build() - .execute_with(|| { - // account 5 should not exist - // ext_deposit is 10, value is 9, not satisfies for ext_deposit - assert_noop!( - Balances::transfer(Some(1).into(), 5, 9), - Error::<$test, _>::ExistentialDeposit, - ); - assert_eq!(Balances::free_balance(1), 100); - }); - } - - #[test] - fn reserved_balance_should_prevent_reclaim_count() { - <$ext_builder>::default() - .existential_deposit(256 * 1) - .monied(true) - .build() - .execute_with(|| { - System::inc_account_nonce(&2); - assert_eq!(Balances::total_balance(&2), 256 * 20); - - assert_ok!(Balances::reserve(&2, 256 * 19 + 1)); // account 2 becomes mostly reserved - assert_eq!(Balances::free_balance(2), 255); // "free" account deleted." - assert_eq!(Balances::total_balance(&2), 256 * 20); // reserve still exists. - assert_eq!(System::account_nonce(&2), 1); - - // account 4 tries to take index 1 for account 5. - assert_ok!(Balances::transfer(Some(4).into(), 5, 256 * 1 + 0x69)); - assert_eq!(Balances::total_balance(&5), 256 * 1 + 0x69); - - assert!(Balances::slash(&2, 256 * 19 + 2).1.is_zero()); // account 2 gets slashed - // "reserve" account reduced to 255 (below ED) so account deleted - assert_eq!(Balances::total_balance(&2), 0); - assert_eq!(System::account_nonce(&2), 0); // nonce zero - - // account 4 tries to take index 1 again for account 6. - assert_ok!(Balances::transfer(Some(4).into(), 6, 256 * 1 + 0x69)); - assert_eq!(Balances::total_balance(&6), 256 * 1 + 0x69); - }); - } - - #[test] - fn reward_should_work() { - <$ext_builder>::default().monied(true).build().execute_with(|| { - assert_eq!(Balances::total_balance(&1), 10); - assert_ok!(Balances::deposit_into_existing(&1, 10).map(drop)); - System::assert_last_event(RuntimeEvent::Balances(crate::Event::Deposit { who: 1, amount: 10 })); - assert_eq!(Balances::total_balance(&1), 20); - assert_eq!(>::get(), 120); - }); - } - - #[test] - fn dust_account_removal_should_work() { - <$ext_builder>::default() - .existential_deposit(100) - .monied(true) - .build() - .execute_with(|| { - System::inc_account_nonce(&2); - assert_eq!(System::account_nonce(&2), 1); - assert_eq!(Balances::total_balance(&2), 2000); - // index 1 (account 2) becomes zombie - assert_ok!(Balances::transfer(Some(2).into(), 5, 1901)); - assert_eq!(Balances::total_balance(&2), 0); - assert_eq!(Balances::total_balance(&5), 1901); - assert_eq!(System::account_nonce(&2), 0); - }); - } - - #[test] - fn balance_works() { - <$ext_builder>::default().build().execute_with(|| { - let _ = Balances::deposit_creating(&1, 42); - System::assert_has_event(RuntimeEvent::Balances(crate::Event::Deposit { who: 1, amount: 42 })); - assert_eq!(Balances::free_balance(1), 42); - assert_eq!(Balances::reserved_balance(1), 0); - assert_eq!(Balances::total_balance(&1), 42); - assert_eq!(Balances::free_balance(2), 0); - assert_eq!(Balances::reserved_balance(2), 0); - assert_eq!(Balances::total_balance(&2), 0); - }); - } - - #[test] - fn balance_transfer_works() { - <$ext_builder>::default().build().execute_with(|| { - let _ = Balances::deposit_creating(&1, 111); - assert_ok!(Balances::transfer(Some(1).into(), 2, 69)); - assert_eq!(Balances::total_balance(&1), 42); - assert_eq!(Balances::total_balance(&2), 69); - }); - } - - #[test] - fn force_transfer_works() { - <$ext_builder>::default().build().execute_with(|| { - let _ = Balances::deposit_creating(&1, 111); - assert_noop!( - Balances::force_transfer(Some(2).into(), 1, 2, 69), - BadOrigin, - ); - assert_ok!(Balances::force_transfer(RawOrigin::Root.into(), 1, 2, 69)); - assert_eq!(Balances::total_balance(&1), 42); - assert_eq!(Balances::total_balance(&2), 69); - }); - } - - #[test] - fn reserving_balance_should_work() { - <$ext_builder>::default().build().execute_with(|| { - let _ = Balances::deposit_creating(&1, 111); - - assert_eq!(Balances::total_balance(&1), 111); - assert_eq!(Balances::free_balance(1), 111); - assert_eq!(Balances::reserved_balance(1), 0); - - assert_ok!(Balances::reserve(&1, 69)); - - assert_eq!(Balances::total_balance(&1), 111); - assert_eq!(Balances::free_balance(1), 42); - assert_eq!(Balances::reserved_balance(1), 69); - }); - } - - #[test] - fn balance_transfer_when_reserved_should_not_work() { - <$ext_builder>::default().build().execute_with(|| { - let _ = Balances::deposit_creating(&1, 111); - assert_ok!(Balances::reserve(&1, 69)); - assert_noop!( - Balances::transfer(Some(1).into(), 2, 69), - Error::<$test, _>::InsufficientBalance, - ); - }); - } - - #[test] - fn deducting_balance_should_work() { - <$ext_builder>::default().build().execute_with(|| { - let _ = Balances::deposit_creating(&1, 111); - assert_ok!(Balances::reserve(&1, 69)); - assert_eq!(Balances::free_balance(1), 42); - }); - } - - #[test] - fn refunding_balance_should_work() { - <$ext_builder>::default().build().execute_with(|| { - let _ = Balances::deposit_creating(&1, 42); - assert_ok!(Balances::mutate_account(&1, |a| a.reserved = 69)); - Balances::unreserve(&1, 69); - assert_eq!(Balances::free_balance(1), 111); - assert_eq!(Balances::reserved_balance(1), 0); - }); - } - - #[test] - fn slashing_balance_should_work() { - <$ext_builder>::default().build().execute_with(|| { - let _ = Balances::deposit_creating(&1, 111); - assert_ok!(Balances::reserve(&1, 69)); - assert!(Balances::slash(&1, 69).1.is_zero()); - assert_eq!(Balances::free_balance(1), 0); - assert_eq!(Balances::reserved_balance(1), 42); - assert_eq!(>::get(), 42); - }); - } - - #[test] - fn withdrawing_balance_should_work() { - <$ext_builder>::default().build().execute_with(|| { - let _ = Balances::deposit_creating(&2, 111); - let _ = Balances::withdraw( - &2, 11, WithdrawReasons::TRANSFER, ExistenceRequirement::KeepAlive - ); - System::assert_last_event(RuntimeEvent::Balances(crate::Event::Withdraw { who: 2, amount: 11 })); - assert_eq!(Balances::free_balance(2), 100); - assert_eq!(>::get(), 100); - }); - } - - #[test] - fn slashing_incomplete_balance_should_work() { - <$ext_builder>::default().build().execute_with(|| { - let _ = Balances::deposit_creating(&1, 42); - assert_ok!(Balances::reserve(&1, 21)); - assert_eq!(Balances::slash(&1, 69).1, 27); - assert_eq!(Balances::free_balance(1), 0); - assert_eq!(Balances::reserved_balance(1), 0); - assert_eq!(>::get(), 0); - }); - } - - #[test] - fn unreserving_balance_should_work() { - <$ext_builder>::default().build().execute_with(|| { - let _ = Balances::deposit_creating(&1, 111); - assert_ok!(Balances::reserve(&1, 111)); - Balances::unreserve(&1, 42); - assert_eq!(Balances::reserved_balance(1), 69); - assert_eq!(Balances::free_balance(1), 42); - }); - } - - #[test] - fn slashing_reserved_balance_should_work() { - <$ext_builder>::default().build().execute_with(|| { - let _ = Balances::deposit_creating(&1, 111); - assert_ok!(Balances::reserve(&1, 111)); - assert_eq!(Balances::slash_reserved(&1, 42).1, 0); - assert_eq!(Balances::reserved_balance(1), 69); - assert_eq!(Balances::free_balance(1), 0); - assert_eq!(>::get(), 69); - }); - } - - #[test] - fn slashing_incomplete_reserved_balance_should_work() { - <$ext_builder>::default().build().execute_with(|| { - let _ = Balances::deposit_creating(&1, 111); - assert_ok!(Balances::reserve(&1, 42)); - assert_eq!(Balances::slash_reserved(&1, 69).1, 27); - assert_eq!(Balances::free_balance(1), 69); - assert_eq!(Balances::reserved_balance(1), 0); - assert_eq!(>::get(), 69); - }); - } - - #[test] - fn repatriating_reserved_balance_should_work() { - <$ext_builder>::default().build().execute_with(|| { - let _ = Balances::deposit_creating(&1, 110); - let _ = Balances::deposit_creating(&2, 1); - assert_ok!(Balances::reserve(&1, 110)); - assert_ok!(Balances::repatriate_reserved(&1, &2, 41, Status::Free), 0); - System::assert_last_event( - RuntimeEvent::Balances(crate::Event::ReserveRepatriated { from: 1, to: 2, amount: 41, destination_status: Status::Free }) - ); - assert_eq!(Balances::reserved_balance(1), 69); - assert_eq!(Balances::free_balance(1), 0); - assert_eq!(Balances::reserved_balance(2), 0); - assert_eq!(Balances::free_balance(2), 42); - }); - } - - #[test] - fn transferring_reserved_balance_should_work() { - <$ext_builder>::default().build().execute_with(|| { - let _ = Balances::deposit_creating(&1, 110); - let _ = Balances::deposit_creating(&2, 1); - assert_ok!(Balances::reserve(&1, 110)); - assert_ok!(Balances::repatriate_reserved(&1, &2, 41, Status::Reserved), 0); - assert_eq!(Balances::reserved_balance(1), 69); - assert_eq!(Balances::free_balance(1), 0); - assert_eq!(Balances::reserved_balance(2), 41); - assert_eq!(Balances::free_balance(2), 1); - }); - } - - #[test] - fn transferring_reserved_balance_to_yourself_should_work() { - <$ext_builder>::default().build().execute_with(|| { - let _ = Balances::deposit_creating(&1, 110); - assert_ok!(Balances::reserve(&1, 50)); - assert_ok!(Balances::repatriate_reserved(&1, &1, 50, Status::Free), 0); - assert_eq!(Balances::free_balance(1), 110); - assert_eq!(Balances::reserved_balance(1), 0); - - assert_ok!(Balances::reserve(&1, 50)); - assert_ok!(Balances::repatriate_reserved(&1, &1, 60, Status::Free), 10); - assert_eq!(Balances::free_balance(1), 110); - assert_eq!(Balances::reserved_balance(1), 0); - }); - } - - #[test] - fn transferring_reserved_balance_to_nonexistent_should_fail() { - <$ext_builder>::default().build().execute_with(|| { - let _ = Balances::deposit_creating(&1, 111); - assert_ok!(Balances::reserve(&1, 111)); - assert_noop!(Balances::repatriate_reserved(&1, &2, 42, Status::Free), Error::<$test, _>::DeadAccount); - }); - } - - #[test] - fn transferring_incomplete_reserved_balance_should_work() { - <$ext_builder>::default().build().execute_with(|| { - let _ = Balances::deposit_creating(&1, 110); - let _ = Balances::deposit_creating(&2, 1); - assert_ok!(Balances::reserve(&1, 41)); - assert_ok!(Balances::repatriate_reserved(&1, &2, 69, Status::Free), 28); - assert_eq!(Balances::reserved_balance(1), 0); - assert_eq!(Balances::free_balance(1), 69); - assert_eq!(Balances::reserved_balance(2), 0); - assert_eq!(Balances::free_balance(2), 42); - }); - } - - #[test] - fn transferring_too_high_value_should_not_panic() { - <$ext_builder>::default().build().execute_with(|| { - Balances::make_free_balance_be(&1, u64::MAX); - Balances::make_free_balance_be(&2, 1); - - assert_err!( - Balances::transfer(Some(1).into(), 2, u64::MAX), - ArithmeticError::Overflow, - ); - - assert_eq!(Balances::free_balance(1), u64::MAX); - assert_eq!(Balances::free_balance(2), 1); - }); - } - - #[test] - fn account_create_on_free_too_low_with_other() { - <$ext_builder>::default().existential_deposit(100).build().execute_with(|| { - let _ = Balances::deposit_creating(&1, 100); - assert_eq!(>::get(), 100); - - // No-op. - let _ = Balances::deposit_creating(&2, 50); - assert_eq!(Balances::free_balance(2), 0); - assert_eq!(>::get(), 100); - }) - } - - #[test] - fn account_create_on_free_too_low() { - <$ext_builder>::default().existential_deposit(100).build().execute_with(|| { - // No-op. - let _ = Balances::deposit_creating(&2, 50); - assert_eq!(Balances::free_balance(2), 0); - assert_eq!(>::get(), 0); - }) - } - - #[test] - fn account_removal_on_free_too_low() { - <$ext_builder>::default().existential_deposit(100).build().execute_with(|| { - assert_eq!(>::get(), 0); - - // Setup two accounts with free balance above the existential threshold. - let _ = Balances::deposit_creating(&1, 110); - let _ = Balances::deposit_creating(&2, 110); - - assert_eq!(Balances::free_balance(1), 110); - assert_eq!(Balances::free_balance(2), 110); - assert_eq!(>::get(), 220); - - // Transfer funds from account 1 of such amount that after this transfer - // the balance of account 1 will be below the existential threshold. - // This should lead to the removal of all balance of this account. - assert_ok!(Balances::transfer(Some(1).into(), 2, 20)); - - // Verify free balance removal of account 1. - assert_eq!(Balances::free_balance(1), 0); - assert_eq!(Balances::free_balance(2), 130); - - // Verify that TotalIssuance tracks balance removal when free balance is too low. - assert_eq!(>::get(), 130); - }); - } - - #[test] - fn burn_must_work() { - <$ext_builder>::default().monied(true).build().execute_with(|| { - let init_total_issuance = Balances::total_issuance(); - let imbalance = Balances::burn(10); - assert_eq!(Balances::total_issuance(), init_total_issuance - 10); - drop(imbalance); - assert_eq!(Balances::total_issuance(), init_total_issuance); - }); - } - - #[test] - fn transfer_keep_alive_works() { - <$ext_builder>::default().existential_deposit(1).build().execute_with(|| { - let _ = Balances::deposit_creating(&1, 100); - assert_noop!( - Balances::transfer_keep_alive(Some(1).into(), 2, 100), - Error::<$test, _>::KeepAlive - ); - assert_eq!(Balances::total_balance(&1), 100); - assert_eq!(Balances::total_balance(&2), 0); - }); - } - - #[test] - #[should_panic = "the balance of any account should always be at least the existential deposit."] - fn cannot_set_genesis_value_below_ed() { - ($existential_deposit).with(|v| *v.borrow_mut() = 11); - let mut t = frame_system::GenesisConfig::default().build_storage::<$test>().unwrap(); - let _ = pallet_balances::GenesisConfig::<$test> { - balances: vec![(1, 10)], - }.assimilate_storage(&mut t).unwrap(); - } - - #[test] - #[should_panic = "duplicate balances in genesis."] - fn cannot_set_genesis_value_twice() { - let mut t = frame_system::GenesisConfig::default().build_storage::<$test>().unwrap(); - let _ = pallet_balances::GenesisConfig::<$test> { - balances: vec![(1, 10), (2, 20), (1, 15)], - }.assimilate_storage(&mut t).unwrap(); - } - - #[test] - fn dust_moves_between_free_and_reserved() { - <$ext_builder>::default() - .existential_deposit(100) - .build() - .execute_with(|| { - // Set balance to free and reserved at the existential deposit - assert_ok!(Balances::set_balance(RawOrigin::Root.into(), 1, 100, 0)); - // Check balance - assert_eq!(Balances::free_balance(1), 100); - assert_eq!(Balances::reserved_balance(1), 0); - - // Reserve some free balance - assert_ok!(Balances::reserve(&1, 50)); - // Check balance, the account should be ok. - assert_eq!(Balances::free_balance(1), 50); - assert_eq!(Balances::reserved_balance(1), 50); - - // Reserve the rest of the free balance - assert_ok!(Balances::reserve(&1, 50)); - // Check balance, the account should be ok. - assert_eq!(Balances::free_balance(1), 0); - assert_eq!(Balances::reserved_balance(1), 100); - - // Unreserve everything - Balances::unreserve(&1, 100); - // Check balance, all 100 should move to free_balance - assert_eq!(Balances::free_balance(1), 100); - assert_eq!(Balances::reserved_balance(1), 0); - }); - } - - #[test] - fn account_deleted_when_just_dust() { - <$ext_builder>::default() - .existential_deposit(100) - .build() - .execute_with(|| { - // Set balance to free and reserved at the existential deposit - assert_ok!(Balances::set_balance(RawOrigin::Root.into(), 1, 50, 50)); - // Check balance - assert_eq!(Balances::free_balance(1), 50); - assert_eq!(Balances::reserved_balance(1), 50); - - // Reserve some free balance - let res = Balances::slash(&1, 1); - assert_eq!(res, (NegativeImbalance::new(1), 0)); - - // The account should be dead. - assert_eq!(Balances::free_balance(1), 0); - assert_eq!(Balances::reserved_balance(1), 0); - }); - } - - #[test] - fn emit_events_with_reserve_and_unreserve() { - <$ext_builder>::default() - .build() - .execute_with(|| { - let _ = Balances::deposit_creating(&1, 100); - - System::set_block_number(2); - assert_ok!(Balances::reserve(&1, 10)); - - System::assert_last_event(RuntimeEvent::Balances(crate::Event::Reserved { who: 1, amount: 10 })); - - System::set_block_number(3); - assert!(Balances::unreserve(&1, 5).is_zero()); - - System::assert_last_event(RuntimeEvent::Balances(crate::Event::Unreserved { who: 1, amount: 5 })); - - System::set_block_number(4); - assert_eq!(Balances::unreserve(&1, 6), 1); - - // should only unreserve 5 - System::assert_last_event(RuntimeEvent::Balances(crate::Event::Unreserved { who: 1, amount: 5 })); - }); - } - - #[test] - fn emit_events_with_existential_deposit() { - <$ext_builder>::default() - .existential_deposit(100) - .build() - .execute_with(|| { - assert_ok!(Balances::set_balance(RawOrigin::Root.into(), 1, 100, 0)); - - assert_eq!( - events(), - [ - RuntimeEvent::System(system::Event::NewAccount { account: 1 }), - RuntimeEvent::Balances(crate::Event::Endowed { account: 1, free_balance: 100 }), - RuntimeEvent::Balances(crate::Event::BalanceSet { who: 1, free: 100, reserved: 0 }), - ] - ); - - let res = Balances::slash(&1, 1); - assert_eq!(res, (NegativeImbalance::new(1), 0)); - - assert_eq!( - events(), - [ - RuntimeEvent::System(system::Event::KilledAccount { account: 1 }), - RuntimeEvent::Balances(crate::Event::DustLost { account: 1, amount: 99 }), - RuntimeEvent::Balances(crate::Event::Slashed { who: 1, amount: 1 }), - ] - ); - }); - } - - #[test] - fn emit_events_with_no_existential_deposit_suicide() { - <$ext_builder>::default() - .existential_deposit(1) - .build() - .execute_with(|| { - assert_ok!(Balances::set_balance(RawOrigin::Root.into(), 1, 100, 0)); - - assert_eq!( - events(), - [ - RuntimeEvent::System(system::Event::NewAccount { account: 1 }), - RuntimeEvent::Balances(crate::Event::Endowed { account: 1, free_balance: 100 }), - RuntimeEvent::Balances(crate::Event::BalanceSet { who: 1, free: 100, reserved: 0 }), - ] - ); - - let res = Balances::slash(&1, 100); - assert_eq!(res, (NegativeImbalance::new(100), 0)); - - assert_eq!( - events(), - [ - RuntimeEvent::System(system::Event::KilledAccount { account: 1 }), - RuntimeEvent::Balances(crate::Event::Slashed { who: 1, amount: 100 }), - ] - ); - }); - } - - #[test] - fn slash_loop_works() { - <$ext_builder>::default() - .existential_deposit(100) - .build() - .execute_with(|| { - /* User has no reference counter, so they can die in these scenarios */ - - // SCENARIO: Slash would not kill account. - assert_ok!(Balances::set_balance(RuntimeOrigin::root(), 1, 1_000, 0)); - // Slashed completed in full - assert_eq!(Balances::slash(&1, 900), (NegativeImbalance::new(900), 0)); - // Account is still alive - assert!(System::account_exists(&1)); - System::assert_last_event(RuntimeEvent::Balances(crate::Event::Slashed { who: 1, amount: 900 })); - - // SCENARIO: Slash will kill account because not enough balance left. - assert_ok!(Balances::set_balance(RuntimeOrigin::root(), 1, 1_000, 0)); - // Slashed completed in full - assert_eq!(Balances::slash(&1, 950), (NegativeImbalance::new(950), 0)); - // Account is killed - assert!(!System::account_exists(&1)); - - // SCENARIO: Over-slash will kill account, and report missing slash amount. - assert_ok!(Balances::set_balance(RuntimeOrigin::root(), 1, 1_000, 0)); - // Slashed full free_balance, and reports 300 not slashed - assert_eq!(Balances::slash(&1, 1_300), (NegativeImbalance::new(1000), 300)); - // Account is dead - assert!(!System::account_exists(&1)); - - // SCENARIO: Over-slash can take from reserved, but keep alive. - assert_ok!(Balances::set_balance(RuntimeOrigin::root(), 1, 1_000, 400)); - // Slashed full free_balance and 300 of reserved balance - assert_eq!(Balances::slash(&1, 1_300), (NegativeImbalance::new(1300), 0)); - // Account is still alive - assert!(System::account_exists(&1)); - - // SCENARIO: Over-slash can take from reserved, and kill. - assert_ok!(Balances::set_balance(RuntimeOrigin::root(), 1, 1_000, 350)); - // Slashed full free_balance and 300 of reserved balance - assert_eq!(Balances::slash(&1, 1_300), (NegativeImbalance::new(1300), 0)); - // Account is dead because 50 reserved balance is not enough to keep alive - assert!(!System::account_exists(&1)); - - // SCENARIO: Over-slash can take as much as possible from reserved, kill, and report missing amount. - assert_ok!(Balances::set_balance(RuntimeOrigin::root(), 1, 1_000, 250)); - // Slashed full free_balance and 300 of reserved balance - assert_eq!(Balances::slash(&1, 1_300), (NegativeImbalance::new(1250), 50)); - // Account is super dead - assert!(!System::account_exists(&1)); - - /* User will now have a reference counter on them, keeping them alive in these scenarios */ - - // SCENARIO: Slash would not kill account. - assert_ok!(Balances::set_balance(RuntimeOrigin::root(), 1, 1_000, 0)); - assert_ok!(System::inc_consumers(&1)); // <-- Reference counter added here is enough for all tests - // Slashed completed in full - assert_eq!(Balances::slash(&1, 900), (NegativeImbalance::new(900), 0)); - // Account is still alive - assert!(System::account_exists(&1)); - - // SCENARIO: Slash will take as much as possible without killing account. - assert_ok!(Balances::set_balance(RuntimeOrigin::root(), 1, 1_000, 0)); - // Slashed completed in full - assert_eq!(Balances::slash(&1, 950), (NegativeImbalance::new(900), 50)); - // Account is still alive - assert!(System::account_exists(&1)); - - // SCENARIO: Over-slash will not kill account, and report missing slash amount. - assert_ok!(Balances::set_balance(RuntimeOrigin::root(), 1, 1_000, 0)); - // Slashed full free_balance minus ED, and reports 400 not slashed - assert_eq!(Balances::slash(&1, 1_300), (NegativeImbalance::new(900), 400)); - // Account is still alive - assert!(System::account_exists(&1)); - - // SCENARIO: Over-slash can take from reserved, but keep alive. - assert_ok!(Balances::set_balance(RuntimeOrigin::root(), 1, 1_000, 400)); - // Slashed full free_balance and 300 of reserved balance - assert_eq!(Balances::slash(&1, 1_300), (NegativeImbalance::new(1300), 0)); - // Account is still alive - assert!(System::account_exists(&1)); - - // SCENARIO: Over-slash can take from reserved, but keep alive. - assert_ok!(Balances::set_balance(RuntimeOrigin::root(), 1, 1_000, 350)); - // Slashed full free_balance and 250 of reserved balance to leave ED - assert_eq!(Balances::slash(&1, 1_300), (NegativeImbalance::new(1250), 50)); - // Account is still alive - assert!(System::account_exists(&1)); - - // SCENARIO: Over-slash can take as much as possible from reserved and report missing amount. - assert_ok!(Balances::set_balance(RuntimeOrigin::root(), 1, 1_000, 250)); - // Slashed full free_balance and 300 of reserved balance - assert_eq!(Balances::slash(&1, 1_300), (NegativeImbalance::new(1150), 150)); - // Account is still alive - assert!(System::account_exists(&1)); - - // Slash on non-existent account is okay. - assert_eq!(Balances::slash(&12345, 1_300), (NegativeImbalance::new(0), 1300)); - }); - } - - #[test] - fn slash_reserved_loop_works() { - <$ext_builder>::default() - .existential_deposit(100) - .build() - .execute_with(|| { - /* User has no reference counter, so they can die in these scenarios */ - - // SCENARIO: Slash would not kill account. - assert_ok!(Balances::set_balance(RuntimeOrigin::root(), 1, 50, 1_000)); - // Slashed completed in full - assert_eq!(Balances::slash_reserved(&1, 900), (NegativeImbalance::new(900), 0)); - // Account is still alive - assert!(System::account_exists(&1)); - - // SCENARIO: Slash would kill account. - assert_ok!(Balances::set_balance(RuntimeOrigin::root(), 1, 50, 1_000)); - // Slashed completed in full - assert_eq!(Balances::slash_reserved(&1, 1_000), (NegativeImbalance::new(1_000), 0)); - // Account is dead - assert!(!System::account_exists(&1)); - - // SCENARIO: Over-slash would kill account, and reports left over slash. - assert_ok!(Balances::set_balance(RuntimeOrigin::root(), 1, 50, 1_000)); - // Slashed completed in full - assert_eq!(Balances::slash_reserved(&1, 1_300), (NegativeImbalance::new(1_000), 300)); - // Account is dead - assert!(!System::account_exists(&1)); - - // SCENARIO: Over-slash does not take from free balance. - assert_ok!(Balances::set_balance(RuntimeOrigin::root(), 1, 300, 1_000)); - // Slashed completed in full - assert_eq!(Balances::slash_reserved(&1, 1_300), (NegativeImbalance::new(1_000), 300)); - // Account is alive because of free balance - assert!(System::account_exists(&1)); - - /* User has a reference counter, so they cannot die */ - - // SCENARIO: Slash would not kill account. - assert_ok!(Balances::set_balance(RuntimeOrigin::root(), 1, 50, 1_000)); - assert_ok!(System::inc_consumers(&1)); // <-- Reference counter added here is enough for all tests - // Slashed completed in full - assert_eq!(Balances::slash_reserved(&1, 900), (NegativeImbalance::new(900), 0)); - // Account is still alive - assert!(System::account_exists(&1)); - - // SCENARIO: Slash as much as possible without killing. - assert_ok!(Balances::set_balance(RuntimeOrigin::root(), 1, 50, 1_000)); - // Slashed as much as possible - assert_eq!(Balances::slash_reserved(&1, 1_000), (NegativeImbalance::new(950), 50)); - // Account is still alive - assert!(System::account_exists(&1)); - - // SCENARIO: Over-slash reports correctly, where reserved is needed to keep alive. - assert_ok!(Balances::set_balance(RuntimeOrigin::root(), 1, 50, 1_000)); - // Slashed as much as possible - assert_eq!(Balances::slash_reserved(&1, 1_300), (NegativeImbalance::new(950), 350)); - // Account is still alive - assert!(System::account_exists(&1)); - - // SCENARIO: Over-slash reports correctly, where full reserved is removed. - assert_ok!(Balances::set_balance(RuntimeOrigin::root(), 1, 200, 1_000)); - // Slashed as much as possible - assert_eq!(Balances::slash_reserved(&1, 1_300), (NegativeImbalance::new(1_000), 300)); - // Account is still alive - assert!(System::account_exists(&1)); - - // Slash on non-existent account is okay. - assert_eq!(Balances::slash_reserved(&12345, 1_300), (NegativeImbalance::new(0), 1300)); - }); - } - - #[test] - fn operations_on_dead_account_should_not_change_state() { - // These functions all use `mutate_account` which may introduce a storage change when - // the account never existed to begin with, and shouldn't exist in the end. - <$ext_builder>::default() - .existential_deposit(0) - .build() - .execute_with(|| { - assert!(!frame_system::Account::::contains_key(&1337)); - - // Unreserve - assert_storage_noop!(assert_eq!(Balances::unreserve(&1337, 42), 42)); - // Reserve - assert_noop!(Balances::reserve(&1337, 42), Error::::InsufficientBalance); - // Slash Reserve - assert_storage_noop!(assert_eq!(Balances::slash_reserved(&1337, 42).1, 42)); - // Repatriate Reserve - assert_noop!(Balances::repatriate_reserved(&1337, &1338, 42, Status::Free), Error::::DeadAccount); - // Slash - assert_storage_noop!(assert_eq!(Balances::slash(&1337, 42).1, 42)); - }); - } - - #[test] - fn transfer_keep_alive_all_free_succeed() { - <$ext_builder>::default() - .existential_deposit(100) - .build() - .execute_with(|| { - assert_ok!(Balances::set_balance(RuntimeOrigin::root(), 1, 100, 100)); - assert_ok!(Balances::transfer_keep_alive(Some(1).into(), 2, 100)); - assert_eq!(Balances::total_balance(&1), 100); - assert_eq!(Balances::total_balance(&2), 100); - }); - } - - #[test] - fn transfer_all_works() { - <$ext_builder>::default() - .existential_deposit(100) - .build() - .execute_with(|| { - // setup - assert_ok!(Balances::set_balance(RuntimeOrigin::root(), 1, 200, 0)); - assert_ok!(Balances::set_balance(RuntimeOrigin::root(), 2, 0, 0)); - // transfer all and allow death - assert_ok!(Balances::transfer_all(Some(1).into(), 2, false)); - assert_eq!(Balances::total_balance(&1), 0); - assert_eq!(Balances::total_balance(&2), 200); - - // setup - assert_ok!(Balances::set_balance(RuntimeOrigin::root(), 1, 200, 0)); - assert_ok!(Balances::set_balance(RuntimeOrigin::root(), 2, 0, 0)); - // transfer all and keep alive - assert_ok!(Balances::transfer_all(Some(1).into(), 2, true)); - assert_eq!(Balances::total_balance(&1), 100); - assert_eq!(Balances::total_balance(&2), 100); - - // setup - assert_ok!(Balances::set_balance(RuntimeOrigin::root(), 1, 200, 10)); - assert_ok!(Balances::set_balance(RuntimeOrigin::root(), 2, 0, 0)); - // transfer all and allow death w/ reserved - assert_ok!(Balances::transfer_all(Some(1).into(), 2, false)); - assert_eq!(Balances::total_balance(&1), 0); - assert_eq!(Balances::total_balance(&2), 200); - - // setup - assert_ok!(Balances::set_balance(RuntimeOrigin::root(), 1, 200, 10)); - assert_ok!(Balances::set_balance(RuntimeOrigin::root(), 2, 0, 0)); - // transfer all and keep alive w/ reserved - assert_ok!(Balances::transfer_all(Some(1).into(), 2, true)); - assert_eq!(Balances::total_balance(&1), 100); - assert_eq!(Balances::total_balance(&2), 110); - }); - } - - #[test] - fn named_reserve_should_work() { - <$ext_builder>::default().build().execute_with(|| { - let _ = Balances::deposit_creating(&1, 111); - - let id_1 = [1u8; 8]; - let id_2 = [2u8; 8]; - let id_3 = [3u8; 8]; - - // reserve - - assert_noop!(Balances::reserve_named(&id_1, &1, 112), Error::::InsufficientBalance); - - assert_ok!(Balances::reserve_named(&id_1, &1, 12)); - - assert_eq!(Balances::reserved_balance(1), 12); - assert_eq!(Balances::reserved_balance_named(&id_1, &1), 12); - assert_eq!(Balances::reserved_balance_named(&id_2, &1), 0); - - assert_ok!(Balances::reserve_named(&id_1, &1, 2)); - - assert_eq!(Balances::reserved_balance(1), 14); - assert_eq!(Balances::reserved_balance_named(&id_1, &1), 14); - assert_eq!(Balances::reserved_balance_named(&id_2, &1), 0); - - assert_ok!(Balances::reserve_named(&id_2, &1, 23)); - - assert_eq!(Balances::reserved_balance(1), 37); - assert_eq!(Balances::reserved_balance_named(&id_1, &1), 14); - assert_eq!(Balances::reserved_balance_named(&id_2, &1), 23); - - assert_ok!(Balances::reserve(&1, 34)); - - assert_eq!(Balances::reserved_balance(1), 71); - assert_eq!(Balances::reserved_balance_named(&id_1, &1), 14); - assert_eq!(Balances::reserved_balance_named(&id_2, &1), 23); - - assert_eq!(Balances::total_balance(&1), 111); - assert_eq!(Balances::free_balance(1), 40); - - assert_noop!(Balances::reserve_named(&id_3, &1, 2), Error::::TooManyReserves); - - // unreserve - - assert_eq!(Balances::unreserve_named(&id_1, &1, 10), 0); - - assert_eq!(Balances::reserved_balance(1), 61); - assert_eq!(Balances::reserved_balance_named(&id_1, &1), 4); - assert_eq!(Balances::reserved_balance_named(&id_2, &1), 23); - - assert_eq!(Balances::unreserve_named(&id_1, &1, 5), 1); - - assert_eq!(Balances::reserved_balance(1), 57); - assert_eq!(Balances::reserved_balance_named(&id_1, &1), 0); - assert_eq!(Balances::reserved_balance_named(&id_2, &1), 23); - - assert_eq!(Balances::unreserve_named(&id_2, &1, 3), 0); - - assert_eq!(Balances::reserved_balance(1), 54); - assert_eq!(Balances::reserved_balance_named(&id_1, &1), 0); - assert_eq!(Balances::reserved_balance_named(&id_2, &1), 20); - - assert_eq!(Balances::total_balance(&1), 111); - assert_eq!(Balances::free_balance(1), 57); - - // slash_reserved_named - - assert_ok!(Balances::reserve_named(&id_1, &1, 10)); - - assert_eq!(Balances::slash_reserved_named(&id_1, &1, 25).1, 15); - - assert_eq!(Balances::reserved_balance(1), 54); - assert_eq!(Balances::reserved_balance_named(&id_1, &1), 0); - assert_eq!(Balances::reserved_balance_named(&id_2, &1), 20); - assert_eq!(Balances::total_balance(&1), 101); - - assert_eq!(Balances::slash_reserved_named(&id_2, &1, 5).1, 0); - - assert_eq!(Balances::reserved_balance(1), 49); - assert_eq!(Balances::reserved_balance_named(&id_1, &1), 0); - assert_eq!(Balances::reserved_balance_named(&id_2, &1), 15); - assert_eq!(Balances::total_balance(&1), 96); - - // repatriate_reserved_named - - let _ = Balances::deposit_creating(&2, 100); - - assert_eq!(Balances::repatriate_reserved_named(&id_2, &1, &2, 10, Status::Reserved).unwrap(), 0); - - assert_eq!(Balances::reserved_balance_named(&id_2, &1), 5); - assert_eq!(Balances::reserved_balance_named(&id_2, &2), 10); - assert_eq!(Balances::reserved_balance(&2), 10); - - assert_eq!(Balances::repatriate_reserved_named(&id_2, &2, &1, 11, Status::Reserved).unwrap(), 1); - - assert_eq!(Balances::reserved_balance_named(&id_2, &1), 15); - assert_eq!(Balances::reserved_balance_named(&id_2, &2), 0); - assert_eq!(Balances::reserved_balance(&2), 0); - - assert_eq!(Balances::repatriate_reserved_named(&id_2, &1, &2, 10, Status::Free).unwrap(), 0); - assert_eq!(Balances::reserved_balance_named(&id_2, &1), 5); - assert_eq!(Balances::reserved_balance_named(&id_2, &2), 0); - assert_eq!(Balances::free_balance(&2), 110); - - // repatriate_reserved_named to self - - assert_eq!(Balances::repatriate_reserved_named(&id_2, &1, &1, 10, Status::Reserved).unwrap(), 5); - assert_eq!(Balances::reserved_balance_named(&id_2, &1), 5); - - assert_eq!(Balances::free_balance(&1), 47); - - assert_eq!(Balances::repatriate_reserved_named(&id_2, &1, &1, 15, Status::Free).unwrap(), 10); - assert_eq!(Balances::reserved_balance_named(&id_2, &1), 0); - - assert_eq!(Balances::free_balance(&1), 52); - }); - } - - #[test] - fn reserved_named_to_yourself_should_work() { - <$ext_builder>::default().build().execute_with(|| { - let _ = Balances::deposit_creating(&1, 110); - - let id = [1u8; 8]; - - assert_ok!(Balances::reserve_named(&id, &1, 50)); - assert_ok!(Balances::repatriate_reserved_named(&id, &1, &1, 50, Status::Free), 0); - assert_eq!(Balances::free_balance(1), 110); - assert_eq!(Balances::reserved_balance_named(&id, &1), 0); - - assert_ok!(Balances::reserve_named(&id, &1, 50)); - assert_ok!(Balances::repatriate_reserved_named(&id, &1, &1, 60, Status::Free), 10); - assert_eq!(Balances::free_balance(1), 110); - assert_eq!(Balances::reserved_balance_named(&id, &1), 0); - }); - } - - #[test] - fn ensure_reserved_named_should_work() { - <$ext_builder>::default().build().execute_with(|| { - let _ = Balances::deposit_creating(&1, 111); - - let id = [1u8; 8]; - - assert_ok!(Balances::ensure_reserved_named(&id, &1, 15)); - assert_eq!(Balances::reserved_balance_named(&id, &1), 15); - - assert_ok!(Balances::ensure_reserved_named(&id, &1, 10)); - assert_eq!(Balances::reserved_balance_named(&id, &1), 10); - - assert_ok!(Balances::ensure_reserved_named(&id, &1, 20)); - assert_eq!(Balances::reserved_balance_named(&id, &1), 20); - }); - } - - #[test] - fn unreserve_all_named_should_work() { - <$ext_builder>::default().build().execute_with(|| { - let _ = Balances::deposit_creating(&1, 111); - - let id = [1u8; 8]; - - assert_ok!(Balances::reserve_named(&id, &1, 15)); - - assert_eq!(Balances::unreserve_all_named(&id, &1), 15); - assert_eq!(Balances::reserved_balance_named(&id, &1), 0); - assert_eq!(Balances::free_balance(&1), 111); - - assert_eq!(Balances::unreserve_all_named(&id, &1), 0); - }); - } - - #[test] - fn slash_all_reserved_named_should_work() { - <$ext_builder>::default().build().execute_with(|| { - let _ = Balances::deposit_creating(&1, 111); - - let id = [1u8; 8]; - - assert_ok!(Balances::reserve_named(&id, &1, 15)); - - assert_eq!(Balances::slash_all_reserved_named(&id, &1).peek(), 15); - assert_eq!(Balances::reserved_balance_named(&id, &1), 0); - assert_eq!(Balances::free_balance(&1), 96); - - assert_eq!(Balances::slash_all_reserved_named(&id, &1).peek(), 0); - }); - } - - #[test] - fn repatriate_all_reserved_named_should_work() { - <$ext_builder>::default().build().execute_with(|| { - let _ = Balances::deposit_creating(&1, 111); - let _ = Balances::deposit_creating(&2, 10); - let _ = Balances::deposit_creating(&3, 10); - - let id = [1u8; 8]; - - assert_ok!(Balances::reserve_named(&id, &1, 15)); - - assert_ok!(Balances::repatriate_all_reserved_named(&id, &1, &2, Status::Reserved)); - assert_eq!(Balances::reserved_balance_named(&id, &1), 0); - assert_eq!(Balances::reserved_balance_named(&id, &2), 15); - - assert_ok!(Balances::repatriate_all_reserved_named(&id, &2, &3, Status::Free)); - assert_eq!(Balances::reserved_balance_named(&id, &2), 0); - assert_eq!(Balances::free_balance(&3), 25); - }); - } - - #[test] - fn set_balance_handles_killing_account() { - <$ext_builder>::default().build().execute_with(|| { - let _ = Balances::deposit_creating(&1, 111); - assert_ok!(frame_system::Pallet::::inc_consumers(&1)); - assert_noop!( - Balances::set_balance(RuntimeOrigin::root(), 1, 0, 0), - DispatchError::ConsumerRemaining, - ); - }); - } - - #[test] - fn set_balance_handles_total_issuance() { - <$ext_builder>::default().build().execute_with(|| { - let old_total_issuance = Balances::total_issuance(); - assert_ok!(Balances::set_balance(RuntimeOrigin::root(), 1337, 69, 42)); - assert_eq!(Balances::total_issuance(), old_total_issuance + 69 + 42); - assert_eq!(Balances::total_balance(&1337), 69 + 42); - assert_eq!(Balances::free_balance(&1337), 69); - assert_eq!(Balances::reserved_balance(&1337), 42); - }); - } - - #[test] - fn fungible_unbalanced_trait_set_balance_works() { - <$ext_builder>::default().build().execute_with(|| { - assert_eq!(>::balance(&1337), 0); - assert_ok!(>::set_balance(&1337, 100)); - assert_eq!(>::balance(&1337), 100); - - assert_ok!(Balances::reserve(&1337, 60)); - assert_eq!(Balances::free_balance(1337) , 40); - assert_eq!(Balances::reserved_balance(1337), 60); - - assert_noop!(>::set_balance(&1337, 0), ArithmeticError::Underflow); - - assert_ok!(>::set_balance(&1337, 60)); - assert_eq!(Balances::free_balance(1337) , 0); - assert_eq!(Balances::reserved_balance(1337), 60); - }); - } - - #[test] - fn fungible_unbalanced_trait_set_total_issuance_works() { - <$ext_builder>::default().build().execute_with(|| { - assert_eq!(>::total_issuance(), 0); - >::set_total_issuance(100); - assert_eq!(>::total_issuance(), 100); - }); - } - - #[test] - fn fungible_unbalanced_trait_decrease_balance_simple_works() { - <$ext_builder>::default().build().execute_with(|| { - // An Account that starts at 100 - assert_ok!(>::set_balance(&1337, 100)); - // and reserves 50 - assert_ok!(Balances::reserve(&1337, 50)); - // and is decreased by 20 - assert_ok!(>::decrease_balance(&1337, 20)); - // should end up at 80. - assert_eq!(>::balance(&1337), 80); - }); - } - - #[test] - fn fungible_unbalanced_trait_decrease_balance_works() { - <$ext_builder>::default().build().execute_with(|| { - assert_ok!(>::set_balance(&1337, 100)); - assert_eq!(>::balance(&1337), 100); - - assert_noop!( - >::decrease_balance(&1337, 101), - TokenError::NoFunds - ); - assert_eq!( - >::decrease_balance(&1337, 100), - Ok(100) - ); - assert_eq!(>::balance(&1337), 0); - - // free: 40, reserved: 60 - assert_ok!(>::set_balance(&1337, 100)); - assert_ok!(Balances::reserve(&1337, 60)); - assert_eq!(Balances::free_balance(1337) , 40); - assert_eq!(Balances::reserved_balance(1337), 60); - assert_noop!( - >::decrease_balance(&1337, 41), - TokenError::NoFunds - ); - assert_eq!( - >::decrease_balance(&1337, 40), - Ok(40) - ); - assert_eq!(>::balance(&1337), 60); - assert_eq!(Balances::free_balance(1337), 0); - assert_eq!(Balances::reserved_balance(1337), 60); - }); - } - - #[test] - fn fungible_unbalanced_trait_decrease_balance_at_most_works() { - <$ext_builder>::default().build().execute_with(|| { - assert_ok!(>::set_balance(&1337, 100)); - assert_eq!(>::balance(&1337), 100); - - assert_eq!( - >::decrease_balance_at_most(&1337, 101), - 100 - ); - assert_eq!(>::balance(&1337), 0); - - assert_ok!(>::set_balance(&1337, 100)); - assert_eq!( - >::decrease_balance_at_most(&1337, 100), - 100 - ); - assert_eq!(>::balance(&1337), 0); - - // free: 40, reserved: 60 - assert_ok!(>::set_balance(&1337, 100)); - assert_ok!(Balances::reserve(&1337, 60)); - assert_eq!(Balances::free_balance(1337) , 40); - assert_eq!(Balances::reserved_balance(1337), 60); - assert_eq!( - >::decrease_balance_at_most(&1337, 0), - 0 - ); - assert_eq!(Balances::free_balance(1337) , 40); - assert_eq!(Balances::reserved_balance(1337), 60); - assert_eq!( - >::decrease_balance_at_most(&1337, 10), - 10 - ); - assert_eq!(Balances::free_balance(1337), 30); - assert_eq!( - >::decrease_balance_at_most(&1337, 200), - 30 - ); - assert_eq!(>::balance(&1337), 60); - assert_eq!(Balances::free_balance(1337), 0); - assert_eq!(Balances::reserved_balance(1337), 60); - }); - } - - #[test] - fn fungible_unbalanced_trait_increase_balance_works() { - <$ext_builder>::default().build().execute_with(|| { - assert_noop!( - >::increase_balance(&1337, 0), - TokenError::BelowMinimum - ); - assert_eq!( - >::increase_balance(&1337, 1), - Ok(1) - ); - assert_noop!( - >::increase_balance(&1337, u64::MAX), - ArithmeticError::Overflow - ); - }); - } - - #[test] - fn fungible_unbalanced_trait_increase_balance_at_most_works() { - <$ext_builder>::default().build().execute_with(|| { - assert_eq!( - >::increase_balance_at_most(&1337, 0), - 0 - ); - assert_eq!( - >::increase_balance_at_most(&1337, 1), - 1 - ); - assert_eq!( - >::increase_balance_at_most(&1337, u64::MAX), - u64::MAX - 1 - ); - }); - } - } -} diff --git a/frame/balances/src/tests/currency_tests.rs b/frame/balances/src/tests/currency_tests.rs new file mode 100644 index 000000000..b0a38f4ac --- /dev/null +++ b/frame/balances/src/tests/currency_tests.rs @@ -0,0 +1,1231 @@ +// This file is part of Substrate. + +// Copyright (C) 2017-2022 Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +//! Tests regarding the functionality of the `Currency` trait set implementations. + +use super::*; +use crate::NegativeImbalance; +use frame_support::traits::{ + BalanceStatus::{Free, Reserved}, + Currency, + ExistenceRequirement::{self, AllowDeath}, + LockIdentifier, LockableCurrency, NamedReservableCurrency, ReservableCurrency, WithdrawReasons, +}; + +const ID_1: LockIdentifier = *b"1 "; +const ID_2: LockIdentifier = *b"2 "; + +pub const CALL: &::RuntimeCall = + &RuntimeCall::Balances(crate::Call::transfer_allow_death { dest: 0, value: 0 }); + +#[test] +fn basic_locking_should_work() { + ExtBuilder::default() + .existential_deposit(1) + .monied(true) + .build_and_execute_with(|| { + assert_eq!(Balances::free_balance(1), 10); + Balances::set_lock(ID_1, &1, 9, WithdrawReasons::all()); + assert_noop!( + >::transfer(&1, &2, 5, AllowDeath), + TokenError::Frozen + ); + }); +} + +#[test] +fn account_should_be_reaped() { + ExtBuilder::default() + .existential_deposit(1) + .monied(true) + .build_and_execute_with(|| { + assert_eq!(Balances::free_balance(1), 10); + assert_ok!(>::transfer(&1, &2, 10, AllowDeath)); + assert_eq!(System::providers(&1), 0); + assert_eq!(System::consumers(&1), 0); + // Check that the account is dead. + assert!(!frame_system::Account::::contains_key(&1)); + }); +} + +#[test] +fn reap_failed_due_to_provider_and_consumer() { + ExtBuilder::default() + .existential_deposit(1) + .monied(true) + .build_and_execute_with(|| { + // SCENARIO: only one provider and there are remaining consumers. + assert_ok!(System::inc_consumers(&1)); + assert!(!System::can_dec_provider(&1)); + assert_noop!( + >::transfer(&1, &2, 10, AllowDeath), + TokenError::Frozen + ); + assert!(System::account_exists(&1)); + assert_eq!(Balances::free_balance(1), 10); + + // SCENARIO: more than one provider, but will not kill account due to other provider. + assert_eq!(System::inc_providers(&1), frame_system::IncRefStatus::Existed); + assert_eq!(System::providers(&1), 2); + assert!(System::can_dec_provider(&1)); + assert_ok!(>::transfer(&1, &2, 10, AllowDeath)); + assert_eq!(System::providers(&1), 1); + assert!(System::account_exists(&1)); + assert_eq!(Balances::free_balance(1), 0); + }); +} + +#[test] +fn partial_locking_should_work() { + ExtBuilder::default() + .existential_deposit(1) + .monied(true) + .build_and_execute_with(|| { + Balances::set_lock(ID_1, &1, 5, WithdrawReasons::all()); + assert_ok!(>::transfer(&1, &2, 1, AllowDeath)); + }); +} + +#[test] +fn lock_removal_should_work() { + ExtBuilder::default() + .existential_deposit(1) + .monied(true) + .build_and_execute_with(|| { + Balances::set_lock(ID_1, &1, u64::MAX, WithdrawReasons::all()); + Balances::remove_lock(ID_1, &1); + assert_ok!(>::transfer(&1, &2, 1, AllowDeath)); + }); +} + +#[test] +fn lock_replacement_should_work() { + ExtBuilder::default() + .existential_deposit(1) + .monied(true) + .build_and_execute_with(|| { + Balances::set_lock(ID_1, &1, u64::MAX, WithdrawReasons::all()); + Balances::set_lock(ID_1, &1, 5, WithdrawReasons::all()); + assert_ok!(>::transfer(&1, &2, 1, AllowDeath)); + }); +} + +#[test] +fn double_locking_should_work() { + ExtBuilder::default() + .existential_deposit(1) + .monied(true) + .build_and_execute_with(|| { + Balances::set_lock(ID_1, &1, 5, WithdrawReasons::all()); + Balances::set_lock(ID_2, &1, 5, WithdrawReasons::all()); + assert_ok!(>::transfer(&1, &2, 1, AllowDeath)); + }); +} + +#[test] +fn combination_locking_should_work() { + ExtBuilder::default() + .existential_deposit(1) + .monied(true) + .build_and_execute_with(|| { + Balances::set_lock(ID_1, &1, u64::MAX, WithdrawReasons::empty()); + Balances::set_lock(ID_2, &1, 0, WithdrawReasons::all()); + assert_ok!(>::transfer(&1, &2, 1, AllowDeath)); + }); +} + +#[test] +fn lock_value_extension_should_work() { + ExtBuilder::default() + .existential_deposit(1) + .monied(true) + .build_and_execute_with(|| { + Balances::set_lock(ID_1, &1, 5, WithdrawReasons::all()); + assert_noop!( + >::transfer(&1, &2, 6, AllowDeath), + TokenError::Frozen + ); + Balances::extend_lock(ID_1, &1, 2, WithdrawReasons::all()); + assert_noop!( + >::transfer(&1, &2, 6, AllowDeath), + TokenError::Frozen + ); + Balances::extend_lock(ID_1, &1, 8, WithdrawReasons::all()); + assert_noop!( + >::transfer(&1, &2, 3, AllowDeath), + TokenError::Frozen + ); + }); +} + +#[test] +fn lock_should_work_reserve() { + ExtBuilder::default() + .existential_deposit(1) + .monied(true) + .build_and_execute_with(|| { + pallet_transaction_payment::NextFeeMultiplier::::put( + Multiplier::saturating_from_integer(1), + ); + Balances::set_lock(ID_1, &1, 10, WithdrawReasons::RESERVE); + assert_noop!( + >::transfer(&1, &2, 1, AllowDeath), + TokenError::Frozen + ); + assert_noop!(Balances::reserve(&1, 1), Error::::LiquidityRestrictions,); + assert!( as SignedExtension>::pre_dispatch( + ChargeTransactionPayment::from(1), + &1, + CALL, + &info_from_weight(Weight::from_parts(1, 0)), + 1, + ) + .is_err()); + assert!( as SignedExtension>::pre_dispatch( + ChargeTransactionPayment::from(0), + &1, + CALL, + &info_from_weight(Weight::from_parts(1, 0)), + 1, + ) + .is_err()); + }); +} + +#[test] +fn lock_should_work_tx_fee() { + ExtBuilder::default() + .existential_deposit(1) + .monied(true) + .build_and_execute_with(|| { + Balances::set_lock(ID_1, &1, 10, WithdrawReasons::TRANSACTION_PAYMENT); + assert_noop!( + >::transfer(&1, &2, 1, AllowDeath), + TokenError::Frozen + ); + assert_noop!(Balances::reserve(&1, 1), Error::::LiquidityRestrictions,); + assert!( as SignedExtension>::pre_dispatch( + ChargeTransactionPayment::from(1), + &1, + CALL, + &info_from_weight(Weight::from_parts(1, 0)), + 1, + ) + .is_err()); + assert!( as SignedExtension>::pre_dispatch( + ChargeTransactionPayment::from(0), + &1, + CALL, + &info_from_weight(Weight::from_parts(1, 0)), + 1, + ) + .is_err()); + }); +} + +#[test] +fn lock_block_number_extension_should_work() { + ExtBuilder::default() + .existential_deposit(1) + .monied(true) + .build_and_execute_with(|| { + Balances::set_lock(ID_1, &1, 10, WithdrawReasons::all()); + assert_noop!( + >::transfer(&1, &2, 6, AllowDeath), + TokenError::Frozen + ); + Balances::extend_lock(ID_1, &1, 10, WithdrawReasons::all()); + assert_noop!( + >::transfer(&1, &2, 6, AllowDeath), + TokenError::Frozen + ); + System::set_block_number(2); + Balances::extend_lock(ID_1, &1, 10, WithdrawReasons::all()); + assert_noop!( + >::transfer(&1, &2, 3, AllowDeath), + TokenError::Frozen + ); + }); +} + +#[test] +fn lock_reasons_extension_should_work() { + ExtBuilder::default() + .existential_deposit(1) + .monied(true) + .build_and_execute_with(|| { + Balances::set_lock(ID_1, &1, 10, WithdrawReasons::TRANSFER); + assert_noop!( + >::transfer(&1, &2, 6, AllowDeath), + TokenError::Frozen + ); + Balances::extend_lock(ID_1, &1, 10, WithdrawReasons::empty()); + assert_noop!( + >::transfer(&1, &2, 6, AllowDeath), + TokenError::Frozen + ); + Balances::extend_lock(ID_1, &1, 10, WithdrawReasons::RESERVE); + assert_noop!( + >::transfer(&1, &2, 6, AllowDeath), + TokenError::Frozen + ); + }); +} + +#[test] +fn reserved_balance_should_prevent_reclaim_count() { + ExtBuilder::default() + .existential_deposit(256 * 1) + .monied(true) + .build_and_execute_with(|| { + System::inc_account_nonce(&2); + assert_eq!(Balances::total_balance(&2), 256 * 20); + assert_eq!(System::providers(&2), 1); + System::inc_providers(&2); + assert_eq!(System::providers(&2), 2); + + assert_ok!(Balances::reserve(&2, 256 * 19 + 1)); // account 2 becomes mostly reserved + assert_eq!(System::providers(&2), 1); + assert_eq!(Balances::free_balance(2), 255); // "free" account would be deleted. + assert_eq!(Balances::total_balance(&2), 256 * 20); // reserve still exists. + assert_eq!(System::account_nonce(&2), 1); + + // account 4 tries to take index 1 for account 5. + assert_ok!(Balances::transfer_allow_death(Some(4).into(), 5, 256 * 1 + 0x69)); + assert_eq!(Balances::total_balance(&5), 256 * 1 + 0x69); + + assert!(Balances::slash_reserved(&2, 256 * 19 + 1).1.is_zero()); // account 2 gets slashed + + // "reserve" account reduced to 255 (below ED) so account no longer consuming + assert_ok!(System::dec_providers(&2)); + assert_eq!(System::providers(&2), 0); + // account deleted + assert_eq!(System::account_nonce(&2), 0); // nonce zero + assert_eq!(Balances::total_balance(&2), 0); + + // account 4 tries to take index 1 again for account 6. + assert_ok!(Balances::transfer_allow_death(Some(4).into(), 6, 256 * 1 + 0x69)); + assert_eq!(Balances::total_balance(&6), 256 * 1 + 0x69); + }); +} + +#[test] +fn reward_should_work() { + ExtBuilder::default().monied(true).build_and_execute_with(|| { + assert_eq!(Balances::total_balance(&1), 10); + assert_ok!(Balances::deposit_into_existing(&1, 10).map(drop)); + System::assert_last_event(RuntimeEvent::Balances(crate::Event::Deposit { + who: 1, + amount: 10, + })); + assert_eq!(Balances::total_balance(&1), 20); + assert_eq!(Balances::total_issuance(), 120); + }); +} + +#[test] +fn balance_works() { + ExtBuilder::default().build_and_execute_with(|| { + let _ = Balances::deposit_creating(&1, 42); + System::assert_has_event(RuntimeEvent::Balances(crate::Event::Deposit { + who: 1, + amount: 42, + })); + assert_eq!(Balances::free_balance(1), 42); + assert_eq!(Balances::reserved_balance(1), 0); + assert_eq!(Balances::total_balance(&1), 42); + assert_eq!(Balances::free_balance(2), 0); + assert_eq!(Balances::reserved_balance(2), 0); + assert_eq!(Balances::total_balance(&2), 0); + }); +} + +#[test] +fn reserving_balance_should_work() { + ExtBuilder::default().build_and_execute_with(|| { + let _ = Balances::deposit_creating(&1, 111); + + assert_eq!(Balances::total_balance(&1), 111); + assert_eq!(Balances::free_balance(1), 111); + assert_eq!(Balances::reserved_balance(1), 0); + + assert_ok!(Balances::reserve(&1, 69)); + + assert_eq!(Balances::total_balance(&1), 111); + assert_eq!(Balances::free_balance(1), 42); + assert_eq!(Balances::reserved_balance(1), 69); + }); +} + +#[test] +fn deducting_balance_should_work() { + ExtBuilder::default().build_and_execute_with(|| { + let _ = Balances::deposit_creating(&1, 111); + assert_ok!(Balances::reserve(&1, 69)); + assert_eq!(Balances::free_balance(1), 42); + }); +} + +#[test] +fn refunding_balance_should_work() { + ExtBuilder::default().build_and_execute_with(|| { + let _ = Balances::deposit_creating(&1, 42); + assert_ok!(Balances::mutate_account(&1, |a| a.reserved = 69)); + Balances::unreserve(&1, 69); + assert_eq!(Balances::free_balance(1), 111); + assert_eq!(Balances::reserved_balance(1), 0); + }); +} + +#[test] +fn slashing_balance_should_work() { + ExtBuilder::default().build_and_execute_with(|| { + let _ = Balances::deposit_creating(&1, 112); + assert_ok!(Balances::reserve(&1, 69)); + assert!(Balances::slash(&1, 42).1.is_zero()); + assert_eq!(Balances::free_balance(1), 1); + assert_eq!(Balances::reserved_balance(1), 69); + assert_eq!(Balances::total_issuance(), 70); + }); +} + +#[test] +fn withdrawing_balance_should_work() { + ExtBuilder::default().build_and_execute_with(|| { + let _ = Balances::deposit_creating(&2, 111); + let _ = + Balances::withdraw(&2, 11, WithdrawReasons::TRANSFER, ExistenceRequirement::KeepAlive); + System::assert_last_event(RuntimeEvent::Balances(crate::Event::Withdraw { + who: 2, + amount: 11, + })); + assert_eq!(Balances::free_balance(2), 100); + assert_eq!(Balances::total_issuance(), 100); + }); +} + +#[test] +fn withdrawing_balance_should_fail_when_not_expendable() { + ExtBuilder::default().build_and_execute_with(|| { + ExistentialDeposit::set(10); + let _ = Balances::deposit_creating(&2, 20); + assert_ok!(Balances::reserve(&2, 5)); + assert_noop!( + Balances::withdraw(&2, 6, WithdrawReasons::TRANSFER, ExistenceRequirement::KeepAlive), + Error::::Expendability, + ); + assert_ok!(Balances::withdraw( + &2, + 5, + WithdrawReasons::TRANSFER, + ExistenceRequirement::KeepAlive + ),); + }); +} + +#[test] +fn slashing_incomplete_balance_should_work() { + ExtBuilder::default().build_and_execute_with(|| { + let _ = Balances::deposit_creating(&1, 42); + assert_ok!(Balances::reserve(&1, 21)); + assert_eq!(Balances::slash(&1, 69).1, 49); + assert_eq!(Balances::free_balance(1), 1); + assert_eq!(Balances::reserved_balance(1), 21); + assert_eq!(Balances::total_issuance(), 22); + }); +} + +#[test] +fn unreserving_balance_should_work() { + ExtBuilder::default().build_and_execute_with(|| { + let _ = Balances::deposit_creating(&1, 111); + assert_ok!(Balances::reserve(&1, 110)); + Balances::unreserve(&1, 41); + assert_eq!(Balances::reserved_balance(1), 69); + assert_eq!(Balances::free_balance(1), 42); + }); +} + +#[test] +fn slashing_reserved_balance_should_work() { + ExtBuilder::default().build_and_execute_with(|| { + let _ = Balances::deposit_creating(&1, 112); + assert_ok!(Balances::reserve(&1, 111)); + assert_eq!(Balances::slash_reserved(&1, 42).1, 0); + assert_eq!(Balances::reserved_balance(1), 69); + assert_eq!(Balances::free_balance(1), 1); + assert_eq!(Balances::total_issuance(), 70); + }); +} + +#[test] +fn slashing_incomplete_reserved_balance_should_work() { + ExtBuilder::default().build_and_execute_with(|| { + let _ = Balances::deposit_creating(&1, 111); + assert_ok!(Balances::reserve(&1, 42)); + assert_eq!(Balances::slash_reserved(&1, 69).1, 27); + assert_eq!(Balances::free_balance(1), 69); + assert_eq!(Balances::reserved_balance(1), 0); + assert_eq!(Balances::total_issuance(), 69); + }); +} + +#[test] +fn repatriating_reserved_balance_should_work() { + ExtBuilder::default().build_and_execute_with(|| { + let _ = Balances::deposit_creating(&1, 111); + let _ = Balances::deposit_creating(&2, 1); + assert_ok!(Balances::reserve(&1, 110)); + assert_ok!(Balances::repatriate_reserved(&1, &2, 41, Free), 0); + System::assert_last_event(RuntimeEvent::Balances(crate::Event::ReserveRepatriated { + from: 1, + to: 2, + amount: 41, + destination_status: Free, + })); + assert_eq!(Balances::reserved_balance(1), 69); + assert_eq!(Balances::free_balance(1), 1); + assert_eq!(Balances::reserved_balance(2), 0); + assert_eq!(Balances::free_balance(2), 42); + }); +} + +#[test] +fn transferring_reserved_balance_should_work() { + ExtBuilder::default().build_and_execute_with(|| { + let _ = Balances::deposit_creating(&1, 111); + let _ = Balances::deposit_creating(&2, 1); + assert_ok!(Balances::reserve(&1, 110)); + assert_ok!(Balances::repatriate_reserved(&1, &2, 41, Reserved), 0); + assert_eq!(Balances::reserved_balance(1), 69); + assert_eq!(Balances::free_balance(1), 1); + assert_eq!(Balances::reserved_balance(2), 41); + assert_eq!(Balances::free_balance(2), 1); + }); +} + +#[test] +fn transferring_reserved_balance_to_yourself_should_work() { + ExtBuilder::default().build_and_execute_with(|| { + let _ = Balances::deposit_creating(&1, 110); + assert_ok!(Balances::reserve(&1, 50)); + assert_ok!(Balances::repatriate_reserved(&1, &1, 50, Free), 0); + assert_eq!(Balances::free_balance(1), 110); + assert_eq!(Balances::reserved_balance(1), 0); + + assert_ok!(Balances::reserve(&1, 50)); + assert_ok!(Balances::repatriate_reserved(&1, &1, 60, Free), 10); + assert_eq!(Balances::free_balance(1), 110); + assert_eq!(Balances::reserved_balance(1), 0); + }); +} + +#[test] +fn transferring_reserved_balance_to_nonexistent_should_fail() { + ExtBuilder::default().build_and_execute_with(|| { + let _ = Balances::deposit_creating(&1, 111); + assert_ok!(Balances::reserve(&1, 110)); + assert_noop!( + Balances::repatriate_reserved(&1, &2, 42, Free), + Error::::DeadAccount + ); + }); +} + +#[test] +fn transferring_incomplete_reserved_balance_should_work() { + ExtBuilder::default().build_and_execute_with(|| { + let _ = Balances::deposit_creating(&1, 110); + let _ = Balances::deposit_creating(&2, 1); + assert_ok!(Balances::reserve(&1, 41)); + assert_ok!(Balances::repatriate_reserved(&1, &2, 69, Free), 28); + assert_eq!(Balances::reserved_balance(1), 0); + assert_eq!(Balances::free_balance(1), 69); + assert_eq!(Balances::reserved_balance(2), 0); + assert_eq!(Balances::free_balance(2), 42); + }); +} + +#[test] +fn transferring_too_high_value_should_not_panic() { + ExtBuilder::default().build_and_execute_with(|| { + Balances::make_free_balance_be(&1, u64::MAX); + Balances::make_free_balance_be(&2, 1); + + assert_err!( + >::transfer(&1, &2, u64::MAX, AllowDeath), + ArithmeticError::Overflow, + ); + + assert_eq!(Balances::free_balance(1), u64::MAX); + assert_eq!(Balances::free_balance(2), 1); + }); +} + +#[test] +fn account_create_on_free_too_low_with_other() { + ExtBuilder::default().existential_deposit(100).build_and_execute_with(|| { + let _ = Balances::deposit_creating(&1, 100); + assert_eq!(Balances::total_issuance(), 100); + + // No-op. + let _ = Balances::deposit_creating(&2, 50); + assert_eq!(Balances::free_balance(2), 0); + assert_eq!(Balances::total_issuance(), 100); + }) +} + +#[test] +fn account_create_on_free_too_low() { + ExtBuilder::default().existential_deposit(100).build_and_execute_with(|| { + // No-op. + let _ = Balances::deposit_creating(&2, 50); + assert_eq!(Balances::free_balance(2), 0); + assert_eq!(Balances::total_issuance(), 0); + }) +} + +#[test] +fn account_removal_on_free_too_low() { + ExtBuilder::default().existential_deposit(100).build_and_execute_with(|| { + assert_eq!(Balances::total_issuance(), 0); + + // Setup two accounts with free balance above the existential threshold. + let _ = Balances::deposit_creating(&1, 110); + let _ = Balances::deposit_creating(&2, 110); + + assert_eq!(Balances::free_balance(1), 110); + assert_eq!(Balances::free_balance(2), 110); + assert_eq!(Balances::total_issuance(), 220); + + // Transfer funds from account 1 of such amount that after this transfer + // the balance of account 1 will be below the existential threshold. + // This should lead to the removal of all balance of this account. + assert_ok!(Balances::transfer_allow_death(Some(1).into(), 2, 20)); + + // Verify free balance removal of account 1. + assert_eq!(Balances::free_balance(1), 0); + assert_eq!(Balances::free_balance(2), 130); + + // Verify that TotalIssuance tracks balance removal when free balance is too low. + assert_eq!(Balances::total_issuance(), 130); + }); +} + +#[test] +fn burn_must_work() { + ExtBuilder::default().monied(true).build_and_execute_with(|| { + let init_total_issuance = Balances::total_issuance(); + let imbalance = Balances::burn(10); + assert_eq!(Balances::total_issuance(), init_total_issuance - 10); + drop(imbalance); + assert_eq!(Balances::total_issuance(), init_total_issuance); + }); +} + +#[test] +#[should_panic = "the balance of any account should always be at least the existential deposit."] +fn cannot_set_genesis_value_below_ed() { + EXISTENTIAL_DEPOSIT.with(|v| *v.borrow_mut() = 11); + let mut t = frame_system::GenesisConfig::default().build_storage::().unwrap(); + let _ = crate::GenesisConfig:: { balances: vec![(1, 10)] } + .assimilate_storage(&mut t) + .unwrap(); +} + +#[test] +#[should_panic = "duplicate balances in genesis."] +fn cannot_set_genesis_value_twice() { + let mut t = frame_system::GenesisConfig::default().build_storage::().unwrap(); + let _ = crate::GenesisConfig:: { balances: vec![(1, 10), (2, 20), (1, 15)] } + .assimilate_storage(&mut t) + .unwrap(); +} + +#[test] +fn existential_deposit_respected_when_reserving() { + ExtBuilder::default().existential_deposit(100).build_and_execute_with(|| { + // Set balance to free and reserved at the existential deposit + assert_ok!(Balances::force_set_balance(RawOrigin::Root.into(), 1, 101)); + // Check balance + assert_eq!(Balances::free_balance(1), 101); + assert_eq!(Balances::reserved_balance(1), 0); + + // Reserve some free balance + assert_ok!(Balances::reserve(&1, 1)); + // Check balance, the account should be ok. + assert_eq!(Balances::free_balance(1), 100); + assert_eq!(Balances::reserved_balance(1), 1); + + // Cannot reserve any more of the free balance. + assert_noop!(Balances::reserve(&1, 1), DispatchError::ConsumerRemaining); + }); +} + +#[test] +fn slash_fails_when_account_needed() { + ExtBuilder::default().existential_deposit(50).build_and_execute_with(|| { + // Set balance to free and reserved at the existential deposit + assert_ok!(Balances::force_set_balance(RawOrigin::Root.into(), 1, 52)); + assert_ok!(Balances::reserve(&1, 1)); + // Check balance + assert_eq!(Balances::free_balance(1), 51); + assert_eq!(Balances::reserved_balance(1), 1); + + // Slash a small amount + let res = Balances::slash(&1, 1); + assert_eq!(res, (NegativeImbalance::new(1), 0)); + + // The account should be dead. + assert_eq!(Balances::free_balance(1), 50); + assert_eq!(Balances::reserved_balance(1), 1); + + // Slashing again doesn't work since we require the ED + let res = Balances::slash(&1, 1); + assert_eq!(res, (NegativeImbalance::new(0), 1)); + + // The account should be dead. + assert_eq!(Balances::free_balance(1), 50); + assert_eq!(Balances::reserved_balance(1), 1); + }); +} + +#[test] +fn account_deleted_when_just_dust() { + ExtBuilder::default().existential_deposit(50).build_and_execute_with(|| { + // Set balance to free and reserved at the existential deposit + assert_ok!(Balances::force_set_balance(RawOrigin::Root.into(), 1, 50)); + // Check balance + assert_eq!(Balances::free_balance(1), 50); + + // Slash a small amount + let res = Balances::slash(&1, 1); + assert_eq!(res, (NegativeImbalance::new(1), 0)); + + // The account should be dead. + assert_eq!(Balances::free_balance(1), 0); + }); +} + +#[test] +fn emit_events_with_reserve_and_unreserve() { + ExtBuilder::default().build_and_execute_with(|| { + let _ = Balances::deposit_creating(&1, 100); + + System::set_block_number(2); + assert_ok!(Balances::reserve(&1, 10)); + + System::assert_last_event(RuntimeEvent::Balances(crate::Event::Reserved { + who: 1, + amount: 10, + })); + + System::set_block_number(3); + assert!(Balances::unreserve(&1, 5).is_zero()); + + System::assert_last_event(RuntimeEvent::Balances(crate::Event::Unreserved { + who: 1, + amount: 5, + })); + + System::set_block_number(4); + assert_eq!(Balances::unreserve(&1, 6), 1); + + // should only unreserve 5 + System::assert_last_event(RuntimeEvent::Balances(crate::Event::Unreserved { + who: 1, + amount: 5, + })); + }); +} + +#[test] +fn emit_events_with_existential_deposit() { + ExtBuilder::default().existential_deposit(100).build_and_execute_with(|| { + assert_ok!(Balances::force_set_balance(RawOrigin::Root.into(), 1, 100)); + + assert_eq!( + events(), + [ + RuntimeEvent::System(system::Event::NewAccount { account: 1 }), + RuntimeEvent::Balances(crate::Event::Endowed { account: 1, free_balance: 100 }), + RuntimeEvent::Balances(crate::Event::BalanceSet { who: 1, free: 100 }), + ] + ); + + let res = Balances::slash(&1, 1); + assert_eq!(res, (NegativeImbalance::new(1), 0)); + + assert_eq!( + events(), + [ + RuntimeEvent::System(system::Event::KilledAccount { account: 1 }), + RuntimeEvent::Balances(crate::Event::DustLost { account: 1, amount: 99 }), + RuntimeEvent::Balances(crate::Event::Slashed { who: 1, amount: 1 }), + ] + ); + }); +} + +#[test] +fn emit_events_with_no_existential_deposit_suicide() { + ExtBuilder::default().existential_deposit(1).build_and_execute_with(|| { + Balances::make_free_balance_be(&1, 100); + + assert_eq!( + events(), + [ + RuntimeEvent::Balances(crate::Event::BalanceSet { who: 1, free: 100 }), + RuntimeEvent::System(system::Event::NewAccount { account: 1 }), + RuntimeEvent::Balances(crate::Event::Endowed { account: 1, free_balance: 100 }), + ] + ); + + let res = Balances::slash(&1, 100); + assert_eq!(res, (NegativeImbalance::new(100), 0)); + + assert_eq!( + events(), + [ + RuntimeEvent::System(system::Event::KilledAccount { account: 1 }), + RuntimeEvent::Balances(crate::Event::Slashed { who: 1, amount: 100 }), + ] + ); + }); +} + +#[test] +fn slash_over_works() { + ExtBuilder::default().existential_deposit(100).build_and_execute_with(|| { + // SCENARIO: Over-slash will kill account, and report missing slash amount. + Balances::make_free_balance_be(&1, 1_000); + // Slashed full free_balance, and reports 300 not slashed + assert_eq!(Balances::slash(&1, 1_300), (NegativeImbalance::new(1000), 300)); + // Account is dead + assert!(!System::account_exists(&1)); + }); +} + +#[test] +fn slash_full_works() { + ExtBuilder::default().existential_deposit(100).build_and_execute_with(|| { + Balances::make_free_balance_be(&1, 1_000); + // Slashed completed in full + assert_eq!(Balances::slash(&1, 1_000), (NegativeImbalance::new(1000), 0)); + // Account is still alive + assert!(!System::account_exists(&1)); + System::assert_last_event(RuntimeEvent::Balances(crate::Event::Slashed { + who: 1, + amount: 1000, + })); + }); +} + +#[test] +fn slash_partial_works() { + ExtBuilder::default().existential_deposit(100).build_and_execute_with(|| { + Balances::make_free_balance_be(&1, 1_000); + // Slashed completed in full + assert_eq!(Balances::slash(&1, 900), (NegativeImbalance::new(900), 0)); + // Account is still alive + assert!(System::account_exists(&1)); + System::assert_last_event(RuntimeEvent::Balances(crate::Event::Slashed { + who: 1, + amount: 900, + })); + }); +} + +#[test] +fn slash_dusting_works() { + ExtBuilder::default().existential_deposit(100).build_and_execute_with(|| { + Balances::make_free_balance_be(&1, 1_000); + // Slashed completed in full + assert_eq!(Balances::slash(&1, 950), (NegativeImbalance::new(950), 0)); + assert!(!System::account_exists(&1)); + System::assert_last_event(RuntimeEvent::Balances(crate::Event::Slashed { + who: 1, + amount: 950, + })); + }); +} + +#[test] +fn slash_does_not_take_from_reserve() { + ExtBuilder::default().existential_deposit(100).build_and_execute_with(|| { + Balances::make_free_balance_be(&1, 1_000); + assert_ok!(Balances::reserve(&1, 100)); + // Slashed completed in full + assert_eq!(Balances::slash(&1, 900), (NegativeImbalance::new(800), 100)); + assert_eq!(Balances::reserved_balance(&1), 100); + System::assert_last_event(RuntimeEvent::Balances(crate::Event::Slashed { + who: 1, + amount: 800, + })); + }); +} + +#[test] +fn slash_consumed_slash_full_works() { + ExtBuilder::default().existential_deposit(100).build_and_execute_with(|| { + Balances::make_free_balance_be(&1, 1_000); + assert_ok!(System::inc_consumers(&1)); // <-- Reference counter added here is enough for all tests + // Slashed completed in full + assert_eq!(Balances::slash(&1, 900), (NegativeImbalance::new(900), 0)); + // Account is still alive + assert!(System::account_exists(&1)); + }); +} + +#[test] +fn slash_consumed_slash_over_works() { + ExtBuilder::default().existential_deposit(100).build_and_execute_with(|| { + Balances::make_free_balance_be(&1, 1_000); + assert_ok!(System::inc_consumers(&1)); // <-- Reference counter added here is enough for all tests + // Slashed completed in full + assert_eq!(Balances::slash(&1, 1_000), (NegativeImbalance::new(900), 100)); + // Account is still alive + assert!(System::account_exists(&1)); + }); +} + +#[test] +fn slash_consumed_slash_partial_works() { + ExtBuilder::default().existential_deposit(100).build_and_execute_with(|| { + Balances::make_free_balance_be(&1, 1_000); + assert_ok!(System::inc_consumers(&1)); // <-- Reference counter added here is enough for all tests + // Slashed completed in full + assert_eq!(Balances::slash(&1, 800), (NegativeImbalance::new(800), 0)); + // Account is still alive + assert!(System::account_exists(&1)); + }); +} + +#[test] +fn slash_on_non_existant_works() { + ExtBuilder::default().existential_deposit(100).build_and_execute_with(|| { + // Slash on non-existent account is okay. + assert_eq!(Balances::slash(&12345, 1_300), (NegativeImbalance::new(0), 1300)); + }); +} + +#[test] +fn slash_reserved_slash_partial_works() { + ExtBuilder::default().existential_deposit(100).build_and_execute_with(|| { + Balances::make_free_balance_be(&1, 1_000); + assert_ok!(Balances::reserve(&1, 900)); + // Slashed completed in full + assert_eq!(Balances::slash_reserved(&1, 800), (NegativeImbalance::new(800), 0)); + assert_eq!(System::consumers(&1), 1); + assert_eq!(Balances::reserved_balance(&1), 100); + assert_eq!(Balances::free_balance(&1), 100); + }); +} + +#[test] +fn slash_reserved_slash_everything_works() { + ExtBuilder::default().existential_deposit(100).build_and_execute_with(|| { + Balances::make_free_balance_be(&1, 1_000); + assert_ok!(Balances::reserve(&1, 900)); + assert_eq!(System::consumers(&1), 1); + // Slashed completed in full + assert_eq!(Balances::slash_reserved(&1, 900), (NegativeImbalance::new(900), 0)); + assert_eq!(System::consumers(&1), 0); + // Account is still alive + assert!(System::account_exists(&1)); + }); +} + +#[test] +fn slash_reserved_overslash_does_not_touch_free_balance() { + ExtBuilder::default().existential_deposit(100).build_and_execute_with(|| { + // SCENARIO: Over-slash doesn't touch free balance. + Balances::make_free_balance_be(&1, 1_000); + assert_ok!(Balances::reserve(&1, 800)); + // Slashed done + assert_eq!(Balances::slash_reserved(&1, 900), (NegativeImbalance::new(800), 100)); + assert_eq!(Balances::free_balance(&1), 200); + }); +} + +#[test] +fn slash_reserved_on_non_existant_works() { + ExtBuilder::default().existential_deposit(100).build_and_execute_with(|| { + // Slash on non-existent account is okay. + assert_eq!(Balances::slash_reserved(&12345, 1_300), (NegativeImbalance::new(0), 1300)); + }); +} + +#[test] +fn operations_on_dead_account_should_not_change_state() { + // These functions all use `mutate_account` which may introduce a storage change when + // the account never existed to begin with, and shouldn't exist in the end. + ExtBuilder::default().existential_deposit(0).build_and_execute_with(|| { + assert!(!frame_system::Account::::contains_key(&1337)); + + // Unreserve + assert_storage_noop!(assert_eq!(Balances::unreserve(&1337, 42), 42)); + // Reserve + assert_noop!(Balances::reserve(&1337, 42), Error::::InsufficientBalance); + // Slash Reserve + assert_storage_noop!(assert_eq!(Balances::slash_reserved(&1337, 42).1, 42)); + // Repatriate Reserve + assert_noop!( + Balances::repatriate_reserved(&1337, &1338, 42, Free), + Error::::DeadAccount + ); + // Slash + assert_storage_noop!(assert_eq!(Balances::slash(&1337, 42).1, 42)); + }); +} + +#[test] +fn named_reserve_should_work() { + ExtBuilder::default().build_and_execute_with(|| { + let _ = Balances::deposit_creating(&1, 111); + + let id_1 = TestId::Foo; + let id_2 = TestId::Bar; + let id_3 = TestId::Baz; + + // reserve + + assert_noop!( + Balances::reserve_named(&id_1, &1, 112), + Error::::InsufficientBalance + ); + + assert_ok!(Balances::reserve_named(&id_1, &1, 12)); + + assert_eq!(Balances::reserved_balance(1), 12); + assert_eq!(Balances::reserved_balance_named(&id_1, &1), 12); + assert_eq!(Balances::reserved_balance_named(&id_2, &1), 0); + + assert_ok!(Balances::reserve_named(&id_1, &1, 2)); + + assert_eq!(Balances::reserved_balance(1), 14); + assert_eq!(Balances::reserved_balance_named(&id_1, &1), 14); + assert_eq!(Balances::reserved_balance_named(&id_2, &1), 0); + + assert_ok!(Balances::reserve_named(&id_2, &1, 23)); + + assert_eq!(Balances::reserved_balance(1), 37); + assert_eq!(Balances::reserved_balance_named(&id_1, &1), 14); + assert_eq!(Balances::reserved_balance_named(&id_2, &1), 23); + + assert_ok!(Balances::reserve(&1, 34)); + + assert_eq!(Balances::reserved_balance(1), 71); + assert_eq!(Balances::reserved_balance_named(&id_1, &1), 14); + assert_eq!(Balances::reserved_balance_named(&id_2, &1), 23); + + assert_eq!(Balances::total_balance(&1), 111); + assert_eq!(Balances::free_balance(1), 40); + + assert_noop!(Balances::reserve_named(&id_3, &1, 2), Error::::TooManyReserves); + + // unreserve + + assert_eq!(Balances::unreserve_named(&id_1, &1, 10), 0); + + assert_eq!(Balances::reserved_balance(1), 61); + assert_eq!(Balances::reserved_balance_named(&id_1, &1), 4); + assert_eq!(Balances::reserved_balance_named(&id_2, &1), 23); + + assert_eq!(Balances::unreserve_named(&id_1, &1, 5), 1); + + assert_eq!(Balances::reserved_balance(1), 57); + assert_eq!(Balances::reserved_balance_named(&id_1, &1), 0); + assert_eq!(Balances::reserved_balance_named(&id_2, &1), 23); + + assert_eq!(Balances::unreserve_named(&id_2, &1, 3), 0); + + assert_eq!(Balances::reserved_balance(1), 54); + assert_eq!(Balances::reserved_balance_named(&id_1, &1), 0); + assert_eq!(Balances::reserved_balance_named(&id_2, &1), 20); + + assert_eq!(Balances::total_balance(&1), 111); + assert_eq!(Balances::free_balance(1), 57); + + // slash_reserved_named + + assert_ok!(Balances::reserve_named(&id_1, &1, 10)); + + assert_eq!(Balances::slash_reserved_named(&id_1, &1, 25).1, 15); + + assert_eq!(Balances::reserved_balance(1), 54); + assert_eq!(Balances::reserved_balance_named(&id_1, &1), 0); + assert_eq!(Balances::reserved_balance_named(&id_2, &1), 20); + assert_eq!(Balances::total_balance(&1), 101); + + assert_eq!(Balances::slash_reserved_named(&id_2, &1, 5).1, 0); + + assert_eq!(Balances::reserved_balance(1), 49); + assert_eq!(Balances::reserved_balance_named(&id_1, &1), 0); + assert_eq!(Balances::reserved_balance_named(&id_2, &1), 15); + assert_eq!(Balances::total_balance(&1), 96); + + // repatriate_reserved_named + + let _ = Balances::deposit_creating(&2, 100); + + assert_eq!(Balances::repatriate_reserved_named(&id_2, &1, &2, 10, Reserved).unwrap(), 0); + + assert_eq!(Balances::reserved_balance_named(&id_2, &1), 5); + assert_eq!(Balances::reserved_balance_named(&id_2, &2), 10); + assert_eq!(Balances::reserved_balance(&2), 10); + + assert_eq!(Balances::repatriate_reserved_named(&id_2, &2, &1, 11, Reserved).unwrap(), 1); + + assert_eq!(Balances::reserved_balance_named(&id_2, &1), 15); + assert_eq!(Balances::reserved_balance_named(&id_2, &2), 0); + assert_eq!(Balances::reserved_balance(&2), 0); + + assert_eq!(Balances::repatriate_reserved_named(&id_2, &1, &2, 10, Free).unwrap(), 0); + assert_eq!(Balances::reserved_balance_named(&id_2, &1), 5); + assert_eq!(Balances::reserved_balance_named(&id_2, &2), 0); + assert_eq!(Balances::free_balance(&2), 110); + + // repatriate_reserved_named to self + + assert_eq!(Balances::repatriate_reserved_named(&id_2, &1, &1, 10, Reserved).unwrap(), 5); + assert_eq!(Balances::reserved_balance_named(&id_2, &1), 5); + + assert_eq!(Balances::free_balance(&1), 47); + + assert_eq!(Balances::repatriate_reserved_named(&id_2, &1, &1, 15, Free).unwrap(), 10); + assert_eq!(Balances::reserved_balance_named(&id_2, &1), 0); + + assert_eq!(Balances::free_balance(&1), 52); + }); +} + +#[test] +fn reserved_named_to_yourself_should_work() { + ExtBuilder::default().build_and_execute_with(|| { + let _ = Balances::deposit_creating(&1, 110); + + let id = TestId::Foo; + + assert_ok!(Balances::reserve_named(&id, &1, 50)); + assert_ok!(Balances::repatriate_reserved_named(&id, &1, &1, 50, Free), 0); + assert_eq!(Balances::free_balance(1), 110); + assert_eq!(Balances::reserved_balance_named(&id, &1), 0); + + assert_ok!(Balances::reserve_named(&id, &1, 50)); + assert_ok!(Balances::repatriate_reserved_named(&id, &1, &1, 60, Free), 10); + assert_eq!(Balances::free_balance(1), 110); + assert_eq!(Balances::reserved_balance_named(&id, &1), 0); + }); +} + +#[test] +fn ensure_reserved_named_should_work() { + ExtBuilder::default().build_and_execute_with(|| { + let _ = Balances::deposit_creating(&1, 111); + + let id = TestId::Foo; + + assert_ok!(Balances::ensure_reserved_named(&id, &1, 15)); + assert_eq!(Balances::reserved_balance_named(&id, &1), 15); + + assert_ok!(Balances::ensure_reserved_named(&id, &1, 10)); + assert_eq!(Balances::reserved_balance_named(&id, &1), 10); + + assert_ok!(Balances::ensure_reserved_named(&id, &1, 20)); + assert_eq!(Balances::reserved_balance_named(&id, &1), 20); + }); +} + +#[test] +fn unreserve_all_named_should_work() { + ExtBuilder::default().build_and_execute_with(|| { + let _ = Balances::deposit_creating(&1, 111); + + let id = TestId::Foo; + + assert_ok!(Balances::reserve_named(&id, &1, 15)); + + assert_eq!(Balances::unreserve_all_named(&id, &1), 15); + assert_eq!(Balances::reserved_balance_named(&id, &1), 0); + assert_eq!(Balances::free_balance(&1), 111); + + assert_eq!(Balances::unreserve_all_named(&id, &1), 0); + }); +} + +#[test] +fn slash_all_reserved_named_should_work() { + ExtBuilder::default().build_and_execute_with(|| { + let _ = Balances::deposit_creating(&1, 111); + + let id = TestId::Foo; + + assert_ok!(Balances::reserve_named(&id, &1, 15)); + + assert_eq!(Balances::slash_all_reserved_named(&id, &1).peek(), 15); + assert_eq!(Balances::reserved_balance_named(&id, &1), 0); + assert_eq!(Balances::free_balance(&1), 96); + + assert_eq!(Balances::slash_all_reserved_named(&id, &1).peek(), 0); + }); +} + +#[test] +fn repatriate_all_reserved_named_should_work() { + ExtBuilder::default().build_and_execute_with(|| { + let _ = Balances::deposit_creating(&1, 111); + let _ = Balances::deposit_creating(&2, 10); + let _ = Balances::deposit_creating(&3, 10); + + let id = TestId::Foo; + + assert_ok!(Balances::reserve_named(&id, &1, 15)); + + assert_ok!(Balances::repatriate_all_reserved_named(&id, &1, &2, Reserved)); + assert_eq!(Balances::reserved_balance_named(&id, &1), 0); + assert_eq!(Balances::reserved_balance_named(&id, &2), 15); + + assert_ok!(Balances::repatriate_all_reserved_named(&id, &2, &3, Free)); + assert_eq!(Balances::reserved_balance_named(&id, &2), 0); + assert_eq!(Balances::free_balance(&3), 25); + }); +} + +#[test] +fn freezing_and_locking_should_work() { + ExtBuilder::default() + .existential_deposit(1) + .monied(true) + .build_and_execute_with(|| { + assert_ok!(>::set_freeze(&TestId::Foo, &1, 4)); + Balances::set_lock(ID_1, &1, 5, WithdrawReasons::all()); + assert_eq!(System::consumers(&1), 2); + assert_eq!(Balances::account(&1).frozen, 5); + assert_ok!(>::set_freeze(&TestId::Foo, &1, 6)); + assert_eq!(Balances::account(&1).frozen, 6); + assert_ok!(>::set_freeze(&TestId::Foo, &1, 4)); + assert_eq!(Balances::account(&1).frozen, 5); + Balances::set_lock(ID_1, &1, 3, WithdrawReasons::all()); + assert_eq!(Balances::account(&1).frozen, 4); + Balances::set_lock(ID_1, &1, 5, WithdrawReasons::all()); + assert_eq!(Balances::account(&1).frozen, 5); + Balances::remove_lock(ID_1, &1); + assert_eq!(Balances::account(&1).frozen, 4); + assert_eq!(System::consumers(&1), 1); + }); +} diff --git a/frame/balances/src/tests/dispatchable_tests.rs b/frame/balances/src/tests/dispatchable_tests.rs new file mode 100644 index 000000000..76d0961e5 --- /dev/null +++ b/frame/balances/src/tests/dispatchable_tests.rs @@ -0,0 +1,224 @@ +// This file is part of Substrate. + +// Copyright (C) 2017-2022 Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +//! Tests regarding the functionality of the dispatchables/extrinsics. + +use super::*; +use frame_support::traits::tokens::Preservation::Expendable; +use fungible::{hold::Mutate as HoldMutate, Inspect, Mutate}; + +#[test] +fn default_indexing_on_new_accounts_should_not_work2() { + ExtBuilder::default() + .existential_deposit(10) + .monied(true) + .build_and_execute_with(|| { + // account 5 should not exist + // ext_deposit is 10, value is 9, not satisfies for ext_deposit + assert_noop!( + Balances::transfer_allow_death(Some(1).into(), 5, 9), + TokenError::BelowMinimum, + ); + assert_eq!(Balances::free_balance(1), 100); + }); +} + +#[test] +fn dust_account_removal_should_work() { + ExtBuilder::default() + .existential_deposit(100) + .monied(true) + .build_and_execute_with(|| { + System::inc_account_nonce(&2); + assert_eq!(System::account_nonce(&2), 1); + assert_eq!(Balances::total_balance(&2), 2000); + // index 1 (account 2) becomes zombie + assert_ok!(Balances::transfer_allow_death(Some(2).into(), 5, 1901)); + assert_eq!(Balances::total_balance(&2), 0); + assert_eq!(Balances::total_balance(&5), 1901); + assert_eq!(System::account_nonce(&2), 0); + }); +} + +#[test] +fn balance_transfer_works() { + ExtBuilder::default().build_and_execute_with(|| { + let _ = Balances::mint_into(&1, 111); + assert_ok!(Balances::transfer_allow_death(Some(1).into(), 2, 69)); + assert_eq!(Balances::total_balance(&1), 42); + assert_eq!(Balances::total_balance(&2), 69); + }); +} + +#[test] +fn force_transfer_works() { + ExtBuilder::default().build_and_execute_with(|| { + let _ = Balances::mint_into(&1, 111); + assert_noop!(Balances::force_transfer(Some(2).into(), 1, 2, 69), BadOrigin,); + assert_ok!(Balances::force_transfer(RawOrigin::Root.into(), 1, 2, 69)); + assert_eq!(Balances::total_balance(&1), 42); + assert_eq!(Balances::total_balance(&2), 69); + }); +} + +#[test] +fn balance_transfer_when_on_hold_should_not_work() { + ExtBuilder::default().build_and_execute_with(|| { + let _ = Balances::mint_into(&1, 111); + assert_ok!(Balances::hold(&TestId::Foo, &1, 69)); + assert_noop!( + Balances::transfer_allow_death(Some(1).into(), 2, 69), + TokenError::FundsUnavailable, + ); + }); +} + +#[test] +fn transfer_keep_alive_works() { + ExtBuilder::default().existential_deposit(1).build_and_execute_with(|| { + let _ = Balances::mint_into(&1, 100); + assert_noop!( + Balances::transfer_keep_alive(Some(1).into(), 2, 100), + TokenError::NotExpendable + ); + assert_eq!(Balances::total_balance(&1), 100); + assert_eq!(Balances::total_balance(&2), 0); + }); +} + +#[test] +fn transfer_keep_alive_all_free_succeed() { + ExtBuilder::default().existential_deposit(100).build_and_execute_with(|| { + assert_ok!(Balances::force_set_balance(RuntimeOrigin::root(), 1, 300)); + assert_ok!(Balances::hold(&TestId::Foo, &1, 100)); + assert_ok!(Balances::transfer_keep_alive(Some(1).into(), 2, 100)); + assert_eq!(Balances::total_balance(&1), 200); + assert_eq!(Balances::total_balance(&2), 100); + }); +} + +#[test] +fn transfer_all_works_1() { + ExtBuilder::default().existential_deposit(100).build().execute_with(|| { + // setup + assert_ok!(Balances::force_set_balance(RuntimeOrigin::root(), 1, 200)); + assert_ok!(Balances::force_set_balance(RuntimeOrigin::root(), 2, 0)); + // transfer all and allow death + assert_ok!(Balances::transfer_all(Some(1).into(), 2, false)); + assert_eq!(Balances::total_balance(&1), 0); + assert_eq!(Balances::total_balance(&2), 200); + }); +} + +#[test] +fn transfer_all_works_2() { + ExtBuilder::default().existential_deposit(100).build().execute_with(|| { + // setup + assert_ok!(Balances::force_set_balance(RuntimeOrigin::root(), 1, 200)); + assert_ok!(Balances::force_set_balance(RuntimeOrigin::root(), 2, 0)); + // transfer all and keep alive + assert_ok!(Balances::transfer_all(Some(1).into(), 2, true)); + assert_eq!(Balances::total_balance(&1), 100); + assert_eq!(Balances::total_balance(&2), 100); + }); +} + +#[test] +fn transfer_all_works_3() { + ExtBuilder::default().existential_deposit(100).build().execute_with(|| { + // setup + assert_ok!(Balances::force_set_balance(RuntimeOrigin::root(), 1, 210)); + assert_ok!(Balances::hold(&TestId::Foo, &1, 10)); + assert_ok!(Balances::force_set_balance(RuntimeOrigin::root(), 2, 0)); + // transfer all and allow death w/ reserved + assert_ok!(Balances::transfer_all(Some(1).into(), 2, false)); + assert_eq!(Balances::total_balance(&1), 110); + assert_eq!(Balances::total_balance(&2), 100); + }); +} + +#[test] +fn transfer_all_works_4() { + ExtBuilder::default().existential_deposit(100).build().execute_with(|| { + // setup + assert_ok!(Balances::force_set_balance(RuntimeOrigin::root(), 1, 210)); + assert_ok!(Balances::hold(&TestId::Foo, &1, 10)); + assert_ok!(Balances::force_set_balance(RuntimeOrigin::root(), 2, 0)); + // transfer all and keep alive w/ reserved + assert_ok!(Balances::transfer_all(Some(1).into(), 2, true)); + assert_eq!(Balances::total_balance(&1), 110); + assert_eq!(Balances::total_balance(&2), 100); + }); +} + +#[test] +fn set_balance_handles_killing_account() { + ExtBuilder::default().build_and_execute_with(|| { + let _ = Balances::mint_into(&1, 111); + assert_ok!(frame_system::Pallet::::inc_consumers(&1)); + assert_noop!( + Balances::force_set_balance(RuntimeOrigin::root(), 1, 0), + DispatchError::ConsumerRemaining, + ); + }); +} + +#[test] +fn set_balance_handles_total_issuance() { + ExtBuilder::default().build_and_execute_with(|| { + let old_total_issuance = Balances::total_issuance(); + assert_ok!(Balances::force_set_balance(RuntimeOrigin::root(), 1337, 69)); + assert_eq!(Balances::total_issuance(), old_total_issuance + 69); + assert_eq!(Balances::total_balance(&1337), 69); + assert_eq!(Balances::free_balance(&1337), 69); + }); +} + +#[test] +fn upgrade_accounts_should_work() { + ExtBuilder::default() + .existential_deposit(1) + .monied(true) + .build_and_execute_with(|| { + System::inc_providers(&7); + assert_ok!(::AccountStore::try_mutate_exists( + &7, + |a| -> DispatchResult { + *a = Some(AccountData { + free: 5, + reserved: 5, + frozen: Zero::zero(), + flags: crate::types::ExtraFlags::old_logic(), + }); + Ok(()) + } + )); + assert!(!Balances::account(&7).flags.is_new_logic()); + assert_eq!(System::providers(&7), 1); + assert_eq!(System::consumers(&7), 0); + assert_ok!(Balances::upgrade_accounts(Some(1).into(), vec![7])); + assert!(Balances::account(&7).flags.is_new_logic()); + assert_eq!(System::providers(&7), 1); + assert_eq!(System::consumers(&7), 1); + + >::unreserve(&7, 5); + assert_ok!(>::transfer(&7, &1, 10, Expendable)); + assert_eq!(Balances::total_balance(&7), 0); + assert_eq!(System::providers(&7), 0); + assert_eq!(System::consumers(&7), 0); + }); +} diff --git a/frame/balances/src/tests/fungible_tests.rs b/frame/balances/src/tests/fungible_tests.rs new file mode 100644 index 000000000..128086885 --- /dev/null +++ b/frame/balances/src/tests/fungible_tests.rs @@ -0,0 +1,399 @@ +// This file is part of Substrate. + +// Copyright (C) 2017-2022 Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +//! Tests regarding the functionality of the `fungible` trait set implementations. + +use super::*; +use frame_support::traits::tokens::{ + Fortitude::{Force, Polite}, + Precision::{BestEffort, Exact}, + Preservation::{Expendable, Preserve, Protect}, + Restriction::Free, +}; +use fungible::{Inspect, InspectFreeze, InspectHold, Mutate, MutateFreeze, MutateHold, Unbalanced}; + +#[test] +fn inspect_trait_reducible_balance_basic_works() { + ExtBuilder::default().existential_deposit(10).build_and_execute_with(|| { + Balances::set_balance(&1, 100); + assert_eq!(Balances::reducible_balance(&1, Expendable, Polite), 100); + assert_eq!(Balances::reducible_balance(&1, Protect, Polite), 90); + assert_eq!(Balances::reducible_balance(&1, Preserve, Polite), 90); + assert_eq!(Balances::reducible_balance(&1, Expendable, Force), 100); + assert_eq!(Balances::reducible_balance(&1, Protect, Force), 90); + assert_eq!(Balances::reducible_balance(&1, Preserve, Force), 90); + }); +} + +#[test] +fn inspect_trait_reducible_balance_other_provide_works() { + ExtBuilder::default().existential_deposit(10).build_and_execute_with(|| { + Balances::set_balance(&1, 100); + System::inc_providers(&1); + assert_eq!(Balances::reducible_balance(&1, Expendable, Polite), 100); + assert_eq!(Balances::reducible_balance(&1, Protect, Polite), 100); + assert_eq!(Balances::reducible_balance(&1, Preserve, Polite), 90); + assert_eq!(Balances::reducible_balance(&1, Expendable, Force), 100); + assert_eq!(Balances::reducible_balance(&1, Protect, Force), 100); + assert_eq!(Balances::reducible_balance(&1, Preserve, Force), 90); + }); +} + +#[test] +fn inspect_trait_reducible_balance_frozen_works() { + ExtBuilder::default().existential_deposit(10).build_and_execute_with(|| { + Balances::set_balance(&1, 100); + assert_ok!(Balances::set_freeze(&TestId::Foo, &1, 50)); + assert_eq!(Balances::reducible_balance(&1, Expendable, Polite), 50); + assert_eq!(Balances::reducible_balance(&1, Protect, Polite), 50); + assert_eq!(Balances::reducible_balance(&1, Preserve, Polite), 50); + assert_eq!(Balances::reducible_balance(&1, Expendable, Force), 90); + assert_eq!(Balances::reducible_balance(&1, Protect, Force), 90); + assert_eq!(Balances::reducible_balance(&1, Preserve, Force), 90); + }); +} + +#[test] +fn unbalanced_trait_set_balance_works() { + ExtBuilder::default().build_and_execute_with(|| { + assert_eq!(>::balance(&1337), 0); + assert_ok!(Balances::write_balance(&1337, 100)); + assert_eq!(>::balance(&1337), 100); + + assert_ok!(>::hold(&TestId::Foo, &1337, 60)); + assert_eq!(>::balance(&1337), 40); + assert_eq!(>::total_balance_on_hold(&1337), 60); + assert_eq!( + >::balance_on_hold(&TestId::Foo, &1337), + 60 + ); + + assert_noop!(Balances::write_balance(&1337, 0), Error::::InsufficientBalance); + + assert_ok!(Balances::write_balance(&1337, 1)); + assert_eq!(>::balance(&1337), 1); + assert_eq!( + >::balance_on_hold(&TestId::Foo, &1337), + 60 + ); + + assert_ok!(>::release(&TestId::Foo, &1337, 60, Exact)); + assert_eq!(>::balance_on_hold(&TestId::Foo, &1337), 0); + assert_eq!(>::total_balance_on_hold(&1337), 0); + }); +} + +#[test] +fn unbalanced_trait_set_total_issuance_works() { + ExtBuilder::default().build_and_execute_with(|| { + assert_eq!(>::total_issuance(), 0); + Balances::set_total_issuance(100); + assert_eq!(>::total_issuance(), 100); + }); +} + +#[test] +fn unbalanced_trait_decrease_balance_simple_works() { + ExtBuilder::default().build_and_execute_with(|| { + // An Account that starts at 100 + assert_ok!(Balances::write_balance(&1337, 100)); + assert_eq!(>::balance(&1337), 100); + // and reserves 50 + assert_ok!(>::hold(&TestId::Foo, &1337, 50)); + assert_eq!(>::balance(&1337), 50); + // and is decreased by 20 + assert_ok!(Balances::decrease_balance(&1337, 20, Exact, Expendable, Polite)); + assert_eq!(>::balance(&1337), 30); + }); +} + +#[test] +fn unbalanced_trait_decrease_balance_works_1() { + ExtBuilder::default().build_and_execute_with(|| { + assert_ok!(Balances::write_balance(&1337, 100)); + assert_eq!(>::balance(&1337), 100); + + assert_noop!( + Balances::decrease_balance(&1337, 101, Exact, Expendable, Polite), + TokenError::FundsUnavailable + ); + assert_eq!(Balances::decrease_balance(&1337, 100, Exact, Expendable, Polite), Ok(100)); + assert_eq!(>::balance(&1337), 0); + }); +} + +#[test] +fn unbalanced_trait_decrease_balance_works_2() { + ExtBuilder::default().build_and_execute_with(|| { + // free: 40, reserved: 60 + assert_ok!(Balances::write_balance(&1337, 100)); + assert_ok!(Balances::hold(&TestId::Foo, &1337, 60)); + assert_eq!(>::balance(&1337), 40); + assert_eq!(Balances::total_balance_on_hold(&1337), 60); + assert_noop!( + Balances::decrease_balance(&1337, 40, Exact, Expendable, Polite), + Error::::InsufficientBalance + ); + assert_eq!(Balances::decrease_balance(&1337, 39, Exact, Expendable, Polite), Ok(39)); + assert_eq!(>::balance(&1337), 1); + assert_eq!(Balances::total_balance_on_hold(&1337), 60); + }); +} + +#[test] +fn unbalanced_trait_decrease_balance_at_most_works_1() { + ExtBuilder::default().build_and_execute_with(|| { + assert_ok!(Balances::write_balance(&1337, 100)); + assert_eq!(>::balance(&1337), 100); + + assert_eq!(Balances::decrease_balance(&1337, 101, BestEffort, Expendable, Polite), Ok(100)); + assert_eq!(>::balance(&1337), 0); + }); +} + +#[test] +fn unbalanced_trait_decrease_balance_at_most_works_2() { + ExtBuilder::default().build_and_execute_with(|| { + assert_ok!(Balances::write_balance(&1337, 99)); + assert_eq!(Balances::decrease_balance(&1337, 99, BestEffort, Expendable, Polite), Ok(99)); + assert_eq!(>::balance(&1337), 0); + }); +} + +#[test] +fn unbalanced_trait_decrease_balance_at_most_works_3() { + ExtBuilder::default().build_and_execute_with(|| { + // free: 40, reserved: 60 + assert_ok!(Balances::write_balance(&1337, 100)); + assert_ok!(Balances::hold(&TestId::Foo, &1337, 60)); + assert_eq!(Balances::free_balance(1337), 40); + assert_eq!(Balances::total_balance_on_hold(&1337), 60); + assert_eq!(Balances::decrease_balance(&1337, 0, BestEffort, Expendable, Polite), Ok(0)); + assert_eq!(Balances::free_balance(1337), 40); + assert_eq!(Balances::total_balance_on_hold(&1337), 60); + assert_eq!(Balances::decrease_balance(&1337, 10, BestEffort, Expendable, Polite), Ok(10)); + assert_eq!(Balances::free_balance(1337), 30); + assert_eq!(Balances::decrease_balance(&1337, 200, BestEffort, Expendable, Polite), Ok(29)); + assert_eq!(>::balance(&1337), 1); + assert_eq!(Balances::free_balance(1337), 1); + assert_eq!(Balances::total_balance_on_hold(&1337), 60); + }); +} + +#[test] +fn unbalanced_trait_increase_balance_works() { + ExtBuilder::default().build_and_execute_with(|| { + assert_noop!(Balances::increase_balance(&1337, 0, Exact), TokenError::BelowMinimum); + assert_eq!(Balances::increase_balance(&1337, 1, Exact), Ok(1)); + assert_noop!(Balances::increase_balance(&1337, u64::MAX, Exact), ArithmeticError::Overflow); + }); +} + +#[test] +fn unbalanced_trait_increase_balance_at_most_works() { + ExtBuilder::default().build_and_execute_with(|| { + assert_eq!(Balances::increase_balance(&1337, 0, BestEffort), Ok(0)); + assert_eq!(Balances::increase_balance(&1337, 1, BestEffort), Ok(1)); + assert_eq!(Balances::increase_balance(&1337, u64::MAX, BestEffort), Ok(u64::MAX - 1)); + }); +} + +#[test] +fn freezing_and_holds_should_overlap() { + ExtBuilder::default() + .existential_deposit(1) + .monied(true) + .build_and_execute_with(|| { + assert_ok!(Balances::set_freeze(&TestId::Foo, &1, 10)); + assert_ok!(Balances::hold(&TestId::Foo, &1, 9)); + assert_eq!(Balances::account(&1).free, 1); + assert_eq!(System::consumers(&1), 1); + assert_eq!(Balances::account(&1).free, 1); + assert_eq!(Balances::account(&1).frozen, 10); + assert_eq!(Balances::account(&1).reserved, 9); + assert_eq!(Balances::total_balance_on_hold(&1), 9); + }); +} + +#[test] +fn frozen_hold_balance_cannot_be_moved_without_force() { + ExtBuilder::default() + .existential_deposit(1) + .monied(true) + .build_and_execute_with(|| { + assert_ok!(Balances::set_freeze(&TestId::Foo, &1, 10)); + assert_ok!(Balances::hold(&TestId::Foo, &1, 9)); + assert_eq!(Balances::reducible_total_balance_on_hold(&1, Force), 9); + assert_eq!(Balances::reducible_total_balance_on_hold(&1, Polite), 0); + let e = TokenError::Frozen; + assert_noop!( + Balances::transfer_on_hold(&TestId::Foo, &1, &2, 1, Exact, Free, Polite), + e + ); + assert_ok!(Balances::transfer_on_hold(&TestId::Foo, &1, &2, 1, Exact, Free, Force)); + }); +} + +#[test] +fn frozen_hold_balance_best_effort_transfer_works() { + ExtBuilder::default() + .existential_deposit(1) + .monied(true) + .build_and_execute_with(|| { + assert_ok!(Balances::set_freeze(&TestId::Foo, &1, 5)); + assert_ok!(Balances::hold(&TestId::Foo, &1, 9)); + assert_eq!(Balances::reducible_total_balance_on_hold(&1, Force), 9); + assert_eq!(Balances::reducible_total_balance_on_hold(&1, Polite), 5); + assert_ok!(Balances::transfer_on_hold( + &TestId::Foo, + &1, + &2, + 10, + BestEffort, + Free, + Polite + )); + assert_eq!(Balances::total_balance(&1), 5); + assert_eq!(Balances::total_balance(&2), 25); + }); +} + +#[test] +fn partial_freezing_should_work() { + ExtBuilder::default() + .existential_deposit(1) + .monied(true) + .build_and_execute_with(|| { + assert_ok!(Balances::set_freeze(&TestId::Foo, &1, 5)); + assert_eq!(System::consumers(&1), 1); + assert_ok!(>::transfer(&1, &2, 5, Expendable)); + assert_noop!( + >::transfer(&1, &2, 1, Expendable), + TokenError::Frozen + ); + }); +} + +#[test] +fn thaw_should_work() { + ExtBuilder::default() + .existential_deposit(1) + .monied(true) + .build_and_execute_with(|| { + assert_ok!(Balances::set_freeze(&TestId::Foo, &1, u64::MAX)); + assert_ok!(Balances::thaw(&TestId::Foo, &1)); + assert_eq!(System::consumers(&1), 0); + assert_eq!(Balances::balance_frozen(&TestId::Foo, &1), 0); + assert_eq!(Balances::account(&1).frozen, 0); + assert_ok!(>::transfer(&1, &2, 10, Expendable)); + }); +} + +#[test] +fn set_freeze_zero_should_work() { + ExtBuilder::default() + .existential_deposit(1) + .monied(true) + .build_and_execute_with(|| { + assert_ok!(Balances::set_freeze(&TestId::Foo, &1, u64::MAX)); + assert_ok!(Balances::set_freeze(&TestId::Foo, &1, 0)); + assert_eq!(System::consumers(&1), 0); + assert_eq!(Balances::balance_frozen(&TestId::Foo, &1), 0); + assert_eq!(Balances::account(&1).frozen, 0); + assert_ok!(>::transfer(&1, &2, 10, Expendable)); + }); +} + +#[test] +fn set_freeze_should_work() { + ExtBuilder::default() + .existential_deposit(1) + .monied(true) + .build_and_execute_with(|| { + assert_ok!(Balances::set_freeze(&TestId::Foo, &1, u64::MAX)); + assert_ok!(Balances::set_freeze(&TestId::Foo, &1, 5)); + assert_ok!(>::transfer(&1, &2, 5, Expendable)); + assert_noop!( + >::transfer(&1, &2, 1, Expendable), + TokenError::Frozen + ); + }); +} + +#[test] +fn extend_freeze_should_work() { + ExtBuilder::default() + .existential_deposit(1) + .monied(true) + .build_and_execute_with(|| { + assert_ok!(Balances::set_freeze(&TestId::Foo, &1, 5)); + assert_ok!(Balances::extend_freeze(&TestId::Foo, &1, 10)); + assert_eq!(Balances::account(&1).frozen, 10); + assert_eq!(Balances::balance_frozen(&TestId::Foo, &1), 10); + assert_noop!( + >::transfer(&1, &2, 1, Expendable), + TokenError::Frozen + ); + }); +} + +#[test] +fn double_freezing_should_work() { + ExtBuilder::default() + .existential_deposit(1) + .monied(true) + .build_and_execute_with(|| { + assert_ok!(Balances::set_freeze(&TestId::Foo, &1, 5)); + assert_ok!(Balances::set_freeze(&TestId::Bar, &1, 5)); + assert_eq!(System::consumers(&1), 1); + assert_ok!(>::transfer(&1, &2, 5, Expendable)); + assert_noop!( + >::transfer(&1, &2, 1, Expendable), + TokenError::Frozen + ); + }); +} + +#[test] +fn can_hold_entire_balance_when_second_provider() { + ExtBuilder::default() + .existential_deposit(1) + .monied(false) + .build_and_execute_with(|| { + >::set_balance(&1, 100); + assert_noop!(Balances::hold(&TestId::Foo, &1, 100), TokenError::FundsUnavailable); + System::inc_providers(&1); + assert_eq!(System::providers(&1), 2); + assert_ok!(Balances::hold(&TestId::Foo, &1, 100)); + assert_eq!(System::providers(&1), 1); + assert_noop!(System::dec_providers(&1), DispatchError::ConsumerRemaining); + }); +} + +#[test] +fn unholding_frees_hold_slot() { + ExtBuilder::default() + .existential_deposit(1) + .monied(false) + .build_and_execute_with(|| { + >::set_balance(&1, 100); + assert_ok!(Balances::hold(&TestId::Foo, &1, 10)); + assert_ok!(Balances::hold(&TestId::Bar, &1, 10)); + assert_ok!(Balances::release(&TestId::Foo, &1, 10, Exact)); + assert_ok!(Balances::hold(&TestId::Baz, &1, 10)); + }); +} diff --git a/frame/balances/src/tests/mod.rs b/frame/balances/src/tests/mod.rs new file mode 100644 index 000000000..c4a8a631c --- /dev/null +++ b/frame/balances/src/tests/mod.rs @@ -0,0 +1,296 @@ +// This file is part of Substrate. + +// Copyright (C) 2018-2022 Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +//! Tests. + +#![cfg(test)] + +use crate::{self as pallet_balances, AccountData, Config, CreditOf, Error, Pallet}; +use codec::{Decode, Encode, MaxEncodedLen}; +use frame_support::{ + assert_err, assert_noop, assert_ok, assert_storage_noop, + dispatch::DispatchInfo, + parameter_types, + traits::{ + tokens::fungible, ConstU32, ConstU64, ConstU8, Imbalance as ImbalanceT, OnUnbalanced, + StorageMapShim, StoredMap, + }, + weights::{IdentityFee, Weight}, + RuntimeDebug, +}; +use frame_system::{self as system, RawOrigin}; +use pallet_transaction_payment::{ChargeTransactionPayment, CurrencyAdapter, Multiplier}; +use scale_info::TypeInfo; +use sp_core::H256; +use sp_io; +use sp_runtime::{ + testing::Header, + traits::{BadOrigin, IdentityLookup, SignedExtension, Zero}, + ArithmeticError, DispatchError, DispatchResult, FixedPointNumber, TokenError, +}; + +mod currency_tests; +mod dispatchable_tests; +mod fungible_tests; +mod reentrancy_tests; + +type UncheckedExtrinsic = frame_system::mocking::MockUncheckedExtrinsic; +type Block = frame_system::mocking::MockBlock; + +#[derive( + Encode, + Decode, + Copy, + Clone, + Eq, + PartialEq, + Ord, + PartialOrd, + MaxEncodedLen, + TypeInfo, + RuntimeDebug, +)] +pub enum TestId { + Foo, + Bar, + Baz, +} + +frame_support::construct_runtime!( + pub struct Test where + Block = Block, + NodeBlock = Block, + UncheckedExtrinsic = UncheckedExtrinsic, + { + System: frame_system::{Pallet, Call, Config, Storage, Event}, + Balances: pallet_balances::{Pallet, Call, Storage, Config, Event}, + TransactionPayment: pallet_transaction_payment::{Pallet, Storage, Event}, + } +); + +parameter_types! { + pub BlockWeights: frame_system::limits::BlockWeights = + frame_system::limits::BlockWeights::simple_max( + frame_support::weights::Weight::from_parts(1024, u64::MAX), + ); + pub static ExistentialDeposit: u64 = 0; +} +impl frame_system::Config for Test { + type BaseCallFilter = frame_support::traits::Everything; + type BlockWeights = BlockWeights; + type BlockLength = (); + type DbWeight = (); + type RuntimeOrigin = RuntimeOrigin; + type Index = u64; + type BlockNumber = u64; + type RuntimeCall = RuntimeCall; + type Hash = H256; + type Hashing = ::sp_runtime::traits::BlakeTwo256; + type AccountId = u64; + type Lookup = IdentityLookup; + type Header = Header; + type RuntimeEvent = RuntimeEvent; + type BlockHashCount = ConstU64<250>; + type Version = (); + type PalletInfo = PalletInfo; + type AccountData = super::AccountData; + type OnNewAccount = (); + type OnKilledAccount = (); + type SystemWeightInfo = (); + type SS58Prefix = (); + type OnSetCode = (); + type MaxConsumers = frame_support::traits::ConstU32<16>; +} + +impl pallet_transaction_payment::Config for Test { + type RuntimeEvent = RuntimeEvent; + type OnChargeTransaction = CurrencyAdapter, ()>; + type OperationalFeeMultiplier = ConstU8<5>; + type WeightToFee = IdentityFee; + type LengthToFee = IdentityFee; + type FeeMultiplierUpdate = (); +} + +impl Config for Test { + type Balance = u64; + type DustRemoval = DustTrap; + type RuntimeEvent = RuntimeEvent; + type ExistentialDeposit = ExistentialDeposit; + type AccountStore = TestAccountStore; + type MaxLocks = ConstU32<50>; + type MaxReserves = ConstU32<2>; + type ReserveIdentifier = TestId; + type WeightInfo = (); + type HoldIdentifier = TestId; + type FreezeIdentifier = TestId; + type MaxFreezes = ConstU32<2>; + type MaxHolds = ConstU32<2>; +} + +#[derive(Clone)] +pub struct ExtBuilder { + existential_deposit: u64, + monied: bool, + dust_trap: Option, +} +impl Default for ExtBuilder { + fn default() -> Self { + Self { existential_deposit: 1, monied: false, dust_trap: None } + } +} +impl ExtBuilder { + pub fn existential_deposit(mut self, existential_deposit: u64) -> Self { + self.existential_deposit = existential_deposit; + self + } + pub fn monied(mut self, monied: bool) -> Self { + self.monied = monied; + if self.existential_deposit == 0 { + self.existential_deposit = 1; + } + self + } + pub fn dust_trap(mut self, account: u64) -> Self { + self.dust_trap = Some(account); + self + } + pub fn set_associated_consts(&self) { + DUST_TRAP_TARGET.with(|v| v.replace(self.dust_trap)); + EXISTENTIAL_DEPOSIT.with(|v| v.replace(self.existential_deposit)); + } + pub fn build(self) -> sp_io::TestExternalities { + self.set_associated_consts(); + let mut t = frame_system::GenesisConfig::default().build_storage::().unwrap(); + pallet_balances::GenesisConfig:: { + balances: if self.monied { + vec![ + (1, 10 * self.existential_deposit), + (2, 20 * self.existential_deposit), + (3, 30 * self.existential_deposit), + (4, 40 * self.existential_deposit), + (12, 10 * self.existential_deposit), + ] + } else { + vec![] + }, + } + .assimilate_storage(&mut t) + .unwrap(); + + let mut ext = sp_io::TestExternalities::new(t); + ext.execute_with(|| System::set_block_number(1)); + ext + } + pub fn build_and_execute_with(self, f: impl Fn()) { + let other = self.clone(); + UseSystem::set(false); + other.build().execute_with(|| f()); + UseSystem::set(true); + self.build().execute_with(|| f()); + } +} + +parameter_types! { + static DustTrapTarget: Option = None; +} + +pub struct DustTrap; + +impl OnUnbalanced> for DustTrap { + fn on_nonzero_unbalanced(amount: CreditOf) { + match DustTrapTarget::get() { + None => drop(amount), + Some(a) => { + let result = >::resolve(&a, amount); + debug_assert!(result.is_ok()); + }, + } + } +} + +parameter_types! { + pub static UseSystem: bool = false; +} + +type BalancesAccountStore = StorageMapShim, u64, super::AccountData>; +type SystemAccountStore = frame_system::Pallet; + +pub struct TestAccountStore; +impl StoredMap> for TestAccountStore { + fn get(k: &u64) -> super::AccountData { + if UseSystem::get() { + >::get(k) + } else { + >::get(k) + } + } + fn try_mutate_exists>( + k: &u64, + f: impl FnOnce(&mut Option>) -> Result, + ) -> Result { + if UseSystem::get() { + >::try_mutate_exists(k, f) + } else { + >::try_mutate_exists(k, f) + } + } + fn mutate( + k: &u64, + f: impl FnOnce(&mut super::AccountData) -> R, + ) -> Result { + if UseSystem::get() { + >::mutate(k, f) + } else { + >::mutate(k, f) + } + } + fn mutate_exists( + k: &u64, + f: impl FnOnce(&mut Option>) -> R, + ) -> Result { + if UseSystem::get() { + >::mutate_exists(k, f) + } else { + >::mutate_exists(k, f) + } + } + fn insert(k: &u64, t: super::AccountData) -> Result<(), DispatchError> { + if UseSystem::get() { + >::insert(k, t) + } else { + >::insert(k, t) + } + } + fn remove(k: &u64) -> Result<(), DispatchError> { + if UseSystem::get() { + >::remove(k) + } else { + >::remove(k) + } + } +} + +pub fn events() -> Vec { + let evt = System::events().into_iter().map(|evt| evt.event).collect::>(); + System::reset_events(); + evt +} + +/// create a transaction info struct from weight. Handy to avoid building the whole struct. +pub fn info_from_weight(w: Weight) -> DispatchInfo { + DispatchInfo { weight: w, ..Default::default() } +} diff --git a/frame/balances/src/tests/reentrancy_tests.rs b/frame/balances/src/tests/reentrancy_tests.rs new file mode 100644 index 000000000..e97bf2ed2 --- /dev/null +++ b/frame/balances/src/tests/reentrancy_tests.rs @@ -0,0 +1,195 @@ +// This file is part of Substrate. + +// Copyright (C) 2017-2022 Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +//! Tests regarding the reentrancy functionality. + +use super::*; +use frame_support::traits::tokens::{ + Fortitude::Force, + Precision::BestEffort, + Preservation::{Expendable, Protect}, +}; +use fungible::Balanced; + +#[test] +fn transfer_dust_removal_tst1_should_work() { + ExtBuilder::default() + .existential_deposit(100) + .dust_trap(1) + .build_and_execute_with(|| { + // Verification of reentrancy in dust removal + assert_ok!(Balances::force_set_balance(RawOrigin::Root.into(), 1, 1000)); + assert_ok!(Balances::force_set_balance(RawOrigin::Root.into(), 2, 500)); + + // In this transaction, account 2 free balance + // drops below existential balance + // and dust balance is removed from account 2 + assert_ok!(Balances::transfer_allow_death(RawOrigin::Signed(2).into(), 3, 450)); + + // As expected dust balance is removed. + assert_eq!(Balances::free_balance(&2), 0); + + // As expected beneficiary account 3 + // received the transfered fund. + assert_eq!(Balances::free_balance(&3), 450); + + // Dust balance is deposited to account 1 + // during the process of dust removal. + assert_eq!(Balances::free_balance(&1), 1050); + + // Verify the events + assert_eq!(System::events().len(), 12); + + System::assert_has_event(RuntimeEvent::Balances(crate::Event::Transfer { + from: 2, + to: 3, + amount: 450, + })); + System::assert_has_event(RuntimeEvent::Balances(crate::Event::DustLost { + account: 2, + amount: 50, + })); + System::assert_has_event(RuntimeEvent::Balances(crate::Event::Deposit { + who: 1, + amount: 50, + })); + }); +} + +#[test] +fn transfer_dust_removal_tst2_should_work() { + ExtBuilder::default() + .existential_deposit(100) + .dust_trap(1) + .build_and_execute_with(|| { + // Verification of reentrancy in dust removal + assert_ok!(Balances::force_set_balance(RawOrigin::Root.into(), 1, 1000)); + assert_ok!(Balances::force_set_balance(RawOrigin::Root.into(), 2, 500)); + + // In this transaction, account 2 free balance + // drops below existential balance + // and dust balance is removed from account 2 + assert_ok!(Balances::transfer_allow_death(RawOrigin::Signed(2).into(), 1, 450)); + + // As expected dust balance is removed. + assert_eq!(Balances::free_balance(&2), 0); + + // Dust balance is deposited to account 1 + // during the process of dust removal. + assert_eq!(Balances::free_balance(&1), 1500); + + // Verify the events + assert_eq!(System::events().len(), 10); + + System::assert_has_event(RuntimeEvent::Balances(crate::Event::Transfer { + from: 2, + to: 1, + amount: 450, + })); + System::assert_has_event(RuntimeEvent::Balances(crate::Event::DustLost { + account: 2, + amount: 50, + })); + System::assert_has_event(RuntimeEvent::Balances(crate::Event::Deposit { + who: 1, + amount: 50, + })); + }); +} + +#[test] +fn repatriating_reserved_balance_dust_removal_should_work() { + ExtBuilder::default() + .existential_deposit(100) + .dust_trap(1) + .build_and_execute_with(|| { + // Verification of reentrancy in dust removal + assert_ok!(Balances::force_set_balance(RawOrigin::Root.into(), 1, 1000)); + assert_ok!(Balances::force_set_balance(RawOrigin::Root.into(), 2, 500)); + + // Reserve a value on account 2, + // Such that free balance is lower than + // Exestintial deposit. + assert_ok!(Balances::transfer_allow_death(RuntimeOrigin::signed(2), 1, 450)); + + // Since free balance of account 2 is lower than + // existential deposit, dust amount is + // removed from the account 2 + assert_eq!(Balances::reserved_balance(2), 0); + assert_eq!(Balances::free_balance(2), 0); + + // account 1 is credited with reserved amount + // together with dust balance during dust + // removal. + assert_eq!(Balances::reserved_balance(1), 0); + assert_eq!(Balances::free_balance(1), 1500); + + // Verify the events + assert_eq!(System::events().len(), 10); + + System::assert_has_event(RuntimeEvent::Balances(crate::Event::Transfer { + from: 2, + to: 1, + amount: 450, + })); + System::assert_has_event(RuntimeEvent::Balances(crate::Event::DustLost { + account: 2, + amount: 50, + })); + System::assert_has_event(RuntimeEvent::Balances(crate::Event::Deposit { + who: 1, + amount: 50, + })); + }); +} + +#[test] +fn emit_events_with_no_existential_deposit_suicide_with_dust() { + ExtBuilder::default().existential_deposit(2).build_and_execute_with(|| { + assert_ok!(Balances::force_set_balance(RawOrigin::Root.into(), 1, 100)); + + assert_eq!( + events(), + [ + RuntimeEvent::System(system::Event::NewAccount { account: 1 }), + RuntimeEvent::Balances(crate::Event::Endowed { account: 1, free_balance: 100 }), + RuntimeEvent::Balances(crate::Event::BalanceSet { who: 1, free: 100 }), + ] + ); + + let res = Balances::withdraw(&1, 98, BestEffort, Protect, Force); + assert_eq!(res.unwrap().peek(), 98); + + // no events + assert_eq!( + events(), + [RuntimeEvent::Balances(crate::Event::Withdraw { who: 1, amount: 98 })] + ); + + let res = Balances::withdraw(&1, 1, BestEffort, Expendable, Force); + assert_eq!(res.unwrap().peek(), 1); + + assert_eq!( + events(), + [ + RuntimeEvent::System(system::Event::KilledAccount { account: 1 }), + RuntimeEvent::Balances(crate::Event::DustLost { account: 1, amount: 1 }), + RuntimeEvent::Balances(crate::Event::Withdraw { who: 1, amount: 1 }) + ] + ); + }); +} diff --git a/frame/balances/src/tests_composite.rs b/frame/balances/src/tests_composite.rs deleted file mode 100644 index 765ab194d..000000000 --- a/frame/balances/src/tests_composite.rs +++ /dev/null @@ -1,149 +0,0 @@ -// This file is part of Substrate. - -// Copyright (C) Parity Technologies (UK) Ltd. -// SPDX-License-Identifier: Apache-2.0 - -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -//! Test utilities - -#![cfg(test)] - -use crate::{self as pallet_balances, decl_tests, Config, Pallet}; -use frame_support::{ - dispatch::DispatchInfo, - parameter_types, - traits::{ConstU32, ConstU64, ConstU8}, - weights::{IdentityFee, Weight}, -}; -use pallet_transaction_payment::CurrencyAdapter; -use sp_core::H256; -use sp_io; -use sp_runtime::{testing::Header, traits::IdentityLookup}; -type UncheckedExtrinsic = frame_system::mocking::MockUncheckedExtrinsic; -type Block = frame_system::mocking::MockBlock; - -frame_support::construct_runtime!( - pub enum Test where - Block = Block, - NodeBlock = Block, - UncheckedExtrinsic = UncheckedExtrinsic, - { - System: frame_system::{Pallet, Call, Config, Storage, Event}, - Balances: pallet_balances::{Pallet, Call, Storage, Config, Event}, - TransactionPayment: pallet_transaction_payment::{Pallet, Storage, Event}, - } -); - -parameter_types! { - pub BlockWeights: frame_system::limits::BlockWeights = - frame_system::limits::BlockWeights::simple_max( - frame_support::weights::Weight::from_parts(1024, u64::MAX), - ); - pub static ExistentialDeposit: u64 = 0; -} -impl frame_system::Config for Test { - type BaseCallFilter = frame_support::traits::Everything; - type BlockWeights = BlockWeights; - type BlockLength = (); - type DbWeight = (); - type RuntimeOrigin = RuntimeOrigin; - type Index = u64; - type BlockNumber = u64; - type RuntimeCall = RuntimeCall; - type Hash = H256; - type Hashing = ::sp_runtime::traits::BlakeTwo256; - type AccountId = u64; - type Lookup = IdentityLookup; - type Header = Header; - type RuntimeEvent = RuntimeEvent; - type BlockHashCount = ConstU64<250>; - type Version = (); - type PalletInfo = PalletInfo; - type AccountData = super::AccountData; - type OnNewAccount = (); - type OnKilledAccount = (); - type SystemWeightInfo = (); - type SS58Prefix = (); - type OnSetCode = (); - type MaxConsumers = frame_support::traits::ConstU32<16>; -} - -impl pallet_transaction_payment::Config for Test { - type RuntimeEvent = RuntimeEvent; - type OnChargeTransaction = CurrencyAdapter, ()>; - type OperationalFeeMultiplier = ConstU8<5>; - type WeightToFee = IdentityFee; - type LengthToFee = IdentityFee; - type FeeMultiplierUpdate = (); -} - -impl Config for Test { - type Balance = u64; - type DustRemoval = (); - type RuntimeEvent = RuntimeEvent; - type ExistentialDeposit = ExistentialDeposit; - type AccountStore = frame_system::Pallet; - type MaxLocks = (); - type MaxReserves = ConstU32<2>; - type ReserveIdentifier = [u8; 8]; - type WeightInfo = (); -} - -pub struct ExtBuilder { - existential_deposit: u64, - monied: bool, -} -impl Default for ExtBuilder { - fn default() -> Self { - Self { existential_deposit: 1, monied: false } - } -} -impl ExtBuilder { - pub fn existential_deposit(mut self, existential_deposit: u64) -> Self { - self.existential_deposit = existential_deposit; - self - } - pub fn monied(mut self, monied: bool) -> Self { - self.monied = monied; - self - } - pub fn set_associated_consts(&self) { - EXISTENTIAL_DEPOSIT.with(|v| *v.borrow_mut() = self.existential_deposit); - } - pub fn build(self) -> sp_io::TestExternalities { - self.set_associated_consts(); - let mut t = frame_system::GenesisConfig::default().build_storage::().unwrap(); - pallet_balances::GenesisConfig:: { - balances: if self.monied { - vec![ - (1, 10 * self.existential_deposit), - (2, 20 * self.existential_deposit), - (3, 30 * self.existential_deposit), - (4, 40 * self.existential_deposit), - (12, 10 * self.existential_deposit), - ] - } else { - vec![] - }, - } - .assimilate_storage(&mut t) - .unwrap(); - - let mut ext = sp_io::TestExternalities::new(t); - ext.execute_with(|| System::set_block_number(1)); - ext - } -} - -decl_tests! { Test, ExtBuilder, EXISTENTIAL_DEPOSIT } diff --git a/frame/balances/src/tests_local.rs b/frame/balances/src/tests_local.rs deleted file mode 100644 index 929f77b54..000000000 --- a/frame/balances/src/tests_local.rs +++ /dev/null @@ -1,191 +0,0 @@ -// This file is part of Substrate. - -// Copyright (C) Parity Technologies (UK) Ltd. -// SPDX-License-Identifier: Apache-2.0 - -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -//! Test utilities - -#![cfg(test)] - -use crate::{self as pallet_balances, decl_tests, Config, Pallet}; -use frame_support::{ - dispatch::DispatchInfo, - parameter_types, - traits::{ConstU32, ConstU64, ConstU8, StorageMapShim}, - weights::{IdentityFee, Weight}, -}; -use pallet_transaction_payment::CurrencyAdapter; -use sp_core::H256; -use sp_io; -use sp_runtime::{testing::Header, traits::IdentityLookup}; - -type UncheckedExtrinsic = frame_system::mocking::MockUncheckedExtrinsic; -type Block = frame_system::mocking::MockBlock; - -frame_support::construct_runtime!( - pub struct Test where - Block = Block, - NodeBlock = Block, - UncheckedExtrinsic = UncheckedExtrinsic, - { - System: frame_system::{Pallet, Call, Config, Storage, Event}, - Balances: pallet_balances::{Pallet, Call, Storage, Config, Event}, - TransactionPayment: pallet_transaction_payment::{Pallet, Storage, Event}, - } -); - -parameter_types! { - pub BlockWeights: frame_system::limits::BlockWeights = - frame_system::limits::BlockWeights::simple_max( - frame_support::weights::Weight::from_parts(1024, u64::MAX), - ); - pub static ExistentialDeposit: u64 = 0; -} -impl frame_system::Config for Test { - type BaseCallFilter = frame_support::traits::Everything; - type BlockWeights = BlockWeights; - type BlockLength = (); - type DbWeight = (); - type RuntimeOrigin = RuntimeOrigin; - type Index = u64; - type BlockNumber = u64; - type RuntimeCall = RuntimeCall; - type Hash = H256; - type Hashing = ::sp_runtime::traits::BlakeTwo256; - type AccountId = u64; - type Lookup = IdentityLookup; - type Header = Header; - type RuntimeEvent = RuntimeEvent; - type BlockHashCount = ConstU64<250>; - type Version = (); - type PalletInfo = PalletInfo; - type AccountData = (); - type OnNewAccount = (); - type OnKilledAccount = (); - type SystemWeightInfo = (); - type SS58Prefix = (); - type OnSetCode = (); - type MaxConsumers = frame_support::traits::ConstU32<16>; -} - -impl pallet_transaction_payment::Config for Test { - type RuntimeEvent = RuntimeEvent; - type OnChargeTransaction = CurrencyAdapter, ()>; - type OperationalFeeMultiplier = ConstU8<5>; - type WeightToFee = IdentityFee; - type LengthToFee = IdentityFee; - type FeeMultiplierUpdate = (); -} - -impl Config for Test { - type Balance = u64; - type DustRemoval = (); - type RuntimeEvent = RuntimeEvent; - type ExistentialDeposit = ExistentialDeposit; - type AccountStore = - StorageMapShim, system::Provider, u64, super::AccountData>; - type MaxLocks = ConstU32<50>; - type MaxReserves = ConstU32<2>; - type ReserveIdentifier = [u8; 8]; - type WeightInfo = (); -} - -pub struct ExtBuilder { - existential_deposit: u64, - monied: bool, -} -impl Default for ExtBuilder { - fn default() -> Self { - Self { existential_deposit: 1, monied: false } - } -} -impl ExtBuilder { - pub fn existential_deposit(mut self, existential_deposit: u64) -> Self { - self.existential_deposit = existential_deposit; - self - } - pub fn monied(mut self, monied: bool) -> Self { - self.monied = monied; - if self.existential_deposit == 0 { - self.existential_deposit = 1; - } - self - } - pub fn set_associated_consts(&self) { - EXISTENTIAL_DEPOSIT.with(|v| *v.borrow_mut() = self.existential_deposit); - } - pub fn build(self) -> sp_io::TestExternalities { - self.set_associated_consts(); - let mut t = frame_system::GenesisConfig::default().build_storage::().unwrap(); - pallet_balances::GenesisConfig:: { - balances: if self.monied { - vec![ - (1, 10 * self.existential_deposit), - (2, 20 * self.existential_deposit), - (3, 30 * self.existential_deposit), - (4, 40 * self.existential_deposit), - (12, 10 * self.existential_deposit), - ] - } else { - vec![] - }, - } - .assimilate_storage(&mut t) - .unwrap(); - - let mut ext = sp_io::TestExternalities::new(t); - ext.execute_with(|| System::set_block_number(1)); - ext - } -} - -decl_tests! { Test, ExtBuilder, EXISTENTIAL_DEPOSIT } - -#[test] -fn emit_events_with_no_existential_deposit_suicide_with_dust() { - ::default().existential_deposit(2).build().execute_with(|| { - assert_ok!(Balances::set_balance(RawOrigin::Root.into(), 1, 100, 0)); - - assert_eq!( - events(), - [ - RuntimeEvent::System(system::Event::NewAccount { account: 1 }), - RuntimeEvent::Balances(crate::Event::Endowed { account: 1, free_balance: 100 }), - RuntimeEvent::Balances(crate::Event::BalanceSet { who: 1, free: 100, reserved: 0 }), - ] - ); - - let res = Balances::slash(&1, 98); - assert_eq!(res, (NegativeImbalance::new(98), 0)); - - // no events - assert_eq!( - events(), - [RuntimeEvent::Balances(crate::Event::Slashed { who: 1, amount: 98 })] - ); - - let res = Balances::slash(&1, 1); - assert_eq!(res, (NegativeImbalance::new(1), 0)); - - assert_eq!( - events(), - [ - RuntimeEvent::System(system::Event::KilledAccount { account: 1 }), - RuntimeEvent::Balances(crate::Event::DustLost { account: 1, amount: 1 }), - RuntimeEvent::Balances(crate::Event::Slashed { who: 1, amount: 1 }) - ] - ); - }); -} diff --git a/frame/balances/src/tests_reentrancy.rs b/frame/balances/src/tests_reentrancy.rs deleted file mode 100644 index 828dfa237..000000000 --- a/frame/balances/src/tests_reentrancy.rs +++ /dev/null @@ -1,264 +0,0 @@ -// This file is part of Substrate. - -// Copyright (C) Parity Technologies (UK) Ltd. -// SPDX-License-Identifier: Apache-2.0 - -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -//! Test setup for potential reentracy and lost updates of nested mutations. - -#![cfg(test)] - -use crate::{self as pallet_balances, Config}; -use frame_support::{ - parameter_types, - traits::{ConstU32, ConstU64, StorageMapShim}, -}; -use sp_core::H256; -use sp_io; -use sp_runtime::{testing::Header, traits::IdentityLookup}; - -use crate::*; -use frame_support::{ - assert_ok, - traits::{Currency, ReservableCurrency}, -}; -use frame_system::RawOrigin; - -type UncheckedExtrinsic = frame_system::mocking::MockUncheckedExtrinsic; -type Block = frame_system::mocking::MockBlock; - -frame_support::construct_runtime!( - pub enum Test where - Block = Block, - NodeBlock = Block, - UncheckedExtrinsic = UncheckedExtrinsic, - { - System: frame_system::{Pallet, Call, Config, Storage, Event}, - Balances: pallet_balances::{Pallet, Call, Storage, Config, Event}, - } -); - -parameter_types! { - pub BlockWeights: frame_system::limits::BlockWeights = - frame_system::limits::BlockWeights::simple_max( - frame_support::weights::Weight::from_parts(1024, u64::MAX), - ); - pub static ExistentialDeposit: u64 = 0; -} -impl frame_system::Config for Test { - type BaseCallFilter = frame_support::traits::Everything; - type BlockWeights = BlockWeights; - type BlockLength = (); - type DbWeight = (); - type RuntimeOrigin = RuntimeOrigin; - type Index = u64; - type BlockNumber = u64; - type RuntimeCall = RuntimeCall; - type Hash = H256; - type Hashing = ::sp_runtime::traits::BlakeTwo256; - type AccountId = u64; - type Lookup = IdentityLookup; - type Header = Header; - type RuntimeEvent = RuntimeEvent; - type BlockHashCount = ConstU64<250>; - type Version = (); - type PalletInfo = PalletInfo; - type AccountData = (); - type OnNewAccount = (); - type OnKilledAccount = (); - type SystemWeightInfo = (); - type SS58Prefix = (); - type OnSetCode = (); - type MaxConsumers = frame_support::traits::ConstU32<16>; -} - -pub struct OnDustRemoval; -impl OnUnbalanced> for OnDustRemoval { - fn on_nonzero_unbalanced(amount: NegativeImbalance) { - assert_ok!(Balances::resolve_into_existing(&1, amount)); - } -} - -impl Config for Test { - type Balance = u64; - type DustRemoval = OnDustRemoval; - type RuntimeEvent = RuntimeEvent; - type ExistentialDeposit = ExistentialDeposit; - type AccountStore = - StorageMapShim, system::Provider, u64, super::AccountData>; - type MaxLocks = ConstU32<50>; - type MaxReserves = ConstU32<2>; - type ReserveIdentifier = [u8; 8]; - type WeightInfo = (); -} - -pub struct ExtBuilder { - existential_deposit: u64, -} -impl Default for ExtBuilder { - fn default() -> Self { - Self { existential_deposit: 1 } - } -} -impl ExtBuilder { - pub fn existential_deposit(mut self, existential_deposit: u64) -> Self { - self.existential_deposit = existential_deposit; - self - } - - pub fn set_associated_consts(&self) { - EXISTENTIAL_DEPOSIT.with(|v| *v.borrow_mut() = self.existential_deposit); - } - - pub fn build(self) -> sp_io::TestExternalities { - self.set_associated_consts(); - let mut t = frame_system::GenesisConfig::default().build_storage::().unwrap(); - pallet_balances::GenesisConfig:: { balances: vec![] } - .assimilate_storage(&mut t) - .unwrap(); - let mut ext = sp_io::TestExternalities::new(t); - ext.execute_with(|| System::set_block_number(1)); - ext - } -} - -#[test] -fn transfer_dust_removal_tst1_should_work() { - ExtBuilder::default().existential_deposit(100).build().execute_with(|| { - // Verification of reentrancy in dust removal - assert_ok!(Balances::set_balance(RawOrigin::Root.into(), 1, 1000, 0)); - assert_ok!(Balances::set_balance(RawOrigin::Root.into(), 2, 500, 0)); - - // In this transaction, account 2 free balance - // drops below existential balance - // and dust balance is removed from account 2 - assert_ok!(Balances::transfer(RawOrigin::Signed(2).into(), 3, 450)); - - // As expected dust balance is removed. - assert_eq!(Balances::free_balance(&2), 0); - - // As expected beneficiary account 3 - // received the transfered fund. - assert_eq!(Balances::free_balance(&3), 450); - - // Dust balance is deposited to account 1 - // during the process of dust removal. - assert_eq!(Balances::free_balance(&1), 1050); - - // Verify the events - assert_eq!(System::events().len(), 12); - - System::assert_has_event(RuntimeEvent::Balances(crate::Event::Transfer { - from: 2, - to: 3, - amount: 450, - })); - System::assert_has_event(RuntimeEvent::Balances(crate::Event::DustLost { - account: 2, - amount: 50, - })); - System::assert_has_event(RuntimeEvent::Balances(crate::Event::Deposit { - who: 1, - amount: 50, - })); - }); -} - -#[test] -fn transfer_dust_removal_tst2_should_work() { - ExtBuilder::default().existential_deposit(100).build().execute_with(|| { - // Verification of reentrancy in dust removal - assert_ok!(Balances::set_balance(RawOrigin::Root.into(), 1, 1000, 0)); - assert_ok!(Balances::set_balance(RawOrigin::Root.into(), 2, 500, 0)); - - // In this transaction, account 2 free balance - // drops below existential balance - // and dust balance is removed from account 2 - assert_ok!(Balances::transfer(RawOrigin::Signed(2).into(), 1, 450)); - - // As expected dust balance is removed. - assert_eq!(Balances::free_balance(&2), 0); - - // Dust balance is deposited to account 1 - // during the process of dust removal. - assert_eq!(Balances::free_balance(&1), 1500); - - // Verify the events - assert_eq!(System::events().len(), 10); - - System::assert_has_event(RuntimeEvent::Balances(crate::Event::Transfer { - from: 2, - to: 1, - amount: 450, - })); - System::assert_has_event(RuntimeEvent::Balances(crate::Event::DustLost { - account: 2, - amount: 50, - })); - System::assert_has_event(RuntimeEvent::Balances(crate::Event::Deposit { - who: 1, - amount: 50, - })); - }); -} - -#[test] -fn repatriating_reserved_balance_dust_removal_should_work() { - ExtBuilder::default().existential_deposit(100).build().execute_with(|| { - // Verification of reentrancy in dust removal - assert_ok!(Balances::set_balance(RawOrigin::Root.into(), 1, 1000, 0)); - assert_ok!(Balances::set_balance(RawOrigin::Root.into(), 2, 500, 0)); - - // Reserve a value on account 2, - // Such that free balance is lower than - // Exestintial deposit. - assert_ok!(Balances::reserve(&2, 450)); - - // Transfer of reserved fund from slashed account 2 to - // beneficiary account 1 - assert_ok!(Balances::repatriate_reserved(&2, &1, 450, Status::Free), 0); - - // Since free balance of account 2 is lower than - // existential deposit, dust amount is - // removed from the account 2 - assert_eq!(Balances::reserved_balance(2), 0); - assert_eq!(Balances::free_balance(2), 0); - - // account 1 is credited with reserved amount - // together with dust balance during dust - // removal. - assert_eq!(Balances::reserved_balance(1), 0); - assert_eq!(Balances::free_balance(1), 1500); - - // Verify the events - assert_eq!(System::events().len(), 11); - - System::assert_has_event(RuntimeEvent::Balances(crate::Event::ReserveRepatriated { - from: 2, - to: 1, - amount: 450, - destination_status: Status::Free, - })); - - System::assert_has_event(RuntimeEvent::Balances(crate::Event::DustLost { - account: 2, - amount: 50, - })); - - System::assert_last_event(RuntimeEvent::Balances(crate::Event::Deposit { - who: 1, - amount: 50, - })); - }); -} diff --git a/frame/balances/src/types.rs b/frame/balances/src/types.rs new file mode 100644 index 000000000..c96e1e44b --- /dev/null +++ b/frame/balances/src/types.rs @@ -0,0 +1,157 @@ +// This file is part of Substrate. + +// Copyright (C) 2017-2022 Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +//! Types used in the pallet. + +use crate::{Config, CreditOf, Event, Pallet}; +use codec::{Decode, Encode, MaxEncodedLen}; +use core::ops::BitOr; +use frame_support::{ + traits::{Imbalance, LockIdentifier, OnUnbalanced, WithdrawReasons}, + RuntimeDebug, +}; +use scale_info::TypeInfo; +use sp_runtime::Saturating; + +/// Simplified reasons for withdrawing balance. +#[derive(Encode, Decode, Clone, Copy, PartialEq, Eq, RuntimeDebug, MaxEncodedLen, TypeInfo)] +pub enum Reasons { + /// Paying system transaction fees. + Fee = 0, + /// Any reason other than paying system transaction fees. + Misc = 1, + /// Any reason at all. + All = 2, +} + +impl From for Reasons { + fn from(r: WithdrawReasons) -> Reasons { + if r == WithdrawReasons::TRANSACTION_PAYMENT { + Reasons::Fee + } else if r.contains(WithdrawReasons::TRANSACTION_PAYMENT) { + Reasons::All + } else { + Reasons::Misc + } + } +} + +impl BitOr for Reasons { + type Output = Reasons; + fn bitor(self, other: Reasons) -> Reasons { + if self == other { + return self + } + Reasons::All + } +} + +/// A single lock on a balance. There can be many of these on an account and they "overlap", so the +/// same balance is frozen by multiple locks. +#[derive(Encode, Decode, Clone, PartialEq, Eq, RuntimeDebug, MaxEncodedLen, TypeInfo)] +pub struct BalanceLock { + /// An identifier for this lock. Only one lock may be in existence for each identifier. + pub id: LockIdentifier, + /// The amount which the free balance may not drop below when this lock is in effect. + pub amount: Balance, + /// If true, then the lock remains in effect even for payment of transaction fees. + pub reasons: Reasons, +} + +/// Store named reserved balance. +#[derive(Encode, Decode, Clone, PartialEq, Eq, RuntimeDebug, MaxEncodedLen, TypeInfo)] +pub struct ReserveData { + /// The identifier for the named reserve. + pub id: ReserveIdentifier, + /// The amount of the named reserve. + pub amount: Balance, +} + +/// An identifier and balance. +#[derive(Encode, Decode, Clone, PartialEq, Eq, RuntimeDebug, MaxEncodedLen, TypeInfo)] +pub struct IdAmount { + /// An identifier for this item. + pub id: Id, + /// Some amount for this item. + pub amount: Balance, +} + +/// All balance information for an account. +#[derive(Encode, Decode, Clone, PartialEq, Eq, Default, RuntimeDebug, MaxEncodedLen, TypeInfo)] +pub struct AccountData { + /// Non-reserved part of the balance which the account holder may be able to control. + /// + /// This is the only balance that matters in terms of most operations on tokens. + pub free: Balance, + /// Balance which is has active holds on it and may not be used at all. + /// + /// This is the sum of all individual holds together with any sums still under the (deprecated) + /// reserves API. + pub reserved: Balance, + /// The amount that `free` may not drop below when reducing the balance, except for actions + /// where the account owner cannot reasonably benefit from thr balance reduction, such as + /// slashing. + pub frozen: Balance, + /// Extra information about this account. The MSB is a flag indicating whether the new ref- + /// counting logic is in place for this account. + pub flags: ExtraFlags, +} + +const IS_NEW_LOGIC: u128 = 0x80000000_00000000_00000000_00000000u128; + +#[derive(Encode, Decode, Clone, PartialEq, Eq, RuntimeDebug, MaxEncodedLen, TypeInfo)] +pub struct ExtraFlags(u128); +impl Default for ExtraFlags { + fn default() -> Self { + Self(IS_NEW_LOGIC) + } +} +impl ExtraFlags { + pub fn old_logic() -> Self { + Self(0) + } + pub fn set_new_logic(&mut self) { + self.0 = self.0 | IS_NEW_LOGIC + } + pub fn is_new_logic(&self) -> bool { + (self.0 & IS_NEW_LOGIC) == IS_NEW_LOGIC + } +} + +impl AccountData { + pub fn usable(&self) -> Balance { + self.free.saturating_sub(self.frozen) + } + + /// The total balance in this account including any that is reserved and ignoring any frozen. + pub fn total(&self) -> Balance { + self.free.saturating_add(self.reserved) + } +} + +pub struct DustCleaner, I: 'static = ()>( + pub(crate) Option<(T::AccountId, CreditOf)>, +); + +impl, I: 'static> Drop for DustCleaner { + fn drop(&mut self) { + if let Some((who, dust)) = self.0.take() { + Pallet::::deposit_event(Event::DustLost { account: who, amount: dust.peek() }); + T::DustRemoval::on_unbalanced(dust); + } + } +} diff --git a/frame/balances/src/weights.rs b/frame/balances/src/weights.rs index e7fd539c0..a2a9f0019 100644 --- a/frame/balances/src/weights.rs +++ b/frame/balances/src/weights.rs @@ -49,13 +49,14 @@ use sp_std::marker::PhantomData; /// Weight functions needed for pallet_balances. pub trait WeightInfo { - fn transfer() -> Weight; + fn transfer_allow_death() -> Weight; fn transfer_keep_alive() -> Weight; - fn set_balance_creating() -> Weight; - fn set_balance_killing() -> Weight; + fn force_set_balance_creating() -> Weight; + fn force_set_balance_killing() -> Weight; fn force_transfer() -> Weight; fn transfer_all() -> Weight; fn force_unreserve() -> Weight; + fn upgrade_accounts(u: u32, ) -> Weight; } /// Weights for pallet_balances using the Substrate node and recommended hardware. @@ -63,7 +64,7 @@ pub struct SubstrateWeight(PhantomData); impl WeightInfo for SubstrateWeight { /// Storage: System Account (r:1 w:1) /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) - fn transfer() -> Weight { + fn transfer_allow_death() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `3593` @@ -85,7 +86,7 @@ impl WeightInfo for SubstrateWeight { } /// Storage: System Account (r:1 w:1) /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) - fn set_balance_creating() -> Weight { + fn force_set_balance_creating() -> Weight { // Proof Size summary in bytes: // Measured: `174` // Estimated: `3593` @@ -96,7 +97,7 @@ impl WeightInfo for SubstrateWeight { } /// Storage: System Account (r:1 w:1) /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) - fn set_balance_killing() -> Weight { + fn force_set_balance_killing() -> Weight { // Proof Size summary in bytes: // Measured: `174` // Estimated: `3593` @@ -138,13 +139,28 @@ impl WeightInfo for SubstrateWeight { .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } + /// Storage: System Account (r:999 w:999) + /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) + /// The range of component `u` is `[1, 1000]`. + fn upgrade_accounts(u: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `0 + u * (135 ±0)` + // Estimated: `990 + u * (2603 ±0)` + // Minimum execution time: 19_851_000 picoseconds. + Weight::from_parts(20_099_000, 990) + // Standard Error: 15_586 + .saturating_add(Weight::from_parts(14_892_860, 0).saturating_mul(u.into())) + .saturating_add(T::DbWeight::get().reads((1_u64).saturating_mul(u.into()))) + .saturating_add(T::DbWeight::get().writes((1_u64).saturating_mul(u.into()))) + .saturating_add(Weight::from_parts(0, 2603).saturating_mul(u.into())) + } } // For backwards compatibility and tests impl WeightInfo for () { /// Storage: System Account (r:1 w:1) /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) - fn transfer() -> Weight { + fn transfer_allow_death() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `3593` @@ -166,7 +182,7 @@ impl WeightInfo for () { } /// Storage: System Account (r:1 w:1) /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) - fn set_balance_creating() -> Weight { + fn force_set_balance_creating() -> Weight { // Proof Size summary in bytes: // Measured: `174` // Estimated: `3593` @@ -177,7 +193,7 @@ impl WeightInfo for () { } /// Storage: System Account (r:1 w:1) /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) - fn set_balance_killing() -> Weight { + fn force_set_balance_killing() -> Weight { // Proof Size summary in bytes: // Measured: `174` // Estimated: `3593` @@ -219,4 +235,19 @@ impl WeightInfo for () { .saturating_add(RocksDbWeight::get().reads(1_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } + /// Storage: System Account (r:999 w:999) + /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) + /// The range of component `u` is `[1, 1000]`. + fn upgrade_accounts(u: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `0 + u * (135 ±0)` + // Estimated: `990 + u * (2603 ±0)` + // Minimum execution time: 19_851_000 picoseconds. + Weight::from_parts(20_099_000, 990) + // Standard Error: 15_586 + .saturating_add(Weight::from_parts(14_892_860, 0).saturating_mul(u.into())) + .saturating_add(RocksDbWeight::get().reads((1_u64).saturating_mul(u.into()))) + .saturating_add(RocksDbWeight::get().writes((1_u64).saturating_mul(u.into()))) + .saturating_add(Weight::from_parts(0, 2603).saturating_mul(u.into())) + } } diff --git a/frame/beefy/src/mock.rs b/frame/beefy/src/mock.rs index c92966235..ceb95263e 100644 --- a/frame/beefy/src/mock.rs +++ b/frame/beefy/src/mock.rs @@ -161,6 +161,10 @@ impl pallet_balances::Config for Test { type ExistentialDeposit = ConstU128<1>; type AccountStore = System; type WeightInfo = (); + type HoldIdentifier = (); + type MaxHolds = (); + type FreezeIdentifier = (); + type MaxFreezes = (); } impl pallet_timestamp::Config for Test { diff --git a/frame/bounties/src/benchmarking.rs b/frame/bounties/src/benchmarking.rs index 04465a25e..0675328c3 100644 --- a/frame/bounties/src/benchmarking.rs +++ b/frame/bounties/src/benchmarking.rs @@ -57,9 +57,12 @@ fn setup_bounty, I: 'static>( let fee = value / 2u32.into(); let deposit = T::BountyDepositBase::get() + T::DataDepositPerByte::get() * T::MaximumReasonLength::get().into(); - let _ = T::Currency::make_free_balance_be(&caller, deposit); + let _ = T::Currency::make_free_balance_be(&caller, deposit + T::Currency::minimum_balance()); let curator = account("curator", u, SEED); - let _ = T::Currency::make_free_balance_be(&curator, fee / 2u32.into()); + let _ = T::Currency::make_free_balance_be( + &curator, + fee / 2u32.into() + T::Currency::minimum_balance(), + ); let reason = vec![0; d as usize]; (caller, curator, fee, value, reason) } diff --git a/frame/bounties/src/tests.rs b/frame/bounties/src/tests.rs index 27aa08585..ef3da7564 100644 --- a/frame/bounties/src/tests.rs +++ b/frame/bounties/src/tests.rs @@ -100,6 +100,10 @@ impl pallet_balances::Config for Test { type ExistentialDeposit = ConstU64<1>; type AccountStore = System; type WeightInfo = (); + type FreezeIdentifier = (); + type MaxFreezes = (); + type HoldIdentifier = (); + type MaxHolds = (); } parameter_types! { pub const ProposalBond: Permill = Permill::from_percent(5); @@ -754,7 +758,7 @@ fn award_and_claim_bounty_works() { System::set_block_number(5); >::on_initialize(5); - assert_ok!(Balances::transfer( + assert_ok!(Balances::transfer_allow_death( RuntimeOrigin::signed(0), Bounties::bounty_account_id(0), 10 @@ -832,7 +836,7 @@ fn cancel_and_refund() { System::set_block_number(2); >::on_initialize(2); - assert_ok!(Balances::transfer( + assert_ok!(Balances::transfer_allow_death( RuntimeOrigin::signed(0), Bounties::bounty_account_id(0), 10 diff --git a/frame/child-bounties/src/benchmarking.rs b/frame/child-bounties/src/benchmarking.rs index 0a83f7c39..e49d9c836 100644 --- a/frame/child-bounties/src/benchmarking.rs +++ b/frame/child-bounties/src/benchmarking.rs @@ -63,9 +63,12 @@ fn setup_bounty( let fee = value / 2u32.into(); let deposit = T::BountyDepositBase::get() + T::DataDepositPerByte::get() * T::MaximumReasonLength::get().into(); - let _ = T::Currency::make_free_balance_be(&caller, deposit); + let _ = T::Currency::make_free_balance_be(&caller, deposit + T::Currency::minimum_balance()); let curator = account("curator", user, SEED); - let _ = T::Currency::make_free_balance_be(&curator, fee / 2u32.into()); + let _ = T::Currency::make_free_balance_be( + &curator, + fee / 2u32.into() + T::Currency::minimum_balance(), + ); let reason = vec![0; description as usize]; (caller, curator, fee, value, reason) } @@ -73,7 +76,10 @@ fn setup_bounty( fn setup_child_bounty(user: u32, description: u32) -> BenchmarkChildBounty { let (caller, curator, fee, value, reason) = setup_bounty::(user, description); let child_curator = account("child-curator", user, SEED); - let _ = T::Currency::make_free_balance_be(&child_curator, fee / 2u32.into()); + let _ = T::Currency::make_free_balance_be( + &child_curator, + fee / 2u32.into() + T::Currency::minimum_balance(), + ); let child_bounty_value = (value - fee) / 4u32.into(); let child_bounty_fee = child_bounty_value / 2u32.into(); diff --git a/frame/child-bounties/src/tests.rs b/frame/child-bounties/src/tests.rs index 0172468ec..a936312ae 100644 --- a/frame/child-bounties/src/tests.rs +++ b/frame/child-bounties/src/tests.rs @@ -35,7 +35,7 @@ use sp_core::H256; use sp_runtime::{ testing::Header, traits::{BadOrigin, BlakeTwo256, IdentityLookup}, - Perbill, Permill, + Perbill, Permill, TokenError, }; use super::Event as ChildBountiesEvent; @@ -103,6 +103,10 @@ impl pallet_balances::Config for Test { type ExistentialDeposit = ConstU64<1>; type AccountStore = System; type WeightInfo = (); + type FreezeIdentifier = (); + type MaxFreezes = (); + type HoldIdentifier = (); + type MaxHolds = (); } parameter_types! { pub const ProposalBond: Permill = Permill::from_percent(5); @@ -248,7 +252,7 @@ fn add_child_bounty() { assert_noop!( ChildBounties::add_child_bounty(RuntimeOrigin::signed(4), 0, 50, b"12345-p1".to_vec()), - pallet_balances::Error::::KeepAlive, + TokenError::NotExpendable, ); assert_noop!( diff --git a/frame/contracts/src/benchmarking/mod.rs b/frame/contracts/src/benchmarking/mod.rs index 2fb033cf8..fb8e4ac4b 100644 --- a/frame/contracts/src/benchmarking/mod.rs +++ b/frame/contracts/src/benchmarking/mod.rs @@ -798,15 +798,15 @@ benchmarks! { let instance = Contract::::new(code, vec![])?; let origin = RawOrigin::Signed(instance.caller.clone()); let deposit_account = instance.info()?.deposit_account().clone(); - assert_eq!(T::Currency::total_balance(&beneficiary), 0u32.into()); + assert_eq!(>::total_balance(&beneficiary), 0u32.into()); assert_eq!(T::Currency::free_balance(&instance.account_id), Pallet::::min_balance() * 2u32.into()); assert_ne!(T::Currency::free_balance(&deposit_account), 0u32.into()); }: call(origin, instance.addr.clone(), 0u32.into(), Weight::MAX, None, vec![]) verify { if r > 0 { - assert_eq!(T::Currency::total_balance(&instance.account_id), 0u32.into()); - assert_eq!(T::Currency::total_balance(&deposit_account), 0u32.into()); - assert_eq!(T::Currency::total_balance(&beneficiary), Pallet::::min_balance() * 2u32.into()); + assert_eq!(>::total_balance(&instance.account_id), 0u32.into()); + assert_eq!(>::total_balance(&deposit_account), 0u32.into()); + assert_eq!(>::total_balance(&beneficiary), Pallet::::min_balance() * 2u32.into()); } } @@ -1573,12 +1573,12 @@ benchmarks! { instance.set_balance(value * (r + 1).into()); let origin = RawOrigin::Signed(instance.caller.clone()); for account in &accounts { - assert_eq!(T::Currency::total_balance(account), 0u32.into()); + assert_eq!(>::total_balance(account), 0u32.into()); } }: call(origin, instance.addr, 0u32.into(), Weight::MAX, None, vec![]) verify { for account in &accounts { - assert_eq!(T::Currency::total_balance(account), value); + assert_eq!(>::total_balance(account), value); } } diff --git a/frame/contracts/src/exec.rs b/frame/contracts/src/exec.rs index bd63c524c..6a053712e 100644 --- a/frame/contracts/src/exec.rs +++ b/frame/contracts/src/exec.rs @@ -25,7 +25,10 @@ use frame_support::{ crypto::ecdsa::ECDSAExt, dispatch::{DispatchError, DispatchResult, DispatchResultWithPostInfo, Dispatchable}, storage::{with_transaction, TransactionOutcome}, - traits::{Contains, Currency, ExistenceRequirement, OriginTrait, Randomness, Time}, + traits::{ + tokens::{Fortitude::Polite, Preservation::Expendable}, + Contains, Currency, ExistenceRequirement, OriginTrait, Randomness, Time, + }, weights::Weight, Blake2_128Concat, BoundedVec, StorageHasher, }; @@ -1198,7 +1201,7 @@ where T::Currency::transfer( &frame.account_id, beneficiary, - T::Currency::reducible_balance(&frame.account_id, false), + T::Currency::reducible_balance(&frame.account_id, Expendable, Polite), ExistenceRequirement::AllowDeath, )?; info.queue_trie_for_deletion()?; @@ -2751,8 +2754,10 @@ mod tests { RuntimeCall::System(SysCall::remark_with_event { remark: b"Hello".to_vec() }); // transfers are disallowed by the `TestFiler` (see below) - let forbidden_call = - RuntimeCall::Balances(BalanceCall::transfer { dest: CHARLIE, value: 22 }); + let forbidden_call = RuntimeCall::Balances(BalanceCall::transfer_allow_death { + dest: CHARLIE, + value: 22, + }); // simple cases: direct call assert_err!( @@ -2772,7 +2777,7 @@ mod tests { }); TestFilter::set_filter(|call| match call { - RuntimeCall::Balances(pallet_balances::Call::transfer { .. }) => false, + RuntimeCall::Balances(pallet_balances::Call::transfer_allow_death { .. }) => false, _ => true, }); diff --git a/frame/contracts/src/lib.rs b/frame/contracts/src/lib.rs index 7aac94d11..d3369b6c2 100644 --- a/frame/contracts/src/lib.rs +++ b/frame/contracts/src/lib.rs @@ -187,7 +187,7 @@ pub mod pallet { type Randomness: Randomness; /// The currency in which fees are paid and contract balances are held. - type Currency: ReservableCurrency + type Currency: ReservableCurrency // TODO: Move to fungible traits + Inspect>; /// The overarching event type. diff --git a/frame/contracts/src/storage/meter.rs b/frame/contracts/src/storage/meter.rs index b4fc443fe..ef3e01f6d 100644 --- a/frame/contracts/src/storage/meter.rs +++ b/frame/contracts/src/storage/meter.rs @@ -25,7 +25,10 @@ use codec::Encode; use frame_support::{ dispatch::DispatchError, ensure, - traits::{tokens::WithdrawConsequence, Currency, ExistenceRequirement, Get}, + traits::{ + tokens::{Fortitude::Polite, Preservation::Protect, WithdrawConsequence}, + Currency, ExistenceRequirement, Get, + }, DefaultNoBound, RuntimeDebugNoBound, }; use pallet_contracts_primitives::StorageDeposit as Deposit; @@ -456,7 +459,7 @@ impl Ext for ReservingExt { // We are sending the `min_leftover` and the `min_balance` from the origin // account as part of a contract call. Hence origin needs to have those left over // as free balance after accounting for all deposits. - let max = T::Currency::reducible_balance(origin, true) + let max = T::Currency::reducible_balance(origin, Protect, Polite) .saturating_sub(min_leftover) .saturating_sub(Pallet::::min_balance()); let limit = limit.unwrap_or(max); diff --git a/frame/contracts/src/tests.rs b/frame/contracts/src/tests.rs index 30a39eebf..185945b71 100644 --- a/frame/contracts/src/tests.rs +++ b/frame/contracts/src/tests.rs @@ -37,7 +37,7 @@ use frame_support::{ storage::child, traits::{ ConstU32, ConstU64, Contains, Currency, ExistenceRequirement, Get, LockableCurrency, - OnIdle, OnInitialize, ReservableCurrency, WithdrawReasons, + OnIdle, OnInitialize, WithdrawReasons, }, weights::{constants::WEIGHT_REF_TIME_PER_SECOND, Weight}, }; @@ -48,7 +48,7 @@ use sp_keystore::{testing::MemoryKeystore, KeystoreExt}; use sp_runtime::{ testing::{Header, H256}, traits::{BlakeTwo256, Convert, Hash, IdentityLookup}, - AccountId32, + AccountId32, TokenError, }; use std::{ops::Deref, sync::Arc}; @@ -318,6 +318,10 @@ impl pallet_balances::Config for Test { type ExistentialDeposit = ExistentialDeposit; type AccountStore = System; type WeightInfo = (); + type FreezeIdentifier = (); + type MaxFreezes = (); + type HoldIdentifier = (); + type MaxHolds = (); } impl pallet_timestamp::Config for Test { @@ -1054,7 +1058,7 @@ fn transfer_allow_death_cannot_kill_account() { total_balance, ExistenceRequirement::AllowDeath, ), - pallet_balances::Error::::KeepAlive, + TokenError::Frozen, ); assert_eq!(::Currency::total_balance(&addr), total_balance); @@ -1474,25 +1478,6 @@ fn transfer_return_code() { .result .unwrap(); assert_return_code!(result, RuntimeReturnCode::TransferFailed); - - // Contract has enough total balance in order to not go below the min balance - // threshold when transfering 100 balance but this balance is reserved so - // the transfer still fails. - Balances::make_free_balance_be(&addr, min_balance + 100); - Balances::reserve(&addr, min_balance + 100).unwrap(); - let result = Contracts::bare_call( - ALICE, - addr, - 0, - GAS_LIMIT, - None, - vec![], - false, - Determinism::Deterministic, - ) - .result - .unwrap(); - assert_return_code!(result, RuntimeReturnCode::TransferFailed); }); } @@ -1569,29 +1554,6 @@ fn call_return_code() { .unwrap(); assert_return_code!(result, RuntimeReturnCode::TransferFailed); - // Contract has enough total balance in order to not go below the min balance - // threshold when transfering 100 balance but this balance is reserved so - // the transfer still fails. - Balances::make_free_balance_be(&addr_bob, min_balance + 100); - Balances::reserve(&addr_bob, min_balance + 100).unwrap(); - let result = Contracts::bare_call( - ALICE, - addr_bob.clone(), - 0, - GAS_LIMIT, - None, - AsRef::<[u8]>::as_ref(&addr_django) - .iter() - .chain(&0u32.to_le_bytes()) - .cloned() - .collect(), - false, - Determinism::Deterministic, - ) - .result - .unwrap(); - assert_return_code!(result, RuntimeReturnCode::TransferFailed); - // Contract has enough balance but callee reverts because "1" is passed. Balances::make_free_balance_be(&addr_bob, min_balance + 1000); let result = Contracts::bare_call( @@ -1683,25 +1645,6 @@ fn instantiate_return_code() { .unwrap(); assert_return_code!(result, RuntimeReturnCode::TransferFailed); - // Contract has enough total balance in order to not go below the min_balance - // threshold when transfering the balance but this balance is reserved so - // the transfer still fails. - Balances::make_free_balance_be(&addr, min_balance + 10_000); - Balances::reserve(&addr, min_balance + 10_000).unwrap(); - let result = Contracts::bare_call( - ALICE, - addr.clone(), - 0, - GAS_LIMIT, - None, - callee_hash.clone(), - false, - Determinism::Deterministic, - ) - .result - .unwrap(); - assert_return_code!(result, RuntimeReturnCode::TransferFailed); - // Contract has enough balance but the passed code hash is invalid Balances::make_free_balance_be(&addr, min_balance + 10_000); let result = Contracts::bare_call( @@ -2805,7 +2748,7 @@ fn gas_estimation_call_runtime() { // Call something trivial with a huge gas limit so that we can observe the effects // of pre-charging. This should create a difference between consumed and required. - let call = RuntimeCall::Balances(pallet_balances::Call::transfer { + let call = RuntimeCall::Balances(pallet_balances::Call::transfer_allow_death { dest: addr_callee, value: min_balance * 10, }); diff --git a/frame/conviction-voting/src/benchmarking.rs b/frame/conviction-voting/src/benchmarking.rs index 27f46507d..8701ed7eb 100644 --- a/frame/conviction-voting/src/benchmarking.rs +++ b/frame/conviction-voting/src/benchmarking.rs @@ -23,7 +23,11 @@ use assert_matches::assert_matches; use frame_benchmarking::v1::{account, benchmarks_instance_pallet, whitelist_account}; use frame_support::{ dispatch::RawOrigin, - traits::{fungible, Currency, Get}, + traits::{ + fungible, + tokens::{Fortitude::Polite, Preservation::Expendable}, + Currency, Get, + }, }; use sp_runtime::traits::Bounded; use sp_std::collections::btree_map::BTreeMap; @@ -257,13 +261,13 @@ benchmarks_instance_pallet! { } } - let orig_usable = >::reducible_balance(&caller, false); + let orig_usable = >::reducible_balance(&caller, Expendable, Polite); let polls = &all_polls[&class]; // Vote big on the class with the most ongoing votes of them to bump the lock and make it // hard to recompute when removed. ConvictionVoting::::vote(RawOrigin::Signed(caller.clone()).into(), polls[0], big_account_vote)?; - let now_usable = >::reducible_balance(&caller, false); + let now_usable = >::reducible_balance(&caller, Expendable, Polite); assert_eq!(orig_usable - now_usable, 100u32.into()); // Remove the vote @@ -272,7 +276,7 @@ benchmarks_instance_pallet! { // We can now unlock on `class` from 200 to 100... }: _(RawOrigin::Signed(caller.clone()), class, caller_lookup) verify { - assert_eq!(orig_usable, >::reducible_balance(&caller, false)); + assert_eq!(orig_usable, >::reducible_balance(&caller, Expendable, Polite)); } impl_benchmark_test_suite!( diff --git a/frame/conviction-voting/src/lib.rs b/frame/conviction-voting/src/lib.rs index f28953c4a..072e57035 100644 --- a/frame/conviction-voting/src/lib.rs +++ b/frame/conviction-voting/src/lib.rs @@ -388,7 +388,10 @@ impl, I: 'static> Pallet { poll_index: PollIndexOf, vote: AccountVote>, ) -> DispatchResult { - ensure!(vote.balance() <= T::Currency::free_balance(who), Error::::InsufficientFunds); + ensure!( + vote.balance() <= T::Currency::total_balance(who), + Error::::InsufficientFunds + ); T::Polls::try_access_poll(poll_index, |poll_status| { let (tally, class) = poll_status.ensure_ongoing().ok_or(Error::::NotOngoing)?; VotingFor::::try_mutate(who, &class, |voting| { @@ -548,7 +551,7 @@ impl, I: 'static> Pallet { ) -> Result { ensure!(who != target, Error::::Nonsense); T::Polls::classes().binary_search(&class).map_err(|_| Error::::BadClass)?; - ensure!(balance <= T::Currency::free_balance(&who), Error::::InsufficientFunds); + ensure!(balance <= T::Currency::total_balance(&who), Error::::InsufficientFunds); let votes = VotingFor::::try_mutate(&who, &class, |voting| -> Result { let old = sp_std::mem::replace( diff --git a/frame/conviction-voting/src/tests.rs b/frame/conviction-voting/src/tests.rs index e01931d7a..f33e511a1 100644 --- a/frame/conviction-voting/src/tests.rs +++ b/frame/conviction-voting/src/tests.rs @@ -51,7 +51,7 @@ frame_support::construct_runtime!( pub struct BaseFilter; impl Contains for BaseFilter { fn contains(call: &RuntimeCall) -> bool { - !matches!(call, &RuntimeCall::Balances(pallet_balances::Call::set_balance { .. })) + !matches!(call, &RuntimeCall::Balances(pallet_balances::Call::force_set_balance { .. })) } } @@ -92,6 +92,10 @@ impl pallet_balances::Config for Test { type ExistentialDeposit = ConstU64<1>; type AccountStore = System; type WeightInfo = (); + type FreezeIdentifier = (); + type MaxFreezes = (); + type HoldIdentifier = (); + type MaxHolds = (); } #[derive(Clone, PartialEq, Eq, Debug)] diff --git a/frame/democracy/src/tests.rs b/frame/democracy/src/tests.rs index 9e7357659..06fde5129 100644 --- a/frame/democracy/src/tests.rs +++ b/frame/democracy/src/tests.rs @@ -72,7 +72,7 @@ frame_support::construct_runtime!( pub struct BaseFilter; impl Contains for BaseFilter { fn contains(call: &RuntimeCall) -> bool { - !matches!(call, &RuntimeCall::Balances(pallet_balances::Call::set_balance { .. })) + !matches!(call, &RuntimeCall::Balances(pallet_balances::Call::force_set_balance { .. })) } } @@ -144,6 +144,10 @@ impl pallet_balances::Config for Test { type ExistentialDeposit = ConstU64<1>; type AccountStore = System; type WeightInfo = (); + type FreezeIdentifier = (); + type MaxFreezes = (); + type HoldIdentifier = (); + type MaxHolds = (); } parameter_types! { pub static PreimageByteDeposit: u64 = 0; @@ -223,7 +227,7 @@ fn params_should_work() { } fn set_balance_proposal(value: u64) -> BoundedCallOf { - let inner = pallet_balances::Call::set_balance { who: 42, new_free: value, new_reserved: 0 }; + let inner = pallet_balances::Call::force_set_balance { who: 42, new_free: value }; let outer = RuntimeCall::Balances(inner); Preimage::bound(outer).unwrap() } diff --git a/frame/election-provider-multi-phase/src/benchmarking.rs b/frame/election-provider-multi-phase/src/benchmarking.rs index 4d48f1790..a5946c6a1 100644 --- a/frame/election-provider-multi-phase/src/benchmarking.rs +++ b/frame/election-provider-multi-phase/src/benchmarking.rs @@ -219,7 +219,7 @@ frame_benchmarking::benchmarks! { finalize_signed_phase_accept_solution { let receiver = account("receiver", 0, SEED); - let initial_balance = T::Currency::minimum_balance() * 10u32.into(); + let initial_balance = T::Currency::minimum_balance() + 10u32.into(); T::Currency::make_free_balance_be(&receiver, initial_balance); let ready = Default::default(); let deposit: BalanceOf = 10u32.into(); @@ -228,7 +228,7 @@ frame_benchmarking::benchmarks! { let call_fee: BalanceOf = 30u32.into(); assert_ok!(T::Currency::reserve(&receiver, deposit)); - assert_eq!(T::Currency::free_balance(&receiver), initial_balance - 10u32.into()); + assert_eq!(T::Currency::free_balance(&receiver), T::Currency::minimum_balance()); }: { >::finalize_signed_phase_accept_solution( ready, @@ -246,17 +246,17 @@ frame_benchmarking::benchmarks! { finalize_signed_phase_reject_solution { let receiver = account("receiver", 0, SEED); - let initial_balance = T::Currency::minimum_balance().max(One::one()) * 10u32.into(); + let initial_balance = T::Currency::minimum_balance() + 10u32.into(); let deposit: BalanceOf = 10u32.into(); T::Currency::make_free_balance_be(&receiver, initial_balance); assert_ok!(T::Currency::reserve(&receiver, deposit)); - assert_eq!(T::Currency::free_balance(&receiver), initial_balance - 10u32.into()); + assert_eq!(T::Currency::free_balance(&receiver), T::Currency::minimum_balance()); assert_eq!(T::Currency::reserved_balance(&receiver), 10u32.into()); }: { >::finalize_signed_phase_reject_solution(&receiver, deposit) } verify { - assert_eq!(T::Currency::free_balance(&receiver), initial_balance - 10u32.into()); + assert_eq!(T::Currency::free_balance(&receiver), T::Currency::minimum_balance()); assert_eq!(T::Currency::reserved_balance(&receiver), 0u32.into()); } diff --git a/frame/election-provider-multi-phase/src/mock.rs b/frame/election-provider-multi-phase/src/mock.rs index da7a0cf1d..8c1877760 100644 --- a/frame/election-provider-multi-phase/src/mock.rs +++ b/frame/election-provider-multi-phase/src/mock.rs @@ -254,6 +254,10 @@ impl pallet_balances::Config for Runtime { type MaxReserves = (); type ReserveIdentifier = [u8; 8]; type WeightInfo = (); + type FreezeIdentifier = (); + type MaxFreezes = (); + type HoldIdentifier = (); + type MaxHolds = (); } #[derive(Default, Eq, PartialEq, Debug, Clone, Copy)] diff --git a/frame/elections-phragmen/src/lib.rs b/frame/elections-phragmen/src/lib.rs index e3eed6ec4..d83c94db1 100644 --- a/frame/elections-phragmen/src/lib.rs +++ b/frame/elections-phragmen/src/lib.rs @@ -1248,6 +1248,10 @@ mod tests { type MaxReserves = (); type ReserveIdentifier = [u8; 8]; type WeightInfo = (); + type FreezeIdentifier = (); + type MaxFreezes = (); + type HoldIdentifier = (); + type MaxHolds = (); } frame_support::parameter_types! { @@ -2121,7 +2125,8 @@ mod tests { assert_ok!(submit_candidacy(RuntimeOrigin::signed(4))); // User has 100 free and 50 reserved. - assert_ok!(Balances::set_balance(RuntimeOrigin::root(), 2, 100, 50)); + assert_ok!(Balances::force_set_balance(RuntimeOrigin::root(), 2, 150)); + assert_ok!(Balances::reserve(&2, 50)); // User tries to vote with 150 tokens. assert_ok!(vote(RuntimeOrigin::signed(2), vec![4, 5], 150)); // We truncate to only their free balance, after reserving additional for voting. diff --git a/frame/examples/basic/src/tests.rs b/frame/examples/basic/src/tests.rs index 2e6a03bdd..d9a8a4e8e 100644 --- a/frame/examples/basic/src/tests.rs +++ b/frame/examples/basic/src/tests.rs @@ -87,6 +87,10 @@ impl pallet_balances::Config for Test { type ExistentialDeposit = ConstU64<1>; type AccountStore = System; type WeightInfo = (); + type FreezeIdentifier = (); + type MaxFreezes = (); + type HoldIdentifier = (); + type MaxHolds = (); } impl Config for Test { diff --git a/frame/executive/src/lib.rs b/frame/executive/src/lib.rs index 7a8de4314..10cf6e06b 100644 --- a/frame/executive/src/lib.rs +++ b/frame/executive/src/lib.rs @@ -688,13 +688,10 @@ mod tests { use frame_support::{ assert_err, parameter_types, - traits::{ - ConstU32, ConstU64, ConstU8, Currency, LockIdentifier, LockableCurrency, - WithdrawReasons, - }, + traits::{fungible, ConstU32, ConstU64, ConstU8, Currency}, weights::{ConstantMultiplier, IdentityFee, RuntimeDbWeight, Weight, WeightToFee}, }; - use frame_system::{Call as SystemCall, ChainContext, LastRuntimeUpgradeInfo}; + use frame_system::{ChainContext, LastRuntimeUpgradeInfo}; use pallet_balances::Call as BalancesCall; use pallet_transaction_payment::CurrencyAdapter; @@ -899,6 +896,10 @@ mod tests { type MaxReserves = (); type ReserveIdentifier = [u8; 8]; type WeightInfo = (); + type FreezeIdentifier = (); + type MaxFreezes = ConstU32<1>; + type HoldIdentifier = (); + type MaxHolds = ConstU32<1>; } parameter_types! { @@ -972,7 +973,7 @@ mod tests { } fn call_transfer(dest: u64, value: u64) -> RuntimeCall { - RuntimeCall::Balances(BalancesCall::transfer { dest, value }) + RuntimeCall::Balances(BalancesCall::transfer_allow_death { dest, value }) } #[test] @@ -1025,13 +1026,13 @@ mod tests { block_import_works_inner( new_test_ext_v0(1), array_bytes::hex_n_into_unchecked( - "216e61b2689d1243eb56d89c9084db48e50ebebc4871d758db131432c675d7c0", + "65e953676859e7a33245908af7ad3637d6861eb90416d433d485e95e2dd174a1", ), ); block_import_works_inner( new_test_ext(1), array_bytes::hex_n_into_unchecked( - "4738b4c0aab02d6ddfa62a2a6831ccc975a9f978f7db8d7ea8e68eba8639530a", + "5a19b3d6fdb7241836349fdcbe2d9df4d4f945b949d979e31ad50bff1cbcd1c2", ), ); } @@ -1116,7 +1117,7 @@ mod tests { let mut t = new_test_ext(10000); // given: TestXt uses the encoded len as fixed Len: let xt = TestXt::new( - RuntimeCall::Balances(BalancesCall::transfer { dest: 33, value: 0 }), + RuntimeCall::Balances(BalancesCall::transfer_allow_death { dest: 33, value: 0 }), sign_extra(1, 0, 0), ); let encoded = xt.encode(); @@ -1139,7 +1140,10 @@ mod tests { for nonce in 0..=num_to_exhaust_block { let xt = TestXt::new( - RuntimeCall::Balances(BalancesCall::transfer { dest: 33, value: 0 }), + RuntimeCall::Balances(BalancesCall::transfer_allow_death { + dest: 33, + value: 0, + }), sign_extra(1, nonce.into(), 0), ); let res = Executive::apply_extrinsic(xt); @@ -1164,15 +1168,15 @@ mod tests { #[test] fn block_weight_and_size_is_stored_per_tx() { let xt = TestXt::new( - RuntimeCall::Balances(BalancesCall::transfer { dest: 33, value: 0 }), + RuntimeCall::Balances(BalancesCall::transfer_allow_death { dest: 33, value: 0 }), sign_extra(1, 0, 0), ); let x1 = TestXt::new( - RuntimeCall::Balances(BalancesCall::transfer { dest: 33, value: 0 }), + RuntimeCall::Balances(BalancesCall::transfer_allow_death { dest: 33, value: 0 }), sign_extra(1, 1, 0), ); let x2 = TestXt::new( - RuntimeCall::Balances(BalancesCall::transfer { dest: 33, value: 0 }), + RuntimeCall::Balances(BalancesCall::transfer_allow_death { dest: 33, value: 0 }), sign_extra(1, 2, 0), ); let len = xt.clone().encode().len() as u32; @@ -1258,50 +1262,30 @@ mod tests { } #[test] - fn can_pay_for_tx_fee_on_full_lock() { - let id: LockIdentifier = *b"0 "; - let execute_with_lock = |lock: WithdrawReasons| { - let mut t = new_test_ext(1); - t.execute_with(|| { - as LockableCurrency>::set_lock( - id, &1, 110, lock, - ); - let xt = TestXt::new( - RuntimeCall::System(SystemCall::remark { remark: vec![1u8] }), - sign_extra(1, 0, 0), - ); - let weight = xt.get_dispatch_info().weight + - ::BlockWeights::get() - .get(DispatchClass::Normal) - .base_extrinsic; - let fee: Balance = - ::WeightToFee::weight_to_fee( - &weight, - ); - Executive::initialize_block(&Header::new( - 1, - H256::default(), - H256::default(), - [69u8; 32].into(), - Digest::default(), - )); - - if lock == WithdrawReasons::except(WithdrawReasons::TRANSACTION_PAYMENT) { - assert!(Executive::apply_extrinsic(xt).unwrap().is_ok()); - // tx fee has been deducted. - assert_eq!(>::total_balance(&1), 111 - fee); - } else { - assert_eq!( - Executive::apply_extrinsic(xt), - Err(InvalidTransaction::Payment.into()), - ); - assert_eq!(>::total_balance(&1), 111); - } - }); - }; + fn can_not_pay_for_tx_fee_on_full_lock() { + let mut t = new_test_ext(1); + t.execute_with(|| { + as fungible::MutateFreeze>::set_freeze( + &(), + &1, + 110, + ) + .unwrap(); + let xt = TestXt::new( + RuntimeCall::System(frame_system::Call::remark { remark: vec![1u8] }), + sign_extra(1, 0, 0), + ); + Executive::initialize_block(&Header::new( + 1, + H256::default(), + H256::default(), + [69u8; 32].into(), + Digest::default(), + )); - execute_with_lock(WithdrawReasons::all()); - execute_with_lock(WithdrawReasons::except(WithdrawReasons::TRANSACTION_PAYMENT)); + assert_eq!(Executive::apply_extrinsic(xt), Err(InvalidTransaction::Payment.into()),); + assert_eq!(>::total_balance(&1), 111); + }); } #[test] @@ -1443,7 +1427,7 @@ mod tests { #[test] fn custom_runtime_upgrade_is_called_when_using_execute_block_trait() { let xt = TestXt::new( - RuntimeCall::Balances(BalancesCall::transfer { dest: 33, value: 0 }), + RuntimeCall::Balances(BalancesCall::transfer_allow_death { dest: 33, value: 0 }), sign_extra(1, 0, 0), ); @@ -1569,7 +1553,7 @@ mod tests { #[should_panic(expected = "Invalid inherent position for extrinsic at index 1")] fn invalid_inherent_position_fail() { let xt1 = TestXt::new( - RuntimeCall::Balances(BalancesCall::transfer { dest: 33, value: 0 }), + RuntimeCall::Balances(BalancesCall::transfer_allow_death { dest: 33, value: 0 }), sign_extra(1, 0, 0), ); let xt2 = TestXt::new(RuntimeCall::Custom(custom::Call::inherent_call {}), None); diff --git a/frame/fast-unstake/src/mock.rs b/frame/fast-unstake/src/mock.rs index c9a1d6d51..fbe6c4592 100644 --- a/frame/fast-unstake/src/mock.rs +++ b/frame/fast-unstake/src/mock.rs @@ -89,6 +89,10 @@ impl pallet_balances::Config for Runtime { type ExistentialDeposit = ExistentialDeposit; type AccountStore = System; type WeightInfo = (); + type FreezeIdentifier = (); + type MaxFreezes = (); + type HoldIdentifier = (); + type MaxHolds = (); } pallet_staking_reward_curve::build! { diff --git a/frame/grandpa/src/mock.rs b/frame/grandpa/src/mock.rs index 4e4f2f79d..a7359f689 100644 --- a/frame/grandpa/src/mock.rs +++ b/frame/grandpa/src/mock.rs @@ -142,6 +142,10 @@ impl pallet_balances::Config for Test { type ExistentialDeposit = ConstU128<1>; type AccountStore = System; type WeightInfo = (); + type FreezeIdentifier = (); + type MaxFreezes = (); + type HoldIdentifier = (); + type MaxHolds = (); } impl pallet_timestamp::Config for Test { diff --git a/frame/identity/src/tests.rs b/frame/identity/src/tests.rs index 0e15f08dd..ba9749172 100644 --- a/frame/identity/src/tests.rs +++ b/frame/identity/src/tests.rs @@ -85,6 +85,10 @@ impl pallet_balances::Config for Test { type MaxReserves = (); type ReserveIdentifier = [u8; 8]; type WeightInfo = (); + type FreezeIdentifier = (); + type MaxFreezes = (); + type HoldIdentifier = (); + type MaxHolds = (); } parameter_types! { diff --git a/frame/indices/src/mock.rs b/frame/indices/src/mock.rs index 6cf1f9d97..8bd05d04a 100644 --- a/frame/indices/src/mock.rs +++ b/frame/indices/src/mock.rs @@ -76,6 +76,10 @@ impl pallet_balances::Config for Test { type ExistentialDeposit = ConstU64<1>; type AccountStore = System; type WeightInfo = (); + type FreezeIdentifier = (); + type MaxFreezes = (); + type HoldIdentifier = (); + type MaxHolds = (); } impl Config for Test { diff --git a/frame/lottery/src/mock.rs b/frame/lottery/src/mock.rs index 9b1a933d0..7afd0e319 100644 --- a/frame/lottery/src/mock.rs +++ b/frame/lottery/src/mock.rs @@ -89,6 +89,10 @@ impl pallet_balances::Config for Test { type ExistentialDeposit = ConstU64<1>; type AccountStore = System; type WeightInfo = (); + type FreezeIdentifier = (); + type MaxFreezes = (); + type HoldIdentifier = (); + type MaxHolds = (); } parameter_types! { diff --git a/frame/lottery/src/tests.rs b/frame/lottery/src/tests.rs index 09386c7e3..ae3a6c858 100644 --- a/frame/lottery/src/tests.rs +++ b/frame/lottery/src/tests.rs @@ -23,8 +23,7 @@ use mock::{ new_test_ext, run_to_block, Balances, BalancesCall, Lottery, RuntimeCall, RuntimeOrigin, SystemCall, Test, }; -use pallet_balances::Error as BalancesError; -use sp_runtime::traits::BadOrigin; +use sp_runtime::{traits::BadOrigin, TokenError}; #[test] fn initial_state() { @@ -45,7 +44,7 @@ fn basic_end_to_end_works() { let delay = 5; let calls = vec![ RuntimeCall::Balances(BalancesCall::force_transfer { source: 0, dest: 0, value: 0 }), - RuntimeCall::Balances(BalancesCall::transfer { dest: 0, value: 0 }), + RuntimeCall::Balances(BalancesCall::transfer_allow_death { dest: 0, value: 0 }), ]; // Set calls for the lottery @@ -56,7 +55,10 @@ fn basic_end_to_end_works() { assert!(crate::Lottery::::get().is_some()); assert_eq!(Balances::free_balance(&1), 100); - let call = Box::new(RuntimeCall::Balances(BalancesCall::transfer { dest: 2, value: 20 })); + let call = Box::new(RuntimeCall::Balances(BalancesCall::transfer_allow_death { + dest: 2, + value: 20, + })); assert_ok!(Lottery::buy_ticket(RuntimeOrigin::signed(1), call.clone())); // 20 from the transfer, 10 from buying a ticket assert_eq!(Balances::free_balance(&1), 100 - 20 - 10); @@ -129,7 +131,7 @@ fn set_calls_works() { let calls = vec![ RuntimeCall::Balances(BalancesCall::force_transfer { source: 0, dest: 0, value: 0 }), - RuntimeCall::Balances(BalancesCall::transfer { dest: 0, value: 0 }), + RuntimeCall::Balances(BalancesCall::transfer_allow_death { dest: 0, value: 0 }), ]; assert_ok!(Lottery::set_calls(RuntimeOrigin::root(), calls)); @@ -137,7 +139,7 @@ fn set_calls_works() { let too_many_calls = vec![ RuntimeCall::Balances(BalancesCall::force_transfer { source: 0, dest: 0, value: 0 }), - RuntimeCall::Balances(BalancesCall::transfer { dest: 0, value: 0 }), + RuntimeCall::Balances(BalancesCall::transfer_allow_death { dest: 0, value: 0 }), RuntimeCall::System(SystemCall::remark { remark: vec![] }), ]; @@ -157,7 +159,7 @@ fn call_to_indices_works() { new_test_ext().execute_with(|| { let calls = vec![ RuntimeCall::Balances(BalancesCall::force_transfer { source: 0, dest: 0, value: 0 }), - RuntimeCall::Balances(BalancesCall::transfer { dest: 0, value: 0 }), + RuntimeCall::Balances(BalancesCall::transfer_allow_death { dest: 0, value: 0 }), ]; let indices = Lottery::calls_to_indices(&calls).unwrap().into_inner(); // Only comparing the length since it is otherwise dependant on the API @@ -166,7 +168,7 @@ fn call_to_indices_works() { let too_many_calls = vec![ RuntimeCall::Balances(BalancesCall::force_transfer { source: 0, dest: 0, value: 0 }), - RuntimeCall::Balances(BalancesCall::transfer { dest: 0, value: 0 }), + RuntimeCall::Balances(BalancesCall::transfer_allow_death { dest: 0, value: 0 }), RuntimeCall::System(SystemCall::remark { remark: vec![] }), ]; assert_noop!(Lottery::calls_to_indices(&too_many_calls), Error::::TooManyCalls); @@ -203,7 +205,10 @@ fn buy_ticket_works_as_simple_passthrough() { // as a simple passthrough to the real call. new_test_ext().execute_with(|| { // No lottery set up - let call = Box::new(RuntimeCall::Balances(BalancesCall::transfer { dest: 2, value: 20 })); + let call = Box::new(RuntimeCall::Balances(BalancesCall::transfer_allow_death { + dest: 2, + value: 20, + })); // This is just a basic transfer then assert_ok!(Lottery::buy_ticket(RuntimeOrigin::signed(1), call.clone())); assert_eq!(Balances::free_balance(&1), 100 - 20); @@ -212,7 +217,7 @@ fn buy_ticket_works_as_simple_passthrough() { // Lottery is set up, but too expensive to enter, so `do_buy_ticket` fails. let calls = vec![ RuntimeCall::Balances(BalancesCall::force_transfer { source: 0, dest: 0, value: 0 }), - RuntimeCall::Balances(BalancesCall::transfer { dest: 0, value: 0 }), + RuntimeCall::Balances(BalancesCall::transfer_allow_death { dest: 0, value: 0 }), ]; assert_ok!(Lottery::set_calls(RuntimeOrigin::root(), calls)); @@ -223,11 +228,13 @@ fn buy_ticket_works_as_simple_passthrough() { assert_eq!(TicketsCount::::get(), 0); // If call would fail, the whole thing still fails the same - let fail_call = - Box::new(RuntimeCall::Balances(BalancesCall::transfer { dest: 2, value: 1000 })); + let fail_call = Box::new(RuntimeCall::Balances(BalancesCall::transfer_allow_death { + dest: 2, + value: 1000, + })); assert_noop!( Lottery::buy_ticket(RuntimeOrigin::signed(1), fail_call), - BalancesError::::InsufficientBalance, + ArithmeticError::Underflow, ); let bad_origin_call = Box::new(RuntimeCall::Balances(BalancesCall::force_transfer { @@ -243,8 +250,10 @@ fn buy_ticket_works_as_simple_passthrough() { assert_ok!(Lottery::buy_ticket(RuntimeOrigin::signed(2), remark_call)); assert_eq!(TicketsCount::::get(), 0); - let successful_call = - Box::new(RuntimeCall::Balances(BalancesCall::transfer { dest: 2, value: 1 })); + let successful_call = Box::new(RuntimeCall::Balances(BalancesCall::transfer_allow_death { + dest: 2, + value: 1, + })); assert_ok!(Lottery::buy_ticket(RuntimeOrigin::signed(2), successful_call)); assert_eq!(TicketsCount::::get(), 1); }); @@ -256,12 +265,15 @@ fn buy_ticket_works() { // Set calls for the lottery. let calls = vec![ RuntimeCall::System(SystemCall::remark { remark: vec![] }), - RuntimeCall::Balances(BalancesCall::transfer { dest: 0, value: 0 }), + RuntimeCall::Balances(BalancesCall::transfer_allow_death { dest: 0, value: 0 }), ]; assert_ok!(Lottery::set_calls(RuntimeOrigin::root(), calls)); // Can't buy ticket before start - let call = Box::new(RuntimeCall::Balances(BalancesCall::transfer { dest: 2, value: 1 })); + let call = Box::new(RuntimeCall::Balances(BalancesCall::transfer_allow_death { + dest: 2, + value: 1, + })); assert_ok!(Lottery::buy_ticket(RuntimeOrigin::signed(1), call.clone())); assert_eq!(TicketsCount::::get(), 0); @@ -274,7 +286,10 @@ fn buy_ticket_works() { assert_eq!(TicketsCount::::get(), 1); // Can't buy another of the same ticket (even if call is slightly changed) - let call = Box::new(RuntimeCall::Balances(BalancesCall::transfer { dest: 3, value: 30 })); + let call = Box::new(RuntimeCall::Balances(BalancesCall::transfer_allow_death { + dest: 3, + value: 30, + })); assert_ok!(Lottery::buy_ticket(RuntimeOrigin::signed(1), call)); assert_eq!(TicketsCount::::get(), 1); @@ -302,7 +317,8 @@ fn buy_ticket_works() { #[test] fn do_buy_ticket_already_participating() { new_test_ext().execute_with(|| { - let calls = vec![RuntimeCall::Balances(BalancesCall::transfer { dest: 0, value: 0 })]; + let calls = + vec![RuntimeCall::Balances(BalancesCall::transfer_allow_death { dest: 0, value: 0 })]; assert_ok!(Lottery::set_calls(RuntimeOrigin::root(), calls.clone())); assert_ok!(Lottery::start_lottery(RuntimeOrigin::root(), 1, 10, 10, false)); @@ -317,7 +333,8 @@ fn do_buy_ticket_already_participating() { #[test] fn buy_ticket_already_participating() { new_test_ext().execute_with(|| { - let calls = vec![RuntimeCall::Balances(BalancesCall::transfer { dest: 0, value: 0 })]; + let calls = + vec![RuntimeCall::Balances(BalancesCall::transfer_allow_death { dest: 0, value: 0 })]; assert_ok!(Lottery::set_calls(RuntimeOrigin::root(), calls.clone())); assert_ok!(Lottery::start_lottery(RuntimeOrigin::root(), 1, 10, 10, false)); @@ -337,7 +354,8 @@ fn buy_ticket_already_participating() { #[test] fn buy_ticket_insufficient_balance() { new_test_ext().execute_with(|| { - let calls = vec![RuntimeCall::Balances(BalancesCall::transfer { dest: 0, value: 0 })]; + let calls = + vec![RuntimeCall::Balances(BalancesCall::transfer_allow_death { dest: 0, value: 0 })]; assert_ok!(Lottery::set_calls(RuntimeOrigin::root(), calls.clone())); // Price set to 100. assert_ok!(Lottery::start_lottery(RuntimeOrigin::root(), 100, 10, 10, false)); @@ -352,16 +370,14 @@ fn buy_ticket_insufficient_balance() { #[test] fn do_buy_ticket_insufficient_balance() { new_test_ext().execute_with(|| { - let calls = vec![RuntimeCall::Balances(BalancesCall::transfer { dest: 0, value: 0 })]; + let calls = + vec![RuntimeCall::Balances(BalancesCall::transfer_allow_death { dest: 0, value: 0 })]; assert_ok!(Lottery::set_calls(RuntimeOrigin::root(), calls.clone())); // Price set to 101. assert_ok!(Lottery::start_lottery(RuntimeOrigin::root(), 101, 10, 10, false)); // Buying fails with InsufficientBalance. - assert_noop!( - Lottery::do_buy_ticket(&1, &calls[0]), - BalancesError::::InsufficientBalance - ); + assert_noop!(Lottery::do_buy_ticket(&1, &calls[0]), TokenError::FundsUnavailable,); assert!(TicketsCount::::get().is_zero()); }); } @@ -369,13 +385,13 @@ fn do_buy_ticket_insufficient_balance() { #[test] fn do_buy_ticket_keep_alive() { new_test_ext().execute_with(|| { - let calls = vec![RuntimeCall::Balances(BalancesCall::transfer { dest: 0, value: 0 })]; + let calls = + vec![RuntimeCall::Balances(BalancesCall::transfer_allow_death { dest: 0, value: 0 })]; assert_ok!(Lottery::set_calls(RuntimeOrigin::root(), calls.clone())); // Price set to 100. assert_ok!(Lottery::start_lottery(RuntimeOrigin::root(), 100, 10, 10, false)); - // Buying fails with KeepAlive. - assert_noop!(Lottery::do_buy_ticket(&1, &calls[0]), BalancesError::::KeepAlive); + assert_noop!(Lottery::do_buy_ticket(&1, &calls[0]), TokenError::NotExpendable); assert!(TicketsCount::::get().is_zero()); }); } @@ -421,7 +437,8 @@ fn choose_ticket_trivial_cases() { #[test] fn choose_account_one_participant() { new_test_ext().execute_with(|| { - let calls = vec![RuntimeCall::Balances(BalancesCall::transfer { dest: 0, value: 0 })]; + let calls = + vec![RuntimeCall::Balances(BalancesCall::transfer_allow_death { dest: 0, value: 0 })]; assert_ok!(Lottery::set_calls(RuntimeOrigin::root(), calls.clone())); assert_ok!(Lottery::start_lottery(RuntimeOrigin::root(), 10, 10, 10, false)); let call = Box::new(calls[0].clone()); diff --git a/frame/multisig/src/tests.rs b/frame/multisig/src/tests.rs index c5517195d..7e7f16680 100644 --- a/frame/multisig/src/tests.rs +++ b/frame/multisig/src/tests.rs @@ -30,6 +30,7 @@ use sp_core::H256; use sp_runtime::{ testing::Header, traits::{BlakeTwo256, IdentityLookup}, + TokenError, }; type UncheckedExtrinsic = frame_system::mocking::MockUncheckedExtrinsic; @@ -84,6 +85,10 @@ impl pallet_balances::Config for Test { type ExistentialDeposit = ConstU64<1>; type AccountStore = System; type WeightInfo = (); + type FreezeIdentifier = (); + type MaxFreezes = (); + type HoldIdentifier = (); + type MaxHolds = (); } pub struct TestBaseCallFilter; @@ -107,7 +112,7 @@ impl Config for Test { type WeightInfo = (); } -use pallet_balances::{Call as BalancesCall, Error as BalancesError}; +use pallet_balances::Call as BalancesCall; pub fn new_test_ext() -> sp_io::TestExternalities { let mut t = frame_system::GenesisConfig::default().build_storage::().unwrap(); @@ -126,16 +131,16 @@ fn now() -> Timepoint { } fn call_transfer(dest: u64, value: u64) -> Box { - Box::new(RuntimeCall::Balances(BalancesCall::transfer { dest, value })) + Box::new(RuntimeCall::Balances(BalancesCall::transfer_allow_death { dest, value })) } #[test] fn multisig_deposit_is_taken_and_returned() { new_test_ext().execute_with(|| { let multi = Multisig::multi_account_id(&[1, 2, 3][..], 2); - assert_ok!(Balances::transfer(RuntimeOrigin::signed(1), multi, 5)); - assert_ok!(Balances::transfer(RuntimeOrigin::signed(2), multi, 5)); - assert_ok!(Balances::transfer(RuntimeOrigin::signed(3), multi, 5)); + assert_ok!(Balances::transfer_allow_death(RuntimeOrigin::signed(1), multi, 5)); + assert_ok!(Balances::transfer_allow_death(RuntimeOrigin::signed(2), multi, 5)); + assert_ok!(Balances::transfer_allow_death(RuntimeOrigin::signed(3), multi, 5)); let call = call_transfer(6, 15); let call_weight = call.get_dispatch_info().weight; @@ -196,9 +201,9 @@ fn cancel_multisig_returns_deposit() { fn timepoint_checking_works() { new_test_ext().execute_with(|| { let multi = Multisig::multi_account_id(&[1, 2, 3][..], 2); - assert_ok!(Balances::transfer(RuntimeOrigin::signed(1), multi, 5)); - assert_ok!(Balances::transfer(RuntimeOrigin::signed(2), multi, 5)); - assert_ok!(Balances::transfer(RuntimeOrigin::signed(3), multi, 5)); + assert_ok!(Balances::transfer_allow_death(RuntimeOrigin::signed(1), multi, 5)); + assert_ok!(Balances::transfer_allow_death(RuntimeOrigin::signed(2), multi, 5)); + assert_ok!(Balances::transfer_allow_death(RuntimeOrigin::signed(3), multi, 5)); let call = call_transfer(6, 15); let hash = blake2_256(&call.encode()); @@ -254,9 +259,9 @@ fn timepoint_checking_works() { fn multisig_2_of_3_works() { new_test_ext().execute_with(|| { let multi = Multisig::multi_account_id(&[1, 2, 3][..], 2); - assert_ok!(Balances::transfer(RuntimeOrigin::signed(1), multi, 5)); - assert_ok!(Balances::transfer(RuntimeOrigin::signed(2), multi, 5)); - assert_ok!(Balances::transfer(RuntimeOrigin::signed(3), multi, 5)); + assert_ok!(Balances::transfer_allow_death(RuntimeOrigin::signed(1), multi, 5)); + assert_ok!(Balances::transfer_allow_death(RuntimeOrigin::signed(2), multi, 5)); + assert_ok!(Balances::transfer_allow_death(RuntimeOrigin::signed(3), multi, 5)); let call = call_transfer(6, 15); let call_weight = call.get_dispatch_info().weight; @@ -287,9 +292,9 @@ fn multisig_2_of_3_works() { fn multisig_3_of_3_works() { new_test_ext().execute_with(|| { let multi = Multisig::multi_account_id(&[1, 2, 3][..], 3); - assert_ok!(Balances::transfer(RuntimeOrigin::signed(1), multi, 5)); - assert_ok!(Balances::transfer(RuntimeOrigin::signed(2), multi, 5)); - assert_ok!(Balances::transfer(RuntimeOrigin::signed(3), multi, 5)); + assert_ok!(Balances::transfer_allow_death(RuntimeOrigin::signed(1), multi, 5)); + assert_ok!(Balances::transfer_allow_death(RuntimeOrigin::signed(2), multi, 5)); + assert_ok!(Balances::transfer_allow_death(RuntimeOrigin::signed(3), multi, 5)); let call = call_transfer(6, 15); let call_weight = call.get_dispatch_info().weight; @@ -357,9 +362,9 @@ fn cancel_multisig_works() { fn multisig_2_of_3_as_multi_works() { new_test_ext().execute_with(|| { let multi = Multisig::multi_account_id(&[1, 2, 3][..], 2); - assert_ok!(Balances::transfer(RuntimeOrigin::signed(1), multi, 5)); - assert_ok!(Balances::transfer(RuntimeOrigin::signed(2), multi, 5)); - assert_ok!(Balances::transfer(RuntimeOrigin::signed(3), multi, 5)); + assert_ok!(Balances::transfer_allow_death(RuntimeOrigin::signed(1), multi, 5)); + assert_ok!(Balances::transfer_allow_death(RuntimeOrigin::signed(2), multi, 5)); + assert_ok!(Balances::transfer_allow_death(RuntimeOrigin::signed(3), multi, 5)); let call = call_transfer(6, 15); let call_weight = call.get_dispatch_info().weight; @@ -389,9 +394,9 @@ fn multisig_2_of_3_as_multi_works() { fn multisig_2_of_3_as_multi_with_many_calls_works() { new_test_ext().execute_with(|| { let multi = Multisig::multi_account_id(&[1, 2, 3][..], 2); - assert_ok!(Balances::transfer(RuntimeOrigin::signed(1), multi, 5)); - assert_ok!(Balances::transfer(RuntimeOrigin::signed(2), multi, 5)); - assert_ok!(Balances::transfer(RuntimeOrigin::signed(3), multi, 5)); + assert_ok!(Balances::transfer_allow_death(RuntimeOrigin::signed(1), multi, 5)); + assert_ok!(Balances::transfer_allow_death(RuntimeOrigin::signed(2), multi, 5)); + assert_ok!(Balances::transfer_allow_death(RuntimeOrigin::signed(3), multi, 5)); let call1 = call_transfer(6, 10); let call1_weight = call1.get_dispatch_info().weight; @@ -440,9 +445,9 @@ fn multisig_2_of_3_as_multi_with_many_calls_works() { fn multisig_2_of_3_cannot_reissue_same_call() { new_test_ext().execute_with(|| { let multi = Multisig::multi_account_id(&[1, 2, 3][..], 2); - assert_ok!(Balances::transfer(RuntimeOrigin::signed(1), multi, 5)); - assert_ok!(Balances::transfer(RuntimeOrigin::signed(2), multi, 5)); - assert_ok!(Balances::transfer(RuntimeOrigin::signed(3), multi, 5)); + assert_ok!(Balances::transfer_allow_death(RuntimeOrigin::signed(1), multi, 5)); + assert_ok!(Balances::transfer_allow_death(RuntimeOrigin::signed(2), multi, 5)); + assert_ok!(Balances::transfer_allow_death(RuntimeOrigin::signed(3), multi, 5)); let call = call_transfer(6, 10); let call_weight = call.get_dispatch_info().weight; @@ -482,14 +487,13 @@ fn multisig_2_of_3_cannot_reissue_same_call() { call_weight )); - let err = DispatchError::from(BalancesError::::InsufficientBalance).stripped(); System::assert_last_event( pallet_multisig::Event::MultisigExecuted { approving: 3, timepoint: now(), multisig: multi, call_hash: hash, - result: Err(err), + result: Err(TokenError::FundsUnavailable.into()), } .into(), ); @@ -593,9 +597,9 @@ fn duplicate_approvals_are_ignored() { fn multisig_1_of_3_works() { new_test_ext().execute_with(|| { let multi = Multisig::multi_account_id(&[1, 2, 3][..], 1); - assert_ok!(Balances::transfer(RuntimeOrigin::signed(1), multi, 5)); - assert_ok!(Balances::transfer(RuntimeOrigin::signed(2), multi, 5)); - assert_ok!(Balances::transfer(RuntimeOrigin::signed(3), multi, 5)); + assert_ok!(Balances::transfer_allow_death(RuntimeOrigin::signed(1), multi, 5)); + assert_ok!(Balances::transfer_allow_death(RuntimeOrigin::signed(2), multi, 5)); + assert_ok!(Balances::transfer_allow_death(RuntimeOrigin::signed(3), multi, 5)); let call = call_transfer(6, 15); let hash = blake2_256(&call.encode()); @@ -646,9 +650,9 @@ fn multisig_filters() { fn weight_check_works() { new_test_ext().execute_with(|| { let multi = Multisig::multi_account_id(&[1, 2, 3][..], 2); - assert_ok!(Balances::transfer(RuntimeOrigin::signed(1), multi, 5)); - assert_ok!(Balances::transfer(RuntimeOrigin::signed(2), multi, 5)); - assert_ok!(Balances::transfer(RuntimeOrigin::signed(3), multi, 5)); + assert_ok!(Balances::transfer_allow_death(RuntimeOrigin::signed(1), multi, 5)); + assert_ok!(Balances::transfer_allow_death(RuntimeOrigin::signed(2), multi, 5)); + assert_ok!(Balances::transfer_allow_death(RuntimeOrigin::signed(3), multi, 5)); let call = call_transfer(6, 15); assert_ok!(Multisig::as_multi( @@ -682,9 +686,9 @@ fn multisig_handles_no_preimage_after_all_approve() { // the call will go through. new_test_ext().execute_with(|| { let multi = Multisig::multi_account_id(&[1, 2, 3][..], 3); - assert_ok!(Balances::transfer(RuntimeOrigin::signed(1), multi, 5)); - assert_ok!(Balances::transfer(RuntimeOrigin::signed(2), multi, 5)); - assert_ok!(Balances::transfer(RuntimeOrigin::signed(3), multi, 5)); + assert_ok!(Balances::transfer_allow_death(RuntimeOrigin::signed(1), multi, 5)); + assert_ok!(Balances::transfer_allow_death(RuntimeOrigin::signed(2), multi, 5)); + assert_ok!(Balances::transfer_allow_death(RuntimeOrigin::signed(3), multi, 5)); let call = call_transfer(6, 15); let call_weight = call.get_dispatch_info().weight; diff --git a/frame/nfts/src/mock.rs b/frame/nfts/src/mock.rs index f21547b72..91ac5d6a7 100644 --- a/frame/nfts/src/mock.rs +++ b/frame/nfts/src/mock.rs @@ -89,6 +89,10 @@ impl pallet_balances::Config for Test { type MaxLocks = (); type MaxReserves = ConstU32<50>; type ReserveIdentifier = [u8; 8]; + type FreezeIdentifier = (); + type MaxFreezes = (); + type HoldIdentifier = (); + type MaxHolds = (); } parameter_types! { diff --git a/frame/nicks/src/lib.rs b/frame/nicks/src/lib.rs index b74d2e6ee..5f510ce8e 100644 --- a/frame/nicks/src/lib.rs +++ b/frame/nicks/src/lib.rs @@ -295,6 +295,10 @@ mod tests { type ExistentialDeposit = ConstU64<1>; type AccountStore = System; type WeightInfo = (); + type FreezeIdentifier = (); + type MaxFreezes = (); + type HoldIdentifier = (); + type MaxHolds = (); } ord_parameter_types! { diff --git a/frame/nis/README.md b/frame/nis/README.md index 4eaddae17..0c3f0c383 100644 --- a/frame/nis/README.md +++ b/frame/nis/README.md @@ -1,2 +1,5 @@ +# NIS Module -License: Apache-2.0 +Provides a non-interactiove variant of staking. + +License: Apache-2.0 \ No newline at end of file diff --git a/frame/nis/src/benchmarking.rs b/frame/nis/src/benchmarking.rs index 6fc9fbc2b..0cc9e7421 100644 --- a/frame/nis/src/benchmarking.rs +++ b/frame/nis/src/benchmarking.rs @@ -21,7 +21,9 @@ use super::*; use frame_benchmarking::v1::{account, benchmarks, whitelisted_caller, BenchmarkError}; -use frame_support::traits::{nonfungible::Inspect, Currency, EnsureOrigin, Get}; +use frame_support::traits::{ + fungible::Inspect as FunInspect, nonfungible::Inspect, EnsureOrigin, Get, +}; use frame_system::RawOrigin; use sp_arithmetic::Perquintill; use sp_runtime::{ @@ -35,7 +37,7 @@ use crate::Pallet as Nis; const SEED: u32 = 0; type BalanceOf = - <::Currency as Currency<::AccountId>>::Balance; + <::Currency as FunInspect<::AccountId>>::Balance; fn fill_queues() -> Result<(), DispatchError> { // filling queues involves filling the first queue entirely and placing a single item in all @@ -45,10 +47,7 @@ fn fill_queues() -> Result<(), DispatchError> { let bids = T::MaxQueueLen::get(); let caller: T::AccountId = whitelisted_caller(); - T::Currency::make_free_balance_be( - &caller, - T::MinBid::get() * BalanceOf::::from(queues + bids), - ); + T::Currency::set_balance(&caller, T::MinBid::get() * BalanceOf::::from(queues + bids)); for _ in 0..bids { Nis::::place_bid(RawOrigin::Signed(caller.clone()).into(), T::MinBid::get(), 1)?; @@ -63,7 +62,9 @@ benchmarks! { place_bid { let l in 0..(T::MaxQueueLen::get() - 1); let caller: T::AccountId = whitelisted_caller(); - T::Currency::make_free_balance_be(&caller, BalanceOf::::max_value()); + let ed = T::Currency::minimum_balance(); + let bid = T::MinBid::get(); + T::Currency::set_balance(&caller, (ed + bid) * BalanceOf::::from(l + 1) + bid); for i in 0..l { Nis::::place_bid(RawOrigin::Signed(caller.clone()).into(), T::MinBid::get(), 1)?; } @@ -75,7 +76,10 @@ benchmarks! { place_bid_max { let caller: T::AccountId = whitelisted_caller(); let origin = RawOrigin::Signed(caller.clone()); - T::Currency::make_free_balance_be(&caller, BalanceOf::::max_value()); + let ed = T::Currency::minimum_balance(); + let bid = T::MinBid::get(); + let ql = T::MaxQueueLen::get(); + T::Currency::set_balance(&caller, (ed + bid) * BalanceOf::::from(ql + 1) + bid); for i in 0..T::MaxQueueLen::get() { Nis::::place_bid(origin.clone().into(), T::MinBid::get(), 1)?; } @@ -90,7 +94,9 @@ benchmarks! { retract_bid { let l in 1..T::MaxQueueLen::get(); let caller: T::AccountId = whitelisted_caller(); - T::Currency::make_free_balance_be(&caller, BalanceOf::::max_value()); + let ed = T::Currency::minimum_balance(); + let bid = T::MinBid::get(); + T::Currency::set_balance(&caller, (ed + bid) * BalanceOf::::from(l + 1) + bid); for i in 0..l { Nis::::place_bid(RawOrigin::Signed(caller.clone()).into(), T::MinBid::get(), 1)?; } @@ -104,66 +110,81 @@ benchmarks! { T::FundOrigin::try_successful_origin().map_err(|_| BenchmarkError::Weightless)?; let caller: T::AccountId = whitelisted_caller(); let bid = T::MinBid::get().max(One::one()); - T::Currency::make_free_balance_be(&caller, bid); + let ed = T::Currency::minimum_balance(); + T::Currency::set_balance(&caller, ed + bid); Nis::::place_bid(RawOrigin::Signed(caller.clone()).into(), bid, 1)?; Nis::::process_queues(Perquintill::one(), 1, 1, &mut WeightCounter::unlimited()); Nis::::communify(RawOrigin::Signed(caller.clone()).into(), 0)?; - let original = T::Currency::free_balance(&Nis::::account_id()); - T::Currency::make_free_balance_be(&Nis::::account_id(), BalanceOf::::min_value()); + let original = T::Currency::balance(&Nis::::account_id()); + T::Currency::set_balance(&Nis::::account_id(), BalanceOf::::min_value()); }: _(origin) verify { // Must fund at least 99.999% of the required amount. let missing = Perquintill::from_rational( - T::Currency::free_balance(&Nis::::account_id()), original).left_from_one(); + T::Currency::balance(&Nis::::account_id()), original).left_from_one(); assert!(missing <= Perquintill::one() / 100_000); } - thaw_private { + communify { let caller: T::AccountId = whitelisted_caller(); - T::Currency::make_free_balance_be(&caller, T::MinBid::get() * BalanceOf::::from(3u32)); - Nis::::place_bid(RawOrigin::Signed(caller.clone()).into(), T::MinBid::get(), 1)?; - Nis::::place_bid(RawOrigin::Signed(caller.clone()).into(), T::MinBid::get(), 1)?; + let bid = T::MinBid::get().max(One::one()) * 100u32.into(); + let ed = T::Currency::minimum_balance(); + T::Currency::set_balance(&caller, ed + bid + bid); + Nis::::place_bid(RawOrigin::Signed(caller.clone()).into(), bid, 1)?; + Nis::::place_bid(RawOrigin::Signed(caller.clone()).into(), bid, 1)?; Nis::::process_queues(Perquintill::one(), 1, 2, &mut WeightCounter::unlimited()); - Receipts::::mutate(0, |m_g| if let Some(ref mut g) = m_g { g.expiry = Zero::zero() }); - }: _(RawOrigin::Signed(caller.clone()), 0, None) + }: _(RawOrigin::Signed(caller.clone()), 0) verify { - assert!(Receipts::::get(0).is_none()); + assert_eq!(Nis::::owner(&0), None); } - thaw_communal { + privatize { let caller: T::AccountId = whitelisted_caller(); - T::Currency::make_free_balance_be(&caller, T::MinBid::get() * BalanceOf::::from(3u32)); - Nis::::place_bid(RawOrigin::Signed(caller.clone()).into(), T::MinBid::get(), 1)?; - Nis::::place_bid(RawOrigin::Signed(caller.clone()).into(), T::MinBid::get(), 1)?; + let bid = T::MinBid::get().max(One::one()); + let ed = T::Currency::minimum_balance(); + T::Currency::set_balance(&caller, ed + bid + bid); + Nis::::place_bid(RawOrigin::Signed(caller.clone()).into(), bid, 1)?; + Nis::::place_bid(RawOrigin::Signed(caller.clone()).into(), bid, 1)?; Nis::::process_queues(Perquintill::one(), 1, 2, &mut WeightCounter::unlimited()); - Receipts::::mutate(0, |m_g| if let Some(ref mut g) = m_g { g.expiry = Zero::zero() }); Nis::::communify(RawOrigin::Signed(caller.clone()).into(), 0)?; }: _(RawOrigin::Signed(caller.clone()), 0) verify { - assert!(Receipts::::get(0).is_none()); + assert_eq!(Nis::::owner(&0), Some(caller)); } - privatize { + thaw_private { + let whale: T::AccountId = account("whale", 0, SEED); let caller: T::AccountId = whitelisted_caller(); - T::Currency::make_free_balance_be(&caller, T::MinBid::get() * BalanceOf::::from(3u32)); - Nis::::place_bid(RawOrigin::Signed(caller.clone()).into(), T::MinBid::get(), 1)?; - Nis::::place_bid(RawOrigin::Signed(caller.clone()).into(), T::MinBid::get(), 1)?; + let bid = T::MinBid::get().max(One::one()); + let ed = T::Currency::minimum_balance(); + T::Currency::set_balance(&caller, ed + bid + bid); + // Ensure we don't get throttled. + T::Currency::set_balance(&whale, T::ThawThrottle::get().0.saturating_reciprocal_mul_ceil(T::Currency::balance(&caller))); + Nis::::place_bid(RawOrigin::Signed(caller.clone()).into(), bid, 1)?; + Nis::::place_bid(RawOrigin::Signed(caller.clone()).into(), bid, 1)?; Nis::::process_queues(Perquintill::one(), 1, 2, &mut WeightCounter::unlimited()); - Nis::::communify(RawOrigin::Signed(caller.clone()).into(), 0)?; - }: _(RawOrigin::Signed(caller.clone()), 0) + frame_system::Pallet::::set_block_number(Receipts::::get(0).unwrap().expiry); + }: _(RawOrigin::Signed(caller.clone()), 0, None) verify { - assert_eq!(Nis::::owner(&0), Some(caller)); + assert!(Receipts::::get(0).is_none()); } - communify { + thaw_communal { + let whale: T::AccountId = account("whale", 0, SEED); let caller: T::AccountId = whitelisted_caller(); - T::Currency::make_free_balance_be(&caller, T::MinBid::get() * BalanceOf::::from(3u32)); - Nis::::place_bid(RawOrigin::Signed(caller.clone()).into(), T::MinBid::get(), 1)?; - Nis::::place_bid(RawOrigin::Signed(caller.clone()).into(), T::MinBid::get(), 1)?; + let bid = T::MinBid::get().max(One::one()); + let ed = T::Currency::minimum_balance(); + T::Currency::set_balance(&caller, ed + bid + bid); + // Ensure we don't get throttled. + T::Currency::set_balance(&whale, T::ThawThrottle::get().0.saturating_reciprocal_mul_ceil(T::Currency::balance(&caller))); + Nis::::place_bid(RawOrigin::Signed(caller.clone()).into(), bid, 1)?; + Nis::::place_bid(RawOrigin::Signed(caller.clone()).into(), bid, 1)?; Nis::::process_queues(Perquintill::one(), 1, 2, &mut WeightCounter::unlimited()); + frame_system::Pallet::::set_block_number(Receipts::::get(0).unwrap().expiry); + Nis::::communify(RawOrigin::Signed(caller.clone()).into(), 0)?; }: _(RawOrigin::Signed(caller.clone()), 0) verify { - assert_eq!(Nis::::owner(&0), None); + assert!(Receipts::::get(0).is_none()); } process_queues { @@ -197,6 +218,9 @@ benchmarks! { process_bid { let who = account::("bidder", 0, SEED); + let min_bid = T::MinBid::get().max(One::one()); + let ed = T::Currency::minimum_balance(); + T::Currency::set_balance(&who, ed + min_bid); let bid = Bid { amount: T::MinBid::get(), who, @@ -216,5 +240,5 @@ benchmarks! { ) } - impl_benchmark_test_suite!(Nis, crate::mock::new_test_ext(), crate::mock::Test); + impl_benchmark_test_suite!(Nis, crate::mock::new_test_ext_empty(), crate::mock::Test); } diff --git a/frame/nis/src/lib.rs b/frame/nis/src/lib.rs index 6fd9c60a8..0b8d292ec 100644 --- a/frame/nis/src/lib.rs +++ b/frame/nis/src/lib.rs @@ -71,21 +71,21 @@ //! //! ## Terms //! -//! - *Effective total issuance*: The total issuance of balances in the system, including all claims -//! of all outstanding receipts but excluding `IgnoredIssuance`. +//! - *Effective total issuance*: The total issuance of balances in the system, equal to the active +//! issuance plus the value of all outstanding receipts, less `IgnoredIssuance`. #![cfg_attr(not(feature = "std"), no_std)] -use frame_support::{ - dispatch::{DispatchError, DispatchResult}, - traits::fungible::{Inspect as FungibleInspect, Mutate as FungibleMutate}, +use frame_support::traits::{ + fungible::{self, Inspect as FunInspect, Mutate as FunMutate}, + tokens::{DepositConsequence, Fortitude, Preservation, Provenance, WithdrawConsequence}, }; pub use pallet::*; use sp_arithmetic::{traits::Unsigned, RationalArg}; use sp_core::TypedGet; use sp_runtime::{ traits::{Convert, ConvertBack}, - Perquintill, + DispatchError, Perquintill, }; mod benchmarking; @@ -117,7 +117,7 @@ where } pub struct NoCounterpart(sp_std::marker::PhantomData); -impl FungibleInspect for NoCounterpart { +impl FunInspect for NoCounterpart { type Balance = u32; fn total_issuance() -> u32 { 0 @@ -125,34 +125,30 @@ impl FungibleInspect for NoCounterpart { fn minimum_balance() -> u32 { 0 } - fn balance(_who: &T) -> u32 { + fn balance(_: &T) -> u32 { 0 } - fn reducible_balance(_who: &T, _keep_alive: bool) -> u32 { + fn total_balance(_: &T) -> u32 { 0 } - fn can_deposit( - _who: &T, - _amount: u32, - _mint: bool, - ) -> frame_support::traits::tokens::DepositConsequence { - frame_support::traits::tokens::DepositConsequence::Success + fn reducible_balance(_: &T, _: Preservation, _: Fortitude) -> u32 { + 0 } - fn can_withdraw( - _who: &T, - _amount: u32, - ) -> frame_support::traits::tokens::WithdrawConsequence { - frame_support::traits::tokens::WithdrawConsequence::Success + fn can_deposit(_: &T, _: u32, _: Provenance) -> DepositConsequence { + DepositConsequence::Success } -} -impl FungibleMutate for NoCounterpart { - fn mint_into(_who: &T, _amount: u32) -> DispatchResult { - Ok(()) + fn can_withdraw(_: &T, _: u32) -> WithdrawConsequence { + WithdrawConsequence::Success } - fn burn_from(_who: &T, _amount: u32) -> Result { - Ok(0) +} +impl fungible::Unbalanced for NoCounterpart { + fn handle_dust(_: fungible::Dust) {} + fn write_balance(_: &T, _: Self::Balance) -> Result, DispatchError> { + Ok(None) } + fn set_total_issuance(_: Self::Balance) {} } +impl FunMutate for NoCounterpart {} impl Convert for NoCounterpart { fn convert(_: Perquintill) -> u32 { 0 @@ -161,15 +157,24 @@ impl Convert for NoCounterpart { #[frame_support::pallet] pub mod pallet { - use super::{FungibleInspect, FungibleMutate}; + use super::{FunInspect, FunMutate}; pub use crate::weights::WeightInfo; use frame_support::{ pallet_prelude::*, traits::{ - nonfungible::{Inspect as NonfungibleInspect, Transfer as NonfungibleTransfer}, - Currency, Defensive, DefensiveSaturating, - ExistenceRequirement::AllowDeath, - NamedReservableCurrency, OnUnbalanced, + fungible::{ + self, + hold::{Inspect as FunHoldInspect, Mutate as FunHoldMutate}, + Balanced as FunBalanced, + }, + nonfungible::{Inspect as NftInspect, Transfer as NftTransfer}, + tokens::{ + Fortitude::Polite, + Precision::{BestEffort, Exact}, + Preservation::Expendable, + Restriction::{Free, OnHold}, + }, + Defensive, DefensiveSaturating, OnUnbalanced, }, PalletId, }; @@ -177,15 +182,14 @@ pub mod pallet { use sp_arithmetic::{PerThing, Perquintill}; use sp_runtime::{ traits::{AccountIdConversion, Bounded, Convert, ConvertBack, Saturating, Zero}, - TokenError, + Rounding, TokenError, }; use sp_std::prelude::*; type BalanceOf = - <::Currency as Currency<::AccountId>>::Balance; - type PositiveImbalanceOf = <::Currency as Currency< - ::AccountId, - >>::PositiveImbalance; + <::Currency as FunInspect<::AccountId>>::Balance; + type DebtOf = + fungible::Debt<::AccountId, ::Currency>; type ReceiptRecordOf = ReceiptRecord< ::AccountId, ::BlockNumber, @@ -209,7 +213,16 @@ pub mod pallet { type PalletId: Get; /// Currency type that this works on. - type Currency: NamedReservableCurrency; + type Currency: FunInspect + + FunMutate + + FunBalanced + + FunHoldInspect + + FunHoldMutate; + + /// The identifier of the hold reason. + + #[pallet::constant] + type HoldReason: Get<>::Reason>; /// Just the `Currency::Balance` type; we have this item to allow us to constrain it to /// `From`. @@ -231,7 +244,7 @@ pub mod pallet { type IgnoredIssuance: Get>; /// The accounting system for the fungible counterpart tokens. - type Counterpart: FungibleMutate; + type Counterpart: FunMutate; /// The system to convert an overall proportion of issuance into a number of fungible /// counterpart tokens. @@ -239,12 +252,12 @@ pub mod pallet { /// In general it's best to use `WithMaximumOf`. type CounterpartAmount: ConvertBack< Perquintill, - >::Balance, + >::Balance, >; /// Unbalanced handler to account for funds created (in case of a higher total issuance over /// freezing period). - type Deficit: OnUnbalanced>; + type Deficit: OnUnbalanced>; /// The target sum of all receipts' proportions. type Target: Get; @@ -301,12 +314,6 @@ pub mod pallet { /// The maximum proportion which may be thawed and the period over which it is reset. #[pallet::constant] type ThawThrottle: Get<(Perquintill, Self::BlockNumber)>; - - /// The name for the reserve ID. - #[pallet::constant] - type ReserveId: Get< - >::ReserveIdentifier, - >; } #[pallet::pallet] @@ -348,7 +355,7 @@ pub mod pallet { /// /// `issuance - frozen + proportion * issuance` /// - /// where `issuance = total_issuance - IgnoredIssuance` + /// where `issuance = active_issuance - IgnoredIssuance` #[derive( Clone, Eq, PartialEq, Default, Encode, Decode, RuntimeDebug, TypeInfo, MaxEncodedLen, )] @@ -473,6 +480,9 @@ pub mod pallet { AlreadyCommunal, /// The receipt is already private. AlreadyPrivate, + Release1, + Release2, + Tah, } pub(crate) struct WeightCounter { @@ -554,16 +564,17 @@ pub mod pallet { |q| -> Result<(u32, BalanceOf), DispatchError> { let queue_full = q.len() == T::MaxQueueLen::get() as usize; ensure!(!queue_full || q[0].amount < amount, Error::::BidTooLow); - T::Currency::reserve_named(&T::ReserveId::get(), &who, amount)?; + T::Currency::hold(&T::HoldReason::get(), &who, amount)?; // queue is let mut bid = Bid { amount, who: who.clone() }; let net = if queue_full { sp_std::mem::swap(&mut q[0], &mut bid); - let _ = T::Currency::unreserve_named( - &T::ReserveId::get(), + let _ = T::Currency::release( + &T::HoldReason::get(), &bid.who, bid.amount, + BestEffort, ); Self::deposit_event(Event::::BidDropped { who: bid.who, @@ -615,19 +626,21 @@ pub mod pallet { ensure!(queue_index < queue_count, Error::::DurationTooBig); let bid = Bid { amount, who }; - let new_len = Queues::::try_mutate(duration, |q| -> Result { - let pos = q.iter().position(|i| i == &bid).ok_or(Error::::UnknownBid)?; - q.remove(pos); - Ok(q.len() as u32) - })?; + let mut queue = Queues::::get(duration); + let pos = queue.iter().position(|i| i == &bid).ok_or(Error::::UnknownBid)?; + queue.remove(pos); + let new_len = queue.len() as u32; + + T::Currency::release(&T::HoldReason::get(), &bid.who, bid.amount, BestEffort)?; + + Queues::::insert(duration, queue); QueueTotals::::mutate(|qs| { qs.bounded_resize(queue_count, (0, Zero::zero())); qs[queue_index].0 = new_len; qs[queue_index].1.saturating_reduce(bid.amount); }); - T::Currency::unreserve_named(&T::ReserveId::get(), &bid.who, bid.amount); Self::deposit_event(Event::BidRetracted { who: bid.who, amount: bid.amount, duration }); Ok(()) @@ -645,7 +658,7 @@ pub mod pallet { let issuance = Self::issuance_with(&our_account, &summary); let deficit = issuance.required.saturating_sub(issuance.holdings); ensure!(!deficit.is_zero(), Error::::AlreadyFunded); - T::Deficit::on_unbalanced(T::Currency::deposit_creating(&our_account, deficit)); + T::Deficit::on_unbalanced(T::Currency::deposit(&our_account, deficit, Exact)?); Self::deposit_event(Event::::Funded { deficit }); Ok(()) } @@ -702,6 +715,7 @@ pub mod pallet { // Multiply the proportion it is by the total issued. let our_account = Self::account_id(); let effective_issuance = Self::issuance_with(&our_account, &summary).effective; + // let amount = proportion.mul_ceil(effective_issuance); let amount = proportion * effective_issuance; receipt.proportion.saturating_reduce(proportion); @@ -710,46 +724,40 @@ pub mod pallet { let dropped = receipt.proportion.is_zero(); if amount > on_hold { - T::Currency::unreserve_named(&T::ReserveId::get(), &who, on_hold); + T::Currency::release(&T::HoldReason::get(), &who, on_hold, Exact) + .map_err(|_| Error::::Release1)?; let deficit = amount - on_hold; // Try to transfer deficit from pot to receipt owner. summary.receipts_on_hold.saturating_reduce(on_hold); on_hold = Zero::zero(); - T::Currency::transfer(&our_account, &who, deficit, AllowDeath) + T::Currency::transfer(&our_account, &who, deficit, Expendable) .map_err(|_| Error::::Unfunded)?; } else { - T::Currency::unreserve_named(&T::ReserveId::get(), &who, amount); on_hold.saturating_reduce(amount); summary.receipts_on_hold.saturating_reduce(amount); if dropped && !on_hold.is_zero() { // Reclaim any remainder: - // Transfer `excess` to the pot if we have now fully compensated for the - // receipt. - // - // This will legitimately fail if there is no pot account in existance. - // There's nothing we can do about this so we just swallow the error. - // This code is not ideal and could fail in the second phase leaving - // the system in an invalid state. It can be fixed properly with the - // new API in https://github.com/paritytech/substrate/pull/12951 - // - // Below is what it should look like then: - // let _ = T::Currency::repatriate_reserved_named( - // &T::ReserveId::get(), - // &who, - // &our_account, - // excess, - // BalanceStatus::Free, - // ).defensive(); - T::Currency::unreserve_named(&T::ReserveId::get(), &who, on_hold); - // It could theoretically be locked, so really we should be using a more - // forceful variant. But the alternative `repatriate_reserved_named` will - // fail if the destination account doesn't exist. This should be fixed when - // we move to the `fungible::*` traits, which should include a force - // transfer function to transfer the reserved balance into free balance in - // the destination regardless of locks and create it if it doesn't exist. - let _ = T::Currency::transfer(&who, &Self::account_id(), on_hold, AllowDeath); + // Transfer excess of `on_hold` to the pot if we have now fully compensated for + // the receipt. + T::Currency::transfer_on_hold( + &T::HoldReason::get(), + &who, + &our_account, + on_hold, + Exact, + Free, + Polite, + ) + .map(|_| ()) + // We ignore this error as it just means the amount we're trying to deposit is + // dust and the beneficiary account doesn't exist. + .or_else( + |e| if e == TokenError::CannotCreate.into() { Ok(()) } else { Err(e) }, + )?; summary.receipts_on_hold.saturating_reduce(on_hold); } + T::Currency::release(&T::HoldReason::get(), &who, amount, Exact) + .map_err(|_| Error::::Release2)?; } if dropped { @@ -797,7 +805,8 @@ pub mod pallet { summary.thawed.saturating_accrue(receipt.proportion); ensure!(summary.thawed <= throttle, Error::::Throttled); - T::Counterpart::burn_from(&who, T::CounterpartAmount::convert(receipt.proportion))?; + let cp_amount = T::CounterpartAmount::convert(receipt.proportion); + T::Counterpart::burn_from(&who, cp_amount, Exact, Polite)?; // Multiply the proportion it is by the total issued. let our_account = Self::account_id(); @@ -807,7 +816,7 @@ pub mod pallet { summary.proportion_owed.saturating_reduce(receipt.proportion); // Try to transfer amount owed from pot to receipt owner. - T::Currency::transfer(&our_account, &who, amount, AllowDeath) + T::Currency::transfer(&our_account, &who, amount, Expendable) .map_err(|_| Error::::Unfunded)?; Receipts::::remove(index); @@ -840,11 +849,10 @@ pub mod pallet { ensure!(owner == who, Error::::NotOwner); // Unreserve and transfer the funds to the pot. - T::Currency::unreserve_named(&T::ReserveId::get(), &who, on_hold); - // Transfer `excess` to the pot if we have now fully compensated for the receipt. - T::Currency::transfer(&who, &Self::account_id(), on_hold, AllowDeath) + let reason = T::HoldReason::get(); + let us = Self::account_id(); + T::Currency::transfer_on_hold(&reason, &who, &us, on_hold, Exact, Free, Polite) .map_err(|_| Error::::Unfunded)?; - // TODO #12951: ^^^ The above should be done in a single operation `transfer_on_hold`. // Record that we've moved the amount reserved. let mut summary: SummaryRecordOf = Summary::::get(); @@ -881,16 +889,20 @@ pub mod pallet { let effective_issuance = Self::issuance_with(&our_account, &summary).effective; let max_amount = receipt.proportion * effective_issuance; // Avoid trying to place more in the account's reserve than we have available in the pot - let amount = max_amount.min(T::Currency::free_balance(&our_account)); + let amount = max_amount.min(T::Currency::balance(&our_account)); // Burn fungible counterparts. - T::Counterpart::burn_from(&who, T::CounterpartAmount::convert(receipt.proportion))?; + T::Counterpart::burn_from( + &who, + T::CounterpartAmount::convert(receipt.proportion), + Exact, + Polite, + )?; // Transfer the funds from the pot to the owner and reserve - T::Currency::transfer(&Self::account_id(), &who, amount, AllowDeath) - .map_err(|_| Error::::Unfunded)?; - T::Currency::reserve_named(&T::ReserveId::get(), &who, amount)?; - // TODO: ^^^ The above should be done in a single operation `transfer_and_hold`. + let reason = T::HoldReason::get(); + let us = Self::account_id(); + T::Currency::transfer_and_hold(&reason, &us, &who, amount, Exact, Expendable, Polite)?; // Record that we've moved the amount reserved. summary.receipts_on_hold.saturating_accrue(amount); @@ -920,7 +932,7 @@ pub mod pallet { pub required: Balance, } - impl NonfungibleInspect for Pallet { + impl NftInspect for Pallet { type ItemId = ReceiptIndex; fn owner(item: &ReceiptIndex) -> Option { @@ -939,31 +951,19 @@ pub mod pallet { } } - impl NonfungibleTransfer for Pallet { - fn transfer(index: &ReceiptIndex, destination: &T::AccountId) -> DispatchResult { + impl NftTransfer for Pallet { + fn transfer(index: &ReceiptIndex, dest: &T::AccountId) -> DispatchResult { let mut item = Receipts::::get(index).ok_or(TokenError::UnknownAsset)?; let (owner, on_hold) = item.owner.take().ok_or(Error::::AlreadyCommunal)?; - // TODO: This should all be replaced by a single call `transfer_held`. - let shortfall = T::Currency::unreserve_named(&T::ReserveId::get(), &owner, on_hold); - if !shortfall.is_zero() { - let _ = - T::Currency::reserve_named(&T::ReserveId::get(), &owner, on_hold - shortfall); - return Err(TokenError::NoFunds.into()) - } - if let Err(e) = T::Currency::transfer(&owner, destination, on_hold, AllowDeath) { - let _ = T::Currency::reserve_named(&T::ReserveId::get(), &owner, on_hold); - return Err(e) - } - // This can never fail, and if it somehow does, then we can't handle this gracefully. - let _ = - T::Currency::reserve_named(&T::ReserveId::get(), destination, on_hold).defensive(); + let reason = T::HoldReason::get(); + T::Currency::transfer_on_hold(&reason, &owner, dest, on_hold, Exact, OnHold, Polite)?; - item.owner = Some((destination.clone(), on_hold)); + item.owner = Some((dest.clone(), on_hold)); Receipts::::insert(&index, &item); Pallet::::deposit_event(Event::::Transferred { from: owner, - to: destination.clone(), + to: dest.clone(), index: *index, }); Ok(()) @@ -997,9 +997,9 @@ pub mod pallet { summary: &SummaryRecordOf, ) -> IssuanceInfo> { let total_issuance = - T::Currency::total_issuance().saturating_sub(T::IgnoredIssuance::get()); + T::Currency::active_issuance().saturating_sub(T::IgnoredIssuance::get()); let holdings = - T::Currency::free_balance(our_account).saturating_add(summary.receipts_on_hold); + T::Currency::balance(our_account).saturating_add(summary.receipts_on_hold); let other = total_issuance.saturating_sub(holdings); let effective = summary.proportion_owed.left_from_one().saturating_reciprocal_mul(other); @@ -1136,7 +1136,9 @@ pub mod pallet { // Now to activate the bid... let n = amount; let d = issuance.effective; - let proportion = Perquintill::from_rational(n, d); + let proportion = + Perquintill::from_rational_with_rounding(n, d, Rounding::NearestPrefDown) + .defensive_unwrap_or_default(); let who = bid.who; let index = summary.index; summary.proportion_owed.defensive_saturating_accrue(proportion); diff --git a/frame/nis/src/mock.rs b/frame/nis/src/mock.rs index 8594c88a5..0ca669093 100644 --- a/frame/nis/src/mock.rs +++ b/frame/nis/src/mock.rs @@ -19,13 +19,18 @@ use crate::{self as pallet_nis, Perquintill, WithMaximumOf}; +use codec::{Decode, Encode, MaxEncodedLen}; use frame_support::{ ord_parameter_types, parameter_types, - traits::{ConstU16, ConstU32, ConstU64, Currency, OnFinalize, OnInitialize, StorageMapShim}, + traits::{ + fungible::Inspect, ConstU16, ConstU32, ConstU64, Everything, OnFinalize, OnInitialize, + StorageMapShim, + }, weights::Weight, PalletId, }; use pallet_balances::{Instance1, Instance2}; +use scale_info::TypeInfo; use sp_core::{ConstU128, H256}; use sp_runtime::{ testing::Header, @@ -35,6 +40,8 @@ use sp_runtime::{ type UncheckedExtrinsic = frame_system::mocking::MockUncheckedExtrinsic; type Block = frame_system::mocking::MockBlock; +pub type Balance = u64; + // Configure a mock runtime to test the pallet. frame_support::construct_runtime!( pub enum Test where @@ -50,7 +57,7 @@ frame_support::construct_runtime!( ); impl frame_system::Config for Test { - type BaseCallFilter = frame_support::traits::Everything; + type BaseCallFilter = Everything; type BlockWeights = (); type BlockLength = (); type RuntimeOrigin = RuntimeOrigin; @@ -67,35 +74,45 @@ impl frame_system::Config for Test { type DbWeight = (); type Version = (); type PalletInfo = PalletInfo; - type AccountData = pallet_balances::AccountData; + type AccountData = pallet_balances::AccountData; type OnNewAccount = (); type OnKilledAccount = (); type SystemWeightInfo = (); type SS58Prefix = ConstU16<42>; type OnSetCode = (); - type MaxConsumers = frame_support::traits::ConstU32<16>; + type MaxConsumers = ConstU32<16>; } impl pallet_balances::Config for Test { - type Balance = u64; + type Balance = Balance; type DustRemoval = (); type RuntimeEvent = RuntimeEvent; - type ExistentialDeposit = frame_support::traits::ConstU64<1>; + type ExistentialDeposit = ConstU64<1>; type AccountStore = System; type WeightInfo = (); type MaxLocks = (); type MaxReserves = ConstU32<1>; type ReserveIdentifier = [u8; 8]; + type FreezeIdentifier = (); + type MaxFreezes = (); + type HoldIdentifier = HoldIdentifier; + type MaxHolds = ConstU32<1>; +} + +#[derive( + Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Encode, Decode, MaxEncodedLen, Debug, TypeInfo, +)] +pub enum HoldIdentifier { + Nis, } impl pallet_balances::Config for Test { type Balance = u128; type DustRemoval = (); type RuntimeEvent = RuntimeEvent; - type ExistentialDeposit = frame_support::traits::ConstU128<1>; + type ExistentialDeposit = ConstU128<1>; type AccountStore = StorageMapShim< pallet_balances::Account, - frame_system::Provider, u64, pallet_balances::AccountData, >; @@ -103,16 +120,20 @@ impl pallet_balances::Config for Test { type MaxLocks = (); type MaxReserves = (); type ReserveIdentifier = [u8; 8]; + type FreezeIdentifier = (); + type MaxFreezes = (); + type HoldIdentifier = (); + type MaxHolds = (); } parameter_types! { - pub IgnoredIssuance: u64 = Balances::total_balance(&0); // Account zero is ignored. + pub IgnoredIssuance: Balance = Balances::total_balance(&0); // Account zero is ignored. pub const NisPalletId: PalletId = PalletId(*b"py/nis "); pub static Target: Perquintill = Perquintill::zero(); pub const MinReceipt: Perquintill = Perquintill::from_percent(1); pub const ThawThrottle: (Perquintill, u64) = (Perquintill::from_percent(25), 5); pub static MaxIntakeWeight: Weight = Weight::from_parts(2_000_000_000_000, 0); - pub const ReserveId: [u8; 8] = *b"py/nis "; + pub const HoldReason: HoldIdentifier = HoldIdentifier::Nis; } ord_parameter_types! { @@ -140,7 +161,7 @@ impl pallet_nis::Config for Test { type MaxIntakeWeight = MaxIntakeWeight; type MinReceipt = MinReceipt; type ThawThrottle = ThawThrottle; - type ReserveId = ReserveId; + type HoldReason = HoldReason; } // This function basically just builds a genesis storage key/value store according to @@ -155,6 +176,13 @@ pub fn new_test_ext() -> sp_io::TestExternalities { t.into() } +// This function basically just builds a genesis storage key/value store according to +// our desired mockup, but without any balances. +#[cfg(feature = "runtime-benchmarks")] +pub fn new_test_ext_empty() -> sp_io::TestExternalities { + frame_system::GenesisConfig::default().build_storage::().unwrap().into() +} + pub fn run_to_block(n: u64) { while System::block_number() < n { Nis::on_finalize(System::block_number()); diff --git a/frame/nis/src/tests.rs b/frame/nis/src/tests.rs index d0808367e..7350da97d 100644 --- a/frame/nis/src/tests.rs +++ b/frame/nis/src/tests.rs @@ -22,19 +22,22 @@ use crate::{mock::*, Error}; use frame_support::{ assert_noop, assert_ok, traits::{ + fungible::{hold::Inspect as InspectHold, Inspect as FunInspect, Mutate as FunMutate}, nonfungible::{Inspect, Transfer}, - Currency, + tokens::{Fortitude::Force, Precision::Exact}, }, }; -use pallet_balances::{Error as BalancesError, Instance1}; use sp_arithmetic::Perquintill; -use sp_runtime::{Saturating, TokenError}; +use sp_runtime::{ + Saturating, + TokenError::{self, FundsUnavailable}, +}; -fn pot() -> u64 { +fn pot() -> Balance { Balances::free_balance(&Nis::account_id()) } -fn holdings() -> u64 { +fn holdings() -> Balance { Nis::issuance().holdings } @@ -42,8 +45,8 @@ fn signed(who: u64) -> RuntimeOrigin { RuntimeOrigin::signed(who) } -fn enlarge(amount: u64, max_bids: u32) { - let summary: SummaryRecord = Summary::::get(); +fn enlarge(amount: Balance, max_bids: u32) { + let summary: SummaryRecord = Summary::::get(); let increase_in_proportion_owed = Perquintill::from_rational(amount, Nis::issuance().effective); let target = summary.proportion_owed.saturating_add(increase_in_proportion_owed); Nis::process_queues(target, u32::max_value(), max_bids, &mut WeightCounter::unlimited()); @@ -75,10 +78,7 @@ fn place_bid_works() { new_test_ext().execute_with(|| { run_to_block(1); assert_noop!(Nis::place_bid(signed(1), 1, 2), Error::::AmountTooSmall); - assert_noop!( - Nis::place_bid(signed(1), 101, 2), - BalancesError::::InsufficientBalance - ); + assert_noop!(Nis::place_bid(signed(1), 101, 2), FundsUnavailable); assert_noop!(Nis::place_bid(signed(1), 10, 4), Error::::DurationTooBig); assert_ok!(Nis::place_bid(signed(1), 10, 2)); assert_eq!(Balances::reserved_balance(1), 10); @@ -451,16 +451,16 @@ fn communify_works() { assert_noop!(Nis::thaw_communal(signed(1), 1), Error::::UnknownReceipt); // Transfer some of the fungibles away. - assert_ok!(NisBalances::transfer(signed(1), 2, 100_000)); + assert_ok!(NisBalances::transfer_allow_death(signed(1), 2, 100_000)); assert_eq!(NisBalances::free_balance(&1), 2_000_000); assert_eq!(NisBalances::free_balance(&2), 100_000); // Communal thawing with the correct index is not possible now. - assert_noop!(Nis::thaw_communal(signed(1), 0), TokenError::NoFunds); - assert_noop!(Nis::thaw_communal(signed(2), 0), TokenError::NoFunds); + assert_noop!(Nis::thaw_communal(signed(1), 0), TokenError::FundsUnavailable); + assert_noop!(Nis::thaw_communal(signed(2), 0), TokenError::FundsUnavailable); // Transfer the rest to 2... - assert_ok!(NisBalances::transfer(signed(1), 2, 2_000_000)); + assert_ok!(NisBalances::transfer_allow_death(signed(1), 2, 2_000_000)); assert_eq!(NisBalances::free_balance(&1), 0); assert_eq!(NisBalances::free_balance(&2), 2_100_000); @@ -487,8 +487,8 @@ fn privatize_works() { assert_ok!(Nis::communify(signed(1), 0)); // Transfer the fungibles to #2 - assert_ok!(NisBalances::transfer(signed(1), 2, 2_100_000)); - assert_noop!(Nis::privatize(signed(1), 0), TokenError::NoFunds); + assert_ok!(NisBalances::transfer_allow_death(signed(1), 2, 2_100_000)); + assert_noop!(Nis::privatize(signed(1), 0), TokenError::FundsUnavailable); // Privatize assert_ok!(Nis::privatize(signed(2), 0)); @@ -513,16 +513,16 @@ fn privatize_and_thaw_with_another_receipt_works() { assert_ok!(Nis::communify(signed(2), 1)); // Transfer half of fungibles to #3 from each of #1 and #2, and the other half from #2 to #4 - assert_ok!(NisBalances::transfer(signed(1), 3, 1_050_000)); - assert_ok!(NisBalances::transfer(signed(2), 3, 1_050_000)); - assert_ok!(NisBalances::transfer(signed(2), 4, 1_050_000)); + assert_ok!(NisBalances::transfer_allow_death(signed(1), 3, 1_050_000)); + assert_ok!(NisBalances::transfer_allow_death(signed(2), 3, 1_050_000)); + assert_ok!(NisBalances::transfer_allow_death(signed(2), 4, 1_050_000)); // #3 privatizes, partially thaws, then re-communifies with #0, then transfers the fungibles // to #2 assert_ok!(Nis::privatize(signed(3), 0)); assert_ok!(Nis::thaw_private(signed(3), 0, Some(Perquintill::from_percent(5)))); assert_ok!(Nis::communify(signed(3), 0)); - assert_ok!(NisBalances::transfer(signed(3), 1, 1_050_000)); + assert_ok!(NisBalances::transfer_allow_death(signed(3), 1, 1_050_000)); // #1 now has enough to thaw using receipt 1 assert_ok!(Nis::thaw_communal(signed(1), 1)); @@ -536,17 +536,21 @@ fn privatize_and_thaw_with_another_receipt_works() { fn communal_thaw_when_issuance_higher_works() { new_test_ext().execute_with(|| { run_to_block(1); + assert_ok!(Balances::transfer_allow_death(signed(2), 1, 1)); assert_ok!(Nis::place_bid(signed(1), 100, 1)); enlarge(100, 1); + assert_eq!(Balances::total_balance(&1), 101); assert_ok!(Nis::communify(signed(1), 0)); + assert_eq!(Balances::total_balance_on_hold(&1), 0); + assert_eq!(Balances::total_balance(&1), 1); - assert_eq!(NisBalances::free_balance(1), 5_250_000); // (25% of 21m) + assert_eq!(NisBalances::free_balance(1), 5_250_000); // (12.5% of 21m) // Everybody else's balances goes up by 50% - Balances::make_free_balance_be(&2, 150); - Balances::make_free_balance_be(&3, 150); - Balances::make_free_balance_be(&4, 150); + assert_ok!(Balances::mint_into(&2, 50)); + assert_ok!(Balances::mint_into(&3, 50)); + assert_ok!(Balances::mint_into(&4, 50)); run_to_block(4); @@ -556,16 +560,20 @@ fn communal_thaw_when_issuance_higher_works() { assert_ok!(Nis::fund_deficit(signed(1))); // Transfer counterparts away... - assert_ok!(NisBalances::transfer(signed(1), 2, 250_000)); + assert_ok!(NisBalances::transfer_allow_death(signed(1), 2, 125_000)); // ...and it's not thawable. - assert_noop!(Nis::thaw_communal(signed(1), 0), TokenError::NoFunds); + assert_noop!(Nis::thaw_communal(signed(1), 0), TokenError::FundsUnavailable); // Transfer counterparts back... - assert_ok!(NisBalances::transfer(signed(2), 1, 250_000)); + assert_ok!(NisBalances::transfer_allow_death(signed(2), 1, 125_000)); // ...and it is. assert_ok!(Nis::thaw_communal(signed(1), 0)); + assert_eq!(Balances::total_balance(&1), 151); + assert_ok!(Balances::transfer_allow_death(signed(1), 2, 1)); + assert_eq!(Balances::total_balance(&1), 150); assert_eq!(Balances::free_balance(1), 150); + assert_eq!(Balances::total_balance_on_hold(&1), 0); assert_eq!(Balances::reserved_balance(1), 0); }); } @@ -574,13 +582,14 @@ fn communal_thaw_when_issuance_higher_works() { fn private_thaw_when_issuance_higher_works() { new_test_ext().execute_with(|| { run_to_block(1); + assert_ok!(Balances::transfer_allow_death(signed(2), 1, 1)); assert_ok!(Nis::place_bid(signed(1), 100, 1)); enlarge(100, 1); // Everybody else's balances goes up by 50% - Balances::make_free_balance_be(&2, 150); - Balances::make_free_balance_be(&3, 150); - Balances::make_free_balance_be(&4, 150); + assert_ok!(Balances::mint_into(&2, 50)); + assert_ok!(Balances::mint_into(&3, 50)); + assert_ok!(Balances::mint_into(&4, 50)); run_to_block(4); @@ -591,6 +600,7 @@ fn private_thaw_when_issuance_higher_works() { assert_ok!(Nis::thaw_private(signed(1), 0, None)); + assert_ok!(Balances::transfer_allow_death(signed(1), 2, 1)); assert_eq!(Balances::free_balance(1), 150); assert_eq!(Balances::reserved_balance(1), 0); }); @@ -601,15 +611,16 @@ fn thaw_with_ignored_issuance_works() { new_test_ext().execute_with(|| { run_to_block(1); // Give account zero some balance. - Balances::make_free_balance_be(&0, 200); + assert_ok!(Balances::mint_into(&0, 200)); + assert_ok!(Balances::transfer_allow_death(signed(2), 1, 1)); assert_ok!(Nis::place_bid(signed(1), 100, 1)); enlarge(100, 1); // Account zero transfers 50 into everyone else's accounts. - assert_ok!(Balances::transfer(signed(0), 2, 50)); - assert_ok!(Balances::transfer(signed(0), 3, 50)); - assert_ok!(Balances::transfer(signed(0), 4, 50)); + assert_ok!(Balances::transfer_allow_death(signed(0), 2, 50)); + assert_ok!(Balances::transfer_allow_death(signed(0), 3, 50)); + assert_ok!(Balances::transfer_allow_death(signed(0), 4, 50)); run_to_block(4); // Unfunded initially... @@ -620,6 +631,7 @@ fn thaw_with_ignored_issuance_works() { assert_ok!(Nis::thaw_private(signed(1), 0, None)); // Account zero changes have been ignored. + assert_ok!(Balances::transfer_allow_death(signed(1), 2, 1)); assert_eq!(Balances::free_balance(1), 150); assert_eq!(Balances::reserved_balance(1), 0); }); @@ -629,17 +641,19 @@ fn thaw_with_ignored_issuance_works() { fn thaw_when_issuance_lower_works() { new_test_ext().execute_with(|| { run_to_block(1); + assert_ok!(Balances::transfer_allow_death(signed(2), 1, 1)); assert_ok!(Nis::place_bid(signed(1), 100, 1)); enlarge(100, 1); // Everybody else's balances goes down by 25% - Balances::make_free_balance_be(&2, 75); - Balances::make_free_balance_be(&3, 75); - Balances::make_free_balance_be(&4, 75); + assert_ok!(Balances::burn_from(&2, 25, Exact, Force)); + assert_ok!(Balances::burn_from(&3, 25, Exact, Force)); + assert_ok!(Balances::burn_from(&4, 25, Exact, Force)); run_to_block(4); assert_ok!(Nis::thaw_private(signed(1), 0, None)); + assert_ok!(Balances::transfer_allow_death(signed(1), 2, 1)); assert_eq!(Balances::free_balance(1), 75); assert_eq!(Balances::reserved_balance(1), 0); }); @@ -649,15 +663,16 @@ fn thaw_when_issuance_lower_works() { fn multiple_thaws_works() { new_test_ext().execute_with(|| { run_to_block(1); + assert_ok!(Balances::transfer_allow_death(signed(3), 1, 1)); assert_ok!(Nis::place_bid(signed(1), 40, 1)); assert_ok!(Nis::place_bid(signed(1), 60, 1)); assert_ok!(Nis::place_bid(signed(2), 50, 1)); enlarge(200, 3); // Double everyone's free balances. - Balances::make_free_balance_be(&2, 100); - Balances::make_free_balance_be(&3, 200); - Balances::make_free_balance_be(&4, 200); + assert_ok!(Balances::mint_into(&2, 50)); + assert_ok!(Balances::mint_into(&3, 100)); + assert_ok!(Balances::mint_into(&4, 100)); assert_ok!(Nis::fund_deficit(signed(1))); run_to_block(4); @@ -667,8 +682,11 @@ fn multiple_thaws_works() { run_to_block(5); assert_ok!(Nis::thaw_private(signed(2), 2, None)); + assert_ok!(Balances::transfer_allow_death(signed(1), 3, 1)); assert_eq!(Balances::free_balance(1), 200); assert_eq!(Balances::free_balance(2), 200); + assert_eq!(Balances::total_balance(&1), 200); + assert_eq!(Balances::total_balance(&2), 200); }); } @@ -676,15 +694,16 @@ fn multiple_thaws_works() { fn multiple_thaws_works_in_alternative_thaw_order() { new_test_ext().execute_with(|| { run_to_block(1); + assert_ok!(Balances::transfer_allow_death(signed(3), 1, 1)); assert_ok!(Nis::place_bid(signed(1), 40, 1)); assert_ok!(Nis::place_bid(signed(1), 60, 1)); assert_ok!(Nis::place_bid(signed(2), 50, 1)); enlarge(200, 3); // Double everyone's free balances. - Balances::make_free_balance_be(&2, 100); - Balances::make_free_balance_be(&3, 200); - Balances::make_free_balance_be(&4, 200); + assert_ok!(Balances::mint_into(&2, 50)); + assert_ok!(Balances::mint_into(&3, 100)); + assert_ok!(Balances::mint_into(&4, 100)); assert_ok!(Nis::fund_deficit(signed(1))); run_to_block(4); @@ -695,8 +714,11 @@ fn multiple_thaws_works_in_alternative_thaw_order() { run_to_block(5); assert_ok!(Nis::thaw_private(signed(1), 1, None)); + assert_ok!(Balances::transfer_allow_death(signed(1), 3, 1)); assert_eq!(Balances::free_balance(1), 200); assert_eq!(Balances::free_balance(2), 200); + assert_eq!(Balances::total_balance(&1), 200); + assert_eq!(Balances::total_balance(&2), 200); }); } diff --git a/frame/nomination-pools/benchmarking/src/mock.rs b/frame/nomination-pools/benchmarking/src/mock.rs index 4a1a52868..cffb712ea 100644 --- a/frame/nomination-pools/benchmarking/src/mock.rs +++ b/frame/nomination-pools/benchmarking/src/mock.rs @@ -75,6 +75,10 @@ impl pallet_balances::Config for Runtime { type ExistentialDeposit = ExistentialDeposit; type AccountStore = System; type WeightInfo = (); + type FreezeIdentifier = (); + type MaxFreezes = (); + type HoldIdentifier = (); + type MaxHolds = (); } pallet_staking_reward_curve::build! { diff --git a/frame/nomination-pools/src/mock.rs b/frame/nomination-pools/src/mock.rs index c6b094f0a..6d83ef61d 100644 --- a/frame/nomination-pools/src/mock.rs +++ b/frame/nomination-pools/src/mock.rs @@ -196,6 +196,10 @@ impl pallet_balances::Config for Runtime { type ExistentialDeposit = ExistentialDeposit; type AccountStore = System; type WeightInfo = (); + type FreezeIdentifier = (); + type MaxFreezes = (); + type HoldIdentifier = (); + type MaxHolds = (); } pub struct BalanceToU256; diff --git a/frame/nomination-pools/src/tests.rs b/frame/nomination-pools/src/tests.rs index a4fda2e5d..4cb255e23 100644 --- a/frame/nomination-pools/src/tests.rs +++ b/frame/nomination-pools/src/tests.rs @@ -39,6 +39,16 @@ macro_rules! member_unbonding_eras { pub const DEFAULT_ROLES: PoolRoles = PoolRoles { depositor: 10, root: Some(900), nominator: Some(901), bouncer: Some(902) }; +fn deposit_rewards(r: u128) { + let b = Balances::free_balance(&default_reward_account()).checked_add(r).unwrap(); + Balances::make_free_balance_be(&default_reward_account(), b); +} + +fn remove_rewards(r: u128) { + let b = Balances::free_balance(&default_reward_account()).checked_sub(r).unwrap(); + Balances::make_free_balance_be(&default_reward_account(), b); +} + #[test] fn test_setup_works() { ExtBuilder::default().build_and_execute(|| { @@ -469,6 +479,8 @@ mod sub_pools { } mod join { + use sp_runtime::TokenError; + use super::*; #[test] @@ -592,7 +604,7 @@ mod join { // Balance needs to be gt Balance::MAX / `MaxPointsToBalance` assert_noop!( Pools::join(RuntimeOrigin::signed(11), 5, 123), - pallet_balances::Error::::InsufficientBalance, + TokenError::FundsUnavailable, ); StakingMock::set_bonded_balance(Pools::create_bonded_account(1), max_points_to_balance); @@ -749,7 +761,7 @@ mod claim_payout { // and the reward pool has earned 100 in rewards assert_eq!(Balances::free_balance(default_reward_account()), ed); - assert_ok!(Balances::mutate_account(&default_reward_account(), |a| a.free += 100)); + deposit_rewards(100); let _ = pool_events_since_last_call(); @@ -796,7 +808,7 @@ mod claim_payout { assert_eq!(Balances::free_balance(&default_reward_account()), ed); // Given the reward pool has some new rewards - assert_ok!(Balances::mutate_account(&default_reward_account(), |a| a.free += 50)); + deposit_rewards(50); // When assert_ok!(Pools::claim_payout(RuntimeOrigin::signed(10))); @@ -825,7 +837,7 @@ mod claim_payout { assert_eq!(Balances::free_balance(&default_reward_account()), ed + 25); // Given del 50 hasn't claimed and the reward pools has just earned 50 - assert_ok!(Balances::mutate_account(&default_reward_account(), |a| a.free += 50)); + deposit_rewards(50); assert_eq!(Balances::free_balance(&default_reward_account()), ed + 75); // When @@ -855,7 +867,7 @@ mod claim_payout { assert_eq!(Balances::free_balance(&default_reward_account()), ed + 20); // Given del 40 hasn't claimed and the reward pool has just earned 400 - assert_ok!(Balances::mutate_account(&default_reward_account(), |a| a.free += 400)); + deposit_rewards(400); assert_eq!(Balances::free_balance(&default_reward_account()), ed + 420); // When @@ -874,7 +886,7 @@ mod claim_payout { assert_eq!(Balances::free_balance(&default_reward_account()), ed + 380); // Given del 40 + del 50 haven't claimed and the reward pool has earned 20 - assert_ok!(Balances::mutate_account(&default_reward_account(), |a| a.free += 20)); + deposit_rewards(20); assert_eq!(Balances::free_balance(&default_reward_account()), ed + 400); // When @@ -974,7 +986,7 @@ mod claim_payout { ); // The pool earns 10 points - assert_ok!(Balances::mutate_account(&default_reward_account(), |a| a.free += 10)); + deposit_rewards(10); assert_ok!(Pools::do_reward_payout( &10, @@ -1010,7 +1022,7 @@ mod claim_payout { assert_eq!(reward_pool, rew(0, 0, 0)); // Given the pool has earned some rewards for the first time - assert_ok!(Balances::mutate_account(&default_reward_account(), |a| a.free += 5)); + deposit_rewards(5); // When let payout = @@ -1031,7 +1043,7 @@ mod claim_payout { assert_eq!(member, del(0.5)); // Given the pool has earned rewards again - assert_ok!(Balances::mutate_account(&default_reward_account(), |a| a.free += 10)); + deposit_rewards(10); // When let payout = @@ -1090,7 +1102,7 @@ mod claim_payout { assert_eq!(bonded_pool.points, 100); // and the reward pool has earned 100 in rewards - assert_ok!(Balances::mutate_account(&default_reward_account(), |a| a.free += 100)); + deposit_rewards(100); // When let payout = @@ -1135,7 +1147,7 @@ mod claim_payout { assert_eq!(reward_pool, rew(0, 0, 100)); // Given the reward pool has some new rewards - assert_ok!(Balances::mutate_account(&default_reward_account(), |a| a.free += 50)); + deposit_rewards(50); // When let payout = @@ -1166,7 +1178,7 @@ mod claim_payout { assert_eq!(reward_pool, rew(0, 0, 125)); // Given del_50 hasn't claimed and the reward pools has just earned 50 - assert_ok!(Balances::mutate_account(&default_reward_account(), |a| a.free += 50)); + deposit_rewards(50); // When let payout = @@ -1197,7 +1209,7 @@ mod claim_payout { assert_eq!(reward_pool, rew(0, 0, 180)); // Given del_40 hasn't claimed and the reward pool has just earned 400 - assert_ok!(Balances::mutate_account(&default_reward_account(), |a| a.free += 400)); + deposit_rewards(400); // When let payout = @@ -1214,7 +1226,7 @@ mod claim_payout { assert_eq!(reward_pool, rew(0, 0, 220)); // Given del_40 + del_50 haven't claimed and the reward pool has earned 20 - assert_ok!(Balances::mutate_account(&default_reward_account(), |a| a.free += 20)); + deposit_rewards(20); // When let payout = @@ -1252,14 +1264,14 @@ mod claim_payout { fn rewards_distribution_is_fair_basic() { ExtBuilder::default().build_and_execute(|| { // reward pool by 10. - Balances::mutate_account(&default_reward_account(), |f| f.free += 10).unwrap(); + deposit_rewards(10); // 20 joins afterwards. Balances::make_free_balance_be(&20, Balances::minimum_balance() + 10); assert_ok!(Pools::join(RuntimeOrigin::signed(20), 10, 1)); // reward by another 20 - Balances::mutate_account(&default_reward_account(), |f| f.free += 20).unwrap(); + deposit_rewards(20); // 10 should claim 10 + 10, 20 should claim 20 / 2. assert_ok!(Pools::claim_payout(RuntimeOrigin::signed(10))); @@ -1276,7 +1288,7 @@ mod claim_payout { ); // any upcoming rewards are shared equally. - Balances::mutate_account(&default_reward_account(), |f| f.free += 20).unwrap(); + deposit_rewards(20); assert_ok!(Pools::claim_payout(RuntimeOrigin::signed(10))); assert_ok!(Pools::claim_payout(RuntimeOrigin::signed(20))); @@ -1296,12 +1308,12 @@ mod claim_payout { // basically checks the case where the amount of rewards is less than the pool shares. for // this, we have to rely on fixed point arithmetic. ExtBuilder::default().build_and_execute(|| { - Balances::mutate_account(&default_reward_account(), |f| f.free += 3).unwrap(); + deposit_rewards(3); Balances::make_free_balance_be(&20, Balances::minimum_balance() + 10); assert_ok!(Pools::join(RuntimeOrigin::signed(20), 10, 1)); - Balances::mutate_account(&default_reward_account(), |f| f.free += 6).unwrap(); + deposit_rewards(6); // 10 should claim 3, 20 should claim 3 + 3. assert_ok!(Pools::claim_payout(RuntimeOrigin::signed(10))); @@ -1319,7 +1331,7 @@ mod claim_payout { ); // any upcoming rewards are shared equally. - Balances::mutate_account(&default_reward_account(), |f| f.free += 8).unwrap(); + deposit_rewards(8); assert_ok!(Pools::claim_payout(RuntimeOrigin::signed(10))); assert_ok!(Pools::claim_payout(RuntimeOrigin::signed(20))); @@ -1333,7 +1345,7 @@ mod claim_payout { ); // uneven upcoming rewards are shared equally, rounded down. - Balances::mutate_account(&default_reward_account(), |f| f.free += 7).unwrap(); + deposit_rewards(7); assert_ok!(Pools::claim_payout(RuntimeOrigin::signed(10))); assert_ok!(Pools::claim_payout(RuntimeOrigin::signed(20))); @@ -1353,17 +1365,17 @@ mod claim_payout { ExtBuilder::default().build_and_execute(|| { let ed = Balances::minimum_balance(); - Balances::mutate_account(&default_reward_account(), |f| f.free += 30).unwrap(); + deposit_rewards(30); Balances::make_free_balance_be(&20, ed + 10); assert_ok!(Pools::join(RuntimeOrigin::signed(20), 10, 1)); - Balances::mutate_account(&default_reward_account(), |f| f.free += 100).unwrap(); + deposit_rewards(100); Balances::make_free_balance_be(&30, ed + 10); assert_ok!(Pools::join(RuntimeOrigin::signed(30), 10, 1)); - Balances::mutate_account(&default_reward_account(), |f| f.free += 60).unwrap(); + deposit_rewards(60); // 10 should claim 10, 20 should claim nothing. assert_ok!(Pools::claim_payout(RuntimeOrigin::signed(10))); @@ -1384,7 +1396,7 @@ mod claim_payout { ); // any upcoming rewards are shared equally. - Balances::mutate_account(&default_reward_account(), |f| f.free += 30).unwrap(); + deposit_rewards(30); assert_ok!(Pools::claim_payout(RuntimeOrigin::signed(10))); assert_ok!(Pools::claim_payout(RuntimeOrigin::signed(20))); @@ -1407,7 +1419,7 @@ mod claim_payout { let ed = Balances::minimum_balance(); assert_eq!(Pools::api_pending_rewards(10), Some(0)); - Balances::mutate_account(&default_reward_account(), |f| f.free += 30).unwrap(); + deposit_rewards(30); assert_eq!(Pools::api_pending_rewards(10), Some(30)); assert_eq!(Pools::api_pending_rewards(20), None); @@ -1417,7 +1429,7 @@ mod claim_payout { assert_eq!(Pools::api_pending_rewards(10), Some(30)); assert_eq!(Pools::api_pending_rewards(20), Some(0)); - Balances::mutate_account(&default_reward_account(), |f| f.free += 100).unwrap(); + deposit_rewards(100); assert_eq!(Pools::api_pending_rewards(10), Some(30 + 50)); assert_eq!(Pools::api_pending_rewards(20), Some(50)); @@ -1430,7 +1442,7 @@ mod claim_payout { assert_eq!(Pools::api_pending_rewards(20), Some(50)); assert_eq!(Pools::api_pending_rewards(30), Some(0)); - Balances::mutate_account(&default_reward_account(), |f| f.free += 60).unwrap(); + deposit_rewards(60); assert_eq!(Pools::api_pending_rewards(10), Some(30 + 50 + 20)); assert_eq!(Pools::api_pending_rewards(20), Some(50 + 20)); @@ -1464,7 +1476,7 @@ mod claim_payout { Balances::make_free_balance_be(&30, ed + 20); assert_ok!(Pools::join(RuntimeOrigin::signed(30), 10, 1)); - Balances::mutate_account(&default_reward_account(), |f| f.free += 40).unwrap(); + deposit_rewards(40); // everyone claims. assert_ok!(Pools::claim_payout(RuntimeOrigin::signed(10))); @@ -1488,7 +1500,7 @@ mod claim_payout { assert_ok!(Pools::bond_extra(RuntimeOrigin::signed(30), BondExtra::FreeBalance(10))); // more rewards come in. - Balances::mutate_account(&default_reward_account(), |f| f.free += 100).unwrap(); + deposit_rewards(100); assert_ok!(Pools::claim_payout(RuntimeOrigin::signed(10))); assert_ok!(Pools::claim_payout(RuntimeOrigin::signed(20))); @@ -1514,7 +1526,7 @@ mod claim_payout { Balances::make_free_balance_be(&20, ed + 20); assert_ok!(Pools::join(RuntimeOrigin::signed(20), 20, 1)); - Balances::mutate_account(&default_reward_account(), |f| f.free += 30).unwrap(); + deposit_rewards(30); // everyone claims. assert_ok!(Pools::claim_payout(RuntimeOrigin::signed(10))); @@ -1535,7 +1547,7 @@ mod claim_payout { assert_ok!(Pools::unbond(RuntimeOrigin::signed(20), 20, 10)); // more rewards come in. - Balances::mutate_account(&default_reward_account(), |f| f.free += 100).unwrap(); + deposit_rewards(100); assert_ok!(Pools::claim_payout(RuntimeOrigin::signed(10))); assert_ok!(Pools::claim_payout(RuntimeOrigin::signed(20))); @@ -1562,7 +1574,7 @@ mod claim_payout { assert_ok!(Pools::join(RuntimeOrigin::signed(30), 10, 1)); // 10 gets 10, 20 gets 20, 30 gets 10 - Balances::mutate_account(&default_reward_account(), |f| f.free += 40).unwrap(); + deposit_rewards(40); // some claim. assert_ok!(Pools::claim_payout(RuntimeOrigin::signed(10))); @@ -1581,7 +1593,7 @@ mod claim_payout { ); // 10 gets 20, 20 gets 40, 30 gets 20 - Balances::mutate_account(&default_reward_account(), |f| f.free += 80).unwrap(); + deposit_rewards(80); // some claim. assert_ok!(Pools::claim_payout(RuntimeOrigin::signed(10))); @@ -1596,7 +1608,7 @@ mod claim_payout { ); // 10 gets 20, 20 gets 40, 30 gets 20 - Balances::mutate_account(&default_reward_account(), |f| f.free += 80).unwrap(); + deposit_rewards(80); // some claim. assert_ok!(Pools::claim_payout(RuntimeOrigin::signed(10))); @@ -1629,7 +1641,7 @@ mod claim_payout { assert_ok!(Pools::join(RuntimeOrigin::signed(20), 20, 1)); // 10 gets 10, 20 gets 20, 30 gets 10 - Balances::mutate_account(&default_reward_account(), |f| f.free += 30).unwrap(); + deposit_rewards(30); // some claim. assert_ok!(Pools::claim_payout(RuntimeOrigin::signed(10))); @@ -1645,7 +1657,7 @@ mod claim_payout { ); // 20 has not claimed yet, more reward comes - Balances::mutate_account(&default_reward_account(), |f| f.free += 60).unwrap(); + deposit_rewards(60); // and 20 bonds more -- they should not have more share of this reward. assert_ok!(Pools::bond_extra(RuntimeOrigin::signed(20), BondExtra::FreeBalance(10))); @@ -1665,7 +1677,7 @@ mod claim_payout { ); // but in the next round of rewards, the extra10 they bonded has an impact. - Balances::mutate_account(&default_reward_account(), |f| f.free += 60).unwrap(); + deposit_rewards(60); // everyone claim. assert_ok!(Pools::claim_payout(RuntimeOrigin::signed(10))); @@ -1695,7 +1707,7 @@ mod claim_payout { assert_eq!(member_10.last_recorded_reward_counter, 0.into()); // transfer some reward to pool 1. - Balances::mutate_account(&default_reward_account(), |f| f.free += 60).unwrap(); + deposit_rewards(60); // create pool 2 Balances::make_free_balance_be(&20, 100); @@ -1779,7 +1791,7 @@ mod claim_payout { } // transfer some reward to pool 1. - Balances::mutate_account(&default_reward_account(), |f| f.free += 60).unwrap(); + deposit_rewards(60); { join(30, 10); @@ -1849,7 +1861,7 @@ mod claim_payout { // 10 bonds extra again with some rewards. This reward should be split equally between // 10 and 20, as they both have equal points now. - Balances::mutate_account(&default_reward_account(), |f| f.free += 30).unwrap(); + deposit_rewards(30); { assert_ok!(Pools::bond_extra( @@ -1905,7 +1917,7 @@ mod claim_payout { MaxPoolMembersPerPool::::set(None); // pool receives some rewards. - Balances::mutate_account(&default_reward_account(), |f| f.free += 30).unwrap(); + deposit_rewards(30); System::reset_events(); // 10 cashes it out, and bonds it. @@ -1975,7 +1987,7 @@ mod claim_payout { } // some rewards come in. - Balances::mutate_account(&default_reward_account(), |f| f.free += 30).unwrap(); + deposit_rewards(30); // and 30 also unbonds half. { @@ -2058,7 +2070,7 @@ mod claim_payout { ); // some rewards come in. - Balances::mutate_account(&default_reward_account(), |f| f.free += 40).unwrap(); + deposit_rewards(40); // everyone claims assert_ok!(Pools::claim_payout(RuntimeOrigin::signed(10))); @@ -2120,8 +2132,7 @@ mod claim_payout { .build_and_execute(|| { // some rewards come in. assert_eq!(Balances::free_balance(&default_reward_account()), unit); - Balances::mutate_account(&default_reward_account(), |f| f.free += unit / 1000) - .unwrap(); + deposit_rewards(unit / 1000); // everyone claims assert_ok!(Pools::claim_payout(RuntimeOrigin::signed(10))); @@ -3329,11 +3340,7 @@ mod withdraw_unbonded { ); assert_eq!( balances_events_since_last_call(), - vec![BEvent::BalanceSet { - who: default_bonded_account(), - free: 300, - reserved: 0 - }] + vec![BEvent::BalanceSet { who: default_bonded_account(), free: 300 }] ); // When @@ -3447,11 +3454,7 @@ mod withdraw_unbonded { ); assert_eq!( balances_events_since_last_call(), - vec![BEvent::BalanceSet { - who: default_bonded_account(), - free: 300, - reserved: 0 - },] + vec![BEvent::BalanceSet { who: default_bonded_account(), free: 300 },] ); CurrentEra::set(StakingMock::bonding_duration()); @@ -5078,18 +5081,15 @@ mod reward_counter_precision { let expected_smallest_reward = inflation(50) / 10u128.pow(18); // tad bit less. cannot be paid out. - assert_ok!(Balances::mutate_account(&default_reward_account(), |a| a.free += - expected_smallest_reward - 1)); + deposit_rewards(expected_smallest_reward - 1); assert_ok!(Pools::claim_payout(RuntimeOrigin::signed(10))); assert_eq!(pool_events_since_last_call(), vec![]); // revert it. - assert_ok!(Balances::mutate_account(&default_reward_account(), |a| a.free -= - expected_smallest_reward - 1)); + remove_rewards(expected_smallest_reward - 1); // tad bit more. can be claimed. - assert_ok!(Balances::mutate_account(&default_reward_account(), |a| a.free += - expected_smallest_reward + 1)); + deposit_rewards(expected_smallest_reward + 1); assert_ok!(Pools::claim_payout(RuntimeOrigin::signed(10))); assert_eq!( pool_events_since_last_call(), @@ -5114,9 +5114,7 @@ mod reward_counter_precision { assert_ok!(Pools::join(RuntimeOrigin::signed(20), tiny_bond / 2, 1)); // Suddenly, add a shit ton of rewards. - assert_ok!( - Balances::mutate_account(&default_reward_account(), |a| a.free += inflation(1)) - ); + deposit_rewards(inflation(1)); // now claim. assert_ok!(Pools::claim_payout(RuntimeOrigin::signed(10))); @@ -5155,8 +5153,7 @@ mod reward_counter_precision { // is earning all of the inflation per year (which is really unrealistic, but worse // case), that will be: let pool_total_earnings_10_years = inflation(10) - POLKADOT_TOTAL_ISSUANCE_GENESIS; - assert_ok!(Balances::mutate_account(&default_reward_account(), |a| a.free += - pool_total_earnings_10_years)); + deposit_rewards(pool_total_earnings_10_years); // some whale now joins with the other half ot the total issuance. This will bloat all // the calculation regarding current reward counter. @@ -5186,7 +5183,7 @@ mod reward_counter_precision { assert_ok!(Pools::join(RuntimeOrigin::signed(30), 10 * DOT, 1)); // and give a reasonably small reward to the pool. - assert_ok!(Balances::mutate_account(&default_reward_account(), |a| a.free += DOT)); + deposit_rewards(DOT); assert_ok!(Pools::claim_payout(RuntimeOrigin::signed(30))); assert_eq!( @@ -5258,9 +5255,7 @@ mod reward_counter_precision { assert_ok!(Pools::join(RuntimeOrigin::signed(20), 10 * DOT, 1)); // earn some small rewards - assert_ok!( - Balances::mutate_account(&default_reward_account(), |a| a.free += DOT / 1000) - ); + deposit_rewards(DOT / 1000); // no point in claiming for 20 (nonetheless, it should be harmless) assert!(pending_rewards(20).unwrap().is_zero()); @@ -5280,9 +5275,7 @@ mod reward_counter_precision { // earn some small more, still nothing can be claimed for 20, but 10 claims their // share. - assert_ok!( - Balances::mutate_account(&default_reward_account(), |a| a.free += DOT / 1000) - ); + deposit_rewards(DOT / 1000); assert!(pending_rewards(20).unwrap().is_zero()); assert_ok!(Pools::claim_payout(RuntimeOrigin::signed(10))); assert_eq!( @@ -5291,9 +5284,7 @@ mod reward_counter_precision { ); // earn some more rewards, this time 20 can also claim. - assert_ok!( - Balances::mutate_account(&default_reward_account(), |a| a.free += DOT / 1000) - ); + deposit_rewards(DOT / 1000); assert_eq!(pending_rewards(20).unwrap(), 1); assert_ok!(Pools::claim_payout(RuntimeOrigin::signed(10))); assert_ok!(Pools::claim_payout(RuntimeOrigin::signed(20))); @@ -5332,9 +5323,7 @@ mod reward_counter_precision { assert_ok!(Pools::join(RuntimeOrigin::signed(20), 10 * DOT, 1)); // earn some small rewards - assert_ok!( - Balances::mutate_account(&default_reward_account(), |a| a.free += DOT / 1000) - ); + deposit_rewards(DOT / 1000); // if 20 claims now, their reward counter should stay the same, so that they have a // chance of claiming this if they let it accumulate. Also see @@ -5442,7 +5431,7 @@ mod commission { // Pool earns 80 points and a payout is triggered. // Given: - assert_ok!(Balances::mutate_account(&default_reward_account(), |a| a.free += 80)); + deposit_rewards(80); assert_eq!( PoolMembers::::get(10).unwrap(), PoolMember:: { pool_id, points: 10, ..Default::default() } @@ -5489,7 +5478,7 @@ mod commission { // is next called, which is not done in this test segment.. // Given: - assert_ok!(Balances::mutate_account(&default_reward_account(), |a| a.free += 100)); + deposit_rewards(100); // When: assert_ok!(Pools::claim_payout(RuntimeOrigin::signed(10))); @@ -5625,7 +5614,7 @@ mod commission { 1, Some((Perbill::from_percent(10), root)), )); - assert_ok!(Balances::mutate_account(&default_reward_account(), |a| a.free += 40)); + deposit_rewards(40); // When: assert_ok!(Pools::claim_payout(RuntimeOrigin::signed(10))); @@ -6377,7 +6366,7 @@ mod commission { ); // The pool earns 10 points - assert_ok!(Balances::mutate_account(&default_reward_account(), |a| a.free += 10)); + deposit_rewards(10); assert_ok!(Pools::claim_payout(RuntimeOrigin::signed(10))); // Then: @@ -6387,7 +6376,7 @@ mod commission { ); // The pool earns 17 points - assert_ok!(Balances::mutate_account(&default_reward_account(), |a| a.free += 17)); + deposit_rewards(17); assert_ok!(Pools::claim_payout(RuntimeOrigin::signed(10))); // Then: @@ -6397,7 +6386,7 @@ mod commission { ); // The pool earns 50 points - assert_ok!(Balances::mutate_account(&default_reward_account(), |a| a.free += 50)); + deposit_rewards(50); assert_ok!(Pools::claim_payout(RuntimeOrigin::signed(10))); // Then: @@ -6407,7 +6396,7 @@ mod commission { ); // The pool earns 10439 points - assert_ok!(Balances::mutate_account(&default_reward_account(), |a| a.free += 10439)); + deposit_rewards(10439); assert_ok!(Pools::claim_payout(RuntimeOrigin::signed(10))); // Then: @@ -6427,7 +6416,7 @@ mod commission { )); // Given: - assert_ok!(Balances::mutate_account(&default_reward_account(), |a| a.free += 200)); + deposit_rewards(200); assert_ok!(Pools::claim_payout(RuntimeOrigin::signed(10))); // Then: @@ -6469,7 +6458,7 @@ mod commission { // When: // The pool earns 100 points - assert_ok!(Balances::mutate_account(&default_reward_account(), |a| a.free += 100)); + deposit_rewards(100); // Change commission to 20% assert_ok!(Pools::set_commission( @@ -6486,7 +6475,7 @@ mod commission { ); // The pool earns 100 points - assert_ok!(Balances::mutate_account(&default_reward_account(), |a| a.free += 100)); + deposit_rewards(100); // Then: @@ -6535,7 +6524,7 @@ mod commission { // When: // The pool earns 100 points - assert_ok!(Balances::mutate_account(&default_reward_account(), |a| a.free += 100)); + deposit_rewards(100); // Claim payout: assert_ok!(Pools::claim_payout(RuntimeOrigin::signed(10))); @@ -6592,7 +6581,7 @@ mod commission { ); // The pool earns 10 points - assert_ok!(Balances::mutate_account(&default_reward_account(), |a| a.free += 10)); + deposit_rewards(10); // execute the payout assert_ok!(Pools::do_reward_payout( @@ -6634,7 +6623,7 @@ mod commission { ); // The pool earns 10 points - assert_ok!(Balances::mutate_account(&default_reward_account(), |a| a.free += 10)); + deposit_rewards(10); // execute the payout assert_ok!(Pools::do_reward_payout( @@ -6677,7 +6666,7 @@ mod commission { ); // Pool earns 80 points, payout is triggered. - assert_ok!(Balances::mutate_account(&default_reward_account(), |a| a.free += 80)); + deposit_rewards(80); assert_eq!( PoolMembers::::get(10).unwrap(), PoolMember:: { pool_id, points: 10, ..Default::default() } diff --git a/frame/nomination-pools/test-staking/src/mock.rs b/frame/nomination-pools/test-staking/src/mock.rs index 0550c8e0c..9726f5e6d 100644 --- a/frame/nomination-pools/test-staking/src/mock.rs +++ b/frame/nomination-pools/test-staking/src/mock.rs @@ -86,6 +86,10 @@ impl pallet_balances::Config for Runtime { type ExistentialDeposit = ExistentialDeposit; type AccountStore = System; type WeightInfo = (); + type FreezeIdentifier = (); + type MaxFreezes = (); + type HoldIdentifier = (); + type MaxHolds = (); } pallet_staking_reward_curve::build! { diff --git a/frame/offences/benchmarking/src/mock.rs b/frame/offences/benchmarking/src/mock.rs index 233aa449d..058361e9f 100644 --- a/frame/offences/benchmarking/src/mock.rs +++ b/frame/offences/benchmarking/src/mock.rs @@ -74,6 +74,10 @@ impl pallet_balances::Config for Test { type ExistentialDeposit = ConstU64<10>; type AccountStore = System; type WeightInfo = (); + type FreezeIdentifier = (); + type MaxFreezes = (); + type HoldIdentifier = (); + type MaxHolds = (); } impl pallet_timestamp::Config for Test { diff --git a/frame/preimage/src/mock.rs b/frame/preimage/src/mock.rs index 23875ccb0..5054a77a8 100644 --- a/frame/preimage/src/mock.rs +++ b/frame/preimage/src/mock.rs @@ -84,6 +84,10 @@ impl pallet_balances::Config for Test { type MaxLocks = (); type MaxReserves = ConstU32<50>; type ReserveIdentifier = [u8; 8]; + type FreezeIdentifier = (); + type MaxFreezes = (); + type HoldIdentifier = (); + type MaxHolds = (); } ord_parameter_types! { diff --git a/frame/proxy/src/tests.rs b/frame/proxy/src/tests.rs index c49c344ac..f3771083c 100644 --- a/frame/proxy/src/tests.rs +++ b/frame/proxy/src/tests.rs @@ -88,6 +88,10 @@ impl pallet_balances::Config for Test { type ExistentialDeposit = ConstU64<1>; type AccountStore = System; type WeightInfo = (); + type FreezeIdentifier = (); + type MaxFreezes = (); + type HoldIdentifier = (); + type MaxHolds = (); } impl pallet_utility::Config for Test { type RuntimeEvent = RuntimeEvent; @@ -124,7 +128,10 @@ impl InstanceFilter for ProxyType { match self { ProxyType::Any => true, ProxyType::JustTransfer => { - matches!(c, RuntimeCall::Balances(pallet_balances::Call::transfer { .. })) + matches!( + c, + RuntimeCall::Balances(pallet_balances::Call::transfer_allow_death { .. }) + ) }, ProxyType::JustUtility => matches!(c, RuntimeCall::Utility { .. }), } @@ -161,7 +168,7 @@ impl Config for Test { use super::{Call as ProxyCall, Event as ProxyEvent}; use frame_system::Call as SystemCall; -use pallet_balances::{Call as BalancesCall, Error as BalancesError, Event as BalancesEvent}; +use pallet_balances::{Call as BalancesCall, Event as BalancesEvent}; use pallet_utility::{Call as UtilityCall, Event as UtilityEvent}; type SystemError = frame_system::Error; @@ -169,7 +176,7 @@ type SystemError = frame_system::Error; pub fn new_test_ext() -> sp_io::TestExternalities { let mut t = frame_system::GenesisConfig::default().build_storage::().unwrap(); pallet_balances::GenesisConfig:: { - balances: vec![(1, 10), (2, 10), (3, 10), (4, 10), (5, 2)], + balances: vec![(1, 10), (2, 10), (3, 10), (4, 10), (5, 3)], } .assimilate_storage(&mut t) .unwrap(); @@ -193,7 +200,7 @@ fn expect_events(e: Vec) { } fn call_transfer(dest: u64, value: u64) -> RuntimeCall { - RuntimeCall::Balances(BalancesCall::transfer { dest, value }) + RuntimeCall::Balances(BalancesCall::transfer_allow_death { dest, value }) } #[test] @@ -345,7 +352,7 @@ fn proxy_announced_removes_announcement_and_returns_deposit() { #[test] fn filtering_works() { new_test_ext().execute_with(|| { - assert!(Balances::mutate_account(&1, |a| a.free = 1000).is_ok()); + Balances::make_free_balance_be(&1, 1000); assert_ok!(Proxy::add_proxy(RuntimeOrigin::signed(1), 2, ProxyType::Any, 0)); assert_ok!(Proxy::add_proxy(RuntimeOrigin::signed(1), 3, ProxyType::JustTransfer, 0)); assert_ok!(Proxy::add_proxy(RuntimeOrigin::signed(1), 4, ProxyType::JustUtility, 0)); @@ -361,7 +368,7 @@ fn filtering_works() { ); let derivative_id = Utility::derivative_account_id(1, 0); - assert!(Balances::mutate_account(&derivative_id, |a| a.free = 1000).is_ok()); + Balances::make_free_balance_be(&derivative_id, 1000); let inner = Box::new(call_transfer(6, 1)); let call = Box::new(RuntimeCall::Utility(UtilityCall::as_derivative { @@ -516,7 +523,7 @@ fn cannot_add_proxy_without_balance() { assert_eq!(Balances::reserved_balance(5), 2); assert_noop!( Proxy::add_proxy(RuntimeOrigin::signed(5), 4, ProxyType::Any, 0), - BalancesError::::InsufficientBalance + DispatchError::ConsumerRemaining, ); }); } @@ -564,6 +571,7 @@ fn proxying_works() { #[test] fn pure_works() { new_test_ext().execute_with(|| { + Balances::make_free_balance_be(&1, 11); // An extra one for the ED. assert_ok!(Proxy::create_pure(RuntimeOrigin::signed(1), ProxyType::Any, 0, 0)); let anon = Proxy::pure_account(&1, &ProxyType::Any, 0, None); System::assert_last_event( @@ -592,7 +600,7 @@ fn pure_works() { assert_ok!(Proxy::create_pure(RuntimeOrigin::signed(1), ProxyType::Any, 0, 0)); let call = Box::new(call_transfer(6, 1)); - assert_ok!(Balances::transfer(RuntimeOrigin::signed(3), anon, 5)); + assert_ok!(Balances::transfer_allow_death(RuntimeOrigin::signed(3), anon, 5)); assert_ok!(Proxy::proxy(RuntimeOrigin::signed(1), anon, None, call)); System::assert_last_event(ProxyEvent::ProxyExecuted { result: Ok(()) }.into()); assert_eq!(Balances::free_balance(6), 1); @@ -611,9 +619,9 @@ fn pure_works() { Proxy::kill_pure(RuntimeOrigin::signed(1), 1, ProxyType::Any, 0, 1, 0), Error::::NoPermission ); - assert_eq!(Balances::free_balance(1), 0); + assert_eq!(Balances::free_balance(1), 1); assert_ok!(Proxy::proxy(RuntimeOrigin::signed(1), anon, None, call.clone())); - assert_eq!(Balances::free_balance(1), 2); + assert_eq!(Balances::free_balance(1), 3); assert_noop!( Proxy::proxy(RuntimeOrigin::signed(1), anon, None, call.clone()), Error::::NotProxy diff --git a/frame/recovery/src/mock.rs b/frame/recovery/src/mock.rs index 1d21be801..5c190e2a2 100644 --- a/frame/recovery/src/mock.rs +++ b/frame/recovery/src/mock.rs @@ -86,6 +86,10 @@ impl pallet_balances::Config for Test { type ExistentialDeposit = ExistentialDeposit; type AccountStore = System; type WeightInfo = (); + type FreezeIdentifier = (); + type MaxFreezes = (); + type HoldIdentifier = (); + type MaxHolds = (); } parameter_types! { diff --git a/frame/recovery/src/tests.rs b/frame/recovery/src/tests.rs index 9381d6a88..85024d0bc 100644 --- a/frame/recovery/src/tests.rs +++ b/frame/recovery/src/tests.rs @@ -45,7 +45,10 @@ fn set_recovered_works() { // Root can set a recovered account though assert_ok!(Recovery::set_recovered(RuntimeOrigin::root(), 5, 1)); // Account 1 should now be able to make a call through account 5 - let call = Box::new(RuntimeCall::Balances(BalancesCall::transfer { dest: 1, value: 100 })); + let call = Box::new(RuntimeCall::Balances(BalancesCall::transfer_allow_death { + dest: 1, + value: 100, + })); assert_ok!(Recovery::as_recovered(RuntimeOrigin::signed(1), 5, call)); // Account 1 has successfully drained the funds from account 5 assert_eq!(Balances::free_balance(1), 200); @@ -93,7 +96,10 @@ fn recovery_life_cycle_works() { assert_ok!(Recovery::as_recovered(RuntimeOrigin::signed(1), 5, call)); // Account 1 should now be able to make a call through account 5 to get all of their funds assert_eq!(Balances::free_balance(5), 110); - let call = Box::new(RuntimeCall::Balances(BalancesCall::transfer { dest: 1, value: 110 })); + let call = Box::new(RuntimeCall::Balances(BalancesCall::transfer_allow_death { + dest: 1, + value: 110, + })); assert_ok!(Recovery::as_recovered(RuntimeOrigin::signed(1), 5, call)); // All funds have been fully recovered! assert_eq!(Balances::free_balance(1), 200); diff --git a/frame/referenda/src/migration.rs b/frame/referenda/src/migration.rs index e15eb499b..c27ab452a 100644 --- a/frame/referenda/src/migration.rs +++ b/frame/referenda/src/migration.rs @@ -191,7 +191,7 @@ pub mod test { #[test] pub fn referendum_status_v0() { // make sure the bytes of the encoded referendum v0 is decodable. - let ongoing_encoded = sp_core::Bytes::from_str("0x00000000013001012a000000000000000400000100000000000000010000000000000001000000000000000a00000000000000000000000000000000000100").unwrap(); + let ongoing_encoded = sp_core::Bytes::from_str("0x00000000012c01082a0000000000000004000100000000000000010000000000000001000000000000000a00000000000000000000000000000000000100").unwrap(); let ongoing_dec = v0::ReferendumInfoOf::::decode(&mut &*ongoing_encoded).unwrap(); let ongoing = v0::ReferendumInfoOf::::Ongoing(create_status_v0()); assert_eq!(ongoing, ongoing_dec); diff --git a/frame/referenda/src/mock.rs b/frame/referenda/src/mock.rs index ade2a09a9..cdedb7955 100644 --- a/frame/referenda/src/mock.rs +++ b/frame/referenda/src/mock.rs @@ -57,7 +57,7 @@ frame_support::construct_runtime!( pub struct BaseFilter; impl Contains for BaseFilter { fn contains(call: &RuntimeCall) -> bool { - !matches!(call, &RuntimeCall::Balances(pallet_balances::Call::set_balance { .. })) + !matches!(call, &RuntimeCall::Balances(pallet_balances::Call::force_set_balance { .. })) } } @@ -120,6 +120,10 @@ impl pallet_balances::Config for Test { type ExistentialDeposit = ConstU64<1>; type AccountStore = System; type WeightInfo = (); + type FreezeIdentifier = (); + type MaxFreezes = (); + type HoldIdentifier = (); + type MaxHolds = (); } parameter_types! { pub static AlarmInterval: u64 = 1; @@ -295,19 +299,14 @@ impl VoteTally for Tally { } pub fn set_balance_proposal(value: u64) -> Vec { - RuntimeCall::Balances(pallet_balances::Call::set_balance { - who: 42, - new_free: value, - new_reserved: 0, - }) - .encode() + RuntimeCall::Balances(pallet_balances::Call::force_set_balance { who: 42, new_free: value }) + .encode() } pub fn set_balance_proposal_bounded(value: u64) -> BoundedCallOf { - let c = RuntimeCall::Balances(pallet_balances::Call::set_balance { + let c = RuntimeCall::Balances(pallet_balances::Call::force_set_balance { who: 42, new_free: value, - new_reserved: 0, }); ::bound(c).unwrap() } diff --git a/frame/root-offences/src/mock.rs b/frame/root-offences/src/mock.rs index 0937c43d6..828551e4d 100644 --- a/frame/root-offences/src/mock.rs +++ b/frame/root-offences/src/mock.rs @@ -121,6 +121,10 @@ impl pallet_balances::Config for Test { type ExistentialDeposit = ConstU64<1>; type AccountStore = System; type WeightInfo = (); + type FreezeIdentifier = (); + type MaxFreezes = (); + type HoldIdentifier = (); + type MaxHolds = (); } pallet_staking_reward_curve::build! { diff --git a/frame/scored-pool/src/mock.rs b/frame/scored-pool/src/mock.rs index a91b1a60f..f10a1320e 100644 --- a/frame/scored-pool/src/mock.rs +++ b/frame/scored-pool/src/mock.rs @@ -91,6 +91,10 @@ impl pallet_balances::Config for Test { type ExistentialDeposit = ConstU64<1>; type AccountStore = System; type WeightInfo = (); + type FreezeIdentifier = (); + type MaxFreezes = (); + type HoldIdentifier = (); + type MaxHolds = (); } parameter_types! { diff --git a/frame/session/benchmarking/src/mock.rs b/frame/session/benchmarking/src/mock.rs index 4c4accbbf..b7671255f 100644 --- a/frame/session/benchmarking/src/mock.rs +++ b/frame/session/benchmarking/src/mock.rs @@ -84,6 +84,10 @@ impl pallet_balances::Config for Test { type ExistentialDeposit = ConstU64<10>; type AccountStore = System; type WeightInfo = (); + type FreezeIdentifier = (); + type MaxFreezes = (); + type HoldIdentifier = (); + type MaxHolds = (); } impl pallet_timestamp::Config for Test { diff --git a/frame/society/src/mock.rs b/frame/society/src/mock.rs index 73565dedc..9f72febc2 100644 --- a/frame/society/src/mock.rs +++ b/frame/society/src/mock.rs @@ -93,6 +93,10 @@ impl pallet_balances::Config for Test { type ExistentialDeposit = ConstU64<1>; type AccountStore = System; type WeightInfo = (); + type FreezeIdentifier = (); + type MaxFreezes = (); + type HoldIdentifier = (); + type MaxHolds = (); } impl Config for Test { diff --git a/frame/staking/src/mock.rs b/frame/staking/src/mock.rs index e876940cb..c2f559a97 100644 --- a/frame/staking/src/mock.rs +++ b/frame/staking/src/mock.rs @@ -157,6 +157,10 @@ impl pallet_balances::Config for Test { type ExistentialDeposit = ExistentialDeposit; type AccountStore = System; type WeightInfo = (); + type FreezeIdentifier = (); + type MaxFreezes = (); + type HoldIdentifier = (); + type MaxHolds = (); } sp_runtime::impl_opaque_keys! { diff --git a/frame/staking/src/tests.rs b/frame/staking/src/tests.rs index 966eae8c1..36cafcc18 100644 --- a/frame/staking/src/tests.rs +++ b/frame/staking/src/tests.rs @@ -30,7 +30,7 @@ use pallet_balances::Error as BalancesError; use sp_runtime::{ assert_eq_error_rate, traits::{BadOrigin, Dispatchable}, - Perbill, Percent, Rounding, + Perbill, Percent, Rounding, TokenError, }; use sp_staking::{ offence::{DisableStrategy, OffenceDetails, OnOffenceHandler}, @@ -98,8 +98,8 @@ fn force_unstake_works() { add_slash(&11); // Cant transfer assert_noop!( - Balances::transfer(RuntimeOrigin::signed(11), 1, 10), - BalancesError::::LiquidityRestrictions + Balances::transfer_allow_death(RuntimeOrigin::signed(11), 1, 10), + TokenError::Frozen, ); // Force unstake requires root. assert_noop!(Staking::force_unstake(RuntimeOrigin::signed(11), 11, 2), BadOrigin); @@ -113,7 +113,7 @@ fn force_unstake_works() { // No longer bonded. assert_eq!(Staking::bonded(&11), None); // Transfer works. - assert_ok!(Balances::transfer(RuntimeOrigin::signed(11), 1, 10)); + assert_ok!(Balances::transfer_allow_death(RuntimeOrigin::signed(11), 1, 10)); }); } @@ -960,14 +960,14 @@ fn cannot_transfer_staked_balance() { assert_eq!(Staking::eras_stakers(active_era(), 11).total, 1000); // Confirm account 11 cannot transfer as a result assert_noop!( - Balances::transfer(RuntimeOrigin::signed(11), 20, 1), - BalancesError::::LiquidityRestrictions + Balances::transfer_allow_death(RuntimeOrigin::signed(11), 20, 1), + TokenError::Frozen, ); // Give account 11 extra free balance let _ = Balances::make_free_balance_be(&11, 10000); // Confirm that account 11 can now transfer some balance - assert_ok!(Balances::transfer(RuntimeOrigin::signed(11), 20, 1)); + assert_ok!(Balances::transfer_allow_death(RuntimeOrigin::signed(11), 20, 1)); }); } @@ -985,10 +985,10 @@ fn cannot_transfer_staked_balance_2() { assert_eq!(Staking::eras_stakers(active_era(), 21).total, 1000); // Confirm account 21 can transfer at most 1000 assert_noop!( - Balances::transfer(RuntimeOrigin::signed(21), 20, 1001), - BalancesError::::LiquidityRestrictions + Balances::transfer_allow_death(RuntimeOrigin::signed(21), 20, 1001), + TokenError::Frozen, ); - assert_ok!(Balances::transfer(RuntimeOrigin::signed(21), 20, 1000)); + assert_ok!(Balances::transfer_allow_death(RuntimeOrigin::signed(21), 20, 1000)); }); } @@ -4187,7 +4187,7 @@ fn payout_creates_controller() { bond_nominator(1234, 1337, 100, vec![11]); // kill controller - assert_ok!(Balances::transfer(RuntimeOrigin::signed(1337), 1234, 100)); + assert_ok!(Balances::transfer_allow_death(RuntimeOrigin::signed(1337), 1234, 100)); assert_eq!(Balances::free_balance(1337), 0); mock::start_active_era(1); diff --git a/frame/state-trie-migration/src/lib.rs b/frame/state-trie-migration/src/lib.rs index 5385c6b5f..1f6266d99 100644 --- a/frame/state-trie-migration/src/lib.rs +++ b/frame/state-trie-migration/src/lib.rs @@ -1128,6 +1128,10 @@ mod mock { type MaxReserves = (); type ReserveIdentifier = [u8; 8]; type WeightInfo = (); + type FreezeIdentifier = (); + type MaxFreezes = (); + type HoldIdentifier = (); + type MaxHolds = (); } /// Test only Weights for state migration. diff --git a/frame/support/src/traits/stored_map.rs b/frame/support/src/traits/stored_map.rs index a073b2682..cbe70f293 100644 --- a/frame/support/src/traits/stored_map.rs +++ b/frame/support/src/traits/stored_map.rs @@ -17,7 +17,7 @@ //! Traits and associated datatypes for managing abstract stored values. -use crate::{storage::StorageMap, traits::misc::HandleLifetime}; +use crate::storage::StorageMap; use codec::FullCodec; use sp_runtime::DispatchError; @@ -81,48 +81,29 @@ pub trait StoredMap { /// be the default value), or where the account is being removed or reset back to the default value /// where previously it did exist (though may have been in a default state). This works well with /// system module's `CallOnCreatedAccount` and `CallKillAccount`. -pub struct StorageMapShim(sp_std::marker::PhantomData<(S, L, K, T)>); -impl< - S: StorageMap, - L: HandleLifetime, - K: FullCodec, - T: FullCodec + Default, - > StoredMap for StorageMapShim +pub struct StorageMapShim(sp_std::marker::PhantomData<(S, K, T)>); +impl, K: FullCodec, T: FullCodec + Default> StoredMap + for StorageMapShim { fn get(k: &K) -> T { S::get(k) } fn insert(k: &K, t: T) -> Result<(), DispatchError> { - if !S::contains_key(&k) { - L::created(k)?; - } S::insert(k, t); Ok(()) } fn remove(k: &K) -> Result<(), DispatchError> { if S::contains_key(&k) { - L::killed(k)?; S::remove(k); } Ok(()) } fn mutate(k: &K, f: impl FnOnce(&mut T) -> R) -> Result { - if !S::contains_key(&k) { - L::created(k)?; - } Ok(S::mutate(k, f)) } fn mutate_exists(k: &K, f: impl FnOnce(&mut Option) -> R) -> Result { S::try_mutate_exists(k, |maybe_value| { - let existed = maybe_value.is_some(); let r = f(maybe_value); - let exists = maybe_value.is_some(); - - if !existed && exists { - L::created(k)?; - } else if existed && !exists { - L::killed(k)?; - } Ok(r) }) } @@ -131,15 +112,7 @@ impl< f: impl FnOnce(&mut Option) -> Result, ) -> Result { S::try_mutate_exists(k, |maybe_value| { - let existed = maybe_value.is_some(); let r = f(maybe_value)?; - let exists = maybe_value.is_some(); - - if !existed && exists { - L::created(k).map_err(E::from)?; - } else if existed && !exists { - L::killed(k).map_err(E::from)?; - } Ok(r) }) } diff --git a/frame/support/src/traits/tokens.rs b/frame/support/src/traits/tokens.rs index 2d2bd63ad..d2753caa6 100644 --- a/frame/support/src/traits/tokens.rs +++ b/frame/support/src/traits/tokens.rs @@ -30,6 +30,7 @@ pub use imbalance::Imbalance; pub mod pay; pub use misc::{ AssetId, Balance, BalanceConversion, BalanceStatus, ConvertRank, DepositConsequence, - ExistenceRequirement, GetSalary, Locker, WithdrawConsequence, WithdrawReasons, + ExistenceRequirement, Fortitude, GetSalary, Locker, Precision, Preservation, Provenance, + Restriction, WithdrawConsequence, WithdrawReasons, }; pub use pay::{Pay, PayFromAccount, PaymentStatus}; diff --git a/frame/support/src/traits/tokens/fungible.rs b/frame/support/src/traits/tokens/fungible.rs deleted file mode 100644 index 12bac2972..000000000 --- a/frame/support/src/traits/tokens/fungible.rs +++ /dev/null @@ -1,367 +0,0 @@ -// This file is part of Substrate. - -// Copyright (C) Parity Technologies (UK) Ltd. -// SPDX-License-Identifier: Apache-2.0 - -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -//! The traits for dealing with a single fungible token class and any associated types. - -use super::{ - misc::{Balance, DepositConsequence, WithdrawConsequence}, - *, -}; -use crate::{ - dispatch::{DispatchError, DispatchResult}, - traits::misc::Get, -}; -use sp_runtime::traits::Saturating; - -mod balanced; -mod imbalance; -pub use balanced::{Balanced, Unbalanced}; -pub use imbalance::{CreditOf, DebtOf, HandleImbalanceDrop, Imbalance}; - -/// Trait for providing balance-inspection access to a fungible asset. -pub trait Inspect { - /// Scalar type for representing balance of an account. - type Balance: Balance; - - /// The total amount of issuance in the system. - fn total_issuance() -> Self::Balance; - - /// The total amount of issuance in the system excluding those which are controlled by the - /// system. - fn active_issuance() -> Self::Balance { - Self::total_issuance() - } - - /// The minimum balance any single account may have. - fn minimum_balance() -> Self::Balance; - - /// Get the balance of `who`. - fn balance(who: &AccountId) -> Self::Balance; - - /// Get the maximum amount that `who` can withdraw/transfer successfully. - fn reducible_balance(who: &AccountId, keep_alive: bool) -> Self::Balance; - - /// Returns `true` if the balance of `who` may be increased by `amount`. - /// - /// - `who`: The account of which the balance should be increased by `amount`. - /// - `amount`: How much should the balance be increased? - /// - `mint`: Will `amount` be minted to deposit it into `account`? - fn can_deposit(who: &AccountId, amount: Self::Balance, mint: bool) -> DepositConsequence; - - /// Returns `Failed` if the balance of `who` may not be decreased by `amount`, otherwise - /// the consequence. - fn can_withdraw(who: &AccountId, amount: Self::Balance) -> WithdrawConsequence; -} - -/// Trait for providing an ERC-20 style fungible asset. -pub trait Mutate: Inspect { - /// Increase the balance of `who` by exactly `amount`, minting new tokens. If that isn't - /// possible then an `Err` is returned and nothing is changed. - fn mint_into(who: &AccountId, amount: Self::Balance) -> DispatchResult; - - /// Decrease the balance of `who` by at least `amount`, possibly slightly more in the case of - /// minimum_balance requirements, burning the tokens. If that isn't possible then an `Err` is - /// returned and nothing is changed. If successful, the amount of tokens reduced is returned. - fn burn_from(who: &AccountId, amount: Self::Balance) -> Result; - - // TODO: Remove. - /// Attempt to reduce the balance of `who` by as much as possible up to `amount`, and possibly - /// slightly more due to minimum_balance requirements. If no decrease is possible then an `Err` - /// is returned and nothing is changed. If successful, the amount of tokens reduced is returned. - /// - /// The default implementation just uses `withdraw` along with `reducible_balance` to ensure - /// that it doesn't fail. - fn slash(who: &AccountId, amount: Self::Balance) -> Result { - Self::burn_from(who, Self::reducible_balance(who, false).min(amount)) - } - - /// Transfer funds from one account into another. The default implementation uses `mint_into` - /// and `burn_from` and may generate unwanted events. - fn teleport( - source: &AccountId, - dest: &AccountId, - amount: Self::Balance, - ) -> Result { - let extra = Self::can_withdraw(&source, amount).into_result()?; - // As we first burn and then mint, we don't need to check if `mint` fits into the supply. - // If we can withdraw/burn it, we can also mint it again. - Self::can_deposit(dest, amount.saturating_add(extra), false).into_result()?; - let actual = Self::burn_from(source, amount)?; - debug_assert!( - actual == amount.saturating_add(extra), - "can_withdraw must agree with withdraw; qed" - ); - match Self::mint_into(dest, actual) { - Ok(_) => Ok(actual), - Err(err) => { - debug_assert!(false, "can_deposit returned true previously; qed"); - // attempt to return the funds back to source - let revert = Self::mint_into(source, actual); - debug_assert!(revert.is_ok(), "withdrew funds previously; qed"); - Err(err) - }, - } - } -} - -/// Trait for providing a fungible asset which can only be transferred. -pub trait Transfer: Inspect { - /// Transfer funds from one account into another. - fn transfer( - source: &AccountId, - dest: &AccountId, - amount: Self::Balance, - keep_alive: bool, - ) -> Result; - - /// Reduce the active issuance by some amount. - fn deactivate(_: Self::Balance) {} - - /// Increase the active issuance by some amount, up to the outstanding amount reduced. - fn reactivate(_: Self::Balance) {} -} - -/// Trait for inspecting a fungible asset which can be reserved. -pub trait InspectHold: Inspect { - /// Amount of funds held in reserve by `who`. - fn balance_on_hold(who: &AccountId) -> Self::Balance; - - /// Check to see if some `amount` of funds of `who` may be placed on hold. - fn can_hold(who: &AccountId, amount: Self::Balance) -> bool; -} - -// TODO: Introduce `HoldReason`. -/// Trait for mutating a fungible asset which can be reserved. -pub trait MutateHold: InspectHold + Transfer { - /// Hold some funds in an account. - fn hold(who: &AccountId, amount: Self::Balance) -> DispatchResult; - - /// Release up to `amount` held funds in an account. - /// - /// The actual amount released is returned with `Ok`. - /// - /// If `best_effort` is `true`, then the amount actually unreserved and returned as the inner - /// value of `Ok` may be smaller than the `amount` passed. - fn release( - who: &AccountId, - amount: Self::Balance, - best_effort: bool, - ) -> Result; - - // TODO: Introduce repatriate_held - - /// Transfer held funds into a destination account. - /// - /// If `on_hold` is `true`, then the destination account must already exist and the assets - /// transferred will still be on hold in the destination account. If not, then the destination - /// account need not already exist, but must be creatable. - /// - /// If `best_effort` is `true`, then an amount less than `amount` may be transferred without - /// error. - /// - /// The actual amount transferred is returned, or `Err` in the case of error and nothing is - /// changed. - fn transfer_held( - source: &AccountId, - dest: &AccountId, - amount: Self::Balance, - best_effort: bool, - on_held: bool, - ) -> Result; -} - -/// Trait for slashing a fungible asset which can be reserved. -pub trait BalancedHold: Balanced + MutateHold { - /// Reduce the balance of some funds on hold in an account. - /// - /// The resulting imbalance is the first item of the tuple returned. - /// - /// As much funds that are on hold up to `amount` will be deducted as possible. If this is less - /// than `amount`, then a non-zero second item will be returned. - fn slash_held( - who: &AccountId, - amount: Self::Balance, - ) -> (CreditOf, Self::Balance); -} - -impl + MutateHold> BalancedHold for T { - // TODO: This should be implemented properly, and `slash` should be removed. - fn slash_held( - who: &AccountId, - amount: Self::Balance, - ) -> (CreditOf, Self::Balance) { - let actual = match Self::release(who, amount, true) { - Ok(x) => x, - Err(_) => return (Imbalance::default(), amount), - }; - >::slash(who, actual) - } -} - -/// Convert a `fungibles` trait implementation into a `fungible` trait implementation by identifying -/// a single item. -pub struct ItemOf< - F: fungibles::Inspect, - A: Get<>::AssetId>, - AccountId, ->(sp_std::marker::PhantomData<(F, A, AccountId)>); - -impl< - F: fungibles::Inspect, - A: Get<>::AssetId>, - AccountId, - > Inspect for ItemOf -{ - type Balance = >::Balance; - fn total_issuance() -> Self::Balance { - >::total_issuance(A::get()) - } - fn active_issuance() -> Self::Balance { - >::active_issuance(A::get()) - } - fn minimum_balance() -> Self::Balance { - >::minimum_balance(A::get()) - } - fn balance(who: &AccountId) -> Self::Balance { - >::balance(A::get(), who) - } - fn reducible_balance(who: &AccountId, keep_alive: bool) -> Self::Balance { - >::reducible_balance(A::get(), who, keep_alive) - } - fn can_deposit(who: &AccountId, amount: Self::Balance, mint: bool) -> DepositConsequence { - >::can_deposit(A::get(), who, amount, mint) - } - fn can_withdraw(who: &AccountId, amount: Self::Balance) -> WithdrawConsequence { - >::can_withdraw(A::get(), who, amount) - } -} - -impl< - F: fungibles::Mutate, - A: Get<>::AssetId>, - AccountId, - > Mutate for ItemOf -{ - fn mint_into(who: &AccountId, amount: Self::Balance) -> DispatchResult { - >::mint_into(A::get(), who, amount) - } - fn burn_from(who: &AccountId, amount: Self::Balance) -> Result { - >::burn_from(A::get(), who, amount) - } -} - -impl< - F: fungibles::Transfer, - A: Get<>::AssetId>, - AccountId, - > Transfer for ItemOf -{ - fn transfer( - source: &AccountId, - dest: &AccountId, - amount: Self::Balance, - keep_alive: bool, - ) -> Result { - >::transfer(A::get(), source, dest, amount, keep_alive) - } - fn deactivate(amount: Self::Balance) { - >::deactivate(A::get(), amount) - } - fn reactivate(amount: Self::Balance) { - >::reactivate(A::get(), amount) - } -} - -impl< - F: fungibles::InspectHold, - A: Get<>::AssetId>, - AccountId, - > InspectHold for ItemOf -{ - fn balance_on_hold(who: &AccountId) -> Self::Balance { - >::balance_on_hold(A::get(), who) - } - fn can_hold(who: &AccountId, amount: Self::Balance) -> bool { - >::can_hold(A::get(), who, amount) - } -} - -impl< - F: fungibles::MutateHold, - A: Get<>::AssetId>, - AccountId, - > MutateHold for ItemOf -{ - fn hold(who: &AccountId, amount: Self::Balance) -> DispatchResult { - >::hold(A::get(), who, amount) - } - fn release( - who: &AccountId, - amount: Self::Balance, - best_effort: bool, - ) -> Result { - >::release(A::get(), who, amount, best_effort) - } - fn transfer_held( - source: &AccountId, - dest: &AccountId, - amount: Self::Balance, - best_effort: bool, - on_hold: bool, - ) -> Result { - >::transfer_held( - A::get(), - source, - dest, - amount, - best_effort, - on_hold, - ) - } -} - -impl< - F: fungibles::Unbalanced, - A: Get<>::AssetId>, - AccountId, - > Unbalanced for ItemOf -{ - fn set_balance(who: &AccountId, amount: Self::Balance) -> DispatchResult { - >::set_balance(A::get(), who, amount) - } - fn set_total_issuance(amount: Self::Balance) -> () { - >::set_total_issuance(A::get(), amount) - } - fn decrease_balance( - who: &AccountId, - amount: Self::Balance, - ) -> Result { - >::decrease_balance(A::get(), who, amount) - } - fn decrease_balance_at_most(who: &AccountId, amount: Self::Balance) -> Self::Balance { - >::decrease_balance_at_most(A::get(), who, amount) - } - fn increase_balance( - who: &AccountId, - amount: Self::Balance, - ) -> Result { - >::increase_balance(A::get(), who, amount) - } - fn increase_balance_at_most(who: &AccountId, amount: Self::Balance) -> Self::Balance { - >::increase_balance_at_most(A::get(), who, amount) - } -} diff --git a/frame/support/src/traits/tokens/fungible/balanced.rs b/frame/support/src/traits/tokens/fungible/balanced.rs deleted file mode 100644 index 437d2c82d..000000000 --- a/frame/support/src/traits/tokens/fungible/balanced.rs +++ /dev/null @@ -1,354 +0,0 @@ -// This file is part of Substrate. - -// Copyright (C) Parity Technologies (UK) Ltd. -// SPDX-License-Identifier: Apache-2.0 - -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -//! The trait and associated types for sets of fungible tokens that manage total issuance without -//! requiring atomic balanced operations. - -use super::{super::Imbalance as ImbalanceT, *}; -use crate::{ - dispatch::{DispatchError, DispatchResult}, - traits::misc::{SameOrOther, TryDrop}, -}; -use sp_runtime::{ - traits::{CheckedAdd, Zero}, - ArithmeticError, TokenError, -}; -use sp_std::marker::PhantomData; - -/// A fungible token class where any creation and deletion of tokens is semi-explicit and where the -/// total supply is maintained automatically. -/// -/// This is auto-implemented when a token class has `Unbalanced` implemented. -pub trait Balanced: Inspect { - /// The type for managing what happens when an instance of `Debt` is dropped without being used. - type OnDropDebt: HandleImbalanceDrop; - /// The type for managing what happens when an instance of `Credit` is dropped without being - /// used. - type OnDropCredit: HandleImbalanceDrop; - - /// Reduce the total issuance by `amount` and return the according imbalance. The imbalance will - /// typically be used to reduce an account by the same amount with e.g. `settle`. - /// - /// This is infallible, but doesn't guarantee that the entire `amount` is burnt, for example - /// in the case of underflow. - fn rescind(amount: Self::Balance) -> DebtOf; - - /// Increase the total issuance by `amount` and return the according imbalance. The imbalance - /// will typically be used to increase an account by the same amount with e.g. - /// `resolve_into_existing` or `resolve_creating`. - /// - /// This is infallible, but doesn't guarantee that the entire `amount` is issued, for example - /// in the case of overflow. - fn issue(amount: Self::Balance) -> CreditOf; - - /// Produce a pair of imbalances that cancel each other out exactly. - /// - /// This is just the same as burning and issuing the same amount and has no effect on the - /// total issuance. - fn pair(amount: Self::Balance) -> (DebtOf, CreditOf) { - (Self::rescind(amount), Self::issue(amount)) - } - - /// Deducts up to `value` from the combined balance of `who`. This function cannot fail. - /// - /// The resulting imbalance is the first item of the tuple returned. - /// - /// As much funds up to `value` will be deducted as possible. If this is less than `value`, - /// then a non-zero second item will be returned. - fn slash(who: &AccountId, amount: Self::Balance) -> (CreditOf, Self::Balance); - - /// Mints exactly `value` into the account of `who`. - /// - /// If `who` doesn't exist, nothing is done and an `Err` returned. This could happen because it - /// the account doesn't yet exist and it isn't possible to create it under the current - /// circumstances and with `value` in it. - fn deposit( - who: &AccountId, - value: Self::Balance, - ) -> Result, DispatchError>; - - /// Removes `value` balance from `who` account if possible. - /// - /// If the removal is not possible, then it returns `Err` and nothing is changed. - /// - /// If the operation is successful, this will return `Ok` with a `NegativeImbalance` whose value - /// is no less than `value`. It may be more in the case that removing it reduced it below - /// `Self::minimum_balance()`. - fn withdraw( - who: &AccountId, - value: Self::Balance, - // TODO: liveness: ExistenceRequirement, - ) -> Result, DispatchError>; - - /// The balance of `who` is increased in order to counter `credit`. If the whole of `credit` - /// cannot be countered, then nothing is changed and the original `credit` is returned in an - /// `Err`. - /// - /// Please note: If `credit.peek()` is less than `Self::minimum_balance()`, then `who` must - /// already exist for this to succeed. - fn resolve( - who: &AccountId, - credit: CreditOf, - ) -> Result<(), CreditOf> { - let v = credit.peek(); - let debt = match Self::deposit(who, v) { - Err(_) => return Err(credit), - Ok(d) => d, - }; - let result = credit.offset(debt).try_drop(); - debug_assert!(result.is_ok(), "ok deposit return must be equal to credit value; qed"); - Ok(()) - } - - /// The balance of `who` is decreased in order to counter `debt`. If the whole of `debt` - /// cannot be countered, then nothing is changed and the original `debt` is returned in an - /// `Err`. - fn settle( - who: &AccountId, - debt: DebtOf, - // TODO: liveness: ExistenceRequirement, - ) -> Result, DebtOf> { - let amount = debt.peek(); - let credit = match Self::withdraw(who, amount) { - Err(_) => return Err(debt), - Ok(d) => d, - }; - match credit.offset(debt) { - SameOrOther::None => Ok(CreditOf::::zero()), - SameOrOther::Same(dust) => Ok(dust), - SameOrOther::Other(rest) => { - debug_assert!(false, "ok withdraw return must be at least debt value; qed"); - Err(rest) - }, - } - } -} - -/// A fungible token class where the balance can be set arbitrarily. -/// -/// **WARNING** -/// Do not use this directly unless you want trouble, since it allows you to alter account balances -/// without keeping the issuance up to date. It has no safeguards against accidentally creating -/// token imbalances in your system leading to accidental imflation or deflation. It's really just -/// for the underlying datatype to implement so the user gets the much safer `Balanced` trait to -/// use. -pub trait Unbalanced: Inspect { - /// Set the balance of `who` to `amount`. If this cannot be done for some reason (e.g. - /// because the account cannot be created or an overflow) then an `Err` is returned. - fn set_balance(who: &AccountId, amount: Self::Balance) -> DispatchResult; - - /// Set the total issuance to `amount`. - fn set_total_issuance(amount: Self::Balance); - - /// Reduce the balance of `who` by `amount`. If it cannot be reduced by that amount for - /// some reason, return `Err` and don't reduce it at all. If Ok, return the imbalance. - /// - /// Minimum balance will be respected and the returned imbalance may be up to - /// `Self::minimum_balance() - 1` greater than `amount`. - fn decrease_balance( - who: &AccountId, - amount: Self::Balance, - ) -> Result { - let old_balance = Self::balance(who); - let (mut new_balance, mut amount) = if Self::reducible_balance(who, false) < amount { - return Err(TokenError::NoFunds.into()) - } else { - (old_balance - amount, amount) - }; - if new_balance < Self::minimum_balance() { - amount = amount.saturating_add(new_balance); - new_balance = Zero::zero(); - } - // Defensive only - this should not fail now. - Self::set_balance(who, new_balance)?; - Ok(amount) - } - - /// Reduce the balance of `who` by the most that is possible, up to `amount`. - /// - /// Minimum balance will be respected and the returned imbalance may be up to - /// `Self::minimum_balance() - 1` greater than `amount`. - /// - /// Return the imbalance by which the account was reduced. - fn decrease_balance_at_most(who: &AccountId, amount: Self::Balance) -> Self::Balance { - let old_balance = Self::balance(who); - let old_free_balance = Self::reducible_balance(who, false); - let (mut new_balance, mut amount) = if old_free_balance < amount { - (old_balance.saturating_sub(old_free_balance), old_free_balance) - } else { - (old_balance - amount, amount) - }; - let minimum_balance = Self::minimum_balance(); - if new_balance < minimum_balance { - amount = amount.saturating_add(new_balance); - new_balance = Zero::zero(); - } - let mut r = Self::set_balance(who, new_balance); - if r.is_err() { - // Some error, probably because we tried to destroy an account which cannot be - // destroyed. - if new_balance.is_zero() && amount >= minimum_balance { - new_balance = minimum_balance; - amount -= minimum_balance; - r = Self::set_balance(who, new_balance); - } - if r.is_err() { - // Still an error. Apparently it's not possible to reduce at all. - amount = Zero::zero(); - } - } - amount - } - - /// Increase the balance of `who` by `amount`. If it cannot be increased by that amount - /// for some reason, return `Err` and don't increase it at all. If Ok, return the imbalance. - /// - /// Minimum balance will be respected and an error will be returned if - /// `amount < Self::minimum_balance()` when the account of `who` is zero. - fn increase_balance( - who: &AccountId, - amount: Self::Balance, - ) -> Result { - let old_balance = Self::balance(who); - let new_balance = old_balance.checked_add(&amount).ok_or(ArithmeticError::Overflow)?; - if new_balance < Self::minimum_balance() { - return Err(TokenError::BelowMinimum.into()) - } - if old_balance != new_balance { - Self::set_balance(who, new_balance)?; - } - Ok(amount) - } - - /// Increase the balance of `who` by the most that is possible, up to `amount`. - /// - /// Minimum balance will be respected and the returned imbalance will be zero in the case that - /// `amount < Self::minimum_balance()`. - /// - /// Return the imbalance by which the account was increased. - fn increase_balance_at_most(who: &AccountId, amount: Self::Balance) -> Self::Balance { - let old_balance = Self::balance(who); - let mut new_balance = old_balance.saturating_add(amount); - let mut amount = new_balance - old_balance; - if new_balance < Self::minimum_balance() { - new_balance = Zero::zero(); - amount = Zero::zero(); - } - if old_balance == new_balance || Self::set_balance(who, new_balance).is_ok() { - amount - } else { - Zero::zero() - } - } -} - -/// Simple handler for an imbalance drop which increases the total issuance of the system by the -/// imbalance amount. Used for leftover debt. -pub struct IncreaseIssuance(PhantomData<(AccountId, U)>); -impl> HandleImbalanceDrop - for IncreaseIssuance -{ - fn handle(amount: U::Balance) { - U::set_total_issuance(U::total_issuance().saturating_add(amount)) - } -} - -/// Simple handler for an imbalance drop which decreases the total issuance of the system by the -/// imbalance amount. Used for leftover credit. -pub struct DecreaseIssuance(PhantomData<(AccountId, U)>); -impl> HandleImbalanceDrop - for DecreaseIssuance -{ - fn handle(amount: U::Balance) { - U::set_total_issuance(U::total_issuance().saturating_sub(amount)) - } -} - -/// An imbalance type which uses `DecreaseIssuance` to deal with anything `Drop`ed. -/// -/// Basically means that funds in someone's account have been removed and not yet placed anywhere -/// else. If it gets dropped, then those funds will be assumed to be "burned" and the total supply -/// will be accordingly decreased to ensure it equals the sum of the balances of all accounts. -type Credit = Imbalance< - >::Balance, - DecreaseIssuance, - IncreaseIssuance, ->; - -/// An imbalance type which uses `IncreaseIssuance` to deal with anything `Drop`ed. -/// -/// Basically means that there are funds in someone's account whose origin is as yet unaccounted -/// for. If it gets dropped, then those funds will be assumed to be "minted" and the total supply -/// will be accordingly increased to ensure it equals the sum of the balances of all accounts. -type Debt = Imbalance< - >::Balance, - IncreaseIssuance, - DecreaseIssuance, ->; - -/// Create some `Credit` item. Only for internal use. -fn credit>(amount: U::Balance) -> Credit { - Imbalance::new(amount) -} - -/// Create some `Debt` item. Only for internal use. -fn debt>(amount: U::Balance) -> Debt { - Imbalance::new(amount) -} - -impl> Balanced for U { - type OnDropCredit = DecreaseIssuance; - type OnDropDebt = IncreaseIssuance; - fn rescind(amount: Self::Balance) -> Debt { - let old = U::total_issuance(); - let new = old.saturating_sub(amount); - U::set_total_issuance(new); - debt(old - new) - } - fn issue(amount: Self::Balance) -> Credit { - let old = U::total_issuance(); - let new = old.saturating_add(amount); - U::set_total_issuance(new); - credit(new - old) - } - fn slash(who: &AccountId, amount: Self::Balance) -> (Credit, Self::Balance) { - let slashed = U::decrease_balance_at_most(who, amount); - // `slashed` could be less than, greater than or equal to `amount`. - // If slashed == amount, it means the account had at least amount in it and it could all be - // removed without a problem. - // If slashed > amount, it means the account had more than amount in it, but not enough more - // to push it over minimum_balance. - // If slashed < amount, it means the account didn't have enough in it to be reduced by - // `amount` without being destroyed. - (credit(slashed), amount.saturating_sub(slashed)) - } - fn deposit( - who: &AccountId, - amount: Self::Balance, - ) -> Result, DispatchError> { - let increase = U::increase_balance(who, amount)?; - Ok(debt(increase)) - } - fn withdraw( - who: &AccountId, - amount: Self::Balance, - // TODO: liveness: ExistenceRequirement, - ) -> Result, DispatchError> { - let decrease = U::decrease_balance(who, amount)?; - Ok(credit(decrease)) - } -} diff --git a/frame/support/src/traits/tokens/fungible/freeze.rs b/frame/support/src/traits/tokens/fungible/freeze.rs new file mode 100644 index 000000000..1ec3a5fad --- /dev/null +++ b/frame/support/src/traits/tokens/fungible/freeze.rs @@ -0,0 +1,68 @@ +// This file is part of Substrate. + +// Copyright (C) 2019-2022 Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +//! The traits for putting freezes within a single fungible token class. + +use scale_info::TypeInfo; +use sp_runtime::DispatchResult; + +/// Trait for inspecting a fungible asset which can be frozen. Freezing is essentially setting a +/// minimum balance bellow which the total balance (inclusive of any funds placed on hold) may not +/// be normally allowed to drop. Generally, freezers will provide an "update" function such that +/// if the total balance does drop below the limit, then the freezer can update their housekeeping +/// accordingly. +pub trait Inspect: super::Inspect { + /// An identifier for a freeze. + type Id: codec::Encode + TypeInfo + 'static; + + /// Amount of funds held in reserve by `who` for the given `id`. + fn balance_frozen(id: &Self::Id, who: &AccountId) -> Self::Balance; + + /// The amount of the balance which can become frozen. Defaults to `total_balance()`. + fn balance_freezable(who: &AccountId) -> Self::Balance { + Self::total_balance(who) + } + + /// Returns `true` if it's possible to introduce a freeze for the given `id` onto the + /// account of `who`. This will be true as long as the implementor supports as many + /// concurrent freeze locks as there are possible values of `id`. + fn can_freeze(id: &Self::Id, who: &AccountId) -> bool; +} + +/// Trait for introducing, altering and removing locks to freeze an account's funds so they never +/// go below a set minimum. +pub trait Mutate: Inspect { + /// Prevent actions which would reduce the balance of the account of `who` below the given + /// `amount` and identify this restriction though the given `id`. Unlike `extend_freeze`, any + /// outstanding freeze in place for `who` under the `id` are dropped. + /// + /// If `amount` is zero, it is equivalent to using `thaw`. + /// + /// Note that `amount` can be greater than the total balance, if desired. + fn set_freeze(id: &Self::Id, who: &AccountId, amount: Self::Balance) -> DispatchResult; + + /// Prevent the balance of the account of `who` from being reduced below the given `amount` and + /// identify this restriction though the given `id`. Unlike `set_freeze`, this does not + /// counteract any pre-existing freezes in place for `who` under the `id`. Also unlike + /// `set_freeze`, in the case that `amount` is zero, this is no-op and never fails. + /// + /// Note that more funds can be locked than the total balance, if desired. + fn extend_freeze(id: &Self::Id, who: &AccountId, amount: Self::Balance) -> DispatchResult; + + /// Remove an existing lock. + fn thaw(id: &Self::Id, who: &AccountId) -> DispatchResult; +} diff --git a/frame/support/src/traits/tokens/fungible/hold.rs b/frame/support/src/traits/tokens/fungible/hold.rs new file mode 100644 index 000000000..ddcb8c6ac --- /dev/null +++ b/frame/support/src/traits/tokens/fungible/hold.rs @@ -0,0 +1,393 @@ +// This file is part of Substrate. + +// Copyright (C) 2019-2022 Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +//! The traits for putting holds within a single fungible token class. + +use crate::{ + ensure, + traits::tokens::{ + DepositConsequence::Success, + Fortitude::{self, Force}, + Precision::{self, BestEffort, Exact}, + Preservation::{self, Protect}, + Provenance::Extant, + Restriction::{self, Free, OnHold}, + }, +}; +use scale_info::TypeInfo; +use sp_arithmetic::{ + traits::{CheckedAdd, CheckedSub, Zero}, + ArithmeticError, +}; +use sp_runtime::{DispatchError, DispatchResult, Saturating, TokenError}; + +use super::*; + +/// Trait for inspecting a fungible asset whose accounts support partitioning and slashing. +pub trait Inspect: super::Inspect { + /// An identifier for a hold. Used for disambiguating different holds so that + /// they can be individually replaced or removed and funds from one hold don't accidentally + /// become unreserved or slashed for another. + type Reason: codec::Encode + TypeInfo + 'static; + + /// Amount of funds on hold (for all hold reasons) of `who`. + fn total_balance_on_hold(who: &AccountId) -> Self::Balance; + + /// Get the maximum amount that the `total_balance_on_hold` of `who` can be reduced successfully + /// based on whether we are willing to force the reduction and potentially go below user-level + /// restrictions on the minimum amount of the account. Note: This cannot bring the account into + /// an inconsistent state with regards any required existential deposit. + /// + /// Always less than `total_balance_on_hold()`. + fn reducible_total_balance_on_hold(who: &AccountId, force: Fortitude) -> Self::Balance; + + /// Amount of funds on hold (for the given reason) of `who`. + fn balance_on_hold(reason: &Self::Reason, who: &AccountId) -> Self::Balance; + + /// Returns `true` if it's possible to place (additional) funds under a hold of a given + /// `reason`. This may fail if the account has exhausted a limited number of concurrent + /// holds or if it cannot be made to exist (e.g. there is no provider reference). + /// + /// NOTE: This does not take into account changes which could be made to the account of `who` + /// (such as removing a provider reference) after this call is made. Any usage of this should + /// therefore ensure the account is already in the appropriate state prior to calling it. + fn hold_available(reason: &Self::Reason, who: &AccountId) -> bool; + + /// Check to see if some `amount` of funds of `who` may be placed on hold with the given + /// `reason`. Reasons why this may not be true: + /// + /// - The implementor supports only a limited number of concurrent holds on an account which is + /// the possible values of `reason`; + /// - The total balance of the account is less than `amount`; + /// - Removing `amount` from the total balance would kill the account and remove the only + /// provider reference. + /// + /// Note: we pass `true` as the third argument to `reducible_balance` since we assume that if + /// needed the balance can slashed. If we are using a simple non-forcing reserve-transfer, then + /// we really ought to check that we are not reducing the funds below the freeze-limit (if any). + /// + /// NOTE: This does not take into account changes which could be made to the account of `who` + /// (such as removing a provider reference) after this call is made. Any usage of this should + /// therefore ensure the account is already in the appropriate state prior to calling it. + fn ensure_can_hold( + reason: &Self::Reason, + who: &AccountId, + amount: Self::Balance, + ) -> DispatchResult { + ensure!(Self::hold_available(reason, who), TokenError::CannotCreateHold); + ensure!( + amount <= Self::reducible_balance(who, Protect, Force), + TokenError::FundsUnavailable + ); + Ok(()) + } + + /// Check to see if some `amount` of funds of `who` may be placed on hold for the given + /// `reason`. Reasons why this may not be true: + /// + /// - The implementor supports only a limited number of concurrernt holds on an account which is + /// the possible values of `reason`; + /// - The main balance of the account is less than `amount`; + /// - Removing `amount` from the main balance would kill the account and remove the only + /// provider reference. + /// + /// NOTE: This does not take into account changes which could be made to the account of `who` + /// (such as removing a provider reference) after this call is made. Any usage of this should + /// therefore ensure the account is already in the appropriate state prior to calling it. + fn can_hold(reason: &Self::Reason, who: &AccountId, amount: Self::Balance) -> bool { + Self::ensure_can_hold(reason, who, amount).is_ok() + } +} + +/// A fungible, holdable token class where the balance on hold can be set arbitrarily. +/// +/// **WARNING** +/// Do not use this directly unless you want trouble, since it allows you to alter account balances +/// without keeping the issuance up to date. It has no safeguards against accidentally creating +/// token imbalances in your system leading to accidental imflation or deflation. It's really just +/// for the underlying datatype to implement so the user gets the much safer `Balanced` trait to +/// use. +pub trait Unbalanced: Inspect { + /// Forcefully set the balance on hold of `who` to `amount`. This is independent of any other + /// balances on hold or the main ("free") balance. + /// + /// If this call executes successfully, you can `assert_eq!(Self::balance_on_hold(), amount);`. + /// + /// This function does its best to force the balance change through, but will not break system + /// invariants such as any Existential Deposits needed or overflows/underflows. + /// If this cannot be done for some reason (e.g. because the account doesn't exist) then an + /// `Err` is returned. + // Implmentation note: This should increment the consumer refs if it moves total on hold from + // zero to non-zero and decrement in the opposite direction. + // + // Since this was not done in the previous logic, this will need either a migration or a + // state item which tracks whether the account is on the old logic or new. + fn set_balance_on_hold( + reason: &Self::Reason, + who: &AccountId, + amount: Self::Balance, + ) -> DispatchResult; + + /// Reduce the balance on hold of `who` by `amount`. + /// + /// If `precision` is `Exact` and it cannot be reduced by that amount for + /// some reason, return `Err` and don't reduce it at all. If `precision` is `BestEffort`, then + /// reduce the balance of `who` by the most that is possible, up to `amount`. + /// + /// In either case, if `Ok` is returned then the inner is the amount by which is was reduced. + fn decrease_balance_on_hold( + reason: &Self::Reason, + who: &AccountId, + mut amount: Self::Balance, + precision: Precision, + ) -> Result { + let old_balance = Self::balance_on_hold(reason, who); + if let BestEffort = precision { + amount = amount.min(old_balance); + } + let new_balance = old_balance.checked_sub(&amount).ok_or(TokenError::FundsUnavailable)?; + Self::set_balance_on_hold(reason, who, new_balance)?; + Ok(amount) + } + + /// Increase the balance on hold of `who` by `amount`. + /// + /// If it cannot be increased by that amount for some reason, return `Err` and don't increase + /// it at all. If Ok, return the imbalance. + fn increase_balance_on_hold( + reason: &Self::Reason, + who: &AccountId, + amount: Self::Balance, + precision: Precision, + ) -> Result { + let old_balance = Self::balance_on_hold(reason, who); + let new_balance = if let BestEffort = precision { + old_balance.saturating_add(amount) + } else { + old_balance.checked_add(&amount).ok_or(ArithmeticError::Overflow)? + }; + let amount = new_balance.saturating_sub(old_balance); + if !amount.is_zero() { + Self::set_balance_on_hold(reason, who, new_balance)?; + } + Ok(amount) + } +} + +/// Trait for mutating a fungible asset which can be placed on hold. +pub trait Mutate: + Inspect + super::Unbalanced + Unbalanced +{ + /// Hold some funds in an account. If a hold for `reason` is already in place, then this + /// will increase it. + fn hold(reason: &Self::Reason, who: &AccountId, amount: Self::Balance) -> DispatchResult { + // NOTE: This doesn't change the total balance of the account so there's no need to + // check liquidity. + + Self::ensure_can_hold(reason, who, amount)?; + // Should be infallible now, but we proceed softly anyway. + Self::decrease_balance(who, amount, Exact, Protect, Force)?; + Self::increase_balance_on_hold(reason, who, amount, BestEffort)?; + Self::done_hold(reason, who, amount); + Ok(()) + } + + /// Release up to `amount` held funds in an account. + /// + /// The actual amount released is returned with `Ok`. + /// + /// If `precision` is `BestEffort`, then the amount actually unreserved and returned as the + /// inner value of `Ok` may be smaller than the `amount` passed. + /// + /// NOTE! The inner of the `Ok` result variant returns the *actual* amount released. This is the + /// opposite of the `ReservableCurrency::unreserve()` result, which gives the amount not able + /// to be released! + fn release( + reason: &Self::Reason, + who: &AccountId, + amount: Self::Balance, + precision: Precision, + ) -> Result { + // NOTE: This doesn't change the total balance of the account so there's no need to + // check liquidity. + + // We want to make sure we can deposit the amount in advance. If we can't then something is + // very wrong. + ensure!(Self::can_deposit(who, amount, Extant) == Success, TokenError::CannotCreate); + // Get the amount we can actually take from the hold. This might be less than what we want + // if we're only doing a best-effort. + let amount = Self::decrease_balance_on_hold(reason, who, amount, precision)?; + // Increase the main balance by what we took. We always do a best-effort here because we + // already checked that we can deposit before. + let actual = Self::increase_balance(who, amount, BestEffort)?; + Self::done_release(reason, who, actual); + Ok(actual) + } + + /// Attempt to decrease the balance of `who` which is held for the given `reason` by `amount`. + /// + /// If `precision` is `BestEffort`, then as much as possible is reduced, up to `amount`, and the + /// amount of tokens reduced is returned. Otherwise, if the total amount can be reduced, then it + /// is and the amount returned, and if not, then nothing changes and `Err` is returned. + /// + /// If `force` is `Force`, then locks/freezes will be ignored. This should only be used when + /// conducting slashing or other activity which materially disadvantages the account holder + /// since it could provide a means of circumventing freezes. + fn burn_held( + reason: &Self::Reason, + who: &AccountId, + mut amount: Self::Balance, + precision: Precision, + force: Fortitude, + ) -> Result { + // We must check total-balance requirements if `!force`. + let liquid = Self::reducible_total_balance_on_hold(who, force); + if let BestEffort = precision { + amount = amount.min(liquid); + } else { + ensure!(amount <= liquid, TokenError::Frozen); + } + let amount = Self::decrease_balance_on_hold(reason, who, amount, precision)?; + Self::set_total_issuance(Self::total_issuance().saturating_sub(amount)); + Self::done_burn_held(reason, who, amount); + Ok(amount) + } + + /// Transfer held funds into a destination account. + /// + /// If `on_hold` is `true`, then the destination account must already exist and the assets + /// transferred will still be on hold in the destination account. If not, then the destination + /// account need not already exist, but must be creatable. + /// + /// If `precision` is `BestEffort`, then an amount less than `amount` may be transferred without + /// error. + /// + /// If `force` is `Force`, then other fund-locking mechanisms may be disregarded. It should be + /// left as `Polite` in most circumstances, but when you want the same power as a `slash`, it + /// may be `Force`. + /// + /// The actual amount transferred is returned, or `Err` in the case of error and nothing is + /// changed. + fn transfer_on_hold( + reason: &Self::Reason, + source: &AccountId, + dest: &AccountId, + mut amount: Self::Balance, + precision: Precision, + mode: Restriction, + force: Fortitude, + ) -> Result { + // We must check total-balance requirements if `force` is `Fortitude::Polite`. + let have = Self::balance_on_hold(reason, source); + let liquid = Self::reducible_total_balance_on_hold(source, force); + if let BestEffort = precision { + amount = amount.min(liquid).min(have); + } else { + ensure!(amount <= liquid, TokenError::Frozen); + ensure!(amount <= have, TokenError::FundsUnavailable); + } + + // We want to make sure we can deposit the amount in advance. If we can't then something is + // very wrong. + ensure!(Self::can_deposit(dest, amount, Extant) == Success, TokenError::CannotCreate); + ensure!(mode == Free || Self::hold_available(reason, dest), TokenError::CannotCreateHold); + + let amount = Self::decrease_balance_on_hold(reason, source, amount, precision)?; + let actual = if mode == OnHold { + Self::increase_balance_on_hold(reason, dest, amount, precision)? + } else { + Self::increase_balance(dest, amount, precision)? + }; + Self::done_transfer_on_hold(reason, source, dest, actual); + Ok(actual) + } + + /// Transfer some `amount` of free balance from `source` to become owned by `dest` but on hold + /// for `reason`. + /// + /// If `precision` is `BestEffort`, then an amount less than `amount` may be transferred without + /// error. + /// + /// `source` must obey the requirements of `keep_alive`. + /// + /// If `force` is `Force`, then other fund-locking mechanisms may be disregarded. It should be + /// left as `Polite` in most circumstances, but when you want the same power as a `slash`, it + /// may be `Force`. + /// + /// The amount placed on hold is returned or `Err` in the case of error and nothing is changed. + /// + /// WARNING: This may return an error after a partial storage mutation. It should be used only + /// inside a transactional storage context and an `Err` result must imply a storage rollback. + fn transfer_and_hold( + reason: &Self::Reason, + source: &AccountId, + dest: &AccountId, + amount: Self::Balance, + precision: Precision, + expendability: Preservation, + force: Fortitude, + ) -> Result { + ensure!(Self::hold_available(reason, dest), TokenError::CannotCreateHold); + ensure!(Self::can_deposit(dest, amount, Extant) == Success, TokenError::CannotCreate); + let actual = Self::decrease_balance(source, amount, precision, expendability, force)?; + Self::increase_balance_on_hold(reason, dest, actual, precision)?; + Self::done_transfer_on_hold(reason, source, dest, actual); + Ok(actual) + } + + fn done_hold(_reason: &Self::Reason, _who: &AccountId, _amount: Self::Balance) {} + fn done_release(_reason: &Self::Reason, _who: &AccountId, _amount: Self::Balance) {} + fn done_burn_held(_reason: &Self::Reason, _who: &AccountId, _amount: Self::Balance) {} + fn done_transfer_on_hold( + _reason: &Self::Reason, + _source: &AccountId, + _dest: &AccountId, + _amount: Self::Balance, + ) { + } + fn done_transfer_and_hold( + _reason: &Self::Reason, + _source: &AccountId, + _dest: &AccountId, + _transferred: Self::Balance, + ) { + } +} + +/// Trait for slashing a fungible asset which can be place on hold. +pub trait Balanced: super::Balanced + Unbalanced { + /// Reduce the balance of some funds on hold in an account. + /// + /// The resulting imbalance is the first item of the tuple returned. + /// + /// As much funds that are on hold up to `amount` will be deducted as possible. If this is less + /// than `amount`, then a non-zero second item will be returned. + fn slash( + reason: &Self::Reason, + who: &AccountId, + amount: Self::Balance, + ) -> (Credit, Self::Balance) { + let decrease = Self::decrease_balance_on_hold(reason, who, amount, BestEffort) + .unwrap_or(Default::default()); + let credit = + Imbalance::::new(decrease); + Self::done_slash(reason, who, decrease); + (credit, amount.saturating_sub(decrease)) + } + + fn done_slash(_reason: &Self::Reason, _who: &AccountId, _amount: Self::Balance) {} +} diff --git a/frame/support/src/traits/tokens/fungible/imbalance.rs b/frame/support/src/traits/tokens/fungible/imbalance.rs index 1b3d16c62..de85924a4 100644 --- a/frame/support/src/traits/tokens/fungible/imbalance.rs +++ b/frame/support/src/traits/tokens/fungible/imbalance.rs @@ -18,8 +18,11 @@ //! The imbalance type and its associates, which handles keeps everything adding up properly with //! unbalanced operations. -use super::{super::Imbalance as ImbalanceT, balanced::Balanced, misc::Balance, *}; -use crate::traits::misc::{SameOrOther, TryDrop}; +use super::{super::Imbalance as ImbalanceT, Balanced, *}; +use crate::traits::{ + misc::{SameOrOther, TryDrop}, + tokens::Balance, +}; use sp_runtime::{traits::Zero, RuntimeDebug}; use sp_std::marker::PhantomData; @@ -30,6 +33,10 @@ pub trait HandleImbalanceDrop { fn handle(amount: Balance); } +impl HandleImbalanceDrop for () { + fn handle(_: Balance) {} +} + /// An imbalance in the system, representing a divergence of recorded token supply from the sum of /// the balances of all accounts. This is `must_use` in order to ensure it gets handled (placing /// into an account, settling from an account or altering the supply). @@ -135,7 +142,7 @@ impl, OppositeOnDrop: HandleImbalance } /// Imbalance implying that the total_issuance value is less than the sum of all account balances. -pub type DebtOf = Imbalance< +pub type Debt = Imbalance< >::Balance, // This will generally be implemented by increasing the total_issuance value. >::OnDropDebt, @@ -144,7 +151,7 @@ pub type DebtOf = Imbalance< /// Imbalance implying that the total_issuance value is greater than the sum of all account /// balances. -pub type CreditOf = Imbalance< +pub type Credit = Imbalance< >::Balance, // This will generally be implemented by decreasing the total_issuance value. >::OnDropCredit, diff --git a/frame/support/src/traits/tokens/fungible/item_of.rs b/frame/support/src/traits/tokens/fungible/item_of.rs new file mode 100644 index 000000000..cf2d96ef2 --- /dev/null +++ b/frame/support/src/traits/tokens/fungible/item_of.rs @@ -0,0 +1,451 @@ +// This file is part of Substrate. + +// Copyright (C) 2019-2022 Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +//! Adapter to use `fungibles::*` implementations as `fungible::*`. + +use sp_core::Get; +use sp_runtime::{DispatchError, DispatchResult}; + +use super::*; +use crate::traits::tokens::{ + fungibles, DepositConsequence, Fortitude, Imbalance as ImbalanceT, Precision, Preservation, + Provenance, Restriction, WithdrawConsequence, +}; + +/// Convert a `fungibles` trait implementation into a `fungible` trait implementation by identifying +/// a single item. +pub struct ItemOf< + F: fungibles::Inspect, + A: Get<>::AssetId>, + AccountId, +>(sp_std::marker::PhantomData<(F, A, AccountId)>); + +impl< + F: fungibles::Inspect, + A: Get<>::AssetId>, + AccountId, + > Inspect for ItemOf +{ + type Balance = >::Balance; + fn total_issuance() -> Self::Balance { + >::total_issuance(A::get()) + } + fn active_issuance() -> Self::Balance { + >::active_issuance(A::get()) + } + fn minimum_balance() -> Self::Balance { + >::minimum_balance(A::get()) + } + fn balance(who: &AccountId) -> Self::Balance { + >::balance(A::get(), who) + } + fn total_balance(who: &AccountId) -> Self::Balance { + >::total_balance(A::get(), who) + } + fn reducible_balance( + who: &AccountId, + preservation: Preservation, + force: Fortitude, + ) -> Self::Balance { + >::reducible_balance(A::get(), who, preservation, force) + } + fn can_deposit( + who: &AccountId, + amount: Self::Balance, + provenance: Provenance, + ) -> DepositConsequence { + >::can_deposit(A::get(), who, amount, provenance) + } + fn can_withdraw(who: &AccountId, amount: Self::Balance) -> WithdrawConsequence { + >::can_withdraw(A::get(), who, amount) + } +} + +impl< + F: fungibles::InspectHold, + A: Get<>::AssetId>, + AccountId, + > InspectHold for ItemOf +{ + type Reason = F::Reason; + + fn reducible_total_balance_on_hold(who: &AccountId, force: Fortitude) -> Self::Balance { + >::reducible_total_balance_on_hold( + A::get(), + who, + force, + ) + } + fn hold_available(reason: &Self::Reason, who: &AccountId) -> bool { + >::hold_available(A::get(), reason, who) + } + fn total_balance_on_hold(who: &AccountId) -> Self::Balance { + >::total_balance_on_hold(A::get(), who) + } + fn balance_on_hold(reason: &Self::Reason, who: &AccountId) -> Self::Balance { + >::balance_on_hold(A::get(), reason, who) + } + fn can_hold(reason: &Self::Reason, who: &AccountId, amount: Self::Balance) -> bool { + >::can_hold(A::get(), reason, who, amount) + } +} + +impl< + F: fungibles::InspectFreeze, + A: Get<>::AssetId>, + AccountId, + > InspectFreeze for ItemOf +{ + type Id = F::Id; + fn balance_frozen(id: &Self::Id, who: &AccountId) -> Self::Balance { + >::balance_frozen(A::get(), id, who) + } + fn balance_freezable(who: &AccountId) -> Self::Balance { + >::balance_freezable(A::get(), who) + } + fn can_freeze(id: &Self::Id, who: &AccountId) -> bool { + >::can_freeze(A::get(), id, who) + } +} + +impl< + F: fungibles::Unbalanced, + A: Get<>::AssetId>, + AccountId, + > Unbalanced for ItemOf +{ + fn handle_dust(dust: regular::Dust) + where + Self: Sized, + { + >::handle_dust(fungibles::Dust(A::get(), dust.0)) + } + fn write_balance( + who: &AccountId, + amount: Self::Balance, + ) -> Result, DispatchError> { + >::write_balance(A::get(), who, amount) + } + fn set_total_issuance(amount: Self::Balance) -> () { + >::set_total_issuance(A::get(), amount) + } + fn decrease_balance( + who: &AccountId, + amount: Self::Balance, + precision: Precision, + preservation: Preservation, + force: Fortitude, + ) -> Result { + >::decrease_balance( + A::get(), + who, + amount, + precision, + preservation, + force, + ) + } + fn increase_balance( + who: &AccountId, + amount: Self::Balance, + precision: Precision, + ) -> Result { + >::increase_balance(A::get(), who, amount, precision) + } +} + +impl< + F: fungibles::UnbalancedHold, + A: Get<>::AssetId>, + AccountId, + > UnbalancedHold for ItemOf +{ + fn set_balance_on_hold( + reason: &Self::Reason, + who: &AccountId, + amount: Self::Balance, + ) -> DispatchResult { + >::set_balance_on_hold( + A::get(), + reason, + who, + amount, + ) + } + fn decrease_balance_on_hold( + reason: &Self::Reason, + who: &AccountId, + amount: Self::Balance, + precision: Precision, + ) -> Result { + >::decrease_balance_on_hold( + A::get(), + reason, + who, + amount, + precision, + ) + } + fn increase_balance_on_hold( + reason: &Self::Reason, + who: &AccountId, + amount: Self::Balance, + precision: Precision, + ) -> Result { + >::increase_balance_on_hold( + A::get(), + reason, + who, + amount, + precision, + ) + } +} + +impl< + F: fungibles::Mutate, + A: Get<>::AssetId>, + AccountId, + > Mutate for ItemOf +{ + fn mint_into(who: &AccountId, amount: Self::Balance) -> Result { + >::mint_into(A::get(), who, amount) + } + fn burn_from( + who: &AccountId, + amount: Self::Balance, + precision: Precision, + force: Fortitude, + ) -> Result { + >::burn_from(A::get(), who, amount, precision, force) + } + fn shelve(who: &AccountId, amount: Self::Balance) -> Result { + >::shelve(A::get(), who, amount) + } + fn restore(who: &AccountId, amount: Self::Balance) -> Result { + >::restore(A::get(), who, amount) + } + fn transfer( + source: &AccountId, + dest: &AccountId, + amount: Self::Balance, + preservation: Preservation, + ) -> Result { + >::transfer(A::get(), source, dest, amount, preservation) + } + + fn set_balance(who: &AccountId, amount: Self::Balance) -> Self::Balance { + >::set_balance(A::get(), who, amount) + } +} + +impl< + F: fungibles::MutateHold, + A: Get<>::AssetId>, + AccountId, + > MutateHold for ItemOf +{ + fn hold(reason: &Self::Reason, who: &AccountId, amount: Self::Balance) -> DispatchResult { + >::hold(A::get(), reason, who, amount) + } + fn release( + reason: &Self::Reason, + who: &AccountId, + amount: Self::Balance, + precision: Precision, + ) -> Result { + >::release(A::get(), reason, who, amount, precision) + } + fn burn_held( + reason: &Self::Reason, + who: &AccountId, + amount: Self::Balance, + precision: Precision, + force: Fortitude, + ) -> Result { + >::burn_held( + A::get(), + reason, + who, + amount, + precision, + force, + ) + } + fn transfer_on_hold( + reason: &Self::Reason, + source: &AccountId, + dest: &AccountId, + amount: Self::Balance, + precision: Precision, + mode: Restriction, + force: Fortitude, + ) -> Result { + >::transfer_on_hold( + A::get(), + reason, + source, + dest, + amount, + precision, + mode, + force, + ) + } + fn transfer_and_hold( + reason: &Self::Reason, + source: &AccountId, + dest: &AccountId, + amount: Self::Balance, + precision: Precision, + preservation: Preservation, + force: Fortitude, + ) -> Result { + >::transfer_and_hold( + A::get(), + reason, + source, + dest, + amount, + precision, + preservation, + force, + ) + } +} + +impl< + F: fungibles::MutateFreeze, + A: Get<>::AssetId>, + AccountId, + > MutateFreeze for ItemOf +{ + fn set_freeze(id: &Self::Id, who: &AccountId, amount: Self::Balance) -> DispatchResult { + >::set_freeze(A::get(), id, who, amount) + } + fn extend_freeze(id: &Self::Id, who: &AccountId, amount: Self::Balance) -> DispatchResult { + >::extend_freeze(A::get(), id, who, amount) + } + fn thaw(id: &Self::Id, who: &AccountId) -> DispatchResult { + >::thaw(A::get(), id, who) + } +} + +pub struct ConvertImbalanceDropHandler( + sp_std::marker::PhantomData<(AccountId, Balance, AssetIdType, AssetId, Handler)>, +); + +impl< + AccountId, + Balance, + AssetIdType, + AssetId: Get, + Handler: crate::traits::tokens::fungibles::HandleImbalanceDrop, + > HandleImbalanceDrop + for ConvertImbalanceDropHandler +{ + fn handle(amount: Balance) { + Handler::handle(AssetId::get(), amount) + } +} + +impl< + F: fungibles::Inspect + + fungibles::Unbalanced + + fungibles::Balanced, + A: Get<>::AssetId>, + AccountId, + > Balanced for ItemOf +{ + type OnDropDebt = + ConvertImbalanceDropHandler; + type OnDropCredit = + ConvertImbalanceDropHandler; + fn deposit( + who: &AccountId, + value: Self::Balance, + precision: Precision, + ) -> Result, DispatchError> { + >::deposit(A::get(), who, value, precision) + .map(|debt| Imbalance::new(debt.peek())) + } + fn issue(amount: Self::Balance) -> Credit { + Imbalance::new(>::issue(A::get(), amount).peek()) + } + fn pair(amount: Self::Balance) -> (Debt, Credit) { + let (a, b) = >::pair(A::get(), amount); + (Imbalance::new(a.peek()), Imbalance::new(b.peek())) + } + fn rescind(amount: Self::Balance) -> Debt { + Imbalance::new(>::rescind(A::get(), amount).peek()) + } + fn resolve( + who: &AccountId, + credit: Credit, + ) -> Result<(), Credit> { + let credit = fungibles::Imbalance::new(A::get(), credit.peek()); + >::resolve(who, credit) + .map_err(|credit| Imbalance::new(credit.peek())) + } + fn settle( + who: &AccountId, + debt: Debt, + preservation: Preservation, + ) -> Result, Debt> { + let debt = fungibles::Imbalance::new(A::get(), debt.peek()); + >::settle(who, debt, preservation) + .map(|credit| Imbalance::new(credit.peek())) + .map_err(|debt| Imbalance::new(debt.peek())) + } + fn withdraw( + who: &AccountId, + value: Self::Balance, + precision: Precision, + preservation: Preservation, + force: Fortitude, + ) -> Result, DispatchError> { + >::withdraw( + A::get(), + who, + value, + precision, + preservation, + force, + ) + .map(|credit| Imbalance::new(credit.peek())) + } +} + +impl< + F: fungibles::BalancedHold, + A: Get<>::AssetId>, + AccountId, + > BalancedHold for ItemOf +{ + fn slash( + reason: &Self::Reason, + who: &AccountId, + amount: Self::Balance, + ) -> (Credit, Self::Balance) { + let (credit, amount) = + >::slash(A::get(), reason, who, amount); + (Imbalance::new(credit.peek()), amount) + } +} + +#[test] +fn test() {} diff --git a/frame/support/src/traits/tokens/fungible/mod.rs b/frame/support/src/traits/tokens/fungible/mod.rs new file mode 100644 index 000000000..204b85ac2 --- /dev/null +++ b/frame/support/src/traits/tokens/fungible/mod.rs @@ -0,0 +1,56 @@ +// This file is part of Substrate. + +// Copyright (C) 2019-2022 Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +//! The traits for dealing with a single fungible token class and any associated types. +//! +//! ### User-implememted traits +//! - `Inspect`: Regular balance inspector functions. +//! - `Unbalanced`: Low-level balance mutating functions. Does not guarantee proper book-keeping and +//! so should not be called into directly from application code. Other traits depend on this and +//! provide default implementations based on it. +//! - `UnbalancedHold`: Low-level balance mutating functions for balances placed on hold. Does not +//! guarantee proper book-keeping and so should not be called into directly from application code. +//! Other traits depend on this and provide default implementations based on it. +//! - `Mutate`: Regular balance mutator functions. Pre-implemented using `Unbalanced`, though the +//! `done_*` functions should likely be reimplemented in case you want to do something following +//! the operation such as emit events. +//! - `InspectHold`: Inspector functions for balances on hold. +//! - `MutateHold`: Mutator functions for balances on hold. Mostly pre-implemented using +//! `UnbalancedHold`. +//! - `InspectFreeze`: Inspector functions for frozen balance. +//! - `MutateFreeze`: Mutator functions for frozen balance. +//! - `Balanced`: One-sided mutator functions for regular balances, which return imbalance objects +//! which guarantee eventual book-keeping. May be useful for some sophisticated operations where +//! funds must be removed from an account before it is known precisely what should be done with +//! them. + +pub mod freeze; +pub mod hold; +mod imbalance; +mod item_of; +mod regular; + +pub use freeze::{Inspect as InspectFreeze, Mutate as MutateFreeze}; +pub use hold::{ + Balanced as BalancedHold, Inspect as InspectHold, Mutate as MutateHold, + Unbalanced as UnbalancedHold, +}; +pub use imbalance::{Credit, Debt, HandleImbalanceDrop, Imbalance}; +pub use item_of::ItemOf; +pub use regular::{ + Balanced, DecreaseIssuance, Dust, IncreaseIssuance, Inspect, Mutate, Unbalanced, +}; diff --git a/frame/support/src/traits/tokens/fungible/regular.rs b/frame/support/src/traits/tokens/fungible/regular.rs new file mode 100644 index 000000000..574392cac --- /dev/null +++ b/frame/support/src/traits/tokens/fungible/regular.rs @@ -0,0 +1,506 @@ +// This file is part of Substrate. + +// Copyright (C) 2019-2022 Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +//! `Inspect` and `Mutate` traits for working with regular balances. + +use crate::{ + dispatch::DispatchError, + ensure, + traits::{ + tokens::{ + misc::{ + Balance, DepositConsequence, + Fortitude::{self, Force, Polite}, + Precision::{self, BestEffort, Exact}, + Preservation::{self, Expendable}, + Provenance::{self, Extant}, + WithdrawConsequence, + }, + Imbalance as ImbalanceT, + }, + SameOrOther, TryDrop, + }, +}; +use sp_arithmetic::traits::{CheckedAdd, CheckedSub, One}; +use sp_runtime::{traits::Saturating, ArithmeticError, TokenError}; +use sp_std::marker::PhantomData; + +use super::{Credit, Debt, HandleImbalanceDrop, Imbalance}; + +/// Trait for providing balance-inspection access to a fungible asset. +pub trait Inspect: Sized { + /// Scalar type for representing balance of an account. + type Balance: Balance; + + /// The total amount of issuance in the system. + fn total_issuance() -> Self::Balance; + + /// The total amount of issuance in the system excluding those which are controlled by the + /// system. + fn active_issuance() -> Self::Balance { + Self::total_issuance() + } + + /// The minimum balance any single account may have. + fn minimum_balance() -> Self::Balance; + + /// Get the total amount of funds whose ultimate bneficial ownership can be determined as `who`. + /// + /// This may include funds which are wholly inaccessible to `who`, either temporarily or even + /// indefinitely. + /// + /// For the amount of the balance which is currently free to be removed from the account without + /// error, use `reducible_balance`. + /// + /// For the amount of the balance which may eventually be free to be removed from the account, + /// use `balance()`. + fn total_balance(who: &AccountId) -> Self::Balance; + + /// Get the balance of `who` which does not include funds which are exclusively allocated to + /// subsystems of the chain ("on hold" or "reserved"). + /// + /// In general this isn't especially useful outside of tests, and for practical purposes, you'll + /// want to use `reducible_balance()`. + fn balance(who: &AccountId) -> Self::Balance; + + /// Get the maximum amount that `who` can withdraw/transfer successfully based on whether the + /// account should be kept alive (`preservation`) or whether we are willing to force the + /// reduction and potentially go below user-level restrictions on the minimum amount of the + /// account. + /// + /// Always less than or equal to `balance()`. + fn reducible_balance( + who: &AccountId, + preservation: Preservation, + force: Fortitude, + ) -> Self::Balance; + + /// Returns `true` if the balance of `who` may be increased by `amount`. + /// + /// - `who`: The account of which the balance should be increased by `amount`. + /// - `amount`: How much should the balance be increased? + /// - `provenance`: Will `amount` be minted to deposit it into `account` or is it already in the + /// system? + fn can_deposit( + who: &AccountId, + amount: Self::Balance, + provenance: Provenance, + ) -> DepositConsequence; + + /// Returns `Success` if the balance of `who` may be decreased by `amount`, otherwise + /// the consequence. + fn can_withdraw(who: &AccountId, amount: Self::Balance) -> WithdrawConsequence; +} + +/// Special dust type which can be type-safely converted into a `Credit`. +#[must_use] +pub struct Dust>(pub(crate) T::Balance); + +impl> Dust { + /// Convert `Dust` into an instance of `Credit`. + pub fn into_credit(self) -> Credit { + Credit::::new(self.0) + } +} + +/// A fungible token class where the balance can be set arbitrarily. +/// +/// **WARNING** +/// Do not use this directly unless you want trouble, since it allows you to alter account balances +/// without keeping the issuance up to date. It has no safeguards against accidentally creating +/// token imbalances in your system leading to accidental imflation or deflation. It's really just +/// for the underlying datatype to implement so the user gets the much safer `Balanced` trait to +/// use. +pub trait Unbalanced: Inspect { + /// Create some dust and handle it with `Self::handle_dust`. This is an unbalanced operation + /// and it must only be used when an account is modified in a raw fashion, outside of the entire + /// fungibles API. The `amount` is capped at `Self::minimum_balance() - 1`. + /// + /// This should not be reimplemented. + fn handle_raw_dust(amount: Self::Balance) { + Self::handle_dust(Dust(amount.min(Self::minimum_balance().saturating_sub(One::one())))) + } + + /// Do something with the dust which has been destroyed from the system. `Dust` can be converted + /// into a `Credit` with the `Balanced` trait impl. + fn handle_dust(dust: Dust); + + /// Forcefully set the balance of `who` to `amount`. + /// + /// If this call executes successfully, you can `assert_eq!(Self::balance(), amount);`. + /// + /// For implementations which include one or more balances on hold, then these are *not* + /// included in the `amount`. + /// + /// This function does its best to force the balance change through, but will not break system + /// invariants such as any Existential Deposits needed or overflows/underflows. + /// If this cannot be done for some reason (e.g. because the account cannot be created, deleted + /// or would overflow) then an `Err` is returned. + /// + /// If `Ok` is returned then its inner, if `Some` is the amount which was discarded as dust due + /// to existential deposit requirements. The default implementation of `decrease_balance` and + /// `increase_balance` converts this into an `Imbalance` and then passes it into `handle_dust`. + fn write_balance( + who: &AccountId, + amount: Self::Balance, + ) -> Result, DispatchError>; + + /// Set the total issuance to `amount`. + fn set_total_issuance(amount: Self::Balance); + + /// Reduce the balance of `who` by `amount`. + /// + /// If `precision` is `Exact` and it cannot be reduced by that amount for + /// some reason, return `Err` and don't reduce it at all. If `precision` is `BestEffort`, then + /// reduce the balance of `who` by the most that is possible, up to `amount`. + /// + /// In either case, if `Ok` is returned then the inner is the amount by which is was reduced. + /// Minimum balance will be respected and thus the returned amount may be up to + /// `Self::minimum_balance() - 1` greater than `amount` in the case that the reduction caused + /// the account to be deleted. + fn decrease_balance( + who: &AccountId, + mut amount: Self::Balance, + precision: Precision, + preservation: Preservation, + force: Fortitude, + ) -> Result { + let old_balance = Self::balance(who); + let free = Self::reducible_balance(who, preservation, force); + if let BestEffort = precision { + amount = amount.min(free); + } + let new_balance = old_balance.checked_sub(&amount).ok_or(TokenError::FundsUnavailable)?; + if let Some(dust) = Self::write_balance(who, new_balance)? { + Self::handle_dust(Dust(dust)); + } + Ok(old_balance.saturating_sub(new_balance)) + } + + /// Increase the balance of `who` by `amount`. + /// + /// If it cannot be increased by that amount for some reason, return `Err` and don't increase + /// it at all. If Ok, return the imbalance. + /// Minimum balance will be respected and an error will be returned if + /// `amount < Self::minimum_balance()` when the account of `who` is zero. + fn increase_balance( + who: &AccountId, + amount: Self::Balance, + precision: Precision, + ) -> Result { + let old_balance = Self::balance(who); + let new_balance = if let BestEffort = precision { + old_balance.saturating_add(amount) + } else { + old_balance.checked_add(&amount).ok_or(ArithmeticError::Overflow)? + }; + if new_balance < Self::minimum_balance() { + // Attempt to increase from 0 to below minimum -> stays at zero. + if let BestEffort = precision { + Ok(Default::default()) + } else { + Err(TokenError::BelowMinimum.into()) + } + } else { + if new_balance == old_balance { + Ok(Default::default()) + } else { + if let Some(dust) = Self::write_balance(who, new_balance)? { + Self::handle_dust(Dust(dust)); + } + Ok(new_balance.saturating_sub(old_balance)) + } + } + } + + /// Reduce the active issuance by some amount. + fn deactivate(_: Self::Balance) {} + + /// Increase the active issuance by some amount, up to the outstanding amount reduced. + fn reactivate(_: Self::Balance) {} +} + +/// Trait for providing a basic fungible asset. +pub trait Mutate: Inspect + Unbalanced { + /// Increase the balance of `who` by exactly `amount`, minting new tokens. If that isn't + /// possible then an `Err` is returned and nothing is changed. + fn mint_into(who: &AccountId, amount: Self::Balance) -> Result { + Self::total_issuance().checked_add(&amount).ok_or(ArithmeticError::Overflow)?; + let actual = Self::increase_balance(who, amount, Exact)?; + Self::set_total_issuance(Self::total_issuance().saturating_add(actual)); + Self::done_mint_into(who, amount); + Ok(actual) + } + + /// Decrease the balance of `who` by at least `amount`, possibly slightly more in the case of + /// minimum-balance requirements, burning the tokens. If that isn't possible then an `Err` is + /// returned and nothing is changed. If successful, the amount of tokens reduced is returned. + fn burn_from( + who: &AccountId, + amount: Self::Balance, + precision: Precision, + force: Fortitude, + ) -> Result { + let actual = Self::reducible_balance(who, Expendable, force).min(amount); + ensure!(actual == amount || precision == BestEffort, TokenError::FundsUnavailable); + Self::total_issuance().checked_sub(&actual).ok_or(ArithmeticError::Overflow)?; + let actual = Self::decrease_balance(who, actual, BestEffort, Expendable, force)?; + Self::set_total_issuance(Self::total_issuance().saturating_sub(actual)); + Self::done_burn_from(who, actual); + Ok(actual) + } + + /// Attempt to decrease the `asset` balance of `who` by `amount`. + /// + /// Equivalent to `burn_from`, except with an expectation that within the bounds of some + /// universal issuance, the total assets `suspend`ed and `resume`d will be equivalent. The + /// implementation may be configured such that the total assets suspended may never be less than + /// the total assets resumed (which is the invariant for an issuing system), or the reverse + /// (which the invariant in a non-issuing system). + /// + /// Because of this expectation, any metadata associated with the asset is expected to survive + /// the suspect-resume cycle. + fn shelve(who: &AccountId, amount: Self::Balance) -> Result { + let actual = Self::reducible_balance(who, Expendable, Polite).min(amount); + ensure!(actual == amount, TokenError::FundsUnavailable); + Self::total_issuance().checked_sub(&actual).ok_or(ArithmeticError::Overflow)?; + let actual = Self::decrease_balance(who, actual, BestEffort, Expendable, Polite)?; + Self::set_total_issuance(Self::total_issuance().saturating_sub(actual)); + Self::done_shelve(who, actual); + Ok(actual) + } + + /// Attempt to increase the `asset` balance of `who` by `amount`. + /// + /// Equivalent to `mint_into`, except with an expectation that within the bounds of some + /// universal issuance, the total assets `suspend`ed and `resume`d will be equivalent. The + /// implementation may be configured such that the total assets suspended may never be less than + /// the total assets resumed (which is the invariant for an issuing system), or the reverse + /// (which the invariant in a non-issuing system). + /// + /// Because of this expectation, any metadata associated with the asset is expected to survive + /// the suspect-resume cycle. + fn restore(who: &AccountId, amount: Self::Balance) -> Result { + Self::total_issuance().checked_add(&amount).ok_or(ArithmeticError::Overflow)?; + let actual = Self::increase_balance(who, amount, Exact)?; + Self::set_total_issuance(Self::total_issuance().saturating_add(actual)); + Self::done_restore(who, amount); + Ok(actual) + } + + /// Transfer funds from one account into another. + fn transfer( + source: &AccountId, + dest: &AccountId, + amount: Self::Balance, + preservation: Preservation, + ) -> Result { + let _extra = Self::can_withdraw(source, amount).into_result(preservation != Expendable)?; + Self::can_deposit(dest, amount, Extant).into_result()?; + Self::decrease_balance(source, amount, BestEffort, preservation, Polite)?; + // This should never fail as we checked `can_deposit` earlier. But we do a best-effort + // anyway. + let _ = Self::increase_balance(dest, amount, BestEffort); + Self::done_transfer(source, dest, amount); + Ok(amount) + } + + /// Simple infallible function to force an account to have a particular balance, good for use + /// in tests and benchmarks but not recommended for production code owing to the lack of + /// error reporting. + /// + /// Returns the new balance. + fn set_balance(who: &AccountId, amount: Self::Balance) -> Self::Balance { + let b = Self::balance(who); + if b > amount { + Self::burn_from(who, b - amount, BestEffort, Force).map(|d| amount.saturating_sub(d)) + } else { + Self::mint_into(who, amount - b).map(|d| amount.saturating_add(d)) + } + .unwrap_or(b) + } + + fn done_mint_into(_who: &AccountId, _amount: Self::Balance) {} + fn done_burn_from(_who: &AccountId, _amount: Self::Balance) {} + fn done_shelve(_who: &AccountId, _amount: Self::Balance) {} + fn done_restore(_who: &AccountId, _amount: Self::Balance) {} + fn done_transfer(_source: &AccountId, _dest: &AccountId, _amount: Self::Balance) {} +} + +/// Simple handler for an imbalance drop which increases the total issuance of the system by the +/// imbalance amount. Used for leftover debt. +pub struct IncreaseIssuance(PhantomData<(AccountId, U)>); +impl> HandleImbalanceDrop + for IncreaseIssuance +{ + fn handle(amount: U::Balance) { + U::set_total_issuance(U::total_issuance().saturating_add(amount)) + } +} + +/// Simple handler for an imbalance drop which decreases the total issuance of the system by the +/// imbalance amount. Used for leftover credit. +pub struct DecreaseIssuance(PhantomData<(AccountId, U)>); +impl> HandleImbalanceDrop + for DecreaseIssuance +{ + fn handle(amount: U::Balance) { + U::set_total_issuance(U::total_issuance().saturating_sub(amount)) + } +} + +/// A fungible token class where any creation and deletion of tokens is semi-explicit and where the +/// total supply is maintained automatically. +/// +/// This is auto-implemented when a token class has `Unbalanced` implemented. +pub trait Balanced: Inspect + Unbalanced { + /// The type for managing what happens when an instance of `Debt` is dropped without being used. + type OnDropDebt: HandleImbalanceDrop; + /// The type for managing what happens when an instance of `Credit` is dropped without being + /// used. + type OnDropCredit: HandleImbalanceDrop; + + /// Reduce the total issuance by `amount` and return the according imbalance. The imbalance will + /// typically be used to reduce an account by the same amount with e.g. `settle`. + /// + /// This is infallible, but doesn't guarantee that the entire `amount` is burnt, for example + /// in the case of underflow. + fn rescind(amount: Self::Balance) -> Debt { + let old = Self::total_issuance(); + let new = old.saturating_sub(amount); + Self::set_total_issuance(new); + let delta = old - new; + Self::done_rescind(delta); + Imbalance::::new(delta) + } + + /// Increase the total issuance by `amount` and return the according imbalance. The imbalance + /// will typically be used to increase an account by the same amount with e.g. + /// `resolve_into_existing` or `resolve_creating`. + /// + /// This is infallible, but doesn't guarantee that the entire `amount` is issued, for example + /// in the case of overflow. + fn issue(amount: Self::Balance) -> Credit { + let old = Self::total_issuance(); + let new = old.saturating_add(amount); + Self::set_total_issuance(new); + let delta = new - old; + Self::done_issue(delta); + Imbalance::::new(delta) + } + + /// Produce a pair of imbalances that cancel each other out exactly. + /// + /// This is just the same as burning and issuing the same amount and has no effect on the + /// total issuance. + fn pair(amount: Self::Balance) -> (Debt, Credit) { + (Self::rescind(amount), Self::issue(amount)) + } + + /// Mints `value` into the account of `who`, creating it as needed. + /// + /// If `precision` is `BestEffort` and `value` in full could not be minted (e.g. due to + /// overflow), then the maximum is minted, up to `value`. If `precision` is `Exact`, then + /// exactly `value` must be minted into the account of `who` or the operation will fail with an + /// `Err` and nothing will change. + /// + /// If the operation is successful, this will return `Ok` with a `Debt` of the total value + /// added to the account. + fn deposit( + who: &AccountId, + value: Self::Balance, + precision: Precision, + ) -> Result, DispatchError> { + let increase = Self::increase_balance(who, value, precision)?; + Self::done_deposit(who, increase); + Ok(Imbalance::::new(increase)) + } + + /// Removes `value` balance from `who` account if possible. + /// + /// If `precision` is `BestEffort` and `value` in full could not be removed (e.g. due to + /// underflow), then the maximum is removed, up to `value`. If `precision` is `Exact`, then + /// exactly `value` must be removed from the account of `who` or the operation will fail with an + /// `Err` and nothing will change. + /// + /// If the removal is needed but not possible, then it returns `Err` and nothing is changed. + /// If the account needed to be deleted, then slightly more than `value` may be removed from the + /// account owning since up to (but not including) minimum balance may also need to be removed. + /// + /// If the operation is successful, this will return `Ok` with a `Credit` of the total value + /// removed from the account. + fn withdraw( + who: &AccountId, + value: Self::Balance, + precision: Precision, + preservation: Preservation, + force: Fortitude, + ) -> Result, DispatchError> { + let decrease = Self::decrease_balance(who, value, precision, preservation, force)?; + Self::done_withdraw(who, decrease); + Ok(Imbalance::::new(decrease)) + } + + /// The balance of `who` is increased in order to counter `credit`. If the whole of `credit` + /// cannot be countered, then nothing is changed and the original `credit` is returned in an + /// `Err`. + /// + /// Please note: If `credit.peek()` is less than `Self::minimum_balance()`, then `who` must + /// already exist for this to succeed. + fn resolve( + who: &AccountId, + credit: Credit, + ) -> Result<(), Credit> { + let v = credit.peek(); + let debt = match Self::deposit(who, v, Exact) { + Err(_) => return Err(credit), + Ok(d) => d, + }; + let result = credit.offset(debt).try_drop(); + debug_assert!(result.is_ok(), "ok deposit return must be equal to credit value; qed"); + Ok(()) + } + + /// The balance of `who` is decreased in order to counter `debt`. If the whole of `debt` + /// cannot be countered, then nothing is changed and the original `debt` is returned in an + /// `Err`. + fn settle( + who: &AccountId, + debt: Debt, + preservation: Preservation, + ) -> Result, Debt> { + let amount = debt.peek(); + let credit = match Self::withdraw(who, amount, Exact, preservation, Polite) { + Err(_) => return Err(debt), + Ok(d) => d, + }; + + match credit.offset(debt) { + SameOrOther::None => Ok(Credit::::zero()), + SameOrOther::Same(dust) => Ok(dust), + SameOrOther::Other(rest) => { + debug_assert!(false, "ok withdraw return must be at least debt value; qed"); + Err(rest) + }, + } + } + + fn done_rescind(_amount: Self::Balance) {} + fn done_issue(_amount: Self::Balance) {} + fn done_deposit(_who: &AccountId, _amount: Self::Balance) {} + fn done_withdraw(_who: &AccountId, _amount: Self::Balance) {} +} diff --git a/frame/support/src/traits/tokens/fungibles.rs b/frame/support/src/traits/tokens/fungibles.rs deleted file mode 100644 index d146832f3..000000000 --- a/frame/support/src/traits/tokens/fungibles.rs +++ /dev/null @@ -1,332 +0,0 @@ -// This file is part of Substrate. - -// Copyright (C) Parity Technologies (UK) Ltd. -// SPDX-License-Identifier: Apache-2.0 - -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -//! The traits for sets of fungible tokens and any associated types. - -use super::{ - misc::{AssetId, Balance}, - *, -}; -use crate::dispatch::{DispatchError, DispatchResult}; -use sp_runtime::traits::Saturating; -use sp_std::vec::Vec; - -pub mod approvals; -mod balanced; -pub mod enumerable; -pub use enumerable::InspectEnumerable; -pub mod metadata; -pub use balanced::{Balanced, Unbalanced}; -mod imbalance; -pub use imbalance::{CreditOf, DebtOf, HandleImbalanceDrop, Imbalance}; -pub mod roles; - -/// Trait for providing balance-inspection access to a set of named fungible assets. -pub trait Inspect { - /// Means of identifying one asset class from another. - type AssetId: AssetId; - - /// Scalar type for representing balance of an account. - type Balance: Balance; - - /// The total amount of issuance in the system. - fn total_issuance(asset: Self::AssetId) -> Self::Balance; - - /// The total amount of issuance in the system excluding those which are controlled by the - /// system. - fn active_issuance(asset: Self::AssetId) -> Self::Balance { - Self::total_issuance(asset) - } - - /// The minimum balance any single account may have. - fn minimum_balance(asset: Self::AssetId) -> Self::Balance; - - /// Get the `asset` balance of `who`. - fn balance(asset: Self::AssetId, who: &AccountId) -> Self::Balance; - - /// Get the maximum amount of `asset` that `who` can withdraw/transfer successfully. - fn reducible_balance(asset: Self::AssetId, who: &AccountId, keep_alive: bool) -> Self::Balance; - - /// Returns `true` if the `asset` balance of `who` may be increased by `amount`. - /// - /// - `asset`: The asset that should be deposited. - /// - `who`: The account of which the balance should be increased by `amount`. - /// - `amount`: How much should the balance be increased? - /// - `mint`: Will `amount` be minted to deposit it into `account`? - fn can_deposit( - asset: Self::AssetId, - who: &AccountId, - amount: Self::Balance, - mint: bool, - ) -> DepositConsequence; - - /// Returns `Failed` if the `asset` balance of `who` may not be decreased by `amount`, otherwise - /// the consequence. - fn can_withdraw( - asset: Self::AssetId, - who: &AccountId, - amount: Self::Balance, - ) -> WithdrawConsequence; - - /// Returns `true` if an `asset` exists. - fn asset_exists(asset: Self::AssetId) -> bool; -} - -/// Trait for reading metadata from a fungible asset. -pub trait InspectMetadata: Inspect { - /// Return the name of an asset. - fn name(asset: &Self::AssetId) -> Vec; - - /// Return the symbol of an asset. - fn symbol(asset: &Self::AssetId) -> Vec; - - /// Return the decimals of an asset. - fn decimals(asset: &Self::AssetId) -> u8; -} - -/// Trait for providing a set of named fungible assets which can be created and destroyed. -pub trait Mutate: Inspect { - /// Attempt to increase the `asset` balance of `who` by `amount`. - /// - /// If not possible then don't do anything. Possible reasons for failure include: - /// - Minimum balance not met. - /// - Account cannot be created (e.g. because there is no provider reference and/or the asset - /// isn't considered worth anything). - /// - /// Since this is an operation which should be possible to take alone, if successful it will - /// increase the overall supply of the underlying token. - fn mint_into(asset: Self::AssetId, who: &AccountId, amount: Self::Balance) -> DispatchResult; - - /// Attempt to reduce the `asset` balance of `who` by `amount`. - /// - /// If not possible then don't do anything. Possible reasons for failure include: - /// - Less funds in the account than `amount` - /// - Liquidity requirements (locks, reservations) prevent the funds from being removed - /// - Operation would require destroying the account and it is required to stay alive (e.g. - /// because it's providing a needed provider reference). - /// - /// Since this is an operation which should be possible to take alone, if successful it will - /// reduce the overall supply of the underlying token. - /// - /// Due to minimum balance requirements, it's possible that the amount withdrawn could be up to - /// `Self::minimum_balance() - 1` more than the `amount`. The total amount withdrawn is returned - /// in an `Ok` result. This may be safely ignored if you don't mind the overall supply reducing. - fn burn_from( - asset: Self::AssetId, - who: &AccountId, - amount: Self::Balance, - ) -> Result; - - /// Attempt to reduce the `asset` balance of `who` by as much as possible up to `amount`, and - /// possibly slightly more due to minimum_balance requirements. If no decrease is possible then - /// an `Err` is returned and nothing is changed. If successful, the amount of tokens reduced is - /// returned. - /// - /// The default implementation just uses `withdraw` along with `reducible_balance` to ensure - /// that is doesn't fail. - fn slash( - asset: Self::AssetId, - who: &AccountId, - amount: Self::Balance, - ) -> Result { - Self::burn_from(asset, who, Self::reducible_balance(asset, who, false).min(amount)) - } - - /// Transfer funds from one account into another. The default implementation uses `mint_into` - /// and `burn_from` and may generate unwanted events. - fn teleport( - asset: Self::AssetId, - source: &AccountId, - dest: &AccountId, - amount: Self::Balance, - ) -> Result { - let extra = Self::can_withdraw(asset, &source, amount).into_result()?; - // As we first burn and then mint, we don't need to check if `mint` fits into the supply. - // If we can withdraw/burn it, we can also mint it again. - Self::can_deposit(asset, dest, amount.saturating_add(extra), false).into_result()?; - let actual = Self::burn_from(asset, source, amount)?; - debug_assert!( - actual == amount.saturating_add(extra), - "can_withdraw must agree with withdraw; qed" - ); - match Self::mint_into(asset, dest, actual) { - Ok(_) => Ok(actual), - Err(err) => { - debug_assert!(false, "can_deposit returned true previously; qed"); - // attempt to return the funds back to source - let revert = Self::mint_into(asset, source, actual); - debug_assert!(revert.is_ok(), "withdrew funds previously; qed"); - Err(err) - }, - } - } -} - -/// Trait for providing a set of named fungible assets which can only be transferred. -pub trait Transfer: Inspect { - /// Transfer funds from one account into another. - fn transfer( - asset: Self::AssetId, - source: &AccountId, - dest: &AccountId, - amount: Self::Balance, - keep_alive: bool, - ) -> Result; - - /// Reduce the active issuance by some amount. - fn deactivate(_: Self::AssetId, _: Self::Balance) {} - - /// Increase the active issuance by some amount, up to the outstanding amount reduced. - fn reactivate(_: Self::AssetId, _: Self::Balance) {} -} - -/// Trait for inspecting a set of named fungible assets which can be placed on hold. -pub trait InspectHold: Inspect { - /// Amount of funds held in hold. - fn balance_on_hold(asset: Self::AssetId, who: &AccountId) -> Self::Balance; - - /// Check to see if some `amount` of `asset` may be held on the account of `who`. - fn can_hold(asset: Self::AssetId, who: &AccountId, amount: Self::Balance) -> bool; -} - -/// Trait for mutating a set of named fungible assets which can be placed on hold. -pub trait MutateHold: InspectHold + Transfer { - /// Hold some funds in an account. - fn hold(asset: Self::AssetId, who: &AccountId, amount: Self::Balance) -> DispatchResult; - - /// Release some funds in an account from being on hold. - /// - /// If `best_effort` is `true`, then the amount actually released and returned as the inner - /// value of `Ok` may be smaller than the `amount` passed. - fn release( - asset: Self::AssetId, - who: &AccountId, - amount: Self::Balance, - best_effort: bool, - ) -> Result; - - /// Transfer held funds into a destination account. - /// - /// If `on_hold` is `true`, then the destination account must already exist and the assets - /// transferred will still be on hold in the destination account. If not, then the destination - /// account need not already exist, but must be creatable. - /// - /// If `best_effort` is `true`, then an amount less than `amount` may be transferred without - /// error. - /// - /// The actual amount transferred is returned, or `Err` in the case of error and nothing is - /// changed. - fn transfer_held( - asset: Self::AssetId, - source: &AccountId, - dest: &AccountId, - amount: Self::Balance, - best_effort: bool, - on_hold: bool, - ) -> Result; -} - -/// Trait for mutating one of several types of fungible assets which can be held. -pub trait BalancedHold: Balanced + MutateHold { - /// Release and slash some funds in an account. - /// - /// The resulting imbalance is the first item of the tuple returned. - /// - /// As much funds up to `amount` will be deducted as possible. If this is less than `amount`, - /// then a non-zero second item will be returned. - fn slash_held( - asset: Self::AssetId, - who: &AccountId, - amount: Self::Balance, - ) -> (CreditOf, Self::Balance); -} - -impl + MutateHold> BalancedHold for T { - fn slash_held( - asset: Self::AssetId, - who: &AccountId, - amount: Self::Balance, - ) -> (CreditOf, Self::Balance) { - let actual = match Self::release(asset, who, amount, true) { - Ok(x) => x, - Err(_) => return (Imbalance::zero(asset), amount), - }; - >::slash(asset, who, actual) - } -} - -/// Trait for providing the ability to create new fungible assets. -pub trait Create: Inspect { - /// Create a new fungible asset. - fn create( - id: Self::AssetId, - admin: AccountId, - is_sufficient: bool, - min_balance: Self::Balance, - ) -> DispatchResult; -} - -/// Trait for providing the ability to destroy existing fungible assets. -pub trait Destroy: Inspect { - /// Start the destruction an existing fungible asset. - /// * `id`: The `AssetId` to be destroyed. successfully. - /// * `maybe_check_owner`: An optional account id that can be used to authorize the destroy - /// command. If not provided, no authorization checks will be performed before destroying - /// asset. - fn start_destroy(id: Self::AssetId, maybe_check_owner: Option) -> DispatchResult; - - /// Destroy all accounts associated with a given asset. - /// `destroy_accounts` should only be called after `start_destroy` has been called, and the - /// asset is in a `Destroying` state - /// - /// * `id`: The identifier of the asset to be destroyed. This must identify an existing asset. - /// * `max_items`: The maximum number of accounts to be destroyed for a given call of the - /// function. This value should be small enough to allow the operation fit into a logical - /// block. - /// - /// Response: - /// * u32: Total number of approvals which were actually destroyed - /// - /// Due to weight restrictions, this function may need to be called multiple - /// times to fully destroy all approvals. It will destroy `max_items` approvals at a - /// time. - fn destroy_accounts(id: Self::AssetId, max_items: u32) -> Result; - /// Destroy all approvals associated with a given asset up to the `max_items` - /// `destroy_approvals` should only be called after `start_destroy` has been called, and the - /// asset is in a `Destroying` state - /// - /// * `id`: The identifier of the asset to be destroyed. This must identify an existing asset. - /// * `max_items`: The maximum number of accounts to be destroyed for a given call of the - /// function. This value should be small enough to allow the operation fit into a logical - /// block. - /// - /// Response: - /// * u32: Total number of approvals which were actually destroyed - /// - /// Due to weight restrictions, this function may need to be called multiple - /// times to fully destroy all approvals. It will destroy `max_items` approvals at a - /// time. - fn destroy_approvals(id: Self::AssetId, max_items: u32) -> Result; - - /// Complete destroying asset and unreserve currency. - /// `finish_destroy` should only be called after `start_destroy` has been called, and the - /// asset is in a `Destroying` state. All accounts or approvals should be destroyed before - /// hand. - /// - /// * `id`: The identifier of the asset to be destroyed. This must identify an existing asset. - fn finish_destroy(id: Self::AssetId) -> DispatchResult; -} diff --git a/frame/support/src/traits/tokens/fungibles/balanced.rs b/frame/support/src/traits/tokens/fungibles/balanced.rs deleted file mode 100644 index 598ba250e..000000000 --- a/frame/support/src/traits/tokens/fungibles/balanced.rs +++ /dev/null @@ -1,394 +0,0 @@ -// This file is part of Substrate. - -// Copyright (C) Parity Technologies (UK) Ltd. -// SPDX-License-Identifier: Apache-2.0 - -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -//! The trait and associated types for sets of fungible tokens that manage total issuance without -//! requiring atomic balanced operations. - -use super::*; -use crate::{ - dispatch::{DispatchError, DispatchResult}, - traits::misc::{SameOrOther, TryDrop}, -}; -use sp_arithmetic::traits::Saturating; -use sp_runtime::{ - traits::{CheckedAdd, Zero}, - ArithmeticError, TokenError, -}; -use sp_std::marker::PhantomData; - -/// A fungible token class where any creation and deletion of tokens is semi-explicit and where the -/// total supply is maintained automatically. -/// -/// This is auto-implemented when a token class has `Unbalanced` implemented. -pub trait Balanced: Inspect { - /// The type for managing what happens when an instance of `Debt` is dropped without being used. - type OnDropDebt: HandleImbalanceDrop; - /// The type for managing what happens when an instance of `Credit` is dropped without being - /// used. - type OnDropCredit: HandleImbalanceDrop; - - /// Reduce the total issuance by `amount` and return the according imbalance. The imbalance will - /// typically be used to reduce an account by the same amount with e.g. `settle`. - /// - /// This is infallible, but doesn't guarantee that the entire `amount` is burnt, for example - /// in the case of underflow. - fn rescind(asset: Self::AssetId, amount: Self::Balance) -> DebtOf; - - /// Increase the total issuance by `amount` and return the according imbalance. The imbalance - /// will typically be used to increase an account by the same amount with e.g. - /// `resolve_into_existing` or `resolve_creating`. - /// - /// This is infallible, but doesn't guarantee that the entire `amount` is issued, for example - /// in the case of overflow. - fn issue(asset: Self::AssetId, amount: Self::Balance) -> CreditOf; - - /// Produce a pair of imbalances that cancel each other out exactly. - /// - /// This is just the same as burning and issuing the same amount and has no effect on the - /// total issuance. - fn pair( - asset: Self::AssetId, - amount: Self::Balance, - ) -> (DebtOf, CreditOf) { - (Self::rescind(asset, amount), Self::issue(asset, amount)) - } - - /// Deducts up to `value` from the combined balance of `who`, preferring to deduct from the - /// free balance. This function cannot fail. - /// - /// The resulting imbalance is the first item of the tuple returned. - /// - /// As much funds up to `value` will be deducted as possible. If this is less than `value`, - /// then a non-zero second item will be returned. - fn slash( - asset: Self::AssetId, - who: &AccountId, - amount: Self::Balance, - ) -> (CreditOf, Self::Balance); - - /// Mints exactly `value` into the `asset` account of `who`. - /// - /// If `who` doesn't exist, nothing is done and an `Err` returned. This could happen because it - /// the account doesn't yet exist and it isn't possible to create it under the current - /// circumstances and with `value` in it. - fn deposit( - asset: Self::AssetId, - who: &AccountId, - value: Self::Balance, - ) -> Result, DispatchError>; - - /// Removes `value` free `asset` balance from `who` account if possible. - /// - /// If the removal is not possible, then it returns `Err` and nothing is changed. - /// - /// If the operation is successful, this will return `Ok` with a `NegativeImbalance` whose value - /// is no less than `value`. It may be more in the case that removing it reduced it below - /// `Self::minimum_balance()`. - fn withdraw( - asset: Self::AssetId, - who: &AccountId, - value: Self::Balance, - // TODO: liveness: ExistenceRequirement, - ) -> Result, DispatchError>; - - /// The balance of `who` is increased in order to counter `credit`. If the whole of `credit` - /// cannot be countered, then nothing is changed and the original `credit` is returned in an - /// `Err`. - /// - /// Please note: If `credit.peek()` is less than `Self::minimum_balance()`, then `who` must - /// already exist for this to succeed. - fn resolve( - who: &AccountId, - credit: CreditOf, - ) -> Result<(), CreditOf> { - let v = credit.peek(); - let debt = match Self::deposit(credit.asset(), who, v) { - Err(_) => return Err(credit), - Ok(d) => d, - }; - if let Ok(result) = credit.offset(debt) { - let result = result.try_drop(); - debug_assert!(result.is_ok(), "ok deposit return must be equal to credit value; qed"); - } else { - debug_assert!(false, "debt.asset is credit.asset; qed"); - } - Ok(()) - } - - /// The balance of `who` is decreased in order to counter `debt`. If the whole of `debt` - /// cannot be countered, then nothing is changed and the original `debt` is returned in an - /// `Err`. - fn settle( - who: &AccountId, - debt: DebtOf, - // TODO: liveness: ExistenceRequirement, - ) -> Result, DebtOf> { - let amount = debt.peek(); - let asset = debt.asset(); - let credit = match Self::withdraw(asset, who, amount) { - Err(_) => return Err(debt), - Ok(d) => d, - }; - match credit.offset(debt) { - Ok(SameOrOther::None) => Ok(CreditOf::::zero(asset)), - Ok(SameOrOther::Same(dust)) => Ok(dust), - Ok(SameOrOther::Other(rest)) => { - debug_assert!(false, "ok withdraw return must be at least debt value; qed"); - Err(rest) - }, - Err(_) => { - debug_assert!(false, "debt.asset is credit.asset; qed"); - Ok(CreditOf::::zero(asset)) - }, - } - } -} - -/// A fungible token class where the balance can be set arbitrarily. -/// -/// **WARNING** -/// Do not use this directly unless you want trouble, since it allows you to alter account balances -/// without keeping the issuance up to date. It has no safeguards against accidentally creating -/// token imbalances in your system leading to accidental inflation or deflation. It's really just -/// for the underlying datatype to implement so the user gets the much safer `Balanced` trait to -/// use. -pub trait Unbalanced: Inspect { - /// Set the `asset` balance of `who` to `amount`. If this cannot be done for some reason (e.g. - /// because the account cannot be created or an overflow) then an `Err` is returned. - fn set_balance(asset: Self::AssetId, who: &AccountId, amount: Self::Balance) -> DispatchResult; - - /// Set the total issuance of `asset` to `amount`. - fn set_total_issuance(asset: Self::AssetId, amount: Self::Balance); - - /// Reduce the `asset` balance of `who` by `amount`. If it cannot be reduced by that amount for - /// some reason, return `Err` and don't reduce it at all. If Ok, return the imbalance. - /// - /// Minimum balance will be respected and the returned imbalance may be up to - /// `Self::minimum_balance() - 1` greater than `amount`. - fn decrease_balance( - asset: Self::AssetId, - who: &AccountId, - amount: Self::Balance, - ) -> Result { - let old_balance = Self::balance(asset, who); - let (mut new_balance, mut amount) = if Self::reducible_balance(asset, who, false) < amount { - return Err(TokenError::NoFunds.into()) - } else { - (old_balance - amount, amount) - }; - if new_balance < Self::minimum_balance(asset) { - amount = amount.saturating_add(new_balance); - new_balance = Zero::zero(); - } - // Defensive only - this should not fail now. - Self::set_balance(asset, who, new_balance)?; - Ok(amount) - } - - /// Reduce the `asset` balance of `who` by the most that is possible, up to `amount`. - /// - /// Minimum balance will be respected and the returned imbalance may be up to - /// `Self::minimum_balance() - 1` greater than `amount`. - /// - /// Return the imbalance by which the account was reduced. - fn decrease_balance_at_most( - asset: Self::AssetId, - who: &AccountId, - amount: Self::Balance, - ) -> Self::Balance { - let old_balance = Self::balance(asset, who); - let old_free_balance = Self::reducible_balance(asset, who, false); - let (mut new_balance, mut amount) = if old_free_balance < amount { - (old_balance.saturating_sub(old_free_balance), old_free_balance) - } else { - (old_balance - amount, amount) - }; - let minimum_balance = Self::minimum_balance(asset); - if new_balance < minimum_balance { - amount = amount.saturating_add(new_balance); - new_balance = Zero::zero(); - } - let mut r = Self::set_balance(asset, who, new_balance); - if r.is_err() { - // Some error, probably because we tried to destroy an account which cannot be - // destroyed. - if new_balance.is_zero() && amount >= minimum_balance { - new_balance = minimum_balance; - amount -= minimum_balance; - r = Self::set_balance(asset, who, new_balance); - } - if r.is_err() { - // Still an error. Apparently it's not possible to reduce at all. - amount = Zero::zero(); - } - } - amount - } - - /// Increase the `asset` balance of `who` by `amount`. If it cannot be increased by that amount - /// for some reason, return `Err` and don't increase it at all. If Ok, return the imbalance. - /// - /// Minimum balance will be respected and an error will be returned if - /// `amount < Self::minimum_balance()` when the account of `who` is zero. - fn increase_balance( - asset: Self::AssetId, - who: &AccountId, - amount: Self::Balance, - ) -> Result { - let old_balance = Self::balance(asset, who); - let new_balance = old_balance.checked_add(&amount).ok_or(ArithmeticError::Overflow)?; - if new_balance < Self::minimum_balance(asset) { - return Err(TokenError::BelowMinimum.into()) - } - if old_balance != new_balance { - Self::set_balance(asset, who, new_balance)?; - } - Ok(amount) - } - - /// Increase the `asset` balance of `who` by the most that is possible, up to `amount`. - /// - /// Minimum balance will be respected and the returned imbalance will be zero in the case that - /// `amount < Self::minimum_balance()`. - /// - /// Return the imbalance by which the account was increased. - fn increase_balance_at_most( - asset: Self::AssetId, - who: &AccountId, - amount: Self::Balance, - ) -> Self::Balance { - let old_balance = Self::balance(asset, who); - let mut new_balance = old_balance.saturating_add(amount); - let mut amount = new_balance - old_balance; - if new_balance < Self::minimum_balance(asset) { - new_balance = Zero::zero(); - amount = Zero::zero(); - } - if old_balance == new_balance || Self::set_balance(asset, who, new_balance).is_ok() { - amount - } else { - Zero::zero() - } - } -} - -/// Simple handler for an imbalance drop which increases the total issuance of the system by the -/// imbalance amount. Used for leftover debt. -pub struct IncreaseIssuance(PhantomData<(AccountId, U)>); -impl> HandleImbalanceDrop - for IncreaseIssuance -{ - fn handle(asset: U::AssetId, amount: U::Balance) { - U::set_total_issuance(asset, U::total_issuance(asset).saturating_add(amount)) - } -} - -/// Simple handler for an imbalance drop which decreases the total issuance of the system by the -/// imbalance amount. Used for leftover credit. -pub struct DecreaseIssuance(PhantomData<(AccountId, U)>); -impl> HandleImbalanceDrop - for DecreaseIssuance -{ - fn handle(asset: U::AssetId, amount: U::Balance) { - U::set_total_issuance(asset, U::total_issuance(asset).saturating_sub(amount)) - } -} - -/// An imbalance type which uses `DecreaseIssuance` to deal with anything `Drop`ed. -/// -/// Basically means that funds in someone's account have been removed and not yet placed anywhere -/// else. If it gets dropped, then those funds will be assumed to be "burned" and the total supply -/// will be accordingly decreased to ensure it equals the sum of the balances of all accounts. -type Credit = Imbalance< - >::AssetId, - >::Balance, - DecreaseIssuance, - IncreaseIssuance, ->; - -/// An imbalance type which uses `IncreaseIssuance` to deal with anything `Drop`ed. -/// -/// Basically means that there are funds in someone's account whose origin is as yet unaccounted -/// for. If it gets dropped, then those funds will be assumed to be "minted" and the total supply -/// will be accordingly increased to ensure it equals the sum of the balances of all accounts. -type Debt = Imbalance< - >::AssetId, - >::Balance, - IncreaseIssuance, - DecreaseIssuance, ->; - -/// Create some `Credit` item. Only for internal use. -fn credit>( - asset: U::AssetId, - amount: U::Balance, -) -> Credit { - Imbalance::new(asset, amount) -} - -/// Create some `Debt` item. Only for internal use. -fn debt>( - asset: U::AssetId, - amount: U::Balance, -) -> Debt { - Imbalance::new(asset, amount) -} - -impl> Balanced for U { - type OnDropCredit = DecreaseIssuance; - type OnDropDebt = IncreaseIssuance; - fn rescind(asset: Self::AssetId, amount: Self::Balance) -> Debt { - U::set_total_issuance(asset, U::total_issuance(asset).saturating_sub(amount)); - debt(asset, amount) - } - fn issue(asset: Self::AssetId, amount: Self::Balance) -> Credit { - U::set_total_issuance(asset, U::total_issuance(asset).saturating_add(amount)); - credit(asset, amount) - } - fn slash( - asset: Self::AssetId, - who: &AccountId, - amount: Self::Balance, - ) -> (Credit, Self::Balance) { - let slashed = U::decrease_balance_at_most(asset, who, amount); - // `slashed` could be less than, greater than or equal to `amount`. - // If slashed == amount, it means the account had at least amount in it and it could all be - // removed without a problem. - // If slashed > amount, it means the account had more than amount in it, but not enough more - // to push it over minimum_balance. - // If slashed < amount, it means the account didn't have enough in it to be reduced by - // `amount` without being destroyed. - (credit(asset, slashed), amount.saturating_sub(slashed)) - } - fn deposit( - asset: Self::AssetId, - who: &AccountId, - amount: Self::Balance, - ) -> Result, DispatchError> { - let increase = U::increase_balance(asset, who, amount)?; - Ok(debt(asset, increase)) - } - fn withdraw( - asset: Self::AssetId, - who: &AccountId, - amount: Self::Balance, - // TODO: liveness: ExistenceRequirement, - ) -> Result, DispatchError> { - let decrease = U::decrease_balance(asset, who, amount)?; - Ok(credit(asset, decrease)) - } -} diff --git a/frame/support/src/traits/tokens/fungibles/enumerable.rs b/frame/support/src/traits/tokens/fungibles/enumerable.rs index 5d7266d9f..08bb784a7 100644 --- a/frame/support/src/traits/tokens/fungibles/enumerable.rs +++ b/frame/support/src/traits/tokens/fungibles/enumerable.rs @@ -15,10 +15,8 @@ // See the License for the specific language governing permissions and // limitations under the License. -use crate::traits::fungibles::Inspect; - /// Interface for enumerating assets in existence or owned by a given account. -pub trait InspectEnumerable: Inspect { +pub trait Inspect: super::Inspect { type AssetsIterator; /// Returns an iterator of the collections in existence. diff --git a/frame/support/src/traits/tokens/fungibles/freeze.rs b/frame/support/src/traits/tokens/fungibles/freeze.rs new file mode 100644 index 000000000..08549c2d4 --- /dev/null +++ b/frame/support/src/traits/tokens/fungibles/freeze.rs @@ -0,0 +1,78 @@ +// This file is part of Substrate. + +// Copyright (C) 2019-2022 Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +//! The traits for putting freezes within a single fungible token class. + +use scale_info::TypeInfo; +use sp_runtime::DispatchResult; + +/// Trait for inspecting a fungible asset which can be frozen. Freezing is essentially setting a +/// minimum balance below which the total balance (inclusive of any funds placed on hold) may not +/// be normally allowed to drop. Generally, freezers will provide an "update" function such that +/// if the total balance does drop below the limit, then the freezer can update their housekeeping +/// accordingly. +pub trait Inspect: super::Inspect { + /// An identifier for a freeze. + type Id: codec::Encode + TypeInfo + 'static; + + /// Amount of funds held in reserve by `who` for the given `id`. + fn balance_frozen(asset: Self::AssetId, id: &Self::Id, who: &AccountId) -> Self::Balance; + + /// The amount of the balance which can become frozen. Defaults to `total_balance()`. + fn balance_freezable(asset: Self::AssetId, who: &AccountId) -> Self::Balance { + Self::total_balance(asset, who) + } + + /// Returns `true` if it's possible to introduce a freeze for the given `id` onto the + /// account of `who`. This will be true as long as the implementor supports as many + /// concurrent freeze locks as there are possible values of `id`. + fn can_freeze(asset: Self::AssetId, id: &Self::Id, who: &AccountId) -> bool; +} + +/// Trait for introducing, altering and removing locks to freeze an account's funds so they never +/// go below a set minimum. +pub trait Mutate: Inspect { + /// Prevent actions which would reduce the balance of the account of `who` below the given + /// `amount` and identify this restriction though the given `id`. Unlike `extend_freeze`, any + /// outstanding freeze in place for `who` under the `id` are dropped. + /// + /// If `amount` is zero, it is equivalent to using `thaw`. + /// + /// Note that `amount` can be greater than the total balance, if desired. + fn set_freeze( + asset: Self::AssetId, + id: &Self::Id, + who: &AccountId, + amount: Self::Balance, + ) -> DispatchResult; + + /// Prevent the balance of the account of `who` from being reduced below the given `amount` and + /// identify this restriction though the given `id`. Unlike `set_freeze`, this does not + /// counteract any pre-existing freezes in place for `who` under the `id`. Also unlike + /// `set_freeze`, in the case that `amount` is zero, this is no-op and never fails. + /// + /// Note that more funds can be locked than the total balance, if desired. + fn extend_freeze( + asset: Self::AssetId, + id: &Self::Id, + who: &AccountId, + amount: Self::Balance, + ) -> DispatchResult; + + /// Remove an existing lock. + fn thaw(asset: Self::AssetId, id: &Self::Id, who: &AccountId) -> DispatchResult; +} diff --git a/frame/support/src/traits/tokens/fungibles/hold.rs b/frame/support/src/traits/tokens/fungibles/hold.rs new file mode 100644 index 000000000..68580ebff --- /dev/null +++ b/frame/support/src/traits/tokens/fungibles/hold.rs @@ -0,0 +1,457 @@ +// This file is part of Substrate. + +// Copyright (C) 2019-2022 Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +//! The traits for putting holds within a single fungible token class. + +use crate::{ + ensure, + traits::tokens::{ + DepositConsequence::Success, + Fortitude::{self, Force}, + Precision::{self, BestEffort, Exact}, + Preservation::{self, Protect}, + Provenance::Extant, + Restriction::{self, Free, OnHold}, + }, +}; +use scale_info::TypeInfo; +use sp_arithmetic::{ + traits::{CheckedAdd, CheckedSub, Zero}, + ArithmeticError, +}; +use sp_runtime::{DispatchError, DispatchResult, Saturating, TokenError}; + +use super::*; + +/// Trait for inspecting a fungible asset whose accounts support partitioning and slashing. +pub trait Inspect: super::Inspect { + /// An identifier for a hold. Used for disambiguating different holds so that + /// they can be individually replaced or removed and funds from one hold don't accidentally + /// become unreserved or slashed for another. + type Reason: codec::Encode + TypeInfo + 'static; + + /// Amount of funds on hold (for all hold reasons) of `who`. + fn total_balance_on_hold(asset: Self::AssetId, who: &AccountId) -> Self::Balance; + + /// Get the maximum amount that the `total_balance_on_hold` of `who` can be reduced successfully + /// based on whether we are willing to force the reduction and potentially go below user-level + /// restrictions on the minimum amount of the account. Note: This cannot bring the account into + /// an inconsistent state with regards any required existential deposit. + /// + /// Always less than `total_balance_on_hold()`. + fn reducible_total_balance_on_hold( + asset: Self::AssetId, + who: &AccountId, + force: Fortitude, + ) -> Self::Balance; + + /// Amount of funds on hold (for the given reason) of `who`. + fn balance_on_hold( + asset: Self::AssetId, + reason: &Self::Reason, + who: &AccountId, + ) -> Self::Balance; + + /// Returns `true` if it's possible to place (additional) funds under a hold of a given + /// `reason`. This may fail if the account has exhausted a limited number of concurrent + /// holds or if it cannot be made to exist (e.g. there is no provider reference). + /// + /// NOTE: This does not take into account changes which could be made to the account of `who` + /// (such as removing a provider reference) after this call is made. Any usage of this should + /// therefore ensure the account is already in the appropriate state prior to calling it. + fn hold_available(asset: Self::AssetId, reason: &Self::Reason, who: &AccountId) -> bool; + + /// Check to see if some `amount` of funds of `who` may be placed on hold with the given + /// `reason`. Reasons why this may not be true: + /// + /// - The implementor supports only a limited number of concurrent holds on an account which is + /// the possible values of `reason`; + /// - The total balance of the account is less than `amount`; + /// - Removing `amount` from the total balance would kill the account and remove the only + /// provider reference. + /// + /// Note: we pass `Fortitude::Force` as the last argument to `reducible_balance` since we assume + /// that if needed the balance can slashed. If we are using a simple non-forcing + /// reserve-transfer, then we really ought to check that we are not reducing the funds below the + /// freeze-limit (if any). + /// + /// NOTE: This does not take into account changes which could be made to the account of `who` + /// (such as removing a provider reference) after this call is made. Any usage of this should + /// therefore ensure the account is already in the appropriate state prior to calling it. + fn ensure_can_hold( + asset: Self::AssetId, + reason: &Self::Reason, + who: &AccountId, + amount: Self::Balance, + ) -> DispatchResult { + ensure!(Self::hold_available(asset, reason, who), TokenError::CannotCreateHold); + ensure!( + amount <= Self::reducible_balance(asset, who, Protect, Force), + TokenError::FundsUnavailable + ); + Ok(()) + } + + /// Check to see if some `amount` of funds of `who` may be placed on hold for the given + /// `reason`. Reasons why this may not be true: + /// + /// - The implementor supports only a limited number of concurrent holds on an account which is + /// the possible values of `reason`; + /// - The main balance of the account is less than `amount`; + /// - Removing `amount` from the main balance would kill the account and remove the only + /// provider reference. + /// + /// NOTE: This does not take into account changes which could be made to the account of `who` + /// (such as removing a provider reference) after this call is made. Any usage of this should + /// therefore ensure the account is already in the appropriate state prior to calling it. + fn can_hold( + asset: Self::AssetId, + reason: &Self::Reason, + who: &AccountId, + amount: Self::Balance, + ) -> bool { + Self::ensure_can_hold(asset, reason, who, amount).is_ok() + } +} + +/// A fungible, holdable token class where the balance on hold can be set arbitrarily. +/// +/// **WARNING** +/// Do not use this directly unless you want trouble, since it allows you to alter account balances +/// without keeping the issuance up to date. It has no safeguards against accidentally creating +/// token imbalances in your system leading to accidental imflation or deflation. It's really just +/// for the underlying datatype to implement so the user gets the much safer `Balanced` trait to +/// use. +pub trait Unbalanced: Inspect { + /// Forcefully set the balance on hold of `who` to `amount`. This is independent of any other + /// balances on hold or the main ("free") balance. + /// + /// If this call executes successfully, you can `assert_eq!(Self::balance_on_hold(), amount);`. + /// + /// This function does its best to force the balance change through, but will not break system + /// invariants such as any Existential Deposits needed or overflows/underflows. + /// If this cannot be done for some reason (e.g. because the account doesn't exist) then an + /// `Err` is returned. + // Implmentation note: This should increment the consumer refs if it moves total on hold from + // zero to non-zero and decrement in the opposite direction. + // + // Since this was not done in the previous logic, this will need either a migration or a + // state item which tracks whether the account is on the old logic or new. + fn set_balance_on_hold( + asset: Self::AssetId, + reason: &Self::Reason, + who: &AccountId, + amount: Self::Balance, + ) -> DispatchResult; + + /// Reduce the balance on hold of `who` by `amount`. + /// + /// If `precision` is `Precision::Exact` and it cannot be reduced by that amount for + /// some reason, return `Err` and don't reduce it at all. If `precision` is + /// `Precision::BestEffort`, then reduce the balance of `who` by the most that is possible, up + /// to `amount`. + /// + /// In either case, if `Ok` is returned then the inner is the amount by which is was reduced. + fn decrease_balance_on_hold( + asset: Self::AssetId, + reason: &Self::Reason, + who: &AccountId, + mut amount: Self::Balance, + precision: Precision, + ) -> Result { + let old_balance = Self::balance_on_hold(asset, reason, who); + if let BestEffort = precision { + amount = amount.min(old_balance); + } + let new_balance = old_balance.checked_sub(&amount).ok_or(TokenError::FundsUnavailable)?; + Self::set_balance_on_hold(asset, reason, who, new_balance)?; + Ok(amount) + } + + /// Increase the balance on hold of `who` by `amount`. + /// + /// If it cannot be increased by that amount for some reason, return `Err` and don't increase + /// it at all. If Ok, return the imbalance. + fn increase_balance_on_hold( + asset: Self::AssetId, + reason: &Self::Reason, + who: &AccountId, + amount: Self::Balance, + precision: Precision, + ) -> Result { + let old_balance = Self::balance_on_hold(asset, reason, who); + let new_balance = if let BestEffort = precision { + old_balance.saturating_add(amount) + } else { + old_balance.checked_add(&amount).ok_or(ArithmeticError::Overflow)? + }; + let amount = new_balance.saturating_sub(old_balance); + if !amount.is_zero() { + Self::set_balance_on_hold(asset, reason, who, new_balance)?; + } + Ok(amount) + } +} + +/// Trait for slashing a fungible asset which can be place on hold. +pub trait Balanced: super::Balanced + Unbalanced { + /// Reduce the balance of some funds on hold in an account. + /// + /// The resulting imbalance is the first item of the tuple returned. + /// + /// As much funds that are on hold up to `amount` will be deducted as possible. If this is less + /// than `amount`, then a non-zero second item will be returned. + fn slash( + asset: Self::AssetId, + reason: &Self::Reason, + who: &AccountId, + amount: Self::Balance, + ) -> (Credit, Self::Balance) { + let decrease = Self::decrease_balance_on_hold(asset, reason, who, amount, BestEffort) + .unwrap_or(Default::default()); + let credit = + Imbalance::::new( + asset, decrease, + ); + Self::done_slash(asset, reason, who, decrease); + (credit, amount.saturating_sub(decrease)) + } + + fn done_slash( + _asset: Self::AssetId, + _reason: &Self::Reason, + _who: &AccountId, + _amount: Self::Balance, + ) { + } +} + +/// Trait for mutating a fungible asset which can be placed on hold. +pub trait Mutate: + Inspect + super::Unbalanced + Unbalanced +{ + /// Hold some funds in an account. If a hold for `reason` is already in place, then this + /// will increase it. + fn hold( + asset: Self::AssetId, + reason: &Self::Reason, + who: &AccountId, + amount: Self::Balance, + ) -> DispatchResult { + // NOTE: This doesn't change the total balance of the account so there's no need to + // check liquidity. + + Self::ensure_can_hold(asset, reason, who, amount)?; + // Should be infallible now, but we proceed softly anyway. + Self::decrease_balance(asset, who, amount, Exact, Protect, Force)?; + Self::increase_balance_on_hold(asset, reason, who, amount, BestEffort)?; + Self::done_hold(asset, reason, who, amount); + Ok(()) + } + + /// Release up to `amount` held funds in an account. + /// + /// The actual amount released is returned with `Ok`. + /// + /// If `precision` is `BestEffort`, then the amount actually unreserved and returned as the + /// inner value of `Ok` may be smaller than the `amount` passed. + fn release( + asset: Self::AssetId, + reason: &Self::Reason, + who: &AccountId, + amount: Self::Balance, + precision: Precision, + ) -> Result { + // NOTE: This doesn't change the total balance of the account so there's no need to + // check liquidity. + + // We want to make sure we can deposit the amount in advance. If we can't then something is + // very wrong. + ensure!(Self::can_deposit(asset, who, amount, Extant) == Success, TokenError::CannotCreate); + // Get the amount we can actually take from the hold. This might be less than what we want + // if we're only doing a best-effort. + let amount = Self::decrease_balance_on_hold(asset, reason, who, amount, precision)?; + // Increase the main balance by what we took. We always do a best-effort here because we + // already checked that we can deposit before. + let actual = Self::increase_balance(asset, who, amount, BestEffort)?; + Self::done_release(asset, reason, who, actual); + Ok(actual) + } + + /// Attempt to decrease the balance of `who` which is held for the given `reason` by `amount`. + /// + /// If `precision` is true, then as much as possible is reduced, up to `amount`, and the + /// amount of tokens reduced is returned. Otherwise, if the total amount can be reduced, then it + /// is and the amount returned, and if not, then nothing changes and `Err` is returned. + /// + /// If `force` is `Force`, then locks/freezes will be ignored. This should only be used when + /// conducting slashing or other activity which materially disadvantages the account holder + /// since it could provide a means of circumventing freezes. + fn burn_held( + asset: Self::AssetId, + reason: &Self::Reason, + who: &AccountId, + mut amount: Self::Balance, + precision: Precision, + force: Fortitude, + ) -> Result { + // We must check total-balance requirements if `!force`. + let liquid = Self::reducible_total_balance_on_hold(asset, who, force); + if let BestEffort = precision { + amount = amount.min(liquid); + } else { + ensure!(amount <= liquid, TokenError::Frozen); + } + let amount = Self::decrease_balance_on_hold(asset, reason, who, amount, precision)?; + Self::set_total_issuance(asset, Self::total_issuance(asset).saturating_sub(amount)); + Self::done_burn_held(asset, reason, who, amount); + Ok(amount) + } + + /// Transfer held funds into a destination account. + /// + /// If `on_hold` is `true`, then the destination account must already exist and the assets + /// transferred will still be on hold in the destination account. If not, then the destination + /// account need not already exist, but must be creatable. + /// + /// If `precision` is `BestEffort`, then an amount less than `amount` may be transferred without + /// error. + /// + /// If `force` is `Force`, then other fund-locking mechanisms may be disregarded. It should be + /// left as `Regular` in most circumstances, but when you want the same power as a `slash`, it + /// may be `Force`. + /// + /// The actual amount transferred is returned, or `Err` in the case of error and nothing is + /// changed. + fn transfer_on_hold( + asset: Self::AssetId, + reason: &Self::Reason, + source: &AccountId, + dest: &AccountId, + mut amount: Self::Balance, + precision: Precision, + mode: Restriction, + force: Fortitude, + ) -> Result { + // We must check total-balance requirements if `!force`. + let have = Self::balance_on_hold(asset, reason, source); + let liquid = Self::reducible_total_balance_on_hold(asset, source, force); + if let BestEffort = precision { + amount = amount.min(liquid).min(have); + } else { + ensure!(amount <= liquid, TokenError::Frozen); + ensure!(amount <= have, TokenError::FundsUnavailable); + } + + // We want to make sure we can deposit the amount in advance. If we can't then something is + // very wrong. + ensure!( + Self::can_deposit(asset, dest, amount, Extant) == Success, + TokenError::CannotCreate + ); + ensure!( + mode == Free || Self::hold_available(asset, reason, dest), + TokenError::CannotCreateHold + ); + + let amount = Self::decrease_balance_on_hold(asset, reason, source, amount, precision)?; + let actual = if mode == OnHold { + Self::increase_balance_on_hold(asset, reason, dest, amount, precision)? + } else { + Self::increase_balance(asset, dest, amount, precision)? + }; + Self::done_transfer_on_hold(asset, reason, source, dest, actual); + Ok(actual) + } + + /// Transfer some `amount` of free balance from `source` to become owned by `dest` but on hold + /// for `reason`. + /// for `reason`. + /// + /// If `precision` is `BestEffort`, then an amount less than `amount` may be transferred without + /// error. + /// + /// `source` must obey the requirements of `keep_alive`. + /// + /// If `force` is `Force`, then other fund-locking mechanisms may be disregarded. It should be + /// left as `Regular` in most circumstances, but when you want the same power as a `slash`, it + /// may be `Force`. + /// + /// The amount placed on hold is returned or `Err` in the case of error and nothing is changed. + /// + /// WARNING: This may return an error after a partial storage mutation. It should be used only + /// inside a transactional storage context and an `Err` result must imply a storage rollback. + fn transfer_and_hold( + asset: Self::AssetId, + reason: &Self::Reason, + source: &AccountId, + dest: &AccountId, + amount: Self::Balance, + precision: Precision, + expendability: Preservation, + force: Fortitude, + ) -> Result { + ensure!(Self::hold_available(asset, reason, dest), TokenError::CannotCreateHold); + ensure!( + Self::can_deposit(asset, dest, amount, Extant) == Success, + TokenError::CannotCreate + ); + let actual = + Self::decrease_balance(asset, source, amount, precision, expendability, force)?; + Self::increase_balance_on_hold(asset, reason, dest, actual, precision)?; + Self::done_transfer_on_hold(asset, reason, source, dest, actual); + Ok(actual) + } + + fn done_hold( + _asset: Self::AssetId, + _reason: &Self::Reason, + _who: &AccountId, + _amount: Self::Balance, + ) { + } + fn done_release( + _asset: Self::AssetId, + _reason: &Self::Reason, + _who: &AccountId, + _amount: Self::Balance, + ) { + } + fn done_burn_held( + _asset: Self::AssetId, + _reason: &Self::Reason, + _who: &AccountId, + _amount: Self::Balance, + ) { + } + fn done_transfer_on_hold( + _asset: Self::AssetId, + _reason: &Self::Reason, + _source: &AccountId, + _dest: &AccountId, + _amount: Self::Balance, + ) { + } + fn done_transfer_and_hold( + _asset: Self::AssetId, + _reason: &Self::Reason, + _source: &AccountId, + _dest: &AccountId, + _transferred: Self::Balance, + ) { + } +} diff --git a/frame/support/src/traits/tokens/fungibles/imbalance.rs b/frame/support/src/traits/tokens/fungibles/imbalance.rs index 87445859c..ab18eec38 100644 --- a/frame/support/src/traits/tokens/fungibles/imbalance.rs +++ b/frame/support/src/traits/tokens/fungibles/imbalance.rs @@ -18,12 +18,11 @@ //! The imbalance type and its associates, which handles keeps everything adding up properly with //! unbalanced operations. -use super::{ - balanced::Balanced, - fungibles::{AssetId, Balance}, - *, +use super::*; +use crate::traits::{ + misc::{SameOrOther, TryDrop}, + tokens::{AssetId, Balance}, }; -use crate::traits::misc::{SameOrOther, TryDrop}; use sp_runtime::{traits::Zero, RuntimeDebug}; use sp_std::marker::PhantomData; @@ -160,7 +159,7 @@ impl< } /// Imbalance implying that the total_issuance value is less than the sum of all account balances. -pub type DebtOf = Imbalance< +pub type Debt = Imbalance< >::AssetId, >::Balance, // This will generally be implemented by increasing the total_issuance value. @@ -170,7 +169,7 @@ pub type DebtOf = Imbalance< /// Imbalance implying that the total_issuance value is greater than the sum of all account /// balances. -pub type CreditOf = Imbalance< +pub type Credit = Imbalance< >::AssetId, >::Balance, // This will generally be implemented by decreasing the total_issuance value. diff --git a/frame/support/src/traits/tokens/fungibles/lifetime.rs b/frame/support/src/traits/tokens/fungibles/lifetime.rs new file mode 100644 index 000000000..9e2c306f6 --- /dev/null +++ b/frame/support/src/traits/tokens/fungibles/lifetime.rs @@ -0,0 +1,84 @@ +// This file is part of Substrate. + +// Copyright (C) 2019-2022 Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +//! Traits for creating and destroying assets. + +use sp_runtime::{DispatchError, DispatchResult}; + +use super::Inspect; + +/// Trait for providing the ability to create new fungible assets. +pub trait Create: Inspect { + /// Create a new fungible asset. + fn create( + id: Self::AssetId, + admin: AccountId, + is_sufficient: bool, + min_balance: Self::Balance, + ) -> DispatchResult; +} + +/// Trait for providing the ability to destroy existing fungible assets. +pub trait Destroy: Inspect { + /// Start the destruction an existing fungible asset. + /// * `id`: The `AssetId` to be destroyed. successfully. + /// * `maybe_check_owner`: An optional account id that can be used to authorize the destroy + /// command. If not provided, no authorization checks will be performed before destroying + /// asset. + fn start_destroy(id: Self::AssetId, maybe_check_owner: Option) -> DispatchResult; + + /// Destroy all accounts associated with a given asset. + /// `destroy_accounts` should only be called after `start_destroy` has been called, and the + /// asset is in a `Destroying` state + /// + /// * `id`: The identifier of the asset to be destroyed. This must identify an existing asset. + /// * `max_items`: The maximum number of accounts to be destroyed for a given call of the + /// function. This value should be small enough to allow the operation fit into a logical + /// block. + /// + /// Response: + /// * u32: Total number of approvals which were actually destroyed + /// + /// Due to weight restrictions, this function may need to be called multiple + /// times to fully destroy all approvals. It will destroy `max_items` approvals at a + /// time. + fn destroy_accounts(id: Self::AssetId, max_items: u32) -> Result; + /// Destroy all approvals associated with a given asset up to the `max_items` + /// `destroy_approvals` should only be called after `start_destroy` has been called, and the + /// asset is in a `Destroying` state + /// + /// * `id`: The identifier of the asset to be destroyed. This must identify an existing asset. + /// * `max_items`: The maximum number of accounts to be destroyed for a given call of the + /// function. This value should be small enough to allow the operation fit into a logical + /// block. + /// + /// Response: + /// * u32: Total number of approvals which were actually destroyed + /// + /// Due to weight restrictions, this function may need to be called multiple + /// times to fully destroy all approvals. It will destroy `max_items` approvals at a + /// time. + fn destroy_approvals(id: Self::AssetId, max_items: u32) -> Result; + + /// Complete destroying asset and unreserve currency. + /// `finish_destroy` should only be called after `start_destroy` has been called, and the + /// asset is in a `Destroying` state. All accounts or approvals should be destroyed before + /// hand. + /// + /// * `id`: The identifier of the asset to be destroyed. This must identify an existing asset. + fn finish_destroy(id: Self::AssetId) -> DispatchResult; +} diff --git a/frame/support/src/traits/tokens/fungibles/mod.rs b/frame/support/src/traits/tokens/fungibles/mod.rs new file mode 100644 index 000000000..697eff39f --- /dev/null +++ b/frame/support/src/traits/tokens/fungibles/mod.rs @@ -0,0 +1,40 @@ +// This file is part of Substrate. + +// Copyright (C) 2019-2022 Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +//! The traits for sets of fungible tokens and any associated types. + +pub mod approvals; +mod enumerable; +pub mod freeze; +pub mod hold; +mod imbalance; +mod lifetime; +pub mod metadata; +mod regular; +pub mod roles; + +pub use enumerable::Inspect as InspectEnumerable; +pub use freeze::{Inspect as InspectFreeze, Mutate as MutateFreeze}; +pub use hold::{ + Balanced as BalancedHold, Inspect as InspectHold, Mutate as MutateHold, + Unbalanced as UnbalancedHold, +}; +pub use imbalance::{Credit, Debt, HandleImbalanceDrop, Imbalance}; +pub use lifetime::{Create, Destroy}; +pub use regular::{ + Balanced, DecreaseIssuance, Dust, IncreaseIssuance, Inspect, Mutate, Unbalanced, +}; diff --git a/frame/support/src/traits/tokens/fungibles/regular.rs b/frame/support/src/traits/tokens/fungibles/regular.rs new file mode 100644 index 000000000..03c149880 --- /dev/null +++ b/frame/support/src/traits/tokens/fungibles/regular.rs @@ -0,0 +1,571 @@ +// This file is part of Substrate. + +// Copyright (C) 2019-2022 Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +//! `Inspect` and `Mutate` traits for working with regular balances. + +use sp_std::marker::PhantomData; + +use crate::{ + dispatch::DispatchError, + ensure, + traits::{ + tokens::{ + misc::{ + Balance, DepositConsequence, + Fortitude::{self, Force, Polite}, + Precision::{self, BestEffort, Exact}, + Preservation::{self, Expendable}, + Provenance::{self, Extant}, + WithdrawConsequence, + }, + AssetId, + }, + SameOrOther, TryDrop, + }, +}; +use sp_arithmetic::traits::{CheckedAdd, CheckedSub, One}; +use sp_runtime::{traits::Saturating, ArithmeticError, TokenError}; + +use super::{Credit, Debt, HandleImbalanceDrop, Imbalance}; + +/// Trait for providing balance-inspection access to a set of named fungible assets. +pub trait Inspect: Sized { + /// Means of identifying one asset class from another. + type AssetId: AssetId; + + /// Scalar type for representing balance of an account. + type Balance: Balance; + + /// The total amount of issuance in the system. + fn total_issuance(asset: Self::AssetId) -> Self::Balance; + + /// The total amount of issuance in the system excluding those which are controlled by the + /// system. + fn active_issuance(asset: Self::AssetId) -> Self::Balance { + Self::total_issuance(asset) + } + + /// The minimum balance any single account may have. + fn minimum_balance(asset: Self::AssetId) -> Self::Balance; + + /// Get the total amount of funds whose ultimate bneficial ownership can be determined as `who`. + /// + /// This may include funds which are wholly inaccessible to `who`, either temporarily or even + /// indefinitely. + /// + /// For the amount of the balance which is currently free to be removed from the account without + /// error, use `reducible_balance`. + /// + /// For the amount of the balance which may eventually be free to be removed from the account, + /// use `balance()`. + fn total_balance(asset: Self::AssetId, who: &AccountId) -> Self::Balance; + + /// Get the balance of `who` which does not include funds which are exclusively allocated to + /// subsystems of the chain ("on hold" or "reserved"). + /// + /// In general this isn't especially useful outside of tests, and for practical purposes, you'll + /// want to use `reducible_balance()`. + fn balance(asset: Self::AssetId, who: &AccountId) -> Self::Balance; + + /// Get the maximum amount that `who` can withdraw/transfer successfully based on whether the + /// account should be kept alive (`preservation`) or whether we are willing to force the + /// transfer and potentially go below user-level restrictions on the minimum amount of the + /// account. + /// + /// Always less than `free_balance()`. + fn reducible_balance( + asset: Self::AssetId, + who: &AccountId, + preservation: Preservation, + force: Fortitude, + ) -> Self::Balance; + + /// Returns `true` if the `asset` balance of `who` may be increased by `amount`. + /// + /// - `asset`: The asset that should be deposited. + /// - `who`: The account of which the balance should be increased by `amount`. + /// - `amount`: How much should the balance be increased? + /// - `mint`: Will `amount` be minted to deposit it into `account`? + fn can_deposit( + asset: Self::AssetId, + who: &AccountId, + amount: Self::Balance, + provenance: Provenance, + ) -> DepositConsequence; + + /// Returns `Failed` if the `asset` balance of `who` may not be decreased by `amount`, otherwise + /// the consequence. + fn can_withdraw( + asset: Self::AssetId, + who: &AccountId, + amount: Self::Balance, + ) -> WithdrawConsequence; + + /// Returns `true` if an `asset` exists. + fn asset_exists(asset: Self::AssetId) -> bool; +} + +/// Special dust type which can be type-safely converted into a `Credit`. +#[must_use] +pub struct Dust>(pub(crate) T::AssetId, pub(crate) T::Balance); + +impl> Dust { + /// Convert `Dust` into an instance of `Credit`. + pub fn into_credit(self) -> Credit { + Credit::::new(self.0, self.1) + } +} + +/// A fungible token class where the balance can be set arbitrarily. +/// +/// **WARNING** +/// Do not use this directly unless you want trouble, since it allows you to alter account balances +/// without keeping the issuance up to date. It has no safeguards against accidentally creating +/// token imbalances in your system leading to accidental imflation or deflation. It's really just +/// for the underlying datatype to implement so the user gets the much safer `Balanced` trait to +/// use. +pub trait Unbalanced: Inspect { + /// Create some dust and handle it with `Self::handle_dust`. This is an unbalanced operation + /// and it must only be used when an account is modified in a raw fashion, outside of the entire + /// fungibles API. The `amount` is capped at `Self::minimum_balance() - 1`. + /// + /// This should not be reimplemented. + fn handle_raw_dust(asset: Self::AssetId, amount: Self::Balance) { + Self::handle_dust(Dust( + asset, + amount.min(Self::minimum_balance(asset).saturating_sub(One::one())), + )) + } + + /// Do something with the dust which has been destroyed from the system. `Dust` can be converted + /// into a `Credit` with the `Balanced` trait impl. + fn handle_dust(dust: Dust); + + /// Forcefully set the balance of `who` to `amount`. + /// + /// If this call executes successfully, you can `assert_eq!(Self::balance(), amount);`. + /// + /// For implementations which include one or more balances on hold, then these are *not* + /// included in the `amount`. + /// + /// This function does its best to force the balance change through, but will not break system + /// invariants such as any Existential Deposits needed or overflows/underflows. + /// If this cannot be done for some reason (e.g. because the account cannot be created, deleted + /// or would overflow) then an `Err` is returned. + fn write_balance( + asset: Self::AssetId, + who: &AccountId, + amount: Self::Balance, + ) -> Result, DispatchError>; + + /// Set the total issuance to `amount`. + fn set_total_issuance(asset: Self::AssetId, amount: Self::Balance); + + /// Reduce the balance of `who` by `amount`. + /// + /// If `precision` is `Exact` and it cannot be reduced by that amount for + /// some reason, return `Err` and don't reduce it at all. If `precision` is `BestEffort`, then + /// reduce the balance of `who` by the most that is possible, up to `amount`. + /// + /// In either case, if `Ok` is returned then the inner is the amount by which is was reduced. + /// Minimum balance will be respected and thus the returned amount may be up to + /// `Self::minimum_balance() - 1` greater than `amount` in the case that the reduction caused + /// the account to be deleted. + fn decrease_balance( + asset: Self::AssetId, + who: &AccountId, + mut amount: Self::Balance, + precision: Precision, + preservation: Preservation, + force: Fortitude, + ) -> Result { + let old_balance = Self::balance(asset, who); + let free = Self::reducible_balance(asset, who, preservation, force); + if let BestEffort = precision { + amount = amount.min(free); + } + let new_balance = old_balance.checked_sub(&amount).ok_or(TokenError::FundsUnavailable)?; + if let Some(dust) = Self::write_balance(asset, who, new_balance)? { + Self::handle_dust(Dust(asset, dust)); + } + Ok(old_balance.saturating_sub(new_balance)) + } + + /// Increase the balance of `who` by `amount`. + /// + /// If it cannot be increased by that amount for some reason, return `Err` and don't increase + /// it at all. If Ok, return the imbalance. + /// Minimum balance will be respected and an error will be returned if + /// `amount < Self::minimum_balance()` when the account of `who` is zero. + fn increase_balance( + asset: Self::AssetId, + who: &AccountId, + amount: Self::Balance, + precision: Precision, + ) -> Result { + let old_balance = Self::balance(asset, who); + let new_balance = if let BestEffort = precision { + old_balance.saturating_add(amount) + } else { + old_balance.checked_add(&amount).ok_or(ArithmeticError::Overflow)? + }; + if new_balance < Self::minimum_balance(asset) { + // Attempt to increase from 0 to below minimum -> stays at zero. + if let BestEffort = precision { + Ok(Self::Balance::default()) + } else { + Err(TokenError::BelowMinimum.into()) + } + } else { + if new_balance == old_balance { + Ok(Self::Balance::default()) + } else { + if let Some(dust) = Self::write_balance(asset, who, new_balance)? { + Self::handle_dust(Dust(asset, dust)); + } + Ok(new_balance.saturating_sub(old_balance)) + } + } + } + + /// Reduce the active issuance by some amount. + fn deactivate(_asset: Self::AssetId, _: Self::Balance) {} + + /// Increase the active issuance by some amount, up to the outstanding amount reduced. + fn reactivate(_asset: Self::AssetId, _: Self::Balance) {} +} + +/// Trait for providing a basic fungible asset. +pub trait Mutate: Inspect + Unbalanced { + /// Increase the balance of `who` by exactly `amount`, minting new tokens. If that isn't + /// possible then an `Err` is returned and nothing is changed. + fn mint_into( + asset: Self::AssetId, + who: &AccountId, + amount: Self::Balance, + ) -> Result { + Self::total_issuance(asset) + .checked_add(&amount) + .ok_or(ArithmeticError::Overflow)?; + let actual = Self::increase_balance(asset, who, amount, Exact)?; + Self::set_total_issuance(asset, Self::total_issuance(asset).saturating_add(actual)); + Self::done_mint_into(asset, who, amount); + Ok(actual) + } + + /// Decrease the balance of `who` by at least `amount`, possibly slightly more in the case of + /// minimum-balance requirements, burning the tokens. If that isn't possible then an `Err` is + /// returned and nothing is changed. If successful, the amount of tokens reduced is returned. + fn burn_from( + asset: Self::AssetId, + who: &AccountId, + amount: Self::Balance, + precision: Precision, + force: Fortitude, + ) -> Result { + let actual = Self::reducible_balance(asset, who, Expendable, force).min(amount); + ensure!(actual == amount || precision == BestEffort, TokenError::FundsUnavailable); + Self::total_issuance(asset) + .checked_sub(&actual) + .ok_or(ArithmeticError::Overflow)?; + let actual = Self::decrease_balance(asset, who, actual, BestEffort, Expendable, force)?; + Self::set_total_issuance(asset, Self::total_issuance(asset).saturating_sub(actual)); + Self::done_burn_from(asset, who, actual); + Ok(actual) + } + + /// Attempt to decrease the `asset` balance of `who` by `amount`. + /// + /// Equivalent to `burn_from`, except with an expectation that within the bounds of some + /// universal issuance, the total assets `suspend`ed and `resume`d will be equivalent. The + /// implementation may be configured such that the total assets suspended may never be less than + /// the total assets resumed (which is the invariant for an issuing system), or the reverse + /// (which the invariant in a non-issuing system). + /// + /// Because of this expectation, any metadata associated with the asset is expected to survive + /// the suspect-resume cycle. + fn shelve( + asset: Self::AssetId, + who: &AccountId, + amount: Self::Balance, + ) -> Result { + let actual = Self::reducible_balance(asset, who, Expendable, Polite).min(amount); + ensure!(actual == amount, TokenError::FundsUnavailable); + Self::total_issuance(asset) + .checked_sub(&actual) + .ok_or(ArithmeticError::Overflow)?; + let actual = Self::decrease_balance(asset, who, actual, BestEffort, Expendable, Polite)?; + Self::set_total_issuance(asset, Self::total_issuance(asset).saturating_sub(actual)); + Self::done_shelve(asset, who, actual); + Ok(actual) + } + + /// Attempt to increase the `asset` balance of `who` by `amount`. + /// + /// Equivalent to `mint_into`, except with an expectation that within the bounds of some + /// universal issuance, the total assets `suspend`ed and `resume`d will be equivalent. The + /// implementation may be configured such that the total assets suspended may never be less than + /// the total assets resumed (which is the invariant for an issuing system), or the reverse + /// (which the invariant in a non-issuing system). + /// + /// Because of this expectation, any metadata associated with the asset is expected to survive + /// the suspect-resume cycle. + fn restore( + asset: Self::AssetId, + who: &AccountId, + amount: Self::Balance, + ) -> Result { + Self::total_issuance(asset) + .checked_add(&amount) + .ok_or(ArithmeticError::Overflow)?; + let actual = Self::increase_balance(asset, who, amount, Exact)?; + Self::set_total_issuance(asset, Self::total_issuance(asset).saturating_add(actual)); + Self::done_restore(asset, who, amount); + Ok(actual) + } + + /// Transfer funds from one account into another. + fn transfer( + asset: Self::AssetId, + source: &AccountId, + dest: &AccountId, + amount: Self::Balance, + preservation: Preservation, + ) -> Result { + let _extra = + Self::can_withdraw(asset, source, amount).into_result(preservation != Expendable)?; + Self::can_deposit(asset, dest, amount, Extant).into_result()?; + Self::decrease_balance(asset, source, amount, BestEffort, preservation, Polite)?; + // This should never fail as we checked `can_deposit` earlier. But we do a best-effort + // anyway. + let _ = Self::increase_balance(asset, dest, amount, BestEffort); + Self::done_transfer(asset, source, dest, amount); + Ok(amount) + } + + /// Simple infallible function to force an account to have a particular balance, good for use + /// in tests and benchmarks but not recommended for production code owing to the lack of + /// error reporting. + /// + /// Returns the new balance. + fn set_balance(asset: Self::AssetId, who: &AccountId, amount: Self::Balance) -> Self::Balance { + let b = Self::balance(asset, who); + if b > amount { + Self::burn_from(asset, who, b - amount, BestEffort, Force) + .map(|d| amount.saturating_sub(d)) + } else { + Self::mint_into(asset, who, amount - b).map(|d| amount.saturating_add(d)) + } + .unwrap_or(b) + } + fn done_mint_into(_asset: Self::AssetId, _who: &AccountId, _amount: Self::Balance) {} + fn done_burn_from(_asset: Self::AssetId, _who: &AccountId, _amount: Self::Balance) {} + fn done_shelve(_asset: Self::AssetId, _who: &AccountId, _amount: Self::Balance) {} + fn done_restore(_asset: Self::AssetId, _who: &AccountId, _amount: Self::Balance) {} + fn done_transfer( + _asset: Self::AssetId, + _source: &AccountId, + _dest: &AccountId, + _amount: Self::Balance, + ) { + } +} + +/// Simple handler for an imbalance drop which increases the total issuance of the system by the +/// imbalance amount. Used for leftover debt. +pub struct IncreaseIssuance(PhantomData<(AccountId, U)>); +impl> HandleImbalanceDrop + for IncreaseIssuance +{ + fn handle(asset: U::AssetId, amount: U::Balance) { + U::set_total_issuance(asset, U::total_issuance(asset).saturating_add(amount)) + } +} + +/// Simple handler for an imbalance drop which decreases the total issuance of the system by the +/// imbalance amount. Used for leftover credit. +pub struct DecreaseIssuance(PhantomData<(AccountId, U)>); +impl> HandleImbalanceDrop + for DecreaseIssuance +{ + fn handle(asset: U::AssetId, amount: U::Balance) { + U::set_total_issuance(asset, U::total_issuance(asset).saturating_sub(amount)) + } +} + +/// A fungible token class where any creation and deletion of tokens is semi-explicit and where the +/// total supply is maintained automatically. +/// +/// This is auto-implemented when a token class has `Unbalanced` implemented. +pub trait Balanced: Inspect + Unbalanced { + /// The type for managing what happens when an instance of `Debt` is dropped without being used. + type OnDropDebt: HandleImbalanceDrop; + /// The type for managing what happens when an instance of `Credit` is dropped without being + /// used. + type OnDropCredit: HandleImbalanceDrop; + + /// Reduce the total issuance by `amount` and return the according imbalance. The imbalance will + /// typically be used to reduce an account by the same amount with e.g. `settle`. + /// + /// This is infallible, but doesn't guarantee that the entire `amount` is burnt, for example + /// in the case of underflow. + fn rescind(asset: Self::AssetId, amount: Self::Balance) -> Debt { + let old = Self::total_issuance(asset); + let new = old.saturating_sub(amount); + Self::set_total_issuance(asset, new); + let delta = old - new; + Self::done_rescind(asset, delta); + Imbalance::::new( + asset, delta, + ) + } + + /// Increase the total issuance by `amount` and return the according imbalance. The imbalance + /// will typically be used to increase an account by the same amount with e.g. + /// `resolve_into_existing` or `resolve_creating`. + /// + /// This is infallible, but doesn't guarantee that the entire `amount` is issued, for example + /// in the case of overflow. + fn issue(asset: Self::AssetId, amount: Self::Balance) -> Credit { + let old = Self::total_issuance(asset); + let new = old.saturating_add(amount); + Self::set_total_issuance(asset, new); + let delta = new - old; + Self::done_issue(asset, delta); + Imbalance::::new( + asset, delta, + ) + } + + /// Produce a pair of imbalances that cancel each other out exactly. + /// + /// This is just the same as burning and issuing the same amount and has no effect on the + /// total issuance. + fn pair( + asset: Self::AssetId, + amount: Self::Balance, + ) -> (Debt, Credit) { + (Self::rescind(asset, amount), Self::issue(asset, amount)) + } + + /// Mints `value` into the account of `who`, creating it as needed. + /// + /// If `precision` is `BestEffort` and `value` in full could not be minted (e.g. due to + /// overflow), then the maximum is minted, up to `value`. If `precision` is `Exact`, then + /// exactly `value` must be minted into the account of `who` or the operation will fail with an + /// `Err` and nothing will change. + /// + /// If the operation is successful, this will return `Ok` with a `Debt` of the total value + /// added to the account. + fn deposit( + asset: Self::AssetId, + who: &AccountId, + value: Self::Balance, + precision: Precision, + ) -> Result, DispatchError> { + let increase = Self::increase_balance(asset, who, value, precision)?; + Self::done_deposit(asset, who, increase); + Ok(Imbalance::::new( + asset, increase, + )) + } + + /// Removes `value` balance from `who` account if possible. + /// + /// If `precision` is `BestEffort` and `value` in full could not be removed (e.g. due to + /// underflow), then the maximum is removed, up to `value`. If `precision` is `Exact`, then + /// exactly `value` must be removed from the account of `who` or the operation will fail with an + /// `Err` and nothing will change. + /// + /// If the removal is needed but not possible, then it returns `Err` and nothing is changed. + /// If the account needed to be deleted, then slightly more than `value` may be removed from the + /// account owning since up to (but not including) minimum balance may also need to be removed. + /// + /// If the operation is successful, this will return `Ok` with a `Credit` of the total value + /// removed from the account. + fn withdraw( + asset: Self::AssetId, + who: &AccountId, + value: Self::Balance, + precision: Precision, + preservation: Preservation, + force: Fortitude, + ) -> Result, DispatchError> { + let decrease = Self::decrease_balance(asset, who, value, precision, preservation, force)?; + Self::done_withdraw(asset, who, decrease); + Ok(Imbalance::::new( + asset, decrease, + )) + } + + /// The balance of `who` is increased in order to counter `credit`. If the whole of `credit` + /// cannot be countered, then nothing is changed and the original `credit` is returned in an + /// `Err`. + /// + /// Please note: If `credit.peek()` is less than `Self::minimum_balance()`, then `who` must + /// already exist for this to succeed. + fn resolve( + who: &AccountId, + credit: Credit, + ) -> Result<(), Credit> { + let v = credit.peek(); + let debt = match Self::deposit(credit.asset(), who, v, Exact) { + Err(_) => return Err(credit), + Ok(d) => d, + }; + if let Ok(result) = credit.offset(debt) { + let result = result.try_drop(); + debug_assert!(result.is_ok(), "ok deposit return must be equal to credit value; qed"); + } else { + debug_assert!(false, "debt.asset is credit.asset; qed"); + } + Ok(()) + } + + /// The balance of `who` is decreased in order to counter `debt`. If the whole of `debt` + /// cannot be countered, then nothing is changed and the original `debt` is returned in an + /// `Err`. + fn settle( + who: &AccountId, + debt: Debt, + preservation: Preservation, + ) -> Result, Debt> { + let amount = debt.peek(); + let asset = debt.asset(); + let credit = match Self::withdraw(asset, who, amount, Exact, preservation, Polite) { + Err(_) => return Err(debt), + Ok(d) => d, + }; + match credit.offset(debt) { + Ok(SameOrOther::None) => Ok(Credit::::zero(asset)), + Ok(SameOrOther::Same(dust)) => Ok(dust), + Ok(SameOrOther::Other(rest)) => { + debug_assert!(false, "ok withdraw return must be at least debt value; qed"); + Err(rest) + }, + Err(_) => { + debug_assert!(false, "debt.asset is credit.asset; qed"); + Ok(Credit::::zero(asset)) + }, + } + } + + fn done_rescind(_asset: Self::AssetId, _amount: Self::Balance) {} + fn done_issue(_asset: Self::AssetId, _amount: Self::Balance) {} + fn done_deposit(_asset: Self::AssetId, _who: &AccountId, _amount: Self::Balance) {} + fn done_withdraw(_asset: Self::AssetId, _who: &AccountId, _amount: Self::Balance) {} +} diff --git a/frame/support/src/traits/tokens/misc.rs b/frame/support/src/traits/tokens/misc.rs index 6113642c8..8ad3a9535 100644 --- a/frame/support/src/traits/tokens/misc.rs +++ b/frame/support/src/traits/tokens/misc.rs @@ -23,12 +23,65 @@ use sp_core::RuntimeDebug; use sp_runtime::{traits::Convert, ArithmeticError, DispatchError, TokenError}; use sp_std::fmt::Debug; +/// The origin of funds to be used for a deposit operation. +#[derive(Copy, Clone, RuntimeDebug, Eq, PartialEq)] +pub enum Provenance { + /// The funds will be minted into the system, increasing total issuance (and potentially + /// causing an overflow there). + Minted, + /// The funds already exist in the system, therefore will not affect total issuance. + Extant, +} + +/// The mode under which usage of funds may be restricted. +#[derive(Copy, Clone, RuntimeDebug, Eq, PartialEq)] +pub enum Restriction { + /// Funds are under the normal conditions. + Free, + /// Funds are on hold. + OnHold, +} + +/// The mode by which we describe whether an operation should keep an account alive. +#[derive(Copy, Clone, RuntimeDebug, Eq, PartialEq)] +pub enum Preservation { + /// We don't care if the account gets killed by this operation. + Expendable, + /// The account may not be killed, but we don't care if the balance gets dusted. + Protect, + /// The account may not be killed and our provider reference must remain (in the context of + /// tokens, this means that the account may not be dusted). + Preserve, +} + +/// The privilege with which a withdraw operation is conducted. +#[derive(Copy, Clone, RuntimeDebug, Eq, PartialEq)] +pub enum Fortitude { + /// The operation should execute with regular privilege. + Polite, + /// The operation should be forced to succeed if possible. This is usually employed for system- + /// level security-critical events such as slashing. + Force, +} + +/// The precision required of an operation generally involving some aspect of quantitative fund +/// withdrawal or transfer. +#[derive(Copy, Clone, RuntimeDebug, Eq, PartialEq)] +pub enum Precision { + /// The operation should must either proceed either exactly according to the amounts involved + /// or not at all. + Exact, + /// The operation may be considered successful even if less than the specified amounts are + /// available to be used. In this case a best effort will be made. + BestEffort, +} + /// One of a number of consequences of withdrawing a fungible from an account. #[derive(Copy, Clone, RuntimeDebug, Eq, PartialEq)] pub enum WithdrawConsequence { /// Withdraw could not happen since the amount to be withdrawn is less than the total funds in /// the account. - NoFunds, + BalanceLow, /// The withdraw would mean the account dying when it needs to exist (usually because it is a /// provider and there are consumer references on it). WouldDie, @@ -53,15 +106,16 @@ pub enum WithdrawConsequence { impl WithdrawConsequence { /// Convert the type into a `Result` with `DispatchError` as the error or the additional /// `Balance` by which the account will be reduced. - pub fn into_result(self) -> Result { + pub fn into_result(self, keep_nonzero: bool) -> Result { use WithdrawConsequence::*; match self { - NoFunds => Err(TokenError::NoFunds.into()), - WouldDie => Err(TokenError::WouldDie.into()), + BalanceLow => Err(TokenError::FundsUnavailable.into()), + WouldDie => Err(TokenError::OnlyProvider.into()), UnknownAsset => Err(TokenError::UnknownAsset.into()), Underflow => Err(ArithmeticError::Underflow.into()), Overflow => Err(ArithmeticError::Overflow.into()), Frozen => Err(TokenError::Frozen.into()), + ReducedToZero(_) if keep_nonzero => Err(TokenError::NotExpendable.into()), ReducedToZero(result) => Ok(result), Success => Ok(Zero::zero()), } diff --git a/frame/support/src/traits/tokens/pay.rs b/frame/support/src/traits/tokens/pay.rs index 1c6a147b5..23bd113bf 100644 --- a/frame/support/src/traits/tokens/pay.rs +++ b/frame/support/src/traits/tokens/pay.rs @@ -22,7 +22,7 @@ use scale_info::TypeInfo; use sp_core::{RuntimeDebug, TypedGet}; use sp_std::fmt::Debug; -use super::{fungible, Balance}; +use super::{fungible, Balance, Preservation::Expendable}; /// Can be implemented by `PayFromAccount` using a `fungible` impl, but can also be implemented with /// XCM/MultiAsset and made generic over assets. @@ -76,9 +76,7 @@ pub enum PaymentStatus { /// Simple implementation of `Pay` which makes a payment from a "pot" - i.e. a single account. pub struct PayFromAccount(sp_std::marker::PhantomData<(F, A)>); -impl + fungible::Mutate> Pay - for PayFromAccount -{ +impl> Pay for PayFromAccount { type Balance = F::Balance; type Beneficiary = A::Type; type AssetKind = (); @@ -88,7 +86,7 @@ impl + fungible::Mutate> Pa _: Self::AssetKind, amount: Self::Balance, ) -> Result { - >::transfer(&A::get(), who, amount, false).map_err(|_| ())?; + >::transfer(&A::get(), who, amount, Expendable).map_err(|_| ())?; Ok(()) } fn check_payment(_: ()) -> PaymentStatus { diff --git a/frame/system/src/lib.rs b/frame/system/src/lib.rs index faa1ee5d6..88291c326 100644 --- a/frame/system/src/lib.rs +++ b/frame/system/src/lib.rs @@ -1661,10 +1661,6 @@ impl BlockNumberProvider for Pallet { } } -fn is_providing(d: &T) -> bool { - d != &T::default() -} - /// Implement StoredMap for a simple single-item, provide-when-not-default system. This works fine /// for storing a single item which allows the account to continue existing as long as it's not /// empty/default. @@ -1680,23 +1676,12 @@ impl StoredMap for Pallet { f: impl FnOnce(&mut Option) -> Result, ) -> Result { let account = Account::::get(k); - let was_providing = is_providing(&account.data); - let mut some_data = if was_providing { Some(account.data) } else { None }; + let is_default = account.data == T::AccountData::default(); + let mut some_data = if is_default { None } else { Some(account.data) }; let result = f(&mut some_data)?; - let is_providing = some_data.is_some(); - if !was_providing && is_providing { - Self::inc_providers(k); - } else if was_providing && !is_providing { - match Self::dec_providers(k)? { - DecRefStatus::Reaped => return Ok(result), - DecRefStatus::Exists => { - // Update value as normal... - }, - } - } else if !was_providing && !is_providing { - return Ok(result) + if Self::providers(k) > 0 { + Account::::mutate(k, |a| a.data = some_data.unwrap_or_default()); } - Account::::mutate(k, |a| a.data = some_data.unwrap_or_default()); Ok(result) } } diff --git a/frame/system/src/tests.rs b/frame/system/src/tests.rs index ebb28ca87..9b60c6915 100644 --- a/frame/system/src/tests.rs +++ b/frame/system/src/tests.rs @@ -37,6 +37,7 @@ fn origin_works() { #[test] fn stored_map_works() { new_test_ext().execute_with(|| { + assert_eq!(System::inc_providers(&0), IncRefStatus::Created); assert_ok!(System::insert(&0, 42)); assert!(!System::is_provider_required(&0)); @@ -56,6 +57,7 @@ fn stored_map_works() { assert!(Killed::get().is_empty()); assert_ok!(System::remove(&0)); + assert_ok!(System::dec_providers(&0)); assert_eq!(Killed::get(), vec![0u64]); }); } diff --git a/frame/tips/src/tests.rs b/frame/tips/src/tests.rs index fa4ec1501..b2d97de18 100644 --- a/frame/tips/src/tests.rs +++ b/frame/tips/src/tests.rs @@ -97,6 +97,10 @@ impl pallet_balances::Config for Test { type ExistentialDeposit = ConstU64<1>; type AccountStore = System; type WeightInfo = (); + type FreezeIdentifier = (); + type MaxFreezes = (); + type HoldIdentifier = (); + type MaxHolds = (); } parameter_types! { static TenToFourteenTestValue: Vec = vec![10,11,12,13,14]; diff --git a/frame/transaction-payment/asset-tx-payment/src/lib.rs b/frame/transaction-payment/asset-tx-payment/src/lib.rs index b9cd6ef99..4e83d8b48 100644 --- a/frame/transaction-payment/asset-tx-payment/src/lib.rs +++ b/frame/transaction-payment/asset-tx-payment/src/lib.rs @@ -42,7 +42,7 @@ use frame_support::{ dispatch::{DispatchInfo, DispatchResult, PostDispatchInfo}, traits::{ tokens::{ - fungibles::{Balanced, CreditOf, Inspect}, + fungibles::{Balanced, Credit, Inspect}, WithdrawConsequence, }, IsType, @@ -104,7 +104,7 @@ pub enum InitialPayment { /// The initial fee was payed in the native currency. Native(LiquidityInfoOf), /// The initial fee was payed in an asset. - Asset(CreditOf), + Asset(Credit), } pub use pallet::*; @@ -159,7 +159,7 @@ where AssetBalanceOf: Send + Sync + FixedPointOperand, BalanceOf: Send + Sync + FixedPointOperand + IsType>, ChargeAssetIdOf: Send + Sync, - CreditOf: IsType>, + Credit: IsType>, { /// Utility constructor. Used only in client/factory code. pub fn from(tip: BalanceOf, asset_id: Option>) -> Self { @@ -216,7 +216,7 @@ where AssetBalanceOf: Send + Sync + FixedPointOperand, BalanceOf: Send + Sync + From + FixedPointOperand + IsType>, ChargeAssetIdOf: Send + Sync, - CreditOf: IsType>, + Credit: IsType>, { const IDENTIFIER: &'static str = "ChargeAssetTxPayment"; type AccountId = T::AccountId; diff --git a/frame/transaction-payment/asset-tx-payment/src/mock.rs b/frame/transaction-payment/asset-tx-payment/src/mock.rs index cd5147c3a..be7baaf2b 100644 --- a/frame/transaction-payment/asset-tx-payment/src/mock.rs +++ b/frame/transaction-payment/asset-tx-payment/src/mock.rs @@ -120,6 +120,10 @@ impl pallet_balances::Config for Runtime { type WeightInfo = (); type MaxReserves = ConstU32<50>; type ReserveIdentifier = [u8; 8]; + type FreezeIdentifier = (); + type MaxFreezes = (); + type HoldIdentifier = (); + type MaxHolds = (); } impl WeightToFeeT for WeightToFee { @@ -193,7 +197,7 @@ impl pallet_authorship::Config for Runtime { pub struct CreditToBlockAuthor; impl HandleCredit for CreditToBlockAuthor { - fn handle_credit(credit: CreditOf) { + fn handle_credit(credit: Credit) { if let Some(author) = pallet_authorship::Pallet::::author() { // What to do in case paying the author fails (e.g. because `fee < min_balance`) // default: drop the result which will trigger the `OnDrop` of the imbalance. diff --git a/frame/transaction-payment/asset-tx-payment/src/payment.rs b/frame/transaction-payment/asset-tx-payment/src/payment.rs index e273ffcfc..f8d6888b9 100644 --- a/frame/transaction-payment/asset-tx-payment/src/payment.rs +++ b/frame/transaction-payment/asset-tx-payment/src/payment.rs @@ -20,8 +20,10 @@ use crate::Config; use codec::FullCodec; use frame_support::{ traits::{ - fungibles::{Balanced, CreditOf, Inspect}, - tokens::{Balance, BalanceConversion}, + fungibles::{Balanced, Credit, Inspect}, + tokens::{ + Balance, BalanceConversion, Fortitude::Polite, Precision::Exact, Preservation::Protect, + }, }, unsigned::TransactionValidityError, }; @@ -75,13 +77,13 @@ pub trait HandleCredit> { /// Implement to determine what to do with the withdrawn asset fees. /// Default for `CreditOf` from the assets pallet is to burn and /// decrease total issuance. - fn handle_credit(credit: CreditOf); + fn handle_credit(credit: Credit); } /// Default implementation that just drops the credit according to the `OnDrop` in the underlying /// imbalance type. impl> HandleCredit for () { - fn handle_credit(_credit: CreditOf) {} + fn handle_credit(_credit: Credit) {} } /// Implements the asset transaction for a balance to asset converter (implementing @@ -101,7 +103,7 @@ where { type Balance = BalanceOf; type AssetId = AssetIdOf; - type LiquidityInfo = CreditOf; + type LiquidityInfo = Credit; /// Withdraw the predicted fee from the transaction origin. /// @@ -126,8 +128,15 @@ where if !matches!(can_withdraw, WithdrawConsequence::Success) { return Err(InvalidTransaction::Payment.into()) } - >::withdraw(asset_id, who, converted_fee) - .map_err(|_| TransactionValidityError::from(InvalidTransaction::Payment)) + >::withdraw( + asset_id, + who, + converted_fee, + Exact, + Protect, + Polite, + ) + .map_err(|_| TransactionValidityError::from(InvalidTransaction::Payment)) } /// Hand the fee and the tip over to the `[HandleCredit]` implementation. diff --git a/frame/transaction-payment/asset-tx-payment/src/tests.rs b/frame/transaction-payment/asset-tx-payment/src/tests.rs index cd9891825..2fee9c849 100644 --- a/frame/transaction-payment/asset-tx-payment/src/tests.rs +++ b/frame/transaction-payment/asset-tx-payment/src/tests.rs @@ -28,7 +28,7 @@ use pallet_balances::Call as BalancesCall; use sp_runtime::traits::StaticLookup; const CALL: &::RuntimeCall = - &RuntimeCall::Balances(BalancesCall::transfer { dest: 2, value: 69 }); + &RuntimeCall::Balances(BalancesCall::transfer_allow_death { dest: 2, value: 69 }); pub struct ExtBuilder { balance_factor: u64, diff --git a/frame/transaction-payment/src/mock.rs b/frame/transaction-payment/src/mock.rs index bfb9a194f..741f09448 100644 --- a/frame/transaction-payment/src/mock.rs +++ b/frame/transaction-payment/src/mock.rs @@ -49,7 +49,7 @@ frame_support::construct_runtime!( ); pub(crate) const CALL: &::RuntimeCall = - &RuntimeCall::Balances(BalancesCall::transfer { dest: 2, value: 69 }); + &RuntimeCall::Balances(BalancesCall::transfer_allow_death { dest: 2, value: 69 }); parameter_types! { pub(crate) static ExtrinsicBaseWeight: Weight = Weight::zero(); @@ -113,6 +113,10 @@ impl pallet_balances::Config for Runtime { type MaxReserves = (); type ReserveIdentifier = [u8; 8]; type WeightInfo = (); + type FreezeIdentifier = (); + type MaxFreezes = (); + type HoldIdentifier = (); + type MaxHolds = (); } impl WeightToFeeT for WeightToFee { diff --git a/frame/transaction-payment/src/tests.rs b/frame/transaction-payment/src/tests.rs index 218f50e1c..d5109609e 100644 --- a/frame/transaction-payment/src/tests.rs +++ b/frame/transaction-payment/src/tests.rs @@ -285,7 +285,7 @@ fn signed_ext_length_fee_is_also_updated_per_congestion() { #[test] fn query_info_and_fee_details_works() { - let call = RuntimeCall::Balances(BalancesCall::transfer { dest: 2, value: 69 }); + let call = RuntimeCall::Balances(BalancesCall::transfer_allow_death { dest: 2, value: 69 }); let origin = 111111; let extra = (); let xt = TestXt::new(call.clone(), Some((origin, extra))); @@ -348,7 +348,7 @@ fn query_info_and_fee_details_works() { #[test] fn query_call_info_and_fee_details_works() { - let call = RuntimeCall::Balances(BalancesCall::transfer { dest: 2, value: 69 }); + let call = RuntimeCall::Balances(BalancesCall::transfer_allow_death { dest: 2, value: 69 }); let info = call.get_dispatch_info(); let encoded_call = call.encode(); let len = encoded_call.len() as u32; @@ -530,7 +530,11 @@ fn refund_does_not_recreate_account() { assert_eq!(Balances::free_balance(2), 200 - 5 - 10 - 100 - 5); // kill the account between pre and post dispatch - assert_ok!(Balances::transfer(Some(2).into(), 3, Balances::free_balance(2))); + assert_ok!(Balances::transfer_allow_death( + Some(2).into(), + 3, + Balances::free_balance(2) + )); assert_eq!(Balances::free_balance(2), 0); assert_ok!(ChargeTransactionPayment::::post_dispatch( diff --git a/frame/transaction-storage/src/mock.rs b/frame/transaction-storage/src/mock.rs index f54c134d7..3a87d8eae 100644 --- a/frame/transaction-storage/src/mock.rs +++ b/frame/transaction-storage/src/mock.rs @@ -84,6 +84,10 @@ impl pallet_balances::Config for Test { type MaxLocks = (); type MaxReserves = (); type ReserveIdentifier = (); + type FreezeIdentifier = (); + type MaxFreezes = (); + type HoldIdentifier = (); + type MaxHolds = (); } impl pallet_transaction_storage::Config for Test { diff --git a/frame/treasury/src/tests.rs b/frame/treasury/src/tests.rs index 24d2d01f9..67b21ff62 100644 --- a/frame/treasury/src/tests.rs +++ b/frame/treasury/src/tests.rs @@ -90,6 +90,10 @@ impl pallet_balances::Config for Test { type ExistentialDeposit = ConstU64<1>; type AccountStore = System; type WeightInfo = (); + type FreezeIdentifier = (); + type MaxFreezes = (); + type HoldIdentifier = (); + type MaxHolds = (); } impl pallet_utility::Config for Test { diff --git a/frame/uniques/src/mock.rs b/frame/uniques/src/mock.rs index 67d994d79..bad393a48 100644 --- a/frame/uniques/src/mock.rs +++ b/frame/uniques/src/mock.rs @@ -82,6 +82,10 @@ impl pallet_balances::Config for Test { type MaxLocks = (); type MaxReserves = ConstU32<50>; type ReserveIdentifier = [u8; 8]; + type FreezeIdentifier = (); + type MaxFreezes = (); + type HoldIdentifier = (); + type MaxHolds = (); } impl Config for Test { diff --git a/frame/utility/Cargo.toml b/frame/utility/Cargo.toml index 099526d3d..c00255e3d 100644 --- a/frame/utility/Cargo.toml +++ b/frame/utility/Cargo.toml @@ -47,5 +47,6 @@ runtime-benchmarks = [ "frame-benchmarking/runtime-benchmarks", "frame-support/runtime-benchmarks", "frame-system/runtime-benchmarks", + "pallet-collective/runtime-benchmarks", ] try-runtime = ["frame-support/try-runtime"] diff --git a/frame/utility/src/tests.rs b/frame/utility/src/tests.rs index d9ac2ebbc..04f272824 100644 --- a/frame/utility/src/tests.rs +++ b/frame/utility/src/tests.rs @@ -35,6 +35,7 @@ use sp_core::H256; use sp_runtime::{ testing::Header, traits::{BlakeTwo256, Hash, IdentityLookup}, + TokenError, }; type BlockNumber = u64; @@ -185,6 +186,10 @@ impl pallet_balances::Config for Test { type ExistentialDeposit = ConstU64<1>; type AccountStore = System; type WeightInfo = (); + type FreezeIdentifier = (); + type MaxFreezes = (); + type HoldIdentifier = (); + type MaxHolds = (); } impl pallet_root_testing::Config for Test {} @@ -226,7 +231,7 @@ impl Contains for TestBaseCallFilter { fn contains(c: &RuntimeCall) -> bool { match *c { // Transfer works. Use `transfer_keep_alive` for a call that doesn't pass the filter. - RuntimeCall::Balances(pallet_balances::Call::transfer { .. }) => true, + RuntimeCall::Balances(pallet_balances::Call::transfer_allow_death { .. }) => true, RuntimeCall::Utility(_) => true, // For benchmarking, this acts as a noop call RuntimeCall::System(frame_system::Call::remark { .. }) => true, @@ -253,7 +258,7 @@ type ExampleCall = example::Call; type UtilityCall = crate::Call; use frame_system::Call as SystemCall; -use pallet_balances::{Call as BalancesCall, Error as BalancesError}; +use pallet_balances::Call as BalancesCall; use pallet_root_testing::Call as RootTestingCall; use pallet_timestamp::Call as TimestampCall; @@ -278,7 +283,7 @@ pub fn new_test_ext() -> sp_io::TestExternalities { } fn call_transfer(dest: u64, value: u64) -> RuntimeCall { - RuntimeCall::Balances(BalancesCall::transfer { dest, value }) + RuntimeCall::Balances(BalancesCall::transfer_allow_death { dest, value }) } fn call_foobar(err: bool, start_weight: Weight, end_weight: Option) -> RuntimeCall { @@ -289,10 +294,10 @@ fn call_foobar(err: bool, start_weight: Weight, end_weight: Option) -> R fn as_derivative_works() { new_test_ext().execute_with(|| { let sub_1_0 = Utility::derivative_account_id(1, 0); - assert_ok!(Balances::transfer(RuntimeOrigin::signed(1), sub_1_0, 5)); + assert_ok!(Balances::transfer_allow_death(RuntimeOrigin::signed(1), sub_1_0, 5)); assert_err_ignore_postinfo!( Utility::as_derivative(RuntimeOrigin::signed(1), 1, Box::new(call_transfer(6, 3)),), - BalancesError::::InsufficientBalance + TokenError::FundsUnavailable, ); assert_ok!(Utility::as_derivative( RuntimeOrigin::signed(1), @@ -599,7 +604,7 @@ fn batch_all_revert() { ), pays_fee: Pays::Yes }, - error: pallet_balances::Error::::InsufficientBalance.into() + error: TokenError::FundsUnavailable.into(), } ); assert_eq!(Balances::free_balance(1), 10); diff --git a/frame/vesting/src/benchmarking.rs b/frame/vesting/src/benchmarking.rs index 35a094a9f..15be51984 100644 --- a/frame/vesting/src/benchmarking.rs +++ b/frame/vesting/src/benchmarking.rs @@ -139,6 +139,7 @@ benchmarks! { let other: T::AccountId = account("other", 0, SEED); let other_lookup = T::Lookup::unlookup(other.clone()); + T::Currency::make_free_balance_be(&other, T::Currency::minimum_balance()); add_locks::(&other, l as u8); let expected_balance = add_vesting_schedules::(other_lookup.clone(), s)?; @@ -168,6 +169,7 @@ benchmarks! { let other: T::AccountId = account("other", 0, SEED); let other_lookup = T::Lookup::unlookup(other.clone()); + T::Currency::make_free_balance_be(&other, T::Currency::minimum_balance()); add_locks::(&other, l as u8); add_vesting_schedules::(other_lookup.clone(), s)?; // At block 21 everything is unlocked. @@ -200,8 +202,10 @@ benchmarks! { let target: T::AccountId = account("target", 0, SEED); let target_lookup = T::Lookup::unlookup(target.clone()); // Give target existing locks + T::Currency::make_free_balance_be(&target, T::Currency::minimum_balance()); add_locks::(&target, l as u8); // Add one vesting schedules. + let orig_balance = T::Currency::free_balance(&target); let mut expected_balance = add_vesting_schedules::(target_lookup.clone(), s)?; let transfer_amount = T::MinVestedTransfer::get(); @@ -216,7 +220,7 @@ benchmarks! { }: _(RawOrigin::Signed(caller), target_lookup, vesting_schedule) verify { assert_eq!( - expected_balance, + orig_balance + expected_balance, T::Currency::free_balance(&target), "Transfer didn't happen", ); @@ -238,8 +242,10 @@ benchmarks! { let target: T::AccountId = account("target", 0, SEED); let target_lookup = T::Lookup::unlookup(target.clone()); // Give target existing locks + T::Currency::make_free_balance_be(&target, T::Currency::minimum_balance()); add_locks::(&target, l as u8); // Add one less than max vesting schedules + let orig_balance = T::Currency::free_balance(&target); let mut expected_balance = add_vesting_schedules::(target_lookup.clone(), s)?; let transfer_amount = T::MinVestedTransfer::get(); @@ -254,7 +260,7 @@ benchmarks! { }: _(RawOrigin::Root, source_lookup, target_lookup, vesting_schedule) verify { assert_eq!( - expected_balance, + orig_balance + expected_balance, T::Currency::free_balance(&target), "Transfer didn't happen", ); @@ -272,6 +278,7 @@ benchmarks! { let caller: T::AccountId = account("caller", 0, SEED); let caller_lookup = T::Lookup::unlookup(caller.clone()); // Give target existing locks. + T::Currency::make_free_balance_be(&caller, T::Currency::minimum_balance()); add_locks::(&caller, l as u8); // Add max vesting schedules. let expected_balance = add_vesting_schedules::(caller_lookup, s)?; @@ -322,6 +329,7 @@ benchmarks! { let caller: T::AccountId = account("caller", 0, SEED); let caller_lookup = T::Lookup::unlookup(caller.clone()); // Give target other locks. + T::Currency::make_free_balance_be(&caller, T::Currency::minimum_balance()); add_locks::(&caller, l as u8); // Add max vesting schedules. let total_transferred = add_vesting_schedules::(caller_lookup, s)?; diff --git a/frame/vesting/src/mock.rs b/frame/vesting/src/mock.rs index f2ad6a700..be6afd4a6 100644 --- a/frame/vesting/src/mock.rs +++ b/frame/vesting/src/mock.rs @@ -80,6 +80,10 @@ impl pallet_balances::Config for Test { type MaxReserves = (); type ReserveIdentifier = [u8; 8]; type WeightInfo = (); + type FreezeIdentifier = (); + type MaxFreezes = (); + type HoldIdentifier = (); + type MaxHolds = (); } parameter_types! { pub const MinVestedTransfer: u64 = 256 * 2; diff --git a/frame/vesting/src/tests.rs b/frame/vesting/src/tests.rs index efc134cab..46afe895f 100644 --- a/frame/vesting/src/tests.rs +++ b/frame/vesting/src/tests.rs @@ -17,7 +17,10 @@ use frame_support::{assert_noop, assert_ok, assert_storage_noop, dispatch::EncodeLike}; use frame_system::RawOrigin; -use sp_runtime::traits::{BadOrigin, Identity}; +use sp_runtime::{ + traits::{BadOrigin, Identity}, + TokenError, +}; use super::{Vesting as VestingStorage, *}; use crate::mock::{Balances, ExtBuilder, System, Test, Vesting}; @@ -180,10 +183,8 @@ fn unvested_balance_should_not_transfer() { assert_eq!(user1_free_balance, 100); // Account 1 has free balance // Account 1 has only 5 units vested at block 1 (plus 50 unvested) assert_eq!(Vesting::vesting_balance(&1), Some(45)); - assert_noop!( - Balances::transfer(Some(1).into(), 2, 56), - pallet_balances::Error::::LiquidityRestrictions, - ); // Account 1 cannot send more than vested amount + // Account 1 cannot send more than vested amount... + assert_noop!(Balances::transfer_allow_death(Some(1).into(), 2, 56), TokenError::Frozen); }); } @@ -195,7 +196,7 @@ fn vested_balance_should_transfer() { // Account 1 has only 5 units vested at block 1 (plus 50 unvested) assert_eq!(Vesting::vesting_balance(&1), Some(45)); assert_ok!(Vesting::vest(Some(1).into())); - assert_ok!(Balances::transfer(Some(1).into(), 2, 55)); + assert_ok!(Balances::transfer_allow_death(Some(1).into(), 2, 55)); }); } @@ -213,7 +214,7 @@ fn vested_balance_should_transfer_with_multi_sched() { // Account 1 has only 256 units unlocking at block 1 (plus 1280 already fee). assert_eq!(Vesting::vesting_balance(&1), Some(2304)); assert_ok!(Vesting::vest(Some(1).into())); - assert_ok!(Balances::transfer(Some(1).into(), 2, 1536)); + assert_ok!(Balances::transfer_allow_death(Some(1).into(), 2, 1536)); }); } @@ -233,7 +234,7 @@ fn vested_balance_should_transfer_using_vest_other() { // Account 1 has only 5 units vested at block 1 (plus 50 unvested) assert_eq!(Vesting::vesting_balance(&1), Some(45)); assert_ok!(Vesting::vest_other(Some(2).into(), 1)); - assert_ok!(Balances::transfer(Some(1).into(), 2, 55)); + assert_ok!(Balances::transfer_allow_death(Some(1).into(), 2, 55)); }); } @@ -251,7 +252,7 @@ fn vested_balance_should_transfer_using_vest_other_with_multi_sched() { // Account 1 has only 256 units unlocking at block 1 (plus 1280 already free). assert_eq!(Vesting::vesting_balance(&1), Some(2304)); assert_ok!(Vesting::vest_other(Some(2).into(), 1)); - assert_ok!(Balances::transfer(Some(1).into(), 2, 1536)); + assert_ok!(Balances::transfer_allow_death(Some(1).into(), 2, 1536)); }); } @@ -266,8 +267,8 @@ fn non_vested_cannot_vest_other() { #[test] fn extra_balance_should_transfer() { ExtBuilder::default().existential_deposit(10).build().execute_with(|| { - assert_ok!(Balances::transfer(Some(3).into(), 1, 100)); - assert_ok!(Balances::transfer(Some(3).into(), 2, 100)); + assert_ok!(Balances::transfer_allow_death(Some(3).into(), 1, 100)); + assert_ok!(Balances::transfer_allow_death(Some(3).into(), 2, 100)); let user1_free_balance = Balances::free_balance(&1); assert_eq!(user1_free_balance, 200); // Account 1 has 100 more free balance than normal @@ -278,12 +279,13 @@ fn extra_balance_should_transfer() { // Account 1 has only 5 units vested at block 1 (plus 150 unvested) assert_eq!(Vesting::vesting_balance(&1), Some(45)); assert_ok!(Vesting::vest(Some(1).into())); - assert_ok!(Balances::transfer(Some(1).into(), 3, 155)); // Account 1 can send extra units gained + assert_ok!(Balances::transfer_allow_death(Some(1).into(), 3, 155)); // Account 1 can send extra units gained // Account 2 has no units vested at block 1, but gained 100 assert_eq!(Vesting::vesting_balance(&2), Some(200)); assert_ok!(Vesting::vest(Some(2).into())); - assert_ok!(Balances::transfer(Some(2).into(), 3, 100)); // Account 2 can send extra units gained + assert_ok!(Balances::transfer_allow_death(Some(2).into(), 3, 100)); // Account 2 can send extra + // units gained }); } @@ -305,7 +307,7 @@ fn liquid_funds_should_transfer_with_delayed_vesting() { assert_eq!(Vesting::vesting(&12).unwrap(), vec![user12_vesting_schedule]); // Account 12 can still send liquid funds - assert_ok!(Balances::transfer(Some(12).into(), 3, 256 * 5)); + assert_ok!(Balances::transfer_allow_death(Some(12).into(), 3, 256 * 5)); }); } @@ -1144,14 +1146,11 @@ fn vested_transfer_less_than_existential_deposit_fails() { ); // vested_transfer fails. - assert_noop!( - Vesting::vested_transfer(Some(3).into(), 99, sched), - pallet_balances::Error::::ExistentialDeposit, - ); + assert_noop!(Vesting::vested_transfer(Some(3).into(), 99, sched), TokenError::BelowMinimum,); // force_vested_transfer fails. assert_noop!( Vesting::force_vested_transfer(RawOrigin::Root.into(), 3, 99, sched), - pallet_balances::Error::::ExistentialDeposit, + TokenError::BelowMinimum, ); }); } diff --git a/frame/whitelist/src/mock.rs b/frame/whitelist/src/mock.rs index b16286863..d644cd661 100644 --- a/frame/whitelist/src/mock.rs +++ b/frame/whitelist/src/mock.rs @@ -86,6 +86,10 @@ impl pallet_balances::Config for Test { type ExistentialDeposit = ConstU64<1>; type AccountStore = System; type WeightInfo = (); + type FreezeIdentifier = (); + type MaxFreezes = (); + type HoldIdentifier = (); + type MaxHolds = (); } impl pallet_preimage::Config for Test { diff --git a/primitives/runtime/src/lib.rs b/primitives/runtime/src/lib.rs index dc03e074f..622eac3d8 100644 --- a/primitives/runtime/src/lib.rs +++ b/primitives/runtime/src/lib.rs @@ -606,9 +606,10 @@ impl From for DispatchError { #[cfg_attr(feature = "std", derive(Serialize, Deserialize))] pub enum TokenError { /// Funds are unavailable. - NoFunds, - /// Account that must exist would die. - WouldDie, + FundsUnavailable, + /// Some part of the balance gives the only provider reference to the account and thus cannot + /// be (re)moved. + OnlyProvider, /// Account cannot exist with the funds that would be given. BelowMinimum, /// Account cannot be created. @@ -619,18 +620,25 @@ pub enum TokenError { Frozen, /// Operation is not supported by the asset. Unsupported, + /// Account cannot be created for a held balance. + CannotCreateHold, + /// Withdrawal would cause unwanted loss of account. + NotExpendable, } impl From for &'static str { fn from(e: TokenError) -> &'static str { match e { - TokenError::NoFunds => "Funds are unavailable", - TokenError::WouldDie => "Account that must exist would die", + TokenError::FundsUnavailable => "Funds are unavailable", + TokenError::OnlyProvider => "Account that must exist would die", TokenError::BelowMinimum => "Account cannot exist with the funds that would be given", TokenError::CannotCreate => "Account cannot be created", TokenError::UnknownAsset => "The asset in question is unknown", TokenError::Frozen => "Funds exist but are frozen", TokenError::Unsupported => "Operation is not supported by the asset", + TokenError::CannotCreateHold => + "Account cannot be created for recording amount on hold", + TokenError::NotExpendable => "Account that is desired to remain would die", } } } @@ -994,8 +1002,8 @@ mod tests { Module(ModuleError { index: 2, error: [1, 0, 0, 0], message: None }), ConsumerRemaining, NoProviders, - Token(TokenError::NoFunds), - Token(TokenError::WouldDie), + Token(TokenError::FundsUnavailable), + Token(TokenError::OnlyProvider), Token(TokenError::BelowMinimum), Token(TokenError::CannotCreate), Token(TokenError::UnknownAsset), From 5f6cc69377b2bca3f123e598597a95f023d1b981 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 20 Mar 2023 12:27:43 +0000 Subject: [PATCH 258/558] Bump hashbrown from 0.12.3 to 0.13.2 (#13570) Bumps [hashbrown](https://github.com/rust-lang/hashbrown) from 0.12.3 to 0.13.2. - [Release notes](https://github.com/rust-lang/hashbrown/releases) - [Changelog](https://github.com/rust-lang/hashbrown/blob/master/CHANGELOG.md) - [Commits](https://github.com/rust-lang/hashbrown/compare/v0.12.3...v0.13.2) --- updated-dependencies: - dependency-name: hashbrown dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- Cargo.lock | 2 +- primitives/trie/Cargo.toml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index da100658b..3145d736f 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -10673,7 +10673,7 @@ dependencies = [ "array-bytes", "criterion", "hash-db", - "hashbrown 0.12.3", + "hashbrown 0.13.2", "lazy_static", "memory-db", "nohash-hasher", diff --git a/primitives/trie/Cargo.toml b/primitives/trie/Cargo.toml index 21582296b..0dacb76d9 100644 --- a/primitives/trie/Cargo.toml +++ b/primitives/trie/Cargo.toml @@ -20,7 +20,7 @@ harness = false [dependencies] ahash = { version = "0.8.2", optional = true } codec = { package = "parity-scale-codec", version = "3.2.2", default-features = false } -hashbrown = { version = "0.12.3", optional = true } +hashbrown = { version = "0.13.2", optional = true } hash-db = { version = "0.16.0", default-features = false } lazy_static = { version = "1.4.0", optional = true } memory-db = { version = "0.32.0", default-features = false } From 9795fa31435426137d91936a52cab8e50ebe2f50 Mon Sep 17 00:00:00 2001 From: Oliver Tale-Yazdi Date: Mon, 20 Mar 2023 13:34:36 +0100 Subject: [PATCH 259/558] Hotfix NIS benchmark (#13651) * Hotfix NIS rounding error Signed-off-by: Oliver Tale-Yazdi * Also fix communal Signed-off-by: Oliver Tale-Yazdi --------- Signed-off-by: Oliver Tale-Yazdi --- frame/nis/src/benchmarking.rs | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/frame/nis/src/benchmarking.rs b/frame/nis/src/benchmarking.rs index 0cc9e7421..10fee2abe 100644 --- a/frame/nis/src/benchmarking.rs +++ b/frame/nis/src/benchmarking.rs @@ -164,6 +164,9 @@ benchmarks! { Nis::::place_bid(RawOrigin::Signed(caller.clone()).into(), bid, 1)?; Nis::::process_queues(Perquintill::one(), 1, 2, &mut WeightCounter::unlimited()); frame_system::Pallet::::set_block_number(Receipts::::get(0).unwrap().expiry); + // FIXME: Ensure that the pallet has enough funding. This should already be the case, but + // a rounding error can cause it to fail. + T::Currency::set_balance(&Nis::::account_id(), BalanceOf::::max_value() / 10u32.into()); }: _(RawOrigin::Signed(caller.clone()), 0, None) verify { assert!(Receipts::::get(0).is_none()); @@ -182,6 +185,9 @@ benchmarks! { Nis::::process_queues(Perquintill::one(), 1, 2, &mut WeightCounter::unlimited()); frame_system::Pallet::::set_block_number(Receipts::::get(0).unwrap().expiry); Nis::::communify(RawOrigin::Signed(caller.clone()).into(), 0)?; + // FIXME: Ensure that the pallet has enough funding. This should already be the case, but + // a rounding error can cause it to fail. + T::Currency::set_balance(&Nis::::account_id(), BalanceOf::::max_value() / 10u32.into()); }: _(RawOrigin::Signed(caller.clone()), 0) verify { assert!(Receipts::::get(0).is_none()); From 97c68138e2da66bab9aabd072a92a42c9631f912 Mon Sep 17 00:00:00 2001 From: Mira Ressel Date: Wed, 15 Mar 2023 22:37:50 +0100 Subject: [PATCH 260/558] Bump ci-linux image for rust 1.68 --- .gitlab-ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 6cf062736..f9e74364a 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -48,7 +48,7 @@ variables: CARGO_INCREMENTAL: 0 DOCKER_OS: "debian:stretch" ARCH: "x86_64" - CI_IMAGE: "paritytech/ci-linux:production" + CI_IMAGE: "paritytech/ci-linux@sha256:7c76c3f9639f919447abbf9db535588178fde4df583d6926444d44cc20c094e6" # staging 2023-03-20 BUILDAH_IMAGE: "quay.io/buildah/stable:v1.27" RUSTY_CACHIER_SINGLE_BRANCH: master RUSTY_CACHIER_DONT_OPERATE_ON_MAIN_BRANCH: "true" From 46c06438bd457c42890be3102d5413c80c91a92f Mon Sep 17 00:00:00 2001 From: Mira Ressel Date: Wed, 15 Mar 2023 22:55:40 +0100 Subject: [PATCH 261/558] Update ui tests for rust 1.68 --- .../both_use_and_excluded_parts.stderr | 9 +++-- .../exclude_undefined_part.stderr | 9 +++-- .../no_std_genesis_config.stderr | 15 -------- .../old_unsupported_pallet_decl.stderr | 4 +-- .../undefined_genesis_config_part.stderr | 15 -------- .../undefined_inherent_part.stderr | 20 +++++------ .../undefined_origin_part.stderr | 18 ---------- .../undefined_validate_unsigned_part.stderr | 8 ++--- .../use_undefined_part.stderr | 9 +++-- .../test/tests/derive_no_bound_ui/eq.stderr | 20 +++++------ .../call_argument_invalid_bound_2.stderr | 14 +++----- .../genesis_default_not_satisfied.stderr | 18 +++++----- .../tests/pallet_ui/hooks_invalid_item.stderr | 20 +++++------ ...orage_result_query_missing_generics.stderr | 2 +- .../type_value_forgotten_where_clause.stderr | 18 +++++----- .../weight_argument_has_suffix.stderr | 34 ------------------- .../ui/impl_incorrect_method_signature.stderr | 8 ++--- ...reference_in_impl_runtime_apis_call.stderr | 4 +-- 18 files changed, 83 insertions(+), 162 deletions(-) diff --git a/frame/support/test/tests/construct_runtime_ui/both_use_and_excluded_parts.stderr b/frame/support/test/tests/construct_runtime_ui/both_use_and_excluded_parts.stderr index 5f1fccd43..b1c1879aa 100644 --- a/frame/support/test/tests/construct_runtime_ui/both_use_and_excluded_parts.stderr +++ b/frame/support/test/tests/construct_runtime_ui/both_use_and_excluded_parts.stderr @@ -8,9 +8,12 @@ error[E0412]: cannot find type `RuntimeCall` in this scope --> tests/construct_runtime_ui/both_use_and_excluded_parts.rs:18:64 | 18 | pub type UncheckedExtrinsic = generic::UncheckedExtrinsic; - | - ^^^^^^^^^^^ not found in this scope - | | - | help: you might be missing a type parameter: `` + | ^^^^^^^^^^^ not found in this scope + | +help: you might be missing a type parameter + | +18 | pub type UncheckedExtrinsic = generic::UncheckedExtrinsic; + | +++++++++++++ error[E0412]: cannot find type `Runtime` in this scope --> tests/construct_runtime_ui/both_use_and_excluded_parts.rs:20:25 diff --git a/frame/support/test/tests/construct_runtime_ui/exclude_undefined_part.stderr b/frame/support/test/tests/construct_runtime_ui/exclude_undefined_part.stderr index c623ecfbf..66098898b 100644 --- a/frame/support/test/tests/construct_runtime_ui/exclude_undefined_part.stderr +++ b/frame/support/test/tests/construct_runtime_ui/exclude_undefined_part.stderr @@ -8,9 +8,12 @@ error[E0412]: cannot find type `RuntimeCall` in this scope --> tests/construct_runtime_ui/exclude_undefined_part.rs:23:64 | 23 | pub type UncheckedExtrinsic = generic::UncheckedExtrinsic; - | - ^^^^^^^^^^^ not found in this scope - | | - | help: you might be missing a type parameter: `` + | ^^^^^^^^^^^ not found in this scope + | +help: you might be missing a type parameter + | +23 | pub type UncheckedExtrinsic = generic::UncheckedExtrinsic; + | +++++++++++++ error[E0412]: cannot find type `Runtime` in this scope --> tests/construct_runtime_ui/exclude_undefined_part.rs:25:25 diff --git a/frame/support/test/tests/construct_runtime_ui/no_std_genesis_config.stderr b/frame/support/test/tests/construct_runtime_ui/no_std_genesis_config.stderr index 2cf451aa6..cfc8c9264 100644 --- a/frame/support/test/tests/construct_runtime_ui/no_std_genesis_config.stderr +++ b/frame/support/test/tests/construct_runtime_ui/no_std_genesis_config.stderr @@ -29,18 +29,3 @@ help: consider importing this struct | 1 | use frame_system::GenesisConfig; | - -error[E0283]: type annotations needed - --> tests/construct_runtime_ui/no_std_genesis_config.rs:40:1 - | -40 | / construct_runtime! { -41 | | pub struct Runtime where -42 | | Block = Block, -43 | | NodeBlock = Block, -... | -48 | | } -49 | | } - | |_^ cannot infer type - | - = note: cannot satisfy `_: std::default::Default` - = note: this error originates in the derive macro `Default` (in Nightly builds, run with -Z macro-backtrace for more info) diff --git a/frame/support/test/tests/construct_runtime_ui/old_unsupported_pallet_decl.stderr b/frame/support/test/tests/construct_runtime_ui/old_unsupported_pallet_decl.stderr index 6029e7e6f..b1338e892 100644 --- a/frame/support/test/tests/construct_runtime_ui/old_unsupported_pallet_decl.stderr +++ b/frame/support/test/tests/construct_runtime_ui/old_unsupported_pallet_decl.stderr @@ -18,7 +18,7 @@ error: cannot find macro `decl_storage` in this scope 6 | decl_storage! { | ^^^^^^^^^^^^ | - = note: consider importing this macro: + = help: consider importing this macro: frame_support::decl_storage error: cannot find macro `decl_module` in this scope @@ -27,5 +27,5 @@ error: cannot find macro `decl_module` in this scope 10 | decl_module! { | ^^^^^^^^^^^ | - = note: consider importing this macro: + = help: consider importing this macro: frame_support::decl_module diff --git a/frame/support/test/tests/construct_runtime_ui/undefined_genesis_config_part.stderr b/frame/support/test/tests/construct_runtime_ui/undefined_genesis_config_part.stderr index 72099c1b9..f56780c0b 100644 --- a/frame/support/test/tests/construct_runtime_ui/undefined_genesis_config_part.stderr +++ b/frame/support/test/tests/construct_runtime_ui/undefined_genesis_config_part.stderr @@ -32,18 +32,3 @@ help: consider importing this struct | 1 | use frame_system::GenesisConfig; | - -error[E0283]: type annotations needed - --> tests/construct_runtime_ui/undefined_genesis_config_part.rs:49:1 - | -49 | / construct_runtime! { -50 | | pub struct Runtime where -51 | | Block = Block, -52 | | NodeBlock = Block, -... | -57 | | } -58 | | } - | |_^ cannot infer type - | - = note: cannot satisfy `_: std::default::Default` - = note: this error originates in the derive macro `Default` (in Nightly builds, run with -Z macro-backtrace for more info) diff --git a/frame/support/test/tests/construct_runtime_ui/undefined_inherent_part.stderr b/frame/support/test/tests/construct_runtime_ui/undefined_inherent_part.stderr index dab651426..180137913 100644 --- a/frame/support/test/tests/construct_runtime_ui/undefined_inherent_part.stderr +++ b/frame/support/test/tests/construct_runtime_ui/undefined_inherent_part.stderr @@ -15,7 +15,7 @@ error: `Pallet` does not have #[pallet::inherent] defined, perhaps you should re | = note: this error originates in the macro `pallet::__substrate_inherent_check::is_inherent_part_defined` which comes from the expansion of the macro `construct_runtime` (in Nightly builds, run with -Z macro-backtrace for more info) -error[E0599]: no function or associated item named `create_inherent` found for struct `pallet::Pallet` in the current scope +error[E0599]: no function or associated item named `create_inherent` found for struct `Pallet` in the current scope --> tests/construct_runtime_ui/undefined_inherent_part.rs:49:1 | 11 | pub struct Pallet(_); @@ -28,14 +28,14 @@ error[E0599]: no function or associated item named `create_inherent` found for s ... | 57 | | } 58 | | } - | |_^ function or associated item not found in `pallet::Pallet` + | |_^ function or associated item not found in `Pallet` | = help: items from traits can only be used if the trait is implemented and in scope = note: the following trait defines an item `create_inherent`, perhaps you need to implement it: candidate #1: `ProvideInherent` = note: this error originates in the macro `construct_runtime` (in Nightly builds, run with -Z macro-backtrace for more info) -error[E0599]: no function or associated item named `is_inherent` found for struct `pallet::Pallet` in the current scope +error[E0599]: no function or associated item named `is_inherent` found for struct `Pallet` in the current scope --> tests/construct_runtime_ui/undefined_inherent_part.rs:49:1 | 11 | pub struct Pallet(_); @@ -48,14 +48,14 @@ error[E0599]: no function or associated item named `is_inherent` found for struc ... | 57 | | } 58 | | } - | |_^ function or associated item not found in `pallet::Pallet` + | |_^ function or associated item not found in `Pallet` | = help: items from traits can only be used if the trait is implemented and in scope = note: the following trait defines an item `is_inherent`, perhaps you need to implement it: candidate #1: `ProvideInherent` = note: this error originates in the macro `construct_runtime` (in Nightly builds, run with -Z macro-backtrace for more info) -error[E0599]: no function or associated item named `check_inherent` found for struct `pallet::Pallet` in the current scope +error[E0599]: no function or associated item named `check_inherent` found for struct `Pallet` in the current scope --> tests/construct_runtime_ui/undefined_inherent_part.rs:49:1 | 11 | pub struct Pallet(_); @@ -68,14 +68,14 @@ error[E0599]: no function or associated item named `check_inherent` found for st ... | 57 | | } 58 | | } - | |_^ function or associated item not found in `pallet::Pallet` + | |_^ function or associated item not found in `Pallet` | = help: items from traits can only be used if the trait is implemented and in scope = note: the following trait defines an item `check_inherent`, perhaps you need to implement it: candidate #1: `ProvideInherent` = note: this error originates in the macro `construct_runtime` (in Nightly builds, run with -Z macro-backtrace for more info) -error[E0599]: no associated item named `INHERENT_IDENTIFIER` found for struct `pallet::Pallet` in the current scope +error[E0599]: no associated item named `INHERENT_IDENTIFIER` found for struct `Pallet` in the current scope --> tests/construct_runtime_ui/undefined_inherent_part.rs:49:1 | 11 | pub struct Pallet(_); @@ -88,14 +88,14 @@ error[E0599]: no associated item named `INHERENT_IDENTIFIER` found for struct `p ... | 57 | | } 58 | | } - | |_^ associated item not found in `pallet::Pallet` + | |_^ associated item not found in `Pallet` | = help: items from traits can only be used if the trait is implemented and in scope = note: the following trait defines an item `INHERENT_IDENTIFIER`, perhaps you need to implement it: candidate #1: `ProvideInherent` = note: this error originates in the macro `construct_runtime` (in Nightly builds, run with -Z macro-backtrace for more info) -error[E0599]: no function or associated item named `is_inherent_required` found for struct `pallet::Pallet` in the current scope +error[E0599]: no function or associated item named `is_inherent_required` found for struct `Pallet` in the current scope --> tests/construct_runtime_ui/undefined_inherent_part.rs:49:1 | 11 | pub struct Pallet(_); @@ -108,7 +108,7 @@ error[E0599]: no function or associated item named `is_inherent_required` found ... | 57 | | } 58 | | } - | |_^ function or associated item not found in `pallet::Pallet` + | |_^ function or associated item not found in `Pallet` | = help: items from traits can only be used if the trait is implemented and in scope = note: the following trait defines an item `is_inherent_required`, perhaps you need to implement it: diff --git a/frame/support/test/tests/construct_runtime_ui/undefined_origin_part.stderr b/frame/support/test/tests/construct_runtime_ui/undefined_origin_part.stderr index dd1bdf761..dbade9fed 100644 --- a/frame/support/test/tests/construct_runtime_ui/undefined_origin_part.stderr +++ b/frame/support/test/tests/construct_runtime_ui/undefined_origin_part.stderr @@ -32,21 +32,3 @@ help: consider importing this type alias | 1 | use frame_system::Origin; | - -error[E0282]: type annotations needed - --> tests/construct_runtime_ui/undefined_origin_part.rs:49:1 - | -49 | / construct_runtime! { -50 | | pub struct Runtime where -51 | | Block = Block, -52 | | NodeBlock = Block, -... | -57 | | } -58 | | } - | |_^ cannot infer type of the type parameter `AccountId` declared on the enum `RawOrigin` - | - = note: this error originates in the macro `construct_runtime` (in Nightly builds, run with -Z macro-backtrace for more info) -help: consider specifying the generic argument - | -58 | }:: - | +++++++++++++ diff --git a/frame/support/test/tests/construct_runtime_ui/undefined_validate_unsigned_part.stderr b/frame/support/test/tests/construct_runtime_ui/undefined_validate_unsigned_part.stderr index 14106ddd0..fa52ce227 100644 --- a/frame/support/test/tests/construct_runtime_ui/undefined_validate_unsigned_part.stderr +++ b/frame/support/test/tests/construct_runtime_ui/undefined_validate_unsigned_part.stderr @@ -29,7 +29,7 @@ error[E0599]: no variant or associated item named `Pallet` found for enum `Runti 58 | | } | |_- variant or associated item `Pallet` not found for this enum -error[E0599]: no function or associated item named `pre_dispatch` found for struct `pallet::Pallet` in the current scope +error[E0599]: no function or associated item named `pre_dispatch` found for struct `Pallet` in the current scope --> tests/construct_runtime_ui/undefined_validate_unsigned_part.rs:49:1 | 11 | pub struct Pallet(_); @@ -42,7 +42,7 @@ error[E0599]: no function or associated item named `pre_dispatch` found for stru ... | 57 | | } 58 | | } - | |_^ function or associated item not found in `pallet::Pallet` + | |_^ function or associated item not found in `Pallet` | = help: items from traits can only be used if the trait is implemented and in scope = note: the following traits define an item `pre_dispatch`, perhaps you need to implement one of them: @@ -50,7 +50,7 @@ error[E0599]: no function or associated item named `pre_dispatch` found for stru candidate #2: `ValidateUnsigned` = note: this error originates in the macro `construct_runtime` (in Nightly builds, run with -Z macro-backtrace for more info) -error[E0599]: no function or associated item named `validate_unsigned` found for struct `pallet::Pallet` in the current scope +error[E0599]: no function or associated item named `validate_unsigned` found for struct `Pallet` in the current scope --> tests/construct_runtime_ui/undefined_validate_unsigned_part.rs:49:1 | 11 | pub struct Pallet(_); @@ -63,7 +63,7 @@ error[E0599]: no function or associated item named `validate_unsigned` found for ... | 57 | | } 58 | | } - | |_^ function or associated item not found in `pallet::Pallet` + | |_^ function or associated item not found in `Pallet` | = help: items from traits can only be used if the trait is implemented and in scope = note: the following traits define an item `validate_unsigned`, perhaps you need to implement one of them: diff --git a/frame/support/test/tests/construct_runtime_ui/use_undefined_part.stderr b/frame/support/test/tests/construct_runtime_ui/use_undefined_part.stderr index e289c75fb..cb6b6a44d 100644 --- a/frame/support/test/tests/construct_runtime_ui/use_undefined_part.stderr +++ b/frame/support/test/tests/construct_runtime_ui/use_undefined_part.stderr @@ -8,9 +8,12 @@ error[E0412]: cannot find type `RuntimeCall` in this scope --> tests/construct_runtime_ui/use_undefined_part.rs:23:64 | 23 | pub type UncheckedExtrinsic = generic::UncheckedExtrinsic; - | - ^^^^^^^^^^^ not found in this scope - | | - | help: you might be missing a type parameter: `` + | ^^^^^^^^^^^ not found in this scope + | +help: you might be missing a type parameter + | +23 | pub type UncheckedExtrinsic = generic::UncheckedExtrinsic; + | +++++++++++++ error[E0412]: cannot find type `Runtime` in this scope --> tests/construct_runtime_ui/use_undefined_part.rs:25:25 diff --git a/frame/support/test/tests/derive_no_bound_ui/eq.stderr b/frame/support/test/tests/derive_no_bound_ui/eq.stderr index 97ac29f96..eb3345eed 100644 --- a/frame/support/test/tests/derive_no_bound_ui/eq.stderr +++ b/frame/support/test/tests/derive_no_bound_ui/eq.stderr @@ -1,12 +1,12 @@ error[E0277]: can't compare `Foo` with `Foo` - --> tests/derive_no_bound_ui/eq.rs:6:8 - | -6 | struct Foo { - | ^^^ no implementation for `Foo == Foo` - | - = help: the trait `PartialEq` is not implemented for `Foo` + --> tests/derive_no_bound_ui/eq.rs:6:8 + | +6 | struct Foo { + | ^^^^^^^^^^^^^^ no implementation for `Foo == Foo` + | + = help: the trait `PartialEq` is not implemented for `Foo` note: required by a bound in `std::cmp::Eq` - --> $RUST/core/src/cmp.rs - | - | pub trait Eq: PartialEq { - | ^^^^^^^^^^^^^^^ required by this bound in `std::cmp::Eq` + --> $RUST/core/src/cmp.rs + | + | pub trait Eq: PartialEq { + | ^^^^^^^^^^^^^^^ required by this bound in `Eq` diff --git a/frame/support/test/tests/pallet_ui/call_argument_invalid_bound_2.stderr b/frame/support/test/tests/pallet_ui/call_argument_invalid_bound_2.stderr index 3e2e70432..476258c03 100644 --- a/frame/support/test/tests/pallet_ui/call_argument_invalid_bound_2.stderr +++ b/frame/support/test/tests/pallet_ui/call_argument_invalid_bound_2.stderr @@ -23,17 +23,11 @@ error[E0369]: binary operation `==` cannot be applied to type `&::Bar: WrapperTypeEncode` is not satisfied --> tests/pallet_ui/call_argument_invalid_bound_2.rs:21:36 | -1 | / #[frame_support::pallet] -2 | | mod pallet { -3 | | use frame_support::pallet_prelude::{Hooks, DispatchResultWithPostInfo}; -4 | | use frame_system::pallet_prelude::{BlockNumberFor, OriginFor}; -... | -16 | | -17 | | #[pallet::call] - | |__________________- required by a bound introduced by this call +1 | #[frame_support::pallet] + | ------------------------ required by a bound introduced by this call ... -21 | pub fn foo(origin: OriginFor, _bar: T::Bar) -> DispatchResultWithPostInfo { - | ^^^^ the trait `WrapperTypeEncode` is not implemented for `::Bar` +21 | pub fn foo(origin: OriginFor, _bar: T::Bar) -> DispatchResultWithPostInfo { + | ^^^^ the trait `WrapperTypeEncode` is not implemented for `::Bar` | = note: required for `::Bar` to implement `Encode` diff --git a/frame/support/test/tests/pallet_ui/genesis_default_not_satisfied.stderr b/frame/support/test/tests/pallet_ui/genesis_default_not_satisfied.stderr index 809039285..22b5ce941 100644 --- a/frame/support/test/tests/pallet_ui/genesis_default_not_satisfied.stderr +++ b/frame/support/test/tests/pallet_ui/genesis_default_not_satisfied.stderr @@ -1,14 +1,14 @@ error[E0277]: the trait bound `pallet::GenesisConfig: std::default::Default` is not satisfied - --> tests/pallet_ui/genesis_default_not_satisfied.rs:22:18 - | -22 | impl GenesisBuild for GenesisConfig {} - | ^^^^^^^^^^^^^^^ the trait `std::default::Default` is not implemented for `pallet::GenesisConfig` - | + --> tests/pallet_ui/genesis_default_not_satisfied.rs:22:38 + | +22 | impl GenesisBuild for GenesisConfig {} + | ^^^^^^^^^^^^^ the trait `std::default::Default` is not implemented for `pallet::GenesisConfig` + | note: required by a bound in `GenesisBuild` - --> $WORKSPACE/frame/support/src/traits/hooks.rs - | - | pub trait GenesisBuild: Default + sp_runtime::traits::MaybeSerializeDeserialize { - | ^^^^^^^ required by this bound in `GenesisBuild` + --> $WORKSPACE/frame/support/src/traits/hooks.rs + | + | pub trait GenesisBuild: Default + sp_runtime::traits::MaybeSerializeDeserialize { + | ^^^^^^^ required by this bound in `GenesisBuild` help: consider annotating `pallet::GenesisConfig` with `#[derive(Default)]` | 19 | #[derive(Default)] diff --git a/frame/support/test/tests/pallet_ui/hooks_invalid_item.stderr b/frame/support/test/tests/pallet_ui/hooks_invalid_item.stderr index ff52a094d..9c30179bc 100644 --- a/frame/support/test/tests/pallet_ui/hooks_invalid_item.stderr +++ b/frame/support/test/tests/pallet_ui/hooks_invalid_item.stderr @@ -1,15 +1,15 @@ error[E0107]: missing generics for trait `Hooks` - --> tests/pallet_ui/hooks_invalid_item.rs:12:18 - | -12 | impl Hooks for Pallet {} - | ^^^^^ expected 1 generic argument - | + --> tests/pallet_ui/hooks_invalid_item.rs:12:18 + | +12 | impl Hooks for Pallet {} + | ^^^^^ expected 1 generic argument + | note: trait defined here, with 1 generic parameter: `BlockNumber` - --> $WORKSPACE/frame/support/src/traits/hooks.rs - | - | pub trait Hooks { - | ^^^^^ ----------- + --> $WORKSPACE/frame/support/src/traits/hooks.rs + | + | pub trait Hooks { + | ^^^^^ ----------- help: add missing generic argument | 12 | impl Hooks for Pallet {} - | ~~~~~~~~~~~~~~~~~~ + | +++++++++++++ diff --git a/frame/support/test/tests/pallet_ui/storage_result_query_missing_generics.stderr b/frame/support/test/tests/pallet_ui/storage_result_query_missing_generics.stderr index 98265462b..9e63fd03d 100644 --- a/frame/support/test/tests/pallet_ui/storage_result_query_missing_generics.stderr +++ b/frame/support/test/tests/pallet_ui/storage_result_query_missing_generics.stderr @@ -12,4 +12,4 @@ note: enum defined here, with 1 generic parameter: `T` help: add missing generic argument | 17 | type Foo = StorageValue<_, u8, ResultQuery::NonExistentValue>>; - | ~~~~~~~~ + | +++ diff --git a/frame/support/test/tests/pallet_ui/type_value_forgotten_where_clause.stderr b/frame/support/test/tests/pallet_ui/type_value_forgotten_where_clause.stderr index 6288dcd53..4b4c78b14 100644 --- a/frame/support/test/tests/pallet_ui/type_value_forgotten_where_clause.stderr +++ b/frame/support/test/tests/pallet_ui/type_value_forgotten_where_clause.stderr @@ -1,52 +1,52 @@ error[E0277]: the trait bound `::AccountId: From` is not satisfied - --> $DIR/type_value_forgotten_where_clause.rs:24:34 + --> tests/pallet_ui/type_value_forgotten_where_clause.rs:24:34 | 24 | #[pallet::type_value] fn Foo() -> u32 { 3u32 } | ^^^^^^ the trait `From` is not implemented for `::AccountId` | note: required by a bound in `pallet::Config` - --> $DIR/type_value_forgotten_where_clause.rs:8:51 + --> tests/pallet_ui/type_value_forgotten_where_clause.rs:8:51 | 7 | pub trait Config: frame_system::Config | ------ required by a bound in this 8 | where ::AccountId: From - | ^^^^^^^^^ required by this bound in `pallet::Config` + | ^^^^^^^^^ required by this bound in `Config` help: consider further restricting the associated type | 24 | #[pallet::type_value] fn Foo() -> u32 where ::AccountId: From { 3u32 } | +++++++++++++++++++++++++++++++++++++++++++++++++++++++ error[E0277]: the trait bound `::AccountId: From` is not satisfied - --> $DIR/type_value_forgotten_where_clause.rs:24:12 + --> tests/pallet_ui/type_value_forgotten_where_clause.rs:24:12 | 24 | #[pallet::type_value] fn Foo() -> u32 { 3u32 } | ^^^^^^^^^^ the trait `From` is not implemented for `::AccountId` | note: required by a bound in `pallet::Config` - --> $DIR/type_value_forgotten_where_clause.rs:8:51 + --> tests/pallet_ui/type_value_forgotten_where_clause.rs:8:51 | 7 | pub trait Config: frame_system::Config | ------ required by a bound in this 8 | where ::AccountId: From - | ^^^^^^^^^ required by this bound in `pallet::Config` + | ^^^^^^^^^ required by this bound in `Config` help: consider further restricting the associated type | 24 | #[pallet::type_value where ::AccountId: From] fn Foo() -> u32 { 3u32 } | +++++++++++++++++++++++++++++++++++++++++++++++++++++++ error[E0277]: the trait bound `::AccountId: From` is not satisfied - --> $DIR/type_value_forgotten_where_clause.rs:24:12 + --> tests/pallet_ui/type_value_forgotten_where_clause.rs:24:12 | 24 | #[pallet::type_value] fn Foo() -> u32 { 3u32 } | ^^^^^^^^^^ the trait `From` is not implemented for `::AccountId` | note: required by a bound in `pallet::Config` - --> $DIR/type_value_forgotten_where_clause.rs:8:51 + --> tests/pallet_ui/type_value_forgotten_where_clause.rs:8:51 | 7 | pub trait Config: frame_system::Config | ------ required by a bound in this 8 | where ::AccountId: From - | ^^^^^^^^^ required by this bound in `pallet::Config` + | ^^^^^^^^^ required by this bound in `Config` help: consider further restricting the associated type | 24 | #[pallet::type_value] fn Foo() -> u32 where ::AccountId: From { 3u32 } diff --git a/frame/support/test/tests/pallet_ui/weight_argument_has_suffix.stderr b/frame/support/test/tests/pallet_ui/weight_argument_has_suffix.stderr index c9b2010f8..df9b0798a 100644 --- a/frame/support/test/tests/pallet_ui/weight_argument_has_suffix.stderr +++ b/frame/support/test/tests/pallet_ui/weight_argument_has_suffix.stderr @@ -5,37 +5,3 @@ error: invalid suffix `something` for number literal | ^^^^^^^^^^^^^^^ invalid suffix `something` | = help: the suffix must be one of the numeric types (`u32`, `isize`, `f32`, etc.) - -error[E0308]: mismatched types - --> tests/pallet_ui/weight_argument_has_suffix.rs:12:12 - | -12 | #[pallet::call] - | ^^^^ - | | - | expected trait `frame_support::dispatch::ClassifyDispatch`, found trait `frame_support::dispatch::WeighData` - | arguments to this function are incorrect - | - = note: expected reference `&dyn frame_support::dispatch::ClassifyDispatch<()>` - found reference `&dyn frame_support::dispatch::WeighData<()>` -note: associated function defined here - --> $WORKSPACE/frame/support/src/dispatch.rs - | - | fn classify_dispatch(&self, target: T) -> DispatchClass; - | ^^^^^^^^^^^^^^^^^ - -error[E0308]: mismatched types - --> tests/pallet_ui/weight_argument_has_suffix.rs:12:12 - | -12 | #[pallet::call] - | ^^^^ - | | - | expected trait `frame_support::dispatch::PaysFee`, found trait `frame_support::dispatch::WeighData` - | arguments to this function are incorrect - | - = note: expected reference `&dyn frame_support::dispatch::PaysFee<()>` - found reference `&dyn frame_support::dispatch::WeighData<()>` -note: associated function defined here - --> $WORKSPACE/frame/support/src/dispatch.rs - | - | fn pays_fee(&self, _target: T) -> Pays; - | ^^^^^^^^ diff --git a/primitives/api/test/tests/ui/impl_incorrect_method_signature.stderr b/primitives/api/test/tests/ui/impl_incorrect_method_signature.stderr index f5b6ac1da..5b3624822 100644 --- a/primitives/api/test/tests/ui/impl_incorrect_method_signature.stderr +++ b/primitives/api/test/tests/ui/impl_incorrect_method_signature.stderr @@ -4,7 +4,7 @@ error[E0053]: method `test` has an incompatible type for trait 19 | fn test(data: String) {} | ^^^^^^ | | - | expected `u64`, found struct `std::string::String` + | expected `u64`, found struct `String` | help: change the parameter type to match the trait: `u64` | note: type in trait @@ -12,8 +12,8 @@ note: type in trait | 13 | fn test(data: u64); | ^^^ - = note: expected fn pointer `fn(u64)` - found fn pointer `fn(std::string::String)` + = note: expected signature `fn(u64)` + found signature `fn(std::string::String)` error[E0308]: mismatched types --> tests/ui/impl_incorrect_method_signature.rs:19:11 @@ -21,7 +21,7 @@ error[E0308]: mismatched types 17 | / sp_api::impl_runtime_apis! { 18 | | impl self::Api for Runtime { 19 | | fn test(data: String) {} - | | ^^^^ expected `u64`, found struct `std::string::String` + | | ^^^^ expected `u64`, found struct `String` 20 | | } ... | 32 | | } diff --git a/primitives/api/test/tests/ui/type_reference_in_impl_runtime_apis_call.stderr b/primitives/api/test/tests/ui/type_reference_in_impl_runtime_apis_call.stderr index 06f8226ec..1515bd3a1 100644 --- a/primitives/api/test/tests/ui/type_reference_in_impl_runtime_apis_call.stderr +++ b/primitives/api/test/tests/ui/type_reference_in_impl_runtime_apis_call.stderr @@ -12,8 +12,8 @@ note: type in trait | 13 | fn test(data: u64); | ^^^ - = note: expected fn pointer `fn(u64)` - found fn pointer `fn(&u64)` + = note: expected signature `fn(u64)` + found signature `fn(&u64)` error[E0308]: mismatched types --> tests/ui/type_reference_in_impl_runtime_apis_call.rs:19:11 From ba9582568d5550c50adc9f5e3626f06eb8a5133b Mon Sep 17 00:00:00 2001 From: Mira Ressel Date: Thu, 16 Mar 2023 15:38:06 +0100 Subject: [PATCH 262/558] address clippy & rustdoc warnings The comment was out of date anyway, State::Live no longer takes a snapshot_path argument. --- client/state-db/src/lib.rs | 2 +- primitives/runtime/src/offchain/http.rs | 2 +- primitives/trie/src/lib.rs | 7 ++----- utils/frame/try-runtime/cli/src/lib.rs | 2 -- 4 files changed, 4 insertions(+), 9 deletions(-) diff --git a/client/state-db/src/lib.rs b/client/state-db/src/lib.rs index 5ebfbaad6..b41240d62 100644 --- a/client/state-db/src/lib.rs +++ b/client/state-db/src/lib.rs @@ -406,7 +406,7 @@ impl StateDbSync { } fn prune(&mut self, commit: &mut CommitSet) -> Result<(), Error> { - if let (&mut Some(ref mut pruning), &PruningMode::Constrained(ref constraints)) = + if let (&mut Some(ref mut pruning), PruningMode::Constrained(constraints)) = (&mut self.pruning, &self.mode) { loop { diff --git a/primitives/runtime/src/offchain/http.rs b/primitives/runtime/src/offchain/http.rs index 0229203c1..25e5c1007 100644 --- a/primitives/runtime/src/offchain/http.rs +++ b/primitives/runtime/src/offchain/http.rs @@ -450,7 +450,7 @@ impl Headers { /// and collect them on your own. pub fn find(&self, name: &str) -> Option<&str> { let raw = name.as_bytes(); - for &(ref key, ref val) in &self.raw { + for (key, val) in &self.raw { if &**key == raw { return str::from_utf8(val).ok() } diff --git a/primitives/trie/src/lib.rs b/primitives/trie/src/lib.rs index 175fb32d4..941554585 100644 --- a/primitives/trie/src/lib.rs +++ b/primitives/trie/src/lib.rs @@ -647,7 +647,7 @@ mod tests { count: 1000, }; let mut d = st.make(); - d.sort_by(|&(ref a, _), &(ref b, _)| a.cmp(b)); + d.sort_by(|(a, _), (b, _)| a.cmp(b)); let dr = d.iter().map(|v| (&v.0[..], &v.1[..])).collect(); check_input(&dr); } @@ -716,10 +716,7 @@ mod tests { t } - fn unpopulate_trie<'db, T: TrieConfiguration>( - t: &mut TrieDBMut<'db, T>, - v: &[(Vec, Vec)], - ) { + fn unpopulate_trie(t: &mut TrieDBMut<'_, T>, v: &[(Vec, Vec)]) { for i in v { let key: &[u8] = &i.0; t.remove(key).unwrap(); diff --git a/utils/frame/try-runtime/cli/src/lib.rs b/utils/frame/try-runtime/cli/src/lib.rs index e1d75064a..bd914d3a2 100644 --- a/utils/frame/try-runtime/cli/src/lib.rs +++ b/utils/frame/try-runtime/cli/src/lib.rs @@ -599,8 +599,6 @@ pub struct LiveState { #[derive(Debug, Clone, clap::Subcommand)] pub enum State { /// Use a state snapshot as the source of runtime state. - /// - /// This can be crated by passing a value to [`State::Live::snapshot_path`]. Snap { #[arg(short, long)] snapshot_path: PathBuf, From 9d2d020c5134d8c7870396cb04bca63a674abfd5 Mon Sep 17 00:00:00 2001 From: Davide Galassi Date: Mon, 20 Mar 2023 19:21:26 +0100 Subject: [PATCH 263/558] Keystore overhaul (iter 2) (#13634) * Remove bloat about remote keystore * Update docs and remove unused 'KeystoreRef' trait * Use wherever possible, MemoryKeystore for testing * Remove unrequired fully qualified method syntax for Keystore --- Cargo.lock | 1 - bin/node-template/node/src/service.rs | 24 +----- bin/node/cli/benches/block_production.rs | 1 - bin/node/cli/benches/transaction_pool.rs | 1 - bin/node/cli/src/service.rs | 35 +++++---- bin/node/executor/tests/submit_transaction.rs | 63 ++++++--------- bin/utils/chain-spec-builder/src/main.rs | 12 +-- .../authority-discovery/src/worker/tests.rs | 4 +- client/cli/src/commands/insert_key.rs | 11 +-- client/cli/src/config.rs | 7 +- client/cli/src/params/keystore_params.rs | 6 +- client/cli/src/runner.rs | 1 - client/consensus/aura/src/lib.rs | 49 ++++++------ client/consensus/babe/rpc/Cargo.toml | 1 - client/consensus/babe/rpc/src/lib.rs | 29 +++---- client/consensus/babe/src/authorship.rs | 32 +++----- client/consensus/babe/src/lib.rs | 25 +++--- client/consensus/babe/src/tests.rs | 19 +++-- .../beefy/src/communication/gossip.rs | 16 ++-- client/consensus/beefy/src/keystore.rs | 61 +++++++-------- client/consensus/beefy/src/tests.rs | 7 +- client/consensus/grandpa/src/lib.rs | 4 +- client/consensus/grandpa/src/tests.rs | 7 +- client/keystore/src/local.rs | 43 ++++++----- client/rpc/src/author/mod.rs | 9 ++- client/rpc/src/author/tests.rs | 8 +- client/service/src/builder.rs | 60 +++------------ client/service/src/config.rs | 2 - client/service/test/src/lib.rs | 1 - frame/benchmarking/src/baseline.rs | 5 +- frame/contracts/src/tests.rs | 4 +- frame/examples/offchain-worker/src/tests.rs | 44 ++++------- frame/nfts/src/mock.rs | 4 +- .../application-crypto/test/src/ecdsa.rs | 2 +- .../application-crypto/test/src/ed25519.rs | 2 +- .../application-crypto/test/src/sr25519.rs | 2 +- primitives/consensus/beefy/src/commitment.rs | 12 +-- primitives/consensus/beefy/src/witness.rs | 13 +--- primitives/consensus/grandpa/src/lib.rs | 18 ++--- primitives/io/src/lib.rs | 77 +++++++++---------- primitives/keystore/src/lib.rs | 9 ++- primitives/keystore/src/testing.rs | 28 +++---- .../benchmarking-cli/src/pallet/command.rs | 7 +- utils/frame/try-runtime/cli/src/lib.rs | 3 +- 44 files changed, 312 insertions(+), 457 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 3145d736f..492887356 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -8438,7 +8438,6 @@ dependencies = [ "sp-keystore", "sp-runtime", "substrate-test-runtime-client", - "tempfile", "thiserror", "tokio", ] diff --git a/bin/node-template/node/src/service.rs b/bin/node-template/node/src/service.rs index 40ad80c7b..dfbb5e963 100644 --- a/bin/node-template/node/src/service.rs +++ b/bin/node-template/node/src/service.rs @@ -5,7 +5,6 @@ use sc_client_api::BlockBackend; use sc_consensus_aura::{ImportQueueParams, SlotProportion, StartAuraParams}; use sc_consensus_grandpa::SharedVoterState; pub use sc_executor::NativeElseWasmExecutor; -use sc_keystore::LocalKeystore; use sc_service::{error::Error as ServiceError, Configuration, TaskManager, WarpSyncParams}; use sc_telemetry::{Telemetry, TelemetryWorker}; use sp_consensus_aura::sr25519::AuthorityPair as AuraPair; @@ -58,10 +57,6 @@ pub fn new_partial( >, ServiceError, > { - if config.keystore_remote.is_some() { - return Err(ServiceError::Other("Remote Keystores are not supported.".into())) - } - let telemetry = config .telemetry_endpoints .clone() @@ -147,13 +142,6 @@ pub fn new_partial( }) } -fn remote_keystore(_url: &String) -> Result, &'static str> { - // FIXME: here would the concrete keystore be built, - // must return a concrete type (NOT `LocalKeystore`) that - // implements `Keystore` - Err("Remote Keystore not supported.") -} - /// Builds a new service for a full client. pub fn new_full(mut config: Configuration) -> Result { let sc_service::PartialComponents { @@ -161,22 +149,12 @@ pub fn new_full(mut config: Configuration) -> Result backend, mut task_manager, import_queue, - mut keystore_container, + keystore_container, select_chain, transaction_pool, other: (block_import, grandpa_link, mut telemetry), } = new_partial(&config)?; - if let Some(url) = &config.keystore_remote { - match remote_keystore(url) { - Ok(k) => keystore_container.set_remote_keystore(k), - Err(e) => - return Err(ServiceError::Other(format!( - "Error hooking up remote keystore for {}: {}", - url, e - ))), - }; - } let grandpa_protocol_name = sc_consensus_grandpa::protocol_standard_name( &client.block_hash(0).ok().flatten().expect("Genesis block exists; qed"), &config.chain_spec, diff --git a/bin/node/cli/benches/block_production.rs b/bin/node/cli/benches/block_production.rs index f1c65955e..c7f0cd20e 100644 --- a/bin/node/cli/benches/block_production.rs +++ b/bin/node/cli/benches/block_production.rs @@ -69,7 +69,6 @@ fn new_node(tokio_handle: Handle) -> node_cli::service::NewFullBase { transaction_pool: Default::default(), network: network_config, keystore: KeystoreConfig::InMemory, - keystore_remote: Default::default(), database: DatabaseSource::RocksDb { path: root.join("db"), cache_size: 128 }, trie_cache_maximum_size: Some(64 * 1024 * 1024), state_pruning: Some(PruningMode::ArchiveAll), diff --git a/bin/node/cli/benches/transaction_pool.rs b/bin/node/cli/benches/transaction_pool.rs index 72c1c8160..7488ec033 100644 --- a/bin/node/cli/benches/transaction_pool.rs +++ b/bin/node/cli/benches/transaction_pool.rs @@ -64,7 +64,6 @@ fn new_node(tokio_handle: Handle) -> node_cli::service::NewFullBase { }, network: network_config, keystore: KeystoreConfig::InMemory, - keystore_remote: Default::default(), database: DatabaseSource::RocksDb { path: root.join("db"), cache_size: 128 }, trie_cache_maximum_size: Some(64 * 1024 * 1024), state_pruning: Some(PruningMode::ArchiveAll), diff --git a/bin/node/cli/src/service.rs b/bin/node/cli/src/service.rs index d32223339..be8553b7b 100644 --- a/bin/node/cli/src/service.rs +++ b/bin/node/cli/src/service.rs @@ -595,7 +595,7 @@ mod tests { use sp_core::{crypto::Pair as CryptoPair, Public}; use sp_inherents::InherentDataProvider; use sp_keyring::AccountKeyring; - use sp_keystore::{Keystore, KeystorePtr}; + use sp_keystore::KeystorePtr; use sp_runtime::{ generic::{Digest, Era, SignedPayload}, key_types::BABE, @@ -615,12 +615,13 @@ mod tests { sp_tracing::try_init_simple(); let keystore_path = tempfile::tempdir().expect("Creates keystore path"); - let keystore: KeystorePtr = - Arc::new(LocalKeystore::open(keystore_path.path(), None).expect("Creates keystore")); - let alice: sp_consensus_babe::AuthorityId = - Keystore::sr25519_generate_new(&*keystore, BABE, Some("//Alice")) - .expect("Creates authority pair") - .into(); + let keystore: KeystorePtr = LocalKeystore::open(keystore_path.path(), None) + .expect("Creates keystore") + .into(); + let alice: sp_consensus_babe::AuthorityId = keystore + .sr25519_generate_new(BABE, Some("//Alice")) + .expect("Creates authority pair") + .into(); let chain_spec = crate::chain_spec::tests::integration_test_config_with_single_authority(); @@ -735,16 +736,16 @@ mod tests { // sign the pre-sealed hash of the block and then // add it to a digest item. let to_sign = pre_hash.encode(); - let signature = Keystore::sign_with( - &*keystore, - sp_consensus_babe::AuthorityId::ID, - &alice.to_public_crypto_pair(), - &to_sign, - ) - .unwrap() - .unwrap() - .try_into() - .unwrap(); + let signature = keystore + .sign_with( + sp_consensus_babe::AuthorityId::ID, + &alice.to_public_crypto_pair(), + &to_sign, + ) + .unwrap() + .unwrap() + .try_into() + .unwrap(); let item = ::babe_seal(signature); slot += 1; diff --git a/bin/node/executor/tests/submit_transaction.rs b/bin/node/executor/tests/submit_transaction.rs index a3d6681fb..9bff415d2 100644 --- a/bin/node/executor/tests/submit_transaction.rs +++ b/bin/node/executor/tests/submit_transaction.rs @@ -22,7 +22,6 @@ use sp_application_crypto::AppKey; use sp_core::offchain::{testing::TestTransactionPoolExt, TransactionPoolExt}; use sp_keyring::sr25519::Keyring::Alice; use sp_keystore::{testing::MemoryKeystore, Keystore, KeystoreExt}; -use std::sync::Arc; pub mod common; use self::common::*; @@ -63,25 +62,16 @@ fn should_submit_signed_transaction() { t.register_extension(TransactionPoolExt::new(pool)); let keystore = MemoryKeystore::new(); - Keystore::sr25519_generate_new( - &keystore, - sr25519::AuthorityId::ID, - Some(&format!("{}/hunter1", PHRASE)), - ) - .unwrap(); - Keystore::sr25519_generate_new( - &keystore, - sr25519::AuthorityId::ID, - Some(&format!("{}/hunter2", PHRASE)), - ) - .unwrap(); - Keystore::sr25519_generate_new( - &keystore, - sr25519::AuthorityId::ID, - Some(&format!("{}/hunter3", PHRASE)), - ) - .unwrap(); - t.register_extension(KeystoreExt(Arc::new(keystore))); + keystore + .sr25519_generate_new(sr25519::AuthorityId::ID, Some(&format!("{}/hunter1", PHRASE))) + .unwrap(); + keystore + .sr25519_generate_new(sr25519::AuthorityId::ID, Some(&format!("{}/hunter2", PHRASE))) + .unwrap(); + keystore + .sr25519_generate_new(sr25519::AuthorityId::ID, Some(&format!("{}/hunter3", PHRASE))) + .unwrap(); + t.register_extension(KeystoreExt::new(keystore)); t.execute_with(|| { let results = @@ -106,19 +96,13 @@ fn should_submit_signed_twice_from_the_same_account() { t.register_extension(TransactionPoolExt::new(pool)); let keystore = MemoryKeystore::new(); - Keystore::sr25519_generate_new( - &keystore, - sr25519::AuthorityId::ID, - Some(&format!("{}/hunter1", PHRASE)), - ) - .unwrap(); - Keystore::sr25519_generate_new( - &keystore, - sr25519::AuthorityId::ID, - Some(&format!("{}/hunter2", PHRASE)), - ) - .unwrap(); - t.register_extension(KeystoreExt(Arc::new(keystore))); + keystore + .sr25519_generate_new(sr25519::AuthorityId::ID, Some(&format!("{}/hunter1", PHRASE))) + .unwrap(); + keystore + .sr25519_generate_new(sr25519::AuthorityId::ID, Some(&format!("{}/hunter2", PHRASE))) + .unwrap(); + t.register_extension(KeystoreExt::new(keystore)); t.execute_with(|| { let result = @@ -169,7 +153,7 @@ fn should_submit_signed_twice_from_all_accounts() { keystore .sr25519_generate_new(sr25519::AuthorityId::ID, Some(&format!("{}/hunter2", PHRASE))) .unwrap(); - t.register_extension(KeystoreExt(Arc::new(keystore))); + t.register_extension(KeystoreExt::new(keystore)); t.execute_with(|| { let results = Signer::::all_accounts() @@ -227,13 +211,10 @@ fn submitted_transaction_should_be_valid() { t.register_extension(TransactionPoolExt::new(pool)); let keystore = MemoryKeystore::new(); - Keystore::sr25519_generate_new( - &keystore, - sr25519::AuthorityId::ID, - Some(&format!("{}/hunter1", PHRASE)), - ) - .unwrap(); - t.register_extension(KeystoreExt(Arc::new(keystore))); + keystore + .sr25519_generate_new(sr25519::AuthorityId::ID, Some(&format!("{}/hunter1", PHRASE))) + .unwrap(); + t.register_extension(KeystoreExt::new(keystore)); t.execute_with(|| { let results = diff --git a/bin/utils/chain-spec-builder/src/main.rs b/bin/utils/chain-spec-builder/src/main.rs index f7b7d94c5..f94fca1c1 100644 --- a/bin/utils/chain-spec-builder/src/main.rs +++ b/bin/utils/chain-spec-builder/src/main.rs @@ -19,7 +19,6 @@ use std::{ fs, path::{Path, PathBuf}, - sync::Arc, }; use ansi_term::Style; @@ -32,7 +31,7 @@ use sp_core::{ crypto::{ByteArray, Ss58Codec}, sr25519, }; -use sp_keystore::{Keystore, KeystorePtr}; +use sp_keystore::KeystorePtr; /// A utility to easily create a testnet chain spec definition with a given set /// of authorities and endowed accounts and/or generate random accounts. @@ -164,16 +163,17 @@ fn generate_chain_spec( fn generate_authority_keys_and_store(seeds: &[String], keystore_path: &Path) -> Result<(), String> { for (n, seed) in seeds.iter().enumerate() { - let keystore: KeystorePtr = Arc::new( + let keystore: KeystorePtr = LocalKeystore::open(keystore_path.join(format!("auth-{}", n)), None) - .map_err(|err| err.to_string())?, - ); + .map_err(|err| err.to_string())? + .into(); let (_, _, grandpa, babe, im_online, authority_discovery) = chain_spec::authority_keys_from_seed(seed); let insert_key = |key_type, public| { - Keystore::insert(&*keystore, key_type, &format!("//{}", seed), public) + keystore + .insert(key_type, &format!("//{}", seed), public) .map_err(|_| format!("Failed to insert key: {}", grandpa)) }; diff --git a/client/authority-discovery/src/worker/tests.rs b/client/authority-discovery/src/worker/tests.rs index c918d7993..49055fec5 100644 --- a/client/authority-discovery/src/worker/tests.rs +++ b/client/authority-discovery/src/worker/tests.rs @@ -697,7 +697,7 @@ fn addresses_to_publish_adds_p2p() { Arc::new(TestApi { authorities: vec![] }), network.clone(), Box::pin(dht_event_rx), - Role::PublishAndDiscover(Arc::new(MemoryKeystore::new())), + Role::PublishAndDiscover(MemoryKeystore::new().into()), Some(prometheus_endpoint::Registry::new()), Default::default(), ); @@ -731,7 +731,7 @@ fn addresses_to_publish_respects_existing_p2p_protocol() { Arc::new(TestApi { authorities: vec![] }), network.clone(), Box::pin(dht_event_rx), - Role::PublishAndDiscover(Arc::new(MemoryKeystore::new())), + Role::PublishAndDiscover(MemoryKeystore::new().into()), Some(prometheus_endpoint::Registry::new()), Default::default(), ); diff --git a/client/cli/src/commands/insert_key.rs b/client/cli/src/commands/insert_key.rs index b02c0181d..e80058d44 100644 --- a/client/cli/src/commands/insert_key.rs +++ b/client/cli/src/commands/insert_key.rs @@ -24,8 +24,7 @@ use clap::Parser; use sc_keystore::LocalKeystore; use sc_service::config::{BasePath, KeystoreConfig}; use sp_core::crypto::{KeyTypeId, SecretString}; -use sp_keystore::{Keystore, KeystorePtr}; -use std::sync::Arc; +use sp_keystore::KeystorePtr; /// The `insert` command #[derive(Debug, Clone, Parser)] @@ -67,9 +66,9 @@ impl InsertKeyCmd { let config_dir = base_path.config_dir(chain_spec.id()); let (keystore, public) = match self.keystore_params.keystore_config(&config_dir)? { - (_, KeystoreConfig::Path { path, password }) => { + KeystoreConfig::Path { path, password } => { let public = with_crypto_scheme!(self.scheme, to_vec(&suri, password.clone()))?; - let keystore: KeystorePtr = Arc::new(LocalKeystore::open(path, password)?); + let keystore: KeystorePtr = LocalKeystore::open(path, password)?.into(); (keystore, public) }, _ => unreachable!("keystore_config always returns path and password; qed"), @@ -78,7 +77,8 @@ impl InsertKeyCmd { let key_type = KeyTypeId::try_from(self.key_type.as_str()).map_err(|_| Error::KeyTypeInvalid)?; - Keystore::insert(&*keystore, key_type, &suri, &public[..]) + keystore + .insert(key_type, &suri, &public[..]) .map_err(|_| Error::KeystoreOperation)?; Ok(()) @@ -95,6 +95,7 @@ mod tests { use super::*; use sc_service::{ChainSpec, ChainType, GenericChainSpec, NoExtension}; use sp_core::{sr25519::Pair, ByteArray, Pair as _}; + use sp_keystore::Keystore; use tempfile::TempDir; struct Cli; diff --git a/client/cli/src/config.rs b/client/cli/src/config.rs index 5fedcf99a..063b2c398 100644 --- a/client/cli/src/config.rs +++ b/client/cli/src/config.rs @@ -185,10 +185,10 @@ pub trait CliConfiguration: Sized { /// /// By default this is retrieved from `KeystoreParams` if it is available. Otherwise it uses /// `KeystoreConfig::InMemory`. - fn keystore_config(&self, config_dir: &PathBuf) -> Result<(Option, KeystoreConfig)> { + fn keystore_config(&self, config_dir: &PathBuf) -> Result { self.keystore_params() .map(|x| x.keystore_config(config_dir)) - .unwrap_or_else(|| Ok((None, KeystoreConfig::InMemory))) + .unwrap_or_else(|| Ok(KeystoreConfig::InMemory)) } /// Get the database cache size. @@ -505,7 +505,7 @@ pub trait CliConfiguration: Sized { let role = self.role(is_dev)?; let max_runtime_instances = self.max_runtime_instances()?.unwrap_or(8); let is_validator = role.is_authority(); - let (keystore_remote, keystore) = self.keystore_config(&config_dir)?; + let keystore = self.keystore_config(&config_dir)?; let telemetry_endpoints = self.telemetry_endpoints(&chain_spec)?; let runtime_cache_size = self.runtime_cache_size()?; @@ -524,7 +524,6 @@ pub trait CliConfiguration: Sized { node_key, DCV::p2p_listen_port(), )?, - keystore_remote, keystore, database: self.database_config(&config_dir, database_cache_size, database)?, trie_cache_maximum_size: self.trie_cache_maximum_size()?, diff --git a/client/cli/src/params/keystore_params.rs b/client/cli/src/params/keystore_params.rs index e6e6fd9eb..8933110c4 100644 --- a/client/cli/src/params/keystore_params.rs +++ b/client/cli/src/params/keystore_params.rs @@ -68,9 +68,7 @@ pub fn secret_string_from_str(s: &str) -> std::result::Result Result<(Option, KeystoreConfig)> { + pub fn keystore_config(&self, config_dir: &Path) -> Result { let password = if self.password_interactive { Some(SecretString::new(input_keystore_password()?)) } else if let Some(ref file) = self.password_filename { @@ -85,7 +83,7 @@ impl KeystoreParams { .clone() .unwrap_or_else(|| config_dir.join(DEFAULT_KEYSTORE_CONFIG_PATH)); - Ok((self.keystore_uri.clone(), KeystoreConfig::Path { path, password })) + Ok(KeystoreConfig::Path { path, password }) } /// helper method to fetch password from `KeyParams` or read from stdin diff --git a/client/cli/src/runner.rs b/client/cli/src/runner.rs index 47adfcf89..3d216aef4 100644 --- a/client/cli/src/runner.rs +++ b/client/cli/src/runner.rs @@ -345,7 +345,6 @@ mod tests { transaction_pool: Default::default(), network: NetworkConfiguration::new_memory(), keystore: sc_service::config::KeystoreConfig::InMemory, - keystore_remote: None, database: sc_client_db::DatabaseSource::ParityDb { path: PathBuf::from("db") }, trie_cache_maximum_size: None, state_pruning: None, diff --git a/client/consensus/aura/src/lib.rs b/client/consensus/aura/src/lib.rs index 8cdad148d..d06a6016a 100644 --- a/client/consensus/aura/src/lib.rs +++ b/client/consensus/aura/src/lib.rs @@ -51,7 +51,7 @@ use sp_consensus::{BlockOrigin, Environment, Error as ConsensusError, Proposer, use sp_consensus_slots::Slot; use sp_core::crypto::{ByteArray, Pair, Public}; use sp_inherents::CreateInherentDataProviders; -use sp_keystore::{Keystore, KeystorePtr}; +use sp_keystore::KeystorePtr; use sp_runtime::{ traits::{Block as BlockT, Header, Member, NumberFor, Zero}, DigestItem, @@ -418,10 +418,10 @@ where ) -> Option { let expected_author = slot_author::

(slot, epoch_data); expected_author.and_then(|p| { - if Keystore::has_keys( - &*self.keystore, - &[(p.to_raw_vec(), sp_application_crypto::key_types::AURA)], - ) { + if self + .keystore + .has_keys(&[(p.to_raw_vec(), sp_application_crypto::key_types::AURA)]) + { Some(p.clone()) } else { None @@ -449,19 +449,16 @@ where // add it to a digest item. let public_type_pair = public.to_public_crypto_pair(); let public = public.to_raw_vec(); - let signature = Keystore::sign_with( - &*self.keystore, - as AppKey>::ID, - &public_type_pair, - header_hash.as_ref(), - ) - .map_err(|e| sp_consensus::Error::CannotSign(public.clone(), e.to_string()))? - .ok_or_else(|| { - sp_consensus::Error::CannotSign( - public.clone(), - "Could not find key in keystore.".into(), - ) - })?; + let signature = self + .keystore + .sign_with( as AppKey>::ID, &public_type_pair, header_hash.as_ref()) + .map_err(|e| sp_consensus::Error::CannotSign(public.clone(), e.to_string()))? + .ok_or_else(|| { + sp_consensus::Error::CannotSign( + public.clone(), + "Could not find key in keystore.".into(), + ) + })?; let signature = signature .clone() .try_into() @@ -648,6 +645,7 @@ mod tests { use sp_consensus_aura::sr25519::AuthorityPair; use sp_inherents::InherentData; use sp_keyring::sr25519::Keyring; + use sp_keystore::Keystore; use sp_runtime::{ traits::{Block as BlockT, Header as _}, Digest, @@ -798,7 +796,8 @@ mod tests { LocalKeystore::open(keystore_path.path(), None).expect("Creates keystore."), ); - Keystore::sr25519_generate_new(&*keystore, AURA, Some(&key.to_seed())) + keystore + .sr25519_generate_new(AURA, Some(&key.to_seed())) .expect("Creates authority key"); keystore_paths.push(keystore_path); @@ -883,7 +882,8 @@ mod tests { let keystore_path = tempfile::tempdir().expect("Creates keystore path"); let keystore = LocalKeystore::open(keystore_path.path(), None).expect("Creates keystore."); - let public = Keystore::sr25519_generate_new(&keystore, AuthorityPair::ID, None) + let public = keystore + .sr25519_generate_new(AuthorityPair::ID, None) .expect("Key should be created"); authorities.push(public.into()); @@ -933,12 +933,9 @@ mod tests { let keystore_path = tempfile::tempdir().expect("Creates keystore path"); let keystore = LocalKeystore::open(keystore_path.path(), None).expect("Creates keystore."); - Keystore::sr25519_generate_new( - &keystore, - AuthorityPair::ID, - Some(&Keyring::Alice.to_seed()), - ) - .expect("Key should be created"); + keystore + .sr25519_generate_new(AuthorityPair::ID, Some(&Keyring::Alice.to_seed())) + .expect("Key should be created"); let net = Arc::new(Mutex::new(net)); diff --git a/client/consensus/babe/rpc/Cargo.toml b/client/consensus/babe/rpc/Cargo.toml index 4f5aaf854..f81a14a97 100644 --- a/client/consensus/babe/rpc/Cargo.toml +++ b/client/consensus/babe/rpc/Cargo.toml @@ -31,7 +31,6 @@ sp-runtime = { version = "7.0.0", path = "../../../../primitives/runtime" } [dev-dependencies] serde_json = "1.0.85" -tempfile = "3.1.0" tokio = "1.22.0" sc-consensus = { version = "0.10.0-dev", path = "../../../consensus/common" } sc-keystore = { version = "4.0.0-dev", path = "../../../keystore" } diff --git a/client/consensus/babe/rpc/src/lib.rs b/client/consensus/babe/rpc/src/lib.rs index bc4d1c17e..005d84938 100644 --- a/client/consensus/babe/rpc/src/lib.rs +++ b/client/consensus/babe/rpc/src/lib.rs @@ -37,7 +37,7 @@ use sp_consensus_babe::{ digests::PreDigest, AuthorityId, BabeApi as BabeRuntimeApi, BabeConfiguration, }; use sp_core::crypto::ByteArray; -use sp_keystore::{Keystore, KeystorePtr}; +use sp_keystore::KeystorePtr; use sp_runtime::traits::{Block as BlockT, Header as _}; use std::{collections::HashMap, sync::Arc}; @@ -117,7 +117,7 @@ where .iter() .enumerate() .filter_map(|(i, a)| { - if Keystore::has_keys(&*self.keystore, &[(a.0.to_raw_vec(), AuthorityId::ID)]) { + if self.keystore.has_keys(&[(a.0.to_raw_vec(), AuthorityId::ID)]) { Some((a.0.clone(), i)) } else { None @@ -210,30 +210,21 @@ where #[cfg(test)] mod tests { use super::*; - use sc_keystore::LocalKeystore; - use sp_application_crypto::AppPair; + use sc_consensus_babe::block_import; use sp_core::crypto::key_types::BABE; use sp_keyring::Sr25519Keyring; - use sp_keystore::{Keystore, KeystorePtr}; + use sp_keystore::{testing::MemoryKeystore, Keystore}; use substrate_test_runtime_client::{ runtime::Block, Backend, DefaultTestClientBuilderExt, TestClient, TestClientBuilder, TestClientBuilderExt, }; - use sc_consensus_babe::{block_import, AuthorityPair}; - use std::sync::Arc; - - /// creates keystore backed by a temp file - fn create_temp_keystore( - authority: Sr25519Keyring, - ) -> (KeystorePtr, tempfile::TempDir) { - let keystore_path = tempfile::tempdir().expect("Creates keystore path"); - let keystore = - Arc::new(LocalKeystore::open(keystore_path.path(), None).expect("Creates keystore")); - Keystore::sr25519_generate_new(&*keystore, BABE, Some(&authority.to_seed())) + fn create_keystore(authority: Sr25519Keyring) -> KeystorePtr { + let keystore = MemoryKeystore::new(); + keystore + .sr25519_generate_new(BABE, Some(&authority.to_seed())) .expect("Creates authority key"); - - (keystore, keystore_path) + keystore.into() } fn test_babe_rpc_module( @@ -247,7 +238,7 @@ mod tests { .expect("can initialize block-import"); let epoch_changes = link.epoch_changes().clone(); - let keystore = create_temp_keystore::(Sr25519Keyring::Alice).0; + let keystore = create_keystore(Sr25519Keyring::Alice); Babe::new(client.clone(), epoch_changes, keystore, config, longest_chain, deny_unsafe) } diff --git a/client/consensus/babe/src/authorship.rs b/client/consensus/babe/src/authorship.rs index 956e886d1..c4b43b0d0 100644 --- a/client/consensus/babe/src/authorship.rs +++ b/client/consensus/babe/src/authorship.rs @@ -29,7 +29,7 @@ use sp_consensus_babe::{ }; use sp_consensus_vrf::schnorrkel::{VRFOutput, VRFProof}; use sp_core::{blake2_256, crypto::ByteArray, U256}; -use sp_keystore::{Keystore, KeystorePtr}; +use sp_keystore::KeystorePtr; /// Calculates the primary selection threshold for a given authority, taking /// into account `c` (`1 - c` represents the probability of a slot being empty). @@ -153,8 +153,7 @@ fn claim_secondary_slot( if authority_id == expected_author { let pre_digest = if author_secondary_vrf { let transcript_data = make_transcript_data(randomness, slot, epoch_index); - let result = Keystore::sr25519_vrf_sign( - &**keystore, + let result = keystore.sr25519_vrf_sign( AuthorityId::ID, authority_id.as_ref(), transcript_data, @@ -169,10 +168,7 @@ fn claim_secondary_slot( } else { None } - } else if Keystore::has_keys( - &**keystore, - &[(authority_id.to_raw_vec(), AuthorityId::ID)], - ) { + } else if keystore.has_keys(&[(authority_id.to_raw_vec(), AuthorityId::ID)]) { Some(PreDigest::SecondaryPlain(SecondaryPlainPreDigest { slot, authority_index: *authority_index as u32, @@ -254,12 +250,8 @@ fn claim_primary_slot( for (authority_id, authority_index) in keys { let transcript = make_transcript(randomness, slot, epoch_index); let transcript_data = make_transcript_data(randomness, slot, epoch_index); - let result = Keystore::sr25519_vrf_sign( - &**keystore, - AuthorityId::ID, - authority_id.as_ref(), - transcript_data, - ); + let result = + keystore.sr25519_vrf_sign(AuthorityId::ID, authority_id.as_ref(), transcript_data); if let Ok(Some(signature)) = result { let public = PublicKey::from_bytes(&authority_id.to_raw_vec()).ok()?; let inout = match signature.output.attach_input_hash(&public, transcript) { @@ -287,20 +279,16 @@ fn claim_primary_slot( #[cfg(test)] mod tests { use super::*; - use sc_keystore::LocalKeystore; use sp_consensus_babe::{AllowedSlots, AuthorityId, BabeEpochConfiguration}; use sp_core::{crypto::Pair as _, sr25519::Pair}; - use std::sync::Arc; + use sp_keystore::testing::MemoryKeystore; #[test] fn claim_secondary_plain_slot_works() { - let keystore: KeystorePtr = Arc::new(LocalKeystore::in_memory()); - let valid_public_key = Keystore::sr25519_generate_new( - &*keystore, - AuthorityId::ID, - Some(sp_core::crypto::DEV_PHRASE), - ) - .unwrap(); + let keystore: KeystorePtr = MemoryKeystore::new().into(); + let valid_public_key = keystore + .sr25519_generate_new(AuthorityId::ID, Some(sp_core::crypto::DEV_PHRASE)) + .unwrap(); let authorities = vec![ (AuthorityId::from(Pair::generate().0.public()), 5), diff --git a/client/consensus/babe/src/lib.rs b/client/consensus/babe/src/lib.rs index 621aac391..e2c2793b1 100644 --- a/client/consensus/babe/src/lib.rs +++ b/client/consensus/babe/src/lib.rs @@ -119,7 +119,7 @@ use sp_consensus_babe::inherents::BabeInherentData; use sp_consensus_slots::Slot; use sp_core::{crypto::ByteArray, ExecutionContext}; use sp_inherents::{CreateInherentDataProviders, InherentData, InherentDataProvider}; -use sp_keystore::{Keystore, KeystorePtr}; +use sp_keystore::KeystorePtr; use sp_runtime::{ generic::OpaqueDigestItemId, traits::{Block as BlockT, Header, NumberFor, SaturatedConversion, Zero}, @@ -834,19 +834,16 @@ where // add it to a digest item. let public_type_pair = public.clone().into(); let public = public.to_raw_vec(); - let signature = Keystore::sign_with( - &*self.keystore, - ::ID, - &public_type_pair, - header_hash.as_ref(), - ) - .map_err(|e| sp_consensus::Error::CannotSign(public.clone(), e.to_string()))? - .ok_or_else(|| { - sp_consensus::Error::CannotSign( - public.clone(), - "Could not find key in keystore.".into(), - ) - })?; + let signature = self + .keystore + .sign_with(::ID, &public_type_pair, header_hash.as_ref()) + .map_err(|e| sp_consensus::Error::CannotSign(public.clone(), e.to_string()))? + .ok_or_else(|| { + sp_consensus::Error::CannotSign( + public.clone(), + "Could not find key in keystore.".into(), + ) + })?; let signature: AuthoritySignature = signature .clone() .try_into() diff --git a/client/consensus/babe/src/tests.rs b/client/consensus/babe/src/tests.rs index 4ac15bf26..7f8448d91 100644 --- a/client/consensus/babe/src/tests.rs +++ b/client/consensus/babe/src/tests.rs @@ -369,10 +369,11 @@ async fn rejects_empty_block() { } fn create_keystore(authority: Sr25519Keyring) -> KeystorePtr { - let keystore = Arc::new(MemoryKeystore::new()); - Keystore::sr25519_generate_new(&*keystore, BABE, Some(&authority.to_seed())) - .expect("Generates authority key"); + let keystore = MemoryKeystore::new(); keystore + .sr25519_generate_new(BABE, Some(&authority.to_seed())) + .expect("Generates authority key"); + keystore.into() } async fn run_one_test(mutator: impl Fn(&mut TestHeader, Stage) + Send + Sync + 'static) { @@ -637,7 +638,8 @@ fn claim_vrf_check() { v => panic!("Unexpected pre-digest variant {:?}", v), }; let transcript = make_transcript_data(&epoch.randomness.clone(), 0.into(), epoch.epoch_index); - let sign = Keystore::sr25519_vrf_sign(&*keystore, AuthorityId::ID, &public, transcript) + let sign = keystore + .sr25519_vrf_sign(AuthorityId::ID, &public, transcript) .unwrap() .unwrap(); assert_eq!(pre_digest.vrf_output, VRFOutput(sign.output)); @@ -648,7 +650,8 @@ fn claim_vrf_check() { v => panic!("Unexpected pre-digest variant {:?}", v), }; let transcript = make_transcript_data(&epoch.randomness.clone(), 1.into(), epoch.epoch_index); - let sign = Keystore::sr25519_vrf_sign(&*keystore, AuthorityId::ID, &public, transcript) + let sign = keystore + .sr25519_vrf_sign(AuthorityId::ID, &public, transcript) .unwrap() .unwrap(); assert_eq!(pre_digest.vrf_output, VRFOutput(sign.output)); @@ -661,7 +664,8 @@ fn claim_vrf_check() { }; let fixed_epoch = epoch.clone_for_slot(slot); let transcript = make_transcript_data(&epoch.randomness.clone(), slot, fixed_epoch.epoch_index); - let sign = Keystore::sr25519_vrf_sign(&*keystore, AuthorityId::ID, &public, transcript) + let sign = keystore + .sr25519_vrf_sign(AuthorityId::ID, &public, transcript) .unwrap() .unwrap(); assert_eq!(fixed_epoch.epoch_index, 11); @@ -675,7 +679,8 @@ fn claim_vrf_check() { }; let fixed_epoch = epoch.clone_for_slot(slot); let transcript = make_transcript_data(&epoch.randomness.clone(), slot, fixed_epoch.epoch_index); - let sign = Keystore::sr25519_vrf_sign(&*keystore, AuthorityId::ID, &public, transcript) + let sign = keystore + .sr25519_vrf_sign(AuthorityId::ID, &public, transcript) .unwrap() .unwrap(); assert_eq!(fixed_epoch.epoch_index, 11); diff --git a/client/consensus/beefy/src/communication/gossip.rs b/client/consensus/beefy/src/communication/gossip.rs index 4bbd7a7f6..2b5e772c0 100644 --- a/client/consensus/beefy/src/communication/gossip.rs +++ b/client/consensus/beefy/src/communication/gossip.rs @@ -243,17 +243,14 @@ where #[cfg(test)] mod tests { - use sc_keystore::LocalKeystore; - use sc_network_test::Block; - use sp_keystore::{Keystore, KeystorePtr}; - + use super::*; use crate::keystore::BeefyKeystore; + use sc_network_test::Block; use sp_consensus_beefy::{ crypto::Signature, known_payloads, Commitment, Keyring, MmrRootHash, Payload, VoteMessage, KEY_TYPE, }; - - use super::*; + use sp_keystore::{testing::MemoryKeystore, Keystore}; #[test] fn known_votes_insert_remove() { @@ -306,10 +303,9 @@ mod tests { } fn sign_commitment(who: &Keyring, commitment: &Commitment) -> Signature { - let store: KeystorePtr = std::sync::Arc::new(LocalKeystore::in_memory()); - Keystore::ecdsa_generate_new(&*store, KEY_TYPE, Some(&who.to_seed())).unwrap(); - let beefy_keystore: BeefyKeystore = Some(store).into(); - + let store = MemoryKeystore::new(); + store.ecdsa_generate_new(KEY_TYPE, Some(&who.to_seed())).unwrap(); + let beefy_keystore: BeefyKeystore = Some(store.into()).into(); beefy_keystore.sign(&who.public(), &commitment.encode()).unwrap() } diff --git a/client/consensus/beefy/src/keystore.rs b/client/consensus/beefy/src/keystore.rs index 596e8f2eb..795d4cc8a 100644 --- a/client/consensus/beefy/src/keystore.rs +++ b/client/consensus/beefy/src/keystore.rs @@ -18,7 +18,7 @@ use sp_application_crypto::RuntimeAppPublic; use sp_core::keccak_256; -use sp_keystore::{Keystore, KeystorePtr}; +use sp_keystore::KeystorePtr; use log::warn; @@ -50,7 +50,7 @@ impl BeefyKeystore { // we do check for multiple private keys as a key store sanity check. let public: Vec = keys .iter() - .filter(|k| Keystore::has_keys(&*store, &[(k.to_raw_vec(), KEY_TYPE)])) + .filter(|k| store.has_keys(&[(k.to_raw_vec(), KEY_TYPE)])) .cloned() .collect(); @@ -77,7 +77,8 @@ impl BeefyKeystore { let msg = keccak_256(message); let public = public.as_ref(); - let sig = Keystore::ecdsa_sign_prehashed(&*store, KEY_TYPE, public, &msg) + let sig = store + .ecdsa_sign_prehashed(KEY_TYPE, public, &msg) .map_err(|e| error::Error::Keystore(e.to_string()))? .ok_or_else(|| error::Error::Signature("ecdsa_sign_prehashed() failed".to_string()))?; @@ -94,10 +95,8 @@ impl BeefyKeystore { pub fn public_keys(&self) -> Result, error::Error> { let store = self.0.clone().ok_or_else(|| error::Error::Keystore("no Keystore".into()))?; - let pk: Vec = Keystore::ecdsa_public_keys(&*store, KEY_TYPE) - .drain(..) - .map(Public::from) - .collect(); + let pk: Vec = + store.ecdsa_public_keys(KEY_TYPE).drain(..).map(Public::from).collect(); Ok(pk) } @@ -118,18 +117,15 @@ impl From> for BeefyKeystore { #[cfg(test)] pub mod tests { - use std::sync::Arc; - - use sc_keystore::LocalKeystore; - use sp_core::{ecdsa, Pair}; - use sp_consensus_beefy::{crypto, Keyring}; + use sp_core::{ecdsa, Pair}; + use sp_keystore::testing::MemoryKeystore; use super::*; use crate::error::Error; fn keystore() -> KeystorePtr { - Arc::new(LocalKeystore::in_memory()) + MemoryKeystore::new().into() } #[test] @@ -197,11 +193,11 @@ pub mod tests { fn authority_id_works() { let store = keystore(); - let alice: crypto::Public = - Keystore::ecdsa_generate_new(&*store, KEY_TYPE, Some(&Keyring::Alice.to_seed())) - .ok() - .unwrap() - .into(); + let alice: crypto::Public = store + .ecdsa_generate_new(KEY_TYPE, Some(&Keyring::Alice.to_seed())) + .ok() + .unwrap() + .into(); let bob = Keyring::Bob.public(); let charlie = Keyring::Charlie.public(); @@ -223,11 +219,11 @@ pub mod tests { fn sign_works() { let store = keystore(); - let alice: crypto::Public = - Keystore::ecdsa_generate_new(&*store, KEY_TYPE, Some(&Keyring::Alice.to_seed())) - .ok() - .unwrap() - .into(); + let alice: crypto::Public = store + .ecdsa_generate_new(KEY_TYPE, Some(&Keyring::Alice.to_seed())) + .ok() + .unwrap() + .into(); let store: BeefyKeystore = Some(store).into(); @@ -243,9 +239,7 @@ pub mod tests { fn sign_error() { let store = keystore(); - let _ = Keystore::ecdsa_generate_new(&*store, KEY_TYPE, Some(&Keyring::Bob.to_seed())) - .ok() - .unwrap(); + store.ecdsa_generate_new(KEY_TYPE, Some(&Keyring::Bob.to_seed())).ok().unwrap(); let store: BeefyKeystore = Some(store).into(); @@ -274,11 +268,11 @@ pub mod tests { fn verify_works() { let store = keystore(); - let alice: crypto::Public = - Keystore::ecdsa_generate_new(&*store, KEY_TYPE, Some(&Keyring::Alice.to_seed())) - .ok() - .unwrap() - .into(); + let alice: crypto::Public = store + .ecdsa_generate_new(KEY_TYPE, Some(&Keyring::Alice.to_seed())) + .ok() + .unwrap() + .into(); let store: BeefyKeystore = Some(store).into(); @@ -300,9 +294,8 @@ pub mod tests { let store = keystore(); - let add_key = |key_type, seed: Option<&str>| { - Keystore::ecdsa_generate_new(&*store, key_type, seed).unwrap() - }; + let add_key = + |key_type, seed: Option<&str>| store.ecdsa_generate_new(key_type, seed).unwrap(); // test keys let _ = add_key(TEST_TYPE, Some(Keyring::Alice.to_seed().as_str())); diff --git a/client/consensus/beefy/src/tests.rs b/client/consensus/beefy/src/tests.rs index 9a3653f7c..0ad5f1088 100644 --- a/client/consensus/beefy/src/tests.rs +++ b/client/consensus/beefy/src/tests.rs @@ -340,10 +340,11 @@ pub(crate) fn make_beefy_ids(keys: &[BeefyKeyring]) -> Vec { } pub(crate) fn create_beefy_keystore(authority: BeefyKeyring) -> KeystorePtr { - let keystore = Arc::new(MemoryKeystore::new()); - Keystore::ecdsa_generate_new(&*keystore, BeefyKeyType, Some(&authority.to_seed())) - .expect("Creates authority key"); + let keystore = MemoryKeystore::new(); keystore + .ecdsa_generate_new(BeefyKeyType, Some(&authority.to_seed())) + .expect("Creates authority key"); + keystore.into() } async fn voter_init_setup( diff --git a/client/consensus/grandpa/src/lib.rs b/client/consensus/grandpa/src/lib.rs index 6b3730fc7..cf98e1792 100644 --- a/client/consensus/grandpa/src/lib.rs +++ b/client/consensus/grandpa/src/lib.rs @@ -79,7 +79,7 @@ use sp_consensus_grandpa::{ AuthorityList, AuthoritySignature, SetId, CLIENT_LOG_TARGET as LOG_TARGET, }; use sp_core::{crypto::ByteArray, traits::CallContext}; -use sp_keystore::{Keystore, KeystorePtr}; +use sp_keystore::KeystorePtr; use sp_runtime::{ generic::BlockId, traits::{Block as BlockT, NumberFor, Zero}, @@ -1141,7 +1141,7 @@ fn local_authority_id( keystore.and_then(|keystore| { voters .iter() - .find(|(p, _)| Keystore::has_keys(&**keystore, &[(p.to_raw_vec(), AuthorityId::ID)])) + .find(|(p, _)| keystore.has_keys(&[(p.to_raw_vec(), AuthorityId::ID)])) .map(|(p, _)| p.clone()) }) } diff --git a/client/consensus/grandpa/src/tests.rs b/client/consensus/grandpa/src/tests.rs index 6f19ca739..7a3f862d3 100644 --- a/client/consensus/grandpa/src/tests.rs +++ b/client/consensus/grandpa/src/tests.rs @@ -281,10 +281,11 @@ fn make_ids(keys: &[Ed25519Keyring]) -> AuthorityList { } fn create_keystore(authority: Ed25519Keyring) -> KeystorePtr { - let keystore = Arc::new(MemoryKeystore::new()); - Keystore::ed25519_generate_new(&*keystore, GRANDPA, Some(&authority.to_seed())) - .expect("Creates authority key"); + let keystore = MemoryKeystore::new(); keystore + .ed25519_generate_new(GRANDPA, Some(&authority.to_seed())) + .expect("Creates authority key"); + keystore.into() } async fn run_until_complete(future: impl Future + Unpin, net: &Arc>) { diff --git a/client/keystore/src/local.rs b/client/keystore/src/local.rs index 18ee58b87..a4af78117 100644 --- a/client/keystore/src/local.rs +++ b/client/keystore/src/local.rs @@ -135,6 +135,9 @@ impl Keystore for LocalKeystore { .unwrap_or_default() } + /// Generate a new pair compatible with the 'ed25519' signature scheme. + /// + /// If the `[seed]` is `Some` then the key will be ephemeral and stored in memory. fn sr25519_generate_new( &self, id: KeyTypeId, @@ -162,6 +165,9 @@ impl Keystore for LocalKeystore { .unwrap_or_default() } + /// Generate a new pair compatible with the 'sr25519' signature scheme. + /// + /// If the `[seed]` is `Some` then the key will be ephemeral and stored in memory. fn ed25519_generate_new( &self, id: KeyTypeId, @@ -189,6 +195,9 @@ impl Keystore for LocalKeystore { .unwrap_or_default() } + /// Generate a new pair compatible with the 'ecdsa' signature scheme. + /// + /// If the `[seed]` is `Some` then the key will be ephemeral and stored in memory. fn ecdsa_generate_new( &self, id: KeyTypeId, @@ -504,17 +513,14 @@ mod tests { let key: ed25519::AppPair = store.0.write().generate().unwrap(); let key2 = ed25519::Pair::generate().0; - assert!(!Keystore::has_keys(&store, &[(key2.public().to_vec(), ed25519::AppPublic::ID)])); + assert!(!store.has_keys(&[(key2.public().to_vec(), ed25519::AppPublic::ID)])); - assert!(!Keystore::has_keys( - &store, - &[ - (key2.public().to_vec(), ed25519::AppPublic::ID), - (key.public().to_raw_vec(), ed25519::AppPublic::ID), - ], - )); + assert!(!store.has_keys(&[ + (key2.public().to_vec(), ed25519::AppPublic::ID), + (key.public().to_raw_vec(), ed25519::AppPublic::ID), + ],)); - assert!(Keystore::has_keys(&store, &[(key.public().to_raw_vec(), ed25519::AppPublic::ID)])); + assert!(store.has_keys(&[(key.public().to_raw_vec(), ed25519::AppPublic::ID)])); } #[test] @@ -626,31 +632,30 @@ mod tests { let file_name = temp_dir.path().join(array_bytes::bytes2hex("", &SR25519.0[..2])); fs::write(file_name, "test").expect("Invalid file is written"); - assert!(Keystore::sr25519_public_keys(&store, SR25519).is_empty()); + assert!(store.sr25519_public_keys(SR25519).is_empty()); } #[test] fn generate_with_seed_is_not_stored() { let temp_dir = TempDir::new().unwrap(); let store = LocalKeystore::open(temp_dir.path(), None).unwrap(); - let _alice_tmp_key = - Keystore::sr25519_generate_new(&store, TEST_KEY_TYPE, Some("//Alice")).unwrap(); + let _alice_tmp_key = store.sr25519_generate_new(TEST_KEY_TYPE, Some("//Alice")).unwrap(); - assert_eq!(Keystore::sr25519_public_keys(&store, TEST_KEY_TYPE).len(), 1); + assert_eq!(store.sr25519_public_keys(TEST_KEY_TYPE).len(), 1); drop(store); let store = LocalKeystore::open(temp_dir.path(), None).unwrap(); - assert_eq!(Keystore::sr25519_public_keys(&store, TEST_KEY_TYPE).len(), 0); + assert_eq!(store.sr25519_public_keys(TEST_KEY_TYPE).len(), 0); } #[test] fn generate_can_be_fetched_in_memory() { let store = LocalKeystore::in_memory(); - Keystore::sr25519_generate_new(&store, TEST_KEY_TYPE, Some("//Alice")).unwrap(); + store.sr25519_generate_new(TEST_KEY_TYPE, Some("//Alice")).unwrap(); - assert_eq!(Keystore::sr25519_public_keys(&store, TEST_KEY_TYPE).len(), 1); - Keystore::sr25519_generate_new(&store, TEST_KEY_TYPE, None).unwrap(); - assert_eq!(Keystore::sr25519_public_keys(&store, TEST_KEY_TYPE).len(), 2); + assert_eq!(store.sr25519_public_keys(TEST_KEY_TYPE).len(), 1); + store.sr25519_generate_new(TEST_KEY_TYPE, None).unwrap(); + assert_eq!(store.sr25519_public_keys(TEST_KEY_TYPE).len(), 2); } #[test] @@ -661,7 +666,7 @@ mod tests { let temp_dir = TempDir::new().unwrap(); let store = LocalKeystore::open(temp_dir.path(), None).unwrap(); - let public = Keystore::sr25519_generate_new(&store, TEST_KEY_TYPE, None).unwrap(); + let public = store.sr25519_generate_new(TEST_KEY_TYPE, None).unwrap(); let path = store.0.read().key_file_path(public.as_ref(), TEST_KEY_TYPE).unwrap(); let permissions = File::open(path).unwrap().metadata().unwrap().permissions(); diff --git a/client/rpc/src/author/mod.rs b/client/rpc/src/author/mod.rs index 9752a32b1..00a126500 100644 --- a/client/rpc/src/author/mod.rs +++ b/client/rpc/src/author/mod.rs @@ -40,7 +40,7 @@ use sc_transaction_pool_api::{ use sp_api::ProvideRuntimeApi; use sp_blockchain::HeaderBackend; use sp_core::Bytes; -use sp_keystore::{Keystore, KeystorePtr}; +use sp_keystore::KeystorePtr; use sp_runtime::{generic, traits::Block as BlockT}; use sp_session::SessionKeys; @@ -112,7 +112,8 @@ where self.deny_unsafe.check_if_safe()?; let key_type = key_type.as_str().try_into().map_err(|_| Error::BadKeyType)?; - Keystore::insert(&*self.keystore, key_type, &suri, &public[..]) + self.keystore + .insert(key_type, &suri, &public[..]) .map_err(|_| Error::KeystoreUnavailable)?; Ok(()) } @@ -139,14 +140,14 @@ where .map_err(|e| Error::Client(Box::new(e)))? .ok_or(Error::InvalidSessionKeys)?; - Ok(Keystore::has_keys(&*self.keystore, &keys)) + Ok(self.keystore.has_keys(&keys)) } fn has_key(&self, public_key: Bytes, key_type: String) -> RpcResult { self.deny_unsafe.check_if_safe()?; let key_type = key_type.as_str().try_into().map_err(|_| Error::BadKeyType)?; - Ok(Keystore::has_keys(&*self.keystore, &[(public_key.to_vec(), key_type)])) + Ok(self.keystore.has_keys(&[(public_key.to_vec(), key_type)])) } fn pending_extrinsics(&self) -> RpcResult> { diff --git a/client/rpc/src/author/tests.rs b/client/rpc/src/author/tests.rs index 209673fce..75b6390c8 100644 --- a/client/rpc/src/author/tests.rs +++ b/client/rpc/src/author/tests.rs @@ -36,7 +36,7 @@ use sp_core::{ testing::{ED25519, SR25519}, H256, }; -use sp_keystore::testing::MemoryKeystore; +use sp_keystore::{testing::MemoryKeystore, Keystore}; use std::sync::Arc; use substrate_test_runtime_client::{ self, @@ -225,7 +225,7 @@ async fn author_should_insert_key() { keypair.public().0.to_vec().into(), ); api.call::<_, ()>("author_insertKey", params).await.unwrap(); - let pubkeys = Keystore::keys(&*setup.keystore, ED25519).unwrap(); + let pubkeys = setup.keystore.keys(ED25519).unwrap(); assert!( pubkeys.contains(&CryptoTypePublicPair(ed25519::CRYPTO_ID, keypair.public().to_raw_vec())) @@ -240,8 +240,8 @@ async fn author_should_rotate_keys() { let new_pubkeys: Bytes = api.call("author_rotateKeys", EmptyParams::new()).await.unwrap(); let session_keys = SessionKeys::decode(&mut &new_pubkeys[..]).expect("SessionKeys decode successfully"); - let ed25519_pubkeys = Keystore::keys(&*setup.keystore, ED25519).unwrap(); - let sr25519_pubkeys = Keystore::keys(&*setup.keystore, SR25519).unwrap(); + let ed25519_pubkeys = setup.keystore.keys(ED25519).unwrap(); + let sr25519_pubkeys = setup.keystore.keys(SR25519).unwrap(); assert!(ed25519_pubkeys .contains(&CryptoTypePublicPair(ed25519::CRYPTO_ID, session_keys.ed25519.to_raw_vec()))); assert!(sr25519_pubkeys diff --git a/client/service/src/builder.rs b/client/service/src/builder.rs index 034d056d6..bee40e9a3 100644 --- a/client/service/src/builder.rs +++ b/client/service/src/builder.rs @@ -67,7 +67,7 @@ use sp_consensus::block_validation::{ BlockAnnounceValidator, Chain, DefaultBlockAnnounceValidator, }; use sp_core::traits::{CodeExecutor, SpawnNamed}; -use sp_keystore::{Keystore, KeystorePtr}; +use sp_keystore::KeystorePtr; use sp_runtime::traits::{Block as BlockT, BlockIdTo, NumberFor, Zero}; use std::{str::FromStr, sync::Arc, time::SystemTime}; @@ -85,24 +85,8 @@ pub type TFullCallExecutor = type TFullParts = (TFullClient, Arc>, KeystoreContainer, TaskManager); -trait AsKeystoreRef { - fn keystore_ref(&self) -> Arc; -} - -impl AsKeystoreRef for Arc -where - T: Keystore + 'static, -{ - fn keystore_ref(&self) -> Arc { - self.clone() - } -} - -/// Construct and hold different layers of Keystore wrappers -pub struct KeystoreContainer { - remote: Option>, - local: Arc, -} +/// Construct a local keystore shareable container +pub struct KeystoreContainer(Arc); impl KeystoreContainer { /// Construct KeystoreContainer @@ -113,41 +97,17 @@ impl KeystoreContainer { KeystoreConfig::InMemory => LocalKeystore::in_memory(), }); - Ok(Self { remote: Default::default(), local: keystore }) + Ok(Self(keystore)) } - /// Set the remote keystore. - /// Should be called right away at startup and not at runtime: - /// even though this overrides any previously set remote store, it - /// does not reset any references previously handed out - they will - /// stick around. - pub fn set_remote_keystore(&mut self, remote: Arc) - where - T: Keystore + 'static, - { - self.remote = Some(Box::new(remote)) + /// Returns a shared reference to a dynamic `Keystore` trait implementation. + pub fn keystore(&self) -> KeystorePtr { + self.0.clone() } - /// Returns an adapter to a `Keystore` implementation. - pub fn keystore(&self) -> Arc { - if let Some(c) = self.remote.as_ref() { - c.keystore_ref() - } else { - self.local.clone() - } - } - - /// Returns the local keystore if available - /// - /// The function will return None if the available keystore is not a local keystore. - /// - /// # Note - /// - /// Using the [`LocalKeystore`] will result in loosing the ability to use any other keystore - /// implementation, like a remote keystore for example. Only use this if you a certain that you - /// require it! - pub fn local_keystore(&self) -> Option> { - Some(self.local.clone()) + /// Returns a shared reference to the local keystore . + pub fn local_keystore(&self) -> Arc { + self.0.clone() } } diff --git a/client/service/src/config.rs b/client/service/src/config.rs index c7d98a453..6550fcdd8 100644 --- a/client/service/src/config.rs +++ b/client/service/src/config.rs @@ -61,8 +61,6 @@ pub struct Configuration { pub network: NetworkConfiguration, /// Configuration for the keystore. pub keystore: KeystoreConfig, - /// Remote URI to connect to for async keystore support - pub keystore_remote: Option, /// Configuration for the database. pub database: DatabaseSource, /// Maximum size of internal trie cache in bytes. diff --git a/client/service/test/src/lib.rs b/client/service/test/src/lib.rs index f80446a4d..8a2e5050b 100644 --- a/client/service/test/src/lib.rs +++ b/client/service/test/src/lib.rs @@ -242,7 +242,6 @@ fn node_config< tokio_handle, transaction_pool: Default::default(), network: network_config, - keystore_remote: Default::default(), keystore: KeystoreConfig::Path { path: root.join("key"), password: None }, database: DatabaseSource::RocksDb { path: root.join("db"), cache_size: 128 }, trie_cache_maximum_size: Some(16 * 1024 * 1024), diff --git a/frame/benchmarking/src/baseline.rs b/frame/benchmarking/src/baseline.rs index 1f6e9c5a6..25336b697 100644 --- a/frame/benchmarking/src/baseline.rs +++ b/frame/benchmarking/src/baseline.rs @@ -160,12 +160,11 @@ pub mod mock { impl super::Config for Test {} pub fn new_test_ext() -> sp_io::TestExternalities { - use sp_keystore::{testing::MemoryKeystore, KeystoreExt, KeystorePtr}; - use sp_std::sync::Arc; + use sp_keystore::{testing::MemoryKeystore, KeystoreExt}; let t = frame_system::GenesisConfig::default().build_storage::().unwrap(); let mut ext = sp_io::TestExternalities::new(t); - ext.register_extension(KeystoreExt(Arc::new(MemoryKeystore::new()) as KeystorePtr)); + ext.register_extension(KeystoreExt::new(MemoryKeystore::new())); ext } diff --git a/frame/contracts/src/tests.rs b/frame/contracts/src/tests.rs index 185945b71..915a56a3f 100644 --- a/frame/contracts/src/tests.rs +++ b/frame/contracts/src/tests.rs @@ -50,7 +50,7 @@ use sp_runtime::{ traits::{BlakeTwo256, Convert, Hash, IdentityLookup}, AccountId32, TokenError, }; -use std::{ops::Deref, sync::Arc}; +use std::ops::Deref; use crate as pallet_contracts; @@ -444,7 +444,7 @@ impl ExtBuilder { .assimilate_storage(&mut t) .unwrap(); let mut ext = sp_io::TestExternalities::new(t); - ext.register_extension(KeystoreExt(Arc::new(MemoryKeystore::new()))); + ext.register_extension(KeystoreExt::new(MemoryKeystore::new())); ext.execute_with(|| System::set_block_number(1)); ext } diff --git a/frame/examples/offchain-worker/src/tests.rs b/frame/examples/offchain-worker/src/tests.rs index c51359043..3df7f4a8d 100644 --- a/frame/examples/offchain-worker/src/tests.rs +++ b/frame/examples/offchain-worker/src/tests.rs @@ -27,7 +27,6 @@ use sp_core::{ sr25519::Signature, H256, }; -use std::sync::Arc; use sp_keystore::{testing::MemoryKeystore, Keystore, KeystoreExt}; use sp_runtime::{ @@ -206,17 +205,14 @@ fn should_submit_signed_transaction_on_chain() { let (offchain, offchain_state) = testing::TestOffchainExt::new(); let (pool, pool_state) = testing::TestTransactionPoolExt::new(); let keystore = MemoryKeystore::new(); - Keystore::sr25519_generate_new( - &keystore, - crate::crypto::Public::ID, - Some(&format!("{}/hunter1", PHRASE)), - ) - .unwrap(); + keystore + .sr25519_generate_new(crate::crypto::Public::ID, Some(&format!("{}/hunter1", PHRASE))) + .unwrap(); let mut t = sp_io::TestExternalities::default(); t.register_extension(OffchainWorkerExt::new(offchain)); t.register_extension(TransactionPoolExt::new(pool)); - t.register_extension(KeystoreExt(Arc::new(keystore))); + t.register_extension(KeystoreExt::new(keystore)); price_oracle_response(&mut offchain_state.write()); @@ -241,21 +237,16 @@ fn should_submit_unsigned_transaction_on_chain_for_any_account() { let keystore = MemoryKeystore::new(); - Keystore::sr25519_generate_new( - &keystore, - crate::crypto::Public::ID, - Some(&format!("{}/hunter1", PHRASE)), - ) - .unwrap(); - - let public_key = *Keystore::sr25519_public_keys(&keystore, crate::crypto::Public::ID) - .get(0) + keystore + .sr25519_generate_new(crate::crypto::Public::ID, Some(&format!("{}/hunter1", PHRASE))) .unwrap(); + let public_key = *keystore.sr25519_public_keys(crate::crypto::Public::ID).get(0).unwrap(); + let mut t = sp_io::TestExternalities::default(); t.register_extension(OffchainWorkerExt::new(offchain)); t.register_extension(TransactionPoolExt::new(pool)); - t.register_extension(KeystoreExt(Arc::new(keystore))); + t.register_extension(KeystoreExt::new(keystore)); price_oracle_response(&mut offchain_state.write()); @@ -300,21 +291,16 @@ fn should_submit_unsigned_transaction_on_chain_for_all_accounts() { let keystore = MemoryKeystore::new(); - Keystore::sr25519_generate_new( - &keystore, - crate::crypto::Public::ID, - Some(&format!("{}/hunter1", PHRASE)), - ) - .unwrap(); - - let public_key = *Keystore::sr25519_public_keys(&keystore, crate::crypto::Public::ID) - .get(0) + keystore + .sr25519_generate_new(crate::crypto::Public::ID, Some(&format!("{}/hunter1", PHRASE))) .unwrap(); + let public_key = *keystore.sr25519_public_keys(crate::crypto::Public::ID).get(0).unwrap(); + let mut t = sp_io::TestExternalities::default(); t.register_extension(OffchainWorkerExt::new(offchain)); t.register_extension(TransactionPoolExt::new(pool)); - t.register_extension(KeystoreExt(Arc::new(keystore))); + t.register_extension(KeystoreExt::new(keystore)); price_oracle_response(&mut offchain_state.write()); @@ -360,7 +346,7 @@ fn should_submit_raw_unsigned_transaction_on_chain() { let mut t = sp_io::TestExternalities::default(); t.register_extension(OffchainWorkerExt::new(offchain)); t.register_extension(TransactionPoolExt::new(pool)); - t.register_extension(KeystoreExt(Arc::new(keystore))); + t.register_extension(KeystoreExt::new(keystore)); price_oracle_response(&mut offchain_state.write()); diff --git a/frame/nfts/src/mock.rs b/frame/nfts/src/mock.rs index 91ac5d6a7..e2856a07b 100644 --- a/frame/nfts/src/mock.rs +++ b/frame/nfts/src/mock.rs @@ -31,7 +31,6 @@ use sp_runtime::{ traits::{BlakeTwo256, IdentifyAccount, IdentityLookup, Verify}, MultiSignature, }; -use std::sync::Arc; type UncheckedExtrinsic = frame_system::mocking::MockUncheckedExtrinsic; type Block = frame_system::mocking::MockBlock; @@ -134,9 +133,8 @@ impl Config for Test { pub(crate) fn new_test_ext() -> sp_io::TestExternalities { let t = frame_system::GenesisConfig::default().build_storage::().unwrap(); - let keystore = MemoryKeystore::new(); let mut ext = sp_io::TestExternalities::new(t); - ext.register_extension(KeystoreExt(Arc::new(keystore))); + ext.register_extension(KeystoreExt::new(MemoryKeystore::new())); ext.execute_with(|| System::set_block_number(1)); ext } diff --git a/primitives/application-crypto/test/src/ecdsa.rs b/primitives/application-crypto/test/src/ecdsa.rs index 7b96b42d5..fe9e17a65 100644 --- a/primitives/application-crypto/test/src/ecdsa.rs +++ b/primitives/application-crypto/test/src/ecdsa.rs @@ -34,7 +34,7 @@ fn ecdsa_works_in_runtime() { .test_ecdsa_crypto(test_client.chain_info().genesis_hash) .expect("Tests `ecdsa` crypto."); - let supported_keys = Keystore::keys(&*keystore, ECDSA).unwrap(); + let supported_keys = keystore.keys(ECDSA).unwrap(); assert!(supported_keys.contains(&public.clone().into())); assert!(AppPair::verify(&signature, "ecdsa", &AppPublic::from(public))); } diff --git a/primitives/application-crypto/test/src/ed25519.rs b/primitives/application-crypto/test/src/ed25519.rs index 1cf2b574b..5687bd013 100644 --- a/primitives/application-crypto/test/src/ed25519.rs +++ b/primitives/application-crypto/test/src/ed25519.rs @@ -35,7 +35,7 @@ fn ed25519_works_in_runtime() { .test_ed25519_crypto(test_client.chain_info().genesis_hash) .expect("Tests `ed25519` crypto."); - let supported_keys = Keystore::keys(&*keystore, ED25519).unwrap(); + let supported_keys = keystore.keys(ED25519).unwrap(); assert!(supported_keys.contains(&public.clone().into())); assert!(AppPair::verify(&signature, "ed25519", &AppPublic::from(public))); } diff --git a/primitives/application-crypto/test/src/sr25519.rs b/primitives/application-crypto/test/src/sr25519.rs index aa8f75c0d..92886b80c 100644 --- a/primitives/application-crypto/test/src/sr25519.rs +++ b/primitives/application-crypto/test/src/sr25519.rs @@ -35,7 +35,7 @@ fn sr25519_works_in_runtime() { .test_sr25519_crypto(test_client.chain_info().genesis_hash) .expect("Tests `sr25519` crypto."); - let supported_keys = Keystore::keys(&*keystore, SR25519).unwrap(); + let supported_keys = keystore.keys(SR25519).unwrap(); assert!(supported_keys.contains(&public.clone().into())); assert!(AppPair::verify(&signature, "sr25519", &AppPublic::from(public))); } diff --git a/primitives/consensus/beefy/src/commitment.rs b/primitives/consensus/beefy/src/commitment.rs index 6ae17b06d..8ad3cc372 100644 --- a/primitives/consensus/beefy/src/commitment.rs +++ b/primitives/consensus/beefy/src/commitment.rs @@ -253,7 +253,7 @@ mod tests { use crate::{crypto, known_payloads, KEY_TYPE}; use codec::Decode; use sp_core::{keccak_256, Pair}; - use sp_keystore::{testing::MemoryKeystore, Keystore, KeystorePtr}; + use sp_keystore::{testing::MemoryKeystore, KeystorePtr}; type TestCommitment = Commitment; type TestSignedCommitment = SignedCommitment; @@ -266,17 +266,13 @@ mod tests { let store: KeystorePtr = MemoryKeystore::new().into(); let alice = sp_core::ecdsa::Pair::from_string("//Alice", None).unwrap(); - let _ = Keystore::insert(&*store, KEY_TYPE, "//Alice", alice.public().as_ref()).unwrap(); + store.insert(KEY_TYPE, "//Alice", alice.public().as_ref()).unwrap(); let msg = keccak_256(b"This is the first message"); - let sig1 = Keystore::ecdsa_sign_prehashed(&*store, KEY_TYPE, &alice.public(), &msg) - .unwrap() - .unwrap(); + let sig1 = store.ecdsa_sign_prehashed(KEY_TYPE, &alice.public(), &msg).unwrap().unwrap(); let msg = keccak_256(b"This is the second message"); - let sig2 = Keystore::ecdsa_sign_prehashed(&*store, KEY_TYPE, &alice.public(), &msg) - .unwrap() - .unwrap(); + let sig2 = store.ecdsa_sign_prehashed(KEY_TYPE, &alice.public(), &msg).unwrap().unwrap(); (sig1.into(), sig2.into()) } diff --git a/primitives/consensus/beefy/src/witness.rs b/primitives/consensus/beefy/src/witness.rs index 70b924c0a..ff9a0401d 100644 --- a/primitives/consensus/beefy/src/witness.rs +++ b/primitives/consensus/beefy/src/witness.rs @@ -74,9 +74,8 @@ impl SignedCommitmentWitness Vec { - let keystore = &***self - .extension::() - .expect("No `keystore` associated for the current context!"); - Keystore::ed25519_public_keys(keystore, id) + self.extension::() + .expect("No `keystore` associated for the current context!") + .ed25519_public_keys(id) } /// Generate an `ed22519` key for the given key type using an optional `seed` and @@ -745,10 +744,10 @@ pub trait Crypto { /// Returns the public key. fn ed25519_generate(&mut self, id: KeyTypeId, seed: Option>) -> ed25519::Public { let seed = seed.as_ref().map(|s| std::str::from_utf8(s).expect("Seed is valid utf8!")); - let keystore = &***self - .extension::() - .expect("No `keystore` associated for the current context!"); - Keystore::ed25519_generate_new(keystore, id, seed).expect("`ed25519_generate` failed") + self.extension::() + .expect("No `keystore` associated for the current context!") + .ed25519_generate_new(id, seed) + .expect("`ed25519_generate` failed") } /// Sign the given `msg` with the `ed25519` key that corresponds to the given public key and @@ -761,10 +760,9 @@ pub trait Crypto { pub_key: &ed25519::Public, msg: &[u8], ) -> Option { - let keystore = &***self - .extension::() - .expect("No `keystore` associated for the current context!"); - Keystore::sign_with(keystore, id, &pub_key.into(), msg) + self.extension::() + .expect("No `keystore` associated for the current context!") + .sign_with(id, &pub_key.into(), msg) .ok() .flatten() .and_then(|sig| ed25519::Signature::from_slice(&sig)) @@ -873,10 +871,9 @@ pub trait Crypto { /// Returns all `sr25519` public keys for the given key id from the keystore. fn sr25519_public_keys(&mut self, id: KeyTypeId) -> Vec { - let keystore = &***self - .extension::() - .expect("No `keystore` associated for the current context!"); - Keystore::sr25519_public_keys(keystore, id) + self.extension::() + .expect("No `keystore` associated for the current context!") + .sr25519_public_keys(id) } /// Generate an `sr22519` key for the given key type using an optional seed and @@ -887,10 +884,10 @@ pub trait Crypto { /// Returns the public key. fn sr25519_generate(&mut self, id: KeyTypeId, seed: Option>) -> sr25519::Public { let seed = seed.as_ref().map(|s| std::str::from_utf8(s).expect("Seed is valid utf8!")); - let keystore = &***self - .extension::() - .expect("No `keystore` associated for the current context!"); - Keystore::sr25519_generate_new(keystore, id, seed).expect("`sr25519_generate` failed") + self.extension::() + .expect("No `keystore` associated for the current context!") + .sr25519_generate_new(id, seed) + .expect("`sr25519_generate` failed") } /// Sign the given `msg` with the `sr25519` key that corresponds to the given public key and @@ -903,10 +900,9 @@ pub trait Crypto { pub_key: &sr25519::Public, msg: &[u8], ) -> Option { - let keystore = &***self - .extension::() - .expect("No `keystore` associated for the current context!"); - Keystore::sign_with(keystore, id, &pub_key.into(), msg) + self.extension::() + .expect("No `keystore` associated for the current context!") + .sign_with(id, &pub_key.into(), msg) .ok() .flatten() .and_then(|sig| sr25519::Signature::from_slice(&sig)) @@ -922,10 +918,9 @@ pub trait Crypto { /// Returns all `ecdsa` public keys for the given key id from the keystore. fn ecdsa_public_keys(&mut self, id: KeyTypeId) -> Vec { - let keystore = &***self - .extension::() - .expect("No `keystore` associated for the current context!"); - Keystore::ecdsa_public_keys(keystore, id) + self.extension::() + .expect("No `keystore` associated for the current context!") + .ecdsa_public_keys(id) } /// Generate an `ecdsa` key for the given key type using an optional `seed` and @@ -936,10 +931,10 @@ pub trait Crypto { /// Returns the public key. fn ecdsa_generate(&mut self, id: KeyTypeId, seed: Option>) -> ecdsa::Public { let seed = seed.as_ref().map(|s| std::str::from_utf8(s).expect("Seed is valid utf8!")); - let keystore = &***self - .extension::() - .expect("No `keystore` associated for the current context!"); - Keystore::ecdsa_generate_new(keystore, id, seed).expect("`ecdsa_generate` failed") + self.extension::() + .expect("No `keystore` associated for the current context!") + .ecdsa_generate_new(id, seed) + .expect("`ecdsa_generate` failed") } /// Sign the given `msg` with the `ecdsa` key that corresponds to the given public key and @@ -952,10 +947,9 @@ pub trait Crypto { pub_key: &ecdsa::Public, msg: &[u8], ) -> Option { - let keystore = &***self - .extension::() - .expect("No `keystore` associated for the current context!"); - Keystore::sign_with(keystore, id, &pub_key.into(), msg) + self.extension::() + .expect("No `keystore` associated for the current context!") + .sign_with(id, &pub_key.into(), msg) .ok() .flatten() .and_then(|sig| ecdsa::Signature::from_slice(&sig)) @@ -971,10 +965,11 @@ pub trait Crypto { pub_key: &ecdsa::Public, msg: &[u8; 32], ) -> Option { - let keystore = &***self - .extension::() - .expect("No `keystore` associated for the current context!"); - Keystore::ecdsa_sign_prehashed(keystore, id, pub_key, msg).ok().flatten() + self.extension::() + .expect("No `keystore` associated for the current context!") + .ecdsa_sign_prehashed(id, pub_key, msg) + .ok() + .flatten() } /// Verify `ecdsa` signature. diff --git a/primitives/keystore/src/lib.rs b/primitives/keystore/src/lib.rs index e0c00967b..d1cbe227e 100644 --- a/primitives/keystore/src/lib.rs +++ b/primitives/keystore/src/lib.rs @@ -150,10 +150,17 @@ pub trait Keystore: Send + Sync { ) -> Result, Error>; } -/// A pointer to a keystore. +/// A shared pointer to a keystore implementation. pub type KeystorePtr = Arc; sp_externalities::decl_extension! { /// The keystore extension to register/retrieve from the externalities. pub struct KeystoreExt(KeystorePtr); } + +impl KeystoreExt { + /// Create a new instance of `KeystoreExt` + pub fn new(keystore: T) -> Self { + Self(Arc::new(keystore)) + } +} diff --git a/primitives/keystore/src/testing.rs b/primitives/keystore/src/testing.rs index fecc11342..73b560d56 100644 --- a/primitives/keystore/src/testing.rs +++ b/primitives/keystore/src/testing.rs @@ -304,9 +304,9 @@ mod tests { fn store_key_and_extract() { let store = MemoryKeystore::new(); - let public = Keystore::ed25519_generate_new(&store, ED25519, None).expect("Generates key"); + let public = store.ed25519_generate_new(ED25519, None).expect("Generates key"); - let public_keys = Keystore::keys(&store, ED25519).unwrap(); + let public_keys = store.keys(ED25519).unwrap(); assert!(public_keys.contains(&public.into())); } @@ -318,10 +318,11 @@ mod tests { let secret_uri = "//Alice"; let key_pair = sr25519::Pair::from_string(secret_uri, None).expect("Generates key pair"); - Keystore::insert(&store, SR25519, secret_uri, key_pair.public().as_ref()) + store + .insert(SR25519, secret_uri, key_pair.public().as_ref()) .expect("Inserts unknown key"); - let public_keys = Keystore::keys(&store, SR25519).unwrap(); + let public_keys = store.keys(SR25519).unwrap(); assert!(public_keys.contains(&key_pair.public().into())); } @@ -342,19 +343,14 @@ mod tests { ], }; - let result = Keystore::sr25519_vrf_sign( - &store, - SR25519, - &key_pair.public(), - transcript_data.clone(), - ); + let result = store.sr25519_vrf_sign(SR25519, &key_pair.public(), transcript_data.clone()); assert!(result.unwrap().is_none()); - Keystore::insert(&store, SR25519, secret_uri, key_pair.public().as_ref()) + store + .insert(SR25519, secret_uri, key_pair.public().as_ref()) .expect("Inserts unknown key"); - let result = - Keystore::sr25519_vrf_sign(&store, SR25519, &key_pair.public(), transcript_data); + let result = store.sr25519_vrf_sign(SR25519, &key_pair.public(), transcript_data); assert!(result.unwrap().is_some()); } @@ -369,13 +365,13 @@ mod tests { let msg = sp_core::keccak_256(b"this should be a hashed message"); // no key in key store - let res = Keystore::ecdsa_sign_prehashed(&store, ECDSA, &pair.public(), &msg).unwrap(); + let res = store.ecdsa_sign_prehashed(ECDSA, &pair.public(), &msg).unwrap(); assert!(res.is_none()); // insert key, sign again - Keystore::insert(&store, ECDSA, suri, pair.public().as_ref()).unwrap(); + store.insert(ECDSA, suri, pair.public().as_ref()).unwrap(); - let res = Keystore::ecdsa_sign_prehashed(&store, ECDSA, &pair.public(), &msg).unwrap(); + let res = store.ecdsa_sign_prehashed(ECDSA, &pair.public(), &msg).unwrap(); assert!(res.is_some()); } } diff --git a/utils/frame/benchmarking-cli/src/pallet/command.rs b/utils/frame/benchmarking-cli/src/pallet/command.rs index f11e6bd92..15ebc668f 100644 --- a/utils/frame/benchmarking-cli/src/pallet/command.rs +++ b/utils/frame/benchmarking-cli/src/pallet/command.rs @@ -38,10 +38,10 @@ use sp_core::{ traits::CallContext, }; use sp_externalities::Extensions; -use sp_keystore::{testing::MemoryKeystore, KeystoreExt, KeystorePtr}; +use sp_keystore::{testing::MemoryKeystore, KeystoreExt}; use sp_runtime::traits::{Block as BlockT, Header as HeaderT}; use sp_state_machine::StateMachine; -use std::{collections::HashMap, fmt::Debug, fs, str::FromStr, sync::Arc, time}; +use std::{collections::HashMap, fmt::Debug, fs, str::FromStr, time}; /// Logging target const LOG_TARGET: &'static str = "frame::benchmark::pallet"; @@ -218,9 +218,10 @@ impl PalletCmd { let extensions = || -> Extensions { let mut extensions = Extensions::default(); - extensions.register(KeystoreExt(Arc::new(MemoryKeystore::new()) as KeystorePtr)); let (offchain, _) = TestOffchainExt::new(); let (pool, _) = TestTransactionPoolExt::new(); + let keystore = MemoryKeystore::new(); + extensions.register(KeystoreExt::new(keystore)); extensions.register(OffchainWorkerExt::new(offchain.clone())); extensions.register(OffchainDbExt::new(offchain)); extensions.register(TransactionPoolExt::new(pool)); diff --git a/utils/frame/try-runtime/cli/src/lib.rs b/utils/frame/try-runtime/cli/src/lib.rs index bd914d3a2..34966fc2a 100644 --- a/utils/frame/try-runtime/cli/src/lib.rs +++ b/utils/frame/try-runtime/cli/src/lib.rs @@ -816,9 +816,10 @@ pub(crate) fn full_extensions() -> Extensions { extensions.register(TaskExecutorExt::new(TaskExecutor::new())); let (offchain, _offchain_state) = TestOffchainExt::new(); let (pool, _pool_state) = TestTransactionPoolExt::new(); + let keystore = MemoryKeystore::new(); extensions.register(OffchainDbExt::new(offchain.clone())); extensions.register(OffchainWorkerExt::new(offchain)); - extensions.register(KeystoreExt(std::sync::Arc::new(MemoryKeystore::new()))); + extensions.register(KeystoreExt::new(keystore)); extensions.register(TransactionPoolExt::new(pool)); extensions From b8b8cce3400c4c157579404e17d4c623a83c3617 Mon Sep 17 00:00:00 2001 From: Michal Kucharczyk <1728078+michalkucharczyk@users.noreply.github.com> Date: Mon, 20 Mar 2023 21:58:21 +0100 Subject: [PATCH 264/558] frame::executive: LOG_TARGET const added (#13650) part of: #12873 Co-authored-by: parity-processbot <> --- frame/executive/src/lib.rs | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/frame/executive/src/lib.rs b/frame/executive/src/lib.rs index 10cf6e06b..5003920ea 100644 --- a/frame/executive/src/lib.rs +++ b/frame/executive/src/lib.rs @@ -137,6 +137,9 @@ use sp_runtime::{ }; use sp_std::{marker::PhantomData, prelude::*}; +#[allow(dead_code)] +const LOG_TARGET: &str = "runtime::executive"; + pub type CheckedOf = >::Checked; pub type CallOf = as Applyable>::Call; pub type OriginOf = as Dispatchable>::RuntimeOrigin; @@ -240,7 +243,7 @@ where select: frame_try_runtime::TryStateSelect, ) -> Result { frame_support::log::info!( - target: "frame::executive", + target: LOG_TARGET, "try-runtime: executing block #{:?} / state root check: {:?} / signature check: {:?} / try-state-select: {:?}", block.header().number(), state_root_check, @@ -277,7 +280,7 @@ where for e in extrinsics { if let Err(err) = try_apply_extrinsic(e.clone()) { frame_support::log::error!( - target: "runtime::executive", "executing transaction {:?} failed due to {:?}. Aborting the rest of the block execution.", + target: LOG_TARGET, "executing transaction {:?} failed due to {:?}. Aborting the rest of the block execution.", e, err, ); @@ -296,7 +299,7 @@ where select, ) .map_err(|e| { - frame_support::log::error!(target: "runtime::executive", "failure: {:?}", e); + frame_support::log::error!(target: LOG_TARGET, "failure: {:?}", e); e })?; drop(_guard); From 8f0b0f69e79c0dcf1487b9858792439c35a1d4cd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alexander=20Thei=C3=9Fen?= Date: Tue, 21 Mar 2023 00:09:22 +0100 Subject: [PATCH 265/558] contracts: Upgrade to wasmi 0.28 (#13312) * Upgrade to wasmi 0.28 * ".git/.scripts/commands/bench/bench.sh" pallet dev pallet_contracts * Update stale comment * Renamed variants of `Determinism` * Compile fix --------- Co-authored-by: command-bot <> --- Cargo.lock | 21 +- bin/node/runtime/src/lib.rs | 2 +- frame/contracts/Cargo.toml | 4 +- frame/contracts/proc-macro/src/lib.rs | 4 +- frame/contracts/src/benchmarking/code.rs | 2 +- frame/contracts/src/benchmarking/mod.rs | 12 +- frame/contracts/src/exec.rs | 74 +- frame/contracts/src/lib.rs | 12 +- frame/contracts/src/migration.rs | 4 +- frame/contracts/src/schedule.rs | 7 +- frame/contracts/src/tests.rs | 129 +- frame/contracts/src/wasm/mod.rs | 22 +- frame/contracts/src/wasm/prepare.rs | 70 +- frame/contracts/src/wasm/runtime.rs | 6 +- frame/contracts/src/weights.rs | 2362 +++++++++++----------- 15 files changed, 1338 insertions(+), 1393 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 492887356..5306d2015 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -5751,7 +5751,7 @@ dependencies = [ "sp-runtime", "sp-std", "wasm-instrument 0.4.0", - "wasmi 0.20.0", + "wasmi 0.28.0", "wasmparser-nostd", "wat", ] @@ -12188,13 +12188,13 @@ dependencies = [ [[package]] name = "wasmi" -version = "0.20.0" +version = "0.28.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "01bf50edb2ea9d922aa75a7bf3c15e26a6c9e2d18c56e862b49737a582901729" +checksum = "8e61a7006b0fdf24f6bbe8dcfdad5ca1b350de80061fb2827f31c82fbbb9565a" dependencies = [ "spin 0.9.5", "wasmi_arena", - "wasmi_core 0.5.0", + "wasmi_core 0.12.0", "wasmparser-nostd", ] @@ -12209,9 +12209,9 @@ dependencies = [ [[package]] name = "wasmi_arena" -version = "0.1.0" +version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a1ea379cbb0b41f3a9f0bf7b47036d036aae7f43383d8cc487d4deccf40dee0a" +checksum = "401c1f35e413fac1846d4843745589d9ec678977ab35a384db8ae7830525d468" [[package]] name = "wasmi_core" @@ -12229,13 +12229,14 @@ dependencies = [ [[package]] name = "wasmi_core" -version = "0.5.0" +version = "0.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c5bf998ab792be85e20e771fe14182b4295571ad1d4f89d3da521c1bef5f597a" +checksum = "624e6333e861ef49095d2d678b76ebf30b06bf37effca845be7e5b87c90071b7" dependencies = [ "downcast-rs", "libm 0.2.6", "num-traits", + "paste", ] [[package]] @@ -12250,9 +12251,9 @@ dependencies = [ [[package]] name = "wasmparser-nostd" -version = "0.91.0" +version = "0.100.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c37f310b5a62bfd5ae7c0f1d8e6f98af16a5d6d84ba764e9c36439ec14e318b" +checksum = "9157cab83003221bfd385833ab587a039f5d6fa7304854042ba358a3b09e0724" dependencies = [ "indexmap-nostd", ] diff --git a/bin/node/runtime/src/lib.rs b/bin/node/runtime/src/lib.rs index 3f0ea0712..48bea5ddc 100644 --- a/bin/node/runtime/src/lib.rs +++ b/bin/node/runtime/src/lib.rs @@ -2147,7 +2147,7 @@ impl_runtime_apis! { storage_deposit_limit, input_data, true, - pallet_contracts::Determinism::Deterministic, + pallet_contracts::Determinism::Enforced, ) } diff --git a/frame/contracts/Cargo.toml b/frame/contracts/Cargo.toml index e19326b78..f357a21b9 100644 --- a/frame/contracts/Cargo.toml +++ b/frame/contracts/Cargo.toml @@ -26,8 +26,8 @@ serde = { version = "1", optional = true, features = ["derive"] } smallvec = { version = "1", default-features = false, features = [ "const_generics", ] } -wasmi = { version = "0.20", default-features = false } -wasmparser = { package = "wasmparser-nostd", version = "0.91", default-features = false } +wasmi = { version = "0.28", default-features = false } +wasmparser = { package = "wasmparser-nostd", version = "0.100", default-features = false } impl-trait-for-tuples = "0.2" # Only used in benchmarking to generate random contract code diff --git a/frame/contracts/proc-macro/src/lib.rs b/frame/contracts/proc-macro/src/lib.rs index c3e61dae5..32241068f 100644 --- a/frame/contracts/proc-macro/src/lib.rs +++ b/frame/contracts/proc-macro/src/lib.rs @@ -613,7 +613,7 @@ fn expand_functions(def: &EnvDef, expand_blocks: bool, host_state: TokenStream2) let inner = if expand_blocks { quote! { || #output { let (memory, ctx) = __caller__ - .host_data() + .data() .memory() .expect("Memory must be set when setting up host data; qed") .data_and_store_mut(&mut __caller__); @@ -630,7 +630,7 @@ fn expand_functions(def: &EnvDef, expand_blocks: bool, host_state: TokenStream2) let map_err = if expand_blocks { quote! { |reason| { - ::wasmi::core::Trap::host(reason) + ::wasmi::core::Trap::from(reason) } } } else { diff --git a/frame/contracts/src/benchmarking/code.rs b/frame/contracts/src/benchmarking/code.rs index c2d623ae4..07f24f385 100644 --- a/frame/contracts/src/benchmarking/code.rs +++ b/frame/contracts/src/benchmarking/code.rs @@ -512,7 +512,7 @@ pub fn max_pages() -> u32 { fn inject_gas_metering(module: Module) -> Module { let schedule = T::Schedule::get(); - let gas_rules = schedule.rules(Determinism::Deterministic); + let gas_rules = schedule.rules(Determinism::Enforced); let backend = gas_metering::host_function::Injector::new("seal0", "gas"); gas_metering::inject(module, backend, &gas_rules).unwrap() } diff --git a/frame/contracts/src/benchmarking/mod.rs b/frame/contracts/src/benchmarking/mod.rs index fb8e4ac4b..9b75b140f 100644 --- a/frame/contracts/src/benchmarking/mod.rs +++ b/frame/contracts/src/benchmarking/mod.rs @@ -381,7 +381,7 @@ benchmarks! { T::Currency::make_free_balance_be(&caller, caller_funding::()); let WasmModule { code, hash, .. } = WasmModule::::sized(c, Location::Call); let origin = RawOrigin::Signed(caller.clone()); - }: _(origin, code, None, Determinism::Deterministic) + }: _(origin, code, None, Determinism::Enforced) verify { // uploading the code reserves some balance in the callers account assert!(T::Currency::reserved_balance(&caller) > 0u32.into()); @@ -397,7 +397,7 @@ benchmarks! { T::Currency::make_free_balance_be(&caller, caller_funding::()); let WasmModule { code, hash, .. } = WasmModule::::dummy(); let origin = RawOrigin::Signed(caller.clone()); - let uploaded = >::bare_upload_code(caller.clone(), code, None, Determinism::Deterministic)?; + let uploaded = >::bare_upload_code(caller.clone(), code, None, Determinism::Enforced)?; assert_eq!(uploaded.code_hash, hash); assert_eq!(uploaded.deposit, T::Currency::reserved_balance(&caller)); assert!(>::code_exists(&hash)); @@ -941,7 +941,7 @@ benchmarks! { None, vec![], true, - Determinism::Deterministic, + Determinism::Enforced, ) .result?; } @@ -990,7 +990,7 @@ benchmarks! { None, vec![], true, - Determinism::Deterministic, + Determinism::Enforced, ) .result?; } @@ -3063,7 +3063,7 @@ benchmarks! { None, data, false, - Determinism::Deterministic, + Determinism::Enforced, ) .result?; } @@ -3112,7 +3112,7 @@ benchmarks! { None, data, false, - Determinism::Deterministic, + Determinism::Enforced, ) .result?; } diff --git a/frame/contracts/src/exec.rs b/frame/contracts/src/exec.rs index 6a053712e..51ea234f2 100644 --- a/frame/contracts/src/exec.rs +++ b/frame/contracts/src/exec.rs @@ -665,7 +665,7 @@ where schedule, value, debug_message, - Determinism::Deterministic, + Determinism::Enforced, )?; let account_id = stack.top_frame().account_id.clone(); stack.run(executable, input_data).map(|ret| (account_id, ret)) @@ -759,10 +759,10 @@ where }, }; - // `AllowIndeterminism` will only be ever set in case of off-chain execution. + // `Relaxed` will only be ever set in case of off-chain execution. // Instantiations are never allowed even when executing off-chain. if !(executable.is_deterministic() || - (matches!(determinism, Determinism::AllowIndeterminism) && + (matches!(determinism, Determinism::Relaxed) && matches!(entry_point, ExportedFunction::Call))) { return Err(Error::::Indeterministic.into()) @@ -1607,7 +1607,7 @@ mod tests { value, vec![], None, - Determinism::Deterministic, + Determinism::Enforced, ), Ok(_) ); @@ -1661,7 +1661,7 @@ mod tests { value, vec![], None, - Determinism::Deterministic, + Determinism::Enforced, ) .unwrap(); @@ -1703,7 +1703,7 @@ mod tests { value, vec![], None, - Determinism::Deterministic, + Determinism::Enforced, ) .unwrap(); @@ -1739,7 +1739,7 @@ mod tests { 55, vec![], None, - Determinism::Deterministic, + Determinism::Enforced, ) .unwrap(); @@ -1791,7 +1791,7 @@ mod tests { 0, vec![], None, - Determinism::Deterministic, + Determinism::Enforced, ); let output = result.unwrap(); @@ -1824,7 +1824,7 @@ mod tests { 0, vec![], None, - Determinism::Deterministic, + Determinism::Enforced, ); let output = result.unwrap(); @@ -1855,7 +1855,7 @@ mod tests { 0, vec![1, 2, 3, 4], None, - Determinism::Deterministic, + Determinism::Enforced, ); assert_matches!(result, Ok(_)); }); @@ -1935,7 +1935,7 @@ mod tests { value, vec![], None, - Determinism::Deterministic, + Determinism::Enforced, ); assert_matches!(result, Ok(_)); @@ -1981,7 +1981,7 @@ mod tests { 0, vec![], None, - Determinism::Deterministic, + Determinism::Enforced, ); assert_matches!(result, Ok(_)); @@ -2015,7 +2015,7 @@ mod tests { 0, vec![], None, - Determinism::Deterministic, + Determinism::Enforced, ); assert_matches!(result, Ok(_)); }); @@ -2045,7 +2045,7 @@ mod tests { 0, vec![0], None, - Determinism::Deterministic, + Determinism::Enforced, ); assert_matches!(result, Ok(_)); }); @@ -2073,7 +2073,7 @@ mod tests { 0, vec![0], None, - Determinism::Deterministic, + Determinism::Enforced, ); assert_matches!(result, Ok(_)); }); @@ -2109,7 +2109,7 @@ mod tests { 0, vec![0], None, - Determinism::Deterministic, + Determinism::Enforced, ); assert_matches!(result, Ok(_)); }); @@ -2145,7 +2145,7 @@ mod tests { 0, vec![], None, - Determinism::Deterministic, + Determinism::Enforced, ); assert_matches!(result, Ok(_)); @@ -2304,7 +2304,7 @@ mod tests { min_balance * 10, vec![], None, - Determinism::Deterministic, + Determinism::Enforced, ), Ok(_) ); @@ -2369,7 +2369,7 @@ mod tests { 0, vec![], None, - Determinism::Deterministic, + Determinism::Enforced, ), Ok(_) ); @@ -2455,7 +2455,7 @@ mod tests { 0, vec![0], None, - Determinism::Deterministic, + Determinism::Enforced, ); assert_matches!(result, Ok(_)); }); @@ -2521,7 +2521,7 @@ mod tests { 0, vec![], Some(&mut debug_buffer), - Determinism::Deterministic, + Determinism::Enforced, ) .unwrap(); }); @@ -2555,7 +2555,7 @@ mod tests { 0, vec![], Some(&mut debug_buffer), - Determinism::Deterministic, + Determinism::Enforced, ); assert!(result.is_err()); }); @@ -2592,7 +2592,7 @@ mod tests { 0, vec![], Some(&mut debug_buf_after), - Determinism::Deterministic, + Determinism::Enforced, ) .unwrap(); assert_eq!(debug_buf_before, debug_buf_after); @@ -2625,7 +2625,7 @@ mod tests { 0, CHARLIE.encode(), None, - Determinism::Deterministic + Determinism::Enforced )); // Calling into oneself fails @@ -2639,7 +2639,7 @@ mod tests { 0, BOB.encode(), None, - Determinism::Deterministic + Determinism::Enforced ) .map_err(|e| e.error), >::ReentranceDenied, @@ -2678,7 +2678,7 @@ mod tests { 0, vec![0], None, - Determinism::Deterministic + Determinism::Enforced ) .map_err(|e| e.error), >::ReentranceDenied, @@ -2713,7 +2713,7 @@ mod tests { 0, vec![], None, - Determinism::Deterministic, + Determinism::Enforced, ) .unwrap(); @@ -2798,7 +2798,7 @@ mod tests { 0, vec![], None, - Determinism::Deterministic, + Determinism::Enforced, ) .unwrap(); @@ -3007,7 +3007,7 @@ mod tests { 0, vec![], None, - Determinism::Deterministic + Determinism::Enforced )); }); } @@ -3134,7 +3134,7 @@ mod tests { 0, vec![], None, - Determinism::Deterministic + Determinism::Enforced )); }); } @@ -3173,7 +3173,7 @@ mod tests { 0, vec![], None, - Determinism::Deterministic + Determinism::Enforced )); }); } @@ -3212,7 +3212,7 @@ mod tests { 0, vec![], None, - Determinism::Deterministic + Determinism::Enforced )); }); } @@ -3268,7 +3268,7 @@ mod tests { 0, vec![], None, - Determinism::Deterministic + Determinism::Enforced )); }); } @@ -3324,7 +3324,7 @@ mod tests { 0, vec![], None, - Determinism::Deterministic + Determinism::Enforced )); }); } @@ -3356,7 +3356,7 @@ mod tests { 0, vec![], None, - Determinism::Deterministic, + Determinism::Enforced, ); assert_matches!(result, Ok(_)); }); @@ -3402,7 +3402,7 @@ mod tests { 0, vec![], None, - Determinism::Deterministic + Determinism::Enforced )); }); } @@ -3432,7 +3432,7 @@ mod tests { 0, vec![], None, - Determinism::Deterministic, + Determinism::Enforced, ); assert_matches!(result, Ok(_)); }); diff --git a/frame/contracts/src/lib.rs b/frame/contracts/src/lib.rs index d3369b6c2..cd4b7daa6 100644 --- a/frame/contracts/src/lib.rs +++ b/frame/contracts/src/lib.rs @@ -509,9 +509,9 @@ pub mod pallet { /// the in storage version to the current /// [`InstructionWeights::version`](InstructionWeights). /// - /// - `determinism`: If this is set to any other value but [`Determinism::Deterministic`] - /// then the only way to use this code is to delegate call into it from an offchain - /// execution. Set to [`Determinism::Deterministic`] if in doubt. + /// - `determinism`: If this is set to any other value but [`Determinism::Enforced`] then + /// the only way to use this code is to delegate call into it from an offchain execution. + /// Set to [`Determinism::Enforced`] if in doubt. /// /// # Note /// @@ -625,8 +625,8 @@ pub mod pallet { storage_deposit_limit: storage_deposit_limit.map(Into::into), debug_message: None, }; - let mut output = CallInput:: { dest, determinism: Determinism::Deterministic } - .run_guarded(common); + let mut output = + CallInput:: { dest, determinism: Determinism::Enforced }.run_guarded(common); if let Ok(retval) = &output.result { if retval.did_revert() { output.result = Err(>::ContractReverted.into()); @@ -1096,7 +1096,7 @@ impl Invokable for InstantiateInput { binary.clone(), &schedule, common.origin.clone(), - Determinism::Deterministic, + Determinism::Enforced, TryInstantiate::Skip, ) .map_err(|(err, msg)| { diff --git a/frame/contracts/src/migration.rs b/frame/contracts/src/migration.rs index cd8f1dd1c..96a4c3203 100644 --- a/frame/contracts/src/migration.rs +++ b/frame/contracts/src/migration.rs @@ -394,7 +394,7 @@ mod v9 { initial: old.initial, maximum: old.maximum, code: old.code, - determinism: Determinism::Deterministic, + determinism: Determinism::Enforced, }) }); } @@ -464,7 +464,7 @@ mod post_checks { fn v9() -> Result<(), &'static str> { for value in CodeStorage::::iter_values() { ensure!( - value.determinism == Determinism::Deterministic, + value.determinism == Determinism::Enforced, "All pre-existing codes need to be deterministic." ); } diff --git a/frame/contracts/src/schedule.rs b/frame/contracts/src/schedule.rs index 796a7f6d3..ccf1e98bd 100644 --- a/frame/contracts/src/schedule.rs +++ b/frame/contracts/src/schedule.rs @@ -175,9 +175,9 @@ pub struct InstructionWeights { pub version: u32, /// Weight to be used for instructions which don't have benchmarks assigned. /// - /// This weight is used whenever a code is uploaded with [`Determinism::AllowIndeterminism`] + /// This weight is used whenever a code is uploaded with [`Determinism::Relaxed`] /// and an instruction (usually a float instruction) is encountered. This weight is **not** - /// used if a contract is uploaded with [`Determinism::Deterministic`]. If this field is set to + /// used if a contract is uploaded with [`Determinism::Enforced`]. If this field is set to /// `0` (the default) only deterministic codes are allowed to be uploaded. pub fallback: u32, pub i64const: u32, @@ -715,8 +715,7 @@ impl<'a, T: Config> gas_metering::Rules for ScheduleRules<'a, T> { // Returning None makes the gas instrumentation fail which we intend for // unsupported or unknown instructions. Offchain we might allow indeterminism and hence // use the fallback weight for those instructions. - _ if matches!(self.determinism, Determinism::AllowIndeterminism) && w.fallback > 0 => - w.fallback, + _ if matches!(self.determinism, Determinism::Relaxed) && w.fallback > 0 => w.fallback, _ => return None, }; Some(weight) diff --git a/frame/contracts/src/tests.rs b/frame/contracts/src/tests.rs index 915a56a3f..146e5fd24 100644 --- a/frame/contracts/src/tests.rs +++ b/frame/contracts/src/tests.rs @@ -529,7 +529,7 @@ fn instantiate_and_call_and_deposit_event() { RuntimeOrigin::signed(ALICE), wasm, None, - Determinism::Deterministic + Determinism::Enforced )); // Drop previous events @@ -726,13 +726,8 @@ fn instantiate_unique_trie_id() { ExtBuilder::default().existential_deposit(500).build().execute_with(|| { let _ = Balances::deposit_creating(&ALICE, 1_000_000); - Contracts::upload_code( - RuntimeOrigin::signed(ALICE), - wasm, - None, - Determinism::Deterministic, - ) - .unwrap(); + Contracts::upload_code(RuntimeOrigin::signed(ALICE), wasm, None, Determinism::Enforced) + .unwrap(); // Instantiate the contract and store its trie id for later comparison. let addr = Contracts::bare_instantiate( @@ -860,7 +855,7 @@ fn deploy_and_call_other_contract() { .result .unwrap() .account_id; - Contracts::bare_upload_code(ALICE, callee_wasm, None, Determinism::Deterministic).unwrap(); + Contracts::bare_upload_code(ALICE, callee_wasm, None, Determinism::Enforced).unwrap(); let callee_addr = Contracts::contract_address( &caller_addr, @@ -1011,7 +1006,7 @@ fn delegate_call() { RuntimeOrigin::signed(ALICE), callee_wasm, Some(codec::Compact(100_000)), - Determinism::Deterministic, + Determinism::Enforced, )); assert_ok!(Contracts::call( @@ -1321,7 +1316,7 @@ fn destroy_contract_and_transfer_funds() { ExtBuilder::default().existential_deposit(50).build().execute_with(|| { // Create code hash for bob to instantiate let _ = Balances::deposit_creating(&ALICE, 1_000_000); - Contracts::bare_upload_code(ALICE, callee_wasm, None, Determinism::Deterministic).unwrap(); + Contracts::bare_upload_code(ALICE, callee_wasm, None, Determinism::Enforced).unwrap(); // This deploys the BOB contract, which in turn deploys the CHARLIE contract during // construction. @@ -1431,7 +1426,7 @@ fn crypto_hashes() { None, params, false, - Determinism::Deterministic, + Determinism::Enforced, ) .result .unwrap(); @@ -1473,7 +1468,7 @@ fn transfer_return_code() { None, vec![], false, - Determinism::Deterministic, + Determinism::Enforced, ) .result .unwrap(); @@ -1514,7 +1509,7 @@ fn call_return_code() { None, AsRef::<[u8]>::as_ref(&DJANGO).to_vec(), false, - Determinism::Deterministic, + Determinism::Enforced, ) .result .unwrap(); @@ -1548,7 +1543,7 @@ fn call_return_code() { .cloned() .collect(), false, - Determinism::Deterministic, + Determinism::Enforced, ) .result .unwrap(); @@ -1568,7 +1563,7 @@ fn call_return_code() { .cloned() .collect(), false, - Determinism::Deterministic, + Determinism::Enforced, ) .result .unwrap(); @@ -1587,7 +1582,7 @@ fn call_return_code() { .cloned() .collect(), false, - Determinism::Deterministic, + Determinism::Enforced, ) .result .unwrap(); @@ -1639,7 +1634,7 @@ fn instantiate_return_code() { None, callee_hash.clone(), false, - Determinism::Deterministic, + Determinism::Enforced, ) .result .unwrap(); @@ -1655,7 +1650,7 @@ fn instantiate_return_code() { None, vec![0; 33], false, - Determinism::Deterministic, + Determinism::Enforced, ) .result .unwrap(); @@ -1670,7 +1665,7 @@ fn instantiate_return_code() { None, callee_hash.iter().chain(&1u32.to_le_bytes()).cloned().collect(), false, - Determinism::Deterministic, + Determinism::Enforced, ) .result .unwrap(); @@ -1685,7 +1680,7 @@ fn instantiate_return_code() { None, callee_hash.iter().chain(&2u32.to_le_bytes()).cloned().collect(), false, - Determinism::Deterministic, + Determinism::Enforced, ) .result .unwrap(); @@ -1772,7 +1767,7 @@ fn chain_extension_works() { None, input.clone(), false, - Determinism::Deterministic, + Determinism::Enforced, ); assert_eq!(TestExtension::last_seen_buffer(), input); assert_eq!(result.result.unwrap().data, input); @@ -1786,7 +1781,7 @@ fn chain_extension_works() { None, ExtensionInput { extension_id: 0, func_id: 1, extra: &[] }.into(), false, - Determinism::Deterministic, + Determinism::Enforced, ) .result .unwrap(); @@ -1802,7 +1797,7 @@ fn chain_extension_works() { None, ExtensionInput { extension_id: 0, func_id: 2, extra: &[0] }.into(), false, - Determinism::Deterministic, + Determinism::Enforced, ); assert_ok!(result.result); let gas_consumed = result.gas_consumed; @@ -1814,7 +1809,7 @@ fn chain_extension_works() { None, ExtensionInput { extension_id: 0, func_id: 2, extra: &[42] }.into(), false, - Determinism::Deterministic, + Determinism::Enforced, ); assert_ok!(result.result); assert_eq!(result.gas_consumed.ref_time(), gas_consumed.ref_time() + 42); @@ -1826,7 +1821,7 @@ fn chain_extension_works() { None, ExtensionInput { extension_id: 0, func_id: 2, extra: &[95] }.into(), false, - Determinism::Deterministic, + Determinism::Enforced, ); assert_ok!(result.result); assert_eq!(result.gas_consumed.ref_time(), gas_consumed.ref_time() + 95); @@ -1840,7 +1835,7 @@ fn chain_extension_works() { None, ExtensionInput { extension_id: 0, func_id: 3, extra: &[] }.into(), false, - Determinism::Deterministic, + Determinism::Enforced, ) .result .unwrap(); @@ -1858,7 +1853,7 @@ fn chain_extension_works() { None, ExtensionInput { extension_id: 1, func_id: 0, extra: &[] }.into(), false, - Determinism::Deterministic, + Determinism::Enforced, ) .result .unwrap(); @@ -1919,7 +1914,7 @@ fn chain_extension_temp_storage_works() { None, input.clone(), false, - Determinism::Deterministic + Determinism::Enforced ) .result ); @@ -2480,7 +2475,7 @@ fn reinstrument_does_charge() { None, zero.clone(), false, - Determinism::Deterministic, + Determinism::Enforced, ); assert!(!result0.result.unwrap().did_revert()); @@ -2492,7 +2487,7 @@ fn reinstrument_does_charge() { None, zero.clone(), false, - Determinism::Deterministic, + Determinism::Enforced, ); assert!(!result1.result.unwrap().did_revert()); @@ -2514,7 +2509,7 @@ fn reinstrument_does_charge() { None, zero.clone(), false, - Determinism::Deterministic, + Determinism::Enforced, ); assert!(!result2.result.unwrap().did_revert()); assert!(result2.gas_consumed.ref_time() > result1.gas_consumed.ref_time()); @@ -2553,7 +2548,7 @@ fn debug_message_works() { None, vec![], true, - Determinism::Deterministic, + Determinism::Enforced, ); assert_matches!(result.result, Ok(_)); @@ -2589,7 +2584,7 @@ fn debug_message_logging_disabled() { None, vec![], false, - Determinism::Deterministic, + Determinism::Enforced, ); assert_matches!(result.result, Ok(_)); // the dispatchables always run without debugging @@ -2625,7 +2620,7 @@ fn debug_message_invalid_utf8() { None, vec![], true, - Determinism::Deterministic, + Determinism::Enforced, ); assert_ok!(result.result); assert!(result.debug_message.is_empty()); @@ -2684,7 +2679,7 @@ fn gas_estimation_nested_call_fixed_limit() { None, input.clone(), false, - Determinism::Deterministic, + Determinism::Enforced, ); assert_ok!(&result.result); @@ -2701,7 +2696,7 @@ fn gas_estimation_nested_call_fixed_limit() { Some(result.storage_deposit.charge_or_zero()), input, false, - Determinism::Deterministic, + Determinism::Enforced, ) .result ); @@ -2760,7 +2755,7 @@ fn gas_estimation_call_runtime() { None, call.encode(), false, - Determinism::Deterministic, + Determinism::Enforced, ); // contract encodes the result of the dispatch runtime let outcome = u32::decode(&mut result.result.unwrap().data.as_ref()).unwrap(); @@ -2777,7 +2772,7 @@ fn gas_estimation_call_runtime() { None, call.encode(), false, - Determinism::Deterministic, + Determinism::Enforced, ) .result ); @@ -2840,7 +2835,7 @@ fn gas_call_runtime_reentrancy_guarded() { None, call.encode(), false, - Determinism::Deterministic, + Determinism::Enforced, ) .result .unwrap(); @@ -2902,7 +2897,7 @@ fn ecdsa_recover() { None, params, false, - Determinism::Deterministic, + Determinism::Enforced, ) .result .unwrap(); @@ -2926,7 +2921,7 @@ fn upload_code_works() { RuntimeOrigin::signed(ALICE), wasm, Some(codec::Compact(1_000)), - Determinism::Deterministic, + Determinism::Enforced, )); assert!(>::contains_key(code_hash)); @@ -2966,7 +2961,7 @@ fn upload_code_limit_too_low() { RuntimeOrigin::signed(ALICE), wasm, Some(codec::Compact(100)), - Determinism::Deterministic + Determinism::Enforced ), >::StorageDepositLimitExhausted, ); @@ -2990,7 +2985,7 @@ fn upload_code_not_enough_balance() { RuntimeOrigin::signed(ALICE), wasm, Some(codec::Compact(1_000)), - Determinism::Deterministic + Determinism::Enforced ), >::StorageDepositNotEnoughFunds, ); @@ -3013,7 +3008,7 @@ fn remove_code_works() { RuntimeOrigin::signed(ALICE), wasm, Some(codec::Compact(1_000)), - Determinism::Deterministic, + Determinism::Enforced, )); assert!(>::contains_key(code_hash)); @@ -3068,7 +3063,7 @@ fn remove_code_wrong_origin() { RuntimeOrigin::signed(ALICE), wasm, Some(codec::Compact(1_000)), - Determinism::Deterministic, + Determinism::Enforced, )); assert_noop!( @@ -3538,7 +3533,7 @@ fn set_code_extrinsic() { RuntimeOrigin::signed(ALICE), new_wasm, None, - Determinism::Deterministic + Determinism::Enforced )); // Drop previous events @@ -3658,7 +3653,7 @@ fn contract_reverted() { RuntimeOrigin::signed(ALICE), wasm.clone(), None, - Determinism::Deterministic + Determinism::Enforced )); // Calling extrinsic: revert leads to an error @@ -3746,7 +3741,7 @@ fn contract_reverted() { None, input, false, - Determinism::Deterministic, + Determinism::Enforced, ) .result .unwrap(); @@ -3766,7 +3761,7 @@ fn code_rejected_error_works() { RuntimeOrigin::signed(ALICE), wasm.clone(), None, - Determinism::Deterministic + Determinism::Enforced ), >::CodeRejected, ); @@ -3792,7 +3787,7 @@ fn code_rejected_error_works() { RuntimeOrigin::signed(ALICE), wasm.clone(), None, - Determinism::Deterministic + Determinism::Enforced ), >::CodeRejected, ); @@ -3842,7 +3837,7 @@ fn set_code_hash() { RuntimeOrigin::signed(ALICE), new_wasm.clone(), None, - Determinism::Deterministic + Determinism::Enforced )); System::reset_events(); @@ -3856,7 +3851,7 @@ fn set_code_hash() { None, new_code_hash.as_ref().to_vec(), true, - Determinism::Deterministic, + Determinism::Enforced, ) .result .unwrap(); @@ -3871,7 +3866,7 @@ fn set_code_hash() { None, vec![], true, - Determinism::Deterministic, + Determinism::Enforced, ) .result .unwrap(); @@ -4268,7 +4263,7 @@ fn cannot_instantiate_indeterministic_code() { RuntimeOrigin::signed(ALICE), wasm.clone(), None, - Determinism::Deterministic + Determinism::Enforced ), >::CodeRejected, ); @@ -4278,7 +4273,7 @@ fn cannot_instantiate_indeterministic_code() { RuntimeOrigin::signed(ALICE), wasm, None, - Determinism::AllowIndeterminism, + Determinism::Relaxed, )); assert_err_ignore_postinfo!( Contracts::instantiate( @@ -4332,7 +4327,7 @@ fn cannot_instantiate_indeterministic_code() { None, code_hash.encode(), false, - Determinism::Deterministic, + Determinism::Enforced, ) .result, >::Indeterministic, @@ -4348,7 +4343,7 @@ fn cannot_instantiate_indeterministic_code() { None, code_hash.encode(), false, - Determinism::AllowIndeterminism, + Determinism::Relaxed, ) .result, >::Indeterministic, @@ -4368,7 +4363,7 @@ fn cannot_set_code_indeterministic_code() { RuntimeOrigin::signed(ALICE), wasm, None, - Determinism::AllowIndeterminism, + Determinism::Relaxed, )); // Create the contract that will call `seal_set_code_hash` @@ -4396,7 +4391,7 @@ fn cannot_set_code_indeterministic_code() { None, code_hash.encode(), false, - Determinism::AllowIndeterminism, + Determinism::Relaxed, ) .result, >::Indeterministic, @@ -4416,7 +4411,7 @@ fn delegate_call_indeterministic_code() { RuntimeOrigin::signed(ALICE), wasm, None, - Determinism::AllowIndeterminism, + Determinism::Relaxed, )); // Create the contract that will call `seal_delegate_call` @@ -4444,7 +4439,7 @@ fn delegate_call_indeterministic_code() { None, code_hash.encode(), false, - Determinism::Deterministic, + Determinism::Enforced, ) .result, >::Indeterministic, @@ -4460,7 +4455,7 @@ fn delegate_call_indeterministic_code() { None, code_hash.encode(), false, - Determinism::AllowIndeterminism, + Determinism::Relaxed, ) .result ); @@ -4499,7 +4494,7 @@ fn reentrance_count_works_with_call() { None, input, true, - Determinism::Deterministic, + Determinism::Enforced, ) .result .unwrap(); @@ -4538,7 +4533,7 @@ fn reentrance_count_works_with_delegated_call() { None, input, true, - Determinism::Deterministic, + Determinism::Enforced, ) .result .unwrap(); @@ -4590,7 +4585,7 @@ fn account_reentrance_count_works() { None, contract_addr.encode(), true, - Determinism::Deterministic, + Determinism::Enforced, ) .result .unwrap(); @@ -4603,7 +4598,7 @@ fn account_reentrance_count_works() { None, another_contract_addr.encode(), true, - Determinism::Deterministic, + Determinism::Enforced, ) .result .unwrap(); diff --git a/frame/contracts/src/wasm/mod.rs b/frame/contracts/src/wasm/mod.rs index cfda9c376..5e4ffcfe0 100644 --- a/frame/contracts/src/wasm/mod.rs +++ b/frame/contracts/src/wasm/mod.rs @@ -127,7 +127,7 @@ pub enum Determinism { /// allowed. /// /// Dispatchables always use this mode in order to make on-chain execution deterministic. - Deterministic, + Enforced, /// Allow calling or uploading an indeterministic code. /// /// This is only possible when calling into `pallet-contracts` directly via @@ -136,7 +136,7 @@ pub enum Determinism { /// # Note /// /// **Never** use this mode for on-chain execution. - AllowIndeterminism, + Relaxed, } impl ExportedFunction { @@ -225,7 +225,7 @@ impl PrefabWasmModule { let engine = Engine::new(&config); let module = Module::new(&engine, code)?; let mut store = Store::new(&engine, host_state); - let mut linker = Linker::new(); + let mut linker = Linker::new(&engine); E::define( &mut store, &mut linker, @@ -329,7 +329,7 @@ impl Executable for PrefabWasmModule { log::debug!(target: "runtime::contracts", "failed to instantiate code: {}", msg); Error::::CodeRejected })?; - store.state_mut().set_memory(memory); + store.data_mut().set_memory(memory); let exported_func = instance .get_export(&store, function.identifier()) @@ -346,7 +346,7 @@ impl Executable for PrefabWasmModule { let result = exported_func.call(&mut store, &[], &mut []); - store.into_state().to_execution_result(result) + store.into_data().to_execution_result(result) } fn code_hash(&self) -> &CodeHash { @@ -358,7 +358,7 @@ impl Executable for PrefabWasmModule { } fn is_deterministic(&self) -> bool { - matches!(self.determinism, Determinism::Deterministic) + matches!(self.determinism, Determinism::Enforced) } } @@ -653,7 +653,7 @@ mod tests { wasm, &schedule, ALICE, - Determinism::Deterministic, + Determinism::Enforced, TryInstantiate::Instantiate, ) .map_err(|err| err.0)? @@ -3058,12 +3058,8 @@ mod tests { let schedule = crate::Schedule::::default(); #[cfg(not(feature = "runtime-benchmarks"))] assert_err!(execute(CODE_RANDOM, vec![], MockExt::default()), >::CodeRejected); - self::prepare::reinstrument::( - &wasm, - &schedule, - Determinism::Deterministic, - ) - .unwrap(); + self::prepare::reinstrument::(&wasm, &schedule, Determinism::Enforced) + .unwrap(); } /// This test check that an unstable interface cannot be deployed. In case of runtime diff --git a/frame/contracts/src/wasm/prepare.rs b/frame/contracts/src/wasm/prepare.rs index 6a2eb6a76..f0065d77a 100644 --- a/frame/contracts/src/wasm/prepare.rs +++ b/frame/contracts/src/wasm/prepare.rs @@ -155,52 +155,6 @@ impl<'a, T: Config> ContractModule<'a, T> { Ok(()) } - /// Ensures that no floating point types are in use. - fn ensure_no_floating_types(&self) -> Result<(), &'static str> { - if let Some(global_section) = self.module.global_section() { - for global in global_section.entries() { - match global.global_type().content_type() { - ValueType::F32 | ValueType::F64 => - return Err("use of floating point type in globals is forbidden"), - _ => {}, - } - } - } - - if let Some(code_section) = self.module.code_section() { - for func_body in code_section.bodies() { - for local in func_body.locals() { - match local.value_type() { - ValueType::F32 | ValueType::F64 => - return Err("use of floating point type in locals is forbidden"), - _ => {}, - } - } - } - } - - if let Some(type_section) = self.module.type_section() { - for wasm_type in type_section.types() { - match wasm_type { - Type::Function(func_type) => { - let return_type = func_type.results().get(0); - for value_type in func_type.params().iter().chain(return_type) { - match value_type { - ValueType::F32 | ValueType::F64 => - return Err( - "use of floating point type in function types is forbidden", - ), - _ => {}, - } - } - }, - } - } - } - - Ok(()) - } - /// Ensure that no function exists that has more parameters than allowed. fn ensure_parameter_limit(&self, limit: u32) -> Result<(), &'static str> { let type_section = if let Some(type_section) = self.module.type_section() { @@ -411,10 +365,9 @@ where memory64: false, extended_const: false, component_model: false, - // This is not our only defense: We check for float types later in the preparation - // process. Additionally, all instructions explicitly need to have weights assigned + // This is not our only defense: All instructions explicitly need to have weights assigned // or the deployment will fail. We have none assigned for float instructions. - deterministic_only: matches!(determinism, Determinism::Deterministic), + floats: matches!(determinism, Determinism::Relaxed), mutable_global: false, saturating_float_to_int: false, sign_extension: false, @@ -422,6 +375,7 @@ where multi_value: false, reference_types: false, simd: false, + memory_control: false, }) .validate_all(original_code) .map_err(|err| { @@ -439,10 +393,6 @@ where contract_module.ensure_parameter_limit(schedule.limits.parameters)?; contract_module.ensure_br_table_size_limit(schedule.limits.br_table_size)?; - if matches!(determinism, Determinism::Deterministic) { - contract_module.ensure_no_floating_types()?; - } - // We disallow importing `gas` function here since it is treated as implementation detail. let disallowed_imports = [b"gas".as_ref()]; let memory_limits = @@ -608,7 +558,7 @@ pub mod benchmarking { deposit: Default::default(), refcount: 0, }), - determinism: Determinism::Deterministic, + determinism: Determinism::Enforced, }) } } @@ -682,7 +632,7 @@ mod tests { wasm, &schedule, ALICE, - Determinism::Deterministic, + Determinism::Enforced, TryInstantiate::Instantiate, ); assert_matches::assert_matches!(r.map_err(|(_, msg)| msg), $($expected)*); @@ -704,7 +654,7 @@ mod tests { ) (func (export "deploy")) )"#, - Err("gas instrumentation failed") + Err("validation of new code failed") ); mod functions { @@ -1214,7 +1164,7 @@ mod tests { (func (export "deploy")) ) "#, - Err("use of floating point type in globals is forbidden") + Err("validation of new code failed") ); prepare_test!( @@ -1226,7 +1176,7 @@ mod tests { (func (export "deploy")) ) "#, - Err("use of floating point type in locals is forbidden") + Err("validation of new code failed") ); prepare_test!( @@ -1238,7 +1188,7 @@ mod tests { (func (export "deploy")) ) "#, - Err("use of floating point type in function types is forbidden") + Err("validation of new code failed") ); prepare_test!( @@ -1250,7 +1200,7 @@ mod tests { (func (export "deploy")) ) "#, - Err("use of floating point type in function types is forbidden") + Err("validation of new code failed") ); } } diff --git a/frame/contracts/src/wasm/runtime.rs b/frame/contracts/src/wasm/runtime.rs index a8a19e6f9..43a029ead 100644 --- a/frame/contracts/src/wasm/runtime.rs +++ b/frame/contracts/src/wasm/runtime.rs @@ -482,11 +482,7 @@ impl<'a, E: Ext + 'a> Runtime<'a, E> { Err(wasmi::Error::Trap(trap)) => { // If we encoded a reason then it is some abort generated by a host function. // Otherwise the trap came from the contract. - let reason: TrapReason = *trap - .into_host() - .ok_or(Error::::ContractTrapped)? - .downcast() - .expect("`TrapReason` is the only type we use to encode host errors; qed"); + let reason: TrapReason = trap.downcast().ok_or(Error::::ContractTrapped)?; match reason { Return(ReturnData { flags, data }) => { let flags = diff --git a/frame/contracts/src/weights.rs b/frame/contracts/src/weights.rs index b16f11eb7..4333f26c5 100644 --- a/frame/contracts/src/weights.rs +++ b/frame/contracts/src/weights.rs @@ -18,7 +18,7 @@ //! Autogenerated weights for pallet_contracts //! //! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev -//! DATE: 2023-03-14, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! DATE: 2023-03-17, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` //! WORST CASE MAP SIZE: `1000000` //! HOSTNAME: `bm3`, CPU: `Intel(R) Core(TM) i7-7700K CPU @ 4.20GHz` //! EXECUTION: Some(Wasm), WASM-EXECUTION: Compiled, CHAIN: Some("dev"), DB CACHE: 1024 @@ -177,8 +177,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `109` // Estimated: `1594` - // Minimum execution time: 2_736_000 picoseconds. - Weight::from_parts(2_931_000, 1594) + // Minimum execution time: 2_567_000 picoseconds. + Weight::from_parts(2_711_000, 1594) .saturating_add(T::DbWeight::get().reads(1_u64)) } /// Storage: Skipped Metadata (r:0 w:0) @@ -186,12 +186,12 @@ impl WeightInfo for SubstrateWeight { /// The range of component `k` is `[0, 1024]`. fn on_initialize_per_trie_key(k: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `481 + k * (69 ±0)` - // Estimated: `471 + k * (70 ±0)` - // Minimum execution time: 10_759_000 picoseconds. - Weight::from_parts(6_965_683, 471) - // Standard Error: 1_019 - .saturating_add(Weight::from_parts(961_947, 0).saturating_mul(k.into())) + // Measured: `450 + k * (69 ±0)` + // Estimated: `440 + k * (70 ±0)` + // Minimum execution time: 11_048_000 picoseconds. + Weight::from_parts(7_029_322, 440) + // Standard Error: 1_158 + .saturating_add(Weight::from_parts(975_010, 0).saturating_mul(k.into())) .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(T::DbWeight::get().reads((1_u64).saturating_mul(k.into()))) .saturating_add(T::DbWeight::get().writes(1_u64)) @@ -203,12 +203,12 @@ impl WeightInfo for SubstrateWeight { /// The range of component `q` is `[0, 128]`. fn on_initialize_per_queue_item(q: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `281 + q * (33 ±0)` - // Estimated: `1753 + q * (33 ±0)` - // Minimum execution time: 2_633_000 picoseconds. - Weight::from_parts(10_668_837, 1753) - // Standard Error: 3_538 - .saturating_add(Weight::from_parts(1_277_168, 0).saturating_mul(q.into())) + // Measured: `250 + q * (33 ±0)` + // Estimated: `1725 + q * (33 ±0)` + // Minimum execution time: 2_645_000 picoseconds. + Weight::from_parts(10_865_477, 1725) + // Standard Error: 3_383 + .saturating_add(Weight::from_parts(1_295_351, 0).saturating_mul(q.into())) .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) .saturating_add(Weight::from_parts(0, 33).saturating_mul(q.into())) @@ -220,12 +220,12 @@ impl WeightInfo for SubstrateWeight { /// The range of component `c` is `[0, 61717]`. fn reinstrument(c: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `270 + c * (1 ±0)` - // Estimated: `4015 + c * (2 ±0)` - // Minimum execution time: 30_264_000 picoseconds. - Weight::from_parts(27_106_554, 4015) - // Standard Error: 53 - .saturating_add(Weight::from_parts(49_705, 0).saturating_mul(c.into())) + // Measured: `238 + c * (1 ±0)` + // Estimated: `3951 + c * (2 ±0)` + // Minimum execution time: 30_817_000 picoseconds. + Weight::from_parts(29_762_622, 3951) + // Standard Error: 62 + .saturating_add(Weight::from_parts(54_714, 0).saturating_mul(c.into())) .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) .saturating_add(Weight::from_parts(0, 2).saturating_mul(c.into())) @@ -243,12 +243,12 @@ impl WeightInfo for SubstrateWeight { /// The range of component `c` is `[0, 125952]`. fn call_with_code_per_byte(c: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `803` - // Estimated: `21880 + c * (5 ±0)` - // Minimum execution time: 309_249_000 picoseconds. - Weight::from_parts(323_353_590, 21880) - // Standard Error: 25 - .saturating_add(Weight::from_parts(31_359, 0).saturating_mul(c.into())) + // Measured: `707` + // Estimated: `21400 + c * (5 ±0)` + // Minimum execution time: 245_493_000 picoseconds. + Weight::from_parts(253_703_384, 21400) + // Standard Error: 27 + .saturating_add(Weight::from_parts(38_078, 0).saturating_mul(c.into())) .saturating_add(T::DbWeight::get().reads(6_u64)) .saturating_add(T::DbWeight::get().writes(4_u64)) .saturating_add(Weight::from_parts(0, 5).saturating_mul(c.into())) @@ -276,14 +276,14 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `270` // Estimated: `26207` - // Minimum execution time: 3_136_707_000 picoseconds. - Weight::from_parts(564_055_378, 26207) - // Standard Error: 286 - .saturating_add(Weight::from_parts(93_308, 0).saturating_mul(c.into())) - // Standard Error: 16 - .saturating_add(Weight::from_parts(1_165, 0).saturating_mul(i.into())) - // Standard Error: 16 - .saturating_add(Weight::from_parts(1_435, 0).saturating_mul(s.into())) + // Minimum execution time: 3_079_430_000 picoseconds. + Weight::from_parts(555_022_483, 26207) + // Standard Error: 290 + .saturating_add(Weight::from_parts(107_411, 0).saturating_mul(c.into())) + // Standard Error: 17 + .saturating_add(Weight::from_parts(1_123, 0).saturating_mul(i.into())) + // Standard Error: 17 + .saturating_add(Weight::from_parts(1_434, 0).saturating_mul(s.into())) .saturating_add(T::DbWeight::get().reads(9_u64)) .saturating_add(T::DbWeight::get().writes(10_u64)) } @@ -305,14 +305,14 @@ impl WeightInfo for SubstrateWeight { /// The range of component `s` is `[0, 1048576]`. fn instantiate(i: u32, s: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `546` - // Estimated: `28969` - // Minimum execution time: 1_631_314_000 picoseconds. - Weight::from_parts(236_693_159, 28969) + // Measured: `482` + // Estimated: `28521` + // Minimum execution time: 1_600_733_000 picoseconds. + Weight::from_parts(228_681_962, 28521) // Standard Error: 8 - .saturating_add(Weight::from_parts(1_434, 0).saturating_mul(i.into())) + .saturating_add(Weight::from_parts(1_445, 0).saturating_mul(i.into())) // Standard Error: 8 - .saturating_add(Weight::from_parts(1_445, 0).saturating_mul(s.into())) + .saturating_add(Weight::from_parts(1_456, 0).saturating_mul(s.into())) .saturating_add(T::DbWeight::get().reads(9_u64)) .saturating_add(T::DbWeight::get().writes(7_u64)) } @@ -328,10 +328,10 @@ impl WeightInfo for SubstrateWeight { /// Proof Skipped: System EventTopics (max_values: None, max_size: None, mode: Measured) fn call() -> Weight { // Proof Size summary in bytes: - // Measured: `855` - // Estimated: `22095` - // Minimum execution time: 167_139_000 picoseconds. - Weight::from_parts(168_034_000, 22095) + // Measured: `759` + // Estimated: `21615` + // Minimum execution time: 172_310_000 picoseconds. + Weight::from_parts(176_655_000, 21615) .saturating_add(T::DbWeight::get().reads(6_u64)) .saturating_add(T::DbWeight::get().writes(4_u64)) } @@ -348,10 +348,10 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `109` // Estimated: `7366` - // Minimum execution time: 306_518_000 picoseconds. - Weight::from_parts(325_652_407, 7366) - // Standard Error: 134 - .saturating_add(Weight::from_parts(93_502, 0).saturating_mul(c.into())) + // Minimum execution time: 246_364_000 picoseconds. + Weight::from_parts(262_887_234, 7366) + // Standard Error: 90 + .saturating_add(Weight::from_parts(107_926, 0).saturating_mul(c.into())) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().writes(4_u64)) } @@ -365,10 +365,10 @@ impl WeightInfo for SubstrateWeight { /// Proof: Contracts PristineCode (max_values: None, max_size: Some(125988), added: 128463, mode: Measured) fn remove_code() -> Weight { // Proof Size summary in bytes: - // Measured: `287` - // Estimated: `8078` - // Minimum execution time: 29_084_000 picoseconds. - Weight::from_parts(29_350_000, 8078) + // Measured: `255` + // Estimated: `7950` + // Minimum execution time: 29_226_000 picoseconds. + Weight::from_parts(29_542_000, 7950) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().writes(4_u64)) } @@ -380,10 +380,10 @@ impl WeightInfo for SubstrateWeight { /// Proof Skipped: System EventTopics (max_values: None, max_size: None, mode: Measured) fn set_code() -> Weight { // Proof Size summary in bytes: - // Measured: `666` - // Estimated: `19818` - // Minimum execution time: 32_996_000 picoseconds. - Weight::from_parts(33_365_000, 19818) + // Measured: `570` + // Estimated: `19530` + // Minimum execution time: 33_921_000 picoseconds. + Weight::from_parts(35_056_000, 19530) .saturating_add(T::DbWeight::get().reads(6_u64)) .saturating_add(T::DbWeight::get().writes(6_u64)) } @@ -400,12 +400,12 @@ impl WeightInfo for SubstrateWeight { /// The range of component `r` is `[0, 1600]`. fn seal_caller(r: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `877 + r * (6 ±0)` - // Estimated: `22210 + r * (30 ±0)` - // Minimum execution time: 298_315_000 picoseconds. - Weight::from_parts(304_612_433, 22210) - // Standard Error: 904 - .saturating_add(Weight::from_parts(285_473, 0).saturating_mul(r.into())) + // Measured: `781 + r * (6 ±0)` + // Estimated: `21730 + r * (30 ±0)` + // Minimum execution time: 232_118_000 picoseconds. + Weight::from_parts(233_883_823, 21730) + // Standard Error: 642 + .saturating_add(Weight::from_parts(340_543, 0).saturating_mul(r.into())) .saturating_add(T::DbWeight::get().reads(6_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) .saturating_add(Weight::from_parts(0, 30).saturating_mul(r.into())) @@ -423,16 +423,16 @@ impl WeightInfo for SubstrateWeight { /// The range of component `r` is `[0, 1600]`. fn seal_is_contract(r: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `935 + r * (272 ±0)` - // Estimated: `22315 + r * (3835 ±0)` - // Minimum execution time: 299_169_000 picoseconds. - Weight::from_parts(138_157_704, 22315) - // Standard Error: 6_115 - .saturating_add(Weight::from_parts(3_244_482, 0).saturating_mul(r.into())) + // Measured: `839 + r * (240 ±0)` + // Estimated: `21835 + r * (3675 ±0)` + // Minimum execution time: 231_712_000 picoseconds. + Weight::from_parts(74_297_097, 21835) + // Standard Error: 6_099 + .saturating_add(Weight::from_parts(3_322_192, 0).saturating_mul(r.into())) .saturating_add(T::DbWeight::get().reads(6_u64)) .saturating_add(T::DbWeight::get().reads((1_u64).saturating_mul(r.into()))) .saturating_add(T::DbWeight::get().writes(3_u64)) - .saturating_add(Weight::from_parts(0, 3835).saturating_mul(r.into())) + .saturating_add(Weight::from_parts(0, 3675).saturating_mul(r.into())) } /// Storage: System Account (r:1 w:0) /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: Measured) @@ -447,16 +447,16 @@ impl WeightInfo for SubstrateWeight { /// The range of component `r` is `[0, 1600]`. fn seal_code_hash(r: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `927 + r * (276 ±0)` - // Estimated: `22335 + r * (3855 ±0)` - // Minimum execution time: 299_262_000 picoseconds. - Weight::from_parts(157_105_627, 22335) - // Standard Error: 5_634 - .saturating_add(Weight::from_parts(3_995_666, 0).saturating_mul(r.into())) + // Measured: `831 + r * (244 ±0)` + // Estimated: `21855 + r * (3695 ±0)` + // Minimum execution time: 233_122_000 picoseconds. + Weight::from_parts(74_369_473, 21855) + // Standard Error: 6_027 + .saturating_add(Weight::from_parts(4_177_187, 0).saturating_mul(r.into())) .saturating_add(T::DbWeight::get().reads(6_u64)) .saturating_add(T::DbWeight::get().reads((1_u64).saturating_mul(r.into()))) .saturating_add(T::DbWeight::get().writes(3_u64)) - .saturating_add(Weight::from_parts(0, 3855).saturating_mul(r.into())) + .saturating_add(Weight::from_parts(0, 3695).saturating_mul(r.into())) } /// Storage: System Account (r:1 w:0) /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: Measured) @@ -471,12 +471,12 @@ impl WeightInfo for SubstrateWeight { /// The range of component `r` is `[0, 1600]`. fn seal_own_code_hash(r: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `884 + r * (6 ±0)` - // Estimated: `22250 + r * (30 ±0)` - // Minimum execution time: 298_259_000 picoseconds. - Weight::from_parts(303_498_245, 22250) - // Standard Error: 874 - .saturating_add(Weight::from_parts(352_839, 0).saturating_mul(r.into())) + // Measured: `788 + r * (6 ±0)` + // Estimated: `21770 + r * (30 ±0)` + // Minimum execution time: 232_822_000 picoseconds. + Weight::from_parts(232_507_418, 21770) + // Standard Error: 961 + .saturating_add(Weight::from_parts(419_331, 0).saturating_mul(r.into())) .saturating_add(T::DbWeight::get().reads(6_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) .saturating_add(Weight::from_parts(0, 30).saturating_mul(r.into())) @@ -494,12 +494,12 @@ impl WeightInfo for SubstrateWeight { /// The range of component `r` is `[0, 1600]`. fn seal_caller_is_origin(r: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `874 + r * (3 ±0)` - // Estimated: `22215 + r * (15 ±0)` - // Minimum execution time: 295_689_000 picoseconds. - Weight::from_parts(314_004_541, 22215) - // Standard Error: 1_803 - .saturating_add(Weight::from_parts(131_102, 0).saturating_mul(r.into())) + // Measured: `778 + r * (3 ±0)` + // Estimated: `21735 + r * (15 ±0)` + // Minimum execution time: 229_719_000 picoseconds. + Weight::from_parts(235_257_789, 21735) + // Standard Error: 451 + .saturating_add(Weight::from_parts(161_101, 0).saturating_mul(r.into())) .saturating_add(T::DbWeight::get().reads(6_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) .saturating_add(Weight::from_parts(0, 15).saturating_mul(r.into())) @@ -517,12 +517,12 @@ impl WeightInfo for SubstrateWeight { /// The range of component `r` is `[0, 1600]`. fn seal_address(r: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `878 + r * (6 ±0)` - // Estimated: `22220 + r * (30 ±0)` - // Minimum execution time: 297_579_000 picoseconds. - Weight::from_parts(299_326_920, 22220) - // Standard Error: 990 - .saturating_add(Weight::from_parts(284_789, 0).saturating_mul(r.into())) + // Measured: `782 + r * (6 ±0)` + // Estimated: `21740 + r * (30 ±0)` + // Minimum execution time: 231_517_000 picoseconds. + Weight::from_parts(236_185_452, 21740) + // Standard Error: 651 + .saturating_add(Weight::from_parts(334_659, 0).saturating_mul(r.into())) .saturating_add(T::DbWeight::get().reads(6_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) .saturating_add(Weight::from_parts(0, 30).saturating_mul(r.into())) @@ -540,12 +540,12 @@ impl WeightInfo for SubstrateWeight { /// The range of component `r` is `[0, 1600]`. fn seal_gas_left(r: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `879 + r * (6 ±0)` - // Estimated: `22205 + r * (30 ±0)` - // Minimum execution time: 297_651_000 picoseconds. - Weight::from_parts(304_465_467, 22205) - // Standard Error: 736 - .saturating_add(Weight::from_parts(272_149, 0).saturating_mul(r.into())) + // Measured: `783 + r * (6 ±0)` + // Estimated: `21725 + r * (30 ±0)` + // Minimum execution time: 232_855_000 picoseconds. + Weight::from_parts(239_986_328, 21725) + // Standard Error: 4_075 + .saturating_add(Weight::from_parts(345_726, 0).saturating_mul(r.into())) .saturating_add(T::DbWeight::get().reads(6_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) .saturating_add(Weight::from_parts(0, 30).saturating_mul(r.into())) @@ -563,12 +563,12 @@ impl WeightInfo for SubstrateWeight { /// The range of component `r` is `[0, 1600]`. fn seal_balance(r: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `1050 + r * (6 ±0)` - // Estimated: `25258 + r * (30 ±0)` - // Minimum execution time: 296_957_000 picoseconds. - Weight::from_parts(308_680_068, 25258) - // Standard Error: 1_377 - .saturating_add(Weight::from_parts(1_419_294, 0).saturating_mul(r.into())) + // Measured: `922 + r * (6 ±0)` + // Estimated: `24633 + r * (30 ±0)` + // Minimum execution time: 231_685_000 picoseconds. + Weight::from_parts(245_514_261, 24633) + // Standard Error: 1_635 + .saturating_add(Weight::from_parts(1_523_359, 0).saturating_mul(r.into())) .saturating_add(T::DbWeight::get().reads(7_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) .saturating_add(Weight::from_parts(0, 30).saturating_mul(r.into())) @@ -586,12 +586,12 @@ impl WeightInfo for SubstrateWeight { /// The range of component `r` is `[0, 1600]`. fn seal_value_transferred(r: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `888 + r * (6 ±0)` - // Estimated: `22305 + r * (30 ±0)` - // Minimum execution time: 297_735_000 picoseconds. - Weight::from_parts(301_533_807, 22305) - // Standard Error: 1_119 - .saturating_add(Weight::from_parts(277_289, 0).saturating_mul(r.into())) + // Measured: `792 + r * (6 ±0)` + // Estimated: `21825 + r * (30 ±0)` + // Minimum execution time: 233_361_000 picoseconds. + Weight::from_parts(234_553_471, 21825) + // Standard Error: 1_108 + .saturating_add(Weight::from_parts(330_383, 0).saturating_mul(r.into())) .saturating_add(T::DbWeight::get().reads(6_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) .saturating_add(Weight::from_parts(0, 30).saturating_mul(r.into())) @@ -609,12 +609,12 @@ impl WeightInfo for SubstrateWeight { /// The range of component `r` is `[0, 1600]`. fn seal_minimum_balance(r: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `886 + r * (6 ±0)` - // Estimated: `22295 + r * (30 ±0)` - // Minimum execution time: 299_599_000 picoseconds. - Weight::from_parts(303_664_496, 22295) - // Standard Error: 1_009 - .saturating_add(Weight::from_parts(280_353, 0).saturating_mul(r.into())) + // Measured: `790 + r * (6 ±0)` + // Estimated: `21815 + r * (30 ±0)` + // Minimum execution time: 231_918_000 picoseconds. + Weight::from_parts(239_128_416, 21815) + // Standard Error: 1_473 + .saturating_add(Weight::from_parts(317_879, 0).saturating_mul(r.into())) .saturating_add(T::DbWeight::get().reads(6_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) .saturating_add(Weight::from_parts(0, 30).saturating_mul(r.into())) @@ -632,12 +632,12 @@ impl WeightInfo for SubstrateWeight { /// The range of component `r` is `[0, 1600]`. fn seal_block_number(r: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `883 + r * (6 ±0)` - // Estimated: `22285 + r * (30 ±0)` - // Minimum execution time: 297_641_000 picoseconds. - Weight::from_parts(302_437_979, 22285) - // Standard Error: 669 - .saturating_add(Weight::from_parts(270_403, 0).saturating_mul(r.into())) + // Measured: `787 + r * (6 ±0)` + // Estimated: `21805 + r * (30 ±0)` + // Minimum execution time: 231_936_000 picoseconds. + Weight::from_parts(232_240_207, 21805) + // Standard Error: 731 + .saturating_add(Weight::from_parts(330_713, 0).saturating_mul(r.into())) .saturating_add(T::DbWeight::get().reads(6_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) .saturating_add(Weight::from_parts(0, 30).saturating_mul(r.into())) @@ -655,12 +655,12 @@ impl WeightInfo for SubstrateWeight { /// The range of component `r` is `[0, 1600]`. fn seal_now(r: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `874 + r * (6 ±0)` - // Estimated: `22215 + r * (30 ±0)` - // Minimum execution time: 297_217_000 picoseconds. - Weight::from_parts(299_538_157, 22215) - // Standard Error: 732 - .saturating_add(Weight::from_parts(280_800, 0).saturating_mul(r.into())) + // Measured: `778 + r * (6 ±0)` + // Estimated: `21735 + r * (30 ±0)` + // Minimum execution time: 231_978_000 picoseconds. + Weight::from_parts(224_881_201, 21735) + // Standard Error: 1_296 + .saturating_add(Weight::from_parts(346_872, 0).saturating_mul(r.into())) .saturating_add(T::DbWeight::get().reads(6_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) .saturating_add(Weight::from_parts(0, 30).saturating_mul(r.into())) @@ -680,12 +680,12 @@ impl WeightInfo for SubstrateWeight { /// The range of component `r` is `[0, 1600]`. fn seal_weight_to_fee(r: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `952 + r * (10 ±0)` - // Estimated: `25022 + r * (60 ±0)` - // Minimum execution time: 297_286_000 picoseconds. - Weight::from_parts(311_436_352, 25022) - // Standard Error: 6_811 - .saturating_add(Weight::from_parts(1_291_941, 0).saturating_mul(r.into())) + // Measured: `856 + r * (10 ±0)` + // Estimated: `24446 + r * (60 ±0)` + // Minimum execution time: 231_707_000 picoseconds. + Weight::from_parts(245_939_854, 24446) + // Standard Error: 1_369 + .saturating_add(Weight::from_parts(1_380_453, 0).saturating_mul(r.into())) .saturating_add(T::DbWeight::get().reads(7_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) .saturating_add(Weight::from_parts(0, 60).saturating_mul(r.into())) @@ -703,12 +703,12 @@ impl WeightInfo for SubstrateWeight { /// The range of component `r` is `[0, 1600]`. fn seal_gas(r: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `841 + r * (4 ±0)` - // Estimated: `22035 + r * (20 ±0)` - // Minimum execution time: 154_772_000 picoseconds. - Weight::from_parts(159_224_457, 22035) - // Standard Error: 212 - .saturating_add(Weight::from_parts(102_264, 0).saturating_mul(r.into())) + // Measured: `745 + r * (4 ±0)` + // Estimated: `21555 + r * (20 ±0)` + // Minimum execution time: 157_429_000 picoseconds. + Weight::from_parts(161_047_355, 21555) + // Standard Error: 1_220 + .saturating_add(Weight::from_parts(134_497, 0).saturating_mul(r.into())) .saturating_add(T::DbWeight::get().reads(6_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) .saturating_add(Weight::from_parts(0, 20).saturating_mul(r.into())) @@ -726,12 +726,12 @@ impl WeightInfo for SubstrateWeight { /// The range of component `r` is `[0, 1600]`. fn seal_input(r: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `876 + r * (6 ±0)` - // Estimated: `22220 + r * (30 ±0)` - // Minimum execution time: 297_248_000 picoseconds. - Weight::from_parts(303_805_584, 22220) - // Standard Error: 858 - .saturating_add(Weight::from_parts(223_587, 0).saturating_mul(r.into())) + // Measured: `780 + r * (6 ±0)` + // Estimated: `21740 + r * (30 ±0)` + // Minimum execution time: 232_561_000 picoseconds. + Weight::from_parts(230_989_499, 21740) + // Standard Error: 848 + .saturating_add(Weight::from_parts(282_053, 0).saturating_mul(r.into())) .saturating_add(T::DbWeight::get().reads(6_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) .saturating_add(Weight::from_parts(0, 30).saturating_mul(r.into())) @@ -749,12 +749,12 @@ impl WeightInfo for SubstrateWeight { /// The range of component `n` is `[0, 1048576]`. fn seal_input_per_byte(n: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `880` - // Estimated: `22220` - // Minimum execution time: 298_464_000 picoseconds. - Weight::from_parts(300_996_431, 22220) - // Standard Error: 1 - .saturating_add(Weight::from_parts(600, 0).saturating_mul(n.into())) + // Measured: `784` + // Estimated: `21740` + // Minimum execution time: 237_080_000 picoseconds. + Weight::from_parts(239_380_994, 21740) + // Standard Error: 2 + .saturating_add(Weight::from_parts(592, 0).saturating_mul(n.into())) .saturating_add(T::DbWeight::get().reads(6_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) } @@ -771,12 +771,12 @@ impl WeightInfo for SubstrateWeight { /// The range of component `r` is `[0, 1]`. fn seal_return(r: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `864 + r * (45 ±0)` - // Estimated: `22140 + r * (225 ±0)` - // Minimum execution time: 294_347_000 picoseconds. - Weight::from_parts(296_793_034, 22140) - // Standard Error: 277_251 - .saturating_add(Weight::from_parts(1_711_665, 0).saturating_mul(r.into())) + // Measured: `768 + r * (45 ±0)` + // Estimated: `21660 + r * (225 ±0)` + // Minimum execution time: 228_669_000 picoseconds. + Weight::from_parts(230_916_471, 21660) + // Standard Error: 295_520 + .saturating_add(Weight::from_parts(1_420_928, 0).saturating_mul(r.into())) .saturating_add(T::DbWeight::get().reads(6_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) .saturating_add(Weight::from_parts(0, 225).saturating_mul(r.into())) @@ -794,12 +794,12 @@ impl WeightInfo for SubstrateWeight { /// The range of component `n` is `[0, 1048576]`. fn seal_return_per_byte(n: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `874` - // Estimated: `22255` - // Minimum execution time: 297_257_000 picoseconds. - Weight::from_parts(300_088_288, 22255) + // Measured: `778` + // Estimated: `21775` + // Minimum execution time: 231_626_000 picoseconds. + Weight::from_parts(232_097_413, 21775) // Standard Error: 1 - .saturating_add(Weight::from_parts(181, 0).saturating_mul(n.into())) + .saturating_add(Weight::from_parts(185, 0).saturating_mul(n.into())) .saturating_add(T::DbWeight::get().reads(6_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) } @@ -820,17 +820,17 @@ impl WeightInfo for SubstrateWeight { /// The range of component `r` is `[0, 1]`. fn seal_terminate(r: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `906 + r * (452 ±0)` - // Estimated: `26183 + r * (15994 ±0)` - // Minimum execution time: 296_510_000 picoseconds. - Weight::from_parts(299_169_757, 26183) - // Standard Error: 205_313 - .saturating_add(Weight::from_parts(78_059_642, 0).saturating_mul(r.into())) + // Measured: `810 + r * (356 ±0)` + // Estimated: `25511 + r * (15321 ±0)` + // Minimum execution time: 230_758_000 picoseconds. + Weight::from_parts(233_235_993, 25511) + // Standard Error: 196_168 + .saturating_add(Weight::from_parts(81_177_506, 0).saturating_mul(r.into())) .saturating_add(T::DbWeight::get().reads(6_u64)) .saturating_add(T::DbWeight::get().reads((6_u64).saturating_mul(r.into()))) .saturating_add(T::DbWeight::get().writes(3_u64)) .saturating_add(T::DbWeight::get().writes((7_u64).saturating_mul(r.into()))) - .saturating_add(Weight::from_parts(0, 15994).saturating_mul(r.into())) + .saturating_add(Weight::from_parts(0, 15321).saturating_mul(r.into())) } /// Storage: System Account (r:1 w:0) /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: Measured) @@ -847,12 +847,12 @@ impl WeightInfo for SubstrateWeight { /// The range of component `r` is `[0, 1600]`. fn seal_random(r: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `921 + r * (10 ±0)` - // Estimated: `24859 + r * (60 ±0)` - // Minimum execution time: 299_299_000 picoseconds. - Weight::from_parts(314_487_015, 24859) - // Standard Error: 1_552 - .saturating_add(Weight::from_parts(1_753_960, 0).saturating_mul(r.into())) + // Measured: `825 + r * (10 ±0)` + // Estimated: `24283 + r * (60 ±0)` + // Minimum execution time: 232_457_000 picoseconds. + Weight::from_parts(240_943_161, 24283) + // Standard Error: 3_085 + .saturating_add(Weight::from_parts(1_837_168, 0).saturating_mul(r.into())) .saturating_add(T::DbWeight::get().reads(7_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) .saturating_add(Weight::from_parts(0, 60).saturating_mul(r.into())) @@ -870,12 +870,12 @@ impl WeightInfo for SubstrateWeight { /// The range of component `r` is `[0, 1600]`. fn seal_deposit_event(r: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `874 + r * (10 ±0)` - // Estimated: `22215 + r * (50 ±0)` - // Minimum execution time: 296_188_000 picoseconds. - Weight::from_parts(305_901_539, 22215) - // Standard Error: 2_782 - .saturating_add(Weight::from_parts(3_456_054, 0).saturating_mul(r.into())) + // Measured: `778 + r * (10 ±0)` + // Estimated: `21735 + r * (50 ±0)` + // Minimum execution time: 229_788_000 picoseconds. + Weight::from_parts(233_520_858, 21735) + // Standard Error: 9_561 + .saturating_add(Weight::from_parts(3_596_294, 0).saturating_mul(r.into())) .saturating_add(T::DbWeight::get().reads(6_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) .saturating_add(Weight::from_parts(0, 50).saturating_mul(r.into())) @@ -894,14 +894,14 @@ impl WeightInfo for SubstrateWeight { /// The range of component `n` is `[0, 16384]`. fn seal_deposit_event_per_topic_and_byte(t: u32, n: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `893 + t * (32 ±0)` - // Estimated: `22320 + t * (2640 ±0)` - // Minimum execution time: 313_860_000 picoseconds. - Weight::from_parts(312_473_092, 22320) - // Standard Error: 250_852 - .saturating_add(Weight::from_parts(2_258_502, 0).saturating_mul(t.into())) - // Standard Error: 70 - .saturating_add(Weight::from_parts(312, 0).saturating_mul(n.into())) + // Measured: `797 + t * (32 ±0)` + // Estimated: `21840 + t * (2640 ±0)` + // Minimum execution time: 248_294_000 picoseconds. + Weight::from_parts(239_709_759, 21840) + // Standard Error: 66_903 + .saturating_add(Weight::from_parts(2_880_732, 0).saturating_mul(t.into())) + // Standard Error: 18 + .saturating_add(Weight::from_parts(725, 0).saturating_mul(n.into())) .saturating_add(T::DbWeight::get().reads(6_u64)) .saturating_add(T::DbWeight::get().reads((1_u64).saturating_mul(t.into()))) .saturating_add(T::DbWeight::get().writes(3_u64)) @@ -921,12 +921,12 @@ impl WeightInfo for SubstrateWeight { /// The range of component `r` is `[0, 1600]`. fn seal_debug_message(r: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `873 + r * (7 ±0)` - // Estimated: `22205 + r * (35 ±0)` - // Minimum execution time: 162_043_000 picoseconds. - Weight::from_parts(166_132_332, 22205) - // Standard Error: 716 - .saturating_add(Weight::from_parts(184_981, 0).saturating_mul(r.into())) + // Measured: `777 + r * (7 ±0)` + // Estimated: `21725 + r * (35 ±0)` + // Minimum execution time: 170_601_000 picoseconds. + Weight::from_parts(165_819_714, 21725) + // Standard Error: 452 + .saturating_add(Weight::from_parts(240_362, 0).saturating_mul(r.into())) .saturating_add(T::DbWeight::get().reads(6_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) .saturating_add(Weight::from_parts(0, 35).saturating_mul(r.into())) @@ -944,12 +944,12 @@ impl WeightInfo for SubstrateWeight { /// The range of component `i` is `[0, 1048576]`. fn seal_debug_message_per_byte(i: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `125824` - // Estimated: `270073` - // Minimum execution time: 414_433_000 picoseconds. - Weight::from_parts(417_483_627, 270073) - // Standard Error: 1 - .saturating_add(Weight::from_parts(748, 0).saturating_mul(i.into())) + // Measured: `125728` + // Estimated: `269977` + // Minimum execution time: 349_543_000 picoseconds. + Weight::from_parts(352_945_102, 269977) + // Standard Error: 2 + .saturating_add(Weight::from_parts(749, 0).saturating_mul(i.into())) .saturating_add(T::DbWeight::get().reads(6_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) } @@ -958,12 +958,12 @@ impl WeightInfo for SubstrateWeight { /// The range of component `r` is `[0, 800]`. fn seal_set_storage(r: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `941 + r * (292 ±0)` - // Estimated: `939 + r * (293 ±0)` - // Minimum execution time: 299_500_000 picoseconds. - Weight::from_parts(194_466_413, 939) - // Standard Error: 9_986 - .saturating_add(Weight::from_parts(6_010_112, 0).saturating_mul(r.into())) + // Measured: `845 + r * (292 ±0)` + // Estimated: `843 + r * (293 ±0)` + // Minimum execution time: 238_766_000 picoseconds. + Weight::from_parts(130_994_282, 843) + // Standard Error: 10_413 + .saturating_add(Weight::from_parts(6_134_087, 0).saturating_mul(r.into())) .saturating_add(T::DbWeight::get().reads(6_u64)) .saturating_add(T::DbWeight::get().reads((1_u64).saturating_mul(r.into()))) .saturating_add(T::DbWeight::get().writes(3_u64)) @@ -975,12 +975,12 @@ impl WeightInfo for SubstrateWeight { /// The range of component `n` is `[0, 16384]`. fn seal_set_storage_per_new_byte(n: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `1432` - // Estimated: `1405` - // Minimum execution time: 314_171_000 picoseconds. - Weight::from_parts(335_595_397, 1405) - // Standard Error: 67 - .saturating_add(Weight::from_parts(90, 0).saturating_mul(n.into())) + // Measured: `1304` + // Estimated: `1280` + // Minimum execution time: 248_137_000 picoseconds. + Weight::from_parts(265_535_917, 1280) + // Standard Error: 28 + .saturating_add(Weight::from_parts(387, 0).saturating_mul(n.into())) .saturating_add(T::DbWeight::get().reads(8_u64)) .saturating_add(T::DbWeight::get().writes(6_u64)) } @@ -989,12 +989,12 @@ impl WeightInfo for SubstrateWeight { /// The range of component `n` is `[0, 16384]`. fn seal_set_storage_per_old_byte(n: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `1295 + n * (1 ±0)` - // Estimated: `1292 + n * (1 ±0)` - // Minimum execution time: 313_479_000 picoseconds. - Weight::from_parts(317_435_100, 1292) - // Standard Error: 41 - .saturating_add(Weight::from_parts(106, 0).saturating_mul(n.into())) + // Measured: `1167 + n * (1 ±0)` + // Estimated: `1167 + n * (1 ±0)` + // Minimum execution time: 246_752_000 picoseconds. + Weight::from_parts(250_315_210, 1167) + // Standard Error: 42 + .saturating_add(Weight::from_parts(135, 0).saturating_mul(n.into())) .saturating_add(T::DbWeight::get().reads(7_u64)) .saturating_add(T::DbWeight::get().writes(4_u64)) .saturating_add(Weight::from_parts(0, 1).saturating_mul(n.into())) @@ -1004,12 +1004,12 @@ impl WeightInfo for SubstrateWeight { /// The range of component `r` is `[0, 800]`. fn seal_clear_storage(r: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `937 + r * (288 ±0)` - // Estimated: `941 + r * (289 ±0)` - // Minimum execution time: 297_831_000 picoseconds. - Weight::from_parts(196_983_778, 941) - // Standard Error: 9_899 - .saturating_add(Weight::from_parts(5_904_642, 0).saturating_mul(r.into())) + // Measured: `841 + r * (288 ±0)` + // Estimated: `845 + r * (289 ±0)` + // Minimum execution time: 232_873_000 picoseconds. + Weight::from_parts(130_360_558, 845) + // Standard Error: 10_026 + .saturating_add(Weight::from_parts(6_023_862, 0).saturating_mul(r.into())) .saturating_add(T::DbWeight::get().reads(6_u64)) .saturating_add(T::DbWeight::get().reads((1_u64).saturating_mul(r.into()))) .saturating_add(T::DbWeight::get().writes(3_u64)) @@ -1021,10 +1021,12 @@ impl WeightInfo for SubstrateWeight { /// The range of component `n` is `[0, 16384]`. fn seal_clear_storage_per_byte(n: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `1291 + n * (1 ±0)` - // Estimated: `1288 + n * (1 ±0)` - // Minimum execution time: 320_156_000 picoseconds. - Weight::from_parts(327_504_368, 1288) + // Measured: `1163 + n * (1 ±0)` + // Estimated: `1163 + n * (1 ±0)` + // Minimum execution time: 247_079_000 picoseconds. + Weight::from_parts(248_922_003, 1163) + // Standard Error: 22 + .saturating_add(Weight::from_parts(201, 0).saturating_mul(n.into())) .saturating_add(T::DbWeight::get().reads(7_u64)) .saturating_add(T::DbWeight::get().writes(4_u64)) .saturating_add(Weight::from_parts(0, 1).saturating_mul(n.into())) @@ -1034,12 +1036,12 @@ impl WeightInfo for SubstrateWeight { /// The range of component `r` is `[0, 800]`. fn seal_get_storage(r: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `931 + r * (296 ±0)` - // Estimated: `936 + r * (297 ±0)` - // Minimum execution time: 305_849_000 picoseconds. - Weight::from_parts(219_649_351, 936) - // Standard Error: 9_157 - .saturating_add(Weight::from_parts(4_846_108, 0).saturating_mul(r.into())) + // Measured: `835 + r * (296 ±0)` + // Estimated: `840 + r * (297 ±0)` + // Minimum execution time: 232_696_000 picoseconds. + Weight::from_parts(144_147_151, 840) + // Standard Error: 8_846 + .saturating_add(Weight::from_parts(4_931_414, 0).saturating_mul(r.into())) .saturating_add(T::DbWeight::get().reads(6_u64)) .saturating_add(T::DbWeight::get().reads((1_u64).saturating_mul(r.into()))) .saturating_add(T::DbWeight::get().writes(3_u64)) @@ -1050,12 +1052,12 @@ impl WeightInfo for SubstrateWeight { /// The range of component `n` is `[0, 16384]`. fn seal_get_storage_per_byte(n: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `1307 + n * (1 ±0)` - // Estimated: `1304 + n * (1 ±0)` - // Minimum execution time: 312_106_000 picoseconds. - Weight::from_parts(315_905_779, 1304) - // Standard Error: 44 - .saturating_add(Weight::from_parts(674, 0).saturating_mul(n.into())) + // Measured: `1179 + n * (1 ±0)` + // Estimated: `1179 + n * (1 ±0)` + // Minimum execution time: 247_795_000 picoseconds. + Weight::from_parts(252_944_118, 1179) + // Standard Error: 45 + .saturating_add(Weight::from_parts(480, 0).saturating_mul(n.into())) .saturating_add(T::DbWeight::get().reads(7_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) .saturating_add(Weight::from_parts(0, 1).saturating_mul(n.into())) @@ -1065,12 +1067,12 @@ impl WeightInfo for SubstrateWeight { /// The range of component `r` is `[0, 800]`. fn seal_contains_storage(r: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `952 + r * (288 ±0)` - // Estimated: `953 + r * (289 ±0)` - // Minimum execution time: 299_372_000 picoseconds. - Weight::from_parts(211_293_493, 953) - // Standard Error: 8_509 - .saturating_add(Weight::from_parts(4_688_993, 0).saturating_mul(r.into())) + // Measured: `856 + r * (288 ±0)` + // Estimated: `857 + r * (289 ±0)` + // Minimum execution time: 232_166_000 picoseconds. + Weight::from_parts(148_503_428, 857) + // Standard Error: 8_704 + .saturating_add(Weight::from_parts(4_766_055, 0).saturating_mul(r.into())) .saturating_add(T::DbWeight::get().reads(6_u64)) .saturating_add(T::DbWeight::get().reads((1_u64).saturating_mul(r.into()))) .saturating_add(T::DbWeight::get().writes(3_u64)) @@ -1081,12 +1083,12 @@ impl WeightInfo for SubstrateWeight { /// The range of component `n` is `[0, 16384]`. fn seal_contains_storage_per_byte(n: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `1294 + n * (1 ±0)` - // Estimated: `1291 + n * (1 ±0)` - // Minimum execution time: 311_605_000 picoseconds. - Weight::from_parts(315_473_850, 1291) - // Standard Error: 37 - .saturating_add(Weight::from_parts(3, 0).saturating_mul(n.into())) + // Measured: `1166 + n * (1 ±0)` + // Estimated: `1166 + n * (1 ±0)` + // Minimum execution time: 246_128_000 picoseconds. + Weight::from_parts(248_323_705, 1166) + // Standard Error: 21 + .saturating_add(Weight::from_parts(92, 0).saturating_mul(n.into())) .saturating_add(T::DbWeight::get().reads(7_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) .saturating_add(Weight::from_parts(0, 1).saturating_mul(n.into())) @@ -1096,12 +1098,12 @@ impl WeightInfo for SubstrateWeight { /// The range of component `r` is `[0, 800]`. fn seal_take_storage(r: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `925 + r * (296 ±0)` - // Estimated: `932 + r * (297 ±0)` - // Minimum execution time: 298_231_000 picoseconds. - Weight::from_parts(200_178_698, 932) - // Standard Error: 10_452 - .saturating_add(Weight::from_parts(6_107_653, 0).saturating_mul(r.into())) + // Measured: `829 + r * (296 ±0)` + // Estimated: `836 + r * (297 ±0)` + // Minimum execution time: 232_976_000 picoseconds. + Weight::from_parts(132_373_848, 836) + // Standard Error: 9_855 + .saturating_add(Weight::from_parts(6_181_821, 0).saturating_mul(r.into())) .saturating_add(T::DbWeight::get().reads(6_u64)) .saturating_add(T::DbWeight::get().reads((1_u64).saturating_mul(r.into()))) .saturating_add(T::DbWeight::get().writes(3_u64)) @@ -1113,12 +1115,12 @@ impl WeightInfo for SubstrateWeight { /// The range of component `n` is `[0, 16384]`. fn seal_take_storage_per_byte(n: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `1308 + n * (1 ±0)` - // Estimated: `1305 + n * (1 ±0)` - // Minimum execution time: 314_970_000 picoseconds. - Weight::from_parts(318_135_821, 1305) - // Standard Error: 26 - .saturating_add(Weight::from_parts(630, 0).saturating_mul(n.into())) + // Measured: `1180 + n * (1 ±0)` + // Estimated: `1180 + n * (1 ±0)` + // Minimum execution time: 247_907_000 picoseconds. + Weight::from_parts(251_984_133, 1180) + // Standard Error: 41 + .saturating_add(Weight::from_parts(639, 0).saturating_mul(n.into())) .saturating_add(T::DbWeight::get().reads(7_u64)) .saturating_add(T::DbWeight::get().writes(4_u64)) .saturating_add(Weight::from_parts(0, 1).saturating_mul(n.into())) @@ -1136,12 +1138,12 @@ impl WeightInfo for SubstrateWeight { /// The range of component `r` is `[0, 1600]`. fn seal_transfer(r: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `1501 + r * (45 ±0)` - // Estimated: `27383 + r * (2700 ±0)` - // Minimum execution time: 299_629_000 picoseconds. - Weight::from_parts(150_915_187, 27383) - // Standard Error: 25_604 - .saturating_add(Weight::from_parts(20_859_844, 0).saturating_mul(r.into())) + // Measured: `1373 + r * (45 ±0)` + // Estimated: `26753 + r * (2700 ±0)` + // Minimum execution time: 232_614_000 picoseconds. + Weight::from_parts(127_024_367, 26753) + // Standard Error: 54_521 + .saturating_add(Weight::from_parts(20_975_575, 0).saturating_mul(r.into())) .saturating_add(T::DbWeight::get().reads(7_u64)) .saturating_add(T::DbWeight::get().reads((1_u64).saturating_mul(r.into()))) .saturating_add(T::DbWeight::get().writes(4_u64)) @@ -1161,17 +1163,17 @@ impl WeightInfo for SubstrateWeight { /// The range of component `r` is `[0, 1600]`. fn seal_call(r: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `1670 + r * (288 ±0)` - // Estimated: `27813 + r * (6391 ±0)` - // Minimum execution time: 299_578_000 picoseconds. - Weight::from_parts(300_036_000, 27813) - // Standard Error: 102_709 - .saturating_add(Weight::from_parts(283_767_316, 0).saturating_mul(r.into())) + // Measured: `1512 + r * (256 ±0)` + // Estimated: `27163 + r * (6231 ±0)` + // Minimum execution time: 233_446_000 picoseconds. + Weight::from_parts(234_338_000, 27163) + // Standard Error: 122_754 + .saturating_add(Weight::from_parts(216_709_600, 0).saturating_mul(r.into())) .saturating_add(T::DbWeight::get().reads(7_u64)) .saturating_add(T::DbWeight::get().reads((2_u64).saturating_mul(r.into()))) .saturating_add(T::DbWeight::get().writes(3_u64)) .saturating_add(T::DbWeight::get().writes((2_u64).saturating_mul(r.into()))) - .saturating_add(Weight::from_parts(0, 6391).saturating_mul(r.into())) + .saturating_add(Weight::from_parts(0, 6231).saturating_mul(r.into())) } /// Storage: System Account (r:1 w:0) /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: Measured) @@ -1186,17 +1188,17 @@ impl WeightInfo for SubstrateWeight { /// The range of component `r` is `[0, 1600]`. fn seal_delegate_call(r: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `0 + r * (935 ±0)` - // Estimated: `22235 + r * (8322 ±10)` - // Minimum execution time: 298_943_000 picoseconds. - Weight::from_parts(299_619_000, 22235) - // Standard Error: 117_493 - .saturating_add(Weight::from_parts(280_555_517, 0).saturating_mul(r.into())) + // Measured: `0 + r * (902 ±0)` + // Estimated: `21755 + r * (8167 ±7)` + // Minimum execution time: 232_717_000 picoseconds. + Weight::from_parts(233_405_000, 21755) + // Standard Error: 98_607 + .saturating_add(Weight::from_parts(212_329_100, 0).saturating_mul(r.into())) .saturating_add(T::DbWeight::get().reads(6_u64)) .saturating_add(T::DbWeight::get().reads((2_u64).saturating_mul(r.into()))) .saturating_add(T::DbWeight::get().writes(3_u64)) .saturating_add(T::DbWeight::get().writes((1_u64).saturating_mul(r.into()))) - .saturating_add(Weight::from_parts(0, 8322).saturating_mul(r.into())) + .saturating_add(Weight::from_parts(0, 8167).saturating_mul(r.into())) } /// Storage: System Account (r:3 w:2) /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: Measured) @@ -1212,19 +1214,19 @@ impl WeightInfo for SubstrateWeight { /// The range of component `c` is `[0, 1048576]`. fn seal_call_per_transfer_clone_byte(t: u32, c: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `1314 + t * (268 ±0)` - // Estimated: `31815 + t * (6290 ±0)` - // Minimum execution time: 453_013_000 picoseconds. - Weight::from_parts(442_536_283, 31815) - // Standard Error: 1_339_541 - .saturating_add(Weight::from_parts(17_062_445, 0).saturating_mul(t.into())) - // Standard Error: 1 - .saturating_add(Weight::from_parts(604, 0).saturating_mul(c.into())) + // Measured: `1154 + t * (204 ±0)` + // Estimated: `31015 + t * (5970 ±0)` + // Minimum execution time: 392_113_000 picoseconds. + Weight::from_parts(376_522_702, 31015) + // Standard Error: 1_388_011 + .saturating_add(Weight::from_parts(19_125_093, 0).saturating_mul(t.into())) + // Standard Error: 2 + .saturating_add(Weight::from_parts(609, 0).saturating_mul(c.into())) .saturating_add(T::DbWeight::get().reads(9_u64)) .saturating_add(T::DbWeight::get().reads((2_u64).saturating_mul(t.into()))) .saturating_add(T::DbWeight::get().writes(5_u64)) .saturating_add(T::DbWeight::get().writes((2_u64).saturating_mul(t.into()))) - .saturating_add(Weight::from_parts(0, 6290).saturating_mul(t.into())) + .saturating_add(Weight::from_parts(0, 5970).saturating_mul(t.into())) } /// Storage: System Account (r:3202 w:3202) /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: Measured) @@ -1243,17 +1245,17 @@ impl WeightInfo for SubstrateWeight { /// The range of component `r` is `[0, 1600]`. fn seal_instantiate(r: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `1988 + r * (319 ±0)` - // Estimated: `34745 + r * (17090 ±0)` - // Minimum execution time: 300_123_000 picoseconds. - Weight::from_parts(300_406_000, 34745) - // Standard Error: 420_997 - .saturating_add(Weight::from_parts(382_704_025, 0).saturating_mul(r.into())) + // Measured: `1859 + r * (253 ±0)` + // Estimated: `33859 + r * (16628 ±0)` + // Minimum execution time: 234_438_000 picoseconds. + Weight::from_parts(234_751_000, 33859) + // Standard Error: 450_507 + .saturating_add(Weight::from_parts(313_121_404, 0).saturating_mul(r.into())) .saturating_add(T::DbWeight::get().reads(8_u64)) .saturating_add(T::DbWeight::get().reads((6_u64).saturating_mul(r.into()))) .saturating_add(T::DbWeight::get().writes(6_u64)) .saturating_add(T::DbWeight::get().writes((5_u64).saturating_mul(r.into()))) - .saturating_add(Weight::from_parts(0, 17090).saturating_mul(r.into())) + .saturating_add(Weight::from_parts(0, 16628).saturating_mul(r.into())) } /// Storage: System Account (r:4 w:4) /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: Measured) @@ -1274,21 +1276,21 @@ impl WeightInfo for SubstrateWeight { /// The range of component `s` is `[0, 983040]`. fn seal_instantiate_per_transfer_input_salt_byte(t: u32, i: u32, s: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `1231 + t * (219 ±0)` - // Estimated: `43797 + t * (3812 ±2)` - // Minimum execution time: 1_636_322_000 picoseconds. - Weight::from_parts(360_859_331, 43797) - // Standard Error: 4_816_923 - .saturating_add(Weight::from_parts(109_179_023, 0).saturating_mul(t.into())) + // Measured: `1071 + t * (187 ±0)` + // Estimated: `42684 + t * (3588 ±2)` + // Minimum execution time: 1_570_178_000 picoseconds. + Weight::from_parts(284_062_841, 42684) + // Standard Error: 4_396_597 + .saturating_add(Weight::from_parts(106_424_960, 0).saturating_mul(t.into())) // Standard Error: 7 - .saturating_add(Weight::from_parts(1_180, 0).saturating_mul(i.into())) + .saturating_add(Weight::from_parts(1_183, 0).saturating_mul(i.into())) // Standard Error: 7 - .saturating_add(Weight::from_parts(1_344, 0).saturating_mul(s.into())) + .saturating_add(Weight::from_parts(1_354, 0).saturating_mul(s.into())) .saturating_add(T::DbWeight::get().reads(13_u64)) .saturating_add(T::DbWeight::get().reads((1_u64).saturating_mul(t.into()))) .saturating_add(T::DbWeight::get().writes(10_u64)) .saturating_add(T::DbWeight::get().writes((1_u64).saturating_mul(t.into()))) - .saturating_add(Weight::from_parts(0, 3812).saturating_mul(t.into())) + .saturating_add(Weight::from_parts(0, 3588).saturating_mul(t.into())) } /// Storage: System Account (r:1 w:0) /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: Measured) @@ -1303,12 +1305,12 @@ impl WeightInfo for SubstrateWeight { /// The range of component `r` is `[0, 1600]`. fn seal_hash_sha2_256(r: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `873 + r * (8 ±0)` - // Estimated: `22190 + r * (40 ±0)` - // Minimum execution time: 297_521_000 picoseconds. - Weight::from_parts(303_523_260, 22190) - // Standard Error: 1_162 - .saturating_add(Weight::from_parts(542_201, 0).saturating_mul(r.into())) + // Measured: `777 + r * (8 ±0)` + // Estimated: `21710 + r * (40 ±0)` + // Minimum execution time: 230_557_000 picoseconds. + Weight::from_parts(233_276_698, 21710) + // Standard Error: 1_026 + .saturating_add(Weight::from_parts(587_723, 0).saturating_mul(r.into())) .saturating_add(T::DbWeight::get().reads(6_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) .saturating_add(Weight::from_parts(0, 40).saturating_mul(r.into())) @@ -1326,12 +1328,12 @@ impl WeightInfo for SubstrateWeight { /// The range of component `n` is `[0, 1048576]`. fn seal_hash_sha2_256_per_byte(n: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `881` - // Estimated: `22225` - // Minimum execution time: 299_877_000 picoseconds. - Weight::from_parts(293_538_014, 22225) - // Standard Error: 2 - .saturating_add(Weight::from_parts(3_967, 0).saturating_mul(n.into())) + // Measured: `785` + // Estimated: `21745` + // Minimum execution time: 234_151_000 picoseconds. + Weight::from_parts(240_199_365, 21745) + // Standard Error: 3 + .saturating_add(Weight::from_parts(3_941, 0).saturating_mul(n.into())) .saturating_add(T::DbWeight::get().reads(6_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) } @@ -1348,12 +1350,12 @@ impl WeightInfo for SubstrateWeight { /// The range of component `r` is `[0, 1600]`. fn seal_hash_keccak_256(r: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `875 + r * (8 ±0)` - // Estimated: `22205 + r * (40 ±0)` - // Minimum execution time: 297_672_000 picoseconds. - Weight::from_parts(299_933_312, 22205) - // Standard Error: 1_138 - .saturating_add(Weight::from_parts(713_189, 0).saturating_mul(r.into())) + // Measured: `779 + r * (8 ±0)` + // Estimated: `21725 + r * (40 ±0)` + // Minimum execution time: 229_931_000 picoseconds. + Weight::from_parts(234_961_690, 21725) + // Standard Error: 1_073 + .saturating_add(Weight::from_parts(758_259, 0).saturating_mul(r.into())) .saturating_add(T::DbWeight::get().reads(6_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) .saturating_add(Weight::from_parts(0, 40).saturating_mul(r.into())) @@ -1371,12 +1373,12 @@ impl WeightInfo for SubstrateWeight { /// The range of component `n` is `[0, 1048576]`. fn seal_hash_keccak_256_per_byte(n: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `883` - // Estimated: `22245` - // Minimum execution time: 299_048_000 picoseconds. - Weight::from_parts(293_055_982, 22245) + // Measured: `787` + // Estimated: `21765` + // Minimum execution time: 232_448_000 picoseconds. + Weight::from_parts(225_974_008, 21765) // Standard Error: 2 - .saturating_add(Weight::from_parts(3_179, 0).saturating_mul(n.into())) + .saturating_add(Weight::from_parts(3_174, 0).saturating_mul(n.into())) .saturating_add(T::DbWeight::get().reads(6_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) } @@ -1393,12 +1395,12 @@ impl WeightInfo for SubstrateWeight { /// The range of component `r` is `[0, 1600]`. fn seal_hash_blake2_256(r: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `875 + r * (8 ±0)` - // Estimated: `22220 + r * (40 ±0)` - // Minimum execution time: 301_991_000 picoseconds. - Weight::from_parts(300_027_441, 22220) - // Standard Error: 981 - .saturating_add(Weight::from_parts(391_319, 0).saturating_mul(r.into())) + // Measured: `779 + r * (8 ±0)` + // Estimated: `21740 + r * (40 ±0)` + // Minimum execution time: 229_251_000 picoseconds. + Weight::from_parts(234_182_627, 21740) + // Standard Error: 836 + .saturating_add(Weight::from_parts(424_375, 0).saturating_mul(r.into())) .saturating_add(T::DbWeight::get().reads(6_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) .saturating_add(Weight::from_parts(0, 40).saturating_mul(r.into())) @@ -1416,12 +1418,12 @@ impl WeightInfo for SubstrateWeight { /// The range of component `n` is `[0, 1048576]`. fn seal_hash_blake2_256_per_byte(n: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `883` - // Estimated: `22265` - // Minimum execution time: 296_522_000 picoseconds. - Weight::from_parts(296_121_638, 22265) + // Measured: `787` + // Estimated: `21785` + // Minimum execution time: 230_896_000 picoseconds. + Weight::from_parts(224_913_556, 21785) // Standard Error: 2 - .saturating_add(Weight::from_parts(916, 0).saturating_mul(n.into())) + .saturating_add(Weight::from_parts(919, 0).saturating_mul(n.into())) .saturating_add(T::DbWeight::get().reads(6_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) } @@ -1438,12 +1440,12 @@ impl WeightInfo for SubstrateWeight { /// The range of component `r` is `[0, 1600]`. fn seal_hash_blake2_128(r: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `875 + r * (8 ±0)` - // Estimated: `22225 + r * (40 ±0)` - // Minimum execution time: 296_228_000 picoseconds. - Weight::from_parts(301_472_299, 22225) - // Standard Error: 875 - .saturating_add(Weight::from_parts(381_027, 0).saturating_mul(r.into())) + // Measured: `779 + r * (8 ±0)` + // Estimated: `21745 + r * (40 ±0)` + // Minimum execution time: 230_314_000 picoseconds. + Weight::from_parts(233_426_327, 21745) + // Standard Error: 926 + .saturating_add(Weight::from_parts(425_341, 0).saturating_mul(r.into())) .saturating_add(T::DbWeight::get().reads(6_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) .saturating_add(Weight::from_parts(0, 40).saturating_mul(r.into())) @@ -1461,12 +1463,12 @@ impl WeightInfo for SubstrateWeight { /// The range of component `n` is `[0, 1048576]`. fn seal_hash_blake2_128_per_byte(n: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `883` - // Estimated: `22235` - // Minimum execution time: 296_644_000 picoseconds. - Weight::from_parts(289_879_744, 22235) + // Measured: `787` + // Estimated: `21755` + // Minimum execution time: 230_424_000 picoseconds. + Weight::from_parts(231_136_218, 21755) // Standard Error: 2 - .saturating_add(Weight::from_parts(925, 0).saturating_mul(n.into())) + .saturating_add(Weight::from_parts(917, 0).saturating_mul(n.into())) .saturating_add(T::DbWeight::get().reads(6_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) } @@ -1483,12 +1485,12 @@ impl WeightInfo for SubstrateWeight { /// The range of component `r` is `[0, 1600]`. fn seal_ecdsa_recover(r: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `800 + r * (78 ±0)` - // Estimated: `21845 + r * (390 ±0)` - // Minimum execution time: 297_804_000 picoseconds. - Weight::from_parts(471_994_534, 21845) - // Standard Error: 9_479 - .saturating_add(Weight::from_parts(36_886_028, 0).saturating_mul(r.into())) + // Measured: `704 + r * (78 ±0)` + // Estimated: `21370 + r * (390 ±0)` + // Minimum execution time: 232_862_000 picoseconds. + Weight::from_parts(379_152_870, 21370) + // Standard Error: 11_607 + .saturating_add(Weight::from_parts(36_955_410, 0).saturating_mul(r.into())) .saturating_add(T::DbWeight::get().reads(6_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) .saturating_add(Weight::from_parts(0, 390).saturating_mul(r.into())) @@ -1506,12 +1508,12 @@ impl WeightInfo for SubstrateWeight { /// The range of component `r` is `[0, 1600]`. fn seal_ecdsa_to_eth_address(r: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `645 + r * (44 ±0)` - // Estimated: `21110 + r * (220 ±0)` - // Minimum execution time: 298_623_000 picoseconds. - Weight::from_parts(322_192_102, 21110) - // Standard Error: 3_207 - .saturating_add(Weight::from_parts(9_243_653, 0).saturating_mul(r.into())) + // Measured: `549 + r * (44 ±0)` + // Estimated: `20630 + r * (220 ±0)` + // Minimum execution time: 232_668_000 picoseconds. + Weight::from_parts(253_308_468, 20630) + // Standard Error: 3_022 + .saturating_add(Weight::from_parts(9_249_849, 0).saturating_mul(r.into())) .saturating_add(T::DbWeight::get().reads(6_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) .saturating_add(Weight::from_parts(0, 220).saturating_mul(r.into())) @@ -1531,17 +1533,17 @@ impl WeightInfo for SubstrateWeight { /// The range of component `r` is `[0, 1600]`. fn seal_set_code_hash(r: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `0 + r * (1030 ±0)` - // Estimated: `30592 + r * (11919 ±7)` - // Minimum execution time: 298_574_000 picoseconds. - Weight::from_parts(299_383_000, 30592) - // Standard Error: 44_061 - .saturating_add(Weight::from_parts(21_625_366, 0).saturating_mul(r.into())) + // Measured: `0 + r * (964 ±0)` + // Estimated: `29920 + r * (11544 ±7)` + // Minimum execution time: 233_910_000 picoseconds. + Weight::from_parts(234_607_000, 29920) + // Standard Error: 46_118 + .saturating_add(Weight::from_parts(21_927_957, 0).saturating_mul(r.into())) .saturating_add(T::DbWeight::get().reads(6_u64)) .saturating_add(T::DbWeight::get().reads((3_u64).saturating_mul(r.into()))) .saturating_add(T::DbWeight::get().writes(3_u64)) .saturating_add(T::DbWeight::get().writes((2_u64).saturating_mul(r.into()))) - .saturating_add(Weight::from_parts(0, 11919).saturating_mul(r.into())) + .saturating_add(Weight::from_parts(0, 11544).saturating_mul(r.into())) } /// Storage: System Account (r:1 w:0) /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: Measured) @@ -1556,12 +1558,12 @@ impl WeightInfo for SubstrateWeight { /// The range of component `r` is `[0, 1600]`. fn seal_reentrance_count(r: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `869 + r * (3 ±0)` - // Estimated: `22215 + r * (15 ±0)` - // Minimum execution time: 297_014_000 picoseconds. - Weight::from_parts(301_226_615, 22215) - // Standard Error: 439 - .saturating_add(Weight::from_parts(143_017, 0).saturating_mul(r.into())) + // Measured: `773 + r * (3 ±0)` + // Estimated: `21735 + r * (15 ±0)` + // Minimum execution time: 231_823_000 picoseconds. + Weight::from_parts(237_996_137, 21735) + // Standard Error: 598 + .saturating_add(Weight::from_parts(163_106, 0).saturating_mul(r.into())) .saturating_add(T::DbWeight::get().reads(6_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) .saturating_add(Weight::from_parts(0, 15).saturating_mul(r.into())) @@ -1579,12 +1581,12 @@ impl WeightInfo for SubstrateWeight { /// The range of component `r` is `[0, 1600]`. fn seal_account_reentrance_count(r: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `2072 + r * (39 ±0)` - // Estimated: `27645 + r * (200 ±0)` - // Minimum execution time: 299_074_000 picoseconds. - Weight::from_parts(336_979_016, 27645) - // Standard Error: 1_163 - .saturating_add(Weight::from_parts(227_998, 0).saturating_mul(r.into())) + // Measured: `1975 + r * (39 ±0)` + // Estimated: `27145 + r * (200 ±0)` + // Minimum execution time: 235_077_000 picoseconds. + Weight::from_parts(267_876_360, 27145) + // Standard Error: 1_147 + .saturating_add(Weight::from_parts(256_275, 0).saturating_mul(r.into())) .saturating_add(T::DbWeight::get().reads(6_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) .saturating_add(Weight::from_parts(0, 200).saturating_mul(r.into())) @@ -1604,12 +1606,12 @@ impl WeightInfo for SubstrateWeight { /// The range of component `r` is `[0, 1600]`. fn seal_instantiation_nonce(r: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `872 + r * (3 ±0)` - // Estimated: `24580 + r * (18 ±0)` - // Minimum execution time: 296_959_000 picoseconds. - Weight::from_parts(303_796_839, 24580) + // Measured: `776 + r * (3 ±0)` + // Estimated: `24004 + r * (18 ±0)` + // Minimum execution time: 231_384_000 picoseconds. + Weight::from_parts(237_086_900, 24004) // Standard Error: 534 - .saturating_add(Weight::from_parts(118_978, 0).saturating_mul(r.into())) + .saturating_add(Weight::from_parts(140_842, 0).saturating_mul(r.into())) .saturating_add(T::DbWeight::get().reads(7_u64)) .saturating_add(T::DbWeight::get().writes(4_u64)) .saturating_add(Weight::from_parts(0, 18).saturating_mul(r.into())) @@ -1619,508 +1621,510 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_020_000 picoseconds. - Weight::from_parts(1_355_107, 0) - // Standard Error: 1 - .saturating_add(Weight::from_parts(4_110, 0).saturating_mul(r.into())) + // Minimum execution time: 1_721_000 picoseconds. + Weight::from_parts(2_456_746, 0) + // Standard Error: 30 + .saturating_add(Weight::from_parts(2_788, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 5000]`. fn instr_i64load(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_224_000 picoseconds. - Weight::from_parts(1_819_284, 0) - // Standard Error: 10 - .saturating_add(Weight::from_parts(10_803, 0).saturating_mul(r.into())) + // Minimum execution time: 1_824_000 picoseconds. + Weight::from_parts(2_366_036, 0) + // Standard Error: 5 + .saturating_add(Weight::from_parts(6_446, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 5000]`. fn instr_i64store(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_216_000 picoseconds. - Weight::from_parts(1_795_011, 0) - // Standard Error: 5 - .saturating_add(Weight::from_parts(10_000, 0).saturating_mul(r.into())) + // Minimum execution time: 1_840_000 picoseconds. + Weight::from_parts(2_332_115, 0) + // Standard Error: 4 + .saturating_add(Weight::from_parts(6_012, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 5000]`. fn instr_select(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_127_000 picoseconds. - Weight::from_parts(1_491_730, 0) - // Standard Error: 4 - .saturating_add(Weight::from_parts(11_471, 0).saturating_mul(r.into())) + // Minimum execution time: 1_731_000 picoseconds. + Weight::from_parts(2_078_873, 0) + // Standard Error: 3 + .saturating_add(Weight::from_parts(7_931, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 5000]`. fn instr_if(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_044_000 picoseconds. - Weight::from_parts(2_330_852, 0) - // Standard Error: 69 - .saturating_add(Weight::from_parts(12_866, 0).saturating_mul(r.into())) + // Minimum execution time: 1_768_000 picoseconds. + Weight::from_parts(1_916_551, 0) + // Standard Error: 8 + .saturating_add(Weight::from_parts(10_597, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 5000]`. fn instr_br(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_067_000 picoseconds. - Weight::from_parts(1_399_626, 0) - // Standard Error: 5 - .saturating_add(Weight::from_parts(6_430, 0).saturating_mul(r.into())) + // Minimum execution time: 1_735_000 picoseconds. + Weight::from_parts(1_962_845, 0) + // Standard Error: 14 + .saturating_add(Weight::from_parts(4_631, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 5000]`. fn instr_br_if(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_087_000 picoseconds. - Weight::from_parts(1_463_592, 0) - // Standard Error: 12 - .saturating_add(Weight::from_parts(9_707, 0).saturating_mul(r.into())) + // Minimum execution time: 1_688_000 picoseconds. + Weight::from_parts(1_172_825, 0) + // Standard Error: 76 + .saturating_add(Weight::from_parts(8_007, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 5000]`. fn instr_br_table(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_110_000 picoseconds. - Weight::from_parts(1_058_258, 0) - // Standard Error: 22 - .saturating_add(Weight::from_parts(11_713, 0).saturating_mul(r.into())) + // Minimum execution time: 1_725_000 picoseconds. + Weight::from_parts(1_535_833, 0) + // Standard Error: 26 + .saturating_add(Weight::from_parts(9_489, 0).saturating_mul(r.into())) } /// The range of component `e` is `[1, 256]`. - fn instr_br_table_per_entry(_e: u32, ) -> Weight { + fn instr_br_table_per_entry(e: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_189_000 picoseconds. - Weight::from_parts(1_416_188, 0) + // Minimum execution time: 1_814_000 picoseconds. + Weight::from_parts(1_926_367, 0) + // Standard Error: 16 + .saturating_add(Weight::from_parts(219, 0).saturating_mul(e.into())) } /// The range of component `r` is `[0, 5000]`. fn instr_call(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 2_201_000 picoseconds. - Weight::from_parts(3_375_851, 0) - // Standard Error: 96 - .saturating_add(Weight::from_parts(22_970, 0).saturating_mul(r.into())) + // Minimum execution time: 1_770_000 picoseconds. + Weight::from_parts(2_436_918, 0) + // Standard Error: 8 + .saturating_add(Weight::from_parts(17_890, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 5000]`. fn instr_call_indirect(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_286_000 picoseconds. - Weight::from_parts(2_817_725, 0) - // Standard Error: 55 - .saturating_add(Weight::from_parts(29_437, 0).saturating_mul(r.into())) + // Minimum execution time: 1_920_000 picoseconds. + Weight::from_parts(2_729_712, 0) + // Standard Error: 39 + .saturating_add(Weight::from_parts(24_853, 0).saturating_mul(r.into())) } /// The range of component `l` is `[0, 1024]`. fn instr_call_per_local(l: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_319_000 picoseconds. - Weight::from_parts(1_636_286, 0) - // Standard Error: 31 - .saturating_add(Weight::from_parts(1_240, 0).saturating_mul(l.into())) + // Minimum execution time: 1_869_000 picoseconds. + Weight::from_parts(2_147_970, 0) + // Standard Error: 46 + .saturating_add(Weight::from_parts(1_237, 0).saturating_mul(l.into())) } /// The range of component `r` is `[0, 5000]`. fn instr_local_get(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 2_507_000 picoseconds. - Weight::from_parts(2_785_119, 0) + // Minimum execution time: 3_012_000 picoseconds. + Weight::from_parts(3_234_440, 0) // Standard Error: 1 - .saturating_add(Weight::from_parts(4_601, 0).saturating_mul(r.into())) + .saturating_add(Weight::from_parts(2_433, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 5000]`. fn instr_local_set(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 2_494_000 picoseconds. - Weight::from_parts(2_948_015, 0) - // Standard Error: 14 - .saturating_add(Weight::from_parts(4_788, 0).saturating_mul(r.into())) + // Minimum execution time: 3_037_000 picoseconds. + Weight::from_parts(3_204_600, 0) + // Standard Error: 2 + .saturating_add(Weight::from_parts(3_627, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 5000]`. fn instr_local_tee(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 2_443_000 picoseconds. - Weight::from_parts(3_065_273, 0) - // Standard Error: 15 - .saturating_add(Weight::from_parts(6_489, 0).saturating_mul(r.into())) + // Minimum execution time: 2_974_000 picoseconds. + Weight::from_parts(3_301_366, 0) + // Standard Error: 2 + .saturating_add(Weight::from_parts(3_836, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 5000]`. fn instr_global_get(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_214_000 picoseconds. - Weight::from_parts(1_634_049, 0) - // Standard Error: 3 - .saturating_add(Weight::from_parts(8_960, 0).saturating_mul(r.into())) + // Minimum execution time: 1_853_000 picoseconds. + Weight::from_parts(2_310_141, 0) + // Standard Error: 4 + .saturating_add(Weight::from_parts(8_387, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 5000]`. fn instr_global_set(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_180_000 picoseconds. - Weight::from_parts(1_555_599, 0) + // Minimum execution time: 1_828_000 picoseconds. + Weight::from_parts(2_315_323, 0) // Standard Error: 4 - .saturating_add(Weight::from_parts(9_205, 0).saturating_mul(r.into())) + .saturating_add(Weight::from_parts(8_797, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 5000]`. fn instr_memory_current(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_223_000 picoseconds. - Weight::from_parts(1_626_002, 0) - // Standard Error: 5 - .saturating_add(Weight::from_parts(8_181, 0).saturating_mul(r.into())) + // Minimum execution time: 1_800_000 picoseconds. + Weight::from_parts(2_117_252, 0) + // Standard Error: 4 + .saturating_add(Weight::from_parts(3_741, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 16]`. fn instr_memory_grow(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_148_000 picoseconds. - Weight::from_parts(307_155, 0) - // Standard Error: 138_541 - .saturating_add(Weight::from_parts(13_291_570, 0).saturating_mul(r.into())) + // Minimum execution time: 1_743_000 picoseconds. + Weight::from_parts(1_054_728, 0) + // Standard Error: 137_128 + .saturating_add(Weight::from_parts(13_179_446, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 5000]`. fn instr_i64clz(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_079_000 picoseconds. - Weight::from_parts(1_372_409, 0) - // Standard Error: 50 - .saturating_add(Weight::from_parts(6_427, 0).saturating_mul(r.into())) + // Minimum execution time: 1_735_000 picoseconds. + Weight::from_parts(2_026_568, 0) + // Standard Error: 2 + .saturating_add(Weight::from_parts(3_802, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 5000]`. fn instr_i64ctz(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_116_000 picoseconds. - Weight::from_parts(1_415_180, 0) + // Minimum execution time: 1_630_000 picoseconds. + Weight::from_parts(1_980_222, 0) // Standard Error: 2 - .saturating_add(Weight::from_parts(6_353, 0).saturating_mul(r.into())) + .saturating_add(Weight::from_parts(3_735, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 5000]`. fn instr_i64popcnt(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_058_000 picoseconds. - Weight::from_parts(1_314_206, 0) - // Standard Error: 35 - .saturating_add(Weight::from_parts(6_431, 0).saturating_mul(r.into())) + // Minimum execution time: 1_695_000 picoseconds. + Weight::from_parts(2_040_266, 0) + // Standard Error: 8 + .saturating_add(Weight::from_parts(3_718, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 5000]`. fn instr_i64eqz(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_059_000 picoseconds. - Weight::from_parts(1_455_331, 0) - // Standard Error: 2 - .saturating_add(Weight::from_parts(6_507, 0).saturating_mul(r.into())) + // Minimum execution time: 1_697_000 picoseconds. + Weight::from_parts(2_004_663, 0) + // Standard Error: 3 + .saturating_add(Weight::from_parts(3_661, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 5000]`. fn instr_i64extendsi32(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_057_000 picoseconds. - Weight::from_parts(1_421_085, 0) - // Standard Error: 2 - .saturating_add(Weight::from_parts(6_177, 0).saturating_mul(r.into())) + // Minimum execution time: 1_705_000 picoseconds. + Weight::from_parts(2_682_122, 0) + // Standard Error: 98 + .saturating_add(Weight::from_parts(3_705, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 5000]`. fn instr_i64extendui32(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_079_000 picoseconds. - Weight::from_parts(1_429_275, 0) + // Minimum execution time: 1_674_000 picoseconds. + Weight::from_parts(1_989_835, 0) // Standard Error: 2 - .saturating_add(Weight::from_parts(6_175, 0).saturating_mul(r.into())) + .saturating_add(Weight::from_parts(3_801, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 5000]`. fn instr_i32wrapi64(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_065_000 picoseconds. - Weight::from_parts(1_403_813, 0) - // Standard Error: 3 - .saturating_add(Weight::from_parts(6_192, 0).saturating_mul(r.into())) + // Minimum execution time: 1_695_000 picoseconds. + Weight::from_parts(2_083_824, 0) + // Standard Error: 17 + .saturating_add(Weight::from_parts(3_719, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 5000]`. fn instr_i64eq(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_061_000 picoseconds. - Weight::from_parts(1_421_984, 0) + // Minimum execution time: 1_678_000 picoseconds. + Weight::from_parts(1_996_335, 0) // Standard Error: 4 - .saturating_add(Weight::from_parts(9_094, 0).saturating_mul(r.into())) + .saturating_add(Weight::from_parts(5_995, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 5000]`. fn instr_i64ne(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_075_000 picoseconds. - Weight::from_parts(1_431_453, 0) - // Standard Error: 2 - .saturating_add(Weight::from_parts(9_084, 0).saturating_mul(r.into())) + // Minimum execution time: 1_697_000 picoseconds. + Weight::from_parts(2_046_566, 0) + // Standard Error: 3 + .saturating_add(Weight::from_parts(5_936, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 5000]`. fn instr_i64lts(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_100_000 picoseconds. - Weight::from_parts(1_452_384, 0) - // Standard Error: 3 - .saturating_add(Weight::from_parts(9_081, 0).saturating_mul(r.into())) + // Minimum execution time: 1_721_000 picoseconds. + Weight::from_parts(2_406_930, 0) + // Standard Error: 150 + .saturating_add(Weight::from_parts(5_992, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 5000]`. fn instr_i64ltu(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_090_000 picoseconds. - Weight::from_parts(1_466_416, 0) - // Standard Error: 17 - .saturating_add(Weight::from_parts(9_091, 0).saturating_mul(r.into())) + // Minimum execution time: 1_749_000 picoseconds. + Weight::from_parts(1_987_158, 0) + // Standard Error: 2 + .saturating_add(Weight::from_parts(5_994, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 5000]`. fn instr_i64gts(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_056_000 picoseconds. - Weight::from_parts(1_676_091, 0) - // Standard Error: 47 - .saturating_add(Weight::from_parts(9_082, 0).saturating_mul(r.into())) + // Minimum execution time: 1_706_000 picoseconds. + Weight::from_parts(2_038_138, 0) + // Standard Error: 2 + .saturating_add(Weight::from_parts(5_802, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 5000]`. fn instr_i64gtu(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_053_000 picoseconds. - Weight::from_parts(1_232_790, 0) - // Standard Error: 43 - .saturating_add(Weight::from_parts(9_329, 0).saturating_mul(r.into())) + // Minimum execution time: 1_670_000 picoseconds. + Weight::from_parts(2_555_035, 0) + // Standard Error: 74 + .saturating_add(Weight::from_parts(5_958, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 5000]`. fn instr_i64les(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_091_000 picoseconds. - Weight::from_parts(1_476_212, 0) - // Standard Error: 4 - .saturating_add(Weight::from_parts(9_074, 0).saturating_mul(r.into())) + // Minimum execution time: 1_694_000 picoseconds. + Weight::from_parts(1_991_929, 0) + // Standard Error: 27 + .saturating_add(Weight::from_parts(6_037, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 5000]`. fn instr_i64leu(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_083_000 picoseconds. - Weight::from_parts(1_484_966, 0) - // Standard Error: 3 - .saturating_add(Weight::from_parts(9_184, 0).saturating_mul(r.into())) + // Minimum execution time: 1_684_000 picoseconds. + Weight::from_parts(1_969_284, 0) + // Standard Error: 2 + .saturating_add(Weight::from_parts(6_104, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 5000]`. fn instr_i64ges(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_089_000 picoseconds. - Weight::from_parts(1_516_766, 0) - // Standard Error: 4 - .saturating_add(Weight::from_parts(9_063, 0).saturating_mul(r.into())) + // Minimum execution time: 1_697_000 picoseconds. + Weight::from_parts(2_023_608, 0) + // Standard Error: 3 + .saturating_add(Weight::from_parts(5_946, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 5000]`. fn instr_i64geu(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 996_000 picoseconds. - Weight::from_parts(1_460_638, 0) - // Standard Error: 2 - .saturating_add(Weight::from_parts(9_076, 0).saturating_mul(r.into())) + // Minimum execution time: 1_659_000 picoseconds. + Weight::from_parts(1_963_145, 0) + // Standard Error: 6 + .saturating_add(Weight::from_parts(6_014, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 5000]`. fn instr_i64add(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_095_000 picoseconds. - Weight::from_parts(1_448_412, 0) + // Minimum execution time: 1_722_000 picoseconds. + Weight::from_parts(2_016_286, 0) // Standard Error: 3 - .saturating_add(Weight::from_parts(8_977, 0).saturating_mul(r.into())) + .saturating_add(Weight::from_parts(5_889, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 5000]`. fn instr_i64sub(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_071_000 picoseconds. - Weight::from_parts(1_459_165, 0) - // Standard Error: 2 - .saturating_add(Weight::from_parts(8_852, 0).saturating_mul(r.into())) + // Minimum execution time: 1_656_000 picoseconds. + Weight::from_parts(1_966_234, 0) + // Standard Error: 17 + .saturating_add(Weight::from_parts(6_171, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 5000]`. fn instr_i64mul(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_082_000 picoseconds. - Weight::from_parts(1_450_718, 0) - // Standard Error: 3 - .saturating_add(Weight::from_parts(8_860, 0).saturating_mul(r.into())) + // Minimum execution time: 1_695_000 picoseconds. + Weight::from_parts(2_004_559, 0) + // Standard Error: 2 + .saturating_add(Weight::from_parts(5_785, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 5000]`. fn instr_i64divs(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_065_000 picoseconds. - Weight::from_parts(1_436_871, 0) - // Standard Error: 9 - .saturating_add(Weight::from_parts(15_241, 0).saturating_mul(r.into())) + // Minimum execution time: 1_681_000 picoseconds. + Weight::from_parts(2_135_505, 0) + // Standard Error: 6 + .saturating_add(Weight::from_parts(11_761, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 5000]`. fn instr_i64divu(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_065_000 picoseconds. - Weight::from_parts(1_423_806, 0) - // Standard Error: 17 - .saturating_add(Weight::from_parts(14_706, 0).saturating_mul(r.into())) + // Minimum execution time: 1_653_000 picoseconds. + Weight::from_parts(2_153_167, 0) + // Standard Error: 5 + .saturating_add(Weight::from_parts(10_523, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 5000]`. fn instr_i64rems(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_059_000 picoseconds. - Weight::from_parts(1_735_816, 0) - // Standard Error: 66 - .saturating_add(Weight::from_parts(15_230, 0).saturating_mul(r.into())) + // Minimum execution time: 1_697_000 picoseconds. + Weight::from_parts(2_570_266, 0) + // Standard Error: 50 + .saturating_add(Weight::from_parts(11_971, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 5000]`. fn instr_i64remu(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_076_000 picoseconds. - Weight::from_parts(1_451_290, 0) - // Standard Error: 6 - .saturating_add(Weight::from_parts(14_530, 0).saturating_mul(r.into())) + // Minimum execution time: 1_719_000 picoseconds. + Weight::from_parts(2_101_082, 0) + // Standard Error: 3 + .saturating_add(Weight::from_parts(10_709, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 5000]`. fn instr_i64and(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_079_000 picoseconds. - Weight::from_parts(1_457_537, 0) - // Standard Error: 3 - .saturating_add(Weight::from_parts(8_963, 0).saturating_mul(r.into())) + // Minimum execution time: 1_745_000 picoseconds. + Weight::from_parts(2_621_890, 0) + // Standard Error: 94 + .saturating_add(Weight::from_parts(5_545, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 5000]`. fn instr_i64or(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_032_000 picoseconds. - Weight::from_parts(1_475_315, 0) - // Standard Error: 4 - .saturating_add(Weight::from_parts(8_956, 0).saturating_mul(r.into())) + // Minimum execution time: 1_670_000 picoseconds. + Weight::from_parts(2_046_821, 0) + // Standard Error: 2 + .saturating_add(Weight::from_parts(5_762, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 5000]`. fn instr_i64xor(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_056_000 picoseconds. - Weight::from_parts(1_450_071, 0) + // Minimum execution time: 1_714_000 picoseconds. + Weight::from_parts(2_010_173, 0) // Standard Error: 2 - .saturating_add(Weight::from_parts(8_960, 0).saturating_mul(r.into())) + .saturating_add(Weight::from_parts(5_911, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 5000]`. fn instr_i64shl(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_116_000 picoseconds. - Weight::from_parts(1_429_705, 0) + // Minimum execution time: 1_756_000 picoseconds. + Weight::from_parts(2_044_772, 0) // Standard Error: 2 - .saturating_add(Weight::from_parts(9_027, 0).saturating_mul(r.into())) + .saturating_add(Weight::from_parts(5_857, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 5000]`. fn instr_i64shrs(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_059_000 picoseconds. - Weight::from_parts(1_429_307, 0) - // Standard Error: 4 - .saturating_add(Weight::from_parts(9_048, 0).saturating_mul(r.into())) + // Minimum execution time: 1_741_000 picoseconds. + Weight::from_parts(2_012_819, 0) + // Standard Error: 2 + .saturating_add(Weight::from_parts(6_127, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 5000]`. fn instr_i64shru(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_093_000 picoseconds. - Weight::from_parts(1_411_827, 0) - // Standard Error: 4 - .saturating_add(Weight::from_parts(9_528, 0).saturating_mul(r.into())) + // Minimum execution time: 1_693_000 picoseconds. + Weight::from_parts(2_090_981, 0) + // Standard Error: 2 + .saturating_add(Weight::from_parts(5_839, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 5000]`. fn instr_i64rotl(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_115_000 picoseconds. - Weight::from_parts(1_441_025, 0) + // Minimum execution time: 1_704_000 picoseconds. + Weight::from_parts(2_014_108, 0) // Standard Error: 4 - .saturating_add(Weight::from_parts(9_043, 0).saturating_mul(r.into())) + .saturating_add(Weight::from_parts(6_005, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 5000]`. fn instr_i64rotr(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_077_000 picoseconds. - Weight::from_parts(1_480_666, 0) - // Standard Error: 4 - .saturating_add(Weight::from_parts(9_031, 0).saturating_mul(r.into())) + // Minimum execution time: 1_715_000 picoseconds. + Weight::from_parts(2_053_720, 0) + // Standard Error: 5 + .saturating_add(Weight::from_parts(5_860, 0).saturating_mul(r.into())) } } @@ -2132,8 +2136,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `109` // Estimated: `1594` - // Minimum execution time: 2_736_000 picoseconds. - Weight::from_parts(2_931_000, 1594) + // Minimum execution time: 2_567_000 picoseconds. + Weight::from_parts(2_711_000, 1594) .saturating_add(RocksDbWeight::get().reads(1_u64)) } /// Storage: Skipped Metadata (r:0 w:0) @@ -2141,12 +2145,12 @@ impl WeightInfo for () { /// The range of component `k` is `[0, 1024]`. fn on_initialize_per_trie_key(k: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `481 + k * (69 ±0)` - // Estimated: `471 + k * (70 ±0)` - // Minimum execution time: 10_759_000 picoseconds. - Weight::from_parts(6_965_683, 471) - // Standard Error: 1_019 - .saturating_add(Weight::from_parts(961_947, 0).saturating_mul(k.into())) + // Measured: `450 + k * (69 ±0)` + // Estimated: `440 + k * (70 ±0)` + // Minimum execution time: 11_048_000 picoseconds. + Weight::from_parts(7_029_322, 440) + // Standard Error: 1_158 + .saturating_add(Weight::from_parts(975_010, 0).saturating_mul(k.into())) .saturating_add(RocksDbWeight::get().reads(1_u64)) .saturating_add(RocksDbWeight::get().reads((1_u64).saturating_mul(k.into()))) .saturating_add(RocksDbWeight::get().writes(1_u64)) @@ -2158,12 +2162,12 @@ impl WeightInfo for () { /// The range of component `q` is `[0, 128]`. fn on_initialize_per_queue_item(q: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `281 + q * (33 ±0)` - // Estimated: `1753 + q * (33 ±0)` - // Minimum execution time: 2_633_000 picoseconds. - Weight::from_parts(10_668_837, 1753) - // Standard Error: 3_538 - .saturating_add(Weight::from_parts(1_277_168, 0).saturating_mul(q.into())) + // Measured: `250 + q * (33 ±0)` + // Estimated: `1725 + q * (33 ±0)` + // Minimum execution time: 2_645_000 picoseconds. + Weight::from_parts(10_865_477, 1725) + // Standard Error: 3_383 + .saturating_add(Weight::from_parts(1_295_351, 0).saturating_mul(q.into())) .saturating_add(RocksDbWeight::get().reads(1_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) .saturating_add(Weight::from_parts(0, 33).saturating_mul(q.into())) @@ -2175,12 +2179,12 @@ impl WeightInfo for () { /// The range of component `c` is `[0, 61717]`. fn reinstrument(c: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `270 + c * (1 ±0)` - // Estimated: `4015 + c * (2 ±0)` - // Minimum execution time: 30_264_000 picoseconds. - Weight::from_parts(27_106_554, 4015) - // Standard Error: 53 - .saturating_add(Weight::from_parts(49_705, 0).saturating_mul(c.into())) + // Measured: `238 + c * (1 ±0)` + // Estimated: `3951 + c * (2 ±0)` + // Minimum execution time: 30_817_000 picoseconds. + Weight::from_parts(29_762_622, 3951) + // Standard Error: 62 + .saturating_add(Weight::from_parts(54_714, 0).saturating_mul(c.into())) .saturating_add(RocksDbWeight::get().reads(1_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) .saturating_add(Weight::from_parts(0, 2).saturating_mul(c.into())) @@ -2198,12 +2202,12 @@ impl WeightInfo for () { /// The range of component `c` is `[0, 125952]`. fn call_with_code_per_byte(c: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `803` - // Estimated: `21880 + c * (5 ±0)` - // Minimum execution time: 309_249_000 picoseconds. - Weight::from_parts(323_353_590, 21880) - // Standard Error: 25 - .saturating_add(Weight::from_parts(31_359, 0).saturating_mul(c.into())) + // Measured: `707` + // Estimated: `21400 + c * (5 ±0)` + // Minimum execution time: 245_493_000 picoseconds. + Weight::from_parts(253_703_384, 21400) + // Standard Error: 27 + .saturating_add(Weight::from_parts(38_078, 0).saturating_mul(c.into())) .saturating_add(RocksDbWeight::get().reads(6_u64)) .saturating_add(RocksDbWeight::get().writes(4_u64)) .saturating_add(Weight::from_parts(0, 5).saturating_mul(c.into())) @@ -2231,14 +2235,14 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `270` // Estimated: `26207` - // Minimum execution time: 3_136_707_000 picoseconds. - Weight::from_parts(564_055_378, 26207) - // Standard Error: 286 - .saturating_add(Weight::from_parts(93_308, 0).saturating_mul(c.into())) - // Standard Error: 16 - .saturating_add(Weight::from_parts(1_165, 0).saturating_mul(i.into())) - // Standard Error: 16 - .saturating_add(Weight::from_parts(1_435, 0).saturating_mul(s.into())) + // Minimum execution time: 3_079_430_000 picoseconds. + Weight::from_parts(555_022_483, 26207) + // Standard Error: 290 + .saturating_add(Weight::from_parts(107_411, 0).saturating_mul(c.into())) + // Standard Error: 17 + .saturating_add(Weight::from_parts(1_123, 0).saturating_mul(i.into())) + // Standard Error: 17 + .saturating_add(Weight::from_parts(1_434, 0).saturating_mul(s.into())) .saturating_add(RocksDbWeight::get().reads(9_u64)) .saturating_add(RocksDbWeight::get().writes(10_u64)) } @@ -2260,14 +2264,14 @@ impl WeightInfo for () { /// The range of component `s` is `[0, 1048576]`. fn instantiate(i: u32, s: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `546` - // Estimated: `28969` - // Minimum execution time: 1_631_314_000 picoseconds. - Weight::from_parts(236_693_159, 28969) + // Measured: `482` + // Estimated: `28521` + // Minimum execution time: 1_600_733_000 picoseconds. + Weight::from_parts(228_681_962, 28521) // Standard Error: 8 - .saturating_add(Weight::from_parts(1_434, 0).saturating_mul(i.into())) + .saturating_add(Weight::from_parts(1_445, 0).saturating_mul(i.into())) // Standard Error: 8 - .saturating_add(Weight::from_parts(1_445, 0).saturating_mul(s.into())) + .saturating_add(Weight::from_parts(1_456, 0).saturating_mul(s.into())) .saturating_add(RocksDbWeight::get().reads(9_u64)) .saturating_add(RocksDbWeight::get().writes(7_u64)) } @@ -2283,10 +2287,10 @@ impl WeightInfo for () { /// Proof Skipped: System EventTopics (max_values: None, max_size: None, mode: Measured) fn call() -> Weight { // Proof Size summary in bytes: - // Measured: `855` - // Estimated: `22095` - // Minimum execution time: 167_139_000 picoseconds. - Weight::from_parts(168_034_000, 22095) + // Measured: `759` + // Estimated: `21615` + // Minimum execution time: 172_310_000 picoseconds. + Weight::from_parts(176_655_000, 21615) .saturating_add(RocksDbWeight::get().reads(6_u64)) .saturating_add(RocksDbWeight::get().writes(4_u64)) } @@ -2303,10 +2307,10 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `109` // Estimated: `7366` - // Minimum execution time: 306_518_000 picoseconds. - Weight::from_parts(325_652_407, 7366) - // Standard Error: 134 - .saturating_add(Weight::from_parts(93_502, 0).saturating_mul(c.into())) + // Minimum execution time: 246_364_000 picoseconds. + Weight::from_parts(262_887_234, 7366) + // Standard Error: 90 + .saturating_add(Weight::from_parts(107_926, 0).saturating_mul(c.into())) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().writes(4_u64)) } @@ -2320,10 +2324,10 @@ impl WeightInfo for () { /// Proof: Contracts PristineCode (max_values: None, max_size: Some(125988), added: 128463, mode: Measured) fn remove_code() -> Weight { // Proof Size summary in bytes: - // Measured: `287` - // Estimated: `8078` - // Minimum execution time: 29_084_000 picoseconds. - Weight::from_parts(29_350_000, 8078) + // Measured: `255` + // Estimated: `7950` + // Minimum execution time: 29_226_000 picoseconds. + Weight::from_parts(29_542_000, 7950) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().writes(4_u64)) } @@ -2335,10 +2339,10 @@ impl WeightInfo for () { /// Proof Skipped: System EventTopics (max_values: None, max_size: None, mode: Measured) fn set_code() -> Weight { // Proof Size summary in bytes: - // Measured: `666` - // Estimated: `19818` - // Minimum execution time: 32_996_000 picoseconds. - Weight::from_parts(33_365_000, 19818) + // Measured: `570` + // Estimated: `19530` + // Minimum execution time: 33_921_000 picoseconds. + Weight::from_parts(35_056_000, 19530) .saturating_add(RocksDbWeight::get().reads(6_u64)) .saturating_add(RocksDbWeight::get().writes(6_u64)) } @@ -2355,12 +2359,12 @@ impl WeightInfo for () { /// The range of component `r` is `[0, 1600]`. fn seal_caller(r: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `877 + r * (6 ±0)` - // Estimated: `22210 + r * (30 ±0)` - // Minimum execution time: 298_315_000 picoseconds. - Weight::from_parts(304_612_433, 22210) - // Standard Error: 904 - .saturating_add(Weight::from_parts(285_473, 0).saturating_mul(r.into())) + // Measured: `781 + r * (6 ±0)` + // Estimated: `21730 + r * (30 ±0)` + // Minimum execution time: 232_118_000 picoseconds. + Weight::from_parts(233_883_823, 21730) + // Standard Error: 642 + .saturating_add(Weight::from_parts(340_543, 0).saturating_mul(r.into())) .saturating_add(RocksDbWeight::get().reads(6_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) .saturating_add(Weight::from_parts(0, 30).saturating_mul(r.into())) @@ -2378,16 +2382,16 @@ impl WeightInfo for () { /// The range of component `r` is `[0, 1600]`. fn seal_is_contract(r: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `935 + r * (272 ±0)` - // Estimated: `22315 + r * (3835 ±0)` - // Minimum execution time: 299_169_000 picoseconds. - Weight::from_parts(138_157_704, 22315) - // Standard Error: 6_115 - .saturating_add(Weight::from_parts(3_244_482, 0).saturating_mul(r.into())) + // Measured: `839 + r * (240 ±0)` + // Estimated: `21835 + r * (3675 ±0)` + // Minimum execution time: 231_712_000 picoseconds. + Weight::from_parts(74_297_097, 21835) + // Standard Error: 6_099 + .saturating_add(Weight::from_parts(3_322_192, 0).saturating_mul(r.into())) .saturating_add(RocksDbWeight::get().reads(6_u64)) .saturating_add(RocksDbWeight::get().reads((1_u64).saturating_mul(r.into()))) .saturating_add(RocksDbWeight::get().writes(3_u64)) - .saturating_add(Weight::from_parts(0, 3835).saturating_mul(r.into())) + .saturating_add(Weight::from_parts(0, 3675).saturating_mul(r.into())) } /// Storage: System Account (r:1 w:0) /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: Measured) @@ -2402,16 +2406,16 @@ impl WeightInfo for () { /// The range of component `r` is `[0, 1600]`. fn seal_code_hash(r: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `927 + r * (276 ±0)` - // Estimated: `22335 + r * (3855 ±0)` - // Minimum execution time: 299_262_000 picoseconds. - Weight::from_parts(157_105_627, 22335) - // Standard Error: 5_634 - .saturating_add(Weight::from_parts(3_995_666, 0).saturating_mul(r.into())) + // Measured: `831 + r * (244 ±0)` + // Estimated: `21855 + r * (3695 ±0)` + // Minimum execution time: 233_122_000 picoseconds. + Weight::from_parts(74_369_473, 21855) + // Standard Error: 6_027 + .saturating_add(Weight::from_parts(4_177_187, 0).saturating_mul(r.into())) .saturating_add(RocksDbWeight::get().reads(6_u64)) .saturating_add(RocksDbWeight::get().reads((1_u64).saturating_mul(r.into()))) .saturating_add(RocksDbWeight::get().writes(3_u64)) - .saturating_add(Weight::from_parts(0, 3855).saturating_mul(r.into())) + .saturating_add(Weight::from_parts(0, 3695).saturating_mul(r.into())) } /// Storage: System Account (r:1 w:0) /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: Measured) @@ -2426,12 +2430,12 @@ impl WeightInfo for () { /// The range of component `r` is `[0, 1600]`. fn seal_own_code_hash(r: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `884 + r * (6 ±0)` - // Estimated: `22250 + r * (30 ±0)` - // Minimum execution time: 298_259_000 picoseconds. - Weight::from_parts(303_498_245, 22250) - // Standard Error: 874 - .saturating_add(Weight::from_parts(352_839, 0).saturating_mul(r.into())) + // Measured: `788 + r * (6 ±0)` + // Estimated: `21770 + r * (30 ±0)` + // Minimum execution time: 232_822_000 picoseconds. + Weight::from_parts(232_507_418, 21770) + // Standard Error: 961 + .saturating_add(Weight::from_parts(419_331, 0).saturating_mul(r.into())) .saturating_add(RocksDbWeight::get().reads(6_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) .saturating_add(Weight::from_parts(0, 30).saturating_mul(r.into())) @@ -2449,12 +2453,12 @@ impl WeightInfo for () { /// The range of component `r` is `[0, 1600]`. fn seal_caller_is_origin(r: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `874 + r * (3 ±0)` - // Estimated: `22215 + r * (15 ±0)` - // Minimum execution time: 295_689_000 picoseconds. - Weight::from_parts(314_004_541, 22215) - // Standard Error: 1_803 - .saturating_add(Weight::from_parts(131_102, 0).saturating_mul(r.into())) + // Measured: `778 + r * (3 ±0)` + // Estimated: `21735 + r * (15 ±0)` + // Minimum execution time: 229_719_000 picoseconds. + Weight::from_parts(235_257_789, 21735) + // Standard Error: 451 + .saturating_add(Weight::from_parts(161_101, 0).saturating_mul(r.into())) .saturating_add(RocksDbWeight::get().reads(6_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) .saturating_add(Weight::from_parts(0, 15).saturating_mul(r.into())) @@ -2472,12 +2476,12 @@ impl WeightInfo for () { /// The range of component `r` is `[0, 1600]`. fn seal_address(r: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `878 + r * (6 ±0)` - // Estimated: `22220 + r * (30 ±0)` - // Minimum execution time: 297_579_000 picoseconds. - Weight::from_parts(299_326_920, 22220) - // Standard Error: 990 - .saturating_add(Weight::from_parts(284_789, 0).saturating_mul(r.into())) + // Measured: `782 + r * (6 ±0)` + // Estimated: `21740 + r * (30 ±0)` + // Minimum execution time: 231_517_000 picoseconds. + Weight::from_parts(236_185_452, 21740) + // Standard Error: 651 + .saturating_add(Weight::from_parts(334_659, 0).saturating_mul(r.into())) .saturating_add(RocksDbWeight::get().reads(6_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) .saturating_add(Weight::from_parts(0, 30).saturating_mul(r.into())) @@ -2495,12 +2499,12 @@ impl WeightInfo for () { /// The range of component `r` is `[0, 1600]`. fn seal_gas_left(r: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `879 + r * (6 ±0)` - // Estimated: `22205 + r * (30 ±0)` - // Minimum execution time: 297_651_000 picoseconds. - Weight::from_parts(304_465_467, 22205) - // Standard Error: 736 - .saturating_add(Weight::from_parts(272_149, 0).saturating_mul(r.into())) + // Measured: `783 + r * (6 ±0)` + // Estimated: `21725 + r * (30 ±0)` + // Minimum execution time: 232_855_000 picoseconds. + Weight::from_parts(239_986_328, 21725) + // Standard Error: 4_075 + .saturating_add(Weight::from_parts(345_726, 0).saturating_mul(r.into())) .saturating_add(RocksDbWeight::get().reads(6_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) .saturating_add(Weight::from_parts(0, 30).saturating_mul(r.into())) @@ -2518,12 +2522,12 @@ impl WeightInfo for () { /// The range of component `r` is `[0, 1600]`. fn seal_balance(r: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `1050 + r * (6 ±0)` - // Estimated: `25258 + r * (30 ±0)` - // Minimum execution time: 296_957_000 picoseconds. - Weight::from_parts(308_680_068, 25258) - // Standard Error: 1_377 - .saturating_add(Weight::from_parts(1_419_294, 0).saturating_mul(r.into())) + // Measured: `922 + r * (6 ±0)` + // Estimated: `24633 + r * (30 ±0)` + // Minimum execution time: 231_685_000 picoseconds. + Weight::from_parts(245_514_261, 24633) + // Standard Error: 1_635 + .saturating_add(Weight::from_parts(1_523_359, 0).saturating_mul(r.into())) .saturating_add(RocksDbWeight::get().reads(7_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) .saturating_add(Weight::from_parts(0, 30).saturating_mul(r.into())) @@ -2541,12 +2545,12 @@ impl WeightInfo for () { /// The range of component `r` is `[0, 1600]`. fn seal_value_transferred(r: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `888 + r * (6 ±0)` - // Estimated: `22305 + r * (30 ±0)` - // Minimum execution time: 297_735_000 picoseconds. - Weight::from_parts(301_533_807, 22305) - // Standard Error: 1_119 - .saturating_add(Weight::from_parts(277_289, 0).saturating_mul(r.into())) + // Measured: `792 + r * (6 ±0)` + // Estimated: `21825 + r * (30 ±0)` + // Minimum execution time: 233_361_000 picoseconds. + Weight::from_parts(234_553_471, 21825) + // Standard Error: 1_108 + .saturating_add(Weight::from_parts(330_383, 0).saturating_mul(r.into())) .saturating_add(RocksDbWeight::get().reads(6_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) .saturating_add(Weight::from_parts(0, 30).saturating_mul(r.into())) @@ -2564,12 +2568,12 @@ impl WeightInfo for () { /// The range of component `r` is `[0, 1600]`. fn seal_minimum_balance(r: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `886 + r * (6 ±0)` - // Estimated: `22295 + r * (30 ±0)` - // Minimum execution time: 299_599_000 picoseconds. - Weight::from_parts(303_664_496, 22295) - // Standard Error: 1_009 - .saturating_add(Weight::from_parts(280_353, 0).saturating_mul(r.into())) + // Measured: `790 + r * (6 ±0)` + // Estimated: `21815 + r * (30 ±0)` + // Minimum execution time: 231_918_000 picoseconds. + Weight::from_parts(239_128_416, 21815) + // Standard Error: 1_473 + .saturating_add(Weight::from_parts(317_879, 0).saturating_mul(r.into())) .saturating_add(RocksDbWeight::get().reads(6_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) .saturating_add(Weight::from_parts(0, 30).saturating_mul(r.into())) @@ -2587,12 +2591,12 @@ impl WeightInfo for () { /// The range of component `r` is `[0, 1600]`. fn seal_block_number(r: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `883 + r * (6 ±0)` - // Estimated: `22285 + r * (30 ±0)` - // Minimum execution time: 297_641_000 picoseconds. - Weight::from_parts(302_437_979, 22285) - // Standard Error: 669 - .saturating_add(Weight::from_parts(270_403, 0).saturating_mul(r.into())) + // Measured: `787 + r * (6 ±0)` + // Estimated: `21805 + r * (30 ±0)` + // Minimum execution time: 231_936_000 picoseconds. + Weight::from_parts(232_240_207, 21805) + // Standard Error: 731 + .saturating_add(Weight::from_parts(330_713, 0).saturating_mul(r.into())) .saturating_add(RocksDbWeight::get().reads(6_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) .saturating_add(Weight::from_parts(0, 30).saturating_mul(r.into())) @@ -2610,12 +2614,12 @@ impl WeightInfo for () { /// The range of component `r` is `[0, 1600]`. fn seal_now(r: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `874 + r * (6 ±0)` - // Estimated: `22215 + r * (30 ±0)` - // Minimum execution time: 297_217_000 picoseconds. - Weight::from_parts(299_538_157, 22215) - // Standard Error: 732 - .saturating_add(Weight::from_parts(280_800, 0).saturating_mul(r.into())) + // Measured: `778 + r * (6 ±0)` + // Estimated: `21735 + r * (30 ±0)` + // Minimum execution time: 231_978_000 picoseconds. + Weight::from_parts(224_881_201, 21735) + // Standard Error: 1_296 + .saturating_add(Weight::from_parts(346_872, 0).saturating_mul(r.into())) .saturating_add(RocksDbWeight::get().reads(6_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) .saturating_add(Weight::from_parts(0, 30).saturating_mul(r.into())) @@ -2635,12 +2639,12 @@ impl WeightInfo for () { /// The range of component `r` is `[0, 1600]`. fn seal_weight_to_fee(r: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `952 + r * (10 ±0)` - // Estimated: `25022 + r * (60 ±0)` - // Minimum execution time: 297_286_000 picoseconds. - Weight::from_parts(311_436_352, 25022) - // Standard Error: 6_811 - .saturating_add(Weight::from_parts(1_291_941, 0).saturating_mul(r.into())) + // Measured: `856 + r * (10 ±0)` + // Estimated: `24446 + r * (60 ±0)` + // Minimum execution time: 231_707_000 picoseconds. + Weight::from_parts(245_939_854, 24446) + // Standard Error: 1_369 + .saturating_add(Weight::from_parts(1_380_453, 0).saturating_mul(r.into())) .saturating_add(RocksDbWeight::get().reads(7_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) .saturating_add(Weight::from_parts(0, 60).saturating_mul(r.into())) @@ -2658,12 +2662,12 @@ impl WeightInfo for () { /// The range of component `r` is `[0, 1600]`. fn seal_gas(r: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `841 + r * (4 ±0)` - // Estimated: `22035 + r * (20 ±0)` - // Minimum execution time: 154_772_000 picoseconds. - Weight::from_parts(159_224_457, 22035) - // Standard Error: 212 - .saturating_add(Weight::from_parts(102_264, 0).saturating_mul(r.into())) + // Measured: `745 + r * (4 ±0)` + // Estimated: `21555 + r * (20 ±0)` + // Minimum execution time: 157_429_000 picoseconds. + Weight::from_parts(161_047_355, 21555) + // Standard Error: 1_220 + .saturating_add(Weight::from_parts(134_497, 0).saturating_mul(r.into())) .saturating_add(RocksDbWeight::get().reads(6_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) .saturating_add(Weight::from_parts(0, 20).saturating_mul(r.into())) @@ -2681,12 +2685,12 @@ impl WeightInfo for () { /// The range of component `r` is `[0, 1600]`. fn seal_input(r: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `876 + r * (6 ±0)` - // Estimated: `22220 + r * (30 ±0)` - // Minimum execution time: 297_248_000 picoseconds. - Weight::from_parts(303_805_584, 22220) - // Standard Error: 858 - .saturating_add(Weight::from_parts(223_587, 0).saturating_mul(r.into())) + // Measured: `780 + r * (6 ±0)` + // Estimated: `21740 + r * (30 ±0)` + // Minimum execution time: 232_561_000 picoseconds. + Weight::from_parts(230_989_499, 21740) + // Standard Error: 848 + .saturating_add(Weight::from_parts(282_053, 0).saturating_mul(r.into())) .saturating_add(RocksDbWeight::get().reads(6_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) .saturating_add(Weight::from_parts(0, 30).saturating_mul(r.into())) @@ -2704,12 +2708,12 @@ impl WeightInfo for () { /// The range of component `n` is `[0, 1048576]`. fn seal_input_per_byte(n: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `880` - // Estimated: `22220` - // Minimum execution time: 298_464_000 picoseconds. - Weight::from_parts(300_996_431, 22220) - // Standard Error: 1 - .saturating_add(Weight::from_parts(600, 0).saturating_mul(n.into())) + // Measured: `784` + // Estimated: `21740` + // Minimum execution time: 237_080_000 picoseconds. + Weight::from_parts(239_380_994, 21740) + // Standard Error: 2 + .saturating_add(Weight::from_parts(592, 0).saturating_mul(n.into())) .saturating_add(RocksDbWeight::get().reads(6_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) } @@ -2726,12 +2730,12 @@ impl WeightInfo for () { /// The range of component `r` is `[0, 1]`. fn seal_return(r: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `864 + r * (45 ±0)` - // Estimated: `22140 + r * (225 ±0)` - // Minimum execution time: 294_347_000 picoseconds. - Weight::from_parts(296_793_034, 22140) - // Standard Error: 277_251 - .saturating_add(Weight::from_parts(1_711_665, 0).saturating_mul(r.into())) + // Measured: `768 + r * (45 ±0)` + // Estimated: `21660 + r * (225 ±0)` + // Minimum execution time: 228_669_000 picoseconds. + Weight::from_parts(230_916_471, 21660) + // Standard Error: 295_520 + .saturating_add(Weight::from_parts(1_420_928, 0).saturating_mul(r.into())) .saturating_add(RocksDbWeight::get().reads(6_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) .saturating_add(Weight::from_parts(0, 225).saturating_mul(r.into())) @@ -2749,12 +2753,12 @@ impl WeightInfo for () { /// The range of component `n` is `[0, 1048576]`. fn seal_return_per_byte(n: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `874` - // Estimated: `22255` - // Minimum execution time: 297_257_000 picoseconds. - Weight::from_parts(300_088_288, 22255) + // Measured: `778` + // Estimated: `21775` + // Minimum execution time: 231_626_000 picoseconds. + Weight::from_parts(232_097_413, 21775) // Standard Error: 1 - .saturating_add(Weight::from_parts(181, 0).saturating_mul(n.into())) + .saturating_add(Weight::from_parts(185, 0).saturating_mul(n.into())) .saturating_add(RocksDbWeight::get().reads(6_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) } @@ -2775,17 +2779,17 @@ impl WeightInfo for () { /// The range of component `r` is `[0, 1]`. fn seal_terminate(r: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `906 + r * (452 ±0)` - // Estimated: `26183 + r * (15994 ±0)` - // Minimum execution time: 296_510_000 picoseconds. - Weight::from_parts(299_169_757, 26183) - // Standard Error: 205_313 - .saturating_add(Weight::from_parts(78_059_642, 0).saturating_mul(r.into())) + // Measured: `810 + r * (356 ±0)` + // Estimated: `25511 + r * (15321 ±0)` + // Minimum execution time: 230_758_000 picoseconds. + Weight::from_parts(233_235_993, 25511) + // Standard Error: 196_168 + .saturating_add(Weight::from_parts(81_177_506, 0).saturating_mul(r.into())) .saturating_add(RocksDbWeight::get().reads(6_u64)) .saturating_add(RocksDbWeight::get().reads((6_u64).saturating_mul(r.into()))) .saturating_add(RocksDbWeight::get().writes(3_u64)) .saturating_add(RocksDbWeight::get().writes((7_u64).saturating_mul(r.into()))) - .saturating_add(Weight::from_parts(0, 15994).saturating_mul(r.into())) + .saturating_add(Weight::from_parts(0, 15321).saturating_mul(r.into())) } /// Storage: System Account (r:1 w:0) /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: Measured) @@ -2802,12 +2806,12 @@ impl WeightInfo for () { /// The range of component `r` is `[0, 1600]`. fn seal_random(r: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `921 + r * (10 ±0)` - // Estimated: `24859 + r * (60 ±0)` - // Minimum execution time: 299_299_000 picoseconds. - Weight::from_parts(314_487_015, 24859) - // Standard Error: 1_552 - .saturating_add(Weight::from_parts(1_753_960, 0).saturating_mul(r.into())) + // Measured: `825 + r * (10 ±0)` + // Estimated: `24283 + r * (60 ±0)` + // Minimum execution time: 232_457_000 picoseconds. + Weight::from_parts(240_943_161, 24283) + // Standard Error: 3_085 + .saturating_add(Weight::from_parts(1_837_168, 0).saturating_mul(r.into())) .saturating_add(RocksDbWeight::get().reads(7_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) .saturating_add(Weight::from_parts(0, 60).saturating_mul(r.into())) @@ -2825,12 +2829,12 @@ impl WeightInfo for () { /// The range of component `r` is `[0, 1600]`. fn seal_deposit_event(r: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `874 + r * (10 ±0)` - // Estimated: `22215 + r * (50 ±0)` - // Minimum execution time: 296_188_000 picoseconds. - Weight::from_parts(305_901_539, 22215) - // Standard Error: 2_782 - .saturating_add(Weight::from_parts(3_456_054, 0).saturating_mul(r.into())) + // Measured: `778 + r * (10 ±0)` + // Estimated: `21735 + r * (50 ±0)` + // Minimum execution time: 229_788_000 picoseconds. + Weight::from_parts(233_520_858, 21735) + // Standard Error: 9_561 + .saturating_add(Weight::from_parts(3_596_294, 0).saturating_mul(r.into())) .saturating_add(RocksDbWeight::get().reads(6_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) .saturating_add(Weight::from_parts(0, 50).saturating_mul(r.into())) @@ -2849,14 +2853,14 @@ impl WeightInfo for () { /// The range of component `n` is `[0, 16384]`. fn seal_deposit_event_per_topic_and_byte(t: u32, n: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `893 + t * (32 ±0)` - // Estimated: `22320 + t * (2640 ±0)` - // Minimum execution time: 313_860_000 picoseconds. - Weight::from_parts(312_473_092, 22320) - // Standard Error: 250_852 - .saturating_add(Weight::from_parts(2_258_502, 0).saturating_mul(t.into())) - // Standard Error: 70 - .saturating_add(Weight::from_parts(312, 0).saturating_mul(n.into())) + // Measured: `797 + t * (32 ±0)` + // Estimated: `21840 + t * (2640 ±0)` + // Minimum execution time: 248_294_000 picoseconds. + Weight::from_parts(239_709_759, 21840) + // Standard Error: 66_903 + .saturating_add(Weight::from_parts(2_880_732, 0).saturating_mul(t.into())) + // Standard Error: 18 + .saturating_add(Weight::from_parts(725, 0).saturating_mul(n.into())) .saturating_add(RocksDbWeight::get().reads(6_u64)) .saturating_add(RocksDbWeight::get().reads((1_u64).saturating_mul(t.into()))) .saturating_add(RocksDbWeight::get().writes(3_u64)) @@ -2876,12 +2880,12 @@ impl WeightInfo for () { /// The range of component `r` is `[0, 1600]`. fn seal_debug_message(r: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `873 + r * (7 ±0)` - // Estimated: `22205 + r * (35 ±0)` - // Minimum execution time: 162_043_000 picoseconds. - Weight::from_parts(166_132_332, 22205) - // Standard Error: 716 - .saturating_add(Weight::from_parts(184_981, 0).saturating_mul(r.into())) + // Measured: `777 + r * (7 ±0)` + // Estimated: `21725 + r * (35 ±0)` + // Minimum execution time: 170_601_000 picoseconds. + Weight::from_parts(165_819_714, 21725) + // Standard Error: 452 + .saturating_add(Weight::from_parts(240_362, 0).saturating_mul(r.into())) .saturating_add(RocksDbWeight::get().reads(6_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) .saturating_add(Weight::from_parts(0, 35).saturating_mul(r.into())) @@ -2899,12 +2903,12 @@ impl WeightInfo for () { /// The range of component `i` is `[0, 1048576]`. fn seal_debug_message_per_byte(i: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `125824` - // Estimated: `270073` - // Minimum execution time: 414_433_000 picoseconds. - Weight::from_parts(417_483_627, 270073) - // Standard Error: 1 - .saturating_add(Weight::from_parts(748, 0).saturating_mul(i.into())) + // Measured: `125728` + // Estimated: `269977` + // Minimum execution time: 349_543_000 picoseconds. + Weight::from_parts(352_945_102, 269977) + // Standard Error: 2 + .saturating_add(Weight::from_parts(749, 0).saturating_mul(i.into())) .saturating_add(RocksDbWeight::get().reads(6_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) } @@ -2913,12 +2917,12 @@ impl WeightInfo for () { /// The range of component `r` is `[0, 800]`. fn seal_set_storage(r: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `941 + r * (292 ±0)` - // Estimated: `939 + r * (293 ±0)` - // Minimum execution time: 299_500_000 picoseconds. - Weight::from_parts(194_466_413, 939) - // Standard Error: 9_986 - .saturating_add(Weight::from_parts(6_010_112, 0).saturating_mul(r.into())) + // Measured: `845 + r * (292 ±0)` + // Estimated: `843 + r * (293 ±0)` + // Minimum execution time: 238_766_000 picoseconds. + Weight::from_parts(130_994_282, 843) + // Standard Error: 10_413 + .saturating_add(Weight::from_parts(6_134_087, 0).saturating_mul(r.into())) .saturating_add(RocksDbWeight::get().reads(6_u64)) .saturating_add(RocksDbWeight::get().reads((1_u64).saturating_mul(r.into()))) .saturating_add(RocksDbWeight::get().writes(3_u64)) @@ -2930,12 +2934,12 @@ impl WeightInfo for () { /// The range of component `n` is `[0, 16384]`. fn seal_set_storage_per_new_byte(n: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `1432` - // Estimated: `1405` - // Minimum execution time: 314_171_000 picoseconds. - Weight::from_parts(335_595_397, 1405) - // Standard Error: 67 - .saturating_add(Weight::from_parts(90, 0).saturating_mul(n.into())) + // Measured: `1304` + // Estimated: `1280` + // Minimum execution time: 248_137_000 picoseconds. + Weight::from_parts(265_535_917, 1280) + // Standard Error: 28 + .saturating_add(Weight::from_parts(387, 0).saturating_mul(n.into())) .saturating_add(RocksDbWeight::get().reads(8_u64)) .saturating_add(RocksDbWeight::get().writes(6_u64)) } @@ -2944,12 +2948,12 @@ impl WeightInfo for () { /// The range of component `n` is `[0, 16384]`. fn seal_set_storage_per_old_byte(n: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `1295 + n * (1 ±0)` - // Estimated: `1292 + n * (1 ±0)` - // Minimum execution time: 313_479_000 picoseconds. - Weight::from_parts(317_435_100, 1292) - // Standard Error: 41 - .saturating_add(Weight::from_parts(106, 0).saturating_mul(n.into())) + // Measured: `1167 + n * (1 ±0)` + // Estimated: `1167 + n * (1 ±0)` + // Minimum execution time: 246_752_000 picoseconds. + Weight::from_parts(250_315_210, 1167) + // Standard Error: 42 + .saturating_add(Weight::from_parts(135, 0).saturating_mul(n.into())) .saturating_add(RocksDbWeight::get().reads(7_u64)) .saturating_add(RocksDbWeight::get().writes(4_u64)) .saturating_add(Weight::from_parts(0, 1).saturating_mul(n.into())) @@ -2959,12 +2963,12 @@ impl WeightInfo for () { /// The range of component `r` is `[0, 800]`. fn seal_clear_storage(r: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `937 + r * (288 ±0)` - // Estimated: `941 + r * (289 ±0)` - // Minimum execution time: 297_831_000 picoseconds. - Weight::from_parts(196_983_778, 941) - // Standard Error: 9_899 - .saturating_add(Weight::from_parts(5_904_642, 0).saturating_mul(r.into())) + // Measured: `841 + r * (288 ±0)` + // Estimated: `845 + r * (289 ±0)` + // Minimum execution time: 232_873_000 picoseconds. + Weight::from_parts(130_360_558, 845) + // Standard Error: 10_026 + .saturating_add(Weight::from_parts(6_023_862, 0).saturating_mul(r.into())) .saturating_add(RocksDbWeight::get().reads(6_u64)) .saturating_add(RocksDbWeight::get().reads((1_u64).saturating_mul(r.into()))) .saturating_add(RocksDbWeight::get().writes(3_u64)) @@ -2976,10 +2980,12 @@ impl WeightInfo for () { /// The range of component `n` is `[0, 16384]`. fn seal_clear_storage_per_byte(n: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `1291 + n * (1 ±0)` - // Estimated: `1288 + n * (1 ±0)` - // Minimum execution time: 320_156_000 picoseconds. - Weight::from_parts(327_504_368, 1288) + // Measured: `1163 + n * (1 ±0)` + // Estimated: `1163 + n * (1 ±0)` + // Minimum execution time: 247_079_000 picoseconds. + Weight::from_parts(248_922_003, 1163) + // Standard Error: 22 + .saturating_add(Weight::from_parts(201, 0).saturating_mul(n.into())) .saturating_add(RocksDbWeight::get().reads(7_u64)) .saturating_add(RocksDbWeight::get().writes(4_u64)) .saturating_add(Weight::from_parts(0, 1).saturating_mul(n.into())) @@ -2989,12 +2995,12 @@ impl WeightInfo for () { /// The range of component `r` is `[0, 800]`. fn seal_get_storage(r: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `931 + r * (296 ±0)` - // Estimated: `936 + r * (297 ±0)` - // Minimum execution time: 305_849_000 picoseconds. - Weight::from_parts(219_649_351, 936) - // Standard Error: 9_157 - .saturating_add(Weight::from_parts(4_846_108, 0).saturating_mul(r.into())) + // Measured: `835 + r * (296 ±0)` + // Estimated: `840 + r * (297 ±0)` + // Minimum execution time: 232_696_000 picoseconds. + Weight::from_parts(144_147_151, 840) + // Standard Error: 8_846 + .saturating_add(Weight::from_parts(4_931_414, 0).saturating_mul(r.into())) .saturating_add(RocksDbWeight::get().reads(6_u64)) .saturating_add(RocksDbWeight::get().reads((1_u64).saturating_mul(r.into()))) .saturating_add(RocksDbWeight::get().writes(3_u64)) @@ -3005,12 +3011,12 @@ impl WeightInfo for () { /// The range of component `n` is `[0, 16384]`. fn seal_get_storage_per_byte(n: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `1307 + n * (1 ±0)` - // Estimated: `1304 + n * (1 ±0)` - // Minimum execution time: 312_106_000 picoseconds. - Weight::from_parts(315_905_779, 1304) - // Standard Error: 44 - .saturating_add(Weight::from_parts(674, 0).saturating_mul(n.into())) + // Measured: `1179 + n * (1 ±0)` + // Estimated: `1179 + n * (1 ±0)` + // Minimum execution time: 247_795_000 picoseconds. + Weight::from_parts(252_944_118, 1179) + // Standard Error: 45 + .saturating_add(Weight::from_parts(480, 0).saturating_mul(n.into())) .saturating_add(RocksDbWeight::get().reads(7_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) .saturating_add(Weight::from_parts(0, 1).saturating_mul(n.into())) @@ -3020,12 +3026,12 @@ impl WeightInfo for () { /// The range of component `r` is `[0, 800]`. fn seal_contains_storage(r: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `952 + r * (288 ±0)` - // Estimated: `953 + r * (289 ±0)` - // Minimum execution time: 299_372_000 picoseconds. - Weight::from_parts(211_293_493, 953) - // Standard Error: 8_509 - .saturating_add(Weight::from_parts(4_688_993, 0).saturating_mul(r.into())) + // Measured: `856 + r * (288 ±0)` + // Estimated: `857 + r * (289 ±0)` + // Minimum execution time: 232_166_000 picoseconds. + Weight::from_parts(148_503_428, 857) + // Standard Error: 8_704 + .saturating_add(Weight::from_parts(4_766_055, 0).saturating_mul(r.into())) .saturating_add(RocksDbWeight::get().reads(6_u64)) .saturating_add(RocksDbWeight::get().reads((1_u64).saturating_mul(r.into()))) .saturating_add(RocksDbWeight::get().writes(3_u64)) @@ -3036,12 +3042,12 @@ impl WeightInfo for () { /// The range of component `n` is `[0, 16384]`. fn seal_contains_storage_per_byte(n: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `1294 + n * (1 ±0)` - // Estimated: `1291 + n * (1 ±0)` - // Minimum execution time: 311_605_000 picoseconds. - Weight::from_parts(315_473_850, 1291) - // Standard Error: 37 - .saturating_add(Weight::from_parts(3, 0).saturating_mul(n.into())) + // Measured: `1166 + n * (1 ±0)` + // Estimated: `1166 + n * (1 ±0)` + // Minimum execution time: 246_128_000 picoseconds. + Weight::from_parts(248_323_705, 1166) + // Standard Error: 21 + .saturating_add(Weight::from_parts(92, 0).saturating_mul(n.into())) .saturating_add(RocksDbWeight::get().reads(7_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) .saturating_add(Weight::from_parts(0, 1).saturating_mul(n.into())) @@ -3051,12 +3057,12 @@ impl WeightInfo for () { /// The range of component `r` is `[0, 800]`. fn seal_take_storage(r: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `925 + r * (296 ±0)` - // Estimated: `932 + r * (297 ±0)` - // Minimum execution time: 298_231_000 picoseconds. - Weight::from_parts(200_178_698, 932) - // Standard Error: 10_452 - .saturating_add(Weight::from_parts(6_107_653, 0).saturating_mul(r.into())) + // Measured: `829 + r * (296 ±0)` + // Estimated: `836 + r * (297 ±0)` + // Minimum execution time: 232_976_000 picoseconds. + Weight::from_parts(132_373_848, 836) + // Standard Error: 9_855 + .saturating_add(Weight::from_parts(6_181_821, 0).saturating_mul(r.into())) .saturating_add(RocksDbWeight::get().reads(6_u64)) .saturating_add(RocksDbWeight::get().reads((1_u64).saturating_mul(r.into()))) .saturating_add(RocksDbWeight::get().writes(3_u64)) @@ -3068,12 +3074,12 @@ impl WeightInfo for () { /// The range of component `n` is `[0, 16384]`. fn seal_take_storage_per_byte(n: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `1308 + n * (1 ±0)` - // Estimated: `1305 + n * (1 ±0)` - // Minimum execution time: 314_970_000 picoseconds. - Weight::from_parts(318_135_821, 1305) - // Standard Error: 26 - .saturating_add(Weight::from_parts(630, 0).saturating_mul(n.into())) + // Measured: `1180 + n * (1 ±0)` + // Estimated: `1180 + n * (1 ±0)` + // Minimum execution time: 247_907_000 picoseconds. + Weight::from_parts(251_984_133, 1180) + // Standard Error: 41 + .saturating_add(Weight::from_parts(639, 0).saturating_mul(n.into())) .saturating_add(RocksDbWeight::get().reads(7_u64)) .saturating_add(RocksDbWeight::get().writes(4_u64)) .saturating_add(Weight::from_parts(0, 1).saturating_mul(n.into())) @@ -3091,12 +3097,12 @@ impl WeightInfo for () { /// The range of component `r` is `[0, 1600]`. fn seal_transfer(r: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `1501 + r * (45 ±0)` - // Estimated: `27383 + r * (2700 ±0)` - // Minimum execution time: 299_629_000 picoseconds. - Weight::from_parts(150_915_187, 27383) - // Standard Error: 25_604 - .saturating_add(Weight::from_parts(20_859_844, 0).saturating_mul(r.into())) + // Measured: `1373 + r * (45 ±0)` + // Estimated: `26753 + r * (2700 ±0)` + // Minimum execution time: 232_614_000 picoseconds. + Weight::from_parts(127_024_367, 26753) + // Standard Error: 54_521 + .saturating_add(Weight::from_parts(20_975_575, 0).saturating_mul(r.into())) .saturating_add(RocksDbWeight::get().reads(7_u64)) .saturating_add(RocksDbWeight::get().reads((1_u64).saturating_mul(r.into()))) .saturating_add(RocksDbWeight::get().writes(4_u64)) @@ -3116,17 +3122,17 @@ impl WeightInfo for () { /// The range of component `r` is `[0, 1600]`. fn seal_call(r: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `1670 + r * (288 ±0)` - // Estimated: `27813 + r * (6391 ±0)` - // Minimum execution time: 299_578_000 picoseconds. - Weight::from_parts(300_036_000, 27813) - // Standard Error: 102_709 - .saturating_add(Weight::from_parts(283_767_316, 0).saturating_mul(r.into())) + // Measured: `1512 + r * (256 ±0)` + // Estimated: `27163 + r * (6231 ±0)` + // Minimum execution time: 233_446_000 picoseconds. + Weight::from_parts(234_338_000, 27163) + // Standard Error: 122_754 + .saturating_add(Weight::from_parts(216_709_600, 0).saturating_mul(r.into())) .saturating_add(RocksDbWeight::get().reads(7_u64)) .saturating_add(RocksDbWeight::get().reads((2_u64).saturating_mul(r.into()))) .saturating_add(RocksDbWeight::get().writes(3_u64)) .saturating_add(RocksDbWeight::get().writes((2_u64).saturating_mul(r.into()))) - .saturating_add(Weight::from_parts(0, 6391).saturating_mul(r.into())) + .saturating_add(Weight::from_parts(0, 6231).saturating_mul(r.into())) } /// Storage: System Account (r:1 w:0) /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: Measured) @@ -3141,17 +3147,17 @@ impl WeightInfo for () { /// The range of component `r` is `[0, 1600]`. fn seal_delegate_call(r: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `0 + r * (935 ±0)` - // Estimated: `22235 + r * (8322 ±10)` - // Minimum execution time: 298_943_000 picoseconds. - Weight::from_parts(299_619_000, 22235) - // Standard Error: 117_493 - .saturating_add(Weight::from_parts(280_555_517, 0).saturating_mul(r.into())) + // Measured: `0 + r * (902 ±0)` + // Estimated: `21755 + r * (8167 ±7)` + // Minimum execution time: 232_717_000 picoseconds. + Weight::from_parts(233_405_000, 21755) + // Standard Error: 98_607 + .saturating_add(Weight::from_parts(212_329_100, 0).saturating_mul(r.into())) .saturating_add(RocksDbWeight::get().reads(6_u64)) .saturating_add(RocksDbWeight::get().reads((2_u64).saturating_mul(r.into()))) .saturating_add(RocksDbWeight::get().writes(3_u64)) .saturating_add(RocksDbWeight::get().writes((1_u64).saturating_mul(r.into()))) - .saturating_add(Weight::from_parts(0, 8322).saturating_mul(r.into())) + .saturating_add(Weight::from_parts(0, 8167).saturating_mul(r.into())) } /// Storage: System Account (r:3 w:2) /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: Measured) @@ -3167,19 +3173,19 @@ impl WeightInfo for () { /// The range of component `c` is `[0, 1048576]`. fn seal_call_per_transfer_clone_byte(t: u32, c: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `1314 + t * (268 ±0)` - // Estimated: `31815 + t * (6290 ±0)` - // Minimum execution time: 453_013_000 picoseconds. - Weight::from_parts(442_536_283, 31815) - // Standard Error: 1_339_541 - .saturating_add(Weight::from_parts(17_062_445, 0).saturating_mul(t.into())) - // Standard Error: 1 - .saturating_add(Weight::from_parts(604, 0).saturating_mul(c.into())) + // Measured: `1154 + t * (204 ±0)` + // Estimated: `31015 + t * (5970 ±0)` + // Minimum execution time: 392_113_000 picoseconds. + Weight::from_parts(376_522_702, 31015) + // Standard Error: 1_388_011 + .saturating_add(Weight::from_parts(19_125_093, 0).saturating_mul(t.into())) + // Standard Error: 2 + .saturating_add(Weight::from_parts(609, 0).saturating_mul(c.into())) .saturating_add(RocksDbWeight::get().reads(9_u64)) .saturating_add(RocksDbWeight::get().reads((2_u64).saturating_mul(t.into()))) .saturating_add(RocksDbWeight::get().writes(5_u64)) .saturating_add(RocksDbWeight::get().writes((2_u64).saturating_mul(t.into()))) - .saturating_add(Weight::from_parts(0, 6290).saturating_mul(t.into())) + .saturating_add(Weight::from_parts(0, 5970).saturating_mul(t.into())) } /// Storage: System Account (r:3202 w:3202) /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: Measured) @@ -3198,17 +3204,17 @@ impl WeightInfo for () { /// The range of component `r` is `[0, 1600]`. fn seal_instantiate(r: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `1988 + r * (319 ±0)` - // Estimated: `34745 + r * (17090 ±0)` - // Minimum execution time: 300_123_000 picoseconds. - Weight::from_parts(300_406_000, 34745) - // Standard Error: 420_997 - .saturating_add(Weight::from_parts(382_704_025, 0).saturating_mul(r.into())) + // Measured: `1859 + r * (253 ±0)` + // Estimated: `33859 + r * (16628 ±0)` + // Minimum execution time: 234_438_000 picoseconds. + Weight::from_parts(234_751_000, 33859) + // Standard Error: 450_507 + .saturating_add(Weight::from_parts(313_121_404, 0).saturating_mul(r.into())) .saturating_add(RocksDbWeight::get().reads(8_u64)) .saturating_add(RocksDbWeight::get().reads((6_u64).saturating_mul(r.into()))) .saturating_add(RocksDbWeight::get().writes(6_u64)) .saturating_add(RocksDbWeight::get().writes((5_u64).saturating_mul(r.into()))) - .saturating_add(Weight::from_parts(0, 17090).saturating_mul(r.into())) + .saturating_add(Weight::from_parts(0, 16628).saturating_mul(r.into())) } /// Storage: System Account (r:4 w:4) /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: Measured) @@ -3229,21 +3235,21 @@ impl WeightInfo for () { /// The range of component `s` is `[0, 983040]`. fn seal_instantiate_per_transfer_input_salt_byte(t: u32, i: u32, s: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `1231 + t * (219 ±0)` - // Estimated: `43797 + t * (3812 ±2)` - // Minimum execution time: 1_636_322_000 picoseconds. - Weight::from_parts(360_859_331, 43797) - // Standard Error: 4_816_923 - .saturating_add(Weight::from_parts(109_179_023, 0).saturating_mul(t.into())) + // Measured: `1071 + t * (187 ±0)` + // Estimated: `42684 + t * (3588 ±2)` + // Minimum execution time: 1_570_178_000 picoseconds. + Weight::from_parts(284_062_841, 42684) + // Standard Error: 4_396_597 + .saturating_add(Weight::from_parts(106_424_960, 0).saturating_mul(t.into())) // Standard Error: 7 - .saturating_add(Weight::from_parts(1_180, 0).saturating_mul(i.into())) + .saturating_add(Weight::from_parts(1_183, 0).saturating_mul(i.into())) // Standard Error: 7 - .saturating_add(Weight::from_parts(1_344, 0).saturating_mul(s.into())) + .saturating_add(Weight::from_parts(1_354, 0).saturating_mul(s.into())) .saturating_add(RocksDbWeight::get().reads(13_u64)) .saturating_add(RocksDbWeight::get().reads((1_u64).saturating_mul(t.into()))) .saturating_add(RocksDbWeight::get().writes(10_u64)) .saturating_add(RocksDbWeight::get().writes((1_u64).saturating_mul(t.into()))) - .saturating_add(Weight::from_parts(0, 3812).saturating_mul(t.into())) + .saturating_add(Weight::from_parts(0, 3588).saturating_mul(t.into())) } /// Storage: System Account (r:1 w:0) /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: Measured) @@ -3258,12 +3264,12 @@ impl WeightInfo for () { /// The range of component `r` is `[0, 1600]`. fn seal_hash_sha2_256(r: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `873 + r * (8 ±0)` - // Estimated: `22190 + r * (40 ±0)` - // Minimum execution time: 297_521_000 picoseconds. - Weight::from_parts(303_523_260, 22190) - // Standard Error: 1_162 - .saturating_add(Weight::from_parts(542_201, 0).saturating_mul(r.into())) + // Measured: `777 + r * (8 ±0)` + // Estimated: `21710 + r * (40 ±0)` + // Minimum execution time: 230_557_000 picoseconds. + Weight::from_parts(233_276_698, 21710) + // Standard Error: 1_026 + .saturating_add(Weight::from_parts(587_723, 0).saturating_mul(r.into())) .saturating_add(RocksDbWeight::get().reads(6_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) .saturating_add(Weight::from_parts(0, 40).saturating_mul(r.into())) @@ -3281,12 +3287,12 @@ impl WeightInfo for () { /// The range of component `n` is `[0, 1048576]`. fn seal_hash_sha2_256_per_byte(n: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `881` - // Estimated: `22225` - // Minimum execution time: 299_877_000 picoseconds. - Weight::from_parts(293_538_014, 22225) - // Standard Error: 2 - .saturating_add(Weight::from_parts(3_967, 0).saturating_mul(n.into())) + // Measured: `785` + // Estimated: `21745` + // Minimum execution time: 234_151_000 picoseconds. + Weight::from_parts(240_199_365, 21745) + // Standard Error: 3 + .saturating_add(Weight::from_parts(3_941, 0).saturating_mul(n.into())) .saturating_add(RocksDbWeight::get().reads(6_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) } @@ -3303,12 +3309,12 @@ impl WeightInfo for () { /// The range of component `r` is `[0, 1600]`. fn seal_hash_keccak_256(r: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `875 + r * (8 ±0)` - // Estimated: `22205 + r * (40 ±0)` - // Minimum execution time: 297_672_000 picoseconds. - Weight::from_parts(299_933_312, 22205) - // Standard Error: 1_138 - .saturating_add(Weight::from_parts(713_189, 0).saturating_mul(r.into())) + // Measured: `779 + r * (8 ±0)` + // Estimated: `21725 + r * (40 ±0)` + // Minimum execution time: 229_931_000 picoseconds. + Weight::from_parts(234_961_690, 21725) + // Standard Error: 1_073 + .saturating_add(Weight::from_parts(758_259, 0).saturating_mul(r.into())) .saturating_add(RocksDbWeight::get().reads(6_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) .saturating_add(Weight::from_parts(0, 40).saturating_mul(r.into())) @@ -3326,12 +3332,12 @@ impl WeightInfo for () { /// The range of component `n` is `[0, 1048576]`. fn seal_hash_keccak_256_per_byte(n: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `883` - // Estimated: `22245` - // Minimum execution time: 299_048_000 picoseconds. - Weight::from_parts(293_055_982, 22245) + // Measured: `787` + // Estimated: `21765` + // Minimum execution time: 232_448_000 picoseconds. + Weight::from_parts(225_974_008, 21765) // Standard Error: 2 - .saturating_add(Weight::from_parts(3_179, 0).saturating_mul(n.into())) + .saturating_add(Weight::from_parts(3_174, 0).saturating_mul(n.into())) .saturating_add(RocksDbWeight::get().reads(6_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) } @@ -3348,12 +3354,12 @@ impl WeightInfo for () { /// The range of component `r` is `[0, 1600]`. fn seal_hash_blake2_256(r: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `875 + r * (8 ±0)` - // Estimated: `22220 + r * (40 ±0)` - // Minimum execution time: 301_991_000 picoseconds. - Weight::from_parts(300_027_441, 22220) - // Standard Error: 981 - .saturating_add(Weight::from_parts(391_319, 0).saturating_mul(r.into())) + // Measured: `779 + r * (8 ±0)` + // Estimated: `21740 + r * (40 ±0)` + // Minimum execution time: 229_251_000 picoseconds. + Weight::from_parts(234_182_627, 21740) + // Standard Error: 836 + .saturating_add(Weight::from_parts(424_375, 0).saturating_mul(r.into())) .saturating_add(RocksDbWeight::get().reads(6_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) .saturating_add(Weight::from_parts(0, 40).saturating_mul(r.into())) @@ -3371,12 +3377,12 @@ impl WeightInfo for () { /// The range of component `n` is `[0, 1048576]`. fn seal_hash_blake2_256_per_byte(n: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `883` - // Estimated: `22265` - // Minimum execution time: 296_522_000 picoseconds. - Weight::from_parts(296_121_638, 22265) + // Measured: `787` + // Estimated: `21785` + // Minimum execution time: 230_896_000 picoseconds. + Weight::from_parts(224_913_556, 21785) // Standard Error: 2 - .saturating_add(Weight::from_parts(916, 0).saturating_mul(n.into())) + .saturating_add(Weight::from_parts(919, 0).saturating_mul(n.into())) .saturating_add(RocksDbWeight::get().reads(6_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) } @@ -3393,12 +3399,12 @@ impl WeightInfo for () { /// The range of component `r` is `[0, 1600]`. fn seal_hash_blake2_128(r: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `875 + r * (8 ±0)` - // Estimated: `22225 + r * (40 ±0)` - // Minimum execution time: 296_228_000 picoseconds. - Weight::from_parts(301_472_299, 22225) - // Standard Error: 875 - .saturating_add(Weight::from_parts(381_027, 0).saturating_mul(r.into())) + // Measured: `779 + r * (8 ±0)` + // Estimated: `21745 + r * (40 ±0)` + // Minimum execution time: 230_314_000 picoseconds. + Weight::from_parts(233_426_327, 21745) + // Standard Error: 926 + .saturating_add(Weight::from_parts(425_341, 0).saturating_mul(r.into())) .saturating_add(RocksDbWeight::get().reads(6_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) .saturating_add(Weight::from_parts(0, 40).saturating_mul(r.into())) @@ -3416,12 +3422,12 @@ impl WeightInfo for () { /// The range of component `n` is `[0, 1048576]`. fn seal_hash_blake2_128_per_byte(n: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `883` - // Estimated: `22235` - // Minimum execution time: 296_644_000 picoseconds. - Weight::from_parts(289_879_744, 22235) + // Measured: `787` + // Estimated: `21755` + // Minimum execution time: 230_424_000 picoseconds. + Weight::from_parts(231_136_218, 21755) // Standard Error: 2 - .saturating_add(Weight::from_parts(925, 0).saturating_mul(n.into())) + .saturating_add(Weight::from_parts(917, 0).saturating_mul(n.into())) .saturating_add(RocksDbWeight::get().reads(6_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) } @@ -3438,12 +3444,12 @@ impl WeightInfo for () { /// The range of component `r` is `[0, 1600]`. fn seal_ecdsa_recover(r: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `800 + r * (78 ±0)` - // Estimated: `21845 + r * (390 ±0)` - // Minimum execution time: 297_804_000 picoseconds. - Weight::from_parts(471_994_534, 21845) - // Standard Error: 9_479 - .saturating_add(Weight::from_parts(36_886_028, 0).saturating_mul(r.into())) + // Measured: `704 + r * (78 ±0)` + // Estimated: `21370 + r * (390 ±0)` + // Minimum execution time: 232_862_000 picoseconds. + Weight::from_parts(379_152_870, 21370) + // Standard Error: 11_607 + .saturating_add(Weight::from_parts(36_955_410, 0).saturating_mul(r.into())) .saturating_add(RocksDbWeight::get().reads(6_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) .saturating_add(Weight::from_parts(0, 390).saturating_mul(r.into())) @@ -3461,12 +3467,12 @@ impl WeightInfo for () { /// The range of component `r` is `[0, 1600]`. fn seal_ecdsa_to_eth_address(r: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `645 + r * (44 ±0)` - // Estimated: `21110 + r * (220 ±0)` - // Minimum execution time: 298_623_000 picoseconds. - Weight::from_parts(322_192_102, 21110) - // Standard Error: 3_207 - .saturating_add(Weight::from_parts(9_243_653, 0).saturating_mul(r.into())) + // Measured: `549 + r * (44 ±0)` + // Estimated: `20630 + r * (220 ±0)` + // Minimum execution time: 232_668_000 picoseconds. + Weight::from_parts(253_308_468, 20630) + // Standard Error: 3_022 + .saturating_add(Weight::from_parts(9_249_849, 0).saturating_mul(r.into())) .saturating_add(RocksDbWeight::get().reads(6_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) .saturating_add(Weight::from_parts(0, 220).saturating_mul(r.into())) @@ -3486,17 +3492,17 @@ impl WeightInfo for () { /// The range of component `r` is `[0, 1600]`. fn seal_set_code_hash(r: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `0 + r * (1030 ±0)` - // Estimated: `30592 + r * (11919 ±7)` - // Minimum execution time: 298_574_000 picoseconds. - Weight::from_parts(299_383_000, 30592) - // Standard Error: 44_061 - .saturating_add(Weight::from_parts(21_625_366, 0).saturating_mul(r.into())) + // Measured: `0 + r * (964 ±0)` + // Estimated: `29920 + r * (11544 ±7)` + // Minimum execution time: 233_910_000 picoseconds. + Weight::from_parts(234_607_000, 29920) + // Standard Error: 46_118 + .saturating_add(Weight::from_parts(21_927_957, 0).saturating_mul(r.into())) .saturating_add(RocksDbWeight::get().reads(6_u64)) .saturating_add(RocksDbWeight::get().reads((3_u64).saturating_mul(r.into()))) .saturating_add(RocksDbWeight::get().writes(3_u64)) .saturating_add(RocksDbWeight::get().writes((2_u64).saturating_mul(r.into()))) - .saturating_add(Weight::from_parts(0, 11919).saturating_mul(r.into())) + .saturating_add(Weight::from_parts(0, 11544).saturating_mul(r.into())) } /// Storage: System Account (r:1 w:0) /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: Measured) @@ -3511,12 +3517,12 @@ impl WeightInfo for () { /// The range of component `r` is `[0, 1600]`. fn seal_reentrance_count(r: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `869 + r * (3 ±0)` - // Estimated: `22215 + r * (15 ±0)` - // Minimum execution time: 297_014_000 picoseconds. - Weight::from_parts(301_226_615, 22215) - // Standard Error: 439 - .saturating_add(Weight::from_parts(143_017, 0).saturating_mul(r.into())) + // Measured: `773 + r * (3 ±0)` + // Estimated: `21735 + r * (15 ±0)` + // Minimum execution time: 231_823_000 picoseconds. + Weight::from_parts(237_996_137, 21735) + // Standard Error: 598 + .saturating_add(Weight::from_parts(163_106, 0).saturating_mul(r.into())) .saturating_add(RocksDbWeight::get().reads(6_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) .saturating_add(Weight::from_parts(0, 15).saturating_mul(r.into())) @@ -3534,12 +3540,12 @@ impl WeightInfo for () { /// The range of component `r` is `[0, 1600]`. fn seal_account_reentrance_count(r: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `2072 + r * (39 ±0)` - // Estimated: `27645 + r * (200 ±0)` - // Minimum execution time: 299_074_000 picoseconds. - Weight::from_parts(336_979_016, 27645) - // Standard Error: 1_163 - .saturating_add(Weight::from_parts(227_998, 0).saturating_mul(r.into())) + // Measured: `1975 + r * (39 ±0)` + // Estimated: `27145 + r * (200 ±0)` + // Minimum execution time: 235_077_000 picoseconds. + Weight::from_parts(267_876_360, 27145) + // Standard Error: 1_147 + .saturating_add(Weight::from_parts(256_275, 0).saturating_mul(r.into())) .saturating_add(RocksDbWeight::get().reads(6_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) .saturating_add(Weight::from_parts(0, 200).saturating_mul(r.into())) @@ -3559,12 +3565,12 @@ impl WeightInfo for () { /// The range of component `r` is `[0, 1600]`. fn seal_instantiation_nonce(r: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `872 + r * (3 ±0)` - // Estimated: `24580 + r * (18 ±0)` - // Minimum execution time: 296_959_000 picoseconds. - Weight::from_parts(303_796_839, 24580) + // Measured: `776 + r * (3 ±0)` + // Estimated: `24004 + r * (18 ±0)` + // Minimum execution time: 231_384_000 picoseconds. + Weight::from_parts(237_086_900, 24004) // Standard Error: 534 - .saturating_add(Weight::from_parts(118_978, 0).saturating_mul(r.into())) + .saturating_add(Weight::from_parts(140_842, 0).saturating_mul(r.into())) .saturating_add(RocksDbWeight::get().reads(7_u64)) .saturating_add(RocksDbWeight::get().writes(4_u64)) .saturating_add(Weight::from_parts(0, 18).saturating_mul(r.into())) @@ -3574,507 +3580,509 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_020_000 picoseconds. - Weight::from_parts(1_355_107, 0) - // Standard Error: 1 - .saturating_add(Weight::from_parts(4_110, 0).saturating_mul(r.into())) + // Minimum execution time: 1_721_000 picoseconds. + Weight::from_parts(2_456_746, 0) + // Standard Error: 30 + .saturating_add(Weight::from_parts(2_788, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 5000]`. fn instr_i64load(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_224_000 picoseconds. - Weight::from_parts(1_819_284, 0) - // Standard Error: 10 - .saturating_add(Weight::from_parts(10_803, 0).saturating_mul(r.into())) + // Minimum execution time: 1_824_000 picoseconds. + Weight::from_parts(2_366_036, 0) + // Standard Error: 5 + .saturating_add(Weight::from_parts(6_446, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 5000]`. fn instr_i64store(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_216_000 picoseconds. - Weight::from_parts(1_795_011, 0) - // Standard Error: 5 - .saturating_add(Weight::from_parts(10_000, 0).saturating_mul(r.into())) + // Minimum execution time: 1_840_000 picoseconds. + Weight::from_parts(2_332_115, 0) + // Standard Error: 4 + .saturating_add(Weight::from_parts(6_012, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 5000]`. fn instr_select(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_127_000 picoseconds. - Weight::from_parts(1_491_730, 0) - // Standard Error: 4 - .saturating_add(Weight::from_parts(11_471, 0).saturating_mul(r.into())) + // Minimum execution time: 1_731_000 picoseconds. + Weight::from_parts(2_078_873, 0) + // Standard Error: 3 + .saturating_add(Weight::from_parts(7_931, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 5000]`. fn instr_if(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_044_000 picoseconds. - Weight::from_parts(2_330_852, 0) - // Standard Error: 69 - .saturating_add(Weight::from_parts(12_866, 0).saturating_mul(r.into())) + // Minimum execution time: 1_768_000 picoseconds. + Weight::from_parts(1_916_551, 0) + // Standard Error: 8 + .saturating_add(Weight::from_parts(10_597, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 5000]`. fn instr_br(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_067_000 picoseconds. - Weight::from_parts(1_399_626, 0) - // Standard Error: 5 - .saturating_add(Weight::from_parts(6_430, 0).saturating_mul(r.into())) + // Minimum execution time: 1_735_000 picoseconds. + Weight::from_parts(1_962_845, 0) + // Standard Error: 14 + .saturating_add(Weight::from_parts(4_631, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 5000]`. fn instr_br_if(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_087_000 picoseconds. - Weight::from_parts(1_463_592, 0) - // Standard Error: 12 - .saturating_add(Weight::from_parts(9_707, 0).saturating_mul(r.into())) + // Minimum execution time: 1_688_000 picoseconds. + Weight::from_parts(1_172_825, 0) + // Standard Error: 76 + .saturating_add(Weight::from_parts(8_007, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 5000]`. fn instr_br_table(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_110_000 picoseconds. - Weight::from_parts(1_058_258, 0) - // Standard Error: 22 - .saturating_add(Weight::from_parts(11_713, 0).saturating_mul(r.into())) + // Minimum execution time: 1_725_000 picoseconds. + Weight::from_parts(1_535_833, 0) + // Standard Error: 26 + .saturating_add(Weight::from_parts(9_489, 0).saturating_mul(r.into())) } /// The range of component `e` is `[1, 256]`. - fn instr_br_table_per_entry(_e: u32, ) -> Weight { + fn instr_br_table_per_entry(e: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_189_000 picoseconds. - Weight::from_parts(1_416_188, 0) + // Minimum execution time: 1_814_000 picoseconds. + Weight::from_parts(1_926_367, 0) + // Standard Error: 16 + .saturating_add(Weight::from_parts(219, 0).saturating_mul(e.into())) } /// The range of component `r` is `[0, 5000]`. fn instr_call(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 2_201_000 picoseconds. - Weight::from_parts(3_375_851, 0) - // Standard Error: 96 - .saturating_add(Weight::from_parts(22_970, 0).saturating_mul(r.into())) + // Minimum execution time: 1_770_000 picoseconds. + Weight::from_parts(2_436_918, 0) + // Standard Error: 8 + .saturating_add(Weight::from_parts(17_890, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 5000]`. fn instr_call_indirect(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_286_000 picoseconds. - Weight::from_parts(2_817_725, 0) - // Standard Error: 55 - .saturating_add(Weight::from_parts(29_437, 0).saturating_mul(r.into())) + // Minimum execution time: 1_920_000 picoseconds. + Weight::from_parts(2_729_712, 0) + // Standard Error: 39 + .saturating_add(Weight::from_parts(24_853, 0).saturating_mul(r.into())) } /// The range of component `l` is `[0, 1024]`. fn instr_call_per_local(l: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_319_000 picoseconds. - Weight::from_parts(1_636_286, 0) - // Standard Error: 31 - .saturating_add(Weight::from_parts(1_240, 0).saturating_mul(l.into())) + // Minimum execution time: 1_869_000 picoseconds. + Weight::from_parts(2_147_970, 0) + // Standard Error: 46 + .saturating_add(Weight::from_parts(1_237, 0).saturating_mul(l.into())) } /// The range of component `r` is `[0, 5000]`. fn instr_local_get(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 2_507_000 picoseconds. - Weight::from_parts(2_785_119, 0) + // Minimum execution time: 3_012_000 picoseconds. + Weight::from_parts(3_234_440, 0) // Standard Error: 1 - .saturating_add(Weight::from_parts(4_601, 0).saturating_mul(r.into())) + .saturating_add(Weight::from_parts(2_433, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 5000]`. fn instr_local_set(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 2_494_000 picoseconds. - Weight::from_parts(2_948_015, 0) - // Standard Error: 14 - .saturating_add(Weight::from_parts(4_788, 0).saturating_mul(r.into())) + // Minimum execution time: 3_037_000 picoseconds. + Weight::from_parts(3_204_600, 0) + // Standard Error: 2 + .saturating_add(Weight::from_parts(3_627, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 5000]`. fn instr_local_tee(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 2_443_000 picoseconds. - Weight::from_parts(3_065_273, 0) - // Standard Error: 15 - .saturating_add(Weight::from_parts(6_489, 0).saturating_mul(r.into())) + // Minimum execution time: 2_974_000 picoseconds. + Weight::from_parts(3_301_366, 0) + // Standard Error: 2 + .saturating_add(Weight::from_parts(3_836, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 5000]`. fn instr_global_get(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_214_000 picoseconds. - Weight::from_parts(1_634_049, 0) - // Standard Error: 3 - .saturating_add(Weight::from_parts(8_960, 0).saturating_mul(r.into())) + // Minimum execution time: 1_853_000 picoseconds. + Weight::from_parts(2_310_141, 0) + // Standard Error: 4 + .saturating_add(Weight::from_parts(8_387, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 5000]`. fn instr_global_set(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_180_000 picoseconds. - Weight::from_parts(1_555_599, 0) + // Minimum execution time: 1_828_000 picoseconds. + Weight::from_parts(2_315_323, 0) // Standard Error: 4 - .saturating_add(Weight::from_parts(9_205, 0).saturating_mul(r.into())) + .saturating_add(Weight::from_parts(8_797, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 5000]`. fn instr_memory_current(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_223_000 picoseconds. - Weight::from_parts(1_626_002, 0) - // Standard Error: 5 - .saturating_add(Weight::from_parts(8_181, 0).saturating_mul(r.into())) + // Minimum execution time: 1_800_000 picoseconds. + Weight::from_parts(2_117_252, 0) + // Standard Error: 4 + .saturating_add(Weight::from_parts(3_741, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 16]`. fn instr_memory_grow(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_148_000 picoseconds. - Weight::from_parts(307_155, 0) - // Standard Error: 138_541 - .saturating_add(Weight::from_parts(13_291_570, 0).saturating_mul(r.into())) + // Minimum execution time: 1_743_000 picoseconds. + Weight::from_parts(1_054_728, 0) + // Standard Error: 137_128 + .saturating_add(Weight::from_parts(13_179_446, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 5000]`. fn instr_i64clz(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_079_000 picoseconds. - Weight::from_parts(1_372_409, 0) - // Standard Error: 50 - .saturating_add(Weight::from_parts(6_427, 0).saturating_mul(r.into())) + // Minimum execution time: 1_735_000 picoseconds. + Weight::from_parts(2_026_568, 0) + // Standard Error: 2 + .saturating_add(Weight::from_parts(3_802, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 5000]`. fn instr_i64ctz(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_116_000 picoseconds. - Weight::from_parts(1_415_180, 0) + // Minimum execution time: 1_630_000 picoseconds. + Weight::from_parts(1_980_222, 0) // Standard Error: 2 - .saturating_add(Weight::from_parts(6_353, 0).saturating_mul(r.into())) + .saturating_add(Weight::from_parts(3_735, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 5000]`. fn instr_i64popcnt(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_058_000 picoseconds. - Weight::from_parts(1_314_206, 0) - // Standard Error: 35 - .saturating_add(Weight::from_parts(6_431, 0).saturating_mul(r.into())) + // Minimum execution time: 1_695_000 picoseconds. + Weight::from_parts(2_040_266, 0) + // Standard Error: 8 + .saturating_add(Weight::from_parts(3_718, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 5000]`. fn instr_i64eqz(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_059_000 picoseconds. - Weight::from_parts(1_455_331, 0) - // Standard Error: 2 - .saturating_add(Weight::from_parts(6_507, 0).saturating_mul(r.into())) + // Minimum execution time: 1_697_000 picoseconds. + Weight::from_parts(2_004_663, 0) + // Standard Error: 3 + .saturating_add(Weight::from_parts(3_661, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 5000]`. fn instr_i64extendsi32(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_057_000 picoseconds. - Weight::from_parts(1_421_085, 0) - // Standard Error: 2 - .saturating_add(Weight::from_parts(6_177, 0).saturating_mul(r.into())) + // Minimum execution time: 1_705_000 picoseconds. + Weight::from_parts(2_682_122, 0) + // Standard Error: 98 + .saturating_add(Weight::from_parts(3_705, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 5000]`. fn instr_i64extendui32(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_079_000 picoseconds. - Weight::from_parts(1_429_275, 0) + // Minimum execution time: 1_674_000 picoseconds. + Weight::from_parts(1_989_835, 0) // Standard Error: 2 - .saturating_add(Weight::from_parts(6_175, 0).saturating_mul(r.into())) + .saturating_add(Weight::from_parts(3_801, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 5000]`. fn instr_i32wrapi64(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_065_000 picoseconds. - Weight::from_parts(1_403_813, 0) - // Standard Error: 3 - .saturating_add(Weight::from_parts(6_192, 0).saturating_mul(r.into())) + // Minimum execution time: 1_695_000 picoseconds. + Weight::from_parts(2_083_824, 0) + // Standard Error: 17 + .saturating_add(Weight::from_parts(3_719, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 5000]`. fn instr_i64eq(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_061_000 picoseconds. - Weight::from_parts(1_421_984, 0) + // Minimum execution time: 1_678_000 picoseconds. + Weight::from_parts(1_996_335, 0) // Standard Error: 4 - .saturating_add(Weight::from_parts(9_094, 0).saturating_mul(r.into())) + .saturating_add(Weight::from_parts(5_995, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 5000]`. fn instr_i64ne(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_075_000 picoseconds. - Weight::from_parts(1_431_453, 0) - // Standard Error: 2 - .saturating_add(Weight::from_parts(9_084, 0).saturating_mul(r.into())) + // Minimum execution time: 1_697_000 picoseconds. + Weight::from_parts(2_046_566, 0) + // Standard Error: 3 + .saturating_add(Weight::from_parts(5_936, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 5000]`. fn instr_i64lts(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_100_000 picoseconds. - Weight::from_parts(1_452_384, 0) - // Standard Error: 3 - .saturating_add(Weight::from_parts(9_081, 0).saturating_mul(r.into())) + // Minimum execution time: 1_721_000 picoseconds. + Weight::from_parts(2_406_930, 0) + // Standard Error: 150 + .saturating_add(Weight::from_parts(5_992, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 5000]`. fn instr_i64ltu(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_090_000 picoseconds. - Weight::from_parts(1_466_416, 0) - // Standard Error: 17 - .saturating_add(Weight::from_parts(9_091, 0).saturating_mul(r.into())) + // Minimum execution time: 1_749_000 picoseconds. + Weight::from_parts(1_987_158, 0) + // Standard Error: 2 + .saturating_add(Weight::from_parts(5_994, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 5000]`. fn instr_i64gts(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_056_000 picoseconds. - Weight::from_parts(1_676_091, 0) - // Standard Error: 47 - .saturating_add(Weight::from_parts(9_082, 0).saturating_mul(r.into())) + // Minimum execution time: 1_706_000 picoseconds. + Weight::from_parts(2_038_138, 0) + // Standard Error: 2 + .saturating_add(Weight::from_parts(5_802, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 5000]`. fn instr_i64gtu(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_053_000 picoseconds. - Weight::from_parts(1_232_790, 0) - // Standard Error: 43 - .saturating_add(Weight::from_parts(9_329, 0).saturating_mul(r.into())) + // Minimum execution time: 1_670_000 picoseconds. + Weight::from_parts(2_555_035, 0) + // Standard Error: 74 + .saturating_add(Weight::from_parts(5_958, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 5000]`. fn instr_i64les(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_091_000 picoseconds. - Weight::from_parts(1_476_212, 0) - // Standard Error: 4 - .saturating_add(Weight::from_parts(9_074, 0).saturating_mul(r.into())) + // Minimum execution time: 1_694_000 picoseconds. + Weight::from_parts(1_991_929, 0) + // Standard Error: 27 + .saturating_add(Weight::from_parts(6_037, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 5000]`. fn instr_i64leu(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_083_000 picoseconds. - Weight::from_parts(1_484_966, 0) - // Standard Error: 3 - .saturating_add(Weight::from_parts(9_184, 0).saturating_mul(r.into())) + // Minimum execution time: 1_684_000 picoseconds. + Weight::from_parts(1_969_284, 0) + // Standard Error: 2 + .saturating_add(Weight::from_parts(6_104, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 5000]`. fn instr_i64ges(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_089_000 picoseconds. - Weight::from_parts(1_516_766, 0) - // Standard Error: 4 - .saturating_add(Weight::from_parts(9_063, 0).saturating_mul(r.into())) + // Minimum execution time: 1_697_000 picoseconds. + Weight::from_parts(2_023_608, 0) + // Standard Error: 3 + .saturating_add(Weight::from_parts(5_946, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 5000]`. fn instr_i64geu(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 996_000 picoseconds. - Weight::from_parts(1_460_638, 0) - // Standard Error: 2 - .saturating_add(Weight::from_parts(9_076, 0).saturating_mul(r.into())) + // Minimum execution time: 1_659_000 picoseconds. + Weight::from_parts(1_963_145, 0) + // Standard Error: 6 + .saturating_add(Weight::from_parts(6_014, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 5000]`. fn instr_i64add(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_095_000 picoseconds. - Weight::from_parts(1_448_412, 0) + // Minimum execution time: 1_722_000 picoseconds. + Weight::from_parts(2_016_286, 0) // Standard Error: 3 - .saturating_add(Weight::from_parts(8_977, 0).saturating_mul(r.into())) + .saturating_add(Weight::from_parts(5_889, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 5000]`. fn instr_i64sub(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_071_000 picoseconds. - Weight::from_parts(1_459_165, 0) - // Standard Error: 2 - .saturating_add(Weight::from_parts(8_852, 0).saturating_mul(r.into())) + // Minimum execution time: 1_656_000 picoseconds. + Weight::from_parts(1_966_234, 0) + // Standard Error: 17 + .saturating_add(Weight::from_parts(6_171, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 5000]`. fn instr_i64mul(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_082_000 picoseconds. - Weight::from_parts(1_450_718, 0) - // Standard Error: 3 - .saturating_add(Weight::from_parts(8_860, 0).saturating_mul(r.into())) + // Minimum execution time: 1_695_000 picoseconds. + Weight::from_parts(2_004_559, 0) + // Standard Error: 2 + .saturating_add(Weight::from_parts(5_785, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 5000]`. fn instr_i64divs(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_065_000 picoseconds. - Weight::from_parts(1_436_871, 0) - // Standard Error: 9 - .saturating_add(Weight::from_parts(15_241, 0).saturating_mul(r.into())) + // Minimum execution time: 1_681_000 picoseconds. + Weight::from_parts(2_135_505, 0) + // Standard Error: 6 + .saturating_add(Weight::from_parts(11_761, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 5000]`. fn instr_i64divu(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_065_000 picoseconds. - Weight::from_parts(1_423_806, 0) - // Standard Error: 17 - .saturating_add(Weight::from_parts(14_706, 0).saturating_mul(r.into())) + // Minimum execution time: 1_653_000 picoseconds. + Weight::from_parts(2_153_167, 0) + // Standard Error: 5 + .saturating_add(Weight::from_parts(10_523, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 5000]`. fn instr_i64rems(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_059_000 picoseconds. - Weight::from_parts(1_735_816, 0) - // Standard Error: 66 - .saturating_add(Weight::from_parts(15_230, 0).saturating_mul(r.into())) + // Minimum execution time: 1_697_000 picoseconds. + Weight::from_parts(2_570_266, 0) + // Standard Error: 50 + .saturating_add(Weight::from_parts(11_971, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 5000]`. fn instr_i64remu(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_076_000 picoseconds. - Weight::from_parts(1_451_290, 0) - // Standard Error: 6 - .saturating_add(Weight::from_parts(14_530, 0).saturating_mul(r.into())) + // Minimum execution time: 1_719_000 picoseconds. + Weight::from_parts(2_101_082, 0) + // Standard Error: 3 + .saturating_add(Weight::from_parts(10_709, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 5000]`. fn instr_i64and(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_079_000 picoseconds. - Weight::from_parts(1_457_537, 0) - // Standard Error: 3 - .saturating_add(Weight::from_parts(8_963, 0).saturating_mul(r.into())) + // Minimum execution time: 1_745_000 picoseconds. + Weight::from_parts(2_621_890, 0) + // Standard Error: 94 + .saturating_add(Weight::from_parts(5_545, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 5000]`. fn instr_i64or(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_032_000 picoseconds. - Weight::from_parts(1_475_315, 0) - // Standard Error: 4 - .saturating_add(Weight::from_parts(8_956, 0).saturating_mul(r.into())) + // Minimum execution time: 1_670_000 picoseconds. + Weight::from_parts(2_046_821, 0) + // Standard Error: 2 + .saturating_add(Weight::from_parts(5_762, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 5000]`. fn instr_i64xor(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_056_000 picoseconds. - Weight::from_parts(1_450_071, 0) + // Minimum execution time: 1_714_000 picoseconds. + Weight::from_parts(2_010_173, 0) // Standard Error: 2 - .saturating_add(Weight::from_parts(8_960, 0).saturating_mul(r.into())) + .saturating_add(Weight::from_parts(5_911, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 5000]`. fn instr_i64shl(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_116_000 picoseconds. - Weight::from_parts(1_429_705, 0) + // Minimum execution time: 1_756_000 picoseconds. + Weight::from_parts(2_044_772, 0) // Standard Error: 2 - .saturating_add(Weight::from_parts(9_027, 0).saturating_mul(r.into())) + .saturating_add(Weight::from_parts(5_857, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 5000]`. fn instr_i64shrs(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_059_000 picoseconds. - Weight::from_parts(1_429_307, 0) - // Standard Error: 4 - .saturating_add(Weight::from_parts(9_048, 0).saturating_mul(r.into())) + // Minimum execution time: 1_741_000 picoseconds. + Weight::from_parts(2_012_819, 0) + // Standard Error: 2 + .saturating_add(Weight::from_parts(6_127, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 5000]`. fn instr_i64shru(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_093_000 picoseconds. - Weight::from_parts(1_411_827, 0) - // Standard Error: 4 - .saturating_add(Weight::from_parts(9_528, 0).saturating_mul(r.into())) + // Minimum execution time: 1_693_000 picoseconds. + Weight::from_parts(2_090_981, 0) + // Standard Error: 2 + .saturating_add(Weight::from_parts(5_839, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 5000]`. fn instr_i64rotl(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_115_000 picoseconds. - Weight::from_parts(1_441_025, 0) + // Minimum execution time: 1_704_000 picoseconds. + Weight::from_parts(2_014_108, 0) // Standard Error: 4 - .saturating_add(Weight::from_parts(9_043, 0).saturating_mul(r.into())) + .saturating_add(Weight::from_parts(6_005, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 5000]`. fn instr_i64rotr(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_077_000 picoseconds. - Weight::from_parts(1_480_666, 0) - // Standard Error: 4 - .saturating_add(Weight::from_parts(9_031, 0).saturating_mul(r.into())) + // Minimum execution time: 1_715_000 picoseconds. + Weight::from_parts(2_053_720, 0) + // Standard Error: 5 + .saturating_add(Weight::from_parts(5_860, 0).saturating_mul(r.into())) } } From cb7e510ee0df351e26c8032eabe19b8f3948d35f Mon Sep 17 00:00:00 2001 From: Michal Kucharczyk <1728078+michalkucharczyk@users.noreply.github.com> Date: Tue, 21 Mar 2023 08:56:33 +0100 Subject: [PATCH 266/558] zombienet: renamed deprecated transfer function (#13652) * zombienet: renamed deprecated transfer function * camelCase --- zombienet/0000-block-building/transaction-gets-finalized.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/zombienet/0000-block-building/transaction-gets-finalized.js b/zombienet/0000-block-building/transaction-gets-finalized.js index a38cf603f..d16e85f2c 100644 --- a/zombienet/0000-block-building/transaction-gets-finalized.js +++ b/zombienet/0000-block-building/transaction-gets-finalized.js @@ -14,7 +14,7 @@ async function run(nodeName, networkInfo, args) { const bob = '5FHneW46xGXgs5mUiveU4sbTyGBzmstUspZC92UhjJM694ty'; // Create a extrinsic, transferring 10^20 units to Bob - const transfer = api.tx.balances.transfer(bob, 10n**20n); + const transfer = api.tx.balances.transferAllowDeath(bob, 10n**20n); let transaction_success_event = false; try { From 6e71ebb00a06605fd45d236e17be741055f37ccd Mon Sep 17 00:00:00 2001 From: Arkadiy Paronyan Date: Tue, 21 Mar 2023 10:45:40 +0100 Subject: [PATCH 267/558] Bump parity-db (#13656) --- Cargo.lock | 16 ++++++++-------- bin/node/bench/Cargo.toml | 2 +- client/db/Cargo.toml | 2 +- 3 files changed, 10 insertions(+), 10 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 5306d2015..e2ec461e1 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -652,9 +652,9 @@ dependencies = [ [[package]] name = "block-buffer" -version = "0.10.3" +version = "0.10.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "69cce20737498f97b993470a6e536b8523f0af7892a4f928cceb1ac5e52ebe7e" +checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71" dependencies = [ "generic-array 0.14.6", ] @@ -1706,7 +1706,7 @@ version = "0.10.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8168378f4e5023e7218c89c891c0fd8ecdb5e5e4f18cb78f38cf245dd021e76f" dependencies = [ - "block-buffer 0.10.3", + "block-buffer 0.10.4", "crypto-common", "subtle", ] @@ -3667,9 +3667,9 @@ checksum = "884e2677b40cc8c339eaefcb701c32ef1fd2493d71118dc0ca4b6a736c93bd67" [[package]] name = "libc" -version = "0.2.139" +version = "0.2.140" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "201de327520df007757c1f0adce6e827fe8562fbc28bfd9c15571c66ca1f5f79" +checksum = "99227334921fae1a979cf0bfdfcc6b3e5ce376ef57e16fb6fb3ea2ed6095f80c" [[package]] name = "libgit2-sys" @@ -6925,9 +6925,9 @@ dependencies = [ [[package]] name = "parity-db" -version = "0.4.4" +version = "0.4.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "df89dd8311063c54ae4e03d9aeb597b04212a57e82c339344130a9cad9b3e2d9" +checksum = "00bfb81cf5c90a222db2fb7b3a7cbf8cc7f38dfb6647aca4d98edf8281f56ed5" dependencies = [ "blake2", "crc32fast", @@ -11820,7 +11820,7 @@ checksum = "97fee6b57c6a41524a810daee9286c02d7752c4253064d0b05472833a438f675" dependencies = [ "cfg-if", "digest 0.10.6", - "rand 0.8.5", + "rand 0.7.3", "static_assertions", ] diff --git a/bin/node/bench/Cargo.toml b/bin/node/bench/Cargo.toml index be5e94ee4..153a721da 100644 --- a/bin/node/bench/Cargo.toml +++ b/bin/node/bench/Cargo.toml @@ -38,7 +38,7 @@ tempfile = "3.1.0" fs_extra = "1" rand = { version = "0.8.5", features = ["small_rng"] } lazy_static = "1.4.0" -parity-db = "0.4.4" +parity-db = "0.4.6" sc-transaction-pool = { version = "4.0.0-dev", path = "../../../client/transaction-pool" } sc-transaction-pool-api = { version = "4.0.0-dev", path = "../../../client/transaction-pool/api" } futures = { version = "0.3.21", features = ["thread-pool"] } diff --git a/client/db/Cargo.toml b/client/db/Cargo.toml index c96e5c740..8e4bcf18a 100644 --- a/client/db/Cargo.toml +++ b/client/db/Cargo.toml @@ -22,7 +22,7 @@ kvdb-memorydb = "0.13.0" kvdb-rocksdb = { version = "0.17.0", optional = true } linked-hash-map = "0.5.4" log = "0.4.17" -parity-db = "0.4.4" +parity-db = "0.4.6" parking_lot = "0.12.1" sc-client-api = { version = "4.0.0-dev", path = "../api" } sc-state-db = { version = "0.10.0-dev", path = "../state-db" } From 859bb12cd83b685f1c128133412b3afd8cc6de68 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alexander=20Thei=C3=9Fen?= Date: Tue, 21 Mar 2023 10:46:25 +0100 Subject: [PATCH 268/558] contracts: Follow up for benchmark simplification (#13635) * Rename BATCHES -> RUNS * Reduce runs of slow benchmarks * ".git/.scripts/commands/bench/bench.sh" pallet dev pallet_contracts --------- Co-authored-by: command-bot <> --- frame/contracts/src/benchmarking/mod.rs | 188 +-- frame/contracts/src/weights.rs | 1930 +++++++++++------------ 2 files changed, 1060 insertions(+), 1058 deletions(-) diff --git a/frame/contracts/src/benchmarking/mod.rs b/frame/contracts/src/benchmarking/mod.rs index 9b75b140f..5494b57b6 100644 --- a/frame/contracts/src/benchmarking/mod.rs +++ b/frame/contracts/src/benchmarking/mod.rs @@ -45,18 +45,18 @@ use sp_runtime::{ use sp_std::prelude::*; use wasm_instrument::parity_wasm::elements::{BlockType, BrTableData, Instruction, ValueType}; -/// How many batches we do per API benchmark. +/// How many runs we do per API benchmark. /// /// This is picked more or less arbitrary. We experimented with different numbers until /// the results appeared to be stable. Reducing the number would speed up the benchmarks /// but might make the results less precise. -const API_BENCHMARK_BATCHES: u32 = 1600; +const API_BENCHMARK_RUNS: u32 = 1600; -/// How many batches we do per instruction benchmark. +/// How many runs we do per instruction benchmark. /// -/// Same rationale as for [`API_BENCHMARK_BATCHES`]. The number is bigger because instruction +/// Same rationale as for [`API_BENCHMARK_RUNS`]. The number is bigger because instruction /// benchmarks are faster. -const INSTR_BENCHMARK_BATCHES: u32 = 5000; +const INSTR_BENCHMARK_RUNS: u32 = 5000; /// An instantiated and deployed contract. struct Contract { @@ -425,7 +425,7 @@ benchmarks! { #[pov_mode = Measured] seal_caller { - let r in 0 .. API_BENCHMARK_BATCHES; + let r in 0 .. API_BENCHMARK_RUNS; let instance = Contract::::new(WasmModule::getter( "seal0", "seal_caller", r ), vec![])?; @@ -434,7 +434,7 @@ benchmarks! { #[pov_mode = Measured] seal_is_contract { - let r in 0 .. API_BENCHMARK_BATCHES; + let r in 0 .. API_BENCHMARK_RUNS; let accounts = (0 .. r) .map(|n| account::("account", n, 0)) .collect::>(); @@ -472,7 +472,7 @@ benchmarks! { #[pov_mode = Measured] seal_code_hash { - let r in 0 .. API_BENCHMARK_BATCHES; + let r in 0 .. API_BENCHMARK_RUNS; let accounts = (0 .. r) .map(|n| account::("account", n, 0)) .collect::>(); @@ -518,7 +518,7 @@ benchmarks! { #[pov_mode = Measured] seal_own_code_hash { - let r in 0 .. API_BENCHMARK_BATCHES; + let r in 0 .. API_BENCHMARK_RUNS; let instance = Contract::::new(WasmModule::getter( "seal0", "seal_own_code_hash", r ), vec![])?; @@ -527,7 +527,7 @@ benchmarks! { #[pov_mode = Measured] seal_caller_is_origin { - let r in 0 .. API_BENCHMARK_BATCHES; + let r in 0 .. API_BENCHMARK_RUNS; let code = WasmModule::::from(ModuleDefinition { memory: Some(ImportedMemory::max::()), imported_functions: vec![ImportedFunction { @@ -548,7 +548,7 @@ benchmarks! { #[pov_mode = Measured] seal_address { - let r in 0 .. API_BENCHMARK_BATCHES; + let r in 0 .. API_BENCHMARK_RUNS; let instance = Contract::::new(WasmModule::getter( "seal0", "seal_address", r ), vec![])?; @@ -557,7 +557,7 @@ benchmarks! { #[pov_mode = Measured] seal_gas_left { - let r in 0 .. API_BENCHMARK_BATCHES; + let r in 0 .. API_BENCHMARK_RUNS; let instance = Contract::::new(WasmModule::getter( "seal0", "seal_gas_left", r ), vec![])?; @@ -566,7 +566,7 @@ benchmarks! { #[pov_mode = Measured] seal_balance { - let r in 0 .. API_BENCHMARK_BATCHES; + let r in 0 .. API_BENCHMARK_RUNS; let instance = Contract::::new(WasmModule::getter( "seal0", "seal_balance", r ), vec![])?; @@ -575,7 +575,7 @@ benchmarks! { #[pov_mode = Measured] seal_value_transferred { - let r in 0 .. API_BENCHMARK_BATCHES; + let r in 0 .. API_BENCHMARK_RUNS; let instance = Contract::::new(WasmModule::getter( "seal0", "seal_value_transferred", r ), vec![])?; @@ -584,7 +584,7 @@ benchmarks! { #[pov_mode = Measured] seal_minimum_balance { - let r in 0 .. API_BENCHMARK_BATCHES; + let r in 0 .. API_BENCHMARK_RUNS; let instance = Contract::::new(WasmModule::getter( "seal0", "seal_minimum_balance", r ), vec![])?; @@ -593,7 +593,7 @@ benchmarks! { #[pov_mode = Measured] seal_block_number { - let r in 0 .. API_BENCHMARK_BATCHES; + let r in 0 .. API_BENCHMARK_RUNS; let instance = Contract::::new(WasmModule::getter( "seal0", "seal_block_number", r ), vec![])?; @@ -602,7 +602,7 @@ benchmarks! { #[pov_mode = Measured] seal_now { - let r in 0 .. API_BENCHMARK_BATCHES; + let r in 0 .. API_BENCHMARK_RUNS; let instance = Contract::::new(WasmModule::getter( "seal0", "seal_now", r ), vec![])?; @@ -611,7 +611,7 @@ benchmarks! { #[pov_mode = Measured] seal_weight_to_fee { - let r in 0 .. API_BENCHMARK_BATCHES; + let r in 0 .. API_BENCHMARK_RUNS; let pages = code::max_pages::(); let code = WasmModule::::from(ModuleDefinition { memory: Some(ImportedMemory::max::()), @@ -639,7 +639,7 @@ benchmarks! { #[pov_mode = Measured] seal_gas { - let r in 0 .. API_BENCHMARK_BATCHES; + let r in 0 .. API_BENCHMARK_RUNS; let code = WasmModule::::from(ModuleDefinition { imported_functions: vec![ImportedFunction { module: "seal0", @@ -660,7 +660,7 @@ benchmarks! { #[pov_mode = Measured] seal_input { - let r in 0 .. API_BENCHMARK_BATCHES; + let r in 0 .. API_BENCHMARK_RUNS; let code = WasmModule::::from(ModuleDefinition { memory: Some(ImportedMemory::max::()), imported_functions: vec![ImportedFunction { @@ -815,7 +815,7 @@ benchmarks! { // used. #[pov_mode = Measured] seal_random { - let r in 0 .. API_BENCHMARK_BATCHES; + let r in 0 .. API_BENCHMARK_RUNS; let pages = code::max_pages::(); let subject_len = T::Schedule::get().limits.subject_len; assert!(subject_len < 1024); @@ -850,7 +850,7 @@ benchmarks! { // We benchmark for the worst case (largest event). #[pov_mode = Measured] seal_deposit_event { - let r in 0 .. API_BENCHMARK_BATCHES; + let r in 0 .. API_BENCHMARK_RUNS; let code = WasmModule::::from(ModuleDefinition { memory: Some(ImportedMemory::max::()), imported_functions: vec![ImportedFunction { @@ -914,7 +914,7 @@ benchmarks! { // against an excessive use. #[pov_mode = Measured] seal_debug_message { - let r in 0 .. API_BENCHMARK_BATCHES; + let r in 0 .. API_BENCHMARK_RUNS; let code = WasmModule::::from(ModuleDefinition { memory: Some(ImportedMemory { min_pages: 1, max_pages: 1 }), imported_functions: vec![ImportedFunction { @@ -1009,7 +1009,7 @@ benchmarks! { #[skip_meta] #[pov_mode = Measured] seal_set_storage { - let r in 0 .. API_BENCHMARK_BATCHES/2; + let r in 0 .. API_BENCHMARK_RUNS/2; let max_key_len = T::MaxStorageKeyLen::get(); let keys = (0 .. r) .map(|n| { let mut h = T::Hashing::hash_of(&n).as_ref().to_vec(); @@ -1143,11 +1143,11 @@ benchmarks! { // Similar to seal_set_storage. We store all the keys that we are about to // delete beforehand in order to prevent any optimizations that could occur when // deleting a non existing key. We generate keys of a maximum length, and have to - // reduce batch size in order to make resulting contract code size less than MaxCodeLen. + // the amount of runs in order to make resulting contract code size less than MaxCodeLen. #[skip_meta] #[pov_mode = Measured] seal_clear_storage { - let r in 0 .. API_BENCHMARK_BATCHES/2; + let r in 0 .. API_BENCHMARK_RUNS/2; let max_key_len = T::MaxStorageKeyLen::get(); let keys = (0 .. r) .map(|n| { let mut h = T::Hashing::hash_of(&n).as_ref().to_vec(); @@ -1236,7 +1236,7 @@ benchmarks! { #[skip_meta] #[pov_mode = Measured] seal_get_storage { - let r in 0 .. API_BENCHMARK_BATCHES/2; + let r in 0 .. API_BENCHMARK_RUNS/2; let max_key_len = T::MaxStorageKeyLen::get(); let keys = (0 .. r) .map(|n| { let mut h = T::Hashing::hash_of(&n).as_ref().to_vec(); @@ -1339,7 +1339,7 @@ benchmarks! { #[skip_meta] #[pov_mode = Measured] seal_contains_storage { - let r in 0 .. API_BENCHMARK_BATCHES/2; + let r in 0 .. API_BENCHMARK_RUNS/2; let max_key_len = T::MaxStorageKeyLen::get(); let keys = (0 .. r) .map(|n| { let mut h = T::Hashing::hash_of(&n).as_ref().to_vec(); @@ -1429,7 +1429,7 @@ benchmarks! { #[skip_meta] #[pov_mode = Measured] seal_take_storage { - let r in 0 .. API_BENCHMARK_BATCHES/2; + let r in 0 .. API_BENCHMARK_RUNS/2; let max_key_len = T::MaxStorageKeyLen::get(); let keys = (0 .. r) .map(|n| { let mut h = T::Hashing::hash_of(&n).as_ref().to_vec(); @@ -1531,7 +1531,7 @@ benchmarks! { // We transfer to unique accounts. #[pov_mode = Measured] seal_transfer { - let r in 0 .. API_BENCHMARK_BATCHES; + let r in 0 .. API_BENCHMARK_RUNS; let accounts = (0..r) .map(|i| account::("receiver", i, 0)) .collect::>(); @@ -1583,9 +1583,11 @@ benchmarks! { } // We call unique accounts. + // + // This is a slow call: We redeuce the number of runs. #[pov_mode = Measured] seal_call { - let r in 0 .. API_BENCHMARK_BATCHES; + let r in 0 .. API_BENCHMARK_RUNS / 2; let dummy_code = WasmModule::::dummy_with_bytes(0); let callees = (0..r) .map(|i| Contract::with_index(i + 1, dummy_code.clone(), vec![])) @@ -1642,9 +1644,10 @@ benchmarks! { let origin = RawOrigin::Signed(instance.caller.clone()); }: call(origin, instance.addr, 0u32.into(), Weight::MAX, None, vec![]) + // This is a slow call: We redeuce the number of runs. #[pov_mode = Measured] seal_delegate_call { - let r in 0 .. API_BENCHMARK_BATCHES; + let r in 0 .. API_BENCHMARK_RUNS / 2; let hashes = (0..r) .map(|i| { let code = WasmModule::::dummy_with_bytes(i); @@ -1751,9 +1754,10 @@ benchmarks! { }: call(origin, instance.addr, 0u32.into(), Weight::MAX, None, bytes) // We assume that every instantiate sends at least the minimum balance. + // This is a slow call: We redeuce the number of runs. #[pov_mode = Measured] seal_instantiate { - let r in 0 .. API_BENCHMARK_BATCHES; + let r in 0 .. API_BENCHMARK_RUNS / 2; let hashes = (0..r) .map(|i| { let code = WasmModule::::from(ModuleDefinition { @@ -1939,7 +1943,7 @@ benchmarks! { // Only the overhead of calling the function itself with minimal arguments. #[pov_mode = Measured] seal_hash_sha2_256 { - let r in 0 .. API_BENCHMARK_BATCHES; + let r in 0 .. API_BENCHMARK_RUNS; let instance = Contract::::new(WasmModule::hasher( "seal_hash_sha2_256", r, 0, ), vec![])?; @@ -1959,7 +1963,7 @@ benchmarks! { // Only the overhead of calling the function itself with minimal arguments. #[pov_mode = Measured] seal_hash_keccak_256 { - let r in 0 .. API_BENCHMARK_BATCHES; + let r in 0 .. API_BENCHMARK_RUNS; let instance = Contract::::new(WasmModule::hasher( "seal_hash_keccak_256", r, 0, ), vec![])?; @@ -1979,7 +1983,7 @@ benchmarks! { // Only the overhead of calling the function itself with minimal arguments. #[pov_mode = Measured] seal_hash_blake2_256 { - let r in 0 .. API_BENCHMARK_BATCHES; + let r in 0 .. API_BENCHMARK_RUNS; let instance = Contract::::new(WasmModule::hasher( "seal_hash_blake2_256", r, 0, ), vec![])?; @@ -1999,7 +2003,7 @@ benchmarks! { // Only the overhead of calling the function itself with minimal arguments. #[pov_mode = Measured] seal_hash_blake2_128 { - let r in 0 .. API_BENCHMARK_BATCHES; + let r in 0 .. API_BENCHMARK_RUNS; let instance = Contract::::new(WasmModule::hasher( "seal_hash_blake2_128", r, 0, ), vec![])?; @@ -2018,9 +2022,10 @@ benchmarks! { // Only calling the function itself with valid arguments. // It generates different private keys and signatures for the message "Hello world". + // This is a slow call: We redeuce the number of runs. #[pov_mode = Measured] seal_ecdsa_recover { - let r in 0 .. API_BENCHMARK_BATCHES; + let r in 0 .. API_BENCHMARK_RUNS / 10; let message_hash = sp_io::hashing::blake2_256("Hello world".as_bytes()); let key_type = sp_core::crypto::KeyTypeId(*b"code"); @@ -2067,9 +2072,10 @@ benchmarks! { // Only calling the function itself for the list of // generated different ECDSA keys. + // This is a slow call: We redeuce the number of runs. #[pov_mode = Measured] seal_ecdsa_to_eth_address { - let r in 0 .. API_BENCHMARK_BATCHES; + let r in 0 .. API_BENCHMARK_RUNS / 10; let key_type = sp_core::crypto::KeyTypeId(*b"code"); let pub_keys_bytes = (0..r) .flat_map(|_| { @@ -2105,7 +2111,7 @@ benchmarks! { #[pov_mode = Measured] seal_set_code_hash { - let r in 0 .. API_BENCHMARK_BATCHES; + let r in 0 .. API_BENCHMARK_RUNS; let code_hashes = (0..r) .map(|i| { let new_code = WasmModule::::dummy_with_bytes(i); @@ -2146,7 +2152,7 @@ benchmarks! { #[pov_mode = Measured] seal_reentrance_count { - let r in 0 .. API_BENCHMARK_BATCHES; + let r in 0 .. API_BENCHMARK_RUNS; let code = WasmModule::::from(ModuleDefinition { memory: Some(ImportedMemory::max::()), imported_functions: vec![ImportedFunction { @@ -2167,7 +2173,7 @@ benchmarks! { #[pov_mode = Measured] seal_account_reentrance_count { - let r in 0 .. API_BENCHMARK_BATCHES; + let r in 0 .. API_BENCHMARK_RUNS; let dummy_code = WasmModule::::dummy_with_bytes(0); let accounts = (0..r) .map(|i| Contract::with_index(i + 1, dummy_code.clone(), vec![])) @@ -2201,7 +2207,7 @@ benchmarks! { #[pov_mode = Measured] seal_instantiation_nonce { - let r in 0 .. API_BENCHMARK_BATCHES; + let r in 0 .. API_BENCHMARK_RUNS; let code = WasmModule::::from(ModuleDefinition { memory: Some(ImportedMemory::max::()), imported_functions: vec![ImportedFunction { @@ -2232,7 +2238,7 @@ benchmarks! { // w_i{32,64}const = w_drop = w_bench / 2 #[pov_mode = Ignored] instr_i64const { - let r in 0 .. INSTR_BENCHMARK_BATCHES; + let r in 0 .. INSTR_BENCHMARK_RUNS; let mut sbox = Sandbox::from(&WasmModule::::from(ModuleDefinition { call_body: Some(body::repeated_dyn(r, vec![ RandomI64Repeated(1), @@ -2247,7 +2253,7 @@ benchmarks! { // w_i{32,64}load = w_bench - 2 * w_param #[pov_mode = Ignored] instr_i64load { - let r in 0 .. INSTR_BENCHMARK_BATCHES; + let r in 0 .. INSTR_BENCHMARK_RUNS; let mut sbox = Sandbox::from(&WasmModule::::from(ModuleDefinition { memory: Some(ImportedMemory::max::()), call_body: Some(body::repeated_dyn(r, vec![ @@ -2264,7 +2270,7 @@ benchmarks! { // w_i{32,64}store{...} = w_bench - 2 * w_param #[pov_mode = Ignored] instr_i64store { - let r in 0 .. INSTR_BENCHMARK_BATCHES; + let r in 0 .. INSTR_BENCHMARK_RUNS; let mut sbox = Sandbox::from(&WasmModule::::from(ModuleDefinition { memory: Some(ImportedMemory::max::()), call_body: Some(body::repeated_dyn(r, vec![ @@ -2281,7 +2287,7 @@ benchmarks! { // w_select = w_bench - 4 * w_param #[pov_mode = Ignored] instr_select { - let r in 0 .. INSTR_BENCHMARK_BATCHES; + let r in 0 .. INSTR_BENCHMARK_RUNS; let mut sbox = Sandbox::from(&WasmModule::::from(ModuleDefinition { call_body: Some(body::repeated_dyn(r, vec![ RandomI64Repeated(1), @@ -2299,7 +2305,7 @@ benchmarks! { // w_if = w_bench - 3 * w_param #[pov_mode = Ignored] instr_if { - let r in 0 .. INSTR_BENCHMARK_BATCHES; + let r in 0 .. INSTR_BENCHMARK_RUNS; let mut sbox = Sandbox::from(&WasmModule::::from(ModuleDefinition { call_body: Some(body::repeated_dyn(r, vec![ RandomI32(0, 2), @@ -2320,7 +2326,7 @@ benchmarks! { // Block instructions are not counted. #[pov_mode = Ignored] instr_br { - let r in 0 .. INSTR_BENCHMARK_BATCHES; + let r in 0 .. INSTR_BENCHMARK_RUNS; let mut sbox = Sandbox::from(&WasmModule::::from(ModuleDefinition { call_body: Some(body::repeated_dyn(r, vec![ Regular(Instruction::Block(BlockType::NoResult)), @@ -2347,7 +2353,7 @@ benchmarks! { // Block instructions are not counted. #[pov_mode = Ignored] instr_br_if { - let r in 0 .. INSTR_BENCHMARK_BATCHES; + let r in 0 .. INSTR_BENCHMARK_RUNS; let mut sbox = Sandbox::from(&WasmModule::::from(ModuleDefinition { call_body: Some(body::repeated_dyn(r, vec![ Regular(Instruction::Block(BlockType::NoResult)), @@ -2375,7 +2381,7 @@ benchmarks! { // Block instructions are not counted. #[pov_mode = Ignored] instr_br_table { - let r in 0 .. INSTR_BENCHMARK_BATCHES; + let r in 0 .. INSTR_BENCHMARK_RUNS; let table = Box::new(BrTableData { table: Box::new([1, 1, 1]), default: 1, @@ -2442,7 +2448,7 @@ benchmarks! { // w_call = w_bench - 2 * w_param #[pov_mode = Ignored] instr_call { - let r in 0 .. INSTR_BENCHMARK_BATCHES; + let r in 0 .. INSTR_BENCHMARK_RUNS; let mut sbox = Sandbox::from(&WasmModule::::from(ModuleDefinition { // We need to make use of the stack here in order to trigger stack height // instrumentation. @@ -2463,7 +2469,7 @@ benchmarks! { // w_call_indrect = w_bench - 3 * w_param #[pov_mode = Ignored] instr_call_indirect { - let r in 0 .. INSTR_BENCHMARK_BATCHES; + let r in 0 .. INSTR_BENCHMARK_RUNS; let num_elements = T::Schedule::get().limits.table_size; use self::code::TableSegment; let mut sbox = Sandbox::from(&WasmModule::::from(ModuleDefinition { @@ -2511,7 +2517,7 @@ benchmarks! { // w_local_get = w_bench - 1 * w_param #[pov_mode = Ignored] instr_local_get { - let r in 0 .. INSTR_BENCHMARK_BATCHES; + let r in 0 .. INSTR_BENCHMARK_RUNS; let max_locals = T::Schedule::get().limits.locals; let mut call_body = body::repeated_dyn(r, vec![ RandomGetLocal(0, max_locals), @@ -2529,7 +2535,7 @@ benchmarks! { // w_local_set = w_bench - 1 * w_param #[pov_mode = Ignored] instr_local_set { - let r in 0 .. INSTR_BENCHMARK_BATCHES; + let r in 0 .. INSTR_BENCHMARK_RUNS; let max_locals = T::Schedule::get().limits.locals; let mut call_body = body::repeated_dyn(r, vec![ RandomI64Repeated(1), @@ -2547,7 +2553,7 @@ benchmarks! { // w_local_tee = w_bench - 2 * w_param #[pov_mode = Ignored] instr_local_tee { - let r in 0 .. INSTR_BENCHMARK_BATCHES; + let r in 0 .. INSTR_BENCHMARK_RUNS; let max_locals = T::Schedule::get().limits.locals; let mut call_body = body::repeated_dyn(r, vec![ RandomI64Repeated(1), @@ -2566,7 +2572,7 @@ benchmarks! { // w_global_get = w_bench - 1 * w_param #[pov_mode = Ignored] instr_global_get { - let r in 0 .. INSTR_BENCHMARK_BATCHES; + let r in 0 .. INSTR_BENCHMARK_RUNS; let max_globals = T::Schedule::get().limits.globals; let mut sbox = Sandbox::from(&WasmModule::::from(ModuleDefinition { call_body: Some(body::repeated_dyn(r, vec![ @@ -2583,7 +2589,7 @@ benchmarks! { // w_global_set = w_bench - 1 * w_param #[pov_mode = Ignored] instr_global_set { - let r in 0 .. INSTR_BENCHMARK_BATCHES; + let r in 0 .. INSTR_BENCHMARK_RUNS; let max_globals = T::Schedule::get().limits.globals; let mut sbox = Sandbox::from(&WasmModule::::from(ModuleDefinition { call_body: Some(body::repeated_dyn(r, vec![ @@ -2600,7 +2606,7 @@ benchmarks! { // w_memory_get = w_bench - 1 * w_param #[pov_mode = Ignored] instr_memory_current { - let r in 0 .. INSTR_BENCHMARK_BATCHES; + let r in 0 .. INSTR_BENCHMARK_RUNS; let mut sbox = Sandbox::from(&WasmModule::::from(ModuleDefinition { memory: Some(ImportedMemory::max::()), call_body: Some(body::repeated(r, &[ @@ -2642,7 +2648,7 @@ benchmarks! { #[pov_mode = Ignored] instr_i64clz { - let r in 0 .. INSTR_BENCHMARK_BATCHES; + let r in 0 .. INSTR_BENCHMARK_RUNS; let mut sbox = Sandbox::from(&WasmModule::::unary_instr( Instruction::I64Clz, r, @@ -2653,7 +2659,7 @@ benchmarks! { #[pov_mode = Ignored] instr_i64ctz { - let r in 0 .. INSTR_BENCHMARK_BATCHES; + let r in 0 .. INSTR_BENCHMARK_RUNS; let mut sbox = Sandbox::from(&WasmModule::::unary_instr( Instruction::I64Ctz, r, @@ -2664,7 +2670,7 @@ benchmarks! { #[pov_mode = Ignored] instr_i64popcnt { - let r in 0 .. INSTR_BENCHMARK_BATCHES; + let r in 0 .. INSTR_BENCHMARK_RUNS; let mut sbox = Sandbox::from(&WasmModule::::unary_instr( Instruction::I64Popcnt, r, @@ -2675,7 +2681,7 @@ benchmarks! { #[pov_mode = Ignored] instr_i64eqz { - let r in 0 .. INSTR_BENCHMARK_BATCHES; + let r in 0 .. INSTR_BENCHMARK_RUNS; let mut sbox = Sandbox::from(&WasmModule::::unary_instr( Instruction::I64Eqz, r, @@ -2686,7 +2692,7 @@ benchmarks! { #[pov_mode = Ignored] instr_i64extendsi32 { - let r in 0 .. INSTR_BENCHMARK_BATCHES; + let r in 0 .. INSTR_BENCHMARK_RUNS; let mut sbox = Sandbox::from(&WasmModule::::from(ModuleDefinition { call_body: Some(body::repeated_dyn(r, vec![ RandomI32Repeated(1), @@ -2701,7 +2707,7 @@ benchmarks! { #[pov_mode = Ignored] instr_i64extendui32 { - let r in 0 .. INSTR_BENCHMARK_BATCHES; + let r in 0 .. INSTR_BENCHMARK_RUNS; let mut sbox = Sandbox::from(&WasmModule::::from(ModuleDefinition { call_body: Some(body::repeated_dyn(r, vec![ RandomI32Repeated(1), @@ -2716,7 +2722,7 @@ benchmarks! { #[pov_mode = Ignored] instr_i32wrapi64 { - let r in 0 .. INSTR_BENCHMARK_BATCHES; + let r in 0 .. INSTR_BENCHMARK_RUNS; let mut sbox = Sandbox::from(&WasmModule::::unary_instr( Instruction::I32WrapI64, r, @@ -2730,7 +2736,7 @@ benchmarks! { #[pov_mode = Ignored] instr_i64eq { - let r in 0 .. INSTR_BENCHMARK_BATCHES; + let r in 0 .. INSTR_BENCHMARK_RUNS; let mut sbox = Sandbox::from(&WasmModule::::binary_instr( Instruction::I64Eq, r, @@ -2741,7 +2747,7 @@ benchmarks! { #[pov_mode = Ignored] instr_i64ne { - let r in 0 .. INSTR_BENCHMARK_BATCHES; + let r in 0 .. INSTR_BENCHMARK_RUNS; let mut sbox = Sandbox::from(&WasmModule::::binary_instr( Instruction::I64Ne, r, @@ -2752,7 +2758,7 @@ benchmarks! { #[pov_mode = Ignored] instr_i64lts { - let r in 0 .. INSTR_BENCHMARK_BATCHES; + let r in 0 .. INSTR_BENCHMARK_RUNS; let mut sbox = Sandbox::from(&WasmModule::::binary_instr( Instruction::I64LtS, r, @@ -2763,7 +2769,7 @@ benchmarks! { #[pov_mode = Ignored] instr_i64ltu { - let r in 0 .. INSTR_BENCHMARK_BATCHES; + let r in 0 .. INSTR_BENCHMARK_RUNS; let mut sbox = Sandbox::from(&WasmModule::::binary_instr( Instruction::I64LtU, r, @@ -2774,7 +2780,7 @@ benchmarks! { #[pov_mode = Ignored] instr_i64gts { - let r in 0 .. INSTR_BENCHMARK_BATCHES; + let r in 0 .. INSTR_BENCHMARK_RUNS; let mut sbox = Sandbox::from(&WasmModule::::binary_instr( Instruction::I64GtS, r, @@ -2785,7 +2791,7 @@ benchmarks! { #[pov_mode = Ignored] instr_i64gtu { - let r in 0 .. INSTR_BENCHMARK_BATCHES; + let r in 0 .. INSTR_BENCHMARK_RUNS; let mut sbox = Sandbox::from(&WasmModule::::binary_instr( Instruction::I64GtU, r, @@ -2796,7 +2802,7 @@ benchmarks! { #[pov_mode = Ignored] instr_i64les { - let r in 0 .. INSTR_BENCHMARK_BATCHES; + let r in 0 .. INSTR_BENCHMARK_RUNS; let mut sbox = Sandbox::from(&WasmModule::::binary_instr( Instruction::I64LeS, r, @@ -2807,7 +2813,7 @@ benchmarks! { #[pov_mode = Ignored] instr_i64leu { - let r in 0 .. INSTR_BENCHMARK_BATCHES; + let r in 0 .. INSTR_BENCHMARK_RUNS; let mut sbox = Sandbox::from(&WasmModule::::binary_instr( Instruction::I64LeU, r, @@ -2818,7 +2824,7 @@ benchmarks! { #[pov_mode = Ignored] instr_i64ges { - let r in 0 .. INSTR_BENCHMARK_BATCHES; + let r in 0 .. INSTR_BENCHMARK_RUNS; let mut sbox = Sandbox::from(&WasmModule::::binary_instr( Instruction::I64GeS, r, @@ -2829,7 +2835,7 @@ benchmarks! { #[pov_mode = Ignored] instr_i64geu { - let r in 0 .. INSTR_BENCHMARK_BATCHES; + let r in 0 .. INSTR_BENCHMARK_RUNS; let mut sbox = Sandbox::from(&WasmModule::::binary_instr( Instruction::I64GeU, r, @@ -2840,7 +2846,7 @@ benchmarks! { #[pov_mode = Ignored] instr_i64add { - let r in 0 .. INSTR_BENCHMARK_BATCHES; + let r in 0 .. INSTR_BENCHMARK_RUNS; let mut sbox = Sandbox::from(&WasmModule::::binary_instr( Instruction::I64Add, r, @@ -2851,7 +2857,7 @@ benchmarks! { #[pov_mode = Ignored] instr_i64sub { - let r in 0 .. INSTR_BENCHMARK_BATCHES; + let r in 0 .. INSTR_BENCHMARK_RUNS; let mut sbox = Sandbox::from(&WasmModule::::binary_instr( Instruction::I64Sub, r, @@ -2862,7 +2868,7 @@ benchmarks! { #[pov_mode = Ignored] instr_i64mul { - let r in 0 .. INSTR_BENCHMARK_BATCHES; + let r in 0 .. INSTR_BENCHMARK_RUNS; let mut sbox = Sandbox::from(&WasmModule::::binary_instr( Instruction::I64Mul, r, @@ -2873,7 +2879,7 @@ benchmarks! { #[pov_mode = Ignored] instr_i64divs { - let r in 0 .. INSTR_BENCHMARK_BATCHES; + let r in 0 .. INSTR_BENCHMARK_RUNS; let mut sbox = Sandbox::from(&WasmModule::::binary_instr( Instruction::I64DivS, r, @@ -2884,7 +2890,7 @@ benchmarks! { #[pov_mode = Ignored] instr_i64divu { - let r in 0 .. INSTR_BENCHMARK_BATCHES; + let r in 0 .. INSTR_BENCHMARK_RUNS; let mut sbox = Sandbox::from(&WasmModule::::binary_instr( Instruction::I64DivU, r, @@ -2895,7 +2901,7 @@ benchmarks! { #[pov_mode = Ignored] instr_i64rems { - let r in 0 .. INSTR_BENCHMARK_BATCHES; + let r in 0 .. INSTR_BENCHMARK_RUNS; let mut sbox = Sandbox::from(&WasmModule::::binary_instr( Instruction::I64RemS, r, @@ -2906,7 +2912,7 @@ benchmarks! { #[pov_mode = Ignored] instr_i64remu { - let r in 0 .. INSTR_BENCHMARK_BATCHES; + let r in 0 .. INSTR_BENCHMARK_RUNS; let mut sbox = Sandbox::from(&WasmModule::::binary_instr( Instruction::I64RemU, r, @@ -2917,7 +2923,7 @@ benchmarks! { #[pov_mode = Ignored] instr_i64and { - let r in 0 .. INSTR_BENCHMARK_BATCHES; + let r in 0 .. INSTR_BENCHMARK_RUNS; let mut sbox = Sandbox::from(&WasmModule::::binary_instr( Instruction::I64And, r, @@ -2928,7 +2934,7 @@ benchmarks! { #[pov_mode = Ignored] instr_i64or { - let r in 0 .. INSTR_BENCHMARK_BATCHES; + let r in 0 .. INSTR_BENCHMARK_RUNS; let mut sbox = Sandbox::from(&WasmModule::::binary_instr( Instruction::I64Or, r, @@ -2939,7 +2945,7 @@ benchmarks! { #[pov_mode = Ignored] instr_i64xor { - let r in 0 .. INSTR_BENCHMARK_BATCHES; + let r in 0 .. INSTR_BENCHMARK_RUNS; let mut sbox = Sandbox::from(&WasmModule::::binary_instr( Instruction::I64Xor, r, @@ -2950,7 +2956,7 @@ benchmarks! { #[pov_mode = Ignored] instr_i64shl { - let r in 0 .. INSTR_BENCHMARK_BATCHES; + let r in 0 .. INSTR_BENCHMARK_RUNS; let mut sbox = Sandbox::from(&WasmModule::::binary_instr( Instruction::I64Shl, r, @@ -2961,7 +2967,7 @@ benchmarks! { #[pov_mode = Ignored] instr_i64shrs { - let r in 0 .. INSTR_BENCHMARK_BATCHES; + let r in 0 .. INSTR_BENCHMARK_RUNS; let mut sbox = Sandbox::from(&WasmModule::::binary_instr( Instruction::I64ShrS, r, @@ -2972,7 +2978,7 @@ benchmarks! { #[pov_mode = Ignored] instr_i64shru { - let r in 0 .. INSTR_BENCHMARK_BATCHES; + let r in 0 .. INSTR_BENCHMARK_RUNS; let mut sbox = Sandbox::from(&WasmModule::::binary_instr( Instruction::I64ShrU, r, @@ -2983,7 +2989,7 @@ benchmarks! { #[pov_mode = Ignored] instr_i64rotl { - let r in 0 .. INSTR_BENCHMARK_BATCHES; + let r in 0 .. INSTR_BENCHMARK_RUNS; let mut sbox = Sandbox::from(&WasmModule::::binary_instr( Instruction::I64Rotl, r, @@ -2994,7 +3000,7 @@ benchmarks! { #[pov_mode = Ignored] instr_i64rotr { - let r in 0 .. INSTR_BENCHMARK_BATCHES; + let r in 0 .. INSTR_BENCHMARK_RUNS; let mut sbox = Sandbox::from(&WasmModule::::binary_instr( Instruction::I64Rotr, r, diff --git a/frame/contracts/src/weights.rs b/frame/contracts/src/weights.rs index 4333f26c5..dfe6fb20d 100644 --- a/frame/contracts/src/weights.rs +++ b/frame/contracts/src/weights.rs @@ -18,7 +18,7 @@ //! Autogenerated weights for pallet_contracts //! //! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev -//! DATE: 2023-03-17, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! DATE: 2023-03-21, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` //! WORST CASE MAP SIZE: `1000000` //! HOSTNAME: `bm3`, CPU: `Intel(R) Core(TM) i7-7700K CPU @ 4.20GHz` //! EXECUTION: Some(Wasm), WASM-EXECUTION: Compiled, CHAIN: Some("dev"), DB CACHE: 1024 @@ -177,8 +177,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `109` // Estimated: `1594` - // Minimum execution time: 2_567_000 picoseconds. - Weight::from_parts(2_711_000, 1594) + // Minimum execution time: 2_585_000 picoseconds. + Weight::from_parts(2_802_000, 1594) .saturating_add(T::DbWeight::get().reads(1_u64)) } /// Storage: Skipped Metadata (r:0 w:0) @@ -188,10 +188,10 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `450 + k * (69 ±0)` // Estimated: `440 + k * (70 ±0)` - // Minimum execution time: 11_048_000 picoseconds. - Weight::from_parts(7_029_322, 440) - // Standard Error: 1_158 - .saturating_add(Weight::from_parts(975_010, 0).saturating_mul(k.into())) + // Minimum execution time: 10_825_000 picoseconds. + Weight::from_parts(5_747_064, 440) + // Standard Error: 1_037 + .saturating_add(Weight::from_parts(973_689, 0).saturating_mul(k.into())) .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(T::DbWeight::get().reads((1_u64).saturating_mul(k.into()))) .saturating_add(T::DbWeight::get().writes(1_u64)) @@ -205,10 +205,10 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `250 + q * (33 ±0)` // Estimated: `1725 + q * (33 ±0)` - // Minimum execution time: 2_645_000 picoseconds. - Weight::from_parts(10_865_477, 1725) - // Standard Error: 3_383 - .saturating_add(Weight::from_parts(1_295_351, 0).saturating_mul(q.into())) + // Minimum execution time: 2_718_000 picoseconds. + Weight::from_parts(11_436_305, 1725) + // Standard Error: 3_619 + .saturating_add(Weight::from_parts(1_296_955, 0).saturating_mul(q.into())) .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) .saturating_add(Weight::from_parts(0, 33).saturating_mul(q.into())) @@ -222,10 +222,10 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `238 + c * (1 ±0)` // Estimated: `3951 + c * (2 ±0)` - // Minimum execution time: 30_817_000 picoseconds. - Weight::from_parts(29_762_622, 3951) - // Standard Error: 62 - .saturating_add(Weight::from_parts(54_714, 0).saturating_mul(c.into())) + // Minimum execution time: 37_882_000 picoseconds. + Weight::from_parts(43_548_816, 3951) + // Standard Error: 49 + .saturating_add(Weight::from_parts(53_936, 0).saturating_mul(c.into())) .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) .saturating_add(Weight::from_parts(0, 2).saturating_mul(c.into())) @@ -245,10 +245,10 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `707` // Estimated: `21400 + c * (5 ±0)` - // Minimum execution time: 245_493_000 picoseconds. - Weight::from_parts(253_703_384, 21400) - // Standard Error: 27 - .saturating_add(Weight::from_parts(38_078, 0).saturating_mul(c.into())) + // Minimum execution time: 313_392_000 picoseconds. + Weight::from_parts(325_419_093, 21400) + // Standard Error: 25 + .saturating_add(Weight::from_parts(37_877, 0).saturating_mul(c.into())) .saturating_add(T::DbWeight::get().reads(6_u64)) .saturating_add(T::DbWeight::get().writes(4_u64)) .saturating_add(Weight::from_parts(0, 5).saturating_mul(c.into())) @@ -276,14 +276,14 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `270` // Estimated: `26207` - // Minimum execution time: 3_079_430_000 picoseconds. - Weight::from_parts(555_022_483, 26207) - // Standard Error: 290 - .saturating_add(Weight::from_parts(107_411, 0).saturating_mul(c.into())) - // Standard Error: 17 - .saturating_add(Weight::from_parts(1_123, 0).saturating_mul(i.into())) - // Standard Error: 17 - .saturating_add(Weight::from_parts(1_434, 0).saturating_mul(s.into())) + // Minimum execution time: 3_274_352_000 picoseconds. + Weight::from_parts(681_171_416, 26207) + // Standard Error: 336 + .saturating_add(Weight::from_parts(106_391, 0).saturating_mul(c.into())) + // Standard Error: 19 + .saturating_add(Weight::from_parts(1_147, 0).saturating_mul(i.into())) + // Standard Error: 19 + .saturating_add(Weight::from_parts(1_493, 0).saturating_mul(s.into())) .saturating_add(T::DbWeight::get().reads(9_u64)) .saturating_add(T::DbWeight::get().writes(10_u64)) } @@ -307,12 +307,12 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `482` // Estimated: `28521` - // Minimum execution time: 1_600_733_000 picoseconds. - Weight::from_parts(228_681_962, 28521) + // Minimum execution time: 1_692_637_000 picoseconds. + Weight::from_parts(283_252_265, 28521) // Standard Error: 8 - .saturating_add(Weight::from_parts(1_445, 0).saturating_mul(i.into())) + .saturating_add(Weight::from_parts(1_498, 0).saturating_mul(i.into())) // Standard Error: 8 - .saturating_add(Weight::from_parts(1_456, 0).saturating_mul(s.into())) + .saturating_add(Weight::from_parts(1_487, 0).saturating_mul(s.into())) .saturating_add(T::DbWeight::get().reads(9_u64)) .saturating_add(T::DbWeight::get().writes(7_u64)) } @@ -330,8 +330,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `759` // Estimated: `21615` - // Minimum execution time: 172_310_000 picoseconds. - Weight::from_parts(176_655_000, 21615) + // Minimum execution time: 192_369_000 picoseconds. + Weight::from_parts(193_417_000, 21615) .saturating_add(T::DbWeight::get().reads(6_u64)) .saturating_add(T::DbWeight::get().writes(4_u64)) } @@ -348,10 +348,10 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `109` // Estimated: `7366` - // Minimum execution time: 246_364_000 picoseconds. - Weight::from_parts(262_887_234, 7366) - // Standard Error: 90 - .saturating_add(Weight::from_parts(107_926, 0).saturating_mul(c.into())) + // Minimum execution time: 301_972_000 picoseconds. + Weight::from_parts(313_248_051, 7366) + // Standard Error: 82 + .saturating_add(Weight::from_parts(107_321, 0).saturating_mul(c.into())) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().writes(4_u64)) } @@ -367,8 +367,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `255` // Estimated: `7950` - // Minimum execution time: 29_226_000 picoseconds. - Weight::from_parts(29_542_000, 7950) + // Minimum execution time: 33_013_000 picoseconds. + Weight::from_parts(33_622_000, 7950) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().writes(4_u64)) } @@ -382,8 +382,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `570` // Estimated: `19530` - // Minimum execution time: 33_921_000 picoseconds. - Weight::from_parts(35_056_000, 19530) + // Minimum execution time: 33_518_000 picoseconds. + Weight::from_parts(33_819_000, 19530) .saturating_add(T::DbWeight::get().reads(6_u64)) .saturating_add(T::DbWeight::get().writes(6_u64)) } @@ -402,10 +402,10 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `781 + r * (6 ±0)` // Estimated: `21730 + r * (30 ±0)` - // Minimum execution time: 232_118_000 picoseconds. - Weight::from_parts(233_883_823, 21730) - // Standard Error: 642 - .saturating_add(Weight::from_parts(340_543, 0).saturating_mul(r.into())) + // Minimum execution time: 283_945_000 picoseconds. + Weight::from_parts(289_209_599, 21730) + // Standard Error: 901 + .saturating_add(Weight::from_parts(327_601, 0).saturating_mul(r.into())) .saturating_add(T::DbWeight::get().reads(6_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) .saturating_add(Weight::from_parts(0, 30).saturating_mul(r.into())) @@ -425,10 +425,10 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `839 + r * (240 ±0)` // Estimated: `21835 + r * (3675 ±0)` - // Minimum execution time: 231_712_000 picoseconds. - Weight::from_parts(74_297_097, 21835) - // Standard Error: 6_099 - .saturating_add(Weight::from_parts(3_322_192, 0).saturating_mul(r.into())) + // Minimum execution time: 284_949_000 picoseconds. + Weight::from_parts(126_196_485, 21835) + // Standard Error: 6_260 + .saturating_add(Weight::from_parts(3_368_849, 0).saturating_mul(r.into())) .saturating_add(T::DbWeight::get().reads(6_u64)) .saturating_add(T::DbWeight::get().reads((1_u64).saturating_mul(r.into()))) .saturating_add(T::DbWeight::get().writes(3_u64)) @@ -449,10 +449,10 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `831 + r * (244 ±0)` // Estimated: `21855 + r * (3695 ±0)` - // Minimum execution time: 233_122_000 picoseconds. - Weight::from_parts(74_369_473, 21855) - // Standard Error: 6_027 - .saturating_add(Weight::from_parts(4_177_187, 0).saturating_mul(r.into())) + // Minimum execution time: 286_429_000 picoseconds. + Weight::from_parts(124_820_396, 21855) + // Standard Error: 6_539 + .saturating_add(Weight::from_parts(4_163_535, 0).saturating_mul(r.into())) .saturating_add(T::DbWeight::get().reads(6_u64)) .saturating_add(T::DbWeight::get().reads((1_u64).saturating_mul(r.into()))) .saturating_add(T::DbWeight::get().writes(3_u64)) @@ -473,10 +473,10 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `788 + r * (6 ±0)` // Estimated: `21770 + r * (30 ±0)` - // Minimum execution time: 232_822_000 picoseconds. - Weight::from_parts(232_507_418, 21770) - // Standard Error: 961 - .saturating_add(Weight::from_parts(419_331, 0).saturating_mul(r.into())) + // Minimum execution time: 285_042_000 picoseconds. + Weight::from_parts(288_096_303, 21770) + // Standard Error: 972 + .saturating_add(Weight::from_parts(409_782, 0).saturating_mul(r.into())) .saturating_add(T::DbWeight::get().reads(6_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) .saturating_add(Weight::from_parts(0, 30).saturating_mul(r.into())) @@ -496,10 +496,10 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `778 + r * (3 ±0)` // Estimated: `21735 + r * (15 ±0)` - // Minimum execution time: 229_719_000 picoseconds. - Weight::from_parts(235_257_789, 21735) - // Standard Error: 451 - .saturating_add(Weight::from_parts(161_101, 0).saturating_mul(r.into())) + // Minimum execution time: 282_820_000 picoseconds. + Weight::from_parts(287_104_710, 21735) + // Standard Error: 421 + .saturating_add(Weight::from_parts(167_907, 0).saturating_mul(r.into())) .saturating_add(T::DbWeight::get().reads(6_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) .saturating_add(Weight::from_parts(0, 15).saturating_mul(r.into())) @@ -519,10 +519,10 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `782 + r * (6 ±0)` // Estimated: `21740 + r * (30 ±0)` - // Minimum execution time: 231_517_000 picoseconds. - Weight::from_parts(236_185_452, 21740) - // Standard Error: 651 - .saturating_add(Weight::from_parts(334_659, 0).saturating_mul(r.into())) + // Minimum execution time: 284_619_000 picoseconds. + Weight::from_parts(281_326_785, 21740) + // Standard Error: 1_379 + .saturating_add(Weight::from_parts(342_779, 0).saturating_mul(r.into())) .saturating_add(T::DbWeight::get().reads(6_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) .saturating_add(Weight::from_parts(0, 30).saturating_mul(r.into())) @@ -542,10 +542,10 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `783 + r * (6 ±0)` // Estimated: `21725 + r * (30 ±0)` - // Minimum execution time: 232_855_000 picoseconds. - Weight::from_parts(239_986_328, 21725) - // Standard Error: 4_075 - .saturating_add(Weight::from_parts(345_726, 0).saturating_mul(r.into())) + // Minimum execution time: 284_703_000 picoseconds. + Weight::from_parts(289_479_932, 21725) + // Standard Error: 745 + .saturating_add(Weight::from_parts(323_625, 0).saturating_mul(r.into())) .saturating_add(T::DbWeight::get().reads(6_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) .saturating_add(Weight::from_parts(0, 30).saturating_mul(r.into())) @@ -565,10 +565,10 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `922 + r * (6 ±0)` // Estimated: `24633 + r * (30 ±0)` - // Minimum execution time: 231_685_000 picoseconds. - Weight::from_parts(245_514_261, 24633) - // Standard Error: 1_635 - .saturating_add(Weight::from_parts(1_523_359, 0).saturating_mul(r.into())) + // Minimum execution time: 283_610_000 picoseconds. + Weight::from_parts(299_901_534, 24633) + // Standard Error: 1_177 + .saturating_add(Weight::from_parts(1_474_603, 0).saturating_mul(r.into())) .saturating_add(T::DbWeight::get().reads(7_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) .saturating_add(Weight::from_parts(0, 30).saturating_mul(r.into())) @@ -588,10 +588,10 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `792 + r * (6 ±0)` // Estimated: `21825 + r * (30 ±0)` - // Minimum execution time: 233_361_000 picoseconds. - Weight::from_parts(234_553_471, 21825) - // Standard Error: 1_108 - .saturating_add(Weight::from_parts(330_383, 0).saturating_mul(r.into())) + // Minimum execution time: 284_474_000 picoseconds. + Weight::from_parts(283_540_273, 21825) + // Standard Error: 1_164 + .saturating_add(Weight::from_parts(339_262, 0).saturating_mul(r.into())) .saturating_add(T::DbWeight::get().reads(6_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) .saturating_add(Weight::from_parts(0, 30).saturating_mul(r.into())) @@ -611,10 +611,10 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `790 + r * (6 ±0)` // Estimated: `21815 + r * (30 ±0)` - // Minimum execution time: 231_918_000 picoseconds. - Weight::from_parts(239_128_416, 21815) - // Standard Error: 1_473 - .saturating_add(Weight::from_parts(317_879, 0).saturating_mul(r.into())) + // Minimum execution time: 284_521_000 picoseconds. + Weight::from_parts(285_747_754, 21815) + // Standard Error: 889 + .saturating_add(Weight::from_parts(326_428, 0).saturating_mul(r.into())) .saturating_add(T::DbWeight::get().reads(6_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) .saturating_add(Weight::from_parts(0, 30).saturating_mul(r.into())) @@ -634,10 +634,10 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `787 + r * (6 ±0)` // Estimated: `21805 + r * (30 ±0)` - // Minimum execution time: 231_936_000 picoseconds. - Weight::from_parts(232_240_207, 21805) - // Standard Error: 731 - .saturating_add(Weight::from_parts(330_713, 0).saturating_mul(r.into())) + // Minimum execution time: 284_103_000 picoseconds. + Weight::from_parts(283_801_256, 21805) + // Standard Error: 1_051 + .saturating_add(Weight::from_parts(334_081, 0).saturating_mul(r.into())) .saturating_add(T::DbWeight::get().reads(6_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) .saturating_add(Weight::from_parts(0, 30).saturating_mul(r.into())) @@ -657,10 +657,10 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `778 + r * (6 ±0)` // Estimated: `21735 + r * (30 ±0)` - // Minimum execution time: 231_978_000 picoseconds. - Weight::from_parts(224_881_201, 21735) - // Standard Error: 1_296 - .saturating_add(Weight::from_parts(346_872, 0).saturating_mul(r.into())) + // Minimum execution time: 284_187_000 picoseconds. + Weight::from_parts(289_414_364, 21735) + // Standard Error: 796 + .saturating_add(Weight::from_parts(324_603, 0).saturating_mul(r.into())) .saturating_add(T::DbWeight::get().reads(6_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) .saturating_add(Weight::from_parts(0, 30).saturating_mul(r.into())) @@ -682,10 +682,10 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `856 + r * (10 ±0)` // Estimated: `24446 + r * (60 ±0)` - // Minimum execution time: 231_707_000 picoseconds. - Weight::from_parts(245_939_854, 24446) - // Standard Error: 1_369 - .saturating_add(Weight::from_parts(1_380_453, 0).saturating_mul(r.into())) + // Minimum execution time: 284_953_000 picoseconds. + Weight::from_parts(290_535_752, 24446) + // Standard Error: 2_462 + .saturating_add(Weight::from_parts(1_361_518, 0).saturating_mul(r.into())) .saturating_add(T::DbWeight::get().reads(7_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) .saturating_add(Weight::from_parts(0, 60).saturating_mul(r.into())) @@ -705,10 +705,10 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `745 + r * (4 ±0)` // Estimated: `21555 + r * (20 ±0)` - // Minimum execution time: 157_429_000 picoseconds. - Weight::from_parts(161_047_355, 21555) - // Standard Error: 1_220 - .saturating_add(Weight::from_parts(134_497, 0).saturating_mul(r.into())) + // Minimum execution time: 160_775_000 picoseconds. + Weight::from_parts(164_652_364, 21555) + // Standard Error: 284 + .saturating_add(Weight::from_parts(132_574, 0).saturating_mul(r.into())) .saturating_add(T::DbWeight::get().reads(6_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) .saturating_add(Weight::from_parts(0, 20).saturating_mul(r.into())) @@ -728,10 +728,10 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `780 + r * (6 ±0)` // Estimated: `21740 + r * (30 ±0)` - // Minimum execution time: 232_561_000 picoseconds. - Weight::from_parts(230_989_499, 21740) - // Standard Error: 848 - .saturating_add(Weight::from_parts(282_053, 0).saturating_mul(r.into())) + // Minimum execution time: 284_072_000 picoseconds. + Weight::from_parts(288_418_644, 21740) + // Standard Error: 792 + .saturating_add(Weight::from_parts(272_881, 0).saturating_mul(r.into())) .saturating_add(T::DbWeight::get().reads(6_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) .saturating_add(Weight::from_parts(0, 30).saturating_mul(r.into())) @@ -751,10 +751,10 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `784` // Estimated: `21740` - // Minimum execution time: 237_080_000 picoseconds. - Weight::from_parts(239_380_994, 21740) - // Standard Error: 2 - .saturating_add(Weight::from_parts(592, 0).saturating_mul(n.into())) + // Minimum execution time: 286_671_000 picoseconds. + Weight::from_parts(292_151_662, 21740) + // Standard Error: 1 + .saturating_add(Weight::from_parts(638, 0).saturating_mul(n.into())) .saturating_add(T::DbWeight::get().reads(6_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) } @@ -773,10 +773,10 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `768 + r * (45 ±0)` // Estimated: `21660 + r * (225 ±0)` - // Minimum execution time: 228_669_000 picoseconds. - Weight::from_parts(230_916_471, 21660) - // Standard Error: 295_520 - .saturating_add(Weight::from_parts(1_420_928, 0).saturating_mul(r.into())) + // Minimum execution time: 280_334_000 picoseconds. + Weight::from_parts(283_487_571, 21660) + // Standard Error: 267_797 + .saturating_add(Weight::from_parts(3_803_128, 0).saturating_mul(r.into())) .saturating_add(T::DbWeight::get().reads(6_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) .saturating_add(Weight::from_parts(0, 225).saturating_mul(r.into())) @@ -796,10 +796,10 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `778` // Estimated: `21775` - // Minimum execution time: 231_626_000 picoseconds. - Weight::from_parts(232_097_413, 21775) + // Minimum execution time: 284_004_000 picoseconds. + Weight::from_parts(283_681_350, 21775) // Standard Error: 1 - .saturating_add(Weight::from_parts(185, 0).saturating_mul(n.into())) + .saturating_add(Weight::from_parts(232, 0).saturating_mul(n.into())) .saturating_add(T::DbWeight::get().reads(6_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) } @@ -822,10 +822,10 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `810 + r * (356 ±0)` // Estimated: `25511 + r * (15321 ±0)` - // Minimum execution time: 230_758_000 picoseconds. - Weight::from_parts(233_235_993, 25511) - // Standard Error: 196_168 - .saturating_add(Weight::from_parts(81_177_506, 0).saturating_mul(r.into())) + // Minimum execution time: 284_143_000 picoseconds. + Weight::from_parts(287_218_324, 25511) + // Standard Error: 343_611 + .saturating_add(Weight::from_parts(109_895_675, 0).saturating_mul(r.into())) .saturating_add(T::DbWeight::get().reads(6_u64)) .saturating_add(T::DbWeight::get().reads((6_u64).saturating_mul(r.into()))) .saturating_add(T::DbWeight::get().writes(3_u64)) @@ -849,10 +849,10 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `825 + r * (10 ±0)` // Estimated: `24283 + r * (60 ±0)` - // Minimum execution time: 232_457_000 picoseconds. - Weight::from_parts(240_943_161, 24283) - // Standard Error: 3_085 - .saturating_add(Weight::from_parts(1_837_168, 0).saturating_mul(r.into())) + // Minimum execution time: 285_037_000 picoseconds. + Weight::from_parts(299_804_606, 24283) + // Standard Error: 5_518 + .saturating_add(Weight::from_parts(1_848_164, 0).saturating_mul(r.into())) .saturating_add(T::DbWeight::get().reads(7_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) .saturating_add(Weight::from_parts(0, 60).saturating_mul(r.into())) @@ -872,10 +872,10 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `778 + r * (10 ±0)` // Estimated: `21735 + r * (50 ±0)` - // Minimum execution time: 229_788_000 picoseconds. - Weight::from_parts(233_520_858, 21735) - // Standard Error: 9_561 - .saturating_add(Weight::from_parts(3_596_294, 0).saturating_mul(r.into())) + // Minimum execution time: 282_886_000 picoseconds. + Weight::from_parts(293_171_736, 21735) + // Standard Error: 2_171 + .saturating_add(Weight::from_parts(3_491_303, 0).saturating_mul(r.into())) .saturating_add(T::DbWeight::get().reads(6_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) .saturating_add(Weight::from_parts(0, 50).saturating_mul(r.into())) @@ -896,12 +896,12 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `797 + t * (32 ±0)` // Estimated: `21840 + t * (2640 ±0)` - // Minimum execution time: 248_294_000 picoseconds. - Weight::from_parts(239_709_759, 21840) - // Standard Error: 66_903 - .saturating_add(Weight::from_parts(2_880_732, 0).saturating_mul(t.into())) - // Standard Error: 18 - .saturating_add(Weight::from_parts(725, 0).saturating_mul(n.into())) + // Minimum execution time: 300_675_000 picoseconds. + Weight::from_parts(296_092_420, 21840) + // Standard Error: 130_733 + .saturating_add(Weight::from_parts(2_487_957, 0).saturating_mul(t.into())) + // Standard Error: 36 + .saturating_add(Weight::from_parts(565, 0).saturating_mul(n.into())) .saturating_add(T::DbWeight::get().reads(6_u64)) .saturating_add(T::DbWeight::get().reads((1_u64).saturating_mul(t.into()))) .saturating_add(T::DbWeight::get().writes(3_u64)) @@ -923,10 +923,10 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `777 + r * (7 ±0)` // Estimated: `21725 + r * (35 ±0)` - // Minimum execution time: 170_601_000 picoseconds. - Weight::from_parts(165_819_714, 21725) - // Standard Error: 452 - .saturating_add(Weight::from_parts(240_362, 0).saturating_mul(r.into())) + // Minimum execution time: 166_638_000 picoseconds. + Weight::from_parts(171_353_083, 21725) + // Standard Error: 550 + .saturating_add(Weight::from_parts(238_768, 0).saturating_mul(r.into())) .saturating_add(T::DbWeight::get().reads(6_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) .saturating_add(Weight::from_parts(0, 35).saturating_mul(r.into())) @@ -946,10 +946,10 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `125728` // Estimated: `269977` - // Minimum execution time: 349_543_000 picoseconds. - Weight::from_parts(352_945_102, 269977) - // Standard Error: 2 - .saturating_add(Weight::from_parts(749, 0).saturating_mul(i.into())) + // Minimum execution time: 414_136_000 picoseconds. + Weight::from_parts(416_093_921, 269977) + // Standard Error: 3 + .saturating_add(Weight::from_parts(794, 0).saturating_mul(i.into())) .saturating_add(T::DbWeight::get().reads(6_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) } @@ -960,10 +960,10 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `845 + r * (292 ±0)` // Estimated: `843 + r * (293 ±0)` - // Minimum execution time: 238_766_000 picoseconds. - Weight::from_parts(130_994_282, 843) - // Standard Error: 10_413 - .saturating_add(Weight::from_parts(6_134_087, 0).saturating_mul(r.into())) + // Minimum execution time: 285_920_000 picoseconds. + Weight::from_parts(184_945_789, 843) + // Standard Error: 9_604 + .saturating_add(Weight::from_parts(6_012_522, 0).saturating_mul(r.into())) .saturating_add(T::DbWeight::get().reads(6_u64)) .saturating_add(T::DbWeight::get().reads((1_u64).saturating_mul(r.into()))) .saturating_add(T::DbWeight::get().writes(3_u64)) @@ -977,10 +977,10 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `1304` // Estimated: `1280` - // Minimum execution time: 248_137_000 picoseconds. - Weight::from_parts(265_535_917, 1280) - // Standard Error: 28 - .saturating_add(Weight::from_parts(387, 0).saturating_mul(n.into())) + // Minimum execution time: 299_772_000 picoseconds. + Weight::from_parts(333_451_106, 1280) + // Standard Error: 54 + .saturating_add(Weight::from_parts(579, 0).saturating_mul(n.into())) .saturating_add(T::DbWeight::get().reads(8_u64)) .saturating_add(T::DbWeight::get().writes(6_u64)) } @@ -991,10 +991,10 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `1167 + n * (1 ±0)` // Estimated: `1167 + n * (1 ±0)` - // Minimum execution time: 246_752_000 picoseconds. - Weight::from_parts(250_315_210, 1167) - // Standard Error: 42 - .saturating_add(Weight::from_parts(135, 0).saturating_mul(n.into())) + // Minimum execution time: 299_279_000 picoseconds. + Weight::from_parts(302_336_567, 1167) + // Standard Error: 25 + .saturating_add(Weight::from_parts(86, 0).saturating_mul(n.into())) .saturating_add(T::DbWeight::get().reads(7_u64)) .saturating_add(T::DbWeight::get().writes(4_u64)) .saturating_add(Weight::from_parts(0, 1).saturating_mul(n.into())) @@ -1006,10 +1006,10 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `841 + r * (288 ±0)` // Estimated: `845 + r * (289 ±0)` - // Minimum execution time: 232_873_000 picoseconds. - Weight::from_parts(130_360_558, 845) - // Standard Error: 10_026 - .saturating_add(Weight::from_parts(6_023_862, 0).saturating_mul(r.into())) + // Minimum execution time: 284_689_000 picoseconds. + Weight::from_parts(185_207_302, 845) + // Standard Error: 10_030 + .saturating_add(Weight::from_parts(5_871_325, 0).saturating_mul(r.into())) .saturating_add(T::DbWeight::get().reads(6_u64)) .saturating_add(T::DbWeight::get().reads((1_u64).saturating_mul(r.into()))) .saturating_add(T::DbWeight::get().writes(3_u64)) @@ -1023,10 +1023,10 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `1163 + n * (1 ±0)` // Estimated: `1163 + n * (1 ±0)` - // Minimum execution time: 247_079_000 picoseconds. - Weight::from_parts(248_922_003, 1163) - // Standard Error: 22 - .saturating_add(Weight::from_parts(201, 0).saturating_mul(n.into())) + // Minimum execution time: 299_364_000 picoseconds. + Weight::from_parts(302_089_070, 1163) + // Standard Error: 23 + .saturating_add(Weight::from_parts(128, 0).saturating_mul(n.into())) .saturating_add(T::DbWeight::get().reads(7_u64)) .saturating_add(T::DbWeight::get().writes(4_u64)) .saturating_add(Weight::from_parts(0, 1).saturating_mul(n.into())) @@ -1038,10 +1038,10 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `835 + r * (296 ±0)` // Estimated: `840 + r * (297 ±0)` - // Minimum execution time: 232_696_000 picoseconds. - Weight::from_parts(144_147_151, 840) - // Standard Error: 8_846 - .saturating_add(Weight::from_parts(4_931_414, 0).saturating_mul(r.into())) + // Minimum execution time: 285_175_000 picoseconds. + Weight::from_parts(200_262_957, 840) + // Standard Error: 8_681 + .saturating_add(Weight::from_parts(4_899_266, 0).saturating_mul(r.into())) .saturating_add(T::DbWeight::get().reads(6_u64)) .saturating_add(T::DbWeight::get().reads((1_u64).saturating_mul(r.into()))) .saturating_add(T::DbWeight::get().writes(3_u64)) @@ -1054,10 +1054,10 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `1179 + n * (1 ±0)` // Estimated: `1179 + n * (1 ±0)` - // Minimum execution time: 247_795_000 picoseconds. - Weight::from_parts(252_944_118, 1179) - // Standard Error: 45 - .saturating_add(Weight::from_parts(480, 0).saturating_mul(n.into())) + // Minimum execution time: 299_459_000 picoseconds. + Weight::from_parts(302_451_160, 1179) + // Standard Error: 36 + .saturating_add(Weight::from_parts(731, 0).saturating_mul(n.into())) .saturating_add(T::DbWeight::get().reads(7_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) .saturating_add(Weight::from_parts(0, 1).saturating_mul(n.into())) @@ -1069,10 +1069,10 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `856 + r * (288 ±0)` // Estimated: `857 + r * (289 ±0)` - // Minimum execution time: 232_166_000 picoseconds. - Weight::from_parts(148_503_428, 857) - // Standard Error: 8_704 - .saturating_add(Weight::from_parts(4_766_055, 0).saturating_mul(r.into())) + // Minimum execution time: 286_384_000 picoseconds. + Weight::from_parts(203_389_467, 857) + // Standard Error: 8_817 + .saturating_add(Weight::from_parts(4_692_347, 0).saturating_mul(r.into())) .saturating_add(T::DbWeight::get().reads(6_u64)) .saturating_add(T::DbWeight::get().reads((1_u64).saturating_mul(r.into()))) .saturating_add(T::DbWeight::get().writes(3_u64)) @@ -1085,10 +1085,10 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `1166 + n * (1 ±0)` // Estimated: `1166 + n * (1 ±0)` - // Minimum execution time: 246_128_000 picoseconds. - Weight::from_parts(248_323_705, 1166) - // Standard Error: 21 - .saturating_add(Weight::from_parts(92, 0).saturating_mul(n.into())) + // Minimum execution time: 297_450_000 picoseconds. + Weight::from_parts(300_459_851, 1166) + // Standard Error: 39 + .saturating_add(Weight::from_parts(108, 0).saturating_mul(n.into())) .saturating_add(T::DbWeight::get().reads(7_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) .saturating_add(Weight::from_parts(0, 1).saturating_mul(n.into())) @@ -1100,10 +1100,10 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `829 + r * (296 ±0)` // Estimated: `836 + r * (297 ±0)` - // Minimum execution time: 232_976_000 picoseconds. - Weight::from_parts(132_373_848, 836) - // Standard Error: 9_855 - .saturating_add(Weight::from_parts(6_181_821, 0).saturating_mul(r.into())) + // Minimum execution time: 285_572_000 picoseconds. + Weight::from_parts(182_642_557, 836) + // Standard Error: 9_977 + .saturating_add(Weight::from_parts(6_090_684, 0).saturating_mul(r.into())) .saturating_add(T::DbWeight::get().reads(6_u64)) .saturating_add(T::DbWeight::get().reads((1_u64).saturating_mul(r.into()))) .saturating_add(T::DbWeight::get().writes(3_u64)) @@ -1117,10 +1117,10 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `1180 + n * (1 ±0)` // Estimated: `1180 + n * (1 ±0)` - // Minimum execution time: 247_907_000 picoseconds. - Weight::from_parts(251_984_133, 1180) - // Standard Error: 41 - .saturating_add(Weight::from_parts(639, 0).saturating_mul(n.into())) + // Minimum execution time: 301_344_000 picoseconds. + Weight::from_parts(303_770_522, 1180) + // Standard Error: 29 + .saturating_add(Weight::from_parts(807, 0).saturating_mul(n.into())) .saturating_add(T::DbWeight::get().reads(7_u64)) .saturating_add(T::DbWeight::get().writes(4_u64)) .saturating_add(Weight::from_parts(0, 1).saturating_mul(n.into())) @@ -1140,10 +1140,10 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `1373 + r * (45 ±0)` // Estimated: `26753 + r * (2700 ±0)` - // Minimum execution time: 232_614_000 picoseconds. - Weight::from_parts(127_024_367, 26753) - // Standard Error: 54_521 - .saturating_add(Weight::from_parts(20_975_575, 0).saturating_mul(r.into())) + // Minimum execution time: 286_835_000 picoseconds. + Weight::from_parts(245_206_457, 26753) + // Standard Error: 73_782 + .saturating_add(Weight::from_parts(36_414_448, 0).saturating_mul(r.into())) .saturating_add(T::DbWeight::get().reads(7_u64)) .saturating_add(T::DbWeight::get().reads((1_u64).saturating_mul(r.into()))) .saturating_add(T::DbWeight::get().writes(4_u64)) @@ -1152,53 +1152,53 @@ impl WeightInfo for SubstrateWeight { } /// Storage: System Account (r:1 w:0) /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: Measured) - /// Storage: Contracts ContractInfoOf (r:1601 w:1601) + /// Storage: Contracts ContractInfoOf (r:801 w:801) /// Proof: Contracts ContractInfoOf (max_values: None, max_size: Some(290), added: 2765, mode: Measured) /// Storage: Contracts CodeStorage (r:2 w:0) /// Proof: Contracts CodeStorage (max_values: None, max_size: Some(126001), added: 128476, mode: Measured) /// Storage: Timestamp Now (r:1 w:0) /// Proof: Timestamp Now (max_values: Some(1), max_size: Some(8), added: 503, mode: Measured) - /// Storage: System EventTopics (r:1602 w:1602) + /// Storage: System EventTopics (r:802 w:802) /// Proof Skipped: System EventTopics (max_values: None, max_size: None, mode: Measured) - /// The range of component `r` is `[0, 1600]`. + /// The range of component `r` is `[0, 800]`. fn seal_call(r: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `1512 + r * (256 ±0)` - // Estimated: `27163 + r * (6231 ±0)` - // Minimum execution time: 233_446_000 picoseconds. - Weight::from_parts(234_338_000, 27163) - // Standard Error: 122_754 - .saturating_add(Weight::from_parts(216_709_600, 0).saturating_mul(r.into())) + // Measured: `1237 + r * (256 ±0)` + // Estimated: `26028 + r * (6235 ±0)` + // Minimum execution time: 287_184_000 picoseconds. + Weight::from_parts(287_525_000, 26028) + // Standard Error: 66_791 + .saturating_add(Weight::from_parts(261_473_539, 0).saturating_mul(r.into())) .saturating_add(T::DbWeight::get().reads(7_u64)) .saturating_add(T::DbWeight::get().reads((2_u64).saturating_mul(r.into()))) .saturating_add(T::DbWeight::get().writes(3_u64)) .saturating_add(T::DbWeight::get().writes((2_u64).saturating_mul(r.into()))) - .saturating_add(Weight::from_parts(0, 6231).saturating_mul(r.into())) + .saturating_add(Weight::from_parts(0, 6235).saturating_mul(r.into())) } /// Storage: System Account (r:1 w:0) /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: Measured) /// Storage: Contracts ContractInfoOf (r:1 w:1) /// Proof: Contracts ContractInfoOf (max_values: None, max_size: Some(290), added: 2765, mode: Measured) - /// Storage: Contracts CodeStorage (r:1536 w:0) + /// Storage: Contracts CodeStorage (r:736 w:0) /// Proof: Contracts CodeStorage (max_values: None, max_size: Some(126001), added: 128476, mode: Measured) /// Storage: Timestamp Now (r:1 w:0) /// Proof: Timestamp Now (max_values: Some(1), max_size: Some(8), added: 503, mode: Measured) - /// Storage: System EventTopics (r:1537 w:1537) + /// Storage: System EventTopics (r:737 w:737) /// Proof Skipped: System EventTopics (max_values: None, max_size: None, mode: Measured) - /// The range of component `r` is `[0, 1600]`. + /// The range of component `r` is `[0, 800]`. fn seal_delegate_call(r: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `0 + r * (902 ±0)` - // Estimated: `21755 + r * (8167 ±7)` - // Minimum execution time: 232_717_000 picoseconds. - Weight::from_parts(233_405_000, 21755) - // Standard Error: 98_607 - .saturating_add(Weight::from_parts(212_329_100, 0).saturating_mul(r.into())) + // Measured: `0 + r * (502 ±0)` + // Estimated: `21755 + r * (6329 ±10)` + // Minimum execution time: 285_759_000 picoseconds. + Weight::from_parts(286_643_000, 21755) + // Standard Error: 133_180 + .saturating_add(Weight::from_parts(257_186_897, 0).saturating_mul(r.into())) .saturating_add(T::DbWeight::get().reads(6_u64)) .saturating_add(T::DbWeight::get().reads((2_u64).saturating_mul(r.into()))) .saturating_add(T::DbWeight::get().writes(3_u64)) .saturating_add(T::DbWeight::get().writes((1_u64).saturating_mul(r.into()))) - .saturating_add(Weight::from_parts(0, 8167).saturating_mul(r.into())) + .saturating_add(Weight::from_parts(0, 6329).saturating_mul(r.into())) } /// Storage: System Account (r:3 w:2) /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: Measured) @@ -1216,46 +1216,46 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `1154 + t * (204 ±0)` // Estimated: `31015 + t * (5970 ±0)` - // Minimum execution time: 392_113_000 picoseconds. - Weight::from_parts(376_522_702, 31015) - // Standard Error: 1_388_011 - .saturating_add(Weight::from_parts(19_125_093, 0).saturating_mul(t.into())) - // Standard Error: 2 - .saturating_add(Weight::from_parts(609, 0).saturating_mul(c.into())) + // Minimum execution time: 459_675_000 picoseconds. + Weight::from_parts(427_010_987, 31015) + // Standard Error: 1_277_377 + .saturating_add(Weight::from_parts(36_899_889, 0).saturating_mul(t.into())) + // Standard Error: 1 + .saturating_add(Weight::from_parts(651, 0).saturating_mul(c.into())) .saturating_add(T::DbWeight::get().reads(9_u64)) .saturating_add(T::DbWeight::get().reads((2_u64).saturating_mul(t.into()))) .saturating_add(T::DbWeight::get().writes(5_u64)) .saturating_add(T::DbWeight::get().writes((2_u64).saturating_mul(t.into()))) .saturating_add(Weight::from_parts(0, 5970).saturating_mul(t.into())) } - /// Storage: System Account (r:3202 w:3202) + /// Storage: System Account (r:1602 w:1602) /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: Measured) - /// Storage: Contracts ContractInfoOf (r:1601 w:1601) + /// Storage: Contracts ContractInfoOf (r:801 w:801) /// Proof: Contracts ContractInfoOf (max_values: None, max_size: Some(290), added: 2765, mode: Measured) - /// Storage: Contracts CodeStorage (r:1601 w:0) + /// Storage: Contracts CodeStorage (r:801 w:0) /// Proof: Contracts CodeStorage (max_values: None, max_size: Some(126001), added: 128476, mode: Measured) /// Storage: Timestamp Now (r:1 w:0) /// Proof: Timestamp Now (max_values: Some(1), max_size: Some(8), added: 503, mode: Measured) /// Storage: Contracts Nonce (r:1 w:1) /// Proof: Contracts Nonce (max_values: Some(1), max_size: Some(8), added: 503, mode: Measured) - /// Storage: Contracts OwnerInfoOf (r:1600 w:1600) + /// Storage: Contracts OwnerInfoOf (r:800 w:800) /// Proof: Contracts OwnerInfoOf (max_values: None, max_size: Some(88), added: 2563, mode: Measured) - /// Storage: System EventTopics (r:1602 w:1602) + /// Storage: System EventTopics (r:802 w:802) /// Proof Skipped: System EventTopics (max_values: None, max_size: None, mode: Measured) - /// The range of component `r` is `[0, 1600]`. + /// The range of component `r` is `[0, 800]`. fn seal_instantiate(r: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `1859 + r * (253 ±0)` - // Estimated: `33859 + r * (16628 ±0)` - // Minimum execution time: 234_438_000 picoseconds. - Weight::from_parts(234_751_000, 33859) - // Standard Error: 450_507 - .saturating_add(Weight::from_parts(313_121_404, 0).saturating_mul(r.into())) + // Measured: `1301 + r * (254 ±0)` + // Estimated: `30977 + r * (16635 ±0)` + // Minimum execution time: 285_816_000 picoseconds. + Weight::from_parts(286_349_000, 30977) + // Standard Error: 269_144 + .saturating_add(Weight::from_parts(394_282_520, 0).saturating_mul(r.into())) .saturating_add(T::DbWeight::get().reads(8_u64)) .saturating_add(T::DbWeight::get().reads((6_u64).saturating_mul(r.into()))) .saturating_add(T::DbWeight::get().writes(6_u64)) .saturating_add(T::DbWeight::get().writes((5_u64).saturating_mul(r.into()))) - .saturating_add(Weight::from_parts(0, 16628).saturating_mul(r.into())) + .saturating_add(Weight::from_parts(0, 16635).saturating_mul(r.into())) } /// Storage: System Account (r:4 w:4) /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: Measured) @@ -1278,14 +1278,14 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `1071 + t * (187 ±0)` // Estimated: `42684 + t * (3588 ±2)` - // Minimum execution time: 1_570_178_000 picoseconds. - Weight::from_parts(284_062_841, 42684) - // Standard Error: 4_396_597 - .saturating_add(Weight::from_parts(106_424_960, 0).saturating_mul(t.into())) + // Minimum execution time: 1_708_330_000 picoseconds. + Weight::from_parts(395_059_764, 42684) + // Standard Error: 4_545_552 + .saturating_add(Weight::from_parts(114_039_862, 0).saturating_mul(t.into())) // Standard Error: 7 - .saturating_add(Weight::from_parts(1_183, 0).saturating_mul(i.into())) + .saturating_add(Weight::from_parts(1_213, 0).saturating_mul(i.into())) // Standard Error: 7 - .saturating_add(Weight::from_parts(1_354, 0).saturating_mul(s.into())) + .saturating_add(Weight::from_parts(1_379, 0).saturating_mul(s.into())) .saturating_add(T::DbWeight::get().reads(13_u64)) .saturating_add(T::DbWeight::get().reads((1_u64).saturating_mul(t.into()))) .saturating_add(T::DbWeight::get().writes(10_u64)) @@ -1307,10 +1307,10 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `777 + r * (8 ±0)` // Estimated: `21710 + r * (40 ±0)` - // Minimum execution time: 230_557_000 picoseconds. - Weight::from_parts(233_276_698, 21710) - // Standard Error: 1_026 - .saturating_add(Weight::from_parts(587_723, 0).saturating_mul(r.into())) + // Minimum execution time: 283_738_000 picoseconds. + Weight::from_parts(289_885_978, 21710) + // Standard Error: 1_057 + .saturating_add(Weight::from_parts(575_432, 0).saturating_mul(r.into())) .saturating_add(T::DbWeight::get().reads(6_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) .saturating_add(Weight::from_parts(0, 40).saturating_mul(r.into())) @@ -1330,10 +1330,10 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `785` // Estimated: `21745` - // Minimum execution time: 234_151_000 picoseconds. - Weight::from_parts(240_199_365, 21745) - // Standard Error: 3 - .saturating_add(Weight::from_parts(3_941, 0).saturating_mul(n.into())) + // Minimum execution time: 285_070_000 picoseconds. + Weight::from_parts(283_987_687, 21745) + // Standard Error: 6 + .saturating_add(Weight::from_parts(4_008, 0).saturating_mul(n.into())) .saturating_add(T::DbWeight::get().reads(6_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) } @@ -1352,10 +1352,10 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `779 + r * (8 ±0)` // Estimated: `21725 + r * (40 ±0)` - // Minimum execution time: 229_931_000 picoseconds. - Weight::from_parts(234_961_690, 21725) - // Standard Error: 1_073 - .saturating_add(Weight::from_parts(758_259, 0).saturating_mul(r.into())) + // Minimum execution time: 281_613_000 picoseconds. + Weight::from_parts(285_429_053, 21725) + // Standard Error: 1_164 + .saturating_add(Weight::from_parts(756_244, 0).saturating_mul(r.into())) .saturating_add(T::DbWeight::get().reads(6_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) .saturating_add(Weight::from_parts(0, 40).saturating_mul(r.into())) @@ -1375,10 +1375,10 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `787` // Estimated: `21765` - // Minimum execution time: 232_448_000 picoseconds. - Weight::from_parts(225_974_008, 21765) + // Minimum execution time: 284_593_000 picoseconds. + Weight::from_parts(278_467_111, 21765) // Standard Error: 2 - .saturating_add(Weight::from_parts(3_174, 0).saturating_mul(n.into())) + .saturating_add(Weight::from_parts(3_217, 0).saturating_mul(n.into())) .saturating_add(T::DbWeight::get().reads(6_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) } @@ -1397,10 +1397,10 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `779 + r * (8 ±0)` // Estimated: `21740 + r * (40 ±0)` - // Minimum execution time: 229_251_000 picoseconds. - Weight::from_parts(234_182_627, 21740) - // Standard Error: 836 - .saturating_add(Weight::from_parts(424_375, 0).saturating_mul(r.into())) + // Minimum execution time: 281_759_000 picoseconds. + Weight::from_parts(288_807_137, 21740) + // Standard Error: 805 + .saturating_add(Weight::from_parts(424_378, 0).saturating_mul(r.into())) .saturating_add(T::DbWeight::get().reads(6_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) .saturating_add(Weight::from_parts(0, 40).saturating_mul(r.into())) @@ -1420,10 +1420,10 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `787` // Estimated: `21785` - // Minimum execution time: 230_896_000 picoseconds. - Weight::from_parts(224_913_556, 21785) + // Minimum execution time: 282_666_000 picoseconds. + Weight::from_parts(274_357_944, 21785) // Standard Error: 2 - .saturating_add(Weight::from_parts(919, 0).saturating_mul(n.into())) + .saturating_add(Weight::from_parts(974, 0).saturating_mul(n.into())) .saturating_add(T::DbWeight::get().reads(6_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) } @@ -1442,10 +1442,10 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `779 + r * (8 ±0)` // Estimated: `21745 + r * (40 ±0)` - // Minimum execution time: 230_314_000 picoseconds. - Weight::from_parts(233_426_327, 21745) - // Standard Error: 926 - .saturating_add(Weight::from_parts(425_341, 0).saturating_mul(r.into())) + // Minimum execution time: 285_073_000 picoseconds. + Weight::from_parts(287_226_796, 21745) + // Standard Error: 951 + .saturating_add(Weight::from_parts(425_368, 0).saturating_mul(r.into())) .saturating_add(T::DbWeight::get().reads(6_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) .saturating_add(Weight::from_parts(0, 40).saturating_mul(r.into())) @@ -1465,10 +1465,10 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `787` // Estimated: `21755` - // Minimum execution time: 230_424_000 picoseconds. - Weight::from_parts(231_136_218, 21755) + // Minimum execution time: 283_407_000 picoseconds. + Weight::from_parts(276_737_242, 21755) // Standard Error: 2 - .saturating_add(Weight::from_parts(917, 0).saturating_mul(n.into())) + .saturating_add(Weight::from_parts(967, 0).saturating_mul(n.into())) .saturating_add(T::DbWeight::get().reads(6_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) } @@ -1482,18 +1482,18 @@ impl WeightInfo for SubstrateWeight { /// Proof: Timestamp Now (max_values: Some(1), max_size: Some(8), added: 503, mode: Measured) /// Storage: System EventTopics (r:2 w:2) /// Proof Skipped: System EventTopics (max_values: None, max_size: None, mode: Measured) - /// The range of component `r` is `[0, 1600]`. + /// The range of component `r` is `[0, 160]`. fn seal_ecdsa_recover(r: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `704 + r * (78 ±0)` - // Estimated: `21370 + r * (390 ±0)` - // Minimum execution time: 232_862_000 picoseconds. - Weight::from_parts(379_152_870, 21370) - // Standard Error: 11_607 - .saturating_add(Weight::from_parts(36_955_410, 0).saturating_mul(r.into())) + // Measured: `822 + r * (76 ±0)` + // Estimated: `21705 + r * (385 ±0)` + // Minimum execution time: 285_130_000 picoseconds. + Weight::from_parts(299_449_202, 21705) + // Standard Error: 16_535 + .saturating_add(Weight::from_parts(37_655_189, 0).saturating_mul(r.into())) .saturating_add(T::DbWeight::get().reads(6_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) - .saturating_add(Weight::from_parts(0, 390).saturating_mul(r.into())) + .saturating_add(Weight::from_parts(0, 385).saturating_mul(r.into())) } /// Storage: System Account (r:1 w:0) /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: Measured) @@ -1505,18 +1505,18 @@ impl WeightInfo for SubstrateWeight { /// Proof: Timestamp Now (max_values: Some(1), max_size: Some(8), added: 503, mode: Measured) /// Storage: System EventTopics (r:2 w:2) /// Proof Skipped: System EventTopics (max_values: None, max_size: None, mode: Measured) - /// The range of component `r` is `[0, 1600]`. + /// The range of component `r` is `[0, 160]`. fn seal_ecdsa_to_eth_address(r: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `549 + r * (44 ±0)` - // Estimated: `20630 + r * (220 ±0)` - // Minimum execution time: 232_668_000 picoseconds. - Weight::from_parts(253_308_468, 20630) - // Standard Error: 3_022 - .saturating_add(Weight::from_parts(9_249_849, 0).saturating_mul(r.into())) + // Measured: `792 + r * (42 ±0)` + // Estimated: `21780 + r * (210 ±0)` + // Minimum execution time: 284_494_000 picoseconds. + Weight::from_parts(282_154_339, 21780) + // Standard Error: 12_278 + .saturating_add(Weight::from_parts(9_501_559, 0).saturating_mul(r.into())) .saturating_add(T::DbWeight::get().reads(6_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) - .saturating_add(Weight::from_parts(0, 220).saturating_mul(r.into())) + .saturating_add(Weight::from_parts(0, 210).saturating_mul(r.into())) } /// Storage: System Account (r:1 w:0) /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: Measured) @@ -1535,10 +1535,10 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `0 + r * (964 ±0)` // Estimated: `29920 + r * (11544 ±7)` - // Minimum execution time: 233_910_000 picoseconds. - Weight::from_parts(234_607_000, 29920) - // Standard Error: 46_118 - .saturating_add(Weight::from_parts(21_927_957, 0).saturating_mul(r.into())) + // Minimum execution time: 285_306_000 picoseconds. + Weight::from_parts(286_080_000, 29920) + // Standard Error: 43_813 + .saturating_add(Weight::from_parts(21_758_329, 0).saturating_mul(r.into())) .saturating_add(T::DbWeight::get().reads(6_u64)) .saturating_add(T::DbWeight::get().reads((3_u64).saturating_mul(r.into()))) .saturating_add(T::DbWeight::get().writes(3_u64)) @@ -1560,10 +1560,10 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `773 + r * (3 ±0)` // Estimated: `21735 + r * (15 ±0)` - // Minimum execution time: 231_823_000 picoseconds. - Weight::from_parts(237_996_137, 21735) - // Standard Error: 598 - .saturating_add(Weight::from_parts(163_106, 0).saturating_mul(r.into())) + // Minimum execution time: 283_487_000 picoseconds. + Weight::from_parts(289_280_189, 21735) + // Standard Error: 829 + .saturating_add(Weight::from_parts(168_973, 0).saturating_mul(r.into())) .saturating_add(T::DbWeight::get().reads(6_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) .saturating_add(Weight::from_parts(0, 15).saturating_mul(r.into())) @@ -1583,10 +1583,10 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `1975 + r * (39 ±0)` // Estimated: `27145 + r * (200 ±0)` - // Minimum execution time: 235_077_000 picoseconds. - Weight::from_parts(267_876_360, 27145) - // Standard Error: 1_147 - .saturating_add(Weight::from_parts(256_275, 0).saturating_mul(r.into())) + // Minimum execution time: 287_413_000 picoseconds. + Weight::from_parts(314_662_286, 27145) + // Standard Error: 1_099 + .saturating_add(Weight::from_parts(262_201, 0).saturating_mul(r.into())) .saturating_add(T::DbWeight::get().reads(6_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) .saturating_add(Weight::from_parts(0, 200).saturating_mul(r.into())) @@ -1608,10 +1608,10 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `776 + r * (3 ±0)` // Estimated: `24004 + r * (18 ±0)` - // Minimum execution time: 231_384_000 picoseconds. - Weight::from_parts(237_086_900, 24004) - // Standard Error: 534 - .saturating_add(Weight::from_parts(140_842, 0).saturating_mul(r.into())) + // Minimum execution time: 282_601_000 picoseconds. + Weight::from_parts(289_374_203, 24004) + // Standard Error: 452 + .saturating_add(Weight::from_parts(142_661, 0).saturating_mul(r.into())) .saturating_add(T::DbWeight::get().reads(7_u64)) .saturating_add(T::DbWeight::get().writes(4_u64)) .saturating_add(Weight::from_parts(0, 18).saturating_mul(r.into())) @@ -1621,510 +1621,508 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_721_000 picoseconds. - Weight::from_parts(2_456_746, 0) - // Standard Error: 30 - .saturating_add(Weight::from_parts(2_788, 0).saturating_mul(r.into())) + // Minimum execution time: 1_692_000 picoseconds. + Weight::from_parts(2_069_482, 0) + // Standard Error: 40 + .saturating_add(Weight::from_parts(2_922, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 5000]`. fn instr_i64load(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_824_000 picoseconds. - Weight::from_parts(2_366_036, 0) - // Standard Error: 5 - .saturating_add(Weight::from_parts(6_446, 0).saturating_mul(r.into())) + // Minimum execution time: 1_690_000 picoseconds. + Weight::from_parts(2_303_602, 0) + // Standard Error: 4 + .saturating_add(Weight::from_parts(6_433, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 5000]`. fn instr_i64store(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_840_000 picoseconds. - Weight::from_parts(2_332_115, 0) + // Minimum execution time: 1_701_000 picoseconds. + Weight::from_parts(2_321_142, 0) // Standard Error: 4 - .saturating_add(Weight::from_parts(6_012, 0).saturating_mul(r.into())) + .saturating_add(Weight::from_parts(6_025, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 5000]`. fn instr_select(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_731_000 picoseconds. - Weight::from_parts(2_078_873, 0) - // Standard Error: 3 - .saturating_add(Weight::from_parts(7_931, 0).saturating_mul(r.into())) + // Minimum execution time: 1_656_000 picoseconds. + Weight::from_parts(2_090_881, 0) + // Standard Error: 5 + .saturating_add(Weight::from_parts(7_941, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 5000]`. fn instr_if(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_768_000 picoseconds. - Weight::from_parts(1_916_551, 0) - // Standard Error: 8 - .saturating_add(Weight::from_parts(10_597, 0).saturating_mul(r.into())) + // Minimum execution time: 1_659_000 picoseconds. + Weight::from_parts(1_816_547, 0) + // Standard Error: 5 + .saturating_add(Weight::from_parts(10_578, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 5000]`. fn instr_br(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_735_000 picoseconds. - Weight::from_parts(1_962_845, 0) - // Standard Error: 14 - .saturating_add(Weight::from_parts(4_631, 0).saturating_mul(r.into())) + // Minimum execution time: 1_683_000 picoseconds. + Weight::from_parts(1_970_907, 0) + // Standard Error: 12 + .saturating_add(Weight::from_parts(4_636, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 5000]`. fn instr_br_if(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_688_000 picoseconds. - Weight::from_parts(1_172_825, 0) - // Standard Error: 76 - .saturating_add(Weight::from_parts(8_007, 0).saturating_mul(r.into())) + // Minimum execution time: 1_651_000 picoseconds. + Weight::from_parts(2_263_817, 0) + // Standard Error: 68 + .saturating_add(Weight::from_parts(7_529, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 5000]`. fn instr_br_table(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_725_000 picoseconds. - Weight::from_parts(1_535_833, 0) - // Standard Error: 26 - .saturating_add(Weight::from_parts(9_489, 0).saturating_mul(r.into())) + // Minimum execution time: 1_687_000 picoseconds. + Weight::from_parts(1_349_186, 0) + // Standard Error: 31 + .saturating_add(Weight::from_parts(9_732, 0).saturating_mul(r.into())) } /// The range of component `e` is `[1, 256]`. - fn instr_br_table_per_entry(e: u32, ) -> Weight { + fn instr_br_table_per_entry(_e: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_814_000 picoseconds. - Weight::from_parts(1_926_367, 0) - // Standard Error: 16 - .saturating_add(Weight::from_parts(219, 0).saturating_mul(e.into())) + // Minimum execution time: 1_777_000 picoseconds. + Weight::from_parts(2_036_446, 0) } /// The range of component `r` is `[0, 5000]`. fn instr_call(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_770_000 picoseconds. - Weight::from_parts(2_436_918, 0) - // Standard Error: 8 - .saturating_add(Weight::from_parts(17_890, 0).saturating_mul(r.into())) + // Minimum execution time: 1_686_000 picoseconds. + Weight::from_parts(464_449, 0) + // Standard Error: 383 + .saturating_add(Weight::from_parts(19_121, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 5000]`. fn instr_call_indirect(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_920_000 picoseconds. - Weight::from_parts(2_729_712, 0) - // Standard Error: 39 - .saturating_add(Weight::from_parts(24_853, 0).saturating_mul(r.into())) + // Minimum execution time: 1_855_000 picoseconds. + Weight::from_parts(3_381_585, 0) + // Standard Error: 18 + .saturating_add(Weight::from_parts(24_245, 0).saturating_mul(r.into())) } /// The range of component `l` is `[0, 1024]`. fn instr_call_per_local(l: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_869_000 picoseconds. - Weight::from_parts(2_147_970, 0) - // Standard Error: 46 - .saturating_add(Weight::from_parts(1_237, 0).saturating_mul(l.into())) + // Minimum execution time: 1_792_000 picoseconds. + Weight::from_parts(2_006_024, 0) + // Standard Error: 16 + .saturating_add(Weight::from_parts(2_181, 0).saturating_mul(l.into())) } /// The range of component `r` is `[0, 5000]`. fn instr_local_get(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 3_012_000 picoseconds. - Weight::from_parts(3_234_440, 0) - // Standard Error: 1 - .saturating_add(Weight::from_parts(2_433, 0).saturating_mul(r.into())) + // Minimum execution time: 3_918_000 picoseconds. + Weight::from_parts(4_618_761, 0) + // Standard Error: 49 + .saturating_add(Weight::from_parts(2_312, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 5000]`. fn instr_local_set(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 3_037_000 picoseconds. - Weight::from_parts(3_204_600, 0) + // Minimum execution time: 3_889_000 picoseconds. + Weight::from_parts(4_151_280, 0) // Standard Error: 2 - .saturating_add(Weight::from_parts(3_627, 0).saturating_mul(r.into())) + .saturating_add(Weight::from_parts(3_623, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 5000]`. fn instr_local_tee(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 2_974_000 picoseconds. - Weight::from_parts(3_301_366, 0) - // Standard Error: 2 - .saturating_add(Weight::from_parts(3_836, 0).saturating_mul(r.into())) + // Minimum execution time: 3_880_000 picoseconds. + Weight::from_parts(4_225_780, 0) + // Standard Error: 3 + .saturating_add(Weight::from_parts(3_847, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 5000]`. fn instr_global_get(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_853_000 picoseconds. - Weight::from_parts(2_310_141, 0) + // Minimum execution time: 1_765_000 picoseconds. + Weight::from_parts(2_216_674, 0) // Standard Error: 4 - .saturating_add(Weight::from_parts(8_387, 0).saturating_mul(r.into())) + .saturating_add(Weight::from_parts(8_393, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 5000]`. fn instr_global_set(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_828_000 picoseconds. - Weight::from_parts(2_315_323, 0) - // Standard Error: 4 - .saturating_add(Weight::from_parts(8_797, 0).saturating_mul(r.into())) + // Minimum execution time: 1_764_000 picoseconds. + Weight::from_parts(2_246_735, 0) + // Standard Error: 5 + .saturating_add(Weight::from_parts(8_877, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 5000]`. fn instr_memory_current(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_800_000 picoseconds. - Weight::from_parts(2_117_252, 0) - // Standard Error: 4 - .saturating_add(Weight::from_parts(3_741, 0).saturating_mul(r.into())) + // Minimum execution time: 1_758_000 picoseconds. + Weight::from_parts(1_922_386, 0) + // Standard Error: 94 + .saturating_add(Weight::from_parts(3_868, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 16]`. fn instr_memory_grow(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_743_000 picoseconds. - Weight::from_parts(1_054_728, 0) - // Standard Error: 137_128 - .saturating_add(Weight::from_parts(13_179_446, 0).saturating_mul(r.into())) + // Minimum execution time: 1_635_000 picoseconds. + Weight::from_parts(1_118_785, 0) + // Standard Error: 134_978 + .saturating_add(Weight::from_parts(16_343_664, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 5000]`. fn instr_i64clz(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_735_000 picoseconds. - Weight::from_parts(2_026_568, 0) + // Minimum execution time: 1_648_000 picoseconds. + Weight::from_parts(2_012_545, 0) // Standard Error: 2 - .saturating_add(Weight::from_parts(3_802, 0).saturating_mul(r.into())) + .saturating_add(Weight::from_parts(3_824, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 5000]`. fn instr_i64ctz(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_630_000 picoseconds. - Weight::from_parts(1_980_222, 0) + // Minimum execution time: 1_688_000 picoseconds. + Weight::from_parts(1_995_956, 0) // Standard Error: 2 - .saturating_add(Weight::from_parts(3_735, 0).saturating_mul(r.into())) + .saturating_add(Weight::from_parts(3_757, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 5000]`. fn instr_i64popcnt(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_695_000 picoseconds. - Weight::from_parts(2_040_266, 0) - // Standard Error: 8 - .saturating_add(Weight::from_parts(3_718, 0).saturating_mul(r.into())) + // Minimum execution time: 1_631_000 picoseconds. + Weight::from_parts(2_011_493, 0) + // Standard Error: 3 + .saturating_add(Weight::from_parts(3_755, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 5000]`. fn instr_i64eqz(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_697_000 picoseconds. - Weight::from_parts(2_004_663, 0) - // Standard Error: 3 - .saturating_add(Weight::from_parts(3_661, 0).saturating_mul(r.into())) + // Minimum execution time: 1_667_000 picoseconds. + Weight::from_parts(1_958_798, 0) + // Standard Error: 2 + .saturating_add(Weight::from_parts(3_677, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 5000]`. fn instr_i64extendsi32(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_705_000 picoseconds. - Weight::from_parts(2_682_122, 0) - // Standard Error: 98 - .saturating_add(Weight::from_parts(3_705, 0).saturating_mul(r.into())) + // Minimum execution time: 1_674_000 picoseconds. + Weight::from_parts(2_009_555, 0) + // Standard Error: 7 + .saturating_add(Weight::from_parts(3_863, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 5000]`. fn instr_i64extendui32(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_674_000 picoseconds. - Weight::from_parts(1_989_835, 0) + // Minimum execution time: 1_659_000 picoseconds. + Weight::from_parts(2_014_985, 0) // Standard Error: 2 - .saturating_add(Weight::from_parts(3_801, 0).saturating_mul(r.into())) + .saturating_add(Weight::from_parts(3_821, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 5000]`. fn instr_i32wrapi64(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_695_000 picoseconds. - Weight::from_parts(2_083_824, 0) - // Standard Error: 17 - .saturating_add(Weight::from_parts(3_719, 0).saturating_mul(r.into())) + // Minimum execution time: 1_640_000 picoseconds. + Weight::from_parts(2_013_939, 0) + // Standard Error: 2 + .saturating_add(Weight::from_parts(3_708, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 5000]`. fn instr_i64eq(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_678_000 picoseconds. - Weight::from_parts(1_996_335, 0) - // Standard Error: 4 - .saturating_add(Weight::from_parts(5_995, 0).saturating_mul(r.into())) + // Minimum execution time: 1_631_000 picoseconds. + Weight::from_parts(2_002_814, 0) + // Standard Error: 2 + .saturating_add(Weight::from_parts(6_008, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 5000]`. fn instr_i64ne(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_697_000 picoseconds. - Weight::from_parts(2_046_566, 0) + // Minimum execution time: 1_647_000 picoseconds. + Weight::from_parts(2_032_158, 0) // Standard Error: 3 - .saturating_add(Weight::from_parts(5_936, 0).saturating_mul(r.into())) + .saturating_add(Weight::from_parts(5_944, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 5000]`. fn instr_i64lts(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_721_000 picoseconds. - Weight::from_parts(2_406_930, 0) - // Standard Error: 150 - .saturating_add(Weight::from_parts(5_992, 0).saturating_mul(r.into())) + // Minimum execution time: 1_669_000 picoseconds. + Weight::from_parts(2_040_386, 0) + // Standard Error: 3 + .saturating_add(Weight::from_parts(6_009, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 5000]`. fn instr_i64ltu(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_749_000 picoseconds. - Weight::from_parts(1_987_158, 0) - // Standard Error: 2 - .saturating_add(Weight::from_parts(5_994, 0).saturating_mul(r.into())) + // Minimum execution time: 1_637_000 picoseconds. + Weight::from_parts(1_983_695, 0) + // Standard Error: 4 + .saturating_add(Weight::from_parts(6_027, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 5000]`. fn instr_i64gts(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_706_000 picoseconds. - Weight::from_parts(2_038_138, 0) + // Minimum execution time: 1_701_000 picoseconds. + Weight::from_parts(2_054_295, 0) // Standard Error: 2 - .saturating_add(Weight::from_parts(5_802, 0).saturating_mul(r.into())) + .saturating_add(Weight::from_parts(5_799, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 5000]`. fn instr_i64gtu(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_670_000 picoseconds. - Weight::from_parts(2_555_035, 0) - // Standard Error: 74 - .saturating_add(Weight::from_parts(5_958, 0).saturating_mul(r.into())) + // Minimum execution time: 1_653_000 picoseconds. + Weight::from_parts(2_749_807, 0) + // Standard Error: 90 + .saturating_add(Weight::from_parts(5_945, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 5000]`. fn instr_i64les(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_694_000 picoseconds. - Weight::from_parts(1_991_929, 0) - // Standard Error: 27 - .saturating_add(Weight::from_parts(6_037, 0).saturating_mul(r.into())) + // Minimum execution time: 1_651_000 picoseconds. + Weight::from_parts(1_979_111, 0) + // Standard Error: 3 + .saturating_add(Weight::from_parts(6_011, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 5000]`. fn instr_i64leu(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_684_000 picoseconds. - Weight::from_parts(1_969_284, 0) - // Standard Error: 2 - .saturating_add(Weight::from_parts(6_104, 0).saturating_mul(r.into())) + // Minimum execution time: 1_743_000 picoseconds. + Weight::from_parts(2_058_081, 0) + // Standard Error: 3 + .saturating_add(Weight::from_parts(6_085, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 5000]`. fn instr_i64ges(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_697_000 picoseconds. - Weight::from_parts(2_023_608, 0) + // Minimum execution time: 1_638_000 picoseconds. + Weight::from_parts(2_038_929, 0) // Standard Error: 3 - .saturating_add(Weight::from_parts(5_946, 0).saturating_mul(r.into())) + .saturating_add(Weight::from_parts(5_941, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 5000]`. fn instr_i64geu(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_659_000 picoseconds. - Weight::from_parts(1_963_145, 0) - // Standard Error: 6 - .saturating_add(Weight::from_parts(6_014, 0).saturating_mul(r.into())) + // Minimum execution time: 1_641_000 picoseconds. + Weight::from_parts(2_036_587, 0) + // Standard Error: 2 + .saturating_add(Weight::from_parts(6_008, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 5000]`. fn instr_i64add(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_722_000 picoseconds. - Weight::from_parts(2_016_286, 0) + // Minimum execution time: 1_624_000 picoseconds. + Weight::from_parts(2_080_562, 0) // Standard Error: 3 - .saturating_add(Weight::from_parts(5_889, 0).saturating_mul(r.into())) + .saturating_add(Weight::from_parts(5_826, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 5000]`. fn instr_i64sub(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_656_000 picoseconds. - Weight::from_parts(1_966_234, 0) - // Standard Error: 17 - .saturating_add(Weight::from_parts(6_171, 0).saturating_mul(r.into())) + // Minimum execution time: 1_652_000 picoseconds. + Weight::from_parts(2_039_535, 0) + // Standard Error: 2 + .saturating_add(Weight::from_parts(6_137, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 5000]`. fn instr_i64mul(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_695_000 picoseconds. - Weight::from_parts(2_004_559, 0) - // Standard Error: 2 - .saturating_add(Weight::from_parts(5_785, 0).saturating_mul(r.into())) + // Minimum execution time: 1_666_000 picoseconds. + Weight::from_parts(2_056_354, 0) + // Standard Error: 6 + .saturating_add(Weight::from_parts(5_780, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 5000]`. fn instr_i64divs(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_681_000 picoseconds. - Weight::from_parts(2_135_505, 0) - // Standard Error: 6 - .saturating_add(Weight::from_parts(11_761, 0).saturating_mul(r.into())) + // Minimum execution time: 1_648_000 picoseconds. + Weight::from_parts(2_077_695, 0) + // Standard Error: 4 + .saturating_add(Weight::from_parts(11_775, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 5000]`. fn instr_i64divu(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_653_000 picoseconds. - Weight::from_parts(2_153_167, 0) - // Standard Error: 5 - .saturating_add(Weight::from_parts(10_523, 0).saturating_mul(r.into())) + // Minimum execution time: 1_797_000 picoseconds. + Weight::from_parts(2_772_388, 0) + // Standard Error: 33 + .saturating_add(Weight::from_parts(10_333, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 5000]`. fn instr_i64rems(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_697_000 picoseconds. - Weight::from_parts(2_570_266, 0) - // Standard Error: 50 - .saturating_add(Weight::from_parts(11_971, 0).saturating_mul(r.into())) + // Minimum execution time: 1_699_000 picoseconds. + Weight::from_parts(2_174_288, 0) + // Standard Error: 6 + .saturating_add(Weight::from_parts(11_778, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 5000]`. fn instr_i64remu(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_719_000 picoseconds. - Weight::from_parts(2_101_082, 0) + // Minimum execution time: 1_685_000 picoseconds. + Weight::from_parts(2_091_037, 0) // Standard Error: 3 - .saturating_add(Weight::from_parts(10_709, 0).saturating_mul(r.into())) + .saturating_add(Weight::from_parts(10_694, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 5000]`. fn instr_i64and(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_745_000 picoseconds. - Weight::from_parts(2_621_890, 0) - // Standard Error: 94 - .saturating_add(Weight::from_parts(5_545, 0).saturating_mul(r.into())) + // Minimum execution time: 1_636_000 picoseconds. + Weight::from_parts(1_975_521, 0) + // Standard Error: 2 + .saturating_add(Weight::from_parts(5_695, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 5000]`. fn instr_i64or(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_670_000 picoseconds. - Weight::from_parts(2_046_821, 0) + // Minimum execution time: 1_619_000 picoseconds. + Weight::from_parts(2_045_492, 0) // Standard Error: 2 - .saturating_add(Weight::from_parts(5_762, 0).saturating_mul(r.into())) + .saturating_add(Weight::from_parts(5_770, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 5000]`. fn instr_i64xor(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_714_000 picoseconds. - Weight::from_parts(2_010_173, 0) - // Standard Error: 2 - .saturating_add(Weight::from_parts(5_911, 0).saturating_mul(r.into())) + // Minimum execution time: 1_668_000 picoseconds. + Weight::from_parts(2_055_460, 0) + // Standard Error: 3 + .saturating_add(Weight::from_parts(5_851, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 5000]`. fn instr_i64shl(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_756_000 picoseconds. - Weight::from_parts(2_044_772, 0) + // Minimum execution time: 1_681_000 picoseconds. + Weight::from_parts(2_023_370, 0) // Standard Error: 2 - .saturating_add(Weight::from_parts(5_857, 0).saturating_mul(r.into())) + .saturating_add(Weight::from_parts(5_853, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 5000]`. fn instr_i64shrs(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_741_000 picoseconds. - Weight::from_parts(2_012_819, 0) - // Standard Error: 2 - .saturating_add(Weight::from_parts(6_127, 0).saturating_mul(r.into())) + // Minimum execution time: 1_714_000 picoseconds. + Weight::from_parts(2_067_584, 0) + // Standard Error: 3 + .saturating_add(Weight::from_parts(6_133, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 5000]`. fn instr_i64shru(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_693_000 picoseconds. - Weight::from_parts(2_090_981, 0) - // Standard Error: 2 - .saturating_add(Weight::from_parts(5_839, 0).saturating_mul(r.into())) + // Minimum execution time: 1_602_000 picoseconds. + Weight::from_parts(2_055_530, 0) + // Standard Error: 138 + .saturating_add(Weight::from_parts(6_014, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 5000]`. fn instr_i64rotl(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_704_000 picoseconds. - Weight::from_parts(2_014_108, 0) - // Standard Error: 4 - .saturating_add(Weight::from_parts(6_005, 0).saturating_mul(r.into())) + // Minimum execution time: 1_709_000 picoseconds. + Weight::from_parts(2_016_365, 0) + // Standard Error: 2 + .saturating_add(Weight::from_parts(5_985, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 5000]`. fn instr_i64rotr(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_715_000 picoseconds. - Weight::from_parts(2_053_720, 0) - // Standard Error: 5 - .saturating_add(Weight::from_parts(5_860, 0).saturating_mul(r.into())) + // Minimum execution time: 1_661_000 picoseconds. + Weight::from_parts(2_003_063, 0) + // Standard Error: 2 + .saturating_add(Weight::from_parts(5_863, 0).saturating_mul(r.into())) } } @@ -2136,8 +2134,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `109` // Estimated: `1594` - // Minimum execution time: 2_567_000 picoseconds. - Weight::from_parts(2_711_000, 1594) + // Minimum execution time: 2_585_000 picoseconds. + Weight::from_parts(2_802_000, 1594) .saturating_add(RocksDbWeight::get().reads(1_u64)) } /// Storage: Skipped Metadata (r:0 w:0) @@ -2147,10 +2145,10 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `450 + k * (69 ±0)` // Estimated: `440 + k * (70 ±0)` - // Minimum execution time: 11_048_000 picoseconds. - Weight::from_parts(7_029_322, 440) - // Standard Error: 1_158 - .saturating_add(Weight::from_parts(975_010, 0).saturating_mul(k.into())) + // Minimum execution time: 10_825_000 picoseconds. + Weight::from_parts(5_747_064, 440) + // Standard Error: 1_037 + .saturating_add(Weight::from_parts(973_689, 0).saturating_mul(k.into())) .saturating_add(RocksDbWeight::get().reads(1_u64)) .saturating_add(RocksDbWeight::get().reads((1_u64).saturating_mul(k.into()))) .saturating_add(RocksDbWeight::get().writes(1_u64)) @@ -2164,10 +2162,10 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `250 + q * (33 ±0)` // Estimated: `1725 + q * (33 ±0)` - // Minimum execution time: 2_645_000 picoseconds. - Weight::from_parts(10_865_477, 1725) - // Standard Error: 3_383 - .saturating_add(Weight::from_parts(1_295_351, 0).saturating_mul(q.into())) + // Minimum execution time: 2_718_000 picoseconds. + Weight::from_parts(11_436_305, 1725) + // Standard Error: 3_619 + .saturating_add(Weight::from_parts(1_296_955, 0).saturating_mul(q.into())) .saturating_add(RocksDbWeight::get().reads(1_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) .saturating_add(Weight::from_parts(0, 33).saturating_mul(q.into())) @@ -2181,10 +2179,10 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `238 + c * (1 ±0)` // Estimated: `3951 + c * (2 ±0)` - // Minimum execution time: 30_817_000 picoseconds. - Weight::from_parts(29_762_622, 3951) - // Standard Error: 62 - .saturating_add(Weight::from_parts(54_714, 0).saturating_mul(c.into())) + // Minimum execution time: 37_882_000 picoseconds. + Weight::from_parts(43_548_816, 3951) + // Standard Error: 49 + .saturating_add(Weight::from_parts(53_936, 0).saturating_mul(c.into())) .saturating_add(RocksDbWeight::get().reads(1_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) .saturating_add(Weight::from_parts(0, 2).saturating_mul(c.into())) @@ -2204,10 +2202,10 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `707` // Estimated: `21400 + c * (5 ±0)` - // Minimum execution time: 245_493_000 picoseconds. - Weight::from_parts(253_703_384, 21400) - // Standard Error: 27 - .saturating_add(Weight::from_parts(38_078, 0).saturating_mul(c.into())) + // Minimum execution time: 313_392_000 picoseconds. + Weight::from_parts(325_419_093, 21400) + // Standard Error: 25 + .saturating_add(Weight::from_parts(37_877, 0).saturating_mul(c.into())) .saturating_add(RocksDbWeight::get().reads(6_u64)) .saturating_add(RocksDbWeight::get().writes(4_u64)) .saturating_add(Weight::from_parts(0, 5).saturating_mul(c.into())) @@ -2235,14 +2233,14 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `270` // Estimated: `26207` - // Minimum execution time: 3_079_430_000 picoseconds. - Weight::from_parts(555_022_483, 26207) - // Standard Error: 290 - .saturating_add(Weight::from_parts(107_411, 0).saturating_mul(c.into())) - // Standard Error: 17 - .saturating_add(Weight::from_parts(1_123, 0).saturating_mul(i.into())) - // Standard Error: 17 - .saturating_add(Weight::from_parts(1_434, 0).saturating_mul(s.into())) + // Minimum execution time: 3_274_352_000 picoseconds. + Weight::from_parts(681_171_416, 26207) + // Standard Error: 336 + .saturating_add(Weight::from_parts(106_391, 0).saturating_mul(c.into())) + // Standard Error: 19 + .saturating_add(Weight::from_parts(1_147, 0).saturating_mul(i.into())) + // Standard Error: 19 + .saturating_add(Weight::from_parts(1_493, 0).saturating_mul(s.into())) .saturating_add(RocksDbWeight::get().reads(9_u64)) .saturating_add(RocksDbWeight::get().writes(10_u64)) } @@ -2266,12 +2264,12 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `482` // Estimated: `28521` - // Minimum execution time: 1_600_733_000 picoseconds. - Weight::from_parts(228_681_962, 28521) + // Minimum execution time: 1_692_637_000 picoseconds. + Weight::from_parts(283_252_265, 28521) // Standard Error: 8 - .saturating_add(Weight::from_parts(1_445, 0).saturating_mul(i.into())) + .saturating_add(Weight::from_parts(1_498, 0).saturating_mul(i.into())) // Standard Error: 8 - .saturating_add(Weight::from_parts(1_456, 0).saturating_mul(s.into())) + .saturating_add(Weight::from_parts(1_487, 0).saturating_mul(s.into())) .saturating_add(RocksDbWeight::get().reads(9_u64)) .saturating_add(RocksDbWeight::get().writes(7_u64)) } @@ -2289,8 +2287,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `759` // Estimated: `21615` - // Minimum execution time: 172_310_000 picoseconds. - Weight::from_parts(176_655_000, 21615) + // Minimum execution time: 192_369_000 picoseconds. + Weight::from_parts(193_417_000, 21615) .saturating_add(RocksDbWeight::get().reads(6_u64)) .saturating_add(RocksDbWeight::get().writes(4_u64)) } @@ -2307,10 +2305,10 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `109` // Estimated: `7366` - // Minimum execution time: 246_364_000 picoseconds. - Weight::from_parts(262_887_234, 7366) - // Standard Error: 90 - .saturating_add(Weight::from_parts(107_926, 0).saturating_mul(c.into())) + // Minimum execution time: 301_972_000 picoseconds. + Weight::from_parts(313_248_051, 7366) + // Standard Error: 82 + .saturating_add(Weight::from_parts(107_321, 0).saturating_mul(c.into())) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().writes(4_u64)) } @@ -2326,8 +2324,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `255` // Estimated: `7950` - // Minimum execution time: 29_226_000 picoseconds. - Weight::from_parts(29_542_000, 7950) + // Minimum execution time: 33_013_000 picoseconds. + Weight::from_parts(33_622_000, 7950) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().writes(4_u64)) } @@ -2341,8 +2339,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `570` // Estimated: `19530` - // Minimum execution time: 33_921_000 picoseconds. - Weight::from_parts(35_056_000, 19530) + // Minimum execution time: 33_518_000 picoseconds. + Weight::from_parts(33_819_000, 19530) .saturating_add(RocksDbWeight::get().reads(6_u64)) .saturating_add(RocksDbWeight::get().writes(6_u64)) } @@ -2361,10 +2359,10 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `781 + r * (6 ±0)` // Estimated: `21730 + r * (30 ±0)` - // Minimum execution time: 232_118_000 picoseconds. - Weight::from_parts(233_883_823, 21730) - // Standard Error: 642 - .saturating_add(Weight::from_parts(340_543, 0).saturating_mul(r.into())) + // Minimum execution time: 283_945_000 picoseconds. + Weight::from_parts(289_209_599, 21730) + // Standard Error: 901 + .saturating_add(Weight::from_parts(327_601, 0).saturating_mul(r.into())) .saturating_add(RocksDbWeight::get().reads(6_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) .saturating_add(Weight::from_parts(0, 30).saturating_mul(r.into())) @@ -2384,10 +2382,10 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `839 + r * (240 ±0)` // Estimated: `21835 + r * (3675 ±0)` - // Minimum execution time: 231_712_000 picoseconds. - Weight::from_parts(74_297_097, 21835) - // Standard Error: 6_099 - .saturating_add(Weight::from_parts(3_322_192, 0).saturating_mul(r.into())) + // Minimum execution time: 284_949_000 picoseconds. + Weight::from_parts(126_196_485, 21835) + // Standard Error: 6_260 + .saturating_add(Weight::from_parts(3_368_849, 0).saturating_mul(r.into())) .saturating_add(RocksDbWeight::get().reads(6_u64)) .saturating_add(RocksDbWeight::get().reads((1_u64).saturating_mul(r.into()))) .saturating_add(RocksDbWeight::get().writes(3_u64)) @@ -2408,10 +2406,10 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `831 + r * (244 ±0)` // Estimated: `21855 + r * (3695 ±0)` - // Minimum execution time: 233_122_000 picoseconds. - Weight::from_parts(74_369_473, 21855) - // Standard Error: 6_027 - .saturating_add(Weight::from_parts(4_177_187, 0).saturating_mul(r.into())) + // Minimum execution time: 286_429_000 picoseconds. + Weight::from_parts(124_820_396, 21855) + // Standard Error: 6_539 + .saturating_add(Weight::from_parts(4_163_535, 0).saturating_mul(r.into())) .saturating_add(RocksDbWeight::get().reads(6_u64)) .saturating_add(RocksDbWeight::get().reads((1_u64).saturating_mul(r.into()))) .saturating_add(RocksDbWeight::get().writes(3_u64)) @@ -2432,10 +2430,10 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `788 + r * (6 ±0)` // Estimated: `21770 + r * (30 ±0)` - // Minimum execution time: 232_822_000 picoseconds. - Weight::from_parts(232_507_418, 21770) - // Standard Error: 961 - .saturating_add(Weight::from_parts(419_331, 0).saturating_mul(r.into())) + // Minimum execution time: 285_042_000 picoseconds. + Weight::from_parts(288_096_303, 21770) + // Standard Error: 972 + .saturating_add(Weight::from_parts(409_782, 0).saturating_mul(r.into())) .saturating_add(RocksDbWeight::get().reads(6_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) .saturating_add(Weight::from_parts(0, 30).saturating_mul(r.into())) @@ -2455,10 +2453,10 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `778 + r * (3 ±0)` // Estimated: `21735 + r * (15 ±0)` - // Minimum execution time: 229_719_000 picoseconds. - Weight::from_parts(235_257_789, 21735) - // Standard Error: 451 - .saturating_add(Weight::from_parts(161_101, 0).saturating_mul(r.into())) + // Minimum execution time: 282_820_000 picoseconds. + Weight::from_parts(287_104_710, 21735) + // Standard Error: 421 + .saturating_add(Weight::from_parts(167_907, 0).saturating_mul(r.into())) .saturating_add(RocksDbWeight::get().reads(6_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) .saturating_add(Weight::from_parts(0, 15).saturating_mul(r.into())) @@ -2478,10 +2476,10 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `782 + r * (6 ±0)` // Estimated: `21740 + r * (30 ±0)` - // Minimum execution time: 231_517_000 picoseconds. - Weight::from_parts(236_185_452, 21740) - // Standard Error: 651 - .saturating_add(Weight::from_parts(334_659, 0).saturating_mul(r.into())) + // Minimum execution time: 284_619_000 picoseconds. + Weight::from_parts(281_326_785, 21740) + // Standard Error: 1_379 + .saturating_add(Weight::from_parts(342_779, 0).saturating_mul(r.into())) .saturating_add(RocksDbWeight::get().reads(6_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) .saturating_add(Weight::from_parts(0, 30).saturating_mul(r.into())) @@ -2501,10 +2499,10 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `783 + r * (6 ±0)` // Estimated: `21725 + r * (30 ±0)` - // Minimum execution time: 232_855_000 picoseconds. - Weight::from_parts(239_986_328, 21725) - // Standard Error: 4_075 - .saturating_add(Weight::from_parts(345_726, 0).saturating_mul(r.into())) + // Minimum execution time: 284_703_000 picoseconds. + Weight::from_parts(289_479_932, 21725) + // Standard Error: 745 + .saturating_add(Weight::from_parts(323_625, 0).saturating_mul(r.into())) .saturating_add(RocksDbWeight::get().reads(6_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) .saturating_add(Weight::from_parts(0, 30).saturating_mul(r.into())) @@ -2524,10 +2522,10 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `922 + r * (6 ±0)` // Estimated: `24633 + r * (30 ±0)` - // Minimum execution time: 231_685_000 picoseconds. - Weight::from_parts(245_514_261, 24633) - // Standard Error: 1_635 - .saturating_add(Weight::from_parts(1_523_359, 0).saturating_mul(r.into())) + // Minimum execution time: 283_610_000 picoseconds. + Weight::from_parts(299_901_534, 24633) + // Standard Error: 1_177 + .saturating_add(Weight::from_parts(1_474_603, 0).saturating_mul(r.into())) .saturating_add(RocksDbWeight::get().reads(7_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) .saturating_add(Weight::from_parts(0, 30).saturating_mul(r.into())) @@ -2547,10 +2545,10 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `792 + r * (6 ±0)` // Estimated: `21825 + r * (30 ±0)` - // Minimum execution time: 233_361_000 picoseconds. - Weight::from_parts(234_553_471, 21825) - // Standard Error: 1_108 - .saturating_add(Weight::from_parts(330_383, 0).saturating_mul(r.into())) + // Minimum execution time: 284_474_000 picoseconds. + Weight::from_parts(283_540_273, 21825) + // Standard Error: 1_164 + .saturating_add(Weight::from_parts(339_262, 0).saturating_mul(r.into())) .saturating_add(RocksDbWeight::get().reads(6_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) .saturating_add(Weight::from_parts(0, 30).saturating_mul(r.into())) @@ -2570,10 +2568,10 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `790 + r * (6 ±0)` // Estimated: `21815 + r * (30 ±0)` - // Minimum execution time: 231_918_000 picoseconds. - Weight::from_parts(239_128_416, 21815) - // Standard Error: 1_473 - .saturating_add(Weight::from_parts(317_879, 0).saturating_mul(r.into())) + // Minimum execution time: 284_521_000 picoseconds. + Weight::from_parts(285_747_754, 21815) + // Standard Error: 889 + .saturating_add(Weight::from_parts(326_428, 0).saturating_mul(r.into())) .saturating_add(RocksDbWeight::get().reads(6_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) .saturating_add(Weight::from_parts(0, 30).saturating_mul(r.into())) @@ -2593,10 +2591,10 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `787 + r * (6 ±0)` // Estimated: `21805 + r * (30 ±0)` - // Minimum execution time: 231_936_000 picoseconds. - Weight::from_parts(232_240_207, 21805) - // Standard Error: 731 - .saturating_add(Weight::from_parts(330_713, 0).saturating_mul(r.into())) + // Minimum execution time: 284_103_000 picoseconds. + Weight::from_parts(283_801_256, 21805) + // Standard Error: 1_051 + .saturating_add(Weight::from_parts(334_081, 0).saturating_mul(r.into())) .saturating_add(RocksDbWeight::get().reads(6_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) .saturating_add(Weight::from_parts(0, 30).saturating_mul(r.into())) @@ -2616,10 +2614,10 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `778 + r * (6 ±0)` // Estimated: `21735 + r * (30 ±0)` - // Minimum execution time: 231_978_000 picoseconds. - Weight::from_parts(224_881_201, 21735) - // Standard Error: 1_296 - .saturating_add(Weight::from_parts(346_872, 0).saturating_mul(r.into())) + // Minimum execution time: 284_187_000 picoseconds. + Weight::from_parts(289_414_364, 21735) + // Standard Error: 796 + .saturating_add(Weight::from_parts(324_603, 0).saturating_mul(r.into())) .saturating_add(RocksDbWeight::get().reads(6_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) .saturating_add(Weight::from_parts(0, 30).saturating_mul(r.into())) @@ -2641,10 +2639,10 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `856 + r * (10 ±0)` // Estimated: `24446 + r * (60 ±0)` - // Minimum execution time: 231_707_000 picoseconds. - Weight::from_parts(245_939_854, 24446) - // Standard Error: 1_369 - .saturating_add(Weight::from_parts(1_380_453, 0).saturating_mul(r.into())) + // Minimum execution time: 284_953_000 picoseconds. + Weight::from_parts(290_535_752, 24446) + // Standard Error: 2_462 + .saturating_add(Weight::from_parts(1_361_518, 0).saturating_mul(r.into())) .saturating_add(RocksDbWeight::get().reads(7_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) .saturating_add(Weight::from_parts(0, 60).saturating_mul(r.into())) @@ -2664,10 +2662,10 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `745 + r * (4 ±0)` // Estimated: `21555 + r * (20 ±0)` - // Minimum execution time: 157_429_000 picoseconds. - Weight::from_parts(161_047_355, 21555) - // Standard Error: 1_220 - .saturating_add(Weight::from_parts(134_497, 0).saturating_mul(r.into())) + // Minimum execution time: 160_775_000 picoseconds. + Weight::from_parts(164_652_364, 21555) + // Standard Error: 284 + .saturating_add(Weight::from_parts(132_574, 0).saturating_mul(r.into())) .saturating_add(RocksDbWeight::get().reads(6_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) .saturating_add(Weight::from_parts(0, 20).saturating_mul(r.into())) @@ -2687,10 +2685,10 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `780 + r * (6 ±0)` // Estimated: `21740 + r * (30 ±0)` - // Minimum execution time: 232_561_000 picoseconds. - Weight::from_parts(230_989_499, 21740) - // Standard Error: 848 - .saturating_add(Weight::from_parts(282_053, 0).saturating_mul(r.into())) + // Minimum execution time: 284_072_000 picoseconds. + Weight::from_parts(288_418_644, 21740) + // Standard Error: 792 + .saturating_add(Weight::from_parts(272_881, 0).saturating_mul(r.into())) .saturating_add(RocksDbWeight::get().reads(6_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) .saturating_add(Weight::from_parts(0, 30).saturating_mul(r.into())) @@ -2710,10 +2708,10 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `784` // Estimated: `21740` - // Minimum execution time: 237_080_000 picoseconds. - Weight::from_parts(239_380_994, 21740) - // Standard Error: 2 - .saturating_add(Weight::from_parts(592, 0).saturating_mul(n.into())) + // Minimum execution time: 286_671_000 picoseconds. + Weight::from_parts(292_151_662, 21740) + // Standard Error: 1 + .saturating_add(Weight::from_parts(638, 0).saturating_mul(n.into())) .saturating_add(RocksDbWeight::get().reads(6_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) } @@ -2732,10 +2730,10 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `768 + r * (45 ±0)` // Estimated: `21660 + r * (225 ±0)` - // Minimum execution time: 228_669_000 picoseconds. - Weight::from_parts(230_916_471, 21660) - // Standard Error: 295_520 - .saturating_add(Weight::from_parts(1_420_928, 0).saturating_mul(r.into())) + // Minimum execution time: 280_334_000 picoseconds. + Weight::from_parts(283_487_571, 21660) + // Standard Error: 267_797 + .saturating_add(Weight::from_parts(3_803_128, 0).saturating_mul(r.into())) .saturating_add(RocksDbWeight::get().reads(6_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) .saturating_add(Weight::from_parts(0, 225).saturating_mul(r.into())) @@ -2755,10 +2753,10 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `778` // Estimated: `21775` - // Minimum execution time: 231_626_000 picoseconds. - Weight::from_parts(232_097_413, 21775) + // Minimum execution time: 284_004_000 picoseconds. + Weight::from_parts(283_681_350, 21775) // Standard Error: 1 - .saturating_add(Weight::from_parts(185, 0).saturating_mul(n.into())) + .saturating_add(Weight::from_parts(232, 0).saturating_mul(n.into())) .saturating_add(RocksDbWeight::get().reads(6_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) } @@ -2781,10 +2779,10 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `810 + r * (356 ±0)` // Estimated: `25511 + r * (15321 ±0)` - // Minimum execution time: 230_758_000 picoseconds. - Weight::from_parts(233_235_993, 25511) - // Standard Error: 196_168 - .saturating_add(Weight::from_parts(81_177_506, 0).saturating_mul(r.into())) + // Minimum execution time: 284_143_000 picoseconds. + Weight::from_parts(287_218_324, 25511) + // Standard Error: 343_611 + .saturating_add(Weight::from_parts(109_895_675, 0).saturating_mul(r.into())) .saturating_add(RocksDbWeight::get().reads(6_u64)) .saturating_add(RocksDbWeight::get().reads((6_u64).saturating_mul(r.into()))) .saturating_add(RocksDbWeight::get().writes(3_u64)) @@ -2808,10 +2806,10 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `825 + r * (10 ±0)` // Estimated: `24283 + r * (60 ±0)` - // Minimum execution time: 232_457_000 picoseconds. - Weight::from_parts(240_943_161, 24283) - // Standard Error: 3_085 - .saturating_add(Weight::from_parts(1_837_168, 0).saturating_mul(r.into())) + // Minimum execution time: 285_037_000 picoseconds. + Weight::from_parts(299_804_606, 24283) + // Standard Error: 5_518 + .saturating_add(Weight::from_parts(1_848_164, 0).saturating_mul(r.into())) .saturating_add(RocksDbWeight::get().reads(7_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) .saturating_add(Weight::from_parts(0, 60).saturating_mul(r.into())) @@ -2831,10 +2829,10 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `778 + r * (10 ±0)` // Estimated: `21735 + r * (50 ±0)` - // Minimum execution time: 229_788_000 picoseconds. - Weight::from_parts(233_520_858, 21735) - // Standard Error: 9_561 - .saturating_add(Weight::from_parts(3_596_294, 0).saturating_mul(r.into())) + // Minimum execution time: 282_886_000 picoseconds. + Weight::from_parts(293_171_736, 21735) + // Standard Error: 2_171 + .saturating_add(Weight::from_parts(3_491_303, 0).saturating_mul(r.into())) .saturating_add(RocksDbWeight::get().reads(6_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) .saturating_add(Weight::from_parts(0, 50).saturating_mul(r.into())) @@ -2855,12 +2853,12 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `797 + t * (32 ±0)` // Estimated: `21840 + t * (2640 ±0)` - // Minimum execution time: 248_294_000 picoseconds. - Weight::from_parts(239_709_759, 21840) - // Standard Error: 66_903 - .saturating_add(Weight::from_parts(2_880_732, 0).saturating_mul(t.into())) - // Standard Error: 18 - .saturating_add(Weight::from_parts(725, 0).saturating_mul(n.into())) + // Minimum execution time: 300_675_000 picoseconds. + Weight::from_parts(296_092_420, 21840) + // Standard Error: 130_733 + .saturating_add(Weight::from_parts(2_487_957, 0).saturating_mul(t.into())) + // Standard Error: 36 + .saturating_add(Weight::from_parts(565, 0).saturating_mul(n.into())) .saturating_add(RocksDbWeight::get().reads(6_u64)) .saturating_add(RocksDbWeight::get().reads((1_u64).saturating_mul(t.into()))) .saturating_add(RocksDbWeight::get().writes(3_u64)) @@ -2882,10 +2880,10 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `777 + r * (7 ±0)` // Estimated: `21725 + r * (35 ±0)` - // Minimum execution time: 170_601_000 picoseconds. - Weight::from_parts(165_819_714, 21725) - // Standard Error: 452 - .saturating_add(Weight::from_parts(240_362, 0).saturating_mul(r.into())) + // Minimum execution time: 166_638_000 picoseconds. + Weight::from_parts(171_353_083, 21725) + // Standard Error: 550 + .saturating_add(Weight::from_parts(238_768, 0).saturating_mul(r.into())) .saturating_add(RocksDbWeight::get().reads(6_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) .saturating_add(Weight::from_parts(0, 35).saturating_mul(r.into())) @@ -2905,10 +2903,10 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `125728` // Estimated: `269977` - // Minimum execution time: 349_543_000 picoseconds. - Weight::from_parts(352_945_102, 269977) - // Standard Error: 2 - .saturating_add(Weight::from_parts(749, 0).saturating_mul(i.into())) + // Minimum execution time: 414_136_000 picoseconds. + Weight::from_parts(416_093_921, 269977) + // Standard Error: 3 + .saturating_add(Weight::from_parts(794, 0).saturating_mul(i.into())) .saturating_add(RocksDbWeight::get().reads(6_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) } @@ -2919,10 +2917,10 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `845 + r * (292 ±0)` // Estimated: `843 + r * (293 ±0)` - // Minimum execution time: 238_766_000 picoseconds. - Weight::from_parts(130_994_282, 843) - // Standard Error: 10_413 - .saturating_add(Weight::from_parts(6_134_087, 0).saturating_mul(r.into())) + // Minimum execution time: 285_920_000 picoseconds. + Weight::from_parts(184_945_789, 843) + // Standard Error: 9_604 + .saturating_add(Weight::from_parts(6_012_522, 0).saturating_mul(r.into())) .saturating_add(RocksDbWeight::get().reads(6_u64)) .saturating_add(RocksDbWeight::get().reads((1_u64).saturating_mul(r.into()))) .saturating_add(RocksDbWeight::get().writes(3_u64)) @@ -2936,10 +2934,10 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `1304` // Estimated: `1280` - // Minimum execution time: 248_137_000 picoseconds. - Weight::from_parts(265_535_917, 1280) - // Standard Error: 28 - .saturating_add(Weight::from_parts(387, 0).saturating_mul(n.into())) + // Minimum execution time: 299_772_000 picoseconds. + Weight::from_parts(333_451_106, 1280) + // Standard Error: 54 + .saturating_add(Weight::from_parts(579, 0).saturating_mul(n.into())) .saturating_add(RocksDbWeight::get().reads(8_u64)) .saturating_add(RocksDbWeight::get().writes(6_u64)) } @@ -2950,10 +2948,10 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `1167 + n * (1 ±0)` // Estimated: `1167 + n * (1 ±0)` - // Minimum execution time: 246_752_000 picoseconds. - Weight::from_parts(250_315_210, 1167) - // Standard Error: 42 - .saturating_add(Weight::from_parts(135, 0).saturating_mul(n.into())) + // Minimum execution time: 299_279_000 picoseconds. + Weight::from_parts(302_336_567, 1167) + // Standard Error: 25 + .saturating_add(Weight::from_parts(86, 0).saturating_mul(n.into())) .saturating_add(RocksDbWeight::get().reads(7_u64)) .saturating_add(RocksDbWeight::get().writes(4_u64)) .saturating_add(Weight::from_parts(0, 1).saturating_mul(n.into())) @@ -2965,10 +2963,10 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `841 + r * (288 ±0)` // Estimated: `845 + r * (289 ±0)` - // Minimum execution time: 232_873_000 picoseconds. - Weight::from_parts(130_360_558, 845) - // Standard Error: 10_026 - .saturating_add(Weight::from_parts(6_023_862, 0).saturating_mul(r.into())) + // Minimum execution time: 284_689_000 picoseconds. + Weight::from_parts(185_207_302, 845) + // Standard Error: 10_030 + .saturating_add(Weight::from_parts(5_871_325, 0).saturating_mul(r.into())) .saturating_add(RocksDbWeight::get().reads(6_u64)) .saturating_add(RocksDbWeight::get().reads((1_u64).saturating_mul(r.into()))) .saturating_add(RocksDbWeight::get().writes(3_u64)) @@ -2982,10 +2980,10 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `1163 + n * (1 ±0)` // Estimated: `1163 + n * (1 ±0)` - // Minimum execution time: 247_079_000 picoseconds. - Weight::from_parts(248_922_003, 1163) - // Standard Error: 22 - .saturating_add(Weight::from_parts(201, 0).saturating_mul(n.into())) + // Minimum execution time: 299_364_000 picoseconds. + Weight::from_parts(302_089_070, 1163) + // Standard Error: 23 + .saturating_add(Weight::from_parts(128, 0).saturating_mul(n.into())) .saturating_add(RocksDbWeight::get().reads(7_u64)) .saturating_add(RocksDbWeight::get().writes(4_u64)) .saturating_add(Weight::from_parts(0, 1).saturating_mul(n.into())) @@ -2997,10 +2995,10 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `835 + r * (296 ±0)` // Estimated: `840 + r * (297 ±0)` - // Minimum execution time: 232_696_000 picoseconds. - Weight::from_parts(144_147_151, 840) - // Standard Error: 8_846 - .saturating_add(Weight::from_parts(4_931_414, 0).saturating_mul(r.into())) + // Minimum execution time: 285_175_000 picoseconds. + Weight::from_parts(200_262_957, 840) + // Standard Error: 8_681 + .saturating_add(Weight::from_parts(4_899_266, 0).saturating_mul(r.into())) .saturating_add(RocksDbWeight::get().reads(6_u64)) .saturating_add(RocksDbWeight::get().reads((1_u64).saturating_mul(r.into()))) .saturating_add(RocksDbWeight::get().writes(3_u64)) @@ -3013,10 +3011,10 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `1179 + n * (1 ±0)` // Estimated: `1179 + n * (1 ±0)` - // Minimum execution time: 247_795_000 picoseconds. - Weight::from_parts(252_944_118, 1179) - // Standard Error: 45 - .saturating_add(Weight::from_parts(480, 0).saturating_mul(n.into())) + // Minimum execution time: 299_459_000 picoseconds. + Weight::from_parts(302_451_160, 1179) + // Standard Error: 36 + .saturating_add(Weight::from_parts(731, 0).saturating_mul(n.into())) .saturating_add(RocksDbWeight::get().reads(7_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) .saturating_add(Weight::from_parts(0, 1).saturating_mul(n.into())) @@ -3028,10 +3026,10 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `856 + r * (288 ±0)` // Estimated: `857 + r * (289 ±0)` - // Minimum execution time: 232_166_000 picoseconds. - Weight::from_parts(148_503_428, 857) - // Standard Error: 8_704 - .saturating_add(Weight::from_parts(4_766_055, 0).saturating_mul(r.into())) + // Minimum execution time: 286_384_000 picoseconds. + Weight::from_parts(203_389_467, 857) + // Standard Error: 8_817 + .saturating_add(Weight::from_parts(4_692_347, 0).saturating_mul(r.into())) .saturating_add(RocksDbWeight::get().reads(6_u64)) .saturating_add(RocksDbWeight::get().reads((1_u64).saturating_mul(r.into()))) .saturating_add(RocksDbWeight::get().writes(3_u64)) @@ -3044,10 +3042,10 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `1166 + n * (1 ±0)` // Estimated: `1166 + n * (1 ±0)` - // Minimum execution time: 246_128_000 picoseconds. - Weight::from_parts(248_323_705, 1166) - // Standard Error: 21 - .saturating_add(Weight::from_parts(92, 0).saturating_mul(n.into())) + // Minimum execution time: 297_450_000 picoseconds. + Weight::from_parts(300_459_851, 1166) + // Standard Error: 39 + .saturating_add(Weight::from_parts(108, 0).saturating_mul(n.into())) .saturating_add(RocksDbWeight::get().reads(7_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) .saturating_add(Weight::from_parts(0, 1).saturating_mul(n.into())) @@ -3059,10 +3057,10 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `829 + r * (296 ±0)` // Estimated: `836 + r * (297 ±0)` - // Minimum execution time: 232_976_000 picoseconds. - Weight::from_parts(132_373_848, 836) - // Standard Error: 9_855 - .saturating_add(Weight::from_parts(6_181_821, 0).saturating_mul(r.into())) + // Minimum execution time: 285_572_000 picoseconds. + Weight::from_parts(182_642_557, 836) + // Standard Error: 9_977 + .saturating_add(Weight::from_parts(6_090_684, 0).saturating_mul(r.into())) .saturating_add(RocksDbWeight::get().reads(6_u64)) .saturating_add(RocksDbWeight::get().reads((1_u64).saturating_mul(r.into()))) .saturating_add(RocksDbWeight::get().writes(3_u64)) @@ -3076,10 +3074,10 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `1180 + n * (1 ±0)` // Estimated: `1180 + n * (1 ±0)` - // Minimum execution time: 247_907_000 picoseconds. - Weight::from_parts(251_984_133, 1180) - // Standard Error: 41 - .saturating_add(Weight::from_parts(639, 0).saturating_mul(n.into())) + // Minimum execution time: 301_344_000 picoseconds. + Weight::from_parts(303_770_522, 1180) + // Standard Error: 29 + .saturating_add(Weight::from_parts(807, 0).saturating_mul(n.into())) .saturating_add(RocksDbWeight::get().reads(7_u64)) .saturating_add(RocksDbWeight::get().writes(4_u64)) .saturating_add(Weight::from_parts(0, 1).saturating_mul(n.into())) @@ -3099,10 +3097,10 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `1373 + r * (45 ±0)` // Estimated: `26753 + r * (2700 ±0)` - // Minimum execution time: 232_614_000 picoseconds. - Weight::from_parts(127_024_367, 26753) - // Standard Error: 54_521 - .saturating_add(Weight::from_parts(20_975_575, 0).saturating_mul(r.into())) + // Minimum execution time: 286_835_000 picoseconds. + Weight::from_parts(245_206_457, 26753) + // Standard Error: 73_782 + .saturating_add(Weight::from_parts(36_414_448, 0).saturating_mul(r.into())) .saturating_add(RocksDbWeight::get().reads(7_u64)) .saturating_add(RocksDbWeight::get().reads((1_u64).saturating_mul(r.into()))) .saturating_add(RocksDbWeight::get().writes(4_u64)) @@ -3111,53 +3109,53 @@ impl WeightInfo for () { } /// Storage: System Account (r:1 w:0) /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: Measured) - /// Storage: Contracts ContractInfoOf (r:1601 w:1601) + /// Storage: Contracts ContractInfoOf (r:801 w:801) /// Proof: Contracts ContractInfoOf (max_values: None, max_size: Some(290), added: 2765, mode: Measured) /// Storage: Contracts CodeStorage (r:2 w:0) /// Proof: Contracts CodeStorage (max_values: None, max_size: Some(126001), added: 128476, mode: Measured) /// Storage: Timestamp Now (r:1 w:0) /// Proof: Timestamp Now (max_values: Some(1), max_size: Some(8), added: 503, mode: Measured) - /// Storage: System EventTopics (r:1602 w:1602) + /// Storage: System EventTopics (r:802 w:802) /// Proof Skipped: System EventTopics (max_values: None, max_size: None, mode: Measured) - /// The range of component `r` is `[0, 1600]`. + /// The range of component `r` is `[0, 800]`. fn seal_call(r: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `1512 + r * (256 ±0)` - // Estimated: `27163 + r * (6231 ±0)` - // Minimum execution time: 233_446_000 picoseconds. - Weight::from_parts(234_338_000, 27163) - // Standard Error: 122_754 - .saturating_add(Weight::from_parts(216_709_600, 0).saturating_mul(r.into())) + // Measured: `1237 + r * (256 ±0)` + // Estimated: `26028 + r * (6235 ±0)` + // Minimum execution time: 287_184_000 picoseconds. + Weight::from_parts(287_525_000, 26028) + // Standard Error: 66_791 + .saturating_add(Weight::from_parts(261_473_539, 0).saturating_mul(r.into())) .saturating_add(RocksDbWeight::get().reads(7_u64)) .saturating_add(RocksDbWeight::get().reads((2_u64).saturating_mul(r.into()))) .saturating_add(RocksDbWeight::get().writes(3_u64)) .saturating_add(RocksDbWeight::get().writes((2_u64).saturating_mul(r.into()))) - .saturating_add(Weight::from_parts(0, 6231).saturating_mul(r.into())) + .saturating_add(Weight::from_parts(0, 6235).saturating_mul(r.into())) } /// Storage: System Account (r:1 w:0) /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: Measured) /// Storage: Contracts ContractInfoOf (r:1 w:1) /// Proof: Contracts ContractInfoOf (max_values: None, max_size: Some(290), added: 2765, mode: Measured) - /// Storage: Contracts CodeStorage (r:1536 w:0) + /// Storage: Contracts CodeStorage (r:736 w:0) /// Proof: Contracts CodeStorage (max_values: None, max_size: Some(126001), added: 128476, mode: Measured) /// Storage: Timestamp Now (r:1 w:0) /// Proof: Timestamp Now (max_values: Some(1), max_size: Some(8), added: 503, mode: Measured) - /// Storage: System EventTopics (r:1537 w:1537) + /// Storage: System EventTopics (r:737 w:737) /// Proof Skipped: System EventTopics (max_values: None, max_size: None, mode: Measured) - /// The range of component `r` is `[0, 1600]`. + /// The range of component `r` is `[0, 800]`. fn seal_delegate_call(r: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `0 + r * (902 ±0)` - // Estimated: `21755 + r * (8167 ±7)` - // Minimum execution time: 232_717_000 picoseconds. - Weight::from_parts(233_405_000, 21755) - // Standard Error: 98_607 - .saturating_add(Weight::from_parts(212_329_100, 0).saturating_mul(r.into())) + // Measured: `0 + r * (502 ±0)` + // Estimated: `21755 + r * (6329 ±10)` + // Minimum execution time: 285_759_000 picoseconds. + Weight::from_parts(286_643_000, 21755) + // Standard Error: 133_180 + .saturating_add(Weight::from_parts(257_186_897, 0).saturating_mul(r.into())) .saturating_add(RocksDbWeight::get().reads(6_u64)) .saturating_add(RocksDbWeight::get().reads((2_u64).saturating_mul(r.into()))) .saturating_add(RocksDbWeight::get().writes(3_u64)) .saturating_add(RocksDbWeight::get().writes((1_u64).saturating_mul(r.into()))) - .saturating_add(Weight::from_parts(0, 8167).saturating_mul(r.into())) + .saturating_add(Weight::from_parts(0, 6329).saturating_mul(r.into())) } /// Storage: System Account (r:3 w:2) /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: Measured) @@ -3175,46 +3173,46 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `1154 + t * (204 ±0)` // Estimated: `31015 + t * (5970 ±0)` - // Minimum execution time: 392_113_000 picoseconds. - Weight::from_parts(376_522_702, 31015) - // Standard Error: 1_388_011 - .saturating_add(Weight::from_parts(19_125_093, 0).saturating_mul(t.into())) - // Standard Error: 2 - .saturating_add(Weight::from_parts(609, 0).saturating_mul(c.into())) + // Minimum execution time: 459_675_000 picoseconds. + Weight::from_parts(427_010_987, 31015) + // Standard Error: 1_277_377 + .saturating_add(Weight::from_parts(36_899_889, 0).saturating_mul(t.into())) + // Standard Error: 1 + .saturating_add(Weight::from_parts(651, 0).saturating_mul(c.into())) .saturating_add(RocksDbWeight::get().reads(9_u64)) .saturating_add(RocksDbWeight::get().reads((2_u64).saturating_mul(t.into()))) .saturating_add(RocksDbWeight::get().writes(5_u64)) .saturating_add(RocksDbWeight::get().writes((2_u64).saturating_mul(t.into()))) .saturating_add(Weight::from_parts(0, 5970).saturating_mul(t.into())) } - /// Storage: System Account (r:3202 w:3202) + /// Storage: System Account (r:1602 w:1602) /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: Measured) - /// Storage: Contracts ContractInfoOf (r:1601 w:1601) + /// Storage: Contracts ContractInfoOf (r:801 w:801) /// Proof: Contracts ContractInfoOf (max_values: None, max_size: Some(290), added: 2765, mode: Measured) - /// Storage: Contracts CodeStorage (r:1601 w:0) + /// Storage: Contracts CodeStorage (r:801 w:0) /// Proof: Contracts CodeStorage (max_values: None, max_size: Some(126001), added: 128476, mode: Measured) /// Storage: Timestamp Now (r:1 w:0) /// Proof: Timestamp Now (max_values: Some(1), max_size: Some(8), added: 503, mode: Measured) /// Storage: Contracts Nonce (r:1 w:1) /// Proof: Contracts Nonce (max_values: Some(1), max_size: Some(8), added: 503, mode: Measured) - /// Storage: Contracts OwnerInfoOf (r:1600 w:1600) + /// Storage: Contracts OwnerInfoOf (r:800 w:800) /// Proof: Contracts OwnerInfoOf (max_values: None, max_size: Some(88), added: 2563, mode: Measured) - /// Storage: System EventTopics (r:1602 w:1602) + /// Storage: System EventTopics (r:802 w:802) /// Proof Skipped: System EventTopics (max_values: None, max_size: None, mode: Measured) - /// The range of component `r` is `[0, 1600]`. + /// The range of component `r` is `[0, 800]`. fn seal_instantiate(r: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `1859 + r * (253 ±0)` - // Estimated: `33859 + r * (16628 ±0)` - // Minimum execution time: 234_438_000 picoseconds. - Weight::from_parts(234_751_000, 33859) - // Standard Error: 450_507 - .saturating_add(Weight::from_parts(313_121_404, 0).saturating_mul(r.into())) + // Measured: `1301 + r * (254 ±0)` + // Estimated: `30977 + r * (16635 ±0)` + // Minimum execution time: 285_816_000 picoseconds. + Weight::from_parts(286_349_000, 30977) + // Standard Error: 269_144 + .saturating_add(Weight::from_parts(394_282_520, 0).saturating_mul(r.into())) .saturating_add(RocksDbWeight::get().reads(8_u64)) .saturating_add(RocksDbWeight::get().reads((6_u64).saturating_mul(r.into()))) .saturating_add(RocksDbWeight::get().writes(6_u64)) .saturating_add(RocksDbWeight::get().writes((5_u64).saturating_mul(r.into()))) - .saturating_add(Weight::from_parts(0, 16628).saturating_mul(r.into())) + .saturating_add(Weight::from_parts(0, 16635).saturating_mul(r.into())) } /// Storage: System Account (r:4 w:4) /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: Measured) @@ -3237,14 +3235,14 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `1071 + t * (187 ±0)` // Estimated: `42684 + t * (3588 ±2)` - // Minimum execution time: 1_570_178_000 picoseconds. - Weight::from_parts(284_062_841, 42684) - // Standard Error: 4_396_597 - .saturating_add(Weight::from_parts(106_424_960, 0).saturating_mul(t.into())) + // Minimum execution time: 1_708_330_000 picoseconds. + Weight::from_parts(395_059_764, 42684) + // Standard Error: 4_545_552 + .saturating_add(Weight::from_parts(114_039_862, 0).saturating_mul(t.into())) // Standard Error: 7 - .saturating_add(Weight::from_parts(1_183, 0).saturating_mul(i.into())) + .saturating_add(Weight::from_parts(1_213, 0).saturating_mul(i.into())) // Standard Error: 7 - .saturating_add(Weight::from_parts(1_354, 0).saturating_mul(s.into())) + .saturating_add(Weight::from_parts(1_379, 0).saturating_mul(s.into())) .saturating_add(RocksDbWeight::get().reads(13_u64)) .saturating_add(RocksDbWeight::get().reads((1_u64).saturating_mul(t.into()))) .saturating_add(RocksDbWeight::get().writes(10_u64)) @@ -3266,10 +3264,10 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `777 + r * (8 ±0)` // Estimated: `21710 + r * (40 ±0)` - // Minimum execution time: 230_557_000 picoseconds. - Weight::from_parts(233_276_698, 21710) - // Standard Error: 1_026 - .saturating_add(Weight::from_parts(587_723, 0).saturating_mul(r.into())) + // Minimum execution time: 283_738_000 picoseconds. + Weight::from_parts(289_885_978, 21710) + // Standard Error: 1_057 + .saturating_add(Weight::from_parts(575_432, 0).saturating_mul(r.into())) .saturating_add(RocksDbWeight::get().reads(6_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) .saturating_add(Weight::from_parts(0, 40).saturating_mul(r.into())) @@ -3289,10 +3287,10 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `785` // Estimated: `21745` - // Minimum execution time: 234_151_000 picoseconds. - Weight::from_parts(240_199_365, 21745) - // Standard Error: 3 - .saturating_add(Weight::from_parts(3_941, 0).saturating_mul(n.into())) + // Minimum execution time: 285_070_000 picoseconds. + Weight::from_parts(283_987_687, 21745) + // Standard Error: 6 + .saturating_add(Weight::from_parts(4_008, 0).saturating_mul(n.into())) .saturating_add(RocksDbWeight::get().reads(6_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) } @@ -3311,10 +3309,10 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `779 + r * (8 ±0)` // Estimated: `21725 + r * (40 ±0)` - // Minimum execution time: 229_931_000 picoseconds. - Weight::from_parts(234_961_690, 21725) - // Standard Error: 1_073 - .saturating_add(Weight::from_parts(758_259, 0).saturating_mul(r.into())) + // Minimum execution time: 281_613_000 picoseconds. + Weight::from_parts(285_429_053, 21725) + // Standard Error: 1_164 + .saturating_add(Weight::from_parts(756_244, 0).saturating_mul(r.into())) .saturating_add(RocksDbWeight::get().reads(6_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) .saturating_add(Weight::from_parts(0, 40).saturating_mul(r.into())) @@ -3334,10 +3332,10 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `787` // Estimated: `21765` - // Minimum execution time: 232_448_000 picoseconds. - Weight::from_parts(225_974_008, 21765) + // Minimum execution time: 284_593_000 picoseconds. + Weight::from_parts(278_467_111, 21765) // Standard Error: 2 - .saturating_add(Weight::from_parts(3_174, 0).saturating_mul(n.into())) + .saturating_add(Weight::from_parts(3_217, 0).saturating_mul(n.into())) .saturating_add(RocksDbWeight::get().reads(6_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) } @@ -3356,10 +3354,10 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `779 + r * (8 ±0)` // Estimated: `21740 + r * (40 ±0)` - // Minimum execution time: 229_251_000 picoseconds. - Weight::from_parts(234_182_627, 21740) - // Standard Error: 836 - .saturating_add(Weight::from_parts(424_375, 0).saturating_mul(r.into())) + // Minimum execution time: 281_759_000 picoseconds. + Weight::from_parts(288_807_137, 21740) + // Standard Error: 805 + .saturating_add(Weight::from_parts(424_378, 0).saturating_mul(r.into())) .saturating_add(RocksDbWeight::get().reads(6_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) .saturating_add(Weight::from_parts(0, 40).saturating_mul(r.into())) @@ -3379,10 +3377,10 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `787` // Estimated: `21785` - // Minimum execution time: 230_896_000 picoseconds. - Weight::from_parts(224_913_556, 21785) + // Minimum execution time: 282_666_000 picoseconds. + Weight::from_parts(274_357_944, 21785) // Standard Error: 2 - .saturating_add(Weight::from_parts(919, 0).saturating_mul(n.into())) + .saturating_add(Weight::from_parts(974, 0).saturating_mul(n.into())) .saturating_add(RocksDbWeight::get().reads(6_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) } @@ -3401,10 +3399,10 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `779 + r * (8 ±0)` // Estimated: `21745 + r * (40 ±0)` - // Minimum execution time: 230_314_000 picoseconds. - Weight::from_parts(233_426_327, 21745) - // Standard Error: 926 - .saturating_add(Weight::from_parts(425_341, 0).saturating_mul(r.into())) + // Minimum execution time: 285_073_000 picoseconds. + Weight::from_parts(287_226_796, 21745) + // Standard Error: 951 + .saturating_add(Weight::from_parts(425_368, 0).saturating_mul(r.into())) .saturating_add(RocksDbWeight::get().reads(6_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) .saturating_add(Weight::from_parts(0, 40).saturating_mul(r.into())) @@ -3424,10 +3422,10 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `787` // Estimated: `21755` - // Minimum execution time: 230_424_000 picoseconds. - Weight::from_parts(231_136_218, 21755) + // Minimum execution time: 283_407_000 picoseconds. + Weight::from_parts(276_737_242, 21755) // Standard Error: 2 - .saturating_add(Weight::from_parts(917, 0).saturating_mul(n.into())) + .saturating_add(Weight::from_parts(967, 0).saturating_mul(n.into())) .saturating_add(RocksDbWeight::get().reads(6_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) } @@ -3441,18 +3439,18 @@ impl WeightInfo for () { /// Proof: Timestamp Now (max_values: Some(1), max_size: Some(8), added: 503, mode: Measured) /// Storage: System EventTopics (r:2 w:2) /// Proof Skipped: System EventTopics (max_values: None, max_size: None, mode: Measured) - /// The range of component `r` is `[0, 1600]`. + /// The range of component `r` is `[0, 160]`. fn seal_ecdsa_recover(r: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `704 + r * (78 ±0)` - // Estimated: `21370 + r * (390 ±0)` - // Minimum execution time: 232_862_000 picoseconds. - Weight::from_parts(379_152_870, 21370) - // Standard Error: 11_607 - .saturating_add(Weight::from_parts(36_955_410, 0).saturating_mul(r.into())) + // Measured: `822 + r * (76 ±0)` + // Estimated: `21705 + r * (385 ±0)` + // Minimum execution time: 285_130_000 picoseconds. + Weight::from_parts(299_449_202, 21705) + // Standard Error: 16_535 + .saturating_add(Weight::from_parts(37_655_189, 0).saturating_mul(r.into())) .saturating_add(RocksDbWeight::get().reads(6_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) - .saturating_add(Weight::from_parts(0, 390).saturating_mul(r.into())) + .saturating_add(Weight::from_parts(0, 385).saturating_mul(r.into())) } /// Storage: System Account (r:1 w:0) /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: Measured) @@ -3464,18 +3462,18 @@ impl WeightInfo for () { /// Proof: Timestamp Now (max_values: Some(1), max_size: Some(8), added: 503, mode: Measured) /// Storage: System EventTopics (r:2 w:2) /// Proof Skipped: System EventTopics (max_values: None, max_size: None, mode: Measured) - /// The range of component `r` is `[0, 1600]`. + /// The range of component `r` is `[0, 160]`. fn seal_ecdsa_to_eth_address(r: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `549 + r * (44 ±0)` - // Estimated: `20630 + r * (220 ±0)` - // Minimum execution time: 232_668_000 picoseconds. - Weight::from_parts(253_308_468, 20630) - // Standard Error: 3_022 - .saturating_add(Weight::from_parts(9_249_849, 0).saturating_mul(r.into())) + // Measured: `792 + r * (42 ±0)` + // Estimated: `21780 + r * (210 ±0)` + // Minimum execution time: 284_494_000 picoseconds. + Weight::from_parts(282_154_339, 21780) + // Standard Error: 12_278 + .saturating_add(Weight::from_parts(9_501_559, 0).saturating_mul(r.into())) .saturating_add(RocksDbWeight::get().reads(6_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) - .saturating_add(Weight::from_parts(0, 220).saturating_mul(r.into())) + .saturating_add(Weight::from_parts(0, 210).saturating_mul(r.into())) } /// Storage: System Account (r:1 w:0) /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: Measured) @@ -3494,10 +3492,10 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `0 + r * (964 ±0)` // Estimated: `29920 + r * (11544 ±7)` - // Minimum execution time: 233_910_000 picoseconds. - Weight::from_parts(234_607_000, 29920) - // Standard Error: 46_118 - .saturating_add(Weight::from_parts(21_927_957, 0).saturating_mul(r.into())) + // Minimum execution time: 285_306_000 picoseconds. + Weight::from_parts(286_080_000, 29920) + // Standard Error: 43_813 + .saturating_add(Weight::from_parts(21_758_329, 0).saturating_mul(r.into())) .saturating_add(RocksDbWeight::get().reads(6_u64)) .saturating_add(RocksDbWeight::get().reads((3_u64).saturating_mul(r.into()))) .saturating_add(RocksDbWeight::get().writes(3_u64)) @@ -3519,10 +3517,10 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `773 + r * (3 ±0)` // Estimated: `21735 + r * (15 ±0)` - // Minimum execution time: 231_823_000 picoseconds. - Weight::from_parts(237_996_137, 21735) - // Standard Error: 598 - .saturating_add(Weight::from_parts(163_106, 0).saturating_mul(r.into())) + // Minimum execution time: 283_487_000 picoseconds. + Weight::from_parts(289_280_189, 21735) + // Standard Error: 829 + .saturating_add(Weight::from_parts(168_973, 0).saturating_mul(r.into())) .saturating_add(RocksDbWeight::get().reads(6_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) .saturating_add(Weight::from_parts(0, 15).saturating_mul(r.into())) @@ -3542,10 +3540,10 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `1975 + r * (39 ±0)` // Estimated: `27145 + r * (200 ±0)` - // Minimum execution time: 235_077_000 picoseconds. - Weight::from_parts(267_876_360, 27145) - // Standard Error: 1_147 - .saturating_add(Weight::from_parts(256_275, 0).saturating_mul(r.into())) + // Minimum execution time: 287_413_000 picoseconds. + Weight::from_parts(314_662_286, 27145) + // Standard Error: 1_099 + .saturating_add(Weight::from_parts(262_201, 0).saturating_mul(r.into())) .saturating_add(RocksDbWeight::get().reads(6_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) .saturating_add(Weight::from_parts(0, 200).saturating_mul(r.into())) @@ -3567,10 +3565,10 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `776 + r * (3 ±0)` // Estimated: `24004 + r * (18 ±0)` - // Minimum execution time: 231_384_000 picoseconds. - Weight::from_parts(237_086_900, 24004) - // Standard Error: 534 - .saturating_add(Weight::from_parts(140_842, 0).saturating_mul(r.into())) + // Minimum execution time: 282_601_000 picoseconds. + Weight::from_parts(289_374_203, 24004) + // Standard Error: 452 + .saturating_add(Weight::from_parts(142_661, 0).saturating_mul(r.into())) .saturating_add(RocksDbWeight::get().reads(7_u64)) .saturating_add(RocksDbWeight::get().writes(4_u64)) .saturating_add(Weight::from_parts(0, 18).saturating_mul(r.into())) @@ -3580,509 +3578,507 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_721_000 picoseconds. - Weight::from_parts(2_456_746, 0) - // Standard Error: 30 - .saturating_add(Weight::from_parts(2_788, 0).saturating_mul(r.into())) + // Minimum execution time: 1_692_000 picoseconds. + Weight::from_parts(2_069_482, 0) + // Standard Error: 40 + .saturating_add(Weight::from_parts(2_922, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 5000]`. fn instr_i64load(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_824_000 picoseconds. - Weight::from_parts(2_366_036, 0) - // Standard Error: 5 - .saturating_add(Weight::from_parts(6_446, 0).saturating_mul(r.into())) + // Minimum execution time: 1_690_000 picoseconds. + Weight::from_parts(2_303_602, 0) + // Standard Error: 4 + .saturating_add(Weight::from_parts(6_433, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 5000]`. fn instr_i64store(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_840_000 picoseconds. - Weight::from_parts(2_332_115, 0) + // Minimum execution time: 1_701_000 picoseconds. + Weight::from_parts(2_321_142, 0) // Standard Error: 4 - .saturating_add(Weight::from_parts(6_012, 0).saturating_mul(r.into())) + .saturating_add(Weight::from_parts(6_025, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 5000]`. fn instr_select(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_731_000 picoseconds. - Weight::from_parts(2_078_873, 0) - // Standard Error: 3 - .saturating_add(Weight::from_parts(7_931, 0).saturating_mul(r.into())) + // Minimum execution time: 1_656_000 picoseconds. + Weight::from_parts(2_090_881, 0) + // Standard Error: 5 + .saturating_add(Weight::from_parts(7_941, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 5000]`. fn instr_if(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_768_000 picoseconds. - Weight::from_parts(1_916_551, 0) - // Standard Error: 8 - .saturating_add(Weight::from_parts(10_597, 0).saturating_mul(r.into())) + // Minimum execution time: 1_659_000 picoseconds. + Weight::from_parts(1_816_547, 0) + // Standard Error: 5 + .saturating_add(Weight::from_parts(10_578, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 5000]`. fn instr_br(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_735_000 picoseconds. - Weight::from_parts(1_962_845, 0) - // Standard Error: 14 - .saturating_add(Weight::from_parts(4_631, 0).saturating_mul(r.into())) + // Minimum execution time: 1_683_000 picoseconds. + Weight::from_parts(1_970_907, 0) + // Standard Error: 12 + .saturating_add(Weight::from_parts(4_636, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 5000]`. fn instr_br_if(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_688_000 picoseconds. - Weight::from_parts(1_172_825, 0) - // Standard Error: 76 - .saturating_add(Weight::from_parts(8_007, 0).saturating_mul(r.into())) + // Minimum execution time: 1_651_000 picoseconds. + Weight::from_parts(2_263_817, 0) + // Standard Error: 68 + .saturating_add(Weight::from_parts(7_529, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 5000]`. fn instr_br_table(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_725_000 picoseconds. - Weight::from_parts(1_535_833, 0) - // Standard Error: 26 - .saturating_add(Weight::from_parts(9_489, 0).saturating_mul(r.into())) + // Minimum execution time: 1_687_000 picoseconds. + Weight::from_parts(1_349_186, 0) + // Standard Error: 31 + .saturating_add(Weight::from_parts(9_732, 0).saturating_mul(r.into())) } /// The range of component `e` is `[1, 256]`. - fn instr_br_table_per_entry(e: u32, ) -> Weight { + fn instr_br_table_per_entry(_e: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_814_000 picoseconds. - Weight::from_parts(1_926_367, 0) - // Standard Error: 16 - .saturating_add(Weight::from_parts(219, 0).saturating_mul(e.into())) + // Minimum execution time: 1_777_000 picoseconds. + Weight::from_parts(2_036_446, 0) } /// The range of component `r` is `[0, 5000]`. fn instr_call(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_770_000 picoseconds. - Weight::from_parts(2_436_918, 0) - // Standard Error: 8 - .saturating_add(Weight::from_parts(17_890, 0).saturating_mul(r.into())) + // Minimum execution time: 1_686_000 picoseconds. + Weight::from_parts(464_449, 0) + // Standard Error: 383 + .saturating_add(Weight::from_parts(19_121, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 5000]`. fn instr_call_indirect(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_920_000 picoseconds. - Weight::from_parts(2_729_712, 0) - // Standard Error: 39 - .saturating_add(Weight::from_parts(24_853, 0).saturating_mul(r.into())) + // Minimum execution time: 1_855_000 picoseconds. + Weight::from_parts(3_381_585, 0) + // Standard Error: 18 + .saturating_add(Weight::from_parts(24_245, 0).saturating_mul(r.into())) } /// The range of component `l` is `[0, 1024]`. fn instr_call_per_local(l: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_869_000 picoseconds. - Weight::from_parts(2_147_970, 0) - // Standard Error: 46 - .saturating_add(Weight::from_parts(1_237, 0).saturating_mul(l.into())) + // Minimum execution time: 1_792_000 picoseconds. + Weight::from_parts(2_006_024, 0) + // Standard Error: 16 + .saturating_add(Weight::from_parts(2_181, 0).saturating_mul(l.into())) } /// The range of component `r` is `[0, 5000]`. fn instr_local_get(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 3_012_000 picoseconds. - Weight::from_parts(3_234_440, 0) - // Standard Error: 1 - .saturating_add(Weight::from_parts(2_433, 0).saturating_mul(r.into())) + // Minimum execution time: 3_918_000 picoseconds. + Weight::from_parts(4_618_761, 0) + // Standard Error: 49 + .saturating_add(Weight::from_parts(2_312, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 5000]`. fn instr_local_set(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 3_037_000 picoseconds. - Weight::from_parts(3_204_600, 0) + // Minimum execution time: 3_889_000 picoseconds. + Weight::from_parts(4_151_280, 0) // Standard Error: 2 - .saturating_add(Weight::from_parts(3_627, 0).saturating_mul(r.into())) + .saturating_add(Weight::from_parts(3_623, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 5000]`. fn instr_local_tee(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 2_974_000 picoseconds. - Weight::from_parts(3_301_366, 0) - // Standard Error: 2 - .saturating_add(Weight::from_parts(3_836, 0).saturating_mul(r.into())) + // Minimum execution time: 3_880_000 picoseconds. + Weight::from_parts(4_225_780, 0) + // Standard Error: 3 + .saturating_add(Weight::from_parts(3_847, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 5000]`. fn instr_global_get(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_853_000 picoseconds. - Weight::from_parts(2_310_141, 0) + // Minimum execution time: 1_765_000 picoseconds. + Weight::from_parts(2_216_674, 0) // Standard Error: 4 - .saturating_add(Weight::from_parts(8_387, 0).saturating_mul(r.into())) + .saturating_add(Weight::from_parts(8_393, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 5000]`. fn instr_global_set(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_828_000 picoseconds. - Weight::from_parts(2_315_323, 0) - // Standard Error: 4 - .saturating_add(Weight::from_parts(8_797, 0).saturating_mul(r.into())) + // Minimum execution time: 1_764_000 picoseconds. + Weight::from_parts(2_246_735, 0) + // Standard Error: 5 + .saturating_add(Weight::from_parts(8_877, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 5000]`. fn instr_memory_current(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_800_000 picoseconds. - Weight::from_parts(2_117_252, 0) - // Standard Error: 4 - .saturating_add(Weight::from_parts(3_741, 0).saturating_mul(r.into())) + // Minimum execution time: 1_758_000 picoseconds. + Weight::from_parts(1_922_386, 0) + // Standard Error: 94 + .saturating_add(Weight::from_parts(3_868, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 16]`. fn instr_memory_grow(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_743_000 picoseconds. - Weight::from_parts(1_054_728, 0) - // Standard Error: 137_128 - .saturating_add(Weight::from_parts(13_179_446, 0).saturating_mul(r.into())) + // Minimum execution time: 1_635_000 picoseconds. + Weight::from_parts(1_118_785, 0) + // Standard Error: 134_978 + .saturating_add(Weight::from_parts(16_343_664, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 5000]`. fn instr_i64clz(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_735_000 picoseconds. - Weight::from_parts(2_026_568, 0) + // Minimum execution time: 1_648_000 picoseconds. + Weight::from_parts(2_012_545, 0) // Standard Error: 2 - .saturating_add(Weight::from_parts(3_802, 0).saturating_mul(r.into())) + .saturating_add(Weight::from_parts(3_824, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 5000]`. fn instr_i64ctz(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_630_000 picoseconds. - Weight::from_parts(1_980_222, 0) + // Minimum execution time: 1_688_000 picoseconds. + Weight::from_parts(1_995_956, 0) // Standard Error: 2 - .saturating_add(Weight::from_parts(3_735, 0).saturating_mul(r.into())) + .saturating_add(Weight::from_parts(3_757, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 5000]`. fn instr_i64popcnt(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_695_000 picoseconds. - Weight::from_parts(2_040_266, 0) - // Standard Error: 8 - .saturating_add(Weight::from_parts(3_718, 0).saturating_mul(r.into())) + // Minimum execution time: 1_631_000 picoseconds. + Weight::from_parts(2_011_493, 0) + // Standard Error: 3 + .saturating_add(Weight::from_parts(3_755, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 5000]`. fn instr_i64eqz(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_697_000 picoseconds. - Weight::from_parts(2_004_663, 0) - // Standard Error: 3 - .saturating_add(Weight::from_parts(3_661, 0).saturating_mul(r.into())) + // Minimum execution time: 1_667_000 picoseconds. + Weight::from_parts(1_958_798, 0) + // Standard Error: 2 + .saturating_add(Weight::from_parts(3_677, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 5000]`. fn instr_i64extendsi32(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_705_000 picoseconds. - Weight::from_parts(2_682_122, 0) - // Standard Error: 98 - .saturating_add(Weight::from_parts(3_705, 0).saturating_mul(r.into())) + // Minimum execution time: 1_674_000 picoseconds. + Weight::from_parts(2_009_555, 0) + // Standard Error: 7 + .saturating_add(Weight::from_parts(3_863, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 5000]`. fn instr_i64extendui32(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_674_000 picoseconds. - Weight::from_parts(1_989_835, 0) + // Minimum execution time: 1_659_000 picoseconds. + Weight::from_parts(2_014_985, 0) // Standard Error: 2 - .saturating_add(Weight::from_parts(3_801, 0).saturating_mul(r.into())) + .saturating_add(Weight::from_parts(3_821, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 5000]`. fn instr_i32wrapi64(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_695_000 picoseconds. - Weight::from_parts(2_083_824, 0) - // Standard Error: 17 - .saturating_add(Weight::from_parts(3_719, 0).saturating_mul(r.into())) + // Minimum execution time: 1_640_000 picoseconds. + Weight::from_parts(2_013_939, 0) + // Standard Error: 2 + .saturating_add(Weight::from_parts(3_708, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 5000]`. fn instr_i64eq(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_678_000 picoseconds. - Weight::from_parts(1_996_335, 0) - // Standard Error: 4 - .saturating_add(Weight::from_parts(5_995, 0).saturating_mul(r.into())) + // Minimum execution time: 1_631_000 picoseconds. + Weight::from_parts(2_002_814, 0) + // Standard Error: 2 + .saturating_add(Weight::from_parts(6_008, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 5000]`. fn instr_i64ne(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_697_000 picoseconds. - Weight::from_parts(2_046_566, 0) + // Minimum execution time: 1_647_000 picoseconds. + Weight::from_parts(2_032_158, 0) // Standard Error: 3 - .saturating_add(Weight::from_parts(5_936, 0).saturating_mul(r.into())) + .saturating_add(Weight::from_parts(5_944, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 5000]`. fn instr_i64lts(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_721_000 picoseconds. - Weight::from_parts(2_406_930, 0) - // Standard Error: 150 - .saturating_add(Weight::from_parts(5_992, 0).saturating_mul(r.into())) + // Minimum execution time: 1_669_000 picoseconds. + Weight::from_parts(2_040_386, 0) + // Standard Error: 3 + .saturating_add(Weight::from_parts(6_009, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 5000]`. fn instr_i64ltu(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_749_000 picoseconds. - Weight::from_parts(1_987_158, 0) - // Standard Error: 2 - .saturating_add(Weight::from_parts(5_994, 0).saturating_mul(r.into())) + // Minimum execution time: 1_637_000 picoseconds. + Weight::from_parts(1_983_695, 0) + // Standard Error: 4 + .saturating_add(Weight::from_parts(6_027, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 5000]`. fn instr_i64gts(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_706_000 picoseconds. - Weight::from_parts(2_038_138, 0) + // Minimum execution time: 1_701_000 picoseconds. + Weight::from_parts(2_054_295, 0) // Standard Error: 2 - .saturating_add(Weight::from_parts(5_802, 0).saturating_mul(r.into())) + .saturating_add(Weight::from_parts(5_799, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 5000]`. fn instr_i64gtu(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_670_000 picoseconds. - Weight::from_parts(2_555_035, 0) - // Standard Error: 74 - .saturating_add(Weight::from_parts(5_958, 0).saturating_mul(r.into())) + // Minimum execution time: 1_653_000 picoseconds. + Weight::from_parts(2_749_807, 0) + // Standard Error: 90 + .saturating_add(Weight::from_parts(5_945, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 5000]`. fn instr_i64les(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_694_000 picoseconds. - Weight::from_parts(1_991_929, 0) - // Standard Error: 27 - .saturating_add(Weight::from_parts(6_037, 0).saturating_mul(r.into())) + // Minimum execution time: 1_651_000 picoseconds. + Weight::from_parts(1_979_111, 0) + // Standard Error: 3 + .saturating_add(Weight::from_parts(6_011, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 5000]`. fn instr_i64leu(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_684_000 picoseconds. - Weight::from_parts(1_969_284, 0) - // Standard Error: 2 - .saturating_add(Weight::from_parts(6_104, 0).saturating_mul(r.into())) + // Minimum execution time: 1_743_000 picoseconds. + Weight::from_parts(2_058_081, 0) + // Standard Error: 3 + .saturating_add(Weight::from_parts(6_085, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 5000]`. fn instr_i64ges(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_697_000 picoseconds. - Weight::from_parts(2_023_608, 0) + // Minimum execution time: 1_638_000 picoseconds. + Weight::from_parts(2_038_929, 0) // Standard Error: 3 - .saturating_add(Weight::from_parts(5_946, 0).saturating_mul(r.into())) + .saturating_add(Weight::from_parts(5_941, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 5000]`. fn instr_i64geu(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_659_000 picoseconds. - Weight::from_parts(1_963_145, 0) - // Standard Error: 6 - .saturating_add(Weight::from_parts(6_014, 0).saturating_mul(r.into())) + // Minimum execution time: 1_641_000 picoseconds. + Weight::from_parts(2_036_587, 0) + // Standard Error: 2 + .saturating_add(Weight::from_parts(6_008, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 5000]`. fn instr_i64add(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_722_000 picoseconds. - Weight::from_parts(2_016_286, 0) + // Minimum execution time: 1_624_000 picoseconds. + Weight::from_parts(2_080_562, 0) // Standard Error: 3 - .saturating_add(Weight::from_parts(5_889, 0).saturating_mul(r.into())) + .saturating_add(Weight::from_parts(5_826, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 5000]`. fn instr_i64sub(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_656_000 picoseconds. - Weight::from_parts(1_966_234, 0) - // Standard Error: 17 - .saturating_add(Weight::from_parts(6_171, 0).saturating_mul(r.into())) + // Minimum execution time: 1_652_000 picoseconds. + Weight::from_parts(2_039_535, 0) + // Standard Error: 2 + .saturating_add(Weight::from_parts(6_137, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 5000]`. fn instr_i64mul(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_695_000 picoseconds. - Weight::from_parts(2_004_559, 0) - // Standard Error: 2 - .saturating_add(Weight::from_parts(5_785, 0).saturating_mul(r.into())) + // Minimum execution time: 1_666_000 picoseconds. + Weight::from_parts(2_056_354, 0) + // Standard Error: 6 + .saturating_add(Weight::from_parts(5_780, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 5000]`. fn instr_i64divs(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_681_000 picoseconds. - Weight::from_parts(2_135_505, 0) - // Standard Error: 6 - .saturating_add(Weight::from_parts(11_761, 0).saturating_mul(r.into())) + // Minimum execution time: 1_648_000 picoseconds. + Weight::from_parts(2_077_695, 0) + // Standard Error: 4 + .saturating_add(Weight::from_parts(11_775, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 5000]`. fn instr_i64divu(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_653_000 picoseconds. - Weight::from_parts(2_153_167, 0) - // Standard Error: 5 - .saturating_add(Weight::from_parts(10_523, 0).saturating_mul(r.into())) + // Minimum execution time: 1_797_000 picoseconds. + Weight::from_parts(2_772_388, 0) + // Standard Error: 33 + .saturating_add(Weight::from_parts(10_333, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 5000]`. fn instr_i64rems(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_697_000 picoseconds. - Weight::from_parts(2_570_266, 0) - // Standard Error: 50 - .saturating_add(Weight::from_parts(11_971, 0).saturating_mul(r.into())) + // Minimum execution time: 1_699_000 picoseconds. + Weight::from_parts(2_174_288, 0) + // Standard Error: 6 + .saturating_add(Weight::from_parts(11_778, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 5000]`. fn instr_i64remu(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_719_000 picoseconds. - Weight::from_parts(2_101_082, 0) + // Minimum execution time: 1_685_000 picoseconds. + Weight::from_parts(2_091_037, 0) // Standard Error: 3 - .saturating_add(Weight::from_parts(10_709, 0).saturating_mul(r.into())) + .saturating_add(Weight::from_parts(10_694, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 5000]`. fn instr_i64and(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_745_000 picoseconds. - Weight::from_parts(2_621_890, 0) - // Standard Error: 94 - .saturating_add(Weight::from_parts(5_545, 0).saturating_mul(r.into())) + // Minimum execution time: 1_636_000 picoseconds. + Weight::from_parts(1_975_521, 0) + // Standard Error: 2 + .saturating_add(Weight::from_parts(5_695, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 5000]`. fn instr_i64or(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_670_000 picoseconds. - Weight::from_parts(2_046_821, 0) + // Minimum execution time: 1_619_000 picoseconds. + Weight::from_parts(2_045_492, 0) // Standard Error: 2 - .saturating_add(Weight::from_parts(5_762, 0).saturating_mul(r.into())) + .saturating_add(Weight::from_parts(5_770, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 5000]`. fn instr_i64xor(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_714_000 picoseconds. - Weight::from_parts(2_010_173, 0) - // Standard Error: 2 - .saturating_add(Weight::from_parts(5_911, 0).saturating_mul(r.into())) + // Minimum execution time: 1_668_000 picoseconds. + Weight::from_parts(2_055_460, 0) + // Standard Error: 3 + .saturating_add(Weight::from_parts(5_851, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 5000]`. fn instr_i64shl(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_756_000 picoseconds. - Weight::from_parts(2_044_772, 0) + // Minimum execution time: 1_681_000 picoseconds. + Weight::from_parts(2_023_370, 0) // Standard Error: 2 - .saturating_add(Weight::from_parts(5_857, 0).saturating_mul(r.into())) + .saturating_add(Weight::from_parts(5_853, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 5000]`. fn instr_i64shrs(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_741_000 picoseconds. - Weight::from_parts(2_012_819, 0) - // Standard Error: 2 - .saturating_add(Weight::from_parts(6_127, 0).saturating_mul(r.into())) + // Minimum execution time: 1_714_000 picoseconds. + Weight::from_parts(2_067_584, 0) + // Standard Error: 3 + .saturating_add(Weight::from_parts(6_133, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 5000]`. fn instr_i64shru(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_693_000 picoseconds. - Weight::from_parts(2_090_981, 0) - // Standard Error: 2 - .saturating_add(Weight::from_parts(5_839, 0).saturating_mul(r.into())) + // Minimum execution time: 1_602_000 picoseconds. + Weight::from_parts(2_055_530, 0) + // Standard Error: 138 + .saturating_add(Weight::from_parts(6_014, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 5000]`. fn instr_i64rotl(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_704_000 picoseconds. - Weight::from_parts(2_014_108, 0) - // Standard Error: 4 - .saturating_add(Weight::from_parts(6_005, 0).saturating_mul(r.into())) + // Minimum execution time: 1_709_000 picoseconds. + Weight::from_parts(2_016_365, 0) + // Standard Error: 2 + .saturating_add(Weight::from_parts(5_985, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 5000]`. fn instr_i64rotr(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_715_000 picoseconds. - Weight::from_parts(2_053_720, 0) - // Standard Error: 5 - .saturating_add(Weight::from_parts(5_860, 0).saturating_mul(r.into())) + // Minimum execution time: 1_661_000 picoseconds. + Weight::from_parts(2_003_063, 0) + // Standard Error: 2 + .saturating_add(Weight::from_parts(5_863, 0).saturating_mul(r.into())) } } From a1cf6ee484c28e313df8082c1efc643ba54810b7 Mon Sep 17 00:00:00 2001 From: Sergejs Kostjucenko <85877331+sergejparity@users.noreply.github.com> Date: Tue, 21 Mar 2023 14:05:26 +0200 Subject: [PATCH 269/558] fix publish docker description ci job (#13663) * fix publish docker description ci job * remove publish-refs from docker description jobs --- scripts/ci/gitlab/pipeline/publish.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/scripts/ci/gitlab/pipeline/publish.yml b/scripts/ci/gitlab/pipeline/publish.yml index 188a09386..6a3d928f6 100644 --- a/scripts/ci/gitlab/pipeline/publish.yml +++ b/scripts/ci/gitlab/pipeline/publish.yml @@ -52,7 +52,6 @@ stage: publish extends: - .kubernetes-env - - .publish-refs variables: CI_IMAGE: paritytech/dockerhub-description DOCKERHUB_REPOSITORY: parity/$PRODUCT @@ -63,6 +62,8 @@ - if: $CI_COMMIT_REF_NAME == "master" changes: - scripts/ci/docker/$PRODUCT.Dockerfile.README.md + before_script: + - echo script: - cd / && sh entrypoint.sh From b3cacfb059e9d79d1cde270c796dc0f33ff3ef20 Mon Sep 17 00:00:00 2001 From: Mira Ressel Date: Tue, 21 Mar 2023 13:06:55 +0100 Subject: [PATCH 270/558] only push docker descriptions on master branch pipelines (#13664) Currently those jobs also get included in multi-project pipelines such as https://gitlab.parity.io/parity/mirrors/scripts/-/pipelines/255546, even when the description hasn't actually changed, because `changes:` conditions always evaluate to true on non-branch pipelines. --- scripts/ci/gitlab/pipeline/publish.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/ci/gitlab/pipeline/publish.yml b/scripts/ci/gitlab/pipeline/publish.yml index 6a3d928f6..a77209644 100644 --- a/scripts/ci/gitlab/pipeline/publish.yml +++ b/scripts/ci/gitlab/pipeline/publish.yml @@ -59,7 +59,7 @@ DOCKER_PASSWORD: $Docker_Hub_Pass_Parity README_FILEPATH: $CI_PROJECT_DIR/scripts/ci/docker/$PRODUCT.Dockerfile.README.md rules: - - if: $CI_COMMIT_REF_NAME == "master" + - if: $CI_COMMIT_REF_NAME == $CI_DEFAULT_BRANCH && $CI_PIPELINE_SOURCE == "push" changes: - scripts/ci/docker/$PRODUCT.Dockerfile.README.md before_script: From a14236059c2d3da052fb08295082341aa7b87240 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 21 Mar 2023 13:53:04 +0000 Subject: [PATCH 271/558] Bump k256 from 0.11.6 to 0.13.0 (#13624) Bumps [k256](https://github.com/RustCrypto/elliptic-curves) from 0.11.6 to 0.13.0. - [Release notes](https://github.com/RustCrypto/elliptic-curves/releases) - [Commits](https://github.com/RustCrypto/elliptic-curves/compare/k256/v0.11.6...k256/v0.13.0) --- updated-dependencies: - dependency-name: k256 dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Davide Galassi --- Cargo.lock | 202 ++++++++++++++++++++++++++++++++------- frame/support/Cargo.toml | 2 +- 2 files changed, 170 insertions(+), 34 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index e2ec461e1..4a0cc4cd3 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -484,6 +484,12 @@ version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "349a06037c7bf932dd7e7d1f653678b2038b9ad46a74102f1fc7bd7872678cce" +[[package]] +name = "base16ct" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4c7f02d4ea65f2c1853089ffd8d2787bdbc63de2f0d29dedbcf8ccdfa0ccd4cf" + [[package]] name = "base58" version = "0.2.0" @@ -1374,6 +1380,18 @@ dependencies = [ "zeroize", ] +[[package]] +name = "crypto-bigint" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7c2538c4e68e52548bacb3e83ac549f903d44f011ac9d5abb5e132e67d0808f7" +dependencies = [ + "generic-array 0.14.6", + "rand_core 0.6.4", + "subtle", + "zeroize", +] + [[package]] name = "crypto-common" version = "0.1.6" @@ -1589,6 +1607,16 @@ dependencies = [ "zeroize", ] +[[package]] +name = "der" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bc906908ea6458456e5eaa160a9c08543ec3d1e6f71e2235cedd660cb65f9df0" +dependencies = [ + "const-oid", + "zeroize", +] + [[package]] name = "der-parser" version = "7.0.0" @@ -1826,10 +1854,22 @@ version = "0.14.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "413301934810f597c1d19ca71c8710e99a3f1ba28a0d2ebc01551a2daeea3c5c" dependencies = [ - "der", - "elliptic-curve", - "rfc6979", - "signature", + "der 0.6.1", + "elliptic-curve 0.12.3", + "rfc6979 0.3.1", + "signature 1.6.4", +] + +[[package]] +name = "ecdsa" +version = "0.16.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d1b0a1222f8072619e8a6b667a854020a03d363738303203c09468b3424a420a" +dependencies = [ + "der 0.7.1", + "elliptic-curve 0.13.2", + "rfc6979 0.4.0", + "signature 2.0.0", ] [[package]] @@ -1838,7 +1878,7 @@ version = "1.5.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "91cff35c70bba8a626e3185d8cd48cc11b5437e1a5bcd15b9b5fa3c64b6dfee7" dependencies = [ - "signature", + "signature 1.6.4", ] [[package]] @@ -1881,18 +1921,37 @@ version = "0.12.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e7bb888ab5300a19b8e5bceef25ac745ad065f3c9f7efc6de1b91958110891d3" dependencies = [ - "base16ct", - "crypto-bigint", - "der", + "base16ct 0.1.1", + "crypto-bigint 0.4.9", + "der 0.6.1", "digest 0.10.6", - "ff", + "ff 0.12.1", "generic-array 0.14.6", - "group", + "group 0.12.1", "hkdf", "pem-rfc7468", - "pkcs8", + "pkcs8 0.9.0", "rand_core 0.6.4", - "sec1", + "sec1 0.3.0", + "subtle", + "zeroize", +] + +[[package]] +name = "elliptic-curve" +version = "0.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6ea5a92946e8614bb585254898bb7dd1ddad241ace60c52149e3765e34cc039d" +dependencies = [ + "base16ct 0.2.0", + "crypto-bigint 0.5.1", + "digest 0.10.6", + "ff 0.13.0", + "generic-array 0.14.6", + "group 0.13.0", + "pkcs8 0.10.1", + "rand_core 0.6.4", + "sec1 0.7.1", "subtle", "zeroize", ] @@ -2050,6 +2109,16 @@ dependencies = [ "subtle", ] +[[package]] +name = "ff" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ded41244b729663b1e574f1b4fb731469f69f79c17667b5d776b16cda0479449" +dependencies = [ + "rand_core 0.6.4", + "subtle", +] + [[package]] name = "fiat-crypto" version = "0.1.17" @@ -2720,6 +2789,7 @@ checksum = "bff49e947297f3312447abdca79f45f4738097cc82b06e72054d2223f601f1b9" dependencies = [ "typenum", "version_check", + "zeroize", ] [[package]] @@ -2829,7 +2899,18 @@ version = "0.12.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5dfbfb3a6cfbd390d5c9564ab283a0349b9b9fcd46a706c1eb10e0db70bfbac7" dependencies = [ - "ff", + "ff 0.12.1", + "rand_core 0.6.4", + "subtle", +] + +[[package]] +name = "group" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0f9ef7462f7c099f518d754361858f86d8a07af53ba9af0fe635bbccb151a63" +dependencies = [ + "ff 0.13.0", "rand_core 0.6.4", "subtle", ] @@ -3482,13 +3563,14 @@ dependencies = [ [[package]] name = "k256" -version = "0.11.6" +version = "0.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "72c1e0b51e7ec0a97369623508396067a486bd0cbed95a2659a4b863d28cfc8b" +checksum = "955890845095ccf31ef83ad41a05aabb4d8cc23dc3cac5a9f5c89cf26dd0da75" dependencies = [ "cfg-if", - "ecdsa", - "elliptic-curve", + "ecdsa 0.16.1", + "elliptic-curve 0.13.2", + "once_cell", "sha2 0.10.6", ] @@ -3764,7 +3846,7 @@ dependencies = [ "prost-build", "rand 0.8.5", "rw-stream-sink", - "sec1", + "sec1 0.3.0", "sha2 0.10.6", "smallvec", "thiserror", @@ -3798,7 +3880,7 @@ dependencies = [ "prost-build", "rand 0.8.5", "rw-stream-sink", - "sec1", + "sec1 0.3.0", "sha2 0.10.6", "smallvec", "thiserror", @@ -5372,8 +5454,8 @@ version = "0.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "51f44edd08f51e2ade572f141051021c5af22677e42b7dd28a88155151c33594" dependencies = [ - "ecdsa", - "elliptic-curve", + "ecdsa 0.14.8", + "elliptic-curve 0.12.3", "sha2 0.10.6", ] @@ -5383,8 +5465,8 @@ version = "0.11.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dfc8c5bf642dde52bb9e87c0ecd8ca5a76faac2eeed98dedb7c717997e1080aa" dependencies = [ - "ecdsa", - "elliptic-curve", + "ecdsa 0.14.8", + "elliptic-curve 0.12.3", "sha2 0.10.6", ] @@ -7188,8 +7270,18 @@ version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9eca2c590a5f85da82668fa685c09ce2888b9430e83299debf1f34b65fd4a4ba" dependencies = [ - "der", - "spki", + "der 0.6.1", + "spki 0.6.0", +] + +[[package]] +name = "pkcs8" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3d2820d87d2b008616e5c27212dd9e0e694fb4c6b522de06094106813328cb49" +dependencies = [ + "der 0.7.1", + "spki 0.7.0", ] [[package]] @@ -7822,11 +7914,21 @@ version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7743f17af12fa0b03b803ba12cd6a8d9483a587e89c69445e3909655c0b9fabb" dependencies = [ - "crypto-bigint", + "crypto-bigint 0.4.9", "hmac 0.12.1", "zeroize", ] +[[package]] +name = "rfc6979" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8dd2a808d456c4a54e300a23e9f5a67e122c3024119acbfd73e3bf664491cb2" +dependencies = [ + "hmac 0.12.1", + "subtle", +] + [[package]] name = "ring" version = "0.16.20" @@ -9605,10 +9707,24 @@ version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3be24c1842290c45df0a7bf069e0c268a747ad05a192f2fd7dcfdbc1cba40928" dependencies = [ - "base16ct", - "der", + "base16ct 0.1.1", + "der 0.6.1", "generic-array 0.14.6", - "pkcs8", + "pkcs8 0.9.0", + "subtle", + "zeroize", +] + +[[package]] +name = "sec1" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "48518a2b5775ba8ca5b46596aae011caa431e6ce7e4a67ead66d92f08884220e" +dependencies = [ + "base16ct 0.2.0", + "der 0.7.1", + "generic-array 0.14.6", + "pkcs8 0.10.1", "subtle", "zeroize", ] @@ -9831,6 +9947,16 @@ dependencies = [ "rand_core 0.6.4", ] +[[package]] +name = "signature" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8fe458c98333f9c8152221191a77e2a44e8325d0193484af2e9421a53019e57d" +dependencies = [ + "digest 0.10.6", + "rand_core 0.6.4", +] + [[package]] name = "simba" version = "0.8.0" @@ -10764,7 +10890,17 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "67cf02bbac7a337dc36e4f5a693db6c21e7863f45070f7064577eb4367a3212b" dependencies = [ "base64ct", - "der", + "der 0.6.1", +] + +[[package]] +name = "spki" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c0445c905640145c7ea8c1993555957f65e7c46d0535b91ba501bc9bfc85522f" +dependencies = [ + "base64ct", + "der 0.7.1", ] [[package]] @@ -12568,7 +12704,7 @@ dependencies = [ "ccm", "curve25519-dalek 3.2.0", "der-parser 8.1.0", - "elliptic-curve", + "elliptic-curve 0.12.3", "hkdf", "hmac 0.12.1", "log", @@ -12580,11 +12716,11 @@ dependencies = [ "rcgen 0.9.3", "ring", "rustls 0.19.1", - "sec1", + "sec1 0.3.0", "serde", "sha1", "sha2 0.10.6", - "signature", + "signature 1.6.4", "subtle", "thiserror", "tokio", diff --git a/frame/support/Cargo.toml b/frame/support/Cargo.toml index 008d4b3f2..820658372 100644 --- a/frame/support/Cargo.toml +++ b/frame/support/Cargo.toml @@ -37,7 +37,7 @@ impl-trait-for-tuples = "0.2.2" smallvec = "1.8.0" log = { version = "0.4.17", default-features = false } sp-core-hashing-proc-macro = { version = "5.0.0", path = "../../primitives/core/hashing/proc-macro" } -k256 = { version = "0.11.5", default-features = false, features = ["ecdsa"] } +k256 = { version = "0.13.0", default-features = false, features = ["ecdsa"] } environmental = { version = "1.1.4", default-features = false } [dev-dependencies] From 936e995d91be03f8efe7e75b1be2f6ad1e3a60e9 Mon Sep 17 00:00:00 2001 From: Niklas Adolfsson Date: Tue, 21 Mar 2023 16:25:33 +0100 Subject: [PATCH 272/558] refactor: state RPC remove `max_rpc_payload_size` (#13649) This limit is not needed anymore as the JSON-RPC servers doesn't terminate the connection if the RPC max limit is exceeded anymore --- client/rpc/src/state/mod.rs | 10 ++---- client/rpc/src/state/state_full.rs | 10 ++---- client/rpc/src/state/tests.rs | 24 +++++++------- client/service/src/builder.rs | 8 ++--- client/tracing/src/block/mod.rs | 53 +++++++----------------------- 5 files changed, 30 insertions(+), 75 deletions(-) diff --git a/client/rpc/src/state/mod.rs b/client/rpc/src/state/mod.rs index 12d59ad3b..f81ec991d 100644 --- a/client/rpc/src/state/mod.rs +++ b/client/rpc/src/state/mod.rs @@ -169,7 +169,6 @@ pub fn new_full( client: Arc, executor: SubscriptionTaskExecutor, deny_unsafe: DenyUnsafe, - rpc_max_payload: Option, ) -> (State, ChildState) where Block: BlockT + 'static, @@ -189,12 +188,9 @@ where + 'static, Client::Api: Metadata, { - let child_backend = Box::new(self::state_full::FullState::new( - client.clone(), - executor.clone(), - rpc_max_payload, - )); - let backend = Box::new(self::state_full::FullState::new(client, executor, rpc_max_payload)); + let child_backend = + Box::new(self::state_full::FullState::new(client.clone(), executor.clone())); + let backend = Box::new(self::state_full::FullState::new(client, executor)); (State { backend, deny_unsafe }, ChildState { backend: child_backend }) } diff --git a/client/rpc/src/state/state_full.rs b/client/rpc/src/state/state_full.rs index f26d42484..20ca5f713 100644 --- a/client/rpc/src/state/state_full.rs +++ b/client/rpc/src/state/state_full.rs @@ -66,7 +66,6 @@ pub struct FullState { client: Arc, executor: SubscriptionTaskExecutor, _phantom: PhantomData<(BE, Block)>, - rpc_max_payload: Option, } impl FullState @@ -79,12 +78,8 @@ where Block: BlockT + 'static, { /// Create new state API backend for full nodes. - pub fn new( - client: Arc, - executor: SubscriptionTaskExecutor, - rpc_max_payload: Option, - ) -> Self { - Self { client, executor, _phantom: PhantomData, rpc_max_payload } + pub fn new(client: Arc, executor: SubscriptionTaskExecutor) -> Self { + Self { client, executor, _phantom: PhantomData } } /// Returns given block hash or best block hash if None is passed. @@ -481,7 +476,6 @@ where targets, storage_keys, methods, - self.rpc_max_payload, ) .trace_block() .map_err(|e| invalid_block::(block, None, e.to_string())) diff --git a/client/rpc/src/state/tests.rs b/client/rpc/src/state/tests.rs index 1ccc609e4..ae193e662 100644 --- a/client/rpc/src/state/tests.rs +++ b/client/rpc/src/state/tests.rs @@ -55,7 +55,7 @@ async fn should_return_storage() { .add_extra_storage(b":map:acc2".to_vec(), vec![1, 2, 3]) .build(); let genesis_hash = client.genesis_hash(); - let (client, child) = new_full(Arc::new(client), test_executor(), DenyUnsafe::No, None); + let (client, child) = new_full(Arc::new(client), test_executor(), DenyUnsafe::No); let key = StorageKey(KEY.to_vec()); assert_eq!( @@ -103,7 +103,7 @@ async fn should_return_storage_entries() { .add_extra_child_storage(&child_info, KEY2.to_vec(), CHILD_VALUE2.to_vec()) .build(); let genesis_hash = client.genesis_hash(); - let (_client, child) = new_full(Arc::new(client), test_executor(), DenyUnsafe::No, None); + let (_client, child) = new_full(Arc::new(client), test_executor(), DenyUnsafe::No); let keys = &[StorageKey(KEY1.to_vec()), StorageKey(KEY2.to_vec())]; assert_eq!( @@ -134,7 +134,7 @@ async fn should_return_child_storage() { .build(), ); let genesis_hash = client.genesis_hash(); - let (_client, child) = new_full(client, test_executor(), DenyUnsafe::No, None); + let (_client, child) = new_full(client, test_executor(), DenyUnsafe::No); let child_key = prefixed_storage_key(); let key = StorageKey(b"key".to_vec()); @@ -165,7 +165,7 @@ async fn should_return_child_storage_entries() { .build(), ); let genesis_hash = client.genesis_hash(); - let (_client, child) = new_full(client, test_executor(), DenyUnsafe::No, None); + let (_client, child) = new_full(client, test_executor(), DenyUnsafe::No); let child_key = prefixed_storage_key(); let keys = vec![StorageKey(b"key1".to_vec()), StorageKey(b"key2".to_vec())]; @@ -196,7 +196,7 @@ async fn should_return_child_storage_entries() { async fn should_call_contract() { let client = Arc::new(substrate_test_runtime_client::new()); let genesis_hash = client.genesis_hash(); - let (client, _child) = new_full(client, test_executor(), DenyUnsafe::No, None); + let (client, _child) = new_full(client, test_executor(), DenyUnsafe::No); use jsonrpsee::{core::Error, types::error::CallError}; @@ -210,7 +210,7 @@ async fn should_call_contract() { async fn should_notify_about_storage_changes() { let mut sub = { let mut client = Arc::new(substrate_test_runtime_client::new()); - let (api, _child) = new_full(client.clone(), test_executor(), DenyUnsafe::No, None); + let (api, _child) = new_full(client.clone(), test_executor(), DenyUnsafe::No); let api_rpc = api.into_rpc(); let sub = api_rpc.subscribe("state_subscribeStorage", EmptyParams::new()).await.unwrap(); @@ -242,7 +242,7 @@ async fn should_notify_about_storage_changes() { async fn should_send_initial_storage_changes_and_notifications() { let mut sub = { let mut client = Arc::new(substrate_test_runtime_client::new()); - let (api, _child) = new_full(client.clone(), test_executor(), DenyUnsafe::No, None); + let (api, _child) = new_full(client.clone(), test_executor(), DenyUnsafe::No); let alice_balance_key = blake2_256(&runtime::system::balance_of_key(AccountKeyring::Alice.into())); @@ -278,7 +278,7 @@ async fn should_send_initial_storage_changes_and_notifications() { #[tokio::test] async fn should_query_storage() { async fn run_tests(mut client: Arc) { - let (api, _child) = new_full(client.clone(), test_executor(), DenyUnsafe::No, None); + let (api, _child) = new_full(client.clone(), test_executor(), DenyUnsafe::No); let mut add_block = |nonce| { let mut builder = client.new_block(Default::default()).unwrap(); @@ -480,7 +480,7 @@ async fn should_query_storage() { #[tokio::test] async fn should_return_runtime_version() { let client = Arc::new(substrate_test_runtime_client::new()); - let (api, _child) = new_full(client.clone(), test_executor(), DenyUnsafe::No, None); + let (api, _child) = new_full(client.clone(), test_executor(), DenyUnsafe::No); let result = "{\"specName\":\"test\",\"implName\":\"parity-test\",\"authoringVersion\":1,\ \"specVersion\":2,\"implVersion\":2,\"apis\":[[\"0xdf6acb689907609b\",4],\ @@ -501,7 +501,7 @@ async fn should_return_runtime_version() { async fn should_notify_on_runtime_version_initially() { let mut sub = { let client = Arc::new(substrate_test_runtime_client::new()); - let (api, _child) = new_full(client, test_executor(), DenyUnsafe::No, None); + let (api, _child) = new_full(client, test_executor(), DenyUnsafe::No); let api_rpc = api.into_rpc(); let sub = api_rpc @@ -530,7 +530,7 @@ fn should_deserialize_storage_key() { #[tokio::test] async fn wildcard_storage_subscriptions_are_rpc_unsafe() { let client = Arc::new(substrate_test_runtime_client::new()); - let (api, _child) = new_full(client, test_executor(), DenyUnsafe::Yes, None); + let (api, _child) = new_full(client, test_executor(), DenyUnsafe::Yes); let api_rpc = api.into_rpc(); let err = api_rpc.subscribe("state_subscribeStorage", EmptyParams::new()).await; @@ -540,7 +540,7 @@ async fn wildcard_storage_subscriptions_are_rpc_unsafe() { #[tokio::test] async fn concrete_storage_subscriptions_are_rpc_safe() { let client = Arc::new(substrate_test_runtime_client::new()); - let (api, _child) = new_full(client, test_executor(), DenyUnsafe::Yes, None); + let (api, _child) = new_full(client, test_executor(), DenyUnsafe::Yes); let api_rpc = api.into_rpc(); let key = StorageKey(STORAGE_KEY.to_vec()); diff --git a/client/service/src/builder.rs b/client/service/src/builder.rs index bee40e9a3..f0f9a7ed0 100644 --- a/client/service/src/builder.rs +++ b/client/service/src/builder.rs @@ -631,12 +631,8 @@ where let (chain, state, child_state) = { let chain = sc_rpc::chain::new_full(client.clone(), task_executor.clone()).into_rpc(); - let (state, child_state) = sc_rpc::state::new_full( - client.clone(), - task_executor.clone(), - deny_unsafe, - config.rpc_max_payload, - ); + let (state, child_state) = + sc_rpc::state::new_full(client.clone(), task_executor.clone(), deny_unsafe); let state = state.into_rpc(); let child_state = child_state.into_rpc(); diff --git a/client/tracing/src/block/mod.rs b/client/tracing/src/block/mod.rs index f5efadc34..c0442abc1 100644 --- a/client/tracing/src/block/mod.rs +++ b/client/tracing/src/block/mod.rs @@ -34,38 +34,21 @@ use tracing::{ use crate::{SpanDatum, TraceEvent, Values}; use sc_client_api::BlockBackend; -use sc_rpc_server::RPC_MAX_PAYLOAD_DEFAULT; use sp_api::{Core, Encode, Metadata, ProvideRuntimeApi}; use sp_blockchain::HeaderBackend; use sp_core::hexdisplay::HexDisplay; -use sp_rpc::tracing::{BlockTrace, Span, TraceBlockResponse, TraceError}; +use sp_rpc::tracing::{BlockTrace, Span, TraceBlockResponse}; use sp_runtime::{ generic::BlockId, traits::{Block as BlockT, Header}, }; use sp_tracing::{WASM_NAME_KEY, WASM_TARGET_KEY, WASM_TRACE_IDENTIFIER}; -// Heuristic for average event size in bytes. -const AVG_EVENT: usize = 600 * 8; -// Heuristic for average span size in bytes. -const AVG_SPAN: usize = 100 * 8; -// Estimate of the max base RPC payload size when the Id is bound as a u64. If strings -// are used for the RPC Id this may need to be adjusted. Note: The base payload -// does not include the RPC result. -// -// The estimate is based on the JSON-RPC response message which has the following format: -// `{"jsonrpc":"2.0","result":[],"id":18446744073709551615}`. -// -// We care about the total size of the payload because jsonrpc-server will simply ignore -// messages larger than `sc_rpc_server::MAX_PAYLOAD` and the caller will not get any -// response. -const BASE_PAYLOAD: usize = 100; // Default to only pallet, frame support and state related traces const DEFAULT_TARGETS: &str = "pallet,frame,state"; const TRACE_TARGET: &str = "block_trace"; // The name of a field required for all events. const REQUIRED_EVENT_FIELD: &str = "method"; -const MEGABYTE: usize = 1024 * 1024; /// Tracing Block Result type alias pub type TraceBlockResult = Result; @@ -182,7 +165,6 @@ pub struct BlockExecutor { targets: Option, storage_keys: Option, methods: Option, - rpc_max_payload: usize, } impl BlockExecutor @@ -203,12 +185,8 @@ where targets: Option, storage_keys: Option, methods: Option, - rpc_max_payload: Option, ) -> Self { - let rpc_max_payload = rpc_max_payload - .map(|mb| mb.saturating_mul(MEGABYTE)) - .unwrap_or(RPC_MAX_PAYLOAD_DEFAULT); - Self { client, block, targets, storage_keys, methods, rpc_max_payload } + Self { client, block, targets, storage_keys, methods } } /// Execute block, record all spans and events belonging to `Self::targets` @@ -289,24 +267,15 @@ where .collect(); tracing::debug!(target: "state_tracing", "Captured {} spans and {} events", spans.len(), events.len()); - let approx_payload_size = BASE_PAYLOAD + events.len() * AVG_EVENT + spans.len() * AVG_SPAN; - let response = if approx_payload_size > self.rpc_max_payload { - TraceBlockResponse::TraceError(TraceError { - error: "Payload likely exceeds max payload size of RPC server.".to_string(), - }) - } else { - TraceBlockResponse::BlockTrace(BlockTrace { - block_hash: block_id_as_string(BlockId::::Hash(self.block)), - parent_hash: block_id_as_string(BlockId::::Hash(parent_hash)), - tracing_targets: targets.to_string(), - storage_keys: self.storage_keys.clone().unwrap_or_default(), - methods: self.methods.clone().unwrap_or_default(), - spans, - events, - }) - }; - - Ok(response) + Ok(TraceBlockResponse::BlockTrace(BlockTrace { + block_hash: block_id_as_string(BlockId::::Hash(self.block)), + parent_hash: block_id_as_string(BlockId::::Hash(parent_hash)), + tracing_targets: targets.to_string(), + storage_keys: self.storage_keys.clone().unwrap_or_default(), + methods: self.methods.clone().unwrap_or_default(), + spans, + events, + })) } } From 81aa7f317d0bb8476ab660bc67853f59aed84d4e Mon Sep 17 00:00:00 2001 From: Mira Ressel Date: Tue, 21 Mar 2023 13:05:06 +0100 Subject: [PATCH 273/558] core-fellowship: Remove unused dev-dependencies --- frame/core-fellowship/Cargo.toml | 6 ------ 1 file changed, 6 deletions(-) diff --git a/frame/core-fellowship/Cargo.toml b/frame/core-fellowship/Cargo.toml index 2785346c7..16b673fb4 100644 --- a/frame/core-fellowship/Cargo.toml +++ b/frame/core-fellowship/Cargo.toml @@ -25,10 +25,6 @@ sp-io = { version = "7.0.0", default-features = false, path = "../../primitives/ sp-runtime = { version = "7.0.0", default-features = false, path = "../../primitives/runtime" } sp-std = { version = "5.0.0", default-features = false, path = "../../primitives/std" } -[dev-dependencies] -pallet-ranked-collective = { version = "4.0.0-dev", default-features = false, path = "../ranked-collective" } -pallet-salary = { version = "4.0.0-dev", default-features = false, path = "../salary" } - [features] default = ["std"] std = [ @@ -49,7 +45,5 @@ runtime-benchmarks = [ "frame-support/runtime-benchmarks", "frame-system/runtime-benchmarks", "sp-runtime/runtime-benchmarks", - "pallet-ranked-collective/runtime-benchmarks", - "pallet-salary/runtime-benchmarks", ] try-runtime = ["frame-support/try-runtime"] From 6f79a5a26480cca90727edd0e9193c876788dcc3 Mon Sep 17 00:00:00 2001 From: Oliver Tale-Yazdi Date: Tue, 21 Mar 2023 17:27:45 +0100 Subject: [PATCH 274/558] Update lockfile The CI needs this. Signed-off-by: Oliver Tale-Yazdi --- Cargo.lock | 2 -- 1 file changed, 2 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 4a0cc4cd3..5b2879e1b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -5886,8 +5886,6 @@ dependencies = [ "frame-support", "frame-system", "log", - "pallet-ranked-collective", - "pallet-salary", "parity-scale-codec", "scale-info", "sp-arithmetic", From 5b29f150365cc6eacb1f618e8797d81f0aada46a Mon Sep 17 00:00:00 2001 From: Mira Ressel Date: Wed, 22 Mar 2023 09:33:06 +0100 Subject: [PATCH 275/558] move back to ci-linux@production image (#13669) --- .gitlab-ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index f9e74364a..6cf062736 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -48,7 +48,7 @@ variables: CARGO_INCREMENTAL: 0 DOCKER_OS: "debian:stretch" ARCH: "x86_64" - CI_IMAGE: "paritytech/ci-linux@sha256:7c76c3f9639f919447abbf9db535588178fde4df583d6926444d44cc20c094e6" # staging 2023-03-20 + CI_IMAGE: "paritytech/ci-linux:production" BUILDAH_IMAGE: "quay.io/buildah/stable:v1.27" RUSTY_CACHIER_SINGLE_BRANCH: master RUSTY_CACHIER_DONT_OPERATE_ON_MAIN_BRANCH: "true" From 2b42fbd860d7034ce9c716a37016b0101be69491 Mon Sep 17 00:00:00 2001 From: Davide Galassi Date: Wed, 22 Mar 2023 11:09:10 +0100 Subject: [PATCH 276/558] Crypto Pair trait refactory (#13657) * Crypto pair refactory * Remove unused method * Apply review suggestions * Remove leftovers * Associated type is not really required * Fix after refactory * Fix benchmark-ui test --------- Co-authored-by: Anton --- ...bad_return_type_blank_with_question.stderr | 2 +- primitives/application-crypto/src/lib.rs | 5 +- primitives/core/src/crypto.rs | 79 ++++++++++++----- primitives/core/src/ecdsa.rs | 49 +---------- primitives/core/src/ed25519.rs | 51 +---------- primitives/core/src/sr25519.rs | 85 +++---------------- 6 files changed, 75 insertions(+), 196 deletions(-) diff --git a/frame/support/test/tests/benchmark_ui/bad_return_type_blank_with_question.stderr b/frame/support/test/tests/benchmark_ui/bad_return_type_blank_with_question.stderr index 1f8c9f1e1..601bbd20f 100644 --- a/frame/support/test/tests/benchmark_ui/bad_return_type_blank_with_question.stderr +++ b/frame/support/test/tests/benchmark_ui/bad_return_type_blank_with_question.stderr @@ -7,4 +7,4 @@ error[E0277]: the `?` operator can only be used in a function that returns `Resu 15 | something()?; | ^ cannot use the `?` operator in a function that returns `()` | - = help: the trait `FromResidual>` is not implemented for `()` + = help: the trait `FromResidual>` is not implemented for `()` diff --git a/primitives/application-crypto/src/lib.rs b/primitives/application-crypto/src/lib.rs index 992ecd1d0..0685477dd 100644 --- a/primitives/application-crypto/src/lib.rs +++ b/primitives/application-crypto/src/lib.rs @@ -23,7 +23,7 @@ pub use sp_core::crypto::{key_types, CryptoTypeId, KeyTypeId}; #[doc(hidden)] #[cfg(feature = "full_crypto")] -pub use sp_core::crypto::{DeriveJunction, Pair, SecretStringError, Ss58Codec}; +pub use sp_core::crypto::{DeriveError, DeriveJunction, Pair, SecretStringError, Ss58Codec}; #[doc(hidden)] pub use sp_core::{ self, @@ -129,7 +129,6 @@ macro_rules! app_crypto_pair { type Public = Public; type Seed = <$pair as $crate::Pair>::Seed; type Signature = Signature; - type DeriveError = <$pair as $crate::Pair>::DeriveError; $crate::app_crypto_pair_functions_if_std!($pair); @@ -137,7 +136,7 @@ macro_rules! app_crypto_pair { &self, path: Iter, seed: Option, - ) -> Result<(Self, Option), Self::DeriveError> { + ) -> Result<(Self, Option), $crate::DeriveError> { self.0.derive(path, seed).map(|x| (Self(x.0), x.1)) } fn from_seed(seed: &Self::Seed) -> Self { diff --git a/primitives/core/src/crypto.rs b/primitives/core/src/crypto.rs index 16af3d069..80e65b342 100644 --- a/primitives/core/src/crypto.rs +++ b/primitives/core/src/crypto.rs @@ -24,6 +24,8 @@ use crate::hexdisplay::HexDisplay; use crate::{ed25519, sr25519}; #[cfg(feature = "std")] use base58::{FromBase58, ToBase58}; +#[cfg(feature = "std")] +use bip39::{Language, Mnemonic, MnemonicType}; use codec::{Decode, Encode, MaxEncodedLen}; #[cfg(feature = "std")] use rand::{rngs::OsRng, RngCore}; @@ -52,10 +54,6 @@ pub const DEV_PHRASE: &str = /// The address of the associated root phrase for our publicly known keys. pub const DEV_ADDRESS: &str = "5DfhGyQdFobKM8NsWvEeAKk5EQQgYe9AydgJ7rMB6E1EqRzV"; -/// The infallible type. -#[derive(crate::RuntimeDebug)] -pub enum Infallible {} - /// The length of the junction identifier. Note that this is also referred to as the /// `CHAIN_CODE_LENGTH` in the context of Schnorrkel. #[cfg(feature = "full_crypto")] @@ -108,6 +106,16 @@ pub enum SecretStringError { InvalidPath, } +/// An error when deriving a key. +#[cfg_attr(feature = "std", derive(thiserror::Error))] +#[derive(Debug, Clone, PartialEq, Eq)] +#[cfg(feature = "full_crypto")] +pub enum DeriveError { + /// A soft key was found in the path (and is unsupported). + #[cfg_attr(feature = "std", error("Soft key in path"))] + SoftKeyInPath, +} + /// A since derivation junction description. It is the single parameter used when creating /// a new secret key from an existing secret key and, in the case of `SoftRaw` and `SoftIndex` /// a new public key from an existing public key. @@ -691,40 +699,45 @@ mod dummy { type Public = Dummy; type Seed = Dummy; type Signature = Dummy; - type DeriveError = (); + #[cfg(feature = "std")] fn generate_with_phrase(_: Option<&str>) -> (Self, String, Self::Seed) { Default::default() } + #[cfg(feature = "std")] fn from_phrase(_: &str, _: Option<&str>) -> Result<(Self, Self::Seed), SecretStringError> { Ok(Default::default()) } + fn derive>( &self, _: Iter, _: Option, - ) -> Result<(Self, Option), Self::DeriveError> { + ) -> Result<(Self, Option), DeriveError> { Ok((Self, None)) } - fn from_seed(_: &Self::Seed) -> Self { - Self - } + fn from_seed_slice(_: &[u8]) -> Result { Ok(Self) } + fn sign(&self, _: &[u8]) -> Self::Signature { Self } + fn verify>(_: &Self::Signature, _: M, _: &Self::Public) -> bool { true } + fn verify_weak, M: AsRef<[u8]>>(_: &[u8], _: M, _: P) -> bool { true } + fn public(&self) -> Self::Public { Self } + fn to_raw_vec(&self) -> Vec { vec![] } @@ -845,9 +858,6 @@ pub trait Pair: CryptoType + Sized + Clone + Send + Sync + 'static { /// and verified with the message and a public key. type Signature: AsRef<[u8]>; - /// Error returned from the `derive` function. - type DeriveError; - /// Generate new secure (random) key pair. /// /// This is only for ephemeral keys really, since you won't have access to the secret key @@ -866,27 +876,47 @@ pub trait Pair: CryptoType + Sized + Clone + Send + Sync + 'static { /// This is generally slower than `generate()`, so prefer that unless you need to persist /// the key from the current session. #[cfg(feature = "std")] - fn generate_with_phrase(password: Option<&str>) -> (Self, String, Self::Seed); + fn generate_with_phrase(password: Option<&str>) -> (Self, String, Self::Seed) { + let mnemonic = Mnemonic::new(MnemonicType::Words12, Language::English); + let phrase = mnemonic.phrase(); + let (pair, seed) = Self::from_phrase(phrase, password) + .expect("All phrases generated by Mnemonic are valid; qed"); + (pair, phrase.to_owned(), seed) + } /// Returns the KeyPair from the English BIP39 seed `phrase`, or `None` if it's invalid. #[cfg(feature = "std")] fn from_phrase( phrase: &str, password: Option<&str>, - ) -> Result<(Self, Self::Seed), SecretStringError>; + ) -> Result<(Self, Self::Seed), SecretStringError> { + let mnemonic = Mnemonic::from_phrase(phrase, Language::English) + .map_err(|_| SecretStringError::InvalidPhrase)?; + let big_seed = + substrate_bip39::seed_from_entropy(mnemonic.entropy(), password.unwrap_or("")) + .map_err(|_| SecretStringError::InvalidSeed)?; + let mut seed = Self::Seed::default(); + let seed_slice = seed.as_mut(); + let seed_len = seed_slice.len(); + debug_assert!(seed_len <= big_seed.len()); + seed_slice[..seed_len].copy_from_slice(&big_seed[..seed_len]); + Self::from_seed_slice(seed_slice).map(|x| (x, seed)) + } /// Derive a child key from a series of given junctions. fn derive>( &self, path: Iter, seed: Option, - ) -> Result<(Self, Option), Self::DeriveError>; + ) -> Result<(Self, Option), DeriveError>; /// Generate new key pair from the provided `seed`. /// /// @WARNING: THIS WILL ONLY BE SECURE IF THE `seed` IS SECURE. If it can be guessed /// by an attacker then they can also derive your key. - fn from_seed(seed: &Self::Seed) -> Self; + fn from_seed(seed: &Self::Seed) -> Self { + Self::from_seed_slice(seed.as_ref()).expect("seed has valid length; qed") + } /// Make a new key pair from secret seed material. The slice must be the correct size or /// it will return `None`. @@ -1202,14 +1232,15 @@ mod tests { type Public = TestPublic; type Seed = [u8; 8]; type Signature = [u8; 0]; - type DeriveError = (); fn generate() -> (Self, ::Seed) { (TestPair::Generated, [0u8; 8]) } + fn generate_with_phrase(_password: Option<&str>) -> (Self, String, ::Seed) { (TestPair::GeneratedWithPhrase, "".into(), [0u8; 8]) } + fn from_phrase( phrase: &str, password: Option<&str>, @@ -1222,11 +1253,12 @@ mod tests { [0u8; 8], )) } + fn derive>( &self, path_iter: Iter, _: Option<[u8; 8]>, - ) -> Result<(Self, Option<[u8; 8]>), Self::DeriveError> { + ) -> Result<(Self, Option<[u8; 8]>), DeriveError> { Ok(( match self.clone() { TestPair::Standard { phrase, password, path } => TestPair::Standard { @@ -1240,21 +1272,21 @@ mod tests { if path_iter.count() == 0 { x } else { - return Err(()) + return Err(DeriveError::SoftKeyInPath) }, }, None, )) } - fn from_seed(_seed: &::Seed) -> Self { - TestPair::Seed(_seed.as_ref().to_owned()) - } + fn sign(&self, _message: &[u8]) -> Self::Signature { [] } + fn verify>(_: &Self::Signature, _: M, _: &Self::Public) -> bool { true } + fn verify_weak, M: AsRef<[u8]>>( _sig: &[u8], _message: M, @@ -1262,12 +1294,15 @@ mod tests { ) -> bool { true } + fn public(&self) -> Self::Public { TestPublic } + fn from_seed_slice(seed: &[u8]) -> Result { Ok(TestPair::Seed(seed.to_owned())) } + fn to_raw_vec(&self) -> Vec { vec![] } diff --git a/primitives/core/src/ecdsa.rs b/primitives/core/src/ecdsa.rs index d68ba39a0..e63b283d2 100644 --- a/primitives/core/src/ecdsa.rs +++ b/primitives/core/src/ecdsa.rs @@ -29,11 +29,9 @@ use crate::crypto::{ }; #[cfg(feature = "full_crypto")] use crate::{ - crypto::{DeriveJunction, Pair as TraitPair, SecretStringError}, + crypto::{DeriveError, DeriveJunction, Pair as TraitPair, SecretStringError}, hashing::blake2_256, }; -#[cfg(feature = "std")] -use bip39::{Language, Mnemonic, MnemonicType}; #[cfg(all(feature = "full_crypto", not(feature = "std")))] use secp256k1::Secp256k1; #[cfg(feature = "std")] @@ -367,13 +365,6 @@ fn derive_hard_junction(secret_seed: &Seed, cc: &[u8; 32]) -> Seed { ("Secp256k1HDKD", secret_seed, cc).using_encoded(sp_core_hashing::blake2_256) } -/// An error when deriving a key. -#[cfg(feature = "full_crypto")] -pub enum DeriveError { - /// A soft key was found in the path (and is unsupported). - SoftKeyInPath, -} - /// A key pair. #[cfg(feature = "full_crypto")] #[derive(Clone)] @@ -387,44 +378,6 @@ impl TraitPair for Pair { type Public = Public; type Seed = Seed; type Signature = Signature; - type DeriveError = DeriveError; - - /// Generate new secure (random) key pair and provide the recovery phrase. - /// - /// You can recover the same key later with `from_phrase`. - #[cfg(feature = "std")] - fn generate_with_phrase(password: Option<&str>) -> (Pair, String, Seed) { - let mnemonic = Mnemonic::new(MnemonicType::Words12, Language::English); - let phrase = mnemonic.phrase(); - let (pair, seed) = Self::from_phrase(phrase, password) - .expect("All phrases generated by Mnemonic are valid; qed"); - (pair, phrase.to_owned(), seed) - } - - /// Generate key pair from given recovery phrase and password. - #[cfg(feature = "std")] - fn from_phrase( - phrase: &str, - password: Option<&str>, - ) -> Result<(Pair, Seed), SecretStringError> { - let big_seed = substrate_bip39::seed_from_entropy( - Mnemonic::from_phrase(phrase, Language::English) - .map_err(|_| SecretStringError::InvalidPhrase)? - .entropy(), - password.unwrap_or(""), - ) - .map_err(|_| SecretStringError::InvalidSeed)?; - let mut seed = Seed::default(); - seed.copy_from_slice(&big_seed[0..32]); - Self::from_seed_slice(&big_seed[0..32]).map(|x| (x, seed)) - } - - /// Make a new key pair from secret seed material. - /// - /// You should never need to use this; generate(), generate_with_phrase - fn from_seed(seed: &Seed) -> Pair { - Self::from_seed_slice(&seed[..]).expect("seed has valid length; qed") - } /// Make a new key pair from secret seed material. The slice must be 32 bytes long or it /// will return `None`. diff --git a/primitives/core/src/ed25519.rs b/primitives/core/src/ed25519.rs index 76b3064ad..503c127a9 100644 --- a/primitives/core/src/ed25519.rs +++ b/primitives/core/src/ed25519.rs @@ -35,9 +35,7 @@ use crate::crypto::{ CryptoType, CryptoTypeId, CryptoTypePublicPair, Derive, Public as TraitPublic, UncheckedFrom, }; #[cfg(feature = "full_crypto")] -use crate::crypto::{DeriveJunction, Pair as TraitPair, SecretStringError}; -#[cfg(feature = "std")] -use bip39::{Language, Mnemonic, MnemonicType}; +use crate::crypto::{DeriveError, DeriveJunction, Pair as TraitPair, SecretStringError}; #[cfg(feature = "full_crypto")] use core::convert::TryFrom; #[cfg(feature = "full_crypto")] @@ -46,8 +44,6 @@ use ed25519_zebra::{SigningKey, VerificationKey}; use serde::{de, Deserialize, Deserializer, Serialize, Serializer}; use sp_runtime_interface::pass_by::PassByInner; use sp_std::ops::Deref; -#[cfg(feature = "std")] -use substrate_bip39::seed_from_entropy; /// An identifier used to match public keys against ed25519 keys pub const CRYPTO_ID: CryptoTypeId = CryptoTypeId(*b"ed25"); @@ -385,56 +381,11 @@ fn derive_hard_junction(secret_seed: &Seed, cc: &[u8; 32]) -> Seed { ("Ed25519HDKD", secret_seed, cc).using_encoded(sp_core_hashing::blake2_256) } -/// An error when deriving a key. -#[cfg(feature = "full_crypto")] -pub enum DeriveError { - /// A soft key was found in the path (and is unsupported). - SoftKeyInPath, -} - #[cfg(feature = "full_crypto")] impl TraitPair for Pair { type Public = Public; type Seed = Seed; type Signature = Signature; - type DeriveError = DeriveError; - - /// Generate new secure (random) key pair and provide the recovery phrase. - /// - /// You can recover the same key later with `from_phrase`. - #[cfg(feature = "std")] - fn generate_with_phrase(password: Option<&str>) -> (Pair, String, Seed) { - let mnemonic = Mnemonic::new(MnemonicType::Words12, Language::English); - let phrase = mnemonic.phrase(); - let (pair, seed) = Self::from_phrase(phrase, password) - .expect("All phrases generated by Mnemonic are valid; qed"); - (pair, phrase.to_owned(), seed) - } - - /// Generate key pair from given recovery phrase and password. - #[cfg(feature = "std")] - fn from_phrase( - phrase: &str, - password: Option<&str>, - ) -> Result<(Pair, Seed), SecretStringError> { - let big_seed = seed_from_entropy( - Mnemonic::from_phrase(phrase, Language::English) - .map_err(|_| SecretStringError::InvalidPhrase)? - .entropy(), - password.unwrap_or(""), - ) - .map_err(|_| SecretStringError::InvalidSeed)?; - let mut seed = Seed::default(); - seed.copy_from_slice(&big_seed[0..32]); - Self::from_seed_slice(&big_seed[0..32]).map(|x| (x, seed)) - } - - /// Make a new key pair from secret seed material. - /// - /// You should never need to use this; generate(), generate_with_phrase - fn from_seed(seed: &Seed) -> Pair { - Self::from_seed_slice(&seed[..]).expect("seed has valid length; qed") - } /// Make a new key pair from secret seed material. The slice must be 32 bytes long or it /// will return `None`. diff --git a/primitives/core/src/sr25519.rs b/primitives/core/src/sr25519.rs index 809d83aaf..8dbcca9e2 100644 --- a/primitives/core/src/sr25519.rs +++ b/primitives/core/src/sr25519.rs @@ -24,9 +24,7 @@ #[cfg(feature = "std")] use crate::crypto::Ss58Codec; #[cfg(feature = "full_crypto")] -use crate::crypto::{DeriveJunction, Infallible, Pair as TraitPair, SecretStringError}; -#[cfg(feature = "std")] -use bip39::{Language, Mnemonic, MnemonicType}; +use crate::crypto::{DeriveError, DeriveJunction, Pair as TraitPair, SecretStringError}; #[cfg(feature = "full_crypto")] use schnorrkel::{ derive::{ChainCode, Derivation, CHAIN_CODE_LENGTH}, @@ -34,8 +32,6 @@ use schnorrkel::{ }; #[cfg(feature = "full_crypto")] use sp_std::vec::Vec; -#[cfg(feature = "std")] -use substrate_bip39::mini_secret_from_entropy; use crate::{ crypto::{ @@ -458,16 +454,6 @@ impl TraitPair for Pair { type Public = Public; type Seed = Seed; type Signature = Signature; - type DeriveError = Infallible; - - /// Make a new key pair from raw secret seed material. - /// - /// This is generated using schnorrkel's Mini-Secret-Keys. - /// - /// A MiniSecretKey is literally what Ed25519 calls a SecretKey, which is just 32 random bytes. - fn from_seed(seed: &Seed) -> Pair { - Self::from_seed_slice(&seed[..]).expect("32 bytes can always build a key; qed") - } /// Get the public key. fn public(&self) -> Public { @@ -476,10 +462,12 @@ impl TraitPair for Pair { Public(pk) } - /// Make a new key pair from secret seed material. The slice must be 32 bytes long or it - /// will return `None`. + /// Make a new key pair from raw secret seed material. + /// + /// This is generated using schnorrkel's Mini-Secret-Keys. /// - /// You should never need to use this; generate(), generate_with_phrase(), from_phrase() + /// A `MiniSecretKey` is literally what Ed25519 calls a `SecretKey`, which is just 32 random + /// bytes. fn from_seed_slice(seed: &[u8]) -> Result { match seed.len() { MINI_SECRET_KEY_LENGTH => Ok(Pair( @@ -495,42 +483,16 @@ impl TraitPair for Pair { _ => Err(SecretStringError::InvalidSeedLength), } } - #[cfg(feature = "std")] - fn generate_with_phrase(password: Option<&str>) -> (Pair, String, Seed) { - let mnemonic = Mnemonic::new(MnemonicType::Words12, Language::English); - let phrase = mnemonic.phrase(); - let (pair, seed) = Self::from_phrase(phrase, password) - .expect("All phrases generated by Mnemonic are valid; qed"); - (pair, phrase.to_owned(), seed) - } - #[cfg(feature = "std")] - fn from_phrase( - phrase: &str, - password: Option<&str>, - ) -> Result<(Pair, Seed), SecretStringError> { - Mnemonic::from_phrase(phrase, Language::English) - .map_err(|_| SecretStringError::InvalidPhrase) - .map(|m| Self::from_entropy(m.entropy(), password)) - } fn derive>( &self, path: Iter, seed: Option, - ) -> Result<(Pair, Option), Self::DeriveError> { - let seed = if let Some(s) = seed { - if let Ok(msk) = MiniSecretKey::from_bytes(&s) { - if msk.expand(ExpansionMode::Ed25519) == self.0.secret { - Some(msk) - } else { - None - } - } else { - None - } - } else { - None - }; + ) -> Result<(Pair, Option), DeriveError> { + let seed = seed + .and_then(|s| MiniSecretKey::from_bytes(&s).ok()) + .filter(|msk| msk.expand(ExpansionMode::Ed25519) == self.0.secret); + let init = self.0.secret.clone(); let (result, seed) = path.fold((init, seed), |(acc, acc_seed), j| match (j, acc_seed) { (DeriveJunction::Soft(cc), _) => (acc.derived_key_simple(ChainCode(cc), &[]).0, None), @@ -572,18 +534,6 @@ impl TraitPair for Pair { #[cfg(feature = "std")] impl Pair { - /// Make a new key pair from binary data derived from a valid seed phrase. - /// - /// This uses a key derivation function to convert the entropy into a seed, then returns - /// the pair generated from it. - pub fn from_entropy(entropy: &[u8], password: Option<&str>) -> (Pair, Seed) { - let mini_key: MiniSecretKey = mini_secret_from_entropy(entropy, password.unwrap_or("")) - .expect("32 bytes can always build a key; qed"); - - let kp = mini_key.expand_to_keypair(ExpansionMode::Ed25519); - (Pair(kp), mini_key.to_bytes()) - } - /// Verify a signature on a message. Returns `true` if the signature is good. /// Supports old 0.1.1 deprecated signatures and should be used only for backward /// compatibility. @@ -652,10 +602,8 @@ pub fn verify_batch( #[cfg(test)] mod compatibility_test { use super::*; - use crate::crypto::DEV_PHRASE; - - // NOTE: tests to ensure addresses that are created with the `0.1.x` version (pre-audit) are - // still functional. + use crate::crypto::{Ss58Codec, DEV_ADDRESS, DEV_PHRASE}; + use serde_json; #[test] fn derive_soft_known_pair_should_work() { @@ -690,13 +638,6 @@ mod compatibility_test { assert!(Pair::verify_deprecated(&signature, &message[..], &public)); assert!(!Pair::verify(&signature, &message[..], &public)); } -} - -#[cfg(test)] -mod test { - use super::*; - use crate::crypto::{Ss58Codec, DEV_ADDRESS, DEV_PHRASE}; - use serde_json; #[test] fn default_phrase_should_be_used() { From c120cd35b2e087ff4ee54683f9ead3d93e4a30f1 Mon Sep 17 00:00:00 2001 From: Liam Aharon Date: Wed, 22 Mar 2023 21:47:44 +1100 Subject: [PATCH 277/558] Fix try_runtime_cli doc broken links (#13672) --- utils/frame/try-runtime/cli/src/lib.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/utils/frame/try-runtime/cli/src/lib.rs b/utils/frame/try-runtime/cli/src/lib.rs index 34966fc2a..0a521d121 100644 --- a/utils/frame/try-runtime/cli/src/lib.rs +++ b/utils/frame/try-runtime/cli/src/lib.rs @@ -29,9 +29,9 @@ //! //! Some resources about the above: //! -//! 1. +//! 1. //! 2. -//! 3. +//! 3. //! //! --- //! From d6899f8215810975defe48b423f4b1b1b6fcc17e Mon Sep 17 00:00:00 2001 From: Alexandru Vasile <60601340+lexnv@users.noreply.github.com> Date: Wed, 22 Mar 2023 12:52:10 +0200 Subject: [PATCH 278/558] fix/chain_head: Ensure correct events for finalized branch (#13632) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * chain_head/follow: Ensure correct events for finalized branch Signed-off-by: Alexandru Vasile * Reenable tests Signed-off-by: Alexandru Vasile * Do some clean ups and add some more docs * Fix gramatic * Update client/rpc-spec-v2/src/chain_head/chain_head_follow.rs Co-authored-by: Sebastian Kunert * rpc/chain_head: Introduce error for absent headers Signed-off-by: Alexandru Vasile --------- Signed-off-by: Alexandru Vasile Co-authored-by: Bastian Köcher Co-authored-by: Bastian Köcher Co-authored-by: Sebastian Kunert --- .../src/chain_head/chain_head_follow.rs | 100 ++++++------------ .../src/chain_head/subscription.rs | 6 +- client/rpc-spec-v2/src/chain_head/tests.rs | 6 -- 3 files changed, 34 insertions(+), 78 deletions(-) diff --git a/client/rpc-spec-v2/src/chain_head/chain_head_follow.rs b/client/rpc-spec-v2/src/chain_head/chain_head_follow.rs index 9173b7340..a0d19654e 100644 --- a/client/rpc-spec-v2/src/chain_head/chain_head_follow.rs +++ b/client/rpc-spec-v2/src/chain_head/chain_head_follow.rs @@ -40,10 +40,7 @@ use sp_api::CallApiAt; use sp_blockchain::{ Backend as BlockChainBackend, Error as BlockChainError, HeaderBackend, HeaderMetadata, Info, }; -use sp_runtime::{ - traits::{Block as BlockT, Header as HeaderT, One}, - Saturating, -}; +use sp_runtime::traits::{Block as BlockT, Header as HeaderT, NumberFor}; use std::{collections::HashSet, sync::Arc}; /// Generates the events of the `chainHead_follow` method. @@ -116,7 +113,7 @@ struct StartupPoint { /// The head of the finalized chain. pub finalized_hash: Block::Hash, /// Last finalized block number. - pub finalized_number: <::Header as HeaderT>::Number, + pub finalized_number: NumberFor, } impl From> for StartupPoint { @@ -318,10 +315,7 @@ where } // Ensure we are only reporting blocks after the starting point. - let Some(block_number) = self.client.number(notification.hash)? else { - return Err(SubscriptionManagementError::BlockNumberAbsent) - }; - if block_number < startup_point.finalized_number { + if *notification.header.number() < startup_point.finalized_number { return Ok(Default::default()) } @@ -349,59 +343,48 @@ where return Ok(Default::default()) }; - // Find the parent hash. - let Some(first_number) = self.client.number(*first_hash)? else { - return Err(SubscriptionManagementError::BlockNumberAbsent) - }; - let Some(parent) = self.client.hash(first_number.saturating_sub(One::one()))? else { - return Err(SubscriptionManagementError::BlockHashAbsent) + // Find the parent header. + let Some(first_header) = self.client.header(*first_hash)? else { + return Err(SubscriptionManagementError::BlockHeaderAbsent) }; - let last_finalized = finalized_block_hashes - .last() - .expect("At least one finalized hash inserted; qed"); - let parents = std::iter::once(&parent).chain(finalized_block_hashes.iter()); - for (hash, parent) in finalized_block_hashes.iter().zip(parents) { - // This block is already reported by the import notification. + let parents = + std::iter::once(first_header.parent_hash()).chain(finalized_block_hashes.iter()); + for (i, (hash, parent)) in finalized_block_hashes.iter().zip(parents).enumerate() { + // Check if the block was already reported and thus, is already pinned. if !self.sub_handle.pin_block(*hash)? { continue } - // Generate only the `NewBlock` event for this block. - if hash != last_finalized { + // Generate `NewBlock` events for all blocks beside the last block in the list + if i + 1 != finalized_block_hashes.len() { + // Generate only the `NewBlock` event for this block. events.extend(self.generate_import_events(*hash, *parent, false)); - continue - } - - match self.best_block_cache { - Some(best_block_hash) => { - // If the best reported block is a children of the last finalized, - // then we had a gap in notification. + } else { + // If we end up here and the `best_block` is a descendent of the finalized block + // (last block in the list), it means that there were skipped notifications. + // Otherwise `pin_block` would had returned `true`. + // + // When the node falls out of sync and then syncs up to the tip of the chain, it can + // happen that we skip notifications. Then it is better to terminate the connection + // instead of trying to send notifications for all missed blocks. + if let Some(best_block_hash) = self.best_block_cache { let ancestor = sp_blockchain::lowest_common_ancestor( &*self.client, - *last_finalized, + *hash, best_block_hash, )?; - // A descendent of the finalized block was already reported - // before the `NewBlock` event containing the finalized block - // is reported. - if ancestor.hash == *last_finalized { + if ancestor.hash == *hash { return Err(SubscriptionManagementError::Custom( "A descendent of the finalized block was already reported".into(), )) } - self.best_block_cache = Some(*hash); - }, - // This is the first best block event that we generate. - None => { - self.best_block_cache = Some(*hash); - }, - }; + } - // This is the first time we see this block. Generate the `NewBlock` event; if this is - // the last block, also generate the `BestBlock` event. - events.extend(self.generate_import_events(*hash, *parent, true)) + // Let's generate the `NewBlock` and `NewBestBlock` events for the block. + events.extend(self.generate_import_events(*hash, *parent, true)) + } } Ok(events) @@ -448,17 +431,13 @@ where let last_finalized = notification.hash; // Ensure we are only reporting blocks after the starting point. - let Some(block_number) = self.client.number(last_finalized)? else { - return Err(SubscriptionManagementError::BlockNumberAbsent) - }; - if block_number < startup_point.finalized_number { + if *notification.header.number() < startup_point.finalized_number { return Ok(Default::default()) } // The tree route contains the exclusive path from the last finalized block to the block // reported by the notification. Ensure the finalized block is also reported. - let mut finalized_block_hashes = - notification.tree_route.iter().cloned().collect::>(); + let mut finalized_block_hashes = notification.tree_route.to_vec(); finalized_block_hashes.push(last_finalized); // If the finalized hashes were not reported yet, generate the `NewBlock` events. @@ -476,9 +455,8 @@ where match self.best_block_cache { Some(block_cache) => { - // Check if the current best block is also reported as pruned. - let reported_pruned = pruned_block_hashes.iter().find(|&&hash| hash == block_cache); - if reported_pruned.is_none() { + // If the best block wasn't pruned, we are done here. + if !pruned_block_hashes.iter().any(|hash| *hash == block_cache) { events.push(finalized_event); return Ok(events) } @@ -499,20 +477,6 @@ where events.push(finalized_event); Ok(events) } else { - let ancestor = sp_blockchain::lowest_common_ancestor( - &*self.client, - last_finalized, - best_block_hash, - )?; - - // The client's best block must be a descendent of the last finalized block. - // In other words, the lowest common ancestor must be the last finalized block. - if ancestor.hash != last_finalized { - return Err(SubscriptionManagementError::Custom( - "The finalized block is not an ancestor of the best block".into(), - )) - } - // The RPC needs to also submit a new best block changed before the // finalized event. self.best_block_cache = Some(best_block_hash); diff --git a/client/rpc-spec-v2/src/chain_head/subscription.rs b/client/rpc-spec-v2/src/chain_head/subscription.rs index 77d57e747..687374bba 100644 --- a/client/rpc-spec-v2/src/chain_head/subscription.rs +++ b/client/rpc-spec-v2/src/chain_head/subscription.rs @@ -36,10 +36,8 @@ pub enum SubscriptionManagementError { ExceededLimits, /// Error originated from the blockchain (client or backend). Blockchain(Error), - /// The database does not contain a block number. - BlockNumberAbsent, - /// The database does not contain a block hash. - BlockHashAbsent, + /// The database does not contain a block header. + BlockHeaderAbsent, /// Custom error. Custom(String), } diff --git a/client/rpc-spec-v2/src/chain_head/tests.rs b/client/rpc-spec-v2/src/chain_head/tests.rs index 0110c97a0..fcd906dcf 100644 --- a/client/rpc-spec-v2/src/chain_head/tests.rs +++ b/client/rpc-spec-v2/src/chain_head/tests.rs @@ -1024,9 +1024,6 @@ async fn follow_prune_best_block() { } #[tokio::test] -#[cfg(disable_flaky)] -#[allow(dead_code)] -// FIXME: https://github.com/paritytech/substrate/issues/11321 async fn follow_forks_pruned_block() { let builder = TestClientBuilder::new(); let backend = builder.backend(); @@ -1140,9 +1137,6 @@ async fn follow_forks_pruned_block() { } #[tokio::test] -#[cfg(disable_flaky)] -#[allow(dead_code)] -// FIXME: https://github.com/paritytech/substrate/issues/11321 async fn follow_report_multiple_pruned_block() { let builder = TestClientBuilder::new(); let backend = builder.backend(); From 8bd57418b79d2450079755c0d631168b8fad2e97 Mon Sep 17 00:00:00 2001 From: Kasper Ziemianek Date: Wed, 22 Mar 2023 13:13:31 +0100 Subject: [PATCH 279/558] GetCallIndex trait (#13558) * GetCallIndex trait * final impl * ".git/.scripts/commands/fmt/fmt.sh" * Docs Signed-off-by: Oliver Tale-Yazdi * One more test Signed-off-by: Oliver Tale-Yazdi * Doc Signed-off-by: Oliver Tale-Yazdi --------- Signed-off-by: Oliver Tale-Yazdi Co-authored-by: command-bot <> Co-authored-by: Oliver Tale-Yazdi --- frame/support/procedural/src/lib.rs | 3 +- .../procedural/src/pallet/expand/call.rs | 15 ++++++++++ frame/support/src/dispatch.rs | 3 +- frame/support/src/traits.rs | 4 +-- frame/support/src/traits/metadata.rs | 10 ++++++- frame/support/test/tests/pallet.rs | 29 +++++++++++++++++-- 6 files changed, 56 insertions(+), 8 deletions(-) diff --git a/frame/support/procedural/src/lib.rs b/frame/support/procedural/src/lib.rs index 0515a4b7d..45f222029 100644 --- a/frame/support/procedural/src/lib.rs +++ b/frame/support/procedural/src/lib.rs @@ -984,7 +984,8 @@ pub fn compact(_: TokenStream, _: TokenStream) -> TokenStream { /// /// The macro creates an enum `Call` with one variant per dispatchable. This enum implements: /// [`Clone`], [`Eq`], [`PartialEq`], [`Debug`] (with stripped implementation in `not("std")`), -/// `Encode`, `Decode`, `GetDispatchInfo`, `GetCallName`, and `UnfilteredDispatchable`. +/// `Encode`, `Decode`, `GetDispatchInfo`, `GetCallName`, `GetCallIndex` and +/// `UnfilteredDispatchable`. /// /// The macro implements the `Callable` trait on `Pallet` and a function `call_functions` /// which returns the dispatchable metadata. diff --git a/frame/support/procedural/src/pallet/expand/call.rs b/frame/support/procedural/src/pallet/expand/call.rs index 7672609a9..0fdf4455a 100644 --- a/frame/support/procedural/src/pallet/expand/call.rs +++ b/frame/support/procedural/src/pallet/expand/call.rs @@ -324,6 +324,21 @@ pub fn expand_call(def: &mut Def) -> proc_macro2::TokenStream { } } + impl<#type_impl_gen> #frame_support::dispatch::GetCallIndex for #call_ident<#type_use_gen> + #where_clause + { + fn get_call_index(&self) -> u8 { + match *self { + #( Self::#fn_name { .. } => #call_index, )* + Self::__Ignore(_, _) => unreachable!("__PhantomItem cannot be used."), + } + } + + fn get_call_indices() -> &'static [u8] { + &[ #( #call_index, )* ] + } + } + impl<#type_impl_gen> #frame_support::traits::UnfilteredDispatchable for #call_ident<#type_use_gen> #where_clause diff --git a/frame/support/src/dispatch.rs b/frame/support/src/dispatch.rs index 390555b02..1b87acb4d 100644 --- a/frame/support/src/dispatch.rs +++ b/frame/support/src/dispatch.rs @@ -29,7 +29,8 @@ pub use crate::{ result, }, traits::{ - CallMetadata, GetCallMetadata, GetCallName, GetStorageVersion, UnfilteredDispatchable, + CallMetadata, GetCallIndex, GetCallMetadata, GetCallName, GetStorageVersion, + UnfilteredDispatchable, }, }; #[cfg(feature = "std")] diff --git a/frame/support/src/traits.rs b/frame/support/src/traits.rs index 4b92cfbae..17d2fb06c 100644 --- a/frame/support/src/traits.rs +++ b/frame/support/src/traits.rs @@ -74,8 +74,8 @@ pub use randomness::Randomness; mod metadata; pub use metadata::{ - CallMetadata, CrateVersion, GetCallMetadata, GetCallName, GetStorageVersion, PalletInfo, - PalletInfoAccess, PalletInfoData, PalletsInfoAccess, StorageVersion, + CallMetadata, CrateVersion, GetCallIndex, GetCallMetadata, GetCallName, GetStorageVersion, + PalletInfo, PalletInfoAccess, PalletInfoData, PalletsInfoAccess, StorageVersion, STORAGE_VERSION_STORAGE_KEY_POSTFIX, }; diff --git a/frame/support/src/traits/metadata.rs b/frame/support/src/traits/metadata.rs index f3e4b955d..20ddb1d34 100644 --- a/frame/support/src/traits/metadata.rs +++ b/frame/support/src/traits/metadata.rs @@ -101,12 +101,20 @@ pub struct CallMetadata { /// Gets the function name of the Call. pub trait GetCallName { - /// Return all function names. + /// Return all function names in the same order as [`GetCallIndex`]. fn get_call_names() -> &'static [&'static str]; /// Return the function name of the Call. fn get_call_name(&self) -> &'static str; } +/// Gets the function index of the Call. +pub trait GetCallIndex { + /// Return all call indices in the same order as [`GetCallName`]. + fn get_call_indices() -> &'static [u8]; + /// Return the index of this Call. + fn get_call_index(&self) -> u8; +} + /// Gets the metadata for the Call - function name and pallet name. pub trait GetCallMetadata { /// Return all module names. diff --git a/frame/support/test/tests/pallet.rs b/frame/support/test/tests/pallet.rs index b9e531fb4..52acdc5c2 100644 --- a/frame/support/test/tests/pallet.rs +++ b/frame/support/test/tests/pallet.rs @@ -25,8 +25,8 @@ use frame_support::{ pallet_prelude::{StorageInfoTrait, ValueQuery}, storage::unhashed, traits::{ - ConstU32, GetCallName, GetStorageVersion, OnFinalize, OnGenesis, OnInitialize, - OnRuntimeUpgrade, PalletError, PalletInfoAccess, StorageVersion, + ConstU32, GetCallIndex, GetCallName, GetStorageVersion, OnFinalize, OnGenesis, + OnInitialize, OnRuntimeUpgrade, PalletError, PalletInfoAccess, StorageVersion, }, weights::{RuntimeDbWeight, Weight}, }; @@ -231,6 +231,12 @@ pub mod pallet { Ok(().into()) } + #[pallet::call_index(4)] + #[pallet::weight(1)] + pub fn foo_index_out_of_order(_origin: OriginFor) -> DispatchResult { + Ok(()) + } + // Test for DispatchResult return type #[pallet::call_index(2)] #[pallet::weight(1)] @@ -728,8 +734,25 @@ fn call_expand() { assert_eq!(call_foo.get_call_name(), "foo"); assert_eq!( pallet::Call::::get_call_names(), - &["foo", "foo_storage_layer", "foo_no_post_info", "check_for_dispatch_context"], + &[ + "foo", + "foo_storage_layer", + "foo_index_out_of_order", + "foo_no_post_info", + "check_for_dispatch_context" + ], ); + + assert_eq!(call_foo.get_call_index(), 0u8); + assert_eq!(pallet::Call::::get_call_indices(), &[0u8, 1u8, 4u8, 2u8, 3u8]) +} + +#[test] +fn call_expand_index() { + let call_foo = pallet::Call::::foo_index_out_of_order {}; + + assert_eq!(call_foo.get_call_index(), 4u8); + assert_eq!(pallet::Call::::get_call_indices(), &[0u8, 1u8, 4u8, 2u8, 3u8]) } #[test] From a9e85ae50d0efbf66f9a715af3b31bf37b884a6e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bastian=20K=C3=B6cher?= Date: Wed, 22 Mar 2023 20:46:35 +0100 Subject: [PATCH 280/558] Remove unused trait (#13682) --- primitives/core/src/traits.rs | 13 ------------- 1 file changed, 13 deletions(-) diff --git a/primitives/core/src/traits.rs b/primitives/core/src/traits.rs index 0e42ee3ad..091b1cdb1 100644 --- a/primitives/core/src/traits.rs +++ b/primitives/core/src/traits.rs @@ -181,19 +181,6 @@ impl TaskExecutorExt { } } -/// Runtime spawn extension. -pub trait RuntimeSpawn: Send { - /// Create new runtime instance and use dynamic dispatch to invoke with specified payload. - /// - /// Returns handle of the spawned task. - /// - /// Function pointers (`dispatcher_ref`, `func`) are WASM pointer types. - fn spawn_call(&self, dispatcher_ref: u32, func: u32, payload: Vec) -> u64; - - /// Join the result of previously created runtime instance invocation. - fn join(&self, handle: u64) -> Vec; -} - /// Something that can spawn tasks (blocking and non-blocking) with an assigned name /// and optional group. #[dyn_clonable::clonable] From 118414302ace70fff0a913fe7ee825569c16c612 Mon Sep 17 00:00:00 2001 From: Sebastian Kunert Date: Thu, 23 Mar 2023 11:35:39 +0100 Subject: [PATCH 281/558] Remove unused light-client leftover (#13687) * Remove unused light-client leftover * Remove more unused code --- client/network/sync/src/engine.rs | 39 +------------------------------ 1 file changed, 1 insertion(+), 38 deletions(-) diff --git a/client/network/sync/src/engine.rs b/client/network/sync/src/engine.rs index e6e62101f..a9c9afc3c 100644 --- a/client/network/sync/src/engine.rs +++ b/client/network/sync/src/engine.rs @@ -58,10 +58,7 @@ use sc_network_common::{ use sc_utils::mpsc::{tracing_unbounded, TracingUnboundedReceiver, TracingUnboundedSender}; use sp_blockchain::HeaderMetadata; use sp_consensus::block_validation::BlockAnnounceValidator; -use sp_runtime::{ - traits::{Block as BlockT, CheckedSub, Header, NumberFor, Zero}, - SaturatedConversion, -}; +use sp_runtime::traits::{Block as BlockT, Header, NumberFor, Zero}; use std::{ collections::{HashMap, HashSet}, @@ -77,24 +74,15 @@ use std::{ /// Interval at which we perform time based maintenance const TICK_TIMEOUT: std::time::Duration = std::time::Duration::from_millis(1100); -/// When light node connects to the full node and the full node is behind light node -/// for at least `LIGHT_MAXIMAL_BLOCKS_DIFFERENCE` blocks, we consider it not useful -/// and disconnect to free connection slot. -const LIGHT_MAXIMAL_BLOCKS_DIFFERENCE: u64 = 8192; - /// Maximum number of known block hashes to keep for a peer. const MAX_KNOWN_BLOCKS: usize = 1024; // ~32kb per peer + LruHashSet overhead mod rep { use sc_peerset::ReputationChange as Rep; - /// Reputation change when we are a light client and a peer is behind us. - pub const PEER_BEHIND_US_LIGHT: Rep = Rep::new(-(1 << 8), "Useless for a light peer"); /// We received a message that failed to decode. pub const BAD_MESSAGE: Rep = Rep::new(-(1 << 12), "Bad message"); /// Peer has different genesis. pub const GENESIS_MISMATCH: Rep = Rep::new_fatal("Genesis mismatch"); - /// Peer role does not match (e.g. light peer connecting to another light peer). - pub const BAD_ROLE: Rep = Rep::new_fatal("Unsupported role"); /// Peer send us a block announcement that failed at validation. pub const BAD_BLOCK_ANNOUNCEMENT: Rep = Rep::new(-(1 << 12), "Bad block announcement"); } @@ -834,31 +822,6 @@ where return Err(()) } - if self.roles.is_light() { - // we're not interested in light peers - if status.roles.is_light() { - log::debug!(target: "sync", "Peer {} is unable to serve light requests", who); - self.network_service.report_peer(who, rep::BAD_ROLE); - self.network_service - .disconnect_peer(who, self.block_announce_protocol_name.clone()); - return Err(()) - } - - // we don't interested in peers that are far behind us - let self_best_block = self.client.info().best_number; - let blocks_difference = self_best_block - .checked_sub(&status.best_number) - .unwrap_or_else(Zero::zero) - .saturated_into::(); - if blocks_difference > LIGHT_MAXIMAL_BLOCKS_DIFFERENCE { - log::debug!(target: "sync", "Peer {} is far behind us and will unable to serve light requests", who); - self.network_service.report_peer(who, rep::PEER_BEHIND_US_LIGHT); - self.network_service - .disconnect_peer(who, self.block_announce_protocol_name.clone()); - return Err(()) - } - } - let no_slot_peer = self.default_peers_set_no_slot_peers.contains(&who); let this_peer_reserved_slot: usize = if no_slot_peer { 1 } else { 0 }; From 988f6add941b57ceb2af00ab815b3727f03fc1fb Mon Sep 17 00:00:00 2001 From: Alexandru Vasile <60601340+lexnv@users.noreply.github.com> Date: Thu, 23 Mar 2023 15:40:02 +0200 Subject: [PATCH 282/558] chain_head/tests: Check finalized block event before new block (#13680) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * chain_head/tests: Mock client for custom block notification Signed-off-by: Alexandru Vasile * chain_head/tests: Check finalized block event before new block Signed-off-by: Alexandru Vasile * Update client/rpc-spec-v2/src/chain_head/test_utils.rs Co-authored-by: Bastian Köcher * Update client/rpc-spec-v2/src/chain_head/test_utils.rs Co-authored-by: Bastian Köcher * Update client/rpc-spec-v2/src/chain_head/test_utils.rs Co-authored-by: Bastian Köcher * chain_head/tests: Run import events with 10min timeout Signed-off-by: Alexandru Vasile * chain_head/tests: Add comments about test Signed-off-by: Alexandru Vasile --------- Signed-off-by: Alexandru Vasile Co-authored-by: Bastian Köcher --- Cargo.lock | 3 +- client/rpc-spec-v2/Cargo.toml | 1 + client/rpc-spec-v2/src/chain_head/mod.rs | 2 + .../rpc-spec-v2/src/chain_head/test_utils.rs | 320 ++++++++++++++++++ client/rpc-spec-v2/src/chain_head/tests.rs | 103 +++++- 5 files changed, 427 insertions(+), 2 deletions(-) create mode 100644 client/rpc-spec-v2/src/chain_head/test_utils.rs diff --git a/Cargo.lock b/Cargo.lock index 5b2879e1b..a645ecec6 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -9275,6 +9275,7 @@ dependencies = [ "sc-chain-spec", "sc-client-api", "sc-transaction-pool-api", + "sc-utils", "serde", "serde_json", "sp-api", @@ -11954,7 +11955,7 @@ checksum = "97fee6b57c6a41524a810daee9286c02d7752c4253064d0b05472833a438f675" dependencies = [ "cfg-if", "digest 0.10.6", - "rand 0.7.3", + "rand 0.8.5", "static_assertions", ] diff --git a/client/rpc-spec-v2/Cargo.toml b/client/rpc-spec-v2/Cargo.toml index 43fb18908..23b96877f 100644 --- a/client/rpc-spec-v2/Cargo.toml +++ b/client/rpc-spec-v2/Cargo.toml @@ -43,4 +43,5 @@ substrate-test-runtime = { version = "2.0.0", path = "../../test-utils/runtime" sp-consensus = { version = "0.10.0-dev", path = "../../primitives/consensus/common" } sp-maybe-compressed-blob = { version = "4.1.0-dev", path = "../../primitives/maybe-compressed-blob" } sc-block-builder = { version = "0.10.0-dev", path = "../block-builder" } +sc-utils = { version = "4.0.0-dev", path = "../utils" } assert_matches = "1.3.0" diff --git a/client/rpc-spec-v2/src/chain_head/mod.rs b/client/rpc-spec-v2/src/chain_head/mod.rs index afa8d3b21..1c489d323 100644 --- a/client/rpc-spec-v2/src/chain_head/mod.rs +++ b/client/rpc-spec-v2/src/chain_head/mod.rs @@ -22,6 +22,8 @@ //! //! Methods are prefixed by `chainHead`. +#[cfg(test)] +mod test_utils; #[cfg(test)] mod tests; diff --git a/client/rpc-spec-v2/src/chain_head/test_utils.rs b/client/rpc-spec-v2/src/chain_head/test_utils.rs new file mode 100644 index 000000000..ee563debb --- /dev/null +++ b/client/rpc-spec-v2/src/chain_head/test_utils.rs @@ -0,0 +1,320 @@ +// This file is part of Substrate. + +// Copyright (C) Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +use parking_lot::Mutex; +use sc_client_api::{ + execution_extensions::ExecutionExtensions, BlockBackend, BlockImportNotification, + BlockchainEvents, CallExecutor, ChildInfo, ExecutorProvider, FinalityNotification, + FinalityNotifications, FinalizeSummary, ImportNotifications, KeysIter, PairsIter, StorageData, + StorageEventStream, StorageKey, StorageProvider, +}; +use sc_utils::mpsc::{tracing_unbounded, TracingUnboundedSender}; +use sp_api::{CallApiAt, CallApiAtParams, NumberFor, RuntimeVersion}; +use sp_blockchain::{BlockStatus, CachedHeaderMetadata, HeaderBackend, HeaderMetadata, Info}; +use sp_consensus::BlockOrigin; +use sp_runtime::{ + generic::SignedBlock, + traits::{Block as BlockT, Header as HeaderT}, + Justifications, +}; +use std::sync::Arc; +use substrate_test_runtime::{Block, Hash, Header}; + +pub struct ChainHeadMockClient { + client: Arc, + import_sinks: Mutex>>>, + finality_sinks: Mutex>>>, +} + +impl ChainHeadMockClient { + pub fn new(client: Arc) -> Self { + ChainHeadMockClient { + client, + import_sinks: Default::default(), + finality_sinks: Default::default(), + } + } + + pub async fn trigger_import_stream(&self, header: Header) { + // Ensure the client called the `import_notification_stream`. + while self.import_sinks.lock().is_empty() { + tokio::time::sleep(tokio::time::Duration::from_secs(1)).await; + } + + // Build the notification. + let (sink, _stream) = tracing_unbounded("test_sink", 100_000); + let notification = + BlockImportNotification::new(header.hash(), BlockOrigin::Own, header, true, None, sink); + + for sink in self.import_sinks.lock().iter_mut() { + sink.unbounded_send(notification.clone()).unwrap(); + } + } + + pub async fn trigger_finality_stream(&self, header: Header) { + // Ensure the client called the `finality_notification_stream`. + while self.finality_sinks.lock().is_empty() { + tokio::time::sleep(tokio::time::Duration::from_secs(1)).await; + } + + // Build the notification. + let (sink, _stream) = tracing_unbounded("test_sink", 100_000); + let summary = FinalizeSummary { + header: header.clone(), + finalized: vec![header.hash()], + stale_heads: vec![], + }; + let notification = FinalityNotification::from_summary(summary, sink); + + for sink in self.finality_sinks.lock().iter_mut() { + sink.unbounded_send(notification.clone()).unwrap(); + } + } +} + +// ChainHead calls `import_notification_stream` and `finality_notification_stream` in order to +// subscribe to block events. +impl BlockchainEvents for ChainHeadMockClient { + fn import_notification_stream(&self) -> ImportNotifications { + let (sink, stream) = tracing_unbounded("import_notification_stream", 1024); + self.import_sinks.lock().push(sink); + stream + } + + fn every_import_notification_stream(&self) -> ImportNotifications { + unimplemented!() + } + + fn finality_notification_stream(&self) -> FinalityNotifications { + let (sink, stream) = tracing_unbounded("finality_notification_stream", 1024); + self.finality_sinks.lock().push(sink); + stream + } + + fn storage_changes_notification_stream( + &self, + _filter_keys: Option<&[StorageKey]>, + _child_filter_keys: Option<&[(StorageKey, Option>)]>, + ) -> sp_blockchain::Result> { + unimplemented!() + } +} + +// The following implementations are imposed by the `chainHead` trait bounds. + +impl, Client: ExecutorProvider> + ExecutorProvider for ChainHeadMockClient +{ + type Executor = >::Executor; + + fn executor(&self) -> &Self::Executor { + self.client.executor() + } + + fn execution_extensions(&self) -> &ExecutionExtensions { + self.client.execution_extensions() + } +} + +impl< + BE: sc_client_api::backend::Backend + Send + Sync + 'static, + Block: BlockT, + Client: StorageProvider, + > StorageProvider for ChainHeadMockClient +{ + fn storage( + &self, + hash: Block::Hash, + key: &StorageKey, + ) -> sp_blockchain::Result> { + self.client.storage(hash, key) + } + + fn storage_hash( + &self, + hash: Block::Hash, + key: &StorageKey, + ) -> sp_blockchain::Result> { + self.client.storage_hash(hash, key) + } + + fn storage_keys( + &self, + hash: Block::Hash, + prefix: Option<&StorageKey>, + start_key: Option<&StorageKey>, + ) -> sp_blockchain::Result> { + self.client.storage_keys(hash, prefix, start_key) + } + + fn storage_pairs( + &self, + hash: ::Hash, + prefix: Option<&StorageKey>, + start_key: Option<&StorageKey>, + ) -> sp_blockchain::Result> { + self.client.storage_pairs(hash, prefix, start_key) + } + + fn child_storage( + &self, + hash: Block::Hash, + child_info: &ChildInfo, + key: &StorageKey, + ) -> sp_blockchain::Result> { + self.client.child_storage(hash, child_info, key) + } + + fn child_storage_keys( + &self, + hash: Block::Hash, + child_info: ChildInfo, + prefix: Option<&StorageKey>, + start_key: Option<&StorageKey>, + ) -> sp_blockchain::Result> { + self.client.child_storage_keys(hash, child_info, prefix, start_key) + } + + fn child_storage_hash( + &self, + hash: Block::Hash, + child_info: &ChildInfo, + key: &StorageKey, + ) -> sp_blockchain::Result> { + self.client.child_storage_hash(hash, child_info, key) + } +} + +impl> CallApiAt for ChainHeadMockClient { + type StateBackend = >::StateBackend; + + fn call_api_at( + &self, + params: CallApiAtParams>::StateBackend>, + ) -> Result, sp_api::ApiError> { + self.client.call_api_at(params) + } + + fn runtime_version_at(&self, hash: Block::Hash) -> Result { + self.client.runtime_version_at(hash) + } + + fn state_at(&self, at: Block::Hash) -> Result { + self.client.state_at(at) + } +} + +impl> BlockBackend + for ChainHeadMockClient +{ + fn block_body( + &self, + hash: Block::Hash, + ) -> sp_blockchain::Result::Extrinsic>>> { + self.client.block_body(hash) + } + + fn block(&self, hash: Block::Hash) -> sp_blockchain::Result>> { + self.client.block(hash) + } + + fn block_status(&self, hash: Block::Hash) -> sp_blockchain::Result { + self.client.block_status(hash) + } + + fn justifications(&self, hash: Block::Hash) -> sp_blockchain::Result> { + self.client.justifications(hash) + } + + fn block_hash(&self, number: NumberFor) -> sp_blockchain::Result> { + self.client.block_hash(number) + } + + fn indexed_transaction(&self, hash: Block::Hash) -> sp_blockchain::Result>> { + self.client.indexed_transaction(hash) + } + + fn has_indexed_transaction(&self, hash: Block::Hash) -> sp_blockchain::Result { + self.client.has_indexed_transaction(hash) + } + + fn block_indexed_body(&self, hash: Block::Hash) -> sp_blockchain::Result>>> { + self.client.block_indexed_body(hash) + } + fn requires_full_sync(&self) -> bool { + self.client.requires_full_sync() + } +} + +impl + Send + Sync> HeaderMetadata + for ChainHeadMockClient +{ + type Error = >::Error; + + fn header_metadata( + &self, + hash: Block::Hash, + ) -> Result, Self::Error> { + self.client.header_metadata(hash) + } + + fn insert_header_metadata( + &self, + hash: Block::Hash, + header_metadata: CachedHeaderMetadata, + ) { + self.client.insert_header_metadata(hash, header_metadata) + } + + fn remove_header_metadata(&self, hash: Block::Hash) { + self.client.remove_header_metadata(hash) + } +} + +impl + Send + Sync> HeaderBackend + for ChainHeadMockClient +{ + fn header( + &self, + hash: Block::Hash, + ) -> sp_blockchain::Result::Header>> { + self.client.header(hash) + } + + fn info(&self) -> Info { + self.client.info() + } + + fn status(&self, hash: Block::Hash) -> sc_client_api::blockchain::Result { + self.client.status(hash) + } + + fn number( + &self, + hash: Block::Hash, + ) -> sc_client_api::blockchain::Result::Header as HeaderT>::Number>> { + self.client.number(hash) + } + + fn hash( + &self, + number: <::Header as HeaderT>::Number, + ) -> sp_blockchain::Result> { + self.client.hash(number) + } +} diff --git a/client/rpc-spec-v2/src/chain_head/tests.rs b/client/rpc-spec-v2/src/chain_head/tests.rs index fcd906dcf..1d5cb8da2 100644 --- a/client/rpc-spec-v2/src/chain_head/tests.rs +++ b/client/rpc-spec-v2/src/chain_head/tests.rs @@ -1,6 +1,9 @@ +use crate::chain_head::test_utils::ChainHeadMockClient; + use super::*; use assert_matches::assert_matches; use codec::{Decode, Encode}; +use futures::Future; use jsonrpsee::{ core::{error::Error, server::rpc_module::Subscription as RpcSubscription}, types::{error::CallError, EmptyServerParams as EmptyParams}, @@ -33,7 +36,7 @@ const CHILD_STORAGE_KEY: &[u8] = b"child"; const CHILD_VALUE: &[u8] = b"child value"; async fn get_next_event(sub: &mut RpcSubscription) -> T { - let (event, _sub_id) = tokio::time::timeout(std::time::Duration::from_secs(1), sub.next()) + let (event, _sub_id) = tokio::time::timeout(std::time::Duration::from_secs(60), sub.next()) .await .unwrap() .unwrap() @@ -41,6 +44,12 @@ async fn get_next_event(sub: &mut RpcSubscriptio event } +async fn run_with_timeout(future: F) -> ::Output { + tokio::time::timeout(std::time::Duration::from_secs(60 * 10), future) + .await + .unwrap() +} + async fn setup_api() -> ( Arc>, RpcModule>>, @@ -1317,3 +1326,95 @@ async fn follow_report_multiple_pruned_block() { }); assert_eq!(event, expected); } + +#[tokio::test] +async fn follow_finalized_before_new_block() { + let builder = TestClientBuilder::new(); + let backend = builder.backend(); + let mut client = Arc::new(builder.build()); + + let client_mock = Arc::new(ChainHeadMockClient::new(client.clone())); + + let api = ChainHead::new( + client_mock.clone(), + backend, + Arc::new(TaskExecutor::default()), + CHAIN_GENESIS, + MAX_PINNED_BLOCKS, + ) + .into_rpc(); + + // Make sure the block is imported for it to be pinned. + let block_1 = client.new_block(Default::default()).unwrap().build().unwrap().block; + let block_1_hash = block_1.header.hash(); + client.import(BlockOrigin::Own, block_1.clone()).await.unwrap(); + + let mut sub = api.subscribe("chainHead_unstable_follow", [false]).await.unwrap(); + + // Trigger the `FinalizedNotification` for block 1 before the `BlockImportNotification`, and + // expect for the `chainHead` to generate `NewBlock`, `BestBlock` and `Finalized` events. + + // Trigger the Finalized notification before the NewBlock one. + run_with_timeout(client_mock.trigger_finality_stream(block_1.header.clone())).await; + + // Initialized must always be reported first. + let finalized_hash = client.info().finalized_hash; + let event: FollowEvent = get_next_event(&mut sub).await; + let expected = FollowEvent::Initialized(Initialized { + finalized_block_hash: format!("{:?}", finalized_hash), + finalized_block_runtime: None, + runtime_updates: false, + }); + assert_eq!(event, expected); + + // Block 1 must be reported because we triggered the finalized notification. + let event: FollowEvent = get_next_event(&mut sub).await; + let expected = FollowEvent::NewBlock(NewBlock { + block_hash: format!("{:?}", block_1_hash), + parent_block_hash: format!("{:?}", finalized_hash), + new_runtime: None, + runtime_updates: false, + }); + assert_eq!(event, expected); + + let event: FollowEvent = get_next_event(&mut sub).await; + let expected = FollowEvent::BestBlockChanged(BestBlockChanged { + best_block_hash: format!("{:?}", block_1_hash), + }); + assert_eq!(event, expected); + + let event: FollowEvent = get_next_event(&mut sub).await; + let expected = FollowEvent::Finalized(Finalized { + finalized_block_hashes: vec![format!("{:?}", block_1_hash)], + pruned_block_hashes: vec![], + }); + assert_eq!(event, expected); + + let block_2 = client.new_block(Default::default()).unwrap().build().unwrap().block; + let block_2_hash = block_2.header.hash(); + client.import(BlockOrigin::Own, block_2.clone()).await.unwrap(); + + // Triggering the `BlockImportNotification` notification for block 1 should have no effect + // on the notification because the events were handled by the `FinalizedNotification`. + // Also trigger the `BlockImportNotification` notification for block 2 to ensure + // `NewBlock and `BestBlock` events are generated. + + // Trigger NewBlock notification for block 1 and block 2. + run_with_timeout(client_mock.trigger_import_stream(block_1.header)).await; + run_with_timeout(client_mock.trigger_import_stream(block_2.header)).await; + + let event: FollowEvent = get_next_event(&mut sub).await; + let expected = FollowEvent::NewBlock(NewBlock { + block_hash: format!("{:?}", block_2_hash), + parent_block_hash: format!("{:?}", block_1_hash), + new_runtime: None, + runtime_updates: false, + }); + assert_eq!(event, expected); + + let event: FollowEvent = get_next_event(&mut sub).await; + let expected = FollowEvent::BestBlockChanged(BestBlockChanged { + best_block_hash: format!("{:?}", block_2_hash), + }); + assert_eq!(event, expected); +} From 905861780ee0f6556d915a70a7a49371fc1fad2c Mon Sep 17 00:00:00 2001 From: Oliver Tale-Yazdi Date: Thu, 23 Mar 2023 18:09:29 +0100 Subject: [PATCH 283/558] Remove Weight::without_{ref_time, proof_size} (#13637) Signed-off-by: Oliver Tale-Yazdi --- primitives/weights/src/weight_v2.rs | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/primitives/weights/src/weight_v2.rs b/primitives/weights/src/weight_v2.rs index 76c40c16c..ca1371459 100644 --- a/primitives/weights/src/weight_v2.rs +++ b/primitives/weights/src/weight_v2.rs @@ -74,16 +74,6 @@ impl Weight { &mut self.proof_size } - /// Return self but discard any reference time. - pub const fn without_ref_time(&self) -> Self { - Self { ref_time: 0, proof_size: self.proof_size } - } - - /// Return self but discard any proof size. - pub const fn without_proof_size(&self) -> Self { - Self { ref_time: self.ref_time, proof_size: 0 } - } - pub const MAX: Self = Self { ref_time: u64::MAX, proof_size: u64::MAX }; /// Get the conservative min of `self` and `other` weight. From bf395c8308c481a9774373e0b0b14bd7a2e4b8d2 Mon Sep 17 00:00:00 2001 From: Davide Galassi Date: Fri, 24 Mar 2023 14:46:02 +0100 Subject: [PATCH 284/558] Keystore overhaul (final) (#13683) * Introduce keystore specialized sign methods * Get rid of 'AppKey::UntypedGeneric' associated type. Untyped generics are accessible using associated types 'Generic' associated type. I.e. ::Public::Generic * Get rid of 'CryptoTypePublicPair' * Trivial fix * Small refactory of local keystore implementations * Remove 'crypto_id' method from 'Public' * Trivial rename of 'AppKey' to 'AppCrypto' * Remove unused import * Improve docs * Better signature related errors for authority-discovery * Apply review suggestion * Apply review suggestions Co-authored-by: Koute * Authority discoverty signing error revisited * Signing error revisited for babe and aura as well * Further cleanup --------- Co-authored-by: Koute --- bin/node/cli/src/service.rs | 12 +- bin/node/executor/tests/submit_transaction.rs | 2 +- client/authority-discovery/src/error.rs | 9 +- client/authority-discovery/src/worker.rs | 27 ++- client/consensus/aura/src/lib.rs | 59 +++-- client/consensus/babe/rpc/src/lib.rs | 2 +- client/consensus/babe/src/authorship.rs | 2 +- client/consensus/babe/src/lib.rs | 47 ++-- client/consensus/grandpa/src/lib.rs | 2 +- client/consensus/slots/src/lib.rs | 2 +- client/keystore/src/local.rs | 214 +++++++++--------- client/rpc/src/author/tests.rs | 14 +- primitives/application-crypto/src/lib.rs | 39 +--- primitives/application-crypto/src/traits.rs | 29 ++- .../application-crypto/test/src/ecdsa.rs | 11 +- .../application-crypto/test/src/ed25519.rs | 11 +- .../application-crypto/test/src/sr25519.rs | 11 +- primitives/consensus/common/src/error.rs | 4 +- primitives/consensus/grandpa/src/lib.rs | 5 +- primitives/core/src/crypto.rs | 37 +-- primitives/core/src/ecdsa.rs | 21 +- primitives/core/src/ed25519.rs | 22 +- primitives/core/src/sr25519.rs | 23 +- primitives/io/src/lib.rs | 9 +- primitives/keystore/src/lib.rs | 191 ++++++++++------ primitives/keystore/src/testing.rs | 197 ++++++++-------- primitives/runtime/src/traits.rs | 18 +- 27 files changed, 478 insertions(+), 542 deletions(-) diff --git a/bin/node/cli/src/service.rs b/bin/node/cli/src/service.rs index be8553b7b..ca6830085 100644 --- a/bin/node/cli/src/service.rs +++ b/bin/node/cli/src/service.rs @@ -592,7 +592,7 @@ mod tests { use sc_service_test::TestNetNode; use sc_transaction_pool_api::{ChainEvent, MaintainedTransactionPool}; use sp_consensus::{BlockOrigin, Environment, Proposer}; - use sp_core::{crypto::Pair as CryptoPair, Public}; + use sp_core::crypto::Pair; use sp_inherents::InherentDataProvider; use sp_keyring::AccountKeyring; use sp_keystore::KeystorePtr; @@ -737,16 +737,10 @@ mod tests { // add it to a digest item. let to_sign = pre_hash.encode(); let signature = keystore - .sign_with( - sp_consensus_babe::AuthorityId::ID, - &alice.to_public_crypto_pair(), - &to_sign, - ) - .unwrap() + .sr25519_sign(sp_consensus_babe::AuthorityId::ID, alice.as_ref(), &to_sign) .unwrap() - .try_into() .unwrap(); - let item = ::babe_seal(signature); + let item = ::babe_seal(signature.into()); slot += 1; let mut params = BlockImportParams::new(BlockOrigin::File, new_header); diff --git a/bin/node/executor/tests/submit_transaction.rs b/bin/node/executor/tests/submit_transaction.rs index 9bff415d2..b260f90a8 100644 --- a/bin/node/executor/tests/submit_transaction.rs +++ b/bin/node/executor/tests/submit_transaction.rs @@ -18,7 +18,7 @@ use codec::Decode; use frame_system::offchain::{SendSignedTransaction, Signer, SubmitTransaction}; use kitchensink_runtime::{Executive, Indices, Runtime, UncheckedExtrinsic}; -use sp_application_crypto::AppKey; +use sp_application_crypto::AppCrypto; use sp_core::offchain::{testing::TestTransactionPoolExt, TransactionPoolExt}; use sp_keyring::sr25519::Keyring::Alice; use sp_keystore::{testing::MemoryKeystore, Keystore, KeystoreExt}; diff --git a/client/authority-discovery/src/error.rs b/client/authority-discovery/src/error.rs index 89c05b71b..447116b0f 100644 --- a/client/authority-discovery/src/error.rs +++ b/client/authority-discovery/src/error.rs @@ -18,8 +18,6 @@ //! Authority discovery errors. -use sp_core::crypto::CryptoTypePublicPair; - /// AuthorityDiscovery Result. pub type Result = std::result::Result; @@ -59,11 +57,8 @@ pub enum Error { #[error("Failed to parse a libp2p key.")] ParsingLibp2pIdentity(#[from] libp2p::identity::error::DecodingError), - #[error("Failed to sign using a specific public key.")] - MissingSignature(CryptoTypePublicPair), - - #[error("Failed to sign using all public keys.")] - Signing, + #[error("Failed to sign: {0}.")] + CannotSign(String), #[error("Failed to register Prometheus metric.")] Prometheus(#[from] prometheus_endpoint::PrometheusError), diff --git a/client/authority-discovery/src/worker.rs b/client/authority-discovery/src/worker.rs index 5d7d21a5b..43e44cac4 100644 --- a/client/authority-discovery/src/worker.rs +++ b/client/authority-discovery/src/worker.rs @@ -32,7 +32,7 @@ use std::{ use futures::{channel::mpsc, future, stream::Fuse, FutureExt, Stream, StreamExt}; use addr_cache::AddrCache; -use codec::Decode; +use codec::{Decode, Encode}; use ip_network::IpNetwork; use libp2p::{ core::multiaddr, @@ -43,6 +43,7 @@ use log::{debug, error, log_enabled}; use prometheus_endpoint::{register, Counter, CounterVec, Gauge, Opts, U64}; use prost::Message; use rand::{seq::SliceRandom, thread_rng}; + use sc_network::{ event::DhtEvent, KademliaKey, NetworkDHTProvider, NetworkSigner, NetworkStateInfo, Signature, }; @@ -51,8 +52,7 @@ use sp_authority_discovery::{ AuthorityDiscoveryApi, AuthorityId, AuthorityPair, AuthoritySignature, }; use sp_blockchain::HeaderBackend; - -use sp_core::crypto::{key_types, CryptoTypePublicPair, Pair}; +use sp_core::crypto::{key_types, ByteArray, Pair}; use sp_keystore::{Keystore, KeystorePtr}; use sp_runtime::traits::Block as BlockT; @@ -127,7 +127,7 @@ pub struct Worker { publish_if_changed_interval: ExpIncInterval, /// List of keys onto which addresses have been published at the latest publication. /// Used to check whether they have changed. - latest_published_keys: HashSet, + latest_published_keys: HashSet, /// Same value as in the configuration. publish_non_global_ips: bool, /// Same value as in the configuration. @@ -339,7 +339,7 @@ where let keys = Worker::::get_own_public_keys_within_authority_set( key_store.clone(), self.client.as_ref(), - ).await?.into_iter().map(Into::into).collect::>(); + ).await?.into_iter().collect::>(); if only_if_changed && keys == self.latest_published_keys { return Ok(()) @@ -654,7 +654,7 @@ fn sign_record_with_peer_id( ) -> Result { let signature = network .sign_with_local_identity(serialized_record) - .map_err(|_| Error::Signing)?; + .map_err(|e| Error::CannotSign(format!("{} (network packet)", e)))?; let public_key = signature.public_key.to_protobuf_encoding(); let signature = signature.bytes; Ok(schema::PeerSignature { signature, public_key }) @@ -664,15 +664,20 @@ fn sign_record_with_authority_ids( serialized_record: Vec, peer_signature: Option, key_store: &dyn Keystore, - keys: Vec, + keys: Vec, ) -> Result)>> { let mut result = Vec::with_capacity(keys.len()); for key in keys.iter() { let auth_signature = key_store - .sign_with(key_types::AUTHORITY_DISCOVERY, key, &serialized_record) - .map_err(|_| Error::Signing)? - .ok_or_else(|| Error::MissingSignature(key.clone()))?; + .sr25519_sign(key_types::AUTHORITY_DISCOVERY, key.as_ref(), &serialized_record) + .map_err(|e| Error::CannotSign(format!("{}. Key: {:?}", e, key)))? + .ok_or_else(|| { + Error::CannotSign(format!("Could not find key in keystore. Key: {:?}", key)) + })?; + + // Scale encode + let auth_signature = auth_signature.encode(); let signed_record = schema::SignedAuthorityRecord { record: serialized_record.clone(), @@ -681,7 +686,7 @@ fn sign_record_with_authority_ids( } .encode_to_vec(); - result.push((hash_authority_id(&key.1), signed_record)); + result.push((hash_authority_id(key.as_slice()), signed_record)); } Ok(result) diff --git a/client/consensus/aura/src/lib.rs b/client/consensus/aura/src/lib.rs index d06a6016a..a48eeac5c 100644 --- a/client/consensus/aura/src/lib.rs +++ b/client/consensus/aura/src/lib.rs @@ -45,7 +45,7 @@ use sc_consensus_slots::{ }; use sc_telemetry::TelemetryHandle; use sp_api::{Core, ProvideRuntimeApi}; -use sp_application_crypto::{AppKey, AppPublic}; +use sp_application_crypto::{AppCrypto, AppPublic}; use sp_blockchain::{HeaderBackend, Result as CResult}; use sp_consensus::{BlockOrigin, Environment, Error as ConsensusError, Proposer, SelectChain}; use sp_consensus_slots::Slot; @@ -205,7 +205,7 @@ pub fn start_aura( telemetry, compatibility_mode, }: StartAuraParams>, -) -> Result, sp_consensus::Error> +) -> Result, ConsensusError> where P: Pair + Send + Sync, P::Public: AppPublic + Hash + Member + Encode + Decode, @@ -222,7 +222,7 @@ where CIDP: CreateInherentDataProviders + Send + 'static, CIDP::InherentDataProviders: InherentDataProviderExt + Send, BS: BackoffAuthoringBlocksStrategy> + Send + Sync + 'static, - Error: std::error::Error + Send + From + 'static, + Error: std::error::Error + Send + From + 'static, { let worker = build_aura_worker::(BuildAuraWorkerParams { client, @@ -320,7 +320,7 @@ where P::Public: AppPublic + Hash + Member + Encode + Decode, P::Signature: TryFrom> + Hash + Member + Encode + Decode, I: BlockImport> + Send + Sync + 'static, - Error: std::error::Error + Send + From + 'static, + Error: std::error::Error + Send + From + 'static, SO: SyncOracle + Send + Sync + Clone, L: sc_consensus::JustificationSyncLink, BS: BackoffAuthoringBlocksStrategy> + Send + Sync + 'static, @@ -374,13 +374,13 @@ where SO: SyncOracle + Send + Clone + Sync, L: sc_consensus::JustificationSyncLink, BS: BackoffAuthoringBlocksStrategy> + Send + Sync + 'static, - Error: std::error::Error + Send + From + 'static, + Error: std::error::Error + Send + From + 'static, { type BlockImport = I; type SyncOracle = SO; type JustificationSyncLink = L; type CreateProposer = - Pin> + Send + 'static>>; + Pin> + Send + 'static>>; type Proposer = E::Proposer; type Claim = P::Public; type AuxData = Vec>; @@ -393,11 +393,7 @@ where &mut self.block_import } - fn aux_data( - &self, - header: &B::Header, - _slot: Slot, - ) -> Result { + fn aux_data(&self, header: &B::Header, _slot: Slot) -> Result { authorities( self.client.as_ref(), header.hash(), @@ -406,17 +402,17 @@ where ) } - fn authorities_len(&self, epoch_data: &Self::AuxData) -> Option { - Some(epoch_data.len()) + fn authorities_len(&self, authorities: &Self::AuxData) -> Option { + Some(authorities.len()) } async fn claim_slot( &self, _header: &B::Header, slot: Slot, - epoch_data: &Self::AuxData, + authorities: &Self::AuxData, ) -> Option { - let expected_author = slot_author::

(slot, epoch_data); + let expected_author = slot_author::

(slot, authorities); expected_author.and_then(|p| { if self .keystore @@ -440,29 +436,30 @@ where body: Vec, storage_changes: StorageChanges<>::Transaction, B>, public: Self::Claim, - _epoch: Self::AuxData, + _authorities: Self::AuxData, ) -> Result< sc_consensus::BlockImportParams>::Transaction>, - sp_consensus::Error, + ConsensusError, > { - // sign the pre-sealed hash of the block and then - // add it to a digest item. - let public_type_pair = public.to_public_crypto_pair(); - let public = public.to_raw_vec(); let signature = self .keystore - .sign_with( as AppKey>::ID, &public_type_pair, header_hash.as_ref()) - .map_err(|e| sp_consensus::Error::CannotSign(public.clone(), e.to_string()))? + .sign_with( + as AppCrypto>::ID, + as AppCrypto>::CRYPTO_ID, + public.as_slice(), + header_hash.as_ref(), + ) + .map_err(|e| ConsensusError::CannotSign(format!("{}. Key: {:?}", e, public)))? .ok_or_else(|| { - sp_consensus::Error::CannotSign( - public.clone(), - "Could not find key in keystore.".into(), - ) + ConsensusError::CannotSign(format!( + "Could not find key in keystore. Key: {:?}", + public + )) })?; let signature = signature .clone() .try_into() - .map_err(|_| sp_consensus::Error::InvalidSignature(signature, public))?; + .map_err(|_| ConsensusError::InvalidSignature(signature, public.to_raw_vec()))?; let signature_digest_item = >::aura_seal(signature); @@ -507,7 +504,7 @@ where fn proposer(&mut self, block: &B::Header) -> Self::CreateProposer { self.env .init(block) - .map_err(|e| sp_consensus::Error::ClientImport(format!("{:?}", e))) + .map_err(|e| ConsensusError::ClientImport(format!("{:?}", e))) .boxed() } @@ -620,14 +617,14 @@ where Default::default(), ), ) - .map_err(|_| sp_consensus::Error::InvalidAuthoritiesSet)?; + .map_err(|_| ConsensusError::InvalidAuthoritiesSet)?; }, } runtime_api .authorities(parent_hash) .ok() - .ok_or(sp_consensus::Error::InvalidAuthoritiesSet) + .ok_or(ConsensusError::InvalidAuthoritiesSet) } #[cfg(test)] diff --git a/client/consensus/babe/rpc/src/lib.rs b/client/consensus/babe/rpc/src/lib.rs index 005d84938..cdc8dbfd8 100644 --- a/client/consensus/babe/rpc/src/lib.rs +++ b/client/consensus/babe/rpc/src/lib.rs @@ -30,7 +30,7 @@ use sc_consensus_epochs::{descendent_query, Epoch as EpochT, SharedEpochChanges} use sc_rpc_api::DenyUnsafe; use serde::{Deserialize, Serialize}; use sp_api::ProvideRuntimeApi; -use sp_application_crypto::AppKey; +use sp_application_crypto::AppCrypto; use sp_blockchain::{Error as BlockChainError, HeaderBackend, HeaderMetadata}; use sp_consensus::{Error as ConsensusError, SelectChain}; use sp_consensus_babe::{ diff --git a/client/consensus/babe/src/authorship.rs b/client/consensus/babe/src/authorship.rs index c4b43b0d0..3ff4c393f 100644 --- a/client/consensus/babe/src/authorship.rs +++ b/client/consensus/babe/src/authorship.rs @@ -22,7 +22,7 @@ use super::Epoch; use codec::Encode; use sc_consensus_epochs::Epoch as EpochT; use schnorrkel::{keys::PublicKey, vrf::VRFInOut}; -use sp_application_crypto::AppKey; +use sp_application_crypto::AppCrypto; use sp_consensus_babe::{ digests::{PreDigest, PrimaryPreDigest, SecondaryPlainPreDigest, SecondaryVRFPreDigest}, make_transcript, make_transcript_data, AuthorityId, BabeAuthorityWeight, Slot, BABE_VRF_PREFIX, diff --git a/client/consensus/babe/src/lib.rs b/client/consensus/babe/src/lib.rs index e2c2793b1..a59870ced 100644 --- a/client/consensus/babe/src/lib.rs +++ b/client/consensus/babe/src/lib.rs @@ -108,7 +108,7 @@ use sc_consensus_slots::{ }; use sc_telemetry::{telemetry, TelemetryHandle, CONSENSUS_DEBUG, CONSENSUS_TRACE}; use sp_api::{ApiExt, ProvideRuntimeApi}; -use sp_application_crypto::AppKey; +use sp_application_crypto::AppCrypto; use sp_block_builder::BlockBuilder as BlockBuilderApi; use sp_blockchain::{ Backend as _, BlockStatus, Error as ClientError, ForkBackend, HeaderBackend, HeaderMetadata, @@ -117,7 +117,7 @@ use sp_blockchain::{ use sp_consensus::{BlockOrigin, Environment, Error as ConsensusError, Proposer, SelectChain}; use sp_consensus_babe::inherents::BabeInherentData; use sp_consensus_slots::Slot; -use sp_core::{crypto::ByteArray, ExecutionContext}; +use sp_core::ExecutionContext; use sp_inherents::{CreateInherentDataProviders, InherentData, InherentDataProvider}; use sp_keystore::KeystorePtr; use sp_runtime::{ @@ -274,7 +274,7 @@ pub enum Error { MultipleConfigChangeDigests, /// Could not extract timestamp and slot #[error("Could not extract timestamp and slot: {0}")] - Extraction(sp_consensus::Error), + Extraction(ConsensusError), /// Could not fetch epoch #[error("Could not fetch epoch at {0:?}")] FetchEpoch(B::Hash), @@ -471,7 +471,7 @@ pub fn start_babe( max_block_proposal_slot_portion, telemetry, }: BabeParams, -) -> Result, sp_consensus::Error> +) -> Result, ConsensusError> where B: BlockT, C: ProvideRuntimeApi @@ -739,7 +739,7 @@ where type SyncOracle = SO; type JustificationSyncLink = L; type CreateProposer = - Pin> + Send + 'static>>; + Pin> + Send + 'static>>; type Proposer = E::Proposer; type BlockImport = I; type AuxData = ViableEpochDescriptor, Epoch>; @@ -762,7 +762,7 @@ where slot, ) .map_err(|e| ConsensusError::ChainLookup(e.to_string()))? - .ok_or(sp_consensus::Error::InvalidAuthoritiesSet) + .ok_or(ConsensusError::InvalidAuthoritiesSet) } fn authorities_len(&self, epoch_descriptor: &Self::AuxData) -> Option { @@ -827,28 +827,21 @@ where (_, public): Self::Claim, epoch_descriptor: Self::AuxData, ) -> Result< - sc_consensus::BlockImportParams>::Transaction>, - sp_consensus::Error, + BlockImportParams>::Transaction>, + ConsensusError, > { - // sign the pre-sealed hash of the block and then - // add it to a digest item. - let public_type_pair = public.clone().into(); - let public = public.to_raw_vec(); let signature = self .keystore - .sign_with(::ID, &public_type_pair, header_hash.as_ref()) - .map_err(|e| sp_consensus::Error::CannotSign(public.clone(), e.to_string()))? + .sr25519_sign(::ID, public.as_ref(), header_hash.as_ref()) + .map_err(|e| ConsensusError::CannotSign(format!("{}. Key: {:?}", e, public)))? .ok_or_else(|| { - sp_consensus::Error::CannotSign( - public.clone(), - "Could not find key in keystore.".into(), - ) + ConsensusError::CannotSign(format!( + "Could not find key in keystore. Key: {:?}", + public + )) })?; - let signature: AuthoritySignature = signature - .clone() - .try_into() - .map_err(|_| sp_consensus::Error::InvalidSignature(signature, public))?; - let digest_item = ::babe_seal(signature); + + let digest_item = ::babe_seal(signature.into()); let mut import_block = BlockImportParams::new(BlockOrigin::Own, header); import_block.post_digests.push(digest_item); @@ -891,11 +884,7 @@ where } fn proposer(&mut self, block: &B::Header) -> Self::CreateProposer { - Box::pin( - self.env - .init(block) - .map_err(|e| sp_consensus::Error::ClientImport(format!("{:?}", e))), - ) + Box::pin(self.env.init(block).map_err(|e| ConsensusError::ClientImport(e.to_string()))) } fn telemetry(&self) -> Option { @@ -1182,7 +1171,7 @@ where .create_inherent_data_providers .create_inherent_data_providers(parent_hash, ()) .await - .map_err(|e| Error::::Client(sp_consensus::Error::from(e).into()))?; + .map_err(|e| Error::::Client(ConsensusError::from(e).into()))?; let slot_now = create_inherent_data_providers.slot(); diff --git a/client/consensus/grandpa/src/lib.rs b/client/consensus/grandpa/src/lib.rs index cf98e1792..90cb8a51f 100644 --- a/client/consensus/grandpa/src/lib.rs +++ b/client/consensus/grandpa/src/lib.rs @@ -72,7 +72,7 @@ use sc_network::types::ProtocolName; use sc_telemetry::{telemetry, TelemetryHandle, CONSENSUS_DEBUG, CONSENSUS_INFO}; use sc_utils::mpsc::{tracing_unbounded, TracingUnboundedReceiver}; use sp_api::ProvideRuntimeApi; -use sp_application_crypto::AppKey; +use sp_application_crypto::AppCrypto; use sp_blockchain::{Error as ClientError, HeaderBackend, HeaderMetadata, Result as ClientResult}; use sp_consensus::SelectChain; use sp_consensus_grandpa::{ diff --git a/client/consensus/slots/src/lib.rs b/client/consensus/slots/src/lib.rs index f1d817ab7..e41473407 100644 --- a/client/consensus/slots/src/lib.rs +++ b/client/consensus/slots/src/lib.rs @@ -150,7 +150,7 @@ pub trait SimpleSlotWorker { body: Vec, storage_changes: StorageChanges<>::Transaction, B>, public: Self::Claim, - epoch: Self::AuxData, + aux_data: Self::AuxData, ) -> Result< sc_consensus::BlockImportParams>::Transaction>, sp_consensus::Error, diff --git a/client/keystore/src/local.rs b/client/keystore/src/local.rs index a4af78117..abdf97bb2 100644 --- a/client/keystore/src/local.rs +++ b/client/keystore/src/local.rs @@ -18,13 +18,10 @@ //! Local keystore implementation use parking_lot::RwLock; -use sp_application_crypto::{ecdsa, ed25519, sr25519, AppKey, AppPair, IsWrappedBy}; +use sp_application_crypto::{AppCrypto, AppPair, IsWrappedBy}; use sp_core::{ - crypto::{ - ByteArray, CryptoTypePublicPair, ExposeSecret, KeyTypeId, Pair as PairT, SecretString, - }, - sr25519::{Pair as Sr25519Pair, Public as Sr25519Public}, - Encode, + crypto::{ByteArray, ExposeSecret, KeyTypeId, Pair as CorePair, SecretString}, + ecdsa, ed25519, sr25519, }; use sp_keystore::{ vrf::{make_transcript, VRFSignature, VRFTranscriptData}, @@ -62,67 +59,13 @@ impl LocalKeystore { /// `Err(_)` when something failed. pub fn key_pair( &self, - public: &::Public, + public: &::Public, ) -> Result> { self.0.read().key_pair::(public) } } impl Keystore for LocalKeystore { - fn keys(&self, id: KeyTypeId) -> std::result::Result, TraitError> { - let raw_keys = self.0.read().raw_public_keys(id)?; - Ok(raw_keys.into_iter().fold(Vec::new(), |mut v, k| { - v.push(CryptoTypePublicPair(sr25519::CRYPTO_ID, k.clone())); - v.push(CryptoTypePublicPair(ed25519::CRYPTO_ID, k.clone())); - v.push(CryptoTypePublicPair(ecdsa::CRYPTO_ID, k)); - v - })) - } - - fn sign_with( - &self, - id: KeyTypeId, - key: &CryptoTypePublicPair, - msg: &[u8], - ) -> std::result::Result>, TraitError> { - match key.0 { - ed25519::CRYPTO_ID => { - let pub_key = ed25519::Public::from_slice(key.1.as_slice()).map_err(|()| { - TraitError::Other("Corrupted public key - Invalid size".into()) - })?; - let key_pair = self - .0 - .read() - .key_pair_by_type::(&pub_key, id) - .map_err(TraitError::from)?; - key_pair.map(|k| k.sign(msg).encode()).map(Ok).transpose() - }, - sr25519::CRYPTO_ID => { - let pub_key = sr25519::Public::from_slice(key.1.as_slice()).map_err(|()| { - TraitError::Other("Corrupted public key - Invalid size".into()) - })?; - let key_pair = self - .0 - .read() - .key_pair_by_type::(&pub_key, id) - .map_err(TraitError::from)?; - key_pair.map(|k| k.sign(msg).encode()).map(Ok).transpose() - }, - ecdsa::CRYPTO_ID => { - let pub_key = ecdsa::Public::from_slice(key.1.as_slice()).map_err(|()| { - TraitError::Other("Corrupted public key - Invalid size".into()) - })?; - let key_pair = self - .0 - .read() - .key_pair_by_type::(&pub_key, id) - .map_err(TraitError::from)?; - key_pair.map(|k| k.sign(msg).encode()).map(Ok).transpose() - }, - _ => Err(TraitError::KeyNotSupported(id)), - } - } - fn sr25519_public_keys(&self, key_type: KeyTypeId) -> Vec { self.0 .read() @@ -140,19 +83,49 @@ impl Keystore for LocalKeystore { /// If the `[seed]` is `Some` then the key will be ephemeral and stored in memory. fn sr25519_generate_new( &self, - id: KeyTypeId, + key_type: KeyTypeId, seed: Option<&str>, ) -> std::result::Result { let pair = match seed { - Some(seed) => - self.0.write().insert_ephemeral_from_seed_by_type::(seed, id), - None => self.0.write().generate_by_type::(id), + Some(seed) => self + .0 + .write() + .insert_ephemeral_from_seed_by_type::(seed, key_type), + None => self.0.write().generate_by_type::(key_type), } .map_err(|e| -> TraitError { e.into() })?; Ok(pair.public()) } + fn sr25519_sign( + &self, + key_type: KeyTypeId, + public: &sr25519::Public, + msg: &[u8], + ) -> std::result::Result, TraitError> { + let res = self + .0 + .read() + .key_pair_by_type::(public, key_type)? + .map(|pair| pair.sign(msg)); + Ok(res) + } + + fn sr25519_vrf_sign( + &self, + key_type: KeyTypeId, + public: &sr25519::Public, + transcript_data: VRFTranscriptData, + ) -> std::result::Result, TraitError> { + let res = self.0.read().key_pair_by_type::(public, key_type)?.map(|pair| { + let transcript = make_transcript(transcript_data); + let (inout, proof, _) = pair.as_ref().vrf_sign(transcript); + VRFSignature { output: inout.to_output(), proof } + }); + Ok(res) + } + fn ed25519_public_keys(&self, key_type: KeyTypeId) -> Vec { self.0 .read() @@ -170,19 +143,35 @@ impl Keystore for LocalKeystore { /// If the `[seed]` is `Some` then the key will be ephemeral and stored in memory. fn ed25519_generate_new( &self, - id: KeyTypeId, + key_type: KeyTypeId, seed: Option<&str>, ) -> std::result::Result { let pair = match seed { - Some(seed) => - self.0.write().insert_ephemeral_from_seed_by_type::(seed, id), - None => self.0.write().generate_by_type::(id), + Some(seed) => self + .0 + .write() + .insert_ephemeral_from_seed_by_type::(seed, key_type), + None => self.0.write().generate_by_type::(key_type), } .map_err(|e| -> TraitError { e.into() })?; Ok(pair.public()) } + fn ed25519_sign( + &self, + key_type: KeyTypeId, + public: &ed25519::Public, + msg: &[u8], + ) -> std::result::Result, TraitError> { + let res = self + .0 + .read() + .key_pair_by_type::(public, key_type)? + .map(|pair| pair.sign(msg)); + Ok(res) + } + fn ecdsa_public_keys(&self, key_type: KeyTypeId) -> Vec { self.0 .read() @@ -200,19 +189,47 @@ impl Keystore for LocalKeystore { /// If the `[seed]` is `Some` then the key will be ephemeral and stored in memory. fn ecdsa_generate_new( &self, - id: KeyTypeId, + key_type: KeyTypeId, seed: Option<&str>, ) -> std::result::Result { let pair = match seed { Some(seed) => - self.0.write().insert_ephemeral_from_seed_by_type::(seed, id), - None => self.0.write().generate_by_type::(id), + self.0.write().insert_ephemeral_from_seed_by_type::(seed, key_type), + None => self.0.write().generate_by_type::(key_type), } .map_err(|e| -> TraitError { e.into() })?; Ok(pair.public()) } + fn ecdsa_sign( + &self, + key_type: KeyTypeId, + public: &ecdsa::Public, + msg: &[u8], + ) -> std::result::Result, TraitError> { + let res = self + .0 + .read() + .key_pair_by_type::(public, key_type)? + .map(|pair| pair.sign(msg)); + Ok(res) + } + + fn ecdsa_sign_prehashed( + &self, + key_type: KeyTypeId, + public: &ecdsa::Public, + msg: &[u8; 32], + ) -> std::result::Result, TraitError> { + let res = self + .0 + .read() + .key_pair_by_type::(public, key_type)? + .map(|pair| pair.sign_prehashed(msg)); + Ok(res) + } + fn insert( &self, key_type: KeyTypeId, @@ -222,39 +239,15 @@ impl Keystore for LocalKeystore { self.0.write().insert(key_type, suri, public).map_err(|_| ()) } + fn keys(&self, key_type: KeyTypeId) -> std::result::Result>, TraitError> { + self.0.read().raw_public_keys(key_type).map_err(|e| e.into()) + } + fn has_keys(&self, public_keys: &[(Vec, KeyTypeId)]) -> bool { public_keys .iter() .all(|(p, t)| self.0.read().key_phrase_by_type(p, *t).ok().flatten().is_some()) } - - fn sr25519_vrf_sign( - &self, - key_type: KeyTypeId, - public: &Sr25519Public, - transcript_data: VRFTranscriptData, - ) -> std::result::Result, TraitError> { - let transcript = make_transcript(transcript_data); - let pair = self.0.read().key_pair_by_type::(public, key_type)?; - - if let Some(pair) = pair { - let (inout, proof, _) = pair.as_ref().vrf_sign(transcript); - Ok(Some(VRFSignature { output: inout.to_output(), proof })) - } else { - Ok(None) - } - } - - fn ecdsa_sign_prehashed( - &self, - id: KeyTypeId, - public: &ecdsa::Public, - msg: &[u8; 32], - ) -> std::result::Result, TraitError> { - let pair = self.0.read().key_pair_by_type::(public, id)?; - - pair.map(|k| k.sign_prehashed(msg)).map(Ok).transpose() - } } impl Into for LocalKeystore { @@ -305,7 +298,12 @@ impl KeystoreInner { /// Insert the given public/private key pair with the given key type. /// /// Does not place it into the file system store. - fn insert_ephemeral_pair(&mut self, pair: &Pair, seed: &str, key_type: KeyTypeId) { + fn insert_ephemeral_pair( + &mut self, + pair: &Pair, + seed: &str, + key_type: KeyTypeId, + ) { let key = (key_type, pair.public().to_raw_vec()); self.additional.insert(key, seed.into()); } @@ -325,7 +323,7 @@ impl KeystoreInner { /// /// Places it into the file system store, if a path is configured. Otherwise insert /// it into the memory cache only. - fn generate_by_type(&mut self, key_type: KeyTypeId) -> Result { + fn generate_by_type(&mut self, key_type: KeyTypeId) -> Result { let (pair, phrase, _) = Pair::generate_with_phrase(self.password()); if let Some(path) = self.key_file_path(pair.public().as_slice(), key_type) { Self::write_to_file(path, &phrase)?; @@ -354,7 +352,7 @@ impl KeystoreInner { /// Create a new key from seed. /// /// Does not place it into the file system store. - fn insert_ephemeral_from_seed_by_type( + fn insert_ephemeral_from_seed_by_type( &mut self, seed: &str, key_type: KeyTypeId, @@ -386,7 +384,7 @@ impl KeystoreInner { } /// Get a key pair for the given public key and key type. - fn key_pair_by_type( + fn key_pair_by_type( &self, public: &Pair::Public, key_type: KeyTypeId, @@ -418,12 +416,12 @@ impl KeystoreInner { } /// Returns a list of raw public keys filtered by `KeyTypeId` - fn raw_public_keys(&self, id: KeyTypeId) -> Result>> { + fn raw_public_keys(&self, key_type: KeyTypeId) -> Result>> { let mut public_keys: Vec> = self .additional .keys() .into_iter() - .filter_map(|k| if k.0 == id { Some(k.1.clone()) } else { None }) + .filter_map(|k| if k.0 == key_type { Some(k.1.clone()) } else { None }) .collect(); if let Some(path) = &self.path { @@ -435,7 +433,7 @@ impl KeystoreInner { if let Some(name) = path.file_name().and_then(|n| n.to_str()) { match array_bytes::hex2bytes(name) { Ok(ref hex) if hex.len() > 4 => { - if hex[0..4] != id.0 { + if hex[0..4] != key_type.0 { continue } let public = hex[4..].to_vec(); @@ -456,7 +454,7 @@ impl KeystoreInner { /// when something failed. pub fn key_pair( &self, - public: &::Public, + public: &::Public, ) -> Result> { self.key_pair_by_type::(IsWrappedBy::from_ref(public), Pair::ID) .map(|v| v.map(Into::into)) diff --git a/client/rpc/src/author/tests.rs b/client/rpc/src/author/tests.rs index 75b6390c8..fbbd1a92f 100644 --- a/client/rpc/src/author/tests.rs +++ b/client/rpc/src/author/tests.rs @@ -31,8 +31,8 @@ use sc_transaction_pool_api::TransactionStatus; use sp_core::{ blake2_256, bytes::to_hex, - crypto::{ByteArray, CryptoTypePublicPair, Pair}, - ed25519, sr25519, + crypto::{ByteArray, Pair}, + ed25519, testing::{ED25519, SR25519}, H256, }; @@ -227,9 +227,7 @@ async fn author_should_insert_key() { api.call::<_, ()>("author_insertKey", params).await.unwrap(); let pubkeys = setup.keystore.keys(ED25519).unwrap(); - assert!( - pubkeys.contains(&CryptoTypePublicPair(ed25519::CRYPTO_ID, keypair.public().to_raw_vec())) - ); + assert!(pubkeys.contains(&keypair.public().to_raw_vec())); } #[tokio::test] @@ -242,10 +240,8 @@ async fn author_should_rotate_keys() { SessionKeys::decode(&mut &new_pubkeys[..]).expect("SessionKeys decode successfully"); let ed25519_pubkeys = setup.keystore.keys(ED25519).unwrap(); let sr25519_pubkeys = setup.keystore.keys(SR25519).unwrap(); - assert!(ed25519_pubkeys - .contains(&CryptoTypePublicPair(ed25519::CRYPTO_ID, session_keys.ed25519.to_raw_vec()))); - assert!(sr25519_pubkeys - .contains(&CryptoTypePublicPair(sr25519::CRYPTO_ID, session_keys.sr25519.to_raw_vec()))); + assert!(ed25519_pubkeys.contains(&session_keys.ed25519.to_raw_vec())); + assert!(sr25519_pubkeys.contains(&session_keys.sr25519.to_raw_vec())); } #[tokio::test] diff --git a/primitives/application-crypto/src/lib.rs b/primitives/application-crypto/src/lib.rs index 0685477dd..bf7388133 100644 --- a/primitives/application-crypto/src/lib.rs +++ b/primitives/application-crypto/src/lib.rs @@ -27,10 +27,7 @@ pub use sp_core::crypto::{DeriveError, DeriveJunction, Pair, SecretStringError, #[doc(hidden)] pub use sp_core::{ self, - crypto::{ - ByteArray, CryptoType, CryptoTypePublicPair, Derive, IsWrappedBy, Public, UncheckedFrom, - Wraps, - }, + crypto::{ByteArray, CryptoType, Derive, IsWrappedBy, Public, UncheckedFrom, Wraps}, RuntimeDebug, }; @@ -170,8 +167,7 @@ macro_rules! app_crypto_pair { } } - impl $crate::AppKey for Pair { - type UntypedGeneric = $pair; + impl $crate::AppCrypto for Pair { type Public = Public; type Pair = Pair; type Signature = Signature; @@ -238,8 +234,7 @@ macro_rules! app_crypto_public_full_crypto { type Pair = Pair; } - impl $crate::AppKey for Public { - type UntypedGeneric = $public; + impl $crate::AppCrypto for Public { type Public = Public; type Pair = Pair; type Signature = Signature; @@ -272,8 +267,7 @@ macro_rules! app_crypto_public_not_full_crypto { impl $crate::CryptoType for Public {} - impl $crate::AppKey for Public { - type UntypedGeneric = $public; + impl $crate::AppCrypto for Public { type Public = Public; type Signature = Signature; const ID: $crate::KeyTypeId = $key_type; @@ -306,11 +300,8 @@ macro_rules! app_crypto_public_common { impl $crate::ByteArray for Public { const LEN: usize = <$public>::LEN; } - impl $crate::Public for Public { - fn to_public_crypto_pair(&self) -> $crate::CryptoTypePublicPair { - $crate::CryptoTypePublicPair($crypto_type, $crate::ByteArray::to_raw_vec(self)) - } - } + + impl $crate::Public for Public {} impl $crate::AppPublic for Public { type Generic = $public; @@ -350,18 +341,6 @@ macro_rules! app_crypto_public_common { } } - impl From for $crate::CryptoTypePublicPair { - fn from(key: Public) -> Self { - (&key).into() - } - } - - impl From<&Public> for $crate::CryptoTypePublicPair { - fn from(key: &Public) -> Self { - $crate::CryptoTypePublicPair($crypto_type, $crate::ByteArray::to_raw_vec(key)) - } - } - impl<'a> TryFrom<&'a [u8]> for Public { type Error = (); @@ -450,8 +429,7 @@ macro_rules! app_crypto_signature_full_crypto { type Pair = Pair; } - impl $crate::AppKey for Signature { - type UntypedGeneric = $sig; + impl $crate::AppCrypto for Signature { type Public = Public; type Pair = Pair; type Signature = Signature; @@ -482,8 +460,7 @@ macro_rules! app_crypto_signature_not_full_crypto { impl $crate::CryptoType for Signature {} - impl $crate::AppKey for Signature { - type UntypedGeneric = $sig; + impl $crate::AppCrypto for Signature { type Public = Public; type Signature = Signature; const ID: $crate::KeyTypeId = $key_type; diff --git a/primitives/application-crypto/src/traits.rs b/primitives/application-crypto/src/traits.rs index e3586ccec..f672efb8a 100644 --- a/primitives/application-crypto/src/traits.rs +++ b/primitives/application-crypto/src/traits.rs @@ -23,24 +23,22 @@ use sp_core::crypto::{CryptoType, CryptoTypeId, IsWrappedBy, KeyTypeId, Public}; use sp_std::{fmt::Debug, vec::Vec}; /// An application-specific key. -pub trait AppKey: 'static + Send + Sync + Sized + CryptoType + Clone { - /// The corresponding type as a generic crypto type. - type UntypedGeneric: IsWrappedBy; +pub trait AppCrypto: 'static + Send + Sync + Sized + CryptoType + Clone { + /// Identifier for application-specific key type. + const ID: KeyTypeId; + + /// Identifier of the crypto type of this application-specific key type. + const CRYPTO_ID: CryptoTypeId; /// The corresponding public key type in this application scheme. type Public: AppPublic; - /// The corresponding key pair type in this application scheme. - #[cfg(feature = "full_crypto")] - type Pair: AppPair; - /// The corresponding signature type in this application scheme. type Signature: AppSignature; - /// An identifier for this application-specific key type. - const ID: KeyTypeId; - /// The identifier of the crypto type of this application-specific key type. - const CRYPTO_ID: CryptoTypeId; + /// The corresponding key pair type in this application scheme. + #[cfg(feature = "full_crypto")] + type Pair: AppPair; } /// Type which implements Hash in std, not when no-std (std variant). @@ -63,7 +61,7 @@ impl MaybeDebugHash for T {} /// A application's public key. pub trait AppPublic: - AppKey + Public + Ord + PartialOrd + Eq + PartialEq + Debug + MaybeHash + codec::Codec + AppCrypto + Public + Ord + PartialOrd + Eq + PartialEq + Debug + MaybeHash + codec::Codec { /// The wrapped type which is just a plain instance of `Public`. type Generic: IsWrappedBy @@ -79,14 +77,15 @@ pub trait AppPublic: /// A application's key pair. #[cfg(feature = "full_crypto")] -pub trait AppPair: AppKey + Pair::Public> { +pub trait AppPair: AppCrypto + Pair::Public> { /// The wrapped type which is just a plain instance of `Pair`. type Generic: IsWrappedBy - + Pair::Public as AppPublic>::Generic>; + + Pair::Public as AppPublic>::Generic> + + Pair::Signature as AppSignature>::Generic>; } /// A application's signature. -pub trait AppSignature: AppKey + Eq + PartialEq + Debug + MaybeHash { +pub trait AppSignature: AppCrypto + Eq + PartialEq + Debug + MaybeHash { /// The wrapped type which is just a plain instance of `Signature`. type Generic: IsWrappedBy + Eq + PartialEq + Debug + MaybeHash; } diff --git a/primitives/application-crypto/test/src/ecdsa.rs b/primitives/application-crypto/test/src/ecdsa.rs index fe9e17a65..99ca6f4c4 100644 --- a/primitives/application-crypto/test/src/ecdsa.rs +++ b/primitives/application-crypto/test/src/ecdsa.rs @@ -17,8 +17,11 @@ //! Integration tests for ecdsa use sp_api::ProvideRuntimeApi; -use sp_application_crypto::ecdsa::{AppPair, AppPublic}; -use sp_core::{crypto::Pair, testing::ECDSA}; +use sp_application_crypto::ecdsa::AppPair; +use sp_core::{ + crypto::{ByteArray, Pair}, + testing::ECDSA, +}; use sp_keystore::{testing::MemoryKeystore, Keystore}; use std::sync::Arc; use substrate_test_runtime_client::{ @@ -35,6 +38,6 @@ fn ecdsa_works_in_runtime() { .expect("Tests `ecdsa` crypto."); let supported_keys = keystore.keys(ECDSA).unwrap(); - assert!(supported_keys.contains(&public.clone().into())); - assert!(AppPair::verify(&signature, "ecdsa", &AppPublic::from(public))); + assert!(supported_keys.contains(&public.to_raw_vec())); + assert!(AppPair::verify(&signature, "ecdsa", &public)); } diff --git a/primitives/application-crypto/test/src/ed25519.rs b/primitives/application-crypto/test/src/ed25519.rs index 5687bd013..f4553f95b 100644 --- a/primitives/application-crypto/test/src/ed25519.rs +++ b/primitives/application-crypto/test/src/ed25519.rs @@ -18,8 +18,11 @@ //! Integration tests for ed25519 use sp_api::ProvideRuntimeApi; -use sp_application_crypto::ed25519::{AppPair, AppPublic}; -use sp_core::{crypto::Pair, testing::ED25519}; +use sp_application_crypto::ed25519::AppPair; +use sp_core::{ + crypto::{ByteArray, Pair}, + testing::ED25519, +}; use sp_keystore::{testing::MemoryKeystore, Keystore}; use std::sync::Arc; use substrate_test_runtime_client::{ @@ -36,6 +39,6 @@ fn ed25519_works_in_runtime() { .expect("Tests `ed25519` crypto."); let supported_keys = keystore.keys(ED25519).unwrap(); - assert!(supported_keys.contains(&public.clone().into())); - assert!(AppPair::verify(&signature, "ed25519", &AppPublic::from(public))); + assert!(supported_keys.contains(&public.to_raw_vec())); + assert!(AppPair::verify(&signature, "ed25519", &public)); } diff --git a/primitives/application-crypto/test/src/sr25519.rs b/primitives/application-crypto/test/src/sr25519.rs index 92886b80c..736521d7d 100644 --- a/primitives/application-crypto/test/src/sr25519.rs +++ b/primitives/application-crypto/test/src/sr25519.rs @@ -18,8 +18,11 @@ //! Integration tests for sr25519 use sp_api::ProvideRuntimeApi; -use sp_application_crypto::sr25519::{AppPair, AppPublic}; -use sp_core::{crypto::Pair, testing::SR25519}; +use sp_application_crypto::sr25519::AppPair; +use sp_core::{ + crypto::{ByteArray, Pair}, + testing::SR25519, +}; use sp_keystore::{testing::MemoryKeystore, Keystore}; use std::sync::Arc; use substrate_test_runtime_client::{ @@ -36,6 +39,6 @@ fn sr25519_works_in_runtime() { .expect("Tests `sr25519` crypto."); let supported_keys = keystore.keys(SR25519).unwrap(); - assert!(supported_keys.contains(&public.clone().into())); - assert!(AppPair::verify(&signature, "sr25519", &AppPublic::from(public))); + assert!(supported_keys.contains(&public.to_raw_vec())); + assert!(AppPair::verify(&signature, "sr25519", &public)); } diff --git a/primitives/consensus/common/src/error.rs b/primitives/consensus/common/src/error.rs index e881259da..fb8d0447f 100644 --- a/primitives/consensus/common/src/error.rs +++ b/primitives/consensus/common/src/error.rs @@ -48,8 +48,8 @@ pub enum Error { #[error("Chain lookup failed: {0}")] ChainLookup(String), /// Signing failed. - #[error("Failed to sign using key: {0:?}. Reason: {1}")] - CannotSign(Vec, String), + #[error("Failed to sign: {0}")] + CannotSign(String), /// Some other error. #[error(transparent)] Other(#[from] Box), diff --git a/primitives/consensus/grandpa/src/lib.rs b/primitives/consensus/grandpa/src/lib.rs index 7f5857e4b..728ce3092 100644 --- a/primitives/consensus/grandpa/src/lib.rs +++ b/primitives/consensus/grandpa/src/lib.rs @@ -451,12 +451,11 @@ where H: Encode, N: Encode, { - use sp_application_crypto::AppKey; - use sp_core::crypto::Public; + use sp_application_crypto::AppCrypto; let encoded = localized_payload(round, set_id, &message); let signature = keystore - .sign_with(AuthorityId::ID, &public.to_public_crypto_pair(), &encoded[..]) + .ed25519_sign(AuthorityId::ID, public.as_ref(), &encoded[..]) .ok() .flatten()? .try_into() diff --git a/primitives/core/src/crypto.rs b/primitives/core/src/crypto.rs index 80e65b342..2e60ac237 100644 --- a/primitives/core/src/crypto.rs +++ b/primitives/core/src/crypto.rs @@ -19,8 +19,6 @@ //! Cryptographic utilities. // end::description[] -#[cfg(feature = "std")] -use crate::hexdisplay::HexDisplay; use crate::{ed25519, sr25519}; #[cfg(feature = "std")] use base58::{FromBase58, ToBase58}; @@ -487,10 +485,7 @@ pub trait ByteArray: AsRef<[u8]> + AsMut<[u8]> + for<'a> TryFrom<&'a [u8], Error } /// Trait suitable for typical cryptographic PKI key public type. -pub trait Public: ByteArray + Derive + CryptoType + PartialEq + Eq + Clone + Send + Sync { - /// Return `CryptoTypePublicPair` from public key. - fn to_public_crypto_pair(&self) -> CryptoTypePublicPair; -} +pub trait Public: ByteArray + Derive + CryptoType + PartialEq + Eq + Clone + Send + Sync {} /// An opaque 32-byte cryptographic identifier. #[derive(Clone, Eq, PartialEq, Ord, PartialOrd, Encode, Decode, MaxEncodedLen, TypeInfo)] @@ -689,11 +684,7 @@ mod dummy { b"" } } - impl Public for Dummy { - fn to_public_crypto_pair(&self) -> CryptoTypePublicPair { - CryptoTypePublicPair(CryptoTypeId(*b"dumm"), ::to_raw_vec(self)) - } - } + impl Public for Dummy {} impl Pair for Dummy { type Public = Dummy; @@ -1118,24 +1109,6 @@ impl<'a> TryFrom<&'a str> for KeyTypeId { #[cfg_attr(feature = "std", derive(serde::Serialize, serde::Deserialize))] pub struct CryptoTypeId(pub [u8; 4]); -/// A type alias of CryptoTypeId & a public key -#[derive(Debug, Clone, Default, PartialEq, Eq, PartialOrd, Ord, Hash, Encode, Decode)] -#[cfg_attr(feature = "std", derive(serde::Serialize, serde::Deserialize))] -pub struct CryptoTypePublicPair(pub CryptoTypeId, pub Vec); - -#[cfg(feature = "std")] -impl sp_std::fmt::Display for CryptoTypePublicPair { - fn fmt(&self, f: &mut sp_std::fmt::Formatter) -> sp_std::fmt::Result { - let id = match str::from_utf8(&(self.0).0[..]) { - Ok(id) => id.to_string(), - Err(_) => { - format!("{:#?}", self.0) - }, - }; - write!(f, "{}-{}", id, HexDisplay::from(&self.1)) - } -} - /// Known key types; this also functions as a global registry of key types for projects wishing to /// avoid collisions with each other. /// @@ -1223,11 +1196,7 @@ mod tests { vec![] } } - impl Public for TestPublic { - fn to_public_crypto_pair(&self) -> CryptoTypePublicPair { - CryptoTypePublicPair(CryptoTypeId(*b"dumm"), self.to_raw_vec()) - } - } + impl Public for TestPublic {} impl Pair for TestPair { type Public = TestPublic; type Seed = [u8; 8]; diff --git a/primitives/core/src/ecdsa.rs b/primitives/core/src/ecdsa.rs index e63b283d2..8dc4a5513 100644 --- a/primitives/core/src/ecdsa.rs +++ b/primitives/core/src/ecdsa.rs @@ -24,8 +24,7 @@ use sp_runtime_interface::pass_by::PassByInner; #[cfg(feature = "std")] use crate::crypto::Ss58Codec; use crate::crypto::{ - ByteArray, CryptoType, CryptoTypeId, CryptoTypePublicPair, Derive, Public as TraitPublic, - UncheckedFrom, + ByteArray, CryptoType, CryptoTypeId, Derive, Public as TraitPublic, UncheckedFrom, }; #[cfg(feature = "full_crypto")] use crate::{ @@ -103,23 +102,7 @@ impl ByteArray for Public { const LEN: usize = 33; } -impl TraitPublic for Public { - fn to_public_crypto_pair(&self) -> CryptoTypePublicPair { - CryptoTypePublicPair(CRYPTO_ID, self.to_raw_vec()) - } -} - -impl From for CryptoTypePublicPair { - fn from(key: Public) -> Self { - (&key).into() - } -} - -impl From<&Public> for CryptoTypePublicPair { - fn from(key: &Public) -> Self { - CryptoTypePublicPair(CRYPTO_ID, key.to_raw_vec()) - } -} +impl TraitPublic for Public {} impl Derive for Public {} diff --git a/primitives/core/src/ed25519.rs b/primitives/core/src/ed25519.rs index 503c127a9..884d29dc1 100644 --- a/primitives/core/src/ed25519.rs +++ b/primitives/core/src/ed25519.rs @@ -31,9 +31,7 @@ use scale_info::TypeInfo; #[cfg(feature = "std")] use crate::crypto::Ss58Codec; -use crate::crypto::{ - CryptoType, CryptoTypeId, CryptoTypePublicPair, Derive, Public as TraitPublic, UncheckedFrom, -}; +use crate::crypto::{CryptoType, CryptoTypeId, Derive, Public as TraitPublic, UncheckedFrom}; #[cfg(feature = "full_crypto")] use crate::crypto::{DeriveError, DeriveJunction, Pair as TraitPair, SecretStringError}; #[cfg(feature = "full_crypto")] @@ -355,26 +353,10 @@ impl ByteArray for Public { const LEN: usize = 32; } -impl TraitPublic for Public { - fn to_public_crypto_pair(&self) -> CryptoTypePublicPair { - CryptoTypePublicPair(CRYPTO_ID, self.to_raw_vec()) - } -} +impl TraitPublic for Public {} impl Derive for Public {} -impl From for CryptoTypePublicPair { - fn from(key: Public) -> Self { - (&key).into() - } -} - -impl From<&Public> for CryptoTypePublicPair { - fn from(key: &Public) -> Self { - CryptoTypePublicPair(CRYPTO_ID, key.to_raw_vec()) - } -} - /// Derive a single hard junction. #[cfg(feature = "full_crypto")] fn derive_hard_junction(secret_seed: &Seed, cc: &[u8; 32]) -> Seed { diff --git a/primitives/core/src/sr25519.rs b/primitives/core/src/sr25519.rs index 8dbcca9e2..d7c1c79c3 100644 --- a/primitives/core/src/sr25519.rs +++ b/primitives/core/src/sr25519.rs @@ -34,10 +34,7 @@ use schnorrkel::{ use sp_std::vec::Vec; use crate::{ - crypto::{ - ByteArray, CryptoType, CryptoTypeId, CryptoTypePublicPair, Derive, Public as TraitPublic, - UncheckedFrom, - }, + crypto::{ByteArray, CryptoType, CryptoTypeId, Derive, Public as TraitPublic, UncheckedFrom}, hash::{H256, H512}, }; use codec::{Decode, Encode, MaxEncodedLen}; @@ -386,23 +383,7 @@ impl ByteArray for Public { const LEN: usize = 32; } -impl TraitPublic for Public { - fn to_public_crypto_pair(&self) -> CryptoTypePublicPair { - CryptoTypePublicPair(CRYPTO_ID, self.to_raw_vec()) - } -} - -impl From for CryptoTypePublicPair { - fn from(key: Public) -> Self { - (&key).into() - } -} - -impl From<&Public> for CryptoTypePublicPair { - fn from(key: &Public) -> Self { - CryptoTypePublicPair(CRYPTO_ID, key.to_raw_vec()) - } -} +impl TraitPublic for Public {} #[cfg(feature = "std")] impl From for Pair { diff --git a/primitives/io/src/lib.rs b/primitives/io/src/lib.rs index 709e1fa64..306ac8e60 100644 --- a/primitives/io/src/lib.rs +++ b/primitives/io/src/lib.rs @@ -762,10 +762,9 @@ pub trait Crypto { ) -> Option { self.extension::() .expect("No `keystore` associated for the current context!") - .sign_with(id, &pub_key.into(), msg) + .ed25519_sign(id, pub_key, msg) .ok() .flatten() - .and_then(|sig| ed25519::Signature::from_slice(&sig)) } /// Verify `ed25519` signature. @@ -902,10 +901,9 @@ pub trait Crypto { ) -> Option { self.extension::() .expect("No `keystore` associated for the current context!") - .sign_with(id, &pub_key.into(), msg) + .sr25519_sign(id, pub_key, msg) .ok() .flatten() - .and_then(|sig| sr25519::Signature::from_slice(&sig)) } /// Verify an `sr25519` signature. @@ -949,10 +947,9 @@ pub trait Crypto { ) -> Option { self.extension::() .expect("No `keystore` associated for the current context!") - .sign_with(id, &pub_key.into(), msg) + .ecdsa_sign(id, pub_key, msg) .ok() .flatten() - .and_then(|sig| ecdsa::Signature::from_slice(&sig)) } /// Sign the given a pre-hashed `msg` with the `ecdsa` key that corresponds to the given public diff --git a/primitives/keystore/src/lib.rs b/primitives/keystore/src/lib.rs index d1cbe227e..577969cdf 100644 --- a/primitives/keystore/src/lib.rs +++ b/primitives/keystore/src/lib.rs @@ -21,7 +21,7 @@ pub mod vrf; use crate::vrf::{VRFSignature, VRFTranscriptData}; use sp_core::{ - crypto::{CryptoTypePublicPair, KeyTypeId}, + crypto::{ByteArray, CryptoTypeId, KeyTypeId}, ecdsa, ed25519, sr25519, }; use std::sync::Arc; @@ -45,109 +45,174 @@ pub enum Error { /// Something that generates, stores and provides access to secret keys. pub trait Keystore: Send + Sync { - /// Returns all sr25519 public keys for the given key type. - fn sr25519_public_keys(&self, id: KeyTypeId) -> Vec; + /// Returns all the sr25519 public keys for the given key type. + fn sr25519_public_keys(&self, key_type: KeyTypeId) -> Vec; /// Generate a new sr25519 key pair for the given key type and an optional seed. /// - /// If the given seed is `Some(_)`, the key pair will only be stored in memory. - /// - /// Returns the public key of the generated key pair. + /// Returns an `sr25519::Public` key of the generated key pair or an `Err` if + /// something failed during key generation. fn sr25519_generate_new( &self, - id: KeyTypeId, + key_type: KeyTypeId, seed: Option<&str>, ) -> Result; + /// Generate an sr25519 signature for a given message. + /// + /// Receives [`KeyTypeId`] and an [`sr25519::Public`] key to be able to map + /// them to a private key that exists in the keystore. + /// + /// Returns an [`sr25519::Signature`] or `None` in case the given `key_type` + /// and `public` combination doesn't exist in the keystore. + /// An `Err` will be returned if generating the signature itself failed. + fn sr25519_sign( + &self, + key_type: KeyTypeId, + public: &sr25519::Public, + msg: &[u8], + ) -> Result, Error>; + + /// Generate an sr25519 VRF signature for a given transcript data. + /// + /// Receives [`KeyTypeId`] and an [`sr25519::Public`] key to be able to map + /// them to a private key that exists in the keystore. + /// + /// Returns a result containing the signature data. + /// Namely, VRFOutput and VRFProof which are returned inside the `VRFSignature` + /// container struct. + /// Returns `None` if the given `key_type` and `public` combination doesn't + /// exist in the keystore or an `Err` when something failed. + fn sr25519_vrf_sign( + &self, + key_type: KeyTypeId, + public: &sr25519::Public, + transcript_data: VRFTranscriptData, + ) -> Result, Error>; + /// Returns all ed25519 public keys for the given key type. - fn ed25519_public_keys(&self, id: KeyTypeId) -> Vec; + fn ed25519_public_keys(&self, key_type: KeyTypeId) -> Vec; /// Generate a new ed25519 key pair for the given key type and an optional seed. /// - /// If the given seed is `Some(_)`, the key pair will only be stored in memory. - /// - /// Returns the public key of the generated key pair. + /// Returns an `ed25519::Public` key of the generated key pair or an `Err` if + /// something failed during key generation. fn ed25519_generate_new( &self, - id: KeyTypeId, + key_type: KeyTypeId, seed: Option<&str>, ) -> Result; + /// Generate an ed25519 signature for a given message. + /// + /// Receives [`KeyTypeId`] and an [`ed25519::Public`] key to be able to map + /// them to a private key that exists in the keystore. + /// + /// Returns an [`ed25519::Signature`] or `None` in case the given `key_type` + /// and `public` combination doesn't exist in the keystore. + /// An `Err` will be returned if generating the signature itself failed. + fn ed25519_sign( + &self, + key_type: KeyTypeId, + public: &ed25519::Public, + msg: &[u8], + ) -> Result, Error>; + /// Returns all ecdsa public keys for the given key type. - fn ecdsa_public_keys(&self, id: KeyTypeId) -> Vec; + fn ecdsa_public_keys(&self, key_type: KeyTypeId) -> Vec; /// Generate a new ecdsa key pair for the given key type and an optional seed. /// - /// If the given seed is `Some(_)`, the key pair will only be stored in memory. + /// Returns an `ecdsa::Public` key of the generated key pair or an `Err` if + /// something failed during key generation. + fn ecdsa_generate_new( + &self, + key_type: KeyTypeId, + seed: Option<&str>, + ) -> Result; + + /// Generate an ecdsa signature for a given message. + /// + /// Receives [`KeyTypeId`] and an [`ecdsa::Public`] key to be able to map + /// them to a private key that exists in the keystore. + /// + /// Returns an [`ecdsa::Signature`] or `None` in case the given `key_type` + /// and `public` combination doesn't exist in the keystore. + /// An `Err` will be returned if generating the signature itself failed. + fn ecdsa_sign( + &self, + key_type: KeyTypeId, + public: &ecdsa::Public, + msg: &[u8], + ) -> Result, Error>; + + /// Generate an ecdsa signature for a given pre-hashed message. + /// + /// Receives [`KeyTypeId`] and an [`ecdsa::Public`] key to be able to map + /// them to a private key that exists in the keystore. /// - /// Returns the public key of the generated key pair. - fn ecdsa_generate_new(&self, id: KeyTypeId, seed: Option<&str>) - -> Result; + /// Returns an [`ecdsa::Signature`] or `None` in case the given `key_type` + /// and `public` combination doesn't exist in the keystore. + /// An `Err` will be returned if generating the signature itself failed. + fn ecdsa_sign_prehashed( + &self, + key_type: KeyTypeId, + public: &ecdsa::Public, + msg: &[u8; 32], + ) -> Result, Error>; /// Insert a new secret key. fn insert(&self, key_type: KeyTypeId, suri: &str, public: &[u8]) -> Result<(), ()>; - /// List all supported keys + /// List all supported keys of a given type. /// - /// Returns a set of public keys the signer supports. - fn keys(&self, id: KeyTypeId) -> Result, Error>; + /// Returns a set of public keys the signer supports in raw format. + fn keys(&self, key_type: KeyTypeId) -> Result>, Error>; /// Checks if the private keys for the given public key and key type combinations exist. /// /// Returns `true` iff all private keys could be found. fn has_keys(&self, public_keys: &[(Vec, KeyTypeId)]) -> bool; - /// Sign with key + /// Convenience method to sign a message using the given key type and a raw public key + /// for secret lookup. /// - /// Signs a message with the private key that matches - /// the public key passed. + /// The message is signed using the cryptographic primitive specified by `crypto_id`. + /// + /// Schemes supported by the default trait implementation: sr25519, ed25519 and ecdsa. + /// To support more schemes you can overwrite this method. /// /// Returns the SCALE encoded signature if key is found and supported, `None` if the key doesn't /// exist or an error when something failed. fn sign_with( &self, id: KeyTypeId, - key: &CryptoTypePublicPair, + crypto_id: CryptoTypeId, + public: &[u8], msg: &[u8], - ) -> Result>, Error>; - - /// Generate VRF signature for given transcript data. - /// - /// Receives KeyTypeId and Public key to be able to map - /// them to a private key that exists in the keystore which - /// is, in turn, used for signing the provided transcript. - /// - /// Returns a result containing the signature data. - /// Namely, VRFOutput and VRFProof which are returned - /// inside the `VRFSignature` container struct. - /// - /// This function will return `None` if the given `key_type` and `public` combination - /// doesn't exist in the keystore or an `Err` when something failed. - fn sr25519_vrf_sign( - &self, - key_type: KeyTypeId, - public: &sr25519::Public, - transcript_data: VRFTranscriptData, - ) -> Result, Error>; - - /// Generate an ECDSA signature for a given pre-hashed message. - /// - /// Receives [`KeyTypeId`] and an [`ecdsa::Public`] key to be able to map - /// them to a private key that exists in the keystore. This private key is, - /// in turn, used for signing the provided pre-hashed message. - /// - /// The `msg` argument provided should be a hashed message for which an - /// ECDSA signature should be generated. - /// - /// Returns an [`ecdsa::Signature`] or `None` in case the given `id` and - /// `public` combination doesn't exist in the keystore. An `Err` will be - /// returned if generating the signature itself failed. - fn ecdsa_sign_prehashed( - &self, - id: KeyTypeId, - public: &ecdsa::Public, - msg: &[u8; 32], - ) -> Result, Error>; + ) -> Result>, Error> { + use codec::Encode; + + let signature = match crypto_id { + sr25519::CRYPTO_ID => { + let public = sr25519::Public::from_slice(public) + .map_err(|_| Error::ValidationError("Invalid public key format".into()))?; + self.sr25519_sign(id, &public, msg)?.map(|s| s.encode()) + }, + ed25519::CRYPTO_ID => { + let public = ed25519::Public::from_slice(public) + .map_err(|_| Error::ValidationError("Invalid public key format".into()))?; + self.ed25519_sign(id, &public, msg)?.map(|s| s.encode()) + }, + ecdsa::CRYPTO_ID => { + let public = ecdsa::Public::from_slice(public) + .map_err(|_| Error::ValidationError("Invalid public key format".into()))?; + self.ecdsa_sign(id, &public, msg)?.map(|s| s.encode()) + }, + _ => return Err(Error::KeyNotSupported(id)), + }; + Ok(signature) + } } /// A shared pointer to a keystore implementation. diff --git a/primitives/keystore/src/testing.rs b/primitives/keystore/src/testing.rs index 73b560d56..de034c658 100644 --- a/primitives/keystore/src/testing.rs +++ b/primitives/keystore/src/testing.rs @@ -18,7 +18,7 @@ //! Types that should only be used for testing! use sp_core::{ - crypto::{ByteArray, CryptoTypePublicPair, KeyTypeId, Pair}, + crypto::{ByteArray, KeyTypeId, Pair}, ecdsa, ed25519, sr25519, }; @@ -42,51 +42,44 @@ impl MemoryKeystore { Self::default() } - fn sr25519_key_pair(&self, id: KeyTypeId, pub_key: &sr25519::Public) -> Option { - self.keys.read().get(&id).and_then(|inner| { - inner.get(pub_key.as_slice()).map(|s| { + fn sr25519_key_pair( + &self, + key_type: KeyTypeId, + public: &sr25519::Public, + ) -> Option { + self.keys.read().get(&key_type).and_then(|inner| { + inner.get(public.as_slice()).map(|s| { sr25519::Pair::from_string(s, None).expect("`sr25519` seed slice is valid") }) }) } - fn ed25519_key_pair(&self, id: KeyTypeId, pub_key: &ed25519::Public) -> Option { - self.keys.read().get(&id).and_then(|inner| { - inner.get(pub_key.as_slice()).map(|s| { + fn ed25519_key_pair( + &self, + key_type: KeyTypeId, + public: &ed25519::Public, + ) -> Option { + self.keys.read().get(&key_type).and_then(|inner| { + inner.get(public.as_slice()).map(|s| { ed25519::Pair::from_string(s, None).expect("`ed25519` seed slice is valid") }) }) } - fn ecdsa_key_pair(&self, id: KeyTypeId, pub_key: &ecdsa::Public) -> Option { - self.keys.read().get(&id).and_then(|inner| { + fn ecdsa_key_pair(&self, key_type: KeyTypeId, public: &ecdsa::Public) -> Option { + self.keys.read().get(&key_type).and_then(|inner| { inner - .get(pub_key.as_slice()) + .get(public.as_slice()) .map(|s| ecdsa::Pair::from_string(s, None).expect("`ecdsa` seed slice is valid")) }) } } impl Keystore for MemoryKeystore { - fn keys(&self, id: KeyTypeId) -> Result, Error> { - self.keys - .read() - .get(&id) - .map(|map| { - Ok(map.keys().fold(Vec::new(), |mut v, k| { - v.push(CryptoTypePublicPair(sr25519::CRYPTO_ID, k.clone())); - v.push(CryptoTypePublicPair(ed25519::CRYPTO_ID, k.clone())); - v.push(CryptoTypePublicPair(ecdsa::CRYPTO_ID, k.clone())); - v - })) - }) - .unwrap_or_else(|| Ok(vec![])) - } - - fn sr25519_public_keys(&self, id: KeyTypeId) -> Vec { + fn sr25519_public_keys(&self, key_type: KeyTypeId) -> Vec { self.keys .read() - .get(&id) + .get(&key_type) .map(|keys| { keys.values() .map(|s| { @@ -100,7 +93,7 @@ impl Keystore for MemoryKeystore { fn sr25519_generate_new( &self, - id: KeyTypeId, + key_type: KeyTypeId, seed: Option<&str>, ) -> Result { match seed { @@ -110,7 +103,7 @@ impl Keystore for MemoryKeystore { })?; self.keys .write() - .entry(id) + .entry(key_type) .or_default() .insert(pair.public().to_raw_vec(), seed.into()); Ok(pair.public()) @@ -119,7 +112,7 @@ impl Keystore for MemoryKeystore { let (pair, phrase, _) = sr25519::Pair::generate_with_phrase(None); self.keys .write() - .entry(id) + .entry(key_type) .or_default() .insert(pair.public().to_raw_vec(), phrase); Ok(pair.public()) @@ -127,10 +120,33 @@ impl Keystore for MemoryKeystore { } } - fn ed25519_public_keys(&self, id: KeyTypeId) -> Vec { + fn sr25519_sign( + &self, + key_type: KeyTypeId, + public: &sr25519::Public, + msg: &[u8], + ) -> Result, Error> { + Ok(self.sr25519_key_pair(key_type, public).map(|pair| pair.sign(msg))) + } + + fn sr25519_vrf_sign( + &self, + key_type: KeyTypeId, + public: &sr25519::Public, + transcript_data: VRFTranscriptData, + ) -> Result, Error> { + let transcript = make_transcript(transcript_data); + let pair = + if let Some(k) = self.sr25519_key_pair(key_type, public) { k } else { return Ok(None) }; + + let (inout, proof, _) = pair.as_ref().vrf_sign(transcript); + Ok(Some(VRFSignature { output: inout.to_output(), proof })) + } + + fn ed25519_public_keys(&self, key_type: KeyTypeId) -> Vec { self.keys .read() - .get(&id) + .get(&key_type) .map(|keys| { keys.values() .map(|s| { @@ -144,7 +160,7 @@ impl Keystore for MemoryKeystore { fn ed25519_generate_new( &self, - id: KeyTypeId, + key_type: KeyTypeId, seed: Option<&str>, ) -> Result { match seed { @@ -154,7 +170,7 @@ impl Keystore for MemoryKeystore { })?; self.keys .write() - .entry(id) + .entry(key_type) .or_default() .insert(pair.public().to_raw_vec(), seed.into()); Ok(pair.public()) @@ -163,7 +179,7 @@ impl Keystore for MemoryKeystore { let (pair, phrase, _) = ed25519::Pair::generate_with_phrase(None); self.keys .write() - .entry(id) + .entry(key_type) .or_default() .insert(pair.public().to_raw_vec(), phrase); Ok(pair.public()) @@ -171,10 +187,19 @@ impl Keystore for MemoryKeystore { } } - fn ecdsa_public_keys(&self, id: KeyTypeId) -> Vec { + fn ed25519_sign( + &self, + key_type: KeyTypeId, + public: &ed25519::Public, + msg: &[u8], + ) -> Result, Error> { + Ok(self.ed25519_key_pair(key_type, public).map(|pair| pair.sign(msg))) + } + + fn ecdsa_public_keys(&self, key_type: KeyTypeId) -> Vec { self.keys .read() - .get(&id) + .get(&key_type) .map(|keys| { keys.values() .map(|s| { @@ -188,7 +213,7 @@ impl Keystore for MemoryKeystore { fn ecdsa_generate_new( &self, - id: KeyTypeId, + key_type: KeyTypeId, seed: Option<&str>, ) -> Result { match seed { @@ -197,7 +222,7 @@ impl Keystore for MemoryKeystore { .map_err(|_| Error::ValidationError("Generates an `ecdsa` pair.".to_owned()))?; self.keys .write() - .entry(id) + .entry(key_type) .or_default() .insert(pair.public().to_raw_vec(), seed.into()); Ok(pair.public()) @@ -206,7 +231,7 @@ impl Keystore for MemoryKeystore { let (pair, phrase, _) = ecdsa::Pair::generate_with_phrase(None); self.keys .write() - .entry(id) + .entry(key_type) .or_default() .insert(pair.public().to_raw_vec(), phrase); Ok(pair.public()) @@ -214,75 +239,49 @@ impl Keystore for MemoryKeystore { } } - fn insert(&self, id: KeyTypeId, suri: &str, public: &[u8]) -> Result<(), ()> { + fn ecdsa_sign( + &self, + key_type: KeyTypeId, + public: &ecdsa::Public, + msg: &[u8], + ) -> Result, Error> { + Ok(self.ecdsa_key_pair(key_type, public).map(|pair| pair.sign(msg))) + } + + fn ecdsa_sign_prehashed( + &self, + key_type: KeyTypeId, + public: &ecdsa::Public, + msg: &[u8; 32], + ) -> Result, Error> { + let pair = self.ecdsa_key_pair(key_type, public); + pair.map(|pair| pair.sign_prehashed(msg)).map(Ok).transpose() + } + + fn insert(&self, key_type: KeyTypeId, suri: &str, public: &[u8]) -> Result<(), ()> { self.keys .write() - .entry(id) + .entry(key_type) .or_default() .insert(public.to_owned(), suri.to_string()); Ok(()) } + fn keys(&self, key_type: KeyTypeId) -> Result>, Error> { + let keys = self + .keys + .read() + .get(&key_type) + .map(|map| map.keys().cloned().collect()) + .unwrap_or_default(); + Ok(keys) + } + fn has_keys(&self, public_keys: &[(Vec, KeyTypeId)]) -> bool { public_keys .iter() .all(|(k, t)| self.keys.read().get(t).and_then(|s| s.get(k)).is_some()) } - - fn sign_with( - &self, - id: KeyTypeId, - key: &CryptoTypePublicPair, - msg: &[u8], - ) -> Result>, Error> { - use codec::Encode; - - match key.0 { - ed25519::CRYPTO_ID => { - let key_pair = self - .ed25519_key_pair(id, &ed25519::Public::from_slice(key.1.as_slice()).unwrap()); - - key_pair.map(|k| k.sign(msg).encode()).map(Ok).transpose() - }, - sr25519::CRYPTO_ID => { - let key_pair = self - .sr25519_key_pair(id, &sr25519::Public::from_slice(key.1.as_slice()).unwrap()); - - key_pair.map(|k| k.sign(msg).encode()).map(Ok).transpose() - }, - ecdsa::CRYPTO_ID => { - let key_pair = - self.ecdsa_key_pair(id, &ecdsa::Public::from_slice(key.1.as_slice()).unwrap()); - - key_pair.map(|k| k.sign(msg).encode()).map(Ok).transpose() - }, - _ => Err(Error::KeyNotSupported(id)), - } - } - - fn sr25519_vrf_sign( - &self, - key_type: KeyTypeId, - public: &sr25519::Public, - transcript_data: VRFTranscriptData, - ) -> Result, Error> { - let transcript = make_transcript(transcript_data); - let pair = - if let Some(k) = self.sr25519_key_pair(key_type, public) { k } else { return Ok(None) }; - - let (inout, proof, _) = pair.as_ref().vrf_sign(transcript); - Ok(Some(VRFSignature { output: inout.to_output(), proof })) - } - - fn ecdsa_sign_prehashed( - &self, - id: KeyTypeId, - public: &ecdsa::Public, - msg: &[u8; 32], - ) -> Result, Error> { - let pair = self.ecdsa_key_pair(id, public); - pair.map(|k| k.sign_prehashed(msg)).map(Ok).transpose() - } } impl Into for MemoryKeystore { @@ -306,7 +305,7 @@ mod tests { let public = store.ed25519_generate_new(ED25519, None).expect("Generates key"); - let public_keys = store.keys(ED25519).unwrap(); + let public_keys = store.ed25519_public_keys(ED25519); assert!(public_keys.contains(&public.into())); } @@ -322,7 +321,7 @@ mod tests { .insert(SR25519, secret_uri, key_pair.public().as_ref()) .expect("Inserts unknown key"); - let public_keys = store.keys(SR25519).unwrap(); + let public_keys = store.sr25519_public_keys(SR25519); assert!(public_keys.contains(&key_pair.public().into())); } diff --git a/primitives/runtime/src/traits.rs b/primitives/runtime/src/traits.rs index 4bba6f6bc..95f977077 100644 --- a/primitives/runtime/src/traits.rs +++ b/primitives/runtime/src/traits.rs @@ -30,7 +30,7 @@ use crate::{ use impl_trait_for_tuples::impl_for_tuples; #[cfg(feature = "std")] use serde::{de::DeserializeOwned, Deserialize, Serialize}; -use sp_application_crypto::AppKey; +use sp_application_crypto::AppCrypto; pub use sp_arithmetic::traits::{ checked_pow, ensure_pow, AtLeast32Bit, AtLeast32BitUnsigned, Bounded, CheckedAdd, CheckedDiv, CheckedMul, CheckedShl, CheckedShr, CheckedSub, Ensure, EnsureAdd, EnsureAddAssign, EnsureDiv, @@ -148,10 +148,10 @@ pub trait AppVerify { } impl< - S: Verify::Public as sp_application_crypto::AppPublic>::Generic> + S: Verify::Public as sp_application_crypto::AppPublic>::Generic> + From, T: sp_application_crypto::Wraps - + sp_application_crypto::AppKey + + sp_application_crypto::AppCrypto + sp_application_crypto::AppSignature + AsRef + AsMut @@ -159,16 +159,18 @@ impl< > AppVerify for T where ::Signer: IdentifyAccount::Signer>, - <::Public as sp_application_crypto::AppPublic>::Generic: IdentifyAccount< - AccountId = <::Public as sp_application_crypto::AppPublic>::Generic, + <::Public as sp_application_crypto::AppPublic>::Generic: IdentifyAccount< + AccountId = <::Public as sp_application_crypto::AppPublic>::Generic, >, { - type AccountId = ::Public; - fn verify>(&self, msg: L, signer: &::Public) -> bool { + type AccountId = ::Public; + fn verify>(&self, msg: L, signer: &::Public) -> bool { use sp_application_crypto::IsWrappedBy; let inner: &S = self.as_ref(); let inner_pubkey = - <::Public as sp_application_crypto::AppPublic>::Generic::from_ref(signer); + <::Public as sp_application_crypto::AppPublic>::Generic::from_ref( + signer, + ); Verify::verify(inner, msg, inner_pubkey) } } From 656b139c24b660c9b3edd39e2418a0dd4a075cbd Mon Sep 17 00:00:00 2001 From: Gavin Wood Date: Fri, 24 Mar 2023 14:48:37 +0000 Subject: [PATCH 285/558] Use sensible maths for `from_rational` (#13660) * Use sensible maths for from_rational * Fixes * Fixes * More fixes * Remove debugging * Add fuzzer tests Signed-off-by: Oliver Tale-Yazdi * Prevent panics Signed-off-by: Oliver Tale-Yazdi * docs Signed-off-by: Oliver Tale-Yazdi * Clean up old code Signed-off-by: Oliver Tale-Yazdi * Test all rounding modes of from_rational Signed-off-by: Oliver Tale-Yazdi * Clean up code Signed-off-by: Oliver Tale-Yazdi * Revert "Prevent panics" This reverts commit 7e88ac76138a1b590e68b68318505b69efe1e1f6. * fix imports Signed-off-by: Oliver Tale-Yazdi * cleanup Signed-off-by: Oliver Tale-Yazdi * Fuzz test multiply_rational Signed-off-by: Oliver Tale-Yazdi * Fix import Signed-off-by: Oliver Tale-Yazdi * fmt Signed-off-by: Oliver Tale-Yazdi * Return None in multiply_rational on zero div Signed-off-by: Oliver Tale-Yazdi --------- Signed-off-by: Oliver Tale-Yazdi Co-authored-by: Oliver Tale-Yazdi --- Cargo.lock | 42 ++++++- frame/staking/src/tests.rs | 8 +- primitives/arithmetic/fuzzer/Cargo.toml | 11 +- .../src/multiply_by_rational_with_rounding.rs | 99 +++++++++------ .../fuzzer/src/per_thing_from_rational.rs | 105 ++++++++++++++++ .../fuzzer/src/per_thing_mult_fraction.rs | 69 +++++++++++ .../fuzzer/src/per_thing_rational.rs | 116 ------------------ primitives/arithmetic/src/helpers_128bit.rs | 4 +- primitives/arithmetic/src/lib.rs | 2 +- primitives/arithmetic/src/per_things.rs | 89 ++++---------- primitives/arithmetic/src/rational.rs | 48 ++++++++ primitives/arithmetic/src/traits.rs | 6 +- 12 files changed, 372 insertions(+), 227 deletions(-) create mode 100644 primitives/arithmetic/fuzzer/src/per_thing_from_rational.rs create mode 100644 primitives/arithmetic/fuzzer/src/per_thing_mult_fraction.rs delete mode 100644 primitives/arithmetic/fuzzer/src/per_thing_rational.rs diff --git a/Cargo.lock b/Cargo.lock index a645ecec6..33bca4005 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -220,9 +220,9 @@ dependencies = [ [[package]] name = "arbitrary" -version = "1.2.3" +version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3e90af4de65aa7b293ef2d09daff88501eb254f58edde2e1ac02c82d873eadad" +checksum = "e2d098ff73c1ca148721f37baad5ea6a465a13f9573aba8641fbbbae8164a54e" [[package]] name = "arc-swap" @@ -2224,6 +2224,16 @@ dependencies = [ "percent-encoding", ] +[[package]] +name = "fraction" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3027ae1df8d41b4bed2241c8fdad4acc1e7af60c8e17743534b545e77182d678" +dependencies = [ + "lazy_static", + "num", +] + [[package]] name = "fragile" version = "2.0.0" @@ -5286,6 +5296,20 @@ dependencies = [ "winapi", ] +[[package]] +name = "num" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "43db66d1170d347f9a065114077f7dccb00c1b9478c89384490a3425279a4606" +dependencies = [ + "num-bigint", + "num-complex", + "num-integer", + "num-iter", + "num-rational", + "num-traits", +] + [[package]] name = "num-bigint" version = "0.4.3" @@ -5326,6 +5350,17 @@ dependencies = [ "num-traits", ] +[[package]] +name = "num-iter" +version = "0.1.43" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7d03e6c028c5dc5cac6e2dec0efda81fc887605bb3d884578bb6d6bf7514e252" +dependencies = [ + "autocfg", + "num-integer", + "num-traits", +] + [[package]] name = "num-rational" version = "0.4.1" @@ -10143,8 +10178,11 @@ dependencies = [ name = "sp-arithmetic-fuzzer" version = "2.0.0" dependencies = [ + "arbitrary", + "fraction", "honggfuzz", "num-bigint", + "num-traits", "primitive-types", "sp-arithmetic", ] diff --git a/frame/staking/src/tests.rs b/frame/staking/src/tests.rs index 36cafcc18..d97eb3ef8 100644 --- a/frame/staking/src/tests.rs +++ b/frame/staking/src/tests.rs @@ -5461,7 +5461,7 @@ fn proportional_ledger_slash_works() { ledger.active = unit; ledger.total = unit * 4 + value; // When - assert_eq!(ledger.slash(slash, 0, 0), slash - 5); + assert_eq!(ledger.slash(slash, 0, 0), slash); // Then // The amount slashed out of `unit` let affected_balance = value + unit * 4; @@ -5477,12 +5477,12 @@ fn proportional_ledger_slash_works() { value - value_slash }; assert_eq!(ledger.active, unit_slashed); - assert_eq!(ledger.unlocking, vec![c(5, value_slashed)]); - assert_eq!(ledger.total, value_slashed); + assert_eq!(ledger.unlocking, vec![c(5, value_slashed), c(7, 32)]); + assert_eq!(ledger.total, value_slashed + 32); assert_eq!(LedgerSlashPerEra::get().0, 0); assert_eq!( LedgerSlashPerEra::get().1, - BTreeMap::from([(4, 0), (5, value_slashed), (6, 0), (7, 0)]) + BTreeMap::from([(4, 0), (5, value_slashed), (6, 0), (7, 32)]) ); } diff --git a/primitives/arithmetic/fuzzer/Cargo.toml b/primitives/arithmetic/fuzzer/Cargo.toml index 7be800a2e..99dbdf748 100644 --- a/primitives/arithmetic/fuzzer/Cargo.toml +++ b/primitives/arithmetic/fuzzer/Cargo.toml @@ -14,8 +14,11 @@ publish = false targets = ["x86_64-unknown-linux-gnu"] [dependencies] +arbitrary = "1.3.0" +fraction = "0.13.1" honggfuzz = "0.5.49" num-bigint = "0.4.3" +num-traits = "0.2.15" primitive-types = "0.12.0" sp-arithmetic = { version = "6.0.0", path = ".." } @@ -28,8 +31,12 @@ name = "normalize" path = "src/normalize.rs" [[bin]] -name = "per_thing_rational" -path = "src/per_thing_rational.rs" +name = "per_thing_from_rational" +path = "src/per_thing_from_rational.rs" + +[[bin]] +name = "per_thing_mult_fraction" +path = "src/per_thing_mult_fraction.rs" [[bin]] name = "multiply_by_rational_with_rounding" diff --git a/primitives/arithmetic/fuzzer/src/multiply_by_rational_with_rounding.rs b/primitives/arithmetic/fuzzer/src/multiply_by_rational_with_rounding.rs index 13c9d022b..e9a3208a7 100644 --- a/primitives/arithmetic/fuzzer/src/multiply_by_rational_with_rounding.rs +++ b/primitives/arithmetic/fuzzer/src/multiply_by_rational_with_rounding.rs @@ -29,52 +29,79 @@ //! More information about `honggfuzz` can be found //! [here](https://docs.rs/honggfuzz/). +use fraction::prelude::BigFraction as Fraction; use honggfuzz::fuzz; -use sp_arithmetic::{helpers_128bit::multiply_by_rational_with_rounding, traits::Zero, Rounding}; +use sp_arithmetic::{MultiplyRational, Rounding, Rounding::*}; +/// Tries to demonstrate that `multiply_by_rational_with_rounding` is incorrect. fn main() { loop { - fuzz!(|data: ([u8; 16], [u8; 16], [u8; 16])| { - let (a_bytes, b_bytes, c_bytes) = data; - let (a, b, c) = ( - u128::from_be_bytes(a_bytes), - u128::from_be_bytes(b_bytes), - u128::from_be_bytes(c_bytes), - ); + fuzz!(|data: (u128, u128, u128, ArbitraryRounding)| { + let (f, n, d, r) = (data.0, data.1, data.2, data.3 .0); - println!("++ Equation: {} * {} / {}", a, b, c); - - // The point of this fuzzing is to make sure that `multiply_by_rational_with_rounding` - // is 100% accurate as long as the value fits in a u128. - if let Some(result) = multiply_by_rational_with_rounding(a, b, c, Rounding::Down) { - let truth = mul_div(a, b, c); - - if result != truth && result != truth + 1 { - println!("++ Expected {}", truth); - println!("+++++++ Got {}", result); - panic!(); - } - } + check::(f as u8, n as u8, d as u8, r); + check::(f as u16, n as u16, d as u16, r); + check::(f as u32, n as u32, d as u32, r); + check::(f as u64, n as u64, d as u64, r); + check::(f, n, d, r); }) } } -fn mul_div(a: u128, b: u128, c: u128) -> u128 { - use primitive_types::U256; - if a.is_zero() { - return Zero::zero() - } - let c = c.max(1); +fn check(f: N, n: N, d: N, r: Rounding) +where + N: MultiplyRational + Into + Copy + core::fmt::Debug, +{ + let Some(got) = f.multiply_rational(n, d, r) else { + return; + }; + + let (ae, be, ce) = + (Fraction::from(f.into()), Fraction::from(n.into()), Fraction::from(d.into())); + let want = round(ae * be / ce, r); - // e for extended - let ae: U256 = a.into(); - let be: U256 = b.into(); - let ce: U256 = c.into(); + assert_eq!( + Fraction::from(got.into()), + want, + "{:?} * {:?} / {:?} = {:?} != {:?}", + f, + n, + d, + got, + want + ); +} + +/// Round a `Fraction` according to the given mode. +fn round(f: Fraction, r: Rounding) -> Fraction { + match r { + Up => f.ceil(), + NearestPrefUp => + if f.fract() < Fraction::from(0.5) { + f.floor() + } else { + f.ceil() + }, + Down => f.floor(), + NearestPrefDown => + if f.fract() > Fraction::from(0.5) { + f.ceil() + } else { + f.floor() + }, + } +} - let r = ae * be / ce; - if r > u128::MAX.into() { - a - } else { - r.as_u128() +/// An [`arbitrary::Arbitrary`] [`Rounding`] mode. +struct ArbitraryRounding(Rounding); +impl arbitrary::Arbitrary<'_> for ArbitraryRounding { + fn arbitrary(u: &mut arbitrary::Unstructured<'_>) -> arbitrary::Result { + Ok(Self(match u.int_in_range(0..=3).unwrap() { + 0 => Up, + 1 => NearestPrefUp, + 2 => Down, + 3 => NearestPrefDown, + _ => unreachable!(), + })) } } diff --git a/primitives/arithmetic/fuzzer/src/per_thing_from_rational.rs b/primitives/arithmetic/fuzzer/src/per_thing_from_rational.rs new file mode 100644 index 000000000..93af4df9e --- /dev/null +++ b/primitives/arithmetic/fuzzer/src/per_thing_from_rational.rs @@ -0,0 +1,105 @@ +// This file is part of Substrate. + +// Copyright (C) Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +//! # Running +//! Running this fuzzer can be done with `cargo hfuzz run per_thing_from_rational`. `honggfuzz` CLI +//! options can be used by setting `HFUZZ_RUN_ARGS`, such as `-n 4` to use 4 threads. +//! +//! # Debugging a panic +//! Once a panic is found, it can be debugged with +//! `cargo hfuzz run-debug per_thing_from_rational hfuzz_workspace/per_thing_from_rational/*.fuzz`. + +use fraction::prelude::BigFraction as Fraction; +use honggfuzz::fuzz; +use sp_arithmetic::{ + traits::SaturatedConversion, PerThing, Perbill, Percent, Perquintill, Rounding::*, *, +}; + +/// Tries to demonstrate that `from_rational` is incorrect for any rounding modes. +/// +/// NOTE: This `Fraction` library is really slow. Using f128/f256 does not work for the large +/// numbers. But an optimization could be done do use either floats or Fraction depending on the +/// size of the inputs. +fn main() { + loop { + fuzz!(|data: (u128, u128, ArbitraryRounding)| { + let (n, d, r) = (data.0.min(data.1), data.0.max(data.1).max(1), data.2); + + check::(n, d, r.0); + check::(n, d, r.0); + check::(n, d, r.0); + check::(n, d, r.0); + check::(n, d, r.0); + }) + } +} + +/// Assert that the parts of `from_rational` are correct for the given rounding mode. +fn check(a: u128, b: u128, r: Rounding) +where + Per::Inner: Into, +{ + let approx_ratio = Per::from_rational_with_rounding(a, b, r).unwrap(); + let approx_parts = Fraction::from(approx_ratio.deconstruct().saturated_into::()); + + let perfect_ratio = if a == 0 && b == 0 { + Fraction::from(1) + } else { + Fraction::from(a) / Fraction::from(b.max(1)) + }; + let perfect_parts = round(perfect_ratio * Fraction::from(Per::ACCURACY.into()), r); + + assert_eq!( + approx_parts, perfect_parts, + "approx_parts: {}, perfect_parts: {}, a: {}, b: {}", + approx_parts, perfect_parts, a, b + ); +} + +/// Round a `Fraction` according to the given mode. +fn round(f: Fraction, r: Rounding) -> Fraction { + match r { + Up => f.ceil(), + NearestPrefUp => + if f.fract() < Fraction::from(0.5) { + f.floor() + } else { + f.ceil() + }, + Down => f.floor(), + NearestPrefDown => + if f.fract() > Fraction::from(0.5) { + f.ceil() + } else { + f.floor() + }, + } +} + +/// An [`arbitrary::Arbitrary`] [`Rounding`] mode. +struct ArbitraryRounding(Rounding); +impl arbitrary::Arbitrary<'_> for ArbitraryRounding { + fn arbitrary(u: &mut arbitrary::Unstructured<'_>) -> arbitrary::Result { + Ok(Self(match u.int_in_range(0..=3).unwrap() { + 0 => Up, + 1 => NearestPrefUp, + 2 => Down, + 3 => NearestPrefDown, + _ => unreachable!(), + })) + } +} diff --git a/primitives/arithmetic/fuzzer/src/per_thing_mult_fraction.rs b/primitives/arithmetic/fuzzer/src/per_thing_mult_fraction.rs new file mode 100644 index 000000000..9cfe28a78 --- /dev/null +++ b/primitives/arithmetic/fuzzer/src/per_thing_mult_fraction.rs @@ -0,0 +1,69 @@ +// This file is part of Substrate. + +// Copyright (C) Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +//! # Running +//! Running this fuzzer can be done with `cargo hfuzz run per_thing_mult_fraction`. `honggfuzz` CLI +//! options can be used by setting `HFUZZ_RUN_ARGS`, such as `-n 4` to use 4 threads. +//! +//! # Debugging a panic +//! Once a panic is found, it can be debugged with +//! `cargo hfuzz run-debug per_thing_mult_fraction hfuzz_workspace/per_thing_mult_fraction/*.fuzz`. + +use honggfuzz::fuzz; +use sp_arithmetic::{PerThing, Perbill, Percent, Perquintill, *}; + +/// Tries to disprove `(n / d) * d <= n` for any `PerThing`s. +fn main() { + loop { + fuzz!(|data: (u128, u128)| { + let (n, d) = (data.0.min(data.1), data.0.max(data.1).max(1)); + + check_mul::(n, d); + check_mul::(n, d); + check_mul::(n, d); + check_mul::(n, d); + + check_reciprocal_mul::(n, d); + check_reciprocal_mul::(n, d); + check_reciprocal_mul::(n, d); + check_reciprocal_mul::(n, d); + }) + } +} + +/// Checks that `(n / d) * d <= n`. +fn check_mul(n: u128, d: u128) +where + P: PerThing + core::ops::Mul, +{ + let q = P::from_rational_with_rounding(n, d, Rounding::Down).unwrap(); + assert!(q * d <= n, "{:?} * {:?} <= {:?}", q, d, n); +} + +/// Checks that `n / (n / d) >= d`. +fn check_reciprocal_mul(n: u128, d: u128) +where + P: PerThing + core::ops::Mul, +{ + let q = P::from_rational_with_rounding(n, d, Rounding::Down).unwrap(); + if q.is_zero() { + return + } + + let r = q.saturating_reciprocal_mul_floor(n); + assert!(r >= d, "{} / ({} / {}) != {} but {}", n, n, d, d, r); +} diff --git a/primitives/arithmetic/fuzzer/src/per_thing_rational.rs b/primitives/arithmetic/fuzzer/src/per_thing_rational.rs deleted file mode 100644 index c9c514656..000000000 --- a/primitives/arithmetic/fuzzer/src/per_thing_rational.rs +++ /dev/null @@ -1,116 +0,0 @@ -// This file is part of Substrate. - -// Copyright (C) Parity Technologies (UK) Ltd. -// SPDX-License-Identifier: Apache-2.0 - -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -//! # Running -//! Running this fuzzer can be done with `cargo hfuzz run per_thing_rational`. `honggfuzz` CLI -//! options can be used by setting `HFUZZ_RUN_ARGS`, such as `-n 4` to use 4 threads. -//! -//! # Debugging a panic -//! Once a panic is found, it can be debugged with -//! `cargo hfuzz run-debug per_thing_rational hfuzz_workspace/per_thing_rational/*.fuzz`. - -use honggfuzz::fuzz; -use sp_arithmetic::{traits::SaturatedConversion, PerThing, PerU16, Perbill, Percent, Perquintill}; - -fn main() { - loop { - fuzz!(|data: ((u16, u16), (u32, u32), (u64, u64))| { - let (u16_pair, u32_pair, u64_pair) = data; - - // peru16 - let (smaller, bigger) = (u16_pair.0.min(u16_pair.1), u16_pair.0.max(u16_pair.1)); - let ratio = PerU16::from_rational(smaller, bigger); - assert_per_thing_equal_error( - ratio, - PerU16::from_float(smaller as f64 / bigger.max(1) as f64), - 1, - ); - let (smaller, bigger) = (u32_pair.0.min(u32_pair.1), u32_pair.0.max(u32_pair.1)); - let ratio = PerU16::from_rational(smaller, bigger); - assert_per_thing_equal_error( - ratio, - PerU16::from_float(smaller as f64 / bigger.max(1) as f64), - 1, - ); - let (smaller, bigger) = (u64_pair.0.min(u64_pair.1), u64_pair.0.max(u64_pair.1)); - let ratio = PerU16::from_rational(smaller, bigger); - assert_per_thing_equal_error( - ratio, - PerU16::from_float(smaller as f64 / bigger.max(1) as f64), - 1, - ); - - // percent - let (smaller, bigger) = (u16_pair.0.min(u16_pair.1), u16_pair.0.max(u16_pair.1)); - let ratio = Percent::from_rational(smaller, bigger); - assert_per_thing_equal_error( - ratio, - Percent::from_float(smaller as f64 / bigger.max(1) as f64), - 1, - ); - - let (smaller, bigger) = (u32_pair.0.min(u32_pair.1), u32_pair.0.max(u32_pair.1)); - let ratio = Percent::from_rational(smaller, bigger); - assert_per_thing_equal_error( - ratio, - Percent::from_float(smaller as f64 / bigger.max(1) as f64), - 1, - ); - - let (smaller, bigger) = (u64_pair.0.min(u64_pair.1), u64_pair.0.max(u64_pair.1)); - let ratio = Percent::from_rational(smaller, bigger); - assert_per_thing_equal_error( - ratio, - Percent::from_float(smaller as f64 / bigger.max(1) as f64), - 1, - ); - - // perbill - let (smaller, bigger) = (u32_pair.0.min(u32_pair.1), u32_pair.0.max(u32_pair.1)); - let ratio = Perbill::from_rational(smaller, bigger); - assert_per_thing_equal_error( - ratio, - Perbill::from_float(smaller as f64 / bigger.max(1) as f64), - 100, - ); - - let (smaller, bigger) = (u64_pair.0.min(u64_pair.1), u64_pair.0.max(u64_pair.1)); - let ratio = Perbill::from_rational(smaller, bigger); - assert_per_thing_equal_error( - ratio, - Perbill::from_float(smaller as f64 / bigger.max(1) as f64), - 100, - ); - - // perquintillion - let (smaller, bigger) = (u64_pair.0.min(u64_pair.1), u64_pair.0.max(u64_pair.1)); - let ratio = Perquintill::from_rational(smaller, bigger); - assert_per_thing_equal_error( - ratio, - Perquintill::from_float(smaller as f64 / bigger.max(1) as f64), - 1000, - ); - }) - } -} - -fn assert_per_thing_equal_error(a: P, b: P, err: u128) { - let a_abs = a.deconstruct().saturated_into::(); - let b_abs = b.deconstruct().saturated_into::(); - let diff = a_abs.max(b_abs) - a_abs.min(b_abs); - assert!(diff <= err, "{:?} !~ {:?}", a, b); -} diff --git a/primitives/arithmetic/src/helpers_128bit.rs b/primitives/arithmetic/src/helpers_128bit.rs index 56432d15a..9b9c74ba5 100644 --- a/primitives/arithmetic/src/helpers_128bit.rs +++ b/primitives/arithmetic/src/helpers_128bit.rs @@ -182,8 +182,8 @@ mod double128 { } } -/// Returns `a * b / c` and `(a * b) % c` (wrapping to 128 bits) or `None` in the case of -/// overflow and c = 0. +/// Returns `a * b / c` (wrapping to 128 bits) or `None` in the case of +/// overflow. pub const fn multiply_by_rational_with_rounding( a: u128, b: u128, diff --git a/primitives/arithmetic/src/lib.rs b/primitives/arithmetic/src/lib.rs index 581206b6d..e7ba08a53 100644 --- a/primitives/arithmetic/src/lib.rs +++ b/primitives/arithmetic/src/lib.rs @@ -45,7 +45,7 @@ pub use per_things::{ InnerOf, MultiplyArg, PerThing, PerU16, Perbill, Percent, Permill, Perquintill, RationalArg, ReciprocalArg, Rounding, SignedRounding, UpperOf, }; -pub use rational::{Rational128, RationalInfinite}; +pub use rational::{MultiplyRational, Rational128, RationalInfinite}; use sp_std::{cmp::Ordering, fmt::Debug, prelude::*}; use traits::{BaseArithmetic, One, SaturatedConversion, Unsigned, Zero}; diff --git a/primitives/arithmetic/src/per_things.rs b/primitives/arithmetic/src/per_things.rs index 068bdb4e1..c4cb51966 100644 --- a/primitives/arithmetic/src/per_things.rs +++ b/primitives/arithmetic/src/per_things.rs @@ -26,7 +26,7 @@ use codec::{CompactAs, Encode}; use num_traits::{Pow, SaturatingAdd, SaturatingSub}; use sp_std::{ fmt, ops, - ops::{Add, AddAssign, Div, Rem, Sub}, + ops::{Add, Sub}, prelude::*, }; @@ -46,6 +46,7 @@ pub trait RationalArg: + Unsigned + Zero + One + + crate::MultiplyRational { } @@ -58,7 +59,8 @@ impl< + ops::AddAssign + Unsigned + Zero - + One, + + One + + crate::MultiplyRational, > RationalArg for T { } @@ -105,7 +107,7 @@ pub trait PerThing: + Pow { /// The data type used to build this per-thingy. - type Inner: BaseArithmetic + Unsigned + Copy + Into + fmt::Debug; + type Inner: BaseArithmetic + Unsigned + Copy + Into + fmt::Debug + crate::MultiplyRational; /// A data type larger than `Self::Inner`, used to avoid overflow in some computations. /// It must be able to compute `ACCURACY^2`. @@ -115,7 +117,8 @@ pub trait PerThing: + TryInto + UniqueSaturatedInto + Unsigned - + fmt::Debug; + + fmt::Debug + + crate::MultiplyRational; /// The accuracy of this type. const ACCURACY: Self::Inner; @@ -538,39 +541,6 @@ where rem_mul_div_inner.into() } -/// Just a simple generic integer divide with custom rounding. -fn div_rounded(n: N, d: N, r: Rounding) -> N -where - N: Clone - + Eq - + Ord - + Zero - + One - + AddAssign - + Add - + Rem - + Div, -{ - let mut o = n.clone() / d.clone(); - use Rounding::*; - let two = || N::one() + N::one(); - if match r { - Up => !((n % d).is_zero()), - NearestPrefDown => { - let rem = n % d.clone(); - rem > d / two() - }, - NearestPrefUp => { - let rem = n % d.clone(); - rem >= d.clone() / two() + d % two() - }, - Down => false, - } { - o += N::one() - } - o -} - macro_rules! implement_per_thing { ( $name:ident, @@ -687,7 +657,8 @@ macro_rules! implement_per_thing { + ops::AddAssign + Unsigned + Zero - + One, + + One + + $crate::MultiplyRational, Self::Inner: Into { // q cannot be zero. @@ -695,30 +666,8 @@ macro_rules! implement_per_thing { // p should not be bigger than q. if p > q { return Err(()) } - let factor = div_rounded::(q.clone(), $max.into(), Rounding::Up).max(One::one()); - - // q cannot overflow: (q / (q/$max)) < $max. p < q hence p also cannot overflow. - let q_reduce: $type = div_rounded(q, factor.clone(), r) - .try_into() - .map_err(|_| "Failed to convert") - .expect( - "`q / ceil(q/$max) < $max`; macro prevents any type being created that \ - does not satisfy this; qed" - ); - let p_reduce: $type = div_rounded(p, factor, r) - .try_into() - .map_err(|_| "Failed to convert") - .expect( - "`p / ceil(p/$max) < $max`; macro prevents any type being created that \ - does not satisfy this; qed" - ); - - // `p_reduced` and `q_reduced` are within `Self::Inner`. Multiplication by another - // `$max` will always fit in `$upper_type`. This is guaranteed by the macro tests. - let n = p_reduce as $upper_type * <$upper_type>::from($max); - let d = q_reduce as $upper_type; - let part = div_rounded(n, d, r); - Ok($name(part as Self::Inner)) + let max: N = $max.into(); + max.multiply_rational(p, q, r).ok_or(())?.try_into().map(|x| $name(x)).map_err(|_| ()) } } @@ -1862,6 +1811,22 @@ fn from_rational_with_rounding_works_in_extreme_case() { } } +#[test] +fn from_rational_with_rounding_breakage() { + let n = 372633774963620730670986667244911905u128; + let d = 512593663333074177468745541591173060u128; + let q = Perquintill::from_rational_with_rounding(n, d, Rounding::Down).unwrap(); + assert!(q * d <= n); +} + +#[test] +fn from_rational_with_rounding_breakage_2() { + let n = 36893488147419103230u128; + let d = 36893488147419103630u128; + let q = Perquintill::from_rational_with_rounding(n, d, Rounding::Up).unwrap(); + assert!(q * d >= n); +} + implement_per_thing!(Percent, test_per_cent, [u32, u64, u128], 100u8, u8, u16, "_Percent_",); implement_per_thing_with_perthousand!( PerU16, diff --git a/primitives/arithmetic/src/rational.rs b/primitives/arithmetic/src/rational.rs index 0fcb7d4d9..ebd89c615 100644 --- a/primitives/arithmetic/src/rational.rs +++ b/primitives/arithmetic/src/rational.rs @@ -284,6 +284,54 @@ impl PartialEq for Rational128 { } } +pub trait MultiplyRational: Sized { + fn multiply_rational(self, n: Self, d: Self, r: Rounding) -> Option; +} + +macro_rules! impl_rrm { + ($ulow:ty, $uhi:ty) => { + impl MultiplyRational for $ulow { + fn multiply_rational(self, n: Self, d: Self, r: Rounding) -> Option { + if d.is_zero() { + return None + } + + let sn = (self as $uhi) * (n as $uhi); + let mut result = sn / (d as $uhi); + let remainder = (sn % (d as $uhi)) as $ulow; + if match r { + Rounding::Up => remainder > 0, + // cannot be `(d + 1) / 2` since `d` might be `max_value` and overflow. + Rounding::NearestPrefUp => remainder >= d / 2 + d % 2, + Rounding::NearestPrefDown => remainder > d / 2, + Rounding::Down => false, + } { + result = match result.checked_add(1) { + Some(v) => v, + None => return None, + }; + } + if result > (<$ulow>::max_value() as $uhi) { + None + } else { + Some(result as $ulow) + } + } + } + }; +} + +impl_rrm!(u8, u16); +impl_rrm!(u16, u32); +impl_rrm!(u32, u64); +impl_rrm!(u64, u128); + +impl MultiplyRational for u128 { + fn multiply_rational(self, n: Self, d: Self, r: Rounding) -> Option { + crate::helpers_128bit::multiply_by_rational_with_rounding(self, n, d, r) + } +} + #[cfg(test)] mod tests { use super::{helpers_128bit::*, *}; diff --git a/primitives/arithmetic/src/traits.rs b/primitives/arithmetic/src/traits.rs index 33b4e376a..061b11b3e 100644 --- a/primitives/arithmetic/src/traits.rs +++ b/primitives/arithmetic/src/traits.rs @@ -32,6 +32,8 @@ use sp_std::ops::{ Add, AddAssign, Div, DivAssign, Mul, MulAssign, Rem, RemAssign, Shl, Shr, Sub, SubAssign, }; +use crate::MultiplyRational; + /// A meta trait for arithmetic type operations, regardless of any limitation on size. pub trait BaseArithmetic: From @@ -186,9 +188,9 @@ pub trait AtLeast32Bit: BaseArithmetic + From + From {} impl + From> AtLeast32Bit for T {} /// A meta trait for arithmetic. Same as [`AtLeast32Bit `], but also bounded to be unsigned. -pub trait AtLeast32BitUnsigned: AtLeast32Bit + Unsigned {} +pub trait AtLeast32BitUnsigned: AtLeast32Bit + Unsigned + MultiplyRational {} -impl AtLeast32BitUnsigned for T {} +impl AtLeast32BitUnsigned for T {} /// Just like `From` except that if the source value is too big to fit into the destination type /// then it'll saturate the destination. From ad66dd2ff1c6c97d9d5d25a0dc7d3a44342c9a3a Mon Sep 17 00:00:00 2001 From: Oliver Tale-Yazdi Date: Fri, 24 Mar 2023 16:09:34 +0100 Subject: [PATCH 286/558] try-runtime: run migration checks per default (#13684) * try-runtime: run migration checks per default The current behaviour of having to explicetly specify --checks seems to cause confusion. Therefore bringing back the old behaviour of running the pre- and post-upgrade checks per default. Signed-off-by: Oliver Tale-Yazdi * Fix docs Signed-off-by: Oliver Tale-Yazdi --------- Signed-off-by: Oliver Tale-Yazdi --- .../try-runtime/cli/src/commands/on_runtime_upgrade.rs | 4 ++-- utils/frame/try-runtime/cli/src/lib.rs | 10 +++++----- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/utils/frame/try-runtime/cli/src/commands/on_runtime_upgrade.rs b/utils/frame/try-runtime/cli/src/commands/on_runtime_upgrade.rs index c8582ea4d..948ff9c03 100644 --- a/utils/frame/try-runtime/cli/src/commands/on_runtime_upgrade.rs +++ b/utils/frame/try-runtime/cli/src/commands/on_runtime_upgrade.rs @@ -40,8 +40,8 @@ pub struct OnRuntimeUpgradeCmd { /// Performing any checks will potentially invalidate the measured PoV/Weight. // NOTE: The clap attributes make it backwards compatible with the previous `--checks` flag. #[clap(long, - default_value = "None", - default_missing_value = "All", + default_value = "pre-and-post", + default_missing_value = "all", num_args = 0..=1, require_equals = true, verbatim_doc_comment)] diff --git a/utils/frame/try-runtime/cli/src/lib.rs b/utils/frame/try-runtime/cli/src/lib.rs index 0a521d121..bc9fd99d3 100644 --- a/utils/frame/try-runtime/cli/src/lib.rs +++ b/utils/frame/try-runtime/cli/src/lib.rs @@ -134,10 +134,10 @@ //! //! ```ignore //! -//! #[cfg(feature = try-runtime)] +//! #[cfg(feature = "try-runtime")] //! fn pre_upgrade() -> Result, &'static str> {} //! -//! #[cfg(feature = try-runtime)] +//! #[cfg(feature = "try-runtime")] //! fn post_upgrade(state: Vec) -> Result<(), &'static str> {} //! ``` //! @@ -152,9 +152,9 @@ //! //! Similarly, each pallet can expose a function in `#[pallet::hooks]` section as follows: //! -//! ``` -//! #[cfg(feature = try-runtime)] -//! fn try_state(_) -> Result<(), &'static str> {} +//! ```ignore +//! #[cfg(feature = "try-runtime")] +//! fn try_state(_: BlockNumber) -> Result<(), &'static str> {} //! ``` //! //! which is called on numerous code paths in the try-runtime tool. These checks should ensure that From 770d23709a7e004ae8f5fff1598bb4cf72928b43 Mon Sep 17 00:00:00 2001 From: Gavin Wood Date: Fri, 24 Mar 2023 17:48:21 +0000 Subject: [PATCH 287/558] Make ED of zero (kind of) work (#13655) * Minimum of 1 for ED * Avoid need for ED to be minimum one * Docs * Ban ED of zero unless feature enabled * use integrity_test * Docs * Cleanup * Update frame/balances/Cargo.toml Co-authored-by: Oliver Tale-Yazdi * Update frame/balances/src/lib.rs Co-authored-by: Oliver Tale-Yazdi * Update frame/balances/src/lib.rs Co-authored-by: Oliver Tale-Yazdi * Ensure dodgy code is disabled by default * zero_ed -> insecure_zero_ed --------- Co-authored-by: Oliver Tale-Yazdi Co-authored-by: parity-processbot <> --- frame/balances/Cargo.toml | 2 + frame/balances/src/lib.rs | 55 +++++++++++++++++++--- frame/balances/src/tests/currency_tests.rs | 15 +++++- frame/balances/src/tests/mod.rs | 2 +- frame/vesting/src/mock.rs | 2 +- 5 files changed, 65 insertions(+), 11 deletions(-) diff --git a/frame/balances/Cargo.toml b/frame/balances/Cargo.toml index 15ef136aa..9646a934d 100644 --- a/frame/balances/Cargo.toml +++ b/frame/balances/Cargo.toml @@ -39,5 +39,7 @@ std = [ "sp-runtime/std", "sp-std/std", ] +# Enable support for setting the existential deposit to zero. +insecure_zero_ed = [] runtime-benchmarks = ["frame-benchmarking/runtime-benchmarks"] try-runtime = ["frame-support/try-runtime"] diff --git a/frame/balances/src/lib.rs b/frame/balances/src/lib.rs index ca8e86ef2..460f2be37 100644 --- a/frame/balances/src/lib.rs +++ b/frame/balances/src/lib.rs @@ -151,6 +151,10 @@ //! ## Assumptions //! //! * Total issued balanced of all accounts should be less than `Config::Balance::max_value()`. +//! * Existential Deposit is set to a value greater than zero. +//! +//! Note, you may find the Balances pallet still functions with an ED of zero in some circumstances, +//! however this is not a configuration which is generally supported, nor will it be. #![cfg_attr(not(feature = "std"), no_std)] mod benchmarking; @@ -231,7 +235,14 @@ pub mod pallet { /// Handler for the unbalanced reduction when removing a dust account. type DustRemoval: OnUnbalanced>; - /// The minimum amount required to keep an account open. + /// The minimum amount required to keep an account open. MUST BE GREATER THAN ZERO! + /// + /// If you *really* need it to be zero, you can enable the feature `insecure_zero_ed` for + /// this pallet. However, you do so at your own risk: this will open up a major DoS vector. + /// In case you have multiple sources of provider references, you may also get unexpected + /// behaviour if you set this to zero. + /// + /// Bottom line: Do yourself a favour and make it at least one! #[pallet::constant] type ExistentialDeposit: Get; @@ -445,6 +456,7 @@ pub mod pallet { impl, I: 'static> GenesisBuild for GenesisConfig { fn build(&self) { let total = self.balances.iter().fold(Zero::zero(), |acc: T::Balance, &(_, n)| acc + n); + >::put(total); for (_, balance) in &self.balances { @@ -492,6 +504,17 @@ pub mod pallet { } } + #[pallet::hooks] + impl, I: 'static> Hooks for Pallet { + #[cfg(not(feature = "insecure_zero_ed"))] + fn integrity_test() { + assert!( + !>::ExistentialDeposit::get().is_zero(), + "The existential deposit must be greater than zero!" + ); + } + } + #[pallet::call] impl, I: 'static> Pallet { /// Transfer some liquid free balance to another account. @@ -533,7 +556,7 @@ pub mod pallet { ) -> DispatchResultWithPostInfo { ensure_root(origin)?; let who = T::Lookup::lookup(who)?; - let existential_deposit = T::ExistentialDeposit::get(); + let existential_deposit = Self::ed(); let wipeout = new_free < existential_deposit; let new_free = if wipeout { Zero::zero() } else { new_free }; @@ -716,7 +739,7 @@ pub mod pallet { ) -> DispatchResultWithPostInfo { ensure_root(origin)?; let who = T::Lookup::lookup(who)?; - let existential_deposit = T::ExistentialDeposit::get(); + let existential_deposit = Self::ed(); let wipeout = new_free < existential_deposit; let new_free = if wipeout { Zero::zero() } else { new_free }; @@ -742,6 +765,9 @@ pub mod pallet { } impl, I: 'static> Pallet { + fn ed() -> T::Balance { + T::ExistentialDeposit::get() + } /// Ensure the account `who` is using the new logic. /// /// Returns `true` if the account did get upgraded, `false` if it didn't need upgrading. @@ -756,7 +782,7 @@ pub mod pallet { // Gah!! We have a non-zero reserve balance but no provider refs :( // This shouldn't practically happen, but we need a failsafe anyway: let's give // them enough for an ED. - a.free = a.free.min(T::ExistentialDeposit::get()); + a.free = a.free.min(Self::ed()); system::Pallet::::inc_providers(who); } let _ = system::Pallet::::inc_consumers(who).defensive(); @@ -864,6 +890,20 @@ pub mod pallet { Self::try_mutate_account(who, |a, _| -> Result { Ok(f(a)) }) } + /// Returns `true` when `who` has some providers or `insecure_zero_ed` feature is disnabled. + /// Returns `false` otherwise. + #[cfg(not(feature = "insecure_zero_ed"))] + fn have_providers_or_no_zero_ed(_: &T::AccountId) -> bool { + true + } + + /// Returns `true` when `who` has some providers or `insecure_zero_ed` feature is disnabled. + /// Returns `false` otherwise. + #[cfg(feature = "insecure_zero_ed")] + fn have_providers_or_no_zero_ed(who: &T::AccountId) -> bool { + frame_system::Pallet::::providers(who) > 0 + } + /// Mutate an account to some new value, or delete it entirely with `None`. Will enforce /// `ExistentialDeposit` law, annulling the account as needed. This will do nothing if the /// result of `f` is an `Err`. @@ -885,13 +925,14 @@ pub mod pallet { let result = T::AccountStore::try_mutate_exists(who, |maybe_account| { let is_new = maybe_account.is_none(); let mut account = maybe_account.take().unwrap_or_default(); - let did_provide = account.free >= T::ExistentialDeposit::get(); + let did_provide = + account.free >= Self::ed() && Self::have_providers_or_no_zero_ed(who); let did_consume = !is_new && (!account.reserved.is_zero() || !account.frozen.is_zero()); let result = f(&mut account, is_new)?; - let does_provide = account.free >= T::ExistentialDeposit::get(); + let does_provide = account.free >= Self::ed(); let does_consume = !account.reserved.is_zero() || !account.frozen.is_zero(); if !did_provide && does_provide { @@ -930,7 +971,7 @@ pub mod pallet { // // We should never be dropping if reserved is non-zero. Reserved being non-zero // should imply that we have a consumer ref, so this is economically safe. - let ed = T::ExistentialDeposit::get(); + let ed = Self::ed(); let maybe_dust = if account.free < ed && account.reserved.is_zero() { if account.free.is_zero() { None diff --git a/frame/balances/src/tests/currency_tests.rs b/frame/balances/src/tests/currency_tests.rs index b0a38f4ac..f74e6bb8c 100644 --- a/frame/balances/src/tests/currency_tests.rs +++ b/frame/balances/src/tests/currency_tests.rs @@ -23,7 +23,8 @@ use frame_support::traits::{ BalanceStatus::{Free, Reserved}, Currency, ExistenceRequirement::{self, AllowDeath}, - LockIdentifier, LockableCurrency, NamedReservableCurrency, ReservableCurrency, WithdrawReasons, + Hooks, LockIdentifier, LockableCurrency, NamedReservableCurrency, ReservableCurrency, + WithdrawReasons, }; const ID_1: LockIdentifier = *b"1 "; @@ -974,7 +975,7 @@ fn slash_reserved_on_non_existant_works() { fn operations_on_dead_account_should_not_change_state() { // These functions all use `mutate_account` which may introduce a storage change when // the account never existed to begin with, and shouldn't exist in the end. - ExtBuilder::default().existential_deposit(0).build_and_execute_with(|| { + ExtBuilder::default().existential_deposit(1).build_and_execute_with(|| { assert!(!frame_system::Account::::contains_key(&1337)); // Unreserve @@ -993,6 +994,16 @@ fn operations_on_dead_account_should_not_change_state() { }); } +#[test] +#[should_panic = "The existential deposit must be greater than zero!"] +fn zero_ed_is_prohibited() { + // These functions all use `mutate_account` which may introduce a storage change when + // the account never existed to begin with, and shouldn't exist in the end. + ExtBuilder::default().existential_deposit(0).build_and_execute_with(|| { + Balances::integrity_test(); + }); +} + #[test] fn named_reserve_should_work() { ExtBuilder::default().build_and_execute_with(|| { diff --git a/frame/balances/src/tests/mod.rs b/frame/balances/src/tests/mod.rs index c4a8a631c..68e7e8203 100644 --- a/frame/balances/src/tests/mod.rs +++ b/frame/balances/src/tests/mod.rs @@ -87,7 +87,7 @@ parameter_types! { frame_system::limits::BlockWeights::simple_max( frame_support::weights::Weight::from_parts(1024, u64::MAX), ); - pub static ExistentialDeposit: u64 = 0; + pub static ExistentialDeposit: u64 = 1; } impl frame_system::Config for Test { type BaseCallFilter = frame_support::traits::Everything; diff --git a/frame/vesting/src/mock.rs b/frame/vesting/src/mock.rs index be6afd4a6..1adb36b73 100644 --- a/frame/vesting/src/mock.rs +++ b/frame/vesting/src/mock.rs @@ -89,7 +89,7 @@ parameter_types! { pub const MinVestedTransfer: u64 = 256 * 2; pub UnvestedFundsAllowedWithdrawReasons: WithdrawReasons = WithdrawReasons::except(WithdrawReasons::TRANSFER | WithdrawReasons::RESERVE); - pub static ExistentialDeposit: u64 = 0; + pub static ExistentialDeposit: u64 = 1; } impl Config for Test { type BlockNumberToBalance = Identity; From d0449fd4219cb3c77e46a9916c7996a1136ca63e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bastian=20K=C3=B6cher?= Date: Fri, 24 Mar 2023 18:50:16 +0100 Subject: [PATCH 288/558] try-runtime: Use configured wasm execution method (#13694) * try-runtime: Use configured wasm execution method * Fix compilation --- client/cli/src/arg_enums.rs | 4 ++-- utils/frame/try-runtime/cli/src/lib.rs | 8 ++++---- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/client/cli/src/arg_enums.rs b/client/cli/src/arg_enums.rs index c3399a896..7e2498cec 100644 --- a/client/cli/src/arg_enums.rs +++ b/client/cli/src/arg_enums.rs @@ -74,12 +74,12 @@ impl std::fmt::Display for WasmExecutionMethod { /// into an execution method which can be used internally. pub fn execution_method_from_cli( execution_method: WasmExecutionMethod, - _instantiation_strategy: WasmtimeInstantiationStrategy, + instantiation_strategy: WasmtimeInstantiationStrategy, ) -> sc_service::config::WasmExecutionMethod { match execution_method { WasmExecutionMethod::Interpreted => sc_service::config::WasmExecutionMethod::Interpreted, WasmExecutionMethod::Compiled => sc_service::config::WasmExecutionMethod::Compiled { - instantiation_strategy: match _instantiation_strategy { + instantiation_strategy: match instantiation_strategy { WasmtimeInstantiationStrategy::PoolingCopyOnWrite => sc_service::config::WasmtimeInstantiationStrategy::PoolingCopyOnWrite, WasmtimeInstantiationStrategy::RecreateInstanceCopyOnWrite => diff --git a/utils/frame/try-runtime/cli/src/lib.rs b/utils/frame/try-runtime/cli/src/lib.rs index bc9fd99d3..e1873f6a0 100644 --- a/utils/frame/try-runtime/cli/src/lib.rs +++ b/utils/frame/try-runtime/cli/src/lib.rs @@ -133,7 +133,6 @@ //! given the right flag: //! //! ```ignore -//! //! #[cfg(feature = "try-runtime")] //! fn pre_upgrade() -> Result, &'static str> {} //! @@ -365,8 +364,9 @@ use remote_externalities::{ TestExternalities, }; use sc_cli::{ - CliConfiguration, RuntimeVersion, WasmExecutionMethod, WasmtimeInstantiationStrategy, - DEFAULT_WASMTIME_INSTANTIATION_STRATEGY, DEFAULT_WASM_EXECUTION_METHOD, + execution_method_from_cli, CliConfiguration, RuntimeVersion, WasmExecutionMethod, + WasmtimeInstantiationStrategy, DEFAULT_WASMTIME_INSTANTIATION_STRATEGY, + DEFAULT_WASM_EXECUTION_METHOD, }; use sc_executor::{sp_wasm_interface::HostFunctions, WasmExecutor}; use sp_api::HashT; @@ -831,7 +831,7 @@ pub(crate) fn build_executor(shared: &SharedParams) -> WasmExe let runtime_cache_size = 2; WasmExecutor::new( - sc_executor::WasmExecutionMethod::Interpreted, + execution_method_from_cli(shared.wasm_method, shared.wasmtime_instantiation_strategy), heap_pages, max_runtime_instances, None, From e87f7acc4191274a2889f1b9adb59260e038b0a8 Mon Sep 17 00:00:00 2001 From: Mira Ressel Date: Fri, 24 Mar 2023 22:00:48 +0100 Subject: [PATCH 289/558] bump timestamp script to v0.2 (#13710) --- .gitlab-ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 6cf062736..c4d88ebde 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -313,7 +313,7 @@ include: rules: - if: $PIPELINE != "automatic-crate-publishing" - project: parity/infrastructure/ci_cd/shared - ref: v0.1 + ref: v0.2 file: /common/timestamp.yml #### stage: notify From a3df63ef370adb7f806aa6ffa876eb17672b7ca8 Mon Sep 17 00:00:00 2001 From: Davide Galassi Date: Fri, 24 Mar 2023 23:54:32 +0100 Subject: [PATCH 290/558] Fix docs (#13709) --- primitives/application-crypto/src/traits.rs | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/primitives/application-crypto/src/traits.rs b/primitives/application-crypto/src/traits.rs index f672efb8a..b3bcd0ce2 100644 --- a/primitives/application-crypto/src/traits.rs +++ b/primitives/application-crypto/src/traits.rs @@ -22,7 +22,14 @@ use codec::Codec; use sp_core::crypto::{CryptoType, CryptoTypeId, IsWrappedBy, KeyTypeId, Public}; use sp_std::{fmt::Debug, vec::Vec}; -/// An application-specific key. +/// An application-specific cryptographic object. +/// +/// Combines all the core types and constants that are defined by a particular +/// cryptographic scheme when it is used in a specific application domain. +/// +/// Typically, the implementers of this trait are its associated types themselves. +/// This provides a convenient way to access generic information about the scheme +/// given any of the associated types. pub trait AppCrypto: 'static + Send + Sync + Sized + CryptoType + Clone { /// Identifier for application-specific key type. const ID: KeyTypeId; From 5766265306d104b4a5d7cf1082bcae78986dfff0 Mon Sep 17 00:00:00 2001 From: Aaro Altonen <48052676+altonen@users.noreply.github.com> Date: Sat, 25 Mar 2023 01:47:28 +0200 Subject: [PATCH 291/558] Get the correct number of connected peers in `SyncState` (#13700) `Protocol` is not a reliable source for the information of connected peers because it doesn't have real-time information of the actual connectivity state because it's not resposible for accepting/rejecting connections and gets that information with delay from `SyncinEngine`. --- client/informant/src/display.rs | 2 +- client/network/common/src/sync.rs | 2 ++ client/network/sync/src/engine.rs | 4 +++- client/network/sync/src/lib.rs | 1 + 4 files changed, 7 insertions(+), 2 deletions(-) diff --git a/client/informant/src/display.rs b/client/informant/src/display.rs index 46e722927..2f1013072 100644 --- a/client/informant/src/display.rs +++ b/client/informant/src/display.rs @@ -76,7 +76,7 @@ impl InformantDisplay { let best_number = info.chain.best_number; let best_hash = info.chain.best_hash; let finalized_number = info.chain.finalized_number; - let num_connected_peers = net_status.num_connected_peers; + let num_connected_peers = sync_status.num_connected_peers; let speed = speed::(best_number, self.last_number, self.last_update); let total_bytes_inbound = net_status.total_bytes_inbound; let total_bytes_outbound = net_status.total_bytes_outbound; diff --git a/client/network/common/src/sync.rs b/client/network/common/src/sync.rs index 130f354b7..d02a81379 100644 --- a/client/network/common/src/sync.rs +++ b/client/network/common/src/sync.rs @@ -94,6 +94,8 @@ pub struct SyncStatus { pub best_seen_block: Option>, /// Number of peers participating in syncing. pub num_peers: u32, + /// Number of peers known to `SyncingEngine` (both full and light). + pub num_connected_peers: u32, /// Number of blocks queued for import pub queued_blocks: u32, /// State sync status in progress, if any. diff --git a/client/network/sync/src/engine.rs b/client/network/sync/src/engine.rs index a9c9afc3c..b4c1a2ed0 100644 --- a/client/network/sync/src/engine.rs +++ b/client/network/sync/src/engine.rs @@ -711,7 +711,9 @@ where ToServiceCommand::NewBestBlockImported(hash, number) => self.new_best_block_imported(hash, number), ToServiceCommand::Status(tx) => { - let _ = tx.send(self.chain_sync.status()); + let mut status = self.chain_sync.status(); + status.num_connected_peers = self.peers.len() as u32; + let _ = tx.send(status); }, ToServiceCommand::NumActivePeers(tx) => { let _ = tx.send(self.chain_sync.num_active_peers()); diff --git a/client/network/sync/src/lib.rs b/client/network/sync/src/lib.rs index 45d14ffa7..28959e7f9 100644 --- a/client/network/sync/src/lib.rs +++ b/client/network/sync/src/lib.rs @@ -523,6 +523,7 @@ where state: sync_state, best_seen_block, num_peers: self.peers.len() as u32, + num_connected_peers: 0u32, queued_blocks: self.queue_blocks.len() as u32, state_sync: self.state_sync.as_ref().map(|s| s.progress()), warp_sync: warp_sync_progress, From ea7238237da06731ff2c4280d71f00c0b361e74c Mon Sep 17 00:00:00 2001 From: Harald Heckmann Date: Sat, 25 Mar 2023 00:53:18 +0100 Subject: [PATCH 292/558] Emit event when changing total locked value in pallet-balances (#12287) * Emit Locked/Unlocked events * Implement lock event tests * Adhere to style guide * Use saturating math Co-authored-by: Kian Paimani <5588131+kianenigma@users.noreply.github.com> * Fix typo * Emit event on change locks and add tests * Adjust event docstring --------- Co-authored-by: Kian Paimani <5588131+kianenigma@users.noreply.github.com> Co-authored-by: parity-processbot <> --- frame/balances/src/lib.rs | 16 +++++++ frame/balances/src/tests/currency_tests.rs | 55 ++++++++++++++++++++++ 2 files changed, 71 insertions(+) diff --git a/frame/balances/src/lib.rs b/frame/balances/src/lib.rs index 460f2be37..d4be80698 100644 --- a/frame/balances/src/lib.rs +++ b/frame/balances/src/lib.rs @@ -330,6 +330,10 @@ pub mod pallet { Issued { amount: T::Balance }, /// Total issuance was decreased by `amount`, creating a debt to be balanced. Rescinded { amount: T::Balance }, + /// Some balance was locked. + Locked { who: T::AccountId, amount: T::Balance }, + /// Some balance was unlocked. + Unlocked { who: T::AccountId, amount: T::Balance }, } #[pallet::error] @@ -1016,9 +1020,12 @@ pub mod pallet { ); } let freezes = Freezes::::get(who); + let mut prev_frozen = Zero::zero(); + let mut after_frozen = Zero::zero(); // TODO: Revisit this assumption. We no manipulate consumer/provider refs. // No way this can fail since we do not alter the existential balances. let res = Self::mutate_account(who, |b| { + prev_frozen = b.frozen; b.frozen = Zero::zero(); for l in locks.iter() { b.frozen = b.frozen.max(l.amount); @@ -1026,6 +1033,7 @@ pub mod pallet { for l in freezes.iter() { b.frozen = b.frozen.max(l.amount); } + after_frozen = b.frozen; }); debug_assert!(res.is_ok()); if let Ok((_, maybe_dust)) = res { @@ -1053,6 +1061,14 @@ pub mod pallet { ); } } + + if prev_frozen > after_frozen { + let amount = prev_frozen.saturating_sub(after_frozen); + Self::deposit_event(Event::Unlocked { who: who.clone(), amount }); + } else if after_frozen > prev_frozen { + let amount = after_frozen.saturating_sub(prev_frozen); + Self::deposit_event(Event::Locked { who: who.clone(), amount }); + } } /// Update the account entry for `who`, given the locks. diff --git a/frame/balances/src/tests/currency_tests.rs b/frame/balances/src/tests/currency_tests.rs index f74e6bb8c..034c92f65 100644 --- a/frame/balances/src/tests/currency_tests.rs +++ b/frame/balances/src/tests/currency_tests.rs @@ -754,6 +754,61 @@ fn emit_events_with_reserve_and_unreserve() { }); } +#[test] +fn emit_events_with_changing_locks() { + ExtBuilder::default().build_and_execute_with(|| { + let _ = Balances::deposit_creating(&1, 100); + System::reset_events(); + + // Locks = [] --> [10] + Balances::set_lock(*b"LOCK_000", &1, 10, WithdrawReasons::TRANSFER); + assert_eq!(events(), [RuntimeEvent::Balances(crate::Event::Locked { who: 1, amount: 10 })]); + + // Locks = [10] --> [15] + Balances::set_lock(*b"LOCK_000", &1, 15, WithdrawReasons::TRANSFER); + assert_eq!(events(), [RuntimeEvent::Balances(crate::Event::Locked { who: 1, amount: 5 })]); + + // Locks = [15] --> [15, 20] + Balances::set_lock(*b"LOCK_001", &1, 20, WithdrawReasons::TRANSACTION_PAYMENT); + assert_eq!(events(), [RuntimeEvent::Balances(crate::Event::Locked { who: 1, amount: 5 })]); + + // Locks = [15, 20] --> [17, 20] + Balances::set_lock(*b"LOCK_000", &1, 17, WithdrawReasons::TRANSACTION_PAYMENT); + for event in events() { + match event { + RuntimeEvent::Balances(crate::Event::Locked { .. }) => { + assert!(false, "unexpected lock event") + }, + RuntimeEvent::Balances(crate::Event::Unlocked { .. }) => { + assert!(false, "unexpected unlock event") + }, + _ => continue, + } + } + + // Locks = [17, 20] --> [17, 15] + Balances::set_lock(*b"LOCK_001", &1, 15, WithdrawReasons::TRANSFER); + assert_eq!( + events(), + [RuntimeEvent::Balances(crate::Event::Unlocked { who: 1, amount: 3 })] + ); + + // Locks = [17, 15] --> [15] + Balances::remove_lock(*b"LOCK_000", &1); + assert_eq!( + events(), + [RuntimeEvent::Balances(crate::Event::Unlocked { who: 1, amount: 2 })] + ); + + // Locks = [15] --> [] + Balances::remove_lock(*b"LOCK_001", &1); + assert_eq!( + events(), + [RuntimeEvent::Balances(crate::Event::Unlocked { who: 1, amount: 15 })] + ); + }); +} + #[test] fn emit_events_with_existential_deposit() { ExtBuilder::default().existential_deposit(100).build_and_execute_with(|| { From 78c99dadc961a46aaa90c231a209f0855a97e8ca Mon Sep 17 00:00:00 2001 From: Oliver Tale-Yazdi Date: Sat, 25 Mar 2023 16:59:31 +0100 Subject: [PATCH 293/558] Fix failing offences bench test (#13712) Signed-off-by: Oliver Tale-Yazdi --- frame/offences/benchmarking/src/lib.rs | 11 ++++++++++- frame/offences/benchmarking/src/mock.rs | 2 +- 2 files changed, 11 insertions(+), 2 deletions(-) diff --git a/frame/offences/benchmarking/src/lib.rs b/frame/offences/benchmarking/src/lib.rs index 0efbdcd48..894a725b5 100644 --- a/frame/offences/benchmarking/src/lib.rs +++ b/frame/offences/benchmarking/src/lib.rs @@ -335,6 +335,12 @@ benchmarks! { let balance_slash = |id| core::iter::once( ::RuntimeEvent::from(pallet_balances::Event::::Slashed{ who: id, amount: slash_amount.into() }) ); + let balance_locked = |id| core::iter::once( + ::RuntimeEvent::from(pallet_balances::Event::::Locked{ who: id, amount: slash_amount.into() }) + ); + let balance_unlocked = |id| core::iter::once( + ::RuntimeEvent::from(pallet_balances::Event::::Unlocked{ who: id, amount: slash_amount.into() }) + ); let chill = |id| core::iter::once( ::RuntimeEvent::from(StakingEvent::::Chilled{ stash: id }) ); @@ -349,12 +355,15 @@ benchmarks! { let slash_events = raw_offenders.into_iter() .flat_map(|offender| { let nom_slashes = offender.nominator_stashes.into_iter().flat_map(|nom| { - balance_slash(nom.clone()).map(Into::into).chain(slash(nom).map(Into::into)).map(Box::new) + balance_slash(nom.clone()).map(Into::into) + .chain(balance_unlocked(nom.clone()).map(Into::into)) + .chain(slash(nom).map(Into::into)).map(Box::new) }); let events = chill(offender.stash.clone()).map(Into::into).map(Box::new) .chain(slash_report(offender.stash.clone()).map(Into::into).map(Box::new)) .chain(balance_slash(offender.stash.clone()).map(Into::into).map(Box::new)) + .chain(balance_unlocked(offender.stash.clone()).map(Into::into).map(Box::new)) .chain(slash(offender.stash).map(Into::into).map(Box::new)) .chain(nom_slashes) .collect::>(); diff --git a/frame/offences/benchmarking/src/mock.rs b/frame/offences/benchmarking/src/mock.rs index 058361e9f..668d88e0b 100644 --- a/frame/offences/benchmarking/src/mock.rs +++ b/frame/offences/benchmarking/src/mock.rs @@ -65,7 +65,7 @@ impl frame_system::Config for Test { } impl pallet_balances::Config for Test { - type MaxLocks = (); + type MaxLocks = ConstU32<128>; type MaxReserves = (); type ReserveIdentifier = [u8; 8]; type Balance = Balance; From aeefc8bda0c0eecc67ca2d5ad24665ff27191968 Mon Sep 17 00:00:00 2001 From: yjh Date: Mon, 27 Mar 2023 06:07:17 +0800 Subject: [PATCH 294/558] chore(sc-cli): improve runner and signals (#13688) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * chore(sc-cli): improve runner and signals * Update client/cli/src/runner.rs * fmt --------- Co-authored-by: Bastian Köcher --- client/cli/src/lib.rs | 2 + client/cli/src/runner.rs | 101 +++++--------------------------------- client/cli/src/signals.rs | 92 ++++++++++++++++++++++++++++++++++ 3 files changed, 106 insertions(+), 89 deletions(-) create mode 100644 client/cli/src/signals.rs diff --git a/client/cli/src/lib.rs b/client/cli/src/lib.rs index e73321ecc..5d451bbed 100644 --- a/client/cli/src/lib.rs +++ b/client/cli/src/lib.rs @@ -31,6 +31,7 @@ mod config; mod error; mod params; mod runner; +mod signals; pub use arg_enums::*; pub use clap; @@ -41,6 +42,7 @@ pub use params::*; pub use runner::*; pub use sc_service::{ChainSpec, Role}; pub use sc_tracing::logging::LoggerBuilder; +pub use signals::Signals; pub use sp_version::RuntimeVersion; /// Substrate client CLI diff --git a/client/cli/src/runner.rs b/client/cli/src/runner.rs index 3d216aef4..a8b75f266 100644 --- a/client/cli/src/runner.rs +++ b/client/cli/src/runner.rs @@ -16,80 +16,15 @@ // You should have received a copy of the GNU General Public License // along with this program. If not, see . -use crate::{error::Error as CliError, Result, SubstrateCli}; +use crate::{error::Error as CliError, Result, Signals, SubstrateCli}; use chrono::prelude::*; -use futures::{ - future::{self, BoxFuture, FutureExt}, - pin_mut, select, Future, -}; +use futures::{future::FutureExt, Future}; use log::info; use sc_service::{Configuration, Error as ServiceError, TaskManager}; use sc_utils::metrics::{TOKIO_THREADS_ALIVE, TOKIO_THREADS_TOTAL}; use std::{marker::PhantomData, time::Duration}; -/// Abstraction over OS signals to handle the shutdown of the node smoothly. -/// -/// On `unix` this represents `SigInt` and `SigTerm`. -pub struct Signals(BoxFuture<'static, ()>); - -impl Signals { - /// Capture the relevant signals to handle shutdown of the node smoothly. - /// - /// Needs to be called in a Tokio context to have access to the tokio reactor. - #[cfg(target_family = "unix")] - pub fn capture() -> std::result::Result { - use tokio::signal::unix::{signal, SignalKind}; - - let mut stream_int = signal(SignalKind::interrupt()).map_err(ServiceError::Io)?; - let mut stream_term = signal(SignalKind::terminate()).map_err(ServiceError::Io)?; - - Ok(Signals( - async move { - future::select(stream_int.recv().boxed(), stream_term.recv().boxed()).await; - } - .boxed(), - )) - } - - /// Capture the relevant signals to handle shutdown of the node smoothly. - /// - /// Needs to be called in a Tokio context to have access to the tokio reactor. - #[cfg(not(unix))] - pub fn capture() -> std::result::Result { - use tokio::signal::ctrl_c; - - Ok(Signals( - async move { - let _ = ctrl_c().await; - } - .boxed(), - )) - } - - /// A dummy signal that never returns. - pub fn dummy() -> Self { - Self(future::pending().boxed()) - } -} - -async fn main(func: F, signals: impl Future) -> std::result::Result<(), E> -where - F: Future> + future::FusedFuture, - E: std::error::Error + Send + Sync + 'static, -{ - let signals = signals.fuse(); - - pin_mut!(func, signals); - - select! { - _ = signals => {}, - res = func => res?, - } - - Ok(()) -} - -/// Build a tokio runtime with all features +/// Build a tokio runtime with all features. pub fn build_runtime() -> std::result::Result { tokio::runtime::Builder::new_multi_thread() .on_thread_start(|| { @@ -103,25 +38,6 @@ pub fn build_runtime() -> std::result::Result( - tokio_runtime: tokio::runtime::Runtime, - future: F, - task_manager: TaskManager, - signals: impl Future, -) -> std::result::Result<(), E> -where - F: Future> + future::Future, - E: std::error::Error + Send + Sync + 'static + From, -{ - let f = future.fuse(); - pin_mut!(f); - - tokio_runtime.block_on(main(f, signals))?; - drop(task_manager); - - Ok(()) -} - /// A Substrate CLI runtime that can be used to run a node or a command pub struct Runner { config: Configuration, @@ -171,7 +87,10 @@ impl Runner { self.print_node_infos(); let mut task_manager = self.tokio_runtime.block_on(initialize(self.config))?; - let res = self.tokio_runtime.block_on(main(task_manager.future().fuse(), self.signals.0)); + + let res = self + .tokio_runtime + .block_on(self.signals.run_until_signal(task_manager.future().fuse())); // We need to drop the task manager here to inform all tasks that they should shut down. // // This is important to be done before we instruct the tokio runtime to shutdown. Otherwise @@ -234,7 +153,11 @@ impl Runner { E: std::error::Error + Send + Sync + 'static + From + From, { let (future, task_manager) = runner(self.config)?; - run_until_exit::<_, E>(self.tokio_runtime, future, task_manager, self.signals.0) + self.tokio_runtime.block_on(self.signals.run_until_signal(future.fuse()))?; + // Drop the task manager before dropping the rest, to ensure that all futures were informed + // about the shut down. + drop(task_manager); + Ok(()) } /// Get an immutable reference to the node Configuration diff --git a/client/cli/src/signals.rs b/client/cli/src/signals.rs new file mode 100644 index 000000000..4b6a6f957 --- /dev/null +++ b/client/cli/src/signals.rs @@ -0,0 +1,92 @@ +// This file is part of Substrate. + +// Copyright (C) Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +use futures::{ + future::{self, BoxFuture, FutureExt}, + pin_mut, select, Future, +}; + +use sc_service::Error as ServiceError; + +/// Abstraction over OS signals to handle the shutdown of the node smoothly. +/// +/// On `unix` this represents `SigInt` and `SigTerm`. +pub struct Signals(BoxFuture<'static, ()>); + +impl Signals { + /// Return the signals future. + pub fn future(self) -> BoxFuture<'static, ()> { + self.0 + } + + /// Capture the relevant signals to handle shutdown of the node smoothly. + /// + /// Needs to be called in a Tokio context to have access to the tokio reactor. + #[cfg(target_family = "unix")] + pub fn capture() -> std::result::Result { + use tokio::signal::unix::{signal, SignalKind}; + + let mut stream_int = signal(SignalKind::interrupt()).map_err(ServiceError::Io)?; + let mut stream_term = signal(SignalKind::terminate()).map_err(ServiceError::Io)?; + + Ok(Signals( + async move { + future::select(stream_int.recv().boxed(), stream_term.recv().boxed()).await; + } + .boxed(), + )) + } + + /// Capture the relevant signals to handle shutdown of the node smoothly. + /// + /// Needs to be called in a Tokio context to have access to the tokio reactor. + #[cfg(not(unix))] + pub fn capture() -> Result { + use tokio::signal::ctrl_c; + + Ok(Signals( + async move { + let _ = ctrl_c().await; + } + .boxed(), + )) + } + + /// A dummy signal that never returns. + pub fn dummy() -> Self { + Self(future::pending().boxed()) + } + + /// Run a future task until receive a signal. + pub async fn run_until_signal(self, func: F) -> Result<(), E> + where + F: Future> + future::FusedFuture, + E: std::error::Error + Send + Sync + 'static, + { + let signals = self.future().fuse(); + + pin_mut!(func, signals); + + select! { + _ = signals => {}, + res = func => res?, + } + + Ok(()) + } +} From 83c81985521b9f956890539d8b62371c40093c60 Mon Sep 17 00:00:00 2001 From: Mira Ressel Date: Mon, 27 Mar 2023 07:23:53 +0200 Subject: [PATCH 295/558] add a new ci job to fuzz sp-arithmetic (#13673) as requested in https://github.com/paritytech/ci_cd/issues/761 --- scripts/ci/gitlab/pipeline/test.yml | 36 +++++++++++++++++++++++++++++ 1 file changed, 36 insertions(+) diff --git a/scripts/ci/gitlab/pipeline/test.yml b/scripts/ci/gitlab/pipeline/test.yml index 7c5f43c29..b5c37d243 100644 --- a/scripts/ci/gitlab/pipeline/test.yml +++ b/scripts/ci/gitlab/pipeline/test.yml @@ -432,3 +432,39 @@ cargo-check-each-crate-macos: - time cargo check --workspace --locked tags: - osx + +cargo-hfuzz: + stage: test + extends: + - .docker-env + - .test-refs + - .pipeline-stopper-artifacts + variables: + # max 10s per iteration, 60s per file + HFUZZ_RUN_ARGS: > + --exit_upon_crash + --exit_code_upon_crash 1 + --timeout 10 + --run_time 60 + artifacts: + name: "hfuzz-$CI_COMMIT_SHORT_SHA" + expire_in: 7 days + when: on_failure + paths: + - primitives/arithmetic/fuzzer/hfuzz_workspace/ + script: + # use git version of honggfuzz-rs until v0.5.56 is out, we need a few recent changes: + # https://github.com/rust-fuzz/honggfuzz-rs/pull/75 to avoid breakage on debian + # https://github.com/rust-fuzz/honggfuzz-rs/pull/81 fix to the above pr + # https://github.com/rust-fuzz/honggfuzz-rs/pull/82 fix for handling rusty-cachier's absolute CARGO_TARGET_DIR + - | + cat >>Cargo.toml < Date: Mon, 27 Mar 2023 09:50:31 +0200 Subject: [PATCH 296/558] sc-slots: Forward block size limit (#13716) --- client/consensus/slots/src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client/consensus/slots/src/lib.rs b/client/consensus/slots/src/lib.rs index e41473407..5057e7858 100644 --- a/client/consensus/slots/src/lib.rs +++ b/client/consensus/slots/src/lib.rs @@ -217,7 +217,7 @@ pub trait SimpleSlotWorker { inherent_data, sp_runtime::generic::Digest { logs }, proposing_remaining_duration.mul_f32(0.98), - None, + slot_info.block_size_limit, ) .map_err(|e| sp_consensus::Error::ClientImport(e.to_string())); From bede15d5085fd5741e2522f85f36a7ec45cd9554 Mon Sep 17 00:00:00 2001 From: Gavin Wood Date: Mon, 27 Mar 2023 14:04:24 +0200 Subject: [PATCH 297/558] FRAME: Fix the Referenda confirming alarm (#13704) * Fix the Referenda confirming alarm * Add minimal regression test This fails on bf395c8308c481a9774373e0b0b14bd7a2e4b8d2 since the downwards rounding voids the curve delay. Signed-off-by: Oliver Tale-Yazdi --------- Signed-off-by: Oliver Tale-Yazdi Co-authored-by: Oliver Tale-Yazdi --- frame/referenda/src/lib.rs | 2 +- frame/referenda/src/tests.rs | 18 ++++++++++++++++++ 2 files changed, 19 insertions(+), 1 deletion(-) diff --git a/frame/referenda/src/lib.rs b/frame/referenda/src/lib.rs index f69985006..b96cf5a76 100644 --- a/frame/referenda/src/lib.rs +++ b/frame/referenda/src/lib.rs @@ -1206,7 +1206,7 @@ impl, I: 'static> Pallet { let until_approval = track.min_approval.delay(approval); let until_support = track.min_support.delay(support); let offset = until_support.max(until_approval); - deciding.since.saturating_add(offset * track.decision_period) + deciding.since.saturating_add(offset.mul_ceil(track.decision_period)) }) } diff --git a/frame/referenda/src/tests.rs b/frame/referenda/src/tests.rs index f728350c3..0a1561d00 100644 --- a/frame/referenda/src/tests.rs +++ b/frame/referenda/src/tests.rs @@ -286,6 +286,24 @@ fn alarm_interval_works() { }); } +#[test] +fn decision_time_is_correct() { + new_test_ext().execute_with(|| { + let decision_time = |since: u64| { + Pallet::::decision_time( + &DecidingStatus { since: since.into(), confirming: None }, + &Tally { ayes: 100, nays: 5 }, + TestTracksInfo::tracks()[0].0, + &TestTracksInfo::tracks()[0].1, + ) + }; + + for i in 0u64..=100 { + assert!(decision_time(i) > i, "The decision time should be delayed by the curve"); + } + }); +} + #[test] fn auto_timeout_should_happen_with_nothing_but_submit() { new_test_ext().execute_with(|| { From 8f0414c761486eb6cea2a091ebdb3d3efbdebb9b Mon Sep 17 00:00:00 2001 From: Aaron Bassett Date: Mon, 27 Mar 2023 16:03:04 +0200 Subject: [PATCH 298/558] Update pallet licenses to Apache-2.0 (#13467) --- frame/fast-unstake/Cargo.toml | 2 +- frame/scheduler/README.md | 2 +- frame/transaction-storage/Cargo.toml | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/frame/fast-unstake/Cargo.toml b/frame/fast-unstake/Cargo.toml index 36b4c3a5e..8e4df2f2b 100644 --- a/frame/fast-unstake/Cargo.toml +++ b/frame/fast-unstake/Cargo.toml @@ -3,7 +3,7 @@ name = "pallet-fast-unstake" version = "4.0.0-dev" authors = ["Parity Technologies "] edition = "2021" -license = "Unlicense" +license = "Apache-2.0" homepage = "https://substrate.io" repository = "https://github.com/paritytech/substrate/" description = "FRAME fast unstake pallet" diff --git a/frame/scheduler/README.md b/frame/scheduler/README.md index 9a209031d..bdd2c2226 100644 --- a/frame/scheduler/README.md +++ b/frame/scheduler/README.md @@ -31,4 +31,4 @@ then those filter will not be used when dispatching the schedule call. `Vec` parameter that can be used for identification. * `cancel_named` - the named complement to the cancel function. -License: Unlicense +License: Apache 2.0 diff --git a/frame/transaction-storage/Cargo.toml b/frame/transaction-storage/Cargo.toml index 527ff4f24..c23be8194 100644 --- a/frame/transaction-storage/Cargo.toml +++ b/frame/transaction-storage/Cargo.toml @@ -3,7 +3,7 @@ name = "pallet-transaction-storage" version = "4.0.0-dev" authors = ["Parity Technologies "] edition = "2021" -license = "Unlicense" +license = "Apache-2.0" homepage = "https://substrate.io" repository = "https://github.com/paritytech/substrate/" description = "Storage chain pallet" From 46bdb43422007619c4efa4b03e32ad7b2f9b282e Mon Sep 17 00:00:00 2001 From: Ross Bulat Date: Mon, 27 Mar 2023 16:02:03 +0100 Subject: [PATCH 299/558] Reward pool migration fix (#13715) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * reward pool migration fix * comment * remove generic * rm max * foramtting * Add note to V4 migration Signed-off-by: Oliver Tale-Yazdi * Add more asserts to the V5 migration Signed-off-by: Oliver Tale-Yazdi * Make compile Signed-off-by: Oliver Tale-Yazdi * Update frame/nomination-pools/src/migration.rs Co-authored-by: Gonçalo Pestana * Make V4 migration re-usable Otherwise it wont chain together with the V5. Signed-off-by: Oliver Tale-Yazdi * Add MigrateV3ToV5 wrapper Signed-off-by: Oliver Tale-Yazdi --------- Signed-off-by: Oliver Tale-Yazdi Co-authored-by: Oliver Tale-Yazdi Co-authored-by: Gonçalo Pestana --- frame/nomination-pools/src/lib.rs | 2 +- frame/nomination-pools/src/migration.rs | 162 +++++++++++++++++++++++- 2 files changed, 161 insertions(+), 3 deletions(-) diff --git a/frame/nomination-pools/src/lib.rs b/frame/nomination-pools/src/lib.rs index e73d4b417..cfde05ffe 100644 --- a/frame/nomination-pools/src/lib.rs +++ b/frame/nomination-pools/src/lib.rs @@ -1496,7 +1496,7 @@ pub mod pallet { use sp_runtime::Perbill; /// The current storage version. - const STORAGE_VERSION: StorageVersion = StorageVersion::new(4); + const STORAGE_VERSION: StorageVersion = StorageVersion::new(5); #[pallet::pallet] #[pallet::storage_version(STORAGE_VERSION)] diff --git a/frame/nomination-pools/src/migration.rs b/frame/nomination-pools/src/migration.rs index f6f166d7f..45d642411 100644 --- a/frame/nomination-pools/src/migration.rs +++ b/frame/nomination-pools/src/migration.rs @@ -478,9 +478,22 @@ pub mod v4 { } } + /// Migrates from `v3` directly to `v5` to avoid the broken `v4` migration. + #[allow(deprecated)] + pub type MigrateV3ToV5 = (v4::MigrateToV4, v5::MigrateToV5); + + /// # Warning + /// + /// To avoid mangled storage please use `MigrateV3ToV5` instead. + /// See: github.com/paritytech/substrate/pull/13715 + /// /// This migration adds a `commission` field to every `BondedPoolInner`, if /// any. + #[deprecated( + note = "To avoid mangled storage please use `MigrateV3ToV5` instead. See: github.com/paritytech/substrate/pull/13715" + )] pub struct MigrateToV4(sp_std::marker::PhantomData<(T, U)>); + #[allow(deprecated)] impl> OnRuntimeUpgrade for MigrateToV4 { fn on_runtime_upgrade() -> Weight { let current = Pallet::::current_storage_version(); @@ -493,7 +506,8 @@ pub mod v4 { onchain ); - if current == 4 && onchain == 3 { + if onchain == 3 { + log!(warn, "Please run MigrateToV5 immediately after this migration. See github.com/paritytech/substrate/pull/13715"); let initial_global_max_commission = U::get(); GlobalMaxCommission::::set(Some(initial_global_max_commission)); log!( @@ -508,7 +522,7 @@ pub mod v4 { Some(old_value.migrate_to_v4()) }); - current.put::>(); + StorageVersion::new(4).put::>(); log!(info, "Upgraded {} pools, storage to version {:?}", translated, current); // reads: translated + onchain version. @@ -548,3 +562,147 @@ pub mod v4 { } } } + +pub mod v5 { + use super::*; + + #[derive(Decode)] + pub struct OldRewardPool { + last_recorded_reward_counter: T::RewardCounter, + last_recorded_total_payouts: BalanceOf, + total_rewards_claimed: BalanceOf, + } + + impl OldRewardPool { + fn migrate_to_v5(self) -> RewardPool { + RewardPool { + last_recorded_reward_counter: self.last_recorded_reward_counter, + last_recorded_total_payouts: self.last_recorded_total_payouts, + total_rewards_claimed: self.total_rewards_claimed, + total_commission_pending: Zero::zero(), + total_commission_claimed: Zero::zero(), + } + } + } + + /// This migration adds `total_commission_pending` and `total_commission_claimed` field to every + /// `RewardPool`, if any. + pub struct MigrateToV5(sp_std::marker::PhantomData); + impl OnRuntimeUpgrade for MigrateToV5 { + fn on_runtime_upgrade() -> Weight { + let current = Pallet::::current_storage_version(); + let onchain = Pallet::::on_chain_storage_version(); + + log!( + info, + "Running migration with current storage version {:?} / onchain {:?}", + current, + onchain + ); + + if current == 5 && onchain == 4 { + let mut translated = 0u64; + RewardPools::::translate::, _>(|_id, old_value| { + translated.saturating_inc(); + Some(old_value.migrate_to_v5()) + }); + + current.put::>(); + log!(info, "Upgraded {} pools, storage to version {:?}", translated, current); + + // reads: translated + onchain version. + // writes: translated + current.put. + T::DbWeight::get().reads_writes(translated + 1, translated + 1) + } else { + log!(info, "Migration did not execute. This probably should be removed"); + T::DbWeight::get().reads(1) + } + } + + #[cfg(feature = "try-runtime")] + fn pre_upgrade() -> Result, &'static str> { + ensure!( + Pallet::::current_storage_version() > Pallet::::on_chain_storage_version(), + "the on_chain version is equal or more than the current one" + ); + + let rpool_keys = RewardPools::::iter_keys().count(); + let rpool_values = RewardPools::::iter_values().count(); + if rpool_keys != rpool_values { + log!(info, "🔥 There are {} undecodable RewardPools in storage. This migration will try to correct them. keys: {}, values: {}", rpool_keys.saturating_sub(rpool_values), rpool_keys, rpool_values); + } + + ensure!( + PoolMembers::::iter_keys().count() == PoolMembers::::iter_values().count(), + "There are undecodable PoolMembers in storage. This migration will not fix that." + ); + ensure!( + BondedPools::::iter_keys().count() == BondedPools::::iter_values().count(), + "There are undecodable BondedPools in storage. This migration will not fix that." + ); + ensure!( + SubPoolsStorage::::iter_keys().count() == + SubPoolsStorage::::iter_values().count(), + "There are undecodable SubPools in storage. This migration will not fix that." + ); + ensure!( + Metadata::::iter_keys().count() == Metadata::::iter_values().count(), + "There are undecodable Metadata in storage. This migration will not fix that." + ); + + Ok((rpool_values as u64).encode()) + } + + #[cfg(feature = "try-runtime")] + fn post_upgrade(data: Vec) -> Result<(), &'static str> { + let old_rpool_values: u64 = Decode::decode(&mut &data[..]).unwrap(); + let rpool_keys = RewardPools::::iter_keys().count() as u64; + let rpool_values = RewardPools::::iter_values().count() as u64; + ensure!( + rpool_keys == rpool_values, + "There are STILL undecodable RewardPools - migration failed" + ); + + if old_rpool_values != rpool_values { + log!( + info, + "🎉 Fixed {} undecodable RewardPools.", + rpool_values.saturating_sub(old_rpool_values) + ); + } + + // ensure all RewardPools items now contain `total_commission_pending` and + // `total_commission_claimed` field. + ensure!( + RewardPools::::iter().all(|(_, reward_pool)| reward_pool + .total_commission_pending + .is_zero() && reward_pool + .total_commission_claimed + .is_zero()), + "a commission value has been incorrectly set" + ); + ensure!(Pallet::::on_chain_storage_version() == 5, "wrong storage version"); + + // These should not have been touched - just in case. + ensure!( + PoolMembers::::iter_keys().count() == PoolMembers::::iter_values().count(), + "There are undecodable PoolMembers in storage." + ); + ensure!( + BondedPools::::iter_keys().count() == BondedPools::::iter_values().count(), + "There are undecodable BondedPools in storage." + ); + ensure!( + SubPoolsStorage::::iter_keys().count() == + SubPoolsStorage::::iter_values().count(), + "There are undecodable SubPools in storage." + ); + ensure!( + Metadata::::iter_keys().count() == Metadata::::iter_values().count(), + "There are undecodable Metadata in storage." + ); + + Ok(()) + } + } +} From f2c12fd68a0d225864baec488136a2334070d712 Mon Sep 17 00:00:00 2001 From: PG Herveou Date: Mon, 27 Mar 2023 19:33:59 +0200 Subject: [PATCH 300/558] contracts: Add host function tracing (#13648) --- frame/contracts/README.md | 12 + frame/contracts/proc-macro/src/lib.rs | 36 +- frame/contracts/src/wasm/runtime.rs | 1 + frame/contracts/src/weights.rs | 1818 +++++++++++++------------ 4 files changed, 959 insertions(+), 908 deletions(-) diff --git a/frame/contracts/README.md b/frame/contracts/README.md index 9b1d3f7d6..4f5f10a3a 100644 --- a/frame/contracts/README.md +++ b/frame/contracts/README.md @@ -135,6 +135,18 @@ to `error` in order to prevent them from spamming the console. `--dev`: Use a dev chain spec `--tmp`: Use temporary storage for chain data (the chain state is deleted on exit) +## Host function tracing + +For contract authors, it can be a helpful debugging tool to see which host functions are called, with which arguments, and what the result was. + +In order to see these messages on the node console, the log level for the `runtime::contracts::strace` target needs to be raised to the `trace` level. + +Example: + +```bash +cargo run --release -- --dev -lerror,runtime::contracts::strace=trace,runtime::contracts=debug +``` + ## Unstable Interfaces Driven by the desire to have an iterative approach in developing new contract interfaces diff --git a/frame/contracts/proc-macro/src/lib.rs b/frame/contracts/proc-macro/src/lib.rs index 32241068f..d54470dd0 100644 --- a/frame/contracts/proc-macro/src/lib.rs +++ b/frame/contracts/proc-macro/src/lib.rs @@ -596,6 +596,7 @@ fn expand_functions(def: &EnvDef, expand_blocks: bool, host_state: TokenStream2) let impls = def.host_funcs.iter().map(|f| { // skip the context and memory argument let params = f.item.sig.inputs.iter().skip(2); + let (module, name, body, wasm_output, output) = ( f.module(), &f.name, @@ -606,6 +607,39 @@ fn expand_functions(def: &EnvDef, expand_blocks: bool, host_state: TokenStream2) let is_stable = f.is_stable; let not_deprecated = f.not_deprecated; + // wrapped host function body call with host function traces + // see https://github.com/paritytech/substrate/tree/master/frame/contracts#host-function-tracing + let wrapped_body_with_trace = { + let trace_fmt_args = params.clone().filter_map(|arg| match arg { + syn::FnArg::Receiver(_) => None, + syn::FnArg::Typed(p) => { + match *p.pat.clone() { + syn::Pat::Ident(ref pat_ident) => Some(pat_ident.ident.clone()), + _ => None, + } + }, + }); + + let params_fmt_str = trace_fmt_args.clone().map(|s| format!("{s}: {{:?}}")).collect::>().join(", "); + let trace_fmt_str = format!("{}::{}({}) = {{:?}}\n", module, name, params_fmt_str); + + quote! { + if ::log::log_enabled!(target: "runtime::contracts::strace", ::log::Level::Trace) { + let result = #body; + { + use sp_std::fmt::Write; + let mut w = sp_std::Writer::default(); + let _ = core::write!(&mut w, #trace_fmt_str, #( #trace_fmt_args, )* result); + let msg = core::str::from_utf8(&w.inner()).unwrap_or_default(); + ctx.ext().append_debug_buffer(msg); + } + result + } else { + #body + } + } + }; + // If we don't expand blocks (implementing for `()`) we change a few things: // - We replace any code by unreachable! // - Allow unused variables as the code that uses is not expanded @@ -617,7 +651,7 @@ fn expand_functions(def: &EnvDef, expand_blocks: bool, host_state: TokenStream2) .memory() .expect("Memory must be set when setting up host data; qed") .data_and_store_mut(&mut __caller__); - #body + #wrapped_body_with_trace } } } else { quote! { || -> #wasm_output { diff --git a/frame/contracts/src/wasm/runtime.rs b/frame/contracts/src/wasm/runtime.rs index 43a029ead..88b5b87dd 100644 --- a/frame/contracts/src/wasm/runtime.rs +++ b/frame/contracts/src/wasm/runtime.rs @@ -84,6 +84,7 @@ enum KeyType { /// will not be changed or removed. This means that any contract **must not** exhaustively /// match return codes. Instead, contracts should prepare for unknown variants and deal with /// those errors gracefully in order to be forward compatible. +#[derive(Debug)] #[repr(u32)] pub enum ReturnCode { /// API call successful. diff --git a/frame/contracts/src/weights.rs b/frame/contracts/src/weights.rs index dfe6fb20d..9e37a238b 100644 --- a/frame/contracts/src/weights.rs +++ b/frame/contracts/src/weights.rs @@ -18,7 +18,7 @@ //! Autogenerated weights for pallet_contracts //! //! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev -//! DATE: 2023-03-21, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! DATE: 2023-03-23, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` //! WORST CASE MAP SIZE: `1000000` //! HOSTNAME: `bm3`, CPU: `Intel(R) Core(TM) i7-7700K CPU @ 4.20GHz` //! EXECUTION: Some(Wasm), WASM-EXECUTION: Compiled, CHAIN: Some("dev"), DB CACHE: 1024 @@ -177,8 +177,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `109` // Estimated: `1594` - // Minimum execution time: 2_585_000 picoseconds. - Weight::from_parts(2_802_000, 1594) + // Minimum execution time: 2_713_000 picoseconds. + Weight::from_parts(2_811_000, 1594) .saturating_add(T::DbWeight::get().reads(1_u64)) } /// Storage: Skipped Metadata (r:0 w:0) @@ -188,10 +188,10 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `450 + k * (69 ±0)` // Estimated: `440 + k * (70 ±0)` - // Minimum execution time: 10_825_000 picoseconds. - Weight::from_parts(5_747_064, 440) - // Standard Error: 1_037 - .saturating_add(Weight::from_parts(973_689, 0).saturating_mul(k.into())) + // Minimum execution time: 11_011_000 picoseconds. + Weight::from_parts(7_025_244, 440) + // Standard Error: 1_217 + .saturating_add(Weight::from_parts(980_818, 0).saturating_mul(k.into())) .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(T::DbWeight::get().reads((1_u64).saturating_mul(k.into()))) .saturating_add(T::DbWeight::get().writes(1_u64)) @@ -205,10 +205,10 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `250 + q * (33 ±0)` // Estimated: `1725 + q * (33 ±0)` - // Minimum execution time: 2_718_000 picoseconds. - Weight::from_parts(11_436_305, 1725) - // Standard Error: 3_619 - .saturating_add(Weight::from_parts(1_296_955, 0).saturating_mul(q.into())) + // Minimum execution time: 2_802_000 picoseconds. + Weight::from_parts(10_768_336, 1725) + // Standard Error: 3_424 + .saturating_add(Weight::from_parts(1_323_649, 0).saturating_mul(q.into())) .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) .saturating_add(Weight::from_parts(0, 33).saturating_mul(q.into())) @@ -222,10 +222,10 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `238 + c * (1 ±0)` // Estimated: `3951 + c * (2 ±0)` - // Minimum execution time: 37_882_000 picoseconds. - Weight::from_parts(43_548_816, 3951) - // Standard Error: 49 - .saturating_add(Weight::from_parts(53_936, 0).saturating_mul(c.into())) + // Minimum execution time: 30_299_000 picoseconds. + Weight::from_parts(24_608_986, 3951) + // Standard Error: 75 + .saturating_add(Weight::from_parts(54_619, 0).saturating_mul(c.into())) .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) .saturating_add(Weight::from_parts(0, 2).saturating_mul(c.into())) @@ -245,10 +245,10 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `707` // Estimated: `21400 + c * (5 ±0)` - // Minimum execution time: 313_392_000 picoseconds. - Weight::from_parts(325_419_093, 21400) - // Standard Error: 25 - .saturating_add(Weight::from_parts(37_877, 0).saturating_mul(c.into())) + // Minimum execution time: 265_835_000 picoseconds. + Weight::from_parts(275_985_164, 21400) + // Standard Error: 36 + .saturating_add(Weight::from_parts(37_980, 0).saturating_mul(c.into())) .saturating_add(T::DbWeight::get().reads(6_u64)) .saturating_add(T::DbWeight::get().writes(4_u64)) .saturating_add(Weight::from_parts(0, 5).saturating_mul(c.into())) @@ -276,14 +276,14 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `270` // Estimated: `26207` - // Minimum execution time: 3_274_352_000 picoseconds. - Weight::from_parts(681_171_416, 26207) - // Standard Error: 336 - .saturating_add(Weight::from_parts(106_391, 0).saturating_mul(c.into())) - // Standard Error: 19 - .saturating_add(Weight::from_parts(1_147, 0).saturating_mul(i.into())) - // Standard Error: 19 - .saturating_add(Weight::from_parts(1_493, 0).saturating_mul(s.into())) + // Minimum execution time: 3_124_508_000 picoseconds. + Weight::from_parts(617_467_897, 26207) + // Standard Error: 293 + .saturating_add(Weight::from_parts(106_971, 0).saturating_mul(c.into())) + // Standard Error: 17 + .saturating_add(Weight::from_parts(1_156, 0).saturating_mul(i.into())) + // Standard Error: 17 + .saturating_add(Weight::from_parts(1_395, 0).saturating_mul(s.into())) .saturating_add(T::DbWeight::get().reads(9_u64)) .saturating_add(T::DbWeight::get().writes(10_u64)) } @@ -307,12 +307,12 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `482` // Estimated: `28521` - // Minimum execution time: 1_692_637_000 picoseconds. - Weight::from_parts(283_252_265, 28521) + // Minimum execution time: 1_649_483_000 picoseconds. + Weight::from_parts(287_642_416, 28521) // Standard Error: 8 - .saturating_add(Weight::from_parts(1_498, 0).saturating_mul(i.into())) + .saturating_add(Weight::from_parts(1_450, 0).saturating_mul(i.into())) // Standard Error: 8 - .saturating_add(Weight::from_parts(1_487, 0).saturating_mul(s.into())) + .saturating_add(Weight::from_parts(1_443, 0).saturating_mul(s.into())) .saturating_add(T::DbWeight::get().reads(9_u64)) .saturating_add(T::DbWeight::get().writes(7_u64)) } @@ -330,8 +330,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `759` // Estimated: `21615` - // Minimum execution time: 192_369_000 picoseconds. - Weight::from_parts(193_417_000, 21615) + // Minimum execution time: 192_302_000 picoseconds. + Weight::from_parts(193_192_000, 21615) .saturating_add(T::DbWeight::get().reads(6_u64)) .saturating_add(T::DbWeight::get().writes(4_u64)) } @@ -348,10 +348,10 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `109` // Estimated: `7366` - // Minimum execution time: 301_972_000 picoseconds. - Weight::from_parts(313_248_051, 7366) - // Standard Error: 82 - .saturating_add(Weight::from_parts(107_321, 0).saturating_mul(c.into())) + // Minimum execution time: 246_401_000 picoseconds. + Weight::from_parts(261_505_456, 7366) + // Standard Error: 83 + .saturating_add(Weight::from_parts(109_136, 0).saturating_mul(c.into())) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().writes(4_u64)) } @@ -367,8 +367,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `255` // Estimated: `7950` - // Minimum execution time: 33_013_000 picoseconds. - Weight::from_parts(33_622_000, 7950) + // Minimum execution time: 33_913_000 picoseconds. + Weight::from_parts(34_186_000, 7950) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().writes(4_u64)) } @@ -382,8 +382,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `570` // Estimated: `19530` - // Minimum execution time: 33_518_000 picoseconds. - Weight::from_parts(33_819_000, 19530) + // Minimum execution time: 33_801_000 picoseconds. + Weight::from_parts(34_877_000, 19530) .saturating_add(T::DbWeight::get().reads(6_u64)) .saturating_add(T::DbWeight::get().writes(6_u64)) } @@ -402,10 +402,10 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `781 + r * (6 ±0)` // Estimated: `21730 + r * (30 ±0)` - // Minimum execution time: 283_945_000 picoseconds. - Weight::from_parts(289_209_599, 21730) - // Standard Error: 901 - .saturating_add(Weight::from_parts(327_601, 0).saturating_mul(r.into())) + // Minimum execution time: 237_679_000 picoseconds. + Weight::from_parts(243_022_905, 21730) + // Standard Error: 940 + .saturating_add(Weight::from_parts(324_389, 0).saturating_mul(r.into())) .saturating_add(T::DbWeight::get().reads(6_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) .saturating_add(Weight::from_parts(0, 30).saturating_mul(r.into())) @@ -425,10 +425,10 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `839 + r * (240 ±0)` // Estimated: `21835 + r * (3675 ±0)` - // Minimum execution time: 284_949_000 picoseconds. - Weight::from_parts(126_196_485, 21835) - // Standard Error: 6_260 - .saturating_add(Weight::from_parts(3_368_849, 0).saturating_mul(r.into())) + // Minimum execution time: 235_635_000 picoseconds. + Weight::from_parts(76_942_144, 21835) + // Standard Error: 6_214 + .saturating_add(Weight::from_parts(3_328_756, 0).saturating_mul(r.into())) .saturating_add(T::DbWeight::get().reads(6_u64)) .saturating_add(T::DbWeight::get().reads((1_u64).saturating_mul(r.into()))) .saturating_add(T::DbWeight::get().writes(3_u64)) @@ -449,10 +449,10 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `831 + r * (244 ±0)` // Estimated: `21855 + r * (3695 ±0)` - // Minimum execution time: 286_429_000 picoseconds. - Weight::from_parts(124_820_396, 21855) - // Standard Error: 6_539 - .saturating_add(Weight::from_parts(4_163_535, 0).saturating_mul(r.into())) + // Minimum execution time: 237_123_000 picoseconds. + Weight::from_parts(77_880_739, 21855) + // Standard Error: 5_970 + .saturating_add(Weight::from_parts(4_103_281, 0).saturating_mul(r.into())) .saturating_add(T::DbWeight::get().reads(6_u64)) .saturating_add(T::DbWeight::get().reads((1_u64).saturating_mul(r.into()))) .saturating_add(T::DbWeight::get().writes(3_u64)) @@ -473,10 +473,10 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `788 + r * (6 ±0)` // Estimated: `21770 + r * (30 ±0)` - // Minimum execution time: 285_042_000 picoseconds. - Weight::from_parts(288_096_303, 21770) - // Standard Error: 972 - .saturating_add(Weight::from_parts(409_782, 0).saturating_mul(r.into())) + // Minimum execution time: 236_621_000 picoseconds. + Weight::from_parts(238_240_015, 21770) + // Standard Error: 742 + .saturating_add(Weight::from_parts(404_691, 0).saturating_mul(r.into())) .saturating_add(T::DbWeight::get().reads(6_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) .saturating_add(Weight::from_parts(0, 30).saturating_mul(r.into())) @@ -496,10 +496,10 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `778 + r * (3 ±0)` // Estimated: `21735 + r * (15 ±0)` - // Minimum execution time: 282_820_000 picoseconds. - Weight::from_parts(287_104_710, 21735) - // Standard Error: 421 - .saturating_add(Weight::from_parts(167_907, 0).saturating_mul(r.into())) + // Minimum execution time: 233_274_000 picoseconds. + Weight::from_parts(239_596_227, 21735) + // Standard Error: 551 + .saturating_add(Weight::from_parts(164_429, 0).saturating_mul(r.into())) .saturating_add(T::DbWeight::get().reads(6_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) .saturating_add(Weight::from_parts(0, 15).saturating_mul(r.into())) @@ -519,10 +519,10 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `782 + r * (6 ±0)` // Estimated: `21740 + r * (30 ±0)` - // Minimum execution time: 284_619_000 picoseconds. - Weight::from_parts(281_326_785, 21740) - // Standard Error: 1_379 - .saturating_add(Weight::from_parts(342_779, 0).saturating_mul(r.into())) + // Minimum execution time: 235_661_000 picoseconds. + Weight::from_parts(239_063_406, 21740) + // Standard Error: 933 + .saturating_add(Weight::from_parts(327_679, 0).saturating_mul(r.into())) .saturating_add(T::DbWeight::get().reads(6_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) .saturating_add(Weight::from_parts(0, 30).saturating_mul(r.into())) @@ -542,10 +542,10 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `783 + r * (6 ±0)` // Estimated: `21725 + r * (30 ±0)` - // Minimum execution time: 284_703_000 picoseconds. - Weight::from_parts(289_479_932, 21725) - // Standard Error: 745 - .saturating_add(Weight::from_parts(323_625, 0).saturating_mul(r.into())) + // Minimum execution time: 235_583_000 picoseconds. + Weight::from_parts(251_641_549, 21725) + // Standard Error: 1_104 + .saturating_add(Weight::from_parts(315_873, 0).saturating_mul(r.into())) .saturating_add(T::DbWeight::get().reads(6_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) .saturating_add(Weight::from_parts(0, 30).saturating_mul(r.into())) @@ -565,10 +565,10 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `922 + r * (6 ±0)` // Estimated: `24633 + r * (30 ±0)` - // Minimum execution time: 283_610_000 picoseconds. - Weight::from_parts(299_901_534, 24633) - // Standard Error: 1_177 - .saturating_add(Weight::from_parts(1_474_603, 0).saturating_mul(r.into())) + // Minimum execution time: 235_325_000 picoseconds. + Weight::from_parts(256_582_010, 24633) + // Standard Error: 1_349 + .saturating_add(Weight::from_parts(1_483_116, 0).saturating_mul(r.into())) .saturating_add(T::DbWeight::get().reads(7_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) .saturating_add(Weight::from_parts(0, 30).saturating_mul(r.into())) @@ -588,10 +588,10 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `792 + r * (6 ±0)` // Estimated: `21825 + r * (30 ±0)` - // Minimum execution time: 284_474_000 picoseconds. - Weight::from_parts(283_540_273, 21825) - // Standard Error: 1_164 - .saturating_add(Weight::from_parts(339_262, 0).saturating_mul(r.into())) + // Minimum execution time: 235_358_000 picoseconds. + Weight::from_parts(233_421_484, 21825) + // Standard Error: 1_178 + .saturating_add(Weight::from_parts(337_947, 0).saturating_mul(r.into())) .saturating_add(T::DbWeight::get().reads(6_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) .saturating_add(Weight::from_parts(0, 30).saturating_mul(r.into())) @@ -611,10 +611,10 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `790 + r * (6 ±0)` // Estimated: `21815 + r * (30 ±0)` - // Minimum execution time: 284_521_000 picoseconds. - Weight::from_parts(285_747_754, 21815) - // Standard Error: 889 - .saturating_add(Weight::from_parts(326_428, 0).saturating_mul(r.into())) + // Minimum execution time: 236_570_000 picoseconds. + Weight::from_parts(245_853_078, 21815) + // Standard Error: 1_947 + .saturating_add(Weight::from_parts(319_972, 0).saturating_mul(r.into())) .saturating_add(T::DbWeight::get().reads(6_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) .saturating_add(Weight::from_parts(0, 30).saturating_mul(r.into())) @@ -634,10 +634,10 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `787 + r * (6 ±0)` // Estimated: `21805 + r * (30 ±0)` - // Minimum execution time: 284_103_000 picoseconds. - Weight::from_parts(283_801_256, 21805) - // Standard Error: 1_051 - .saturating_add(Weight::from_parts(334_081, 0).saturating_mul(r.into())) + // Minimum execution time: 235_027_000 picoseconds. + Weight::from_parts(239_719_689, 21805) + // Standard Error: 654 + .saturating_add(Weight::from_parts(326_988, 0).saturating_mul(r.into())) .saturating_add(T::DbWeight::get().reads(6_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) .saturating_add(Weight::from_parts(0, 30).saturating_mul(r.into())) @@ -657,10 +657,10 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `778 + r * (6 ±0)` // Estimated: `21735 + r * (30 ±0)` - // Minimum execution time: 284_187_000 picoseconds. - Weight::from_parts(289_414_364, 21735) - // Standard Error: 796 - .saturating_add(Weight::from_parts(324_603, 0).saturating_mul(r.into())) + // Minimum execution time: 236_547_000 picoseconds. + Weight::from_parts(239_390_326, 21735) + // Standard Error: 912 + .saturating_add(Weight::from_parts(327_495, 0).saturating_mul(r.into())) .saturating_add(T::DbWeight::get().reads(6_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) .saturating_add(Weight::from_parts(0, 30).saturating_mul(r.into())) @@ -682,10 +682,10 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `856 + r * (10 ±0)` // Estimated: `24446 + r * (60 ±0)` - // Minimum execution time: 284_953_000 picoseconds. - Weight::from_parts(290_535_752, 24446) - // Standard Error: 2_462 - .saturating_add(Weight::from_parts(1_361_518, 0).saturating_mul(r.into())) + // Minimum execution time: 235_710_000 picoseconds. + Weight::from_parts(238_998_789, 24446) + // Standard Error: 2_055 + .saturating_add(Weight::from_parts(1_373_992, 0).saturating_mul(r.into())) .saturating_add(T::DbWeight::get().reads(7_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) .saturating_add(Weight::from_parts(0, 60).saturating_mul(r.into())) @@ -705,10 +705,10 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `745 + r * (4 ±0)` // Estimated: `21555 + r * (20 ±0)` - // Minimum execution time: 160_775_000 picoseconds. - Weight::from_parts(164_652_364, 21555) - // Standard Error: 284 - .saturating_add(Weight::from_parts(132_574, 0).saturating_mul(r.into())) + // Minimum execution time: 161_133_000 picoseconds. + Weight::from_parts(167_097_346, 21555) + // Standard Error: 245 + .saturating_add(Weight::from_parts(128_503, 0).saturating_mul(r.into())) .saturating_add(T::DbWeight::get().reads(6_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) .saturating_add(Weight::from_parts(0, 20).saturating_mul(r.into())) @@ -728,10 +728,10 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `780 + r * (6 ±0)` // Estimated: `21740 + r * (30 ±0)` - // Minimum execution time: 284_072_000 picoseconds. - Weight::from_parts(288_418_644, 21740) - // Standard Error: 792 - .saturating_add(Weight::from_parts(272_881, 0).saturating_mul(r.into())) + // Minimum execution time: 234_790_000 picoseconds. + Weight::from_parts(242_392_710, 21740) + // Standard Error: 2_506 + .saturating_add(Weight::from_parts(273_470, 0).saturating_mul(r.into())) .saturating_add(T::DbWeight::get().reads(6_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) .saturating_add(Weight::from_parts(0, 30).saturating_mul(r.into())) @@ -751,10 +751,10 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `784` // Estimated: `21740` - // Minimum execution time: 286_671_000 picoseconds. - Weight::from_parts(292_151_662, 21740) - // Standard Error: 1 - .saturating_add(Weight::from_parts(638, 0).saturating_mul(n.into())) + // Minimum execution time: 236_837_000 picoseconds. + Weight::from_parts(237_860_073, 21740) + // Standard Error: 2 + .saturating_add(Weight::from_parts(602, 0).saturating_mul(n.into())) .saturating_add(T::DbWeight::get().reads(6_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) } @@ -773,10 +773,10 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `768 + r * (45 ±0)` // Estimated: `21660 + r * (225 ±0)` - // Minimum execution time: 280_334_000 picoseconds. - Weight::from_parts(283_487_571, 21660) - // Standard Error: 267_797 - .saturating_add(Weight::from_parts(3_803_128, 0).saturating_mul(r.into())) + // Minimum execution time: 232_047_000 picoseconds. + Weight::from_parts(234_629_293, 21660) + // Standard Error: 171_808 + .saturating_add(Weight::from_parts(663_306, 0).saturating_mul(r.into())) .saturating_add(T::DbWeight::get().reads(6_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) .saturating_add(Weight::from_parts(0, 225).saturating_mul(r.into())) @@ -796,10 +796,10 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `778` // Estimated: `21775` - // Minimum execution time: 284_004_000 picoseconds. - Weight::from_parts(283_681_350, 21775) + // Minimum execution time: 235_035_000 picoseconds. + Weight::from_parts(234_442_091, 21775) // Standard Error: 1 - .saturating_add(Weight::from_parts(232, 0).saturating_mul(n.into())) + .saturating_add(Weight::from_parts(185, 0).saturating_mul(n.into())) .saturating_add(T::DbWeight::get().reads(6_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) } @@ -822,10 +822,10 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `810 + r * (356 ±0)` // Estimated: `25511 + r * (15321 ±0)` - // Minimum execution time: 284_143_000 picoseconds. - Weight::from_parts(287_218_324, 25511) - // Standard Error: 343_611 - .saturating_add(Weight::from_parts(109_895_675, 0).saturating_mul(r.into())) + // Minimum execution time: 234_140_000 picoseconds. + Weight::from_parts(236_805_906, 25511) + // Standard Error: 435_181 + .saturating_add(Weight::from_parts(118_144_693, 0).saturating_mul(r.into())) .saturating_add(T::DbWeight::get().reads(6_u64)) .saturating_add(T::DbWeight::get().reads((6_u64).saturating_mul(r.into()))) .saturating_add(T::DbWeight::get().writes(3_u64)) @@ -849,10 +849,10 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `825 + r * (10 ±0)` // Estimated: `24283 + r * (60 ±0)` - // Minimum execution time: 285_037_000 picoseconds. - Weight::from_parts(299_804_606, 24283) - // Standard Error: 5_518 - .saturating_add(Weight::from_parts(1_848_164, 0).saturating_mul(r.into())) + // Minimum execution time: 235_271_000 picoseconds. + Weight::from_parts(256_019_682, 24283) + // Standard Error: 2_016 + .saturating_add(Weight::from_parts(1_862_085, 0).saturating_mul(r.into())) .saturating_add(T::DbWeight::get().reads(7_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) .saturating_add(Weight::from_parts(0, 60).saturating_mul(r.into())) @@ -872,10 +872,10 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `778 + r * (10 ±0)` // Estimated: `21735 + r * (50 ±0)` - // Minimum execution time: 282_886_000 picoseconds. - Weight::from_parts(293_171_736, 21735) - // Standard Error: 2_171 - .saturating_add(Weight::from_parts(3_491_303, 0).saturating_mul(r.into())) + // Minimum execution time: 233_092_000 picoseconds. + Weight::from_parts(248_483_473, 21735) + // Standard Error: 2_182 + .saturating_add(Weight::from_parts(3_551_674, 0).saturating_mul(r.into())) .saturating_add(T::DbWeight::get().reads(6_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) .saturating_add(Weight::from_parts(0, 50).saturating_mul(r.into())) @@ -896,12 +896,12 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `797 + t * (32 ±0)` // Estimated: `21840 + t * (2640 ±0)` - // Minimum execution time: 300_675_000 picoseconds. - Weight::from_parts(296_092_420, 21840) - // Standard Error: 130_733 - .saturating_add(Weight::from_parts(2_487_957, 0).saturating_mul(t.into())) - // Standard Error: 36 - .saturating_add(Weight::from_parts(565, 0).saturating_mul(n.into())) + // Minimum execution time: 252_307_000 picoseconds. + Weight::from_parts(245_237_726, 21840) + // Standard Error: 79_824 + .saturating_add(Weight::from_parts(2_364_618, 0).saturating_mul(t.into())) + // Standard Error: 22 + .saturating_add(Weight::from_parts(636, 0).saturating_mul(n.into())) .saturating_add(T::DbWeight::get().reads(6_u64)) .saturating_add(T::DbWeight::get().reads((1_u64).saturating_mul(t.into()))) .saturating_add(T::DbWeight::get().writes(3_u64)) @@ -923,10 +923,10 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `777 + r * (7 ±0)` // Estimated: `21725 + r * (35 ±0)` - // Minimum execution time: 166_638_000 picoseconds. - Weight::from_parts(171_353_083, 21725) - // Standard Error: 550 - .saturating_add(Weight::from_parts(238_768, 0).saturating_mul(r.into())) + // Minimum execution time: 165_367_000 picoseconds. + Weight::from_parts(170_164_725, 21725) + // Standard Error: 487 + .saturating_add(Weight::from_parts(237_281, 0).saturating_mul(r.into())) .saturating_add(T::DbWeight::get().reads(6_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) .saturating_add(Weight::from_parts(0, 35).saturating_mul(r.into())) @@ -946,10 +946,10 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `125728` // Estimated: `269977` - // Minimum execution time: 414_136_000 picoseconds. - Weight::from_parts(416_093_921, 269977) - // Standard Error: 3 - .saturating_add(Weight::from_parts(794, 0).saturating_mul(i.into())) + // Minimum execution time: 350_914_000 picoseconds. + Weight::from_parts(354_461_646, 269977) + // Standard Error: 1 + .saturating_add(Weight::from_parts(747, 0).saturating_mul(i.into())) .saturating_add(T::DbWeight::get().reads(6_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) } @@ -960,10 +960,10 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `845 + r * (292 ±0)` // Estimated: `843 + r * (293 ±0)` - // Minimum execution time: 285_920_000 picoseconds. - Weight::from_parts(184_945_789, 843) - // Standard Error: 9_604 - .saturating_add(Weight::from_parts(6_012_522, 0).saturating_mul(r.into())) + // Minimum execution time: 236_273_000 picoseconds. + Weight::from_parts(137_922_946, 843) + // Standard Error: 10_363 + .saturating_add(Weight::from_parts(6_034_776, 0).saturating_mul(r.into())) .saturating_add(T::DbWeight::get().reads(6_u64)) .saturating_add(T::DbWeight::get().reads((1_u64).saturating_mul(r.into()))) .saturating_add(T::DbWeight::get().writes(3_u64)) @@ -977,10 +977,10 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `1304` // Estimated: `1280` - // Minimum execution time: 299_772_000 picoseconds. - Weight::from_parts(333_451_106, 1280) - // Standard Error: 54 - .saturating_add(Weight::from_parts(579, 0).saturating_mul(n.into())) + // Minimum execution time: 251_462_000 picoseconds. + Weight::from_parts(287_009_907, 1280) + // Standard Error: 62 + .saturating_add(Weight::from_parts(384, 0).saturating_mul(n.into())) .saturating_add(T::DbWeight::get().reads(8_u64)) .saturating_add(T::DbWeight::get().writes(6_u64)) } @@ -991,10 +991,10 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `1167 + n * (1 ±0)` // Estimated: `1167 + n * (1 ±0)` - // Minimum execution time: 299_279_000 picoseconds. - Weight::from_parts(302_336_567, 1167) - // Standard Error: 25 - .saturating_add(Weight::from_parts(86, 0).saturating_mul(n.into())) + // Minimum execution time: 250_985_000 picoseconds. + Weight::from_parts(253_693_249, 1167) + // Standard Error: 21 + .saturating_add(Weight::from_parts(92, 0).saturating_mul(n.into())) .saturating_add(T::DbWeight::get().reads(7_u64)) .saturating_add(T::DbWeight::get().writes(4_u64)) .saturating_add(Weight::from_parts(0, 1).saturating_mul(n.into())) @@ -1006,10 +1006,10 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `841 + r * (288 ±0)` // Estimated: `845 + r * (289 ±0)` - // Minimum execution time: 284_689_000 picoseconds. - Weight::from_parts(185_207_302, 845) - // Standard Error: 10_030 - .saturating_add(Weight::from_parts(5_871_325, 0).saturating_mul(r.into())) + // Minimum execution time: 235_462_000 picoseconds. + Weight::from_parts(141_240_297, 845) + // Standard Error: 9_687 + .saturating_add(Weight::from_parts(5_906_737, 0).saturating_mul(r.into())) .saturating_add(T::DbWeight::get().reads(6_u64)) .saturating_add(T::DbWeight::get().reads((1_u64).saturating_mul(r.into()))) .saturating_add(T::DbWeight::get().writes(3_u64)) @@ -1023,10 +1023,10 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `1163 + n * (1 ±0)` // Estimated: `1163 + n * (1 ±0)` - // Minimum execution time: 299_364_000 picoseconds. - Weight::from_parts(302_089_070, 1163) - // Standard Error: 23 - .saturating_add(Weight::from_parts(128, 0).saturating_mul(n.into())) + // Minimum execution time: 250_887_000 picoseconds. + Weight::from_parts(253_321_064, 1163) + // Standard Error: 28 + .saturating_add(Weight::from_parts(169, 0).saturating_mul(n.into())) .saturating_add(T::DbWeight::get().reads(7_u64)) .saturating_add(T::DbWeight::get().writes(4_u64)) .saturating_add(Weight::from_parts(0, 1).saturating_mul(n.into())) @@ -1038,10 +1038,10 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `835 + r * (296 ±0)` // Estimated: `840 + r * (297 ±0)` - // Minimum execution time: 285_175_000 picoseconds. - Weight::from_parts(200_262_957, 840) - // Standard Error: 8_681 - .saturating_add(Weight::from_parts(4_899_266, 0).saturating_mul(r.into())) + // Minimum execution time: 236_542_000 picoseconds. + Weight::from_parts(147_992_508, 840) + // Standard Error: 8_849 + .saturating_add(Weight::from_parts(4_946_692, 0).saturating_mul(r.into())) .saturating_add(T::DbWeight::get().reads(6_u64)) .saturating_add(T::DbWeight::get().reads((1_u64).saturating_mul(r.into()))) .saturating_add(T::DbWeight::get().writes(3_u64)) @@ -1054,10 +1054,10 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `1179 + n * (1 ±0)` // Estimated: `1179 + n * (1 ±0)` - // Minimum execution time: 299_459_000 picoseconds. - Weight::from_parts(302_451_160, 1179) - // Standard Error: 36 - .saturating_add(Weight::from_parts(731, 0).saturating_mul(n.into())) + // Minimum execution time: 249_987_000 picoseconds. + Weight::from_parts(254_866_627, 1179) + // Standard Error: 53 + .saturating_add(Weight::from_parts(554, 0).saturating_mul(n.into())) .saturating_add(T::DbWeight::get().reads(7_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) .saturating_add(Weight::from_parts(0, 1).saturating_mul(n.into())) @@ -1069,10 +1069,10 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `856 + r * (288 ±0)` // Estimated: `857 + r * (289 ±0)` - // Minimum execution time: 286_384_000 picoseconds. - Weight::from_parts(203_389_467, 857) - // Standard Error: 8_817 - .saturating_add(Weight::from_parts(4_692_347, 0).saturating_mul(r.into())) + // Minimum execution time: 236_635_000 picoseconds. + Weight::from_parts(157_805_789, 857) + // Standard Error: 7_699 + .saturating_add(Weight::from_parts(4_709_422, 0).saturating_mul(r.into())) .saturating_add(T::DbWeight::get().reads(6_u64)) .saturating_add(T::DbWeight::get().reads((1_u64).saturating_mul(r.into()))) .saturating_add(T::DbWeight::get().writes(3_u64)) @@ -1085,10 +1085,10 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `1166 + n * (1 ±0)` // Estimated: `1166 + n * (1 ±0)` - // Minimum execution time: 297_450_000 picoseconds. - Weight::from_parts(300_459_851, 1166) - // Standard Error: 39 - .saturating_add(Weight::from_parts(108, 0).saturating_mul(n.into())) + // Minimum execution time: 252_660_000 picoseconds. + Weight::from_parts(255_250_747, 1166) + // Standard Error: 14 + .saturating_add(Weight::from_parts(133, 0).saturating_mul(n.into())) .saturating_add(T::DbWeight::get().reads(7_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) .saturating_add(Weight::from_parts(0, 1).saturating_mul(n.into())) @@ -1100,10 +1100,10 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `829 + r * (296 ±0)` // Estimated: `836 + r * (297 ±0)` - // Minimum execution time: 285_572_000 picoseconds. - Weight::from_parts(182_642_557, 836) - // Standard Error: 9_977 - .saturating_add(Weight::from_parts(6_090_684, 0).saturating_mul(r.into())) + // Minimum execution time: 240_198_000 picoseconds. + Weight::from_parts(133_188_357, 836) + // Standard Error: 10_661 + .saturating_add(Weight::from_parts(6_147_538, 0).saturating_mul(r.into())) .saturating_add(T::DbWeight::get().reads(6_u64)) .saturating_add(T::DbWeight::get().reads((1_u64).saturating_mul(r.into()))) .saturating_add(T::DbWeight::get().writes(3_u64)) @@ -1117,10 +1117,10 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `1180 + n * (1 ±0)` // Estimated: `1180 + n * (1 ±0)` - // Minimum execution time: 301_344_000 picoseconds. - Weight::from_parts(303_770_522, 1180) - // Standard Error: 29 - .saturating_add(Weight::from_parts(807, 0).saturating_mul(n.into())) + // Minimum execution time: 252_131_000 picoseconds. + Weight::from_parts(259_960_286, 1180) + // Standard Error: 121 + .saturating_add(Weight::from_parts(192, 0).saturating_mul(n.into())) .saturating_add(T::DbWeight::get().reads(7_u64)) .saturating_add(T::DbWeight::get().writes(4_u64)) .saturating_add(Weight::from_parts(0, 1).saturating_mul(n.into())) @@ -1140,10 +1140,10 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `1373 + r * (45 ±0)` // Estimated: `26753 + r * (2700 ±0)` - // Minimum execution time: 286_835_000 picoseconds. - Weight::from_parts(245_206_457, 26753) - // Standard Error: 73_782 - .saturating_add(Weight::from_parts(36_414_448, 0).saturating_mul(r.into())) + // Minimum execution time: 235_860_000 picoseconds. + Weight::from_parts(124_993_651, 26753) + // Standard Error: 22_811 + .saturating_add(Weight::from_parts(36_467_740, 0).saturating_mul(r.into())) .saturating_add(T::DbWeight::get().reads(7_u64)) .saturating_add(T::DbWeight::get().reads((1_u64).saturating_mul(r.into()))) .saturating_add(T::DbWeight::get().writes(4_u64)) @@ -1165,10 +1165,10 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `1237 + r * (256 ±0)` // Estimated: `26028 + r * (6235 ±0)` - // Minimum execution time: 287_184_000 picoseconds. - Weight::from_parts(287_525_000, 26028) - // Standard Error: 66_791 - .saturating_add(Weight::from_parts(261_473_539, 0).saturating_mul(r.into())) + // Minimum execution time: 237_221_000 picoseconds. + Weight::from_parts(237_632_000, 26028) + // Standard Error: 89_502 + .saturating_add(Weight::from_parts(212_211_534, 0).saturating_mul(r.into())) .saturating_add(T::DbWeight::get().reads(7_u64)) .saturating_add(T::DbWeight::get().reads((2_u64).saturating_mul(r.into()))) .saturating_add(T::DbWeight::get().writes(3_u64)) @@ -1189,11 +1189,11 @@ impl WeightInfo for SubstrateWeight { fn seal_delegate_call(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0 + r * (502 ±0)` - // Estimated: `21755 + r * (6329 ±10)` - // Minimum execution time: 285_759_000 picoseconds. - Weight::from_parts(286_643_000, 21755) - // Standard Error: 133_180 - .saturating_add(Weight::from_parts(257_186_897, 0).saturating_mul(r.into())) + // Estimated: `21755 + r * (6329 ±3)` + // Minimum execution time: 236_965_000 picoseconds. + Weight::from_parts(238_110_000, 21755) + // Standard Error: 101_332 + .saturating_add(Weight::from_parts(206_790_203, 0).saturating_mul(r.into())) .saturating_add(T::DbWeight::get().reads(6_u64)) .saturating_add(T::DbWeight::get().reads((2_u64).saturating_mul(r.into()))) .saturating_add(T::DbWeight::get().writes(3_u64)) @@ -1216,12 +1216,12 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `1154 + t * (204 ±0)` // Estimated: `31015 + t * (5970 ±0)` - // Minimum execution time: 459_675_000 picoseconds. - Weight::from_parts(427_010_987, 31015) - // Standard Error: 1_277_377 - .saturating_add(Weight::from_parts(36_899_889, 0).saturating_mul(t.into())) + // Minimum execution time: 411_974_000 picoseconds. + Weight::from_parts(391_387_689, 31015) + // Standard Error: 1_320_695 + .saturating_add(Weight::from_parts(29_766_122, 0).saturating_mul(t.into())) // Standard Error: 1 - .saturating_add(Weight::from_parts(651, 0).saturating_mul(c.into())) + .saturating_add(Weight::from_parts(597, 0).saturating_mul(c.into())) .saturating_add(T::DbWeight::get().reads(9_u64)) .saturating_add(T::DbWeight::get().reads((2_u64).saturating_mul(t.into()))) .saturating_add(T::DbWeight::get().writes(5_u64)) @@ -1247,10 +1247,10 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `1301 + r * (254 ±0)` // Estimated: `30977 + r * (16635 ±0)` - // Minimum execution time: 285_816_000 picoseconds. - Weight::from_parts(286_349_000, 30977) - // Standard Error: 269_144 - .saturating_add(Weight::from_parts(394_282_520, 0).saturating_mul(r.into())) + // Minimum execution time: 237_595_000 picoseconds. + Weight::from_parts(238_068_000, 30977) + // Standard Error: 254_409 + .saturating_add(Weight::from_parts(346_436_154, 0).saturating_mul(r.into())) .saturating_add(T::DbWeight::get().reads(8_u64)) .saturating_add(T::DbWeight::get().reads((6_u64).saturating_mul(r.into()))) .saturating_add(T::DbWeight::get().writes(6_u64)) @@ -1278,14 +1278,14 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `1071 + t * (187 ±0)` // Estimated: `42684 + t * (3588 ±2)` - // Minimum execution time: 1_708_330_000 picoseconds. - Weight::from_parts(395_059_764, 42684) - // Standard Error: 4_545_552 - .saturating_add(Weight::from_parts(114_039_862, 0).saturating_mul(t.into())) + // Minimum execution time: 1_616_768_000 picoseconds. + Weight::from_parts(363_003_254, 42684) + // Standard Error: 4_398_669 + .saturating_add(Weight::from_parts(104_529_967, 0).saturating_mul(t.into())) // Standard Error: 7 - .saturating_add(Weight::from_parts(1_213, 0).saturating_mul(i.into())) + .saturating_add(Weight::from_parts(1_162, 0).saturating_mul(i.into())) // Standard Error: 7 - .saturating_add(Weight::from_parts(1_379, 0).saturating_mul(s.into())) + .saturating_add(Weight::from_parts(1_333, 0).saturating_mul(s.into())) .saturating_add(T::DbWeight::get().reads(13_u64)) .saturating_add(T::DbWeight::get().reads((1_u64).saturating_mul(t.into()))) .saturating_add(T::DbWeight::get().writes(10_u64)) @@ -1307,10 +1307,10 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `777 + r * (8 ±0)` // Estimated: `21710 + r * (40 ±0)` - // Minimum execution time: 283_738_000 picoseconds. - Weight::from_parts(289_885_978, 21710) - // Standard Error: 1_057 - .saturating_add(Weight::from_parts(575_432, 0).saturating_mul(r.into())) + // Minimum execution time: 233_814_000 picoseconds. + Weight::from_parts(241_291_041, 21710) + // Standard Error: 1_422 + .saturating_add(Weight::from_parts(575_846, 0).saturating_mul(r.into())) .saturating_add(T::DbWeight::get().reads(6_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) .saturating_add(Weight::from_parts(0, 40).saturating_mul(r.into())) @@ -1330,10 +1330,10 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `785` // Estimated: `21745` - // Minimum execution time: 285_070_000 picoseconds. - Weight::from_parts(283_987_687, 21745) - // Standard Error: 6 - .saturating_add(Weight::from_parts(4_008, 0).saturating_mul(n.into())) + // Minimum execution time: 236_572_000 picoseconds. + Weight::from_parts(235_648_055, 21745) + // Standard Error: 3 + .saturating_add(Weight::from_parts(3_947, 0).saturating_mul(n.into())) .saturating_add(T::DbWeight::get().reads(6_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) } @@ -1352,10 +1352,10 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `779 + r * (8 ±0)` // Estimated: `21725 + r * (40 ±0)` - // Minimum execution time: 281_613_000 picoseconds. - Weight::from_parts(285_429_053, 21725) - // Standard Error: 1_164 - .saturating_add(Weight::from_parts(756_244, 0).saturating_mul(r.into())) + // Minimum execution time: 234_473_000 picoseconds. + Weight::from_parts(239_805_309, 21725) + // Standard Error: 1_113 + .saturating_add(Weight::from_parts(752_507, 0).saturating_mul(r.into())) .saturating_add(T::DbWeight::get().reads(6_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) .saturating_add(Weight::from_parts(0, 40).saturating_mul(r.into())) @@ -1375,10 +1375,10 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `787` // Estimated: `21765` - // Minimum execution time: 284_593_000 picoseconds. - Weight::from_parts(278_467_111, 21765) + // Minimum execution time: 235_209_000 picoseconds. + Weight::from_parts(228_548_524, 21765) // Standard Error: 2 - .saturating_add(Weight::from_parts(3_217, 0).saturating_mul(n.into())) + .saturating_add(Weight::from_parts(3_171, 0).saturating_mul(n.into())) .saturating_add(T::DbWeight::get().reads(6_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) } @@ -1397,10 +1397,10 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `779 + r * (8 ±0)` // Estimated: `21740 + r * (40 ±0)` - // Minimum execution time: 281_759_000 picoseconds. - Weight::from_parts(288_807_137, 21740) - // Standard Error: 805 - .saturating_add(Weight::from_parts(424_378, 0).saturating_mul(r.into())) + // Minimum execution time: 233_282_000 picoseconds. + Weight::from_parts(240_864_680, 21740) + // Standard Error: 958 + .saturating_add(Weight::from_parts(418_308, 0).saturating_mul(r.into())) .saturating_add(T::DbWeight::get().reads(6_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) .saturating_add(Weight::from_parts(0, 40).saturating_mul(r.into())) @@ -1420,10 +1420,10 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `787` // Estimated: `21785` - // Minimum execution time: 282_666_000 picoseconds. - Weight::from_parts(274_357_944, 21785) + // Minimum execution time: 234_667_000 picoseconds. + Weight::from_parts(227_810_077, 21785) // Standard Error: 2 - .saturating_add(Weight::from_parts(974, 0).saturating_mul(n.into())) + .saturating_add(Weight::from_parts(925, 0).saturating_mul(n.into())) .saturating_add(T::DbWeight::get().reads(6_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) } @@ -1442,10 +1442,10 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `779 + r * (8 ±0)` // Estimated: `21745 + r * (40 ±0)` - // Minimum execution time: 285_073_000 picoseconds. - Weight::from_parts(287_226_796, 21745) - // Standard Error: 951 - .saturating_add(Weight::from_parts(425_368, 0).saturating_mul(r.into())) + // Minimum execution time: 234_040_000 picoseconds. + Weight::from_parts(237_970_694, 21745) + // Standard Error: 979 + .saturating_add(Weight::from_parts(416_562, 0).saturating_mul(r.into())) .saturating_add(T::DbWeight::get().reads(6_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) .saturating_add(Weight::from_parts(0, 40).saturating_mul(r.into())) @@ -1465,10 +1465,10 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `787` // Estimated: `21755` - // Minimum execution time: 283_407_000 picoseconds. - Weight::from_parts(276_737_242, 21755) + // Minimum execution time: 234_840_000 picoseconds. + Weight::from_parts(227_849_778, 21755) // Standard Error: 2 - .saturating_add(Weight::from_parts(967, 0).saturating_mul(n.into())) + .saturating_add(Weight::from_parts(923, 0).saturating_mul(n.into())) .saturating_add(T::DbWeight::get().reads(6_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) } @@ -1487,10 +1487,10 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `822 + r * (76 ±0)` // Estimated: `21705 + r * (385 ±0)` - // Minimum execution time: 285_130_000 picoseconds. - Weight::from_parts(299_449_202, 21705) - // Standard Error: 16_535 - .saturating_add(Weight::from_parts(37_655_189, 0).saturating_mul(r.into())) + // Minimum execution time: 236_174_000 picoseconds. + Weight::from_parts(252_457_690, 21705) + // Standard Error: 20_130 + .saturating_add(Weight::from_parts(37_792_805, 0).saturating_mul(r.into())) .saturating_add(T::DbWeight::get().reads(6_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) .saturating_add(Weight::from_parts(0, 385).saturating_mul(r.into())) @@ -1510,10 +1510,10 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `792 + r * (42 ±0)` // Estimated: `21780 + r * (210 ±0)` - // Minimum execution time: 284_494_000 picoseconds. - Weight::from_parts(282_154_339, 21780) - // Standard Error: 12_278 - .saturating_add(Weight::from_parts(9_501_559, 0).saturating_mul(r.into())) + // Minimum execution time: 236_251_000 picoseconds. + Weight::from_parts(242_254_305, 21780) + // Standard Error: 9_765 + .saturating_add(Weight::from_parts(9_341_334, 0).saturating_mul(r.into())) .saturating_add(T::DbWeight::get().reads(6_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) .saturating_add(Weight::from_parts(0, 210).saturating_mul(r.into())) @@ -1535,10 +1535,10 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `0 + r * (964 ±0)` // Estimated: `29920 + r * (11544 ±7)` - // Minimum execution time: 285_306_000 picoseconds. - Weight::from_parts(286_080_000, 29920) - // Standard Error: 43_813 - .saturating_add(Weight::from_parts(21_758_329, 0).saturating_mul(r.into())) + // Minimum execution time: 236_462_000 picoseconds. + Weight::from_parts(236_997_000, 29920) + // Standard Error: 46_527 + .saturating_add(Weight::from_parts(21_858_761, 0).saturating_mul(r.into())) .saturating_add(T::DbWeight::get().reads(6_u64)) .saturating_add(T::DbWeight::get().reads((3_u64).saturating_mul(r.into()))) .saturating_add(T::DbWeight::get().writes(3_u64)) @@ -1560,10 +1560,10 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `773 + r * (3 ±0)` // Estimated: `21735 + r * (15 ±0)` - // Minimum execution time: 283_487_000 picoseconds. - Weight::from_parts(289_280_189, 21735) - // Standard Error: 829 - .saturating_add(Weight::from_parts(168_973, 0).saturating_mul(r.into())) + // Minimum execution time: 235_085_000 picoseconds. + Weight::from_parts(239_410_836, 21735) + // Standard Error: 414 + .saturating_add(Weight::from_parts(167_067, 0).saturating_mul(r.into())) .saturating_add(T::DbWeight::get().reads(6_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) .saturating_add(Weight::from_parts(0, 15).saturating_mul(r.into())) @@ -1583,10 +1583,10 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `1975 + r * (39 ±0)` // Estimated: `27145 + r * (200 ±0)` - // Minimum execution time: 287_413_000 picoseconds. - Weight::from_parts(314_662_286, 27145) - // Standard Error: 1_099 - .saturating_add(Weight::from_parts(262_201, 0).saturating_mul(r.into())) + // Minimum execution time: 236_690_000 picoseconds. + Weight::from_parts(268_793_030, 27145) + // Standard Error: 1_210 + .saturating_add(Weight::from_parts(263_330, 0).saturating_mul(r.into())) .saturating_add(T::DbWeight::get().reads(6_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) .saturating_add(Weight::from_parts(0, 200).saturating_mul(r.into())) @@ -1608,10 +1608,10 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `776 + r * (3 ±0)` // Estimated: `24004 + r * (18 ±0)` - // Minimum execution time: 282_601_000 picoseconds. - Weight::from_parts(289_374_203, 24004) - // Standard Error: 452 - .saturating_add(Weight::from_parts(142_661, 0).saturating_mul(r.into())) + // Minimum execution time: 236_289_000 picoseconds. + Weight::from_parts(246_581_099, 24004) + // Standard Error: 1_300 + .saturating_add(Weight::from_parts(137_499, 0).saturating_mul(r.into())) .saturating_add(T::DbWeight::get().reads(7_u64)) .saturating_add(T::DbWeight::get().writes(4_u64)) .saturating_add(Weight::from_parts(0, 18).saturating_mul(r.into())) @@ -1621,508 +1621,510 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_692_000 picoseconds. - Weight::from_parts(2_069_482, 0) - // Standard Error: 40 - .saturating_add(Weight::from_parts(2_922, 0).saturating_mul(r.into())) + // Minimum execution time: 1_679_000 picoseconds. + Weight::from_parts(1_934_194, 0) + // Standard Error: 1 + .saturating_add(Weight::from_parts(3_000, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 5000]`. fn instr_i64load(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_690_000 picoseconds. - Weight::from_parts(2_303_602, 0) - // Standard Error: 4 - .saturating_add(Weight::from_parts(6_433, 0).saturating_mul(r.into())) + // Minimum execution time: 1_872_000 picoseconds. + Weight::from_parts(2_605_712, 0) + // Standard Error: 43 + .saturating_add(Weight::from_parts(6_321, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 5000]`. fn instr_i64store(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_701_000 picoseconds. - Weight::from_parts(2_321_142, 0) - // Standard Error: 4 - .saturating_add(Weight::from_parts(6_025, 0).saturating_mul(r.into())) + // Minimum execution time: 1_837_000 picoseconds. + Weight::from_parts(2_035_143, 0) + // Standard Error: 65 + .saturating_add(Weight::from_parts(6_202, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 5000]`. fn instr_select(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_656_000 picoseconds. - Weight::from_parts(2_090_881, 0) + // Minimum execution time: 1_684_000 picoseconds. + Weight::from_parts(2_044_218, 0) // Standard Error: 5 - .saturating_add(Weight::from_parts(7_941, 0).saturating_mul(r.into())) + .saturating_add(Weight::from_parts(7_929, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 5000]`. fn instr_if(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_659_000 picoseconds. - Weight::from_parts(1_816_547, 0) - // Standard Error: 5 - .saturating_add(Weight::from_parts(10_578, 0).saturating_mul(r.into())) + // Minimum execution time: 1_635_000 picoseconds. + Weight::from_parts(2_009_851, 0) + // Standard Error: 6 + .saturating_add(Weight::from_parts(10_724, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 5000]`. fn instr_br(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_683_000 picoseconds. - Weight::from_parts(1_970_907, 0) + // Minimum execution time: 1_667_000 picoseconds. + Weight::from_parts(1_869_395, 0) // Standard Error: 12 - .saturating_add(Weight::from_parts(4_636, 0).saturating_mul(r.into())) + .saturating_add(Weight::from_parts(4_618, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 5000]`. fn instr_br_if(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_651_000 picoseconds. - Weight::from_parts(2_263_817, 0) - // Standard Error: 68 - .saturating_add(Weight::from_parts(7_529, 0).saturating_mul(r.into())) + // Minimum execution time: 1_698_000 picoseconds. + Weight::from_parts(2_184_182, 0) + // Standard Error: 16 + .saturating_add(Weight::from_parts(6_833, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 5000]`. fn instr_br_table(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_687_000 picoseconds. - Weight::from_parts(1_349_186, 0) - // Standard Error: 31 - .saturating_add(Weight::from_parts(9_732, 0).saturating_mul(r.into())) + // Minimum execution time: 1_670_000 picoseconds. + Weight::from_parts(1_471_988, 0) + // Standard Error: 30 + .saturating_add(Weight::from_parts(9_550, 0).saturating_mul(r.into())) } /// The range of component `e` is `[1, 256]`. - fn instr_br_table_per_entry(_e: u32, ) -> Weight { + fn instr_br_table_per_entry(e: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_777_000 picoseconds. - Weight::from_parts(2_036_446, 0) + // Minimum execution time: 1_778_000 picoseconds. + Weight::from_parts(1_925_086, 0) + // Standard Error: 136 + .saturating_add(Weight::from_parts(502, 0).saturating_mul(e.into())) } /// The range of component `r` is `[0, 5000]`. fn instr_call(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_686_000 picoseconds. - Weight::from_parts(464_449, 0) - // Standard Error: 383 - .saturating_add(Weight::from_parts(19_121, 0).saturating_mul(r.into())) + // Minimum execution time: 1_759_000 picoseconds. + Weight::from_parts(2_372_048, 0) + // Standard Error: 16 + .saturating_add(Weight::from_parts(17_953, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 5000]`. fn instr_call_indirect(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_855_000 picoseconds. - Weight::from_parts(3_381_585, 0) - // Standard Error: 18 - .saturating_add(Weight::from_parts(24_245, 0).saturating_mul(r.into())) + // Minimum execution time: 1_852_000 picoseconds. + Weight::from_parts(3_125_003, 0) + // Standard Error: 25 + .saturating_add(Weight::from_parts(24_218, 0).saturating_mul(r.into())) } /// The range of component `l` is `[0, 1024]`. fn instr_call_per_local(l: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_792_000 picoseconds. - Weight::from_parts(2_006_024, 0) - // Standard Error: 16 - .saturating_add(Weight::from_parts(2_181, 0).saturating_mul(l.into())) + // Minimum execution time: 1_795_000 picoseconds. + Weight::from_parts(2_121_763, 0) + // Standard Error: 45 + .saturating_add(Weight::from_parts(1_207, 0).saturating_mul(l.into())) } /// The range of component `r` is `[0, 5000]`. fn instr_local_get(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 3_918_000 picoseconds. - Weight::from_parts(4_618_761, 0) - // Standard Error: 49 - .saturating_add(Weight::from_parts(2_312, 0).saturating_mul(r.into())) + // Minimum execution time: 3_019_000 picoseconds. + Weight::from_parts(3_267_108, 0) + // Standard Error: 55 + .saturating_add(Weight::from_parts(2_527, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 5000]`. fn instr_local_set(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 3_889_000 picoseconds. - Weight::from_parts(4_151_280, 0) + // Minimum execution time: 2_944_000 picoseconds. + Weight::from_parts(3_183_331, 0) // Standard Error: 2 - .saturating_add(Weight::from_parts(3_623, 0).saturating_mul(r.into())) + .saturating_add(Weight::from_parts(3_618, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 5000]`. fn instr_local_tee(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 3_880_000 picoseconds. - Weight::from_parts(4_225_780, 0) - // Standard Error: 3 - .saturating_add(Weight::from_parts(3_847, 0).saturating_mul(r.into())) + // Minimum execution time: 3_009_000 picoseconds. + Weight::from_parts(3_178_158, 0) + // Standard Error: 41 + .saturating_add(Weight::from_parts(4_075, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 5000]`. fn instr_global_get(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_765_000 picoseconds. - Weight::from_parts(2_216_674, 0) - // Standard Error: 4 - .saturating_add(Weight::from_parts(8_393, 0).saturating_mul(r.into())) + // Minimum execution time: 1_748_000 picoseconds. + Weight::from_parts(2_371_911, 0) + // Standard Error: 10 + .saturating_add(Weight::from_parts(8_378, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 5000]`. fn instr_global_set(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_764_000 picoseconds. - Weight::from_parts(2_246_735, 0) - // Standard Error: 5 - .saturating_add(Weight::from_parts(8_877, 0).saturating_mul(r.into())) + // Minimum execution time: 1_795_000 picoseconds. + Weight::from_parts(1_311_997, 0) + // Standard Error: 157 + .saturating_add(Weight::from_parts(9_410, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 5000]`. fn instr_memory_current(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_758_000 picoseconds. - Weight::from_parts(1_922_386, 0) - // Standard Error: 94 - .saturating_add(Weight::from_parts(3_868, 0).saturating_mul(r.into())) + // Minimum execution time: 2_059_000 picoseconds. + Weight::from_parts(2_416_611, 0) + // Standard Error: 5 + .saturating_add(Weight::from_parts(3_775, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 16]`. fn instr_memory_grow(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_635_000 picoseconds. - Weight::from_parts(1_118_785, 0) - // Standard Error: 134_978 - .saturating_add(Weight::from_parts(16_343_664, 0).saturating_mul(r.into())) + // Minimum execution time: 1_764_000 picoseconds. + Weight::from_parts(1_414_442, 0) + // Standard Error: 142_321 + .saturating_add(Weight::from_parts(13_210_495, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 5000]`. fn instr_i64clz(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_648_000 picoseconds. - Weight::from_parts(2_012_545, 0) + // Minimum execution time: 1_665_000 picoseconds. + Weight::from_parts(2_047_968, 0) // Standard Error: 2 - .saturating_add(Weight::from_parts(3_824, 0).saturating_mul(r.into())) + .saturating_add(Weight::from_parts(4_035, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 5000]`. fn instr_i64ctz(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_688_000 picoseconds. - Weight::from_parts(1_995_956, 0) - // Standard Error: 2 - .saturating_add(Weight::from_parts(3_757, 0).saturating_mul(r.into())) + // Minimum execution time: 1_696_000 picoseconds. + Weight::from_parts(2_101_548, 0) + // Standard Error: 13 + .saturating_add(Weight::from_parts(3_754, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 5000]`. fn instr_i64popcnt(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_631_000 picoseconds. - Weight::from_parts(2_011_493, 0) - // Standard Error: 3 - .saturating_add(Weight::from_parts(3_755, 0).saturating_mul(r.into())) + // Minimum execution time: 1_686_000 picoseconds. + Weight::from_parts(2_023_482, 0) + // Standard Error: 2 + .saturating_add(Weight::from_parts(3_770, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 5000]`. fn instr_i64eqz(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_667_000 picoseconds. - Weight::from_parts(1_958_798, 0) + // Minimum execution time: 1_700_000 picoseconds. + Weight::from_parts(2_035_759, 0) // Standard Error: 2 - .saturating_add(Weight::from_parts(3_677, 0).saturating_mul(r.into())) + .saturating_add(Weight::from_parts(3_660, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 5000]`. fn instr_i64extendsi32(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_674_000 picoseconds. - Weight::from_parts(2_009_555, 0) - // Standard Error: 7 - .saturating_add(Weight::from_parts(3_863, 0).saturating_mul(r.into())) + // Minimum execution time: 1_682_000 picoseconds. + Weight::from_parts(2_015_828, 0) + // Standard Error: 2 + .saturating_add(Weight::from_parts(3_977, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 5000]`. fn instr_i64extendui32(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_659_000 picoseconds. - Weight::from_parts(2_014_985, 0) + // Minimum execution time: 1_660_000 picoseconds. + Weight::from_parts(2_032_387, 0) // Standard Error: 2 - .saturating_add(Weight::from_parts(3_821, 0).saturating_mul(r.into())) + .saturating_add(Weight::from_parts(3_826, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 5000]`. fn instr_i32wrapi64(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_640_000 picoseconds. - Weight::from_parts(2_013_939, 0) - // Standard Error: 2 - .saturating_add(Weight::from_parts(3_708, 0).saturating_mul(r.into())) + // Minimum execution time: 1_635_000 picoseconds. + Weight::from_parts(2_013_228, 0) + // Standard Error: 3 + .saturating_add(Weight::from_parts(3_752, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 5000]`. fn instr_i64eq(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_631_000 picoseconds. - Weight::from_parts(2_002_814, 0) - // Standard Error: 2 - .saturating_add(Weight::from_parts(6_008, 0).saturating_mul(r.into())) + // Minimum execution time: 1_698_000 picoseconds. + Weight::from_parts(2_844_817, 0) + // Standard Error: 56 + .saturating_add(Weight::from_parts(5_746, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 5000]`. fn instr_i64ne(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_647_000 picoseconds. - Weight::from_parts(2_032_158, 0) - // Standard Error: 3 - .saturating_add(Weight::from_parts(5_944, 0).saturating_mul(r.into())) + // Minimum execution time: 1_686_000 picoseconds. + Weight::from_parts(2_208_884, 0) + // Standard Error: 138 + .saturating_add(Weight::from_parts(6_032, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 5000]`. fn instr_i64lts(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_669_000 picoseconds. - Weight::from_parts(2_040_386, 0) + // Minimum execution time: 1_695_000 picoseconds. + Weight::from_parts(2_060_880, 0) // Standard Error: 3 - .saturating_add(Weight::from_parts(6_009, 0).saturating_mul(r.into())) + .saturating_add(Weight::from_parts(5_966, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 5000]`. fn instr_i64ltu(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_637_000 picoseconds. - Weight::from_parts(1_983_695, 0) - // Standard Error: 4 - .saturating_add(Weight::from_parts(6_027, 0).saturating_mul(r.into())) + // Minimum execution time: 1_700_000 picoseconds. + Weight::from_parts(2_143_484, 0) + // Standard Error: 3 + .saturating_add(Weight::from_parts(5_932, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 5000]`. fn instr_i64gts(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_701_000 picoseconds. - Weight::from_parts(2_054_295, 0) - // Standard Error: 2 - .saturating_add(Weight::from_parts(5_799, 0).saturating_mul(r.into())) + // Minimum execution time: 1_677_000 picoseconds. + Weight::from_parts(2_081_646, 0) + // Standard Error: 3 + .saturating_add(Weight::from_parts(5_813, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 5000]`. fn instr_i64gtu(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_653_000 picoseconds. - Weight::from_parts(2_749_807, 0) - // Standard Error: 90 - .saturating_add(Weight::from_parts(5_945, 0).saturating_mul(r.into())) + // Minimum execution time: 2_476_000 picoseconds. + Weight::from_parts(2_161_801, 0) + // Standard Error: 4 + .saturating_add(Weight::from_parts(6_078, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 5000]`. fn instr_i64les(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_651_000 picoseconds. - Weight::from_parts(1_979_111, 0) + // Minimum execution time: 1_677_000 picoseconds. + Weight::from_parts(2_043_451, 0) // Standard Error: 3 - .saturating_add(Weight::from_parts(6_011, 0).saturating_mul(r.into())) + .saturating_add(Weight::from_parts(5_988, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 5000]`. fn instr_i64leu(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_743_000 picoseconds. - Weight::from_parts(2_058_081, 0) - // Standard Error: 3 - .saturating_add(Weight::from_parts(6_085, 0).saturating_mul(r.into())) + // Minimum execution time: 1_694_000 picoseconds. + Weight::from_parts(2_058_196, 0) + // Standard Error: 2 + .saturating_add(Weight::from_parts(6_058, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 5000]`. fn instr_i64ges(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_638_000 picoseconds. - Weight::from_parts(2_038_929, 0) - // Standard Error: 3 - .saturating_add(Weight::from_parts(5_941, 0).saturating_mul(r.into())) + // Minimum execution time: 1_675_000 picoseconds. + Weight::from_parts(2_036_798, 0) + // Standard Error: 2 + .saturating_add(Weight::from_parts(5_956, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 5000]`. fn instr_i64geu(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_641_000 picoseconds. - Weight::from_parts(2_036_587, 0) + // Minimum execution time: 1_712_000 picoseconds. + Weight::from_parts(2_121_407, 0) // Standard Error: 2 - .saturating_add(Weight::from_parts(6_008, 0).saturating_mul(r.into())) + .saturating_add(Weight::from_parts(5_944, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 5000]`. fn instr_i64add(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_624_000 picoseconds. - Weight::from_parts(2_080_562, 0) - // Standard Error: 3 - .saturating_add(Weight::from_parts(5_826, 0).saturating_mul(r.into())) + // Minimum execution time: 1_675_000 picoseconds. + Weight::from_parts(2_061_053, 0) + // Standard Error: 6 + .saturating_add(Weight::from_parts(5_823, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 5000]`. fn instr_i64sub(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_652_000 picoseconds. - Weight::from_parts(2_039_535, 0) - // Standard Error: 2 - .saturating_add(Weight::from_parts(6_137, 0).saturating_mul(r.into())) + // Minimum execution time: 1_686_000 picoseconds. + Weight::from_parts(2_023_347, 0) + // Standard Error: 4 + .saturating_add(Weight::from_parts(6_126, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 5000]`. fn instr_i64mul(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_666_000 picoseconds. - Weight::from_parts(2_056_354, 0) - // Standard Error: 6 - .saturating_add(Weight::from_parts(5_780, 0).saturating_mul(r.into())) + // Minimum execution time: 1_678_000 picoseconds. + Weight::from_parts(2_390_920, 0) + // Standard Error: 55 + .saturating_add(Weight::from_parts(5_635, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 5000]`. fn instr_i64divs(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_648_000 picoseconds. - Weight::from_parts(2_077_695, 0) - // Standard Error: 4 - .saturating_add(Weight::from_parts(11_775, 0).saturating_mul(r.into())) + // Minimum execution time: 1_728_000 picoseconds. + Weight::from_parts(1_944_457, 0) + // Standard Error: 7 + .saturating_add(Weight::from_parts(11_848, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 5000]`. fn instr_i64divu(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_797_000 picoseconds. - Weight::from_parts(2_772_388, 0) - // Standard Error: 33 - .saturating_add(Weight::from_parts(10_333, 0).saturating_mul(r.into())) + // Minimum execution time: 1_649_000 picoseconds. + Weight::from_parts(1_881_148, 0) + // Standard Error: 6 + .saturating_add(Weight::from_parts(10_618, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 5000]`. fn instr_i64rems(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_699_000 picoseconds. - Weight::from_parts(2_174_288, 0) + // Minimum execution time: 1_708_000 picoseconds. + Weight::from_parts(1_767_912, 0) // Standard Error: 6 - .saturating_add(Weight::from_parts(11_778, 0).saturating_mul(r.into())) + .saturating_add(Weight::from_parts(12_099, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 5000]`. fn instr_i64remu(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_685_000 picoseconds. - Weight::from_parts(2_091_037, 0) + // Minimum execution time: 1_650_000 picoseconds. + Weight::from_parts(1_998_575, 0) // Standard Error: 3 - .saturating_add(Weight::from_parts(10_694, 0).saturating_mul(r.into())) + .saturating_add(Weight::from_parts(10_632, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 5000]`. fn instr_i64and(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_636_000 picoseconds. - Weight::from_parts(1_975_521, 0) - // Standard Error: 2 - .saturating_add(Weight::from_parts(5_695, 0).saturating_mul(r.into())) + // Minimum execution time: 1_633_000 picoseconds. + Weight::from_parts(2_029_981, 0) + // Standard Error: 3 + .saturating_add(Weight::from_parts(5_662, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 5000]`. fn instr_i64or(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_619_000 picoseconds. - Weight::from_parts(2_045_492, 0) - // Standard Error: 2 - .saturating_add(Weight::from_parts(5_770, 0).saturating_mul(r.into())) + // Minimum execution time: 1_621_000 picoseconds. + Weight::from_parts(2_029_743, 0) + // Standard Error: 3 + .saturating_add(Weight::from_parts(5_762, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 5000]`. fn instr_i64xor(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_668_000 picoseconds. - Weight::from_parts(2_055_460, 0) - // Standard Error: 3 - .saturating_add(Weight::from_parts(5_851, 0).saturating_mul(r.into())) + // Minimum execution time: 1_641_000 picoseconds. + Weight::from_parts(2_127_132, 0) + // Standard Error: 5 + .saturating_add(Weight::from_parts(5_818, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 5000]`. fn instr_i64shl(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_681_000 picoseconds. - Weight::from_parts(2_023_370, 0) - // Standard Error: 2 - .saturating_add(Weight::from_parts(5_853, 0).saturating_mul(r.into())) + // Minimum execution time: 1_690_000 picoseconds. + Weight::from_parts(2_021_035, 0) + // Standard Error: 4 + .saturating_add(Weight::from_parts(5_917, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 5000]`. fn instr_i64shrs(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_714_000 picoseconds. - Weight::from_parts(2_067_584, 0) + // Minimum execution time: 1_661_000 picoseconds. + Weight::from_parts(2_055_069, 0) // Standard Error: 3 - .saturating_add(Weight::from_parts(6_133, 0).saturating_mul(r.into())) + .saturating_add(Weight::from_parts(6_094, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 5000]`. fn instr_i64shru(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_602_000 picoseconds. - Weight::from_parts(2_055_530, 0) - // Standard Error: 138 - .saturating_add(Weight::from_parts(6_014, 0).saturating_mul(r.into())) + // Minimum execution time: 1_677_000 picoseconds. + Weight::from_parts(2_024_748, 0) + // Standard Error: 2 + .saturating_add(Weight::from_parts(5_862, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 5000]`. fn instr_i64rotl(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_709_000 picoseconds. - Weight::from_parts(2_016_365, 0) - // Standard Error: 2 - .saturating_add(Weight::from_parts(5_985, 0).saturating_mul(r.into())) + // Minimum execution time: 1_651_000 picoseconds. + Weight::from_parts(2_005_814, 0) + // Standard Error: 3 + .saturating_add(Weight::from_parts(6_007, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 5000]`. fn instr_i64rotr(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_661_000 picoseconds. - Weight::from_parts(2_003_063, 0) - // Standard Error: 2 - .saturating_add(Weight::from_parts(5_863, 0).saturating_mul(r.into())) + // Minimum execution time: 1_703_000 picoseconds. + Weight::from_parts(2_019_636, 0) + // Standard Error: 3 + .saturating_add(Weight::from_parts(5_862, 0).saturating_mul(r.into())) } } @@ -2134,8 +2136,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `109` // Estimated: `1594` - // Minimum execution time: 2_585_000 picoseconds. - Weight::from_parts(2_802_000, 1594) + // Minimum execution time: 2_713_000 picoseconds. + Weight::from_parts(2_811_000, 1594) .saturating_add(RocksDbWeight::get().reads(1_u64)) } /// Storage: Skipped Metadata (r:0 w:0) @@ -2145,10 +2147,10 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `450 + k * (69 ±0)` // Estimated: `440 + k * (70 ±0)` - // Minimum execution time: 10_825_000 picoseconds. - Weight::from_parts(5_747_064, 440) - // Standard Error: 1_037 - .saturating_add(Weight::from_parts(973_689, 0).saturating_mul(k.into())) + // Minimum execution time: 11_011_000 picoseconds. + Weight::from_parts(7_025_244, 440) + // Standard Error: 1_217 + .saturating_add(Weight::from_parts(980_818, 0).saturating_mul(k.into())) .saturating_add(RocksDbWeight::get().reads(1_u64)) .saturating_add(RocksDbWeight::get().reads((1_u64).saturating_mul(k.into()))) .saturating_add(RocksDbWeight::get().writes(1_u64)) @@ -2162,10 +2164,10 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `250 + q * (33 ±0)` // Estimated: `1725 + q * (33 ±0)` - // Minimum execution time: 2_718_000 picoseconds. - Weight::from_parts(11_436_305, 1725) - // Standard Error: 3_619 - .saturating_add(Weight::from_parts(1_296_955, 0).saturating_mul(q.into())) + // Minimum execution time: 2_802_000 picoseconds. + Weight::from_parts(10_768_336, 1725) + // Standard Error: 3_424 + .saturating_add(Weight::from_parts(1_323_649, 0).saturating_mul(q.into())) .saturating_add(RocksDbWeight::get().reads(1_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) .saturating_add(Weight::from_parts(0, 33).saturating_mul(q.into())) @@ -2179,10 +2181,10 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `238 + c * (1 ±0)` // Estimated: `3951 + c * (2 ±0)` - // Minimum execution time: 37_882_000 picoseconds. - Weight::from_parts(43_548_816, 3951) - // Standard Error: 49 - .saturating_add(Weight::from_parts(53_936, 0).saturating_mul(c.into())) + // Minimum execution time: 30_299_000 picoseconds. + Weight::from_parts(24_608_986, 3951) + // Standard Error: 75 + .saturating_add(Weight::from_parts(54_619, 0).saturating_mul(c.into())) .saturating_add(RocksDbWeight::get().reads(1_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) .saturating_add(Weight::from_parts(0, 2).saturating_mul(c.into())) @@ -2202,10 +2204,10 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `707` // Estimated: `21400 + c * (5 ±0)` - // Minimum execution time: 313_392_000 picoseconds. - Weight::from_parts(325_419_093, 21400) - // Standard Error: 25 - .saturating_add(Weight::from_parts(37_877, 0).saturating_mul(c.into())) + // Minimum execution time: 265_835_000 picoseconds. + Weight::from_parts(275_985_164, 21400) + // Standard Error: 36 + .saturating_add(Weight::from_parts(37_980, 0).saturating_mul(c.into())) .saturating_add(RocksDbWeight::get().reads(6_u64)) .saturating_add(RocksDbWeight::get().writes(4_u64)) .saturating_add(Weight::from_parts(0, 5).saturating_mul(c.into())) @@ -2233,14 +2235,14 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `270` // Estimated: `26207` - // Minimum execution time: 3_274_352_000 picoseconds. - Weight::from_parts(681_171_416, 26207) - // Standard Error: 336 - .saturating_add(Weight::from_parts(106_391, 0).saturating_mul(c.into())) - // Standard Error: 19 - .saturating_add(Weight::from_parts(1_147, 0).saturating_mul(i.into())) - // Standard Error: 19 - .saturating_add(Weight::from_parts(1_493, 0).saturating_mul(s.into())) + // Minimum execution time: 3_124_508_000 picoseconds. + Weight::from_parts(617_467_897, 26207) + // Standard Error: 293 + .saturating_add(Weight::from_parts(106_971, 0).saturating_mul(c.into())) + // Standard Error: 17 + .saturating_add(Weight::from_parts(1_156, 0).saturating_mul(i.into())) + // Standard Error: 17 + .saturating_add(Weight::from_parts(1_395, 0).saturating_mul(s.into())) .saturating_add(RocksDbWeight::get().reads(9_u64)) .saturating_add(RocksDbWeight::get().writes(10_u64)) } @@ -2264,12 +2266,12 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `482` // Estimated: `28521` - // Minimum execution time: 1_692_637_000 picoseconds. - Weight::from_parts(283_252_265, 28521) + // Minimum execution time: 1_649_483_000 picoseconds. + Weight::from_parts(287_642_416, 28521) // Standard Error: 8 - .saturating_add(Weight::from_parts(1_498, 0).saturating_mul(i.into())) + .saturating_add(Weight::from_parts(1_450, 0).saturating_mul(i.into())) // Standard Error: 8 - .saturating_add(Weight::from_parts(1_487, 0).saturating_mul(s.into())) + .saturating_add(Weight::from_parts(1_443, 0).saturating_mul(s.into())) .saturating_add(RocksDbWeight::get().reads(9_u64)) .saturating_add(RocksDbWeight::get().writes(7_u64)) } @@ -2287,8 +2289,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `759` // Estimated: `21615` - // Minimum execution time: 192_369_000 picoseconds. - Weight::from_parts(193_417_000, 21615) + // Minimum execution time: 192_302_000 picoseconds. + Weight::from_parts(193_192_000, 21615) .saturating_add(RocksDbWeight::get().reads(6_u64)) .saturating_add(RocksDbWeight::get().writes(4_u64)) } @@ -2305,10 +2307,10 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `109` // Estimated: `7366` - // Minimum execution time: 301_972_000 picoseconds. - Weight::from_parts(313_248_051, 7366) - // Standard Error: 82 - .saturating_add(Weight::from_parts(107_321, 0).saturating_mul(c.into())) + // Minimum execution time: 246_401_000 picoseconds. + Weight::from_parts(261_505_456, 7366) + // Standard Error: 83 + .saturating_add(Weight::from_parts(109_136, 0).saturating_mul(c.into())) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().writes(4_u64)) } @@ -2324,8 +2326,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `255` // Estimated: `7950` - // Minimum execution time: 33_013_000 picoseconds. - Weight::from_parts(33_622_000, 7950) + // Minimum execution time: 33_913_000 picoseconds. + Weight::from_parts(34_186_000, 7950) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().writes(4_u64)) } @@ -2339,8 +2341,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `570` // Estimated: `19530` - // Minimum execution time: 33_518_000 picoseconds. - Weight::from_parts(33_819_000, 19530) + // Minimum execution time: 33_801_000 picoseconds. + Weight::from_parts(34_877_000, 19530) .saturating_add(RocksDbWeight::get().reads(6_u64)) .saturating_add(RocksDbWeight::get().writes(6_u64)) } @@ -2359,10 +2361,10 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `781 + r * (6 ±0)` // Estimated: `21730 + r * (30 ±0)` - // Minimum execution time: 283_945_000 picoseconds. - Weight::from_parts(289_209_599, 21730) - // Standard Error: 901 - .saturating_add(Weight::from_parts(327_601, 0).saturating_mul(r.into())) + // Minimum execution time: 237_679_000 picoseconds. + Weight::from_parts(243_022_905, 21730) + // Standard Error: 940 + .saturating_add(Weight::from_parts(324_389, 0).saturating_mul(r.into())) .saturating_add(RocksDbWeight::get().reads(6_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) .saturating_add(Weight::from_parts(0, 30).saturating_mul(r.into())) @@ -2382,10 +2384,10 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `839 + r * (240 ±0)` // Estimated: `21835 + r * (3675 ±0)` - // Minimum execution time: 284_949_000 picoseconds. - Weight::from_parts(126_196_485, 21835) - // Standard Error: 6_260 - .saturating_add(Weight::from_parts(3_368_849, 0).saturating_mul(r.into())) + // Minimum execution time: 235_635_000 picoseconds. + Weight::from_parts(76_942_144, 21835) + // Standard Error: 6_214 + .saturating_add(Weight::from_parts(3_328_756, 0).saturating_mul(r.into())) .saturating_add(RocksDbWeight::get().reads(6_u64)) .saturating_add(RocksDbWeight::get().reads((1_u64).saturating_mul(r.into()))) .saturating_add(RocksDbWeight::get().writes(3_u64)) @@ -2406,10 +2408,10 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `831 + r * (244 ±0)` // Estimated: `21855 + r * (3695 ±0)` - // Minimum execution time: 286_429_000 picoseconds. - Weight::from_parts(124_820_396, 21855) - // Standard Error: 6_539 - .saturating_add(Weight::from_parts(4_163_535, 0).saturating_mul(r.into())) + // Minimum execution time: 237_123_000 picoseconds. + Weight::from_parts(77_880_739, 21855) + // Standard Error: 5_970 + .saturating_add(Weight::from_parts(4_103_281, 0).saturating_mul(r.into())) .saturating_add(RocksDbWeight::get().reads(6_u64)) .saturating_add(RocksDbWeight::get().reads((1_u64).saturating_mul(r.into()))) .saturating_add(RocksDbWeight::get().writes(3_u64)) @@ -2430,10 +2432,10 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `788 + r * (6 ±0)` // Estimated: `21770 + r * (30 ±0)` - // Minimum execution time: 285_042_000 picoseconds. - Weight::from_parts(288_096_303, 21770) - // Standard Error: 972 - .saturating_add(Weight::from_parts(409_782, 0).saturating_mul(r.into())) + // Minimum execution time: 236_621_000 picoseconds. + Weight::from_parts(238_240_015, 21770) + // Standard Error: 742 + .saturating_add(Weight::from_parts(404_691, 0).saturating_mul(r.into())) .saturating_add(RocksDbWeight::get().reads(6_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) .saturating_add(Weight::from_parts(0, 30).saturating_mul(r.into())) @@ -2453,10 +2455,10 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `778 + r * (3 ±0)` // Estimated: `21735 + r * (15 ±0)` - // Minimum execution time: 282_820_000 picoseconds. - Weight::from_parts(287_104_710, 21735) - // Standard Error: 421 - .saturating_add(Weight::from_parts(167_907, 0).saturating_mul(r.into())) + // Minimum execution time: 233_274_000 picoseconds. + Weight::from_parts(239_596_227, 21735) + // Standard Error: 551 + .saturating_add(Weight::from_parts(164_429, 0).saturating_mul(r.into())) .saturating_add(RocksDbWeight::get().reads(6_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) .saturating_add(Weight::from_parts(0, 15).saturating_mul(r.into())) @@ -2476,10 +2478,10 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `782 + r * (6 ±0)` // Estimated: `21740 + r * (30 ±0)` - // Minimum execution time: 284_619_000 picoseconds. - Weight::from_parts(281_326_785, 21740) - // Standard Error: 1_379 - .saturating_add(Weight::from_parts(342_779, 0).saturating_mul(r.into())) + // Minimum execution time: 235_661_000 picoseconds. + Weight::from_parts(239_063_406, 21740) + // Standard Error: 933 + .saturating_add(Weight::from_parts(327_679, 0).saturating_mul(r.into())) .saturating_add(RocksDbWeight::get().reads(6_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) .saturating_add(Weight::from_parts(0, 30).saturating_mul(r.into())) @@ -2499,10 +2501,10 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `783 + r * (6 ±0)` // Estimated: `21725 + r * (30 ±0)` - // Minimum execution time: 284_703_000 picoseconds. - Weight::from_parts(289_479_932, 21725) - // Standard Error: 745 - .saturating_add(Weight::from_parts(323_625, 0).saturating_mul(r.into())) + // Minimum execution time: 235_583_000 picoseconds. + Weight::from_parts(251_641_549, 21725) + // Standard Error: 1_104 + .saturating_add(Weight::from_parts(315_873, 0).saturating_mul(r.into())) .saturating_add(RocksDbWeight::get().reads(6_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) .saturating_add(Weight::from_parts(0, 30).saturating_mul(r.into())) @@ -2522,10 +2524,10 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `922 + r * (6 ±0)` // Estimated: `24633 + r * (30 ±0)` - // Minimum execution time: 283_610_000 picoseconds. - Weight::from_parts(299_901_534, 24633) - // Standard Error: 1_177 - .saturating_add(Weight::from_parts(1_474_603, 0).saturating_mul(r.into())) + // Minimum execution time: 235_325_000 picoseconds. + Weight::from_parts(256_582_010, 24633) + // Standard Error: 1_349 + .saturating_add(Weight::from_parts(1_483_116, 0).saturating_mul(r.into())) .saturating_add(RocksDbWeight::get().reads(7_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) .saturating_add(Weight::from_parts(0, 30).saturating_mul(r.into())) @@ -2545,10 +2547,10 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `792 + r * (6 ±0)` // Estimated: `21825 + r * (30 ±0)` - // Minimum execution time: 284_474_000 picoseconds. - Weight::from_parts(283_540_273, 21825) - // Standard Error: 1_164 - .saturating_add(Weight::from_parts(339_262, 0).saturating_mul(r.into())) + // Minimum execution time: 235_358_000 picoseconds. + Weight::from_parts(233_421_484, 21825) + // Standard Error: 1_178 + .saturating_add(Weight::from_parts(337_947, 0).saturating_mul(r.into())) .saturating_add(RocksDbWeight::get().reads(6_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) .saturating_add(Weight::from_parts(0, 30).saturating_mul(r.into())) @@ -2568,10 +2570,10 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `790 + r * (6 ±0)` // Estimated: `21815 + r * (30 ±0)` - // Minimum execution time: 284_521_000 picoseconds. - Weight::from_parts(285_747_754, 21815) - // Standard Error: 889 - .saturating_add(Weight::from_parts(326_428, 0).saturating_mul(r.into())) + // Minimum execution time: 236_570_000 picoseconds. + Weight::from_parts(245_853_078, 21815) + // Standard Error: 1_947 + .saturating_add(Weight::from_parts(319_972, 0).saturating_mul(r.into())) .saturating_add(RocksDbWeight::get().reads(6_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) .saturating_add(Weight::from_parts(0, 30).saturating_mul(r.into())) @@ -2591,10 +2593,10 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `787 + r * (6 ±0)` // Estimated: `21805 + r * (30 ±0)` - // Minimum execution time: 284_103_000 picoseconds. - Weight::from_parts(283_801_256, 21805) - // Standard Error: 1_051 - .saturating_add(Weight::from_parts(334_081, 0).saturating_mul(r.into())) + // Minimum execution time: 235_027_000 picoseconds. + Weight::from_parts(239_719_689, 21805) + // Standard Error: 654 + .saturating_add(Weight::from_parts(326_988, 0).saturating_mul(r.into())) .saturating_add(RocksDbWeight::get().reads(6_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) .saturating_add(Weight::from_parts(0, 30).saturating_mul(r.into())) @@ -2614,10 +2616,10 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `778 + r * (6 ±0)` // Estimated: `21735 + r * (30 ±0)` - // Minimum execution time: 284_187_000 picoseconds. - Weight::from_parts(289_414_364, 21735) - // Standard Error: 796 - .saturating_add(Weight::from_parts(324_603, 0).saturating_mul(r.into())) + // Minimum execution time: 236_547_000 picoseconds. + Weight::from_parts(239_390_326, 21735) + // Standard Error: 912 + .saturating_add(Weight::from_parts(327_495, 0).saturating_mul(r.into())) .saturating_add(RocksDbWeight::get().reads(6_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) .saturating_add(Weight::from_parts(0, 30).saturating_mul(r.into())) @@ -2639,10 +2641,10 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `856 + r * (10 ±0)` // Estimated: `24446 + r * (60 ±0)` - // Minimum execution time: 284_953_000 picoseconds. - Weight::from_parts(290_535_752, 24446) - // Standard Error: 2_462 - .saturating_add(Weight::from_parts(1_361_518, 0).saturating_mul(r.into())) + // Minimum execution time: 235_710_000 picoseconds. + Weight::from_parts(238_998_789, 24446) + // Standard Error: 2_055 + .saturating_add(Weight::from_parts(1_373_992, 0).saturating_mul(r.into())) .saturating_add(RocksDbWeight::get().reads(7_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) .saturating_add(Weight::from_parts(0, 60).saturating_mul(r.into())) @@ -2662,10 +2664,10 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `745 + r * (4 ±0)` // Estimated: `21555 + r * (20 ±0)` - // Minimum execution time: 160_775_000 picoseconds. - Weight::from_parts(164_652_364, 21555) - // Standard Error: 284 - .saturating_add(Weight::from_parts(132_574, 0).saturating_mul(r.into())) + // Minimum execution time: 161_133_000 picoseconds. + Weight::from_parts(167_097_346, 21555) + // Standard Error: 245 + .saturating_add(Weight::from_parts(128_503, 0).saturating_mul(r.into())) .saturating_add(RocksDbWeight::get().reads(6_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) .saturating_add(Weight::from_parts(0, 20).saturating_mul(r.into())) @@ -2685,10 +2687,10 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `780 + r * (6 ±0)` // Estimated: `21740 + r * (30 ±0)` - // Minimum execution time: 284_072_000 picoseconds. - Weight::from_parts(288_418_644, 21740) - // Standard Error: 792 - .saturating_add(Weight::from_parts(272_881, 0).saturating_mul(r.into())) + // Minimum execution time: 234_790_000 picoseconds. + Weight::from_parts(242_392_710, 21740) + // Standard Error: 2_506 + .saturating_add(Weight::from_parts(273_470, 0).saturating_mul(r.into())) .saturating_add(RocksDbWeight::get().reads(6_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) .saturating_add(Weight::from_parts(0, 30).saturating_mul(r.into())) @@ -2708,10 +2710,10 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `784` // Estimated: `21740` - // Minimum execution time: 286_671_000 picoseconds. - Weight::from_parts(292_151_662, 21740) - // Standard Error: 1 - .saturating_add(Weight::from_parts(638, 0).saturating_mul(n.into())) + // Minimum execution time: 236_837_000 picoseconds. + Weight::from_parts(237_860_073, 21740) + // Standard Error: 2 + .saturating_add(Weight::from_parts(602, 0).saturating_mul(n.into())) .saturating_add(RocksDbWeight::get().reads(6_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) } @@ -2730,10 +2732,10 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `768 + r * (45 ±0)` // Estimated: `21660 + r * (225 ±0)` - // Minimum execution time: 280_334_000 picoseconds. - Weight::from_parts(283_487_571, 21660) - // Standard Error: 267_797 - .saturating_add(Weight::from_parts(3_803_128, 0).saturating_mul(r.into())) + // Minimum execution time: 232_047_000 picoseconds. + Weight::from_parts(234_629_293, 21660) + // Standard Error: 171_808 + .saturating_add(Weight::from_parts(663_306, 0).saturating_mul(r.into())) .saturating_add(RocksDbWeight::get().reads(6_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) .saturating_add(Weight::from_parts(0, 225).saturating_mul(r.into())) @@ -2753,10 +2755,10 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `778` // Estimated: `21775` - // Minimum execution time: 284_004_000 picoseconds. - Weight::from_parts(283_681_350, 21775) + // Minimum execution time: 235_035_000 picoseconds. + Weight::from_parts(234_442_091, 21775) // Standard Error: 1 - .saturating_add(Weight::from_parts(232, 0).saturating_mul(n.into())) + .saturating_add(Weight::from_parts(185, 0).saturating_mul(n.into())) .saturating_add(RocksDbWeight::get().reads(6_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) } @@ -2779,10 +2781,10 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `810 + r * (356 ±0)` // Estimated: `25511 + r * (15321 ±0)` - // Minimum execution time: 284_143_000 picoseconds. - Weight::from_parts(287_218_324, 25511) - // Standard Error: 343_611 - .saturating_add(Weight::from_parts(109_895_675, 0).saturating_mul(r.into())) + // Minimum execution time: 234_140_000 picoseconds. + Weight::from_parts(236_805_906, 25511) + // Standard Error: 435_181 + .saturating_add(Weight::from_parts(118_144_693, 0).saturating_mul(r.into())) .saturating_add(RocksDbWeight::get().reads(6_u64)) .saturating_add(RocksDbWeight::get().reads((6_u64).saturating_mul(r.into()))) .saturating_add(RocksDbWeight::get().writes(3_u64)) @@ -2806,10 +2808,10 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `825 + r * (10 ±0)` // Estimated: `24283 + r * (60 ±0)` - // Minimum execution time: 285_037_000 picoseconds. - Weight::from_parts(299_804_606, 24283) - // Standard Error: 5_518 - .saturating_add(Weight::from_parts(1_848_164, 0).saturating_mul(r.into())) + // Minimum execution time: 235_271_000 picoseconds. + Weight::from_parts(256_019_682, 24283) + // Standard Error: 2_016 + .saturating_add(Weight::from_parts(1_862_085, 0).saturating_mul(r.into())) .saturating_add(RocksDbWeight::get().reads(7_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) .saturating_add(Weight::from_parts(0, 60).saturating_mul(r.into())) @@ -2829,10 +2831,10 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `778 + r * (10 ±0)` // Estimated: `21735 + r * (50 ±0)` - // Minimum execution time: 282_886_000 picoseconds. - Weight::from_parts(293_171_736, 21735) - // Standard Error: 2_171 - .saturating_add(Weight::from_parts(3_491_303, 0).saturating_mul(r.into())) + // Minimum execution time: 233_092_000 picoseconds. + Weight::from_parts(248_483_473, 21735) + // Standard Error: 2_182 + .saturating_add(Weight::from_parts(3_551_674, 0).saturating_mul(r.into())) .saturating_add(RocksDbWeight::get().reads(6_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) .saturating_add(Weight::from_parts(0, 50).saturating_mul(r.into())) @@ -2853,12 +2855,12 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `797 + t * (32 ±0)` // Estimated: `21840 + t * (2640 ±0)` - // Minimum execution time: 300_675_000 picoseconds. - Weight::from_parts(296_092_420, 21840) - // Standard Error: 130_733 - .saturating_add(Weight::from_parts(2_487_957, 0).saturating_mul(t.into())) - // Standard Error: 36 - .saturating_add(Weight::from_parts(565, 0).saturating_mul(n.into())) + // Minimum execution time: 252_307_000 picoseconds. + Weight::from_parts(245_237_726, 21840) + // Standard Error: 79_824 + .saturating_add(Weight::from_parts(2_364_618, 0).saturating_mul(t.into())) + // Standard Error: 22 + .saturating_add(Weight::from_parts(636, 0).saturating_mul(n.into())) .saturating_add(RocksDbWeight::get().reads(6_u64)) .saturating_add(RocksDbWeight::get().reads((1_u64).saturating_mul(t.into()))) .saturating_add(RocksDbWeight::get().writes(3_u64)) @@ -2880,10 +2882,10 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `777 + r * (7 ±0)` // Estimated: `21725 + r * (35 ±0)` - // Minimum execution time: 166_638_000 picoseconds. - Weight::from_parts(171_353_083, 21725) - // Standard Error: 550 - .saturating_add(Weight::from_parts(238_768, 0).saturating_mul(r.into())) + // Minimum execution time: 165_367_000 picoseconds. + Weight::from_parts(170_164_725, 21725) + // Standard Error: 487 + .saturating_add(Weight::from_parts(237_281, 0).saturating_mul(r.into())) .saturating_add(RocksDbWeight::get().reads(6_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) .saturating_add(Weight::from_parts(0, 35).saturating_mul(r.into())) @@ -2903,10 +2905,10 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `125728` // Estimated: `269977` - // Minimum execution time: 414_136_000 picoseconds. - Weight::from_parts(416_093_921, 269977) - // Standard Error: 3 - .saturating_add(Weight::from_parts(794, 0).saturating_mul(i.into())) + // Minimum execution time: 350_914_000 picoseconds. + Weight::from_parts(354_461_646, 269977) + // Standard Error: 1 + .saturating_add(Weight::from_parts(747, 0).saturating_mul(i.into())) .saturating_add(RocksDbWeight::get().reads(6_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) } @@ -2917,10 +2919,10 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `845 + r * (292 ±0)` // Estimated: `843 + r * (293 ±0)` - // Minimum execution time: 285_920_000 picoseconds. - Weight::from_parts(184_945_789, 843) - // Standard Error: 9_604 - .saturating_add(Weight::from_parts(6_012_522, 0).saturating_mul(r.into())) + // Minimum execution time: 236_273_000 picoseconds. + Weight::from_parts(137_922_946, 843) + // Standard Error: 10_363 + .saturating_add(Weight::from_parts(6_034_776, 0).saturating_mul(r.into())) .saturating_add(RocksDbWeight::get().reads(6_u64)) .saturating_add(RocksDbWeight::get().reads((1_u64).saturating_mul(r.into()))) .saturating_add(RocksDbWeight::get().writes(3_u64)) @@ -2934,10 +2936,10 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `1304` // Estimated: `1280` - // Minimum execution time: 299_772_000 picoseconds. - Weight::from_parts(333_451_106, 1280) - // Standard Error: 54 - .saturating_add(Weight::from_parts(579, 0).saturating_mul(n.into())) + // Minimum execution time: 251_462_000 picoseconds. + Weight::from_parts(287_009_907, 1280) + // Standard Error: 62 + .saturating_add(Weight::from_parts(384, 0).saturating_mul(n.into())) .saturating_add(RocksDbWeight::get().reads(8_u64)) .saturating_add(RocksDbWeight::get().writes(6_u64)) } @@ -2948,10 +2950,10 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `1167 + n * (1 ±0)` // Estimated: `1167 + n * (1 ±0)` - // Minimum execution time: 299_279_000 picoseconds. - Weight::from_parts(302_336_567, 1167) - // Standard Error: 25 - .saturating_add(Weight::from_parts(86, 0).saturating_mul(n.into())) + // Minimum execution time: 250_985_000 picoseconds. + Weight::from_parts(253_693_249, 1167) + // Standard Error: 21 + .saturating_add(Weight::from_parts(92, 0).saturating_mul(n.into())) .saturating_add(RocksDbWeight::get().reads(7_u64)) .saturating_add(RocksDbWeight::get().writes(4_u64)) .saturating_add(Weight::from_parts(0, 1).saturating_mul(n.into())) @@ -2963,10 +2965,10 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `841 + r * (288 ±0)` // Estimated: `845 + r * (289 ±0)` - // Minimum execution time: 284_689_000 picoseconds. - Weight::from_parts(185_207_302, 845) - // Standard Error: 10_030 - .saturating_add(Weight::from_parts(5_871_325, 0).saturating_mul(r.into())) + // Minimum execution time: 235_462_000 picoseconds. + Weight::from_parts(141_240_297, 845) + // Standard Error: 9_687 + .saturating_add(Weight::from_parts(5_906_737, 0).saturating_mul(r.into())) .saturating_add(RocksDbWeight::get().reads(6_u64)) .saturating_add(RocksDbWeight::get().reads((1_u64).saturating_mul(r.into()))) .saturating_add(RocksDbWeight::get().writes(3_u64)) @@ -2980,10 +2982,10 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `1163 + n * (1 ±0)` // Estimated: `1163 + n * (1 ±0)` - // Minimum execution time: 299_364_000 picoseconds. - Weight::from_parts(302_089_070, 1163) - // Standard Error: 23 - .saturating_add(Weight::from_parts(128, 0).saturating_mul(n.into())) + // Minimum execution time: 250_887_000 picoseconds. + Weight::from_parts(253_321_064, 1163) + // Standard Error: 28 + .saturating_add(Weight::from_parts(169, 0).saturating_mul(n.into())) .saturating_add(RocksDbWeight::get().reads(7_u64)) .saturating_add(RocksDbWeight::get().writes(4_u64)) .saturating_add(Weight::from_parts(0, 1).saturating_mul(n.into())) @@ -2995,10 +2997,10 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `835 + r * (296 ±0)` // Estimated: `840 + r * (297 ±0)` - // Minimum execution time: 285_175_000 picoseconds. - Weight::from_parts(200_262_957, 840) - // Standard Error: 8_681 - .saturating_add(Weight::from_parts(4_899_266, 0).saturating_mul(r.into())) + // Minimum execution time: 236_542_000 picoseconds. + Weight::from_parts(147_992_508, 840) + // Standard Error: 8_849 + .saturating_add(Weight::from_parts(4_946_692, 0).saturating_mul(r.into())) .saturating_add(RocksDbWeight::get().reads(6_u64)) .saturating_add(RocksDbWeight::get().reads((1_u64).saturating_mul(r.into()))) .saturating_add(RocksDbWeight::get().writes(3_u64)) @@ -3011,10 +3013,10 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `1179 + n * (1 ±0)` // Estimated: `1179 + n * (1 ±0)` - // Minimum execution time: 299_459_000 picoseconds. - Weight::from_parts(302_451_160, 1179) - // Standard Error: 36 - .saturating_add(Weight::from_parts(731, 0).saturating_mul(n.into())) + // Minimum execution time: 249_987_000 picoseconds. + Weight::from_parts(254_866_627, 1179) + // Standard Error: 53 + .saturating_add(Weight::from_parts(554, 0).saturating_mul(n.into())) .saturating_add(RocksDbWeight::get().reads(7_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) .saturating_add(Weight::from_parts(0, 1).saturating_mul(n.into())) @@ -3026,10 +3028,10 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `856 + r * (288 ±0)` // Estimated: `857 + r * (289 ±0)` - // Minimum execution time: 286_384_000 picoseconds. - Weight::from_parts(203_389_467, 857) - // Standard Error: 8_817 - .saturating_add(Weight::from_parts(4_692_347, 0).saturating_mul(r.into())) + // Minimum execution time: 236_635_000 picoseconds. + Weight::from_parts(157_805_789, 857) + // Standard Error: 7_699 + .saturating_add(Weight::from_parts(4_709_422, 0).saturating_mul(r.into())) .saturating_add(RocksDbWeight::get().reads(6_u64)) .saturating_add(RocksDbWeight::get().reads((1_u64).saturating_mul(r.into()))) .saturating_add(RocksDbWeight::get().writes(3_u64)) @@ -3042,10 +3044,10 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `1166 + n * (1 ±0)` // Estimated: `1166 + n * (1 ±0)` - // Minimum execution time: 297_450_000 picoseconds. - Weight::from_parts(300_459_851, 1166) - // Standard Error: 39 - .saturating_add(Weight::from_parts(108, 0).saturating_mul(n.into())) + // Minimum execution time: 252_660_000 picoseconds. + Weight::from_parts(255_250_747, 1166) + // Standard Error: 14 + .saturating_add(Weight::from_parts(133, 0).saturating_mul(n.into())) .saturating_add(RocksDbWeight::get().reads(7_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) .saturating_add(Weight::from_parts(0, 1).saturating_mul(n.into())) @@ -3057,10 +3059,10 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `829 + r * (296 ±0)` // Estimated: `836 + r * (297 ±0)` - // Minimum execution time: 285_572_000 picoseconds. - Weight::from_parts(182_642_557, 836) - // Standard Error: 9_977 - .saturating_add(Weight::from_parts(6_090_684, 0).saturating_mul(r.into())) + // Minimum execution time: 240_198_000 picoseconds. + Weight::from_parts(133_188_357, 836) + // Standard Error: 10_661 + .saturating_add(Weight::from_parts(6_147_538, 0).saturating_mul(r.into())) .saturating_add(RocksDbWeight::get().reads(6_u64)) .saturating_add(RocksDbWeight::get().reads((1_u64).saturating_mul(r.into()))) .saturating_add(RocksDbWeight::get().writes(3_u64)) @@ -3074,10 +3076,10 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `1180 + n * (1 ±0)` // Estimated: `1180 + n * (1 ±0)` - // Minimum execution time: 301_344_000 picoseconds. - Weight::from_parts(303_770_522, 1180) - // Standard Error: 29 - .saturating_add(Weight::from_parts(807, 0).saturating_mul(n.into())) + // Minimum execution time: 252_131_000 picoseconds. + Weight::from_parts(259_960_286, 1180) + // Standard Error: 121 + .saturating_add(Weight::from_parts(192, 0).saturating_mul(n.into())) .saturating_add(RocksDbWeight::get().reads(7_u64)) .saturating_add(RocksDbWeight::get().writes(4_u64)) .saturating_add(Weight::from_parts(0, 1).saturating_mul(n.into())) @@ -3097,10 +3099,10 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `1373 + r * (45 ±0)` // Estimated: `26753 + r * (2700 ±0)` - // Minimum execution time: 286_835_000 picoseconds. - Weight::from_parts(245_206_457, 26753) - // Standard Error: 73_782 - .saturating_add(Weight::from_parts(36_414_448, 0).saturating_mul(r.into())) + // Minimum execution time: 235_860_000 picoseconds. + Weight::from_parts(124_993_651, 26753) + // Standard Error: 22_811 + .saturating_add(Weight::from_parts(36_467_740, 0).saturating_mul(r.into())) .saturating_add(RocksDbWeight::get().reads(7_u64)) .saturating_add(RocksDbWeight::get().reads((1_u64).saturating_mul(r.into()))) .saturating_add(RocksDbWeight::get().writes(4_u64)) @@ -3122,10 +3124,10 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `1237 + r * (256 ±0)` // Estimated: `26028 + r * (6235 ±0)` - // Minimum execution time: 287_184_000 picoseconds. - Weight::from_parts(287_525_000, 26028) - // Standard Error: 66_791 - .saturating_add(Weight::from_parts(261_473_539, 0).saturating_mul(r.into())) + // Minimum execution time: 237_221_000 picoseconds. + Weight::from_parts(237_632_000, 26028) + // Standard Error: 89_502 + .saturating_add(Weight::from_parts(212_211_534, 0).saturating_mul(r.into())) .saturating_add(RocksDbWeight::get().reads(7_u64)) .saturating_add(RocksDbWeight::get().reads((2_u64).saturating_mul(r.into()))) .saturating_add(RocksDbWeight::get().writes(3_u64)) @@ -3146,11 +3148,11 @@ impl WeightInfo for () { fn seal_delegate_call(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0 + r * (502 ±0)` - // Estimated: `21755 + r * (6329 ±10)` - // Minimum execution time: 285_759_000 picoseconds. - Weight::from_parts(286_643_000, 21755) - // Standard Error: 133_180 - .saturating_add(Weight::from_parts(257_186_897, 0).saturating_mul(r.into())) + // Estimated: `21755 + r * (6329 ±3)` + // Minimum execution time: 236_965_000 picoseconds. + Weight::from_parts(238_110_000, 21755) + // Standard Error: 101_332 + .saturating_add(Weight::from_parts(206_790_203, 0).saturating_mul(r.into())) .saturating_add(RocksDbWeight::get().reads(6_u64)) .saturating_add(RocksDbWeight::get().reads((2_u64).saturating_mul(r.into()))) .saturating_add(RocksDbWeight::get().writes(3_u64)) @@ -3173,12 +3175,12 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `1154 + t * (204 ±0)` // Estimated: `31015 + t * (5970 ±0)` - // Minimum execution time: 459_675_000 picoseconds. - Weight::from_parts(427_010_987, 31015) - // Standard Error: 1_277_377 - .saturating_add(Weight::from_parts(36_899_889, 0).saturating_mul(t.into())) + // Minimum execution time: 411_974_000 picoseconds. + Weight::from_parts(391_387_689, 31015) + // Standard Error: 1_320_695 + .saturating_add(Weight::from_parts(29_766_122, 0).saturating_mul(t.into())) // Standard Error: 1 - .saturating_add(Weight::from_parts(651, 0).saturating_mul(c.into())) + .saturating_add(Weight::from_parts(597, 0).saturating_mul(c.into())) .saturating_add(RocksDbWeight::get().reads(9_u64)) .saturating_add(RocksDbWeight::get().reads((2_u64).saturating_mul(t.into()))) .saturating_add(RocksDbWeight::get().writes(5_u64)) @@ -3204,10 +3206,10 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `1301 + r * (254 ±0)` // Estimated: `30977 + r * (16635 ±0)` - // Minimum execution time: 285_816_000 picoseconds. - Weight::from_parts(286_349_000, 30977) - // Standard Error: 269_144 - .saturating_add(Weight::from_parts(394_282_520, 0).saturating_mul(r.into())) + // Minimum execution time: 237_595_000 picoseconds. + Weight::from_parts(238_068_000, 30977) + // Standard Error: 254_409 + .saturating_add(Weight::from_parts(346_436_154, 0).saturating_mul(r.into())) .saturating_add(RocksDbWeight::get().reads(8_u64)) .saturating_add(RocksDbWeight::get().reads((6_u64).saturating_mul(r.into()))) .saturating_add(RocksDbWeight::get().writes(6_u64)) @@ -3235,14 +3237,14 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `1071 + t * (187 ±0)` // Estimated: `42684 + t * (3588 ±2)` - // Minimum execution time: 1_708_330_000 picoseconds. - Weight::from_parts(395_059_764, 42684) - // Standard Error: 4_545_552 - .saturating_add(Weight::from_parts(114_039_862, 0).saturating_mul(t.into())) + // Minimum execution time: 1_616_768_000 picoseconds. + Weight::from_parts(363_003_254, 42684) + // Standard Error: 4_398_669 + .saturating_add(Weight::from_parts(104_529_967, 0).saturating_mul(t.into())) // Standard Error: 7 - .saturating_add(Weight::from_parts(1_213, 0).saturating_mul(i.into())) + .saturating_add(Weight::from_parts(1_162, 0).saturating_mul(i.into())) // Standard Error: 7 - .saturating_add(Weight::from_parts(1_379, 0).saturating_mul(s.into())) + .saturating_add(Weight::from_parts(1_333, 0).saturating_mul(s.into())) .saturating_add(RocksDbWeight::get().reads(13_u64)) .saturating_add(RocksDbWeight::get().reads((1_u64).saturating_mul(t.into()))) .saturating_add(RocksDbWeight::get().writes(10_u64)) @@ -3264,10 +3266,10 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `777 + r * (8 ±0)` // Estimated: `21710 + r * (40 ±0)` - // Minimum execution time: 283_738_000 picoseconds. - Weight::from_parts(289_885_978, 21710) - // Standard Error: 1_057 - .saturating_add(Weight::from_parts(575_432, 0).saturating_mul(r.into())) + // Minimum execution time: 233_814_000 picoseconds. + Weight::from_parts(241_291_041, 21710) + // Standard Error: 1_422 + .saturating_add(Weight::from_parts(575_846, 0).saturating_mul(r.into())) .saturating_add(RocksDbWeight::get().reads(6_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) .saturating_add(Weight::from_parts(0, 40).saturating_mul(r.into())) @@ -3287,10 +3289,10 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `785` // Estimated: `21745` - // Minimum execution time: 285_070_000 picoseconds. - Weight::from_parts(283_987_687, 21745) - // Standard Error: 6 - .saturating_add(Weight::from_parts(4_008, 0).saturating_mul(n.into())) + // Minimum execution time: 236_572_000 picoseconds. + Weight::from_parts(235_648_055, 21745) + // Standard Error: 3 + .saturating_add(Weight::from_parts(3_947, 0).saturating_mul(n.into())) .saturating_add(RocksDbWeight::get().reads(6_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) } @@ -3309,10 +3311,10 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `779 + r * (8 ±0)` // Estimated: `21725 + r * (40 ±0)` - // Minimum execution time: 281_613_000 picoseconds. - Weight::from_parts(285_429_053, 21725) - // Standard Error: 1_164 - .saturating_add(Weight::from_parts(756_244, 0).saturating_mul(r.into())) + // Minimum execution time: 234_473_000 picoseconds. + Weight::from_parts(239_805_309, 21725) + // Standard Error: 1_113 + .saturating_add(Weight::from_parts(752_507, 0).saturating_mul(r.into())) .saturating_add(RocksDbWeight::get().reads(6_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) .saturating_add(Weight::from_parts(0, 40).saturating_mul(r.into())) @@ -3332,10 +3334,10 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `787` // Estimated: `21765` - // Minimum execution time: 284_593_000 picoseconds. - Weight::from_parts(278_467_111, 21765) + // Minimum execution time: 235_209_000 picoseconds. + Weight::from_parts(228_548_524, 21765) // Standard Error: 2 - .saturating_add(Weight::from_parts(3_217, 0).saturating_mul(n.into())) + .saturating_add(Weight::from_parts(3_171, 0).saturating_mul(n.into())) .saturating_add(RocksDbWeight::get().reads(6_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) } @@ -3354,10 +3356,10 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `779 + r * (8 ±0)` // Estimated: `21740 + r * (40 ±0)` - // Minimum execution time: 281_759_000 picoseconds. - Weight::from_parts(288_807_137, 21740) - // Standard Error: 805 - .saturating_add(Weight::from_parts(424_378, 0).saturating_mul(r.into())) + // Minimum execution time: 233_282_000 picoseconds. + Weight::from_parts(240_864_680, 21740) + // Standard Error: 958 + .saturating_add(Weight::from_parts(418_308, 0).saturating_mul(r.into())) .saturating_add(RocksDbWeight::get().reads(6_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) .saturating_add(Weight::from_parts(0, 40).saturating_mul(r.into())) @@ -3377,10 +3379,10 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `787` // Estimated: `21785` - // Minimum execution time: 282_666_000 picoseconds. - Weight::from_parts(274_357_944, 21785) + // Minimum execution time: 234_667_000 picoseconds. + Weight::from_parts(227_810_077, 21785) // Standard Error: 2 - .saturating_add(Weight::from_parts(974, 0).saturating_mul(n.into())) + .saturating_add(Weight::from_parts(925, 0).saturating_mul(n.into())) .saturating_add(RocksDbWeight::get().reads(6_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) } @@ -3399,10 +3401,10 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `779 + r * (8 ±0)` // Estimated: `21745 + r * (40 ±0)` - // Minimum execution time: 285_073_000 picoseconds. - Weight::from_parts(287_226_796, 21745) - // Standard Error: 951 - .saturating_add(Weight::from_parts(425_368, 0).saturating_mul(r.into())) + // Minimum execution time: 234_040_000 picoseconds. + Weight::from_parts(237_970_694, 21745) + // Standard Error: 979 + .saturating_add(Weight::from_parts(416_562, 0).saturating_mul(r.into())) .saturating_add(RocksDbWeight::get().reads(6_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) .saturating_add(Weight::from_parts(0, 40).saturating_mul(r.into())) @@ -3422,10 +3424,10 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `787` // Estimated: `21755` - // Minimum execution time: 283_407_000 picoseconds. - Weight::from_parts(276_737_242, 21755) + // Minimum execution time: 234_840_000 picoseconds. + Weight::from_parts(227_849_778, 21755) // Standard Error: 2 - .saturating_add(Weight::from_parts(967, 0).saturating_mul(n.into())) + .saturating_add(Weight::from_parts(923, 0).saturating_mul(n.into())) .saturating_add(RocksDbWeight::get().reads(6_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) } @@ -3444,10 +3446,10 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `822 + r * (76 ±0)` // Estimated: `21705 + r * (385 ±0)` - // Minimum execution time: 285_130_000 picoseconds. - Weight::from_parts(299_449_202, 21705) - // Standard Error: 16_535 - .saturating_add(Weight::from_parts(37_655_189, 0).saturating_mul(r.into())) + // Minimum execution time: 236_174_000 picoseconds. + Weight::from_parts(252_457_690, 21705) + // Standard Error: 20_130 + .saturating_add(Weight::from_parts(37_792_805, 0).saturating_mul(r.into())) .saturating_add(RocksDbWeight::get().reads(6_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) .saturating_add(Weight::from_parts(0, 385).saturating_mul(r.into())) @@ -3467,10 +3469,10 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `792 + r * (42 ±0)` // Estimated: `21780 + r * (210 ±0)` - // Minimum execution time: 284_494_000 picoseconds. - Weight::from_parts(282_154_339, 21780) - // Standard Error: 12_278 - .saturating_add(Weight::from_parts(9_501_559, 0).saturating_mul(r.into())) + // Minimum execution time: 236_251_000 picoseconds. + Weight::from_parts(242_254_305, 21780) + // Standard Error: 9_765 + .saturating_add(Weight::from_parts(9_341_334, 0).saturating_mul(r.into())) .saturating_add(RocksDbWeight::get().reads(6_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) .saturating_add(Weight::from_parts(0, 210).saturating_mul(r.into())) @@ -3492,10 +3494,10 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `0 + r * (964 ±0)` // Estimated: `29920 + r * (11544 ±7)` - // Minimum execution time: 285_306_000 picoseconds. - Weight::from_parts(286_080_000, 29920) - // Standard Error: 43_813 - .saturating_add(Weight::from_parts(21_758_329, 0).saturating_mul(r.into())) + // Minimum execution time: 236_462_000 picoseconds. + Weight::from_parts(236_997_000, 29920) + // Standard Error: 46_527 + .saturating_add(Weight::from_parts(21_858_761, 0).saturating_mul(r.into())) .saturating_add(RocksDbWeight::get().reads(6_u64)) .saturating_add(RocksDbWeight::get().reads((3_u64).saturating_mul(r.into()))) .saturating_add(RocksDbWeight::get().writes(3_u64)) @@ -3517,10 +3519,10 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `773 + r * (3 ±0)` // Estimated: `21735 + r * (15 ±0)` - // Minimum execution time: 283_487_000 picoseconds. - Weight::from_parts(289_280_189, 21735) - // Standard Error: 829 - .saturating_add(Weight::from_parts(168_973, 0).saturating_mul(r.into())) + // Minimum execution time: 235_085_000 picoseconds. + Weight::from_parts(239_410_836, 21735) + // Standard Error: 414 + .saturating_add(Weight::from_parts(167_067, 0).saturating_mul(r.into())) .saturating_add(RocksDbWeight::get().reads(6_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) .saturating_add(Weight::from_parts(0, 15).saturating_mul(r.into())) @@ -3540,10 +3542,10 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `1975 + r * (39 ±0)` // Estimated: `27145 + r * (200 ±0)` - // Minimum execution time: 287_413_000 picoseconds. - Weight::from_parts(314_662_286, 27145) - // Standard Error: 1_099 - .saturating_add(Weight::from_parts(262_201, 0).saturating_mul(r.into())) + // Minimum execution time: 236_690_000 picoseconds. + Weight::from_parts(268_793_030, 27145) + // Standard Error: 1_210 + .saturating_add(Weight::from_parts(263_330, 0).saturating_mul(r.into())) .saturating_add(RocksDbWeight::get().reads(6_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) .saturating_add(Weight::from_parts(0, 200).saturating_mul(r.into())) @@ -3565,10 +3567,10 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `776 + r * (3 ±0)` // Estimated: `24004 + r * (18 ±0)` - // Minimum execution time: 282_601_000 picoseconds. - Weight::from_parts(289_374_203, 24004) - // Standard Error: 452 - .saturating_add(Weight::from_parts(142_661, 0).saturating_mul(r.into())) + // Minimum execution time: 236_289_000 picoseconds. + Weight::from_parts(246_581_099, 24004) + // Standard Error: 1_300 + .saturating_add(Weight::from_parts(137_499, 0).saturating_mul(r.into())) .saturating_add(RocksDbWeight::get().reads(7_u64)) .saturating_add(RocksDbWeight::get().writes(4_u64)) .saturating_add(Weight::from_parts(0, 18).saturating_mul(r.into())) @@ -3578,507 +3580,509 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_692_000 picoseconds. - Weight::from_parts(2_069_482, 0) - // Standard Error: 40 - .saturating_add(Weight::from_parts(2_922, 0).saturating_mul(r.into())) + // Minimum execution time: 1_679_000 picoseconds. + Weight::from_parts(1_934_194, 0) + // Standard Error: 1 + .saturating_add(Weight::from_parts(3_000, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 5000]`. fn instr_i64load(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_690_000 picoseconds. - Weight::from_parts(2_303_602, 0) - // Standard Error: 4 - .saturating_add(Weight::from_parts(6_433, 0).saturating_mul(r.into())) + // Minimum execution time: 1_872_000 picoseconds. + Weight::from_parts(2_605_712, 0) + // Standard Error: 43 + .saturating_add(Weight::from_parts(6_321, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 5000]`. fn instr_i64store(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_701_000 picoseconds. - Weight::from_parts(2_321_142, 0) - // Standard Error: 4 - .saturating_add(Weight::from_parts(6_025, 0).saturating_mul(r.into())) + // Minimum execution time: 1_837_000 picoseconds. + Weight::from_parts(2_035_143, 0) + // Standard Error: 65 + .saturating_add(Weight::from_parts(6_202, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 5000]`. fn instr_select(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_656_000 picoseconds. - Weight::from_parts(2_090_881, 0) + // Minimum execution time: 1_684_000 picoseconds. + Weight::from_parts(2_044_218, 0) // Standard Error: 5 - .saturating_add(Weight::from_parts(7_941, 0).saturating_mul(r.into())) + .saturating_add(Weight::from_parts(7_929, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 5000]`. fn instr_if(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_659_000 picoseconds. - Weight::from_parts(1_816_547, 0) - // Standard Error: 5 - .saturating_add(Weight::from_parts(10_578, 0).saturating_mul(r.into())) + // Minimum execution time: 1_635_000 picoseconds. + Weight::from_parts(2_009_851, 0) + // Standard Error: 6 + .saturating_add(Weight::from_parts(10_724, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 5000]`. fn instr_br(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_683_000 picoseconds. - Weight::from_parts(1_970_907, 0) + // Minimum execution time: 1_667_000 picoseconds. + Weight::from_parts(1_869_395, 0) // Standard Error: 12 - .saturating_add(Weight::from_parts(4_636, 0).saturating_mul(r.into())) + .saturating_add(Weight::from_parts(4_618, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 5000]`. fn instr_br_if(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_651_000 picoseconds. - Weight::from_parts(2_263_817, 0) - // Standard Error: 68 - .saturating_add(Weight::from_parts(7_529, 0).saturating_mul(r.into())) + // Minimum execution time: 1_698_000 picoseconds. + Weight::from_parts(2_184_182, 0) + // Standard Error: 16 + .saturating_add(Weight::from_parts(6_833, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 5000]`. fn instr_br_table(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_687_000 picoseconds. - Weight::from_parts(1_349_186, 0) - // Standard Error: 31 - .saturating_add(Weight::from_parts(9_732, 0).saturating_mul(r.into())) + // Minimum execution time: 1_670_000 picoseconds. + Weight::from_parts(1_471_988, 0) + // Standard Error: 30 + .saturating_add(Weight::from_parts(9_550, 0).saturating_mul(r.into())) } /// The range of component `e` is `[1, 256]`. - fn instr_br_table_per_entry(_e: u32, ) -> Weight { + fn instr_br_table_per_entry(e: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_777_000 picoseconds. - Weight::from_parts(2_036_446, 0) + // Minimum execution time: 1_778_000 picoseconds. + Weight::from_parts(1_925_086, 0) + // Standard Error: 136 + .saturating_add(Weight::from_parts(502, 0).saturating_mul(e.into())) } /// The range of component `r` is `[0, 5000]`. fn instr_call(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_686_000 picoseconds. - Weight::from_parts(464_449, 0) - // Standard Error: 383 - .saturating_add(Weight::from_parts(19_121, 0).saturating_mul(r.into())) + // Minimum execution time: 1_759_000 picoseconds. + Weight::from_parts(2_372_048, 0) + // Standard Error: 16 + .saturating_add(Weight::from_parts(17_953, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 5000]`. fn instr_call_indirect(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_855_000 picoseconds. - Weight::from_parts(3_381_585, 0) - // Standard Error: 18 - .saturating_add(Weight::from_parts(24_245, 0).saturating_mul(r.into())) + // Minimum execution time: 1_852_000 picoseconds. + Weight::from_parts(3_125_003, 0) + // Standard Error: 25 + .saturating_add(Weight::from_parts(24_218, 0).saturating_mul(r.into())) } /// The range of component `l` is `[0, 1024]`. fn instr_call_per_local(l: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_792_000 picoseconds. - Weight::from_parts(2_006_024, 0) - // Standard Error: 16 - .saturating_add(Weight::from_parts(2_181, 0).saturating_mul(l.into())) + // Minimum execution time: 1_795_000 picoseconds. + Weight::from_parts(2_121_763, 0) + // Standard Error: 45 + .saturating_add(Weight::from_parts(1_207, 0).saturating_mul(l.into())) } /// The range of component `r` is `[0, 5000]`. fn instr_local_get(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 3_918_000 picoseconds. - Weight::from_parts(4_618_761, 0) - // Standard Error: 49 - .saturating_add(Weight::from_parts(2_312, 0).saturating_mul(r.into())) + // Minimum execution time: 3_019_000 picoseconds. + Weight::from_parts(3_267_108, 0) + // Standard Error: 55 + .saturating_add(Weight::from_parts(2_527, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 5000]`. fn instr_local_set(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 3_889_000 picoseconds. - Weight::from_parts(4_151_280, 0) + // Minimum execution time: 2_944_000 picoseconds. + Weight::from_parts(3_183_331, 0) // Standard Error: 2 - .saturating_add(Weight::from_parts(3_623, 0).saturating_mul(r.into())) + .saturating_add(Weight::from_parts(3_618, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 5000]`. fn instr_local_tee(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 3_880_000 picoseconds. - Weight::from_parts(4_225_780, 0) - // Standard Error: 3 - .saturating_add(Weight::from_parts(3_847, 0).saturating_mul(r.into())) + // Minimum execution time: 3_009_000 picoseconds. + Weight::from_parts(3_178_158, 0) + // Standard Error: 41 + .saturating_add(Weight::from_parts(4_075, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 5000]`. fn instr_global_get(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_765_000 picoseconds. - Weight::from_parts(2_216_674, 0) - // Standard Error: 4 - .saturating_add(Weight::from_parts(8_393, 0).saturating_mul(r.into())) + // Minimum execution time: 1_748_000 picoseconds. + Weight::from_parts(2_371_911, 0) + // Standard Error: 10 + .saturating_add(Weight::from_parts(8_378, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 5000]`. fn instr_global_set(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_764_000 picoseconds. - Weight::from_parts(2_246_735, 0) - // Standard Error: 5 - .saturating_add(Weight::from_parts(8_877, 0).saturating_mul(r.into())) + // Minimum execution time: 1_795_000 picoseconds. + Weight::from_parts(1_311_997, 0) + // Standard Error: 157 + .saturating_add(Weight::from_parts(9_410, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 5000]`. fn instr_memory_current(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_758_000 picoseconds. - Weight::from_parts(1_922_386, 0) - // Standard Error: 94 - .saturating_add(Weight::from_parts(3_868, 0).saturating_mul(r.into())) + // Minimum execution time: 2_059_000 picoseconds. + Weight::from_parts(2_416_611, 0) + // Standard Error: 5 + .saturating_add(Weight::from_parts(3_775, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 16]`. fn instr_memory_grow(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_635_000 picoseconds. - Weight::from_parts(1_118_785, 0) - // Standard Error: 134_978 - .saturating_add(Weight::from_parts(16_343_664, 0).saturating_mul(r.into())) + // Minimum execution time: 1_764_000 picoseconds. + Weight::from_parts(1_414_442, 0) + // Standard Error: 142_321 + .saturating_add(Weight::from_parts(13_210_495, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 5000]`. fn instr_i64clz(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_648_000 picoseconds. - Weight::from_parts(2_012_545, 0) + // Minimum execution time: 1_665_000 picoseconds. + Weight::from_parts(2_047_968, 0) // Standard Error: 2 - .saturating_add(Weight::from_parts(3_824, 0).saturating_mul(r.into())) + .saturating_add(Weight::from_parts(4_035, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 5000]`. fn instr_i64ctz(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_688_000 picoseconds. - Weight::from_parts(1_995_956, 0) - // Standard Error: 2 - .saturating_add(Weight::from_parts(3_757, 0).saturating_mul(r.into())) + // Minimum execution time: 1_696_000 picoseconds. + Weight::from_parts(2_101_548, 0) + // Standard Error: 13 + .saturating_add(Weight::from_parts(3_754, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 5000]`. fn instr_i64popcnt(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_631_000 picoseconds. - Weight::from_parts(2_011_493, 0) - // Standard Error: 3 - .saturating_add(Weight::from_parts(3_755, 0).saturating_mul(r.into())) + // Minimum execution time: 1_686_000 picoseconds. + Weight::from_parts(2_023_482, 0) + // Standard Error: 2 + .saturating_add(Weight::from_parts(3_770, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 5000]`. fn instr_i64eqz(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_667_000 picoseconds. - Weight::from_parts(1_958_798, 0) + // Minimum execution time: 1_700_000 picoseconds. + Weight::from_parts(2_035_759, 0) // Standard Error: 2 - .saturating_add(Weight::from_parts(3_677, 0).saturating_mul(r.into())) + .saturating_add(Weight::from_parts(3_660, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 5000]`. fn instr_i64extendsi32(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_674_000 picoseconds. - Weight::from_parts(2_009_555, 0) - // Standard Error: 7 - .saturating_add(Weight::from_parts(3_863, 0).saturating_mul(r.into())) + // Minimum execution time: 1_682_000 picoseconds. + Weight::from_parts(2_015_828, 0) + // Standard Error: 2 + .saturating_add(Weight::from_parts(3_977, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 5000]`. fn instr_i64extendui32(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_659_000 picoseconds. - Weight::from_parts(2_014_985, 0) + // Minimum execution time: 1_660_000 picoseconds. + Weight::from_parts(2_032_387, 0) // Standard Error: 2 - .saturating_add(Weight::from_parts(3_821, 0).saturating_mul(r.into())) + .saturating_add(Weight::from_parts(3_826, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 5000]`. fn instr_i32wrapi64(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_640_000 picoseconds. - Weight::from_parts(2_013_939, 0) - // Standard Error: 2 - .saturating_add(Weight::from_parts(3_708, 0).saturating_mul(r.into())) + // Minimum execution time: 1_635_000 picoseconds. + Weight::from_parts(2_013_228, 0) + // Standard Error: 3 + .saturating_add(Weight::from_parts(3_752, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 5000]`. fn instr_i64eq(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_631_000 picoseconds. - Weight::from_parts(2_002_814, 0) - // Standard Error: 2 - .saturating_add(Weight::from_parts(6_008, 0).saturating_mul(r.into())) + // Minimum execution time: 1_698_000 picoseconds. + Weight::from_parts(2_844_817, 0) + // Standard Error: 56 + .saturating_add(Weight::from_parts(5_746, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 5000]`. fn instr_i64ne(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_647_000 picoseconds. - Weight::from_parts(2_032_158, 0) - // Standard Error: 3 - .saturating_add(Weight::from_parts(5_944, 0).saturating_mul(r.into())) + // Minimum execution time: 1_686_000 picoseconds. + Weight::from_parts(2_208_884, 0) + // Standard Error: 138 + .saturating_add(Weight::from_parts(6_032, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 5000]`. fn instr_i64lts(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_669_000 picoseconds. - Weight::from_parts(2_040_386, 0) + // Minimum execution time: 1_695_000 picoseconds. + Weight::from_parts(2_060_880, 0) // Standard Error: 3 - .saturating_add(Weight::from_parts(6_009, 0).saturating_mul(r.into())) + .saturating_add(Weight::from_parts(5_966, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 5000]`. fn instr_i64ltu(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_637_000 picoseconds. - Weight::from_parts(1_983_695, 0) - // Standard Error: 4 - .saturating_add(Weight::from_parts(6_027, 0).saturating_mul(r.into())) + // Minimum execution time: 1_700_000 picoseconds. + Weight::from_parts(2_143_484, 0) + // Standard Error: 3 + .saturating_add(Weight::from_parts(5_932, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 5000]`. fn instr_i64gts(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_701_000 picoseconds. - Weight::from_parts(2_054_295, 0) - // Standard Error: 2 - .saturating_add(Weight::from_parts(5_799, 0).saturating_mul(r.into())) + // Minimum execution time: 1_677_000 picoseconds. + Weight::from_parts(2_081_646, 0) + // Standard Error: 3 + .saturating_add(Weight::from_parts(5_813, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 5000]`. fn instr_i64gtu(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_653_000 picoseconds. - Weight::from_parts(2_749_807, 0) - // Standard Error: 90 - .saturating_add(Weight::from_parts(5_945, 0).saturating_mul(r.into())) + // Minimum execution time: 2_476_000 picoseconds. + Weight::from_parts(2_161_801, 0) + // Standard Error: 4 + .saturating_add(Weight::from_parts(6_078, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 5000]`. fn instr_i64les(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_651_000 picoseconds. - Weight::from_parts(1_979_111, 0) + // Minimum execution time: 1_677_000 picoseconds. + Weight::from_parts(2_043_451, 0) // Standard Error: 3 - .saturating_add(Weight::from_parts(6_011, 0).saturating_mul(r.into())) + .saturating_add(Weight::from_parts(5_988, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 5000]`. fn instr_i64leu(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_743_000 picoseconds. - Weight::from_parts(2_058_081, 0) - // Standard Error: 3 - .saturating_add(Weight::from_parts(6_085, 0).saturating_mul(r.into())) + // Minimum execution time: 1_694_000 picoseconds. + Weight::from_parts(2_058_196, 0) + // Standard Error: 2 + .saturating_add(Weight::from_parts(6_058, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 5000]`. fn instr_i64ges(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_638_000 picoseconds. - Weight::from_parts(2_038_929, 0) - // Standard Error: 3 - .saturating_add(Weight::from_parts(5_941, 0).saturating_mul(r.into())) + // Minimum execution time: 1_675_000 picoseconds. + Weight::from_parts(2_036_798, 0) + // Standard Error: 2 + .saturating_add(Weight::from_parts(5_956, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 5000]`. fn instr_i64geu(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_641_000 picoseconds. - Weight::from_parts(2_036_587, 0) + // Minimum execution time: 1_712_000 picoseconds. + Weight::from_parts(2_121_407, 0) // Standard Error: 2 - .saturating_add(Weight::from_parts(6_008, 0).saturating_mul(r.into())) + .saturating_add(Weight::from_parts(5_944, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 5000]`. fn instr_i64add(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_624_000 picoseconds. - Weight::from_parts(2_080_562, 0) - // Standard Error: 3 - .saturating_add(Weight::from_parts(5_826, 0).saturating_mul(r.into())) + // Minimum execution time: 1_675_000 picoseconds. + Weight::from_parts(2_061_053, 0) + // Standard Error: 6 + .saturating_add(Weight::from_parts(5_823, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 5000]`. fn instr_i64sub(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_652_000 picoseconds. - Weight::from_parts(2_039_535, 0) - // Standard Error: 2 - .saturating_add(Weight::from_parts(6_137, 0).saturating_mul(r.into())) + // Minimum execution time: 1_686_000 picoseconds. + Weight::from_parts(2_023_347, 0) + // Standard Error: 4 + .saturating_add(Weight::from_parts(6_126, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 5000]`. fn instr_i64mul(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_666_000 picoseconds. - Weight::from_parts(2_056_354, 0) - // Standard Error: 6 - .saturating_add(Weight::from_parts(5_780, 0).saturating_mul(r.into())) + // Minimum execution time: 1_678_000 picoseconds. + Weight::from_parts(2_390_920, 0) + // Standard Error: 55 + .saturating_add(Weight::from_parts(5_635, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 5000]`. fn instr_i64divs(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_648_000 picoseconds. - Weight::from_parts(2_077_695, 0) - // Standard Error: 4 - .saturating_add(Weight::from_parts(11_775, 0).saturating_mul(r.into())) + // Minimum execution time: 1_728_000 picoseconds. + Weight::from_parts(1_944_457, 0) + // Standard Error: 7 + .saturating_add(Weight::from_parts(11_848, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 5000]`. fn instr_i64divu(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_797_000 picoseconds. - Weight::from_parts(2_772_388, 0) - // Standard Error: 33 - .saturating_add(Weight::from_parts(10_333, 0).saturating_mul(r.into())) + // Minimum execution time: 1_649_000 picoseconds. + Weight::from_parts(1_881_148, 0) + // Standard Error: 6 + .saturating_add(Weight::from_parts(10_618, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 5000]`. fn instr_i64rems(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_699_000 picoseconds. - Weight::from_parts(2_174_288, 0) + // Minimum execution time: 1_708_000 picoseconds. + Weight::from_parts(1_767_912, 0) // Standard Error: 6 - .saturating_add(Weight::from_parts(11_778, 0).saturating_mul(r.into())) + .saturating_add(Weight::from_parts(12_099, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 5000]`. fn instr_i64remu(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_685_000 picoseconds. - Weight::from_parts(2_091_037, 0) + // Minimum execution time: 1_650_000 picoseconds. + Weight::from_parts(1_998_575, 0) // Standard Error: 3 - .saturating_add(Weight::from_parts(10_694, 0).saturating_mul(r.into())) + .saturating_add(Weight::from_parts(10_632, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 5000]`. fn instr_i64and(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_636_000 picoseconds. - Weight::from_parts(1_975_521, 0) - // Standard Error: 2 - .saturating_add(Weight::from_parts(5_695, 0).saturating_mul(r.into())) + // Minimum execution time: 1_633_000 picoseconds. + Weight::from_parts(2_029_981, 0) + // Standard Error: 3 + .saturating_add(Weight::from_parts(5_662, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 5000]`. fn instr_i64or(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_619_000 picoseconds. - Weight::from_parts(2_045_492, 0) - // Standard Error: 2 - .saturating_add(Weight::from_parts(5_770, 0).saturating_mul(r.into())) + // Minimum execution time: 1_621_000 picoseconds. + Weight::from_parts(2_029_743, 0) + // Standard Error: 3 + .saturating_add(Weight::from_parts(5_762, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 5000]`. fn instr_i64xor(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_668_000 picoseconds. - Weight::from_parts(2_055_460, 0) - // Standard Error: 3 - .saturating_add(Weight::from_parts(5_851, 0).saturating_mul(r.into())) + // Minimum execution time: 1_641_000 picoseconds. + Weight::from_parts(2_127_132, 0) + // Standard Error: 5 + .saturating_add(Weight::from_parts(5_818, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 5000]`. fn instr_i64shl(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_681_000 picoseconds. - Weight::from_parts(2_023_370, 0) - // Standard Error: 2 - .saturating_add(Weight::from_parts(5_853, 0).saturating_mul(r.into())) + // Minimum execution time: 1_690_000 picoseconds. + Weight::from_parts(2_021_035, 0) + // Standard Error: 4 + .saturating_add(Weight::from_parts(5_917, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 5000]`. fn instr_i64shrs(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_714_000 picoseconds. - Weight::from_parts(2_067_584, 0) + // Minimum execution time: 1_661_000 picoseconds. + Weight::from_parts(2_055_069, 0) // Standard Error: 3 - .saturating_add(Weight::from_parts(6_133, 0).saturating_mul(r.into())) + .saturating_add(Weight::from_parts(6_094, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 5000]`. fn instr_i64shru(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_602_000 picoseconds. - Weight::from_parts(2_055_530, 0) - // Standard Error: 138 - .saturating_add(Weight::from_parts(6_014, 0).saturating_mul(r.into())) + // Minimum execution time: 1_677_000 picoseconds. + Weight::from_parts(2_024_748, 0) + // Standard Error: 2 + .saturating_add(Weight::from_parts(5_862, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 5000]`. fn instr_i64rotl(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_709_000 picoseconds. - Weight::from_parts(2_016_365, 0) - // Standard Error: 2 - .saturating_add(Weight::from_parts(5_985, 0).saturating_mul(r.into())) + // Minimum execution time: 1_651_000 picoseconds. + Weight::from_parts(2_005_814, 0) + // Standard Error: 3 + .saturating_add(Weight::from_parts(6_007, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 5000]`. fn instr_i64rotr(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_661_000 picoseconds. - Weight::from_parts(2_003_063, 0) - // Standard Error: 2 - .saturating_add(Weight::from_parts(5_863, 0).saturating_mul(r.into())) + // Minimum execution time: 1_703_000 picoseconds. + Weight::from_parts(2_019_636, 0) + // Standard Error: 3 + .saturating_add(Weight::from_parts(5_862, 0).saturating_mul(r.into())) } } From faeed2895ad8f275c7f06075ea17269459605087 Mon Sep 17 00:00:00 2001 From: Aaron Bassett Date: Mon, 27 Mar 2023 22:36:20 +0200 Subject: [PATCH 301/558] Change license of node-template and FRAME examples to MIT-0 (#13465) * Change node-template license from Unlicense to MIT-0 * Change frame examples license from Unlicense to MIT-0 * Update bin/node-template/LICENSE --------- Co-authored-by: Oliver Tale-Yazdi Co-authored-by: parity-processbot <> --- bin/node-template/LICENSE | 34 +++++++------------ bin/node-template/node/Cargo.toml | 2 +- bin/node-template/pallets/template/Cargo.toml | 2 +- bin/node-template/pallets/template/README.md | 2 +- bin/node-template/runtime/Cargo.toml | 2 +- frame/examples/basic/Cargo.toml | 2 +- frame/examples/basic/README.md | 2 +- frame/examples/offchain-worker/Cargo.toml | 2 +- frame/examples/offchain-worker/README.md | 2 +- 9 files changed, 21 insertions(+), 29 deletions(-) diff --git a/bin/node-template/LICENSE b/bin/node-template/LICENSE index cf1ab25da..ffa0b3f2d 100644 --- a/bin/node-template/LICENSE +++ b/bin/node-template/LICENSE @@ -1,24 +1,16 @@ -This is free and unencumbered software released into the public domain. +MIT No Attribution -Anyone is free to copy, modify, publish, use, compile, sell, or -distribute this software, either in source code form or as a compiled -binary, for any purpose, commercial or non-commercial, and by any -means. +Copyright Parity Technologies (UK) Ltd. -In jurisdictions that recognize copyright laws, the author or authors -of this software dedicate any and all copyright interest in the -software to the public domain. We make this dedication for the benefit -of the public at large and to the detriment of our heirs and -successors. We intend this dedication to be an overt act of -relinquishment in perpetuity of all present and future rights to this -software under copyright law. +Permission is hereby granted, free of charge, to any person obtaining a copy of this +software and associated documentation files (the "Software"), to deal in the Software +without restriction, including without limitation the rights to use, copy, modify, +merge, publish, distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so. -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. -IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR -OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, -ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR -OTHER DEALINGS IN THE SOFTWARE. - -For more information, please refer to +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, +INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT +HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. \ No newline at end of file diff --git a/bin/node-template/node/Cargo.toml b/bin/node-template/node/Cargo.toml index 979c00120..ac64b8881 100644 --- a/bin/node-template/node/Cargo.toml +++ b/bin/node-template/node/Cargo.toml @@ -5,7 +5,7 @@ description = "A fresh FRAME-based Substrate node, ready for hacking." authors = ["Substrate DevHub "] homepage = "https://substrate.io/" edition = "2021" -license = "Unlicense" +license = "MIT-0" publish = false repository = "https://github.com/substrate-developer-hub/substrate-node-template/" build = "build.rs" diff --git a/bin/node-template/pallets/template/Cargo.toml b/bin/node-template/pallets/template/Cargo.toml index f6607b25c..ddf7c6e52 100644 --- a/bin/node-template/pallets/template/Cargo.toml +++ b/bin/node-template/pallets/template/Cargo.toml @@ -5,7 +5,7 @@ description = "FRAME pallet template for defining custom runtime logic." authors = ["Substrate DevHub "] homepage = "https://substrate.io" edition = "2021" -license = "Unlicense" +license = "MIT-0" publish = false repository = "https://github.com/substrate-developer-hub/substrate-node-template/" diff --git a/bin/node-template/pallets/template/README.md b/bin/node-template/pallets/template/README.md index 8d751a422..d0d59537c 100644 --- a/bin/node-template/pallets/template/README.md +++ b/bin/node-template/pallets/template/README.md @@ -1 +1 @@ -License: Unlicense \ No newline at end of file +License: MIT-0 \ No newline at end of file diff --git a/bin/node-template/runtime/Cargo.toml b/bin/node-template/runtime/Cargo.toml index 90fa6269e..8915061c1 100644 --- a/bin/node-template/runtime/Cargo.toml +++ b/bin/node-template/runtime/Cargo.toml @@ -5,7 +5,7 @@ description = "A fresh FRAME-based Substrate node, ready for hacking." authors = ["Substrate DevHub "] homepage = "https://substrate.io/" edition = "2021" -license = "Unlicense" +license = "MIT-0" publish = false repository = "https://github.com/substrate-developer-hub/substrate-node-template/" diff --git a/frame/examples/basic/Cargo.toml b/frame/examples/basic/Cargo.toml index 47f623f45..2f854011b 100644 --- a/frame/examples/basic/Cargo.toml +++ b/frame/examples/basic/Cargo.toml @@ -3,7 +3,7 @@ name = "pallet-example-basic" version = "4.0.0-dev" authors = ["Parity Technologies "] edition = "2021" -license = "Unlicense" +license = "MIT-0" homepage = "https://substrate.io" repository = "https://github.com/paritytech/substrate/" description = "FRAME example pallet" diff --git a/frame/examples/basic/README.md b/frame/examples/basic/README.md index 358829192..2af50573f 100644 --- a/frame/examples/basic/README.md +++ b/frame/examples/basic/README.md @@ -237,4 +237,4 @@ pub trait Config: ::Config { } // that the implementation is based on.

-License: Unlicense +License: MIT-0 diff --git a/frame/examples/offchain-worker/Cargo.toml b/frame/examples/offchain-worker/Cargo.toml index f94cf36b2..fb4c12ae0 100644 --- a/frame/examples/offchain-worker/Cargo.toml +++ b/frame/examples/offchain-worker/Cargo.toml @@ -3,7 +3,7 @@ name = "pallet-example-offchain-worker" version = "4.0.0-dev" authors = ["Parity Technologies "] edition = "2021" -license = "Unlicense" +license = "MIT-0" homepage = "https://substrate.io" repository = "https://github.com/paritytech/substrate/" description = "FRAME example pallet for offchain worker" diff --git a/frame/examples/offchain-worker/README.md b/frame/examples/offchain-worker/README.md index 587431c92..7b8905cda 100644 --- a/frame/examples/offchain-worker/README.md +++ b/frame/examples/offchain-worker/README.md @@ -26,4 +26,4 @@ Additional logic in OCW is put in place to prevent spamming the network with bot and unsigned transactions, and custom `UnsignedValidator` makes sure that there is only one unsigned transaction floating in the network. -License: Unlicense +License: MIT-0 From 3fa99a9859fd552b81963c20198210db2f0e6ed6 Mon Sep 17 00:00:00 2001 From: Joyce Siqueira <98593770+the-right-joyce@users.noreply.github.com> Date: Mon, 27 Mar 2023 18:27:16 -0300 Subject: [PATCH 302/558] updating labels descriptions (#13557) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * updating labels descriptions * delete milestones * Update docs/CONTRIBUTING.adoc Co-authored-by: Bastian Köcher * link to label docs * Update docs/CONTRIBUTING.adoc Co-authored-by: Oliver Tale-Yazdi --------- Co-authored-by: Bastian Köcher Co-authored-by: parity-processbot <> Co-authored-by: Oliver Tale-Yazdi --- docs/CONTRIBUTING.adoc | 38 +++++++++++++++++--------------------- 1 file changed, 17 insertions(+), 21 deletions(-) diff --git a/docs/CONTRIBUTING.adoc b/docs/CONTRIBUTING.adoc index d39b8a6d7..05a9434ac 100644 --- a/docs/CONTRIBUTING.adoc +++ b/docs/CONTRIBUTING.adoc @@ -30,26 +30,23 @@ A Pull Request (PR) needs to be reviewed and approved by project maintainers unl - `A-*` Pull request status. ONE REQUIRED. - `B-*` Changelog and/or Runtime-upgrade post composition markers. ONE REQUIRED. (used by automation) -- `C-*` Release notes release-priority markers. EXACTLY ONE REQUIRED. (used by automation) -- `D-*` More general tags on the PR denoting various implications and requirements. +- `C-*` Release notes release-criticality markers. EXACTLY ONE REQUIRED. (used by automation) +- `D-*` Audit tags denoting auditing requirements on the PR. *Process:* . Please tag each PR with exactly one `A`, `B`, `C` and `D` label at the minimum. -. Once a PR is ready for review please add the https://github.com/paritytech/substrate/pulls?q=is%3Apr+is%3Aopen+label%3AA0-pleasereview[`A0-pleasereview`] label. Generally PRs should sit with this label for 48 hours in order to garner feedback. It may be merged before if all relevant parties had a look at it. -. If the first review is not an approval, swap `A0-pleasereview` to any label `[A3, A7]` to indicate that the PR has received some feedback, but needs further work. For example. https://github.com/paritytech/substrate/labels/A3-inprogress[`A3-inprogress`] is a general indicator that the PR is work in progress and https://github.com/paritytech/substrate/labels/A4-gotissues[`A4-gotissues`] means that it has significant problems that need fixing. Once the work is done, change the label back to `A0-pleasereview`. You might end up swapping a few times back and forth to climb up the A label group. Once a PR is https://github.com/paritytech/substrate/labels/A8-mergeoncegreen[`A8-mergeoncegreen`], it is ready to merge. -. PRs must be tagged with their release notes requirements via the `B1-B9` labels. -. PRs must be tagged with their release importance via the `C1-C9` labels. +. Once a PR is ready for review please add the https://github.com/paritytech/substrate/pulls?q=is%3Apr+is%3Aopen+label%3AA0-please_review+[`A0-please_review`] label. Generally PRs should sit with this label for 48 hours in order to garner feedback. It may be merged before if all relevant parties had a look at it. +. If the first review is not an approval, swap `A0-please_review` to any label `[A3, A5]` to indicate that the PR has received some feedback, but needs further work. For example. https://github.com/paritytech/substrate/labels/A3-in_progress[`A3-in_progress`] is a general indicator that the PR is work in progress. +. PRs must be tagged with their release notes requirements via the `B*` labels. If a PR should be mentioned in the release notes, use `B1` and add either: `T0-node`, `T1-runtime`or `T2-API` - this defines in which section of the release notes the PR will be shown. +. PRs must be tagged with their release importance via the `C1-C7` labels. . PRs must be tagged with their audit requirements via the `D1-D9` labels. -. PRs that must be backported to a stable branch must be tagged with https://github.com/paritytech/substrate/labels/E1-runtimemigration[`E0-patchthis`]. -. PRs that introduce runtime migrations must be tagged with https://github.com/paritytech/substrate/labels/E1-runtimemigration[`E1-runtimemigration`]. See the https://github.com/paritytech/substrate/blob/master/utils/frame/try-runtime/cli/src/lib.rs#L18[Migration Best Practices here] for more info about how to test runtime migrations. -. PRs that introduce irreversible database migrations must be tagged with https://github.com/paritytech/substrate/labels/E2-databasemigration[`E2-databasemigration`]. -. PRs that add host functions must be tagged with with https://github.com/paritytech/substrate/labels/E4-newhostfunctions[`E4-newhostfunctions`]. -. PRs that break the external API must be tagged with https://github.com/paritytech/substrate/labels/E5-breaksapi[`E5-breaksapi`]. -. PRs that materially change the FRAME/runtime semantics must be tagged with https://github.com/paritytech/substrate/labels/E6-transactionversion[`E6-transactionversion`]. -. PRs that change the mechanism for block authoring in a backwards-incompatible way must be tagged with https://github.com/paritytech/substrate/labels/E7-breaksauthoring[`E7-breaksauthoring`]. -. PRs that "break everything" must be tagged with https://github.com/paritytech/substrate/labels/E8-breakseverything[`E8-breakseverything`]. -. PRs that block a new release must be tagged with https://github.com/paritytech/substrate/labels/E9-blocker%20%E2%9B%94%EF%B8%8F[`E9-blocker`]. +. PRs that introduce runtime migrations must be tagged with https://github.com/paritytech/substrate/labels/E0-runtime_migration[`E0-runtime_migration`]. See the https://github.com/paritytech/substrate/blob/master/utils/frame/try-runtime/cli/src/lib.rs#L18[Migration Best Practices here] for more info about how to test runtime migrations. +. PRs that introduce irreversible database migrations must be tagged with https://github.com/paritytech/substrate/labels/E1-database_migration[`E1-database_migration`]. +. PRs that add host functions must be tagged with with https://github.com/paritytech/substrate/labels/E3-host_functions[`E3-host_functions`]. +. PRs that break the external API must be tagged with https://github.com/paritytech/substrate/labels/F3-breaks_API[`F3-breaks_API`]. +. PRs that change the mechanism for block authoring in a backwards-incompatible way must be tagged with https://github.com/paritytech/substrate/labels/F1-breaks_authoring[`F1-breaks_authoring`]. +. PRs that "break everything" must be tagged with https://github.com/paritytech/substrate/labels/F0-breaks_everything[`F0-breaks_everything`]. . PRs should be categorized into projects. . No PR should be merged until all reviews' comments are addressed and CI is successful. @@ -91,21 +88,20 @@ To create a Polkadot companion PR: Note: The merge-bot currently doesn't work with forks on org accounts, only individual accounts. (Hint: it's recommended to use `bot merge` to merge all substrate PRs, not just ones with a polkadot companion.) -If your PR is reviewed well, but a Polkadot PR is missing, signal it with https://github.com/paritytech/substrate/labels/A7-needspolkadotpr[`A7-needspolkadotpr`] to prevent it from getting automatically merged. +If your PR is reviewed well, but a Polkadot PR is missing, signal it with https://github.com/paritytech/substrate/labels/E6-needs_polkadot_pr[`E6-needs_polkadot_pr`] to prevent it from getting automatically merged. In most cases the CI will add this label automatically. As there might be multiple pending PRs that might conflict with one another, a) you should not merge the substrate PR until the Polkadot PR has also been reviewed and b) both should be merged pretty quickly after another to not block others. == Helping out -We use https://github.com/paritytech/substrate/labels[labels] to manage PRs and issues and communicate state of a PR. Please familiarize yourself with them. Furthermore we are organizing issues in https://github.com/paritytech/substrate/milestones[milestones]. Best way to get started is to a pick a ticket from the current milestone tagged https://github.com/paritytech/substrate/issues?q=is%3Aissue+is%3Aopen+label%3AQ2-easy[`easy`] or https://github.com/paritytech/substrate/issues?q=is%3Aissue+is%3Aopen+label%3AQ3-medium[`medium`] and get going or https://github.com/paritytech/substrate/issues?q=is%3Aissue+is%3Aopen+label%3AX1-mentor[`mentor`] and get in contact with the mentor offering their support on that larger task. +We use https://paritytech.github.io/labels/doc_substrate.html[labels] to manage PRs and issues and communicate state of a PR. Please familiarize yourself with them. Best way to get started is to a pick a ticket tagged https://github.com/paritytech/substrate/issues?q=is%3Aissue+is%3Aopen+label%3AZ1-easy[`easy`] or https://github.com/paritytech/substrate/issues?q=is%3Aissue+is%3Aopen+label%3AZ2-medium[`medium`] and get going or https://github.com/paritytech/substrate/issues?q=is%3Aissue+is%3Aopen+label%3AZ6-mentor[`mentor`] and get in contact with the mentor offering their support on that larger task. == Issues Please label issues with the following labels: -. `I-*` Issue severity and type. EXACTLY ONE REQUIRED. -. `P-*` Issue priority. AT MOST ONE ALLOWED. -. `Q-*` Issue difficulty. AT MOST ONE ALLOWED. -. `Z-*` More general tags on the issue, denoting context and resolution. +. `I-*` or `J-*` Issue severity and type. EXACTLY ONE REQUIRED. +. `U-*` Issue urgency, suggesting in what time manner does this issue need to be resolved. AT MOST ONE ALLOWED. +. `Z-*` Issue difficulty. AT MOST ONE ALLOWED. == Releases From ae49625b5f7a4fa267082f8d415ea4f66ac02423 Mon Sep 17 00:00:00 2001 From: Vladimir Istyufeev Date: Tue, 28 Mar 2023 04:00:18 +0400 Subject: [PATCH 303/558] CI: Investigate why `RUSTFLAGS` differs on `master` and PRs (#13686) --- scripts/ci/gitlab/pipeline/test.yml | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/scripts/ci/gitlab/pipeline/test.yml b/scripts/ci/gitlab/pipeline/test.yml index b5c37d243..d00f06b09 100644 --- a/scripts/ci/gitlab/pipeline/test.yml +++ b/scripts/ci/gitlab/pipeline/test.yml @@ -236,17 +236,20 @@ test-frame-support: variables: # Enable debug assertions since we are running optimized builds for testing # but still want to have debug assertions. - RUSTFLAGS: "-Cdebug-assertions=y -Dwarnings" + RUSTFLAGS: "-C debug-assertions -D warnings" RUST_BACKTRACE: 1 WASM_BUILD_NO_COLOR: 1 - WASM_BUILD_RUSTFLAGS: "-Cdebug-assertions=y -Dwarnings" + WASM_BUILD_RUSTFLAGS: "-C debug-assertions -D warnings" # Ensure we run the UI tests. RUN_UI_TESTS: 1 script: + - echo $RUSTFLAGS - rusty-cachier snapshot create - - time cargo test --locked -p frame-support-test --features=frame-feature-testing,no-metadata-docs --manifest-path ./frame/support/test/Cargo.toml --test pallet # does not reuse cache 1 min 44 sec - - time cargo test --locked -p frame-support-test --features=frame-feature-testing,frame-feature-testing-2,no-metadata-docs --manifest-path ./frame/support/test/Cargo.toml --test pallet # does not reuse cache 1 min 44 sec + - cat /cargo_target_dir/debug/.fingerprint/memory_units-759eddf317490d2b/lib-memory_units.json || true + - time cargo test --verbose --locked -p frame-support-test --features=frame-feature-testing,no-metadata-docs --manifest-path ./frame/support/test/Cargo.toml --test pallet + - time cargo test --verbose --locked -p frame-support-test --features=frame-feature-testing,frame-feature-testing-2,no-metadata-docs --manifest-path ./frame/support/test/Cargo.toml --test pallet - SUBSTRATE_TEST_TIMEOUT=1 time cargo test -p substrate-test-utils --release --verbose --locked -- --ignored timeout + - cat /cargo_target_dir/debug/.fingerprint/memory_units-759eddf317490d2b/lib-memory_units.json || true - rusty-cachier cache upload # This job runs tests that don't work with cargo-nextest in test-linux-stable From 9b43d8f891b1e53be2aa55687632ac6b17800cf8 Mon Sep 17 00:00:00 2001 From: Sebastian Kunert Date: Tue, 28 Mar 2023 09:10:50 +0200 Subject: [PATCH 304/558] Remove HeaderBackend requirement from AuthorityDiscovery and NetworkWorker (#13730) * Remove `HeaderBackend` requirement from `NetworkWorker` * Remove HeaderBackend from authority-discovery --- client/authority-discovery/src/error.rs | 4 ++++ client/authority-discovery/src/lib.rs | 3 ++- client/authority-discovery/src/worker.rs | 15 +++++++++++---- client/network/src/config.rs | 10 +++++----- client/network/src/service.rs | 19 +++++++------------ client/network/test/src/lib.rs | 8 +++++--- client/network/test/src/service.rs | 9 ++++++--- client/service/src/builder.rs | 5 +++-- 8 files changed, 43 insertions(+), 30 deletions(-) diff --git a/client/authority-discovery/src/error.rs b/client/authority-discovery/src/error.rs index 447116b0f..13148bb33 100644 --- a/client/authority-discovery/src/error.rs +++ b/client/authority-discovery/src/error.rs @@ -23,6 +23,7 @@ pub type Result = std::result::Result; /// Error type for the authority discovery module. #[derive(Debug, thiserror::Error)] +#[allow(missing_docs)] pub enum Error { #[error("Received dht value found event with records with different keys.")] ReceivingDhtValueFoundEventWithDifferentKeys, @@ -71,4 +72,7 @@ pub enum Error { #[error("Received authority record without a valid signature for the remote peer id.")] MissingPeerIdSignature, + + #[error("Unable to fetch best block.")] + BestBlockFetchingError, } diff --git a/client/authority-discovery/src/lib.rs b/client/authority-discovery/src/lib.rs index a3c669909..6bb12804c 100644 --- a/client/authority-discovery/src/lib.rs +++ b/client/authority-discovery/src/lib.rs @@ -28,6 +28,7 @@ //! See [`Worker`] and [`Service`] for more documentation. pub use crate::{ + error::Error, service::Service, worker::{AuthorityDiscovery, NetworkProvider, Role, Worker}, }; @@ -148,7 +149,7 @@ pub fn new_worker_and_service_with_config + HeaderBackend + 'static, + Client: AuthorityDiscovery + 'static, DhtEventStream: Stream + Unpin, { let (to_worker, from_service) = mpsc::channel(0); diff --git a/client/authority-discovery/src/worker.rs b/client/authority-discovery/src/worker.rs index 43e44cac4..590d1dd19 100644 --- a/client/authority-discovery/src/worker.rs +++ b/client/authority-discovery/src/worker.rs @@ -157,12 +157,15 @@ pub trait AuthorityDiscovery { /// Retrieve authority identifiers of the current and next authority set. async fn authorities(&self, at: Block::Hash) -> std::result::Result, ApiError>; + + /// Retrieve best block hash + async fn best_hash(&self) -> std::result::Result; } #[async_trait::async_trait] impl AuthorityDiscovery for T where - T: ProvideRuntimeApi + Send + Sync, + T: ProvideRuntimeApi + HeaderBackend + Send + Sync, T::Api: AuthorityDiscoveryApi, Block: BlockT, { @@ -172,13 +175,17 @@ where ) -> std::result::Result, ApiError> { self.runtime_api().authorities(at) } + + async fn best_hash(&self) -> std::result::Result { + Ok(self.info().best_hash) + } } impl Worker where Block: BlockT + Unpin + 'static, Network: NetworkProvider, - Client: AuthorityDiscovery + HeaderBackend + 'static, + Client: AuthorityDiscovery + 'static, DhtEventStream: Stream + Unpin, { /// Construct a [`Worker`]. @@ -376,7 +383,7 @@ where } async fn refill_pending_lookups_queue(&mut self) -> Result<()> { - let best_hash = self.client.info().best_hash; + let best_hash = self.client.best_hash().await?; let local_keys = match &self.role { Role::PublishAndDiscover(key_store) => key_store @@ -594,7 +601,7 @@ where .into_iter() .collect::>(); - let best_hash = client.info().best_hash; + let best_hash = client.best_hash().await?; let authorities = client .authorities(best_hash) .await diff --git a/client/network/src/config.rs b/client/network/src/config.rs index 925a7795d..3c6801f22 100644 --- a/client/network/src/config.rs +++ b/client/network/src/config.rs @@ -34,6 +34,7 @@ use prometheus_endpoint::Registry; pub use sc_network_common::{role::Role, sync::warp::WarpSyncProvider, ExHashT}; use zeroize::Zeroize; +use sp_runtime::traits::Block as BlockT; use std::{ error::Error, fmt, fs, @@ -44,7 +45,6 @@ use std::{ path::{Path, PathBuf}, pin::Pin, str::{self, FromStr}, - sync::Arc, }; pub use libp2p::{ @@ -688,7 +688,7 @@ impl NetworkConfiguration { } /// Network initialization parameters. -pub struct Params { +pub struct Params { /// Assigned role for our node (full, light, ...). pub role: Role, @@ -698,12 +698,12 @@ pub struct Params { /// Network layer configuration. pub network_config: NetworkConfiguration, - /// Client that contains the blockchain. - pub chain: Arc, - /// Legacy name of the protocol to use on the wire. Should be different for each chain. pub protocol_id: ProtocolId, + /// Genesis hash of the chain + pub genesis_hash: Block::Hash, + /// Fork ID to distinguish protocols of different hard forks. Part of the standard protocol /// name on the wire. pub fork_id: Option, diff --git a/client/network/src/service.rs b/client/network/src/service.rs index 6dc00b36c..5f60f5132 100644 --- a/client/network/src/service.rs +++ b/client/network/src/service.rs @@ -73,8 +73,7 @@ use parking_lot::Mutex; use sc_network_common::ExHashT; use sc_peerset::PeersetHandle; use sc_utils::mpsc::{tracing_unbounded, TracingUnboundedReceiver, TracingUnboundedSender}; -use sp_blockchain::HeaderBackend; -use sp_runtime::traits::{Block as BlockT, Zero}; +use sp_runtime::traits::Block as BlockT; use std::{ cmp, @@ -147,9 +146,7 @@ where /// Returns a `NetworkWorker` that implements `Future` and must be regularly polled in order /// for the network processing to advance. From it, you can extract a `NetworkService` using /// `worker.service()`. The `NetworkService` can be shared through the codebase. - pub fn new + 'static>( - mut params: Params, - ) -> Result { + pub fn new(mut params: Params) -> Result { // Private and public keys configuration. let local_identity = params.network_config.node_key.clone().into_keypair()?; let local_public = local_identity.public(); @@ -277,13 +274,11 @@ where config.discovery_limit( u64::from(params.network_config.default_peers_set.out_peers) + 15, ); - let genesis_hash = params - .chain - .hash(Zero::zero()) - .ok() - .flatten() - .expect("Genesis block exists; qed"); - config.with_kademlia(genesis_hash, params.fork_id.as_deref(), ¶ms.protocol_id); + config.with_kademlia( + params.genesis_hash, + params.fork_id.as_deref(), + ¶ms.protocol_id, + ); config.with_dht_random_walk(params.network_config.enable_dht_random_walk); config.allow_non_globals_in_dht(params.network_config.allow_non_globals_in_dht); config.use_kademlia_disjoint_query_paths( diff --git a/client/network/test/src/lib.rs b/client/network/test/src/lib.rs index 75b8287b0..6a9930403 100644 --- a/client/network/test/src/lib.rs +++ b/client/network/test/src/lib.rs @@ -83,7 +83,7 @@ use sp_core::H256; use sp_runtime::{ codec::{Decode, Encode}, generic::BlockId, - traits::{Block as BlockT, Header as HeaderT, NumberFor}, + traits::{Block as BlockT, Header as HeaderT, NumberFor, Zero}, Justification, Justifications, }; use substrate_test_runtime_client::AccountKeyring; @@ -916,13 +916,15 @@ where let sync_service_import_queue = Box::new(sync_service.clone()); let sync_service = Arc::new(sync_service.clone()); - let network = NetworkWorker::new(sc_network::config::Params { + let genesis_hash = + client.hash(Zero::zero()).ok().flatten().expect("Genesis block exists; qed"); + let network = NetworkWorker::new::(sc_network::config::Params { role: if config.is_authority { Role::Authority } else { Role::Full }, executor: Box::new(|f| { tokio::spawn(f); }), network_config, - chain: client.clone(), + genesis_hash, protocol_id, fork_id, metrics_registry: None, diff --git a/client/network/test/src/service.rs b/client/network/test/src/service.rs index b1de2a91e..67915c386 100644 --- a/client/network/test/src/service.rs +++ b/client/network/test/src/service.rs @@ -34,7 +34,8 @@ use sc_network_sync::{ service::network::{NetworkServiceHandle, NetworkServiceProvider}, state_request_handler::StateRequestHandler, }; -use sp_runtime::traits::Block as BlockT; +use sp_blockchain::HeaderBackend; +use sp_runtime::traits::{Block as BlockT, Zero}; use substrate_test_runtime_client::{ runtime::{Block as TestBlock, Hash as TestHash}, TestClientBuilder, TestClientBuilderExt as _, @@ -194,17 +195,19 @@ impl TestNetworkBuilder { ) .unwrap(); let mut link = self.link.unwrap_or(Box::new(chain_sync_service.clone())); + let genesis_hash = + client.hash(Zero::zero()).ok().flatten().expect("Genesis block exists; qed"); let worker = NetworkWorker::< substrate_test_runtime_client::runtime::Block, substrate_test_runtime_client::runtime::Hash, - >::new(config::Params { + >::new(config::Params:: { block_announce_config, role: config::Role::Full, executor: Box::new(|f| { tokio::spawn(f); }), + genesis_hash, network_config, - chain: client.clone(), protocol_id, fork_id, metrics_registry: None, diff --git a/client/service/src/builder.rs b/client/service/src/builder.rs index f0f9a7ed0..7cbbf2a4d 100644 --- a/client/service/src/builder.rs +++ b/client/service/src/builder.rs @@ -850,7 +850,8 @@ where protocol_config })); - let mut network_params = sc_network::config::Params { + let genesis_hash = client.hash(Zero::zero()).ok().flatten().expect("Genesis block exists; qed"); + let mut network_params = sc_network::config::Params:: { role: config.role.clone(), executor: { let spawn_handle = Clone::clone(&spawn_handle); @@ -859,7 +860,7 @@ where }) }, network_config: config.network.clone(), - chain: client.clone(), + genesis_hash, protocol_id: protocol_id.clone(), fork_id: config.chain_spec.fork_id().map(ToOwned::to_owned), metrics_registry: config.prometheus_config.as_ref().map(|config| config.registry.clone()), From dec0369a35893c2be432e74358c4c7039e1e57be Mon Sep 17 00:00:00 2001 From: Vladimir Istyufeev Date: Tue, 28 Mar 2023 15:41:10 +0400 Subject: [PATCH 305/558] CI: rephrase `RUSTFLAGS` (#13735) --- .gitlab-ci.yml | 2 ++ scripts/ci/gitlab/pipeline/test.yml | 21 ++++++++++----------- 2 files changed, 12 insertions(+), 11 deletions(-) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index c4d88ebde..093a0bb41 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -105,6 +105,8 @@ variables: # $WASM_BUILD_WORKSPACE_HINT enables wasm-builder to find the Cargo.lock from within generated # packages - export WASM_BUILD_WORKSPACE_HINT="$PWD" + # ensure that RUSTFLAGS are set correctly + - echo $RUSTFLAGS .job-switcher: before_script: diff --git a/scripts/ci/gitlab/pipeline/test.yml b/scripts/ci/gitlab/pipeline/test.yml index d00f06b09..331b382e4 100644 --- a/scripts/ci/gitlab/pipeline/test.yml +++ b/scripts/ci/gitlab/pipeline/test.yml @@ -202,10 +202,10 @@ test-linux-stable: variables: # Enable debug assertions since we are running optimized builds for testing # but still want to have debug assertions. - RUSTFLAGS: "-Cdebug-assertions=y -Dwarnings" + RUSTFLAGS: "-C debug-assertions -D warnings" RUST_BACKTRACE: 1 WASM_BUILD_NO_COLOR: 1 - WASM_BUILD_RUSTFLAGS: "-Cdebug-assertions=y -Dwarnings" + WASM_BUILD_RUSTFLAGS: "-C debug-assertions -D warnings" # Ensure we run the UI tests. RUN_UI_TESTS: 1 # needed for rusty-cachier to keep cache in test-linux-stable folder and not in test-linux-stable-1/3 @@ -243,7 +243,6 @@ test-frame-support: # Ensure we run the UI tests. RUN_UI_TESTS: 1 script: - - echo $RUSTFLAGS - rusty-cachier snapshot create - cat /cargo_target_dir/debug/.fingerprint/memory_units-759eddf317490d2b/lib-memory_units.json || true - time cargo test --verbose --locked -p frame-support-test --features=frame-feature-testing,no-metadata-docs --manifest-path ./frame/support/test/Cargo.toml --test pallet @@ -261,10 +260,10 @@ test-linux-stable-extra: variables: # Enable debug assertions since we are running optimized builds for testing # but still want to have debug assertions. - RUSTFLAGS: "-Cdebug-assertions=y -Dwarnings" + RUSTFLAGS: "-C debug-assertions -D warnings" RUST_BACKTRACE: 1 WASM_BUILD_NO_COLOR: 1 - WASM_BUILD_RUSTFLAGS: "-Cdebug-assertions=y -Dwarnings" + WASM_BUILD_RUSTFLAGS: "-C debug-assertions -D warnings" # Ensure we run the UI tests. RUN_UI_TESTS: 1 script: @@ -286,10 +285,10 @@ quick-benchmarks: variables: # Enable debug assertions since we are running optimized builds for testing # but still want to have debug assertions. - RUSTFLAGS: "-Cdebug-assertions=y -Dwarnings" + RUSTFLAGS: "-C debug-assertions -D warnings" RUST_BACKTRACE: "full" WASM_BUILD_NO_COLOR: 1 - WASM_BUILD_RUSTFLAGS: "-Cdebug-assertions=y -Dwarnings" + WASM_BUILD_RUSTFLAGS: "-C debug-assertions -D warnings" script: - rusty-cachier snapshot create - time cargo run --locked --release --features runtime-benchmarks -- benchmark pallet --execution wasm --wasm-execution compiled --chain dev --pallet "*" --extrinsic "*" --steps 2 --repeat 1 @@ -305,7 +304,7 @@ test-frame-examples-compile-to-wasm: RUSTY_CACHIER_TOOLCHAIN: nightly # Enable debug assertions since we are running optimized builds for testing # but still want to have debug assertions. - RUSTFLAGS: "-Cdebug-assertions=y" + RUSTFLAGS: "-C debug-assertions" RUST_BACKTRACE: 1 script: - rusty-cachier snapshot create @@ -324,10 +323,10 @@ test-linux-stable-int: variables: # Enable debug assertions since we are running optimized builds for testing # but still want to have debug assertions. - RUSTFLAGS: "-Cdebug-assertions=y -Dwarnings" + RUSTFLAGS: "-C debug-assertions -D warnings" RUST_BACKTRACE: 1 WASM_BUILD_NO_COLOR: 1 - WASM_BUILD_RUSTFLAGS: "-Cdebug-assertions=y -Dwarnings" + WASM_BUILD_RUSTFLAGS: "-C debug-assertions -D warnings" # Ensure we run the UI tests. RUN_UI_TESTS: 1 script: @@ -373,7 +372,7 @@ test-full-crypto-feature: RUSTY_CACHIER_TOOLCHAIN: nightly # Enable debug assertions since we are running optimized builds for testing # but still want to have debug assertions. - RUSTFLAGS: "-Cdebug-assertions=y" + RUSTFLAGS: "-C debug-assertions" RUST_BACKTRACE: 1 script: - rusty-cachier snapshot create From 549165727c3b3c14b6f90011085aaebbeff9d7e3 Mon Sep 17 00:00:00 2001 From: Kasper Ziemianek Date: Tue, 28 Mar 2023 16:38:13 +0200 Subject: [PATCH 306/558] contracts: proper event link in docs (#13729) --- frame/contracts/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frame/contracts/README.md b/frame/contracts/README.md index 4f5f10a3a..68fa49deb 100644 --- a/frame/contracts/README.md +++ b/frame/contracts/README.md @@ -5,7 +5,7 @@ The Contract module provides functionality for the runtime to deploy and execute - [`Call`](https://paritytech.github.io/substrate/master/pallet_contracts/pallet/enum.Call.html) - [`Config`](https://paritytech.github.io/substrate/master/pallet_contracts/pallet/trait.Config.html) - [`Error`](https://paritytech.github.io/substrate/master/pallet_contracts/pallet/enum.Error.html) -- [`Event`](https://paritytech.github.io/substrate/master/pallet_contracts/pallet/enum.Error.html) +- [`Event`](https://paritytech.github.io/substrate/master/pallet_contracts/pallet/enum.Event.html) ## Overview From f40ba0d09785159a31a43b16b47ddb5ae8e62e1d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bastian=20K=C3=B6cher?= Date: Wed, 29 Mar 2023 09:17:50 +0200 Subject: [PATCH 307/558] Support stable rust for compiling the runtime (#13580) * Support stable rust for compiling the runtime This pull request brings support for compiling the runtime with stable Rust. This requires at least rust 1.68.0 to work on stable. The code is written in a way that it is backwards compatible and should automatically work when someone compiles with 1.68.0+ stable. * We always support nightlies! * :facepalm: * Sort by version * Review feedback * Review feedback * Fix version parsing * Apply suggestions from code review Co-authored-by: Koute --------- Co-authored-by: Koute --- Cargo.lock | 1 + primitives/io/Cargo.toml | 4 + primitives/io/build.rs | 27 ++++ primitives/io/src/lib.rs | 4 +- utils/wasm-builder/README.md | 11 +- utils/wasm-builder/src/lib.rs | 124 ++++++++++----- utils/wasm-builder/src/prerequisites.rs | 9 +- utils/wasm-builder/src/version.rs | 198 ++++++++++++++++++++++++ 8 files changed, 327 insertions(+), 51 deletions(-) create mode 100644 primitives/io/build.rs create mode 100644 utils/wasm-builder/src/version.rs diff --git a/Cargo.lock b/Cargo.lock index 33bca4005..cf30a6a94 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -10474,6 +10474,7 @@ dependencies = [ "libsecp256k1", "log", "parity-scale-codec", + "rustversion", "secp256k1", "sp-core", "sp-externalities", diff --git a/primitives/io/Cargo.toml b/primitives/io/Cargo.toml index e56bfcf56..c6e716396 100644 --- a/primitives/io/Cargo.toml +++ b/primitives/io/Cargo.toml @@ -9,6 +9,7 @@ repository = "https://github.com/paritytech/substrate/" description = "I/O for Substrate runtimes" documentation = "https://docs.rs/sp-io" readme = "README.md" +build = "build.rs" [package.metadata.docs.rs] targets = ["x86_64-unknown-linux-gnu"] @@ -37,6 +38,9 @@ ed25519-dalek = { version = "1.0.1", default-features = false, optional = true } # Force the usage of ed25519, this is being used in `ed25519-dalek`. ed25519 = { version = "1.5.2", optional = true } +[build-dependencies] +rustversion = "1.0.6" + [features] default = ["std"] std = [ diff --git a/primitives/io/build.rs b/primitives/io/build.rs new file mode 100644 index 000000000..8a9c0b642 --- /dev/null +++ b/primitives/io/build.rs @@ -0,0 +1,27 @@ +// This file is part of Substrate. + +// Copyright (C) Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +#[rustversion::before(1.68)] +fn main() { + if !cfg!(feature = "std") { + println!("cargo:rustc-cfg=enable_alloc_error_handler"); + } +} + +#[rustversion::since(1.68)] +fn main() {} diff --git a/primitives/io/src/lib.rs b/primitives/io/src/lib.rs index 306ac8e60..72300eb10 100644 --- a/primitives/io/src/lib.rs +++ b/primitives/io/src/lib.rs @@ -19,7 +19,7 @@ #![warn(missing_docs)] #![cfg_attr(not(feature = "std"), no_std)] -#![cfg_attr(not(feature = "std"), feature(alloc_error_handler))] +#![cfg_attr(enable_alloc_error_handler, feature(alloc_error_handler))] #![cfg_attr( feature = "std", doc = "Substrate runtime standard library as compiled when linked with Rust's standard library." @@ -1643,7 +1643,7 @@ pub fn panic(info: &core::panic::PanicInfo) -> ! { } /// A default OOM handler for WASM environment. -#[cfg(all(not(feature = "disable_oom"), not(feature = "std")))] +#[cfg(all(not(feature = "disable_oom"), enable_alloc_error_handler))] #[alloc_error_handler] pub fn oom(_: core::alloc::Layout) -> ! { #[cfg(feature = "improved_panic_error_reporting")] diff --git a/utils/wasm-builder/README.md b/utils/wasm-builder/README.md index a10611cc2..b1ccb1b35 100644 --- a/utils/wasm-builder/README.md +++ b/utils/wasm-builder/README.md @@ -77,8 +77,13 @@ Wasm builder requires the following prerequisites for building the Wasm binary: - rust nightly + `wasm32-unknown-unknown` toolchain -If a specific rust nightly is installed with `rustup`, it is important that the wasm target is installed -as well. For example if installing the rust nightly from 20.02.2020 using `rustup install nightly-2020-02-20`, -the wasm target needs to be installed as well `rustup target add wasm32-unknown-unknown --toolchain nightly-2020-02-20`. +or + +- rust stable and version at least 1.68.0 + `wasm32-unknown-unknown` toolchain + +If a specific rust is installed with `rustup`, it is important that the wasm target is +installed as well. For example if installing the rust from 20.02.2020 using `rustup +install nightly-2020-02-20`, the wasm target needs to be installed as well `rustup target add +wasm32-unknown-unknown --toolchain nightly-2020-02-20`. License: Apache-2.0 diff --git a/utils/wasm-builder/src/lib.rs b/utils/wasm-builder/src/lib.rs index 659b95525..8405b5a0b 100644 --- a/utils/wasm-builder/src/lib.rs +++ b/utils/wasm-builder/src/lib.rs @@ -100,8 +100,12 @@ //! //! - rust nightly + `wasm32-unknown-unknown` toolchain //! -//! If a specific rust nightly is installed with `rustup`, it is important that the wasm target is -//! installed as well. For example if installing the rust nightly from 20.02.2020 using `rustup +//! or +//! +//! - rust stable and version at least 1.68.0 + `wasm32-unknown-unknown` toolchain +//! +//! If a specific rust is installed with `rustup`, it is important that the wasm target is +//! installed as well. For example if installing the rust from 20.02.2020 using `rustup //! install nightly-2020-02-20`, the wasm target needs to be installed as well `rustup target add //! wasm32-unknown-unknown --toolchain nightly-2020-02-20`. @@ -111,9 +115,11 @@ use std::{ path::{Path, PathBuf}, process::Command, }; +use version::Version; mod builder; mod prerequisites; +mod version; mod wasm_project; pub use builder::{WasmBuilder, WasmBuilderSelectProject}; @@ -172,53 +178,59 @@ fn copy_file_if_changed(src: PathBuf, dst: PathBuf) { } } -/// Get a cargo command that compiles with nightly -fn get_nightly_cargo() -> CargoCommand { +/// Get a cargo command that should be used to invoke the compilation. +fn get_cargo_command() -> CargoCommand { let env_cargo = CargoCommand::new(&env::var("CARGO").expect("`CARGO` env variable is always set by cargo")); let default_cargo = CargoCommand::new("cargo"); - let rustup_run_nightly = CargoCommand::new_with_args("rustup", &["run", "nightly", "cargo"]); let wasm_toolchain = env::var(WASM_BUILD_TOOLCHAIN).ok(); // First check if the user requested a specific toolchain - if let Some(cmd) = wasm_toolchain.and_then(|t| get_rustup_nightly(Some(t))) { + if let Some(cmd) = + wasm_toolchain.map(|t| CargoCommand::new_with_args("rustup", &["run", &t, "cargo"])) + { cmd - } else if env_cargo.is_nightly() { + } else if env_cargo.supports_substrate_wasm_env() { env_cargo - } else if default_cargo.is_nightly() { + } else if default_cargo.supports_substrate_wasm_env() { default_cargo - } else if rustup_run_nightly.is_nightly() { - rustup_run_nightly } else { - // If no command before provided us with a nightly compiler, we try to search one - // with rustup. If that fails as well, we return the default cargo and let the prequisities - // check fail. - get_rustup_nightly(None).unwrap_or(default_cargo) + // If no command before provided us with a cargo that supports our Substrate wasm env, we + // try to search one with rustup. If that fails as well, we return the default cargo and let + // the prequisities check fail. + get_rustup_command().unwrap_or(default_cargo) } } -/// Get a nightly from rustup. If `selected` is `Some(_)`, a `CargoCommand` using the given -/// nightly is returned. -fn get_rustup_nightly(selected: Option) -> Option { +/// Get the newest rustup command that supports our Substrate wasm env. +/// +/// Stable versions are always favored over nightly versions even if the nightly versions are +/// newer. +fn get_rustup_command() -> Option { let host = format!("-{}", env::var("HOST").expect("`HOST` is always set by cargo")); - let version = match selected { - Some(selected) => selected, - None => { - let output = Command::new("rustup").args(&["toolchain", "list"]).output().ok()?.stdout; - let lines = output.as_slice().lines(); + let output = Command::new("rustup").args(&["toolchain", "list"]).output().ok()?.stdout; + let lines = output.as_slice().lines(); + + let mut versions = Vec::new(); + for line in lines.filter_map(|l| l.ok()) { + let rustup_version = line.trim_end_matches(&host); + + let cmd = CargoCommand::new_with_args("rustup", &["run", &rustup_version, "cargo"]); - let mut latest_nightly = None; - for line in lines.filter_map(|l| l.ok()) { - if line.starts_with("nightly-") && line.ends_with(&host) { - // Rustup prints them sorted - latest_nightly = Some(line.clone()); - } - } + if !cmd.supports_substrate_wasm_env() { + continue + } + + let Some(cargo_version) = cmd.version() else { continue; }; - latest_nightly?.trim_end_matches(&host).into() - }, - }; + versions.push((cargo_version, rustup_version.to_string())); + } + + // Sort by the parsed version to get the latest version (greatest version) at the end of the + // vec. + versions.sort_by_key(|v| v.0); + let version = &versions.last()?.1; Some(CargoCommand::new_with_args("rustup", &["run", &version, "cargo"])) } @@ -228,17 +240,23 @@ fn get_rustup_nightly(selected: Option) -> Option { struct CargoCommand { program: String, args: Vec, + version: Option, } impl CargoCommand { fn new(program: &str) -> Self { - CargoCommand { program: program.into(), args: Vec::new() } + let version = Self::extract_version(program, &[]); + + CargoCommand { program: program.into(), args: Vec::new(), version } } fn new_with_args(program: &str, args: &[&str]) -> Self { + let version = Self::extract_version(program, args); + CargoCommand { program: program.into(), args: args.iter().map(ToString::to_string).collect(), + version, } } @@ -248,20 +266,40 @@ impl CargoCommand { cmd } - /// Check if the supplied cargo command is a nightly version - fn is_nightly(&self) -> bool { + fn extract_version(program: &str, args: &[&str]) -> Option { + let version = Command::new(program) + .args(args) + .arg("--version") + .output() + .ok() + .and_then(|o| String::from_utf8(o.stdout).ok())?; + + Version::extract(&version) + } + + /// Returns the version of this cargo command or `None` if it failed to extract the version. + fn version(&self) -> Option { + self.version + } + + /// Check if the supplied cargo command supports our Substrate wasm environment. + /// + /// This means that either the cargo version is at minimum 1.68.0 or this is a nightly cargo. + /// + /// Assumes that cargo version matches the rustc version. + fn supports_substrate_wasm_env(&self) -> bool { // `RUSTC_BOOTSTRAP` tells a stable compiler to behave like a nightly. So, when this env // variable is set, we can assume that whatever rust compiler we have, it is a nightly // compiler. For "more" information, see: // https://github.com/rust-lang/rust/blob/fa0f7d0080d8e7e9eb20aa9cbf8013f96c81287f/src/libsyntax/feature_gate/check.rs#L891 - env::var("RUSTC_BOOTSTRAP").is_ok() || - self.command() - .arg("--version") - .output() - .map_err(|_| ()) - .and_then(|o| String::from_utf8(o.stdout).map_err(|_| ())) - .unwrap_or_default() - .contains("-nightly") + if env::var("RUSTC_BOOTSTRAP").is_ok() { + return true + } + + let Some(version) = self.version() else { return false }; + + // Check if major and minor are greater or equal than 1.68 or this is a nightly. + version.major > 1 || (version.major == 1 && version.minor >= 68) || version.is_nightly } } diff --git a/utils/wasm-builder/src/prerequisites.rs b/utils/wasm-builder/src/prerequisites.rs index ca07a0292..f5a985ab9 100644 --- a/utils/wasm-builder/src/prerequisites.rs +++ b/utils/wasm-builder/src/prerequisites.rs @@ -35,10 +35,13 @@ fn print_error_message(message: &str) -> String { /// /// Returns the versioned cargo command on success. pub(crate) fn check() -> Result { - let cargo_command = crate::get_nightly_cargo(); + let cargo_command = crate::get_cargo_command(); - if !cargo_command.is_nightly() { - return Err(print_error_message("Rust nightly not installed, please install it!")) + if !cargo_command.supports_substrate_wasm_env() { + return Err(print_error_message( + "Cannot compile the WASM runtime: no compatible Rust compiler found!\n\ + Install at least Rust 1.68.0 or a recent nightly version.", + )) } check_wasm_toolchain_installed(cargo_command) diff --git a/utils/wasm-builder/src/version.rs b/utils/wasm-builder/src/version.rs new file mode 100644 index 000000000..77e62b394 --- /dev/null +++ b/utils/wasm-builder/src/version.rs @@ -0,0 +1,198 @@ +// This file is part of Substrate. + +// Copyright (C) Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +use std::cmp::Ordering; + +/// The version of rustc/cargo. +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +pub struct Version { + pub major: u32, + pub minor: u32, + pub patch: u32, + pub is_nightly: bool, + pub year: u32, + pub month: u32, + pub day: u32, +} + +impl Version { + /// Returns if `self` is a stable version. + pub fn is_stable(&self) -> bool { + !self.is_nightly + } + + /// Return if `self` is a nightly version. + pub fn is_nightly(&self) -> bool { + self.is_nightly + } + + /// Extract from the given `version` string. + pub fn extract(version: &str) -> Option { + let mut is_nightly = false; + let version_parts = version + .trim() + .split(" ") + .nth(1)? + .split(".") + .filter_map(|v| { + if let Some(rest) = v.strip_suffix("-nightly") { + is_nightly = true; + rest.parse().ok() + } else { + v.parse().ok() + } + }) + .collect::>(); + + if version_parts.len() != 3 { + return None + } + + let date = version.split(" ").nth(3)?; + + let date_parts = date + .split("-") + .filter_map(|v| v.trim().strip_suffix(")").unwrap_or(v).parse().ok()) + .collect::>(); + + if date_parts.len() != 3 { + return None + } + + Some(Version { + major: version_parts[0], + minor: version_parts[1], + patch: version_parts[2], + is_nightly, + year: date_parts[0], + month: date_parts[1], + day: date_parts[2], + }) + } +} + +/// Ordering is done in the following way: +/// +/// 1. `stable` > `nightly` +/// 2. Then compare major, minor and patch. +/// 3. Last compare the date. +impl Ord for Version { + fn cmp(&self, other: &Self) -> Ordering { + if self == other { + return Ordering::Equal + } + + // Ensure that `stable > nightly` + if self.is_stable() && other.is_nightly() { + return Ordering::Greater + } else if self.is_nightly() && other.is_stable() { + return Ordering::Less + } + + let to_compare = [ + (self.major, other.major), + (self.minor, other.minor), + (self.patch, other.patch), + (self.year, other.year), + (self.month, other.month), + (self.day, other.day), + ]; + + to_compare + .iter() + .find_map(|(l, r)| if l != r { l.partial_cmp(&r) } else { None }) + // We already checked this right at the beginning, so we should never return here + // `Equal`. + .unwrap_or(Ordering::Equal) + } +} + +impl PartialOrd for Version { + fn partial_cmp(&self, other: &Self) -> Option { + Some(self.cmp(other)) + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn version_compare_and_extract_works() { + let version_1_66_0 = Version::extract("cargo 1.66.0 (d65d197ad 2022-11-15)").unwrap(); + let version_1_66_1 = Version::extract("cargo 1.66.1 (d65d197ad 2022-11-15)").unwrap(); + let version_1_66_0_nightly = + Version::extract("cargo 1.66.0-nightly (d65d197ad 2022-10-15)").unwrap(); + let version_1_66_0_nightly_older_date = + Version::extract("cargo 1.66.0-nightly (d65d197ad 2022-10-14)").unwrap(); + let version_1_65_0 = Version::extract("cargo 1.65.0 (d65d197ad 2022-10-15)").unwrap(); + let version_1_65_0_older_date = + Version::extract("cargo 1.65.0 (d65d197ad 2022-10-14)").unwrap(); + + assert!(version_1_66_1 > version_1_66_0); + assert!(version_1_66_1 > version_1_65_0); + assert!(version_1_66_1 > version_1_66_0_nightly); + assert!(version_1_66_1 > version_1_66_0_nightly_older_date); + assert!(version_1_66_1 > version_1_65_0_older_date); + + assert!(version_1_66_0 > version_1_65_0); + assert!(version_1_66_0 > version_1_66_0_nightly); + assert!(version_1_66_0 > version_1_66_0_nightly_older_date); + assert!(version_1_66_0 > version_1_65_0_older_date); + + assert!(version_1_65_0 > version_1_66_0_nightly); + assert!(version_1_65_0 > version_1_66_0_nightly_older_date); + assert!(version_1_65_0 > version_1_65_0_older_date); + + let mut versions = vec![ + version_1_66_0, + version_1_66_0_nightly, + version_1_66_0_nightly_older_date, + version_1_65_0_older_date, + version_1_65_0, + version_1_66_1, + ]; + versions.sort_by(|a, b| b.cmp(a)); + + let expected_versions_order = vec![ + version_1_66_1, + version_1_66_0, + version_1_65_0, + version_1_65_0_older_date, + version_1_66_0_nightly, + version_1_66_0_nightly_older_date, + ]; + assert_eq!(expected_versions_order, versions); + } + + #[test] + fn parse_with_newline() { + let version_1_66_0 = Version::extract("cargo 1.66.0 (d65d197ad 2022-11-15)\n").unwrap(); + assert_eq!( + Version { + major: 1, + minor: 66, + patch: 0, + is_nightly: false, + year: 2022, + month: 11, + day: 15 + }, + version_1_66_0 + ); + } +} From 4498e271881194bd626b1ac94e7ae7aa1f2e37c4 Mon Sep 17 00:00:00 2001 From: Mira Ressel Date: Wed, 29 Mar 2023 12:27:48 +0200 Subject: [PATCH 308/558] provide a default value for RELENG_SCRIPTS_BRANCH (#13743) Currently this variable is configured exclusively via a project variable, which makes it impossible to override for specific test prs. --- .gitlab-ci.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 093a0bb41..c9b68bca1 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -50,6 +50,7 @@ variables: ARCH: "x86_64" CI_IMAGE: "paritytech/ci-linux:production" BUILDAH_IMAGE: "quay.io/buildah/stable:v1.27" + RELENG_SCRIPTS_BRANCH: "master" RUSTY_CACHIER_SINGLE_BRANCH: master RUSTY_CACHIER_DONT_OPERATE_ON_MAIN_BRANCH: "true" RUSTY_CACHIER_COMPRESSION_METHOD: zstd From 56d8cbfcac926439aa78b684b7c77921dd2f4a7f Mon Sep 17 00:00:00 2001 From: Mira Ressel Date: Wed, 29 Mar 2023 15:27:40 +0200 Subject: [PATCH 309/558] roll out new debian 11 ci image (#13744) --- .gitlab-ci.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index c9b68bca1..3928c1901 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -48,9 +48,9 @@ variables: CARGO_INCREMENTAL: 0 DOCKER_OS: "debian:stretch" ARCH: "x86_64" - CI_IMAGE: "paritytech/ci-linux:production" + CI_IMAGE: "paritytech/ci-linux@sha256:a8b63e4f1ab37f90034b9edd3a936a1d4ae897560e25010e0f2fccd8274c6f27" # staging 2023-03-28 BUILDAH_IMAGE: "quay.io/buildah/stable:v1.27" - RELENG_SCRIPTS_BRANCH: "master" + RELENG_SCRIPTS_BRANCH: "mira/pg-13" # TODO: back to master when the ci image is moved back to prod RUSTY_CACHIER_SINGLE_BRANCH: master RUSTY_CACHIER_DONT_OPERATE_ON_MAIN_BRANCH: "true" RUSTY_CACHIER_COMPRESSION_METHOD: zstd From 49ba186c53c24a3ace99c55ecd75370d8e65da1f Mon Sep 17 00:00:00 2001 From: Davide Galassi Date: Wed, 29 Mar 2023 15:46:05 +0200 Subject: [PATCH 310/558] Swap 'base58' with 'bs58' (#13739) * Swap base58 with bs58 * Removed unused clone * std flag --- Cargo.lock | 8 +------- primitives/core/Cargo.toml | 14 ++++---------- primitives/core/src/crypto.rs | 6 ++---- 3 files changed, 7 insertions(+), 21 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index cf30a6a94..a49f8fc42 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -490,12 +490,6 @@ version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4c7f02d4ea65f2c1853089ffd8d2787bdbc63de2f0d29dedbcf8ccdfa0ccd4cf" -[[package]] -name = "base58" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6107fe1be6682a68940da878d9e9f5e90ca5745b3dec9fd1bb393c8777d4f581" - [[package]] name = "base64" version = "0.13.1" @@ -10357,10 +10351,10 @@ name = "sp-core" version = "7.0.0" dependencies = [ "array-bytes", - "base58", "bitflags", "blake2", "bounded-collections", + "bs58", "criterion", "dyn-clonable", "ed25519-zebra", diff --git a/primitives/core/Cargo.toml b/primitives/core/Cargo.toml index 092baeeda..9b253fd15 100644 --- a/primitives/core/Cargo.toml +++ b/primitives/core/Cargo.toml @@ -13,10 +13,7 @@ documentation = "https://docs.rs/sp-core" targets = ["x86_64-unknown-linux-gnu"] [dependencies] -codec = { package = "parity-scale-codec", version = "3.2.2", default-features = false, features = [ - "derive", - "max-encoded-len", -] } +codec = { package = "parity-scale-codec", version = "3.2.2", default-features = false, features = ["derive","max-encoded-len"] } scale-info = { version = "2.1.1", default-features = false, features = ["derive"] } log = { version = "0.4.17", default-features = false } serde = { version = "1.0.136", optional = true, features = ["derive"] } @@ -25,7 +22,7 @@ primitive-types = { version = "0.12.0", default-features = false, features = ["c impl-serde = { version = "0.4.0", optional = true } hash-db = { version = "0.16.0", default-features = false } hash256-std-hasher = { version = "0.15.2", default-features = false } -base58 = { version = "0.2.0", optional = true } +bs58 = { version = "0.4.0", default-features = false, optional = true } rand = { version = "0.8.5", features = ["small_rng"], optional = true } substrate-bip39 = { version = "0.4.4", optional = true } tiny-bip39 = { version = "1.0.0", optional = true } @@ -47,10 +44,7 @@ bitflags = "1.3" array-bytes = { version = "4.1", optional = true } ed25519-zebra = { version = "3.1.0", default-features = false, optional = true } blake2 = { version = "0.10.4", default-features = false, optional = true } -schnorrkel = { version = "0.9.1", features = [ - "preaudit_deprecated", - "u64_backend", -], default-features = false, optional = true } +schnorrkel = { version = "0.9.1", features = ["preaudit_deprecated", "u64_backend"], default-features = false, optional = true } libsecp256k1 = { version = "0.7", default-features = false, features = ["static-context"], optional = true } merlin = { version = "2.0", default-features = false, optional = true } secp256k1 = { version = "0.24.0", default-features = false, features = ["recovery", "alloc"], optional = true } @@ -96,7 +90,7 @@ std = [ "blake2/std", "array-bytes", "ed25519-zebra/std", - "base58", + "bs58/std", "substrate-bip39", "tiny-bip39", "rand", diff --git a/primitives/core/src/crypto.rs b/primitives/core/src/crypto.rs index 2e60ac237..0f86abf86 100644 --- a/primitives/core/src/crypto.rs +++ b/primitives/core/src/crypto.rs @@ -21,8 +21,6 @@ use crate::{ed25519, sr25519}; #[cfg(feature = "std")] -use base58::{FromBase58, ToBase58}; -#[cfg(feature = "std")] use bip39::{Language, Mnemonic, MnemonicType}; use codec::{Decode, Encode, MaxEncodedLen}; #[cfg(feature = "std")] @@ -276,7 +274,7 @@ pub trait Ss58Codec: Sized + AsMut<[u8]> + AsRef<[u8]> + ByteArray { const CHECKSUM_LEN: usize = 2; let body_len = Self::LEN; - let data = s.from_base58().map_err(|_| PublicError::BadBase58)?; + let data = bs58::decode(s).into_vec().map_err(|_| PublicError::BadBase58)?; if data.len() < 2 { return Err(PublicError::BadLength) } @@ -345,7 +343,7 @@ pub trait Ss58Codec: Sized + AsMut<[u8]> + AsRef<[u8]> + ByteArray { v.extend(self.as_ref()); let r = ss58hash(&v); v.extend(&r[0..2]); - v.to_base58() + bs58::encode(v).into_string() } /// Return the ss58-check string for this key. From d7b9969c96f621b9cd93f91ebc8220aa620ac0e8 Mon Sep 17 00:00:00 2001 From: Michal Kucharczyk <1728078+michalkucharczyk@users.noreply.github.com> Date: Wed, 29 Mar 2023 16:54:02 +0200 Subject: [PATCH 311/558] proc-macro: check for non-args runtime calls added (#13742) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * proc-macro: check for non-args runtime calls added * Update primitives/api/proc-macro/src/impl_runtime_apis.rs Co-authored-by: Bastian Köcher --------- Co-authored-by: Bastian Köcher Co-authored-by: parity-processbot <> --- primitives/api/proc-macro/src/impl_runtime_apis.rs | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/primitives/api/proc-macro/src/impl_runtime_apis.rs b/primitives/api/proc-macro/src/impl_runtime_apis.rs index 5ac07975d..d0725ffd2 100644 --- a/primitives/api/proc-macro/src/impl_runtime_apis.rs +++ b/primitives/api/proc-macro/src/impl_runtime_apis.rs @@ -79,7 +79,14 @@ fn generate_impl_call( let pborrow = params.iter().map(|v| &v.2); let decode_params = if params.is_empty() { - quote!() + quote!( + if !#input.is_empty() { + panic!( + "Bad input data provided to {}: expected no parameters, but input buffer is not empty.", + #fn_name_str + ); + } + ) } else { let let_binding = if params.len() == 1 { quote! { From bf8e565791ca2760a52a1074168808d6858cd8cb Mon Sep 17 00:00:00 2001 From: Mira Ressel Date: Wed, 29 Mar 2023 20:25:41 +0200 Subject: [PATCH 312/558] Clean up after debian 11 rollout (#13762) This reverts commit 56d8cbfcac926439aa78b684b7c77921dd2f4a7f. --- .gitlab-ci.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 3928c1901..c9b68bca1 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -48,9 +48,9 @@ variables: CARGO_INCREMENTAL: 0 DOCKER_OS: "debian:stretch" ARCH: "x86_64" - CI_IMAGE: "paritytech/ci-linux@sha256:a8b63e4f1ab37f90034b9edd3a936a1d4ae897560e25010e0f2fccd8274c6f27" # staging 2023-03-28 + CI_IMAGE: "paritytech/ci-linux:production" BUILDAH_IMAGE: "quay.io/buildah/stable:v1.27" - RELENG_SCRIPTS_BRANCH: "mira/pg-13" # TODO: back to master when the ci image is moved back to prod + RELENG_SCRIPTS_BRANCH: "master" RUSTY_CACHIER_SINGLE_BRANCH: master RUSTY_CACHIER_DONT_OPERATE_ON_MAIN_BRANCH: "true" RUSTY_CACHIER_COMPRESSION_METHOD: zstd From e2176625e4120c01fa524fca858747c7a78c1bb7 Mon Sep 17 00:00:00 2001 From: Roman Useinov Date: Wed, 29 Mar 2023 22:30:15 +0200 Subject: [PATCH 313/558] [Fix] Bump tuple element number in frame-support. (#13760) That is to avoid hitting the pallet limit that emits a cryptic error. More detail could be found here: https://substrate.stackexchange.com/questions/7212 . Co-authored-by: parity-processbot <> --- bin/node/runtime/Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bin/node/runtime/Cargo.toml b/bin/node/runtime/Cargo.toml index 16f1d0a4c..bc9f3af9d 100644 --- a/bin/node/runtime/Cargo.toml +++ b/bin/node/runtime/Cargo.toml @@ -46,7 +46,7 @@ sp-io = { version = "7.0.0", default-features = false, path = "../../../primitiv frame-executive = { version = "4.0.0-dev", default-features = false, path = "../../../frame/executive" } frame-benchmarking = { version = "4.0.0-dev", default-features = false, path = "../../../frame/benchmarking" } frame-benchmarking-pallet-pov = { version = "4.0.0-dev", default-features = false, path = "../../../frame/benchmarking/pov" } -frame-support = { version = "4.0.0-dev", default-features = false, path = "../../../frame/support" } +frame-support = { version = "4.0.0-dev", default-features = false, path = "../../../frame/support", features = ["tuples-96"] } frame-system = { version = "4.0.0-dev", default-features = false, path = "../../../frame/system" } frame-system-benchmarking = { version = "4.0.0-dev", default-features = false, path = "../../../frame/system/benchmarking", optional = true } frame-election-provider-support = { version = "4.0.0-dev", default-features = false, path = "../../../frame/election-provider-support" } From 9c92e4987160a17daa72f79186d981b6fbe5879e Mon Sep 17 00:00:00 2001 From: Davide Galassi Date: Thu, 30 Mar 2023 01:22:34 +0200 Subject: [PATCH 314/558] Generic keystore internals (#13749) * Generic testing keystore internals * Generic local keystore internals * Restore deleted comment --- client/keystore/src/local.rs | 121 +++++++++------------ primitives/keystore/src/testing.rs | 164 +++++++++-------------------- 2 files changed, 95 insertions(+), 190 deletions(-) diff --git a/client/keystore/src/local.rs b/client/keystore/src/local.rs index abdf97bb2..2486b2fd0 100644 --- a/client/keystore/src/local.rs +++ b/client/keystore/src/local.rs @@ -63,21 +63,50 @@ impl LocalKeystore { ) -> Result> { self.0.read().key_pair::(public) } -} -impl Keystore for LocalKeystore { - fn sr25519_public_keys(&self, key_type: KeyTypeId) -> Vec { + fn public_keys(&self, key_type: KeyTypeId) -> Vec { self.0 .read() .raw_public_keys(key_type) .map(|v| { - v.into_iter() - .filter_map(|k| sr25519::Public::from_slice(k.as_slice()).ok()) - .collect() + v.into_iter().filter_map(|k| T::Public::from_slice(k.as_slice()).ok()).collect() }) .unwrap_or_default() } + fn generate_new( + &self, + key_type: KeyTypeId, + seed: Option<&str>, + ) -> std::result::Result { + let pair = match seed { + Some(seed) => self.0.write().insert_ephemeral_from_seed_by_type::(seed, key_type), + None => self.0.write().generate_by_type::(key_type), + } + .map_err(|e| -> TraitError { e.into() })?; + Ok(pair.public()) + } + + fn sign( + &self, + key_type: KeyTypeId, + public: &T::Public, + msg: &[u8], + ) -> std::result::Result, TraitError> { + let signature = self + .0 + .read() + .key_pair_by_type::(public, key_type)? + .map(|pair| pair.sign(msg)); + Ok(signature) + } +} + +impl Keystore for LocalKeystore { + fn sr25519_public_keys(&self, key_type: KeyTypeId) -> Vec { + self.public_keys::(key_type) + } + /// Generate a new pair compatible with the 'ed25519' signature scheme. /// /// If the `[seed]` is `Some` then the key will be ephemeral and stored in memory. @@ -86,16 +115,7 @@ impl Keystore for LocalKeystore { key_type: KeyTypeId, seed: Option<&str>, ) -> std::result::Result { - let pair = match seed { - Some(seed) => self - .0 - .write() - .insert_ephemeral_from_seed_by_type::(seed, key_type), - None => self.0.write().generate_by_type::(key_type), - } - .map_err(|e| -> TraitError { e.into() })?; - - Ok(pair.public()) + self.generate_new::(key_type, seed) } fn sr25519_sign( @@ -104,12 +124,7 @@ impl Keystore for LocalKeystore { public: &sr25519::Public, msg: &[u8], ) -> std::result::Result, TraitError> { - let res = self - .0 - .read() - .key_pair_by_type::(public, key_type)? - .map(|pair| pair.sign(msg)); - Ok(res) + self.sign::(key_type, public, msg) } fn sr25519_vrf_sign( @@ -118,24 +133,16 @@ impl Keystore for LocalKeystore { public: &sr25519::Public, transcript_data: VRFTranscriptData, ) -> std::result::Result, TraitError> { - let res = self.0.read().key_pair_by_type::(public, key_type)?.map(|pair| { + let sig = self.0.read().key_pair_by_type::(public, key_type)?.map(|pair| { let transcript = make_transcript(transcript_data); let (inout, proof, _) = pair.as_ref().vrf_sign(transcript); VRFSignature { output: inout.to_output(), proof } }); - Ok(res) + Ok(sig) } fn ed25519_public_keys(&self, key_type: KeyTypeId) -> Vec { - self.0 - .read() - .raw_public_keys(key_type) - .map(|v| { - v.into_iter() - .filter_map(|k| ed25519::Public::from_slice(k.as_slice()).ok()) - .collect() - }) - .unwrap_or_default() + self.public_keys::(key_type) } /// Generate a new pair compatible with the 'sr25519' signature scheme. @@ -146,16 +153,7 @@ impl Keystore for LocalKeystore { key_type: KeyTypeId, seed: Option<&str>, ) -> std::result::Result { - let pair = match seed { - Some(seed) => self - .0 - .write() - .insert_ephemeral_from_seed_by_type::(seed, key_type), - None => self.0.write().generate_by_type::(key_type), - } - .map_err(|e| -> TraitError { e.into() })?; - - Ok(pair.public()) + self.generate_new::(key_type, seed) } fn ed25519_sign( @@ -164,24 +162,11 @@ impl Keystore for LocalKeystore { public: &ed25519::Public, msg: &[u8], ) -> std::result::Result, TraitError> { - let res = self - .0 - .read() - .key_pair_by_type::(public, key_type)? - .map(|pair| pair.sign(msg)); - Ok(res) + self.sign::(key_type, public, msg) } fn ecdsa_public_keys(&self, key_type: KeyTypeId) -> Vec { - self.0 - .read() - .raw_public_keys(key_type) - .map(|v| { - v.into_iter() - .filter_map(|k| ecdsa::Public::from_slice(k.as_slice()).ok()) - .collect() - }) - .unwrap_or_default() + self.public_keys::(key_type) } /// Generate a new pair compatible with the 'ecdsa' signature scheme. @@ -192,14 +177,7 @@ impl Keystore for LocalKeystore { key_type: KeyTypeId, seed: Option<&str>, ) -> std::result::Result { - let pair = match seed { - Some(seed) => - self.0.write().insert_ephemeral_from_seed_by_type::(seed, key_type), - None => self.0.write().generate_by_type::(key_type), - } - .map_err(|e| -> TraitError { e.into() })?; - - Ok(pair.public()) + self.generate_new::(key_type, seed) } fn ecdsa_sign( @@ -208,12 +186,7 @@ impl Keystore for LocalKeystore { public: &ecdsa::Public, msg: &[u8], ) -> std::result::Result, TraitError> { - let res = self - .0 - .read() - .key_pair_by_type::(public, key_type)? - .map(|pair| pair.sign(msg)); - Ok(res) + self.sign::(key_type, public, msg) } fn ecdsa_sign_prehashed( @@ -222,12 +195,12 @@ impl Keystore for LocalKeystore { public: &ecdsa::Public, msg: &[u8; 32], ) -> std::result::Result, TraitError> { - let res = self + let sig = self .0 .read() .key_pair_by_type::(public, key_type)? .map(|pair| pair.sign_prehashed(msg)); - Ok(res) + Ok(sig) } fn insert( diff --git a/primitives/keystore/src/testing.rs b/primitives/keystore/src/testing.rs index de034c658..e5107cba7 100644 --- a/primitives/keystore/src/testing.rs +++ b/primitives/keystore/src/testing.rs @@ -42,65 +42,36 @@ impl MemoryKeystore { Self::default() } - fn sr25519_key_pair( - &self, - key_type: KeyTypeId, - public: &sr25519::Public, - ) -> Option { - self.keys.read().get(&key_type).and_then(|inner| { - inner.get(public.as_slice()).map(|s| { - sr25519::Pair::from_string(s, None).expect("`sr25519` seed slice is valid") - }) - }) - } - - fn ed25519_key_pair( - &self, - key_type: KeyTypeId, - public: &ed25519::Public, - ) -> Option { - self.keys.read().get(&key_type).and_then(|inner| { - inner.get(public.as_slice()).map(|s| { - ed25519::Pair::from_string(s, None).expect("`ed25519` seed slice is valid") - }) - }) - } - - fn ecdsa_key_pair(&self, key_type: KeyTypeId, public: &ecdsa::Public) -> Option { + fn pair(&self, key_type: KeyTypeId, public: &T::Public) -> Option { self.keys.read().get(&key_type).and_then(|inner| { inner .get(public.as_slice()) - .map(|s| ecdsa::Pair::from_string(s, None).expect("`ecdsa` seed slice is valid")) + .map(|s| T::from_string(s, None).expect("seed slice is valid")) }) } -} -impl Keystore for MemoryKeystore { - fn sr25519_public_keys(&self, key_type: KeyTypeId) -> Vec { + fn public_keys(&self, key_type: KeyTypeId) -> Vec { self.keys .read() .get(&key_type) .map(|keys| { keys.values() - .map(|s| { - sr25519::Pair::from_string(s, None).expect("`sr25519` seed slice is valid") - }) + .map(|s| T::from_string(s, None).expect("seed slice is valid")) .map(|p| p.public()) .collect() }) .unwrap_or_default() } - fn sr25519_generate_new( + fn generate_new( &self, key_type: KeyTypeId, seed: Option<&str>, - ) -> Result { + ) -> Result { match seed { Some(seed) => { - let pair = sr25519::Pair::from_string(seed, None).map_err(|_| { - Error::ValidationError("Generates an `sr25519` pair.".to_owned()) - })?; + let pair = T::from_string(seed, None) + .map_err(|_| Error::ValidationError("Generates a pair.".to_owned()))?; self.keys .write() .entry(key_type) @@ -109,7 +80,7 @@ impl Keystore for MemoryKeystore { Ok(pair.public()) }, None => { - let (pair, phrase, _) = sr25519::Pair::generate_with_phrase(None); + let (pair, phrase, _) = T::generate_with_phrase(None); self.keys .write() .entry(key_type) @@ -120,13 +91,37 @@ impl Keystore for MemoryKeystore { } } + fn sign( + &self, + key_type: KeyTypeId, + public: &T::Public, + msg: &[u8], + ) -> Result, Error> { + let sig = self.pair::(key_type, public).map(|pair| pair.sign(msg)); + Ok(sig) + } +} + +impl Keystore for MemoryKeystore { + fn sr25519_public_keys(&self, key_type: KeyTypeId) -> Vec { + self.public_keys::(key_type) + } + + fn sr25519_generate_new( + &self, + key_type: KeyTypeId, + seed: Option<&str>, + ) -> Result { + self.generate_new::(key_type, seed) + } + fn sr25519_sign( &self, key_type: KeyTypeId, public: &sr25519::Public, msg: &[u8], ) -> Result, Error> { - Ok(self.sr25519_key_pair(key_type, public).map(|pair| pair.sign(msg))) + self.sign::(key_type, public, msg) } fn sr25519_vrf_sign( @@ -135,27 +130,16 @@ impl Keystore for MemoryKeystore { public: &sr25519::Public, transcript_data: VRFTranscriptData, ) -> Result, Error> { - let transcript = make_transcript(transcript_data); - let pair = - if let Some(k) = self.sr25519_key_pair(key_type, public) { k } else { return Ok(None) }; - - let (inout, proof, _) = pair.as_ref().vrf_sign(transcript); - Ok(Some(VRFSignature { output: inout.to_output(), proof })) + let sig = self.pair::(key_type, public).map(|pair| { + let transcript = make_transcript(transcript_data); + let (inout, proof, _) = pair.as_ref().vrf_sign(transcript); + VRFSignature { output: inout.to_output(), proof } + }); + Ok(sig) } fn ed25519_public_keys(&self, key_type: KeyTypeId) -> Vec { - self.keys - .read() - .get(&key_type) - .map(|keys| { - keys.values() - .map(|s| { - ed25519::Pair::from_string(s, None).expect("`ed25519` seed slice is valid") - }) - .map(|p| p.public()) - .collect() - }) - .unwrap_or_default() + self.public_keys::(key_type) } fn ed25519_generate_new( @@ -163,28 +147,7 @@ impl Keystore for MemoryKeystore { key_type: KeyTypeId, seed: Option<&str>, ) -> Result { - match seed { - Some(seed) => { - let pair = ed25519::Pair::from_string(seed, None).map_err(|_| { - Error::ValidationError("Generates an `ed25519` pair.".to_owned()) - })?; - self.keys - .write() - .entry(key_type) - .or_default() - .insert(pair.public().to_raw_vec(), seed.into()); - Ok(pair.public()) - }, - None => { - let (pair, phrase, _) = ed25519::Pair::generate_with_phrase(None); - self.keys - .write() - .entry(key_type) - .or_default() - .insert(pair.public().to_raw_vec(), phrase); - Ok(pair.public()) - }, - } + self.generate_new::(key_type, seed) } fn ed25519_sign( @@ -193,22 +156,11 @@ impl Keystore for MemoryKeystore { public: &ed25519::Public, msg: &[u8], ) -> Result, Error> { - Ok(self.ed25519_key_pair(key_type, public).map(|pair| pair.sign(msg))) + self.sign::(key_type, public, msg) } fn ecdsa_public_keys(&self, key_type: KeyTypeId) -> Vec { - self.keys - .read() - .get(&key_type) - .map(|keys| { - keys.values() - .map(|s| { - ecdsa::Pair::from_string(s, None).expect("`ecdsa` seed slice is valid") - }) - .map(|p| p.public()) - .collect() - }) - .unwrap_or_default() + self.public_keys::(key_type) } fn ecdsa_generate_new( @@ -216,27 +168,7 @@ impl Keystore for MemoryKeystore { key_type: KeyTypeId, seed: Option<&str>, ) -> Result { - match seed { - Some(seed) => { - let pair = ecdsa::Pair::from_string(seed, None) - .map_err(|_| Error::ValidationError("Generates an `ecdsa` pair.".to_owned()))?; - self.keys - .write() - .entry(key_type) - .or_default() - .insert(pair.public().to_raw_vec(), seed.into()); - Ok(pair.public()) - }, - None => { - let (pair, phrase, _) = ecdsa::Pair::generate_with_phrase(None); - self.keys - .write() - .entry(key_type) - .or_default() - .insert(pair.public().to_raw_vec(), phrase); - Ok(pair.public()) - }, - } + self.generate_new::(key_type, seed) } fn ecdsa_sign( @@ -245,7 +177,7 @@ impl Keystore for MemoryKeystore { public: &ecdsa::Public, msg: &[u8], ) -> Result, Error> { - Ok(self.ecdsa_key_pair(key_type, public).map(|pair| pair.sign(msg))) + self.sign::(key_type, public, msg) } fn ecdsa_sign_prehashed( @@ -254,8 +186,8 @@ impl Keystore for MemoryKeystore { public: &ecdsa::Public, msg: &[u8; 32], ) -> Result, Error> { - let pair = self.ecdsa_key_pair(key_type, public); - pair.map(|pair| pair.sign_prehashed(msg)).map(Ok).transpose() + let sig = self.pair::(key_type, public).map(|pair| pair.sign_prehashed(msg)); + Ok(sig) } fn insert(&self, key_type: KeyTypeId, suri: &str, public: &[u8]) -> Result<(), ()> { From 14f1a3951d5a9de4dd3ae3ff83fd5ef66c4fbbe7 Mon Sep 17 00:00:00 2001 From: Davide Galassi Date: Thu, 30 Mar 2023 10:30:35 +0200 Subject: [PATCH 315/558] Application Crypto cleanup (#13746) * Adjust application crypto docs * Blanket implementation for 'RuntimeAppPublic' trait * Blanket implementation for 'BoundToRuntimeAppPublic' for 'RuntimeAppPublic' * Relax type bounds * Docs fix * restore MaybeHash * Commit suggestion Co-authored-by: Anton --------- Co-authored-by: Anton --- primitives/application-crypto/src/ecdsa.rs | 8 +- primitives/application-crypto/src/ed25519.rs | 8 +- primitives/application-crypto/src/lib.rs | 100 +++++++------------ primitives/application-crypto/src/sr25519.rs | 8 +- primitives/application-crypto/src/traits.rs | 69 +++++++++---- primitives/core/src/crypto.rs | 2 +- primitives/runtime/src/testing.rs | 7 +- 7 files changed, 91 insertions(+), 111 deletions(-) diff --git a/primitives/application-crypto/src/ecdsa.rs b/primitives/application-crypto/src/ecdsa.rs index 011456df0..27ffe1257 100644 --- a/primitives/application-crypto/src/ecdsa.rs +++ b/primitives/application-crypto/src/ecdsa.rs @@ -24,13 +24,7 @@ use sp_std::vec::Vec; pub use sp_core::ecdsa::*; mod app { - use sp_core::testing::ECDSA; - - crate::app_crypto!(super, ECDSA); - - impl crate::traits::BoundToRuntimeAppPublic for Public { - type Public = Self; - } + crate::app_crypto!(super, sp_core::testing::ECDSA); } #[cfg(feature = "full_crypto")] diff --git a/primitives/application-crypto/src/ed25519.rs b/primitives/application-crypto/src/ed25519.rs index 822dd3fa5..bc0501837 100644 --- a/primitives/application-crypto/src/ed25519.rs +++ b/primitives/application-crypto/src/ed25519.rs @@ -24,13 +24,7 @@ use sp_std::vec::Vec; pub use sp_core::ed25519::*; mod app { - use sp_core::testing::ED25519; - - crate::app_crypto!(super, ED25519); - - impl crate::traits::BoundToRuntimeAppPublic for Public { - type Public = Self; - } + crate::app_crypto!(super, sp_core::testing::ED25519); } #[cfg(feature = "full_crypto")] diff --git a/primitives/application-crypto/src/lib.rs b/primitives/application-crypto/src/lib.rs index bf7388133..3f12e06e1 100644 --- a/primitives/application-crypto/src/lib.rs +++ b/primitives/application-crypto/src/lib.rs @@ -48,14 +48,15 @@ mod traits; pub use traits::*; -/// Declares Public, Pair, Signature types which are functionally equivalent to `$pair`, but are new -/// Application-specific types whose identifier is `$key_type`. +/// Declares `Public`, `Pair` and `Signature` types which are functionally equivalent +/// to the corresponding types defined by `$module` but are new application-specific +/// types whose identifier is `$key_type`. /// /// ```rust -/// # use sp_application_crypto::{app_crypto, wrap, ed25519, KeyTypeId}; -/// // Declare a new set of crypto types using Ed25519 logic that identifies as `KeyTypeId` +/// # use sp_application_crypto::{app_crypto, ed25519, KeyTypeId}; +/// // Declare a new set of crypto types using ed25519 logic that identifies as `KeyTypeId` /// // of value `b"fuba"`. -/// app_crypto!(ed25519, KeyTypeId(*b"_uba")); +/// app_crypto!(ed25519, KeyTypeId(*b"fuba")); /// ``` #[cfg(feature = "full_crypto")] #[macro_export] @@ -78,14 +79,15 @@ macro_rules! app_crypto { }; } -/// Declares Public, Pair, Signature types which are functionally equivalent to `$pair`, but are new -/// Application-specific types whose identifier is `$key_type`. +/// Declares `Public`, `Pair` and `Signature` types which are functionally equivalent +/// to the corresponding types defined by `$module` but that are new application-specific +/// types whose identifier is `$key_type`. /// /// ```rust -/// # use sp_application_crypto::{app_crypto, wrap, ed25519, KeyTypeId}; -/// // Declare a new set of crypto types using Ed25519 logic that identifies as `KeyTypeId` +/// # use sp_application_crypto::{app_crypto, ed25519, KeyTypeId}; +/// // Declare a new set of crypto types using ed25519 logic that identifies as `KeyTypeId` /// // of value `b"fuba"`. -/// app_crypto!(ed25519, KeyTypeId(*b"_uba")); +/// app_crypto!(ed25519, KeyTypeId(*b"fuba")); /// ``` #[cfg(not(feature = "full_crypto"))] #[macro_export] @@ -107,8 +109,8 @@ macro_rules! app_crypto { }; } -/// Declares Pair type which is functionally equivalent to `$pair`, but is new -/// Application-specific type whose identifier is `$key_type`. +/// Declares `Pair` type which is functionally equivalent to `$pair`, but is +/// new application-specific type whose identifier is `$key_type`. #[macro_export] macro_rules! app_crypto_pair { ($pair:ty, $key_type:expr, $crypto_type:expr) => { @@ -208,10 +210,10 @@ macro_rules! app_crypto_pair_functions_if_std { ($pair:ty) => {}; } -/// Declares Public type which is functionally equivalent to `$public`, but is new -/// Application-specific type whose identifier is `$key_type`. -/// can only be used together with `full_crypto` feature -/// For full functionality, app_crypto_public_common! must be called too. +/// Declares `Public` type which is functionally equivalent to `$public` but is +/// new application-specific type whose identifier is `$key_type`. +/// For full functionality, `app_crypto_public_common!` must be called too. +/// Can only be used with `full_crypto` feature. #[doc(hidden)] #[macro_export] macro_rules! app_crypto_public_full_crypto { @@ -244,10 +246,10 @@ macro_rules! app_crypto_public_full_crypto { }; } -/// Declares Public type which is functionally equivalent to `$public`, but is new -/// Application-specific type whose identifier is `$key_type`. -/// can only be used without `full_crypto` feature -/// For full functionality, app_crypto_public_common! must be called too. +/// Declares `Public` type which is functionally equivalent to `$public` but is +/// new application-specific type whose identifier is `$key_type`. +/// For full functionality, `app_crypto_public_common!` must be called too. +/// Can only be used without `full_crypto` feature. #[doc(hidden)] #[macro_export] macro_rules! app_crypto_public_not_full_crypto { @@ -276,9 +278,9 @@ macro_rules! app_crypto_public_not_full_crypto { }; } -/// Declares Public type which is functionally equivalent to `$public`, but is new -/// Application-specific type whose identifier is `$key_type`. -/// For full functionality, app_crypto_public_(not)_full_crypto! must be called too. +/// Declares `Public` type which is functionally equivalent to `$public` but is +/// new application-specific type whose identifier is `$key_type`. +/// For full functionality, `app_crypto_public_(not)_full_crypto!` must be called too. #[doc(hidden)] #[macro_export] macro_rules! app_crypto_public_common { @@ -307,40 +309,6 @@ macro_rules! app_crypto_public_common { type Generic = $public; } - impl $crate::RuntimeAppPublic for Public - where - $public: $crate::RuntimePublic, - { - const ID: $crate::KeyTypeId = $key_type; - const CRYPTO_ID: $crate::CryptoTypeId = $crypto_type; - - type Signature = Signature; - - fn all() -> $crate::Vec { - <$public as $crate::RuntimePublic>::all($key_type) - .into_iter() - .map(Self) - .collect() - } - - fn generate_pair(seed: Option<$crate::Vec>) -> Self { - Self(<$public as $crate::RuntimePublic>::generate_pair($key_type, seed)) - } - - fn sign>(&self, msg: &M) -> Option { - <$public as $crate::RuntimePublic>::sign(self.as_ref(), $key_type, msg) - .map(Signature) - } - - fn verify>(&self, msg: &M, signature: &Self::Signature) -> bool { - <$public as $crate::RuntimePublic>::verify(self.as_ref(), msg, &signature.as_ref()) - } - - fn to_raw_vec(&self) -> $crate::Vec { - <$public as $crate::RuntimePublic>::to_raw_vec(&self.0) - } - } - impl<'a> TryFrom<&'a [u8]> for Public { type Error = (); @@ -407,8 +375,8 @@ macro_rules! app_crypto_public_common_if_std { /// Declares Signature type which is functionally equivalent to `$sig`, but is new /// Application-specific type whose identifier is `$key_type`. -/// can only be used together with `full_crypto` feature /// For full functionality, app_crypto_public_common! must be called too. +/// Can only be used with `full_crypto` feature #[doc(hidden)] #[macro_export] macro_rules! app_crypto_signature_full_crypto { @@ -439,10 +407,10 @@ macro_rules! app_crypto_signature_full_crypto { }; } -/// Declares Signature type which is functionally equivalent to `$sig`, but is new -/// Application-specific type whose identifier is `$key_type`. -/// can only be used without `full_crypto` feature -/// For full functionality, app_crypto_public_common! must be called too. +/// Declares `Signature` type which is functionally equivalent to `$sig`, but is new +/// application-specific type whose identifier is `$key_type`. +/// For full functionality, `app_crypto_signature_common` must be called too. +/// Can only be used without `full_crypto` feature. #[doc(hidden)] #[macro_export] macro_rules! app_crypto_signature_not_full_crypto { @@ -452,8 +420,8 @@ macro_rules! app_crypto_signature_not_full_crypto { #[derive(Clone, Eq, PartialEq, $crate::codec::Encode, $crate::codec::Decode, - $crate::scale_info::TypeInfo, $crate::RuntimeDebug, + $crate::scale_info::TypeInfo, )] pub struct Signature($sig); } @@ -469,9 +437,9 @@ macro_rules! app_crypto_signature_not_full_crypto { }; } -/// Declares Signature type which is functionally equivalent to `$sig`, but is new -/// Application-specific type whose identifier is `$key_type`. -/// For full functionality, app_crypto_public_(not)_full_crypto! must be called too. +/// Declares `Signature` type which is functionally equivalent to `$sig`, but is new +/// application-specific type whose identifier is `$key_type`. +/// For full functionality, app_crypto_signature_(not)_full_crypto! must be called too. #[doc(hidden)] #[macro_export] macro_rules! app_crypto_signature_common { diff --git a/primitives/application-crypto/src/sr25519.rs b/primitives/application-crypto/src/sr25519.rs index a961f5cf3..7c91bfa7b 100644 --- a/primitives/application-crypto/src/sr25519.rs +++ b/primitives/application-crypto/src/sr25519.rs @@ -24,13 +24,7 @@ use sp_std::vec::Vec; pub use sp_core::sr25519::*; mod app { - use sp_core::testing::SR25519; - - crate::app_crypto!(super, SR25519); - - impl crate::traits::BoundToRuntimeAppPublic for Public { - type Public = Self; - } + crate::app_crypto!(super, sp_core::testing::SR25519); } #[cfg(feature = "full_crypto")] diff --git a/primitives/application-crypto/src/traits.rs b/primitives/application-crypto/src/traits.rs index b3bcd0ce2..88d4bf369 100644 --- a/primitives/application-crypto/src/traits.rs +++ b/primitives/application-crypto/src/traits.rs @@ -15,10 +15,11 @@ // See the License for the specific language governing permissions and // limitations under the License. +use codec::Codec; +use scale_info::TypeInfo; + #[cfg(feature = "full_crypto")] use sp_core::crypto::Pair; - -use codec::Codec; use sp_core::crypto::{CryptoType, CryptoTypeId, IsWrappedBy, KeyTypeId, Public}; use sp_std::{fmt::Debug, vec::Vec}; @@ -60,15 +61,9 @@ pub trait MaybeHash {} #[cfg(all(not(feature = "std"), not(feature = "full_crypto")))] impl MaybeHash for T {} -/// Type which implements Debug and Hash in std, not when no-std (no-std variant with crypto). -#[cfg(all(not(feature = "std"), feature = "full_crypto"))] -pub trait MaybeDebugHash: sp_std::hash::Hash {} -#[cfg(all(not(feature = "std"), feature = "full_crypto"))] -impl MaybeDebugHash for T {} - /// A application's public key. pub trait AppPublic: - AppCrypto + Public + Ord + PartialOrd + Eq + PartialEq + Debug + MaybeHash + codec::Codec + AppCrypto + Public + Ord + PartialOrd + Eq + PartialEq + Debug + MaybeHash + Codec { /// The wrapped type which is just a plain instance of `Public`. type Generic: IsWrappedBy @@ -79,7 +74,7 @@ pub trait AppPublic: + PartialEq + Debug + MaybeHash - + codec::Codec; + + Codec; } /// A application's key pair. @@ -92,15 +87,15 @@ pub trait AppPair: AppCrypto + Pair::Public> { } /// A application's signature. -pub trait AppSignature: AppCrypto + Eq + PartialEq + Debug + MaybeHash { +pub trait AppSignature: AppCrypto + Eq + PartialEq + Debug { /// The wrapped type which is just a plain instance of `Signature`. - type Generic: IsWrappedBy + Eq + PartialEq + Debug + MaybeHash; + type Generic: IsWrappedBy + Eq + PartialEq + Debug; } /// A runtime interface for a public key. pub trait RuntimePublic: Sized { /// The signature that will be generated when signing with the corresponding private key. - type Signature: Codec + Debug + MaybeHash + Eq + PartialEq + Clone; + type Signature: Debug + Eq + PartialEq + Clone; /// Returns all public keys for the given key type in the keystore. fn all(key_type: KeyTypeId) -> crate::Vec; @@ -132,11 +127,9 @@ pub trait RuntimePublic: Sized { pub trait RuntimeAppPublic: Sized { /// An identifier for this application-specific key type. const ID: KeyTypeId; - /// The identifier of the crypto type of this application-specific key type. - const CRYPTO_ID: CryptoTypeId; /// The signature that will be generated when signing with the corresponding private key. - type Signature: Codec + Debug + MaybeHash + Eq + PartialEq + Clone + scale_info::TypeInfo; + type Signature: Debug + Eq + PartialEq + Clone + TypeInfo + Codec; /// Returns all public keys for this application in the keystore. fn all() -> crate::Vec; @@ -163,8 +156,50 @@ pub trait RuntimeAppPublic: Sized { fn to_raw_vec(&self) -> Vec; } -/// Something that bound to a fixed [`RuntimeAppPublic`]. +impl RuntimeAppPublic for T +where + T: AppPublic + AsRef<::Generic>, + ::Generic: RuntimePublic, + ::Signature: TypeInfo + + Codec + + From<<::Generic as RuntimePublic>::Signature> + + AsRef<<::Generic as RuntimePublic>::Signature>, +{ + const ID: KeyTypeId = ::ID; + + type Signature = ::Signature; + + fn all() -> crate::Vec { + <::Generic as RuntimePublic>::all(Self::ID) + .into_iter() + .map(|p| p.into()) + .collect() + } + + fn generate_pair(seed: Option>) -> Self { + <::Generic as RuntimePublic>::generate_pair(Self::ID, seed).into() + } + + fn sign>(&self, msg: &M) -> Option { + <::Generic as RuntimePublic>::sign(self.as_ref(), Self::ID, msg) + .map(|s| s.into()) + } + + fn verify>(&self, msg: &M, signature: &Self::Signature) -> bool { + <::Generic as RuntimePublic>::verify(self.as_ref(), msg, signature.as_ref()) + } + + fn to_raw_vec(&self) -> Vec { + <::Generic as RuntimePublic>::to_raw_vec(self.as_ref()) + } +} + +/// Something that is bound to a fixed [`RuntimeAppPublic`]. pub trait BoundToRuntimeAppPublic { /// The [`RuntimeAppPublic`] this type is bound to. type Public: RuntimeAppPublic; } + +impl BoundToRuntimeAppPublic for T { + type Public = Self; +} diff --git a/primitives/core/src/crypto.rs b/primitives/core/src/crypto.rs index 0f86abf86..f77e952d8 100644 --- a/primitives/core/src/crypto.rs +++ b/primitives/core/src/crypto.rs @@ -482,7 +482,7 @@ pub trait ByteArray: AsRef<[u8]> + AsMut<[u8]> + for<'a> TryFrom<&'a [u8], Error } } -/// Trait suitable for typical cryptographic PKI key public type. +/// Trait suitable for typical cryptographic key public type. pub trait Public: ByteArray + Derive + CryptoType + PartialEq + Eq + Clone + Send + Sync {} /// An opaque 32-byte cryptographic identifier. diff --git a/primitives/runtime/src/testing.rs b/primitives/runtime/src/testing.rs index 7e2ee5403..6d02e2309 100644 --- a/primitives/runtime/src/testing.rs +++ b/primitives/runtime/src/testing.rs @@ -26,7 +26,7 @@ use crate::{ PostDispatchInfoOf, SignedExtension, ValidateUnsigned, }, transaction_validity::{TransactionSource, TransactionValidity, TransactionValidityError}, - ApplyExtrinsicResultWithInfo, CryptoTypeId, KeyTypeId, + ApplyExtrinsicResultWithInfo, KeyTypeId, }; use serde::{de::Error as DeError, Deserialize, Deserializer, Serialize, Serializer}; use sp_core::{ @@ -115,7 +115,6 @@ impl UintAuthorityId { impl sp_application_crypto::RuntimeAppPublic for UintAuthorityId { const ID: KeyTypeId = key_types::DUMMY; - const CRYPTO_ID: CryptoTypeId = CryptoTypeId(*b"dumm"); type Signature = TestSignature; @@ -157,10 +156,6 @@ impl OpaqueKeys for UintAuthorityId { } } -impl crate::BoundToRuntimeAppPublic for UintAuthorityId { - type Public = Self; -} - impl traits::IdentifyAccount for UintAuthorityId { type AccountId = u64; From 44998291834b3a36a11c8dede63ce60362f1a3a5 Mon Sep 17 00:00:00 2001 From: Aaro Altonen <48052676+altonen@users.noreply.github.com> Date: Thu, 30 Mar 2023 14:59:58 +0300 Subject: [PATCH 316/558] Attempt to relieve pressure on `mpsc_network_worker` (#13725) * Attempt to relieve pressure on `mpsc_network_worker` `SyncingEngine` interacting with `NetworkWorker` can put a lot of strain on the channel if the number of inbound connections is high. This is because `SyncingEngine` is notified of each inbound substream which it then can either accept or reject and this causes a lot of message exchange on the already busy channel. Use a direct channel pair between `Protocol` and `SyncingEngine` to exchange notification events. It is a temporary change to alleviate the problems caused by syncing being an independent protocol and the fix will be removed once `NotificationService` is implemented. * Apply review comments * fixes * trigger ci * Fix tests Verify that both peers have a connection now that the validation goes through `SyncingEngine`. Depending on how the tasks are scheduled, one of them might not have the peer registered in `SyncingEngine` at which point the test won't make any progress because block announcement received from an unknown peer is discarded. Move polling of `ChainSync` at the end of the function so that if a block announcement causes a block request to be sent, that can be sent in the same call to `SyncingEngine::poll()`. --------- Co-authored-by: parity-processbot <> --- Cargo.lock | 1 + client/network/src/config.rs | 11 +- client/network/src/event.rs | 47 ++++++- client/network/src/lib.rs | 6 +- client/network/src/protocol.rs | 108 +++++++++++++--- client/network/src/service.rs | 6 +- client/network/sync/src/engine.rs | 199 +++++++++++++---------------- client/network/test/Cargo.toml | 1 + client/network/test/src/lib.rs | 12 +- client/network/test/src/service.rs | 6 +- client/network/test/src/sync.rs | 2 +- client/service/src/builder.rs | 13 +- 12 files changed, 259 insertions(+), 153 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index a49f8fc42..7fd0122f3 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -9128,6 +9128,7 @@ dependencies = [ "sc-network-light", "sc-network-sync", "sc-service", + "sc-utils", "sp-blockchain", "sp-consensus", "sp-consensus-babe", diff --git a/client/network/src/config.rs b/client/network/src/config.rs index 3c6801f22..781ae9c78 100644 --- a/client/network/src/config.rs +++ b/client/network/src/config.rs @@ -22,6 +22,7 @@ //! See the documentation of [`Params`]. pub use crate::{ + protocol::NotificationsSink, request_responses::{ IncomingRequest, OutgoingResponse, ProtocolConfig as RequestResponseConfig, }, @@ -31,7 +32,12 @@ pub use crate::{ use codec::Encode; use libp2p::{identity::Keypair, multiaddr, Multiaddr, PeerId}; use prometheus_endpoint::Registry; -pub use sc_network_common::{role::Role, sync::warp::WarpSyncProvider, ExHashT}; +pub use sc_network_common::{ + role::{Role, Roles}, + sync::warp::WarpSyncProvider, + ExHashT, +}; +use sc_utils::mpsc::TracingUnboundedSender; use zeroize::Zeroize; use sp_runtime::traits::Block as BlockT; @@ -714,6 +720,9 @@ pub struct Params { /// Block announce protocol configuration pub block_announce_config: NonDefaultSetConfig, + /// TX channel for direct communication with `SyncingEngine` and `Protocol`. + pub tx: TracingUnboundedSender>, + /// Request response protocol configurations pub request_response_protocol_configs: Vec, } diff --git a/client/network/src/event.rs b/client/network/src/event.rs index 3ecd8f931..975fde0e4 100644 --- a/client/network/src/event.rs +++ b/client/network/src/event.rs @@ -19,12 +19,14 @@ //! Network event types. These are are not the part of the protocol, but rather //! events that happen on the network like DHT get/put results received. -use crate::types::ProtocolName; +use crate::{types::ProtocolName, NotificationsSink}; use bytes::Bytes; +use futures::channel::oneshot; use libp2p::{core::PeerId, kad::record::Key}; -use sc_network_common::role::ObservedRole; +use sc_network_common::{role::ObservedRole, sync::message::BlockAnnouncesHandshake}; +use sp_runtime::traits::Block as BlockT; /// Events generated by DHT as a response to get_value and put_value requests. #[derive(Debug, Clone)] @@ -90,3 +92,44 @@ pub enum Event { messages: Vec<(ProtocolName, Bytes)>, }, } + +/// Event sent to `SyncingEngine` +// TODO: remove once `NotificationService` is implemented. +pub enum SyncEvent { + /// Opened a substream with the given node with the given notifications protocol. + /// + /// The protocol is always one of the notification protocols that have been registered. + NotificationStreamOpened { + /// Node we opened the substream with. + remote: PeerId, + /// Received handshake. + received_handshake: BlockAnnouncesHandshake, + /// Notification sink. + sink: NotificationsSink, + /// Channel for reporting accept/reject of the substream. + tx: oneshot::Sender, + }, + + /// Closed a substream with the given node. Always matches a corresponding previous + /// `NotificationStreamOpened` message. + NotificationStreamClosed { + /// Node we closed the substream with. + remote: PeerId, + }, + + /// Notification sink was replaced. + NotificationSinkReplaced { + /// Node we closed the substream with. + remote: PeerId, + /// Notification sink. + sink: NotificationsSink, + }, + + /// Received one or more messages from the given node using the given protocol. + NotificationsReceived { + /// Node we received the message from. + remote: PeerId, + /// Concerned protocol and associated message. + messages: Vec, + }, +} diff --git a/client/network/src/lib.rs b/client/network/src/lib.rs index c290f4b94..5374ac134 100644 --- a/client/network/src/lib.rs +++ b/client/network/src/lib.rs @@ -259,7 +259,7 @@ pub mod request_responses; pub mod types; pub mod utils; -pub use event::{DhtEvent, Event}; +pub use event::{DhtEvent, Event, SyncEvent}; #[doc(inline)] pub use libp2p::{multiaddr, Multiaddr, PeerId}; pub use request_responses::{IfDisconnected, RequestFailure, RequestResponseConfig}; @@ -278,8 +278,8 @@ pub use service::{ NetworkStatusProvider, NetworkSyncForkRequest, NotificationSender as NotificationSenderT, NotificationSenderError, NotificationSenderReady, }, - DecodingError, Keypair, NetworkService, NetworkWorker, NotificationSender, OutboundFailure, - PublicKey, + DecodingError, Keypair, NetworkService, NetworkWorker, NotificationSender, NotificationsSink, + OutboundFailure, PublicKey, }; pub use types::ProtocolName; diff --git a/client/network/src/protocol.rs b/client/network/src/protocol.rs index 06ca02c0c..a7e6f36ef 100644 --- a/client/network/src/protocol.rs +++ b/client/network/src/protocol.rs @@ -24,6 +24,7 @@ use crate::{ use bytes::Bytes; use codec::{DecodeAll, Encode}; +use futures::{channel::oneshot, stream::FuturesUnordered, StreamExt}; use libp2p::{ core::connection::ConnectionId, swarm::{ @@ -35,11 +36,14 @@ use libp2p::{ use log::{debug, error, warn}; use sc_network_common::{role::Roles, sync::message::BlockAnnouncesHandshake}; +use sc_utils::mpsc::TracingUnboundedSender; use sp_runtime::traits::Block as BlockT; use std::{ collections::{HashMap, HashSet, VecDeque}, + future::Future, iter, + pin::Pin, task::Poll, }; @@ -68,6 +72,9 @@ mod rep { pub const BAD_MESSAGE: Rep = Rep::new(-(1 << 12), "Bad message"); } +type PendingSyncSubstreamValidation = + Pin> + Send>>; + // Lock must always be taken in order declared here. pub struct Protocol { /// Pending list of messages to return from `poll` as a priority. @@ -87,6 +94,8 @@ pub struct Protocol { bad_handshake_substreams: HashSet<(PeerId, sc_peerset::SetId)>, /// Connected peers. peers: HashMap, + sync_substream_validations: FuturesUnordered, + tx: TracingUnboundedSender>, _marker: std::marker::PhantomData, } @@ -96,6 +105,7 @@ impl Protocol { roles: Roles, network_config: &config::NetworkConfiguration, block_announces_protocol: config::NonDefaultSetConfig, + tx: TracingUnboundedSender>, ) -> error::Result<(Self, sc_peerset::PeersetHandle, Vec<(PeerId, Multiaddr)>)> { let mut known_addresses = Vec::new(); @@ -179,6 +189,8 @@ impl Protocol { .collect(), bad_handshake_substreams: Default::default(), peers: HashMap::new(), + sync_substream_validations: FuturesUnordered::new(), + tx, // TODO: remove when `BlockAnnouncesHandshake` is moved away from `Protocol` _marker: Default::default(), }; @@ -418,6 +430,23 @@ impl NetworkBehaviour for Protocol { return Poll::Ready(NetworkBehaviourAction::CloseConnection { peer_id, connection }), }; + while let Poll::Ready(Some(validation_result)) = + self.sync_substream_validations.poll_next_unpin(cx) + { + match validation_result { + Ok((peer, roles)) => { + self.peers.insert(peer, roles); + }, + Err(peer) => { + log::debug!( + target: "sub-libp2p", + "`SyncingEngine` rejected stream" + ); + self.behaviour.disconnect_peer(&peer, HARDCODED_PEERSETS_SYNC); + }, + } + } + let outcome = match event { NotificationsOut::CustomProtocolOpen { peer_id, @@ -440,16 +469,29 @@ impl NetworkBehaviour for Protocol { best_hash: handshake.best_hash, genesis_hash: handshake.genesis_hash, }; - self.peers.insert(peer_id, roles); - CustomMessageOutcome::NotificationStreamOpened { - remote: peer_id, - protocol: self.notification_protocols[usize::from(set_id)].clone(), - negotiated_fallback, - received_handshake: handshake.encode(), - roles, - notifications_sink, - } + let (tx, rx) = oneshot::channel(); + let _ = self.tx.unbounded_send( + crate::SyncEvent::NotificationStreamOpened { + remote: peer_id, + received_handshake: handshake, + sink: notifications_sink, + tx, + }, + ); + self.sync_substream_validations.push(Box::pin(async move { + match rx.await { + Ok(accepted) => + if accepted { + Ok((peer_id, roles)) + } else { + Err(peer_id) + }, + Err(_) => Err(peer_id), + } + })); + + CustomMessageOutcome::None }, Ok(msg) => { debug!( @@ -469,15 +511,27 @@ impl NetworkBehaviour for Protocol { let roles = handshake.roles; self.peers.insert(peer_id, roles); - CustomMessageOutcome::NotificationStreamOpened { - remote: peer_id, - protocol: self.notification_protocols[usize::from(set_id)] - .clone(), - negotiated_fallback, - received_handshake, - roles, - notifications_sink, - } + let (tx, rx) = oneshot::channel(); + let _ = self.tx.unbounded_send( + crate::SyncEvent::NotificationStreamOpened { + remote: peer_id, + received_handshake: handshake, + sink: notifications_sink, + tx, + }, + ); + self.sync_substream_validations.push(Box::pin(async move { + match rx.await { + Ok(accepted) => + if accepted { + Ok((peer_id, roles)) + } else { + Err(peer_id) + }, + Err(_) => Err(peer_id), + } + })); + CustomMessageOutcome::None }, Err(err2) => { log::debug!( @@ -535,6 +589,12 @@ impl NetworkBehaviour for Protocol { NotificationsOut::CustomProtocolReplaced { peer_id, notifications_sink, set_id } => if self.bad_handshake_substreams.contains(&(peer_id, set_id)) { CustomMessageOutcome::None + } else if set_id == HARDCODED_PEERSETS_SYNC { + let _ = self.tx.unbounded_send(crate::SyncEvent::NotificationSinkReplaced { + remote: peer_id, + sink: notifications_sink, + }); + CustomMessageOutcome::None } else { CustomMessageOutcome::NotificationStreamReplaced { remote: peer_id, @@ -548,6 +608,12 @@ impl NetworkBehaviour for Protocol { // handshake. The outer layers have never received an opening event about this // substream, and consequently shouldn't receive a closing event either. CustomMessageOutcome::None + } else if set_id == HARDCODED_PEERSETS_SYNC { + let _ = self.tx.unbounded_send(crate::SyncEvent::NotificationStreamClosed { + remote: peer_id, + }); + self.peers.remove(&peer_id); + CustomMessageOutcome::None } else { CustomMessageOutcome::NotificationStreamClosed { remote: peer_id, @@ -558,6 +624,12 @@ impl NetworkBehaviour for Protocol { NotificationsOut::Notification { peer_id, set_id, message } => { if self.bad_handshake_substreams.contains(&(peer_id, set_id)) { CustomMessageOutcome::None + } else if set_id == HARDCODED_PEERSETS_SYNC { + let _ = self.tx.unbounded_send(crate::SyncEvent::NotificationsReceived { + remote: peer_id, + messages: vec![message.freeze()], + }); + CustomMessageOutcome::None } else { let protocol_name = self.notification_protocols[usize::from(set_id)].clone(); CustomMessageOutcome::NotificationsReceived { diff --git a/client/network/src/service.rs b/client/network/src/service.rs index 5f60f5132..9708b24d2 100644 --- a/client/network/src/service.rs +++ b/client/network/src/service.rs @@ -36,7 +36,7 @@ use crate::{ network_state::{ NetworkState, NotConnectedPeer as NetworkStateNotConnectedPeer, Peer as NetworkStatePeer, }, - protocol::{self, NotificationsSink, NotifsHandlerError, Protocol, Ready}, + protocol::{self, NotifsHandlerError, Protocol, Ready}, request_responses::{IfDisconnected, RequestFailure}, service::{ signature::{Signature, SigningError}, @@ -91,6 +91,7 @@ use std::{ pub use behaviour::{InboundFailure, OutboundFailure, ResponseFailure}; pub use libp2p::identity::{error::DecodingError, Keypair, PublicKey}; +pub use protocol::NotificationsSink; mod metrics; mod out_events; @@ -146,7 +147,7 @@ where /// Returns a `NetworkWorker` that implements `Future` and must be regularly polled in order /// for the network processing to advance. From it, you can extract a `NetworkService` using /// `worker.service()`. The `NetworkService` can be shared through the codebase. - pub fn new(mut params: Params) -> Result { + pub fn new(mut params: Params) -> Result { // Private and public keys configuration. let local_identity = params.network_config.node_key.clone().into_keypair()?; let local_public = local_identity.public(); @@ -227,6 +228,7 @@ where From::from(¶ms.role), ¶ms.network_config, params.block_announce_config, + params.tx, )?; // List of multiaddresses that we know in the network. diff --git a/client/network/sync/src/engine.rs b/client/network/sync/src/engine.rs index b4c1a2ed0..e3d45a980 100644 --- a/client/network/sync/src/engine.rs +++ b/client/network/sync/src/engine.rs @@ -24,8 +24,8 @@ use crate::{ ChainSync, ClientError, SyncingService, }; -use codec::{Decode, DecodeAll, Encode}; -use futures::{FutureExt, Stream, StreamExt}; +use codec::{Decode, Encode}; +use futures::{FutureExt, StreamExt}; use futures_timer::Delay; use libp2p::PeerId; use lru::LruCache; @@ -39,9 +39,8 @@ use sc_network::{ config::{ NetworkConfiguration, NonDefaultSetConfig, ProtocolId, SyncMode as SyncOperationMode, }, - event::Event, utils::LruHashSet, - ProtocolName, + NotificationsSink, ProtocolName, }; use sc_network_common::{ role::Roles, @@ -63,7 +62,6 @@ use sp_runtime::traits::{Block as BlockT, Header, NumberFor, Zero}; use std::{ collections::{HashMap, HashSet}, num::NonZeroUsize, - pin::Pin, sync::{ atomic::{AtomicBool, AtomicUsize, Ordering}, Arc, @@ -79,8 +77,6 @@ const MAX_KNOWN_BLOCKS: usize = 1024; // ~32kb per peer + LruHashSet overhead mod rep { use sc_peerset::ReputationChange as Rep; - /// We received a message that failed to decode. - pub const BAD_MESSAGE: Rep = Rep::new(-(1 << 12), "Bad message"); /// Peer has different genesis. pub const GENESIS_MISMATCH: Rep = Rep::new_fatal("Genesis mismatch"); /// Peer send us a block announcement that failed at validation. @@ -162,6 +158,8 @@ pub struct Peer { pub info: ExtendedPeerInfo, /// Holds a set of blocks known to this peer. pub known_blocks: LruHashSet, + /// Notification sink. + sink: NotificationsSink, } pub struct SyncingEngine { @@ -184,6 +182,9 @@ pub struct SyncingEngine { /// Channel for receiving service commands service_rx: TracingUnboundedReceiver>, + /// Channel for receiving inbound connections from `Protocol`. + rx: sc_utils::mpsc::TracingUnboundedReceiver>, + /// Assigned roles. roles: Roles, @@ -254,6 +255,7 @@ where block_request_protocol_name: ProtocolName, state_request_protocol_name: ProtocolName, warp_sync_protocol_name: Option, + rx: sc_utils::mpsc::TracingUnboundedReceiver>, ) -> Result<(Self, SyncingService, NonDefaultSetConfig), ClientError> { let mode = match network_config.sync_mode { SyncOperationMode::Full => SyncMode::Full, @@ -347,6 +349,7 @@ where num_connected: num_connected.clone(), is_major_syncing: is_major_syncing.clone(), service_rx, + rx, genesis_hash, important_peers, default_peers_set_no_slot_connected_peers: HashSet::new(), @@ -554,11 +557,7 @@ where data: Some(data.clone()), }; - self.network_service.write_notification( - *who, - self.block_announce_protocol_name.clone(), - message.encode(), - ); + peer.sink.send_sync_notification(message.encode()); } } } @@ -575,17 +574,13 @@ where ) } - pub async fn run(mut self, mut stream: Pin + Send>>) { + pub async fn run(mut self) { loop { - futures::future::poll_fn(|cx| self.poll(cx, &mut stream)).await; + futures::future::poll_fn(|cx| self.poll(cx)).await; } } - pub fn poll( - &mut self, - cx: &mut std::task::Context, - event_stream: &mut Pin + Send>>, - ) -> Poll<()> { + pub fn poll(&mut self, cx: &mut std::task::Context) -> Poll<()> { self.num_connected.store(self.peers.len(), Ordering::Relaxed); self.is_major_syncing .store(self.chain_sync.status().state.is_major_syncing(), Ordering::Relaxed); @@ -595,84 +590,6 @@ where self.tick_timeout.reset(TICK_TIMEOUT); } - while let Poll::Ready(Some(event)) = event_stream.poll_next_unpin(cx) { - match event { - Event::NotificationStreamOpened { - remote, protocol, received_handshake, .. - } => { - if protocol != self.block_announce_protocol_name { - continue - } - - match as DecodeAll>::decode_all( - &mut &received_handshake[..], - ) { - Ok(handshake) => { - if self.on_sync_peer_connected(remote, handshake).is_err() { - log::debug!( - target: "sync", - "Failed to register peer {remote:?}: {received_handshake:?}", - ); - } - }, - Err(err) => { - log::debug!( - target: "sync", - "Couldn't decode handshake sent by {}: {:?}: {}", - remote, - received_handshake, - err, - ); - self.network_service.report_peer(remote, rep::BAD_MESSAGE); - }, - } - }, - Event::NotificationStreamClosed { remote, protocol } => { - if protocol != self.block_announce_protocol_name { - continue - } - - if self.on_sync_peer_disconnected(remote).is_err() { - log::trace!( - target: "sync", - "Disconnected peer which had earlier been refused by on_sync_peer_connected {}", - remote - ); - } - }, - Event::NotificationsReceived { remote, messages } => { - for (protocol, message) in messages { - if protocol != self.block_announce_protocol_name { - continue - } - - if self.peers.contains_key(&remote) { - if let Ok(announce) = BlockAnnounce::decode(&mut message.as_ref()) { - self.push_block_announce_validation(remote, announce); - - // Make sure that the newly added block announce validation future - // was polled once to be registered in the task. - if let Poll::Ready(res) = - self.chain_sync.poll_block_announce_validation(cx) - { - self.process_block_announce_validation_result(res) - } - } else { - log::warn!(target: "sub-libp2p", "Failed to decode block announce"); - } - } else { - log::trace!( - target: "sync", - "Received sync for peer earlier refused by sync layer: {}", - remote - ); - } - } - }, - _ => {}, - } - } - while let Poll::Ready(Some(event)) = self.service_rx.poll_next_unpin(cx) { match event { ToServiceCommand::SetSyncForkRequest(peers, hash, number) => { @@ -746,6 +663,70 @@ where } } + while let Poll::Ready(Some(event)) = self.rx.poll_next_unpin(cx) { + match event { + sc_network::SyncEvent::NotificationStreamOpened { + remote, + received_handshake, + sink, + tx, + } => match self.on_sync_peer_connected(remote, &received_handshake, sink) { + Ok(()) => { + let _ = tx.send(true); + }, + Err(()) => { + log::debug!( + target: "sync", + "Failed to register peer {remote:?}: {received_handshake:?}", + ); + let _ = tx.send(false); + }, + }, + sc_network::SyncEvent::NotificationStreamClosed { remote } => { + if self.on_sync_peer_disconnected(remote).is_err() { + log::trace!( + target: "sync", + "Disconnected peer which had earlier been refused by on_sync_peer_connected {}", + remote + ); + } + }, + sc_network::SyncEvent::NotificationsReceived { remote, messages } => { + for message in messages { + if self.peers.contains_key(&remote) { + if let Ok(announce) = BlockAnnounce::decode(&mut message.as_ref()) { + self.push_block_announce_validation(remote, announce); + + // Make sure that the newly added block announce validation future + // was polled once to be registered in the task. + if let Poll::Ready(res) = + self.chain_sync.poll_block_announce_validation(cx) + { + self.process_block_announce_validation_result(res) + } + } else { + log::warn!(target: "sub-libp2p", "Failed to decode block announce"); + } + } else { + log::trace!( + target: "sync", + "Received sync for peer earlier refused by sync layer: {}", + remote + ); + } + } + }, + sc_network::SyncEvent::NotificationSinkReplaced { remote, sink } => { + if let Some(peer) = self.peers.get_mut(&remote) { + peer.sink = sink; + } + }, + } + } + + // poll `ChainSync` last because of a block announcement was received through the + // event stream between `SyncingEngine` and `Protocol` and the validation finished + // right after it as queued, the resulting block request (if any) can be sent right away. while let Poll::Ready(result) = self.chain_sync.poll(cx) { self.process_block_announce_validation_result(result); } @@ -757,13 +738,13 @@ where /// /// Returns a result if the handshake of this peer was indeed accepted. pub fn on_sync_peer_disconnected(&mut self, peer: PeerId) -> Result<(), ()> { - if self.important_peers.contains(&peer) { - log::warn!(target: "sync", "Reserved peer {} disconnected", peer); - } else { - log::debug!(target: "sync", "{} disconnected", peer); - } - if self.peers.remove(&peer).is_some() { + if self.important_peers.contains(&peer) { + log::warn!(target: "sync", "Reserved peer {} disconnected", peer); + } else { + log::debug!(target: "sync", "{} disconnected", peer); + } + self.chain_sync.peer_disconnected(&peer); self.default_peers_set_no_slot_connected_peers.remove(&peer); self.event_streams @@ -782,7 +763,8 @@ where pub fn on_sync_peer_connected( &mut self, who: PeerId, - status: BlockAnnouncesHandshake, + status: &BlockAnnouncesHandshake, + sink: NotificationsSink, ) -> Result<(), ()> { log::trace!(target: "sync", "New peer {} {:?}", who, status); @@ -794,8 +776,6 @@ where if status.genesis_hash != self.genesis_hash { self.network_service.report_peer(who, rep::GENESIS_MISMATCH); - self.network_service - .disconnect_peer(who, self.block_announce_protocol_name.clone()); if self.important_peers.contains(&who) { log::error!( @@ -834,8 +814,6 @@ where this_peer_reserved_slot { log::debug!(target: "sync", "Too many full nodes, rejecting {}", who); - self.network_service - .disconnect_peer(who, self.block_announce_protocol_name.clone()); return Err(()) } @@ -844,8 +822,6 @@ where { // Make sure that not all slots are occupied by light clients. log::debug!(target: "sync", "Too many light nodes, rejecting {}", who); - self.network_service - .disconnect_peer(who, self.block_announce_protocol_name.clone()); return Err(()) } @@ -858,14 +834,13 @@ where known_blocks: LruHashSet::new( NonZeroUsize::new(MAX_KNOWN_BLOCKS).expect("Constant is nonzero"), ), + sink, }; let req = if peer.info.roles.is_full() { match self.chain_sync.new_peer(who, peer.info.best_hash, peer.info.best_number) { Ok(req) => req, Err(BadPeer(id, repu)) => { - self.network_service - .disconnect_peer(id, self.block_announce_protocol_name.clone()); self.network_service.report_peer(id, repu); return Err(()) }, diff --git a/client/network/test/Cargo.toml b/client/network/test/Cargo.toml index 8368fa278..9763feed5 100644 --- a/client/network/test/Cargo.toml +++ b/client/network/test/Cargo.toml @@ -26,6 +26,7 @@ sc-client-api = { version = "4.0.0-dev", path = "../../api" } sc-consensus = { version = "0.10.0-dev", path = "../../consensus/common" } sc-network = { version = "0.10.0-dev", path = "../" } sc-network-common = { version = "0.10.0-dev", path = "../common" } +sc-utils = { version = "4.0.0-dev", path = "../../utils" } sc-network-light = { version = "0.10.0-dev", path = "../light" } sc-network-sync = { version = "0.10.0-dev", path = "../sync" } sc-service = { version = "0.10.0-dev", default-features = false, features = ["test-helpers"], path = "../../service" } diff --git a/client/network/test/src/lib.rs b/client/network/test/src/lib.rs index 6a9930403..f85d6ed63 100644 --- a/client/network/test/src/lib.rs +++ b/client/network/test/src/lib.rs @@ -55,8 +55,8 @@ use sc_network::{ }, request_responses::ProtocolConfig as RequestResponseConfig, types::ProtocolName, - Multiaddr, NetworkBlock, NetworkEventStream, NetworkService, NetworkStateInfo, - NetworkSyncForkRequest, NetworkWorker, + Multiaddr, NetworkBlock, NetworkService, NetworkStateInfo, NetworkSyncForkRequest, + NetworkWorker, }; use sc_network_common::{ role::Roles, @@ -896,6 +896,7 @@ where let (chain_sync_network_provider, chain_sync_network_handle) = NetworkServiceProvider::new(); + let (tx, rx) = sc_utils::mpsc::tracing_unbounded("mpsc_syncing_engine_protocol", 100_000); let (engine, sync_service, block_announce_config) = sc_network_sync::engine::SyncingEngine::new( Roles::from(if config.is_authority { &Role::Authority } else { &Role::Full }), @@ -911,6 +912,7 @@ where block_request_protocol_config.name.clone(), state_request_protocol_config.name.clone(), Some(warp_protocol_config.name.clone()), + rx, ) .unwrap(); let sync_service_import_queue = Box::new(sync_service.clone()); @@ -918,7 +920,7 @@ where let genesis_hash = client.hash(Zero::zero()).ok().flatten().expect("Genesis block exists; qed"); - let network = NetworkWorker::new::(sc_network::config::Params { + let network = NetworkWorker::new(sc_network::config::Params { role: if config.is_authority { Role::Authority } else { Role::Full }, executor: Box::new(|f| { tokio::spawn(f); @@ -929,6 +931,7 @@ where fork_id, metrics_registry: None, block_announce_config, + tx, request_response_protocol_configs: [ block_request_protocol_config, state_request_protocol_config, @@ -950,9 +953,8 @@ where import_queue.run(sync_service_import_queue).await; }); - let service = network.service().clone(); tokio::spawn(async move { - engine.run(service.event_stream("syncing")).await; + engine.run().await; }); self.mut_peers(move |peers| { diff --git a/client/network/test/src/service.rs b/client/network/test/src/service.rs index 67915c386..5871860a7 100644 --- a/client/network/test/src/service.rs +++ b/client/network/test/src/service.rs @@ -177,6 +177,7 @@ impl TestNetworkBuilder { let (chain_sync_network_provider, chain_sync_network_handle) = self.chain_sync_network.unwrap_or(NetworkServiceProvider::new()); + let (tx, rx) = sc_utils::mpsc::tracing_unbounded("mpsc_syncing_engine_protocol", 100_000); let (engine, chain_sync_service, block_announce_config) = SyncingEngine::new( Roles::from(&config::Role::Full), @@ -192,6 +193,7 @@ impl TestNetworkBuilder { block_request_protocol_config.name.clone(), state_request_protocol_config.name.clone(), None, + rx, ) .unwrap(); let mut link = self.link.unwrap_or(Box::new(chain_sync_service.clone())); @@ -217,6 +219,7 @@ impl TestNetworkBuilder { light_client_request_protocol_config, ] .to_vec(), + tx, }) .unwrap(); @@ -234,8 +237,7 @@ impl TestNetworkBuilder { tokio::time::sleep(std::time::Duration::from_millis(250)).await; } }); - let stream = worker.service().event_stream("syncing"); - tokio::spawn(engine.run(stream)); + tokio::spawn(engine.run()); TestNetwork::new(worker) } diff --git a/client/network/test/src/sync.rs b/client/network/test/src/sync.rs index d87b03fb3..af46d15a2 100644 --- a/client/network/test/src/sync.rs +++ b/client/network/test/src/sync.rs @@ -414,7 +414,7 @@ async fn can_sync_small_non_best_forks() { // poll until the two nodes connect, otherwise announcing the block will not work futures::future::poll_fn::<(), _>(|cx| { net.poll(cx); - if net.peer(0).num_peers() == 0 { + if net.peer(0).num_peers() == 0 || net.peer(1).num_peers() == 0 { Poll::Pending } else { Poll::Ready(()) diff --git a/client/service/src/builder.rs b/client/service/src/builder.rs index 7cbbf2a4d..b04228e6b 100644 --- a/client/service/src/builder.rs +++ b/client/service/src/builder.rs @@ -38,9 +38,7 @@ use sc_client_db::{Backend, DatabaseSettings}; use sc_consensus::import_queue::ImportQueue; use sc_executor::RuntimeVersionOf; use sc_keystore::LocalKeystore; -use sc_network::{ - config::SyncMode, NetworkEventStream, NetworkService, NetworkStateInfo, NetworkStatusProvider, -}; +use sc_network::{config::SyncMode, NetworkService, NetworkStateInfo, NetworkStatusProvider}; use sc_network_bitswap::BitswapRequestHandler; use sc_network_common::{role::Roles, sync::warp::WarpSyncParams}; use sc_network_light::light_client_requests::handler::LightClientRequestHandler; @@ -825,6 +823,7 @@ where protocol_config }; + let (tx, rx) = sc_utils::mpsc::tracing_unbounded("mpsc_syncing_engine_protocol", 100_000); let (chain_sync_network_provider, chain_sync_network_handle) = NetworkServiceProvider::new(); let (engine, sync_service, block_announce_config) = SyncingEngine::new( Roles::from(&config.role), @@ -840,6 +839,7 @@ where block_request_protocol_config.name.clone(), state_request_protocol_config.name.clone(), warp_sync_protocol_config.as_ref().map(|config| config.name.clone()), + rx, )?; let sync_service_import_queue = sync_service.clone(); let sync_service = Arc::new(sync_service); @@ -865,6 +865,7 @@ where fork_id: config.chain_spec.fork_id().map(ToOwned::to_owned), metrics_registry: config.prometheus_config.as_ref().map(|config| config.registry.clone()), block_announce_config, + tx, request_response_protocol_configs: request_response_protocol_configs .into_iter() .chain([ @@ -904,15 +905,13 @@ where )?; spawn_handle.spawn("network-transactions-handler", Some("networking"), tx_handler.run()); - spawn_handle.spawn( + spawn_handle.spawn_blocking( "chain-sync-network-service-provider", Some("networking"), chain_sync_network_provider.run(network.clone()), ); spawn_handle.spawn("import-queue", None, import_queue.run(Box::new(sync_service_import_queue))); - - let event_stream = network.event_stream("syncing"); - spawn_handle.spawn("syncing", None, engine.run(event_stream)); + spawn_handle.spawn_blocking("syncing", None, engine.run()); let (system_rpc_tx, system_rpc_rx) = tracing_unbounded("mpsc_system_rpc", 10_000); spawn_handle.spawn( From 9b8e6e780352051029b0fdc56fcac5074bd74552 Mon Sep 17 00:00:00 2001 From: Roman Useinov Date: Thu, 30 Mar 2023 14:53:47 +0200 Subject: [PATCH 317/558] [Enhancement] Throw an error when there are too many pallets (#13763) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * [Enhancement] Throw an error when there are too many pallets * fix ui test * fix PR comments * Update frame/support/procedural/src/construct_runtime/mod.rs Co-authored-by: Bastian Köcher * Update frame/support/procedural/src/construct_runtime/mod.rs Co-authored-by: Bastian Köcher * ".git/.scripts/commands/fmt/fmt.sh" --------- Co-authored-by: Bastian Köcher Co-authored-by: command-bot <> --- .../procedural/src/construct_runtime/mod.rs | 40 ++++- .../number_of_pallets_exceeds_tuple_size.rs | 169 ++++++++++++++++++ ...umber_of_pallets_exceeds_tuple_size.stderr | 61 +++++++ 3 files changed, 267 insertions(+), 3 deletions(-) create mode 100644 frame/support/test/tests/construct_runtime_ui/number_of_pallets_exceeds_tuple_size.rs create mode 100644 frame/support/test/tests/construct_runtime_ui/number_of_pallets_exceeds_tuple_size.stderr diff --git a/frame/support/procedural/src/construct_runtime/mod.rs b/frame/support/procedural/src/construct_runtime/mod.rs index 37f23efed..9250186dc 100644 --- a/frame/support/procedural/src/construct_runtime/mod.rs +++ b/frame/support/procedural/src/construct_runtime/mod.rs @@ -157,7 +157,7 @@ use proc_macro::TokenStream; use proc_macro2::TokenStream as TokenStream2; use quote::quote; use std::{collections::HashSet, str::FromStr}; -use syn::{Ident, Result}; +use syn::{spanned::Spanned, Ident, Result}; /// The fixed name of the system pallet. const SYSTEM_PALLET_NAME: &str = "System"; @@ -170,9 +170,12 @@ pub fn construct_runtime(input: TokenStream) -> TokenStream { let res = match definition { RuntimeDeclaration::Implicit(implicit_def) => - construct_runtime_intermediary_expansion(input_copy.into(), implicit_def), + check_pallet_number(input_copy.clone().into(), implicit_def.pallets.len()).and_then( + |_| construct_runtime_intermediary_expansion(input_copy.into(), implicit_def), + ), RuntimeDeclaration::Explicit(explicit_decl) => - construct_runtime_final_expansion(explicit_decl), + check_pallet_number(input_copy.into(), explicit_decl.pallets.len()) + .and_then(|_| construct_runtime_final_expansion(explicit_decl)), }; res.unwrap_or_else(|e| e.to_compile_error()).into() @@ -616,3 +619,34 @@ fn decl_static_assertions( #(#error_encoded_size_check)* } } + +fn check_pallet_number(input: TokenStream2, pallet_num: usize) -> Result<()> { + let max_pallet_num = { + if cfg!(feature = "tuples-96") { + 96 + } else if cfg!(feature = "tuples-128") { + 128 + } else { + 64 + } + }; + + if pallet_num > max_pallet_num { + let no_feature = max_pallet_num == 128; + return Err(syn::Error::new( + input.span(), + format!( + "{} To increase this limit, enable the tuples-{} feature of [frame_support]. {}", + "The number of pallets exceeds the maximum number of tuple elements.", + max_pallet_num + 32, + if no_feature { + "If the feature does not exist - it needs to be implemented." + } else { + "" + }, + ), + )) + } + + Ok(()) +} diff --git a/frame/support/test/tests/construct_runtime_ui/number_of_pallets_exceeds_tuple_size.rs b/frame/support/test/tests/construct_runtime_ui/number_of_pallets_exceeds_tuple_size.rs new file mode 100644 index 000000000..5dfc67c83 --- /dev/null +++ b/frame/support/test/tests/construct_runtime_ui/number_of_pallets_exceeds_tuple_size.rs @@ -0,0 +1,169 @@ +use frame_support::construct_runtime; +use sp_core::sr25519; +use sp_runtime::{generic, traits::BlakeTwo256}; + +#[frame_support::pallet] +mod pallet { + #[pallet::config] + pub trait Config: frame_system::Config {} + + #[pallet::pallet] + pub struct Pallet(core::marker::PhantomData); +} + +pub type Signature = sr25519::Signature; +pub type BlockNumber = u32; +pub type Header = generic::Header; +pub type Block = generic::Block; +pub type UncheckedExtrinsic = generic::UncheckedExtrinsic; + +impl pallet::Config for Runtime {} + +impl frame_system::Config for Runtime { + type BaseCallFilter = frame_support::traits::Everything; + type RuntimeOrigin = RuntimeOrigin; + type Index = u64; + type BlockNumber = u32; + type RuntimeCall = RuntimeCall; + type Hash = sp_runtime::testing::H256; + type Hashing = sp_runtime::traits::BlakeTwo256; + type AccountId = u64; + type Lookup = sp_runtime::traits::IdentityLookup; + type Header = Header; + type RuntimeEvent = RuntimeEvent; + type BlockHashCount = frame_support::traits::ConstU32<250>; + type BlockWeights = (); + type BlockLength = (); + type DbWeight = (); + type Version = (); + type PalletInfo = PalletInfo; + type AccountData = (); + type OnNewAccount = (); + type OnKilledAccount = (); + type SystemWeightInfo = (); + type SS58Prefix = (); + type OnSetCode = (); + type MaxConsumers = frame_support::traits::ConstU32<16>; +} + +construct_runtime! { + pub struct Runtime where + Block = Block, + NodeBlock = Block, + UncheckedExtrinsic = UncheckedExtrinsic + { + System: frame_system::{Pallet, Call, Storage, Config, Event}, + Pallet1: pallet::{Pallet}, + Pallet2: pallet::{Pallet}, + Pallet3: pallet::{Pallet}, + Pallet4: pallet::{Pallet}, + Pallet5: pallet::{Pallet}, + Pallet6: pallet::{Pallet}, + Pallet7: pallet::{Pallet}, + Pallet8: pallet::{Pallet}, + Pallet9: pallet::{Pallet}, + Pallet10: pallet::{Pallet}, + Pallet11: pallet::{Pallet}, + Pallet12: pallet::{Pallet}, + Pallet13: pallet::{Pallet}, + Pallet14: pallet::{Pallet}, + Pallet15: pallet::{Pallet}, + Pallet16: pallet::{Pallet}, + Pallet17: pallet::{Pallet}, + Pallet18: pallet::{Pallet}, + Pallet19: pallet::{Pallet}, + Pallet20: pallet::{Pallet}, + Pallet21: pallet::{Pallet}, + Pallet22: pallet::{Pallet}, + Pallet23: pallet::{Pallet}, + Pallet24: pallet::{Pallet}, + Pallet25: pallet::{Pallet}, + Pallet26: pallet::{Pallet}, + Pallet27: pallet::{Pallet}, + Pallet28: pallet::{Pallet}, + Pallet29: pallet::{Pallet}, + Pallet30: pallet::{Pallet}, + Pallet31: pallet::{Pallet}, + Pallet32: pallet::{Pallet}, + Pallet33: pallet::{Pallet}, + Pallet34: pallet::{Pallet}, + Pallet35: pallet::{Pallet}, + Pallet36: pallet::{Pallet}, + Pallet37: pallet::{Pallet}, + Pallet38: pallet::{Pallet}, + Pallet39: pallet::{Pallet}, + Pallet40: pallet::{Pallet}, + Pallet41: pallet::{Pallet}, + Pallet42: pallet::{Pallet}, + Pallet43: pallet::{Pallet}, + Pallet44: pallet::{Pallet}, + Pallet45: pallet::{Pallet}, + Pallet46: pallet::{Pallet}, + Pallet47: pallet::{Pallet}, + Pallet48: pallet::{Pallet}, + Pallet49: pallet::{Pallet}, + Pallet50: pallet::{Pallet}, + Pallet51: pallet::{Pallet}, + Pallet52: pallet::{Pallet}, + Pallet53: pallet::{Pallet}, + Pallet54: pallet::{Pallet}, + Pallet55: pallet::{Pallet}, + Pallet56: pallet::{Pallet}, + Pallet57: pallet::{Pallet}, + Pallet58: pallet::{Pallet}, + Pallet59: pallet::{Pallet}, + Pallet60: pallet::{Pallet}, + Pallet61: pallet::{Pallet}, + Pallet62: pallet::{Pallet}, + Pallet63: pallet::{Pallet}, + Pallet64: pallet::{Pallet}, + Pallet65: pallet::{Pallet}, + Pallet66: pallet::{Pallet}, + Pallet67: pallet::{Pallet}, + Pallet68: pallet::{Pallet}, + Pallet69: pallet::{Pallet}, + Pallet70: pallet::{Pallet}, + Pallet71: pallet::{Pallet}, + Pallet72: pallet::{Pallet}, + Pallet73: pallet::{Pallet}, + Pallet74: pallet::{Pallet}, + Pallet75: pallet::{Pallet}, + Pallet76: pallet::{Pallet}, + Pallet77: pallet::{Pallet}, + Pallet78: pallet::{Pallet}, + Pallet79: pallet::{Pallet}, + Pallet80: pallet::{Pallet}, + Pallet81: pallet::{Pallet}, + Pallet82: pallet::{Pallet}, + Pallet83: pallet::{Pallet}, + Pallet84: pallet::{Pallet}, + Pallet85: pallet::{Pallet}, + Pallet86: pallet::{Pallet}, + Pallet87: pallet::{Pallet}, + Pallet88: pallet::{Pallet}, + Pallet89: pallet::{Pallet}, + Pallet90: pallet::{Pallet}, + Pallet91: pallet::{Pallet}, + Pallet92: pallet::{Pallet}, + Pallet93: pallet::{Pallet}, + Pallet94: pallet::{Pallet}, + Pallet95: pallet::{Pallet}, + Pallet96: pallet::{Pallet}, + Pallet97: pallet::{Pallet}, + Pallet98: pallet::{Pallet}, + Pallet99: pallet::{Pallet}, + Pallet100: pallet::{Pallet}, + Pallet101: pallet::{Pallet}, + Pallet102: pallet::{Pallet}, + Pallet103: pallet::{Pallet}, + Pallet104: pallet::{Pallet}, + Pallet105: pallet::{Pallet}, + Pallet106: pallet::{Pallet}, + Pallet107: pallet::{Pallet}, + Pallet108: pallet::{Pallet}, + Pallet109: pallet::{Pallet}, + Pallet110: pallet::{Pallet}, + } +} + +fn main() {} diff --git a/frame/support/test/tests/construct_runtime_ui/number_of_pallets_exceeds_tuple_size.stderr b/frame/support/test/tests/construct_runtime_ui/number_of_pallets_exceeds_tuple_size.stderr new file mode 100644 index 000000000..dbd81ef36 --- /dev/null +++ b/frame/support/test/tests/construct_runtime_ui/number_of_pallets_exceeds_tuple_size.stderr @@ -0,0 +1,61 @@ +error: The number of pallets exceeds the maximum number of tuple elements. To increase this limit, enable the tuples-96 feature of [frame_support]. + --> tests/construct_runtime_ui/number_of_pallets_exceeds_tuple_size.rs:50:2 + | +50 | pub struct Runtime where + | ^^^ + +error[E0412]: cannot find type `RuntimeCall` in this scope + --> tests/construct_runtime_ui/number_of_pallets_exceeds_tuple_size.rs:18:64 + | +18 | pub type UncheckedExtrinsic = generic::UncheckedExtrinsic; + | ^^^^^^^^^^^ not found in this scope + | +help: you might be missing a type parameter + | +18 | pub type UncheckedExtrinsic = generic::UncheckedExtrinsic; + | +++++++++++++ + +error[E0412]: cannot find type `Runtime` in this scope + --> tests/construct_runtime_ui/number_of_pallets_exceeds_tuple_size.rs:20:25 + | +20 | impl pallet::Config for Runtime {} + | ^^^^^^^ not found in this scope + +error[E0412]: cannot find type `Runtime` in this scope + --> tests/construct_runtime_ui/number_of_pallets_exceeds_tuple_size.rs:22:31 + | +22 | impl frame_system::Config for Runtime { + | ^^^^^^^ not found in this scope + +error[E0412]: cannot find type `RuntimeOrigin` in this scope + --> tests/construct_runtime_ui/number_of_pallets_exceeds_tuple_size.rs:24:23 + | +24 | type RuntimeOrigin = RuntimeOrigin; + | ^^^^^^^^^^^^^ help: you might have meant to use the associated type: `Self::RuntimeOrigin` + +error[E0412]: cannot find type `RuntimeCall` in this scope + --> tests/construct_runtime_ui/number_of_pallets_exceeds_tuple_size.rs:27:21 + | +27 | type RuntimeCall = RuntimeCall; + | ^^^^^^^^^^^ help: you might have meant to use the associated type: `Self::RuntimeCall` + +error[E0412]: cannot find type `RuntimeEvent` in this scope + --> tests/construct_runtime_ui/number_of_pallets_exceeds_tuple_size.rs:33:22 + | +33 | type RuntimeEvent = RuntimeEvent; + | ^^^^^^^^^^^^ help: you might have meant to use the associated type: `Self::RuntimeEvent` + +error[E0412]: cannot find type `PalletInfo` in this scope + --> tests/construct_runtime_ui/number_of_pallets_exceeds_tuple_size.rs:39:20 + | +39 | type PalletInfo = PalletInfo; + | ^^^^^^^^^^ + | +help: you might have meant to use the associated type + | +39 | type PalletInfo = Self::PalletInfo; + | ~~~~~~~~~~~~~~~~ +help: consider importing this trait + | +1 | use frame_support::traits::PalletInfo; + | From a0468b335a4bb57ad83d6b0a19a8088ec1319eb1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alexander=20Thei=C3=9Fen?= Date: Thu, 30 Mar 2023 16:20:12 +0200 Subject: [PATCH 318/558] Build wasm for mvp cpu (#13758) --- utils/wasm-builder/src/wasm_project.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/utils/wasm-builder/src/wasm_project.rs b/utils/wasm-builder/src/wasm_project.rs index a3038c4e9..d25fb4acd 100644 --- a/utils/wasm-builder/src/wasm_project.rs +++ b/utils/wasm-builder/src/wasm_project.rs @@ -632,7 +632,7 @@ fn build_project( let mut build_cmd = cargo_cmd.command(); let rustflags = format!( - "-C link-arg=--export-table {} {}", + "-C target-cpu=mvp -C link-arg=--export-table {} {}", default_rustflags, env::var(crate::WASM_BUILD_RUSTFLAGS_ENV).unwrap_or_default(), ); From 025325d0c2c478a6f27789408ecaabaf8b9e8680 Mon Sep 17 00:00:00 2001 From: Adrian Catangiu Date: Thu, 30 Mar 2023 17:23:36 +0300 Subject: [PATCH 319/558] BEEFY: gossip finality proofs (#13727) * sc-consensus-beefy: add justifications to gossip protocol * sc-consensus-beefy: voter gossips finality proofs * sc-consensus-beefy: add finality proof gossip test * sc-consensus-beefy: always gossip finality proof Gossip finality proof in _both_ cases of reaching finality threshold through votes: 1. threshold reached through self vote, 2. threshold reached through incoming vote. * address comments --- .../beefy/src/communication/gossip.rs | 516 +++++++++++++----- .../consensus/beefy/src/communication/mod.rs | 6 +- client/consensus/beefy/src/justification.rs | 16 +- client/consensus/beefy/src/lib.rs | 2 +- client/consensus/beefy/src/round.rs | 38 +- client/consensus/beefy/src/tests.rs | 176 +++++- client/consensus/beefy/src/worker.rs | 94 +++- 7 files changed, 642 insertions(+), 206 deletions(-) diff --git a/client/consensus/beefy/src/communication/gossip.rs b/client/consensus/beefy/src/communication/gossip.rs index 2b5e772c0..376172fc2 100644 --- a/client/consensus/beefy/src/communication/gossip.rs +++ b/client/consensus/beefy/src/communication/gossip.rs @@ -28,10 +28,17 @@ use log::{debug, trace}; use parking_lot::{Mutex, RwLock}; use wasm_timer::Instant; -use crate::{communication::peers::KnownPeers, keystore::BeefyKeystore, LOG_TARGET}; +use crate::{ + communication::peers::KnownPeers, + justification::{ + proof_block_num_and_set_id, verify_with_validator_set, BeefyVersionedFinalityProof, + }, + keystore::BeefyKeystore, + LOG_TARGET, +}; use sp_consensus_beefy::{ - crypto::{Public, Signature}, - ValidatorSetId, VoteMessage, + crypto::{AuthorityId, Signature}, + ValidatorSet, ValidatorSetId, VoteMessage, }; // Timeout for rebroadcasting messages. @@ -40,59 +47,128 @@ const REBROADCAST_AFTER: Duration = Duration::from_secs(60); #[cfg(test)] const REBROADCAST_AFTER: Duration = Duration::from_secs(5); -/// Gossip engine messages topic -pub(crate) fn topic() -> B::Hash +/// BEEFY gossip message type that gets encoded and sent on the network. +#[derive(Debug, Encode, Decode)] +pub(crate) enum GossipMessage { + /// BEEFY message with commitment and single signature. + Vote(VoteMessage, AuthorityId, Signature>), + /// BEEFY justification with commitment and signatures. + FinalityProof(BeefyVersionedFinalityProof), +} + +impl GossipMessage { + /// Return inner vote if this message is a Vote. + pub fn unwrap_vote(self) -> Option, AuthorityId, Signature>> { + match self { + GossipMessage::Vote(vote) => Some(vote), + GossipMessage::FinalityProof(_) => None, + } + } + + /// Return inner finality proof if this message is a FinalityProof. + pub fn unwrap_finality_proof(self) -> Option> { + match self { + GossipMessage::Vote(_) => None, + GossipMessage::FinalityProof(proof) => Some(proof), + } + } +} + +/// Gossip engine votes messages topic +pub(crate) fn votes_topic() -> B::Hash where B: Block, { - <::Hashing as Hash>::hash(b"beefy") + <::Hashing as Hash>::hash(b"beefy-votes") } -#[derive(Debug)] -pub(crate) struct GossipVoteFilter { - pub start: NumberFor, - pub end: NumberFor, - pub validator_set_id: ValidatorSetId, +/// Gossip engine justifications messages topic +pub(crate) fn proofs_topic() -> B::Hash +where + B: Block, +{ + <::Hashing as Hash>::hash(b"beefy-justifications") } /// A type that represents hash of the message. pub type MessageHash = [u8; 8]; -struct VotesFilter { - filter: Option>, - live: BTreeMap, fnv::FnvHashSet>, +#[derive(Clone, Debug)] +pub(crate) struct GossipFilterCfg<'a, B: Block> { + pub start: NumberFor, + pub end: NumberFor, + pub validator_set: &'a ValidatorSet, +} + +#[derive(Clone, Debug)] +struct FilterInner { + pub start: NumberFor, + pub end: NumberFor, + pub validator_set: ValidatorSet, +} + +struct Filter { + inner: Option>, + live_votes: BTreeMap, fnv::FnvHashSet>, } -impl VotesFilter { +impl Filter { pub fn new() -> Self { - Self { filter: None, live: BTreeMap::new() } + Self { inner: None, live_votes: BTreeMap::new() } } /// Update filter to new `start` and `set_id`. - fn update(&mut self, filter: GossipVoteFilter) { - self.live.retain(|&round, _| round >= filter.start && round <= filter.end); - self.filter = Some(filter); + fn update(&mut self, cfg: GossipFilterCfg) { + self.live_votes.retain(|&round, _| round >= cfg.start && round <= cfg.end); + // only clone+overwrite big validator_set if set_id changed + match self.inner.as_mut() { + Some(f) if f.validator_set.id() == cfg.validator_set.id() => { + f.start = cfg.start; + f.end = cfg.end; + }, + _ => + self.inner = Some(FilterInner { + start: cfg.start, + end: cfg.end, + validator_set: cfg.validator_set.clone(), + }), + } + } + + /// Return true if `max(session_start, best_beefy) <= round <= best_grandpa`, + /// and vote `set_id` matches session set id. + /// + /// Latest concluded round is still considered alive to allow proper gossiping for it. + fn is_vote_accepted(&self, round: NumberFor, set_id: ValidatorSetId) -> bool { + self.inner + .as_ref() + .map(|f| set_id == f.validator_set.id() && round >= f.start && round <= f.end) + .unwrap_or(false) } /// Return true if `round` is >= than `max(session_start, best_beefy)`, - /// and vote set id matches session set id. + /// and proof `set_id` matches session set id. /// /// Latest concluded round is still considered alive to allow proper gossiping for it. - fn is_live(&self, round: NumberFor, set_id: ValidatorSetId) -> bool { - self.filter + fn is_finality_proof_accepted(&self, round: NumberFor, set_id: ValidatorSetId) -> bool { + self.inner .as_ref() - .map(|f| set_id == f.validator_set_id && round >= f.start && round <= f.end) + .map(|f| set_id == f.validator_set.id() && round >= f.start) .unwrap_or(false) } /// Add new _known_ `hash` to the round's known votes. - fn add_known(&mut self, round: NumberFor, hash: MessageHash) { - self.live.entry(round).or_default().insert(hash); + fn add_known_vote(&mut self, round: NumberFor, hash: MessageHash) { + self.live_votes.entry(round).or_default().insert(hash); } /// Check if `hash` is already part of round's known votes. - fn is_known(&self, round: NumberFor, hash: &MessageHash) -> bool { - self.live.get(&round).map(|known| known.contains(hash)).unwrap_or(false) + fn is_known_vote(&self, round: NumberFor, hash: &MessageHash) -> bool { + self.live_votes.get(&round).map(|known| known.contains(hash)).unwrap_or(false) + } + + fn validator_set(&self) -> Option<&ValidatorSet> { + self.inner.as_ref().map(|f| &f.validator_set) } } @@ -108,8 +184,9 @@ pub(crate) struct GossipValidator where B: Block, { - topic: B::Hash, - votes_filter: RwLock>, + votes_topic: B::Hash, + justifs_topic: B::Hash, + gossip_filter: RwLock>, next_rebroadcast: Mutex, known_peers: Arc>>, } @@ -120,8 +197,9 @@ where { pub fn new(known_peers: Arc>>) -> GossipValidator { GossipValidator { - topic: topic::(), - votes_filter: RwLock::new(VotesFilter::new()), + votes_topic: votes_topic::(), + justifs_topic: proofs_topic::(), + gossip_filter: RwLock::new(Filter::new()), next_rebroadcast: Mutex::new(Instant::now() + REBROADCAST_AFTER), known_peers, } @@ -130,9 +208,79 @@ where /// Update gossip validator filter. /// /// Only votes for `set_id` and rounds `start <= round <= end` will be accepted. - pub(crate) fn update_filter(&self, filter: GossipVoteFilter) { + pub(crate) fn update_filter(&self, filter: GossipFilterCfg) { debug!(target: LOG_TARGET, "🥩 New gossip filter {:?}", filter); - self.votes_filter.write().update(filter); + self.gossip_filter.write().update(filter); + } + + fn validate_vote( + &self, + vote: VoteMessage, AuthorityId, Signature>, + sender: &PeerId, + data: &[u8], + ) -> ValidationResult { + let msg_hash = twox_64(data); + let round = vote.commitment.block_number; + let set_id = vote.commitment.validator_set_id; + self.known_peers.lock().note_vote_for(*sender, round); + + // Verify general usefulness of the message. + // We are going to discard old votes right away (without verification) + // Also we keep track of already received votes to avoid verifying duplicates. + { + let filter = self.gossip_filter.read(); + + if !filter.is_vote_accepted(round, set_id) { + return ValidationResult::Discard + } + + if filter.is_known_vote(round, &msg_hash) { + return ValidationResult::ProcessAndKeep(self.votes_topic) + } + } + + if BeefyKeystore::verify(&vote.id, &vote.signature, &vote.commitment.encode()) { + self.gossip_filter.write().add_known_vote(round, msg_hash); + ValidationResult::ProcessAndKeep(self.votes_topic) + } else { + // TODO: report peer + debug!( + target: LOG_TARGET, + "🥩 Bad signature on message: {:?}, from: {:?}", vote, sender + ); + ValidationResult::Discard + } + } + + fn validate_finality_proof( + &self, + proof: BeefyVersionedFinalityProof, + sender: &PeerId, + ) -> ValidationResult { + let (round, set_id) = proof_block_num_and_set_id::(&proof); + self.known_peers.lock().note_vote_for(*sender, round); + + let guard = self.gossip_filter.read(); + // Verify general usefulness of the justifications. + if !guard.is_finality_proof_accepted(round, set_id) { + return ValidationResult::Discard + } + // Verify justification signatures. + guard + .validator_set() + .map(|validator_set| { + if let Ok(()) = verify_with_validator_set::(round, validator_set, &proof) { + ValidationResult::ProcessAndKeep(self.justifs_topic) + } else { + // TODO: report peer + debug!( + target: LOG_TARGET, + "🥩 Bad signatures on message: {:?}, from: {:?}", proof, sender + ); + ValidationResult::Discard + } + }) + .unwrap_or(ValidationResult::Discard) } } @@ -150,57 +298,38 @@ where sender: &PeerId, mut data: &[u8], ) -> ValidationResult { - if let Ok(msg) = VoteMessage::, Public, Signature>::decode(&mut data) { - let msg_hash = twox_64(data); - let round = msg.commitment.block_number; - let set_id = msg.commitment.validator_set_id; - self.known_peers.lock().note_vote_for(*sender, round); - - // Verify general usefulness of the message. - // We are going to discard old votes right away (without verification) - // Also we keep track of already received votes to avoid verifying duplicates. - { - let filter = self.votes_filter.read(); - - if !filter.is_live(round, set_id) { - return ValidationResult::Discard - } - - if filter.is_known(round, &msg_hash) { - return ValidationResult::ProcessAndKeep(self.topic) - } - } - - if BeefyKeystore::verify(&msg.id, &msg.signature, &msg.commitment.encode()) { - self.votes_filter.write().add_known(round, msg_hash); - return ValidationResult::ProcessAndKeep(self.topic) - } else { - // TODO: report peer - debug!( - target: LOG_TARGET, - "🥩 Bad signature on message: {:?}, from: {:?}", msg, sender - ); - } + match GossipMessage::::decode(&mut data) { + Ok(GossipMessage::Vote(msg)) => self.validate_vote(msg, sender, data), + Ok(GossipMessage::FinalityProof(proof)) => self.validate_finality_proof(proof, sender), + Err(e) => { + debug!(target: LOG_TARGET, "Error decoding message: {}", e); + ValidationResult::Discard + }, } - - ValidationResult::Discard } fn message_expired<'a>(&'a self) -> Box bool + 'a> { - let filter = self.votes_filter.read(); - Box::new(move |_topic, mut data| { - let msg = match VoteMessage::, Public, Signature>::decode(&mut data) { - Ok(vote) => vote, - Err(_) => return true, - }; - - let round = msg.commitment.block_number; - let set_id = msg.commitment.validator_set_id; - let expired = !filter.is_live(round, set_id); - - trace!(target: LOG_TARGET, "🥩 Message for round #{} expired: {}", round, expired); - - expired + let filter = self.gossip_filter.read(); + Box::new(move |_topic, mut data| match GossipMessage::::decode(&mut data) { + Ok(GossipMessage::Vote(msg)) => { + let round = msg.commitment.block_number; + let set_id = msg.commitment.validator_set_id; + let expired = !filter.is_vote_accepted(round, set_id); + trace!(target: LOG_TARGET, "🥩 Vote for round #{} expired: {}", round, expired); + expired + }, + Ok(GossipMessage::FinalityProof(proof)) => { + let (round, set_id) = proof_block_num_and_set_id::(&proof); + let expired = !filter.is_finality_proof_accepted(round, set_id); + trace!( + target: LOG_TARGET, + "🥩 Finality proof for round #{} expired: {}", + round, + expired + ); + expired + }, + Err(_) => true, }) } @@ -219,68 +348,80 @@ where } }; - let filter = self.votes_filter.read(); + let filter = self.gossip_filter.read(); Box::new(move |_who, intent, _topic, mut data| { if let MessageIntent::PeriodicRebroadcast = intent { return do_rebroadcast } - let msg = match VoteMessage::, Public, Signature>::decode(&mut data) { - Ok(vote) => vote, - Err(_) => return false, - }; - - let round = msg.commitment.block_number; - let set_id = msg.commitment.validator_set_id; - let allowed = filter.is_live(round, set_id); - - trace!(target: LOG_TARGET, "🥩 Message for round #{} allowed: {}", round, allowed); - - allowed + match GossipMessage::::decode(&mut data) { + Ok(GossipMessage::Vote(msg)) => { + let round = msg.commitment.block_number; + let set_id = msg.commitment.validator_set_id; + let allowed = filter.is_vote_accepted(round, set_id); + trace!(target: LOG_TARGET, "🥩 Vote for round #{} allowed: {}", round, allowed); + allowed + }, + Ok(GossipMessage::FinalityProof(proof)) => { + let (round, set_id) = proof_block_num_and_set_id::(&proof); + let allowed = filter.is_finality_proof_accepted(round, set_id); + trace!( + target: LOG_TARGET, + "🥩 Finality proof for round #{} allowed: {}", + round, + allowed + ); + allowed + }, + Err(_) => false, + } }) } } #[cfg(test)] -mod tests { +pub(crate) mod tests { use super::*; use crate::keystore::BeefyKeystore; use sc_network_test::Block; use sp_consensus_beefy::{ - crypto::Signature, known_payloads, Commitment, Keyring, MmrRootHash, Payload, VoteMessage, - KEY_TYPE, + crypto::Signature, known_payloads, Commitment, Keyring, MmrRootHash, Payload, + SignedCommitment, VoteMessage, KEY_TYPE, }; use sp_keystore::{testing::MemoryKeystore, Keystore}; #[test] fn known_votes_insert_remove() { - let mut kv = VotesFilter::::new(); + let mut filter = Filter::::new(); let msg_hash = twox_64(b"data"); - - kv.add_known(1, msg_hash); - kv.add_known(1, msg_hash); - kv.add_known(2, msg_hash); - assert_eq!(kv.live.len(), 2); - - kv.add_known(3, msg_hash); - assert!(kv.is_known(3, &msg_hash)); - assert!(!kv.is_known(3, &twox_64(b"other"))); - assert!(!kv.is_known(4, &msg_hash)); - assert_eq!(kv.live.len(), 3); - - assert!(kv.filter.is_none()); - assert!(!kv.is_live(1, 1)); - - kv.update(GossipVoteFilter { start: 3, end: 10, validator_set_id: 1 }); - assert_eq!(kv.live.len(), 1); - assert!(kv.live.contains_key(&3)); - assert!(!kv.is_live(2, 1)); - assert!(kv.is_live(3, 1)); - assert!(kv.is_live(4, 1)); - assert!(!kv.is_live(4, 2)); - - kv.update(GossipVoteFilter { start: 5, end: 10, validator_set_id: 2 }); - assert!(kv.live.is_empty()); + let keys = vec![Keyring::Alice.public()]; + let validator_set = ValidatorSet::::new(keys.clone(), 1).unwrap(); + + filter.add_known_vote(1, msg_hash); + filter.add_known_vote(1, msg_hash); + filter.add_known_vote(2, msg_hash); + assert_eq!(filter.live_votes.len(), 2); + + filter.add_known_vote(3, msg_hash); + assert!(filter.is_known_vote(3, &msg_hash)); + assert!(!filter.is_known_vote(3, &twox_64(b"other"))); + assert!(!filter.is_known_vote(4, &msg_hash)); + assert_eq!(filter.live_votes.len(), 3); + + assert!(filter.inner.is_none()); + assert!(!filter.is_vote_accepted(1, 1)); + + filter.update(GossipFilterCfg { start: 3, end: 10, validator_set: &validator_set }); + assert_eq!(filter.live_votes.len(), 1); + assert!(filter.live_votes.contains_key(&3)); + assert!(!filter.is_vote_accepted(2, 1)); + assert!(filter.is_vote_accepted(3, 1)); + assert!(filter.is_vote_accepted(4, 1)); + assert!(!filter.is_vote_accepted(4, 2)); + + let validator_set = ValidatorSet::::new(keys, 2).unwrap(); + filter.update(GossipFilterCfg { start: 5, end: 10, validator_set: &validator_set }); + assert!(filter.live_votes.is_empty()); } struct TestContext; @@ -302,14 +443,14 @@ mod tests { } } - fn sign_commitment(who: &Keyring, commitment: &Commitment) -> Signature { + pub fn sign_commitment(who: &Keyring, commitment: &Commitment) -> Signature { let store = MemoryKeystore::new(); store.ecdsa_generate_new(KEY_TYPE, Some(&who.to_seed())).unwrap(); let beefy_keystore: BeefyKeystore = Some(store.into()).into(); beefy_keystore.sign(&who.public(), &commitment.encode()).unwrap() } - fn dummy_vote(block_number: u64) -> VoteMessage { + fn dummy_vote(block_number: u64) -> VoteMessage { let payload = Payload::from_single_entry( known_payloads::MMR_ROOT_ID, MmrRootHash::default().encode(), @@ -320,51 +461,111 @@ mod tests { VoteMessage { commitment, id: Keyring::Alice.public(), signature } } + pub fn dummy_proof( + block_number: u64, + validator_set: &ValidatorSet, + ) -> BeefyVersionedFinalityProof { + let payload = Payload::from_single_entry( + known_payloads::MMR_ROOT_ID, + MmrRootHash::default().encode(), + ); + let commitment = Commitment { payload, block_number, validator_set_id: validator_set.id() }; + let signatures = validator_set + .validators() + .iter() + .map(|validator: &AuthorityId| { + Some(sign_commitment(&Keyring::from_public(validator).unwrap(), &commitment)) + }) + .collect(); + + BeefyVersionedFinalityProof::::V1(SignedCommitment { commitment, signatures }) + } + #[test] - fn should_avoid_verifying_signatures_twice() { + fn should_validate_messages() { + let keys = vec![Keyring::Alice.public()]; + let validator_set = ValidatorSet::::new(keys.clone(), 0).unwrap(); let gv = GossipValidator::::new(Arc::new(Mutex::new(KnownPeers::new()))); - gv.update_filter(GossipVoteFilter { start: 0, end: 10, validator_set_id: 0 }); + gv.update_filter(GossipFilterCfg { start: 0, end: 10, validator_set: &validator_set }); let sender = sc_network::PeerId::random(); let mut context = TestContext; let vote = dummy_vote(3); + let gossip_vote = GossipMessage::::Vote(vote.clone()); // first time the cache should be populated - let res = gv.validate(&mut context, &sender, &vote.encode()); + let res = gv.validate(&mut context, &sender, &gossip_vote.encode()); assert!(matches!(res, ValidationResult::ProcessAndKeep(_))); assert_eq!( - gv.votes_filter.read().live.get(&vote.commitment.block_number).map(|x| x.len()), + gv.gossip_filter + .read() + .live_votes + .get(&vote.commitment.block_number) + .map(|x| x.len()), Some(1) ); // second time we should hit the cache - let res = gv.validate(&mut context, &sender, &vote.encode()); - + let res = gv.validate(&mut context, &sender, &gossip_vote.encode()); assert!(matches!(res, ValidationResult::ProcessAndKeep(_))); // next we should quickly reject if the round is not live - gv.update_filter(GossipVoteFilter { start: 7, end: 10, validator_set_id: 0 }); + gv.update_filter(GossipFilterCfg { start: 7, end: 10, validator_set: &validator_set }); let number = vote.commitment.block_number; let set_id = vote.commitment.validator_set_id; - assert!(!gv.votes_filter.read().is_live(number, set_id)); + assert!(!gv.gossip_filter.read().is_vote_accepted(number, set_id)); let res = gv.validate(&mut context, &sender, &vote.encode()); + assert!(matches!(res, ValidationResult::Discard)); + + // reject old proof + let proof = dummy_proof(5, &validator_set); + let encoded_proof = GossipMessage::::FinalityProof(proof).encode(); + let res = gv.validate(&mut context, &sender, &encoded_proof); + assert!(matches!(res, ValidationResult::Discard)); + + // accept next proof with good set_id + let proof = dummy_proof(7, &validator_set); + let encoded_proof = GossipMessage::::FinalityProof(proof).encode(); + let res = gv.validate(&mut context, &sender, &encoded_proof); + assert!(matches!(res, ValidationResult::ProcessAndKeep(_))); + + // accept future proof with good set_id + let proof = dummy_proof(20, &validator_set); + let encoded_proof = GossipMessage::::FinalityProof(proof).encode(); + let res = gv.validate(&mut context, &sender, &encoded_proof); + assert!(matches!(res, ValidationResult::ProcessAndKeep(_))); + // reject proof, wrong set_id + let bad_validator_set = ValidatorSet::::new(keys, 1).unwrap(); + let proof = dummy_proof(20, &bad_validator_set); + let encoded_proof = GossipMessage::::FinalityProof(proof).encode(); + let res = gv.validate(&mut context, &sender, &encoded_proof); + assert!(matches!(res, ValidationResult::Discard)); + + // reject proof, bad signatures (Bob instead of Alice) + let bad_validator_set = + ValidatorSet::::new(vec![Keyring::Bob.public()], 0).unwrap(); + let proof = dummy_proof(20, &bad_validator_set); + let encoded_proof = GossipMessage::::FinalityProof(proof).encode(); + let res = gv.validate(&mut context, &sender, &encoded_proof); assert!(matches!(res, ValidationResult::Discard)); } #[test] fn messages_allowed_and_expired() { + let keys = vec![Keyring::Alice.public()]; + let validator_set = ValidatorSet::::new(keys.clone(), 0).unwrap(); let gv = GossipValidator::::new(Arc::new(Mutex::new(KnownPeers::new()))); - gv.update_filter(GossipVoteFilter { start: 0, end: 10, validator_set_id: 0 }); + gv.update_filter(GossipFilterCfg { start: 0, end: 10, validator_set: &validator_set }); let sender = sc_network::PeerId::random(); let topic = Default::default(); let intent = MessageIntent::Broadcast; // conclude 2 - gv.update_filter(GossipVoteFilter { start: 2, end: 10, validator_set_id: 0 }); + gv.update_filter(GossipFilterCfg { start: 2, end: 10, validator_set: &validator_set }); let mut allowed = gv.message_allowed(); let mut expired = gv.message_expired(); @@ -374,33 +575,68 @@ mod tests { // inactive round 1 -> expired let vote = dummy_vote(1); - let mut encoded_vote = vote.encode(); + let mut encoded_vote = GossipMessage::::Vote(vote).encode(); assert!(!allowed(&sender, intent, &topic, &mut encoded_vote)); assert!(expired(topic, &mut encoded_vote)); + let proof = dummy_proof(1, &validator_set); + let mut encoded_proof = GossipMessage::::FinalityProof(proof).encode(); + assert!(!allowed(&sender, intent, &topic, &mut encoded_proof)); + assert!(expired(topic, &mut encoded_proof)); // active round 2 -> !expired - concluded but still gossiped let vote = dummy_vote(2); - let mut encoded_vote = vote.encode(); + let mut encoded_vote = GossipMessage::::Vote(vote).encode(); assert!(allowed(&sender, intent, &topic, &mut encoded_vote)); assert!(!expired(topic, &mut encoded_vote)); + let proof = dummy_proof(2, &validator_set); + let mut encoded_proof = GossipMessage::::FinalityProof(proof).encode(); + assert!(allowed(&sender, intent, &topic, &mut encoded_proof)); + assert!(!expired(topic, &mut encoded_proof)); + // using wrong set_id -> !allowed, expired + let bad_validator_set = ValidatorSet::::new(keys.clone(), 1).unwrap(); + let proof = dummy_proof(2, &bad_validator_set); + let mut encoded_proof = GossipMessage::::FinalityProof(proof).encode(); + assert!(!allowed(&sender, intent, &topic, &mut encoded_proof)); + assert!(expired(topic, &mut encoded_proof)); // in progress round 3 -> !expired let vote = dummy_vote(3); - let mut encoded_vote = vote.encode(); + let mut encoded_vote = GossipMessage::::Vote(vote).encode(); assert!(allowed(&sender, intent, &topic, &mut encoded_vote)); assert!(!expired(topic, &mut encoded_vote)); + let proof = dummy_proof(3, &validator_set); + let mut encoded_proof = GossipMessage::::FinalityProof(proof).encode(); + assert!(allowed(&sender, intent, &topic, &mut encoded_proof)); + assert!(!expired(topic, &mut encoded_proof)); // unseen round 4 -> !expired - let vote = dummy_vote(3); - let mut encoded_vote = vote.encode(); + let vote = dummy_vote(4); + let mut encoded_vote = GossipMessage::::Vote(vote).encode(); assert!(allowed(&sender, intent, &topic, &mut encoded_vote)); assert!(!expired(topic, &mut encoded_vote)); + let proof = dummy_proof(4, &validator_set); + let mut encoded_proof = GossipMessage::::FinalityProof(proof).encode(); + assert!(allowed(&sender, intent, &topic, &mut encoded_proof)); + assert!(!expired(topic, &mut encoded_proof)); + + // future round 11 -> expired + let vote = dummy_vote(11); + let mut encoded_vote = GossipMessage::::Vote(vote).encode(); + assert!(!allowed(&sender, intent, &topic, &mut encoded_vote)); + assert!(expired(topic, &mut encoded_vote)); + // future proofs allowed while same set_id -> allowed + let proof = dummy_proof(11, &validator_set); + let mut encoded_proof = GossipMessage::::FinalityProof(proof).encode(); + assert!(allowed(&sender, intent, &topic, &mut encoded_proof)); + assert!(!expired(topic, &mut encoded_proof)); } #[test] fn messages_rebroadcast() { + let keys = vec![Keyring::Alice.public()]; + let validator_set = ValidatorSet::::new(keys.clone(), 0).unwrap(); let gv = GossipValidator::::new(Arc::new(Mutex::new(KnownPeers::new()))); - gv.update_filter(GossipVoteFilter { start: 0, end: 10, validator_set_id: 0 }); + gv.update_filter(GossipFilterCfg { start: 0, end: 10, validator_set: &validator_set }); let sender = sc_network::PeerId::random(); let topic = Default::default(); diff --git a/client/consensus/beefy/src/communication/mod.rs b/client/consensus/beefy/src/communication/mod.rs index 295d549bb..13735a9d3 100644 --- a/client/consensus/beefy/src/communication/mod.rs +++ b/client/consensus/beefy/src/communication/mod.rs @@ -29,7 +29,7 @@ pub(crate) mod beefy_protocol_name { use sc_network::ProtocolName; /// BEEFY votes gossip protocol name suffix. - const GOSSIP_NAME: &str = "/beefy/1"; + const GOSSIP_NAME: &str = "/beefy/2"; /// BEEFY justifications protocol name suffix. const JUSTIFICATIONS_NAME: &str = "/beefy/justifications/1"; @@ -86,7 +86,7 @@ mod tests { let genesis_hash = H256::random(); let genesis_hex = array_bytes::bytes2hex("", genesis_hash.as_ref()); - let expected_gossip_name = format!("/{}/beefy/1", genesis_hex); + let expected_gossip_name = format!("/{}/beefy/2", genesis_hex); let gossip_proto_name = gossip_protocol_name(&genesis_hash, None); assert_eq!(gossip_proto_name.to_string(), expected_gossip_name); @@ -101,7 +101,7 @@ mod tests { ]; let genesis_hex = "32043c7b3a6ad8f6c2bc8bc121d4caab09377b5e082b0cfbbb39ad13bc4acd93"; - let expected_gossip_name = format!("/{}/beefy/1", genesis_hex); + let expected_gossip_name = format!("/{}/beefy/2", genesis_hex); let gossip_proto_name = gossip_protocol_name(&genesis_hash, None); assert_eq!(gossip_proto_name.to_string(), expected_gossip_name); diff --git a/client/consensus/beefy/src/justification.rs b/client/consensus/beefy/src/justification.rs index 1bd250b2a..5175fd17d 100644 --- a/client/consensus/beefy/src/justification.rs +++ b/client/consensus/beefy/src/justification.rs @@ -21,13 +21,21 @@ use codec::{Decode, Encode}; use sp_consensus::Error as ConsensusError; use sp_consensus_beefy::{ crypto::{AuthorityId, Signature}, - ValidatorSet, VersionedFinalityProof, + ValidatorSet, ValidatorSetId, VersionedFinalityProof, }; use sp_runtime::traits::{Block as BlockT, NumberFor}; /// A finality proof with matching BEEFY authorities' signatures. -pub type BeefyVersionedFinalityProof = - sp_consensus_beefy::VersionedFinalityProof, Signature>; +pub type BeefyVersionedFinalityProof = VersionedFinalityProof, Signature>; + +pub(crate) fn proof_block_num_and_set_id( + proof: &BeefyVersionedFinalityProof, +) -> (NumberFor, ValidatorSetId) { + match proof { + VersionedFinalityProof::V1(sc) => + (sc.commitment.block_number, sc.commitment.validator_set_id), + } +} /// Decode and verify a Beefy FinalityProof. pub(crate) fn decode_and_verify_finality_proof( @@ -41,7 +49,7 @@ pub(crate) fn decode_and_verify_finality_proof( } /// Verify the Beefy finality proof against the validator set at the block it was generated. -fn verify_with_validator_set( +pub(crate) fn verify_with_validator_set( target_number: NumberFor, validator_set: &ValidatorSet, proof: &BeefyVersionedFinalityProof, diff --git a/client/consensus/beefy/src/lib.rs b/client/consensus/beefy/src/lib.rs index b84fa45e7..3c66cc6eb 100644 --- a/client/consensus/beefy/src/lib.rs +++ b/client/consensus/beefy/src/lib.rs @@ -288,7 +288,7 @@ pub async fn start_beefy_gadget( }; // Update the gossip validator with the right starting round and set id. if let Err(e) = persisted_state - .current_gossip_filter() + .gossip_filter_config() .map(|f| gossip_validator.update_filter(f)) { error!(target: LOG_TARGET, "Error: {:?}. Terminating.", e); diff --git a/client/consensus/beefy/src/round.rs b/client/consensus/beefy/src/round.rs index 64d03beee..d8948ff98 100644 --- a/client/consensus/beefy/src/round.rs +++ b/client/consensus/beefy/src/round.rs @@ -21,7 +21,7 @@ use crate::LOG_TARGET; use codec::{Decode, Encode}; use log::debug; use sp_consensus_beefy::{ - crypto::{AuthorityId, Public, Signature}, + crypto::{AuthorityId, Signature}, Commitment, EquivocationProof, SignedCommitment, ValidatorSet, ValidatorSetId, VoteMessage, }; use sp_runtime::traits::{Block, NumberFor}; @@ -33,11 +33,11 @@ use std::collections::BTreeMap; /// Does not do any validation on votes or signatures, layers above need to handle that (gossip). #[derive(Debug, Decode, Default, Encode, PartialEq)] pub(crate) struct RoundTracker { - votes: BTreeMap, + votes: BTreeMap, } impl RoundTracker { - fn add_vote(&mut self, vote: (Public, Signature)) -> bool { + fn add_vote(&mut self, vote: (AuthorityId, Signature)) -> bool { if self.votes.contains_key(&vote.0) { return false } @@ -61,7 +61,7 @@ pub fn threshold(authorities: usize) -> usize { pub enum VoteImportResult { Ok, RoundConcluded(SignedCommitment, Signature>), - Equivocation(EquivocationProof, Public, Signature>), + Equivocation(EquivocationProof, AuthorityId, Signature>), Invalid, Stale, } @@ -73,9 +73,10 @@ pub enum VoteImportResult { #[derive(Debug, Decode, Encode, PartialEq)] pub(crate) struct Rounds { rounds: BTreeMap>, RoundTracker>, - previous_votes: BTreeMap<(Public, NumberFor), VoteMessage, Public, Signature>>, + previous_votes: + BTreeMap<(AuthorityId, NumberFor), VoteMessage, AuthorityId, Signature>>, session_start: NumberFor, - validator_set: ValidatorSet, + validator_set: ValidatorSet, mandatory_done: bool, best_done: Option>, } @@ -84,7 +85,10 @@ impl Rounds where B: Block, { - pub(crate) fn new(session_start: NumberFor, validator_set: ValidatorSet) -> Self { + pub(crate) fn new( + session_start: NumberFor, + validator_set: ValidatorSet, + ) -> Self { Rounds { rounds: BTreeMap::new(), previous_votes: BTreeMap::new(), @@ -95,7 +99,7 @@ where } } - pub(crate) fn validator_set(&self) -> &ValidatorSet { + pub(crate) fn validator_set(&self) -> &ValidatorSet { &self.validator_set } @@ -103,7 +107,7 @@ where self.validator_set.id() } - pub(crate) fn validators(&self) -> &[Public] { + pub(crate) fn validators(&self) -> &[AuthorityId] { self.validator_set.validators() } @@ -199,11 +203,11 @@ mod tests { use sc_network_test::Block; use sp_consensus_beefy::{ - crypto::Public, known_payloads::MMR_ROOT_ID, Commitment, EquivocationProof, Keyring, - Payload, SignedCommitment, ValidatorSet, VoteMessage, + known_payloads::MMR_ROOT_ID, Commitment, EquivocationProof, Keyring, Payload, + SignedCommitment, ValidatorSet, VoteMessage, }; - use super::{threshold, Block as BlockT, RoundTracker, Rounds}; + use super::{threshold, AuthorityId, Block as BlockT, RoundTracker, Rounds}; use crate::round::VoteImportResult; impl Rounds @@ -251,7 +255,7 @@ mod tests { fn new_rounds() { sp_tracing::try_init_simple(); - let validators = ValidatorSet::::new( + let validators = ValidatorSet::::new( vec![Keyring::Alice.public(), Keyring::Bob.public(), Keyring::Charlie.public()], 42, ) @@ -272,7 +276,7 @@ mod tests { fn add_and_conclude_votes() { sp_tracing::try_init_simple(); - let validators = ValidatorSet::::new( + let validators = ValidatorSet::::new( vec![ Keyring::Alice.public(), Keyring::Bob.public(), @@ -338,7 +342,7 @@ mod tests { fn old_rounds_not_accepted() { sp_tracing::try_init_simple(); - let validators = ValidatorSet::::new( + let validators = ValidatorSet::::new( vec![Keyring::Alice.public(), Keyring::Bob.public(), Keyring::Charlie.public()], 42, ) @@ -384,7 +388,7 @@ mod tests { fn multiple_rounds() { sp_tracing::try_init_simple(); - let validators = ValidatorSet::::new( + let validators = ValidatorSet::::new( vec![Keyring::Alice.public(), Keyring::Bob.public(), Keyring::Charlie.public()], Default::default(), ) @@ -459,7 +463,7 @@ mod tests { fn should_provide_equivocation_proof() { sp_tracing::try_init_simple(); - let validators = ValidatorSet::::new( + let validators = ValidatorSet::::new( vec![Keyring::Alice.public(), Keyring::Bob.public()], Default::default(), ) diff --git a/client/consensus/beefy/src/tests.rs b/client/consensus/beefy/src/tests.rs index 0ad5f1088..f36c2cd68 100644 --- a/client/consensus/beefy/src/tests.rs +++ b/client/consensus/beefy/src/tests.rs @@ -21,15 +21,18 @@ use crate::{ aux_schema::{load_persistent, tests::verify_persisted_version}, beefy_block_import_and_links, - communication::request_response::{ - on_demand_justifications_protocol_config, BeefyJustifsRequestHandler, + communication::{ + gossip::{ + proofs_topic, tests::sign_commitment, votes_topic, GossipFilterCfg, GossipMessage, + }, + request_response::{on_demand_justifications_protocol_config, BeefyJustifsRequestHandler}, }, gossip_protocol_name, justification::*, load_or_init_voter_state, wait_for_runtime_pallet, BeefyRPCLinks, BeefyVoterLinks, KnownPeers, PersistedState, }; -use futures::{future, stream::FuturesUnordered, Future, StreamExt}; +use futures::{future, stream::FuturesUnordered, Future, FutureExt, StreamExt}; use parking_lot::Mutex; use sc_client_api::{Backend as BackendT, BlockchainEvents, FinalityNotifications, HeaderBackend}; use sc_consensus::{ @@ -48,16 +51,16 @@ use sp_consensus::BlockOrigin; use sp_consensus_beefy::{ crypto::{AuthorityId, Signature}, known_payloads, - mmr::MmrRootProvider, + mmr::{find_mmr_root_digest, MmrRootProvider}, BeefyApi, Commitment, ConsensusLog, EquivocationProof, Keyring as BeefyKeyring, MmrRootHash, OpaqueKeyOwnershipProof, Payload, SignedCommitment, ValidatorSet, ValidatorSetId, - VersionedFinalityProof, BEEFY_ENGINE_ID, KEY_TYPE as BeefyKeyType, + VersionedFinalityProof, VoteMessage, BEEFY_ENGINE_ID, KEY_TYPE as BeefyKeyType, }; use sp_core::H256; use sp_keystore::{testing::MemoryKeystore, Keystore, KeystorePtr}; use sp_mmr_primitives::{Error as MmrError, MmrApi}; use sp_runtime::{ - codec::Encode, + codec::{Decode, Encode}, traits::{Header as HeaderT, NumberFor}, BuildStorage, DigestItem, EncodedJustification, Justifications, Storage, }; @@ -503,16 +506,15 @@ async fn wait_for_beefy_signed_commitments( run_until(wait_for, net).await; } -async fn streams_empty_after_timeout( +async fn streams_empty_after_future( streams: Vec>, - net: &Arc>, - timeout: Option, + future: Option, ) where T: std::fmt::Debug, T: std::cmp::PartialEq, { - if let Some(timeout) = timeout { - run_for(timeout, net).await; + if let Some(future) = future { + future.await; } for mut stream in streams.into_iter() { future::poll_fn(move |cx| { @@ -523,6 +525,18 @@ async fn streams_empty_after_timeout( } } +async fn streams_empty_after_timeout( + streams: Vec>, + net: &Arc>, + timeout: Option, +) where + T: std::fmt::Debug, + T: std::cmp::PartialEq, +{ + let timeout = timeout.map(|timeout| Box::pin(run_for(timeout, net))); + streams_empty_after_future(streams, timeout).await; +} + async fn finalize_block_and_wait_for_beefy( net: &Arc>, // peer index and key @@ -1229,3 +1243,143 @@ async fn beefy_reports_equivocations() { assert_eq!(equivocation_proof.first.id, BeefyKeyring::Bob.public()); assert_eq!(equivocation_proof.first.commitment.block_number, 1); } + +#[tokio::test] +async fn gossipped_finality_proofs() { + sp_tracing::try_init_simple(); + + let validators = [BeefyKeyring::Alice, BeefyKeyring::Bob, BeefyKeyring::Charlie]; + // Only Alice and Bob are running the voter -> finality threshold not reached + let peers = [BeefyKeyring::Alice, BeefyKeyring::Bob]; + let validator_set = ValidatorSet::new(make_beefy_ids(&validators), 0).unwrap(); + let session_len = 30; + let min_block_delta = 1; + + let mut net = BeefyTestNet::new(3); + let api = Arc::new(TestApi::with_validator_set(&validator_set)); + let beefy_peers = peers.iter().enumerate().map(|(id, key)| (id, key, api.clone())).collect(); + + let charlie = &net.peers[2]; + let known_peers = Arc::new(Mutex::new(KnownPeers::::new())); + // Charlie will run just the gossip engine and not the full voter. + let charlie_gossip_validator = + Arc::new(crate::communication::gossip::GossipValidator::new(known_peers)); + charlie_gossip_validator.update_filter(GossipFilterCfg:: { + start: 1, + end: 10, + validator_set: &validator_set, + }); + let mut charlie_gossip_engine = sc_network_gossip::GossipEngine::new( + charlie.network_service().clone(), + charlie.sync_service().clone(), + beefy_gossip_proto_name(), + charlie_gossip_validator.clone(), + None, + ); + + // Alice and Bob run full voter. + tokio::spawn(initialize_beefy(&mut net, beefy_peers, min_block_delta)); + + let net = Arc::new(Mutex::new(net)); + + // Pump net + Charlie gossip to see peers. + let timeout = Box::pin(tokio::time::sleep(Duration::from_millis(200))); + let gossip_engine_pump = &mut charlie_gossip_engine; + let pump_with_timeout = future::select(gossip_engine_pump, timeout); + run_until(pump_with_timeout, &net).await; + + // push 10 blocks + let hashes = net.lock().generate_blocks_and_sync(10, session_len, &validator_set, true).await; + + let peers = peers.into_iter().enumerate(); + + // Alice, Bob and Charlie finalize #1, Alice and Bob vote on it, but not Charlie. + let finalize = hashes[1]; + let (best_blocks, versioned_finality_proof) = get_beefy_streams(&mut net.lock(), peers.clone()); + net.lock().peer(0).client().as_client().finalize_block(finalize, None).unwrap(); + net.lock().peer(1).client().as_client().finalize_block(finalize, None).unwrap(); + net.lock().peer(2).client().as_client().finalize_block(finalize, None).unwrap(); + // verify nothing gets finalized by BEEFY + let timeout = Box::pin(tokio::time::sleep(Duration::from_millis(100))); + let pump_net = futures::future::poll_fn(|cx| { + net.lock().poll(cx); + Poll::<()>::Pending + }); + let pump_gossip = &mut charlie_gossip_engine; + let pump_with_timeout = future::select(pump_gossip, future::select(pump_net, timeout)); + streams_empty_after_future(best_blocks, Some(pump_with_timeout)).await; + streams_empty_after_timeout(versioned_finality_proof, &net, None).await; + + let (best_blocks, versioned_finality_proof) = get_beefy_streams(&mut net.lock(), peers.clone()); + // Charlie gossips finality proof for #1 -> Alice and Bob also finalize. + let proof = crate::communication::gossip::tests::dummy_proof(1, &validator_set); + let gossip_proof = GossipMessage::::FinalityProof(proof); + let encoded_proof = gossip_proof.encode(); + charlie_gossip_engine.gossip_message(proofs_topic::(), encoded_proof, true); + // Expect #1 is finalized. + wait_for_best_beefy_blocks(best_blocks, &net, &[1]).await; + wait_for_beefy_signed_commitments(versioned_finality_proof, &net, &[1]).await; + + // Code above verifies gossipped finality proofs are correctly imported and consumed by voters. + // Next, let's verify finality proofs are correctly generated and gossipped by voters. + + // Everyone finalizes #2 + let block_number = 2u64; + let finalize = hashes[block_number as usize]; + let (best_blocks, versioned_finality_proof) = get_beefy_streams(&mut net.lock(), peers.clone()); + net.lock().peer(0).client().as_client().finalize_block(finalize, None).unwrap(); + net.lock().peer(1).client().as_client().finalize_block(finalize, None).unwrap(); + net.lock().peer(2).client().as_client().finalize_block(finalize, None).unwrap(); + + // Simulate Charlie vote on #2 + let header = net.lock().peer(2).client().as_client().expect_header(finalize).unwrap(); + let mmr_root = find_mmr_root_digest::(&header).unwrap(); + let payload = Payload::from_single_entry(known_payloads::MMR_ROOT_ID, mmr_root.encode()); + let commitment = Commitment { payload, block_number, validator_set_id: validator_set.id() }; + let signature = sign_commitment(&BeefyKeyring::Charlie, &commitment); + let vote_message = VoteMessage { commitment, id: BeefyKeyring::Charlie.public(), signature }; + let encoded_vote = GossipMessage::::Vote(vote_message).encode(); + charlie_gossip_engine.gossip_message(votes_topic::(), encoded_vote, true); + + // Expect #2 is finalized. + wait_for_best_beefy_blocks(best_blocks, &net, &[2]).await; + wait_for_beefy_signed_commitments(versioned_finality_proof, &net, &[2]).await; + + // Now verify Charlie also sees the gossipped proof generated by either Alice or Bob. + let mut charlie_gossip_proofs = Box::pin( + charlie_gossip_engine + .messages_for(proofs_topic::()) + .filter_map(|notification| async move { + GossipMessage::::decode(&mut ¬ification.message[..]).ok().and_then( + |message| match message { + GossipMessage::::Vote(_) => unreachable!(), + GossipMessage::::FinalityProof(proof) => Some(proof), + }, + ) + }) + .fuse(), + ); + loop { + let pump_net = futures::future::poll_fn(|cx| { + net.lock().poll(cx); + Poll::<()>::Pending + }); + let mut gossip_engine = &mut charlie_gossip_engine; + futures::select! { + // pump gossip engine + _ = gossip_engine => unreachable!(), + // pump network + _ = pump_net.fuse() => unreachable!(), + // verify finality proof has been gossipped + proof = charlie_gossip_proofs.next() => { + let proof = proof.unwrap(); + let (round, _) = proof_block_num_and_set_id::(&proof); + match round { + 1 => continue, // finality proof generated by Charlie in the previous round + 2 => break, // finality proof generated by Alice or Bob and gossiped to Charlie + _ => panic!("Charlie got unexpected finality proof"), + } + }, + } + } +} diff --git a/client/consensus/beefy/src/worker.rs b/client/consensus/beefy/src/worker.rs index 0260d7693..19225ec21 100644 --- a/client/consensus/beefy/src/worker.rs +++ b/client/consensus/beefy/src/worker.rs @@ -18,7 +18,7 @@ use crate::{ communication::{ - gossip::{topic, GossipValidator, GossipVoteFilter}, + gossip::{proofs_topic, votes_topic, GossipFilterCfg, GossipMessage, GossipValidator}, request_response::outgoing_requests_engine::OnDemandJustificationsEngine, }, error::Error, @@ -42,7 +42,7 @@ use sp_consensus_beefy::{ check_equivocation_proof, crypto::{AuthorityId, Signature}, BeefyApi, Commitment, ConsensusLog, EquivocationProof, PayloadProvider, ValidatorSet, - ValidatorSetId, VersionedFinalityProof, VoteMessage, BEEFY_ENGINE_ID, + VersionedFinalityProof, VoteMessage, BEEFY_ENGINE_ID, }; use sp_runtime::{ generic::OpaqueDigestItemId, @@ -158,8 +158,8 @@ impl VoterOracle { self.sessions.front_mut().ok_or(Error::UninitSession) } - fn current_validator_set_id(&self) -> Result { - self.active_rounds().map(|r| r.validator_set_id()) + fn current_validator_set(&self) -> Result<&ValidatorSet, Error> { + self.active_rounds().map(|r| r.validator_set()) } // Prune the sessions queue to keep the Oracle in one of the expected three states. @@ -301,10 +301,10 @@ impl PersistedState { self.voting_oracle.best_grandpa_block_header = best_grandpa; } - pub(crate) fn current_gossip_filter(&self) -> Result, Error> { + pub(crate) fn gossip_filter_config(&self) -> Result, Error> { let (start, end) = self.voting_oracle.accepted_interval()?; - let validator_set_id = self.voting_oracle.current_validator_set_id()?; - Ok(GossipVoteFilter { start, end, validator_set_id }) + let validator_set = self.voting_oracle.current_validator_set()?; + Ok(GossipFilterCfg { start, end, validator_set }) } } @@ -494,7 +494,7 @@ where // Update gossip validator votes filter. if let Err(e) = self .persisted_state - .current_gossip_filter() + .gossip_filter_config() .map(|filter| self.gossip_validator.update_filter(filter)) { error!(target: LOG_TARGET, "🥩 Voter error: {:?}", e); @@ -509,7 +509,12 @@ where ) -> Result<(), Error> { let block_num = vote.commitment.block_number; match self.voting_oracle().triage_round(block_num)? { - RoundAction::Process => self.handle_vote(vote)?, + RoundAction::Process => + if let Some(finality_proof) = self.handle_vote(vote)? { + let gossip_proof = GossipMessage::::FinalityProof(finality_proof); + let encoded_proof = gossip_proof.encode(); + self.gossip_engine.gossip_message(proofs_topic::(), encoded_proof, true); + }, RoundAction::Drop => metric_inc!(self, beefy_stale_votes), RoundAction::Enqueue => error!(target: LOG_TARGET, "🥩 unexpected vote: {:?}.", vote), }; @@ -554,7 +559,7 @@ where fn handle_vote( &mut self, vote: VoteMessage, AuthorityId, Signature>, - ) -> Result<(), Error> { + ) -> Result>, Error> { let rounds = self.persisted_state.voting_oracle.active_rounds_mut()?; let block_number = vote.commitment.block_number; @@ -567,8 +572,9 @@ where ); // We created the `finality_proof` and know to be valid. // New state is persisted after finalization. - self.finalize(finality_proof)?; + self.finalize(finality_proof.clone())?; metric_inc!(self, beefy_good_votes_processed); + return Ok(Some(finality_proof)) }, VoteImportResult::Ok => { // Persist state after handling mandatory block vote. @@ -590,7 +596,7 @@ where VoteImportResult::Invalid => metric_inc!(self, beefy_invalid_votes), VoteImportResult::Stale => metric_inc!(self, beefy_stale_votes), }; - Ok(()) + Ok(None) } /// Provide BEEFY finality for block based on `finality_proof`: @@ -643,7 +649,7 @@ where // Update gossip validator votes filter. self.persisted_state - .current_gossip_filter() + .gossip_filter_config() .map(|filter| self.gossip_validator.update_filter(filter))?; Ok(()) } @@ -758,20 +764,20 @@ where BeefyKeystore::verify(&authority_id, &signature, &encoded_commitment) ); - let message = VoteMessage { commitment, id: authority_id, signature }; - - let encoded_message = message.encode(); - - metric_inc!(self, beefy_votes_sent); - - debug!(target: LOG_TARGET, "🥩 Sent vote message: {:?}", message); - - if let Err(err) = self.handle_vote(message) { + let vote = VoteMessage { commitment, id: authority_id, signature }; + if let Some(finality_proof) = self.handle_vote(vote.clone()).map_err(|err| { error!(target: LOG_TARGET, "🥩 Error handling self vote: {}", err); + err + })? { + let encoded_proof = GossipMessage::::FinalityProof(finality_proof).encode(); + self.gossip_engine.gossip_message(proofs_topic::(), encoded_proof, true); + } else { + metric_inc!(self, beefy_votes_sent); + debug!(target: LOG_TARGET, "🥩 Sent vote message: {:?}", vote); + let encoded_vote = GossipMessage::::Vote(vote).encode(); + self.gossip_engine.gossip_message(votes_topic::(), encoded_vote, false); } - self.gossip_engine.gossip_message(topic::(), encoded_message, false); - // Persist state after vote to avoid double voting in case of voter restarts. self.persisted_state.best_voted = target_number; metric_set!(self, beefy_best_voted, target_number); @@ -816,17 +822,28 @@ where let mut votes = Box::pin( self.gossip_engine - .messages_for(topic::()) + .messages_for(votes_topic::()) .filter_map(|notification| async move { - let vote = VoteMessage::, AuthorityId, Signature>::decode( - &mut ¬ification.message[..], - ) - .ok(); + let vote = GossipMessage::::decode(&mut ¬ification.message[..]) + .ok() + .and_then(|message| message.unwrap_vote()); trace!(target: LOG_TARGET, "🥩 Got vote message: {:?}", vote); vote }) .fuse(), ); + let mut gossip_proofs = Box::pin( + self.gossip_engine + .messages_for(proofs_topic::()) + .filter_map(|notification| async move { + let proof = GossipMessage::::decode(&mut ¬ification.message[..]) + .ok() + .and_then(|message| message.unwrap_finality_proof()); + trace!(target: LOG_TARGET, "🥩 Got gossip proof message: {:?}", proof); + proof + }) + .fuse(), + ); loop { // Act on changed 'state'. @@ -872,6 +889,20 @@ where return; } }, + justif = gossip_proofs.next() => { + if let Some(justif) = justif { + // Gossiped justifications have already been verified by `GossipValidator`. + if let Err(err) = self.triage_incoming_justif(justif) { + debug!(target: LOG_TARGET, "🥩 {}", err); + } + } else { + error!( + target: LOG_TARGET, + "🥩 Finality proofs gossiping stream terminated, closing worker." + ); + return; + } + }, // Finally process incoming votes. vote = votes.next() => { if let Some(vote) = vote { @@ -880,7 +911,10 @@ where debug!(target: LOG_TARGET, "🥩 {}", err); } } else { - error!(target: LOG_TARGET, "🥩 Votes gossiping stream terminated, closing worker."); + error!( + target: LOG_TARGET, + "🥩 Votes gossiping stream terminated, closing worker." + ); return; } }, From 4c7866a0df02e1c45fb4b5f473b2265a6bdb5465 Mon Sep 17 00:00:00 2001 From: Kian Paimani <5588131+kianenigma@users.noreply.github.com> Date: Thu, 30 Mar 2023 16:50:45 +0200 Subject: [PATCH 320/558] Fix nomiantion pools doc render (#13748) Co-authored-by: parity-processbot <> --- frame/nomination-pools/src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frame/nomination-pools/src/lib.rs b/frame/nomination-pools/src/lib.rs index cfde05ffe..e7c99e023 100644 --- a/frame/nomination-pools/src/lib.rs +++ b/frame/nomination-pools/src/lib.rs @@ -133,7 +133,7 @@ //! * Root: can change the nominator, bouncer, or itself, manage and claim commission, and can //! perform any of the actions the nominator or bouncer can. //! -//! ## Commission +//! ### Commission //! //! A pool can optionally have a commission configuration, via the `root` role, set with //! [`Call::set_commission`] and claimed with [`Call::claim_commission`]. A payee account must be From ee38a52d9ea643e4eec1e954b0b603aea4461416 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bastian=20K=C3=B6cher?= Date: Thu, 30 Mar 2023 21:31:08 +0200 Subject: [PATCH 321/558] sp-runtime-interface-test: Fix flaky test (#13770) --- primitives/runtime-interface/test/src/lib.rs | 105 +++++++++++-------- 1 file changed, 64 insertions(+), 41 deletions(-) diff --git a/primitives/runtime-interface/test/src/lib.rs b/primitives/runtime-interface/test/src/lib.rs index d691d4846..269b62333 100644 --- a/primitives/runtime-interface/test/src/lib.rs +++ b/primitives/runtime-interface/test/src/lib.rs @@ -176,62 +176,85 @@ fn test_versionining_register_only() { call_wasm_method::(wasm_binary_unwrap(), "test_versionning_register_only_works"); } +fn run_test_in_another_process( + test_name: &str, + test_body: impl FnOnce(), +) -> Option { + if std::env::var("RUN_FORKED_TEST").is_ok() { + test_body(); + None + } else { + let output = std::process::Command::new(std::env::current_exe().unwrap()) + .arg(test_name) + .env("RUN_FORKED_TEST", "1") + .output() + .unwrap(); + + assert!(output.status.success()); + Some(output) + } +} + #[test] fn test_tracing() { - use std::fmt; - use tracing::span::Id as SpanId; - use tracing_core::field::{Field, Visit}; - - #[derive(Clone)] - struct TracingSubscriber(Arc>); - - struct FieldConsumer(&'static str, Option); - impl Visit for FieldConsumer { - fn record_debug(&mut self, field: &Field, value: &dyn fmt::Debug) { - if field.name() == self.0 { - self.1 = Some(format!("{:?}", value)) + // Run in a different process to ensure that the `Span` is registered with our local + // `TracingSubscriber`. + run_test_in_another_process("test_tracing", || { + use std::fmt; + use tracing::span::Id as SpanId; + use tracing_core::field::{Field, Visit}; + + #[derive(Clone)] + struct TracingSubscriber(Arc>); + + struct FieldConsumer(&'static str, Option); + impl Visit for FieldConsumer { + fn record_debug(&mut self, field: &Field, value: &dyn fmt::Debug) { + if field.name() == self.0 { + self.1 = Some(format!("{:?}", value)) + } } } - } - #[derive(Default)] - struct Inner { - spans: HashSet, - } - - impl tracing::subscriber::Subscriber for TracingSubscriber { - fn enabled(&self, _: &tracing::Metadata) -> bool { - true + #[derive(Default)] + struct Inner { + spans: HashSet, } - fn new_span(&self, span: &tracing::span::Attributes) -> tracing::Id { - let mut inner = self.0.lock().unwrap(); - let id = SpanId::from_u64((inner.spans.len() + 1) as _); - let mut f = FieldConsumer("name", None); - span.record(&mut f); - inner.spans.insert(f.1.unwrap_or_else(|| span.metadata().name().to_owned())); - id - } + impl tracing::subscriber::Subscriber for TracingSubscriber { + fn enabled(&self, _: &tracing::Metadata) -> bool { + true + } - fn record(&self, _: &SpanId, _: &tracing::span::Record) {} + fn new_span(&self, span: &tracing::span::Attributes) -> tracing::Id { + let mut inner = self.0.lock().unwrap(); + let id = SpanId::from_u64((inner.spans.len() + 1) as _); + let mut f = FieldConsumer("name", None); + span.record(&mut f); + inner.spans.insert(f.1.unwrap_or_else(|| span.metadata().name().to_owned())); + id + } - fn record_follows_from(&self, _: &SpanId, _: &SpanId) {} + fn record(&self, _: &SpanId, _: &tracing::span::Record) {} - fn event(&self, _: &tracing::Event) {} + fn record_follows_from(&self, _: &SpanId, _: &SpanId) {} - fn enter(&self, _: &SpanId) {} + fn event(&self, _: &tracing::Event) {} - fn exit(&self, _: &SpanId) {} - } + fn enter(&self, _: &SpanId) {} - let subscriber = TracingSubscriber(Default::default()); - let _guard = tracing::subscriber::set_default(subscriber.clone()); + fn exit(&self, _: &SpanId) {} + } - // Call some method to generate a trace - call_wasm_method::(wasm_binary_unwrap(), "test_return_data"); + let subscriber = TracingSubscriber(Default::default()); + let _guard = tracing::subscriber::set_default(subscriber.clone()); + + // Call some method to generate a trace + call_wasm_method::(wasm_binary_unwrap(), "test_return_data"); - let inner = subscriber.0.lock().unwrap(); - assert!(inner.spans.contains("return_input_version_1")); + let inner = subscriber.0.lock().unwrap(); + assert!(inner.spans.contains("return_input_version_1")); + }); } #[test] From 4bf67fb3dc0f095e4210b5ebc5f1f2ea24bf36c0 Mon Sep 17 00:00:00 2001 From: Javier Viola Date: Thu, 30 Mar 2023 18:01:50 -0300 Subject: [PATCH 322/558] bump zombienet version (#13772) --- .gitlab-ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index c9b68bca1..470ce2ce3 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -56,7 +56,7 @@ variables: RUSTY_CACHIER_COMPRESSION_METHOD: zstd NEXTEST_FAILURE_OUTPUT: immediate-final NEXTEST_SUCCESS_OUTPUT: final - ZOMBIENET_IMAGE: "docker.io/paritytech/zombienet:v1.3.37" + ZOMBIENET_IMAGE: "docker.io/paritytech/zombienet:v1.3.43" .shared-default: &shared-default retry: From de4cca40b5da41e76c111c66229521f1052ea298 Mon Sep 17 00:00:00 2001 From: PG Herveou Date: Fri, 31 Mar 2023 13:03:56 +0200 Subject: [PATCH 323/558] [Contracts] Overflowing bounded `DeletionQueue` allows DoS against contract termination (#13702) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * [Contracts review] Overflowing bounded `DeletionQueue` allows DoS against contract termination * wip * wip * wip * wip * wip * fix doc * wip * PR review * unbreak tests * fixes * update budget computation * PR comment: use BlockWeights::get().max_block * PR comment: Update queue_trie_for_deletion signature * PR comment: update deletion budget docstring * PR comment: impl Default with derive(DefaultNoBound) * PR comment: Remove DeletedContract * PR comment Add ring_buffer test * remove missed comment * misc comments * contracts: add sr25519_recover * Revert "contracts: add sr25519_recover" This reverts commit d4600e00934b90e5882cf5288f36f98911b51722. * ".git/.scripts/commands/bench/bench.sh" pallet dev pallet_contracts * PR comments update print_schedule * Update frame/contracts/src/benchmarking/mod.rs * Update frame/contracts/src/storage.rs * Update frame/contracts/src/storage.rs * rm temporary fixes * fix extra ; * Update frame/contracts/src/storage.rs Co-authored-by: juangirini * Update frame/contracts/src/storage.rs Co-authored-by: Alexander Theißen * Update frame/contracts/src/lib.rs Co-authored-by: Alexander Theißen * Update frame/contracts/src/lib.rs Co-authored-by: Alexander Theißen * Support stable rust for compiling the runtime (#13580) * Support stable rust for compiling the runtime This pull request brings support for compiling the runtime with stable Rust. This requires at least rust 1.68.0 to work on stable. The code is written in a way that it is backwards compatible and should automatically work when someone compiles with 1.68.0+ stable. * We always support nightlies! * :facepalm: * Sort by version * Review feedback * Review feedback * Fix version parsing * Apply suggestions from code review Co-authored-by: Koute --------- Co-authored-by: Koute * github PR commit fixes * Revert "Support stable rust for compiling the runtime (#13580)" This reverts commit 0b985aa5ad114a42003519b712d25a6acc40b0ad. * Restore DeletionQueueMap * fix namings * PR comment * move comments * Update frame/contracts/src/storage.rs * Update frame/contracts/src/storage.rs * fixes --------- Co-authored-by: command-bot <> Co-authored-by: juangirini Co-authored-by: Alexander Theißen Co-authored-by: Bastian Köcher Co-authored-by: Koute --- bin/node/runtime/src/lib.rs | 9 - frame/contracts/src/benchmarking/mod.rs | 26 +- frame/contracts/src/exec.rs | 2 +- frame/contracts/src/lib.rs | 65 +- frame/contracts/src/storage.rs | 154 +- frame/contracts/src/tests.rs | 153 +- frame/contracts/src/weights.rs | 1885 +++++++++++------------ 7 files changed, 1118 insertions(+), 1176 deletions(-) diff --git a/bin/node/runtime/src/lib.rs b/bin/node/runtime/src/lib.rs index 48bea5ddc..5562dc263 100644 --- a/bin/node/runtime/src/lib.rs +++ b/bin/node/runtime/src/lib.rs @@ -1198,13 +1198,6 @@ impl pallet_tips::Config for Runtime { parameter_types! { pub const DepositPerItem: Balance = deposit(1, 0); pub const DepositPerByte: Balance = deposit(0, 1); - pub const DeletionQueueDepth: u32 = 128; - // The lazy deletion runs inside on_initialize. - pub DeletionWeightLimit: Weight = RuntimeBlockWeights::get() - .per_class - .get(DispatchClass::Normal) - .max_total - .unwrap_or(RuntimeBlockWeights::get().max_block); pub Schedule: pallet_contracts::Schedule = Default::default(); } @@ -1227,8 +1220,6 @@ impl pallet_contracts::Config for Runtime { type WeightPrice = pallet_transaction_payment::Pallet; type WeightInfo = pallet_contracts::weights::SubstrateWeight; type ChainExtension = (); - type DeletionQueueDepth = DeletionQueueDepth; - type DeletionWeightLimit = DeletionWeightLimit; type Schedule = Schedule; type AddressGenerator = pallet_contracts::DefaultAddressGenerator; type MaxCodeLen = ConstU32<{ 123 * 1024 }>; diff --git a/frame/contracts/src/benchmarking/mod.rs b/frame/contracts/src/benchmarking/mod.rs index 5494b57b6..1bb6f3395 100644 --- a/frame/contracts/src/benchmarking/mod.rs +++ b/frame/contracts/src/benchmarking/mod.rs @@ -214,19 +214,7 @@ benchmarks! { on_initialize_per_trie_key { let k in 0..1024; let instance = Contract::::with_storage(WasmModule::dummy(), k, T::Schedule::get().limits.payload_len)?; - instance.info()?.queue_trie_for_deletion()?; - }: { - ContractInfo::::process_deletion_queue_batch(Weight::MAX) - } - - #[pov_mode = Measured] - on_initialize_per_queue_item { - let q in 0..1024.min(T::DeletionQueueDepth::get()); - for i in 0 .. q { - let instance = Contract::::with_index(i, WasmModule::dummy(), vec![])?; - instance.info()?.queue_trie_for_deletion()?; - ContractInfoOf::::remove(instance.account_id); - } + instance.info()?.queue_trie_for_deletion(); }: { ContractInfo::::process_deletion_queue_batch(Weight::MAX) } @@ -3020,16 +3008,12 @@ benchmarks! { print_schedule { #[cfg(feature = "std")] { - let weight_limit = T::DeletionWeightLimit::get(); - let max_queue_depth = T::DeletionQueueDepth::get() as usize; - let empty_queue_throughput = ContractInfo::::deletion_budget(0, weight_limit); - let full_queue_throughput = ContractInfo::::deletion_budget(max_queue_depth, weight_limit); + let max_weight = ::BlockWeights::get().max_block; + let (weight_per_key, key_budget) = ContractInfo::::deletion_budget(max_weight); println!("{:#?}", Schedule::::default()); println!("###############################################"); - println!("Lazy deletion weight per key: {}", empty_queue_throughput.0); - println!("Lazy deletion throughput per block (empty queue, full queue): {}, {}", - empty_queue_throughput.1, full_queue_throughput.1, - ); + println!("Lazy deletion weight per key: {weight_per_key}"); + println!("Lazy deletion throughput per block: {key_budget}"); } #[cfg(not(feature = "std"))] Err("Run this bench with a native runtime in order to see the schedule.")?; diff --git a/frame/contracts/src/exec.rs b/frame/contracts/src/exec.rs index 51ea234f2..03e1c4fd3 100644 --- a/frame/contracts/src/exec.rs +++ b/frame/contracts/src/exec.rs @@ -1204,7 +1204,7 @@ where T::Currency::reducible_balance(&frame.account_id, Expendable, Polite), ExistenceRequirement::AllowDeath, )?; - info.queue_trie_for_deletion()?; + info.queue_trie_for_deletion(); ContractInfoOf::::remove(&frame.account_id); E::remove_user(info.code_hash); Contracts::::deposit_event( diff --git a/frame/contracts/src/lib.rs b/frame/contracts/src/lib.rs index cd4b7daa6..dc93a7f06 100644 --- a/frame/contracts/src/lib.rs +++ b/frame/contracts/src/lib.rs @@ -102,7 +102,7 @@ mod tests; use crate::{ exec::{AccountIdOf, ErrorOrigin, ExecError, Executable, Key, Stack as ExecStack}, gas::GasMeter, - storage::{meter::Meter as StorageMeter, ContractInfo, DeletedContract}, + storage::{meter::Meter as StorageMeter, ContractInfo, DeletionQueueManager}, wasm::{OwnerInfo, PrefabWasmModule, TryInstantiate}, weights::WeightInfo, }; @@ -245,33 +245,6 @@ pub mod pallet { /// memory usage of your runtime. type CallStack: Array>; - /// The maximum number of contracts that can be pending for deletion. - /// - /// When a contract is deleted by calling `seal_terminate` it becomes inaccessible - /// immediately, but the deletion of the storage items it has accumulated is performed - /// later. The contract is put into the deletion queue. This defines how many - /// contracts can be queued up at the same time. If that limit is reached `seal_terminate` - /// will fail. The action must be retried in a later block in that case. - /// - /// The reasons for limiting the queue depth are: - /// - /// 1. The queue is in storage in order to be persistent between blocks. We want to limit - /// the amount of storage that can be consumed. - /// 2. The queue is stored in a vector and needs to be decoded as a whole when reading - /// it at the end of each block. Longer queues take more weight to decode and hence - /// limit the amount of items that can be deleted per block. - #[pallet::constant] - type DeletionQueueDepth: Get; - - /// The maximum amount of weight that can be consumed per block for lazy trie removal. - /// - /// The amount of weight that is dedicated per block to work on the deletion queue. Larger - /// values allow more trie keys to be deleted in each block but reduce the amount of - /// weight that is left for transactions. See [`Self::DeletionQueueDepth`] for more - /// information about the deletion queue. - #[pallet::constant] - type DeletionWeightLimit: Get; - /// The amount of balance a caller has to pay for each byte of storage. /// /// # Note @@ -329,25 +302,6 @@ pub mod pallet { .saturating_add(T::WeightInfo::on_process_deletion_queue_batch()) } - fn on_initialize(_block: T::BlockNumber) -> Weight { - // We want to process the deletion_queue in the on_idle hook. Only in the case - // that the queue length has reached its maximal depth, we process it here. - let max_len = T::DeletionQueueDepth::get() as usize; - let queue_len = >::decode_len().unwrap_or(0); - if queue_len >= max_len { - // We do not want to go above the block limit and rather avoid lazy deletion - // in that case. This should only happen on runtime upgrades. - let weight_limit = T::BlockWeights::get() - .max_block - .saturating_sub(System::::block_weight().total()) - .min(T::DeletionWeightLimit::get()); - ContractInfo::::process_deletion_queue_batch(weight_limit) - .saturating_add(T::WeightInfo::on_process_deletion_queue_batch()) - } else { - T::WeightInfo::on_process_deletion_queue_batch() - } - } - fn integrity_test() { // Total runtime memory is expected to have 128Mb upper limit const MAX_RUNTIME_MEM: u32 = 1024 * 1024 * 128; @@ -860,12 +814,6 @@ pub mod pallet { /// in this error. Note that this usually shouldn't happen as deploying such contracts /// is rejected. NoChainExtension, - /// Removal of a contract failed because the deletion queue is full. - /// - /// This can happen when calling `seal_terminate`. - /// The queue is filled by deleting contracts and emptied by a fixed amount each block. - /// Trying again during another block is the only way to resolve this issue. - DeletionQueueFull, /// A contract with the same AccountId already exists. DuplicateContract, /// A contract self destructed in its constructor. @@ -949,10 +897,15 @@ pub mod pallet { /// Evicted contracts that await child trie deletion. /// /// Child trie deletion is a heavy operation depending on the amount of storage items - /// stored in said trie. Therefore this operation is performed lazily in `on_initialize`. + /// stored in said trie. Therefore this operation is performed lazily in `on_idle`. + #[pallet::storage] + pub(crate) type DeletionQueue = StorageMap<_, Twox64Concat, u32, TrieId>; + + /// A pair of monotonic counters used to track the latest contract marked for deletion + /// and the latest deleted contract in queue. #[pallet::storage] - pub(crate) type DeletionQueue = - StorageValue<_, BoundedVec, ValueQuery>; + pub(crate) type DeletionQueueCounter = + StorageValue<_, DeletionQueueManager, ValueQuery>; } /// Context of a contract invocation. diff --git a/frame/contracts/src/storage.rs b/frame/contracts/src/storage.rs index 19c5f391d..769caef07 100644 --- a/frame/contracts/src/storage.rs +++ b/frame/contracts/src/storage.rs @@ -22,15 +22,15 @@ pub mod meter; use crate::{ exec::{AccountIdOf, Key}, weights::WeightInfo, - AddressGenerator, BalanceOf, CodeHash, Config, ContractInfoOf, DeletionQueue, Error, Pallet, - TrieId, SENTINEL, + AddressGenerator, BalanceOf, CodeHash, Config, ContractInfoOf, DeletionQueue, + DeletionQueueCounter, Error, Pallet, TrieId, SENTINEL, }; use codec::{Decode, Encode, MaxEncodedLen}; use frame_support::{ - dispatch::{DispatchError, DispatchResult}, + dispatch::DispatchError, storage::child::{self, ChildInfo}, weights::Weight, - RuntimeDebugNoBound, + DefaultNoBound, RuntimeDebugNoBound, }; use scale_info::TypeInfo; use sp_io::KillStorageResult; @@ -38,7 +38,7 @@ use sp_runtime::{ traits::{Hash, Saturating, Zero}, RuntimeDebug, }; -use sp_std::{ops::Deref, prelude::*}; +use sp_std::{marker::PhantomData, ops::Deref, prelude::*}; /// Information for managing an account and its sub trie abstraction. /// This is the required info to cache for an account. @@ -204,27 +204,21 @@ impl ContractInfo { /// Push a contract's trie to the deletion queue for lazy removal. /// /// You must make sure that the contract is also removed when queuing the trie for deletion. - pub fn queue_trie_for_deletion(&self) -> DispatchResult { - >::try_append(DeletedContract { trie_id: self.trie_id.clone() }) - .map_err(|_| >::DeletionQueueFull.into()) + pub fn queue_trie_for_deletion(&self) { + DeletionQueueManager::::load().insert(self.trie_id.clone()); } /// Calculates the weight that is necessary to remove one key from the trie and how many - /// of those keys can be deleted from the deletion queue given the supplied queue length - /// and weight limit. - pub fn deletion_budget(queue_len: usize, weight_limit: Weight) -> (Weight, u32) { + /// of those keys can be deleted from the deletion queue given the supplied weight limit. + pub fn deletion_budget(weight_limit: Weight) -> (Weight, u32) { let base_weight = T::WeightInfo::on_process_deletion_queue_batch(); - let weight_per_queue_item = T::WeightInfo::on_initialize_per_queue_item(1) - - T::WeightInfo::on_initialize_per_queue_item(0); let weight_per_key = T::WeightInfo::on_initialize_per_trie_key(1) - T::WeightInfo::on_initialize_per_trie_key(0); - let decoding_weight = weight_per_queue_item.saturating_mul(queue_len as u64); // `weight_per_key` being zero makes no sense and would constitute a failure to // benchmark properly. We opt for not removing any keys at all in this case. let key_budget = weight_limit .saturating_sub(base_weight) - .saturating_sub(decoding_weight) .checked_div_per_component(&weight_per_key) .unwrap_or(0) as u32; @@ -235,13 +229,13 @@ impl ContractInfo { /// /// It returns the amount of weight used for that task. pub fn process_deletion_queue_batch(weight_limit: Weight) -> Weight { - let queue_len = >::decode_len().unwrap_or(0); - if queue_len == 0 { + let mut queue = >::load(); + + if queue.is_empty() { return Weight::zero() } - let (weight_per_key, mut remaining_key_budget) = - Self::deletion_budget(queue_len, weight_limit); + let (weight_per_key, mut remaining_key_budget) = Self::deletion_budget(weight_limit); // We want to check whether we have enough weight to decode the queue before // proceeding. Too little weight for decoding might happen during runtime upgrades @@ -250,30 +244,25 @@ impl ContractInfo { return weight_limit } - let mut queue = >::get(); + while remaining_key_budget > 0 { + let Some(entry) = queue.next() else { break }; - while !queue.is_empty() && remaining_key_budget > 0 { - // Cannot panic due to loop condition - let trie = &mut queue[0]; #[allow(deprecated)] let outcome = child::kill_storage( - &ChildInfo::new_default(&trie.trie_id), + &ChildInfo::new_default(&entry.trie_id), Some(remaining_key_budget), ); - let keys_removed = match outcome { + + match outcome { // This happens when our budget wasn't large enough to remove all keys. - KillStorageResult::SomeRemaining(c) => c, - KillStorageResult::AllRemoved(c) => { - // We do not care to preserve order. The contract is deleted already and - // no one waits for the trie to be deleted. - queue.swap_remove(0); - c + KillStorageResult::SomeRemaining(_) => return weight_limit, + KillStorageResult::AllRemoved(keys_removed) => { + entry.remove(); + remaining_key_budget = remaining_key_budget.saturating_sub(keys_removed); }, }; - remaining_key_budget = remaining_key_budget.saturating_sub(keys_removed); } - >::put(queue); weight_limit.saturating_sub(weight_per_key.saturating_mul(u64::from(remaining_key_budget))) } @@ -281,25 +270,9 @@ impl ContractInfo { pub fn load_code_hash(account: &AccountIdOf) -> Option> { >::get(account).map(|i| i.code_hash) } - - /// Fill up the queue in order to exercise the limits during testing. - #[cfg(test)] - pub fn fill_queue_with_dummies() { - use frame_support::{traits::Get, BoundedVec}; - let queue: Vec = (0..T::DeletionQueueDepth::get()) - .map(|_| DeletedContract { trie_id: TrieId::default() }) - .collect(); - let bounded: BoundedVec<_, _> = queue.try_into().map_err(|_| ()).unwrap(); - >::put(bounded); - } } -#[derive(Encode, Decode, TypeInfo, MaxEncodedLen)] -pub struct DeletedContract { - pub(crate) trie_id: TrieId, -} - -/// Information about what happended to the pre-existing value when calling [`ContractInfo::write`]. +/// Information about what happened to the pre-existing value when calling [`ContractInfo::write`]. #[cfg_attr(test, derive(Debug, PartialEq))] pub enum WriteOutcome { /// No value existed at the specified key. @@ -352,3 +325,84 @@ impl Deref for DepositAccount { &self.0 } } + +/// Manage the removal of contracts storage that are marked for deletion. +/// +/// When a contract is deleted by calling `seal_terminate` it becomes inaccessible +/// immediately, but the deletion of the storage items it has accumulated is performed +/// later by pulling the contract from the queue in the `on_idle` hook. +#[derive(Encode, Decode, TypeInfo, MaxEncodedLen, DefaultNoBound, Clone)] +#[scale_info(skip_type_params(T))] +pub struct DeletionQueueManager { + /// Counter used as a key for inserting a new deleted contract in the queue. + /// The counter is incremented after each insertion. + insert_counter: u32, + /// The index used to read the next element to be deleted in the queue. + /// The counter is incremented after each deletion. + delete_counter: u32, + + _phantom: PhantomData, +} + +/// View on a contract that is marked for deletion. +struct DeletionQueueEntry<'a, T: Config> { + /// the trie id of the contract to delete. + trie_id: TrieId, + + /// A mutable reference on the queue so that the contract can be removed, and none can be added + /// or read in the meantime. + queue: &'a mut DeletionQueueManager, +} + +impl<'a, T: Config> DeletionQueueEntry<'a, T> { + /// Remove the contract from the deletion queue. + fn remove(self) { + >::remove(self.queue.delete_counter); + self.queue.delete_counter = self.queue.delete_counter.wrapping_add(1); + >::set(self.queue.clone()); + } +} + +impl DeletionQueueManager { + /// Load the `DeletionQueueCounter`, so we can perform read or write operations on the + /// DeletionQueue storage. + fn load() -> Self { + >::get() + } + + /// Returns `true` if the queue contains no elements. + fn is_empty(&self) -> bool { + self.insert_counter.wrapping_sub(self.delete_counter) == 0 + } + + /// Insert a contract in the deletion queue. + fn insert(&mut self, trie_id: TrieId) { + >::insert(self.insert_counter, trie_id); + self.insert_counter = self.insert_counter.wrapping_add(1); + >::set(self.clone()); + } + + /// Fetch the next contract to be deleted. + /// + /// Note: + /// we use the delete counter to get the next value to read from the queue and thus don't pay + /// the cost of an extra call to `sp_io::storage::next_key` to lookup the next entry in the map + fn next(&mut self) -> Option> { + if self.is_empty() { + return None + } + + let entry = >::get(self.delete_counter); + entry.map(|trie_id| DeletionQueueEntry { trie_id, queue: self }) + } +} + +#[cfg(test)] +impl DeletionQueueManager { + pub fn from_test_values(insert_counter: u32, delete_counter: u32) -> Self { + Self { insert_counter, delete_counter, _phantom: Default::default() } + } + pub fn as_test_tuple(&self) -> (u32, u32) { + (self.insert_counter, self.delete_counter) + } +} diff --git a/frame/contracts/src/tests.rs b/frame/contracts/src/tests.rs index 146e5fd24..beaec458e 100644 --- a/frame/contracts/src/tests.rs +++ b/frame/contracts/src/tests.rs @@ -22,26 +22,27 @@ use crate::{ Result as ExtensionResult, RetVal, ReturnFlags, SysConfig, }, exec::{Frame, Key}, + storage::DeletionQueueManager, tests::test_utils::{get_contract, get_contract_checked}, wasm::{Determinism, PrefabWasmModule, ReturnCode as RuntimeReturnCode}, weights::WeightInfo, BalanceOf, Code, CodeStorage, Config, ContractInfo, ContractInfoOf, DefaultAddressGenerator, - DeletionQueue, Error, Pallet, Schedule, + DeletionQueueCounter, Error, Pallet, Schedule, }; use assert_matches::assert_matches; use codec::Encode; use frame_support::{ assert_err, assert_err_ignore_postinfo, assert_noop, assert_ok, - dispatch::{DispatchClass, DispatchErrorWithPostInfo, PostDispatchInfo}, + dispatch::{DispatchErrorWithPostInfo, PostDispatchInfo}, parameter_types, storage::child, traits::{ - ConstU32, ConstU64, Contains, Currency, ExistenceRequirement, Get, LockableCurrency, - OnIdle, OnInitialize, WithdrawReasons, + ConstU32, ConstU64, Contains, Currency, ExistenceRequirement, LockableCurrency, OnIdle, + OnInitialize, WithdrawReasons, }, weights::{constants::WEIGHT_REF_TIME_PER_SECOND, Weight}, }; -use frame_system::{self as system, EventRecord, Phase}; +use frame_system::{EventRecord, Phase}; use pretty_assertions::{assert_eq, assert_ne}; use sp_io::hashing::blake2_256; use sp_keystore::{testing::MemoryKeystore, KeystoreExt}; @@ -383,7 +384,6 @@ impl Contains for TestFilter { } parameter_types! { - pub const DeletionWeightLimit: Weight = GAS_LIMIT; pub static UnstableInterface: bool = true; } @@ -399,8 +399,6 @@ impl Config for Test { type WeightInfo = (); type ChainExtension = (TestExtension, DisabledExtension, RevertingExtension, TempStorageExtension); - type DeletionQueueDepth = ConstU32<1024>; - type DeletionWeightLimit = DeletionWeightLimit; type Schedule = MySchedule; type DepositPerByte = DepositPerByte; type DepositPerItem = DepositPerItem; @@ -1972,25 +1970,6 @@ fn lazy_removal_works() { }); } -#[test] -fn lazy_removal_on_full_queue_works_on_initialize() { - ExtBuilder::default().existential_deposit(50).build().execute_with(|| { - // Fill the deletion queue with dummy values, so that on_initialize attempts - // to clear the queue - ContractInfo::::fill_queue_with_dummies(); - - let queue_len_initial = >::decode_len().unwrap_or(0); - - // Run the lazy removal - Contracts::on_initialize(System::block_number()); - - let queue_len_after_on_initialize = >::decode_len().unwrap_or(0); - - // Queue length should be decreased after call of on_initialize() - assert!(queue_len_initial - queue_len_after_on_initialize > 0); - }); -} - #[test] fn lazy_batch_removal_works() { let (code, _hash) = compile_module::("self_destruct").unwrap(); @@ -2054,7 +2033,7 @@ fn lazy_removal_partial_remove_works() { // We create a contract with some extra keys above the weight limit let extra_keys = 7u32; let weight_limit = Weight::from_parts(5_000_000_000, 0); - let (_, max_keys) = ContractInfo::::deletion_budget(1, weight_limit); + let (_, max_keys) = ContractInfo::::deletion_budget(weight_limit); let vals: Vec<_> = (0..max_keys + extra_keys) .map(|i| (blake2_256(&i.encode()), (i as u32), (i as u32).encode())) .collect(); @@ -2139,33 +2118,6 @@ fn lazy_removal_partial_remove_works() { }); } -#[test] -fn lazy_removal_does_no_run_on_full_queue_and_full_block() { - ExtBuilder::default().existential_deposit(50).build().execute_with(|| { - // Fill up the block which should prevent the lazy storage removal from running. - System::register_extra_weight_unchecked( - ::BlockWeights::get().max_block, - DispatchClass::Mandatory, - ); - - // Fill the deletion queue with dummy values, so that on_initialize attempts - // to clear the queue - ContractInfo::::fill_queue_with_dummies(); - - // Check that on_initialize() tries to perform lazy removal but removes nothing - // as no more weight is left for that. - let weight_used = Contracts::on_initialize(System::block_number()); - let base = <::WeightInfo as WeightInfo>::on_process_deletion_queue_batch(); - assert_eq!(weight_used, base); - - // Check that the deletion queue is still full after execution of the - // on_initialize() hook. - let max_len: u32 = ::DeletionQueueDepth::get(); - let queue_len: u32 = >::decode_len().unwrap_or(0).try_into().unwrap(); - assert_eq!(max_len, queue_len); - }); -} - #[test] fn lazy_removal_does_no_run_on_low_remaining_weight() { let (code, _hash) = compile_module::("self_destruct").unwrap(); @@ -2209,7 +2161,7 @@ fn lazy_removal_does_no_run_on_low_remaining_weight() { // But value should be still there as the lazy removal did not run, yet. assert_matches!(child::get(trie, &[99]), Some(42)); - // Assign a remaining weight which is too low for a successfull deletion of the contract + // Assign a remaining weight which is too low for a successful deletion of the contract let low_remaining_weight = <::WeightInfo as WeightInfo>::on_process_deletion_queue_batch(); @@ -2259,7 +2211,7 @@ fn lazy_removal_does_not_use_all_weight() { .account_id; let info = get_contract(&addr); - let (weight_per_key, max_keys) = ContractInfo::::deletion_budget(1, weight_limit); + let (weight_per_key, max_keys) = ContractInfo::::deletion_budget(weight_limit); // We create a contract with one less storage item than we can remove within the limit let vals: Vec<_> = (0..max_keys - 1) @@ -2314,40 +2266,75 @@ fn lazy_removal_does_not_use_all_weight() { } #[test] -fn deletion_queue_full() { +fn deletion_queue_ring_buffer_overflow() { let (code, _hash) = compile_module::("self_destruct").unwrap(); - ExtBuilder::default().existential_deposit(50).build().execute_with(|| { + let mut ext = ExtBuilder::default().existential_deposit(50).build(); + + // setup the deletion queue with custom counters + ext.execute_with(|| { + let queue = DeletionQueueManager::from_test_values(u32::MAX - 1, u32::MAX - 1); + >::set(queue); + }); + + // commit the changes to the storage + ext.commit_all().unwrap(); + + ext.execute_with(|| { let min_balance = ::Currency::minimum_balance(); let _ = Balances::deposit_creating(&ALICE, 1000 * min_balance); + let mut tries: Vec = vec![]; - let addr = Contracts::bare_instantiate( - ALICE, - min_balance * 100, - GAS_LIMIT, - None, - Code::Upload(code), - vec![], - vec![], - false, - ) - .result - .unwrap() - .account_id; + // add 3 contracts to the deletion queue + for i in 0..3u8 { + let addr = Contracts::bare_instantiate( + ALICE, + min_balance * 100, + GAS_LIMIT, + None, + Code::Upload(code.clone()), + vec![], + vec![i], + false, + ) + .result + .unwrap() + .account_id; - // fill the deletion queue up until its limit - ContractInfo::::fill_queue_with_dummies(); + let info = get_contract(&addr); + let trie = &info.child_trie_info(); - // Terminate the contract should fail - assert_err_ignore_postinfo!( - Contracts::call(RuntimeOrigin::signed(ALICE), addr.clone(), 0, GAS_LIMIT, None, vec![],), - Error::::DeletionQueueFull, - ); + // Put value into the contracts child trie + child::put(trie, &[99], &42); - // Contract should exist because removal failed - get_contract(&addr); - }); -} + // Terminate the contract. Contract info should be gone, but value should be still + // there as the lazy removal did not run, yet. + assert_ok!(Contracts::call( + RuntimeOrigin::signed(ALICE), + addr.clone(), + 0, + GAS_LIMIT, + None, + vec![] + )); + + assert!(!>::contains_key(&addr)); + assert_matches!(child::get(trie, &[99]), Some(42)); + + tries.push(trie.clone()) + } + + // Run single lazy removal + Contracts::on_idle(System::block_number(), Weight::MAX); + // The single lazy removal should have removed all queued tries + for trie in tries.iter() { + assert_matches!(child::get::(trie, &[99]), None); + } + + // insert and delete counter values should go from u32::MAX - 1 to 1 + assert_eq!(>::get().as_test_tuple(), (1, 1)); + }) +} #[test] fn refcounter() { let (wasm, code_hash) = compile_module::("self_destruct").unwrap(); diff --git a/frame/contracts/src/weights.rs b/frame/contracts/src/weights.rs index 9e37a238b..22a24aa27 100644 --- a/frame/contracts/src/weights.rs +++ b/frame/contracts/src/weights.rs @@ -18,7 +18,7 @@ //! Autogenerated weights for pallet_contracts //! //! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev -//! DATE: 2023-03-23, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! DATE: 2023-03-28, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` //! WORST CASE MAP SIZE: `1000000` //! HOSTNAME: `bm3`, CPU: `Intel(R) Core(TM) i7-7700K CPU @ 4.20GHz` //! EXECUTION: Some(Wasm), WASM-EXECUTION: Compiled, CHAIN: Some("dev"), DB CACHE: 1024 @@ -51,7 +51,6 @@ use sp_std::marker::PhantomData; pub trait WeightInfo { fn on_process_deletion_queue_batch() -> Weight; fn on_initialize_per_trie_key(k: u32, ) -> Weight; - fn on_initialize_per_queue_item(q: u32, ) -> Weight; fn reinstrument(c: u32, ) -> Weight; fn call_with_code_per_byte(c: u32, ) -> Weight; fn instantiate_with_code(c: u32, i: u32, s: u32, ) -> Weight; @@ -171,14 +170,14 @@ pub trait WeightInfo { /// Weights for pallet_contracts using the Substrate node and recommended hardware. pub struct SubstrateWeight(PhantomData); impl WeightInfo for SubstrateWeight { - /// Storage: Contracts DeletionQueue (r:1 w:0) - /// Proof: Contracts DeletionQueue (max_values: Some(1), max_size: Some(16642), added: 17137, mode: Measured) + /// Storage: Contracts DeletionQueueCounter (r:1 w:0) + /// Proof: Contracts DeletionQueueCounter (max_values: Some(1), max_size: Some(8), added: 503, mode: Measured) fn on_process_deletion_queue_batch() -> Weight { // Proof Size summary in bytes: // Measured: `109` // Estimated: `1594` - // Minimum execution time: 2_713_000 picoseconds. - Weight::from_parts(2_811_000, 1594) + // Minimum execution time: 2_677_000 picoseconds. + Weight::from_parts(2_899_000, 1594) .saturating_add(T::DbWeight::get().reads(1_u64)) } /// Storage: Skipped Metadata (r:0 w:0) @@ -186,33 +185,18 @@ impl WeightInfo for SubstrateWeight { /// The range of component `k` is `[0, 1024]`. fn on_initialize_per_trie_key(k: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `450 + k * (69 ±0)` - // Estimated: `440 + k * (70 ±0)` - // Minimum execution time: 11_011_000 picoseconds. - Weight::from_parts(7_025_244, 440) - // Standard Error: 1_217 - .saturating_add(Weight::from_parts(980_818, 0).saturating_mul(k.into())) - .saturating_add(T::DbWeight::get().reads(1_u64)) + // Measured: `488 + k * (69 ±0)` + // Estimated: `478 + k * (70 ±0)` + // Minimum execution time: 14_006_000 picoseconds. + Weight::from_parts(8_735_946, 478) + // Standard Error: 1_370 + .saturating_add(Weight::from_parts(989_501, 0).saturating_mul(k.into())) + .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().reads((1_u64).saturating_mul(k.into()))) - .saturating_add(T::DbWeight::get().writes(1_u64)) + .saturating_add(T::DbWeight::get().writes(2_u64)) .saturating_add(T::DbWeight::get().writes((1_u64).saturating_mul(k.into()))) .saturating_add(Weight::from_parts(0, 70).saturating_mul(k.into())) } - /// Storage: Contracts DeletionQueue (r:1 w:1) - /// Proof: Contracts DeletionQueue (max_values: Some(1), max_size: Some(16642), added: 17137, mode: Measured) - /// The range of component `q` is `[0, 128]`. - fn on_initialize_per_queue_item(q: u32, ) -> Weight { - // Proof Size summary in bytes: - // Measured: `250 + q * (33 ±0)` - // Estimated: `1725 + q * (33 ±0)` - // Minimum execution time: 2_802_000 picoseconds. - Weight::from_parts(10_768_336, 1725) - // Standard Error: 3_424 - .saturating_add(Weight::from_parts(1_323_649, 0).saturating_mul(q.into())) - .saturating_add(T::DbWeight::get().reads(1_u64)) - .saturating_add(T::DbWeight::get().writes(1_u64)) - .saturating_add(Weight::from_parts(0, 33).saturating_mul(q.into())) - } /// Storage: Contracts PristineCode (r:1 w:0) /// Proof: Contracts PristineCode (max_values: None, max_size: Some(125988), added: 128463, mode: Measured) /// Storage: Contracts CodeStorage (r:0 w:1) @@ -222,10 +206,10 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `238 + c * (1 ±0)` // Estimated: `3951 + c * (2 ±0)` - // Minimum execution time: 30_299_000 picoseconds. - Weight::from_parts(24_608_986, 3951) - // Standard Error: 75 - .saturating_add(Weight::from_parts(54_619, 0).saturating_mul(c.into())) + // Minimum execution time: 30_951_000 picoseconds. + Weight::from_parts(25_988_560, 3951) + // Standard Error: 57 + .saturating_add(Weight::from_parts(54_692, 0).saturating_mul(c.into())) .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) .saturating_add(Weight::from_parts(0, 2).saturating_mul(c.into())) @@ -245,10 +229,10 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `707` // Estimated: `21400 + c * (5 ±0)` - // Minimum execution time: 265_835_000 picoseconds. - Weight::from_parts(275_985_164, 21400) - // Standard Error: 36 - .saturating_add(Weight::from_parts(37_980, 0).saturating_mul(c.into())) + // Minimum execution time: 263_107_000 picoseconds. + Weight::from_parts(268_289_665, 21400) + // Standard Error: 33 + .saturating_add(Weight::from_parts(38_534, 0).saturating_mul(c.into())) .saturating_add(T::DbWeight::get().reads(6_u64)) .saturating_add(T::DbWeight::get().writes(4_u64)) .saturating_add(Weight::from_parts(0, 5).saturating_mul(c.into())) @@ -276,14 +260,14 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `270` // Estimated: `26207` - // Minimum execution time: 3_124_508_000 picoseconds. - Weight::from_parts(617_467_897, 26207) - // Standard Error: 293 - .saturating_add(Weight::from_parts(106_971, 0).saturating_mul(c.into())) - // Standard Error: 17 - .saturating_add(Weight::from_parts(1_156, 0).saturating_mul(i.into())) - // Standard Error: 17 - .saturating_add(Weight::from_parts(1_395, 0).saturating_mul(s.into())) + // Minimum execution time: 3_122_319_000 picoseconds. + Weight::from_parts(487_802_180, 26207) + // Standard Error: 345 + .saturating_add(Weight::from_parts(108_237, 0).saturating_mul(c.into())) + // Standard Error: 20 + .saturating_add(Weight::from_parts(1_166, 0).saturating_mul(i.into())) + // Standard Error: 20 + .saturating_add(Weight::from_parts(1_505, 0).saturating_mul(s.into())) .saturating_add(T::DbWeight::get().reads(9_u64)) .saturating_add(T::DbWeight::get().writes(10_u64)) } @@ -307,12 +291,12 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `482` // Estimated: `28521` - // Minimum execution time: 1_649_483_000 picoseconds. - Weight::from_parts(287_642_416, 28521) + // Minimum execution time: 1_650_623_000 picoseconds. + Weight::from_parts(286_494_456, 28521) // Standard Error: 8 - .saturating_add(Weight::from_parts(1_450, 0).saturating_mul(i.into())) + .saturating_add(Weight::from_parts(1_438, 0).saturating_mul(i.into())) // Standard Error: 8 - .saturating_add(Weight::from_parts(1_443, 0).saturating_mul(s.into())) + .saturating_add(Weight::from_parts(1_453, 0).saturating_mul(s.into())) .saturating_add(T::DbWeight::get().reads(9_u64)) .saturating_add(T::DbWeight::get().writes(7_u64)) } @@ -330,8 +314,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `759` // Estimated: `21615` - // Minimum execution time: 192_302_000 picoseconds. - Weight::from_parts(193_192_000, 21615) + // Minimum execution time: 191_046_000 picoseconds. + Weight::from_parts(192_544_000, 21615) .saturating_add(T::DbWeight::get().reads(6_u64)) .saturating_add(T::DbWeight::get().writes(4_u64)) } @@ -348,10 +332,10 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `109` // Estimated: `7366` - // Minimum execution time: 246_401_000 picoseconds. - Weight::from_parts(261_505_456, 7366) - // Standard Error: 83 - .saturating_add(Weight::from_parts(109_136, 0).saturating_mul(c.into())) + // Minimum execution time: 244_260_000 picoseconds. + Weight::from_parts(254_693_985, 7366) + // Standard Error: 80 + .saturating_add(Weight::from_parts(108_246, 0).saturating_mul(c.into())) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().writes(4_u64)) } @@ -367,8 +351,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `255` // Estimated: `7950` - // Minimum execution time: 33_913_000 picoseconds. - Weight::from_parts(34_186_000, 7950) + // Minimum execution time: 33_492_000 picoseconds. + Weight::from_parts(34_079_000, 7950) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().writes(4_u64)) } @@ -382,8 +366,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `570` // Estimated: `19530` - // Minimum execution time: 33_801_000 picoseconds. - Weight::from_parts(34_877_000, 19530) + // Minimum execution time: 33_475_000 picoseconds. + Weight::from_parts(33_856_000, 19530) .saturating_add(T::DbWeight::get().reads(6_u64)) .saturating_add(T::DbWeight::get().writes(6_u64)) } @@ -402,10 +386,10 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `781 + r * (6 ±0)` // Estimated: `21730 + r * (30 ±0)` - // Minimum execution time: 237_679_000 picoseconds. - Weight::from_parts(243_022_905, 21730) - // Standard Error: 940 - .saturating_add(Weight::from_parts(324_389, 0).saturating_mul(r.into())) + // Minimum execution time: 234_197_000 picoseconds. + Weight::from_parts(236_830_305, 21730) + // Standard Error: 818 + .saturating_add(Weight::from_parts(336_446, 0).saturating_mul(r.into())) .saturating_add(T::DbWeight::get().reads(6_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) .saturating_add(Weight::from_parts(0, 30).saturating_mul(r.into())) @@ -425,10 +409,10 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `839 + r * (240 ±0)` // Estimated: `21835 + r * (3675 ±0)` - // Minimum execution time: 235_635_000 picoseconds. - Weight::from_parts(76_942_144, 21835) - // Standard Error: 6_214 - .saturating_add(Weight::from_parts(3_328_756, 0).saturating_mul(r.into())) + // Minimum execution time: 235_872_000 picoseconds. + Weight::from_parts(78_877_890, 21835) + // Standard Error: 6_405 + .saturating_add(Weight::from_parts(3_358_341, 0).saturating_mul(r.into())) .saturating_add(T::DbWeight::get().reads(6_u64)) .saturating_add(T::DbWeight::get().reads((1_u64).saturating_mul(r.into()))) .saturating_add(T::DbWeight::get().writes(3_u64)) @@ -449,10 +433,10 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `831 + r * (244 ±0)` // Estimated: `21855 + r * (3695 ±0)` - // Minimum execution time: 237_123_000 picoseconds. - Weight::from_parts(77_880_739, 21855) - // Standard Error: 5_970 - .saturating_add(Weight::from_parts(4_103_281, 0).saturating_mul(r.into())) + // Minimum execution time: 239_035_000 picoseconds. + Weight::from_parts(93_255_085, 21855) + // Standard Error: 5_922 + .saturating_add(Weight::from_parts(4_144_910, 0).saturating_mul(r.into())) .saturating_add(T::DbWeight::get().reads(6_u64)) .saturating_add(T::DbWeight::get().reads((1_u64).saturating_mul(r.into()))) .saturating_add(T::DbWeight::get().writes(3_u64)) @@ -473,10 +457,10 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `788 + r * (6 ±0)` // Estimated: `21770 + r * (30 ±0)` - // Minimum execution time: 236_621_000 picoseconds. - Weight::from_parts(238_240_015, 21770) - // Standard Error: 742 - .saturating_add(Weight::from_parts(404_691, 0).saturating_mul(r.into())) + // Minimum execution time: 236_507_000 picoseconds. + Weight::from_parts(238_253_211, 21770) + // Standard Error: 975 + .saturating_add(Weight::from_parts(413_919, 0).saturating_mul(r.into())) .saturating_add(T::DbWeight::get().reads(6_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) .saturating_add(Weight::from_parts(0, 30).saturating_mul(r.into())) @@ -496,10 +480,10 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `778 + r * (3 ±0)` // Estimated: `21735 + r * (15 ±0)` - // Minimum execution time: 233_274_000 picoseconds. - Weight::from_parts(239_596_227, 21735) - // Standard Error: 551 - .saturating_add(Weight::from_parts(164_429, 0).saturating_mul(r.into())) + // Minimum execution time: 235_521_000 picoseconds. + Weight::from_parts(238_397_362, 21735) + // Standard Error: 452 + .saturating_add(Weight::from_parts(160_860, 0).saturating_mul(r.into())) .saturating_add(T::DbWeight::get().reads(6_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) .saturating_add(Weight::from_parts(0, 15).saturating_mul(r.into())) @@ -519,10 +503,10 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `782 + r * (6 ±0)` // Estimated: `21740 + r * (30 ±0)` - // Minimum execution time: 235_661_000 picoseconds. - Weight::from_parts(239_063_406, 21740) - // Standard Error: 933 - .saturating_add(Weight::from_parts(327_679, 0).saturating_mul(r.into())) + // Minimum execution time: 234_722_000 picoseconds. + Weight::from_parts(242_936_096, 21740) + // Standard Error: 1_178 + .saturating_add(Weight::from_parts(329_075, 0).saturating_mul(r.into())) .saturating_add(T::DbWeight::get().reads(6_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) .saturating_add(Weight::from_parts(0, 30).saturating_mul(r.into())) @@ -542,10 +526,10 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `783 + r * (6 ±0)` // Estimated: `21725 + r * (30 ±0)` - // Minimum execution time: 235_583_000 picoseconds. - Weight::from_parts(251_641_549, 21725) - // Standard Error: 1_104 - .saturating_add(Weight::from_parts(315_873, 0).saturating_mul(r.into())) + // Minimum execution time: 235_654_000 picoseconds. + Weight::from_parts(245_887_792, 21725) + // Standard Error: 903 + .saturating_add(Weight::from_parts(325_168, 0).saturating_mul(r.into())) .saturating_add(T::DbWeight::get().reads(6_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) .saturating_add(Weight::from_parts(0, 30).saturating_mul(r.into())) @@ -565,10 +549,10 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `922 + r * (6 ±0)` // Estimated: `24633 + r * (30 ±0)` - // Minimum execution time: 235_325_000 picoseconds. - Weight::from_parts(256_582_010, 24633) - // Standard Error: 1_349 - .saturating_add(Weight::from_parts(1_483_116, 0).saturating_mul(r.into())) + // Minimum execution time: 233_599_000 picoseconds. + Weight::from_parts(251_561_602, 24633) + // Standard Error: 3_348 + .saturating_add(Weight::from_parts(1_475_443, 0).saturating_mul(r.into())) .saturating_add(T::DbWeight::get().reads(7_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) .saturating_add(Weight::from_parts(0, 30).saturating_mul(r.into())) @@ -588,10 +572,10 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `792 + r * (6 ±0)` // Estimated: `21825 + r * (30 ±0)` - // Minimum execution time: 235_358_000 picoseconds. - Weight::from_parts(233_421_484, 21825) - // Standard Error: 1_178 - .saturating_add(Weight::from_parts(337_947, 0).saturating_mul(r.into())) + // Minimum execution time: 235_087_000 picoseconds. + Weight::from_parts(235_855_322, 21825) + // Standard Error: 867 + .saturating_add(Weight::from_parts(346_707, 0).saturating_mul(r.into())) .saturating_add(T::DbWeight::get().reads(6_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) .saturating_add(Weight::from_parts(0, 30).saturating_mul(r.into())) @@ -611,10 +595,10 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `790 + r * (6 ±0)` // Estimated: `21815 + r * (30 ±0)` - // Minimum execution time: 236_570_000 picoseconds. - Weight::from_parts(245_853_078, 21815) - // Standard Error: 1_947 - .saturating_add(Weight::from_parts(319_972, 0).saturating_mul(r.into())) + // Minimum execution time: 237_103_000 picoseconds. + Weight::from_parts(239_272_188, 21815) + // Standard Error: 892 + .saturating_add(Weight::from_parts(328_334, 0).saturating_mul(r.into())) .saturating_add(T::DbWeight::get().reads(6_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) .saturating_add(Weight::from_parts(0, 30).saturating_mul(r.into())) @@ -634,10 +618,10 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `787 + r * (6 ±0)` // Estimated: `21805 + r * (30 ±0)` - // Minimum execution time: 235_027_000 picoseconds. - Weight::from_parts(239_719_689, 21805) - // Standard Error: 654 - .saturating_add(Weight::from_parts(326_988, 0).saturating_mul(r.into())) + // Minimum execution time: 234_761_000 picoseconds. + Weight::from_parts(238_601_784, 21805) + // Standard Error: 725 + .saturating_add(Weight::from_parts(325_758, 0).saturating_mul(r.into())) .saturating_add(T::DbWeight::get().reads(6_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) .saturating_add(Weight::from_parts(0, 30).saturating_mul(r.into())) @@ -657,10 +641,10 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `778 + r * (6 ±0)` // Estimated: `21735 + r * (30 ±0)` - // Minimum execution time: 236_547_000 picoseconds. - Weight::from_parts(239_390_326, 21735) - // Standard Error: 912 - .saturating_add(Weight::from_parts(327_495, 0).saturating_mul(r.into())) + // Minimum execution time: 235_249_000 picoseconds. + Weight::from_parts(239_861_242, 21735) + // Standard Error: 751 + .saturating_add(Weight::from_parts(325_795, 0).saturating_mul(r.into())) .saturating_add(T::DbWeight::get().reads(6_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) .saturating_add(Weight::from_parts(0, 30).saturating_mul(r.into())) @@ -682,10 +666,10 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `856 + r * (10 ±0)` // Estimated: `24446 + r * (60 ±0)` - // Minimum execution time: 235_710_000 picoseconds. - Weight::from_parts(238_998_789, 24446) - // Standard Error: 2_055 - .saturating_add(Weight::from_parts(1_373_992, 0).saturating_mul(r.into())) + // Minimum execution time: 234_912_000 picoseconds. + Weight::from_parts(254_783_734, 24446) + // Standard Error: 1_610 + .saturating_add(Weight::from_parts(1_307_506, 0).saturating_mul(r.into())) .saturating_add(T::DbWeight::get().reads(7_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) .saturating_add(Weight::from_parts(0, 60).saturating_mul(r.into())) @@ -705,10 +689,10 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `745 + r * (4 ±0)` // Estimated: `21555 + r * (20 ±0)` - // Minimum execution time: 161_133_000 picoseconds. - Weight::from_parts(167_097_346, 21555) - // Standard Error: 245 - .saturating_add(Weight::from_parts(128_503, 0).saturating_mul(r.into())) + // Minimum execution time: 160_125_000 picoseconds. + Weight::from_parts(164_915_574, 21555) + // Standard Error: 332 + .saturating_add(Weight::from_parts(132_326, 0).saturating_mul(r.into())) .saturating_add(T::DbWeight::get().reads(6_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) .saturating_add(Weight::from_parts(0, 20).saturating_mul(r.into())) @@ -728,10 +712,10 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `780 + r * (6 ±0)` // Estimated: `21740 + r * (30 ±0)` - // Minimum execution time: 234_790_000 picoseconds. - Weight::from_parts(242_392_710, 21740) - // Standard Error: 2_506 - .saturating_add(Weight::from_parts(273_470, 0).saturating_mul(r.into())) + // Minimum execution time: 234_717_000 picoseconds. + Weight::from_parts(238_540_521, 21740) + // Standard Error: 599 + .saturating_add(Weight::from_parts(277_303, 0).saturating_mul(r.into())) .saturating_add(T::DbWeight::get().reads(6_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) .saturating_add(Weight::from_parts(0, 30).saturating_mul(r.into())) @@ -751,10 +735,10 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `784` // Estimated: `21740` - // Minimum execution time: 236_837_000 picoseconds. - Weight::from_parts(237_860_073, 21740) - // Standard Error: 2 - .saturating_add(Weight::from_parts(602, 0).saturating_mul(n.into())) + // Minimum execution time: 235_792_000 picoseconds. + Weight::from_parts(244_114_692, 21740) + // Standard Error: 1 + .saturating_add(Weight::from_parts(589, 0).saturating_mul(n.into())) .saturating_add(T::DbWeight::get().reads(6_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) } @@ -773,10 +757,10 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `768 + r * (45 ±0)` // Estimated: `21660 + r * (225 ±0)` - // Minimum execution time: 232_047_000 picoseconds. - Weight::from_parts(234_629_293, 21660) - // Standard Error: 171_808 - .saturating_add(Weight::from_parts(663_306, 0).saturating_mul(r.into())) + // Minimum execution time: 231_166_000 picoseconds. + Weight::from_parts(233_339_177, 21660) + // Standard Error: 155_889 + .saturating_add(Weight::from_parts(3_124_322, 0).saturating_mul(r.into())) .saturating_add(T::DbWeight::get().reads(6_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) .saturating_add(Weight::from_parts(0, 225).saturating_mul(r.into())) @@ -796,10 +780,10 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `778` // Estimated: `21775` - // Minimum execution time: 235_035_000 picoseconds. - Weight::from_parts(234_442_091, 21775) + // Minimum execution time: 235_721_000 picoseconds. + Weight::from_parts(237_413_703, 21775) // Standard Error: 1 - .saturating_add(Weight::from_parts(185, 0).saturating_mul(n.into())) + .saturating_add(Weight::from_parts(177, 0).saturating_mul(n.into())) .saturating_add(T::DbWeight::get().reads(6_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) } @@ -811,26 +795,28 @@ impl WeightInfo for SubstrateWeight { /// Proof: Contracts CodeStorage (max_values: None, max_size: Some(126001), added: 128476, mode: Measured) /// Storage: Timestamp Now (r:1 w:0) /// Proof: Timestamp Now (max_values: Some(1), max_size: Some(8), added: 503, mode: Measured) - /// Storage: Contracts DeletionQueue (r:1 w:1) - /// Proof: Contracts DeletionQueue (max_values: Some(1), max_size: Some(16642), added: 17137, mode: Measured) + /// Storage: Contracts DeletionQueueCounter (r:1 w:1) + /// Proof: Contracts DeletionQueueCounter (max_values: Some(1), max_size: Some(8), added: 503, mode: Measured) /// Storage: Contracts OwnerInfoOf (r:1 w:1) /// Proof: Contracts OwnerInfoOf (max_values: None, max_size: Some(88), added: 2563, mode: Measured) /// Storage: System EventTopics (r:3 w:3) /// Proof Skipped: System EventTopics (max_values: None, max_size: None, mode: Measured) + /// Storage: Contracts DeletionQueue (r:0 w:1) + /// Proof: Contracts DeletionQueue (max_values: None, max_size: Some(142), added: 2617, mode: Measured) /// The range of component `r` is `[0, 1]`. fn seal_terminate(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `810 + r * (356 ±0)` - // Estimated: `25511 + r * (15321 ±0)` - // Minimum execution time: 234_140_000 picoseconds. - Weight::from_parts(236_805_906, 25511) - // Standard Error: 435_181 - .saturating_add(Weight::from_parts(118_144_693, 0).saturating_mul(r.into())) + // Estimated: `26094 + r * (15904 ±0)` + // Minimum execution time: 233_525_000 picoseconds. + Weight::from_parts(235_871_034, 26094) + // Standard Error: 235_338 + .saturating_add(Weight::from_parts(118_659_865, 0).saturating_mul(r.into())) .saturating_add(T::DbWeight::get().reads(6_u64)) .saturating_add(T::DbWeight::get().reads((6_u64).saturating_mul(r.into()))) .saturating_add(T::DbWeight::get().writes(3_u64)) - .saturating_add(T::DbWeight::get().writes((7_u64).saturating_mul(r.into()))) - .saturating_add(Weight::from_parts(0, 15321).saturating_mul(r.into())) + .saturating_add(T::DbWeight::get().writes((8_u64).saturating_mul(r.into()))) + .saturating_add(Weight::from_parts(0, 15904).saturating_mul(r.into())) } /// Storage: System Account (r:1 w:0) /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: Measured) @@ -849,10 +835,10 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `825 + r * (10 ±0)` // Estimated: `24283 + r * (60 ±0)` - // Minimum execution time: 235_271_000 picoseconds. - Weight::from_parts(256_019_682, 24283) - // Standard Error: 2_016 - .saturating_add(Weight::from_parts(1_862_085, 0).saturating_mul(r.into())) + // Minimum execution time: 235_007_000 picoseconds. + Weight::from_parts(248_419_686, 24283) + // Standard Error: 1_847 + .saturating_add(Weight::from_parts(1_815_822, 0).saturating_mul(r.into())) .saturating_add(T::DbWeight::get().reads(7_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) .saturating_add(Weight::from_parts(0, 60).saturating_mul(r.into())) @@ -872,10 +858,10 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `778 + r * (10 ±0)` // Estimated: `21735 + r * (50 ±0)` - // Minimum execution time: 233_092_000 picoseconds. - Weight::from_parts(248_483_473, 21735) - // Standard Error: 2_182 - .saturating_add(Weight::from_parts(3_551_674, 0).saturating_mul(r.into())) + // Minimum execution time: 232_912_000 picoseconds. + Weight::from_parts(256_142_885, 21735) + // Standard Error: 2_741 + .saturating_add(Weight::from_parts(3_542_482, 0).saturating_mul(r.into())) .saturating_add(T::DbWeight::get().reads(6_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) .saturating_add(Weight::from_parts(0, 50).saturating_mul(r.into())) @@ -896,12 +882,12 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `797 + t * (32 ±0)` // Estimated: `21840 + t * (2640 ±0)` - // Minimum execution time: 252_307_000 picoseconds. - Weight::from_parts(245_237_726, 21840) - // Standard Error: 79_824 - .saturating_add(Weight::from_parts(2_364_618, 0).saturating_mul(t.into())) - // Standard Error: 22 - .saturating_add(Weight::from_parts(636, 0).saturating_mul(n.into())) + // Minimum execution time: 251_018_000 picoseconds. + Weight::from_parts(245_280_765, 21840) + // Standard Error: 90_317 + .saturating_add(Weight::from_parts(2_434_496, 0).saturating_mul(t.into())) + // Standard Error: 25 + .saturating_add(Weight::from_parts(599, 0).saturating_mul(n.into())) .saturating_add(T::DbWeight::get().reads(6_u64)) .saturating_add(T::DbWeight::get().reads((1_u64).saturating_mul(t.into()))) .saturating_add(T::DbWeight::get().writes(3_u64)) @@ -923,10 +909,10 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `777 + r * (7 ±0)` // Estimated: `21725 + r * (35 ±0)` - // Minimum execution time: 165_367_000 picoseconds. - Weight::from_parts(170_164_725, 21725) - // Standard Error: 487 - .saturating_add(Weight::from_parts(237_281, 0).saturating_mul(r.into())) + // Minimum execution time: 164_022_000 picoseconds. + Weight::from_parts(168_658_387, 21725) + // Standard Error: 685 + .saturating_add(Weight::from_parts(238_133, 0).saturating_mul(r.into())) .saturating_add(T::DbWeight::get().reads(6_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) .saturating_add(Weight::from_parts(0, 35).saturating_mul(r.into())) @@ -946,10 +932,10 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `125728` // Estimated: `269977` - // Minimum execution time: 350_914_000 picoseconds. - Weight::from_parts(354_461_646, 269977) - // Standard Error: 1 - .saturating_add(Weight::from_parts(747, 0).saturating_mul(i.into())) + // Minimum execution time: 351_043_000 picoseconds. + Weight::from_parts(353_707_344, 269977) + // Standard Error: 3 + .saturating_add(Weight::from_parts(752, 0).saturating_mul(i.into())) .saturating_add(T::DbWeight::get().reads(6_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) } @@ -960,10 +946,10 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `845 + r * (292 ±0)` // Estimated: `843 + r * (293 ±0)` - // Minimum execution time: 236_273_000 picoseconds. - Weight::from_parts(137_922_946, 843) - // Standard Error: 10_363 - .saturating_add(Weight::from_parts(6_034_776, 0).saturating_mul(r.into())) + // Minimum execution time: 235_854_000 picoseconds. + Weight::from_parts(133_986_225, 843) + // Standard Error: 9_550 + .saturating_add(Weight::from_parts(6_093_051, 0).saturating_mul(r.into())) .saturating_add(T::DbWeight::get().reads(6_u64)) .saturating_add(T::DbWeight::get().reads((1_u64).saturating_mul(r.into()))) .saturating_add(T::DbWeight::get().writes(3_u64)) @@ -977,10 +963,10 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `1304` // Estimated: `1280` - // Minimum execution time: 251_462_000 picoseconds. - Weight::from_parts(287_009_907, 1280) - // Standard Error: 62 - .saturating_add(Weight::from_parts(384, 0).saturating_mul(n.into())) + // Minimum execution time: 252_321_000 picoseconds. + Weight::from_parts(285_820_577, 1280) + // Standard Error: 60 + .saturating_add(Weight::from_parts(600, 0).saturating_mul(n.into())) .saturating_add(T::DbWeight::get().reads(8_u64)) .saturating_add(T::DbWeight::get().writes(6_u64)) } @@ -991,10 +977,10 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `1167 + n * (1 ±0)` // Estimated: `1167 + n * (1 ±0)` - // Minimum execution time: 250_985_000 picoseconds. - Weight::from_parts(253_693_249, 1167) - // Standard Error: 21 - .saturating_add(Weight::from_parts(92, 0).saturating_mul(n.into())) + // Minimum execution time: 252_047_000 picoseconds. + Weight::from_parts(254_244_310, 1167) + // Standard Error: 15 + .saturating_add(Weight::from_parts(144, 0).saturating_mul(n.into())) .saturating_add(T::DbWeight::get().reads(7_u64)) .saturating_add(T::DbWeight::get().writes(4_u64)) .saturating_add(Weight::from_parts(0, 1).saturating_mul(n.into())) @@ -1006,10 +992,10 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `841 + r * (288 ±0)` // Estimated: `845 + r * (289 ±0)` - // Minimum execution time: 235_462_000 picoseconds. - Weight::from_parts(141_240_297, 845) - // Standard Error: 9_687 - .saturating_add(Weight::from_parts(5_906_737, 0).saturating_mul(r.into())) + // Minimum execution time: 235_697_000 picoseconds. + Weight::from_parts(143_200_942, 845) + // Standard Error: 11_358 + .saturating_add(Weight::from_parts(5_934_318, 0).saturating_mul(r.into())) .saturating_add(T::DbWeight::get().reads(6_u64)) .saturating_add(T::DbWeight::get().reads((1_u64).saturating_mul(r.into()))) .saturating_add(T::DbWeight::get().writes(3_u64)) @@ -1023,10 +1009,10 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `1163 + n * (1 ±0)` // Estimated: `1163 + n * (1 ±0)` - // Minimum execution time: 250_887_000 picoseconds. - Weight::from_parts(253_321_064, 1163) - // Standard Error: 28 - .saturating_add(Weight::from_parts(169, 0).saturating_mul(n.into())) + // Minimum execution time: 250_360_000 picoseconds. + Weight::from_parts(252_712_722, 1163) + // Standard Error: 15 + .saturating_add(Weight::from_parts(130, 0).saturating_mul(n.into())) .saturating_add(T::DbWeight::get().reads(7_u64)) .saturating_add(T::DbWeight::get().writes(4_u64)) .saturating_add(Weight::from_parts(0, 1).saturating_mul(n.into())) @@ -1038,10 +1024,10 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `835 + r * (296 ±0)` // Estimated: `840 + r * (297 ±0)` - // Minimum execution time: 236_542_000 picoseconds. - Weight::from_parts(147_992_508, 840) - // Standard Error: 8_849 - .saturating_add(Weight::from_parts(4_946_692, 0).saturating_mul(r.into())) + // Minimum execution time: 235_613_000 picoseconds. + Weight::from_parts(150_213_792, 840) + // Standard Error: 8_617 + .saturating_add(Weight::from_parts(4_962_874, 0).saturating_mul(r.into())) .saturating_add(T::DbWeight::get().reads(6_u64)) .saturating_add(T::DbWeight::get().reads((1_u64).saturating_mul(r.into()))) .saturating_add(T::DbWeight::get().writes(3_u64)) @@ -1054,10 +1040,10 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `1179 + n * (1 ±0)` // Estimated: `1179 + n * (1 ±0)` - // Minimum execution time: 249_987_000 picoseconds. - Weight::from_parts(254_866_627, 1179) - // Standard Error: 53 - .saturating_add(Weight::from_parts(554, 0).saturating_mul(n.into())) + // Minimum execution time: 249_137_000 picoseconds. + Weight::from_parts(253_529_386, 1179) + // Standard Error: 41 + .saturating_add(Weight::from_parts(612, 0).saturating_mul(n.into())) .saturating_add(T::DbWeight::get().reads(7_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) .saturating_add(Weight::from_parts(0, 1).saturating_mul(n.into())) @@ -1069,10 +1055,10 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `856 + r * (288 ±0)` // Estimated: `857 + r * (289 ±0)` - // Minimum execution time: 236_635_000 picoseconds. - Weight::from_parts(157_805_789, 857) - // Standard Error: 7_699 - .saturating_add(Weight::from_parts(4_709_422, 0).saturating_mul(r.into())) + // Minimum execution time: 235_659_000 picoseconds. + Weight::from_parts(158_846_683, 857) + // Standard Error: 7_806 + .saturating_add(Weight::from_parts(4_728_537, 0).saturating_mul(r.into())) .saturating_add(T::DbWeight::get().reads(6_u64)) .saturating_add(T::DbWeight::get().reads((1_u64).saturating_mul(r.into()))) .saturating_add(T::DbWeight::get().writes(3_u64)) @@ -1085,10 +1071,10 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `1166 + n * (1 ±0)` // Estimated: `1166 + n * (1 ±0)` - // Minimum execution time: 252_660_000 picoseconds. - Weight::from_parts(255_250_747, 1166) - // Standard Error: 14 - .saturating_add(Weight::from_parts(133, 0).saturating_mul(n.into())) + // Minimum execution time: 248_553_000 picoseconds. + Weight::from_parts(250_703_269, 1166) + // Standard Error: 17 + .saturating_add(Weight::from_parts(163, 0).saturating_mul(n.into())) .saturating_add(T::DbWeight::get().reads(7_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) .saturating_add(Weight::from_parts(0, 1).saturating_mul(n.into())) @@ -1100,10 +1086,10 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `829 + r * (296 ±0)` // Estimated: `836 + r * (297 ±0)` - // Minimum execution time: 240_198_000 picoseconds. - Weight::from_parts(133_188_357, 836) - // Standard Error: 10_661 - .saturating_add(Weight::from_parts(6_147_538, 0).saturating_mul(r.into())) + // Minimum execution time: 236_431_000 picoseconds. + Weight::from_parts(131_745_419, 836) + // Standard Error: 10_161 + .saturating_add(Weight::from_parts(6_182_174, 0).saturating_mul(r.into())) .saturating_add(T::DbWeight::get().reads(6_u64)) .saturating_add(T::DbWeight::get().reads((1_u64).saturating_mul(r.into()))) .saturating_add(T::DbWeight::get().writes(3_u64)) @@ -1117,10 +1103,10 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `1180 + n * (1 ±0)` // Estimated: `1180 + n * (1 ±0)` - // Minimum execution time: 252_131_000 picoseconds. - Weight::from_parts(259_960_286, 1180) - // Standard Error: 121 - .saturating_add(Weight::from_parts(192, 0).saturating_mul(n.into())) + // Minimum execution time: 252_551_000 picoseconds. + Weight::from_parts(254_517_030, 1180) + // Standard Error: 16 + .saturating_add(Weight::from_parts(712, 0).saturating_mul(n.into())) .saturating_add(T::DbWeight::get().reads(7_u64)) .saturating_add(T::DbWeight::get().writes(4_u64)) .saturating_add(Weight::from_parts(0, 1).saturating_mul(n.into())) @@ -1140,10 +1126,10 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `1373 + r * (45 ±0)` // Estimated: `26753 + r * (2700 ±0)` - // Minimum execution time: 235_860_000 picoseconds. - Weight::from_parts(124_993_651, 26753) - // Standard Error: 22_811 - .saturating_add(Weight::from_parts(36_467_740, 0).saturating_mul(r.into())) + // Minimum execution time: 236_663_000 picoseconds. + Weight::from_parts(237_485_000, 26753) + // Standard Error: 42_414 + .saturating_add(Weight::from_parts(36_849_514, 0).saturating_mul(r.into())) .saturating_add(T::DbWeight::get().reads(7_u64)) .saturating_add(T::DbWeight::get().reads((1_u64).saturating_mul(r.into()))) .saturating_add(T::DbWeight::get().writes(4_u64)) @@ -1165,10 +1151,10 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `1237 + r * (256 ±0)` // Estimated: `26028 + r * (6235 ±0)` - // Minimum execution time: 237_221_000 picoseconds. - Weight::from_parts(237_632_000, 26028) - // Standard Error: 89_502 - .saturating_add(Weight::from_parts(212_211_534, 0).saturating_mul(r.into())) + // Minimum execution time: 237_101_000 picoseconds. + Weight::from_parts(237_827_000, 26028) + // Standard Error: 82_878 + .saturating_add(Weight::from_parts(211_777_724, 0).saturating_mul(r.into())) .saturating_add(T::DbWeight::get().reads(7_u64)) .saturating_add(T::DbWeight::get().reads((2_u64).saturating_mul(r.into()))) .saturating_add(T::DbWeight::get().writes(3_u64)) @@ -1190,10 +1176,10 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `0 + r * (502 ±0)` // Estimated: `21755 + r * (6329 ±3)` - // Minimum execution time: 236_965_000 picoseconds. - Weight::from_parts(238_110_000, 21755) - // Standard Error: 101_332 - .saturating_add(Weight::from_parts(206_790_203, 0).saturating_mul(r.into())) + // Minimum execution time: 241_213_000 picoseconds. + Weight::from_parts(241_900_000, 21755) + // Standard Error: 99_976 + .saturating_add(Weight::from_parts(207_520_077, 0).saturating_mul(r.into())) .saturating_add(T::DbWeight::get().reads(6_u64)) .saturating_add(T::DbWeight::get().reads((2_u64).saturating_mul(r.into()))) .saturating_add(T::DbWeight::get().writes(3_u64)) @@ -1216,12 +1202,12 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `1154 + t * (204 ±0)` // Estimated: `31015 + t * (5970 ±0)` - // Minimum execution time: 411_974_000 picoseconds. - Weight::from_parts(391_387_689, 31015) - // Standard Error: 1_320_695 - .saturating_add(Weight::from_parts(29_766_122, 0).saturating_mul(t.into())) + // Minimum execution time: 414_784_000 picoseconds. + Weight::from_parts(384_902_379, 31015) + // Standard Error: 1_228_593 + .saturating_add(Weight::from_parts(33_226_901, 0).saturating_mul(t.into())) // Standard Error: 1 - .saturating_add(Weight::from_parts(597, 0).saturating_mul(c.into())) + .saturating_add(Weight::from_parts(601, 0).saturating_mul(c.into())) .saturating_add(T::DbWeight::get().reads(9_u64)) .saturating_add(T::DbWeight::get().reads((2_u64).saturating_mul(t.into()))) .saturating_add(T::DbWeight::get().writes(5_u64)) @@ -1247,10 +1233,10 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `1301 + r * (254 ±0)` // Estimated: `30977 + r * (16635 ±0)` - // Minimum execution time: 237_595_000 picoseconds. - Weight::from_parts(238_068_000, 30977) - // Standard Error: 254_409 - .saturating_add(Weight::from_parts(346_436_154, 0).saturating_mul(r.into())) + // Minimum execution time: 236_963_000 picoseconds. + Weight::from_parts(237_711_000, 30977) + // Standard Error: 265_576 + .saturating_add(Weight::from_parts(347_359_908, 0).saturating_mul(r.into())) .saturating_add(T::DbWeight::get().reads(8_u64)) .saturating_add(T::DbWeight::get().reads((6_u64).saturating_mul(r.into()))) .saturating_add(T::DbWeight::get().writes(6_u64)) @@ -1278,14 +1264,14 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `1071 + t * (187 ±0)` // Estimated: `42684 + t * (3588 ±2)` - // Minimum execution time: 1_616_768_000 picoseconds. - Weight::from_parts(363_003_254, 42684) - // Standard Error: 4_398_669 - .saturating_add(Weight::from_parts(104_529_967, 0).saturating_mul(t.into())) + // Minimum execution time: 1_615_191_000 picoseconds. + Weight::from_parts(337_408_450, 42684) + // Standard Error: 4_581_951 + .saturating_add(Weight::from_parts(115_730_776, 0).saturating_mul(t.into())) // Standard Error: 7 - .saturating_add(Weight::from_parts(1_162, 0).saturating_mul(i.into())) + .saturating_add(Weight::from_parts(1_171, 0).saturating_mul(i.into())) // Standard Error: 7 - .saturating_add(Weight::from_parts(1_333, 0).saturating_mul(s.into())) + .saturating_add(Weight::from_parts(1_346, 0).saturating_mul(s.into())) .saturating_add(T::DbWeight::get().reads(13_u64)) .saturating_add(T::DbWeight::get().reads((1_u64).saturating_mul(t.into()))) .saturating_add(T::DbWeight::get().writes(10_u64)) @@ -1307,10 +1293,10 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `777 + r * (8 ±0)` // Estimated: `21710 + r * (40 ±0)` - // Minimum execution time: 233_814_000 picoseconds. - Weight::from_parts(241_291_041, 21710) - // Standard Error: 1_422 - .saturating_add(Weight::from_parts(575_846, 0).saturating_mul(r.into())) + // Minimum execution time: 233_601_000 picoseconds. + Weight::from_parts(246_594_905, 21710) + // Standard Error: 2_840 + .saturating_add(Weight::from_parts(578_751, 0).saturating_mul(r.into())) .saturating_add(T::DbWeight::get().reads(6_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) .saturating_add(Weight::from_parts(0, 40).saturating_mul(r.into())) @@ -1330,10 +1316,10 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `785` // Estimated: `21745` - // Minimum execution time: 236_572_000 picoseconds. - Weight::from_parts(235_648_055, 21745) + // Minimum execution time: 233_735_000 picoseconds. + Weight::from_parts(243_432_330, 21745) // Standard Error: 3 - .saturating_add(Weight::from_parts(3_947, 0).saturating_mul(n.into())) + .saturating_add(Weight::from_parts(3_927, 0).saturating_mul(n.into())) .saturating_add(T::DbWeight::get().reads(6_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) } @@ -1352,10 +1338,10 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `779 + r * (8 ±0)` // Estimated: `21725 + r * (40 ±0)` - // Minimum execution time: 234_473_000 picoseconds. - Weight::from_parts(239_805_309, 21725) - // Standard Error: 1_113 - .saturating_add(Weight::from_parts(752_507, 0).saturating_mul(r.into())) + // Minimum execution time: 232_173_000 picoseconds. + Weight::from_parts(239_806_011, 21725) + // Standard Error: 1_530 + .saturating_add(Weight::from_parts(749_641, 0).saturating_mul(r.into())) .saturating_add(T::DbWeight::get().reads(6_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) .saturating_add(Weight::from_parts(0, 40).saturating_mul(r.into())) @@ -1375,10 +1361,10 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `787` // Estimated: `21765` - // Minimum execution time: 235_209_000 picoseconds. - Weight::from_parts(228_548_524, 21765) + // Minimum execution time: 235_046_000 picoseconds. + Weight::from_parts(229_500_792, 21765) // Standard Error: 2 - .saturating_add(Weight::from_parts(3_171, 0).saturating_mul(n.into())) + .saturating_add(Weight::from_parts(3_166, 0).saturating_mul(n.into())) .saturating_add(T::DbWeight::get().reads(6_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) } @@ -1397,10 +1383,10 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `779 + r * (8 ±0)` // Estimated: `21740 + r * (40 ±0)` - // Minimum execution time: 233_282_000 picoseconds. - Weight::from_parts(240_864_680, 21740) - // Standard Error: 958 - .saturating_add(Weight::from_parts(418_308, 0).saturating_mul(r.into())) + // Minimum execution time: 231_708_000 picoseconds. + Weight::from_parts(235_347_566, 21740) + // Standard Error: 987 + .saturating_add(Weight::from_parts(428_819, 0).saturating_mul(r.into())) .saturating_add(T::DbWeight::get().reads(6_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) .saturating_add(Weight::from_parts(0, 40).saturating_mul(r.into())) @@ -1420,10 +1406,10 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `787` // Estimated: `21785` - // Minimum execution time: 234_667_000 picoseconds. - Weight::from_parts(227_810_077, 21785) - // Standard Error: 2 - .saturating_add(Weight::from_parts(925, 0).saturating_mul(n.into())) + // Minimum execution time: 234_068_000 picoseconds. + Weight::from_parts(226_519_852, 21785) + // Standard Error: 1 + .saturating_add(Weight::from_parts(916, 0).saturating_mul(n.into())) .saturating_add(T::DbWeight::get().reads(6_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) } @@ -1442,10 +1428,10 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `779 + r * (8 ±0)` // Estimated: `21745 + r * (40 ±0)` - // Minimum execution time: 234_040_000 picoseconds. - Weight::from_parts(237_970_694, 21745) - // Standard Error: 979 - .saturating_add(Weight::from_parts(416_562, 0).saturating_mul(r.into())) + // Minimum execution time: 231_872_000 picoseconds. + Weight::from_parts(236_694_763, 21745) + // Standard Error: 870 + .saturating_add(Weight::from_parts(420_853, 0).saturating_mul(r.into())) .saturating_add(T::DbWeight::get().reads(6_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) .saturating_add(Weight::from_parts(0, 40).saturating_mul(r.into())) @@ -1465,10 +1451,10 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `787` // Estimated: `21755` - // Minimum execution time: 234_840_000 picoseconds. - Weight::from_parts(227_849_778, 21755) + // Minimum execution time: 233_707_000 picoseconds. + Weight::from_parts(226_312_559, 21755) // Standard Error: 2 - .saturating_add(Weight::from_parts(923, 0).saturating_mul(n.into())) + .saturating_add(Weight::from_parts(924, 0).saturating_mul(n.into())) .saturating_add(T::DbWeight::get().reads(6_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) } @@ -1487,10 +1473,10 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `822 + r * (76 ±0)` // Estimated: `21705 + r * (385 ±0)` - // Minimum execution time: 236_174_000 picoseconds. - Weight::from_parts(252_457_690, 21705) - // Standard Error: 20_130 - .saturating_add(Weight::from_parts(37_792_805, 0).saturating_mul(r.into())) + // Minimum execution time: 234_962_000 picoseconds. + Weight::from_parts(252_611_292, 21705) + // Standard Error: 20_134 + .saturating_add(Weight::from_parts(37_745_358, 0).saturating_mul(r.into())) .saturating_add(T::DbWeight::get().reads(6_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) .saturating_add(Weight::from_parts(0, 385).saturating_mul(r.into())) @@ -1509,11 +1495,11 @@ impl WeightInfo for SubstrateWeight { fn seal_ecdsa_to_eth_address(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `792 + r * (42 ±0)` - // Estimated: `21780 + r * (210 ±0)` - // Minimum execution time: 236_251_000 picoseconds. - Weight::from_parts(242_254_305, 21780) - // Standard Error: 9_765 - .saturating_add(Weight::from_parts(9_341_334, 0).saturating_mul(r.into())) + // Estimated: `21775 + r * (210 ±0)` + // Minimum execution time: 234_869_000 picoseconds. + Weight::from_parts(240_188_331, 21775) + // Standard Error: 9_910 + .saturating_add(Weight::from_parts(9_332_432, 0).saturating_mul(r.into())) .saturating_add(T::DbWeight::get().reads(6_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) .saturating_add(Weight::from_parts(0, 210).saturating_mul(r.into())) @@ -1534,11 +1520,11 @@ impl WeightInfo for SubstrateWeight { fn seal_set_code_hash(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0 + r * (964 ±0)` - // Estimated: `29920 + r * (11544 ±7)` - // Minimum execution time: 236_462_000 picoseconds. - Weight::from_parts(236_997_000, 29920) - // Standard Error: 46_527 - .saturating_add(Weight::from_parts(21_858_761, 0).saturating_mul(r.into())) + // Estimated: `29920 + r * (11544 ±10)` + // Minimum execution time: 235_036_000 picoseconds. + Weight::from_parts(235_538_000, 29920) + // Standard Error: 47_360 + .saturating_add(Weight::from_parts(22_113_144, 0).saturating_mul(r.into())) .saturating_add(T::DbWeight::get().reads(6_u64)) .saturating_add(T::DbWeight::get().reads((3_u64).saturating_mul(r.into()))) .saturating_add(T::DbWeight::get().writes(3_u64)) @@ -1560,10 +1546,10 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `773 + r * (3 ±0)` // Estimated: `21735 + r * (15 ±0)` - // Minimum execution time: 235_085_000 picoseconds. - Weight::from_parts(239_410_836, 21735) - // Standard Error: 414 - .saturating_add(Weight::from_parts(167_067, 0).saturating_mul(r.into())) + // Minimum execution time: 237_884_000 picoseconds. + Weight::from_parts(243_315_095, 21735) + // Standard Error: 560 + .saturating_add(Weight::from_parts(162_206, 0).saturating_mul(r.into())) .saturating_add(T::DbWeight::get().reads(6_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) .saturating_add(Weight::from_parts(0, 15).saturating_mul(r.into())) @@ -1583,10 +1569,10 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `1975 + r * (39 ±0)` // Estimated: `27145 + r * (200 ±0)` - // Minimum execution time: 236_690_000 picoseconds. - Weight::from_parts(268_793_030, 27145) - // Standard Error: 1_210 - .saturating_add(Weight::from_parts(263_330, 0).saturating_mul(r.into())) + // Minimum execution time: 239_402_000 picoseconds. + Weight::from_parts(267_214_783, 27145) + // Standard Error: 1_166 + .saturating_add(Weight::from_parts(261_915, 0).saturating_mul(r.into())) .saturating_add(T::DbWeight::get().reads(6_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) .saturating_add(Weight::from_parts(0, 200).saturating_mul(r.into())) @@ -1608,10 +1594,10 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `776 + r * (3 ±0)` // Estimated: `24004 + r * (18 ±0)` - // Minimum execution time: 236_289_000 picoseconds. - Weight::from_parts(246_581_099, 24004) - // Standard Error: 1_300 - .saturating_add(Weight::from_parts(137_499, 0).saturating_mul(r.into())) + // Minimum execution time: 233_153_000 picoseconds. + Weight::from_parts(238_999_965, 24004) + // Standard Error: 291 + .saturating_add(Weight::from_parts(143_971, 0).saturating_mul(r.into())) .saturating_add(T::DbWeight::get().reads(7_u64)) .saturating_add(T::DbWeight::get().writes(4_u64)) .saturating_add(Weight::from_parts(0, 18).saturating_mul(r.into())) @@ -1621,9 +1607,9 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_679_000 picoseconds. - Weight::from_parts(1_934_194, 0) - // Standard Error: 1 + // Minimum execution time: 1_706_000 picoseconds. + Weight::from_parts(1_962_558, 0) + // Standard Error: 2 .saturating_add(Weight::from_parts(3_000, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 5000]`. @@ -1631,513 +1617,513 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_872_000 picoseconds. - Weight::from_parts(2_605_712, 0) - // Standard Error: 43 - .saturating_add(Weight::from_parts(6_321, 0).saturating_mul(r.into())) + // Minimum execution time: 1_870_000 picoseconds. + Weight::from_parts(2_481_243, 0) + // Standard Error: 4 + .saturating_add(Weight::from_parts(6_346, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 5000]`. fn instr_i64store(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_837_000 picoseconds. - Weight::from_parts(2_035_143, 0) - // Standard Error: 65 - .saturating_add(Weight::from_parts(6_202, 0).saturating_mul(r.into())) + // Minimum execution time: 1_830_000 picoseconds. + Weight::from_parts(2_389_658, 0) + // Standard Error: 4 + .saturating_add(Weight::from_parts(5_997, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 5000]`. fn instr_select(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_684_000 picoseconds. - Weight::from_parts(2_044_218, 0) - // Standard Error: 5 - .saturating_add(Weight::from_parts(7_929, 0).saturating_mul(r.into())) + // Minimum execution time: 1_734_000 picoseconds. + Weight::from_parts(2_170_618, 0) + // Standard Error: 4 + .saturating_add(Weight::from_parts(7_919, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 5000]`. fn instr_if(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_635_000 picoseconds. - Weight::from_parts(2_009_851, 0) - // Standard Error: 6 - .saturating_add(Weight::from_parts(10_724, 0).saturating_mul(r.into())) + // Minimum execution time: 1_661_000 picoseconds. + Weight::from_parts(1_945_771, 0) + // Standard Error: 11 + .saturating_add(Weight::from_parts(10_840, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 5000]`. fn instr_br(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_667_000 picoseconds. - Weight::from_parts(1_869_395, 0) - // Standard Error: 12 - .saturating_add(Weight::from_parts(4_618, 0).saturating_mul(r.into())) + // Minimum execution time: 1_682_000 picoseconds. + Weight::from_parts(1_970_774, 0) + // Standard Error: 11 + .saturating_add(Weight::from_parts(4_569, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 5000]`. fn instr_br_if(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_698_000 picoseconds. - Weight::from_parts(2_184_182, 0) - // Standard Error: 16 - .saturating_add(Weight::from_parts(6_833, 0).saturating_mul(r.into())) + // Minimum execution time: 1_668_000 picoseconds. + Weight::from_parts(1_227_080, 0) + // Standard Error: 76 + .saturating_add(Weight::from_parts(8_066, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 5000]`. fn instr_br_table(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_670_000 picoseconds. - Weight::from_parts(1_471_988, 0) - // Standard Error: 30 - .saturating_add(Weight::from_parts(9_550, 0).saturating_mul(r.into())) + // Minimum execution time: 1_653_000 picoseconds. + Weight::from_parts(1_394_759, 0) + // Standard Error: 39 + .saturating_add(Weight::from_parts(9_566, 0).saturating_mul(r.into())) } /// The range of component `e` is `[1, 256]`. fn instr_br_table_per_entry(e: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_778_000 picoseconds. - Weight::from_parts(1_925_086, 0) - // Standard Error: 136 - .saturating_add(Weight::from_parts(502, 0).saturating_mul(e.into())) + // Minimum execution time: 1_771_000 picoseconds. + Weight::from_parts(1_905_594, 0) + // Standard Error: 147 + .saturating_add(Weight::from_parts(925, 0).saturating_mul(e.into())) } /// The range of component `r` is `[0, 5000]`. fn instr_call(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_759_000 picoseconds. - Weight::from_parts(2_372_048, 0) - // Standard Error: 16 - .saturating_add(Weight::from_parts(17_953, 0).saturating_mul(r.into())) + // Minimum execution time: 1_863_000 picoseconds. + Weight::from_parts(2_472_635, 0) + // Standard Error: 13 + .saturating_add(Weight::from_parts(17_892, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 5000]`. fn instr_call_indirect(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_852_000 picoseconds. - Weight::from_parts(3_125_003, 0) - // Standard Error: 25 - .saturating_add(Weight::from_parts(24_218, 0).saturating_mul(r.into())) + // Minimum execution time: 2_013_000 picoseconds. + Weight::from_parts(3_077_100, 0) + // Standard Error: 18 + .saturating_add(Weight::from_parts(24_287, 0).saturating_mul(r.into())) } /// The range of component `l` is `[0, 1024]`. fn instr_call_per_local(l: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_795_000 picoseconds. - Weight::from_parts(2_121_763, 0) - // Standard Error: 45 - .saturating_add(Weight::from_parts(1_207, 0).saturating_mul(l.into())) + // Minimum execution time: 1_818_000 picoseconds. + Weight::from_parts(2_109_143, 0) + // Standard Error: 34 + .saturating_add(Weight::from_parts(1_249, 0).saturating_mul(l.into())) } /// The range of component `r` is `[0, 5000]`. fn instr_local_get(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 3_019_000 picoseconds. - Weight::from_parts(3_267_108, 0) - // Standard Error: 55 - .saturating_add(Weight::from_parts(2_527, 0).saturating_mul(r.into())) + // Minimum execution time: 3_083_000 picoseconds. + Weight::from_parts(3_291_328, 0) + // Standard Error: 1 + .saturating_add(Weight::from_parts(2_505, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 5000]`. fn instr_local_set(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 2_944_000 picoseconds. - Weight::from_parts(3_183_331, 0) + // Minimum execution time: 2_987_000 picoseconds. + Weight::from_parts(3_276_863, 0) // Standard Error: 2 - .saturating_add(Weight::from_parts(3_618, 0).saturating_mul(r.into())) + .saturating_add(Weight::from_parts(3_617, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 5000]`. fn instr_local_tee(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 3_009_000 picoseconds. - Weight::from_parts(3_178_158, 0) - // Standard Error: 41 - .saturating_add(Weight::from_parts(4_075, 0).saturating_mul(r.into())) + // Minimum execution time: 2_942_000 picoseconds. + Weight::from_parts(3_350_581, 0) + // Standard Error: 3 + .saturating_add(Weight::from_parts(3_976, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 5000]`. fn instr_global_get(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_748_000 picoseconds. - Weight::from_parts(2_371_911, 0) - // Standard Error: 10 - .saturating_add(Weight::from_parts(8_378, 0).saturating_mul(r.into())) + // Minimum execution time: 1_867_000 picoseconds. + Weight::from_parts(2_920_748, 0) + // Standard Error: 44 + .saturating_add(Weight::from_parts(8_229, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 5000]`. fn instr_global_set(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_795_000 picoseconds. - Weight::from_parts(1_311_997, 0) - // Standard Error: 157 - .saturating_add(Weight::from_parts(9_410, 0).saturating_mul(r.into())) + // Minimum execution time: 1_757_000 picoseconds. + Weight::from_parts(2_235_198, 0) + // Standard Error: 4 + .saturating_add(Weight::from_parts(8_815, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 5000]`. fn instr_memory_current(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 2_059_000 picoseconds. - Weight::from_parts(2_416_611, 0) - // Standard Error: 5 - .saturating_add(Weight::from_parts(3_775, 0).saturating_mul(r.into())) + // Minimum execution time: 1_824_000 picoseconds. + Weight::from_parts(1_941_816, 0) + // Standard Error: 86 + .saturating_add(Weight::from_parts(4_043, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 16]`. fn instr_memory_grow(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_764_000 picoseconds. - Weight::from_parts(1_414_442, 0) - // Standard Error: 142_321 - .saturating_add(Weight::from_parts(13_210_495, 0).saturating_mul(r.into())) + // Minimum execution time: 1_793_000 picoseconds. + Weight::from_parts(1_104_829, 0) + // Standard Error: 137_800 + .saturating_add(Weight::from_parts(13_336_784, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 5000]`. fn instr_i64clz(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_665_000 picoseconds. - Weight::from_parts(2_047_968, 0) + // Minimum execution time: 1_693_000 picoseconds. + Weight::from_parts(2_037_305, 0) // Standard Error: 2 - .saturating_add(Weight::from_parts(4_035, 0).saturating_mul(r.into())) + .saturating_add(Weight::from_parts(4_044, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 5000]`. fn instr_i64ctz(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_696_000 picoseconds. - Weight::from_parts(2_101_548, 0) - // Standard Error: 13 - .saturating_add(Weight::from_parts(3_754, 0).saturating_mul(r.into())) + // Minimum execution time: 1_751_000 picoseconds. + Weight::from_parts(2_082_016, 0) + // Standard Error: 2 + .saturating_add(Weight::from_parts(3_767, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 5000]`. fn instr_i64popcnt(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_686_000 picoseconds. - Weight::from_parts(2_023_482, 0) + // Minimum execution time: 1_751_000 picoseconds. + Weight::from_parts(2_110_625, 0) // Standard Error: 2 - .saturating_add(Weight::from_parts(3_770, 0).saturating_mul(r.into())) + .saturating_add(Weight::from_parts(3_754, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 5000]`. fn instr_i64eqz(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_700_000 picoseconds. - Weight::from_parts(2_035_759, 0) + // Minimum execution time: 1_756_000 picoseconds. + Weight::from_parts(2_100_327, 0) // Standard Error: 2 - .saturating_add(Weight::from_parts(3_660, 0).saturating_mul(r.into())) + .saturating_add(Weight::from_parts(3_664, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 5000]`. fn instr_i64extendsi32(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_682_000 picoseconds. - Weight::from_parts(2_015_828, 0) - // Standard Error: 2 - .saturating_add(Weight::from_parts(3_977, 0).saturating_mul(r.into())) + // Minimum execution time: 1_643_000 picoseconds. + Weight::from_parts(2_169_153, 0) + // Standard Error: 3 + .saturating_add(Weight::from_parts(3_961, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 5000]`. fn instr_i64extendui32(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_660_000 picoseconds. - Weight::from_parts(2_032_387, 0) + // Minimum execution time: 1_704_000 picoseconds. + Weight::from_parts(2_049_172, 0) // Standard Error: 2 - .saturating_add(Weight::from_parts(3_826, 0).saturating_mul(r.into())) + .saturating_add(Weight::from_parts(3_833, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 5000]`. fn instr_i32wrapi64(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_635_000 picoseconds. - Weight::from_parts(2_013_228, 0) - // Standard Error: 3 - .saturating_add(Weight::from_parts(3_752, 0).saturating_mul(r.into())) + // Minimum execution time: 1_726_000 picoseconds. + Weight::from_parts(2_064_387, 0) + // Standard Error: 2 + .saturating_add(Weight::from_parts(3_745, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 5000]`. fn instr_i64eq(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_698_000 picoseconds. - Weight::from_parts(2_844_817, 0) + // Minimum execution time: 1_696_000 picoseconds. + Weight::from_parts(2_426_905, 0) // Standard Error: 56 - .saturating_add(Weight::from_parts(5_746, 0).saturating_mul(r.into())) + .saturating_add(Weight::from_parts(5_915, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 5000]`. fn instr_i64ne(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_686_000 picoseconds. - Weight::from_parts(2_208_884, 0) - // Standard Error: 138 - .saturating_add(Weight::from_parts(6_032, 0).saturating_mul(r.into())) + // Minimum execution time: 1_617_000 picoseconds. + Weight::from_parts(2_035_161, 0) + // Standard Error: 94 + .saturating_add(Weight::from_parts(6_073, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 5000]`. fn instr_i64lts(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_695_000 picoseconds. - Weight::from_parts(2_060_880, 0) - // Standard Error: 3 - .saturating_add(Weight::from_parts(5_966, 0).saturating_mul(r.into())) + // Minimum execution time: 1_756_000 picoseconds. + Weight::from_parts(2_098_926, 0) + // Standard Error: 2 + .saturating_add(Weight::from_parts(6_002, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 5000]`. fn instr_i64ltu(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_700_000 picoseconds. - Weight::from_parts(2_143_484, 0) - // Standard Error: 3 - .saturating_add(Weight::from_parts(5_932, 0).saturating_mul(r.into())) + // Minimum execution time: 1_706_000 picoseconds. + Weight::from_parts(2_257_972, 0) + // Standard Error: 23 + .saturating_add(Weight::from_parts(5_982, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 5000]`. fn instr_i64gts(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_677_000 picoseconds. - Weight::from_parts(2_081_646, 0) - // Standard Error: 3 - .saturating_add(Weight::from_parts(5_813, 0).saturating_mul(r.into())) + // Minimum execution time: 1_761_000 picoseconds. + Weight::from_parts(2_114_141, 0) + // Standard Error: 2 + .saturating_add(Weight::from_parts(5_815, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 5000]`. fn instr_i64gtu(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 2_476_000 picoseconds. - Weight::from_parts(2_161_801, 0) - // Standard Error: 4 - .saturating_add(Weight::from_parts(6_078, 0).saturating_mul(r.into())) + // Minimum execution time: 1_700_000 picoseconds. + Weight::from_parts(2_053_201, 0) + // Standard Error: 2 + .saturating_add(Weight::from_parts(6_137, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 5000]`. fn instr_i64les(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_677_000 picoseconds. - Weight::from_parts(2_043_451, 0) + // Minimum execution time: 1_705_000 picoseconds. + Weight::from_parts(2_101_782, 0) // Standard Error: 3 - .saturating_add(Weight::from_parts(5_988, 0).saturating_mul(r.into())) + .saturating_add(Weight::from_parts(6_014, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 5000]`. fn instr_i64leu(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_694_000 picoseconds. - Weight::from_parts(2_058_196, 0) - // Standard Error: 2 - .saturating_add(Weight::from_parts(6_058, 0).saturating_mul(r.into())) + // Minimum execution time: 1_856_000 picoseconds. + Weight::from_parts(2_149_707, 0) + // Standard Error: 3 + .saturating_add(Weight::from_parts(6_086, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 5000]`. fn instr_i64ges(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_675_000 picoseconds. - Weight::from_parts(2_036_798, 0) - // Standard Error: 2 - .saturating_add(Weight::from_parts(5_956, 0).saturating_mul(r.into())) + // Minimum execution time: 1_739_000 picoseconds. + Weight::from_parts(2_143_216, 0) + // Standard Error: 3 + .saturating_add(Weight::from_parts(5_934, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 5000]`. fn instr_i64geu(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_712_000 picoseconds. - Weight::from_parts(2_121_407, 0) - // Standard Error: 2 - .saturating_add(Weight::from_parts(5_944, 0).saturating_mul(r.into())) + // Minimum execution time: 1_716_000 picoseconds. + Weight::from_parts(2_065_762, 0) + // Standard Error: 3 + .saturating_add(Weight::from_parts(6_009, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 5000]`. fn instr_i64add(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_675_000 picoseconds. - Weight::from_parts(2_061_053, 0) - // Standard Error: 6 - .saturating_add(Weight::from_parts(5_823, 0).saturating_mul(r.into())) + // Minimum execution time: 1_664_000 picoseconds. + Weight::from_parts(3_062_283, 0) + // Standard Error: 94 + .saturating_add(Weight::from_parts(5_645, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 5000]`. fn instr_i64sub(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_686_000 picoseconds. - Weight::from_parts(2_023_347, 0) - // Standard Error: 4 - .saturating_add(Weight::from_parts(6_126, 0).saturating_mul(r.into())) + // Minimum execution time: 1_671_000 picoseconds. + Weight::from_parts(2_011_145, 0) + // Standard Error: 44 + .saturating_add(Weight::from_parts(6_220, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 5000]`. fn instr_i64mul(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_678_000 picoseconds. - Weight::from_parts(2_390_920, 0) - // Standard Error: 55 - .saturating_add(Weight::from_parts(5_635, 0).saturating_mul(r.into())) + // Minimum execution time: 1_759_000 picoseconds. + Weight::from_parts(2_095_420, 0) + // Standard Error: 4 + .saturating_add(Weight::from_parts(5_723, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 5000]`. fn instr_i64divs(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_728_000 picoseconds. - Weight::from_parts(1_944_457, 0) - // Standard Error: 7 - .saturating_add(Weight::from_parts(11_848, 0).saturating_mul(r.into())) + // Minimum execution time: 1_672_000 picoseconds. + Weight::from_parts(2_184_044, 0) + // Standard Error: 5 + .saturating_add(Weight::from_parts(11_782, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 5000]`. fn instr_i64divu(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_649_000 picoseconds. - Weight::from_parts(1_881_148, 0) - // Standard Error: 6 - .saturating_add(Weight::from_parts(10_618, 0).saturating_mul(r.into())) + // Minimum execution time: 1_752_000 picoseconds. + Weight::from_parts(2_276_209, 0) + // Standard Error: 5 + .saturating_add(Weight::from_parts(10_513, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 5000]`. fn instr_i64rems(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_708_000 picoseconds. - Weight::from_parts(1_767_912, 0) - // Standard Error: 6 - .saturating_add(Weight::from_parts(12_099, 0).saturating_mul(r.into())) + // Minimum execution time: 1_711_000 picoseconds. + Weight::from_parts(2_720_989, 0) + // Standard Error: 24 + .saturating_add(Weight::from_parts(11_884, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 5000]`. fn instr_i64remu(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_650_000 picoseconds. - Weight::from_parts(1_998_575, 0) + // Minimum execution time: 1_713_000 picoseconds. + Weight::from_parts(2_091_403, 0) // Standard Error: 3 - .saturating_add(Weight::from_parts(10_632, 0).saturating_mul(r.into())) + .saturating_add(Weight::from_parts(10_628, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 5000]`. fn instr_i64and(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_633_000 picoseconds. - Weight::from_parts(2_029_981, 0) - // Standard Error: 3 - .saturating_add(Weight::from_parts(5_662, 0).saturating_mul(r.into())) + // Minimum execution time: 1_704_000 picoseconds. + Weight::from_parts(2_054_652, 0) + // Standard Error: 2 + .saturating_add(Weight::from_parts(5_672, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 5000]`. fn instr_i64or(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_621_000 picoseconds. - Weight::from_parts(2_029_743, 0) - // Standard Error: 3 - .saturating_add(Weight::from_parts(5_762, 0).saturating_mul(r.into())) + // Minimum execution time: 1_743_000 picoseconds. + Weight::from_parts(2_032_806, 0) + // Standard Error: 19 + .saturating_add(Weight::from_parts(5_795, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 5000]`. fn instr_i64xor(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_641_000 picoseconds. - Weight::from_parts(2_127_132, 0) + // Minimum execution time: 1_667_000 picoseconds. + Weight::from_parts(2_031_702, 0) // Standard Error: 5 - .saturating_add(Weight::from_parts(5_818, 0).saturating_mul(r.into())) + .saturating_add(Weight::from_parts(5_923, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 5000]`. fn instr_i64shl(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_690_000 picoseconds. - Weight::from_parts(2_021_035, 0) - // Standard Error: 4 - .saturating_add(Weight::from_parts(5_917, 0).saturating_mul(r.into())) + // Minimum execution time: 1_735_000 picoseconds. + Weight::from_parts(2_946_634, 0) + // Standard Error: 54 + .saturating_add(Weight::from_parts(5_685, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 5000]`. fn instr_i64shrs(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_661_000 picoseconds. - Weight::from_parts(2_055_069, 0) - // Standard Error: 3 - .saturating_add(Weight::from_parts(6_094, 0).saturating_mul(r.into())) + // Minimum execution time: 1_652_000 picoseconds. + Weight::from_parts(2_023_049, 0) + // Standard Error: 4 + .saturating_add(Weight::from_parts(6_146, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 5000]`. fn instr_i64shru(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_677_000 picoseconds. - Weight::from_parts(2_024_748, 0) - // Standard Error: 2 - .saturating_add(Weight::from_parts(5_862, 0).saturating_mul(r.into())) + // Minimum execution time: 1_654_000 picoseconds. + Weight::from_parts(2_148_951, 0) + // Standard Error: 3 + .saturating_add(Weight::from_parts(5_869, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 5000]`. fn instr_i64rotl(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_651_000 picoseconds. - Weight::from_parts(2_005_814, 0) + // Minimum execution time: 1_730_000 picoseconds. + Weight::from_parts(2_130_543, 0) // Standard Error: 3 - .saturating_add(Weight::from_parts(6_007, 0).saturating_mul(r.into())) + .saturating_add(Weight::from_parts(5_999, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 5000]`. fn instr_i64rotr(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_703_000 picoseconds. - Weight::from_parts(2_019_636, 0) + // Minimum execution time: 1_728_000 picoseconds. + Weight::from_parts(2_172_886, 0) // Standard Error: 3 - .saturating_add(Weight::from_parts(5_862, 0).saturating_mul(r.into())) + .saturating_add(Weight::from_parts(5_843, 0).saturating_mul(r.into())) } } // For backwards compatibility and tests impl WeightInfo for () { - /// Storage: Contracts DeletionQueue (r:1 w:0) - /// Proof: Contracts DeletionQueue (max_values: Some(1), max_size: Some(16642), added: 17137, mode: Measured) + /// Storage: Contracts DeletionQueueCounter (r:1 w:0) + /// Proof: Contracts DeletionQueueCounter (max_values: Some(1), max_size: Some(8), added: 503, mode: Measured) fn on_process_deletion_queue_batch() -> Weight { // Proof Size summary in bytes: // Measured: `109` // Estimated: `1594` - // Minimum execution time: 2_713_000 picoseconds. - Weight::from_parts(2_811_000, 1594) + // Minimum execution time: 2_677_000 picoseconds. + Weight::from_parts(2_899_000, 1594) .saturating_add(RocksDbWeight::get().reads(1_u64)) } /// Storage: Skipped Metadata (r:0 w:0) @@ -2145,33 +2131,18 @@ impl WeightInfo for () { /// The range of component `k` is `[0, 1024]`. fn on_initialize_per_trie_key(k: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `450 + k * (69 ±0)` - // Estimated: `440 + k * (70 ±0)` - // Minimum execution time: 11_011_000 picoseconds. - Weight::from_parts(7_025_244, 440) - // Standard Error: 1_217 - .saturating_add(Weight::from_parts(980_818, 0).saturating_mul(k.into())) - .saturating_add(RocksDbWeight::get().reads(1_u64)) + // Measured: `488 + k * (69 ±0)` + // Estimated: `478 + k * (70 ±0)` + // Minimum execution time: 14_006_000 picoseconds. + Weight::from_parts(8_735_946, 478) + // Standard Error: 1_370 + .saturating_add(Weight::from_parts(989_501, 0).saturating_mul(k.into())) + .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().reads((1_u64).saturating_mul(k.into()))) - .saturating_add(RocksDbWeight::get().writes(1_u64)) + .saturating_add(RocksDbWeight::get().writes(2_u64)) .saturating_add(RocksDbWeight::get().writes((1_u64).saturating_mul(k.into()))) .saturating_add(Weight::from_parts(0, 70).saturating_mul(k.into())) } - /// Storage: Contracts DeletionQueue (r:1 w:1) - /// Proof: Contracts DeletionQueue (max_values: Some(1), max_size: Some(16642), added: 17137, mode: Measured) - /// The range of component `q` is `[0, 128]`. - fn on_initialize_per_queue_item(q: u32, ) -> Weight { - // Proof Size summary in bytes: - // Measured: `250 + q * (33 ±0)` - // Estimated: `1725 + q * (33 ±0)` - // Minimum execution time: 2_802_000 picoseconds. - Weight::from_parts(10_768_336, 1725) - // Standard Error: 3_424 - .saturating_add(Weight::from_parts(1_323_649, 0).saturating_mul(q.into())) - .saturating_add(RocksDbWeight::get().reads(1_u64)) - .saturating_add(RocksDbWeight::get().writes(1_u64)) - .saturating_add(Weight::from_parts(0, 33).saturating_mul(q.into())) - } /// Storage: Contracts PristineCode (r:1 w:0) /// Proof: Contracts PristineCode (max_values: None, max_size: Some(125988), added: 128463, mode: Measured) /// Storage: Contracts CodeStorage (r:0 w:1) @@ -2181,10 +2152,10 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `238 + c * (1 ±0)` // Estimated: `3951 + c * (2 ±0)` - // Minimum execution time: 30_299_000 picoseconds. - Weight::from_parts(24_608_986, 3951) - // Standard Error: 75 - .saturating_add(Weight::from_parts(54_619, 0).saturating_mul(c.into())) + // Minimum execution time: 30_951_000 picoseconds. + Weight::from_parts(25_988_560, 3951) + // Standard Error: 57 + .saturating_add(Weight::from_parts(54_692, 0).saturating_mul(c.into())) .saturating_add(RocksDbWeight::get().reads(1_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) .saturating_add(Weight::from_parts(0, 2).saturating_mul(c.into())) @@ -2204,10 +2175,10 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `707` // Estimated: `21400 + c * (5 ±0)` - // Minimum execution time: 265_835_000 picoseconds. - Weight::from_parts(275_985_164, 21400) - // Standard Error: 36 - .saturating_add(Weight::from_parts(37_980, 0).saturating_mul(c.into())) + // Minimum execution time: 263_107_000 picoseconds. + Weight::from_parts(268_289_665, 21400) + // Standard Error: 33 + .saturating_add(Weight::from_parts(38_534, 0).saturating_mul(c.into())) .saturating_add(RocksDbWeight::get().reads(6_u64)) .saturating_add(RocksDbWeight::get().writes(4_u64)) .saturating_add(Weight::from_parts(0, 5).saturating_mul(c.into())) @@ -2235,14 +2206,14 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `270` // Estimated: `26207` - // Minimum execution time: 3_124_508_000 picoseconds. - Weight::from_parts(617_467_897, 26207) - // Standard Error: 293 - .saturating_add(Weight::from_parts(106_971, 0).saturating_mul(c.into())) - // Standard Error: 17 - .saturating_add(Weight::from_parts(1_156, 0).saturating_mul(i.into())) - // Standard Error: 17 - .saturating_add(Weight::from_parts(1_395, 0).saturating_mul(s.into())) + // Minimum execution time: 3_122_319_000 picoseconds. + Weight::from_parts(487_802_180, 26207) + // Standard Error: 345 + .saturating_add(Weight::from_parts(108_237, 0).saturating_mul(c.into())) + // Standard Error: 20 + .saturating_add(Weight::from_parts(1_166, 0).saturating_mul(i.into())) + // Standard Error: 20 + .saturating_add(Weight::from_parts(1_505, 0).saturating_mul(s.into())) .saturating_add(RocksDbWeight::get().reads(9_u64)) .saturating_add(RocksDbWeight::get().writes(10_u64)) } @@ -2266,12 +2237,12 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `482` // Estimated: `28521` - // Minimum execution time: 1_649_483_000 picoseconds. - Weight::from_parts(287_642_416, 28521) + // Minimum execution time: 1_650_623_000 picoseconds. + Weight::from_parts(286_494_456, 28521) // Standard Error: 8 - .saturating_add(Weight::from_parts(1_450, 0).saturating_mul(i.into())) + .saturating_add(Weight::from_parts(1_438, 0).saturating_mul(i.into())) // Standard Error: 8 - .saturating_add(Weight::from_parts(1_443, 0).saturating_mul(s.into())) + .saturating_add(Weight::from_parts(1_453, 0).saturating_mul(s.into())) .saturating_add(RocksDbWeight::get().reads(9_u64)) .saturating_add(RocksDbWeight::get().writes(7_u64)) } @@ -2289,8 +2260,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `759` // Estimated: `21615` - // Minimum execution time: 192_302_000 picoseconds. - Weight::from_parts(193_192_000, 21615) + // Minimum execution time: 191_046_000 picoseconds. + Weight::from_parts(192_544_000, 21615) .saturating_add(RocksDbWeight::get().reads(6_u64)) .saturating_add(RocksDbWeight::get().writes(4_u64)) } @@ -2307,10 +2278,10 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `109` // Estimated: `7366` - // Minimum execution time: 246_401_000 picoseconds. - Weight::from_parts(261_505_456, 7366) - // Standard Error: 83 - .saturating_add(Weight::from_parts(109_136, 0).saturating_mul(c.into())) + // Minimum execution time: 244_260_000 picoseconds. + Weight::from_parts(254_693_985, 7366) + // Standard Error: 80 + .saturating_add(Weight::from_parts(108_246, 0).saturating_mul(c.into())) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().writes(4_u64)) } @@ -2326,8 +2297,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `255` // Estimated: `7950` - // Minimum execution time: 33_913_000 picoseconds. - Weight::from_parts(34_186_000, 7950) + // Minimum execution time: 33_492_000 picoseconds. + Weight::from_parts(34_079_000, 7950) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().writes(4_u64)) } @@ -2341,8 +2312,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `570` // Estimated: `19530` - // Minimum execution time: 33_801_000 picoseconds. - Weight::from_parts(34_877_000, 19530) + // Minimum execution time: 33_475_000 picoseconds. + Weight::from_parts(33_856_000, 19530) .saturating_add(RocksDbWeight::get().reads(6_u64)) .saturating_add(RocksDbWeight::get().writes(6_u64)) } @@ -2361,10 +2332,10 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `781 + r * (6 ±0)` // Estimated: `21730 + r * (30 ±0)` - // Minimum execution time: 237_679_000 picoseconds. - Weight::from_parts(243_022_905, 21730) - // Standard Error: 940 - .saturating_add(Weight::from_parts(324_389, 0).saturating_mul(r.into())) + // Minimum execution time: 234_197_000 picoseconds. + Weight::from_parts(236_830_305, 21730) + // Standard Error: 818 + .saturating_add(Weight::from_parts(336_446, 0).saturating_mul(r.into())) .saturating_add(RocksDbWeight::get().reads(6_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) .saturating_add(Weight::from_parts(0, 30).saturating_mul(r.into())) @@ -2384,10 +2355,10 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `839 + r * (240 ±0)` // Estimated: `21835 + r * (3675 ±0)` - // Minimum execution time: 235_635_000 picoseconds. - Weight::from_parts(76_942_144, 21835) - // Standard Error: 6_214 - .saturating_add(Weight::from_parts(3_328_756, 0).saturating_mul(r.into())) + // Minimum execution time: 235_872_000 picoseconds. + Weight::from_parts(78_877_890, 21835) + // Standard Error: 6_405 + .saturating_add(Weight::from_parts(3_358_341, 0).saturating_mul(r.into())) .saturating_add(RocksDbWeight::get().reads(6_u64)) .saturating_add(RocksDbWeight::get().reads((1_u64).saturating_mul(r.into()))) .saturating_add(RocksDbWeight::get().writes(3_u64)) @@ -2408,10 +2379,10 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `831 + r * (244 ±0)` // Estimated: `21855 + r * (3695 ±0)` - // Minimum execution time: 237_123_000 picoseconds. - Weight::from_parts(77_880_739, 21855) - // Standard Error: 5_970 - .saturating_add(Weight::from_parts(4_103_281, 0).saturating_mul(r.into())) + // Minimum execution time: 239_035_000 picoseconds. + Weight::from_parts(93_255_085, 21855) + // Standard Error: 5_922 + .saturating_add(Weight::from_parts(4_144_910, 0).saturating_mul(r.into())) .saturating_add(RocksDbWeight::get().reads(6_u64)) .saturating_add(RocksDbWeight::get().reads((1_u64).saturating_mul(r.into()))) .saturating_add(RocksDbWeight::get().writes(3_u64)) @@ -2432,10 +2403,10 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `788 + r * (6 ±0)` // Estimated: `21770 + r * (30 ±0)` - // Minimum execution time: 236_621_000 picoseconds. - Weight::from_parts(238_240_015, 21770) - // Standard Error: 742 - .saturating_add(Weight::from_parts(404_691, 0).saturating_mul(r.into())) + // Minimum execution time: 236_507_000 picoseconds. + Weight::from_parts(238_253_211, 21770) + // Standard Error: 975 + .saturating_add(Weight::from_parts(413_919, 0).saturating_mul(r.into())) .saturating_add(RocksDbWeight::get().reads(6_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) .saturating_add(Weight::from_parts(0, 30).saturating_mul(r.into())) @@ -2455,10 +2426,10 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `778 + r * (3 ±0)` // Estimated: `21735 + r * (15 ±0)` - // Minimum execution time: 233_274_000 picoseconds. - Weight::from_parts(239_596_227, 21735) - // Standard Error: 551 - .saturating_add(Weight::from_parts(164_429, 0).saturating_mul(r.into())) + // Minimum execution time: 235_521_000 picoseconds. + Weight::from_parts(238_397_362, 21735) + // Standard Error: 452 + .saturating_add(Weight::from_parts(160_860, 0).saturating_mul(r.into())) .saturating_add(RocksDbWeight::get().reads(6_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) .saturating_add(Weight::from_parts(0, 15).saturating_mul(r.into())) @@ -2478,10 +2449,10 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `782 + r * (6 ±0)` // Estimated: `21740 + r * (30 ±0)` - // Minimum execution time: 235_661_000 picoseconds. - Weight::from_parts(239_063_406, 21740) - // Standard Error: 933 - .saturating_add(Weight::from_parts(327_679, 0).saturating_mul(r.into())) + // Minimum execution time: 234_722_000 picoseconds. + Weight::from_parts(242_936_096, 21740) + // Standard Error: 1_178 + .saturating_add(Weight::from_parts(329_075, 0).saturating_mul(r.into())) .saturating_add(RocksDbWeight::get().reads(6_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) .saturating_add(Weight::from_parts(0, 30).saturating_mul(r.into())) @@ -2501,10 +2472,10 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `783 + r * (6 ±0)` // Estimated: `21725 + r * (30 ±0)` - // Minimum execution time: 235_583_000 picoseconds. - Weight::from_parts(251_641_549, 21725) - // Standard Error: 1_104 - .saturating_add(Weight::from_parts(315_873, 0).saturating_mul(r.into())) + // Minimum execution time: 235_654_000 picoseconds. + Weight::from_parts(245_887_792, 21725) + // Standard Error: 903 + .saturating_add(Weight::from_parts(325_168, 0).saturating_mul(r.into())) .saturating_add(RocksDbWeight::get().reads(6_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) .saturating_add(Weight::from_parts(0, 30).saturating_mul(r.into())) @@ -2524,10 +2495,10 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `922 + r * (6 ±0)` // Estimated: `24633 + r * (30 ±0)` - // Minimum execution time: 235_325_000 picoseconds. - Weight::from_parts(256_582_010, 24633) - // Standard Error: 1_349 - .saturating_add(Weight::from_parts(1_483_116, 0).saturating_mul(r.into())) + // Minimum execution time: 233_599_000 picoseconds. + Weight::from_parts(251_561_602, 24633) + // Standard Error: 3_348 + .saturating_add(Weight::from_parts(1_475_443, 0).saturating_mul(r.into())) .saturating_add(RocksDbWeight::get().reads(7_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) .saturating_add(Weight::from_parts(0, 30).saturating_mul(r.into())) @@ -2547,10 +2518,10 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `792 + r * (6 ±0)` // Estimated: `21825 + r * (30 ±0)` - // Minimum execution time: 235_358_000 picoseconds. - Weight::from_parts(233_421_484, 21825) - // Standard Error: 1_178 - .saturating_add(Weight::from_parts(337_947, 0).saturating_mul(r.into())) + // Minimum execution time: 235_087_000 picoseconds. + Weight::from_parts(235_855_322, 21825) + // Standard Error: 867 + .saturating_add(Weight::from_parts(346_707, 0).saturating_mul(r.into())) .saturating_add(RocksDbWeight::get().reads(6_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) .saturating_add(Weight::from_parts(0, 30).saturating_mul(r.into())) @@ -2570,10 +2541,10 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `790 + r * (6 ±0)` // Estimated: `21815 + r * (30 ±0)` - // Minimum execution time: 236_570_000 picoseconds. - Weight::from_parts(245_853_078, 21815) - // Standard Error: 1_947 - .saturating_add(Weight::from_parts(319_972, 0).saturating_mul(r.into())) + // Minimum execution time: 237_103_000 picoseconds. + Weight::from_parts(239_272_188, 21815) + // Standard Error: 892 + .saturating_add(Weight::from_parts(328_334, 0).saturating_mul(r.into())) .saturating_add(RocksDbWeight::get().reads(6_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) .saturating_add(Weight::from_parts(0, 30).saturating_mul(r.into())) @@ -2593,10 +2564,10 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `787 + r * (6 ±0)` // Estimated: `21805 + r * (30 ±0)` - // Minimum execution time: 235_027_000 picoseconds. - Weight::from_parts(239_719_689, 21805) - // Standard Error: 654 - .saturating_add(Weight::from_parts(326_988, 0).saturating_mul(r.into())) + // Minimum execution time: 234_761_000 picoseconds. + Weight::from_parts(238_601_784, 21805) + // Standard Error: 725 + .saturating_add(Weight::from_parts(325_758, 0).saturating_mul(r.into())) .saturating_add(RocksDbWeight::get().reads(6_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) .saturating_add(Weight::from_parts(0, 30).saturating_mul(r.into())) @@ -2616,10 +2587,10 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `778 + r * (6 ±0)` // Estimated: `21735 + r * (30 ±0)` - // Minimum execution time: 236_547_000 picoseconds. - Weight::from_parts(239_390_326, 21735) - // Standard Error: 912 - .saturating_add(Weight::from_parts(327_495, 0).saturating_mul(r.into())) + // Minimum execution time: 235_249_000 picoseconds. + Weight::from_parts(239_861_242, 21735) + // Standard Error: 751 + .saturating_add(Weight::from_parts(325_795, 0).saturating_mul(r.into())) .saturating_add(RocksDbWeight::get().reads(6_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) .saturating_add(Weight::from_parts(0, 30).saturating_mul(r.into())) @@ -2641,10 +2612,10 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `856 + r * (10 ±0)` // Estimated: `24446 + r * (60 ±0)` - // Minimum execution time: 235_710_000 picoseconds. - Weight::from_parts(238_998_789, 24446) - // Standard Error: 2_055 - .saturating_add(Weight::from_parts(1_373_992, 0).saturating_mul(r.into())) + // Minimum execution time: 234_912_000 picoseconds. + Weight::from_parts(254_783_734, 24446) + // Standard Error: 1_610 + .saturating_add(Weight::from_parts(1_307_506, 0).saturating_mul(r.into())) .saturating_add(RocksDbWeight::get().reads(7_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) .saturating_add(Weight::from_parts(0, 60).saturating_mul(r.into())) @@ -2664,10 +2635,10 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `745 + r * (4 ±0)` // Estimated: `21555 + r * (20 ±0)` - // Minimum execution time: 161_133_000 picoseconds. - Weight::from_parts(167_097_346, 21555) - // Standard Error: 245 - .saturating_add(Weight::from_parts(128_503, 0).saturating_mul(r.into())) + // Minimum execution time: 160_125_000 picoseconds. + Weight::from_parts(164_915_574, 21555) + // Standard Error: 332 + .saturating_add(Weight::from_parts(132_326, 0).saturating_mul(r.into())) .saturating_add(RocksDbWeight::get().reads(6_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) .saturating_add(Weight::from_parts(0, 20).saturating_mul(r.into())) @@ -2687,10 +2658,10 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `780 + r * (6 ±0)` // Estimated: `21740 + r * (30 ±0)` - // Minimum execution time: 234_790_000 picoseconds. - Weight::from_parts(242_392_710, 21740) - // Standard Error: 2_506 - .saturating_add(Weight::from_parts(273_470, 0).saturating_mul(r.into())) + // Minimum execution time: 234_717_000 picoseconds. + Weight::from_parts(238_540_521, 21740) + // Standard Error: 599 + .saturating_add(Weight::from_parts(277_303, 0).saturating_mul(r.into())) .saturating_add(RocksDbWeight::get().reads(6_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) .saturating_add(Weight::from_parts(0, 30).saturating_mul(r.into())) @@ -2710,10 +2681,10 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `784` // Estimated: `21740` - // Minimum execution time: 236_837_000 picoseconds. - Weight::from_parts(237_860_073, 21740) - // Standard Error: 2 - .saturating_add(Weight::from_parts(602, 0).saturating_mul(n.into())) + // Minimum execution time: 235_792_000 picoseconds. + Weight::from_parts(244_114_692, 21740) + // Standard Error: 1 + .saturating_add(Weight::from_parts(589, 0).saturating_mul(n.into())) .saturating_add(RocksDbWeight::get().reads(6_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) } @@ -2732,10 +2703,10 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `768 + r * (45 ±0)` // Estimated: `21660 + r * (225 ±0)` - // Minimum execution time: 232_047_000 picoseconds. - Weight::from_parts(234_629_293, 21660) - // Standard Error: 171_808 - .saturating_add(Weight::from_parts(663_306, 0).saturating_mul(r.into())) + // Minimum execution time: 231_166_000 picoseconds. + Weight::from_parts(233_339_177, 21660) + // Standard Error: 155_889 + .saturating_add(Weight::from_parts(3_124_322, 0).saturating_mul(r.into())) .saturating_add(RocksDbWeight::get().reads(6_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) .saturating_add(Weight::from_parts(0, 225).saturating_mul(r.into())) @@ -2755,10 +2726,10 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `778` // Estimated: `21775` - // Minimum execution time: 235_035_000 picoseconds. - Weight::from_parts(234_442_091, 21775) + // Minimum execution time: 235_721_000 picoseconds. + Weight::from_parts(237_413_703, 21775) // Standard Error: 1 - .saturating_add(Weight::from_parts(185, 0).saturating_mul(n.into())) + .saturating_add(Weight::from_parts(177, 0).saturating_mul(n.into())) .saturating_add(RocksDbWeight::get().reads(6_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) } @@ -2770,26 +2741,28 @@ impl WeightInfo for () { /// Proof: Contracts CodeStorage (max_values: None, max_size: Some(126001), added: 128476, mode: Measured) /// Storage: Timestamp Now (r:1 w:0) /// Proof: Timestamp Now (max_values: Some(1), max_size: Some(8), added: 503, mode: Measured) - /// Storage: Contracts DeletionQueue (r:1 w:1) - /// Proof: Contracts DeletionQueue (max_values: Some(1), max_size: Some(16642), added: 17137, mode: Measured) + /// Storage: Contracts DeletionQueueCounter (r:1 w:1) + /// Proof: Contracts DeletionQueueCounter (max_values: Some(1), max_size: Some(8), added: 503, mode: Measured) /// Storage: Contracts OwnerInfoOf (r:1 w:1) /// Proof: Contracts OwnerInfoOf (max_values: None, max_size: Some(88), added: 2563, mode: Measured) /// Storage: System EventTopics (r:3 w:3) /// Proof Skipped: System EventTopics (max_values: None, max_size: None, mode: Measured) + /// Storage: Contracts DeletionQueue (r:0 w:1) + /// Proof: Contracts DeletionQueue (max_values: None, max_size: Some(142), added: 2617, mode: Measured) /// The range of component `r` is `[0, 1]`. fn seal_terminate(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `810 + r * (356 ±0)` - // Estimated: `25511 + r * (15321 ±0)` - // Minimum execution time: 234_140_000 picoseconds. - Weight::from_parts(236_805_906, 25511) - // Standard Error: 435_181 - .saturating_add(Weight::from_parts(118_144_693, 0).saturating_mul(r.into())) + // Estimated: `26094 + r * (15904 ±0)` + // Minimum execution time: 233_525_000 picoseconds. + Weight::from_parts(235_871_034, 26094) + // Standard Error: 235_338 + .saturating_add(Weight::from_parts(118_659_865, 0).saturating_mul(r.into())) .saturating_add(RocksDbWeight::get().reads(6_u64)) .saturating_add(RocksDbWeight::get().reads((6_u64).saturating_mul(r.into()))) .saturating_add(RocksDbWeight::get().writes(3_u64)) - .saturating_add(RocksDbWeight::get().writes((7_u64).saturating_mul(r.into()))) - .saturating_add(Weight::from_parts(0, 15321).saturating_mul(r.into())) + .saturating_add(RocksDbWeight::get().writes((8_u64).saturating_mul(r.into()))) + .saturating_add(Weight::from_parts(0, 15904).saturating_mul(r.into())) } /// Storage: System Account (r:1 w:0) /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: Measured) @@ -2808,10 +2781,10 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `825 + r * (10 ±0)` // Estimated: `24283 + r * (60 ±0)` - // Minimum execution time: 235_271_000 picoseconds. - Weight::from_parts(256_019_682, 24283) - // Standard Error: 2_016 - .saturating_add(Weight::from_parts(1_862_085, 0).saturating_mul(r.into())) + // Minimum execution time: 235_007_000 picoseconds. + Weight::from_parts(248_419_686, 24283) + // Standard Error: 1_847 + .saturating_add(Weight::from_parts(1_815_822, 0).saturating_mul(r.into())) .saturating_add(RocksDbWeight::get().reads(7_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) .saturating_add(Weight::from_parts(0, 60).saturating_mul(r.into())) @@ -2831,10 +2804,10 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `778 + r * (10 ±0)` // Estimated: `21735 + r * (50 ±0)` - // Minimum execution time: 233_092_000 picoseconds. - Weight::from_parts(248_483_473, 21735) - // Standard Error: 2_182 - .saturating_add(Weight::from_parts(3_551_674, 0).saturating_mul(r.into())) + // Minimum execution time: 232_912_000 picoseconds. + Weight::from_parts(256_142_885, 21735) + // Standard Error: 2_741 + .saturating_add(Weight::from_parts(3_542_482, 0).saturating_mul(r.into())) .saturating_add(RocksDbWeight::get().reads(6_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) .saturating_add(Weight::from_parts(0, 50).saturating_mul(r.into())) @@ -2855,12 +2828,12 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `797 + t * (32 ±0)` // Estimated: `21840 + t * (2640 ±0)` - // Minimum execution time: 252_307_000 picoseconds. - Weight::from_parts(245_237_726, 21840) - // Standard Error: 79_824 - .saturating_add(Weight::from_parts(2_364_618, 0).saturating_mul(t.into())) - // Standard Error: 22 - .saturating_add(Weight::from_parts(636, 0).saturating_mul(n.into())) + // Minimum execution time: 251_018_000 picoseconds. + Weight::from_parts(245_280_765, 21840) + // Standard Error: 90_317 + .saturating_add(Weight::from_parts(2_434_496, 0).saturating_mul(t.into())) + // Standard Error: 25 + .saturating_add(Weight::from_parts(599, 0).saturating_mul(n.into())) .saturating_add(RocksDbWeight::get().reads(6_u64)) .saturating_add(RocksDbWeight::get().reads((1_u64).saturating_mul(t.into()))) .saturating_add(RocksDbWeight::get().writes(3_u64)) @@ -2882,10 +2855,10 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `777 + r * (7 ±0)` // Estimated: `21725 + r * (35 ±0)` - // Minimum execution time: 165_367_000 picoseconds. - Weight::from_parts(170_164_725, 21725) - // Standard Error: 487 - .saturating_add(Weight::from_parts(237_281, 0).saturating_mul(r.into())) + // Minimum execution time: 164_022_000 picoseconds. + Weight::from_parts(168_658_387, 21725) + // Standard Error: 685 + .saturating_add(Weight::from_parts(238_133, 0).saturating_mul(r.into())) .saturating_add(RocksDbWeight::get().reads(6_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) .saturating_add(Weight::from_parts(0, 35).saturating_mul(r.into())) @@ -2905,10 +2878,10 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `125728` // Estimated: `269977` - // Minimum execution time: 350_914_000 picoseconds. - Weight::from_parts(354_461_646, 269977) - // Standard Error: 1 - .saturating_add(Weight::from_parts(747, 0).saturating_mul(i.into())) + // Minimum execution time: 351_043_000 picoseconds. + Weight::from_parts(353_707_344, 269977) + // Standard Error: 3 + .saturating_add(Weight::from_parts(752, 0).saturating_mul(i.into())) .saturating_add(RocksDbWeight::get().reads(6_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) } @@ -2919,10 +2892,10 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `845 + r * (292 ±0)` // Estimated: `843 + r * (293 ±0)` - // Minimum execution time: 236_273_000 picoseconds. - Weight::from_parts(137_922_946, 843) - // Standard Error: 10_363 - .saturating_add(Weight::from_parts(6_034_776, 0).saturating_mul(r.into())) + // Minimum execution time: 235_854_000 picoseconds. + Weight::from_parts(133_986_225, 843) + // Standard Error: 9_550 + .saturating_add(Weight::from_parts(6_093_051, 0).saturating_mul(r.into())) .saturating_add(RocksDbWeight::get().reads(6_u64)) .saturating_add(RocksDbWeight::get().reads((1_u64).saturating_mul(r.into()))) .saturating_add(RocksDbWeight::get().writes(3_u64)) @@ -2936,10 +2909,10 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `1304` // Estimated: `1280` - // Minimum execution time: 251_462_000 picoseconds. - Weight::from_parts(287_009_907, 1280) - // Standard Error: 62 - .saturating_add(Weight::from_parts(384, 0).saturating_mul(n.into())) + // Minimum execution time: 252_321_000 picoseconds. + Weight::from_parts(285_820_577, 1280) + // Standard Error: 60 + .saturating_add(Weight::from_parts(600, 0).saturating_mul(n.into())) .saturating_add(RocksDbWeight::get().reads(8_u64)) .saturating_add(RocksDbWeight::get().writes(6_u64)) } @@ -2950,10 +2923,10 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `1167 + n * (1 ±0)` // Estimated: `1167 + n * (1 ±0)` - // Minimum execution time: 250_985_000 picoseconds. - Weight::from_parts(253_693_249, 1167) - // Standard Error: 21 - .saturating_add(Weight::from_parts(92, 0).saturating_mul(n.into())) + // Minimum execution time: 252_047_000 picoseconds. + Weight::from_parts(254_244_310, 1167) + // Standard Error: 15 + .saturating_add(Weight::from_parts(144, 0).saturating_mul(n.into())) .saturating_add(RocksDbWeight::get().reads(7_u64)) .saturating_add(RocksDbWeight::get().writes(4_u64)) .saturating_add(Weight::from_parts(0, 1).saturating_mul(n.into())) @@ -2965,10 +2938,10 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `841 + r * (288 ±0)` // Estimated: `845 + r * (289 ±0)` - // Minimum execution time: 235_462_000 picoseconds. - Weight::from_parts(141_240_297, 845) - // Standard Error: 9_687 - .saturating_add(Weight::from_parts(5_906_737, 0).saturating_mul(r.into())) + // Minimum execution time: 235_697_000 picoseconds. + Weight::from_parts(143_200_942, 845) + // Standard Error: 11_358 + .saturating_add(Weight::from_parts(5_934_318, 0).saturating_mul(r.into())) .saturating_add(RocksDbWeight::get().reads(6_u64)) .saturating_add(RocksDbWeight::get().reads((1_u64).saturating_mul(r.into()))) .saturating_add(RocksDbWeight::get().writes(3_u64)) @@ -2982,10 +2955,10 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `1163 + n * (1 ±0)` // Estimated: `1163 + n * (1 ±0)` - // Minimum execution time: 250_887_000 picoseconds. - Weight::from_parts(253_321_064, 1163) - // Standard Error: 28 - .saturating_add(Weight::from_parts(169, 0).saturating_mul(n.into())) + // Minimum execution time: 250_360_000 picoseconds. + Weight::from_parts(252_712_722, 1163) + // Standard Error: 15 + .saturating_add(Weight::from_parts(130, 0).saturating_mul(n.into())) .saturating_add(RocksDbWeight::get().reads(7_u64)) .saturating_add(RocksDbWeight::get().writes(4_u64)) .saturating_add(Weight::from_parts(0, 1).saturating_mul(n.into())) @@ -2997,10 +2970,10 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `835 + r * (296 ±0)` // Estimated: `840 + r * (297 ±0)` - // Minimum execution time: 236_542_000 picoseconds. - Weight::from_parts(147_992_508, 840) - // Standard Error: 8_849 - .saturating_add(Weight::from_parts(4_946_692, 0).saturating_mul(r.into())) + // Minimum execution time: 235_613_000 picoseconds. + Weight::from_parts(150_213_792, 840) + // Standard Error: 8_617 + .saturating_add(Weight::from_parts(4_962_874, 0).saturating_mul(r.into())) .saturating_add(RocksDbWeight::get().reads(6_u64)) .saturating_add(RocksDbWeight::get().reads((1_u64).saturating_mul(r.into()))) .saturating_add(RocksDbWeight::get().writes(3_u64)) @@ -3013,10 +2986,10 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `1179 + n * (1 ±0)` // Estimated: `1179 + n * (1 ±0)` - // Minimum execution time: 249_987_000 picoseconds. - Weight::from_parts(254_866_627, 1179) - // Standard Error: 53 - .saturating_add(Weight::from_parts(554, 0).saturating_mul(n.into())) + // Minimum execution time: 249_137_000 picoseconds. + Weight::from_parts(253_529_386, 1179) + // Standard Error: 41 + .saturating_add(Weight::from_parts(612, 0).saturating_mul(n.into())) .saturating_add(RocksDbWeight::get().reads(7_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) .saturating_add(Weight::from_parts(0, 1).saturating_mul(n.into())) @@ -3028,10 +3001,10 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `856 + r * (288 ±0)` // Estimated: `857 + r * (289 ±0)` - // Minimum execution time: 236_635_000 picoseconds. - Weight::from_parts(157_805_789, 857) - // Standard Error: 7_699 - .saturating_add(Weight::from_parts(4_709_422, 0).saturating_mul(r.into())) + // Minimum execution time: 235_659_000 picoseconds. + Weight::from_parts(158_846_683, 857) + // Standard Error: 7_806 + .saturating_add(Weight::from_parts(4_728_537, 0).saturating_mul(r.into())) .saturating_add(RocksDbWeight::get().reads(6_u64)) .saturating_add(RocksDbWeight::get().reads((1_u64).saturating_mul(r.into()))) .saturating_add(RocksDbWeight::get().writes(3_u64)) @@ -3044,10 +3017,10 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `1166 + n * (1 ±0)` // Estimated: `1166 + n * (1 ±0)` - // Minimum execution time: 252_660_000 picoseconds. - Weight::from_parts(255_250_747, 1166) - // Standard Error: 14 - .saturating_add(Weight::from_parts(133, 0).saturating_mul(n.into())) + // Minimum execution time: 248_553_000 picoseconds. + Weight::from_parts(250_703_269, 1166) + // Standard Error: 17 + .saturating_add(Weight::from_parts(163, 0).saturating_mul(n.into())) .saturating_add(RocksDbWeight::get().reads(7_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) .saturating_add(Weight::from_parts(0, 1).saturating_mul(n.into())) @@ -3059,10 +3032,10 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `829 + r * (296 ±0)` // Estimated: `836 + r * (297 ±0)` - // Minimum execution time: 240_198_000 picoseconds. - Weight::from_parts(133_188_357, 836) - // Standard Error: 10_661 - .saturating_add(Weight::from_parts(6_147_538, 0).saturating_mul(r.into())) + // Minimum execution time: 236_431_000 picoseconds. + Weight::from_parts(131_745_419, 836) + // Standard Error: 10_161 + .saturating_add(Weight::from_parts(6_182_174, 0).saturating_mul(r.into())) .saturating_add(RocksDbWeight::get().reads(6_u64)) .saturating_add(RocksDbWeight::get().reads((1_u64).saturating_mul(r.into()))) .saturating_add(RocksDbWeight::get().writes(3_u64)) @@ -3076,10 +3049,10 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `1180 + n * (1 ±0)` // Estimated: `1180 + n * (1 ±0)` - // Minimum execution time: 252_131_000 picoseconds. - Weight::from_parts(259_960_286, 1180) - // Standard Error: 121 - .saturating_add(Weight::from_parts(192, 0).saturating_mul(n.into())) + // Minimum execution time: 252_551_000 picoseconds. + Weight::from_parts(254_517_030, 1180) + // Standard Error: 16 + .saturating_add(Weight::from_parts(712, 0).saturating_mul(n.into())) .saturating_add(RocksDbWeight::get().reads(7_u64)) .saturating_add(RocksDbWeight::get().writes(4_u64)) .saturating_add(Weight::from_parts(0, 1).saturating_mul(n.into())) @@ -3099,10 +3072,10 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `1373 + r * (45 ±0)` // Estimated: `26753 + r * (2700 ±0)` - // Minimum execution time: 235_860_000 picoseconds. - Weight::from_parts(124_993_651, 26753) - // Standard Error: 22_811 - .saturating_add(Weight::from_parts(36_467_740, 0).saturating_mul(r.into())) + // Minimum execution time: 236_663_000 picoseconds. + Weight::from_parts(237_485_000, 26753) + // Standard Error: 42_414 + .saturating_add(Weight::from_parts(36_849_514, 0).saturating_mul(r.into())) .saturating_add(RocksDbWeight::get().reads(7_u64)) .saturating_add(RocksDbWeight::get().reads((1_u64).saturating_mul(r.into()))) .saturating_add(RocksDbWeight::get().writes(4_u64)) @@ -3124,10 +3097,10 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `1237 + r * (256 ±0)` // Estimated: `26028 + r * (6235 ±0)` - // Minimum execution time: 237_221_000 picoseconds. - Weight::from_parts(237_632_000, 26028) - // Standard Error: 89_502 - .saturating_add(Weight::from_parts(212_211_534, 0).saturating_mul(r.into())) + // Minimum execution time: 237_101_000 picoseconds. + Weight::from_parts(237_827_000, 26028) + // Standard Error: 82_878 + .saturating_add(Weight::from_parts(211_777_724, 0).saturating_mul(r.into())) .saturating_add(RocksDbWeight::get().reads(7_u64)) .saturating_add(RocksDbWeight::get().reads((2_u64).saturating_mul(r.into()))) .saturating_add(RocksDbWeight::get().writes(3_u64)) @@ -3149,10 +3122,10 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `0 + r * (502 ±0)` // Estimated: `21755 + r * (6329 ±3)` - // Minimum execution time: 236_965_000 picoseconds. - Weight::from_parts(238_110_000, 21755) - // Standard Error: 101_332 - .saturating_add(Weight::from_parts(206_790_203, 0).saturating_mul(r.into())) + // Minimum execution time: 241_213_000 picoseconds. + Weight::from_parts(241_900_000, 21755) + // Standard Error: 99_976 + .saturating_add(Weight::from_parts(207_520_077, 0).saturating_mul(r.into())) .saturating_add(RocksDbWeight::get().reads(6_u64)) .saturating_add(RocksDbWeight::get().reads((2_u64).saturating_mul(r.into()))) .saturating_add(RocksDbWeight::get().writes(3_u64)) @@ -3175,12 +3148,12 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `1154 + t * (204 ±0)` // Estimated: `31015 + t * (5970 ±0)` - // Minimum execution time: 411_974_000 picoseconds. - Weight::from_parts(391_387_689, 31015) - // Standard Error: 1_320_695 - .saturating_add(Weight::from_parts(29_766_122, 0).saturating_mul(t.into())) + // Minimum execution time: 414_784_000 picoseconds. + Weight::from_parts(384_902_379, 31015) + // Standard Error: 1_228_593 + .saturating_add(Weight::from_parts(33_226_901, 0).saturating_mul(t.into())) // Standard Error: 1 - .saturating_add(Weight::from_parts(597, 0).saturating_mul(c.into())) + .saturating_add(Weight::from_parts(601, 0).saturating_mul(c.into())) .saturating_add(RocksDbWeight::get().reads(9_u64)) .saturating_add(RocksDbWeight::get().reads((2_u64).saturating_mul(t.into()))) .saturating_add(RocksDbWeight::get().writes(5_u64)) @@ -3206,10 +3179,10 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `1301 + r * (254 ±0)` // Estimated: `30977 + r * (16635 ±0)` - // Minimum execution time: 237_595_000 picoseconds. - Weight::from_parts(238_068_000, 30977) - // Standard Error: 254_409 - .saturating_add(Weight::from_parts(346_436_154, 0).saturating_mul(r.into())) + // Minimum execution time: 236_963_000 picoseconds. + Weight::from_parts(237_711_000, 30977) + // Standard Error: 265_576 + .saturating_add(Weight::from_parts(347_359_908, 0).saturating_mul(r.into())) .saturating_add(RocksDbWeight::get().reads(8_u64)) .saturating_add(RocksDbWeight::get().reads((6_u64).saturating_mul(r.into()))) .saturating_add(RocksDbWeight::get().writes(6_u64)) @@ -3237,14 +3210,14 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `1071 + t * (187 ±0)` // Estimated: `42684 + t * (3588 ±2)` - // Minimum execution time: 1_616_768_000 picoseconds. - Weight::from_parts(363_003_254, 42684) - // Standard Error: 4_398_669 - .saturating_add(Weight::from_parts(104_529_967, 0).saturating_mul(t.into())) + // Minimum execution time: 1_615_191_000 picoseconds. + Weight::from_parts(337_408_450, 42684) + // Standard Error: 4_581_951 + .saturating_add(Weight::from_parts(115_730_776, 0).saturating_mul(t.into())) // Standard Error: 7 - .saturating_add(Weight::from_parts(1_162, 0).saturating_mul(i.into())) + .saturating_add(Weight::from_parts(1_171, 0).saturating_mul(i.into())) // Standard Error: 7 - .saturating_add(Weight::from_parts(1_333, 0).saturating_mul(s.into())) + .saturating_add(Weight::from_parts(1_346, 0).saturating_mul(s.into())) .saturating_add(RocksDbWeight::get().reads(13_u64)) .saturating_add(RocksDbWeight::get().reads((1_u64).saturating_mul(t.into()))) .saturating_add(RocksDbWeight::get().writes(10_u64)) @@ -3266,10 +3239,10 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `777 + r * (8 ±0)` // Estimated: `21710 + r * (40 ±0)` - // Minimum execution time: 233_814_000 picoseconds. - Weight::from_parts(241_291_041, 21710) - // Standard Error: 1_422 - .saturating_add(Weight::from_parts(575_846, 0).saturating_mul(r.into())) + // Minimum execution time: 233_601_000 picoseconds. + Weight::from_parts(246_594_905, 21710) + // Standard Error: 2_840 + .saturating_add(Weight::from_parts(578_751, 0).saturating_mul(r.into())) .saturating_add(RocksDbWeight::get().reads(6_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) .saturating_add(Weight::from_parts(0, 40).saturating_mul(r.into())) @@ -3289,10 +3262,10 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `785` // Estimated: `21745` - // Minimum execution time: 236_572_000 picoseconds. - Weight::from_parts(235_648_055, 21745) + // Minimum execution time: 233_735_000 picoseconds. + Weight::from_parts(243_432_330, 21745) // Standard Error: 3 - .saturating_add(Weight::from_parts(3_947, 0).saturating_mul(n.into())) + .saturating_add(Weight::from_parts(3_927, 0).saturating_mul(n.into())) .saturating_add(RocksDbWeight::get().reads(6_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) } @@ -3311,10 +3284,10 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `779 + r * (8 ±0)` // Estimated: `21725 + r * (40 ±0)` - // Minimum execution time: 234_473_000 picoseconds. - Weight::from_parts(239_805_309, 21725) - // Standard Error: 1_113 - .saturating_add(Weight::from_parts(752_507, 0).saturating_mul(r.into())) + // Minimum execution time: 232_173_000 picoseconds. + Weight::from_parts(239_806_011, 21725) + // Standard Error: 1_530 + .saturating_add(Weight::from_parts(749_641, 0).saturating_mul(r.into())) .saturating_add(RocksDbWeight::get().reads(6_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) .saturating_add(Weight::from_parts(0, 40).saturating_mul(r.into())) @@ -3334,10 +3307,10 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `787` // Estimated: `21765` - // Minimum execution time: 235_209_000 picoseconds. - Weight::from_parts(228_548_524, 21765) + // Minimum execution time: 235_046_000 picoseconds. + Weight::from_parts(229_500_792, 21765) // Standard Error: 2 - .saturating_add(Weight::from_parts(3_171, 0).saturating_mul(n.into())) + .saturating_add(Weight::from_parts(3_166, 0).saturating_mul(n.into())) .saturating_add(RocksDbWeight::get().reads(6_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) } @@ -3356,10 +3329,10 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `779 + r * (8 ±0)` // Estimated: `21740 + r * (40 ±0)` - // Minimum execution time: 233_282_000 picoseconds. - Weight::from_parts(240_864_680, 21740) - // Standard Error: 958 - .saturating_add(Weight::from_parts(418_308, 0).saturating_mul(r.into())) + // Minimum execution time: 231_708_000 picoseconds. + Weight::from_parts(235_347_566, 21740) + // Standard Error: 987 + .saturating_add(Weight::from_parts(428_819, 0).saturating_mul(r.into())) .saturating_add(RocksDbWeight::get().reads(6_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) .saturating_add(Weight::from_parts(0, 40).saturating_mul(r.into())) @@ -3379,10 +3352,10 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `787` // Estimated: `21785` - // Minimum execution time: 234_667_000 picoseconds. - Weight::from_parts(227_810_077, 21785) - // Standard Error: 2 - .saturating_add(Weight::from_parts(925, 0).saturating_mul(n.into())) + // Minimum execution time: 234_068_000 picoseconds. + Weight::from_parts(226_519_852, 21785) + // Standard Error: 1 + .saturating_add(Weight::from_parts(916, 0).saturating_mul(n.into())) .saturating_add(RocksDbWeight::get().reads(6_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) } @@ -3401,10 +3374,10 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `779 + r * (8 ±0)` // Estimated: `21745 + r * (40 ±0)` - // Minimum execution time: 234_040_000 picoseconds. - Weight::from_parts(237_970_694, 21745) - // Standard Error: 979 - .saturating_add(Weight::from_parts(416_562, 0).saturating_mul(r.into())) + // Minimum execution time: 231_872_000 picoseconds. + Weight::from_parts(236_694_763, 21745) + // Standard Error: 870 + .saturating_add(Weight::from_parts(420_853, 0).saturating_mul(r.into())) .saturating_add(RocksDbWeight::get().reads(6_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) .saturating_add(Weight::from_parts(0, 40).saturating_mul(r.into())) @@ -3424,10 +3397,10 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `787` // Estimated: `21755` - // Minimum execution time: 234_840_000 picoseconds. - Weight::from_parts(227_849_778, 21755) + // Minimum execution time: 233_707_000 picoseconds. + Weight::from_parts(226_312_559, 21755) // Standard Error: 2 - .saturating_add(Weight::from_parts(923, 0).saturating_mul(n.into())) + .saturating_add(Weight::from_parts(924, 0).saturating_mul(n.into())) .saturating_add(RocksDbWeight::get().reads(6_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) } @@ -3446,10 +3419,10 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `822 + r * (76 ±0)` // Estimated: `21705 + r * (385 ±0)` - // Minimum execution time: 236_174_000 picoseconds. - Weight::from_parts(252_457_690, 21705) - // Standard Error: 20_130 - .saturating_add(Weight::from_parts(37_792_805, 0).saturating_mul(r.into())) + // Minimum execution time: 234_962_000 picoseconds. + Weight::from_parts(252_611_292, 21705) + // Standard Error: 20_134 + .saturating_add(Weight::from_parts(37_745_358, 0).saturating_mul(r.into())) .saturating_add(RocksDbWeight::get().reads(6_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) .saturating_add(Weight::from_parts(0, 385).saturating_mul(r.into())) @@ -3468,11 +3441,11 @@ impl WeightInfo for () { fn seal_ecdsa_to_eth_address(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `792 + r * (42 ±0)` - // Estimated: `21780 + r * (210 ±0)` - // Minimum execution time: 236_251_000 picoseconds. - Weight::from_parts(242_254_305, 21780) - // Standard Error: 9_765 - .saturating_add(Weight::from_parts(9_341_334, 0).saturating_mul(r.into())) + // Estimated: `21775 + r * (210 ±0)` + // Minimum execution time: 234_869_000 picoseconds. + Weight::from_parts(240_188_331, 21775) + // Standard Error: 9_910 + .saturating_add(Weight::from_parts(9_332_432, 0).saturating_mul(r.into())) .saturating_add(RocksDbWeight::get().reads(6_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) .saturating_add(Weight::from_parts(0, 210).saturating_mul(r.into())) @@ -3493,11 +3466,11 @@ impl WeightInfo for () { fn seal_set_code_hash(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0 + r * (964 ±0)` - // Estimated: `29920 + r * (11544 ±7)` - // Minimum execution time: 236_462_000 picoseconds. - Weight::from_parts(236_997_000, 29920) - // Standard Error: 46_527 - .saturating_add(Weight::from_parts(21_858_761, 0).saturating_mul(r.into())) + // Estimated: `29920 + r * (11544 ±10)` + // Minimum execution time: 235_036_000 picoseconds. + Weight::from_parts(235_538_000, 29920) + // Standard Error: 47_360 + .saturating_add(Weight::from_parts(22_113_144, 0).saturating_mul(r.into())) .saturating_add(RocksDbWeight::get().reads(6_u64)) .saturating_add(RocksDbWeight::get().reads((3_u64).saturating_mul(r.into()))) .saturating_add(RocksDbWeight::get().writes(3_u64)) @@ -3519,10 +3492,10 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `773 + r * (3 ±0)` // Estimated: `21735 + r * (15 ±0)` - // Minimum execution time: 235_085_000 picoseconds. - Weight::from_parts(239_410_836, 21735) - // Standard Error: 414 - .saturating_add(Weight::from_parts(167_067, 0).saturating_mul(r.into())) + // Minimum execution time: 237_884_000 picoseconds. + Weight::from_parts(243_315_095, 21735) + // Standard Error: 560 + .saturating_add(Weight::from_parts(162_206, 0).saturating_mul(r.into())) .saturating_add(RocksDbWeight::get().reads(6_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) .saturating_add(Weight::from_parts(0, 15).saturating_mul(r.into())) @@ -3542,10 +3515,10 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `1975 + r * (39 ±0)` // Estimated: `27145 + r * (200 ±0)` - // Minimum execution time: 236_690_000 picoseconds. - Weight::from_parts(268_793_030, 27145) - // Standard Error: 1_210 - .saturating_add(Weight::from_parts(263_330, 0).saturating_mul(r.into())) + // Minimum execution time: 239_402_000 picoseconds. + Weight::from_parts(267_214_783, 27145) + // Standard Error: 1_166 + .saturating_add(Weight::from_parts(261_915, 0).saturating_mul(r.into())) .saturating_add(RocksDbWeight::get().reads(6_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) .saturating_add(Weight::from_parts(0, 200).saturating_mul(r.into())) @@ -3567,10 +3540,10 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `776 + r * (3 ±0)` // Estimated: `24004 + r * (18 ±0)` - // Minimum execution time: 236_289_000 picoseconds. - Weight::from_parts(246_581_099, 24004) - // Standard Error: 1_300 - .saturating_add(Weight::from_parts(137_499, 0).saturating_mul(r.into())) + // Minimum execution time: 233_153_000 picoseconds. + Weight::from_parts(238_999_965, 24004) + // Standard Error: 291 + .saturating_add(Weight::from_parts(143_971, 0).saturating_mul(r.into())) .saturating_add(RocksDbWeight::get().reads(7_u64)) .saturating_add(RocksDbWeight::get().writes(4_u64)) .saturating_add(Weight::from_parts(0, 18).saturating_mul(r.into())) @@ -3580,9 +3553,9 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_679_000 picoseconds. - Weight::from_parts(1_934_194, 0) - // Standard Error: 1 + // Minimum execution time: 1_706_000 picoseconds. + Weight::from_parts(1_962_558, 0) + // Standard Error: 2 .saturating_add(Weight::from_parts(3_000, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 5000]`. @@ -3590,499 +3563,499 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_872_000 picoseconds. - Weight::from_parts(2_605_712, 0) - // Standard Error: 43 - .saturating_add(Weight::from_parts(6_321, 0).saturating_mul(r.into())) + // Minimum execution time: 1_870_000 picoseconds. + Weight::from_parts(2_481_243, 0) + // Standard Error: 4 + .saturating_add(Weight::from_parts(6_346, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 5000]`. fn instr_i64store(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_837_000 picoseconds. - Weight::from_parts(2_035_143, 0) - // Standard Error: 65 - .saturating_add(Weight::from_parts(6_202, 0).saturating_mul(r.into())) + // Minimum execution time: 1_830_000 picoseconds. + Weight::from_parts(2_389_658, 0) + // Standard Error: 4 + .saturating_add(Weight::from_parts(5_997, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 5000]`. fn instr_select(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_684_000 picoseconds. - Weight::from_parts(2_044_218, 0) - // Standard Error: 5 - .saturating_add(Weight::from_parts(7_929, 0).saturating_mul(r.into())) + // Minimum execution time: 1_734_000 picoseconds. + Weight::from_parts(2_170_618, 0) + // Standard Error: 4 + .saturating_add(Weight::from_parts(7_919, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 5000]`. fn instr_if(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_635_000 picoseconds. - Weight::from_parts(2_009_851, 0) - // Standard Error: 6 - .saturating_add(Weight::from_parts(10_724, 0).saturating_mul(r.into())) + // Minimum execution time: 1_661_000 picoseconds. + Weight::from_parts(1_945_771, 0) + // Standard Error: 11 + .saturating_add(Weight::from_parts(10_840, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 5000]`. fn instr_br(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_667_000 picoseconds. - Weight::from_parts(1_869_395, 0) - // Standard Error: 12 - .saturating_add(Weight::from_parts(4_618, 0).saturating_mul(r.into())) + // Minimum execution time: 1_682_000 picoseconds. + Weight::from_parts(1_970_774, 0) + // Standard Error: 11 + .saturating_add(Weight::from_parts(4_569, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 5000]`. fn instr_br_if(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_698_000 picoseconds. - Weight::from_parts(2_184_182, 0) - // Standard Error: 16 - .saturating_add(Weight::from_parts(6_833, 0).saturating_mul(r.into())) + // Minimum execution time: 1_668_000 picoseconds. + Weight::from_parts(1_227_080, 0) + // Standard Error: 76 + .saturating_add(Weight::from_parts(8_066, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 5000]`. fn instr_br_table(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_670_000 picoseconds. - Weight::from_parts(1_471_988, 0) - // Standard Error: 30 - .saturating_add(Weight::from_parts(9_550, 0).saturating_mul(r.into())) + // Minimum execution time: 1_653_000 picoseconds. + Weight::from_parts(1_394_759, 0) + // Standard Error: 39 + .saturating_add(Weight::from_parts(9_566, 0).saturating_mul(r.into())) } /// The range of component `e` is `[1, 256]`. fn instr_br_table_per_entry(e: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_778_000 picoseconds. - Weight::from_parts(1_925_086, 0) - // Standard Error: 136 - .saturating_add(Weight::from_parts(502, 0).saturating_mul(e.into())) + // Minimum execution time: 1_771_000 picoseconds. + Weight::from_parts(1_905_594, 0) + // Standard Error: 147 + .saturating_add(Weight::from_parts(925, 0).saturating_mul(e.into())) } /// The range of component `r` is `[0, 5000]`. fn instr_call(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_759_000 picoseconds. - Weight::from_parts(2_372_048, 0) - // Standard Error: 16 - .saturating_add(Weight::from_parts(17_953, 0).saturating_mul(r.into())) + // Minimum execution time: 1_863_000 picoseconds. + Weight::from_parts(2_472_635, 0) + // Standard Error: 13 + .saturating_add(Weight::from_parts(17_892, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 5000]`. fn instr_call_indirect(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_852_000 picoseconds. - Weight::from_parts(3_125_003, 0) - // Standard Error: 25 - .saturating_add(Weight::from_parts(24_218, 0).saturating_mul(r.into())) + // Minimum execution time: 2_013_000 picoseconds. + Weight::from_parts(3_077_100, 0) + // Standard Error: 18 + .saturating_add(Weight::from_parts(24_287, 0).saturating_mul(r.into())) } /// The range of component `l` is `[0, 1024]`. fn instr_call_per_local(l: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_795_000 picoseconds. - Weight::from_parts(2_121_763, 0) - // Standard Error: 45 - .saturating_add(Weight::from_parts(1_207, 0).saturating_mul(l.into())) + // Minimum execution time: 1_818_000 picoseconds. + Weight::from_parts(2_109_143, 0) + // Standard Error: 34 + .saturating_add(Weight::from_parts(1_249, 0).saturating_mul(l.into())) } /// The range of component `r` is `[0, 5000]`. fn instr_local_get(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 3_019_000 picoseconds. - Weight::from_parts(3_267_108, 0) - // Standard Error: 55 - .saturating_add(Weight::from_parts(2_527, 0).saturating_mul(r.into())) + // Minimum execution time: 3_083_000 picoseconds. + Weight::from_parts(3_291_328, 0) + // Standard Error: 1 + .saturating_add(Weight::from_parts(2_505, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 5000]`. fn instr_local_set(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 2_944_000 picoseconds. - Weight::from_parts(3_183_331, 0) + // Minimum execution time: 2_987_000 picoseconds. + Weight::from_parts(3_276_863, 0) // Standard Error: 2 - .saturating_add(Weight::from_parts(3_618, 0).saturating_mul(r.into())) + .saturating_add(Weight::from_parts(3_617, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 5000]`. fn instr_local_tee(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 3_009_000 picoseconds. - Weight::from_parts(3_178_158, 0) - // Standard Error: 41 - .saturating_add(Weight::from_parts(4_075, 0).saturating_mul(r.into())) + // Minimum execution time: 2_942_000 picoseconds. + Weight::from_parts(3_350_581, 0) + // Standard Error: 3 + .saturating_add(Weight::from_parts(3_976, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 5000]`. fn instr_global_get(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_748_000 picoseconds. - Weight::from_parts(2_371_911, 0) - // Standard Error: 10 - .saturating_add(Weight::from_parts(8_378, 0).saturating_mul(r.into())) + // Minimum execution time: 1_867_000 picoseconds. + Weight::from_parts(2_920_748, 0) + // Standard Error: 44 + .saturating_add(Weight::from_parts(8_229, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 5000]`. fn instr_global_set(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_795_000 picoseconds. - Weight::from_parts(1_311_997, 0) - // Standard Error: 157 - .saturating_add(Weight::from_parts(9_410, 0).saturating_mul(r.into())) + // Minimum execution time: 1_757_000 picoseconds. + Weight::from_parts(2_235_198, 0) + // Standard Error: 4 + .saturating_add(Weight::from_parts(8_815, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 5000]`. fn instr_memory_current(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 2_059_000 picoseconds. - Weight::from_parts(2_416_611, 0) - // Standard Error: 5 - .saturating_add(Weight::from_parts(3_775, 0).saturating_mul(r.into())) + // Minimum execution time: 1_824_000 picoseconds. + Weight::from_parts(1_941_816, 0) + // Standard Error: 86 + .saturating_add(Weight::from_parts(4_043, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 16]`. fn instr_memory_grow(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_764_000 picoseconds. - Weight::from_parts(1_414_442, 0) - // Standard Error: 142_321 - .saturating_add(Weight::from_parts(13_210_495, 0).saturating_mul(r.into())) + // Minimum execution time: 1_793_000 picoseconds. + Weight::from_parts(1_104_829, 0) + // Standard Error: 137_800 + .saturating_add(Weight::from_parts(13_336_784, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 5000]`. fn instr_i64clz(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_665_000 picoseconds. - Weight::from_parts(2_047_968, 0) + // Minimum execution time: 1_693_000 picoseconds. + Weight::from_parts(2_037_305, 0) // Standard Error: 2 - .saturating_add(Weight::from_parts(4_035, 0).saturating_mul(r.into())) + .saturating_add(Weight::from_parts(4_044, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 5000]`. fn instr_i64ctz(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_696_000 picoseconds. - Weight::from_parts(2_101_548, 0) - // Standard Error: 13 - .saturating_add(Weight::from_parts(3_754, 0).saturating_mul(r.into())) + // Minimum execution time: 1_751_000 picoseconds. + Weight::from_parts(2_082_016, 0) + // Standard Error: 2 + .saturating_add(Weight::from_parts(3_767, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 5000]`. fn instr_i64popcnt(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_686_000 picoseconds. - Weight::from_parts(2_023_482, 0) + // Minimum execution time: 1_751_000 picoseconds. + Weight::from_parts(2_110_625, 0) // Standard Error: 2 - .saturating_add(Weight::from_parts(3_770, 0).saturating_mul(r.into())) + .saturating_add(Weight::from_parts(3_754, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 5000]`. fn instr_i64eqz(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_700_000 picoseconds. - Weight::from_parts(2_035_759, 0) + // Minimum execution time: 1_756_000 picoseconds. + Weight::from_parts(2_100_327, 0) // Standard Error: 2 - .saturating_add(Weight::from_parts(3_660, 0).saturating_mul(r.into())) + .saturating_add(Weight::from_parts(3_664, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 5000]`. fn instr_i64extendsi32(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_682_000 picoseconds. - Weight::from_parts(2_015_828, 0) - // Standard Error: 2 - .saturating_add(Weight::from_parts(3_977, 0).saturating_mul(r.into())) + // Minimum execution time: 1_643_000 picoseconds. + Weight::from_parts(2_169_153, 0) + // Standard Error: 3 + .saturating_add(Weight::from_parts(3_961, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 5000]`. fn instr_i64extendui32(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_660_000 picoseconds. - Weight::from_parts(2_032_387, 0) + // Minimum execution time: 1_704_000 picoseconds. + Weight::from_parts(2_049_172, 0) // Standard Error: 2 - .saturating_add(Weight::from_parts(3_826, 0).saturating_mul(r.into())) + .saturating_add(Weight::from_parts(3_833, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 5000]`. fn instr_i32wrapi64(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_635_000 picoseconds. - Weight::from_parts(2_013_228, 0) - // Standard Error: 3 - .saturating_add(Weight::from_parts(3_752, 0).saturating_mul(r.into())) + // Minimum execution time: 1_726_000 picoseconds. + Weight::from_parts(2_064_387, 0) + // Standard Error: 2 + .saturating_add(Weight::from_parts(3_745, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 5000]`. fn instr_i64eq(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_698_000 picoseconds. - Weight::from_parts(2_844_817, 0) + // Minimum execution time: 1_696_000 picoseconds. + Weight::from_parts(2_426_905, 0) // Standard Error: 56 - .saturating_add(Weight::from_parts(5_746, 0).saturating_mul(r.into())) + .saturating_add(Weight::from_parts(5_915, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 5000]`. fn instr_i64ne(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_686_000 picoseconds. - Weight::from_parts(2_208_884, 0) - // Standard Error: 138 - .saturating_add(Weight::from_parts(6_032, 0).saturating_mul(r.into())) + // Minimum execution time: 1_617_000 picoseconds. + Weight::from_parts(2_035_161, 0) + // Standard Error: 94 + .saturating_add(Weight::from_parts(6_073, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 5000]`. fn instr_i64lts(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_695_000 picoseconds. - Weight::from_parts(2_060_880, 0) - // Standard Error: 3 - .saturating_add(Weight::from_parts(5_966, 0).saturating_mul(r.into())) + // Minimum execution time: 1_756_000 picoseconds. + Weight::from_parts(2_098_926, 0) + // Standard Error: 2 + .saturating_add(Weight::from_parts(6_002, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 5000]`. fn instr_i64ltu(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_700_000 picoseconds. - Weight::from_parts(2_143_484, 0) - // Standard Error: 3 - .saturating_add(Weight::from_parts(5_932, 0).saturating_mul(r.into())) + // Minimum execution time: 1_706_000 picoseconds. + Weight::from_parts(2_257_972, 0) + // Standard Error: 23 + .saturating_add(Weight::from_parts(5_982, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 5000]`. fn instr_i64gts(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_677_000 picoseconds. - Weight::from_parts(2_081_646, 0) - // Standard Error: 3 - .saturating_add(Weight::from_parts(5_813, 0).saturating_mul(r.into())) + // Minimum execution time: 1_761_000 picoseconds. + Weight::from_parts(2_114_141, 0) + // Standard Error: 2 + .saturating_add(Weight::from_parts(5_815, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 5000]`. fn instr_i64gtu(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 2_476_000 picoseconds. - Weight::from_parts(2_161_801, 0) - // Standard Error: 4 - .saturating_add(Weight::from_parts(6_078, 0).saturating_mul(r.into())) + // Minimum execution time: 1_700_000 picoseconds. + Weight::from_parts(2_053_201, 0) + // Standard Error: 2 + .saturating_add(Weight::from_parts(6_137, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 5000]`. fn instr_i64les(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_677_000 picoseconds. - Weight::from_parts(2_043_451, 0) + // Minimum execution time: 1_705_000 picoseconds. + Weight::from_parts(2_101_782, 0) // Standard Error: 3 - .saturating_add(Weight::from_parts(5_988, 0).saturating_mul(r.into())) + .saturating_add(Weight::from_parts(6_014, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 5000]`. fn instr_i64leu(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_694_000 picoseconds. - Weight::from_parts(2_058_196, 0) - // Standard Error: 2 - .saturating_add(Weight::from_parts(6_058, 0).saturating_mul(r.into())) + // Minimum execution time: 1_856_000 picoseconds. + Weight::from_parts(2_149_707, 0) + // Standard Error: 3 + .saturating_add(Weight::from_parts(6_086, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 5000]`. fn instr_i64ges(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_675_000 picoseconds. - Weight::from_parts(2_036_798, 0) - // Standard Error: 2 - .saturating_add(Weight::from_parts(5_956, 0).saturating_mul(r.into())) + // Minimum execution time: 1_739_000 picoseconds. + Weight::from_parts(2_143_216, 0) + // Standard Error: 3 + .saturating_add(Weight::from_parts(5_934, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 5000]`. fn instr_i64geu(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_712_000 picoseconds. - Weight::from_parts(2_121_407, 0) - // Standard Error: 2 - .saturating_add(Weight::from_parts(5_944, 0).saturating_mul(r.into())) + // Minimum execution time: 1_716_000 picoseconds. + Weight::from_parts(2_065_762, 0) + // Standard Error: 3 + .saturating_add(Weight::from_parts(6_009, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 5000]`. fn instr_i64add(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_675_000 picoseconds. - Weight::from_parts(2_061_053, 0) - // Standard Error: 6 - .saturating_add(Weight::from_parts(5_823, 0).saturating_mul(r.into())) + // Minimum execution time: 1_664_000 picoseconds. + Weight::from_parts(3_062_283, 0) + // Standard Error: 94 + .saturating_add(Weight::from_parts(5_645, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 5000]`. fn instr_i64sub(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_686_000 picoseconds. - Weight::from_parts(2_023_347, 0) - // Standard Error: 4 - .saturating_add(Weight::from_parts(6_126, 0).saturating_mul(r.into())) + // Minimum execution time: 1_671_000 picoseconds. + Weight::from_parts(2_011_145, 0) + // Standard Error: 44 + .saturating_add(Weight::from_parts(6_220, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 5000]`. fn instr_i64mul(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_678_000 picoseconds. - Weight::from_parts(2_390_920, 0) - // Standard Error: 55 - .saturating_add(Weight::from_parts(5_635, 0).saturating_mul(r.into())) + // Minimum execution time: 1_759_000 picoseconds. + Weight::from_parts(2_095_420, 0) + // Standard Error: 4 + .saturating_add(Weight::from_parts(5_723, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 5000]`. fn instr_i64divs(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_728_000 picoseconds. - Weight::from_parts(1_944_457, 0) - // Standard Error: 7 - .saturating_add(Weight::from_parts(11_848, 0).saturating_mul(r.into())) + // Minimum execution time: 1_672_000 picoseconds. + Weight::from_parts(2_184_044, 0) + // Standard Error: 5 + .saturating_add(Weight::from_parts(11_782, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 5000]`. fn instr_i64divu(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_649_000 picoseconds. - Weight::from_parts(1_881_148, 0) - // Standard Error: 6 - .saturating_add(Weight::from_parts(10_618, 0).saturating_mul(r.into())) + // Minimum execution time: 1_752_000 picoseconds. + Weight::from_parts(2_276_209, 0) + // Standard Error: 5 + .saturating_add(Weight::from_parts(10_513, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 5000]`. fn instr_i64rems(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_708_000 picoseconds. - Weight::from_parts(1_767_912, 0) - // Standard Error: 6 - .saturating_add(Weight::from_parts(12_099, 0).saturating_mul(r.into())) + // Minimum execution time: 1_711_000 picoseconds. + Weight::from_parts(2_720_989, 0) + // Standard Error: 24 + .saturating_add(Weight::from_parts(11_884, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 5000]`. fn instr_i64remu(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_650_000 picoseconds. - Weight::from_parts(1_998_575, 0) + // Minimum execution time: 1_713_000 picoseconds. + Weight::from_parts(2_091_403, 0) // Standard Error: 3 - .saturating_add(Weight::from_parts(10_632, 0).saturating_mul(r.into())) + .saturating_add(Weight::from_parts(10_628, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 5000]`. fn instr_i64and(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_633_000 picoseconds. - Weight::from_parts(2_029_981, 0) - // Standard Error: 3 - .saturating_add(Weight::from_parts(5_662, 0).saturating_mul(r.into())) + // Minimum execution time: 1_704_000 picoseconds. + Weight::from_parts(2_054_652, 0) + // Standard Error: 2 + .saturating_add(Weight::from_parts(5_672, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 5000]`. fn instr_i64or(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_621_000 picoseconds. - Weight::from_parts(2_029_743, 0) - // Standard Error: 3 - .saturating_add(Weight::from_parts(5_762, 0).saturating_mul(r.into())) + // Minimum execution time: 1_743_000 picoseconds. + Weight::from_parts(2_032_806, 0) + // Standard Error: 19 + .saturating_add(Weight::from_parts(5_795, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 5000]`. fn instr_i64xor(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_641_000 picoseconds. - Weight::from_parts(2_127_132, 0) + // Minimum execution time: 1_667_000 picoseconds. + Weight::from_parts(2_031_702, 0) // Standard Error: 5 - .saturating_add(Weight::from_parts(5_818, 0).saturating_mul(r.into())) + .saturating_add(Weight::from_parts(5_923, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 5000]`. fn instr_i64shl(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_690_000 picoseconds. - Weight::from_parts(2_021_035, 0) - // Standard Error: 4 - .saturating_add(Weight::from_parts(5_917, 0).saturating_mul(r.into())) + // Minimum execution time: 1_735_000 picoseconds. + Weight::from_parts(2_946_634, 0) + // Standard Error: 54 + .saturating_add(Weight::from_parts(5_685, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 5000]`. fn instr_i64shrs(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_661_000 picoseconds. - Weight::from_parts(2_055_069, 0) - // Standard Error: 3 - .saturating_add(Weight::from_parts(6_094, 0).saturating_mul(r.into())) + // Minimum execution time: 1_652_000 picoseconds. + Weight::from_parts(2_023_049, 0) + // Standard Error: 4 + .saturating_add(Weight::from_parts(6_146, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 5000]`. fn instr_i64shru(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_677_000 picoseconds. - Weight::from_parts(2_024_748, 0) - // Standard Error: 2 - .saturating_add(Weight::from_parts(5_862, 0).saturating_mul(r.into())) + // Minimum execution time: 1_654_000 picoseconds. + Weight::from_parts(2_148_951, 0) + // Standard Error: 3 + .saturating_add(Weight::from_parts(5_869, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 5000]`. fn instr_i64rotl(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_651_000 picoseconds. - Weight::from_parts(2_005_814, 0) + // Minimum execution time: 1_730_000 picoseconds. + Weight::from_parts(2_130_543, 0) // Standard Error: 3 - .saturating_add(Weight::from_parts(6_007, 0).saturating_mul(r.into())) + .saturating_add(Weight::from_parts(5_999, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 5000]`. fn instr_i64rotr(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_703_000 picoseconds. - Weight::from_parts(2_019_636, 0) + // Minimum execution time: 1_728_000 picoseconds. + Weight::from_parts(2_172_886, 0) // Standard Error: 3 - .saturating_add(Weight::from_parts(5_862, 0).saturating_mul(r.into())) + .saturating_add(Weight::from_parts(5_843, 0).saturating_mul(r.into())) } } From a7bc9c2a8c0a14fd6033de1f972a11a2b3dbb73a Mon Sep 17 00:00:00 2001 From: asynchronous rob Date: Fri, 31 Mar 2023 05:00:07 -0700 Subject: [PATCH 324/558] Refactor: extract most aura logic out to standalone module, make use of these (#13764) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Extract most aura logic out to standalone module, make use of these * Update client/consensus/aura/src/standalone.rs improve docs Co-authored-by: Bastian Köcher * add slot_duration_at * ".git/.scripts/commands/fmt/fmt.sh" --------- Co-authored-by: Bastian Köcher Co-authored-by: parity-processbot <> --- client/consensus/aura/src/import_queue.rs | 46 ++- client/consensus/aura/src/lib.rs | 127 +------- client/consensus/aura/src/standalone.rs | 357 ++++++++++++++++++++++ 3 files changed, 393 insertions(+), 137 deletions(-) create mode 100644 client/consensus/aura/src/standalone.rs diff --git a/client/consensus/aura/src/import_queue.rs b/client/consensus/aura/src/import_queue.rs index 46e0ccb4e..ef7a2a1cc 100644 --- a/client/consensus/aura/src/import_queue.rs +++ b/client/consensus/aura/src/import_queue.rs @@ -19,7 +19,7 @@ //! Module implementing the logic for verifying and importing AuRa blocks. use crate::{ - aura_err, authorities, find_pre_digest, slot_author, AuthorityId, CompatibilityMode, Error, + authorities, standalone::SealVerificationError, AuthorityId, CompatibilityMode, Error, LOG_TARGET, }; use codec::{Codec, Decode, Encode}; @@ -36,7 +36,7 @@ use sp_api::{ApiExt, ProvideRuntimeApi}; use sp_block_builder::BlockBuilder as BlockBuilderApi; use sp_blockchain::HeaderBackend; use sp_consensus::Error as ConsensusError; -use sp_consensus_aura::{digests::CompatibleDigestItem, inherents::AuraInherentData, AuraApi}; +use sp_consensus_aura::{inherents::AuraInherentData, AuraApi}; use sp_consensus_slots::Slot; use sp_core::{crypto::Pair, ExecutionContext}; use sp_inherents::{CreateInherentDataProviders, InherentDataProvider as _}; @@ -54,7 +54,7 @@ use std::{fmt::Debug, hash::Hash, marker::PhantomData, sync::Arc}; fn check_header( client: &C, slot_now: Slot, - mut header: B::Header, + header: B::Header, hash: B::Hash, authorities: &[AuthorityId

], check_for_equivocation: CheckForEquivocation, @@ -64,27 +64,16 @@ where C: sc_client_api::backend::AuxStore, P::Public: Encode + Decode + PartialEq + Clone, { - let seal = header.digest_mut().pop().ok_or(Error::HeaderUnsealed(hash))?; - - let sig = seal.as_aura_seal().ok_or_else(|| aura_err(Error::HeaderBadSeal(hash)))?; - - let slot = find_pre_digest::(&header)?; - - if slot > slot_now { - header.digest_mut().push(seal); - Ok(CheckedHeader::Deferred(header, slot)) - } else { - // check the signature is valid under the expected authority and - // chain state. - let expected_author = - slot_author::

(slot, authorities).ok_or(Error::SlotAuthorNotFound)?; - - let pre_hash = header.hash(); - - if P::verify(&sig, pre_hash.as_ref(), expected_author) { - if check_for_equivocation.check_for_equivocation() { + let check_result = + crate::standalone::check_header_slot_and_seal::(slot_now, header, authorities); + + match check_result { + Ok((header, slot, seal)) => { + let expected_author = crate::standalone::slot_author::

(slot, &authorities); + let should_equiv_check = check_for_equivocation.check_for_equivocation(); + if let (true, Some(expected)) = (should_equiv_check, expected_author) { if let Some(equivocation_proof) = - check_equivocation(client, slot_now, slot, &header, expected_author) + check_equivocation(client, slot_now, slot, &header, expected) .map_err(Error::Client)? { info!( @@ -98,9 +87,14 @@ where } Ok(CheckedHeader::Checked(header, (slot, seal))) - } else { - Err(Error::BadSignature(hash)) - } + }, + Err(SealVerificationError::Deferred(header, slot)) => + Ok(CheckedHeader::Deferred(header, slot)), + Err(SealVerificationError::Unsealed) => Err(Error::HeaderUnsealed(hash)), + Err(SealVerificationError::BadSeal) => Err(Error::HeaderBadSeal(hash)), + Err(SealVerificationError::BadSignature) => Err(Error::BadSignature(hash)), + Err(SealVerificationError::SlotAuthorNotFound) => Err(Error::SlotAuthorNotFound), + Err(SealVerificationError::InvalidPreDigest(e)) => Err(Error::from(e)), } } diff --git a/client/consensus/aura/src/lib.rs b/client/consensus/aura/src/lib.rs index a48eeac5c..1dc364283 100644 --- a/client/consensus/aura/src/lib.rs +++ b/client/consensus/aura/src/lib.rs @@ -33,11 +33,10 @@ use std::{fmt::Debug, hash::Hash, marker::PhantomData, pin::Pin, sync::Arc}; use futures::prelude::*; -use log::{debug, trace}; use codec::{Codec, Decode, Encode}; -use sc_client_api::{backend::AuxStore, BlockOf, UsageProvider}; +use sc_client_api::{backend::AuxStore, BlockOf}; use sc_consensus::{BlockImport, BlockImportParams, ForkChoiceStrategy, StateAction}; use sc_consensus_slots::{ BackoffAuthoringBlocksStrategy, InherentDataProviderExt, SimpleSlotWorkerToSlotWorker, @@ -45,20 +44,19 @@ use sc_consensus_slots::{ }; use sc_telemetry::TelemetryHandle; use sp_api::{Core, ProvideRuntimeApi}; -use sp_application_crypto::{AppCrypto, AppPublic}; -use sp_blockchain::{HeaderBackend, Result as CResult}; +use sp_application_crypto::AppPublic; +use sp_blockchain::HeaderBackend; use sp_consensus::{BlockOrigin, Environment, Error as ConsensusError, Proposer, SelectChain}; use sp_consensus_slots::Slot; -use sp_core::crypto::{ByteArray, Pair, Public}; +use sp_core::crypto::{Pair, Public}; use sp_inherents::CreateInherentDataProviders; use sp_keystore::KeystorePtr; -use sp_runtime::{ - traits::{Block as BlockT, Header, Member, NumberFor, Zero}, - DigestItem, -}; +use sp_runtime::traits::{Block as BlockT, Header, Member, NumberFor}; mod import_queue; +pub mod standalone; +pub use crate::standalone::{find_pre_digest, slot_duration}; pub use import_queue::{ build_verifier, import_queue, AuraVerifier, BuildVerifierParams, CheckForEquivocation, ImportQueueParams, @@ -112,39 +110,6 @@ impl Default for CompatibilityMode { } } -/// Get the slot duration for Aura. -pub fn slot_duration(client: &C) -> CResult -where - A: Codec, - B: BlockT, - C: AuxStore + ProvideRuntimeApi + UsageProvider, - C::Api: AuraApi, -{ - client - .runtime_api() - .slot_duration(client.usage_info().chain.best_hash) - .map_err(|err| err.into()) -} - -/// Get slot author for given block along with authorities. -fn slot_author(slot: Slot, authorities: &[AuthorityId

]) -> Option<&AuthorityId

> { - if authorities.is_empty() { - return None - } - - let idx = *slot % (authorities.len() as u64); - assert!( - idx <= usize::MAX as u64, - "It is impossible to have a vector with length beyond the address space; qed", - ); - - let current_author = authorities.get(idx as usize).expect( - "authorities not empty; index constrained to list length;this is a valid index; qed", - ); - - Some(current_author) -} - /// Parameters of [`start_aura`]. pub struct StartAuraParams { /// The duration of a slot. @@ -412,21 +377,11 @@ where slot: Slot, authorities: &Self::AuxData, ) -> Option { - let expected_author = slot_author::

(slot, authorities); - expected_author.and_then(|p| { - if self - .keystore - .has_keys(&[(p.to_raw_vec(), sp_application_crypto::key_types::AURA)]) - { - Some(p.clone()) - } else { - None - } - }) + crate::standalone::claim_slot::

(slot, authorities, &self.keystore).await } fn pre_digest_data(&self, slot: Slot, _claim: &Self::Claim) -> Vec { - vec![>::aura_pre_digest(slot)] + vec![crate::standalone::pre_digest::

(slot)] } async fn block_import_params( @@ -441,28 +396,8 @@ where sc_consensus::BlockImportParams>::Transaction>, ConsensusError, > { - let signature = self - .keystore - .sign_with( - as AppCrypto>::ID, - as AppCrypto>::CRYPTO_ID, - public.as_slice(), - header_hash.as_ref(), - ) - .map_err(|e| ConsensusError::CannotSign(format!("{}. Key: {:?}", e, public)))? - .ok_or_else(|| { - ConsensusError::CannotSign(format!( - "Could not find key in keystore. Key: {:?}", - public - )) - })?; - let signature = signature - .clone() - .try_into() - .map_err(|_| ConsensusError::InvalidSignature(signature, public.to_raw_vec()))?; - let signature_digest_item = - >::aura_seal(signature); + crate::standalone::seal::<_, P>(header_hash, &public, &self.keystore)?; let mut import_block = BlockImportParams::new(BlockOrigin::Own, header); import_block.post_digests.push(signature_digest_item); @@ -526,11 +461,6 @@ where } } -fn aura_err(error: Error) -> Error { - debug!(target: LOG_TARGET, "{}", error); - error -} - /// Aura Errors #[derive(Debug, thiserror::Error)] pub enum Error { @@ -569,22 +499,13 @@ impl From> for String { } } -/// Get pre-digests from the header -pub fn find_pre_digest(header: &B::Header) -> Result> { - if header.number().is_zero() { - return Ok(0.into()) - } - - let mut pre_digest: Option = None; - for log in header.digest().logs() { - trace!(target: LOG_TARGET, "Checking log {:?}", log); - match (CompatibleDigestItem::::as_aura_pre_digest(log), pre_digest.is_some()) { - (Some(_), true) => return Err(aura_err(Error::MultipleHeaders)), - (None, _) => trace!(target: LOG_TARGET, "Ignoring digest not meant for us"), - (s, false) => pre_digest = s, +impl From for Error { + fn from(e: crate::standalone::PreDigestLookupError) -> Self { + match e { + crate::standalone::PreDigestLookupError::MultipleHeaders => Error::MultipleHeaders, + crate::standalone::PreDigestLookupError::NoDigestFound => Error::NoDigestFound, } } - pre_digest.ok_or_else(|| aura_err(Error::NoDigestFound)) } fn authorities( @@ -637,7 +558,7 @@ mod tests { use sc_consensus_slots::{BackoffAuthoringOnFinalizedHeadLagging, SimpleSlotWorker}; use sc_keystore::LocalKeystore; use sc_network_test::{Block as TestBlock, *}; - use sp_application_crypto::key_types::AURA; + use sp_application_crypto::{key_types::AURA, AppCrypto}; use sp_consensus::{DisableProofRecording, NoNetwork as DummyOracle, Proposal}; use sp_consensus_aura::sr25519::AuthorityPair; use sp_inherents::InherentData; @@ -851,22 +772,6 @@ mod tests { .await; } - #[test] - fn authorities_call_works() { - let client = substrate_test_runtime_client::new(); - - assert_eq!(client.chain_info().best_number, 0); - assert_eq!( - authorities(&client, client.chain_info().best_hash, 1, &CompatibilityMode::None) - .unwrap(), - vec![ - Keyring::Alice.public().into(), - Keyring::Bob.public().into(), - Keyring::Charlie.public().into() - ] - ); - } - #[tokio::test] async fn current_node_authority_should_claim_slot() { let net = AuraTestNet::new(4); diff --git a/client/consensus/aura/src/standalone.rs b/client/consensus/aura/src/standalone.rs new file mode 100644 index 000000000..0f9b8668d --- /dev/null +++ b/client/consensus/aura/src/standalone.rs @@ -0,0 +1,357 @@ +// This file is part of Substrate. + +// Copyright (C) Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +//! Standalone functions used within the implementation of Aura. + +use std::fmt::Debug; + +use log::trace; + +use codec::Codec; + +use sc_client_api::{backend::AuxStore, UsageProvider}; +use sp_api::{Core, ProvideRuntimeApi}; +use sp_application_crypto::{AppCrypto, AppPublic}; +use sp_blockchain::Result as CResult; +use sp_consensus::Error as ConsensusError; +use sp_consensus_slots::Slot; +use sp_core::crypto::{ByteArray, Pair}; +use sp_keystore::KeystorePtr; +use sp_runtime::{ + traits::{Block as BlockT, Header, NumberFor, Zero}, + DigestItem, +}; + +pub use sc_consensus_slots::check_equivocation; + +use super::{ + AuraApi, AuthorityId, CompatibilityMode, CompatibleDigestItem, SlotDuration, LOG_TARGET, +}; + +/// Get the slot duration for Aura by reading from a runtime API at the best block's state. +pub fn slot_duration(client: &C) -> CResult +where + A: Codec, + B: BlockT, + C: AuxStore + ProvideRuntimeApi + UsageProvider, + C::Api: AuraApi, +{ + slot_duration_at(client, client.usage_info().chain.best_hash) +} + +/// Get the slot duration for Aura by reading from a runtime API at a given block's state. +pub fn slot_duration_at(client: &C, block_hash: B::Hash) -> CResult +where + A: Codec, + B: BlockT, + C: AuxStore + ProvideRuntimeApi, + C::Api: AuraApi, +{ + client.runtime_api().slot_duration(block_hash).map_err(|err| err.into()) +} + +/// Get the slot author for given block along with authorities. +pub fn slot_author(slot: Slot, authorities: &[AuthorityId

]) -> Option<&AuthorityId

> { + if authorities.is_empty() { + return None + } + + let idx = *slot % (authorities.len() as u64); + assert!( + idx <= usize::MAX as u64, + "It is impossible to have a vector with length beyond the address space; qed", + ); + + let current_author = authorities.get(idx as usize).expect( + "authorities not empty; index constrained to list length;this is a valid index; qed", + ); + + Some(current_author) +} + +/// Attempt to claim a slot using a keystore. +/// +/// This returns `None` if the slot author is not locally controlled, and `Some` if it is, +/// with the public key of the slot author. +pub async fn claim_slot( + slot: Slot, + authorities: &[AuthorityId

], + keystore: &KeystorePtr, +) -> Option { + let expected_author = slot_author::

(slot, authorities); + expected_author.and_then(|p| { + if keystore.has_keys(&[(p.to_raw_vec(), sp_application_crypto::key_types::AURA)]) { + Some(p.clone()) + } else { + None + } + }) +} + +/// Produce the pre-runtime digest containing the slot info. +/// +/// This is intended to be put into the block header prior to runtime execution, +/// so the runtime can read the slot in this way. +pub fn pre_digest(slot: Slot) -> sp_runtime::DigestItem +where + P::Signature: Codec, +{ + >::aura_pre_digest(slot) +} + +/// Produce the seal digest item by signing the hash of a block. +/// +/// Note that after this is added to a block header, the hash of the block will change. +pub fn seal( + header_hash: &Hash, + public: &P::Public, + keystore: &KeystorePtr, +) -> Result +where + Hash: AsRef<[u8]>, + P: Pair, + P::Signature: Codec + TryFrom>, + P::Public: AppPublic, +{ + let signature = keystore + .sign_with( + as AppCrypto>::ID, + as AppCrypto>::CRYPTO_ID, + public.as_slice(), + header_hash.as_ref(), + ) + .map_err(|e| ConsensusError::CannotSign(format!("{}. Key: {:?}", e, public)))? + .ok_or_else(|| { + ConsensusError::CannotSign(format!("Could not find key in keystore. Key: {:?}", public)) + })?; + + let signature = signature + .clone() + .try_into() + .map_err(|_| ConsensusError::InvalidSignature(signature, public.to_raw_vec()))?; + + let signature_digest_item = + >::aura_seal(signature); + + Ok(signature_digest_item) +} + +/// Errors in pre-digest lookup. +#[derive(Debug, thiserror::Error)] +pub enum PreDigestLookupError { + /// Multiple Aura pre-runtime headers + #[error("Multiple Aura pre-runtime headers")] + MultipleHeaders, + /// No Aura pre-runtime digest found + #[error("No Aura pre-runtime digest found")] + NoDigestFound, +} + +/// Extract a pre-digest from a block header. +/// +/// This fails if there is no pre-digest or there are multiple. +/// +/// Returns the `slot` stored in the pre-digest or an error if no pre-digest was found. +pub fn find_pre_digest( + header: &B::Header, +) -> Result { + if header.number().is_zero() { + return Ok(0.into()) + } + + let mut pre_digest: Option = None; + for log in header.digest().logs() { + trace!(target: LOG_TARGET, "Checking log {:?}", log); + match (CompatibleDigestItem::::as_aura_pre_digest(log), pre_digest.is_some()) { + (Some(_), true) => return Err(PreDigestLookupError::MultipleHeaders), + (None, _) => trace!(target: LOG_TARGET, "Ignoring digest not meant for us"), + (s, false) => pre_digest = s, + } + } + pre_digest.ok_or_else(|| PreDigestLookupError::NoDigestFound) +} + +/// Fetch the current set of authorities from the runtime at a specific block. +/// +/// The compatibility mode and context block number informs this function whether +/// to initialize the hypothetical block created by the runtime API as backwards compatibility +/// for older chains. +pub fn fetch_authorities_with_compatibility_mode( + client: &C, + parent_hash: B::Hash, + context_block_number: NumberFor, + compatibility_mode: &CompatibilityMode>, +) -> Result, ConsensusError> +where + A: Codec + Debug, + B: BlockT, + C: ProvideRuntimeApi, + C::Api: AuraApi, +{ + let runtime_api = client.runtime_api(); + + match compatibility_mode { + CompatibilityMode::None => {}, + // Use `initialize_block` until we hit the block that should disable the mode. + CompatibilityMode::UseInitializeBlock { until } => + if *until > context_block_number { + runtime_api + .initialize_block( + parent_hash, + &B::Header::new( + context_block_number, + Default::default(), + Default::default(), + parent_hash, + Default::default(), + ), + ) + .map_err(|_| ConsensusError::InvalidAuthoritiesSet)?; + }, + } + + runtime_api + .authorities(parent_hash) + .ok() + .ok_or(ConsensusError::InvalidAuthoritiesSet) +} + +/// Load the current set of authorities from a runtime at a specific block. +pub fn fetch_authorities( + client: &C, + parent_hash: B::Hash, +) -> Result, ConsensusError> +where + A: Codec + Debug, + B: BlockT, + C: ProvideRuntimeApi, + C::Api: AuraApi, +{ + client + .runtime_api() + .authorities(parent_hash) + .ok() + .ok_or(ConsensusError::InvalidAuthoritiesSet) +} + +/// Errors in slot and seal verification. +#[derive(Debug, thiserror::Error)] +pub enum SealVerificationError

{ + /// Header is deferred to the future. + #[error("Header slot is in the future")] + Deferred(Header, Slot), + + /// The header has no seal digest. + #[error("Header is unsealed.")] + Unsealed, + + /// The header has a malformed seal. + #[error("Header has a malformed seal")] + BadSeal, + + /// The header has a bad signature. + #[error("Header has a bad signature")] + BadSignature, + + /// No slot author found. + #[error("No slot author for provided slot")] + SlotAuthorNotFound, + + /// Header has no valid slot pre-digest. + #[error("Header has no valid slot pre-digest")] + InvalidPreDigest(PreDigestLookupError), +} + +/// Check a header has been signed by the right key. If the slot is too far in the future, an error +/// will be returned. If it's successful, returns the pre-header (i.e. without the seal), +/// the slot, and the digest item containing the seal. +/// +/// Note that this does not check for equivocations, and [`check_equivocation`] is recommended +/// for that purpose. +/// +/// This digest item will always return `Some` when used with `as_aura_seal`. +pub fn check_header_slot_and_seal( + slot_now: Slot, + mut header: B::Header, + authorities: &[AuthorityId

], +) -> Result<(B::Header, Slot, DigestItem), SealVerificationError> +where + P::Signature: Codec, + P::Public: Codec + PartialEq + Clone, +{ + let seal = header.digest_mut().pop().ok_or(SealVerificationError::Unsealed)?; + + let sig = seal.as_aura_seal().ok_or(SealVerificationError::BadSeal)?; + + let slot = find_pre_digest::(&header) + .map_err(SealVerificationError::InvalidPreDigest)?; + + if slot > slot_now { + header.digest_mut().push(seal); + return Err(SealVerificationError::Deferred(header, slot)) + } else { + // check the signature is valid under the expected authority and + // chain state. + let expected_author = + slot_author::

(slot, authorities).ok_or(SealVerificationError::SlotAuthorNotFound)?; + + let pre_hash = header.hash(); + + if P::verify(&sig, pre_hash.as_ref(), expected_author) { + Ok((header, slot, seal)) + } else { + Err(SealVerificationError::BadSignature) + } + } +} + +#[cfg(test)] +mod tests { + use super::*; + use sp_keyring::sr25519::Keyring; + + #[test] + fn authorities_call_works() { + let client = substrate_test_runtime_client::new(); + + assert_eq!(client.chain_info().best_number, 0); + assert_eq!( + fetch_authorities_with_compatibility_mode( + &client, + client.chain_info().best_hash, + 1, + &CompatibilityMode::None + ) + .unwrap(), + vec![ + Keyring::Alice.public().into(), + Keyring::Bob.public().into(), + Keyring::Charlie.public().into() + ] + ); + + assert_eq!( + fetch_authorities(&client, client.chain_info().best_hash).unwrap(), + vec![ + Keyring::Alice.public().into(), + Keyring::Bob.public().into(), + Keyring::Charlie.public().into() + ] + ); + } +} From 75dee3c6fc72f6399a43f498e0e42f94fb82a7bd Mon Sep 17 00:00:00 2001 From: PG Herveou Date: Fri, 31 Mar 2023 19:44:29 +0200 Subject: [PATCH 325/558] contracts: make test work with debugger (#13776) * contracts: make test work with debugger * fix path * PR review * Add comment * space * Update frame/contracts/src/tests.rs * lint * spelling --- frame/contracts/src/tests.rs | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/frame/contracts/src/tests.rs b/frame/contracts/src/tests.rs index beaec458e..bae6c1946 100644 --- a/frame/contracts/src/tests.rs +++ b/frame/contracts/src/tests.rs @@ -456,7 +456,14 @@ fn compile_module(fixture_name: &str) -> wat::Result<(Vec, Date: Sat, 1 Apr 2023 18:19:12 +0100 Subject: [PATCH 326/558] add claim_commission weight (#13774) --- frame/nomination-pools/src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frame/nomination-pools/src/lib.rs b/frame/nomination-pools/src/lib.rs index e7c99e023..78f0c730c 100644 --- a/frame/nomination-pools/src/lib.rs +++ b/frame/nomination-pools/src/lib.rs @@ -2617,7 +2617,7 @@ pub mod pallet { /// commission is paid out and added to total claimed commission`. Total pending commission /// is reset to zero. the current. #[pallet::call_index(20)] - #[pallet::weight(0)] + #[pallet::weight(T::WeightInfo::claim_commission())] pub fn claim_commission(origin: OriginFor, pool_id: PoolId) -> DispatchResult { let who = ensure_signed(origin)?; Self::do_claim_commission(who, pool_id) From d6f278b9685ac871c79e45551b66111b77cb1b71 Mon Sep 17 00:00:00 2001 From: Gavin Wood Date: Sat, 1 Apr 2023 23:59:37 +0200 Subject: [PATCH 327/558] FRAME: Minor fix for failsafe. (#13741) * max instead of min * Remove debug stuff * remove debug code * warn log on no provider ref * format string for log --------- Co-authored-by: muharem --- frame/balances/src/lib.rs | 7 ++++++- frame/nis/src/lib.rs | 9 ++------- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/frame/balances/src/lib.rs b/frame/balances/src/lib.rs index d4be80698..dcf602cd5 100644 --- a/frame/balances/src/lib.rs +++ b/frame/balances/src/lib.rs @@ -786,7 +786,12 @@ pub mod pallet { // Gah!! We have a non-zero reserve balance but no provider refs :( // This shouldn't practically happen, but we need a failsafe anyway: let's give // them enough for an ED. - a.free = a.free.min(Self::ed()); + log::warn!( + target: LOG_TARGET, + "account with a non-zero reserve balance has no provider refs, account_id: '{:?}'.", + who + ); + a.free = a.free.max(Self::ed()); system::Pallet::::inc_providers(who); } let _ = system::Pallet::::inc_consumers(who).defensive(); diff --git a/frame/nis/src/lib.rs b/frame/nis/src/lib.rs index 0b8d292ec..539011c51 100644 --- a/frame/nis/src/lib.rs +++ b/frame/nis/src/lib.rs @@ -480,9 +480,6 @@ pub mod pallet { AlreadyCommunal, /// The receipt is already private. AlreadyPrivate, - Release1, - Release2, - Tah, } pub(crate) struct WeightCounter { @@ -724,8 +721,7 @@ pub mod pallet { let dropped = receipt.proportion.is_zero(); if amount > on_hold { - T::Currency::release(&T::HoldReason::get(), &who, on_hold, Exact) - .map_err(|_| Error::::Release1)?; + T::Currency::release(&T::HoldReason::get(), &who, on_hold, Exact)?; let deficit = amount - on_hold; // Try to transfer deficit from pot to receipt owner. summary.receipts_on_hold.saturating_reduce(on_hold); @@ -756,8 +752,7 @@ pub mod pallet { )?; summary.receipts_on_hold.saturating_reduce(on_hold); } - T::Currency::release(&T::HoldReason::get(), &who, amount, Exact) - .map_err(|_| Error::::Release2)?; + T::Currency::release(&T::HoldReason::get(), &who, amount, Exact)?; } if dropped { From db40c484953a17a1eefb92ee743415749892a727 Mon Sep 17 00:00:00 2001 From: Damian Straszak Date: Mon, 3 Apr 2023 10:36:28 +0200 Subject: [PATCH 328/558] Adjustments to RPC-query docstrings. (#13698) * Changes in RPC docs. * ".git/.scripts/commands/fmt/fmt.sh" --------- Co-authored-by: command-bot <> --- client/rpc-api/src/state/mod.rs | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/client/rpc-api/src/state/mod.rs b/client/rpc-api/src/state/mod.rs index ebc1eb9b4..dbc2a5054 100644 --- a/client/rpc-api/src/state/mod.rs +++ b/client/rpc-api/src/state/mod.rs @@ -33,7 +33,7 @@ pub use self::helpers::ReadProof; /// Substrate state API #[rpc(client, server)] pub trait StateApi { - /// Call a contract at a block's state. + /// Call a method from the runtime API at a block's state. #[method(name = "state_call", aliases = ["state_callAt"], blocking)] fn call(&self, name: String, bytes: Bytes, hash: Option) -> RpcResult; @@ -85,8 +85,10 @@ pub trait StateApi { /// Query historical storage entries (by key) starting from a block given as the second /// parameter. /// - /// NOTE This first returned result contains the initial state of storage for all keys. + /// NOTE: The first returned result contains the initial state of storage for all keys. /// Subsequent values in the vector represent changes to the previous state (diffs). + /// WARNING: The time complexity of this query is O(|keys|*dist(block, hash)), and the + /// memory complexity is O(dist(block, hash)) -- use with caution. #[method(name = "state_queryStorage", blocking)] fn query_storage( &self, @@ -95,7 +97,9 @@ pub trait StateApi { hash: Option, ) -> RpcResult>>; - /// Query storage entries (by key) starting at block hash given as the second parameter. + /// Query storage entries (by key) at a block hash given as the second parameter. + /// NOTE: Each StorageChangeSet in the result corresponds to exactly one element -- + /// the storage value under an input key at the input block hash. #[method(name = "state_queryStorageAt", blocking)] fn query_storage_at( &self, From be9fa62238fcfd7eb49218809a6b981f71c34eb3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bastian=20K=C3=B6cher?= Date: Mon, 3 Apr 2023 11:37:18 +0200 Subject: [PATCH 329/558] Force upgrade snow to 0.9.2 (#13806) This fixes the compilation on master for the node template that was not pulling the latest release as part of its build. --- Cargo.lock | 11 ++++++----- client/network/Cargo.toml | 4 ++++ 2 files changed, 10 insertions(+), 5 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 7fd0122f3..1e9a87e81 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1473,9 +1473,9 @@ dependencies = [ [[package]] name = "curve25519-dalek" -version = "4.0.0-rc.0" +version = "4.0.0-rc.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8da00a7a9a4eb92a0a0f8e75660926d48f0d0f3c537e455c457bcdaa1e16b1ac" +checksum = "8d4ba9852b42210c7538b75484f9daa0655e9a3ac04f693747bb0f02cf3cfe16" dependencies = [ "cfg-if", "fiat-crypto", @@ -8955,6 +8955,7 @@ dependencies = [ "serde", "serde_json", "smallvec", + "snow", "sp-arithmetic", "sp-blockchain", "sp-consensus", @@ -10034,14 +10035,14 @@ checksum = "5e9f0ab6ef7eb7353d9119c170a436d1bf248eea575ac42d19d12f4e34130831" [[package]] name = "snow" -version = "0.9.1" +version = "0.9.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "12ba5f4d4ff12bdb6a169ed51b7c48c0e0ac4b0b4b31012b2571e97d78d3201d" +checksum = "5ccba027ba85743e09d15c03296797cad56395089b832b48b5a5217880f57733" dependencies = [ "aes-gcm 0.9.4", "blake2", "chacha20poly1305", - "curve25519-dalek 4.0.0-rc.0", + "curve25519-dalek 4.0.0-rc.1", "rand_core 0.6.4", "ring", "rustc_version 0.4.0", diff --git a/client/network/Cargo.toml b/client/network/Cargo.toml index 90b5ce871..6fc4131f7 100644 --- a/client/network/Cargo.toml +++ b/client/network/Cargo.toml @@ -51,6 +51,10 @@ sp-blockchain = { version = "4.0.0-dev", path = "../../primitives/blockchain" } sp-consensus = { version = "0.10.0-dev", path = "../../primitives/consensus/common" } sp-core = { version = "7.0.0", path = "../../primitives/core" } sp-runtime = { version = "7.0.0", path = "../../primitives/runtime" } +# Force 0.9.2 as snow release to fix the compilation. +# +# When libp2p also enforces this version, we can get rid off this extra dep here. +snow = "0.9.2" [dev-dependencies] assert_matches = "1.3" From b94d98bfcb167c80a96a748955671a60ce09360d Mon Sep 17 00:00:00 2001 From: Muharem Ismailov Date: Mon, 3 Apr 2023 12:59:01 +0200 Subject: [PATCH 330/558] Scheduler pre block limit note (#13231) Co-authored-by: parity-processbot <> --- bin/node/runtime/src/lib.rs | 3 +++ frame/referenda/src/lib.rs | 15 +++++++-------- frame/scheduler/src/lib.rs | 4 ++++ 3 files changed, 14 insertions(+), 8 deletions(-) diff --git a/bin/node/runtime/src/lib.rs b/bin/node/runtime/src/lib.rs index 5562dc263..433ca8e8b 100644 --- a/bin/node/runtime/src/lib.rs +++ b/bin/node/runtime/src/lib.rs @@ -364,7 +364,10 @@ impl pallet_scheduler::Config for Runtime { type RuntimeCall = RuntimeCall; type MaximumWeight = MaximumSchedulerWeight; type ScheduleOrigin = EnsureRoot; + #[cfg(feature = "runtime-benchmarks")] type MaxScheduledPerBlock = ConstU32<512>; + #[cfg(not(feature = "runtime-benchmarks"))] + type MaxScheduledPerBlock = ConstU32<50>; type WeightInfo = pallet_scheduler::weights::SubstrateWeight; type OriginPrivilegeCmp = EqualPrivilegeOnly; type Preimages = Preimage; diff --git a/frame/referenda/src/lib.rs b/frame/referenda/src/lib.rs index b96cf5a76..68837376c 100644 --- a/frame/referenda/src/lib.rs +++ b/frame/referenda/src/lib.rs @@ -874,22 +874,21 @@ impl, I: 'static> Pallet { let when = (when.saturating_add(alarm_interval.saturating_sub(One::one())) / alarm_interval) .saturating_mul(alarm_interval); - let maybe_result = T::Scheduler::schedule( + let result = T::Scheduler::schedule( DispatchTime::At(when), None, 128u8, frame_system::RawOrigin::Root.into(), call, - ) - .ok() - .map(|x| (when, x)); + ); debug_assert!( - maybe_result.is_some(), - "Unable to schedule a new alarm at #{:?} (now: #{:?})?!", + result.is_ok(), + "Unable to schedule a new alarm at #{:?} (now: #{:?}), scheduler error: `{:?}`", when, - frame_system::Pallet::::block_number() + frame_system::Pallet::::block_number(), + result.unwrap_err(), ); - maybe_result + result.ok().map(|x| (when, x)) } /// Mutate a referendum's `status` into the correct deciding state. diff --git a/frame/scheduler/src/lib.rs b/frame/scheduler/src/lib.rs index 09bb35611..8194f286c 100644 --- a/frame/scheduler/src/lib.rs +++ b/frame/scheduler/src/lib.rs @@ -216,6 +216,10 @@ pub mod pallet { type OriginPrivilegeCmp: PrivilegeCmp; /// The maximum number of scheduled calls in the queue for a single block. + /// + /// NOTE: + /// + Dependent pallets' benchmarks might require a higher limit for the setting. Set a + /// higher limit under `runtime-benchmarks` feature. #[pallet::constant] type MaxScheduledPerBlock: Get; From dd45632f298db02f6946c906296bbf86a74f4f46 Mon Sep 17 00:00:00 2001 From: Koute Date: Mon, 3 Apr 2023 21:00:31 +0900 Subject: [PATCH 331/558] Disable `sign-ext` WASM feature when building runtimes (#13804) Co-authored-by: parity-processbot <> --- utils/wasm-builder/src/wasm_project.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/utils/wasm-builder/src/wasm_project.rs b/utils/wasm-builder/src/wasm_project.rs index d25fb4acd..c45a40a6b 100644 --- a/utils/wasm-builder/src/wasm_project.rs +++ b/utils/wasm-builder/src/wasm_project.rs @@ -632,7 +632,7 @@ fn build_project( let mut build_cmd = cargo_cmd.command(); let rustflags = format!( - "-C target-cpu=mvp -C link-arg=--export-table {} {}", + "-C target-cpu=mvp -C target-feature=-sign-ext -C link-arg=--export-table {} {}", default_rustflags, env::var(crate::WASM_BUILD_RUSTFLAGS_ENV).unwrap_or_default(), ); From bbcc63a96c8b77b9d07968cc2612a5c15b8cc0e5 Mon Sep 17 00:00:00 2001 From: William Freudenberger Date: Tue, 4 Apr 2023 09:40:30 +0200 Subject: [PATCH 332/558] refactor: inconsistent BalanceConversion fn (#13610) * refactor: inconsistent BalanceConversion fn * Revert "refactor: inconsistent BalanceConversion fn" This reverts commit 1177877631a11b64df6f019b5390a8a7018e3a3f. * refactor: rename BalanceConversion trait * feat: add ConversionFromAssetBalance --- frame/assets/src/tests.rs | 2 +- frame/assets/src/types.rs | 4 ++-- frame/support/src/traits/tokens.rs | 6 +++--- frame/support/src/traits/tokens/misc.rs | 14 ++++++++++++-- .../asset-tx-payment/src/payment.rs | 7 ++++--- 5 files changed, 22 insertions(+), 11 deletions(-) diff --git a/frame/assets/src/tests.rs b/frame/assets/src/tests.rs index d4a49ac5a..afd224ad6 100644 --- a/frame/assets/src/tests.rs +++ b/frame/assets/src/tests.rs @@ -1162,7 +1162,7 @@ fn set_min_balance_should_work() { #[test] fn balance_conversion_should_work() { new_test_ext().execute_with(|| { - use frame_support::traits::tokens::BalanceConversion; + use frame_support::traits::tokens::ConversionToAssetBalance; let id = 42; assert_ok!(Assets::force_create(RuntimeOrigin::root(), id, 1, true, 10)); diff --git a/frame/assets/src/types.rs b/frame/assets/src/types.rs index d50461ba5..c83e764f4 100644 --- a/frame/assets/src/types.rs +++ b/frame/assets/src/types.rs @@ -20,7 +20,7 @@ use super::*; use frame_support::{ pallet_prelude::*, - traits::{fungible, tokens::BalanceConversion}, + traits::{fungible, tokens::ConversionToAssetBalance}, }; use sp_runtime::{traits::Convert, FixedPointNumber, FixedPointOperand, FixedU128}; @@ -228,7 +228,7 @@ type BalanceOf = >>::Balance; /// Converts a balance value into an asset balance based on the ratio between the fungible's /// minimum balance and the minimum asset balance. pub struct BalanceToAssetBalance(PhantomData<(F, T, CON, I)>); -impl BalanceConversion, AssetIdOf, AssetBalanceOf> +impl ConversionToAssetBalance, AssetIdOf, AssetBalanceOf> for BalanceToAssetBalance where F: fungible::Inspect>, diff --git a/frame/support/src/traits/tokens.rs b/frame/support/src/traits/tokens.rs index d2753caa6..253b49c66 100644 --- a/frame/support/src/traits/tokens.rs +++ b/frame/support/src/traits/tokens.rs @@ -29,8 +29,8 @@ pub mod nonfungibles_v2; pub use imbalance::Imbalance; pub mod pay; pub use misc::{ - AssetId, Balance, BalanceConversion, BalanceStatus, ConvertRank, DepositConsequence, - ExistenceRequirement, Fortitude, GetSalary, Locker, Precision, Preservation, Provenance, - Restriction, WithdrawConsequence, WithdrawReasons, + AssetId, Balance, BalanceStatus, ConversionFromAssetBalance, ConversionToAssetBalance, + ConvertRank, DepositConsequence, ExistenceRequirement, Fortitude, GetSalary, Locker, Precision, + Preservation, Provenance, Restriction, WithdrawConsequence, WithdrawReasons, }; pub use pay::{Pay, PayFromAccount, PaymentStatus}; diff --git a/frame/support/src/traits/tokens/misc.rs b/frame/support/src/traits/tokens/misc.rs index 8ad3a9535..75aef0e04 100644 --- a/frame/support/src/traits/tokens/misc.rs +++ b/frame/support/src/traits/tokens/misc.rs @@ -244,9 +244,19 @@ impl< } /// Converts a balance value into an asset balance. -pub trait BalanceConversion { +pub trait ConversionToAssetBalance { type Error; - fn to_asset_balance(balance: InBalance, asset_id: AssetId) -> Result; + fn to_asset_balance(balance: InBalance, asset_id: AssetId) + -> Result; +} + +/// Converts an asset balance value into balance. +pub trait ConversionFromAssetBalance { + type Error; + fn from_asset_balance( + balance: AssetBalance, + asset_id: AssetId, + ) -> Result; } /// Trait to handle asset locking mechanism to ensure interactions with the asset can be implemented diff --git a/frame/transaction-payment/asset-tx-payment/src/payment.rs b/frame/transaction-payment/asset-tx-payment/src/payment.rs index f8d6888b9..49e78fb8b 100644 --- a/frame/transaction-payment/asset-tx-payment/src/payment.rs +++ b/frame/transaction-payment/asset-tx-payment/src/payment.rs @@ -22,7 +22,8 @@ use frame_support::{ traits::{ fungibles::{Balanced, Credit, Inspect}, tokens::{ - Balance, BalanceConversion, Fortitude::Polite, Precision::Exact, Preservation::Protect, + Balance, ConversionToAssetBalance, Fortitude::Polite, Precision::Exact, + Preservation::Protect, }, }, unsigned::TransactionValidityError, @@ -87,7 +88,7 @@ impl> HandleCredit for () { } /// Implements the asset transaction for a balance to asset converter (implementing -/// [`BalanceConversion`]) and a credit handler (implementing [`HandleCredit`]). +/// [`ConversionToAssetBalance`]) and a credit handler (implementing [`HandleCredit`]). /// /// The credit handler is given the complete fee in terms of the asset used for the transaction. pub struct FungiblesAdapter(PhantomData<(CON, HC)>); @@ -97,7 +98,7 @@ pub struct FungiblesAdapter(PhantomData<(CON, HC)>); impl OnChargeAssetTransaction for FungiblesAdapter where T: Config, - CON: BalanceConversion, AssetIdOf, AssetBalanceOf>, + CON: ConversionToAssetBalance, AssetIdOf, AssetBalanceOf>, HC: HandleCredit, AssetIdOf: FullCodec + Copy + MaybeSerializeDeserialize + Debug + Default + Eq + TypeInfo, { From 842e651fac49176781660704c643b63f0fb72705 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bastian=20K=C3=B6cher?= Date: Tue, 4 Apr 2023 12:02:47 +0200 Subject: [PATCH 333/558] Remove deprecated batch verification (#13799) This removes the deprecated batch verification. This was actually never really activated. Nevertheless, we need to keep the host functions around to support old runtimes which may import these host functions. However, we do not give access to these functions anymore. This means that any new runtime can not call them anymore. The host function implementations we keep will not do batch verification and will instead fall back to the always existing option of directly verifying the passed signature. `finish_batch_verification` will return the combined result of all the batch verify calls. This removes the `TaskExecutorExt` which only existed to support the batch verification. So, any code that used this extension can just remove the registration of them. It also removes `SignatureBatching` that was used by `frame-executive` to control the batch verification. However, there wasn't any `Verify` implementation that called the batch verification functions. --- client/service/src/builder.rs | 1 - client/service/src/client/call_executor.rs | 11 +- client/service/src/client/client.rs | 9 +- client/service/test/src/client/mod.rs | 7 - frame/executive/src/lib.rs | 6 - primitives/api/test/tests/runtime_calls.rs | 1 - primitives/core/src/traits.rs | 12 - primitives/io/src/batch_verifier.rs | 211 ---------------- primitives/io/src/lib.rs | 228 +++++------------- primitives/runtime/src/lib.rs | 66 +---- primitives/state-machine/src/lib.rs | 36 +-- primitives/state-machine/src/testing.rs | 7 +- test-utils/client/src/lib.rs | 1 - .../benchmarking-cli/src/pallet/command.rs | 4 - utils/frame/try-runtime/cli/src/lib.rs | 6 +- 15 files changed, 76 insertions(+), 530 deletions(-) delete mode 100644 primitives/io/src/batch_verifier.rs diff --git a/client/service/src/builder.rs b/client/service/src/builder.rs index b04228e6b..5d639431f 100644 --- a/client/service/src/builder.rs +++ b/client/service/src/builder.rs @@ -272,7 +272,6 @@ where let executor = crate::client::LocalCallExecutor::new( backend.clone(), executor, - spawn_handle.clone(), config.clone(), execution_extensions, )?; diff --git a/client/service/src/client/call_executor.rs b/client/service/src/client/call_executor.rs index 82d3e36ac..4d019be90 100644 --- a/client/service/src/client/call_executor.rs +++ b/client/service/src/client/call_executor.rs @@ -23,7 +23,7 @@ use sc_client_api::{ use sc_executor::{RuntimeVersion, RuntimeVersionOf}; use sp_api::{ProofRecorder, StorageTransactionCache}; use sp_core::{ - traits::{CallContext, CodeExecutor, RuntimeCode, SpawnNamed}, + traits::{CallContext, CodeExecutor, RuntimeCode}, ExecutionContext, }; use sp_runtime::{generic::BlockId, traits::Block as BlockT}; @@ -39,7 +39,6 @@ pub struct LocalCallExecutor { executor: E, wasm_override: Arc>, wasm_substitutes: WasmSubstitutes, - spawn_handle: Box, execution_extensions: Arc>, } @@ -52,7 +51,6 @@ where pub fn new( backend: Arc, executor: E, - spawn_handle: Box, client_config: ClientConfig, execution_extensions: ExecutionExtensions, ) -> sp_blockchain::Result { @@ -72,7 +70,6 @@ where backend, executor, wasm_override: Arc::new(wasm_override), - spawn_handle, wasm_substitutes, execution_extensions: Arc::new(execution_extensions), }) @@ -142,7 +139,6 @@ where backend: self.backend.clone(), executor: self.executor.clone(), wasm_override: self.wasm_override.clone(), - spawn_handle: self.spawn_handle.clone(), wasm_substitutes: self.wasm_substitutes.clone(), execution_extensions: self.execution_extensions.clone(), } @@ -196,7 +192,6 @@ where call_data, extensions, &runtime_code, - self.spawn_handle.clone(), context, ) .set_parent_hash(at_hash); @@ -256,7 +251,6 @@ where call_data, extensions, &runtime_code, - self.spawn_handle.clone(), call_context, ) .with_storage_transaction_cache(storage_transaction_cache.as_deref_mut()) @@ -272,7 +266,6 @@ where call_data, extensions, &runtime_code, - self.spawn_handle.clone(), call_context, ) .with_storage_transaction_cache(storage_transaction_cache.as_deref_mut()) @@ -313,7 +306,6 @@ where trie_backend, &mut Default::default(), &self.executor, - self.spawn_handle.clone(), method, call_data, &runtime_code, @@ -425,7 +417,6 @@ mod tests { backend: backend.clone(), executor: executor.clone(), wasm_override: Arc::new(Some(overrides)), - spawn_handle: Box::new(TaskExecutor::new()), wasm_substitutes: WasmSubstitutes::new( Default::default(), executor.clone(), diff --git a/client/service/src/client/client.rs b/client/service/src/client/client.rs index d5212021f..3adb6d897 100644 --- a/client/service/src/client/client.rs +++ b/client/service/src/client/client.rs @@ -245,13 +245,8 @@ where sc_offchain::OffchainDb::factory_from_backend(&*backend), ); - let call_executor = LocalCallExecutor::new( - backend.clone(), - executor, - spawn_handle.clone(), - config.clone(), - extensions, - )?; + let call_executor = + LocalCallExecutor::new(backend.clone(), executor, config.clone(), extensions)?; Client::new( backend, diff --git a/client/service/test/src/client/mod.rs b/client/service/test/src/client/mod.rs index 96a0a8fe2..e6545abf1 100644 --- a/client/service/test/src/client/mod.rs +++ b/client/service/test/src/client/mod.rs @@ -104,7 +104,6 @@ fn construct_block( let mut overlay = OverlayedChanges::default(); let backend_runtime_code = sp_state_machine::backend::BackendRuntimeCode::new(backend); let runtime_code = backend_runtime_code.runtime_code().expect("Code is part of the backend"); - let task_executor = Box::new(TaskExecutor::new()); StateMachine::new( backend, @@ -114,7 +113,6 @@ fn construct_block( &header.encode(), Default::default(), &runtime_code, - task_executor.clone() as Box<_>, CallContext::Onchain, ) .execute(ExecutionStrategy::NativeElseWasm) @@ -129,7 +127,6 @@ fn construct_block( &tx.encode(), Default::default(), &runtime_code, - task_executor.clone() as Box<_>, CallContext::Onchain, ) .execute(ExecutionStrategy::NativeElseWasm) @@ -144,7 +141,6 @@ fn construct_block( &[], Default::default(), &runtime_code, - task_executor.clone() as Box<_>, CallContext::Onchain, ) .execute(ExecutionStrategy::NativeElseWasm) @@ -217,7 +213,6 @@ fn construct_genesis_should_work_with_native() { &b1data, Default::default(), &runtime_code, - TaskExecutor::new(), CallContext::Onchain, ) .execute(ExecutionStrategy::NativeElseWasm) @@ -251,7 +246,6 @@ fn construct_genesis_should_work_with_wasm() { &b1data, Default::default(), &runtime_code, - TaskExecutor::new(), CallContext::Onchain, ) .execute(ExecutionStrategy::AlwaysWasm) @@ -285,7 +279,6 @@ fn construct_genesis_with_bad_transaction_should_panic() { &b1data, Default::default(), &runtime_code, - TaskExecutor::new(), CallContext::Onchain, ) .execute(ExecutionStrategy::NativeElseWasm); diff --git a/frame/executive/src/lib.rs b/frame/executive/src/lib.rs index 5003920ea..6a1041a28 100644 --- a/frame/executive/src/lib.rs +++ b/frame/executive/src/lib.rs @@ -480,16 +480,10 @@ where // any initial checks Self::initial_checks(&block); - let signature_batching = sp_runtime::SignatureBatching::start(); - // execute extrinsics let (header, extrinsics) = block.deconstruct(); Self::execute_extrinsics_with_book_keeping(extrinsics, *header.number()); - if !signature_batching.verify() { - panic!("Signature verification failed."); - } - // any final checks Self::final_checks(&header); } diff --git a/primitives/api/test/tests/runtime_calls.rs b/primitives/api/test/tests/runtime_calls.rs index 69f9a88ff..b57793aad 100644 --- a/primitives/api/test/tests/runtime_calls.rs +++ b/primitives/api/test/tests/runtime_calls.rs @@ -188,7 +188,6 @@ fn record_proof_works() { &backend, &mut overlay, &executor, - sp_core::testing::TaskExecutor::new(), "Core_execute_block", &block.encode(), &runtime_code, diff --git a/primitives/core/src/traits.rs b/primitives/core/src/traits.rs index 091b1cdb1..513278684 100644 --- a/primitives/core/src/traits.rs +++ b/primitives/core/src/traits.rs @@ -169,18 +169,6 @@ impl ReadRuntimeVersionExt { } } -sp_externalities::decl_extension! { - /// Task executor extension. - pub struct TaskExecutorExt(Box); -} - -impl TaskExecutorExt { - /// New instance of task executor extension. - pub fn new(spawn_handle: impl SpawnNamed + Send + 'static) -> Self { - Self(Box::new(spawn_handle)) - } -} - /// Something that can spawn tasks (blocking and non-blocking) with an assigned name /// and optional group. #[dyn_clonable::clonable] diff --git a/primitives/io/src/batch_verifier.rs b/primitives/io/src/batch_verifier.rs deleted file mode 100644 index e6d8c6131..000000000 --- a/primitives/io/src/batch_verifier.rs +++ /dev/null @@ -1,211 +0,0 @@ -// This file is part of Substrate. - -// Copyright (C) Parity Technologies (UK) Ltd. -// SPDX-License-Identifier: Apache-2.0 - -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -//! Batch/parallel verification. - -use futures::{channel::oneshot, future::FutureExt}; -use sp_core::{crypto::Pair, ecdsa, ed25519, sr25519, traits::SpawnNamed}; -use std::sync::{ - atomic::{AtomicBool, Ordering as AtomicOrdering}, - Arc, -}; - -#[derive(Debug, Clone)] -struct Sr25519BatchItem { - signature: sr25519::Signature, - pub_key: sr25519::Public, - message: Vec, -} - -/// Batch verifier. -/// -/// Used to parallel-verify signatures for runtime host. Provide task executor and -/// just push (`push_ed25519`, `push_sr25519`) as many signature as you need. At the end, -/// call `verify_and_clear to get a result. After that, batch verifier is ready for the -/// next batching job. -pub struct BatchVerifier { - scheduler: Box, - sr25519_items: Vec, - invalid: Arc, - pending_tasks: Vec>, -} - -impl BatchVerifier { - pub fn new(scheduler: Box) -> Self { - BatchVerifier { - scheduler, - sr25519_items: Default::default(), - invalid: Arc::new(false.into()), - pending_tasks: vec![], - } - } - - /// Spawn a verification task. - /// - /// Returns `false` if there was already an invalid verification or if - /// the verification could not be spawned. - fn spawn_verification_task( - &mut self, - f: impl FnOnce() -> bool + Send + 'static, - name: &'static str, - ) -> bool { - // there is already invalid transaction encountered - if self.invalid.load(AtomicOrdering::Relaxed) { - return false - } - - let invalid_clone = self.invalid.clone(); - let (sender, receiver) = oneshot::channel(); - self.pending_tasks.push(receiver); - - self.scheduler.spawn( - name, - None, - async move { - if !f() { - invalid_clone.store(true, AtomicOrdering::Relaxed); - } - if sender.send(()).is_err() { - // sanity - log::warn!("Verification halted while result was pending"); - invalid_clone.store(true, AtomicOrdering::Relaxed); - } - } - .boxed(), - ); - - true - } - - /// Push ed25519 signature to verify. - /// - /// Returns false if some of the pushed signatures before already failed the check - /// (in this case it won't verify anything else) - pub fn push_ed25519( - &mut self, - signature: ed25519::Signature, - pub_key: ed25519::Public, - message: Vec, - ) -> bool { - self.spawn_verification_task( - move || ed25519::Pair::verify(&signature, &message, &pub_key), - "substrate_ed25519_verify", - ) - } - - /// Push sr25519 signature to verify. - /// - /// Returns false if some of the pushed signatures before already failed the check. - /// (in this case it won't verify anything else) - pub fn push_sr25519( - &mut self, - signature: sr25519::Signature, - pub_key: sr25519::Public, - message: Vec, - ) -> bool { - if self.invalid.load(AtomicOrdering::Relaxed) { - return false - } - self.sr25519_items.push(Sr25519BatchItem { signature, pub_key, message }); - - if self.sr25519_items.len() >= 128 { - let items = std::mem::take(&mut self.sr25519_items); - self.spawn_verification_task( - move || Self::verify_sr25519_batch(items), - "substrate_sr25519_verify", - ) - } else { - true - } - } - - /// Push ecdsa signature to verify. - /// - /// Returns false if some of the pushed signatures before already failed the check - /// (in this case it won't verify anything else) - pub fn push_ecdsa( - &mut self, - signature: ecdsa::Signature, - pub_key: ecdsa::Public, - message: Vec, - ) -> bool { - self.spawn_verification_task( - move || ecdsa::Pair::verify(&signature, &message, &pub_key), - "substrate_ecdsa_verify", - ) - } - - fn verify_sr25519_batch(items: Vec) -> bool { - let messages = items.iter().map(|item| &item.message[..]).collect(); - let signatures = items.iter().map(|item| &item.signature).collect(); - let pub_keys = items.iter().map(|item| &item.pub_key).collect(); - - sr25519::verify_batch(messages, signatures, pub_keys) - } - - /// Verify all previously pushed signatures since last call and return - /// aggregated result. - #[must_use] - pub fn verify_and_clear(&mut self) -> bool { - let pending = std::mem::take(&mut self.pending_tasks); - let started = std::time::Instant::now(); - - log::trace!( - target: "runtime", - "Batch-verification: {} pending tasks, {} sr25519 signatures", - pending.len(), - self.sr25519_items.len(), - ); - - if !Self::verify_sr25519_batch(std::mem::take(&mut self.sr25519_items)) { - return false - } - - if pending.len() > 0 { - let (sender, receiver) = std::sync::mpsc::channel(); - self.scheduler.spawn( - "substrate-batch-verify-join", - None, - async move { - futures::future::join_all(pending).await; - sender.send(()).expect( - "Channel never panics if receiver is live. \ - Receiver is always live until received this data; qed. ", - ); - } - .boxed(), - ); - - if receiver.recv().is_err() { - log::warn!( - target: "runtime", - "Haven't received async result from verification task. Returning false.", - ); - - return false - } - } - - log::trace!( - target: "runtime", - "Finalization of batch verification took {} ms", - started.elapsed().as_millis(), - ); - - !self.invalid.swap(false, AtomicOrdering::Relaxed) - } -} diff --git a/primitives/io/src/lib.rs b/primitives/io/src/lib.rs index 72300eb10..750b5d592 100644 --- a/primitives/io/src/lib.rs +++ b/primitives/io/src/lib.rs @@ -40,7 +40,6 @@ use sp_core::{ hexdisplay::HexDisplay, offchain::{OffchainDbExt, OffchainWorkerExt, TransactionPoolExt}, storage::ChildInfo, - traits::TaskExecutorExt, }; #[cfg(feature = "std")] use sp_keystore::KeystoreExt; @@ -75,12 +74,6 @@ use secp256k1::{ #[cfg(feature = "std")] use sp_externalities::{Externalities, ExternalitiesExt}; -#[cfg(feature = "std")] -mod batch_verifier; - -#[cfg(feature = "std")] -use batch_verifier::BatchVerifier; - pub use sp_externalities::MultiRemovalResults; #[cfg(feature = "std")] @@ -801,15 +794,25 @@ pub trait Crypto { /// needs to be called. /// /// Returns `true` when the verification is either successful or batched. + /// + /// NOTE: Is tagged with `register_only` to keep the functions around for backwards + /// compatibility with old runtimes, but it should not be used anymore by new runtimes. + /// The implementation emulates the old behavior, but isn't doing any batch verification + /// anymore. + #[version(1, register_only)] fn ed25519_batch_verify( &mut self, sig: &ed25519::Signature, msg: &[u8], pub_key: &ed25519::Public, ) -> bool { - self.extension::() - .map(|extension| extension.push_ed25519(sig.clone(), *pub_key, msg.to_vec())) - .unwrap_or_else(|| ed25519_verify(sig, msg, pub_key)) + let res = ed25519_verify(sig, msg, pub_key); + + if let Some(ext) = self.extension::() { + ext.0 &= res; + } + + res } /// Verify `sr25519` signature. @@ -828,25 +831,36 @@ pub trait Crypto { /// needs to be called. /// /// Returns `true` when the verification is either successful or batched. + /// + /// NOTE: Is tagged with `register_only` to keep the functions around for backwards + /// compatibility with old runtimes, but it should not be used anymore by new runtimes. + /// The implementation emulates the old behavior, but isn't doing any batch verification + /// anymore. + #[version(1, register_only)] fn sr25519_batch_verify( &mut self, sig: &sr25519::Signature, msg: &[u8], pub_key: &sr25519::Public, ) -> bool { - self.extension::() - .map(|extension| extension.push_sr25519(sig.clone(), *pub_key, msg.to_vec())) - .unwrap_or_else(|| sr25519_verify(sig, msg, pub_key)) + let res = sr25519_verify(sig, msg, pub_key); + + if let Some(ext) = self.extension::() { + ext.0 &= res; + } + + res } /// Start verification extension. + /// + /// NOTE: Is tagged with `register_only` to keep the functions around for backwards + /// compatibility with old runtimes, but it should not be used anymore by new runtimes. + /// The implementation emulates the old behavior, but isn't doing any batch verification + /// anymore. + #[version(1, register_only)] fn start_batch_verify(&mut self) { - let scheduler = self - .extension::() - .expect("No task executor associated with the current context!") - .clone(); - - self.register_extension(VerificationExt(BatchVerifier::new(scheduler))) + self.register_extension(VerificationExtDeprecated(true)) .expect("Failed to register required extension: `VerificationExt`"); } @@ -856,13 +870,19 @@ pub trait Crypto { /// deferred by `sr25519_verify`/`ed25519_verify`. /// /// Will panic if no `VerificationExt` is registered (`start_batch_verify` was not called). + /// + /// NOTE: Is tagged with `register_only` to keep the functions around for backwards + /// compatibility with old runtimes, but it should not be used anymore by new runtimes. + /// The implementation emulates the old behavior, but isn't doing any batch verification + /// anymore. + #[version(1, register_only)] fn finish_batch_verify(&mut self) -> bool { let result = self - .extension::() + .extension::() .expect("`finish_batch_verify` should only be called after `start_batch_verify`") - .verify_and_clear(); + .0; - self.deregister_extension::() + self.deregister_extension::() .expect("No verification extension in current context!"); result @@ -1005,15 +1025,25 @@ pub trait Crypto { /// needs to be called. /// /// Returns `true` when the verification is either successful or batched. + /// + /// NOTE: Is tagged with `register_only` to keep the functions around for backwards + /// compatibility with old runtimes, but it should not be used anymore by new runtimes. + /// The implementation emulates the old behavior, but isn't doing any batch verification + /// anymore. + #[version(1, register_only)] fn ecdsa_batch_verify( &mut self, sig: &ecdsa::Signature, msg: &[u8], pub_key: &ecdsa::Public, ) -> bool { - self.extension::() - .map(|extension| extension.push_ecdsa(sig.clone(), *pub_key, msg.to_vec())) - .unwrap_or_else(|| ecdsa_verify(sig, msg, pub_key)) + let res = ecdsa_verify(sig, msg, pub_key); + + if let Some(ext) = self.extension::() { + ext.0 &= res; + } + + res } /// Verify and recover a SECP256k1 ECDSA signature. @@ -1186,8 +1216,10 @@ pub trait OffchainIndex { #[cfg(feature = "std")] sp_externalities::decl_extension! { - /// Batch verification extension to register/retrieve from the externalities. - pub struct VerificationExt(BatchVerifier); + /// Deprecated verification context. + /// + /// Stores the combined result of all verifications that are done in the same context. + struct VerificationExtDeprecated(bool); } /// Interface that provides functions to access the offchain functionality. @@ -1376,7 +1408,7 @@ pub trait Offchain { /// Read all response headers. /// /// Returns a vector of pairs `(HeaderKey, HeaderValue)`. - /// NOTE response headers have to be read before response body. + /// NOTE: response headers have to be read before response body. fn http_response_headers(&mut self, request_id: HttpRequestId) -> Vec<(Vec, Vec)> { self.extension::() .expect("http_response_headers can be called only in the offchain worker context") @@ -1389,7 +1421,7 @@ pub trait Offchain { /// is reached or server closed the connection. /// If `0` is returned it means that the response has been fully consumed /// and the `request_id` is now invalid. - /// NOTE this implies that response headers must be read before draining the body. + /// NOTE: this implies that response headers must be read before draining the body. /// Passing `None` as a deadline blocks forever. fn http_response_read_body( &mut self, @@ -1684,12 +1716,8 @@ pub type SubstrateHostFunctions = ( #[cfg(test)] mod tests { use super::*; - use sp_core::{ - crypto::UncheckedInto, map, storage::Storage, testing::TaskExecutor, - traits::TaskExecutorExt, - }; + use sp_core::{crypto::UncheckedInto, map, storage::Storage}; use sp_state_machine::BasicExternalities; - use std::any::TypeId; #[test] fn storage_works() { @@ -1781,54 +1809,6 @@ mod tests { }); } - #[test] - fn batch_verify_start_finish_works() { - let mut ext = BasicExternalities::default(); - ext.register_extension(TaskExecutorExt::new(TaskExecutor::new())); - - ext.execute_with(|| { - crypto::start_batch_verify(); - }); - - assert!(ext.extensions().get_mut(TypeId::of::()).is_some()); - - ext.execute_with(|| { - assert!(crypto::finish_batch_verify()); - }); - - assert!(ext.extensions().get_mut(TypeId::of::()).is_none()); - } - - #[test] - fn long_sr25519_batching() { - let mut ext = BasicExternalities::default(); - ext.register_extension(TaskExecutorExt::new(TaskExecutor::new())); - ext.execute_with(|| { - let pair = sr25519::Pair::generate_with_phrase(None).0; - let pair_unused = sr25519::Pair::generate_with_phrase(None).0; - crypto::start_batch_verify(); - for it in 0..70 { - let msg = format!("Schnorrkel {}!", it); - let signature = pair.sign(msg.as_bytes()); - crypto::sr25519_batch_verify(&signature, msg.as_bytes(), &pair.public()); - } - - // push invalid - let msg = b"asdf!"; - let signature = pair.sign(msg); - crypto::sr25519_batch_verify(&signature, msg, &pair_unused.public()); - assert!(!crypto::finish_batch_verify()); - - crypto::start_batch_verify(); - for it in 0..70 { - let msg = format!("Schnorrkel {}!", it); - let signature = pair.sign(msg.as_bytes()); - crypto::sr25519_batch_verify(&signature, msg.as_bytes(), &pair.public()); - } - assert!(crypto::finish_batch_verify()); - }); - } - fn zero_ed_pub() -> ed25519::Public { [0u8; 32].unchecked_into() } @@ -1837,90 +1817,6 @@ mod tests { ed25519::Signature::from_raw([0u8; 64]) } - fn zero_sr_pub() -> sr25519::Public { - [0u8; 32].unchecked_into() - } - - fn zero_sr_sig() -> sr25519::Signature { - sr25519::Signature::from_raw([0u8; 64]) - } - - #[test] - fn batching_works() { - let mut ext = BasicExternalities::default(); - ext.register_extension(TaskExecutorExt::new(TaskExecutor::new())); - - ext.execute_with(|| { - // valid ed25519 signature - crypto::start_batch_verify(); - crypto::ed25519_batch_verify(&zero_ed_sig(), &Vec::new(), &zero_ed_pub()); - assert!(crypto::finish_batch_verify()); - - // 2 valid ed25519 signatures - crypto::start_batch_verify(); - - let pair = ed25519::Pair::generate_with_phrase(None).0; - let msg = b"Important message"; - let signature = pair.sign(msg); - crypto::ed25519_batch_verify(&signature, msg, &pair.public()); - - let pair = ed25519::Pair::generate_with_phrase(None).0; - let msg = b"Even more important message"; - let signature = pair.sign(msg); - crypto::ed25519_batch_verify(&signature, msg, &pair.public()); - - assert!(crypto::finish_batch_verify()); - - // 1 valid, 1 invalid ed25519 signature - crypto::start_batch_verify(); - - let pair1 = ed25519::Pair::generate_with_phrase(None).0; - let pair2 = ed25519::Pair::generate_with_phrase(None).0; - let msg = b"Important message"; - let signature = pair1.sign(msg); - - crypto::ed25519_batch_verify(&zero_ed_sig(), &Vec::new(), &zero_ed_pub()); - crypto::ed25519_batch_verify(&signature, msg, &pair1.public()); - crypto::ed25519_batch_verify(&signature, msg, &pair2.public()); - - assert!(!crypto::finish_batch_verify()); - - // 1 valid ed25519, 2 valid sr25519 - crypto::start_batch_verify(); - - let pair = ed25519::Pair::generate_with_phrase(None).0; - let msg = b"Ed25519 batching"; - let signature = pair.sign(msg); - crypto::ed25519_batch_verify(&signature, msg, &pair.public()); - - let pair = sr25519::Pair::generate_with_phrase(None).0; - let msg = b"Schnorrkel rules"; - let signature = pair.sign(msg); - crypto::sr25519_batch_verify(&signature, msg, &pair.public()); - - let pair = sr25519::Pair::generate_with_phrase(None).0; - let msg = b"Schnorrkel batches!"; - let signature = pair.sign(msg); - crypto::sr25519_batch_verify(&signature, msg, &pair.public()); - - assert!(crypto::finish_batch_verify()); - - // 1 valid sr25519, 1 invalid sr25519 - crypto::start_batch_verify(); - - let pair1 = sr25519::Pair::generate_with_phrase(None).0; - let pair2 = sr25519::Pair::generate_with_phrase(None).0; - let msg = b"Schnorrkcel!"; - let signature = pair1.sign(msg); - - crypto::sr25519_batch_verify(&signature, msg, &pair1.public()); - crypto::sr25519_batch_verify(&signature, msg, &pair2.public()); - crypto::sr25519_batch_verify(&zero_sr_sig(), &Vec::new(), &zero_sr_pub()); - - assert!(!crypto::finish_batch_verify()); - }); - } - #[test] fn use_dalek_ext_works() { let mut ext = BasicExternalities::default(); diff --git a/primitives/runtime/src/lib.rs b/primitives/runtime/src/lib.rs index 622eac3d8..2d959425e 100644 --- a/primitives/runtime/src/lib.rs +++ b/primitives/runtime/src/lib.rs @@ -904,40 +904,6 @@ pub fn print(print: impl traits::Printable) { print.print(); } -/// Batching session. -/// -/// To be used in runtime only. Outside of runtime, just construct -/// `BatchVerifier` directly. -#[must_use = "`verify()` needs to be called to finish batch signature verification!"] -pub struct SignatureBatching(bool); - -impl SignatureBatching { - /// Start new batching session. - pub fn start() -> Self { - sp_io::crypto::start_batch_verify(); - SignatureBatching(false) - } - - /// Verify all signatures submitted during the batching session. - #[must_use] - pub fn verify(mut self) -> bool { - self.0 = true; - sp_io::crypto::finish_batch_verify() - } -} - -impl Drop for SignatureBatching { - fn drop(&mut self) { - // Sanity check. If user forgets to actually call `verify()`. - // - // We should not panic if the current thread is already panicking, - // because Rust otherwise aborts the process. - if !self.0 && !sp_std::thread::panicking() { - panic!("Signature verification has not been called before `SignatureBatching::drop`") - } - } -} - /// Describes on what should happen with a storage transaction. pub enum TransactionOutcome { /// Commit the transaction. @@ -962,7 +928,7 @@ mod tests { use super::*; use codec::{Decode, Encode}; - use sp_core::crypto::{Pair, UncheckedFrom}; + use sp_core::crypto::Pair; use sp_io::TestExternalities; use sp_state_machine::create_proof_check_backend; @@ -1045,36 +1011,6 @@ mod tests { assert!(multi_sig.verify(msg, &multi_signer.into_account())); } - #[test] - #[should_panic(expected = "Signature verification has not been called")] - fn batching_still_finishes_when_not_called_directly() { - let mut ext = sp_state_machine::BasicExternalities::default(); - ext.register_extension(sp_core::traits::TaskExecutorExt::new( - sp_core::testing::TaskExecutor::new(), - )); - - ext.execute_with(|| { - let _batching = SignatureBatching::start(); - let dummy = UncheckedFrom::unchecked_from([1; 32]); - let dummy_sig = UncheckedFrom::unchecked_from([1; 64]); - sp_io::crypto::sr25519_verify(&dummy_sig, &Vec::new(), &dummy); - }); - } - - #[test] - #[should_panic(expected = "Hey, I'm an error")] - fn batching_does_not_panic_while_thread_is_already_panicking() { - let mut ext = sp_state_machine::BasicExternalities::default(); - ext.register_extension(sp_core::traits::TaskExecutorExt::new( - sp_core::testing::TaskExecutor::new(), - )); - - ext.execute_with(|| { - let _batching = SignatureBatching::start(); - panic!("Hey, I'm an error"); - }); - } - #[test] fn execute_and_generate_proof_works() { use codec::Encode; diff --git a/primitives/state-machine/src/lib.rs b/primitives/state-machine/src/lib.rs index 90ee962da..c68cf4d00 100644 --- a/primitives/state-machine/src/lib.rs +++ b/primitives/state-machine/src/lib.rs @@ -163,7 +163,7 @@ mod execution { use sp_core::{ hexdisplay::HexDisplay, storage::{ChildInfo, ChildType, PrefixedStorageKey}, - traits::{CallContext, CodeExecutor, ReadRuntimeVersionExt, RuntimeCode, SpawnNamed}, + traits::{CallContext, CodeExecutor, ReadRuntimeVersionExt, RuntimeCode}, }; use sp_externalities::Extensions; use std::{ @@ -324,11 +324,9 @@ mod execution { call_data: &'a [u8], mut extensions: Extensions, runtime_code: &'a RuntimeCode, - spawn_handle: impl SpawnNamed + Send + 'static, context: CallContext, ) -> Self { extensions.register(ReadRuntimeVersionExt::new(exec.clone())); - extensions.register(sp_core::traits::TaskExecutorExt::new(spawn_handle)); Self { backend, @@ -511,11 +509,10 @@ mod execution { } /// Prove execution using the given state backend, overlayed changes, and call executor. - pub fn prove_execution( + pub fn prove_execution( backend: &mut B, overlay: &mut OverlayedChanges, exec: &Exec, - spawn_handle: Spawn, method: &str, call_data: &[u8], runtime_code: &RuntimeCode, @@ -525,14 +522,12 @@ mod execution { H: Hasher, H::Out: Ord + 'static + codec::Codec, Exec: CodeExecutor + Clone + 'static, - Spawn: SpawnNamed + Send + 'static, { let trie_backend = backend.as_trie_backend(); - prove_execution_on_trie_backend::<_, _, _, _>( + prove_execution_on_trie_backend::<_, _, _>( trie_backend, overlay, exec, - spawn_handle, method, call_data, runtime_code, @@ -549,11 +544,10 @@ mod execution { /// /// Note: changes to code will be in place if this call is made again. For running partial /// blocks (e.g. a transaction at a time), ensure a different method is used. - pub fn prove_execution_on_trie_backend( + pub fn prove_execution_on_trie_backend( trie_backend: &TrieBackend, overlay: &mut OverlayedChanges, exec: &Exec, - spawn_handle: Spawn, method: &str, call_data: &[u8], runtime_code: &RuntimeCode, @@ -564,7 +558,6 @@ mod execution { H: Hasher, H::Out: Ord + 'static + codec::Codec, Exec: CodeExecutor + 'static + Clone, - Spawn: SpawnNamed + Send + 'static, { let proving_backend = TrieBackendBuilder::wrap(trie_backend).with_recorder(Default::default()).build(); @@ -577,7 +570,6 @@ mod execution { call_data, extensions, runtime_code, - spawn_handle, CallContext::Offchain, ) .execute_using_consensus_failure_handler::<_>(always_wasm())?; @@ -590,12 +582,11 @@ mod execution { } /// Check execution proof, generated by `prove_execution` call. - pub fn execution_proof_check( + pub fn execution_proof_check( root: H::Out, proof: StorageProof, overlay: &mut OverlayedChanges, exec: &Exec, - spawn_handle: Spawn, method: &str, call_data: &[u8], runtime_code: &RuntimeCode, @@ -604,14 +595,12 @@ mod execution { H: Hasher + 'static, Exec: CodeExecutor + Clone + 'static, H::Out: Ord + 'static + codec::Codec, - Spawn: SpawnNamed + Send + 'static, { let trie_backend = create_proof_check_backend::(root, proof)?; - execution_proof_check_on_trie_backend::<_, _, _>( + execution_proof_check_on_trie_backend::<_, _>( &trie_backend, overlay, exec, - spawn_handle, method, call_data, runtime_code, @@ -619,11 +608,10 @@ mod execution { } /// Check execution proof on proving backend, generated by `prove_execution` call. - pub fn execution_proof_check_on_trie_backend( + pub fn execution_proof_check_on_trie_backend( trie_backend: &TrieBackend, H>, overlay: &mut OverlayedChanges, exec: &Exec, - spawn_handle: Spawn, method: &str, call_data: &[u8], runtime_code: &RuntimeCode, @@ -632,7 +620,6 @@ mod execution { H: Hasher, H::Out: Ord + 'static + codec::Codec, Exec: CodeExecutor + Clone + 'static, - Spawn: SpawnNamed + Send + 'static, { StateMachine::<_, H, Exec>::new( trie_backend, @@ -642,7 +629,6 @@ mod execution { call_data, Extensions::default(), runtime_code, - spawn_handle, CallContext::Offchain, ) .execute_using_consensus_failure_handler(always_untrusted_wasm()) @@ -1309,7 +1295,6 @@ mod tests { use sp_core::{ map, storage::{ChildInfo, StateVersion}, - testing::TaskExecutor, traits::{CallContext, CodeExecutor, Externalities, RuntimeCode}, H256, }; @@ -1384,7 +1369,6 @@ mod tests { &[], Default::default(), &wasm_code, - TaskExecutor::new(), CallContext::Offchain, ); @@ -1413,7 +1397,6 @@ mod tests { &[], Default::default(), &wasm_code, - TaskExecutor::new(), CallContext::Offchain, ); @@ -1443,7 +1426,6 @@ mod tests { &[], Default::default(), &wasm_code, - TaskExecutor::new(), CallContext::Offchain, ); @@ -1475,7 +1457,6 @@ mod tests { &mut remote_backend, &mut Default::default(), &executor, - TaskExecutor::new(), "test", &[], &RuntimeCode::empty(), @@ -1483,12 +1464,11 @@ mod tests { .unwrap(); // check proof locally - let local_result = execution_proof_check::( + let local_result = execution_proof_check::( remote_root, remote_proof, &mut Default::default(), &executor, - TaskExecutor::new(), "test", &[], &RuntimeCode::empty(), diff --git a/primitives/state-machine/src/testing.rs b/primitives/state-machine/src/testing.rs index a9f6399a9..3a0165fe4 100644 --- a/primitives/state-machine/src/testing.rs +++ b/primitives/state-machine/src/testing.rs @@ -34,8 +34,6 @@ use sp_core::{ well_known_keys::{is_child_storage_key, CODE}, StateVersion, Storage, }, - testing::TaskExecutor, - traits::TaskExecutorExt, }; use sp_externalities::{Extension, ExtensionStore, Extensions}; use sp_trie::StorageProof; @@ -105,9 +103,6 @@ where storage.top.insert(CODE.to_vec(), code.to_vec()); - let mut extensions = Extensions::default(); - extensions.register(TaskExecutorExt::new(TaskExecutor::new())); - let offchain_db = TestPersistentOffchainDB::new(); let backend = (storage, state_version).into(); @@ -115,7 +110,7 @@ where TestExternalities { overlay: OverlayedChanges::default(), offchain_db, - extensions, + extensions: Default::default(), backend, storage_transaction_cache: Default::default(), state_version, diff --git a/test-utils/client/src/lib.rs b/test-utils/client/src/lib.rs index a91aa9992..5e3c9f703 100644 --- a/test-utils/client/src/lib.rs +++ b/test-utils/client/src/lib.rs @@ -291,7 +291,6 @@ impl let executor = LocalCallExecutor::new( self.backend.clone(), executor, - Box::new(sp_core::testing::TaskExecutor::new()), Default::default(), ExecutionExtensions::new( self.execution_strategies.clone(), diff --git a/utils/frame/benchmarking-cli/src/pallet/command.rs b/utils/frame/benchmarking-cli/src/pallet/command.rs index 15ebc668f..5016d65b8 100644 --- a/utils/frame/benchmarking-cli/src/pallet/command.rs +++ b/utils/frame/benchmarking-cli/src/pallet/command.rs @@ -238,7 +238,6 @@ impl PalletCmd { &(self.extra).encode(), extensions(), &sp_state_machine::backend::BackendRuntimeCode::new(state).runtime_code()?, - sp_core::testing::TaskExecutor::new(), CallContext::Offchain, ) .execute(strategy.into()) @@ -376,7 +375,6 @@ impl PalletCmd { extensions(), &sp_state_machine::backend::BackendRuntimeCode::new(state) .runtime_code()?, - sp_core::testing::TaskExecutor::new(), CallContext::Offchain, ) .execute(strategy.into()) @@ -417,7 +415,6 @@ impl PalletCmd { extensions(), &sp_state_machine::backend::BackendRuntimeCode::new(state) .runtime_code()?, - sp_core::testing::TaskExecutor::new(), CallContext::Offchain, ) .execute(strategy.into()) @@ -450,7 +447,6 @@ impl PalletCmd { extensions(), &sp_state_machine::backend::BackendRuntimeCode::new(state) .runtime_code()?, - sp_core::testing::TaskExecutor::new(), CallContext::Offchain, ) .execute(strategy.into()) diff --git a/utils/frame/try-runtime/cli/src/lib.rs b/utils/frame/try-runtime/cli/src/lib.rs index e1873f6a0..733eab7f5 100644 --- a/utils/frame/try-runtime/cli/src/lib.rs +++ b/utils/frame/try-runtime/cli/src/lib.rs @@ -377,8 +377,7 @@ use sp_core::{ OffchainDbExt, OffchainWorkerExt, TransactionPoolExt, }, storage::well_known_keys, - testing::TaskExecutor, - traits::{CallContext, ReadRuntimeVersion, TaskExecutorExt}, + traits::{CallContext, ReadRuntimeVersion}, twox_128, H256, }; use sp_externalities::Extensions; @@ -813,7 +812,6 @@ where /// Build all extensions that we typically use. pub(crate) fn full_extensions() -> Extensions { let mut extensions = Extensions::default(); - extensions.register(TaskExecutorExt::new(TaskExecutor::new())); let (offchain, _offchain_state) = TestOffchainExt::new(); let (pool, _pool_state) = TestTransactionPoolExt::new(); let keystore = MemoryKeystore::new(); @@ -875,7 +873,6 @@ pub(crate) fn state_machine_call( data, extensions, &sp_state_machine::backend::BackendRuntimeCode::new(&ext.backend).runtime_code()?, - sp_core::testing::TaskExecutor::new(), CallContext::Offchain, ) .execute(sp_state_machine::ExecutionStrategy::AlwaysWasm) @@ -915,7 +912,6 @@ pub(crate) fn state_machine_call_with_proof Date: Tue, 4 Apr 2023 13:32:10 +0200 Subject: [PATCH 334/558] Uniform pallet warnings (#13798) * Use proc-macro-warning crate Signed-off-by: Oliver Tale-Yazdi * Fixup Signed-off-by: Oliver Tale-Yazdi * Fix pallet_ui tests Also renamed some of the odd-named ones. Signed-off-by: Oliver Tale-Yazdi * Update dep Signed-off-by: Oliver Tale-Yazdi * Ignore hardcoded weight warning To be fixed in https://github.com/paritytech/substrate/issues/13813 Signed-off-by: Oliver Tale-Yazdi * Fix test pallet Signed-off-by: Oliver Tale-Yazdi * Fix more tests Signed-off-by: Oliver Tale-Yazdi --------- Signed-off-by: Oliver Tale-Yazdi Co-authored-by: parity-processbot <> --- Cargo.lock | 12 +++++ frame/benchmarking/pov/src/lib.rs | 4 +- frame/benchmarking/src/tests.rs | 2 +- frame/benchmarking/src/tests_instance.rs | 4 +- frame/collective/src/tests.rs | 2 +- frame/examples/offchain-worker/src/lib.rs | 6 +-- frame/executive/src/lib.rs | 2 +- frame/membership/src/lib.rs | 14 ++--- frame/nicks/src/lib.rs | 8 +-- frame/scored-pool/src/lib.rs | 12 ++--- frame/sudo/src/lib.rs | 2 +- frame/support/procedural/Cargo.toml | 1 + .../procedural/src/pallet/expand/call.rs | 54 ++++++++++++------- frame/support/test/tests/pallet.rs | 8 +-- frame/support/test/tests/pallet_instance.rs | 2 +- .../call_argument_invalid_bound.stderr | 13 +++++ .../call_argument_invalid_bound_2.stderr | 13 +++++ .../call_argument_invalid_bound_3.stderr | 13 +++++ .../tests/pallet_ui/call_missing_index.rs | 5 ++ .../tests/pallet_ui/call_missing_index.stderr | 47 +++++++++++++--- ....rs => call_weight_argument_has_suffix.rs} | 4 +- .../call_weight_argument_has_suffix.stderr | 20 +++++++ .../pallet_ui/call_weight_const_warning.rs | 21 ++++++++ .../call_weight_const_warning.stderr | 12 +++++ .../call_weight_const_warning_twice.rs | 25 +++++++++ .../call_weight_const_warning_twice.stderr | 31 +++++++++++ ...ev_mode_without_arg_max_encoded_len.stderr | 24 ++++++--- .../weight_argument_has_suffix.stderr | 7 --- frame/support/test/tests/storage_layers.rs | 2 +- frame/utility/src/tests.rs | 4 +- 30 files changed, 299 insertions(+), 75 deletions(-) rename frame/support/test/tests/pallet_ui/{weight_argument_has_suffix.rs => call_weight_argument_has_suffix.rs} (70%) create mode 100644 frame/support/test/tests/pallet_ui/call_weight_argument_has_suffix.stderr create mode 100644 frame/support/test/tests/pallet_ui/call_weight_const_warning.rs create mode 100644 frame/support/test/tests/pallet_ui/call_weight_const_warning.stderr create mode 100644 frame/support/test/tests/pallet_ui/call_weight_const_warning_twice.rs create mode 100644 frame/support/test/tests/pallet_ui/call_weight_const_warning_twice.stderr delete mode 100644 frame/support/test/tests/pallet_ui/weight_argument_has_suffix.stderr diff --git a/Cargo.lock b/Cargo.lock index 1e9a87e81..ed633fcaa 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2468,6 +2468,7 @@ dependencies = [ "derive-syn-parse", "frame-support-procedural-tools", "itertools", + "proc-macro-warning", "proc-macro2", "quote", "syn", @@ -7511,6 +7512,17 @@ dependencies = [ "version_check", ] +[[package]] +name = "proc-macro-warning" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9d4f284d87b9cedc2ff57223cbc4e3937cd6063c01e92c8e2a8c080df0013933" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "proc-macro2" version = "1.0.51" diff --git a/frame/benchmarking/pov/src/lib.rs b/frame/benchmarking/pov/src/lib.rs index dccfe72d3..66000e541 100644 --- a/frame/benchmarking/pov/src/lib.rs +++ b/frame/benchmarking/pov/src/lib.rs @@ -120,14 +120,14 @@ pub mod pallet { #[pallet::call] impl Pallet { #[pallet::call_index(0)] - #[pallet::weight(0)] + #[pallet::weight({0})] pub fn emit_event(_origin: OriginFor) -> DispatchResult { Self::deposit_event(Event::TestEvent); Ok(()) } #[pallet::call_index(1)] - #[pallet::weight(0)] + #[pallet::weight({0})] pub fn noop(_origin: OriginFor) -> DispatchResult { Ok(()) } diff --git a/frame/benchmarking/src/tests.rs b/frame/benchmarking/src/tests.rs index 453e8e125..80b631bb7 100644 --- a/frame/benchmarking/src/tests.rs +++ b/frame/benchmarking/src/tests.rs @@ -29,7 +29,7 @@ use sp_runtime::{ use sp_std::prelude::*; use std::cell::RefCell; -#[frame_support::pallet] +#[frame_support::pallet(dev_mode)] mod pallet_test { use frame_support::pallet_prelude::*; use frame_system::pallet_prelude::*; diff --git a/frame/benchmarking/src/tests_instance.rs b/frame/benchmarking/src/tests_instance.rs index d49d7cc81..92d78b9ec 100644 --- a/frame/benchmarking/src/tests_instance.rs +++ b/frame/benchmarking/src/tests_instance.rs @@ -61,7 +61,7 @@ mod pallet_test { ::OtherEvent: Into<>::RuntimeEvent>, { #[pallet::call_index(0)] - #[pallet::weight(0)] + #[pallet::weight({0})] pub fn set_value(origin: OriginFor, n: u32) -> DispatchResult { let _sender = ensure_signed(origin)?; Value::::put(n); @@ -69,7 +69,7 @@ mod pallet_test { } #[pallet::call_index(1)] - #[pallet::weight(0)] + #[pallet::weight({0})] pub fn dummy(origin: OriginFor, _n: u32) -> DispatchResult { let _sender = ensure_none(origin)?; Ok(()) diff --git a/frame/collective/src/tests.rs b/frame/collective/src/tests.rs index 79f6cc27f..2550ab3ed 100644 --- a/frame/collective/src/tests.rs +++ b/frame/collective/src/tests.rs @@ -51,7 +51,7 @@ frame_support::construct_runtime!( mod mock_democracy { pub use pallet::*; - #[frame_support::pallet] + #[frame_support::pallet(dev_mode)] pub mod pallet { use frame_support::pallet_prelude::*; use frame_system::pallet_prelude::*; diff --git a/frame/examples/offchain-worker/src/lib.rs b/frame/examples/offchain-worker/src/lib.rs index 1a63955c0..6ce852417 100644 --- a/frame/examples/offchain-worker/src/lib.rs +++ b/frame/examples/offchain-worker/src/lib.rs @@ -229,7 +229,7 @@ pub mod pallet { /// This example is not focused on correctness of the oracle itself, but rather its /// purpose is to showcase offchain worker capabilities. #[pallet::call_index(0)] - #[pallet::weight(0)] + #[pallet::weight({0})] pub fn submit_price(origin: OriginFor, price: u32) -> DispatchResultWithPostInfo { // Retrieve sender of the transaction. let who = ensure_signed(origin)?; @@ -255,7 +255,7 @@ pub mod pallet { /// This example is not focused on correctness of the oracle itself, but rather its /// purpose is to showcase offchain worker capabilities. #[pallet::call_index(1)] - #[pallet::weight(0)] + #[pallet::weight({0})] pub fn submit_price_unsigned( origin: OriginFor, _block_number: T::BlockNumber, @@ -272,7 +272,7 @@ pub mod pallet { } #[pallet::call_index(2)] - #[pallet::weight(0)] + #[pallet::weight({0})] pub fn submit_price_unsigned_with_signed_payload( origin: OriginFor, price_payload: PricePayload, diff --git a/frame/executive/src/lib.rs b/frame/executive/src/lib.rs index 6a1041a28..aad1de11d 100644 --- a/frame/executive/src/lib.rs +++ b/frame/executive/src/lib.rs @@ -694,7 +694,7 @@ mod tests { const TEST_KEY: &[u8] = b":test:key:"; - #[frame_support::pallet] + #[frame_support::pallet(dev_mode)] mod custom { use frame_support::pallet_prelude::*; use frame_system::pallet_prelude::*; diff --git a/frame/membership/src/lib.rs b/frame/membership/src/lib.rs index 0be33a956..e703b88f3 100644 --- a/frame/membership/src/lib.rs +++ b/frame/membership/src/lib.rs @@ -168,7 +168,7 @@ pub mod pallet { /// /// May only be called from `T::AddOrigin`. #[pallet::call_index(0)] - #[pallet::weight(50_000_000)] + #[pallet::weight({50_000_000})] pub fn add_member(origin: OriginFor, who: AccountIdLookupOf) -> DispatchResult { T::AddOrigin::ensure_origin(origin)?; let who = T::Lookup::lookup(who)?; @@ -191,7 +191,7 @@ pub mod pallet { /// /// May only be called from `T::RemoveOrigin`. #[pallet::call_index(1)] - #[pallet::weight(50_000_000)] + #[pallet::weight({50_000_000})] pub fn remove_member(origin: OriginFor, who: AccountIdLookupOf) -> DispatchResult { T::RemoveOrigin::ensure_origin(origin)?; let who = T::Lookup::lookup(who)?; @@ -215,7 +215,7 @@ pub mod pallet { /// /// Prime membership is *not* passed from `remove` to `add`, if extant. #[pallet::call_index(2)] - #[pallet::weight(50_000_000)] + #[pallet::weight({50_000_000})] pub fn swap_member( origin: OriginFor, remove: AccountIdLookupOf, @@ -249,7 +249,7 @@ pub mod pallet { /// /// May only be called from `T::ResetOrigin`. #[pallet::call_index(3)] - #[pallet::weight(50_000_000)] + #[pallet::weight({50_000_000})] pub fn reset_members(origin: OriginFor, members: Vec) -> DispatchResult { T::ResetOrigin::ensure_origin(origin)?; @@ -272,7 +272,7 @@ pub mod pallet { /// /// Prime membership is passed from the origin account to `new`, if extant. #[pallet::call_index(4)] - #[pallet::weight(50_000_000)] + #[pallet::weight({50_000_000})] pub fn change_key(origin: OriginFor, new: AccountIdLookupOf) -> DispatchResult { let remove = ensure_signed(origin)?; let new = T::Lookup::lookup(new)?; @@ -307,7 +307,7 @@ pub mod pallet { /// /// May only be called from `T::PrimeOrigin`. #[pallet::call_index(5)] - #[pallet::weight(50_000_000)] + #[pallet::weight({50_000_000})] pub fn set_prime(origin: OriginFor, who: AccountIdLookupOf) -> DispatchResult { T::PrimeOrigin::ensure_origin(origin)?; let who = T::Lookup::lookup(who)?; @@ -321,7 +321,7 @@ pub mod pallet { /// /// May only be called from `T::PrimeOrigin`. #[pallet::call_index(6)] - #[pallet::weight(50_000_000)] + #[pallet::weight({50_000_000})] pub fn clear_prime(origin: OriginFor) -> DispatchResult { T::PrimeOrigin::ensure_origin(origin)?; Prime::::kill(); diff --git a/frame/nicks/src/lib.rs b/frame/nicks/src/lib.rs index 5f510ce8e..92865c773 100644 --- a/frame/nicks/src/lib.rs +++ b/frame/nicks/src/lib.rs @@ -131,7 +131,7 @@ pub mod pallet { /// ## Complexity /// - O(1). #[pallet::call_index(0)] - #[pallet::weight(50_000_000)] + #[pallet::weight({50_000_000})] pub fn set_name(origin: OriginFor, name: Vec) -> DispatchResult { let sender = ensure_signed(origin)?; @@ -160,7 +160,7 @@ pub mod pallet { /// ## Complexity /// - O(1). #[pallet::call_index(1)] - #[pallet::weight(70_000_000)] + #[pallet::weight({70_000_000})] pub fn clear_name(origin: OriginFor) -> DispatchResult { let sender = ensure_signed(origin)?; @@ -183,7 +183,7 @@ pub mod pallet { /// ## Complexity /// - O(1). #[pallet::call_index(2)] - #[pallet::weight(70_000_000)] + #[pallet::weight({70_000_000})] pub fn kill_name(origin: OriginFor, target: AccountIdLookupOf) -> DispatchResult { T::ForceOrigin::ensure_origin(origin)?; @@ -207,7 +207,7 @@ pub mod pallet { /// ## Complexity /// - O(1). #[pallet::call_index(3)] - #[pallet::weight(70_000_000)] + #[pallet::weight({70_000_000})] pub fn force_name( origin: OriginFor, target: AccountIdLookupOf, diff --git a/frame/scored-pool/src/lib.rs b/frame/scored-pool/src/lib.rs index 336a69a79..8bd44e2ff 100644 --- a/frame/scored-pool/src/lib.rs +++ b/frame/scored-pool/src/lib.rs @@ -70,7 +70,7 @@ //! //! #[pallet::call] //! impl Pallet { -//! #[pallet::weight(0)] +//! #[pallet::weight({0})] //! pub fn candidate(origin: OriginFor) -> DispatchResult { //! let who = ensure_signed(origin)?; //! @@ -311,7 +311,7 @@ pub mod pallet { /// The `index` parameter of this function must be set to /// the index of the transactor in the `Pool`. #[pallet::call_index(0)] - #[pallet::weight(0)] + #[pallet::weight({0})] pub fn submit_candidacy(origin: OriginFor) -> DispatchResult { let who = ensure_signed(origin)?; ensure!(!>::contains_key(&who), Error::::AlreadyInPool); @@ -341,7 +341,7 @@ pub mod pallet { /// The `index` parameter of this function must be set to /// the index of the transactor in the `Pool`. #[pallet::call_index(1)] - #[pallet::weight(0)] + #[pallet::weight({0})] pub fn withdraw_candidacy(origin: OriginFor, index: u32) -> DispatchResult { let who = ensure_signed(origin)?; @@ -360,7 +360,7 @@ pub mod pallet { /// The `index` parameter of this function must be set to /// the index of `dest` in the `Pool`. #[pallet::call_index(2)] - #[pallet::weight(0)] + #[pallet::weight({0})] pub fn kick( origin: OriginFor, dest: AccountIdLookupOf, @@ -385,7 +385,7 @@ pub mod pallet { /// The `index` parameter of this function must be set to /// the index of the `dest` in the `Pool`. #[pallet::call_index(3)] - #[pallet::weight(0)] + #[pallet::weight({0})] pub fn score( origin: OriginFor, dest: AccountIdLookupOf, @@ -425,7 +425,7 @@ pub mod pallet { /// /// May only be called from root. #[pallet::call_index(4)] - #[pallet::weight(0)] + #[pallet::weight({0})] pub fn change_member_count(origin: OriginFor, count: u32) -> DispatchResult { ensure_root(origin)?; Self::update_member_count(count).map_err(Into::into) diff --git a/frame/sudo/src/lib.rs b/frame/sudo/src/lib.rs index 47309833a..1bc206e00 100644 --- a/frame/sudo/src/lib.rs +++ b/frame/sudo/src/lib.rs @@ -195,7 +195,7 @@ pub mod pallet { /// ## Complexity /// - O(1). #[pallet::call_index(2)] - #[pallet::weight(0)] + #[pallet::weight({0})] // FIXME pub fn set_key( origin: OriginFor, new: AccountIdLookupOf, diff --git a/frame/support/procedural/Cargo.toml b/frame/support/procedural/Cargo.toml index ee1ca4dff..6dbdd3b3a 100644 --- a/frame/support/procedural/Cargo.toml +++ b/frame/support/procedural/Cargo.toml @@ -23,6 +23,7 @@ proc-macro2 = "1.0.37" quote = "1.0.10" syn = { version = "1.0.98", features = ["full"] } frame-support-procedural-tools = { version = "4.0.0-dev", path = "./tools" } +proc-macro-warning = { version = "0.2.0", default-features = false } [features] default = ["std"] diff --git a/frame/support/procedural/src/pallet/expand/call.rs b/frame/support/procedural/src/pallet/expand/call.rs index 0fdf4455a..74075848d 100644 --- a/frame/support/procedural/src/pallet/expand/call.rs +++ b/frame/support/procedural/src/pallet/expand/call.rs @@ -54,30 +54,47 @@ pub fn expand_call(def: &mut Def) -> proc_macro2::TokenStream { .map(|fn_name| format!("Create a call with the variant `{}`.", fn_name)) .collect::>(); - let mut warning_structs = Vec::new(); - let mut warning_names = Vec::new(); + let mut call_index_warnings = Vec::new(); // Emit a warning for each call that is missing `call_index` when not in dev-mode. for method in &methods { if method.explicit_call_index || def.dev_mode { continue } - let name = syn::Ident::new(&format!("{}", method.name), method.name.span()); - let warning: syn::ItemStruct = syn::parse_quote!( - #[deprecated(note = r" - Implicit call indices are deprecated in favour of explicit ones. - Please ensure that all calls have the `pallet::call_index` attribute or that the - `dev-mode` of the pallet is enabled. For more info see: - and - .")] - #[allow(non_camel_case_types)] - struct #name; - ); - warning_names.push(name); - warning_structs.push(warning); + let warning = proc_macro_warning::Warning::new_deprecated("ImplicitCallIndex") + .index(call_index_warnings.len()) + .old("use implicit call indices") + .new("ensure that all calls have a `pallet::call_index` attribute or put the pallet into `dev` mode") + .help_links(&[ + "https://github.com/paritytech/substrate/pull/12891", + "https://github.com/paritytech/substrate/pull/11381" + ]) + .span(method.name.span()) + .build(); + call_index_warnings.push(warning); } let fn_weight = methods.iter().map(|method| &method.weight); + let mut weight_warnings = Vec::new(); + for weight in fn_weight.clone() { + if def.dev_mode { + continue + } + + match weight { + syn::Expr::Lit(lit) => { + let warning = proc_macro_warning::Warning::new_deprecated("ConstantWeight") + .index(weight_warnings.len()) + .old("use hard-coded constant as call weight") + .new("benchmark all calls or put the pallet into `dev` mode") + .help_link("https://github.com/paritytech/substrate/pull/13798") + .span(lit.span()) + .build(); + weight_warnings.push(warning); + }, + _ => {}, + } + } let fn_doc = methods.iter().map(|method| &method.docs).collect::>(); @@ -203,9 +220,10 @@ pub fn expand_call(def: &mut Def) -> proc_macro2::TokenStream { quote::quote_spanned!(span => mod warnings { #( - #warning_structs - // This triggers each deprecated warning once. - const _: Option<#warning_names> = None; + #call_index_warnings + )* + #( + #weight_warnings )* } diff --git a/frame/support/test/tests/pallet.rs b/frame/support/test/tests/pallet.rs index 52acdc5c2..1a367c8b3 100644 --- a/frame/support/test/tests/pallet.rs +++ b/frame/support/test/tests/pallet.rs @@ -218,7 +218,7 @@ pub mod pallet { /// Doc comment put in metadata #[pallet::call_index(1)] - #[pallet::weight(1)] + #[pallet::weight({1})] pub fn foo_storage_layer( _origin: OriginFor, #[pallet::compact] foo: u32, @@ -232,20 +232,20 @@ pub mod pallet { } #[pallet::call_index(4)] - #[pallet::weight(1)] + #[pallet::weight({1})] pub fn foo_index_out_of_order(_origin: OriginFor) -> DispatchResult { Ok(()) } // Test for DispatchResult return type #[pallet::call_index(2)] - #[pallet::weight(1)] + #[pallet::weight({1})] pub fn foo_no_post_info(_origin: OriginFor) -> DispatchResult { Ok(()) } #[pallet::call_index(3)] - #[pallet::weight(1)] + #[pallet::weight({1})] pub fn check_for_dispatch_context(_origin: OriginFor) -> DispatchResult { with_context::<(), _>(|_| ()).ok_or_else(|| DispatchError::Unavailable) } diff --git a/frame/support/test/tests/pallet_instance.rs b/frame/support/test/tests/pallet_instance.rs index 517f920c5..241492a59 100644 --- a/frame/support/test/tests/pallet_instance.rs +++ b/frame/support/test/tests/pallet_instance.rs @@ -27,7 +27,7 @@ use sp_io::{ }; use sp_runtime::{DispatchError, ModuleError}; -#[frame_support::pallet] +#[frame_support::pallet(dev_mode)] pub mod pallet { use codec::MaxEncodedLen; use frame_support::{pallet_prelude::*, parameter_types, scale_info}; diff --git a/frame/support/test/tests/pallet_ui/call_argument_invalid_bound.stderr b/frame/support/test/tests/pallet_ui/call_argument_invalid_bound.stderr index aed53b07b..d10bf1359 100644 --- a/frame/support/test/tests/pallet_ui/call_argument_invalid_bound.stderr +++ b/frame/support/test/tests/pallet_ui/call_argument_invalid_bound.stderr @@ -1,3 +1,16 @@ +error: use of deprecated constant `pallet::warnings::ConstantWeight_0::_w`: + It is deprecated to use hard-coded constant as call weight. + Please instead benchmark all calls or put the pallet into `dev` mode. + + For more info see: + + --> tests/pallet_ui/call_argument_invalid_bound.rs:19:20 + | +19 | #[pallet::weight(0)] + | ^ + | + = note: `-D deprecated` implied by `-D warnings` + error[E0277]: `::Bar` doesn't implement `std::fmt::Debug` --> tests/pallet_ui/call_argument_invalid_bound.rs:21:36 | diff --git a/frame/support/test/tests/pallet_ui/call_argument_invalid_bound_2.stderr b/frame/support/test/tests/pallet_ui/call_argument_invalid_bound_2.stderr index 476258c03..7173cdcd4 100644 --- a/frame/support/test/tests/pallet_ui/call_argument_invalid_bound_2.stderr +++ b/frame/support/test/tests/pallet_ui/call_argument_invalid_bound_2.stderr @@ -1,3 +1,16 @@ +error: use of deprecated constant `pallet::warnings::ConstantWeight_0::_w`: + It is deprecated to use hard-coded constant as call weight. + Please instead benchmark all calls or put the pallet into `dev` mode. + + For more info see: + + --> tests/pallet_ui/call_argument_invalid_bound_2.rs:19:20 + | +19 | #[pallet::weight(0)] + | ^ + | + = note: `-D deprecated` implied by `-D warnings` + error[E0277]: `::Bar` doesn't implement `std::fmt::Debug` --> tests/pallet_ui/call_argument_invalid_bound_2.rs:21:36 | diff --git a/frame/support/test/tests/pallet_ui/call_argument_invalid_bound_3.stderr b/frame/support/test/tests/pallet_ui/call_argument_invalid_bound_3.stderr index 395da09cb..1b084dd2f 100644 --- a/frame/support/test/tests/pallet_ui/call_argument_invalid_bound_3.stderr +++ b/frame/support/test/tests/pallet_ui/call_argument_invalid_bound_3.stderr @@ -1,3 +1,16 @@ +error: use of deprecated constant `pallet::warnings::ConstantWeight_0::_w`: + It is deprecated to use hard-coded constant as call weight. + Please instead benchmark all calls or put the pallet into `dev` mode. + + For more info see: + + --> tests/pallet_ui/call_argument_invalid_bound_3.rs:21:20 + | +21 | #[pallet::weight(0)] + | ^ + | + = note: `-D deprecated` implied by `-D warnings` + error[E0277]: `Bar` doesn't implement `std::fmt::Debug` --> tests/pallet_ui/call_argument_invalid_bound_3.rs:23:36 | diff --git a/frame/support/test/tests/pallet_ui/call_missing_index.rs b/frame/support/test/tests/pallet_ui/call_missing_index.rs index 7e305a573..98c49f493 100644 --- a/frame/support/test/tests/pallet_ui/call_missing_index.rs +++ b/frame/support/test/tests/pallet_ui/call_missing_index.rs @@ -15,6 +15,11 @@ mod pallet { pub fn foo(_: OriginFor) -> DispatchResult { Ok(()) } + + #[pallet::weight(0)] + pub fn bar(_: OriginFor) -> DispatchResult { + Ok(()) + } } } diff --git a/frame/support/test/tests/pallet_ui/call_missing_index.stderr b/frame/support/test/tests/pallet_ui/call_missing_index.stderr index 9d0020497..82dbe1d24 100644 --- a/frame/support/test/tests/pallet_ui/call_missing_index.stderr +++ b/frame/support/test/tests/pallet_ui/call_missing_index.stderr @@ -1,12 +1,47 @@ -error: use of deprecated struct `pallet::warnings::foo`: - Implicit call indices are deprecated in favour of explicit ones. - Please ensure that all calls have the `pallet::call_index` attribute or that the - `dev-mode` of the pallet is enabled. For more info see: - and - . +error: use of deprecated constant `pallet::warnings::ImplicitCallIndex_0::_w`: + It is deprecated to use implicit call indices. + Please instead ensure that all calls have a `pallet::call_index` attribute or put the pallet into `dev` mode. + + For more info see: + + --> tests/pallet_ui/call_missing_index.rs:15:10 | 15 | pub fn foo(_: OriginFor) -> DispatchResult { | ^^^ | = note: `-D deprecated` implied by `-D warnings` + +error: use of deprecated constant `pallet::warnings::ImplicitCallIndex_1::_w`: + It is deprecated to use implicit call indices. + Please instead ensure that all calls have a `pallet::call_index` attribute or put the pallet into `dev` mode. + + For more info see: + + + --> tests/pallet_ui/call_missing_index.rs:20:10 + | +20 | pub fn bar(_: OriginFor) -> DispatchResult { + | ^^^ + +error: use of deprecated constant `pallet::warnings::ConstantWeight_0::_w`: + It is deprecated to use hard-coded constant as call weight. + Please instead benchmark all calls or put the pallet into `dev` mode. + + For more info see: + + --> tests/pallet_ui/call_missing_index.rs:14:20 + | +14 | #[pallet::weight(0)] + | ^ + +error: use of deprecated constant `pallet::warnings::ConstantWeight_1::_w`: + It is deprecated to use hard-coded constant as call weight. + Please instead benchmark all calls or put the pallet into `dev` mode. + + For more info see: + + --> tests/pallet_ui/call_missing_index.rs:19:20 + | +19 | #[pallet::weight(0)] + | ^ diff --git a/frame/support/test/tests/pallet_ui/weight_argument_has_suffix.rs b/frame/support/test/tests/pallet_ui/call_weight_argument_has_suffix.rs similarity index 70% rename from frame/support/test/tests/pallet_ui/weight_argument_has_suffix.rs rename to frame/support/test/tests/pallet_ui/call_weight_argument_has_suffix.rs index 99195d21b..c122877e8 100644 --- a/frame/support/test/tests/pallet_ui/weight_argument_has_suffix.rs +++ b/frame/support/test/tests/pallet_ui/call_weight_argument_has_suffix.rs @@ -1,6 +1,6 @@ #[frame_support::pallet] mod pallet { - use frame_support::pallet_prelude::DispatchResultWithPostInfo; + use frame_support::pallet_prelude::DispatchResult; use frame_system::pallet_prelude::OriginFor; #[pallet::config] @@ -13,7 +13,7 @@ mod pallet { impl Pallet { #[pallet::call_index(0)] #[pallet::weight(10_000something)] - pub fn foo(origin: OriginFor) -> DispatchResultWithPostInfo { Ok(().into()) } + pub fn foo(_: OriginFor) -> DispatchResult { Ok(()) } } } diff --git a/frame/support/test/tests/pallet_ui/call_weight_argument_has_suffix.stderr b/frame/support/test/tests/pallet_ui/call_weight_argument_has_suffix.stderr new file mode 100644 index 000000000..0651f003b --- /dev/null +++ b/frame/support/test/tests/pallet_ui/call_weight_argument_has_suffix.stderr @@ -0,0 +1,20 @@ +error: invalid suffix `something` for number literal + --> tests/pallet_ui/call_weight_argument_has_suffix.rs:15:26 + | +15 | #[pallet::weight(10_000something)] + | ^^^^^^^^^^^^^^^ invalid suffix `something` + | + = help: the suffix must be one of the numeric types (`u32`, `isize`, `f32`, etc.) + +error: use of deprecated constant `pallet::warnings::ConstantWeight_0::_w`: + It is deprecated to use hard-coded constant as call weight. + Please instead benchmark all calls or put the pallet into `dev` mode. + + For more info see: + + --> tests/pallet_ui/call_weight_argument_has_suffix.rs:15:26 + | +15 | #[pallet::weight(10_000something)] + | ^^^^^^^^^^^^^^^ + | + = note: `-D deprecated` implied by `-D warnings` diff --git a/frame/support/test/tests/pallet_ui/call_weight_const_warning.rs b/frame/support/test/tests/pallet_ui/call_weight_const_warning.rs new file mode 100644 index 000000000..2e5dc2a64 --- /dev/null +++ b/frame/support/test/tests/pallet_ui/call_weight_const_warning.rs @@ -0,0 +1,21 @@ +#[frame_support::pallet] +mod pallet { + use frame_support::pallet_prelude::DispatchResult; + use frame_system::pallet_prelude::OriginFor; + + #[pallet::config] + pub trait Config: frame_system::Config {} + + #[pallet::pallet] + pub struct Pallet(core::marker::PhantomData); + + #[pallet::call] + impl Pallet { + #[pallet::call_index(0)] + #[pallet::weight(123_u64)] + pub fn foo(_: OriginFor) -> DispatchResult { Ok(()) } + } +} + +fn main() { +} diff --git a/frame/support/test/tests/pallet_ui/call_weight_const_warning.stderr b/frame/support/test/tests/pallet_ui/call_weight_const_warning.stderr new file mode 100644 index 000000000..b04c3ec39 --- /dev/null +++ b/frame/support/test/tests/pallet_ui/call_weight_const_warning.stderr @@ -0,0 +1,12 @@ +error: use of deprecated constant `pallet::warnings::ConstantWeight_0::_w`: + It is deprecated to use hard-coded constant as call weight. + Please instead benchmark all calls or put the pallet into `dev` mode. + + For more info see: + + --> tests/pallet_ui/call_weight_const_warning.rs:15:26 + | +15 | #[pallet::weight(123_u64)] + | ^^^^^^^ + | + = note: `-D deprecated` implied by `-D warnings` diff --git a/frame/support/test/tests/pallet_ui/call_weight_const_warning_twice.rs b/frame/support/test/tests/pallet_ui/call_weight_const_warning_twice.rs new file mode 100644 index 000000000..798911dba --- /dev/null +++ b/frame/support/test/tests/pallet_ui/call_weight_const_warning_twice.rs @@ -0,0 +1,25 @@ +#[frame_support::pallet] +mod pallet { + use frame_support::pallet_prelude::DispatchResult; + use frame_system::pallet_prelude::OriginFor; + + #[pallet::config] + pub trait Config: frame_system::Config {} + + #[pallet::pallet] + pub struct Pallet(core::marker::PhantomData); + + #[pallet::call] + impl Pallet { + #[pallet::call_index(0)] + #[pallet::weight(123)] + pub fn foo(_: OriginFor) -> DispatchResult { Ok(()) } + + #[pallet::call_index(1)] + #[pallet::weight(123_custom_prefix)] + pub fn bar(_: OriginFor) -> DispatchResult { Ok(()) } + } +} + +fn main() { +} diff --git a/frame/support/test/tests/pallet_ui/call_weight_const_warning_twice.stderr b/frame/support/test/tests/pallet_ui/call_weight_const_warning_twice.stderr new file mode 100644 index 000000000..c65679020 --- /dev/null +++ b/frame/support/test/tests/pallet_ui/call_weight_const_warning_twice.stderr @@ -0,0 +1,31 @@ +error: invalid suffix `custom_prefix` for number literal + --> tests/pallet_ui/call_weight_const_warning_twice.rs:19:26 + | +19 | #[pallet::weight(123_custom_prefix)] + | ^^^^^^^^^^^^^^^^^ invalid suffix `custom_prefix` + | + = help: the suffix must be one of the numeric types (`u32`, `isize`, `f32`, etc.) + +error: use of deprecated constant `pallet::warnings::ConstantWeight_0::_w`: + It is deprecated to use hard-coded constant as call weight. + Please instead benchmark all calls or put the pallet into `dev` mode. + + For more info see: + + --> tests/pallet_ui/call_weight_const_warning_twice.rs:15:26 + | +15 | #[pallet::weight(123)] + | ^^^ + | + = note: `-D deprecated` implied by `-D warnings` + +error: use of deprecated constant `pallet::warnings::ConstantWeight_1::_w`: + It is deprecated to use hard-coded constant as call weight. + Please instead benchmark all calls or put the pallet into `dev` mode. + + For more info see: + + --> tests/pallet_ui/call_weight_const_warning_twice.rs:19:26 + | +19 | #[pallet::weight(123_custom_prefix)] + | ^^^^^^^^^^^^^^^^^ diff --git a/frame/support/test/tests/pallet_ui/dev_mode_without_arg_max_encoded_len.stderr b/frame/support/test/tests/pallet_ui/dev_mode_without_arg_max_encoded_len.stderr index c9de99113..cf502ac5b 100644 --- a/frame/support/test/tests/pallet_ui/dev_mode_without_arg_max_encoded_len.stderr +++ b/frame/support/test/tests/pallet_ui/dev_mode_without_arg_max_encoded_len.stderr @@ -1,9 +1,10 @@ -error: use of deprecated struct `pallet::warnings::my_call`: - Implicit call indices are deprecated in favour of explicit ones. - Please ensure that all calls have the `pallet::call_index` attribute or that the - `dev-mode` of the pallet is enabled. For more info see: - and - . +error: use of deprecated constant `pallet::warnings::ImplicitCallIndex_0::_w`: + It is deprecated to use implicit call indices. + Please instead ensure that all calls have a `pallet::call_index` attribute or put the pallet into `dev` mode. + + For more info see: + + --> tests/pallet_ui/dev_mode_without_arg_max_encoded_len.rs:25:10 | 25 | pub fn my_call(_origin: OriginFor) -> DispatchResult { @@ -11,6 +12,17 @@ error: use of deprecated struct `pallet::warnings::my_call`: | = note: `-D deprecated` implied by `-D warnings` +error: use of deprecated constant `pallet::warnings::ConstantWeight_0::_w`: + It is deprecated to use hard-coded constant as call weight. + Please instead benchmark all calls or put the pallet into `dev` mode. + + For more info see: + + --> tests/pallet_ui/dev_mode_without_arg_max_encoded_len.rs:24:20 + | +24 | #[pallet::weight(0)] + | ^ + error[E0277]: the trait bound `Vec: MaxEncodedLen` is not satisfied --> tests/pallet_ui/dev_mode_without_arg_max_encoded_len.rs:11:12 | diff --git a/frame/support/test/tests/pallet_ui/weight_argument_has_suffix.stderr b/frame/support/test/tests/pallet_ui/weight_argument_has_suffix.stderr deleted file mode 100644 index df9b0798a..000000000 --- a/frame/support/test/tests/pallet_ui/weight_argument_has_suffix.stderr +++ /dev/null @@ -1,7 +0,0 @@ -error: invalid suffix `something` for number literal - --> tests/pallet_ui/weight_argument_has_suffix.rs:15:26 - | -15 | #[pallet::weight(10_000something)] - | ^^^^^^^^^^^^^^^ invalid suffix `something` - | - = help: the suffix must be one of the numeric types (`u32`, `isize`, `f32`, etc.) diff --git a/frame/support/test/tests/storage_layers.rs b/frame/support/test/tests/storage_layers.rs index f124aed9a..82174bf9d 100644 --- a/frame/support/test/tests/storage_layers.rs +++ b/frame/support/test/tests/storage_layers.rs @@ -22,7 +22,7 @@ use frame_support::{ use pallet::*; use sp_io::TestExternalities; -#[frame_support::pallet] +#[frame_support::pallet(dev_mode)] pub mod pallet { use frame_support::{ensure, pallet_prelude::*}; use frame_system::pallet_prelude::*; diff --git a/frame/utility/src/tests.rs b/frame/utility/src/tests.rs index 04f272824..d23c57e69 100644 --- a/frame/utility/src/tests.rs +++ b/frame/utility/src/tests.rs @@ -41,7 +41,7 @@ use sp_runtime::{ type BlockNumber = u64; // example module to test behaviors. -#[frame_support::pallet] +#[frame_support::pallet(dev_mode)] pub mod example { use frame_support::{dispatch::WithPostDispatchInfo, pallet_prelude::*}; use frame_system::pallet_prelude::*; @@ -91,7 +91,7 @@ pub mod example { mod mock_democracy { pub use pallet::*; - #[frame_support::pallet] + #[frame_support::pallet(dev_mode)] pub mod pallet { use frame_support::pallet_prelude::*; use frame_system::pallet_prelude::*; From 1f0973906057ebdf58b88e3681847e79f6b9c7a9 Mon Sep 17 00:00:00 2001 From: Fredrik Simonsson Date: Tue, 4 Apr 2023 20:42:46 +0900 Subject: [PATCH 335/558] Update documentation for uniques (This PR renames classes and instances to collections and items in the Uniques pallet in order to follow the commonly accepted NFTs terminology.) (#13322) * Update documentation for uniques The documentation was outdated after merge of #11389 Using the widely spread term collections and item instead of the previous class and instance. * Update README.md --------- Co-authored-by: Jegor Sidorenko <5252494+jsidorenko@users.noreply.github.com> Co-authored-by: Jegor Sidorenko --- frame/uniques/README.md | 74 ++++++++++++++++++++++------------------- 1 file changed, 39 insertions(+), 35 deletions(-) diff --git a/frame/uniques/README.md b/frame/uniques/README.md index 8a91a558b..6cdbcf79f 100644 --- a/frame/uniques/README.md +++ b/frame/uniques/README.md @@ -4,67 +4,71 @@ A simple, secure module for dealing with non-fungible assets. ## Overview -The Uniques module provides functionality for asset management of non-fungible asset classes, including: +The Uniques module provides functionality for non-fungible tokens' management, including: -* Asset Issuance -* Asset Transfer -* Asset Destruction +* Collection Creation +* Item Minting +* Item Transfers +* Item Trading methods +* Attributes Management +* Item Burning -To use it in your runtime, you need to implement the assets [`uniques::Config`](https://paritytech.github.io/substrate/master/pallet_uniques/pallet/trait.Config.html). +To use it in your runtime, you need to implement [`uniques::Config`](https://paritytech.github.io/substrate/master/pallet_uniques/pallet/trait.Config.html). The supported dispatchable functions are documented in the [`uniques::Call`](https://paritytech.github.io/substrate/master/pallet_uniques/pallet/enum.Call.html) enum. ### Terminology -* **Asset issuance:** The creation of a new asset instance. -* **Asset transfer:** The action of transferring an asset instance from one account to another. -* **Asset burning:** The destruction of an asset instance. -* **Non-fungible asset:** An asset for which each unit has unique characteristics. There is exactly - one instance of such an asset in existence and there is exactly one owning account. +* **Collection creation:** The creation of a new collection. +* **Item minting:** The action of creating a new item within a collection. +* **Item transfer:** The action of sending an item from one account to another. +* **Item burning:** The destruction of an item. +* **Non-fungible token (NFT):** An item for which each unit has unique characteristics. There is exactly + one instance of such an item in existence and there is exactly one owning account. ### Goals The Uniques pallet in Substrate is designed to make the following possible: -* Allow accounts to permissionlessly create asset classes (collections of asset instances). -* Allow a named (permissioned) account to mint and burn unique assets within a class. -* Move asset instances between accounts permissionlessly. -* Allow a named (permissioned) account to freeze and unfreeze unique assets within a - class or the entire class. -* Allow the owner of an asset instance to delegate the ability to transfer the asset to some +* Allow accounts to permissionlessly create NFT collections. +* Allow a named (permissioned) account to mint and burn unique items within a collection. +* Move items between accounts permissionlessly. +* Allow a named (permissioned) account to freeze and unfreeze unique items within a + collection or the entire collection. +* Allow the owner of an item to delegate the ability to transfer the item to some named third-party. ## Interface ### Permissionless dispatchables -* `create`: Create a new asset class by placing a deposit. -* `transfer`: Transfer an asset instance to a new owner. -* `redeposit`: Update the deposit amount of an asset instance, potentially freeing funds. +* `create`: Create a new collection by placing a deposit. +* `transfer`: Transfer an item to a new owner. +* `redeposit`: Update the deposit amount of an item, potentially freeing funds. * `approve_transfer`: Name a delegate who may authorise a transfer. * `cancel_approval`: Revert the effects of a previous `approve_transfer`. ### Permissioned dispatchables -* `destroy`: Destroy an asset class. -* `mint`: Mint a new asset instance within an asset class. -* `burn`: Burn an asset instance within an asset class. -* `freeze`: Prevent an individual asset from being transferred. +* `destroy`: Destroy a collection. +* `mint`: Mint a new item within a collection. +* `burn`: Burn an item within a collection. +* `freeze`: Prevent an individual item from being transferred. * `thaw`: Revert the effects of a previous `freeze`. -* `freeze_class`: Prevent all asset within a class from being transferred. -* `thaw_class`: Revert the effects of a previous `freeze_class`. -* `transfer_ownership`: Alter the owner of an asset class, moving all associated deposits. -* `set_team`: Alter the permissioned accounts of an asset class. +* `freeze_collection`: Prevent all items within a collection from being transferred. +* `thaw_collection`: Revert the effects of a previous `freeze_collection`. +* `transfer_ownership`: Alter the owner of a collection, moving all associated deposits. +* `set_team`: Alter the permissioned accounts of a collection. ### Metadata (permissioned) dispatchables -* `set_attribute`: Set a metadata attribute of an asset instance or class. -* `clear_attribute`: Remove a metadata attribute of an asset instance or class. -* `set_metadata`: Set general metadata of an asset instance. -* `clear_metadata`: Remove general metadata of an asset instance. -* `set_class_metadata`: Set general metadata of an asset class. -* `clear_class_metadata`: Remove general metadata of an asset class. +* `set_attribute`: Set an attribute of an item or collection. +* `clear_attribute`: Remove an attribute of an item or collection. +* `set_metadata`: Set general metadata of an item. +* `clear_metadata`: Remove general metadata of an item. +* `set_collection_metadata`: Set general metadata of a collection. +* `clear_collection_metadata`: Remove general metadata of a collection. ### Force (i.e. governance) dispatchables -* `force_create`: Create a new asset class. -* `force_asset_status`: Alter the underlying characteristics of an asset class. +* `force_create`: Create a new collection. +* `force_asset_status`: Alter the underlying characteristics of a collection. Please refer to the [`Call`](https://paritytech.github.io/substrate/master/pallet_uniques/pallet/enum.Call.html) enum and its associated variants for documentation on each function. From 057f2afd686dceb48c08d8a31f35b338bb5992d3 Mon Sep 17 00:00:00 2001 From: Keith Yeung Date: Tue, 4 Apr 2023 20:28:46 +0800 Subject: [PATCH 336/558] Implement #[pallet::composite_enum] (#13722) * Implement #[pallet::hold_reason] * Appease clippy * cargo fmt * Update test expectations * Update test expectations * Support composite_enum attribute instead * Update test expectations * Change hold_reason to composite_enum * Add UI test for unsupported identifier when using composite_enum * Fix comment * Add documentation for pallet::composable_enum * More docs * cargo fmt --- .../construct_runtime/expand/freeze_reason.rs | 67 +++++++++ .../construct_runtime/expand/hold_reason.rs | 67 +++++++++ .../src/construct_runtime/expand/lock_id.rs | 67 +++++++++ .../src/construct_runtime/expand/mod.rs | 8 + .../construct_runtime/expand/slash_reason.rs | 67 +++++++++ .../procedural/src/construct_runtime/mod.rs | 12 ++ .../procedural/src/construct_runtime/parse.rs | 24 +++ frame/support/procedural/src/lib.rs | 27 ++++ .../src/pallet/expand/tt_default_parts.rs | 32 +++- frame/support/procedural/src/pallet/mod.rs | 2 +- .../procedural/src/pallet/parse/composite.rs | 139 ++++++++++++++++++ .../procedural/src/pallet/parse/event.rs | 2 +- .../procedural/src/pallet/parse/mod.rs | 36 +++++ frame/support/src/lib.rs | 33 ++++- .../invalid_module_details_keyword.stderr | 2 +- .../invalid_module_entry.stderr | 2 +- frame/support/test/tests/pallet.rs | 26 ++++ .../composite_enum_unsupported_identifier.rs | 13 ++ ...mposite_enum_unsupported_identifier.stderr | 5 + .../tests/pallet_ui/event_wrong_item.stderr | 2 +- .../tests/pallet_ui/hold_reason_non_enum.rs | 14 ++ .../pallet_ui/hold_reason_non_enum.stderr | 5 + .../tests/pallet_ui/hold_reason_not_pub.rs | 14 ++ .../pallet_ui/hold_reason_not_pub.stderr | 5 + .../test/tests/pallet_ui/lock_id_duplicate.rs | 17 +++ .../tests/pallet_ui/lock_id_duplicate.stderr | 5 + 26 files changed, 682 insertions(+), 11 deletions(-) create mode 100644 frame/support/procedural/src/construct_runtime/expand/freeze_reason.rs create mode 100644 frame/support/procedural/src/construct_runtime/expand/hold_reason.rs create mode 100644 frame/support/procedural/src/construct_runtime/expand/lock_id.rs create mode 100644 frame/support/procedural/src/construct_runtime/expand/slash_reason.rs create mode 100644 frame/support/procedural/src/pallet/parse/composite.rs create mode 100644 frame/support/test/tests/pallet_ui/composite_enum_unsupported_identifier.rs create mode 100644 frame/support/test/tests/pallet_ui/composite_enum_unsupported_identifier.stderr create mode 100644 frame/support/test/tests/pallet_ui/hold_reason_non_enum.rs create mode 100644 frame/support/test/tests/pallet_ui/hold_reason_non_enum.stderr create mode 100644 frame/support/test/tests/pallet_ui/hold_reason_not_pub.rs create mode 100644 frame/support/test/tests/pallet_ui/hold_reason_not_pub.stderr create mode 100644 frame/support/test/tests/pallet_ui/lock_id_duplicate.rs create mode 100644 frame/support/test/tests/pallet_ui/lock_id_duplicate.stderr diff --git a/frame/support/procedural/src/construct_runtime/expand/freeze_reason.rs b/frame/support/procedural/src/construct_runtime/expand/freeze_reason.rs new file mode 100644 index 000000000..889168bf6 --- /dev/null +++ b/frame/support/procedural/src/construct_runtime/expand/freeze_reason.rs @@ -0,0 +1,67 @@ +// This file is part of Substrate. + +// Copyright (C) Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License + +use crate::construct_runtime::{parse::PalletPath, Pallet}; +use proc_macro2::{Ident, TokenStream}; +use quote::quote; + +pub fn expand_outer_freeze_reason(pallet_decls: &[Pallet], scrate: &TokenStream) -> TokenStream { + let mut conversion_fns = Vec::new(); + let mut freeze_reason_variants = Vec::new(); + for decl in pallet_decls { + if let Some(_) = decl.find_part("FreezeReason") { + let variant_name = &decl.name; + let path = &decl.path; + let index = decl.index; + + conversion_fns.push(expand_conversion_fn(path, variant_name)); + + freeze_reason_variants.push(expand_variant(index, path, variant_name)); + } + } + + quote! { + #[derive( + Copy, Clone, Eq, PartialEq, Ord, PartialOrd, + #scrate::codec::Encode, #scrate::codec::Decode, #scrate::codec::MaxEncodedLen, + #scrate::scale_info::TypeInfo, + #scrate::RuntimeDebug, + )] + pub enum RuntimeFreezeReason { + #( #freeze_reason_variants )* + } + + #( #conversion_fns )* + } +} + +fn expand_conversion_fn(path: &PalletPath, variant_name: &Ident) -> TokenStream { + quote! { + impl From<#path::FreezeReason> for RuntimeFreezeReason { + fn from(hr: #path::FreezeReason) -> Self { + RuntimeFreezeReason::#variant_name(hr) + } + } + } +} + +fn expand_variant(index: u8, path: &PalletPath, variant_name: &Ident) -> TokenStream { + quote! { + #[codec(index = #index)] + #variant_name(#path::FreezeReason), + } +} diff --git a/frame/support/procedural/src/construct_runtime/expand/hold_reason.rs b/frame/support/procedural/src/construct_runtime/expand/hold_reason.rs new file mode 100644 index 000000000..60fc094a4 --- /dev/null +++ b/frame/support/procedural/src/construct_runtime/expand/hold_reason.rs @@ -0,0 +1,67 @@ +// This file is part of Substrate. + +// Copyright (C) Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License + +use crate::construct_runtime::{parse::PalletPath, Pallet}; +use proc_macro2::{Ident, TokenStream}; +use quote::quote; + +pub fn expand_outer_hold_reason(pallet_decls: &[Pallet], scrate: &TokenStream) -> TokenStream { + let mut conversion_fns = Vec::new(); + let mut hold_reason_variants = Vec::new(); + for decl in pallet_decls { + if let Some(_) = decl.find_part("HoldReason") { + let variant_name = &decl.name; + let path = &decl.path; + let index = decl.index; + + conversion_fns.push(expand_conversion_fn(path, variant_name)); + + hold_reason_variants.push(expand_variant(index, path, variant_name)); + } + } + + quote! { + #[derive( + Copy, Clone, Eq, PartialEq, Ord, PartialOrd, + #scrate::codec::Encode, #scrate::codec::Decode, #scrate::codec::MaxEncodedLen, + #scrate::scale_info::TypeInfo, + #scrate::RuntimeDebug, + )] + pub enum RuntimeHoldReason { + #( #hold_reason_variants )* + } + + #( #conversion_fns )* + } +} + +fn expand_conversion_fn(path: &PalletPath, variant_name: &Ident) -> TokenStream { + quote! { + impl From<#path::HoldReason> for RuntimeHoldReason { + fn from(hr: #path::HoldReason) -> Self { + RuntimeHoldReason::#variant_name(hr) + } + } + } +} + +fn expand_variant(index: u8, path: &PalletPath, variant_name: &Ident) -> TokenStream { + quote! { + #[codec(index = #index)] + #variant_name(#path::HoldReason), + } +} diff --git a/frame/support/procedural/src/construct_runtime/expand/lock_id.rs b/frame/support/procedural/src/construct_runtime/expand/lock_id.rs new file mode 100644 index 000000000..77c6fefa0 --- /dev/null +++ b/frame/support/procedural/src/construct_runtime/expand/lock_id.rs @@ -0,0 +1,67 @@ +// This file is part of Substrate. + +// Copyright (C) Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License + +use crate::construct_runtime::{parse::PalletPath, Pallet}; +use proc_macro2::{Ident, TokenStream}; +use quote::quote; + +pub fn expand_outer_lock_id(pallet_decls: &[Pallet], scrate: &TokenStream) -> TokenStream { + let mut conversion_fns = Vec::new(); + let mut lock_id_variants = Vec::new(); + for decl in pallet_decls { + if let Some(_) = decl.find_part("LockId") { + let variant_name = &decl.name; + let path = &decl.path; + let index = decl.index; + + conversion_fns.push(expand_conversion_fn(path, variant_name)); + + lock_id_variants.push(expand_variant(index, path, variant_name)); + } + } + + quote! { + #[derive( + Copy, Clone, Eq, PartialEq, Ord, PartialOrd, + #scrate::codec::Encode, #scrate::codec::Decode, #scrate::codec::MaxEncodedLen, + #scrate::scale_info::TypeInfo, + #scrate::RuntimeDebug, + )] + pub enum RuntimeLockId { + #( #lock_id_variants )* + } + + #( #conversion_fns )* + } +} + +fn expand_conversion_fn(path: &PalletPath, variant_name: &Ident) -> TokenStream { + quote! { + impl From<#path::LockId> for RuntimeLockId { + fn from(hr: #path::LockId) -> Self { + RuntimeLockId::#variant_name(hr) + } + } + } +} + +fn expand_variant(index: u8, path: &PalletPath, variant_name: &Ident) -> TokenStream { + quote! { + #[codec(index = #index)] + #variant_name(#path::LockId), + } +} diff --git a/frame/support/procedural/src/construct_runtime/expand/mod.rs b/frame/support/procedural/src/construct_runtime/expand/mod.rs index ace0b23bd..0fd98bb4d 100644 --- a/frame/support/procedural/src/construct_runtime/expand/mod.rs +++ b/frame/support/procedural/src/construct_runtime/expand/mod.rs @@ -18,15 +18,23 @@ mod call; mod config; mod event; +mod freeze_reason; +mod hold_reason; mod inherent; +mod lock_id; mod metadata; mod origin; +mod slash_reason; mod unsigned; pub use call::expand_outer_dispatch; pub use config::expand_outer_config; pub use event::expand_outer_event; +pub use freeze_reason::expand_outer_freeze_reason; +pub use hold_reason::expand_outer_hold_reason; pub use inherent::expand_outer_inherent; +pub use lock_id::expand_outer_lock_id; pub use metadata::expand_runtime_metadata; pub use origin::expand_outer_origin; +pub use slash_reason::expand_outer_slash_reason; pub use unsigned::expand_outer_validate_unsigned; diff --git a/frame/support/procedural/src/construct_runtime/expand/slash_reason.rs b/frame/support/procedural/src/construct_runtime/expand/slash_reason.rs new file mode 100644 index 000000000..2bf647aa7 --- /dev/null +++ b/frame/support/procedural/src/construct_runtime/expand/slash_reason.rs @@ -0,0 +1,67 @@ +// This file is part of Substrate. + +// Copyright (C) Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License + +use crate::construct_runtime::{parse::PalletPath, Pallet}; +use proc_macro2::{Ident, TokenStream}; +use quote::quote; + +pub fn expand_outer_slash_reason(pallet_decls: &[Pallet], scrate: &TokenStream) -> TokenStream { + let mut conversion_fns = Vec::new(); + let mut slash_reason_variants = Vec::new(); + for decl in pallet_decls { + if let Some(_) = decl.find_part("SlashReason") { + let variant_name = &decl.name; + let path = &decl.path; + let index = decl.index; + + conversion_fns.push(expand_conversion_fn(path, variant_name)); + + slash_reason_variants.push(expand_variant(index, path, variant_name)); + } + } + + quote! { + #[derive( + Copy, Clone, Eq, PartialEq, Ord, PartialOrd, + #scrate::codec::Encode, #scrate::codec::Decode, #scrate::codec::MaxEncodedLen, + #scrate::scale_info::TypeInfo, + #scrate::RuntimeDebug, + )] + pub enum RuntimeSlashReason { + #( #slash_reason_variants )* + } + + #( #conversion_fns )* + } +} + +fn expand_conversion_fn(path: &PalletPath, variant_name: &Ident) -> TokenStream { + quote! { + impl From<#path::SlashReason> for RuntimeSlashReason { + fn from(hr: #path::SlashReason) -> Self { + RuntimeSlashReason::#variant_name(hr) + } + } + } +} + +fn expand_variant(index: u8, path: &PalletPath, variant_name: &Ident) -> TokenStream { + quote! { + #[codec(index = #index)] + #variant_name(#path::SlashReason), + } +} diff --git a/frame/support/procedural/src/construct_runtime/mod.rs b/frame/support/procedural/src/construct_runtime/mod.rs index 9250186dc..a0d414154 100644 --- a/frame/support/procedural/src/construct_runtime/mod.rs +++ b/frame/support/procedural/src/construct_runtime/mod.rs @@ -268,6 +268,10 @@ fn construct_runtime_final_expansion( let inherent = expand::expand_outer_inherent(&name, &block, &unchecked_extrinsic, &pallets, &scrate); let validate_unsigned = expand::expand_outer_validate_unsigned(&name, &pallets, &scrate); + let freeze_reason = expand::expand_outer_freeze_reason(&pallets, &scrate); + let hold_reason = expand::expand_outer_hold_reason(&pallets, &scrate); + let lock_id = expand::expand_outer_lock_id(&pallets, &scrate); + let slash_reason = expand::expand_outer_slash_reason(&pallets, &scrate); let integrity_test = decl_integrity_test(&scrate); let static_assertions = decl_static_assertions(&name, &pallets, &scrate); @@ -310,6 +314,14 @@ fn construct_runtime_final_expansion( #validate_unsigned + #freeze_reason + + #hold_reason + + #lock_id + + #slash_reason + #integrity_test #static_assertions diff --git a/frame/support/procedural/src/construct_runtime/parse.rs b/frame/support/procedural/src/construct_runtime/parse.rs index b0bebcd9e..5e6979f1d 100644 --- a/frame/support/procedural/src/construct_runtime/parse.rs +++ b/frame/support/procedural/src/construct_runtime/parse.rs @@ -38,6 +38,10 @@ mod keyword { syn::custom_keyword!(Origin); syn::custom_keyword!(Inherent); syn::custom_keyword!(ValidateUnsigned); + syn::custom_keyword!(FreezeReason); + syn::custom_keyword!(HoldReason); + syn::custom_keyword!(LockId); + syn::custom_keyword!(SlashReason); syn::custom_keyword!(exclude_parts); syn::custom_keyword!(use_parts); } @@ -370,6 +374,10 @@ pub enum PalletPartKeyword { Origin(keyword::Origin), Inherent(keyword::Inherent), ValidateUnsigned(keyword::ValidateUnsigned), + FreezeReason(keyword::FreezeReason), + HoldReason(keyword::HoldReason), + LockId(keyword::LockId), + SlashReason(keyword::SlashReason), } impl Parse for PalletPartKeyword { @@ -392,6 +400,14 @@ impl Parse for PalletPartKeyword { Ok(Self::Inherent(input.parse()?)) } else if lookahead.peek(keyword::ValidateUnsigned) { Ok(Self::ValidateUnsigned(input.parse()?)) + } else if lookahead.peek(keyword::FreezeReason) { + Ok(Self::FreezeReason(input.parse()?)) + } else if lookahead.peek(keyword::HoldReason) { + Ok(Self::HoldReason(input.parse()?)) + } else if lookahead.peek(keyword::LockId) { + Ok(Self::LockId(input.parse()?)) + } else if lookahead.peek(keyword::SlashReason) { + Ok(Self::SlashReason(input.parse()?)) } else { Err(lookahead.error()) } @@ -410,6 +426,10 @@ impl PalletPartKeyword { Self::Origin(_) => "Origin", Self::Inherent(_) => "Inherent", Self::ValidateUnsigned(_) => "ValidateUnsigned", + Self::FreezeReason(_) => "FreezeReason", + Self::HoldReason(_) => "HoldReason", + Self::LockId(_) => "LockId", + Self::SlashReason(_) => "SlashReason", } } @@ -435,6 +455,10 @@ impl Spanned for PalletPartKeyword { Self::Origin(inner) => inner.span(), Self::Inherent(inner) => inner.span(), Self::ValidateUnsigned(inner) => inner.span(), + Self::FreezeReason(inner) => inner.span(), + Self::HoldReason(inner) => inner.span(), + Self::LockId(inner) => inner.span(), + Self::SlashReason(inner) => inner.span(), } } } diff --git a/frame/support/procedural/src/lib.rs b/frame/support/procedural/src/lib.rs index 45f222029..dea2d4943 100644 --- a/frame/support/procedural/src/lib.rs +++ b/frame/support/procedural/src/lib.rs @@ -1411,3 +1411,30 @@ pub fn validate_unsigned(_: TokenStream, _: TokenStream) -> TokenStream { pub fn origin(_: TokenStream, _: TokenStream) -> TokenStream { pallet_macro_stub() } + +/// The `#[pallet::composable_enum]` attribute allows you to define an enum that gets composed as an +/// aggregate enum by `construct_runtime`. This is similar in principle with `#[pallet::event]` and +/// `#[pallet::error]`. +/// +/// The attribute currently only supports enum definitions, and identifiers that are named +/// `FreezeReason`, `HoldReason`, `LockId` or `SlashReason`. Arbitrary identifiers for the enum are +/// not supported. The aggregate enum generated by `construct_runtime` will have the name of +/// `RuntimeFreezeReason`, `RuntimeHoldReason`, `RuntimeLockId` and `RuntimeSlashReason` +/// respectively. +/// +/// NOTE: The aggregate enum generated by `construct_runtime` generates a conversion function from +/// the pallet enum to the aggregate enum, and automatically derives the following traits: +/// +/// ```ignore +/// Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Encode, Decode, MaxEncodedLen, TypeInfo, +/// RuntimeDebug +/// ``` +/// +/// For ease of usage, when no `#[derive]` attributes are found for the enum under +/// `#[pallet::composable_enum]`, the aforementioned traits are automatically derived for it. The +/// inverse is also true: if there are any `#[derive]` attributes found for the enum, then no traits +/// will automatically be derived for it. +#[proc_macro_attribute] +pub fn composable_enum(_: TokenStream, _: TokenStream) -> TokenStream { + pallet_macro_stub() +} diff --git a/frame/support/procedural/src/pallet/expand/tt_default_parts.rs b/frame/support/procedural/src/pallet/expand/tt_default_parts.rs index 564b526e9..f36c765f7 100644 --- a/frame/support/procedural/src/pallet/expand/tt_default_parts.rs +++ b/frame/support/procedural/src/pallet/expand/tt_default_parts.rs @@ -15,7 +15,10 @@ // See the License for the specific language governing permissions and // limitations under the License. -use crate::{pallet::Def, COUNTER}; +use crate::{ + pallet::{CompositeKeyword, Def}, + COUNTER, +}; use syn::spanned::Spanned; /// Generate the `tt_default_parts` macro. @@ -48,6 +51,30 @@ pub fn expand_tt_default_parts(def: &mut Def) -> proc_macro2::TokenStream { let validate_unsigned_part = def.validate_unsigned.as_ref().map(|_| quote::quote!(ValidateUnsigned,)); + let freeze_reason_part = def + .composites + .iter() + .any(|c| matches!(c.composite_keyword, CompositeKeyword::FreezeReason(_))) + .then_some(quote::quote!(FreezeReason,)); + + let hold_reason_part = def + .composites + .iter() + .any(|c| matches!(c.composite_keyword, CompositeKeyword::HoldReason(_))) + .then_some(quote::quote!(HoldReason,)); + + let lock_id_part = def + .composites + .iter() + .any(|c| matches!(c.composite_keyword, CompositeKeyword::LockId(_))) + .then_some(quote::quote!(LockId,)); + + let slash_reason_part = def + .composites + .iter() + .any(|c| matches!(c.composite_keyword, CompositeKeyword::SlashReason(_))) + .then_some(quote::quote!(SlashReason,)); + quote::quote!( // This macro follows the conventions as laid out by the `tt-call` crate. It does not // accept any arguments and simply returns the pallet parts, separated by commas, then @@ -70,7 +97,8 @@ pub fn expand_tt_default_parts(def: &mut Def) -> proc_macro2::TokenStream { tokens = [{ ::{ Pallet, #call_part #storage_part #event_part #origin_part #config_part - #inherent_part #validate_unsigned_part + #inherent_part #validate_unsigned_part #freeze_reason_part + #hold_reason_part #lock_id_part #slash_reason_part } }] } diff --git a/frame/support/procedural/src/pallet/mod.rs b/frame/support/procedural/src/pallet/mod.rs index 31048841c..361871105 100644 --- a/frame/support/procedural/src/pallet/mod.rs +++ b/frame/support/procedural/src/pallet/mod.rs @@ -28,7 +28,7 @@ mod expand; mod parse; -pub use parse::Def; +pub use parse::{composite::keyword::CompositeKeyword, Def}; use syn::spanned::Spanned; mod keyword { diff --git a/frame/support/procedural/src/pallet/parse/composite.rs b/frame/support/procedural/src/pallet/parse/composite.rs new file mode 100644 index 000000000..2406f10d5 --- /dev/null +++ b/frame/support/procedural/src/pallet/parse/composite.rs @@ -0,0 +1,139 @@ +// This file is part of Substrate. + +// Copyright (C) Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +use quote::ToTokens; +use syn::spanned::Spanned; + +pub mod keyword { + use super::*; + + syn::custom_keyword!(FreezeReason); + syn::custom_keyword!(HoldReason); + syn::custom_keyword!(LockId); + syn::custom_keyword!(SlashReason); + pub enum CompositeKeyword { + FreezeReason(FreezeReason), + HoldReason(HoldReason), + LockId(LockId), + SlashReason(SlashReason), + } + + impl Spanned for CompositeKeyword { + fn span(&self) -> proc_macro2::Span { + use CompositeKeyword::*; + match self { + FreezeReason(inner) => inner.span(), + HoldReason(inner) => inner.span(), + LockId(inner) => inner.span(), + SlashReason(inner) => inner.span(), + } + } + } + + impl syn::parse::Parse for CompositeKeyword { + fn parse(input: syn::parse::ParseStream) -> syn::Result { + let lookahead = input.lookahead1(); + if lookahead.peek(FreezeReason) { + Ok(Self::FreezeReason(input.parse()?)) + } else if lookahead.peek(HoldReason) { + Ok(Self::HoldReason(input.parse()?)) + } else if lookahead.peek(LockId) { + Ok(Self::LockId(input.parse()?)) + } else if lookahead.peek(SlashReason) { + Ok(Self::SlashReason(input.parse()?)) + } else { + Err(lookahead.error()) + } + } + } + + impl std::fmt::Display for CompositeKeyword { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + use CompositeKeyword::*; + write!( + f, + "{}", + match self { + FreezeReason(_) => "FreezeReason", + HoldReason(_) => "HoldReason", + LockId(_) => "LockId", + SlashReason(_) => "SlashReason", + } + ) + } + } +} + +pub struct CompositeDef { + /// The index of the HoldReason item in the pallet module. + pub index: usize, + /// The composite keyword used (contains span). + pub composite_keyword: keyword::CompositeKeyword, + /// The span of the pallet::composite_enum attribute. + pub attr_span: proc_macro2::Span, +} + +impl CompositeDef { + pub fn try_from( + attr_span: proc_macro2::Span, + index: usize, + scrate: &proc_macro2::Ident, + item: &mut syn::Item, + ) -> syn::Result { + let item = if let syn::Item::Enum(item) = item { + item + } else { + return Err(syn::Error::new( + item.span(), + "Invalid pallet::composite_enum, expected enum item", + )) + }; + + if !matches!(item.vis, syn::Visibility::Public(_)) { + let msg = format!("Invalid pallet::composite_enum, `{}` must be public", item.ident); + return Err(syn::Error::new(item.span(), msg)) + } + + let has_derive_attr = item.attrs.iter().any(|attr| { + attr.parse_meta() + .ok() + .map(|meta| match meta { + syn::Meta::List(syn::MetaList { path, .. }) => + path.get_ident().map(|ident| ident == "derive").unwrap_or(false), + _ => false, + }) + .unwrap_or(false) + }); + + if !has_derive_attr { + let derive_attr: syn::Attribute = syn::parse_quote! { + #[derive( + Copy, Clone, Eq, PartialEq, Ord, PartialOrd, + #scrate::codec::Encode, #scrate::codec::Decode, #scrate::codec::MaxEncodedLen, + #scrate::scale_info::TypeInfo, + #scrate::RuntimeDebug, + )] + }; + item.attrs.push(derive_attr); + } + + let composite_keyword = + syn::parse2::(item.ident.to_token_stream())?; + + Ok(CompositeDef { index, composite_keyword, attr_span }) + } +} diff --git a/frame/support/procedural/src/pallet/parse/event.rs b/frame/support/procedural/src/pallet/parse/event.rs index 64c3afd55..0fb8ee4f5 100644 --- a/frame/support/procedural/src/pallet/parse/event.rs +++ b/frame/support/procedural/src/pallet/parse/event.rs @@ -104,7 +104,7 @@ impl EventDef { let item = if let syn::Item::Enum(item) = item { item } else { - return Err(syn::Error::new(item.span(), "Invalid pallet::event, expected item enum")) + return Err(syn::Error::new(item.span(), "Invalid pallet::event, expected enum item")) }; let event_attrs: Vec = diff --git a/frame/support/procedural/src/pallet/parse/mod.rs b/frame/support/procedural/src/pallet/parse/mod.rs index 94e6f7a7c..06b9899d0 100644 --- a/frame/support/procedural/src/pallet/parse/mod.rs +++ b/frame/support/procedural/src/pallet/parse/mod.rs @@ -20,6 +20,7 @@ //! Parse the module into `Def` struct through `Def::try_from` function. pub mod call; +pub mod composite; pub mod config; pub mod error; pub mod event; @@ -35,6 +36,7 @@ pub mod storage; pub mod type_value; pub mod validate_unsigned; +use composite::{keyword::CompositeKeyword, CompositeDef}; use frame_support_procedural_tools::generate_crate_access_2018; use syn::spanned::Spanned; @@ -56,6 +58,7 @@ pub struct Def { pub genesis_build: Option, pub validate_unsigned: Option, pub extra_constants: Option, + pub composites: Vec, pub type_values: Vec, pub frame_system: syn::Ident, pub frame_support: syn::Ident, @@ -91,6 +94,7 @@ impl Def { let mut extra_constants = None; let mut storages = vec![]; let mut type_values = vec![]; + let mut composites: Vec = vec![]; for (index, item) in items.iter_mut().enumerate() { let pallet_attr: Option = helper::take_first_item_pallet_attr(item)?; @@ -135,6 +139,32 @@ impl Def { Some(PalletAttr::ExtraConstants(_)) => extra_constants = Some(extra_constants::ExtraConstantsDef::try_from(index, item)?), + Some(PalletAttr::Composite(span)) => { + let composite = + composite::CompositeDef::try_from(span, index, &frame_support, item)?; + if composites.iter().any(|def| { + match (&def.composite_keyword, &composite.composite_keyword) { + ( + CompositeKeyword::FreezeReason(_), + CompositeKeyword::FreezeReason(_), + ) | + (CompositeKeyword::HoldReason(_), CompositeKeyword::HoldReason(_)) | + (CompositeKeyword::LockId(_), CompositeKeyword::LockId(_)) | + ( + CompositeKeyword::SlashReason(_), + CompositeKeyword::SlashReason(_), + ) => true, + _ => false, + } + }) { + let msg = format!( + "Invalid duplicated `{}` definition", + composite.composite_keyword + ); + return Err(syn::Error::new(composite.composite_keyword.span(), &msg)) + } + composites.push(composite); + }, Some(attr) => { let msg = "Invalid duplicated attribute"; return Err(syn::Error::new(attr.span(), msg)) @@ -171,6 +201,7 @@ impl Def { origin, inherent, storages, + composites, type_values, frame_system, frame_support, @@ -385,6 +416,7 @@ mod keyword { syn::custom_keyword!(generate_store); syn::custom_keyword!(Store); syn::custom_keyword!(extra_constants); + syn::custom_keyword!(composite_enum); } /// Parse attributes for item in pallet module @@ -404,6 +436,7 @@ enum PalletAttr { ValidateUnsigned(proc_macro2::Span), TypeValue(proc_macro2::Span), ExtraConstants(proc_macro2::Span), + Composite(proc_macro2::Span), } impl PalletAttr { @@ -423,6 +456,7 @@ impl PalletAttr { Self::ValidateUnsigned(span) => *span, Self::TypeValue(span) => *span, Self::ExtraConstants(span) => *span, + Self::Composite(span) => *span, } } } @@ -464,6 +498,8 @@ impl syn::parse::Parse for PalletAttr { Ok(PalletAttr::TypeValue(content.parse::()?.span())) } else if lookahead.peek(keyword::extra_constants) { Ok(PalletAttr::ExtraConstants(content.parse::()?.span())) + } else if lookahead.peek(keyword::composite_enum) { + Ok(PalletAttr::Composite(content.parse::()?.span())) } else { Err(lookahead.error()) } diff --git a/frame/support/src/lib.rs b/frame/support/src/lib.rs index b84582845..e0ebeb68a 100644 --- a/frame/support/src/lib.rs +++ b/frame/support/src/lib.rs @@ -1552,6 +1552,7 @@ pub mod pallet_prelude { /// * [`pallet::inherent`](#inherent-palletinherent-optional) /// * [`pallet::validate_unsigned`](#validate-unsigned-palletvalidate_unsigned-optional) /// * [`pallet::origin`](#origin-palletorigin-optional) +/// * [`pallet::composable_enum`](#composable-enum-palletcomposable_enum-optional) /// /// Note that at compile-time, the `#[pallet]` macro will analyze and expand all of these /// attributes, ultimately removing their AST nodes before they can be parsed as real @@ -2277,6 +2278,29 @@ pub mod pallet_prelude { /// /// Also see [`pallet::origin`](`frame_support::pallet_macros::origin`) /// +/// # Composable enum `#[pallet::composable_enum]` (optional) +/// +/// The `#[pallet::composable_enum]` attribute allows you to define an enum on the pallet which +/// will then instruct `construct_runtime` to amalgamate all similarly-named enums from other +/// pallets into an aggregate enum. This is similar in principle with how the aggregate enum is +/// generated for `#[pallet::event]` or `#[pallet::error]`. +/// +/// The item tagged with `#[pallet::composable_enum]` MUST be an enum declaration, and can ONLY +/// be the following identifiers: `FreezeReason`, `HoldReason`, `LockId` or `SlashReason`. +/// Custom identifiers are not supported. +/// +/// NOTE: For ease of usage, when no `#[derive]` attributes are detected, the +/// `#[pallet::composable_enum]` attribute will automatically derive the following traits for +/// the enum: +/// +/// ```ignore +/// Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Encode, Decode, MaxEncodedLen, TypeInfo, +/// RuntimeDebug +/// ``` +/// +/// The inverse is also true: if there are any #[derive] attributes present for the enum, then +/// the attribute will not automatically derive any of the traits described above. +/// /// # General notes on instantiable pallets /// /// An instantiable pallet is one where Config is generic, i.e. `Config`. This allows @@ -2808,10 +2832,11 @@ pub use frame_support_procedural::pallet; /// Contains macro stubs for all of the pallet:: macros pub mod pallet_macros { pub use frame_support_procedural::{ - call_index, compact, config, constant, disable_frame_system_supertrait_check, error, event, - extra_constants, generate_deposit, generate_storage_info, generate_store, genesis_build, - genesis_config, getter, hooks, inherent, origin, storage, storage_prefix, storage_version, - type_value, unbounded, validate_unsigned, weight, whitelist_storage, + call_index, compact, composable_enum, config, constant, + disable_frame_system_supertrait_check, error, event, extra_constants, generate_deposit, + generate_storage_info, generate_store, genesis_build, genesis_config, getter, hooks, + inherent, origin, storage, storage_prefix, storage_version, type_value, unbounded, + validate_unsigned, weight, whitelist_storage, }; } diff --git a/frame/support/test/tests/construct_runtime_ui/invalid_module_details_keyword.stderr b/frame/support/test/tests/construct_runtime_ui/invalid_module_details_keyword.stderr index 29df6e4bd..42f79e96d 100644 --- a/frame/support/test/tests/construct_runtime_ui/invalid_module_details_keyword.stderr +++ b/frame/support/test/tests/construct_runtime_ui/invalid_module_details_keyword.stderr @@ -1,4 +1,4 @@ -error: expected one of: `Pallet`, `Call`, `Storage`, `Event`, `Config`, `Origin`, `Inherent`, `ValidateUnsigned` +error: expected one of: `Pallet`, `Call`, `Storage`, `Event`, `Config`, `Origin`, `Inherent`, `ValidateUnsigned`, `FreezeReason`, `HoldReason`, `LockId`, `SlashReason` --> $DIR/invalid_module_details_keyword.rs:9:20 | 9 | system: System::{enum}, diff --git a/frame/support/test/tests/construct_runtime_ui/invalid_module_entry.stderr b/frame/support/test/tests/construct_runtime_ui/invalid_module_entry.stderr index bd3e672dc..6d535ca43 100644 --- a/frame/support/test/tests/construct_runtime_ui/invalid_module_entry.stderr +++ b/frame/support/test/tests/construct_runtime_ui/invalid_module_entry.stderr @@ -1,4 +1,4 @@ -error: expected one of: `Pallet`, `Call`, `Storage`, `Event`, `Config`, `Origin`, `Inherent`, `ValidateUnsigned` +error: expected one of: `Pallet`, `Call`, `Storage`, `Event`, `Config`, `Origin`, `Inherent`, `ValidateUnsigned`, `FreezeReason`, `HoldReason`, `LockId`, `SlashReason` --> $DIR/invalid_module_entry.rs:10:23 | 10 | Balance: balances::{Error}, diff --git a/frame/support/test/tests/pallet.rs b/frame/support/test/tests/pallet.rs index 1a367c8b3..37f1219a8 100644 --- a/frame/support/test/tests/pallet.rs +++ b/frame/support/test/tests/pallet.rs @@ -476,6 +476,11 @@ pub mod pallet { } } + #[pallet::composite_enum] + pub enum HoldReason { + Staking, + } + #[derive(codec::Encode, sp_runtime::RuntimeDebug)] #[cfg_attr(feature = "std", derive(codec::Decode))] pub enum InherentError { @@ -570,6 +575,16 @@ pub mod pallet2 { { fn build(&self) {} } + + #[pallet::composite_enum] + pub enum HoldReason { + Governance, + } + + #[pallet::composite_enum] + pub enum SlashReason { + Equivocation, + } } /// Test that the supertrait check works when we pass some parameter to the `frame_system::Config`. @@ -974,6 +989,17 @@ fn validate_unsigned_expand() { assert_eq!(validity, ValidTransaction::default()); } +#[test] +fn composite_expand() { + let hold_reason: RuntimeHoldReason = pallet::HoldReason::Staking.into(); + let hold_reason2: RuntimeHoldReason = pallet2::HoldReason::Governance.into(); + let slash_reason: RuntimeSlashReason = pallet2::SlashReason::Equivocation.into(); + + assert_eq!(hold_reason, RuntimeHoldReason::Example(pallet::HoldReason::Staking)); + assert_eq!(hold_reason2, RuntimeHoldReason::Example2(pallet2::HoldReason::Governance)); + assert_eq!(slash_reason, RuntimeSlashReason::Example2(pallet2::SlashReason::Equivocation)); +} + #[test] fn pallet_expand_deposit_event() { TestExternalities::default().execute_with(|| { diff --git a/frame/support/test/tests/pallet_ui/composite_enum_unsupported_identifier.rs b/frame/support/test/tests/pallet_ui/composite_enum_unsupported_identifier.rs new file mode 100644 index 000000000..74692ee94 --- /dev/null +++ b/frame/support/test/tests/pallet_ui/composite_enum_unsupported_identifier.rs @@ -0,0 +1,13 @@ +#[frame_support::pallet] +mod pallet { + #[pallet::config] + pub trait Config: frame_system::Config {} + + #[pallet::pallet] + pub struct Pallet(core::marker::PhantomData); + + #[pallet::composite_enum] + pub enum HoldReasons {} +} + +fn main() {} \ No newline at end of file diff --git a/frame/support/test/tests/pallet_ui/composite_enum_unsupported_identifier.stderr b/frame/support/test/tests/pallet_ui/composite_enum_unsupported_identifier.stderr new file mode 100644 index 000000000..902e89237 --- /dev/null +++ b/frame/support/test/tests/pallet_ui/composite_enum_unsupported_identifier.stderr @@ -0,0 +1,5 @@ +error: expected one of: `FreezeReason`, `HoldReason`, `LockId`, `SlashReason` + --> tests/pallet_ui/composite_enum_unsupported_identifier.rs:10:11 + | +10 | pub enum HoldReasons {} + | ^^^^^^^^^^^ diff --git a/frame/support/test/tests/pallet_ui/event_wrong_item.stderr b/frame/support/test/tests/pallet_ui/event_wrong_item.stderr index 21eb0ed35..0ef150dfd 100644 --- a/frame/support/test/tests/pallet_ui/event_wrong_item.stderr +++ b/frame/support/test/tests/pallet_ui/event_wrong_item.stderr @@ -1,4 +1,4 @@ -error: Invalid pallet::event, expected item enum +error: Invalid pallet::event, expected enum item --> $DIR/event_wrong_item.rs:19:2 | 19 | pub struct Foo; diff --git a/frame/support/test/tests/pallet_ui/hold_reason_non_enum.rs b/frame/support/test/tests/pallet_ui/hold_reason_non_enum.rs new file mode 100644 index 000000000..8008c465e --- /dev/null +++ b/frame/support/test/tests/pallet_ui/hold_reason_non_enum.rs @@ -0,0 +1,14 @@ +#[frame_support::pallet] +mod pallet { + #[pallet::config] + pub trait Config: frame_system::Config {} + + #[pallet::pallet] + pub struct Pallet(core::marker::PhantomData); + + #[pallet::composite_enum] + pub struct HoldReason; +} + +fn main() { +} \ No newline at end of file diff --git a/frame/support/test/tests/pallet_ui/hold_reason_non_enum.stderr b/frame/support/test/tests/pallet_ui/hold_reason_non_enum.stderr new file mode 100644 index 000000000..7d86b8d4f --- /dev/null +++ b/frame/support/test/tests/pallet_ui/hold_reason_non_enum.stderr @@ -0,0 +1,5 @@ +error: Invalid pallet::composite_enum, expected enum item + --> tests/pallet_ui/hold_reason_non_enum.rs:10:2 + | +10 | pub struct HoldReason; + | ^^^ diff --git a/frame/support/test/tests/pallet_ui/hold_reason_not_pub.rs b/frame/support/test/tests/pallet_ui/hold_reason_not_pub.rs new file mode 100644 index 000000000..626dad741 --- /dev/null +++ b/frame/support/test/tests/pallet_ui/hold_reason_not_pub.rs @@ -0,0 +1,14 @@ +#[frame_support::pallet] +mod pallet { + #[pallet::config] + pub trait Config: frame_system::Config {} + + #[pallet::pallet] + pub struct Pallet(core::marker::PhantomData); + + #[pallet::composite_enum] + enum HoldReason {} +} + +fn main() { +} \ No newline at end of file diff --git a/frame/support/test/tests/pallet_ui/hold_reason_not_pub.stderr b/frame/support/test/tests/pallet_ui/hold_reason_not_pub.stderr new file mode 100644 index 000000000..e8b0c14e9 --- /dev/null +++ b/frame/support/test/tests/pallet_ui/hold_reason_not_pub.stderr @@ -0,0 +1,5 @@ +error: Invalid pallet::composite_enum, `HoldReason` must be public + --> tests/pallet_ui/hold_reason_not_pub.rs:10:5 + | +10 | enum HoldReason {} + | ^^^^ diff --git a/frame/support/test/tests/pallet_ui/lock_id_duplicate.rs b/frame/support/test/tests/pallet_ui/lock_id_duplicate.rs new file mode 100644 index 000000000..70418efc4 --- /dev/null +++ b/frame/support/test/tests/pallet_ui/lock_id_duplicate.rs @@ -0,0 +1,17 @@ +#[frame_support::pallet] +mod pallet { + #[pallet::config] + pub trait Config: frame_system::Config {} + + #[pallet::pallet] + pub struct Pallet(core::marker::PhantomData); + + #[pallet::composite_enum] + pub enum LockId {} + + #[pallet::composite_enum] + pub enum LockId {} +} + +fn main() { +} \ No newline at end of file diff --git a/frame/support/test/tests/pallet_ui/lock_id_duplicate.stderr b/frame/support/test/tests/pallet_ui/lock_id_duplicate.stderr new file mode 100644 index 000000000..1b7097d0a --- /dev/null +++ b/frame/support/test/tests/pallet_ui/lock_id_duplicate.stderr @@ -0,0 +1,5 @@ +error: Invalid duplicated `LockId` definition + --> tests/pallet_ui/lock_id_duplicate.rs:13:14 + | +13 | pub enum LockId {} + | ^^^^^^ From 8548a97f9272c7ca43ac7c091d81e6b9cc8b69e2 Mon Sep 17 00:00:00 2001 From: s0me0ne-unkn0wn <48632512+s0me0ne-unkn0wn@users.noreply.github.com> Date: Tue, 4 Apr 2023 17:10:27 +0200 Subject: [PATCH 337/558] Expose WASM extensions in executor semantics (#13811) * Expose WASM extensions in executor semantics * Fix benches * Remove redundant extensions --- client/executor/benches/bench.rs | 4 ++++ client/executor/src/wasm_runtime.rs | 4 ++++ client/executor/wasmtime/src/runtime.rs | 20 ++++++++++++++++---- client/executor/wasmtime/src/tests.rs | 8 ++++++++ 4 files changed, 32 insertions(+), 4 deletions(-) diff --git a/client/executor/benches/bench.rs b/client/executor/benches/bench.rs index 10425ea46..f129ebf64 100644 --- a/client/executor/benches/bench.rs +++ b/client/executor/benches/bench.rs @@ -72,6 +72,10 @@ fn initialize( deterministic_stack_limit: None, canonicalize_nans: false, parallel_compilation: true, + wasm_multi_value: false, + wasm_bulk_memory: false, + wasm_reference_types: false, + wasm_simd: false, }, }; diff --git a/client/executor/src/wasm_runtime.rs b/client/executor/src/wasm_runtime.rs index 254380dbb..fb3dd0f38 100644 --- a/client/executor/src/wasm_runtime.rs +++ b/client/executor/src/wasm_runtime.rs @@ -326,6 +326,10 @@ where deterministic_stack_limit: None, canonicalize_nans: false, parallel_compilation: true, + wasm_multi_value: false, + wasm_bulk_memory: false, + wasm_reference_types: false, + wasm_simd: false, }, }, ) diff --git a/client/executor/wasmtime/src/runtime.rs b/client/executor/wasmtime/src/runtime.rs index de7a2ea0e..e01a51f6c 100644 --- a/client/executor/wasmtime/src/runtime.rs +++ b/client/executor/wasmtime/src/runtime.rs @@ -325,10 +325,10 @@ fn common_config(semantics: &Semantics) -> std::result::Result Date: Tue, 4 Apr 2023 20:57:21 +0200 Subject: [PATCH 338/558] Deprecate V1 Weights (#13699) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Remove deprecated pallet calls Signed-off-by: Oliver Tale-Yazdi * Deprecate old weight Signed-off-by: Oliver Tale-Yazdi * Update Runtime API Signed-off-by: Oliver Tale-Yazdi * Fix tests Signed-off-by: Oliver Tale-Yazdi * Delete shitty code Signed-off-by: Oliver Tale-Yazdi * Fix doctest Signed-off-by: Oliver Tale-Yazdi * Update frame/alliance/src/lib.rs Co-authored-by: Koute * Add doc Signed-off-by: Oliver Tale-Yazdi * contracts: Use u64 as old weight type Signed-off-by: Oliver Tale-Yazdi * Update frame/contracts/src/lib.rs Co-authored-by: Alexander Theißen --------- Signed-off-by: Oliver Tale-Yazdi Co-authored-by: Koute Co-authored-by: Alexander Theißen Co-authored-by: parity-processbot <> --- frame/alliance/src/lib.rs | 33 +------- frame/collective/src/lib.rs | 56 +------------- frame/contracts/src/lib.rs | 10 ++- frame/contracts/src/wasm/mod.rs | 19 +++-- frame/support/src/dispatch.rs | 75 ++++++++++--------- frame/system/src/tests.rs | 31 ++++++-- .../rpc/runtime-api/src/lib.rs | 4 +- frame/transaction-payment/rpc/src/lib.rs | 41 +++------- primitives/weights/src/lib.rs | 4 + primitives/weights/src/weight_v2.rs | 7 +- 10 files changed, 108 insertions(+), 172 deletions(-) diff --git a/frame/alliance/src/lib.rs b/frame/alliance/src/lib.rs index 6cc162f7d..d50e20ba0 100644 --- a/frame/alliance/src/lib.rs +++ b/frame/alliance/src/lib.rs @@ -114,7 +114,7 @@ use frame_support::{ ChangeMembers, Currency, Get, InitializeMembers, IsSubType, OnUnbalanced, ReservableCurrency, }, - weights::{OldWeight, Weight}, + weights::Weight, }; use pallet_identity::IdentityField; @@ -539,36 +539,7 @@ pub mod pallet { Ok(()) } - /// Close a vote that is either approved, disapproved, or whose voting period has ended. - /// - /// Must be called by a Fellow. - #[pallet::call_index(2)] - #[pallet::weight({ - let b = *length_bound; - let m = T::MaxFellows::get(); - let p1 = *proposal_weight_bound; - let p2 = T::MaxProposals::get(); - T::WeightInfo::close_early_approved(b, m, p2) - .max(T::WeightInfo::close_early_disapproved(m, p2)) - .max(T::WeightInfo::close_approved(b, m, p2)) - .max(T::WeightInfo::close_disapproved(m, p2)) - .saturating_add(p1.into()) - })] - #[allow(deprecated)] - #[deprecated(note = "1D weight is used in this extrinsic, please migrate to use `close`")] - pub fn close_old_weight( - origin: OriginFor, - proposal_hash: T::Hash, - #[pallet::compact] index: ProposalIndex, - #[pallet::compact] proposal_weight_bound: OldWeight, - #[pallet::compact] length_bound: u32, - ) -> DispatchResultWithPostInfo { - let proposal_weight_bound: Weight = proposal_weight_bound.into(); - let who = ensure_signed(origin)?; - ensure!(Self::has_voting_rights(&who), Error::::NoVotingRights); - - Self::do_close(proposal_hash, index, proposal_weight_bound, length_bound) - } + // Index 2 was `close_old_weight`; it was removed due to weights v1 deprecation. /// Initialize the Alliance, onboard fellows and allies. /// diff --git a/frame/collective/src/lib.rs b/frame/collective/src/lib.rs index c58965d2c..bde08f08c 100644 --- a/frame/collective/src/lib.rs +++ b/frame/collective/src/lib.rs @@ -57,7 +57,7 @@ use frame_support::{ traits::{ Backing, ChangeMembers, EnsureOrigin, Get, GetBacking, InitializeMembers, StorageVersion, }, - weights::{OldWeight, Weight}, + weights::Weight, }; #[cfg(test)] @@ -557,59 +557,7 @@ pub mod pallet { } } - /// Close a vote that is either approved, disapproved or whose voting period has ended. - /// - /// May be called by any signed account in order to finish voting and close the proposal. - /// - /// If called before the end of the voting period it will only close the vote if it is - /// has enough votes to be approved or disapproved. - /// - /// If called after the end of the voting period abstentions are counted as rejections - /// unless there is a prime member set and the prime member cast an approval. - /// - /// If the close operation completes successfully with disapproval, the transaction fee will - /// be waived. Otherwise execution of the approved operation will be charged to the caller. - /// - /// + `proposal_weight_bound`: The maximum amount of weight consumed by executing the closed - /// proposal. - /// + `length_bound`: The upper bound for the length of the proposal in storage. Checked via - /// `storage::read` so it is `size_of::() == 4` larger than the pure length. - /// - /// ## Complexity - /// - `O(B + M + P1 + P2)` where: - /// - `B` is `proposal` size in bytes (length-fee-bounded) - /// - `M` is members-count (code- and governance-bounded) - /// - `P1` is the complexity of `proposal` preimage. - /// - `P2` is proposal-count (code-bounded) - #[pallet::call_index(4)] - #[pallet::weight(( - { - let b = *length_bound; - let m = T::MaxMembers::get(); - let p1 = *proposal_weight_bound; - let p2 = T::MaxProposals::get(); - T::WeightInfo::close_early_approved(b, m, p2) - .max(T::WeightInfo::close_early_disapproved(m, p2)) - .max(T::WeightInfo::close_approved(b, m, p2)) - .max(T::WeightInfo::close_disapproved(m, p2)) - .saturating_add(p1.into()) - }, - DispatchClass::Operational - ))] - #[allow(deprecated)] - #[deprecated(note = "1D weight is used in this extrinsic, please migrate to `close`")] - pub fn close_old_weight( - origin: OriginFor, - proposal_hash: T::Hash, - #[pallet::compact] index: ProposalIndex, - #[pallet::compact] proposal_weight_bound: OldWeight, - #[pallet::compact] length_bound: u32, - ) -> DispatchResultWithPostInfo { - let proposal_weight_bound: Weight = proposal_weight_bound.into(); - let _ = ensure_signed(origin)?; - - Self::do_close(proposal_hash, index, proposal_weight_bound, length_bound) - } + // Index 4 was `close_old_weight`; it was removed due to weights v1 deprecation. /// Disapprove a proposal, close, and remove it from the system, regardless of its current /// state. diff --git a/frame/contracts/src/lib.rs b/frame/contracts/src/lib.rs index dc93a7f06..6b8b85458 100644 --- a/frame/contracts/src/lib.rs +++ b/frame/contracts/src/lib.rs @@ -115,7 +115,7 @@ use frame_support::{ tokens::fungible::Inspect, ConstU32, Contains, Currency, Get, Randomness, ReservableCurrency, Time, }, - weights::{OldWeight, Weight}, + weights::Weight, BoundedVec, WeakBoundedVec, }; use frame_system::Pallet as System; @@ -150,6 +150,12 @@ type RelaxedCodeVec = WeakBoundedVec::MaxCodeLen>; type AccountIdLookupOf = <::Lookup as StaticLookup>::Source; type DebugBufferVec = BoundedVec::MaxDebugBufferLen>; +/// The old weight type. +/// +/// This is a copy of the [`frame_support::weights::OldWeight`] type since the contracts pallet +/// needs to support it indefinitely. +type OldWeight = u64; + /// Used as a sentinel value when reading and writing contract memory. /// /// It is usually used to signal `None` to a contract when only a primitive is allowed @@ -1281,7 +1287,7 @@ impl Pallet { /// Used by backwards compatible extrinsics. We cannot just set the proof_size weight limit to /// zero or an old `Call` will just fail with OutOfGas. fn compat_weight_limit(gas_limit: OldWeight) -> Weight { - Weight::from_parts(gas_limit.0, u64::from(T::MaxCodeLen::get()) * 2) + Weight::from_parts(gas_limit, u64::from(T::MaxCodeLen::get()) * 2) } } diff --git a/frame/contracts/src/wasm/mod.rs b/frame/contracts/src/wasm/mod.rs index 5e4ffcfe0..e4e8bea7e 100644 --- a/frame/contracts/src/wasm/mod.rs +++ b/frame/contracts/src/wasm/mod.rs @@ -370,13 +370,11 @@ mod tests { gas::GasMeter, storage::WriteOutcome, tests::{RuntimeCall, Test, ALICE, BOB}, - BalanceOf, CodeHash, Error, Pallet as Contracts, + BalanceOf, CodeHash, Error, OldWeight, Pallet as Contracts, }; use assert_matches::assert_matches; use frame_support::{ - assert_err, assert_ok, - dispatch::DispatchResultWithPostInfo, - weights::{OldWeight, Weight}, + assert_err, assert_ok, dispatch::DispatchResultWithPostInfo, weights::Weight, }; use pallet_contracts_primitives::{ExecReturnValue, ReturnFlags}; use pretty_assertions::assert_eq; @@ -1632,13 +1630,24 @@ mod tests { let output = execute(CODE_GAS_LEFT, vec![], &mut ext).unwrap(); - let OldWeight(gas_left) = OldWeight::decode(&mut &*output.data).unwrap(); + let gas_left = OldWeight::decode(&mut &*output.data).unwrap(); let actual_left = ext.gas_meter.gas_left(); // TODO: account for proof size weight assert!(gas_left < gas_limit.ref_time(), "gas_left must be less than initial"); assert!(gas_left > actual_left.ref_time(), "gas_left must be greater than final"); } + /// Test that [`frame_support::weights::OldWeight`] en/decodes the same as our + /// [`crate::OldWeight`]. + #[test] + fn old_weight_decode() { + #![allow(deprecated)] + let sp = frame_support::weights::OldWeight(42).encode(); + let our = crate::OldWeight::decode(&mut &*sp).unwrap(); + + assert_eq!(our, 42); + } + const CODE_VALUE_TRANSFERRED: &str = r#" (module (import "seal0" "seal_value_transferred" (func $seal_value_transferred (param i32 i32))) diff --git a/frame/support/src/dispatch.rs b/frame/support/src/dispatch.rs index 1b87acb4d..9e949ef6d 100644 --- a/frame/support/src/dispatch.rs +++ b/frame/support/src/dispatch.rs @@ -550,27 +550,6 @@ impl ClassifyDispatch for (Weight, DispatchClass, Pays) { // TODO: Eventually remove these -impl From> for PostDispatchInfo { - fn from(maybe_actual_computation: Option) -> Self { - let actual_weight = match maybe_actual_computation { - Some(actual_computation) => Some(Weight::from_parts(actual_computation, 0)), - None => None, - }; - Self { actual_weight, pays_fee: Default::default() } - } -} - -impl From<(Option, Pays)> for PostDispatchInfo { - fn from(post_weight_info: (Option, Pays)) -> Self { - let (maybe_actual_time, pays_fee) = post_weight_info; - let actual_weight = match maybe_actual_time { - Some(actual_time) => Some(Weight::from_parts(actual_time, 0)), - None => None, - }; - Self { actual_weight, pays_fee } - } -} - impl ClassifyDispatch for u64 { fn classify_dispatch(&self, _: T) -> DispatchClass { DispatchClass::Normal @@ -730,7 +709,7 @@ impl PaysFee for (u64, Pays) { /// ``` /// # #[macro_use] /// # extern crate frame_support; -/// # use frame_support::{weights::Weight, dispatch::{DispatchResultWithPostInfo, WithPostDispatchInfo}}; +/// # use frame_support::{weights::Weight, dispatch::{DispatchResultWithPostInfo, WithPostDispatchInfo, PostDispatchInfo}}; /// # use frame_system::{Config, ensure_signed}; /// decl_module! { /// pub struct Module for enum Call where origin: T::RuntimeOrigin { @@ -744,7 +723,7 @@ impl PaysFee for (u64, Pays) { /// return Ok(None::.into()); /// } /// // expensive calculation not executed: use only a portion of the weight -/// Ok(Some(100_000).into()) +/// Ok(PostDispatchInfo { actual_weight: Some(Weight::from_parts(100_000, 0)), ..Default::default() }) /// } /// } /// } @@ -3214,7 +3193,7 @@ mod tests { OnInitialize, OnRuntimeUpgrade, PalletInfo, }, }; - use sp_weights::RuntimeDbWeight; + use sp_weights::{RuntimeDbWeight, Weight}; pub trait Config: system::Config + Sized where @@ -3535,13 +3514,24 @@ mod tests { fn test_new_call_variant() { Call::::new_call_variant_aux_0(); } + + pub fn from_actual_ref_time(ref_time: Option) -> PostDispatchInfo { + PostDispatchInfo { + actual_weight: ref_time.map(|t| Weight::from_all(t)), + pays_fee: Default::default(), + } + } + + pub fn from_post_weight_info(ref_time: Option, pays_fee: Pays) -> PostDispatchInfo { + PostDispatchInfo { actual_weight: ref_time.map(|t| Weight::from_all(t)), pays_fee } + } } #[cfg(test)] // Do not complain about unused `dispatch` and `dispatch_aux`. #[allow(dead_code)] mod weight_tests { - use super::*; + use super::{tests::*, *}; use sp_core::{parameter_types, Get}; use sp_weights::RuntimeDbWeight; @@ -3655,9 +3645,12 @@ mod weight_tests { #[test] fn extract_actual_weight_works() { let pre = DispatchInfo { weight: Weight::from_parts(1000, 0), ..Default::default() }; - assert_eq!(extract_actual_weight(&Ok(Some(7).into()), &pre), Weight::from_parts(7, 0)); assert_eq!( - extract_actual_weight(&Ok(Some(1000).into()), &pre), + extract_actual_weight(&Ok(from_actual_ref_time(Some(7))), &pre), + Weight::from_parts(7, 0) + ); + assert_eq!( + extract_actual_weight(&Ok(from_actual_ref_time(Some(1000))), &pre), Weight::from_parts(1000, 0) ); assert_eq!( @@ -3673,7 +3666,7 @@ mod weight_tests { fn extract_actual_weight_caps_at_pre_weight() { let pre = DispatchInfo { weight: Weight::from_parts(1000, 0), ..Default::default() }; assert_eq!( - extract_actual_weight(&Ok(Some(1250).into()), &pre), + extract_actual_weight(&Ok(from_actual_ref_time(Some(1250))), &pre), Weight::from_parts(1000, 0) ); assert_eq!( @@ -3688,10 +3681,19 @@ mod weight_tests { #[test] fn extract_actual_pays_fee_works() { let pre = DispatchInfo { weight: Weight::from_parts(1000, 0), ..Default::default() }; - assert_eq!(extract_actual_pays_fee(&Ok(Some(7).into()), &pre), Pays::Yes); - assert_eq!(extract_actual_pays_fee(&Ok(Some(1000).into()), &pre), Pays::Yes); - assert_eq!(extract_actual_pays_fee(&Ok((Some(1000), Pays::Yes).into()), &pre), Pays::Yes); - assert_eq!(extract_actual_pays_fee(&Ok((Some(1000), Pays::No).into()), &pre), Pays::No); + assert_eq!(extract_actual_pays_fee(&Ok(from_actual_ref_time(Some(7))), &pre), Pays::Yes); + assert_eq!( + extract_actual_pays_fee(&Ok(from_actual_ref_time(Some(1000)).into()), &pre), + Pays::Yes + ); + assert_eq!( + extract_actual_pays_fee(&Ok(from_post_weight_info(Some(1000), Pays::Yes)), &pre), + Pays::Yes + ); + assert_eq!( + extract_actual_pays_fee(&Ok(from_post_weight_info(Some(1000), Pays::No)), &pre), + Pays::No + ); assert_eq!( extract_actual_pays_fee( &Err(DispatchError::BadOrigin.with_weight(Weight::from_parts(9, 0))), @@ -3715,9 +3717,12 @@ mod weight_tests { pays_fee: Pays::No, ..Default::default() }; - assert_eq!(extract_actual_pays_fee(&Ok(Some(7).into()), &pre), Pays::No); - assert_eq!(extract_actual_pays_fee(&Ok(Some(1000).into()), &pre), Pays::No); - assert_eq!(extract_actual_pays_fee(&Ok((Some(1000), Pays::Yes).into()), &pre), Pays::No); + assert_eq!(extract_actual_pays_fee(&Ok(from_actual_ref_time(Some(7))), &pre), Pays::No); + assert_eq!(extract_actual_pays_fee(&Ok(from_actual_ref_time(Some(1000))), &pre), Pays::No); + assert_eq!( + extract_actual_pays_fee(&Ok(from_post_weight_info(Some(1000), Pays::Yes)), &pre), + Pays::No + ); } } diff --git a/frame/system/src/tests.rs b/frame/system/src/tests.rs index 9b60c6915..fc388800e 100644 --- a/frame/system/src/tests.rs +++ b/frame/system/src/tests.rs @@ -236,17 +236,23 @@ fn deposit_event_uses_actual_weight_and_pays_fee() { .get(DispatchClass::Normal) .base_extrinsic; let pre_info = DispatchInfo { weight: Weight::from_parts(1000, 0), ..Default::default() }; - System::note_applied_extrinsic(&Ok(Some(300).into()), pre_info); - System::note_applied_extrinsic(&Ok(Some(1000).into()), pre_info); + System::note_applied_extrinsic(&Ok(from_actual_ref_time(Some(300))), pre_info); + System::note_applied_extrinsic(&Ok(from_actual_ref_time(Some(1000))), pre_info); System::note_applied_extrinsic( // values over the pre info should be capped at pre dispatch value - &Ok(Some(1200).into()), + &Ok(from_actual_ref_time(Some(1200))), + pre_info, + ); + System::note_applied_extrinsic( + &Ok(from_post_weight_info(Some(2_500_000), Pays::Yes)), pre_info, ); - System::note_applied_extrinsic(&Ok((Some(2_500_000), Pays::Yes).into()), pre_info); System::note_applied_extrinsic(&Ok(Pays::No.into()), pre_info); - System::note_applied_extrinsic(&Ok((Some(2_500_000), Pays::No).into()), pre_info); - System::note_applied_extrinsic(&Ok((Some(500), Pays::No).into()), pre_info); + System::note_applied_extrinsic( + &Ok(from_post_weight_info(Some(2_500_000), Pays::No)), + pre_info, + ); + System::note_applied_extrinsic(&Ok(from_post_weight_info(Some(500), Pays::No)), pre_info); System::note_applied_extrinsic( &Err(DispatchError::BadOrigin.with_weight(Weight::from_parts(999, 0))), pre_info, @@ -289,7 +295,7 @@ fn deposit_event_uses_actual_weight_and_pays_fee() { class: DispatchClass::Operational, ..Default::default() }; - System::note_applied_extrinsic(&Ok(Some(300).into()), pre_info); + System::note_applied_extrinsic(&Ok(from_actual_ref_time(Some(300))), pre_info); let got = System::events(); let want = vec![ @@ -691,3 +697,14 @@ fn ensure_signed_stuff_works() { assert_ok!(EnsureSignedBy::::try_origin(successful_origin)); } } + +pub fn from_actual_ref_time(ref_time: Option) -> PostDispatchInfo { + PostDispatchInfo { + actual_weight: ref_time.map(|t| Weight::from_all(t)), + pays_fee: Default::default(), + } +} + +pub fn from_post_weight_info(ref_time: Option, pays_fee: Pays) -> PostDispatchInfo { + PostDispatchInfo { actual_weight: ref_time.map(|t| Weight::from_all(t)), pays_fee } +} diff --git a/frame/transaction-payment/rpc/runtime-api/src/lib.rs b/frame/transaction-payment/rpc/runtime-api/src/lib.rs index ae4a25bf1..0d9c33382 100644 --- a/frame/transaction-payment/rpc/runtime-api/src/lib.rs +++ b/frame/transaction-payment/rpc/runtime-api/src/lib.rs @@ -25,12 +25,10 @@ use sp_runtime::traits::MaybeDisplay; pub use pallet_transaction_payment::{FeeDetails, InclusionFee, RuntimeDispatchInfo}; sp_api::decl_runtime_apis! { - #[api_version(3)] + #[api_version(4)] pub trait TransactionPaymentApi where Balance: Codec + MaybeDisplay, { - #[changed_in(2)] - fn query_info(uxt: Block::Extrinsic, len: u32) -> RuntimeDispatchInfo; fn query_info(uxt: Block::Extrinsic, len: u32) -> RuntimeDispatchInfo; fn query_fee_details(uxt: Block::Extrinsic, len: u32) -> FeeDetails; fn query_weight_to_fee(weight: sp_weights::Weight) -> Balance; diff --git a/frame/transaction-payment/rpc/src/lib.rs b/frame/transaction-payment/rpc/src/lib.rs index 1207a8cbf..7f8ed4b80 100644 --- a/frame/transaction-payment/rpc/src/lib.rs +++ b/frame/transaction-payment/rpc/src/lib.rs @@ -26,7 +26,7 @@ use jsonrpsee::{ types::error::{CallError, ErrorCode, ErrorObject}, }; use pallet_transaction_payment_rpc_runtime_api::{FeeDetails, InclusionFee, RuntimeDispatchInfo}; -use sp_api::{ApiExt, ProvideRuntimeApi}; +use sp_api::ProvideRuntimeApi; use sp_blockchain::HeaderBackend; use sp_core::Bytes; use sp_rpc::number::NumberOrHex; @@ -81,7 +81,7 @@ impl From for i32 { impl TransactionPaymentApiServer< ::Hash, - RuntimeDispatchInfo, + RuntimeDispatchInfo, > for TransactionPayment where Block: BlockT, @@ -93,7 +93,7 @@ where &self, encoded_xt: Bytes, at: Option, - ) -> RpcResult> { + ) -> RpcResult> { let api = self.client.runtime_api(); let at_hash = at.unwrap_or_else(|| self.client.info().best_hash); @@ -115,32 +115,15 @@ where )) } - let api_version = api - .api_version::>(at_hash) - .map_err(|e| map_err(e, "Failed to get transaction payment runtime api version"))? - .ok_or_else(|| { - CallError::Custom(ErrorObject::owned( - Error::RuntimeError.into(), - "Transaction payment runtime api wasn't found in the runtime", - None::, - )) - })?; - - if api_version < 2 { - #[allow(deprecated)] - api.query_info_before_version_2(at_hash, uxt, encoded_len) - .map_err(|e| map_err(e, "Unable to query dispatch info.").into()) - } else { - let res = api - .query_info(at_hash, uxt, encoded_len) - .map_err(|e| map_err(e, "Unable to query dispatch info."))?; - - Ok(RuntimeDispatchInfo { - weight: sp_weights::OldWeight(res.weight.ref_time()), - class: res.class, - partial_fee: res.partial_fee, - }) - } + let res = api + .query_info(at_hash, uxt, encoded_len) + .map_err(|e| map_err(e, "Unable to query dispatch info."))?; + + Ok(RuntimeDispatchInfo { + weight: res.weight, + class: res.class, + partial_fee: res.partial_fee, + }) } fn query_fee_details( diff --git a/primitives/weights/src/lib.rs b/primitives/weights/src/lib.rs index faa641a0f..55d9104b0 100644 --- a/primitives/weights/src/lib.rs +++ b/primitives/weights/src/lib.rs @@ -18,6 +18,9 @@ //! # Primitives for transaction weighting. #![cfg_attr(not(feature = "std"), no_std)] +// TODO remove once `OldWeight` is gone. I dont know why this is needed, maybe by one of the macros +// of `OldWeight`. +#![allow(deprecated)] extern crate self as sp_weights; @@ -68,6 +71,7 @@ pub mod constants { )] #[cfg_attr(feature = "std", derive(Serialize, Deserialize))] #[cfg_attr(feature = "std", serde(transparent))] +#[deprecated(note = "Will be removed soon; use `Weight` instead.")] pub struct OldWeight(pub u64); /// The weight of database operations that the runtime can invoke. diff --git a/primitives/weights/src/weight_v2.rs b/primitives/weights/src/weight_v2.rs index ca1371459..cb1cedf18 100644 --- a/primitives/weights/src/weight_v2.rs +++ b/primitives/weights/src/weight_v2.rs @@ -35,12 +35,6 @@ pub struct Weight { proof_size: u64, } -impl From for Weight { - fn from(old: OldWeight) -> Self { - Weight::from_parts(old.0, 0) - } -} - impl Weight { /// Set the reference time part of the weight. pub const fn set_ref_time(mut self, c: u64) -> Self { @@ -74,6 +68,7 @@ impl Weight { &mut self.proof_size } + /// The maximal weight in all dimensions. pub const MAX: Self = Self { ref_time: u64::MAX, proof_size: u64::MAX }; /// Get the conservative min of `self` and `other` weight. From 3b0f4b205e72076357d2b555dbb745ceb8807672 Mon Sep 17 00:00:00 2001 From: Sasha Gryaznov Date: Wed, 5 Apr 2023 07:34:42 +0300 Subject: [PATCH 339/558] update links to ink! (#13819) --- frame/contracts/README.md | 4 ++-- frame/contracts/benchmarks/README.md | 2 +- frame/contracts/src/chain_extension.rs | 4 ++-- frame/contracts/src/lib.rs | 2 +- 4 files changed, 6 insertions(+), 6 deletions(-) diff --git a/frame/contracts/README.md b/frame/contracts/README.md index 68fa49deb..cf89c9514 100644 --- a/frame/contracts/README.md +++ b/frame/contracts/README.md @@ -106,14 +106,14 @@ Look for the `define_env!` macro invocation. This module executes WebAssembly smart contracts. These can potentially be written in any language that compiles to web assembly. However, using a language that specifically targets this module -will make things a lot easier. One such language is [`ink`](https://github.com/paritytech/ink) +will make things a lot easier. One such language is [`ink!`](https://use.ink) which is an [`eDSL`](https://wiki.haskell.org/Embedded_domain_specific_language) that enables writing WebAssembly based smart contracts in the Rust programming language. ## Debugging Contracts can emit messages to the client when called as RPC through the `seal_debug_message` -API. This is exposed in ink! via +API. This is exposed in [ink!](https://use.ink) via [`ink_env::debug_message()`](https://paritytech.github.io/ink/ink_env/fn.debug_message.html). Those messages are gathered into an internal buffer and send to the RPC client. diff --git a/frame/contracts/benchmarks/README.md b/frame/contracts/benchmarks/README.md index a4b15bd84..a621dd65d 100644 --- a/frame/contracts/benchmarks/README.md +++ b/frame/contracts/benchmarks/README.md @@ -1,6 +1,6 @@ # Benchmarks -This directory contains real world (ink!, solang) contracts which are used in macro benchmarks. +This directory contains real world ([ink!](https://use.ink), [solang](https://github.com/hyperledger/solang)) contracts which are used in macro benchmarks. Those benchmarks are not used to determine weights but rather to compare different contract languages and execution engines with larger wasm modules. diff --git a/frame/contracts/src/chain_extension.rs b/frame/contracts/src/chain_extension.rs index 2c3780708..6d1f3df90 100644 --- a/frame/contracts/src/chain_extension.rs +++ b/frame/contracts/src/chain_extension.rs @@ -66,8 +66,8 @@ //! //! # Example //! -//! The ink! repository maintains an -//! [end-to-end example](https://github.com/paritytech/ink/tree/master/examples/rand-extension) +//! The ink-examples repository maintains an +//! [end-to-end example](https://github.com/paritytech/ink-examples/tree/main/rand-extension) //! on how to use a chain extension in order to provide new features to ink! contracts. use crate::{ diff --git a/frame/contracts/src/lib.rs b/frame/contracts/src/lib.rs index 6b8b85458..26b16b329 100644 --- a/frame/contracts/src/lib.rs +++ b/frame/contracts/src/lib.rs @@ -76,7 +76,7 @@ //! The Contract module is a work in progress. The following examples show how this Contract module //! can be used to instantiate and call contracts. //! -//! * [`ink`](https://github.com/paritytech/ink) is +//! * [`ink!`](https://use.ink) is //! an [`eDSL`](https://wiki.haskell.org/Embedded_domain_specific_language) that enables writing //! WebAssembly based smart contracts in the Rust programming language. From 395853ac15e6e1b2405b7244624499c4fc01cbc2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bastian=20K=C3=B6cher?= Date: Wed, 5 Apr 2023 14:27:26 +0200 Subject: [PATCH 340/558] Move registration of `ReadRuntimeVersionExt` to `ExecutionExtension` (#13820) Instead of registering `ReadRuntimeVersionExt` in `sp-state-machine` it is moved to `ExecutionExtension` which provides the default extensions. --- bin/node/testing/src/bench.rs | 9 +++++++-- client/api/src/execution_extensions.rs | 18 ++++++------------ client/service/src/builder.rs | 1 + client/service/src/client/call_executor.rs | 1 + client/service/src/client/client.rs | 1 + primitives/core/src/traits.rs | 10 ++++++++++ primitives/state-machine/src/lib.rs | 6 ++---- test-utils/client/src/lib.rs | 3 ++- .../benchmarking-cli/src/pallet/command.rs | 3 ++- .../cli/src/commands/execute_block.rs | 2 +- .../cli/src/commands/fast_forward.rs | 4 ++-- .../cli/src/commands/follow_chain.rs | 2 +- .../cli/src/commands/offchain_worker.rs | 2 +- utils/frame/try-runtime/cli/src/lib.rs | 5 +++-- 14 files changed, 40 insertions(+), 27 deletions(-) diff --git a/bin/node/testing/src/bench.rs b/bin/node/testing/src/bench.rs index d6bcf6e25..392b78241 100644 --- a/bin/node/testing/src/bench.rs +++ b/bin/node/testing/src/bench.rs @@ -411,11 +411,16 @@ impl BenchDb { let client = sc_service::new_client( backend.clone(), - executor, + executor.clone(), genesis_block_builder, None, None, - ExecutionExtensions::new(profile.into_execution_strategies(), None, None), + ExecutionExtensions::new( + profile.into_execution_strategies(), + None, + None, + Arc::new(executor), + ), Box::new(task_executor.clone()), None, None, diff --git a/client/api/src/execution_extensions.rs b/client/api/src/execution_extensions.rs index ffa670f7b..9344afbd3 100644 --- a/client/api/src/execution_extensions.rs +++ b/client/api/src/execution_extensions.rs @@ -27,6 +27,7 @@ use parking_lot::RwLock; use sc_transaction_pool_api::OffchainSubmitTransaction; use sp_core::{ offchain::{self, OffchainDbExt, OffchainWorkerExt, TransactionPoolExt}, + traits::{ReadRuntimeVersion, ReadRuntimeVersionExt}, ExecutionContext, }; use sp_externalities::{Extension, Extensions}; @@ -173,18 +174,7 @@ pub struct ExecutionExtensions { // during initialization. transaction_pool: RwLock>>>, extensions_factory: RwLock>>, -} - -impl Default for ExecutionExtensions { - fn default() -> Self { - Self { - strategies: Default::default(), - keystore: None, - offchain_db: None, - transaction_pool: RwLock::new(None), - extensions_factory: RwLock::new(Box::new(())), - } - } + read_runtime_version: Arc, } impl ExecutionExtensions { @@ -193,6 +183,7 @@ impl ExecutionExtensions { strategies: ExecutionStrategies, keystore: Option, offchain_db: Option>, + read_runtime_version: Arc, ) -> Self { let transaction_pool = RwLock::new(None); let extensions_factory = Box::new(()); @@ -202,6 +193,7 @@ impl ExecutionExtensions { offchain_db, extensions_factory: RwLock::new(extensions_factory), transaction_pool, + read_runtime_version, } } @@ -271,6 +263,8 @@ impl ExecutionExtensions { ))); } + extensions.register(ReadRuntimeVersionExt::new(self.read_runtime_version.clone())); + extensions } diff --git a/client/service/src/builder.rs b/client/service/src/builder.rs index 5d639431f..d399b3154 100644 --- a/client/service/src/builder.rs +++ b/client/service/src/builder.rs @@ -181,6 +181,7 @@ where config.execution_strategies.clone(), Some(keystore_container.keystore()), sc_offchain::OffchainDb::factory_from_backend(&*backend), + Arc::new(executor.clone()), ); let wasm_runtime_substitutes = config diff --git a/client/service/src/client/call_executor.rs b/client/service/src/client/call_executor.rs index 4d019be90..ef36768fe 100644 --- a/client/service/src/client/call_executor.rs +++ b/client/service/src/client/call_executor.rs @@ -427,6 +427,7 @@ mod tests { Default::default(), None, None, + Arc::new(executor.clone()), )), }; diff --git a/client/service/src/client/client.rs b/client/service/src/client/client.rs index 3adb6d897..eee7e6b82 100644 --- a/client/service/src/client/client.rs +++ b/client/service/src/client/client.rs @@ -243,6 +243,7 @@ where Default::default(), keystore, sc_offchain::OffchainDb::factory_from_backend(&*backend), + Arc::new(executor.clone()), ); let call_executor = diff --git a/primitives/core/src/traits.rs b/primitives/core/src/traits.rs index 513278684..40137053a 100644 --- a/primitives/core/src/traits.rs +++ b/primitives/core/src/traits.rs @@ -157,6 +157,16 @@ pub trait ReadRuntimeVersion: Send + Sync { ) -> Result, String>; } +impl ReadRuntimeVersion for std::sync::Arc { + fn read_runtime_version( + &self, + wasm_code: &[u8], + ext: &mut dyn Externalities, + ) -> Result, String> { + (**self).read_runtime_version(wasm_code, ext) + } +} + sp_externalities::decl_extension! { /// An extension that provides functionality to read version information from a given wasm blob. pub struct ReadRuntimeVersionExt(Box); diff --git a/primitives/state-machine/src/lib.rs b/primitives/state-machine/src/lib.rs index c68cf4d00..0001d0026 100644 --- a/primitives/state-machine/src/lib.rs +++ b/primitives/state-machine/src/lib.rs @@ -163,7 +163,7 @@ mod execution { use sp_core::{ hexdisplay::HexDisplay, storage::{ChildInfo, ChildType, PrefixedStorageKey}, - traits::{CallContext, CodeExecutor, ReadRuntimeVersionExt, RuntimeCode}, + traits::{CallContext, CodeExecutor, RuntimeCode}, }; use sp_externalities::Extensions; use std::{ @@ -322,12 +322,10 @@ mod execution { exec: &'a Exec, method: &'a str, call_data: &'a [u8], - mut extensions: Extensions, + extensions: Extensions, runtime_code: &'a RuntimeCode, context: CallContext, ) -> Self { - extensions.register(ReadRuntimeVersionExt::new(exec.clone())); - Self { backend, exec, diff --git a/test-utils/client/src/lib.rs b/test-utils/client/src/lib.rs index 5e3c9f703..c4572061c 100644 --- a/test-utils/client/src/lib.rs +++ b/test-utils/client/src/lib.rs @@ -290,12 +290,13 @@ impl }); let executor = LocalCallExecutor::new( self.backend.clone(), - executor, + executor.clone(), Default::default(), ExecutionExtensions::new( self.execution_strategies.clone(), self.keystore.clone(), sc_offchain::OffchainDb::factory_from_backend(&*self.backend), + Arc::new(executor), ), ) .expect("Creates LocalCallExecutor"); diff --git a/utils/frame/benchmarking-cli/src/pallet/command.rs b/utils/frame/benchmarking-cli/src/pallet/command.rs index 5016d65b8..4d583950d 100644 --- a/utils/frame/benchmarking-cli/src/pallet/command.rs +++ b/utils/frame/benchmarking-cli/src/pallet/command.rs @@ -35,7 +35,7 @@ use sp_core::{ testing::{TestOffchainExt, TestTransactionPoolExt}, OffchainDbExt, OffchainWorkerExt, TransactionPoolExt, }, - traits::CallContext, + traits::{CallContext, ReadRuntimeVersionExt}, }; use sp_externalities::Extensions; use sp_keystore::{testing::MemoryKeystore, KeystoreExt}; @@ -225,6 +225,7 @@ impl PalletCmd { extensions.register(OffchainWorkerExt::new(offchain.clone())); extensions.register(OffchainDbExt::new(offchain)); extensions.register(TransactionPoolExt::new(pool)); + extensions.register(ReadRuntimeVersionExt::new(executor.clone())); extensions }; diff --git a/utils/frame/try-runtime/cli/src/commands/execute_block.rs b/utils/frame/try-runtime/cli/src/commands/execute_block.rs index 561bc57c7..48dab6b9b 100644 --- a/utils/frame/try-runtime/cli/src/commands/execute_block.rs +++ b/utils/frame/try-runtime/cli/src/commands/execute_block.rs @@ -133,7 +133,7 @@ where &executor, "TryRuntime_execute_block", &payload, - full_extensions(), + full_extensions(executor.clone()), shared.export_proof, )?; diff --git a/utils/frame/try-runtime/cli/src/commands/fast_forward.rs b/utils/frame/try-runtime/cli/src/commands/fast_forward.rs index 75c48c3c4..0c517c02f 100644 --- a/utils/frame/try-runtime/cli/src/commands/fast_forward.rs +++ b/utils/frame/try-runtime/cli/src/commands/fast_forward.rs @@ -103,7 +103,7 @@ async fn dry_run( executor, method, data, - full_extensions(), + full_extensions(executor.clone()), )?; Ok(::decode(&mut &*result)?) @@ -121,7 +121,7 @@ async fn run( executor, method, data, - full_extensions(), + full_extensions(executor.clone()), )?; let storage_changes = changes.drain_storage_changes( diff --git a/utils/frame/try-runtime/cli/src/commands/follow_chain.rs b/utils/frame/try-runtime/cli/src/commands/follow_chain.rs index 2a67d269c..413c68550 100644 --- a/utils/frame/try-runtime/cli/src/commands/follow_chain.rs +++ b/utils/frame/try-runtime/cli/src/commands/follow_chain.rs @@ -149,7 +149,7 @@ where &executor, "TryRuntime_execute_block", (block, command.state_root_check, command.try_state.clone()).encode().as_ref(), - full_extensions(), + full_extensions(executor.clone()), shared .export_proof .as_ref() diff --git a/utils/frame/try-runtime/cli/src/commands/offchain_worker.rs b/utils/frame/try-runtime/cli/src/commands/offchain_worker.rs index 352d552a3..4da6f0783 100644 --- a/utils/frame/try-runtime/cli/src/commands/offchain_worker.rs +++ b/utils/frame/try-runtime/cli/src/commands/offchain_worker.rs @@ -97,7 +97,7 @@ where &executor, "OffchainWorkerApi_offchain_worker", &payload, - full_extensions(), + full_extensions(executor.clone()), )?; Ok(()) diff --git a/utils/frame/try-runtime/cli/src/lib.rs b/utils/frame/try-runtime/cli/src/lib.rs index 733eab7f5..db690e508 100644 --- a/utils/frame/try-runtime/cli/src/lib.rs +++ b/utils/frame/try-runtime/cli/src/lib.rs @@ -377,7 +377,7 @@ use sp_core::{ OffchainDbExt, OffchainWorkerExt, TransactionPoolExt, }, storage::well_known_keys, - traits::{CallContext, ReadRuntimeVersion}, + traits::{CallContext, ReadRuntimeVersion, ReadRuntimeVersionExt}, twox_128, H256, }; use sp_externalities::Extensions; @@ -810,7 +810,7 @@ where } /// Build all extensions that we typically use. -pub(crate) fn full_extensions() -> Extensions { +pub(crate) fn full_extensions(wasm_executor: WasmExecutor) -> Extensions { let mut extensions = Extensions::default(); let (offchain, _offchain_state) = TestOffchainExt::new(); let (pool, _pool_state) = TestTransactionPoolExt::new(); @@ -819,6 +819,7 @@ pub(crate) fn full_extensions() -> Extensions { extensions.register(OffchainWorkerExt::new(offchain)); extensions.register(KeystoreExt::new(keystore)); extensions.register(TransactionPoolExt::new(pool)); + extensions.register(ReadRuntimeVersionExt::new(wasm_executor)); extensions } From 5380a9a513918c144175cf2a48d66a35e6e7cad4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gon=C3=A7alo=20Pestana?= Date: Wed, 5 Apr 2023 14:14:46 +0100 Subject: [PATCH 341/558] Mention `on_idle` round-robin logic to `trait Hooks` cargo doc (#13797) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Adds on_idle round-robin logic to trait Hooks docs * Makes the docs more concise * Update frame/support/src/traits/hooks.rs Co-authored-by: Bastian Köcher * fmt --------- Co-authored-by: Bastian Köcher --- frame/support/src/traits/hooks.rs | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/frame/support/src/traits/hooks.rs b/frame/support/src/traits/hooks.rs index a3374d0bf..2d346ccd3 100644 --- a/frame/support/src/traits/hooks.rs +++ b/frame/support/src/traits/hooks.rs @@ -227,10 +227,15 @@ pub trait Hooks { fn on_finalize(_n: BlockNumber) {} /// This will be run when the block is being finalized (before `on_finalize`). - /// Implement to have something happen using the remaining weight. - /// Will not fire if the remaining weight is 0. - /// Return the weight used, the hook will subtract it from current weight used - /// and pass the result to the next `on_idle` hook if it exists. + /// + /// Implement to have something happen using the remaining weight. Will not fire if the + /// remaining weight is 0. + /// + /// Each pallet's `on_idle` is chosen to be the first to execute in a round-robin fashion + /// indexed by the block number. + /// + /// Return the weight used, the caller will use this to calculate the remaining weight and then + /// call the next pallet `on_idle` hook if there is still weight left. fn on_idle(_n: BlockNumber, _remaining_weight: Weight) -> Weight { Weight::zero() } From 5601965f39d5899e20f6d89526607ace852c33d4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bastian=20K=C3=B6cher?= Date: Wed, 5 Apr 2023 16:43:17 +0200 Subject: [PATCH 342/558] ProofRecorder: Implement transactional support (#13769) * TrieRecorder: Start adding support for transactions * Adds `transactions` functions and some test * More tests * Docs * Ensure that we rollback failed transactions in the storage proof * FMT * Update primitives/trie/src/recorder.rs Co-authored-by: Dmitry Markin * Review comments * Update primitives/trie/src/recorder.rs Co-authored-by: Sebastian Kunert * ".git/.scripts/commands/fmt/fmt.sh" * For the holy clippy! * Update primitives/trie/src/recorder.rs Co-authored-by: Anton --------- Co-authored-by: Dmitry Markin Co-authored-by: Sebastian Kunert Co-authored-by: command-bot <> Co-authored-by: Anton --- client/block-builder/src/lib.rs | 62 ++- .../api/proc-macro/src/impl_runtime_apis.rs | 50 +- primitives/trie/src/recorder.rs | 463 ++++++++++++++++-- test-utils/runtime/src/lib.rs | 12 +- test-utils/runtime/src/system.rs | 26 + 5 files changed, 573 insertions(+), 40 deletions(-) diff --git a/client/block-builder/src/lib.rs b/client/block-builder/src/lib.rs index d97afadd4..21f8e6fdd 100644 --- a/client/block-builder/src/lib.rs +++ b/client/block-builder/src/lib.rs @@ -312,7 +312,9 @@ mod tests { use sp_blockchain::HeaderBackend; use sp_core::Blake2Hasher; use sp_state_machine::Backend; - use substrate_test_runtime_client::{DefaultTestClientBuilderExt, TestClientBuilderExt}; + use substrate_test_runtime_client::{ + runtime::Extrinsic, DefaultTestClientBuilderExt, TestClientBuilderExt, + }; #[test] fn block_building_storage_proof_does_not_include_runtime_by_default() { @@ -345,4 +347,62 @@ mod tests { .unwrap_err() .contains("Database missing expected key"),); } + + #[test] + fn failing_extrinsic_rolls_back_changes_in_storage_proof() { + let builder = substrate_test_runtime_client::TestClientBuilder::new(); + let backend = builder.backend(); + let client = builder.build(); + + let mut block_builder = BlockBuilder::new( + &client, + client.info().best_hash, + client.info().best_number, + RecordProof::Yes, + Default::default(), + &*backend, + ) + .unwrap(); + + block_builder.push(Extrinsic::ReadAndPanic(8)).unwrap_err(); + + let block = block_builder.build().unwrap(); + + let proof_with_panic = block.proof.expect("Proof is build on request").encoded_size(); + + let mut block_builder = BlockBuilder::new( + &client, + client.info().best_hash, + client.info().best_number, + RecordProof::Yes, + Default::default(), + &*backend, + ) + .unwrap(); + + block_builder.push(Extrinsic::Read(8)).unwrap(); + + let block = block_builder.build().unwrap(); + + let proof_without_panic = block.proof.expect("Proof is build on request").encoded_size(); + + let block = BlockBuilder::new( + &client, + client.info().best_hash, + client.info().best_number, + RecordProof::Yes, + Default::default(), + &*backend, + ) + .unwrap() + .build() + .unwrap(); + + let proof_empty_block = block.proof.expect("Proof is build on request").encoded_size(); + + // Ensure that we rolled back the changes of the panicked transaction. + assert!(proof_without_panic > proof_with_panic); + assert!(proof_without_panic > proof_empty_block); + assert_eq!(proof_empty_block, proof_with_panic); + } } diff --git a/primitives/api/proc-macro/src/impl_runtime_apis.rs b/primitives/api/proc-macro/src/impl_runtime_apis.rs index d0725ffd2..0d265293e 100644 --- a/primitives/api/proc-macro/src/impl_runtime_apis.rs +++ b/primitives/api/proc-macro/src/impl_runtime_apis.rs @@ -243,7 +243,8 @@ fn generate_runtime_api_base_structures() -> Result { &self, call: F, ) -> R where Self: Sized { - #crate_::OverlayedChanges::start_transaction(&mut std::cell::RefCell::borrow_mut(&self.changes)); + self.start_transaction(); + *std::cell::RefCell::borrow_mut(&self.commit_on_success) = false; let res = call(self); *std::cell::RefCell::borrow_mut(&self.commit_on_success) = true; @@ -347,18 +348,51 @@ fn generate_runtime_api_base_structures() -> Result { transactions; qed"; if *std::cell::RefCell::borrow(&self.commit_on_success) { let res = if commit { - #crate_::OverlayedChanges::commit_transaction( + let res = if let Some(recorder) = &self.recorder { + #crate_::ProofRecorder::::commit_transaction(&recorder) + } else { + Ok(()) + }; + + let res2 = #crate_::OverlayedChanges::commit_transaction( &mut std::cell::RefCell::borrow_mut(&self.changes) - ) + ); + + // Will panic on an `Err` below, however we should call commit + // on the recorder and the changes together. + std::result::Result::and(res, std::result::Result::map_err(res2, drop)) } else { - #crate_::OverlayedChanges::rollback_transaction( + let res = if let Some(recorder) = &self.recorder { + #crate_::ProofRecorder::::rollback_transaction(&recorder) + } else { + Ok(()) + }; + + let res2 = #crate_::OverlayedChanges::rollback_transaction( &mut std::cell::RefCell::borrow_mut(&self.changes) - ) + ); + + // Will panic on an `Err` below, however we should call commit + // on the recorder and the changes together. + std::result::Result::and(res, std::result::Result::map_err(res2, drop)) }; std::result::Result::expect(res, proof); } } + + fn start_transaction(&self) { + if !*std::cell::RefCell::borrow(&self.commit_on_success) { + return + } + + #crate_::OverlayedChanges::start_transaction( + &mut std::cell::RefCell::borrow_mut(&self.changes) + ); + if let Some(recorder) = &self.recorder { + #crate_::ProofRecorder::::start_transaction(&recorder); + } + } } )) } @@ -450,11 +484,7 @@ impl<'a> ApiRuntimeImplToApiRuntimeApiImpl<'a> { params: std::vec::Vec, fn_name: &dyn Fn(#crate_::RuntimeVersion) -> &'static str, ) -> std::result::Result, #crate_::ApiError> { - if *std::cell::RefCell::borrow(&self.commit_on_success) { - #crate_::OverlayedChanges::start_transaction( - &mut std::cell::RefCell::borrow_mut(&self.changes) - ); - } + self.start_transaction(); let res = (|| { let version = #crate_::CallApiAt::<__SrApiBlock__>::runtime_version_at( diff --git a/primitives/trie/src/recorder.rs b/primitives/trie/src/recorder.rs index 3bdfda015..728dc8362 100644 --- a/primitives/trie/src/recorder.rs +++ b/primitives/trie/src/recorder.rs @@ -25,7 +25,7 @@ use codec::Encode; use hash_db::Hasher; use parking_lot::Mutex; use std::{ - collections::HashMap, + collections::{HashMap, HashSet}, marker::PhantomData, mem, ops::DerefMut, @@ -38,17 +38,43 @@ use trie_db::{RecordedForKey, TrieAccess}; const LOG_TARGET: &str = "trie-recorder"; +/// Stores all the information per transaction. +#[derive(Default)] +struct Transaction { + /// Stores transaction information about [`RecorderInner::recorded_keys`]. + /// + /// For each transaction we only store the `storage_root` and the old states per key. `None` + /// state means that the key wasn't recorded before. + recorded_keys: HashMap, Option>>, + /// Stores transaction information about [`RecorderInner::accessed_nodes`]. + /// + /// For each transaction we only store the hashes of added nodes. + accessed_nodes: HashSet, +} + /// The internals of [`Recorder`]. struct RecorderInner { /// The keys for that we have recorded the trie nodes and if we have recorded up to the value. - recorded_keys: HashMap, RecordedForKey>>, + /// + /// Mapping: `StorageRoot -> (Key -> RecordedForKey)`. + recorded_keys: HashMap, RecordedForKey>>, + + /// Currently active transactions. + transactions: Vec>, + /// The encoded nodes we accessed while recording. + /// + /// Mapping: `Hash(Node) -> Node`. accessed_nodes: HashMap>, } impl Default for RecorderInner { fn default() -> Self { - Self { recorded_keys: Default::default(), accessed_nodes: Default::default() } + Self { + recorded_keys: Default::default(), + accessed_nodes: Default::default(), + transactions: Vec::new(), + } } } @@ -83,6 +109,8 @@ impl Recorder { /// /// - `storage_root`: The storage root of the trie for which accesses are recorded. This is /// important when recording access to different tries at once (like top and child tries). + /// + /// NOTE: This locks a mutex that stays locked until the return value is dropped. #[inline] pub fn as_trie_recorder( &self, @@ -135,6 +163,72 @@ impl Recorder { mem::take(&mut *self.inner.lock()); self.encoded_size_estimation.store(0, Ordering::Relaxed); } + + /// Start a new transaction. + pub fn start_transaction(&self) { + let mut inner = self.inner.lock(); + inner.transactions.push(Default::default()); + } + + /// Rollback the latest transaction. + /// + /// Returns an error if there wasn't any active transaction. + pub fn rollback_transaction(&self) -> Result<(), ()> { + let mut inner = self.inner.lock(); + + // We locked `inner` and can just update the encoded size locally and then store it back to + // the atomic. + let mut new_encoded_size_estimation = self.encoded_size_estimation.load(Ordering::Relaxed); + let transaction = inner.transactions.pop().ok_or(())?; + + transaction.accessed_nodes.into_iter().for_each(|n| { + if let Some(old) = inner.accessed_nodes.remove(&n) { + new_encoded_size_estimation = + new_encoded_size_estimation.saturating_sub(old.encoded_size()); + } + }); + + transaction.recorded_keys.into_iter().for_each(|(storage_root, keys)| { + keys.into_iter().for_each(|(k, old_state)| { + if let Some(state) = old_state { + inner.recorded_keys.entry(storage_root).or_default().insert(k, state); + } else { + inner.recorded_keys.entry(storage_root).or_default().remove(&k); + } + }); + }); + + self.encoded_size_estimation + .store(new_encoded_size_estimation, Ordering::Relaxed); + + Ok(()) + } + + /// Commit the latest transaction. + /// + /// Returns an error if there wasn't any active transaction. + pub fn commit_transaction(&self) -> Result<(), ()> { + let mut inner = self.inner.lock(); + + let transaction = inner.transactions.pop().ok_or(())?; + + if let Some(parent_transaction) = inner.transactions.last_mut() { + parent_transaction.accessed_nodes.extend(transaction.accessed_nodes); + + transaction.recorded_keys.into_iter().for_each(|(storage_root, keys)| { + keys.into_iter().for_each(|(k, old_state)| { + parent_transaction + .recorded_keys + .entry(storage_root) + .or_default() + .entry(k) + .or_insert(old_state); + }) + }); + } + + Ok(()) + } } /// The [`TrieRecorder`](trie_db::TrieRecorder) implementation. @@ -145,6 +239,50 @@ struct TrieRecorder { _phantom: PhantomData, } +impl>> TrieRecorder { + /// Update the recorded keys entry for the given `full_key`. + fn update_recorded_keys(&mut self, full_key: &[u8], access: RecordedForKey) { + let inner = self.inner.deref_mut(); + + let entry = + inner.recorded_keys.entry(self.storage_root).or_default().entry(full_key.into()); + + let key = entry.key().clone(); + + // We don't need to update the record if we only accessed the `Hash` for the given + // `full_key`. Only `Value` access can be an upgrade from `Hash`. + let entry = if matches!(access, RecordedForKey::Value) { + entry.and_modify(|e| { + if let Some(tx) = inner.transactions.last_mut() { + // Store the previous state only once per transaction. + tx.recorded_keys + .entry(self.storage_root) + .or_default() + .entry(key.clone()) + .or_insert(Some(*e)); + } + + *e = access; + }) + } else { + entry + }; + + entry.or_insert_with(|| { + if let Some(tx) = inner.transactions.last_mut() { + // The key wasn't yet recorded, so there isn't any old state. + tx.recorded_keys + .entry(self.storage_root) + .or_default() + .entry(key) + .or_insert(None); + } + + access + }); + } +} + impl>> trie_db::TrieRecorder for TrieRecorder { @@ -159,11 +297,17 @@ impl>> trie_db::TrieRecord "Recording node", ); - self.inner.accessed_nodes.entry(hash).or_insert_with(|| { + let inner = self.inner.deref_mut(); + + inner.accessed_nodes.entry(hash).or_insert_with(|| { let node = node_owned.to_encoded::>(); encoded_size_update += node.encoded_size(); + if let Some(tx) = inner.transactions.last_mut() { + tx.accessed_nodes.insert(hash); + } + node }); }, @@ -174,11 +318,17 @@ impl>> trie_db::TrieRecord "Recording node", ); - self.inner.accessed_nodes.entry(hash).or_insert_with(|| { + let inner = self.inner.deref_mut(); + + inner.accessed_nodes.entry(hash).or_insert_with(|| { let node = encoded_node.into_owned(); encoded_size_update += node.encoded_size(); + if let Some(tx) = inner.transactions.last_mut() { + tx.accessed_nodes.insert(hash); + } + node }); }, @@ -190,21 +340,21 @@ impl>> trie_db::TrieRecord "Recording value", ); - self.inner.accessed_nodes.entry(hash).or_insert_with(|| { + let inner = self.inner.deref_mut(); + + inner.accessed_nodes.entry(hash).or_insert_with(|| { let value = value.into_owned(); encoded_size_update += value.encoded_size(); + if let Some(tx) = inner.transactions.last_mut() { + tx.accessed_nodes.insert(hash); + } + value }); - self.inner - .recorded_keys - .entry(self.storage_root) - .or_default() - .entry(full_key.to_vec()) - .and_modify(|e| *e = RecordedForKey::Value) - .or_insert(RecordedForKey::Value); + self.update_recorded_keys(full_key, RecordedForKey::Value); }, TrieAccess::Hash { full_key } => { tracing::trace!( @@ -215,12 +365,7 @@ impl>> trie_db::TrieRecord // We don't need to update the `encoded_size_update` as the hash was already // accounted for by the recorded node that holds the hash. - self.inner - .recorded_keys - .entry(self.storage_root) - .or_default() - .entry(full_key.to_vec()) - .or_insert(RecordedForKey::Hash); + self.update_recorded_keys(full_key, RecordedForKey::Hash); }, TrieAccess::NonExisting { full_key } => { tracing::trace!( @@ -232,13 +377,7 @@ impl>> trie_db::TrieRecord // Non-existing access means we recorded all trie nodes up to the value. // Not the actual value, as it doesn't exist, but all trie nodes to know // that the value doesn't exist in the trie. - self.inner - .recorded_keys - .entry(self.storage_root) - .or_default() - .entry(full_key.to_vec()) - .and_modify(|e| *e = RecordedForKey::Value) - .or_insert(RecordedForKey::Value); + self.update_recorded_keys(full_key, RecordedForKey::Value); }, }; @@ -256,14 +395,15 @@ impl>> trie_db::TrieRecord #[cfg(test)] mod tests { - use trie_db::{Trie, TrieDBBuilder, TrieDBMutBuilder, TrieHash, TrieMut}; + use super::*; + use trie_db::{Trie, TrieDBBuilder, TrieDBMutBuilder, TrieHash, TrieMut, TrieRecorder}; type MemoryDB = crate::MemoryDB; type Layout = crate::LayoutV1; type Recorder = super::Recorder; const TEST_DATA: &[(&[u8], &[u8])] = - &[(b"key1", b"val1"), (b"key2", b"val2"), (b"key3", b"val3"), (b"key4", b"val4")]; + &[(b"key1", &[1; 64]), (b"key2", &[2; 64]), (b"key3", &[3; 64]), (b"key4", &[4; 64])]; fn create_trie() -> (MemoryDB, TrieHash) { let mut db = MemoryDB::default(); @@ -300,4 +440,271 @@ mod tests { let trie = TrieDBBuilder::::new(&memory_db, &root).build(); assert_eq!(TEST_DATA[0].1.to_vec(), trie.get(TEST_DATA[0].0).unwrap().unwrap()); } + + #[derive(Debug, Clone, Copy, PartialEq, Eq, Default)] + struct RecorderStats { + accessed_nodes: usize, + recorded_keys: usize, + estimated_size: usize, + } + + impl RecorderStats { + fn extract(recorder: &Recorder) -> Self { + let inner = recorder.inner.lock(); + + let recorded_keys = + inner.recorded_keys.iter().flat_map(|(_, keys)| keys.keys()).count(); + + Self { + recorded_keys, + accessed_nodes: inner.accessed_nodes.len(), + estimated_size: recorder.estimate_encoded_size(), + } + } + } + + #[test] + fn recorder_transactions_rollback_work() { + let (db, root) = create_trie(); + + let recorder = Recorder::default(); + let mut stats = vec![RecorderStats::default()]; + + for i in 0..4 { + recorder.start_transaction(); + { + let mut trie_recorder = recorder.as_trie_recorder(root); + let trie = TrieDBBuilder::::new(&db, &root) + .with_recorder(&mut trie_recorder) + .build(); + + assert_eq!(TEST_DATA[i].1.to_vec(), trie.get(TEST_DATA[i].0).unwrap().unwrap()); + } + stats.push(RecorderStats::extract(&recorder)); + } + + assert_eq!(4, recorder.inner.lock().transactions.len()); + + for i in 0..5 { + assert_eq!(stats[4 - i], RecorderStats::extract(&recorder)); + + let storage_proof = recorder.to_storage_proof(); + let memory_db: MemoryDB = storage_proof.into_memory_db(); + + // Check that we recorded the required data + let trie = TrieDBBuilder::::new(&memory_db, &root).build(); + + // Check that the required data is still present. + for a in 0..4 { + if a < 4 - i { + assert_eq!(TEST_DATA[a].1.to_vec(), trie.get(TEST_DATA[a].0).unwrap().unwrap()); + } else { + // All the data that we already rolled back, should be gone! + assert!(trie.get(TEST_DATA[a].0).is_err()); + } + } + + if i < 4 { + recorder.rollback_transaction().unwrap(); + } + } + + assert_eq!(0, recorder.inner.lock().transactions.len()); + } + + #[test] + fn recorder_transactions_commit_work() { + let (db, root) = create_trie(); + + let recorder = Recorder::default(); + + for i in 0..4 { + recorder.start_transaction(); + { + let mut trie_recorder = recorder.as_trie_recorder(root); + let trie = TrieDBBuilder::::new(&db, &root) + .with_recorder(&mut trie_recorder) + .build(); + + assert_eq!(TEST_DATA[i].1.to_vec(), trie.get(TEST_DATA[i].0).unwrap().unwrap()); + } + } + + let stats = RecorderStats::extract(&recorder); + assert_eq!(4, recorder.inner.lock().transactions.len()); + + for _ in 0..4 { + recorder.commit_transaction().unwrap(); + } + assert_eq!(0, recorder.inner.lock().transactions.len()); + assert_eq!(stats, RecorderStats::extract(&recorder)); + + let storage_proof = recorder.to_storage_proof(); + let memory_db: MemoryDB = storage_proof.into_memory_db(); + + // Check that we recorded the required data + let trie = TrieDBBuilder::::new(&memory_db, &root).build(); + + // Check that the required data is still present. + for i in 0..4 { + assert_eq!(TEST_DATA[i].1.to_vec(), trie.get(TEST_DATA[i].0).unwrap().unwrap()); + } + } + + #[test] + fn recorder_transactions_commit_and_rollback_work() { + let (db, root) = create_trie(); + + let recorder = Recorder::default(); + + for i in 0..2 { + recorder.start_transaction(); + { + let mut trie_recorder = recorder.as_trie_recorder(root); + let trie = TrieDBBuilder::::new(&db, &root) + .with_recorder(&mut trie_recorder) + .build(); + + assert_eq!(TEST_DATA[i].1.to_vec(), trie.get(TEST_DATA[i].0).unwrap().unwrap()); + } + } + + recorder.rollback_transaction().unwrap(); + + for i in 2..4 { + recorder.start_transaction(); + { + let mut trie_recorder = recorder.as_trie_recorder(root); + let trie = TrieDBBuilder::::new(&db, &root) + .with_recorder(&mut trie_recorder) + .build(); + + assert_eq!(TEST_DATA[i].1.to_vec(), trie.get(TEST_DATA[i].0).unwrap().unwrap()); + } + } + + recorder.rollback_transaction().unwrap(); + + assert_eq!(2, recorder.inner.lock().transactions.len()); + + for _ in 0..2 { + recorder.commit_transaction().unwrap(); + } + + assert_eq!(0, recorder.inner.lock().transactions.len()); + + let storage_proof = recorder.to_storage_proof(); + let memory_db: MemoryDB = storage_proof.into_memory_db(); + + // Check that we recorded the required data + let trie = TrieDBBuilder::::new(&memory_db, &root).build(); + + // Check that the required data is still present. + for i in 0..4 { + if i % 2 == 0 { + assert_eq!(TEST_DATA[i].1.to_vec(), trie.get(TEST_DATA[i].0).unwrap().unwrap()); + } else { + assert!(trie.get(TEST_DATA[i].0).is_err()); + } + } + } + + #[test] + fn recorder_transaction_accessed_keys_works() { + let key = TEST_DATA[0].0; + let (db, root) = create_trie(); + + let recorder = Recorder::default(); + + { + let trie_recorder = recorder.as_trie_recorder(root); + assert!(matches!(trie_recorder.trie_nodes_recorded_for_key(key), RecordedForKey::None)); + } + + recorder.start_transaction(); + { + let mut trie_recorder = recorder.as_trie_recorder(root); + let trie = TrieDBBuilder::::new(&db, &root) + .with_recorder(&mut trie_recorder) + .build(); + + assert_eq!( + sp_core::Blake2Hasher::hash(TEST_DATA[0].1), + trie.get_hash(TEST_DATA[0].0).unwrap().unwrap() + ); + assert!(matches!(trie_recorder.trie_nodes_recorded_for_key(key), RecordedForKey::Hash)); + } + + recorder.start_transaction(); + { + let mut trie_recorder = recorder.as_trie_recorder(root); + let trie = TrieDBBuilder::::new(&db, &root) + .with_recorder(&mut trie_recorder) + .build(); + + assert_eq!(TEST_DATA[0].1.to_vec(), trie.get(TEST_DATA[0].0).unwrap().unwrap()); + assert!(matches!( + trie_recorder.trie_nodes_recorded_for_key(key), + RecordedForKey::Value, + )); + } + + recorder.rollback_transaction().unwrap(); + { + let trie_recorder = recorder.as_trie_recorder(root); + assert!(matches!(trie_recorder.trie_nodes_recorded_for_key(key), RecordedForKey::Hash)); + } + + recorder.rollback_transaction().unwrap(); + { + let trie_recorder = recorder.as_trie_recorder(root); + assert!(matches!(trie_recorder.trie_nodes_recorded_for_key(key), RecordedForKey::None)); + } + + recorder.start_transaction(); + { + let mut trie_recorder = recorder.as_trie_recorder(root); + let trie = TrieDBBuilder::::new(&db, &root) + .with_recorder(&mut trie_recorder) + .build(); + + assert_eq!(TEST_DATA[0].1.to_vec(), trie.get(TEST_DATA[0].0).unwrap().unwrap()); + assert!(matches!( + trie_recorder.trie_nodes_recorded_for_key(key), + RecordedForKey::Value, + )); + } + + recorder.start_transaction(); + { + let mut trie_recorder = recorder.as_trie_recorder(root); + let trie = TrieDBBuilder::::new(&db, &root) + .with_recorder(&mut trie_recorder) + .build(); + + assert_eq!( + sp_core::Blake2Hasher::hash(TEST_DATA[0].1), + trie.get_hash(TEST_DATA[0].0).unwrap().unwrap() + ); + assert!(matches!( + trie_recorder.trie_nodes_recorded_for_key(key), + RecordedForKey::Value + )); + } + + recorder.rollback_transaction().unwrap(); + { + let trie_recorder = recorder.as_trie_recorder(root); + assert!(matches!( + trie_recorder.trie_nodes_recorded_for_key(key), + RecordedForKey::Value + )); + } + + recorder.rollback_transaction().unwrap(); + { + let trie_recorder = recorder.as_trie_recorder(root); + assert!(matches!(trie_recorder.trie_nodes_recorded_for_key(key), RecordedForKey::None)); + } + } } diff --git a/test-utils/runtime/src/lib.rs b/test-utils/runtime/src/lib.rs index c9a0ac04d..b5600843c 100644 --- a/test-utils/runtime/src/lib.rs +++ b/test-utils/runtime/src/lib.rs @@ -164,13 +164,21 @@ pub enum Extrinsic { OffchainIndexSet(Vec, Vec), OffchainIndexClear(Vec), Store(Vec), + /// Read X times from the state some data and then panic! + /// + /// Returns `Ok` if it didn't read anything. + ReadAndPanic(u32), + /// Read X times from the state some data. + /// + /// Panics if it can not read `X` times. + Read(u32), } #[cfg(feature = "std")] impl serde::Serialize for Extrinsic { fn serialize(&self, seq: S) -> Result where - S: ::serde::Serializer, + S: serde::Serializer, { self.using_encoded(|bytes| seq.serialize_bytes(bytes)) } @@ -210,6 +218,8 @@ impl BlindCheckable for Extrinsic { Extrinsic::OffchainIndexSet(key, value) => Ok(Extrinsic::OffchainIndexSet(key, value)), Extrinsic::OffchainIndexClear(key) => Ok(Extrinsic::OffchainIndexClear(key)), Extrinsic::Store(data) => Ok(Extrinsic::Store(data)), + Extrinsic::ReadAndPanic(i) => Ok(Extrinsic::ReadAndPanic(i)), + Extrinsic::Read(i) => Ok(Extrinsic::Read(i)), } } } diff --git a/test-utils/runtime/src/system.rs b/test-utils/runtime/src/system.rs index 12ebf486b..fc7505315 100644 --- a/test-utils/runtime/src/system.rs +++ b/test-utils/runtime/src/system.rs @@ -275,6 +275,32 @@ fn execute_transaction_backend(utx: &Extrinsic, extrinsic_index: u32) -> ApplyEx Ok(Ok(())) }, Extrinsic::Store(data) => execute_store(data.clone()), + Extrinsic::ReadAndPanic(i) => execute_read(*i, true), + Extrinsic::Read(i) => execute_read(*i, false), + } +} + +fn execute_read(read: u32, panic_at_end: bool) -> ApplyExtrinsicResult { + let mut next_key = vec![]; + for _ in 0..(read as usize) { + if let Some(next) = sp_io::storage::next_key(&next_key) { + // Read the value + sp_io::storage::get(&next); + + next_key = next; + } else { + if panic_at_end { + return Ok(Ok(())) + } else { + panic!("Could not read {read} times from the state"); + } + } + } + + if panic_at_end { + panic!("BYE") + } else { + Ok(Ok(())) } } From 2bb370fbc36b2631dd85e6f1c1952cdfe7b8db8c Mon Sep 17 00:00:00 2001 From: Liam Aharon Date: Thu, 6 Apr 2023 15:28:21 +1000 Subject: [PATCH 343/558] remove duplicate sync option documentation (#13828) --- client/cli/src/params/network_params.rs | 5 ----- 1 file changed, 5 deletions(-) diff --git a/client/cli/src/params/network_params.rs b/client/cli/src/params/network_params.rs index 106fba75a..acec4b2ac 100644 --- a/client/cli/src/params/network_params.rs +++ b/client/cli/src/params/network_params.rs @@ -132,11 +132,6 @@ pub struct NetworkParams { pub ipfs_server: bool, /// Blockchain syncing mode. - /// - /// - `full`: Download and validate full blockchain history. - /// - `fast`: Download blocks and the latest state only. - /// - `fast-unsafe`: Same as `fast`, but skip downloading state proofs. - /// - `warp`: Download the latest state and proof. #[arg( long, value_enum, From c0793b58c509f352b6006e402350ac9ad8017823 Mon Sep 17 00:00:00 2001 From: Keith Yeung Date: Thu, 6 Apr 2023 14:24:45 +0800 Subject: [PATCH 344/558] Add HoldReason to the NIS pallet (#13823) * Add HoldReason to the NIS pallet * Rename composable_enum to composite_enum * Add encoding test * Add more doc comments --- frame/nis/src/lib.rs | 8 ++++++++ .../src/construct_runtime/expand/freeze_reason.rs | 1 + .../src/construct_runtime/expand/hold_reason.rs | 1 + .../src/construct_runtime/expand/lock_id.rs | 1 + .../src/construct_runtime/expand/slash_reason.rs | 1 + frame/support/procedural/src/lib.rs | 6 +++--- frame/support/src/lib.rs | 12 ++++++------ frame/support/test/tests/pallet.rs | 6 ++++++ 8 files changed, 27 insertions(+), 9 deletions(-) diff --git a/frame/nis/src/lib.rs b/frame/nis/src/lib.rs index 539011c51..9cac5f4b6 100644 --- a/frame/nis/src/lib.rs +++ b/frame/nis/src/lib.rs @@ -482,6 +482,14 @@ pub mod pallet { AlreadyPrivate, } + /// A reason for the NIS pallet placing a hold on funds. + #[pallet::composite_enum] + pub enum HoldReason { + /// The NIS Pallet has reserved it for a non-fungible receipt. + #[codec(index = 0)] + NftReceipt, + } + pub(crate) struct WeightCounter { pub(crate) used: Weight, pub(crate) limit: Weight, diff --git a/frame/support/procedural/src/construct_runtime/expand/freeze_reason.rs b/frame/support/procedural/src/construct_runtime/expand/freeze_reason.rs index 889168bf6..d357fab72 100644 --- a/frame/support/procedural/src/construct_runtime/expand/freeze_reason.rs +++ b/frame/support/procedural/src/construct_runtime/expand/freeze_reason.rs @@ -35,6 +35,7 @@ pub fn expand_outer_freeze_reason(pallet_decls: &[Pallet], scrate: &TokenStream) } quote! { + /// A reason for placing a freeze on funds. #[derive( Copy, Clone, Eq, PartialEq, Ord, PartialOrd, #scrate::codec::Encode, #scrate::codec::Decode, #scrate::codec::MaxEncodedLen, diff --git a/frame/support/procedural/src/construct_runtime/expand/hold_reason.rs b/frame/support/procedural/src/construct_runtime/expand/hold_reason.rs index 60fc094a4..6acfe5382 100644 --- a/frame/support/procedural/src/construct_runtime/expand/hold_reason.rs +++ b/frame/support/procedural/src/construct_runtime/expand/hold_reason.rs @@ -35,6 +35,7 @@ pub fn expand_outer_hold_reason(pallet_decls: &[Pallet], scrate: &TokenStream) - } quote! { + /// A reason for placing a hold on funds. #[derive( Copy, Clone, Eq, PartialEq, Ord, PartialOrd, #scrate::codec::Encode, #scrate::codec::Decode, #scrate::codec::MaxEncodedLen, diff --git a/frame/support/procedural/src/construct_runtime/expand/lock_id.rs b/frame/support/procedural/src/construct_runtime/expand/lock_id.rs index 77c6fefa0..d7031dcaf 100644 --- a/frame/support/procedural/src/construct_runtime/expand/lock_id.rs +++ b/frame/support/procedural/src/construct_runtime/expand/lock_id.rs @@ -35,6 +35,7 @@ pub fn expand_outer_lock_id(pallet_decls: &[Pallet], scrate: &TokenStream) -> To } quote! { + /// An identifier for each lock placed on funds. #[derive( Copy, Clone, Eq, PartialEq, Ord, PartialOrd, #scrate::codec::Encode, #scrate::codec::Decode, #scrate::codec::MaxEncodedLen, diff --git a/frame/support/procedural/src/construct_runtime/expand/slash_reason.rs b/frame/support/procedural/src/construct_runtime/expand/slash_reason.rs index 2bf647aa7..abffb97d4 100644 --- a/frame/support/procedural/src/construct_runtime/expand/slash_reason.rs +++ b/frame/support/procedural/src/construct_runtime/expand/slash_reason.rs @@ -35,6 +35,7 @@ pub fn expand_outer_slash_reason(pallet_decls: &[Pallet], scrate: &TokenStream) } quote! { + /// A reason for slashing funds. #[derive( Copy, Clone, Eq, PartialEq, Ord, PartialOrd, #scrate::codec::Encode, #scrate::codec::Decode, #scrate::codec::MaxEncodedLen, diff --git a/frame/support/procedural/src/lib.rs b/frame/support/procedural/src/lib.rs index dea2d4943..ea997752c 100644 --- a/frame/support/procedural/src/lib.rs +++ b/frame/support/procedural/src/lib.rs @@ -1412,7 +1412,7 @@ pub fn origin(_: TokenStream, _: TokenStream) -> TokenStream { pallet_macro_stub() } -/// The `#[pallet::composable_enum]` attribute allows you to define an enum that gets composed as an +/// The `#[pallet::composite_enum]` attribute allows you to define an enum that gets composed as an /// aggregate enum by `construct_runtime`. This is similar in principle with `#[pallet::event]` and /// `#[pallet::error]`. /// @@ -1431,10 +1431,10 @@ pub fn origin(_: TokenStream, _: TokenStream) -> TokenStream { /// ``` /// /// For ease of usage, when no `#[derive]` attributes are found for the enum under -/// `#[pallet::composable_enum]`, the aforementioned traits are automatically derived for it. The +/// `#[pallet::composite_enum]`, the aforementioned traits are automatically derived for it. The /// inverse is also true: if there are any `#[derive]` attributes found for the enum, then no traits /// will automatically be derived for it. #[proc_macro_attribute] -pub fn composable_enum(_: TokenStream, _: TokenStream) -> TokenStream { +pub fn composite_enum(_: TokenStream, _: TokenStream) -> TokenStream { pallet_macro_stub() } diff --git a/frame/support/src/lib.rs b/frame/support/src/lib.rs index e0ebeb68a..916a8696e 100644 --- a/frame/support/src/lib.rs +++ b/frame/support/src/lib.rs @@ -1552,7 +1552,7 @@ pub mod pallet_prelude { /// * [`pallet::inherent`](#inherent-palletinherent-optional) /// * [`pallet::validate_unsigned`](#validate-unsigned-palletvalidate_unsigned-optional) /// * [`pallet::origin`](#origin-palletorigin-optional) -/// * [`pallet::composable_enum`](#composable-enum-palletcomposable_enum-optional) +/// * [`pallet::composite_enum`](#composite-enum-palletcomposite_enum-optional) /// /// Note that at compile-time, the `#[pallet]` macro will analyze and expand all of these /// attributes, ultimately removing their AST nodes before they can be parsed as real @@ -2278,19 +2278,19 @@ pub mod pallet_prelude { /// /// Also see [`pallet::origin`](`frame_support::pallet_macros::origin`) /// -/// # Composable enum `#[pallet::composable_enum]` (optional) +/// # Composite enum `#[pallet::composite_enum]` (optional) /// -/// The `#[pallet::composable_enum]` attribute allows you to define an enum on the pallet which +/// The `#[pallet::composite_enum]` attribute allows you to define an enum on the pallet which /// will then instruct `construct_runtime` to amalgamate all similarly-named enums from other /// pallets into an aggregate enum. This is similar in principle with how the aggregate enum is /// generated for `#[pallet::event]` or `#[pallet::error]`. /// -/// The item tagged with `#[pallet::composable_enum]` MUST be an enum declaration, and can ONLY +/// The item tagged with `#[pallet::composite_enum]` MUST be an enum declaration, and can ONLY /// be the following identifiers: `FreezeReason`, `HoldReason`, `LockId` or `SlashReason`. /// Custom identifiers are not supported. /// /// NOTE: For ease of usage, when no `#[derive]` attributes are detected, the -/// `#[pallet::composable_enum]` attribute will automatically derive the following traits for +/// `#[pallet::composite_enum]` attribute will automatically derive the following traits for /// the enum: /// /// ```ignore @@ -2832,7 +2832,7 @@ pub use frame_support_procedural::pallet; /// Contains macro stubs for all of the pallet:: macros pub mod pallet_macros { pub use frame_support_procedural::{ - call_index, compact, composable_enum, config, constant, + call_index, compact, composite_enum, config, constant, disable_frame_system_supertrait_check, error, event, extra_constants, generate_deposit, generate_storage_info, generate_store, genesis_build, genesis_config, getter, hooks, inherent, origin, storage, storage_prefix, storage_version, type_value, unbounded, diff --git a/frame/support/test/tests/pallet.rs b/frame/support/test/tests/pallet.rs index 37f1219a8..11f82c4c0 100644 --- a/frame/support/test/tests/pallet.rs +++ b/frame/support/test/tests/pallet.rs @@ -991,6 +991,8 @@ fn validate_unsigned_expand() { #[test] fn composite_expand() { + use codec::Encode; + let hold_reason: RuntimeHoldReason = pallet::HoldReason::Staking.into(); let hold_reason2: RuntimeHoldReason = pallet2::HoldReason::Governance.into(); let slash_reason: RuntimeSlashReason = pallet2::SlashReason::Equivocation.into(); @@ -998,6 +1000,10 @@ fn composite_expand() { assert_eq!(hold_reason, RuntimeHoldReason::Example(pallet::HoldReason::Staking)); assert_eq!(hold_reason2, RuntimeHoldReason::Example2(pallet2::HoldReason::Governance)); assert_eq!(slash_reason, RuntimeSlashReason::Example2(pallet2::SlashReason::Equivocation)); + + assert_eq!(hold_reason.encode(), [1, 0]); + assert_eq!(hold_reason2.encode(), [2, 0]); + assert_eq!(slash_reason.encode(), [2, 0]); } #[test] From a1d3d5004379976b13deab86b975c04ad78bcd72 Mon Sep 17 00:00:00 2001 From: Liam Aharon Date: Thu, 6 Apr 2023 19:49:53 +1000 Subject: [PATCH 345/558] Fix `try-runtime follow-chain`, try-runtime upgrade tuple tests, cli test utils (#13794) * new test for try-runtime tuple stuff * fix * remove development comment * formatting * remove todo comment * follow-chain working test * refactor common cli testing utils * fix comment * revert Cargo.lock changes * update Cargo.lock * improve doc comment * fix error typo * update Cargo.lock * feature gate try-runtime test * build_substrate cli test util * feature gate follow_chain tests * move fn start_node to test-utils * improve test pkg name * use tokio Child and Command * remove redundant import * fix ci * fix ci * don't leave hanging processes * improved child process cleanup * use existing KillChildOnDrop * remove redundant comment * Update test-utils/cli/src/lib.rs Co-authored-by: Koute --------- Co-authored-by: kianenigma Co-authored-by: Koute --- Cargo.lock | 317 +++++++++++------- bin/node/cli/Cargo.toml | 1 + bin/node/cli/tests/benchmark_block_works.rs | 2 +- bin/node/cli/tests/benchmark_pallet_works.rs | 2 - bin/node/cli/tests/check_block_works.rs | 2 +- bin/node/cli/tests/export_import_flow.rs | 2 +- bin/node/cli/tests/inspect_works.rs | 2 +- bin/node/cli/tests/purge_chain_works.rs | 2 +- .../cli/tests/remember_state_pruning_works.rs | 2 +- .../tests/running_the_node_and_interrupt.rs | 17 +- bin/node/cli/tests/telemetry.rs | 2 +- bin/node/cli/tests/temp_base_path_works.rs | 2 +- frame/support/src/lib.rs | 6 +- frame/support/src/traits/hooks.rs | 58 +++- test-utils/cli/Cargo.toml | 23 ++ .../common.rs => test-utils/cli/src/lib.rs | 134 ++++++++ utils/frame/try-runtime/cli/Cargo.toml | 10 +- .../cli/src/commands/follow_chain.rs | 12 +- .../try-runtime/cli/tests/follow_chain.rs | 65 ++++ 19 files changed, 508 insertions(+), 153 deletions(-) create mode 100644 test-utils/cli/Cargo.toml rename bin/node/cli/tests/common.rs => test-utils/cli/src/lib.rs (52%) create mode 100644 utils/frame/try-runtime/cli/tests/follow_chain.rs diff --git a/Cargo.lock b/Cargo.lock index ed633fcaa..d4185103f 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -203,6 +203,12 @@ dependencies = [ "winapi", ] +[[package]] +name = "anstyle" +version = "0.3.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "23ea9e81bd02e310c216d080f6223c179012256e5151c41db88d12c88a1684d2" + [[package]] name = "anyhow" version = "1.0.69" @@ -294,7 +300,7 @@ checksum = "db8b7511298d5b7784b40b092d9e9dcd3a627a5707e4b5e507931ab0d44eeebf" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 1.0.109", "synstructure", ] @@ -306,7 +312,7 @@ checksum = "726535892e8eae7e70657b4c8ea93d26b8553afb1ce617caee529ef96d7dee6c" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 1.0.109", "synstructure", ] @@ -318,7 +324,7 @@ checksum = "2777730b2039ac0f95f093556e61b6d26cebed5393ca6f152717777cec3a42ed" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 1.0.109", ] [[package]] @@ -329,13 +335,14 @@ checksum = "e22d1f4b888c298a027c99dc9048015fac177587de20fc30232a057dfbe24a21" [[package]] name = "assert_cmd" -version = "2.0.8" +version = "2.0.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9834fcc22e0874394a010230586367d4a3e9f11b560f469262678547e1d2575e" +checksum = "ec0b2340f55d9661d76793b2bfc2eb0e62689bd79d067a95707ea762afd5e9dd" dependencies = [ + "anstyle", "bstr", "doc-comment", - "predicates", + "predicates 3.0.2", "predicates-core", "predicates-tree", "wait-timeout", @@ -407,7 +414,7 @@ checksum = "e4655ae1a7b0cdf149156f780c5bf3f1352bc53cbd9e0a361a7ef7b22947e965" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 1.0.109", ] [[package]] @@ -418,7 +425,7 @@ checksum = "1cd7fce9ba8c3c042128ce72d8b2ddbf3a05747efb67ea0313c635e10bda47a2" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 1.0.109", ] [[package]] @@ -564,7 +571,7 @@ dependencies = [ "regex", "rustc-hash", "shlex", - "syn", + "syn 1.0.109", ] [[package]] @@ -1046,7 +1053,7 @@ dependencies = [ "proc-macro-error", "proc-macro2", "quote", - "syn", + "syn 1.0.109", ] [[package]] @@ -1424,7 +1431,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6d2301688392eb071b0bf1a37be05c469d3cc4dbbd95df672fe28ab021e6a096" dependencies = [ "quote", - "syn", + "syn 1.0.109", ] [[package]] @@ -1509,7 +1516,7 @@ dependencies = [ "proc-macro2", "quote", "scratch", - "syn", + "syn 1.0.109", ] [[package]] @@ -1526,7 +1533,7 @@ checksum = "086c685979a698443656e5cf7856c95c642295a38599f12fb1ff76fb28d19892" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 1.0.109", ] [[package]] @@ -1550,7 +1557,7 @@ dependencies = [ "proc-macro2", "quote", "strsim", - "syn", + "syn 1.0.109", ] [[package]] @@ -1561,7 +1568,7 @@ checksum = "b36230598a2d5de7ec1c6f51f72d8a99a9208daff41de2084d06e3fd3ea56685" dependencies = [ "darling_core", "quote", - "syn", + "syn 1.0.109", ] [[package]] @@ -1587,7 +1594,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a5bbed42daaa95e780b60a50546aa345b8413a1e46f9a40a12907d3598f038db" dependencies = [ "data-encoding", - "syn", + "syn 1.0.109", ] [[package]] @@ -1647,7 +1654,7 @@ checksum = "e79116f119dd1dba1abf1f3405f03b9b0e79a27a3883864bfebded8a3dc768cd" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 1.0.109", ] [[package]] @@ -1668,7 +1675,7 @@ dependencies = [ "darling", "proc-macro2", "quote", - "syn", + "syn 1.0.109", ] [[package]] @@ -1678,7 +1685,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8f0314b72bed045f3a68671b3c86328386762c93f82d98c65c3cb5e5f573dd68" dependencies = [ "derive_builder_core", - "syn", + "syn 1.0.109", ] [[package]] @@ -1689,7 +1696,7 @@ checksum = "4fb810d30a7c1953f91334de7244731fc3f3c10d7fe163338a35b9f640960321" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 1.0.109", ] [[package]] @@ -1782,7 +1789,7 @@ checksum = "3bf95dc3f046b9da4f2d51833c0d3547d8564ef6910f5c1ed130306a75b92886" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 1.0.109", ] [[package]] @@ -1833,7 +1840,7 @@ checksum = "558e40ea573c374cf53507fd240b7ee2f5477df7cfebdb97323ec61c719399c5" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 1.0.109", ] [[package]] @@ -1959,7 +1966,7 @@ dependencies = [ "heck", "proc-macro2", "quote", - "syn", + "syn 1.0.109", ] [[package]] @@ -1979,7 +1986,7 @@ checksum = "f58dc3c5e468259f19f2d46304a6b28f1c3d034442e14b322d2b850e36f6d5ae" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 1.0.109", ] [[package]] @@ -2025,6 +2032,17 @@ dependencies = [ "winapi", ] +[[package]] +name = "errno" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "50d6a0976c999d473fe89ad888d5a284e55366d9dc9038b1ba2aa15128c4afa0" +dependencies = [ + "errno-dragonfly", + "libc", + "windows-sys 0.45.0", +] + [[package]] name = "errno-dragonfly" version = "0.1.2" @@ -2060,7 +2078,7 @@ dependencies = [ "fs-err", "proc-macro2", "quote", - "syn", + "syn 1.0.109", ] [[package]] @@ -2137,7 +2155,7 @@ checksum = "8a3de6e8d11b22ff9edc6d916f890800597d60f8b2da1caf2955c274638d6412" dependencies = [ "cfg-if", "libc", - "redox_syscall", + "redox_syscall 0.2.16", "windows-sys 0.45.0", ] @@ -2333,7 +2351,7 @@ dependencies = [ "quote", "scale-info", "sp-arithmetic", - "syn", + "syn 1.0.109", "trybuild", ] @@ -2471,7 +2489,7 @@ dependencies = [ "proc-macro-warning", "proc-macro2", "quote", - "syn", + "syn 1.0.109", ] [[package]] @@ -2482,7 +2500,7 @@ dependencies = [ "proc-macro-crate", "proc-macro2", "quote", - "syn", + "syn 1.0.109", ] [[package]] @@ -2491,7 +2509,7 @@ version = "3.0.0" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 1.0.109", ] [[package]] @@ -2635,9 +2653,9 @@ checksum = "e6d5a32815ae3f33302d95fdcb2ce17862f8c65363dcfd29360480ba1001fc9c" [[package]] name = "futures" -version = "0.3.26" +version = "0.3.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "13e2792b0ff0340399d58445b88fd9770e3489eff258a4cbc1523418f12abf84" +checksum = "23342abe12aba583913b2e62f22225ff9c950774065e4bfb61a19cd9770fec40" dependencies = [ "futures-channel", "futures-core", @@ -2650,9 +2668,9 @@ dependencies = [ [[package]] name = "futures-channel" -version = "0.3.26" +version = "0.3.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2e5317663a9089767a1ec00a487df42e0ca174b61b4483213ac24448e4664df5" +checksum = "955518d47e09b25bbebc7a18df10b81f0c766eaf4c4f1cccef2fca5f2a4fb5f2" dependencies = [ "futures-core", "futures-sink", @@ -2660,15 +2678,15 @@ dependencies = [ [[package]] name = "futures-core" -version = "0.3.26" +version = "0.3.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ec90ff4d0fe1f57d600049061dc6bb68ed03c7d2fbd697274c41805dcb3f8608" +checksum = "4bca583b7e26f571124fe5b7561d49cb2868d79116cfa0eefce955557c6fee8c" [[package]] name = "futures-executor" -version = "0.3.26" +version = "0.3.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e8de0a35a6ab97ec8869e32a2473f4b1324459e14c29275d14b10cb1fd19b50e" +checksum = "ccecee823288125bd88b4d7f565c9e58e41858e47ab72e8ea2d64e93624386e0" dependencies = [ "futures-core", "futures-task", @@ -2678,9 +2696,9 @@ dependencies = [ [[package]] name = "futures-io" -version = "0.3.26" +version = "0.3.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bfb8371b6fb2aeb2d280374607aeabfc99d95c72edfe51692e42d3d7f0d08531" +checksum = "4fff74096e71ed47f8e023204cfd0aa1289cd54ae5430a9523be060cdb849964" [[package]] name = "futures-lite" @@ -2699,13 +2717,13 @@ dependencies = [ [[package]] name = "futures-macro" -version = "0.3.26" +version = "0.3.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "95a73af87da33b5acf53acfebdc339fe592ecf5357ac7c0a7734ab9d8c876a70" +checksum = "89ca545a94061b6365f2c7355b4b32bd20df3ff95f02da9329b34ccc3bd6ee72" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.13", ] [[package]] @@ -2721,15 +2739,15 @@ dependencies = [ [[package]] name = "futures-sink" -version = "0.3.26" +version = "0.3.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f310820bb3e8cfd46c80db4d7fb8353e15dfff853a127158425f31e0be6c8364" +checksum = "f43be4fe21a13b9781a69afa4985b0f6ee0e1afab2c6f454a8cf30e2b2237b6e" [[package]] name = "futures-task" -version = "0.3.26" +version = "0.3.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dcf79a1bf610b10f42aea489289c5a2c478a786509693b80cd39c44ccd936366" +checksum = "76d3d132be6c0e6aa1534069c705a74a5997a356c0dc2f86a47765e5617c5b65" [[package]] name = "futures-timer" @@ -2739,9 +2757,9 @@ checksum = "e64b03909df88034c26dc1547e8970b91f98bdb65165d6a4e9110d94263dbb2c" [[package]] name = "futures-util" -version = "0.3.26" +version = "0.3.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c1d6de3acfef38d2be4b1f543f553131788603495be83da675e180c8d6b7bd1" +checksum = "26b01e40b772d54cf6c6d721c1d1abd0647a0106a12ecaa1c186273392a69533" dependencies = [ "futures-channel", "futures-core", @@ -3291,7 +3309,7 @@ checksum = "11d7a9f6330b71fea57921c9b61c47ee6e84f72d394754eff6163ae67e7395eb" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 1.0.109", ] [[package]] @@ -3515,7 +3533,7 @@ dependencies = [ "proc-macro-crate", "proc-macro2", "quote", - "syn", + "syn 1.0.109", ] [[package]] @@ -4140,7 +4158,7 @@ checksum = "9d527d5827582abd44a6d80c07ff8b50b4ee238a8979e05998474179e79dc400" dependencies = [ "heck", "quote", - "syn", + "syn 1.0.109", ] [[package]] @@ -4375,6 +4393,12 @@ version = "0.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f051f77a7c8e6957c0696eac88f26b0117e54f52d3fc682ab19397a8812846a4" +[[package]] +name = "linux-raw-sys" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d59d8c75012853d2e872fb56bc8a2e53718e2cafe1a4c823143141c6d90c322f" + [[package]] name = "lite-json" version = "0.2.0" @@ -4662,7 +4686,7 @@ dependencies = [ "fragile", "lazy_static", "mockall_derive", - "predicates", + "predicates 2.1.5", "predicates-tree", ] @@ -4675,7 +4699,7 @@ dependencies = [ "cfg-if", "proc-macro2", "quote", - "syn", + "syn 1.0.109", ] [[package]] @@ -4765,7 +4789,7 @@ dependencies = [ "proc-macro-error", "proc-macro2", "quote", - "syn", + "syn 1.0.109", "synstructure", ] @@ -4813,7 +4837,7 @@ checksum = "d232c68884c0c99810a5a4d333ef7e47689cfd0edc85efc9e54e1e6bf5212766" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 1.0.109", ] [[package]] @@ -5030,6 +5054,7 @@ dependencies = [ "sp-transaction-pool", "sp-transaction-storage-proof", "substrate-build-script-utils", + "substrate-cli-test-utils", "substrate-frame-cli", "substrate-rpc-client", "tempfile", @@ -5886,7 +5911,7 @@ version = "4.0.0-dev" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 1.0.109", ] [[package]] @@ -6769,7 +6794,7 @@ dependencies = [ "proc-macro2", "quote", "sp-runtime", - "syn", + "syn 1.0.109", ] [[package]] @@ -7077,7 +7102,7 @@ dependencies = [ "proc-macro-crate", "proc-macro2", "quote", - "syn", + "syn 1.0.109", ] [[package]] @@ -7128,7 +7153,7 @@ dependencies = [ "cfg-if", "instant", "libc", - "redox_syscall", + "redox_syscall 0.2.16", "smallvec", "winapi", ] @@ -7141,7 +7166,7 @@ checksum = "9069cbb9f99e3a5083476ccb29ceb1de18b9118cafa53e90c9551235de2b9521" dependencies = [ "cfg-if", "libc", - "redox_syscall", + "redox_syscall 0.2.16", "smallvec", "windows-sys 0.45.0", ] @@ -7230,7 +7255,7 @@ dependencies = [ "pest_meta", "proc-macro2", "quote", - "syn", + "syn 1.0.109", ] [[package]] @@ -7271,7 +7296,7 @@ checksum = "069bdb1e05adc7a8990dce9cc75370895fbe4e3d58b9b73bf1aee56359344a55" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 1.0.109", ] [[package]] @@ -7427,11 +7452,23 @@ dependencies = [ "regex", ] +[[package]] +name = "predicates" +version = "3.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c575290b64d24745b6c57a12a31465f0a66f3a4799686a6921526a33b0797965" +dependencies = [ + "anstyle", + "difflib", + "itertools", + "predicates-core", +] + [[package]] name = "predicates-core" -version = "1.0.5" +version = "1.0.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "72f883590242d3c6fc5bf50299011695fa6590c2c70eac95ee1bdb9a733ad1a2" +checksum = "b794032607612e7abeb4db69adb4e33590fa6cf1149e95fd7cb00e634b92f174" [[package]] name = "predicates-tree" @@ -7462,7 +7499,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e97e3215779627f01ee256d2fad52f3d95e8e1c11e9fc6fd08f7cd455d5d5c78" dependencies = [ "proc-macro2", - "syn", + "syn 1.0.109", ] [[package]] @@ -7497,7 +7534,7 @@ dependencies = [ "proc-macro-error-attr", "proc-macro2", "quote", - "syn", + "syn 1.0.109", "version_check", ] @@ -7520,14 +7557,14 @@ checksum = "9d4f284d87b9cedc2ff57223cbc4e3937cd6063c01e92c8e2a8c080df0013933" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 1.0.109", ] [[package]] name = "proc-macro2" -version = "1.0.51" +version = "1.0.56" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5d727cae5b39d21da60fa540906919ad737832fe0b1c165da3a34d6548c849d6" +checksum = "2b63bdb0cd06f1f4dedf69b254734f9b45af66e4a031e42a7480257d9898b435" dependencies = [ "unicode-ident", ] @@ -7566,7 +7603,7 @@ checksum = "66a455fbcb954c1a7decf3c586e860fd7889cddf4b8e164be736dbac95a953cd" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 1.0.109", ] [[package]] @@ -7596,7 +7633,7 @@ dependencies = [ "prost", "prost-types", "regex", - "syn", + "syn 1.0.109", "tempfile", "which", ] @@ -7624,7 +7661,7 @@ dependencies = [ "itertools", "proc-macro2", "quote", - "syn", + "syn 1.0.109", ] [[package]] @@ -7691,9 +7728,9 @@ dependencies = [ [[package]] name = "quote" -version = "1.0.23" +version = "1.0.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8856d8364d252a14d474036ea1358d63c9e6965c8e5c1885c18f73d70bff9c7b" +checksum = "4424af4bf778aae2051a77b60283332f386554255d722233d09fbfc7e30da2fc" dependencies = [ "proc-macro2", ] @@ -7856,6 +7893,15 @@ dependencies = [ "bitflags", ] +[[package]] +name = "redox_syscall" +version = "0.3.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "567664f262709473930a4bf9e51bf2ebf3348f2e748ccc50dea20646858f8f29" +dependencies = [ + "bitflags", +] + [[package]] name = "redox_users" version = "0.4.3" @@ -7863,7 +7909,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b033d837a7cf162d7993aded9304e30a83213c648b6e389db233191f891e5c2b" dependencies = [ "getrandom 0.2.8", - "redox_syscall", + "redox_syscall 0.2.16", "thiserror", ] @@ -7884,7 +7930,7 @@ checksum = "9f9c0c92af03644e4806106281fe2e068ac5bc0ae74a707266d06ea27bccee5f" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 1.0.109", ] [[package]] @@ -7901,9 +7947,9 @@ dependencies = [ [[package]] name = "regex" -version = "1.7.1" +version = "1.7.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "48aaa5748ba571fb95cd2c85c09f629215d3a6ece942baa100950af03a34f733" +checksum = "8b1f693b24f6ac912f4893ef08244d70b6067480d2f1a46e950c9691e6749d1d" dependencies = [ "aho-corasick", "memchr", @@ -7921,9 +7967,9 @@ dependencies = [ [[package]] name = "regex-syntax" -version = "0.6.28" +version = "0.6.29" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "456c603be3e8d448b072f410900c09faf164fbce2d480456f50eea6e25f9c848" +checksum = "f162c6dd7b008981e4d40210aca20b4bd0f9b60ca9271061b07f78537722f2e1" [[package]] name = "region" @@ -8106,7 +8152,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "727a1a6d65f786ec22df8a81ca3121107f235970dc1705ed681d3e6e8b9cd5f9" dependencies = [ "bitflags", - "errno", + "errno 0.2.8", "io-lifetimes 0.7.5", "libc", "linux-raw-sys 0.0.46", @@ -8120,13 +8166,27 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f43abb88211988493c1abb44a70efa56ff0ce98f233b7b276146f1f3f7ba9644" dependencies = [ "bitflags", - "errno", + "errno 0.2.8", "io-lifetimes 1.0.5", "libc", "linux-raw-sys 0.1.4", "windows-sys 0.45.0", ] +[[package]] +name = "rustix" +version = "0.37.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2aae838e49b3d63e9274e1c01833cc8139d3fec468c3b84688c628f44b1ae11d" +dependencies = [ + "bitflags", + "errno 0.3.0", + "io-lifetimes 1.0.5", + "libc", + "linux-raw-sys 0.3.1", + "windows-sys 0.45.0", +] + [[package]] name = "rustls" version = "0.19.1" @@ -8340,7 +8400,7 @@ dependencies = [ "proc-macro-crate", "proc-macro2", "quote", - "syn", + "syn 1.0.109", ] [[package]] @@ -9569,7 +9629,7 @@ dependencies = [ "proc-macro-crate", "proc-macro2", "quote", - "syn", + "syn 1.0.109", ] [[package]] @@ -9658,7 +9718,7 @@ dependencies = [ "proc-macro-crate", "proc-macro2", "quote", - "syn", + "syn 1.0.109", ] [[package]] @@ -9871,7 +9931,7 @@ checksum = "af487d118eecd09402d70a5d72551860e788df87b464af30e5ea6a38c75c541e" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 1.0.109", ] [[package]] @@ -10064,9 +10124,9 @@ dependencies = [ [[package]] name = "socket2" -version = "0.4.7" +version = "0.4.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "02e2d2db9033d13a1567121ddd7a095ee144db4e1ca1b1bda3419bc0da294ebd" +checksum = "64a4a911eed85daf18834cfaa86a79b7d266ff93ff5ba14005426219480ed662" dependencies = [ "libc", "winapi", @@ -10117,7 +10177,7 @@ dependencies = [ "proc-macro-crate", "proc-macro2", "quote", - "syn", + "syn 1.0.109", ] [[package]] @@ -10426,7 +10486,7 @@ dependencies = [ "proc-macro2", "quote", "sp-core-hashing", - "syn", + "syn 1.0.109", ] [[package]] @@ -10443,7 +10503,7 @@ version = "5.0.0" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 1.0.109", ] [[package]] @@ -10663,7 +10723,7 @@ dependencies = [ "proc-macro-crate", "proc-macro2", "quote", - "syn", + "syn 1.0.109", ] [[package]] @@ -10887,7 +10947,7 @@ dependencies = [ "proc-macro2", "quote", "sp-version", - "syn", + "syn 1.0.109", ] [[package]] @@ -11001,7 +11061,7 @@ dependencies = [ "memchr", "proc-macro2", "quote", - "syn", + "syn 1.0.109", ] [[package]] @@ -11029,7 +11089,7 @@ dependencies = [ "proc-macro2", "quote", "rustversion", - "syn", + "syn 1.0.109", ] [[package]] @@ -11079,6 +11139,20 @@ dependencies = [ "platforms 2.0.0", ] +[[package]] +name = "substrate-cli-test-utils" +version = "0.1.0" +dependencies = [ + "assert_cmd", + "futures", + "nix 0.26.2", + "node-primitives", + "regex", + "substrate-rpc-client", + "tempfile", + "tokio", +] + [[package]] name = "substrate-frame-cli" version = "4.0.0-dev" @@ -11299,7 +11373,7 @@ dependencies = [ "proc-macro-crate", "proc-macro2", "quote", - "syn", + "syn 1.0.109", ] [[package]] @@ -11353,6 +11427,17 @@ dependencies = [ "unicode-ident", ] +[[package]] +name = "syn" +version = "2.0.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4c9da457c5285ac1f936ebd076af6dac17a61cfe7826f2076b4d015cf47bc8ec" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + [[package]] name = "synstructure" version = "0.12.6" @@ -11361,7 +11446,7 @@ checksum = "f36bdaa60a83aca3921b5259d5400cbf5e90fc51931376a9bd4a0eb79aa7210f" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 1.0.109", "unicode-xid", ] @@ -11400,15 +11485,15 @@ checksum = "8ae9980cab1db3fceee2f6c6f643d5d8de2997c58ee8d25fb0cc8a9e9e7348e5" [[package]] name = "tempfile" -version = "3.4.0" +version = "3.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "af18f7ae1acd354b992402e9ec5864359d693cd8a79dcbef59f76891701c1e95" +checksum = "b9fbec84f381d5795b08656e4912bec604d162bff9291d6189a78f4c8ab87998" dependencies = [ "cfg-if", "fastrand", - "redox_syscall", - "rustix 0.36.8", - "windows-sys 0.42.0", + "redox_syscall 0.3.5", + "rustix 0.37.7", + "windows-sys 0.45.0", ] [[package]] @@ -11449,7 +11534,7 @@ checksum = "1fb327af4685e4d03fa8cbcf1716380da910eeb2bb8be417e7f9fd3fb164f36f" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 1.0.109", ] [[package]] @@ -11580,14 +11665,13 @@ checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" [[package]] name = "tokio" -version = "1.25.0" +version = "1.27.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c8e00990ebabbe4c14c08aca901caed183ecd5c09562a12c824bb53d3c3fd3af" +checksum = "d0de47a4eecbe11f498978a9b29d792f0d2692d1dd003650c24c76510e3bc001" dependencies = [ "autocfg", "bytes", "libc", - "memchr", "mio", "num_cpus", "parking_lot 0.12.1", @@ -11595,18 +11679,18 @@ dependencies = [ "signal-hook-registry", "socket2", "tokio-macros", - "windows-sys 0.42.0", + "windows-sys 0.45.0", ] [[package]] name = "tokio-macros" -version = "1.8.2" +version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d266c00fde287f55d3f1c3e96c500c362a2b8c695076ec180f27918820bc6df8" +checksum = "61a573bdc87985e9d6ddeed1b3d864e8a302c847e40d647746df2f1de209d1ce" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.13", ] [[package]] @@ -11731,7 +11815,7 @@ checksum = "4017f8f45139870ca7e672686113917c71c7a6e02d4924eda67186083c03081a" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 1.0.109", ] [[package]] @@ -11920,6 +12004,7 @@ checksum = "3528ecfd12c466c6f163363caf2d02a71161dd5e1cc6ae7b34207ea2d42d81ed" name = "try-runtime-cli" version = "0.10.0-dev" dependencies = [ + "assert_cmd", "async-trait", "clap 4.1.8", "frame-remote-externalities", @@ -11927,6 +12012,7 @@ dependencies = [ "hex", "log", "parity-scale-codec", + "regex", "sc-cli", "sc-executor", "sc-service", @@ -11948,6 +12034,7 @@ dependencies = [ "sp-transaction-storage-proof", "sp-version", "sp-weights", + "substrate-cli-test-utils", "substrate-rpc-client", "tokio", "zstd", @@ -12229,7 +12316,7 @@ dependencies = [ "once_cell", "proc-macro2", "quote", - "syn", + "syn 1.0.109", "wasm-bindgen-shared", ] @@ -12263,7 +12350,7 @@ checksum = "2aff81306fcac3c7515ad4e177f521b5c9a15f2b08f4e32d823066102f35a5f6" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 1.0.109", "wasm-bindgen-backend", "wasm-bindgen-shared", ] @@ -13205,7 +13292,7 @@ checksum = "44bf07cb3e50ea2003396695d58bf46bc9887a1f362260446fad6bc4e79bd36c" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 1.0.109", "synstructure", ] diff --git a/bin/node/cli/Cargo.toml b/bin/node/cli/Cargo.toml index 4451935c3..77e49ff07 100644 --- a/bin/node/cli/Cargo.toml +++ b/bin/node/cli/Cargo.toml @@ -127,6 +127,7 @@ tokio-util = { version = "0.7.4", features = ["compat"] } wait-timeout = "0.2" substrate-rpc-client = { path = "../../../utils/frame/rpc/client" } pallet-timestamp = { version = "4.0.0-dev", path = "../../../frame/timestamp" } +substrate-cli-test-utils = { path = "../../../test-utils/cli" } [build-dependencies] clap = { version = "4.0.9", optional = true } diff --git a/bin/node/cli/tests/benchmark_block_works.rs b/bin/node/cli/tests/benchmark_block_works.rs index 369e1b3e5..50103a66a 100644 --- a/bin/node/cli/tests/benchmark_block_works.rs +++ b/bin/node/cli/tests/benchmark_block_works.rs @@ -23,7 +23,7 @@ use assert_cmd::cargo::cargo_bin; use std::process::Command; use tempfile::tempdir; -pub mod common; +use substrate_cli_test_utils as common; /// `benchmark block` works for the dev runtime using the wasm executor. #[tokio::test] diff --git a/bin/node/cli/tests/benchmark_pallet_works.rs b/bin/node/cli/tests/benchmark_pallet_works.rs index 053516b95..2d9946543 100644 --- a/bin/node/cli/tests/benchmark_pallet_works.rs +++ b/bin/node/cli/tests/benchmark_pallet_works.rs @@ -21,8 +21,6 @@ use assert_cmd::cargo::cargo_bin; use std::process::Command; -pub mod common; - /// `benchmark pallet` works for the different combinations of `steps` and `repeat`. #[test] fn benchmark_pallet_works() { diff --git a/bin/node/cli/tests/check_block_works.rs b/bin/node/cli/tests/check_block_works.rs index 019d97aa8..67bc5e603 100644 --- a/bin/node/cli/tests/check_block_works.rs +++ b/bin/node/cli/tests/check_block_works.rs @@ -22,7 +22,7 @@ use assert_cmd::cargo::cargo_bin; use std::process::Command; use tempfile::tempdir; -pub mod common; +use substrate_cli_test_utils as common; #[tokio::test] async fn check_block_works() { diff --git a/bin/node/cli/tests/export_import_flow.rs b/bin/node/cli/tests/export_import_flow.rs index afee9ef70..b5785f99e 100644 --- a/bin/node/cli/tests/export_import_flow.rs +++ b/bin/node/cli/tests/export_import_flow.rs @@ -23,7 +23,7 @@ use regex::Regex; use std::{fs, path::PathBuf, process::Command}; use tempfile::{tempdir, TempDir}; -pub mod common; +use substrate_cli_test_utils as common; fn contains_error(logged_output: &str) -> bool { logged_output.contains("Error") diff --git a/bin/node/cli/tests/inspect_works.rs b/bin/node/cli/tests/inspect_works.rs index 56569248b..3695c318a 100644 --- a/bin/node/cli/tests/inspect_works.rs +++ b/bin/node/cli/tests/inspect_works.rs @@ -22,7 +22,7 @@ use assert_cmd::cargo::cargo_bin; use std::process::Command; use tempfile::tempdir; -pub mod common; +use substrate_cli_test_utils as common; #[tokio::test] async fn inspect_works() { diff --git a/bin/node/cli/tests/purge_chain_works.rs b/bin/node/cli/tests/purge_chain_works.rs index 470c0a7e4..77421f865 100644 --- a/bin/node/cli/tests/purge_chain_works.rs +++ b/bin/node/cli/tests/purge_chain_works.rs @@ -20,7 +20,7 @@ use assert_cmd::cargo::cargo_bin; use std::process::Command; use tempfile::tempdir; -pub mod common; +use substrate_cli_test_utils as common; #[tokio::test] #[cfg(unix)] diff --git a/bin/node/cli/tests/remember_state_pruning_works.rs b/bin/node/cli/tests/remember_state_pruning_works.rs index b8c5ddf4a..e28b2ef55 100644 --- a/bin/node/cli/tests/remember_state_pruning_works.rs +++ b/bin/node/cli/tests/remember_state_pruning_works.rs @@ -18,7 +18,7 @@ use tempfile::tempdir; -pub mod common; +use substrate_cli_test_utils as common; #[tokio::test] #[cfg(unix)] diff --git a/bin/node/cli/tests/running_the_node_and_interrupt.rs b/bin/node/cli/tests/running_the_node_and_interrupt.rs index 3d5598f3f..1308067da 100644 --- a/bin/node/cli/tests/running_the_node_and_interrupt.rs +++ b/bin/node/cli/tests/running_the_node_and_interrupt.rs @@ -20,12 +20,12 @@ use assert_cmd::cargo::cargo_bin; use nix::sys::signal::Signal::{self, SIGINT, SIGTERM}; use std::{ - process::{self, Child, Command}, + process::{self, Command}, time::Duration, }; use tempfile::tempdir; -pub mod common; +use substrate_cli_test_utils as common; #[tokio::test] async fn running_the_node_works_and_can_be_interrupted() { @@ -71,17 +71,8 @@ async fn running_the_node_works_and_can_be_interrupted() { #[tokio::test] async fn running_two_nodes_with_the_same_ws_port_should_work() { common::run_with_timeout(Duration::from_secs(60 * 10), async move { - fn start_node() -> Child { - Command::new(cargo_bin("substrate")) - .stdout(process::Stdio::piped()) - .stderr(process::Stdio::piped()) - .args(&["--dev", "--tmp", "--ws-port=45789", "--no-hardware-benchmarks"]) - .spawn() - .unwrap() - } - - let mut first_node = common::KillChildOnDrop(start_node()); - let mut second_node = common::KillChildOnDrop(start_node()); + let mut first_node = common::KillChildOnDrop(common::start_node()); + let mut second_node = common::KillChildOnDrop(common::start_node()); let stderr = first_node.stderr.take().unwrap(); let ws_url = common::extract_info_from_output(stderr).0.ws_url; diff --git a/bin/node/cli/tests/telemetry.rs b/bin/node/cli/tests/telemetry.rs index a68746a2c..f6e46775a 100644 --- a/bin/node/cli/tests/telemetry.rs +++ b/bin/node/cli/tests/telemetry.rs @@ -21,7 +21,7 @@ use std::{process, time::Duration}; use crate::common::KillChildOnDrop; -pub mod common; +use substrate_cli_test_utils as common; pub mod websocket_server; #[tokio::test] diff --git a/bin/node/cli/tests/temp_base_path_works.rs b/bin/node/cli/tests/temp_base_path_works.rs index bfc9f8822..fdcd9e23d 100644 --- a/bin/node/cli/tests/temp_base_path_works.rs +++ b/bin/node/cli/tests/temp_base_path_works.rs @@ -24,7 +24,7 @@ use std::{ time::Duration, }; -pub mod common; +use substrate_cli_test_utils as common; #[allow(dead_code)] // Apparently `#[ignore]` doesn't actually work to disable this one. diff --git a/frame/support/src/lib.rs b/frame/support/src/lib.rs index 916a8696e..ac897fef0 100644 --- a/frame/support/src/lib.rs +++ b/frame/support/src/lib.rs @@ -371,16 +371,14 @@ macro_rules! parameter_types { /// Set the value of this parameter type in the storage. /// - /// This needs to be executed in an externalities provided - /// environment. + /// This needs to be executed in an externalities provided environment. pub fn set(value: &$type) { $crate::storage::unhashed::put(&Self::key(), value); } /// Returns the value of this parameter type. /// - /// This needs to be executed in an externalities provided - /// environment. + /// This needs to be executed in an externalities provided environment. #[allow(unused)] pub fn get() -> $type { $crate::storage::unhashed::get(&Self::key()).unwrap_or_else(|| $value) diff --git a/frame/support/src/traits/hooks.rs b/frame/support/src/traits/hooks.rs index 2d346ccd3..5fea98ee0 100644 --- a/frame/support/src/traits/hooks.rs +++ b/frame/support/src/traits/hooks.rs @@ -197,10 +197,10 @@ impl OnRuntimeUpgrade for Tuple { weight } - #[cfg(feature = "try-runtime")] /// We are executing pre- and post-checks sequentially in order to be able to test several /// consecutive migrations for the same pallet without errors. Therefore pre and post upgrade /// hooks for tuples are a noop. + #[cfg(feature = "try-runtime")] fn try_on_runtime_upgrade(checks: bool) -> Result { let mut weight = Weight::zero(); for_tuples!( #( weight = weight.saturating_add(Tuple::try_on_runtime_upgrade(checks)?); )* ); @@ -364,10 +364,64 @@ pub trait OnTimestampSet { #[cfg(test)] mod tests { use super::*; + use sp_io::TestExternalities; + + #[cfg(feature = "try-runtime")] + #[test] + fn on_runtime_upgrade_pre_post_executed_tuple() { + crate::parameter_types! { + pub static Pre: Vec<&'static str> = Default::default(); + pub static Post: Vec<&'static str> = Default::default(); + } + + macro_rules! impl_test_type { + ($name:ident) => { + struct $name; + impl OnRuntimeUpgrade for $name { + fn on_runtime_upgrade() -> Weight { + Default::default() + } + + #[cfg(feature = "try-runtime")] + fn pre_upgrade() -> Result, &'static str> { + Pre::mutate(|s| s.push(stringify!($name))); + Ok(Vec::new()) + } + + #[cfg(feature = "try-runtime")] + fn post_upgrade(_: Vec) -> Result<(), &'static str> { + Post::mutate(|s| s.push(stringify!($name))); + Ok(()) + } + } + }; + } + + impl_test_type!(Foo); + impl_test_type!(Bar); + impl_test_type!(Baz); + + TestExternalities::default().execute_with(|| { + Foo::try_on_runtime_upgrade(true).unwrap(); + assert_eq!(Pre::take(), vec!["Foo"]); + assert_eq!(Post::take(), vec!["Foo"]); + + <(Foo, Bar, Baz)>::try_on_runtime_upgrade(true).unwrap(); + assert_eq!(Pre::take(), vec!["Foo", "Bar", "Baz"]); + assert_eq!(Post::take(), vec!["Foo", "Bar", "Baz"]); + + <((Foo, Bar), Baz)>::try_on_runtime_upgrade(true).unwrap(); + assert_eq!(Pre::take(), vec!["Foo", "Bar", "Baz"]); + assert_eq!(Post::take(), vec!["Foo", "Bar", "Baz"]); + + <(Foo, (Bar, Baz))>::try_on_runtime_upgrade(true).unwrap(); + assert_eq!(Pre::take(), vec!["Foo", "Bar", "Baz"]); + assert_eq!(Post::take(), vec!["Foo", "Bar", "Baz"]); + }); + } #[test] fn on_initialize_and_on_runtime_upgrade_weight_merge_works() { - use sp_io::TestExternalities; struct Test; impl OnInitialize for Test { diff --git a/test-utils/cli/Cargo.toml b/test-utils/cli/Cargo.toml new file mode 100644 index 000000000..cc05884a6 --- /dev/null +++ b/test-utils/cli/Cargo.toml @@ -0,0 +1,23 @@ +[package] +name = "substrate-cli-test-utils" +description = "CLI testing utilities" +version = "0.1.0" +authors = ["Parity Technologies "] +edition = "2021" +license = "Apache-2.0" +homepage = "https://substrate.io" +repository = "https://github.com/paritytech/substrate/" +publish = false + +[package.metadata.docs.rs] +targets = ["x86_64-unknown-linux-gnu"] + +[dependencies] +substrate-rpc-client = { path = "../../utils/frame/rpc/client" } +assert_cmd = "2.0.10" +nix = "0.26.2" +regex = "1.7.3" +tempfile = "3.5.0" +tokio = { version = "1.22.0", features = ["full"] } +node-primitives = { path = "../../bin/node/primitives" } +futures = "0.3.28" diff --git a/bin/node/cli/tests/common.rs b/test-utils/cli/src/lib.rs similarity index 52% rename from bin/node/cli/tests/common.rs rename to test-utils/cli/src/lib.rs index 4c4824391..1846f1909 100644 --- a/bin/node/cli/tests/common.rs +++ b/test-utils/cli/src/lib.rs @@ -26,12 +26,146 @@ use nix::{ use node_primitives::{Hash, Header}; use regex::Regex; use std::{ + env, io::{BufRead, BufReader, Read}, ops::{Deref, DerefMut}, path::{Path, PathBuf}, process::{self, Child, Command}, time::Duration, }; +use tokio::io::{AsyncBufReadExt, AsyncRead}; + +/// Starts a new Substrate node in development mode with a temporary chain. +/// +/// This function creates a new Substrate node using the `substrate` binary. +/// It configures the node to run in development mode (`--dev`) with a temporary chain (`--tmp`), +/// sets the WebSocket port to 45789 (`--ws-port=45789`). +/// +/// # Returns +/// +/// A [`Child`] process representing the spawned Substrate node. +/// +/// # Panics +/// +/// This function will panic if the `substrate` binary is not found or if the node fails to start. +/// +/// # Examples +/// +/// ```ignore +/// use my_crate::start_node; +/// +/// let child = start_node(); +/// // Interact with the Substrate node using the WebSocket port 45789. +/// // When done, the node will be killed when the `child` is dropped. +/// ``` +/// +/// [`Child`]: std::process::Child +pub fn start_node() -> Child { + Command::new(cargo_bin("substrate")) + .stdout(process::Stdio::piped()) + .stderr(process::Stdio::piped()) + .args(&["--dev", "--tmp", "--ws-port=45789", "--no-hardware-benchmarks"]) + .spawn() + .unwrap() +} + +/// Builds the Substrate project using the provided arguments. +/// +/// This function reads the CARGO_MANIFEST_DIR environment variable to find the root workspace +/// directory. It then runs the `cargo b` command in the root directory with the specified +/// arguments. +/// +/// This can be useful for building the Substrate binary with a desired set of features prior +/// to using the binary in a CLI test. +/// +/// # Arguments +/// +/// * `args: &[&str]` - A slice of string references representing the arguments to pass to the +/// `cargo b` command. +/// +/// # Panics +/// +/// This function will panic if: +/// +/// * The CARGO_MANIFEST_DIR environment variable is not set. +/// * The root workspace directory cannot be determined. +/// * The 'cargo b' command fails to execute. +/// * The 'cargo b' command returns a non-successful status. +/// +/// # Examples +/// +/// ```ignore +/// build_substrate(&["--features=try-runtime"]); +/// ``` +pub fn build_substrate(args: &[&str]) { + // Get the root workspace directory from the CARGO_MANIFEST_DIR environment variable + let manifest_dir = env::var("CARGO_MANIFEST_DIR").expect("CARGO_MANIFEST_DIR not set"); + let root_dir = std::path::Path::new(&manifest_dir) + .parent() + .expect("Failed to find root workspace directory"); + let output = Command::new("cargo") + .arg("build") + .args(args) + .current_dir(root_dir) + .output() + .expect(format!("Failed to execute 'cargo b' with args {:?}'", args).as_str()); + + if !output.status.success() { + panic!( + "Failed to execute 'cargo b' with args {:?}': \n{}", + args, + String::from_utf8_lossy(&output.stderr) + ); + } +} + +/// Takes a readable tokio stream (e.g. from a child process `ChildStderr` or `ChildStdout`) and +/// a `Regex` pattern, and checks each line against the given pattern as it is produced. +/// The function returns OK(()) as soon as a line matching the pattern is found, or an Err if +/// the stream ends without any lines matching the pattern. +/// +/// # Arguments +/// +/// * `child_stream` - An async tokio stream, e.g. from a child process `ChildStderr` or +/// `ChildStdout`. +/// * `re` - A `Regex` pattern to search for in the stream. +/// +/// # Returns +/// +/// * `Ok(())` if a line matching the pattern is found. +/// * `Err(String)` if the stream ends without any lines matching the pattern. +/// +/// # Examples +/// +/// ```ignore +/// use regex::Regex; +/// use tokio::process::Command; +/// use tokio::io::AsyncRead; +/// +/// # async fn run() { +/// let child = Command::new("some-command").stderr(std::process::Stdio::piped()).spawn().unwrap(); +/// let stderr = child.stderr.unwrap(); +/// let re = Regex::new("error:").unwrap(); +/// +/// match wait_for_pattern_match_in_stream(stderr, re).await { +/// Ok(()) => println!("Error found in stderr"), +/// Err(e) => println!("Error: {}", e), +/// } +/// # } +/// ``` +pub async fn wait_for_stream_pattern_match(stream: R, re: Regex) -> Result<(), String> +where + R: AsyncRead + Unpin, +{ + let mut stdio_reader = tokio::io::BufReader::new(stream).lines(); + while let Ok(Some(line)) = stdio_reader.next_line().await { + match re.find(line.as_str()) { + Some(_) => return Ok(()), + None => (), + } + } + Err(String::from("Stream closed without any lines matching the regex.")) +} /// Run the given `future` and panic if the `timeout` is hit. pub async fn run_with_timeout(timeout: Duration, future: impl futures::Future) { diff --git a/utils/frame/try-runtime/cli/Cargo.toml b/utils/frame/try-runtime/cli/Cargo.toml index a22028954..a4d24986e 100644 --- a/utils/frame/try-runtime/cli/Cargo.toml +++ b/utils/frame/try-runtime/cli/Cargo.toml @@ -45,10 +45,10 @@ serde_json = "1.0.85" zstd = { version = "0.11.2", default-features = false } [dev-dependencies] -tokio = "1.22.0" +assert_cmd = "2.0.10" +regex = "1.7.3" +substrate-cli-test-utils = { path = "../../../../test-utils/cli" } +tokio = "1.27.0" [features] -try-runtime = [ - "sp-debug-derive/force-debug", - "frame-try-runtime/try-runtime", -] +try-runtime = ["sp-debug-derive/force-debug", "frame-try-runtime/try-runtime"] diff --git a/utils/frame/try-runtime/cli/src/commands/follow_chain.rs b/utils/frame/try-runtime/cli/src/commands/follow_chain.rs index 413c68550..d5c68978e 100644 --- a/utils/frame/try-runtime/cli/src/commands/follow_chain.rs +++ b/utils/frame/try-runtime/cli/src/commands/follow_chain.rs @@ -108,10 +108,12 @@ where .or_else(|e| { if matches!(e, substrate_rpc_client::Error::ParseError(_)) { log::error!( + target: LOG_TARGET, "failed to parse the block format of remote against the local \ - codebase. The block format has changed, and follow-chain cannot run in \ - this case. Try running this command in a branch of your codebase that has \ - the same block format as the remote chain. For now, we replace the block with an empty one" + codebase. The block format has changed, and follow-chain cannot run in \ + this case. Try running this command in a branch of your codebase that + has the same block format as the remote chain. For now, we replace the \ + block with an empty one." ); } Err(rpc_err_handler(e)) @@ -148,7 +150,9 @@ where state_ext, &executor, "TryRuntime_execute_block", - (block, command.state_root_check, command.try_state.clone()).encode().as_ref(), + (block, command.state_root_check, true, command.try_state.clone()) + .encode() + .as_ref(), full_extensions(executor.clone()), shared .export_proof diff --git a/utils/frame/try-runtime/cli/tests/follow_chain.rs b/utils/frame/try-runtime/cli/tests/follow_chain.rs new file mode 100644 index 000000000..a4961aa28 --- /dev/null +++ b/utils/frame/try-runtime/cli/tests/follow_chain.rs @@ -0,0 +1,65 @@ +// This file is part of Substrate. + +// Copyright (C) Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +#![cfg(unix)] + +#[cfg(feature = "try-runtime")] +mod tests { + use assert_cmd::cargo::cargo_bin; + use regex::Regex; + use std::{ + process::{self}, + time::Duration, + }; + use substrate_cli_test_utils as common; + use tokio::process::{Child, Command}; + + #[tokio::test] + async fn follow_chain_works() { + // Build substrate so binaries used in the test use the latest code. + common::build_substrate(&["--features=try-runtime"]); + + common::run_with_timeout(Duration::from_secs(60), async move { + fn start_follow(ws_url: &str) -> Child { + Command::new(cargo_bin("substrate")) + .stdout(process::Stdio::piped()) + .stderr(process::Stdio::piped()) + .args(&["try-runtime", "--runtime=existing"]) + .args(&["follow-chain", format!("--uri={}", ws_url).as_str()]) + .kill_on_drop(true) + .spawn() + .unwrap() + } + + // Start a node and wait for it to begin finalizing blocks + let mut node = common::KillChildOnDrop(common::start_node()); + let ws_url = common::extract_info_from_output(node.stderr.take().unwrap()).0.ws_url; + common::wait_n_finalized_blocks(1, &ws_url).await; + + // Kick off the follow-chain process and wait for it to process at least 3 blocks. + let mut follow = start_follow(&ws_url); + let re = Regex::new(r#".*executed block ([3-9]|[1-9]\d+).*"#).unwrap(); + let matched = + common::wait_for_stream_pattern_match(follow.stderr.take().unwrap(), re).await; + + // Assert that the follow-chain process has followed at least 3 blocks. + assert!(matches!(matched, Ok(_))); + }) + .await; + } +} From 9e1fa3db3547df432acd42aa6e6419b030212edc Mon Sep 17 00:00:00 2001 From: alexd10s Date: Thu, 6 Apr 2023 12:27:43 +0100 Subject: [PATCH 346/558] Use proper weights in the `pallet-template` (#13817) * benchmarking to generate weights file * add the calculated weights in the extrinsics * use benchmarking v2 syntax to generate the weights * minor syntax change when benchmarking * added WeightInfo in the mock to pass tests * minor cargo fmt format changes --- Cargo.lock | 1 + bin/node-template/pallets/template/Cargo.toml | 1 + .../pallets/template/src/benchmarking.rs | 31 +++++-- bin/node-template/pallets/template/src/lib.rs | 9 +- .../pallets/template/src/mock.rs | 1 + .../pallets/template/src/weights.rs | 91 +++++++++++++++++++ bin/node-template/runtime/src/lib.rs | 1 + 7 files changed, 125 insertions(+), 10 deletions(-) create mode 100644 bin/node-template/pallets/template/src/weights.rs diff --git a/Cargo.lock b/Cargo.lock index d4185103f..525f40ced 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -6864,6 +6864,7 @@ dependencies = [ "sp-core", "sp-io", "sp-runtime", + "sp-std", ] [[package]] diff --git a/bin/node-template/pallets/template/Cargo.toml b/bin/node-template/pallets/template/Cargo.toml index ddf7c6e52..818dc0bbc 100644 --- a/bin/node-template/pallets/template/Cargo.toml +++ b/bin/node-template/pallets/template/Cargo.toml @@ -20,6 +20,7 @@ scale-info = { version = "2.1.1", default-features = false, features = ["derive" frame-benchmarking = { version = "4.0.0-dev", default-features = false, optional = true, path = "../../../../frame/benchmarking" } frame-support = { version = "4.0.0-dev", default-features = false, path = "../../../../frame/support" } frame-system = { version = "4.0.0-dev", default-features = false, path = "../../../../frame/system" } +sp-std = { version = "5.0.0", default-features = false, path = "../../../../primitives/std" } [dev-dependencies] sp-core = { version = "7.0.0", path = "../../../../primitives/core" } diff --git a/bin/node-template/pallets/template/src/benchmarking.rs b/bin/node-template/pallets/template/src/benchmarking.rs index 179084997..5a2624176 100644 --- a/bin/node-template/pallets/template/src/benchmarking.rs +++ b/bin/node-template/pallets/template/src/benchmarking.rs @@ -1,19 +1,34 @@ //! Benchmarking setup for pallet-template - +#![cfg(feature = "runtime-benchmarks")] use super::*; #[allow(unused)] use crate::Pallet as Template; -use frame_benchmarking::v1::{benchmarks, whitelisted_caller}; +use frame_benchmarking::v2::*; use frame_system::RawOrigin; -benchmarks! { - do_something { - let s in 0 .. 100; +#[benchmarks] +mod benchmarks { + use super::*; + + #[benchmark] + fn do_something() { + let value = 100u32.into(); let caller: T::AccountId = whitelisted_caller(); - }: _(RawOrigin::Signed(caller), s) - verify { - assert_eq!(Something::::get(), Some(s)); + #[extrinsic_call] + do_something(RawOrigin::Signed(caller), value); + + assert_eq!(Something::::get(), Some(value)); + } + + #[benchmark] + fn cause_error() { + Something::::put(100u32); + let caller: T::AccountId = whitelisted_caller(); + #[extrinsic_call] + cause_error(RawOrigin::Signed(caller)); + + assert_eq!(Something::::get(), Some(101u32)); } impl_benchmark_test_suite!(Template, crate::mock::new_test_ext(), crate::mock::Test); diff --git a/bin/node-template/pallets/template/src/lib.rs b/bin/node-template/pallets/template/src/lib.rs index 9f17623db..9550d3d54 100644 --- a/bin/node-template/pallets/template/src/lib.rs +++ b/bin/node-template/pallets/template/src/lib.rs @@ -13,9 +13,12 @@ mod tests; #[cfg(feature = "runtime-benchmarks")] mod benchmarking; +pub mod weights; +pub use weights::*; #[frame_support::pallet] pub mod pallet { + use super::*; use frame_support::pallet_prelude::*; use frame_system::pallet_prelude::*; @@ -27,6 +30,8 @@ pub mod pallet { pub trait Config: frame_system::Config { /// Because this pallet emits events, it depends on the runtime's definition of an event. type RuntimeEvent: From> + IsType<::RuntimeEvent>; + /// Type representing the weight of this pallet + type WeightInfo: WeightInfo; } // The pallet's runtime storage items. @@ -64,7 +69,7 @@ pub mod pallet { /// An example dispatchable that takes a singles value as a parameter, writes the value to /// storage and emits an event. This function must be dispatched by a signed extrinsic. #[pallet::call_index(0)] - #[pallet::weight(10_000 + T::DbWeight::get().writes(1).ref_time())] + #[pallet::weight(T::WeightInfo::do_something())] pub fn do_something(origin: OriginFor, something: u32) -> DispatchResult { // Check that the extrinsic was signed and get the signer. // This function will return an error if the extrinsic is not signed. @@ -82,7 +87,7 @@ pub mod pallet { /// An example dispatchable that may throw a custom error. #[pallet::call_index(1)] - #[pallet::weight(10_000 + T::DbWeight::get().reads_writes(1,1).ref_time())] + #[pallet::weight(T::WeightInfo::cause_error())] pub fn cause_error(origin: OriginFor) -> DispatchResult { let _who = ensure_signed(origin)?; diff --git a/bin/node-template/pallets/template/src/mock.rs b/bin/node-template/pallets/template/src/mock.rs index 91a1bf6ed..b4d690537 100644 --- a/bin/node-template/pallets/template/src/mock.rs +++ b/bin/node-template/pallets/template/src/mock.rs @@ -50,6 +50,7 @@ impl frame_system::Config for Test { impl pallet_template::Config for Test { type RuntimeEvent = RuntimeEvent; + type WeightInfo = (); } // Build genesis storage according to the mock runtime. diff --git a/bin/node-template/pallets/template/src/weights.rs b/bin/node-template/pallets/template/src/weights.rs new file mode 100644 index 000000000..952aba49b --- /dev/null +++ b/bin/node-template/pallets/template/src/weights.rs @@ -0,0 +1,91 @@ + +//! Autogenerated weights for pallet_template +//! +//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev +//! DATE: 2023-04-05, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! WORST CASE MAP SIZE: `1000000` +//! HOSTNAME: `Alexs-MacBook-Pro-2.local`, CPU: `` +//! EXECUTION: Some(Wasm), WASM-EXECUTION: Compiled, CHAIN: Some("dev"), DB CACHE: 1024 + +// Executed Command: +// ../../target/release/node-template +// benchmark +// pallet +// --chain +// dev +// --pallet +// pallet_template +// --extrinsic +// * +// --steps=50 +// --repeat=20 +// --execution=wasm +// --wasm-execution=compiled +// --output +// pallets/template/src/weights.rs +// --template +// ../../.maintain/frame-weight-template.hbs + +#![cfg_attr(rustfmt, rustfmt_skip)] +#![allow(unused_parens)] +#![allow(unused_imports)] + +use frame_support::{traits::Get, weights::{Weight, constants::RocksDbWeight}}; +use sp_std::marker::PhantomData; + +/// Weight functions needed for pallet_template. +pub trait WeightInfo { + fn do_something() -> Weight; + fn cause_error() -> Weight; +} + +/// Weights for pallet_template using the Substrate node and recommended hardware. +pub struct SubstrateWeight(PhantomData); +impl WeightInfo for SubstrateWeight { + /// Storage: TemplateModule Something (r:0 w:1) + /// Proof: TemplateModule Something (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) + fn do_something() -> Weight { + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 8_000_000 picoseconds. + Weight::from_parts(8_000_000, 0) + .saturating_add(T::DbWeight::get().writes(1_u64)) + } + /// Storage: TemplateModule Something (r:1 w:1) + /// Proof: TemplateModule Something (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) + fn cause_error() -> Weight { + // Proof Size summary in bytes: + // Measured: `32` + // Estimated: `1489` + // Minimum execution time: 5_000_000 picoseconds. + Weight::from_parts(6_000_000, 1489) + .saturating_add(T::DbWeight::get().reads(1_u64)) + .saturating_add(T::DbWeight::get().writes(1_u64)) + } +} + +// For backwards compatibility and tests +impl WeightInfo for () { + /// Storage: TemplateModule Something (r:0 w:1) + /// Proof: TemplateModule Something (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) + fn do_something() -> Weight { + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 8_000_000 picoseconds. + Weight::from_parts(8_000_000, 0) + .saturating_add(RocksDbWeight::get().writes(1_u64)) + } + /// Storage: TemplateModule Something (r:1 w:1) + /// Proof: TemplateModule Something (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) + fn cause_error() -> Weight { + // Proof Size summary in bytes: + // Measured: `32` + // Estimated: `1489` + // Minimum execution time: 5_000_000 picoseconds. + Weight::from_parts(6_000_000, 1489) + .saturating_add(RocksDbWeight::get().reads(1_u64)) + .saturating_add(RocksDbWeight::get().writes(1_u64)) + } +} diff --git a/bin/node-template/runtime/src/lib.rs b/bin/node-template/runtime/src/lib.rs index 50bcb67cb..3dcf1b556 100644 --- a/bin/node-template/runtime/src/lib.rs +++ b/bin/node-template/runtime/src/lib.rs @@ -270,6 +270,7 @@ impl pallet_sudo::Config for Runtime { /// Configure the pallet-template in pallets/template. impl pallet_template::Config for Runtime { type RuntimeEvent = RuntimeEvent; + type WeightInfo = pallet_template::weights::SubstrateWeight; } // Create the runtime by composing the FRAME pallets that were previously configured. From 9a07e9d564636198afef9260de390ebcd40ac1fc Mon Sep 17 00:00:00 2001 From: Liam Aharon Date: Fri, 7 Apr 2023 00:27:25 +1000 Subject: [PATCH 347/558] `RemovePallet` migration utility struct (#13835) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * remove pallet utility struct * Update frame/support/src/remove_pallet.rs Co-authored-by: Bastian Köcher * Update frame/support/src/remove_pallet.rs Co-authored-by: Bastian Köcher * make removepallet a module of migrations * fix rust doc lint --------- Co-authored-by: Bastian Köcher --- frame/support/src/migrations.rs | 116 ++++++++++++++++++++++++++++++++ 1 file changed, 116 insertions(+) diff --git a/frame/support/src/migrations.rs b/frame/support/src/migrations.rs index 645316042..f54b73aad 100644 --- a/frame/support/src/migrations.rs +++ b/frame/support/src/migrations.rs @@ -15,11 +15,18 @@ // See the License for the specific language governing permissions and // limitations under the License. +#[cfg(feature = "try-runtime")] +use crate::storage::unhashed::contains_prefixed_key; use crate::{ traits::{GetStorageVersion, PalletInfoAccess}, weights::{RuntimeDbWeight, Weight}, }; use impl_trait_for_tuples::impl_for_tuples; +use sp_core::Get; +use sp_io::{hashing::twox_128, storage::clear_prefix, KillStorageResult}; +use sp_std::marker::PhantomData; +#[cfg(feature = "try-runtime")] +use sp_std::vec::Vec; /// Trait used by [`migrate_from_pallet_version_to_storage_version`] to do the actual migration. pub trait PalletVersionToStorageVersionHelper { @@ -67,3 +74,112 @@ pub fn migrate_from_pallet_version_to_storage_version< ) -> Weight { Pallets::migrate(db_weight) } + +/// `RemovePallet` is a utility struct used to remove all storage items associated with a specific +/// pallet. +/// +/// This struct is generic over two parameters: +/// - `P` is a type that implements the `Get` trait for a static string, representing the pallet's +/// name. +/// - `DbWeight` is a type that implements the `Get` trait for `RuntimeDbWeight`, providing the +/// weight for database operations. +/// +/// On runtime upgrade, the `on_runtime_upgrade` function will clear all storage items associated +/// with the specified pallet, logging the number of keys removed. If the `try-runtime` feature is +/// enabled, the `pre_upgrade` and `post_upgrade` functions can be used to verify the storage +/// removal before and after the upgrade. +/// +/// # Examples: +/// ```ignore +/// construct_runtime! { +/// pub enum Runtime where +/// Block = Block, +/// NodeBlock = primitives::Block, +/// UncheckedExtrinsic = UncheckedExtrinsic +/// { +/// System: frame_system::{Pallet, Call, Storage, Config, Event} = 0, +/// +/// SomePalletToRemove: pallet_something::{Pallet, Call, Storage, Event} = 1, +/// AnotherPalletToRemove: pallet_something_else::{Pallet, Call, Storage, Event} = 2, +/// +/// YourOtherPallets... +/// } +/// }; +/// +/// parameter_types! { +/// pub const SomePalletToRemoveStr: &'static str = "SomePalletToRemove"; +/// pub const AnotherPalletToRemoveStr: &'static str = "AnotherPalletToRemove"; +/// } +/// +/// pub type Migrations = ( +/// RemovePallet, +/// RemovePallet, +/// AnyOtherMigrations... +/// ); +/// +/// pub type Executive = frame_executive::Executive< +/// Runtime, +/// Block, +/// frame_system::ChainContext, +/// Runtime, +/// Migrations +/// >; +/// ``` +/// +/// WARNING: `RemovePallet` has no guard rails preventing it from bricking the chain if the +/// operation of removing storage for the given pallet would exceed the block weight limit. +/// +/// If your pallet has too many keys to be removed in a single block, it is advised to wait for +/// a multi-block scheduler currently under development which will allow for removal of storage +/// items (and performing other heavy migrations) over multiple blocks +/// (see ). +pub struct RemovePallet, DbWeight: Get>( + PhantomData<(P, DbWeight)>, +); +impl, DbWeight: Get> frame_support::traits::OnRuntimeUpgrade + for RemovePallet +{ + fn on_runtime_upgrade() -> frame_support::weights::Weight { + let hashed_prefix = twox_128(P::get().as_bytes()); + let keys_removed = match clear_prefix(&hashed_prefix, None) { + KillStorageResult::AllRemoved(value) => value, + KillStorageResult::SomeRemaining(value) => { + log::error!( + "`clear_prefix` failed to remove all keys for {}. THIS SHOULD NEVER HAPPEN! 🚨", + P::get() + ); + value + }, + } as u64; + + log::info!("Removed {} {} keys 🧹", keys_removed, P::get()); + + DbWeight::get().reads_writes(keys_removed + 1, keys_removed) + } + + #[cfg(feature = "try-runtime")] + fn pre_upgrade() -> Result, &'static str> { + let hashed_prefix = twox_128(P::get().as_bytes()); + match contains_prefixed_key(&hashed_prefix) { + true => log::info!("Found {} keys pre-removal 👀", P::get()), + false => log::warn!( + "Migration RemovePallet<{}> can be removed (no keys found pre-removal).", + P::get() + ), + }; + Ok(Vec::new()) + } + + #[cfg(feature = "try-runtime")] + fn post_upgrade(_state: Vec) -> Result<(), &'static str> { + let hashed_prefix = twox_128(P::get().as_bytes()); + match contains_prefixed_key(&hashed_prefix) { + true => { + log::error!("{} has keys remaining post-removal ❗", P::get()); + return Err("Keys remaining post-removal, this should never happen 🚨") + }, + false => log::info!("No {} keys found post-removal 🎉", P::get()), + }; + Ok(()) + } +} From e08235acd2a98b7403b526ff11f63d2869e6cd15 Mon Sep 17 00:00:00 2001 From: Chris Kerr <120631257+piffle-rack@users.noreply.github.com> Date: Thu, 6 Apr 2023 19:06:15 +0300 Subject: [PATCH 348/558] Fix typos (#13842) * Fix typo GRANPA -> GRANDPA * Fix capitalisation Fixed words capitalised in the middle of sentences. --- client/network/README.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/client/network/README.md b/client/network/README.md index d39674724..b9c0e028f 100644 --- a/client/network/README.md +++ b/client/network/README.md @@ -223,7 +223,7 @@ For each peer the sync maintains the number of our common best block with that p whenever peer announce new blocks or our best block advances. This allows to keep track of peers that have new block data and request new information as soon as it is announced. In keep-up mode, we also track peers that announce blocks on all branches and not just the best branch. The sync algorithm tries to be greedy and download -All data that's announced. +all data that's announced. ## Fast sync @@ -262,8 +262,8 @@ After the latest state has been imported the node is fully operational, but is s data. I.e. it is unable to serve bock bodies and headers other than the most recent one. To make sure all nodes have block history available, a background sync process is started that downloads all the missing blocks. It is run in parallel with the keep-up sync and does not interfere with downloading of the recent blocks. -During this download we also import GRANPA justifications for blocks with authority set changes, so that -The warp-synced node has all the data to serve for other nodes nodes that might want to sync from it with +During this download we also import GRANDPA justifications for blocks with authority set changes, so that +the warp-synced node has all the data to serve for other nodes nodes that might want to sync from it with any method. # Usage From 851a8264909acb14d77a0f25a281147f4e689152 Mon Sep 17 00:00:00 2001 From: alexd10s Date: Thu, 6 Apr 2023 17:19:46 +0100 Subject: [PATCH 349/558] Update the frame-weight-template to use PhantomData from core (#13844) * update weights template and recalculate weights from pallet template * update template file in benchmarking-cli --- .maintain/frame-weight-template.hbs | 2 +- Cargo.lock | 1 - bin/node-template/pallets/template/Cargo.toml | 1 - bin/node-template/pallets/template/src/weights.rs | 12 ++++++------ utils/frame/benchmarking-cli/src/pallet/template.hbs | 2 +- 5 files changed, 8 insertions(+), 10 deletions(-) diff --git a/.maintain/frame-weight-template.hbs b/.maintain/frame-weight-template.hbs index ba9ac0798..0df6bef5d 100644 --- a/.maintain/frame-weight-template.hbs +++ b/.maintain/frame-weight-template.hbs @@ -17,7 +17,7 @@ #![allow(unused_imports)] use frame_support::{traits::Get, weights::{Weight, constants::RocksDbWeight}}; -use sp_std::marker::PhantomData; +use core::marker::PhantomData; /// Weight functions needed for {{pallet}}. pub trait WeightInfo { diff --git a/Cargo.lock b/Cargo.lock index 525f40ced..d4185103f 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -6864,7 +6864,6 @@ dependencies = [ "sp-core", "sp-io", "sp-runtime", - "sp-std", ] [[package]] diff --git a/bin/node-template/pallets/template/Cargo.toml b/bin/node-template/pallets/template/Cargo.toml index 818dc0bbc..ddf7c6e52 100644 --- a/bin/node-template/pallets/template/Cargo.toml +++ b/bin/node-template/pallets/template/Cargo.toml @@ -20,7 +20,6 @@ scale-info = { version = "2.1.1", default-features = false, features = ["derive" frame-benchmarking = { version = "4.0.0-dev", default-features = false, optional = true, path = "../../../../frame/benchmarking" } frame-support = { version = "4.0.0-dev", default-features = false, path = "../../../../frame/support" } frame-system = { version = "4.0.0-dev", default-features = false, path = "../../../../frame/system" } -sp-std = { version = "5.0.0", default-features = false, path = "../../../../primitives/std" } [dev-dependencies] sp-core = { version = "7.0.0", path = "../../../../primitives/core" } diff --git a/bin/node-template/pallets/template/src/weights.rs b/bin/node-template/pallets/template/src/weights.rs index 952aba49b..e8fbc09ba 100644 --- a/bin/node-template/pallets/template/src/weights.rs +++ b/bin/node-template/pallets/template/src/weights.rs @@ -2,7 +2,7 @@ //! Autogenerated weights for pallet_template //! //! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev -//! DATE: 2023-04-05, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! DATE: 2023-04-06, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` //! WORST CASE MAP SIZE: `1000000` //! HOSTNAME: `Alexs-MacBook-Pro-2.local`, CPU: `` //! EXECUTION: Some(Wasm), WASM-EXECUTION: Compiled, CHAIN: Some("dev"), DB CACHE: 1024 @@ -31,7 +31,7 @@ #![allow(unused_imports)] use frame_support::{traits::Get, weights::{Weight, constants::RocksDbWeight}}; -use sp_std::marker::PhantomData; +use core::marker::PhantomData; /// Weight functions needed for pallet_template. pub trait WeightInfo { @@ -49,7 +49,7 @@ impl WeightInfo for SubstrateWeight { // Measured: `0` // Estimated: `0` // Minimum execution time: 8_000_000 picoseconds. - Weight::from_parts(8_000_000, 0) + Weight::from_parts(9_000_000, 0) .saturating_add(T::DbWeight::get().writes(1_u64)) } /// Storage: TemplateModule Something (r:1 w:1) @@ -58,7 +58,7 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `32` // Estimated: `1489` - // Minimum execution time: 5_000_000 picoseconds. + // Minimum execution time: 6_000_000 picoseconds. Weight::from_parts(6_000_000, 1489) .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) @@ -74,7 +74,7 @@ impl WeightInfo for () { // Measured: `0` // Estimated: `0` // Minimum execution time: 8_000_000 picoseconds. - Weight::from_parts(8_000_000, 0) + Weight::from_parts(9_000_000, 0) .saturating_add(RocksDbWeight::get().writes(1_u64)) } /// Storage: TemplateModule Something (r:1 w:1) @@ -83,7 +83,7 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `32` // Estimated: `1489` - // Minimum execution time: 5_000_000 picoseconds. + // Minimum execution time: 6_000_000 picoseconds. Weight::from_parts(6_000_000, 1489) .saturating_add(RocksDbWeight::get().reads(1_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) diff --git a/utils/frame/benchmarking-cli/src/pallet/template.hbs b/utils/frame/benchmarking-cli/src/pallet/template.hbs index 88d6b69a6..f852e773c 100644 --- a/utils/frame/benchmarking-cli/src/pallet/template.hbs +++ b/utils/frame/benchmarking-cli/src/pallet/template.hbs @@ -17,7 +17,7 @@ #![allow(unused_imports)] use frame_support::{traits::Get, weights::Weight}; -use sp_std::marker::PhantomData; +use core::marker::PhantomData; /// Weight functions for `{{pallet}}`. pub struct WeightInfo(PhantomData); From d443f148ce0aa6a51798b86c6b3ae54113786f94 Mon Sep 17 00:00:00 2001 From: Aaro Altonen <48052676+altonen@users.noreply.github.com> Date: Fri, 7 Apr 2023 11:20:17 +0300 Subject: [PATCH 350/558] Make blocks per request configurable (#13824) * Make blocks per request configurable * Correct type * Update docs * Update client/cli/src/params/network_params.rs --- client/cli/src/params/network_params.rs | 8 ++ client/network/src/config.rs | 5 ++ .../network/sync/src/block_request_handler.rs | 6 +- client/network/sync/src/blocks.rs | 2 +- client/network/sync/src/engine.rs | 9 ++ client/network/sync/src/lib.rs | 84 ++++++++++++------- 6 files changed, 81 insertions(+), 33 deletions(-) diff --git a/client/cli/src/params/network_params.rs b/client/cli/src/params/network_params.rs index acec4b2ac..9d61e7204 100644 --- a/client/cli/src/params/network_params.rs +++ b/client/cli/src/params/network_params.rs @@ -141,6 +141,13 @@ pub struct NetworkParams { verbatim_doc_comment )] pub sync: SyncMode, + + /// Maximum number of blocks per request. + /// + /// Try reducing this number from the default value if you have a slow network connection + /// and observe block requests timing out. + #[arg(long, value_name = "COUNT", default_value_t = 64)] + pub max_blocks_per_request: u32, } impl NetworkParams { @@ -230,6 +237,7 @@ impl NetworkParams { allow_private_ip, }, max_parallel_downloads: self.max_parallel_downloads, + max_blocks_per_request: self.max_blocks_per_request, enable_dht_random_walk: !self.reserved_only, allow_non_globals_in_dht, kademlia_disjoint_query_paths: self.kademlia_disjoint_query_paths, diff --git a/client/network/src/config.rs b/client/network/src/config.rs index 781ae9c78..e00bfac79 100644 --- a/client/network/src/config.rs +++ b/client/network/src/config.rs @@ -566,6 +566,7 @@ pub struct NetworkConfiguration { /// List of request-response protocols that the node supports. pub request_response_protocols: Vec, + /// Configuration for the default set of nodes used for block syncing and transactions. pub default_peers_set: SetConfig, @@ -590,6 +591,9 @@ pub struct NetworkConfiguration { /// Maximum number of peers to ask the same blocks in parallel. pub max_parallel_downloads: u32, + /// Maximum number of blocks per request. + pub max_blocks_per_request: u32, + /// Initial syncing mode. pub sync_mode: SyncMode, @@ -653,6 +657,7 @@ impl NetworkConfiguration { node_name: node_name.into(), transport: TransportConfig::Normal { enable_mdns: false, allow_private_ip: true }, max_parallel_downloads: 5, + max_blocks_per_request: 64, sync_mode: SyncMode::Full, enable_dht_random_walk: true, allow_non_globals_in_dht: false, diff --git a/client/network/sync/src/block_request_handler.rs b/client/network/sync/src/block_request_handler.rs index 921efd7de..ece565aad 100644 --- a/client/network/sync/src/block_request_handler.rs +++ b/client/network/sync/src/block_request_handler.rs @@ -17,7 +17,10 @@ //! Helper for handling (i.e. answering) block requests from a remote peer via the //! `crate::request_responses::RequestResponsesBehaviour`. -use crate::schema::v1::{block_request::FromBlock, BlockResponse, Direction}; +use crate::{ + schema::v1::{block_request::FromBlock, BlockResponse, Direction}, + MAX_BLOCKS_IN_RESPONSE, +}; use codec::{Decode, Encode}; use futures::{ @@ -50,7 +53,6 @@ use std::{ }; const LOG_TARGET: &str = "sync"; -const MAX_BLOCKS_IN_RESPONSE: usize = 128; const MAX_BODY_BYTES: usize = 8 * 1024 * 1024; const MAX_NUMBER_OF_SAME_REQUESTS_PER_PEER: usize = 2; diff --git a/client/network/sync/src/blocks.rs b/client/network/sync/src/blocks.rs index 4cc1ca080..3c76238be 100644 --- a/client/network/sync/src/blocks.rs +++ b/client/network/sync/src/blocks.rs @@ -109,7 +109,7 @@ impl BlockCollection { pub fn needed_blocks( &mut self, who: PeerId, - count: usize, + count: u32, peer_best: NumberFor, common: NumberFor, max_parallel: u32, diff --git a/client/network/sync/src/engine.rs b/client/network/sync/src/engine.rs index e3d45a980..6fb618a57 100644 --- a/client/network/sync/src/engine.rs +++ b/client/network/sync/src/engine.rs @@ -264,6 +264,14 @@ where SyncOperationMode::Warp => SyncMode::Warp, }; let max_parallel_downloads = network_config.max_parallel_downloads; + let max_blocks_per_request = if network_config.max_blocks_per_request > + crate::MAX_BLOCKS_IN_RESPONSE as u32 + { + log::info!(target: "sync", "clamping maximum blocks per request to {}", crate::MAX_BLOCKS_IN_RESPONSE); + crate::MAX_BLOCKS_IN_RESPONSE as u32 + } else { + network_config.max_blocks_per_request + }; let cache_capacity = NonZeroUsize::new( (network_config.default_peers_set.in_peers as usize + network_config.default_peers_set.out_peers as usize) @@ -318,6 +326,7 @@ where roles, block_announce_validator, max_parallel_downloads, + max_blocks_per_request, warp_sync_params, metrics_registry, network_service.clone(), diff --git a/client/network/sync/src/lib.rs b/client/network/sync/src/lib.rs index 28959e7f9..e112a1715 100644 --- a/client/network/sync/src/lib.rs +++ b/client/network/sync/src/lib.rs @@ -107,9 +107,6 @@ pub mod state_request_handler; pub mod warp; pub mod warp_request_handler; -/// Maximum blocks to request in a single packet. -const MAX_BLOCKS_TO_REQUEST: usize = 64; - /// Maximum blocks to store in the import queue. const MAX_IMPORTING_BLOCKS: usize = 2048; @@ -147,6 +144,9 @@ const MIN_PEERS_TO_START_WARP_SYNC: usize = 3; /// Maximum allowed size for a block announce. const MAX_BLOCK_ANNOUNCE_SIZE: u64 = 1024 * 1024; +/// Maximum blocks per response. +pub(crate) const MAX_BLOCKS_IN_RESPONSE: usize = 128; + mod rep { use sc_peerset::ReputationChange as Rep; /// Reputation change when a peer sent us a message that led to a @@ -311,6 +311,8 @@ pub struct ChainSync { block_announce_validator: Box + Send>, /// Maximum number of peers to ask the same blocks in parallel. max_parallel_downloads: u32, + /// Maximum blocks per request. + max_blocks_per_request: u32, /// Total number of downloaded blocks. downloaded_blocks: usize, /// All block announcement that are currently being validated. @@ -1403,6 +1405,7 @@ where roles: Roles, block_announce_validator: Box + Send>, max_parallel_downloads: u32, + max_blocks_per_request: u32, warp_sync_params: Option>, metrics_registry: Option<&Registry>, network_service: service::network::NetworkServiceHandle, @@ -1437,6 +1440,7 @@ where allowed_requests: Default::default(), block_announce_validator, max_parallel_downloads, + max_blocks_per_request, downloaded_blocks: 0, block_announce_validation: Default::default(), block_announce_validation_per_peer_stats: Default::default(), @@ -2365,6 +2369,7 @@ where let queue = &self.queue_blocks; let allowed_requests = self.allowed_requests.take(); let max_parallel = if is_major_syncing { 1 } else { self.max_parallel_downloads }; + let max_blocks_per_request = self.max_blocks_per_request; let gap_sync = &mut self.gap_sync; self.peers .iter_mut() @@ -2404,6 +2409,7 @@ where blocks, attrs, max_parallel, + max_blocks_per_request, last_finalized, best_queued, ) { @@ -2430,6 +2436,7 @@ where client.block_status(*hash).unwrap_or(BlockStatus::Unknown) } }, + max_blocks_per_request, ) { trace!(target: "sync", "Downloading fork {:?} from {}", hash, id); peer.state = PeerSyncState::DownloadingStale(hash); @@ -2442,6 +2449,7 @@ where attrs, sync.target, sync.best_queued_number, + max_blocks_per_request, ) }) { peer.state = PeerSyncState::DownloadingGap(range.start); @@ -2910,6 +2918,7 @@ fn peer_block_request( blocks: &mut BlockCollection, attrs: BlockAttributes, max_parallel_downloads: u32, + max_blocks_per_request: u32, finalized: NumberFor, best_num: NumberFor, ) -> Option<(Range>, BlockRequest)> { @@ -2925,7 +2934,7 @@ fn peer_block_request( } let range = blocks.needed_blocks( *id, - MAX_BLOCKS_TO_REQUEST, + max_blocks_per_request, peer.best_number, peer.common_number, max_parallel_downloads, @@ -2960,10 +2969,11 @@ fn peer_gap_block_request( attrs: BlockAttributes, target: NumberFor, common_number: NumberFor, + max_blocks_per_request: u32, ) -> Option<(Range>, BlockRequest)> { let range = blocks.needed_blocks( *id, - MAX_BLOCKS_TO_REQUEST, + max_blocks_per_request, std::cmp::min(peer.best_number, target), common_number, 1, @@ -2992,6 +3002,7 @@ fn fork_sync_request( finalized: NumberFor, attributes: BlockAttributes, check_block: impl Fn(&B::Hash) -> BlockStatus, + max_blocks_per_request: u32, ) -> Option<(B::Hash, BlockRequest)> { targets.retain(|hash, r| { if r.number <= finalized { @@ -3011,7 +3022,7 @@ fn fork_sync_request( // Download the fork only if it is behind or not too far ahead our tip of the chain // Otherwise it should be downloaded in full sync mode. if r.number <= best_num || - (r.number - best_num).saturated_into::() < MAX_BLOCKS_TO_REQUEST as u32 + (r.number - best_num).saturated_into::() < max_blocks_per_request as u32 { let parent_status = r.parent_hash.as_ref().map_or(BlockStatus::Unknown, check_block); let count = if parent_status == BlockStatus::Unknown { @@ -3199,6 +3210,7 @@ mod test { Roles::from(&Role::Full), block_announce_validator, 1, + 64, None, None, chain_sync_network_handle, @@ -3265,6 +3277,7 @@ mod test { Roles::from(&Role::Full), Box::new(DefaultBlockAnnounceValidator), 1, + 64, None, None, chain_sync_network_handle, @@ -3446,6 +3459,7 @@ mod test { Roles::from(&Role::Full), Box::new(DefaultBlockAnnounceValidator), 5, + 64, None, None, chain_sync_network_handle, @@ -3572,6 +3586,7 @@ mod test { Roles::from(&Role::Full), Box::new(DefaultBlockAnnounceValidator), 5, + 64, None, None, chain_sync_network_handle, @@ -3586,6 +3601,7 @@ mod test { let peer_id2 = PeerId::random(); let best_block = blocks.last().unwrap().clone(); + let max_blocks_to_request = sync.max_blocks_per_request; // Connect the node we will sync from sync.new_peer(peer_id1, best_block.hash(), *best_block.header().number()) .unwrap(); @@ -3595,8 +3611,8 @@ mod test { while best_block_num < MAX_DOWNLOAD_AHEAD { let request = get_block_request( &mut sync, - FromBlock::Number(MAX_BLOCKS_TO_REQUEST as u64 + best_block_num as u64), - MAX_BLOCKS_TO_REQUEST as u32, + FromBlock::Number(max_blocks_to_request as u64 + best_block_num as u64), + max_blocks_to_request as u32, &peer_id1, ); @@ -3610,14 +3626,14 @@ mod test { let res = sync.on_block_data(&peer_id1, Some(request), response).unwrap(); assert!(matches!( res, - OnBlockData::Import(_, blocks) if blocks.len() == MAX_BLOCKS_TO_REQUEST + OnBlockData::Import(_, blocks) if blocks.len() == max_blocks_to_request as usize ),); - best_block_num += MAX_BLOCKS_TO_REQUEST as u32; + best_block_num += max_blocks_to_request as u32; let _ = sync.on_blocks_processed( - MAX_BLOCKS_TO_REQUEST as usize, - MAX_BLOCKS_TO_REQUEST as usize, + max_blocks_to_request as usize, + max_blocks_to_request as usize, resp_blocks .iter() .rev() @@ -3675,8 +3691,8 @@ mod test { // peer 2 as well. get_block_request( &mut sync, - FromBlock::Number(peer1_from + MAX_BLOCKS_TO_REQUEST as u64), - MAX_BLOCKS_TO_REQUEST as u32, + FromBlock::Number(peer1_from + max_blocks_to_request as u64), + max_blocks_to_request as u32, &peer_id2, ); } @@ -3728,6 +3744,7 @@ mod test { Roles::from(&Role::Full), Box::new(DefaultBlockAnnounceValidator), 5, + 64, None, None, chain_sync_network_handle, @@ -3773,11 +3790,12 @@ mod test { // Now request and import the fork. let mut best_block_num = *finalized_block.header().number() as u32; + let max_blocks_to_request = sync.max_blocks_per_request; while best_block_num < *fork_blocks.last().unwrap().header().number() as u32 - 1 { let request = get_block_request( &mut sync, - FromBlock::Number(MAX_BLOCKS_TO_REQUEST as u64 + best_block_num as u64), - MAX_BLOCKS_TO_REQUEST as u32, + FromBlock::Number(max_blocks_to_request as u64 + best_block_num as u64), + max_blocks_to_request as u32, &peer_id1, ); @@ -3791,14 +3809,14 @@ mod test { let res = sync.on_block_data(&peer_id1, Some(request), response).unwrap(); assert!(matches!( res, - OnBlockData::Import(_, blocks) if blocks.len() == MAX_BLOCKS_TO_REQUEST + OnBlockData::Import(_, blocks) if blocks.len() == sync.max_blocks_per_request as usize ),); - best_block_num += MAX_BLOCKS_TO_REQUEST as u32; + best_block_num += sync.max_blocks_per_request as u32; let _ = sync.on_blocks_processed( - MAX_BLOCKS_TO_REQUEST as usize, - MAX_BLOCKS_TO_REQUEST as usize, + max_blocks_to_request as usize, + max_blocks_to_request as usize, resp_blocks .iter() .rev() @@ -3869,6 +3887,7 @@ mod test { Roles::from(&Role::Full), Box::new(DefaultBlockAnnounceValidator), 5, + 64, None, None, chain_sync_network_handle, @@ -3914,10 +3933,12 @@ mod test { // Now request and import the fork. let mut best_block_num = *finalized_block.header().number() as u32; + let max_blocks_to_request = sync.max_blocks_per_request; + let mut request = get_block_request( &mut sync, - FromBlock::Number(MAX_BLOCKS_TO_REQUEST as u64 + best_block_num as u64), - MAX_BLOCKS_TO_REQUEST as u32, + FromBlock::Number(max_blocks_to_request as u64 + best_block_num as u64), + max_blocks_to_request as u32, &peer_id1, ); let last_block_num = *fork_blocks.last().unwrap().header().number() as u32 - 1; @@ -3932,18 +3953,18 @@ mod test { let res = sync.on_block_data(&peer_id1, Some(request.clone()), response).unwrap(); assert!(matches!( res, - OnBlockData::Import(_, blocks) if blocks.len() == MAX_BLOCKS_TO_REQUEST + OnBlockData::Import(_, blocks) if blocks.len() == max_blocks_to_request as usize ),); - best_block_num += MAX_BLOCKS_TO_REQUEST as u32; + best_block_num += max_blocks_to_request as u32; if best_block_num < last_block_num { // make sure we're not getting a duplicate request in the time before the blocks are // processed request = get_block_request( &mut sync, - FromBlock::Number(MAX_BLOCKS_TO_REQUEST as u64 + best_block_num as u64), - MAX_BLOCKS_TO_REQUEST as u32, + FromBlock::Number(max_blocks_to_request as u64 + best_block_num as u64), + max_blocks_to_request as u32, &peer_id1, ); } @@ -3965,16 +3986,17 @@ mod test { // The import queue may send notifications in batches of varying size. So we simulate // this here by splitting the batch into 2 notifications. + let max_blocks_to_request = sync.max_blocks_per_request; let second_batch = notify_imported.split_off(notify_imported.len() / 2); let _ = sync.on_blocks_processed( - MAX_BLOCKS_TO_REQUEST as usize, - MAX_BLOCKS_TO_REQUEST as usize, + max_blocks_to_request as usize, + max_blocks_to_request as usize, notify_imported, ); let _ = sync.on_blocks_processed( - MAX_BLOCKS_TO_REQUEST as usize, - MAX_BLOCKS_TO_REQUEST as usize, + max_blocks_to_request as usize, + max_blocks_to_request as usize, second_batch, ); @@ -4010,6 +4032,7 @@ mod test { Roles::from(&Role::Full), Box::new(DefaultBlockAnnounceValidator), 1, + 64, None, None, chain_sync_network_handle, @@ -4055,6 +4078,7 @@ mod test { Roles::from(&Role::Full), Box::new(DefaultBlockAnnounceValidator), 1, + 64, None, None, chain_sync_network_handle, From 72dd574acf805fda1dc1e64b7bbd510e12a81312 Mon Sep 17 00:00:00 2001 From: Marcin S Date: Fri, 7 Apr 2023 12:35:41 +0200 Subject: [PATCH 351/558] WASM executor: add `OutputExceedsBounds` variant to `Error` (#13841) * WASM executor: add `OutputExceedsBounds` variant to `Error` Previously this was a `WasmError`, which is intended for runtime construction errors. However this led to confusion as output-exceeds-bounds occurs due to execution of `validate_block`. * Fix warning --- client/executor/common/src/error.rs | 5 ++++- client/executor/src/integration_tests/mod.rs | 11 ++++------- client/executor/wasmtime/src/runtime.rs | 4 ++-- 3 files changed, 10 insertions(+), 10 deletions(-) diff --git a/client/executor/common/src/error.rs b/client/executor/common/src/error.rs index c47164a60..2dfe0bf02 100644 --- a/client/executor/common/src/error.rs +++ b/client/executor/common/src/error.rs @@ -104,6 +104,9 @@ pub enum Error { #[error("Execution aborted due to trap: {0}")] AbortedDueToTrap(MessageWithBacktrace), + + #[error("Output exceeds bounds of wasm memory")] + OutputExceedsBounds, } impl wasmi::HostError for Error {} @@ -153,7 +156,7 @@ pub enum WasmError { Instantiation(String), /// Other error happenend. - #[error("{0}")] + #[error("Other error happened while constructing the runtime: {0}")] Other(String), } diff --git a/client/executor/src/integration_tests/mod.rs b/client/executor/src/integration_tests/mod.rs index 066b1497f..0148968f9 100644 --- a/client/executor/src/integration_tests/mod.rs +++ b/client/executor/src/integration_tests/mod.rs @@ -22,7 +22,7 @@ mod linux; use assert_matches::assert_matches; use codec::{Decode, Encode}; use sc_executor_common::{ - error::{Error, WasmError}, + error::Error, runtime_blob::RuntimeBlob, wasm_runtime::{HeapAllocStrategy, WasmModule}, }; @@ -819,9 +819,8 @@ fn return_huge_len(wasm_method: WasmExecutionMethod) { Error::Runtime => { assert_matches!(wasm_method, WasmExecutionMethod::Interpreted); }, - Error::RuntimeConstruction(WasmError::Other(error)) => { + Error::OutputExceedsBounds => { assert_matches!(wasm_method, WasmExecutionMethod::Compiled { .. }); - assert_eq!(error, "output exceeds bounds of wasm memory"); }, error => panic!("unexpected error: {:?}", error), } @@ -849,9 +848,8 @@ fn return_max_memory_offset_plus_one(wasm_method: WasmExecutionMethod) { Error::Runtime => { assert_matches!(wasm_method, WasmExecutionMethod::Interpreted); }, - Error::RuntimeConstruction(WasmError::Other(error)) => { + Error::OutputExceedsBounds => { assert_matches!(wasm_method, WasmExecutionMethod::Compiled { .. }); - assert_eq!(error, "output exceeds bounds of wasm memory"); }, error => panic!("unexpected error: {:?}", error), } @@ -866,9 +864,8 @@ fn return_overflow(wasm_method: WasmExecutionMethod) { Error::Runtime => { assert_matches!(wasm_method, WasmExecutionMethod::Interpreted); }, - Error::RuntimeConstruction(WasmError::Other(error)) => { + Error::OutputExceedsBounds => { assert_matches!(wasm_method, WasmExecutionMethod::Compiled { .. }); - assert_eq!(error, "output exceeds bounds of wasm memory"); }, error => panic!("unexpected error: {:?}", error), } diff --git a/client/executor/wasmtime/src/runtime.rs b/client/executor/wasmtime/src/runtime.rs index e01a51f6c..c9a2c83e0 100644 --- a/client/executor/wasmtime/src/runtime.rs +++ b/client/executor/wasmtime/src/runtime.rs @@ -26,7 +26,7 @@ use crate::{ use sc_allocator::{AllocationStats, FreeingBumpHeapAllocator}; use sc_executor_common::{ - error::{Result, WasmError}, + error::{Error, Result, WasmError}, runtime_blob::{ self, DataSegmentsSnapshot, ExposedMutableGlobalsSet, GlobalsSnapshot, RuntimeBlob, }, @@ -776,7 +776,7 @@ fn extract_output_data( // Get the size of the WASM memory in bytes. let memory_size = ctx.as_context().data().memory().data_size(ctx); if checked_range(output_ptr as usize, output_len as usize, memory_size).is_none() { - Err(WasmError::Other("output exceeds bounds of wasm memory".into()))? + Err(Error::OutputExceedsBounds)? } let mut output = vec![0; output_len as usize]; From 82e824bc0d21c63943b06d753e53a1e17e5a23c3 Mon Sep 17 00:00:00 2001 From: Sergej Sakac <73715684+Szegoo@users.noreply.github.com> Date: Sat, 8 Apr 2023 20:20:13 +0300 Subject: [PATCH 352/558] Warn if pallet provided to try-state does not exist (#13858) * Warn if pallet does not exist in try-state * unwrap_or_default --- frame/support/src/traits/try_runtime.rs | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/frame/support/src/traits/try_runtime.rs b/frame/support/src/traits/try_runtime.rs index 6103f07a7..bebc24872 100644 --- a/frame/support/src/traits/try_runtime.rs +++ b/frame/support/src/traits/try_runtime.rs @@ -168,11 +168,19 @@ impl TryState::name(), Tuple::try_state) ),* )]; let mut result = Ok(()); - for (name, try_state_fn) in try_state_fns { - if pallet_names.iter().any(|n| n == name.as_bytes()) { + pallet_names.iter().for_each(|pallet_name| { + if let Some((name, try_state_fn)) = + try_state_fns.iter().find(|(name, _)| name.as_bytes() == pallet_name) + { result = result.and(try_state_fn(n.clone(), targets.clone())); + } else { + crate::log::warn!( + "Pallet {:?} not found", + sp_std::str::from_utf8(pallet_name).unwrap_or_default() + ); } - } + }); + result }, } From 2e94e253950457571e91a5df1e60f82ba7b841dd Mon Sep 17 00:00:00 2001 From: yjh Date: Mon, 10 Apr 2023 07:48:40 +0800 Subject: [PATCH 353/558] refactor(sc-executor): use wasm executor builder instead of old apis (#13740) * refactor: use builder api for all executors * improve a lot * remove unused args * cleanup deps * fix inconsistency about heap alloc * add `heap_pages` back to try-runtime * fix * chore: reduce duplicated code for sc-service-test * cleanup code * fmt * improve test executor * improve * use #[deprecated] * set runtime_cache_size: 4 * fix and improve * refactor builder * fix * fix bench * fix tests * fix warnings * fix warnings * fix * fix * update by suggestions * update name --- bin/node-template/node/src/service.rs | 7 +-- bin/node/cli/benches/transaction_pool.rs | 4 +- bin/node/cli/src/service.rs | 7 +-- bin/node/executor/benches/bench.rs | 11 ++-- bin/node/executor/tests/common.rs | 4 +- bin/node/inspect/src/command.rs | 25 ++++----- bin/node/testing/src/bench.rs | 14 ++--- client/executor/benches/bench.rs | 7 ++- client/executor/common/src/wasm_runtime.rs | 7 +++ .../src/{native_executor.rs => executor.rs} | 51 +++++++++++-------- .../executor/src/integration_tests/linux.rs | 4 +- client/executor/src/integration_tests/mod.rs | 34 ++++++------- client/executor/src/lib.rs | 25 +++++---- client/executor/src/wasm_runtime.rs | 2 +- client/executor/wasmtime/src/tests.rs | 6 +-- client/service/src/builder.rs | 35 ++++++++++--- client/service/src/client/call_executor.rs | 25 +++++---- client/service/src/client/wasm_override.rs | 28 +++++----- client/service/src/lib.rs | 6 +-- client/service/test/src/client/mod.rs | 38 +++----------- client/service/test/src/lib.rs | 8 ++- frame/system/src/tests.rs | 4 +- primitives/api/test/tests/runtime_calls.rs | 8 ++- primitives/runtime-interface/test/src/lib.rs | 3 +- test-utils/client/src/lib.rs | 4 +- test-utils/runtime/client/src/lib.rs | 7 +-- test-utils/runtime/src/system.rs | 4 +- .../benchmarking-cli/src/pallet/command.rs | 17 ++++--- utils/frame/try-runtime/cli/src/lib.rs | 28 +++++----- 29 files changed, 213 insertions(+), 210 deletions(-) rename client/executor/src/{native_executor.rs => executor.rs} (94%) diff --git a/bin/node-template/node/src/service.rs b/bin/node-template/node/src/service.rs index dfbb5e963..723d1db3e 100644 --- a/bin/node-template/node/src/service.rs +++ b/bin/node-template/node/src/service.rs @@ -68,12 +68,7 @@ pub fn new_partial( }) .transpose()?; - let executor = NativeElseWasmExecutor::::new( - config.wasm_method, - config.default_heap_pages, - config.max_runtime_instances, - config.runtime_cache_size, - ); + let executor = sc_service::new_native_or_wasm_executor(&config); let (client, backend, keystore_container, task_manager) = sc_service::new_full_parts::( diff --git a/bin/node/cli/benches/transaction_pool.rs b/bin/node/cli/benches/transaction_pool.rs index 7488ec033..abee9c846 100644 --- a/bin/node/cli/benches/transaction_pool.rs +++ b/bin/node/cli/benches/transaction_pool.rs @@ -27,7 +27,7 @@ use sc_client_api::execution_extensions::ExecutionStrategies; use sc_service::{ config::{ BlocksPruning, DatabaseSource, KeystoreConfig, NetworkConfiguration, OffchainWorkerConfig, - PruningMode, TransactionPoolOptions, WasmExecutionMethod, + PruningMode, TransactionPoolOptions, }, BasePath, Configuration, Role, }; @@ -69,7 +69,7 @@ fn new_node(tokio_handle: Handle) -> node_cli::service::NewFullBase { state_pruning: Some(PruningMode::ArchiveAll), blocks_pruning: BlocksPruning::KeepAll, chain_spec: spec, - wasm_method: WasmExecutionMethod::Interpreted, + wasm_method: Default::default(), // NOTE: we enforce the use of the native runtime to make the errors more debuggable execution_strategies: ExecutionStrategies { syncing: sc_client_api::ExecutionStrategy::NativeWhenPossible, diff --git a/bin/node/cli/src/service.rs b/bin/node/cli/src/service.rs index ca6830085..7b0dfcda6 100644 --- a/bin/node/cli/src/service.rs +++ b/bin/node/cli/src/service.rs @@ -163,12 +163,7 @@ pub fn new_partial( }) .transpose()?; - let executor = NativeElseWasmExecutor::::new( - config.wasm_method, - config.default_heap_pages, - config.max_runtime_instances, - config.runtime_cache_size, - ); + let executor = sc_service::new_native_or_wasm_executor(&config); let (client, backend, keystore_container, task_manager) = sc_service::new_full_parts::( diff --git a/bin/node/executor/benches/bench.rs b/bin/node/executor/benches/bench.rs index a8c31068e..aa7d9eb0f 100644 --- a/bin/node/executor/benches/bench.rs +++ b/bin/node/executor/benches/bench.rs @@ -26,7 +26,7 @@ use node_executor::ExecutorDispatch; use node_primitives::{BlockNumber, Hash}; use node_testing::keyring::*; use sc_executor::{ - Externalities, NativeElseWasmExecutor, RuntimeVersionOf, WasmExecutionMethod, + Externalities, NativeElseWasmExecutor, RuntimeVersionOf, WasmExecutionMethod, WasmExecutor, WasmtimeInstantiationStrategy, }; use sp_core::{ @@ -191,12 +191,13 @@ fn bench_execute_block(c: &mut Criterion) { for strategy in execution_methods { group.bench_function(format!("{:?}", strategy), |b| { let genesis_config = node_testing::genesis::config(Some(compact_code_unwrap())); - let (use_native, wasm_method) = match strategy { - ExecutionMethod::Native => (true, WasmExecutionMethod::Interpreted), - ExecutionMethod::Wasm(wasm_method) => (false, wasm_method), + let use_native = match strategy { + ExecutionMethod::Native => true, + ExecutionMethod::Wasm(..) => false, }; - let executor = NativeElseWasmExecutor::new(wasm_method, None, 8, 2); + let executor = + NativeElseWasmExecutor::new_with_wasm_executor(WasmExecutor::builder().build()); let runtime_code = RuntimeCode { code_fetcher: &sp_core::traits::WrappedRuntimeCode(compact_code_unwrap().into()), hash: vec![1, 2, 3], diff --git a/bin/node/executor/tests/common.rs b/bin/node/executor/tests/common.rs index 2f92423ff..6ce9ea3a0 100644 --- a/bin/node/executor/tests/common.rs +++ b/bin/node/executor/tests/common.rs @@ -18,7 +18,7 @@ use codec::{Decode, Encode}; use frame_support::Hashable; use frame_system::offchain::AppCrypto; -use sc_executor::{error::Result, NativeElseWasmExecutor, WasmExecutionMethod}; +use sc_executor::{error::Result, NativeElseWasmExecutor, WasmExecutor}; use sp_consensus_babe::{ digests::{PreDigest, SecondaryPlainPreDigest}, Slot, BABE_ENGINE_ID, @@ -98,7 +98,7 @@ pub fn from_block_number(n: u32) -> Header { } pub fn executor() -> NativeElseWasmExecutor { - NativeElseWasmExecutor::new(WasmExecutionMethod::Interpreted, None, 8, 2) + NativeElseWasmExecutor::new_with_wasm_executor(WasmExecutor::builder().build()) } pub fn executor_call( diff --git a/bin/node/inspect/src/command.rs b/bin/node/inspect/src/command.rs index 3dca979eb..970257683 100644 --- a/bin/node/inspect/src/command.rs +++ b/bin/node/inspect/src/command.rs @@ -23,41 +23,34 @@ use crate::{ Inspector, }; use sc_cli::{CliConfiguration, ImportParams, Result, SharedParams}; -use sc_executor::NativeElseWasmExecutor; -use sc_service::{new_full_client, Configuration, NativeExecutionDispatch}; +use sc_service::{Configuration, NativeExecutionDispatch}; use sp_runtime::traits::Block; use std::str::FromStr; impl InspectCmd { /// Run the inspect command, passing the inspector. - pub fn run(&self, config: Configuration) -> Result<()> + pub fn run(&self, config: Configuration) -> Result<()> where B: Block, B::Hash: FromStr, RA: Send + Sync + 'static, - EX: NativeExecutionDispatch + 'static, + D: NativeExecutionDispatch + 'static, { - let executor = NativeElseWasmExecutor::::new( - config.wasm_method, - config.default_heap_pages, - config.max_runtime_instances, - config.runtime_cache_size, - ); - - let client = new_full_client::(&config, None, executor)?; + let executor = sc_service::new_native_or_wasm_executor::(&config); + let client = sc_service::new_full_client::(&config, None, executor)?; let inspect = Inspector::::new(client); match &self.command { InspectSubCmd::Block { input } => { let input = input.parse()?; - let res = inspect.block(input).map_err(|e| format!("{}", e))?; - println!("{}", res); + let res = inspect.block(input).map_err(|e| e.to_string())?; + println!("{res}"); Ok(()) }, InspectSubCmd::Extrinsic { input } => { let input = input.parse()?; - let res = inspect.extrinsic(input).map_err(|e| format!("{}", e))?; - println!("{}", res); + let res = inspect.extrinsic(input).map_err(|e| e.to_string())?; + println!("{res}"); Ok(()) }, } diff --git a/bin/node/testing/src/bench.rs b/bin/node/testing/src/bench.rs index 392b78241..9dbb26a90 100644 --- a/bin/node/testing/src/bench.rs +++ b/bin/node/testing/src/bench.rs @@ -392,14 +392,14 @@ impl BenchDb { let task_executor = TaskExecutor::new(); let backend = sc_service::new_db_backend(db_config).expect("Should not fail"); - let executor = NativeElseWasmExecutor::new( - WasmExecutionMethod::Compiled { - instantiation_strategy: WasmtimeInstantiationStrategy::PoolingCopyOnWrite, - }, - None, - 8, - 2, + let executor = NativeElseWasmExecutor::new_with_wasm_executor( + sc_executor::WasmExecutor::builder() + .with_execution_method(WasmExecutionMethod::Compiled { + instantiation_strategy: WasmtimeInstantiationStrategy::PoolingCopyOnWrite, + }) + .build(), ); + let client_config = sc_service::ClientConfig::default(); let genesis_block_builder = sc_service::GenesisBlockBuilder::new( &keyring.generate_genesis(), diff --git a/client/executor/benches/bench.rs b/client/executor/benches/bench.rs index f129ebf64..e293329e7 100644 --- a/client/executor/benches/bench.rs +++ b/client/executor/benches/bench.rs @@ -21,7 +21,7 @@ use codec::Encode; use sc_executor_common::{ runtime_blob::RuntimeBlob, - wasm_runtime::{HeapAllocStrategy, WasmInstance, WasmModule}, + wasm_runtime::{HeapAllocStrategy, WasmInstance, WasmModule, DEFAULT_HEAP_ALLOC_STRATEGY}, }; use sc_executor_wasmtime::InstantiationStrategy; use sc_runtime_test::wasm_binary_unwrap as test_runtime; @@ -51,13 +51,12 @@ fn initialize( ) -> Arc { let blob = RuntimeBlob::uncompress_if_needed(runtime).unwrap(); let host_functions = sp_io::SubstrateHostFunctions::host_functions(); - let extra_pages = 2048; let allow_missing_func_imports = true; match method { Method::Interpreted => sc_executor_wasmi::create_runtime( blob, - HeapAllocStrategy::Static { extra_pages }, + DEFAULT_HEAP_ALLOC_STRATEGY, host_functions, allow_missing_func_imports, ) @@ -67,7 +66,7 @@ fn initialize( allow_missing_func_imports, cache_path: None, semantics: sc_executor_wasmtime::Semantics { - heap_alloc_strategy: HeapAllocStrategy::Static { extra_pages }, + heap_alloc_strategy: DEFAULT_HEAP_ALLOC_STRATEGY, instantiation_strategy, deterministic_stack_limit: None, canonicalize_nans: false, diff --git a/client/executor/common/src/wasm_runtime.rs b/client/executor/common/src/wasm_runtime.rs index e3db7e52f..5dac77e59 100644 --- a/client/executor/common/src/wasm_runtime.rs +++ b/client/executor/common/src/wasm_runtime.rs @@ -23,6 +23,13 @@ use sp_wasm_interface::Value; pub use sc_allocator::AllocationStats; +/// Default heap allocation strategy. +pub const DEFAULT_HEAP_ALLOC_STRATEGY: HeapAllocStrategy = + HeapAllocStrategy::Static { extra_pages: DEFAULT_HEAP_ALLOC_PAGES }; + +/// Default heap allocation pages. +pub const DEFAULT_HEAP_ALLOC_PAGES: u32 = 2048; + /// A method to be used to find the entrypoint when calling into the runtime /// /// Contains variants on how to resolve wasm function that will be invoked. diff --git a/client/executor/src/native_executor.rs b/client/executor/src/executor.rs similarity index 94% rename from client/executor/src/native_executor.rs rename to client/executor/src/executor.rs index c72cf3c9c..79250ee0d 100644 --- a/client/executor/src/native_executor.rs +++ b/client/executor/src/executor.rs @@ -32,16 +32,14 @@ use std::{ use codec::Encode; use sc_executor_common::{ runtime_blob::RuntimeBlob, - wasm_runtime::{AllocationStats, HeapAllocStrategy, WasmInstance, WasmModule}, + wasm_runtime::{ + AllocationStats, HeapAllocStrategy, WasmInstance, WasmModule, DEFAULT_HEAP_ALLOC_STRATEGY, + }, }; use sp_core::traits::{CallContext, CodeExecutor, Externalities, RuntimeCode}; use sp_version::{GetNativeVersion, NativeVersion, RuntimeVersion}; use sp_wasm_interface::{ExtendedHostFunctions, HostFunctions}; -/// Default heap allocation strategy. -const DEFAULT_HEAP_ALLOC_STRATEGY: HeapAllocStrategy = - HeapAllocStrategy::Static { extra_pages: 2048 }; - /// Set up the externalities and safe calling environment to execute runtime calls. /// /// If the inner closure panics, it will be caught and return an error. @@ -100,10 +98,10 @@ impl WasmExecutorBuilder { /// Create a new instance of `Self` /// /// - `method`: The wasm execution method that should be used by the executor. - pub fn new(method: WasmExecutionMethod) -> Self { + pub fn new() -> Self { Self { _phantom: PhantomData, - method, + method: WasmExecutionMethod::default(), onchain_heap_alloc_strategy: None, offchain_heap_alloc_strategy: None, max_runtime_instances: 2, @@ -113,6 +111,12 @@ impl WasmExecutorBuilder { } } + /// Create the wasm executor with execution method that should be used by the executor. + pub fn with_execution_method(mut self, method: WasmExecutionMethod) -> Self { + self.method = method; + self + } + /// Create the wasm executor with the given number of `heap_alloc_strategy` for onchain runtime /// calls. pub fn with_onchain_heap_alloc_strategy( @@ -256,6 +260,7 @@ where /// compiled execution method is used. /// /// `runtime_cache_size` - The capacity of runtime cache. + #[deprecated(note = "use `Self::builder` method instead of it")] pub fn new( method: WasmExecutionMethod, default_heap_pages: Option, @@ -283,11 +288,12 @@ where } /// Instantiate a builder for creating an instance of `Self`. - pub fn builder(method: WasmExecutionMethod) -> WasmExecutorBuilder { - WasmExecutorBuilder::new(method) + pub fn builder() -> WasmExecutorBuilder { + WasmExecutorBuilder::new() } /// Ignore missing function imports if set true. + #[deprecated(note = "use `Self::builder` method instead of it")] pub fn allow_missing_host_functions(&mut self, allow_missing_host_functions: bool) { self.allow_missing_host_functions = allow_missing_host_functions } @@ -539,6 +545,7 @@ pub struct NativeElseWasmExecutor { } impl NativeElseWasmExecutor { + /// /// Create new instance. /// /// # Parameters @@ -553,19 +560,23 @@ impl NativeElseWasmExecutor { /// `max_runtime_instances` - The number of runtime instances to keep in memory ready for reuse. /// /// `runtime_cache_size` - The capacity of runtime cache. + #[deprecated(note = "use `Self::new_with_wasm_executor` method instead of it")] pub fn new( fallback_method: WasmExecutionMethod, default_heap_pages: Option, max_runtime_instances: usize, runtime_cache_size: u8, ) -> Self { - let wasm = WasmExecutor::new( - fallback_method, - default_heap_pages, - max_runtime_instances, - None, - runtime_cache_size, - ); + let heap_pages = default_heap_pages.map_or(DEFAULT_HEAP_ALLOC_STRATEGY, |h| { + HeapAllocStrategy::Static { extra_pages: h as _ } + }); + let wasm = WasmExecutor::builder() + .with_execution_method(fallback_method) + .with_onchain_heap_alloc_strategy(heap_pages) + .with_offchain_heap_alloc_strategy(heap_pages) + .with_max_runtime_instances(max_runtime_instances) + .with_runtime_cache_size(runtime_cache_size) + .build(); NativeElseWasmExecutor { native_version: D::native_version(), wasm } } @@ -580,6 +591,7 @@ impl NativeElseWasmExecutor { } /// Ignore missing function imports if set true. + #[deprecated(note = "use `Self::new_with_wasm_executor` method instead of it")] pub fn allow_missing_host_functions(&mut self, allow_missing_host_functions: bool) { self.wasm.allow_missing_host_functions = allow_missing_host_functions } @@ -714,11 +726,8 @@ mod tests { #[test] fn native_executor_registers_custom_interface() { - let executor = NativeElseWasmExecutor::::new( - WasmExecutionMethod::Interpreted, - None, - 8, - 2, + let executor = NativeElseWasmExecutor::::new_with_wasm_executor( + WasmExecutor::builder().build(), ); fn extract_host_functions( diff --git a/client/executor/src/integration_tests/linux.rs b/client/executor/src/integration_tests/linux.rs index 38d75da4e..434cb69bf 100644 --- a/client/executor/src/integration_tests/linux.rs +++ b/client/executor/src/integration_tests/linux.rs @@ -21,7 +21,7 @@ use super::mk_test_runtime; use crate::WasmExecutionMethod; use codec::Encode as _; -use sc_executor_common::wasm_runtime::HeapAllocStrategy; +use sc_executor_common::wasm_runtime::DEFAULT_HEAP_ALLOC_STRATEGY; mod smaps; @@ -74,7 +74,7 @@ fn memory_consumption(wasm_method: WasmExecutionMethod) { // For that we make a series of runtime calls, probing the RSS for the VMA matching the linear // memory. After the call we expect RSS to be equal to 0. - let runtime = mk_test_runtime(wasm_method, HeapAllocStrategy::Static { extra_pages: 1024 }); + let runtime = mk_test_runtime(wasm_method, DEFAULT_HEAP_ALLOC_STRATEGY); let mut instance = runtime.new_instance().unwrap(); let heap_base = instance diff --git a/client/executor/src/integration_tests/mod.rs b/client/executor/src/integration_tests/mod.rs index 0148968f9..63a642f99 100644 --- a/client/executor/src/integration_tests/mod.rs +++ b/client/executor/src/integration_tests/mod.rs @@ -24,7 +24,7 @@ use codec::{Decode, Encode}; use sc_executor_common::{ error::Error, runtime_blob::RuntimeBlob, - wasm_runtime::{HeapAllocStrategy, WasmModule}, + wasm_runtime::{HeapAllocStrategy, WasmModule, DEFAULT_HEAP_ALLOC_STRATEGY}, }; use sc_runtime_test::wasm_binary_unwrap; use sp_core::{ @@ -114,8 +114,10 @@ fn call_in_wasm( execution_method: WasmExecutionMethod, ext: &mut E, ) -> Result, Error> { - let executor = - crate::WasmExecutor::::new(execution_method, Some(1024), 8, None, 2); + let executor = crate::WasmExecutor::::builder() + .with_execution_method(execution_method) + .build(); + executor.uncached_call( RuntimeBlob::uncompress_if_needed(wasm_binary_unwrap()).unwrap(), ext, @@ -446,13 +448,11 @@ test_wasm_execution!(should_trap_when_heap_exhausted); fn should_trap_when_heap_exhausted(wasm_method: WasmExecutionMethod) { let mut ext = TestExternalities::default(); - let executor = crate::WasmExecutor::::new( - wasm_method, - Some(17), // `17` is the initial number of pages compiled into the binary. - 8, - None, - 2, - ); + let executor = crate::WasmExecutor::::builder() + .with_execution_method(wasm_method) + // `17` is the initial number of pages compiled into the binary. + .with_onchain_heap_alloc_strategy(HeapAllocStrategy::Static { extra_pages: 17 }) + .build(); let err = executor .uncached_call( @@ -560,7 +560,7 @@ fn restoration_of_globals(wasm_method: WasmExecutionMethod) { test_wasm_execution!(interpreted_only heap_is_reset_between_calls); fn heap_is_reset_between_calls(wasm_method: WasmExecutionMethod) { - let runtime = mk_test_runtime(wasm_method, HeapAllocStrategy::Static { extra_pages: 1024 }); + let runtime = mk_test_runtime(wasm_method, DEFAULT_HEAP_ALLOC_STRATEGY); let mut instance = runtime.new_instance().unwrap(); let heap_base = instance @@ -579,13 +579,11 @@ fn heap_is_reset_between_calls(wasm_method: WasmExecutionMethod) { test_wasm_execution!(parallel_execution); fn parallel_execution(wasm_method: WasmExecutionMethod) { - let executor = std::sync::Arc::new(crate::WasmExecutor::::new( - wasm_method, - Some(1024), - 8, - None, - 2, - )); + let executor = Arc::new( + crate::WasmExecutor::::builder() + .with_execution_method(wasm_method) + .build(), + ); let threads: Vec<_> = (0..8) .map(|_| { let executor = executor.clone(); diff --git a/client/executor/src/lib.rs b/client/executor/src/lib.rs index e5bae474e..42e7dc7d1 100644 --- a/client/executor/src/lib.rs +++ b/client/executor/src/lib.rs @@ -32,24 +32,29 @@ #![recursion_limit = "128"] #[macro_use] -mod native_executor; +mod executor; #[cfg(test)] mod integration_tests; mod wasm_runtime; -pub use codec::Codec; -pub use native_executor::{ - with_externalities_safe, NativeElseWasmExecutor, NativeExecutionDispatch, WasmExecutor, +pub use self::{ + executor::{ + with_externalities_safe, NativeElseWasmExecutor, NativeExecutionDispatch, WasmExecutor, + }, + wasm_runtime::{read_embedded_version, WasmExecutionMethod}, }; +pub use codec::Codec; #[doc(hidden)] pub use sp_core::traits::Externalities; pub use sp_version::{NativeVersion, RuntimeVersion}; #[doc(hidden)] pub use sp_wasm_interface; -pub use wasm_runtime::{read_embedded_version, WasmExecutionMethod}; pub use wasmi; -pub use sc_executor_common::error; +pub use sc_executor_common::{ + error, + wasm_runtime::{HeapAllocStrategy, DEFAULT_HEAP_ALLOC_PAGES, DEFAULT_HEAP_ALLOC_STRATEGY}, +}; pub use sc_executor_wasmtime::InstantiationStrategy as WasmtimeInstantiationStrategy; /// Extracts the runtime version of a given runtime code. @@ -74,13 +79,7 @@ mod tests { let mut ext = TestExternalities::default(); let mut ext = ext.ext(); - let executor = WasmExecutor::::new( - WasmExecutionMethod::Interpreted, - Some(8), - 8, - None, - 2, - ); + let executor = WasmExecutor::::builder().build(); let res = executor .uncached_call( RuntimeBlob::uncompress_if_needed(wasm_binary_unwrap()).unwrap(), diff --git a/client/executor/src/wasm_runtime.rs b/client/executor/src/wasm_runtime.rs index fb3dd0f38..351b6f377 100644 --- a/client/executor/src/wasm_runtime.rs +++ b/client/executor/src/wasm_runtime.rs @@ -434,7 +434,7 @@ where // The following unwind safety assertion is OK because if the method call panics, the // runtime will be dropped. let runtime = AssertUnwindSafe(runtime.as_ref()); - crate::native_executor::with_externalities_safe(&mut **ext, move || { + crate::executor::with_externalities_safe(&mut **ext, move || { runtime.new_instance()?.call("Core_version".into(), &[]) }) .map_err(|_| WasmError::Instantiation("panic in call to get runtime version".into()))? diff --git a/client/executor/wasmtime/src/tests.rs b/client/executor/wasmtime/src/tests.rs index 0ca609516..e8d7c5ab1 100644 --- a/client/executor/wasmtime/src/tests.rs +++ b/client/executor/wasmtime/src/tests.rs @@ -20,7 +20,7 @@ use codec::{Decode as _, Encode as _}; use sc_executor_common::{ error::Error, runtime_blob::RuntimeBlob, - wasm_runtime::{HeapAllocStrategy, WasmModule}, + wasm_runtime::{HeapAllocStrategy, WasmModule, DEFAULT_HEAP_ALLOC_STRATEGY}, }; use sc_runtime_test::wasm_binary_unwrap; @@ -93,7 +93,7 @@ impl RuntimeBuilder { instantiation_strategy, canonicalize_nans: false, deterministic_stack: false, - heap_pages: HeapAllocStrategy::Static { extra_pages: 1024 }, + heap_pages: DEFAULT_HEAP_ALLOC_STRATEGY, precompile_runtime: false, tmpdir: None, } @@ -477,7 +477,7 @@ fn test_instances_without_reuse_are_not_leaked() { deterministic_stack_limit: None, canonicalize_nans: false, parallel_compilation: true, - heap_alloc_strategy: HeapAllocStrategy::Static { extra_pages: 2048 }, + heap_alloc_strategy: DEFAULT_HEAP_ALLOC_STRATEGY, wasm_multi_value: false, wasm_bulk_memory: false, wasm_reference_types: false, diff --git a/client/service/src/builder.rs b/client/service/src/builder.rs index d399b3154..415fa03a1 100644 --- a/client/service/src/builder.rs +++ b/client/service/src/builder.rs @@ -36,7 +36,10 @@ use sc_client_api::{ }; use sc_client_db::{Backend, DatabaseSettings}; use sc_consensus::import_queue::ImportQueue; -use sc_executor::RuntimeVersionOf; +use sc_executor::{ + sp_wasm_interface::HostFunctions, HeapAllocStrategy, NativeElseWasmExecutor, + NativeExecutionDispatch, RuntimeVersionOf, WasmExecutor, DEFAULT_HEAP_ALLOC_STRATEGY, +}; use sc_keystore::LocalKeystore; use sc_network::{config::SyncMode, NetworkService, NetworkStateInfo, NetworkStatusProvider}; use sc_network_bitswap::BitswapRequestHandler; @@ -74,11 +77,10 @@ pub type TFullClient = Client, TFullCallExecutor, TBl, TRtApi>; /// Full client backend type. -pub type TFullBackend = sc_client_db::Backend; +pub type TFullBackend = Backend; /// Full client call executor type. -pub type TFullCallExecutor = - crate::client::LocalCallExecutor, TExec>; +pub type TFullCallExecutor = crate::client::LocalCallExecutor, TExec>; type TFullParts = (TFullClient, Arc>, KeystoreContainer, TaskManager); @@ -229,6 +231,27 @@ where Ok((client, backend, keystore_container, task_manager)) } +/// Creates a [`NativeElseWasmExecutor`] according to [`Configuration`]. +pub fn new_native_or_wasm_executor( + config: &Configuration, +) -> NativeElseWasmExecutor { + NativeElseWasmExecutor::new_with_wasm_executor(new_wasm_executor(config)) +} + +/// Creates a [`WasmExecutor`] according to [`Configuration`]. +pub fn new_wasm_executor(config: &Configuration) -> WasmExecutor { + let strategy = config + .default_heap_pages + .map_or(DEFAULT_HEAP_ALLOC_STRATEGY, |p| HeapAllocStrategy::Static { extra_pages: p as _ }); + WasmExecutor::::builder() + .with_execution_method(config.wasm_method) + .with_onchain_heap_alloc_strategy(strategy) + .with_offchain_heap_alloc_strategy(strategy) + .with_max_runtime_instances(config.max_runtime_instances) + .with_runtime_cache_size(config.runtime_cache_size) + .build() +} + /// Create an instance of default DB-backend backend. pub fn new_db_backend( settings: DatabaseSettings, @@ -254,7 +277,7 @@ pub fn new_client( telemetry: Option, config: ClientConfig, ) -> Result< - crate::client::Client< + Client< Backend, crate::client::LocalCallExecutor, E>, Block, @@ -277,7 +300,7 @@ where execution_extensions, )?; - crate::client::Client::new( + Client::new( backend, executor, spawn_handle, diff --git a/client/service/src/client/call_executor.rs b/client/service/src/client/call_executor.rs index ef36768fe..7979c139e 100644 --- a/client/service/src/client/call_executor.rs +++ b/client/service/src/client/call_executor.rs @@ -360,7 +360,7 @@ mod tests { use super::*; use backend::Backend; use sc_client_api::in_mem; - use sc_executor::{NativeElseWasmExecutor, WasmExecutionMethod}; + use sc_executor::{NativeElseWasmExecutor, WasmExecutor}; use sp_core::{ testing::TaskExecutor, traits::{FetchRuntimeCode, WrappedRuntimeCode}, @@ -368,14 +368,18 @@ mod tests { use std::collections::HashMap; use substrate_test_runtime_client::{runtime, GenesisInit, LocalExecutorDispatch}; + fn executor() -> NativeElseWasmExecutor { + NativeElseWasmExecutor::new_with_wasm_executor( + WasmExecutor::builder() + .with_max_runtime_instances(1) + .with_runtime_cache_size(2) + .build(), + ) + } + #[test] fn should_get_override_if_exists() { - let executor = NativeElseWasmExecutor::::new( - WasmExecutionMethod::Interpreted, - Some(128), - 1, - 2, - ); + let executor = executor(); let overrides = crate::client::wasm_override::dummy_overrides(); let onchain_code = WrappedRuntimeCode(substrate_test_runtime::wasm_binary_unwrap().into()); @@ -443,12 +447,7 @@ mod tests { fn returns_runtime_version_from_substitute() { const SUBSTITUTE_SPEC_NAME: &str = "substitute-spec-name-cool"; - let executor = NativeElseWasmExecutor::::new( - WasmExecutionMethod::Interpreted, - Some(128), - 1, - 2, - ); + let executor = executor(); let backend = Arc::new(in_mem::Backend::::new()); diff --git a/client/service/src/client/wasm_override.rs b/client/service/src/client/wasm_override.rs index 3a56e5c59..725c8ab94 100644 --- a/client/service/src/client/wasm_override.rs +++ b/client/service/src/client/wasm_override.rs @@ -264,21 +264,26 @@ pub fn dummy_overrides() -> WasmOverride { #[cfg(test)] mod tests { use super::*; - use sc_executor::{NativeElseWasmExecutor, WasmExecutionMethod}; + use sc_executor::{HeapAllocStrategy, NativeElseWasmExecutor, WasmExecutor}; use std::fs::{self, File}; use substrate_test_runtime_client::LocalExecutorDispatch; + fn executor() -> NativeElseWasmExecutor { + NativeElseWasmExecutor::::new_with_wasm_executor( + WasmExecutor::builder() + .with_onchain_heap_alloc_strategy(HeapAllocStrategy::Static {extra_pages: 128}) + .with_offchain_heap_alloc_strategy(HeapAllocStrategy::Static {extra_pages: 128}) + .with_max_runtime_instances(1) + .with_runtime_cache_size(2) + .build() + ) + } + fn wasm_test(fun: F) where F: Fn(&Path, &[u8], &NativeElseWasmExecutor), { - let exec = - NativeElseWasmExecutor::::new( - WasmExecutionMethod::Interpreted, - Some(128), - 1, - 2, - ); + let exec = executor(); let bytes = substrate_test_runtime::wasm_binary_unwrap(); let dir = tempfile::tempdir().expect("Create a temporary directory"); fun(dir.path(), bytes, &exec); @@ -287,12 +292,7 @@ mod tests { #[test] fn should_get_runtime_version() { - let executor = NativeElseWasmExecutor::::new( - WasmExecutionMethod::Interpreted, - Some(128), - 1, - 2, - ); + let executor = executor(); let version = WasmOverride::runtime_version( &executor, diff --git a/client/service/src/lib.rs b/client/service/src/lib.rs index 54f11ec25..c0c7c537c 100644 --- a/client/service/src/lib.rs +++ b/client/service/src/lib.rs @@ -56,9 +56,9 @@ use sp_runtime::{ pub use self::{ builder::{ build_network, build_offchain_workers, new_client, new_db_backend, new_full_client, - new_full_parts, new_full_parts_with_genesis_builder, spawn_tasks, BuildNetworkParams, - KeystoreContainer, NetworkStarter, SpawnTasksParams, TFullBackend, TFullCallExecutor, - TFullClient, + new_full_parts, new_full_parts_with_genesis_builder, new_native_or_wasm_executor, + spawn_tasks, BuildNetworkParams, KeystoreContainer, NetworkStarter, SpawnTasksParams, + TFullBackend, TFullCallExecutor, TFullClient, }, client::{ClientConfig, LocalCallExecutor}, error::Error, diff --git a/client/service/test/src/client/mod.rs b/client/service/test/src/client/mod.rs index e6545abf1..1b282a342 100644 --- a/client/service/test/src/client/mod.rs +++ b/client/service/test/src/client/mod.rs @@ -45,6 +45,7 @@ use sp_trie::{LayoutV0, TrieConfiguration}; use std::{collections::HashSet, sync::Arc}; use substrate_test_runtime::TestAPI; use substrate_test_runtime_client::{ + new_native_or_wasm_executor, prelude::*, runtime::{ genesismap::{insert_genesis_block, GenesisConfig}, @@ -58,29 +59,6 @@ mod db; const TEST_ENGINE_ID: ConsensusEngineId = *b"TEST"; -pub struct ExecutorDispatch; - -impl sc_executor::NativeExecutionDispatch for ExecutorDispatch { - type ExtendHostFunctions = (); - - fn dispatch(method: &str, data: &[u8]) -> Option> { - substrate_test_runtime_client::runtime::api::dispatch(method, data) - } - - fn native_version() -> sc_executor::NativeVersion { - substrate_test_runtime_client::runtime::native_version() - } -} - -fn executor() -> sc_executor::NativeElseWasmExecutor { - sc_executor::NativeElseWasmExecutor::new( - sc_executor::WasmExecutionMethod::Interpreted, - None, - 8, - 2, - ) -} - fn construct_block( backend: &InMemoryBackend, number: BlockNumber, @@ -108,7 +86,7 @@ fn construct_block( StateMachine::new( backend, &mut overlay, - &executor(), + &new_native_or_wasm_executor(), "Core_initialize_block", &header.encode(), Default::default(), @@ -122,7 +100,7 @@ fn construct_block( StateMachine::new( backend, &mut overlay, - &executor(), + &new_native_or_wasm_executor(), "BlockBuilder_apply_extrinsic", &tx.encode(), Default::default(), @@ -136,7 +114,7 @@ fn construct_block( let ret_data = StateMachine::new( backend, &mut overlay, - &executor(), + &new_native_or_wasm_executor(), "BlockBuilder_finalize_block", &[], Default::default(), @@ -208,7 +186,7 @@ fn construct_genesis_should_work_with_native() { let _ = StateMachine::new( &backend, &mut overlay, - &executor(), + &new_native_or_wasm_executor(), "Core_execute_block", &b1data, Default::default(), @@ -241,7 +219,7 @@ fn construct_genesis_should_work_with_wasm() { let _ = StateMachine::new( &backend, &mut overlay, - &executor(), + &new_native_or_wasm_executor(), "Core_execute_block", &b1data, Default::default(), @@ -274,7 +252,7 @@ fn construct_genesis_with_bad_transaction_should_panic() { let r = StateMachine::new( &backend, &mut overlay, - &executor(), + &new_native_or_wasm_executor(), "Core_execute_block", &b1data, Default::default(), @@ -1910,7 +1888,7 @@ fn cleans_up_closed_notification_sinks_on_block_import() { use substrate_test_runtime_client::GenesisInit; let backend = Arc::new(sc_client_api::in_mem::Backend::new()); - let executor = substrate_test_runtime_client::new_native_executor(); + let executor = new_native_or_wasm_executor(); let client_config = sc_service::ClientConfig::default(); let genesis_block_builder = sc_service::GenesisBlockBuilder::new( diff --git a/client/service/test/src/lib.rs b/client/service/test/src/lib.rs index 8a2e5050b..0f20376fe 100644 --- a/client/service/test/src/lib.rs +++ b/client/service/test/src/lib.rs @@ -65,9 +65,7 @@ impl Drop for TestNet { } } -pub trait TestNetNode: - Clone + Future> + Send + 'static -{ +pub trait TestNetNode: Clone + Future> + Send + 'static { type Block: BlockT; type Backend: Backend; type Executor: CallExecutor + Send + Sync; @@ -128,7 +126,7 @@ impl Clone impl Future for TestNetComponents { - type Output = Result<(), sc_service::Error>; + type Output = Result<(), Error>; fn poll(self: Pin<&mut Self>, cx: &mut Context) -> Poll { Pin::new(&mut self.task_manager.lock().future()).poll(cx) @@ -248,7 +246,7 @@ fn node_config< state_pruning: Default::default(), blocks_pruning: BlocksPruning::KeepFinalized, chain_spec: Box::new((*spec).clone()), - wasm_method: sc_service::config::WasmExecutionMethod::Interpreted, + wasm_method: Default::default(), wasm_runtime_overrides: Default::default(), execution_strategies: Default::default(), rpc_http: None, diff --git a/frame/system/src/tests.rs b/frame/system/src/tests.rs index fc388800e..19b8d6a48 100644 --- a/frame/system/src/tests.rs +++ b/frame/system/src/tests.rs @@ -589,7 +589,7 @@ fn assert_runtime_updated_digest(num: usize) { #[test] fn set_code_with_real_wasm_blob() { - let executor = substrate_test_runtime_client::new_native_executor(); + let executor = substrate_test_runtime_client::new_native_or_wasm_executor(); let mut ext = new_test_ext(); ext.register_extension(sp_core::traits::ReadRuntimeVersionExt::new(executor)); ext.execute_with(|| { @@ -613,7 +613,7 @@ fn set_code_with_real_wasm_blob() { #[test] fn runtime_upgraded_with_set_storage() { - let executor = substrate_test_runtime_client::new_native_executor(); + let executor = substrate_test_runtime_client::new_native_or_wasm_executor(); let mut ext = new_test_ext(); ext.register_extension(sp_core::traits::ReadRuntimeVersionExt::new(executor)); ext.execute_with(|| { diff --git a/primitives/api/test/tests/runtime_calls.rs b/primitives/api/test/tests/runtime_calls.rs index b57793aad..956a6a25f 100644 --- a/primitives/api/test/tests/runtime_calls.rs +++ b/primitives/api/test/tests/runtime_calls.rs @@ -29,6 +29,7 @@ use substrate_test_runtime_client::{ use codec::Encode; use sc_block_builder::BlockBuilderProvider; use sp_consensus::SelectChain; +use substrate_test_runtime_client::sc_executor::WasmExecutor; fn calling_function_with_strat(strat: ExecutionStrategy) { let client = TestClientBuilder::new().set_execution_strategy(strat).build(); @@ -178,11 +179,8 @@ fn record_proof_works() { // Use the proof backend to execute `execute_block`. let mut overlay = Default::default(); - let executor = NativeElseWasmExecutor::::new( - WasmExecutionMethod::Interpreted, - None, - 8, - 2, + let executor = NativeElseWasmExecutor::::new_with_wasm_executor( + WasmExecutor::builder().build(), ); execution_proof_check_on_trie_backend( &backend, diff --git a/primitives/runtime-interface/test/src/lib.rs b/primitives/runtime-interface/test/src/lib.rs index 269b62333..e1be3b5d9 100644 --- a/primitives/runtime-interface/test/src/lib.rs +++ b/primitives/runtime-interface/test/src/lib.rs @@ -42,7 +42,8 @@ fn call_wasm_method_with_result( let executor = sc_executor::WasmExecutor::< ExtendedHostFunctions, - >::new(sc_executor::WasmExecutionMethod::Interpreted, Some(8), 8, None, 2); + >::builder() + .build(); let (result, allocation_stats) = executor.uncached_call_with_allocation_stats( RuntimeBlob::uncompress_if_needed(binary).expect("Failed to parse binary"), diff --git a/test-utils/client/src/lib.rs b/test-utils/client/src/lib.rs index c4572061c..fc9ba1c9e 100644 --- a/test-utils/client/src/lib.rs +++ b/test-utils/client/src/lib.rs @@ -27,7 +27,7 @@ pub use sc_client_api::{ BadBlocks, ForkBlocks, }; pub use sc_client_db::{self, Backend, BlocksPruning}; -pub use sc_executor::{self, NativeElseWasmExecutor, WasmExecutionMethod}; +pub use sc_executor::{self, NativeElseWasmExecutor, WasmExecutionMethod, WasmExecutor}; pub use sc_service::{client, RpcHandlers}; pub use sp_consensus; pub use sp_keyring::{ @@ -286,7 +286,7 @@ impl Backend: sc_client_api::backend::Backend + 'static, { let executor = executor.into().unwrap_or_else(|| { - NativeElseWasmExecutor::new(WasmExecutionMethod::Interpreted, None, 8, 2) + NativeElseWasmExecutor::new_with_wasm_executor(WasmExecutor::builder().build()) }); let executor = LocalCallExecutor::new( self.backend.clone(), diff --git a/test-utils/runtime/client/src/lib.rs b/test-utils/runtime/client/src/lib.rs index 099aeab11..5e4b5d6a1 100644 --- a/test-utils/runtime/client/src/lib.rs +++ b/test-utils/runtime/client/src/lib.rs @@ -38,6 +38,7 @@ use sp_core::{ Pair, }; use sp_runtime::traits::{Block as BlockT, Hash as HashT, Header as HeaderT}; +use substrate_test_client::sc_executor::WasmExecutor; use substrate_test_runtime::genesismap::{additional_storage_with_genesis, GenesisConfig}; /// A prelude to import in tests. @@ -171,7 +172,7 @@ pub type Client = client::Client< client::LocalCallExecutor< substrate_test_runtime::Block, B, - sc_executor::NativeElseWasmExecutor, + NativeElseWasmExecutor, >, substrate_test_runtime::Block, substrate_test_runtime::RuntimeApi, @@ -290,6 +291,6 @@ pub fn new() -> Client { } /// Create a new native executor. -pub fn new_native_executor() -> NativeElseWasmExecutor { - NativeElseWasmExecutor::new(sc_executor::WasmExecutionMethod::Interpreted, None, 8, 2) +pub fn new_native_or_wasm_executor() -> NativeElseWasmExecutor { + NativeElseWasmExecutor::new_with_wasm_executor(WasmExecutor::builder().build()) } diff --git a/test-utils/runtime/src/system.rs b/test-utils/runtime/src/system.rs index fc7505315..caabad336 100644 --- a/test-utils/runtime/src/system.rs +++ b/test-utils/runtime/src/system.rs @@ -376,7 +376,7 @@ mod tests { use super::*; use crate::{wasm_binary_unwrap, Header, Transfer}; - use sc_executor::{NativeElseWasmExecutor, WasmExecutionMethod}; + use sc_executor::{NativeElseWasmExecutor, WasmExecutor}; use sp_core::{ map, traits::{CallContext, CodeExecutor, RuntimeCode}, @@ -400,7 +400,7 @@ mod tests { } fn executor() -> NativeElseWasmExecutor { - NativeElseWasmExecutor::new(WasmExecutionMethod::Interpreted, None, 8, 2) + NativeElseWasmExecutor::new_with_wasm_executor(WasmExecutor::builder().build()) } fn new_test_ext() -> TestExternalities { diff --git a/utils/frame/benchmarking-cli/src/pallet/command.rs b/utils/frame/benchmarking-cli/src/pallet/command.rs index 4d583950d..0c3b6b3e8 100644 --- a/utils/frame/benchmarking-cli/src/pallet/command.rs +++ b/utils/frame/benchmarking-cli/src/pallet/command.rs @@ -27,7 +27,7 @@ use sc_cli::{ execution_method_from_cli, CliConfiguration, ExecutionStrategy, Result, SharedParams, }; use sc_client_db::BenchmarkingState; -use sc_executor::NativeElseWasmExecutor; +use sc_executor::{NativeElseWasmExecutor, WasmExecutor}; use sc_service::{Configuration, NativeExecutionDispatch}; use serde::Serialize; use sp_core::{ @@ -209,11 +209,16 @@ impl PalletCmd { // Do not enable storage tracking false, )?; - let executor = NativeElseWasmExecutor::::new( - execution_method_from_cli(self.wasm_method, self.wasmtime_instantiation_strategy), - self.heap_pages, - 2, // The runtime instances cache size. - 2, // The runtime cache size + + let method = + execution_method_from_cli(self.wasm_method, self.wasmtime_instantiation_strategy); + + let executor = NativeElseWasmExecutor::::new_with_wasm_executor( + WasmExecutor::builder() + .with_execution_method(method) + .with_max_runtime_instances(2) + .with_runtime_cache_size(2) + .build(), ); let extensions = || -> Extensions { diff --git a/utils/frame/try-runtime/cli/src/lib.rs b/utils/frame/try-runtime/cli/src/lib.rs index db690e508..cd6c48f52 100644 --- a/utils/frame/try-runtime/cli/src/lib.rs +++ b/utils/frame/try-runtime/cli/src/lib.rs @@ -368,7 +368,9 @@ use sc_cli::{ WasmtimeInstantiationStrategy, DEFAULT_WASMTIME_INSTANTIATION_STRATEGY, DEFAULT_WASM_EXECUTION_METHOD, }; -use sc_executor::{sp_wasm_interface::HostFunctions, WasmExecutor}; +use sc_executor::{ + sp_wasm_interface::HostFunctions, HeapAllocStrategy, WasmExecutor, DEFAULT_HEAP_ALLOC_STRATEGY, +}; use sp_api::HashT; use sp_core::{ hexdisplay::HexDisplay, @@ -824,18 +826,20 @@ pub(crate) fn full_extensions(wasm_executor: WasmExecutor) extensions } +/// Build wasm executor by default config. pub(crate) fn build_executor(shared: &SharedParams) -> WasmExecutor { - let heap_pages = shared.heap_pages.or(Some(2048)); - let max_runtime_instances = 8; - let runtime_cache_size = 2; - - WasmExecutor::new( - execution_method_from_cli(shared.wasm_method, shared.wasmtime_instantiation_strategy), - heap_pages, - max_runtime_instances, - None, - runtime_cache_size, - ) + let heap_pages = shared + .heap_pages + .map_or(DEFAULT_HEAP_ALLOC_STRATEGY, |p| HeapAllocStrategy::Static { extra_pages: p as _ }); + + WasmExecutor::builder() + .with_execution_method(execution_method_from_cli( + shared.wasm_method, + shared.wasmtime_instantiation_strategy, + )) + .with_onchain_heap_alloc_strategy(heap_pages) + .with_offchain_heap_alloc_strategy(heap_pages) + .build() } /// Ensure that the given `ext` is compiled with `try-runtime` From 7363dce0375fd628b6bcea6e41256a0561a4a73f Mon Sep 17 00:00:00 2001 From: Liam Aharon Date: Tue, 11 Apr 2023 07:25:12 +1000 Subject: [PATCH 354/558] remove unused import (#13868) --- client/executor/benches/bench.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client/executor/benches/bench.rs b/client/executor/benches/bench.rs index e293329e7..6cce05f6c 100644 --- a/client/executor/benches/bench.rs +++ b/client/executor/benches/bench.rs @@ -21,7 +21,7 @@ use codec::Encode; use sc_executor_common::{ runtime_blob::RuntimeBlob, - wasm_runtime::{HeapAllocStrategy, WasmInstance, WasmModule, DEFAULT_HEAP_ALLOC_STRATEGY}, + wasm_runtime::{WasmInstance, WasmModule, DEFAULT_HEAP_ALLOC_STRATEGY}, }; use sc_executor_wasmtime::InstantiationStrategy; use sc_runtime_test::wasm_binary_unwrap as test_runtime; From 2b61b1d22d78694bdc83d2b6cb0b3b749a0e668a Mon Sep 17 00:00:00 2001 From: yjh Date: Tue, 11 Apr 2023 18:42:49 +0800 Subject: [PATCH 355/558] chore: remove duplicated arc (#13871) --- client/executor/benches/bench.rs | 6 +++--- client/executor/src/executor.rs | 2 +- client/executor/src/integration_tests/mod.rs | 2 +- client/executor/src/wasm_runtime.rs | 20 ++++++++++---------- 4 files changed, 15 insertions(+), 15 deletions(-) diff --git a/client/executor/benches/bench.rs b/client/executor/benches/bench.rs index 6cce05f6c..772898b8c 100644 --- a/client/executor/benches/bench.rs +++ b/client/executor/benches/bench.rs @@ -48,7 +48,7 @@ fn initialize( _tmpdir: &mut Option, runtime: &[u8], method: Method, -) -> Arc { +) -> Box { let blob = RuntimeBlob::uncompress_if_needed(runtime).unwrap(); let host_functions = sp_io::SubstrateHostFunctions::host_functions(); let allow_missing_func_imports = true; @@ -60,7 +60,7 @@ fn initialize( host_functions, allow_missing_func_imports, ) - .map(|runtime| -> Arc { Arc::new(runtime) }), + .map(|runtime| -> Box { Box::new(runtime) }), Method::Compiled { instantiation_strategy, precompile } => { let config = sc_executor_wasmtime::Config { allow_missing_func_imports, @@ -98,7 +98,7 @@ fn initialize( } else { sc_executor_wasmtime::create_runtime::(blob, config) } - .map(|runtime| -> Arc { Arc::new(runtime) }) + .map(|runtime| -> Box { Box::new(runtime) }) }, } .unwrap() diff --git a/client/executor/src/executor.rs b/client/executor/src/executor.rs index 79250ee0d..a3717f4d2 100644 --- a/client/executor/src/executor.rs +++ b/client/executor/src/executor.rs @@ -320,7 +320,7 @@ where ) -> Result where F: FnOnce( - AssertUnwindSafe<&Arc>, + AssertUnwindSafe<&dyn WasmModule>, AssertUnwindSafe<&mut dyn WasmInstance>, Option<&RuntimeVersion>, AssertUnwindSafe<&mut dyn Externalities>, diff --git a/client/executor/src/integration_tests/mod.rs b/client/executor/src/integration_tests/mod.rs index 63a642f99..b65aeb8d0 100644 --- a/client/executor/src/integration_tests/mod.rs +++ b/client/executor/src/integration_tests/mod.rs @@ -483,7 +483,7 @@ fn should_trap_when_heap_exhausted(wasm_method: WasmExecutionMethod) { fn mk_test_runtime( wasm_method: WasmExecutionMethod, pages: HeapAllocStrategy, -) -> Arc { +) -> Box { let blob = RuntimeBlob::uncompress_if_needed(wasm_binary_unwrap()) .expect("failed to create a runtime blob out of test runtime"); diff --git a/client/executor/src/wasm_runtime.rs b/client/executor/src/wasm_runtime.rs index 351b6f377..41a095081 100644 --- a/client/executor/src/wasm_runtime.rs +++ b/client/executor/src/wasm_runtime.rs @@ -71,14 +71,14 @@ struct VersionedRuntimeId { /// A Wasm runtime object along with its cached runtime version. struct VersionedRuntime { /// Shared runtime that can spawn instances. - module: Arc, + module: Box, /// Runtime version according to `Core_version` if any. version: Option, // TODO: Remove this once the legacy instance reuse instantiation strategy // for `wasmtime` is gone, as this only makes sense with that particular strategy. /// Cached instance pool. - instances: Arc>>>>, + instances: Vec>>>, } impl VersionedRuntime { @@ -86,7 +86,7 @@ impl VersionedRuntime { fn with_instance(&self, ext: &mut dyn Externalities, f: F) -> Result where F: FnOnce( - &Arc, + &dyn WasmModule, &mut dyn WasmInstance, Option<&RuntimeVersion>, &mut dyn Externalities, @@ -106,7 +106,7 @@ impl VersionedRuntime { .map(|r| Ok((r, false))) .unwrap_or_else(|| self.module.new_instance().map(|i| (i, true)))?; - let result = f(&self.module, &mut *instance, self.version.as_ref(), ext); + let result = f(&*self.module, &mut *instance, self.version.as_ref(), ext); if let Err(e) = &result { if new_inst { tracing::warn!( @@ -142,7 +142,7 @@ impl VersionedRuntime { // Allocate a new instance let mut instance = self.module.new_instance()?; - f(&self.module, &mut *instance, self.version.as_ref(), ext) + f(&*self.module, &mut *instance, self.version.as_ref(), ext) }, } } @@ -228,7 +228,7 @@ impl RuntimeCache { where H: HostFunctions, F: FnOnce( - &Arc, + &dyn WasmModule, &mut dyn WasmInstance, Option<&RuntimeVersion>, &mut dyn Externalities, @@ -294,7 +294,7 @@ pub fn create_wasm_runtime_with_code( blob: RuntimeBlob, allow_missing_func_imports: bool, cache_path: Option<&Path>, -) -> Result, WasmError> +) -> Result, WasmError> where H: HostFunctions, { @@ -312,7 +312,7 @@ where H::host_functions(), allow_missing_func_imports, ) - .map(|runtime| -> Arc { Arc::new(runtime) }) + .map(|runtime| -> Box { Box::new(runtime) }) }, WasmExecutionMethod::Compiled { instantiation_strategy } => sc_executor_wasmtime::create_runtime::( @@ -333,7 +333,7 @@ where }, }, ) - .map(|runtime| -> Arc { Arc::new(runtime) }), + .map(|runtime| -> Box { Box::new(runtime) }), } } @@ -448,7 +448,7 @@ where let mut instances = Vec::with_capacity(max_instances); instances.resize_with(max_instances, || Mutex::new(None)); - Ok(VersionedRuntime { module: runtime, version, instances: Arc::new(instances) }) + Ok(VersionedRuntime { module: runtime, version, instances }) } #[cfg(test)] From 932f820625b37913f901c6d25c40fe542bbeb6b5 Mon Sep 17 00:00:00 2001 From: Mira Ressel Date: Tue, 11 Apr 2023 12:57:14 +0200 Subject: [PATCH 356/558] simplify some pattern matches to appease 1.68 clippy (#13833) --- .../grandpa/src/communication/mod.rs | 4 +-- client/network/common/src/sync.rs | 4 +-- .../src/protocol/notifications/behaviour.rs | 27 +++++++------------ client/network/sync/src/blocks.rs | 2 +- client/network/transactions/src/lib.rs | 2 +- client/service/test/src/lib.rs | 5 +--- .../election-provider-multi-phase/src/lib.rs | 8 +++--- frame/offences/src/lib.rs | 2 +- frame/session/src/lib.rs | 2 +- frame/staking/src/pallet/impls.rs | 4 +-- 10 files changed, 23 insertions(+), 37 deletions(-) diff --git a/client/consensus/grandpa/src/communication/mod.rs b/client/consensus/grandpa/src/communication/mod.rs index 81365c20b..9d90035d7 100644 --- a/client/consensus/grandpa/src/communication/mod.rs +++ b/client/consensus/grandpa/src/communication/mod.rs @@ -852,9 +852,7 @@ fn check_compact_commit( // check signatures on all contained precommits. let mut buf = Vec::new(); - for (i, (precommit, &(ref sig, ref id))) in - msg.precommits.iter().zip(&msg.auth_data).enumerate() - { + for (i, (precommit, (sig, id))) in msg.precommits.iter().zip(&msg.auth_data).enumerate() { use crate::communication::gossip::Misbehavior; use finality_grandpa::Message as GrandpaMessage; diff --git a/client/network/common/src/sync.rs b/client/network/common/src/sync.rs index d02a81379..b01091ae0 100644 --- a/client/network/common/src/sync.rs +++ b/client/network/common/src/sync.rs @@ -424,9 +424,9 @@ pub trait ChainSync: Send { /// /// If [`PollBlockAnnounceValidation::ImportHeader`] is returned, then the caller MUST try to /// import passed header (call `on_block_data`). The network request isn't sent in this case. - fn poll_block_announce_validation<'a>( + fn poll_block_announce_validation( &mut self, - cx: &mut std::task::Context<'a>, + cx: &mut std::task::Context<'_>, ) -> Poll>; /// Call when a peer has disconnected. diff --git a/client/network/src/protocol/notifications/behaviour.rs b/client/network/src/protocol/notifications/behaviour.rs index 74e27fa17..9e9338938 100644 --- a/client/network/src/protocol/notifications/behaviour.rs +++ b/client/network/src/protocol/notifications/behaviour.rs @@ -2628,8 +2628,7 @@ mod tests { conn, NotifsHandlerOut::OpenDesiredByRemote { protocol_index: 0 }, ); - if let Some(&PeerState::Incoming { ref connections, .. }) = notif.peers.get(&(peer, set_id)) - { + if let Some(PeerState::Incoming { connections, .. }) = notif.peers.get(&(peer, set_id)) { assert_eq!(connections.len(), 1); assert_eq!(connections[0], (conn, ConnectionState::OpenDesiredByRemote)); } else { @@ -2647,8 +2646,7 @@ mod tests { }, )); - if let Some(&PeerState::Incoming { ref connections, .. }) = notif.peers.get(&(peer, set_id)) - { + if let Some(PeerState::Incoming { connections, .. }) = notif.peers.get(&(peer, set_id)) { assert_eq!(connections.len(), 2); assert_eq!(connections[0], (conn, ConnectionState::OpenDesiredByRemote)); assert_eq!(connections[1], (conn2, ConnectionState::Closed)); @@ -2797,8 +2795,7 @@ mod tests { }, )); - if let Some(&PeerState::Disabled { ref connections, .. }) = notif.peers.get(&(peer, set_id)) - { + if let Some(PeerState::Disabled { connections, .. }) = notif.peers.get(&(peer, set_id)) { assert_eq!(connections.len(), 1); assert_eq!(connections[0], (conn1, ConnectionState::Closed)); } else { @@ -2884,8 +2881,7 @@ mod tests { )); } - if let Some(&PeerState::Disabled { ref connections, .. }) = notif.peers.get(&(peer, set_id)) - { + if let Some(PeerState::Disabled { connections, .. }) = notif.peers.get(&(peer, set_id)) { assert_eq!(connections[0], (conn1, ConnectionState::Closed)); assert_eq!(connections[1], (conn2, ConnectionState::Closed)); } else { @@ -2900,8 +2896,7 @@ mod tests { NotifsHandlerOut::OpenDesiredByRemote { protocol_index: 0 }, ); - if let Some(&PeerState::Enabled { ref connections, .. }) = notif.peers.get(&(peer, set_id)) - { + if let Some(PeerState::Enabled { connections, .. }) = notif.peers.get(&(peer, set_id)) { assert_eq!(connections[0], (conn1, ConnectionState::Opening)); assert_eq!(connections[1], (conn2, ConnectionState::Opening)); } else { @@ -3297,8 +3292,7 @@ mod tests { )); } - if let Some(&PeerState::Disabled { ref connections, .. }) = notif.peers.get(&(peer, set_id)) - { + if let Some(PeerState::Disabled { connections, .. }) = notif.peers.get(&(peer, set_id)) { assert_eq!(connections[0], (conn1, ConnectionState::Closed)); assert_eq!(connections[1], (conn2, ConnectionState::Closed)); } else { @@ -3356,8 +3350,7 @@ mod tests { )); } - if let Some(&PeerState::Disabled { ref connections, .. }) = notif.peers.get(&(peer, set_id)) - { + if let Some(PeerState::Disabled { connections, .. }) = notif.peers.get(&(peer, set_id)) { assert_eq!(connections[0], (conn1, ConnectionState::Closed)); assert_eq!(connections[1], (conn2, ConnectionState::Closed)); } else { @@ -3413,8 +3406,7 @@ mod tests { )); } - if let Some(&PeerState::Disabled { ref connections, .. }) = notif.peers.get(&(peer, set_id)) - { + if let Some(PeerState::Disabled { connections, .. }) = notif.peers.get(&(peer, set_id)) { assert_eq!(connections[0], (conn1, ConnectionState::Closed)); assert_eq!(connections[1], (conn2, ConnectionState::Closed)); } else { @@ -3424,8 +3416,7 @@ mod tests { // open substreams on both active connections notif.peerset_report_connect(peer, set_id); - if let Some(&PeerState::Enabled { ref connections, .. }) = notif.peers.get(&(peer, set_id)) - { + if let Some(PeerState::Enabled { connections, .. }) = notif.peers.get(&(peer, set_id)) { assert_eq!(connections[0], (conn1, ConnectionState::Opening)); assert_eq!(connections[1], (conn2, ConnectionState::Closed)); } else { diff --git a/client/network/sync/src/blocks.rs b/client/network/sync/src/blocks.rs index 3c76238be..240c1ca1f 100644 --- a/client/network/sync/src/blocks.rs +++ b/client/network/sync/src/blocks.rs @@ -89,7 +89,7 @@ impl BlockCollection { Some(&BlockRangeState::Downloading { .. }) => { trace!(target: "sync", "Inserting block data still marked as being downloaded: {}", start); }, - Some(&BlockRangeState::Complete(ref existing)) if existing.len() >= blocks.len() => { + Some(BlockRangeState::Complete(existing)) if existing.len() >= blocks.len() => { trace!(target: "sync", "Ignored block data already downloaded: {}", start); return }, diff --git a/client/network/transactions/src/lib.rs b/client/network/transactions/src/lib.rs index 381dd654b..f57556d39 100644 --- a/client/network/transactions/src/lib.rs +++ b/client/network/transactions/src/lib.rs @@ -473,7 +473,7 @@ where let (hashes, to_send): (Vec<_>, Vec<_>) = transactions .iter() - .filter(|&(ref hash, _)| peer.known_transactions.insert(hash.clone())) + .filter(|(hash, _)| peer.known_transactions.insert(hash.clone())) .cloned() .unzip(); diff --git a/client/service/test/src/lib.rs b/client/service/test/src/lib.rs index 0f20376fe..29e68261a 100644 --- a/client/service/test/src/lib.rs +++ b/client/service/test/src/lib.rs @@ -182,10 +182,7 @@ where loop { interval.tick().await; - if full_nodes - .iter() - .all(|&(ref id, ref service, _, _)| full_predicate(*id, service)) - { + if full_nodes.iter().all(|(id, service, _, _)| full_predicate(*id, service)) { break } } diff --git a/frame/election-provider-multi-phase/src/lib.rs b/frame/election-provider-multi-phase/src/lib.rs index 80bab6807..359903710 100644 --- a/frame/election-provider-multi-phase/src/lib.rs +++ b/frame/election-provider-multi-phase/src/lib.rs @@ -502,10 +502,10 @@ where fn eq(&self, other: &Self) -> bool { use ElectionError::*; match (self, other) { - (&Feasibility(ref x), &Feasibility(ref y)) if x == y => true, - (&Miner(ref x), &Miner(ref y)) if x == y => true, - (&DataProvider(ref x), &DataProvider(ref y)) if x == y => true, - (&Fallback(ref x), &Fallback(ref y)) if x == y => true, + (Feasibility(x), Feasibility(y)) if x == y => true, + (Miner(x), Miner(y)) if x == y => true, + (DataProvider(x), DataProvider(y)) if x == y => true, + (Fallback(x), Fallback(y)) if x == y => true, _ => false, } } diff --git a/frame/offences/src/lib.rs b/frame/offences/src/lib.rs index c89d93c12..96c7c119c 100644 --- a/frame/offences/src/lib.rs +++ b/frame/offences/src/lib.rs @@ -247,7 +247,7 @@ impl> ReportIndexStorage { fn insert(&mut self, time_slot: &O::TimeSlot, report_id: ReportIdOf) { // Insert the report id into the list while maintaining the ordering by the time // slot. - let pos = self.same_kind_reports.partition_point(|&(ref when, _)| when <= time_slot); + let pos = self.same_kind_reports.partition_point(|(when, _)| when <= time_slot); self.same_kind_reports.insert(pos, (time_slot.clone(), report_id)); // Update the list of concurrent reports. diff --git a/frame/session/src/lib.rs b/frame/session/src/lib.rs index ed5f80e60..a9f89412a 100644 --- a/frame/session/src/lib.rs +++ b/frame/session/src/lib.rs @@ -678,7 +678,7 @@ impl Pallet { // since a new validator set always leads to `changed` starting // as true, we can ensure that `now_session_keys` and `next_validators` // have the same length. this function is called once per iteration. - if let Some(&(_, ref old_keys)) = now_session_keys.next() { + if let Some((_, old_keys)) = now_session_keys.next() { if old_keys != keys { changed = true; } diff --git a/frame/staking/src/pallet/impls.rs b/frame/staking/src/pallet/impls.rs index 760345e8d..d5072476f 100644 --- a/frame/staking/src/pallet/impls.rs +++ b/frame/staking/src/pallet/impls.rs @@ -1299,8 +1299,8 @@ where add_db_reads_writes(1, 0); // Reverse because it's more likely to find reports from recent eras. - match eras.iter().rev().find(|&&(_, ref sesh)| sesh <= &slash_session) { - Some(&(ref slash_era, _)) => *slash_era, + match eras.iter().rev().find(|&(_, sesh)| sesh <= &slash_session) { + Some((slash_era, _)) => *slash_era, // Before bonding period. defensive - should be filtered out. None => return consumed_weight, } From 416b0f50bba519146ec7ea45a67980b45cd658e7 Mon Sep 17 00:00:00 2001 From: Alexandru Vasile <60601340+lexnv@users.noreply.github.com> Date: Tue, 11 Apr 2023 14:07:52 +0300 Subject: [PATCH 357/558] Metadata V15: Add Runtime API metadata (#13302) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * impl_runtime_apis: Generate getters for `metadata_at` functions Signed-off-by: Alexandru Vasile * runtime: Implement new `Metadata` runtime trait Signed-off-by: Alexandru Vasile * runtime: Move `metadata_at` functions to construct_runtime macro Signed-off-by: Alexandru Vasile * contruct_runtime: Use `OpaqueMetadata` from hidden imports Signed-off-by: Alexandru Vasile * Adjust testing Signed-off-by: Alexandru Vasile * frame/tests: Add tests for the new API Signed-off-by: Alexandru Vasile * primitives/proc-macro: Helper to extract documentation literals Signed-off-by: Alexandru Vasile * primitives/proc-macro: Helper to filter all `cfg` attributes Signed-off-by: Alexandru Vasile * primitives/proc-macro: Generate documentation getters for metadata Signed-off-by: Alexandru Vasile * primitives/proc-macro: Avoid trait collision with snake case methods Signed-off-by: Alexandru Vasile * proc-macro/tests: Check doc getters Signed-off-by: Alexandru Vasile * primitives/proc-macro: Generate metadata for runtime methods Signed-off-by: Alexandru Vasile * primitives/api: Export scale-info and frame-metadata Signed-off-by: Alexandru Vasile * primitives/proc-macro: Generate metadata for runtime traits Signed-off-by: Alexandru Vasile * frame/runtime: Expose metadata v15 internally Signed-off-by: Alexandru Vasile * test: Use metadata v15 from `lexnv/md_v15_test` branch Signed-off-by: Alexandru Vasile * primitives/proc-macro: Generate crate access one module up Signed-off-by: Alexandru Vasile * frame: Implement `runtime_metadata` for mocks and tests Signed-off-by: Alexandru Vasile * primitives/proc-macro: Fix warnings Signed-off-by: Alexandru Vasile * primitives/proc-macro: Add no-docs flag Signed-off-by: Alexandru Vasile * frame: Adjust more tests Signed-off-by: Alexandru Vasile * frame/tests: Check runtime metadata correctness Signed-off-by: Alexandru Vasile * frame/benchmarking: Adjust benchmarks Signed-off-by: Alexandru Vasile * frame/benchmarks: Adjust more benchmarks Signed-off-by: Alexandru Vasile * primitives/api: Fix clippy Signed-off-by: Alexandru Vasile * primitives/proc-macro: Generate runtime metadata on the `decl_runtime_apis` Signed-off-by: Alexandru Vasile * frame: Abuse Deref to resolve `runtime_metadata` Signed-off-by: Alexandru Vasile * Revert "frame: Implement `runtime_metadata` for mocks and tests" This reverts commit e4782de008148d1f9b2758e9a919314c0a62ed9a. Revert "frame: Adjust more tests" This reverts commit de1352c8c9af14f8f45218d500885567ea0cc84f. Revert "frame/benchmarking: Adjust benchmarks" This reverts commit ae85bbeca3ea1a4dc0ab12958fa903f8c07ba53e. Signed-off-by: Alexandru Vasile Revert "frame/benchmarks: Adjust more benchmarks" This reverts commit d37aa2205e80cceaa04e7f2971bb542d4ce8a6db. * primitives/proc-macro: Remove unused imports and function Signed-off-by: Alexandru Vasile * frame/support: Adjust runtime metadata test Signed-off-by: Alexandru Vasile * primitives/tests: Remove doc getter test Signed-off-by: Alexandru Vasile * frame/support: Enable `no-metadata-docs` feature from `sp-api` Signed-off-by: Alexandru Vasile * primitives/tests: Add `TypeInfo` for test::extrinsic Signed-off-by: Alexandru Vasile * primitives/api: Expose scale-info and frame-metadata Signed-off-by: Alexandru Vasile * Update frame-metadata to include v15 Signed-off-by: Alexandru Vasile * Fix merge conflicts Signed-off-by: Alexandru Vasile * frame/metadata_ir: Add IR for runtime API metadata Signed-off-by: Alexandru Vasile * frame/metadata_ir: Convert IR to V15 Signed-off-by: Alexandru Vasile * primitives/api: Collect IR metadata for runtime API Signed-off-by: Alexandru Vasile * primitives/api: Move `metadata_ir` from frame/support Signed-off-by: Alexandru Vasile * frame/tests: Adjust testing Signed-off-by: Alexandru Vasile * frame/tests: Adjust `metadata_versions` test Signed-off-by: Alexandru Vasile * primitives/runtime_metadata: Exclude default type parameters from methods Signed-off-by: Alexandru Vasile * Update primitives/api/proc-macro/src/runtime_metadata.rs Co-authored-by: Bastian Köcher * Update primitives/api/src/metadata_ir/types.rs Co-authored-by: Bastian Köcher * Update primitives/api/src/metadata_ir/mod.rs Co-authored-by: Bastian Köcher * Update primitives/api/proc-macro/src/utils.rs Co-authored-by: Bastian Köcher * Update primitives/api/proc-macro/src/runtime_metadata.rs Co-authored-by: Bastian Köcher * Update primitives/api/proc-macro/src/runtime_metadata.rs Co-authored-by: Bastian Köcher * Update primitives/api/proc-macro/src/runtime_metadata.rs Co-authored-by: Bastian Köcher * Update primitives/api/proc-macro/src/runtime_metadata.rs Co-authored-by: Bastian Köcher * Update primitives/api/proc-macro/src/runtime_metadata.rs Co-authored-by: Bastian Köcher * Update primitives/api/proc-macro/src/runtime_metadata.rs Co-authored-by: Bastian Köcher * primitives: Fix build Signed-off-by: Alexandru Vasile * primitives/metadata-ir: Move IR to dedicated crate Signed-off-by: Alexandru Vasile * primitives: Reexport metadata-ir and frame-metadata Signed-off-by: Alexandru Vasile * frame: Use apis field instead of runtime Signed-off-by: Alexandru Vasile * Better documentation for the `Deref` abstraction Signed-off-by: Alexandru Vasile * ui-tests: Check empty `impl_runtime_apis` Signed-off-by: Alexandru Vasile * primitives: Remove unneeded bounds on generic params Signed-off-by: Alexandru Vasile * primitives: Rename `collect_where_bounds` to `get_argument_type_param` Signed-off-by: Alexandru Vasile * primitives: Generate crate access per fn call Signed-off-by: Alexandru Vasile * Revert "primitives: Remove unneeded bounds on generic params" This reverts commit 5178e38cf21cfb481156eefd628d62989201d59a. * metadata-ir: Add no-std Signed-off-by: Alexandru Vasile * primitives: Adjust where bounds Signed-off-by: Alexandru Vasile * Change `frame-metadata` branch to "origin/main" Signed-off-by: Alexandru Vasile * Update to `main` from origin Signed-off-by: Alexandru Vasile * Update frame-metadata to crates.io v15.1 Signed-off-by: Alexandru Vasile * Revert "ui-tests: Check empty `impl_runtime_apis`" This reverts commit cf78a7190ad9cba3c3bb2e78dc3d0dc382b2fea9. * Move ui test to primitives/ui Signed-off-by: Alexandru Vasile * Update frame/support/test/tests/runtime_metadata.rs Co-authored-by: Bastian Köcher * Update primitives/api/proc-macro/src/runtime_metadata.rs Co-authored-by: Bastian Köcher * Test already covered by `empty_impl_runtime_apis_call.stderr` This reverts commit 3bafb294cbe9745569bf5e5a1a2e6b4a4c1aadc5. * Retriger CI Signed-off-by: Alexandru Vasile * Import `TokenStream` as `TokenStream2` Signed-off-by: Alexandru Vasile --------- Signed-off-by: Alexandru Vasile Co-authored-by: parity-processbot <> Co-authored-by: Bastian Köcher --- Cargo.lock | 19 +- Cargo.toml | 1 + frame/support/Cargo.toml | 4 +- .../src/construct_runtime/expand/metadata.rs | 21 +- .../procedural/src/construct_runtime/mod.rs | 25 ++ frame/support/src/lib.rs | 2 +- frame/support/test/Cargo.toml | 2 + frame/support/test/tests/pallet.rs | 2 +- frame/support/test/tests/runtime_metadata.rs | 222 ++++++++++++++ primitives/api/Cargo.toml | 7 +- primitives/api/proc-macro/Cargo.toml | 3 + .../api/proc-macro/src/decl_runtime_apis.rs | 25 +- .../api/proc-macro/src/impl_runtime_apis.rs | 19 +- primitives/api/proc-macro/src/lib.rs | 1 + .../api/proc-macro/src/runtime_metadata.rs | 271 ++++++++++++++++++ primitives/api/proc-macro/src/utils.rs | 69 ++++- primitives/api/src/lib.rs | 6 +- primitives/consensus/beefy/src/lib.rs | 2 +- primitives/metadata-ir/Cargo.toml | 28 ++ .../metadata-ir/src/lib.rs | 32 ++- .../metadata-ir/src}/types.rs | 71 +++++ .../metadata-ir/src}/v14.rs | 0 primitives/metadata-ir/src/v15.rs | 188 ++++++++++++ primitives/test-primitives/Cargo.toml | 2 + primitives/test-primitives/src/lib.rs | 2 +- 25 files changed, 993 insertions(+), 31 deletions(-) create mode 100644 frame/support/test/tests/runtime_metadata.rs create mode 100644 primitives/api/proc-macro/src/runtime_metadata.rs create mode 100644 primitives/metadata-ir/Cargo.toml rename frame/support/src/metadata_ir/mod.rs => primitives/metadata-ir/src/lib.rs (76%) rename {frame/support/src/metadata_ir => primitives/metadata-ir/src}/types.rs (83%) rename {frame/support/src/metadata_ir => primitives/metadata-ir/src}/v14.rs (100%) create mode 100644 primitives/metadata-ir/src/v15.rs diff --git a/Cargo.lock b/Cargo.lock index d4185103f..f55180f9c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2413,9 +2413,9 @@ dependencies = [ [[package]] name = "frame-metadata" -version = "15.0.0" +version = "15.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "df6bb8542ef006ef0de09a5c4420787d79823c0ed7924225822362fd2bf2ff2d" +checksum = "878babb0b136e731cc77ec2fd883ff02745ff21e6fb662729953d44923df009c" dependencies = [ "cfg-if", "parity-scale-codec", @@ -2525,6 +2525,7 @@ dependencies = [ "rustversion", "scale-info", "serde", + "sp-api", "sp-arithmetic", "sp-core", "sp-io", @@ -10156,8 +10157,10 @@ dependencies = [ "hash-db", "log", "parity-scale-codec", + "scale-info", "sp-api-proc-macro", "sp-core", + "sp-metadata-ir", "sp-runtime", "sp-state-machine", "sp-std", @@ -10172,6 +10175,7 @@ name = "sp-api-proc-macro" version = "4.0.0-dev" dependencies = [ "Inflector", + "assert_matches", "blake2", "expander", "proc-macro-crate", @@ -10591,6 +10595,16 @@ dependencies = [ "zstd", ] +[[package]] +name = "sp-metadata-ir" +version = "0.1.0" +dependencies = [ + "frame-metadata", + "parity-scale-codec", + "scale-info", + "sp-std", +] + [[package]] name = "sp-mmr-primitives" version = "4.0.0-dev" @@ -10842,6 +10856,7 @@ name = "sp-test-primitives" version = "2.0.0" dependencies = [ "parity-scale-codec", + "scale-info", "serde", "sp-application-crypto", "sp-core", diff --git a/Cargo.toml b/Cargo.toml index de562ad79..cae8976cc 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -202,6 +202,7 @@ members = [ "primitives/keystore", "primitives/maybe-compressed-blob", "primitives/merkle-mountain-range", + "primitives/metadata-ir", "primitives/npos-elections", "primitives/npos-elections/fuzzer", "primitives/offchain", diff --git a/frame/support/Cargo.toml b/frame/support/Cargo.toml index 820658372..7792fdf01 100644 --- a/frame/support/Cargo.toml +++ b/frame/support/Cargo.toml @@ -16,7 +16,7 @@ targets = ["x86_64-unknown-linux-gnu"] serde = { version = "1.0.136", optional = true, features = ["derive"] } codec = { package = "parity-scale-codec", version = "3.2.2", default-features = false, features = ["derive", "max-encoded-len"] } scale-info = { version = "2.1.1", default-features = false, features = ["derive"] } -frame-metadata = { version = "15.0.0", default-features = false, features = ["v14"] } +frame-metadata = { version = "15.1.0", default-features = false, features = ["v14", "v15-unstable"] } sp-api = { version = "4.0.0-dev", default-features = false, path = "../../primitives/api" } sp-std = { version = "5.0.0", default-features = false, path = "../../primitives/std" } sp-io = { version = "7.0.0", default-features = false, path = "../../primitives/io" } @@ -74,7 +74,7 @@ runtime-benchmarks = [] try-runtime = [] # By default some types have documentation, `no-metadata-docs` allows to reduce the documentation # in the metadata. -no-metadata-docs = ["frame-support-procedural/no-metadata-docs"] +no-metadata-docs = ["frame-support-procedural/no-metadata-docs", "sp-api/no-metadata-docs"] # By default some types have documentation, `full-metadata-docs` allows to add documentation to # more types in the metadata. full-metadata-docs = ["scale-info/docs"] diff --git a/frame/support/procedural/src/construct_runtime/expand/metadata.rs b/frame/support/procedural/src/construct_runtime/expand/metadata.rs index ba6a621af..81fc93ba3 100644 --- a/frame/support/procedural/src/construct_runtime/expand/metadata.rs +++ b/frame/support/procedural/src/construct_runtime/expand/metadata.rs @@ -77,6 +77,24 @@ pub fn expand_runtime_metadata( quote! { impl #runtime { fn metadata_ir() -> #scrate::metadata_ir::MetadataIR { + // Each runtime must expose the `runtime_metadata()` to fetch the runtime API metadata. + // The function is implemented by calling `impl_runtime_apis!`. + // + // However, the `construct_runtime!` may be called without calling `impl_runtime_apis!`. + // Rely on the `Deref` trait to differentiate between a runtime that implements + // APIs (by macro impl_runtime_apis!) and a runtime that is simply created (by macro construct_runtime!). + // + // Both `InternalConstructRuntime` and `InternalImplRuntimeApis` expose a `runtime_metadata()` function. + // `InternalConstructRuntime` is implemented by the `construct_runtime!` for Runtime references (`& Runtime`), + // while `InternalImplRuntimeApis` is implemented by the `impl_runtime_apis!` for Runtime (`Runtime`). + // + // Therefore, the `Deref` trait will resolve the `runtime_metadata` from `impl_runtime_apis!` + // when both macros are called; and will resolve an empty `runtime_metadata` when only the `construct_runtime!` + // is called. + // + // `Deref` needs a reference for resolving the function call. + let rt = #runtime; + #scrate::metadata_ir::MetadataIR { pallets: #scrate::sp_std::vec![ #(#pallets),* ], extrinsic: #scrate::metadata_ir::ExtrinsicMetadataIR { @@ -95,7 +113,8 @@ pub fn expand_runtime_metadata( }) .collect(), }, - ty: #scrate::scale_info::meta_type::<#runtime>() + ty: #scrate::scale_info::meta_type::<#runtime>(), + apis: (&rt).runtime_metadata(), } } diff --git a/frame/support/procedural/src/construct_runtime/mod.rs b/frame/support/procedural/src/construct_runtime/mod.rs index a0d414154..c5a48a980 100644 --- a/frame/support/procedural/src/construct_runtime/mod.rs +++ b/frame/support/procedural/src/construct_runtime/mod.rs @@ -296,6 +296,31 @@ fn construct_runtime_final_expansion( type RuntimeBlock = #block; } + // Each runtime must expose the `runtime_metadata()` to fetch the runtime API metadata. + // The function is implemented by calling `impl_runtime_apis!`. + // + // However, the `construct_runtime!` may be called without calling `impl_runtime_apis!`. + // Rely on the `Deref` trait to differentiate between a runtime that implements + // APIs (by macro impl_runtime_apis!) and a runtime that is simply created (by macro construct_runtime!). + // + // Both `InternalConstructRuntime` and `InternalImplRuntimeApis` expose a `runtime_metadata()` function. + // `InternalConstructRuntime` is implemented by the `construct_runtime!` for Runtime references (`& Runtime`), + // while `InternalImplRuntimeApis` is implemented by the `impl_runtime_apis!` for Runtime (`Runtime`). + // + // Therefore, the `Deref` trait will resolve the `runtime_metadata` from `impl_runtime_apis!` + // when both macros are called; and will resolve an empty `runtime_metadata` when only the `construct_runtime!` + // is called. + + #[doc(hidden)] + trait InternalConstructRuntime { + #[inline(always)] + fn runtime_metadata(&self) -> #scrate::sp_std::vec::Vec<#scrate::metadata_ir::RuntimeApiMetadataIR> { + Default::default() + } + } + #[doc(hidden)] + impl InternalConstructRuntime for &#name {} + #outer_event #outer_origin diff --git a/frame/support/src/lib.rs b/frame/support/src/lib.rs index ac897fef0..036bd9346 100644 --- a/frame/support/src/lib.rs +++ b/frame/support/src/lib.rs @@ -50,6 +50,7 @@ pub use paste; pub use scale_info; #[cfg(feature = "std")] pub use serde; +pub use sp_api::metadata_ir; pub use sp_core::{OpaqueMetadata, Void}; #[doc(hidden)] pub use sp_core_hashing_proc_macro; @@ -80,7 +81,6 @@ pub mod error; pub mod crypto; pub mod dispatch_context; pub mod instances; -pub mod metadata_ir; pub mod migrations; pub mod traits; pub mod weights; diff --git a/frame/support/test/Cargo.toml b/frame/support/test/Cargo.toml index 90ef243ee..ad4589c73 100644 --- a/frame/support/test/Cargo.toml +++ b/frame/support/test/Cargo.toml @@ -15,6 +15,7 @@ targets = ["x86_64-unknown-linux-gnu"] serde = { version = "1.0.136", default-features = false, features = ["derive"] } codec = { package = "parity-scale-codec", version = "3.2.2", default-features = false, features = ["derive"] } scale-info = { version = "2.1.1", default-features = false, features = ["derive"] } +sp-api = { version = "4.0.0-dev", default-features = false, path = "../../../primitives/api" } sp-arithmetic = { version = "6.0.0", default-features = false, path = "../../../primitives/arithmetic" } sp-io = { version = "7.0.0", path = "../../../primitives/io", default-features = false } sp-state-machine = { version = "0.13.0", optional = true, path = "../../../primitives/state-machine" } @@ -47,6 +48,7 @@ std = [ "sp-state-machine", "sp-arithmetic/std", "sp-version/std", + "sp-api/std", ] try-runtime = ["frame-support/try-runtime"] # WARNING: diff --git a/frame/support/test/tests/pallet.rs b/frame/support/test/tests/pallet.rs index 11f82c4c0..f1dc97223 100644 --- a/frame/support/test/tests/pallet.rs +++ b/frame/support/test/tests/pallet.rs @@ -1671,7 +1671,7 @@ fn metadata_at_version() { #[test] fn metadata_versions() { - assert_eq!(vec![LATEST_METADATA_VERSION], Runtime::metadata_versions()); + assert_eq!(vec![LATEST_METADATA_VERSION, u32::MAX], Runtime::metadata_versions()); } #[test] diff --git a/frame/support/test/tests/runtime_metadata.rs b/frame/support/test/tests/runtime_metadata.rs new file mode 100644 index 000000000..8c04c785a --- /dev/null +++ b/frame/support/test/tests/runtime_metadata.rs @@ -0,0 +1,222 @@ +// This file is part of Substrate. + +// Copyright (C) Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +use frame_support::{ + metadata_ir::{ + RuntimeApiMetadataIR, RuntimeApiMethodMetadataIR, RuntimeApiMethodParamMetadataIR, + }, + traits::ConstU32, +}; +use scale_info::{form::MetaForm, meta_type}; +use sp_runtime::traits::Block as BlockT; + +pub type BlockNumber = u64; +pub type Index = u64; +pub type Header = sp_runtime::generic::Header; +pub type Block = sp_runtime::generic::Block; +pub type UncheckedExtrinsic = sp_runtime::generic::UncheckedExtrinsic; + +impl frame_system::Config for Runtime { + type BlockWeights = (); + type BlockLength = (); + type DbWeight = (); + type BaseCallFilter = frame_support::traits::Everything; + type RuntimeOrigin = RuntimeOrigin; + type Index = u64; + type BlockNumber = u32; + type RuntimeCall = RuntimeCall; + type Hash = sp_runtime::testing::H256; + type Hashing = sp_runtime::traits::BlakeTwo256; + type AccountId = u64; + type Lookup = sp_runtime::traits::IdentityLookup; + type Header = Header; + type RuntimeEvent = RuntimeEvent; + type BlockHashCount = ConstU32<250>; + type Version = (); + type PalletInfo = PalletInfo; + type AccountData = (); + type OnNewAccount = (); + type OnKilledAccount = (); + type SystemWeightInfo = (); + type SS58Prefix = (); + type OnSetCode = (); + type MaxConsumers = ConstU32<16>; +} + +frame_support::construct_runtime!( + pub enum Runtime where + Block = Block, + NodeBlock = Block, + UncheckedExtrinsic = UncheckedExtrinsic + { + System: frame_system, + } +); + +sp_api::decl_runtime_apis! { + /// ApiWithCustomVersion trait documentation + /// + /// Documentation on multiline. + pub trait Api { + fn test(data: u64); + /// something_with_block. + fn something_with_block(block: Block) -> Block; + fn function_with_two_args(data: u64, block: Block); + fn same_name(); + fn wild_card(_: u32); + } +} + +sp_api::impl_runtime_apis! { + impl self::Api for Runtime { + fn test(_data: u64) { + unimplemented!() + } + + fn something_with_block(_: Block) -> Block { + unimplemented!() + } + + fn function_with_two_args(_: u64, _: Block) { + unimplemented!() + } + + fn same_name() {} + + fn wild_card(_: u32) {} + } + + impl sp_api::Core for Runtime { + fn version() -> sp_version::RuntimeVersion { + unimplemented!() + } + fn execute_block(_: Block) { + unimplemented!() + } + fn initialize_block(_: &::Header) { + unimplemented!() + } + } +} + +#[test] +fn runtime_metadata() { + fn maybe_docs(doc: Vec<&'static str>) -> Vec<&'static str> { + if cfg!(feature = "no-metadata-docs") { + vec![] + } else { + doc + } + } + + let expected_runtime_metadata = vec![ + RuntimeApiMetadataIR { + name: "Api", + methods: vec![ + RuntimeApiMethodMetadataIR { + name: "test", + inputs: vec![RuntimeApiMethodParamMetadataIR:: { + name: "data", + ty: meta_type::(), + }], + output: meta_type::<()>(), + docs: vec![], + }, + RuntimeApiMethodMetadataIR { + name: "something_with_block", + inputs: vec![RuntimeApiMethodParamMetadataIR:: { + name: "block", + ty: meta_type::(), + }], + output: meta_type::(), + docs: maybe_docs(vec![" something_with_block."]), + }, + RuntimeApiMethodMetadataIR { + name: "function_with_two_args", + inputs: vec![ + RuntimeApiMethodParamMetadataIR:: { + name: "data", + ty: meta_type::(), + }, + RuntimeApiMethodParamMetadataIR:: { + name: "block", + ty: meta_type::(), + }, + ], + output: meta_type::<()>(), + docs: vec![], + }, + RuntimeApiMethodMetadataIR { + name: "same_name", + inputs: vec![], + output: meta_type::<()>(), + docs: vec![], + }, + RuntimeApiMethodMetadataIR { + name: "wild_card", + inputs: vec![RuntimeApiMethodParamMetadataIR:: { + name: "_", + ty: meta_type::(), + }], + output: meta_type::<()>(), + docs: vec![], + }, + ], + docs: maybe_docs(vec![ + " ApiWithCustomVersion trait documentation", + "", + " Documentation on multiline.", + ]), + }, + RuntimeApiMetadataIR { + name: "Core", + methods: vec![ + RuntimeApiMethodMetadataIR { + name: "version", + inputs: vec![], + output: meta_type::(), + docs: maybe_docs(vec![" Returns the version of the runtime."]), + }, + RuntimeApiMethodMetadataIR { + name: "execute_block", + inputs: vec![RuntimeApiMethodParamMetadataIR:: { + name: "block", + ty: meta_type::(), + }], + output: meta_type::<()>(), + docs: maybe_docs(vec![" Execute the given block."]), + }, + RuntimeApiMethodMetadataIR { + name: "initialize_block", + inputs: vec![RuntimeApiMethodParamMetadataIR:: { + name: "header", + ty: meta_type::<&::Header>(), + }], + output: meta_type::<()>(), + docs: maybe_docs(vec![" Initialize a block with the given header."]), + }, + ], + docs: maybe_docs(vec![ + " The `Core` runtime api that every Substrate runtime needs to implement.", + ]), + }, + ]; + + let rt = Runtime; + let runtime_metadata = (&rt).runtime_metadata(); + assert_eq!(runtime_metadata, expected_runtime_metadata); +} diff --git a/primitives/api/Cargo.toml b/primitives/api/Cargo.toml index ae1b3294c..55e399346 100644 --- a/primitives/api/Cargo.toml +++ b/primitives/api/Cargo.toml @@ -23,7 +23,8 @@ sp-state-machine = { version = "0.13.0", default-features = false, optional = tr sp-trie = { version = "7.0.0", default-features = false, optional = true, path = "../trie" } hash-db = { version = "0.16.0", optional = true } thiserror = { version = "1.0.30", optional = true } - +scale-info = { version = "2.1.1", default-features = false, features = ["derive"] } +sp-metadata-ir = { version = "0.1.0", default-features = false, path = "../metadata-ir" } log = { version = "0.4.17", default-features = false } [dev-dependencies] @@ -42,6 +43,8 @@ std = [ "hash-db", "thiserror", "log/std", + "scale-info/std", + "sp-metadata-ir/std", ] # Special feature to disable logging completly. # @@ -51,3 +54,5 @@ std = [ # # This sets the max logging level to `off` for `log`. disable-logging = ["log/max_level_off"] +# Do not report the documentation in the metadata. +no-metadata-docs = [] diff --git a/primitives/api/proc-macro/Cargo.toml b/primitives/api/proc-macro/Cargo.toml index ba7c63120..1ba62cc53 100644 --- a/primitives/api/proc-macro/Cargo.toml +++ b/primitives/api/proc-macro/Cargo.toml @@ -24,6 +24,9 @@ proc-macro-crate = "1.1.3" expander = "1.0.0" Inflector = "0.11.4" +[dev-dependencies] +assert_matches = "1.3.0" + # Required for the doc tests [features] default = ["std"] diff --git a/primitives/api/proc-macro/src/decl_runtime_apis.rs b/primitives/api/proc-macro/src/decl_runtime_apis.rs index 3c3056d34..43e1e7969 100644 --- a/primitives/api/proc-macro/src/decl_runtime_apis.rs +++ b/primitives/api/proc-macro/src/decl_runtime_apis.rs @@ -15,16 +15,18 @@ // See the License for the specific language governing permissions and // limitations under the License. -use crate::utils::{ - extract_parameter_names_types_and_borrows, fold_fn_decl_for_client_side, generate_crate_access, - generate_runtime_mod_name_for_trait, parse_runtime_api_version, prefix_function_with_trait, - replace_wild_card_parameter_names, return_type_extract_type, versioned_trait_name, - AllowSelfRefInParameters, -}; - -use crate::common::{ - API_VERSION_ATTRIBUTE, BLOCK_GENERIC_IDENT, CHANGED_IN_ATTRIBUTE, CORE_TRAIT_ATTRIBUTE, - RENAMED_ATTRIBUTE, SUPPORTED_ATTRIBUTE_NAMES, +use crate::{ + common::{ + API_VERSION_ATTRIBUTE, BLOCK_GENERIC_IDENT, CHANGED_IN_ATTRIBUTE, CORE_TRAIT_ATTRIBUTE, + RENAMED_ATTRIBUTE, SUPPORTED_ATTRIBUTE_NAMES, + }, + runtime_metadata::generate_decl_runtime_metadata, + utils::{ + extract_parameter_names_types_and_borrows, fold_fn_decl_for_client_side, + generate_crate_access, generate_runtime_mod_name_for_trait, parse_runtime_api_version, + prefix_function_with_trait, replace_wild_card_parameter_names, return_type_extract_type, + versioned_trait_name, AllowSelfRefInParameters, + }, }; use proc_macro2::{Span, TokenStream}; @@ -219,6 +221,7 @@ fn generate_runtime_decls(decls: &[ItemTrait]) -> Result { let mut decl = decl.clone(); let decl_span = decl.span(); extend_generics_with_block(&mut decl.generics); + let metadata = generate_decl_runtime_metadata(&decl); let mod_name = generate_runtime_mod_name_for_trait(&decl.ident); let found_attributes = remove_supported_attributes(&mut decl.attrs); let api_version = @@ -304,6 +307,8 @@ fn generate_runtime_decls(decls: &[ItemTrait]) -> Result { pub use #versioned_ident as #main_api_ident; + #metadata + pub #api_version pub #id diff --git a/primitives/api/proc-macro/src/impl_runtime_apis.rs b/primitives/api/proc-macro/src/impl_runtime_apis.rs index 0d265293e..c0da8ccf3 100644 --- a/primitives/api/proc-macro/src/impl_runtime_apis.rs +++ b/primitives/api/proc-macro/src/impl_runtime_apis.rs @@ -15,15 +15,17 @@ // See the License for the specific language governing permissions and // limitations under the License. -use crate::utils::{ - extract_all_signature_types, extract_block_type_from_trait_path, extract_impl_trait, - extract_parameter_names_types_and_borrows, generate_crate_access, - generate_runtime_mod_name_for_trait, parse_runtime_api_version, prefix_function_with_trait, - versioned_trait_name, AllowSelfRefInParameters, RequireQualifiedTraitPath, +use crate::{ + common::API_VERSION_ATTRIBUTE, + runtime_metadata::generate_impl_runtime_metadata, + utils::{ + extract_all_signature_types, extract_block_type_from_trait_path, extract_impl_trait, + extract_parameter_names_types_and_borrows, generate_crate_access, + generate_runtime_mod_name_for_trait, parse_runtime_api_version, prefix_function_with_trait, + versioned_trait_name, AllowSelfRefInParameters, RequireQualifiedTraitPath, + }, }; -use crate::common::API_VERSION_ATTRIBUTE; - use proc_macro2::{Span, TokenStream}; use quote::quote; @@ -685,6 +687,7 @@ fn impl_runtime_apis_impl_inner(api_impls: &[ItemImpl]) -> Result { let runtime_api_versions = generate_runtime_api_versions(api_impls)?; let wasm_interface = generate_wasm_interface(api_impls)?; let api_impls_for_runtime_api = generate_api_impl_for_runtime_api(api_impls)?; + let runtime_metadata = generate_impl_runtime_metadata(api_impls)?; let impl_ = quote!( #base_runtime_api @@ -695,6 +698,8 @@ fn impl_runtime_apis_impl_inner(api_impls: &[ItemImpl]) -> Result { #runtime_api_versions + #runtime_metadata + pub mod api { use super::*; diff --git a/primitives/api/proc-macro/src/lib.rs b/primitives/api/proc-macro/src/lib.rs index cea958426..d34f4b7f9 100644 --- a/primitives/api/proc-macro/src/lib.rs +++ b/primitives/api/proc-macro/src/lib.rs @@ -25,6 +25,7 @@ mod common; mod decl_runtime_apis; mod impl_runtime_apis; mod mock_impl_runtime_apis; +mod runtime_metadata; mod utils; #[proc_macro] diff --git a/primitives/api/proc-macro/src/runtime_metadata.rs b/primitives/api/proc-macro/src/runtime_metadata.rs new file mode 100644 index 000000000..02b03baea --- /dev/null +++ b/primitives/api/proc-macro/src/runtime_metadata.rs @@ -0,0 +1,271 @@ +// This file is part of Substrate. + +// Copyright (C) Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +use proc_macro2::TokenStream as TokenStream2; +use quote::quote; +use syn::{parse_quote, ItemImpl, ItemTrait, Result}; + +use crate::{ + common::CHANGED_IN_ATTRIBUTE, + utils::{ + extract_impl_trait, filter_cfg_attributes, generate_crate_access, + generate_runtime_mod_name_for_trait, get_doc_literals, RequireQualifiedTraitPath, + }, +}; + +/// Get the type parameter argument without lifetime or mutability +/// of a runtime metadata function. +/// +/// In the following example, both the `AccountId` and `Index` generic +/// type parameters must implement `scale_info::TypeInfo` because they +/// are added into the metadata using `scale_info::meta_type`. +/// +/// ```ignore +/// trait ExampleAccountNonceApi { +/// fn account_nonce<'a>(account: &'a AccountId) -> Index; +/// } +/// ``` +/// +/// Instead of returning `&'a AccountId` for the first parameter, this function +/// returns `AccountId` to place bounds around it. +fn get_type_param(ty: &syn::Type) -> syn::Type { + // Remove the lifetime and mutability of the type T to + // place bounds around it. + let ty_elem = match &ty { + syn::Type::Reference(reference) => &reference.elem, + syn::Type::Ptr(ptr) => &ptr.elem, + syn::Type::Slice(slice) => &slice.elem, + syn::Type::Array(arr) => &arr.elem, + _ => ty, + }; + + ty_elem.clone() +} + +/// Extract the documentation from the provided attributes. +/// +/// It takes into account the `no-metadata-docs` feature. +fn collect_docs(attrs: &[syn::Attribute], crate_: &TokenStream2) -> TokenStream2 { + if cfg!(feature = "no-metadata-docs") { + quote!(#crate_::vec![]) + } else { + let docs = get_doc_literals(&attrs); + quote!(#crate_::vec![ #( #docs, )* ]) + } +} + +/// Generate the runtime metadata of the provided trait. +/// +/// The metadata is exposed as a generic function on the hidden module +/// of the trait generated by the `decl_runtime_apis`. +pub fn generate_decl_runtime_metadata(decl: &ItemTrait) -> TokenStream2 { + let crate_ = generate_crate_access(); + let mut methods = Vec::new(); + + // Ensure that any function parameter that relies on the `BlockT` bounds + // also has `TypeInfo + 'static` bounds (required by `scale_info::meta_type`). + // + // For example, if a runtime API defines a method that has an input: + // `fn func(input: ::Header)` + // then the runtime metadata will imply `::Header: TypeInfo + 'static`. + // + // This restricts the bounds at the metadata level, without needing to modify the `BlockT` + // itself, since the concrete implementations are already satisfying `TypeInfo`. + let mut where_clause = Vec::new(); + for item in &decl.items { + // Collect metadata for methods only. + let syn::TraitItem::Method(method) = item else { + continue + }; + + // Collect metadata only for the latest methods. + let is_changed_in = + method.attrs.iter().any(|attr| attr.path.is_ident(CHANGED_IN_ATTRIBUTE)); + if is_changed_in { + continue + } + + let mut inputs = Vec::new(); + let signature = &method.sig; + for input in &signature.inputs { + // Exclude `self` from metadata collection. + let syn::FnArg::Typed(typed) = input else { + continue + }; + + let pat = &typed.pat; + let name = quote!(#pat).to_string(); + let ty = &typed.ty; + + where_clause.push(get_type_param(ty)); + + inputs.push(quote!( + #crate_::metadata_ir::RuntimeApiMethodParamMetadataIR { + name: #name, + ty: #crate_::scale_info::meta_type::<#ty>(), + } + )); + } + + let output = match &signature.output { + syn::ReturnType::Default => quote!(#crate_::scale_info::meta_type::<()>()), + syn::ReturnType::Type(_, ty) => { + where_clause.push(get_type_param(ty)); + quote!(#crate_::scale_info::meta_type::<#ty>()) + }, + }; + + // String method name including quotes for constructing `v15::RuntimeApiMethodMetadata`. + let method_name = signature.ident.to_string(); + let docs = collect_docs(&method.attrs, &crate_); + + // Include the method metadata only if its `cfg` features are enabled. + let attrs = filter_cfg_attributes(&method.attrs); + methods.push(quote!( + #( #attrs )* + #crate_::metadata_ir::RuntimeApiMethodMetadataIR { + name: #method_name, + inputs: #crate_::vec![ #( #inputs, )* ], + output: #output, + docs: #docs, + } + )); + } + + let trait_name_ident = &decl.ident; + let trait_name = trait_name_ident.to_string(); + let docs = collect_docs(&decl.attrs, &crate_); + let attrs = filter_cfg_attributes(&decl.attrs); + // The trait generics where already extended with `Block: BlockT`. + let mut generics = decl.generics.clone(); + for generic_param in generics.params.iter_mut() { + let syn::GenericParam::Type(ty) = generic_param else { + continue + }; + + // Default type parameters are not allowed in functions. + ty.eq_token = None; + ty.default = None; + } + + let where_clause = where_clause + .iter() + .map(|ty| quote!(#ty: #crate_::scale_info::TypeInfo + 'static)); + + quote!( + #( #attrs )* + #[inline(always)] + pub fn runtime_metadata #generics () -> #crate_::metadata_ir::RuntimeApiMetadataIR + where #( #where_clause, )* + { + #crate_::metadata_ir::RuntimeApiMetadataIR { + name: #trait_name, + methods: #crate_::vec![ #( #methods, )* ], + docs: #docs, + } + } + ) +} + +/// Implement the `runtime_metadata` function on the runtime that +/// generates the metadata for the given traits. +/// +/// The metadata of each trait is extracted from the generic function +/// exposed by `generate_decl_runtime_metadata`. +pub fn generate_impl_runtime_metadata(impls: &[ItemImpl]) -> Result { + if impls.is_empty() { + return Ok(quote!()) + } + + let crate_ = generate_crate_access(); + + // Get the name of the runtime for which the traits are implemented. + let runtime_name = &impls + .get(0) + .expect("Traits should contain at least one implementation; qed") + .self_ty; + + let mut metadata = Vec::new(); + + for impl_ in impls { + let mut trait_ = extract_impl_trait(&impl_, RequireQualifiedTraitPath::Yes)?.clone(); + + // Implementation traits are always references with a path `impl client::Core ...` + // The trait name is the last segment of this path. + let trait_name_ident = &trait_ + .segments + .last() + .as_ref() + .expect("Trait path should always contain at least one item; qed") + .ident; + + // Extract the generics from the trait to pass to the `runtime_metadata` + // function on the hidden module. + let generics = trait_ + .segments + .iter() + .find_map(|segment| { + if let syn::PathArguments::AngleBracketed(generics) = &segment.arguments { + Some(generics.clone()) + } else { + None + } + }) + .expect("Trait path should always contain at least one generic parameter; qed"); + + let mod_name = generate_runtime_mod_name_for_trait(&trait_name_ident); + // Get absolute path to the `runtime_decl_for_` module by replacing the last segment. + if let Some(segment) = trait_.segments.last_mut() { + *segment = parse_quote!(#mod_name); + } + + let attrs = filter_cfg_attributes(&impl_.attrs); + metadata.push(quote!( + #( #attrs )* + #trait_::runtime_metadata::#generics() + )); + } + + // Each runtime must expose the `runtime_metadata()` to fetch the runtime API metadata. + // The function is implemented by calling `impl_runtime_apis!`. + // + // However, the `construct_runtime!` may be called without calling `impl_runtime_apis!`. + // Rely on the `Deref` trait to differentiate between a runtime that implements + // APIs (by macro impl_runtime_apis!) and a runtime that is simply created (by macro + // construct_runtime!). + // + // Both `InternalConstructRuntime` and `InternalImplRuntimeApis` expose a `runtime_metadata()` + // function. `InternalConstructRuntime` is implemented by the `construct_runtime!` for Runtime + // references (`& Runtime`), while `InternalImplRuntimeApis` is implemented by the + // `impl_runtime_apis!` for Runtime (`Runtime`). + // + // Therefore, the `Deref` trait will resolve the `runtime_metadata` from `impl_runtime_apis!` + // when both macros are called; and will resolve an empty `runtime_metadata` when only the + // `construct_runtime!` is called. + + Ok(quote!( + #[doc(hidden)] + trait InternalImplRuntimeApis { + #[inline(always)] + fn runtime_metadata(&self) -> #crate_::vec::Vec<#crate_::metadata_ir::RuntimeApiMetadataIR> { + #crate_::vec![ #( #metadata, )* ] + } + } + #[doc(hidden)] + impl InternalImplRuntimeApis for #runtime_name {} + )) +} diff --git a/primitives/api/proc-macro/src/utils.rs b/primitives/api/proc-macro/src/utils.rs index 4444a2624..cffaf317f 100644 --- a/primitives/api/proc-macro/src/utils.rs +++ b/primitives/api/proc-macro/src/utils.rs @@ -253,7 +253,74 @@ pub fn parse_runtime_api_version(version: &Attribute) -> Result { version.base10_parse() } -// Each versioned trait is named 'ApiNameVN' where N is the specific version. E.g. ParachainHostV2 +/// Each versioned trait is named 'ApiNameVN' where N is the specific version. E.g. ParachainHostV2 pub fn versioned_trait_name(trait_ident: &Ident, version: u64) -> Ident { format_ident!("{}V{}", trait_ident, version) } + +/// Extract the documentation from the provided attributes. +pub fn get_doc_literals(attrs: &[syn::Attribute]) -> Vec { + attrs + .iter() + .filter_map(|attr| { + let Ok(syn::Meta::NameValue(meta)) = attr.parse_meta() else { + return None + }; + + meta.path.get_ident().filter(|ident| *ident == "doc").map(|_| meta.lit) + }) + .collect() +} + +/// Filters all attributes except the cfg ones. +pub fn filter_cfg_attributes(attrs: &[syn::Attribute]) -> Vec { + attrs.iter().filter(|a| a.path.is_ident("cfg")).cloned().collect() +} + +#[cfg(test)] +mod tests { + use assert_matches::assert_matches; + + use super::*; + + #[test] + fn check_get_doc_literals() { + const FIRST: &'static str = "hello"; + const SECOND: &'static str = "WORLD"; + + let doc: Attribute = parse_quote!(#[doc = #FIRST]); + let doc_world: Attribute = parse_quote!(#[doc = #SECOND]); + + let attrs = vec![ + doc.clone(), + parse_quote!(#[derive(Debug)]), + parse_quote!(#[test]), + parse_quote!(#[allow(non_camel_case_types)]), + doc_world.clone(), + ]; + + let docs = get_doc_literals(&attrs); + assert_eq!(docs.len(), 2); + assert_matches!(&docs[0], syn::Lit::Str(val) if val.value() == FIRST); + assert_matches!(&docs[1], syn::Lit::Str(val) if val.value() == SECOND); + } + + #[test] + fn check_filter_cfg_attributes() { + let cfg_std: Attribute = parse_quote!(#[cfg(feature = "std")]); + let cfg_benchmarks: Attribute = parse_quote!(#[cfg(feature = "runtime-benchmarks")]); + + let attrs = vec![ + cfg_std.clone(), + parse_quote!(#[derive(Debug)]), + parse_quote!(#[test]), + cfg_benchmarks.clone(), + parse_quote!(#[allow(non_camel_case_types)]), + ]; + + let filtered = filter_cfg_attributes(&attrs); + assert_eq!(filtered.len(), 2); + assert_eq!(cfg_std, filtered[0]); + assert_eq!(cfg_benchmarks, filtered[1]); + } +} diff --git a/primitives/api/src/lib.rs b/primitives/api/src/lib.rs index ff101c3ad..02770280f 100644 --- a/primitives/api/src/lib.rs +++ b/primitives/api/src/lib.rs @@ -76,12 +76,16 @@ pub use codec::{self, Decode, DecodeLimit, Encode}; #[cfg(feature = "std")] pub use hash_db::Hasher; #[doc(hidden)] +pub use scale_info; +#[doc(hidden)] #[cfg(not(feature = "std"))] pub use sp_core::to_substrate_wasm_fn_return_value; use sp_core::OpaqueMetadata; #[doc(hidden)] pub use sp_core::{offchain, ExecutionContext}; #[doc(hidden)] +pub use sp_metadata_ir::{self as metadata_ir, frame_metadata as metadata}; +#[doc(hidden)] #[cfg(feature = "std")] pub use sp_runtime::StateVersion; #[doc(hidden)] @@ -101,7 +105,7 @@ pub use sp_state_machine::{ StorageProof, TrieBackend, TrieBackendBuilder, }; #[doc(hidden)] -pub use sp_std::{mem, slice}; +pub use sp_std::{mem, slice, vec}; #[doc(hidden)] pub use sp_version::{create_apis_vec, ApiId, ApisVec, RuntimeVersion}; #[cfg(feature = "std")] diff --git a/primitives/consensus/beefy/src/lib.rs b/primitives/consensus/beefy/src/lib.rs index cc5d1e8cb..268e1925b 100644 --- a/primitives/consensus/beefy/src/lib.rs +++ b/primitives/consensus/beefy/src/lib.rs @@ -284,7 +284,7 @@ impl OnNewValidatorSet for () { /// the runtime API boundary this type is unknown and as such we keep this /// opaque representation, implementors of the runtime API will have to make /// sure that all usages of `OpaqueKeyOwnershipProof` refer to the same type. -#[derive(Decode, Encode, PartialEq)] +#[derive(Decode, Encode, PartialEq, TypeInfo)] pub struct OpaqueKeyOwnershipProof(Vec); impl OpaqueKeyOwnershipProof { /// Create a new `OpaqueKeyOwnershipProof` using the given encoded diff --git a/primitives/metadata-ir/Cargo.toml b/primitives/metadata-ir/Cargo.toml new file mode 100644 index 000000000..27fada9c6 --- /dev/null +++ b/primitives/metadata-ir/Cargo.toml @@ -0,0 +1,28 @@ +[package] +name = "sp-metadata-ir" +version = "0.1.0" +authors = ["Parity Technologies "] +edition = "2021" +license = "Apache-2.0" +homepage = "https://substrate.io" +repository = "https://github.com/paritytech/substrate/" +description = "Intermediate representation of the runtime metadata." +documentation = "https://docs.rs/sp-metadata-ir" + +[package.metadata.docs.rs] +targets = ["x86_64-unknown-linux-gnu"] + +[dependencies] +codec = { package = "parity-scale-codec", version = "3.2.2", default-features = false } +frame-metadata = { version = "15.1.0", default-features = false, features = ["v14", "v15-unstable"] } +scale-info = { version = "2.1.1", default-features = false, features = ["derive"] } +sp-std = { version = "5.0.0", default-features = false, path = "../std" } + +[features] +default = ["std"] +std = [ + "codec/std", + "frame-metadata/std", + "scale-info/std", + "sp-std/std", +] diff --git a/frame/support/src/metadata_ir/mod.rs b/primitives/metadata-ir/src/lib.rs similarity index 76% rename from frame/support/src/metadata_ir/mod.rs rename to primitives/metadata-ir/src/lib.rs index bab205d63..3ddc2911d 100644 --- a/frame/support/src/metadata_ir/mod.rs +++ b/primitives/metadata-ir/src/lib.rs @@ -17,15 +17,28 @@ //! Intermediate representation of the runtime metadata. +#![cfg_attr(not(feature = "std"), no_std)] +#![warn(missing_docs)] + +// Re-export. +#[doc(hidden)] +pub use frame_metadata; + mod types; use frame_metadata::{RuntimeMetadataPrefixed, RuntimeMetadataV14}; pub use types::*; mod v14; +mod v15; /// Metadata V14. const V14: u32 = 14; +/// Metadata V15. +/// +/// Not yet stable, thus we set it to `u32::MAX`. +const V15: u32 = u32::MAX; + /// Transform the IR to the specified version. /// /// Use [`supported_versions`] to find supported versions. @@ -36,13 +49,18 @@ pub fn into_version(metadata: MetadataIR, version: u32) -> Option { + let v15: frame_metadata::v15::RuntimeMetadataV15 = metadata.into(); + Some(v15.into()) + }, _ => None, } } /// Returns the supported metadata versions. pub fn supported_versions() -> sp_std::vec::Vec { - sp_std::vec![V14,] + sp_std::vec![V14, V15] } /// Transform the IR to the latest stable metadata version. @@ -54,7 +72,6 @@ pub fn into_latest(metadata: MetadataIR) -> RuntimeMetadataPrefixed { #[cfg(test)] mod test { use super::*; - use crate::metadata_ir::ExtrinsicMetadataIR; use frame_metadata::{v14::META_RESERVED, RuntimeMetadata}; use scale_info::meta_type; @@ -67,6 +84,7 @@ mod test { signed_extensions: vec![], }, ty: meta_type::<()>(), + apis: vec![], } } @@ -79,4 +97,14 @@ mod test { assert!(matches!(metadata.1, RuntimeMetadata::V14(_))); } + + #[test] + fn into_version_15() { + let ir = ir_metadata(); + let metadata = into_version(ir, V15).expect("Should return prefixed metadata"); + + assert_eq!(metadata.0, META_RESERVED); + + assert!(matches!(metadata.1, RuntimeMetadata::V15(_))); + } } diff --git a/frame/support/src/metadata_ir/types.rs b/primitives/metadata-ir/src/types.rs similarity index 83% rename from frame/support/src/metadata_ir/types.rs rename to primitives/metadata-ir/src/types.rs index 087fd3dca..93ee54891 100644 --- a/frame/support/src/metadata_ir/types.rs +++ b/primitives/metadata-ir/src/types.rs @@ -37,6 +37,77 @@ pub struct MetadataIR { pub extrinsic: ExtrinsicMetadataIR, /// The type of the `Runtime`. pub ty: T::Type, + /// Metadata of the Runtime API. + pub apis: Vec>, +} + +/// Metadata of a runtime trait. +#[derive(Clone, PartialEq, Eq, Encode, Debug)] +pub struct RuntimeApiMetadataIR { + /// Trait name. + pub name: T::String, + /// Trait methods. + pub methods: Vec>, + /// Trait documentation. + pub docs: Vec, +} + +impl IntoPortable for RuntimeApiMetadataIR { + type Output = RuntimeApiMetadataIR; + + fn into_portable(self, registry: &mut Registry) -> Self::Output { + RuntimeApiMetadataIR { + name: self.name.into_portable(registry), + methods: registry.map_into_portable(self.methods), + docs: registry.map_into_portable(self.docs), + } + } +} + +/// Metadata of a runtime method. +#[derive(Clone, PartialEq, Eq, Encode, Debug)] +pub struct RuntimeApiMethodMetadataIR { + /// Method name. + pub name: T::String, + /// Method parameters. + pub inputs: Vec>, + /// Method output. + pub output: T::Type, + /// Method documentation. + pub docs: Vec, +} + +impl IntoPortable for RuntimeApiMethodMetadataIR { + type Output = RuntimeApiMethodMetadataIR; + + fn into_portable(self, registry: &mut Registry) -> Self::Output { + RuntimeApiMethodMetadataIR { + name: self.name.into_portable(registry), + inputs: registry.map_into_portable(self.inputs), + output: registry.register_type(&self.output), + docs: registry.map_into_portable(self.docs), + } + } +} + +/// Metadata of a runtime method parameter. +#[derive(Clone, PartialEq, Eq, Encode, Debug)] +pub struct RuntimeApiMethodParamMetadataIR { + /// Parameter name. + pub name: T::String, + /// Parameter type. + pub ty: T::Type, +} + +impl IntoPortable for RuntimeApiMethodParamMetadataIR { + type Output = RuntimeApiMethodParamMetadataIR; + + fn into_portable(self, registry: &mut Registry) -> Self::Output { + RuntimeApiMethodParamMetadataIR { + name: self.name.into_portable(registry), + ty: registry.register_type(&self.ty), + } + } } /// The intermediate representation for a pallet metadata. diff --git a/frame/support/src/metadata_ir/v14.rs b/primitives/metadata-ir/src/v14.rs similarity index 100% rename from frame/support/src/metadata_ir/v14.rs rename to primitives/metadata-ir/src/v14.rs diff --git a/primitives/metadata-ir/src/v15.rs b/primitives/metadata-ir/src/v15.rs new file mode 100644 index 000000000..86441228d --- /dev/null +++ b/primitives/metadata-ir/src/v15.rs @@ -0,0 +1,188 @@ +// This file is part of Substrate. + +// Copyright (C) Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +//! Convert the IR to V15 metadata. + +use super::types::{ + ExtrinsicMetadataIR, MetadataIR, PalletCallMetadataIR, PalletConstantMetadataIR, + PalletErrorMetadataIR, PalletEventMetadataIR, PalletMetadataIR, PalletStorageMetadataIR, + RuntimeApiMetadataIR, RuntimeApiMethodMetadataIR, RuntimeApiMethodParamMetadataIR, + SignedExtensionMetadataIR, StorageEntryMetadataIR, StorageEntryModifierIR, StorageEntryTypeIR, + StorageHasherIR, +}; + +use frame_metadata::v15::{ + ExtrinsicMetadata, PalletCallMetadata, PalletConstantMetadata, PalletErrorMetadata, + PalletEventMetadata, PalletMetadata, PalletStorageMetadata, RuntimeApiMetadata, + RuntimeApiMethodMetadata, RuntimeApiMethodParamMetadata, RuntimeMetadataV15, + SignedExtensionMetadata, StorageEntryMetadata, StorageEntryModifier, StorageEntryType, + StorageHasher, +}; + +impl From for RuntimeMetadataV15 { + fn from(ir: MetadataIR) -> Self { + RuntimeMetadataV15::new( + ir.pallets.into_iter().map(Into::into).collect(), + ir.extrinsic.into(), + ir.ty, + ir.apis.into_iter().map(Into::into).collect(), + ) + } +} + +impl From for RuntimeApiMetadata { + fn from(ir: RuntimeApiMetadataIR) -> Self { + RuntimeApiMetadata { + name: ir.name, + methods: ir.methods.into_iter().map(Into::into).collect(), + docs: ir.docs, + } + } +} + +impl From for RuntimeApiMethodMetadata { + fn from(ir: RuntimeApiMethodMetadataIR) -> Self { + RuntimeApiMethodMetadata { + name: ir.name, + inputs: ir.inputs.into_iter().map(Into::into).collect(), + output: ir.output, + docs: ir.docs, + } + } +} + +impl From for RuntimeApiMethodParamMetadata { + fn from(ir: RuntimeApiMethodParamMetadataIR) -> Self { + RuntimeApiMethodParamMetadata { name: ir.name, ty: ir.ty } + } +} + +impl From for PalletMetadata { + fn from(ir: PalletMetadataIR) -> Self { + PalletMetadata { + name: ir.name, + storage: ir.storage.map(Into::into), + calls: ir.calls.map(Into::into), + event: ir.event.map(Into::into), + constants: ir.constants.into_iter().map(Into::into).collect(), + error: ir.error.map(Into::into), + index: ir.index, + docs: ir.docs, + } + } +} + +impl From for StorageEntryModifier { + fn from(ir: StorageEntryModifierIR) -> Self { + match ir { + StorageEntryModifierIR::Optional => StorageEntryModifier::Optional, + StorageEntryModifierIR::Default => StorageEntryModifier::Default, + } + } +} + +impl From for StorageHasher { + fn from(ir: StorageHasherIR) -> Self { + match ir { + StorageHasherIR::Blake2_128 => StorageHasher::Blake2_128, + StorageHasherIR::Blake2_256 => StorageHasher::Blake2_256, + StorageHasherIR::Blake2_128Concat => StorageHasher::Blake2_128Concat, + StorageHasherIR::Twox128 => StorageHasher::Twox128, + StorageHasherIR::Twox256 => StorageHasher::Twox256, + StorageHasherIR::Twox64Concat => StorageHasher::Twox64Concat, + StorageHasherIR::Identity => StorageHasher::Identity, + } + } +} + +impl From for StorageEntryType { + fn from(ir: StorageEntryTypeIR) -> Self { + match ir { + StorageEntryTypeIR::Plain(ty) => StorageEntryType::Plain(ty), + StorageEntryTypeIR::Map { hashers, key, value } => StorageEntryType::Map { + hashers: hashers.into_iter().map(Into::into).collect(), + key, + value, + }, + } + } +} + +impl From for StorageEntryMetadata { + fn from(ir: StorageEntryMetadataIR) -> Self { + StorageEntryMetadata { + name: ir.name, + modifier: ir.modifier.into(), + ty: ir.ty.into(), + default: ir.default, + docs: ir.docs, + } + } +} + +impl From for PalletStorageMetadata { + fn from(ir: PalletStorageMetadataIR) -> Self { + PalletStorageMetadata { + prefix: ir.prefix, + entries: ir.entries.into_iter().map(Into::into).collect(), + } + } +} + +impl From for PalletCallMetadata { + fn from(ir: PalletCallMetadataIR) -> Self { + PalletCallMetadata { ty: ir.ty } + } +} + +impl From for PalletEventMetadata { + fn from(ir: PalletEventMetadataIR) -> Self { + PalletEventMetadata { ty: ir.ty } + } +} + +impl From for PalletConstantMetadata { + fn from(ir: PalletConstantMetadataIR) -> Self { + PalletConstantMetadata { name: ir.name, ty: ir.ty, value: ir.value, docs: ir.docs } + } +} + +impl From for PalletErrorMetadata { + fn from(ir: PalletErrorMetadataIR) -> Self { + PalletErrorMetadata { ty: ir.ty } + } +} + +impl From for SignedExtensionMetadata { + fn from(ir: SignedExtensionMetadataIR) -> Self { + SignedExtensionMetadata { + identifier: ir.identifier, + ty: ir.ty, + additional_signed: ir.additional_signed, + } + } +} + +impl From for ExtrinsicMetadata { + fn from(ir: ExtrinsicMetadataIR) -> Self { + ExtrinsicMetadata { + ty: ir.ty, + version: ir.version, + signed_extensions: ir.signed_extensions.into_iter().map(Into::into).collect(), + } + } +} diff --git a/primitives/test-primitives/Cargo.toml b/primitives/test-primitives/Cargo.toml index deb120104..f6d57ce70 100644 --- a/primitives/test-primitives/Cargo.toml +++ b/primitives/test-primitives/Cargo.toml @@ -13,6 +13,7 @@ targets = ["x86_64-unknown-linux-gnu"] [dependencies] codec = { package = "parity-scale-codec", version = "3.2.2", default-features = false, features = ["derive"] } +scale-info = { version = "2.1.1", default-features = false, features = ["derive"] } serde = { version = "1.0.136", features = ["derive"], optional = true } sp-application-crypto = { version = "7.0.0", default-features = false, path = "../application-crypto" } sp-core = { version = "7.0.0", default-features = false, path = "../core" } @@ -28,4 +29,5 @@ std = [ "sp-application-crypto/std", "sp-core/std", "sp-runtime/std", + "scale-info/std", ] diff --git a/primitives/test-primitives/src/lib.rs b/primitives/test-primitives/src/lib.rs index 3a5f3dac4..035acc7a3 100644 --- a/primitives/test-primitives/src/lib.rs +++ b/primitives/test-primitives/src/lib.rs @@ -28,7 +28,7 @@ pub use sp_core::{hash::H256, RuntimeDebug}; use sp_runtime::traits::{BlakeTwo256, Extrinsic as ExtrinsicT, Verify}; /// Extrinsic for test-runtime. -#[derive(Clone, PartialEq, Eq, Encode, Decode, RuntimeDebug)] +#[derive(Clone, PartialEq, Eq, Encode, Decode, RuntimeDebug, scale_info::TypeInfo)] pub enum Extrinsic { IncludeData(Vec), StorageChange(Vec, Option>), From a2f6e05cc4e77734c2fa22167dbe20c845d0702e Mon Sep 17 00:00:00 2001 From: Oliver Tale-Yazdi Date: Tue, 11 Apr 2023 15:53:16 +0200 Subject: [PATCH 358/558] pallet nis: remove benchmarking hack (#13759) * pallet nis: remove benchmark hack Signed-off-by: Oliver Tale-Yazdi * NearestPrefDown -> Down Tested on the kitchensink and in case that n = 10000000000000000 and d = 12000100600000000000000 it rounds the Perquintill up (correctly) since 833326347280+47318160/60000503 should round up. That triggers the path 'amount <= on_hold' in L736. Signed-off-by: Oliver Tale-Yazdi --------- Signed-off-by: Oliver Tale-Yazdi --- frame/nis/src/benchmarking.rs | 6 ------ frame/nis/src/lib.rs | 5 ++--- 2 files changed, 2 insertions(+), 9 deletions(-) diff --git a/frame/nis/src/benchmarking.rs b/frame/nis/src/benchmarking.rs index 10fee2abe..0cc9e7421 100644 --- a/frame/nis/src/benchmarking.rs +++ b/frame/nis/src/benchmarking.rs @@ -164,9 +164,6 @@ benchmarks! { Nis::::place_bid(RawOrigin::Signed(caller.clone()).into(), bid, 1)?; Nis::::process_queues(Perquintill::one(), 1, 2, &mut WeightCounter::unlimited()); frame_system::Pallet::::set_block_number(Receipts::::get(0).unwrap().expiry); - // FIXME: Ensure that the pallet has enough funding. This should already be the case, but - // a rounding error can cause it to fail. - T::Currency::set_balance(&Nis::::account_id(), BalanceOf::::max_value() / 10u32.into()); }: _(RawOrigin::Signed(caller.clone()), 0, None) verify { assert!(Receipts::::get(0).is_none()); @@ -185,9 +182,6 @@ benchmarks! { Nis::::process_queues(Perquintill::one(), 1, 2, &mut WeightCounter::unlimited()); frame_system::Pallet::::set_block_number(Receipts::::get(0).unwrap().expiry); Nis::::communify(RawOrigin::Signed(caller.clone()).into(), 0)?; - // FIXME: Ensure that the pallet has enough funding. This should already be the case, but - // a rounding error can cause it to fail. - T::Currency::set_balance(&Nis::::account_id(), BalanceOf::::max_value() / 10u32.into()); }: _(RawOrigin::Signed(caller.clone()), 0) verify { assert!(Receipts::::get(0).is_none()); diff --git a/frame/nis/src/lib.rs b/frame/nis/src/lib.rs index 9cac5f4b6..c4d0d0d42 100644 --- a/frame/nis/src/lib.rs +++ b/frame/nis/src/lib.rs @@ -1139,9 +1139,8 @@ pub mod pallet { // Now to activate the bid... let n = amount; let d = issuance.effective; - let proportion = - Perquintill::from_rational_with_rounding(n, d, Rounding::NearestPrefDown) - .defensive_unwrap_or_default(); + let proportion = Perquintill::from_rational_with_rounding(n, d, Rounding::Down) + .defensive_unwrap_or_default(); let who = bid.who; let index = summary.index; summary.proportion_owed.defensive_saturating_accrue(proportion); From a1f71b8ef5dd531b101d2c9ec1a43f40715b4db4 Mon Sep 17 00:00:00 2001 From: Liam Aharon Date: Wed, 12 Apr 2023 00:17:25 +1000 Subject: [PATCH 359/558] Fix fungible and fungibles set_balance return value (#13851) * Fix fungible set_balance return value * fix fungibles set_balance return value --- frame/support/src/traits/tokens/fungible/regular.rs | 4 ++-- frame/support/src/traits/tokens/fungibles/regular.rs | 5 ++--- 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/frame/support/src/traits/tokens/fungible/regular.rs b/frame/support/src/traits/tokens/fungible/regular.rs index 574392cac..5a201b33b 100644 --- a/frame/support/src/traits/tokens/fungible/regular.rs +++ b/frame/support/src/traits/tokens/fungible/regular.rs @@ -327,9 +327,9 @@ pub trait Mutate: Inspect + Unbalanced { fn set_balance(who: &AccountId, amount: Self::Balance) -> Self::Balance { let b = Self::balance(who); if b > amount { - Self::burn_from(who, b - amount, BestEffort, Force).map(|d| amount.saturating_sub(d)) + Self::burn_from(who, b - amount, BestEffort, Force).map(|d| b.saturating_sub(d)) } else { - Self::mint_into(who, amount - b).map(|d| amount.saturating_add(d)) + Self::mint_into(who, amount - b).map(|d| b.saturating_add(d)) } .unwrap_or(b) } diff --git a/frame/support/src/traits/tokens/fungibles/regular.rs b/frame/support/src/traits/tokens/fungibles/regular.rs index 03c149880..27d1a50b3 100644 --- a/frame/support/src/traits/tokens/fungibles/regular.rs +++ b/frame/support/src/traits/tokens/fungibles/regular.rs @@ -365,10 +365,9 @@ pub trait Mutate: Inspect + Unbalanced { fn set_balance(asset: Self::AssetId, who: &AccountId, amount: Self::Balance) -> Self::Balance { let b = Self::balance(asset, who); if b > amount { - Self::burn_from(asset, who, b - amount, BestEffort, Force) - .map(|d| amount.saturating_sub(d)) + Self::burn_from(asset, who, b - amount, BestEffort, Force).map(|d| b.saturating_sub(d)) } else { - Self::mint_into(asset, who, amount - b).map(|d| amount.saturating_add(d)) + Self::mint_into(asset, who, amount - b).map(|d| b.saturating_add(d)) } .unwrap_or(b) } From ca6af5d19fc6439d2afae38d2ce68bf250663370 Mon Sep 17 00:00:00 2001 From: Mira Ressel Date: Tue, 11 Apr 2023 17:02:50 +0200 Subject: [PATCH 360/558] use stable rust toolchain in ci (#13830) cargo-fmt stays on the nightly pipeline; our fmt config uses a heap of unstable features. --- scripts/ci/gitlab/pipeline/build.yml | 3 +-- scripts/ci/gitlab/pipeline/test.yml | 27 +++++++++------------------ 2 files changed, 10 insertions(+), 20 deletions(-) diff --git a/scripts/ci/gitlab/pipeline/build.yml b/scripts/ci/gitlab/pipeline/build.yml index 0a36599c7..806a4ef97 100644 --- a/scripts/ci/gitlab/pipeline/build.yml +++ b/scripts/ci/gitlab/pipeline/build.yml @@ -148,7 +148,6 @@ build-rustdoc: variables: SKIP_WASM_BUILD: 1 DOC_INDEX_PAGE: "sc_service/index.html" # default redirected page - RUSTY_CACHIER_TOOLCHAIN: nightly # this variable gets overriden by "rusty-cachier environment inject", use the value as default CARGO_TARGET_DIR: "$CI_PROJECT_DIR/target" artifacts: @@ -163,7 +162,7 @@ build-rustdoc: artifacts: false script: - rusty-cachier snapshot create - - time cargo +nightly doc --locked --workspace --all-features --verbose --no-deps + - time cargo doc --locked --workspace --all-features --verbose --no-deps - rm -f $CARGO_TARGET_DIR/doc/.lock - mv $CARGO_TARGET_DIR/doc ./crate-docs # FIXME: remove me after CI image gets nonroot diff --git a/scripts/ci/gitlab/pipeline/test.yml b/scripts/ci/gitlab/pipeline/test.yml index 331b382e4..50f926215 100644 --- a/scripts/ci/gitlab/pipeline/test.yml +++ b/scripts/ci/gitlab/pipeline/test.yml @@ -61,21 +61,17 @@ cargo-clippy: needs: - job: cargo-fmt artifacts: false - variables: - RUSTY_CACHIER_TOOLCHAIN: nightly extends: - .docker-env - .test-refs script: - rusty-cachier snapshot create - - SKIP_WASM_BUILD=1 env -u RUSTFLAGS cargo +nightly clippy --all-targets + - SKIP_WASM_BUILD=1 env -u RUSTFLAGS cargo clippy --all-targets - rusty-cachier cache upload cargo-check-benches: stage: test variables: - # Override to use nightly toolchain - RUSTY_CACHIER_TOOLCHAIN: "nightly" CI_JOB_NAME: "cargo-check-benches" extends: - .docker-env @@ -105,7 +101,7 @@ cargo-check-benches: - echo "___Running benchmarks___"; - case ${CI_NODE_INDEX} in 1) - SKIP_WASM_BUILD=1 time cargo +nightly check --locked --benches --all; + SKIP_WASM_BUILD=1 time cargo check --locked --benches --all; cargo run --locked --release -p node-bench -- ::trie::read::small --json | tee ./artifacts/benches/$CI_COMMIT_REF_NAME-$CI_COMMIT_SHORT_SHA/::trie::read::small.json; echo "___Uploading cache for rusty-cachier___"; @@ -301,7 +297,6 @@ test-frame-examples-compile-to-wasm: - .docker-env - .test-refs variables: - RUSTY_CACHIER_TOOLCHAIN: nightly # Enable debug assertions since we are running optimized builds for testing # but still want to have debug assertions. RUSTFLAGS: "-C debug-assertions" @@ -309,9 +304,9 @@ test-frame-examples-compile-to-wasm: script: - rusty-cachier snapshot create - cd ./frame/examples/offchain-worker/ - - cargo +nightly build --locked --target=wasm32-unknown-unknown --no-default-features + - cargo build --locked --target=wasm32-unknown-unknown --no-default-features - cd ../basic - - cargo +nightly build --locked --target=wasm32-unknown-unknown --no-default-features + - cargo build --locked --target=wasm32-unknown-unknown --no-default-features - rusty-cachier cache upload test-linux-stable-int: @@ -344,8 +339,6 @@ check-tracing: needs: - job: test-linux-stable-int artifacts: false - variables: - RUSTY_CACHIER_TOOLCHAIN: nightly extends: - .docker-env - .test-refs @@ -353,8 +346,8 @@ check-tracing: script: - rusty-cachier snapshot create # with-tracing must be explicitly activated, we run a test to ensure this works as expected in both cases - - time cargo +nightly test --locked --manifest-path ./primitives/tracing/Cargo.toml --no-default-features - - time cargo +nightly test --locked --manifest-path ./primitives/tracing/Cargo.toml --no-default-features --features=with-tracing + - time cargo test --locked --manifest-path ./primitives/tracing/Cargo.toml --no-default-features + - time cargo test --locked --manifest-path ./primitives/tracing/Cargo.toml --no-default-features --features=with-tracing - rusty-cachier cache upload # more information about this job can be found here: @@ -369,7 +362,6 @@ test-full-crypto-feature: - .docker-env - .test-refs variables: - RUSTY_CACHIER_TOOLCHAIN: nightly # Enable debug assertions since we are running optimized builds for testing # but still want to have debug assertions. RUSTFLAGS: "-C debug-assertions" @@ -377,9 +369,9 @@ test-full-crypto-feature: script: - rusty-cachier snapshot create - cd primitives/core/ - - time cargo +nightly build --locked --verbose --no-default-features --features full_crypto + - time cargo build --locked --verbose --no-default-features --features full_crypto - cd ../application-crypto - - time cargo +nightly build --locked --verbose --no-default-features --features full_crypto + - time cargo build --locked --verbose --no-default-features --features full_crypto - rusty-cachier cache upload check-rustdoc: @@ -388,12 +380,11 @@ check-rustdoc: - .docker-env - .test-refs variables: - RUSTY_CACHIER_TOOLCHAIN: nightly SKIP_WASM_BUILD: 1 RUSTDOCFLAGS: "-Dwarnings" script: - rusty-cachier snapshot create - - time cargo +nightly doc --locked --workspace --all-features --verbose --no-deps + - time cargo doc --locked --workspace --all-features --verbose --no-deps - rusty-cachier cache upload cargo-check-each-crate: From da9f88dcac328d8f6de38dd61e33078c20bd6685 Mon Sep 17 00:00:00 2001 From: gupnik Date: Tue, 11 Apr 2023 20:40:10 +0530 Subject: [PATCH 361/558] Makes storage hashers optional in dev mode (#13815) * Initial changes * Adds UI test for error when _ is used without dev_mode * Minor * ".git/.scripts/commands/fmt/fmt.sh" * Adds test to verify hasher --------- Co-authored-by: command-bot <> --- .../procedural/src/pallet/expand/storage.rs | 13 +++ .../procedural/src/pallet/parse/storage.rs | 44 +++++++--- .../dev_mode_without_arg_default_hasher.rs | 33 +++++++ ...dev_mode_without_arg_default_hasher.stderr | 19 +++++ .../tests/pallet_ui/pass/dev_mode_valid.rs | 85 ++++++++++++++++++- 5 files changed, 182 insertions(+), 12 deletions(-) create mode 100644 frame/support/test/tests/pallet_ui/dev_mode_without_arg_default_hasher.rs create mode 100644 frame/support/test/tests/pallet_ui/dev_mode_without_arg_default_hasher.stderr diff --git a/frame/support/procedural/src/pallet/expand/storage.rs b/frame/support/procedural/src/pallet/expand/storage.rs index 8ba9ee768..efc803033 100644 --- a/frame/support/procedural/src/pallet/expand/storage.rs +++ b/frame/support/procedural/src/pallet/expand/storage.rs @@ -269,6 +269,19 @@ pub fn process_generics(def: &mut Def) -> syn::Result (5, 6, 7), }; + if storage_def.use_default_hasher { + let hasher_indices: Vec = match storage_def.metadata { + Metadata::Map { .. } | Metadata::CountedMap { .. } => vec![1], + Metadata::DoubleMap { .. } => vec![1, 3], + _ => vec![], + }; + for hasher_idx in hasher_indices { + args.args[hasher_idx] = syn::GenericArgument::Type( + syn::parse_quote!(#frame_support::Blake2_128Concat), + ); + } + } + if query_idx < args.args.len() { if let syn::GenericArgument::Type(query_kind) = args.args.index_mut(query_idx) { set_result_query_type_parameter(query_kind)?; diff --git a/frame/support/procedural/src/pallet/parse/storage.rs b/frame/support/procedural/src/pallet/parse/storage.rs index 3ed7ce54e..fee6ec9f1 100644 --- a/frame/support/procedural/src/pallet/parse/storage.rs +++ b/frame/support/procedural/src/pallet/parse/storage.rs @@ -185,6 +185,8 @@ pub struct StorageDef { pub unbounded: bool, /// Whether or not reads to this storage key will be ignored by benchmarking pub whitelisted: bool, + /// Whether or not a default hasher is allowed to replace `_` + pub use_default_hasher: bool, } /// The parsed generic from the @@ -325,12 +327,12 @@ fn check_generics( } } -/// Returns `(named generics, metadata, query kind)` +/// Returns `(named generics, metadata, query kind, use_default_hasher)` fn process_named_generics( storage: &StorageKind, args_span: proc_macro2::Span, args: &[syn::Binding], -) -> syn::Result<(Option, Metadata, Option)> { +) -> syn::Result<(Option, Metadata, Option, bool)> { let mut parsed = HashMap::::new(); // Ensure no duplicate. @@ -480,15 +482,16 @@ fn process_named_generics( let metadata = generics.metadata()?; let query_kind = generics.query_kind(); - Ok((Some(generics), metadata, query_kind)) + Ok((Some(generics), metadata, query_kind, false)) } -/// Returns `(named generics, metadata, query kind)` +/// Returns `(named generics, metadata, query kind, use_default_hasher)` fn process_unnamed_generics( storage: &StorageKind, args_span: proc_macro2::Span, args: &[syn::Type], -) -> syn::Result<(Option, Metadata, Option)> { + dev_mode: bool, +) -> syn::Result<(Option, Metadata, Option, bool)> { let retrieve_arg = |arg_pos| { args.get(arg_pos).cloned().ok_or_else(|| { let msg = format!( @@ -510,18 +513,28 @@ fn process_unnamed_generics( err })?; + let use_default_hasher = |arg_pos| { + if let Some(arg) = retrieve_arg(arg_pos).ok() { + dev_mode && syn::parse2::(arg.to_token_stream()).is_ok() + } else { + false + } + }; + let res = match storage { StorageKind::Value => - (None, Metadata::Value { value: retrieve_arg(1)? }, retrieve_arg(2).ok()), + (None, Metadata::Value { value: retrieve_arg(1)? }, retrieve_arg(2).ok(), false), StorageKind::Map => ( None, Metadata::Map { key: retrieve_arg(2)?, value: retrieve_arg(3)? }, retrieve_arg(4).ok(), + use_default_hasher(1), ), StorageKind::CountedMap => ( None, Metadata::CountedMap { key: retrieve_arg(2)?, value: retrieve_arg(3)? }, retrieve_arg(4).ok(), + use_default_hasher(1), ), StorageKind::DoubleMap => ( None, @@ -531,21 +544,28 @@ fn process_unnamed_generics( value: retrieve_arg(5)?, }, retrieve_arg(6).ok(), + use_default_hasher(1) && use_default_hasher(3), ), StorageKind::NMap => { let keygen = retrieve_arg(1)?; let keys = collect_keys(&keygen)?; - (None, Metadata::NMap { keys, keygen, value: retrieve_arg(2)? }, retrieve_arg(3).ok()) + ( + None, + Metadata::NMap { keys, keygen, value: retrieve_arg(2)? }, + retrieve_arg(3).ok(), + false, + ) }, }; Ok(res) } -/// Returns `(named generics, metadata, query kind)` +/// Returns `(named generics, metadata, query kind, use_default_hasher)` fn process_generics( segment: &syn::PathSegment, -) -> syn::Result<(Option, Metadata, Option)> { + dev_mode: bool, +) -> syn::Result<(Option, Metadata, Option, bool)> { let storage_kind = match &*segment.ident.to_string() { "StorageValue" => StorageKind::Value, "StorageMap" => StorageKind::Map, @@ -583,7 +603,7 @@ fn process_generics( _ => unreachable!("It is asserted above that all generics are types"), }) .collect::>(); - process_unnamed_generics(&storage_kind, args_span, &args) + process_unnamed_generics(&storage_kind, args_span, &args, dev_mode) } else if args.args.iter().all(|gen| matches!(gen, syn::GenericArgument::Binding(_))) { let args = args .args @@ -711,7 +731,8 @@ impl StorageDef { return Err(syn::Error::new(item.ty.span(), msg)) } - let (named_generics, metadata, query_kind) = process_generics(&typ.path.segments[0])?; + let (named_generics, metadata, query_kind, use_default_hasher) = + process_generics(&typ.path.segments[0], dev_mode)?; let query_kind = query_kind .map(|query_kind| { @@ -832,6 +853,7 @@ impl StorageDef { named_generics, unbounded, whitelisted, + use_default_hasher, }) } } diff --git a/frame/support/test/tests/pallet_ui/dev_mode_without_arg_default_hasher.rs b/frame/support/test/tests/pallet_ui/dev_mode_without_arg_default_hasher.rs new file mode 100644 index 000000000..fb1139479 --- /dev/null +++ b/frame/support/test/tests/pallet_ui/dev_mode_without_arg_default_hasher.rs @@ -0,0 +1,33 @@ +#![cfg_attr(not(feature = "std"), no_std)] + +pub use pallet::*; + +#[frame_support::pallet] +pub mod pallet { + use frame_support::pallet_prelude::*; + + // The struct on which we build all of our Pallet logic. + #[pallet::pallet] + pub struct Pallet(_); + + // Your Pallet's configuration trait, representing custom external types and interfaces. + #[pallet::config] + pub trait Config: frame_system::Config {} + + #[pallet::storage] + type MyStorage = StorageValue<_, Vec>; + + #[pallet::storage] + type MyStorageMap = StorageMap<_, _, u32, u64>; + + #[pallet::storage] + type MyStorageDoubleMap = StorageDoubleMap<_, _, u32, _, u64, u64>; + + #[pallet::storage] + type MyCountedStorageMap = CountedStorageMap<_, _, u32, u64>; + + // Your Pallet's internal functions. + impl Pallet {} +} + +fn main() {} diff --git a/frame/support/test/tests/pallet_ui/dev_mode_without_arg_default_hasher.stderr b/frame/support/test/tests/pallet_ui/dev_mode_without_arg_default_hasher.stderr new file mode 100644 index 000000000..5317f5c52 --- /dev/null +++ b/frame/support/test/tests/pallet_ui/dev_mode_without_arg_default_hasher.stderr @@ -0,0 +1,19 @@ +error[E0121]: the placeholder `_` is not allowed within types on item signatures for type aliases + --> tests/pallet_ui/dev_mode_without_arg_default_hasher.rs:21:47 + | +21 | type MyStorageMap = StorageMap<_, _, u32, u64>; + | ^ not allowed in type signatures + +error[E0121]: the placeholder `_` is not allowed within types on item signatures for type aliases + --> tests/pallet_ui/dev_mode_without_arg_default_hasher.rs:24:59 + | +24 | type MyStorageDoubleMap = StorageDoubleMap<_, _, u32, _, u64, u64>; + | ^ ^ not allowed in type signatures + | | + | not allowed in type signatures + +error[E0121]: the placeholder `_` is not allowed within types on item signatures for type aliases + --> tests/pallet_ui/dev_mode_without_arg_default_hasher.rs:27:61 + | +27 | type MyCountedStorageMap = CountedStorageMap<_, _, u32, u64>; + | ^ not allowed in type signatures diff --git a/frame/support/test/tests/pallet_ui/pass/dev_mode_valid.rs b/frame/support/test/tests/pallet_ui/pass/dev_mode_valid.rs index 97e0d585d..483ed9579 100644 --- a/frame/support/test/tests/pallet_ui/pass/dev_mode_valid.rs +++ b/frame/support/test/tests/pallet_ui/pass/dev_mode_valid.rs @@ -1,5 +1,11 @@ #![cfg_attr(not(feature = "std"), no_std)] +use frame_support::{ + traits::{ + ConstU32, + }, +}; + pub use pallet::*; #[frame_support::pallet(dev_mode)] @@ -19,6 +25,16 @@ pub mod pallet { #[pallet::storage] type MyStorage = StorageValue<_, Vec>; + // The Hasher requirement skipped by `dev_mode`. + #[pallet::storage] + pub type MyStorageMap = StorageMap<_, _, u32, u64>; + + #[pallet::storage] + type MyStorageDoubleMap = StorageDoubleMap<_, _, u32, _, u64, u64>; + + #[pallet::storage] + type MyCountedStorageMap = CountedStorageMap<_, _, u32, u64>; + // Your Pallet's callable functions. #[pallet::call] impl Pallet { @@ -32,4 +48,71 @@ pub mod pallet { impl Pallet {} } -fn main() {} +impl frame_system::Config for Runtime { + type BaseCallFilter = frame_support::traits::Everything; + type RuntimeOrigin = RuntimeOrigin; + type Index = u64; + type BlockNumber = u32; + type RuntimeCall = RuntimeCall; + type Hash = sp_runtime::testing::H256; + type Hashing = sp_runtime::traits::BlakeTwo256; + type AccountId = u64; + type Lookup = sp_runtime::traits::IdentityLookup; + type Header = Header; + type RuntimeEvent = RuntimeEvent; + type BlockHashCount = ConstU32<250>; + type BlockWeights = (); + type BlockLength = (); + type DbWeight = (); + type Version = (); + type PalletInfo = PalletInfo; + type AccountData = (); + type OnNewAccount = (); + type OnKilledAccount = (); + type SystemWeightInfo = (); + type SS58Prefix = (); + type OnSetCode = (); + type MaxConsumers = ConstU32<16>; +} + +pub type Header = sp_runtime::generic::Header; +pub type Block = sp_runtime::generic::Block; +pub type UncheckedExtrinsic = sp_runtime::generic::UncheckedExtrinsic; + +frame_support::construct_runtime!( + pub struct Runtime where + Block = Block, + NodeBlock = Block, + UncheckedExtrinsic = UncheckedExtrinsic + { + // Exclude part `Storage` in order not to check its metadata in tests. + System: frame_system exclude_parts { Pallet, Storage }, + Example: pallet, + } +); + +impl pallet::Config for Runtime { + +} + +fn main() { + use frame_support::{pallet_prelude::*}; + use storage::unhashed; + use sp_io::{ + hashing::{blake2_128, twox_128}, + TestExternalities, + }; + + fn blake2_128_concat(d: &[u8]) -> Vec { + let mut v = blake2_128(d).to_vec(); + v.extend_from_slice(d); + v + } + + TestExternalities::default().execute_with(|| { + pallet::MyStorageMap::::insert(1, 2); + let mut k = [twox_128(b"Example"), twox_128(b"MyStorageMap")].concat(); + k.extend(1u32.using_encoded(blake2_128_concat)); + assert_eq!(unhashed::get::(&k), Some(2u64)); + }); +} From 32221d47b68f38139323009615d69493338914e1 Mon Sep 17 00:00:00 2001 From: Gavin Wood Date: Tue, 11 Apr 2023 19:12:08 +0200 Subject: [PATCH 362/558] Force incrementing of consumers (#13878) --- frame/balances/src/lib.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/frame/balances/src/lib.rs b/frame/balances/src/lib.rs index dcf602cd5..029a17053 100644 --- a/frame/balances/src/lib.rs +++ b/frame/balances/src/lib.rs @@ -782,8 +782,8 @@ pub mod pallet { } a.flags.set_new_logic(); if !a.reserved.is_zero() || !a.frozen.is_zero() { - if !system::Pallet::::can_inc_consumer(who) { - // Gah!! We have a non-zero reserve balance but no provider refs :( + if system::Pallet::::providers(who) == 0 { + // Gah!! We have no provider refs :( // This shouldn't practically happen, but we need a failsafe anyway: let's give // them enough for an ED. log::warn!( @@ -794,7 +794,7 @@ pub mod pallet { a.free = a.free.max(Self::ed()); system::Pallet::::inc_providers(who); } - let _ = system::Pallet::::inc_consumers(who).defensive(); + let _ = system::Pallet::::inc_consumers_without_limit(who).defensive(); } // Should never fail - we're only setting a bit. let _ = T::AccountStore::try_mutate_exists(who, |account| -> DispatchResult { From 2b9ca86696bf87a63f8b9a8e48ebfed9d918fbcc Mon Sep 17 00:00:00 2001 From: gupnik Date: Wed, 12 Apr 2023 13:56:40 +0530 Subject: [PATCH 363/558] Fixes error message when _ is used without dev mode (#13886) * Initial changes * Adds UI test for error when _ is used without dev_mode * Minor * ".git/.scripts/commands/fmt/fmt.sh" * Adds test to verify hasher * Fixes error message when _ is used without dev mode * Updates test * Addresses review comment --------- Co-authored-by: command-bot <> --- .../procedural/src/pallet/parse/storage.rs | 18 ++++++++++----- ...dev_mode_without_arg_default_hasher.stderr | 22 ++++++------------- 2 files changed, 19 insertions(+), 21 deletions(-) diff --git a/frame/support/procedural/src/pallet/parse/storage.rs b/frame/support/procedural/src/pallet/parse/storage.rs index fee6ec9f1..4e90a423f 100644 --- a/frame/support/procedural/src/pallet/parse/storage.rs +++ b/frame/support/procedural/src/pallet/parse/storage.rs @@ -514,10 +514,16 @@ fn process_unnamed_generics( })?; let use_default_hasher = |arg_pos| { - if let Some(arg) = retrieve_arg(arg_pos).ok() { - dev_mode && syn::parse2::(arg.to_token_stream()).is_ok() + let arg = retrieve_arg(arg_pos)?; + if syn::parse2::(arg.to_token_stream()).is_ok() { + if dev_mode { + Ok(true) + } else { + let msg = "`_` can only be used in dev_mode. Please specify an appropriate hasher."; + Err(syn::Error::new(arg.span(), msg)) + } } else { - false + Ok(false) } }; @@ -528,13 +534,13 @@ fn process_unnamed_generics( None, Metadata::Map { key: retrieve_arg(2)?, value: retrieve_arg(3)? }, retrieve_arg(4).ok(), - use_default_hasher(1), + use_default_hasher(1)?, ), StorageKind::CountedMap => ( None, Metadata::CountedMap { key: retrieve_arg(2)?, value: retrieve_arg(3)? }, retrieve_arg(4).ok(), - use_default_hasher(1), + use_default_hasher(1)?, ), StorageKind::DoubleMap => ( None, @@ -544,7 +550,7 @@ fn process_unnamed_generics( value: retrieve_arg(5)?, }, retrieve_arg(6).ok(), - use_default_hasher(1) && use_default_hasher(3), + use_default_hasher(1)? && use_default_hasher(3)?, ), StorageKind::NMap => { let keygen = retrieve_arg(1)?; diff --git a/frame/support/test/tests/pallet_ui/dev_mode_without_arg_default_hasher.stderr b/frame/support/test/tests/pallet_ui/dev_mode_without_arg_default_hasher.stderr index 5317f5c52..e0dbc8c95 100644 --- a/frame/support/test/tests/pallet_ui/dev_mode_without_arg_default_hasher.stderr +++ b/frame/support/test/tests/pallet_ui/dev_mode_without_arg_default_hasher.stderr @@ -1,19 +1,11 @@ -error[E0121]: the placeholder `_` is not allowed within types on item signatures for type aliases +error: `_` can only be used in dev_mode. Please specify an appropriate hasher. --> tests/pallet_ui/dev_mode_without_arg_default_hasher.rs:21:47 | 21 | type MyStorageMap = StorageMap<_, _, u32, u64>; - | ^ not allowed in type signatures + | ^ -error[E0121]: the placeholder `_` is not allowed within types on item signatures for type aliases - --> tests/pallet_ui/dev_mode_without_arg_default_hasher.rs:24:59 - | -24 | type MyStorageDoubleMap = StorageDoubleMap<_, _, u32, _, u64, u64>; - | ^ ^ not allowed in type signatures - | | - | not allowed in type signatures - -error[E0121]: the placeholder `_` is not allowed within types on item signatures for type aliases - --> tests/pallet_ui/dev_mode_without_arg_default_hasher.rs:27:61 - | -27 | type MyCountedStorageMap = CountedStorageMap<_, _, u32, u64>; - | ^ not allowed in type signatures +error[E0432]: unresolved import `pallet` + --> tests/pallet_ui/dev_mode_without_arg_default_hasher.rs:3:9 + | +3 | pub use pallet::*; + | ^^^^^^ help: a similar path exists: `test_pallet::pallet` From 4d608f9c42e8d70d835a748fa929e59a99497e90 Mon Sep 17 00:00:00 2001 From: Mira Ressel Date: Wed, 12 Apr 2023 12:05:11 +0200 Subject: [PATCH 364/558] invoke clippy with `--locked` (#13882) --- scripts/ci/gitlab/pipeline/test.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/ci/gitlab/pipeline/test.yml b/scripts/ci/gitlab/pipeline/test.yml index 50f926215..bf7803d56 100644 --- a/scripts/ci/gitlab/pipeline/test.yml +++ b/scripts/ci/gitlab/pipeline/test.yml @@ -66,7 +66,7 @@ cargo-clippy: - .test-refs script: - rusty-cachier snapshot create - - SKIP_WASM_BUILD=1 env -u RUSTFLAGS cargo clippy --all-targets + - SKIP_WASM_BUILD=1 env -u RUSTFLAGS cargo clippy --locked --all-targets - rusty-cachier cache upload cargo-check-benches: From f751d63c1db7bbdb00930eba4ef2e2172ddac83a Mon Sep 17 00:00:00 2001 From: Adrian Catangiu Date: Wed, 12 Apr 2023 14:09:50 +0300 Subject: [PATCH 365/558] sc-consensus-beefy: add peer reputation cost/benefit changes (#13881) * add cost/benefit to gossip messages * report BEEFY gossip peer reputation changes * drop WorkerParams helper struct * add reputation costs to tests * add peer reputation cost/benefit to on-demand-requests protocol * include amount of signatures checked in invalid proof reputation cost Signed-off-by: Adrian Catangiu --- .../beefy/src/communication/gossip.rs | 264 ++++++++++++++---- .../consensus/beefy/src/communication/mod.rs | 33 +++ .../beefy/src/communication/peers.rs | 12 +- .../incoming_requests_handler.rs | 64 +++-- .../src/communication/request_response/mod.rs | 9 +- .../outgoing_requests_engine.rs | 76 +++-- client/consensus/beefy/src/import.rs | 1 + client/consensus/beefy/src/justification.rs | 40 +-- client/consensus/beefy/src/lib.rs | 19 +- client/consensus/beefy/src/metrics.rs | 10 +- client/consensus/beefy/src/tests.rs | 9 +- client/consensus/beefy/src/worker.rs | 118 +++----- 12 files changed, 430 insertions(+), 225 deletions(-) diff --git a/client/consensus/beefy/src/communication/gossip.rs b/client/consensus/beefy/src/communication/gossip.rs index 376172fc2..9be648f87 100644 --- a/client/consensus/beefy/src/communication/gossip.rs +++ b/client/consensus/beefy/src/communication/gossip.rs @@ -18,7 +18,7 @@ use std::{collections::BTreeMap, sync::Arc, time::Duration}; -use sc_network::PeerId; +use sc_network::{PeerId, ReputationChange}; use sc_network_gossip::{MessageIntent, ValidationResult, Validator, ValidatorContext}; use sp_core::hashing::twox_64; use sp_runtime::traits::{Block, Hash, Header, NumberFor}; @@ -26,10 +26,14 @@ use sp_runtime::traits::{Block, Hash, Header, NumberFor}; use codec::{Decode, Encode}; use log::{debug, trace}; use parking_lot::{Mutex, RwLock}; +use sc_utils::mpsc::{tracing_unbounded, TracingUnboundedReceiver, TracingUnboundedSender}; use wasm_timer::Instant; use crate::{ - communication::peers::KnownPeers, + communication::{ + benefit, cost, + peers::{KnownPeers, PeerReport}, + }, justification::{ proof_block_num_and_set_id, verify_with_validator_set, BeefyVersionedFinalityProof, }, @@ -47,6 +51,27 @@ const REBROADCAST_AFTER: Duration = Duration::from_secs(60); #[cfg(test)] const REBROADCAST_AFTER: Duration = Duration::from_secs(5); +#[derive(Debug, PartialEq)] +pub(super) enum Action { + // repropagate under given topic, to the given peers, applying cost/benefit to originator. + Keep(H, ReputationChange), + // discard, applying cost/benefit to originator. + Discard(ReputationChange), +} + +/// An outcome of examining a message. +#[derive(Debug, PartialEq, Clone, Copy)] +enum Consider { + /// Accept the message. + Accept, + /// Message is too early. Reject. + RejectPast, + /// Message is from the future. Reject. + RejectFuture, + /// Message cannot be evaluated. Reject. + RejectOutOfScope, +} + /// BEEFY gossip message type that gets encoded and sent on the network. #[derive(Debug, Encode, Decode)] pub(crate) enum GossipMessage { @@ -135,26 +160,47 @@ impl Filter { } } - /// Return true if `max(session_start, best_beefy) <= round <= best_grandpa`, + /// Accept if `max(session_start, best_beefy) <= round <= best_grandpa`, /// and vote `set_id` matches session set id. /// /// Latest concluded round is still considered alive to allow proper gossiping for it. - fn is_vote_accepted(&self, round: NumberFor, set_id: ValidatorSetId) -> bool { + fn consider_vote(&self, round: NumberFor, set_id: ValidatorSetId) -> Consider { self.inner .as_ref() - .map(|f| set_id == f.validator_set.id() && round >= f.start && round <= f.end) - .unwrap_or(false) + .map(|f| + // only from current set and only [filter.start, filter.end] + if set_id < f.validator_set.id() { + Consider::RejectPast + } else if set_id > f.validator_set.id() { + Consider::RejectFuture + } else if round < f.start { + Consider::RejectPast + } else if round > f.end { + Consider::RejectFuture + } else { + Consider::Accept + }) + .unwrap_or(Consider::RejectOutOfScope) } /// Return true if `round` is >= than `max(session_start, best_beefy)`, /// and proof `set_id` matches session set id. /// /// Latest concluded round is still considered alive to allow proper gossiping for it. - fn is_finality_proof_accepted(&self, round: NumberFor, set_id: ValidatorSetId) -> bool { + fn consider_finality_proof(&self, round: NumberFor, set_id: ValidatorSetId) -> Consider { self.inner .as_ref() - .map(|f| set_id == f.validator_set.id() && round >= f.start) - .unwrap_or(false) + .map(|f| + // only from current set and only >= filter.start + if round < f.start || set_id < f.validator_set.id() { + Consider::RejectPast + } else if set_id > f.validator_set.id() { + Consider::RejectFuture + } else { + Consider::Accept + } + ) + .unwrap_or(Consider::RejectOutOfScope) } /// Add new _known_ `hash` to the round's known votes. @@ -189,20 +235,26 @@ where gossip_filter: RwLock>, next_rebroadcast: Mutex, known_peers: Arc>>, + report_sender: TracingUnboundedSender, } impl GossipValidator where B: Block, { - pub fn new(known_peers: Arc>>) -> GossipValidator { - GossipValidator { + pub(crate) fn new( + known_peers: Arc>>, + ) -> (GossipValidator, TracingUnboundedReceiver) { + let (tx, rx) = tracing_unbounded("mpsc_beefy_gossip_validator", 10_000); + let val = GossipValidator { votes_topic: votes_topic::(), justifs_topic: proofs_topic::(), gossip_filter: RwLock::new(Filter::new()), next_rebroadcast: Mutex::new(Instant::now() + REBROADCAST_AFTER), known_peers, - } + report_sender: tx, + }; + (val, rx) } /// Update gossip validator filter. @@ -213,12 +265,16 @@ where self.gossip_filter.write().update(filter); } + fn report(&self, who: PeerId, cost_benefit: ReputationChange) { + let _ = self.report_sender.unbounded_send(PeerReport { who, cost_benefit }); + } + fn validate_vote( &self, vote: VoteMessage, AuthorityId, Signature>, sender: &PeerId, data: &[u8], - ) -> ValidationResult { + ) -> Action { let msg_hash = twox_64(data); let round = vote.commitment.block_number; let set_id = vote.commitment.validator_set_id; @@ -230,25 +286,37 @@ where { let filter = self.gossip_filter.read(); - if !filter.is_vote_accepted(round, set_id) { - return ValidationResult::Discard + match filter.consider_vote(round, set_id) { + Consider::RejectPast => return Action::Discard(cost::OUTDATED_MESSAGE), + Consider::RejectFuture => return Action::Discard(cost::FUTURE_MESSAGE), + Consider::RejectOutOfScope => return Action::Discard(cost::OUT_OF_SCOPE_MESSAGE), + Consider::Accept => {}, } if filter.is_known_vote(round, &msg_hash) { - return ValidationResult::ProcessAndKeep(self.votes_topic) + return Action::Keep(self.votes_topic, benefit::KNOWN_VOTE_MESSAGE) + } + + // ensure authority is part of the set. + if !filter + .validator_set() + .map(|set| set.validators().contains(&vote.id)) + .unwrap_or(false) + { + debug!(target: LOG_TARGET, "Message from voter not in validator set: {}", vote.id); + return Action::Discard(cost::UNKNOWN_VOTER) } } if BeefyKeystore::verify(&vote.id, &vote.signature, &vote.commitment.encode()) { self.gossip_filter.write().add_known_vote(round, msg_hash); - ValidationResult::ProcessAndKeep(self.votes_topic) + Action::Keep(self.votes_topic, benefit::VOTE_MESSAGE) } else { - // TODO: report peer debug!( target: LOG_TARGET, "🥩 Bad signature on message: {:?}, from: {:?}", vote, sender ); - ValidationResult::Discard + Action::Discard(cost::BAD_SIGNATURE) } } @@ -256,31 +324,38 @@ where &self, proof: BeefyVersionedFinalityProof, sender: &PeerId, - ) -> ValidationResult { + ) -> Action { let (round, set_id) = proof_block_num_and_set_id::(&proof); self.known_peers.lock().note_vote_for(*sender, round); let guard = self.gossip_filter.read(); - // Verify general usefulness of the justifications. - if !guard.is_finality_proof_accepted(round, set_id) { - return ValidationResult::Discard + // Verify general usefulness of the justification. + match guard.consider_finality_proof(round, set_id) { + Consider::RejectPast => return Action::Discard(cost::OUTDATED_MESSAGE), + Consider::RejectFuture => return Action::Discard(cost::FUTURE_MESSAGE), + Consider::RejectOutOfScope => return Action::Discard(cost::OUT_OF_SCOPE_MESSAGE), + Consider::Accept => {}, } // Verify justification signatures. guard .validator_set() .map(|validator_set| { - if let Ok(()) = verify_with_validator_set::(round, validator_set, &proof) { - ValidationResult::ProcessAndKeep(self.justifs_topic) - } else { - // TODO: report peer + if let Err((_, signatures_checked)) = + verify_with_validator_set::(round, validator_set, &proof) + { debug!( target: LOG_TARGET, "🥩 Bad signatures on message: {:?}, from: {:?}", proof, sender ); - ValidationResult::Discard + let mut cost = cost::INVALID_PROOF; + cost.value += + cost::PER_SIGNATURE_CHECKED.saturating_mul(signatures_checked as i32); + Action::Discard(cost) + } else { + Action::Keep(self.justifs_topic, benefit::VALIDATED_PROOF) } }) - .unwrap_or(ValidationResult::Discard) + .unwrap_or(Action::Discard(cost::OUT_OF_SCOPE_MESSAGE)) } } @@ -294,15 +369,32 @@ where fn validate( &self, - _context: &mut dyn ValidatorContext, + context: &mut dyn ValidatorContext, sender: &PeerId, mut data: &[u8], ) -> ValidationResult { - match GossipMessage::::decode(&mut data) { - Ok(GossipMessage::Vote(msg)) => self.validate_vote(msg, sender, data), + let raw = data; + let action = match GossipMessage::::decode(&mut data) { + Ok(GossipMessage::Vote(msg)) => self.validate_vote(msg, sender, raw), Ok(GossipMessage::FinalityProof(proof)) => self.validate_finality_proof(proof, sender), Err(e) => { debug!(target: LOG_TARGET, "Error decoding message: {}", e); + let bytes = raw.len().min(i32::MAX as usize) as i32; + let cost = ReputationChange::new( + bytes.saturating_mul(cost::PER_UNDECODABLE_BYTE), + "BEEFY: Bad packet", + ); + Action::Discard(cost) + }, + }; + match action { + Action::Keep(topic, cb) => { + self.report(*sender, cb); + context.broadcast_message(topic, data.to_vec(), false); + ValidationResult::ProcessAndKeep(topic) + }, + Action::Discard(cb) => { + self.report(*sender, cb); ValidationResult::Discard }, } @@ -314,13 +406,13 @@ where Ok(GossipMessage::Vote(msg)) => { let round = msg.commitment.block_number; let set_id = msg.commitment.validator_set_id; - let expired = !filter.is_vote_accepted(round, set_id); + let expired = filter.consider_vote(round, set_id) != Consider::Accept; trace!(target: LOG_TARGET, "🥩 Vote for round #{} expired: {}", round, expired); expired }, Ok(GossipMessage::FinalityProof(proof)) => { let (round, set_id) = proof_block_num_and_set_id::(&proof); - let expired = !filter.is_finality_proof_accepted(round, set_id); + let expired = filter.consider_finality_proof(round, set_id) != Consider::Accept; trace!( target: LOG_TARGET, "🥩 Finality proof for round #{} expired: {}", @@ -358,13 +450,13 @@ where Ok(GossipMessage::Vote(msg)) => { let round = msg.commitment.block_number; let set_id = msg.commitment.validator_set_id; - let allowed = filter.is_vote_accepted(round, set_id); + let allowed = filter.consider_vote(round, set_id) == Consider::Accept; trace!(target: LOG_TARGET, "🥩 Vote for round #{} allowed: {}", round, allowed); allowed }, Ok(GossipMessage::FinalityProof(proof)) => { let (round, set_id) = proof_block_num_and_set_id::(&proof); - let allowed = filter.is_finality_proof_accepted(round, set_id); + let allowed = filter.consider_finality_proof(round, set_id) == Consider::Accept; trace!( target: LOG_TARGET, "🥩 Finality proof for round #{} allowed: {}", @@ -409,15 +501,16 @@ pub(crate) mod tests { assert_eq!(filter.live_votes.len(), 3); assert!(filter.inner.is_none()); - assert!(!filter.is_vote_accepted(1, 1)); + assert_eq!(filter.consider_vote(1, 1), Consider::RejectOutOfScope); filter.update(GossipFilterCfg { start: 3, end: 10, validator_set: &validator_set }); assert_eq!(filter.live_votes.len(), 1); assert!(filter.live_votes.contains_key(&3)); - assert!(!filter.is_vote_accepted(2, 1)); - assert!(filter.is_vote_accepted(3, 1)); - assert!(filter.is_vote_accepted(4, 1)); - assert!(!filter.is_vote_accepted(4, 2)); + assert_eq!(filter.consider_vote(2, 1), Consider::RejectPast); + assert_eq!(filter.consider_vote(3, 1), Consider::Accept); + assert_eq!(filter.consider_vote(4, 1), Consider::Accept); + assert_eq!(filter.consider_vote(20, 1), Consider::RejectFuture); + assert_eq!(filter.consider_vote(4, 2), Consider::RejectFuture); let validator_set = ValidatorSet::::new(keys, 2).unwrap(); filter.update(GossipFilterCfg { start: 5, end: 10, validator_set: &validator_set }); @@ -430,9 +523,7 @@ pub(crate) mod tests { todo!() } - fn broadcast_message(&mut self, _topic: B::Hash, _message: Vec, _force: bool) { - todo!() - } + fn broadcast_message(&mut self, _topic: B::Hash, _message: Vec, _force: bool) {} fn send_message(&mut self, _who: &sc_network::PeerId, _message: Vec) { todo!() @@ -485,18 +576,39 @@ pub(crate) mod tests { fn should_validate_messages() { let keys = vec![Keyring::Alice.public()]; let validator_set = ValidatorSet::::new(keys.clone(), 0).unwrap(); - let gv = GossipValidator::::new(Arc::new(Mutex::new(KnownPeers::new()))); - gv.update_filter(GossipFilterCfg { start: 0, end: 10, validator_set: &validator_set }); - let sender = sc_network::PeerId::random(); + let (gv, mut report_stream) = + GossipValidator::::new(Arc::new(Mutex::new(KnownPeers::new()))); + let sender = PeerId::random(); let mut context = TestContext; + // reject message, decoding error + let bad_encoding = b"0000000000".as_slice(); + let expected_cost = ReputationChange::new( + (bad_encoding.len() as i32).saturating_mul(cost::PER_UNDECODABLE_BYTE), + "BEEFY: Bad packet", + ); + let mut expected_report = PeerReport { who: sender, cost_benefit: expected_cost }; + let res = gv.validate(&mut context, &sender, bad_encoding); + assert!(matches!(res, ValidationResult::Discard)); + assert_eq!(report_stream.try_recv().unwrap(), expected_report); + + // verify votes validation + let vote = dummy_vote(3); - let gossip_vote = GossipMessage::::Vote(vote.clone()); + let encoded = GossipMessage::::Vote(vote.clone()).encode(); - // first time the cache should be populated - let res = gv.validate(&mut context, &sender, &gossip_vote.encode()); + // filter not initialized + let res = gv.validate(&mut context, &sender, &encoded); + assert!(matches!(res, ValidationResult::Discard)); + expected_report.cost_benefit = cost::OUT_OF_SCOPE_MESSAGE; + assert_eq!(report_stream.try_recv().unwrap(), expected_report); + gv.update_filter(GossipFilterCfg { start: 0, end: 10, validator_set: &validator_set }); + // nothing in cache first time + let res = gv.validate(&mut context, &sender, &encoded); assert!(matches!(res, ValidationResult::ProcessAndKeep(_))); + expected_report.cost_benefit = benefit::VOTE_MESSAGE; + assert_eq!(report_stream.try_recv().unwrap(), expected_report); assert_eq!( gv.gossip_filter .read() @@ -507,43 +619,74 @@ pub(crate) mod tests { ); // second time we should hit the cache - let res = gv.validate(&mut context, &sender, &gossip_vote.encode()); + let res = gv.validate(&mut context, &sender, &encoded); assert!(matches!(res, ValidationResult::ProcessAndKeep(_))); + expected_report.cost_benefit = benefit::KNOWN_VOTE_MESSAGE; + assert_eq!(report_stream.try_recv().unwrap(), expected_report); + + // reject vote, voter not in validator set + let mut bad_vote = vote.clone(); + bad_vote.id = Keyring::Bob.public(); + let bad_vote = GossipMessage::::Vote(bad_vote).encode(); + let res = gv.validate(&mut context, &sender, &bad_vote); + assert!(matches!(res, ValidationResult::Discard)); + expected_report.cost_benefit = cost::UNKNOWN_VOTER; + assert_eq!(report_stream.try_recv().unwrap(), expected_report); - // next we should quickly reject if the round is not live - gv.update_filter(GossipFilterCfg { start: 7, end: 10, validator_set: &validator_set }); - + // reject if the round is not GRANDPA finalized + gv.update_filter(GossipFilterCfg { start: 1, end: 2, validator_set: &validator_set }); let number = vote.commitment.block_number; let set_id = vote.commitment.validator_set_id; - assert!(!gv.gossip_filter.read().is_vote_accepted(number, set_id)); + assert_eq!(gv.gossip_filter.read().consider_vote(number, set_id), Consider::RejectFuture); + let res = gv.validate(&mut context, &sender, &encoded); + assert!(matches!(res, ValidationResult::Discard)); + expected_report.cost_benefit = cost::FUTURE_MESSAGE; + assert_eq!(report_stream.try_recv().unwrap(), expected_report); - let res = gv.validate(&mut context, &sender, &vote.encode()); + // reject if the round is not live anymore + gv.update_filter(GossipFilterCfg { start: 7, end: 10, validator_set: &validator_set }); + let number = vote.commitment.block_number; + let set_id = vote.commitment.validator_set_id; + assert_eq!(gv.gossip_filter.read().consider_vote(number, set_id), Consider::RejectPast); + let res = gv.validate(&mut context, &sender, &encoded); assert!(matches!(res, ValidationResult::Discard)); + expected_report.cost_benefit = cost::OUTDATED_MESSAGE; + assert_eq!(report_stream.try_recv().unwrap(), expected_report); + + // now verify proofs validation // reject old proof let proof = dummy_proof(5, &validator_set); let encoded_proof = GossipMessage::::FinalityProof(proof).encode(); let res = gv.validate(&mut context, &sender, &encoded_proof); assert!(matches!(res, ValidationResult::Discard)); + expected_report.cost_benefit = cost::OUTDATED_MESSAGE; + assert_eq!(report_stream.try_recv().unwrap(), expected_report); // accept next proof with good set_id let proof = dummy_proof(7, &validator_set); let encoded_proof = GossipMessage::::FinalityProof(proof).encode(); let res = gv.validate(&mut context, &sender, &encoded_proof); assert!(matches!(res, ValidationResult::ProcessAndKeep(_))); + expected_report.cost_benefit = benefit::VALIDATED_PROOF; + assert_eq!(report_stream.try_recv().unwrap(), expected_report); // accept future proof with good set_id let proof = dummy_proof(20, &validator_set); let encoded_proof = GossipMessage::::FinalityProof(proof).encode(); let res = gv.validate(&mut context, &sender, &encoded_proof); assert!(matches!(res, ValidationResult::ProcessAndKeep(_))); + expected_report.cost_benefit = benefit::VALIDATED_PROOF; + assert_eq!(report_stream.try_recv().unwrap(), expected_report); - // reject proof, wrong set_id + // reject proof, future set_id let bad_validator_set = ValidatorSet::::new(keys, 1).unwrap(); let proof = dummy_proof(20, &bad_validator_set); let encoded_proof = GossipMessage::::FinalityProof(proof).encode(); let res = gv.validate(&mut context, &sender, &encoded_proof); assert!(matches!(res, ValidationResult::Discard)); + expected_report.cost_benefit = cost::FUTURE_MESSAGE; + assert_eq!(report_stream.try_recv().unwrap(), expected_report); // reject proof, bad signatures (Bob instead of Alice) let bad_validator_set = @@ -552,13 +695,16 @@ pub(crate) mod tests { let encoded_proof = GossipMessage::::FinalityProof(proof).encode(); let res = gv.validate(&mut context, &sender, &encoded_proof); assert!(matches!(res, ValidationResult::Discard)); + expected_report.cost_benefit = cost::INVALID_PROOF; + expected_report.cost_benefit.value += cost::PER_SIGNATURE_CHECKED; + assert_eq!(report_stream.try_recv().unwrap(), expected_report); } #[test] fn messages_allowed_and_expired() { let keys = vec![Keyring::Alice.public()]; let validator_set = ValidatorSet::::new(keys.clone(), 0).unwrap(); - let gv = GossipValidator::::new(Arc::new(Mutex::new(KnownPeers::new()))); + let (gv, _) = GossipValidator::::new(Arc::new(Mutex::new(KnownPeers::new()))); gv.update_filter(GossipFilterCfg { start: 0, end: 10, validator_set: &validator_set }); let sender = sc_network::PeerId::random(); let topic = Default::default(); @@ -635,7 +781,7 @@ pub(crate) mod tests { fn messages_rebroadcast() { let keys = vec![Keyring::Alice.public()]; let validator_set = ValidatorSet::::new(keys.clone(), 0).unwrap(); - let gv = GossipValidator::::new(Arc::new(Mutex::new(KnownPeers::new()))); + let (gv, _) = GossipValidator::::new(Arc::new(Mutex::new(KnownPeers::new()))); gv.update_filter(GossipFilterCfg { start: 0, end: 10, validator_set: &validator_set }); let sender = sc_network::PeerId::random(); let topic = Default::default(); diff --git a/client/consensus/beefy/src/communication/mod.rs b/client/consensus/beefy/src/communication/mod.rs index 13735a9d3..d8e4d2205 100644 --- a/client/consensus/beefy/src/communication/mod.rs +++ b/client/consensus/beefy/src/communication/mod.rs @@ -73,6 +73,39 @@ pub fn beefy_peers_set_config( cfg } +// cost scalars for reporting peers. +mod cost { + use sc_network::ReputationChange as Rep; + // Message that's for an outdated round. + pub(super) const OUTDATED_MESSAGE: Rep = Rep::new(-50, "BEEFY: Past message"); + // Message that's from the future relative to our current set-id. + pub(super) const FUTURE_MESSAGE: Rep = Rep::new(-100, "BEEFY: Future message"); + // Vote message containing bad signature. + pub(super) const BAD_SIGNATURE: Rep = Rep::new(-100, "BEEFY: Bad signature"); + // Message received with vote from voter not in validator set. + pub(super) const UNKNOWN_VOTER: Rep = Rep::new(-150, "BEEFY: Unknown voter"); + // A message received that cannot be evaluated relative to our current state. + pub(super) const OUT_OF_SCOPE_MESSAGE: Rep = Rep::new(-500, "BEEFY: Out-of-scope message"); + // Message containing invalid proof. + pub(super) const INVALID_PROOF: Rep = Rep::new(-5000, "BEEFY: Invalid commit"); + // Reputation cost per signature checked for invalid proof. + pub(super) const PER_SIGNATURE_CHECKED: i32 = -25; + // Reputation cost per byte for un-decodable message. + pub(super) const PER_UNDECODABLE_BYTE: i32 = -5; + // On-demand request was refused by peer. + pub(super) const REFUSAL_RESPONSE: Rep = Rep::new(-100, "BEEFY: Proof request refused"); + // On-demand request for a proof that can't be found in the backend. + pub(super) const UNKOWN_PROOF_REQUEST: Rep = Rep::new(-150, "BEEFY: Unknown proof request"); +} + +// benefit scalars for reporting peers. +mod benefit { + use sc_network::ReputationChange as Rep; + pub(super) const VOTE_MESSAGE: Rep = Rep::new(100, "BEEFY: Round vote message"); + pub(super) const KNOWN_VOTE_MESSAGE: Rep = Rep::new(50, "BEEFY: Known vote"); + pub(super) const VALIDATED_PROOF: Rep = Rep::new(100, "BEEFY: Justification"); +} + #[cfg(test)] mod tests { use super::*; diff --git a/client/consensus/beefy/src/communication/peers.rs b/client/consensus/beefy/src/communication/peers.rs index c2fb06fad..4704b8dcf 100644 --- a/client/consensus/beefy/src/communication/peers.rs +++ b/client/consensus/beefy/src/communication/peers.rs @@ -18,13 +18,17 @@ //! Logic for keeping track of BEEFY peers. -// TODO (issue #12296): replace this naive peer tracking with generic one that infers data -// from multiple network protocols. - -use sc_network::PeerId; +use sc_network::{PeerId, ReputationChange}; use sp_runtime::traits::{Block, NumberFor, Zero}; use std::collections::{HashMap, VecDeque}; +/// Report specifying a reputation change for a given peer. +#[derive(Debug, PartialEq)] +pub(crate) struct PeerReport { + pub who: PeerId, + pub cost_benefit: ReputationChange, +} + struct PeerData { last_voted_on: NumberFor, } diff --git a/client/consensus/beefy/src/communication/request_response/incoming_requests_handler.rs b/client/consensus/beefy/src/communication/request_response/incoming_requests_handler.rs index 1670e9982..d4f4b59f0 100644 --- a/client/consensus/beefy/src/communication/request_response/incoming_requests_handler.rs +++ b/client/consensus/beefy/src/communication/request_response/incoming_requests_handler.rs @@ -32,9 +32,12 @@ use sp_runtime::traits::Block; use std::{marker::PhantomData, sync::Arc}; use crate::{ - communication::request_response::{ - on_demand_justifications_protocol_config, Error, JustificationRequest, - BEEFY_SYNC_LOG_TARGET, + communication::{ + cost, + request_response::{ + on_demand_justifications_protocol_config, Error, JustificationRequest, + BEEFY_SYNC_LOG_TARGET, + }, }, metric_inc, metrics::{register_metrics, OnDemandIncomingRequestsMetrics}, @@ -69,17 +72,20 @@ impl IncomingRequest { /// Params: /// - The raw request to decode /// - Reputation changes to apply for the peer in case decoding fails. - pub fn try_from_raw( + pub fn try_from_raw( raw: netconfig::IncomingRequest, - reputation_changes: Vec, - ) -> Result { + reputation_changes_on_err: F, + ) -> Result + where + F: FnOnce(usize) -> Vec, + { let netconfig::IncomingRequest { payload, peer, pending_response } = raw; let payload = match JustificationRequest::decode(&mut payload.as_ref()) { Ok(payload) => payload, Err(err) => { let response = netconfig::OutgoingResponse { result: Err(()), - reputation_changes, + reputation_changes: reputation_changes_on_err(payload.len()), sent_feedback: None, }; if let Err(_) = pending_response.send(response) { @@ -111,11 +117,11 @@ impl IncomingRequestReceiver { pub async fn recv(&mut self, reputation_changes: F) -> Result, Error> where B: Block, - F: FnOnce() -> Vec, + F: FnOnce(usize) -> Vec, { let req = match self.raw.next().await { None => return Err(Error::RequestChannelExhausted), - Some(raw) => IncomingRequest::::try_from_raw(raw, reputation_changes())?, + Some(raw) => IncomingRequest::::try_from_raw(raw, reputation_changes)?, }; Ok(req) } @@ -159,26 +165,20 @@ where // Sends back justification response if justification found in client backend. fn handle_request(&self, request: IncomingRequest) -> Result<(), Error> { - // TODO (issue #12293): validate `request` and change peer reputation for invalid requests. - - let maybe_encoded_proof = if let Some(hash) = - self.client.block_hash(request.payload.begin).map_err(Error::Client)? - { - self.client - .justifications(hash) - .map_err(Error::Client)? - .and_then(|justifs| justifs.get(BEEFY_ENGINE_ID).cloned()) - // No BEEFY justification present. - .ok_or(()) - } else { - Err(()) - }; - + let mut reputation_changes = vec![]; + let maybe_encoded_proof = self + .client + .block_hash(request.payload.begin) + .ok() + .flatten() + .and_then(|hash| self.client.justifications(hash).ok().flatten()) + .and_then(|justifs| justifs.get(BEEFY_ENGINE_ID).cloned()) + .ok_or_else(|| reputation_changes.push(cost::UNKOWN_PROOF_REQUEST)); request .pending_response .send(netconfig::OutgoingResponse { result: maybe_encoded_proof, - reputation_changes: Vec::new(), + reputation_changes, sent_feedback: None, }) .map_err(|_| Error::SendResponse) @@ -188,7 +188,17 @@ where pub async fn run(mut self) { trace!(target: BEEFY_SYNC_LOG_TARGET, "🥩 Running BeefyJustifsRequestHandler"); - while let Ok(request) = self.request_receiver.recv(|| vec![]).await { + while let Ok(request) = self + .request_receiver + .recv(|bytes| { + let bytes = bytes.min(i32::MAX as usize) as i32; + vec![ReputationChange::new( + bytes.saturating_mul(cost::PER_UNDECODABLE_BYTE), + "BEEFY: Bad request payload", + )] + }) + .await + { let peer = request.peer; match self.handle_request(request) { Ok(()) => { @@ -199,8 +209,8 @@ where ) }, Err(e) => { + // peer reputation changes already applied in `self.handle_request()` metric_inc!(self, beefy_failed_justification_responses); - // TODO (issue #12293): apply reputation changes here based on error type. debug!( target: BEEFY_SYNC_LOG_TARGET, "🥩 Failed to handle BEEFY justification request from {:?}: {}", peer, e, diff --git a/client/consensus/beefy/src/communication/request_response/mod.rs b/client/consensus/beefy/src/communication/request_response/mod.rs index c528d06bb..545ab18cf 100644 --- a/client/consensus/beefy/src/communication/request_response/mod.rs +++ b/client/consensus/beefy/src/communication/request_response/mod.rs @@ -30,7 +30,7 @@ use codec::{Decode, Encode, Error as CodecError}; use sc_network::{config::RequestResponseConfig, PeerId}; use sp_runtime::traits::{Block, NumberFor}; -use crate::communication::beefy_protocol_name::justifications_protocol_name; +use crate::communication::{beefy_protocol_name::justifications_protocol_name, peers::PeerReport}; use incoming_requests_handler::IncomingRequestReceiver; // 10 seems reasonable, considering justifs are explicitly requested only @@ -76,7 +76,7 @@ pub struct JustificationRequest { } #[derive(Debug, thiserror::Error)] -pub enum Error { +pub(crate) enum Error { #[error(transparent)] Client(#[from] sp_blockchain::Error), @@ -99,5 +99,8 @@ pub enum Error { SendResponse, #[error("Received invalid response.")] - InvalidResponse, + InvalidResponse(PeerReport), + + #[error("Internal error while getting response.")] + ResponseError, } diff --git a/client/consensus/beefy/src/communication/request_response/outgoing_requests_engine.rs b/client/consensus/beefy/src/communication/request_response/outgoing_requests_engine.rs index fbf464bd6..10105ff2d 100644 --- a/client/consensus/beefy/src/communication/request_response/outgoing_requests_engine.rs +++ b/client/consensus/beefy/src/communication/request_response/outgoing_requests_engine.rs @@ -31,7 +31,11 @@ use sp_runtime::traits::{Block, NumberFor}; use std::{collections::VecDeque, result::Result, sync::Arc}; use crate::{ - communication::request_response::{Error, JustificationRequest, BEEFY_SYNC_LOG_TARGET}, + communication::{ + benefit, cost, + peers::PeerReport, + request_response::{Error, JustificationRequest, BEEFY_SYNC_LOG_TARGET}, + }, justification::{decode_and_verify_finality_proof, BeefyVersionedFinalityProof}, metric_inc, metrics::{register_metrics, OnDemandOutgoingRequestsMetrics}, @@ -54,6 +58,16 @@ enum State { AwaitingResponse(PeerId, RequestInfo, ResponseReceiver), } +/// Possible engine responses. +pub(crate) enum ResponseInfo { + /// No peer response available yet. + Pending, + /// Valid justification provided alongside peer reputation changes. + ValidProof(BeefyVersionedFinalityProof, PeerReport), + /// No justification yet, only peer reputation changes. + PeerReport(PeerReport), +} + pub struct OnDemandJustificationsEngine { network: Arc, protocol_name: ProtocolName, @@ -84,12 +98,10 @@ impl OnDemandJustificationsEngine { } fn reset_peers_cache_for_block(&mut self, block: NumberFor) { - // TODO (issue #12296): replace peer selection with generic one that involves all protocols. self.peers_cache = self.live_peers.lock().further_than(block); } fn try_next_peer(&mut self) -> Option { - // TODO (issue #12296): replace peer selection with generic one that involves all protocols. let live = self.live_peers.lock(); while let Some(peer) = self.peers_cache.pop_front() { if live.contains(&peer) { @@ -159,24 +171,19 @@ impl OnDemandJustificationsEngine { fn process_response( &mut self, - peer: PeerId, + peer: &PeerId, req_info: &RequestInfo, response: Result, ) -> Result, Error> { response .map_err(|e| { - metric_inc!(self, beefy_on_demand_justification_peer_hang_up); debug!( target: BEEFY_SYNC_LOG_TARGET, - "🥩 for on demand justification #{:?}, peer {:?} hung up: {:?}", - req_info.block, - peer, - e + "🥩 on-demand sc-network channel sender closed, err: {:?}", e ); - Error::InvalidResponse + Error::ResponseError })? .map_err(|e| { - metric_inc!(self, beefy_on_demand_justification_peer_error); debug!( target: BEEFY_SYNC_LOG_TARGET, "🥩 for on demand justification #{:?}, peer {:?} error: {:?}", @@ -184,7 +191,18 @@ impl OnDemandJustificationsEngine { peer, e ); - Error::InvalidResponse + match e { + RequestFailure::Refused => { + metric_inc!(self, beefy_on_demand_justification_peer_refused); + let peer_report = + PeerReport { who: *peer, cost_benefit: cost::REFUSAL_RESPONSE }; + Error::InvalidResponse(peer_report) + }, + _ => { + metric_inc!(self, beefy_on_demand_justification_peer_error); + Error::ResponseError + }, + } }) .and_then(|encoded| { decode_and_verify_finality_proof::( @@ -192,23 +210,26 @@ impl OnDemandJustificationsEngine { req_info.block, &req_info.active_set, ) - .map_err(|e| { + .map_err(|(err, signatures_checked)| { metric_inc!(self, beefy_on_demand_justification_invalid_proof); debug!( target: BEEFY_SYNC_LOG_TARGET, "🥩 for on demand justification #{:?}, peer {:?} responded with invalid proof: {:?}", - req_info.block, peer, e + req_info.block, peer, err ); - Error::InvalidResponse + let mut cost = cost::INVALID_PROOF; + cost.value += + cost::PER_SIGNATURE_CHECKED.saturating_mul(signatures_checked as i32); + Error::InvalidResponse(PeerReport { who: *peer, cost_benefit: cost }) }) }) } - pub async fn next(&mut self) -> Option> { + pub(crate) async fn next(&mut self) -> ResponseInfo { let (peer, req_info, resp) = match &mut self.state { State::Idle => { futures::future::pending::<()>().await; - return None + return ResponseInfo::Pending }, State::AwaitingResponse(peer, req_info, receiver) => { let resp = receiver.await; @@ -220,8 +241,8 @@ impl OnDemandJustificationsEngine { self.state = State::Idle; let block = req_info.block; - self.process_response(peer, &req_info, resp) - .map_err(|_| { + match self.process_response(&peer, &req_info, resp) { + Err(err) => { // No valid justification received, try next peer in our set. if let Some(peer) = self.try_next_peer() { self.request_from_peer(peer, req_info); @@ -231,15 +252,22 @@ impl OnDemandJustificationsEngine { "🥩 ran out of peers to request justif #{:?} from", block ); } - }) - .map(|proof| { + // Report peer based on error type. + if let Error::InvalidResponse(peer_report) = err { + ResponseInfo::PeerReport(peer_report) + } else { + ResponseInfo::Pending + } + }, + Ok(proof) => { metric_inc!(self, beefy_on_demand_justification_good_proof); debug!( target: BEEFY_SYNC_LOG_TARGET, "🥩 received valid on-demand justif #{:?} from {:?}", block, peer ); - proof - }) - .ok() + let peer_report = PeerReport { who: peer, cost_benefit: benefit::VALIDATED_PROOF }; + ResponseInfo::ValidProof(proof, peer_report) + }, + } } } diff --git a/client/consensus/beefy/src/import.rs b/client/consensus/beefy/src/import.rs index dd2ed92ef..bda8169d9 100644 --- a/client/consensus/beefy/src/import.rs +++ b/client/consensus/beefy/src/import.rs @@ -109,6 +109,7 @@ where .ok_or_else(|| ImportError("Unknown validator set".to_string()))?; decode_and_verify_finality_proof::(&encoded[..], number, &validator_set) + .map_err(|(err, _)| err) } } diff --git a/client/consensus/beefy/src/justification.rs b/client/consensus/beefy/src/justification.rs index 5175fd17d..731acdfa6 100644 --- a/client/consensus/beefy/src/justification.rs +++ b/client/consensus/beefy/src/justification.rs @@ -42,9 +42,9 @@ pub(crate) fn decode_and_verify_finality_proof( encoded: &[u8], target_number: NumberFor, validator_set: &ValidatorSet, -) -> Result, ConsensusError> { +) -> Result, (ConsensusError, u32)> { let proof = >::decode(&mut &*encoded) - .map_err(|_| ConsensusError::InvalidJustification)?; + .map_err(|_| (ConsensusError::InvalidJustification, 0))?; verify_with_validator_set::(target_number, validator_set, &proof).map(|_| proof) } @@ -53,14 +53,15 @@ pub(crate) fn verify_with_validator_set( target_number: NumberFor, validator_set: &ValidatorSet, proof: &BeefyVersionedFinalityProof, -) -> Result<(), ConsensusError> { +) -> Result<(), (ConsensusError, u32)> { + let mut signatures_checked = 0u32; match proof { VersionedFinalityProof::V1(signed_commitment) => { if signed_commitment.signatures.len() != validator_set.len() || signed_commitment.commitment.validator_set_id != validator_set.id() || signed_commitment.commitment.block_number != target_number { - return Err(ConsensusError::InvalidJustification) + return Err((ConsensusError::InvalidJustification, 0)) } // Arrangement of signatures in the commitment should be in the same order @@ -73,14 +74,17 @@ pub(crate) fn verify_with_validator_set( .filter(|(id, signature)| { signature .as_ref() - .map(|sig| BeefyKeystore::verify(id, sig, &message[..])) + .map(|sig| { + signatures_checked += 1; + BeefyKeystore::verify(id, sig, &message[..]) + }) .unwrap_or(false) }) .count(); if valid_signatures >= crate::round::threshold(validator_set.len()) { Ok(()) } else { - Err(ConsensusError::InvalidJustification) + Err((ConsensusError::InvalidJustification, signatures_checked)) } }, } @@ -127,16 +131,16 @@ pub(crate) mod tests { // wrong block number -> should fail verification let good_proof = proof.clone().into(); match verify_with_validator_set::(block_num + 1, &validator_set, &good_proof) { - Err(ConsensusError::InvalidJustification) => (), - _ => assert!(false, "Expected Err(ConsensusError::InvalidJustification)"), + Err((ConsensusError::InvalidJustification, 0)) => (), + e => assert!(false, "Got unexpected {:?}", e), }; // wrong validator set id -> should fail verification let good_proof = proof.clone().into(); let other = ValidatorSet::new(make_beefy_ids(keys), 1).unwrap(); match verify_with_validator_set::(block_num, &other, &good_proof) { - Err(ConsensusError::InvalidJustification) => (), - _ => assert!(false, "Expected Err(ConsensusError::InvalidJustification)"), + Err((ConsensusError::InvalidJustification, 0)) => (), + e => assert!(false, "Got unexpected {:?}", e), }; // wrong signatures length -> should fail verification @@ -147,8 +151,8 @@ pub(crate) mod tests { }; bad_signed_commitment.signatures.pop().flatten().unwrap(); match verify_with_validator_set::(block_num + 1, &validator_set, &bad_proof.into()) { - Err(ConsensusError::InvalidJustification) => (), - _ => assert!(false, "Expected Err(ConsensusError::InvalidJustification)"), + Err((ConsensusError::InvalidJustification, 0)) => (), + e => assert!(false, "Got unexpected {:?}", e), }; // not enough signatures -> should fail verification @@ -158,9 +162,9 @@ pub(crate) mod tests { }; // remove a signature (but same length) *bad_signed_commitment.signatures.first_mut().unwrap() = None; - match verify_with_validator_set::(block_num + 1, &validator_set, &bad_proof.into()) { - Err(ConsensusError::InvalidJustification) => (), - _ => assert!(false, "Expected Err(ConsensusError::InvalidJustification)"), + match verify_with_validator_set::(block_num, &validator_set, &bad_proof.into()) { + Err((ConsensusError::InvalidJustification, 2)) => (), + e => assert!(false, "Got unexpected {:?}", e), }; // not enough _correct_ signatures -> should fail verification @@ -171,9 +175,9 @@ pub(crate) mod tests { // change a signature to a different key *bad_signed_commitment.signatures.first_mut().unwrap() = Some(Keyring::Dave.sign(&bad_signed_commitment.commitment.encode())); - match verify_with_validator_set::(block_num + 1, &validator_set, &bad_proof.into()) { - Err(ConsensusError::InvalidJustification) => (), - _ => assert!(false, "Expected Err(ConsensusError::InvalidJustification)"), + match verify_with_validator_set::(block_num, &validator_set, &bad_proof.into()) { + Err((ConsensusError::InvalidJustification, 3)) => (), + e => assert!(false, "Got unexpected {:?}", e), }; } diff --git a/client/consensus/beefy/src/lib.rs b/client/consensus/beefy/src/lib.rs index 3c66cc6eb..d3e5e4bc6 100644 --- a/client/consensus/beefy/src/lib.rs +++ b/client/consensus/beefy/src/lib.rs @@ -52,7 +52,11 @@ use sp_consensus_beefy::{ use sp_keystore::KeystorePtr; use sp_mmr_primitives::MmrApi; use sp_runtime::traits::{Block, Zero}; -use std::{collections::VecDeque, marker::PhantomData, sync::Arc}; +use std::{ + collections::{BTreeMap, VecDeque}, + marker::PhantomData, + sync::Arc, +}; mod aux_schema; mod error; @@ -249,9 +253,10 @@ pub async fn start_beefy_gadget( let known_peers = Arc::new(Mutex::new(KnownPeers::new())); // Default votes filter is to discard everything. // Validator is updated later with correct starting round and set id. - let gossip_validator = - Arc::new(communication::gossip::GossipValidator::new(known_peers.clone())); - let mut gossip_engine = sc_network_gossip::GossipEngine::new( + let (gossip_validator, gossip_report_stream) = + communication::gossip::GossipValidator::new(known_peers.clone()); + let gossip_validator = Arc::new(gossip_validator); + let mut gossip_engine = GossipEngine::new( network.clone(), sync.clone(), gossip_protocol_name, @@ -295,7 +300,7 @@ pub async fn start_beefy_gadget( return } - let worker_params = worker::WorkerParams { + let worker = worker::BeefyWorker { backend, payload_provider, runtime, @@ -303,14 +308,14 @@ pub async fn start_beefy_gadget( key_store: key_store.into(), gossip_engine, gossip_validator, + gossip_report_stream, on_demand_justifications, links, metrics, + pending_justifications: BTreeMap::new(), persisted_state, }; - let worker = worker::BeefyWorker::<_, _, _, _, _>::new(worker_params); - futures::future::join( worker.run(block_import_justif, finality_notifications), on_demand_justifications_handler.run(), diff --git a/client/consensus/beefy/src/metrics.rs b/client/consensus/beefy/src/metrics.rs index 6653763fc..031748bdc 100644 --- a/client/consensus/beefy/src/metrics.rs +++ b/client/consensus/beefy/src/metrics.rs @@ -228,8 +228,8 @@ impl PrometheusRegister for OnDemandIncomingRequestsMetrics { pub struct OnDemandOutgoingRequestsMetrics { /// Number of times there was no good peer to request justification from pub beefy_on_demand_justification_no_peer_to_request_from: Counter, - /// Number of on-demand justification peer hang up - pub beefy_on_demand_justification_peer_hang_up: Counter, + /// Number of on-demand justification peer refused valid requests + pub beefy_on_demand_justification_peer_refused: Counter, /// Number of on-demand justification peer error pub beefy_on_demand_justification_peer_error: Counter, /// Number of on-demand justification invalid proof @@ -249,10 +249,10 @@ impl PrometheusRegister for OnDemandOutgoingRequestsMetrics { )?, registry, )?, - beefy_on_demand_justification_peer_hang_up: register( + beefy_on_demand_justification_peer_refused: register( Counter::new( - "substrate_beefy_on_demand_justification_peer_hang_up", - "Number of on-demand justification peer hang up", + "beefy_on_demand_justification_peer_refused", + "Number of on-demand justification peer refused valid requests", )?, registry, )?, diff --git a/client/consensus/beefy/src/tests.rs b/client/consensus/beefy/src/tests.rs index f36c2cd68..48ecebdac 100644 --- a/client/consensus/beefy/src/tests.rs +++ b/client/consensus/beefy/src/tests.rs @@ -24,6 +24,7 @@ use crate::{ communication::{ gossip::{ proofs_topic, tests::sign_commitment, votes_topic, GossipFilterCfg, GossipMessage, + GossipValidator, }, request_response::{on_demand_justifications_protocol_config, BeefyJustifsRequestHandler}, }, @@ -357,8 +358,8 @@ async fn voter_init_setup( ) -> sp_blockchain::Result> { let backend = net.peer(0).client().as_backend(); let known_peers = Arc::new(Mutex::new(KnownPeers::new())); - let gossip_validator = - Arc::new(crate::communication::gossip::GossipValidator::new(known_peers)); + let (gossip_validator, _) = GossipValidator::new(known_peers); + let gossip_validator = Arc::new(gossip_validator); let mut gossip_engine = sc_network_gossip::GossipEngine::new( net.peer(0).network_service().clone(), net.peer(0).sync_service().clone(), @@ -1262,8 +1263,8 @@ async fn gossipped_finality_proofs() { let charlie = &net.peers[2]; let known_peers = Arc::new(Mutex::new(KnownPeers::::new())); // Charlie will run just the gossip engine and not the full voter. - let charlie_gossip_validator = - Arc::new(crate::communication::gossip::GossipValidator::new(known_peers)); + let (gossip_validator, _) = GossipValidator::new(known_peers); + let charlie_gossip_validator = Arc::new(gossip_validator); charlie_gossip_validator.update_filter(GossipFilterCfg:: { start: 1, end: 10, diff --git a/client/consensus/beefy/src/worker.rs b/client/consensus/beefy/src/worker.rs index 19225ec21..c05de197d 100644 --- a/client/consensus/beefy/src/worker.rs +++ b/client/consensus/beefy/src/worker.rs @@ -19,7 +19,8 @@ use crate::{ communication::{ gossip::{proofs_topic, votes_topic, GossipFilterCfg, GossipMessage, GossipValidator}, - request_response::outgoing_requests_engine::OnDemandJustificationsEngine, + peers::PeerReport, + request_response::outgoing_requests_engine::{OnDemandJustificationsEngine, ResponseInfo}, }, error::Error, justification::BeefyVersionedFinalityProof, @@ -34,7 +35,7 @@ use futures::{stream::Fuse, FutureExt, StreamExt}; use log::{debug, error, info, log_enabled, trace, warn}; use sc_client_api::{Backend, FinalityNotification, FinalityNotifications, HeaderBackend}; use sc_network_gossip::GossipEngine; -use sc_utils::notification::NotificationReceiver; +use sc_utils::{mpsc::TracingUnboundedReceiver, notification::NotificationReceiver}; use sp_api::{BlockId, ProvideRuntimeApi}; use sp_arithmetic::traits::{AtLeast32Bit, Saturating}; use sp_consensus::SyncOracle; @@ -255,20 +256,6 @@ impl VoterOracle { } } -pub(crate) struct WorkerParams { - pub backend: Arc, - pub payload_provider: P, - pub runtime: Arc, - pub sync: Arc, - pub key_store: BeefyKeystore, - pub gossip_engine: GossipEngine, - pub gossip_validator: Arc>, - pub on_demand_justifications: OnDemandJustificationsEngine, - pub links: BeefyVoterLinks, - pub metrics: Option, - pub persisted_state: PersistedState, -} - #[derive(Debug, Decode, Encode, PartialEq)] pub(crate) struct PersistedState { /// Best block we voted on. @@ -311,28 +298,29 @@ impl PersistedState { /// A BEEFY worker plays the BEEFY protocol pub(crate) struct BeefyWorker { // utilities - backend: Arc, - payload_provider: P, - runtime: Arc, - sync: Arc, - key_store: BeefyKeystore, + pub backend: Arc, + pub payload_provider: P, + pub runtime: Arc, + pub sync: Arc, + pub key_store: BeefyKeystore, // communication - gossip_engine: GossipEngine, - gossip_validator: Arc>, - on_demand_justifications: OnDemandJustificationsEngine, + pub gossip_engine: GossipEngine, + pub gossip_validator: Arc>, + pub gossip_report_stream: TracingUnboundedReceiver, + pub on_demand_justifications: OnDemandJustificationsEngine, // channels /// Links between the block importer, the background voter and the RPC layer. - links: BeefyVoterLinks, + pub links: BeefyVoterLinks, // voter state /// BEEFY client metrics. - metrics: Option, + pub metrics: Option, /// Buffer holding justifications for future processing. - pending_justifications: BTreeMap, BeefyVersionedFinalityProof>, + pub pending_justifications: BTreeMap, BeefyVersionedFinalityProof>, /// Persisted voter state. - persisted_state: PersistedState, + pub persisted_state: PersistedState, } impl BeefyWorker @@ -344,43 +332,6 @@ where R: ProvideRuntimeApi, R::Api: BeefyApi, { - /// Return a new BEEFY worker instance. - /// - /// Note that a BEEFY worker is only fully functional if a corresponding - /// BEEFY pallet has been deployed on-chain. - /// - /// The BEEFY pallet is needed in order to keep track of the BEEFY authority set. - pub(crate) fn new(worker_params: WorkerParams) -> Self { - let WorkerParams { - backend, - payload_provider, - runtime, - key_store, - sync, - gossip_engine, - gossip_validator, - on_demand_justifications, - links, - metrics, - persisted_state, - } = worker_params; - - BeefyWorker { - backend, - payload_provider, - runtime, - sync, - key_store, - gossip_engine, - gossip_validator, - on_demand_justifications, - links, - metrics, - pending_justifications: BTreeMap::new(), - persisted_state, - } - } - fn best_grandpa_block(&self) -> NumberFor { *self.persisted_state.voting_oracle.best_grandpa_block_header.number() } @@ -849,7 +800,12 @@ where // Act on changed 'state'. self.process_new_state(); + // Mutable reference used to drive the gossip engine. let mut gossip_engine = &mut self.gossip_engine; + // Use temp val and report after async section, + // to avoid having to Mutex-wrap `gossip_engine`. + let mut gossip_report: Option = None; + // Wait for, and handle external events. // The branches below only change 'state', actual voting happens afterwards, // based on the new resulting 'state'. @@ -870,11 +826,16 @@ where return; }, // Process incoming justifications as these can make some in-flight votes obsolete. - justif = self.on_demand_justifications.next().fuse() => { - if let Some(justif) = justif { - if let Err(err) = self.triage_incoming_justif(justif) { - debug!(target: LOG_TARGET, "🥩 {}", err); - } + response_info = self.on_demand_justifications.next().fuse() => { + match response_info { + ResponseInfo::ValidProof(justif, peer_report) => { + if let Err(err) = self.triage_incoming_justif(justif) { + debug!(target: LOG_TARGET, "🥩 {}", err); + } + gossip_report = Some(peer_report); + }, + ResponseInfo::PeerReport(peer_report) => gossip_report = Some(peer_report), + ResponseInfo::Pending => (), } }, justif = block_import_justif.next() => { @@ -918,6 +879,13 @@ where return; } }, + // Process peer reports. + report = self.gossip_report_stream.next() => { + gossip_report = report; + }, + } + if let Some(PeerReport { who, cost_benefit }) = gossip_report { + self.gossip_engine.report(who, cost_benefit); } } } @@ -1122,7 +1090,8 @@ pub(crate) mod tests { let network = peer.network_service().clone(); let sync = peer.sync_service().clone(); let known_peers = Arc::new(Mutex::new(KnownPeers::new())); - let gossip_validator = Arc::new(GossipValidator::new(known_peers.clone())); + let (gossip_validator, gossip_report_stream) = GossipValidator::new(known_peers.clone()); + let gossip_validator = Arc::new(gossip_validator); let gossip_engine = GossipEngine::new( network.clone(), sync.clone(), @@ -1152,7 +1121,7 @@ pub(crate) mod tests { ) .unwrap(); let payload_provider = MmrRootProvider::new(api.clone()); - let worker_params = crate::worker::WorkerParams { + BeefyWorker { backend, payload_provider, runtime: api, @@ -1160,12 +1129,13 @@ pub(crate) mod tests { links, gossip_engine, gossip_validator, + gossip_report_stream, metrics, sync: Arc::new(sync), on_demand_justifications, + pending_justifications: BTreeMap::new(), persisted_state, - }; - BeefyWorker::<_, _, _, _, _>::new(worker_params) + } } #[test] From ea9ce4c0af36310c0b0db264ab12cf4766b83750 Mon Sep 17 00:00:00 2001 From: Roman Useinov Date: Wed, 12 Apr 2023 14:14:06 +0200 Subject: [PATCH 366/558] [Deps] Bump scale-info to match cumulus and polkadot (#13873) * [Deps] Bump scale-info to match cumulus and polkadot * fix Cargo.lock * fix identity * more fixes * fix * fixes * more fixes --- Cargo.lock | 8 ++--- bin/node-template/pallets/template/Cargo.toml | 2 +- bin/node-template/runtime/Cargo.toml | 2 +- bin/node/executor/Cargo.toml | 2 +- bin/node/primitives/Cargo.toml | 2 +- bin/node/runtime/Cargo.toml | 2 +- client/consensus/babe/Cargo.toml | 2 +- client/rpc-api/Cargo.toml | 2 +- frame/assets/Cargo.toml | 2 +- frame/atomic-swap/Cargo.toml | 2 +- frame/aura/Cargo.toml | 2 +- frame/authority-discovery/Cargo.toml | 2 +- frame/authorship/Cargo.toml | 2 +- frame/babe/Cargo.toml | 2 +- frame/bags-list/Cargo.toml | 2 +- frame/balances/Cargo.toml | 2 +- frame/beefy-mmr/Cargo.toml | 2 +- frame/beefy/Cargo.toml | 2 +- frame/benchmarking/Cargo.toml | 2 +- frame/benchmarking/pov/Cargo.toml | 2 +- frame/bounties/Cargo.toml | 2 +- frame/child-bounties/Cargo.toml | 2 +- frame/collective/Cargo.toml | 2 +- frame/contracts/Cargo.toml | 2 +- frame/contracts/primitives/Cargo.toml | 2 +- frame/conviction-voting/Cargo.toml | 2 +- frame/democracy/Cargo.toml | 2 +- .../election-provider-multi-phase/Cargo.toml | 2 +- frame/election-provider-support/Cargo.toml | 2 +- .../solution-type/fuzzer/Cargo.toml | 2 +- frame/examples/basic/Cargo.toml | 2 +- frame/examples/offchain-worker/Cargo.toml | 2 +- frame/executive/Cargo.toml | 2 +- frame/fast-unstake/Cargo.toml | 2 +- frame/glutton/Cargo.toml | 2 +- frame/grandpa/Cargo.toml | 2 +- frame/identity/Cargo.toml | 2 +- frame/identity/src/types.rs | 18 +++++----- frame/im-online/Cargo.toml | 2 +- frame/indices/Cargo.toml | 2 +- .../Cargo.toml | 2 +- frame/lottery/Cargo.toml | 2 +- frame/membership/Cargo.toml | 2 +- frame/merkle-mountain-range/Cargo.toml | 2 +- frame/message-queue/Cargo.toml | 2 +- frame/multisig/Cargo.toml | 2 +- frame/nfts/Cargo.toml | 2 +- frame/nicks/Cargo.toml | 2 +- frame/nis/Cargo.toml | 2 +- frame/node-authorization/Cargo.toml | 2 +- frame/nomination-pools/Cargo.toml | 2 +- .../nomination-pools/benchmarking/Cargo.toml | 2 +- frame/offences/Cargo.toml | 2 +- frame/preimage/Cargo.toml | 2 +- frame/proxy/Cargo.toml | 2 +- frame/recovery/Cargo.toml | 2 +- frame/referenda/Cargo.toml | 2 +- frame/remark/Cargo.toml | 2 +- frame/root-offences/Cargo.toml | 2 +- frame/root-testing/Cargo.toml | 2 +- frame/scheduler/Cargo.toml | 2 +- frame/scored-pool/Cargo.toml | 2 +- frame/session/Cargo.toml | 2 +- frame/society/Cargo.toml | 2 +- frame/staking/Cargo.toml | 2 +- frame/state-trie-migration/Cargo.toml | 2 +- frame/sudo/Cargo.toml | 2 +- frame/support/Cargo.toml | 2 +- frame/support/test/Cargo.toml | 2 +- frame/support/test/compile_pass/Cargo.toml | 2 +- frame/support/test/tests/pallet.rs | 6 ++-- .../test/tests/pallet_compatibility.rs | 36 +++++++++---------- .../tests/pallet_compatibility_instance.rs | 36 +++++++++---------- frame/system/Cargo.toml | 2 +- frame/system/benchmarking/Cargo.toml | 2 +- frame/timestamp/Cargo.toml | 2 +- frame/tips/Cargo.toml | 2 +- frame/transaction-payment/Cargo.toml | 2 +- .../asset-tx-payment/Cargo.toml | 2 +- frame/transaction-storage/Cargo.toml | 2 +- frame/treasury/Cargo.toml | 2 +- frame/uniques/Cargo.toml | 2 +- frame/utility/Cargo.toml | 2 +- frame/vesting/Cargo.toml | 2 +- frame/whitelist/Cargo.toml | 2 +- primitives/application-crypto/Cargo.toml | 2 +- primitives/arithmetic/Cargo.toml | 2 +- primitives/authority-discovery/Cargo.toml | 2 +- primitives/consensus/aura/Cargo.toml | 2 +- primitives/consensus/babe/Cargo.toml | 2 +- primitives/consensus/beefy/Cargo.toml | 2 +- primitives/consensus/grandpa/Cargo.toml | 2 +- primitives/consensus/vrf/Cargo.toml | 2 +- primitives/core/Cargo.toml | 2 +- primitives/inherents/Cargo.toml | 2 +- primitives/merkle-mountain-range/Cargo.toml | 2 +- primitives/npos-elections/Cargo.toml | 2 +- primitives/npos-elections/fuzzer/Cargo.toml | 2 +- primitives/runtime/Cargo.toml | 2 +- primitives/runtime/src/generic/digest.rs | 8 ++--- primitives/session/Cargo.toml | 2 +- primitives/staking/Cargo.toml | 2 +- .../transaction-storage-proof/Cargo.toml | 2 +- primitives/trie/Cargo.toml | 2 +- primitives/version/Cargo.toml | 2 +- primitives/weights/Cargo.toml | 2 +- test-utils/runtime/Cargo.toml | 2 +- .../rpc/state-trie-migration-rpc/Cargo.toml | 2 +- 108 files changed, 158 insertions(+), 158 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index f55180f9c..6a1f29aa2 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -9698,9 +9698,9 @@ dependencies = [ [[package]] name = "scale-info" -version = "2.3.1" +version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "001cf62ece89779fd16105b5f515ad0e5cedcd5440d3dd806bb067978e7c3608" +checksum = "0cfdffd972d76b22f3d7f81c8be34b2296afd3a25e0a547bd9abe340a4dbbe97" dependencies = [ "bitvec", "cfg-if", @@ -9712,9 +9712,9 @@ dependencies = [ [[package]] name = "scale-info-derive" -version = "2.3.1" +version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "303959cf613a6f6efd19ed4b4ad5bf79966a13352716299ad532cfb115f4205c" +checksum = "61fa974aea2d63dd18a4ec3a49d59af9f34178c73a4f56d2f18205628d00681e" dependencies = [ "proc-macro-crate", "proc-macro2", diff --git a/bin/node-template/pallets/template/Cargo.toml b/bin/node-template/pallets/template/Cargo.toml index ddf7c6e52..bb6d8c511 100644 --- a/bin/node-template/pallets/template/Cargo.toml +++ b/bin/node-template/pallets/template/Cargo.toml @@ -16,7 +16,7 @@ targets = ["x86_64-unknown-linux-gnu"] codec = { package = "parity-scale-codec", version = "3.2.2", default-features = false, features = [ "derive", ] } -scale-info = { version = "2.1.1", default-features = false, features = ["derive"] } +scale-info = { version = "2.5.0", default-features = false, features = ["derive"] } frame-benchmarking = { version = "4.0.0-dev", default-features = false, optional = true, path = "../../../../frame/benchmarking" } frame-support = { version = "4.0.0-dev", default-features = false, path = "../../../../frame/support" } frame-system = { version = "4.0.0-dev", default-features = false, path = "../../../../frame/system" } diff --git a/bin/node-template/runtime/Cargo.toml b/bin/node-template/runtime/Cargo.toml index 8915061c1..930c5b5c4 100644 --- a/bin/node-template/runtime/Cargo.toml +++ b/bin/node-template/runtime/Cargo.toml @@ -14,7 +14,7 @@ targets = ["x86_64-unknown-linux-gnu"] [dependencies] codec = { package = "parity-scale-codec", version = "3.2.2", default-features = false, features = ["derive"] } -scale-info = { version = "2.1.1", default-features = false, features = ["derive"] } +scale-info = { version = "2.5.0", default-features = false, features = ["derive"] } pallet-aura = { version = "4.0.0-dev", default-features = false, path = "../../../frame/aura" } pallet-balances = { version = "4.0.0-dev", default-features = false, path = "../../../frame/balances" } diff --git a/bin/node/executor/Cargo.toml b/bin/node/executor/Cargo.toml index cb661f536..c4711adcb 100644 --- a/bin/node/executor/Cargo.toml +++ b/bin/node/executor/Cargo.toml @@ -14,7 +14,7 @@ targets = ["x86_64-unknown-linux-gnu"] [dependencies] codec = { package = "parity-scale-codec", version = "3.2.2" } -scale-info = { version = "2.1.1", features = ["derive"] } +scale-info = { version = "2.5.0", features = ["derive"] } frame-benchmarking = { version = "4.0.0-dev", path = "../../../frame/benchmarking" } node-primitives = { version = "2.0.0", path = "../primitives" } kitchensink-runtime = { version = "3.0.0-dev", path = "../runtime" } diff --git a/bin/node/primitives/Cargo.toml b/bin/node/primitives/Cargo.toml index 1f515f3e3..78cbf67ce 100644 --- a/bin/node/primitives/Cargo.toml +++ b/bin/node/primitives/Cargo.toml @@ -16,7 +16,7 @@ targets = ["x86_64-unknown-linux-gnu"] codec = { package = "parity-scale-codec", version = "3.2.2", default-features = false, features = [ "derive", ] } -scale-info = { version = "2.1.1", default-features = false, features = ["derive"] } +scale-info = { version = "2.5.0", default-features = false, features = ["derive"] } frame-system = { version = "4.0.0-dev", default-features = false, path = "../../../frame/system" } sp-application-crypto = { version = "7.0.0", default-features = false, path = "../../../primitives/application-crypto" } sp-core = { version = "7.0.0", default-features = false, path = "../../../primitives/core" } diff --git a/bin/node/runtime/Cargo.toml b/bin/node/runtime/Cargo.toml index bc9f3af9d..65a8d208d 100644 --- a/bin/node/runtime/Cargo.toml +++ b/bin/node/runtime/Cargo.toml @@ -20,7 +20,7 @@ codec = { package = "parity-scale-codec", version = "3.2.2", default-features = "derive", "max-encoded-len", ] } -scale-info = { version = "2.1.1", default-features = false, features = ["derive"] } +scale-info = { version = "2.5.0", default-features = false, features = ["derive"] } static_assertions = "1.1.0" log = { version = "0.4.17", default-features = false } diff --git a/client/consensus/babe/Cargo.toml b/client/consensus/babe/Cargo.toml index 8088bef9d..d10ef21c4 100644 --- a/client/consensus/babe/Cargo.toml +++ b/client/consensus/babe/Cargo.toml @@ -15,7 +15,7 @@ targets = ["x86_64-unknown-linux-gnu"] [dependencies] async-trait = "0.1.57" -scale-info = { version = "2.1.1", features = ["derive"] } +scale-info = { version = "2.5.0", features = ["derive"] } codec = { package = "parity-scale-codec", version = "3.2.2", features = ["derive"] } futures = "0.3.21" log = "0.4.17" diff --git a/client/rpc-api/Cargo.toml b/client/rpc-api/Cargo.toml index 261339f13..ba18a1d4f 100644 --- a/client/rpc-api/Cargo.toml +++ b/client/rpc-api/Cargo.toml @@ -14,7 +14,7 @@ targets = ["x86_64-unknown-linux-gnu"] [dependencies] codec = { package = "parity-scale-codec", version = "3.2.2" } -scale-info = { version = "2.1.1", default-features = false, features = ["derive"] } +scale-info = { version = "2.5.0", default-features = false, features = ["derive"] } serde = { version = "1.0.136", features = ["derive"] } serde_json = "1.0.85" thiserror = "1.0" diff --git a/frame/assets/Cargo.toml b/frame/assets/Cargo.toml index 6fe0e3e6c..d3adc47f5 100644 --- a/frame/assets/Cargo.toml +++ b/frame/assets/Cargo.toml @@ -14,7 +14,7 @@ targets = ["x86_64-unknown-linux-gnu"] [dependencies] codec = { package = "parity-scale-codec", version = "3.2.2", default-features = false } -scale-info = { version = "2.1.1", default-features = false, features = ["derive"] } +scale-info = { version = "2.5.0", default-features = false, features = ["derive"] } sp-std = { version = "5.0.0", default-features = false, path = "../../primitives/std" } # Needed for various traits. In our case, `OnFinalize`. sp-runtime = { version = "7.0.0", default-features = false, path = "../../primitives/runtime" } diff --git a/frame/atomic-swap/Cargo.toml b/frame/atomic-swap/Cargo.toml index 9c3bcf73a..fb0f9c0e4 100644 --- a/frame/atomic-swap/Cargo.toml +++ b/frame/atomic-swap/Cargo.toml @@ -14,7 +14,7 @@ targets = ["x86_64-unknown-linux-gnu"] [dependencies] codec = { package = "parity-scale-codec", version = "3.2.2", default-features = false } -scale-info = { version = "2.1.1", default-features = false, features = ["derive"] } +scale-info = { version = "2.5.0", default-features = false, features = ["derive"] } frame-support = { version = "4.0.0-dev", default-features = false, path = "../support" } frame-system = { version = "4.0.0-dev", default-features = false, path = "../system" } sp-core = { version = "7.0.0", default-features = false, path = "../../primitives/core" } diff --git a/frame/aura/Cargo.toml b/frame/aura/Cargo.toml index d2fd5a5b9..ee6465ad3 100644 --- a/frame/aura/Cargo.toml +++ b/frame/aura/Cargo.toml @@ -14,7 +14,7 @@ targets = ["x86_64-unknown-linux-gnu"] [dependencies] codec = { package = "parity-scale-codec", version = "3.2.2", default-features = false, features = ["derive", "max-encoded-len"] } -scale-info = { version = "2.1.1", default-features = false, features = ["derive"] } +scale-info = { version = "2.5.0", default-features = false, features = ["derive"] } frame-support = { version = "4.0.0-dev", default-features = false, path = "../support" } frame-system = { version = "4.0.0-dev", default-features = false, path = "../system" } pallet-timestamp = { version = "4.0.0-dev", default-features = false, path = "../timestamp" } diff --git a/frame/authority-discovery/Cargo.toml b/frame/authority-discovery/Cargo.toml index 50c6c411c..d8693bd85 100644 --- a/frame/authority-discovery/Cargo.toml +++ b/frame/authority-discovery/Cargo.toml @@ -16,7 +16,7 @@ targets = ["x86_64-unknown-linux-gnu"] codec = { package = "parity-scale-codec", version = "3.2.2", default-features = false, features = [ "derive", ] } -scale-info = { version = "2.1.1", default-features = false, features = ["derive"] } +scale-info = { version = "2.5.0", default-features = false, features = ["derive"] } frame-support = { version = "4.0.0-dev", default-features = false, path = "../support" } frame-system = { version = "4.0.0-dev", default-features = false, path = "../system" } pallet-session = { version = "4.0.0-dev", default-features = false, features = [ diff --git a/frame/authorship/Cargo.toml b/frame/authorship/Cargo.toml index a205c51f9..6d8cdd937 100644 --- a/frame/authorship/Cargo.toml +++ b/frame/authorship/Cargo.toml @@ -17,7 +17,7 @@ codec = { package = "parity-scale-codec", version = "3.2.2", default-features = "derive", ] } impl-trait-for-tuples = "0.2.2" -scale-info = { version = "2.1.1", default-features = false, features = ["derive"] } +scale-info = { version = "2.5.0", default-features = false, features = ["derive"] } frame-support = { version = "4.0.0-dev", default-features = false, path = "../support" } frame-system = { version = "4.0.0-dev", default-features = false, path = "../system" } sp-runtime = { version = "7.0.0", default-features = false, path = "../../primitives/runtime" } diff --git a/frame/babe/Cargo.toml b/frame/babe/Cargo.toml index aa5813927..c4f6592db 100644 --- a/frame/babe/Cargo.toml +++ b/frame/babe/Cargo.toml @@ -15,7 +15,7 @@ targets = ["x86_64-unknown-linux-gnu"] [dependencies] codec = { package = "parity-scale-codec", version = "3.2.2", default-features = false, features = ["derive"] } log = { version = "0.4.17", default-features = false } -scale-info = { version = "2.1.1", default-features = false, features = ["derive"] } +scale-info = { version = "2.5.0", default-features = false, features = ["derive"] } frame-benchmarking = { version = "4.0.0-dev", default-features = false, optional = true, path = "../benchmarking" } frame-support = { version = "4.0.0-dev", default-features = false, path = "../support" } frame-system = { version = "4.0.0-dev", default-features = false, path = "../system" } diff --git a/frame/bags-list/Cargo.toml b/frame/bags-list/Cargo.toml index 379698b1d..1678ce1ba 100644 --- a/frame/bags-list/Cargo.toml +++ b/frame/bags-list/Cargo.toml @@ -14,7 +14,7 @@ targets = ["x86_64-unknown-linux-gnu"] [dependencies] # parity codec = { package = "parity-scale-codec", version = "3.2.2", default-features = false, features = ["derive"] } -scale-info = { version = "2.1.1", default-features = false, features = ["derive"] } +scale-info = { version = "2.5.0", default-features = false, features = ["derive"] } # primitives sp-runtime = { version = "7.0.0", default-features = false, path = "../../primitives/runtime" } diff --git a/frame/balances/Cargo.toml b/frame/balances/Cargo.toml index 9646a934d..b3457dd8d 100644 --- a/frame/balances/Cargo.toml +++ b/frame/balances/Cargo.toml @@ -15,7 +15,7 @@ targets = ["x86_64-unknown-linux-gnu"] [dependencies] codec = { package = "parity-scale-codec", version = "3.2.2", default-features = false, features = ["derive", "max-encoded-len"] } log = { version = "0.4.17", default-features = false } -scale-info = { version = "2.1.1", default-features = false, features = ["derive"] } +scale-info = { version = "2.5.0", default-features = false, features = ["derive"] } frame-benchmarking = { version = "4.0.0-dev", default-features = false, optional = true, path = "../benchmarking" } frame-support = { version = "4.0.0-dev", default-features = false, path = "../support" } frame-system = { version = "4.0.0-dev", default-features = false, path = "../system" } diff --git a/frame/beefy-mmr/Cargo.toml b/frame/beefy-mmr/Cargo.toml index 721e24282..984016214 100644 --- a/frame/beefy-mmr/Cargo.toml +++ b/frame/beefy-mmr/Cargo.toml @@ -12,7 +12,7 @@ homepage = "https://substrate.io" array-bytes = { version = "4.1", optional = true } codec = { package = "parity-scale-codec", version = "3.2.2", default-features = false, features = ["derive"] } log = { version = "0.4.17", default-features = false } -scale-info = { version = "2.1.1", default-features = false, features = ["derive"] } +scale-info = { version = "2.5.0", default-features = false, features = ["derive"] } serde = { version = "1.0.136", optional = true } binary-merkle-tree = { version = "4.0.0-dev", default-features = false, path = "../../utils/binary-merkle-tree" } frame-support = { version = "4.0.0-dev", default-features = false, path = "../support" } diff --git a/frame/beefy/Cargo.toml b/frame/beefy/Cargo.toml index 3778ca5f3..21c96ff95 100644 --- a/frame/beefy/Cargo.toml +++ b/frame/beefy/Cargo.toml @@ -10,7 +10,7 @@ homepage = "https://substrate.io" [dependencies] codec = { package = "parity-scale-codec", version = "3.2.2", default-features = false, features = ["derive"] } -scale-info = { version = "2.1.1", default-features = false, features = ["derive"] } +scale-info = { version = "2.5.0", default-features = false, features = ["derive"] } serde = { version = "1.0.136", optional = true } frame-support = { version = "4.0.0-dev", default-features = false, path = "../support" } frame-system = { version = "4.0.0-dev", default-features = false, path = "../system" } diff --git a/frame/benchmarking/Cargo.toml b/frame/benchmarking/Cargo.toml index 7b8a7c5ea..bebe74b1f 100644 --- a/frame/benchmarking/Cargo.toml +++ b/frame/benchmarking/Cargo.toml @@ -17,7 +17,7 @@ codec = { package = "parity-scale-codec", version = "3.2.2", default-features = linregress = { version = "0.5.1", optional = true } log = { version = "0.4.17", default-features = false } paste = "1.0" -scale-info = { version = "2.1.1", default-features = false, features = ["derive"] } +scale-info = { version = "2.5.0", default-features = false, features = ["derive"] } serde = { version = "1.0.136", optional = true } frame-support = { version = "4.0.0-dev", default-features = false, path = "../support" } frame-support-procedural = { version = "4.0.0-dev", default-features = false, path = "../support/procedural" } diff --git a/frame/benchmarking/pov/Cargo.toml b/frame/benchmarking/pov/Cargo.toml index d885821bc..28358142b 100644 --- a/frame/benchmarking/pov/Cargo.toml +++ b/frame/benchmarking/pov/Cargo.toml @@ -13,7 +13,7 @@ targets = ["x86_64-unknown-linux-gnu"] [dependencies] codec = { package = "parity-scale-codec", version = "3.0.0", default-features = false } -scale-info = { version = "2.1.1", default-features = false, features = ["derive"] } +scale-info = { version = "2.5.0", default-features = false, features = ["derive"] } frame-benchmarking = { version = "4.0.0-dev", default-features = false, path = "../" } frame-support = { version = "4.0.0-dev", default-features = false, path = "../../support" } frame-system = { version = "4.0.0-dev", default-features = false, path = "../../system" } diff --git a/frame/bounties/Cargo.toml b/frame/bounties/Cargo.toml index dcb1adae0..a2f39ccd7 100644 --- a/frame/bounties/Cargo.toml +++ b/frame/bounties/Cargo.toml @@ -17,7 +17,7 @@ codec = { package = "parity-scale-codec", version = "3.2.2", default-features = "derive", ] } log = { version = "0.4.17", default-features = false } -scale-info = { version = "2.1.1", default-features = false, features = ["derive"] } +scale-info = { version = "2.5.0", default-features = false, features = ["derive"] } frame-benchmarking = { version = "4.0.0-dev", default-features = false, optional = true, path = "../benchmarking" } frame-support = { version = "4.0.0-dev", default-features = false, path = "../support" } frame-system = { version = "4.0.0-dev", default-features = false, path = "../system" } diff --git a/frame/child-bounties/Cargo.toml b/frame/child-bounties/Cargo.toml index 8d29b7619..48fa22261 100644 --- a/frame/child-bounties/Cargo.toml +++ b/frame/child-bounties/Cargo.toml @@ -17,7 +17,7 @@ codec = { package = "parity-scale-codec", version = "3.2.2", default-features = "derive", ] } log = { version = "0.4.17", default-features = false } -scale-info = { version = "2.1.1", default-features = false, features = ["derive"] } +scale-info = { version = "2.5.0", default-features = false, features = ["derive"] } frame-benchmarking = { version = "4.0.0-dev", default-features = false, optional = true, path = "../benchmarking" } frame-support = { version = "4.0.0-dev", default-features = false, path = "../support" } frame-system = { version = "4.0.0-dev", default-features = false, path = "../system" } diff --git a/frame/collective/Cargo.toml b/frame/collective/Cargo.toml index 26cd5a8ab..7ae881747 100644 --- a/frame/collective/Cargo.toml +++ b/frame/collective/Cargo.toml @@ -15,7 +15,7 @@ targets = ["x86_64-unknown-linux-gnu"] [dependencies] codec = { package = "parity-scale-codec", version = "3.2.2", default-features = false, features = ["derive"] } log = { version = "0.4.17", default-features = false } -scale-info = { version = "2.1.1", default-features = false, features = ["derive"] } +scale-info = { version = "2.5.0", default-features = false, features = ["derive"] } frame-benchmarking = { version = "4.0.0-dev", default-features = false, optional = true, path = "../benchmarking" } frame-support = { version = "4.0.0-dev", default-features = false, path = "../support" } frame-system = { version = "4.0.0-dev", default-features = false, path = "../system" } diff --git a/frame/contracts/Cargo.toml b/frame/contracts/Cargo.toml index f357a21b9..6ab60846f 100644 --- a/frame/contracts/Cargo.toml +++ b/frame/contracts/Cargo.toml @@ -19,7 +19,7 @@ codec = { package = "parity-scale-codec", version = "3.2.2", default-features = "derive", "max-encoded-len", ] } -scale-info = { version = "2.1.1", default-features = false, features = ["derive"] } +scale-info = { version = "2.5.0", default-features = false, features = ["derive"] } log = { version = "0.4", default-features = false } wasm-instrument = { version = "0.4", default-features = false } serde = { version = "1", optional = true, features = ["derive"] } diff --git a/frame/contracts/primitives/Cargo.toml b/frame/contracts/primitives/Cargo.toml index 2460edc5a..b643f03a9 100644 --- a/frame/contracts/primitives/Cargo.toml +++ b/frame/contracts/primitives/Cargo.toml @@ -14,7 +14,7 @@ targets = ["x86_64-unknown-linux-gnu"] [dependencies] bitflags = "1.0" -scale-info = { version = "2.1.1", default-features = false, features = ["derive"] } +scale-info = { version = "2.5.0", default-features = false, features = ["derive"] } codec = { package = "parity-scale-codec", version = "3.2.2", default-features = false, features = ["derive"] } # Substrate Dependencies (This crate should not rely on frame) diff --git a/frame/conviction-voting/Cargo.toml b/frame/conviction-voting/Cargo.toml index d112c54fb..f9390d6b6 100644 --- a/frame/conviction-voting/Cargo.toml +++ b/frame/conviction-voting/Cargo.toml @@ -18,7 +18,7 @@ codec = { package = "parity-scale-codec", version = "3.2.2", default-features = "derive", "max-encoded-len", ] } -scale-info = { version = "2.1.1", default-features = false, features = ["derive"] } +scale-info = { version = "2.5.0", default-features = false, features = ["derive"] } serde = { version = "1.0.136", features = ["derive"], optional = true } frame-benchmarking = { version = "4.0.0-dev", default-features = false, optional = true, path = "../benchmarking" } frame-support = { version = "4.0.0-dev", default-features = false, path = "../support" } diff --git a/frame/democracy/Cargo.toml b/frame/democracy/Cargo.toml index fa107218c..1f46bfaed 100644 --- a/frame/democracy/Cargo.toml +++ b/frame/democracy/Cargo.toml @@ -16,7 +16,7 @@ targets = ["x86_64-unknown-linux-gnu"] codec = { package = "parity-scale-codec", version = "3.2.2", default-features = false, features = [ "derive", ] } -scale-info = { version = "2.1.1", default-features = false, features = ["derive"] } +scale-info = { version = "2.5.0", default-features = false, features = ["derive"] } serde = { version = "1.0.136", features = ["derive"], optional = true } frame-benchmarking = { version = "4.0.0-dev", default-features = false, optional = true, path = "../benchmarking" } frame-support = { version = "4.0.0-dev", default-features = false, path = "../support" } diff --git a/frame/election-provider-multi-phase/Cargo.toml b/frame/election-provider-multi-phase/Cargo.toml index aa734850a..c88ed0120 100644 --- a/frame/election-provider-multi-phase/Cargo.toml +++ b/frame/election-provider-multi-phase/Cargo.toml @@ -15,7 +15,7 @@ targets = ["x86_64-unknown-linux-gnu"] codec = { package = "parity-scale-codec", version = "3.2.2", default-features = false, features = [ "derive", ] } -scale-info = { version = "2.1.1", default-features = false, features = [ +scale-info = { version = "2.5.0", default-features = false, features = [ "derive", ] } log = { version = "0.4.17", default-features = false } diff --git a/frame/election-provider-support/Cargo.toml b/frame/election-provider-support/Cargo.toml index 114caee79..a519d964c 100644 --- a/frame/election-provider-support/Cargo.toml +++ b/frame/election-provider-support/Cargo.toml @@ -13,7 +13,7 @@ targets = ["x86_64-unknown-linux-gnu"] [dependencies] codec = { package = "parity-scale-codec", version = "3.2.2", default-features = false, features = ["derive"] } -scale-info = { version = "2.1.1", default-features = false, features = ["derive"] } +scale-info = { version = "2.5.0", default-features = false, features = ["derive"] } frame-election-provider-solution-type = { version = "4.0.0-dev", path = "solution-type" } frame-support = { version = "4.0.0-dev", default-features = false, path = "../support" } frame-system = { version = "4.0.0-dev", default-features = false, path = "../system" } diff --git a/frame/election-provider-support/solution-type/fuzzer/Cargo.toml b/frame/election-provider-support/solution-type/fuzzer/Cargo.toml index 927569278..580e5eba9 100644 --- a/frame/election-provider-support/solution-type/fuzzer/Cargo.toml +++ b/frame/election-provider-support/solution-type/fuzzer/Cargo.toml @@ -18,7 +18,7 @@ honggfuzz = "0.5" rand = { version = "0.8", features = ["std", "small_rng"] } codec = { package = "parity-scale-codec", version = "3.2.2", default-features = false, features = ["derive"] } -scale-info = { version = "2.1.1", default-features = false, features = ["derive"] } +scale-info = { version = "2.5.0", default-features = false, features = ["derive"] } frame-election-provider-solution-type = { version = "4.0.0-dev", path = ".." } frame-election-provider-support = { version = "4.0.0-dev", path = "../.." } sp-arithmetic = { version = "6.0.0", path = "../../../../primitives/arithmetic" } diff --git a/frame/examples/basic/Cargo.toml b/frame/examples/basic/Cargo.toml index 2f854011b..848d4bbc9 100644 --- a/frame/examples/basic/Cargo.toml +++ b/frame/examples/basic/Cargo.toml @@ -15,7 +15,7 @@ targets = ["x86_64-unknown-linux-gnu"] [dependencies] codec = { package = "parity-scale-codec", version = "3.2.2", default-features = false } log = { version = "0.4.17", default-features = false } -scale-info = { version = "2.1.1", default-features = false, features = ["derive"] } +scale-info = { version = "2.5.0", default-features = false, features = ["derive"] } frame-benchmarking = { version = "4.0.0-dev", default-features = false, optional = true, path = "../../benchmarking" } frame-support = { version = "4.0.0-dev", default-features = false, path = "../../support" } frame-system = { version = "4.0.0-dev", default-features = false, path = "../../system" } diff --git a/frame/examples/offchain-worker/Cargo.toml b/frame/examples/offchain-worker/Cargo.toml index fb4c12ae0..e582b0f99 100644 --- a/frame/examples/offchain-worker/Cargo.toml +++ b/frame/examples/offchain-worker/Cargo.toml @@ -16,7 +16,7 @@ targets = ["x86_64-unknown-linux-gnu"] codec = { package = "parity-scale-codec", version = "3.2.2", default-features = false } lite-json = { version = "0.2.0", default-features = false } log = { version = "0.4.17", default-features = false } -scale-info = { version = "2.1.1", default-features = false, features = ["derive"] } +scale-info = { version = "2.5.0", default-features = false, features = ["derive"] } frame-support = { version = "4.0.0-dev", default-features = false, path = "../../support" } frame-system = { version = "4.0.0-dev", default-features = false, path = "../../system" } sp-core = { version = "7.0.0", default-features = false, path = "../../../primitives/core" } diff --git a/frame/executive/Cargo.toml b/frame/executive/Cargo.toml index 1636cb392..ed661b8ac 100644 --- a/frame/executive/Cargo.toml +++ b/frame/executive/Cargo.toml @@ -16,7 +16,7 @@ targets = ["x86_64-unknown-linux-gnu"] codec = { package = "parity-scale-codec", version = "3.2.2", default-features = false, features = [ "derive", ] } -scale-info = { version = "2.1.1", default-features = false, features = ["derive"] } +scale-info = { version = "2.5.0", default-features = false, features = ["derive"] } frame-support = { version = "4.0.0-dev", default-features = false, path = "../support" } frame-system = { version = "4.0.0-dev", default-features = false, path = "../system" } frame-try-runtime = { version = "0.10.0-dev", default-features = false, path = "../try-runtime", optional = true } diff --git a/frame/fast-unstake/Cargo.toml b/frame/fast-unstake/Cargo.toml index 8e4df2f2b..93fceefa2 100644 --- a/frame/fast-unstake/Cargo.toml +++ b/frame/fast-unstake/Cargo.toml @@ -14,7 +14,7 @@ targets = ["x86_64-unknown-linux-gnu"] [dependencies] codec = { package = "parity-scale-codec", version = "3.2.2", default-features = false } log = { version = "0.4.17", default-features = false } -scale-info = { version = "2.1.1", default-features = false, features = ["derive"] } +scale-info = { version = "2.5.0", default-features = false, features = ["derive"] } frame-support = { version = "4.0.0-dev", default-features = false, path = "../support" } frame-system = { version = "4.0.0-dev", default-features = false, path = "../system" } diff --git a/frame/glutton/Cargo.toml b/frame/glutton/Cargo.toml index c0f031246..bee0d3db6 100644 --- a/frame/glutton/Cargo.toml +++ b/frame/glutton/Cargo.toml @@ -15,7 +15,7 @@ targets = ["x86_64-unknown-linux-gnu"] [dependencies] blake2 = { version = "0.10.4", default-features = false } codec = { package = "parity-scale-codec", version = "3.0.0", default-features = false, features = ["derive"] } -scale-info = { version = "2.1.1", default-features = false, features = ["derive"] } +scale-info = { version = "2.5.0", default-features = false, features = ["derive"] } log = { version = "0.4.14", default-features = false } frame-benchmarking = { version = "4.0.0-dev", default-features = false, optional = true, path = "../benchmarking" } frame-support = { version = "4.0.0-dev", default-features = false, path = "../support" } diff --git a/frame/grandpa/Cargo.toml b/frame/grandpa/Cargo.toml index 4eb796cf0..288d63512 100644 --- a/frame/grandpa/Cargo.toml +++ b/frame/grandpa/Cargo.toml @@ -15,7 +15,7 @@ targets = ["x86_64-unknown-linux-gnu"] [dependencies] codec = { package = "parity-scale-codec", version = "3.2.2", default-features = false, features = ["derive"] } log = { version = "0.4.17", default-features = false } -scale-info = { version = "2.1.1", default-features = false, features = ["derive"] } +scale-info = { version = "2.5.0", default-features = false, features = ["derive"] } frame-benchmarking = { version = "4.0.0-dev", default-features = false, optional = true, path = "../benchmarking" } frame-support = { version = "4.0.0-dev", default-features = false, path = "../support" } frame-system = { version = "4.0.0-dev", default-features = false, path = "../system" } diff --git a/frame/identity/Cargo.toml b/frame/identity/Cargo.toml index c1a250368..23c690889 100644 --- a/frame/identity/Cargo.toml +++ b/frame/identity/Cargo.toml @@ -15,7 +15,7 @@ targets = ["x86_64-unknown-linux-gnu"] [dependencies] codec = { package = "parity-scale-codec", version = "3.2.2", default-features = false, features = ["derive", "max-encoded-len"] } enumflags2 = { version = "0.7.4" } -scale-info = { version = "2.1.1", default-features = false, features = ["derive"] } +scale-info = { version = "2.5.0", default-features = false, features = ["derive"] } frame-benchmarking = { version = "4.0.0-dev", default-features = false, optional = true, path = "../benchmarking" } frame-support = { version = "4.0.0-dev", default-features = false, path = "../support" } frame-system = { version = "4.0.0-dev", default-features = false, path = "../system" } diff --git a/frame/identity/src/types.rs b/frame/identity/src/types.rs index 4d59a5280..1837b30b0 100644 --- a/frame/identity/src/types.rs +++ b/frame/identity/src/types.rs @@ -442,7 +442,7 @@ mod tests { let mut registry = scale_info::Registry::new(); let type_id = registry.register_type(&scale_info::meta_type::()); let registry: scale_info::PortableRegistry = registry.into(); - let type_info = registry.resolve(type_id.id()).unwrap(); + let type_info = registry.resolve(type_id.id).unwrap(); let check_type_info = |data: &Data| { let variant_name = match data { @@ -453,20 +453,20 @@ mod tests { Data::ShaThree256(_) => "ShaThree256".to_string(), Data::Raw(bytes) => format!("Raw{}", bytes.len()), }; - if let scale_info::TypeDef::Variant(variant) = type_info.type_def() { + if let scale_info::TypeDef::Variant(variant) = &type_info.type_def { let variant = variant - .variants() + .variants .iter() - .find(|v| v.name() == &variant_name) + .find(|v| v.name == variant_name) .expect(&format!("Expected to find variant {}", variant_name)); let field_arr_len = variant - .fields() + .fields .first() - .and_then(|f| registry.resolve(f.ty().id())) + .and_then(|f| registry.resolve(f.ty.id)) .map(|ty| { - if let scale_info::TypeDef::Array(arr) = ty.type_def() { - arr.len() + if let scale_info::TypeDef::Array(arr) = &ty.type_def { + arr.len } else { panic!("Should be an array type") } @@ -474,7 +474,7 @@ mod tests { .unwrap_or(0); let encoded = data.encode(); - assert_eq!(encoded[0], variant.index()); + assert_eq!(encoded[0], variant.index); assert_eq!(encoded.len() as u32 - 1, field_arr_len); } else { panic!("Should be a variant type") diff --git a/frame/im-online/Cargo.toml b/frame/im-online/Cargo.toml index d76fba4fb..881139ad5 100644 --- a/frame/im-online/Cargo.toml +++ b/frame/im-online/Cargo.toml @@ -15,7 +15,7 @@ targets = ["x86_64-unknown-linux-gnu"] [dependencies] codec = { package = "parity-scale-codec", version = "3.2.2", default-features = false, features = ["derive"] } log = { version = "0.4.17", default-features = false } -scale-info = { version = "2.1.1", default-features = false, features = ["derive"] } +scale-info = { version = "2.5.0", default-features = false, features = ["derive"] } frame-benchmarking = { version = "4.0.0-dev", default-features = false, optional = true, path = "../benchmarking" } frame-support = { version = "4.0.0-dev", default-features = false, path = "../support" } frame-system = { version = "4.0.0-dev", default-features = false, path = "../system" } diff --git a/frame/indices/Cargo.toml b/frame/indices/Cargo.toml index b667916eb..6895f076d 100644 --- a/frame/indices/Cargo.toml +++ b/frame/indices/Cargo.toml @@ -14,7 +14,7 @@ targets = ["x86_64-unknown-linux-gnu"] [dependencies] codec = { package = "parity-scale-codec", version = "3.2.2", default-features = false, features = ["derive"] } -scale-info = { version = "2.1.1", default-features = false, features = ["derive"] } +scale-info = { version = "2.5.0", default-features = false, features = ["derive"] } frame-benchmarking = { version = "4.0.0-dev", default-features = false, optional = true, path = "../benchmarking" } frame-support = { version = "4.0.0-dev", default-features = false, path = "../support" } frame-system = { version = "4.0.0-dev", default-features = false, path = "../system" } diff --git a/frame/insecure-randomness-collective-flip/Cargo.toml b/frame/insecure-randomness-collective-flip/Cargo.toml index 68ccfadfc..bcebff6a9 100644 --- a/frame/insecure-randomness-collective-flip/Cargo.toml +++ b/frame/insecure-randomness-collective-flip/Cargo.toml @@ -15,7 +15,7 @@ targets = ["x86_64-unknown-linux-gnu"] [dependencies] codec = { package = "parity-scale-codec", version = "3.2.2", default-features = false, features = ["derive"] } safe-mix = { version = "1.0", default-features = false } -scale-info = { version = "2.1.1", default-features = false, features = ["derive"] } +scale-info = { version = "2.5.0", default-features = false, features = ["derive"] } frame-support = { version = "4.0.0-dev", default-features = false, path = "../support" } frame-system = { version = "4.0.0-dev", default-features = false, path = "../system" } sp-runtime = { version = "7.0.0", default-features = false, path = "../../primitives/runtime" } diff --git a/frame/lottery/Cargo.toml b/frame/lottery/Cargo.toml index 8c1bf3010..4d8839612 100644 --- a/frame/lottery/Cargo.toml +++ b/frame/lottery/Cargo.toml @@ -15,7 +15,7 @@ targets = ["x86_64-unknown-linux-gnu"] codec = { package = "parity-scale-codec", version = "3.2.2", default-features = false, features = [ "derive", ] } -scale-info = { version = "2.1.1", default-features = false, features = ["derive"] } +scale-info = { version = "2.5.0", default-features = false, features = ["derive"] } frame-benchmarking = { version = "4.0.0-dev", default-features = false, optional = true, path = "../benchmarking" } frame-support = { version = "4.0.0-dev", default-features = false, path = "../support" } frame-system = { version = "4.0.0-dev", default-features = false, path = "../system" } diff --git a/frame/membership/Cargo.toml b/frame/membership/Cargo.toml index bba5700cd..330d9401d 100644 --- a/frame/membership/Cargo.toml +++ b/frame/membership/Cargo.toml @@ -15,7 +15,7 @@ targets = ["x86_64-unknown-linux-gnu"] [dependencies] codec = { package = "parity-scale-codec", version = "3.2.2", default-features = false } log = { version = "0.4.17", default-features = false } -scale-info = { version = "2.1.1", default-features = false, features = ["derive"] } +scale-info = { version = "2.5.0", default-features = false, features = ["derive"] } frame-benchmarking = { version = "4.0.0-dev", default-features = false, optional = true, path = "../benchmarking" } frame-support = { version = "4.0.0-dev", default-features = false, path = "../support" } frame-system = { version = "4.0.0-dev", default-features = false, path = "../system" } diff --git a/frame/merkle-mountain-range/Cargo.toml b/frame/merkle-mountain-range/Cargo.toml index 3f7180f79..5f6094af1 100644 --- a/frame/merkle-mountain-range/Cargo.toml +++ b/frame/merkle-mountain-range/Cargo.toml @@ -13,7 +13,7 @@ targets = ["x86_64-unknown-linux-gnu"] [dependencies] codec = { package = "parity-scale-codec", version = "3.2.2", default-features = false } -scale-info = { version = "2.1.1", default-features = false, features = ["derive"] } +scale-info = { version = "2.5.0", default-features = false, features = ["derive"] } frame-benchmarking = { version = "4.0.0-dev", default-features = false, optional = true, path = "../benchmarking" } frame-support = { version = "4.0.0-dev", default-features = false, path = "../support" } frame-system = { version = "4.0.0-dev", default-features = false, path = "../system" } diff --git a/frame/message-queue/Cargo.toml b/frame/message-queue/Cargo.toml index 115200b82..404e67813 100644 --- a/frame/message-queue/Cargo.toml +++ b/frame/message-queue/Cargo.toml @@ -10,7 +10,7 @@ description = "FRAME pallet to queue and process messages" [dependencies] codec = { package = "parity-scale-codec", version = "3.2.2", default-features = false, features = ["derive"] } -scale-info = { version = "2.1.2", default-features = false, features = ["derive"] } +scale-info = { version = "2.5.0", default-features = false, features = ["derive"] } serde = { version = "1.0.137", optional = true, features = ["derive"] } log = { version = "0.4.17", default-features = false } diff --git a/frame/multisig/Cargo.toml b/frame/multisig/Cargo.toml index 5cd744124..98a15c5a5 100644 --- a/frame/multisig/Cargo.toml +++ b/frame/multisig/Cargo.toml @@ -14,7 +14,7 @@ targets = ["x86_64-unknown-linux-gnu"] [dependencies] codec = { package = "parity-scale-codec", version = "3.2.2", default-features = false } -scale-info = { version = "2.1.1", default-features = false, features = ["derive"] } +scale-info = { version = "2.5.0", default-features = false, features = ["derive"] } frame-benchmarking = { version = "4.0.0-dev", default-features = false, optional = true, path = "../benchmarking" } frame-support = { version = "4.0.0-dev", default-features = false, path = "../support" } frame-system = { version = "4.0.0-dev", default-features = false, path = "../system" } diff --git a/frame/nfts/Cargo.toml b/frame/nfts/Cargo.toml index 59aa4e091..96d46dacb 100644 --- a/frame/nfts/Cargo.toml +++ b/frame/nfts/Cargo.toml @@ -16,7 +16,7 @@ targets = ["x86_64-unknown-linux-gnu"] codec = { package = "parity-scale-codec", version = "3.2.2", default-features = false } enumflags2 = { version = "0.7.5" } log = { version = "0.4.17", default-features = false } -scale-info = { version = "2.1.1", default-features = false, features = ["derive"] } +scale-info = { version = "2.5.0", default-features = false, features = ["derive"] } frame-benchmarking = { version = "4.0.0-dev", default-features = false, optional = true, path = "../benchmarking" } frame-support = { version = "4.0.0-dev", default-features = false, path = "../support" } frame-system = { version = "4.0.0-dev", default-features = false, path = "../system" } diff --git a/frame/nicks/Cargo.toml b/frame/nicks/Cargo.toml index 1c829efc2..50fc4ef68 100644 --- a/frame/nicks/Cargo.toml +++ b/frame/nicks/Cargo.toml @@ -14,7 +14,7 @@ targets = ["x86_64-unknown-linux-gnu"] [dependencies] codec = { package = "parity-scale-codec", version = "3.2.2", default-features = false, features = ["derive"] } -scale-info = { version = "2.1.1", default-features = false, features = ["derive"] } +scale-info = { version = "2.5.0", default-features = false, features = ["derive"] } frame-support = { version = "4.0.0-dev", default-features = false, path = "../support" } frame-system = { version = "4.0.0-dev", default-features = false, path = "../system" } sp-io = { version = "7.0.0", default-features = false, path = "../../primitives/io" } diff --git a/frame/nis/Cargo.toml b/frame/nis/Cargo.toml index a5fc29cd7..c12d4f2c0 100644 --- a/frame/nis/Cargo.toml +++ b/frame/nis/Cargo.toml @@ -14,7 +14,7 @@ targets = ["x86_64-unknown-linux-gnu"] [dependencies] codec = { package = "parity-scale-codec", version = "3.2.2", default-features = false, features = ["derive"] } -scale-info = { version = "2.1.1", default-features = false, features = ["derive"] } +scale-info = { version = "2.5.0", default-features = false, features = ["derive"] } frame-benchmarking = { version = "4.0.0-dev", default-features = false, optional = true, path = "../benchmarking" } frame-support = { version = "4.0.0-dev", default-features = false, path = "../support" } frame-system = { version = "4.0.0-dev", default-features = false, path = "../system" } diff --git a/frame/node-authorization/Cargo.toml b/frame/node-authorization/Cargo.toml index c60ce1d12..ed3f59a9a 100644 --- a/frame/node-authorization/Cargo.toml +++ b/frame/node-authorization/Cargo.toml @@ -14,7 +14,7 @@ targets = ["x86_64-unknown-linux-gnu"] [dependencies] codec = { package = "parity-scale-codec", version = "3.2.2", default-features = false, features = ["derive"] } log = { version = "0.4.17", default-features = false } -scale-info = { version = "2.1.1", default-features = false, features = ["derive"] } +scale-info = { version = "2.5.0", default-features = false, features = ["derive"] } frame-support = { version = "4.0.0-dev", default-features = false, path = "../support" } frame-system = { version = "4.0.0-dev", default-features = false, path = "../system" } sp-core = { version = "7.0.0", default-features = false, path = "../../primitives/core" } diff --git a/frame/nomination-pools/Cargo.toml b/frame/nomination-pools/Cargo.toml index d51bcec51..c92d9b512 100644 --- a/frame/nomination-pools/Cargo.toml +++ b/frame/nomination-pools/Cargo.toml @@ -14,7 +14,7 @@ targets = ["x86_64-unknown-linux-gnu"] [dependencies] # parity codec = { package = "parity-scale-codec", version = "3.2.2", default-features = false, features = ["derive"] } -scale-info = { version = "2.1.1", default-features = false, features = ["derive"] } +scale-info = { version = "2.5.0", default-features = false, features = ["derive"] } # FRAME frame-support = { version = "4.0.0-dev", default-features = false, path = "../support" } diff --git a/frame/nomination-pools/benchmarking/Cargo.toml b/frame/nomination-pools/benchmarking/Cargo.toml index b96024af7..4f757cafa 100644 --- a/frame/nomination-pools/benchmarking/Cargo.toml +++ b/frame/nomination-pools/benchmarking/Cargo.toml @@ -15,7 +15,7 @@ targets = ["x86_64-unknown-linux-gnu"] [dependencies] # parity codec = { package = "parity-scale-codec", version = "3.2.2", default-features = false, features = ["derive"] } -scale-info = { version = "2.1.1", default-features = false, features = ["derive"] } +scale-info = { version = "2.5.0", default-features = false, features = ["derive"] } # FRAME frame-benchmarking = { version = "4.0.0-dev", default-features = false, path = "../../benchmarking" } diff --git a/frame/offences/Cargo.toml b/frame/offences/Cargo.toml index 47ae264a6..6ebe870a7 100644 --- a/frame/offences/Cargo.toml +++ b/frame/offences/Cargo.toml @@ -15,7 +15,7 @@ targets = ["x86_64-unknown-linux-gnu"] [dependencies] codec = { package = "parity-scale-codec", version = "3.2.2", default-features = false, features = ["derive"] } log = { version = "0.4.17", default-features = false } -scale-info = { version = "2.1.1", default-features = false, features = ["derive"] } +scale-info = { version = "2.5.0", default-features = false, features = ["derive"] } serde = { version = "1.0.136", optional = true } frame-support = { version = "4.0.0-dev", default-features = false, path = "../support" } frame-system = { version = "4.0.0-dev", default-features = false, path = "../system" } diff --git a/frame/preimage/Cargo.toml b/frame/preimage/Cargo.toml index 6bc6c8c53..57dda88b1 100644 --- a/frame/preimage/Cargo.toml +++ b/frame/preimage/Cargo.toml @@ -10,7 +10,7 @@ description = "FRAME pallet for storing preimages of hashes" [dependencies] codec = { package = "parity-scale-codec", version = "3.2.2", default-features = false, features = ["derive"] } -scale-info = { version = "2.1.1", default-features = false, features = ["derive"] } +scale-info = { version = "2.5.0", default-features = false, features = ["derive"] } frame-benchmarking = { version = "4.0.0-dev", default-features = false, optional = true, path = "../benchmarking" } frame-support = { version = "4.0.0-dev", default-features = false, path = "../support" } frame-system = { version = "4.0.0-dev", default-features = false, path = "../system" } diff --git a/frame/proxy/Cargo.toml b/frame/proxy/Cargo.toml index 5c69fcfb9..065cbe5eb 100644 --- a/frame/proxy/Cargo.toml +++ b/frame/proxy/Cargo.toml @@ -14,7 +14,7 @@ targets = ["x86_64-unknown-linux-gnu"] [dependencies] codec = { package = "parity-scale-codec", version = "3.2.2", default-features = false, features = ["max-encoded-len"] } -scale-info = { version = "2.1.1", default-features = false, features = ["derive"] } +scale-info = { version = "2.5.0", default-features = false, features = ["derive"] } frame-benchmarking = { version = "4.0.0-dev", default-features = false, optional = true, path = "../benchmarking" } frame-support = { version = "4.0.0-dev", default-features = false, path = "../support" } frame-system = { version = "4.0.0-dev", default-features = false, path = "../system" } diff --git a/frame/recovery/Cargo.toml b/frame/recovery/Cargo.toml index 3c25c0002..b2e3236e4 100644 --- a/frame/recovery/Cargo.toml +++ b/frame/recovery/Cargo.toml @@ -14,7 +14,7 @@ targets = ["x86_64-unknown-linux-gnu"] [dependencies] codec = { package = "parity-scale-codec", version = "3.2.2", default-features = false, features = ["derive"] } -scale-info = { version = "2.1.1", default-features = false, features = ["derive"] } +scale-info = { version = "2.5.0", default-features = false, features = ["derive"] } frame-benchmarking = { version = "4.0.0-dev", default-features = false, optional = true, path = "../benchmarking" } frame-support = { version = "4.0.0-dev", default-features = false, path = "../support" } frame-system = { version = "4.0.0-dev", default-features = false, path = "../system" } diff --git a/frame/referenda/Cargo.toml b/frame/referenda/Cargo.toml index ac4c8044d..5a092c63b 100644 --- a/frame/referenda/Cargo.toml +++ b/frame/referenda/Cargo.toml @@ -17,7 +17,7 @@ assert_matches = { version = "1.5", optional = true } codec = { package = "parity-scale-codec", version = "3.2.2", default-features = false, features = [ "derive", ] } -scale-info = { version = "2.1.1", default-features = false, features = ["derive"] } +scale-info = { version = "2.5.0", default-features = false, features = ["derive"] } serde = { version = "1.0.136", features = ["derive"], optional = true } sp-arithmetic = { version = "6.0.0", default-features = false, path = "../../primitives/arithmetic" } frame-benchmarking = { version = "4.0.0-dev", default-features = false, optional = true, path = "../benchmarking" } diff --git a/frame/remark/Cargo.toml b/frame/remark/Cargo.toml index 151cc3888..5fc595d71 100644 --- a/frame/remark/Cargo.toml +++ b/frame/remark/Cargo.toml @@ -14,7 +14,7 @@ targets = ["x86_64-unknown-linux-gnu"] [dependencies] codec = { package = "parity-scale-codec", version = "3.2.2", default-features = false } -scale-info = { version = "2.1.1", default-features = false, features = ["derive"] } +scale-info = { version = "2.5.0", default-features = false, features = ["derive"] } serde = { version = "1.0.136", optional = true } frame-benchmarking = { version = "4.0.0-dev", default-features = false, optional = true, path = "../benchmarking" } frame-support = { version = "4.0.0-dev", default-features = false, path = "../support" } diff --git a/frame/root-offences/Cargo.toml b/frame/root-offences/Cargo.toml index fd1f7c632..58fa281dc 100644 --- a/frame/root-offences/Cargo.toml +++ b/frame/root-offences/Cargo.toml @@ -14,7 +14,7 @@ targets = ["x86_64-unknown-linux-gnu"] [dependencies] codec = { package = "parity-scale-codec", version = "3.2.2", default-features = false, features = ["derive"] } -scale-info = { version = "2.1.1", default-features = false, features = ["derive"] } +scale-info = { version = "2.5.0", default-features = false, features = ["derive"] } pallet-session = { version = "4.0.0-dev", features = [ "historical" ], path = "../../frame/session", default-features = false } pallet-staking = { version = "4.0.0-dev", default-features = false, path = "../../frame/staking" } diff --git a/frame/root-testing/Cargo.toml b/frame/root-testing/Cargo.toml index 09262b3d3..6e8c13f77 100644 --- a/frame/root-testing/Cargo.toml +++ b/frame/root-testing/Cargo.toml @@ -15,7 +15,7 @@ targets = ["x86_64-unknown-linux-gnu"] [dependencies] codec = { package = "parity-scale-codec", version = "3.2.2", default-features = false, features = ["derive"] } -scale-info = { version = "2.1.1", default-features = false, features = ["derive"] } +scale-info = { version = "2.5.0", default-features = false, features = ["derive"] } frame-support = { version = "4.0.0-dev", default-features = false, path = "../support" } frame-system = { version = "4.0.0-dev", default-features = false, path = "../system" } sp-core = { version = "7.0.0", default-features = false, path = "../../primitives/core" } diff --git a/frame/scheduler/Cargo.toml b/frame/scheduler/Cargo.toml index 9a67e68a0..50afac933 100644 --- a/frame/scheduler/Cargo.toml +++ b/frame/scheduler/Cargo.toml @@ -12,7 +12,7 @@ readme = "README.md" [dependencies] codec = { package = "parity-scale-codec", version = "3.2.2", default-features = false, features = ["derive"] } log = { version = "0.4.17", default-features = false } -scale-info = { version = "2.1.1", default-features = false, features = ["derive"] } +scale-info = { version = "2.5.0", default-features = false, features = ["derive"] } frame-benchmarking = { version = "4.0.0-dev", default-features = false, optional = true, path = "../benchmarking" } frame-support = { version = "4.0.0-dev", default-features = false, path = "../support" } frame-system = { version = "4.0.0-dev", default-features = false, path = "../system" } diff --git a/frame/scored-pool/Cargo.toml b/frame/scored-pool/Cargo.toml index d44fc1b2f..f38743e8b 100644 --- a/frame/scored-pool/Cargo.toml +++ b/frame/scored-pool/Cargo.toml @@ -14,7 +14,7 @@ targets = ["x86_64-unknown-linux-gnu"] [dependencies] codec = { package = "parity-scale-codec", version = "3.2.2", default-features = false, features = ["derive"] } -scale-info = { version = "2.1.1", default-features = false, features = ["derive"] } +scale-info = { version = "2.5.0", default-features = false, features = ["derive"] } frame-support = { version = "4.0.0-dev", default-features = false, path = "../support" } frame-system = { version = "4.0.0-dev", default-features = false, path = "../system" } sp-io = { version = "7.0.0", default-features = false, path = "../../primitives/io" } diff --git a/frame/session/Cargo.toml b/frame/session/Cargo.toml index bd28bffff..ccf3f7d86 100644 --- a/frame/session/Cargo.toml +++ b/frame/session/Cargo.toml @@ -16,7 +16,7 @@ targets = ["x86_64-unknown-linux-gnu"] codec = { package = "parity-scale-codec", version = "3.2.2", default-features = false, features = ["derive"] } impl-trait-for-tuples = "0.2.2" log = { version = "0.4.17", default-features = false } -scale-info = { version = "2.1.1", default-features = false, features = ["derive"] } +scale-info = { version = "2.5.0", default-features = false, features = ["derive"] } frame-support = { version = "4.0.0-dev", default-features = false, path = "../support" } frame-system = { version = "4.0.0-dev", default-features = false, path = "../system" } pallet-timestamp = { version = "4.0.0-dev", default-features = false, path = "../timestamp" } diff --git a/frame/society/Cargo.toml b/frame/society/Cargo.toml index ddc6ea6aa..ab74e39e4 100644 --- a/frame/society/Cargo.toml +++ b/frame/society/Cargo.toml @@ -15,7 +15,7 @@ targets = ["x86_64-unknown-linux-gnu"] [dependencies] codec = { package = "parity-scale-codec", version = "3.2.2", default-features = false, features = ["derive"] } rand_chacha = { version = "0.2", default-features = false } -scale-info = { version = "2.1.1", default-features = false, features = ["derive"] } +scale-info = { version = "2.5.0", default-features = false, features = ["derive"] } frame-support = { version = "4.0.0-dev", default-features = false, path = "../support" } frame-system = { version = "4.0.0-dev", default-features = false, path = "../system" } sp-runtime = { version = "7.0.0", default-features = false, path = "../../primitives/runtime" } diff --git a/frame/staking/Cargo.toml b/frame/staking/Cargo.toml index 79c0bb5c2..c20f00310 100644 --- a/frame/staking/Cargo.toml +++ b/frame/staking/Cargo.toml @@ -17,7 +17,7 @@ serde = { version = "1.0.136", optional = true } codec = { package = "parity-scale-codec", version = "3.2.2", default-features = false, features = [ "derive", ] } -scale-info = { version = "2.1.1", default-features = false, features = ["derive"] } +scale-info = { version = "2.5.0", default-features = false, features = ["derive"] } sp-std = { version = "5.0.0", default-features = false, path = "../../primitives/std" } sp-io = { version = "7.0.0", default-features = false, path = "../../primitives/io" } sp-runtime = { version = "7.0.0", default-features = false, path = "../../primitives/runtime" } diff --git a/frame/state-trie-migration/Cargo.toml b/frame/state-trie-migration/Cargo.toml index 36b5912a6..8485db5a3 100644 --- a/frame/state-trie-migration/Cargo.toml +++ b/frame/state-trie-migration/Cargo.toml @@ -14,7 +14,7 @@ targets = ["x86_64-unknown-linux-gnu"] [dependencies] codec = { package = "parity-scale-codec", version = "3.2.2", default-features = false } log = { version = "0.4.17", default-features = false } -scale-info = { version = "2.1.1", default-features = false, features = ["derive"] } +scale-info = { version = "2.5.0", default-features = false, features = ["derive"] } serde = { version = "1.0.133", optional = true } thousands = { version = "0.2.0", optional = true } zstd = { version = "0.11.2", default-features = false, optional = true } diff --git a/frame/sudo/Cargo.toml b/frame/sudo/Cargo.toml index 56d04b172..e1161497b 100644 --- a/frame/sudo/Cargo.toml +++ b/frame/sudo/Cargo.toml @@ -14,7 +14,7 @@ targets = ["x86_64-unknown-linux-gnu"] [dependencies] codec = { package = "parity-scale-codec", version = "3.2.2", default-features = false, features = ["derive"] } -scale-info = { version = "2.1.1", default-features = false, features = ["derive"] } +scale-info = { version = "2.5.0", default-features = false, features = ["derive"] } frame-support = { version = "4.0.0-dev", default-features = false, path = "../support" } frame-system = { version = "4.0.0-dev", default-features = false, path = "../system" } sp-io = { version = "7.0.0", default-features = false, path = "../../primitives/io" } diff --git a/frame/support/Cargo.toml b/frame/support/Cargo.toml index 7792fdf01..f62609585 100644 --- a/frame/support/Cargo.toml +++ b/frame/support/Cargo.toml @@ -15,7 +15,7 @@ targets = ["x86_64-unknown-linux-gnu"] [dependencies] serde = { version = "1.0.136", optional = true, features = ["derive"] } codec = { package = "parity-scale-codec", version = "3.2.2", default-features = false, features = ["derive", "max-encoded-len"] } -scale-info = { version = "2.1.1", default-features = false, features = ["derive"] } +scale-info = { version = "2.5.0", default-features = false, features = ["derive"] } frame-metadata = { version = "15.1.0", default-features = false, features = ["v14", "v15-unstable"] } sp-api = { version = "4.0.0-dev", default-features = false, path = "../../primitives/api" } sp-std = { version = "5.0.0", default-features = false, path = "../../primitives/std" } diff --git a/frame/support/test/Cargo.toml b/frame/support/test/Cargo.toml index ad4589c73..9564b6a04 100644 --- a/frame/support/test/Cargo.toml +++ b/frame/support/test/Cargo.toml @@ -14,7 +14,7 @@ targets = ["x86_64-unknown-linux-gnu"] [dependencies] serde = { version = "1.0.136", default-features = false, features = ["derive"] } codec = { package = "parity-scale-codec", version = "3.2.2", default-features = false, features = ["derive"] } -scale-info = { version = "2.1.1", default-features = false, features = ["derive"] } +scale-info = { version = "2.5.0", default-features = false, features = ["derive"] } sp-api = { version = "4.0.0-dev", default-features = false, path = "../../../primitives/api" } sp-arithmetic = { version = "6.0.0", default-features = false, path = "../../../primitives/arithmetic" } sp-io = { version = "7.0.0", path = "../../../primitives/io", default-features = false } diff --git a/frame/support/test/compile_pass/Cargo.toml b/frame/support/test/compile_pass/Cargo.toml index dd5e1b996..4466c24d6 100644 --- a/frame/support/test/compile_pass/Cargo.toml +++ b/frame/support/test/compile_pass/Cargo.toml @@ -13,7 +13,7 @@ targets = ["x86_64-unknown-linux-gnu"] [dependencies] codec = { package = "parity-scale-codec", version = "3.2.2", default-features = false, features = ["derive"] } -scale-info = { version = "2.1.1", default-features = false, features = ["derive"] } +scale-info = { version = "2.5.0", default-features = false, features = ["derive"] } frame-support = { version = "4.0.0-dev", default-features = false, path = "../../" } frame-system = { version = "4.0.0-dev", default-features = false, path = "../../../system" } sp-core = { version = "7.0.0", default-features = false, path = "../../../../primitives/core" } diff --git a/frame/support/test/tests/pallet.rs b/frame/support/test/tests/pallet.rs index f1dc97223..f3c4e0740 100644 --- a/frame/support/test/tests/pallet.rs +++ b/frame/support/test/tests/pallet.rs @@ -1616,9 +1616,9 @@ fn metadata() { }, ]; - let empty_doc = pallets[0].event.as_ref().unwrap().ty.type_info().docs().is_empty() && - pallets[0].error.as_ref().unwrap().ty.type_info().docs().is_empty() && - pallets[0].calls.as_ref().unwrap().ty.type_info().docs().is_empty(); + let empty_doc = pallets[0].event.as_ref().unwrap().ty.type_info().docs.is_empty() && + pallets[0].error.as_ref().unwrap().ty.type_info().docs.is_empty() && + pallets[0].calls.as_ref().unwrap().ty.type_info().docs.is_empty(); if cfg!(feature = "no-metadata-docs") { assert!(empty_doc) diff --git a/frame/support/test/tests/pallet_compatibility.rs b/frame/support/test/tests/pallet_compatibility.rs index 077b3c96e..610316ecf 100644 --- a/frame/support/test/tests/pallet_compatibility.rs +++ b/frame/support/test/tests/pallet_compatibility.rs @@ -295,14 +295,14 @@ mod test { }; let assert_meta_types = |ty_id1, ty_id2| { - let ty1 = types.resolve(ty_id1).map(|ty| ty.type_def()); - let ty2 = types.resolve(ty_id2).map(|ty| ty.type_def()); + let ty1 = types.resolve(ty_id1).map(|ty| ty.type_def.clone()); + let ty2 = types.resolve(ty_id2).map(|ty| ty.type_def.clone()); pretty_assertions::assert_eq!(ty1, ty2); }; - let get_enum_variants = |ty_id| match types.resolve(ty_id).map(|ty| ty.type_def()) { + let get_enum_variants = |ty_id| match types.resolve(ty_id).map(|ty| ty.type_def.clone()) { Some(ty) => match ty { - scale_info::TypeDef::Variant(var) => var.variants(), + scale_info::TypeDef::Variant(var) => var.variants, _ => panic!("Expected variant type"), }, _ => panic!("No type found"), @@ -314,12 +314,12 @@ mod test { for i in 0..vs1.len() { let v1 = &vs2[i]; let v2 = &vs2[i]; - assert_eq!(v1.fields().len(), v2.fields().len()); - for f in 0..v1.fields().len() { - let f1 = &v1.fields()[f]; - let f2 = &v2.fields()[f]; - pretty_assertions::assert_eq!(f1.name(), f2.name()); - pretty_assertions::assert_eq!(f1.ty(), f2.ty()); + assert_eq!(v1.fields.len(), v2.fields.len()); + for f in 0..v1.fields.len() { + let f1 = &v1.fields[f]; + let f2 = &v2.fields[f]; + pretty_assertions::assert_eq!(f1.name, f2.name); + pretty_assertions::assert_eq!(f1.ty, f2.ty); } } }; @@ -328,21 +328,21 @@ mod test { let calls1 = pallets[1].calls.as_ref().unwrap(); let calls2 = pallets[2].calls.as_ref().unwrap(); - assert_meta_types(calls1.ty.id(), calls2.ty.id()); + assert_meta_types(calls1.ty.id, calls2.ty.id); // event: check variants and fields but ignore the type name which will be different - let event1_variants = get_enum_variants(pallets[1].event.as_ref().unwrap().ty.id()); - let event2_variants = get_enum_variants(pallets[2].event.as_ref().unwrap().ty.id()); - assert_enum_variants(event1_variants, event2_variants); + let event1_variants = get_enum_variants(pallets[1].event.as_ref().unwrap().ty.id); + let event2_variants = get_enum_variants(pallets[2].event.as_ref().unwrap().ty.id); + assert_enum_variants(&event1_variants, &event2_variants); - let err1 = get_enum_variants(pallets[1].error.as_ref().unwrap().ty.id()) + let err1 = get_enum_variants(pallets[1].error.as_ref().unwrap().ty.id) .iter() - .filter(|v| v.name() == "__Ignore") + .filter(|v| v.name == "__Ignore") .cloned() .collect::>(); - let err2 = get_enum_variants(pallets[2].error.as_ref().unwrap().ty.id()) + let err2 = get_enum_variants(pallets[2].error.as_ref().unwrap().ty.id) .iter() - .filter(|v| v.name() == "__Ignore") + .filter(|v| v.name == "__Ignore") .cloned() .collect::>(); assert_enum_variants(&err1, &err2); diff --git a/frame/support/test/tests/pallet_compatibility_instance.rs b/frame/support/test/tests/pallet_compatibility_instance.rs index c641556be..73014d996 100644 --- a/frame/support/test/tests/pallet_compatibility_instance.rs +++ b/frame/support/test/tests/pallet_compatibility_instance.rs @@ -298,9 +298,9 @@ mod test { _ => unreachable!(), }; - let get_enum_variants = |ty_id| match types.resolve(ty_id).map(|ty| ty.type_def()) { + let get_enum_variants = |ty_id| match types.resolve(ty_id).map(|ty| ty.type_def.clone()) { Some(ty) => match ty { - scale_info::TypeDef::Variant(var) => var.variants(), + scale_info::TypeDef::Variant(var) => var.variants, _ => panic!("Expected variant type"), }, _ => panic!("No type found"), @@ -312,12 +312,12 @@ mod test { for i in 0..vs1.len() { let v1 = &vs2[i]; let v2 = &vs2[i]; - assert_eq!(v1.fields().len(), v2.fields().len()); - for f in 0..v1.fields().len() { - let f1 = &v1.fields()[f]; - let f2 = &v2.fields()[f]; - pretty_assertions::assert_eq!(f1.name(), f2.name()); - pretty_assertions::assert_eq!(f1.ty(), f2.ty()); + assert_eq!(v1.fields.len(), v2.fields.len()); + for f in 0..v1.fields.len() { + let f1 = &v1.fields[f]; + let f2 = &v2.fields[f]; + pretty_assertions::assert_eq!(f1.name, f2.name); + pretty_assertions::assert_eq!(f1.ty, f2.ty); } } }; @@ -325,23 +325,23 @@ mod test { for i in vec![1, 3, 5].into_iter() { pretty_assertions::assert_eq!(pallets[i].storage, pallets[i + 1].storage); - let call1_variants = get_enum_variants(pallets[i].calls.as_ref().unwrap().ty.id()); - let call2_variants = get_enum_variants(pallets[i + 1].calls.as_ref().unwrap().ty.id()); - assert_enum_variants(call1_variants, call2_variants); + let call1_variants = get_enum_variants(pallets[i].calls.as_ref().unwrap().ty.id); + let call2_variants = get_enum_variants(pallets[i + 1].calls.as_ref().unwrap().ty.id); + assert_enum_variants(&call1_variants, &call2_variants); // event: check variants and fields but ignore the type name which will be different - let event1_variants = get_enum_variants(pallets[i].event.as_ref().unwrap().ty.id()); - let event2_variants = get_enum_variants(pallets[i + 1].event.as_ref().unwrap().ty.id()); - assert_enum_variants(event1_variants, event2_variants); + let event1_variants = get_enum_variants(pallets[i].event.as_ref().unwrap().ty.id); + let event2_variants = get_enum_variants(pallets[i + 1].event.as_ref().unwrap().ty.id); + assert_enum_variants(&event1_variants, &event2_variants); - let err1 = get_enum_variants(pallets[i].error.as_ref().unwrap().ty.id()) + let err1 = get_enum_variants(pallets[i].error.as_ref().unwrap().ty.id) .iter() - .filter(|v| v.name() == "__Ignore") + .filter(|v| v.name == "__Ignore") .cloned() .collect::>(); - let err2 = get_enum_variants(pallets[i + 1].error.as_ref().unwrap().ty.id()) + let err2 = get_enum_variants(pallets[i + 1].error.as_ref().unwrap().ty.id) .iter() - .filter(|v| v.name() == "__Ignore") + .filter(|v| v.name == "__Ignore") .cloned() .collect::>(); assert_enum_variants(&err1, &err2); diff --git a/frame/system/Cargo.toml b/frame/system/Cargo.toml index 5a6c89aae..c97eb6638 100644 --- a/frame/system/Cargo.toml +++ b/frame/system/Cargo.toml @@ -15,7 +15,7 @@ targets = ["x86_64-unknown-linux-gnu"] [dependencies] codec = { package = "parity-scale-codec", version = "3.2.2", default-features = false, features = ["derive"] } log = { version = "0.4.17", default-features = false } -scale-info = { version = "2.1.1", default-features = false, features = ["derive"] } +scale-info = { version = "2.5.0", default-features = false, features = ["derive"] } serde = { version = "1.0.136", features = ["derive"], optional = true } frame-support = { version = "4.0.0-dev", default-features = false, path = "../support" } sp-core = { version = "7.0.0", default-features = false, path = "../../primitives/core" } diff --git a/frame/system/benchmarking/Cargo.toml b/frame/system/benchmarking/Cargo.toml index 8f0009725..50009387a 100644 --- a/frame/system/benchmarking/Cargo.toml +++ b/frame/system/benchmarking/Cargo.toml @@ -14,7 +14,7 @@ targets = ["x86_64-unknown-linux-gnu"] [dependencies] codec = { package = "parity-scale-codec", version = "3.2.2", default-features = false } -scale-info = { version = "2.1.1", default-features = false, features = ["derive"] } +scale-info = { version = "2.5.0", default-features = false, features = ["derive"] } frame-benchmarking = { version = "4.0.0-dev", default-features = false, path = "../../benchmarking" } frame-support = { version = "4.0.0-dev", default-features = false, path = "../../support" } frame-system = { version = "4.0.0-dev", default-features = false, path = "../../system" } diff --git a/frame/timestamp/Cargo.toml b/frame/timestamp/Cargo.toml index 84c56da24..0f2895525 100644 --- a/frame/timestamp/Cargo.toml +++ b/frame/timestamp/Cargo.toml @@ -16,7 +16,7 @@ targets = ["x86_64-unknown-linux-gnu"] [dependencies] codec = { package = "parity-scale-codec", version = "3.2.2", default-features = false, features = ["derive", "max-encoded-len"] } log = { version = "0.4.17", default-features = false } -scale-info = { version = "2.1.1", default-features = false, features = ["derive"] } +scale-info = { version = "2.5.0", default-features = false, features = ["derive"] } frame-benchmarking = { version = "4.0.0-dev", default-features = false, optional = true, path = "../benchmarking" } frame-support = { version = "4.0.0-dev", default-features = false, path = "../support" } frame-system = { version = "4.0.0-dev", default-features = false, path = "../system" } diff --git a/frame/tips/Cargo.toml b/frame/tips/Cargo.toml index c7db61613..caed4edec 100644 --- a/frame/tips/Cargo.toml +++ b/frame/tips/Cargo.toml @@ -15,7 +15,7 @@ targets = ["x86_64-unknown-linux-gnu"] [dependencies] codec = { package = "parity-scale-codec", version = "3.2.2", default-features = false, features = ["derive"] } log = { version = "0.4.17", default-features = false } -scale-info = { version = "2.1.1", default-features = false, features = ["derive"] } +scale-info = { version = "2.5.0", default-features = false, features = ["derive"] } serde = { version = "1.0.136", features = ["derive"], optional = true } frame-benchmarking = { version = "4.0.0-dev", default-features = false, optional = true, path = "../benchmarking" } frame-support = { version = "4.0.0-dev", default-features = false, path = "../support" } diff --git a/frame/transaction-payment/Cargo.toml b/frame/transaction-payment/Cargo.toml index 0c98796e4..b1e22d3e9 100644 --- a/frame/transaction-payment/Cargo.toml +++ b/frame/transaction-payment/Cargo.toml @@ -16,7 +16,7 @@ targets = ["x86_64-unknown-linux-gnu"] codec = { package = "parity-scale-codec", version = "3.2.2", default-features = false, features = [ "derive", ] } -scale-info = { version = "2.1.1", default-features = false, features = ["derive"] } +scale-info = { version = "2.5.0", default-features = false, features = ["derive"] } serde = { version = "1.0.136", optional = true } frame-support = { version = "4.0.0-dev", default-features = false, path = "../support" } frame-system = { version = "4.0.0-dev", default-features = false, path = "../system" } diff --git a/frame/transaction-payment/asset-tx-payment/Cargo.toml b/frame/transaction-payment/asset-tx-payment/Cargo.toml index 324e17a08..574eb9e23 100644 --- a/frame/transaction-payment/asset-tx-payment/Cargo.toml +++ b/frame/transaction-payment/asset-tx-payment/Cargo.toml @@ -26,7 +26,7 @@ frame-benchmarking = { version = "4.0.0-dev", default-features = false, path = " # Other dependencies codec = { package = "parity-scale-codec", version = "3.2.2", default-features = false, features = ["derive"] } -scale-info = { version = "2.1.1", default-features = false, features = ["derive"] } +scale-info = { version = "2.5.0", default-features = false, features = ["derive"] } serde = { version = "1.0.136", optional = true } [dev-dependencies] diff --git a/frame/transaction-storage/Cargo.toml b/frame/transaction-storage/Cargo.toml index c23be8194..aa19ce3fe 100644 --- a/frame/transaction-storage/Cargo.toml +++ b/frame/transaction-storage/Cargo.toml @@ -15,7 +15,7 @@ targets = ["x86_64-unknown-linux-gnu"] [dependencies] array-bytes = { version = "4.1", optional = true } codec = { package = "parity-scale-codec", version = "3.2.2", default-features = false } -scale-info = { version = "2.1.1", default-features = false, features = ["derive"] } +scale-info = { version = "2.5.0", default-features = false, features = ["derive"] } serde = { version = "1.0.136", optional = true } frame-benchmarking = { version = "4.0.0-dev", default-features = false, optional = true, path = "../benchmarking" } frame-support = { version = "4.0.0-dev", default-features = false, path = "../support" } diff --git a/frame/treasury/Cargo.toml b/frame/treasury/Cargo.toml index 23fcb7944..f3c06735f 100644 --- a/frame/treasury/Cargo.toml +++ b/frame/treasury/Cargo.toml @@ -18,7 +18,7 @@ codec = { package = "parity-scale-codec", version = "3.2.2", default-features = "max-encoded-len", ] } impl-trait-for-tuples = "0.2.2" -scale-info = { version = "2.1.1", default-features = false, features = ["derive"] } +scale-info = { version = "2.5.0", default-features = false, features = ["derive"] } serde = { version = "1.0.136", features = ["derive"], optional = true } frame-benchmarking = { version = "4.0.0-dev", default-features = false, optional = true, path = "../benchmarking" } frame-support = { version = "4.0.0-dev", default-features = false, path = "../support" } diff --git a/frame/uniques/Cargo.toml b/frame/uniques/Cargo.toml index a01ea60d8..f88a862da 100644 --- a/frame/uniques/Cargo.toml +++ b/frame/uniques/Cargo.toml @@ -15,7 +15,7 @@ targets = ["x86_64-unknown-linux-gnu"] [dependencies] codec = { package = "parity-scale-codec", version = "3.2.2", default-features = false } log = { version = "0.4.17", default-features = false } -scale-info = { version = "2.1.1", default-features = false, features = ["derive"] } +scale-info = { version = "2.5.0", default-features = false, features = ["derive"] } frame-benchmarking = { version = "4.0.0-dev", default-features = false, optional = true, path = "../benchmarking" } frame-support = { version = "4.0.0-dev", default-features = false, path = "../support" } frame-system = { version = "4.0.0-dev", default-features = false, path = "../system" } diff --git a/frame/utility/Cargo.toml b/frame/utility/Cargo.toml index c00255e3d..a30feec46 100644 --- a/frame/utility/Cargo.toml +++ b/frame/utility/Cargo.toml @@ -14,7 +14,7 @@ targets = ["x86_64-unknown-linux-gnu"] [dependencies] codec = { package = "parity-scale-codec", version = "3.2.2", default-features = false } -scale-info = { version = "2.1.1", default-features = false, features = ["derive"] } +scale-info = { version = "2.5.0", default-features = false, features = ["derive"] } frame-benchmarking = { version = "4.0.0-dev", default-features = false, optional = true, path = "../benchmarking" } frame-support = { version = "4.0.0-dev", default-features = false, path = "../support" } frame-system = { version = "4.0.0-dev", default-features = false, path = "../system" } diff --git a/frame/vesting/Cargo.toml b/frame/vesting/Cargo.toml index c18d1f45e..2aa095bd7 100644 --- a/frame/vesting/Cargo.toml +++ b/frame/vesting/Cargo.toml @@ -17,7 +17,7 @@ codec = { package = "parity-scale-codec", version = "3.2.2", default-features = "derive", ] } log = { version = "0.4.17", default-features = false } -scale-info = { version = "2.1.1", default-features = false, features = ["derive"] } +scale-info = { version = "2.5.0", default-features = false, features = ["derive"] } frame-benchmarking = { version = "4.0.0-dev", default-features = false, optional = true, path = "../benchmarking" } frame-support = { version = "4.0.0-dev", default-features = false, path = "../support" } frame-system = { version = "4.0.0-dev", default-features = false, path = "../system" } diff --git a/frame/whitelist/Cargo.toml b/frame/whitelist/Cargo.toml index 56ceaca9f..21ecc4be3 100644 --- a/frame/whitelist/Cargo.toml +++ b/frame/whitelist/Cargo.toml @@ -13,7 +13,7 @@ targets = ["x86_64-unknown-linux-gnu"] [dependencies] codec = { package = "parity-scale-codec", version = "3.2.2", default-features = false, features = ["derive", "max-encoded-len"] } -scale-info = { version = "2.1", default-features = false, features = ["derive"] } +scale-info = { version = "2.5.0", default-features = false, features = ["derive"] } frame-benchmarking = { version = "4.0.0-dev", default-features = false, optional = true, path = "../benchmarking" } frame-support = { version = "4.0.0-dev", default-features = false, path = "../support" } frame-system = { version = "4.0.0-dev", default-features = false, path = "../system" } diff --git a/primitives/application-crypto/Cargo.toml b/primitives/application-crypto/Cargo.toml index e7aa118df..37b58bc2b 100644 --- a/primitives/application-crypto/Cargo.toml +++ b/primitives/application-crypto/Cargo.toml @@ -17,7 +17,7 @@ targets = ["x86_64-unknown-linux-gnu"] [dependencies] sp-core = { version = "7.0.0", default-features = false, path = "../core" } codec = { package = "parity-scale-codec", version = "3.2.2", default-features = false, features = ["derive"] } -scale-info = { version = "2.1.1", default-features = false, features = ["derive"] } +scale-info = { version = "2.5.0", default-features = false, features = ["derive"] } serde = { version = "1.0.136", optional = true, features = ["derive"] } sp-std = { version = "5.0.0", default-features = false, path = "../std" } sp-io = { version = "7.0.0", default-features = false, path = "../io" } diff --git a/primitives/arithmetic/Cargo.toml b/primitives/arithmetic/Cargo.toml index b3fc6abc3..92eebd4a6 100644 --- a/primitives/arithmetic/Cargo.toml +++ b/primitives/arithmetic/Cargo.toml @@ -20,7 +20,7 @@ codec = { package = "parity-scale-codec", version = "3.2.2", default-features = ] } integer-sqrt = "0.1.2" num-traits = { version = "0.2.8", default-features = false } -scale-info = { version = "2.1.1", default-features = false, features = ["derive"] } +scale-info = { version = "2.5.0", default-features = false, features = ["derive"] } serde = { version = "1.0.136", features = ["derive"], optional = true } static_assertions = "1.1.0" sp-std = { version = "5.0.0", default-features = false, path = "../std" } diff --git a/primitives/authority-discovery/Cargo.toml b/primitives/authority-discovery/Cargo.toml index ff36cf58d..e3a82b5fd 100644 --- a/primitives/authority-discovery/Cargo.toml +++ b/primitives/authority-discovery/Cargo.toml @@ -14,7 +14,7 @@ targets = ["x86_64-unknown-linux-gnu"] [dependencies] codec = { package = "parity-scale-codec", version = "3.2.2", default-features = false } -scale-info = { version = "2.1.1", default-features = false, features = ["derive"] } +scale-info = { version = "2.5.0", default-features = false, features = ["derive"] } sp-api = { version = "4.0.0-dev", default-features = false, path = "../api" } sp-application-crypto = { version = "7.0.0", default-features = false, path = "../application-crypto" } sp-runtime = { version = "7.0.0", default-features = false, path = "../runtime" } diff --git a/primitives/consensus/aura/Cargo.toml b/primitives/consensus/aura/Cargo.toml index 001dc4a69..94a031a54 100644 --- a/primitives/consensus/aura/Cargo.toml +++ b/primitives/consensus/aura/Cargo.toml @@ -15,7 +15,7 @@ targets = ["x86_64-unknown-linux-gnu"] [dependencies] async-trait = { version = "0.1.57", optional = true } codec = { package = "parity-scale-codec", version = "3.2.2", default-features = false } -scale-info = { version = "2.1.1", default-features = false, features = ["derive"] } +scale-info = { version = "2.5.0", default-features = false, features = ["derive"] } sp-api = { version = "4.0.0-dev", default-features = false, path = "../../api" } sp-application-crypto = { version = "7.0.0", default-features = false, path = "../../application-crypto" } sp-consensus = { version = "0.10.0-dev", optional = true, path = "../common" } diff --git a/primitives/consensus/babe/Cargo.toml b/primitives/consensus/babe/Cargo.toml index 334fff281..d106cd0d0 100644 --- a/primitives/consensus/babe/Cargo.toml +++ b/primitives/consensus/babe/Cargo.toml @@ -16,7 +16,7 @@ targets = ["x86_64-unknown-linux-gnu"] async-trait = { version = "0.1.57", optional = true } codec = { package = "parity-scale-codec", version = "3.2.2", default-features = false } merlin = { version = "2.0", default-features = false } -scale-info = { version = "2.1.1", default-features = false, features = ["derive"] } +scale-info = { version = "2.5.0", default-features = false, features = ["derive"] } serde = { version = "1.0.136", features = ["derive"], optional = true } sp-api = { version = "4.0.0-dev", default-features = false, path = "../../api" } sp-application-crypto = { version = "7.0.0", default-features = false, path = "../../application-crypto" } diff --git a/primitives/consensus/beefy/Cargo.toml b/primitives/consensus/beefy/Cargo.toml index 657569d12..144c8452d 100644 --- a/primitives/consensus/beefy/Cargo.toml +++ b/primitives/consensus/beefy/Cargo.toml @@ -14,7 +14,7 @@ targets = ["x86_64-unknown-linux-gnu"] [dependencies] codec = { package = "parity-scale-codec", version = "3.2.2", default-features = false, features = ["derive"] } serde = { version = "1.0.136", optional = true, features = ["derive"] } -scale-info = { version = "2.1.1", default-features = false, features = ["derive"] } +scale-info = { version = "2.5.0", default-features = false, features = ["derive"] } sp-api = { version = "4.0.0-dev", default-features = false, path = "../../api" } sp-application-crypto = { version = "7.0.0", default-features = false, path = "../../application-crypto" } sp-core = { version = "7.0.0", default-features = false, path = "../../core" } diff --git a/primitives/consensus/grandpa/Cargo.toml b/primitives/consensus/grandpa/Cargo.toml index 5536cf735..446471da8 100644 --- a/primitives/consensus/grandpa/Cargo.toml +++ b/primitives/consensus/grandpa/Cargo.toml @@ -17,7 +17,7 @@ targets = ["x86_64-unknown-linux-gnu"] codec = { package = "parity-scale-codec", version = "3.2.2", default-features = false, features = ["derive"] } grandpa = { package = "finality-grandpa", version = "0.16.2", default-features = false, features = ["derive-codec"] } log = { version = "0.4.17", default-features = false } -scale-info = { version = "2.1.1", default-features = false, features = ["derive"] } +scale-info = { version = "2.5.0", default-features = false, features = ["derive"] } serde = { version = "1.0.136", features = ["derive"], optional = true } sp-api = { version = "4.0.0-dev", default-features = false, path = "../../api" } sp-application-crypto = { version = "7.0.0", default-features = false, path = "../../application-crypto" } diff --git a/primitives/consensus/vrf/Cargo.toml b/primitives/consensus/vrf/Cargo.toml index a4f92c30c..e7b4f0f0d 100644 --- a/primitives/consensus/vrf/Cargo.toml +++ b/primitives/consensus/vrf/Cargo.toml @@ -14,7 +14,7 @@ targets = ["x86_64-unknown-linux-gnu"] [dependencies] codec = { package = "parity-scale-codec", version = "3.2.2", default-features = false } -scale-info = { version = "2.1.1", default-features = false } +scale-info = { version = "2.5.0", default-features = false } schnorrkel = { version = "0.9.1", default-features = false, features = ["preaudit_deprecated", "u64_backend"] } sp-core = { version = "7.0.0", default-features = false, path = "../../core" } sp-runtime = { version = "7.0.0", default-features = false, path = "../../runtime" } diff --git a/primitives/core/Cargo.toml b/primitives/core/Cargo.toml index 9b253fd15..1edd28b3f 100644 --- a/primitives/core/Cargo.toml +++ b/primitives/core/Cargo.toml @@ -14,7 +14,7 @@ targets = ["x86_64-unknown-linux-gnu"] [dependencies] codec = { package = "parity-scale-codec", version = "3.2.2", default-features = false, features = ["derive","max-encoded-len"] } -scale-info = { version = "2.1.1", default-features = false, features = ["derive"] } +scale-info = { version = "2.5.0", default-features = false, features = ["derive"] } log = { version = "0.4.17", default-features = false } serde = { version = "1.0.136", optional = true, features = ["derive"] } bounded-collections = { version = "0.1.4", default-features = false } diff --git a/primitives/inherents/Cargo.toml b/primitives/inherents/Cargo.toml index fd073db69..2f5616770 100644 --- a/primitives/inherents/Cargo.toml +++ b/primitives/inherents/Cargo.toml @@ -16,7 +16,7 @@ targets = ["x86_64-unknown-linux-gnu"] [dependencies] async-trait = { version = "0.1.57", optional = true } codec = { package = "parity-scale-codec", version = "3.2.2", default-features = false, features = ["derive"] } -scale-info = { version = "2.1.1", default-features = false, features = ["derive"] } +scale-info = { version = "2.5.0", default-features = false, features = ["derive"] } impl-trait-for-tuples = "0.2.2" thiserror = { version = "1.0.30", optional = true } sp-core = { version = "7.0.0", default-features = false, path = "../core" } diff --git a/primitives/merkle-mountain-range/Cargo.toml b/primitives/merkle-mountain-range/Cargo.toml index 97add1ed1..cb3d6ddd5 100644 --- a/primitives/merkle-mountain-range/Cargo.toml +++ b/primitives/merkle-mountain-range/Cargo.toml @@ -13,7 +13,7 @@ targets = ["x86_64-unknown-linux-gnu"] [dependencies] codec = { package = "parity-scale-codec", version = "3.2.2", default-features = false } -scale-info = { version = "2.1.1", default-features = false, features = ["derive"] } +scale-info = { version = "2.5.0", default-features = false, features = ["derive"] } log = { version = "0.4.17", default-features = false } mmr-lib = { package = "ckb-merkle-mountain-range", version = "0.5.2", default-features = false } serde = { version = "1.0.136", features = ["derive"], optional = true } diff --git a/primitives/npos-elections/Cargo.toml b/primitives/npos-elections/Cargo.toml index 7a4bdbd67..50a7a5bcd 100644 --- a/primitives/npos-elections/Cargo.toml +++ b/primitives/npos-elections/Cargo.toml @@ -14,7 +14,7 @@ targets = ["x86_64-unknown-linux-gnu"] [dependencies] codec = { package = "parity-scale-codec", version = "3.2.2", default-features = false, features = ["derive"] } -scale-info = { version = "2.1.1", default-features = false, features = ["derive"] } +scale-info = { version = "2.5.0", default-features = false, features = ["derive"] } serde = { version = "1.0.136", features = ["derive"], optional = true } sp-arithmetic = { version = "6.0.0", default-features = false, path = "../arithmetic" } sp-core = { version = "7.0.0", default-features = false, path = "../core" } diff --git a/primitives/npos-elections/fuzzer/Cargo.toml b/primitives/npos-elections/fuzzer/Cargo.toml index 8a058cc92..d7cc68454 100644 --- a/primitives/npos-elections/fuzzer/Cargo.toml +++ b/primitives/npos-elections/fuzzer/Cargo.toml @@ -18,7 +18,7 @@ clap = { version = "4.0.9", features = ["derive"] } codec = { package = "parity-scale-codec", version = "3.2.2", default-features = false, features = ["derive"] } honggfuzz = "0.5" rand = { version = "0.8", features = ["std", "small_rng"] } -scale-info = { version = "2.1.1", default-features = false, features = ["derive"] } +scale-info = { version = "2.5.0", default-features = false, features = ["derive"] } sp-npos-elections = { version = "4.0.0-dev", path = ".." } sp-runtime = { version = "7.0.0", path = "../../runtime" } diff --git a/primitives/runtime/Cargo.toml b/primitives/runtime/Cargo.toml index 3976de60c..056cfa3dd 100644 --- a/primitives/runtime/Cargo.toml +++ b/primitives/runtime/Cargo.toml @@ -21,7 +21,7 @@ impl-trait-for-tuples = "0.2.2" log = { version = "0.4.17", default-features = false } paste = "1.0" rand = { version = "0.8.5", optional = true } -scale-info = { version = "2.1.1", default-features = false, features = ["derive"] } +scale-info = { version = "2.5.0", default-features = false, features = ["derive"] } serde = { version = "1.0.136", features = ["derive"], optional = true } sp-application-crypto = { version = "7.0.0", default-features = false, path = "../application-crypto" } sp-arithmetic = { version = "6.0.0", default-features = false, path = "../arithmetic" } diff --git a/primitives/runtime/src/generic/digest.rs b/primitives/runtime/src/generic/digest.rs index 73741ba5d..143fc2ce5 100644 --- a/primitives/runtime/src/generic/digest.rs +++ b/primitives/runtime/src/generic/digest.rs @@ -453,8 +453,8 @@ mod tests { #[test] fn digest_item_type_info() { let type_info = DigestItem::type_info(); - let variants = if let scale_info::TypeDef::Variant(variant) = type_info.type_def() { - variant.variants() + let variants = if let scale_info::TypeDef::Variant(variant) = type_info.type_def { + variant.variants } else { panic!("Should be a TypeDef::TypeDefVariant") }; @@ -475,10 +475,10 @@ mod tests { let encoded = digest_item.encode(); let variant = variants .iter() - .find(|v| v.name() == &variant_name) + .find(|v| v.name == variant_name) .expect(&format!("Variant {} not found", variant_name)); - assert_eq!(encoded[0], variant.index()) + assert_eq!(encoded[0], variant.index) }; check(DigestItemType::Other); diff --git a/primitives/session/Cargo.toml b/primitives/session/Cargo.toml index 31ec009ab..6f362974b 100644 --- a/primitives/session/Cargo.toml +++ b/primitives/session/Cargo.toml @@ -14,7 +14,7 @@ targets = ["x86_64-unknown-linux-gnu"] [dependencies] codec = { package = "parity-scale-codec", version = "3.2.2", default-features = false, features = ["derive"] } -scale-info = { version = "2.1.1", default-features = false, features = ["derive"] } +scale-info = { version = "2.5.0", default-features = false, features = ["derive"] } sp-api = { version = "4.0.0-dev", default-features = false, path = "../api" } sp-core = { version = "7.0.0", default-features = false, path = "../core" } sp-runtime = { version = "7.0.0", optional = true, path = "../runtime" } diff --git a/primitives/staking/Cargo.toml b/primitives/staking/Cargo.toml index a8e5a543d..f92bca278 100644 --- a/primitives/staking/Cargo.toml +++ b/primitives/staking/Cargo.toml @@ -14,7 +14,7 @@ targets = ["x86_64-unknown-linux-gnu"] [dependencies] codec = { package = "parity-scale-codec", version = "3.2.2", default-features = false, features = ["derive"] } -scale-info = { version = "2.1.1", default-features = false, features = ["derive"] } +scale-info = { version = "2.5.0", default-features = false, features = ["derive"] } sp-core = { version = "7.0.0", default-features = false, path = "../core" } sp-runtime = { version = "7.0.0", default-features = false, path = "../runtime" } sp-std = { version = "5.0.0", default-features = false, path = "../std" } diff --git a/primitives/transaction-storage-proof/Cargo.toml b/primitives/transaction-storage-proof/Cargo.toml index 565d3d8d2..72d3175a5 100644 --- a/primitives/transaction-storage-proof/Cargo.toml +++ b/primitives/transaction-storage-proof/Cargo.toml @@ -16,7 +16,7 @@ targets = ["x86_64-unknown-linux-gnu"] async-trait = { version = "0.1.57", optional = true } codec = { package = "parity-scale-codec", version = "3.2.2", default-features = false, features = ["derive"] } log = { version = "0.4.17", optional = true } -scale-info = { version = "2.1.1", default-features = false, features = ["derive"] } +scale-info = { version = "2.5.0", default-features = false, features = ["derive"] } sp-core = { version = "7.0.0", optional = true, path = "../core" } sp-inherents = { version = "4.0.0-dev", default-features = false, path = "../inherents" } sp-runtime = { version = "7.0.0", default-features = false, path = "../runtime" } diff --git a/primitives/trie/Cargo.toml b/primitives/trie/Cargo.toml index 0dacb76d9..be0230300 100644 --- a/primitives/trie/Cargo.toml +++ b/primitives/trie/Cargo.toml @@ -26,7 +26,7 @@ lazy_static = { version = "1.4.0", optional = true } memory-db = { version = "0.32.0", default-features = false } nohash-hasher = { version = "0.2.0", optional = true } parking_lot = { version = "0.12.1", optional = true } -scale-info = { version = "2.1.1", default-features = false, features = ["derive"] } +scale-info = { version = "2.5.0", default-features = false, features = ["derive"] } thiserror = { version = "1.0.30", optional = true } tracing = { version = "0.1.29", optional = true } trie-db = { version = "0.27.0", default-features = false } diff --git a/primitives/version/Cargo.toml b/primitives/version/Cargo.toml index 7d32c540f..6b425aa7b 100644 --- a/primitives/version/Cargo.toml +++ b/primitives/version/Cargo.toml @@ -17,7 +17,7 @@ targets = ["x86_64-unknown-linux-gnu"] codec = { package = "parity-scale-codec", version = "3.2.2", default-features = false, features = ["derive"] } impl-serde = { version = "0.4.0", optional = true } parity-wasm = { version = "0.45", optional = true } -scale-info = { version = "2.1.1", default-features = false, features = ["derive"] } +scale-info = { version = "2.5.0", default-features = false, features = ["derive"] } serde = { version = "1.0.136", features = ["derive"], optional = true } thiserror = { version = "1.0.30", optional = true } sp-core-hashing-proc-macro = { version = "5.0.0", path = "../core/hashing/proc-macro" } diff --git a/primitives/weights/Cargo.toml b/primitives/weights/Cargo.toml index 2368b913b..af1b5647e 100644 --- a/primitives/weights/Cargo.toml +++ b/primitives/weights/Cargo.toml @@ -14,7 +14,7 @@ targets = ["x86_64-unknown-linux-gnu"] [dependencies] codec = { package = "parity-scale-codec", version = "3.2.2", default-features = false, features = ["derive"] } -scale-info = { version = "2.1.1", default-features = false, features = ["derive"] } +scale-info = { version = "2.5.0", default-features = false, features = ["derive"] } serde = { version = "1.0.136", optional = true, features = ["derive"] } smallvec = "1.8.0" sp-arithmetic = { version = "6.0.0", default-features = false, path = "../arithmetic" } diff --git a/test-utils/runtime/Cargo.toml b/test-utils/runtime/Cargo.toml index 1dcdec0db..68267e91d 100644 --- a/test-utils/runtime/Cargo.toml +++ b/test-utils/runtime/Cargo.toml @@ -20,7 +20,7 @@ sp-consensus-babe = { version = "0.10.0-dev", default-features = false, path = " sp-consensus-beefy = { version = "4.0.0-dev", default-features = false, path = "../../primitives/consensus/beefy" } sp-block-builder = { version = "4.0.0-dev", default-features = false, path = "../../primitives/block-builder" } codec = { package = "parity-scale-codec", version = "3.2.2", default-features = false, features = ["derive"] } -scale-info = { version = "2.1.1", default-features = false, features = ["derive"] } +scale-info = { version = "2.5.0", default-features = false, features = ["derive"] } sp-inherents = { version = "4.0.0-dev", default-features = false, path = "../../primitives/inherents" } sp-keyring = { version = "7.0.0", optional = true, path = "../../primitives/keyring" } memory-db = { version = "0.32.0", default-features = false } diff --git a/utils/frame/rpc/state-trie-migration-rpc/Cargo.toml b/utils/frame/rpc/state-trie-migration-rpc/Cargo.toml index 3689a87da..91f500146 100644 --- a/utils/frame/rpc/state-trie-migration-rpc/Cargo.toml +++ b/utils/frame/rpc/state-trie-migration-rpc/Cargo.toml @@ -13,7 +13,7 @@ readme = "README.md" targets = ["x86_64-unknown-linux-gnu"] [dependencies] -scale-info = { version = "2.1.1", default-features = false, features = ["derive"] } +scale-info = { version = "2.5.0", default-features = false, features = ["derive"] } codec = { package = "parity-scale-codec", version = "3.2.2", default-features = false } serde = { version = "1", features = ["derive"] } log = { version = "0.4.17", default-features = false } From b4f857e14768ebdffe0993886e397c54387b90ee Mon Sep 17 00:00:00 2001 From: PG Herveou Date: Wed, 12 Apr 2023 16:49:10 +0200 Subject: [PATCH 367/558] contracts: add sr25519_verify (#13724) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * wip * fix * wip * fix lint * rm fixture fix * missing comment * fix lint * add comment to the wsm file * fix comment * Apply suggestions from code review Co-authored-by: Sasha Gryaznov * wip * wip weights * wip weights * PR comment: test with return code * wip * PR review add mock test * remove * lint * Update frame/contracts/fixtures/sr25519_verify.wat * fix comments * Update frame/contracts/src/benchmarking/mod.rs * Update frame/contracts/src/wasm/runtime.rs * Update frame/contracts/fixtures/sr25519_verify.wat * Update frame/contracts/src/benchmarking/mod.rs * fix lint * ".git/.scripts/commands/bench/bench.sh" pallet dev pallet_contracts * Update frame/contracts/src/wasm/runtime.rs Co-authored-by: Alexander Theißen * PR: review use unstable + remove arbitrary index 4 * Add benchmark for calculating overhead of calling sr25519_verify * fix message length encoding * fix weights * ".git/.scripts/commands/bench/bench.sh" pallet dev pallet_contracts * Apply suggestions from code review * Update frame/contracts/src/wasm/runtime.rs * Update frame/contracts/src/wasm/runtime.rs * Update frame/contracts/src/benchmarking/mod.rs * Update frame/contracts/src/benchmarking/mod.rs * Update frame/contracts/src/schedule.rs Co-authored-by: Sasha Gryaznov * Update frame/contracts/src/schedule.rs Co-authored-by: Sasha Gryaznov * Update frame/contracts/src/wasm/runtime.rs * Update frame/contracts/src/wasm/runtime.rs Co-authored-by: Sasha Gryaznov * PR review --------- Co-authored-by: Sasha Gryaznov Co-authored-by: command-bot <> Co-authored-by: Alexander Theißen --- frame/contracts/fixtures/sr25519_verify.wat | 55 + frame/contracts/src/benchmarking/mod.rs | 107 +- frame/contracts/src/exec.rs | 16 +- frame/contracts/src/schedule.rs | 10 +- frame/contracts/src/tests.rs | 66 + frame/contracts/src/wasm/mod.rs | 49 + frame/contracts/src/wasm/runtime.rs | 47 + frame/contracts/src/weights.rs | 1876 ++++++++++--------- 8 files changed, 1332 insertions(+), 894 deletions(-) create mode 100644 frame/contracts/fixtures/sr25519_verify.wat diff --git a/frame/contracts/fixtures/sr25519_verify.wat b/frame/contracts/fixtures/sr25519_verify.wat new file mode 100644 index 000000000..2da1ceb87 --- /dev/null +++ b/frame/contracts/fixtures/sr25519_verify.wat @@ -0,0 +1,55 @@ +;; This contract: +;; 1) Reads signature, message and public key from the input +;; 2) Calls and return the result of sr25519_verify + +(module + ;; import the host functions from the seal0 module + (import "seal0" "sr25519_verify" (func $sr25519_verify (param i32 i32 i32 i32) (result i32))) + (import "seal0" "seal_input" (func $seal_input (param i32 i32))) + (import "seal0" "seal_return" (func $seal_return (param i32 i32 i32))) + + ;; give the program 1 page of memory + (import "env" "memory" (memory 1 1)) + + ;; [0, 4) length of signature + message + public key - 64 + 11 + 32 = 107 bytes + ;; write the length of the input (6b = 107) bytes at offset 0 + (data (i32.const 0) "\6b") + + (func (export "deploy")) + + (func (export "call") + ;; define local variables + (local $signature_ptr i32) + (local $pub_key_ptr i32) + (local $message_len i32) + (local $message_ptr i32) + + ;; set the pointers to the memory locations + ;; Memory layout during `call` + ;; [10, 74) signature + ;; [74, 106) public key + ;; [106, 117) message (11 bytes) + (local.set $signature_ptr (i32.const 10)) + (local.set $pub_key_ptr (i32.const 74)) + (local.set $message_ptr (i32.const 106)) + + ;; store the input into the memory, starting at the signature and + ;; up to 107 bytes stored at offset 0 + (call $seal_input (local.get $signature_ptr) (i32.const 0)) + + ;; call sr25519_verify and store the return code + (i32.store + (i32.const 0) + (call $sr25519_verify + (local.get $signature_ptr) + (local.get $pub_key_ptr) + (i32.const 11) + (local.get $message_ptr) + ) + ) + + ;; exit with success and take transfer return code to the output buffer + (call $seal_return (i32.const 0) (i32.const 0) (i32.const 4)) + ) +) + diff --git a/frame/contracts/src/benchmarking/mod.rs b/frame/contracts/src/benchmarking/mod.rs index 1bb6f3395..98a3dc36b 100644 --- a/frame/contracts/src/benchmarking/mod.rs +++ b/frame/contracts/src/benchmarking/mod.rs @@ -2008,9 +2008,114 @@ benchmarks! { let origin = RawOrigin::Signed(instance.caller.clone()); }: call(origin, instance.addr, 0u32.into(), Weight::MAX, None, vec![]) + // `n`: Message input length to verify in bytes. + #[pov_mode = Measured] + seal_sr25519_verify_per_byte { + let n in 0 .. T::MaxCodeLen::get() - 255; // need some buffer so the code size does not + // exceed the max code size. + + let message = (0..n).zip((32u8..127u8).cycle()).map(|(_, c)| c).collect::>(); + let message_len = message.len() as i32; + + let key_type = sp_core::crypto::KeyTypeId(*b"code"); + let pub_key = sp_io::crypto::sr25519_generate(key_type, None); + let sig = sp_io::crypto::sr25519_sign(key_type, &pub_key, &message).expect("Generates signature"); + let sig = AsRef::<[u8; 64]>::as_ref(&sig).to_vec(); + + let code = WasmModule::::from(ModuleDefinition { + memory: Some(ImportedMemory::max::()), + imported_functions: vec![ImportedFunction { + module: "seal0", + name: "sr25519_verify", + params: vec![ValueType::I32, ValueType::I32, ValueType::I32, ValueType::I32], + return_type: Some(ValueType::I32), + }], + data_segments: vec![ + DataSegment { + offset: 0, + value: sig, + }, + DataSegment { + offset: 64, + value: pub_key.to_vec(), + }, + DataSegment { + offset: 96, + value: message, + }, + ], + call_body: Some(body::plain(vec![ + Instruction::I32Const(0), // signature_ptr + Instruction::I32Const(64), // pub_key_ptr + Instruction::I32Const(message_len), // message_len + Instruction::I32Const(96), // message_ptr + Instruction::Call(0), + Instruction::Drop, + Instruction::End, + ])), + .. Default::default() + }); + + let instance = Contract::::new(code, vec![])?; + let origin = RawOrigin::Signed(instance.caller.clone()); + }: call(origin, instance.addr, 0u32.into(), Weight::MAX, None, vec![]) + // Only calling the function itself with valid arguments. // It generates different private keys and signatures for the message "Hello world". - // This is a slow call: We redeuce the number of runs. + // This is a slow call: We reduce the number of runs. + #[pov_mode = Measured] + seal_sr25519_verify { + let r in 0 .. API_BENCHMARK_RUNS / 10; + + let message = b"Hello world".to_vec(); + let message_len = message.len() as i32; + let key_type = sp_core::crypto::KeyTypeId(*b"code"); + let sig_params = (0..r) + .map(|i| { + let pub_key = sp_io::crypto::sr25519_generate(key_type, None); + let sig = sp_io::crypto::sr25519_sign(key_type, &pub_key, &message).expect("Generates signature"); + let data: [u8; 96] = [AsRef::<[u8]>::as_ref(&sig), AsRef::<[u8]>::as_ref(&pub_key)].concat().try_into().unwrap(); + data + }) + .flatten() + .collect::>(); + let sig_params_len = sig_params.len() as i32; + + let code = WasmModule::::from(ModuleDefinition { + memory: Some(ImportedMemory::max::()), + imported_functions: vec![ImportedFunction { + module: "seal0", + name: "sr25519_verify", + params: vec![ValueType::I32, ValueType::I32, ValueType::I32, ValueType::I32], + return_type: Some(ValueType::I32), + }], + data_segments: vec![ + DataSegment { + offset: 0, + value: sig_params + }, + DataSegment { + offset: sig_params_len as u32, + value: message, + }, + ], + call_body: Some(body::repeated_dyn(r, vec![ + Counter(0, 96), // signature_ptr + Counter(64, 96), // pub_key_ptr + Regular(Instruction::I32Const(message_len)), // message_len + Regular(Instruction::I32Const(sig_params_len)), // message_ptr + Regular(Instruction::Call(0)), + Regular(Instruction::Drop), + ])), + .. Default::default() + }); + let instance = Contract::::new(code, vec![])?; + let origin = RawOrigin::Signed(instance.caller.clone()); + }: call(origin, instance.addr, 0u32.into(), Weight::MAX, None, vec![]) + + // Only calling the function itself with valid arguments. + // It generates different private keys and signatures for the message "Hello world". + // This is a slow call: We reduce the number of runs. #[pov_mode = Measured] seal_ecdsa_recover { let r in 0 .. API_BENCHMARK_RUNS / 10; diff --git a/frame/contracts/src/exec.rs b/frame/contracts/src/exec.rs index 03e1c4fd3..574f4495e 100644 --- a/frame/contracts/src/exec.rs +++ b/frame/contracts/src/exec.rs @@ -35,7 +35,10 @@ use frame_support::{ use frame_system::RawOrigin; use pallet_contracts_primitives::ExecReturnValue; use smallvec::{Array, SmallVec}; -use sp_core::ecdsa::Public as ECDSAPublic; +use sp_core::{ + ecdsa::Public as ECDSAPublic, + sr25519::{Public as SR25519Public, Signature as SR25519Signature}, +}; use sp_io::{crypto::secp256k1_ecdsa_recover_compressed, hashing::blake2_256}; use sp_runtime::traits::{Convert, Hash}; use sp_std::{marker::PhantomData, mem, prelude::*, vec::Vec}; @@ -272,6 +275,9 @@ pub trait Ext: sealing::Sealed { /// Recovers ECDSA compressed public key based on signature and message hash. fn ecdsa_recover(&self, signature: &[u8; 65], message_hash: &[u8; 32]) -> Result<[u8; 33], ()>; + /// Verify a sr25519 signature. + fn sr25519_verify(&self, signature: &[u8; 64], message: &[u8], pub_key: &[u8; 32]) -> bool; + /// Returns Ethereum address from the ECDSA compressed public key. fn ecdsa_to_eth_address(&self, pk: &[u8; 33]) -> Result<[u8; 20], ()>; @@ -1347,6 +1353,14 @@ where secp256k1_ecdsa_recover_compressed(signature, message_hash).map_err(|_| ()) } + fn sr25519_verify(&self, signature: &[u8; 64], message: &[u8], pub_key: &[u8; 32]) -> bool { + sp_io::crypto::sr25519_verify( + &SR25519Signature(*signature), + message, + &SR25519Public(*pub_key), + ) + } + fn ecdsa_to_eth_address(&self, pk: &[u8; 33]) -> Result<[u8; 20], ()> { ECDSAPublic(*pk).to_eth_address() } diff --git a/frame/contracts/src/schedule.rs b/frame/contracts/src/schedule.rs index ccf1e98bd..747540bce 100644 --- a/frame/contracts/src/schedule.rs +++ b/frame/contracts/src/schedule.rs @@ -139,7 +139,7 @@ impl Limits { /// Describes the weight for all categories of supported wasm instructions. /// /// There there is one field for each wasm instruction that describes the weight to -/// execute one instruction of that name. There are a few execptions: +/// execute one instruction of that name. There are a few exceptions: /// /// 1. If there is a i64 and a i32 variant of an instruction we use the weight /// of the former for both. @@ -409,6 +409,12 @@ pub struct HostFnWeights { /// Weight of calling `seal_ecdsa_to_eth_address`. pub ecdsa_to_eth_address: Weight, + /// Weight of calling `sr25519_verify`. + pub sr25519_verify: Weight, + + /// Weight per byte of calling `sr25519_verify`. + pub sr25519_verify_per_byte: Weight, + /// Weight of calling `reentrance_count`. pub reentrance_count: Weight, @@ -616,6 +622,8 @@ impl Default for HostFnWeights { hash_blake2_128: cost!(seal_hash_blake2_128), hash_blake2_128_per_byte: cost!(seal_hash_blake2_128_per_byte), ecdsa_recover: cost!(seal_ecdsa_recover), + sr25519_verify: cost!(seal_sr25519_verify), + sr25519_verify_per_byte: cost!(seal_sr25519_verify_per_byte), ecdsa_to_eth_address: cost!(seal_ecdsa_to_eth_address), reentrance_count: cost!(seal_reentrance_count), account_reentrance_count: cost!(seal_account_reentrance_count), diff --git a/frame/contracts/src/tests.rs b/frame/contracts/src/tests.rs index bae6c1946..ac1fb07bf 100644 --- a/frame/contracts/src/tests.rs +++ b/frame/contracts/src/tests.rs @@ -2900,6 +2900,72 @@ fn ecdsa_recover() { }) } +#[test] +fn sr25519_verify() { + let (wasm, _code_hash) = compile_module::("sr25519_verify").unwrap(); + + ExtBuilder::default().existential_deposit(50).build().execute_with(|| { + let _ = Balances::deposit_creating(&ALICE, 1_000_000); + + // Instantiate the sr25519_verify contract. + let addr = Contracts::bare_instantiate( + ALICE, + 100_000, + GAS_LIMIT, + None, + Code::Upload(wasm), + vec![], + vec![], + false, + ) + .result + .unwrap() + .account_id; + + let call_with = |message: &[u8; 11]| { + // Alice's signature for "hello world" + #[rustfmt::skip] + let signature: [u8; 64] = [ + 184, 49, 74, 238, 78, 165, 102, 252, 22, 92, 156, 176, 124, 118, 168, 116, 247, + 99, 0, 94, 2, 45, 9, 170, 73, 222, 182, 74, 60, 32, 75, 64, 98, 174, 69, 55, 83, + 85, 180, 98, 208, 75, 231, 57, 205, 62, 4, 105, 26, 136, 172, 17, 123, 99, 90, 255, + 228, 54, 115, 63, 30, 207, 205, 131, + ]; + + // Alice's public key + #[rustfmt::skip] + let public_key: [u8; 32] = [ + 212, 53, 147, 199, 21, 253, 211, 28, 97, 20, 26, 189, 4, 169, 159, 214, 130, 44, + 133, 88, 133, 76, 205, 227, 154, 86, 132, 231, 165, 109, 162, 125, + ]; + + let mut params = vec![]; + params.extend_from_slice(&signature); + params.extend_from_slice(&public_key); + params.extend_from_slice(message); + + >::bare_call( + ALICE, + addr.clone(), + 0, + GAS_LIMIT, + None, + params, + false, + Determinism::Enforced, + ) + .result + .unwrap() + }; + + // verification should succeed for "hello world" + assert_return_code!(call_with(&b"hello world"), RuntimeReturnCode::Success); + + // verification should fail for other messages + assert_return_code!(call_with(&b"hello worlD"), RuntimeReturnCode::Sr25519VerifyFailed); + }) +} + #[test] fn upload_code_works() { let (wasm, code_hash) = compile_module::("dummy").unwrap(); diff --git a/frame/contracts/src/wasm/mod.rs b/frame/contracts/src/wasm/mod.rs index e4e8bea7e..c8f1be6dd 100644 --- a/frame/contracts/src/wasm/mod.rs +++ b/frame/contracts/src/wasm/mod.rs @@ -434,6 +434,7 @@ mod tests { gas_meter: GasMeter, debug_buffer: Vec, ecdsa_recover: RefCell>, + sr25519_verify: RefCell, [u8; 32])>>, code_hashes: Vec>, } @@ -458,6 +459,7 @@ mod tests { gas_meter: GasMeter::new(Weight::from_parts(10_000_000_000, 10 * 1024 * 1024)), debug_buffer: Default::default(), ecdsa_recover: Default::default(), + sr25519_verify: Default::default(), } } } @@ -612,6 +614,10 @@ mod tests { self.ecdsa_recover.borrow_mut().push((*signature, *message_hash)); Ok([3; 33]) } + fn sr25519_verify(&self, signature: &[u8; 64], message: &[u8], pub_key: &[u8; 32]) -> bool { + self.sr25519_verify.borrow_mut().push((*signature, message.to_vec(), *pub_key)); + true + } fn contract_info(&mut self) -> &mut crate::ContractInfo { unimplemented!() } @@ -1319,6 +1325,49 @@ mod tests { ); } + #[test] + fn contract_sr25519() { + const CODE_SR25519: &str = r#" +(module + (import "seal0" "sr25519_verify" (func $sr25519_verify (param i32 i32 i32 i32) (result i32))) + (import "env" "memory" (memory 1 1)) + (func (export "call") + (drop + (call $sr25519_verify + (i32.const 0) ;; Pointer to signature. + (i32.const 64) ;; Pointer to public key. + (i32.const 16) ;; message length. + (i32.const 96) ;; Pointer to message. + ) + ) + ) + (func (export "deploy")) + + ;; Signature (64 bytes) + (data (i32.const 0) + "\01\01\01\01\01\01\01\01\01\01\01\01\01\01\01\01" + "\01\01\01\01\01\01\01\01\01\01\01\01\01\01\01\01" + "\01\01\01\01\01\01\01\01\01\01\01\01\01\01\01\01" + "\01\01\01\01\01\01\01\01\01\01\01\01\01\01\01\01" + ) + + ;; public key (32 bytes) + (data (i32.const 64) + "\01\01\01\01\01\01\01\01\01\01\01\01\01\01\01\01" + "\01\01\01\01\01\01\01\01\01\01\01\01\01\01\01\01" + ) + + ;; message. (16 bytes) + (data (i32.const 96) + "\01\01\01\01\01\01\01\01\01\01\01\01\01\01\01\01" + ) +) +"#; + let mut mock_ext = MockExt::default(); + assert_ok!(execute(&CODE_SR25519, vec![], &mut mock_ext)); + assert_eq!(mock_ext.sr25519_verify.into_inner(), [([1; 64], [1; 16].to_vec(), [1; 32])]); + } + const CODE_GET_STORAGE: &str = r#" (module (import "seal0" "seal_get_storage" (func $seal_get_storage (param i32 i32 i32) (result i32))) diff --git a/frame/contracts/src/wasm/runtime.rs b/frame/contracts/src/wasm/runtime.rs index 88b5b87dd..19eb0fb50 100644 --- a/frame/contracts/src/wasm/runtime.rs +++ b/frame/contracts/src/wasm/runtime.rs @@ -109,6 +109,8 @@ pub enum ReturnCode { /// ECDSA compressed pubkey conversion into Ethereum address failed (most probably /// wrong pubkey provided). EcdsaRecoverFailed = 11, + /// sr25519 signature verification failed. + Sr25519VerifyFailed = 12, } impl From for ReturnCode { @@ -251,6 +253,8 @@ pub enum RuntimeCosts { HashBlake128(u32), /// Weight of calling `seal_ecdsa_recover`. EcdsaRecovery, + /// Weight of calling `seal_sr25519_verify` for the given input size. + Sr25519Verify(u32), /// Weight charged by a chain extension through `seal_call_chain_extension`. ChainExtension(Weight), /// Weight charged for calling into the runtime. @@ -336,6 +340,9 @@ impl RuntimeCosts { .hash_blake2_128 .saturating_add(s.hash_blake2_128_per_byte.saturating_mul(len.into())), EcdsaRecovery => s.ecdsa_recover, + Sr25519Verify(len) => s + .sr25519_verify + .saturating_add(s.sr25519_verify_per_byte.saturating_mul(len.into())), ChainExtension(weight) => weight, CallRuntime(weight) => weight, SetCodeHash => s.set_code_hash, @@ -2466,6 +2473,46 @@ pub mod env { } } + /// Verify a sr25519 signature + /// + /// # Parameters + /// + /// - `signature_ptr`: the pointer into the linear memory where the signature is placed. Should + /// be a value of 64 bytes. + /// - `pub_key_ptr`: the pointer into the linear memory where the public key is placed. Should + /// be a value of 32 bytes. + /// - `message_len`: the length of the message payload. + /// - `message_ptr`: the pointer into the linear memory where the message is placed. + /// + /// # Errors + /// + /// - `ReturnCode::Sr25519VerifyFailed + #[unstable] + fn sr25519_verify( + ctx: _, + memory: _, + signature_ptr: u32, + pub_key_ptr: u32, + message_len: u32, + message_ptr: u32, + ) -> Result { + ctx.charge_gas(RuntimeCosts::Sr25519Verify(message_len))?; + + let mut signature: [u8; 64] = [0; 64]; + ctx.read_sandbox_memory_into_buf(memory, signature_ptr, &mut signature)?; + + let mut pub_key: [u8; 32] = [0; 32]; + ctx.read_sandbox_memory_into_buf(memory, pub_key_ptr, &mut pub_key)?; + + let message: Vec = ctx.read_sandbox_memory(memory, message_ptr, message_len)?; + + if ctx.ext.sr25519_verify(&signature, &message, &pub_key) { + Ok(ReturnCode::Success) + } else { + Ok(ReturnCode::Sr25519VerifyFailed) + } + } + /// Replace the contract code at the specified address with new code. /// /// # Note diff --git a/frame/contracts/src/weights.rs b/frame/contracts/src/weights.rs index 22a24aa27..8e81364ac 100644 --- a/frame/contracts/src/weights.rs +++ b/frame/contracts/src/weights.rs @@ -18,7 +18,7 @@ //! Autogenerated weights for pallet_contracts //! //! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev -//! DATE: 2023-03-28, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! DATE: 2023-04-06, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` //! WORST CASE MAP SIZE: `1000000` //! HOSTNAME: `bm3`, CPU: `Intel(R) Core(TM) i7-7700K CPU @ 4.20GHz` //! EXECUTION: Some(Wasm), WASM-EXECUTION: Compiled, CHAIN: Some("dev"), DB CACHE: 1024 @@ -108,6 +108,8 @@ pub trait WeightInfo { fn seal_hash_blake2_256_per_byte(n: u32, ) -> Weight; fn seal_hash_blake2_128(r: u32, ) -> Weight; fn seal_hash_blake2_128_per_byte(n: u32, ) -> Weight; + fn seal_sr25519_verify_per_byte(n: u32, ) -> Weight; + fn seal_sr25519_verify(r: u32, ) -> Weight; fn seal_ecdsa_recover(r: u32, ) -> Weight; fn seal_ecdsa_to_eth_address(r: u32, ) -> Weight; fn seal_set_code_hash(r: u32, ) -> Weight; @@ -176,8 +178,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `109` // Estimated: `1594` - // Minimum execution time: 2_677_000 picoseconds. - Weight::from_parts(2_899_000, 1594) + // Minimum execution time: 2_503_000 picoseconds. + Weight::from_parts(2_603_000, 1594) .saturating_add(T::DbWeight::get().reads(1_u64)) } /// Storage: Skipped Metadata (r:0 w:0) @@ -187,10 +189,10 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `488 + k * (69 ±0)` // Estimated: `478 + k * (70 ±0)` - // Minimum execution time: 14_006_000 picoseconds. - Weight::from_parts(8_735_946, 478) - // Standard Error: 1_370 - .saturating_add(Weight::from_parts(989_501, 0).saturating_mul(k.into())) + // Minimum execution time: 13_292_000 picoseconds. + Weight::from_parts(9_180_439, 478) + // Standard Error: 997 + .saturating_add(Weight::from_parts(967_522, 0).saturating_mul(k.into())) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().reads((1_u64).saturating_mul(k.into()))) .saturating_add(T::DbWeight::get().writes(2_u64)) @@ -206,10 +208,10 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `238 + c * (1 ±0)` // Estimated: `3951 + c * (2 ±0)` - // Minimum execution time: 30_951_000 picoseconds. - Weight::from_parts(25_988_560, 3951) - // Standard Error: 57 - .saturating_add(Weight::from_parts(54_692, 0).saturating_mul(c.into())) + // Minimum execution time: 30_822_000 picoseconds. + Weight::from_parts(26_457_115, 3951) + // Standard Error: 58 + .saturating_add(Weight::from_parts(54_868, 0).saturating_mul(c.into())) .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) .saturating_add(Weight::from_parts(0, 2).saturating_mul(c.into())) @@ -229,10 +231,10 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `707` // Estimated: `21400 + c * (5 ±0)` - // Minimum execution time: 263_107_000 picoseconds. - Weight::from_parts(268_289_665, 21400) - // Standard Error: 33 - .saturating_add(Weight::from_parts(38_534, 0).saturating_mul(c.into())) + // Minimum execution time: 263_719_000 picoseconds. + Weight::from_parts(275_281_941, 21400) + // Standard Error: 32 + .saturating_add(Weight::from_parts(37_880, 0).saturating_mul(c.into())) .saturating_add(T::DbWeight::get().reads(6_u64)) .saturating_add(T::DbWeight::get().writes(4_u64)) .saturating_add(Weight::from_parts(0, 5).saturating_mul(c.into())) @@ -260,14 +262,14 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `270` // Estimated: `26207` - // Minimum execution time: 3_122_319_000 picoseconds. - Weight::from_parts(487_802_180, 26207) - // Standard Error: 345 - .saturating_add(Weight::from_parts(108_237, 0).saturating_mul(c.into())) - // Standard Error: 20 - .saturating_add(Weight::from_parts(1_166, 0).saturating_mul(i.into())) - // Standard Error: 20 - .saturating_add(Weight::from_parts(1_505, 0).saturating_mul(s.into())) + // Minimum execution time: 3_133_756_000 picoseconds. + Weight::from_parts(556_483_545, 26207) + // Standard Error: 294 + .saturating_add(Weight::from_parts(107_914, 0).saturating_mul(c.into())) + // Standard Error: 17 + .saturating_add(Weight::from_parts(1_143, 0).saturating_mul(i.into())) + // Standard Error: 17 + .saturating_add(Weight::from_parts(1_456, 0).saturating_mul(s.into())) .saturating_add(T::DbWeight::get().reads(9_u64)) .saturating_add(T::DbWeight::get().writes(10_u64)) } @@ -291,12 +293,12 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `482` // Estimated: `28521` - // Minimum execution time: 1_650_623_000 picoseconds. - Weight::from_parts(286_494_456, 28521) + // Minimum execution time: 1_646_953_000 picoseconds. + Weight::from_parts(262_115_215, 28521) // Standard Error: 8 - .saturating_add(Weight::from_parts(1_438, 0).saturating_mul(i.into())) + .saturating_add(Weight::from_parts(1_454, 0).saturating_mul(i.into())) // Standard Error: 8 - .saturating_add(Weight::from_parts(1_453, 0).saturating_mul(s.into())) + .saturating_add(Weight::from_parts(1_463, 0).saturating_mul(s.into())) .saturating_add(T::DbWeight::get().reads(9_u64)) .saturating_add(T::DbWeight::get().writes(7_u64)) } @@ -314,8 +316,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `759` // Estimated: `21615` - // Minimum execution time: 191_046_000 picoseconds. - Weight::from_parts(192_544_000, 21615) + // Minimum execution time: 190_769_000 picoseconds. + Weight::from_parts(191_717_000, 21615) .saturating_add(T::DbWeight::get().reads(6_u64)) .saturating_add(T::DbWeight::get().writes(4_u64)) } @@ -332,10 +334,10 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `109` // Estimated: `7366` - // Minimum execution time: 244_260_000 picoseconds. - Weight::from_parts(254_693_985, 7366) - // Standard Error: 80 - .saturating_add(Weight::from_parts(108_246, 0).saturating_mul(c.into())) + // Minimum execution time: 246_204_000 picoseconds. + Weight::from_parts(243_234_754, 7366) + // Standard Error: 78 + .saturating_add(Weight::from_parts(109_232, 0).saturating_mul(c.into())) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().writes(4_u64)) } @@ -351,8 +353,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `255` // Estimated: `7950` - // Minimum execution time: 33_492_000 picoseconds. - Weight::from_parts(34_079_000, 7950) + // Minimum execution time: 33_516_000 picoseconds. + Weight::from_parts(33_995_000, 7950) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().writes(4_u64)) } @@ -366,8 +368,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `570` // Estimated: `19530` - // Minimum execution time: 33_475_000 picoseconds. - Weight::from_parts(33_856_000, 19530) + // Minimum execution time: 33_255_000 picoseconds. + Weight::from_parts(33_692_000, 19530) .saturating_add(T::DbWeight::get().reads(6_u64)) .saturating_add(T::DbWeight::get().writes(6_u64)) } @@ -386,10 +388,10 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `781 + r * (6 ±0)` // Estimated: `21730 + r * (30 ±0)` - // Minimum execution time: 234_197_000 picoseconds. - Weight::from_parts(236_830_305, 21730) - // Standard Error: 818 - .saturating_add(Weight::from_parts(336_446, 0).saturating_mul(r.into())) + // Minimum execution time: 235_074_000 picoseconds. + Weight::from_parts(243_735_179, 21730) + // Standard Error: 972 + .saturating_add(Weight::from_parts(328_571, 0).saturating_mul(r.into())) .saturating_add(T::DbWeight::get().reads(6_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) .saturating_add(Weight::from_parts(0, 30).saturating_mul(r.into())) @@ -409,10 +411,10 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `839 + r * (240 ±0)` // Estimated: `21835 + r * (3675 ±0)` - // Minimum execution time: 235_872_000 picoseconds. - Weight::from_parts(78_877_890, 21835) - // Standard Error: 6_405 - .saturating_add(Weight::from_parts(3_358_341, 0).saturating_mul(r.into())) + // Minimum execution time: 236_455_000 picoseconds. + Weight::from_parts(81_818_936, 21835) + // Standard Error: 5_874 + .saturating_add(Weight::from_parts(3_316_006, 0).saturating_mul(r.into())) .saturating_add(T::DbWeight::get().reads(6_u64)) .saturating_add(T::DbWeight::get().reads((1_u64).saturating_mul(r.into()))) .saturating_add(T::DbWeight::get().writes(3_u64)) @@ -433,10 +435,10 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `831 + r * (244 ±0)` // Estimated: `21855 + r * (3695 ±0)` - // Minimum execution time: 239_035_000 picoseconds. - Weight::from_parts(93_255_085, 21855) - // Standard Error: 5_922 - .saturating_add(Weight::from_parts(4_144_910, 0).saturating_mul(r.into())) + // Minimum execution time: 237_724_000 picoseconds. + Weight::from_parts(91_384_964, 21855) + // Standard Error: 5_828 + .saturating_add(Weight::from_parts(4_063_221, 0).saturating_mul(r.into())) .saturating_add(T::DbWeight::get().reads(6_u64)) .saturating_add(T::DbWeight::get().reads((1_u64).saturating_mul(r.into()))) .saturating_add(T::DbWeight::get().writes(3_u64)) @@ -457,10 +459,10 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `788 + r * (6 ±0)` // Estimated: `21770 + r * (30 ±0)` - // Minimum execution time: 236_507_000 picoseconds. - Weight::from_parts(238_253_211, 21770) - // Standard Error: 975 - .saturating_add(Weight::from_parts(413_919, 0).saturating_mul(r.into())) + // Minimum execution time: 236_144_000 picoseconds. + Weight::from_parts(239_917_873, 21770) + // Standard Error: 629 + .saturating_add(Weight::from_parts(409_752, 0).saturating_mul(r.into())) .saturating_add(T::DbWeight::get().reads(6_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) .saturating_add(Weight::from_parts(0, 30).saturating_mul(r.into())) @@ -480,10 +482,10 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `778 + r * (3 ±0)` // Estimated: `21735 + r * (15 ±0)` - // Minimum execution time: 235_521_000 picoseconds. - Weight::from_parts(238_397_362, 21735) - // Standard Error: 452 - .saturating_add(Weight::from_parts(160_860, 0).saturating_mul(r.into())) + // Minimum execution time: 232_946_000 picoseconds. + Weight::from_parts(243_163_112, 21735) + // Standard Error: 1_940 + .saturating_add(Weight::from_parts(162_705, 0).saturating_mul(r.into())) .saturating_add(T::DbWeight::get().reads(6_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) .saturating_add(Weight::from_parts(0, 15).saturating_mul(r.into())) @@ -503,10 +505,10 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `782 + r * (6 ±0)` // Estimated: `21740 + r * (30 ±0)` - // Minimum execution time: 234_722_000 picoseconds. - Weight::from_parts(242_936_096, 21740) - // Standard Error: 1_178 - .saturating_add(Weight::from_parts(329_075, 0).saturating_mul(r.into())) + // Minimum execution time: 235_301_000 picoseconds. + Weight::from_parts(240_802_312, 21740) + // Standard Error: 5_362 + .saturating_add(Weight::from_parts(339_001, 0).saturating_mul(r.into())) .saturating_add(T::DbWeight::get().reads(6_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) .saturating_add(Weight::from_parts(0, 30).saturating_mul(r.into())) @@ -526,10 +528,10 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `783 + r * (6 ±0)` // Estimated: `21725 + r * (30 ±0)` - // Minimum execution time: 235_654_000 picoseconds. - Weight::from_parts(245_887_792, 21725) - // Standard Error: 903 - .saturating_add(Weight::from_parts(325_168, 0).saturating_mul(r.into())) + // Minimum execution time: 235_683_000 picoseconds. + Weight::from_parts(237_357_276, 21725) + // Standard Error: 726 + .saturating_add(Weight::from_parts(333_264, 0).saturating_mul(r.into())) .saturating_add(T::DbWeight::get().reads(6_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) .saturating_add(Weight::from_parts(0, 30).saturating_mul(r.into())) @@ -549,10 +551,10 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `922 + r * (6 ±0)` // Estimated: `24633 + r * (30 ±0)` - // Minimum execution time: 233_599_000 picoseconds. - Weight::from_parts(251_561_602, 24633) - // Standard Error: 3_348 - .saturating_add(Weight::from_parts(1_475_443, 0).saturating_mul(r.into())) + // Minimum execution time: 234_327_000 picoseconds. + Weight::from_parts(239_792_456, 24633) + // Standard Error: 3_460 + .saturating_add(Weight::from_parts(1_496_090, 0).saturating_mul(r.into())) .saturating_add(T::DbWeight::get().reads(7_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) .saturating_add(Weight::from_parts(0, 30).saturating_mul(r.into())) @@ -572,10 +574,10 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `792 + r * (6 ±0)` // Estimated: `21825 + r * (30 ±0)` - // Minimum execution time: 235_087_000 picoseconds. - Weight::from_parts(235_855_322, 21825) - // Standard Error: 867 - .saturating_add(Weight::from_parts(346_707, 0).saturating_mul(r.into())) + // Minimum execution time: 235_466_000 picoseconds. + Weight::from_parts(242_580_658, 21825) + // Standard Error: 1_439 + .saturating_add(Weight::from_parts(323_842, 0).saturating_mul(r.into())) .saturating_add(T::DbWeight::get().reads(6_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) .saturating_add(Weight::from_parts(0, 30).saturating_mul(r.into())) @@ -595,10 +597,10 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `790 + r * (6 ±0)` // Estimated: `21815 + r * (30 ±0)` - // Minimum execution time: 237_103_000 picoseconds. - Weight::from_parts(239_272_188, 21815) - // Standard Error: 892 - .saturating_add(Weight::from_parts(328_334, 0).saturating_mul(r.into())) + // Minimum execution time: 238_529_000 picoseconds. + Weight::from_parts(249_276_722, 21815) + // Standard Error: 1_216 + .saturating_add(Weight::from_parts(328_600, 0).saturating_mul(r.into())) .saturating_add(T::DbWeight::get().reads(6_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) .saturating_add(Weight::from_parts(0, 30).saturating_mul(r.into())) @@ -618,10 +620,10 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `787 + r * (6 ±0)` // Estimated: `21805 + r * (30 ±0)` - // Minimum execution time: 234_761_000 picoseconds. - Weight::from_parts(238_601_784, 21805) - // Standard Error: 725 - .saturating_add(Weight::from_parts(325_758, 0).saturating_mul(r.into())) + // Minimum execution time: 234_360_000 picoseconds. + Weight::from_parts(239_927_876, 21805) + // Standard Error: 541 + .saturating_add(Weight::from_parts(319_553, 0).saturating_mul(r.into())) .saturating_add(T::DbWeight::get().reads(6_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) .saturating_add(Weight::from_parts(0, 30).saturating_mul(r.into())) @@ -641,10 +643,10 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `778 + r * (6 ±0)` // Estimated: `21735 + r * (30 ±0)` - // Minimum execution time: 235_249_000 picoseconds. - Weight::from_parts(239_861_242, 21735) - // Standard Error: 751 - .saturating_add(Weight::from_parts(325_795, 0).saturating_mul(r.into())) + // Minimum execution time: 234_461_000 picoseconds. + Weight::from_parts(239_682_633, 21735) + // Standard Error: 544 + .saturating_add(Weight::from_parts(322_943, 0).saturating_mul(r.into())) .saturating_add(T::DbWeight::get().reads(6_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) .saturating_add(Weight::from_parts(0, 30).saturating_mul(r.into())) @@ -666,10 +668,10 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `856 + r * (10 ±0)` // Estimated: `24446 + r * (60 ±0)` - // Minimum execution time: 234_912_000 picoseconds. - Weight::from_parts(254_783_734, 24446) - // Standard Error: 1_610 - .saturating_add(Weight::from_parts(1_307_506, 0).saturating_mul(r.into())) + // Minimum execution time: 234_862_000 picoseconds. + Weight::from_parts(252_614_390, 24446) + // Standard Error: 1_180 + .saturating_add(Weight::from_parts(1_313_286, 0).saturating_mul(r.into())) .saturating_add(T::DbWeight::get().reads(7_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) .saturating_add(Weight::from_parts(0, 60).saturating_mul(r.into())) @@ -689,10 +691,10 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `745 + r * (4 ±0)` // Estimated: `21555 + r * (20 ±0)` - // Minimum execution time: 160_125_000 picoseconds. - Weight::from_parts(164_915_574, 21555) - // Standard Error: 332 - .saturating_add(Weight::from_parts(132_326, 0).saturating_mul(r.into())) + // Minimum execution time: 161_003_000 picoseconds. + Weight::from_parts(165_793_675, 21555) + // Standard Error: 323 + .saturating_add(Weight::from_parts(128_941, 0).saturating_mul(r.into())) .saturating_add(T::DbWeight::get().reads(6_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) .saturating_add(Weight::from_parts(0, 20).saturating_mul(r.into())) @@ -712,10 +714,10 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `780 + r * (6 ±0)` // Estimated: `21740 + r * (30 ±0)` - // Minimum execution time: 234_717_000 picoseconds. - Weight::from_parts(238_540_521, 21740) - // Standard Error: 599 - .saturating_add(Weight::from_parts(277_303, 0).saturating_mul(r.into())) + // Minimum execution time: 235_192_000 picoseconds. + Weight::from_parts(241_225_671, 21740) + // Standard Error: 1_132 + .saturating_add(Weight::from_parts(272_760, 0).saturating_mul(r.into())) .saturating_add(T::DbWeight::get().reads(6_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) .saturating_add(Weight::from_parts(0, 30).saturating_mul(r.into())) @@ -735,10 +737,10 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `784` // Estimated: `21740` - // Minimum execution time: 235_792_000 picoseconds. - Weight::from_parts(244_114_692, 21740) + // Minimum execution time: 238_050_000 picoseconds. + Weight::from_parts(241_274_832, 21740) // Standard Error: 1 - .saturating_add(Weight::from_parts(589, 0).saturating_mul(n.into())) + .saturating_add(Weight::from_parts(590, 0).saturating_mul(n.into())) .saturating_add(T::DbWeight::get().reads(6_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) } @@ -757,10 +759,10 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `768 + r * (45 ±0)` // Estimated: `21660 + r * (225 ±0)` - // Minimum execution time: 231_166_000 picoseconds. - Weight::from_parts(233_339_177, 21660) - // Standard Error: 155_889 - .saturating_add(Weight::from_parts(3_124_322, 0).saturating_mul(r.into())) + // Minimum execution time: 231_807_000 picoseconds. + Weight::from_parts(234_264_848, 21660) + // Standard Error: 185_298 + .saturating_add(Weight::from_parts(1_000_351, 0).saturating_mul(r.into())) .saturating_add(T::DbWeight::get().reads(6_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) .saturating_add(Weight::from_parts(0, 225).saturating_mul(r.into())) @@ -780,10 +782,10 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `778` // Estimated: `21775` - // Minimum execution time: 235_721_000 picoseconds. - Weight::from_parts(237_413_703, 21775) - // Standard Error: 1 - .saturating_add(Weight::from_parts(177, 0).saturating_mul(n.into())) + // Minimum execution time: 234_347_000 picoseconds. + Weight::from_parts(234_774_467, 21775) + // Standard Error: 0 + .saturating_add(Weight::from_parts(183, 0).saturating_mul(n.into())) .saturating_add(T::DbWeight::get().reads(6_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) } @@ -808,10 +810,10 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `810 + r * (356 ±0)` // Estimated: `26094 + r * (15904 ±0)` - // Minimum execution time: 233_525_000 picoseconds. - Weight::from_parts(235_871_034, 26094) - // Standard Error: 235_338 - .saturating_add(Weight::from_parts(118_659_865, 0).saturating_mul(r.into())) + // Minimum execution time: 234_356_000 picoseconds. + Weight::from_parts(236_844_659, 26094) + // Standard Error: 161_035 + .saturating_add(Weight::from_parts(110_725_340, 0).saturating_mul(r.into())) .saturating_add(T::DbWeight::get().reads(6_u64)) .saturating_add(T::DbWeight::get().reads((6_u64).saturating_mul(r.into()))) .saturating_add(T::DbWeight::get().writes(3_u64)) @@ -835,10 +837,10 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `825 + r * (10 ±0)` // Estimated: `24283 + r * (60 ±0)` - // Minimum execution time: 235_007_000 picoseconds. - Weight::from_parts(248_419_686, 24283) - // Standard Error: 1_847 - .saturating_add(Weight::from_parts(1_815_822, 0).saturating_mul(r.into())) + // Minimum execution time: 234_248_000 picoseconds. + Weight::from_parts(255_712_101, 24283) + // Standard Error: 1_474 + .saturating_add(Weight::from_parts(1_753_943, 0).saturating_mul(r.into())) .saturating_add(T::DbWeight::get().reads(7_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) .saturating_add(Weight::from_parts(0, 60).saturating_mul(r.into())) @@ -858,10 +860,10 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `778 + r * (10 ±0)` // Estimated: `21735 + r * (50 ±0)` - // Minimum execution time: 232_912_000 picoseconds. - Weight::from_parts(256_142_885, 21735) - // Standard Error: 2_741 - .saturating_add(Weight::from_parts(3_542_482, 0).saturating_mul(r.into())) + // Minimum execution time: 232_456_000 picoseconds. + Weight::from_parts(230_738_907, 21735) + // Standard Error: 4_163 + .saturating_add(Weight::from_parts(3_496_817, 0).saturating_mul(r.into())) .saturating_add(T::DbWeight::get().reads(6_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) .saturating_add(Weight::from_parts(0, 50).saturating_mul(r.into())) @@ -882,12 +884,12 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `797 + t * (32 ±0)` // Estimated: `21840 + t * (2640 ±0)` - // Minimum execution time: 251_018_000 picoseconds. - Weight::from_parts(245_280_765, 21840) - // Standard Error: 90_317 - .saturating_add(Weight::from_parts(2_434_496, 0).saturating_mul(t.into())) + // Minimum execution time: 251_638_000 picoseconds. + Weight::from_parts(244_791_817, 21840) + // Standard Error: 89_537 + .saturating_add(Weight::from_parts(2_459_492, 0).saturating_mul(t.into())) // Standard Error: 25 - .saturating_add(Weight::from_parts(599, 0).saturating_mul(n.into())) + .saturating_add(Weight::from_parts(626, 0).saturating_mul(n.into())) .saturating_add(T::DbWeight::get().reads(6_u64)) .saturating_add(T::DbWeight::get().reads((1_u64).saturating_mul(t.into()))) .saturating_add(T::DbWeight::get().writes(3_u64)) @@ -909,10 +911,10 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `777 + r * (7 ±0)` // Estimated: `21725 + r * (35 ±0)` - // Minimum execution time: 164_022_000 picoseconds. - Weight::from_parts(168_658_387, 21725) - // Standard Error: 685 - .saturating_add(Weight::from_parts(238_133, 0).saturating_mul(r.into())) + // Minimum execution time: 164_736_000 picoseconds. + Weight::from_parts(164_496_597, 21725) + // Standard Error: 4_619 + .saturating_add(Weight::from_parts(252_013, 0).saturating_mul(r.into())) .saturating_add(T::DbWeight::get().reads(6_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) .saturating_add(Weight::from_parts(0, 35).saturating_mul(r.into())) @@ -932,10 +934,10 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `125728` // Estimated: `269977` - // Minimum execution time: 351_043_000 picoseconds. - Weight::from_parts(353_707_344, 269977) - // Standard Error: 3 - .saturating_add(Weight::from_parts(752, 0).saturating_mul(i.into())) + // Minimum execution time: 352_146_000 picoseconds. + Weight::from_parts(357_758_251, 269977) + // Standard Error: 1 + .saturating_add(Weight::from_parts(735, 0).saturating_mul(i.into())) .saturating_add(T::DbWeight::get().reads(6_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) } @@ -946,10 +948,10 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `845 + r * (292 ±0)` // Estimated: `843 + r * (293 ±0)` - // Minimum execution time: 235_854_000 picoseconds. - Weight::from_parts(133_986_225, 843) - // Standard Error: 9_550 - .saturating_add(Weight::from_parts(6_093_051, 0).saturating_mul(r.into())) + // Minimum execution time: 236_170_000 picoseconds. + Weight::from_parts(136_284_582, 843) + // Standard Error: 10_269 + .saturating_add(Weight::from_parts(5_995_228, 0).saturating_mul(r.into())) .saturating_add(T::DbWeight::get().reads(6_u64)) .saturating_add(T::DbWeight::get().reads((1_u64).saturating_mul(r.into()))) .saturating_add(T::DbWeight::get().writes(3_u64)) @@ -963,10 +965,10 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `1304` // Estimated: `1280` - // Minimum execution time: 252_321_000 picoseconds. - Weight::from_parts(285_820_577, 1280) - // Standard Error: 60 - .saturating_add(Weight::from_parts(600, 0).saturating_mul(n.into())) + // Minimum execution time: 253_837_000 picoseconds. + Weight::from_parts(287_029_261, 1280) + // Standard Error: 51 + .saturating_add(Weight::from_parts(534, 0).saturating_mul(n.into())) .saturating_add(T::DbWeight::get().reads(8_u64)) .saturating_add(T::DbWeight::get().writes(6_u64)) } @@ -977,10 +979,10 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `1167 + n * (1 ±0)` // Estimated: `1167 + n * (1 ±0)` - // Minimum execution time: 252_047_000 picoseconds. - Weight::from_parts(254_244_310, 1167) - // Standard Error: 15 - .saturating_add(Weight::from_parts(144, 0).saturating_mul(n.into())) + // Minimum execution time: 253_037_000 picoseconds. + Weight::from_parts(256_401_533, 1167) + // Standard Error: 18 + .saturating_add(Weight::from_parts(32, 0).saturating_mul(n.into())) .saturating_add(T::DbWeight::get().reads(7_u64)) .saturating_add(T::DbWeight::get().writes(4_u64)) .saturating_add(Weight::from_parts(0, 1).saturating_mul(n.into())) @@ -992,10 +994,10 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `841 + r * (288 ±0)` // Estimated: `845 + r * (289 ±0)` - // Minimum execution time: 235_697_000 picoseconds. - Weight::from_parts(143_200_942, 845) - // Standard Error: 11_358 - .saturating_add(Weight::from_parts(5_934_318, 0).saturating_mul(r.into())) + // Minimum execution time: 236_800_000 picoseconds. + Weight::from_parts(134_748_031, 845) + // Standard Error: 9_560 + .saturating_add(Weight::from_parts(5_923_187, 0).saturating_mul(r.into())) .saturating_add(T::DbWeight::get().reads(6_u64)) .saturating_add(T::DbWeight::get().reads((1_u64).saturating_mul(r.into()))) .saturating_add(T::DbWeight::get().writes(3_u64)) @@ -1009,10 +1011,10 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `1163 + n * (1 ±0)` // Estimated: `1163 + n * (1 ±0)` - // Minimum execution time: 250_360_000 picoseconds. - Weight::from_parts(252_712_722, 1163) - // Standard Error: 15 - .saturating_add(Weight::from_parts(130, 0).saturating_mul(n.into())) + // Minimum execution time: 250_601_000 picoseconds. + Weight::from_parts(253_618_803, 1163) + // Standard Error: 18 + .saturating_add(Weight::from_parts(74, 0).saturating_mul(n.into())) .saturating_add(T::DbWeight::get().reads(7_u64)) .saturating_add(T::DbWeight::get().writes(4_u64)) .saturating_add(Weight::from_parts(0, 1).saturating_mul(n.into())) @@ -1024,10 +1026,10 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `835 + r * (296 ±0)` // Estimated: `840 + r * (297 ±0)` - // Minimum execution time: 235_613_000 picoseconds. - Weight::from_parts(150_213_792, 840) - // Standard Error: 8_617 - .saturating_add(Weight::from_parts(4_962_874, 0).saturating_mul(r.into())) + // Minimum execution time: 236_194_000 picoseconds. + Weight::from_parts(152_630_909, 840) + // Standard Error: 8_573 + .saturating_add(Weight::from_parts(4_896_099, 0).saturating_mul(r.into())) .saturating_add(T::DbWeight::get().reads(6_u64)) .saturating_add(T::DbWeight::get().reads((1_u64).saturating_mul(r.into()))) .saturating_add(T::DbWeight::get().writes(3_u64)) @@ -1040,10 +1042,10 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `1179 + n * (1 ±0)` // Estimated: `1179 + n * (1 ±0)` - // Minimum execution time: 249_137_000 picoseconds. - Weight::from_parts(253_529_386, 1179) - // Standard Error: 41 - .saturating_add(Weight::from_parts(612, 0).saturating_mul(n.into())) + // Minimum execution time: 250_989_000 picoseconds. + Weight::from_parts(260_170_747, 1179) + // Standard Error: 162 + .saturating_add(Weight::from_parts(224, 0).saturating_mul(n.into())) .saturating_add(T::DbWeight::get().reads(7_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) .saturating_add(Weight::from_parts(0, 1).saturating_mul(n.into())) @@ -1055,10 +1057,10 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `856 + r * (288 ±0)` // Estimated: `857 + r * (289 ±0)` - // Minimum execution time: 235_659_000 picoseconds. - Weight::from_parts(158_846_683, 857) - // Standard Error: 7_806 - .saturating_add(Weight::from_parts(4_728_537, 0).saturating_mul(r.into())) + // Minimum execution time: 236_574_000 picoseconds. + Weight::from_parts(155_573_955, 857) + // Standard Error: 10_270 + .saturating_add(Weight::from_parts(4_667_644, 0).saturating_mul(r.into())) .saturating_add(T::DbWeight::get().reads(6_u64)) .saturating_add(T::DbWeight::get().reads((1_u64).saturating_mul(r.into()))) .saturating_add(T::DbWeight::get().writes(3_u64)) @@ -1071,10 +1073,10 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `1166 + n * (1 ±0)` // Estimated: `1166 + n * (1 ±0)` - // Minimum execution time: 248_553_000 picoseconds. - Weight::from_parts(250_703_269, 1166) - // Standard Error: 17 - .saturating_add(Weight::from_parts(163, 0).saturating_mul(n.into())) + // Minimum execution time: 249_502_000 picoseconds. + Weight::from_parts(251_496_311, 1166) + // Standard Error: 26 + .saturating_add(Weight::from_parts(186, 0).saturating_mul(n.into())) .saturating_add(T::DbWeight::get().reads(7_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) .saturating_add(Weight::from_parts(0, 1).saturating_mul(n.into())) @@ -1086,10 +1088,10 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `829 + r * (296 ±0)` // Estimated: `836 + r * (297 ±0)` - // Minimum execution time: 236_431_000 picoseconds. - Weight::from_parts(131_745_419, 836) - // Standard Error: 10_161 - .saturating_add(Weight::from_parts(6_182_174, 0).saturating_mul(r.into())) + // Minimum execution time: 236_030_000 picoseconds. + Weight::from_parts(128_595_856, 836) + // Standard Error: 10_530 + .saturating_add(Weight::from_parts(6_072_638, 0).saturating_mul(r.into())) .saturating_add(T::DbWeight::get().reads(6_u64)) .saturating_add(T::DbWeight::get().reads((1_u64).saturating_mul(r.into()))) .saturating_add(T::DbWeight::get().writes(3_u64)) @@ -1103,10 +1105,10 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `1180 + n * (1 ±0)` // Estimated: `1180 + n * (1 ±0)` - // Minimum execution time: 252_551_000 picoseconds. - Weight::from_parts(254_517_030, 1180) - // Standard Error: 16 - .saturating_add(Weight::from_parts(712, 0).saturating_mul(n.into())) + // Minimum execution time: 254_027_000 picoseconds. + Weight::from_parts(260_739_475, 1180) + // Standard Error: 115 + .saturating_add(Weight::from_parts(330, 0).saturating_mul(n.into())) .saturating_add(T::DbWeight::get().reads(7_u64)) .saturating_add(T::DbWeight::get().writes(4_u64)) .saturating_add(Weight::from_parts(0, 1).saturating_mul(n.into())) @@ -1126,10 +1128,10 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `1373 + r * (45 ±0)` // Estimated: `26753 + r * (2700 ±0)` - // Minimum execution time: 236_663_000 picoseconds. - Weight::from_parts(237_485_000, 26753) - // Standard Error: 42_414 - .saturating_add(Weight::from_parts(36_849_514, 0).saturating_mul(r.into())) + // Minimum execution time: 237_634_000 picoseconds. + Weight::from_parts(238_177_000, 26753) + // Standard Error: 23_320 + .saturating_add(Weight::from_parts(35_467_618, 0).saturating_mul(r.into())) .saturating_add(T::DbWeight::get().reads(7_u64)) .saturating_add(T::DbWeight::get().reads((1_u64).saturating_mul(r.into()))) .saturating_add(T::DbWeight::get().writes(4_u64)) @@ -1151,10 +1153,10 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `1237 + r * (256 ±0)` // Estimated: `26028 + r * (6235 ±0)` - // Minimum execution time: 237_101_000 picoseconds. - Weight::from_parts(237_827_000, 26028) - // Standard Error: 82_878 - .saturating_add(Weight::from_parts(211_777_724, 0).saturating_mul(r.into())) + // Minimum execution time: 237_076_000 picoseconds. + Weight::from_parts(237_792_000, 26028) + // Standard Error: 91_566 + .saturating_add(Weight::from_parts(212_893_975, 0).saturating_mul(r.into())) .saturating_add(T::DbWeight::get().reads(7_u64)) .saturating_add(T::DbWeight::get().reads((2_u64).saturating_mul(r.into()))) .saturating_add(T::DbWeight::get().writes(3_u64)) @@ -1176,10 +1178,10 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `0 + r * (502 ±0)` // Estimated: `21755 + r * (6329 ±3)` - // Minimum execution time: 241_213_000 picoseconds. - Weight::from_parts(241_900_000, 21755) - // Standard Error: 99_976 - .saturating_add(Weight::from_parts(207_520_077, 0).saturating_mul(r.into())) + // Minimum execution time: 236_275_000 picoseconds. + Weight::from_parts(236_849_000, 21755) + // Standard Error: 92_724 + .saturating_add(Weight::from_parts(207_159_396, 0).saturating_mul(r.into())) .saturating_add(T::DbWeight::get().reads(6_u64)) .saturating_add(T::DbWeight::get().reads((2_u64).saturating_mul(r.into()))) .saturating_add(T::DbWeight::get().writes(3_u64)) @@ -1202,12 +1204,12 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `1154 + t * (204 ±0)` // Estimated: `31015 + t * (5970 ±0)` - // Minimum execution time: 414_784_000 picoseconds. - Weight::from_parts(384_902_379, 31015) - // Standard Error: 1_228_593 - .saturating_add(Weight::from_parts(33_226_901, 0).saturating_mul(t.into())) + // Minimum execution time: 412_628_000 picoseconds. + Weight::from_parts(385_108_035, 31015) + // Standard Error: 1_002_078 + .saturating_add(Weight::from_parts(31_204_678, 0).saturating_mul(t.into())) // Standard Error: 1 - .saturating_add(Weight::from_parts(601, 0).saturating_mul(c.into())) + .saturating_add(Weight::from_parts(598, 0).saturating_mul(c.into())) .saturating_add(T::DbWeight::get().reads(9_u64)) .saturating_add(T::DbWeight::get().reads((2_u64).saturating_mul(t.into()))) .saturating_add(T::DbWeight::get().writes(5_u64)) @@ -1233,10 +1235,10 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `1301 + r * (254 ±0)` // Estimated: `30977 + r * (16635 ±0)` - // Minimum execution time: 236_963_000 picoseconds. - Weight::from_parts(237_711_000, 30977) - // Standard Error: 265_576 - .saturating_add(Weight::from_parts(347_359_908, 0).saturating_mul(r.into())) + // Minimum execution time: 237_161_000 picoseconds. + Weight::from_parts(237_620_000, 30977) + // Standard Error: 258_036 + .saturating_add(Weight::from_parts(344_869_637, 0).saturating_mul(r.into())) .saturating_add(T::DbWeight::get().reads(8_u64)) .saturating_add(T::DbWeight::get().reads((6_u64).saturating_mul(r.into()))) .saturating_add(T::DbWeight::get().writes(6_u64)) @@ -1264,14 +1266,14 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `1071 + t * (187 ±0)` // Estimated: `42684 + t * (3588 ±2)` - // Minimum execution time: 1_615_191_000 picoseconds. - Weight::from_parts(337_408_450, 42684) - // Standard Error: 4_581_951 - .saturating_add(Weight::from_parts(115_730_776, 0).saturating_mul(t.into())) + // Minimum execution time: 1_613_172_000 picoseconds. + Weight::from_parts(326_952_137, 42684) + // Standard Error: 4_738_925 + .saturating_add(Weight::from_parts(113_657_996, 0).saturating_mul(t.into())) // Standard Error: 7 - .saturating_add(Weight::from_parts(1_171, 0).saturating_mul(i.into())) + .saturating_add(Weight::from_parts(1_173, 0).saturating_mul(i.into())) // Standard Error: 7 - .saturating_add(Weight::from_parts(1_346, 0).saturating_mul(s.into())) + .saturating_add(Weight::from_parts(1_347, 0).saturating_mul(s.into())) .saturating_add(T::DbWeight::get().reads(13_u64)) .saturating_add(T::DbWeight::get().reads((1_u64).saturating_mul(t.into()))) .saturating_add(T::DbWeight::get().writes(10_u64)) @@ -1293,10 +1295,10 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `777 + r * (8 ±0)` // Estimated: `21710 + r * (40 ±0)` - // Minimum execution time: 233_601_000 picoseconds. - Weight::from_parts(246_594_905, 21710) - // Standard Error: 2_840 - .saturating_add(Weight::from_parts(578_751, 0).saturating_mul(r.into())) + // Minimum execution time: 233_679_000 picoseconds. + Weight::from_parts(238_638_820, 21710) + // Standard Error: 681 + .saturating_add(Weight::from_parts(573_320, 0).saturating_mul(r.into())) .saturating_add(T::DbWeight::get().reads(6_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) .saturating_add(Weight::from_parts(0, 40).saturating_mul(r.into())) @@ -1316,10 +1318,10 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `785` // Estimated: `21745` - // Minimum execution time: 233_735_000 picoseconds. - Weight::from_parts(243_432_330, 21745) - // Standard Error: 3 - .saturating_add(Weight::from_parts(3_927, 0).saturating_mul(n.into())) + // Minimum execution time: 235_992_000 picoseconds. + Weight::from_parts(219_924_252, 21745) + // Standard Error: 5 + .saturating_add(Weight::from_parts(3_978, 0).saturating_mul(n.into())) .saturating_add(T::DbWeight::get().reads(6_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) } @@ -1338,10 +1340,10 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `779 + r * (8 ±0)` // Estimated: `21725 + r * (40 ±0)` - // Minimum execution time: 232_173_000 picoseconds. - Weight::from_parts(239_806_011, 21725) - // Standard Error: 1_530 - .saturating_add(Weight::from_parts(749_641, 0).saturating_mul(r.into())) + // Minimum execution time: 240_661_000 picoseconds. + Weight::from_parts(238_809_422, 21725) + // Standard Error: 820 + .saturating_add(Weight::from_parts(741_326, 0).saturating_mul(r.into())) .saturating_add(T::DbWeight::get().reads(6_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) .saturating_add(Weight::from_parts(0, 40).saturating_mul(r.into())) @@ -1361,10 +1363,10 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `787` // Estimated: `21765` - // Minimum execution time: 235_046_000 picoseconds. - Weight::from_parts(229_500_792, 21765) + // Minimum execution time: 235_136_000 picoseconds. + Weight::from_parts(228_678_487, 21765) // Standard Error: 2 - .saturating_add(Weight::from_parts(3_166, 0).saturating_mul(n.into())) + .saturating_add(Weight::from_parts(3_164, 0).saturating_mul(n.into())) .saturating_add(T::DbWeight::get().reads(6_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) } @@ -1383,10 +1385,10 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `779 + r * (8 ±0)` // Estimated: `21740 + r * (40 ±0)` - // Minimum execution time: 231_708_000 picoseconds. - Weight::from_parts(235_347_566, 21740) - // Standard Error: 987 - .saturating_add(Weight::from_parts(428_819, 0).saturating_mul(r.into())) + // Minimum execution time: 233_364_000 picoseconds. + Weight::from_parts(243_294_285, 21740) + // Standard Error: 1_607 + .saturating_add(Weight::from_parts(413_186, 0).saturating_mul(r.into())) .saturating_add(T::DbWeight::get().reads(6_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) .saturating_add(Weight::from_parts(0, 40).saturating_mul(r.into())) @@ -1406,10 +1408,10 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `787` // Estimated: `21785` - // Minimum execution time: 234_068_000 picoseconds. - Weight::from_parts(226_519_852, 21785) + // Minimum execution time: 234_315_000 picoseconds. + Weight::from_parts(225_569_537, 21785) // Standard Error: 1 - .saturating_add(Weight::from_parts(916, 0).saturating_mul(n.into())) + .saturating_add(Weight::from_parts(919, 0).saturating_mul(n.into())) .saturating_add(T::DbWeight::get().reads(6_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) } @@ -1428,10 +1430,10 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `779 + r * (8 ±0)` // Estimated: `21745 + r * (40 ±0)` - // Minimum execution time: 231_872_000 picoseconds. - Weight::from_parts(236_694_763, 21745) - // Standard Error: 870 - .saturating_add(Weight::from_parts(420_853, 0).saturating_mul(r.into())) + // Minimum execution time: 233_288_000 picoseconds. + Weight::from_parts(239_289_154, 21745) + // Standard Error: 612 + .saturating_add(Weight::from_parts(414_275, 0).saturating_mul(r.into())) .saturating_add(T::DbWeight::get().reads(6_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) .saturating_add(Weight::from_parts(0, 40).saturating_mul(r.into())) @@ -1451,10 +1453,10 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `787` // Estimated: `21755` - // Minimum execution time: 233_707_000 picoseconds. - Weight::from_parts(226_312_559, 21755) - // Standard Error: 2 - .saturating_add(Weight::from_parts(924, 0).saturating_mul(n.into())) + // Minimum execution time: 233_686_000 picoseconds. + Weight::from_parts(227_406_694, 21755) + // Standard Error: 1 + .saturating_add(Weight::from_parts(919, 0).saturating_mul(n.into())) .saturating_add(T::DbWeight::get().reads(6_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) } @@ -1468,15 +1470,61 @@ impl WeightInfo for SubstrateWeight { /// Proof: Timestamp Now (max_values: Some(1), max_size: Some(8), added: 503, mode: Measured) /// Storage: System EventTopics (r:2 w:2) /// Proof Skipped: System EventTopics (max_values: None, max_size: None, mode: Measured) + /// The range of component `n` is `[0, 125697]`. + fn seal_sr25519_verify_per_byte(n: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `916 + n * (1 ±0)` + // Estimated: `22375 + n * (5 ±0)` + // Minimum execution time: 287_258_000 picoseconds. + Weight::from_parts(290_941_609, 22375) + // Standard Error: 8 + .saturating_add(Weight::from_parts(4_785, 0).saturating_mul(n.into())) + .saturating_add(T::DbWeight::get().reads(6_u64)) + .saturating_add(T::DbWeight::get().writes(3_u64)) + .saturating_add(Weight::from_parts(0, 5).saturating_mul(n.into())) + } + /// Storage: System Account (r:1 w:0) + /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: Measured) + /// Storage: Contracts ContractInfoOf (r:1 w:1) + /// Proof: Contracts ContractInfoOf (max_values: None, max_size: Some(290), added: 2765, mode: Measured) + /// Storage: Contracts CodeStorage (r:1 w:0) + /// Proof: Contracts CodeStorage (max_values: None, max_size: Some(126001), added: 128476, mode: Measured) + /// Storage: Timestamp Now (r:1 w:0) + /// Proof: Timestamp Now (max_values: Some(1), max_size: Some(8), added: 503, mode: Measured) + /// Storage: System EventTopics (r:2 w:2) + /// Proof Skipped: System EventTopics (max_values: None, max_size: None, mode: Measured) + /// The range of component `r` is `[0, 160]`. + fn seal_sr25519_verify(r: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `728 + r * (112 ±0)` + // Estimated: `21455 + r * (560 ±0)` + // Minimum execution time: 238_340_000 picoseconds. + Weight::from_parts(246_009_851, 21455) + // Standard Error: 21_677 + .saturating_add(Weight::from_parts(48_139_487, 0).saturating_mul(r.into())) + .saturating_add(T::DbWeight::get().reads(6_u64)) + .saturating_add(T::DbWeight::get().writes(3_u64)) + .saturating_add(Weight::from_parts(0, 560).saturating_mul(r.into())) + } + /// Storage: System Account (r:1 w:0) + /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: Measured) + /// Storage: Contracts ContractInfoOf (r:1 w:1) + /// Proof: Contracts ContractInfoOf (max_values: None, max_size: Some(290), added: 2765, mode: Measured) + /// Storage: Contracts CodeStorage (r:1 w:0) + /// Proof: Contracts CodeStorage (max_values: None, max_size: Some(126001), added: 128476, mode: Measured) + /// Storage: Timestamp Now (r:1 w:0) + /// Proof: Timestamp Now (max_values: Some(1), max_size: Some(8), added: 503, mode: Measured) + /// Storage: System EventTopics (r:2 w:2) + /// Proof Skipped: System EventTopics (max_values: None, max_size: None, mode: Measured) /// The range of component `r` is `[0, 160]`. fn seal_ecdsa_recover(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `822 + r * (76 ±0)` // Estimated: `21705 + r * (385 ±0)` - // Minimum execution time: 234_962_000 picoseconds. - Weight::from_parts(252_611_292, 21705) - // Standard Error: 20_134 - .saturating_add(Weight::from_parts(37_745_358, 0).saturating_mul(r.into())) + // Minimum execution time: 236_244_000 picoseconds. + Weight::from_parts(253_749_242, 21705) + // Standard Error: 19_216 + .saturating_add(Weight::from_parts(37_706_782, 0).saturating_mul(r.into())) .saturating_add(T::DbWeight::get().reads(6_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) .saturating_add(Weight::from_parts(0, 385).saturating_mul(r.into())) @@ -1496,10 +1544,10 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `792 + r * (42 ±0)` // Estimated: `21775 + r * (210 ±0)` - // Minimum execution time: 234_869_000 picoseconds. - Weight::from_parts(240_188_331, 21775) - // Standard Error: 9_910 - .saturating_add(Weight::from_parts(9_332_432, 0).saturating_mul(r.into())) + // Minimum execution time: 235_770_000 picoseconds. + Weight::from_parts(230_780_347, 21775) + // Standard Error: 12_015 + .saturating_add(Weight::from_parts(9_584_947, 0).saturating_mul(r.into())) .saturating_add(T::DbWeight::get().reads(6_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) .saturating_add(Weight::from_parts(0, 210).saturating_mul(r.into())) @@ -1520,11 +1568,11 @@ impl WeightInfo for SubstrateWeight { fn seal_set_code_hash(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0 + r * (964 ±0)` - // Estimated: `29920 + r * (11544 ±10)` - // Minimum execution time: 235_036_000 picoseconds. - Weight::from_parts(235_538_000, 29920) - // Standard Error: 47_360 - .saturating_add(Weight::from_parts(22_113_144, 0).saturating_mul(r.into())) + // Estimated: `29920 + r * (11544 ±7)` + // Minimum execution time: 235_857_000 picoseconds. + Weight::from_parts(236_482_000, 29920) + // Standard Error: 47_818 + .saturating_add(Weight::from_parts(21_765_273, 0).saturating_mul(r.into())) .saturating_add(T::DbWeight::get().reads(6_u64)) .saturating_add(T::DbWeight::get().reads((3_u64).saturating_mul(r.into()))) .saturating_add(T::DbWeight::get().writes(3_u64)) @@ -1546,10 +1594,10 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `773 + r * (3 ±0)` // Estimated: `21735 + r * (15 ±0)` - // Minimum execution time: 237_884_000 picoseconds. - Weight::from_parts(243_315_095, 21735) - // Standard Error: 560 - .saturating_add(Weight::from_parts(162_206, 0).saturating_mul(r.into())) + // Minimum execution time: 235_388_000 picoseconds. + Weight::from_parts(240_149_512, 21735) + // Standard Error: 555 + .saturating_add(Weight::from_parts(162_666, 0).saturating_mul(r.into())) .saturating_add(T::DbWeight::get().reads(6_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) .saturating_add(Weight::from_parts(0, 15).saturating_mul(r.into())) @@ -1569,10 +1617,10 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `1975 + r * (39 ±0)` // Estimated: `27145 + r * (200 ±0)` - // Minimum execution time: 239_402_000 picoseconds. - Weight::from_parts(267_214_783, 27145) - // Standard Error: 1_166 - .saturating_add(Weight::from_parts(261_915, 0).saturating_mul(r.into())) + // Minimum execution time: 237_485_000 picoseconds. + Weight::from_parts(268_044_053, 27145) + // Standard Error: 1_147 + .saturating_add(Weight::from_parts(260_572, 0).saturating_mul(r.into())) .saturating_add(T::DbWeight::get().reads(6_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) .saturating_add(Weight::from_parts(0, 200).saturating_mul(r.into())) @@ -1594,10 +1642,10 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `776 + r * (3 ±0)` // Estimated: `24004 + r * (18 ±0)` - // Minimum execution time: 233_153_000 picoseconds. - Weight::from_parts(238_999_965, 24004) - // Standard Error: 291 - .saturating_add(Weight::from_parts(143_971, 0).saturating_mul(r.into())) + // Minimum execution time: 234_123_000 picoseconds. + Weight::from_parts(245_872_832, 24004) + // Standard Error: 943 + .saturating_add(Weight::from_parts(136_460, 0).saturating_mul(r.into())) .saturating_add(T::DbWeight::get().reads(7_u64)) .saturating_add(T::DbWeight::get().writes(4_u64)) .saturating_add(Weight::from_parts(0, 18).saturating_mul(r.into())) @@ -1607,510 +1655,510 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_706_000 picoseconds. - Weight::from_parts(1_962_558, 0) + // Minimum execution time: 1_606_000 picoseconds. + Weight::from_parts(1_845_698, 0) // Standard Error: 2 - .saturating_add(Weight::from_parts(3_000, 0).saturating_mul(r.into())) + .saturating_add(Weight::from_parts(2_995, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 5000]`. fn instr_i64load(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_870_000 picoseconds. - Weight::from_parts(2_481_243, 0) + // Minimum execution time: 1_711_000 picoseconds. + Weight::from_parts(2_281_581, 0) // Standard Error: 4 - .saturating_add(Weight::from_parts(6_346, 0).saturating_mul(r.into())) + .saturating_add(Weight::from_parts(6_354, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 5000]`. fn instr_i64store(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_830_000 picoseconds. - Weight::from_parts(2_389_658, 0) + // Minimum execution time: 1_680_000 picoseconds. + Weight::from_parts(2_269_238, 0) // Standard Error: 4 - .saturating_add(Weight::from_parts(5_997, 0).saturating_mul(r.into())) + .saturating_add(Weight::from_parts(6_014, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 5000]`. fn instr_select(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_734_000 picoseconds. - Weight::from_parts(2_170_618, 0) - // Standard Error: 4 - .saturating_add(Weight::from_parts(7_919, 0).saturating_mul(r.into())) + // Minimum execution time: 1_592_000 picoseconds. + Weight::from_parts(2_010_963, 0) + // Standard Error: 3 + .saturating_add(Weight::from_parts(7_927, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 5000]`. fn instr_if(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_661_000 picoseconds. - Weight::from_parts(1_945_771, 0) - // Standard Error: 11 - .saturating_add(Weight::from_parts(10_840, 0).saturating_mul(r.into())) + // Minimum execution time: 1_631_000 picoseconds. + Weight::from_parts(1_936_549, 0) + // Standard Error: 6 + .saturating_add(Weight::from_parts(10_426, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 5000]`. fn instr_br(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_682_000 picoseconds. - Weight::from_parts(1_970_774, 0) - // Standard Error: 11 - .saturating_add(Weight::from_parts(4_569, 0).saturating_mul(r.into())) + // Minimum execution time: 1_616_000 picoseconds. + Weight::from_parts(1_938_716, 0) + // Standard Error: 15 + .saturating_add(Weight::from_parts(4_525, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 5000]`. fn instr_br_if(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_668_000 picoseconds. - Weight::from_parts(1_227_080, 0) - // Standard Error: 76 - .saturating_add(Weight::from_parts(8_066, 0).saturating_mul(r.into())) + // Minimum execution time: 1_535_000 picoseconds. + Weight::from_parts(1_109_860, 0) + // Standard Error: 77 + .saturating_add(Weight::from_parts(7_998, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 5000]`. fn instr_br_table(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_653_000 picoseconds. - Weight::from_parts(1_394_759, 0) - // Standard Error: 39 - .saturating_add(Weight::from_parts(9_566, 0).saturating_mul(r.into())) + // Minimum execution time: 1_594_000 picoseconds. + Weight::from_parts(1_478_654, 0) + // Standard Error: 36 + .saturating_add(Weight::from_parts(9_525, 0).saturating_mul(r.into())) } /// The range of component `e` is `[1, 256]`. fn instr_br_table_per_entry(e: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_771_000 picoseconds. - Weight::from_parts(1_905_594, 0) - // Standard Error: 147 - .saturating_add(Weight::from_parts(925, 0).saturating_mul(e.into())) + // Minimum execution time: 1_730_000 picoseconds. + Weight::from_parts(1_836_342, 0) + // Standard Error: 14 + .saturating_add(Weight::from_parts(206, 0).saturating_mul(e.into())) } /// The range of component `r` is `[0, 5000]`. fn instr_call(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_863_000 picoseconds. - Weight::from_parts(2_472_635, 0) - // Standard Error: 13 - .saturating_add(Weight::from_parts(17_892, 0).saturating_mul(r.into())) + // Minimum execution time: 1_607_000 picoseconds. + Weight::from_parts(2_150_133, 0) + // Standard Error: 17 + .saturating_add(Weight::from_parts(18_630, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 5000]`. fn instr_call_indirect(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 2_013_000 picoseconds. - Weight::from_parts(3_077_100, 0) - // Standard Error: 18 - .saturating_add(Weight::from_parts(24_287, 0).saturating_mul(r.into())) + // Minimum execution time: 1_824_000 picoseconds. + Weight::from_parts(3_119_825, 0) + // Standard Error: 15 + .saturating_add(Weight::from_parts(25_304, 0).saturating_mul(r.into())) } /// The range of component `l` is `[0, 1024]`. fn instr_call_per_local(l: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_818_000 picoseconds. - Weight::from_parts(2_109_143, 0) - // Standard Error: 34 - .saturating_add(Weight::from_parts(1_249, 0).saturating_mul(l.into())) + // Minimum execution time: 1_692_000 picoseconds. + Weight::from_parts(1_927_049, 0) + // Standard Error: 14 + .saturating_add(Weight::from_parts(1_217, 0).saturating_mul(l.into())) } /// The range of component `r` is `[0, 5000]`. fn instr_local_get(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 3_083_000 picoseconds. - Weight::from_parts(3_291_328, 0) - // Standard Error: 1 - .saturating_add(Weight::from_parts(2_505, 0).saturating_mul(r.into())) + // Minimum execution time: 2_795_000 picoseconds. + Weight::from_parts(3_107_843, 0) + // Standard Error: 2 + .saturating_add(Weight::from_parts(2_445, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 5000]`. fn instr_local_set(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 2_987_000 picoseconds. - Weight::from_parts(3_276_863, 0) - // Standard Error: 2 - .saturating_add(Weight::from_parts(3_617, 0).saturating_mul(r.into())) + // Minimum execution time: 2_815_000 picoseconds. + Weight::from_parts(3_114_618, 0) + // Standard Error: 3 + .saturating_add(Weight::from_parts(3_639, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 5000]`. fn instr_local_tee(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 2_942_000 picoseconds. - Weight::from_parts(3_350_581, 0) + // Minimum execution time: 2_861_000 picoseconds. + Weight::from_parts(3_193_592, 0) // Standard Error: 3 - .saturating_add(Weight::from_parts(3_976, 0).saturating_mul(r.into())) + .saturating_add(Weight::from_parts(3_957, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 5000]`. fn instr_global_get(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_867_000 picoseconds. - Weight::from_parts(2_920_748, 0) - // Standard Error: 44 - .saturating_add(Weight::from_parts(8_229, 0).saturating_mul(r.into())) + // Minimum execution time: 1_710_000 picoseconds. + Weight::from_parts(2_148_021, 0) + // Standard Error: 3 + .saturating_add(Weight::from_parts(8_391, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 5000]`. fn instr_global_set(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_757_000 picoseconds. - Weight::from_parts(2_235_198, 0) - // Standard Error: 4 - .saturating_add(Weight::from_parts(8_815, 0).saturating_mul(r.into())) + // Minimum execution time: 1_663_000 picoseconds. + Weight::from_parts(2_170_373, 0) + // Standard Error: 3 + .saturating_add(Weight::from_parts(8_812, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 5000]`. fn instr_memory_current(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_824_000 picoseconds. - Weight::from_parts(1_941_816, 0) - // Standard Error: 86 - .saturating_add(Weight::from_parts(4_043, 0).saturating_mul(r.into())) + // Minimum execution time: 1_662_000 picoseconds. + Weight::from_parts(2_601_398, 0) + // Standard Error: 198 + .saturating_add(Weight::from_parts(3_851, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 16]`. fn instr_memory_grow(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_793_000 picoseconds. - Weight::from_parts(1_104_829, 0) - // Standard Error: 137_800 - .saturating_add(Weight::from_parts(13_336_784, 0).saturating_mul(r.into())) + // Minimum execution time: 1_603_000 picoseconds. + Weight::from_parts(332_932, 0) + // Standard Error: 137_529 + .saturating_add(Weight::from_parts(13_233_734, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 5000]`. fn instr_i64clz(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_693_000 picoseconds. - Weight::from_parts(2_037_305, 0) + // Minimum execution time: 1_613_000 picoseconds. + Weight::from_parts(1_922_391, 0) // Standard Error: 2 - .saturating_add(Weight::from_parts(4_044, 0).saturating_mul(r.into())) + .saturating_add(Weight::from_parts(3_903, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 5000]`. fn instr_i64ctz(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_751_000 picoseconds. - Weight::from_parts(2_082_016, 0) + // Minimum execution time: 1_522_000 picoseconds. + Weight::from_parts(1_920_023, 0) // Standard Error: 2 - .saturating_add(Weight::from_parts(3_767, 0).saturating_mul(r.into())) + .saturating_add(Weight::from_parts(3_876, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 5000]`. fn instr_i64popcnt(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_751_000 picoseconds. - Weight::from_parts(2_110_625, 0) - // Standard Error: 2 - .saturating_add(Weight::from_parts(3_754, 0).saturating_mul(r.into())) + // Minimum execution time: 1_590_000 picoseconds. + Weight::from_parts(1_867_406, 0) + // Standard Error: 3 + .saturating_add(Weight::from_parts(3_910, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 5000]`. fn instr_i64eqz(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_756_000 picoseconds. - Weight::from_parts(2_100_327, 0) + // Minimum execution time: 1_577_000 picoseconds. + Weight::from_parts(1_918_037, 0) // Standard Error: 2 - .saturating_add(Weight::from_parts(3_664, 0).saturating_mul(r.into())) + .saturating_add(Weight::from_parts(3_673, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 5000]`. fn instr_i64extendsi32(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_643_000 picoseconds. - Weight::from_parts(2_169_153, 0) + // Minimum execution time: 1_589_000 picoseconds. + Weight::from_parts(1_917_901, 0) // Standard Error: 3 - .saturating_add(Weight::from_parts(3_961, 0).saturating_mul(r.into())) + .saturating_add(Weight::from_parts(3_956, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 5000]`. fn instr_i64extendui32(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_704_000 picoseconds. - Weight::from_parts(2_049_172, 0) + // Minimum execution time: 1_573_000 picoseconds. + Weight::from_parts(1_902_324, 0) // Standard Error: 2 - .saturating_add(Weight::from_parts(3_833, 0).saturating_mul(r.into())) + .saturating_add(Weight::from_parts(3_865, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 5000]`. fn instr_i32wrapi64(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_726_000 picoseconds. - Weight::from_parts(2_064_387, 0) + // Minimum execution time: 1_586_000 picoseconds. + Weight::from_parts(1_953_738, 0) // Standard Error: 2 - .saturating_add(Weight::from_parts(3_745, 0).saturating_mul(r.into())) + .saturating_add(Weight::from_parts(3_838, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 5000]`. fn instr_i64eq(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_696_000 picoseconds. - Weight::from_parts(2_426_905, 0) - // Standard Error: 56 - .saturating_add(Weight::from_parts(5_915, 0).saturating_mul(r.into())) + // Minimum execution time: 1_598_000 picoseconds. + Weight::from_parts(1_912_180, 0) + // Standard Error: 2 + .saturating_add(Weight::from_parts(5_994, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 5000]`. fn instr_i64ne(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_617_000 picoseconds. - Weight::from_parts(2_035_161, 0) - // Standard Error: 94 - .saturating_add(Weight::from_parts(6_073, 0).saturating_mul(r.into())) + // Minimum execution time: 1_573_000 picoseconds. + Weight::from_parts(1_943_725, 0) + // Standard Error: 2 + .saturating_add(Weight::from_parts(5_998, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 5000]`. fn instr_i64lts(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_756_000 picoseconds. - Weight::from_parts(2_098_926, 0) + // Minimum execution time: 1_584_000 picoseconds. + Weight::from_parts(1_949_209, 0) // Standard Error: 2 - .saturating_add(Weight::from_parts(6_002, 0).saturating_mul(r.into())) + .saturating_add(Weight::from_parts(5_991, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 5000]`. fn instr_i64ltu(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_706_000 picoseconds. - Weight::from_parts(2_257_972, 0) - // Standard Error: 23 - .saturating_add(Weight::from_parts(5_982, 0).saturating_mul(r.into())) + // Minimum execution time: 1_576_000 picoseconds. + Weight::from_parts(1_927_466, 0) + // Standard Error: 2 + .saturating_add(Weight::from_parts(6_005, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 5000]`. fn instr_i64gts(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_761_000 picoseconds. - Weight::from_parts(2_114_141, 0) - // Standard Error: 2 - .saturating_add(Weight::from_parts(5_815, 0).saturating_mul(r.into())) + // Minimum execution time: 1_566_000 picoseconds. + Weight::from_parts(2_004_312, 0) + // Standard Error: 3 + .saturating_add(Weight::from_parts(5_799, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 5000]`. fn instr_i64gtu(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_700_000 picoseconds. - Weight::from_parts(2_053_201, 0) - // Standard Error: 2 - .saturating_add(Weight::from_parts(6_137, 0).saturating_mul(r.into())) + // Minimum execution time: 1_538_000 picoseconds. + Weight::from_parts(1_922_703, 0) + // Standard Error: 9 + .saturating_add(Weight::from_parts(6_129, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 5000]`. fn instr_i64les(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_705_000 picoseconds. - Weight::from_parts(2_101_782, 0) + // Minimum execution time: 1_575_000 picoseconds. + Weight::from_parts(1_978_271, 0) // Standard Error: 3 - .saturating_add(Weight::from_parts(6_014, 0).saturating_mul(r.into())) + .saturating_add(Weight::from_parts(5_991, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 5000]`. fn instr_i64leu(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_856_000 picoseconds. - Weight::from_parts(2_149_707, 0) - // Standard Error: 3 - .saturating_add(Weight::from_parts(6_086, 0).saturating_mul(r.into())) + // Minimum execution time: 1_572_000 picoseconds. + Weight::from_parts(4_028_578, 0) + // Standard Error: 263 + .saturating_add(Weight::from_parts(5_784, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 5000]`. fn instr_i64ges(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_739_000 picoseconds. - Weight::from_parts(2_143_216, 0) + // Minimum execution time: 1_615_000 picoseconds. + Weight::from_parts(1_962_065, 0) // Standard Error: 3 - .saturating_add(Weight::from_parts(5_934, 0).saturating_mul(r.into())) + .saturating_add(Weight::from_parts(5_945, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 5000]`. fn instr_i64geu(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_716_000 picoseconds. - Weight::from_parts(2_065_762, 0) - // Standard Error: 3 - .saturating_add(Weight::from_parts(6_009, 0).saturating_mul(r.into())) + // Minimum execution time: 1_578_000 picoseconds. + Weight::from_parts(1_909_721, 0) + // Standard Error: 2 + .saturating_add(Weight::from_parts(5_993, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 5000]`. fn instr_i64add(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_664_000 picoseconds. - Weight::from_parts(3_062_283, 0) - // Standard Error: 94 - .saturating_add(Weight::from_parts(5_645, 0).saturating_mul(r.into())) + // Minimum execution time: 1_609_000 picoseconds. + Weight::from_parts(1_926_472, 0) + // Standard Error: 3 + .saturating_add(Weight::from_parts(5_888, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 5000]`. fn instr_i64sub(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_671_000 picoseconds. - Weight::from_parts(2_011_145, 0) - // Standard Error: 44 - .saturating_add(Weight::from_parts(6_220, 0).saturating_mul(r.into())) + // Minimum execution time: 1_620_000 picoseconds. + Weight::from_parts(2_875_196, 0) + // Standard Error: 177 + .saturating_add(Weight::from_parts(5_990, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 5000]`. fn instr_i64mul(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_759_000 picoseconds. - Weight::from_parts(2_095_420, 0) - // Standard Error: 4 - .saturating_add(Weight::from_parts(5_723, 0).saturating_mul(r.into())) + // Minimum execution time: 1_570_000 picoseconds. + Weight::from_parts(2_155_483, 0) + // Standard Error: 83 + .saturating_add(Weight::from_parts(5_675, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 5000]`. fn instr_i64divs(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_672_000 picoseconds. - Weight::from_parts(2_184_044, 0) + // Minimum execution time: 1_558_000 picoseconds. + Weight::from_parts(2_108_263, 0) // Standard Error: 5 - .saturating_add(Weight::from_parts(11_782, 0).saturating_mul(r.into())) + .saturating_add(Weight::from_parts(11_755, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 5000]`. fn instr_i64divu(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_752_000 picoseconds. - Weight::from_parts(2_276_209, 0) - // Standard Error: 5 - .saturating_add(Weight::from_parts(10_513, 0).saturating_mul(r.into())) + // Minimum execution time: 1_568_000 picoseconds. + Weight::from_parts(1_606_397, 0) + // Standard Error: 51 + .saturating_add(Weight::from_parts(11_011, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 5000]`. fn instr_i64rems(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_711_000 picoseconds. - Weight::from_parts(2_720_989, 0) + // Minimum execution time: 1_630_000 picoseconds. + Weight::from_parts(2_872_487, 0) // Standard Error: 24 - .saturating_add(Weight::from_parts(11_884, 0).saturating_mul(r.into())) + .saturating_add(Weight::from_parts(11_761, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 5000]`. fn instr_i64remu(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_713_000 picoseconds. - Weight::from_parts(2_091_403, 0) - // Standard Error: 3 - .saturating_add(Weight::from_parts(10_628, 0).saturating_mul(r.into())) + // Minimum execution time: 1_575_000 picoseconds. + Weight::from_parts(2_385_398, 0) + // Standard Error: 45 + .saturating_add(Weight::from_parts(10_650, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 5000]`. fn instr_i64and(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_704_000 picoseconds. - Weight::from_parts(2_054_652, 0) + // Minimum execution time: 1_622_000 picoseconds. + Weight::from_parts(1_957_246, 0) // Standard Error: 2 - .saturating_add(Weight::from_parts(5_672, 0).saturating_mul(r.into())) + .saturating_add(Weight::from_parts(5_659, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 5000]`. fn instr_i64or(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_743_000 picoseconds. - Weight::from_parts(2_032_806, 0) - // Standard Error: 19 - .saturating_add(Weight::from_parts(5_795, 0).saturating_mul(r.into())) + // Minimum execution time: 1_551_000 picoseconds. + Weight::from_parts(1_897_168, 0) + // Standard Error: 3 + .saturating_add(Weight::from_parts(5_774, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 5000]`. fn instr_i64xor(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_667_000 picoseconds. - Weight::from_parts(2_031_702, 0) - // Standard Error: 5 - .saturating_add(Weight::from_parts(5_923, 0).saturating_mul(r.into())) + // Minimum execution time: 1_549_000 picoseconds. + Weight::from_parts(1_915_938, 0) + // Standard Error: 3 + .saturating_add(Weight::from_parts(5_915, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 5000]`. fn instr_i64shl(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_735_000 picoseconds. - Weight::from_parts(2_946_634, 0) - // Standard Error: 54 - .saturating_add(Weight::from_parts(5_685, 0).saturating_mul(r.into())) + // Minimum execution time: 1_617_000 picoseconds. + Weight::from_parts(1_932_618, 0) + // Standard Error: 3 + .saturating_add(Weight::from_parts(5_854, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 5000]`. fn instr_i64shrs(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_652_000 picoseconds. - Weight::from_parts(2_023_049, 0) - // Standard Error: 4 - .saturating_add(Weight::from_parts(6_146, 0).saturating_mul(r.into())) + // Minimum execution time: 1_617_000 picoseconds. + Weight::from_parts(1_937_566, 0) + // Standard Error: 3 + .saturating_add(Weight::from_parts(6_134, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 5000]`. fn instr_i64shru(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_654_000 picoseconds. - Weight::from_parts(2_148_951, 0) + // Minimum execution time: 1_584_000 picoseconds. + Weight::from_parts(1_980_681, 0) // Standard Error: 3 - .saturating_add(Weight::from_parts(5_869, 0).saturating_mul(r.into())) + .saturating_add(Weight::from_parts(5_842, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 5000]`. fn instr_i64rotl(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_730_000 picoseconds. - Weight::from_parts(2_130_543, 0) - // Standard Error: 3 - .saturating_add(Weight::from_parts(5_999, 0).saturating_mul(r.into())) + // Minimum execution time: 1_542_000 picoseconds. + Weight::from_parts(2_131_567, 0) + // Standard Error: 57 + .saturating_add(Weight::from_parts(6_010, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 5000]`. fn instr_i64rotr(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_728_000 picoseconds. - Weight::from_parts(2_172_886, 0) - // Standard Error: 3 - .saturating_add(Weight::from_parts(5_843, 0).saturating_mul(r.into())) + // Minimum execution time: 1_609_000 picoseconds. + Weight::from_parts(1_643_214, 0) + // Standard Error: 91 + .saturating_add(Weight::from_parts(6_062, 0).saturating_mul(r.into())) } } @@ -2122,8 +2170,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `109` // Estimated: `1594` - // Minimum execution time: 2_677_000 picoseconds. - Weight::from_parts(2_899_000, 1594) + // Minimum execution time: 2_503_000 picoseconds. + Weight::from_parts(2_603_000, 1594) .saturating_add(RocksDbWeight::get().reads(1_u64)) } /// Storage: Skipped Metadata (r:0 w:0) @@ -2133,10 +2181,10 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `488 + k * (69 ±0)` // Estimated: `478 + k * (70 ±0)` - // Minimum execution time: 14_006_000 picoseconds. - Weight::from_parts(8_735_946, 478) - // Standard Error: 1_370 - .saturating_add(Weight::from_parts(989_501, 0).saturating_mul(k.into())) + // Minimum execution time: 13_292_000 picoseconds. + Weight::from_parts(9_180_439, 478) + // Standard Error: 997 + .saturating_add(Weight::from_parts(967_522, 0).saturating_mul(k.into())) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().reads((1_u64).saturating_mul(k.into()))) .saturating_add(RocksDbWeight::get().writes(2_u64)) @@ -2152,10 +2200,10 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `238 + c * (1 ±0)` // Estimated: `3951 + c * (2 ±0)` - // Minimum execution time: 30_951_000 picoseconds. - Weight::from_parts(25_988_560, 3951) - // Standard Error: 57 - .saturating_add(Weight::from_parts(54_692, 0).saturating_mul(c.into())) + // Minimum execution time: 30_822_000 picoseconds. + Weight::from_parts(26_457_115, 3951) + // Standard Error: 58 + .saturating_add(Weight::from_parts(54_868, 0).saturating_mul(c.into())) .saturating_add(RocksDbWeight::get().reads(1_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) .saturating_add(Weight::from_parts(0, 2).saturating_mul(c.into())) @@ -2175,10 +2223,10 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `707` // Estimated: `21400 + c * (5 ±0)` - // Minimum execution time: 263_107_000 picoseconds. - Weight::from_parts(268_289_665, 21400) - // Standard Error: 33 - .saturating_add(Weight::from_parts(38_534, 0).saturating_mul(c.into())) + // Minimum execution time: 263_719_000 picoseconds. + Weight::from_parts(275_281_941, 21400) + // Standard Error: 32 + .saturating_add(Weight::from_parts(37_880, 0).saturating_mul(c.into())) .saturating_add(RocksDbWeight::get().reads(6_u64)) .saturating_add(RocksDbWeight::get().writes(4_u64)) .saturating_add(Weight::from_parts(0, 5).saturating_mul(c.into())) @@ -2206,14 +2254,14 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `270` // Estimated: `26207` - // Minimum execution time: 3_122_319_000 picoseconds. - Weight::from_parts(487_802_180, 26207) - // Standard Error: 345 - .saturating_add(Weight::from_parts(108_237, 0).saturating_mul(c.into())) - // Standard Error: 20 - .saturating_add(Weight::from_parts(1_166, 0).saturating_mul(i.into())) - // Standard Error: 20 - .saturating_add(Weight::from_parts(1_505, 0).saturating_mul(s.into())) + // Minimum execution time: 3_133_756_000 picoseconds. + Weight::from_parts(556_483_545, 26207) + // Standard Error: 294 + .saturating_add(Weight::from_parts(107_914, 0).saturating_mul(c.into())) + // Standard Error: 17 + .saturating_add(Weight::from_parts(1_143, 0).saturating_mul(i.into())) + // Standard Error: 17 + .saturating_add(Weight::from_parts(1_456, 0).saturating_mul(s.into())) .saturating_add(RocksDbWeight::get().reads(9_u64)) .saturating_add(RocksDbWeight::get().writes(10_u64)) } @@ -2237,12 +2285,12 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `482` // Estimated: `28521` - // Minimum execution time: 1_650_623_000 picoseconds. - Weight::from_parts(286_494_456, 28521) + // Minimum execution time: 1_646_953_000 picoseconds. + Weight::from_parts(262_115_215, 28521) // Standard Error: 8 - .saturating_add(Weight::from_parts(1_438, 0).saturating_mul(i.into())) + .saturating_add(Weight::from_parts(1_454, 0).saturating_mul(i.into())) // Standard Error: 8 - .saturating_add(Weight::from_parts(1_453, 0).saturating_mul(s.into())) + .saturating_add(Weight::from_parts(1_463, 0).saturating_mul(s.into())) .saturating_add(RocksDbWeight::get().reads(9_u64)) .saturating_add(RocksDbWeight::get().writes(7_u64)) } @@ -2260,8 +2308,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `759` // Estimated: `21615` - // Minimum execution time: 191_046_000 picoseconds. - Weight::from_parts(192_544_000, 21615) + // Minimum execution time: 190_769_000 picoseconds. + Weight::from_parts(191_717_000, 21615) .saturating_add(RocksDbWeight::get().reads(6_u64)) .saturating_add(RocksDbWeight::get().writes(4_u64)) } @@ -2278,10 +2326,10 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `109` // Estimated: `7366` - // Minimum execution time: 244_260_000 picoseconds. - Weight::from_parts(254_693_985, 7366) - // Standard Error: 80 - .saturating_add(Weight::from_parts(108_246, 0).saturating_mul(c.into())) + // Minimum execution time: 246_204_000 picoseconds. + Weight::from_parts(243_234_754, 7366) + // Standard Error: 78 + .saturating_add(Weight::from_parts(109_232, 0).saturating_mul(c.into())) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().writes(4_u64)) } @@ -2297,8 +2345,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `255` // Estimated: `7950` - // Minimum execution time: 33_492_000 picoseconds. - Weight::from_parts(34_079_000, 7950) + // Minimum execution time: 33_516_000 picoseconds. + Weight::from_parts(33_995_000, 7950) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().writes(4_u64)) } @@ -2312,8 +2360,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `570` // Estimated: `19530` - // Minimum execution time: 33_475_000 picoseconds. - Weight::from_parts(33_856_000, 19530) + // Minimum execution time: 33_255_000 picoseconds. + Weight::from_parts(33_692_000, 19530) .saturating_add(RocksDbWeight::get().reads(6_u64)) .saturating_add(RocksDbWeight::get().writes(6_u64)) } @@ -2332,10 +2380,10 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `781 + r * (6 ±0)` // Estimated: `21730 + r * (30 ±0)` - // Minimum execution time: 234_197_000 picoseconds. - Weight::from_parts(236_830_305, 21730) - // Standard Error: 818 - .saturating_add(Weight::from_parts(336_446, 0).saturating_mul(r.into())) + // Minimum execution time: 235_074_000 picoseconds. + Weight::from_parts(243_735_179, 21730) + // Standard Error: 972 + .saturating_add(Weight::from_parts(328_571, 0).saturating_mul(r.into())) .saturating_add(RocksDbWeight::get().reads(6_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) .saturating_add(Weight::from_parts(0, 30).saturating_mul(r.into())) @@ -2355,10 +2403,10 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `839 + r * (240 ±0)` // Estimated: `21835 + r * (3675 ±0)` - // Minimum execution time: 235_872_000 picoseconds. - Weight::from_parts(78_877_890, 21835) - // Standard Error: 6_405 - .saturating_add(Weight::from_parts(3_358_341, 0).saturating_mul(r.into())) + // Minimum execution time: 236_455_000 picoseconds. + Weight::from_parts(81_818_936, 21835) + // Standard Error: 5_874 + .saturating_add(Weight::from_parts(3_316_006, 0).saturating_mul(r.into())) .saturating_add(RocksDbWeight::get().reads(6_u64)) .saturating_add(RocksDbWeight::get().reads((1_u64).saturating_mul(r.into()))) .saturating_add(RocksDbWeight::get().writes(3_u64)) @@ -2379,10 +2427,10 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `831 + r * (244 ±0)` // Estimated: `21855 + r * (3695 ±0)` - // Minimum execution time: 239_035_000 picoseconds. - Weight::from_parts(93_255_085, 21855) - // Standard Error: 5_922 - .saturating_add(Weight::from_parts(4_144_910, 0).saturating_mul(r.into())) + // Minimum execution time: 237_724_000 picoseconds. + Weight::from_parts(91_384_964, 21855) + // Standard Error: 5_828 + .saturating_add(Weight::from_parts(4_063_221, 0).saturating_mul(r.into())) .saturating_add(RocksDbWeight::get().reads(6_u64)) .saturating_add(RocksDbWeight::get().reads((1_u64).saturating_mul(r.into()))) .saturating_add(RocksDbWeight::get().writes(3_u64)) @@ -2403,10 +2451,10 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `788 + r * (6 ±0)` // Estimated: `21770 + r * (30 ±0)` - // Minimum execution time: 236_507_000 picoseconds. - Weight::from_parts(238_253_211, 21770) - // Standard Error: 975 - .saturating_add(Weight::from_parts(413_919, 0).saturating_mul(r.into())) + // Minimum execution time: 236_144_000 picoseconds. + Weight::from_parts(239_917_873, 21770) + // Standard Error: 629 + .saturating_add(Weight::from_parts(409_752, 0).saturating_mul(r.into())) .saturating_add(RocksDbWeight::get().reads(6_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) .saturating_add(Weight::from_parts(0, 30).saturating_mul(r.into())) @@ -2426,10 +2474,10 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `778 + r * (3 ±0)` // Estimated: `21735 + r * (15 ±0)` - // Minimum execution time: 235_521_000 picoseconds. - Weight::from_parts(238_397_362, 21735) - // Standard Error: 452 - .saturating_add(Weight::from_parts(160_860, 0).saturating_mul(r.into())) + // Minimum execution time: 232_946_000 picoseconds. + Weight::from_parts(243_163_112, 21735) + // Standard Error: 1_940 + .saturating_add(Weight::from_parts(162_705, 0).saturating_mul(r.into())) .saturating_add(RocksDbWeight::get().reads(6_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) .saturating_add(Weight::from_parts(0, 15).saturating_mul(r.into())) @@ -2449,10 +2497,10 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `782 + r * (6 ±0)` // Estimated: `21740 + r * (30 ±0)` - // Minimum execution time: 234_722_000 picoseconds. - Weight::from_parts(242_936_096, 21740) - // Standard Error: 1_178 - .saturating_add(Weight::from_parts(329_075, 0).saturating_mul(r.into())) + // Minimum execution time: 235_301_000 picoseconds. + Weight::from_parts(240_802_312, 21740) + // Standard Error: 5_362 + .saturating_add(Weight::from_parts(339_001, 0).saturating_mul(r.into())) .saturating_add(RocksDbWeight::get().reads(6_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) .saturating_add(Weight::from_parts(0, 30).saturating_mul(r.into())) @@ -2472,10 +2520,10 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `783 + r * (6 ±0)` // Estimated: `21725 + r * (30 ±0)` - // Minimum execution time: 235_654_000 picoseconds. - Weight::from_parts(245_887_792, 21725) - // Standard Error: 903 - .saturating_add(Weight::from_parts(325_168, 0).saturating_mul(r.into())) + // Minimum execution time: 235_683_000 picoseconds. + Weight::from_parts(237_357_276, 21725) + // Standard Error: 726 + .saturating_add(Weight::from_parts(333_264, 0).saturating_mul(r.into())) .saturating_add(RocksDbWeight::get().reads(6_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) .saturating_add(Weight::from_parts(0, 30).saturating_mul(r.into())) @@ -2495,10 +2543,10 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `922 + r * (6 ±0)` // Estimated: `24633 + r * (30 ±0)` - // Minimum execution time: 233_599_000 picoseconds. - Weight::from_parts(251_561_602, 24633) - // Standard Error: 3_348 - .saturating_add(Weight::from_parts(1_475_443, 0).saturating_mul(r.into())) + // Minimum execution time: 234_327_000 picoseconds. + Weight::from_parts(239_792_456, 24633) + // Standard Error: 3_460 + .saturating_add(Weight::from_parts(1_496_090, 0).saturating_mul(r.into())) .saturating_add(RocksDbWeight::get().reads(7_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) .saturating_add(Weight::from_parts(0, 30).saturating_mul(r.into())) @@ -2518,10 +2566,10 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `792 + r * (6 ±0)` // Estimated: `21825 + r * (30 ±0)` - // Minimum execution time: 235_087_000 picoseconds. - Weight::from_parts(235_855_322, 21825) - // Standard Error: 867 - .saturating_add(Weight::from_parts(346_707, 0).saturating_mul(r.into())) + // Minimum execution time: 235_466_000 picoseconds. + Weight::from_parts(242_580_658, 21825) + // Standard Error: 1_439 + .saturating_add(Weight::from_parts(323_842, 0).saturating_mul(r.into())) .saturating_add(RocksDbWeight::get().reads(6_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) .saturating_add(Weight::from_parts(0, 30).saturating_mul(r.into())) @@ -2541,10 +2589,10 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `790 + r * (6 ±0)` // Estimated: `21815 + r * (30 ±0)` - // Minimum execution time: 237_103_000 picoseconds. - Weight::from_parts(239_272_188, 21815) - // Standard Error: 892 - .saturating_add(Weight::from_parts(328_334, 0).saturating_mul(r.into())) + // Minimum execution time: 238_529_000 picoseconds. + Weight::from_parts(249_276_722, 21815) + // Standard Error: 1_216 + .saturating_add(Weight::from_parts(328_600, 0).saturating_mul(r.into())) .saturating_add(RocksDbWeight::get().reads(6_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) .saturating_add(Weight::from_parts(0, 30).saturating_mul(r.into())) @@ -2564,10 +2612,10 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `787 + r * (6 ±0)` // Estimated: `21805 + r * (30 ±0)` - // Minimum execution time: 234_761_000 picoseconds. - Weight::from_parts(238_601_784, 21805) - // Standard Error: 725 - .saturating_add(Weight::from_parts(325_758, 0).saturating_mul(r.into())) + // Minimum execution time: 234_360_000 picoseconds. + Weight::from_parts(239_927_876, 21805) + // Standard Error: 541 + .saturating_add(Weight::from_parts(319_553, 0).saturating_mul(r.into())) .saturating_add(RocksDbWeight::get().reads(6_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) .saturating_add(Weight::from_parts(0, 30).saturating_mul(r.into())) @@ -2587,10 +2635,10 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `778 + r * (6 ±0)` // Estimated: `21735 + r * (30 ±0)` - // Minimum execution time: 235_249_000 picoseconds. - Weight::from_parts(239_861_242, 21735) - // Standard Error: 751 - .saturating_add(Weight::from_parts(325_795, 0).saturating_mul(r.into())) + // Minimum execution time: 234_461_000 picoseconds. + Weight::from_parts(239_682_633, 21735) + // Standard Error: 544 + .saturating_add(Weight::from_parts(322_943, 0).saturating_mul(r.into())) .saturating_add(RocksDbWeight::get().reads(6_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) .saturating_add(Weight::from_parts(0, 30).saturating_mul(r.into())) @@ -2612,10 +2660,10 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `856 + r * (10 ±0)` // Estimated: `24446 + r * (60 ±0)` - // Minimum execution time: 234_912_000 picoseconds. - Weight::from_parts(254_783_734, 24446) - // Standard Error: 1_610 - .saturating_add(Weight::from_parts(1_307_506, 0).saturating_mul(r.into())) + // Minimum execution time: 234_862_000 picoseconds. + Weight::from_parts(252_614_390, 24446) + // Standard Error: 1_180 + .saturating_add(Weight::from_parts(1_313_286, 0).saturating_mul(r.into())) .saturating_add(RocksDbWeight::get().reads(7_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) .saturating_add(Weight::from_parts(0, 60).saturating_mul(r.into())) @@ -2635,10 +2683,10 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `745 + r * (4 ±0)` // Estimated: `21555 + r * (20 ±0)` - // Minimum execution time: 160_125_000 picoseconds. - Weight::from_parts(164_915_574, 21555) - // Standard Error: 332 - .saturating_add(Weight::from_parts(132_326, 0).saturating_mul(r.into())) + // Minimum execution time: 161_003_000 picoseconds. + Weight::from_parts(165_793_675, 21555) + // Standard Error: 323 + .saturating_add(Weight::from_parts(128_941, 0).saturating_mul(r.into())) .saturating_add(RocksDbWeight::get().reads(6_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) .saturating_add(Weight::from_parts(0, 20).saturating_mul(r.into())) @@ -2658,10 +2706,10 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `780 + r * (6 ±0)` // Estimated: `21740 + r * (30 ±0)` - // Minimum execution time: 234_717_000 picoseconds. - Weight::from_parts(238_540_521, 21740) - // Standard Error: 599 - .saturating_add(Weight::from_parts(277_303, 0).saturating_mul(r.into())) + // Minimum execution time: 235_192_000 picoseconds. + Weight::from_parts(241_225_671, 21740) + // Standard Error: 1_132 + .saturating_add(Weight::from_parts(272_760, 0).saturating_mul(r.into())) .saturating_add(RocksDbWeight::get().reads(6_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) .saturating_add(Weight::from_parts(0, 30).saturating_mul(r.into())) @@ -2681,10 +2729,10 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `784` // Estimated: `21740` - // Minimum execution time: 235_792_000 picoseconds. - Weight::from_parts(244_114_692, 21740) + // Minimum execution time: 238_050_000 picoseconds. + Weight::from_parts(241_274_832, 21740) // Standard Error: 1 - .saturating_add(Weight::from_parts(589, 0).saturating_mul(n.into())) + .saturating_add(Weight::from_parts(590, 0).saturating_mul(n.into())) .saturating_add(RocksDbWeight::get().reads(6_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) } @@ -2703,10 +2751,10 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `768 + r * (45 ±0)` // Estimated: `21660 + r * (225 ±0)` - // Minimum execution time: 231_166_000 picoseconds. - Weight::from_parts(233_339_177, 21660) - // Standard Error: 155_889 - .saturating_add(Weight::from_parts(3_124_322, 0).saturating_mul(r.into())) + // Minimum execution time: 231_807_000 picoseconds. + Weight::from_parts(234_264_848, 21660) + // Standard Error: 185_298 + .saturating_add(Weight::from_parts(1_000_351, 0).saturating_mul(r.into())) .saturating_add(RocksDbWeight::get().reads(6_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) .saturating_add(Weight::from_parts(0, 225).saturating_mul(r.into())) @@ -2726,10 +2774,10 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `778` // Estimated: `21775` - // Minimum execution time: 235_721_000 picoseconds. - Weight::from_parts(237_413_703, 21775) - // Standard Error: 1 - .saturating_add(Weight::from_parts(177, 0).saturating_mul(n.into())) + // Minimum execution time: 234_347_000 picoseconds. + Weight::from_parts(234_774_467, 21775) + // Standard Error: 0 + .saturating_add(Weight::from_parts(183, 0).saturating_mul(n.into())) .saturating_add(RocksDbWeight::get().reads(6_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) } @@ -2754,10 +2802,10 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `810 + r * (356 ±0)` // Estimated: `26094 + r * (15904 ±0)` - // Minimum execution time: 233_525_000 picoseconds. - Weight::from_parts(235_871_034, 26094) - // Standard Error: 235_338 - .saturating_add(Weight::from_parts(118_659_865, 0).saturating_mul(r.into())) + // Minimum execution time: 234_356_000 picoseconds. + Weight::from_parts(236_844_659, 26094) + // Standard Error: 161_035 + .saturating_add(Weight::from_parts(110_725_340, 0).saturating_mul(r.into())) .saturating_add(RocksDbWeight::get().reads(6_u64)) .saturating_add(RocksDbWeight::get().reads((6_u64).saturating_mul(r.into()))) .saturating_add(RocksDbWeight::get().writes(3_u64)) @@ -2781,10 +2829,10 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `825 + r * (10 ±0)` // Estimated: `24283 + r * (60 ±0)` - // Minimum execution time: 235_007_000 picoseconds. - Weight::from_parts(248_419_686, 24283) - // Standard Error: 1_847 - .saturating_add(Weight::from_parts(1_815_822, 0).saturating_mul(r.into())) + // Minimum execution time: 234_248_000 picoseconds. + Weight::from_parts(255_712_101, 24283) + // Standard Error: 1_474 + .saturating_add(Weight::from_parts(1_753_943, 0).saturating_mul(r.into())) .saturating_add(RocksDbWeight::get().reads(7_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) .saturating_add(Weight::from_parts(0, 60).saturating_mul(r.into())) @@ -2804,10 +2852,10 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `778 + r * (10 ±0)` // Estimated: `21735 + r * (50 ±0)` - // Minimum execution time: 232_912_000 picoseconds. - Weight::from_parts(256_142_885, 21735) - // Standard Error: 2_741 - .saturating_add(Weight::from_parts(3_542_482, 0).saturating_mul(r.into())) + // Minimum execution time: 232_456_000 picoseconds. + Weight::from_parts(230_738_907, 21735) + // Standard Error: 4_163 + .saturating_add(Weight::from_parts(3_496_817, 0).saturating_mul(r.into())) .saturating_add(RocksDbWeight::get().reads(6_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) .saturating_add(Weight::from_parts(0, 50).saturating_mul(r.into())) @@ -2828,12 +2876,12 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `797 + t * (32 ±0)` // Estimated: `21840 + t * (2640 ±0)` - // Minimum execution time: 251_018_000 picoseconds. - Weight::from_parts(245_280_765, 21840) - // Standard Error: 90_317 - .saturating_add(Weight::from_parts(2_434_496, 0).saturating_mul(t.into())) + // Minimum execution time: 251_638_000 picoseconds. + Weight::from_parts(244_791_817, 21840) + // Standard Error: 89_537 + .saturating_add(Weight::from_parts(2_459_492, 0).saturating_mul(t.into())) // Standard Error: 25 - .saturating_add(Weight::from_parts(599, 0).saturating_mul(n.into())) + .saturating_add(Weight::from_parts(626, 0).saturating_mul(n.into())) .saturating_add(RocksDbWeight::get().reads(6_u64)) .saturating_add(RocksDbWeight::get().reads((1_u64).saturating_mul(t.into()))) .saturating_add(RocksDbWeight::get().writes(3_u64)) @@ -2855,10 +2903,10 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `777 + r * (7 ±0)` // Estimated: `21725 + r * (35 ±0)` - // Minimum execution time: 164_022_000 picoseconds. - Weight::from_parts(168_658_387, 21725) - // Standard Error: 685 - .saturating_add(Weight::from_parts(238_133, 0).saturating_mul(r.into())) + // Minimum execution time: 164_736_000 picoseconds. + Weight::from_parts(164_496_597, 21725) + // Standard Error: 4_619 + .saturating_add(Weight::from_parts(252_013, 0).saturating_mul(r.into())) .saturating_add(RocksDbWeight::get().reads(6_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) .saturating_add(Weight::from_parts(0, 35).saturating_mul(r.into())) @@ -2878,10 +2926,10 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `125728` // Estimated: `269977` - // Minimum execution time: 351_043_000 picoseconds. - Weight::from_parts(353_707_344, 269977) - // Standard Error: 3 - .saturating_add(Weight::from_parts(752, 0).saturating_mul(i.into())) + // Minimum execution time: 352_146_000 picoseconds. + Weight::from_parts(357_758_251, 269977) + // Standard Error: 1 + .saturating_add(Weight::from_parts(735, 0).saturating_mul(i.into())) .saturating_add(RocksDbWeight::get().reads(6_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) } @@ -2892,10 +2940,10 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `845 + r * (292 ±0)` // Estimated: `843 + r * (293 ±0)` - // Minimum execution time: 235_854_000 picoseconds. - Weight::from_parts(133_986_225, 843) - // Standard Error: 9_550 - .saturating_add(Weight::from_parts(6_093_051, 0).saturating_mul(r.into())) + // Minimum execution time: 236_170_000 picoseconds. + Weight::from_parts(136_284_582, 843) + // Standard Error: 10_269 + .saturating_add(Weight::from_parts(5_995_228, 0).saturating_mul(r.into())) .saturating_add(RocksDbWeight::get().reads(6_u64)) .saturating_add(RocksDbWeight::get().reads((1_u64).saturating_mul(r.into()))) .saturating_add(RocksDbWeight::get().writes(3_u64)) @@ -2909,10 +2957,10 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `1304` // Estimated: `1280` - // Minimum execution time: 252_321_000 picoseconds. - Weight::from_parts(285_820_577, 1280) - // Standard Error: 60 - .saturating_add(Weight::from_parts(600, 0).saturating_mul(n.into())) + // Minimum execution time: 253_837_000 picoseconds. + Weight::from_parts(287_029_261, 1280) + // Standard Error: 51 + .saturating_add(Weight::from_parts(534, 0).saturating_mul(n.into())) .saturating_add(RocksDbWeight::get().reads(8_u64)) .saturating_add(RocksDbWeight::get().writes(6_u64)) } @@ -2923,10 +2971,10 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `1167 + n * (1 ±0)` // Estimated: `1167 + n * (1 ±0)` - // Minimum execution time: 252_047_000 picoseconds. - Weight::from_parts(254_244_310, 1167) - // Standard Error: 15 - .saturating_add(Weight::from_parts(144, 0).saturating_mul(n.into())) + // Minimum execution time: 253_037_000 picoseconds. + Weight::from_parts(256_401_533, 1167) + // Standard Error: 18 + .saturating_add(Weight::from_parts(32, 0).saturating_mul(n.into())) .saturating_add(RocksDbWeight::get().reads(7_u64)) .saturating_add(RocksDbWeight::get().writes(4_u64)) .saturating_add(Weight::from_parts(0, 1).saturating_mul(n.into())) @@ -2938,10 +2986,10 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `841 + r * (288 ±0)` // Estimated: `845 + r * (289 ±0)` - // Minimum execution time: 235_697_000 picoseconds. - Weight::from_parts(143_200_942, 845) - // Standard Error: 11_358 - .saturating_add(Weight::from_parts(5_934_318, 0).saturating_mul(r.into())) + // Minimum execution time: 236_800_000 picoseconds. + Weight::from_parts(134_748_031, 845) + // Standard Error: 9_560 + .saturating_add(Weight::from_parts(5_923_187, 0).saturating_mul(r.into())) .saturating_add(RocksDbWeight::get().reads(6_u64)) .saturating_add(RocksDbWeight::get().reads((1_u64).saturating_mul(r.into()))) .saturating_add(RocksDbWeight::get().writes(3_u64)) @@ -2955,10 +3003,10 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `1163 + n * (1 ±0)` // Estimated: `1163 + n * (1 ±0)` - // Minimum execution time: 250_360_000 picoseconds. - Weight::from_parts(252_712_722, 1163) - // Standard Error: 15 - .saturating_add(Weight::from_parts(130, 0).saturating_mul(n.into())) + // Minimum execution time: 250_601_000 picoseconds. + Weight::from_parts(253_618_803, 1163) + // Standard Error: 18 + .saturating_add(Weight::from_parts(74, 0).saturating_mul(n.into())) .saturating_add(RocksDbWeight::get().reads(7_u64)) .saturating_add(RocksDbWeight::get().writes(4_u64)) .saturating_add(Weight::from_parts(0, 1).saturating_mul(n.into())) @@ -2970,10 +3018,10 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `835 + r * (296 ±0)` // Estimated: `840 + r * (297 ±0)` - // Minimum execution time: 235_613_000 picoseconds. - Weight::from_parts(150_213_792, 840) - // Standard Error: 8_617 - .saturating_add(Weight::from_parts(4_962_874, 0).saturating_mul(r.into())) + // Minimum execution time: 236_194_000 picoseconds. + Weight::from_parts(152_630_909, 840) + // Standard Error: 8_573 + .saturating_add(Weight::from_parts(4_896_099, 0).saturating_mul(r.into())) .saturating_add(RocksDbWeight::get().reads(6_u64)) .saturating_add(RocksDbWeight::get().reads((1_u64).saturating_mul(r.into()))) .saturating_add(RocksDbWeight::get().writes(3_u64)) @@ -2986,10 +3034,10 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `1179 + n * (1 ±0)` // Estimated: `1179 + n * (1 ±0)` - // Minimum execution time: 249_137_000 picoseconds. - Weight::from_parts(253_529_386, 1179) - // Standard Error: 41 - .saturating_add(Weight::from_parts(612, 0).saturating_mul(n.into())) + // Minimum execution time: 250_989_000 picoseconds. + Weight::from_parts(260_170_747, 1179) + // Standard Error: 162 + .saturating_add(Weight::from_parts(224, 0).saturating_mul(n.into())) .saturating_add(RocksDbWeight::get().reads(7_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) .saturating_add(Weight::from_parts(0, 1).saturating_mul(n.into())) @@ -3001,10 +3049,10 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `856 + r * (288 ±0)` // Estimated: `857 + r * (289 ±0)` - // Minimum execution time: 235_659_000 picoseconds. - Weight::from_parts(158_846_683, 857) - // Standard Error: 7_806 - .saturating_add(Weight::from_parts(4_728_537, 0).saturating_mul(r.into())) + // Minimum execution time: 236_574_000 picoseconds. + Weight::from_parts(155_573_955, 857) + // Standard Error: 10_270 + .saturating_add(Weight::from_parts(4_667_644, 0).saturating_mul(r.into())) .saturating_add(RocksDbWeight::get().reads(6_u64)) .saturating_add(RocksDbWeight::get().reads((1_u64).saturating_mul(r.into()))) .saturating_add(RocksDbWeight::get().writes(3_u64)) @@ -3017,10 +3065,10 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `1166 + n * (1 ±0)` // Estimated: `1166 + n * (1 ±0)` - // Minimum execution time: 248_553_000 picoseconds. - Weight::from_parts(250_703_269, 1166) - // Standard Error: 17 - .saturating_add(Weight::from_parts(163, 0).saturating_mul(n.into())) + // Minimum execution time: 249_502_000 picoseconds. + Weight::from_parts(251_496_311, 1166) + // Standard Error: 26 + .saturating_add(Weight::from_parts(186, 0).saturating_mul(n.into())) .saturating_add(RocksDbWeight::get().reads(7_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) .saturating_add(Weight::from_parts(0, 1).saturating_mul(n.into())) @@ -3032,10 +3080,10 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `829 + r * (296 ±0)` // Estimated: `836 + r * (297 ±0)` - // Minimum execution time: 236_431_000 picoseconds. - Weight::from_parts(131_745_419, 836) - // Standard Error: 10_161 - .saturating_add(Weight::from_parts(6_182_174, 0).saturating_mul(r.into())) + // Minimum execution time: 236_030_000 picoseconds. + Weight::from_parts(128_595_856, 836) + // Standard Error: 10_530 + .saturating_add(Weight::from_parts(6_072_638, 0).saturating_mul(r.into())) .saturating_add(RocksDbWeight::get().reads(6_u64)) .saturating_add(RocksDbWeight::get().reads((1_u64).saturating_mul(r.into()))) .saturating_add(RocksDbWeight::get().writes(3_u64)) @@ -3049,10 +3097,10 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `1180 + n * (1 ±0)` // Estimated: `1180 + n * (1 ±0)` - // Minimum execution time: 252_551_000 picoseconds. - Weight::from_parts(254_517_030, 1180) - // Standard Error: 16 - .saturating_add(Weight::from_parts(712, 0).saturating_mul(n.into())) + // Minimum execution time: 254_027_000 picoseconds. + Weight::from_parts(260_739_475, 1180) + // Standard Error: 115 + .saturating_add(Weight::from_parts(330, 0).saturating_mul(n.into())) .saturating_add(RocksDbWeight::get().reads(7_u64)) .saturating_add(RocksDbWeight::get().writes(4_u64)) .saturating_add(Weight::from_parts(0, 1).saturating_mul(n.into())) @@ -3072,10 +3120,10 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `1373 + r * (45 ±0)` // Estimated: `26753 + r * (2700 ±0)` - // Minimum execution time: 236_663_000 picoseconds. - Weight::from_parts(237_485_000, 26753) - // Standard Error: 42_414 - .saturating_add(Weight::from_parts(36_849_514, 0).saturating_mul(r.into())) + // Minimum execution time: 237_634_000 picoseconds. + Weight::from_parts(238_177_000, 26753) + // Standard Error: 23_320 + .saturating_add(Weight::from_parts(35_467_618, 0).saturating_mul(r.into())) .saturating_add(RocksDbWeight::get().reads(7_u64)) .saturating_add(RocksDbWeight::get().reads((1_u64).saturating_mul(r.into()))) .saturating_add(RocksDbWeight::get().writes(4_u64)) @@ -3097,10 +3145,10 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `1237 + r * (256 ±0)` // Estimated: `26028 + r * (6235 ±0)` - // Minimum execution time: 237_101_000 picoseconds. - Weight::from_parts(237_827_000, 26028) - // Standard Error: 82_878 - .saturating_add(Weight::from_parts(211_777_724, 0).saturating_mul(r.into())) + // Minimum execution time: 237_076_000 picoseconds. + Weight::from_parts(237_792_000, 26028) + // Standard Error: 91_566 + .saturating_add(Weight::from_parts(212_893_975, 0).saturating_mul(r.into())) .saturating_add(RocksDbWeight::get().reads(7_u64)) .saturating_add(RocksDbWeight::get().reads((2_u64).saturating_mul(r.into()))) .saturating_add(RocksDbWeight::get().writes(3_u64)) @@ -3122,10 +3170,10 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `0 + r * (502 ±0)` // Estimated: `21755 + r * (6329 ±3)` - // Minimum execution time: 241_213_000 picoseconds. - Weight::from_parts(241_900_000, 21755) - // Standard Error: 99_976 - .saturating_add(Weight::from_parts(207_520_077, 0).saturating_mul(r.into())) + // Minimum execution time: 236_275_000 picoseconds. + Weight::from_parts(236_849_000, 21755) + // Standard Error: 92_724 + .saturating_add(Weight::from_parts(207_159_396, 0).saturating_mul(r.into())) .saturating_add(RocksDbWeight::get().reads(6_u64)) .saturating_add(RocksDbWeight::get().reads((2_u64).saturating_mul(r.into()))) .saturating_add(RocksDbWeight::get().writes(3_u64)) @@ -3148,12 +3196,12 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `1154 + t * (204 ±0)` // Estimated: `31015 + t * (5970 ±0)` - // Minimum execution time: 414_784_000 picoseconds. - Weight::from_parts(384_902_379, 31015) - // Standard Error: 1_228_593 - .saturating_add(Weight::from_parts(33_226_901, 0).saturating_mul(t.into())) + // Minimum execution time: 412_628_000 picoseconds. + Weight::from_parts(385_108_035, 31015) + // Standard Error: 1_002_078 + .saturating_add(Weight::from_parts(31_204_678, 0).saturating_mul(t.into())) // Standard Error: 1 - .saturating_add(Weight::from_parts(601, 0).saturating_mul(c.into())) + .saturating_add(Weight::from_parts(598, 0).saturating_mul(c.into())) .saturating_add(RocksDbWeight::get().reads(9_u64)) .saturating_add(RocksDbWeight::get().reads((2_u64).saturating_mul(t.into()))) .saturating_add(RocksDbWeight::get().writes(5_u64)) @@ -3179,10 +3227,10 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `1301 + r * (254 ±0)` // Estimated: `30977 + r * (16635 ±0)` - // Minimum execution time: 236_963_000 picoseconds. - Weight::from_parts(237_711_000, 30977) - // Standard Error: 265_576 - .saturating_add(Weight::from_parts(347_359_908, 0).saturating_mul(r.into())) + // Minimum execution time: 237_161_000 picoseconds. + Weight::from_parts(237_620_000, 30977) + // Standard Error: 258_036 + .saturating_add(Weight::from_parts(344_869_637, 0).saturating_mul(r.into())) .saturating_add(RocksDbWeight::get().reads(8_u64)) .saturating_add(RocksDbWeight::get().reads((6_u64).saturating_mul(r.into()))) .saturating_add(RocksDbWeight::get().writes(6_u64)) @@ -3210,14 +3258,14 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `1071 + t * (187 ±0)` // Estimated: `42684 + t * (3588 ±2)` - // Minimum execution time: 1_615_191_000 picoseconds. - Weight::from_parts(337_408_450, 42684) - // Standard Error: 4_581_951 - .saturating_add(Weight::from_parts(115_730_776, 0).saturating_mul(t.into())) + // Minimum execution time: 1_613_172_000 picoseconds. + Weight::from_parts(326_952_137, 42684) + // Standard Error: 4_738_925 + .saturating_add(Weight::from_parts(113_657_996, 0).saturating_mul(t.into())) // Standard Error: 7 - .saturating_add(Weight::from_parts(1_171, 0).saturating_mul(i.into())) + .saturating_add(Weight::from_parts(1_173, 0).saturating_mul(i.into())) // Standard Error: 7 - .saturating_add(Weight::from_parts(1_346, 0).saturating_mul(s.into())) + .saturating_add(Weight::from_parts(1_347, 0).saturating_mul(s.into())) .saturating_add(RocksDbWeight::get().reads(13_u64)) .saturating_add(RocksDbWeight::get().reads((1_u64).saturating_mul(t.into()))) .saturating_add(RocksDbWeight::get().writes(10_u64)) @@ -3239,10 +3287,10 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `777 + r * (8 ±0)` // Estimated: `21710 + r * (40 ±0)` - // Minimum execution time: 233_601_000 picoseconds. - Weight::from_parts(246_594_905, 21710) - // Standard Error: 2_840 - .saturating_add(Weight::from_parts(578_751, 0).saturating_mul(r.into())) + // Minimum execution time: 233_679_000 picoseconds. + Weight::from_parts(238_638_820, 21710) + // Standard Error: 681 + .saturating_add(Weight::from_parts(573_320, 0).saturating_mul(r.into())) .saturating_add(RocksDbWeight::get().reads(6_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) .saturating_add(Weight::from_parts(0, 40).saturating_mul(r.into())) @@ -3262,10 +3310,10 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `785` // Estimated: `21745` - // Minimum execution time: 233_735_000 picoseconds. - Weight::from_parts(243_432_330, 21745) - // Standard Error: 3 - .saturating_add(Weight::from_parts(3_927, 0).saturating_mul(n.into())) + // Minimum execution time: 235_992_000 picoseconds. + Weight::from_parts(219_924_252, 21745) + // Standard Error: 5 + .saturating_add(Weight::from_parts(3_978, 0).saturating_mul(n.into())) .saturating_add(RocksDbWeight::get().reads(6_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) } @@ -3284,10 +3332,10 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `779 + r * (8 ±0)` // Estimated: `21725 + r * (40 ±0)` - // Minimum execution time: 232_173_000 picoseconds. - Weight::from_parts(239_806_011, 21725) - // Standard Error: 1_530 - .saturating_add(Weight::from_parts(749_641, 0).saturating_mul(r.into())) + // Minimum execution time: 240_661_000 picoseconds. + Weight::from_parts(238_809_422, 21725) + // Standard Error: 820 + .saturating_add(Weight::from_parts(741_326, 0).saturating_mul(r.into())) .saturating_add(RocksDbWeight::get().reads(6_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) .saturating_add(Weight::from_parts(0, 40).saturating_mul(r.into())) @@ -3307,10 +3355,10 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `787` // Estimated: `21765` - // Minimum execution time: 235_046_000 picoseconds. - Weight::from_parts(229_500_792, 21765) + // Minimum execution time: 235_136_000 picoseconds. + Weight::from_parts(228_678_487, 21765) // Standard Error: 2 - .saturating_add(Weight::from_parts(3_166, 0).saturating_mul(n.into())) + .saturating_add(Weight::from_parts(3_164, 0).saturating_mul(n.into())) .saturating_add(RocksDbWeight::get().reads(6_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) } @@ -3329,10 +3377,10 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `779 + r * (8 ±0)` // Estimated: `21740 + r * (40 ±0)` - // Minimum execution time: 231_708_000 picoseconds. - Weight::from_parts(235_347_566, 21740) - // Standard Error: 987 - .saturating_add(Weight::from_parts(428_819, 0).saturating_mul(r.into())) + // Minimum execution time: 233_364_000 picoseconds. + Weight::from_parts(243_294_285, 21740) + // Standard Error: 1_607 + .saturating_add(Weight::from_parts(413_186, 0).saturating_mul(r.into())) .saturating_add(RocksDbWeight::get().reads(6_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) .saturating_add(Weight::from_parts(0, 40).saturating_mul(r.into())) @@ -3352,10 +3400,10 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `787` // Estimated: `21785` - // Minimum execution time: 234_068_000 picoseconds. - Weight::from_parts(226_519_852, 21785) + // Minimum execution time: 234_315_000 picoseconds. + Weight::from_parts(225_569_537, 21785) // Standard Error: 1 - .saturating_add(Weight::from_parts(916, 0).saturating_mul(n.into())) + .saturating_add(Weight::from_parts(919, 0).saturating_mul(n.into())) .saturating_add(RocksDbWeight::get().reads(6_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) } @@ -3374,10 +3422,10 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `779 + r * (8 ±0)` // Estimated: `21745 + r * (40 ±0)` - // Minimum execution time: 231_872_000 picoseconds. - Weight::from_parts(236_694_763, 21745) - // Standard Error: 870 - .saturating_add(Weight::from_parts(420_853, 0).saturating_mul(r.into())) + // Minimum execution time: 233_288_000 picoseconds. + Weight::from_parts(239_289_154, 21745) + // Standard Error: 612 + .saturating_add(Weight::from_parts(414_275, 0).saturating_mul(r.into())) .saturating_add(RocksDbWeight::get().reads(6_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) .saturating_add(Weight::from_parts(0, 40).saturating_mul(r.into())) @@ -3397,10 +3445,10 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `787` // Estimated: `21755` - // Minimum execution time: 233_707_000 picoseconds. - Weight::from_parts(226_312_559, 21755) - // Standard Error: 2 - .saturating_add(Weight::from_parts(924, 0).saturating_mul(n.into())) + // Minimum execution time: 233_686_000 picoseconds. + Weight::from_parts(227_406_694, 21755) + // Standard Error: 1 + .saturating_add(Weight::from_parts(919, 0).saturating_mul(n.into())) .saturating_add(RocksDbWeight::get().reads(6_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) } @@ -3414,15 +3462,61 @@ impl WeightInfo for () { /// Proof: Timestamp Now (max_values: Some(1), max_size: Some(8), added: 503, mode: Measured) /// Storage: System EventTopics (r:2 w:2) /// Proof Skipped: System EventTopics (max_values: None, max_size: None, mode: Measured) + /// The range of component `n` is `[0, 125697]`. + fn seal_sr25519_verify_per_byte(n: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `916 + n * (1 ±0)` + // Estimated: `22375 + n * (5 ±0)` + // Minimum execution time: 287_258_000 picoseconds. + Weight::from_parts(290_941_609, 22375) + // Standard Error: 8 + .saturating_add(Weight::from_parts(4_785, 0).saturating_mul(n.into())) + .saturating_add(RocksDbWeight::get().reads(6_u64)) + .saturating_add(RocksDbWeight::get().writes(3_u64)) + .saturating_add(Weight::from_parts(0, 5).saturating_mul(n.into())) + } + /// Storage: System Account (r:1 w:0) + /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: Measured) + /// Storage: Contracts ContractInfoOf (r:1 w:1) + /// Proof: Contracts ContractInfoOf (max_values: None, max_size: Some(290), added: 2765, mode: Measured) + /// Storage: Contracts CodeStorage (r:1 w:0) + /// Proof: Contracts CodeStorage (max_values: None, max_size: Some(126001), added: 128476, mode: Measured) + /// Storage: Timestamp Now (r:1 w:0) + /// Proof: Timestamp Now (max_values: Some(1), max_size: Some(8), added: 503, mode: Measured) + /// Storage: System EventTopics (r:2 w:2) + /// Proof Skipped: System EventTopics (max_values: None, max_size: None, mode: Measured) + /// The range of component `r` is `[0, 160]`. + fn seal_sr25519_verify(r: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `728 + r * (112 ±0)` + // Estimated: `21455 + r * (560 ±0)` + // Minimum execution time: 238_340_000 picoseconds. + Weight::from_parts(246_009_851, 21455) + // Standard Error: 21_677 + .saturating_add(Weight::from_parts(48_139_487, 0).saturating_mul(r.into())) + .saturating_add(RocksDbWeight::get().reads(6_u64)) + .saturating_add(RocksDbWeight::get().writes(3_u64)) + .saturating_add(Weight::from_parts(0, 560).saturating_mul(r.into())) + } + /// Storage: System Account (r:1 w:0) + /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: Measured) + /// Storage: Contracts ContractInfoOf (r:1 w:1) + /// Proof: Contracts ContractInfoOf (max_values: None, max_size: Some(290), added: 2765, mode: Measured) + /// Storage: Contracts CodeStorage (r:1 w:0) + /// Proof: Contracts CodeStorage (max_values: None, max_size: Some(126001), added: 128476, mode: Measured) + /// Storage: Timestamp Now (r:1 w:0) + /// Proof: Timestamp Now (max_values: Some(1), max_size: Some(8), added: 503, mode: Measured) + /// Storage: System EventTopics (r:2 w:2) + /// Proof Skipped: System EventTopics (max_values: None, max_size: None, mode: Measured) /// The range of component `r` is `[0, 160]`. fn seal_ecdsa_recover(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `822 + r * (76 ±0)` // Estimated: `21705 + r * (385 ±0)` - // Minimum execution time: 234_962_000 picoseconds. - Weight::from_parts(252_611_292, 21705) - // Standard Error: 20_134 - .saturating_add(Weight::from_parts(37_745_358, 0).saturating_mul(r.into())) + // Minimum execution time: 236_244_000 picoseconds. + Weight::from_parts(253_749_242, 21705) + // Standard Error: 19_216 + .saturating_add(Weight::from_parts(37_706_782, 0).saturating_mul(r.into())) .saturating_add(RocksDbWeight::get().reads(6_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) .saturating_add(Weight::from_parts(0, 385).saturating_mul(r.into())) @@ -3442,10 +3536,10 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `792 + r * (42 ±0)` // Estimated: `21775 + r * (210 ±0)` - // Minimum execution time: 234_869_000 picoseconds. - Weight::from_parts(240_188_331, 21775) - // Standard Error: 9_910 - .saturating_add(Weight::from_parts(9_332_432, 0).saturating_mul(r.into())) + // Minimum execution time: 235_770_000 picoseconds. + Weight::from_parts(230_780_347, 21775) + // Standard Error: 12_015 + .saturating_add(Weight::from_parts(9_584_947, 0).saturating_mul(r.into())) .saturating_add(RocksDbWeight::get().reads(6_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) .saturating_add(Weight::from_parts(0, 210).saturating_mul(r.into())) @@ -3466,11 +3560,11 @@ impl WeightInfo for () { fn seal_set_code_hash(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0 + r * (964 ±0)` - // Estimated: `29920 + r * (11544 ±10)` - // Minimum execution time: 235_036_000 picoseconds. - Weight::from_parts(235_538_000, 29920) - // Standard Error: 47_360 - .saturating_add(Weight::from_parts(22_113_144, 0).saturating_mul(r.into())) + // Estimated: `29920 + r * (11544 ±7)` + // Minimum execution time: 235_857_000 picoseconds. + Weight::from_parts(236_482_000, 29920) + // Standard Error: 47_818 + .saturating_add(Weight::from_parts(21_765_273, 0).saturating_mul(r.into())) .saturating_add(RocksDbWeight::get().reads(6_u64)) .saturating_add(RocksDbWeight::get().reads((3_u64).saturating_mul(r.into()))) .saturating_add(RocksDbWeight::get().writes(3_u64)) @@ -3492,10 +3586,10 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `773 + r * (3 ±0)` // Estimated: `21735 + r * (15 ±0)` - // Minimum execution time: 237_884_000 picoseconds. - Weight::from_parts(243_315_095, 21735) - // Standard Error: 560 - .saturating_add(Weight::from_parts(162_206, 0).saturating_mul(r.into())) + // Minimum execution time: 235_388_000 picoseconds. + Weight::from_parts(240_149_512, 21735) + // Standard Error: 555 + .saturating_add(Weight::from_parts(162_666, 0).saturating_mul(r.into())) .saturating_add(RocksDbWeight::get().reads(6_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) .saturating_add(Weight::from_parts(0, 15).saturating_mul(r.into())) @@ -3515,10 +3609,10 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `1975 + r * (39 ±0)` // Estimated: `27145 + r * (200 ±0)` - // Minimum execution time: 239_402_000 picoseconds. - Weight::from_parts(267_214_783, 27145) - // Standard Error: 1_166 - .saturating_add(Weight::from_parts(261_915, 0).saturating_mul(r.into())) + // Minimum execution time: 237_485_000 picoseconds. + Weight::from_parts(268_044_053, 27145) + // Standard Error: 1_147 + .saturating_add(Weight::from_parts(260_572, 0).saturating_mul(r.into())) .saturating_add(RocksDbWeight::get().reads(6_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) .saturating_add(Weight::from_parts(0, 200).saturating_mul(r.into())) @@ -3540,10 +3634,10 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `776 + r * (3 ±0)` // Estimated: `24004 + r * (18 ±0)` - // Minimum execution time: 233_153_000 picoseconds. - Weight::from_parts(238_999_965, 24004) - // Standard Error: 291 - .saturating_add(Weight::from_parts(143_971, 0).saturating_mul(r.into())) + // Minimum execution time: 234_123_000 picoseconds. + Weight::from_parts(245_872_832, 24004) + // Standard Error: 943 + .saturating_add(Weight::from_parts(136_460, 0).saturating_mul(r.into())) .saturating_add(RocksDbWeight::get().reads(7_u64)) .saturating_add(RocksDbWeight::get().writes(4_u64)) .saturating_add(Weight::from_parts(0, 18).saturating_mul(r.into())) @@ -3553,509 +3647,509 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_706_000 picoseconds. - Weight::from_parts(1_962_558, 0) + // Minimum execution time: 1_606_000 picoseconds. + Weight::from_parts(1_845_698, 0) // Standard Error: 2 - .saturating_add(Weight::from_parts(3_000, 0).saturating_mul(r.into())) + .saturating_add(Weight::from_parts(2_995, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 5000]`. fn instr_i64load(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_870_000 picoseconds. - Weight::from_parts(2_481_243, 0) + // Minimum execution time: 1_711_000 picoseconds. + Weight::from_parts(2_281_581, 0) // Standard Error: 4 - .saturating_add(Weight::from_parts(6_346, 0).saturating_mul(r.into())) + .saturating_add(Weight::from_parts(6_354, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 5000]`. fn instr_i64store(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_830_000 picoseconds. - Weight::from_parts(2_389_658, 0) + // Minimum execution time: 1_680_000 picoseconds. + Weight::from_parts(2_269_238, 0) // Standard Error: 4 - .saturating_add(Weight::from_parts(5_997, 0).saturating_mul(r.into())) + .saturating_add(Weight::from_parts(6_014, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 5000]`. fn instr_select(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_734_000 picoseconds. - Weight::from_parts(2_170_618, 0) - // Standard Error: 4 - .saturating_add(Weight::from_parts(7_919, 0).saturating_mul(r.into())) + // Minimum execution time: 1_592_000 picoseconds. + Weight::from_parts(2_010_963, 0) + // Standard Error: 3 + .saturating_add(Weight::from_parts(7_927, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 5000]`. fn instr_if(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_661_000 picoseconds. - Weight::from_parts(1_945_771, 0) - // Standard Error: 11 - .saturating_add(Weight::from_parts(10_840, 0).saturating_mul(r.into())) + // Minimum execution time: 1_631_000 picoseconds. + Weight::from_parts(1_936_549, 0) + // Standard Error: 6 + .saturating_add(Weight::from_parts(10_426, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 5000]`. fn instr_br(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_682_000 picoseconds. - Weight::from_parts(1_970_774, 0) - // Standard Error: 11 - .saturating_add(Weight::from_parts(4_569, 0).saturating_mul(r.into())) + // Minimum execution time: 1_616_000 picoseconds. + Weight::from_parts(1_938_716, 0) + // Standard Error: 15 + .saturating_add(Weight::from_parts(4_525, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 5000]`. fn instr_br_if(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_668_000 picoseconds. - Weight::from_parts(1_227_080, 0) - // Standard Error: 76 - .saturating_add(Weight::from_parts(8_066, 0).saturating_mul(r.into())) + // Minimum execution time: 1_535_000 picoseconds. + Weight::from_parts(1_109_860, 0) + // Standard Error: 77 + .saturating_add(Weight::from_parts(7_998, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 5000]`. fn instr_br_table(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_653_000 picoseconds. - Weight::from_parts(1_394_759, 0) - // Standard Error: 39 - .saturating_add(Weight::from_parts(9_566, 0).saturating_mul(r.into())) + // Minimum execution time: 1_594_000 picoseconds. + Weight::from_parts(1_478_654, 0) + // Standard Error: 36 + .saturating_add(Weight::from_parts(9_525, 0).saturating_mul(r.into())) } /// The range of component `e` is `[1, 256]`. fn instr_br_table_per_entry(e: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_771_000 picoseconds. - Weight::from_parts(1_905_594, 0) - // Standard Error: 147 - .saturating_add(Weight::from_parts(925, 0).saturating_mul(e.into())) + // Minimum execution time: 1_730_000 picoseconds. + Weight::from_parts(1_836_342, 0) + // Standard Error: 14 + .saturating_add(Weight::from_parts(206, 0).saturating_mul(e.into())) } /// The range of component `r` is `[0, 5000]`. fn instr_call(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_863_000 picoseconds. - Weight::from_parts(2_472_635, 0) - // Standard Error: 13 - .saturating_add(Weight::from_parts(17_892, 0).saturating_mul(r.into())) + // Minimum execution time: 1_607_000 picoseconds. + Weight::from_parts(2_150_133, 0) + // Standard Error: 17 + .saturating_add(Weight::from_parts(18_630, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 5000]`. fn instr_call_indirect(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 2_013_000 picoseconds. - Weight::from_parts(3_077_100, 0) - // Standard Error: 18 - .saturating_add(Weight::from_parts(24_287, 0).saturating_mul(r.into())) + // Minimum execution time: 1_824_000 picoseconds. + Weight::from_parts(3_119_825, 0) + // Standard Error: 15 + .saturating_add(Weight::from_parts(25_304, 0).saturating_mul(r.into())) } /// The range of component `l` is `[0, 1024]`. fn instr_call_per_local(l: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_818_000 picoseconds. - Weight::from_parts(2_109_143, 0) - // Standard Error: 34 - .saturating_add(Weight::from_parts(1_249, 0).saturating_mul(l.into())) + // Minimum execution time: 1_692_000 picoseconds. + Weight::from_parts(1_927_049, 0) + // Standard Error: 14 + .saturating_add(Weight::from_parts(1_217, 0).saturating_mul(l.into())) } /// The range of component `r` is `[0, 5000]`. fn instr_local_get(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 3_083_000 picoseconds. - Weight::from_parts(3_291_328, 0) - // Standard Error: 1 - .saturating_add(Weight::from_parts(2_505, 0).saturating_mul(r.into())) + // Minimum execution time: 2_795_000 picoseconds. + Weight::from_parts(3_107_843, 0) + // Standard Error: 2 + .saturating_add(Weight::from_parts(2_445, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 5000]`. fn instr_local_set(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 2_987_000 picoseconds. - Weight::from_parts(3_276_863, 0) - // Standard Error: 2 - .saturating_add(Weight::from_parts(3_617, 0).saturating_mul(r.into())) + // Minimum execution time: 2_815_000 picoseconds. + Weight::from_parts(3_114_618, 0) + // Standard Error: 3 + .saturating_add(Weight::from_parts(3_639, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 5000]`. fn instr_local_tee(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 2_942_000 picoseconds. - Weight::from_parts(3_350_581, 0) + // Minimum execution time: 2_861_000 picoseconds. + Weight::from_parts(3_193_592, 0) // Standard Error: 3 - .saturating_add(Weight::from_parts(3_976, 0).saturating_mul(r.into())) + .saturating_add(Weight::from_parts(3_957, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 5000]`. fn instr_global_get(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_867_000 picoseconds. - Weight::from_parts(2_920_748, 0) - // Standard Error: 44 - .saturating_add(Weight::from_parts(8_229, 0).saturating_mul(r.into())) + // Minimum execution time: 1_710_000 picoseconds. + Weight::from_parts(2_148_021, 0) + // Standard Error: 3 + .saturating_add(Weight::from_parts(8_391, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 5000]`. fn instr_global_set(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_757_000 picoseconds. - Weight::from_parts(2_235_198, 0) - // Standard Error: 4 - .saturating_add(Weight::from_parts(8_815, 0).saturating_mul(r.into())) + // Minimum execution time: 1_663_000 picoseconds. + Weight::from_parts(2_170_373, 0) + // Standard Error: 3 + .saturating_add(Weight::from_parts(8_812, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 5000]`. fn instr_memory_current(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_824_000 picoseconds. - Weight::from_parts(1_941_816, 0) - // Standard Error: 86 - .saturating_add(Weight::from_parts(4_043, 0).saturating_mul(r.into())) + // Minimum execution time: 1_662_000 picoseconds. + Weight::from_parts(2_601_398, 0) + // Standard Error: 198 + .saturating_add(Weight::from_parts(3_851, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 16]`. fn instr_memory_grow(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_793_000 picoseconds. - Weight::from_parts(1_104_829, 0) - // Standard Error: 137_800 - .saturating_add(Weight::from_parts(13_336_784, 0).saturating_mul(r.into())) + // Minimum execution time: 1_603_000 picoseconds. + Weight::from_parts(332_932, 0) + // Standard Error: 137_529 + .saturating_add(Weight::from_parts(13_233_734, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 5000]`. fn instr_i64clz(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_693_000 picoseconds. - Weight::from_parts(2_037_305, 0) + // Minimum execution time: 1_613_000 picoseconds. + Weight::from_parts(1_922_391, 0) // Standard Error: 2 - .saturating_add(Weight::from_parts(4_044, 0).saturating_mul(r.into())) + .saturating_add(Weight::from_parts(3_903, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 5000]`. fn instr_i64ctz(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_751_000 picoseconds. - Weight::from_parts(2_082_016, 0) + // Minimum execution time: 1_522_000 picoseconds. + Weight::from_parts(1_920_023, 0) // Standard Error: 2 - .saturating_add(Weight::from_parts(3_767, 0).saturating_mul(r.into())) + .saturating_add(Weight::from_parts(3_876, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 5000]`. fn instr_i64popcnt(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_751_000 picoseconds. - Weight::from_parts(2_110_625, 0) - // Standard Error: 2 - .saturating_add(Weight::from_parts(3_754, 0).saturating_mul(r.into())) + // Minimum execution time: 1_590_000 picoseconds. + Weight::from_parts(1_867_406, 0) + // Standard Error: 3 + .saturating_add(Weight::from_parts(3_910, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 5000]`. fn instr_i64eqz(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_756_000 picoseconds. - Weight::from_parts(2_100_327, 0) + // Minimum execution time: 1_577_000 picoseconds. + Weight::from_parts(1_918_037, 0) // Standard Error: 2 - .saturating_add(Weight::from_parts(3_664, 0).saturating_mul(r.into())) + .saturating_add(Weight::from_parts(3_673, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 5000]`. fn instr_i64extendsi32(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_643_000 picoseconds. - Weight::from_parts(2_169_153, 0) + // Minimum execution time: 1_589_000 picoseconds. + Weight::from_parts(1_917_901, 0) // Standard Error: 3 - .saturating_add(Weight::from_parts(3_961, 0).saturating_mul(r.into())) + .saturating_add(Weight::from_parts(3_956, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 5000]`. fn instr_i64extendui32(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_704_000 picoseconds. - Weight::from_parts(2_049_172, 0) + // Minimum execution time: 1_573_000 picoseconds. + Weight::from_parts(1_902_324, 0) // Standard Error: 2 - .saturating_add(Weight::from_parts(3_833, 0).saturating_mul(r.into())) + .saturating_add(Weight::from_parts(3_865, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 5000]`. fn instr_i32wrapi64(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_726_000 picoseconds. - Weight::from_parts(2_064_387, 0) + // Minimum execution time: 1_586_000 picoseconds. + Weight::from_parts(1_953_738, 0) // Standard Error: 2 - .saturating_add(Weight::from_parts(3_745, 0).saturating_mul(r.into())) + .saturating_add(Weight::from_parts(3_838, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 5000]`. fn instr_i64eq(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_696_000 picoseconds. - Weight::from_parts(2_426_905, 0) - // Standard Error: 56 - .saturating_add(Weight::from_parts(5_915, 0).saturating_mul(r.into())) + // Minimum execution time: 1_598_000 picoseconds. + Weight::from_parts(1_912_180, 0) + // Standard Error: 2 + .saturating_add(Weight::from_parts(5_994, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 5000]`. fn instr_i64ne(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_617_000 picoseconds. - Weight::from_parts(2_035_161, 0) - // Standard Error: 94 - .saturating_add(Weight::from_parts(6_073, 0).saturating_mul(r.into())) + // Minimum execution time: 1_573_000 picoseconds. + Weight::from_parts(1_943_725, 0) + // Standard Error: 2 + .saturating_add(Weight::from_parts(5_998, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 5000]`. fn instr_i64lts(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_756_000 picoseconds. - Weight::from_parts(2_098_926, 0) + // Minimum execution time: 1_584_000 picoseconds. + Weight::from_parts(1_949_209, 0) // Standard Error: 2 - .saturating_add(Weight::from_parts(6_002, 0).saturating_mul(r.into())) + .saturating_add(Weight::from_parts(5_991, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 5000]`. fn instr_i64ltu(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_706_000 picoseconds. - Weight::from_parts(2_257_972, 0) - // Standard Error: 23 - .saturating_add(Weight::from_parts(5_982, 0).saturating_mul(r.into())) + // Minimum execution time: 1_576_000 picoseconds. + Weight::from_parts(1_927_466, 0) + // Standard Error: 2 + .saturating_add(Weight::from_parts(6_005, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 5000]`. fn instr_i64gts(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_761_000 picoseconds. - Weight::from_parts(2_114_141, 0) - // Standard Error: 2 - .saturating_add(Weight::from_parts(5_815, 0).saturating_mul(r.into())) + // Minimum execution time: 1_566_000 picoseconds. + Weight::from_parts(2_004_312, 0) + // Standard Error: 3 + .saturating_add(Weight::from_parts(5_799, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 5000]`. fn instr_i64gtu(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_700_000 picoseconds. - Weight::from_parts(2_053_201, 0) - // Standard Error: 2 - .saturating_add(Weight::from_parts(6_137, 0).saturating_mul(r.into())) + // Minimum execution time: 1_538_000 picoseconds. + Weight::from_parts(1_922_703, 0) + // Standard Error: 9 + .saturating_add(Weight::from_parts(6_129, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 5000]`. fn instr_i64les(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_705_000 picoseconds. - Weight::from_parts(2_101_782, 0) + // Minimum execution time: 1_575_000 picoseconds. + Weight::from_parts(1_978_271, 0) // Standard Error: 3 - .saturating_add(Weight::from_parts(6_014, 0).saturating_mul(r.into())) + .saturating_add(Weight::from_parts(5_991, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 5000]`. fn instr_i64leu(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_856_000 picoseconds. - Weight::from_parts(2_149_707, 0) - // Standard Error: 3 - .saturating_add(Weight::from_parts(6_086, 0).saturating_mul(r.into())) + // Minimum execution time: 1_572_000 picoseconds. + Weight::from_parts(4_028_578, 0) + // Standard Error: 263 + .saturating_add(Weight::from_parts(5_784, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 5000]`. fn instr_i64ges(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_739_000 picoseconds. - Weight::from_parts(2_143_216, 0) + // Minimum execution time: 1_615_000 picoseconds. + Weight::from_parts(1_962_065, 0) // Standard Error: 3 - .saturating_add(Weight::from_parts(5_934, 0).saturating_mul(r.into())) + .saturating_add(Weight::from_parts(5_945, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 5000]`. fn instr_i64geu(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_716_000 picoseconds. - Weight::from_parts(2_065_762, 0) - // Standard Error: 3 - .saturating_add(Weight::from_parts(6_009, 0).saturating_mul(r.into())) + // Minimum execution time: 1_578_000 picoseconds. + Weight::from_parts(1_909_721, 0) + // Standard Error: 2 + .saturating_add(Weight::from_parts(5_993, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 5000]`. fn instr_i64add(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_664_000 picoseconds. - Weight::from_parts(3_062_283, 0) - // Standard Error: 94 - .saturating_add(Weight::from_parts(5_645, 0).saturating_mul(r.into())) + // Minimum execution time: 1_609_000 picoseconds. + Weight::from_parts(1_926_472, 0) + // Standard Error: 3 + .saturating_add(Weight::from_parts(5_888, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 5000]`. fn instr_i64sub(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_671_000 picoseconds. - Weight::from_parts(2_011_145, 0) - // Standard Error: 44 - .saturating_add(Weight::from_parts(6_220, 0).saturating_mul(r.into())) + // Minimum execution time: 1_620_000 picoseconds. + Weight::from_parts(2_875_196, 0) + // Standard Error: 177 + .saturating_add(Weight::from_parts(5_990, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 5000]`. fn instr_i64mul(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_759_000 picoseconds. - Weight::from_parts(2_095_420, 0) - // Standard Error: 4 - .saturating_add(Weight::from_parts(5_723, 0).saturating_mul(r.into())) + // Minimum execution time: 1_570_000 picoseconds. + Weight::from_parts(2_155_483, 0) + // Standard Error: 83 + .saturating_add(Weight::from_parts(5_675, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 5000]`. fn instr_i64divs(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_672_000 picoseconds. - Weight::from_parts(2_184_044, 0) + // Minimum execution time: 1_558_000 picoseconds. + Weight::from_parts(2_108_263, 0) // Standard Error: 5 - .saturating_add(Weight::from_parts(11_782, 0).saturating_mul(r.into())) + .saturating_add(Weight::from_parts(11_755, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 5000]`. fn instr_i64divu(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_752_000 picoseconds. - Weight::from_parts(2_276_209, 0) - // Standard Error: 5 - .saturating_add(Weight::from_parts(10_513, 0).saturating_mul(r.into())) + // Minimum execution time: 1_568_000 picoseconds. + Weight::from_parts(1_606_397, 0) + // Standard Error: 51 + .saturating_add(Weight::from_parts(11_011, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 5000]`. fn instr_i64rems(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_711_000 picoseconds. - Weight::from_parts(2_720_989, 0) + // Minimum execution time: 1_630_000 picoseconds. + Weight::from_parts(2_872_487, 0) // Standard Error: 24 - .saturating_add(Weight::from_parts(11_884, 0).saturating_mul(r.into())) + .saturating_add(Weight::from_parts(11_761, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 5000]`. fn instr_i64remu(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_713_000 picoseconds. - Weight::from_parts(2_091_403, 0) - // Standard Error: 3 - .saturating_add(Weight::from_parts(10_628, 0).saturating_mul(r.into())) + // Minimum execution time: 1_575_000 picoseconds. + Weight::from_parts(2_385_398, 0) + // Standard Error: 45 + .saturating_add(Weight::from_parts(10_650, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 5000]`. fn instr_i64and(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_704_000 picoseconds. - Weight::from_parts(2_054_652, 0) + // Minimum execution time: 1_622_000 picoseconds. + Weight::from_parts(1_957_246, 0) // Standard Error: 2 - .saturating_add(Weight::from_parts(5_672, 0).saturating_mul(r.into())) + .saturating_add(Weight::from_parts(5_659, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 5000]`. fn instr_i64or(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_743_000 picoseconds. - Weight::from_parts(2_032_806, 0) - // Standard Error: 19 - .saturating_add(Weight::from_parts(5_795, 0).saturating_mul(r.into())) + // Minimum execution time: 1_551_000 picoseconds. + Weight::from_parts(1_897_168, 0) + // Standard Error: 3 + .saturating_add(Weight::from_parts(5_774, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 5000]`. fn instr_i64xor(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_667_000 picoseconds. - Weight::from_parts(2_031_702, 0) - // Standard Error: 5 - .saturating_add(Weight::from_parts(5_923, 0).saturating_mul(r.into())) + // Minimum execution time: 1_549_000 picoseconds. + Weight::from_parts(1_915_938, 0) + // Standard Error: 3 + .saturating_add(Weight::from_parts(5_915, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 5000]`. fn instr_i64shl(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_735_000 picoseconds. - Weight::from_parts(2_946_634, 0) - // Standard Error: 54 - .saturating_add(Weight::from_parts(5_685, 0).saturating_mul(r.into())) + // Minimum execution time: 1_617_000 picoseconds. + Weight::from_parts(1_932_618, 0) + // Standard Error: 3 + .saturating_add(Weight::from_parts(5_854, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 5000]`. fn instr_i64shrs(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_652_000 picoseconds. - Weight::from_parts(2_023_049, 0) - // Standard Error: 4 - .saturating_add(Weight::from_parts(6_146, 0).saturating_mul(r.into())) + // Minimum execution time: 1_617_000 picoseconds. + Weight::from_parts(1_937_566, 0) + // Standard Error: 3 + .saturating_add(Weight::from_parts(6_134, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 5000]`. fn instr_i64shru(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_654_000 picoseconds. - Weight::from_parts(2_148_951, 0) + // Minimum execution time: 1_584_000 picoseconds. + Weight::from_parts(1_980_681, 0) // Standard Error: 3 - .saturating_add(Weight::from_parts(5_869, 0).saturating_mul(r.into())) + .saturating_add(Weight::from_parts(5_842, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 5000]`. fn instr_i64rotl(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_730_000 picoseconds. - Weight::from_parts(2_130_543, 0) - // Standard Error: 3 - .saturating_add(Weight::from_parts(5_999, 0).saturating_mul(r.into())) + // Minimum execution time: 1_542_000 picoseconds. + Weight::from_parts(2_131_567, 0) + // Standard Error: 57 + .saturating_add(Weight::from_parts(6_010, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 5000]`. fn instr_i64rotr(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_728_000 picoseconds. - Weight::from_parts(2_172_886, 0) - // Standard Error: 3 - .saturating_add(Weight::from_parts(5_843, 0).saturating_mul(r.into())) + // Minimum execution time: 1_609_000 picoseconds. + Weight::from_parts(1_643_214, 0) + // Standard Error: 91 + .saturating_add(Weight::from_parts(6_062, 0).saturating_mul(r.into())) } } From f0d3bbbbc3da6c5455dea0c7cb18f9d0862b5558 Mon Sep 17 00:00:00 2001 From: Sam Johnson Date: Wed, 12 Apr 2023 14:42:22 -0400 Subject: [PATCH 368/558] Globally upgrade to syn 2.x and latest quote and proc_macro2 1x versions (#13846) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * globally upgrade quote to latest 1.0.x (1.0.26) * globally upgrade syn to final 1.0.x version (1.0.109) * globally upgrade proc-macro2 to 1.0.56 * upgrade to syn v2.0.13 and fix everything except NestedMeta * fix parse nested metadata code in decl_runtime_apis.rs * Port more stuff to syn 2.0 * Make the rest compile * Ignore error * update to syn 2.0.14 --------- Co-authored-by: Bastian Köcher --- Cargo.lock | 36 +++--- bin/node/cli/tests/telemetry.rs | 5 +- client/chain-spec/derive/Cargo.toml | 6 +- client/chain-spec/derive/src/impls.rs | 2 +- client/consensus/babe/src/lib.rs | 5 +- client/db/src/pinned_blocks_cache.rs | 5 +- client/service/test/src/client/mod.rs | 5 +- client/state-db/src/lib.rs | 10 +- client/tracing/proc-macro/Cargo.toml | 6 +- frame/contracts/proc-macro/Cargo.toml | 6 +- frame/contracts/proc-macro/src/lib.rs | 10 +- .../solution-type/Cargo.toml | 6 +- .../solution-type/src/lib.rs | 4 +- frame/nfts/src/tests.rs | 6 +- frame/scheduler/src/migration.rs | 10 +- frame/staking/reward-curve/Cargo.toml | 6 +- frame/support/procedural/Cargo.toml | 6 +- frame/support/procedural/src/benchmark.rs | 90 ++++++------- .../procedural/src/construct_runtime/mod.rs | 2 +- .../procedural/src/construct_runtime/parse.rs | 31 ++--- .../procedural/src/default_no_bound.rs | 4 +- .../procedural/src/pallet/expand/call.rs | 10 +- .../procedural/src/pallet/expand/constants.rs | 2 +- .../src/pallet/expand/documentation.rs | 119 ++++++------------ .../procedural/src/pallet/expand/storage.rs | 25 ++-- .../procedural/src/pallet/parse/call.rs | 6 +- .../procedural/src/pallet/parse/composite.rs | 25 ++-- .../procedural/src/pallet/parse/config.rs | 36 ++++-- .../procedural/src/pallet/parse/error.rs | 2 +- .../src/pallet/parse/extra_constants.rs | 4 +- .../procedural/src/pallet/parse/helper.rs | 9 +- .../procedural/src/pallet/parse/hooks.rs | 2 +- .../procedural/src/pallet/parse/storage.rs | 12 +- .../procedural/src/pallet/parse/type_value.rs | 6 +- frame/support/procedural/src/pallet_error.rs | 79 +++++------- .../genesis_config/genesis_config_def.rs | 7 +- frame/support/procedural/src/storage/mod.rs | 4 +- .../src/storage/print_pallet_upgrade.rs | 14 +-- frame/support/procedural/tools/Cargo.toml | 6 +- .../procedural/tools/derive/Cargo.toml | 6 +- frame/support/procedural/tools/src/lib.rs | 13 +- .../pallet_ui/pallet_doc_arg_non_path.stderr | 2 +- .../tests/pallet_ui/pallet_doc_empty.stderr | 2 +- .../pallet_ui/pallet_doc_invalid_arg.stderr | 2 +- .../pallet_ui/pallet_doc_multiple_args.stderr | 2 +- ...result_query_parenthesized_generics.stderr | 4 +- primitives/api/proc-macro/Cargo.toml | 6 +- .../api/proc-macro/src/decl_runtime_apis.rs | 91 ++++++-------- .../api/proc-macro/src/impl_runtime_apis.rs | 6 +- .../proc-macro/src/mock_impl_runtime_apis.rs | 8 +- .../api/proc-macro/src/runtime_metadata.rs | 4 +- primitives/api/proc-macro/src/utils.rs | 14 ++- primitives/arithmetic/src/per_things.rs | 10 +- primitives/core/hashing/proc-macro/Cargo.toml | 6 +- primitives/debug-derive/Cargo.toml | 6 +- .../runtime-interface/proc-macro/Cargo.toml | 6 +- .../bare_function_interface.rs | 15 ++- .../host_function_interface.rs | 6 +- .../src/runtime_interface/trait_decl_impl.rs | 18 +-- .../runtime-interface/proc-macro/src/utils.rs | 26 ++-- primitives/version/proc-macro/Cargo.toml | 6 +- test-utils/derive/Cargo.toml | 6 +- 62 files changed, 409 insertions(+), 485 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 6a1f29aa2..b0a96af54 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2351,7 +2351,7 @@ dependencies = [ "quote", "scale-info", "sp-arithmetic", - "syn 1.0.109", + "syn 2.0.14", "trybuild", ] @@ -2489,7 +2489,7 @@ dependencies = [ "proc-macro-warning", "proc-macro2", "quote", - "syn 1.0.109", + "syn 2.0.14", ] [[package]] @@ -2500,7 +2500,7 @@ dependencies = [ "proc-macro-crate", "proc-macro2", "quote", - "syn 1.0.109", + "syn 2.0.14", ] [[package]] @@ -2509,7 +2509,7 @@ version = "3.0.0" dependencies = [ "proc-macro2", "quote", - "syn 1.0.109", + "syn 2.0.14", ] [[package]] @@ -2724,7 +2724,7 @@ checksum = "89ca545a94061b6365f2c7355b4b32bd20df3ff95f02da9329b34ccc3bd6ee72" dependencies = [ "proc-macro2", "quote", - "syn 2.0.13", + "syn 2.0.14", ] [[package]] @@ -5912,7 +5912,7 @@ version = "4.0.0-dev" dependencies = [ "proc-macro2", "quote", - "syn 1.0.109", + "syn 2.0.14", ] [[package]] @@ -6795,7 +6795,7 @@ dependencies = [ "proc-macro2", "quote", "sp-runtime", - "syn 1.0.109", + "syn 2.0.14", ] [[package]] @@ -8401,7 +8401,7 @@ dependencies = [ "proc-macro-crate", "proc-macro2", "quote", - "syn 1.0.109", + "syn 2.0.14", ] [[package]] @@ -9630,7 +9630,7 @@ dependencies = [ "proc-macro-crate", "proc-macro2", "quote", - "syn 1.0.109", + "syn 2.0.14", ] [[package]] @@ -10181,7 +10181,7 @@ dependencies = [ "proc-macro-crate", "proc-macro2", "quote", - "syn 1.0.109", + "syn 2.0.14", ] [[package]] @@ -10490,7 +10490,7 @@ dependencies = [ "proc-macro2", "quote", "sp-core-hashing", - "syn 1.0.109", + "syn 2.0.14", ] [[package]] @@ -10507,7 +10507,7 @@ version = "5.0.0" dependencies = [ "proc-macro2", "quote", - "syn 1.0.109", + "syn 2.0.14", ] [[package]] @@ -10737,7 +10737,7 @@ dependencies = [ "proc-macro-crate", "proc-macro2", "quote", - "syn 1.0.109", + "syn 2.0.14", ] [[package]] @@ -10962,7 +10962,7 @@ dependencies = [ "proc-macro2", "quote", "sp-version", - "syn 1.0.109", + "syn 2.0.14", ] [[package]] @@ -11388,7 +11388,7 @@ dependencies = [ "proc-macro-crate", "proc-macro2", "quote", - "syn 1.0.109", + "syn 2.0.14", ] [[package]] @@ -11444,9 +11444,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.13" +version = "2.0.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4c9da457c5285ac1f936ebd076af6dac17a61cfe7826f2076b4d015cf47bc8ec" +checksum = "fcf316d5356ed6847742d036f8a39c3b8435cac10bd528a4bd461928a6ab34d5" dependencies = [ "proc-macro2", "quote", @@ -11705,7 +11705,7 @@ checksum = "61a573bdc87985e9d6ddeed1b3d864e8a302c847e40d647746df2f1de209d1ce" dependencies = [ "proc-macro2", "quote", - "syn 2.0.13", + "syn 2.0.14", ] [[package]] diff --git a/bin/node/cli/tests/telemetry.rs b/bin/node/cli/tests/telemetry.rs index f6e46775a..176d2e81a 100644 --- a/bin/node/cli/tests/telemetry.rs +++ b/bin/node/cli/tests/telemetry.rs @@ -57,8 +57,9 @@ async fn telemetry_works() { } }, - Event::TextFrame { .. } => - panic!("Got a TextFrame over the socket, this is a bug"), + Event::TextFrame { .. } => { + panic!("Got a TextFrame over the socket, this is a bug") + }, // Connection has been closed. Event::ConnectionError { .. } => {}, diff --git a/client/chain-spec/derive/Cargo.toml b/client/chain-spec/derive/Cargo.toml index d733bda9b..c16cd45a9 100644 --- a/client/chain-spec/derive/Cargo.toml +++ b/client/chain-spec/derive/Cargo.toml @@ -16,6 +16,6 @@ proc-macro = true [dependencies] proc-macro-crate = "1.1.3" -proc-macro2 = "1.0.37" -quote = "1.0.10" -syn = "1.0.98" +proc-macro2 = "1.0.56" +quote = "1.0.26" +syn = "2.0.14" diff --git a/client/chain-spec/derive/src/impls.rs b/client/chain-spec/derive/src/impls.rs index e54c1895e..c0624897c 100644 --- a/client/chain-spec/derive/src/impls.rs +++ b/client/chain-spec/derive/src/impls.rs @@ -35,7 +35,7 @@ pub fn extension_derive(ast: &DeriveInput) -> proc_macro::TokenStream { .named .iter() .find_map(|f| { - if f.attrs.iter().any(|attr| attr.path.is_ident(ATTRIBUTE_NAME)) { + if f.attrs.iter().any(|attr| attr.path().is_ident(ATTRIBUTE_NAME)) { let typ = &f.ty; Some(quote! { #typ }) } else { diff --git a/client/consensus/babe/src/lib.rs b/client/consensus/babe/src/lib.rs index a59870ced..e540cae1f 100644 --- a/client/consensus/babe/src/lib.rs +++ b/client/consensus/babe/src/lib.rs @@ -556,8 +556,9 @@ fn aux_storage_cleanup + HeaderBackend, Block: B Ok(meta) => { hashes.insert(meta.parent); }, - Err(err) => - warn!(target: LOG_TARGET, "Failed to lookup metadata for block `{:?}`: {}", first, err,), + Err(err) => { + warn!(target: LOG_TARGET, "Failed to lookup metadata for block `{:?}`: {}", first, err,) + }, } // Cleans data for finalized block's ancestors diff --git a/client/db/src/pinned_blocks_cache.rs b/client/db/src/pinned_blocks_cache.rs index f50a7081d..7b346b463 100644 --- a/client/db/src/pinned_blocks_cache.rs +++ b/client/db/src/pinned_blocks_cache.rs @@ -149,8 +149,9 @@ impl PinnedBlocksCache { self.cache.len() ); }, - None => - log::warn!(target: LOG_TARGET, "Unable to bump reference count. hash = {}", hash), + None => { + log::warn!(target: LOG_TARGET, "Unable to bump reference count. hash = {}", hash) + }, }; } diff --git a/client/service/test/src/client/mod.rs b/client/service/test/src/client/mod.rs index 1b282a342..520a9b52f 100644 --- a/client/service/test/src/client/mod.rs +++ b/client/service/test/src/client/mod.rs @@ -158,8 +158,9 @@ fn finality_notification_check( assert_eq!(notif.hash, *finalized.last().unwrap()); assert_eq!(stale_heads, stale_heads_expected); }, - Err(TryRecvError::Closed) => - panic!("unexpected notification result, client send channel was closed"), + Err(TryRecvError::Closed) => { + panic!("unexpected notification result, client send channel was closed") + }, Err(TryRecvError::Empty) => assert!(finalized.is_empty()), } } diff --git a/client/state-db/src/lib.rs b/client/state-db/src/lib.rs index b41240d62..c656f126a 100644 --- a/client/state-db/src/lib.rs +++ b/client/state-db/src/lib.rs @@ -187,12 +187,14 @@ impl fmt::Debug for StateDbError { "Incompatible pruning modes [stored: {:?}; requested: {:?}]", stored, requested ), - Self::TooManySiblingBlocks { number } => - write!(f, "Too many sibling blocks at #{number} inserted"), + Self::TooManySiblingBlocks { number } => { + write!(f, "Too many sibling blocks at #{number} inserted") + }, Self::BlockAlreadyExists => write!(f, "Block already exists"), Self::Metadata(message) => write!(f, "Invalid metadata: {}", message), - Self::BlockUnavailable => - write!(f, "Trying to get a block record from db while it is not commit to db yet"), + Self::BlockUnavailable => { + write!(f, "Trying to get a block record from db while it is not commit to db yet") + }, Self::BlockMissing => write!(f, "Block record is missing from the pruning window"), } } diff --git a/client/tracing/proc-macro/Cargo.toml b/client/tracing/proc-macro/Cargo.toml index 031f1fe49..54078fe1f 100644 --- a/client/tracing/proc-macro/Cargo.toml +++ b/client/tracing/proc-macro/Cargo.toml @@ -16,6 +16,6 @@ proc-macro = true [dependencies] proc-macro-crate = "1.1.3" -proc-macro2 = "1.0.37" -quote = { version = "1.0.10", features = ["proc-macro"] } -syn = { version = "1.0.98", features = ["proc-macro", "full", "extra-traits", "parsing"] } +proc-macro2 = "1.0.56" +quote = { version = "1.0.26", features = ["proc-macro"] } +syn = { version = "2.0.14", features = ["proc-macro", "full", "extra-traits", "parsing"] } diff --git a/frame/contracts/proc-macro/Cargo.toml b/frame/contracts/proc-macro/Cargo.toml index 08dafcc6a..c700d1e33 100644 --- a/frame/contracts/proc-macro/Cargo.toml +++ b/frame/contracts/proc-macro/Cargo.toml @@ -15,9 +15,9 @@ targets = ["x86_64-unknown-linux-gnu"] proc-macro = true [dependencies] -proc-macro2 = "1" -quote = "1" -syn = { version = "1.0.98", features = ["full"] } +proc-macro2 = "1.0.56" +quote = "1.0.26" +syn = { version = "2.0.14", features = ["full"] } [dev-dependencies] diff --git a/frame/contracts/proc-macro/src/lib.rs b/frame/contracts/proc-macro/src/lib.rs index d54470dd0..a6a8187bc 100644 --- a/frame/contracts/proc-macro/src/lib.rs +++ b/frame/contracts/proc-macro/src/lib.rs @@ -209,13 +209,13 @@ impl HostFn { "only #[version()], #[unstable], #[prefixed_alias] and #[deprecated] attributes are allowed."; let span = item.span(); let mut attrs = item.attrs.clone(); - attrs.retain(|a| !a.path.is_ident("doc")); + attrs.retain(|a| !a.path().is_ident("doc")); let mut maybe_version = None; let mut is_stable = true; let mut alias_to = None; let mut not_deprecated = true; while let Some(attr) = attrs.pop() { - let ident = attr.path.get_ident().ok_or(err(span, msg))?.to_string(); + let ident = attr.path().get_ident().ok_or(err(span, msg))?.to_string(); match ident.as_str() { "version" => { if maybe_version.is_some() { @@ -377,7 +377,7 @@ impl EnvDef { _ => None, }; - let selector = |a: &syn::Attribute| a.path.is_ident("prefixed_alias"); + let selector = |a: &syn::Attribute| a.path().is_ident("prefixed_alias"); let aliases = items .iter() @@ -401,7 +401,7 @@ impl EnvDef { } fn is_valid_special_arg(idx: usize, arg: &FnArg) -> bool { - let pat = if let FnArg::Typed(pat) = arg { pat } else { return false }; + let FnArg::Typed(pat) = arg else { return false }; let ident = if let syn::Pat::Ident(ref ident) = *pat.pat { &ident.ident } else { return false }; let name_ok = match idx { 0 => ident == "ctx" || ident == "_ctx", @@ -434,7 +434,7 @@ fn expand_func_doc(func: &HostFn) -> TokenStream2 { ); quote! { #[doc = #alias_doc] } } else { - let docs = func.item.attrs.iter().filter(|a| a.path.is_ident("doc")).map(|d| { + let docs = func.item.attrs.iter().filter(|a| a.path().is_ident("doc")).map(|d| { let docs = d.to_token_stream(); quote! { #docs } }); diff --git a/frame/election-provider-support/solution-type/Cargo.toml b/frame/election-provider-support/solution-type/Cargo.toml index eb9598dca..95ad6f226 100644 --- a/frame/election-provider-support/solution-type/Cargo.toml +++ b/frame/election-provider-support/solution-type/Cargo.toml @@ -15,9 +15,9 @@ targets = ["x86_64-unknown-linux-gnu"] proc-macro = true [dependencies] -syn = { version = "1.0.98", features = ["full", "visit"] } -quote = "1.0" -proc-macro2 = "1.0.37" +syn = { version = "2.0.14", features = ["full", "visit"] } +quote = "1.0.26" +proc-macro2 = "1.0.56" proc-macro-crate = "1.1.3" [dev-dependencies] diff --git a/frame/election-provider-support/solution-type/src/lib.rs b/frame/election-provider-support/solution-type/src/lib.rs index f79554321..693895307 100644 --- a/frame/election-provider-support/solution-type/src/lib.rs +++ b/frame/election-provider-support/solution-type/src/lib.rs @@ -161,7 +161,7 @@ fn check_attributes(input: ParseStream) -> syn::Result { return Ok(false) } let attr = attrs.pop().expect("attributes vec with len 1 can be popped."); - if attr.path.is_ident("compact") { + if attr.path().is_ident("compact") { Ok(true) } else { Err(syn::Error::new_spanned(attr, "compact solution can accept only #[compact]")) @@ -200,7 +200,7 @@ impl Parse for SolutionDef { format!("Expected binding: `{} = ...`", expected), )) }, - syn::GenericArgument::Binding(syn::Binding { ident, ty, .. }) => { + syn::GenericArgument::AssocType(syn::AssocType { ident, ty, .. }) => { // check that we have the right keyword for this position in the argument list if ident == expected { Ok(ty.clone()) diff --git a/frame/nfts/src/tests.rs b/frame/nfts/src/tests.rs index 0fc6fcd15..d36f8b54e 100644 --- a/frame/nfts/src/tests.rs +++ b/frame/nfts/src/tests.rs @@ -2700,7 +2700,11 @@ fn claim_swap_should_work() { Balances::make_free_balance_be(&user_1, initial_balance); Balances::make_free_balance_be(&user_2, initial_balance); - assert_ok!(Nfts::force_create(RuntimeOrigin::root(), user_1.clone(), default_collection_config())); + assert_ok!(Nfts::force_create( + RuntimeOrigin::root(), + user_1.clone(), + default_collection_config() + )); assert_ok!(Nfts::mint( RuntimeOrigin::signed(user_1.clone()), diff --git a/frame/scheduler/src/migration.rs b/frame/scheduler/src/migration.rs index e641d2895..5b3a7631e 100644 --- a/frame/scheduler/src/migration.rs +++ b/frame/scheduler/src/migration.rs @@ -297,8 +297,9 @@ pub mod v4 { target: TARGET, "Did not clean up any agendas. v4::CleanupAgendas can be removed." ), - Some(n) => - log::info!(target: TARGET, "Cleaned up {} agendas, now {}", n, new_agendas), + Some(n) => { + log::info!(target: TARGET, "Cleaned up {} agendas, now {}", n, new_agendas) + }, None => unreachable!( "Number of agendas cannot increase, old {} new {}", old_agendas, new_agendas @@ -560,8 +561,9 @@ mod test { "Agenda {} should be removed", i ), - Some(new) => - assert_eq!(Agenda::::get(i as u64), new, "Agenda wrong {}", i), + Some(new) => { + assert_eq!(Agenda::::get(i as u64), new, "Agenda wrong {}", i) + }, } } }); diff --git a/frame/staking/reward-curve/Cargo.toml b/frame/staking/reward-curve/Cargo.toml index c761517ea..4a97d20a5 100644 --- a/frame/staking/reward-curve/Cargo.toml +++ b/frame/staking/reward-curve/Cargo.toml @@ -16,9 +16,9 @@ proc-macro = true [dependencies] proc-macro-crate = "1.1.3" -proc-macro2 = "1.0.37" -quote = "1.0.10" -syn = { version = "1.0.98", features = ["full", "visit"] } +proc-macro2 = "1.0.56" +quote = "1.0.26" +syn = { version = "2.0.14", features = ["full", "visit"] } [dev-dependencies] sp-runtime = { version = "7.0.0", path = "../../../primitives/runtime" } diff --git a/frame/support/procedural/Cargo.toml b/frame/support/procedural/Cargo.toml index 6dbdd3b3a..d57b81a34 100644 --- a/frame/support/procedural/Cargo.toml +++ b/frame/support/procedural/Cargo.toml @@ -19,9 +19,9 @@ derive-syn-parse = "0.1.5" Inflector = "0.11.4" cfg-expr = "0.10.3" itertools = "0.10.3" -proc-macro2 = "1.0.37" -quote = "1.0.10" -syn = { version = "1.0.98", features = ["full"] } +proc-macro2 = "1.0.56" +quote = "1.0.26" +syn = { version = "2.0.14", features = ["full"] } frame-support-procedural-tools = { version = "4.0.0-dev", path = "./tools" } proc-macro-warning = { version = "0.2.0", default-features = false } diff --git a/frame/support/procedural/src/benchmark.rs b/frame/support/procedural/src/benchmark.rs index 4f9994b6c..cf091e7cb 100644 --- a/frame/support/procedural/src/benchmark.rs +++ b/frame/support/procedural/src/benchmark.rs @@ -21,14 +21,13 @@ use derive_syn_parse::Parse; use frame_support_procedural_tools::generate_crate_access_2018; use proc_macro::TokenStream; use proc_macro2::{Ident, Span, TokenStream as TokenStream2}; -use quote::{quote, quote_spanned, ToTokens}; +use quote::{quote, ToTokens}; use syn::{ - parenthesized, parse::{Nothing, ParseStream}, parse_quote, punctuated::Punctuated, spanned::Spanned, - token::{Colon2, Comma, Gt, Lt, Paren}, + token::{Comma, Gt, Lt, PathSep}, Attribute, Error, Expr, ExprBlock, ExprCall, ExprPath, FnArg, Item, ItemFn, ItemMod, LitInt, Pat, Path, PathArguments, PathSegment, Result, ReturnType, Signature, Stmt, Token, Type, TypePath, Visibility, WhereClause, @@ -45,6 +44,9 @@ mod keywords { custom_keyword!(skip_meta); custom_keyword!(BenchmarkError); custom_keyword!(Result); + + pub const BENCHMARK_TOKEN: &str = stringify!(benchmark); + pub const BENCHMARKS_TOKEN: &str = stringify!(benchmarks); } /// This represents the raw parsed data for a param definition such as `x: Linear<10, 20>`. @@ -95,27 +97,20 @@ impl syn::parse::Parse for BenchmarkAttrKeyword { impl syn::parse::Parse for BenchmarkAttrs { fn parse(input: ParseStream) -> syn::Result { - let lookahead = input.lookahead1(); - if !lookahead.peek(Paren) { - let _nothing: Nothing = input.parse()?; - return Ok(BenchmarkAttrs { skip_meta: false, extra: false }) - } - let content; - let _paren: Paren = parenthesized!(content in input); let mut extra = false; let mut skip_meta = false; - let args = Punctuated::::parse_terminated(&content)?; + let args = Punctuated::::parse_terminated(&input)?; for arg in args.into_iter() { match arg { BenchmarkAttrKeyword::Extra => { if extra { - return Err(content.error("`extra` can only be specified once")) + return Err(input.error("`extra` can only be specified once")) } extra = true; }, BenchmarkAttrKeyword::SkipMeta => { if skip_meta { - return Err(content.error("`skip_meta` can only be specified once")) + return Err(input.error("`skip_meta` can only be specified once")) } skip_meta = true; }, @@ -150,8 +145,6 @@ struct BenchmarkDef { call_def: BenchmarkCallDef, verify_stmts: Vec, last_stmt: Option, - extra: bool, - skip_meta: bool, fn_sig: Signature, fn_vis: Visibility, fn_attrs: Vec, @@ -218,7 +211,7 @@ fn parse_params(item_fn: &ItemFn) -> Result> { return Err(Error::new( var_span, "Benchmark parameter names must consist of a single lowercase letter (a-z) and no other characters.", - )) + )); }; let name = ident.ident.to_token_stream().to_string(); if name.len() > 1 { @@ -253,9 +246,9 @@ fn parse_params(item_fn: &ItemFn) -> Result> { /// Used in several places where the `#[extrinsic_call]` or `#[body]` annotation is missing fn missing_call(item_fn: &ItemFn) -> Result { return Err(Error::new( - item_fn.block.brace_token.span, + item_fn.block.brace_token.span.join(), "No valid #[extrinsic_call] or #[block] annotation could be found in benchmark function body." - )) + )); } /// Finds the `BenchmarkCallDef` and its index (within the list of stmts for the fn) and @@ -264,10 +257,10 @@ fn missing_call(item_fn: &ItemFn) -> Result { fn parse_call_def(item_fn: &ItemFn) -> Result<(usize, BenchmarkCallDef)> { // #[extrinsic_call] / #[block] handling let call_defs = item_fn.block.stmts.iter().enumerate().filter_map(|(i, child)| { - if let Stmt::Semi(Expr::Call(expr_call), _semi) = child { + if let Stmt::Expr(Expr::Call(expr_call), _semi) = child { // #[extrinsic_call] case expr_call.attrs.iter().enumerate().find_map(|(k, attr)| { - let segment = attr.path.segments.last()?; + let segment = attr.path().segments.last()?; let _: keywords::extrinsic_call = syn::parse(segment.ident.to_token_stream().into()).ok()?; let mut expr_call = expr_call.clone(); @@ -281,10 +274,10 @@ fn parse_call_def(item_fn: &ItemFn) -> Result<(usize, BenchmarkCallDef)> { Some(Ok((i, BenchmarkCallDef::ExtrinsicCall { origin, expr_call, attr_span: attr.span() }))) }) - } else if let Stmt::Expr(Expr::Block(block)) = child { + } else if let Stmt::Expr(Expr::Block(block), _) = child { // #[block] case block.attrs.iter().enumerate().find_map(|(k, attr)| { - let segment = attr.path.segments.last()?; + let segment = attr.path().segments.last()?; let _: keywords::block = syn::parse(segment.ident.to_token_stream().into()).ok()?; let mut block = block.clone(); @@ -310,7 +303,7 @@ fn parse_call_def(item_fn: &ItemFn) -> Result<(usize, BenchmarkCallDef)> { impl BenchmarkDef { /// Constructs a [`BenchmarkDef`] by traversing an existing [`ItemFn`] node. - pub fn from(item_fn: &ItemFn, extra: bool, skip_meta: bool) -> Result { + pub fn from(item_fn: &ItemFn) -> Result { let params = parse_params(item_fn)?; ensure_valid_return_type(item_fn)?; let (i, call_def) = parse_call_def(&item_fn)?; @@ -346,8 +339,6 @@ impl BenchmarkDef { call_def, verify_stmts, last_stmt, - extra, - skip_meta, fn_sig: item_fn.sig.clone(), fn_vis: item_fn.vis.clone(), fn_attrs: item_fn.attrs.clone(), @@ -375,7 +366,7 @@ pub fn benchmarks( let mod_attrs: Vec<&Attribute> = module .attrs .iter() - .filter(|attr| syn::parse2::(attr.to_token_stream()).is_err()) + .filter(|attr| !attr.path().is_ident(keywords::BENCHMARKS_TOKEN)) .collect(); let mut benchmark_names: Vec = Vec::new(); @@ -391,35 +382,32 @@ pub fn benchmarks( let Item::Fn(func) = stmt else { return None }; // find #[benchmark] attribute on function def - let benchmark_attr = func.attrs.iter().find_map(|attr| { - let seg = attr.path.segments.last()?; - syn::parse::(seg.ident.to_token_stream().into()).ok()?; - Some(attr) - })?; + let benchmark_attr = + func.attrs.iter().find(|attr| attr.path().is_ident(keywords::BENCHMARK_TOKEN))?; Some((benchmark_attr.clone(), func.clone(), stmt)) }); // parse individual benchmark defs and args for (benchmark_attr, func, stmt) in benchmark_fn_metas { - // parse any args provided to #[benchmark] - let attr_tokens = benchmark_attr.tokens.to_token_stream().into(); - let benchmark_args: BenchmarkAttrs = syn::parse(attr_tokens)?; - // parse benchmark def - let benchmark_def = - BenchmarkDef::from(&func, benchmark_args.extra, benchmark_args.skip_meta)?; + let benchmark_def = BenchmarkDef::from(&func)?; // record benchmark name let name = &func.sig.ident; benchmark_names.push(name.clone()); - // record name sets - if benchmark_def.extra { - extra_benchmark_names.push(name.clone()); - } - if benchmark_def.skip_meta { - skip_meta_benchmark_names.push(name.clone()) + // Check if we need to parse any args + if benchmark_attr.meta.require_path_only().is_err() { + // parse any args provided to #[benchmark] + let benchmark_attrs: BenchmarkAttrs = benchmark_attr.parse_args()?; + + // record name sets + if benchmark_attrs.extra { + extra_benchmark_names.push(name.clone()); + } else if benchmark_attrs.skip_meta { + skip_meta_benchmark_names.push(name.clone()); + } } // expand benchmark @@ -787,7 +775,8 @@ fn expand_benchmark( // determine call name (handles `_` and normal call syntax) let expr_span = expr_call.span(); let call_err = || { - quote_spanned!(expr_span=> "Extrinsic call must be a function call or `_`".to_compile_error()).into() + syn::Error::new(expr_span, "Extrinsic call must be a function call or `_`") + .to_compile_error() }; let call_name = match *expr_call.func { Expr::Path(expr_path) => { @@ -795,10 +784,9 @@ fn expand_benchmark( let Some(segment) = expr_path.path.segments.last() else { return call_err(); }; segment.ident.to_string() }, - Expr::Verbatim(tokens) => { + Expr::Infer(_) => { // `_` style // replace `_` with fn name - let Ok(_) = syn::parse::(tokens.to_token_stream().into()) else { return call_err(); }; name.to_string() }, _ => return call_err(), @@ -806,7 +794,7 @@ fn expand_benchmark( // modify extrinsic call to be prefixed with "new_call_variant" let call_name = format!("new_call_variant_{}", call_name); - let mut punct: Punctuated = Punctuated::new(); + let mut punct: Punctuated = Punctuated::new(); punct.push(PathSegment { arguments: PathArguments::None, ident: Ident::new(call_name.as_str(), Span::call_site()), @@ -847,11 +835,10 @@ fn expand_benchmark( let vis = benchmark_def.fn_vis; // remove #[benchmark] attribute - let fn_attrs: Vec<&Attribute> = benchmark_def + let fn_attrs = benchmark_def .fn_attrs .iter() - .filter(|attr| !syn::parse2::(attr.path.to_token_stream()).is_ok()) - .collect(); + .filter(|attr| !attr.path().is_ident(keywords::BENCHMARK_TOKEN)); // modify signature generics, ident, and inputs, e.g: // before: `fn bench(u: Linear<1, 100>) -> Result<(), BenchmarkError>` @@ -874,10 +861,11 @@ fn expand_benchmark( Some(stmt) => quote!(#stmt), None => quote!(Ok(())), }; + let fn_attrs_clone = fn_attrs.clone(); let fn_def = quote! { #( - #fn_attrs + #fn_attrs_clone )* #vis #sig { #( diff --git a/frame/support/procedural/src/construct_runtime/mod.rs b/frame/support/procedural/src/construct_runtime/mod.rs index c5a48a980..1af44fc00 100644 --- a/frame/support/procedural/src/construct_runtime/mod.rs +++ b/frame/support/procedural/src/construct_runtime/mod.rs @@ -224,7 +224,7 @@ fn construct_runtime_final_expansion( let system_pallet = pallets.iter().find(|decl| decl.name == SYSTEM_PALLET_NAME).ok_or_else(|| { syn::Error::new( - pallets_token.span, + pallets_token.span.join(), "`System` pallet declaration is missing. \ Please add this line: `System: frame_system::{Pallet, Call, Storage, Config, Event},`", ) diff --git a/frame/support/procedural/src/construct_runtime/parse.rs b/frame/support/procedural/src/construct_runtime/parse.rs index 5e6979f1d..f819a90d1 100644 --- a/frame/support/procedural/src/construct_runtime/parse.rs +++ b/frame/support/procedural/src/construct_runtime/parse.rs @@ -17,6 +17,7 @@ use frame_support_procedural_tools::syn_ext as ext; use proc_macro2::{Span, TokenStream}; +use quote::ToTokens; use std::collections::{HashMap, HashSet}; use syn::{ ext::IdentExt, @@ -444,21 +445,21 @@ impl PalletPartKeyword { } } -impl Spanned for PalletPartKeyword { - fn span(&self) -> Span { +impl ToTokens for PalletPartKeyword { + fn to_tokens(&self, tokens: &mut TokenStream) { match self { - Self::Pallet(inner) => inner.span(), - Self::Call(inner) => inner.span(), - Self::Storage(inner) => inner.span(), - Self::Event(inner) => inner.span(), - Self::Config(inner) => inner.span(), - Self::Origin(inner) => inner.span(), - Self::Inherent(inner) => inner.span(), - Self::ValidateUnsigned(inner) => inner.span(), - Self::FreezeReason(inner) => inner.span(), - Self::HoldReason(inner) => inner.span(), - Self::LockId(inner) => inner.span(), - Self::SlashReason(inner) => inner.span(), + Self::Pallet(inner) => inner.to_tokens(tokens), + Self::Call(inner) => inner.to_tokens(tokens), + Self::Storage(inner) => inner.to_tokens(tokens), + Self::Event(inner) => inner.to_tokens(tokens), + Self::Config(inner) => inner.to_tokens(tokens), + Self::Origin(inner) => inner.to_tokens(tokens), + Self::Inherent(inner) => inner.to_tokens(tokens), + Self::ValidateUnsigned(inner) => inner.to_tokens(tokens), + Self::FreezeReason(inner) => inner.to_tokens(tokens), + Self::HoldReason(inner) => inner.to_tokens(tokens), + Self::LockId(inner) => inner.to_tokens(tokens), + Self::SlashReason(inner) => inner.to_tokens(tokens), } } } @@ -681,7 +682,7 @@ fn convert_pallets(pallets: Vec) -> syn::Result proc_macro::To let default_variants = enum_ .variants .into_iter() - .filter(|variant| variant.attrs.iter().any(|attr| attr.path.is_ident("default"))) + .filter(|variant| variant.attrs.iter().any(|attr| attr.path().is_ident("default"))) .collect::>(); match &*default_variants { @@ -80,7 +80,7 @@ pub fn derive_default_no_bound(input: proc_macro::TokenStream) -> proc_macro::To let variant_attrs = default_variant .attrs .iter() - .filter(|a| a.path.is_ident("default")) + .filter(|a| a.path().is_ident("default")) .collect::>(); // check that there is only one #[default] attribute on the variant diff --git a/frame/support/procedural/src/pallet/expand/call.rs b/frame/support/procedural/src/pallet/expand/call.rs index 74075848d..aa5af9d54 100644 --- a/frame/support/procedural/src/pallet/expand/call.rs +++ b/frame/support/procedural/src/pallet/expand/call.rs @@ -188,7 +188,7 @@ pub fn expand_call(def: &mut Def) -> proc_macro2::TokenStream { .map(|c| &mut def.item.content.as_mut().expect("Checked by def parser").1[c.index]) { item_impl.items.iter_mut().for_each(|i| { - if let syn::ImplItem::Method(method) = i { + if let syn::ImplItem::Fn(method) = i { let block = &method.block; method.block = syn::parse_quote! {{ // We execute all dispatchable in a new storage layer, allowing them @@ -206,13 +206,7 @@ pub fn expand_call(def: &mut Def) -> proc_macro2::TokenStream { method .attrs .iter() - .find(|attr| { - if let Ok(syn::Meta::List(syn::MetaList { path, .. })) = attr.parse_meta() { - path.segments.last().map(|seg| seg.ident == "allow").unwrap_or(false) - } else { - false - } - }) + .find(|attr| attr.path().is_ident("allow")) .map_or(proc_macro2::TokenStream::new(), |attr| attr.to_token_stream()) }) .collect::>(); diff --git a/frame/support/procedural/src/pallet/expand/constants.rs b/frame/support/procedural/src/pallet/expand/constants.rs index 21fa492a7..6e1cd3df6 100644 --- a/frame/support/procedural/src/pallet/expand/constants.rs +++ b/frame/support/procedural/src/pallet/expand/constants.rs @@ -23,7 +23,7 @@ struct ConstDef { /// The type in Get, e.g. `u32` in `type Foo: Get;`, but `Self` is replaced by `T` pub type_: syn::Type, /// The doc associated - pub doc: Vec, + pub doc: Vec, /// default_byte implementation pub default_byte_impl: proc_macro2::TokenStream, /// Constant name for Metadata (optional) diff --git a/frame/support/procedural/src/pallet/expand/documentation.rs b/frame/support/procedural/src/pallet/expand/documentation.rs index 1aa46cf57..4e6347e83 100644 --- a/frame/support/procedural/src/pallet/expand/documentation.rs +++ b/frame/support/procedural/src/pallet/expand/documentation.rs @@ -16,54 +16,24 @@ // limitations under the License. use crate::pallet::Def; -use derive_syn_parse::Parse; use proc_macro2::TokenStream; use quote::ToTokens; -use syn::{ - parse::{self, Parse, ParseStream}, - spanned::Spanned, - Attribute, Lit, -}; +use syn::{spanned::Spanned, Attribute, Lit, LitStr}; const DOC: &'static str = "doc"; const PALLET_DOC: &'static str = "pallet_doc"; -mod keywords { - syn::custom_keyword!(include_str); -} - /// Get the documentation file path from the `pallet_doc` attribute. /// /// Supported format: /// `#[pallet_doc(PATH)]`: The path of the file from which the documentation is loaded fn parse_pallet_doc_value(attr: &Attribute) -> syn::Result { - let span = attr.span(); - - let meta = attr.parse_meta()?; - let syn::Meta::List(metalist) = meta else { - let msg = "The `pallet_doc` attribute must receive arguments as a list. Supported format: `pallet_doc(PATH)`"; - return Err(syn::Error::new(span, msg)) - }; + let lit: syn::LitStr = attr.parse_args().map_err(|_| { + let msg = "The `pallet_doc` received an unsupported argument. Supported format: `pallet_doc(\"PATH\")`"; + syn::Error::new(attr.span(), msg) + })?; - let paths: Vec<_> = metalist - .nested - .into_iter() - .map(|nested| { - let syn::NestedMeta::Lit(lit) = nested else { - let msg = "The `pallet_doc` received an unsupported argument. Supported format: `pallet_doc(PATH)`"; - return Err(syn::Error::new(span, msg)) - }; - - Ok(lit) - }) - .collect::>()?; - - if paths.len() != 1 { - let msg = "The `pallet_doc` attribute must receive only one argument. Supported format: `pallet_doc(PATH)`"; - return Err(syn::Error::new(span, msg)) - } - - Ok(DocMetaValue::Path(paths[0].clone())) + Ok(DocMetaValue::Path(lit)) } /// Get the value from the `doc` comment attribute: @@ -71,47 +41,22 @@ fn parse_pallet_doc_value(attr: &Attribute) -> syn::Result { /// Supported formats: /// - `#[doc = "A doc string"]`: Documentation as a string literal /// - `#[doc = include_str!(PATH)]`: Documentation obtained from a path -fn parse_doc_value(attr: &Attribute) -> Option { - let Some(ident) = attr.path.get_ident() else { - return None - }; - if ident != DOC { - return None +fn parse_doc_value(attr: &Attribute) -> syn::Result> { + if !attr.path().is_ident(DOC) { + return Ok(None) } - let parser = |input: ParseStream| DocParser::parse(input); - let result = parse::Parser::parse2(parser, attr.tokens.clone()).ok()?; + let meta = attr.meta.require_name_value()?; - if let Some(lit) = result.lit { - Some(DocMetaValue::Lit(lit)) - } else if let Some(include_doc) = result.include_doc { - Some(DocMetaValue::Path(include_doc.lit)) - } else { - None + match &meta.value { + syn::Expr::Lit(lit) => Ok(Some(DocMetaValue::Lit(lit.lit.clone()))), + syn::Expr::Macro(mac) if mac.mac.path.is_ident("include_str") => + Ok(Some(DocMetaValue::Path(mac.mac.parse_body()?))), + _ => + Err(syn::Error::new(attr.span(), "Expected `= \"docs\"` or `= include_str!(\"PATH\")`")), } } -/// Parse the include_str attribute. -#[derive(Debug, Parse)] -struct IncludeDocParser { - _include_str: keywords::include_str, - _eq_token: syn::token::Bang, - #[paren] - _paren: syn::token::Paren, - #[inside(_paren)] - lit: Lit, -} - -/// Parse the doc literal. -#[derive(Debug, Parse)] -struct DocParser { - _eq_token: syn::token::Eq, - #[peek(Lit)] - lit: Option, - #[parse_if(lit.is_none())] - include_doc: Option, -} - /// Supported documentation tokens. #[derive(Debug)] enum DocMetaValue { @@ -124,7 +69,7 @@ enum DocMetaValue { /// The string literal represents the file `PATH`. /// /// `#[doc = include_str!(PATH)]` - Path(Lit), + Path(LitStr), } impl ToTokens for DocMetaValue { @@ -179,33 +124,39 @@ pub fn expand_documentation(def: &mut Def) -> proc_macro2::TokenStream { let mut index = 0; while index < def.item.attrs.len() { let attr = &def.item.attrs[index]; - if let Some(ident) = attr.path.get_ident() { - if ident == PALLET_DOC { - let elem = def.item.attrs.remove(index); - pallet_docs.push(elem); - // Do not increment the index, we have just removed the - // element from the attributes. - continue - } + if attr.path().get_ident().map_or(false, |i| *i == PALLET_DOC) { + pallet_docs.push(def.item.attrs.remove(index)); + // Do not increment the index, we have just removed the + // element from the attributes. + continue } index += 1; } // Capture the `#[doc = include_str!("../README.md")]` and `#[doc = "Documentation"]`. - let mut docs: Vec<_> = def.item.attrs.iter().filter_map(parse_doc_value).collect(); + let docs = match def + .item + .attrs + .iter() + .filter_map(|v| parse_doc_value(v).transpose()) + .collect::>>() + { + Ok(r) => r, + Err(err) => return err.into_compile_error(), + }; // Capture the `#[pallet_doc("../README.md")]`. - let pallet_docs: Vec<_> = match pallet_docs + let pallet_docs = match pallet_docs .into_iter() .map(|attr| parse_pallet_doc_value(&attr)) - .collect::>() + .collect::>>() { Ok(docs) => docs, Err(err) => return err.into_compile_error(), }; - docs.extend(pallet_docs); + let docs = docs.iter().chain(pallet_docs.iter()); quote::quote!( impl<#type_impl_gen> #pallet_ident<#type_use_gen> #where_clauses{ diff --git a/frame/support/procedural/src/pallet/expand/storage.rs b/frame/support/procedural/src/pallet/expand/storage.rs index efc803033..c742ddcd2 100644 --- a/frame/support/procedural/src/pallet/expand/storage.rs +++ b/frame/support/procedural/src/pallet/expand/storage.rs @@ -384,10 +384,11 @@ pub fn expand_storages(def: &mut Def) -> proc_macro2::TokenStream { QueryKind::OptionQuery => quote::quote_spanned!(storage.attr_span => Option<#value> ), - QueryKind::ResultQuery(error_path, _) => + QueryKind::ResultQuery(error_path, _) => { quote::quote_spanned!(storage.attr_span => Result<#value, #error_path> - ), + ) + }, QueryKind::ValueQuery => quote::quote!(#value), }; quote::quote_spanned!(storage.attr_span => @@ -407,10 +408,11 @@ pub fn expand_storages(def: &mut Def) -> proc_macro2::TokenStream { QueryKind::OptionQuery => quote::quote_spanned!(storage.attr_span => Option<#value> ), - QueryKind::ResultQuery(error_path, _) => + QueryKind::ResultQuery(error_path, _) => { quote::quote_spanned!(storage.attr_span => Result<#value, #error_path> - ), + ) + }, QueryKind::ValueQuery => quote::quote!(#value), }; quote::quote_spanned!(storage.attr_span => @@ -432,10 +434,11 @@ pub fn expand_storages(def: &mut Def) -> proc_macro2::TokenStream { QueryKind::OptionQuery => quote::quote_spanned!(storage.attr_span => Option<#value> ), - QueryKind::ResultQuery(error_path, _) => + QueryKind::ResultQuery(error_path, _) => { quote::quote_spanned!(storage.attr_span => Result<#value, #error_path> - ), + ) + }, QueryKind::ValueQuery => quote::quote!(#value), }; quote::quote_spanned!(storage.attr_span => @@ -457,10 +460,11 @@ pub fn expand_storages(def: &mut Def) -> proc_macro2::TokenStream { QueryKind::OptionQuery => quote::quote_spanned!(storage.attr_span => Option<#value> ), - QueryKind::ResultQuery(error_path, _) => + QueryKind::ResultQuery(error_path, _) => { quote::quote_spanned!(storage.attr_span => Result<#value, #error_path> - ), + ) + }, QueryKind::ValueQuery => quote::quote!(#value), }; quote::quote_spanned!(storage.attr_span => @@ -484,10 +488,11 @@ pub fn expand_storages(def: &mut Def) -> proc_macro2::TokenStream { QueryKind::OptionQuery => quote::quote_spanned!(storage.attr_span => Option<#value> ), - QueryKind::ResultQuery(error_path, _) => + QueryKind::ResultQuery(error_path, _) => { quote::quote_spanned!(storage.attr_span => Result<#value, #error_path> - ), + ) + }, QueryKind::ValueQuery => quote::quote!(#value), }; quote::quote_spanned!(storage.attr_span => diff --git a/frame/support/procedural/src/pallet/parse/call.rs b/frame/support/procedural/src/pallet/parse/call.rs index 5b90c2d98..ae1c039c9 100644 --- a/frame/support/procedural/src/pallet/parse/call.rs +++ b/frame/support/procedural/src/pallet/parse/call.rs @@ -45,7 +45,7 @@ pub struct CallDef { /// The span of the pallet::call attribute. pub attr_span: proc_macro2::Span, /// Docs, specified on the impl Block. - pub docs: Vec, + pub docs: Vec, } /// Definition of dispatchable typically: `#[weight...] fn foo(origin .., param1: ...) -> ..` @@ -62,7 +62,7 @@ pub struct CallVariantDef { /// Whether an explicit call index was specified. pub explicit_call_index: bool, /// Docs, used for metadata. - pub docs: Vec, + pub docs: Vec, /// Attributes annotated at the top of the dispatchable function. pub attrs: Vec, } @@ -173,7 +173,7 @@ impl CallDef { let mut indices = HashMap::new(); let mut last_index: Option = None; for item in &mut item_impl.items { - if let syn::ImplItem::Method(method) = item { + if let syn::ImplItem::Fn(method) = item { if !matches!(method.vis, syn::Visibility::Public(_)) { let msg = "Invalid pallet::call, dispatchable function must be public: \ `pub fn`"; diff --git a/frame/support/procedural/src/pallet/parse/composite.rs b/frame/support/procedural/src/pallet/parse/composite.rs index 2406f10d5..2bbfcd2e9 100644 --- a/frame/support/procedural/src/pallet/parse/composite.rs +++ b/frame/support/procedural/src/pallet/parse/composite.rs @@ -32,14 +32,14 @@ pub mod keyword { SlashReason(SlashReason), } - impl Spanned for CompositeKeyword { - fn span(&self) -> proc_macro2::Span { + impl ToTokens for CompositeKeyword { + fn to_tokens(&self, tokens: &mut proc_macro2::TokenStream) { use CompositeKeyword::*; match self { - FreezeReason(inner) => inner.span(), - HoldReason(inner) => inner.span(), - LockId(inner) => inner.span(), - SlashReason(inner) => inner.span(), + FreezeReason(inner) => inner.to_tokens(tokens), + HoldReason(inner) => inner.to_tokens(tokens), + LockId(inner) => inner.to_tokens(tokens), + SlashReason(inner) => inner.to_tokens(tokens), } } } @@ -109,14 +109,11 @@ impl CompositeDef { } let has_derive_attr = item.attrs.iter().any(|attr| { - attr.parse_meta() - .ok() - .map(|meta| match meta { - syn::Meta::List(syn::MetaList { path, .. }) => - path.get_ident().map(|ident| ident == "derive").unwrap_or(false), - _ => false, - }) - .unwrap_or(false) + if let syn::Meta::List(syn::MetaList { path, .. }) = &attr.meta { + path.get_ident().map(|ident| ident == "derive").unwrap_or(false) + } else { + false + } }); if !has_derive_attr { diff --git a/frame/support/procedural/src/pallet/parse/config.rs b/frame/support/procedural/src/pallet/parse/config.rs index 4d22e50ef..7b52c1664 100644 --- a/frame/support/procedural/src/pallet/parse/config.rs +++ b/frame/support/procedural/src/pallet/parse/config.rs @@ -61,7 +61,7 @@ pub struct ConstMetadataDef { /// The type in Get, e.g. `u32` in `type Foo: Get;`, but `Self` is replaced by `T` pub type_: syn::Type, /// The doc associated - pub doc: Vec, + pub doc: Vec, } impl TryFrom<&syn::TraitItemType> for ConstMetadataDef { @@ -124,23 +124,35 @@ impl syn::parse::Parse for DisableFrameSystemSupertraitCheck { } /// Parse for `#[pallet::constant]` -pub struct TypeAttrConst(proc_macro2::Span); - -impl Spanned for TypeAttrConst { - fn span(&self) -> proc_macro2::Span { - self.0 - } +pub struct TypeAttrConst { + pound_token: syn::Token![#], + bracket_token: syn::token::Bracket, + pallet_ident: syn::Ident, + path_sep_token: syn::token::PathSep, + constant_keyword: keyword::constant, } impl syn::parse::Parse for TypeAttrConst { fn parse(input: syn::parse::ParseStream) -> syn::Result { - input.parse::()?; + let pound_token = input.parse::()?; let content; - syn::bracketed!(content in input); - content.parse::()?; - content.parse::()?; + let bracket_token = syn::bracketed!(content in input); + let pallet_ident = content.parse::()?; + let path_sep_token = content.parse::()?; + let constant_keyword = content.parse::()?; - Ok(TypeAttrConst(content.parse::()?.span())) + Ok(Self { pound_token, bracket_token, pallet_ident, path_sep_token, constant_keyword }) + } +} + +impl ToTokens for TypeAttrConst { + fn to_tokens(&self, tokens: &mut proc_macro2::TokenStream) { + self.pound_token.to_tokens(tokens); + self.bracket_token.surround(tokens, |tokens| { + self.pallet_ident.to_tokens(tokens); + self.path_sep_token.to_tokens(tokens); + self.constant_keyword.to_tokens(tokens); + }) } } diff --git a/frame/support/procedural/src/pallet/parse/error.rs b/frame/support/procedural/src/pallet/parse/error.rs index f344c5d27..6f82ce61f 100644 --- a/frame/support/procedural/src/pallet/parse/error.rs +++ b/frame/support/procedural/src/pallet/parse/error.rs @@ -37,7 +37,7 @@ pub struct ErrorDef { /// The index of error item in pallet module. pub index: usize, /// Variants ident, optional field and doc literals (ordered as declaration order) - pub variants: Vec<(syn::Ident, Option, Vec)>, + pub variants: Vec<(syn::Ident, Option, Vec)>, /// A set of usage of instance, must be check for consistency with trait. pub instances: Vec, /// The keyword error used (contains span). diff --git a/frame/support/procedural/src/pallet/parse/extra_constants.rs b/frame/support/procedural/src/pallet/parse/extra_constants.rs index 8e85d2c26..2ba6c44b7 100644 --- a/frame/support/procedural/src/pallet/parse/extra_constants.rs +++ b/frame/support/procedural/src/pallet/parse/extra_constants.rs @@ -50,7 +50,7 @@ pub struct ExtraConstantDef { /// The type returned by the function pub type_: syn::Type, /// The doc associated - pub doc: Vec, + pub doc: Vec, /// Optional MetaData Name pub metadata_name: Option, } @@ -100,7 +100,7 @@ impl ExtraConstantsDef { let mut extra_constants = vec![]; for impl_item in &mut item.items { - let method = if let syn::ImplItem::Method(method) = impl_item { + let method = if let syn::ImplItem::Fn(method) = impl_item { method } else { let msg = "Invalid pallet::call, only method accepted"; diff --git a/frame/support/procedural/src/pallet/parse/helper.rs b/frame/support/procedural/src/pallet/parse/helper.rs index 4814dce5a..3cdbfb1f5 100644 --- a/frame/support/procedural/src/pallet/parse/helper.rs +++ b/frame/support/procedural/src/pallet/parse/helper.rs @@ -54,7 +54,7 @@ where let attrs = if let Some(attrs) = item.mut_item_attrs() { attrs } else { return Ok(None) }; if let Some(index) = attrs.iter().position(|attr| { - attr.path.segments.first().map_or(false, |segment| segment.ident == "pallet") + attr.path().segments.first().map_or(false, |segment| segment.ident == "pallet") }) { let pallet_attr = attrs.remove(index); Ok(Some(syn::parse2(pallet_attr.into_token_stream())?)) @@ -82,7 +82,7 @@ pub fn get_item_cfg_attrs(attrs: &[syn::Attribute]) -> Vec { attrs .iter() .filter_map(|attr| { - if attr.path.segments.first().map_or(false, |segment| segment.ident == "cfg") { + if attr.path().segments.first().map_or(false, |segment| segment.ident == "cfg") { Some(attr.clone()) } else { None @@ -101,7 +101,6 @@ impl MutItemAttrs for syn::Item { Self::ForeignMod(item) => Some(item.attrs.as_mut()), Self::Impl(item) => Some(item.attrs.as_mut()), Self::Macro(item) => Some(item.attrs.as_mut()), - Self::Macro2(item) => Some(item.attrs.as_mut()), Self::Mod(item) => Some(item.attrs.as_mut()), Self::Static(item) => Some(item.attrs.as_mut()), Self::Struct(item) => Some(item.attrs.as_mut()), @@ -119,7 +118,7 @@ impl MutItemAttrs for syn::TraitItem { fn mut_item_attrs(&mut self) -> Option<&mut Vec> { match self { Self::Const(item) => Some(item.attrs.as_mut()), - Self::Method(item) => Some(item.attrs.as_mut()), + Self::Fn(item) => Some(item.attrs.as_mut()), Self::Type(item) => Some(item.attrs.as_mut()), Self::Macro(item) => Some(item.attrs.as_mut()), _ => None, @@ -139,7 +138,7 @@ impl MutItemAttrs for syn::ItemMod { } } -impl MutItemAttrs for syn::ImplItemMethod { +impl MutItemAttrs for syn::ImplItemFn { fn mut_item_attrs(&mut self) -> Option<&mut Vec> { Some(&mut self.attrs) } diff --git a/frame/support/procedural/src/pallet/parse/hooks.rs b/frame/support/procedural/src/pallet/parse/hooks.rs index f941df5f2..37d7d22f4 100644 --- a/frame/support/procedural/src/pallet/parse/hooks.rs +++ b/frame/support/procedural/src/pallet/parse/hooks.rs @@ -71,7 +71,7 @@ impl HooksDef { } let has_runtime_upgrade = item.items.iter().any(|i| match i { - syn::ImplItem::Method(method) => method.sig.ident == "on_runtime_upgrade", + syn::ImplItem::Fn(method) => method.sig.ident == "on_runtime_upgrade", _ => false, }); diff --git a/frame/support/procedural/src/pallet/parse/storage.rs b/frame/support/procedural/src/pallet/parse/storage.rs index 4e90a423f..16c0851ce 100644 --- a/frame/support/procedural/src/pallet/parse/storage.rs +++ b/frame/support/procedural/src/pallet/parse/storage.rs @@ -159,7 +159,7 @@ pub struct StorageDef { /// The keys and value metadata of the storage. pub metadata: Metadata, /// The doc associated to the storage. - pub docs: Vec, + pub docs: Vec, /// A set of usage of instance, must be check for consistency with config. pub instances: Vec, /// Optional getter to generate. If some then query_kind is ensured to be some as well. @@ -270,7 +270,7 @@ enum StorageKind { /// Check the generics in the `map` contains the generics in `gen` may contains generics in /// `optional_gen`, and doesn't contains any other. fn check_generics( - map: &HashMap, + map: &HashMap, mandatory_generics: &[&str], optional_generics: &[&str], storage_type_name: &str, @@ -331,9 +331,9 @@ fn check_generics( fn process_named_generics( storage: &StorageKind, args_span: proc_macro2::Span, - args: &[syn::Binding], + args: &[syn::AssocType], ) -> syn::Result<(Option, Metadata, Option, bool)> { - let mut parsed = HashMap::::new(); + let mut parsed = HashMap::::new(); // Ensure no duplicate. for arg in args { @@ -610,12 +610,12 @@ fn process_generics( }) .collect::>(); process_unnamed_generics(&storage_kind, args_span, &args, dev_mode) - } else if args.args.iter().all(|gen| matches!(gen, syn::GenericArgument::Binding(_))) { + } else if args.args.iter().all(|gen| matches!(gen, syn::GenericArgument::AssocType(_))) { let args = args .args .iter() .map(|gen| match gen { - syn::GenericArgument::Binding(gen) => gen.clone(), + syn::GenericArgument::AssocType(gen) => gen.clone(), _ => unreachable!("It is asserted above that all generics are bindings"), }) .collect::>(); diff --git a/frame/support/procedural/src/pallet/parse/type_value.rs b/frame/support/procedural/src/pallet/parse/type_value.rs index 4b8050688..4d9db30b3 100644 --- a/frame/support/procedural/src/pallet/parse/type_value.rs +++ b/frame/support/procedural/src/pallet/parse/type_value.rs @@ -39,7 +39,7 @@ pub struct TypeValueDef { /// The span of the pallet::type_value attribute. pub attr_span: proc_macro2::Span, /// Docs on the item. - pub docs: Vec, + pub docs: Vec, } impl TypeValueDef { @@ -57,9 +57,9 @@ impl TypeValueDef { let mut docs = vec![]; for attr in &item.attrs { - if let Ok(syn::Meta::NameValue(meta)) = attr.parse_meta() { + if let syn::Meta::NameValue(meta) = &attr.meta { if meta.path.get_ident().map_or(false, |ident| ident == "doc") { - docs.push(meta.lit); + docs.push(meta.value.clone()); continue } } diff --git a/frame/support/procedural/src/pallet_error.rs b/frame/support/procedural/src/pallet_error.rs index fb0bf52f6..246a5bd4a 100644 --- a/frame/support/procedural/src/pallet_error.rs +++ b/frame/support/procedural/src/pallet_error.rs @@ -17,7 +17,6 @@ use frame_support_procedural_tools::generate_crate_access_2018; use quote::ToTokens; -use std::str::FromStr; // Derive `PalletError` pub fn derive_pallet_error(input: proc_macro::TokenStream) -> proc_macro::TokenStream { @@ -117,38 +116,24 @@ fn generate_field_types( let attrs = &field.attrs; for attr in attrs { - if attr.path.is_ident("codec") { - match attr.parse_meta()? { - syn::Meta::List(ref meta_list) if meta_list.nested.len() == 1 => { - match meta_list - .nested - .first() - .expect("Just checked that there is one item; qed") - { - syn::NestedMeta::Meta(syn::Meta::Path(path)) - if path.get_ident().map_or(false, |i| i == "skip") => - return Ok(None), - - syn::NestedMeta::Meta(syn::Meta::Path(path)) - if path.get_ident().map_or(false, |i| i == "compact") => - { - let field_ty = &field.ty; - return Ok(Some(quote::quote!(#scrate::codec::Compact<#field_ty>))) - }, - - syn::NestedMeta::Meta(syn::Meta::NameValue(syn::MetaNameValue { - path, - lit: syn::Lit::Str(lit_str), - .. - })) if path.get_ident().map_or(false, |i| i == "encoded_as") => { - let ty = proc_macro2::TokenStream::from_str(&lit_str.value())?; - return Ok(Some(ty)) - }, - - _ => (), - } - }, - _ => (), + if attr.path().is_ident("codec") { + let mut res = None; + + attr.parse_nested_meta(|meta| { + if meta.path.is_ident("skip") { + res = Some(None); + } else if meta.path.is_ident("compact") { + let field_ty = &field.ty; + res = Some(Some(quote::quote!(#scrate::codec::Compact<#field_ty>))); + } else if meta.path.is_ident("compact") { + res = Some(Some(meta.value()?.parse()?)); + } + + Ok(()) + })?; + + if let Some(v) = res { + return Ok(v) } } } @@ -163,22 +148,18 @@ fn generate_variant_field_types( let attrs = &variant.attrs; for attr in attrs { - if attr.path.is_ident("codec") { - match attr.parse_meta()? { - syn::Meta::List(ref meta_list) if meta_list.nested.len() == 1 => { - match meta_list - .nested - .first() - .expect("Just checked that there is one item; qed") - { - syn::NestedMeta::Meta(syn::Meta::Path(path)) - if path.get_ident().map_or(false, |i| i == "skip") => - return Ok(None), - - _ => (), - } - }, - _ => (), + if attr.path().is_ident("codec") { + let mut skip = false; + + // We ignore the error intentionally as this isn't `codec(skip)` when + // `parse_nested_meta` fails. + let _ = attr.parse_nested_meta(|meta| { + skip = meta.path.is_ident("skip"); + Ok(()) + }); + + if skip { + return Ok(None) } } } diff --git a/frame/support/procedural/src/storage/genesis_config/genesis_config_def.rs b/frame/support/procedural/src/storage/genesis_config/genesis_config_def.rs index 491db5918..31e9996ee 100644 --- a/frame/support/procedural/src/storage/genesis_config/genesis_config_def.rs +++ b/frame/support/procedural/src/storage/genesis_config/genesis_config_def.rs @@ -133,14 +133,13 @@ impl GenesisConfigDef { .attrs .iter() .map(|attr| { - let meta = attr.parse_meta()?; - if meta.path().is_ident("cfg") { + if attr.meta.path().is_ident("cfg") { return Err(syn::Error::new( - meta.span(), + attr.meta.span(), "extra genesis config items do not support `cfg` attribute", )) } - Ok(meta) + Ok(attr.meta.clone()) }) .collect::>()?; diff --git a/frame/support/procedural/src/storage/mod.rs b/frame/support/procedural/src/storage/mod.rs index 3d3b1443d..c04862256 100644 --- a/frame/support/procedural/src/storage/mod.rs +++ b/frame/support/procedural/src/storage/mod.rs @@ -332,8 +332,8 @@ impl StorageLineDefExt { let doc_attrs = storage_def .attrs .iter() - .filter_map(|a| a.parse_meta().ok()) - .filter(|m| m.path().is_ident("doc")) + .filter(|a| a.meta.path().is_ident("doc")) + .map(|a| a.meta.clone()) .collect(); Self { diff --git a/frame/support/procedural/src/storage/print_pallet_upgrade.rs b/frame/support/procedural/src/storage/print_pallet_upgrade.rs index 03f09a7ed..4e330fd4b 100644 --- a/frame/support/procedural/src/storage/print_pallet_upgrade.rs +++ b/frame/support/procedural/src/storage/print_pallet_upgrade.rs @@ -273,14 +273,12 @@ pub fn maybe_print_pallet_upgrade(def: &super::DeclStorageDefExt) { doc = line.doc_attrs.iter().fold(String::new(), |mut res, attr| { if let syn::Meta::NameValue(name_value) = attr { if name_value.path.is_ident("doc") { - if let syn::Lit::Str(string) = &name_value.lit { - res = format!( - "{} - ///{}", - res, - string.value(), - ); - } + res = format!( + "{} + ///{}", + res, + name_value.value.to_token_stream(), + ); } } res diff --git a/frame/support/procedural/tools/Cargo.toml b/frame/support/procedural/tools/Cargo.toml index 755483f7d..bf828b01b 100644 --- a/frame/support/procedural/tools/Cargo.toml +++ b/frame/support/procedural/tools/Cargo.toml @@ -13,7 +13,7 @@ targets = ["x86_64-unknown-linux-gnu"] [dependencies] proc-macro-crate = "1.1.3" -proc-macro2 = "1.0.37" -quote = "1.0.10" -syn = { version = "1.0.98", features = ["full", "visit", "extra-traits"] } +proc-macro2 = "1.0.56" +quote = "1.0.26" +syn = { version = "2.0.14", features = ["full", "visit", "extra-traits"] } frame-support-procedural-tools-derive = { version = "3.0.0", path = "./derive" } diff --git a/frame/support/procedural/tools/derive/Cargo.toml b/frame/support/procedural/tools/derive/Cargo.toml index a688ee13a..81b5a8487 100644 --- a/frame/support/procedural/tools/derive/Cargo.toml +++ b/frame/support/procedural/tools/derive/Cargo.toml @@ -15,6 +15,6 @@ targets = ["x86_64-unknown-linux-gnu"] proc-macro = true [dependencies] -proc-macro2 = "1.0.37" -quote = { version = "1.0.10", features = ["proc-macro"] } -syn = { version = "1.0.98", features = ["proc-macro", "full", "extra-traits", "parsing"] } +proc-macro2 = "1.0.56" +quote = { version = "1.0.26", features = ["proc-macro"] } +syn = { version = "2.0.14", features = ["proc-macro", "full", "extra-traits", "parsing"] } diff --git a/frame/support/procedural/tools/src/lib.rs b/frame/support/procedural/tools/src/lib.rs index 385b20ad0..541accc8a 100644 --- a/frame/support/procedural/tools/src/lib.rs +++ b/frame/support/procedural/tools/src/lib.rs @@ -102,16 +102,15 @@ pub fn clean_type_string(input: &str) -> String { } /// Return all doc attributes literals found. -pub fn get_doc_literals(attrs: &[syn::Attribute]) -> Vec { +pub fn get_doc_literals(attrs: &[syn::Attribute]) -> Vec { attrs .iter() .filter_map(|attr| { - if let Ok(syn::Meta::NameValue(meta)) = attr.parse_meta() { - if meta.path.get_ident().map_or(false, |ident| ident == "doc") { - Some(meta.lit) - } else { - None - } + if let syn::Meta::NameValue(meta) = &attr.meta { + meta.path + .get_ident() + .filter(|ident| *ident == "doc") + .map(|_| meta.value.clone()) } else { None } diff --git a/frame/support/test/tests/pallet_ui/pallet_doc_arg_non_path.stderr b/frame/support/test/tests/pallet_ui/pallet_doc_arg_non_path.stderr index ba60479c0..9a1249dd3 100644 --- a/frame/support/test/tests/pallet_ui/pallet_doc_arg_non_path.stderr +++ b/frame/support/test/tests/pallet_ui/pallet_doc_arg_non_path.stderr @@ -1,4 +1,4 @@ -error: The `pallet_doc` received an unsupported argument. Supported format: `pallet_doc(PATH)` +error: The `pallet_doc` received an unsupported argument. Supported format: `pallet_doc("PATH")` --> tests/pallet_ui/pallet_doc_arg_non_path.rs:3:1 | 3 | #[pallet_doc(X)] diff --git a/frame/support/test/tests/pallet_ui/pallet_doc_empty.stderr b/frame/support/test/tests/pallet_ui/pallet_doc_empty.stderr index d6a189d79..a220cbe9e 100644 --- a/frame/support/test/tests/pallet_ui/pallet_doc_empty.stderr +++ b/frame/support/test/tests/pallet_ui/pallet_doc_empty.stderr @@ -1,4 +1,4 @@ -error: The `pallet_doc` attribute must receive arguments as a list. Supported format: `pallet_doc(PATH)` +error: The `pallet_doc` received an unsupported argument. Supported format: `pallet_doc("PATH")` --> tests/pallet_ui/pallet_doc_empty.rs:3:1 | 3 | #[pallet_doc] diff --git a/frame/support/test/tests/pallet_ui/pallet_doc_invalid_arg.stderr b/frame/support/test/tests/pallet_ui/pallet_doc_invalid_arg.stderr index 9dd039789..bee7c7085 100644 --- a/frame/support/test/tests/pallet_ui/pallet_doc_invalid_arg.stderr +++ b/frame/support/test/tests/pallet_ui/pallet_doc_invalid_arg.stderr @@ -1,4 +1,4 @@ -error: The `pallet_doc` attribute must receive arguments as a list. Supported format: `pallet_doc(PATH)` +error: The `pallet_doc` received an unsupported argument. Supported format: `pallet_doc("PATH")` --> tests/pallet_ui/pallet_doc_invalid_arg.rs:3:1 | 3 | #[pallet_doc = "invalid"] diff --git a/frame/support/test/tests/pallet_ui/pallet_doc_multiple_args.stderr b/frame/support/test/tests/pallet_ui/pallet_doc_multiple_args.stderr index 58ad75a0a..e76955543 100644 --- a/frame/support/test/tests/pallet_ui/pallet_doc_multiple_args.stderr +++ b/frame/support/test/tests/pallet_ui/pallet_doc_multiple_args.stderr @@ -1,4 +1,4 @@ -error: The `pallet_doc` attribute must receive only one argument. Supported format: `pallet_doc(PATH)` +error: The `pallet_doc` received an unsupported argument. Supported format: `pallet_doc("PATH")` --> tests/pallet_ui/pallet_doc_multiple_args.rs:3:1 | 3 | #[pallet_doc("A", "B")] diff --git a/frame/support/test/tests/pallet_ui/storage_result_query_parenthesized_generics.stderr b/frame/support/test/tests/pallet_ui/storage_result_query_parenthesized_generics.stderr index caffd846f..89ddd1599 100644 --- a/frame/support/test/tests/pallet_ui/storage_result_query_parenthesized_generics.stderr +++ b/frame/support/test/tests/pallet_ui/storage_result_query_parenthesized_generics.stderr @@ -1,5 +1,5 @@ -error: Invalid pallet::storage, unexpected generic args for ResultQuery, expected angle-bracketed arguments, found `(NonExistentValue)` +error: expected `,` --> tests/pallet_ui/storage_result_query_parenthesized_generics.rs:18:55 | 18 | type Foo = StorageValue<_, u8, ResultQuery(NonExistentValue)>; - | ^^^^^^^^^^^^^^^^^^ + | ^ diff --git a/primitives/api/proc-macro/Cargo.toml b/primitives/api/proc-macro/Cargo.toml index 1ba62cc53..594c20e82 100644 --- a/primitives/api/proc-macro/Cargo.toml +++ b/primitives/api/proc-macro/Cargo.toml @@ -16,9 +16,9 @@ targets = ["x86_64-unknown-linux-gnu"] proc-macro = true [dependencies] -quote = "1.0.10" -syn = { version = "1.0.98", features = ["full", "fold", "extra-traits", "visit"] } -proc-macro2 = "1.0.37" +quote = "1.0.26" +syn = { version = "2.0.14", features = ["full", "fold", "extra-traits", "visit"] } +proc-macro2 = "1.0.56" blake2 = { version = "0.10.4", default-features = false } proc-macro-crate = "1.1.3" expander = "1.0.0" diff --git a/primitives/api/proc-macro/src/decl_runtime_apis.rs b/primitives/api/proc-macro/src/decl_runtime_apis.rs index 43e1e7969..cde33c190 100644 --- a/primitives/api/proc-macro/src/decl_runtime_apis.rs +++ b/primitives/api/proc-macro/src/decl_runtime_apis.rs @@ -38,9 +38,10 @@ use syn::{ parse::{Error, Parse, ParseStream, Result}, parse_macro_input, parse_quote, spanned::Spanned, + token::Comma, visit::{self, Visit}, - Attribute, FnArg, GenericParam, Generics, Ident, ItemTrait, Lit, Meta, NestedMeta, TraitBound, - TraitItem, TraitItemMethod, + Attribute, FnArg, GenericParam, Generics, Ident, ItemTrait, LitInt, LitStr, TraitBound, + TraitItem, TraitItemFn, }; use std::collections::{BTreeMap, HashMap}; @@ -76,7 +77,7 @@ fn extend_generics_with_block(generics: &mut Generics) { /// attribute body as `TokenStream`. fn remove_supported_attributes(attrs: &mut Vec) -> HashMap<&'static str, Attribute> { let mut result = HashMap::new(); - attrs.retain(|v| match SUPPORTED_ATTRIBUTE_NAMES.iter().find(|a| v.path.is_ident(a)) { + attrs.retain(|v| match SUPPORTED_ATTRIBUTE_NAMES.iter().find(|a| v.path().is_ident(a)) { Some(attribute) => { result.insert(*attribute, v.clone()); false @@ -159,7 +160,7 @@ impl Fold for ReplaceBlockWithNodeBlock { /// ``` fn generate_versioned_api_traits( api: ItemTrait, - methods: BTreeMap>, + methods: BTreeMap>, ) -> Vec { let mut result = Vec::::new(); for (version, _) in &methods { @@ -169,7 +170,7 @@ fn generate_versioned_api_traits( // Add the methods from the current version and all previous one. Versions are sorted so // it's safe to stop early. for (_, m) in methods.iter().take_while(|(v, _)| v <= &version) { - versioned_trait.items.extend(m.iter().cloned().map(|m| TraitItem::Method(m))); + versioned_trait.items.extend(m.iter().cloned().map(|m| TraitItem::Fn(m))); } result.push(versioned_trait); @@ -180,37 +181,29 @@ fn generate_versioned_api_traits( /// Try to parse the given `Attribute` as `renamed` attribute. fn parse_renamed_attribute(renamed: &Attribute) -> Result<(String, u32)> { - let meta = renamed.parse_meta()?; - - let err = Err(Error::new( - meta.span(), + let err = || { + Error::new( + renamed.span(), &format!( - "Unexpected `{renamed}` attribute. The supported format is `{renamed}(\"old_name\", version_it_was_renamed)`", - renamed = RENAMED_ATTRIBUTE, - ) + "Unexpected `{RENAMED_ATTRIBUTE}` attribute. \ + The supported format is `{RENAMED_ATTRIBUTE}(\"old_name\", version_it_was_renamed)`", + ), ) - ); - - match meta { - Meta::List(list) => - if list.nested.len() > 2 && list.nested.is_empty() { - err - } else { - let mut itr = list.nested.iter(); - let old_name = match itr.next() { - Some(NestedMeta::Lit(Lit::Str(i))) => i.value(), - _ => return err, - }; - - let version = match itr.next() { - Some(NestedMeta::Lit(Lit::Int(i))) => i.base10_parse()?, - _ => return err, - }; - - Ok((old_name, version)) - }, - _ => err, - } + }; + + renamed + .parse_args_with(|input: ParseStream| { + let old_name: LitStr = input.parse()?; + let _comma: Comma = input.parse()?; + let version: LitInt = input.parse()?; + + if !input.is_empty() { + return Err(input.error("No more arguments expected")) + } + + Ok((old_name.value(), version.base10_parse()?)) + }) + .map_err(|_| err()) } /// Generate the declaration of the trait for the runtime. @@ -230,12 +223,12 @@ fn generate_runtime_decls(decls: &[ItemTrait]) -> Result { let trait_api_version = get_api_version(&found_attributes)?; - let mut methods_by_version: BTreeMap> = BTreeMap::new(); + let mut methods_by_version: BTreeMap> = BTreeMap::new(); // Process the items in the declaration. The filter_map function below does a lot of stuff // because the method attributes are stripped at this point decl.items.iter_mut().for_each(|i| match i { - TraitItem::Method(ref mut method) => { + TraitItem::Fn(ref mut method) => { let method_attrs = remove_supported_attributes(&mut method.attrs); let mut method_version = trait_api_version; // validate the api version for the method (if any) and generate default @@ -364,9 +357,8 @@ impl<'a> ToClientSideDecl<'a> { let mut result = Vec::new(); items.into_iter().for_each(|i| match i { - TraitItem::Method(method) => { - let (fn_decl, fn_decl_ctx) = - self.fold_trait_item_method(method, trait_generics_num); + TraitItem::Fn(method) => { + let (fn_decl, fn_decl_ctx) = self.fold_trait_item_fn(method, trait_generics_num); result.push(fn_decl.into()); result.push(fn_decl_ctx.into()); }, @@ -376,11 +368,11 @@ impl<'a> ToClientSideDecl<'a> { result } - fn fold_trait_item_method( + fn fold_trait_item_fn( &mut self, - method: TraitItemMethod, + method: TraitItemFn, trait_generics_num: usize, - ) -> (TraitItemMethod, TraitItemMethod) { + ) -> (TraitItemFn, TraitItemFn) { let crate_ = self.crate_; let context = quote!( #crate_::ExecutionContext::OffchainCall(None) ); let fn_decl = self.create_method_decl(method.clone(), context, trait_generics_num); @@ -391,9 +383,9 @@ impl<'a> ToClientSideDecl<'a> { fn create_method_decl_with_context( &mut self, - method: TraitItemMethod, + method: TraitItemFn, trait_generics_num: usize, - ) -> TraitItemMethod { + ) -> TraitItemFn { let crate_ = self.crate_; let context_arg: syn::FnArg = parse_quote!( context: #crate_::ExecutionContext ); let mut fn_decl_ctx = self.create_method_decl(method, quote!(context), trait_generics_num); @@ -409,10 +401,10 @@ impl<'a> ToClientSideDecl<'a> { /// the actual call into the runtime. fn create_method_decl( &mut self, - mut method: TraitItemMethod, + mut method: TraitItemFn, context: TokenStream, trait_generics_num: usize, - ) -> TraitItemMethod { + ) -> TraitItemFn { let params = match extract_parameter_names_types_and_borrows( &method.sig, AllowSelfRefInParameters::No, @@ -661,7 +653,7 @@ impl CheckTraitDecl { /// All errors will be collected in `self.errors`. fn check(&mut self, trait_: &ItemTrait) { self.check_method_declarations(trait_.items.iter().filter_map(|i| match i { - TraitItem::Method(method) => Some(method), + TraitItem::Fn(method) => Some(method), _ => None, })); @@ -671,10 +663,7 @@ impl CheckTraitDecl { /// Check that the given method declarations are correct. /// /// Any error is stored in `self.errors`. - fn check_method_declarations<'a>( - &mut self, - methods: impl Iterator, - ) { + fn check_method_declarations<'a>(&mut self, methods: impl Iterator) { let mut method_to_signature_changed = HashMap::>>::new(); methods.into_iter().for_each(|method| { diff --git a/primitives/api/proc-macro/src/impl_runtime_apis.rs b/primitives/api/proc-macro/src/impl_runtime_apis.rs index c0da8ccf3..b8dcf625d 100644 --- a/primitives/api/proc-macro/src/impl_runtime_apis.rs +++ b/primitives/api/proc-macro/src/impl_runtime_apis.rs @@ -139,7 +139,7 @@ fn generate_impl_calls( .ident; for item in &impl_.items { - if let ImplItem::Method(method) = item { + if let ImplItem::Fn(method) = item { let impl_call = generate_impl_call(&method.sig, &impl_.self_ty, input, &impl_trait)?; @@ -720,7 +720,7 @@ fn impl_runtime_apis_impl_inner(api_impls: &[ItemImpl]) -> Result { // Filters all attributes except the cfg ones. fn filter_cfg_attrs(attrs: &[Attribute]) -> Vec { - attrs.iter().filter(|a| a.path.is_ident("cfg")).cloned().collect() + attrs.iter().filter(|a| a.path().is_ident("cfg")).cloned().collect() } // Extracts the value of `API_VERSION_ATTRIBUTE` and handles errors. @@ -732,7 +732,7 @@ fn extract_api_version(attrs: &Vec, span: Span) -> Result // First fetch all `API_VERSION_ATTRIBUTE` values (should be only one) let api_ver = attrs .iter() - .filter(|a| a.path.is_ident(API_VERSION_ATTRIBUTE)) + .filter(|a| a.path().is_ident(API_VERSION_ATTRIBUTE)) .collect::>(); if api_ver.len() > 1 { diff --git a/primitives/api/proc-macro/src/mock_impl_runtime_apis.rs b/primitives/api/proc-macro/src/mock_impl_runtime_apis.rs index fc0a754e2..be8c8ca0f 100644 --- a/primitives/api/proc-macro/src/mock_impl_runtime_apis.rs +++ b/primitives/api/proc-macro/src/mock_impl_runtime_apis.rs @@ -192,7 +192,7 @@ fn implement_common_api_traits(block_type: TypePath, self_ty: Type) -> Result) -> bool { let mut found = false; attributes.retain(|attr| { - if attr.path.is_ident(ADVANCED_ATTRIBUTE) { + if attr.path().is_ident(ADVANCED_ATTRIBUTE) { found = true; false } else { @@ -258,7 +258,7 @@ impl<'a> FoldRuntimeApiImpl<'a> { // We also need to overwrite all the `_with_context` methods. To do this, // we clone all methods and add them again with the new name plus one more argument. impl_item.items.extend(impl_item.items.clone().into_iter().filter_map(|i| { - if let syn::ImplItem::Method(mut m) = i { + if let syn::ImplItem::Fn(mut m) = i { m.sig.ident = quote::format_ident!("{}_with_context", m.sig.ident); m.sig.inputs.insert(2, parse_quote!( _: #crate_::ExecutionContext )); @@ -290,7 +290,7 @@ impl<'a> FoldRuntimeApiImpl<'a> { } impl<'a> Fold for FoldRuntimeApiImpl<'a> { - fn fold_impl_item_method(&mut self, mut input: syn::ImplItemMethod) -> syn::ImplItemMethod { + fn fold_impl_item_fn(&mut self, mut input: syn::ImplItemFn) -> syn::ImplItemFn { let block = { let crate_ = generate_crate_access(); let is_advanced = has_advanced_attribute(&mut input.attrs); @@ -377,7 +377,7 @@ impl<'a> Fold for FoldRuntimeApiImpl<'a> { ) }; - let mut input = fold::fold_impl_item_method(self, input); + let mut input = fold::fold_impl_item_fn(self, input); // We need to set the block, after we modified the rest of the ast, otherwise we would // modify our generated block as well. input.block = block; diff --git a/primitives/api/proc-macro/src/runtime_metadata.rs b/primitives/api/proc-macro/src/runtime_metadata.rs index 02b03baea..ae78fb52d 100644 --- a/primitives/api/proc-macro/src/runtime_metadata.rs +++ b/primitives/api/proc-macro/src/runtime_metadata.rs @@ -88,13 +88,13 @@ pub fn generate_decl_runtime_metadata(decl: &ItemTrait) -> TokenStream2 { let mut where_clause = Vec::new(); for item in &decl.items { // Collect metadata for methods only. - let syn::TraitItem::Method(method) = item else { + let syn::TraitItem::Fn(method) = item else { continue }; // Collect metadata only for the latest methods. let is_changed_in = - method.attrs.iter().any(|attr| attr.path.is_ident(CHANGED_IN_ATTRIBUTE)); + method.attrs.iter().any(|attr| attr.path().is_ident(CHANGED_IN_ATTRIBUTE)); if is_changed_in { continue } diff --git a/primitives/api/proc-macro/src/utils.rs b/primitives/api/proc-macro/src/utils.rs index cffaf317f..6716be142 100644 --- a/primitives/api/proc-macro/src/utils.rs +++ b/primitives/api/proc-macro/src/utils.rs @@ -22,7 +22,7 @@ use syn::{ ImplItem, ItemImpl, Pat, Path, PathArguments, Result, ReturnType, Signature, Type, TypePath, }; -use quote::{format_ident, quote}; +use quote::{format_ident, quote, ToTokens}; use proc_macro_crate::{crate_name, FoundCrate}; @@ -158,7 +158,7 @@ pub fn extract_all_signature_types(items: &[ImplItem]) -> Vec { items .iter() .filter_map(|i| match i { - ImplItem::Method(method) => Some(&method.sig), + ImplItem::Fn(method) => Some(&method.sig), _ => None, }) .flat_map(|sig| { @@ -263,18 +263,20 @@ pub fn get_doc_literals(attrs: &[syn::Attribute]) -> Vec { attrs .iter() .filter_map(|attr| { - let Ok(syn::Meta::NameValue(meta)) = attr.parse_meta() else { + let syn::Meta::NameValue(meta) = &attr.meta else { return None }; - - meta.path.get_ident().filter(|ident| *ident == "doc").map(|_| meta.lit) + let Ok(lit) = syn::parse2::(meta.value.to_token_stream()) else { + unreachable!("non-lit doc attribute values do not exist"); + }; + meta.path.get_ident().filter(|ident| *ident == "doc").map(|_| lit) }) .collect() } /// Filters all attributes except the cfg ones. pub fn filter_cfg_attributes(attrs: &[syn::Attribute]) -> Vec { - attrs.iter().filter(|a| a.path.is_ident("cfg")).cloned().collect() + attrs.iter().filter(|a| a.path().is_ident("cfg")).cloned().collect() } #[cfg(test)] diff --git a/primitives/arithmetic/src/per_things.rs b/primitives/arithmetic/src/per_things.rs index c4cb51966..611a4e05e 100644 --- a/primitives/arithmetic/src/per_things.rs +++ b/primitives/arithmetic/src/per_things.rs @@ -527,16 +527,18 @@ where rem_mul_div_inner += 1.into(); } }, - Rounding::NearestPrefDown => + Rounding::NearestPrefDown => { if rem_mul_upper % denom_upper > denom_upper / 2.into() { // `rem * numer / denom` is less than `numer`, so this will not overflow. rem_mul_div_inner += 1.into(); - }, - Rounding::NearestPrefUp => + } + }, + Rounding::NearestPrefUp => { if rem_mul_upper % denom_upper >= denom_upper / 2.into() + denom_upper % 2.into() { // `rem * numer / denom` is less than `numer`, so this will not overflow. rem_mul_div_inner += 1.into(); - }, + } + }, } rem_mul_div_inner.into() } diff --git a/primitives/core/hashing/proc-macro/Cargo.toml b/primitives/core/hashing/proc-macro/Cargo.toml index 6d9747de8..66e9667c8 100644 --- a/primitives/core/hashing/proc-macro/Cargo.toml +++ b/primitives/core/hashing/proc-macro/Cargo.toml @@ -16,7 +16,7 @@ targets = ["x86_64-unknown-linux-gnu"] proc-macro = true [dependencies] -proc-macro2 = "1.0.37" -quote = "1.0.6" -syn = { version = "1.0.98", features = ["full", "parsing"] } +proc-macro2 = "1.0.56" +quote = "1.0.26" +syn = { version = "2.0.14", features = ["full", "parsing"] } sp-core-hashing = { version = "5.0.0", default-features = false, path = "../" } diff --git a/primitives/debug-derive/Cargo.toml b/primitives/debug-derive/Cargo.toml index a903b7044..183d7bd5a 100644 --- a/primitives/debug-derive/Cargo.toml +++ b/primitives/debug-derive/Cargo.toml @@ -17,9 +17,9 @@ targets = ["x86_64-unknown-linux-gnu"] proc-macro = true [dependencies] -quote = "1.0.10" -syn = "1.0.98" -proc-macro2 = "1.0" +quote = "1.0.26" +syn = "2.0.14" +proc-macro2 = "1.0.56" [features] default = [ "std" ] diff --git a/primitives/runtime-interface/proc-macro/Cargo.toml b/primitives/runtime-interface/proc-macro/Cargo.toml index 9bc716101..3a63c1ef5 100644 --- a/primitives/runtime-interface/proc-macro/Cargo.toml +++ b/primitives/runtime-interface/proc-macro/Cargo.toml @@ -18,6 +18,6 @@ proc-macro = true [dependencies] Inflector = "0.11.4" proc-macro-crate = "1.1.3" -proc-macro2 = "1.0.37" -quote = "1.0.10" -syn = { version = "1.0.98", features = ["full", "visit", "fold", "extra-traits"] } +proc-macro2 = "1.0.56" +quote = "1.0.26" +syn = { version = "2.0.14", features = ["full", "visit", "fold", "extra-traits"] } diff --git a/primitives/runtime-interface/proc-macro/src/runtime_interface/bare_function_interface.rs b/primitives/runtime-interface/proc-macro/src/runtime_interface/bare_function_interface.rs index 16c1cffb7..77a29bec3 100644 --- a/primitives/runtime-interface/proc-macro/src/runtime_interface/bare_function_interface.rs +++ b/primitives/runtime-interface/proc-macro/src/runtime_interface/bare_function_interface.rs @@ -36,8 +36,7 @@ use crate::utils::{ }; use syn::{ - parse_quote, spanned::Spanned, FnArg, Ident, ItemTrait, Result, Signature, Token, - TraitItemMethod, + parse_quote, spanned::Spanned, FnArg, Ident, ItemTrait, Result, Signature, Token, TraitItemFn, }; use proc_macro2::{Span, TokenStream}; @@ -116,7 +115,7 @@ fn function_no_std_impl( quote! {} }; - let attrs = method.attrs.iter().filter(|a| !a.path.is_ident("version")); + let attrs = method.attrs.iter().filter(|a| !a.path().is_ident("version")); let cfg_wasm_only = if is_wasm_only { quote! { #[cfg(target_arch = "wasm32")] } @@ -139,12 +138,12 @@ fn function_no_std_impl( /// Generate call to latest function version for `cfg((feature = "std")` /// /// This should generate simple `fn func(..) { func_version_(..) }`. -fn function_std_latest_impl(method: &TraitItemMethod, latest_version: u32) -> Result { +fn function_std_latest_impl(method: &TraitItemFn, latest_version: u32) -> Result { let function_name = &method.sig.ident; let args = get_function_arguments(&method.sig).map(FnArg::Typed); let arg_names = get_function_argument_names(&method.sig).collect::>(); let return_value = &method.sig.output; - let attrs = method.attrs.iter().filter(|a| !a.path.is_ident("version")); + let attrs = method.attrs.iter().filter(|a| !a.path().is_ident("version")); let latest_function_name = create_function_ident_with_version(&method.sig.ident, latest_version); @@ -162,7 +161,7 @@ fn function_std_latest_impl(method: &TraitItemMethod, latest_version: u32) -> Re /// Generates the bare function implementation for `cfg(feature = "std")`. fn function_std_impl( trait_name: &Ident, - method: &TraitItemMethod, + method: &TraitItemFn, version: u32, is_wasm_only: bool, tracing: bool, @@ -185,7 +184,7 @@ fn function_std_impl( .take(1), ); let return_value = &method.sig.output; - let attrs = method.attrs.iter().filter(|a| !a.path.is_ident("version")); + let attrs = method.attrs.iter().filter(|a| !a.path().is_ident("version")); // Don't make the function public accessible when this is a wasm only interface. let call_to_trait = generate_call_to_trait(trait_name, method, version, is_wasm_only); let call_to_trait = if !tracing { @@ -210,7 +209,7 @@ fn function_std_impl( /// Generate the call to the interface trait. fn generate_call_to_trait( trait_name: &Ident, - method: &TraitItemMethod, + method: &TraitItemFn, version: u32, is_wasm_only: bool, ) -> TokenStream { diff --git a/primitives/runtime-interface/proc-macro/src/runtime_interface/host_function_interface.rs b/primitives/runtime-interface/proc-macro/src/runtime_interface/host_function_interface.rs index fb751c69b..f3cdcf7fd 100644 --- a/primitives/runtime-interface/proc-macro/src/runtime_interface/host_function_interface.rs +++ b/primitives/runtime-interface/proc-macro/src/runtime_interface/host_function_interface.rs @@ -30,7 +30,7 @@ use crate::utils::{ }; use syn::{ - spanned::Spanned, Error, Ident, ItemTrait, Pat, Result, ReturnType, Signature, TraitItemMethod, + spanned::Spanned, Error, Ident, ItemTrait, Pat, Result, ReturnType, Signature, TraitItemFn, }; use proc_macro2::{Span, TokenStream}; @@ -78,7 +78,7 @@ pub fn generate(trait_def: &ItemTrait, is_wasm_only: bool) -> Result Result { @@ -136,7 +136,7 @@ fn generate_extern_host_function( } /// Generate the host exchangeable function for the given method. -fn generate_exchangeable_host_function(method: &TraitItemMethod) -> Result { +fn generate_exchangeable_host_function(method: &TraitItemFn) -> Result { let crate_ = generate_crate_access(); let arg_types = get_function_argument_types(&method.sig); let function = &method.sig.ident; diff --git a/primitives/runtime-interface/proc-macro/src/runtime_interface/trait_decl_impl.rs b/primitives/runtime-interface/proc-macro/src/runtime_interface/trait_decl_impl.rs index 4a3a688e5..540e930b0 100644 --- a/primitives/runtime-interface/proc-macro/src/runtime_interface/trait_decl_impl.rs +++ b/primitives/runtime-interface/proc-macro/src/runtime_interface/trait_decl_impl.rs @@ -26,7 +26,7 @@ use crate::utils::{ use syn::{ fold::{self, Fold}, spanned::Spanned, - Error, Generics, ItemTrait, Receiver, Result, TraitItemMethod, Type, Visibility, + Error, Generics, ItemTrait, Receiver, Result, TraitItemFn, Type, Visibility, }; use proc_macro2::TokenStream; @@ -51,7 +51,7 @@ pub fn process(trait_def: &ItemTrait, is_wasm_only: bool) -> Result struct ToEssentialTraitDef { /// All errors found while doing the conversion. errors: Vec, - methods: Vec, + methods: Vec, } impl ToEssentialTraitDef { @@ -59,7 +59,7 @@ impl ToEssentialTraitDef { ToEssentialTraitDef { errors: vec![], methods: vec![] } } - fn into_methods(self) -> Result> { + fn into_methods(self) -> Result> { let mut errors = self.errors; let methods = self.methods; if let Some(first_error) = errors.pop() { @@ -72,8 +72,8 @@ impl ToEssentialTraitDef { } } - fn process(&mut self, method: &TraitItemMethod, version: u32) { - let mut folded = self.fold_trait_item_method(method.clone()); + fn process(&mut self, method: &TraitItemFn, version: u32) { + let mut folded = self.fold_trait_item_fn(method.clone()); folded.sig.ident = create_function_ident_with_version(&folded.sig.ident, version); self.methods.push(folded); } @@ -90,7 +90,7 @@ impl ToEssentialTraitDef { } impl Fold for ToEssentialTraitDef { - fn fold_trait_item_method(&mut self, mut method: TraitItemMethod) -> TraitItemMethod { + fn fold_trait_item_fn(&mut self, mut method: TraitItemFn) -> TraitItemFn { if method.default.take().is_none() { self.push_error(&method, "Methods need to have an implementation."); } @@ -105,9 +105,9 @@ impl Fold for ToEssentialTraitDef { self.error_on_generic_parameters(&method.sig.generics); - method.attrs.retain(|a| !a.path.is_ident("version")); + method.attrs.retain(|a| !a.path().is_ident("version")); - fold::fold_trait_item_method(self, method) + fold::fold_trait_item_fn(self, method) } fn fold_item_trait(&mut self, mut trait_def: ItemTrait) -> ItemTrait { @@ -154,7 +154,7 @@ fn impl_trait_for_externalities(trait_def: &ItemTrait, is_wasm_only: bool) -> Re let interface = get_runtime_interface(trait_def)?; let methods = interface.all_versions().map(|(version, method)| { let mut cloned = (*method).clone(); - cloned.attrs.retain(|a| !a.path.is_ident("version")); + cloned.attrs.retain(|a| !a.path().is_ident("version")); cloned.sig.ident = create_function_ident_with_version(&cloned.sig.ident, version); cloned }); diff --git a/primitives/runtime-interface/proc-macro/src/utils.rs b/primitives/runtime-interface/proc-macro/src/utils.rs index 07f9b5ce0..9818fd684 100644 --- a/primitives/runtime-interface/proc-macro/src/utils.rs +++ b/primitives/runtime-interface/proc-macro/src/utils.rs @@ -21,7 +21,7 @@ use proc_macro2::{Span, TokenStream}; use syn::{ parse::Parse, parse_quote, spanned::Spanned, token, Error, FnArg, Ident, ItemTrait, LitInt, - Pat, PatType, Result, Signature, TraitItem, TraitItemMethod, Type, + Pat, PatType, Result, Signature, TraitItem, TraitItemFn, Type, }; use proc_macro_crate::{crate_name, FoundCrate}; @@ -41,23 +41,23 @@ mod attributes { /// A concrete, specific version of a runtime interface function. pub struct RuntimeInterfaceFunction { - item: TraitItemMethod, + item: TraitItemFn, should_trap_on_return: bool, } impl std::ops::Deref for RuntimeInterfaceFunction { - type Target = TraitItemMethod; + type Target = TraitItemFn; fn deref(&self) -> &Self::Target { &self.item } } impl RuntimeInterfaceFunction { - fn new(item: &TraitItemMethod) -> Result { + fn new(item: &TraitItemFn) -> Result { let mut item = item.clone(); let mut should_trap_on_return = false; item.attrs.retain(|attr| { - if attr.path.is_ident("trap_on_return") { + if attr.path().is_ident("trap_on_return") { should_trap_on_return = true; false } else { @@ -87,7 +87,7 @@ struct RuntimeInterfaceFunctionSet { } impl RuntimeInterfaceFunctionSet { - fn new(version: VersionAttribute, trait_item: &TraitItemMethod) -> Result { + fn new(version: VersionAttribute, trait_item: &TraitItemFn) -> Result { Ok(Self { latest_version_to_call: version.is_callable().then(|| version.version), versions: BTreeMap::from([( @@ -115,11 +115,7 @@ impl RuntimeInterfaceFunctionSet { } /// Add a different version of the function. - fn add_version( - &mut self, - version: VersionAttribute, - trait_item: &TraitItemMethod, - ) -> Result<()> { + fn add_version(&mut self, version: VersionAttribute, trait_item: &TraitItemFn) -> Result<()> { if let Some(existing_item) = self.versions.get(&version.version) { let mut err = Error::new(trait_item.span(), "Duplicated version attribute"); err.combine(Error::new( @@ -275,9 +271,9 @@ pub fn get_function_argument_types_ref_and_mut( } /// Returns an iterator over all trait methods for the given trait definition. -fn get_trait_methods(trait_def: &ItemTrait) -> impl Iterator { +fn get_trait_methods(trait_def: &ItemTrait) -> impl Iterator { trait_def.items.iter().filter_map(|i| match i { - TraitItem::Method(ref method) => Some(method), + TraitItem::Fn(ref method) => Some(method), _ => None, }) } @@ -326,10 +322,10 @@ impl Parse for VersionAttribute { } /// Return [`VersionAttribute`], if present. -fn get_item_version(item: &TraitItemMethod) -> Result> { +fn get_item_version(item: &TraitItemFn) -> Result> { item.attrs .iter() - .find(|attr| attr.path.is_ident("version")) + .find(|attr| attr.path().is_ident("version")) .map(|attr| attr.parse_args()) .transpose() } diff --git a/primitives/version/proc-macro/Cargo.toml b/primitives/version/proc-macro/Cargo.toml index abe9e579a..aac41dd43 100644 --- a/primitives/version/proc-macro/Cargo.toml +++ b/primitives/version/proc-macro/Cargo.toml @@ -17,9 +17,9 @@ proc-macro = true [dependencies] codec = { package = "parity-scale-codec", version = "3.2.2", features = [ "derive" ] } -proc-macro2 = "1.0.37" -quote = "1.0.10" -syn = { version = "1.0.98", features = ["full", "fold", "extra-traits", "visit"] } +proc-macro2 = "1.0.56" +quote = "1.0.26" +syn = { version = "2.0.14", features = ["full", "fold", "extra-traits", "visit"] } [dev-dependencies] sp-version = { version = "5.0.0", path = ".." } diff --git a/test-utils/derive/Cargo.toml b/test-utils/derive/Cargo.toml index b55ace822..f3e38ada2 100644 --- a/test-utils/derive/Cargo.toml +++ b/test-utils/derive/Cargo.toml @@ -11,9 +11,9 @@ publish = false [dependencies] proc-macro-crate = "1.1.3" -proc-macro2 = "1.0.37" -quote = "1.0.10" -syn = { version = "1.0.98", features = ["full"] } +proc-macro2 = "1.0.56" +quote = "1.0.26" +syn = { version = "2.0.14", features = ["full"] } [lib] proc-macro = true From 07c77323010d54410143103c2617ebc12e0a12f3 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 12 Apr 2023 22:50:31 +0000 Subject: [PATCH 369/558] Bump zstd from 0.11.2+zstd.1.5.2 to 0.12.3+zstd.1.5.2 (#13671) Bumps [zstd](https://github.com/gyscos/zstd-rs) from 0.11.2+zstd.1.5.2 to 0.12.3+zstd.1.5.2. - [Release notes](https://github.com/gyscos/zstd-rs/releases) - [Commits](https://github.com/gyscos/zstd-rs/commits) --- updated-dependencies: - dependency-name: zstd dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- Cargo.lock | 31 +++++++++++++++++---- frame/state-trie-migration/Cargo.toml | 2 +- primitives/maybe-compressed-blob/Cargo.toml | 2 +- primitives/runtime/Cargo.toml | 2 +- utils/frame/try-runtime/cli/Cargo.toml | 2 +- 5 files changed, 29 insertions(+), 10 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index b0a96af54..fc0b37bae 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -6836,7 +6836,7 @@ dependencies = [ "substrate-state-trie-migration-rpc", "thousands", "tokio", - "zstd", + "zstd 0.12.3+zstd.1.5.2", ] [[package]] @@ -10592,7 +10592,7 @@ name = "sp-maybe-compressed-blob" version = "4.1.0-dev" dependencies = [ "thiserror", - "zstd", + "zstd 0.12.3+zstd.1.5.2", ] [[package]] @@ -10703,7 +10703,7 @@ dependencies = [ "sp-tracing", "sp-weights", "substrate-test-runtime-client", - "zstd", + "zstd 0.12.3+zstd.1.5.2", ] [[package]] @@ -12052,7 +12052,7 @@ dependencies = [ "substrate-cli-test-utils", "substrate-rpc-client", "tokio", - "zstd", + "zstd 0.12.3+zstd.1.5.2", ] [[package]] @@ -12596,7 +12596,7 @@ dependencies = [ "sha2 0.10.6", "toml", "windows-sys 0.42.0", - "zstd", + "zstd 0.11.2+zstd.1.5.2", ] [[package]] @@ -13317,7 +13317,16 @@ version = "0.11.2+zstd.1.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "20cc960326ece64f010d2d2107537f26dc589a6573a316bd5b1dba685fa5fde4" dependencies = [ - "zstd-safe", + "zstd-safe 5.0.2+zstd.1.5.2", +] + +[[package]] +name = "zstd" +version = "0.12.3+zstd.1.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "76eea132fb024e0e13fd9c2f5d5d595d8a967aa72382ac2f9d39fcc95afd0806" +dependencies = [ + "zstd-safe 6.0.5+zstd.1.5.4", ] [[package]] @@ -13330,6 +13339,16 @@ dependencies = [ "zstd-sys", ] +[[package]] +name = "zstd-safe" +version = "6.0.5+zstd.1.5.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d56d9e60b4b1758206c238a10165fbcae3ca37b01744e394c463463f6529d23b" +dependencies = [ + "libc", + "zstd-sys", +] + [[package]] name = "zstd-sys" version = "2.0.7+zstd.1.5.4" diff --git a/frame/state-trie-migration/Cargo.toml b/frame/state-trie-migration/Cargo.toml index 8485db5a3..9cd875932 100644 --- a/frame/state-trie-migration/Cargo.toml +++ b/frame/state-trie-migration/Cargo.toml @@ -17,7 +17,7 @@ log = { version = "0.4.17", default-features = false } scale-info = { version = "2.5.0", default-features = false, features = ["derive"] } serde = { version = "1.0.133", optional = true } thousands = { version = "0.2.0", optional = true } -zstd = { version = "0.11.2", default-features = false, optional = true } +zstd = { version = "0.12.3", default-features = false, optional = true } frame-benchmarking = { default-features = false, optional = true, path = "../benchmarking" } frame-support = { default-features = false, path = "../support" } frame-system = { default-features = false, path = "../system" } diff --git a/primitives/maybe-compressed-blob/Cargo.toml b/primitives/maybe-compressed-blob/Cargo.toml index 7e2780aa2..67f93c517 100644 --- a/primitives/maybe-compressed-blob/Cargo.toml +++ b/primitives/maybe-compressed-blob/Cargo.toml @@ -12,4 +12,4 @@ readme = "README.md" [dependencies] thiserror = "1.0" -zstd = { version = "0.11.2", default-features = false } +zstd = { version = "0.12.3", default-features = false } diff --git a/primitives/runtime/Cargo.toml b/primitives/runtime/Cargo.toml index 056cfa3dd..4ec55162e 100644 --- a/primitives/runtime/Cargo.toml +++ b/primitives/runtime/Cargo.toml @@ -33,7 +33,7 @@ sp-weights = { version = "4.0.0", default-features = false, path = "../weights" [dev-dependencies] rand = "0.8.5" serde_json = "1.0.85" -zstd = { version = "0.11.2", default-features = false } +zstd = { version = "0.12.3", default-features = false } sp-api = { version = "4.0.0-dev", path = "../api" } sp-state-machine = { version = "0.13.0", path = "../state-machine" } sp-tracing = { version = "6.0.0", path = "../../primitives/tracing" } diff --git a/utils/frame/try-runtime/cli/Cargo.toml b/utils/frame/try-runtime/cli/Cargo.toml index a4d24986e..686d27eef 100644 --- a/utils/frame/try-runtime/cli/Cargo.toml +++ b/utils/frame/try-runtime/cli/Cargo.toml @@ -42,7 +42,7 @@ log = "0.4.17" parity-scale-codec = "3.2.2" serde = "1.0.136" serde_json = "1.0.85" -zstd = { version = "0.11.2", default-features = false } +zstd = { version = "0.12.3", default-features = false } [dev-dependencies] assert_cmd = "2.0.10" From a6cb6d0fb79c303c9af513e856abfb61fc6d8d74 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 13 Apr 2023 11:30:42 +0200 Subject: [PATCH 370/558] Bump toml from 0.5.11 to 0.7.3 (#13619) Bumps [toml](https://github.com/toml-rs/toml) from 0.5.11 to 0.7.3. - [Release notes](https://github.com/toml-rs/toml/releases) - [Commits](https://github.com/toml-rs/toml/compare/toml-v0.5.11...toml-v0.7.3) --- updated-dependencies: - dependency-name: toml dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- Cargo.lock | 58 +++++++++++++++++++++++++++++++++-- utils/wasm-builder/Cargo.toml | 2 +- 2 files changed, 56 insertions(+), 4 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index fc0b37bae..45542b8c2 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -7523,7 +7523,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e17d47ce914bf4de440332250b0edd23ce48c005f59fab39d3335866b114f11a" dependencies = [ "thiserror", - "toml", + "toml 0.5.11", ] [[package]] @@ -9946,6 +9946,15 @@ dependencies = [ "serde", ] +[[package]] +name = "serde_spanned" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0efd8caf556a6cebd3b285caf480045fcc1ac04f6bd786b09a6f11af30c4fcf4" +dependencies = [ + "serde", +] + [[package]] name = "sha-1" version = "0.9.8" @@ -11411,7 +11420,7 @@ dependencies = [ "sp-maybe-compressed-blob", "strum", "tempfile", - "toml", + "toml 0.7.3", "walkdir", "wasm-opt", ] @@ -11768,6 +11777,40 @@ dependencies = [ "serde", ] +[[package]] +name = "toml" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b403acf6f2bb0859c93c7f0d967cb4a75a7ac552100f9322faf64dc047669b21" +dependencies = [ + "serde", + "serde_spanned", + "toml_datetime", + "toml_edit", +] + +[[package]] +name = "toml_datetime" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3ab8ed2edee10b50132aed5f331333428b011c99402b5a534154ed15746f9622" +dependencies = [ + "serde", +] + +[[package]] +name = "toml_edit" +version = "0.19.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "239410c8609e8125456927e6707163a3b1fdb40561e4b803bc041f466ccfdc13" +dependencies = [ + "indexmap", + "serde", + "serde_spanned", + "toml_datetime", + "winnow", +] + [[package]] name = "tower" version = "0.4.13" @@ -12594,7 +12637,7 @@ dependencies = [ "rustix 0.36.8", "serde", "sha2 0.10.6", - "toml", + "toml 0.5.11", "windows-sys 0.42.0", "zstd 0.11.2+zstd.1.5.2", ] @@ -13184,6 +13227,15 @@ version = "0.42.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "447660ad36a13288b1db4d4248e857b510e8c3a225c822ba4fb748c0aafecffd" +[[package]] +name = "winnow" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ae8970b36c66498d8ff1d66685dc86b91b29db0c7739899012f63a63814b4b28" +dependencies = [ + "memchr", +] + [[package]] name = "winreg" version = "0.10.1" diff --git a/utils/wasm-builder/Cargo.toml b/utils/wasm-builder/Cargo.toml index c1ef006b5..e1444a240 100644 --- a/utils/wasm-builder/Cargo.toml +++ b/utils/wasm-builder/Cargo.toml @@ -18,7 +18,7 @@ build-helper = "0.1.1" cargo_metadata = "0.15.2" strum = { version = "0.24.1", features = ["derive"] } tempfile = "3.1.0" -toml = "0.5.4" +toml = "0.7.3" walkdir = "2.3.2" sp-maybe-compressed-blob = { version = "4.1.0-dev", path = "../../primitives/maybe-compressed-blob" } filetime = "0.2.16" From be58e48f609c044fb832d78cf0f85c51e894d2dc Mon Sep 17 00:00:00 2001 From: Oliver Tale-Yazdi Date: Thu, 13 Apr 2023 15:11:58 +0200 Subject: [PATCH 371/558] Fixes PoV over-estimation (#13766) * Align log Signed-off-by: Oliver Tale-Yazdi * Use max instead of sum Signed-off-by: Oliver Tale-Yazdi * Make comment ordering deterministic Signed-off-by: Oliver Tale-Yazdi * Dont add Pov overhead when all is ignored Signed-off-by: Oliver Tale-Yazdi * Update test pallet weights Signed-off-by: Oliver Tale-Yazdi * Re-run weights on bm2 Signed-off-by: Oliver Tale-Yazdi * Fix test Signed-off-by: Oliver Tale-Yazdi * Actually use new weights Fucked up the merge for this file... Signed-off-by: Oliver Tale-Yazdi * Update contract weights Signed-off-by: Oliver Tale-Yazdi --------- Signed-off-by: Oliver Tale-Yazdi --- frame/alliance/src/weights.rs | 475 ++-- frame/assets/src/weights.rs | 347 +-- frame/bags-list/src/weights.rs | 43 +- frame/balances/src/weights.rs | 85 +- frame/benchmarking/pov/src/benchmarking.rs | 1 + frame/benchmarking/pov/src/tests.rs | 4 +- frame/benchmarking/pov/src/weights.rs | 555 ++-- frame/benchmarking/src/weights.rs | 63 +- frame/bounties/src/weights.rs | 151 +- frame/child-bounties/src/weights.rs | 111 +- frame/collective/src/weights.rs | 355 ++- frame/contracts/src/weights.rs | 2239 ++++++++--------- frame/conviction-voting/src/weights.rs | 147 +- frame/core-fellowship/src/weights.rs | 123 +- frame/democracy/src/weights.rs | 423 ++-- .../src/weights.rs | 207 +- frame/elections-phragmen/src/weights.rs | 267 +- frame/fast-unstake/src/weights.rs | 119 +- frame/glutton/src/weights.rs | 123 +- frame/identity/src/weights.rs | 359 ++- frame/im-online/src/weights.rs | 39 +- frame/indices/src/weights.rs | 55 +- frame/lottery/src/weights.rs | 79 +- frame/membership/src/weights.rs | 163 +- frame/message-queue/src/weights.rs | 111 +- frame/multisig/src/weights.rs | 147 +- frame/nfts/src/weights.rs | 503 ++-- frame/nis/src/weights.rs | 299 ++- frame/nomination-pools/src/weights.rs | 335 +-- frame/preimage/src/weights.rs | 115 +- frame/proxy/src/weights.rs | 215 +- frame/ranked-collective/src/weights.rs | 115 +- frame/recovery/src/weights.rs | 139 +- frame/referenda/src/weights.rs | 355 ++- frame/remark/src/weights.rs | 23 +- frame/salary/src/weights.rs | 83 +- frame/scheduler/src/weights.rs | 163 +- frame/session/src/weights.rs | 31 +- frame/staking/src/weights.rs | 563 +++-- frame/state-trie-migration/src/weights.rs | 75 +- frame/support/src/weights/block_weights.rs | 20 +- .../support/src/weights/extrinsic_weights.rs | 20 +- frame/system/src/weights.rs | 87 +- frame/timestamp/src/weights.rs | 27 +- frame/tips/src/weights.rs | 139 +- frame/transaction-storage/src/weights.rs | 47 +- frame/treasury/src/weights.rs | 91 +- frame/uniques/src/weights.rs | 327 ++- frame/utility/src/weights.rs | 71 +- frame/vesting/src/weights.rs | 295 ++- frame/whitelist/src/weights.rs | 67 +- scripts/run_all_benchmarks.sh | 2 +- .../benchmarking-cli/src/pallet/command.rs | 2 +- .../benchmarking-cli/src/pallet/writer.rs | 8 +- 54 files changed, 5500 insertions(+), 5508 deletions(-) diff --git a/frame/alliance/src/weights.rs b/frame/alliance/src/weights.rs index a9756c2d2..0276a9844 100644 --- a/frame/alliance/src/weights.rs +++ b/frame/alliance/src/weights.rs @@ -18,9 +18,9 @@ //! Autogenerated weights for pallet_alliance //! //! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev -//! DATE: 2023-03-15, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! DATE: 2023-04-06, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` //! WORST CASE MAP SIZE: `1000000` -//! HOSTNAME: `bm3`, CPU: `Intel(R) Core(TM) i7-7700K CPU @ 4.20GHz` +//! HOSTNAME: `bm2`, CPU: `Intel(R) Core(TM) i7-7700K CPU @ 4.20GHz` //! EXECUTION: Some(Wasm), WASM-EXECUTION: Compiled, CHAIN: Some("dev"), DB CACHE: 1024 // Executed Command: @@ -31,9 +31,6 @@ // --steps=50 // --repeat=20 // --pallet=pallet_alliance -// --no-storage-info -// --no-median-slopes -// --no-min-squares // --extrinsic=* // --execution=wasm // --wasm-execution=compiled @@ -92,19 +89,19 @@ impl WeightInfo for SubstrateWeight { fn propose_proposed(b: u32, m: u32, p: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `618 + m * (32 ±0) + p * (36 ±0)` - // Estimated: `15463 + m * (124 ±0) + p * (148 ±0)` - // Minimum execution time: 32_788_000 picoseconds. - Weight::from_parts(36_725_375, 15463) - // Standard Error: 138 - .saturating_add(Weight::from_parts(1_252, 0).saturating_mul(b.into())) - // Standard Error: 1_445 - .saturating_add(Weight::from_parts(9_861, 0).saturating_mul(m.into())) - // Standard Error: 1_427 - .saturating_add(Weight::from_parts(127_304, 0).saturating_mul(p.into())) + // Estimated: `6676 + m * (31 ±0) + p * (37 ±0)` + // Minimum execution time: 32_316_000 picoseconds. + Weight::from_parts(35_185_484, 6676) + // Standard Error: 83 + .saturating_add(Weight::from_parts(1_089, 0).saturating_mul(b.into())) + // Standard Error: 871 + .saturating_add(Weight::from_parts(21_235, 0).saturating_mul(m.into())) + // Standard Error: 860 + .saturating_add(Weight::from_parts(120_353, 0).saturating_mul(p.into())) .saturating_add(T::DbWeight::get().reads(4_u64)) .saturating_add(T::DbWeight::get().writes(4_u64)) - .saturating_add(Weight::from_parts(0, 124).saturating_mul(m.into())) - .saturating_add(Weight::from_parts(0, 148).saturating_mul(p.into())) + .saturating_add(Weight::from_parts(0, 31).saturating_mul(m.into())) + .saturating_add(Weight::from_parts(0, 37).saturating_mul(p.into())) } /// Storage: Alliance Members (r:1 w:0) /// Proof: Alliance Members (max_values: None, max_size: Some(3211), added: 5686, mode: MaxEncodedLen) @@ -114,11 +111,11 @@ impl WeightInfo for SubstrateWeight { fn vote(m: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `1042 + m * (64 ±0)` - // Estimated: `11182 + m * (64 ±0)` - // Minimum execution time: 26_632_000 picoseconds. - Weight::from_parts(28_918_089, 11182) - // Standard Error: 947 - .saturating_add(Weight::from_parts(56_565, 0).saturating_mul(m.into())) + // Estimated: `6676 + m * (64 ±0)` + // Minimum execution time: 25_982_000 picoseconds. + Weight::from_parts(28_118_657, 6676) + // Standard Error: 855 + .saturating_add(Weight::from_parts(61_309, 0).saturating_mul(m.into())) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) .saturating_add(Weight::from_parts(0, 64).saturating_mul(m.into())) @@ -138,17 +135,17 @@ impl WeightInfo for SubstrateWeight { fn close_early_disapproved(m: u32, p: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `622 + m * (96 ±0) + p * (37 ±0)` - // Estimated: `15551 + m * (384 ±0) + p * (148 ±0)` - // Minimum execution time: 40_937_000 picoseconds. - Weight::from_parts(39_361_123, 15551) - // Standard Error: 1_609 - .saturating_add(Weight::from_parts(43_225, 0).saturating_mul(m.into())) - // Standard Error: 1_569 - .saturating_add(Weight::from_parts(112_935, 0).saturating_mul(p.into())) + // Estimated: `6676 + m * (96 ±0) + p * (37 ±0)` + // Minimum execution time: 40_922_000 picoseconds. + Weight::from_parts(39_098_903, 6676) + // Standard Error: 714 + .saturating_add(Weight::from_parts(44_125, 0).saturating_mul(m.into())) + // Standard Error: 696 + .saturating_add(Weight::from_parts(111_263, 0).saturating_mul(p.into())) .saturating_add(T::DbWeight::get().reads(4_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) - .saturating_add(Weight::from_parts(0, 384).saturating_mul(m.into())) - .saturating_add(Weight::from_parts(0, 148).saturating_mul(p.into())) + .saturating_add(Weight::from_parts(0, 96).saturating_mul(m.into())) + .saturating_add(Weight::from_parts(0, 37).saturating_mul(p.into())) } /// Storage: Alliance Members (r:1 w:0) /// Proof: Alliance Members (max_values: None, max_size: Some(3211), added: 5686, mode: MaxEncodedLen) @@ -166,19 +163,19 @@ impl WeightInfo for SubstrateWeight { fn close_early_approved(b: u32, m: u32, p: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `903 + m * (96 ±0) + p * (41 ±0)` - // Estimated: `20072 + m * (392 ±0) + p * (160 ±0)` - // Minimum execution time: 51_569_000 picoseconds. - Weight::from_parts(50_722_448, 20072) - // Standard Error: 120 - .saturating_add(Weight::from_parts(474, 0).saturating_mul(b.into())) - // Standard Error: 1_271 - .saturating_add(Weight::from_parts(48_855, 0).saturating_mul(m.into())) - // Standard Error: 1_239 - .saturating_add(Weight::from_parts(139_022, 0).saturating_mul(p.into())) + // Estimated: `6676 + m * (98 ±0) + p * (40 ±0)` + // Minimum execution time: 51_890_000 picoseconds. + Weight::from_parts(49_880_817, 6676) + // Standard Error: 81 + .saturating_add(Weight::from_parts(688, 0).saturating_mul(b.into())) + // Standard Error: 862 + .saturating_add(Weight::from_parts(54_419, 0).saturating_mul(m.into())) + // Standard Error: 840 + .saturating_add(Weight::from_parts(122_253, 0).saturating_mul(p.into())) .saturating_add(T::DbWeight::get().reads(5_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) - .saturating_add(Weight::from_parts(0, 392).saturating_mul(m.into())) - .saturating_add(Weight::from_parts(0, 160).saturating_mul(p.into())) + .saturating_add(Weight::from_parts(0, 98).saturating_mul(m.into())) + .saturating_add(Weight::from_parts(0, 40).saturating_mul(p.into())) } /// Storage: Alliance Members (r:1 w:0) /// Proof: Alliance Members (max_values: None, max_size: Some(3211), added: 5686, mode: MaxEncodedLen) @@ -197,17 +194,17 @@ impl WeightInfo for SubstrateWeight { fn close_disapproved(m: u32, p: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `622 + m * (96 ±0) + p * (37 ±0)` - // Estimated: `17666 + m * (480 ±0) + p * (185 ±0)` - // Minimum execution time: 42_084_000 picoseconds. - Weight::from_parts(39_907_823, 17666) - // Standard Error: 899 - .saturating_add(Weight::from_parts(46_642, 0).saturating_mul(m.into())) - // Standard Error: 888 - .saturating_add(Weight::from_parts(114_161, 0).saturating_mul(p.into())) + // Estimated: `6676 + m * (96 ±0) + p * (37 ±0)` + // Minimum execution time: 42_391_000 picoseconds. + Weight::from_parts(40_156_254, 6676) + // Standard Error: 728 + .saturating_add(Weight::from_parts(47_889, 0).saturating_mul(m.into())) + // Standard Error: 719 + .saturating_add(Weight::from_parts(112_596, 0).saturating_mul(p.into())) .saturating_add(T::DbWeight::get().reads(5_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) - .saturating_add(Weight::from_parts(0, 480).saturating_mul(m.into())) - .saturating_add(Weight::from_parts(0, 185).saturating_mul(p.into())) + .saturating_add(Weight::from_parts(0, 96).saturating_mul(m.into())) + .saturating_add(Weight::from_parts(0, 37).saturating_mul(p.into())) } /// Storage: Alliance Members (r:1 w:0) /// Proof: Alliance Members (max_values: None, max_size: Some(3211), added: 5686, mode: MaxEncodedLen) @@ -227,19 +224,19 @@ impl WeightInfo for SubstrateWeight { fn close_approved(b: u32, m: u32, p: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `591 + m * (96 ±0) + p * (36 ±0)` - // Estimated: `17471 + m * (485 ±0) + p * (180 ±0)` - // Minimum execution time: 42_322_000 picoseconds. - Weight::from_parts(38_753_429, 17471) - // Standard Error: 78 - .saturating_add(Weight::from_parts(1_053, 0).saturating_mul(b.into())) - // Standard Error: 841 - .saturating_add(Weight::from_parts(51_720, 0).saturating_mul(m.into())) - // Standard Error: 811 - .saturating_add(Weight::from_parts(113_343, 0).saturating_mul(p.into())) + // Estimated: `6676 + m * (97 ±0) + p * (36 ±0)` + // Minimum execution time: 42_320_000 picoseconds. + Weight::from_parts(40_205_526, 6676) + // Standard Error: 64 + .saturating_add(Weight::from_parts(49, 0).saturating_mul(b.into())) + // Standard Error: 690 + .saturating_add(Weight::from_parts(46_508, 0).saturating_mul(m.into())) + // Standard Error: 665 + .saturating_add(Weight::from_parts(112_222, 0).saturating_mul(p.into())) .saturating_add(T::DbWeight::get().reads(5_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) - .saturating_add(Weight::from_parts(0, 485).saturating_mul(m.into())) - .saturating_add(Weight::from_parts(0, 180).saturating_mul(p.into())) + .saturating_add(Weight::from_parts(0, 97).saturating_mul(m.into())) + .saturating_add(Weight::from_parts(0, 36).saturating_mul(p.into())) } /// Storage: Alliance Members (r:2 w:2) /// Proof: Alliance Members (max_values: None, max_size: Some(3211), added: 5686, mode: MaxEncodedLen) @@ -250,13 +247,13 @@ impl WeightInfo for SubstrateWeight { fn init_members(m: u32, z: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `217` - // Estimated: `14064` - // Minimum execution time: 33_217_000 picoseconds. - Weight::from_parts(24_495_505, 14064) - // Standard Error: 412 - .saturating_add(Weight::from_parts(108_405, 0).saturating_mul(m.into())) - // Standard Error: 407 - .saturating_add(Weight::from_parts(95_707, 0).saturating_mul(z.into())) + // Estimated: `12362` + // Minimum execution time: 32_509_000 picoseconds. + Weight::from_parts(23_584_337, 12362) + // Standard Error: 377 + .saturating_add(Weight::from_parts(114_917, 0).saturating_mul(m.into())) + // Standard Error: 373 + .saturating_add(Weight::from_parts(97_593, 0).saturating_mul(z.into())) .saturating_add(T::DbWeight::get().reads(3_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) } @@ -278,24 +275,24 @@ impl WeightInfo for SubstrateWeight { fn disband(x: u32, y: u32, z: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0 + x * (50 ±0) + y * (51 ±0) + z * (251 ±0)` - // Estimated: `35975 + x * (2587 ±0) + y * (2590 ±0) + z * (3113 ±1)` - // Minimum execution time: 278_029_000 picoseconds. - Weight::from_parts(279_353_000, 35975) - // Standard Error: 23_655 - .saturating_add(Weight::from_parts(519_330, 0).saturating_mul(x.into())) - // Standard Error: 23_541 - .saturating_add(Weight::from_parts(547_382, 0).saturating_mul(y.into())) - // Standard Error: 47_040 - .saturating_add(Weight::from_parts(10_939_685, 0).saturating_mul(z.into())) + // Estimated: `12362 + x * (2539 ±0) + y * (2539 ±0) + z * (2603 ±1)` + // Minimum execution time: 275_061_000 picoseconds. + Weight::from_parts(565_248, 12362) + // Standard Error: 15_948 + .saturating_add(Weight::from_parts(1_636_348, 0).saturating_mul(x.into())) + // Standard Error: 15_761 + .saturating_add(Weight::from_parts(1_580_146, 0).saturating_mul(y.into())) + // Standard Error: 31_496 + .saturating_add(Weight::from_parts(17_217_382, 0).saturating_mul(z.into())) .saturating_add(T::DbWeight::get().reads(3_u64)) .saturating_add(T::DbWeight::get().reads((1_u64).saturating_mul(x.into()))) .saturating_add(T::DbWeight::get().reads((1_u64).saturating_mul(y.into()))) .saturating_add(T::DbWeight::get().reads((1_u64).saturating_mul(z.into()))) .saturating_add(T::DbWeight::get().writes(4_u64)) .saturating_add(T::DbWeight::get().writes((2_u64).saturating_mul(z.into()))) - .saturating_add(Weight::from_parts(0, 2587).saturating_mul(x.into())) - .saturating_add(Weight::from_parts(0, 2590).saturating_mul(y.into())) - .saturating_add(Weight::from_parts(0, 3113).saturating_mul(z.into())) + .saturating_add(Weight::from_parts(0, 2539).saturating_mul(x.into())) + .saturating_add(Weight::from_parts(0, 2539).saturating_mul(y.into())) + .saturating_add(Weight::from_parts(0, 2603).saturating_mul(z.into())) } /// Storage: Alliance Rule (r:0 w:1) /// Proof: Alliance Rule (max_values: Some(1), max_size: Some(87), added: 582, mode: MaxEncodedLen) @@ -303,8 +300,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 10_129_000 picoseconds. - Weight::from_parts(10_370_000, 0) + // Minimum execution time: 10_261_000 picoseconds. + Weight::from_parts(10_389_000, 0) .saturating_add(T::DbWeight::get().writes(1_u64)) } /// Storage: Alliance Announcements (r:1 w:1) @@ -313,8 +310,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `246` // Estimated: `10187` - // Minimum execution time: 13_601_000 picoseconds. - Weight::from_parts(13_823_000, 10187) + // Minimum execution time: 13_483_000 picoseconds. + Weight::from_parts(13_805_000, 10187) .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -324,8 +321,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `319` // Estimated: `10187` - // Minimum execution time: 14_953_000 picoseconds. - Weight::from_parts(15_239_000, 10187) + // Minimum execution time: 14_816_000 picoseconds. + Weight::from_parts(15_163_000, 10187) .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -340,9 +337,9 @@ impl WeightInfo for SubstrateWeight { fn join_alliance() -> Weight { // Proof Size summary in bytes: // Measured: `468` - // Estimated: `26328` - // Minimum execution time: 42_947_000 picoseconds. - Weight::from_parts(43_602_000, 26328) + // Estimated: `18048` + // Minimum execution time: 46_149_000 picoseconds. + Weight::from_parts(46_827_000, 18048) .saturating_add(T::DbWeight::get().reads(5_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) } @@ -353,9 +350,9 @@ impl WeightInfo for SubstrateWeight { fn nominate_ally() -> Weight { // Proof Size summary in bytes: // Measured: `367` - // Estimated: `22735` - // Minimum execution time: 28_787_000 picoseconds. - Weight::from_parts(29_286_000, 22735) + // Estimated: `18048` + // Minimum execution time: 28_463_000 picoseconds. + Weight::from_parts(28_730_000, 18048) .saturating_add(T::DbWeight::get().reads(4_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -370,9 +367,9 @@ impl WeightInfo for SubstrateWeight { fn elevate_ally() -> Weight { // Proof Size summary in bytes: // Measured: `443` - // Estimated: `15176` - // Minimum execution time: 28_476_000 picoseconds. - Weight::from_parts(28_972_000, 15176) + // Estimated: `12362` + // Minimum execution time: 28_401_000 picoseconds. + Weight::from_parts(28_717_000, 12362) .saturating_add(T::DbWeight::get().reads(3_u64)) .saturating_add(T::DbWeight::get().writes(4_u64)) } @@ -389,9 +386,9 @@ impl WeightInfo for SubstrateWeight { fn give_retirement_notice() -> Weight { // Proof Size summary in bytes: // Measured: `443` - // Estimated: `26548` - // Minimum execution time: 36_773_000 picoseconds. - Weight::from_parts(38_123_000, 26548) + // Estimated: `23734` + // Minimum execution time: 36_538_000 picoseconds. + Weight::from_parts(37_197_000, 23734) .saturating_add(T::DbWeight::get().reads(5_u64)) .saturating_add(T::DbWeight::get().writes(5_u64)) } @@ -406,9 +403,9 @@ impl WeightInfo for SubstrateWeight { fn retire() -> Weight { // Proof Size summary in bytes: // Measured: `687` - // Estimated: `17315` - // Minimum execution time: 37_814_000 picoseconds. - Weight::from_parts(38_353_000, 17315) + // Estimated: `6676` + // Minimum execution time: 42_324_000 picoseconds. + Weight::from_parts(42_890_000, 6676) .saturating_add(T::DbWeight::get().reads(4_u64)) .saturating_add(T::DbWeight::get().writes(4_u64)) } @@ -427,9 +424,9 @@ impl WeightInfo for SubstrateWeight { fn kick_member() -> Weight { // Proof Size summary in bytes: // Measured: `707` - // Estimated: `28776` - // Minimum execution time: 62_194_000 picoseconds. - Weight::from_parts(63_925_000, 28776) + // Estimated: `18048` + // Minimum execution time: 68_003_000 picoseconds. + Weight::from_parts(68_657_000, 18048) .saturating_add(T::DbWeight::get().reads(6_u64)) .saturating_add(T::DbWeight::get().writes(5_u64)) } @@ -442,13 +439,13 @@ impl WeightInfo for SubstrateWeight { fn add_unscrupulous_items(n: u32, l: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `246` - // Estimated: `31874` - // Minimum execution time: 8_369_000 picoseconds. - Weight::from_parts(8_462_000, 31874) - // Standard Error: 3_347 - .saturating_add(Weight::from_parts(1_567_757, 0).saturating_mul(n.into())) - // Standard Error: 1_310 - .saturating_add(Weight::from_parts(72_697, 0).saturating_mul(l.into())) + // Estimated: `27187` + // Minimum execution time: 8_304_000 picoseconds. + Weight::from_parts(8_424_000, 27187) + // Standard Error: 2_765 + .saturating_add(Weight::from_parts(1_529_793, 0).saturating_mul(n.into())) + // Standard Error: 1_082 + .saturating_add(Weight::from_parts(71_352, 0).saturating_mul(l.into())) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().writes(2_u64)) } @@ -460,14 +457,14 @@ impl WeightInfo for SubstrateWeight { /// The range of component `l` is `[0, 255]`. fn remove_unscrupulous_items(n: u32, l: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `0 + n * (289 ±0) + l * (100 ±0)` - // Estimated: `31874` - // Minimum execution time: 8_309_000 picoseconds. - Weight::from_parts(8_450_000, 31874) - // Standard Error: 185_163 - .saturating_add(Weight::from_parts(16_348_419, 0).saturating_mul(n.into())) - // Standard Error: 72_518 - .saturating_add(Weight::from_parts(337_571, 0).saturating_mul(l.into())) + // Measured: `0 + l * (100 ±0) + n * (289 ±0)` + // Estimated: `27187` + // Minimum execution time: 8_348_000 picoseconds. + Weight::from_parts(8_505_000, 27187) + // Standard Error: 187_398 + .saturating_add(Weight::from_parts(16_545_597, 0).saturating_mul(n.into())) + // Standard Error: 73_393 + .saturating_add(Weight::from_parts(350_415, 0).saturating_mul(l.into())) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().writes(2_u64)) } @@ -482,9 +479,9 @@ impl WeightInfo for SubstrateWeight { fn abdicate_fellow_status() -> Weight { // Proof Size summary in bytes: // Measured: `443` - // Estimated: `20862` - // Minimum execution time: 35_363_000 picoseconds. - Weight::from_parts(35_665_000, 20862) + // Estimated: `18048` + // Minimum execution time: 34_461_000 picoseconds. + Weight::from_parts(34_992_000, 18048) .saturating_add(T::DbWeight::get().reads(4_u64)) .saturating_add(T::DbWeight::get().writes(4_u64)) } @@ -508,19 +505,19 @@ impl WeightInfo for () { fn propose_proposed(b: u32, m: u32, p: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `618 + m * (32 ±0) + p * (36 ±0)` - // Estimated: `15463 + m * (124 ±0) + p * (148 ±0)` - // Minimum execution time: 32_788_000 picoseconds. - Weight::from_parts(36_725_375, 15463) - // Standard Error: 138 - .saturating_add(Weight::from_parts(1_252, 0).saturating_mul(b.into())) - // Standard Error: 1_445 - .saturating_add(Weight::from_parts(9_861, 0).saturating_mul(m.into())) - // Standard Error: 1_427 - .saturating_add(Weight::from_parts(127_304, 0).saturating_mul(p.into())) + // Estimated: `6676 + m * (31 ±0) + p * (37 ±0)` + // Minimum execution time: 32_316_000 picoseconds. + Weight::from_parts(35_185_484, 6676) + // Standard Error: 83 + .saturating_add(Weight::from_parts(1_089, 0).saturating_mul(b.into())) + // Standard Error: 871 + .saturating_add(Weight::from_parts(21_235, 0).saturating_mul(m.into())) + // Standard Error: 860 + .saturating_add(Weight::from_parts(120_353, 0).saturating_mul(p.into())) .saturating_add(RocksDbWeight::get().reads(4_u64)) .saturating_add(RocksDbWeight::get().writes(4_u64)) - .saturating_add(Weight::from_parts(0, 124).saturating_mul(m.into())) - .saturating_add(Weight::from_parts(0, 148).saturating_mul(p.into())) + .saturating_add(Weight::from_parts(0, 31).saturating_mul(m.into())) + .saturating_add(Weight::from_parts(0, 37).saturating_mul(p.into())) } /// Storage: Alliance Members (r:1 w:0) /// Proof: Alliance Members (max_values: None, max_size: Some(3211), added: 5686, mode: MaxEncodedLen) @@ -530,11 +527,11 @@ impl WeightInfo for () { fn vote(m: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `1042 + m * (64 ±0)` - // Estimated: `11182 + m * (64 ±0)` - // Minimum execution time: 26_632_000 picoseconds. - Weight::from_parts(28_918_089, 11182) - // Standard Error: 947 - .saturating_add(Weight::from_parts(56_565, 0).saturating_mul(m.into())) + // Estimated: `6676 + m * (64 ±0)` + // Minimum execution time: 25_982_000 picoseconds. + Weight::from_parts(28_118_657, 6676) + // Standard Error: 855 + .saturating_add(Weight::from_parts(61_309, 0).saturating_mul(m.into())) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) .saturating_add(Weight::from_parts(0, 64).saturating_mul(m.into())) @@ -554,17 +551,17 @@ impl WeightInfo for () { fn close_early_disapproved(m: u32, p: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `622 + m * (96 ±0) + p * (37 ±0)` - // Estimated: `15551 + m * (384 ±0) + p * (148 ±0)` - // Minimum execution time: 40_937_000 picoseconds. - Weight::from_parts(39_361_123, 15551) - // Standard Error: 1_609 - .saturating_add(Weight::from_parts(43_225, 0).saturating_mul(m.into())) - // Standard Error: 1_569 - .saturating_add(Weight::from_parts(112_935, 0).saturating_mul(p.into())) + // Estimated: `6676 + m * (96 ±0) + p * (37 ±0)` + // Minimum execution time: 40_922_000 picoseconds. + Weight::from_parts(39_098_903, 6676) + // Standard Error: 714 + .saturating_add(Weight::from_parts(44_125, 0).saturating_mul(m.into())) + // Standard Error: 696 + .saturating_add(Weight::from_parts(111_263, 0).saturating_mul(p.into())) .saturating_add(RocksDbWeight::get().reads(4_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) - .saturating_add(Weight::from_parts(0, 384).saturating_mul(m.into())) - .saturating_add(Weight::from_parts(0, 148).saturating_mul(p.into())) + .saturating_add(Weight::from_parts(0, 96).saturating_mul(m.into())) + .saturating_add(Weight::from_parts(0, 37).saturating_mul(p.into())) } /// Storage: Alliance Members (r:1 w:0) /// Proof: Alliance Members (max_values: None, max_size: Some(3211), added: 5686, mode: MaxEncodedLen) @@ -582,19 +579,19 @@ impl WeightInfo for () { fn close_early_approved(b: u32, m: u32, p: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `903 + m * (96 ±0) + p * (41 ±0)` - // Estimated: `20072 + m * (392 ±0) + p * (160 ±0)` - // Minimum execution time: 51_569_000 picoseconds. - Weight::from_parts(50_722_448, 20072) - // Standard Error: 120 - .saturating_add(Weight::from_parts(474, 0).saturating_mul(b.into())) - // Standard Error: 1_271 - .saturating_add(Weight::from_parts(48_855, 0).saturating_mul(m.into())) - // Standard Error: 1_239 - .saturating_add(Weight::from_parts(139_022, 0).saturating_mul(p.into())) + // Estimated: `6676 + m * (98 ±0) + p * (40 ±0)` + // Minimum execution time: 51_890_000 picoseconds. + Weight::from_parts(49_880_817, 6676) + // Standard Error: 81 + .saturating_add(Weight::from_parts(688, 0).saturating_mul(b.into())) + // Standard Error: 862 + .saturating_add(Weight::from_parts(54_419, 0).saturating_mul(m.into())) + // Standard Error: 840 + .saturating_add(Weight::from_parts(122_253, 0).saturating_mul(p.into())) .saturating_add(RocksDbWeight::get().reads(5_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) - .saturating_add(Weight::from_parts(0, 392).saturating_mul(m.into())) - .saturating_add(Weight::from_parts(0, 160).saturating_mul(p.into())) + .saturating_add(Weight::from_parts(0, 98).saturating_mul(m.into())) + .saturating_add(Weight::from_parts(0, 40).saturating_mul(p.into())) } /// Storage: Alliance Members (r:1 w:0) /// Proof: Alliance Members (max_values: None, max_size: Some(3211), added: 5686, mode: MaxEncodedLen) @@ -613,17 +610,17 @@ impl WeightInfo for () { fn close_disapproved(m: u32, p: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `622 + m * (96 ±0) + p * (37 ±0)` - // Estimated: `17666 + m * (480 ±0) + p * (185 ±0)` - // Minimum execution time: 42_084_000 picoseconds. - Weight::from_parts(39_907_823, 17666) - // Standard Error: 899 - .saturating_add(Weight::from_parts(46_642, 0).saturating_mul(m.into())) - // Standard Error: 888 - .saturating_add(Weight::from_parts(114_161, 0).saturating_mul(p.into())) + // Estimated: `6676 + m * (96 ±0) + p * (37 ±0)` + // Minimum execution time: 42_391_000 picoseconds. + Weight::from_parts(40_156_254, 6676) + // Standard Error: 728 + .saturating_add(Weight::from_parts(47_889, 0).saturating_mul(m.into())) + // Standard Error: 719 + .saturating_add(Weight::from_parts(112_596, 0).saturating_mul(p.into())) .saturating_add(RocksDbWeight::get().reads(5_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) - .saturating_add(Weight::from_parts(0, 480).saturating_mul(m.into())) - .saturating_add(Weight::from_parts(0, 185).saturating_mul(p.into())) + .saturating_add(Weight::from_parts(0, 96).saturating_mul(m.into())) + .saturating_add(Weight::from_parts(0, 37).saturating_mul(p.into())) } /// Storage: Alliance Members (r:1 w:0) /// Proof: Alliance Members (max_values: None, max_size: Some(3211), added: 5686, mode: MaxEncodedLen) @@ -643,19 +640,19 @@ impl WeightInfo for () { fn close_approved(b: u32, m: u32, p: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `591 + m * (96 ±0) + p * (36 ±0)` - // Estimated: `17471 + m * (485 ±0) + p * (180 ±0)` - // Minimum execution time: 42_322_000 picoseconds. - Weight::from_parts(38_753_429, 17471) - // Standard Error: 78 - .saturating_add(Weight::from_parts(1_053, 0).saturating_mul(b.into())) - // Standard Error: 841 - .saturating_add(Weight::from_parts(51_720, 0).saturating_mul(m.into())) - // Standard Error: 811 - .saturating_add(Weight::from_parts(113_343, 0).saturating_mul(p.into())) + // Estimated: `6676 + m * (97 ±0) + p * (36 ±0)` + // Minimum execution time: 42_320_000 picoseconds. + Weight::from_parts(40_205_526, 6676) + // Standard Error: 64 + .saturating_add(Weight::from_parts(49, 0).saturating_mul(b.into())) + // Standard Error: 690 + .saturating_add(Weight::from_parts(46_508, 0).saturating_mul(m.into())) + // Standard Error: 665 + .saturating_add(Weight::from_parts(112_222, 0).saturating_mul(p.into())) .saturating_add(RocksDbWeight::get().reads(5_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) - .saturating_add(Weight::from_parts(0, 485).saturating_mul(m.into())) - .saturating_add(Weight::from_parts(0, 180).saturating_mul(p.into())) + .saturating_add(Weight::from_parts(0, 97).saturating_mul(m.into())) + .saturating_add(Weight::from_parts(0, 36).saturating_mul(p.into())) } /// Storage: Alliance Members (r:2 w:2) /// Proof: Alliance Members (max_values: None, max_size: Some(3211), added: 5686, mode: MaxEncodedLen) @@ -666,13 +663,13 @@ impl WeightInfo for () { fn init_members(m: u32, z: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `217` - // Estimated: `14064` - // Minimum execution time: 33_217_000 picoseconds. - Weight::from_parts(24_495_505, 14064) - // Standard Error: 412 - .saturating_add(Weight::from_parts(108_405, 0).saturating_mul(m.into())) - // Standard Error: 407 - .saturating_add(Weight::from_parts(95_707, 0).saturating_mul(z.into())) + // Estimated: `12362` + // Minimum execution time: 32_509_000 picoseconds. + Weight::from_parts(23_584_337, 12362) + // Standard Error: 377 + .saturating_add(Weight::from_parts(114_917, 0).saturating_mul(m.into())) + // Standard Error: 373 + .saturating_add(Weight::from_parts(97_593, 0).saturating_mul(z.into())) .saturating_add(RocksDbWeight::get().reads(3_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) } @@ -694,24 +691,24 @@ impl WeightInfo for () { fn disband(x: u32, y: u32, z: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0 + x * (50 ±0) + y * (51 ±0) + z * (251 ±0)` - // Estimated: `35975 + x * (2587 ±0) + y * (2590 ±0) + z * (3113 ±1)` - // Minimum execution time: 278_029_000 picoseconds. - Weight::from_parts(279_353_000, 35975) - // Standard Error: 23_655 - .saturating_add(Weight::from_parts(519_330, 0).saturating_mul(x.into())) - // Standard Error: 23_541 - .saturating_add(Weight::from_parts(547_382, 0).saturating_mul(y.into())) - // Standard Error: 47_040 - .saturating_add(Weight::from_parts(10_939_685, 0).saturating_mul(z.into())) + // Estimated: `12362 + x * (2539 ±0) + y * (2539 ±0) + z * (2603 ±1)` + // Minimum execution time: 275_061_000 picoseconds. + Weight::from_parts(565_248, 12362) + // Standard Error: 15_948 + .saturating_add(Weight::from_parts(1_636_348, 0).saturating_mul(x.into())) + // Standard Error: 15_761 + .saturating_add(Weight::from_parts(1_580_146, 0).saturating_mul(y.into())) + // Standard Error: 31_496 + .saturating_add(Weight::from_parts(17_217_382, 0).saturating_mul(z.into())) .saturating_add(RocksDbWeight::get().reads(3_u64)) .saturating_add(RocksDbWeight::get().reads((1_u64).saturating_mul(x.into()))) .saturating_add(RocksDbWeight::get().reads((1_u64).saturating_mul(y.into()))) .saturating_add(RocksDbWeight::get().reads((1_u64).saturating_mul(z.into()))) .saturating_add(RocksDbWeight::get().writes(4_u64)) .saturating_add(RocksDbWeight::get().writes((2_u64).saturating_mul(z.into()))) - .saturating_add(Weight::from_parts(0, 2587).saturating_mul(x.into())) - .saturating_add(Weight::from_parts(0, 2590).saturating_mul(y.into())) - .saturating_add(Weight::from_parts(0, 3113).saturating_mul(z.into())) + .saturating_add(Weight::from_parts(0, 2539).saturating_mul(x.into())) + .saturating_add(Weight::from_parts(0, 2539).saturating_mul(y.into())) + .saturating_add(Weight::from_parts(0, 2603).saturating_mul(z.into())) } /// Storage: Alliance Rule (r:0 w:1) /// Proof: Alliance Rule (max_values: Some(1), max_size: Some(87), added: 582, mode: MaxEncodedLen) @@ -719,8 +716,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 10_129_000 picoseconds. - Weight::from_parts(10_370_000, 0) + // Minimum execution time: 10_261_000 picoseconds. + Weight::from_parts(10_389_000, 0) .saturating_add(RocksDbWeight::get().writes(1_u64)) } /// Storage: Alliance Announcements (r:1 w:1) @@ -729,8 +726,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `246` // Estimated: `10187` - // Minimum execution time: 13_601_000 picoseconds. - Weight::from_parts(13_823_000, 10187) + // Minimum execution time: 13_483_000 picoseconds. + Weight::from_parts(13_805_000, 10187) .saturating_add(RocksDbWeight::get().reads(1_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } @@ -740,8 +737,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `319` // Estimated: `10187` - // Minimum execution time: 14_953_000 picoseconds. - Weight::from_parts(15_239_000, 10187) + // Minimum execution time: 14_816_000 picoseconds. + Weight::from_parts(15_163_000, 10187) .saturating_add(RocksDbWeight::get().reads(1_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } @@ -756,9 +753,9 @@ impl WeightInfo for () { fn join_alliance() -> Weight { // Proof Size summary in bytes: // Measured: `468` - // Estimated: `26328` - // Minimum execution time: 42_947_000 picoseconds. - Weight::from_parts(43_602_000, 26328) + // Estimated: `18048` + // Minimum execution time: 46_149_000 picoseconds. + Weight::from_parts(46_827_000, 18048) .saturating_add(RocksDbWeight::get().reads(5_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) } @@ -769,9 +766,9 @@ impl WeightInfo for () { fn nominate_ally() -> Weight { // Proof Size summary in bytes: // Measured: `367` - // Estimated: `22735` - // Minimum execution time: 28_787_000 picoseconds. - Weight::from_parts(29_286_000, 22735) + // Estimated: `18048` + // Minimum execution time: 28_463_000 picoseconds. + Weight::from_parts(28_730_000, 18048) .saturating_add(RocksDbWeight::get().reads(4_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } @@ -786,9 +783,9 @@ impl WeightInfo for () { fn elevate_ally() -> Weight { // Proof Size summary in bytes: // Measured: `443` - // Estimated: `15176` - // Minimum execution time: 28_476_000 picoseconds. - Weight::from_parts(28_972_000, 15176) + // Estimated: `12362` + // Minimum execution time: 28_401_000 picoseconds. + Weight::from_parts(28_717_000, 12362) .saturating_add(RocksDbWeight::get().reads(3_u64)) .saturating_add(RocksDbWeight::get().writes(4_u64)) } @@ -805,9 +802,9 @@ impl WeightInfo for () { fn give_retirement_notice() -> Weight { // Proof Size summary in bytes: // Measured: `443` - // Estimated: `26548` - // Minimum execution time: 36_773_000 picoseconds. - Weight::from_parts(38_123_000, 26548) + // Estimated: `23734` + // Minimum execution time: 36_538_000 picoseconds. + Weight::from_parts(37_197_000, 23734) .saturating_add(RocksDbWeight::get().reads(5_u64)) .saturating_add(RocksDbWeight::get().writes(5_u64)) } @@ -822,9 +819,9 @@ impl WeightInfo for () { fn retire() -> Weight { // Proof Size summary in bytes: // Measured: `687` - // Estimated: `17315` - // Minimum execution time: 37_814_000 picoseconds. - Weight::from_parts(38_353_000, 17315) + // Estimated: `6676` + // Minimum execution time: 42_324_000 picoseconds. + Weight::from_parts(42_890_000, 6676) .saturating_add(RocksDbWeight::get().reads(4_u64)) .saturating_add(RocksDbWeight::get().writes(4_u64)) } @@ -843,9 +840,9 @@ impl WeightInfo for () { fn kick_member() -> Weight { // Proof Size summary in bytes: // Measured: `707` - // Estimated: `28776` - // Minimum execution time: 62_194_000 picoseconds. - Weight::from_parts(63_925_000, 28776) + // Estimated: `18048` + // Minimum execution time: 68_003_000 picoseconds. + Weight::from_parts(68_657_000, 18048) .saturating_add(RocksDbWeight::get().reads(6_u64)) .saturating_add(RocksDbWeight::get().writes(5_u64)) } @@ -858,13 +855,13 @@ impl WeightInfo for () { fn add_unscrupulous_items(n: u32, l: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `246` - // Estimated: `31874` - // Minimum execution time: 8_369_000 picoseconds. - Weight::from_parts(8_462_000, 31874) - // Standard Error: 3_347 - .saturating_add(Weight::from_parts(1_567_757, 0).saturating_mul(n.into())) - // Standard Error: 1_310 - .saturating_add(Weight::from_parts(72_697, 0).saturating_mul(l.into())) + // Estimated: `27187` + // Minimum execution time: 8_304_000 picoseconds. + Weight::from_parts(8_424_000, 27187) + // Standard Error: 2_765 + .saturating_add(Weight::from_parts(1_529_793, 0).saturating_mul(n.into())) + // Standard Error: 1_082 + .saturating_add(Weight::from_parts(71_352, 0).saturating_mul(l.into())) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().writes(2_u64)) } @@ -876,14 +873,14 @@ impl WeightInfo for () { /// The range of component `l` is `[0, 255]`. fn remove_unscrupulous_items(n: u32, l: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `0 + n * (289 ±0) + l * (100 ±0)` - // Estimated: `31874` - // Minimum execution time: 8_309_000 picoseconds. - Weight::from_parts(8_450_000, 31874) - // Standard Error: 185_163 - .saturating_add(Weight::from_parts(16_348_419, 0).saturating_mul(n.into())) - // Standard Error: 72_518 - .saturating_add(Weight::from_parts(337_571, 0).saturating_mul(l.into())) + // Measured: `0 + l * (100 ±0) + n * (289 ±0)` + // Estimated: `27187` + // Minimum execution time: 8_348_000 picoseconds. + Weight::from_parts(8_505_000, 27187) + // Standard Error: 187_398 + .saturating_add(Weight::from_parts(16_545_597, 0).saturating_mul(n.into())) + // Standard Error: 73_393 + .saturating_add(Weight::from_parts(350_415, 0).saturating_mul(l.into())) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().writes(2_u64)) } @@ -898,9 +895,9 @@ impl WeightInfo for () { fn abdicate_fellow_status() -> Weight { // Proof Size summary in bytes: // Measured: `443` - // Estimated: `20862` - // Minimum execution time: 35_363_000 picoseconds. - Weight::from_parts(35_665_000, 20862) + // Estimated: `18048` + // Minimum execution time: 34_461_000 picoseconds. + Weight::from_parts(34_992_000, 18048) .saturating_add(RocksDbWeight::get().reads(4_u64)) .saturating_add(RocksDbWeight::get().writes(4_u64)) } diff --git a/frame/assets/src/weights.rs b/frame/assets/src/weights.rs index 0d6c41b45..4cc90e25f 100644 --- a/frame/assets/src/weights.rs +++ b/frame/assets/src/weights.rs @@ -18,9 +18,9 @@ //! Autogenerated weights for pallet_assets //! //! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev -//! DATE: 2023-03-15, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! DATE: 2023-04-06, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` //! WORST CASE MAP SIZE: `1000000` -//! HOSTNAME: `bm3`, CPU: `Intel(R) Core(TM) i7-7700K CPU @ 4.20GHz` +//! HOSTNAME: `bm2`, CPU: `Intel(R) Core(TM) i7-7700K CPU @ 4.20GHz` //! EXECUTION: Some(Wasm), WASM-EXECUTION: Compiled, CHAIN: Some("dev"), DB CACHE: 1024 // Executed Command: @@ -31,9 +31,6 @@ // --steps=50 // --repeat=20 // --pallet=pallet_assets -// --no-storage-info -// --no-median-slopes -// --no-min-squares // --extrinsic=* // --execution=wasm // --wasm-execution=compiled @@ -90,9 +87,9 @@ impl WeightInfo for SubstrateWeight { fn create() -> Weight { // Proof Size summary in bytes: // Measured: `293` - // Estimated: `7268` - // Minimum execution time: 27_756_000 picoseconds. - Weight::from_parts(28_637_000, 7268) + // Estimated: `3675` + // Minimum execution time: 33_678_000 picoseconds. + Weight::from_parts(34_320_000, 3675) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().writes(2_u64)) } @@ -102,8 +99,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `153` // Estimated: `3675` - // Minimum execution time: 15_402_000 picoseconds. - Weight::from_parts(15_803_000, 3675) + // Minimum execution time: 15_521_000 picoseconds. + Weight::from_parts(16_035_000, 3675) .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -113,8 +110,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `385` // Estimated: `3675` - // Minimum execution time: 16_027_000 picoseconds. - Weight::from_parts(16_372_000, 3675) + // Minimum execution time: 15_921_000 picoseconds. + Weight::from_parts(16_153_000, 3675) .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -128,16 +125,16 @@ impl WeightInfo for SubstrateWeight { fn destroy_accounts(c: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0 + c * (208 ±0)` - // Estimated: `8232 + c * (5180 ±0)` - // Minimum execution time: 20_667_000 picoseconds. - Weight::from_parts(20_930_000, 8232) - // Standard Error: 7_226 - .saturating_add(Weight::from_parts(12_759_091, 0).saturating_mul(c.into())) + // Estimated: `3675 + c * (2603 ±0)` + // Minimum execution time: 21_242_000 picoseconds. + Weight::from_parts(21_532_000, 3675) + // Standard Error: 6_449 + .saturating_add(Weight::from_parts(13_150_845, 0).saturating_mul(c.into())) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().reads((2_u64).saturating_mul(c.into()))) .saturating_add(T::DbWeight::get().writes(1_u64)) .saturating_add(T::DbWeight::get().writes((2_u64).saturating_mul(c.into()))) - .saturating_add(Weight::from_parts(0, 5180).saturating_mul(c.into())) + .saturating_add(Weight::from_parts(0, 2603).saturating_mul(c.into())) } /// Storage: Assets Asset (r:1 w:1) /// Proof: Assets Asset (max_values: None, max_size: Some(210), added: 2685, mode: MaxEncodedLen) @@ -147,11 +144,11 @@ impl WeightInfo for SubstrateWeight { fn destroy_approvals(a: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `522 + a * (86 ±0)` - // Estimated: `7288 + a * (2623 ±0)` - // Minimum execution time: 20_798_000 picoseconds. - Weight::from_parts(21_129_000, 7288) - // Standard Error: 7_484 - .saturating_add(Weight::from_parts(12_761_996, 0).saturating_mul(a.into())) + // Estimated: `3675 + a * (2623 ±0)` + // Minimum execution time: 21_604_000 picoseconds. + Weight::from_parts(21_881_000, 3675) + // Standard Error: 4_225 + .saturating_add(Weight::from_parts(15_968_205, 0).saturating_mul(a.into())) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().reads((1_u64).saturating_mul(a.into()))) .saturating_add(T::DbWeight::get().writes(1_u64)) @@ -165,9 +162,9 @@ impl WeightInfo for SubstrateWeight { fn finish_destroy() -> Weight { // Proof Size summary in bytes: // Measured: `351` - // Estimated: `7280` - // Minimum execution time: 15_870_000 picoseconds. - Weight::from_parts(16_280_000, 7280) + // Estimated: `3675` + // Minimum execution time: 16_465_000 picoseconds. + Weight::from_parts(16_775_000, 3675) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -178,9 +175,9 @@ impl WeightInfo for SubstrateWeight { fn mint() -> Weight { // Proof Size summary in bytes: // Measured: `351` - // Estimated: `7242` - // Minimum execution time: 28_556_000 picoseconds. - Weight::from_parts(28_972_000, 7242) + // Estimated: `3675` + // Minimum execution time: 30_709_000 picoseconds. + Weight::from_parts(31_159_000, 3675) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().writes(2_u64)) } @@ -191,9 +188,9 @@ impl WeightInfo for SubstrateWeight { fn burn() -> Weight { // Proof Size summary in bytes: // Measured: `459` - // Estimated: `7242` - // Minimum execution time: 35_379_000 picoseconds. - Weight::from_parts(35_826_000, 7242) + // Estimated: `3675` + // Minimum execution time: 36_944_000 picoseconds. + Weight::from_parts(38_166_000, 3675) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().writes(2_u64)) } @@ -206,9 +203,9 @@ impl WeightInfo for SubstrateWeight { fn transfer() -> Weight { // Proof Size summary in bytes: // Measured: `498` - // Estimated: `13412` - // Minimum execution time: 48_837_000 picoseconds. - Weight::from_parts(49_305_000, 13412) + // Estimated: `6144` + // Minimum execution time: 52_497_000 picoseconds. + Weight::from_parts(53_147_000, 6144) .saturating_add(T::DbWeight::get().reads(4_u64)) .saturating_add(T::DbWeight::get().writes(4_u64)) } @@ -221,9 +218,9 @@ impl WeightInfo for SubstrateWeight { fn transfer_keep_alive() -> Weight { // Proof Size summary in bytes: // Measured: `498` - // Estimated: `13412` - // Minimum execution time: 43_603_000 picoseconds. - Weight::from_parts(44_142_000, 13412) + // Estimated: `6144` + // Minimum execution time: 46_203_000 picoseconds. + Weight::from_parts(46_853_000, 6144) .saturating_add(T::DbWeight::get().reads(4_u64)) .saturating_add(T::DbWeight::get().writes(4_u64)) } @@ -236,9 +233,9 @@ impl WeightInfo for SubstrateWeight { fn force_transfer() -> Weight { // Proof Size summary in bytes: // Measured: `498` - // Estimated: `13412` - // Minimum execution time: 49_691_000 picoseconds. - Weight::from_parts(50_402_000, 13412) + // Estimated: `6144` + // Minimum execution time: 52_911_000 picoseconds. + Weight::from_parts(55_511_000, 6144) .saturating_add(T::DbWeight::get().reads(4_u64)) .saturating_add(T::DbWeight::get().writes(4_u64)) } @@ -249,9 +246,9 @@ impl WeightInfo for SubstrateWeight { fn freeze() -> Weight { // Proof Size summary in bytes: // Measured: `459` - // Estimated: `7242` - // Minimum execution time: 19_600_000 picoseconds. - Weight::from_parts(20_435_000, 7242) + // Estimated: `3675` + // Minimum execution time: 20_308_000 picoseconds. + Weight::from_parts(20_524_000, 3675) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -262,9 +259,9 @@ impl WeightInfo for SubstrateWeight { fn thaw() -> Weight { // Proof Size summary in bytes: // Measured: `459` - // Estimated: `7242` - // Minimum execution time: 19_396_000 picoseconds. - Weight::from_parts(19_745_000, 7242) + // Estimated: `3675` + // Minimum execution time: 20_735_000 picoseconds. + Weight::from_parts(21_026_000, 3675) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -274,8 +271,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `385` // Estimated: `3675` - // Minimum execution time: 15_503_000 picoseconds. - Weight::from_parts(15_788_000, 3675) + // Minimum execution time: 16_148_000 picoseconds. + Weight::from_parts(16_404_000, 3675) .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -285,8 +282,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `385` // Estimated: `3675` - // Minimum execution time: 15_874_000 picoseconds. - Weight::from_parts(16_190_000, 3675) + // Minimum execution time: 16_075_000 picoseconds. + Weight::from_parts(16_542_000, 3675) .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -297,9 +294,9 @@ impl WeightInfo for SubstrateWeight { fn transfer_ownership() -> Weight { // Proof Size summary in bytes: // Measured: `351` - // Estimated: `7280` - // Minimum execution time: 17_067_000 picoseconds. - Weight::from_parts(17_355_000, 7280) + // Estimated: `3675` + // Minimum execution time: 17_866_000 picoseconds. + Weight::from_parts(18_129_000, 3675) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -309,8 +306,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `351` // Estimated: `3675` - // Minimum execution time: 16_155_000 picoseconds. - Weight::from_parts(16_442_000, 3675) + // Minimum execution time: 16_637_000 picoseconds. + Weight::from_parts(16_918_000, 3675) .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -320,12 +317,14 @@ impl WeightInfo for SubstrateWeight { /// Proof: Assets Metadata (max_values: None, max_size: Some(140), added: 2615, mode: MaxEncodedLen) /// The range of component `n` is `[0, 50]`. /// The range of component `s` is `[0, 50]`. - fn set_metadata(_n: u32, _s: u32, ) -> Weight { + fn set_metadata(_n: u32, s: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `351` - // Estimated: `7280` - // Minimum execution time: 27_270_000 picoseconds. - Weight::from_parts(28_634_060, 7280) + // Estimated: `3675` + // Minimum execution time: 33_304_000 picoseconds. + Weight::from_parts(34_764_544, 3675) + // Standard Error: 634 + .saturating_add(Weight::from_parts(4_733, 0).saturating_mul(s.into())) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -336,9 +335,9 @@ impl WeightInfo for SubstrateWeight { fn clear_metadata() -> Weight { // Proof Size summary in bytes: // Measured: `515` - // Estimated: `7280` - // Minimum execution time: 27_469_000 picoseconds. - Weight::from_parts(27_911_000, 7280) + // Estimated: `3675` + // Minimum execution time: 33_640_000 picoseconds. + Weight::from_parts(34_100_000, 3675) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -348,14 +347,16 @@ impl WeightInfo for SubstrateWeight { /// Proof: Assets Metadata (max_values: None, max_size: Some(140), added: 2615, mode: MaxEncodedLen) /// The range of component `n` is `[0, 50]`. /// The range of component `s` is `[0, 50]`. - fn force_set_metadata(_n: u32, s: u32, ) -> Weight { + fn force_set_metadata(n: u32, s: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `190` - // Estimated: `7280` - // Minimum execution time: 16_008_000 picoseconds. - Weight::from_parts(17_007_654, 7280) - // Standard Error: 527 - .saturating_add(Weight::from_parts(4_158, 0).saturating_mul(s.into())) + // Estimated: `3675` + // Minimum execution time: 16_549_000 picoseconds. + Weight::from_parts(17_337_982, 3675) + // Standard Error: 586 + .saturating_add(Weight::from_parts(4_584, 0).saturating_mul(n.into())) + // Standard Error: 586 + .saturating_add(Weight::from_parts(4_288, 0).saturating_mul(s.into())) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -366,9 +367,9 @@ impl WeightInfo for SubstrateWeight { fn force_clear_metadata() -> Weight { // Proof Size summary in bytes: // Measured: `515` - // Estimated: `7280` - // Minimum execution time: 27_118_000 picoseconds. - Weight::from_parts(27_776_000, 7280) + // Estimated: `3675` + // Minimum execution time: 34_183_000 picoseconds. + Weight::from_parts(34_577_000, 3675) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -378,8 +379,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `351` // Estimated: `3675` - // Minimum execution time: 14_776_000 picoseconds. - Weight::from_parts(15_281_000, 3675) + // Minimum execution time: 15_505_000 picoseconds. + Weight::from_parts(15_784_000, 3675) .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -390,9 +391,9 @@ impl WeightInfo for SubstrateWeight { fn approve_transfer() -> Weight { // Proof Size summary in bytes: // Measured: `385` - // Estimated: `7288` - // Minimum execution time: 31_324_000 picoseconds. - Weight::from_parts(32_161_000, 7288) + // Estimated: `3675` + // Minimum execution time: 38_762_000 picoseconds. + Weight::from_parts(39_138_000, 3675) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().writes(2_u64)) } @@ -407,9 +408,9 @@ impl WeightInfo for SubstrateWeight { fn transfer_approved() -> Weight { // Proof Size summary in bytes: // Measured: `668` - // Estimated: `17025` - // Minimum execution time: 63_750_000 picoseconds. - Weight::from_parts(64_828_000, 17025) + // Estimated: `6144` + // Minimum execution time: 73_396_000 picoseconds. + Weight::from_parts(73_986_000, 6144) .saturating_add(T::DbWeight::get().reads(5_u64)) .saturating_add(T::DbWeight::get().writes(5_u64)) } @@ -420,9 +421,9 @@ impl WeightInfo for SubstrateWeight { fn cancel_approval() -> Weight { // Proof Size summary in bytes: // Measured: `555` - // Estimated: `7288` - // Minimum execution time: 32_698_000 picoseconds. - Weight::from_parts(33_309_000, 7288) + // Estimated: `3675` + // Minimum execution time: 39_707_000 picoseconds. + Weight::from_parts(40_222_000, 3675) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().writes(2_u64)) } @@ -433,9 +434,9 @@ impl WeightInfo for SubstrateWeight { fn force_cancel_approval() -> Weight { // Proof Size summary in bytes: // Measured: `555` - // Estimated: `7288` - // Minimum execution time: 33_147_000 picoseconds. - Weight::from_parts(33_620_000, 7288) + // Estimated: `3675` + // Minimum execution time: 40_357_000 picoseconds. + Weight::from_parts(40_731_000, 3675) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().writes(2_u64)) } @@ -445,8 +446,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `351` // Estimated: `3675` - // Minimum execution time: 15_910_000 picoseconds. - Weight::from_parts(16_315_000, 3675) + // Minimum execution time: 16_937_000 picoseconds. + Weight::from_parts(17_219_000, 3675) .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -461,9 +462,9 @@ impl WeightInfo for () { fn create() -> Weight { // Proof Size summary in bytes: // Measured: `293` - // Estimated: `7268` - // Minimum execution time: 27_756_000 picoseconds. - Weight::from_parts(28_637_000, 7268) + // Estimated: `3675` + // Minimum execution time: 33_678_000 picoseconds. + Weight::from_parts(34_320_000, 3675) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().writes(2_u64)) } @@ -473,8 +474,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `153` // Estimated: `3675` - // Minimum execution time: 15_402_000 picoseconds. - Weight::from_parts(15_803_000, 3675) + // Minimum execution time: 15_521_000 picoseconds. + Weight::from_parts(16_035_000, 3675) .saturating_add(RocksDbWeight::get().reads(1_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } @@ -484,8 +485,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `385` // Estimated: `3675` - // Minimum execution time: 16_027_000 picoseconds. - Weight::from_parts(16_372_000, 3675) + // Minimum execution time: 15_921_000 picoseconds. + Weight::from_parts(16_153_000, 3675) .saturating_add(RocksDbWeight::get().reads(1_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } @@ -499,16 +500,16 @@ impl WeightInfo for () { fn destroy_accounts(c: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0 + c * (208 ±0)` - // Estimated: `8232 + c * (5180 ±0)` - // Minimum execution time: 20_667_000 picoseconds. - Weight::from_parts(20_930_000, 8232) - // Standard Error: 7_226 - .saturating_add(Weight::from_parts(12_759_091, 0).saturating_mul(c.into())) + // Estimated: `3675 + c * (2603 ±0)` + // Minimum execution time: 21_242_000 picoseconds. + Weight::from_parts(21_532_000, 3675) + // Standard Error: 6_449 + .saturating_add(Weight::from_parts(13_150_845, 0).saturating_mul(c.into())) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().reads((2_u64).saturating_mul(c.into()))) .saturating_add(RocksDbWeight::get().writes(1_u64)) .saturating_add(RocksDbWeight::get().writes((2_u64).saturating_mul(c.into()))) - .saturating_add(Weight::from_parts(0, 5180).saturating_mul(c.into())) + .saturating_add(Weight::from_parts(0, 2603).saturating_mul(c.into())) } /// Storage: Assets Asset (r:1 w:1) /// Proof: Assets Asset (max_values: None, max_size: Some(210), added: 2685, mode: MaxEncodedLen) @@ -518,11 +519,11 @@ impl WeightInfo for () { fn destroy_approvals(a: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `522 + a * (86 ±0)` - // Estimated: `7288 + a * (2623 ±0)` - // Minimum execution time: 20_798_000 picoseconds. - Weight::from_parts(21_129_000, 7288) - // Standard Error: 7_484 - .saturating_add(Weight::from_parts(12_761_996, 0).saturating_mul(a.into())) + // Estimated: `3675 + a * (2623 ±0)` + // Minimum execution time: 21_604_000 picoseconds. + Weight::from_parts(21_881_000, 3675) + // Standard Error: 4_225 + .saturating_add(Weight::from_parts(15_968_205, 0).saturating_mul(a.into())) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().reads((1_u64).saturating_mul(a.into()))) .saturating_add(RocksDbWeight::get().writes(1_u64)) @@ -536,9 +537,9 @@ impl WeightInfo for () { fn finish_destroy() -> Weight { // Proof Size summary in bytes: // Measured: `351` - // Estimated: `7280` - // Minimum execution time: 15_870_000 picoseconds. - Weight::from_parts(16_280_000, 7280) + // Estimated: `3675` + // Minimum execution time: 16_465_000 picoseconds. + Weight::from_parts(16_775_000, 3675) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } @@ -549,9 +550,9 @@ impl WeightInfo for () { fn mint() -> Weight { // Proof Size summary in bytes: // Measured: `351` - // Estimated: `7242` - // Minimum execution time: 28_556_000 picoseconds. - Weight::from_parts(28_972_000, 7242) + // Estimated: `3675` + // Minimum execution time: 30_709_000 picoseconds. + Weight::from_parts(31_159_000, 3675) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().writes(2_u64)) } @@ -562,9 +563,9 @@ impl WeightInfo for () { fn burn() -> Weight { // Proof Size summary in bytes: // Measured: `459` - // Estimated: `7242` - // Minimum execution time: 35_379_000 picoseconds. - Weight::from_parts(35_826_000, 7242) + // Estimated: `3675` + // Minimum execution time: 36_944_000 picoseconds. + Weight::from_parts(38_166_000, 3675) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().writes(2_u64)) } @@ -577,9 +578,9 @@ impl WeightInfo for () { fn transfer() -> Weight { // Proof Size summary in bytes: // Measured: `498` - // Estimated: `13412` - // Minimum execution time: 48_837_000 picoseconds. - Weight::from_parts(49_305_000, 13412) + // Estimated: `6144` + // Minimum execution time: 52_497_000 picoseconds. + Weight::from_parts(53_147_000, 6144) .saturating_add(RocksDbWeight::get().reads(4_u64)) .saturating_add(RocksDbWeight::get().writes(4_u64)) } @@ -592,9 +593,9 @@ impl WeightInfo for () { fn transfer_keep_alive() -> Weight { // Proof Size summary in bytes: // Measured: `498` - // Estimated: `13412` - // Minimum execution time: 43_603_000 picoseconds. - Weight::from_parts(44_142_000, 13412) + // Estimated: `6144` + // Minimum execution time: 46_203_000 picoseconds. + Weight::from_parts(46_853_000, 6144) .saturating_add(RocksDbWeight::get().reads(4_u64)) .saturating_add(RocksDbWeight::get().writes(4_u64)) } @@ -607,9 +608,9 @@ impl WeightInfo for () { fn force_transfer() -> Weight { // Proof Size summary in bytes: // Measured: `498` - // Estimated: `13412` - // Minimum execution time: 49_691_000 picoseconds. - Weight::from_parts(50_402_000, 13412) + // Estimated: `6144` + // Minimum execution time: 52_911_000 picoseconds. + Weight::from_parts(55_511_000, 6144) .saturating_add(RocksDbWeight::get().reads(4_u64)) .saturating_add(RocksDbWeight::get().writes(4_u64)) } @@ -620,9 +621,9 @@ impl WeightInfo for () { fn freeze() -> Weight { // Proof Size summary in bytes: // Measured: `459` - // Estimated: `7242` - // Minimum execution time: 19_600_000 picoseconds. - Weight::from_parts(20_435_000, 7242) + // Estimated: `3675` + // Minimum execution time: 20_308_000 picoseconds. + Weight::from_parts(20_524_000, 3675) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } @@ -633,9 +634,9 @@ impl WeightInfo for () { fn thaw() -> Weight { // Proof Size summary in bytes: // Measured: `459` - // Estimated: `7242` - // Minimum execution time: 19_396_000 picoseconds. - Weight::from_parts(19_745_000, 7242) + // Estimated: `3675` + // Minimum execution time: 20_735_000 picoseconds. + Weight::from_parts(21_026_000, 3675) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } @@ -645,8 +646,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `385` // Estimated: `3675` - // Minimum execution time: 15_503_000 picoseconds. - Weight::from_parts(15_788_000, 3675) + // Minimum execution time: 16_148_000 picoseconds. + Weight::from_parts(16_404_000, 3675) .saturating_add(RocksDbWeight::get().reads(1_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } @@ -656,8 +657,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `385` // Estimated: `3675` - // Minimum execution time: 15_874_000 picoseconds. - Weight::from_parts(16_190_000, 3675) + // Minimum execution time: 16_075_000 picoseconds. + Weight::from_parts(16_542_000, 3675) .saturating_add(RocksDbWeight::get().reads(1_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } @@ -668,9 +669,9 @@ impl WeightInfo for () { fn transfer_ownership() -> Weight { // Proof Size summary in bytes: // Measured: `351` - // Estimated: `7280` - // Minimum execution time: 17_067_000 picoseconds. - Weight::from_parts(17_355_000, 7280) + // Estimated: `3675` + // Minimum execution time: 17_866_000 picoseconds. + Weight::from_parts(18_129_000, 3675) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } @@ -680,8 +681,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `351` // Estimated: `3675` - // Minimum execution time: 16_155_000 picoseconds. - Weight::from_parts(16_442_000, 3675) + // Minimum execution time: 16_637_000 picoseconds. + Weight::from_parts(16_918_000, 3675) .saturating_add(RocksDbWeight::get().reads(1_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } @@ -691,12 +692,14 @@ impl WeightInfo for () { /// Proof: Assets Metadata (max_values: None, max_size: Some(140), added: 2615, mode: MaxEncodedLen) /// The range of component `n` is `[0, 50]`. /// The range of component `s` is `[0, 50]`. - fn set_metadata(_n: u32, _s: u32, ) -> Weight { + fn set_metadata(_n: u32, s: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `351` - // Estimated: `7280` - // Minimum execution time: 27_270_000 picoseconds. - Weight::from_parts(28_634_060, 7280) + // Estimated: `3675` + // Minimum execution time: 33_304_000 picoseconds. + Weight::from_parts(34_764_544, 3675) + // Standard Error: 634 + .saturating_add(Weight::from_parts(4_733, 0).saturating_mul(s.into())) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } @@ -707,9 +710,9 @@ impl WeightInfo for () { fn clear_metadata() -> Weight { // Proof Size summary in bytes: // Measured: `515` - // Estimated: `7280` - // Minimum execution time: 27_469_000 picoseconds. - Weight::from_parts(27_911_000, 7280) + // Estimated: `3675` + // Minimum execution time: 33_640_000 picoseconds. + Weight::from_parts(34_100_000, 3675) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } @@ -719,14 +722,16 @@ impl WeightInfo for () { /// Proof: Assets Metadata (max_values: None, max_size: Some(140), added: 2615, mode: MaxEncodedLen) /// The range of component `n` is `[0, 50]`. /// The range of component `s` is `[0, 50]`. - fn force_set_metadata(_n: u32, s: u32, ) -> Weight { + fn force_set_metadata(n: u32, s: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `190` - // Estimated: `7280` - // Minimum execution time: 16_008_000 picoseconds. - Weight::from_parts(17_007_654, 7280) - // Standard Error: 527 - .saturating_add(Weight::from_parts(4_158, 0).saturating_mul(s.into())) + // Estimated: `3675` + // Minimum execution time: 16_549_000 picoseconds. + Weight::from_parts(17_337_982, 3675) + // Standard Error: 586 + .saturating_add(Weight::from_parts(4_584, 0).saturating_mul(n.into())) + // Standard Error: 586 + .saturating_add(Weight::from_parts(4_288, 0).saturating_mul(s.into())) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } @@ -737,9 +742,9 @@ impl WeightInfo for () { fn force_clear_metadata() -> Weight { // Proof Size summary in bytes: // Measured: `515` - // Estimated: `7280` - // Minimum execution time: 27_118_000 picoseconds. - Weight::from_parts(27_776_000, 7280) + // Estimated: `3675` + // Minimum execution time: 34_183_000 picoseconds. + Weight::from_parts(34_577_000, 3675) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } @@ -749,8 +754,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `351` // Estimated: `3675` - // Minimum execution time: 14_776_000 picoseconds. - Weight::from_parts(15_281_000, 3675) + // Minimum execution time: 15_505_000 picoseconds. + Weight::from_parts(15_784_000, 3675) .saturating_add(RocksDbWeight::get().reads(1_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } @@ -761,9 +766,9 @@ impl WeightInfo for () { fn approve_transfer() -> Weight { // Proof Size summary in bytes: // Measured: `385` - // Estimated: `7288` - // Minimum execution time: 31_324_000 picoseconds. - Weight::from_parts(32_161_000, 7288) + // Estimated: `3675` + // Minimum execution time: 38_762_000 picoseconds. + Weight::from_parts(39_138_000, 3675) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().writes(2_u64)) } @@ -778,9 +783,9 @@ impl WeightInfo for () { fn transfer_approved() -> Weight { // Proof Size summary in bytes: // Measured: `668` - // Estimated: `17025` - // Minimum execution time: 63_750_000 picoseconds. - Weight::from_parts(64_828_000, 17025) + // Estimated: `6144` + // Minimum execution time: 73_396_000 picoseconds. + Weight::from_parts(73_986_000, 6144) .saturating_add(RocksDbWeight::get().reads(5_u64)) .saturating_add(RocksDbWeight::get().writes(5_u64)) } @@ -791,9 +796,9 @@ impl WeightInfo for () { fn cancel_approval() -> Weight { // Proof Size summary in bytes: // Measured: `555` - // Estimated: `7288` - // Minimum execution time: 32_698_000 picoseconds. - Weight::from_parts(33_309_000, 7288) + // Estimated: `3675` + // Minimum execution time: 39_707_000 picoseconds. + Weight::from_parts(40_222_000, 3675) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().writes(2_u64)) } @@ -804,9 +809,9 @@ impl WeightInfo for () { fn force_cancel_approval() -> Weight { // Proof Size summary in bytes: // Measured: `555` - // Estimated: `7288` - // Minimum execution time: 33_147_000 picoseconds. - Weight::from_parts(33_620_000, 7288) + // Estimated: `3675` + // Minimum execution time: 40_357_000 picoseconds. + Weight::from_parts(40_731_000, 3675) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().writes(2_u64)) } @@ -816,8 +821,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `351` // Estimated: `3675` - // Minimum execution time: 15_910_000 picoseconds. - Weight::from_parts(16_315_000, 3675) + // Minimum execution time: 16_937_000 picoseconds. + Weight::from_parts(17_219_000, 3675) .saturating_add(RocksDbWeight::get().reads(1_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } diff --git a/frame/bags-list/src/weights.rs b/frame/bags-list/src/weights.rs index 30efe1ef0..f2b65beba 100644 --- a/frame/bags-list/src/weights.rs +++ b/frame/bags-list/src/weights.rs @@ -18,9 +18,9 @@ //! Autogenerated weights for pallet_bags_list //! //! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev -//! DATE: 2023-03-15, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! DATE: 2023-04-06, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` //! WORST CASE MAP SIZE: `1000000` -//! HOSTNAME: `bm3`, CPU: `Intel(R) Core(TM) i7-7700K CPU @ 4.20GHz` +//! HOSTNAME: `bm2`, CPU: `Intel(R) Core(TM) i7-7700K CPU @ 4.20GHz` //! EXECUTION: Some(Wasm), WASM-EXECUTION: Compiled, CHAIN: Some("dev"), DB CACHE: 1024 // Executed Command: @@ -31,9 +31,6 @@ // --steps=50 // --repeat=20 // --pallet=pallet_bags_list -// --no-storage-info -// --no-median-slopes -// --no-min-squares // --extrinsic=* // --execution=wasm // --wasm-execution=compiled @@ -70,9 +67,9 @@ impl WeightInfo for SubstrateWeight { fn rebag_non_terminal() -> Weight { // Proof Size summary in bytes: // Measured: `1724` - // Estimated: `23146` - // Minimum execution time: 63_526_000 picoseconds. - Weight::from_parts(64_125_000, 23146) + // Estimated: `11506` + // Minimum execution time: 63_335_000 picoseconds. + Weight::from_parts(64_097_000, 11506) .saturating_add(T::DbWeight::get().reads(7_u64)) .saturating_add(T::DbWeight::get().writes(5_u64)) } @@ -87,9 +84,9 @@ impl WeightInfo for SubstrateWeight { fn rebag_terminal() -> Weight { // Proof Size summary in bytes: // Measured: `1618` - // Estimated: `23074` - // Minimum execution time: 62_404_000 picoseconds. - Weight::from_parts(63_178_000, 23074) + // Estimated: `8877` + // Minimum execution time: 62_151_000 picoseconds. + Weight::from_parts(62_827_000, 8877) .saturating_add(T::DbWeight::get().reads(7_u64)) .saturating_add(T::DbWeight::get().writes(5_u64)) } @@ -106,9 +103,9 @@ impl WeightInfo for SubstrateWeight { fn put_in_front_of() -> Weight { // Proof Size summary in bytes: // Measured: `1930` - // Estimated: `30748` - // Minimum execution time: 69_938_000 picoseconds. - Weight::from_parts(70_723_000, 30748) + // Estimated: `11506` + // Minimum execution time: 69_179_000 picoseconds. + Weight::from_parts(69_898_000, 11506) .saturating_add(T::DbWeight::get().reads(10_u64)) .saturating_add(T::DbWeight::get().writes(6_u64)) } @@ -127,9 +124,9 @@ impl WeightInfo for () { fn rebag_non_terminal() -> Weight { // Proof Size summary in bytes: // Measured: `1724` - // Estimated: `23146` - // Minimum execution time: 63_526_000 picoseconds. - Weight::from_parts(64_125_000, 23146) + // Estimated: `11506` + // Minimum execution time: 63_335_000 picoseconds. + Weight::from_parts(64_097_000, 11506) .saturating_add(RocksDbWeight::get().reads(7_u64)) .saturating_add(RocksDbWeight::get().writes(5_u64)) } @@ -144,9 +141,9 @@ impl WeightInfo for () { fn rebag_terminal() -> Weight { // Proof Size summary in bytes: // Measured: `1618` - // Estimated: `23074` - // Minimum execution time: 62_404_000 picoseconds. - Weight::from_parts(63_178_000, 23074) + // Estimated: `8877` + // Minimum execution time: 62_151_000 picoseconds. + Weight::from_parts(62_827_000, 8877) .saturating_add(RocksDbWeight::get().reads(7_u64)) .saturating_add(RocksDbWeight::get().writes(5_u64)) } @@ -163,9 +160,9 @@ impl WeightInfo for () { fn put_in_front_of() -> Weight { // Proof Size summary in bytes: // Measured: `1930` - // Estimated: `30748` - // Minimum execution time: 69_938_000 picoseconds. - Weight::from_parts(70_723_000, 30748) + // Estimated: `11506` + // Minimum execution time: 69_179_000 picoseconds. + Weight::from_parts(69_898_000, 11506) .saturating_add(RocksDbWeight::get().reads(10_u64)) .saturating_add(RocksDbWeight::get().writes(6_u64)) } diff --git a/frame/balances/src/weights.rs b/frame/balances/src/weights.rs index a2a9f0019..f35d9c697 100644 --- a/frame/balances/src/weights.rs +++ b/frame/balances/src/weights.rs @@ -18,26 +18,25 @@ //! Autogenerated weights for pallet_balances //! //! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev -//! DATE: 2023-03-17, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! DATE: 2023-04-06, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` //! WORST CASE MAP SIZE: `1000000` -//! HOSTNAME: `bm3`, CPU: `Intel(R) Core(TM) i7-7700K CPU @ 4.20GHz` +//! HOSTNAME: `bm2`, CPU: `Intel(R) Core(TM) i7-7700K CPU @ 4.20GHz` //! EXECUTION: Some(Wasm), WASM-EXECUTION: Compiled, CHAIN: Some("dev"), DB CACHE: 1024 // Executed Command: -// target/production/substrate +// ./target/production/substrate // benchmark // pallet +// --chain=dev // --steps=50 // --repeat=20 +// --pallet=pallet_balances // --extrinsic=* // --execution=wasm // --wasm-execution=compiled // --heap-pages=4096 -// --json-file=/var/lib/gitlab-runner/builds/zyw4fam_/0/parity/mirrors/substrate/.git/.artifacts/bench.json -// --pallet=pallet_balances -// --chain=dev -// --header=./HEADER-APACHE2 // --output=./frame/balances/src/weights.rs +// --header=./HEADER-APACHE2 // --template=./.maintain/frame-weight-template.hbs #![cfg_attr(rustfmt, rustfmt_skip)] @@ -68,8 +67,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `3593` - // Minimum execution time: 36_551_000 picoseconds. - Weight::from_parts(37_150_000, 3593) + // Minimum execution time: 59_458_000 picoseconds. + Weight::from_parts(60_307_000, 3593) .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -79,8 +78,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `3593` - // Minimum execution time: 28_246_000 picoseconds. - Weight::from_parts(28_647_000, 3593) + // Minimum execution time: 43_056_000 picoseconds. + Weight::from_parts(43_933_000, 3593) .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -90,8 +89,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `174` // Estimated: `3593` - // Minimum execution time: 17_407_000 picoseconds. - Weight::from_parts(17_827_000, 3593) + // Minimum execution time: 17_428_000 picoseconds. + Weight::from_parts(17_731_000, 3593) .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -101,8 +100,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `174` // Estimated: `3593` - // Minimum execution time: 20_941_000 picoseconds. - Weight::from_parts(21_383_000, 3593) + // Minimum execution time: 22_809_000 picoseconds. + Weight::from_parts(23_225_000, 3593) .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -112,8 +111,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `103` // Estimated: `6196` - // Minimum execution time: 38_741_000 picoseconds. - Weight::from_parts(39_201_000, 6196) + // Minimum execution time: 56_929_000 picoseconds. + Weight::from_parts(57_688_000, 6196) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().writes(2_u64)) } @@ -123,8 +122,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `3593` - // Minimum execution time: 33_791_000 picoseconds. - Weight::from_parts(34_500_000, 3593) + // Minimum execution time: 49_820_000 picoseconds. + Weight::from_parts(50_832_000, 3593) .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -134,8 +133,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `174` // Estimated: `3593` - // Minimum execution time: 16_538_000 picoseconds. - Weight::from_parts(16_859_000, 3593) + // Minimum execution time: 20_270_000 picoseconds. + Weight::from_parts(20_597_000, 3593) .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -146,10 +145,10 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `0 + u * (135 ±0)` // Estimated: `990 + u * (2603 ±0)` - // Minimum execution time: 19_851_000 picoseconds. - Weight::from_parts(20_099_000, 990) - // Standard Error: 15_586 - .saturating_add(Weight::from_parts(14_892_860, 0).saturating_mul(u.into())) + // Minimum execution time: 19_847_000 picoseconds. + Weight::from_parts(20_053_000, 990) + // Standard Error: 11_643 + .saturating_add(Weight::from_parts(14_563_782, 0).saturating_mul(u.into())) .saturating_add(T::DbWeight::get().reads((1_u64).saturating_mul(u.into()))) .saturating_add(T::DbWeight::get().writes((1_u64).saturating_mul(u.into()))) .saturating_add(Weight::from_parts(0, 2603).saturating_mul(u.into())) @@ -164,8 +163,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `0` // Estimated: `3593` - // Minimum execution time: 36_551_000 picoseconds. - Weight::from_parts(37_150_000, 3593) + // Minimum execution time: 59_458_000 picoseconds. + Weight::from_parts(60_307_000, 3593) .saturating_add(RocksDbWeight::get().reads(1_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } @@ -175,8 +174,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `0` // Estimated: `3593` - // Minimum execution time: 28_246_000 picoseconds. - Weight::from_parts(28_647_000, 3593) + // Minimum execution time: 43_056_000 picoseconds. + Weight::from_parts(43_933_000, 3593) .saturating_add(RocksDbWeight::get().reads(1_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } @@ -186,8 +185,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `174` // Estimated: `3593` - // Minimum execution time: 17_407_000 picoseconds. - Weight::from_parts(17_827_000, 3593) + // Minimum execution time: 17_428_000 picoseconds. + Weight::from_parts(17_731_000, 3593) .saturating_add(RocksDbWeight::get().reads(1_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } @@ -197,8 +196,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `174` // Estimated: `3593` - // Minimum execution time: 20_941_000 picoseconds. - Weight::from_parts(21_383_000, 3593) + // Minimum execution time: 22_809_000 picoseconds. + Weight::from_parts(23_225_000, 3593) .saturating_add(RocksDbWeight::get().reads(1_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } @@ -208,8 +207,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `103` // Estimated: `6196` - // Minimum execution time: 38_741_000 picoseconds. - Weight::from_parts(39_201_000, 6196) + // Minimum execution time: 56_929_000 picoseconds. + Weight::from_parts(57_688_000, 6196) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().writes(2_u64)) } @@ -219,8 +218,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `0` // Estimated: `3593` - // Minimum execution time: 33_791_000 picoseconds. - Weight::from_parts(34_500_000, 3593) + // Minimum execution time: 49_820_000 picoseconds. + Weight::from_parts(50_832_000, 3593) .saturating_add(RocksDbWeight::get().reads(1_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } @@ -230,8 +229,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `174` // Estimated: `3593` - // Minimum execution time: 16_538_000 picoseconds. - Weight::from_parts(16_859_000, 3593) + // Minimum execution time: 20_270_000 picoseconds. + Weight::from_parts(20_597_000, 3593) .saturating_add(RocksDbWeight::get().reads(1_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } @@ -242,10 +241,10 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `0 + u * (135 ±0)` // Estimated: `990 + u * (2603 ±0)` - // Minimum execution time: 19_851_000 picoseconds. - Weight::from_parts(20_099_000, 990) - // Standard Error: 15_586 - .saturating_add(Weight::from_parts(14_892_860, 0).saturating_mul(u.into())) + // Minimum execution time: 19_847_000 picoseconds. + Weight::from_parts(20_053_000, 990) + // Standard Error: 11_643 + .saturating_add(Weight::from_parts(14_563_782, 0).saturating_mul(u.into())) .saturating_add(RocksDbWeight::get().reads((1_u64).saturating_mul(u.into()))) .saturating_add(RocksDbWeight::get().writes((1_u64).saturating_mul(u.into()))) .saturating_add(Weight::from_parts(0, 2603).saturating_mul(u.into())) diff --git a/frame/benchmarking/pov/src/benchmarking.rs b/frame/benchmarking/pov/src/benchmarking.rs index 46a98ae3f..27191e372 100644 --- a/frame/benchmarking/pov/src/benchmarking.rs +++ b/frame/benchmarking/pov/src/benchmarking.rs @@ -192,6 +192,7 @@ frame_benchmarking::benchmarks! { // Same as above, but we still expect a mathematical worst case PoV size for the bounded one. storage_value_bounded_and_unbounded_read { + (0..1024).for_each(|i| Map1M::::insert(i, i)); }: { assert!(UnboundedValue::::get().is_none()); assert!(BoundedValue::::get().is_none()); diff --git a/frame/benchmarking/pov/src/tests.rs b/frame/benchmarking/pov/src/tests.rs index 19156b930..b908925cc 100644 --- a/frame/benchmarking/pov/src/tests.rs +++ b/frame/benchmarking/pov/src/tests.rs @@ -144,9 +144,9 @@ fn unbounded_read_best_effort() { fn partial_unbounded_read_best_effort() { let w_unbounded = W::storage_value_unbounded_read().proof_size(); let w_bounded = W::storage_value_bounded_read().proof_size(); - let w_partial = W::storage_value_bounded_and_unbounded_read().proof_size(); + let w_both = W::storage_value_bounded_and_unbounded_read().proof_size(); - assert_eq!(w_bounded + w_unbounded, w_partial, "The bounded part increases the PoV"); + assert!(w_both > w_bounded && w_both > w_unbounded, "The bounded part increases the PoV"); } #[test] diff --git a/frame/benchmarking/pov/src/weights.rs b/frame/benchmarking/pov/src/weights.rs index df6dba33b..f16ac7fbc 100644 --- a/frame/benchmarking/pov/src/weights.rs +++ b/frame/benchmarking/pov/src/weights.rs @@ -2,7 +2,7 @@ //! Autogenerated weights for frame_benchmarking_pallet_pov //! //! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev -//! DATE: 2023-02-27, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! DATE: 2023-04-12, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` //! WORST CASE MAP SIZE: `1000000` //! HOSTNAME: `i9`, CPU: `13th Gen Intel(R) Core(TM) i9-13900K` //! EXECUTION: None, WASM-EXECUTION: Compiled, CHAIN: None, DB CACHE: 1024 @@ -28,7 +28,7 @@ #![allow(unused_imports)] use frame_support::{traits::Get, weights::{Weight, constants::RocksDbWeight}}; -use sp_std::marker::PhantomData; +use core::marker::PhantomData; /// Weight functions needed for frame_benchmarking_pallet_pov. pub trait WeightInfo { @@ -61,6 +61,7 @@ pub trait WeightInfo { fn storage_map_partial_unbounded_ignored_read(i: u32, ) -> Weight; fn emit_event() -> Weight; fn noop() -> Weight; + fn storage_iteration() -> Weight; } /// Weights for frame_benchmarking_pallet_pov using the Substrate node and recommended hardware. @@ -72,9 +73,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `136` // Estimated: `1489` - // Minimum execution time: 1_968 nanoseconds. - Weight::from_parts(2_060_000, 0) - .saturating_add(Weight::from_parts(0, 1489)) + // Minimum execution time: 1_706_000 picoseconds. + Weight::from_parts(1_788_000, 1489) .saturating_add(T::DbWeight::get().reads(1_u64)) } /// Storage: Pov Value (r:1 w:0) @@ -83,9 +83,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `136` // Estimated: `0` - // Minimum execution time: 1_934 nanoseconds. - Weight::from_parts(2_092_000, 0) - .saturating_add(Weight::from_parts(0, 0)) + // Minimum execution time: 1_661_000 picoseconds. + Weight::from_parts(1_718_000, 0) .saturating_add(T::DbWeight::get().reads(1_u64)) } /// Storage: Pov Value (r:1 w:0) @@ -95,10 +94,9 @@ impl WeightInfo for SubstrateWeight { fn storage_single_value_ignored_some_read() -> Weight { // Proof Size summary in bytes: // Measured: `160` - // Estimated: `1649` - // Minimum execution time: 2_605 nanoseconds. - Weight::from_parts(2_786_000, 0) - .saturating_add(Weight::from_parts(0, 1649)) + // Estimated: `1489` + // Minimum execution time: 2_226_000 picoseconds. + Weight::from_parts(2_365_000, 1489) .saturating_add(T::DbWeight::get().reads(2_u64)) } /// Storage: Pov Value (r:1 w:0) @@ -107,9 +105,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `136` // Estimated: `1489` - // Minimum execution time: 2_019 nanoseconds. - Weight::from_parts(2_214_000, 0) - .saturating_add(Weight::from_parts(0, 1489)) + // Minimum execution time: 1_785_000 picoseconds. + Weight::from_parts(1_980_000, 1489) .saturating_add(T::DbWeight::get().reads(1_u64)) } /// Storage: Pov Value (r:0 w:1) @@ -118,9 +115,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 279 nanoseconds. - Weight::from_parts(357_000, 0) - .saturating_add(Weight::from_parts(0, 0)) + // Minimum execution time: 254_000 picoseconds. + Weight::from_parts(326_000, 0) .saturating_add(T::DbWeight::get().writes(1_u64)) } /// Storage: Pov Value (r:0 w:1) @@ -129,9 +125,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 291 nanoseconds. - Weight::from_parts(378_000, 0) - .saturating_add(Weight::from_parts(0, 0)) + // Minimum execution time: 239_000 picoseconds. + Weight::from_parts(277_000, 0) .saturating_add(T::DbWeight::get().writes(1_u64)) } /// Storage: Pov Map1M (r:1 w:0) @@ -140,9 +135,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `1275` // Estimated: `4740` - // Minimum execution time: 5_077 nanoseconds. - Weight::from_parts(5_400_000, 0) - .saturating_add(Weight::from_parts(0, 4740)) + // Minimum execution time: 4_760_000 picoseconds. + Weight::from_parts(5_051_000, 4740) .saturating_add(T::DbWeight::get().reads(1_u64)) } /// Storage: Pov Map1M (r:1 w:0) @@ -151,9 +145,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `1544` // Estimated: `5009` - // Minimum execution time: 5_878 nanoseconds. - Weight::from_parts(6_239_000, 0) - .saturating_add(Weight::from_parts(0, 5009)) + // Minimum execution time: 5_490_000 picoseconds. + Weight::from_parts(5_703_000, 5009) .saturating_add(T::DbWeight::get().reads(1_u64)) } /// Storage: Pov Map1M (r:1 w:0) @@ -162,9 +155,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `2044` // Estimated: `5509` - // Minimum execution time: 7_282 nanoseconds. - Weight::from_parts(8_022_000, 0) - .saturating_add(Weight::from_parts(0, 5509)) + // Minimum execution time: 6_397_000 picoseconds. + Weight::from_parts(7_084_000, 5509) .saturating_add(T::DbWeight::get().reads(1_u64)) } /// Storage: Pov Map1M (r:100 w:0) @@ -175,19 +167,18 @@ impl WeightInfo for SubstrateWeight { /// The range of component `m` is `[0, 100]`. fn storage_map_read_per_component(n: u32, m: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `515 + n * (188 ±0) + m * (188 ±0)` - // Estimated: `1980 + n * (3006 ±0) + m * (2511 ±0)` - // Minimum execution time: 195_406 nanoseconds. - Weight::from_parts(129_093_464, 0) - .saturating_add(Weight::from_parts(0, 1980)) - // Standard Error: 12_134 - .saturating_add(Weight::from_parts(855_330, 0).saturating_mul(n.into())) - // Standard Error: 12_134 - .saturating_add(Weight::from_parts(870_523, 0).saturating_mul(m.into())) + // Measured: `515 + m * (188 ±0) + n * (188 ±0)` + // Estimated: `990 + m * (2511 ±0) + n * (3006 ±0)` + // Minimum execution time: 181_481_000 picoseconds. + Weight::from_parts(129_275_141, 990) + // Standard Error: 13_049 + .saturating_add(Weight::from_parts(787_667, 0).saturating_mul(n.into())) + // Standard Error: 13_049 + .saturating_add(Weight::from_parts(830_378, 0).saturating_mul(m.into())) .saturating_add(T::DbWeight::get().reads((1_u64).saturating_mul(n.into()))) .saturating_add(T::DbWeight::get().reads((1_u64).saturating_mul(m.into()))) - .saturating_add(Weight::from_parts(0, 3006).saturating_mul(n.into())) .saturating_add(Weight::from_parts(0, 2511).saturating_mul(m.into())) + .saturating_add(Weight::from_parts(0, 3006).saturating_mul(n.into())) } /// Storage: Pov Map1M (r:100 w:0) /// Proof: Pov Map1M (max_values: Some(1000000), max_size: Some(36), added: 2511, mode: Ignored) @@ -197,19 +188,18 @@ impl WeightInfo for SubstrateWeight { /// The range of component `m` is `[0, 100]`. fn storage_map_read_per_component_one_ignored(n: u32, m: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `515 + n * (188 ±0) + m * (188 ±0)` - // Estimated: `1685 + n * (3195 ±0) + m * (189 ±0)` - // Minimum execution time: 195_053 nanoseconds. - Weight::from_parts(131_322_479, 0) - .saturating_add(Weight::from_parts(0, 1685)) - // Standard Error: 12_161 - .saturating_add(Weight::from_parts(843_047, 0).saturating_mul(n.into())) - // Standard Error: 12_161 - .saturating_add(Weight::from_parts(858_668, 0).saturating_mul(m.into())) + // Measured: `515 + m * (188 ±0) + n * (188 ±0)` + // Estimated: `1685 + m * (189 ±0) + n * (3006 ±0)` + // Minimum execution time: 181_925_000 picoseconds. + Weight::from_parts(134_416_814, 1685) + // Standard Error: 15_678 + .saturating_add(Weight::from_parts(827_168, 0).saturating_mul(n.into())) + // Standard Error: 15_678 + .saturating_add(Weight::from_parts(813_655, 0).saturating_mul(m.into())) .saturating_add(T::DbWeight::get().reads((1_u64).saturating_mul(n.into()))) .saturating_add(T::DbWeight::get().reads((1_u64).saturating_mul(m.into()))) - .saturating_add(Weight::from_parts(0, 3195).saturating_mul(n.into())) .saturating_add(Weight::from_parts(0, 189).saturating_mul(m.into())) + .saturating_add(Weight::from_parts(0, 3006).saturating_mul(n.into())) } /// Storage: Pov Map1M (r:1 w:0) /// Proof: Pov Map1M (max_values: Some(1000000), max_size: Some(36), added: 2511, mode: MaxEncodedLen) @@ -218,11 +208,10 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `170` // Estimated: `3501` - // Minimum execution time: 22 nanoseconds. - Weight::from_parts(2_334_945, 0) - .saturating_add(Weight::from_parts(0, 3501)) - // Standard Error: 624 - .saturating_add(Weight::from_parts(282_046, 0).saturating_mul(n.into())) + // Minimum execution time: 20_000 picoseconds. + Weight::from_parts(2_006_399, 3501) + // Standard Error: 808 + .saturating_add(Weight::from_parts(263_609, 0).saturating_mul(n.into())) .saturating_add(T::DbWeight::get().reads(1_u64)) } /// Storage: Pov Map1M (r:100 w:0) @@ -232,11 +221,10 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `147 + n * (40 ±0)` // Estimated: `990 + n * (2511 ±0)` - // Minimum execution time: 20 nanoseconds. - Weight::from_parts(525_027, 0) - .saturating_add(Weight::from_parts(0, 990)) - // Standard Error: 2_767 - .saturating_add(Weight::from_parts(3_887_350, 0).saturating_mul(n.into())) + // Minimum execution time: 21_000 picoseconds. + Weight::from_parts(3_940_044, 990) + // Standard Error: 4_906 + .saturating_add(Weight::from_parts(3_454_882, 0).saturating_mul(n.into())) .saturating_add(T::DbWeight::get().reads((1_u64).saturating_mul(n.into()))) .saturating_add(Weight::from_parts(0, 2511).saturating_mul(n.into())) } @@ -247,11 +235,10 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `21938 + n * (57 ±0)` // Estimated: `990 + n * (2543 ±0)` - // Minimum execution time: 34 nanoseconds. - Weight::from_parts(18_341_393, 0) - .saturating_add(Weight::from_parts(0, 990)) - // Standard Error: 1_312 - .saturating_add(Weight::from_parts(2_053_135, 0).saturating_mul(n.into())) + // Minimum execution time: 28_000 picoseconds. + Weight::from_parts(20_674_869, 990) + // Standard Error: 3_035 + .saturating_add(Weight::from_parts(1_995_730, 0).saturating_mul(n.into())) .saturating_add(T::DbWeight::get().reads((1_u64).saturating_mul(n.into()))) .saturating_add(Weight::from_parts(0, 2543).saturating_mul(n.into())) } @@ -261,9 +248,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `109` // Estimated: `1518` - // Minimum execution time: 1_163 nanoseconds. - Weight::from_parts(1_274_000, 0) - .saturating_add(Weight::from_parts(0, 1518)) + // Minimum execution time: 1_091_000 picoseconds. + Weight::from_parts(1_181_000, 1518) .saturating_add(T::DbWeight::get().reads(1_u64)) } /// Storage: Pov UnboundedValue (r:1 w:0) @@ -272,9 +258,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `109` // Estimated: `1594` - // Minimum execution time: 1_167 nanoseconds. - Weight::from_parts(1_367_000, 0) - .saturating_add(Weight::from_parts(0, 1594)) + // Minimum execution time: 1_079_000 picoseconds. + Weight::from_parts(1_176_000, 1594) .saturating_add(T::DbWeight::get().reads(1_u64)) } /// Storage: Pov UnboundedValue (r:1 w:0) @@ -283,9 +268,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `109` // Estimated: `0` - // Minimum execution time: 1_155 nanoseconds. - Weight::from_parts(1_248_000, 0) - .saturating_add(Weight::from_parts(0, 0)) + // Minimum execution time: 1_101_000 picoseconds. + Weight::from_parts(1_160_000, 0) .saturating_add(T::DbWeight::get().reads(1_u64)) } /// Storage: Pov UnboundedValue (r:1 w:0) @@ -294,11 +278,10 @@ impl WeightInfo for SubstrateWeight { /// Proof: Pov BoundedValue (max_values: Some(1), max_size: Some(33), added: 528, mode: MaxEncodedLen) fn storage_value_bounded_and_unbounded_read() -> Weight { // Proof Size summary in bytes: - // Measured: `109` - // Estimated: `3112` - // Minimum execution time: 1_424 nanoseconds. - Weight::from_parts(1_601_000, 0) - .saturating_add(Weight::from_parts(0, 3112)) + // Measured: `147` + // Estimated: `1632` + // Minimum execution time: 2_143_000 picoseconds. + Weight::from_parts(2_280_000, 1632) .saturating_add(T::DbWeight::get().reads(2_u64)) } /// Storage: Pov LargeValue (r:1 w:0) @@ -306,13 +289,12 @@ impl WeightInfo for SubstrateWeight { /// The range of component `l` is `[0, 4194304]`. fn measured_storage_value_read_linear_size(l: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `174 + l * (1 ±0)` - // Estimated: `1656 + l * (1 ±0)` - // Minimum execution time: 1_744 nanoseconds. - Weight::from_parts(1_800_000, 0) - .saturating_add(Weight::from_parts(0, 1656)) - // Standard Error: 4 - .saturating_add(Weight::from_parts(443, 0).saturating_mul(l.into())) + // Measured: `142 + l * (1 ±0)` + // Estimated: `1626 + l * (1 ±0)` + // Minimum execution time: 1_665_000 picoseconds. + Weight::from_parts(1_725_000, 1626) + // Standard Error: 3 + .saturating_add(Weight::from_parts(376, 0).saturating_mul(l.into())) .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(Weight::from_parts(0, 1).saturating_mul(l.into())) } @@ -321,13 +303,12 @@ impl WeightInfo for SubstrateWeight { /// The range of component `l` is `[0, 4194304]`. fn mel_storage_value_read_linear_size(l: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `174 + l * (1 ±0)` + // Measured: `142 + l * (1 ±0)` // Estimated: `4195793` - // Minimum execution time: 1_770 nanoseconds. - Weight::from_parts(1_813_000, 0) - .saturating_add(Weight::from_parts(0, 4195793)) - // Standard Error: 6 - .saturating_add(Weight::from_parts(495, 0).saturating_mul(l.into())) + // Minimum execution time: 1_640_000 picoseconds. + Weight::from_parts(1_724_000, 4195793) + // Standard Error: 4 + .saturating_add(Weight::from_parts(395, 0).saturating_mul(l.into())) .saturating_add(T::DbWeight::get().reads(1_u64)) } /// Storage: Pov LargeValue (r:1 w:0) @@ -337,15 +318,14 @@ impl WeightInfo for SubstrateWeight { /// The range of component `l` is `[0, 4194304]`. fn measured_storage_double_value_read_linear_size(l: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `235 + l * (2 ±0)` - // Estimated: `3428 + l * (4 ±0)` - // Minimum execution time: 2_349 nanoseconds. - Weight::from_parts(2_423_000, 0) - .saturating_add(Weight::from_parts(0, 3428)) - // Standard Error: 11 - .saturating_add(Weight::from_parts(950, 0).saturating_mul(l.into())) + // Measured: `171 + l * (2 ±0)` + // Estimated: `1655 + l * (2 ±0)` + // Minimum execution time: 2_263_000 picoseconds. + Weight::from_parts(2_358_000, 1655) + // Standard Error: 8 + .saturating_add(Weight::from_parts(737, 0).saturating_mul(l.into())) .saturating_add(T::DbWeight::get().reads(2_u64)) - .saturating_add(Weight::from_parts(0, 4).saturating_mul(l.into())) + .saturating_add(Weight::from_parts(0, 2).saturating_mul(l.into())) } /// Storage: Pov LargeValue (r:1 w:0) /// Proof: Pov LargeValue (max_values: Some(1), max_size: Some(4194308), added: 4194803, mode: MaxEncodedLen) @@ -354,13 +334,12 @@ impl WeightInfo for SubstrateWeight { /// The range of component `l` is `[0, 4194304]`. fn mel_storage_double_value_read_linear_size(l: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `235 + l * (2 ±0)` - // Estimated: `8391586` - // Minimum execution time: 2_315 nanoseconds. - Weight::from_parts(2_409_000, 0) - .saturating_add(Weight::from_parts(0, 8391586)) - // Standard Error: 12 - .saturating_add(Weight::from_parts(984, 0).saturating_mul(l.into())) + // Measured: `171 + l * (2 ±0)` + // Estimated: `4195793` + // Minimum execution time: 2_161_000 picoseconds. + Weight::from_parts(2_233_000, 4195793) + // Standard Error: 5 + .saturating_add(Weight::from_parts(639, 0).saturating_mul(l.into())) .saturating_add(T::DbWeight::get().reads(2_u64)) } /// Storage: Pov LargeValue (r:1 w:0) @@ -370,13 +349,12 @@ impl WeightInfo for SubstrateWeight { /// The range of component `l` is `[0, 4194304]`. fn mel_mixed_storage_double_value_read_linear_size(l: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `235 + l * (2 ±0)` - // Estimated: `4197507 + l * (2 ±0)` - // Minimum execution time: 2_370 nanoseconds. - Weight::from_parts(2_474_000, 0) - .saturating_add(Weight::from_parts(0, 4197507)) - // Standard Error: 11 - .saturating_add(Weight::from_parts(956, 0).saturating_mul(l.into())) + // Measured: `171 + l * (2 ±0)` + // Estimated: `4195793 + l * (2 ±0)` + // Minimum execution time: 2_149_000 picoseconds. + Weight::from_parts(2_256_000, 4195793) + // Standard Error: 6 + .saturating_add(Weight::from_parts(677, 0).saturating_mul(l.into())) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(Weight::from_parts(0, 2).saturating_mul(l.into())) } @@ -387,13 +365,12 @@ impl WeightInfo for SubstrateWeight { /// The range of component `l` is `[0, 4194304]`. fn measured_mixed_storage_double_value_read_linear_size(l: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `235 + l * (2 ±0)` - // Estimated: `4197507 + l * (2 ±0)` - // Minimum execution time: 2_375 nanoseconds. - Weight::from_parts(2_420_000, 0) - .saturating_add(Weight::from_parts(0, 4197507)) - // Standard Error: 9 - .saturating_add(Weight::from_parts(914, 0).saturating_mul(l.into())) + // Measured: `171 + l * (2 ±0)` + // Estimated: `4195793 + l * (2 ±0)` + // Minimum execution time: 2_254_000 picoseconds. + Weight::from_parts(2_319_000, 4195793) + // Standard Error: 5 + .saturating_add(Weight::from_parts(664, 0).saturating_mul(l.into())) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(Weight::from_parts(0, 2).saturating_mul(l.into())) } @@ -404,15 +381,14 @@ impl WeightInfo for SubstrateWeight { /// The range of component `i` is `[0, 1000]`. fn storage_map_unbounded_both_measured_read(i: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `293 + i * (8 ±0)` - // Estimated: `7504 + i * (16 ±0)` - // Minimum execution time: 3_305 nanoseconds. - Weight::from_parts(3_689_335, 0) - .saturating_add(Weight::from_parts(0, 7504)) - // Standard Error: 29 - .saturating_add(Weight::from_parts(638, 0).saturating_mul(i.into())) + // Measured: `229 + i * (8 ±0)` + // Estimated: `3693 + i * (8 ±0)` + // Minimum execution time: 3_071_000 picoseconds. + Weight::from_parts(3_487_712, 3693) + // Standard Error: 26 + .saturating_add(Weight::from_parts(748, 0).saturating_mul(i.into())) .saturating_add(T::DbWeight::get().reads(2_u64)) - .saturating_add(Weight::from_parts(0, 16).saturating_mul(i.into())) + .saturating_add(Weight::from_parts(0, 8).saturating_mul(i.into())) } /// Storage: Pov Map1M (r:1 w:0) /// Proof: Pov Map1M (max_values: Some(1000000), max_size: Some(36), added: 2511, mode: MaxEncodedLen) @@ -421,13 +397,12 @@ impl WeightInfo for SubstrateWeight { /// The range of component `i` is `[0, 1000]`. fn storage_map_partial_unbounded_read(i: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `260 + i * (4 ±0)` - // Estimated: `7223 + i * (4 ±0)` - // Minimum execution time: 3_469 nanoseconds. - Weight::from_parts(3_878_896, 0) - .saturating_add(Weight::from_parts(0, 7223)) - // Standard Error: 33 - .saturating_add(Weight::from_parts(356, 0).saturating_mul(i.into())) + // Measured: `228 + i * (4 ±0)` + // Estimated: `3692 + i * (4 ±0)` + // Minimum execution time: 3_150_000 picoseconds. + Weight::from_parts(3_582_963, 3692) + // Standard Error: 18 + .saturating_add(Weight::from_parts(380, 0).saturating_mul(i.into())) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(Weight::from_parts(0, 4).saturating_mul(i.into())) } @@ -438,13 +413,12 @@ impl WeightInfo for SubstrateWeight { /// The range of component `i` is `[0, 1000]`. fn storage_map_partial_unbounded_ignored_read(i: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `260 + i * (4 ±0)` - // Estimated: `3758 + i * (4 ±0)` - // Minimum execution time: 3_442 nanoseconds. - Weight::from_parts(3_881_051, 0) - .saturating_add(Weight::from_parts(0, 3758)) - // Standard Error: 35 - .saturating_add(Weight::from_parts(384, 0).saturating_mul(i.into())) + // Measured: `228 + i * (4 ±0)` + // Estimated: `3501 + i * (4 ±0)` + // Minimum execution time: 3_092_000 picoseconds. + Weight::from_parts(3_595_328, 3501) + // Standard Error: 20 + .saturating_add(Weight::from_parts(243, 0).saturating_mul(i.into())) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(Weight::from_parts(0, 4).saturating_mul(i.into())) } @@ -452,17 +426,25 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_619 nanoseconds. - Weight::from_parts(1_728_000, 0) - .saturating_add(Weight::from_parts(0, 0)) + // Minimum execution time: 1_705_000 picoseconds. + Weight::from_parts(1_818_000, 0) } fn noop() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 546 nanoseconds. - Weight::from_parts(640_000, 0) - .saturating_add(Weight::from_parts(0, 0)) + // Minimum execution time: 533_000 picoseconds. + Weight::from_parts(587_000, 0) + } + /// Storage: Pov UnboundedMapTwox (r:65001 w:0) + /// Proof Skipped: Pov UnboundedMapTwox (max_values: None, max_size: None, mode: Measured) + fn storage_iteration() -> Weight { + // Proof Size summary in bytes: + // Measured: `17985289` + // Estimated: `178863754` + // Minimum execution time: 118_753_057_000 picoseconds. + Weight::from_parts(121_396_503_000, 178863754) + .saturating_add(T::DbWeight::get().reads(65001_u64)) } } @@ -474,9 +456,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `136` // Estimated: `1489` - // Minimum execution time: 1_968 nanoseconds. - Weight::from_parts(2_060_000, 0) - .saturating_add(Weight::from_parts(0, 1489)) + // Minimum execution time: 1_706_000 picoseconds. + Weight::from_parts(1_788_000, 1489) .saturating_add(RocksDbWeight::get().reads(1_u64)) } /// Storage: Pov Value (r:1 w:0) @@ -485,9 +466,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `136` // Estimated: `0` - // Minimum execution time: 1_934 nanoseconds. - Weight::from_parts(2_092_000, 0) - .saturating_add(Weight::from_parts(0, 0)) + // Minimum execution time: 1_661_000 picoseconds. + Weight::from_parts(1_718_000, 0) .saturating_add(RocksDbWeight::get().reads(1_u64)) } /// Storage: Pov Value (r:1 w:0) @@ -497,10 +477,9 @@ impl WeightInfo for () { fn storage_single_value_ignored_some_read() -> Weight { // Proof Size summary in bytes: // Measured: `160` - // Estimated: `1649` - // Minimum execution time: 2_605 nanoseconds. - Weight::from_parts(2_786_000, 0) - .saturating_add(Weight::from_parts(0, 1649)) + // Estimated: `1489` + // Minimum execution time: 2_226_000 picoseconds. + Weight::from_parts(2_365_000, 1489) .saturating_add(RocksDbWeight::get().reads(2_u64)) } /// Storage: Pov Value (r:1 w:0) @@ -509,9 +488,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `136` // Estimated: `1489` - // Minimum execution time: 2_019 nanoseconds. - Weight::from_parts(2_214_000, 0) - .saturating_add(Weight::from_parts(0, 1489)) + // Minimum execution time: 1_785_000 picoseconds. + Weight::from_parts(1_980_000, 1489) .saturating_add(RocksDbWeight::get().reads(1_u64)) } /// Storage: Pov Value (r:0 w:1) @@ -520,9 +498,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 279 nanoseconds. - Weight::from_parts(357_000, 0) - .saturating_add(Weight::from_parts(0, 0)) + // Minimum execution time: 254_000 picoseconds. + Weight::from_parts(326_000, 0) .saturating_add(RocksDbWeight::get().writes(1_u64)) } /// Storage: Pov Value (r:0 w:1) @@ -531,9 +508,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 291 nanoseconds. - Weight::from_parts(378_000, 0) - .saturating_add(Weight::from_parts(0, 0)) + // Minimum execution time: 239_000 picoseconds. + Weight::from_parts(277_000, 0) .saturating_add(RocksDbWeight::get().writes(1_u64)) } /// Storage: Pov Map1M (r:1 w:0) @@ -542,9 +518,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `1275` // Estimated: `4740` - // Minimum execution time: 5_077 nanoseconds. - Weight::from_parts(5_400_000, 0) - .saturating_add(Weight::from_parts(0, 4740)) + // Minimum execution time: 4_760_000 picoseconds. + Weight::from_parts(5_051_000, 4740) .saturating_add(RocksDbWeight::get().reads(1_u64)) } /// Storage: Pov Map1M (r:1 w:0) @@ -553,9 +528,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `1544` // Estimated: `5009` - // Minimum execution time: 5_878 nanoseconds. - Weight::from_parts(6_239_000, 0) - .saturating_add(Weight::from_parts(0, 5009)) + // Minimum execution time: 5_490_000 picoseconds. + Weight::from_parts(5_703_000, 5009) .saturating_add(RocksDbWeight::get().reads(1_u64)) } /// Storage: Pov Map1M (r:1 w:0) @@ -564,9 +538,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `2044` // Estimated: `5509` - // Minimum execution time: 7_282 nanoseconds. - Weight::from_parts(8_022_000, 0) - .saturating_add(Weight::from_parts(0, 5509)) + // Minimum execution time: 6_397_000 picoseconds. + Weight::from_parts(7_084_000, 5509) .saturating_add(RocksDbWeight::get().reads(1_u64)) } /// Storage: Pov Map1M (r:100 w:0) @@ -577,19 +550,18 @@ impl WeightInfo for () { /// The range of component `m` is `[0, 100]`. fn storage_map_read_per_component(n: u32, m: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `515 + n * (188 ±0) + m * (188 ±0)` - // Estimated: `1980 + n * (3006 ±0) + m * (2511 ±0)` - // Minimum execution time: 195_406 nanoseconds. - Weight::from_parts(129_093_464, 0) - .saturating_add(Weight::from_parts(0, 1980)) - // Standard Error: 12_134 - .saturating_add(Weight::from_parts(855_330, 0).saturating_mul(n.into())) - // Standard Error: 12_134 - .saturating_add(Weight::from_parts(870_523, 0).saturating_mul(m.into())) + // Measured: `515 + m * (188 ±0) + n * (188 ±0)` + // Estimated: `990 + m * (2511 ±0) + n * (3006 ±0)` + // Minimum execution time: 181_481_000 picoseconds. + Weight::from_parts(129_275_141, 990) + // Standard Error: 13_049 + .saturating_add(Weight::from_parts(787_667, 0).saturating_mul(n.into())) + // Standard Error: 13_049 + .saturating_add(Weight::from_parts(830_378, 0).saturating_mul(m.into())) .saturating_add(RocksDbWeight::get().reads((1_u64).saturating_mul(n.into()))) .saturating_add(RocksDbWeight::get().reads((1_u64).saturating_mul(m.into()))) - .saturating_add(Weight::from_parts(0, 3006).saturating_mul(n.into())) .saturating_add(Weight::from_parts(0, 2511).saturating_mul(m.into())) + .saturating_add(Weight::from_parts(0, 3006).saturating_mul(n.into())) } /// Storage: Pov Map1M (r:100 w:0) /// Proof: Pov Map1M (max_values: Some(1000000), max_size: Some(36), added: 2511, mode: Ignored) @@ -599,19 +571,18 @@ impl WeightInfo for () { /// The range of component `m` is `[0, 100]`. fn storage_map_read_per_component_one_ignored(n: u32, m: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `515 + n * (188 ±0) + m * (188 ±0)` - // Estimated: `1685 + n * (3195 ±0) + m * (189 ±0)` - // Minimum execution time: 195_053 nanoseconds. - Weight::from_parts(131_322_479, 0) - .saturating_add(Weight::from_parts(0, 1685)) - // Standard Error: 12_161 - .saturating_add(Weight::from_parts(843_047, 0).saturating_mul(n.into())) - // Standard Error: 12_161 - .saturating_add(Weight::from_parts(858_668, 0).saturating_mul(m.into())) + // Measured: `515 + m * (188 ±0) + n * (188 ±0)` + // Estimated: `1685 + m * (189 ±0) + n * (3006 ±0)` + // Minimum execution time: 181_925_000 picoseconds. + Weight::from_parts(134_416_814, 1685) + // Standard Error: 15_678 + .saturating_add(Weight::from_parts(827_168, 0).saturating_mul(n.into())) + // Standard Error: 15_678 + .saturating_add(Weight::from_parts(813_655, 0).saturating_mul(m.into())) .saturating_add(RocksDbWeight::get().reads((1_u64).saturating_mul(n.into()))) .saturating_add(RocksDbWeight::get().reads((1_u64).saturating_mul(m.into()))) - .saturating_add(Weight::from_parts(0, 3195).saturating_mul(n.into())) .saturating_add(Weight::from_parts(0, 189).saturating_mul(m.into())) + .saturating_add(Weight::from_parts(0, 3006).saturating_mul(n.into())) } /// Storage: Pov Map1M (r:1 w:0) /// Proof: Pov Map1M (max_values: Some(1000000), max_size: Some(36), added: 2511, mode: MaxEncodedLen) @@ -620,11 +591,10 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `170` // Estimated: `3501` - // Minimum execution time: 22 nanoseconds. - Weight::from_parts(2_334_945, 0) - .saturating_add(Weight::from_parts(0, 3501)) - // Standard Error: 624 - .saturating_add(Weight::from_parts(282_046, 0).saturating_mul(n.into())) + // Minimum execution time: 20_000 picoseconds. + Weight::from_parts(2_006_399, 3501) + // Standard Error: 808 + .saturating_add(Weight::from_parts(263_609, 0).saturating_mul(n.into())) .saturating_add(RocksDbWeight::get().reads(1_u64)) } /// Storage: Pov Map1M (r:100 w:0) @@ -634,11 +604,10 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `147 + n * (40 ±0)` // Estimated: `990 + n * (2511 ±0)` - // Minimum execution time: 20 nanoseconds. - Weight::from_parts(525_027, 0) - .saturating_add(Weight::from_parts(0, 990)) - // Standard Error: 2_767 - .saturating_add(Weight::from_parts(3_887_350, 0).saturating_mul(n.into())) + // Minimum execution time: 21_000 picoseconds. + Weight::from_parts(3_940_044, 990) + // Standard Error: 4_906 + .saturating_add(Weight::from_parts(3_454_882, 0).saturating_mul(n.into())) .saturating_add(RocksDbWeight::get().reads((1_u64).saturating_mul(n.into()))) .saturating_add(Weight::from_parts(0, 2511).saturating_mul(n.into())) } @@ -649,11 +618,10 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `21938 + n * (57 ±0)` // Estimated: `990 + n * (2543 ±0)` - // Minimum execution time: 34 nanoseconds. - Weight::from_parts(18_341_393, 0) - .saturating_add(Weight::from_parts(0, 990)) - // Standard Error: 1_312 - .saturating_add(Weight::from_parts(2_053_135, 0).saturating_mul(n.into())) + // Minimum execution time: 28_000 picoseconds. + Weight::from_parts(20_674_869, 990) + // Standard Error: 3_035 + .saturating_add(Weight::from_parts(1_995_730, 0).saturating_mul(n.into())) .saturating_add(RocksDbWeight::get().reads((1_u64).saturating_mul(n.into()))) .saturating_add(Weight::from_parts(0, 2543).saturating_mul(n.into())) } @@ -663,9 +631,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `109` // Estimated: `1518` - // Minimum execution time: 1_163 nanoseconds. - Weight::from_parts(1_274_000, 0) - .saturating_add(Weight::from_parts(0, 1518)) + // Minimum execution time: 1_091_000 picoseconds. + Weight::from_parts(1_181_000, 1518) .saturating_add(RocksDbWeight::get().reads(1_u64)) } /// Storage: Pov UnboundedValue (r:1 w:0) @@ -674,9 +641,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `109` // Estimated: `1594` - // Minimum execution time: 1_167 nanoseconds. - Weight::from_parts(1_367_000, 0) - .saturating_add(Weight::from_parts(0, 1594)) + // Minimum execution time: 1_079_000 picoseconds. + Weight::from_parts(1_176_000, 1594) .saturating_add(RocksDbWeight::get().reads(1_u64)) } /// Storage: Pov UnboundedValue (r:1 w:0) @@ -685,9 +651,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `109` // Estimated: `0` - // Minimum execution time: 1_155 nanoseconds. - Weight::from_parts(1_248_000, 0) - .saturating_add(Weight::from_parts(0, 0)) + // Minimum execution time: 1_101_000 picoseconds. + Weight::from_parts(1_160_000, 0) .saturating_add(RocksDbWeight::get().reads(1_u64)) } /// Storage: Pov UnboundedValue (r:1 w:0) @@ -696,11 +661,10 @@ impl WeightInfo for () { /// Proof: Pov BoundedValue (max_values: Some(1), max_size: Some(33), added: 528, mode: MaxEncodedLen) fn storage_value_bounded_and_unbounded_read() -> Weight { // Proof Size summary in bytes: - // Measured: `109` - // Estimated: `3112` - // Minimum execution time: 1_424 nanoseconds. - Weight::from_parts(1_601_000, 0) - .saturating_add(Weight::from_parts(0, 3112)) + // Measured: `147` + // Estimated: `1632` + // Minimum execution time: 2_143_000 picoseconds. + Weight::from_parts(2_280_000, 1632) .saturating_add(RocksDbWeight::get().reads(2_u64)) } /// Storage: Pov LargeValue (r:1 w:0) @@ -708,13 +672,12 @@ impl WeightInfo for () { /// The range of component `l` is `[0, 4194304]`. fn measured_storage_value_read_linear_size(l: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `174 + l * (1 ±0)` - // Estimated: `1656 + l * (1 ±0)` - // Minimum execution time: 1_744 nanoseconds. - Weight::from_parts(1_800_000, 0) - .saturating_add(Weight::from_parts(0, 1656)) - // Standard Error: 4 - .saturating_add(Weight::from_parts(443, 0).saturating_mul(l.into())) + // Measured: `142 + l * (1 ±0)` + // Estimated: `1626 + l * (1 ±0)` + // Minimum execution time: 1_665_000 picoseconds. + Weight::from_parts(1_725_000, 1626) + // Standard Error: 3 + .saturating_add(Weight::from_parts(376, 0).saturating_mul(l.into())) .saturating_add(RocksDbWeight::get().reads(1_u64)) .saturating_add(Weight::from_parts(0, 1).saturating_mul(l.into())) } @@ -723,13 +686,12 @@ impl WeightInfo for () { /// The range of component `l` is `[0, 4194304]`. fn mel_storage_value_read_linear_size(l: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `174 + l * (1 ±0)` + // Measured: `142 + l * (1 ±0)` // Estimated: `4195793` - // Minimum execution time: 1_770 nanoseconds. - Weight::from_parts(1_813_000, 0) - .saturating_add(Weight::from_parts(0, 4195793)) - // Standard Error: 6 - .saturating_add(Weight::from_parts(495, 0).saturating_mul(l.into())) + // Minimum execution time: 1_640_000 picoseconds. + Weight::from_parts(1_724_000, 4195793) + // Standard Error: 4 + .saturating_add(Weight::from_parts(395, 0).saturating_mul(l.into())) .saturating_add(RocksDbWeight::get().reads(1_u64)) } /// Storage: Pov LargeValue (r:1 w:0) @@ -739,15 +701,14 @@ impl WeightInfo for () { /// The range of component `l` is `[0, 4194304]`. fn measured_storage_double_value_read_linear_size(l: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `235 + l * (2 ±0)` - // Estimated: `3428 + l * (4 ±0)` - // Minimum execution time: 2_349 nanoseconds. - Weight::from_parts(2_423_000, 0) - .saturating_add(Weight::from_parts(0, 3428)) - // Standard Error: 11 - .saturating_add(Weight::from_parts(950, 0).saturating_mul(l.into())) + // Measured: `171 + l * (2 ±0)` + // Estimated: `1655 + l * (2 ±0)` + // Minimum execution time: 2_263_000 picoseconds. + Weight::from_parts(2_358_000, 1655) + // Standard Error: 8 + .saturating_add(Weight::from_parts(737, 0).saturating_mul(l.into())) .saturating_add(RocksDbWeight::get().reads(2_u64)) - .saturating_add(Weight::from_parts(0, 4).saturating_mul(l.into())) + .saturating_add(Weight::from_parts(0, 2).saturating_mul(l.into())) } /// Storage: Pov LargeValue (r:1 w:0) /// Proof: Pov LargeValue (max_values: Some(1), max_size: Some(4194308), added: 4194803, mode: MaxEncodedLen) @@ -756,13 +717,12 @@ impl WeightInfo for () { /// The range of component `l` is `[0, 4194304]`. fn mel_storage_double_value_read_linear_size(l: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `235 + l * (2 ±0)` - // Estimated: `8391586` - // Minimum execution time: 2_315 nanoseconds. - Weight::from_parts(2_409_000, 0) - .saturating_add(Weight::from_parts(0, 8391586)) - // Standard Error: 12 - .saturating_add(Weight::from_parts(984, 0).saturating_mul(l.into())) + // Measured: `171 + l * (2 ±0)` + // Estimated: `4195793` + // Minimum execution time: 2_161_000 picoseconds. + Weight::from_parts(2_233_000, 4195793) + // Standard Error: 5 + .saturating_add(Weight::from_parts(639, 0).saturating_mul(l.into())) .saturating_add(RocksDbWeight::get().reads(2_u64)) } /// Storage: Pov LargeValue (r:1 w:0) @@ -772,13 +732,12 @@ impl WeightInfo for () { /// The range of component `l` is `[0, 4194304]`. fn mel_mixed_storage_double_value_read_linear_size(l: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `235 + l * (2 ±0)` - // Estimated: `4197507 + l * (2 ±0)` - // Minimum execution time: 2_370 nanoseconds. - Weight::from_parts(2_474_000, 0) - .saturating_add(Weight::from_parts(0, 4197507)) - // Standard Error: 11 - .saturating_add(Weight::from_parts(956, 0).saturating_mul(l.into())) + // Measured: `171 + l * (2 ±0)` + // Estimated: `4195793 + l * (2 ±0)` + // Minimum execution time: 2_149_000 picoseconds. + Weight::from_parts(2_256_000, 4195793) + // Standard Error: 6 + .saturating_add(Weight::from_parts(677, 0).saturating_mul(l.into())) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(Weight::from_parts(0, 2).saturating_mul(l.into())) } @@ -789,13 +748,12 @@ impl WeightInfo for () { /// The range of component `l` is `[0, 4194304]`. fn measured_mixed_storage_double_value_read_linear_size(l: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `235 + l * (2 ±0)` - // Estimated: `4197507 + l * (2 ±0)` - // Minimum execution time: 2_375 nanoseconds. - Weight::from_parts(2_420_000, 0) - .saturating_add(Weight::from_parts(0, 4197507)) - // Standard Error: 9 - .saturating_add(Weight::from_parts(914, 0).saturating_mul(l.into())) + // Measured: `171 + l * (2 ±0)` + // Estimated: `4195793 + l * (2 ±0)` + // Minimum execution time: 2_254_000 picoseconds. + Weight::from_parts(2_319_000, 4195793) + // Standard Error: 5 + .saturating_add(Weight::from_parts(664, 0).saturating_mul(l.into())) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(Weight::from_parts(0, 2).saturating_mul(l.into())) } @@ -806,15 +764,14 @@ impl WeightInfo for () { /// The range of component `i` is `[0, 1000]`. fn storage_map_unbounded_both_measured_read(i: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `293 + i * (8 ±0)` - // Estimated: `7504 + i * (16 ±0)` - // Minimum execution time: 3_305 nanoseconds. - Weight::from_parts(3_689_335, 0) - .saturating_add(Weight::from_parts(0, 7504)) - // Standard Error: 29 - .saturating_add(Weight::from_parts(638, 0).saturating_mul(i.into())) + // Measured: `229 + i * (8 ±0)` + // Estimated: `3693 + i * (8 ±0)` + // Minimum execution time: 3_071_000 picoseconds. + Weight::from_parts(3_487_712, 3693) + // Standard Error: 26 + .saturating_add(Weight::from_parts(748, 0).saturating_mul(i.into())) .saturating_add(RocksDbWeight::get().reads(2_u64)) - .saturating_add(Weight::from_parts(0, 16).saturating_mul(i.into())) + .saturating_add(Weight::from_parts(0, 8).saturating_mul(i.into())) } /// Storage: Pov Map1M (r:1 w:0) /// Proof: Pov Map1M (max_values: Some(1000000), max_size: Some(36), added: 2511, mode: MaxEncodedLen) @@ -823,13 +780,12 @@ impl WeightInfo for () { /// The range of component `i` is `[0, 1000]`. fn storage_map_partial_unbounded_read(i: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `260 + i * (4 ±0)` - // Estimated: `7223 + i * (4 ±0)` - // Minimum execution time: 3_469 nanoseconds. - Weight::from_parts(3_878_896, 0) - .saturating_add(Weight::from_parts(0, 7223)) - // Standard Error: 33 - .saturating_add(Weight::from_parts(356, 0).saturating_mul(i.into())) + // Measured: `228 + i * (4 ±0)` + // Estimated: `3692 + i * (4 ±0)` + // Minimum execution time: 3_150_000 picoseconds. + Weight::from_parts(3_582_963, 3692) + // Standard Error: 18 + .saturating_add(Weight::from_parts(380, 0).saturating_mul(i.into())) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(Weight::from_parts(0, 4).saturating_mul(i.into())) } @@ -840,13 +796,12 @@ impl WeightInfo for () { /// The range of component `i` is `[0, 1000]`. fn storage_map_partial_unbounded_ignored_read(i: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `260 + i * (4 ±0)` - // Estimated: `3758 + i * (4 ±0)` - // Minimum execution time: 3_442 nanoseconds. - Weight::from_parts(3_881_051, 0) - .saturating_add(Weight::from_parts(0, 3758)) - // Standard Error: 35 - .saturating_add(Weight::from_parts(384, 0).saturating_mul(i.into())) + // Measured: `228 + i * (4 ±0)` + // Estimated: `3501 + i * (4 ±0)` + // Minimum execution time: 3_092_000 picoseconds. + Weight::from_parts(3_595_328, 3501) + // Standard Error: 20 + .saturating_add(Weight::from_parts(243, 0).saturating_mul(i.into())) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(Weight::from_parts(0, 4).saturating_mul(i.into())) } @@ -854,16 +809,24 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_619 nanoseconds. - Weight::from_parts(1_728_000, 0) - .saturating_add(Weight::from_parts(0, 0)) + // Minimum execution time: 1_705_000 picoseconds. + Weight::from_parts(1_818_000, 0) } fn noop() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 546 nanoseconds. - Weight::from_parts(640_000, 0) - .saturating_add(Weight::from_parts(0, 0)) + // Minimum execution time: 533_000 picoseconds. + Weight::from_parts(587_000, 0) + } + /// Storage: Pov UnboundedMapTwox (r:65001 w:0) + /// Proof Skipped: Pov UnboundedMapTwox (max_values: None, max_size: None, mode: Measured) + fn storage_iteration() -> Weight { + // Proof Size summary in bytes: + // Measured: `17985289` + // Estimated: `178863754` + // Minimum execution time: 118_753_057_000 picoseconds. + Weight::from_parts(121_396_503_000, 178863754) + .saturating_add(RocksDbWeight::get().reads(65001_u64)) } } diff --git a/frame/benchmarking/src/weights.rs b/frame/benchmarking/src/weights.rs index aa24b99ac..25e2702f7 100644 --- a/frame/benchmarking/src/weights.rs +++ b/frame/benchmarking/src/weights.rs @@ -18,9 +18,9 @@ //! Autogenerated weights for frame_benchmarking //! //! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev -//! DATE: 2023-03-15, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! DATE: 2023-04-06, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` //! WORST CASE MAP SIZE: `1000000` -//! HOSTNAME: `bm3`, CPU: `Intel(R) Core(TM) i7-7700K CPU @ 4.20GHz` +//! HOSTNAME: `bm2`, CPU: `Intel(R) Core(TM) i7-7700K CPU @ 4.20GHz` //! EXECUTION: Some(Wasm), WASM-EXECUTION: Compiled, CHAIN: Some("dev"), DB CACHE: 1024 // Executed Command: @@ -31,9 +31,6 @@ // --steps=50 // --repeat=20 // --pallet=frame_benchmarking -// --no-storage-info -// --no-median-slopes -// --no-min-squares // --extrinsic=* // --execution=wasm // --wasm-execution=compiled @@ -67,49 +64,49 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 205_000 picoseconds. - Weight::from_parts(263_483, 0) + // Minimum execution time: 173_000 picoseconds. + Weight::from_parts(205_895, 0) } /// The range of component `i` is `[0, 1000000]`. fn subtraction(_i: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 186_000 picoseconds. - Weight::from_parts(257_588, 0) + // Minimum execution time: 180_000 picoseconds. + Weight::from_parts(206_967, 0) } /// The range of component `i` is `[0, 1000000]`. fn multiplication(_i: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 192_000 picoseconds. - Weight::from_parts(264_271, 0) + // Minimum execution time: 174_000 picoseconds. + Weight::from_parts(214_304, 0) } /// The range of component `i` is `[0, 1000000]`. fn division(_i: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 191_000 picoseconds. - Weight::from_parts(250_785, 0) + // Minimum execution time: 173_000 picoseconds. + Weight::from_parts(207_804, 0) } fn hashing() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 21_405_207_000 picoseconds. - Weight::from_parts(21_534_243_000, 0) + // Minimum execution time: 21_173_551_000 picoseconds. + Weight::from_parts(21_256_886_000, 0) } /// The range of component `i` is `[0, 100]`. fn sr25519_verification(i: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 255_000 picoseconds. - Weight::from_parts(271_000, 0) - // Standard Error: 12_788 - .saturating_add(Weight::from_parts(47_469_387, 0).saturating_mul(i.into())) + // Minimum execution time: 208_000 picoseconds. + Weight::from_parts(1_227_077, 0) + // Standard Error: 9_390 + .saturating_add(Weight::from_parts(47_152_841, 0).saturating_mul(i.into())) } } @@ -120,48 +117,48 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 205_000 picoseconds. - Weight::from_parts(263_483, 0) + // Minimum execution time: 173_000 picoseconds. + Weight::from_parts(205_895, 0) } /// The range of component `i` is `[0, 1000000]`. fn subtraction(_i: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 186_000 picoseconds. - Weight::from_parts(257_588, 0) + // Minimum execution time: 180_000 picoseconds. + Weight::from_parts(206_967, 0) } /// The range of component `i` is `[0, 1000000]`. fn multiplication(_i: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 192_000 picoseconds. - Weight::from_parts(264_271, 0) + // Minimum execution time: 174_000 picoseconds. + Weight::from_parts(214_304, 0) } /// The range of component `i` is `[0, 1000000]`. fn division(_i: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 191_000 picoseconds. - Weight::from_parts(250_785, 0) + // Minimum execution time: 173_000 picoseconds. + Weight::from_parts(207_804, 0) } fn hashing() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 21_405_207_000 picoseconds. - Weight::from_parts(21_534_243_000, 0) + // Minimum execution time: 21_173_551_000 picoseconds. + Weight::from_parts(21_256_886_000, 0) } /// The range of component `i` is `[0, 100]`. fn sr25519_verification(i: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 255_000 picoseconds. - Weight::from_parts(271_000, 0) - // Standard Error: 12_788 - .saturating_add(Weight::from_parts(47_469_387, 0).saturating_mul(i.into())) + // Minimum execution time: 208_000 picoseconds. + Weight::from_parts(1_227_077, 0) + // Standard Error: 9_390 + .saturating_add(Weight::from_parts(47_152_841, 0).saturating_mul(i.into())) } } diff --git a/frame/bounties/src/weights.rs b/frame/bounties/src/weights.rs index b0459fee6..5a84adf08 100644 --- a/frame/bounties/src/weights.rs +++ b/frame/bounties/src/weights.rs @@ -18,9 +18,9 @@ //! Autogenerated weights for pallet_bounties //! //! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev -//! DATE: 2023-03-15, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! DATE: 2023-04-06, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` //! WORST CASE MAP SIZE: `1000000` -//! HOSTNAME: `bm3`, CPU: `Intel(R) Core(TM) i7-7700K CPU @ 4.20GHz` +//! HOSTNAME: `bm2`, CPU: `Intel(R) Core(TM) i7-7700K CPU @ 4.20GHz` //! EXECUTION: Some(Wasm), WASM-EXECUTION: Compiled, CHAIN: Some("dev"), DB CACHE: 1024 // Executed Command: @@ -31,9 +31,6 @@ // --steps=50 // --repeat=20 // --pallet=pallet_bounties -// --no-storage-info -// --no-median-slopes -// --no-min-squares // --extrinsic=* // --execution=wasm // --wasm-execution=compiled @@ -76,12 +73,14 @@ impl WeightInfo for SubstrateWeight { /// Storage: Bounties Bounties (r:0 w:1) /// Proof: Bounties Bounties (max_values: None, max_size: Some(177), added: 2652, mode: MaxEncodedLen) /// The range of component `d` is `[0, 300]`. - fn propose_bounty(_d: u32, ) -> Weight { + fn propose_bounty(d: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `276` - // Estimated: `5082` - // Minimum execution time: 26_271_000 picoseconds. - Weight::from_parts(28_048_901, 5082) + // Estimated: `3593` + // Minimum execution time: 30_793_000 picoseconds. + Weight::from_parts(31_509_544, 3593) + // Standard Error: 168 + .saturating_add(Weight::from_parts(2_219, 0).saturating_mul(d.into())) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().writes(4_u64)) } @@ -92,9 +91,9 @@ impl WeightInfo for SubstrateWeight { fn approve_bounty() -> Weight { // Proof Size summary in bytes: // Measured: `368` - // Estimated: `5529` - // Minimum execution time: 12_573_000 picoseconds. - Weight::from_parts(12_827_000, 5529) + // Estimated: `3642` + // Minimum execution time: 12_471_000 picoseconds. + Weight::from_parts(12_677_000, 3642) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().writes(2_u64)) } @@ -104,8 +103,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `388` // Estimated: `3642` - // Minimum execution time: 10_555_000 picoseconds. - Weight::from_parts(10_857_000, 3642) + // Minimum execution time: 10_560_000 picoseconds. + Weight::from_parts(10_744_000, 3642) .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -116,9 +115,9 @@ impl WeightInfo for SubstrateWeight { fn unassign_curator() -> Weight { // Proof Size summary in bytes: // Measured: `564` - // Estimated: `7235` - // Minimum execution time: 26_003_000 picoseconds. - Weight::from_parts(26_291_000, 7235) + // Estimated: `3642` + // Minimum execution time: 30_980_000 picoseconds. + Weight::from_parts(31_354_000, 3642) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().writes(2_u64)) } @@ -129,9 +128,9 @@ impl WeightInfo for SubstrateWeight { fn accept_curator() -> Weight { // Proof Size summary in bytes: // Measured: `560` - // Estimated: `7235` - // Minimum execution time: 24_371_000 picoseconds. - Weight::from_parts(24_781_000, 7235) + // Estimated: `3642` + // Minimum execution time: 29_257_000 picoseconds. + Weight::from_parts(29_656_000, 3642) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().writes(2_u64)) } @@ -142,9 +141,9 @@ impl WeightInfo for SubstrateWeight { fn award_bounty() -> Weight { // Proof Size summary in bytes: // Measured: `538` - // Estimated: `7123` - // Minimum execution time: 20_688_000 picoseconds. - Weight::from_parts(20_909_000, 7123) + // Estimated: `3642` + // Minimum execution time: 20_662_000 picoseconds. + Weight::from_parts(20_956_000, 3642) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -159,9 +158,9 @@ impl WeightInfo for SubstrateWeight { fn claim_bounty() -> Weight { // Proof Size summary in bytes: // Measured: `902` - // Estimated: `15934` - // Minimum execution time: 78_933_000 picoseconds. - Weight::from_parts(79_884_000, 15934) + // Estimated: `8799` + // Minimum execution time: 119_287_000 picoseconds. + Weight::from_parts(121_468_000, 8799) .saturating_add(T::DbWeight::get().reads(5_u64)) .saturating_add(T::DbWeight::get().writes(6_u64)) } @@ -176,9 +175,9 @@ impl WeightInfo for SubstrateWeight { fn close_bounty_proposed() -> Weight { // Proof Size summary in bytes: // Measured: `582` - // Estimated: `10716` - // Minimum execution time: 32_332_000 picoseconds. - Weight::from_parts(32_833_000, 10716) + // Estimated: `3642` + // Minimum execution time: 37_759_000 picoseconds. + Weight::from_parts(38_185_000, 3642) .saturating_add(T::DbWeight::get().reads(3_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) } @@ -193,9 +192,9 @@ impl WeightInfo for SubstrateWeight { fn close_bounty_active() -> Weight { // Proof Size summary in bytes: // Measured: `818` - // Estimated: `13319` - // Minimum execution time: 55_686_000 picoseconds. - Weight::from_parts(56_271_000, 13319) + // Estimated: `6196` + // Minimum execution time: 80_332_000 picoseconds. + Weight::from_parts(81_328_000, 6196) .saturating_add(T::DbWeight::get().reads(4_u64)) .saturating_add(T::DbWeight::get().writes(4_u64)) } @@ -205,8 +204,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `424` // Estimated: `3642` - // Minimum execution time: 16_414_000 picoseconds. - Weight::from_parts(16_681_000, 3642) + // Minimum execution time: 16_301_000 picoseconds. + Weight::from_parts(16_611_000, 3642) .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -220,16 +219,16 @@ impl WeightInfo for SubstrateWeight { fn spend_funds(b: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `4 + b * (297 ±0)` - // Estimated: `3867 + b * (7858 ±0)` - // Minimum execution time: 5_398_000 picoseconds. - Weight::from_parts(4_478_947, 3867) - // Standard Error: 41_860 - .saturating_add(Weight::from_parts(33_082_606, 0).saturating_mul(b.into())) + // Estimated: `1887 + b * (5206 ±0)` + // Minimum execution time: 5_430_000 picoseconds. + Weight::from_parts(4_463_976, 1887) + // Standard Error: 43_695 + .saturating_add(Weight::from_parts(39_370_113, 0).saturating_mul(b.into())) .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(T::DbWeight::get().reads((3_u64).saturating_mul(b.into()))) .saturating_add(T::DbWeight::get().writes(1_u64)) .saturating_add(T::DbWeight::get().writes((3_u64).saturating_mul(b.into()))) - .saturating_add(Weight::from_parts(0, 7858).saturating_mul(b.into())) + .saturating_add(Weight::from_parts(0, 5206).saturating_mul(b.into())) } } @@ -244,12 +243,14 @@ impl WeightInfo for () { /// Storage: Bounties Bounties (r:0 w:1) /// Proof: Bounties Bounties (max_values: None, max_size: Some(177), added: 2652, mode: MaxEncodedLen) /// The range of component `d` is `[0, 300]`. - fn propose_bounty(_d: u32, ) -> Weight { + fn propose_bounty(d: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `276` - // Estimated: `5082` - // Minimum execution time: 26_271_000 picoseconds. - Weight::from_parts(28_048_901, 5082) + // Estimated: `3593` + // Minimum execution time: 30_793_000 picoseconds. + Weight::from_parts(31_509_544, 3593) + // Standard Error: 168 + .saturating_add(Weight::from_parts(2_219, 0).saturating_mul(d.into())) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().writes(4_u64)) } @@ -260,9 +261,9 @@ impl WeightInfo for () { fn approve_bounty() -> Weight { // Proof Size summary in bytes: // Measured: `368` - // Estimated: `5529` - // Minimum execution time: 12_573_000 picoseconds. - Weight::from_parts(12_827_000, 5529) + // Estimated: `3642` + // Minimum execution time: 12_471_000 picoseconds. + Weight::from_parts(12_677_000, 3642) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().writes(2_u64)) } @@ -272,8 +273,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `388` // Estimated: `3642` - // Minimum execution time: 10_555_000 picoseconds. - Weight::from_parts(10_857_000, 3642) + // Minimum execution time: 10_560_000 picoseconds. + Weight::from_parts(10_744_000, 3642) .saturating_add(RocksDbWeight::get().reads(1_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } @@ -284,9 +285,9 @@ impl WeightInfo for () { fn unassign_curator() -> Weight { // Proof Size summary in bytes: // Measured: `564` - // Estimated: `7235` - // Minimum execution time: 26_003_000 picoseconds. - Weight::from_parts(26_291_000, 7235) + // Estimated: `3642` + // Minimum execution time: 30_980_000 picoseconds. + Weight::from_parts(31_354_000, 3642) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().writes(2_u64)) } @@ -297,9 +298,9 @@ impl WeightInfo for () { fn accept_curator() -> Weight { // Proof Size summary in bytes: // Measured: `560` - // Estimated: `7235` - // Minimum execution time: 24_371_000 picoseconds. - Weight::from_parts(24_781_000, 7235) + // Estimated: `3642` + // Minimum execution time: 29_257_000 picoseconds. + Weight::from_parts(29_656_000, 3642) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().writes(2_u64)) } @@ -310,9 +311,9 @@ impl WeightInfo for () { fn award_bounty() -> Weight { // Proof Size summary in bytes: // Measured: `538` - // Estimated: `7123` - // Minimum execution time: 20_688_000 picoseconds. - Weight::from_parts(20_909_000, 7123) + // Estimated: `3642` + // Minimum execution time: 20_662_000 picoseconds. + Weight::from_parts(20_956_000, 3642) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } @@ -327,9 +328,9 @@ impl WeightInfo for () { fn claim_bounty() -> Weight { // Proof Size summary in bytes: // Measured: `902` - // Estimated: `15934` - // Minimum execution time: 78_933_000 picoseconds. - Weight::from_parts(79_884_000, 15934) + // Estimated: `8799` + // Minimum execution time: 119_287_000 picoseconds. + Weight::from_parts(121_468_000, 8799) .saturating_add(RocksDbWeight::get().reads(5_u64)) .saturating_add(RocksDbWeight::get().writes(6_u64)) } @@ -344,9 +345,9 @@ impl WeightInfo for () { fn close_bounty_proposed() -> Weight { // Proof Size summary in bytes: // Measured: `582` - // Estimated: `10716` - // Minimum execution time: 32_332_000 picoseconds. - Weight::from_parts(32_833_000, 10716) + // Estimated: `3642` + // Minimum execution time: 37_759_000 picoseconds. + Weight::from_parts(38_185_000, 3642) .saturating_add(RocksDbWeight::get().reads(3_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) } @@ -361,9 +362,9 @@ impl WeightInfo for () { fn close_bounty_active() -> Weight { // Proof Size summary in bytes: // Measured: `818` - // Estimated: `13319` - // Minimum execution time: 55_686_000 picoseconds. - Weight::from_parts(56_271_000, 13319) + // Estimated: `6196` + // Minimum execution time: 80_332_000 picoseconds. + Weight::from_parts(81_328_000, 6196) .saturating_add(RocksDbWeight::get().reads(4_u64)) .saturating_add(RocksDbWeight::get().writes(4_u64)) } @@ -373,8 +374,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `424` // Estimated: `3642` - // Minimum execution time: 16_414_000 picoseconds. - Weight::from_parts(16_681_000, 3642) + // Minimum execution time: 16_301_000 picoseconds. + Weight::from_parts(16_611_000, 3642) .saturating_add(RocksDbWeight::get().reads(1_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } @@ -388,15 +389,15 @@ impl WeightInfo for () { fn spend_funds(b: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `4 + b * (297 ±0)` - // Estimated: `3867 + b * (7858 ±0)` - // Minimum execution time: 5_398_000 picoseconds. - Weight::from_parts(4_478_947, 3867) - // Standard Error: 41_860 - .saturating_add(Weight::from_parts(33_082_606, 0).saturating_mul(b.into())) + // Estimated: `1887 + b * (5206 ±0)` + // Minimum execution time: 5_430_000 picoseconds. + Weight::from_parts(4_463_976, 1887) + // Standard Error: 43_695 + .saturating_add(Weight::from_parts(39_370_113, 0).saturating_mul(b.into())) .saturating_add(RocksDbWeight::get().reads(1_u64)) .saturating_add(RocksDbWeight::get().reads((3_u64).saturating_mul(b.into()))) .saturating_add(RocksDbWeight::get().writes(1_u64)) .saturating_add(RocksDbWeight::get().writes((3_u64).saturating_mul(b.into()))) - .saturating_add(Weight::from_parts(0, 7858).saturating_mul(b.into())) + .saturating_add(Weight::from_parts(0, 5206).saturating_mul(b.into())) } } diff --git a/frame/child-bounties/src/weights.rs b/frame/child-bounties/src/weights.rs index 92be77c49..be30e80a1 100644 --- a/frame/child-bounties/src/weights.rs +++ b/frame/child-bounties/src/weights.rs @@ -18,9 +18,9 @@ //! Autogenerated weights for pallet_child_bounties //! //! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev -//! DATE: 2023-03-15, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! DATE: 2023-04-06, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` //! WORST CASE MAP SIZE: `1000000` -//! HOSTNAME: `bm3`, CPU: `Intel(R) Core(TM) i7-7700K CPU @ 4.20GHz` +//! HOSTNAME: `bm2`, CPU: `Intel(R) Core(TM) i7-7700K CPU @ 4.20GHz` //! EXECUTION: Some(Wasm), WASM-EXECUTION: Compiled, CHAIN: Some("dev"), DB CACHE: 1024 // Executed Command: @@ -31,9 +31,6 @@ // --steps=50 // --repeat=20 // --pallet=pallet_child_bounties -// --no-storage-info -// --no-median-slopes -// --no-min-squares // --extrinsic=* // --execution=wasm // --wasm-execution=compiled @@ -77,14 +74,12 @@ impl WeightInfo for SubstrateWeight { /// Storage: ChildBounties ChildBounties (r:0 w:1) /// Proof: ChildBounties ChildBounties (max_values: None, max_size: Some(145), added: 2620, mode: MaxEncodedLen) /// The range of component `d` is `[0, 300]`. - fn add_child_bounty(d: u32, ) -> Weight { + fn add_child_bounty(_d: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `678` - // Estimated: `14808` - // Minimum execution time: 54_017_000 picoseconds. - Weight::from_parts(55_142_443, 14808) - // Standard Error: 310 - .saturating_add(Weight::from_parts(1_199, 0).saturating_mul(d.into())) + // Estimated: `6196` + // Minimum execution time: 69_784_000 picoseconds. + Weight::from_parts(71_225_354, 6196) .saturating_add(T::DbWeight::get().reads(5_u64)) .saturating_add(T::DbWeight::get().writes(6_u64)) } @@ -97,9 +92,9 @@ impl WeightInfo for SubstrateWeight { fn propose_curator() -> Weight { // Proof Size summary in bytes: // Measured: `732` - // Estimated: `10745` - // Minimum execution time: 19_222_000 picoseconds. - Weight::from_parts(19_446_000, 10745) + // Estimated: `3642` + // Minimum execution time: 19_008_000 picoseconds. + Weight::from_parts(19_219_000, 3642) .saturating_add(T::DbWeight::get().reads(3_u64)) .saturating_add(T::DbWeight::get().writes(2_u64)) } @@ -112,9 +107,9 @@ impl WeightInfo for SubstrateWeight { fn accept_curator() -> Weight { // Proof Size summary in bytes: // Measured: `878` - // Estimated: `10845` - // Minimum execution time: 31_049_000 picoseconds. - Weight::from_parts(31_546_000, 10845) + // Estimated: `3642` + // Minimum execution time: 35_457_000 picoseconds. + Weight::from_parts(36_088_000, 3642) .saturating_add(T::DbWeight::get().reads(3_u64)) .saturating_add(T::DbWeight::get().writes(2_u64)) } @@ -127,9 +122,9 @@ impl WeightInfo for SubstrateWeight { fn unassign_curator() -> Weight { // Proof Size summary in bytes: // Measured: `878` - // Estimated: `10845` - // Minimum execution time: 33_860_000 picoseconds. - Weight::from_parts(34_897_000, 10845) + // Estimated: `3642` + // Minimum execution time: 38_244_000 picoseconds. + Weight::from_parts(38_611_000, 3642) .saturating_add(T::DbWeight::get().reads(3_u64)) .saturating_add(T::DbWeight::get().writes(2_u64)) } @@ -140,9 +135,9 @@ impl WeightInfo for SubstrateWeight { fn award_child_bounty() -> Weight { // Proof Size summary in bytes: // Measured: `775` - // Estimated: `7252` - // Minimum execution time: 23_178_000 picoseconds. - Weight::from_parts(23_412_000, 7252) + // Estimated: `3642` + // Minimum execution time: 22_762_000 picoseconds. + Weight::from_parts(23_249_000, 3642) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -157,9 +152,9 @@ impl WeightInfo for SubstrateWeight { fn claim_child_bounty() -> Weight { // Proof Size summary in bytes: // Measured: `648` - // Estimated: `15890` - // Minimum execution time: 76_189_000 picoseconds. - Weight::from_parts(76_770_000, 15890) + // Estimated: `8799` + // Minimum execution time: 112_019_000 picoseconds. + Weight::from_parts(113_190_000, 8799) .saturating_add(T::DbWeight::get().reads(5_u64)) .saturating_add(T::DbWeight::get().writes(6_u64)) } @@ -178,9 +173,9 @@ impl WeightInfo for SubstrateWeight { fn close_child_bounty_added() -> Weight { // Proof Size summary in bytes: // Measured: `978` - // Estimated: `20422` - // Minimum execution time: 55_161_000 picoseconds. - Weight::from_parts(55_578_000, 20422) + // Estimated: `6196` + // Minimum execution time: 72_477_000 picoseconds. + Weight::from_parts(73_573_000, 6196) .saturating_add(T::DbWeight::get().reads(6_u64)) .saturating_add(T::DbWeight::get().writes(6_u64)) } @@ -199,9 +194,9 @@ impl WeightInfo for SubstrateWeight { fn close_child_bounty_active() -> Weight { // Proof Size summary in bytes: // Measured: `1165` - // Estimated: `23025` - // Minimum execution time: 67_815_000 picoseconds. - Weight::from_parts(68_620_000, 23025) + // Estimated: `8799` + // Minimum execution time: 91_049_000 picoseconds. + Weight::from_parts(91_874_000, 8799) .saturating_add(T::DbWeight::get().reads(7_u64)) .saturating_add(T::DbWeight::get().writes(7_u64)) } @@ -222,14 +217,12 @@ impl WeightInfo for () { /// Storage: ChildBounties ChildBounties (r:0 w:1) /// Proof: ChildBounties ChildBounties (max_values: None, max_size: Some(145), added: 2620, mode: MaxEncodedLen) /// The range of component `d` is `[0, 300]`. - fn add_child_bounty(d: u32, ) -> Weight { + fn add_child_bounty(_d: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `678` - // Estimated: `14808` - // Minimum execution time: 54_017_000 picoseconds. - Weight::from_parts(55_142_443, 14808) - // Standard Error: 310 - .saturating_add(Weight::from_parts(1_199, 0).saturating_mul(d.into())) + // Estimated: `6196` + // Minimum execution time: 69_784_000 picoseconds. + Weight::from_parts(71_225_354, 6196) .saturating_add(RocksDbWeight::get().reads(5_u64)) .saturating_add(RocksDbWeight::get().writes(6_u64)) } @@ -242,9 +235,9 @@ impl WeightInfo for () { fn propose_curator() -> Weight { // Proof Size summary in bytes: // Measured: `732` - // Estimated: `10745` - // Minimum execution time: 19_222_000 picoseconds. - Weight::from_parts(19_446_000, 10745) + // Estimated: `3642` + // Minimum execution time: 19_008_000 picoseconds. + Weight::from_parts(19_219_000, 3642) .saturating_add(RocksDbWeight::get().reads(3_u64)) .saturating_add(RocksDbWeight::get().writes(2_u64)) } @@ -257,9 +250,9 @@ impl WeightInfo for () { fn accept_curator() -> Weight { // Proof Size summary in bytes: // Measured: `878` - // Estimated: `10845` - // Minimum execution time: 31_049_000 picoseconds. - Weight::from_parts(31_546_000, 10845) + // Estimated: `3642` + // Minimum execution time: 35_457_000 picoseconds. + Weight::from_parts(36_088_000, 3642) .saturating_add(RocksDbWeight::get().reads(3_u64)) .saturating_add(RocksDbWeight::get().writes(2_u64)) } @@ -272,9 +265,9 @@ impl WeightInfo for () { fn unassign_curator() -> Weight { // Proof Size summary in bytes: // Measured: `878` - // Estimated: `10845` - // Minimum execution time: 33_860_000 picoseconds. - Weight::from_parts(34_897_000, 10845) + // Estimated: `3642` + // Minimum execution time: 38_244_000 picoseconds. + Weight::from_parts(38_611_000, 3642) .saturating_add(RocksDbWeight::get().reads(3_u64)) .saturating_add(RocksDbWeight::get().writes(2_u64)) } @@ -285,9 +278,9 @@ impl WeightInfo for () { fn award_child_bounty() -> Weight { // Proof Size summary in bytes: // Measured: `775` - // Estimated: `7252` - // Minimum execution time: 23_178_000 picoseconds. - Weight::from_parts(23_412_000, 7252) + // Estimated: `3642` + // Minimum execution time: 22_762_000 picoseconds. + Weight::from_parts(23_249_000, 3642) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } @@ -302,9 +295,9 @@ impl WeightInfo for () { fn claim_child_bounty() -> Weight { // Proof Size summary in bytes: // Measured: `648` - // Estimated: `15890` - // Minimum execution time: 76_189_000 picoseconds. - Weight::from_parts(76_770_000, 15890) + // Estimated: `8799` + // Minimum execution time: 112_019_000 picoseconds. + Weight::from_parts(113_190_000, 8799) .saturating_add(RocksDbWeight::get().reads(5_u64)) .saturating_add(RocksDbWeight::get().writes(6_u64)) } @@ -323,9 +316,9 @@ impl WeightInfo for () { fn close_child_bounty_added() -> Weight { // Proof Size summary in bytes: // Measured: `978` - // Estimated: `20422` - // Minimum execution time: 55_161_000 picoseconds. - Weight::from_parts(55_578_000, 20422) + // Estimated: `6196` + // Minimum execution time: 72_477_000 picoseconds. + Weight::from_parts(73_573_000, 6196) .saturating_add(RocksDbWeight::get().reads(6_u64)) .saturating_add(RocksDbWeight::get().writes(6_u64)) } @@ -344,9 +337,9 @@ impl WeightInfo for () { fn close_child_bounty_active() -> Weight { // Proof Size summary in bytes: // Measured: `1165` - // Estimated: `23025` - // Minimum execution time: 67_815_000 picoseconds. - Weight::from_parts(68_620_000, 23025) + // Estimated: `8799` + // Minimum execution time: 91_049_000 picoseconds. + Weight::from_parts(91_874_000, 8799) .saturating_add(RocksDbWeight::get().reads(7_u64)) .saturating_add(RocksDbWeight::get().writes(7_u64)) } diff --git a/frame/collective/src/weights.rs b/frame/collective/src/weights.rs index 2199c0205..bf739daca 100644 --- a/frame/collective/src/weights.rs +++ b/frame/collective/src/weights.rs @@ -18,9 +18,9 @@ //! Autogenerated weights for pallet_collective //! //! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev -//! DATE: 2023-03-15, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! DATE: 2023-04-06, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` //! WORST CASE MAP SIZE: `1000000` -//! HOSTNAME: `bm3`, CPU: `Intel(R) Core(TM) i7-7700K CPU @ 4.20GHz` +//! HOSTNAME: `bm2`, CPU: `Intel(R) Core(TM) i7-7700K CPU @ 4.20GHz` //! EXECUTION: Some(Wasm), WASM-EXECUTION: Compiled, CHAIN: Some("dev"), DB CACHE: 1024 // Executed Command: @@ -31,9 +31,6 @@ // --steps=50 // --repeat=20 // --pallet=pallet_collective -// --no-storage-info -// --no-median-slopes -// --no-min-squares // --extrinsic=* // --execution=wasm // --wasm-execution=compiled @@ -80,19 +77,19 @@ impl WeightInfo for SubstrateWeight { fn set_members(m: u32, _n: u32, p: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0 + m * (3232 ±0) + p * (3190 ±0)` - // Estimated: `19428 + m * (7799 ±24) + p * (10110 ±24)` - // Minimum execution time: 18_772_000 picoseconds. - Weight::from_parts(18_931_000, 19428) - // Standard Error: 65_247 - .saturating_add(Weight::from_parts(5_050_136, 0).saturating_mul(m.into())) - // Standard Error: 65_247 - .saturating_add(Weight::from_parts(8_235_069, 0).saturating_mul(p.into())) + // Estimated: `15861 + m * (1967 ±23) + p * (4332 ±23)` + // Minimum execution time: 19_398_000 picoseconds. + Weight::from_parts(19_542_000, 15861) + // Standard Error: 71_395 + .saturating_add(Weight::from_parts(5_630_062, 0).saturating_mul(m.into())) + // Standard Error: 71_395 + .saturating_add(Weight::from_parts(8_634_133, 0).saturating_mul(p.into())) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().reads((1_u64).saturating_mul(p.into()))) .saturating_add(T::DbWeight::get().writes(2_u64)) .saturating_add(T::DbWeight::get().writes((1_u64).saturating_mul(p.into()))) - .saturating_add(Weight::from_parts(0, 7799).saturating_mul(m.into())) - .saturating_add(Weight::from_parts(0, 10110).saturating_mul(p.into())) + .saturating_add(Weight::from_parts(0, 1967).saturating_mul(m.into())) + .saturating_add(Weight::from_parts(0, 4332).saturating_mul(p.into())) } /// Storage: Council Members (r:1 w:0) /// Proof Skipped: Council Members (max_values: Some(1), max_size: None, mode: Measured) @@ -102,12 +99,12 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `202 + m * (32 ±0)` // Estimated: `1688 + m * (32 ±0)` - // Minimum execution time: 17_741_000 picoseconds. - Weight::from_parts(17_580_031, 1688) - // Standard Error: 113 - .saturating_add(Weight::from_parts(941, 0).saturating_mul(b.into())) - // Standard Error: 1_170 - .saturating_add(Weight::from_parts(17_329, 0).saturating_mul(m.into())) + // Minimum execution time: 17_579_000 picoseconds. + Weight::from_parts(16_874_624, 1688) + // Standard Error: 34 + .saturating_add(Weight::from_parts(1_617, 0).saturating_mul(b.into())) + // Standard Error: 353 + .saturating_add(Weight::from_parts(19_759, 0).saturating_mul(m.into())) .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(Weight::from_parts(0, 32).saturating_mul(m.into())) } @@ -120,15 +117,15 @@ impl WeightInfo for SubstrateWeight { fn propose_execute(b: u32, m: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `202 + m * (32 ±0)` - // Estimated: `5356 + m * (64 ±0)` - // Minimum execution time: 20_214_000 picoseconds. - Weight::from_parts(19_508_816, 5356) - // Standard Error: 46 - .saturating_add(Weight::from_parts(1_646, 0).saturating_mul(b.into())) - // Standard Error: 475 - .saturating_add(Weight::from_parts(27_145, 0).saturating_mul(m.into())) + // Estimated: `3668 + m * (32 ±0)` + // Minimum execution time: 20_339_000 picoseconds. + Weight::from_parts(19_534_549, 3668) + // Standard Error: 45 + .saturating_add(Weight::from_parts(1_636, 0).saturating_mul(b.into())) + // Standard Error: 469 + .saturating_add(Weight::from_parts(28_178, 0).saturating_mul(m.into())) .saturating_add(T::DbWeight::get().reads(2_u64)) - .saturating_add(Weight::from_parts(0, 64).saturating_mul(m.into())) + .saturating_add(Weight::from_parts(0, 32).saturating_mul(m.into())) } /// Storage: Council Members (r:1 w:0) /// Proof Skipped: Council Members (max_values: Some(1), max_size: None, mode: Measured) @@ -146,19 +143,19 @@ impl WeightInfo for SubstrateWeight { fn propose_proposed(b: u32, m: u32, p: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `492 + m * (32 ±0) + p * (36 ±0)` - // Estimated: `10015 + m * (165 ±0) + p * (180 ±0)` - // Minimum execution time: 27_872_000 picoseconds. - Weight::from_parts(28_010_227, 10015) - // Standard Error: 90 - .saturating_add(Weight::from_parts(2_643, 0).saturating_mul(b.into())) - // Standard Error: 944 - .saturating_add(Weight::from_parts(17_897, 0).saturating_mul(m.into())) - // Standard Error: 932 - .saturating_add(Weight::from_parts(122_368, 0).saturating_mul(p.into())) + // Estimated: `3884 + m * (33 ±0) + p * (36 ±0)` + // Minimum execution time: 27_793_000 picoseconds. + Weight::from_parts(28_095_462, 3884) + // Standard Error: 82 + .saturating_add(Weight::from_parts(2_646, 0).saturating_mul(b.into())) + // Standard Error: 861 + .saturating_add(Weight::from_parts(22_332, 0).saturating_mul(m.into())) + // Standard Error: 850 + .saturating_add(Weight::from_parts(121_560, 0).saturating_mul(p.into())) .saturating_add(T::DbWeight::get().reads(4_u64)) .saturating_add(T::DbWeight::get().writes(4_u64)) - .saturating_add(Weight::from_parts(0, 165).saturating_mul(m.into())) - .saturating_add(Weight::from_parts(0, 180).saturating_mul(p.into())) + .saturating_add(Weight::from_parts(0, 33).saturating_mul(m.into())) + .saturating_add(Weight::from_parts(0, 36).saturating_mul(p.into())) } /// Storage: Council Members (r:1 w:0) /// Proof Skipped: Council Members (max_values: Some(1), max_size: None, mode: Measured) @@ -168,14 +165,14 @@ impl WeightInfo for SubstrateWeight { fn vote(m: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `941 + m * (64 ±0)` - // Estimated: `6830 + m * (128 ±0)` - // Minimum execution time: 22_518_000 picoseconds. - Weight::from_parts(23_281_512, 6830) - // Standard Error: 647 - .saturating_add(Weight::from_parts(53_974, 0).saturating_mul(m.into())) + // Estimated: `4405 + m * (64 ±0)` + // Minimum execution time: 23_096_000 picoseconds. + Weight::from_parts(23_793_304, 4405) + // Standard Error: 675 + .saturating_add(Weight::from_parts(51_741, 0).saturating_mul(m.into())) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) - .saturating_add(Weight::from_parts(0, 128).saturating_mul(m.into())) + .saturating_add(Weight::from_parts(0, 64).saturating_mul(m.into())) } /// Storage: Council Voting (r:1 w:1) /// Proof Skipped: Council Voting (max_values: None, max_size: None, mode: Measured) @@ -190,17 +187,17 @@ impl WeightInfo for SubstrateWeight { fn close_early_disapproved(m: u32, p: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `530 + m * (64 ±0) + p * (36 ±0)` - // Estimated: `8475 + m * (260 ±0) + p * (144 ±0)` - // Minimum execution time: 29_020_000 picoseconds. - Weight::from_parts(29_009_937, 8475) - // Standard Error: 809 - .saturating_add(Weight::from_parts(31_952, 0).saturating_mul(m.into())) - // Standard Error: 789 - .saturating_add(Weight::from_parts(124_282, 0).saturating_mul(p.into())) + // Estimated: `3975 + m * (65 ±0) + p * (36 ±0)` + // Minimum execution time: 29_635_000 picoseconds. + Weight::from_parts(29_574_124, 3975) + // Standard Error: 755 + .saturating_add(Weight::from_parts(29_126, 0).saturating_mul(m.into())) + // Standard Error: 737 + .saturating_add(Weight::from_parts(123_438, 0).saturating_mul(p.into())) .saturating_add(T::DbWeight::get().reads(3_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) - .saturating_add(Weight::from_parts(0, 260).saturating_mul(m.into())) - .saturating_add(Weight::from_parts(0, 144).saturating_mul(p.into())) + .saturating_add(Weight::from_parts(0, 65).saturating_mul(m.into())) + .saturating_add(Weight::from_parts(0, 36).saturating_mul(p.into())) } /// Storage: Council Voting (r:1 w:1) /// Proof Skipped: Council Voting (max_values: None, max_size: None, mode: Measured) @@ -216,20 +213,20 @@ impl WeightInfo for SubstrateWeight { fn close_early_approved(b: u32, m: u32, p: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `832 + b * (1 ±0) + m * (64 ±0) + p * (40 ±0)` - // Estimated: `12636 + b * (4 ±0) + m * (264 ±0) + p * (160 ±0)` - // Minimum execution time: 41_541_000 picoseconds. - Weight::from_parts(43_640_322, 12636) - // Standard Error: 104 - .saturating_add(Weight::from_parts(2_178, 0).saturating_mul(b.into())) - // Standard Error: 1_101 - .saturating_add(Weight::from_parts(20_512, 0).saturating_mul(m.into())) - // Standard Error: 1_073 - .saturating_add(Weight::from_parts(133_112, 0).saturating_mul(p.into())) + // Estimated: `4149 + b * (1 ±0) + m * (66 ±0) + p * (40 ±0)` + // Minimum execution time: 41_934_000 picoseconds. + Weight::from_parts(44_022_379, 4149) + // Standard Error: 105 + .saturating_add(Weight::from_parts(2_266, 0).saturating_mul(b.into())) + // Standard Error: 1_112 + .saturating_add(Weight::from_parts(18_074, 0).saturating_mul(m.into())) + // Standard Error: 1_084 + .saturating_add(Weight::from_parts(132_405, 0).saturating_mul(p.into())) .saturating_add(T::DbWeight::get().reads(4_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) - .saturating_add(Weight::from_parts(0, 4).saturating_mul(b.into())) - .saturating_add(Weight::from_parts(0, 264).saturating_mul(m.into())) - .saturating_add(Weight::from_parts(0, 160).saturating_mul(p.into())) + .saturating_add(Weight::from_parts(0, 1).saturating_mul(b.into())) + .saturating_add(Weight::from_parts(0, 66).saturating_mul(m.into())) + .saturating_add(Weight::from_parts(0, 40).saturating_mul(p.into())) } /// Storage: Council Voting (r:1 w:1) /// Proof Skipped: Council Voting (max_values: None, max_size: None, mode: Measured) @@ -246,17 +243,17 @@ impl WeightInfo for SubstrateWeight { fn close_disapproved(m: u32, p: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `550 + m * (64 ±0) + p * (36 ±0)` - // Estimated: `10570 + m * (325 ±0) + p * (180 ±0)` - // Minimum execution time: 32_773_000 picoseconds. - Weight::from_parts(33_031_550, 10570) - // Standard Error: 1_205 - .saturating_add(Weight::from_parts(26_143, 0).saturating_mul(m.into())) - // Standard Error: 1_175 - .saturating_add(Weight::from_parts(137_503, 0).saturating_mul(p.into())) + // Estimated: `3995 + m * (65 ±0) + p * (36 ±0)` + // Minimum execution time: 33_146_000 picoseconds. + Weight::from_parts(31_957_128, 3995) + // Standard Error: 2_321 + .saturating_add(Weight::from_parts(31_272, 0).saturating_mul(m.into())) + // Standard Error: 2_264 + .saturating_add(Weight::from_parts(156_129, 0).saturating_mul(p.into())) .saturating_add(T::DbWeight::get().reads(4_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) - .saturating_add(Weight::from_parts(0, 325).saturating_mul(m.into())) - .saturating_add(Weight::from_parts(0, 180).saturating_mul(p.into())) + .saturating_add(Weight::from_parts(0, 65).saturating_mul(m.into())) + .saturating_add(Weight::from_parts(0, 36).saturating_mul(p.into())) } /// Storage: Council Voting (r:1 w:1) /// Proof Skipped: Council Voting (max_values: None, max_size: None, mode: Measured) @@ -274,20 +271,20 @@ impl WeightInfo for SubstrateWeight { fn close_approved(b: u32, m: u32, p: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `852 + b * (1 ±0) + m * (64 ±0) + p * (40 ±0)` - // Estimated: `14905 + b * (5 ±0) + m * (330 ±0) + p * (200 ±0)` - // Minimum execution time: 44_855_000 picoseconds. - Weight::from_parts(47_307_460, 14905) + // Estimated: `4169 + b * (1 ±0) + m * (66 ±0) + p * (40 ±0)` + // Minimum execution time: 44_278_000 picoseconds. + Weight::from_parts(46_039_907, 4169) // Standard Error: 100 - .saturating_add(Weight::from_parts(1_536, 0).saturating_mul(b.into())) - // Standard Error: 1_065 - .saturating_add(Weight::from_parts(20_549, 0).saturating_mul(m.into())) - // Standard Error: 1_038 - .saturating_add(Weight::from_parts(133_143, 0).saturating_mul(p.into())) + .saturating_add(Weight::from_parts(2_257, 0).saturating_mul(b.into())) + // Standard Error: 1_062 + .saturating_add(Weight::from_parts(25_055, 0).saturating_mul(m.into())) + // Standard Error: 1_035 + .saturating_add(Weight::from_parts(136_282, 0).saturating_mul(p.into())) .saturating_add(T::DbWeight::get().reads(5_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) - .saturating_add(Weight::from_parts(0, 5).saturating_mul(b.into())) - .saturating_add(Weight::from_parts(0, 330).saturating_mul(m.into())) - .saturating_add(Weight::from_parts(0, 200).saturating_mul(p.into())) + .saturating_add(Weight::from_parts(0, 1).saturating_mul(b.into())) + .saturating_add(Weight::from_parts(0, 66).saturating_mul(m.into())) + .saturating_add(Weight::from_parts(0, 40).saturating_mul(p.into())) } /// Storage: Council Proposals (r:1 w:1) /// Proof Skipped: Council Proposals (max_values: Some(1), max_size: None, mode: Measured) @@ -299,14 +296,14 @@ impl WeightInfo for SubstrateWeight { fn disapprove_proposal(p: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `359 + p * (32 ±0)` - // Estimated: `2562 + p * (96 ±0)` - // Minimum execution time: 16_420_000 picoseconds. - Weight::from_parts(18_236_525, 2562) - // Standard Error: 894 - .saturating_add(Weight::from_parts(115_590, 0).saturating_mul(p.into())) + // Estimated: `1844 + p * (32 ±0)` + // Minimum execution time: 16_500_000 picoseconds. + Weight::from_parts(18_376_538, 1844) + // Standard Error: 755 + .saturating_add(Weight::from_parts(113_189, 0).saturating_mul(p.into())) .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) - .saturating_add(Weight::from_parts(0, 96).saturating_mul(p.into())) + .saturating_add(Weight::from_parts(0, 32).saturating_mul(p.into())) } } @@ -326,19 +323,19 @@ impl WeightInfo for () { fn set_members(m: u32, _n: u32, p: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0 + m * (3232 ±0) + p * (3190 ±0)` - // Estimated: `19428 + m * (7799 ±24) + p * (10110 ±24)` - // Minimum execution time: 18_772_000 picoseconds. - Weight::from_parts(18_931_000, 19428) - // Standard Error: 65_247 - .saturating_add(Weight::from_parts(5_050_136, 0).saturating_mul(m.into())) - // Standard Error: 65_247 - .saturating_add(Weight::from_parts(8_235_069, 0).saturating_mul(p.into())) + // Estimated: `15861 + m * (1967 ±23) + p * (4332 ±23)` + // Minimum execution time: 19_398_000 picoseconds. + Weight::from_parts(19_542_000, 15861) + // Standard Error: 71_395 + .saturating_add(Weight::from_parts(5_630_062, 0).saturating_mul(m.into())) + // Standard Error: 71_395 + .saturating_add(Weight::from_parts(8_634_133, 0).saturating_mul(p.into())) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().reads((1_u64).saturating_mul(p.into()))) .saturating_add(RocksDbWeight::get().writes(2_u64)) .saturating_add(RocksDbWeight::get().writes((1_u64).saturating_mul(p.into()))) - .saturating_add(Weight::from_parts(0, 7799).saturating_mul(m.into())) - .saturating_add(Weight::from_parts(0, 10110).saturating_mul(p.into())) + .saturating_add(Weight::from_parts(0, 1967).saturating_mul(m.into())) + .saturating_add(Weight::from_parts(0, 4332).saturating_mul(p.into())) } /// Storage: Council Members (r:1 w:0) /// Proof Skipped: Council Members (max_values: Some(1), max_size: None, mode: Measured) @@ -348,12 +345,12 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `202 + m * (32 ±0)` // Estimated: `1688 + m * (32 ±0)` - // Minimum execution time: 17_741_000 picoseconds. - Weight::from_parts(17_580_031, 1688) - // Standard Error: 113 - .saturating_add(Weight::from_parts(941, 0).saturating_mul(b.into())) - // Standard Error: 1_170 - .saturating_add(Weight::from_parts(17_329, 0).saturating_mul(m.into())) + // Minimum execution time: 17_579_000 picoseconds. + Weight::from_parts(16_874_624, 1688) + // Standard Error: 34 + .saturating_add(Weight::from_parts(1_617, 0).saturating_mul(b.into())) + // Standard Error: 353 + .saturating_add(Weight::from_parts(19_759, 0).saturating_mul(m.into())) .saturating_add(RocksDbWeight::get().reads(1_u64)) .saturating_add(Weight::from_parts(0, 32).saturating_mul(m.into())) } @@ -366,15 +363,15 @@ impl WeightInfo for () { fn propose_execute(b: u32, m: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `202 + m * (32 ±0)` - // Estimated: `5356 + m * (64 ±0)` - // Minimum execution time: 20_214_000 picoseconds. - Weight::from_parts(19_508_816, 5356) - // Standard Error: 46 - .saturating_add(Weight::from_parts(1_646, 0).saturating_mul(b.into())) - // Standard Error: 475 - .saturating_add(Weight::from_parts(27_145, 0).saturating_mul(m.into())) + // Estimated: `3668 + m * (32 ±0)` + // Minimum execution time: 20_339_000 picoseconds. + Weight::from_parts(19_534_549, 3668) + // Standard Error: 45 + .saturating_add(Weight::from_parts(1_636, 0).saturating_mul(b.into())) + // Standard Error: 469 + .saturating_add(Weight::from_parts(28_178, 0).saturating_mul(m.into())) .saturating_add(RocksDbWeight::get().reads(2_u64)) - .saturating_add(Weight::from_parts(0, 64).saturating_mul(m.into())) + .saturating_add(Weight::from_parts(0, 32).saturating_mul(m.into())) } /// Storage: Council Members (r:1 w:0) /// Proof Skipped: Council Members (max_values: Some(1), max_size: None, mode: Measured) @@ -392,19 +389,19 @@ impl WeightInfo for () { fn propose_proposed(b: u32, m: u32, p: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `492 + m * (32 ±0) + p * (36 ±0)` - // Estimated: `10015 + m * (165 ±0) + p * (180 ±0)` - // Minimum execution time: 27_872_000 picoseconds. - Weight::from_parts(28_010_227, 10015) - // Standard Error: 90 - .saturating_add(Weight::from_parts(2_643, 0).saturating_mul(b.into())) - // Standard Error: 944 - .saturating_add(Weight::from_parts(17_897, 0).saturating_mul(m.into())) - // Standard Error: 932 - .saturating_add(Weight::from_parts(122_368, 0).saturating_mul(p.into())) + // Estimated: `3884 + m * (33 ±0) + p * (36 ±0)` + // Minimum execution time: 27_793_000 picoseconds. + Weight::from_parts(28_095_462, 3884) + // Standard Error: 82 + .saturating_add(Weight::from_parts(2_646, 0).saturating_mul(b.into())) + // Standard Error: 861 + .saturating_add(Weight::from_parts(22_332, 0).saturating_mul(m.into())) + // Standard Error: 850 + .saturating_add(Weight::from_parts(121_560, 0).saturating_mul(p.into())) .saturating_add(RocksDbWeight::get().reads(4_u64)) .saturating_add(RocksDbWeight::get().writes(4_u64)) - .saturating_add(Weight::from_parts(0, 165).saturating_mul(m.into())) - .saturating_add(Weight::from_parts(0, 180).saturating_mul(p.into())) + .saturating_add(Weight::from_parts(0, 33).saturating_mul(m.into())) + .saturating_add(Weight::from_parts(0, 36).saturating_mul(p.into())) } /// Storage: Council Members (r:1 w:0) /// Proof Skipped: Council Members (max_values: Some(1), max_size: None, mode: Measured) @@ -414,14 +411,14 @@ impl WeightInfo for () { fn vote(m: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `941 + m * (64 ±0)` - // Estimated: `6830 + m * (128 ±0)` - // Minimum execution time: 22_518_000 picoseconds. - Weight::from_parts(23_281_512, 6830) - // Standard Error: 647 - .saturating_add(Weight::from_parts(53_974, 0).saturating_mul(m.into())) + // Estimated: `4405 + m * (64 ±0)` + // Minimum execution time: 23_096_000 picoseconds. + Weight::from_parts(23_793_304, 4405) + // Standard Error: 675 + .saturating_add(Weight::from_parts(51_741, 0).saturating_mul(m.into())) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) - .saturating_add(Weight::from_parts(0, 128).saturating_mul(m.into())) + .saturating_add(Weight::from_parts(0, 64).saturating_mul(m.into())) } /// Storage: Council Voting (r:1 w:1) /// Proof Skipped: Council Voting (max_values: None, max_size: None, mode: Measured) @@ -436,17 +433,17 @@ impl WeightInfo for () { fn close_early_disapproved(m: u32, p: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `530 + m * (64 ±0) + p * (36 ±0)` - // Estimated: `8475 + m * (260 ±0) + p * (144 ±0)` - // Minimum execution time: 29_020_000 picoseconds. - Weight::from_parts(29_009_937, 8475) - // Standard Error: 809 - .saturating_add(Weight::from_parts(31_952, 0).saturating_mul(m.into())) - // Standard Error: 789 - .saturating_add(Weight::from_parts(124_282, 0).saturating_mul(p.into())) + // Estimated: `3975 + m * (65 ±0) + p * (36 ±0)` + // Minimum execution time: 29_635_000 picoseconds. + Weight::from_parts(29_574_124, 3975) + // Standard Error: 755 + .saturating_add(Weight::from_parts(29_126, 0).saturating_mul(m.into())) + // Standard Error: 737 + .saturating_add(Weight::from_parts(123_438, 0).saturating_mul(p.into())) .saturating_add(RocksDbWeight::get().reads(3_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) - .saturating_add(Weight::from_parts(0, 260).saturating_mul(m.into())) - .saturating_add(Weight::from_parts(0, 144).saturating_mul(p.into())) + .saturating_add(Weight::from_parts(0, 65).saturating_mul(m.into())) + .saturating_add(Weight::from_parts(0, 36).saturating_mul(p.into())) } /// Storage: Council Voting (r:1 w:1) /// Proof Skipped: Council Voting (max_values: None, max_size: None, mode: Measured) @@ -462,20 +459,20 @@ impl WeightInfo for () { fn close_early_approved(b: u32, m: u32, p: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `832 + b * (1 ±0) + m * (64 ±0) + p * (40 ±0)` - // Estimated: `12636 + b * (4 ±0) + m * (264 ±0) + p * (160 ±0)` - // Minimum execution time: 41_541_000 picoseconds. - Weight::from_parts(43_640_322, 12636) - // Standard Error: 104 - .saturating_add(Weight::from_parts(2_178, 0).saturating_mul(b.into())) - // Standard Error: 1_101 - .saturating_add(Weight::from_parts(20_512, 0).saturating_mul(m.into())) - // Standard Error: 1_073 - .saturating_add(Weight::from_parts(133_112, 0).saturating_mul(p.into())) + // Estimated: `4149 + b * (1 ±0) + m * (66 ±0) + p * (40 ±0)` + // Minimum execution time: 41_934_000 picoseconds. + Weight::from_parts(44_022_379, 4149) + // Standard Error: 105 + .saturating_add(Weight::from_parts(2_266, 0).saturating_mul(b.into())) + // Standard Error: 1_112 + .saturating_add(Weight::from_parts(18_074, 0).saturating_mul(m.into())) + // Standard Error: 1_084 + .saturating_add(Weight::from_parts(132_405, 0).saturating_mul(p.into())) .saturating_add(RocksDbWeight::get().reads(4_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) - .saturating_add(Weight::from_parts(0, 4).saturating_mul(b.into())) - .saturating_add(Weight::from_parts(0, 264).saturating_mul(m.into())) - .saturating_add(Weight::from_parts(0, 160).saturating_mul(p.into())) + .saturating_add(Weight::from_parts(0, 1).saturating_mul(b.into())) + .saturating_add(Weight::from_parts(0, 66).saturating_mul(m.into())) + .saturating_add(Weight::from_parts(0, 40).saturating_mul(p.into())) } /// Storage: Council Voting (r:1 w:1) /// Proof Skipped: Council Voting (max_values: None, max_size: None, mode: Measured) @@ -492,17 +489,17 @@ impl WeightInfo for () { fn close_disapproved(m: u32, p: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `550 + m * (64 ±0) + p * (36 ±0)` - // Estimated: `10570 + m * (325 ±0) + p * (180 ±0)` - // Minimum execution time: 32_773_000 picoseconds. - Weight::from_parts(33_031_550, 10570) - // Standard Error: 1_205 - .saturating_add(Weight::from_parts(26_143, 0).saturating_mul(m.into())) - // Standard Error: 1_175 - .saturating_add(Weight::from_parts(137_503, 0).saturating_mul(p.into())) + // Estimated: `3995 + m * (65 ±0) + p * (36 ±0)` + // Minimum execution time: 33_146_000 picoseconds. + Weight::from_parts(31_957_128, 3995) + // Standard Error: 2_321 + .saturating_add(Weight::from_parts(31_272, 0).saturating_mul(m.into())) + // Standard Error: 2_264 + .saturating_add(Weight::from_parts(156_129, 0).saturating_mul(p.into())) .saturating_add(RocksDbWeight::get().reads(4_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) - .saturating_add(Weight::from_parts(0, 325).saturating_mul(m.into())) - .saturating_add(Weight::from_parts(0, 180).saturating_mul(p.into())) + .saturating_add(Weight::from_parts(0, 65).saturating_mul(m.into())) + .saturating_add(Weight::from_parts(0, 36).saturating_mul(p.into())) } /// Storage: Council Voting (r:1 w:1) /// Proof Skipped: Council Voting (max_values: None, max_size: None, mode: Measured) @@ -520,20 +517,20 @@ impl WeightInfo for () { fn close_approved(b: u32, m: u32, p: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `852 + b * (1 ±0) + m * (64 ±0) + p * (40 ±0)` - // Estimated: `14905 + b * (5 ±0) + m * (330 ±0) + p * (200 ±0)` - // Minimum execution time: 44_855_000 picoseconds. - Weight::from_parts(47_307_460, 14905) + // Estimated: `4169 + b * (1 ±0) + m * (66 ±0) + p * (40 ±0)` + // Minimum execution time: 44_278_000 picoseconds. + Weight::from_parts(46_039_907, 4169) // Standard Error: 100 - .saturating_add(Weight::from_parts(1_536, 0).saturating_mul(b.into())) - // Standard Error: 1_065 - .saturating_add(Weight::from_parts(20_549, 0).saturating_mul(m.into())) - // Standard Error: 1_038 - .saturating_add(Weight::from_parts(133_143, 0).saturating_mul(p.into())) + .saturating_add(Weight::from_parts(2_257, 0).saturating_mul(b.into())) + // Standard Error: 1_062 + .saturating_add(Weight::from_parts(25_055, 0).saturating_mul(m.into())) + // Standard Error: 1_035 + .saturating_add(Weight::from_parts(136_282, 0).saturating_mul(p.into())) .saturating_add(RocksDbWeight::get().reads(5_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) - .saturating_add(Weight::from_parts(0, 5).saturating_mul(b.into())) - .saturating_add(Weight::from_parts(0, 330).saturating_mul(m.into())) - .saturating_add(Weight::from_parts(0, 200).saturating_mul(p.into())) + .saturating_add(Weight::from_parts(0, 1).saturating_mul(b.into())) + .saturating_add(Weight::from_parts(0, 66).saturating_mul(m.into())) + .saturating_add(Weight::from_parts(0, 40).saturating_mul(p.into())) } /// Storage: Council Proposals (r:1 w:1) /// Proof Skipped: Council Proposals (max_values: Some(1), max_size: None, mode: Measured) @@ -545,13 +542,13 @@ impl WeightInfo for () { fn disapprove_proposal(p: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `359 + p * (32 ±0)` - // Estimated: `2562 + p * (96 ±0)` - // Minimum execution time: 16_420_000 picoseconds. - Weight::from_parts(18_236_525, 2562) - // Standard Error: 894 - .saturating_add(Weight::from_parts(115_590, 0).saturating_mul(p.into())) + // Estimated: `1844 + p * (32 ±0)` + // Minimum execution time: 16_500_000 picoseconds. + Weight::from_parts(18_376_538, 1844) + // Standard Error: 755 + .saturating_add(Weight::from_parts(113_189, 0).saturating_mul(p.into())) .saturating_add(RocksDbWeight::get().reads(1_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) - .saturating_add(Weight::from_parts(0, 96).saturating_mul(p.into())) + .saturating_add(Weight::from_parts(0, 32).saturating_mul(p.into())) } } diff --git a/frame/contracts/src/weights.rs b/frame/contracts/src/weights.rs index 8e81364ac..cdaba7f59 100644 --- a/frame/contracts/src/weights.rs +++ b/frame/contracts/src/weights.rs @@ -18,26 +18,25 @@ //! Autogenerated weights for pallet_contracts //! //! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev -//! DATE: 2023-04-06, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! DATE: 2023-04-12, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` //! WORST CASE MAP SIZE: `1000000` -//! HOSTNAME: `bm3`, CPU: `Intel(R) Core(TM) i7-7700K CPU @ 4.20GHz` +//! HOSTNAME: `bm2`, CPU: `Intel(R) Core(TM) i7-7700K CPU @ 4.20GHz` //! EXECUTION: Some(Wasm), WASM-EXECUTION: Compiled, CHAIN: Some("dev"), DB CACHE: 1024 // Executed Command: -// target/production/substrate +// ./target/production/substrate // benchmark // pallet +// --chain=dev // --steps=50 // --repeat=20 +// --pallet=pallet_contracts // --extrinsic=* // --execution=wasm // --wasm-execution=compiled // --heap-pages=4096 -// --json-file=/var/lib/gitlab-runner/builds/zyw4fam_/0/parity/mirrors/substrate/.git/.artifacts/bench.json -// --pallet=pallet_contracts -// --chain=dev -// --header=./HEADER-APACHE2 // --output=./frame/contracts/src/weights.rs +// --header=./HEADER-APACHE2 // --template=./.maintain/frame-weight-template.hbs #![cfg_attr(rustfmt, rustfmt_skip)] @@ -45,7 +44,7 @@ #![allow(unused_imports)] use frame_support::{traits::Get, weights::{Weight, constants::RocksDbWeight}}; -use sp_std::marker::PhantomData; +use core::marker::PhantomData; /// Weight functions needed for pallet_contracts. pub trait WeightInfo { @@ -178,8 +177,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `109` // Estimated: `1594` - // Minimum execution time: 2_503_000 picoseconds. - Weight::from_parts(2_603_000, 1594) + // Minimum execution time: 2_565_000 picoseconds. + Weight::from_parts(2_759_000, 1594) .saturating_add(T::DbWeight::get().reads(1_u64)) } /// Storage: Skipped Metadata (r:0 w:0) @@ -189,10 +188,10 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `488 + k * (69 ±0)` // Estimated: `478 + k * (70 ±0)` - // Minimum execution time: 13_292_000 picoseconds. - Weight::from_parts(9_180_439, 478) - // Standard Error: 997 - .saturating_add(Weight::from_parts(967_522, 0).saturating_mul(k.into())) + // Minimum execution time: 13_480_000 picoseconds. + Weight::from_parts(10_153_869, 478) + // Standard Error: 427 + .saturating_add(Weight::from_parts(958_726, 0).saturating_mul(k.into())) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().reads((1_u64).saturating_mul(k.into()))) .saturating_add(T::DbWeight::get().writes(2_u64)) @@ -207,14 +206,14 @@ impl WeightInfo for SubstrateWeight { fn reinstrument(c: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `238 + c * (1 ±0)` - // Estimated: `3951 + c * (2 ±0)` - // Minimum execution time: 30_822_000 picoseconds. - Weight::from_parts(26_457_115, 3951) - // Standard Error: 58 - .saturating_add(Weight::from_parts(54_868, 0).saturating_mul(c.into())) + // Estimated: `3708 + c * (1 ±0)` + // Minimum execution time: 30_406_000 picoseconds. + Weight::from_parts(28_467_370, 3708) + // Standard Error: 46 + .saturating_add(Weight::from_parts(53_724, 0).saturating_mul(c.into())) .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) - .saturating_add(Weight::from_parts(0, 2).saturating_mul(c.into())) + .saturating_add(Weight::from_parts(0, 1).saturating_mul(c.into())) } /// Storage: Contracts ContractInfoOf (r:1 w:1) /// Proof: Contracts ContractInfoOf (max_values: None, max_size: Some(290), added: 2765, mode: Measured) @@ -230,14 +229,14 @@ impl WeightInfo for SubstrateWeight { fn call_with_code_per_byte(c: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `707` - // Estimated: `21400 + c * (5 ±0)` - // Minimum execution time: 263_719_000 picoseconds. - Weight::from_parts(275_281_941, 21400) - // Standard Error: 32 - .saturating_add(Weight::from_parts(37_880, 0).saturating_mul(c.into())) + // Estimated: `6656 + c * (1 ±0)` + // Minimum execution time: 263_198_000 picoseconds. + Weight::from_parts(276_162_279, 6656) + // Standard Error: 18 + .saturating_add(Weight::from_parts(37_378, 0).saturating_mul(c.into())) .saturating_add(T::DbWeight::get().reads(6_u64)) .saturating_add(T::DbWeight::get().writes(4_u64)) - .saturating_add(Weight::from_parts(0, 5).saturating_mul(c.into())) + .saturating_add(Weight::from_parts(0, 1).saturating_mul(c.into())) } /// Storage: Contracts OwnerInfoOf (r:1 w:1) /// Proof: Contracts OwnerInfoOf (max_values: None, max_size: Some(88), added: 2563, mode: Measured) @@ -261,15 +260,15 @@ impl WeightInfo for SubstrateWeight { fn instantiate_with_code(c: u32, i: u32, s: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `270` - // Estimated: `26207` - // Minimum execution time: 3_133_756_000 picoseconds. - Weight::from_parts(556_483_545, 26207) - // Standard Error: 294 - .saturating_add(Weight::from_parts(107_914, 0).saturating_mul(c.into())) - // Standard Error: 17 - .saturating_add(Weight::from_parts(1_143, 0).saturating_mul(i.into())) - // Standard Error: 17 - .saturating_add(Weight::from_parts(1_456, 0).saturating_mul(s.into())) + // Estimated: `8659` + // Minimum execution time: 3_132_259_000 picoseconds. + Weight::from_parts(513_284_834, 8659) + // Standard Error: 280 + .saturating_add(Weight::from_parts(106_723, 0).saturating_mul(c.into())) + // Standard Error: 16 + .saturating_add(Weight::from_parts(1_166, 0).saturating_mul(i.into())) + // Standard Error: 16 + .saturating_add(Weight::from_parts(1_436, 0).saturating_mul(s.into())) .saturating_add(T::DbWeight::get().reads(9_u64)) .saturating_add(T::DbWeight::get().writes(10_u64)) } @@ -292,13 +291,13 @@ impl WeightInfo for SubstrateWeight { fn instantiate(i: u32, s: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `482` - // Estimated: `28521` - // Minimum execution time: 1_646_953_000 picoseconds. - Weight::from_parts(262_115_215, 28521) - // Standard Error: 8 - .saturating_add(Weight::from_parts(1_454, 0).saturating_mul(i.into())) - // Standard Error: 8 - .saturating_add(Weight::from_parts(1_463, 0).saturating_mul(s.into())) + // Estimated: `6408` + // Minimum execution time: 1_646_604_000 picoseconds. + Weight::from_parts(271_369_256, 6408) + // Standard Error: 7 + .saturating_add(Weight::from_parts(1_426, 0).saturating_mul(i.into())) + // Standard Error: 7 + .saturating_add(Weight::from_parts(1_438, 0).saturating_mul(s.into())) .saturating_add(T::DbWeight::get().reads(9_u64)) .saturating_add(T::DbWeight::get().writes(7_u64)) } @@ -315,9 +314,9 @@ impl WeightInfo for SubstrateWeight { fn call() -> Weight { // Proof Size summary in bytes: // Measured: `759` - // Estimated: `21615` - // Minimum execution time: 190_769_000 picoseconds. - Weight::from_parts(191_717_000, 21615) + // Estimated: `6699` + // Minimum execution time: 191_360_000 picoseconds. + Weight::from_parts(192_625_000, 6699) .saturating_add(T::DbWeight::get().reads(6_u64)) .saturating_add(T::DbWeight::get().writes(4_u64)) } @@ -333,11 +332,11 @@ impl WeightInfo for SubstrateWeight { fn upload_code(c: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `109` - // Estimated: `7366` - // Minimum execution time: 246_204_000 picoseconds. - Weight::from_parts(243_234_754, 7366) - // Standard Error: 78 - .saturating_add(Weight::from_parts(109_232, 0).saturating_mul(c.into())) + // Estimated: `3574` + // Minimum execution time: 245_207_000 picoseconds. + Weight::from_parts(244_703_457, 3574) + // Standard Error: 61 + .saturating_add(Weight::from_parts(106_850, 0).saturating_mul(c.into())) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().writes(4_u64)) } @@ -352,9 +351,9 @@ impl WeightInfo for SubstrateWeight { fn remove_code() -> Weight { // Proof Size summary in bytes: // Measured: `255` - // Estimated: `7950` - // Minimum execution time: 33_516_000 picoseconds. - Weight::from_parts(33_995_000, 7950) + // Estimated: `3720` + // Minimum execution time: 33_560_000 picoseconds. + Weight::from_parts(33_833_000, 3720) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().writes(4_u64)) } @@ -367,9 +366,9 @@ impl WeightInfo for SubstrateWeight { fn set_code() -> Weight { // Proof Size summary in bytes: // Measured: `570` - // Estimated: `19530` - // Minimum execution time: 33_255_000 picoseconds. - Weight::from_parts(33_692_000, 19530) + // Estimated: `8985` + // Minimum execution time: 33_288_000 picoseconds. + Weight::from_parts(33_775_000, 8985) .saturating_add(T::DbWeight::get().reads(6_u64)) .saturating_add(T::DbWeight::get().writes(6_u64)) } @@ -387,14 +386,14 @@ impl WeightInfo for SubstrateWeight { fn seal_caller(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `781 + r * (6 ±0)` - // Estimated: `21730 + r * (30 ±0)` - // Minimum execution time: 235_074_000 picoseconds. - Weight::from_parts(243_735_179, 21730) - // Standard Error: 972 - .saturating_add(Weight::from_parts(328_571, 0).saturating_mul(r.into())) + // Estimated: `6722 + r * (6 ±0)` + // Minimum execution time: 234_292_000 picoseconds. + Weight::from_parts(235_941_911, 6722) + // Standard Error: 413 + .saturating_add(Weight::from_parts(339_913, 0).saturating_mul(r.into())) .saturating_add(T::DbWeight::get().reads(6_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) - .saturating_add(Weight::from_parts(0, 30).saturating_mul(r.into())) + .saturating_add(Weight::from_parts(0, 6).saturating_mul(r.into())) } /// Storage: System Account (r:1 w:0) /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: Measured) @@ -410,15 +409,15 @@ impl WeightInfo for SubstrateWeight { fn seal_is_contract(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `839 + r * (240 ±0)` - // Estimated: `21835 + r * (3675 ±0)` - // Minimum execution time: 236_455_000 picoseconds. - Weight::from_parts(81_818_936, 21835) - // Standard Error: 5_874 - .saturating_add(Weight::from_parts(3_316_006, 0).saturating_mul(r.into())) + // Estimated: `6743 + r * (2715 ±0)` + // Minimum execution time: 236_273_000 picoseconds. + Weight::from_parts(74_380_906, 6743) + // Standard Error: 5_745 + .saturating_add(Weight::from_parts(3_331_781, 0).saturating_mul(r.into())) .saturating_add(T::DbWeight::get().reads(6_u64)) .saturating_add(T::DbWeight::get().reads((1_u64).saturating_mul(r.into()))) .saturating_add(T::DbWeight::get().writes(3_u64)) - .saturating_add(Weight::from_parts(0, 3675).saturating_mul(r.into())) + .saturating_add(Weight::from_parts(0, 2715).saturating_mul(r.into())) } /// Storage: System Account (r:1 w:0) /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: Measured) @@ -434,15 +433,15 @@ impl WeightInfo for SubstrateWeight { fn seal_code_hash(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `831 + r * (244 ±0)` - // Estimated: `21855 + r * (3695 ±0)` - // Minimum execution time: 237_724_000 picoseconds. - Weight::from_parts(91_384_964, 21855) - // Standard Error: 5_828 - .saturating_add(Weight::from_parts(4_063_221, 0).saturating_mul(r.into())) + // Estimated: `6747 + r * (2719 ±0)` + // Minimum execution time: 236_573_000 picoseconds. + Weight::from_parts(82_473_906, 6747) + // Standard Error: 5_510 + .saturating_add(Weight::from_parts(4_131_820, 0).saturating_mul(r.into())) .saturating_add(T::DbWeight::get().reads(6_u64)) .saturating_add(T::DbWeight::get().reads((1_u64).saturating_mul(r.into()))) .saturating_add(T::DbWeight::get().writes(3_u64)) - .saturating_add(Weight::from_parts(0, 3695).saturating_mul(r.into())) + .saturating_add(Weight::from_parts(0, 2719).saturating_mul(r.into())) } /// Storage: System Account (r:1 w:0) /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: Measured) @@ -458,14 +457,14 @@ impl WeightInfo for SubstrateWeight { fn seal_own_code_hash(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `788 + r * (6 ±0)` - // Estimated: `21770 + r * (30 ±0)` - // Minimum execution time: 236_144_000 picoseconds. - Weight::from_parts(239_917_873, 21770) - // Standard Error: 629 - .saturating_add(Weight::from_parts(409_752, 0).saturating_mul(r.into())) + // Estimated: `6730 + r * (6 ±0)` + // Minimum execution time: 235_878_000 picoseconds. + Weight::from_parts(238_387_359, 6730) + // Standard Error: 318 + .saturating_add(Weight::from_parts(409_923, 0).saturating_mul(r.into())) .saturating_add(T::DbWeight::get().reads(6_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) - .saturating_add(Weight::from_parts(0, 30).saturating_mul(r.into())) + .saturating_add(Weight::from_parts(0, 6).saturating_mul(r.into())) } /// Storage: System Account (r:1 w:0) /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: Measured) @@ -481,14 +480,14 @@ impl WeightInfo for SubstrateWeight { fn seal_caller_is_origin(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `778 + r * (3 ±0)` - // Estimated: `21735 + r * (15 ±0)` - // Minimum execution time: 232_946_000 picoseconds. - Weight::from_parts(243_163_112, 21735) - // Standard Error: 1_940 - .saturating_add(Weight::from_parts(162_705, 0).saturating_mul(r.into())) + // Estimated: `6723 + r * (3 ±0)` + // Minimum execution time: 233_476_000 picoseconds. + Weight::from_parts(238_014_452, 6723) + // Standard Error: 145 + .saturating_add(Weight::from_parts(165_823, 0).saturating_mul(r.into())) .saturating_add(T::DbWeight::get().reads(6_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) - .saturating_add(Weight::from_parts(0, 15).saturating_mul(r.into())) + .saturating_add(Weight::from_parts(0, 3).saturating_mul(r.into())) } /// Storage: System Account (r:1 w:0) /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: Measured) @@ -504,14 +503,14 @@ impl WeightInfo for SubstrateWeight { fn seal_address(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `782 + r * (6 ±0)` - // Estimated: `21740 + r * (30 ±0)` - // Minimum execution time: 235_301_000 picoseconds. - Weight::from_parts(240_802_312, 21740) - // Standard Error: 5_362 - .saturating_add(Weight::from_parts(339_001, 0).saturating_mul(r.into())) + // Estimated: `6724 + r * (6 ±0)` + // Minimum execution time: 235_490_000 picoseconds. + Weight::from_parts(240_039_685, 6724) + // Standard Error: 330 + .saturating_add(Weight::from_parts(332_291, 0).saturating_mul(r.into())) .saturating_add(T::DbWeight::get().reads(6_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) - .saturating_add(Weight::from_parts(0, 30).saturating_mul(r.into())) + .saturating_add(Weight::from_parts(0, 6).saturating_mul(r.into())) } /// Storage: System Account (r:1 w:0) /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: Measured) @@ -527,14 +526,14 @@ impl WeightInfo for SubstrateWeight { fn seal_gas_left(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `783 + r * (6 ±0)` - // Estimated: `21725 + r * (30 ±0)` - // Minimum execution time: 235_683_000 picoseconds. - Weight::from_parts(237_357_276, 21725) - // Standard Error: 726 - .saturating_add(Weight::from_parts(333_264, 0).saturating_mul(r.into())) + // Estimated: `6721 + r * (6 ±0)` + // Minimum execution time: 236_093_000 picoseconds. + Weight::from_parts(238_513_328, 6721) + // Standard Error: 206 + .saturating_add(Weight::from_parts(328_899, 0).saturating_mul(r.into())) .saturating_add(T::DbWeight::get().reads(6_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) - .saturating_add(Weight::from_parts(0, 30).saturating_mul(r.into())) + .saturating_add(Weight::from_parts(0, 6).saturating_mul(r.into())) } /// Storage: System Account (r:2 w:0) /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: Measured) @@ -550,14 +549,14 @@ impl WeightInfo for SubstrateWeight { fn seal_balance(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `922 + r * (6 ±0)` - // Estimated: `24633 + r * (30 ±0)` - // Minimum execution time: 234_327_000 picoseconds. - Weight::from_parts(239_792_456, 24633) - // Standard Error: 3_460 - .saturating_add(Weight::from_parts(1_496_090, 0).saturating_mul(r.into())) + // Estimated: `6846 + r * (6 ±0)` + // Minimum execution time: 234_801_000 picoseconds. + Weight::from_parts(243_519_159, 6846) + // Standard Error: 1_367 + .saturating_add(Weight::from_parts(1_449_599, 0).saturating_mul(r.into())) .saturating_add(T::DbWeight::get().reads(7_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) - .saturating_add(Weight::from_parts(0, 30).saturating_mul(r.into())) + .saturating_add(Weight::from_parts(0, 6).saturating_mul(r.into())) } /// Storage: System Account (r:1 w:0) /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: Measured) @@ -573,14 +572,14 @@ impl WeightInfo for SubstrateWeight { fn seal_value_transferred(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `792 + r * (6 ±0)` - // Estimated: `21825 + r * (30 ±0)` - // Minimum execution time: 235_466_000 picoseconds. - Weight::from_parts(242_580_658, 21825) - // Standard Error: 1_439 - .saturating_add(Weight::from_parts(323_842, 0).saturating_mul(r.into())) + // Estimated: `6741 + r * (6 ±0)` + // Minimum execution time: 236_765_000 picoseconds. + Weight::from_parts(237_843_244, 6741) + // Standard Error: 308 + .saturating_add(Weight::from_parts(329_911, 0).saturating_mul(r.into())) .saturating_add(T::DbWeight::get().reads(6_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) - .saturating_add(Weight::from_parts(0, 30).saturating_mul(r.into())) + .saturating_add(Weight::from_parts(0, 6).saturating_mul(r.into())) } /// Storage: System Account (r:1 w:0) /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: Measured) @@ -596,14 +595,14 @@ impl WeightInfo for SubstrateWeight { fn seal_minimum_balance(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `790 + r * (6 ±0)` - // Estimated: `21815 + r * (30 ±0)` - // Minimum execution time: 238_529_000 picoseconds. - Weight::from_parts(249_276_722, 21815) - // Standard Error: 1_216 - .saturating_add(Weight::from_parts(328_600, 0).saturating_mul(r.into())) + // Estimated: `6739 + r * (6 ±0)` + // Minimum execution time: 236_690_000 picoseconds. + Weight::from_parts(241_743_164, 6739) + // Standard Error: 333 + .saturating_add(Weight::from_parts(324_693, 0).saturating_mul(r.into())) .saturating_add(T::DbWeight::get().reads(6_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) - .saturating_add(Weight::from_parts(0, 30).saturating_mul(r.into())) + .saturating_add(Weight::from_parts(0, 6).saturating_mul(r.into())) } /// Storage: System Account (r:1 w:0) /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: Measured) @@ -619,14 +618,14 @@ impl WeightInfo for SubstrateWeight { fn seal_block_number(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `787 + r * (6 ±0)` - // Estimated: `21805 + r * (30 ±0)` - // Minimum execution time: 234_360_000 picoseconds. - Weight::from_parts(239_927_876, 21805) - // Standard Error: 541 - .saturating_add(Weight::from_parts(319_553, 0).saturating_mul(r.into())) + // Estimated: `6737 + r * (6 ±0)` + // Minimum execution time: 236_149_000 picoseconds. + Weight::from_parts(239_090_707, 6737) + // Standard Error: 246 + .saturating_add(Weight::from_parts(344_488, 0).saturating_mul(r.into())) .saturating_add(T::DbWeight::get().reads(6_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) - .saturating_add(Weight::from_parts(0, 30).saturating_mul(r.into())) + .saturating_add(Weight::from_parts(0, 6).saturating_mul(r.into())) } /// Storage: System Account (r:1 w:0) /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: Measured) @@ -642,14 +641,14 @@ impl WeightInfo for SubstrateWeight { fn seal_now(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `778 + r * (6 ±0)` - // Estimated: `21735 + r * (30 ±0)` - // Minimum execution time: 234_461_000 picoseconds. - Weight::from_parts(239_682_633, 21735) - // Standard Error: 544 - .saturating_add(Weight::from_parts(322_943, 0).saturating_mul(r.into())) + // Estimated: `6723 + r * (6 ±0)` + // Minimum execution time: 235_057_000 picoseconds. + Weight::from_parts(237_752_870, 6723) + // Standard Error: 236 + .saturating_add(Weight::from_parts(328_235, 0).saturating_mul(r.into())) .saturating_add(T::DbWeight::get().reads(6_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) - .saturating_add(Weight::from_parts(0, 30).saturating_mul(r.into())) + .saturating_add(Weight::from_parts(0, 6).saturating_mul(r.into())) } /// Storage: System Account (r:1 w:0) /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: Measured) @@ -667,14 +666,14 @@ impl WeightInfo for SubstrateWeight { fn seal_weight_to_fee(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `856 + r * (10 ±0)` - // Estimated: `24446 + r * (60 ±0)` - // Minimum execution time: 234_862_000 picoseconds. - Weight::from_parts(252_614_390, 24446) - // Standard Error: 1_180 - .saturating_add(Weight::from_parts(1_313_286, 0).saturating_mul(r.into())) + // Estimated: `6796 + r * (10 ±0)` + // Minimum execution time: 234_995_000 picoseconds. + Weight::from_parts(246_473_554, 6796) + // Standard Error: 1_015 + .saturating_add(Weight::from_parts(1_337_653, 0).saturating_mul(r.into())) .saturating_add(T::DbWeight::get().reads(7_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) - .saturating_add(Weight::from_parts(0, 60).saturating_mul(r.into())) + .saturating_add(Weight::from_parts(0, 10).saturating_mul(r.into())) } /// Storage: System Account (r:1 w:0) /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: Measured) @@ -690,14 +689,14 @@ impl WeightInfo for SubstrateWeight { fn seal_gas(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `745 + r * (4 ±0)` - // Estimated: `21555 + r * (20 ±0)` - // Minimum execution time: 161_003_000 picoseconds. - Weight::from_parts(165_793_675, 21555) - // Standard Error: 323 - .saturating_add(Weight::from_parts(128_941, 0).saturating_mul(r.into())) + // Estimated: `6687 + r * (4 ±0)` + // Minimum execution time: 160_445_000 picoseconds. + Weight::from_parts(165_558_135, 6687) + // Standard Error: 234 + .saturating_add(Weight::from_parts(133_607, 0).saturating_mul(r.into())) .saturating_add(T::DbWeight::get().reads(6_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) - .saturating_add(Weight::from_parts(0, 20).saturating_mul(r.into())) + .saturating_add(Weight::from_parts(0, 4).saturating_mul(r.into())) } /// Storage: System Account (r:1 w:0) /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: Measured) @@ -713,14 +712,14 @@ impl WeightInfo for SubstrateWeight { fn seal_input(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `780 + r * (6 ±0)` - // Estimated: `21740 + r * (30 ±0)` - // Minimum execution time: 235_192_000 picoseconds. - Weight::from_parts(241_225_671, 21740) - // Standard Error: 1_132 - .saturating_add(Weight::from_parts(272_760, 0).saturating_mul(r.into())) + // Estimated: `6724 + r * (6 ±0)` + // Minimum execution time: 235_065_000 picoseconds. + Weight::from_parts(237_797_177, 6724) + // Standard Error: 336 + .saturating_add(Weight::from_parts(267_302, 0).saturating_mul(r.into())) .saturating_add(T::DbWeight::get().reads(6_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) - .saturating_add(Weight::from_parts(0, 30).saturating_mul(r.into())) + .saturating_add(Weight::from_parts(0, 6).saturating_mul(r.into())) } /// Storage: System Account (r:1 w:0) /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: Measured) @@ -736,11 +735,11 @@ impl WeightInfo for SubstrateWeight { fn seal_input_per_byte(n: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `784` - // Estimated: `21740` - // Minimum execution time: 238_050_000 picoseconds. - Weight::from_parts(241_274_832, 21740) - // Standard Error: 1 - .saturating_add(Weight::from_parts(590, 0).saturating_mul(n.into())) + // Estimated: `6724` + // Minimum execution time: 236_215_000 picoseconds. + Weight::from_parts(239_347_313, 6724) + // Standard Error: 0 + .saturating_add(Weight::from_parts(587, 0).saturating_mul(n.into())) .saturating_add(T::DbWeight::get().reads(6_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) } @@ -758,14 +757,14 @@ impl WeightInfo for SubstrateWeight { fn seal_return(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `768 + r * (45 ±0)` - // Estimated: `21660 + r * (225 ±0)` - // Minimum execution time: 231_807_000 picoseconds. - Weight::from_parts(234_264_848, 21660) - // Standard Error: 185_298 - .saturating_add(Weight::from_parts(1_000_351, 0).saturating_mul(r.into())) + // Estimated: `6708 + r * (45 ±0)` + // Minimum execution time: 231_571_000 picoseconds. + Weight::from_parts(233_477_918, 6708) + // Standard Error: 95_776 + .saturating_add(Weight::from_parts(1_733_181, 0).saturating_mul(r.into())) .saturating_add(T::DbWeight::get().reads(6_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) - .saturating_add(Weight::from_parts(0, 225).saturating_mul(r.into())) + .saturating_add(Weight::from_parts(0, 45).saturating_mul(r.into())) } /// Storage: System Account (r:1 w:0) /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: Measured) @@ -781,11 +780,11 @@ impl WeightInfo for SubstrateWeight { fn seal_return_per_byte(n: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `778` - // Estimated: `21775` - // Minimum execution time: 234_347_000 picoseconds. - Weight::from_parts(234_774_467, 21775) + // Estimated: `6731` + // Minimum execution time: 234_956_000 picoseconds. + Weight::from_parts(236_785_051, 6731) // Standard Error: 0 - .saturating_add(Weight::from_parts(183, 0).saturating_mul(n.into())) + .saturating_add(Weight::from_parts(177, 0).saturating_mul(n.into())) .saturating_add(T::DbWeight::get().reads(6_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) } @@ -809,16 +808,16 @@ impl WeightInfo for SubstrateWeight { fn seal_terminate(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `810 + r * (356 ±0)` - // Estimated: `26094 + r * (15904 ±0)` - // Minimum execution time: 234_356_000 picoseconds. - Weight::from_parts(236_844_659, 26094) - // Standard Error: 161_035 - .saturating_add(Weight::from_parts(110_725_340, 0).saturating_mul(r.into())) + // Estimated: `6750 + r * (7781 ±0)` + // Minimum execution time: 234_275_000 picoseconds. + Weight::from_parts(236_776_769, 6750) + // Standard Error: 137_203 + .saturating_add(Weight::from_parts(110_758_930, 0).saturating_mul(r.into())) .saturating_add(T::DbWeight::get().reads(6_u64)) .saturating_add(T::DbWeight::get().reads((6_u64).saturating_mul(r.into()))) .saturating_add(T::DbWeight::get().writes(3_u64)) .saturating_add(T::DbWeight::get().writes((8_u64).saturating_mul(r.into()))) - .saturating_add(Weight::from_parts(0, 15904).saturating_mul(r.into())) + .saturating_add(Weight::from_parts(0, 7781).saturating_mul(r.into())) } /// Storage: System Account (r:1 w:0) /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: Measured) @@ -836,14 +835,14 @@ impl WeightInfo for SubstrateWeight { fn seal_random(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `825 + r * (10 ±0)` - // Estimated: `24283 + r * (60 ±0)` - // Minimum execution time: 234_248_000 picoseconds. - Weight::from_parts(255_712_101, 24283) - // Standard Error: 1_474 - .saturating_add(Weight::from_parts(1_753_943, 0).saturating_mul(r.into())) + // Estimated: `6769 + r * (10 ±0)` + // Minimum execution time: 235_593_000 picoseconds. + Weight::from_parts(253_731_242, 6769) + // Standard Error: 2_129 + .saturating_add(Weight::from_parts(1_771_297, 0).saturating_mul(r.into())) .saturating_add(T::DbWeight::get().reads(7_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) - .saturating_add(Weight::from_parts(0, 60).saturating_mul(r.into())) + .saturating_add(Weight::from_parts(0, 10).saturating_mul(r.into())) } /// Storage: System Account (r:1 w:0) /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: Measured) @@ -859,14 +858,14 @@ impl WeightInfo for SubstrateWeight { fn seal_deposit_event(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `778 + r * (10 ±0)` - // Estimated: `21735 + r * (50 ±0)` - // Minimum execution time: 232_456_000 picoseconds. - Weight::from_parts(230_738_907, 21735) - // Standard Error: 4_163 - .saturating_add(Weight::from_parts(3_496_817, 0).saturating_mul(r.into())) + // Estimated: `6723 + r * (10 ±0)` + // Minimum execution time: 232_124_000 picoseconds. + Weight::from_parts(245_904_447, 6723) + // Standard Error: 2_185 + .saturating_add(Weight::from_parts(3_470_410, 0).saturating_mul(r.into())) .saturating_add(T::DbWeight::get().reads(6_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) - .saturating_add(Weight::from_parts(0, 50).saturating_mul(r.into())) + .saturating_add(Weight::from_parts(0, 10).saturating_mul(r.into())) } /// Storage: System Account (r:1 w:0) /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: Measured) @@ -883,18 +882,18 @@ impl WeightInfo for SubstrateWeight { fn seal_deposit_event_per_topic_and_byte(t: u32, n: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `797 + t * (32 ±0)` - // Estimated: `21840 + t * (2640 ±0)` - // Minimum execution time: 251_638_000 picoseconds. - Weight::from_parts(244_791_817, 21840) - // Standard Error: 89_537 - .saturating_add(Weight::from_parts(2_459_492, 0).saturating_mul(t.into())) - // Standard Error: 25 - .saturating_add(Weight::from_parts(626, 0).saturating_mul(n.into())) + // Estimated: `6744 + t * (2508 ±0)` + // Minimum execution time: 250_301_000 picoseconds. + Weight::from_parts(245_292_258, 6744) + // Standard Error: 29_864 + .saturating_add(Weight::from_parts(2_163_531, 0).saturating_mul(t.into())) + // Standard Error: 8 + .saturating_add(Weight::from_parts(583, 0).saturating_mul(n.into())) .saturating_add(T::DbWeight::get().reads(6_u64)) .saturating_add(T::DbWeight::get().reads((1_u64).saturating_mul(t.into()))) .saturating_add(T::DbWeight::get().writes(3_u64)) .saturating_add(T::DbWeight::get().writes((1_u64).saturating_mul(t.into()))) - .saturating_add(Weight::from_parts(0, 2640).saturating_mul(t.into())) + .saturating_add(Weight::from_parts(0, 2508).saturating_mul(t.into())) } /// Storage: System Account (r:1 w:0) /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: Measured) @@ -910,14 +909,14 @@ impl WeightInfo for SubstrateWeight { fn seal_debug_message(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `777 + r * (7 ±0)` - // Estimated: `21725 + r * (35 ±0)` - // Minimum execution time: 164_736_000 picoseconds. - Weight::from_parts(164_496_597, 21725) - // Standard Error: 4_619 - .saturating_add(Weight::from_parts(252_013, 0).saturating_mul(r.into())) + // Estimated: `6721 + r * (7 ±0)` + // Minimum execution time: 165_711_000 picoseconds. + Weight::from_parts(168_792_571, 6721) + // Standard Error: 216 + .saturating_add(Weight::from_parts(230_285, 0).saturating_mul(r.into())) .saturating_add(T::DbWeight::get().reads(6_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) - .saturating_add(Weight::from_parts(0, 35).saturating_mul(r.into())) + .saturating_add(Weight::from_parts(0, 7).saturating_mul(r.into())) } /// Storage: System Account (r:1 w:0) /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) @@ -933,11 +932,11 @@ impl WeightInfo for SubstrateWeight { fn seal_debug_message_per_byte(i: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `125728` - // Estimated: `269977` - // Minimum execution time: 352_146_000 picoseconds. - Weight::from_parts(357_758_251, 269977) - // Standard Error: 1 - .saturating_add(Weight::from_parts(735, 0).saturating_mul(i.into())) + // Estimated: `131670` + // Minimum execution time: 348_928_000 picoseconds. + Weight::from_parts(352_224_793, 131670) + // Standard Error: 0 + .saturating_add(Weight::from_parts(731, 0).saturating_mul(i.into())) .saturating_add(T::DbWeight::get().reads(6_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) } @@ -948,10 +947,10 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `845 + r * (292 ±0)` // Estimated: `843 + r * (293 ±0)` - // Minimum execution time: 236_170_000 picoseconds. - Weight::from_parts(136_284_582, 843) - // Standard Error: 10_269 - .saturating_add(Weight::from_parts(5_995_228, 0).saturating_mul(r.into())) + // Minimum execution time: 236_418_000 picoseconds. + Weight::from_parts(129_862_840, 843) + // Standard Error: 9_733 + .saturating_add(Weight::from_parts(6_005_187, 0).saturating_mul(r.into())) .saturating_add(T::DbWeight::get().reads(6_u64)) .saturating_add(T::DbWeight::get().reads((1_u64).saturating_mul(r.into()))) .saturating_add(T::DbWeight::get().writes(3_u64)) @@ -965,10 +964,10 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `1304` // Estimated: `1280` - // Minimum execution time: 253_837_000 picoseconds. - Weight::from_parts(287_029_261, 1280) - // Standard Error: 51 - .saturating_add(Weight::from_parts(534, 0).saturating_mul(n.into())) + // Minimum execution time: 251_599_000 picoseconds. + Weight::from_parts(285_284_665, 1280) + // Standard Error: 46 + .saturating_add(Weight::from_parts(410, 0).saturating_mul(n.into())) .saturating_add(T::DbWeight::get().reads(8_u64)) .saturating_add(T::DbWeight::get().writes(6_u64)) } @@ -979,10 +978,10 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `1167 + n * (1 ±0)` // Estimated: `1167 + n * (1 ±0)` - // Minimum execution time: 253_037_000 picoseconds. - Weight::from_parts(256_401_533, 1167) - // Standard Error: 18 - .saturating_add(Weight::from_parts(32, 0).saturating_mul(n.into())) + // Minimum execution time: 251_309_000 picoseconds. + Weight::from_parts(253_555_552, 1167) + // Standard Error: 9 + .saturating_add(Weight::from_parts(27, 0).saturating_mul(n.into())) .saturating_add(T::DbWeight::get().reads(7_u64)) .saturating_add(T::DbWeight::get().writes(4_u64)) .saturating_add(Weight::from_parts(0, 1).saturating_mul(n.into())) @@ -994,10 +993,10 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `841 + r * (288 ±0)` // Estimated: `845 + r * (289 ±0)` - // Minimum execution time: 236_800_000 picoseconds. - Weight::from_parts(134_748_031, 845) - // Standard Error: 9_560 - .saturating_add(Weight::from_parts(5_923_187, 0).saturating_mul(r.into())) + // Minimum execution time: 235_441_000 picoseconds. + Weight::from_parts(132_980_942, 845) + // Standard Error: 9_421 + .saturating_add(Weight::from_parts(5_854_896, 0).saturating_mul(r.into())) .saturating_add(T::DbWeight::get().reads(6_u64)) .saturating_add(T::DbWeight::get().reads((1_u64).saturating_mul(r.into()))) .saturating_add(T::DbWeight::get().writes(3_u64)) @@ -1011,9 +1010,9 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `1163 + n * (1 ±0)` // Estimated: `1163 + n * (1 ±0)` - // Minimum execution time: 250_601_000 picoseconds. - Weight::from_parts(253_618_803, 1163) - // Standard Error: 18 + // Minimum execution time: 249_967_000 picoseconds. + Weight::from_parts(252_122_186, 1163) + // Standard Error: 10 .saturating_add(Weight::from_parts(74, 0).saturating_mul(n.into())) .saturating_add(T::DbWeight::get().reads(7_u64)) .saturating_add(T::DbWeight::get().writes(4_u64)) @@ -1026,10 +1025,10 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `835 + r * (296 ±0)` // Estimated: `840 + r * (297 ±0)` - // Minimum execution time: 236_194_000 picoseconds. - Weight::from_parts(152_630_909, 840) - // Standard Error: 8_573 - .saturating_add(Weight::from_parts(4_896_099, 0).saturating_mul(r.into())) + // Minimum execution time: 235_647_000 picoseconds. + Weight::from_parts(145_525_169, 840) + // Standard Error: 8_553 + .saturating_add(Weight::from_parts(4_948_021, 0).saturating_mul(r.into())) .saturating_add(T::DbWeight::get().reads(6_u64)) .saturating_add(T::DbWeight::get().reads((1_u64).saturating_mul(r.into()))) .saturating_add(T::DbWeight::get().writes(3_u64)) @@ -1042,10 +1041,10 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `1179 + n * (1 ±0)` // Estimated: `1179 + n * (1 ±0)` - // Minimum execution time: 250_989_000 picoseconds. - Weight::from_parts(260_170_747, 1179) - // Standard Error: 162 - .saturating_add(Weight::from_parts(224, 0).saturating_mul(n.into())) + // Minimum execution time: 249_576_000 picoseconds. + Weight::from_parts(250_747_191, 1179) + // Standard Error: 8 + .saturating_add(Weight::from_parts(717, 0).saturating_mul(n.into())) .saturating_add(T::DbWeight::get().reads(7_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) .saturating_add(Weight::from_parts(0, 1).saturating_mul(n.into())) @@ -1057,10 +1056,10 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `856 + r * (288 ±0)` // Estimated: `857 + r * (289 ±0)` - // Minimum execution time: 236_574_000 picoseconds. - Weight::from_parts(155_573_955, 857) - // Standard Error: 10_270 - .saturating_add(Weight::from_parts(4_667_644, 0).saturating_mul(r.into())) + // Minimum execution time: 236_110_000 picoseconds. + Weight::from_parts(148_420_625, 857) + // Standard Error: 8_175 + .saturating_add(Weight::from_parts(4_684_126, 0).saturating_mul(r.into())) .saturating_add(T::DbWeight::get().reads(6_u64)) .saturating_add(T::DbWeight::get().reads((1_u64).saturating_mul(r.into()))) .saturating_add(T::DbWeight::get().writes(3_u64)) @@ -1073,10 +1072,10 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `1166 + n * (1 ±0)` // Estimated: `1166 + n * (1 ±0)` - // Minimum execution time: 249_502_000 picoseconds. - Weight::from_parts(251_496_311, 1166) - // Standard Error: 26 - .saturating_add(Weight::from_parts(186, 0).saturating_mul(n.into())) + // Minimum execution time: 247_800_000 picoseconds. + Weight::from_parts(249_410_575, 1166) + // Standard Error: 6 + .saturating_add(Weight::from_parts(99, 0).saturating_mul(n.into())) .saturating_add(T::DbWeight::get().reads(7_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) .saturating_add(Weight::from_parts(0, 1).saturating_mul(n.into())) @@ -1088,10 +1087,10 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `829 + r * (296 ±0)` // Estimated: `836 + r * (297 ±0)` - // Minimum execution time: 236_030_000 picoseconds. - Weight::from_parts(128_595_856, 836) - // Standard Error: 10_530 - .saturating_add(Weight::from_parts(6_072_638, 0).saturating_mul(r.into())) + // Minimum execution time: 235_251_000 picoseconds. + Weight::from_parts(128_816_707, 836) + // Standard Error: 9_887 + .saturating_add(Weight::from_parts(6_167_176, 0).saturating_mul(r.into())) .saturating_add(T::DbWeight::get().reads(6_u64)) .saturating_add(T::DbWeight::get().reads((1_u64).saturating_mul(r.into()))) .saturating_add(T::DbWeight::get().writes(3_u64)) @@ -1105,10 +1104,10 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `1180 + n * (1 ±0)` // Estimated: `1180 + n * (1 ±0)` - // Minimum execution time: 254_027_000 picoseconds. - Weight::from_parts(260_739_475, 1180) - // Standard Error: 115 - .saturating_add(Weight::from_parts(330, 0).saturating_mul(n.into())) + // Minimum execution time: 250_401_000 picoseconds. + Weight::from_parts(253_298_243, 1180) + // Standard Error: 9 + .saturating_add(Weight::from_parts(667, 0).saturating_mul(n.into())) .saturating_add(T::DbWeight::get().reads(7_u64)) .saturating_add(T::DbWeight::get().writes(4_u64)) .saturating_add(Weight::from_parts(0, 1).saturating_mul(n.into())) @@ -1127,16 +1126,16 @@ impl WeightInfo for SubstrateWeight { fn seal_transfer(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `1373 + r * (45 ±0)` - // Estimated: `26753 + r * (2700 ±0)` - // Minimum execution time: 237_634_000 picoseconds. - Weight::from_parts(238_177_000, 26753) - // Standard Error: 23_320 - .saturating_add(Weight::from_parts(35_467_618, 0).saturating_mul(r.into())) + // Estimated: `7270 + r * (2520 ±0)` + // Minimum execution time: 236_470_000 picoseconds. + Weight::from_parts(98_898_727, 7270) + // Standard Error: 33_316 + .saturating_add(Weight::from_parts(35_149_946, 0).saturating_mul(r.into())) .saturating_add(T::DbWeight::get().reads(7_u64)) .saturating_add(T::DbWeight::get().reads((1_u64).saturating_mul(r.into()))) .saturating_add(T::DbWeight::get().writes(4_u64)) .saturating_add(T::DbWeight::get().writes((1_u64).saturating_mul(r.into()))) - .saturating_add(Weight::from_parts(0, 2700).saturating_mul(r.into())) + .saturating_add(Weight::from_parts(0, 2520).saturating_mul(r.into())) } /// Storage: System Account (r:1 w:0) /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: Measured) @@ -1152,16 +1151,16 @@ impl WeightInfo for SubstrateWeight { fn seal_call(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `1237 + r * (256 ±0)` - // Estimated: `26028 + r * (6235 ±0)` - // Minimum execution time: 237_076_000 picoseconds. - Weight::from_parts(237_792_000, 26028) - // Standard Error: 91_566 - .saturating_add(Weight::from_parts(212_893_975, 0).saturating_mul(r.into())) + // Estimated: `7125 + r * (2732 ±0)` + // Minimum execution time: 238_303_000 picoseconds. + Weight::from_parts(239_024_000, 7125) + // Standard Error: 65_907 + .saturating_add(Weight::from_parts(209_419_071, 0).saturating_mul(r.into())) .saturating_add(T::DbWeight::get().reads(7_u64)) .saturating_add(T::DbWeight::get().reads((2_u64).saturating_mul(r.into()))) .saturating_add(T::DbWeight::get().writes(3_u64)) .saturating_add(T::DbWeight::get().writes((2_u64).saturating_mul(r.into()))) - .saturating_add(Weight::from_parts(0, 6235).saturating_mul(r.into())) + .saturating_add(Weight::from_parts(0, 2732).saturating_mul(r.into())) } /// Storage: System Account (r:1 w:0) /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: Measured) @@ -1177,16 +1176,16 @@ impl WeightInfo for SubstrateWeight { fn seal_delegate_call(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0 + r * (502 ±0)` - // Estimated: `21755 + r * (6329 ±3)` - // Minimum execution time: 236_275_000 picoseconds. - Weight::from_parts(236_849_000, 21755) - // Standard Error: 92_724 - .saturating_add(Weight::from_parts(207_159_396, 0).saturating_mul(r.into())) + // Estimated: `6727 + r * (2572 ±10)` + // Minimum execution time: 235_961_000 picoseconds. + Weight::from_parts(236_939_000, 6727) + // Standard Error: 83_087 + .saturating_add(Weight::from_parts(205_646_517, 0).saturating_mul(r.into())) .saturating_add(T::DbWeight::get().reads(6_u64)) .saturating_add(T::DbWeight::get().reads((2_u64).saturating_mul(r.into()))) .saturating_add(T::DbWeight::get().writes(3_u64)) .saturating_add(T::DbWeight::get().writes((1_u64).saturating_mul(r.into()))) - .saturating_add(Weight::from_parts(0, 6329).saturating_mul(r.into())) + .saturating_add(Weight::from_parts(0, 2572).saturating_mul(r.into())) } /// Storage: System Account (r:3 w:2) /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: Measured) @@ -1203,18 +1202,18 @@ impl WeightInfo for SubstrateWeight { fn seal_call_per_transfer_clone_byte(t: u32, c: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `1154 + t * (204 ±0)` - // Estimated: `31015 + t * (5970 ±0)` - // Minimum execution time: 412_628_000 picoseconds. - Weight::from_parts(385_108_035, 31015) - // Standard Error: 1_002_078 - .saturating_add(Weight::from_parts(31_204_678, 0).saturating_mul(t.into())) - // Standard Error: 1 - .saturating_add(Weight::from_parts(598, 0).saturating_mul(c.into())) + // Estimated: `9569 + t * (5154 ±0)` + // Minimum execution time: 410_156_000 picoseconds. + Weight::from_parts(378_378_143, 9569) + // Standard Error: 285_172 + .saturating_add(Weight::from_parts(34_736_740, 0).saturating_mul(t.into())) + // Standard Error: 0 + .saturating_add(Weight::from_parts(591, 0).saturating_mul(c.into())) .saturating_add(T::DbWeight::get().reads(9_u64)) .saturating_add(T::DbWeight::get().reads((2_u64).saturating_mul(t.into()))) .saturating_add(T::DbWeight::get().writes(5_u64)) .saturating_add(T::DbWeight::get().writes((2_u64).saturating_mul(t.into()))) - .saturating_add(Weight::from_parts(0, 5970).saturating_mul(t.into())) + .saturating_add(Weight::from_parts(0, 5154).saturating_mul(t.into())) } /// Storage: System Account (r:1602 w:1602) /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: Measured) @@ -1234,16 +1233,16 @@ impl WeightInfo for SubstrateWeight { fn seal_instantiate(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `1301 + r * (254 ±0)` - // Estimated: `30977 + r * (16635 ±0)` - // Minimum execution time: 237_161_000 picoseconds. - Weight::from_parts(237_620_000, 30977) - // Standard Error: 258_036 - .saturating_add(Weight::from_parts(344_869_637, 0).saturating_mul(r.into())) + // Estimated: `7131 + r * (5205 ±0)` + // Minimum execution time: 236_748_000 picoseconds. + Weight::from_parts(237_129_000, 7131) + // Standard Error: 280_059 + .saturating_add(Weight::from_parts(341_428_013, 0).saturating_mul(r.into())) .saturating_add(T::DbWeight::get().reads(8_u64)) .saturating_add(T::DbWeight::get().reads((6_u64).saturating_mul(r.into()))) .saturating_add(T::DbWeight::get().writes(6_u64)) .saturating_add(T::DbWeight::get().writes((5_u64).saturating_mul(r.into()))) - .saturating_add(Weight::from_parts(0, 16635).saturating_mul(r.into())) + .saturating_add(Weight::from_parts(0, 5205).saturating_mul(r.into())) } /// Storage: System Account (r:4 w:4) /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: Measured) @@ -1265,20 +1264,20 @@ impl WeightInfo for SubstrateWeight { fn seal_instantiate_per_transfer_input_salt_byte(t: u32, i: u32, s: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `1071 + t * (187 ±0)` - // Estimated: `42684 + t * (3588 ±2)` - // Minimum execution time: 1_613_172_000 picoseconds. - Weight::from_parts(326_952_137, 42684) - // Standard Error: 4_738_925 - .saturating_add(Weight::from_parts(113_657_996, 0).saturating_mul(t.into())) - // Standard Error: 7 - .saturating_add(Weight::from_parts(1_173, 0).saturating_mul(i.into())) - // Standard Error: 7 - .saturating_add(Weight::from_parts(1_347, 0).saturating_mul(s.into())) + // Estimated: `9492 + t * (2634 ±2)` + // Minimum execution time: 1_613_796_000 picoseconds. + Weight::from_parts(340_002_206, 9492) + // Standard Error: 4_296_381 + .saturating_add(Weight::from_parts(115_239_834, 0).saturating_mul(t.into())) + // Standard Error: 6 + .saturating_add(Weight::from_parts(1_145, 0).saturating_mul(i.into())) + // Standard Error: 6 + .saturating_add(Weight::from_parts(1_315, 0).saturating_mul(s.into())) .saturating_add(T::DbWeight::get().reads(13_u64)) .saturating_add(T::DbWeight::get().reads((1_u64).saturating_mul(t.into()))) .saturating_add(T::DbWeight::get().writes(10_u64)) .saturating_add(T::DbWeight::get().writes((1_u64).saturating_mul(t.into()))) - .saturating_add(Weight::from_parts(0, 3588).saturating_mul(t.into())) + .saturating_add(Weight::from_parts(0, 2634).saturating_mul(t.into())) } /// Storage: System Account (r:1 w:0) /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: Measured) @@ -1294,14 +1293,14 @@ impl WeightInfo for SubstrateWeight { fn seal_hash_sha2_256(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `777 + r * (8 ±0)` - // Estimated: `21710 + r * (40 ±0)` - // Minimum execution time: 233_679_000 picoseconds. - Weight::from_parts(238_638_820, 21710) - // Standard Error: 681 - .saturating_add(Weight::from_parts(573_320, 0).saturating_mul(r.into())) + // Estimated: `6718 + r * (8 ±0)` + // Minimum execution time: 233_111_000 picoseconds. + Weight::from_parts(238_643_933, 6718) + // Standard Error: 184 + .saturating_add(Weight::from_parts(572_296, 0).saturating_mul(r.into())) .saturating_add(T::DbWeight::get().reads(6_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) - .saturating_add(Weight::from_parts(0, 40).saturating_mul(r.into())) + .saturating_add(Weight::from_parts(0, 8).saturating_mul(r.into())) } /// Storage: System Account (r:1 w:0) /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: Measured) @@ -1317,11 +1316,11 @@ impl WeightInfo for SubstrateWeight { fn seal_hash_sha2_256_per_byte(n: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `785` - // Estimated: `21745` - // Minimum execution time: 235_992_000 picoseconds. - Weight::from_parts(219_924_252, 21745) - // Standard Error: 5 - .saturating_add(Weight::from_parts(3_978, 0).saturating_mul(n.into())) + // Estimated: `6725` + // Minimum execution time: 234_746_000 picoseconds. + Weight::from_parts(229_815_552, 6725) + // Standard Error: 1 + .saturating_add(Weight::from_parts(3_892, 0).saturating_mul(n.into())) .saturating_add(T::DbWeight::get().reads(6_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) } @@ -1339,14 +1338,14 @@ impl WeightInfo for SubstrateWeight { fn seal_hash_keccak_256(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `779 + r * (8 ±0)` - // Estimated: `21725 + r * (40 ±0)` - // Minimum execution time: 240_661_000 picoseconds. - Weight::from_parts(238_809_422, 21725) - // Standard Error: 820 - .saturating_add(Weight::from_parts(741_326, 0).saturating_mul(r.into())) + // Estimated: `6721 + r * (8 ±0)` + // Minimum execution time: 232_732_000 picoseconds. + Weight::from_parts(239_007_209, 6721) + // Standard Error: 256 + .saturating_add(Weight::from_parts(733_879, 0).saturating_mul(r.into())) .saturating_add(T::DbWeight::get().reads(6_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) - .saturating_add(Weight::from_parts(0, 40).saturating_mul(r.into())) + .saturating_add(Weight::from_parts(0, 8).saturating_mul(r.into())) } /// Storage: System Account (r:1 w:0) /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: Measured) @@ -1362,11 +1361,11 @@ impl WeightInfo for SubstrateWeight { fn seal_hash_keccak_256_per_byte(n: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `787` - // Estimated: `21765` - // Minimum execution time: 235_136_000 picoseconds. - Weight::from_parts(228_678_487, 21765) - // Standard Error: 2 - .saturating_add(Weight::from_parts(3_164, 0).saturating_mul(n.into())) + // Estimated: `6729` + // Minimum execution time: 234_184_000 picoseconds. + Weight::from_parts(227_603_375, 6729) + // Standard Error: 1 + .saturating_add(Weight::from_parts(3_127, 0).saturating_mul(n.into())) .saturating_add(T::DbWeight::get().reads(6_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) } @@ -1384,14 +1383,14 @@ impl WeightInfo for SubstrateWeight { fn seal_hash_blake2_256(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `779 + r * (8 ±0)` - // Estimated: `21740 + r * (40 ±0)` - // Minimum execution time: 233_364_000 picoseconds. - Weight::from_parts(243_294_285, 21740) - // Standard Error: 1_607 - .saturating_add(Weight::from_parts(413_186, 0).saturating_mul(r.into())) + // Estimated: `6724 + r * (8 ±0)` + // Minimum execution time: 233_038_000 picoseconds. + Weight::from_parts(238_515_817, 6724) + // Standard Error: 255 + .saturating_add(Weight::from_parts(413_343, 0).saturating_mul(r.into())) .saturating_add(T::DbWeight::get().reads(6_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) - .saturating_add(Weight::from_parts(0, 40).saturating_mul(r.into())) + .saturating_add(Weight::from_parts(0, 8).saturating_mul(r.into())) } /// Storage: System Account (r:1 w:0) /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: Measured) @@ -1407,11 +1406,11 @@ impl WeightInfo for SubstrateWeight { fn seal_hash_blake2_256_per_byte(n: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `787` - // Estimated: `21785` - // Minimum execution time: 234_315_000 picoseconds. - Weight::from_parts(225_569_537, 21785) + // Estimated: `6733` + // Minimum execution time: 232_996_000 picoseconds. + Weight::from_parts(226_706_997, 6733) // Standard Error: 1 - .saturating_add(Weight::from_parts(919, 0).saturating_mul(n.into())) + .saturating_add(Weight::from_parts(908, 0).saturating_mul(n.into())) .saturating_add(T::DbWeight::get().reads(6_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) } @@ -1429,14 +1428,14 @@ impl WeightInfo for SubstrateWeight { fn seal_hash_blake2_128(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `779 + r * (8 ±0)` - // Estimated: `21745 + r * (40 ±0)` - // Minimum execution time: 233_288_000 picoseconds. - Weight::from_parts(239_289_154, 21745) - // Standard Error: 612 - .saturating_add(Weight::from_parts(414_275, 0).saturating_mul(r.into())) + // Estimated: `6725 + r * (8 ±0)` + // Minimum execution time: 232_292_000 picoseconds. + Weight::from_parts(237_997_001, 6725) + // Standard Error: 219 + .saturating_add(Weight::from_parts(410_177, 0).saturating_mul(r.into())) .saturating_add(T::DbWeight::get().reads(6_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) - .saturating_add(Weight::from_parts(0, 40).saturating_mul(r.into())) + .saturating_add(Weight::from_parts(0, 8).saturating_mul(r.into())) } /// Storage: System Account (r:1 w:0) /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: Measured) @@ -1452,11 +1451,11 @@ impl WeightInfo for SubstrateWeight { fn seal_hash_blake2_128_per_byte(n: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `787` - // Estimated: `21755` - // Minimum execution time: 233_686_000 picoseconds. - Weight::from_parts(227_406_694, 21755) + // Estimated: `6727` + // Minimum execution time: 234_815_000 picoseconds. + Weight::from_parts(226_317_150, 6727) // Standard Error: 1 - .saturating_add(Weight::from_parts(919, 0).saturating_mul(n.into())) + .saturating_add(Weight::from_parts(911, 0).saturating_mul(n.into())) .saturating_add(T::DbWeight::get().reads(6_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) } @@ -1473,15 +1472,15 @@ impl WeightInfo for SubstrateWeight { /// The range of component `n` is `[0, 125697]`. fn seal_sr25519_verify_per_byte(n: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `916 + n * (1 ±0)` - // Estimated: `22375 + n * (5 ±0)` - // Minimum execution time: 287_258_000 picoseconds. - Weight::from_parts(290_941_609, 22375) - // Standard Error: 8 - .saturating_add(Weight::from_parts(4_785, 0).saturating_mul(n.into())) + // Measured: `912 + n * (1 ±0)` + // Estimated: `6848 + n * (1 ±0)` + // Minimum execution time: 286_323_000 picoseconds. + Weight::from_parts(290_287_955, 6848) + // Standard Error: 1 + .saturating_add(Weight::from_parts(4_693, 0).saturating_mul(n.into())) .saturating_add(T::DbWeight::get().reads(6_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) - .saturating_add(Weight::from_parts(0, 5).saturating_mul(n.into())) + .saturating_add(Weight::from_parts(0, 1).saturating_mul(n.into())) } /// Storage: System Account (r:1 w:0) /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: Measured) @@ -1496,15 +1495,15 @@ impl WeightInfo for SubstrateWeight { /// The range of component `r` is `[0, 160]`. fn seal_sr25519_verify(r: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `728 + r * (112 ±0)` - // Estimated: `21455 + r * (560 ±0)` - // Minimum execution time: 238_340_000 picoseconds. - Weight::from_parts(246_009_851, 21455) - // Standard Error: 21_677 - .saturating_add(Weight::from_parts(48_139_487, 0).saturating_mul(r.into())) + // Measured: `727 + r * (112 ±0)` + // Estimated: `6666 + r * (112 ±0)` + // Minimum execution time: 235_938_000 picoseconds. + Weight::from_parts(242_728_358, 6666) + // Standard Error: 9_725 + .saturating_add(Weight::from_parts(47_527_740, 0).saturating_mul(r.into())) .saturating_add(T::DbWeight::get().reads(6_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) - .saturating_add(Weight::from_parts(0, 560).saturating_mul(r.into())) + .saturating_add(Weight::from_parts(0, 112).saturating_mul(r.into())) } /// Storage: System Account (r:1 w:0) /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: Measured) @@ -1520,14 +1519,14 @@ impl WeightInfo for SubstrateWeight { fn seal_ecdsa_recover(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `822 + r * (76 ±0)` - // Estimated: `21705 + r * (385 ±0)` - // Minimum execution time: 236_244_000 picoseconds. - Weight::from_parts(253_749_242, 21705) - // Standard Error: 19_216 - .saturating_add(Weight::from_parts(37_706_782, 0).saturating_mul(r.into())) + // Estimated: `6716 + r * (77 ±0)` + // Minimum execution time: 236_108_000 picoseconds. + Weight::from_parts(248_577_226, 6716) + // Standard Error: 9_565 + .saturating_add(Weight::from_parts(36_733_552, 0).saturating_mul(r.into())) .saturating_add(T::DbWeight::get().reads(6_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) - .saturating_add(Weight::from_parts(0, 385).saturating_mul(r.into())) + .saturating_add(Weight::from_parts(0, 77).saturating_mul(r.into())) } /// Storage: System Account (r:1 w:0) /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: Measured) @@ -1543,14 +1542,14 @@ impl WeightInfo for SubstrateWeight { fn seal_ecdsa_to_eth_address(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `792 + r * (42 ±0)` - // Estimated: `21775 + r * (210 ±0)` - // Minimum execution time: 235_770_000 picoseconds. - Weight::from_parts(230_780_347, 21775) - // Standard Error: 12_015 - .saturating_add(Weight::from_parts(9_584_947, 0).saturating_mul(r.into())) + // Estimated: `6731 + r * (42 ±0)` + // Minimum execution time: 236_440_000 picoseconds. + Weight::from_parts(240_771_418, 6731) + // Standard Error: 1_849 + .saturating_add(Weight::from_parts(9_185_896, 0).saturating_mul(r.into())) .saturating_add(T::DbWeight::get().reads(6_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) - .saturating_add(Weight::from_parts(0, 210).saturating_mul(r.into())) + .saturating_add(Weight::from_parts(0, 42).saturating_mul(r.into())) } /// Storage: System Account (r:1 w:0) /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: Measured) @@ -1568,16 +1567,16 @@ impl WeightInfo for SubstrateWeight { fn seal_set_code_hash(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0 + r * (964 ±0)` - // Estimated: `29920 + r * (11544 ±7)` - // Minimum execution time: 235_857_000 picoseconds. - Weight::from_parts(236_482_000, 29920) - // Standard Error: 47_818 - .saturating_add(Weight::from_parts(21_765_273, 0).saturating_mul(r.into())) + // Estimated: `8190 + r * (3090 ±10)` + // Minimum execution time: 235_056_000 picoseconds. + Weight::from_parts(235_743_000, 8190) + // Standard Error: 46_122 + .saturating_add(Weight::from_parts(21_447_984, 0).saturating_mul(r.into())) .saturating_add(T::DbWeight::get().reads(6_u64)) .saturating_add(T::DbWeight::get().reads((3_u64).saturating_mul(r.into()))) .saturating_add(T::DbWeight::get().writes(3_u64)) .saturating_add(T::DbWeight::get().writes((2_u64).saturating_mul(r.into()))) - .saturating_add(Weight::from_parts(0, 11544).saturating_mul(r.into())) + .saturating_add(Weight::from_parts(0, 3090).saturating_mul(r.into())) } /// Storage: System Account (r:1 w:0) /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: Measured) @@ -1593,14 +1592,14 @@ impl WeightInfo for SubstrateWeight { fn seal_reentrance_count(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `773 + r * (3 ±0)` - // Estimated: `21735 + r * (15 ±0)` - // Minimum execution time: 235_388_000 picoseconds. - Weight::from_parts(240_149_512, 21735) - // Standard Error: 555 - .saturating_add(Weight::from_parts(162_666, 0).saturating_mul(r.into())) + // Estimated: `6723 + r * (3 ±0)` + // Minimum execution time: 235_213_000 picoseconds. + Weight::from_parts(239_456_464, 6723) + // Standard Error: 130 + .saturating_add(Weight::from_parts(159_851, 0).saturating_mul(r.into())) .saturating_add(T::DbWeight::get().reads(6_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) - .saturating_add(Weight::from_parts(0, 15).saturating_mul(r.into())) + .saturating_add(Weight::from_parts(0, 3).saturating_mul(r.into())) } /// Storage: System Account (r:1 w:0) /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: Measured) @@ -1616,14 +1615,14 @@ impl WeightInfo for SubstrateWeight { fn seal_account_reentrance_count(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `1975 + r * (39 ±0)` - // Estimated: `27145 + r * (200 ±0)` - // Minimum execution time: 237_485_000 picoseconds. - Weight::from_parts(268_044_053, 27145) - // Standard Error: 1_147 - .saturating_add(Weight::from_parts(260_572, 0).saturating_mul(r.into())) + // Estimated: `7805 + r * (40 ±0)` + // Minimum execution time: 237_886_000 picoseconds. + Weight::from_parts(262_430_157, 7805) + // Standard Error: 939 + .saturating_add(Weight::from_parts(260_005, 0).saturating_mul(r.into())) .saturating_add(T::DbWeight::get().reads(6_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) - .saturating_add(Weight::from_parts(0, 200).saturating_mul(r.into())) + .saturating_add(Weight::from_parts(0, 40).saturating_mul(r.into())) } /// Storage: System Account (r:1 w:0) /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: Measured) @@ -1641,524 +1640,524 @@ impl WeightInfo for SubstrateWeight { fn seal_instantiation_nonce(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `776 + r * (3 ±0)` - // Estimated: `24004 + r * (18 ±0)` - // Minimum execution time: 234_123_000 picoseconds. - Weight::from_parts(245_872_832, 24004) - // Standard Error: 943 - .saturating_add(Weight::from_parts(136_460, 0).saturating_mul(r.into())) + // Estimated: `6723 + r * (3 ±0)` + // Minimum execution time: 234_014_000 picoseconds. + Weight::from_parts(240_042_671, 6723) + // Standard Error: 152 + .saturating_add(Weight::from_parts(138_382, 0).saturating_mul(r.into())) .saturating_add(T::DbWeight::get().reads(7_u64)) .saturating_add(T::DbWeight::get().writes(4_u64)) - .saturating_add(Weight::from_parts(0, 18).saturating_mul(r.into())) + .saturating_add(Weight::from_parts(0, 3).saturating_mul(r.into())) } /// The range of component `r` is `[0, 5000]`. fn instr_i64const(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_606_000 picoseconds. - Weight::from_parts(1_845_698, 0) + // Minimum execution time: 1_533_000 picoseconds. + Weight::from_parts(1_846_015, 0) // Standard Error: 2 - .saturating_add(Weight::from_parts(2_995, 0).saturating_mul(r.into())) + .saturating_add(Weight::from_parts(2_935, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 5000]`. fn instr_i64load(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_711_000 picoseconds. - Weight::from_parts(2_281_581, 0) - // Standard Error: 4 - .saturating_add(Weight::from_parts(6_354, 0).saturating_mul(r.into())) + // Minimum execution time: 1_671_000 picoseconds. + Weight::from_parts(2_197_197, 0) + // Standard Error: 3 + .saturating_add(Weight::from_parts(6_335, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 5000]`. fn instr_i64store(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_680_000 picoseconds. - Weight::from_parts(2_269_238, 0) - // Standard Error: 4 - .saturating_add(Weight::from_parts(6_014, 0).saturating_mul(r.into())) + // Minimum execution time: 1_665_000 picoseconds. + Weight::from_parts(2_200_545, 0) + // Standard Error: 3 + .saturating_add(Weight::from_parts(6_011, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 5000]`. fn instr_select(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_592_000 picoseconds. - Weight::from_parts(2_010_963, 0) - // Standard Error: 3 - .saturating_add(Weight::from_parts(7_927, 0).saturating_mul(r.into())) + // Minimum execution time: 1_545_000 picoseconds. + Weight::from_parts(1_977_462, 0) + // Standard Error: 2 + .saturating_add(Weight::from_parts(7_901, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 5000]`. fn instr_if(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_631_000 picoseconds. - Weight::from_parts(1_936_549, 0) - // Standard Error: 6 - .saturating_add(Weight::from_parts(10_426, 0).saturating_mul(r.into())) + // Minimum execution time: 1_515_000 picoseconds. + Weight::from_parts(1_866_184, 0) + // Standard Error: 4 + .saturating_add(Weight::from_parts(10_514, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 5000]`. fn instr_br(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_616_000 picoseconds. - Weight::from_parts(1_938_716, 0) - // Standard Error: 15 - .saturating_add(Weight::from_parts(4_525, 0).saturating_mul(r.into())) + // Minimum execution time: 1_618_000 picoseconds. + Weight::from_parts(1_895_104, 0) + // Standard Error: 12 + .saturating_add(Weight::from_parts(4_523, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 5000]`. fn instr_br_if(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_535_000 picoseconds. - Weight::from_parts(1_109_860, 0) - // Standard Error: 77 - .saturating_add(Weight::from_parts(7_998, 0).saturating_mul(r.into())) + // Minimum execution time: 1_510_000 picoseconds. + Weight::from_parts(1_779_998, 0) + // Standard Error: 8 + .saturating_add(Weight::from_parts(6_832, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 5000]`. fn instr_br_table(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_594_000 picoseconds. - Weight::from_parts(1_478_654, 0) - // Standard Error: 36 - .saturating_add(Weight::from_parts(9_525, 0).saturating_mul(r.into())) + // Minimum execution time: 1_529_000 picoseconds. + Weight::from_parts(1_726_996, 0) + // Standard Error: 26 + .saturating_add(Weight::from_parts(9_199, 0).saturating_mul(r.into())) } /// The range of component `e` is `[1, 256]`. fn instr_br_table_per_entry(e: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_730_000 picoseconds. - Weight::from_parts(1_836_342, 0) - // Standard Error: 14 - .saturating_add(Weight::from_parts(206, 0).saturating_mul(e.into())) + // Minimum execution time: 1_682_000 picoseconds. + Weight::from_parts(1_789_910, 0) + // Standard Error: 16 + .saturating_add(Weight::from_parts(42, 0).saturating_mul(e.into())) } /// The range of component `r` is `[0, 5000]`. fn instr_call(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_607_000 picoseconds. - Weight::from_parts(2_150_133, 0) - // Standard Error: 17 - .saturating_add(Weight::from_parts(18_630, 0).saturating_mul(r.into())) + // Minimum execution time: 1_539_000 picoseconds. + Weight::from_parts(2_093_056, 0) + // Standard Error: 27 + .saturating_add(Weight::from_parts(18_917, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 5000]`. fn instr_call_indirect(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_824_000 picoseconds. - Weight::from_parts(3_119_825, 0) - // Standard Error: 15 - .saturating_add(Weight::from_parts(25_304, 0).saturating_mul(r.into())) + // Minimum execution time: 1_851_000 picoseconds. + Weight::from_parts(3_134_610, 0) + // Standard Error: 34 + .saturating_add(Weight::from_parts(24_714, 0).saturating_mul(r.into())) } /// The range of component `l` is `[0, 1024]`. fn instr_call_per_local(l: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_692_000 picoseconds. - Weight::from_parts(1_927_049, 0) + // Minimum execution time: 1_654_000 picoseconds. + Weight::from_parts(1_885_921, 0) // Standard Error: 14 - .saturating_add(Weight::from_parts(1_217, 0).saturating_mul(l.into())) + .saturating_add(Weight::from_parts(1_243, 0).saturating_mul(l.into())) } /// The range of component `r` is `[0, 5000]`. fn instr_local_get(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 2_795_000 picoseconds. - Weight::from_parts(3_107_843, 0) - // Standard Error: 2 - .saturating_add(Weight::from_parts(2_445, 0).saturating_mul(r.into())) + // Minimum execution time: 2_744_000 picoseconds. + Weight::from_parts(3_014_725, 0) + // Standard Error: 1 + .saturating_add(Weight::from_parts(2_447, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 5000]`. fn instr_local_set(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 2_815_000 picoseconds. - Weight::from_parts(3_114_618, 0) + // Minimum execution time: 2_881_000 picoseconds. + Weight::from_parts(3_137_711, 0) // Standard Error: 3 - .saturating_add(Weight::from_parts(3_639, 0).saturating_mul(r.into())) + .saturating_add(Weight::from_parts(3_608, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 5000]`. fn instr_local_tee(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 2_861_000 picoseconds. - Weight::from_parts(3_193_592, 0) + // Minimum execution time: 2_809_000 picoseconds. + Weight::from_parts(3_142_066, 0) // Standard Error: 3 - .saturating_add(Weight::from_parts(3_957, 0).saturating_mul(r.into())) + .saturating_add(Weight::from_parts(3_841, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 5000]`. fn instr_global_get(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_710_000 picoseconds. - Weight::from_parts(2_148_021, 0) - // Standard Error: 3 - .saturating_add(Weight::from_parts(8_391, 0).saturating_mul(r.into())) + // Minimum execution time: 1_704_000 picoseconds. + Weight::from_parts(2_083_619, 0) + // Standard Error: 2 + .saturating_add(Weight::from_parts(8_366, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 5000]`. fn instr_global_set(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_663_000 picoseconds. - Weight::from_parts(2_170_373, 0) - // Standard Error: 3 - .saturating_add(Weight::from_parts(8_812, 0).saturating_mul(r.into())) + // Minimum execution time: 1_652_000 picoseconds. + Weight::from_parts(2_048_256, 0) + // Standard Error: 5 + .saturating_add(Weight::from_parts(8_826, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 5000]`. fn instr_memory_current(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_662_000 picoseconds. - Weight::from_parts(2_601_398, 0) - // Standard Error: 198 - .saturating_add(Weight::from_parts(3_851, 0).saturating_mul(r.into())) + // Minimum execution time: 1_671_000 picoseconds. + Weight::from_parts(1_924_626, 0) + // Standard Error: 1 + .saturating_add(Weight::from_parts(3_746, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 16]`. fn instr_memory_grow(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_603_000 picoseconds. - Weight::from_parts(332_932, 0) - // Standard Error: 137_529 - .saturating_add(Weight::from_parts(13_233_734, 0).saturating_mul(r.into())) + // Minimum execution time: 1_585_000 picoseconds. + Weight::from_parts(490_856, 0) + // Standard Error: 133_673 + .saturating_add(Weight::from_parts(13_182_582, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 5000]`. fn instr_i64clz(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_613_000 picoseconds. - Weight::from_parts(1_922_391, 0) + // Minimum execution time: 1_533_000 picoseconds. + Weight::from_parts(1_851_563, 0) // Standard Error: 2 - .saturating_add(Weight::from_parts(3_903, 0).saturating_mul(r.into())) + .saturating_add(Weight::from_parts(3_820, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 5000]`. fn instr_i64ctz(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_522_000 picoseconds. - Weight::from_parts(1_920_023, 0) + // Minimum execution time: 1_564_000 picoseconds. + Weight::from_parts(1_914_178, 0) // Standard Error: 2 - .saturating_add(Weight::from_parts(3_876, 0).saturating_mul(r.into())) + .saturating_add(Weight::from_parts(3_732, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 5000]`. fn instr_i64popcnt(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_590_000 picoseconds. - Weight::from_parts(1_867_406, 0) - // Standard Error: 3 - .saturating_add(Weight::from_parts(3_910, 0).saturating_mul(r.into())) + // Minimum execution time: 1_559_000 picoseconds. + Weight::from_parts(1_886_992, 0) + // Standard Error: 2 + .saturating_add(Weight::from_parts(3_731, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 5000]`. fn instr_i64eqz(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_577_000 picoseconds. - Weight::from_parts(1_918_037, 0) + // Minimum execution time: 1_553_000 picoseconds. + Weight::from_parts(1_886_545, 0) // Standard Error: 2 - .saturating_add(Weight::from_parts(3_673, 0).saturating_mul(r.into())) + .saturating_add(Weight::from_parts(3_658, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 5000]`. fn instr_i64extendsi32(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_589_000 picoseconds. - Weight::from_parts(1_917_901, 0) - // Standard Error: 3 - .saturating_add(Weight::from_parts(3_956, 0).saturating_mul(r.into())) + // Minimum execution time: 1_507_000 picoseconds. + Weight::from_parts(1_853_647, 0) + // Standard Error: 2 + .saturating_add(Weight::from_parts(3_852, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 5000]`. fn instr_i64extendui32(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_573_000 picoseconds. - Weight::from_parts(1_902_324, 0) + // Minimum execution time: 1_554_000 picoseconds. + Weight::from_parts(1_868_877, 0) // Standard Error: 2 - .saturating_add(Weight::from_parts(3_865, 0).saturating_mul(r.into())) + .saturating_add(Weight::from_parts(3_806, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 5000]`. fn instr_i32wrapi64(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_586_000 picoseconds. - Weight::from_parts(1_953_738, 0) + // Minimum execution time: 1_514_000 picoseconds. + Weight::from_parts(1_882_233, 0) // Standard Error: 2 - .saturating_add(Weight::from_parts(3_838, 0).saturating_mul(r.into())) + .saturating_add(Weight::from_parts(3_700, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 5000]`. fn instr_i64eq(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_598_000 picoseconds. - Weight::from_parts(1_912_180, 0) + // Minimum execution time: 1_529_000 picoseconds. + Weight::from_parts(1_897_247, 0) // Standard Error: 2 - .saturating_add(Weight::from_parts(5_994, 0).saturating_mul(r.into())) + .saturating_add(Weight::from_parts(5_955, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 5000]`. fn instr_i64ne(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_573_000 picoseconds. - Weight::from_parts(1_943_725, 0) + // Minimum execution time: 1_513_000 picoseconds. + Weight::from_parts(1_922_333, 0) // Standard Error: 2 - .saturating_add(Weight::from_parts(5_998, 0).saturating_mul(r.into())) + .saturating_add(Weight::from_parts(5_933, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 5000]`. fn instr_i64lts(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_584_000 picoseconds. - Weight::from_parts(1_949_209, 0) + // Minimum execution time: 1_512_000 picoseconds. + Weight::from_parts(1_848_668, 0) // Standard Error: 2 - .saturating_add(Weight::from_parts(5_991, 0).saturating_mul(r.into())) + .saturating_add(Weight::from_parts(5_966, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 5000]`. fn instr_i64ltu(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_576_000 picoseconds. - Weight::from_parts(1_927_466, 0) + // Minimum execution time: 1_522_000 picoseconds. + Weight::from_parts(1_875_257, 0) // Standard Error: 2 - .saturating_add(Weight::from_parts(6_005, 0).saturating_mul(r.into())) + .saturating_add(Weight::from_parts(5_965, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 5000]`. fn instr_i64gts(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_566_000 picoseconds. - Weight::from_parts(2_004_312, 0) - // Standard Error: 3 - .saturating_add(Weight::from_parts(5_799, 0).saturating_mul(r.into())) + // Minimum execution time: 1_546_000 picoseconds. + Weight::from_parts(1_836_691, 0) + // Standard Error: 4 + .saturating_add(Weight::from_parts(5_842, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 5000]`. fn instr_i64gtu(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_538_000 picoseconds. - Weight::from_parts(1_922_703, 0) - // Standard Error: 9 - .saturating_add(Weight::from_parts(6_129, 0).saturating_mul(r.into())) + // Minimum execution time: 1_505_000 picoseconds. + Weight::from_parts(1_907_551, 0) + // Standard Error: 2 + .saturating_add(Weight::from_parts(6_075, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 5000]`. fn instr_i64les(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_575_000 picoseconds. - Weight::from_parts(1_978_271, 0) - // Standard Error: 3 - .saturating_add(Weight::from_parts(5_991, 0).saturating_mul(r.into())) + // Minimum execution time: 1_527_000 picoseconds. + Weight::from_parts(1_891_008, 0) + // Standard Error: 2 + .saturating_add(Weight::from_parts(5_971, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 5000]`. fn instr_i64leu(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_572_000 picoseconds. - Weight::from_parts(4_028_578, 0) - // Standard Error: 263 - .saturating_add(Weight::from_parts(5_784, 0).saturating_mul(r.into())) + // Minimum execution time: 1_556_000 picoseconds. + Weight::from_parts(1_910_864, 0) + // Standard Error: 2 + .saturating_add(Weight::from_parts(6_059, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 5000]`. fn instr_i64ges(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_615_000 picoseconds. - Weight::from_parts(1_962_065, 0) - // Standard Error: 3 - .saturating_add(Weight::from_parts(5_945, 0).saturating_mul(r.into())) + // Minimum execution time: 1_544_000 picoseconds. + Weight::from_parts(1_912_650, 0) + // Standard Error: 2 + .saturating_add(Weight::from_parts(5_943, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 5000]`. fn instr_i64geu(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_578_000 picoseconds. - Weight::from_parts(1_909_721, 0) + // Minimum execution time: 1_513_000 picoseconds. + Weight::from_parts(1_855_260, 0) // Standard Error: 2 - .saturating_add(Weight::from_parts(5_993, 0).saturating_mul(r.into())) + .saturating_add(Weight::from_parts(5_975, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 5000]`. fn instr_i64add(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_609_000 picoseconds. - Weight::from_parts(1_926_472, 0) - // Standard Error: 3 - .saturating_add(Weight::from_parts(5_888, 0).saturating_mul(r.into())) + // Minimum execution time: 1_521_000 picoseconds. + Weight::from_parts(1_867_259, 0) + // Standard Error: 2 + .saturating_add(Weight::from_parts(5_846, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 5000]`. fn instr_i64sub(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_620_000 picoseconds. - Weight::from_parts(2_875_196, 0) - // Standard Error: 177 - .saturating_add(Weight::from_parts(5_990, 0).saturating_mul(r.into())) + // Minimum execution time: 1_509_000 picoseconds. + Weight::from_parts(1_893_018, 0) + // Standard Error: 2 + .saturating_add(Weight::from_parts(6_096, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 5000]`. fn instr_i64mul(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_570_000 picoseconds. - Weight::from_parts(2_155_483, 0) - // Standard Error: 83 - .saturating_add(Weight::from_parts(5_675, 0).saturating_mul(r.into())) + // Minimum execution time: 1_496_000 picoseconds. + Weight::from_parts(1_886_659, 0) + // Standard Error: 2 + .saturating_add(Weight::from_parts(5_754, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 5000]`. fn instr_i64divs(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_558_000 picoseconds. - Weight::from_parts(2_108_263, 0) - // Standard Error: 5 - .saturating_add(Weight::from_parts(11_755, 0).saturating_mul(r.into())) + // Minimum execution time: 1_527_000 picoseconds. + Weight::from_parts(1_890_548, 0) + // Standard Error: 2 + .saturating_add(Weight::from_parts(11_842, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 5000]`. fn instr_i64divu(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_568_000 picoseconds. - Weight::from_parts(1_606_397, 0) - // Standard Error: 51 - .saturating_add(Weight::from_parts(11_011, 0).saturating_mul(r.into())) + // Minimum execution time: 1_518_000 picoseconds. + Weight::from_parts(1_891_903, 0) + // Standard Error: 4 + .saturating_add(Weight::from_parts(10_613, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 5000]`. fn instr_i64rems(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_630_000 picoseconds. - Weight::from_parts(2_872_487, 0) - // Standard Error: 24 - .saturating_add(Weight::from_parts(11_761, 0).saturating_mul(r.into())) + // Minimum execution time: 1_504_000 picoseconds. + Weight::from_parts(1_632_694, 0) + // Standard Error: 7 + .saturating_add(Weight::from_parts(12_281, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 5000]`. fn instr_i64remu(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_575_000 picoseconds. - Weight::from_parts(2_385_398, 0) - // Standard Error: 45 - .saturating_add(Weight::from_parts(10_650, 0).saturating_mul(r.into())) + // Minimum execution time: 1_507_000 picoseconds. + Weight::from_parts(1_878_413, 0) + // Standard Error: 2 + .saturating_add(Weight::from_parts(10_737, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 5000]`. fn instr_i64and(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_622_000 picoseconds. - Weight::from_parts(1_957_246, 0) + // Minimum execution time: 1_534_000 picoseconds. + Weight::from_parts(1_898_519, 0) // Standard Error: 2 - .saturating_add(Weight::from_parts(5_659, 0).saturating_mul(r.into())) + .saturating_add(Weight::from_parts(5_645, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 5000]`. fn instr_i64or(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_551_000 picoseconds. - Weight::from_parts(1_897_168, 0) - // Standard Error: 3 - .saturating_add(Weight::from_parts(5_774, 0).saturating_mul(r.into())) + // Minimum execution time: 1_503_000 picoseconds. + Weight::from_parts(1_895_532, 0) + // Standard Error: 2 + .saturating_add(Weight::from_parts(5_745, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 5000]`. fn instr_i64xor(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_549_000 picoseconds. - Weight::from_parts(1_915_938, 0) - // Standard Error: 3 - .saturating_add(Weight::from_parts(5_915, 0).saturating_mul(r.into())) + // Minimum execution time: 1_507_000 picoseconds. + Weight::from_parts(1_868_720, 0) + // Standard Error: 2 + .saturating_add(Weight::from_parts(5_873, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 5000]`. fn instr_i64shl(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_617_000 picoseconds. - Weight::from_parts(1_932_618, 0) - // Standard Error: 3 - .saturating_add(Weight::from_parts(5_854, 0).saturating_mul(r.into())) + // Minimum execution time: 1_513_000 picoseconds. + Weight::from_parts(1_894_207, 0) + // Standard Error: 2 + .saturating_add(Weight::from_parts(5_843, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 5000]`. fn instr_i64shrs(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_617_000 picoseconds. - Weight::from_parts(1_937_566, 0) - // Standard Error: 3 - .saturating_add(Weight::from_parts(6_134, 0).saturating_mul(r.into())) + // Minimum execution time: 1_473_000 picoseconds. + Weight::from_parts(1_880_224, 0) + // Standard Error: 2 + .saturating_add(Weight::from_parts(6_107, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 5000]`. fn instr_i64shru(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_584_000 picoseconds. - Weight::from_parts(1_980_681, 0) - // Standard Error: 3 - .saturating_add(Weight::from_parts(5_842, 0).saturating_mul(r.into())) + // Minimum execution time: 1_447_000 picoseconds. + Weight::from_parts(1_884_551, 0) + // Standard Error: 2 + .saturating_add(Weight::from_parts(5_849, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 5000]`. fn instr_i64rotl(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_542_000 picoseconds. - Weight::from_parts(2_131_567, 0) - // Standard Error: 57 - .saturating_add(Weight::from_parts(6_010, 0).saturating_mul(r.into())) + // Minimum execution time: 1_538_000 picoseconds. + Weight::from_parts(1_908_813, 0) + // Standard Error: 2 + .saturating_add(Weight::from_parts(5_987, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 5000]`. fn instr_i64rotr(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_609_000 picoseconds. - Weight::from_parts(1_643_214, 0) - // Standard Error: 91 - .saturating_add(Weight::from_parts(6_062, 0).saturating_mul(r.into())) + // Minimum execution time: 1_538_000 picoseconds. + Weight::from_parts(1_878_015, 0) + // Standard Error: 2 + .saturating_add(Weight::from_parts(5_848, 0).saturating_mul(r.into())) } } @@ -2170,8 +2169,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `109` // Estimated: `1594` - // Minimum execution time: 2_503_000 picoseconds. - Weight::from_parts(2_603_000, 1594) + // Minimum execution time: 2_565_000 picoseconds. + Weight::from_parts(2_759_000, 1594) .saturating_add(RocksDbWeight::get().reads(1_u64)) } /// Storage: Skipped Metadata (r:0 w:0) @@ -2181,10 +2180,10 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `488 + k * (69 ±0)` // Estimated: `478 + k * (70 ±0)` - // Minimum execution time: 13_292_000 picoseconds. - Weight::from_parts(9_180_439, 478) - // Standard Error: 997 - .saturating_add(Weight::from_parts(967_522, 0).saturating_mul(k.into())) + // Minimum execution time: 13_480_000 picoseconds. + Weight::from_parts(10_153_869, 478) + // Standard Error: 427 + .saturating_add(Weight::from_parts(958_726, 0).saturating_mul(k.into())) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().reads((1_u64).saturating_mul(k.into()))) .saturating_add(RocksDbWeight::get().writes(2_u64)) @@ -2199,14 +2198,14 @@ impl WeightInfo for () { fn reinstrument(c: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `238 + c * (1 ±0)` - // Estimated: `3951 + c * (2 ±0)` - // Minimum execution time: 30_822_000 picoseconds. - Weight::from_parts(26_457_115, 3951) - // Standard Error: 58 - .saturating_add(Weight::from_parts(54_868, 0).saturating_mul(c.into())) + // Estimated: `3708 + c * (1 ±0)` + // Minimum execution time: 30_406_000 picoseconds. + Weight::from_parts(28_467_370, 3708) + // Standard Error: 46 + .saturating_add(Weight::from_parts(53_724, 0).saturating_mul(c.into())) .saturating_add(RocksDbWeight::get().reads(1_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) - .saturating_add(Weight::from_parts(0, 2).saturating_mul(c.into())) + .saturating_add(Weight::from_parts(0, 1).saturating_mul(c.into())) } /// Storage: Contracts ContractInfoOf (r:1 w:1) /// Proof: Contracts ContractInfoOf (max_values: None, max_size: Some(290), added: 2765, mode: Measured) @@ -2222,14 +2221,14 @@ impl WeightInfo for () { fn call_with_code_per_byte(c: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `707` - // Estimated: `21400 + c * (5 ±0)` - // Minimum execution time: 263_719_000 picoseconds. - Weight::from_parts(275_281_941, 21400) - // Standard Error: 32 - .saturating_add(Weight::from_parts(37_880, 0).saturating_mul(c.into())) + // Estimated: `6656 + c * (1 ±0)` + // Minimum execution time: 263_198_000 picoseconds. + Weight::from_parts(276_162_279, 6656) + // Standard Error: 18 + .saturating_add(Weight::from_parts(37_378, 0).saturating_mul(c.into())) .saturating_add(RocksDbWeight::get().reads(6_u64)) .saturating_add(RocksDbWeight::get().writes(4_u64)) - .saturating_add(Weight::from_parts(0, 5).saturating_mul(c.into())) + .saturating_add(Weight::from_parts(0, 1).saturating_mul(c.into())) } /// Storage: Contracts OwnerInfoOf (r:1 w:1) /// Proof: Contracts OwnerInfoOf (max_values: None, max_size: Some(88), added: 2563, mode: Measured) @@ -2253,15 +2252,15 @@ impl WeightInfo for () { fn instantiate_with_code(c: u32, i: u32, s: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `270` - // Estimated: `26207` - // Minimum execution time: 3_133_756_000 picoseconds. - Weight::from_parts(556_483_545, 26207) - // Standard Error: 294 - .saturating_add(Weight::from_parts(107_914, 0).saturating_mul(c.into())) - // Standard Error: 17 - .saturating_add(Weight::from_parts(1_143, 0).saturating_mul(i.into())) - // Standard Error: 17 - .saturating_add(Weight::from_parts(1_456, 0).saturating_mul(s.into())) + // Estimated: `8659` + // Minimum execution time: 3_132_259_000 picoseconds. + Weight::from_parts(513_284_834, 8659) + // Standard Error: 280 + .saturating_add(Weight::from_parts(106_723, 0).saturating_mul(c.into())) + // Standard Error: 16 + .saturating_add(Weight::from_parts(1_166, 0).saturating_mul(i.into())) + // Standard Error: 16 + .saturating_add(Weight::from_parts(1_436, 0).saturating_mul(s.into())) .saturating_add(RocksDbWeight::get().reads(9_u64)) .saturating_add(RocksDbWeight::get().writes(10_u64)) } @@ -2284,13 +2283,13 @@ impl WeightInfo for () { fn instantiate(i: u32, s: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `482` - // Estimated: `28521` - // Minimum execution time: 1_646_953_000 picoseconds. - Weight::from_parts(262_115_215, 28521) - // Standard Error: 8 - .saturating_add(Weight::from_parts(1_454, 0).saturating_mul(i.into())) - // Standard Error: 8 - .saturating_add(Weight::from_parts(1_463, 0).saturating_mul(s.into())) + // Estimated: `6408` + // Minimum execution time: 1_646_604_000 picoseconds. + Weight::from_parts(271_369_256, 6408) + // Standard Error: 7 + .saturating_add(Weight::from_parts(1_426, 0).saturating_mul(i.into())) + // Standard Error: 7 + .saturating_add(Weight::from_parts(1_438, 0).saturating_mul(s.into())) .saturating_add(RocksDbWeight::get().reads(9_u64)) .saturating_add(RocksDbWeight::get().writes(7_u64)) } @@ -2307,9 +2306,9 @@ impl WeightInfo for () { fn call() -> Weight { // Proof Size summary in bytes: // Measured: `759` - // Estimated: `21615` - // Minimum execution time: 190_769_000 picoseconds. - Weight::from_parts(191_717_000, 21615) + // Estimated: `6699` + // Minimum execution time: 191_360_000 picoseconds. + Weight::from_parts(192_625_000, 6699) .saturating_add(RocksDbWeight::get().reads(6_u64)) .saturating_add(RocksDbWeight::get().writes(4_u64)) } @@ -2325,11 +2324,11 @@ impl WeightInfo for () { fn upload_code(c: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `109` - // Estimated: `7366` - // Minimum execution time: 246_204_000 picoseconds. - Weight::from_parts(243_234_754, 7366) - // Standard Error: 78 - .saturating_add(Weight::from_parts(109_232, 0).saturating_mul(c.into())) + // Estimated: `3574` + // Minimum execution time: 245_207_000 picoseconds. + Weight::from_parts(244_703_457, 3574) + // Standard Error: 61 + .saturating_add(Weight::from_parts(106_850, 0).saturating_mul(c.into())) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().writes(4_u64)) } @@ -2344,9 +2343,9 @@ impl WeightInfo for () { fn remove_code() -> Weight { // Proof Size summary in bytes: // Measured: `255` - // Estimated: `7950` - // Minimum execution time: 33_516_000 picoseconds. - Weight::from_parts(33_995_000, 7950) + // Estimated: `3720` + // Minimum execution time: 33_560_000 picoseconds. + Weight::from_parts(33_833_000, 3720) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().writes(4_u64)) } @@ -2359,9 +2358,9 @@ impl WeightInfo for () { fn set_code() -> Weight { // Proof Size summary in bytes: // Measured: `570` - // Estimated: `19530` - // Minimum execution time: 33_255_000 picoseconds. - Weight::from_parts(33_692_000, 19530) + // Estimated: `8985` + // Minimum execution time: 33_288_000 picoseconds. + Weight::from_parts(33_775_000, 8985) .saturating_add(RocksDbWeight::get().reads(6_u64)) .saturating_add(RocksDbWeight::get().writes(6_u64)) } @@ -2379,14 +2378,14 @@ impl WeightInfo for () { fn seal_caller(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `781 + r * (6 ±0)` - // Estimated: `21730 + r * (30 ±0)` - // Minimum execution time: 235_074_000 picoseconds. - Weight::from_parts(243_735_179, 21730) - // Standard Error: 972 - .saturating_add(Weight::from_parts(328_571, 0).saturating_mul(r.into())) + // Estimated: `6722 + r * (6 ±0)` + // Minimum execution time: 234_292_000 picoseconds. + Weight::from_parts(235_941_911, 6722) + // Standard Error: 413 + .saturating_add(Weight::from_parts(339_913, 0).saturating_mul(r.into())) .saturating_add(RocksDbWeight::get().reads(6_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) - .saturating_add(Weight::from_parts(0, 30).saturating_mul(r.into())) + .saturating_add(Weight::from_parts(0, 6).saturating_mul(r.into())) } /// Storage: System Account (r:1 w:0) /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: Measured) @@ -2402,15 +2401,15 @@ impl WeightInfo for () { fn seal_is_contract(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `839 + r * (240 ±0)` - // Estimated: `21835 + r * (3675 ±0)` - // Minimum execution time: 236_455_000 picoseconds. - Weight::from_parts(81_818_936, 21835) - // Standard Error: 5_874 - .saturating_add(Weight::from_parts(3_316_006, 0).saturating_mul(r.into())) + // Estimated: `6743 + r * (2715 ±0)` + // Minimum execution time: 236_273_000 picoseconds. + Weight::from_parts(74_380_906, 6743) + // Standard Error: 5_745 + .saturating_add(Weight::from_parts(3_331_781, 0).saturating_mul(r.into())) .saturating_add(RocksDbWeight::get().reads(6_u64)) .saturating_add(RocksDbWeight::get().reads((1_u64).saturating_mul(r.into()))) .saturating_add(RocksDbWeight::get().writes(3_u64)) - .saturating_add(Weight::from_parts(0, 3675).saturating_mul(r.into())) + .saturating_add(Weight::from_parts(0, 2715).saturating_mul(r.into())) } /// Storage: System Account (r:1 w:0) /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: Measured) @@ -2426,15 +2425,15 @@ impl WeightInfo for () { fn seal_code_hash(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `831 + r * (244 ±0)` - // Estimated: `21855 + r * (3695 ±0)` - // Minimum execution time: 237_724_000 picoseconds. - Weight::from_parts(91_384_964, 21855) - // Standard Error: 5_828 - .saturating_add(Weight::from_parts(4_063_221, 0).saturating_mul(r.into())) + // Estimated: `6747 + r * (2719 ±0)` + // Minimum execution time: 236_573_000 picoseconds. + Weight::from_parts(82_473_906, 6747) + // Standard Error: 5_510 + .saturating_add(Weight::from_parts(4_131_820, 0).saturating_mul(r.into())) .saturating_add(RocksDbWeight::get().reads(6_u64)) .saturating_add(RocksDbWeight::get().reads((1_u64).saturating_mul(r.into()))) .saturating_add(RocksDbWeight::get().writes(3_u64)) - .saturating_add(Weight::from_parts(0, 3695).saturating_mul(r.into())) + .saturating_add(Weight::from_parts(0, 2719).saturating_mul(r.into())) } /// Storage: System Account (r:1 w:0) /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: Measured) @@ -2450,14 +2449,14 @@ impl WeightInfo for () { fn seal_own_code_hash(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `788 + r * (6 ±0)` - // Estimated: `21770 + r * (30 ±0)` - // Minimum execution time: 236_144_000 picoseconds. - Weight::from_parts(239_917_873, 21770) - // Standard Error: 629 - .saturating_add(Weight::from_parts(409_752, 0).saturating_mul(r.into())) + // Estimated: `6730 + r * (6 ±0)` + // Minimum execution time: 235_878_000 picoseconds. + Weight::from_parts(238_387_359, 6730) + // Standard Error: 318 + .saturating_add(Weight::from_parts(409_923, 0).saturating_mul(r.into())) .saturating_add(RocksDbWeight::get().reads(6_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) - .saturating_add(Weight::from_parts(0, 30).saturating_mul(r.into())) + .saturating_add(Weight::from_parts(0, 6).saturating_mul(r.into())) } /// Storage: System Account (r:1 w:0) /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: Measured) @@ -2473,14 +2472,14 @@ impl WeightInfo for () { fn seal_caller_is_origin(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `778 + r * (3 ±0)` - // Estimated: `21735 + r * (15 ±0)` - // Minimum execution time: 232_946_000 picoseconds. - Weight::from_parts(243_163_112, 21735) - // Standard Error: 1_940 - .saturating_add(Weight::from_parts(162_705, 0).saturating_mul(r.into())) + // Estimated: `6723 + r * (3 ±0)` + // Minimum execution time: 233_476_000 picoseconds. + Weight::from_parts(238_014_452, 6723) + // Standard Error: 145 + .saturating_add(Weight::from_parts(165_823, 0).saturating_mul(r.into())) .saturating_add(RocksDbWeight::get().reads(6_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) - .saturating_add(Weight::from_parts(0, 15).saturating_mul(r.into())) + .saturating_add(Weight::from_parts(0, 3).saturating_mul(r.into())) } /// Storage: System Account (r:1 w:0) /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: Measured) @@ -2496,14 +2495,14 @@ impl WeightInfo for () { fn seal_address(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `782 + r * (6 ±0)` - // Estimated: `21740 + r * (30 ±0)` - // Minimum execution time: 235_301_000 picoseconds. - Weight::from_parts(240_802_312, 21740) - // Standard Error: 5_362 - .saturating_add(Weight::from_parts(339_001, 0).saturating_mul(r.into())) + // Estimated: `6724 + r * (6 ±0)` + // Minimum execution time: 235_490_000 picoseconds. + Weight::from_parts(240_039_685, 6724) + // Standard Error: 330 + .saturating_add(Weight::from_parts(332_291, 0).saturating_mul(r.into())) .saturating_add(RocksDbWeight::get().reads(6_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) - .saturating_add(Weight::from_parts(0, 30).saturating_mul(r.into())) + .saturating_add(Weight::from_parts(0, 6).saturating_mul(r.into())) } /// Storage: System Account (r:1 w:0) /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: Measured) @@ -2519,14 +2518,14 @@ impl WeightInfo for () { fn seal_gas_left(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `783 + r * (6 ±0)` - // Estimated: `21725 + r * (30 ±0)` - // Minimum execution time: 235_683_000 picoseconds. - Weight::from_parts(237_357_276, 21725) - // Standard Error: 726 - .saturating_add(Weight::from_parts(333_264, 0).saturating_mul(r.into())) + // Estimated: `6721 + r * (6 ±0)` + // Minimum execution time: 236_093_000 picoseconds. + Weight::from_parts(238_513_328, 6721) + // Standard Error: 206 + .saturating_add(Weight::from_parts(328_899, 0).saturating_mul(r.into())) .saturating_add(RocksDbWeight::get().reads(6_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) - .saturating_add(Weight::from_parts(0, 30).saturating_mul(r.into())) + .saturating_add(Weight::from_parts(0, 6).saturating_mul(r.into())) } /// Storage: System Account (r:2 w:0) /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: Measured) @@ -2542,14 +2541,14 @@ impl WeightInfo for () { fn seal_balance(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `922 + r * (6 ±0)` - // Estimated: `24633 + r * (30 ±0)` - // Minimum execution time: 234_327_000 picoseconds. - Weight::from_parts(239_792_456, 24633) - // Standard Error: 3_460 - .saturating_add(Weight::from_parts(1_496_090, 0).saturating_mul(r.into())) + // Estimated: `6846 + r * (6 ±0)` + // Minimum execution time: 234_801_000 picoseconds. + Weight::from_parts(243_519_159, 6846) + // Standard Error: 1_367 + .saturating_add(Weight::from_parts(1_449_599, 0).saturating_mul(r.into())) .saturating_add(RocksDbWeight::get().reads(7_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) - .saturating_add(Weight::from_parts(0, 30).saturating_mul(r.into())) + .saturating_add(Weight::from_parts(0, 6).saturating_mul(r.into())) } /// Storage: System Account (r:1 w:0) /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: Measured) @@ -2565,14 +2564,14 @@ impl WeightInfo for () { fn seal_value_transferred(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `792 + r * (6 ±0)` - // Estimated: `21825 + r * (30 ±0)` - // Minimum execution time: 235_466_000 picoseconds. - Weight::from_parts(242_580_658, 21825) - // Standard Error: 1_439 - .saturating_add(Weight::from_parts(323_842, 0).saturating_mul(r.into())) + // Estimated: `6741 + r * (6 ±0)` + // Minimum execution time: 236_765_000 picoseconds. + Weight::from_parts(237_843_244, 6741) + // Standard Error: 308 + .saturating_add(Weight::from_parts(329_911, 0).saturating_mul(r.into())) .saturating_add(RocksDbWeight::get().reads(6_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) - .saturating_add(Weight::from_parts(0, 30).saturating_mul(r.into())) + .saturating_add(Weight::from_parts(0, 6).saturating_mul(r.into())) } /// Storage: System Account (r:1 w:0) /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: Measured) @@ -2588,14 +2587,14 @@ impl WeightInfo for () { fn seal_minimum_balance(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `790 + r * (6 ±0)` - // Estimated: `21815 + r * (30 ±0)` - // Minimum execution time: 238_529_000 picoseconds. - Weight::from_parts(249_276_722, 21815) - // Standard Error: 1_216 - .saturating_add(Weight::from_parts(328_600, 0).saturating_mul(r.into())) + // Estimated: `6739 + r * (6 ±0)` + // Minimum execution time: 236_690_000 picoseconds. + Weight::from_parts(241_743_164, 6739) + // Standard Error: 333 + .saturating_add(Weight::from_parts(324_693, 0).saturating_mul(r.into())) .saturating_add(RocksDbWeight::get().reads(6_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) - .saturating_add(Weight::from_parts(0, 30).saturating_mul(r.into())) + .saturating_add(Weight::from_parts(0, 6).saturating_mul(r.into())) } /// Storage: System Account (r:1 w:0) /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: Measured) @@ -2611,14 +2610,14 @@ impl WeightInfo for () { fn seal_block_number(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `787 + r * (6 ±0)` - // Estimated: `21805 + r * (30 ±0)` - // Minimum execution time: 234_360_000 picoseconds. - Weight::from_parts(239_927_876, 21805) - // Standard Error: 541 - .saturating_add(Weight::from_parts(319_553, 0).saturating_mul(r.into())) + // Estimated: `6737 + r * (6 ±0)` + // Minimum execution time: 236_149_000 picoseconds. + Weight::from_parts(239_090_707, 6737) + // Standard Error: 246 + .saturating_add(Weight::from_parts(344_488, 0).saturating_mul(r.into())) .saturating_add(RocksDbWeight::get().reads(6_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) - .saturating_add(Weight::from_parts(0, 30).saturating_mul(r.into())) + .saturating_add(Weight::from_parts(0, 6).saturating_mul(r.into())) } /// Storage: System Account (r:1 w:0) /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: Measured) @@ -2634,14 +2633,14 @@ impl WeightInfo for () { fn seal_now(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `778 + r * (6 ±0)` - // Estimated: `21735 + r * (30 ±0)` - // Minimum execution time: 234_461_000 picoseconds. - Weight::from_parts(239_682_633, 21735) - // Standard Error: 544 - .saturating_add(Weight::from_parts(322_943, 0).saturating_mul(r.into())) + // Estimated: `6723 + r * (6 ±0)` + // Minimum execution time: 235_057_000 picoseconds. + Weight::from_parts(237_752_870, 6723) + // Standard Error: 236 + .saturating_add(Weight::from_parts(328_235, 0).saturating_mul(r.into())) .saturating_add(RocksDbWeight::get().reads(6_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) - .saturating_add(Weight::from_parts(0, 30).saturating_mul(r.into())) + .saturating_add(Weight::from_parts(0, 6).saturating_mul(r.into())) } /// Storage: System Account (r:1 w:0) /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: Measured) @@ -2659,14 +2658,14 @@ impl WeightInfo for () { fn seal_weight_to_fee(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `856 + r * (10 ±0)` - // Estimated: `24446 + r * (60 ±0)` - // Minimum execution time: 234_862_000 picoseconds. - Weight::from_parts(252_614_390, 24446) - // Standard Error: 1_180 - .saturating_add(Weight::from_parts(1_313_286, 0).saturating_mul(r.into())) + // Estimated: `6796 + r * (10 ±0)` + // Minimum execution time: 234_995_000 picoseconds. + Weight::from_parts(246_473_554, 6796) + // Standard Error: 1_015 + .saturating_add(Weight::from_parts(1_337_653, 0).saturating_mul(r.into())) .saturating_add(RocksDbWeight::get().reads(7_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) - .saturating_add(Weight::from_parts(0, 60).saturating_mul(r.into())) + .saturating_add(Weight::from_parts(0, 10).saturating_mul(r.into())) } /// Storage: System Account (r:1 w:0) /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: Measured) @@ -2682,14 +2681,14 @@ impl WeightInfo for () { fn seal_gas(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `745 + r * (4 ±0)` - // Estimated: `21555 + r * (20 ±0)` - // Minimum execution time: 161_003_000 picoseconds. - Weight::from_parts(165_793_675, 21555) - // Standard Error: 323 - .saturating_add(Weight::from_parts(128_941, 0).saturating_mul(r.into())) + // Estimated: `6687 + r * (4 ±0)` + // Minimum execution time: 160_445_000 picoseconds. + Weight::from_parts(165_558_135, 6687) + // Standard Error: 234 + .saturating_add(Weight::from_parts(133_607, 0).saturating_mul(r.into())) .saturating_add(RocksDbWeight::get().reads(6_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) - .saturating_add(Weight::from_parts(0, 20).saturating_mul(r.into())) + .saturating_add(Weight::from_parts(0, 4).saturating_mul(r.into())) } /// Storage: System Account (r:1 w:0) /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: Measured) @@ -2705,14 +2704,14 @@ impl WeightInfo for () { fn seal_input(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `780 + r * (6 ±0)` - // Estimated: `21740 + r * (30 ±0)` - // Minimum execution time: 235_192_000 picoseconds. - Weight::from_parts(241_225_671, 21740) - // Standard Error: 1_132 - .saturating_add(Weight::from_parts(272_760, 0).saturating_mul(r.into())) + // Estimated: `6724 + r * (6 ±0)` + // Minimum execution time: 235_065_000 picoseconds. + Weight::from_parts(237_797_177, 6724) + // Standard Error: 336 + .saturating_add(Weight::from_parts(267_302, 0).saturating_mul(r.into())) .saturating_add(RocksDbWeight::get().reads(6_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) - .saturating_add(Weight::from_parts(0, 30).saturating_mul(r.into())) + .saturating_add(Weight::from_parts(0, 6).saturating_mul(r.into())) } /// Storage: System Account (r:1 w:0) /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: Measured) @@ -2728,11 +2727,11 @@ impl WeightInfo for () { fn seal_input_per_byte(n: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `784` - // Estimated: `21740` - // Minimum execution time: 238_050_000 picoseconds. - Weight::from_parts(241_274_832, 21740) - // Standard Error: 1 - .saturating_add(Weight::from_parts(590, 0).saturating_mul(n.into())) + // Estimated: `6724` + // Minimum execution time: 236_215_000 picoseconds. + Weight::from_parts(239_347_313, 6724) + // Standard Error: 0 + .saturating_add(Weight::from_parts(587, 0).saturating_mul(n.into())) .saturating_add(RocksDbWeight::get().reads(6_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) } @@ -2750,14 +2749,14 @@ impl WeightInfo for () { fn seal_return(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `768 + r * (45 ±0)` - // Estimated: `21660 + r * (225 ±0)` - // Minimum execution time: 231_807_000 picoseconds. - Weight::from_parts(234_264_848, 21660) - // Standard Error: 185_298 - .saturating_add(Weight::from_parts(1_000_351, 0).saturating_mul(r.into())) + // Estimated: `6708 + r * (45 ±0)` + // Minimum execution time: 231_571_000 picoseconds. + Weight::from_parts(233_477_918, 6708) + // Standard Error: 95_776 + .saturating_add(Weight::from_parts(1_733_181, 0).saturating_mul(r.into())) .saturating_add(RocksDbWeight::get().reads(6_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) - .saturating_add(Weight::from_parts(0, 225).saturating_mul(r.into())) + .saturating_add(Weight::from_parts(0, 45).saturating_mul(r.into())) } /// Storage: System Account (r:1 w:0) /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: Measured) @@ -2773,11 +2772,11 @@ impl WeightInfo for () { fn seal_return_per_byte(n: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `778` - // Estimated: `21775` - // Minimum execution time: 234_347_000 picoseconds. - Weight::from_parts(234_774_467, 21775) + // Estimated: `6731` + // Minimum execution time: 234_956_000 picoseconds. + Weight::from_parts(236_785_051, 6731) // Standard Error: 0 - .saturating_add(Weight::from_parts(183, 0).saturating_mul(n.into())) + .saturating_add(Weight::from_parts(177, 0).saturating_mul(n.into())) .saturating_add(RocksDbWeight::get().reads(6_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) } @@ -2801,16 +2800,16 @@ impl WeightInfo for () { fn seal_terminate(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `810 + r * (356 ±0)` - // Estimated: `26094 + r * (15904 ±0)` - // Minimum execution time: 234_356_000 picoseconds. - Weight::from_parts(236_844_659, 26094) - // Standard Error: 161_035 - .saturating_add(Weight::from_parts(110_725_340, 0).saturating_mul(r.into())) + // Estimated: `6750 + r * (7781 ±0)` + // Minimum execution time: 234_275_000 picoseconds. + Weight::from_parts(236_776_769, 6750) + // Standard Error: 137_203 + .saturating_add(Weight::from_parts(110_758_930, 0).saturating_mul(r.into())) .saturating_add(RocksDbWeight::get().reads(6_u64)) .saturating_add(RocksDbWeight::get().reads((6_u64).saturating_mul(r.into()))) .saturating_add(RocksDbWeight::get().writes(3_u64)) .saturating_add(RocksDbWeight::get().writes((8_u64).saturating_mul(r.into()))) - .saturating_add(Weight::from_parts(0, 15904).saturating_mul(r.into())) + .saturating_add(Weight::from_parts(0, 7781).saturating_mul(r.into())) } /// Storage: System Account (r:1 w:0) /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: Measured) @@ -2828,14 +2827,14 @@ impl WeightInfo for () { fn seal_random(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `825 + r * (10 ±0)` - // Estimated: `24283 + r * (60 ±0)` - // Minimum execution time: 234_248_000 picoseconds. - Weight::from_parts(255_712_101, 24283) - // Standard Error: 1_474 - .saturating_add(Weight::from_parts(1_753_943, 0).saturating_mul(r.into())) + // Estimated: `6769 + r * (10 ±0)` + // Minimum execution time: 235_593_000 picoseconds. + Weight::from_parts(253_731_242, 6769) + // Standard Error: 2_129 + .saturating_add(Weight::from_parts(1_771_297, 0).saturating_mul(r.into())) .saturating_add(RocksDbWeight::get().reads(7_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) - .saturating_add(Weight::from_parts(0, 60).saturating_mul(r.into())) + .saturating_add(Weight::from_parts(0, 10).saturating_mul(r.into())) } /// Storage: System Account (r:1 w:0) /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: Measured) @@ -2851,14 +2850,14 @@ impl WeightInfo for () { fn seal_deposit_event(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `778 + r * (10 ±0)` - // Estimated: `21735 + r * (50 ±0)` - // Minimum execution time: 232_456_000 picoseconds. - Weight::from_parts(230_738_907, 21735) - // Standard Error: 4_163 - .saturating_add(Weight::from_parts(3_496_817, 0).saturating_mul(r.into())) + // Estimated: `6723 + r * (10 ±0)` + // Minimum execution time: 232_124_000 picoseconds. + Weight::from_parts(245_904_447, 6723) + // Standard Error: 2_185 + .saturating_add(Weight::from_parts(3_470_410, 0).saturating_mul(r.into())) .saturating_add(RocksDbWeight::get().reads(6_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) - .saturating_add(Weight::from_parts(0, 50).saturating_mul(r.into())) + .saturating_add(Weight::from_parts(0, 10).saturating_mul(r.into())) } /// Storage: System Account (r:1 w:0) /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: Measured) @@ -2875,18 +2874,18 @@ impl WeightInfo for () { fn seal_deposit_event_per_topic_and_byte(t: u32, n: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `797 + t * (32 ±0)` - // Estimated: `21840 + t * (2640 ±0)` - // Minimum execution time: 251_638_000 picoseconds. - Weight::from_parts(244_791_817, 21840) - // Standard Error: 89_537 - .saturating_add(Weight::from_parts(2_459_492, 0).saturating_mul(t.into())) - // Standard Error: 25 - .saturating_add(Weight::from_parts(626, 0).saturating_mul(n.into())) + // Estimated: `6744 + t * (2508 ±0)` + // Minimum execution time: 250_301_000 picoseconds. + Weight::from_parts(245_292_258, 6744) + // Standard Error: 29_864 + .saturating_add(Weight::from_parts(2_163_531, 0).saturating_mul(t.into())) + // Standard Error: 8 + .saturating_add(Weight::from_parts(583, 0).saturating_mul(n.into())) .saturating_add(RocksDbWeight::get().reads(6_u64)) .saturating_add(RocksDbWeight::get().reads((1_u64).saturating_mul(t.into()))) .saturating_add(RocksDbWeight::get().writes(3_u64)) .saturating_add(RocksDbWeight::get().writes((1_u64).saturating_mul(t.into()))) - .saturating_add(Weight::from_parts(0, 2640).saturating_mul(t.into())) + .saturating_add(Weight::from_parts(0, 2508).saturating_mul(t.into())) } /// Storage: System Account (r:1 w:0) /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: Measured) @@ -2902,14 +2901,14 @@ impl WeightInfo for () { fn seal_debug_message(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `777 + r * (7 ±0)` - // Estimated: `21725 + r * (35 ±0)` - // Minimum execution time: 164_736_000 picoseconds. - Weight::from_parts(164_496_597, 21725) - // Standard Error: 4_619 - .saturating_add(Weight::from_parts(252_013, 0).saturating_mul(r.into())) + // Estimated: `6721 + r * (7 ±0)` + // Minimum execution time: 165_711_000 picoseconds. + Weight::from_parts(168_792_571, 6721) + // Standard Error: 216 + .saturating_add(Weight::from_parts(230_285, 0).saturating_mul(r.into())) .saturating_add(RocksDbWeight::get().reads(6_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) - .saturating_add(Weight::from_parts(0, 35).saturating_mul(r.into())) + .saturating_add(Weight::from_parts(0, 7).saturating_mul(r.into())) } /// Storage: System Account (r:1 w:0) /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) @@ -2925,11 +2924,11 @@ impl WeightInfo for () { fn seal_debug_message_per_byte(i: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `125728` - // Estimated: `269977` - // Minimum execution time: 352_146_000 picoseconds. - Weight::from_parts(357_758_251, 269977) - // Standard Error: 1 - .saturating_add(Weight::from_parts(735, 0).saturating_mul(i.into())) + // Estimated: `131670` + // Minimum execution time: 348_928_000 picoseconds. + Weight::from_parts(352_224_793, 131670) + // Standard Error: 0 + .saturating_add(Weight::from_parts(731, 0).saturating_mul(i.into())) .saturating_add(RocksDbWeight::get().reads(6_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) } @@ -2940,10 +2939,10 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `845 + r * (292 ±0)` // Estimated: `843 + r * (293 ±0)` - // Minimum execution time: 236_170_000 picoseconds. - Weight::from_parts(136_284_582, 843) - // Standard Error: 10_269 - .saturating_add(Weight::from_parts(5_995_228, 0).saturating_mul(r.into())) + // Minimum execution time: 236_418_000 picoseconds. + Weight::from_parts(129_862_840, 843) + // Standard Error: 9_733 + .saturating_add(Weight::from_parts(6_005_187, 0).saturating_mul(r.into())) .saturating_add(RocksDbWeight::get().reads(6_u64)) .saturating_add(RocksDbWeight::get().reads((1_u64).saturating_mul(r.into()))) .saturating_add(RocksDbWeight::get().writes(3_u64)) @@ -2957,10 +2956,10 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `1304` // Estimated: `1280` - // Minimum execution time: 253_837_000 picoseconds. - Weight::from_parts(287_029_261, 1280) - // Standard Error: 51 - .saturating_add(Weight::from_parts(534, 0).saturating_mul(n.into())) + // Minimum execution time: 251_599_000 picoseconds. + Weight::from_parts(285_284_665, 1280) + // Standard Error: 46 + .saturating_add(Weight::from_parts(410, 0).saturating_mul(n.into())) .saturating_add(RocksDbWeight::get().reads(8_u64)) .saturating_add(RocksDbWeight::get().writes(6_u64)) } @@ -2971,10 +2970,10 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `1167 + n * (1 ±0)` // Estimated: `1167 + n * (1 ±0)` - // Minimum execution time: 253_037_000 picoseconds. - Weight::from_parts(256_401_533, 1167) - // Standard Error: 18 - .saturating_add(Weight::from_parts(32, 0).saturating_mul(n.into())) + // Minimum execution time: 251_309_000 picoseconds. + Weight::from_parts(253_555_552, 1167) + // Standard Error: 9 + .saturating_add(Weight::from_parts(27, 0).saturating_mul(n.into())) .saturating_add(RocksDbWeight::get().reads(7_u64)) .saturating_add(RocksDbWeight::get().writes(4_u64)) .saturating_add(Weight::from_parts(0, 1).saturating_mul(n.into())) @@ -2986,10 +2985,10 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `841 + r * (288 ±0)` // Estimated: `845 + r * (289 ±0)` - // Minimum execution time: 236_800_000 picoseconds. - Weight::from_parts(134_748_031, 845) - // Standard Error: 9_560 - .saturating_add(Weight::from_parts(5_923_187, 0).saturating_mul(r.into())) + // Minimum execution time: 235_441_000 picoseconds. + Weight::from_parts(132_980_942, 845) + // Standard Error: 9_421 + .saturating_add(Weight::from_parts(5_854_896, 0).saturating_mul(r.into())) .saturating_add(RocksDbWeight::get().reads(6_u64)) .saturating_add(RocksDbWeight::get().reads((1_u64).saturating_mul(r.into()))) .saturating_add(RocksDbWeight::get().writes(3_u64)) @@ -3003,9 +3002,9 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `1163 + n * (1 ±0)` // Estimated: `1163 + n * (1 ±0)` - // Minimum execution time: 250_601_000 picoseconds. - Weight::from_parts(253_618_803, 1163) - // Standard Error: 18 + // Minimum execution time: 249_967_000 picoseconds. + Weight::from_parts(252_122_186, 1163) + // Standard Error: 10 .saturating_add(Weight::from_parts(74, 0).saturating_mul(n.into())) .saturating_add(RocksDbWeight::get().reads(7_u64)) .saturating_add(RocksDbWeight::get().writes(4_u64)) @@ -3018,10 +3017,10 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `835 + r * (296 ±0)` // Estimated: `840 + r * (297 ±0)` - // Minimum execution time: 236_194_000 picoseconds. - Weight::from_parts(152_630_909, 840) - // Standard Error: 8_573 - .saturating_add(Weight::from_parts(4_896_099, 0).saturating_mul(r.into())) + // Minimum execution time: 235_647_000 picoseconds. + Weight::from_parts(145_525_169, 840) + // Standard Error: 8_553 + .saturating_add(Weight::from_parts(4_948_021, 0).saturating_mul(r.into())) .saturating_add(RocksDbWeight::get().reads(6_u64)) .saturating_add(RocksDbWeight::get().reads((1_u64).saturating_mul(r.into()))) .saturating_add(RocksDbWeight::get().writes(3_u64)) @@ -3034,10 +3033,10 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `1179 + n * (1 ±0)` // Estimated: `1179 + n * (1 ±0)` - // Minimum execution time: 250_989_000 picoseconds. - Weight::from_parts(260_170_747, 1179) - // Standard Error: 162 - .saturating_add(Weight::from_parts(224, 0).saturating_mul(n.into())) + // Minimum execution time: 249_576_000 picoseconds. + Weight::from_parts(250_747_191, 1179) + // Standard Error: 8 + .saturating_add(Weight::from_parts(717, 0).saturating_mul(n.into())) .saturating_add(RocksDbWeight::get().reads(7_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) .saturating_add(Weight::from_parts(0, 1).saturating_mul(n.into())) @@ -3049,10 +3048,10 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `856 + r * (288 ±0)` // Estimated: `857 + r * (289 ±0)` - // Minimum execution time: 236_574_000 picoseconds. - Weight::from_parts(155_573_955, 857) - // Standard Error: 10_270 - .saturating_add(Weight::from_parts(4_667_644, 0).saturating_mul(r.into())) + // Minimum execution time: 236_110_000 picoseconds. + Weight::from_parts(148_420_625, 857) + // Standard Error: 8_175 + .saturating_add(Weight::from_parts(4_684_126, 0).saturating_mul(r.into())) .saturating_add(RocksDbWeight::get().reads(6_u64)) .saturating_add(RocksDbWeight::get().reads((1_u64).saturating_mul(r.into()))) .saturating_add(RocksDbWeight::get().writes(3_u64)) @@ -3065,10 +3064,10 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `1166 + n * (1 ±0)` // Estimated: `1166 + n * (1 ±0)` - // Minimum execution time: 249_502_000 picoseconds. - Weight::from_parts(251_496_311, 1166) - // Standard Error: 26 - .saturating_add(Weight::from_parts(186, 0).saturating_mul(n.into())) + // Minimum execution time: 247_800_000 picoseconds. + Weight::from_parts(249_410_575, 1166) + // Standard Error: 6 + .saturating_add(Weight::from_parts(99, 0).saturating_mul(n.into())) .saturating_add(RocksDbWeight::get().reads(7_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) .saturating_add(Weight::from_parts(0, 1).saturating_mul(n.into())) @@ -3080,10 +3079,10 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `829 + r * (296 ±0)` // Estimated: `836 + r * (297 ±0)` - // Minimum execution time: 236_030_000 picoseconds. - Weight::from_parts(128_595_856, 836) - // Standard Error: 10_530 - .saturating_add(Weight::from_parts(6_072_638, 0).saturating_mul(r.into())) + // Minimum execution time: 235_251_000 picoseconds. + Weight::from_parts(128_816_707, 836) + // Standard Error: 9_887 + .saturating_add(Weight::from_parts(6_167_176, 0).saturating_mul(r.into())) .saturating_add(RocksDbWeight::get().reads(6_u64)) .saturating_add(RocksDbWeight::get().reads((1_u64).saturating_mul(r.into()))) .saturating_add(RocksDbWeight::get().writes(3_u64)) @@ -3097,10 +3096,10 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `1180 + n * (1 ±0)` // Estimated: `1180 + n * (1 ±0)` - // Minimum execution time: 254_027_000 picoseconds. - Weight::from_parts(260_739_475, 1180) - // Standard Error: 115 - .saturating_add(Weight::from_parts(330, 0).saturating_mul(n.into())) + // Minimum execution time: 250_401_000 picoseconds. + Weight::from_parts(253_298_243, 1180) + // Standard Error: 9 + .saturating_add(Weight::from_parts(667, 0).saturating_mul(n.into())) .saturating_add(RocksDbWeight::get().reads(7_u64)) .saturating_add(RocksDbWeight::get().writes(4_u64)) .saturating_add(Weight::from_parts(0, 1).saturating_mul(n.into())) @@ -3119,16 +3118,16 @@ impl WeightInfo for () { fn seal_transfer(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `1373 + r * (45 ±0)` - // Estimated: `26753 + r * (2700 ±0)` - // Minimum execution time: 237_634_000 picoseconds. - Weight::from_parts(238_177_000, 26753) - // Standard Error: 23_320 - .saturating_add(Weight::from_parts(35_467_618, 0).saturating_mul(r.into())) + // Estimated: `7270 + r * (2520 ±0)` + // Minimum execution time: 236_470_000 picoseconds. + Weight::from_parts(98_898_727, 7270) + // Standard Error: 33_316 + .saturating_add(Weight::from_parts(35_149_946, 0).saturating_mul(r.into())) .saturating_add(RocksDbWeight::get().reads(7_u64)) .saturating_add(RocksDbWeight::get().reads((1_u64).saturating_mul(r.into()))) .saturating_add(RocksDbWeight::get().writes(4_u64)) .saturating_add(RocksDbWeight::get().writes((1_u64).saturating_mul(r.into()))) - .saturating_add(Weight::from_parts(0, 2700).saturating_mul(r.into())) + .saturating_add(Weight::from_parts(0, 2520).saturating_mul(r.into())) } /// Storage: System Account (r:1 w:0) /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: Measured) @@ -3144,16 +3143,16 @@ impl WeightInfo for () { fn seal_call(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `1237 + r * (256 ±0)` - // Estimated: `26028 + r * (6235 ±0)` - // Minimum execution time: 237_076_000 picoseconds. - Weight::from_parts(237_792_000, 26028) - // Standard Error: 91_566 - .saturating_add(Weight::from_parts(212_893_975, 0).saturating_mul(r.into())) + // Estimated: `7125 + r * (2732 ±0)` + // Minimum execution time: 238_303_000 picoseconds. + Weight::from_parts(239_024_000, 7125) + // Standard Error: 65_907 + .saturating_add(Weight::from_parts(209_419_071, 0).saturating_mul(r.into())) .saturating_add(RocksDbWeight::get().reads(7_u64)) .saturating_add(RocksDbWeight::get().reads((2_u64).saturating_mul(r.into()))) .saturating_add(RocksDbWeight::get().writes(3_u64)) .saturating_add(RocksDbWeight::get().writes((2_u64).saturating_mul(r.into()))) - .saturating_add(Weight::from_parts(0, 6235).saturating_mul(r.into())) + .saturating_add(Weight::from_parts(0, 2732).saturating_mul(r.into())) } /// Storage: System Account (r:1 w:0) /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: Measured) @@ -3169,16 +3168,16 @@ impl WeightInfo for () { fn seal_delegate_call(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0 + r * (502 ±0)` - // Estimated: `21755 + r * (6329 ±3)` - // Minimum execution time: 236_275_000 picoseconds. - Weight::from_parts(236_849_000, 21755) - // Standard Error: 92_724 - .saturating_add(Weight::from_parts(207_159_396, 0).saturating_mul(r.into())) + // Estimated: `6727 + r * (2572 ±10)` + // Minimum execution time: 235_961_000 picoseconds. + Weight::from_parts(236_939_000, 6727) + // Standard Error: 83_087 + .saturating_add(Weight::from_parts(205_646_517, 0).saturating_mul(r.into())) .saturating_add(RocksDbWeight::get().reads(6_u64)) .saturating_add(RocksDbWeight::get().reads((2_u64).saturating_mul(r.into()))) .saturating_add(RocksDbWeight::get().writes(3_u64)) .saturating_add(RocksDbWeight::get().writes((1_u64).saturating_mul(r.into()))) - .saturating_add(Weight::from_parts(0, 6329).saturating_mul(r.into())) + .saturating_add(Weight::from_parts(0, 2572).saturating_mul(r.into())) } /// Storage: System Account (r:3 w:2) /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: Measured) @@ -3195,18 +3194,18 @@ impl WeightInfo for () { fn seal_call_per_transfer_clone_byte(t: u32, c: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `1154 + t * (204 ±0)` - // Estimated: `31015 + t * (5970 ±0)` - // Minimum execution time: 412_628_000 picoseconds. - Weight::from_parts(385_108_035, 31015) - // Standard Error: 1_002_078 - .saturating_add(Weight::from_parts(31_204_678, 0).saturating_mul(t.into())) - // Standard Error: 1 - .saturating_add(Weight::from_parts(598, 0).saturating_mul(c.into())) + // Estimated: `9569 + t * (5154 ±0)` + // Minimum execution time: 410_156_000 picoseconds. + Weight::from_parts(378_378_143, 9569) + // Standard Error: 285_172 + .saturating_add(Weight::from_parts(34_736_740, 0).saturating_mul(t.into())) + // Standard Error: 0 + .saturating_add(Weight::from_parts(591, 0).saturating_mul(c.into())) .saturating_add(RocksDbWeight::get().reads(9_u64)) .saturating_add(RocksDbWeight::get().reads((2_u64).saturating_mul(t.into()))) .saturating_add(RocksDbWeight::get().writes(5_u64)) .saturating_add(RocksDbWeight::get().writes((2_u64).saturating_mul(t.into()))) - .saturating_add(Weight::from_parts(0, 5970).saturating_mul(t.into())) + .saturating_add(Weight::from_parts(0, 5154).saturating_mul(t.into())) } /// Storage: System Account (r:1602 w:1602) /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: Measured) @@ -3226,16 +3225,16 @@ impl WeightInfo for () { fn seal_instantiate(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `1301 + r * (254 ±0)` - // Estimated: `30977 + r * (16635 ±0)` - // Minimum execution time: 237_161_000 picoseconds. - Weight::from_parts(237_620_000, 30977) - // Standard Error: 258_036 - .saturating_add(Weight::from_parts(344_869_637, 0).saturating_mul(r.into())) + // Estimated: `7131 + r * (5205 ±0)` + // Minimum execution time: 236_748_000 picoseconds. + Weight::from_parts(237_129_000, 7131) + // Standard Error: 280_059 + .saturating_add(Weight::from_parts(341_428_013, 0).saturating_mul(r.into())) .saturating_add(RocksDbWeight::get().reads(8_u64)) .saturating_add(RocksDbWeight::get().reads((6_u64).saturating_mul(r.into()))) .saturating_add(RocksDbWeight::get().writes(6_u64)) .saturating_add(RocksDbWeight::get().writes((5_u64).saturating_mul(r.into()))) - .saturating_add(Weight::from_parts(0, 16635).saturating_mul(r.into())) + .saturating_add(Weight::from_parts(0, 5205).saturating_mul(r.into())) } /// Storage: System Account (r:4 w:4) /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: Measured) @@ -3257,20 +3256,20 @@ impl WeightInfo for () { fn seal_instantiate_per_transfer_input_salt_byte(t: u32, i: u32, s: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `1071 + t * (187 ±0)` - // Estimated: `42684 + t * (3588 ±2)` - // Minimum execution time: 1_613_172_000 picoseconds. - Weight::from_parts(326_952_137, 42684) - // Standard Error: 4_738_925 - .saturating_add(Weight::from_parts(113_657_996, 0).saturating_mul(t.into())) - // Standard Error: 7 - .saturating_add(Weight::from_parts(1_173, 0).saturating_mul(i.into())) - // Standard Error: 7 - .saturating_add(Weight::from_parts(1_347, 0).saturating_mul(s.into())) + // Estimated: `9492 + t * (2634 ±2)` + // Minimum execution time: 1_613_796_000 picoseconds. + Weight::from_parts(340_002_206, 9492) + // Standard Error: 4_296_381 + .saturating_add(Weight::from_parts(115_239_834, 0).saturating_mul(t.into())) + // Standard Error: 6 + .saturating_add(Weight::from_parts(1_145, 0).saturating_mul(i.into())) + // Standard Error: 6 + .saturating_add(Weight::from_parts(1_315, 0).saturating_mul(s.into())) .saturating_add(RocksDbWeight::get().reads(13_u64)) .saturating_add(RocksDbWeight::get().reads((1_u64).saturating_mul(t.into()))) .saturating_add(RocksDbWeight::get().writes(10_u64)) .saturating_add(RocksDbWeight::get().writes((1_u64).saturating_mul(t.into()))) - .saturating_add(Weight::from_parts(0, 3588).saturating_mul(t.into())) + .saturating_add(Weight::from_parts(0, 2634).saturating_mul(t.into())) } /// Storage: System Account (r:1 w:0) /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: Measured) @@ -3286,14 +3285,14 @@ impl WeightInfo for () { fn seal_hash_sha2_256(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `777 + r * (8 ±0)` - // Estimated: `21710 + r * (40 ±0)` - // Minimum execution time: 233_679_000 picoseconds. - Weight::from_parts(238_638_820, 21710) - // Standard Error: 681 - .saturating_add(Weight::from_parts(573_320, 0).saturating_mul(r.into())) + // Estimated: `6718 + r * (8 ±0)` + // Minimum execution time: 233_111_000 picoseconds. + Weight::from_parts(238_643_933, 6718) + // Standard Error: 184 + .saturating_add(Weight::from_parts(572_296, 0).saturating_mul(r.into())) .saturating_add(RocksDbWeight::get().reads(6_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) - .saturating_add(Weight::from_parts(0, 40).saturating_mul(r.into())) + .saturating_add(Weight::from_parts(0, 8).saturating_mul(r.into())) } /// Storage: System Account (r:1 w:0) /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: Measured) @@ -3309,11 +3308,11 @@ impl WeightInfo for () { fn seal_hash_sha2_256_per_byte(n: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `785` - // Estimated: `21745` - // Minimum execution time: 235_992_000 picoseconds. - Weight::from_parts(219_924_252, 21745) - // Standard Error: 5 - .saturating_add(Weight::from_parts(3_978, 0).saturating_mul(n.into())) + // Estimated: `6725` + // Minimum execution time: 234_746_000 picoseconds. + Weight::from_parts(229_815_552, 6725) + // Standard Error: 1 + .saturating_add(Weight::from_parts(3_892, 0).saturating_mul(n.into())) .saturating_add(RocksDbWeight::get().reads(6_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) } @@ -3331,14 +3330,14 @@ impl WeightInfo for () { fn seal_hash_keccak_256(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `779 + r * (8 ±0)` - // Estimated: `21725 + r * (40 ±0)` - // Minimum execution time: 240_661_000 picoseconds. - Weight::from_parts(238_809_422, 21725) - // Standard Error: 820 - .saturating_add(Weight::from_parts(741_326, 0).saturating_mul(r.into())) + // Estimated: `6721 + r * (8 ±0)` + // Minimum execution time: 232_732_000 picoseconds. + Weight::from_parts(239_007_209, 6721) + // Standard Error: 256 + .saturating_add(Weight::from_parts(733_879, 0).saturating_mul(r.into())) .saturating_add(RocksDbWeight::get().reads(6_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) - .saturating_add(Weight::from_parts(0, 40).saturating_mul(r.into())) + .saturating_add(Weight::from_parts(0, 8).saturating_mul(r.into())) } /// Storage: System Account (r:1 w:0) /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: Measured) @@ -3354,11 +3353,11 @@ impl WeightInfo for () { fn seal_hash_keccak_256_per_byte(n: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `787` - // Estimated: `21765` - // Minimum execution time: 235_136_000 picoseconds. - Weight::from_parts(228_678_487, 21765) - // Standard Error: 2 - .saturating_add(Weight::from_parts(3_164, 0).saturating_mul(n.into())) + // Estimated: `6729` + // Minimum execution time: 234_184_000 picoseconds. + Weight::from_parts(227_603_375, 6729) + // Standard Error: 1 + .saturating_add(Weight::from_parts(3_127, 0).saturating_mul(n.into())) .saturating_add(RocksDbWeight::get().reads(6_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) } @@ -3376,14 +3375,14 @@ impl WeightInfo for () { fn seal_hash_blake2_256(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `779 + r * (8 ±0)` - // Estimated: `21740 + r * (40 ±0)` - // Minimum execution time: 233_364_000 picoseconds. - Weight::from_parts(243_294_285, 21740) - // Standard Error: 1_607 - .saturating_add(Weight::from_parts(413_186, 0).saturating_mul(r.into())) + // Estimated: `6724 + r * (8 ±0)` + // Minimum execution time: 233_038_000 picoseconds. + Weight::from_parts(238_515_817, 6724) + // Standard Error: 255 + .saturating_add(Weight::from_parts(413_343, 0).saturating_mul(r.into())) .saturating_add(RocksDbWeight::get().reads(6_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) - .saturating_add(Weight::from_parts(0, 40).saturating_mul(r.into())) + .saturating_add(Weight::from_parts(0, 8).saturating_mul(r.into())) } /// Storage: System Account (r:1 w:0) /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: Measured) @@ -3399,11 +3398,11 @@ impl WeightInfo for () { fn seal_hash_blake2_256_per_byte(n: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `787` - // Estimated: `21785` - // Minimum execution time: 234_315_000 picoseconds. - Weight::from_parts(225_569_537, 21785) + // Estimated: `6733` + // Minimum execution time: 232_996_000 picoseconds. + Weight::from_parts(226_706_997, 6733) // Standard Error: 1 - .saturating_add(Weight::from_parts(919, 0).saturating_mul(n.into())) + .saturating_add(Weight::from_parts(908, 0).saturating_mul(n.into())) .saturating_add(RocksDbWeight::get().reads(6_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) } @@ -3421,14 +3420,14 @@ impl WeightInfo for () { fn seal_hash_blake2_128(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `779 + r * (8 ±0)` - // Estimated: `21745 + r * (40 ±0)` - // Minimum execution time: 233_288_000 picoseconds. - Weight::from_parts(239_289_154, 21745) - // Standard Error: 612 - .saturating_add(Weight::from_parts(414_275, 0).saturating_mul(r.into())) + // Estimated: `6725 + r * (8 ±0)` + // Minimum execution time: 232_292_000 picoseconds. + Weight::from_parts(237_997_001, 6725) + // Standard Error: 219 + .saturating_add(Weight::from_parts(410_177, 0).saturating_mul(r.into())) .saturating_add(RocksDbWeight::get().reads(6_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) - .saturating_add(Weight::from_parts(0, 40).saturating_mul(r.into())) + .saturating_add(Weight::from_parts(0, 8).saturating_mul(r.into())) } /// Storage: System Account (r:1 w:0) /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: Measured) @@ -3444,11 +3443,11 @@ impl WeightInfo for () { fn seal_hash_blake2_128_per_byte(n: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `787` - // Estimated: `21755` - // Minimum execution time: 233_686_000 picoseconds. - Weight::from_parts(227_406_694, 21755) + // Estimated: `6727` + // Minimum execution time: 234_815_000 picoseconds. + Weight::from_parts(226_317_150, 6727) // Standard Error: 1 - .saturating_add(Weight::from_parts(919, 0).saturating_mul(n.into())) + .saturating_add(Weight::from_parts(911, 0).saturating_mul(n.into())) .saturating_add(RocksDbWeight::get().reads(6_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) } @@ -3465,15 +3464,15 @@ impl WeightInfo for () { /// The range of component `n` is `[0, 125697]`. fn seal_sr25519_verify_per_byte(n: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `916 + n * (1 ±0)` - // Estimated: `22375 + n * (5 ±0)` - // Minimum execution time: 287_258_000 picoseconds. - Weight::from_parts(290_941_609, 22375) - // Standard Error: 8 - .saturating_add(Weight::from_parts(4_785, 0).saturating_mul(n.into())) + // Measured: `912 + n * (1 ±0)` + // Estimated: `6848 + n * (1 ±0)` + // Minimum execution time: 286_323_000 picoseconds. + Weight::from_parts(290_287_955, 6848) + // Standard Error: 1 + .saturating_add(Weight::from_parts(4_693, 0).saturating_mul(n.into())) .saturating_add(RocksDbWeight::get().reads(6_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) - .saturating_add(Weight::from_parts(0, 5).saturating_mul(n.into())) + .saturating_add(Weight::from_parts(0, 1).saturating_mul(n.into())) } /// Storage: System Account (r:1 w:0) /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: Measured) @@ -3488,15 +3487,15 @@ impl WeightInfo for () { /// The range of component `r` is `[0, 160]`. fn seal_sr25519_verify(r: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `728 + r * (112 ±0)` - // Estimated: `21455 + r * (560 ±0)` - // Minimum execution time: 238_340_000 picoseconds. - Weight::from_parts(246_009_851, 21455) - // Standard Error: 21_677 - .saturating_add(Weight::from_parts(48_139_487, 0).saturating_mul(r.into())) + // Measured: `727 + r * (112 ±0)` + // Estimated: `6666 + r * (112 ±0)` + // Minimum execution time: 235_938_000 picoseconds. + Weight::from_parts(242_728_358, 6666) + // Standard Error: 9_725 + .saturating_add(Weight::from_parts(47_527_740, 0).saturating_mul(r.into())) .saturating_add(RocksDbWeight::get().reads(6_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) - .saturating_add(Weight::from_parts(0, 560).saturating_mul(r.into())) + .saturating_add(Weight::from_parts(0, 112).saturating_mul(r.into())) } /// Storage: System Account (r:1 w:0) /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: Measured) @@ -3512,14 +3511,14 @@ impl WeightInfo for () { fn seal_ecdsa_recover(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `822 + r * (76 ±0)` - // Estimated: `21705 + r * (385 ±0)` - // Minimum execution time: 236_244_000 picoseconds. - Weight::from_parts(253_749_242, 21705) - // Standard Error: 19_216 - .saturating_add(Weight::from_parts(37_706_782, 0).saturating_mul(r.into())) + // Estimated: `6716 + r * (77 ±0)` + // Minimum execution time: 236_108_000 picoseconds. + Weight::from_parts(248_577_226, 6716) + // Standard Error: 9_565 + .saturating_add(Weight::from_parts(36_733_552, 0).saturating_mul(r.into())) .saturating_add(RocksDbWeight::get().reads(6_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) - .saturating_add(Weight::from_parts(0, 385).saturating_mul(r.into())) + .saturating_add(Weight::from_parts(0, 77).saturating_mul(r.into())) } /// Storage: System Account (r:1 w:0) /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: Measured) @@ -3535,14 +3534,14 @@ impl WeightInfo for () { fn seal_ecdsa_to_eth_address(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `792 + r * (42 ±0)` - // Estimated: `21775 + r * (210 ±0)` - // Minimum execution time: 235_770_000 picoseconds. - Weight::from_parts(230_780_347, 21775) - // Standard Error: 12_015 - .saturating_add(Weight::from_parts(9_584_947, 0).saturating_mul(r.into())) + // Estimated: `6731 + r * (42 ±0)` + // Minimum execution time: 236_440_000 picoseconds. + Weight::from_parts(240_771_418, 6731) + // Standard Error: 1_849 + .saturating_add(Weight::from_parts(9_185_896, 0).saturating_mul(r.into())) .saturating_add(RocksDbWeight::get().reads(6_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) - .saturating_add(Weight::from_parts(0, 210).saturating_mul(r.into())) + .saturating_add(Weight::from_parts(0, 42).saturating_mul(r.into())) } /// Storage: System Account (r:1 w:0) /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: Measured) @@ -3560,16 +3559,16 @@ impl WeightInfo for () { fn seal_set_code_hash(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0 + r * (964 ±0)` - // Estimated: `29920 + r * (11544 ±7)` - // Minimum execution time: 235_857_000 picoseconds. - Weight::from_parts(236_482_000, 29920) - // Standard Error: 47_818 - .saturating_add(Weight::from_parts(21_765_273, 0).saturating_mul(r.into())) + // Estimated: `8190 + r * (3090 ±10)` + // Minimum execution time: 235_056_000 picoseconds. + Weight::from_parts(235_743_000, 8190) + // Standard Error: 46_122 + .saturating_add(Weight::from_parts(21_447_984, 0).saturating_mul(r.into())) .saturating_add(RocksDbWeight::get().reads(6_u64)) .saturating_add(RocksDbWeight::get().reads((3_u64).saturating_mul(r.into()))) .saturating_add(RocksDbWeight::get().writes(3_u64)) .saturating_add(RocksDbWeight::get().writes((2_u64).saturating_mul(r.into()))) - .saturating_add(Weight::from_parts(0, 11544).saturating_mul(r.into())) + .saturating_add(Weight::from_parts(0, 3090).saturating_mul(r.into())) } /// Storage: System Account (r:1 w:0) /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: Measured) @@ -3585,14 +3584,14 @@ impl WeightInfo for () { fn seal_reentrance_count(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `773 + r * (3 ±0)` - // Estimated: `21735 + r * (15 ±0)` - // Minimum execution time: 235_388_000 picoseconds. - Weight::from_parts(240_149_512, 21735) - // Standard Error: 555 - .saturating_add(Weight::from_parts(162_666, 0).saturating_mul(r.into())) + // Estimated: `6723 + r * (3 ±0)` + // Minimum execution time: 235_213_000 picoseconds. + Weight::from_parts(239_456_464, 6723) + // Standard Error: 130 + .saturating_add(Weight::from_parts(159_851, 0).saturating_mul(r.into())) .saturating_add(RocksDbWeight::get().reads(6_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) - .saturating_add(Weight::from_parts(0, 15).saturating_mul(r.into())) + .saturating_add(Weight::from_parts(0, 3).saturating_mul(r.into())) } /// Storage: System Account (r:1 w:0) /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: Measured) @@ -3608,14 +3607,14 @@ impl WeightInfo for () { fn seal_account_reentrance_count(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `1975 + r * (39 ±0)` - // Estimated: `27145 + r * (200 ±0)` - // Minimum execution time: 237_485_000 picoseconds. - Weight::from_parts(268_044_053, 27145) - // Standard Error: 1_147 - .saturating_add(Weight::from_parts(260_572, 0).saturating_mul(r.into())) + // Estimated: `7805 + r * (40 ±0)` + // Minimum execution time: 237_886_000 picoseconds. + Weight::from_parts(262_430_157, 7805) + // Standard Error: 939 + .saturating_add(Weight::from_parts(260_005, 0).saturating_mul(r.into())) .saturating_add(RocksDbWeight::get().reads(6_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) - .saturating_add(Weight::from_parts(0, 200).saturating_mul(r.into())) + .saturating_add(Weight::from_parts(0, 40).saturating_mul(r.into())) } /// Storage: System Account (r:1 w:0) /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: Measured) @@ -3633,523 +3632,523 @@ impl WeightInfo for () { fn seal_instantiation_nonce(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `776 + r * (3 ±0)` - // Estimated: `24004 + r * (18 ±0)` - // Minimum execution time: 234_123_000 picoseconds. - Weight::from_parts(245_872_832, 24004) - // Standard Error: 943 - .saturating_add(Weight::from_parts(136_460, 0).saturating_mul(r.into())) + // Estimated: `6723 + r * (3 ±0)` + // Minimum execution time: 234_014_000 picoseconds. + Weight::from_parts(240_042_671, 6723) + // Standard Error: 152 + .saturating_add(Weight::from_parts(138_382, 0).saturating_mul(r.into())) .saturating_add(RocksDbWeight::get().reads(7_u64)) .saturating_add(RocksDbWeight::get().writes(4_u64)) - .saturating_add(Weight::from_parts(0, 18).saturating_mul(r.into())) + .saturating_add(Weight::from_parts(0, 3).saturating_mul(r.into())) } /// The range of component `r` is `[0, 5000]`. fn instr_i64const(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_606_000 picoseconds. - Weight::from_parts(1_845_698, 0) + // Minimum execution time: 1_533_000 picoseconds. + Weight::from_parts(1_846_015, 0) // Standard Error: 2 - .saturating_add(Weight::from_parts(2_995, 0).saturating_mul(r.into())) + .saturating_add(Weight::from_parts(2_935, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 5000]`. fn instr_i64load(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_711_000 picoseconds. - Weight::from_parts(2_281_581, 0) - // Standard Error: 4 - .saturating_add(Weight::from_parts(6_354, 0).saturating_mul(r.into())) + // Minimum execution time: 1_671_000 picoseconds. + Weight::from_parts(2_197_197, 0) + // Standard Error: 3 + .saturating_add(Weight::from_parts(6_335, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 5000]`. fn instr_i64store(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_680_000 picoseconds. - Weight::from_parts(2_269_238, 0) - // Standard Error: 4 - .saturating_add(Weight::from_parts(6_014, 0).saturating_mul(r.into())) + // Minimum execution time: 1_665_000 picoseconds. + Weight::from_parts(2_200_545, 0) + // Standard Error: 3 + .saturating_add(Weight::from_parts(6_011, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 5000]`. fn instr_select(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_592_000 picoseconds. - Weight::from_parts(2_010_963, 0) - // Standard Error: 3 - .saturating_add(Weight::from_parts(7_927, 0).saturating_mul(r.into())) + // Minimum execution time: 1_545_000 picoseconds. + Weight::from_parts(1_977_462, 0) + // Standard Error: 2 + .saturating_add(Weight::from_parts(7_901, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 5000]`. fn instr_if(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_631_000 picoseconds. - Weight::from_parts(1_936_549, 0) - // Standard Error: 6 - .saturating_add(Weight::from_parts(10_426, 0).saturating_mul(r.into())) + // Minimum execution time: 1_515_000 picoseconds. + Weight::from_parts(1_866_184, 0) + // Standard Error: 4 + .saturating_add(Weight::from_parts(10_514, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 5000]`. fn instr_br(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_616_000 picoseconds. - Weight::from_parts(1_938_716, 0) - // Standard Error: 15 - .saturating_add(Weight::from_parts(4_525, 0).saturating_mul(r.into())) + // Minimum execution time: 1_618_000 picoseconds. + Weight::from_parts(1_895_104, 0) + // Standard Error: 12 + .saturating_add(Weight::from_parts(4_523, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 5000]`. fn instr_br_if(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_535_000 picoseconds. - Weight::from_parts(1_109_860, 0) - // Standard Error: 77 - .saturating_add(Weight::from_parts(7_998, 0).saturating_mul(r.into())) + // Minimum execution time: 1_510_000 picoseconds. + Weight::from_parts(1_779_998, 0) + // Standard Error: 8 + .saturating_add(Weight::from_parts(6_832, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 5000]`. fn instr_br_table(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_594_000 picoseconds. - Weight::from_parts(1_478_654, 0) - // Standard Error: 36 - .saturating_add(Weight::from_parts(9_525, 0).saturating_mul(r.into())) + // Minimum execution time: 1_529_000 picoseconds. + Weight::from_parts(1_726_996, 0) + // Standard Error: 26 + .saturating_add(Weight::from_parts(9_199, 0).saturating_mul(r.into())) } /// The range of component `e` is `[1, 256]`. fn instr_br_table_per_entry(e: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_730_000 picoseconds. - Weight::from_parts(1_836_342, 0) - // Standard Error: 14 - .saturating_add(Weight::from_parts(206, 0).saturating_mul(e.into())) + // Minimum execution time: 1_682_000 picoseconds. + Weight::from_parts(1_789_910, 0) + // Standard Error: 16 + .saturating_add(Weight::from_parts(42, 0).saturating_mul(e.into())) } /// The range of component `r` is `[0, 5000]`. fn instr_call(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_607_000 picoseconds. - Weight::from_parts(2_150_133, 0) - // Standard Error: 17 - .saturating_add(Weight::from_parts(18_630, 0).saturating_mul(r.into())) + // Minimum execution time: 1_539_000 picoseconds. + Weight::from_parts(2_093_056, 0) + // Standard Error: 27 + .saturating_add(Weight::from_parts(18_917, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 5000]`. fn instr_call_indirect(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_824_000 picoseconds. - Weight::from_parts(3_119_825, 0) - // Standard Error: 15 - .saturating_add(Weight::from_parts(25_304, 0).saturating_mul(r.into())) + // Minimum execution time: 1_851_000 picoseconds. + Weight::from_parts(3_134_610, 0) + // Standard Error: 34 + .saturating_add(Weight::from_parts(24_714, 0).saturating_mul(r.into())) } /// The range of component `l` is `[0, 1024]`. fn instr_call_per_local(l: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_692_000 picoseconds. - Weight::from_parts(1_927_049, 0) + // Minimum execution time: 1_654_000 picoseconds. + Weight::from_parts(1_885_921, 0) // Standard Error: 14 - .saturating_add(Weight::from_parts(1_217, 0).saturating_mul(l.into())) + .saturating_add(Weight::from_parts(1_243, 0).saturating_mul(l.into())) } /// The range of component `r` is `[0, 5000]`. fn instr_local_get(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 2_795_000 picoseconds. - Weight::from_parts(3_107_843, 0) - // Standard Error: 2 - .saturating_add(Weight::from_parts(2_445, 0).saturating_mul(r.into())) + // Minimum execution time: 2_744_000 picoseconds. + Weight::from_parts(3_014_725, 0) + // Standard Error: 1 + .saturating_add(Weight::from_parts(2_447, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 5000]`. fn instr_local_set(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 2_815_000 picoseconds. - Weight::from_parts(3_114_618, 0) + // Minimum execution time: 2_881_000 picoseconds. + Weight::from_parts(3_137_711, 0) // Standard Error: 3 - .saturating_add(Weight::from_parts(3_639, 0).saturating_mul(r.into())) + .saturating_add(Weight::from_parts(3_608, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 5000]`. fn instr_local_tee(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 2_861_000 picoseconds. - Weight::from_parts(3_193_592, 0) + // Minimum execution time: 2_809_000 picoseconds. + Weight::from_parts(3_142_066, 0) // Standard Error: 3 - .saturating_add(Weight::from_parts(3_957, 0).saturating_mul(r.into())) + .saturating_add(Weight::from_parts(3_841, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 5000]`. fn instr_global_get(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_710_000 picoseconds. - Weight::from_parts(2_148_021, 0) - // Standard Error: 3 - .saturating_add(Weight::from_parts(8_391, 0).saturating_mul(r.into())) + // Minimum execution time: 1_704_000 picoseconds. + Weight::from_parts(2_083_619, 0) + // Standard Error: 2 + .saturating_add(Weight::from_parts(8_366, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 5000]`. fn instr_global_set(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_663_000 picoseconds. - Weight::from_parts(2_170_373, 0) - // Standard Error: 3 - .saturating_add(Weight::from_parts(8_812, 0).saturating_mul(r.into())) + // Minimum execution time: 1_652_000 picoseconds. + Weight::from_parts(2_048_256, 0) + // Standard Error: 5 + .saturating_add(Weight::from_parts(8_826, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 5000]`. fn instr_memory_current(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_662_000 picoseconds. - Weight::from_parts(2_601_398, 0) - // Standard Error: 198 - .saturating_add(Weight::from_parts(3_851, 0).saturating_mul(r.into())) + // Minimum execution time: 1_671_000 picoseconds. + Weight::from_parts(1_924_626, 0) + // Standard Error: 1 + .saturating_add(Weight::from_parts(3_746, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 16]`. fn instr_memory_grow(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_603_000 picoseconds. - Weight::from_parts(332_932, 0) - // Standard Error: 137_529 - .saturating_add(Weight::from_parts(13_233_734, 0).saturating_mul(r.into())) + // Minimum execution time: 1_585_000 picoseconds. + Weight::from_parts(490_856, 0) + // Standard Error: 133_673 + .saturating_add(Weight::from_parts(13_182_582, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 5000]`. fn instr_i64clz(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_613_000 picoseconds. - Weight::from_parts(1_922_391, 0) + // Minimum execution time: 1_533_000 picoseconds. + Weight::from_parts(1_851_563, 0) // Standard Error: 2 - .saturating_add(Weight::from_parts(3_903, 0).saturating_mul(r.into())) + .saturating_add(Weight::from_parts(3_820, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 5000]`. fn instr_i64ctz(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_522_000 picoseconds. - Weight::from_parts(1_920_023, 0) + // Minimum execution time: 1_564_000 picoseconds. + Weight::from_parts(1_914_178, 0) // Standard Error: 2 - .saturating_add(Weight::from_parts(3_876, 0).saturating_mul(r.into())) + .saturating_add(Weight::from_parts(3_732, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 5000]`. fn instr_i64popcnt(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_590_000 picoseconds. - Weight::from_parts(1_867_406, 0) - // Standard Error: 3 - .saturating_add(Weight::from_parts(3_910, 0).saturating_mul(r.into())) + // Minimum execution time: 1_559_000 picoseconds. + Weight::from_parts(1_886_992, 0) + // Standard Error: 2 + .saturating_add(Weight::from_parts(3_731, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 5000]`. fn instr_i64eqz(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_577_000 picoseconds. - Weight::from_parts(1_918_037, 0) + // Minimum execution time: 1_553_000 picoseconds. + Weight::from_parts(1_886_545, 0) // Standard Error: 2 - .saturating_add(Weight::from_parts(3_673, 0).saturating_mul(r.into())) + .saturating_add(Weight::from_parts(3_658, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 5000]`. fn instr_i64extendsi32(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_589_000 picoseconds. - Weight::from_parts(1_917_901, 0) - // Standard Error: 3 - .saturating_add(Weight::from_parts(3_956, 0).saturating_mul(r.into())) + // Minimum execution time: 1_507_000 picoseconds. + Weight::from_parts(1_853_647, 0) + // Standard Error: 2 + .saturating_add(Weight::from_parts(3_852, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 5000]`. fn instr_i64extendui32(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_573_000 picoseconds. - Weight::from_parts(1_902_324, 0) + // Minimum execution time: 1_554_000 picoseconds. + Weight::from_parts(1_868_877, 0) // Standard Error: 2 - .saturating_add(Weight::from_parts(3_865, 0).saturating_mul(r.into())) + .saturating_add(Weight::from_parts(3_806, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 5000]`. fn instr_i32wrapi64(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_586_000 picoseconds. - Weight::from_parts(1_953_738, 0) + // Minimum execution time: 1_514_000 picoseconds. + Weight::from_parts(1_882_233, 0) // Standard Error: 2 - .saturating_add(Weight::from_parts(3_838, 0).saturating_mul(r.into())) + .saturating_add(Weight::from_parts(3_700, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 5000]`. fn instr_i64eq(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_598_000 picoseconds. - Weight::from_parts(1_912_180, 0) + // Minimum execution time: 1_529_000 picoseconds. + Weight::from_parts(1_897_247, 0) // Standard Error: 2 - .saturating_add(Weight::from_parts(5_994, 0).saturating_mul(r.into())) + .saturating_add(Weight::from_parts(5_955, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 5000]`. fn instr_i64ne(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_573_000 picoseconds. - Weight::from_parts(1_943_725, 0) + // Minimum execution time: 1_513_000 picoseconds. + Weight::from_parts(1_922_333, 0) // Standard Error: 2 - .saturating_add(Weight::from_parts(5_998, 0).saturating_mul(r.into())) + .saturating_add(Weight::from_parts(5_933, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 5000]`. fn instr_i64lts(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_584_000 picoseconds. - Weight::from_parts(1_949_209, 0) + // Minimum execution time: 1_512_000 picoseconds. + Weight::from_parts(1_848_668, 0) // Standard Error: 2 - .saturating_add(Weight::from_parts(5_991, 0).saturating_mul(r.into())) + .saturating_add(Weight::from_parts(5_966, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 5000]`. fn instr_i64ltu(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_576_000 picoseconds. - Weight::from_parts(1_927_466, 0) + // Minimum execution time: 1_522_000 picoseconds. + Weight::from_parts(1_875_257, 0) // Standard Error: 2 - .saturating_add(Weight::from_parts(6_005, 0).saturating_mul(r.into())) + .saturating_add(Weight::from_parts(5_965, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 5000]`. fn instr_i64gts(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_566_000 picoseconds. - Weight::from_parts(2_004_312, 0) - // Standard Error: 3 - .saturating_add(Weight::from_parts(5_799, 0).saturating_mul(r.into())) + // Minimum execution time: 1_546_000 picoseconds. + Weight::from_parts(1_836_691, 0) + // Standard Error: 4 + .saturating_add(Weight::from_parts(5_842, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 5000]`. fn instr_i64gtu(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_538_000 picoseconds. - Weight::from_parts(1_922_703, 0) - // Standard Error: 9 - .saturating_add(Weight::from_parts(6_129, 0).saturating_mul(r.into())) + // Minimum execution time: 1_505_000 picoseconds. + Weight::from_parts(1_907_551, 0) + // Standard Error: 2 + .saturating_add(Weight::from_parts(6_075, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 5000]`. fn instr_i64les(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_575_000 picoseconds. - Weight::from_parts(1_978_271, 0) - // Standard Error: 3 - .saturating_add(Weight::from_parts(5_991, 0).saturating_mul(r.into())) + // Minimum execution time: 1_527_000 picoseconds. + Weight::from_parts(1_891_008, 0) + // Standard Error: 2 + .saturating_add(Weight::from_parts(5_971, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 5000]`. fn instr_i64leu(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_572_000 picoseconds. - Weight::from_parts(4_028_578, 0) - // Standard Error: 263 - .saturating_add(Weight::from_parts(5_784, 0).saturating_mul(r.into())) + // Minimum execution time: 1_556_000 picoseconds. + Weight::from_parts(1_910_864, 0) + // Standard Error: 2 + .saturating_add(Weight::from_parts(6_059, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 5000]`. fn instr_i64ges(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_615_000 picoseconds. - Weight::from_parts(1_962_065, 0) - // Standard Error: 3 - .saturating_add(Weight::from_parts(5_945, 0).saturating_mul(r.into())) + // Minimum execution time: 1_544_000 picoseconds. + Weight::from_parts(1_912_650, 0) + // Standard Error: 2 + .saturating_add(Weight::from_parts(5_943, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 5000]`. fn instr_i64geu(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_578_000 picoseconds. - Weight::from_parts(1_909_721, 0) + // Minimum execution time: 1_513_000 picoseconds. + Weight::from_parts(1_855_260, 0) // Standard Error: 2 - .saturating_add(Weight::from_parts(5_993, 0).saturating_mul(r.into())) + .saturating_add(Weight::from_parts(5_975, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 5000]`. fn instr_i64add(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_609_000 picoseconds. - Weight::from_parts(1_926_472, 0) - // Standard Error: 3 - .saturating_add(Weight::from_parts(5_888, 0).saturating_mul(r.into())) + // Minimum execution time: 1_521_000 picoseconds. + Weight::from_parts(1_867_259, 0) + // Standard Error: 2 + .saturating_add(Weight::from_parts(5_846, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 5000]`. fn instr_i64sub(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_620_000 picoseconds. - Weight::from_parts(2_875_196, 0) - // Standard Error: 177 - .saturating_add(Weight::from_parts(5_990, 0).saturating_mul(r.into())) + // Minimum execution time: 1_509_000 picoseconds. + Weight::from_parts(1_893_018, 0) + // Standard Error: 2 + .saturating_add(Weight::from_parts(6_096, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 5000]`. fn instr_i64mul(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_570_000 picoseconds. - Weight::from_parts(2_155_483, 0) - // Standard Error: 83 - .saturating_add(Weight::from_parts(5_675, 0).saturating_mul(r.into())) + // Minimum execution time: 1_496_000 picoseconds. + Weight::from_parts(1_886_659, 0) + // Standard Error: 2 + .saturating_add(Weight::from_parts(5_754, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 5000]`. fn instr_i64divs(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_558_000 picoseconds. - Weight::from_parts(2_108_263, 0) - // Standard Error: 5 - .saturating_add(Weight::from_parts(11_755, 0).saturating_mul(r.into())) + // Minimum execution time: 1_527_000 picoseconds. + Weight::from_parts(1_890_548, 0) + // Standard Error: 2 + .saturating_add(Weight::from_parts(11_842, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 5000]`. fn instr_i64divu(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_568_000 picoseconds. - Weight::from_parts(1_606_397, 0) - // Standard Error: 51 - .saturating_add(Weight::from_parts(11_011, 0).saturating_mul(r.into())) + // Minimum execution time: 1_518_000 picoseconds. + Weight::from_parts(1_891_903, 0) + // Standard Error: 4 + .saturating_add(Weight::from_parts(10_613, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 5000]`. fn instr_i64rems(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_630_000 picoseconds. - Weight::from_parts(2_872_487, 0) - // Standard Error: 24 - .saturating_add(Weight::from_parts(11_761, 0).saturating_mul(r.into())) + // Minimum execution time: 1_504_000 picoseconds. + Weight::from_parts(1_632_694, 0) + // Standard Error: 7 + .saturating_add(Weight::from_parts(12_281, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 5000]`. fn instr_i64remu(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_575_000 picoseconds. - Weight::from_parts(2_385_398, 0) - // Standard Error: 45 - .saturating_add(Weight::from_parts(10_650, 0).saturating_mul(r.into())) + // Minimum execution time: 1_507_000 picoseconds. + Weight::from_parts(1_878_413, 0) + // Standard Error: 2 + .saturating_add(Weight::from_parts(10_737, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 5000]`. fn instr_i64and(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_622_000 picoseconds. - Weight::from_parts(1_957_246, 0) + // Minimum execution time: 1_534_000 picoseconds. + Weight::from_parts(1_898_519, 0) // Standard Error: 2 - .saturating_add(Weight::from_parts(5_659, 0).saturating_mul(r.into())) + .saturating_add(Weight::from_parts(5_645, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 5000]`. fn instr_i64or(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_551_000 picoseconds. - Weight::from_parts(1_897_168, 0) - // Standard Error: 3 - .saturating_add(Weight::from_parts(5_774, 0).saturating_mul(r.into())) + // Minimum execution time: 1_503_000 picoseconds. + Weight::from_parts(1_895_532, 0) + // Standard Error: 2 + .saturating_add(Weight::from_parts(5_745, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 5000]`. fn instr_i64xor(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_549_000 picoseconds. - Weight::from_parts(1_915_938, 0) - // Standard Error: 3 - .saturating_add(Weight::from_parts(5_915, 0).saturating_mul(r.into())) + // Minimum execution time: 1_507_000 picoseconds. + Weight::from_parts(1_868_720, 0) + // Standard Error: 2 + .saturating_add(Weight::from_parts(5_873, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 5000]`. fn instr_i64shl(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_617_000 picoseconds. - Weight::from_parts(1_932_618, 0) - // Standard Error: 3 - .saturating_add(Weight::from_parts(5_854, 0).saturating_mul(r.into())) + // Minimum execution time: 1_513_000 picoseconds. + Weight::from_parts(1_894_207, 0) + // Standard Error: 2 + .saturating_add(Weight::from_parts(5_843, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 5000]`. fn instr_i64shrs(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_617_000 picoseconds. - Weight::from_parts(1_937_566, 0) - // Standard Error: 3 - .saturating_add(Weight::from_parts(6_134, 0).saturating_mul(r.into())) + // Minimum execution time: 1_473_000 picoseconds. + Weight::from_parts(1_880_224, 0) + // Standard Error: 2 + .saturating_add(Weight::from_parts(6_107, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 5000]`. fn instr_i64shru(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_584_000 picoseconds. - Weight::from_parts(1_980_681, 0) - // Standard Error: 3 - .saturating_add(Weight::from_parts(5_842, 0).saturating_mul(r.into())) + // Minimum execution time: 1_447_000 picoseconds. + Weight::from_parts(1_884_551, 0) + // Standard Error: 2 + .saturating_add(Weight::from_parts(5_849, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 5000]`. fn instr_i64rotl(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_542_000 picoseconds. - Weight::from_parts(2_131_567, 0) - // Standard Error: 57 - .saturating_add(Weight::from_parts(6_010, 0).saturating_mul(r.into())) + // Minimum execution time: 1_538_000 picoseconds. + Weight::from_parts(1_908_813, 0) + // Standard Error: 2 + .saturating_add(Weight::from_parts(5_987, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 5000]`. fn instr_i64rotr(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_609_000 picoseconds. - Weight::from_parts(1_643_214, 0) - // Standard Error: 91 - .saturating_add(Weight::from_parts(6_062, 0).saturating_mul(r.into())) + // Minimum execution time: 1_538_000 picoseconds. + Weight::from_parts(1_878_015, 0) + // Standard Error: 2 + .saturating_add(Weight::from_parts(5_848, 0).saturating_mul(r.into())) } } diff --git a/frame/conviction-voting/src/weights.rs b/frame/conviction-voting/src/weights.rs index 765a4276a..bc53855b6 100644 --- a/frame/conviction-voting/src/weights.rs +++ b/frame/conviction-voting/src/weights.rs @@ -18,9 +18,9 @@ //! Autogenerated weights for pallet_conviction_voting //! //! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev -//! DATE: 2023-03-16, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! DATE: 2023-04-06, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` //! WORST CASE MAP SIZE: `1000000` -//! HOSTNAME: `bm3`, CPU: `Intel(R) Core(TM) i7-7700K CPU @ 4.20GHz` +//! HOSTNAME: `bm2`, CPU: `Intel(R) Core(TM) i7-7700K CPU @ 4.20GHz` //! EXECUTION: Some(Wasm), WASM-EXECUTION: Compiled, CHAIN: Some("dev"), DB CACHE: 1024 // Executed Command: @@ -31,9 +31,6 @@ // --steps=50 // --repeat=20 // --pallet=pallet_conviction_voting -// --no-storage-info -// --no-median-slopes -// --no-min-squares // --extrinsic=* // --execution=wasm // --wasm-execution=compiled @@ -71,15 +68,17 @@ impl WeightInfo for SubstrateWeight { /// Proof: ConvictionVoting ClassLocksFor (max_values: None, max_size: Some(59), added: 2534, mode: MaxEncodedLen) /// Storage: Balances Locks (r:1 w:1) /// Proof: Balances Locks (max_values: None, max_size: Some(1299), added: 3774, mode: MaxEncodedLen) + /// Storage: Balances Freezes (r:1 w:0) + /// Proof: Balances Freezes (max_values: None, max_size: Some(49), added: 2524, mode: MaxEncodedLen) /// Storage: Scheduler Agenda (r:2 w:2) /// Proof: Scheduler Agenda (max_values: None, max_size: Some(107022), added: 109497, mode: MaxEncodedLen) fn vote_new() -> Weight { // Proof Size summary in bytes: // Measured: `13074` - // Estimated: `262809` - // Minimum execution time: 94_493_000 picoseconds. - Weight::from_parts(96_510_000, 262809) - .saturating_add(T::DbWeight::get().reads(6_u64)) + // Estimated: `219984` + // Minimum execution time: 99_130_000 picoseconds. + Weight::from_parts(100_355_000, 219984) + .saturating_add(T::DbWeight::get().reads(7_u64)) .saturating_add(T::DbWeight::get().writes(6_u64)) } /// Storage: Referenda ReferendumInfoFor (r:1 w:1) @@ -90,15 +89,17 @@ impl WeightInfo for SubstrateWeight { /// Proof: ConvictionVoting ClassLocksFor (max_values: None, max_size: Some(59), added: 2534, mode: MaxEncodedLen) /// Storage: Balances Locks (r:1 w:1) /// Proof: Balances Locks (max_values: None, max_size: Some(1299), added: 3774, mode: MaxEncodedLen) + /// Storage: Balances Freezes (r:1 w:0) + /// Proof: Balances Freezes (max_values: None, max_size: Some(49), added: 2524, mode: MaxEncodedLen) /// Storage: Scheduler Agenda (r:2 w:2) /// Proof: Scheduler Agenda (max_values: None, max_size: Some(107022), added: 109497, mode: MaxEncodedLen) fn vote_existing() -> Weight { // Proof Size summary in bytes: // Measured: `20216` - // Estimated: `262809` - // Minimum execution time: 252_717_000 picoseconds. - Weight::from_parts(255_200_000, 262809) - .saturating_add(T::DbWeight::get().reads(6_u64)) + // Estimated: `219984` + // Minimum execution time: 276_420_000 picoseconds. + Weight::from_parts(277_433_000, 219984) + .saturating_add(T::DbWeight::get().reads(7_u64)) .saturating_add(T::DbWeight::get().writes(6_u64)) } /// Storage: ConvictionVoting VotingFor (r:1 w:1) @@ -110,9 +111,9 @@ impl WeightInfo for SubstrateWeight { fn remove_vote() -> Weight { // Proof Size summary in bytes: // Measured: `19968` - // Estimated: `254521` - // Minimum execution time: 244_844_000 picoseconds. - Weight::from_parts(247_433_000, 254521) + // Estimated: `219984` + // Minimum execution time: 241_058_000 picoseconds. + Weight::from_parts(242_235_000, 219984) .saturating_add(T::DbWeight::get().reads(4_u64)) .saturating_add(T::DbWeight::get().writes(4_u64)) } @@ -123,9 +124,9 @@ impl WeightInfo for SubstrateWeight { fn remove_other_vote() -> Weight { // Proof Size summary in bytes: // Measured: `12675` - // Estimated: `34537` - // Minimum execution time: 46_271_000 picoseconds. - Weight::from_parts(47_639_000, 34537) + // Estimated: `30706` + // Minimum execution time: 46_385_000 picoseconds. + Weight::from_parts(46_709_000, 30706) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -139,20 +140,22 @@ impl WeightInfo for SubstrateWeight { /// Proof: ConvictionVoting ClassLocksFor (max_values: None, max_size: Some(59), added: 2534, mode: MaxEncodedLen) /// Storage: Balances Locks (r:1 w:1) /// Proof: Balances Locks (max_values: None, max_size: Some(1299), added: 3774, mode: MaxEncodedLen) + /// Storage: Balances Freezes (r:1 w:0) + /// Proof: Balances Freezes (max_values: None, max_size: Some(49), added: 2524, mode: MaxEncodedLen) /// The range of component `r` is `[0, 1]`. fn delegate(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `240 + r * (1627 ±0)` - // Estimated: `180617 + r * (111908 ±0)` - // Minimum execution time: 37_578_000 picoseconds. - Weight::from_parts(39_035_446, 180617) - // Standard Error: 160_560 - .saturating_add(Weight::from_parts(39_642_553, 0).saturating_mul(r.into())) - .saturating_add(T::DbWeight::get().reads(4_u64)) + // Estimated: `109992 + r * (109992 ±0)` + // Minimum execution time: 48_947_000 picoseconds. + Weight::from_parts(50_219_593, 109992) + // Standard Error: 70_238 + .saturating_add(Weight::from_parts(40_509_706, 0).saturating_mul(r.into())) + .saturating_add(T::DbWeight::get().reads(5_u64)) .saturating_add(T::DbWeight::get().reads((3_u64).saturating_mul(r.into()))) .saturating_add(T::DbWeight::get().writes(4_u64)) .saturating_add(T::DbWeight::get().writes((3_u64).saturating_mul(r.into()))) - .saturating_add(Weight::from_parts(0, 111908).saturating_mul(r.into())) + .saturating_add(Weight::from_parts(0, 109992).saturating_mul(r.into())) } /// Storage: ConvictionVoting VotingFor (r:2 w:2) /// Proof: ConvictionVoting VotingFor (max_values: None, max_size: Some(27241), added: 29716, mode: MaxEncodedLen) @@ -164,16 +167,16 @@ impl WeightInfo for SubstrateWeight { fn undelegate(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `406 + r * (1376 ±0)` - // Estimated: `172329 + r * (111908 ±0)` - // Minimum execution time: 22_997_000 picoseconds. - Weight::from_parts(24_180_222, 172329) - // Standard Error: 87_723 - .saturating_add(Weight::from_parts(36_189_677, 0).saturating_mul(r.into())) + // Estimated: `109992 + r * (109992 ±0)` + // Minimum execution time: 23_408_000 picoseconds. + Weight::from_parts(24_087_793, 109992) + // Standard Error: 31_776 + .saturating_add(Weight::from_parts(36_594_606, 0).saturating_mul(r.into())) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().reads((3_u64).saturating_mul(r.into()))) .saturating_add(T::DbWeight::get().writes(2_u64)) .saturating_add(T::DbWeight::get().writes((3_u64).saturating_mul(r.into()))) - .saturating_add(Weight::from_parts(0, 111908).saturating_mul(r.into())) + .saturating_add(Weight::from_parts(0, 109992).saturating_mul(r.into())) } /// Storage: ConvictionVoting VotingFor (r:1 w:1) /// Proof: ConvictionVoting VotingFor (max_values: None, max_size: Some(27241), added: 29716, mode: MaxEncodedLen) @@ -181,13 +184,15 @@ impl WeightInfo for SubstrateWeight { /// Proof: ConvictionVoting ClassLocksFor (max_values: None, max_size: Some(59), added: 2534, mode: MaxEncodedLen) /// Storage: Balances Locks (r:1 w:1) /// Proof: Balances Locks (max_values: None, max_size: Some(1299), added: 3774, mode: MaxEncodedLen) + /// Storage: Balances Freezes (r:1 w:0) + /// Proof: Balances Freezes (max_values: None, max_size: Some(49), added: 2524, mode: MaxEncodedLen) fn unlock() -> Weight { // Proof Size summary in bytes: // Measured: `11734` - // Estimated: `38994` - // Minimum execution time: 55_906_000 picoseconds. - Weight::from_parts(56_819_000, 38994) - .saturating_add(T::DbWeight::get().reads(3_u64)) + // Estimated: `30706` + // Minimum execution time: 65_903_000 picoseconds. + Weight::from_parts(66_460_000, 30706) + .saturating_add(T::DbWeight::get().reads(4_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) } } @@ -202,15 +207,17 @@ impl WeightInfo for () { /// Proof: ConvictionVoting ClassLocksFor (max_values: None, max_size: Some(59), added: 2534, mode: MaxEncodedLen) /// Storage: Balances Locks (r:1 w:1) /// Proof: Balances Locks (max_values: None, max_size: Some(1299), added: 3774, mode: MaxEncodedLen) + /// Storage: Balances Freezes (r:1 w:0) + /// Proof: Balances Freezes (max_values: None, max_size: Some(49), added: 2524, mode: MaxEncodedLen) /// Storage: Scheduler Agenda (r:2 w:2) /// Proof: Scheduler Agenda (max_values: None, max_size: Some(107022), added: 109497, mode: MaxEncodedLen) fn vote_new() -> Weight { // Proof Size summary in bytes: // Measured: `13074` - // Estimated: `262809` - // Minimum execution time: 94_493_000 picoseconds. - Weight::from_parts(96_510_000, 262809) - .saturating_add(RocksDbWeight::get().reads(6_u64)) + // Estimated: `219984` + // Minimum execution time: 99_130_000 picoseconds. + Weight::from_parts(100_355_000, 219984) + .saturating_add(RocksDbWeight::get().reads(7_u64)) .saturating_add(RocksDbWeight::get().writes(6_u64)) } /// Storage: Referenda ReferendumInfoFor (r:1 w:1) @@ -221,15 +228,17 @@ impl WeightInfo for () { /// Proof: ConvictionVoting ClassLocksFor (max_values: None, max_size: Some(59), added: 2534, mode: MaxEncodedLen) /// Storage: Balances Locks (r:1 w:1) /// Proof: Balances Locks (max_values: None, max_size: Some(1299), added: 3774, mode: MaxEncodedLen) + /// Storage: Balances Freezes (r:1 w:0) + /// Proof: Balances Freezes (max_values: None, max_size: Some(49), added: 2524, mode: MaxEncodedLen) /// Storage: Scheduler Agenda (r:2 w:2) /// Proof: Scheduler Agenda (max_values: None, max_size: Some(107022), added: 109497, mode: MaxEncodedLen) fn vote_existing() -> Weight { // Proof Size summary in bytes: // Measured: `20216` - // Estimated: `262809` - // Minimum execution time: 252_717_000 picoseconds. - Weight::from_parts(255_200_000, 262809) - .saturating_add(RocksDbWeight::get().reads(6_u64)) + // Estimated: `219984` + // Minimum execution time: 276_420_000 picoseconds. + Weight::from_parts(277_433_000, 219984) + .saturating_add(RocksDbWeight::get().reads(7_u64)) .saturating_add(RocksDbWeight::get().writes(6_u64)) } /// Storage: ConvictionVoting VotingFor (r:1 w:1) @@ -241,9 +250,9 @@ impl WeightInfo for () { fn remove_vote() -> Weight { // Proof Size summary in bytes: // Measured: `19968` - // Estimated: `254521` - // Minimum execution time: 244_844_000 picoseconds. - Weight::from_parts(247_433_000, 254521) + // Estimated: `219984` + // Minimum execution time: 241_058_000 picoseconds. + Weight::from_parts(242_235_000, 219984) .saturating_add(RocksDbWeight::get().reads(4_u64)) .saturating_add(RocksDbWeight::get().writes(4_u64)) } @@ -254,9 +263,9 @@ impl WeightInfo for () { fn remove_other_vote() -> Weight { // Proof Size summary in bytes: // Measured: `12675` - // Estimated: `34537` - // Minimum execution time: 46_271_000 picoseconds. - Weight::from_parts(47_639_000, 34537) + // Estimated: `30706` + // Minimum execution time: 46_385_000 picoseconds. + Weight::from_parts(46_709_000, 30706) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } @@ -270,20 +279,22 @@ impl WeightInfo for () { /// Proof: ConvictionVoting ClassLocksFor (max_values: None, max_size: Some(59), added: 2534, mode: MaxEncodedLen) /// Storage: Balances Locks (r:1 w:1) /// Proof: Balances Locks (max_values: None, max_size: Some(1299), added: 3774, mode: MaxEncodedLen) + /// Storage: Balances Freezes (r:1 w:0) + /// Proof: Balances Freezes (max_values: None, max_size: Some(49), added: 2524, mode: MaxEncodedLen) /// The range of component `r` is `[0, 1]`. fn delegate(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `240 + r * (1627 ±0)` - // Estimated: `180617 + r * (111908 ±0)` - // Minimum execution time: 37_578_000 picoseconds. - Weight::from_parts(39_035_446, 180617) - // Standard Error: 160_560 - .saturating_add(Weight::from_parts(39_642_553, 0).saturating_mul(r.into())) - .saturating_add(RocksDbWeight::get().reads(4_u64)) + // Estimated: `109992 + r * (109992 ±0)` + // Minimum execution time: 48_947_000 picoseconds. + Weight::from_parts(50_219_593, 109992) + // Standard Error: 70_238 + .saturating_add(Weight::from_parts(40_509_706, 0).saturating_mul(r.into())) + .saturating_add(RocksDbWeight::get().reads(5_u64)) .saturating_add(RocksDbWeight::get().reads((3_u64).saturating_mul(r.into()))) .saturating_add(RocksDbWeight::get().writes(4_u64)) .saturating_add(RocksDbWeight::get().writes((3_u64).saturating_mul(r.into()))) - .saturating_add(Weight::from_parts(0, 111908).saturating_mul(r.into())) + .saturating_add(Weight::from_parts(0, 109992).saturating_mul(r.into())) } /// Storage: ConvictionVoting VotingFor (r:2 w:2) /// Proof: ConvictionVoting VotingFor (max_values: None, max_size: Some(27241), added: 29716, mode: MaxEncodedLen) @@ -295,16 +306,16 @@ impl WeightInfo for () { fn undelegate(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `406 + r * (1376 ±0)` - // Estimated: `172329 + r * (111908 ±0)` - // Minimum execution time: 22_997_000 picoseconds. - Weight::from_parts(24_180_222, 172329) - // Standard Error: 87_723 - .saturating_add(Weight::from_parts(36_189_677, 0).saturating_mul(r.into())) + // Estimated: `109992 + r * (109992 ±0)` + // Minimum execution time: 23_408_000 picoseconds. + Weight::from_parts(24_087_793, 109992) + // Standard Error: 31_776 + .saturating_add(Weight::from_parts(36_594_606, 0).saturating_mul(r.into())) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().reads((3_u64).saturating_mul(r.into()))) .saturating_add(RocksDbWeight::get().writes(2_u64)) .saturating_add(RocksDbWeight::get().writes((3_u64).saturating_mul(r.into()))) - .saturating_add(Weight::from_parts(0, 111908).saturating_mul(r.into())) + .saturating_add(Weight::from_parts(0, 109992).saturating_mul(r.into())) } /// Storage: ConvictionVoting VotingFor (r:1 w:1) /// Proof: ConvictionVoting VotingFor (max_values: None, max_size: Some(27241), added: 29716, mode: MaxEncodedLen) @@ -312,13 +323,15 @@ impl WeightInfo for () { /// Proof: ConvictionVoting ClassLocksFor (max_values: None, max_size: Some(59), added: 2534, mode: MaxEncodedLen) /// Storage: Balances Locks (r:1 w:1) /// Proof: Balances Locks (max_values: None, max_size: Some(1299), added: 3774, mode: MaxEncodedLen) + /// Storage: Balances Freezes (r:1 w:0) + /// Proof: Balances Freezes (max_values: None, max_size: Some(49), added: 2524, mode: MaxEncodedLen) fn unlock() -> Weight { // Proof Size summary in bytes: // Measured: `11734` - // Estimated: `38994` - // Minimum execution time: 55_906_000 picoseconds. - Weight::from_parts(56_819_000, 38994) - .saturating_add(RocksDbWeight::get().reads(3_u64)) + // Estimated: `30706` + // Minimum execution time: 65_903_000 picoseconds. + Weight::from_parts(66_460_000, 30706) + .saturating_add(RocksDbWeight::get().reads(4_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) } } diff --git a/frame/core-fellowship/src/weights.rs b/frame/core-fellowship/src/weights.rs index d43682666..28ebeae64 100644 --- a/frame/core-fellowship/src/weights.rs +++ b/frame/core-fellowship/src/weights.rs @@ -18,9 +18,9 @@ //! Autogenerated weights for pallet_core_fellowship //! //! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev -//! DATE: 2023-03-16, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! DATE: 2023-04-06, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` //! WORST CASE MAP SIZE: `1000000` -//! HOSTNAME: `bm3`, CPU: `Intel(R) Core(TM) i7-7700K CPU @ 4.20GHz` +//! HOSTNAME: `bm2`, CPU: `Intel(R) Core(TM) i7-7700K CPU @ 4.20GHz` //! EXECUTION: Some(Wasm), WASM-EXECUTION: Compiled, CHAIN: Some("dev"), DB CACHE: 1024 // Executed Command: @@ -31,9 +31,6 @@ // --steps=50 // --repeat=20 // --pallet=pallet_core_fellowship -// --no-storage-info -// --no-median-slopes -// --no-min-squares // --extrinsic=* // --execution=wasm // --wasm-execution=compiled @@ -72,8 +69,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 10_384_000 picoseconds. - Weight::from_parts(10_787_000, 0) + // Minimum execution time: 10_390_000 picoseconds. + Weight::from_parts(10_847_000, 0) .saturating_add(T::DbWeight::get().writes(1_u64)) } /// Storage: CoreFellowship Member (r:1 w:1) @@ -91,9 +88,9 @@ impl WeightInfo for SubstrateWeight { fn bump_offboard() -> Weight { // Proof Size summary in bytes: // Measured: `16854` - // Estimated: `35762` - // Minimum execution time: 61_847_000 picoseconds. - Weight::from_parts(62_453_000, 35762) + // Estimated: `19894` + // Minimum execution time: 61_737_000 picoseconds. + Weight::from_parts(62_207_000, 19894) .saturating_add(T::DbWeight::get().reads(6_u64)) .saturating_add(T::DbWeight::get().writes(4_u64)) } @@ -112,9 +109,9 @@ impl WeightInfo for SubstrateWeight { fn bump_demote() -> Weight { // Proof Size summary in bytes: // Measured: `16964` - // Estimated: `35762` - // Minimum execution time: 64_449_000 picoseconds. - Weight::from_parts(65_298_000, 35762) + // Estimated: `19894` + // Minimum execution time: 64_226_000 picoseconds. + Weight::from_parts(64_678_000, 19894) .saturating_add(T::DbWeight::get().reads(6_u64)) .saturating_add(T::DbWeight::get().writes(4_u64)) } @@ -125,9 +122,9 @@ impl WeightInfo for SubstrateWeight { fn set_active() -> Weight { // Proof Size summary in bytes: // Measured: `355` - // Estimated: `7021` - // Minimum execution time: 19_308_000 picoseconds. - Weight::from_parts(19_520_000, 7021) + // Estimated: `3514` + // Minimum execution time: 18_977_000 picoseconds. + Weight::from_parts(19_157_000, 3514) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -144,9 +141,9 @@ impl WeightInfo for SubstrateWeight { fn induct() -> Weight { // Proof Size summary in bytes: // Measured: `113` - // Estimated: `10500` - // Minimum execution time: 29_228_000 picoseconds. - Weight::from_parts(29_683_000, 10500) + // Estimated: `3514` + // Minimum execution time: 28_633_000 picoseconds. + Weight::from_parts(29_074_000, 3514) .saturating_add(T::DbWeight::get().reads(3_u64)) .saturating_add(T::DbWeight::get().writes(5_u64)) } @@ -167,9 +164,9 @@ impl WeightInfo for SubstrateWeight { fn promote() -> Weight { // Proof Size summary in bytes: // Measured: `16832` - // Estimated: `32243` - // Minimum execution time: 59_745_000 picoseconds. - Weight::from_parts(60_290_000, 32243) + // Estimated: `19894` + // Minimum execution time: 59_008_000 picoseconds. + Weight::from_parts(59_690_000, 19894) .saturating_add(T::DbWeight::get().reads(5_u64)) .saturating_add(T::DbWeight::get().writes(6_u64)) } @@ -182,9 +179,9 @@ impl WeightInfo for SubstrateWeight { fn offboard() -> Weight { // Proof Size summary in bytes: // Measured: `326` - // Estimated: `7021` - // Minimum execution time: 18_964_000 picoseconds. - Weight::from_parts(19_603_000, 7021) + // Estimated: `3514` + // Minimum execution time: 18_892_000 picoseconds. + Weight::from_parts(19_095_000, 3514) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().writes(2_u64)) } @@ -195,9 +192,9 @@ impl WeightInfo for SubstrateWeight { fn import() -> Weight { // Proof Size summary in bytes: // Measured: `280` - // Estimated: `7021` - // Minimum execution time: 18_752_000 picoseconds. - Weight::from_parts(19_035_000, 7021) + // Estimated: `3514` + // Minimum execution time: 18_107_000 picoseconds. + Weight::from_parts(18_371_000, 3514) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -210,9 +207,9 @@ impl WeightInfo for SubstrateWeight { fn approve() -> Weight { // Proof Size summary in bytes: // Measured: `16810` - // Estimated: `26915` - // Minimum execution time: 44_972_000 picoseconds. - Weight::from_parts(45_391_000, 26915) + // Estimated: `19894` + // Minimum execution time: 43_243_000 picoseconds. + Weight::from_parts(43_965_000, 19894) .saturating_add(T::DbWeight::get().reads(3_u64)) .saturating_add(T::DbWeight::get().writes(2_u64)) } @@ -223,9 +220,9 @@ impl WeightInfo for SubstrateWeight { fn submit_evidence() -> Weight { // Proof Size summary in bytes: // Measured: `79` - // Estimated: `23408` - // Minimum execution time: 27_820_000 picoseconds. - Weight::from_parts(28_992_000, 23408) + // Estimated: `19894` + // Minimum execution time: 27_881_000 picoseconds. + Weight::from_parts(28_208_000, 19894) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -239,8 +236,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 10_384_000 picoseconds. - Weight::from_parts(10_787_000, 0) + // Minimum execution time: 10_390_000 picoseconds. + Weight::from_parts(10_847_000, 0) .saturating_add(RocksDbWeight::get().writes(1_u64)) } /// Storage: CoreFellowship Member (r:1 w:1) @@ -258,9 +255,9 @@ impl WeightInfo for () { fn bump_offboard() -> Weight { // Proof Size summary in bytes: // Measured: `16854` - // Estimated: `35762` - // Minimum execution time: 61_847_000 picoseconds. - Weight::from_parts(62_453_000, 35762) + // Estimated: `19894` + // Minimum execution time: 61_737_000 picoseconds. + Weight::from_parts(62_207_000, 19894) .saturating_add(RocksDbWeight::get().reads(6_u64)) .saturating_add(RocksDbWeight::get().writes(4_u64)) } @@ -279,9 +276,9 @@ impl WeightInfo for () { fn bump_demote() -> Weight { // Proof Size summary in bytes: // Measured: `16964` - // Estimated: `35762` - // Minimum execution time: 64_449_000 picoseconds. - Weight::from_parts(65_298_000, 35762) + // Estimated: `19894` + // Minimum execution time: 64_226_000 picoseconds. + Weight::from_parts(64_678_000, 19894) .saturating_add(RocksDbWeight::get().reads(6_u64)) .saturating_add(RocksDbWeight::get().writes(4_u64)) } @@ -292,9 +289,9 @@ impl WeightInfo for () { fn set_active() -> Weight { // Proof Size summary in bytes: // Measured: `355` - // Estimated: `7021` - // Minimum execution time: 19_308_000 picoseconds. - Weight::from_parts(19_520_000, 7021) + // Estimated: `3514` + // Minimum execution time: 18_977_000 picoseconds. + Weight::from_parts(19_157_000, 3514) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } @@ -311,9 +308,9 @@ impl WeightInfo for () { fn induct() -> Weight { // Proof Size summary in bytes: // Measured: `113` - // Estimated: `10500` - // Minimum execution time: 29_228_000 picoseconds. - Weight::from_parts(29_683_000, 10500) + // Estimated: `3514` + // Minimum execution time: 28_633_000 picoseconds. + Weight::from_parts(29_074_000, 3514) .saturating_add(RocksDbWeight::get().reads(3_u64)) .saturating_add(RocksDbWeight::get().writes(5_u64)) } @@ -334,9 +331,9 @@ impl WeightInfo for () { fn promote() -> Weight { // Proof Size summary in bytes: // Measured: `16832` - // Estimated: `32243` - // Minimum execution time: 59_745_000 picoseconds. - Weight::from_parts(60_290_000, 32243) + // Estimated: `19894` + // Minimum execution time: 59_008_000 picoseconds. + Weight::from_parts(59_690_000, 19894) .saturating_add(RocksDbWeight::get().reads(5_u64)) .saturating_add(RocksDbWeight::get().writes(6_u64)) } @@ -349,9 +346,9 @@ impl WeightInfo for () { fn offboard() -> Weight { // Proof Size summary in bytes: // Measured: `326` - // Estimated: `7021` - // Minimum execution time: 18_964_000 picoseconds. - Weight::from_parts(19_603_000, 7021) + // Estimated: `3514` + // Minimum execution time: 18_892_000 picoseconds. + Weight::from_parts(19_095_000, 3514) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().writes(2_u64)) } @@ -362,9 +359,9 @@ impl WeightInfo for () { fn import() -> Weight { // Proof Size summary in bytes: // Measured: `280` - // Estimated: `7021` - // Minimum execution time: 18_752_000 picoseconds. - Weight::from_parts(19_035_000, 7021) + // Estimated: `3514` + // Minimum execution time: 18_107_000 picoseconds. + Weight::from_parts(18_371_000, 3514) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } @@ -377,9 +374,9 @@ impl WeightInfo for () { fn approve() -> Weight { // Proof Size summary in bytes: // Measured: `16810` - // Estimated: `26915` - // Minimum execution time: 44_972_000 picoseconds. - Weight::from_parts(45_391_000, 26915) + // Estimated: `19894` + // Minimum execution time: 43_243_000 picoseconds. + Weight::from_parts(43_965_000, 19894) .saturating_add(RocksDbWeight::get().reads(3_u64)) .saturating_add(RocksDbWeight::get().writes(2_u64)) } @@ -390,9 +387,9 @@ impl WeightInfo for () { fn submit_evidence() -> Weight { // Proof Size summary in bytes: // Measured: `79` - // Estimated: `23408` - // Minimum execution time: 27_820_000 picoseconds. - Weight::from_parts(28_992_000, 23408) + // Estimated: `19894` + // Minimum execution time: 27_881_000 picoseconds. + Weight::from_parts(28_208_000, 19894) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } diff --git a/frame/democracy/src/weights.rs b/frame/democracy/src/weights.rs index e0c970a34..a263f2982 100644 --- a/frame/democracy/src/weights.rs +++ b/frame/democracy/src/weights.rs @@ -18,9 +18,9 @@ //! Autogenerated weights for pallet_democracy //! //! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev -//! DATE: 2023-03-16, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! DATE: 2023-04-06, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` //! WORST CASE MAP SIZE: `1000000` -//! HOSTNAME: `bm3`, CPU: `Intel(R) Core(TM) i7-7700K CPU @ 4.20GHz` +//! HOSTNAME: `bm2`, CPU: `Intel(R) Core(TM) i7-7700K CPU @ 4.20GHz` //! EXECUTION: Some(Wasm), WASM-EXECUTION: Compiled, CHAIN: Some("dev"), DB CACHE: 1024 // Executed Command: @@ -31,9 +31,6 @@ // --steps=50 // --repeat=20 // --pallet=pallet_democracy -// --no-storage-info -// --no-median-slopes -// --no-min-squares // --extrinsic=* // --execution=wasm // --wasm-execution=compiled @@ -95,9 +92,9 @@ impl WeightInfo for SubstrateWeight { fn propose() -> Weight { // Proof Size summary in bytes: // Measured: `4801` - // Estimated: `26379` - // Minimum execution time: 39_171_000 picoseconds. - Weight::from_parts(39_779_000, 26379) + // Estimated: `18187` + // Minimum execution time: 43_810_000 picoseconds. + Weight::from_parts(44_439_000, 18187) .saturating_add(T::DbWeight::get().reads(3_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) } @@ -107,8 +104,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `3556` // Estimated: `6695` - // Minimum execution time: 35_694_000 picoseconds. - Weight::from_parts(36_181_000, 6695) + // Minimum execution time: 40_003_000 picoseconds. + Weight::from_parts(40_448_000, 6695) .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -118,13 +115,15 @@ impl WeightInfo for SubstrateWeight { /// Proof: Democracy VotingOf (max_values: None, max_size: Some(3795), added: 6270, mode: MaxEncodedLen) /// Storage: Balances Locks (r:1 w:1) /// Proof: Balances Locks (max_values: None, max_size: Some(1299), added: 3774, mode: MaxEncodedLen) + /// Storage: Balances Freezes (r:1 w:0) + /// Proof: Balances Freezes (max_values: None, max_size: Some(49), added: 2524, mode: MaxEncodedLen) fn vote_new() -> Weight { // Proof Size summary in bytes: // Measured: `3470` - // Estimated: `15690` - // Minimum execution time: 50_274_000 picoseconds. - Weight::from_parts(51_216_000, 15690) - .saturating_add(T::DbWeight::get().reads(3_u64)) + // Estimated: `7260` + // Minimum execution time: 54_737_000 picoseconds. + Weight::from_parts(55_154_000, 7260) + .saturating_add(T::DbWeight::get().reads(4_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) } /// Storage: Democracy ReferendumInfoOf (r:1 w:1) @@ -133,13 +132,15 @@ impl WeightInfo for SubstrateWeight { /// Proof: Democracy VotingOf (max_values: None, max_size: Some(3795), added: 6270, mode: MaxEncodedLen) /// Storage: Balances Locks (r:1 w:1) /// Proof: Balances Locks (max_values: None, max_size: Some(1299), added: 3774, mode: MaxEncodedLen) + /// Storage: Balances Freezes (r:1 w:0) + /// Proof: Balances Freezes (max_values: None, max_size: Some(49), added: 2524, mode: MaxEncodedLen) fn vote_existing() -> Weight { // Proof Size summary in bytes: // Measured: `3492` - // Estimated: `15690` - // Minimum execution time: 50_559_000 picoseconds. - Weight::from_parts(51_030_000, 15690) - .saturating_add(T::DbWeight::get().reads(3_u64)) + // Estimated: `7260` + // Minimum execution time: 59_545_000 picoseconds. + Weight::from_parts(59_955_000, 7260) + .saturating_add(T::DbWeight::get().reads(4_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) } /// Storage: Democracy ReferendumInfoOf (r:1 w:1) @@ -151,9 +152,9 @@ impl WeightInfo for SubstrateWeight { fn emergency_cancel() -> Weight { // Proof Size summary in bytes: // Measured: `366` - // Estimated: `10682` - // Minimum execution time: 27_539_000 picoseconds. - Weight::from_parts(28_040_000, 10682) + // Estimated: `3666` + // Minimum execution time: 27_886_000 picoseconds. + Weight::from_parts(28_372_000, 3666) .saturating_add(T::DbWeight::get().reads(3_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) } @@ -174,9 +175,9 @@ impl WeightInfo for SubstrateWeight { fn blacklist() -> Weight { // Proof Size summary in bytes: // Measured: `5910` - // Estimated: `42332` - // Minimum execution time: 93_053_000 picoseconds. - Weight::from_parts(94_193_000, 42332) + // Estimated: `18187` + // Minimum execution time: 99_273_000 picoseconds. + Weight::from_parts(100_398_000, 18187) .saturating_add(T::DbWeight::get().reads(8_u64)) .saturating_add(T::DbWeight::get().writes(7_u64)) } @@ -187,9 +188,9 @@ impl WeightInfo for SubstrateWeight { fn external_propose() -> Weight { // Proof Size summary in bytes: // Measured: `3416` - // Estimated: `8320` - // Minimum execution time: 14_664_000 picoseconds. - Weight::from_parts(15_064_000, 8320) + // Estimated: `6703` + // Minimum execution time: 14_946_000 picoseconds. + Weight::from_parts(15_114_000, 6703) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -199,8 +200,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 4_073_000 picoseconds. - Weight::from_parts(4_242_000, 0) + // Minimum execution time: 3_870_000 picoseconds. + Weight::from_parts(4_083_000, 0) .saturating_add(T::DbWeight::get().writes(1_u64)) } /// Storage: Democracy NextExternal (r:0 w:1) @@ -209,8 +210,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 3_779_000 picoseconds. - Weight::from_parts(4_091_000, 0) + // Minimum execution time: 3_989_000 picoseconds. + Weight::from_parts(4_166_000, 0) .saturating_add(T::DbWeight::get().writes(1_u64)) } /// Storage: Democracy NextExternal (r:1 w:1) @@ -224,9 +225,9 @@ impl WeightInfo for SubstrateWeight { fn fast_track() -> Weight { // Proof Size summary in bytes: // Measured: `286` - // Estimated: `6624` - // Minimum execution time: 29_596_000 picoseconds. - Weight::from_parts(30_083_000, 6624) + // Estimated: `3518` + // Minimum execution time: 29_776_000 picoseconds. + Weight::from_parts(30_186_000, 3518) .saturating_add(T::DbWeight::get().reads(3_u64)) .saturating_add(T::DbWeight::get().writes(5_u64)) } @@ -239,9 +240,9 @@ impl WeightInfo for SubstrateWeight { fn veto_external() -> Weight { // Proof Size summary in bytes: // Measured: `3519` - // Estimated: `11838` - // Minimum execution time: 34_721_000 picoseconds. - Weight::from_parts(34_989_000, 11838) + // Estimated: `6703` + // Minimum execution time: 33_891_000 picoseconds. + Weight::from_parts(34_265_000, 6703) .saturating_add(T::DbWeight::get().reads(3_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) } @@ -256,9 +257,9 @@ impl WeightInfo for SubstrateWeight { fn cancel_proposal() -> Weight { // Proof Size summary in bytes: // Measured: `5821` - // Estimated: `31993` - // Minimum execution time: 74_835_000 picoseconds. - Weight::from_parts(75_807_000, 31993) + // Estimated: `18187` + // Minimum execution time: 81_510_000 picoseconds. + Weight::from_parts(82_483_000, 18187) .saturating_add(T::DbWeight::get().reads(4_u64)) .saturating_add(T::DbWeight::get().writes(4_u64)) } @@ -270,8 +271,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `271` // Estimated: `3518` - // Minimum execution time: 20_995_000 picoseconds. - Weight::from_parts(21_425_000, 3518) + // Minimum execution time: 21_164_000 picoseconds. + Weight::from_parts(21_624_000, 3518) .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(T::DbWeight::get().writes(2_u64)) } @@ -285,11 +286,11 @@ impl WeightInfo for SubstrateWeight { fn on_initialize_base(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `244 + r * (86 ±0)` - // Estimated: `3968 + r * (2676 ±0)` - // Minimum execution time: 7_002_000 picoseconds. - Weight::from_parts(10_543_534, 3968) - // Standard Error: 7_286 - .saturating_add(Weight::from_parts(2_911_122, 0).saturating_mul(r.into())) + // Estimated: `1489 + r * (2676 ±0)` + // Minimum execution time: 6_925_000 picoseconds. + Weight::from_parts(10_624_198, 1489) + // Standard Error: 5_780 + .saturating_add(Weight::from_parts(2_934_169, 0).saturating_mul(r.into())) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().reads((1_u64).saturating_mul(r.into()))) .saturating_add(T::DbWeight::get().writes(1_u64)) @@ -311,11 +312,11 @@ impl WeightInfo for SubstrateWeight { fn on_initialize_base_with_launch_period(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `244 + r * (86 ±0)` - // Estimated: `25258 + r * (2676 ±0)` - // Minimum execution time: 10_824_000 picoseconds. - Weight::from_parts(14_187_527, 25258) - // Standard Error: 7_148 - .saturating_add(Weight::from_parts(2_886_910, 0).saturating_mul(r.into())) + // Estimated: `18187 + r * (2676 ±0)` + // Minimum execution time: 10_551_000 picoseconds. + Weight::from_parts(13_126_123, 18187) + // Standard Error: 6_391 + .saturating_add(Weight::from_parts(2_952_789, 0).saturating_mul(r.into())) .saturating_add(T::DbWeight::get().reads(5_u64)) .saturating_add(T::DbWeight::get().reads((1_u64).saturating_mul(r.into()))) .saturating_add(T::DbWeight::get().writes(1_u64)) @@ -327,16 +328,18 @@ impl WeightInfo for SubstrateWeight { /// Proof: Democracy ReferendumInfoOf (max_values: None, max_size: Some(201), added: 2676, mode: MaxEncodedLen) /// Storage: Balances Locks (r:1 w:1) /// Proof: Balances Locks (max_values: None, max_size: Some(1299), added: 3774, mode: MaxEncodedLen) + /// Storage: Balances Freezes (r:1 w:0) + /// Proof: Balances Freezes (max_values: None, max_size: Some(49), added: 2524, mode: MaxEncodedLen) /// The range of component `r` is `[0, 99]`. fn delegate(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `830 + r * (108 ±0)` - // Estimated: `25554 + r * (2676 ±0)` - // Minimum execution time: 42_940_000 picoseconds. - Weight::from_parts(45_288_082, 25554) - // Standard Error: 6_380 - .saturating_add(Weight::from_parts(4_219_163, 0).saturating_mul(r.into())) - .saturating_add(T::DbWeight::get().reads(4_u64)) + // Estimated: `19800 + r * (2676 ±0)` + // Minimum execution time: 47_172_000 picoseconds. + Weight::from_parts(49_667_954, 19800) + // Standard Error: 6_129 + .saturating_add(Weight::from_parts(4_230_402, 0).saturating_mul(r.into())) + .saturating_add(T::DbWeight::get().reads(5_u64)) .saturating_add(T::DbWeight::get().reads((1_u64).saturating_mul(r.into()))) .saturating_add(T::DbWeight::get().writes(4_u64)) .saturating_add(T::DbWeight::get().writes((1_u64).saturating_mul(r.into()))) @@ -350,11 +353,11 @@ impl WeightInfo for SubstrateWeight { fn undelegate(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `493 + r * (108 ±0)` - // Estimated: `14520 + r * (2676 ±0)` - // Minimum execution time: 22_203_000 picoseconds. - Weight::from_parts(22_307_372, 14520) - // Standard Error: 7_095 - .saturating_add(Weight::from_parts(4_202_995, 0).saturating_mul(r.into())) + // Estimated: `13530 + r * (2676 ±0)` + // Minimum execution time: 22_360_000 picoseconds. + Weight::from_parts(25_063_237, 13530) + // Standard Error: 5_326 + .saturating_add(Weight::from_parts(4_163_683, 0).saturating_mul(r.into())) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().reads((1_u64).saturating_mul(r.into()))) .saturating_add(T::DbWeight::get().writes(2_u64)) @@ -367,44 +370,48 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 4_046_000 picoseconds. - Weight::from_parts(4_191_000, 0) + // Minimum execution time: 4_047_000 picoseconds. + Weight::from_parts(4_139_000, 0) .saturating_add(T::DbWeight::get().writes(1_u64)) } /// Storage: Democracy VotingOf (r:1 w:1) /// Proof: Democracy VotingOf (max_values: None, max_size: Some(3795), added: 6270, mode: MaxEncodedLen) /// Storage: Balances Locks (r:1 w:1) /// Proof: Balances Locks (max_values: None, max_size: Some(1299), added: 3774, mode: MaxEncodedLen) + /// Storage: Balances Freezes (r:1 w:0) + /// Proof: Balances Freezes (max_values: None, max_size: Some(49), added: 2524, mode: MaxEncodedLen) /// Storage: System Account (r:1 w:1) /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) /// The range of component `r` is `[0, 99]`. fn unlock_remove(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `563` - // Estimated: `15617` - // Minimum execution time: 22_585_000 picoseconds. - Weight::from_parts(28_461_437, 15617) - // Standard Error: 1_636 - .saturating_add(Weight::from_parts(19_140, 0).saturating_mul(r.into())) - .saturating_add(T::DbWeight::get().reads(3_u64)) + // Estimated: `7260` + // Minimum execution time: 27_322_000 picoseconds. + Weight::from_parts(39_909_589, 7260) + // Standard Error: 2_758 + .saturating_add(Weight::from_parts(29_497, 0).saturating_mul(r.into())) + .saturating_add(T::DbWeight::get().reads(4_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) } /// Storage: Democracy VotingOf (r:1 w:1) /// Proof: Democracy VotingOf (max_values: None, max_size: Some(3795), added: 6270, mode: MaxEncodedLen) /// Storage: Balances Locks (r:1 w:1) /// Proof: Balances Locks (max_values: None, max_size: Some(1299), added: 3774, mode: MaxEncodedLen) + /// Storage: Balances Freezes (r:1 w:0) + /// Proof: Balances Freezes (max_values: None, max_size: Some(49), added: 2524, mode: MaxEncodedLen) /// Storage: System Account (r:1 w:1) /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) /// The range of component `r` is `[0, 99]`. fn unlock_set(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `564 + r * (22 ±0)` - // Estimated: `15617` - // Minimum execution time: 26_674_000 picoseconds. - Weight::from_parts(27_815_260, 15617) - // Standard Error: 490 - .saturating_add(Weight::from_parts(68_478, 0).saturating_mul(r.into())) - .saturating_add(T::DbWeight::get().reads(3_u64)) + // Estimated: `7260` + // Minimum execution time: 37_082_000 picoseconds. + Weight::from_parts(38_580_061, 7260) + // Standard Error: 664 + .saturating_add(Weight::from_parts(62_401, 0).saturating_mul(r.into())) + .saturating_add(T::DbWeight::get().reads(4_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) } /// Storage: Democracy ReferendumInfoOf (r:1 w:1) @@ -415,11 +422,11 @@ impl WeightInfo for SubstrateWeight { fn remove_vote(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `728 + r * (26 ±0)` - // Estimated: `10926` - // Minimum execution time: 17_381_000 picoseconds. - Weight::from_parts(19_728_665, 10926) - // Standard Error: 1_065 - .saturating_add(Weight::from_parts(84_481, 0).saturating_mul(r.into())) + // Estimated: `7260` + // Minimum execution time: 17_528_000 picoseconds. + Weight::from_parts(20_075_412, 7260) + // Standard Error: 1_072 + .saturating_add(Weight::from_parts(81_734, 0).saturating_mul(r.into())) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().writes(2_u64)) } @@ -431,11 +438,11 @@ impl WeightInfo for SubstrateWeight { fn remove_other_vote(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `728 + r * (26 ±0)` - // Estimated: `10926` - // Minimum execution time: 17_496_000 picoseconds. - Weight::from_parts(19_845_941, 10926) - // Standard Error: 1_169 - .saturating_add(Weight::from_parts(85_004, 0).saturating_mul(r.into())) + // Estimated: `7260` + // Minimum execution time: 17_517_000 picoseconds. + Weight::from_parts(20_090_718, 7260) + // Standard Error: 1_105 + .saturating_add(Weight::from_parts(82_651, 0).saturating_mul(r.into())) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().writes(2_u64)) } @@ -448,9 +455,9 @@ impl WeightInfo for SubstrateWeight { fn set_external_metadata() -> Weight { // Proof Size summary in bytes: // Measured: `356` - // Estimated: `5173` - // Minimum execution time: 19_076_000 picoseconds. - Weight::from_parts(19_650_000, 5173) + // Estimated: `3556` + // Minimum execution time: 19_234_000 picoseconds. + Weight::from_parts(19_755_000, 3556) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -461,9 +468,9 @@ impl WeightInfo for SubstrateWeight { fn clear_external_metadata() -> Weight { // Proof Size summary in bytes: // Measured: `286` - // Estimated: `5135` - // Minimum execution time: 17_588_000 picoseconds. - Weight::from_parts(17_781_000, 5135) + // Estimated: `3518` + // Minimum execution time: 17_621_000 picoseconds. + Weight::from_parts(17_861_000, 3518) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -476,9 +483,9 @@ impl WeightInfo for SubstrateWeight { fn set_proposal_metadata() -> Weight { // Proof Size summary in bytes: // Measured: `4888` - // Estimated: `21743` - // Minimum execution time: 34_828_000 picoseconds. - Weight::from_parts(35_364_000, 21743) + // Estimated: `18187` + // Minimum execution time: 35_785_000 picoseconds. + Weight::from_parts(36_102_000, 18187) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -489,9 +496,9 @@ impl WeightInfo for SubstrateWeight { fn clear_proposal_metadata() -> Weight { // Proof Size summary in bytes: // Measured: `4822` - // Estimated: `21705` - // Minimum execution time: 32_035_000 picoseconds. - Weight::from_parts(32_554_000, 21705) + // Estimated: `18187` + // Minimum execution time: 33_493_000 picoseconds. + Weight::from_parts(33_747_000, 18187) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -503,8 +510,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `144` // Estimated: `3556` - // Minimum execution time: 15_279_000 picoseconds. - Weight::from_parts(15_556_000, 3556) + // Minimum execution time: 15_557_000 picoseconds. + Weight::from_parts(15_844_000, 3556) .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -515,9 +522,9 @@ impl WeightInfo for SubstrateWeight { fn clear_referendum_metadata() -> Weight { // Proof Size summary in bytes: // Measured: `302` - // Estimated: `7184` - // Minimum execution time: 19_622_000 picoseconds. - Weight::from_parts(19_978_000, 7184) + // Estimated: `3666` + // Minimum execution time: 19_940_000 picoseconds. + Weight::from_parts(20_301_000, 3666) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -536,9 +543,9 @@ impl WeightInfo for () { fn propose() -> Weight { // Proof Size summary in bytes: // Measured: `4801` - // Estimated: `26379` - // Minimum execution time: 39_171_000 picoseconds. - Weight::from_parts(39_779_000, 26379) + // Estimated: `18187` + // Minimum execution time: 43_810_000 picoseconds. + Weight::from_parts(44_439_000, 18187) .saturating_add(RocksDbWeight::get().reads(3_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) } @@ -548,8 +555,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `3556` // Estimated: `6695` - // Minimum execution time: 35_694_000 picoseconds. - Weight::from_parts(36_181_000, 6695) + // Minimum execution time: 40_003_000 picoseconds. + Weight::from_parts(40_448_000, 6695) .saturating_add(RocksDbWeight::get().reads(1_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } @@ -559,13 +566,15 @@ impl WeightInfo for () { /// Proof: Democracy VotingOf (max_values: None, max_size: Some(3795), added: 6270, mode: MaxEncodedLen) /// Storage: Balances Locks (r:1 w:1) /// Proof: Balances Locks (max_values: None, max_size: Some(1299), added: 3774, mode: MaxEncodedLen) + /// Storage: Balances Freezes (r:1 w:0) + /// Proof: Balances Freezes (max_values: None, max_size: Some(49), added: 2524, mode: MaxEncodedLen) fn vote_new() -> Weight { // Proof Size summary in bytes: // Measured: `3470` - // Estimated: `15690` - // Minimum execution time: 50_274_000 picoseconds. - Weight::from_parts(51_216_000, 15690) - .saturating_add(RocksDbWeight::get().reads(3_u64)) + // Estimated: `7260` + // Minimum execution time: 54_737_000 picoseconds. + Weight::from_parts(55_154_000, 7260) + .saturating_add(RocksDbWeight::get().reads(4_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) } /// Storage: Democracy ReferendumInfoOf (r:1 w:1) @@ -574,13 +583,15 @@ impl WeightInfo for () { /// Proof: Democracy VotingOf (max_values: None, max_size: Some(3795), added: 6270, mode: MaxEncodedLen) /// Storage: Balances Locks (r:1 w:1) /// Proof: Balances Locks (max_values: None, max_size: Some(1299), added: 3774, mode: MaxEncodedLen) + /// Storage: Balances Freezes (r:1 w:0) + /// Proof: Balances Freezes (max_values: None, max_size: Some(49), added: 2524, mode: MaxEncodedLen) fn vote_existing() -> Weight { // Proof Size summary in bytes: // Measured: `3492` - // Estimated: `15690` - // Minimum execution time: 50_559_000 picoseconds. - Weight::from_parts(51_030_000, 15690) - .saturating_add(RocksDbWeight::get().reads(3_u64)) + // Estimated: `7260` + // Minimum execution time: 59_545_000 picoseconds. + Weight::from_parts(59_955_000, 7260) + .saturating_add(RocksDbWeight::get().reads(4_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) } /// Storage: Democracy ReferendumInfoOf (r:1 w:1) @@ -592,9 +603,9 @@ impl WeightInfo for () { fn emergency_cancel() -> Weight { // Proof Size summary in bytes: // Measured: `366` - // Estimated: `10682` - // Minimum execution time: 27_539_000 picoseconds. - Weight::from_parts(28_040_000, 10682) + // Estimated: `3666` + // Minimum execution time: 27_886_000 picoseconds. + Weight::from_parts(28_372_000, 3666) .saturating_add(RocksDbWeight::get().reads(3_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) } @@ -615,9 +626,9 @@ impl WeightInfo for () { fn blacklist() -> Weight { // Proof Size summary in bytes: // Measured: `5910` - // Estimated: `42332` - // Minimum execution time: 93_053_000 picoseconds. - Weight::from_parts(94_193_000, 42332) + // Estimated: `18187` + // Minimum execution time: 99_273_000 picoseconds. + Weight::from_parts(100_398_000, 18187) .saturating_add(RocksDbWeight::get().reads(8_u64)) .saturating_add(RocksDbWeight::get().writes(7_u64)) } @@ -628,9 +639,9 @@ impl WeightInfo for () { fn external_propose() -> Weight { // Proof Size summary in bytes: // Measured: `3416` - // Estimated: `8320` - // Minimum execution time: 14_664_000 picoseconds. - Weight::from_parts(15_064_000, 8320) + // Estimated: `6703` + // Minimum execution time: 14_946_000 picoseconds. + Weight::from_parts(15_114_000, 6703) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } @@ -640,8 +651,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 4_073_000 picoseconds. - Weight::from_parts(4_242_000, 0) + // Minimum execution time: 3_870_000 picoseconds. + Weight::from_parts(4_083_000, 0) .saturating_add(RocksDbWeight::get().writes(1_u64)) } /// Storage: Democracy NextExternal (r:0 w:1) @@ -650,8 +661,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 3_779_000 picoseconds. - Weight::from_parts(4_091_000, 0) + // Minimum execution time: 3_989_000 picoseconds. + Weight::from_parts(4_166_000, 0) .saturating_add(RocksDbWeight::get().writes(1_u64)) } /// Storage: Democracy NextExternal (r:1 w:1) @@ -665,9 +676,9 @@ impl WeightInfo for () { fn fast_track() -> Weight { // Proof Size summary in bytes: // Measured: `286` - // Estimated: `6624` - // Minimum execution time: 29_596_000 picoseconds. - Weight::from_parts(30_083_000, 6624) + // Estimated: `3518` + // Minimum execution time: 29_776_000 picoseconds. + Weight::from_parts(30_186_000, 3518) .saturating_add(RocksDbWeight::get().reads(3_u64)) .saturating_add(RocksDbWeight::get().writes(5_u64)) } @@ -680,9 +691,9 @@ impl WeightInfo for () { fn veto_external() -> Weight { // Proof Size summary in bytes: // Measured: `3519` - // Estimated: `11838` - // Minimum execution time: 34_721_000 picoseconds. - Weight::from_parts(34_989_000, 11838) + // Estimated: `6703` + // Minimum execution time: 33_891_000 picoseconds. + Weight::from_parts(34_265_000, 6703) .saturating_add(RocksDbWeight::get().reads(3_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) } @@ -697,9 +708,9 @@ impl WeightInfo for () { fn cancel_proposal() -> Weight { // Proof Size summary in bytes: // Measured: `5821` - // Estimated: `31993` - // Minimum execution time: 74_835_000 picoseconds. - Weight::from_parts(75_807_000, 31993) + // Estimated: `18187` + // Minimum execution time: 81_510_000 picoseconds. + Weight::from_parts(82_483_000, 18187) .saturating_add(RocksDbWeight::get().reads(4_u64)) .saturating_add(RocksDbWeight::get().writes(4_u64)) } @@ -711,8 +722,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `271` // Estimated: `3518` - // Minimum execution time: 20_995_000 picoseconds. - Weight::from_parts(21_425_000, 3518) + // Minimum execution time: 21_164_000 picoseconds. + Weight::from_parts(21_624_000, 3518) .saturating_add(RocksDbWeight::get().reads(1_u64)) .saturating_add(RocksDbWeight::get().writes(2_u64)) } @@ -726,11 +737,11 @@ impl WeightInfo for () { fn on_initialize_base(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `244 + r * (86 ±0)` - // Estimated: `3968 + r * (2676 ±0)` - // Minimum execution time: 7_002_000 picoseconds. - Weight::from_parts(10_543_534, 3968) - // Standard Error: 7_286 - .saturating_add(Weight::from_parts(2_911_122, 0).saturating_mul(r.into())) + // Estimated: `1489 + r * (2676 ±0)` + // Minimum execution time: 6_925_000 picoseconds. + Weight::from_parts(10_624_198, 1489) + // Standard Error: 5_780 + .saturating_add(Weight::from_parts(2_934_169, 0).saturating_mul(r.into())) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().reads((1_u64).saturating_mul(r.into()))) .saturating_add(RocksDbWeight::get().writes(1_u64)) @@ -752,11 +763,11 @@ impl WeightInfo for () { fn on_initialize_base_with_launch_period(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `244 + r * (86 ±0)` - // Estimated: `25258 + r * (2676 ±0)` - // Minimum execution time: 10_824_000 picoseconds. - Weight::from_parts(14_187_527, 25258) - // Standard Error: 7_148 - .saturating_add(Weight::from_parts(2_886_910, 0).saturating_mul(r.into())) + // Estimated: `18187 + r * (2676 ±0)` + // Minimum execution time: 10_551_000 picoseconds. + Weight::from_parts(13_126_123, 18187) + // Standard Error: 6_391 + .saturating_add(Weight::from_parts(2_952_789, 0).saturating_mul(r.into())) .saturating_add(RocksDbWeight::get().reads(5_u64)) .saturating_add(RocksDbWeight::get().reads((1_u64).saturating_mul(r.into()))) .saturating_add(RocksDbWeight::get().writes(1_u64)) @@ -768,16 +779,18 @@ impl WeightInfo for () { /// Proof: Democracy ReferendumInfoOf (max_values: None, max_size: Some(201), added: 2676, mode: MaxEncodedLen) /// Storage: Balances Locks (r:1 w:1) /// Proof: Balances Locks (max_values: None, max_size: Some(1299), added: 3774, mode: MaxEncodedLen) + /// Storage: Balances Freezes (r:1 w:0) + /// Proof: Balances Freezes (max_values: None, max_size: Some(49), added: 2524, mode: MaxEncodedLen) /// The range of component `r` is `[0, 99]`. fn delegate(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `830 + r * (108 ±0)` - // Estimated: `25554 + r * (2676 ±0)` - // Minimum execution time: 42_940_000 picoseconds. - Weight::from_parts(45_288_082, 25554) - // Standard Error: 6_380 - .saturating_add(Weight::from_parts(4_219_163, 0).saturating_mul(r.into())) - .saturating_add(RocksDbWeight::get().reads(4_u64)) + // Estimated: `19800 + r * (2676 ±0)` + // Minimum execution time: 47_172_000 picoseconds. + Weight::from_parts(49_667_954, 19800) + // Standard Error: 6_129 + .saturating_add(Weight::from_parts(4_230_402, 0).saturating_mul(r.into())) + .saturating_add(RocksDbWeight::get().reads(5_u64)) .saturating_add(RocksDbWeight::get().reads((1_u64).saturating_mul(r.into()))) .saturating_add(RocksDbWeight::get().writes(4_u64)) .saturating_add(RocksDbWeight::get().writes((1_u64).saturating_mul(r.into()))) @@ -791,11 +804,11 @@ impl WeightInfo for () { fn undelegate(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `493 + r * (108 ±0)` - // Estimated: `14520 + r * (2676 ±0)` - // Minimum execution time: 22_203_000 picoseconds. - Weight::from_parts(22_307_372, 14520) - // Standard Error: 7_095 - .saturating_add(Weight::from_parts(4_202_995, 0).saturating_mul(r.into())) + // Estimated: `13530 + r * (2676 ±0)` + // Minimum execution time: 22_360_000 picoseconds. + Weight::from_parts(25_063_237, 13530) + // Standard Error: 5_326 + .saturating_add(Weight::from_parts(4_163_683, 0).saturating_mul(r.into())) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().reads((1_u64).saturating_mul(r.into()))) .saturating_add(RocksDbWeight::get().writes(2_u64)) @@ -808,44 +821,48 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 4_046_000 picoseconds. - Weight::from_parts(4_191_000, 0) + // Minimum execution time: 4_047_000 picoseconds. + Weight::from_parts(4_139_000, 0) .saturating_add(RocksDbWeight::get().writes(1_u64)) } /// Storage: Democracy VotingOf (r:1 w:1) /// Proof: Democracy VotingOf (max_values: None, max_size: Some(3795), added: 6270, mode: MaxEncodedLen) /// Storage: Balances Locks (r:1 w:1) /// Proof: Balances Locks (max_values: None, max_size: Some(1299), added: 3774, mode: MaxEncodedLen) + /// Storage: Balances Freezes (r:1 w:0) + /// Proof: Balances Freezes (max_values: None, max_size: Some(49), added: 2524, mode: MaxEncodedLen) /// Storage: System Account (r:1 w:1) /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) /// The range of component `r` is `[0, 99]`. fn unlock_remove(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `563` - // Estimated: `15617` - // Minimum execution time: 22_585_000 picoseconds. - Weight::from_parts(28_461_437, 15617) - // Standard Error: 1_636 - .saturating_add(Weight::from_parts(19_140, 0).saturating_mul(r.into())) - .saturating_add(RocksDbWeight::get().reads(3_u64)) + // Estimated: `7260` + // Minimum execution time: 27_322_000 picoseconds. + Weight::from_parts(39_909_589, 7260) + // Standard Error: 2_758 + .saturating_add(Weight::from_parts(29_497, 0).saturating_mul(r.into())) + .saturating_add(RocksDbWeight::get().reads(4_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) } /// Storage: Democracy VotingOf (r:1 w:1) /// Proof: Democracy VotingOf (max_values: None, max_size: Some(3795), added: 6270, mode: MaxEncodedLen) /// Storage: Balances Locks (r:1 w:1) /// Proof: Balances Locks (max_values: None, max_size: Some(1299), added: 3774, mode: MaxEncodedLen) + /// Storage: Balances Freezes (r:1 w:0) + /// Proof: Balances Freezes (max_values: None, max_size: Some(49), added: 2524, mode: MaxEncodedLen) /// Storage: System Account (r:1 w:1) /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) /// The range of component `r` is `[0, 99]`. fn unlock_set(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `564 + r * (22 ±0)` - // Estimated: `15617` - // Minimum execution time: 26_674_000 picoseconds. - Weight::from_parts(27_815_260, 15617) - // Standard Error: 490 - .saturating_add(Weight::from_parts(68_478, 0).saturating_mul(r.into())) - .saturating_add(RocksDbWeight::get().reads(3_u64)) + // Estimated: `7260` + // Minimum execution time: 37_082_000 picoseconds. + Weight::from_parts(38_580_061, 7260) + // Standard Error: 664 + .saturating_add(Weight::from_parts(62_401, 0).saturating_mul(r.into())) + .saturating_add(RocksDbWeight::get().reads(4_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) } /// Storage: Democracy ReferendumInfoOf (r:1 w:1) @@ -856,11 +873,11 @@ impl WeightInfo for () { fn remove_vote(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `728 + r * (26 ±0)` - // Estimated: `10926` - // Minimum execution time: 17_381_000 picoseconds. - Weight::from_parts(19_728_665, 10926) - // Standard Error: 1_065 - .saturating_add(Weight::from_parts(84_481, 0).saturating_mul(r.into())) + // Estimated: `7260` + // Minimum execution time: 17_528_000 picoseconds. + Weight::from_parts(20_075_412, 7260) + // Standard Error: 1_072 + .saturating_add(Weight::from_parts(81_734, 0).saturating_mul(r.into())) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().writes(2_u64)) } @@ -872,11 +889,11 @@ impl WeightInfo for () { fn remove_other_vote(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `728 + r * (26 ±0)` - // Estimated: `10926` - // Minimum execution time: 17_496_000 picoseconds. - Weight::from_parts(19_845_941, 10926) - // Standard Error: 1_169 - .saturating_add(Weight::from_parts(85_004, 0).saturating_mul(r.into())) + // Estimated: `7260` + // Minimum execution time: 17_517_000 picoseconds. + Weight::from_parts(20_090_718, 7260) + // Standard Error: 1_105 + .saturating_add(Weight::from_parts(82_651, 0).saturating_mul(r.into())) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().writes(2_u64)) } @@ -889,9 +906,9 @@ impl WeightInfo for () { fn set_external_metadata() -> Weight { // Proof Size summary in bytes: // Measured: `356` - // Estimated: `5173` - // Minimum execution time: 19_076_000 picoseconds. - Weight::from_parts(19_650_000, 5173) + // Estimated: `3556` + // Minimum execution time: 19_234_000 picoseconds. + Weight::from_parts(19_755_000, 3556) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } @@ -902,9 +919,9 @@ impl WeightInfo for () { fn clear_external_metadata() -> Weight { // Proof Size summary in bytes: // Measured: `286` - // Estimated: `5135` - // Minimum execution time: 17_588_000 picoseconds. - Weight::from_parts(17_781_000, 5135) + // Estimated: `3518` + // Minimum execution time: 17_621_000 picoseconds. + Weight::from_parts(17_861_000, 3518) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } @@ -917,9 +934,9 @@ impl WeightInfo for () { fn set_proposal_metadata() -> Weight { // Proof Size summary in bytes: // Measured: `4888` - // Estimated: `21743` - // Minimum execution time: 34_828_000 picoseconds. - Weight::from_parts(35_364_000, 21743) + // Estimated: `18187` + // Minimum execution time: 35_785_000 picoseconds. + Weight::from_parts(36_102_000, 18187) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } @@ -930,9 +947,9 @@ impl WeightInfo for () { fn clear_proposal_metadata() -> Weight { // Proof Size summary in bytes: // Measured: `4822` - // Estimated: `21705` - // Minimum execution time: 32_035_000 picoseconds. - Weight::from_parts(32_554_000, 21705) + // Estimated: `18187` + // Minimum execution time: 33_493_000 picoseconds. + Weight::from_parts(33_747_000, 18187) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } @@ -944,8 +961,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `144` // Estimated: `3556` - // Minimum execution time: 15_279_000 picoseconds. - Weight::from_parts(15_556_000, 3556) + // Minimum execution time: 15_557_000 picoseconds. + Weight::from_parts(15_844_000, 3556) .saturating_add(RocksDbWeight::get().reads(1_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } @@ -956,9 +973,9 @@ impl WeightInfo for () { fn clear_referendum_metadata() -> Weight { // Proof Size summary in bytes: // Measured: `302` - // Estimated: `7184` - // Minimum execution time: 19_622_000 picoseconds. - Weight::from_parts(19_978_000, 7184) + // Estimated: `3666` + // Minimum execution time: 19_940_000 picoseconds. + Weight::from_parts(20_301_000, 3666) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } diff --git a/frame/election-provider-multi-phase/src/weights.rs b/frame/election-provider-multi-phase/src/weights.rs index f9dfdfc19..a1ba3514c 100644 --- a/frame/election-provider-multi-phase/src/weights.rs +++ b/frame/election-provider-multi-phase/src/weights.rs @@ -18,9 +18,9 @@ //! Autogenerated weights for pallet_election_provider_multi_phase //! //! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev -//! DATE: 2023-03-16, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! DATE: 2023-04-06, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` //! WORST CASE MAP SIZE: `1000000` -//! HOSTNAME: `bm3`, CPU: `Intel(R) Core(TM) i7-7700K CPU @ 4.20GHz` +//! HOSTNAME: `bm2`, CPU: `Intel(R) Core(TM) i7-7700K CPU @ 4.20GHz` //! EXECUTION: Some(Wasm), WASM-EXECUTION: Compiled, CHAIN: Some("dev"), DB CACHE: 1024 // Executed Command: @@ -31,9 +31,6 @@ // --steps=50 // --repeat=20 // --pallet=pallet_election_provider_multi_phase -// --no-storage-info -// --no-median-slopes -// --no-min-squares // --extrinsic=* // --execution=wasm // --wasm-execution=compiled @@ -85,9 +82,9 @@ impl WeightInfo for SubstrateWeight { fn on_initialize_nothing() -> Weight { // Proof Size summary in bytes: // Measured: `994` - // Estimated: `14903` - // Minimum execution time: 21_538_000 picoseconds. - Weight::from_parts(22_045_000, 14903) + // Estimated: `3481` + // Minimum execution time: 21_239_000 picoseconds. + Weight::from_parts(21_970_000, 3481) .saturating_add(T::DbWeight::get().reads(8_u64)) } /// Storage: ElectionProviderMultiPhase Round (r:1 w:0) @@ -97,9 +94,9 @@ impl WeightInfo for SubstrateWeight { fn on_initialize_open_signed() -> Weight { // Proof Size summary in bytes: // Measured: `114` - // Estimated: `3198` - // Minimum execution time: 12_696_000 picoseconds. - Weight::from_parts(12_950_000, 3198) + // Estimated: `1599` + // Minimum execution time: 13_913_000 picoseconds. + Weight::from_parts(14_329_000, 1599) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -110,9 +107,9 @@ impl WeightInfo for SubstrateWeight { fn on_initialize_open_unsigned() -> Weight { // Proof Size summary in bytes: // Measured: `114` - // Estimated: `3198` - // Minimum execution time: 14_029_000 picoseconds. - Weight::from_parts(14_288_000, 3198) + // Estimated: `1599` + // Minimum execution time: 15_377_000 picoseconds. + Weight::from_parts(15_714_000, 1599) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -123,9 +120,9 @@ impl WeightInfo for SubstrateWeight { fn finalize_signed_phase_accept_solution() -> Weight { // Proof Size summary in bytes: // Measured: `174` - // Estimated: `3767` - // Minimum execution time: 26_468_000 picoseconds. - Weight::from_parts(26_790_000, 3767) + // Estimated: `3593` + // Minimum execution time: 32_899_000 picoseconds. + Weight::from_parts(33_455_000, 3593) .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(T::DbWeight::get().writes(2_u64)) } @@ -135,8 +132,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `174` // Estimated: `3593` - // Minimum execution time: 18_401_000 picoseconds. - Weight::from_parts(18_686_000, 3593) + // Minimum execution time: 22_532_000 picoseconds. + Weight::from_parts(23_039_000, 3593) .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -152,10 +149,10 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 266_901_000 picoseconds. - Weight::from_parts(270_251_000, 0) - // Standard Error: 1_758 - .saturating_add(Weight::from_parts(167_310, 0).saturating_mul(v.into())) + // Minimum execution time: 253_511_000 picoseconds. + Weight::from_parts(261_190_000, 0) + // Standard Error: 1_621 + .saturating_add(Weight::from_parts(157_608, 0).saturating_mul(v.into())) .saturating_add(T::DbWeight::get().writes(3_u64)) } /// Storage: ElectionProviderMultiPhase SignedSubmissionIndices (r:1 w:1) @@ -181,17 +178,17 @@ impl WeightInfo for SubstrateWeight { fn elect_queued(a: u32, d: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `337 + a * (768 ±0) + d * (48 ±0)` - // Estimated: `16191 + a * (6912 ±0) + d * (441 ±0)` - // Minimum execution time: 286_523_000 picoseconds. - Weight::from_parts(19_101_753, 16191) - // Standard Error: 4_194 - .saturating_add(Weight::from_parts(398_102, 0).saturating_mul(a.into())) - // Standard Error: 6_287 - .saturating_add(Weight::from_parts(188_928, 0).saturating_mul(d.into())) + // Estimated: `3889 + a * (768 ±0) + d * (49 ±0)` + // Minimum execution time: 284_994_000 picoseconds. + Weight::from_parts(97_696_734, 3889) + // Standard Error: 4_172 + .saturating_add(Weight::from_parts(331_569, 0).saturating_mul(a.into())) + // Standard Error: 6_254 + .saturating_add(Weight::from_parts(92_198, 0).saturating_mul(d.into())) .saturating_add(T::DbWeight::get().reads(7_u64)) .saturating_add(T::DbWeight::get().writes(8_u64)) - .saturating_add(Weight::from_parts(0, 6912).saturating_mul(a.into())) - .saturating_add(Weight::from_parts(0, 441).saturating_mul(d.into())) + .saturating_add(Weight::from_parts(0, 768).saturating_mul(a.into())) + .saturating_add(Weight::from_parts(0, 49).saturating_mul(d.into())) } /// Storage: ElectionProviderMultiPhase CurrentPhase (r:1 w:0) /// Proof Skipped: ElectionProviderMultiPhase CurrentPhase (max_values: Some(1), max_size: None, mode: Measured) @@ -208,9 +205,9 @@ impl WeightInfo for SubstrateWeight { fn submit() -> Weight { // Proof Size summary in bytes: // Measured: `893` - // Estimated: `11906` - // Minimum execution time: 47_876_000 picoseconds. - Weight::from_parts(48_464_000, 11906) + // Estimated: `2378` + // Minimum execution time: 52_194_000 picoseconds. + Weight::from_parts(53_062_000, 2378) .saturating_add(T::DbWeight::get().reads(5_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) } @@ -234,18 +231,18 @@ impl WeightInfo for SubstrateWeight { /// The range of component `d` is `[200, 400]`. fn submit_unsigned(v: u32, t: u32, a: u32, _d: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `219 + v * (553 ±0) + t * (32 ±0)` - // Estimated: `11928 + v * (3871 ±0) + t * (224 ±0)` - // Minimum execution time: 4_702_825_000 picoseconds. - Weight::from_parts(4_752_015_000, 11928) - // Standard Error: 14_906 - .saturating_add(Weight::from_parts(131_585, 0).saturating_mul(v.into())) - // Standard Error: 44_174 - .saturating_add(Weight::from_parts(4_167_676, 0).saturating_mul(a.into())) + // Measured: `219 + t * (32 ±0) + v * (553 ±0)` + // Estimated: `1704 + t * (32 ±0) + v * (553 ±0)` + // Minimum execution time: 4_843_067_000 picoseconds. + Weight::from_parts(4_860_833_000, 1704) + // Standard Error: 14_594 + .saturating_add(Weight::from_parts(76_611, 0).saturating_mul(v.into())) + // Standard Error: 43_249 + .saturating_add(Weight::from_parts(4_347_887, 0).saturating_mul(a.into())) .saturating_add(T::DbWeight::get().reads(7_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) - .saturating_add(Weight::from_parts(0, 3871).saturating_mul(v.into())) - .saturating_add(Weight::from_parts(0, 224).saturating_mul(t.into())) + .saturating_add(Weight::from_parts(0, 32).saturating_mul(t.into())) + .saturating_add(Weight::from_parts(0, 553).saturating_mul(v.into())) } /// Storage: ElectionProviderMultiPhase DesiredTargets (r:1 w:0) /// Proof Skipped: ElectionProviderMultiPhase DesiredTargets (max_values: Some(1), max_size: None, mode: Measured) @@ -261,17 +258,17 @@ impl WeightInfo for SubstrateWeight { /// The range of component `d` is `[200, 400]`. fn feasibility_check(v: u32, t: u32, a: u32, _d: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `194 + v * (553 ±0) + t * (32 ±0)` - // Estimated: `6716 + v * (2212 ±0) + t * (128 ±0)` - // Minimum execution time: 4_069_075_000 picoseconds. - Weight::from_parts(4_105_635_000, 6716) - // Standard Error: 12_539 - .saturating_add(Weight::from_parts(163_040, 0).saturating_mul(v.into())) - // Standard Error: 37_160 - .saturating_add(Weight::from_parts(3_343_858, 0).saturating_mul(a.into())) + // Measured: `194 + t * (32 ±0) + v * (553 ±0)` + // Estimated: `1679 + t * (32 ±0) + v * (553 ±0)` + // Minimum execution time: 4_190_524_000 picoseconds. + Weight::from_parts(4_200_207_000, 1679) + // Standard Error: 12_454 + .saturating_add(Weight::from_parts(166_342, 0).saturating_mul(v.into())) + // Standard Error: 36_906 + .saturating_add(Weight::from_parts(3_493_372, 0).saturating_mul(a.into())) .saturating_add(T::DbWeight::get().reads(4_u64)) - .saturating_add(Weight::from_parts(0, 2212).saturating_mul(v.into())) - .saturating_add(Weight::from_parts(0, 128).saturating_mul(t.into())) + .saturating_add(Weight::from_parts(0, 32).saturating_mul(t.into())) + .saturating_add(Weight::from_parts(0, 553).saturating_mul(v.into())) } } @@ -296,9 +293,9 @@ impl WeightInfo for () { fn on_initialize_nothing() -> Weight { // Proof Size summary in bytes: // Measured: `994` - // Estimated: `14903` - // Minimum execution time: 21_538_000 picoseconds. - Weight::from_parts(22_045_000, 14903) + // Estimated: `3481` + // Minimum execution time: 21_239_000 picoseconds. + Weight::from_parts(21_970_000, 3481) .saturating_add(RocksDbWeight::get().reads(8_u64)) } /// Storage: ElectionProviderMultiPhase Round (r:1 w:0) @@ -308,9 +305,9 @@ impl WeightInfo for () { fn on_initialize_open_signed() -> Weight { // Proof Size summary in bytes: // Measured: `114` - // Estimated: `3198` - // Minimum execution time: 12_696_000 picoseconds. - Weight::from_parts(12_950_000, 3198) + // Estimated: `1599` + // Minimum execution time: 13_913_000 picoseconds. + Weight::from_parts(14_329_000, 1599) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } @@ -321,9 +318,9 @@ impl WeightInfo for () { fn on_initialize_open_unsigned() -> Weight { // Proof Size summary in bytes: // Measured: `114` - // Estimated: `3198` - // Minimum execution time: 14_029_000 picoseconds. - Weight::from_parts(14_288_000, 3198) + // Estimated: `1599` + // Minimum execution time: 15_377_000 picoseconds. + Weight::from_parts(15_714_000, 1599) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } @@ -334,9 +331,9 @@ impl WeightInfo for () { fn finalize_signed_phase_accept_solution() -> Weight { // Proof Size summary in bytes: // Measured: `174` - // Estimated: `3767` - // Minimum execution time: 26_468_000 picoseconds. - Weight::from_parts(26_790_000, 3767) + // Estimated: `3593` + // Minimum execution time: 32_899_000 picoseconds. + Weight::from_parts(33_455_000, 3593) .saturating_add(RocksDbWeight::get().reads(1_u64)) .saturating_add(RocksDbWeight::get().writes(2_u64)) } @@ -346,8 +343,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `174` // Estimated: `3593` - // Minimum execution time: 18_401_000 picoseconds. - Weight::from_parts(18_686_000, 3593) + // Minimum execution time: 22_532_000 picoseconds. + Weight::from_parts(23_039_000, 3593) .saturating_add(RocksDbWeight::get().reads(1_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } @@ -363,10 +360,10 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 266_901_000 picoseconds. - Weight::from_parts(270_251_000, 0) - // Standard Error: 1_758 - .saturating_add(Weight::from_parts(167_310, 0).saturating_mul(v.into())) + // Minimum execution time: 253_511_000 picoseconds. + Weight::from_parts(261_190_000, 0) + // Standard Error: 1_621 + .saturating_add(Weight::from_parts(157_608, 0).saturating_mul(v.into())) .saturating_add(RocksDbWeight::get().writes(3_u64)) } /// Storage: ElectionProviderMultiPhase SignedSubmissionIndices (r:1 w:1) @@ -392,17 +389,17 @@ impl WeightInfo for () { fn elect_queued(a: u32, d: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `337 + a * (768 ±0) + d * (48 ±0)` - // Estimated: `16191 + a * (6912 ±0) + d * (441 ±0)` - // Minimum execution time: 286_523_000 picoseconds. - Weight::from_parts(19_101_753, 16191) - // Standard Error: 4_194 - .saturating_add(Weight::from_parts(398_102, 0).saturating_mul(a.into())) - // Standard Error: 6_287 - .saturating_add(Weight::from_parts(188_928, 0).saturating_mul(d.into())) + // Estimated: `3889 + a * (768 ±0) + d * (49 ±0)` + // Minimum execution time: 284_994_000 picoseconds. + Weight::from_parts(97_696_734, 3889) + // Standard Error: 4_172 + .saturating_add(Weight::from_parts(331_569, 0).saturating_mul(a.into())) + // Standard Error: 6_254 + .saturating_add(Weight::from_parts(92_198, 0).saturating_mul(d.into())) .saturating_add(RocksDbWeight::get().reads(7_u64)) .saturating_add(RocksDbWeight::get().writes(8_u64)) - .saturating_add(Weight::from_parts(0, 6912).saturating_mul(a.into())) - .saturating_add(Weight::from_parts(0, 441).saturating_mul(d.into())) + .saturating_add(Weight::from_parts(0, 768).saturating_mul(a.into())) + .saturating_add(Weight::from_parts(0, 49).saturating_mul(d.into())) } /// Storage: ElectionProviderMultiPhase CurrentPhase (r:1 w:0) /// Proof Skipped: ElectionProviderMultiPhase CurrentPhase (max_values: Some(1), max_size: None, mode: Measured) @@ -419,9 +416,9 @@ impl WeightInfo for () { fn submit() -> Weight { // Proof Size summary in bytes: // Measured: `893` - // Estimated: `11906` - // Minimum execution time: 47_876_000 picoseconds. - Weight::from_parts(48_464_000, 11906) + // Estimated: `2378` + // Minimum execution time: 52_194_000 picoseconds. + Weight::from_parts(53_062_000, 2378) .saturating_add(RocksDbWeight::get().reads(5_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) } @@ -445,18 +442,18 @@ impl WeightInfo for () { /// The range of component `d` is `[200, 400]`. fn submit_unsigned(v: u32, t: u32, a: u32, _d: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `219 + v * (553 ±0) + t * (32 ±0)` - // Estimated: `11928 + v * (3871 ±0) + t * (224 ±0)` - // Minimum execution time: 4_702_825_000 picoseconds. - Weight::from_parts(4_752_015_000, 11928) - // Standard Error: 14_906 - .saturating_add(Weight::from_parts(131_585, 0).saturating_mul(v.into())) - // Standard Error: 44_174 - .saturating_add(Weight::from_parts(4_167_676, 0).saturating_mul(a.into())) + // Measured: `219 + t * (32 ±0) + v * (553 ±0)` + // Estimated: `1704 + t * (32 ±0) + v * (553 ±0)` + // Minimum execution time: 4_843_067_000 picoseconds. + Weight::from_parts(4_860_833_000, 1704) + // Standard Error: 14_594 + .saturating_add(Weight::from_parts(76_611, 0).saturating_mul(v.into())) + // Standard Error: 43_249 + .saturating_add(Weight::from_parts(4_347_887, 0).saturating_mul(a.into())) .saturating_add(RocksDbWeight::get().reads(7_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) - .saturating_add(Weight::from_parts(0, 3871).saturating_mul(v.into())) - .saturating_add(Weight::from_parts(0, 224).saturating_mul(t.into())) + .saturating_add(Weight::from_parts(0, 32).saturating_mul(t.into())) + .saturating_add(Weight::from_parts(0, 553).saturating_mul(v.into())) } /// Storage: ElectionProviderMultiPhase DesiredTargets (r:1 w:0) /// Proof Skipped: ElectionProviderMultiPhase DesiredTargets (max_values: Some(1), max_size: None, mode: Measured) @@ -472,16 +469,16 @@ impl WeightInfo for () { /// The range of component `d` is `[200, 400]`. fn feasibility_check(v: u32, t: u32, a: u32, _d: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `194 + v * (553 ±0) + t * (32 ±0)` - // Estimated: `6716 + v * (2212 ±0) + t * (128 ±0)` - // Minimum execution time: 4_069_075_000 picoseconds. - Weight::from_parts(4_105_635_000, 6716) - // Standard Error: 12_539 - .saturating_add(Weight::from_parts(163_040, 0).saturating_mul(v.into())) - // Standard Error: 37_160 - .saturating_add(Weight::from_parts(3_343_858, 0).saturating_mul(a.into())) + // Measured: `194 + t * (32 ±0) + v * (553 ±0)` + // Estimated: `1679 + t * (32 ±0) + v * (553 ±0)` + // Minimum execution time: 4_190_524_000 picoseconds. + Weight::from_parts(4_200_207_000, 1679) + // Standard Error: 12_454 + .saturating_add(Weight::from_parts(166_342, 0).saturating_mul(v.into())) + // Standard Error: 36_906 + .saturating_add(Weight::from_parts(3_493_372, 0).saturating_mul(a.into())) .saturating_add(RocksDbWeight::get().reads(4_u64)) - .saturating_add(Weight::from_parts(0, 2212).saturating_mul(v.into())) - .saturating_add(Weight::from_parts(0, 128).saturating_mul(t.into())) + .saturating_add(Weight::from_parts(0, 32).saturating_mul(t.into())) + .saturating_add(Weight::from_parts(0, 553).saturating_mul(v.into())) } } diff --git a/frame/elections-phragmen/src/weights.rs b/frame/elections-phragmen/src/weights.rs index 0ff6ecf29..f0ebb8639 100644 --- a/frame/elections-phragmen/src/weights.rs +++ b/frame/elections-phragmen/src/weights.rs @@ -18,9 +18,9 @@ //! Autogenerated weights for pallet_elections_phragmen //! //! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev -//! DATE: 2023-03-16, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! DATE: 2023-04-06, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` //! WORST CASE MAP SIZE: `1000000` -//! HOSTNAME: `bm3`, CPU: `Intel(R) Core(TM) i7-7700K CPU @ 4.20GHz` +//! HOSTNAME: `bm2`, CPU: `Intel(R) Core(TM) i7-7700K CPU @ 4.20GHz` //! EXECUTION: Some(Wasm), WASM-EXECUTION: Compiled, CHAIN: Some("dev"), DB CACHE: 1024 // Executed Command: @@ -31,9 +31,6 @@ // --steps=50 // --repeat=20 // --pallet=pallet_elections_phragmen -// --no-storage-info -// --no-median-slopes -// --no-min-squares // --extrinsic=* // --execution=wasm // --wasm-execution=compiled @@ -78,18 +75,20 @@ impl WeightInfo for SubstrateWeight { /// Proof Skipped: Elections Voting (max_values: None, max_size: None, mode: Measured) /// Storage: Balances Locks (r:1 w:1) /// Proof: Balances Locks (max_values: None, max_size: Some(1299), added: 3774, mode: MaxEncodedLen) + /// Storage: Balances Freezes (r:1 w:0) + /// Proof: Balances Freezes (max_values: None, max_size: Some(49), added: 2524, mode: MaxEncodedLen) /// The range of component `v` is `[1, 16]`. fn vote_equal(v: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `403 + v * (80 ±0)` - // Estimated: `14292 + v * (320 ±0)` - // Minimum execution time: 30_206_000 picoseconds. - Weight::from_parts(30_696_455, 14292) - // Standard Error: 1_864 - .saturating_add(Weight::from_parts(124_941, 0).saturating_mul(v.into())) - .saturating_add(T::DbWeight::get().reads(5_u64)) + // Estimated: `4764 + v * (80 ±0)` + // Minimum execution time: 33_623_000 picoseconds. + Weight::from_parts(34_531_239, 4764) + // Standard Error: 1_913 + .saturating_add(Weight::from_parts(131_360, 0).saturating_mul(v.into())) + .saturating_add(T::DbWeight::get().reads(6_u64)) .saturating_add(T::DbWeight::get().writes(2_u64)) - .saturating_add(Weight::from_parts(0, 320).saturating_mul(v.into())) + .saturating_add(Weight::from_parts(0, 80).saturating_mul(v.into())) } /// Storage: Elections Candidates (r:1 w:0) /// Proof Skipped: Elections Candidates (max_values: Some(1), max_size: None, mode: Measured) @@ -101,18 +100,20 @@ impl WeightInfo for SubstrateWeight { /// Proof Skipped: Elections Voting (max_values: None, max_size: None, mode: Measured) /// Storage: Balances Locks (r:1 w:1) /// Proof: Balances Locks (max_values: None, max_size: Some(1299), added: 3774, mode: MaxEncodedLen) + /// Storage: Balances Freezes (r:1 w:0) + /// Proof: Balances Freezes (max_values: None, max_size: Some(49), added: 2524, mode: MaxEncodedLen) /// The range of component `v` is `[2, 16]`. fn vote_more(v: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `371 + v * (80 ±0)` - // Estimated: `14164 + v * (320 ±0)` - // Minimum execution time: 40_331_000 picoseconds. - Weight::from_parts(40_936_971, 14164) - // Standard Error: 2_377 - .saturating_add(Weight::from_parts(142_332, 0).saturating_mul(v.into())) - .saturating_add(T::DbWeight::get().reads(5_u64)) + // Estimated: `4764 + v * (80 ±0)` + // Minimum execution time: 46_106_000 picoseconds. + Weight::from_parts(47_067_453, 4764) + // Standard Error: 2_441 + .saturating_add(Weight::from_parts(130_306, 0).saturating_mul(v.into())) + .saturating_add(T::DbWeight::get().reads(6_u64)) .saturating_add(T::DbWeight::get().writes(2_u64)) - .saturating_add(Weight::from_parts(0, 320).saturating_mul(v.into())) + .saturating_add(Weight::from_parts(0, 80).saturating_mul(v.into())) } /// Storage: Elections Candidates (r:1 w:0) /// Proof Skipped: Elections Candidates (max_values: Some(1), max_size: None, mode: Measured) @@ -124,30 +125,34 @@ impl WeightInfo for SubstrateWeight { /// Proof Skipped: Elections Voting (max_values: None, max_size: None, mode: Measured) /// Storage: Balances Locks (r:1 w:1) /// Proof: Balances Locks (max_values: None, max_size: Some(1299), added: 3774, mode: MaxEncodedLen) + /// Storage: Balances Freezes (r:1 w:0) + /// Proof: Balances Freezes (max_values: None, max_size: Some(49), added: 2524, mode: MaxEncodedLen) /// The range of component `v` is `[2, 16]`. fn vote_less(v: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `403 + v * (80 ±0)` - // Estimated: `14292 + v * (320 ±0)` - // Minimum execution time: 40_343_000 picoseconds. - Weight::from_parts(41_055_673, 14292) - // Standard Error: 3_150 - .saturating_add(Weight::from_parts(129_894, 0).saturating_mul(v.into())) - .saturating_add(T::DbWeight::get().reads(5_u64)) + // Estimated: `4764 + v * (80 ±0)` + // Minimum execution time: 46_094_000 picoseconds. + Weight::from_parts(47_054_638, 4764) + // Standard Error: 2_651 + .saturating_add(Weight::from_parts(137_251, 0).saturating_mul(v.into())) + .saturating_add(T::DbWeight::get().reads(6_u64)) .saturating_add(T::DbWeight::get().writes(2_u64)) - .saturating_add(Weight::from_parts(0, 320).saturating_mul(v.into())) + .saturating_add(Weight::from_parts(0, 80).saturating_mul(v.into())) } /// Storage: Elections Voting (r:1 w:1) /// Proof Skipped: Elections Voting (max_values: None, max_size: None, mode: Measured) /// Storage: Balances Locks (r:1 w:1) /// Proof: Balances Locks (max_values: None, max_size: Some(1299), added: 3774, mode: MaxEncodedLen) + /// Storage: Balances Freezes (r:1 w:0) + /// Proof: Balances Freezes (max_values: None, max_size: Some(49), added: 2524, mode: MaxEncodedLen) fn remove_voter() -> Weight { // Proof Size summary in bytes: // Measured: `925` - // Estimated: `9154` - // Minimum execution time: 36_348_000 picoseconds. - Weight::from_parts(36_677_000, 9154) - .saturating_add(T::DbWeight::get().reads(2_u64)) + // Estimated: `4764` + // Minimum execution time: 49_652_000 picoseconds. + Weight::from_parts(50_217_000, 4764) + .saturating_add(T::DbWeight::get().reads(3_u64)) .saturating_add(T::DbWeight::get().writes(2_u64)) } /// Storage: Elections Candidates (r:1 w:1) @@ -160,14 +165,14 @@ impl WeightInfo for SubstrateWeight { fn submit_candidacy(c: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `1570 + c * (48 ±0)` - // Estimated: `9165 + c * (144 ±0)` - // Minimum execution time: 32_963_000 picoseconds. - Weight::from_parts(33_778_406, 9165) - // Standard Error: 2_792 - .saturating_add(Weight::from_parts(71_214, 0).saturating_mul(c.into())) + // Estimated: `3055 + c * (48 ±0)` + // Minimum execution time: 37_797_000 picoseconds. + Weight::from_parts(38_384_713, 3055) + // Standard Error: 1_008 + .saturating_add(Weight::from_parts(71_486, 0).saturating_mul(c.into())) .saturating_add(T::DbWeight::get().reads(3_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) - .saturating_add(Weight::from_parts(0, 144).saturating_mul(c.into())) + .saturating_add(Weight::from_parts(0, 48).saturating_mul(c.into())) } /// Storage: Elections Candidates (r:1 w:1) /// Proof Skipped: Elections Candidates (max_values: Some(1), max_size: None, mode: Measured) @@ -176,10 +181,10 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `285 + c * (48 ±0)` // Estimated: `1770 + c * (48 ±0)` - // Minimum execution time: 26_462_000 picoseconds. - Weight::from_parts(27_289_268, 1770) - // Standard Error: 915 - .saturating_add(Weight::from_parts(45_079, 0).saturating_mul(c.into())) + // Minimum execution time: 31_112_000 picoseconds. + Weight::from_parts(31_660_924, 1770) + // Standard Error: 754 + .saturating_add(Weight::from_parts(48_689, 0).saturating_mul(c.into())) .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) .saturating_add(Weight::from_parts(0, 48).saturating_mul(c.into())) @@ -197,9 +202,9 @@ impl WeightInfo for SubstrateWeight { fn renounce_candidacy_members() -> Weight { // Proof Size summary in bytes: // Measured: `1900` - // Estimated: `15440` - // Minimum execution time: 44_414_000 picoseconds. - Weight::from_parts(44_900_000, 15440) + // Estimated: `3385` + // Minimum execution time: 47_487_000 picoseconds. + Weight::from_parts(47_795_000, 3385) .saturating_add(T::DbWeight::get().reads(4_u64)) .saturating_add(T::DbWeight::get().writes(4_u64)) } @@ -209,8 +214,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `880` // Estimated: `2365` - // Minimum execution time: 28_688_000 picoseconds. - Weight::from_parts(29_027_000, 2365) + // Minimum execution time: 31_479_000 picoseconds. + Weight::from_parts(32_093_000, 2365) .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -238,9 +243,9 @@ impl WeightInfo for SubstrateWeight { fn remove_member_with_replacement() -> Weight { // Proof Size summary in bytes: // Measured: `1900` - // Estimated: `19033` - // Minimum execution time: 50_280_000 picoseconds. - Weight::from_parts(50_906_000, 19033) + // Estimated: `3593` + // Minimum execution time: 53_395_000 picoseconds. + Weight::from_parts(53_952_000, 3593) .saturating_add(T::DbWeight::get().reads(5_u64)) .saturating_add(T::DbWeight::get().writes(5_u64)) } @@ -254,6 +259,8 @@ impl WeightInfo for SubstrateWeight { /// Proof Skipped: Elections Candidates (max_values: Some(1), max_size: None, mode: Measured) /// Storage: Balances Locks (r:512 w:512) /// Proof: Balances Locks (max_values: None, max_size: Some(1299), added: 3774, mode: MaxEncodedLen) + /// Storage: Balances Freezes (r:512 w:0) + /// Proof: Balances Freezes (max_values: None, max_size: Some(49), added: 2524, mode: MaxEncodedLen) /// Storage: System Account (r:512 w:512) /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) /// The range of component `v` is `[256, 512]`. @@ -261,15 +268,15 @@ impl WeightInfo for SubstrateWeight { fn clean_defunct_voters(v: u32, _d: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `1115 + v * (811 ±0)` - // Estimated: `14388 + v * (12096 ±0)` - // Minimum execution time: 14_785_043_000 picoseconds. - Weight::from_parts(14_842_583_000, 14388) - // Standard Error: 242_757 - .saturating_add(Weight::from_parts(36_168_971, 0).saturating_mul(v.into())) + // Estimated: `4587 + v * (3774 ±0)` + // Minimum execution time: 18_089_406_000 picoseconds. + Weight::from_parts(18_125_024_000, 4587) + // Standard Error: 296_666 + .saturating_add(Weight::from_parts(42_527_045, 0).saturating_mul(v.into())) .saturating_add(T::DbWeight::get().reads(4_u64)) - .saturating_add(T::DbWeight::get().reads((3_u64).saturating_mul(v.into()))) + .saturating_add(T::DbWeight::get().reads((4_u64).saturating_mul(v.into()))) .saturating_add(T::DbWeight::get().writes((3_u64).saturating_mul(v.into()))) - .saturating_add(Weight::from_parts(0, 12096).saturating_mul(v.into())) + .saturating_add(Weight::from_parts(0, 3774).saturating_mul(v.into())) } /// Storage: Elections Candidates (r:1 w:1) /// Proof Skipped: Elections Candidates (max_values: Some(1), max_size: None, mode: Measured) @@ -294,22 +301,22 @@ impl WeightInfo for SubstrateWeight { /// The range of component `e` is `[512, 8192]`. fn election_phragmen(c: u32, v: u32, e: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `0 + v * (606 ±0) + e * (28 ±0)` - // Estimated: `309059 + v * (5005 ±8) + e * (89 ±0) + c * (2135 ±7)` - // Minimum execution time: 1_074_961_000 picoseconds. - Weight::from_parts(1_079_460_000, 309059) - // Standard Error: 598_993 - .saturating_add(Weight::from_parts(17_097_981, 0).saturating_mul(v.into())) - // Standard Error: 38_432 - .saturating_add(Weight::from_parts(820_141, 0).saturating_mul(e.into())) + // Measured: `0 + e * (28 ±0) + v * (606 ±0)` + // Estimated: `178887 + c * (2135 ±7) + e * (12 ±0) + v * (2653 ±6)` + // Minimum execution time: 1_193_774_000 picoseconds. + Weight::from_parts(1_196_649_000, 178887) + // Standard Error: 617_531 + .saturating_add(Weight::from_parts(17_672_923, 0).saturating_mul(v.into())) + // Standard Error: 39_622 + .saturating_add(Weight::from_parts(846_866, 0).saturating_mul(e.into())) .saturating_add(T::DbWeight::get().reads(21_u64)) .saturating_add(T::DbWeight::get().reads((1_u64).saturating_mul(c.into()))) .saturating_add(T::DbWeight::get().reads((1_u64).saturating_mul(v.into()))) .saturating_add(T::DbWeight::get().writes(6_u64)) .saturating_add(T::DbWeight::get().writes((1_u64).saturating_mul(c.into()))) - .saturating_add(Weight::from_parts(0, 5005).saturating_mul(v.into())) - .saturating_add(Weight::from_parts(0, 89).saturating_mul(e.into())) .saturating_add(Weight::from_parts(0, 2135).saturating_mul(c.into())) + .saturating_add(Weight::from_parts(0, 12).saturating_mul(e.into())) + .saturating_add(Weight::from_parts(0, 2653).saturating_mul(v.into())) } } @@ -325,18 +332,20 @@ impl WeightInfo for () { /// Proof Skipped: Elections Voting (max_values: None, max_size: None, mode: Measured) /// Storage: Balances Locks (r:1 w:1) /// Proof: Balances Locks (max_values: None, max_size: Some(1299), added: 3774, mode: MaxEncodedLen) + /// Storage: Balances Freezes (r:1 w:0) + /// Proof: Balances Freezes (max_values: None, max_size: Some(49), added: 2524, mode: MaxEncodedLen) /// The range of component `v` is `[1, 16]`. fn vote_equal(v: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `403 + v * (80 ±0)` - // Estimated: `14292 + v * (320 ±0)` - // Minimum execution time: 30_206_000 picoseconds. - Weight::from_parts(30_696_455, 14292) - // Standard Error: 1_864 - .saturating_add(Weight::from_parts(124_941, 0).saturating_mul(v.into())) - .saturating_add(RocksDbWeight::get().reads(5_u64)) + // Estimated: `4764 + v * (80 ±0)` + // Minimum execution time: 33_623_000 picoseconds. + Weight::from_parts(34_531_239, 4764) + // Standard Error: 1_913 + .saturating_add(Weight::from_parts(131_360, 0).saturating_mul(v.into())) + .saturating_add(RocksDbWeight::get().reads(6_u64)) .saturating_add(RocksDbWeight::get().writes(2_u64)) - .saturating_add(Weight::from_parts(0, 320).saturating_mul(v.into())) + .saturating_add(Weight::from_parts(0, 80).saturating_mul(v.into())) } /// Storage: Elections Candidates (r:1 w:0) /// Proof Skipped: Elections Candidates (max_values: Some(1), max_size: None, mode: Measured) @@ -348,18 +357,20 @@ impl WeightInfo for () { /// Proof Skipped: Elections Voting (max_values: None, max_size: None, mode: Measured) /// Storage: Balances Locks (r:1 w:1) /// Proof: Balances Locks (max_values: None, max_size: Some(1299), added: 3774, mode: MaxEncodedLen) + /// Storage: Balances Freezes (r:1 w:0) + /// Proof: Balances Freezes (max_values: None, max_size: Some(49), added: 2524, mode: MaxEncodedLen) /// The range of component `v` is `[2, 16]`. fn vote_more(v: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `371 + v * (80 ±0)` - // Estimated: `14164 + v * (320 ±0)` - // Minimum execution time: 40_331_000 picoseconds. - Weight::from_parts(40_936_971, 14164) - // Standard Error: 2_377 - .saturating_add(Weight::from_parts(142_332, 0).saturating_mul(v.into())) - .saturating_add(RocksDbWeight::get().reads(5_u64)) + // Estimated: `4764 + v * (80 ±0)` + // Minimum execution time: 46_106_000 picoseconds. + Weight::from_parts(47_067_453, 4764) + // Standard Error: 2_441 + .saturating_add(Weight::from_parts(130_306, 0).saturating_mul(v.into())) + .saturating_add(RocksDbWeight::get().reads(6_u64)) .saturating_add(RocksDbWeight::get().writes(2_u64)) - .saturating_add(Weight::from_parts(0, 320).saturating_mul(v.into())) + .saturating_add(Weight::from_parts(0, 80).saturating_mul(v.into())) } /// Storage: Elections Candidates (r:1 w:0) /// Proof Skipped: Elections Candidates (max_values: Some(1), max_size: None, mode: Measured) @@ -371,30 +382,34 @@ impl WeightInfo for () { /// Proof Skipped: Elections Voting (max_values: None, max_size: None, mode: Measured) /// Storage: Balances Locks (r:1 w:1) /// Proof: Balances Locks (max_values: None, max_size: Some(1299), added: 3774, mode: MaxEncodedLen) + /// Storage: Balances Freezes (r:1 w:0) + /// Proof: Balances Freezes (max_values: None, max_size: Some(49), added: 2524, mode: MaxEncodedLen) /// The range of component `v` is `[2, 16]`. fn vote_less(v: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `403 + v * (80 ±0)` - // Estimated: `14292 + v * (320 ±0)` - // Minimum execution time: 40_343_000 picoseconds. - Weight::from_parts(41_055_673, 14292) - // Standard Error: 3_150 - .saturating_add(Weight::from_parts(129_894, 0).saturating_mul(v.into())) - .saturating_add(RocksDbWeight::get().reads(5_u64)) + // Estimated: `4764 + v * (80 ±0)` + // Minimum execution time: 46_094_000 picoseconds. + Weight::from_parts(47_054_638, 4764) + // Standard Error: 2_651 + .saturating_add(Weight::from_parts(137_251, 0).saturating_mul(v.into())) + .saturating_add(RocksDbWeight::get().reads(6_u64)) .saturating_add(RocksDbWeight::get().writes(2_u64)) - .saturating_add(Weight::from_parts(0, 320).saturating_mul(v.into())) + .saturating_add(Weight::from_parts(0, 80).saturating_mul(v.into())) } /// Storage: Elections Voting (r:1 w:1) /// Proof Skipped: Elections Voting (max_values: None, max_size: None, mode: Measured) /// Storage: Balances Locks (r:1 w:1) /// Proof: Balances Locks (max_values: None, max_size: Some(1299), added: 3774, mode: MaxEncodedLen) + /// Storage: Balances Freezes (r:1 w:0) + /// Proof: Balances Freezes (max_values: None, max_size: Some(49), added: 2524, mode: MaxEncodedLen) fn remove_voter() -> Weight { // Proof Size summary in bytes: // Measured: `925` - // Estimated: `9154` - // Minimum execution time: 36_348_000 picoseconds. - Weight::from_parts(36_677_000, 9154) - .saturating_add(RocksDbWeight::get().reads(2_u64)) + // Estimated: `4764` + // Minimum execution time: 49_652_000 picoseconds. + Weight::from_parts(50_217_000, 4764) + .saturating_add(RocksDbWeight::get().reads(3_u64)) .saturating_add(RocksDbWeight::get().writes(2_u64)) } /// Storage: Elections Candidates (r:1 w:1) @@ -407,14 +422,14 @@ impl WeightInfo for () { fn submit_candidacy(c: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `1570 + c * (48 ±0)` - // Estimated: `9165 + c * (144 ±0)` - // Minimum execution time: 32_963_000 picoseconds. - Weight::from_parts(33_778_406, 9165) - // Standard Error: 2_792 - .saturating_add(Weight::from_parts(71_214, 0).saturating_mul(c.into())) + // Estimated: `3055 + c * (48 ±0)` + // Minimum execution time: 37_797_000 picoseconds. + Weight::from_parts(38_384_713, 3055) + // Standard Error: 1_008 + .saturating_add(Weight::from_parts(71_486, 0).saturating_mul(c.into())) .saturating_add(RocksDbWeight::get().reads(3_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) - .saturating_add(Weight::from_parts(0, 144).saturating_mul(c.into())) + .saturating_add(Weight::from_parts(0, 48).saturating_mul(c.into())) } /// Storage: Elections Candidates (r:1 w:1) /// Proof Skipped: Elections Candidates (max_values: Some(1), max_size: None, mode: Measured) @@ -423,10 +438,10 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `285 + c * (48 ±0)` // Estimated: `1770 + c * (48 ±0)` - // Minimum execution time: 26_462_000 picoseconds. - Weight::from_parts(27_289_268, 1770) - // Standard Error: 915 - .saturating_add(Weight::from_parts(45_079, 0).saturating_mul(c.into())) + // Minimum execution time: 31_112_000 picoseconds. + Weight::from_parts(31_660_924, 1770) + // Standard Error: 754 + .saturating_add(Weight::from_parts(48_689, 0).saturating_mul(c.into())) .saturating_add(RocksDbWeight::get().reads(1_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) .saturating_add(Weight::from_parts(0, 48).saturating_mul(c.into())) @@ -444,9 +459,9 @@ impl WeightInfo for () { fn renounce_candidacy_members() -> Weight { // Proof Size summary in bytes: // Measured: `1900` - // Estimated: `15440` - // Minimum execution time: 44_414_000 picoseconds. - Weight::from_parts(44_900_000, 15440) + // Estimated: `3385` + // Minimum execution time: 47_487_000 picoseconds. + Weight::from_parts(47_795_000, 3385) .saturating_add(RocksDbWeight::get().reads(4_u64)) .saturating_add(RocksDbWeight::get().writes(4_u64)) } @@ -456,8 +471,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `880` // Estimated: `2365` - // Minimum execution time: 28_688_000 picoseconds. - Weight::from_parts(29_027_000, 2365) + // Minimum execution time: 31_479_000 picoseconds. + Weight::from_parts(32_093_000, 2365) .saturating_add(RocksDbWeight::get().reads(1_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } @@ -485,9 +500,9 @@ impl WeightInfo for () { fn remove_member_with_replacement() -> Weight { // Proof Size summary in bytes: // Measured: `1900` - // Estimated: `19033` - // Minimum execution time: 50_280_000 picoseconds. - Weight::from_parts(50_906_000, 19033) + // Estimated: `3593` + // Minimum execution time: 53_395_000 picoseconds. + Weight::from_parts(53_952_000, 3593) .saturating_add(RocksDbWeight::get().reads(5_u64)) .saturating_add(RocksDbWeight::get().writes(5_u64)) } @@ -501,6 +516,8 @@ impl WeightInfo for () { /// Proof Skipped: Elections Candidates (max_values: Some(1), max_size: None, mode: Measured) /// Storage: Balances Locks (r:512 w:512) /// Proof: Balances Locks (max_values: None, max_size: Some(1299), added: 3774, mode: MaxEncodedLen) + /// Storage: Balances Freezes (r:512 w:0) + /// Proof: Balances Freezes (max_values: None, max_size: Some(49), added: 2524, mode: MaxEncodedLen) /// Storage: System Account (r:512 w:512) /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) /// The range of component `v` is `[256, 512]`. @@ -508,15 +525,15 @@ impl WeightInfo for () { fn clean_defunct_voters(v: u32, _d: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `1115 + v * (811 ±0)` - // Estimated: `14388 + v * (12096 ±0)` - // Minimum execution time: 14_785_043_000 picoseconds. - Weight::from_parts(14_842_583_000, 14388) - // Standard Error: 242_757 - .saturating_add(Weight::from_parts(36_168_971, 0).saturating_mul(v.into())) + // Estimated: `4587 + v * (3774 ±0)` + // Minimum execution time: 18_089_406_000 picoseconds. + Weight::from_parts(18_125_024_000, 4587) + // Standard Error: 296_666 + .saturating_add(Weight::from_parts(42_527_045, 0).saturating_mul(v.into())) .saturating_add(RocksDbWeight::get().reads(4_u64)) - .saturating_add(RocksDbWeight::get().reads((3_u64).saturating_mul(v.into()))) + .saturating_add(RocksDbWeight::get().reads((4_u64).saturating_mul(v.into()))) .saturating_add(RocksDbWeight::get().writes((3_u64).saturating_mul(v.into()))) - .saturating_add(Weight::from_parts(0, 12096).saturating_mul(v.into())) + .saturating_add(Weight::from_parts(0, 3774).saturating_mul(v.into())) } /// Storage: Elections Candidates (r:1 w:1) /// Proof Skipped: Elections Candidates (max_values: Some(1), max_size: None, mode: Measured) @@ -541,21 +558,21 @@ impl WeightInfo for () { /// The range of component `e` is `[512, 8192]`. fn election_phragmen(c: u32, v: u32, e: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `0 + v * (606 ±0) + e * (28 ±0)` - // Estimated: `309059 + v * (5005 ±8) + e * (89 ±0) + c * (2135 ±7)` - // Minimum execution time: 1_074_961_000 picoseconds. - Weight::from_parts(1_079_460_000, 309059) - // Standard Error: 598_993 - .saturating_add(Weight::from_parts(17_097_981, 0).saturating_mul(v.into())) - // Standard Error: 38_432 - .saturating_add(Weight::from_parts(820_141, 0).saturating_mul(e.into())) + // Measured: `0 + e * (28 ±0) + v * (606 ±0)` + // Estimated: `178887 + c * (2135 ±7) + e * (12 ±0) + v * (2653 ±6)` + // Minimum execution time: 1_193_774_000 picoseconds. + Weight::from_parts(1_196_649_000, 178887) + // Standard Error: 617_531 + .saturating_add(Weight::from_parts(17_672_923, 0).saturating_mul(v.into())) + // Standard Error: 39_622 + .saturating_add(Weight::from_parts(846_866, 0).saturating_mul(e.into())) .saturating_add(RocksDbWeight::get().reads(21_u64)) .saturating_add(RocksDbWeight::get().reads((1_u64).saturating_mul(c.into()))) .saturating_add(RocksDbWeight::get().reads((1_u64).saturating_mul(v.into()))) .saturating_add(RocksDbWeight::get().writes(6_u64)) .saturating_add(RocksDbWeight::get().writes((1_u64).saturating_mul(c.into()))) - .saturating_add(Weight::from_parts(0, 5005).saturating_mul(v.into())) - .saturating_add(Weight::from_parts(0, 89).saturating_mul(e.into())) .saturating_add(Weight::from_parts(0, 2135).saturating_mul(c.into())) + .saturating_add(Weight::from_parts(0, 12).saturating_mul(e.into())) + .saturating_add(Weight::from_parts(0, 2653).saturating_mul(v.into())) } } diff --git a/frame/fast-unstake/src/weights.rs b/frame/fast-unstake/src/weights.rs index b47d4f056..27414a8a8 100644 --- a/frame/fast-unstake/src/weights.rs +++ b/frame/fast-unstake/src/weights.rs @@ -18,9 +18,9 @@ //! Autogenerated weights for pallet_fast_unstake //! //! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev -//! DATE: 2023-03-16, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! DATE: 2023-04-06, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` //! WORST CASE MAP SIZE: `1000000` -//! HOSTNAME: `bm3`, CPU: `Intel(R) Core(TM) i7-7700K CPU @ 4.20GHz` +//! HOSTNAME: `bm2`, CPU: `Intel(R) Core(TM) i7-7700K CPU @ 4.20GHz` //! EXECUTION: Some(Wasm), WASM-EXECUTION: Compiled, CHAIN: Some("dev"), DB CACHE: 1024 // Executed Command: @@ -31,9 +31,6 @@ // --steps=50 // --repeat=20 // --pallet=pallet_fast_unstake -// --no-storage-info -// --no-median-slopes -// --no-min-squares // --extrinsic=* // --execution=wasm // --wasm-execution=compiled @@ -85,6 +82,8 @@ impl WeightInfo for SubstrateWeight { /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) /// Storage: Balances Locks (r:64 w:64) /// Proof: Balances Locks (max_values: None, max_size: Some(1299), added: 3774, mode: MaxEncodedLen) + /// Storage: Balances Freezes (r:64 w:0) + /// Proof: Balances Freezes (max_values: None, max_size: Some(49), added: 2524, mode: MaxEncodedLen) /// Storage: Staking Ledger (r:0 w:64) /// Proof: Staking Ledger (max_values: None, max_size: Some(1091), added: 3566, mode: MaxEncodedLen) /// Storage: Staking Payee (r:0 w:64) @@ -93,16 +92,16 @@ impl WeightInfo for SubstrateWeight { fn on_idle_unstake(b: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `1344 + b * (343 ±0)` - // Estimated: `23254 + b * (17642 ±0)` - // Minimum execution time: 78_484_000 picoseconds. - Weight::from_parts(39_186_541, 23254) - // Standard Error: 37_606 - .saturating_add(Weight::from_parts(42_433_199, 0).saturating_mul(b.into())) + // Estimated: `7253 + b * (3774 ±0)` + // Minimum execution time: 92_282_000 picoseconds. + Weight::from_parts(31_665_344, 7253) + // Standard Error: 35_348 + .saturating_add(Weight::from_parts(57_005_152, 0).saturating_mul(b.into())) .saturating_add(T::DbWeight::get().reads(6_u64)) - .saturating_add(T::DbWeight::get().reads((6_u64).saturating_mul(b.into()))) + .saturating_add(T::DbWeight::get().reads((7_u64).saturating_mul(b.into()))) .saturating_add(T::DbWeight::get().writes(1_u64)) .saturating_add(T::DbWeight::get().writes((5_u64).saturating_mul(b.into()))) - .saturating_add(Weight::from_parts(0, 17642).saturating_mul(b.into())) + .saturating_add(Weight::from_parts(0, 3774).saturating_mul(b.into())) } /// Storage: FastUnstake ErasToCheckPerBlock (r:1 w:0) /// Proof: FastUnstake ErasToCheckPerBlock (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) @@ -122,19 +121,19 @@ impl WeightInfo for SubstrateWeight { /// The range of component `b` is `[1, 64]`. fn on_idle_check(v: u32, b: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `1512 + v * (10037 ±0) + b * (48 ±0)` - // Estimated: `20889 + v * (22551 ±0) + b * (98 ±0)` - // Minimum execution time: 1_538_727_000 picoseconds. - Weight::from_parts(1_549_586_000, 20889) - // Standard Error: 14_184_223 - .saturating_add(Weight::from_parts(452_966_006, 0).saturating_mul(v.into())) - // Standard Error: 56_752_692 - .saturating_add(Weight::from_parts(1_775_601_329, 0).saturating_mul(b.into())) + // Measured: `1512 + b * (48 ±0) + v * (10037 ±0)` + // Estimated: `7253 + b * (49 ±0) + v * (12513 ±0)` + // Minimum execution time: 1_547_716_000 picoseconds. + Weight::from_parts(1_552_476_000, 7253) + // Standard Error: 13_914_457 + .saturating_add(Weight::from_parts(445_314_876, 0).saturating_mul(v.into())) + // Standard Error: 55_673_329 + .saturating_add(Weight::from_parts(1_749_024_692, 0).saturating_mul(b.into())) .saturating_add(T::DbWeight::get().reads(7_u64)) .saturating_add(T::DbWeight::get().reads((1_u64).saturating_mul(v.into()))) .saturating_add(T::DbWeight::get().writes(1_u64)) - .saturating_add(Weight::from_parts(0, 22551).saturating_mul(v.into())) - .saturating_add(Weight::from_parts(0, 98).saturating_mul(b.into())) + .saturating_add(Weight::from_parts(0, 49).saturating_mul(b.into())) + .saturating_add(Weight::from_parts(0, 12513).saturating_mul(v.into())) } /// Storage: FastUnstake ErasToCheckPerBlock (r:1 w:0) /// Proof: FastUnstake ErasToCheckPerBlock (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) @@ -162,15 +161,17 @@ impl WeightInfo for SubstrateWeight { /// Proof: Staking CurrentEra (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) /// Storage: Balances Locks (r:1 w:1) /// Proof: Balances Locks (max_values: None, max_size: Some(1299), added: 3774, mode: MaxEncodedLen) + /// Storage: Balances Freezes (r:1 w:0) + /// Proof: Balances Freezes (max_values: None, max_size: Some(49), added: 2524, mode: MaxEncodedLen) /// Storage: FastUnstake CounterForQueue (r:1 w:1) /// Proof: FastUnstake CounterForQueue (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) fn register_fast_unstake() -> Weight { // Proof Size summary in bytes: // Measured: `1964` - // Estimated: `45775` - // Minimum execution time: 117_552_000 picoseconds. - Weight::from_parts(118_303_000, 45775) - .saturating_add(T::DbWeight::get().reads(14_u64)) + // Estimated: `7253` + // Minimum execution time: 124_644_000 picoseconds. + Weight::from_parts(125_793_000, 7253) + .saturating_add(T::DbWeight::get().reads(15_u64)) .saturating_add(T::DbWeight::get().writes(9_u64)) } /// Storage: FastUnstake ErasToCheckPerBlock (r:1 w:0) @@ -186,9 +187,9 @@ impl WeightInfo for SubstrateWeight { fn deregister() -> Weight { // Proof Size summary in bytes: // Measured: `1223` - // Estimated: `18308` - // Minimum execution time: 42_291_000 picoseconds. - Weight::from_parts(42_835_000, 18308) + // Estimated: `7253` + // Minimum execution time: 45_037_000 picoseconds. + Weight::from_parts(45_545_000, 7253) .saturating_add(T::DbWeight::get().reads(5_u64)) .saturating_add(T::DbWeight::get().writes(2_u64)) } @@ -198,8 +199,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 3_551_000 picoseconds. - Weight::from_parts(3_632_000, 0) + // Minimum execution time: 3_228_000 picoseconds. + Weight::from_parts(3_428_000, 0) .saturating_add(T::DbWeight::get().writes(1_u64)) } } @@ -230,6 +231,8 @@ impl WeightInfo for () { /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) /// Storage: Balances Locks (r:64 w:64) /// Proof: Balances Locks (max_values: None, max_size: Some(1299), added: 3774, mode: MaxEncodedLen) + /// Storage: Balances Freezes (r:64 w:0) + /// Proof: Balances Freezes (max_values: None, max_size: Some(49), added: 2524, mode: MaxEncodedLen) /// Storage: Staking Ledger (r:0 w:64) /// Proof: Staking Ledger (max_values: None, max_size: Some(1091), added: 3566, mode: MaxEncodedLen) /// Storage: Staking Payee (r:0 w:64) @@ -238,16 +241,16 @@ impl WeightInfo for () { fn on_idle_unstake(b: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `1344 + b * (343 ±0)` - // Estimated: `23254 + b * (17642 ±0)` - // Minimum execution time: 78_484_000 picoseconds. - Weight::from_parts(39_186_541, 23254) - // Standard Error: 37_606 - .saturating_add(Weight::from_parts(42_433_199, 0).saturating_mul(b.into())) + // Estimated: `7253 + b * (3774 ±0)` + // Minimum execution time: 92_282_000 picoseconds. + Weight::from_parts(31_665_344, 7253) + // Standard Error: 35_348 + .saturating_add(Weight::from_parts(57_005_152, 0).saturating_mul(b.into())) .saturating_add(RocksDbWeight::get().reads(6_u64)) - .saturating_add(RocksDbWeight::get().reads((6_u64).saturating_mul(b.into()))) + .saturating_add(RocksDbWeight::get().reads((7_u64).saturating_mul(b.into()))) .saturating_add(RocksDbWeight::get().writes(1_u64)) .saturating_add(RocksDbWeight::get().writes((5_u64).saturating_mul(b.into()))) - .saturating_add(Weight::from_parts(0, 17642).saturating_mul(b.into())) + .saturating_add(Weight::from_parts(0, 3774).saturating_mul(b.into())) } /// Storage: FastUnstake ErasToCheckPerBlock (r:1 w:0) /// Proof: FastUnstake ErasToCheckPerBlock (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) @@ -267,19 +270,19 @@ impl WeightInfo for () { /// The range of component `b` is `[1, 64]`. fn on_idle_check(v: u32, b: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `1512 + v * (10037 ±0) + b * (48 ±0)` - // Estimated: `20889 + v * (22551 ±0) + b * (98 ±0)` - // Minimum execution time: 1_538_727_000 picoseconds. - Weight::from_parts(1_549_586_000, 20889) - // Standard Error: 14_184_223 - .saturating_add(Weight::from_parts(452_966_006, 0).saturating_mul(v.into())) - // Standard Error: 56_752_692 - .saturating_add(Weight::from_parts(1_775_601_329, 0).saturating_mul(b.into())) + // Measured: `1512 + b * (48 ±0) + v * (10037 ±0)` + // Estimated: `7253 + b * (49 ±0) + v * (12513 ±0)` + // Minimum execution time: 1_547_716_000 picoseconds. + Weight::from_parts(1_552_476_000, 7253) + // Standard Error: 13_914_457 + .saturating_add(Weight::from_parts(445_314_876, 0).saturating_mul(v.into())) + // Standard Error: 55_673_329 + .saturating_add(Weight::from_parts(1_749_024_692, 0).saturating_mul(b.into())) .saturating_add(RocksDbWeight::get().reads(7_u64)) .saturating_add(RocksDbWeight::get().reads((1_u64).saturating_mul(v.into()))) .saturating_add(RocksDbWeight::get().writes(1_u64)) - .saturating_add(Weight::from_parts(0, 22551).saturating_mul(v.into())) - .saturating_add(Weight::from_parts(0, 98).saturating_mul(b.into())) + .saturating_add(Weight::from_parts(0, 49).saturating_mul(b.into())) + .saturating_add(Weight::from_parts(0, 12513).saturating_mul(v.into())) } /// Storage: FastUnstake ErasToCheckPerBlock (r:1 w:0) /// Proof: FastUnstake ErasToCheckPerBlock (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) @@ -307,15 +310,17 @@ impl WeightInfo for () { /// Proof: Staking CurrentEra (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) /// Storage: Balances Locks (r:1 w:1) /// Proof: Balances Locks (max_values: None, max_size: Some(1299), added: 3774, mode: MaxEncodedLen) + /// Storage: Balances Freezes (r:1 w:0) + /// Proof: Balances Freezes (max_values: None, max_size: Some(49), added: 2524, mode: MaxEncodedLen) /// Storage: FastUnstake CounterForQueue (r:1 w:1) /// Proof: FastUnstake CounterForQueue (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) fn register_fast_unstake() -> Weight { // Proof Size summary in bytes: // Measured: `1964` - // Estimated: `45775` - // Minimum execution time: 117_552_000 picoseconds. - Weight::from_parts(118_303_000, 45775) - .saturating_add(RocksDbWeight::get().reads(14_u64)) + // Estimated: `7253` + // Minimum execution time: 124_644_000 picoseconds. + Weight::from_parts(125_793_000, 7253) + .saturating_add(RocksDbWeight::get().reads(15_u64)) .saturating_add(RocksDbWeight::get().writes(9_u64)) } /// Storage: FastUnstake ErasToCheckPerBlock (r:1 w:0) @@ -331,9 +336,9 @@ impl WeightInfo for () { fn deregister() -> Weight { // Proof Size summary in bytes: // Measured: `1223` - // Estimated: `18308` - // Minimum execution time: 42_291_000 picoseconds. - Weight::from_parts(42_835_000, 18308) + // Estimated: `7253` + // Minimum execution time: 45_037_000 picoseconds. + Weight::from_parts(45_545_000, 7253) .saturating_add(RocksDbWeight::get().reads(5_u64)) .saturating_add(RocksDbWeight::get().writes(2_u64)) } @@ -343,8 +348,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 3_551_000 picoseconds. - Weight::from_parts(3_632_000, 0) + // Minimum execution time: 3_228_000 picoseconds. + Weight::from_parts(3_428_000, 0) .saturating_add(RocksDbWeight::get().writes(1_u64)) } } diff --git a/frame/glutton/src/weights.rs b/frame/glutton/src/weights.rs index bd409645a..82bac91c6 100644 --- a/frame/glutton/src/weights.rs +++ b/frame/glutton/src/weights.rs @@ -18,9 +18,9 @@ //! Autogenerated weights for pallet_glutton //! //! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev -//! DATE: 2023-03-16, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! DATE: 2023-04-06, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` //! WORST CASE MAP SIZE: `1000000` -//! HOSTNAME: `bm3`, CPU: `Intel(R) Core(TM) i7-7700K CPU @ 4.20GHz` +//! HOSTNAME: `bm2`, CPU: `Intel(R) Core(TM) i7-7700K CPU @ 4.20GHz` //! EXECUTION: Some(Wasm), WASM-EXECUTION: Compiled, CHAIN: Some("dev"), DB CACHE: 1024 // Executed Command: @@ -31,9 +31,6 @@ // --steps=50 // --repeat=20 // --pallet=pallet_glutton -// --no-storage-info -// --no-median-slopes -// --no-min-squares // --extrinsic=* // --execution=wasm // --wasm-execution=compiled @@ -74,10 +71,10 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `4` // Estimated: `1489` - // Minimum execution time: 10_384_000 picoseconds. - Weight::from_parts(10_598_000, 1489) - // Standard Error: 1_369 - .saturating_add(Weight::from_parts(1_580_155, 0).saturating_mul(n.into())) + // Minimum execution time: 10_410_000 picoseconds. + Weight::from_parts(10_515_000, 1489) + // Standard Error: 1_069 + .saturating_add(Weight::from_parts(1_513_013, 0).saturating_mul(n.into())) .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) .saturating_add(T::DbWeight::get().writes((1_u64).saturating_mul(n.into()))) @@ -91,10 +88,10 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `65` // Estimated: `1489` - // Minimum execution time: 11_022_000 picoseconds. - Weight::from_parts(11_319_000, 1489) - // Standard Error: 1_108 - .saturating_add(Weight::from_parts(1_048_682, 0).saturating_mul(n.into())) + // Minimum execution time: 11_105_000 picoseconds. + Weight::from_parts(584_850, 1489) + // Standard Error: 1_417 + .saturating_add(Weight::from_parts(1_054_988, 0).saturating_mul(n.into())) .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) .saturating_add(T::DbWeight::get().writes((1_u64).saturating_mul(n.into()))) @@ -104,10 +101,10 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 660_000 picoseconds. - Weight::from_parts(7_814_200, 0) - // Standard Error: 33 - .saturating_add(Weight::from_parts(94_390, 0).saturating_mul(i.into())) + // Minimum execution time: 709_000 picoseconds. + Weight::from_parts(7_409_096, 0) + // Standard Error: 23 + .saturating_add(Weight::from_parts(95_342, 0).saturating_mul(i.into())) } /// Storage: Glutton TrashData (r:5000 w:0) /// Proof: Glutton TrashData (max_values: Some(65000), max_size: Some(1036), added: 3016, mode: MaxEncodedLen) @@ -116,10 +113,10 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `119036 + i * (1022 ±0)` // Estimated: `990 + i * (3016 ±0)` - // Minimum execution time: 712_000 picoseconds. - Weight::from_parts(814_000, 990) - // Standard Error: 1_805 - .saturating_add(Weight::from_parts(5_407_690, 0).saturating_mul(i.into())) + // Minimum execution time: 584_000 picoseconds. + Weight::from_parts(674_000, 990) + // Standard Error: 1_802 + .saturating_add(Weight::from_parts(5_360_522, 0).saturating_mul(i.into())) .saturating_add(T::DbWeight::get().reads((1_u64).saturating_mul(i.into()))) .saturating_add(Weight::from_parts(0, 3016).saturating_mul(i.into())) } @@ -132,9 +129,9 @@ impl WeightInfo for SubstrateWeight { fn on_idle_high_proof_waste() -> Weight { // Proof Size summary in bytes: // Measured: `1900466` - // Estimated: `5242760` - // Minimum execution time: 54_903_045_000 picoseconds. - Weight::from_parts(55_194_691_000, 5242760) + // Estimated: `5239782` + // Minimum execution time: 57_124_610_000 picoseconds. + Weight::from_parts(57_256_059_000, 5239782) .saturating_add(T::DbWeight::get().reads(1739_u64)) } /// Storage: Glutton Storage (r:1 w:0) @@ -146,9 +143,9 @@ impl WeightInfo for SubstrateWeight { fn on_idle_low_proof_waste() -> Weight { // Proof Size summary in bytes: // Measured: `9516` - // Estimated: `19048` - // Minimum execution time: 97_295_331_000 picoseconds. - Weight::from_parts(97_853_502_000, 19048) + // Estimated: `16070` + // Minimum execution time: 101_500_066_000 picoseconds. + Weight::from_parts(101_621_640_000, 16070) .saturating_add(T::DbWeight::get().reads(7_u64)) } /// Storage: Glutton Storage (r:1 w:0) @@ -158,9 +155,9 @@ impl WeightInfo for SubstrateWeight { fn empty_on_idle() -> Weight { // Proof Size summary in bytes: // Measured: `4` - // Estimated: `2978` - // Minimum execution time: 4_280_000 picoseconds. - Weight::from_parts(4_381_000, 2978) + // Estimated: `1489` + // Minimum execution time: 4_164_000 picoseconds. + Weight::from_parts(4_378_000, 1489) .saturating_add(T::DbWeight::get().reads(2_u64)) } /// Storage: Glutton Compute (r:0 w:1) @@ -169,8 +166,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 8_758_000 picoseconds. - Weight::from_parts(8_988_000, 0) + // Minimum execution time: 8_795_000 picoseconds. + Weight::from_parts(9_076_000, 0) .saturating_add(T::DbWeight::get().writes(1_u64)) } /// Storage: Glutton Storage (r:0 w:1) @@ -179,8 +176,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 8_901_000 picoseconds. - Weight::from_parts(9_291_000, 0) + // Minimum execution time: 8_979_000 picoseconds. + Weight::from_parts(9_195_000, 0) .saturating_add(T::DbWeight::get().writes(1_u64)) } } @@ -196,10 +193,10 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `4` // Estimated: `1489` - // Minimum execution time: 10_384_000 picoseconds. - Weight::from_parts(10_598_000, 1489) - // Standard Error: 1_369 - .saturating_add(Weight::from_parts(1_580_155, 0).saturating_mul(n.into())) + // Minimum execution time: 10_410_000 picoseconds. + Weight::from_parts(10_515_000, 1489) + // Standard Error: 1_069 + .saturating_add(Weight::from_parts(1_513_013, 0).saturating_mul(n.into())) .saturating_add(RocksDbWeight::get().reads(1_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) .saturating_add(RocksDbWeight::get().writes((1_u64).saturating_mul(n.into()))) @@ -213,10 +210,10 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `65` // Estimated: `1489` - // Minimum execution time: 11_022_000 picoseconds. - Weight::from_parts(11_319_000, 1489) - // Standard Error: 1_108 - .saturating_add(Weight::from_parts(1_048_682, 0).saturating_mul(n.into())) + // Minimum execution time: 11_105_000 picoseconds. + Weight::from_parts(584_850, 1489) + // Standard Error: 1_417 + .saturating_add(Weight::from_parts(1_054_988, 0).saturating_mul(n.into())) .saturating_add(RocksDbWeight::get().reads(1_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) .saturating_add(RocksDbWeight::get().writes((1_u64).saturating_mul(n.into()))) @@ -226,10 +223,10 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 660_000 picoseconds. - Weight::from_parts(7_814_200, 0) - // Standard Error: 33 - .saturating_add(Weight::from_parts(94_390, 0).saturating_mul(i.into())) + // Minimum execution time: 709_000 picoseconds. + Weight::from_parts(7_409_096, 0) + // Standard Error: 23 + .saturating_add(Weight::from_parts(95_342, 0).saturating_mul(i.into())) } /// Storage: Glutton TrashData (r:5000 w:0) /// Proof: Glutton TrashData (max_values: Some(65000), max_size: Some(1036), added: 3016, mode: MaxEncodedLen) @@ -238,10 +235,10 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `119036 + i * (1022 ±0)` // Estimated: `990 + i * (3016 ±0)` - // Minimum execution time: 712_000 picoseconds. - Weight::from_parts(814_000, 990) - // Standard Error: 1_805 - .saturating_add(Weight::from_parts(5_407_690, 0).saturating_mul(i.into())) + // Minimum execution time: 584_000 picoseconds. + Weight::from_parts(674_000, 990) + // Standard Error: 1_802 + .saturating_add(Weight::from_parts(5_360_522, 0).saturating_mul(i.into())) .saturating_add(RocksDbWeight::get().reads((1_u64).saturating_mul(i.into()))) .saturating_add(Weight::from_parts(0, 3016).saturating_mul(i.into())) } @@ -254,9 +251,9 @@ impl WeightInfo for () { fn on_idle_high_proof_waste() -> Weight { // Proof Size summary in bytes: // Measured: `1900466` - // Estimated: `5242760` - // Minimum execution time: 54_903_045_000 picoseconds. - Weight::from_parts(55_194_691_000, 5242760) + // Estimated: `5239782` + // Minimum execution time: 57_124_610_000 picoseconds. + Weight::from_parts(57_256_059_000, 5239782) .saturating_add(RocksDbWeight::get().reads(1739_u64)) } /// Storage: Glutton Storage (r:1 w:0) @@ -268,9 +265,9 @@ impl WeightInfo for () { fn on_idle_low_proof_waste() -> Weight { // Proof Size summary in bytes: // Measured: `9516` - // Estimated: `19048` - // Minimum execution time: 97_295_331_000 picoseconds. - Weight::from_parts(97_853_502_000, 19048) + // Estimated: `16070` + // Minimum execution time: 101_500_066_000 picoseconds. + Weight::from_parts(101_621_640_000, 16070) .saturating_add(RocksDbWeight::get().reads(7_u64)) } /// Storage: Glutton Storage (r:1 w:0) @@ -280,9 +277,9 @@ impl WeightInfo for () { fn empty_on_idle() -> Weight { // Proof Size summary in bytes: // Measured: `4` - // Estimated: `2978` - // Minimum execution time: 4_280_000 picoseconds. - Weight::from_parts(4_381_000, 2978) + // Estimated: `1489` + // Minimum execution time: 4_164_000 picoseconds. + Weight::from_parts(4_378_000, 1489) .saturating_add(RocksDbWeight::get().reads(2_u64)) } /// Storage: Glutton Compute (r:0 w:1) @@ -291,8 +288,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 8_758_000 picoseconds. - Weight::from_parts(8_988_000, 0) + // Minimum execution time: 8_795_000 picoseconds. + Weight::from_parts(9_076_000, 0) .saturating_add(RocksDbWeight::get().writes(1_u64)) } /// Storage: Glutton Storage (r:0 w:1) @@ -301,8 +298,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 8_901_000 picoseconds. - Weight::from_parts(9_291_000, 0) + // Minimum execution time: 8_979_000 picoseconds. + Weight::from_parts(9_195_000, 0) .saturating_add(RocksDbWeight::get().writes(1_u64)) } } diff --git a/frame/identity/src/weights.rs b/frame/identity/src/weights.rs index bad6aa768..faefd00fb 100644 --- a/frame/identity/src/weights.rs +++ b/frame/identity/src/weights.rs @@ -18,9 +18,9 @@ //! Autogenerated weights for pallet_identity //! //! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev -//! DATE: 2023-03-16, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! DATE: 2023-04-06, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` //! WORST CASE MAP SIZE: `1000000` -//! HOSTNAME: `bm3`, CPU: `Intel(R) Core(TM) i7-7700K CPU @ 4.20GHz` +//! HOSTNAME: `bm2`, CPU: `Intel(R) Core(TM) i7-7700K CPU @ 4.20GHz` //! EXECUTION: Some(Wasm), WASM-EXECUTION: Compiled, CHAIN: Some("dev"), DB CACHE: 1024 // Executed Command: @@ -31,9 +31,6 @@ // --steps=50 // --repeat=20 // --pallet=pallet_identity -// --no-storage-info -// --no-median-slopes -// --no-min-squares // --extrinsic=* // --execution=wasm // --wasm-execution=compiled @@ -79,10 +76,10 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `32 + r * (57 ±0)` // Estimated: `2626` - // Minimum execution time: 13_092_000 picoseconds. - Weight::from_parts(13_798_719, 2626) - // Standard Error: 1_553 - .saturating_add(Weight::from_parts(109_215, 0).saturating_mul(r.into())) + // Minimum execution time: 12_851_000 picoseconds. + Weight::from_parts(13_448_645, 2626) + // Standard Error: 1_636 + .saturating_add(Weight::from_parts(113_654, 0).saturating_mul(r.into())) .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -94,12 +91,12 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `442 + r * (5 ±0)` // Estimated: `11003` - // Minimum execution time: 30_800_000 picoseconds. - Weight::from_parts(31_140_040, 11003) - // Standard Error: 4_896 - .saturating_add(Weight::from_parts(69_348, 0).saturating_mul(r.into())) - // Standard Error: 955 - .saturating_add(Weight::from_parts(430_082, 0).saturating_mul(x.into())) + // Minimum execution time: 33_342_000 picoseconds. + Weight::from_parts(33_155_116, 11003) + // Standard Error: 2_307 + .saturating_add(Weight::from_parts(56_409, 0).saturating_mul(r.into())) + // Standard Error: 450 + .saturating_add(Weight::from_parts(437_684, 0).saturating_mul(x.into())) .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -113,11 +110,11 @@ impl WeightInfo for SubstrateWeight { fn set_subs_new(s: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `101` - // Estimated: `18716 + s * (2589 ±0)` - // Minimum execution time: 10_301_000 picoseconds. - Weight::from_parts(23_255_636, 18716) - // Standard Error: 5_562 - .saturating_add(Weight::from_parts(2_978_765, 0).saturating_mul(s.into())) + // Estimated: `11003 + s * (2589 ±0)` + // Minimum execution time: 10_315_000 picoseconds. + Weight::from_parts(26_535_526, 11003) + // Standard Error: 4_344 + .saturating_add(Weight::from_parts(3_016_873, 0).saturating_mul(s.into())) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().reads((1_u64).saturating_mul(s.into()))) .saturating_add(T::DbWeight::get().writes(1_u64)) @@ -134,11 +131,11 @@ impl WeightInfo for SubstrateWeight { fn set_subs_old(p: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `194 + p * (32 ±0)` - // Estimated: `17726` - // Minimum execution time: 10_095_000 picoseconds. - Weight::from_parts(23_197_533, 17726) - // Standard Error: 3_460 - .saturating_add(Weight::from_parts(1_227_498, 0).saturating_mul(p.into())) + // Estimated: `11003` + // Minimum execution time: 10_220_000 picoseconds. + Weight::from_parts(25_050_056, 11003) + // Standard Error: 3_621 + .saturating_add(Weight::from_parts(1_291_143, 0).saturating_mul(p.into())) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) .saturating_add(T::DbWeight::get().writes((1_u64).saturating_mul(p.into()))) @@ -155,13 +152,13 @@ impl WeightInfo for SubstrateWeight { fn clear_identity(_r: u32, s: u32, x: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `469 + r * (5 ±0) + s * (32 ±0) + x * (66 ±0)` - // Estimated: `17726` - // Minimum execution time: 51_054_000 picoseconds. - Weight::from_parts(30_184_047, 17726) - // Standard Error: 2_441 - .saturating_add(Weight::from_parts(1_221_983, 0).saturating_mul(s.into())) - // Standard Error: 2_441 - .saturating_add(Weight::from_parts(234_084, 0).saturating_mul(x.into())) + // Estimated: `11003` + // Minimum execution time: 56_018_000 picoseconds. + Weight::from_parts(37_757_186, 11003) + // Standard Error: 1_852 + .saturating_add(Weight::from_parts(1_257_980, 0).saturating_mul(s.into())) + // Standard Error: 1_852 + .saturating_add(Weight::from_parts(215_426, 0).saturating_mul(x.into())) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().writes(2_u64)) .saturating_add(T::DbWeight::get().writes((1_u64).saturating_mul(s.into()))) @@ -175,13 +172,13 @@ impl WeightInfo for SubstrateWeight { fn request_judgement(r: u32, x: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `367 + r * (57 ±0) + x * (66 ±0)` - // Estimated: `13629` - // Minimum execution time: 32_747_000 picoseconds. - Weight::from_parts(29_557_659, 13629) - // Standard Error: 4_616 - .saturating_add(Weight::from_parts(200_494, 0).saturating_mul(r.into())) - // Standard Error: 900 - .saturating_add(Weight::from_parts(468_381, 0).saturating_mul(x.into())) + // Estimated: `11003` + // Minimum execution time: 34_792_000 picoseconds. + Weight::from_parts(35_368_327, 11003) + // Standard Error: 3_476 + .saturating_add(Weight::from_parts(78_981, 0).saturating_mul(r.into())) + // Standard Error: 678 + .saturating_add(Weight::from_parts(459_077, 0).saturating_mul(x.into())) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -189,16 +186,14 @@ impl WeightInfo for SubstrateWeight { /// Proof: Identity IdentityOf (max_values: None, max_size: Some(7538), added: 10013, mode: MaxEncodedLen) /// The range of component `r` is `[1, 20]`. /// The range of component `x` is `[0, 100]`. - fn cancel_request(r: u32, x: u32, ) -> Weight { + fn cancel_request(_r: u32, x: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `398 + x * (66 ±0)` // Estimated: `11003` - // Minimum execution time: 28_853_000 picoseconds. - Weight::from_parts(26_811_119, 11003) - // Standard Error: 15_667 - .saturating_add(Weight::from_parts(240_647, 0).saturating_mul(r.into())) - // Standard Error: 3_056 - .saturating_add(Weight::from_parts(462_536, 0).saturating_mul(x.into())) + // Minimum execution time: 31_306_000 picoseconds. + Weight::from_parts(33_304_799, 11003) + // Standard Error: 892 + .saturating_add(Weight::from_parts(451_655, 0).saturating_mul(x.into())) .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -209,10 +204,10 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `89 + r * (57 ±0)` // Estimated: `2626` - // Minimum execution time: 8_413_000 picoseconds. - Weight::from_parts(8_846_436, 2626) - // Standard Error: 1_470 - .saturating_add(Weight::from_parts(113_780, 0).saturating_mul(r.into())) + // Minimum execution time: 8_215_000 picoseconds. + Weight::from_parts(8_692_102, 2626) + // Standard Error: 1_455 + .saturating_add(Weight::from_parts(110_912, 0).saturating_mul(r.into())) .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -223,10 +218,10 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `89 + r * (57 ±0)` // Estimated: `2626` - // Minimum execution time: 8_753_000 picoseconds. - Weight::from_parts(9_143_938, 2626) - // Standard Error: 1_490 - .saturating_add(Weight::from_parts(113_406, 0).saturating_mul(r.into())) + // Minimum execution time: 8_397_000 picoseconds. + Weight::from_parts(8_787_656, 2626) + // Standard Error: 1_440 + .saturating_add(Weight::from_parts(111_212, 0).saturating_mul(r.into())) .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -237,10 +232,10 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `89 + r * (57 ±0)` // Estimated: `2626` - // Minimum execution time: 8_320_000 picoseconds. - Weight::from_parts(8_800_306, 2626) - // Standard Error: 1_345 - .saturating_add(Weight::from_parts(125_258, 0).saturating_mul(r.into())) + // Minimum execution time: 8_466_000 picoseconds. + Weight::from_parts(8_689_763, 2626) + // Standard Error: 1_536 + .saturating_add(Weight::from_parts(106_371, 0).saturating_mul(r.into())) .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -253,13 +248,13 @@ impl WeightInfo for SubstrateWeight { fn provide_judgement(r: u32, x: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `445 + r * (57 ±0) + x * (66 ±0)` - // Estimated: `13629` - // Minimum execution time: 25_313_000 picoseconds. - Weight::from_parts(23_968_548, 13629) - // Standard Error: 4_143 - .saturating_add(Weight::from_parts(164_678, 0).saturating_mul(r.into())) - // Standard Error: 766 - .saturating_add(Weight::from_parts(713_576, 0).saturating_mul(x.into())) + // Estimated: `11003` + // Minimum execution time: 25_132_000 picoseconds. + Weight::from_parts(20_582_313, 11003) + // Standard Error: 10_427 + .saturating_add(Weight::from_parts(277_545, 0).saturating_mul(r.into())) + // Standard Error: 1_929 + .saturating_add(Weight::from_parts(747_966, 0).saturating_mul(x.into())) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -277,15 +272,15 @@ impl WeightInfo for SubstrateWeight { fn kill_identity(r: u32, s: u32, x: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `676 + r * (5 ±0) + s * (32 ±0) + x * (66 ±0)` - // Estimated: `21319` - // Minimum execution time: 64_638_000 picoseconds. - Weight::from_parts(43_317_654, 21319) - // Standard Error: 10_182 - .saturating_add(Weight::from_parts(61_967, 0).saturating_mul(r.into())) - // Standard Error: 1_988 - .saturating_add(Weight::from_parts(1_224_202, 0).saturating_mul(s.into())) - // Standard Error: 1_988 - .saturating_add(Weight::from_parts(233_897, 0).saturating_mul(x.into())) + // Estimated: `11003` + // Minimum execution time: 72_043_000 picoseconds. + Weight::from_parts(50_994_735, 11003) + // Standard Error: 9_304 + .saturating_add(Weight::from_parts(123_052, 0).saturating_mul(r.into())) + // Standard Error: 1_817 + .saturating_add(Weight::from_parts(1_256_713, 0).saturating_mul(s.into())) + // Standard Error: 1_817 + .saturating_add(Weight::from_parts(219_242, 0).saturating_mul(x.into())) .saturating_add(T::DbWeight::get().reads(3_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) .saturating_add(T::DbWeight::get().writes((1_u64).saturating_mul(s.into()))) @@ -300,11 +295,11 @@ impl WeightInfo for SubstrateWeight { fn add_sub(s: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `475 + s * (36 ±0)` - // Estimated: `21305` - // Minimum execution time: 28_661_000 picoseconds. - Weight::from_parts(33_432_641, 21305) - // Standard Error: 1_547 - .saturating_add(Weight::from_parts(71_314, 0).saturating_mul(s.into())) + // Estimated: `11003` + // Minimum execution time: 30_747_000 picoseconds. + Weight::from_parts(35_975_985, 11003) + // Standard Error: 1_625 + .saturating_add(Weight::from_parts(73_263, 0).saturating_mul(s.into())) .saturating_add(T::DbWeight::get().reads(3_u64)) .saturating_add(T::DbWeight::get().writes(2_u64)) } @@ -316,11 +311,11 @@ impl WeightInfo for SubstrateWeight { fn rename_sub(s: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `591 + s * (3 ±0)` - // Estimated: `14582` - // Minimum execution time: 13_885_000 picoseconds. - Weight::from_parts(16_105_165, 14582) - // Standard Error: 562 - .saturating_add(Weight::from_parts(17_087, 0).saturating_mul(s.into())) + // Estimated: `11003` + // Minimum execution time: 13_586_000 picoseconds. + Weight::from_parts(15_909_245, 11003) + // Standard Error: 611 + .saturating_add(Weight::from_parts(16_949, 0).saturating_mul(s.into())) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -334,11 +329,11 @@ impl WeightInfo for SubstrateWeight { fn remove_sub(s: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `638 + s * (35 ±0)` - // Estimated: `21305` - // Minimum execution time: 32_192_000 picoseconds. - Weight::from_parts(35_393_444, 21305) - // Standard Error: 2_198 - .saturating_add(Weight::from_parts(62_316, 0).saturating_mul(s.into())) + // Estimated: `11003` + // Minimum execution time: 34_286_000 picoseconds. + Weight::from_parts(37_391_401, 11003) + // Standard Error: 1_099 + .saturating_add(Weight::from_parts(61_165, 0).saturating_mul(s.into())) .saturating_add(T::DbWeight::get().reads(3_u64)) .saturating_add(T::DbWeight::get().writes(2_u64)) } @@ -350,11 +345,11 @@ impl WeightInfo for SubstrateWeight { fn quit_sub(s: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `564 + s * (37 ±0)` - // Estimated: `10302` - // Minimum execution time: 20_803_000 picoseconds. - Weight::from_parts(23_462_393, 10302) - // Standard Error: 1_619 - .saturating_add(Weight::from_parts(59_938, 0).saturating_mul(s.into())) + // Estimated: `6723` + // Minimum execution time: 22_197_000 picoseconds. + Weight::from_parts(24_630_311, 6723) + // Standard Error: 1_092 + .saturating_add(Weight::from_parts(63_415, 0).saturating_mul(s.into())) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().writes(2_u64)) } @@ -369,10 +364,10 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `32 + r * (57 ±0)` // Estimated: `2626` - // Minimum execution time: 13_092_000 picoseconds. - Weight::from_parts(13_798_719, 2626) - // Standard Error: 1_553 - .saturating_add(Weight::from_parts(109_215, 0).saturating_mul(r.into())) + // Minimum execution time: 12_851_000 picoseconds. + Weight::from_parts(13_448_645, 2626) + // Standard Error: 1_636 + .saturating_add(Weight::from_parts(113_654, 0).saturating_mul(r.into())) .saturating_add(RocksDbWeight::get().reads(1_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } @@ -384,12 +379,12 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `442 + r * (5 ±0)` // Estimated: `11003` - // Minimum execution time: 30_800_000 picoseconds. - Weight::from_parts(31_140_040, 11003) - // Standard Error: 4_896 - .saturating_add(Weight::from_parts(69_348, 0).saturating_mul(r.into())) - // Standard Error: 955 - .saturating_add(Weight::from_parts(430_082, 0).saturating_mul(x.into())) + // Minimum execution time: 33_342_000 picoseconds. + Weight::from_parts(33_155_116, 11003) + // Standard Error: 2_307 + .saturating_add(Weight::from_parts(56_409, 0).saturating_mul(r.into())) + // Standard Error: 450 + .saturating_add(Weight::from_parts(437_684, 0).saturating_mul(x.into())) .saturating_add(RocksDbWeight::get().reads(1_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } @@ -403,11 +398,11 @@ impl WeightInfo for () { fn set_subs_new(s: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `101` - // Estimated: `18716 + s * (2589 ±0)` - // Minimum execution time: 10_301_000 picoseconds. - Weight::from_parts(23_255_636, 18716) - // Standard Error: 5_562 - .saturating_add(Weight::from_parts(2_978_765, 0).saturating_mul(s.into())) + // Estimated: `11003 + s * (2589 ±0)` + // Minimum execution time: 10_315_000 picoseconds. + Weight::from_parts(26_535_526, 11003) + // Standard Error: 4_344 + .saturating_add(Weight::from_parts(3_016_873, 0).saturating_mul(s.into())) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().reads((1_u64).saturating_mul(s.into()))) .saturating_add(RocksDbWeight::get().writes(1_u64)) @@ -424,11 +419,11 @@ impl WeightInfo for () { fn set_subs_old(p: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `194 + p * (32 ±0)` - // Estimated: `17726` - // Minimum execution time: 10_095_000 picoseconds. - Weight::from_parts(23_197_533, 17726) - // Standard Error: 3_460 - .saturating_add(Weight::from_parts(1_227_498, 0).saturating_mul(p.into())) + // Estimated: `11003` + // Minimum execution time: 10_220_000 picoseconds. + Weight::from_parts(25_050_056, 11003) + // Standard Error: 3_621 + .saturating_add(Weight::from_parts(1_291_143, 0).saturating_mul(p.into())) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) .saturating_add(RocksDbWeight::get().writes((1_u64).saturating_mul(p.into()))) @@ -445,13 +440,13 @@ impl WeightInfo for () { fn clear_identity(_r: u32, s: u32, x: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `469 + r * (5 ±0) + s * (32 ±0) + x * (66 ±0)` - // Estimated: `17726` - // Minimum execution time: 51_054_000 picoseconds. - Weight::from_parts(30_184_047, 17726) - // Standard Error: 2_441 - .saturating_add(Weight::from_parts(1_221_983, 0).saturating_mul(s.into())) - // Standard Error: 2_441 - .saturating_add(Weight::from_parts(234_084, 0).saturating_mul(x.into())) + // Estimated: `11003` + // Minimum execution time: 56_018_000 picoseconds. + Weight::from_parts(37_757_186, 11003) + // Standard Error: 1_852 + .saturating_add(Weight::from_parts(1_257_980, 0).saturating_mul(s.into())) + // Standard Error: 1_852 + .saturating_add(Weight::from_parts(215_426, 0).saturating_mul(x.into())) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().writes(2_u64)) .saturating_add(RocksDbWeight::get().writes((1_u64).saturating_mul(s.into()))) @@ -465,13 +460,13 @@ impl WeightInfo for () { fn request_judgement(r: u32, x: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `367 + r * (57 ±0) + x * (66 ±0)` - // Estimated: `13629` - // Minimum execution time: 32_747_000 picoseconds. - Weight::from_parts(29_557_659, 13629) - // Standard Error: 4_616 - .saturating_add(Weight::from_parts(200_494, 0).saturating_mul(r.into())) - // Standard Error: 900 - .saturating_add(Weight::from_parts(468_381, 0).saturating_mul(x.into())) + // Estimated: `11003` + // Minimum execution time: 34_792_000 picoseconds. + Weight::from_parts(35_368_327, 11003) + // Standard Error: 3_476 + .saturating_add(Weight::from_parts(78_981, 0).saturating_mul(r.into())) + // Standard Error: 678 + .saturating_add(Weight::from_parts(459_077, 0).saturating_mul(x.into())) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } @@ -479,16 +474,14 @@ impl WeightInfo for () { /// Proof: Identity IdentityOf (max_values: None, max_size: Some(7538), added: 10013, mode: MaxEncodedLen) /// The range of component `r` is `[1, 20]`. /// The range of component `x` is `[0, 100]`. - fn cancel_request(r: u32, x: u32, ) -> Weight { + fn cancel_request(_r: u32, x: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `398 + x * (66 ±0)` // Estimated: `11003` - // Minimum execution time: 28_853_000 picoseconds. - Weight::from_parts(26_811_119, 11003) - // Standard Error: 15_667 - .saturating_add(Weight::from_parts(240_647, 0).saturating_mul(r.into())) - // Standard Error: 3_056 - .saturating_add(Weight::from_parts(462_536, 0).saturating_mul(x.into())) + // Minimum execution time: 31_306_000 picoseconds. + Weight::from_parts(33_304_799, 11003) + // Standard Error: 892 + .saturating_add(Weight::from_parts(451_655, 0).saturating_mul(x.into())) .saturating_add(RocksDbWeight::get().reads(1_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } @@ -499,10 +492,10 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `89 + r * (57 ±0)` // Estimated: `2626` - // Minimum execution time: 8_413_000 picoseconds. - Weight::from_parts(8_846_436, 2626) - // Standard Error: 1_470 - .saturating_add(Weight::from_parts(113_780, 0).saturating_mul(r.into())) + // Minimum execution time: 8_215_000 picoseconds. + Weight::from_parts(8_692_102, 2626) + // Standard Error: 1_455 + .saturating_add(Weight::from_parts(110_912, 0).saturating_mul(r.into())) .saturating_add(RocksDbWeight::get().reads(1_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } @@ -513,10 +506,10 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `89 + r * (57 ±0)` // Estimated: `2626` - // Minimum execution time: 8_753_000 picoseconds. - Weight::from_parts(9_143_938, 2626) - // Standard Error: 1_490 - .saturating_add(Weight::from_parts(113_406, 0).saturating_mul(r.into())) + // Minimum execution time: 8_397_000 picoseconds. + Weight::from_parts(8_787_656, 2626) + // Standard Error: 1_440 + .saturating_add(Weight::from_parts(111_212, 0).saturating_mul(r.into())) .saturating_add(RocksDbWeight::get().reads(1_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } @@ -527,10 +520,10 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `89 + r * (57 ±0)` // Estimated: `2626` - // Minimum execution time: 8_320_000 picoseconds. - Weight::from_parts(8_800_306, 2626) - // Standard Error: 1_345 - .saturating_add(Weight::from_parts(125_258, 0).saturating_mul(r.into())) + // Minimum execution time: 8_466_000 picoseconds. + Weight::from_parts(8_689_763, 2626) + // Standard Error: 1_536 + .saturating_add(Weight::from_parts(106_371, 0).saturating_mul(r.into())) .saturating_add(RocksDbWeight::get().reads(1_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } @@ -543,13 +536,13 @@ impl WeightInfo for () { fn provide_judgement(r: u32, x: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `445 + r * (57 ±0) + x * (66 ±0)` - // Estimated: `13629` - // Minimum execution time: 25_313_000 picoseconds. - Weight::from_parts(23_968_548, 13629) - // Standard Error: 4_143 - .saturating_add(Weight::from_parts(164_678, 0).saturating_mul(r.into())) - // Standard Error: 766 - .saturating_add(Weight::from_parts(713_576, 0).saturating_mul(x.into())) + // Estimated: `11003` + // Minimum execution time: 25_132_000 picoseconds. + Weight::from_parts(20_582_313, 11003) + // Standard Error: 10_427 + .saturating_add(Weight::from_parts(277_545, 0).saturating_mul(r.into())) + // Standard Error: 1_929 + .saturating_add(Weight::from_parts(747_966, 0).saturating_mul(x.into())) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } @@ -567,15 +560,15 @@ impl WeightInfo for () { fn kill_identity(r: u32, s: u32, x: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `676 + r * (5 ±0) + s * (32 ±0) + x * (66 ±0)` - // Estimated: `21319` - // Minimum execution time: 64_638_000 picoseconds. - Weight::from_parts(43_317_654, 21319) - // Standard Error: 10_182 - .saturating_add(Weight::from_parts(61_967, 0).saturating_mul(r.into())) - // Standard Error: 1_988 - .saturating_add(Weight::from_parts(1_224_202, 0).saturating_mul(s.into())) - // Standard Error: 1_988 - .saturating_add(Weight::from_parts(233_897, 0).saturating_mul(x.into())) + // Estimated: `11003` + // Minimum execution time: 72_043_000 picoseconds. + Weight::from_parts(50_994_735, 11003) + // Standard Error: 9_304 + .saturating_add(Weight::from_parts(123_052, 0).saturating_mul(r.into())) + // Standard Error: 1_817 + .saturating_add(Weight::from_parts(1_256_713, 0).saturating_mul(s.into())) + // Standard Error: 1_817 + .saturating_add(Weight::from_parts(219_242, 0).saturating_mul(x.into())) .saturating_add(RocksDbWeight::get().reads(3_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) .saturating_add(RocksDbWeight::get().writes((1_u64).saturating_mul(s.into()))) @@ -590,11 +583,11 @@ impl WeightInfo for () { fn add_sub(s: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `475 + s * (36 ±0)` - // Estimated: `21305` - // Minimum execution time: 28_661_000 picoseconds. - Weight::from_parts(33_432_641, 21305) - // Standard Error: 1_547 - .saturating_add(Weight::from_parts(71_314, 0).saturating_mul(s.into())) + // Estimated: `11003` + // Minimum execution time: 30_747_000 picoseconds. + Weight::from_parts(35_975_985, 11003) + // Standard Error: 1_625 + .saturating_add(Weight::from_parts(73_263, 0).saturating_mul(s.into())) .saturating_add(RocksDbWeight::get().reads(3_u64)) .saturating_add(RocksDbWeight::get().writes(2_u64)) } @@ -606,11 +599,11 @@ impl WeightInfo for () { fn rename_sub(s: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `591 + s * (3 ±0)` - // Estimated: `14582` - // Minimum execution time: 13_885_000 picoseconds. - Weight::from_parts(16_105_165, 14582) - // Standard Error: 562 - .saturating_add(Weight::from_parts(17_087, 0).saturating_mul(s.into())) + // Estimated: `11003` + // Minimum execution time: 13_586_000 picoseconds. + Weight::from_parts(15_909_245, 11003) + // Standard Error: 611 + .saturating_add(Weight::from_parts(16_949, 0).saturating_mul(s.into())) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } @@ -624,11 +617,11 @@ impl WeightInfo for () { fn remove_sub(s: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `638 + s * (35 ±0)` - // Estimated: `21305` - // Minimum execution time: 32_192_000 picoseconds. - Weight::from_parts(35_393_444, 21305) - // Standard Error: 2_198 - .saturating_add(Weight::from_parts(62_316, 0).saturating_mul(s.into())) + // Estimated: `11003` + // Minimum execution time: 34_286_000 picoseconds. + Weight::from_parts(37_391_401, 11003) + // Standard Error: 1_099 + .saturating_add(Weight::from_parts(61_165, 0).saturating_mul(s.into())) .saturating_add(RocksDbWeight::get().reads(3_u64)) .saturating_add(RocksDbWeight::get().writes(2_u64)) } @@ -640,11 +633,11 @@ impl WeightInfo for () { fn quit_sub(s: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `564 + s * (37 ±0)` - // Estimated: `10302` - // Minimum execution time: 20_803_000 picoseconds. - Weight::from_parts(23_462_393, 10302) - // Standard Error: 1_619 - .saturating_add(Weight::from_parts(59_938, 0).saturating_mul(s.into())) + // Estimated: `6723` + // Minimum execution time: 22_197_000 picoseconds. + Weight::from_parts(24_630_311, 6723) + // Standard Error: 1_092 + .saturating_add(Weight::from_parts(63_415, 0).saturating_mul(s.into())) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().writes(2_u64)) } diff --git a/frame/im-online/src/weights.rs b/frame/im-online/src/weights.rs index f97df1c36..64c1eb5f3 100644 --- a/frame/im-online/src/weights.rs +++ b/frame/im-online/src/weights.rs @@ -18,9 +18,9 @@ //! Autogenerated weights for pallet_im_online //! //! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev -//! DATE: 2023-03-16, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! DATE: 2023-04-06, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` //! WORST CASE MAP SIZE: `1000000` -//! HOSTNAME: `bm3`, CPU: `Intel(R) Core(TM) i7-7700K CPU @ 4.20GHz` +//! HOSTNAME: `bm2`, CPU: `Intel(R) Core(TM) i7-7700K CPU @ 4.20GHz` //! EXECUTION: Some(Wasm), WASM-EXECUTION: Compiled, CHAIN: Some("dev"), DB CACHE: 1024 // Executed Command: @@ -31,9 +31,6 @@ // --steps=50 // --repeat=20 // --pallet=pallet_im_online -// --no-storage-info -// --no-median-slopes -// --no-min-squares // --extrinsic=* // --execution=wasm // --wasm-execution=compiled @@ -72,17 +69,17 @@ impl WeightInfo for SubstrateWeight { fn validate_unsigned_and_then_heartbeat(k: u32, e: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `295 + k * (32 ±0)` - // Estimated: `10349544 + e * (35 ±0) + k * (64 ±0)` - // Minimum execution time: 106_224_000 picoseconds. - Weight::from_parts(79_172_319, 10349544) - // Standard Error: 259 - .saturating_add(Weight::from_parts(29_576, 0).saturating_mul(k.into())) - // Standard Error: 2_611 - .saturating_add(Weight::from_parts(422_369, 0).saturating_mul(e.into())) + // Estimated: `10024497 + e * (35 ±0) + k * (32 ±0)` + // Minimum execution time: 95_573_000 picoseconds. + Weight::from_parts(78_856_572, 10024497) + // Standard Error: 315 + .saturating_add(Weight::from_parts(22_926, 0).saturating_mul(k.into())) + // Standard Error: 3_181 + .saturating_add(Weight::from_parts(362_093, 0).saturating_mul(e.into())) .saturating_add(T::DbWeight::get().reads(4_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) .saturating_add(Weight::from_parts(0, 35).saturating_mul(e.into())) - .saturating_add(Weight::from_parts(0, 64).saturating_mul(k.into())) + .saturating_add(Weight::from_parts(0, 32).saturating_mul(k.into())) } } @@ -103,16 +100,16 @@ impl WeightInfo for () { fn validate_unsigned_and_then_heartbeat(k: u32, e: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `295 + k * (32 ±0)` - // Estimated: `10349544 + e * (35 ±0) + k * (64 ±0)` - // Minimum execution time: 106_224_000 picoseconds. - Weight::from_parts(79_172_319, 10349544) - // Standard Error: 259 - .saturating_add(Weight::from_parts(29_576, 0).saturating_mul(k.into())) - // Standard Error: 2_611 - .saturating_add(Weight::from_parts(422_369, 0).saturating_mul(e.into())) + // Estimated: `10024497 + e * (35 ±0) + k * (32 ±0)` + // Minimum execution time: 95_573_000 picoseconds. + Weight::from_parts(78_856_572, 10024497) + // Standard Error: 315 + .saturating_add(Weight::from_parts(22_926, 0).saturating_mul(k.into())) + // Standard Error: 3_181 + .saturating_add(Weight::from_parts(362_093, 0).saturating_mul(e.into())) .saturating_add(RocksDbWeight::get().reads(4_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) .saturating_add(Weight::from_parts(0, 35).saturating_mul(e.into())) - .saturating_add(Weight::from_parts(0, 64).saturating_mul(k.into())) + .saturating_add(Weight::from_parts(0, 32).saturating_mul(k.into())) } } diff --git a/frame/indices/src/weights.rs b/frame/indices/src/weights.rs index 87160ccfb..21d01c14e 100644 --- a/frame/indices/src/weights.rs +++ b/frame/indices/src/weights.rs @@ -18,9 +18,9 @@ //! Autogenerated weights for pallet_indices //! //! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev -//! DATE: 2023-03-16, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! DATE: 2023-04-06, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` //! WORST CASE MAP SIZE: `1000000` -//! HOSTNAME: `bm3`, CPU: `Intel(R) Core(TM) i7-7700K CPU @ 4.20GHz` +//! HOSTNAME: `bm2`, CPU: `Intel(R) Core(TM) i7-7700K CPU @ 4.20GHz` //! EXECUTION: Some(Wasm), WASM-EXECUTION: Compiled, CHAIN: Some("dev"), DB CACHE: 1024 // Executed Command: @@ -31,9 +31,6 @@ // --steps=50 // --repeat=20 // --pallet=pallet_indices -// --no-storage-info -// --no-median-slopes -// --no-min-squares // --extrinsic=* // --execution=wasm // --wasm-execution=compiled @@ -67,8 +64,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `76` // Estimated: `3534` - // Minimum execution time: 22_365_000 picoseconds. - Weight::from_parts(22_713_000, 3534) + // Minimum execution time: 27_250_000 picoseconds. + Weight::from_parts(27_829_000, 3534) .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -79,9 +76,9 @@ impl WeightInfo for SubstrateWeight { fn transfer() -> Weight { // Proof Size summary in bytes: // Measured: `275` - // Estimated: `7127` - // Minimum execution time: 28_380_000 picoseconds. - Weight::from_parts(29_251_000, 7127) + // Estimated: `3593` + // Minimum execution time: 37_880_000 picoseconds. + Weight::from_parts(38_329_000, 3593) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().writes(2_u64)) } @@ -91,8 +88,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `172` // Estimated: `3534` - // Minimum execution time: 22_932_000 picoseconds. - Weight::from_parts(23_442_000, 3534) + // Minimum execution time: 27_455_000 picoseconds. + Weight::from_parts(27_788_000, 3534) .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -103,9 +100,9 @@ impl WeightInfo for SubstrateWeight { fn force_transfer() -> Weight { // Proof Size summary in bytes: // Measured: `275` - // Estimated: `7127` - // Minimum execution time: 25_223_000 picoseconds. - Weight::from_parts(25_519_000, 7127) + // Estimated: `3593` + // Minimum execution time: 29_865_000 picoseconds. + Weight::from_parts(30_420_000, 3593) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().writes(2_u64)) } @@ -115,8 +112,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `172` // Estimated: `3534` - // Minimum execution time: 25_223_000 picoseconds. - Weight::from_parts(26_127_000, 3534) + // Minimum execution time: 29_689_000 picoseconds. + Weight::from_parts(30_443_000, 3534) .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -130,8 +127,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `76` // Estimated: `3534` - // Minimum execution time: 22_365_000 picoseconds. - Weight::from_parts(22_713_000, 3534) + // Minimum execution time: 27_250_000 picoseconds. + Weight::from_parts(27_829_000, 3534) .saturating_add(RocksDbWeight::get().reads(1_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } @@ -142,9 +139,9 @@ impl WeightInfo for () { fn transfer() -> Weight { // Proof Size summary in bytes: // Measured: `275` - // Estimated: `7127` - // Minimum execution time: 28_380_000 picoseconds. - Weight::from_parts(29_251_000, 7127) + // Estimated: `3593` + // Minimum execution time: 37_880_000 picoseconds. + Weight::from_parts(38_329_000, 3593) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().writes(2_u64)) } @@ -154,8 +151,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `172` // Estimated: `3534` - // Minimum execution time: 22_932_000 picoseconds. - Weight::from_parts(23_442_000, 3534) + // Minimum execution time: 27_455_000 picoseconds. + Weight::from_parts(27_788_000, 3534) .saturating_add(RocksDbWeight::get().reads(1_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } @@ -166,9 +163,9 @@ impl WeightInfo for () { fn force_transfer() -> Weight { // Proof Size summary in bytes: // Measured: `275` - // Estimated: `7127` - // Minimum execution time: 25_223_000 picoseconds. - Weight::from_parts(25_519_000, 7127) + // Estimated: `3593` + // Minimum execution time: 29_865_000 picoseconds. + Weight::from_parts(30_420_000, 3593) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().writes(2_u64)) } @@ -178,8 +175,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `172` // Estimated: `3534` - // Minimum execution time: 25_223_000 picoseconds. - Weight::from_parts(26_127_000, 3534) + // Minimum execution time: 29_689_000 picoseconds. + Weight::from_parts(30_443_000, 3534) .saturating_add(RocksDbWeight::get().reads(1_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } diff --git a/frame/lottery/src/weights.rs b/frame/lottery/src/weights.rs index c8c373ad6..c21b09e7d 100644 --- a/frame/lottery/src/weights.rs +++ b/frame/lottery/src/weights.rs @@ -18,9 +18,9 @@ //! Autogenerated weights for pallet_lottery //! //! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev -//! DATE: 2023-03-16, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! DATE: 2023-04-06, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` //! WORST CASE MAP SIZE: `1000000` -//! HOSTNAME: `bm3`, CPU: `Intel(R) Core(TM) i7-7700K CPU @ 4.20GHz` +//! HOSTNAME: `bm2`, CPU: `Intel(R) Core(TM) i7-7700K CPU @ 4.20GHz` //! EXECUTION: Some(Wasm), WASM-EXECUTION: Compiled, CHAIN: Some("dev"), DB CACHE: 1024 // Executed Command: @@ -31,9 +31,6 @@ // --steps=50 // --repeat=20 // --pallet=pallet_lottery -// --no-storage-info -// --no-median-slopes -// --no-min-squares // --extrinsic=* // --execution=wasm // --wasm-execution=compiled @@ -79,9 +76,9 @@ impl WeightInfo for SubstrateWeight { fn buy_ticket() -> Weight { // Proof Size summary in bytes: // Measured: `452` - // Estimated: `13121` - // Minimum execution time: 43_076_000 picoseconds. - Weight::from_parts(43_902_000, 13121) + // Estimated: `3593` + // Minimum execution time: 61_502_000 picoseconds. + Weight::from_parts(62_578_000, 3593) .saturating_add(T::DbWeight::get().reads(6_u64)) .saturating_add(T::DbWeight::get().writes(4_u64)) } @@ -92,10 +89,10 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 8_222_000 picoseconds. - Weight::from_parts(9_166_238, 0) - // Standard Error: 3_489 - .saturating_add(Weight::from_parts(328_564, 0).saturating_mul(n.into())) + // Minimum execution time: 8_282_000 picoseconds. + Weight::from_parts(9_271_031, 0) + // Standard Error: 3_756 + .saturating_add(Weight::from_parts(349_990, 0).saturating_mul(n.into())) .saturating_add(T::DbWeight::get().writes(1_u64)) } /// Storage: Lottery Lottery (r:1 w:1) @@ -107,9 +104,9 @@ impl WeightInfo for SubstrateWeight { fn start_lottery() -> Weight { // Proof Size summary in bytes: // Measured: `161` - // Estimated: `6596` - // Minimum execution time: 36_555_000 picoseconds. - Weight::from_parts(37_008_000, 6596) + // Estimated: `3593` + // Minimum execution time: 38_975_000 picoseconds. + Weight::from_parts(39_552_000, 3593) .saturating_add(T::DbWeight::get().reads(3_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) } @@ -119,8 +116,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `219` // Estimated: `1514` - // Minimum execution time: 8_230_000 picoseconds. - Weight::from_parts(8_355_000, 1514) + // Minimum execution time: 8_243_000 picoseconds. + Weight::from_parts(8_359_000, 1514) .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -137,9 +134,9 @@ impl WeightInfo for SubstrateWeight { fn on_initialize_end() -> Weight { // Proof Size summary in bytes: // Measured: `524` - // Estimated: `16787` - // Minimum execution time: 60_097_000 picoseconds. - Weight::from_parts(62_055_000, 16787) + // Estimated: `6196` + // Minimum execution time: 76_062_000 picoseconds. + Weight::from_parts(76_547_000, 6196) .saturating_add(T::DbWeight::get().reads(6_u64)) .saturating_add(T::DbWeight::get().writes(4_u64)) } @@ -158,9 +155,9 @@ impl WeightInfo for SubstrateWeight { fn on_initialize_repeat() -> Weight { // Proof Size summary in bytes: // Measured: `524` - // Estimated: `18276` - // Minimum execution time: 61_354_000 picoseconds. - Weight::from_parts(62_429_000, 18276) + // Estimated: `6196` + // Minimum execution time: 78_089_000 picoseconds. + Weight::from_parts(78_632_000, 6196) .saturating_add(T::DbWeight::get().reads(7_u64)) .saturating_add(T::DbWeight::get().writes(5_u64)) } @@ -185,9 +182,9 @@ impl WeightInfo for () { fn buy_ticket() -> Weight { // Proof Size summary in bytes: // Measured: `452` - // Estimated: `13121` - // Minimum execution time: 43_076_000 picoseconds. - Weight::from_parts(43_902_000, 13121) + // Estimated: `3593` + // Minimum execution time: 61_502_000 picoseconds. + Weight::from_parts(62_578_000, 3593) .saturating_add(RocksDbWeight::get().reads(6_u64)) .saturating_add(RocksDbWeight::get().writes(4_u64)) } @@ -198,10 +195,10 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 8_222_000 picoseconds. - Weight::from_parts(9_166_238, 0) - // Standard Error: 3_489 - .saturating_add(Weight::from_parts(328_564, 0).saturating_mul(n.into())) + // Minimum execution time: 8_282_000 picoseconds. + Weight::from_parts(9_271_031, 0) + // Standard Error: 3_756 + .saturating_add(Weight::from_parts(349_990, 0).saturating_mul(n.into())) .saturating_add(RocksDbWeight::get().writes(1_u64)) } /// Storage: Lottery Lottery (r:1 w:1) @@ -213,9 +210,9 @@ impl WeightInfo for () { fn start_lottery() -> Weight { // Proof Size summary in bytes: // Measured: `161` - // Estimated: `6596` - // Minimum execution time: 36_555_000 picoseconds. - Weight::from_parts(37_008_000, 6596) + // Estimated: `3593` + // Minimum execution time: 38_975_000 picoseconds. + Weight::from_parts(39_552_000, 3593) .saturating_add(RocksDbWeight::get().reads(3_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) } @@ -225,8 +222,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `219` // Estimated: `1514` - // Minimum execution time: 8_230_000 picoseconds. - Weight::from_parts(8_355_000, 1514) + // Minimum execution time: 8_243_000 picoseconds. + Weight::from_parts(8_359_000, 1514) .saturating_add(RocksDbWeight::get().reads(1_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } @@ -243,9 +240,9 @@ impl WeightInfo for () { fn on_initialize_end() -> Weight { // Proof Size summary in bytes: // Measured: `524` - // Estimated: `16787` - // Minimum execution time: 60_097_000 picoseconds. - Weight::from_parts(62_055_000, 16787) + // Estimated: `6196` + // Minimum execution time: 76_062_000 picoseconds. + Weight::from_parts(76_547_000, 6196) .saturating_add(RocksDbWeight::get().reads(6_u64)) .saturating_add(RocksDbWeight::get().writes(4_u64)) } @@ -264,9 +261,9 @@ impl WeightInfo for () { fn on_initialize_repeat() -> Weight { // Proof Size summary in bytes: // Measured: `524` - // Estimated: `18276` - // Minimum execution time: 61_354_000 picoseconds. - Weight::from_parts(62_429_000, 18276) + // Estimated: `6196` + // Minimum execution time: 78_089_000 picoseconds. + Weight::from_parts(78_632_000, 6196) .saturating_add(RocksDbWeight::get().reads(7_u64)) .saturating_add(RocksDbWeight::get().writes(5_u64)) } diff --git a/frame/membership/src/weights.rs b/frame/membership/src/weights.rs index 31c359b44..70c50d869 100644 --- a/frame/membership/src/weights.rs +++ b/frame/membership/src/weights.rs @@ -18,9 +18,9 @@ //! Autogenerated weights for pallet_membership //! //! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev -//! DATE: 2023-03-16, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! DATE: 2023-04-06, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` //! WORST CASE MAP SIZE: `1000000` -//! HOSTNAME: `bm3`, CPU: `Intel(R) Core(TM) i7-7700K CPU @ 4.20GHz` +//! HOSTNAME: `bm2`, CPU: `Intel(R) Core(TM) i7-7700K CPU @ 4.20GHz` //! EXECUTION: Some(Wasm), WASM-EXECUTION: Compiled, CHAIN: Some("dev"), DB CACHE: 1024 // Executed Command: @@ -31,9 +31,6 @@ // --steps=50 // --repeat=20 // --pallet=pallet_membership -// --no-storage-info -// --no-median-slopes -// --no-min-squares // --extrinsic=* // --execution=wasm // --wasm-execution=compiled @@ -75,14 +72,14 @@ impl WeightInfo for SubstrateWeight { fn add_member(m: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `174 + m * (64 ±0)` - // Estimated: `6691 + m * (192 ±0)` - // Minimum execution time: 17_587_000 picoseconds. - Weight::from_parts(18_658_163, 6691) - // Standard Error: 710 - .saturating_add(Weight::from_parts(46_294, 0).saturating_mul(m.into())) + // Estimated: `4687 + m * (64 ±0)` + // Minimum execution time: 18_264_000 picoseconds. + Weight::from_parts(19_343_697, 4687) + // Standard Error: 699 + .saturating_add(Weight::from_parts(44_401, 0).saturating_mul(m.into())) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) - .saturating_add(Weight::from_parts(0, 192).saturating_mul(m.into())) + .saturating_add(Weight::from_parts(0, 64).saturating_mul(m.into())) } /// Storage: TechnicalMembership Members (r:1 w:1) /// Proof: TechnicalMembership Members (max_values: Some(1), max_size: Some(3202), added: 3697, mode: MaxEncodedLen) @@ -98,14 +95,14 @@ impl WeightInfo for SubstrateWeight { fn remove_member(m: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `278 + m * (64 ±0)` - // Estimated: `8520 + m * (192 ±0)` - // Minimum execution time: 20_402_000 picoseconds. - Weight::from_parts(21_165_819, 8520) - // Standard Error: 643 - .saturating_add(Weight::from_parts(45_481, 0).saturating_mul(m.into())) + // Estimated: `4687 + m * (64 ±0)` + // Minimum execution time: 21_092_000 picoseconds. + Weight::from_parts(22_140_391, 4687) + // Standard Error: 545 + .saturating_add(Weight::from_parts(40_638, 0).saturating_mul(m.into())) .saturating_add(T::DbWeight::get().reads(3_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) - .saturating_add(Weight::from_parts(0, 192).saturating_mul(m.into())) + .saturating_add(Weight::from_parts(0, 64).saturating_mul(m.into())) } /// Storage: TechnicalMembership Members (r:1 w:1) /// Proof: TechnicalMembership Members (max_values: Some(1), max_size: Some(3202), added: 3697, mode: MaxEncodedLen) @@ -121,14 +118,14 @@ impl WeightInfo for SubstrateWeight { fn swap_member(m: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `278 + m * (64 ±0)` - // Estimated: `8520 + m * (192 ±0)` - // Minimum execution time: 20_380_000 picoseconds. - Weight::from_parts(21_633_260, 8520) - // Standard Error: 770 - .saturating_add(Weight::from_parts(55_504, 0).saturating_mul(m.into())) + // Estimated: `4687 + m * (64 ±0)` + // Minimum execution time: 21_002_000 picoseconds. + Weight::from_parts(22_364_944, 4687) + // Standard Error: 752 + .saturating_add(Weight::from_parts(54_363, 0).saturating_mul(m.into())) .saturating_add(T::DbWeight::get().reads(3_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) - .saturating_add(Weight::from_parts(0, 192).saturating_mul(m.into())) + .saturating_add(Weight::from_parts(0, 64).saturating_mul(m.into())) } /// Storage: TechnicalMembership Members (r:1 w:1) /// Proof: TechnicalMembership Members (max_values: Some(1), max_size: Some(3202), added: 3697, mode: MaxEncodedLen) @@ -144,14 +141,14 @@ impl WeightInfo for SubstrateWeight { fn reset_member(m: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `278 + m * (64 ±0)` - // Estimated: `8520 + m * (192 ±0)` - // Minimum execution time: 19_989_000 picoseconds. - Weight::from_parts(22_352_059, 8520) - // Standard Error: 2_878 - .saturating_add(Weight::from_parts(156_367, 0).saturating_mul(m.into())) + // Estimated: `4687 + m * (64 ±0)` + // Minimum execution time: 20_443_000 picoseconds. + Weight::from_parts(22_188_301, 4687) + // Standard Error: 945 + .saturating_add(Weight::from_parts(162_799, 0).saturating_mul(m.into())) .saturating_add(T::DbWeight::get().reads(3_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) - .saturating_add(Weight::from_parts(0, 192).saturating_mul(m.into())) + .saturating_add(Weight::from_parts(0, 64).saturating_mul(m.into())) } /// Storage: TechnicalMembership Members (r:1 w:1) /// Proof: TechnicalMembership Members (max_values: Some(1), max_size: Some(3202), added: 3697, mode: MaxEncodedLen) @@ -167,14 +164,14 @@ impl WeightInfo for SubstrateWeight { fn change_key(m: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `278 + m * (64 ±0)` - // Estimated: `8520 + m * (192 ±0)` - // Minimum execution time: 21_275_000 picoseconds. - Weight::from_parts(23_344_594, 8520) - // Standard Error: 2_750 - .saturating_add(Weight::from_parts(46_736, 0).saturating_mul(m.into())) + // Estimated: `4687 + m * (64 ±0)` + // Minimum execution time: 21_527_000 picoseconds. + Weight::from_parts(23_146_706, 4687) + // Standard Error: 724 + .saturating_add(Weight::from_parts(55_027, 0).saturating_mul(m.into())) .saturating_add(T::DbWeight::get().reads(3_u64)) .saturating_add(T::DbWeight::get().writes(4_u64)) - .saturating_add(Weight::from_parts(0, 192).saturating_mul(m.into())) + .saturating_add(Weight::from_parts(0, 64).saturating_mul(m.into())) } /// Storage: TechnicalMembership Members (r:1 w:0) /// Proof: TechnicalMembership Members (max_values: Some(1), max_size: Some(3202), added: 3697, mode: MaxEncodedLen) @@ -186,11 +183,11 @@ impl WeightInfo for SubstrateWeight { fn set_prime(m: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `32 + m * (32 ±0)` - // Estimated: `4719 + m * (32 ±0)` - // Minimum execution time: 8_087_000 picoseconds. - Weight::from_parts(8_909_627, 4719) - // Standard Error: 1_572 - .saturating_add(Weight::from_parts(17_186, 0).saturating_mul(m.into())) + // Estimated: `4687 + m * (32 ±0)` + // Minimum execution time: 8_054_000 picoseconds. + Weight::from_parts(8_558_341, 4687) + // Standard Error: 360 + .saturating_add(Weight::from_parts(16_362, 0).saturating_mul(m.into())) .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(T::DbWeight::get().writes(2_u64)) .saturating_add(Weight::from_parts(0, 32).saturating_mul(m.into())) @@ -200,14 +197,12 @@ impl WeightInfo for SubstrateWeight { /// Storage: TechnicalCommittee Prime (r:0 w:1) /// Proof Skipped: TechnicalCommittee Prime (max_values: Some(1), max_size: None, mode: Measured) /// The range of component `m` is `[1, 100]`. - fn clear_prime(m: u32, ) -> Weight { + fn clear_prime(_m: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 3_752_000 picoseconds. - Weight::from_parts(4_081_144, 0) - // Standard Error: 229 - .saturating_add(Weight::from_parts(1_298, 0).saturating_mul(m.into())) + // Minimum execution time: 3_784_000 picoseconds. + Weight::from_parts(4_100_031, 0) .saturating_add(T::DbWeight::get().writes(2_u64)) } } @@ -226,14 +221,14 @@ impl WeightInfo for () { fn add_member(m: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `174 + m * (64 ±0)` - // Estimated: `6691 + m * (192 ±0)` - // Minimum execution time: 17_587_000 picoseconds. - Weight::from_parts(18_658_163, 6691) - // Standard Error: 710 - .saturating_add(Weight::from_parts(46_294, 0).saturating_mul(m.into())) + // Estimated: `4687 + m * (64 ±0)` + // Minimum execution time: 18_264_000 picoseconds. + Weight::from_parts(19_343_697, 4687) + // Standard Error: 699 + .saturating_add(Weight::from_parts(44_401, 0).saturating_mul(m.into())) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) - .saturating_add(Weight::from_parts(0, 192).saturating_mul(m.into())) + .saturating_add(Weight::from_parts(0, 64).saturating_mul(m.into())) } /// Storage: TechnicalMembership Members (r:1 w:1) /// Proof: TechnicalMembership Members (max_values: Some(1), max_size: Some(3202), added: 3697, mode: MaxEncodedLen) @@ -249,14 +244,14 @@ impl WeightInfo for () { fn remove_member(m: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `278 + m * (64 ±0)` - // Estimated: `8520 + m * (192 ±0)` - // Minimum execution time: 20_402_000 picoseconds. - Weight::from_parts(21_165_819, 8520) - // Standard Error: 643 - .saturating_add(Weight::from_parts(45_481, 0).saturating_mul(m.into())) + // Estimated: `4687 + m * (64 ±0)` + // Minimum execution time: 21_092_000 picoseconds. + Weight::from_parts(22_140_391, 4687) + // Standard Error: 545 + .saturating_add(Weight::from_parts(40_638, 0).saturating_mul(m.into())) .saturating_add(RocksDbWeight::get().reads(3_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) - .saturating_add(Weight::from_parts(0, 192).saturating_mul(m.into())) + .saturating_add(Weight::from_parts(0, 64).saturating_mul(m.into())) } /// Storage: TechnicalMembership Members (r:1 w:1) /// Proof: TechnicalMembership Members (max_values: Some(1), max_size: Some(3202), added: 3697, mode: MaxEncodedLen) @@ -272,14 +267,14 @@ impl WeightInfo for () { fn swap_member(m: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `278 + m * (64 ±0)` - // Estimated: `8520 + m * (192 ±0)` - // Minimum execution time: 20_380_000 picoseconds. - Weight::from_parts(21_633_260, 8520) - // Standard Error: 770 - .saturating_add(Weight::from_parts(55_504, 0).saturating_mul(m.into())) + // Estimated: `4687 + m * (64 ±0)` + // Minimum execution time: 21_002_000 picoseconds. + Weight::from_parts(22_364_944, 4687) + // Standard Error: 752 + .saturating_add(Weight::from_parts(54_363, 0).saturating_mul(m.into())) .saturating_add(RocksDbWeight::get().reads(3_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) - .saturating_add(Weight::from_parts(0, 192).saturating_mul(m.into())) + .saturating_add(Weight::from_parts(0, 64).saturating_mul(m.into())) } /// Storage: TechnicalMembership Members (r:1 w:1) /// Proof: TechnicalMembership Members (max_values: Some(1), max_size: Some(3202), added: 3697, mode: MaxEncodedLen) @@ -295,14 +290,14 @@ impl WeightInfo for () { fn reset_member(m: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `278 + m * (64 ±0)` - // Estimated: `8520 + m * (192 ±0)` - // Minimum execution time: 19_989_000 picoseconds. - Weight::from_parts(22_352_059, 8520) - // Standard Error: 2_878 - .saturating_add(Weight::from_parts(156_367, 0).saturating_mul(m.into())) + // Estimated: `4687 + m * (64 ±0)` + // Minimum execution time: 20_443_000 picoseconds. + Weight::from_parts(22_188_301, 4687) + // Standard Error: 945 + .saturating_add(Weight::from_parts(162_799, 0).saturating_mul(m.into())) .saturating_add(RocksDbWeight::get().reads(3_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) - .saturating_add(Weight::from_parts(0, 192).saturating_mul(m.into())) + .saturating_add(Weight::from_parts(0, 64).saturating_mul(m.into())) } /// Storage: TechnicalMembership Members (r:1 w:1) /// Proof: TechnicalMembership Members (max_values: Some(1), max_size: Some(3202), added: 3697, mode: MaxEncodedLen) @@ -318,14 +313,14 @@ impl WeightInfo for () { fn change_key(m: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `278 + m * (64 ±0)` - // Estimated: `8520 + m * (192 ±0)` - // Minimum execution time: 21_275_000 picoseconds. - Weight::from_parts(23_344_594, 8520) - // Standard Error: 2_750 - .saturating_add(Weight::from_parts(46_736, 0).saturating_mul(m.into())) + // Estimated: `4687 + m * (64 ±0)` + // Minimum execution time: 21_527_000 picoseconds. + Weight::from_parts(23_146_706, 4687) + // Standard Error: 724 + .saturating_add(Weight::from_parts(55_027, 0).saturating_mul(m.into())) .saturating_add(RocksDbWeight::get().reads(3_u64)) .saturating_add(RocksDbWeight::get().writes(4_u64)) - .saturating_add(Weight::from_parts(0, 192).saturating_mul(m.into())) + .saturating_add(Weight::from_parts(0, 64).saturating_mul(m.into())) } /// Storage: TechnicalMembership Members (r:1 w:0) /// Proof: TechnicalMembership Members (max_values: Some(1), max_size: Some(3202), added: 3697, mode: MaxEncodedLen) @@ -337,11 +332,11 @@ impl WeightInfo for () { fn set_prime(m: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `32 + m * (32 ±0)` - // Estimated: `4719 + m * (32 ±0)` - // Minimum execution time: 8_087_000 picoseconds. - Weight::from_parts(8_909_627, 4719) - // Standard Error: 1_572 - .saturating_add(Weight::from_parts(17_186, 0).saturating_mul(m.into())) + // Estimated: `4687 + m * (32 ±0)` + // Minimum execution time: 8_054_000 picoseconds. + Weight::from_parts(8_558_341, 4687) + // Standard Error: 360 + .saturating_add(Weight::from_parts(16_362, 0).saturating_mul(m.into())) .saturating_add(RocksDbWeight::get().reads(1_u64)) .saturating_add(RocksDbWeight::get().writes(2_u64)) .saturating_add(Weight::from_parts(0, 32).saturating_mul(m.into())) @@ -351,14 +346,12 @@ impl WeightInfo for () { /// Storage: TechnicalCommittee Prime (r:0 w:1) /// Proof Skipped: TechnicalCommittee Prime (max_values: Some(1), max_size: None, mode: Measured) /// The range of component `m` is `[1, 100]`. - fn clear_prime(m: u32, ) -> Weight { + fn clear_prime(_m: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 3_752_000 picoseconds. - Weight::from_parts(4_081_144, 0) - // Standard Error: 229 - .saturating_add(Weight::from_parts(1_298, 0).saturating_mul(m.into())) + // Minimum execution time: 3_784_000 picoseconds. + Weight::from_parts(4_100_031, 0) .saturating_add(RocksDbWeight::get().writes(2_u64)) } } diff --git a/frame/message-queue/src/weights.rs b/frame/message-queue/src/weights.rs index fc44456db..9dae12f51 100644 --- a/frame/message-queue/src/weights.rs +++ b/frame/message-queue/src/weights.rs @@ -18,9 +18,9 @@ //! Autogenerated weights for pallet_message_queue //! //! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev -//! DATE: 2023-03-16, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! DATE: 2023-04-06, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` //! WORST CASE MAP SIZE: `1000000` -//! HOSTNAME: `bm3`, CPU: `Intel(R) Core(TM) i7-7700K CPU @ 4.20GHz` +//! HOSTNAME: `bm2`, CPU: `Intel(R) Core(TM) i7-7700K CPU @ 4.20GHz` //! EXECUTION: Some(Wasm), WASM-EXECUTION: Compiled, CHAIN: Some("dev"), DB CACHE: 1024 // Executed Command: @@ -31,9 +31,6 @@ // --steps=50 // --repeat=20 // --pallet=pallet_message_queue -// --no-storage-info -// --no-median-slopes -// --no-min-squares // --extrinsic=* // --execution=wasm // --wasm-execution=compiled @@ -73,9 +70,9 @@ impl WeightInfo for SubstrateWeight { fn ready_ring_knit() -> Weight { // Proof Size summary in bytes: // Measured: `233` - // Estimated: `7527` - // Minimum execution time: 12_561_000 picoseconds. - Weight::from_parts(12_758_000, 7527) + // Estimated: `6038` + // Minimum execution time: 12_076_000 picoseconds. + Weight::from_parts(12_350_000, 6038) .saturating_add(T::DbWeight::get().reads(3_u64)) .saturating_add(T::DbWeight::get().writes(2_u64)) } @@ -86,9 +83,9 @@ impl WeightInfo for SubstrateWeight { fn ready_ring_unknit() -> Weight { // Proof Size summary in bytes: // Measured: `233` - // Estimated: `7527` - // Minimum execution time: 11_854_000 picoseconds. - Weight::from_parts(12_178_000, 7527) + // Estimated: `6038` + // Minimum execution time: 11_586_000 picoseconds. + Weight::from_parts(11_912_000, 6038) .saturating_add(T::DbWeight::get().reads(3_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) } @@ -98,8 +95,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `42` // Estimated: `3514` - // Minimum execution time: 7_900_000 picoseconds. - Weight::from_parts(8_046_000, 3514) + // Minimum execution time: 4_581_000 picoseconds. + Weight::from_parts(4_715_000, 3514) .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -109,8 +106,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `113` // Estimated: `69049` - // Minimum execution time: 6_284_000 picoseconds. - Weight::from_parts(6_433_000, 69049) + // Minimum execution time: 5_826_000 picoseconds. + Weight::from_parts(5_932_000, 69049) .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -120,8 +117,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `113` // Estimated: `69049` - // Minimum execution time: 6_418_000 picoseconds. - Weight::from_parts(6_633_000, 69049) + // Minimum execution time: 6_235_000 picoseconds. + Weight::from_parts(6_430_000, 69049) .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -129,8 +126,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 52_661_000 picoseconds. - Weight::from_parts(52_994_000, 0) + // Minimum execution time: 53_860_000 picoseconds. + Weight::from_parts(53_984_000, 0) } /// Storage: MessageQueue ServiceHead (r:1 w:1) /// Proof: MessageQueue ServiceHead (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) @@ -139,9 +136,9 @@ impl WeightInfo for SubstrateWeight { fn bump_service_head() -> Weight { // Proof Size summary in bytes: // Measured: `140` - // Estimated: `5003` - // Minimum execution time: 7_132_000 picoseconds. - Weight::from_parts(7_386_000, 5003) + // Estimated: `3514` + // Minimum execution time: 7_018_000 picoseconds. + Weight::from_parts(7_205_000, 3514) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -152,9 +149,9 @@ impl WeightInfo for SubstrateWeight { fn reap_page() -> Weight { // Proof Size summary in bytes: // Measured: `65710` - // Estimated: `72563` - // Minimum execution time: 54_377_000 picoseconds. - Weight::from_parts(54_804_000, 72563) + // Estimated: `69049` + // Minimum execution time: 53_485_000 picoseconds. + Weight::from_parts(54_154_000, 69049) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().writes(2_u64)) } @@ -165,9 +162,9 @@ impl WeightInfo for SubstrateWeight { fn execute_overweight_page_removed() -> Weight { // Proof Size summary in bytes: // Measured: `65710` - // Estimated: `72563` - // Minimum execution time: 69_461_000 picoseconds. - Weight::from_parts(70_016_000, 72563) + // Estimated: `69049` + // Minimum execution time: 68_830_000 picoseconds. + Weight::from_parts(69_487_000, 69049) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().writes(2_u64)) } @@ -178,9 +175,9 @@ impl WeightInfo for SubstrateWeight { fn execute_overweight_page_updated() -> Weight { // Proof Size summary in bytes: // Measured: `65710` - // Estimated: `72563` - // Minimum execution time: 81_787_000 picoseconds. - Weight::from_parts(83_100_000, 72563) + // Estimated: `69049` + // Minimum execution time: 81_643_000 picoseconds. + Weight::from_parts(82_399_000, 69049) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().writes(2_u64)) } @@ -195,9 +192,9 @@ impl WeightInfo for () { fn ready_ring_knit() -> Weight { // Proof Size summary in bytes: // Measured: `233` - // Estimated: `7527` - // Minimum execution time: 12_561_000 picoseconds. - Weight::from_parts(12_758_000, 7527) + // Estimated: `6038` + // Minimum execution time: 12_076_000 picoseconds. + Weight::from_parts(12_350_000, 6038) .saturating_add(RocksDbWeight::get().reads(3_u64)) .saturating_add(RocksDbWeight::get().writes(2_u64)) } @@ -208,9 +205,9 @@ impl WeightInfo for () { fn ready_ring_unknit() -> Weight { // Proof Size summary in bytes: // Measured: `233` - // Estimated: `7527` - // Minimum execution time: 11_854_000 picoseconds. - Weight::from_parts(12_178_000, 7527) + // Estimated: `6038` + // Minimum execution time: 11_586_000 picoseconds. + Weight::from_parts(11_912_000, 6038) .saturating_add(RocksDbWeight::get().reads(3_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) } @@ -220,8 +217,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `42` // Estimated: `3514` - // Minimum execution time: 7_900_000 picoseconds. - Weight::from_parts(8_046_000, 3514) + // Minimum execution time: 4_581_000 picoseconds. + Weight::from_parts(4_715_000, 3514) .saturating_add(RocksDbWeight::get().reads(1_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } @@ -231,8 +228,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `113` // Estimated: `69049` - // Minimum execution time: 6_284_000 picoseconds. - Weight::from_parts(6_433_000, 69049) + // Minimum execution time: 5_826_000 picoseconds. + Weight::from_parts(5_932_000, 69049) .saturating_add(RocksDbWeight::get().reads(1_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } @@ -242,8 +239,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `113` // Estimated: `69049` - // Minimum execution time: 6_418_000 picoseconds. - Weight::from_parts(6_633_000, 69049) + // Minimum execution time: 6_235_000 picoseconds. + Weight::from_parts(6_430_000, 69049) .saturating_add(RocksDbWeight::get().reads(1_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } @@ -251,8 +248,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 52_661_000 picoseconds. - Weight::from_parts(52_994_000, 0) + // Minimum execution time: 53_860_000 picoseconds. + Weight::from_parts(53_984_000, 0) } /// Storage: MessageQueue ServiceHead (r:1 w:1) /// Proof: MessageQueue ServiceHead (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) @@ -261,9 +258,9 @@ impl WeightInfo for () { fn bump_service_head() -> Weight { // Proof Size summary in bytes: // Measured: `140` - // Estimated: `5003` - // Minimum execution time: 7_132_000 picoseconds. - Weight::from_parts(7_386_000, 5003) + // Estimated: `3514` + // Minimum execution time: 7_018_000 picoseconds. + Weight::from_parts(7_205_000, 3514) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } @@ -274,9 +271,9 @@ impl WeightInfo for () { fn reap_page() -> Weight { // Proof Size summary in bytes: // Measured: `65710` - // Estimated: `72563` - // Minimum execution time: 54_377_000 picoseconds. - Weight::from_parts(54_804_000, 72563) + // Estimated: `69049` + // Minimum execution time: 53_485_000 picoseconds. + Weight::from_parts(54_154_000, 69049) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().writes(2_u64)) } @@ -287,9 +284,9 @@ impl WeightInfo for () { fn execute_overweight_page_removed() -> Weight { // Proof Size summary in bytes: // Measured: `65710` - // Estimated: `72563` - // Minimum execution time: 69_461_000 picoseconds. - Weight::from_parts(70_016_000, 72563) + // Estimated: `69049` + // Minimum execution time: 68_830_000 picoseconds. + Weight::from_parts(69_487_000, 69049) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().writes(2_u64)) } @@ -300,9 +297,9 @@ impl WeightInfo for () { fn execute_overweight_page_updated() -> Weight { // Proof Size summary in bytes: // Measured: `65710` - // Estimated: `72563` - // Minimum execution time: 81_787_000 picoseconds. - Weight::from_parts(83_100_000, 72563) + // Estimated: `69049` + // Minimum execution time: 81_643_000 picoseconds. + Weight::from_parts(82_399_000, 69049) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().writes(2_u64)) } diff --git a/frame/multisig/src/weights.rs b/frame/multisig/src/weights.rs index 8941ce44f..7fda4bec8 100644 --- a/frame/multisig/src/weights.rs +++ b/frame/multisig/src/weights.rs @@ -18,9 +18,9 @@ //! Autogenerated weights for pallet_multisig //! //! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev -//! DATE: 2023-03-16, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! DATE: 2023-04-06, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` //! WORST CASE MAP SIZE: `1000000` -//! HOSTNAME: `bm3`, CPU: `Intel(R) Core(TM) i7-7700K CPU @ 4.20GHz` +//! HOSTNAME: `bm2`, CPU: `Intel(R) Core(TM) i7-7700K CPU @ 4.20GHz` //! EXECUTION: Some(Wasm), WASM-EXECUTION: Compiled, CHAIN: Some("dev"), DB CACHE: 1024 // Executed Command: @@ -31,9 +31,6 @@ // --steps=50 // --repeat=20 // --pallet=pallet_multisig -// --no-storage-info -// --no-median-slopes -// --no-min-squares // --extrinsic=* // --execution=wasm // --wasm-execution=compiled @@ -68,10 +65,10 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 12_096_000 picoseconds. - Weight::from_parts(12_442_958, 0) - // Standard Error: 1 - .saturating_add(Weight::from_parts(494, 0).saturating_mul(z.into())) + // Minimum execution time: 12_199_000 picoseconds. + Weight::from_parts(12_595_771, 0) + // Standard Error: 2 + .saturating_add(Weight::from_parts(490, 0).saturating_mul(z.into())) } /// Storage: Multisig Multisigs (r:1 w:1) /// Proof: Multisig Multisigs (max_values: None, max_size: Some(3346), added: 5821, mode: MaxEncodedLen) @@ -81,12 +78,12 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `301 + s * (2 ±0)` // Estimated: `6811` - // Minimum execution time: 37_609_000 picoseconds. - Weight::from_parts(32_693_100, 6811) - // Standard Error: 449 - .saturating_add(Weight::from_parts(56_623, 0).saturating_mul(s.into())) - // Standard Error: 4 - .saturating_add(Weight::from_parts(1_203, 0).saturating_mul(z.into())) + // Minimum execution time: 42_810_000 picoseconds. + Weight::from_parts(37_500_997, 6811) + // Standard Error: 308 + .saturating_add(Weight::from_parts(59_961, 0).saturating_mul(s.into())) + // Standard Error: 3 + .saturating_add(Weight::from_parts(1_198, 0).saturating_mul(z.into())) .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -98,12 +95,12 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `320` // Estimated: `6811` - // Minimum execution time: 27_434_000 picoseconds. - Weight::from_parts(22_659_090, 6811) - // Standard Error: 328 - .saturating_add(Weight::from_parts(53_508, 0).saturating_mul(s.into())) - // Standard Error: 3 - .saturating_add(Weight::from_parts(1_213, 0).saturating_mul(z.into())) + // Minimum execution time: 27_775_000 picoseconds. + Weight::from_parts(22_868_524, 6811) + // Standard Error: 273 + .saturating_add(Weight::from_parts(55_219, 0).saturating_mul(s.into())) + // Standard Error: 2 + .saturating_add(Weight::from_parts(1_202, 0).saturating_mul(z.into())) .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -116,13 +113,13 @@ impl WeightInfo for SubstrateWeight { fn as_multi_complete(s: u32, z: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `426 + s * (33 ±0)` - // Estimated: `10404` - // Minimum execution time: 43_592_000 picoseconds. - Weight::from_parts(36_465_266, 10404) - // Standard Error: 1_049 - .saturating_add(Weight::from_parts(78_833, 0).saturating_mul(s.into())) - // Standard Error: 10 - .saturating_add(Weight::from_parts(1_238, 0).saturating_mul(z.into())) + // Estimated: `6811` + // Minimum execution time: 48_223_000 picoseconds. + Weight::from_parts(39_193_453, 6811) + // Standard Error: 2_162 + .saturating_add(Weight::from_parts(93_763, 0).saturating_mul(s.into())) + // Standard Error: 21 + .saturating_add(Weight::from_parts(1_372, 0).saturating_mul(z.into())) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().writes(2_u64)) } @@ -133,10 +130,10 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `301 + s * (2 ±0)` // Estimated: `6811` - // Minimum execution time: 30_216_000 picoseconds. - Weight::from_parts(31_430_373, 6811) - // Standard Error: 967 - .saturating_add(Weight::from_parts(57_035, 0).saturating_mul(s.into())) + // Minimum execution time: 34_775_000 picoseconds. + Weight::from_parts(35_966_626, 6811) + // Standard Error: 464 + .saturating_add(Weight::from_parts(61_492, 0).saturating_mul(s.into())) .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -147,10 +144,10 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `320` // Estimated: `6811` - // Minimum execution time: 19_960_000 picoseconds. - Weight::from_parts(21_281_659, 6811) - // Standard Error: 435 - .saturating_add(Weight::from_parts(56_445, 0).saturating_mul(s.into())) + // Minimum execution time: 19_947_000 picoseconds. + Weight::from_parts(21_253_025, 6811) + // Standard Error: 402 + .saturating_add(Weight::from_parts(58_491, 0).saturating_mul(s.into())) .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -161,10 +158,10 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `492 + s * (1 ±0)` // Estimated: `6811` - // Minimum execution time: 31_219_000 picoseconds. - Weight::from_parts(32_395_963, 6811) - // Standard Error: 500 - .saturating_add(Weight::from_parts(56_853, 0).saturating_mul(s.into())) + // Minimum execution time: 35_023_000 picoseconds. + Weight::from_parts(36_756_977, 6811) + // Standard Error: 547 + .saturating_add(Weight::from_parts(62_235, 0).saturating_mul(s.into())) .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -177,10 +174,10 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 12_096_000 picoseconds. - Weight::from_parts(12_442_958, 0) - // Standard Error: 1 - .saturating_add(Weight::from_parts(494, 0).saturating_mul(z.into())) + // Minimum execution time: 12_199_000 picoseconds. + Weight::from_parts(12_595_771, 0) + // Standard Error: 2 + .saturating_add(Weight::from_parts(490, 0).saturating_mul(z.into())) } /// Storage: Multisig Multisigs (r:1 w:1) /// Proof: Multisig Multisigs (max_values: None, max_size: Some(3346), added: 5821, mode: MaxEncodedLen) @@ -190,12 +187,12 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `301 + s * (2 ±0)` // Estimated: `6811` - // Minimum execution time: 37_609_000 picoseconds. - Weight::from_parts(32_693_100, 6811) - // Standard Error: 449 - .saturating_add(Weight::from_parts(56_623, 0).saturating_mul(s.into())) - // Standard Error: 4 - .saturating_add(Weight::from_parts(1_203, 0).saturating_mul(z.into())) + // Minimum execution time: 42_810_000 picoseconds. + Weight::from_parts(37_500_997, 6811) + // Standard Error: 308 + .saturating_add(Weight::from_parts(59_961, 0).saturating_mul(s.into())) + // Standard Error: 3 + .saturating_add(Weight::from_parts(1_198, 0).saturating_mul(z.into())) .saturating_add(RocksDbWeight::get().reads(1_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } @@ -207,12 +204,12 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `320` // Estimated: `6811` - // Minimum execution time: 27_434_000 picoseconds. - Weight::from_parts(22_659_090, 6811) - // Standard Error: 328 - .saturating_add(Weight::from_parts(53_508, 0).saturating_mul(s.into())) - // Standard Error: 3 - .saturating_add(Weight::from_parts(1_213, 0).saturating_mul(z.into())) + // Minimum execution time: 27_775_000 picoseconds. + Weight::from_parts(22_868_524, 6811) + // Standard Error: 273 + .saturating_add(Weight::from_parts(55_219, 0).saturating_mul(s.into())) + // Standard Error: 2 + .saturating_add(Weight::from_parts(1_202, 0).saturating_mul(z.into())) .saturating_add(RocksDbWeight::get().reads(1_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } @@ -225,13 +222,13 @@ impl WeightInfo for () { fn as_multi_complete(s: u32, z: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `426 + s * (33 ±0)` - // Estimated: `10404` - // Minimum execution time: 43_592_000 picoseconds. - Weight::from_parts(36_465_266, 10404) - // Standard Error: 1_049 - .saturating_add(Weight::from_parts(78_833, 0).saturating_mul(s.into())) - // Standard Error: 10 - .saturating_add(Weight::from_parts(1_238, 0).saturating_mul(z.into())) + // Estimated: `6811` + // Minimum execution time: 48_223_000 picoseconds. + Weight::from_parts(39_193_453, 6811) + // Standard Error: 2_162 + .saturating_add(Weight::from_parts(93_763, 0).saturating_mul(s.into())) + // Standard Error: 21 + .saturating_add(Weight::from_parts(1_372, 0).saturating_mul(z.into())) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().writes(2_u64)) } @@ -242,10 +239,10 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `301 + s * (2 ±0)` // Estimated: `6811` - // Minimum execution time: 30_216_000 picoseconds. - Weight::from_parts(31_430_373, 6811) - // Standard Error: 967 - .saturating_add(Weight::from_parts(57_035, 0).saturating_mul(s.into())) + // Minimum execution time: 34_775_000 picoseconds. + Weight::from_parts(35_966_626, 6811) + // Standard Error: 464 + .saturating_add(Weight::from_parts(61_492, 0).saturating_mul(s.into())) .saturating_add(RocksDbWeight::get().reads(1_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } @@ -256,10 +253,10 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `320` // Estimated: `6811` - // Minimum execution time: 19_960_000 picoseconds. - Weight::from_parts(21_281_659, 6811) - // Standard Error: 435 - .saturating_add(Weight::from_parts(56_445, 0).saturating_mul(s.into())) + // Minimum execution time: 19_947_000 picoseconds. + Weight::from_parts(21_253_025, 6811) + // Standard Error: 402 + .saturating_add(Weight::from_parts(58_491, 0).saturating_mul(s.into())) .saturating_add(RocksDbWeight::get().reads(1_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } @@ -270,10 +267,10 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `492 + s * (1 ±0)` // Estimated: `6811` - // Minimum execution time: 31_219_000 picoseconds. - Weight::from_parts(32_395_963, 6811) - // Standard Error: 500 - .saturating_add(Weight::from_parts(56_853, 0).saturating_mul(s.into())) + // Minimum execution time: 35_023_000 picoseconds. + Weight::from_parts(36_756_977, 6811) + // Standard Error: 547 + .saturating_add(Weight::from_parts(62_235, 0).saturating_mul(s.into())) .saturating_add(RocksDbWeight::get().reads(1_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } diff --git a/frame/nfts/src/weights.rs b/frame/nfts/src/weights.rs index c5010a73c..19a61974a 100644 --- a/frame/nfts/src/weights.rs +++ b/frame/nfts/src/weights.rs @@ -18,9 +18,9 @@ //! Autogenerated weights for pallet_nfts //! //! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev -//! DATE: 2023-03-16, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! DATE: 2023-04-06, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` //! WORST CASE MAP SIZE: `1000000` -//! HOSTNAME: `bm3`, CPU: `Intel(R) Core(TM) i7-7700K CPU @ 4.20GHz` +//! HOSTNAME: `bm2`, CPU: `Intel(R) Core(TM) i7-7700K CPU @ 4.20GHz` //! EXECUTION: Some(Wasm), WASM-EXECUTION: Compiled, CHAIN: Some("dev"), DB CACHE: 1024 // Executed Command: @@ -31,9 +31,6 @@ // --steps=50 // --repeat=20 // --pallet=pallet_nfts -// --no-storage-info -// --no-median-slopes -// --no-min-squares // --extrinsic=* // --execution=wasm // --wasm-execution=compiled @@ -108,9 +105,9 @@ impl WeightInfo for SubstrateWeight { fn create() -> Weight { // Proof Size summary in bytes: // Measured: `182` - // Estimated: `5038` - // Minimum execution time: 36_780_000 picoseconds. - Weight::from_parts(37_508_000, 5038) + // Estimated: `3549` + // Minimum execution time: 40_664_000 picoseconds. + Weight::from_parts(41_224_000, 3549) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().writes(5_u64)) } @@ -127,9 +124,9 @@ impl WeightInfo for SubstrateWeight { fn force_create() -> Weight { // Proof Size summary in bytes: // Measured: `42` - // Estimated: `5038` - // Minimum execution time: 25_144_000 picoseconds. - Weight::from_parts(25_800_000, 5038) + // Estimated: `3549` + // Minimum execution time: 24_725_000 picoseconds. + Weight::from_parts(25_147_000, 3549) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().writes(5_u64)) } @@ -152,16 +149,14 @@ impl WeightInfo for SubstrateWeight { /// The range of component `m` is `[0, 1000]`. /// The range of component `c` is `[0, 1000]`. /// The range of component `a` is `[0, 1000]`. - fn destroy(m: u32, _c: u32, a: u32, ) -> Weight { + fn destroy(_m: u32, _c: u32, a: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `32186 + a * (332 ±0)` - // Estimated: `2538589 + a * (2921 ±0)` - // Minimum execution time: 1_122_858_000 picoseconds. - Weight::from_parts(1_094_711_913, 2538589) - // Standard Error: 4_485 - .saturating_add(Weight::from_parts(3_481, 0).saturating_mul(m.into())) - // Standard Error: 4_485 - .saturating_add(Weight::from_parts(5_520_602, 0).saturating_mul(a.into())) + // Estimated: `2523990 + a * (2921 ±0)` + // Minimum execution time: 1_100_509_000 picoseconds. + Weight::from_parts(1_081_634_178, 2523990) + // Standard Error: 3_025 + .saturating_add(Weight::from_parts(5_339_415, 0).saturating_mul(a.into())) .saturating_add(T::DbWeight::get().reads(1004_u64)) .saturating_add(T::DbWeight::get().reads((1_u64).saturating_mul(a.into()))) .saturating_add(T::DbWeight::get().writes(1005_u64)) @@ -183,9 +178,9 @@ impl WeightInfo for SubstrateWeight { fn mint() -> Weight { // Proof Size summary in bytes: // Measured: `421` - // Estimated: `18460` - // Minimum execution time: 48_590_000 picoseconds. - Weight::from_parts(49_260_000, 18460) + // Estimated: `4326` + // Minimum execution time: 52_464_000 picoseconds. + Weight::from_parts(52_847_000, 4326) .saturating_add(T::DbWeight::get().reads(5_u64)) .saturating_add(T::DbWeight::get().writes(4_u64)) } @@ -204,9 +199,9 @@ impl WeightInfo for SubstrateWeight { fn force_mint() -> Weight { // Proof Size summary in bytes: // Measured: `421` - // Estimated: `18460` - // Minimum execution time: 46_646_000 picoseconds. - Weight::from_parts(47_331_000, 18460) + // Estimated: `4326` + // Minimum execution time: 50_327_000 picoseconds. + Weight::from_parts(51_093_000, 4326) .saturating_add(T::DbWeight::get().reads(5_u64)) .saturating_add(T::DbWeight::get().writes(4_u64)) } @@ -229,9 +224,9 @@ impl WeightInfo for SubstrateWeight { fn burn() -> Weight { // Proof Size summary in bytes: // Measured: `530` - // Estimated: `14993` - // Minimum execution time: 46_976_000 picoseconds. - Weight::from_parts(47_606_000, 14993) + // Estimated: `4326` + // Minimum execution time: 51_342_000 picoseconds. + Weight::from_parts(51_846_000, 4326) .saturating_add(T::DbWeight::get().reads(4_u64)) .saturating_add(T::DbWeight::get().writes(7_u64)) } @@ -252,9 +247,9 @@ impl WeightInfo for SubstrateWeight { fn transfer() -> Weight { // Proof Size summary in bytes: // Measured: `559` - // Estimated: `14926` - // Minimum execution time: 38_013_000 picoseconds. - Weight::from_parts(38_420_000, 14926) + // Estimated: `4326` + // Minimum execution time: 38_309_000 picoseconds. + Weight::from_parts(38_672_000, 4326) .saturating_add(T::DbWeight::get().reads(4_u64)) .saturating_add(T::DbWeight::get().writes(5_u64)) } @@ -268,11 +263,11 @@ impl WeightInfo for SubstrateWeight { fn redeposit(i: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `729 + i * (108 ±0)` - // Estimated: `8077 + i * (3336 ±0)` - // Minimum execution time: 17_428_000 picoseconds. - Weight::from_parts(17_664_000, 8077) - // Standard Error: 16_385 - .saturating_add(Weight::from_parts(14_150_405, 0).saturating_mul(i.into())) + // Estimated: `3549 + i * (3336 ±0)` + // Minimum execution time: 17_525_000 picoseconds. + Weight::from_parts(17_657_000, 3549) + // Standard Error: 15_884 + .saturating_add(Weight::from_parts(16_026_633, 0).saturating_mul(i.into())) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().reads((1_u64).saturating_mul(i.into()))) .saturating_add(T::DbWeight::get().writes((1_u64).saturating_mul(i.into()))) @@ -285,9 +280,9 @@ impl WeightInfo for SubstrateWeight { fn lock_item_transfer() -> Weight { // Proof Size summary in bytes: // Measured: `401` - // Estimated: `7047` - // Minimum execution time: 21_760_000 picoseconds. - Weight::from_parts(22_063_000, 7047) + // Estimated: `3534` + // Minimum execution time: 21_814_000 picoseconds. + Weight::from_parts(22_171_000, 3534) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -298,9 +293,9 @@ impl WeightInfo for SubstrateWeight { fn unlock_item_transfer() -> Weight { // Proof Size summary in bytes: // Measured: `401` - // Estimated: `7047` - // Minimum execution time: 21_622_000 picoseconds. - Weight::from_parts(22_056_000, 7047) + // Estimated: `3534` + // Minimum execution time: 21_728_000 picoseconds. + Weight::from_parts(21_893_000, 3534) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -311,9 +306,9 @@ impl WeightInfo for SubstrateWeight { fn lock_collection() -> Weight { // Proof Size summary in bytes: // Measured: `306` - // Estimated: `7087` - // Minimum execution time: 18_820_000 picoseconds. - Weight::from_parts(19_212_000, 7087) + // Estimated: `3549` + // Minimum execution time: 18_359_000 picoseconds. + Weight::from_parts(19_101_000, 3549) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -326,9 +321,9 @@ impl WeightInfo for SubstrateWeight { fn transfer_ownership() -> Weight { // Proof Size summary in bytes: // Measured: `354` - // Estimated: `7066` - // Minimum execution time: 25_433_000 picoseconds. - Weight::from_parts(25_793_000, 7066) + // Estimated: `3549` + // Minimum execution time: 24_713_000 picoseconds. + Weight::from_parts(25_032_000, 3549) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().writes(4_u64)) } @@ -339,9 +334,9 @@ impl WeightInfo for SubstrateWeight { fn set_team() -> Weight { // Proof Size summary in bytes: // Measured: `335` - // Estimated: `9627` - // Minimum execution time: 43_368_000 picoseconds. - Weight::from_parts(43_974_000, 9627) + // Estimated: `6078` + // Minimum execution time: 42_372_000 picoseconds. + Weight::from_parts(42_971_000, 6078) .saturating_add(T::DbWeight::get().reads(3_u64)) .saturating_add(T::DbWeight::get().writes(5_u64)) } @@ -353,8 +348,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `277` // Estimated: `3549` - // Minimum execution time: 20_009_000 picoseconds. - Weight::from_parts(20_220_000, 3549) + // Minimum execution time: 19_703_000 picoseconds. + Weight::from_parts(19_993_000, 3549) .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) } @@ -366,8 +361,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `242` // Estimated: `3549` - // Minimum execution time: 16_048_000 picoseconds. - Weight::from_parts(16_353_000, 3549) + // Minimum execution time: 15_500_000 picoseconds. + Weight::from_parts(15_929_000, 3549) .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -378,9 +373,9 @@ impl WeightInfo for SubstrateWeight { fn lock_item_properties() -> Weight { // Proof Size summary in bytes: // Measured: `401` - // Estimated: `7047` - // Minimum execution time: 20_890_000 picoseconds. - Weight::from_parts(21_237_000, 7047) + // Estimated: `3534` + // Minimum execution time: 20_778_000 picoseconds. + Weight::from_parts(21_187_000, 3534) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -397,9 +392,9 @@ impl WeightInfo for SubstrateWeight { fn set_attribute() -> Weight { // Proof Size summary in bytes: // Measured: `505` - // Estimated: `18045` - // Minimum execution time: 50_686_000 picoseconds. - Weight::from_parts(50_981_000, 18045) + // Estimated: `3911` + // Minimum execution time: 53_016_000 picoseconds. + Weight::from_parts(53_579_000, 3911) .saturating_add(T::DbWeight::get().reads(5_u64)) .saturating_add(T::DbWeight::get().writes(2_u64)) } @@ -410,9 +405,9 @@ impl WeightInfo for SubstrateWeight { fn force_set_attribute() -> Weight { // Proof Size summary in bytes: // Measured: `310` - // Estimated: `7460` - // Minimum execution time: 28_714_000 picoseconds. - Weight::from_parts(29_044_000, 7460) + // Estimated: `3911` + // Minimum execution time: 28_790_000 picoseconds. + Weight::from_parts(29_157_000, 3911) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().writes(2_u64)) } @@ -427,9 +422,9 @@ impl WeightInfo for SubstrateWeight { fn clear_attribute() -> Weight { // Proof Size summary in bytes: // Measured: `916` - // Estimated: `14507` - // Minimum execution time: 46_232_000 picoseconds. - Weight::from_parts(46_738_000, 14507) + // Estimated: `3911` + // Minimum execution time: 48_584_000 picoseconds. + Weight::from_parts(49_202_000, 3911) .saturating_add(T::DbWeight::get().reads(4_u64)) .saturating_add(T::DbWeight::get().writes(2_u64)) } @@ -440,9 +435,9 @@ impl WeightInfo for SubstrateWeight { fn approve_item_attributes() -> Weight { // Proof Size summary in bytes: // Measured: `347` - // Estimated: `8472` - // Minimum execution time: 19_332_000 picoseconds. - Weight::from_parts(19_765_000, 8472) + // Estimated: `4326` + // Minimum execution time: 19_616_000 picoseconds. + Weight::from_parts(19_972_000, 4326) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -458,11 +453,11 @@ impl WeightInfo for SubstrateWeight { fn cancel_item_attributes_approval(n: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `803 + n * (364 ±0)` - // Estimated: `15976 + n * (2921 ±0)` - // Minimum execution time: 29_227_000 picoseconds. - Weight::from_parts(29_661_000, 15976) - // Standard Error: 3_574 - .saturating_add(Weight::from_parts(5_442_937, 0).saturating_mul(n.into())) + // Estimated: `4326 + n * (2921 ±0)` + // Minimum execution time: 28_897_000 picoseconds. + Weight::from_parts(29_061_000, 4326) + // Standard Error: 3_139 + .saturating_add(Weight::from_parts(5_396_415, 0).saturating_mul(n.into())) .saturating_add(T::DbWeight::get().reads(4_u64)) .saturating_add(T::DbWeight::get().reads((1_u64).saturating_mul(n.into()))) .saturating_add(T::DbWeight::get().writes(2_u64)) @@ -482,9 +477,9 @@ impl WeightInfo for SubstrateWeight { fn set_metadata() -> Weight { // Proof Size summary in bytes: // Measured: `505` - // Estimated: `17739` - // Minimum execution time: 41_297_000 picoseconds. - Weight::from_parts(41_902_000, 17739) + // Estimated: `3605` + // Minimum execution time: 43_748_000 picoseconds. + Weight::from_parts(44_178_000, 3605) .saturating_add(T::DbWeight::get().reads(5_u64)) .saturating_add(T::DbWeight::get().writes(2_u64)) } @@ -499,9 +494,9 @@ impl WeightInfo for SubstrateWeight { fn clear_metadata() -> Weight { // Proof Size summary in bytes: // Measured: `608` - // Estimated: `14201` - // Minimum execution time: 39_312_000 picoseconds. - Weight::from_parts(40_048_000, 14201) + // Estimated: `3605` + // Minimum execution time: 42_116_000 picoseconds. + Weight::from_parts(42_455_000, 3605) .saturating_add(T::DbWeight::get().reads(4_u64)) .saturating_add(T::DbWeight::get().writes(2_u64)) } @@ -516,9 +511,9 @@ impl WeightInfo for SubstrateWeight { fn set_collection_metadata() -> Weight { // Proof Size summary in bytes: // Measured: `364` - // Estimated: `14173` - // Minimum execution time: 36_932_000 picoseconds. - Weight::from_parts(38_207_000, 14173) + // Estimated: `3552` + // Minimum execution time: 40_926_000 picoseconds. + Weight::from_parts(41_512_000, 3552) .saturating_add(T::DbWeight::get().reads(4_u64)) .saturating_add(T::DbWeight::get().writes(2_u64)) } @@ -533,9 +528,9 @@ impl WeightInfo for SubstrateWeight { fn clear_collection_metadata() -> Weight { // Proof Size summary in bytes: // Measured: `475` - // Estimated: `14173` - // Minimum execution time: 35_264_000 picoseconds. - Weight::from_parts(35_829_000, 14173) + // Estimated: `3552` + // Minimum execution time: 39_792_000 picoseconds. + Weight::from_parts(40_443_000, 3552) .saturating_add(T::DbWeight::get().reads(4_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -546,9 +541,9 @@ impl WeightInfo for SubstrateWeight { fn approve_transfer() -> Weight { // Proof Size summary in bytes: // Measured: `376` - // Estimated: `7864` - // Minimum execution time: 22_819_000 picoseconds. - Weight::from_parts(23_121_000, 7864) + // Estimated: `4326` + // Minimum execution time: 22_648_000 picoseconds. + Weight::from_parts(23_139_000, 4326) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -558,8 +553,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `384` // Estimated: `4326` - // Minimum execution time: 20_377_000 picoseconds. - Weight::from_parts(20_625_000, 4326) + // Minimum execution time: 20_552_000 picoseconds. + Weight::from_parts(20_920_000, 4326) .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -569,8 +564,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `384` // Estimated: `4326` - // Minimum execution time: 19_180_000 picoseconds. - Weight::from_parts(19_468_000, 4326) + // Minimum execution time: 19_114_000 picoseconds. + Weight::from_parts(19_876_000, 4326) .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -580,8 +575,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `42` // Estimated: `3517` - // Minimum execution time: 17_381_000 picoseconds. - Weight::from_parts(17_718_000, 3517) + // Minimum execution time: 17_089_000 picoseconds. + Weight::from_parts(17_363_000, 3517) .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -592,9 +587,9 @@ impl WeightInfo for SubstrateWeight { fn set_collection_max_supply() -> Weight { // Proof Size summary in bytes: // Measured: `306` - // Estimated: `7087` - // Minimum execution time: 20_373_000 picoseconds. - Weight::from_parts(20_662_000, 7087) + // Estimated: `3549` + // Minimum execution time: 20_667_000 picoseconds. + Weight::from_parts(20_898_000, 3549) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -605,9 +600,9 @@ impl WeightInfo for SubstrateWeight { fn update_mint_settings() -> Weight { // Proof Size summary in bytes: // Measured: `289` - // Estimated: `7072` - // Minimum execution time: 20_154_000 picoseconds. - Weight::from_parts(20_592_000, 7072) + // Estimated: `3538` + // Minimum execution time: 19_666_000 picoseconds. + Weight::from_parts(20_136_000, 3538) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -622,9 +617,9 @@ impl WeightInfo for SubstrateWeight { fn set_price() -> Weight { // Proof Size summary in bytes: // Measured: `484` - // Estimated: `11377` - // Minimum execution time: 25_644_000 picoseconds. - Weight::from_parts(25_999_000, 11377) + // Estimated: `4326` + // Minimum execution time: 25_778_000 picoseconds. + Weight::from_parts(26_447_000, 4326) .saturating_add(T::DbWeight::get().reads(3_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -645,9 +640,9 @@ impl WeightInfo for SubstrateWeight { fn buy_item() -> Weight { // Proof Size summary in bytes: // Measured: `671` - // Estimated: `18480` - // Minimum execution time: 51_542_000 picoseconds. - Weight::from_parts(52_203_000, 18480) + // Estimated: `4326` + // Minimum execution time: 50_809_000 picoseconds. + Weight::from_parts(51_503_000, 4326) .saturating_add(T::DbWeight::get().reads(5_u64)) .saturating_add(T::DbWeight::get().writes(5_u64)) } @@ -656,10 +651,10 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 2_796_000 picoseconds. - Weight::from_parts(5_517_617, 0) - // Standard Error: 14_247 - .saturating_add(Weight::from_parts(3_757_796, 0).saturating_mul(n.into())) + // Minimum execution time: 2_789_000 picoseconds. + Weight::from_parts(5_528_034, 0) + // Standard Error: 14_405 + .saturating_add(Weight::from_parts(3_788_038, 0).saturating_mul(n.into())) } /// Storage: Nfts Item (r:2 w:0) /// Proof: Nfts Item (max_values: None, max_size: Some(861), added: 3336, mode: MaxEncodedLen) @@ -669,8 +664,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `460` // Estimated: `7662` - // Minimum execution time: 23_218_000 picoseconds. - Weight::from_parts(23_590_000, 7662) + // Minimum execution time: 22_884_000 picoseconds. + Weight::from_parts(23_732_000, 7662) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -681,9 +676,9 @@ impl WeightInfo for SubstrateWeight { fn cancel_swap() -> Weight { // Proof Size summary in bytes: // Measured: `479` - // Estimated: `7862` - // Minimum execution time: 22_536_000 picoseconds. - Weight::from_parts(22_874_000, 7862) + // Estimated: `4326` + // Minimum execution time: 22_686_000 picoseconds. + Weight::from_parts(23_088_000, 4326) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -704,9 +699,9 @@ impl WeightInfo for SubstrateWeight { fn claim_swap() -> Weight { // Proof Size summary in bytes: // Measured: `800` - // Estimated: `24321` - // Minimum execution time: 77_372_000 picoseconds. - Weight::from_parts(78_109_000, 24321) + // Estimated: `7662` + // Minimum execution time: 77_494_000 picoseconds. + Weight::from_parts(78_650_000, 7662) .saturating_add(T::DbWeight::get().reads(7_u64)) .saturating_add(T::DbWeight::get().writes(10_u64)) } @@ -732,11 +727,11 @@ impl WeightInfo for SubstrateWeight { fn mint_pre_signed(n: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `595` - // Estimated: `29192 + n * (2921 ±0)` - // Minimum execution time: 131_935_000 picoseconds. - Weight::from_parts(137_455_891, 29192) - // Standard Error: 31_617 - .saturating_add(Weight::from_parts(27_550_750, 0).saturating_mul(n.into())) + // Estimated: `6078 + n * (2921 ±0)` + // Minimum execution time: 139_109_000 picoseconds. + Weight::from_parts(144_449_034, 6078) + // Standard Error: 26_869 + .saturating_add(Weight::from_parts(29_961_772, 0).saturating_mul(n.into())) .saturating_add(T::DbWeight::get().reads(8_u64)) .saturating_add(T::DbWeight::get().reads((1_u64).saturating_mul(n.into()))) .saturating_add(T::DbWeight::get().writes(6_u64)) @@ -759,11 +754,11 @@ impl WeightInfo for SubstrateWeight { fn set_attributes_pre_signed(n: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `625` - // Estimated: `20142 + n * (2921 ±0)` - // Minimum execution time: 77_694_000 picoseconds. - Weight::from_parts(90_089_732, 20142) - // Standard Error: 71_327 - .saturating_add(Weight::from_parts(27_832_189, 0).saturating_mul(n.into())) + // Estimated: `4326 + n * (2921 ±0)` + // Minimum execution time: 78_280_000 picoseconds. + Weight::from_parts(92_826_883, 4326) + // Standard Error: 81_125 + .saturating_add(Weight::from_parts(29_898_245, 0).saturating_mul(n.into())) .saturating_add(T::DbWeight::get().reads(4_u64)) .saturating_add(T::DbWeight::get().reads((1_u64).saturating_mul(n.into()))) .saturating_add(T::DbWeight::get().writes(2_u64)) @@ -787,9 +782,9 @@ impl WeightInfo for () { fn create() -> Weight { // Proof Size summary in bytes: // Measured: `182` - // Estimated: `5038` - // Minimum execution time: 36_780_000 picoseconds. - Weight::from_parts(37_508_000, 5038) + // Estimated: `3549` + // Minimum execution time: 40_664_000 picoseconds. + Weight::from_parts(41_224_000, 3549) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().writes(5_u64)) } @@ -806,9 +801,9 @@ impl WeightInfo for () { fn force_create() -> Weight { // Proof Size summary in bytes: // Measured: `42` - // Estimated: `5038` - // Minimum execution time: 25_144_000 picoseconds. - Weight::from_parts(25_800_000, 5038) + // Estimated: `3549` + // Minimum execution time: 24_725_000 picoseconds. + Weight::from_parts(25_147_000, 3549) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().writes(5_u64)) } @@ -831,16 +826,14 @@ impl WeightInfo for () { /// The range of component `m` is `[0, 1000]`. /// The range of component `c` is `[0, 1000]`. /// The range of component `a` is `[0, 1000]`. - fn destroy(m: u32, _c: u32, a: u32, ) -> Weight { + fn destroy(_m: u32, _c: u32, a: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `32186 + a * (332 ±0)` - // Estimated: `2538589 + a * (2921 ±0)` - // Minimum execution time: 1_122_858_000 picoseconds. - Weight::from_parts(1_094_711_913, 2538589) - // Standard Error: 4_485 - .saturating_add(Weight::from_parts(3_481, 0).saturating_mul(m.into())) - // Standard Error: 4_485 - .saturating_add(Weight::from_parts(5_520_602, 0).saturating_mul(a.into())) + // Estimated: `2523990 + a * (2921 ±0)` + // Minimum execution time: 1_100_509_000 picoseconds. + Weight::from_parts(1_081_634_178, 2523990) + // Standard Error: 3_025 + .saturating_add(Weight::from_parts(5_339_415, 0).saturating_mul(a.into())) .saturating_add(RocksDbWeight::get().reads(1004_u64)) .saturating_add(RocksDbWeight::get().reads((1_u64).saturating_mul(a.into()))) .saturating_add(RocksDbWeight::get().writes(1005_u64)) @@ -862,9 +855,9 @@ impl WeightInfo for () { fn mint() -> Weight { // Proof Size summary in bytes: // Measured: `421` - // Estimated: `18460` - // Minimum execution time: 48_590_000 picoseconds. - Weight::from_parts(49_260_000, 18460) + // Estimated: `4326` + // Minimum execution time: 52_464_000 picoseconds. + Weight::from_parts(52_847_000, 4326) .saturating_add(RocksDbWeight::get().reads(5_u64)) .saturating_add(RocksDbWeight::get().writes(4_u64)) } @@ -883,9 +876,9 @@ impl WeightInfo for () { fn force_mint() -> Weight { // Proof Size summary in bytes: // Measured: `421` - // Estimated: `18460` - // Minimum execution time: 46_646_000 picoseconds. - Weight::from_parts(47_331_000, 18460) + // Estimated: `4326` + // Minimum execution time: 50_327_000 picoseconds. + Weight::from_parts(51_093_000, 4326) .saturating_add(RocksDbWeight::get().reads(5_u64)) .saturating_add(RocksDbWeight::get().writes(4_u64)) } @@ -908,9 +901,9 @@ impl WeightInfo for () { fn burn() -> Weight { // Proof Size summary in bytes: // Measured: `530` - // Estimated: `14993` - // Minimum execution time: 46_976_000 picoseconds. - Weight::from_parts(47_606_000, 14993) + // Estimated: `4326` + // Minimum execution time: 51_342_000 picoseconds. + Weight::from_parts(51_846_000, 4326) .saturating_add(RocksDbWeight::get().reads(4_u64)) .saturating_add(RocksDbWeight::get().writes(7_u64)) } @@ -931,9 +924,9 @@ impl WeightInfo for () { fn transfer() -> Weight { // Proof Size summary in bytes: // Measured: `559` - // Estimated: `14926` - // Minimum execution time: 38_013_000 picoseconds. - Weight::from_parts(38_420_000, 14926) + // Estimated: `4326` + // Minimum execution time: 38_309_000 picoseconds. + Weight::from_parts(38_672_000, 4326) .saturating_add(RocksDbWeight::get().reads(4_u64)) .saturating_add(RocksDbWeight::get().writes(5_u64)) } @@ -947,11 +940,11 @@ impl WeightInfo for () { fn redeposit(i: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `729 + i * (108 ±0)` - // Estimated: `8077 + i * (3336 ±0)` - // Minimum execution time: 17_428_000 picoseconds. - Weight::from_parts(17_664_000, 8077) - // Standard Error: 16_385 - .saturating_add(Weight::from_parts(14_150_405, 0).saturating_mul(i.into())) + // Estimated: `3549 + i * (3336 ±0)` + // Minimum execution time: 17_525_000 picoseconds. + Weight::from_parts(17_657_000, 3549) + // Standard Error: 15_884 + .saturating_add(Weight::from_parts(16_026_633, 0).saturating_mul(i.into())) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().reads((1_u64).saturating_mul(i.into()))) .saturating_add(RocksDbWeight::get().writes((1_u64).saturating_mul(i.into()))) @@ -964,9 +957,9 @@ impl WeightInfo for () { fn lock_item_transfer() -> Weight { // Proof Size summary in bytes: // Measured: `401` - // Estimated: `7047` - // Minimum execution time: 21_760_000 picoseconds. - Weight::from_parts(22_063_000, 7047) + // Estimated: `3534` + // Minimum execution time: 21_814_000 picoseconds. + Weight::from_parts(22_171_000, 3534) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } @@ -977,9 +970,9 @@ impl WeightInfo for () { fn unlock_item_transfer() -> Weight { // Proof Size summary in bytes: // Measured: `401` - // Estimated: `7047` - // Minimum execution time: 21_622_000 picoseconds. - Weight::from_parts(22_056_000, 7047) + // Estimated: `3534` + // Minimum execution time: 21_728_000 picoseconds. + Weight::from_parts(21_893_000, 3534) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } @@ -990,9 +983,9 @@ impl WeightInfo for () { fn lock_collection() -> Weight { // Proof Size summary in bytes: // Measured: `306` - // Estimated: `7087` - // Minimum execution time: 18_820_000 picoseconds. - Weight::from_parts(19_212_000, 7087) + // Estimated: `3549` + // Minimum execution time: 18_359_000 picoseconds. + Weight::from_parts(19_101_000, 3549) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } @@ -1005,9 +998,9 @@ impl WeightInfo for () { fn transfer_ownership() -> Weight { // Proof Size summary in bytes: // Measured: `354` - // Estimated: `7066` - // Minimum execution time: 25_433_000 picoseconds. - Weight::from_parts(25_793_000, 7066) + // Estimated: `3549` + // Minimum execution time: 24_713_000 picoseconds. + Weight::from_parts(25_032_000, 3549) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().writes(4_u64)) } @@ -1018,9 +1011,9 @@ impl WeightInfo for () { fn set_team() -> Weight { // Proof Size summary in bytes: // Measured: `335` - // Estimated: `9627` - // Minimum execution time: 43_368_000 picoseconds. - Weight::from_parts(43_974_000, 9627) + // Estimated: `6078` + // Minimum execution time: 42_372_000 picoseconds. + Weight::from_parts(42_971_000, 6078) .saturating_add(RocksDbWeight::get().reads(3_u64)) .saturating_add(RocksDbWeight::get().writes(5_u64)) } @@ -1032,8 +1025,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `277` // Estimated: `3549` - // Minimum execution time: 20_009_000 picoseconds. - Weight::from_parts(20_220_000, 3549) + // Minimum execution time: 19_703_000 picoseconds. + Weight::from_parts(19_993_000, 3549) .saturating_add(RocksDbWeight::get().reads(1_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) } @@ -1045,8 +1038,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `242` // Estimated: `3549` - // Minimum execution time: 16_048_000 picoseconds. - Weight::from_parts(16_353_000, 3549) + // Minimum execution time: 15_500_000 picoseconds. + Weight::from_parts(15_929_000, 3549) .saturating_add(RocksDbWeight::get().reads(1_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } @@ -1057,9 +1050,9 @@ impl WeightInfo for () { fn lock_item_properties() -> Weight { // Proof Size summary in bytes: // Measured: `401` - // Estimated: `7047` - // Minimum execution time: 20_890_000 picoseconds. - Weight::from_parts(21_237_000, 7047) + // Estimated: `3534` + // Minimum execution time: 20_778_000 picoseconds. + Weight::from_parts(21_187_000, 3534) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } @@ -1076,9 +1069,9 @@ impl WeightInfo for () { fn set_attribute() -> Weight { // Proof Size summary in bytes: // Measured: `505` - // Estimated: `18045` - // Minimum execution time: 50_686_000 picoseconds. - Weight::from_parts(50_981_000, 18045) + // Estimated: `3911` + // Minimum execution time: 53_016_000 picoseconds. + Weight::from_parts(53_579_000, 3911) .saturating_add(RocksDbWeight::get().reads(5_u64)) .saturating_add(RocksDbWeight::get().writes(2_u64)) } @@ -1089,9 +1082,9 @@ impl WeightInfo for () { fn force_set_attribute() -> Weight { // Proof Size summary in bytes: // Measured: `310` - // Estimated: `7460` - // Minimum execution time: 28_714_000 picoseconds. - Weight::from_parts(29_044_000, 7460) + // Estimated: `3911` + // Minimum execution time: 28_790_000 picoseconds. + Weight::from_parts(29_157_000, 3911) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().writes(2_u64)) } @@ -1106,9 +1099,9 @@ impl WeightInfo for () { fn clear_attribute() -> Weight { // Proof Size summary in bytes: // Measured: `916` - // Estimated: `14507` - // Minimum execution time: 46_232_000 picoseconds. - Weight::from_parts(46_738_000, 14507) + // Estimated: `3911` + // Minimum execution time: 48_584_000 picoseconds. + Weight::from_parts(49_202_000, 3911) .saturating_add(RocksDbWeight::get().reads(4_u64)) .saturating_add(RocksDbWeight::get().writes(2_u64)) } @@ -1119,9 +1112,9 @@ impl WeightInfo for () { fn approve_item_attributes() -> Weight { // Proof Size summary in bytes: // Measured: `347` - // Estimated: `8472` - // Minimum execution time: 19_332_000 picoseconds. - Weight::from_parts(19_765_000, 8472) + // Estimated: `4326` + // Minimum execution time: 19_616_000 picoseconds. + Weight::from_parts(19_972_000, 4326) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } @@ -1137,11 +1130,11 @@ impl WeightInfo for () { fn cancel_item_attributes_approval(n: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `803 + n * (364 ±0)` - // Estimated: `15976 + n * (2921 ±0)` - // Minimum execution time: 29_227_000 picoseconds. - Weight::from_parts(29_661_000, 15976) - // Standard Error: 3_574 - .saturating_add(Weight::from_parts(5_442_937, 0).saturating_mul(n.into())) + // Estimated: `4326 + n * (2921 ±0)` + // Minimum execution time: 28_897_000 picoseconds. + Weight::from_parts(29_061_000, 4326) + // Standard Error: 3_139 + .saturating_add(Weight::from_parts(5_396_415, 0).saturating_mul(n.into())) .saturating_add(RocksDbWeight::get().reads(4_u64)) .saturating_add(RocksDbWeight::get().reads((1_u64).saturating_mul(n.into()))) .saturating_add(RocksDbWeight::get().writes(2_u64)) @@ -1161,9 +1154,9 @@ impl WeightInfo for () { fn set_metadata() -> Weight { // Proof Size summary in bytes: // Measured: `505` - // Estimated: `17739` - // Minimum execution time: 41_297_000 picoseconds. - Weight::from_parts(41_902_000, 17739) + // Estimated: `3605` + // Minimum execution time: 43_748_000 picoseconds. + Weight::from_parts(44_178_000, 3605) .saturating_add(RocksDbWeight::get().reads(5_u64)) .saturating_add(RocksDbWeight::get().writes(2_u64)) } @@ -1178,9 +1171,9 @@ impl WeightInfo for () { fn clear_metadata() -> Weight { // Proof Size summary in bytes: // Measured: `608` - // Estimated: `14201` - // Minimum execution time: 39_312_000 picoseconds. - Weight::from_parts(40_048_000, 14201) + // Estimated: `3605` + // Minimum execution time: 42_116_000 picoseconds. + Weight::from_parts(42_455_000, 3605) .saturating_add(RocksDbWeight::get().reads(4_u64)) .saturating_add(RocksDbWeight::get().writes(2_u64)) } @@ -1195,9 +1188,9 @@ impl WeightInfo for () { fn set_collection_metadata() -> Weight { // Proof Size summary in bytes: // Measured: `364` - // Estimated: `14173` - // Minimum execution time: 36_932_000 picoseconds. - Weight::from_parts(38_207_000, 14173) + // Estimated: `3552` + // Minimum execution time: 40_926_000 picoseconds. + Weight::from_parts(41_512_000, 3552) .saturating_add(RocksDbWeight::get().reads(4_u64)) .saturating_add(RocksDbWeight::get().writes(2_u64)) } @@ -1212,9 +1205,9 @@ impl WeightInfo for () { fn clear_collection_metadata() -> Weight { // Proof Size summary in bytes: // Measured: `475` - // Estimated: `14173` - // Minimum execution time: 35_264_000 picoseconds. - Weight::from_parts(35_829_000, 14173) + // Estimated: `3552` + // Minimum execution time: 39_792_000 picoseconds. + Weight::from_parts(40_443_000, 3552) .saturating_add(RocksDbWeight::get().reads(4_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } @@ -1225,9 +1218,9 @@ impl WeightInfo for () { fn approve_transfer() -> Weight { // Proof Size summary in bytes: // Measured: `376` - // Estimated: `7864` - // Minimum execution time: 22_819_000 picoseconds. - Weight::from_parts(23_121_000, 7864) + // Estimated: `4326` + // Minimum execution time: 22_648_000 picoseconds. + Weight::from_parts(23_139_000, 4326) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } @@ -1237,8 +1230,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `384` // Estimated: `4326` - // Minimum execution time: 20_377_000 picoseconds. - Weight::from_parts(20_625_000, 4326) + // Minimum execution time: 20_552_000 picoseconds. + Weight::from_parts(20_920_000, 4326) .saturating_add(RocksDbWeight::get().reads(1_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } @@ -1248,8 +1241,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `384` // Estimated: `4326` - // Minimum execution time: 19_180_000 picoseconds. - Weight::from_parts(19_468_000, 4326) + // Minimum execution time: 19_114_000 picoseconds. + Weight::from_parts(19_876_000, 4326) .saturating_add(RocksDbWeight::get().reads(1_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } @@ -1259,8 +1252,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `42` // Estimated: `3517` - // Minimum execution time: 17_381_000 picoseconds. - Weight::from_parts(17_718_000, 3517) + // Minimum execution time: 17_089_000 picoseconds. + Weight::from_parts(17_363_000, 3517) .saturating_add(RocksDbWeight::get().reads(1_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } @@ -1271,9 +1264,9 @@ impl WeightInfo for () { fn set_collection_max_supply() -> Weight { // Proof Size summary in bytes: // Measured: `306` - // Estimated: `7087` - // Minimum execution time: 20_373_000 picoseconds. - Weight::from_parts(20_662_000, 7087) + // Estimated: `3549` + // Minimum execution time: 20_667_000 picoseconds. + Weight::from_parts(20_898_000, 3549) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } @@ -1284,9 +1277,9 @@ impl WeightInfo for () { fn update_mint_settings() -> Weight { // Proof Size summary in bytes: // Measured: `289` - // Estimated: `7072` - // Minimum execution time: 20_154_000 picoseconds. - Weight::from_parts(20_592_000, 7072) + // Estimated: `3538` + // Minimum execution time: 19_666_000 picoseconds. + Weight::from_parts(20_136_000, 3538) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } @@ -1301,9 +1294,9 @@ impl WeightInfo for () { fn set_price() -> Weight { // Proof Size summary in bytes: // Measured: `484` - // Estimated: `11377` - // Minimum execution time: 25_644_000 picoseconds. - Weight::from_parts(25_999_000, 11377) + // Estimated: `4326` + // Minimum execution time: 25_778_000 picoseconds. + Weight::from_parts(26_447_000, 4326) .saturating_add(RocksDbWeight::get().reads(3_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } @@ -1324,9 +1317,9 @@ impl WeightInfo for () { fn buy_item() -> Weight { // Proof Size summary in bytes: // Measured: `671` - // Estimated: `18480` - // Minimum execution time: 51_542_000 picoseconds. - Weight::from_parts(52_203_000, 18480) + // Estimated: `4326` + // Minimum execution time: 50_809_000 picoseconds. + Weight::from_parts(51_503_000, 4326) .saturating_add(RocksDbWeight::get().reads(5_u64)) .saturating_add(RocksDbWeight::get().writes(5_u64)) } @@ -1335,10 +1328,10 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 2_796_000 picoseconds. - Weight::from_parts(5_517_617, 0) - // Standard Error: 14_247 - .saturating_add(Weight::from_parts(3_757_796, 0).saturating_mul(n.into())) + // Minimum execution time: 2_789_000 picoseconds. + Weight::from_parts(5_528_034, 0) + // Standard Error: 14_405 + .saturating_add(Weight::from_parts(3_788_038, 0).saturating_mul(n.into())) } /// Storage: Nfts Item (r:2 w:0) /// Proof: Nfts Item (max_values: None, max_size: Some(861), added: 3336, mode: MaxEncodedLen) @@ -1348,8 +1341,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `460` // Estimated: `7662` - // Minimum execution time: 23_218_000 picoseconds. - Weight::from_parts(23_590_000, 7662) + // Minimum execution time: 22_884_000 picoseconds. + Weight::from_parts(23_732_000, 7662) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } @@ -1360,9 +1353,9 @@ impl WeightInfo for () { fn cancel_swap() -> Weight { // Proof Size summary in bytes: // Measured: `479` - // Estimated: `7862` - // Minimum execution time: 22_536_000 picoseconds. - Weight::from_parts(22_874_000, 7862) + // Estimated: `4326` + // Minimum execution time: 22_686_000 picoseconds. + Weight::from_parts(23_088_000, 4326) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } @@ -1383,9 +1376,9 @@ impl WeightInfo for () { fn claim_swap() -> Weight { // Proof Size summary in bytes: // Measured: `800` - // Estimated: `24321` - // Minimum execution time: 77_372_000 picoseconds. - Weight::from_parts(78_109_000, 24321) + // Estimated: `7662` + // Minimum execution time: 77_494_000 picoseconds. + Weight::from_parts(78_650_000, 7662) .saturating_add(RocksDbWeight::get().reads(7_u64)) .saturating_add(RocksDbWeight::get().writes(10_u64)) } @@ -1411,11 +1404,11 @@ impl WeightInfo for () { fn mint_pre_signed(n: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `595` - // Estimated: `29192 + n * (2921 ±0)` - // Minimum execution time: 131_935_000 picoseconds. - Weight::from_parts(137_455_891, 29192) - // Standard Error: 31_617 - .saturating_add(Weight::from_parts(27_550_750, 0).saturating_mul(n.into())) + // Estimated: `6078 + n * (2921 ±0)` + // Minimum execution time: 139_109_000 picoseconds. + Weight::from_parts(144_449_034, 6078) + // Standard Error: 26_869 + .saturating_add(Weight::from_parts(29_961_772, 0).saturating_mul(n.into())) .saturating_add(RocksDbWeight::get().reads(8_u64)) .saturating_add(RocksDbWeight::get().reads((1_u64).saturating_mul(n.into()))) .saturating_add(RocksDbWeight::get().writes(6_u64)) @@ -1438,11 +1431,11 @@ impl WeightInfo for () { fn set_attributes_pre_signed(n: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `625` - // Estimated: `20142 + n * (2921 ±0)` - // Minimum execution time: 77_694_000 picoseconds. - Weight::from_parts(90_089_732, 20142) - // Standard Error: 71_327 - .saturating_add(Weight::from_parts(27_832_189, 0).saturating_mul(n.into())) + // Estimated: `4326 + n * (2921 ±0)` + // Minimum execution time: 78_280_000 picoseconds. + Weight::from_parts(92_826_883, 4326) + // Standard Error: 81_125 + .saturating_add(Weight::from_parts(29_898_245, 0).saturating_mul(n.into())) .saturating_add(RocksDbWeight::get().reads(4_u64)) .saturating_add(RocksDbWeight::get().reads((1_u64).saturating_mul(n.into()))) .saturating_add(RocksDbWeight::get().writes(2_u64)) diff --git a/frame/nis/src/weights.rs b/frame/nis/src/weights.rs index 14b7b4d9c..4f92da874 100644 --- a/frame/nis/src/weights.rs +++ b/frame/nis/src/weights.rs @@ -18,9 +18,9 @@ //! Autogenerated weights for pallet_nis //! //! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev -//! DATE: 2023-03-16, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! DATE: 2023-04-06, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` //! WORST CASE MAP SIZE: `1000000` -//! HOSTNAME: `bm3`, CPU: `Intel(R) Core(TM) i7-7700K CPU @ 4.20GHz` +//! HOSTNAME: `bm2`, CPU: `Intel(R) Core(TM) i7-7700K CPU @ 4.20GHz` //! EXECUTION: Some(Wasm), WASM-EXECUTION: Compiled, CHAIN: Some("dev"), DB CACHE: 1024 // Executed Command: @@ -31,9 +31,6 @@ // --steps=50 // --repeat=20 // --pallet=pallet_nis -// --no-storage-info -// --no-median-slopes -// --no-min-squares // --extrinsic=* // --execution=wasm // --wasm-execution=compiled @@ -55,10 +52,10 @@ pub trait WeightInfo { fn place_bid_max() -> Weight; fn retract_bid(l: u32, ) -> Weight; fn fund_deficit() -> Weight; + fn communify() -> Weight; + fn privatize() -> Weight; fn thaw_private() -> Weight; fn thaw_communal() -> Weight; - fn privatize() -> Weight; - fn communify() -> Weight; fn process_queues() -> Weight; fn process_queue() -> Weight; fn process_bid() -> Weight; @@ -69,52 +66,52 @@ pub struct SubstrateWeight(PhantomData); impl WeightInfo for SubstrateWeight { /// Storage: Nis Queues (r:1 w:1) /// Proof: Nis Queues (max_values: None, max_size: Some(48022), added: 50497, mode: MaxEncodedLen) - /// Storage: Balances Reserves (r:1 w:1) - /// Proof: Balances Reserves (max_values: None, max_size: Some(1249), added: 3724, mode: MaxEncodedLen) + /// Storage: Balances Holds (r:1 w:1) + /// Proof: Balances Holds (max_values: None, max_size: Some(66), added: 2541, mode: MaxEncodedLen) /// Storage: Nis QueueTotals (r:1 w:1) /// Proof: Nis QueueTotals (max_values: Some(1), max_size: Some(6002), added: 6497, mode: MaxEncodedLen) /// The range of component `l` is `[0, 999]`. fn place_bid(l: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `6182 + l * (48 ±0)` - // Estimated: `63688` - // Minimum execution time: 31_160_000 picoseconds. - Weight::from_parts(39_023_414, 63688) - // Standard Error: 214 - .saturating_add(Weight::from_parts(46_106, 0).saturating_mul(l.into())) + // Measured: `6175 + l * (48 ±0)` + // Estimated: `51487` + // Minimum execution time: 49_132_000 picoseconds. + Weight::from_parts(55_373_619, 51487) + // Standard Error: 198 + .saturating_add(Weight::from_parts(44_421, 0).saturating_mul(l.into())) .saturating_add(T::DbWeight::get().reads(3_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) } /// Storage: Nis Queues (r:1 w:1) /// Proof: Nis Queues (max_values: None, max_size: Some(48022), added: 50497, mode: MaxEncodedLen) - /// Storage: Balances Reserves (r:1 w:1) - /// Proof: Balances Reserves (max_values: None, max_size: Some(1249), added: 3724, mode: MaxEncodedLen) + /// Storage: Balances Holds (r:1 w:1) + /// Proof: Balances Holds (max_values: None, max_size: Some(66), added: 2541, mode: MaxEncodedLen) /// Storage: Nis QueueTotals (r:1 w:1) /// Proof: Nis QueueTotals (max_values: Some(1), max_size: Some(6002), added: 6497, mode: MaxEncodedLen) fn place_bid_max() -> Weight { // Proof Size summary in bytes: - // Measured: `54184` - // Estimated: `63688` - // Minimum execution time: 85_703_000 picoseconds. - Weight::from_parts(86_613_000, 63688) + // Measured: `54177` + // Estimated: `51487` + // Minimum execution time: 111_471_000 picoseconds. + Weight::from_parts(112_287_000, 51487) .saturating_add(T::DbWeight::get().reads(3_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) } /// Storage: Nis Queues (r:1 w:1) /// Proof: Nis Queues (max_values: None, max_size: Some(48022), added: 50497, mode: MaxEncodedLen) + /// Storage: Balances Holds (r:1 w:1) + /// Proof: Balances Holds (max_values: None, max_size: Some(66), added: 2541, mode: MaxEncodedLen) /// Storage: Nis QueueTotals (r:1 w:1) /// Proof: Nis QueueTotals (max_values: Some(1), max_size: Some(6002), added: 6497, mode: MaxEncodedLen) - /// Storage: Balances Reserves (r:1 w:1) - /// Proof: Balances Reserves (max_values: None, max_size: Some(1249), added: 3724, mode: MaxEncodedLen) /// The range of component `l` is `[1, 1000]`. fn retract_bid(l: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `6182 + l * (48 ±0)` - // Estimated: `63688` - // Minimum execution time: 37_637_000 picoseconds. - Weight::from_parts(39_977_788, 63688) - // Standard Error: 152 - .saturating_add(Weight::from_parts(34_595, 0).saturating_mul(l.into())) + // Measured: `6175 + l * (48 ±0)` + // Estimated: `51487` + // Minimum execution time: 51_134_000 picoseconds. + Weight::from_parts(52_353_883, 51487) + // Standard Error: 161 + .saturating_add(Weight::from_parts(62_171, 0).saturating_mul(l.into())) .saturating_add(T::DbWeight::get().reads(3_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) } @@ -125,47 +122,32 @@ impl WeightInfo for SubstrateWeight { fn fund_deficit() -> Weight { // Proof Size summary in bytes: // Measured: `191` - // Estimated: `5118` - // Minimum execution time: 35_807_000 picoseconds. - Weight::from_parts(36_329_000, 5118) + // Estimated: `3593` + // Minimum execution time: 41_421_000 picoseconds. + Weight::from_parts(41_762_000, 3593) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } /// Storage: Nis Receipts (r:1 w:1) /// Proof: Nis Receipts (max_values: None, max_size: Some(81), added: 2556, mode: MaxEncodedLen) - /// Storage: Nis Summary (r:1 w:1) - /// Proof: Nis Summary (max_values: Some(1), max_size: Some(40), added: 535, mode: MaxEncodedLen) - /// Storage: System Account (r:1 w:0) + /// Storage: Balances Holds (r:1 w:1) + /// Proof: Balances Holds (max_values: None, max_size: Some(66), added: 2541, mode: MaxEncodedLen) + /// Storage: System Account (r:1 w:1) /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) - /// Storage: Balances Reserves (r:1 w:1) - /// Proof: Balances Reserves (max_values: None, max_size: Some(1249), added: 3724, mode: MaxEncodedLen) - fn thaw_private() -> Weight { - // Proof Size summary in bytes: - // Measured: `360` - // Estimated: `13378` - // Minimum execution time: 51_342_000 picoseconds. - Weight::from_parts(51_976_000, 13378) - .saturating_add(T::DbWeight::get().reads(4_u64)) - .saturating_add(T::DbWeight::get().writes(3_u64)) - } - /// Storage: Nis Receipts (r:1 w:1) - /// Proof: Nis Receipts (max_values: None, max_size: Some(81), added: 2556, mode: MaxEncodedLen) /// Storage: Nis Summary (r:1 w:1) /// Proof: Nis Summary (max_values: Some(1), max_size: Some(40), added: 535, mode: MaxEncodedLen) /// Storage: Assets Asset (r:1 w:1) /// Proof: Assets Asset (max_values: None, max_size: Some(210), added: 2685, mode: MaxEncodedLen) /// Storage: Assets Account (r:1 w:1) /// Proof: Assets Account (max_values: None, max_size: Some(102), added: 2577, mode: MaxEncodedLen) - /// Storage: System Account (r:1 w:1) - /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) - fn thaw_communal() -> Weight { + fn communify() -> Weight { // Proof Size summary in bytes: - // Measured: `773` - // Estimated: `15906` - // Minimum execution time: 76_535_000 picoseconds. - Weight::from_parts(78_073_000, 15906) - .saturating_add(T::DbWeight::get().reads(5_u64)) - .saturating_add(T::DbWeight::get().writes(5_u64)) + // Measured: `667` + // Estimated: `3675` + // Minimum execution time: 74_179_000 picoseconds. + Weight::from_parts(74_795_000, 3675) + .saturating_add(T::DbWeight::get().reads(6_u64)) + .saturating_add(T::DbWeight::get().writes(6_u64)) } /// Storage: Nis Receipts (r:1 w:1) /// Proof: Nis Receipts (max_values: None, max_size: Some(81), added: 2556, mode: MaxEncodedLen) @@ -177,37 +159,52 @@ impl WeightInfo for SubstrateWeight { /// Proof: Assets Asset (max_values: None, max_size: Some(210), added: 2685, mode: MaxEncodedLen) /// Storage: Assets Account (r:1 w:1) /// Proof: Assets Account (max_values: None, max_size: Some(102), added: 2577, mode: MaxEncodedLen) - /// Storage: Balances Reserves (r:1 w:1) - /// Proof: Balances Reserves (max_values: None, max_size: Some(1249), added: 3724, mode: MaxEncodedLen) + /// Storage: Balances Holds (r:1 w:1) + /// Proof: Balances Holds (max_values: None, max_size: Some(66), added: 2541, mode: MaxEncodedLen) fn privatize() -> Weight { // Proof Size summary in bytes: - // Measured: `835` - // Estimated: `20620` - // Minimum execution time: 84_680_000 picoseconds. - Weight::from_parts(85_661_000, 20620) + // Measured: `828` + // Estimated: `3675` + // Minimum execution time: 85_252_000 picoseconds. + Weight::from_parts(85_949_000, 3675) .saturating_add(T::DbWeight::get().reads(6_u64)) .saturating_add(T::DbWeight::get().writes(6_u64)) } /// Storage: Nis Receipts (r:1 w:1) /// Proof: Nis Receipts (max_values: None, max_size: Some(81), added: 2556, mode: MaxEncodedLen) - /// Storage: Balances Reserves (r:1 w:1) - /// Proof: Balances Reserves (max_values: None, max_size: Some(1249), added: 3724, mode: MaxEncodedLen) + /// Storage: Nis Summary (r:1 w:1) + /// Proof: Nis Summary (max_values: Some(1), max_size: Some(40), added: 535, mode: MaxEncodedLen) /// Storage: System Account (r:1 w:1) /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) + /// Storage: Balances Holds (r:1 w:1) + /// Proof: Balances Holds (max_values: None, max_size: Some(66), added: 2541, mode: MaxEncodedLen) + fn thaw_private() -> Weight { + // Proof Size summary in bytes: + // Measured: `456` + // Estimated: `3593` + // Minimum execution time: 82_100_000 picoseconds. + Weight::from_parts(82_563_000, 3593) + .saturating_add(T::DbWeight::get().reads(4_u64)) + .saturating_add(T::DbWeight::get().writes(4_u64)) + } + /// Storage: Nis Receipts (r:1 w:1) + /// Proof: Nis Receipts (max_values: None, max_size: Some(81), added: 2556, mode: MaxEncodedLen) /// Storage: Nis Summary (r:1 w:1) /// Proof: Nis Summary (max_values: Some(1), max_size: Some(40), added: 535, mode: MaxEncodedLen) /// Storage: Assets Asset (r:1 w:1) /// Proof: Assets Asset (max_values: None, max_size: Some(210), added: 2685, mode: MaxEncodedLen) /// Storage: Assets Account (r:1 w:1) /// Proof: Assets Account (max_values: None, max_size: Some(102), added: 2577, mode: MaxEncodedLen) - fn communify() -> Weight { + /// Storage: System Account (r:1 w:1) + /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) + fn thaw_communal() -> Weight { // Proof Size summary in bytes: - // Measured: `674` - // Estimated: `20620` - // Minimum execution time: 72_828_000 picoseconds. - Weight::from_parts(73_553_000, 20620) - .saturating_add(T::DbWeight::get().reads(6_u64)) - .saturating_add(T::DbWeight::get().writes(6_u64)) + // Measured: `773` + // Estimated: `3675` + // Minimum execution time: 86_498_000 picoseconds. + Weight::from_parts(87_175_000, 3675) + .saturating_add(T::DbWeight::get().reads(5_u64)) + .saturating_add(T::DbWeight::get().writes(5_u64)) } /// Storage: Nis Summary (r:1 w:1) /// Proof: Nis Summary (max_values: Some(1), max_size: Some(40), added: 535, mode: MaxEncodedLen) @@ -218,9 +215,9 @@ impl WeightInfo for SubstrateWeight { fn process_queues() -> Weight { // Proof Size summary in bytes: // Measured: `6624` - // Estimated: `12605` - // Minimum execution time: 23_311_000 picoseconds. - Weight::from_parts(23_855_000, 12605) + // Estimated: `7487` + // Minimum execution time: 22_507_000 picoseconds. + Weight::from_parts(22_788_000, 7487) .saturating_add(T::DbWeight::get().reads(3_u64)) .saturating_add(T::DbWeight::get().writes(2_u64)) } @@ -230,8 +227,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `42` // Estimated: `51487` - // Minimum execution time: 4_485_000 picoseconds. - Weight::from_parts(4_717_000, 51487) + // Minimum execution time: 4_692_000 picoseconds. + Weight::from_parts(4_862_000, 51487) .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -241,8 +238,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 7_837_000 picoseconds. - Weight::from_parts(12_913_000, 0) + // Minimum execution time: 8_031_000 picoseconds. + Weight::from_parts(8_183_000, 0) .saturating_add(T::DbWeight::get().writes(1_u64)) } } @@ -251,52 +248,52 @@ impl WeightInfo for SubstrateWeight { impl WeightInfo for () { /// Storage: Nis Queues (r:1 w:1) /// Proof: Nis Queues (max_values: None, max_size: Some(48022), added: 50497, mode: MaxEncodedLen) - /// Storage: Balances Reserves (r:1 w:1) - /// Proof: Balances Reserves (max_values: None, max_size: Some(1249), added: 3724, mode: MaxEncodedLen) + /// Storage: Balances Holds (r:1 w:1) + /// Proof: Balances Holds (max_values: None, max_size: Some(66), added: 2541, mode: MaxEncodedLen) /// Storage: Nis QueueTotals (r:1 w:1) /// Proof: Nis QueueTotals (max_values: Some(1), max_size: Some(6002), added: 6497, mode: MaxEncodedLen) /// The range of component `l` is `[0, 999]`. fn place_bid(l: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `6182 + l * (48 ±0)` - // Estimated: `63688` - // Minimum execution time: 31_160_000 picoseconds. - Weight::from_parts(39_023_414, 63688) - // Standard Error: 214 - .saturating_add(Weight::from_parts(46_106, 0).saturating_mul(l.into())) + // Measured: `6175 + l * (48 ±0)` + // Estimated: `51487` + // Minimum execution time: 49_132_000 picoseconds. + Weight::from_parts(55_373_619, 51487) + // Standard Error: 198 + .saturating_add(Weight::from_parts(44_421, 0).saturating_mul(l.into())) .saturating_add(RocksDbWeight::get().reads(3_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) } /// Storage: Nis Queues (r:1 w:1) /// Proof: Nis Queues (max_values: None, max_size: Some(48022), added: 50497, mode: MaxEncodedLen) - /// Storage: Balances Reserves (r:1 w:1) - /// Proof: Balances Reserves (max_values: None, max_size: Some(1249), added: 3724, mode: MaxEncodedLen) + /// Storage: Balances Holds (r:1 w:1) + /// Proof: Balances Holds (max_values: None, max_size: Some(66), added: 2541, mode: MaxEncodedLen) /// Storage: Nis QueueTotals (r:1 w:1) /// Proof: Nis QueueTotals (max_values: Some(1), max_size: Some(6002), added: 6497, mode: MaxEncodedLen) fn place_bid_max() -> Weight { // Proof Size summary in bytes: - // Measured: `54184` - // Estimated: `63688` - // Minimum execution time: 85_703_000 picoseconds. - Weight::from_parts(86_613_000, 63688) + // Measured: `54177` + // Estimated: `51487` + // Minimum execution time: 111_471_000 picoseconds. + Weight::from_parts(112_287_000, 51487) .saturating_add(RocksDbWeight::get().reads(3_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) } /// Storage: Nis Queues (r:1 w:1) /// Proof: Nis Queues (max_values: None, max_size: Some(48022), added: 50497, mode: MaxEncodedLen) + /// Storage: Balances Holds (r:1 w:1) + /// Proof: Balances Holds (max_values: None, max_size: Some(66), added: 2541, mode: MaxEncodedLen) /// Storage: Nis QueueTotals (r:1 w:1) /// Proof: Nis QueueTotals (max_values: Some(1), max_size: Some(6002), added: 6497, mode: MaxEncodedLen) - /// Storage: Balances Reserves (r:1 w:1) - /// Proof: Balances Reserves (max_values: None, max_size: Some(1249), added: 3724, mode: MaxEncodedLen) /// The range of component `l` is `[1, 1000]`. fn retract_bid(l: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `6182 + l * (48 ±0)` - // Estimated: `63688` - // Minimum execution time: 37_637_000 picoseconds. - Weight::from_parts(39_977_788, 63688) - // Standard Error: 152 - .saturating_add(Weight::from_parts(34_595, 0).saturating_mul(l.into())) + // Measured: `6175 + l * (48 ±0)` + // Estimated: `51487` + // Minimum execution time: 51_134_000 picoseconds. + Weight::from_parts(52_353_883, 51487) + // Standard Error: 161 + .saturating_add(Weight::from_parts(62_171, 0).saturating_mul(l.into())) .saturating_add(RocksDbWeight::get().reads(3_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) } @@ -307,47 +304,32 @@ impl WeightInfo for () { fn fund_deficit() -> Weight { // Proof Size summary in bytes: // Measured: `191` - // Estimated: `5118` - // Minimum execution time: 35_807_000 picoseconds. - Weight::from_parts(36_329_000, 5118) + // Estimated: `3593` + // Minimum execution time: 41_421_000 picoseconds. + Weight::from_parts(41_762_000, 3593) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } /// Storage: Nis Receipts (r:1 w:1) /// Proof: Nis Receipts (max_values: None, max_size: Some(81), added: 2556, mode: MaxEncodedLen) - /// Storage: Nis Summary (r:1 w:1) - /// Proof: Nis Summary (max_values: Some(1), max_size: Some(40), added: 535, mode: MaxEncodedLen) - /// Storage: System Account (r:1 w:0) + /// Storage: Balances Holds (r:1 w:1) + /// Proof: Balances Holds (max_values: None, max_size: Some(66), added: 2541, mode: MaxEncodedLen) + /// Storage: System Account (r:1 w:1) /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) - /// Storage: Balances Reserves (r:1 w:1) - /// Proof: Balances Reserves (max_values: None, max_size: Some(1249), added: 3724, mode: MaxEncodedLen) - fn thaw_private() -> Weight { - // Proof Size summary in bytes: - // Measured: `360` - // Estimated: `13378` - // Minimum execution time: 51_342_000 picoseconds. - Weight::from_parts(51_976_000, 13378) - .saturating_add(RocksDbWeight::get().reads(4_u64)) - .saturating_add(RocksDbWeight::get().writes(3_u64)) - } - /// Storage: Nis Receipts (r:1 w:1) - /// Proof: Nis Receipts (max_values: None, max_size: Some(81), added: 2556, mode: MaxEncodedLen) /// Storage: Nis Summary (r:1 w:1) /// Proof: Nis Summary (max_values: Some(1), max_size: Some(40), added: 535, mode: MaxEncodedLen) /// Storage: Assets Asset (r:1 w:1) /// Proof: Assets Asset (max_values: None, max_size: Some(210), added: 2685, mode: MaxEncodedLen) /// Storage: Assets Account (r:1 w:1) /// Proof: Assets Account (max_values: None, max_size: Some(102), added: 2577, mode: MaxEncodedLen) - /// Storage: System Account (r:1 w:1) - /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) - fn thaw_communal() -> Weight { + fn communify() -> Weight { // Proof Size summary in bytes: - // Measured: `773` - // Estimated: `15906` - // Minimum execution time: 76_535_000 picoseconds. - Weight::from_parts(78_073_000, 15906) - .saturating_add(RocksDbWeight::get().reads(5_u64)) - .saturating_add(RocksDbWeight::get().writes(5_u64)) + // Measured: `667` + // Estimated: `3675` + // Minimum execution time: 74_179_000 picoseconds. + Weight::from_parts(74_795_000, 3675) + .saturating_add(RocksDbWeight::get().reads(6_u64)) + .saturating_add(RocksDbWeight::get().writes(6_u64)) } /// Storage: Nis Receipts (r:1 w:1) /// Proof: Nis Receipts (max_values: None, max_size: Some(81), added: 2556, mode: MaxEncodedLen) @@ -359,37 +341,52 @@ impl WeightInfo for () { /// Proof: Assets Asset (max_values: None, max_size: Some(210), added: 2685, mode: MaxEncodedLen) /// Storage: Assets Account (r:1 w:1) /// Proof: Assets Account (max_values: None, max_size: Some(102), added: 2577, mode: MaxEncodedLen) - /// Storage: Balances Reserves (r:1 w:1) - /// Proof: Balances Reserves (max_values: None, max_size: Some(1249), added: 3724, mode: MaxEncodedLen) + /// Storage: Balances Holds (r:1 w:1) + /// Proof: Balances Holds (max_values: None, max_size: Some(66), added: 2541, mode: MaxEncodedLen) fn privatize() -> Weight { // Proof Size summary in bytes: - // Measured: `835` - // Estimated: `20620` - // Minimum execution time: 84_680_000 picoseconds. - Weight::from_parts(85_661_000, 20620) + // Measured: `828` + // Estimated: `3675` + // Minimum execution time: 85_252_000 picoseconds. + Weight::from_parts(85_949_000, 3675) .saturating_add(RocksDbWeight::get().reads(6_u64)) .saturating_add(RocksDbWeight::get().writes(6_u64)) } /// Storage: Nis Receipts (r:1 w:1) /// Proof: Nis Receipts (max_values: None, max_size: Some(81), added: 2556, mode: MaxEncodedLen) - /// Storage: Balances Reserves (r:1 w:1) - /// Proof: Balances Reserves (max_values: None, max_size: Some(1249), added: 3724, mode: MaxEncodedLen) + /// Storage: Nis Summary (r:1 w:1) + /// Proof: Nis Summary (max_values: Some(1), max_size: Some(40), added: 535, mode: MaxEncodedLen) /// Storage: System Account (r:1 w:1) /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) + /// Storage: Balances Holds (r:1 w:1) + /// Proof: Balances Holds (max_values: None, max_size: Some(66), added: 2541, mode: MaxEncodedLen) + fn thaw_private() -> Weight { + // Proof Size summary in bytes: + // Measured: `456` + // Estimated: `3593` + // Minimum execution time: 82_100_000 picoseconds. + Weight::from_parts(82_563_000, 3593) + .saturating_add(RocksDbWeight::get().reads(4_u64)) + .saturating_add(RocksDbWeight::get().writes(4_u64)) + } + /// Storage: Nis Receipts (r:1 w:1) + /// Proof: Nis Receipts (max_values: None, max_size: Some(81), added: 2556, mode: MaxEncodedLen) /// Storage: Nis Summary (r:1 w:1) /// Proof: Nis Summary (max_values: Some(1), max_size: Some(40), added: 535, mode: MaxEncodedLen) /// Storage: Assets Asset (r:1 w:1) /// Proof: Assets Asset (max_values: None, max_size: Some(210), added: 2685, mode: MaxEncodedLen) /// Storage: Assets Account (r:1 w:1) /// Proof: Assets Account (max_values: None, max_size: Some(102), added: 2577, mode: MaxEncodedLen) - fn communify() -> Weight { + /// Storage: System Account (r:1 w:1) + /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) + fn thaw_communal() -> Weight { // Proof Size summary in bytes: - // Measured: `674` - // Estimated: `20620` - // Minimum execution time: 72_828_000 picoseconds. - Weight::from_parts(73_553_000, 20620) - .saturating_add(RocksDbWeight::get().reads(6_u64)) - .saturating_add(RocksDbWeight::get().writes(6_u64)) + // Measured: `773` + // Estimated: `3675` + // Minimum execution time: 86_498_000 picoseconds. + Weight::from_parts(87_175_000, 3675) + .saturating_add(RocksDbWeight::get().reads(5_u64)) + .saturating_add(RocksDbWeight::get().writes(5_u64)) } /// Storage: Nis Summary (r:1 w:1) /// Proof: Nis Summary (max_values: Some(1), max_size: Some(40), added: 535, mode: MaxEncodedLen) @@ -400,9 +397,9 @@ impl WeightInfo for () { fn process_queues() -> Weight { // Proof Size summary in bytes: // Measured: `6624` - // Estimated: `12605` - // Minimum execution time: 23_311_000 picoseconds. - Weight::from_parts(23_855_000, 12605) + // Estimated: `7487` + // Minimum execution time: 22_507_000 picoseconds. + Weight::from_parts(22_788_000, 7487) .saturating_add(RocksDbWeight::get().reads(3_u64)) .saturating_add(RocksDbWeight::get().writes(2_u64)) } @@ -412,8 +409,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `42` // Estimated: `51487` - // Minimum execution time: 4_485_000 picoseconds. - Weight::from_parts(4_717_000, 51487) + // Minimum execution time: 4_692_000 picoseconds. + Weight::from_parts(4_862_000, 51487) .saturating_add(RocksDbWeight::get().reads(1_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } @@ -423,8 +420,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 7_837_000 picoseconds. - Weight::from_parts(12_913_000, 0) + // Minimum execution time: 8_031_000 picoseconds. + Weight::from_parts(8_183_000, 0) .saturating_add(RocksDbWeight::get().writes(1_u64)) } } diff --git a/frame/nomination-pools/src/weights.rs b/frame/nomination-pools/src/weights.rs index 003ba93eb..0ac50d2f7 100644 --- a/frame/nomination-pools/src/weights.rs +++ b/frame/nomination-pools/src/weights.rs @@ -18,9 +18,9 @@ //! Autogenerated weights for pallet_nomination_pools //! //! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev -//! DATE: 2023-03-16, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! DATE: 2023-04-06, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` //! WORST CASE MAP SIZE: `1000000` -//! HOSTNAME: `bm3`, CPU: `Intel(R) Core(TM) i7-7700K CPU @ 4.20GHz` +//! HOSTNAME: `bm2`, CPU: `Intel(R) Core(TM) i7-7700K CPU @ 4.20GHz` //! EXECUTION: Some(Wasm), WASM-EXECUTION: Compiled, CHAIN: Some("dev"), DB CACHE: 1024 // Executed Command: @@ -31,9 +31,6 @@ // --steps=50 // --repeat=20 // --pallet=pallet_nomination_pools -// --no-storage-info -// --no-median-slopes -// --no-min-squares // --extrinsic=* // --execution=wasm // --wasm-execution=compiled @@ -100,6 +97,8 @@ impl WeightInfo for SubstrateWeight { /// Proof: NominationPools CounterForPoolMembers (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) /// Storage: Balances Locks (r:1 w:1) /// Proof: Balances Locks (max_values: None, max_size: Some(1299), added: 3774, mode: MaxEncodedLen) + /// Storage: Balances Freezes (r:1 w:0) + /// Proof: Balances Freezes (max_values: None, max_size: Some(49), added: 2524, mode: MaxEncodedLen) /// Storage: VoterList ListNodes (r:3 w:3) /// Proof: VoterList ListNodes (max_values: None, max_size: Some(154), added: 2629, mode: MaxEncodedLen) /// Storage: VoterList ListBags (r:2 w:2) @@ -107,10 +106,10 @@ impl WeightInfo for SubstrateWeight { fn join() -> Weight { // Proof Size summary in bytes: // Measured: `3300` - // Estimated: `52435` - // Minimum execution time: 160_675_000 picoseconds. - Weight::from_parts(162_605_000, 52435) - .saturating_add(T::DbWeight::get().reads(18_u64)) + // Estimated: `8877` + // Minimum execution time: 186_523_000 picoseconds. + Weight::from_parts(187_817_000, 8877) + .saturating_add(T::DbWeight::get().reads(19_u64)) .saturating_add(T::DbWeight::get().writes(12_u64)) } /// Storage: NominationPools PoolMembers (r:1 w:1) @@ -129,6 +128,8 @@ impl WeightInfo for SubstrateWeight { /// Proof: Staking Ledger (max_values: None, max_size: Some(1091), added: 3566, mode: MaxEncodedLen) /// Storage: Balances Locks (r:1 w:1) /// Proof: Balances Locks (max_values: None, max_size: Some(1299), added: 3774, mode: MaxEncodedLen) + /// Storage: Balances Freezes (r:1 w:0) + /// Proof: Balances Freezes (max_values: None, max_size: Some(49), added: 2524, mode: MaxEncodedLen) /// Storage: VoterList ListNodes (r:3 w:3) /// Proof: VoterList ListNodes (max_values: None, max_size: Some(154), added: 2629, mode: MaxEncodedLen) /// Storage: VoterList ListBags (r:2 w:2) @@ -136,10 +137,10 @@ impl WeightInfo for SubstrateWeight { fn bond_extra_transfer() -> Weight { // Proof Size summary in bytes: // Measured: `3310` - // Estimated: `49070` - // Minimum execution time: 156_820_000 picoseconds. - Weight::from_parts(159_798_000, 49070) - .saturating_add(T::DbWeight::get().reads(15_u64)) + // Estimated: `8877` + // Minimum execution time: 183_120_000 picoseconds. + Weight::from_parts(184_749_000, 8877) + .saturating_add(T::DbWeight::get().reads(16_u64)) .saturating_add(T::DbWeight::get().writes(12_u64)) } /// Storage: NominationPools ClaimPermissions (r:1 w:0) @@ -160,6 +161,8 @@ impl WeightInfo for SubstrateWeight { /// Proof: Staking Ledger (max_values: None, max_size: Some(1091), added: 3566, mode: MaxEncodedLen) /// Storage: Balances Locks (r:1 w:1) /// Proof: Balances Locks (max_values: None, max_size: Some(1299), added: 3774, mode: MaxEncodedLen) + /// Storage: Balances Freezes (r:1 w:0) + /// Proof: Balances Freezes (max_values: None, max_size: Some(49), added: 2524, mode: MaxEncodedLen) /// Storage: VoterList ListNodes (r:3 w:3) /// Proof: VoterList ListNodes (max_values: None, max_size: Some(154), added: 2629, mode: MaxEncodedLen) /// Storage: VoterList ListBags (r:2 w:2) @@ -167,10 +170,10 @@ impl WeightInfo for SubstrateWeight { fn bond_extra_other() -> Weight { // Proof Size summary in bytes: // Measured: `3375` - // Estimated: `52576` - // Minimum execution time: 175_820_000 picoseconds. - Weight::from_parts(177_378_000, 52576) - .saturating_add(T::DbWeight::get().reads(16_u64)) + // Estimated: `8877` + // Minimum execution time: 218_799_000 picoseconds. + Weight::from_parts(220_139_000, 8877) + .saturating_add(T::DbWeight::get().reads(17_u64)) .saturating_add(T::DbWeight::get().writes(13_u64)) } /// Storage: NominationPools ClaimPermissions (r:1 w:0) @@ -188,9 +191,9 @@ impl WeightInfo for SubstrateWeight { fn claim_payout() -> Weight { // Proof Size summary in bytes: // Measured: `1171` - // Estimated: `19532` - // Minimum execution time: 62_011_000 picoseconds. - Weight::from_parts(62_680_000, 19532) + // Estimated: `3702` + // Minimum execution time: 79_493_000 picoseconds. + Weight::from_parts(80_266_000, 3702) .saturating_add(T::DbWeight::get().reads(6_u64)) .saturating_add(T::DbWeight::get().writes(4_u64)) } @@ -216,6 +219,8 @@ impl WeightInfo for SubstrateWeight { /// Proof: Staking MinNominatorBond (max_values: Some(1), max_size: Some(16), added: 511, mode: MaxEncodedLen) /// Storage: Balances Locks (r:1 w:1) /// Proof: Balances Locks (max_values: None, max_size: Some(1299), added: 3774, mode: MaxEncodedLen) + /// Storage: Balances Freezes (r:1 w:0) + /// Proof: Balances Freezes (max_values: None, max_size: Some(49), added: 2524, mode: MaxEncodedLen) /// Storage: VoterList ListNodes (r:3 w:3) /// Proof: VoterList ListNodes (max_values: None, max_size: Some(154), added: 2629, mode: MaxEncodedLen) /// Storage: VoterList ListBags (r:2 w:2) @@ -227,10 +232,10 @@ impl WeightInfo for SubstrateWeight { fn unbond() -> Weight { // Proof Size summary in bytes: // Measured: `3586` - // Estimated: `82816` - // Minimum execution time: 163_294_000 picoseconds. - Weight::from_parts(164_375_000, 82816) - .saturating_add(T::DbWeight::get().reads(19_u64)) + // Estimated: `27847` + // Minimum execution time: 168_592_000 picoseconds. + Weight::from_parts(170_130_000, 27847) + .saturating_add(T::DbWeight::get().reads(20_u64)) .saturating_add(T::DbWeight::get().writes(13_u64)) } /// Storage: NominationPools BondedPools (r:1 w:0) @@ -243,16 +248,18 @@ impl WeightInfo for SubstrateWeight { /// Proof: Staking CurrentEra (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) /// Storage: Balances Locks (r:1 w:1) /// Proof: Balances Locks (max_values: None, max_size: Some(1299), added: 3774, mode: MaxEncodedLen) + /// Storage: Balances Freezes (r:1 w:0) + /// Proof: Balances Freezes (max_values: None, max_size: Some(49), added: 2524, mode: MaxEncodedLen) /// The range of component `s` is `[0, 100]`. fn pool_withdraw_unbonded(s: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `1687` - // Estimated: `18031` - // Minimum execution time: 54_775_000 picoseconds. - Weight::from_parts(55_724_944, 18031) - // Standard Error: 536 - .saturating_add(Weight::from_parts(10_059, 0).saturating_mul(s.into())) - .saturating_add(T::DbWeight::get().reads(5_u64)) + // Estimated: `4764` + // Minimum execution time: 63_254_000 picoseconds. + Weight::from_parts(64_154_755, 4764) + // Standard Error: 344 + .saturating_add(Weight::from_parts(8_798, 0).saturating_mul(s.into())) + .saturating_add(T::DbWeight::get().reads(6_u64)) .saturating_add(T::DbWeight::get().writes(2_u64)) } /// Storage: NominationPools PoolMembers (r:1 w:1) @@ -269,6 +276,8 @@ impl WeightInfo for SubstrateWeight { /// Proof: Staking Ledger (max_values: None, max_size: Some(1091), added: 3566, mode: MaxEncodedLen) /// Storage: Balances Locks (r:1 w:1) /// Proof: Balances Locks (max_values: None, max_size: Some(1299), added: 3774, mode: MaxEncodedLen) + /// Storage: Balances Freezes (r:1 w:0) + /// Proof: Balances Freezes (max_values: None, max_size: Some(49), added: 2524, mode: MaxEncodedLen) /// Storage: System Account (r:1 w:1) /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) /// Storage: NominationPools CounterForPoolMembers (r:1 w:1) @@ -279,12 +288,12 @@ impl WeightInfo for SubstrateWeight { fn withdraw_unbonded_update(s: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `2115` - // Estimated: `54662` - // Minimum execution time: 106_820_000 picoseconds. - Weight::from_parts(109_870_849, 54662) - // Standard Error: 11_111 - .saturating_add(Weight::from_parts(2_006, 0).saturating_mul(s.into())) - .saturating_add(T::DbWeight::get().reads(9_u64)) + // Estimated: `27847` + // Minimum execution time: 131_339_000 picoseconds. + Weight::from_parts(133_590_267, 27847) + // Standard Error: 1_058 + .saturating_add(Weight::from_parts(9_932, 0).saturating_mul(s.into())) + .saturating_add(T::DbWeight::get().reads(10_u64)) .saturating_add(T::DbWeight::get().writes(8_u64)) } /// Storage: NominationPools PoolMembers (r:1 w:1) @@ -309,6 +318,8 @@ impl WeightInfo for SubstrateWeight { /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) /// Storage: Balances Locks (r:1 w:1) /// Proof: Balances Locks (max_values: None, max_size: Some(1299), added: 3774, mode: MaxEncodedLen) + /// Storage: Balances Freezes (r:1 w:0) + /// Proof: Balances Freezes (max_values: None, max_size: Some(49), added: 2524, mode: MaxEncodedLen) /// Storage: NominationPools CounterForPoolMembers (r:1 w:1) /// Proof: NominationPools CounterForPoolMembers (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) /// Storage: NominationPools ReversePoolIdLookup (r:1 w:1) @@ -330,15 +341,13 @@ impl WeightInfo for SubstrateWeight { /// Storage: NominationPools ClaimPermissions (r:0 w:1) /// Proof: NominationPools ClaimPermissions (max_values: None, max_size: Some(41), added: 2516, mode: MaxEncodedLen) /// The range of component `s` is `[0, 100]`. - fn withdraw_unbonded_kill(s: u32, ) -> Weight { + fn withdraw_unbonded_kill(_s: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `2470` - // Estimated: `87490` - // Minimum execution time: 170_908_000 picoseconds. - Weight::from_parts(173_616_555, 87490) - // Standard Error: 2_324 - .saturating_add(Weight::from_parts(4_397, 0).saturating_mul(s.into())) - .saturating_add(T::DbWeight::get().reads(20_u64)) + // Estimated: `27847` + // Minimum execution time: 219_026_000 picoseconds. + Weight::from_parts(223_230_356, 27847) + .saturating_add(T::DbWeight::get().reads(21_u64)) .saturating_add(T::DbWeight::get().writes(18_u64)) } /// Storage: NominationPools LastPoolId (r:1 w:1) @@ -371,6 +380,8 @@ impl WeightInfo for SubstrateWeight { /// Proof: Staking CurrentEra (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) /// Storage: Balances Locks (r:1 w:1) /// Proof: Balances Locks (max_values: None, max_size: Some(1299), added: 3774, mode: MaxEncodedLen) + /// Storage: Balances Freezes (r:1 w:0) + /// Proof: Balances Freezes (max_values: None, max_size: Some(49), added: 2524, mode: MaxEncodedLen) /// Storage: NominationPools RewardPools (r:1 w:1) /// Proof: NominationPools RewardPools (max_values: None, max_size: Some(92), added: 2567, mode: MaxEncodedLen) /// Storage: NominationPools CounterForRewardPools (r:1 w:1) @@ -386,10 +397,10 @@ impl WeightInfo for SubstrateWeight { fn create() -> Weight { // Proof Size summary in bytes: // Measured: `1289` - // Estimated: `51410` - // Minimum execution time: 148_934_000 picoseconds. - Weight::from_parts(149_702_000, 51410) - .saturating_add(T::DbWeight::get().reads(21_u64)) + // Estimated: `6196` + // Minimum execution time: 189_710_000 picoseconds. + Weight::from_parts(190_251_000, 6196) + .saturating_add(T::DbWeight::get().reads(22_u64)) .saturating_add(T::DbWeight::get().writes(15_u64)) } /// Storage: NominationPools BondedPools (r:1 w:0) @@ -420,11 +431,11 @@ impl WeightInfo for SubstrateWeight { fn nominate(n: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `1849` - // Estimated: `33934 + n * (2520 ±0)` - // Minimum execution time: 68_986_000 picoseconds. - Weight::from_parts(69_474_269, 33934) - // Standard Error: 6_602 - .saturating_add(Weight::from_parts(1_416_294, 0).saturating_mul(n.into())) + // Estimated: `4556 + n * (2520 ±0)` + // Minimum execution time: 70_044_000 picoseconds. + Weight::from_parts(69_307_256, 4556) + // Standard Error: 6_066 + .saturating_add(Weight::from_parts(1_517_942, 0).saturating_mul(n.into())) .saturating_add(T::DbWeight::get().reads(12_u64)) .saturating_add(T::DbWeight::get().reads((1_u64).saturating_mul(n.into()))) .saturating_add(T::DbWeight::get().writes(5_u64)) @@ -439,9 +450,9 @@ impl WeightInfo for SubstrateWeight { fn set_state() -> Weight { // Proof Size summary in bytes: // Measured: `1438` - // Estimated: `11778` - // Minimum execution time: 36_300_000 picoseconds. - Weight::from_parts(36_713_000, 11778) + // Estimated: `4556` + // Minimum execution time: 36_610_000 picoseconds. + Weight::from_parts(37_212_000, 4556) .saturating_add(T::DbWeight::get().reads(3_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -455,11 +466,11 @@ impl WeightInfo for SubstrateWeight { fn set_metadata(n: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `531` - // Estimated: `8909` - // Minimum execution time: 14_976_000 picoseconds. - Weight::from_parts(15_538_088, 8909) - // Standard Error: 71 - .saturating_add(Weight::from_parts(1_548, 0).saturating_mul(n.into())) + // Estimated: `3735` + // Minimum execution time: 15_334_000 picoseconds. + Weight::from_parts(15_753_107, 3735) + // Standard Error: 62 + .saturating_add(Weight::from_parts(1_365, 0).saturating_mul(n.into())) .saturating_add(T::DbWeight::get().reads(3_u64)) .saturating_add(T::DbWeight::get().writes(2_u64)) } @@ -479,8 +490,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 7_259_000 picoseconds. - Weight::from_parts(7_499_000, 0) + // Minimum execution time: 7_156_000 picoseconds. + Weight::from_parts(7_596_000, 0) .saturating_add(T::DbWeight::get().writes(6_u64)) } /// Storage: NominationPools BondedPools (r:1 w:1) @@ -489,8 +500,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `531` // Estimated: `3685` - // Minimum execution time: 20_454_000 picoseconds. - Weight::from_parts(20_771_000, 3685) + // Minimum execution time: 20_342_000 picoseconds. + Weight::from_parts(20_699_000, 3685) .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -515,9 +526,9 @@ impl WeightInfo for SubstrateWeight { fn chill() -> Weight { // Proof Size summary in bytes: // Measured: `2012` - // Estimated: `29455` - // Minimum execution time: 66_404_000 picoseconds. - Weight::from_parts(66_872_000, 29455) + // Estimated: `4556` + // Minimum execution time: 66_608_000 picoseconds. + Weight::from_parts(67_416_000, 4556) .saturating_add(T::DbWeight::get().reads(9_u64)) .saturating_add(T::DbWeight::get().writes(5_u64)) } @@ -532,9 +543,9 @@ impl WeightInfo for SubstrateWeight { fn set_commission() -> Weight { // Proof Size summary in bytes: // Measured: `770` - // Estimated: `12324` - // Minimum execution time: 34_240_000 picoseconds. - Weight::from_parts(34_797_000, 12324) + // Estimated: `3685` + // Minimum execution time: 34_619_000 picoseconds. + Weight::from_parts(34_894_000, 3685) .saturating_add(T::DbWeight::get().reads(4_u64)) .saturating_add(T::DbWeight::get().writes(2_u64)) } @@ -544,8 +555,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `571` // Estimated: `3685` - // Minimum execution time: 19_469_000 picoseconds. - Weight::from_parts(19_865_000, 3685) + // Minimum execution time: 19_676_000 picoseconds. + Weight::from_parts(19_958_000, 3685) .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -555,8 +566,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `531` // Estimated: `3685` - // Minimum execution time: 20_423_000 picoseconds. - Weight::from_parts(20_620_000, 3685) + // Minimum execution time: 20_404_000 picoseconds. + Weight::from_parts(20_859_000, 3685) .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -567,9 +578,9 @@ impl WeightInfo for SubstrateWeight { fn set_claim_permission() -> Weight { // Proof Size summary in bytes: // Measured: `542` - // Estimated: `7208` - // Minimum execution time: 15_346_000 picoseconds. - Weight::from_parts(15_613_000, 7208) + // Estimated: `3702` + // Minimum execution time: 15_401_000 picoseconds. + Weight::from_parts(15_706_000, 3702) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -584,9 +595,9 @@ impl WeightInfo for SubstrateWeight { fn claim_commission() -> Weight { // Proof Size summary in bytes: // Measured: `968` - // Estimated: `12324` - // Minimum execution time: 48_749_000 picoseconds. - Weight::from_parts(49_131_000, 12324) + // Estimated: `3685` + // Minimum execution time: 66_775_000 picoseconds. + Weight::from_parts(67_242_000, 3685) .saturating_add(T::DbWeight::get().reads(4_u64)) .saturating_add(T::DbWeight::get().writes(2_u64)) } @@ -618,6 +629,8 @@ impl WeightInfo for () { /// Proof: NominationPools CounterForPoolMembers (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) /// Storage: Balances Locks (r:1 w:1) /// Proof: Balances Locks (max_values: None, max_size: Some(1299), added: 3774, mode: MaxEncodedLen) + /// Storage: Balances Freezes (r:1 w:0) + /// Proof: Balances Freezes (max_values: None, max_size: Some(49), added: 2524, mode: MaxEncodedLen) /// Storage: VoterList ListNodes (r:3 w:3) /// Proof: VoterList ListNodes (max_values: None, max_size: Some(154), added: 2629, mode: MaxEncodedLen) /// Storage: VoterList ListBags (r:2 w:2) @@ -625,10 +638,10 @@ impl WeightInfo for () { fn join() -> Weight { // Proof Size summary in bytes: // Measured: `3300` - // Estimated: `52435` - // Minimum execution time: 160_675_000 picoseconds. - Weight::from_parts(162_605_000, 52435) - .saturating_add(RocksDbWeight::get().reads(18_u64)) + // Estimated: `8877` + // Minimum execution time: 186_523_000 picoseconds. + Weight::from_parts(187_817_000, 8877) + .saturating_add(RocksDbWeight::get().reads(19_u64)) .saturating_add(RocksDbWeight::get().writes(12_u64)) } /// Storage: NominationPools PoolMembers (r:1 w:1) @@ -647,6 +660,8 @@ impl WeightInfo for () { /// Proof: Staking Ledger (max_values: None, max_size: Some(1091), added: 3566, mode: MaxEncodedLen) /// Storage: Balances Locks (r:1 w:1) /// Proof: Balances Locks (max_values: None, max_size: Some(1299), added: 3774, mode: MaxEncodedLen) + /// Storage: Balances Freezes (r:1 w:0) + /// Proof: Balances Freezes (max_values: None, max_size: Some(49), added: 2524, mode: MaxEncodedLen) /// Storage: VoterList ListNodes (r:3 w:3) /// Proof: VoterList ListNodes (max_values: None, max_size: Some(154), added: 2629, mode: MaxEncodedLen) /// Storage: VoterList ListBags (r:2 w:2) @@ -654,10 +669,10 @@ impl WeightInfo for () { fn bond_extra_transfer() -> Weight { // Proof Size summary in bytes: // Measured: `3310` - // Estimated: `49070` - // Minimum execution time: 156_820_000 picoseconds. - Weight::from_parts(159_798_000, 49070) - .saturating_add(RocksDbWeight::get().reads(15_u64)) + // Estimated: `8877` + // Minimum execution time: 183_120_000 picoseconds. + Weight::from_parts(184_749_000, 8877) + .saturating_add(RocksDbWeight::get().reads(16_u64)) .saturating_add(RocksDbWeight::get().writes(12_u64)) } /// Storage: NominationPools ClaimPermissions (r:1 w:0) @@ -678,6 +693,8 @@ impl WeightInfo for () { /// Proof: Staking Ledger (max_values: None, max_size: Some(1091), added: 3566, mode: MaxEncodedLen) /// Storage: Balances Locks (r:1 w:1) /// Proof: Balances Locks (max_values: None, max_size: Some(1299), added: 3774, mode: MaxEncodedLen) + /// Storage: Balances Freezes (r:1 w:0) + /// Proof: Balances Freezes (max_values: None, max_size: Some(49), added: 2524, mode: MaxEncodedLen) /// Storage: VoterList ListNodes (r:3 w:3) /// Proof: VoterList ListNodes (max_values: None, max_size: Some(154), added: 2629, mode: MaxEncodedLen) /// Storage: VoterList ListBags (r:2 w:2) @@ -685,10 +702,10 @@ impl WeightInfo for () { fn bond_extra_other() -> Weight { // Proof Size summary in bytes: // Measured: `3375` - // Estimated: `52576` - // Minimum execution time: 175_820_000 picoseconds. - Weight::from_parts(177_378_000, 52576) - .saturating_add(RocksDbWeight::get().reads(16_u64)) + // Estimated: `8877` + // Minimum execution time: 218_799_000 picoseconds. + Weight::from_parts(220_139_000, 8877) + .saturating_add(RocksDbWeight::get().reads(17_u64)) .saturating_add(RocksDbWeight::get().writes(13_u64)) } /// Storage: NominationPools ClaimPermissions (r:1 w:0) @@ -706,9 +723,9 @@ impl WeightInfo for () { fn claim_payout() -> Weight { // Proof Size summary in bytes: // Measured: `1171` - // Estimated: `19532` - // Minimum execution time: 62_011_000 picoseconds. - Weight::from_parts(62_680_000, 19532) + // Estimated: `3702` + // Minimum execution time: 79_493_000 picoseconds. + Weight::from_parts(80_266_000, 3702) .saturating_add(RocksDbWeight::get().reads(6_u64)) .saturating_add(RocksDbWeight::get().writes(4_u64)) } @@ -734,6 +751,8 @@ impl WeightInfo for () { /// Proof: Staking MinNominatorBond (max_values: Some(1), max_size: Some(16), added: 511, mode: MaxEncodedLen) /// Storage: Balances Locks (r:1 w:1) /// Proof: Balances Locks (max_values: None, max_size: Some(1299), added: 3774, mode: MaxEncodedLen) + /// Storage: Balances Freezes (r:1 w:0) + /// Proof: Balances Freezes (max_values: None, max_size: Some(49), added: 2524, mode: MaxEncodedLen) /// Storage: VoterList ListNodes (r:3 w:3) /// Proof: VoterList ListNodes (max_values: None, max_size: Some(154), added: 2629, mode: MaxEncodedLen) /// Storage: VoterList ListBags (r:2 w:2) @@ -745,10 +764,10 @@ impl WeightInfo for () { fn unbond() -> Weight { // Proof Size summary in bytes: // Measured: `3586` - // Estimated: `82816` - // Minimum execution time: 163_294_000 picoseconds. - Weight::from_parts(164_375_000, 82816) - .saturating_add(RocksDbWeight::get().reads(19_u64)) + // Estimated: `27847` + // Minimum execution time: 168_592_000 picoseconds. + Weight::from_parts(170_130_000, 27847) + .saturating_add(RocksDbWeight::get().reads(20_u64)) .saturating_add(RocksDbWeight::get().writes(13_u64)) } /// Storage: NominationPools BondedPools (r:1 w:0) @@ -761,16 +780,18 @@ impl WeightInfo for () { /// Proof: Staking CurrentEra (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) /// Storage: Balances Locks (r:1 w:1) /// Proof: Balances Locks (max_values: None, max_size: Some(1299), added: 3774, mode: MaxEncodedLen) + /// Storage: Balances Freezes (r:1 w:0) + /// Proof: Balances Freezes (max_values: None, max_size: Some(49), added: 2524, mode: MaxEncodedLen) /// The range of component `s` is `[0, 100]`. fn pool_withdraw_unbonded(s: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `1687` - // Estimated: `18031` - // Minimum execution time: 54_775_000 picoseconds. - Weight::from_parts(55_724_944, 18031) - // Standard Error: 536 - .saturating_add(Weight::from_parts(10_059, 0).saturating_mul(s.into())) - .saturating_add(RocksDbWeight::get().reads(5_u64)) + // Estimated: `4764` + // Minimum execution time: 63_254_000 picoseconds. + Weight::from_parts(64_154_755, 4764) + // Standard Error: 344 + .saturating_add(Weight::from_parts(8_798, 0).saturating_mul(s.into())) + .saturating_add(RocksDbWeight::get().reads(6_u64)) .saturating_add(RocksDbWeight::get().writes(2_u64)) } /// Storage: NominationPools PoolMembers (r:1 w:1) @@ -787,6 +808,8 @@ impl WeightInfo for () { /// Proof: Staking Ledger (max_values: None, max_size: Some(1091), added: 3566, mode: MaxEncodedLen) /// Storage: Balances Locks (r:1 w:1) /// Proof: Balances Locks (max_values: None, max_size: Some(1299), added: 3774, mode: MaxEncodedLen) + /// Storage: Balances Freezes (r:1 w:0) + /// Proof: Balances Freezes (max_values: None, max_size: Some(49), added: 2524, mode: MaxEncodedLen) /// Storage: System Account (r:1 w:1) /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) /// Storage: NominationPools CounterForPoolMembers (r:1 w:1) @@ -797,12 +820,12 @@ impl WeightInfo for () { fn withdraw_unbonded_update(s: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `2115` - // Estimated: `54662` - // Minimum execution time: 106_820_000 picoseconds. - Weight::from_parts(109_870_849, 54662) - // Standard Error: 11_111 - .saturating_add(Weight::from_parts(2_006, 0).saturating_mul(s.into())) - .saturating_add(RocksDbWeight::get().reads(9_u64)) + // Estimated: `27847` + // Minimum execution time: 131_339_000 picoseconds. + Weight::from_parts(133_590_267, 27847) + // Standard Error: 1_058 + .saturating_add(Weight::from_parts(9_932, 0).saturating_mul(s.into())) + .saturating_add(RocksDbWeight::get().reads(10_u64)) .saturating_add(RocksDbWeight::get().writes(8_u64)) } /// Storage: NominationPools PoolMembers (r:1 w:1) @@ -827,6 +850,8 @@ impl WeightInfo for () { /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) /// Storage: Balances Locks (r:1 w:1) /// Proof: Balances Locks (max_values: None, max_size: Some(1299), added: 3774, mode: MaxEncodedLen) + /// Storage: Balances Freezes (r:1 w:0) + /// Proof: Balances Freezes (max_values: None, max_size: Some(49), added: 2524, mode: MaxEncodedLen) /// Storage: NominationPools CounterForPoolMembers (r:1 w:1) /// Proof: NominationPools CounterForPoolMembers (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) /// Storage: NominationPools ReversePoolIdLookup (r:1 w:1) @@ -848,15 +873,13 @@ impl WeightInfo for () { /// Storage: NominationPools ClaimPermissions (r:0 w:1) /// Proof: NominationPools ClaimPermissions (max_values: None, max_size: Some(41), added: 2516, mode: MaxEncodedLen) /// The range of component `s` is `[0, 100]`. - fn withdraw_unbonded_kill(s: u32, ) -> Weight { + fn withdraw_unbonded_kill(_s: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `2470` - // Estimated: `87490` - // Minimum execution time: 170_908_000 picoseconds. - Weight::from_parts(173_616_555, 87490) - // Standard Error: 2_324 - .saturating_add(Weight::from_parts(4_397, 0).saturating_mul(s.into())) - .saturating_add(RocksDbWeight::get().reads(20_u64)) + // Estimated: `27847` + // Minimum execution time: 219_026_000 picoseconds. + Weight::from_parts(223_230_356, 27847) + .saturating_add(RocksDbWeight::get().reads(21_u64)) .saturating_add(RocksDbWeight::get().writes(18_u64)) } /// Storage: NominationPools LastPoolId (r:1 w:1) @@ -889,6 +912,8 @@ impl WeightInfo for () { /// Proof: Staking CurrentEra (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) /// Storage: Balances Locks (r:1 w:1) /// Proof: Balances Locks (max_values: None, max_size: Some(1299), added: 3774, mode: MaxEncodedLen) + /// Storage: Balances Freezes (r:1 w:0) + /// Proof: Balances Freezes (max_values: None, max_size: Some(49), added: 2524, mode: MaxEncodedLen) /// Storage: NominationPools RewardPools (r:1 w:1) /// Proof: NominationPools RewardPools (max_values: None, max_size: Some(92), added: 2567, mode: MaxEncodedLen) /// Storage: NominationPools CounterForRewardPools (r:1 w:1) @@ -904,10 +929,10 @@ impl WeightInfo for () { fn create() -> Weight { // Proof Size summary in bytes: // Measured: `1289` - // Estimated: `51410` - // Minimum execution time: 148_934_000 picoseconds. - Weight::from_parts(149_702_000, 51410) - .saturating_add(RocksDbWeight::get().reads(21_u64)) + // Estimated: `6196` + // Minimum execution time: 189_710_000 picoseconds. + Weight::from_parts(190_251_000, 6196) + .saturating_add(RocksDbWeight::get().reads(22_u64)) .saturating_add(RocksDbWeight::get().writes(15_u64)) } /// Storage: NominationPools BondedPools (r:1 w:0) @@ -938,11 +963,11 @@ impl WeightInfo for () { fn nominate(n: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `1849` - // Estimated: `33934 + n * (2520 ±0)` - // Minimum execution time: 68_986_000 picoseconds. - Weight::from_parts(69_474_269, 33934) - // Standard Error: 6_602 - .saturating_add(Weight::from_parts(1_416_294, 0).saturating_mul(n.into())) + // Estimated: `4556 + n * (2520 ±0)` + // Minimum execution time: 70_044_000 picoseconds. + Weight::from_parts(69_307_256, 4556) + // Standard Error: 6_066 + .saturating_add(Weight::from_parts(1_517_942, 0).saturating_mul(n.into())) .saturating_add(RocksDbWeight::get().reads(12_u64)) .saturating_add(RocksDbWeight::get().reads((1_u64).saturating_mul(n.into()))) .saturating_add(RocksDbWeight::get().writes(5_u64)) @@ -957,9 +982,9 @@ impl WeightInfo for () { fn set_state() -> Weight { // Proof Size summary in bytes: // Measured: `1438` - // Estimated: `11778` - // Minimum execution time: 36_300_000 picoseconds. - Weight::from_parts(36_713_000, 11778) + // Estimated: `4556` + // Minimum execution time: 36_610_000 picoseconds. + Weight::from_parts(37_212_000, 4556) .saturating_add(RocksDbWeight::get().reads(3_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } @@ -973,11 +998,11 @@ impl WeightInfo for () { fn set_metadata(n: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `531` - // Estimated: `8909` - // Minimum execution time: 14_976_000 picoseconds. - Weight::from_parts(15_538_088, 8909) - // Standard Error: 71 - .saturating_add(Weight::from_parts(1_548, 0).saturating_mul(n.into())) + // Estimated: `3735` + // Minimum execution time: 15_334_000 picoseconds. + Weight::from_parts(15_753_107, 3735) + // Standard Error: 62 + .saturating_add(Weight::from_parts(1_365, 0).saturating_mul(n.into())) .saturating_add(RocksDbWeight::get().reads(3_u64)) .saturating_add(RocksDbWeight::get().writes(2_u64)) } @@ -997,8 +1022,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 7_259_000 picoseconds. - Weight::from_parts(7_499_000, 0) + // Minimum execution time: 7_156_000 picoseconds. + Weight::from_parts(7_596_000, 0) .saturating_add(RocksDbWeight::get().writes(6_u64)) } /// Storage: NominationPools BondedPools (r:1 w:1) @@ -1007,8 +1032,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `531` // Estimated: `3685` - // Minimum execution time: 20_454_000 picoseconds. - Weight::from_parts(20_771_000, 3685) + // Minimum execution time: 20_342_000 picoseconds. + Weight::from_parts(20_699_000, 3685) .saturating_add(RocksDbWeight::get().reads(1_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } @@ -1033,9 +1058,9 @@ impl WeightInfo for () { fn chill() -> Weight { // Proof Size summary in bytes: // Measured: `2012` - // Estimated: `29455` - // Minimum execution time: 66_404_000 picoseconds. - Weight::from_parts(66_872_000, 29455) + // Estimated: `4556` + // Minimum execution time: 66_608_000 picoseconds. + Weight::from_parts(67_416_000, 4556) .saturating_add(RocksDbWeight::get().reads(9_u64)) .saturating_add(RocksDbWeight::get().writes(5_u64)) } @@ -1050,9 +1075,9 @@ impl WeightInfo for () { fn set_commission() -> Weight { // Proof Size summary in bytes: // Measured: `770` - // Estimated: `12324` - // Minimum execution time: 34_240_000 picoseconds. - Weight::from_parts(34_797_000, 12324) + // Estimated: `3685` + // Minimum execution time: 34_619_000 picoseconds. + Weight::from_parts(34_894_000, 3685) .saturating_add(RocksDbWeight::get().reads(4_u64)) .saturating_add(RocksDbWeight::get().writes(2_u64)) } @@ -1062,8 +1087,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `571` // Estimated: `3685` - // Minimum execution time: 19_469_000 picoseconds. - Weight::from_parts(19_865_000, 3685) + // Minimum execution time: 19_676_000 picoseconds. + Weight::from_parts(19_958_000, 3685) .saturating_add(RocksDbWeight::get().reads(1_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } @@ -1073,8 +1098,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `531` // Estimated: `3685` - // Minimum execution time: 20_423_000 picoseconds. - Weight::from_parts(20_620_000, 3685) + // Minimum execution time: 20_404_000 picoseconds. + Weight::from_parts(20_859_000, 3685) .saturating_add(RocksDbWeight::get().reads(1_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } @@ -1085,9 +1110,9 @@ impl WeightInfo for () { fn set_claim_permission() -> Weight { // Proof Size summary in bytes: // Measured: `542` - // Estimated: `7208` - // Minimum execution time: 15_346_000 picoseconds. - Weight::from_parts(15_613_000, 7208) + // Estimated: `3702` + // Minimum execution time: 15_401_000 picoseconds. + Weight::from_parts(15_706_000, 3702) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } @@ -1102,9 +1127,9 @@ impl WeightInfo for () { fn claim_commission() -> Weight { // Proof Size summary in bytes: // Measured: `968` - // Estimated: `12324` - // Minimum execution time: 48_749_000 picoseconds. - Weight::from_parts(49_131_000, 12324) + // Estimated: `3685` + // Minimum execution time: 66_775_000 picoseconds. + Weight::from_parts(67_242_000, 3685) .saturating_add(RocksDbWeight::get().reads(4_u64)) .saturating_add(RocksDbWeight::get().writes(2_u64)) } diff --git a/frame/preimage/src/weights.rs b/frame/preimage/src/weights.rs index 168cb634c..2177309db 100644 --- a/frame/preimage/src/weights.rs +++ b/frame/preimage/src/weights.rs @@ -18,9 +18,9 @@ //! Autogenerated weights for pallet_preimage //! //! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev -//! DATE: 2023-03-16, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! DATE: 2023-04-06, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` //! WORST CASE MAP SIZE: `1000000` -//! HOSTNAME: `bm3`, CPU: `Intel(R) Core(TM) i7-7700K CPU @ 4.20GHz` +//! HOSTNAME: `bm2`, CPU: `Intel(R) Core(TM) i7-7700K CPU @ 4.20GHz` //! EXECUTION: Some(Wasm), WASM-EXECUTION: Compiled, CHAIN: Some("dev"), DB CACHE: 1024 // Executed Command: @@ -31,9 +31,6 @@ // --steps=50 // --repeat=20 // --pallet=pallet_preimage -// --no-storage-info -// --no-median-slopes -// --no-min-squares // --extrinsic=* // --execution=wasm // --wasm-execution=compiled @@ -77,10 +74,10 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `143` // Estimated: `3556` - // Minimum execution time: 27_010_000 picoseconds. - Weight::from_parts(27_665_000, 3556) + // Minimum execution time: 31_578_000 picoseconds. + Weight::from_parts(31_955_000, 3556) // Standard Error: 0 - .saturating_add(Weight::from_parts(1_414, 0).saturating_mul(s.into())) + .saturating_add(Weight::from_parts(1_395, 0).saturating_mul(s.into())) .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(T::DbWeight::get().writes(2_u64)) } @@ -93,10 +90,10 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `106` // Estimated: `3556` - // Minimum execution time: 16_984_000 picoseconds. - Weight::from_parts(17_455_000, 3556) + // Minimum execution time: 17_017_000 picoseconds. + Weight::from_parts(17_549_000, 3556) // Standard Error: 0 - .saturating_add(Weight::from_parts(1_414, 0).saturating_mul(s.into())) + .saturating_add(Weight::from_parts(1_394, 0).saturating_mul(s.into())) .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(T::DbWeight::get().writes(2_u64)) } @@ -109,10 +106,10 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `106` // Estimated: `3556` - // Minimum execution time: 16_579_000 picoseconds. - Weight::from_parts(16_849_000, 3556) + // Minimum execution time: 16_507_000 picoseconds. + Weight::from_parts(16_624_000, 3556) // Standard Error: 0 - .saturating_add(Weight::from_parts(1_414, 0).saturating_mul(s.into())) + .saturating_add(Weight::from_parts(1_395, 0).saturating_mul(s.into())) .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(T::DbWeight::get().writes(2_u64)) } @@ -124,8 +121,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `289` // Estimated: `3556` - // Minimum execution time: 33_552_000 picoseconds. - Weight::from_parts(35_134_000, 3556) + // Minimum execution time: 38_016_000 picoseconds. + Weight::from_parts(38_909_000, 3556) .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(T::DbWeight::get().writes(2_u64)) } @@ -137,8 +134,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `144` // Estimated: `3556` - // Minimum execution time: 22_102_000 picoseconds. - Weight::from_parts(22_860_000, 3556) + // Minimum execution time: 21_408_000 picoseconds. + Weight::from_parts(22_343_000, 3556) .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(T::DbWeight::get().writes(2_u64)) } @@ -148,8 +145,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `188` // Estimated: `3556` - // Minimum execution time: 19_590_000 picoseconds. - Weight::from_parts(20_638_000, 3556) + // Minimum execution time: 20_035_000 picoseconds. + Weight::from_parts(20_639_000, 3556) .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -159,8 +156,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `144` // Estimated: `3556` - // Minimum execution time: 11_689_000 picoseconds. - Weight::from_parts(12_577_000, 3556) + // Minimum execution time: 12_028_000 picoseconds. + Weight::from_parts(12_509_000, 3556) .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -170,8 +167,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `42` // Estimated: `3556` - // Minimum execution time: 13_673_000 picoseconds. - Weight::from_parts(14_210_000, 3556) + // Minimum execution time: 13_568_000 picoseconds. + Weight::from_parts(14_161_000, 3556) .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -181,8 +178,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `106` // Estimated: `3556` - // Minimum execution time: 8_810_000 picoseconds. - Weight::from_parts(9_044_000, 3556) + // Minimum execution time: 8_538_000 picoseconds. + Weight::from_parts(8_933_000, 3556) .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -194,8 +191,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `144` // Estimated: `3556` - // Minimum execution time: 21_236_000 picoseconds. - Weight::from_parts(21_684_000, 3556) + // Minimum execution time: 20_692_000 picoseconds. + Weight::from_parts(21_770_000, 3556) .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(T::DbWeight::get().writes(2_u64)) } @@ -205,8 +202,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `106` // Estimated: `3556` - // Minimum execution time: 8_709_000 picoseconds. - Weight::from_parts(8_970_000, 3556) + // Minimum execution time: 8_572_000 picoseconds. + Weight::from_parts(8_795_000, 3556) .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -216,8 +213,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `106` // Estimated: `3556` - // Minimum execution time: 8_976_000 picoseconds. - Weight::from_parts(9_164_000, 3556) + // Minimum execution time: 8_266_000 picoseconds. + Weight::from_parts(8_721_000, 3556) .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -234,10 +231,10 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `143` // Estimated: `3556` - // Minimum execution time: 27_010_000 picoseconds. - Weight::from_parts(27_665_000, 3556) + // Minimum execution time: 31_578_000 picoseconds. + Weight::from_parts(31_955_000, 3556) // Standard Error: 0 - .saturating_add(Weight::from_parts(1_414, 0).saturating_mul(s.into())) + .saturating_add(Weight::from_parts(1_395, 0).saturating_mul(s.into())) .saturating_add(RocksDbWeight::get().reads(1_u64)) .saturating_add(RocksDbWeight::get().writes(2_u64)) } @@ -250,10 +247,10 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `106` // Estimated: `3556` - // Minimum execution time: 16_984_000 picoseconds. - Weight::from_parts(17_455_000, 3556) + // Minimum execution time: 17_017_000 picoseconds. + Weight::from_parts(17_549_000, 3556) // Standard Error: 0 - .saturating_add(Weight::from_parts(1_414, 0).saturating_mul(s.into())) + .saturating_add(Weight::from_parts(1_394, 0).saturating_mul(s.into())) .saturating_add(RocksDbWeight::get().reads(1_u64)) .saturating_add(RocksDbWeight::get().writes(2_u64)) } @@ -266,10 +263,10 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `106` // Estimated: `3556` - // Minimum execution time: 16_579_000 picoseconds. - Weight::from_parts(16_849_000, 3556) + // Minimum execution time: 16_507_000 picoseconds. + Weight::from_parts(16_624_000, 3556) // Standard Error: 0 - .saturating_add(Weight::from_parts(1_414, 0).saturating_mul(s.into())) + .saturating_add(Weight::from_parts(1_395, 0).saturating_mul(s.into())) .saturating_add(RocksDbWeight::get().reads(1_u64)) .saturating_add(RocksDbWeight::get().writes(2_u64)) } @@ -281,8 +278,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `289` // Estimated: `3556` - // Minimum execution time: 33_552_000 picoseconds. - Weight::from_parts(35_134_000, 3556) + // Minimum execution time: 38_016_000 picoseconds. + Weight::from_parts(38_909_000, 3556) .saturating_add(RocksDbWeight::get().reads(1_u64)) .saturating_add(RocksDbWeight::get().writes(2_u64)) } @@ -294,8 +291,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `144` // Estimated: `3556` - // Minimum execution time: 22_102_000 picoseconds. - Weight::from_parts(22_860_000, 3556) + // Minimum execution time: 21_408_000 picoseconds. + Weight::from_parts(22_343_000, 3556) .saturating_add(RocksDbWeight::get().reads(1_u64)) .saturating_add(RocksDbWeight::get().writes(2_u64)) } @@ -305,8 +302,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `188` // Estimated: `3556` - // Minimum execution time: 19_590_000 picoseconds. - Weight::from_parts(20_638_000, 3556) + // Minimum execution time: 20_035_000 picoseconds. + Weight::from_parts(20_639_000, 3556) .saturating_add(RocksDbWeight::get().reads(1_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } @@ -316,8 +313,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `144` // Estimated: `3556` - // Minimum execution time: 11_689_000 picoseconds. - Weight::from_parts(12_577_000, 3556) + // Minimum execution time: 12_028_000 picoseconds. + Weight::from_parts(12_509_000, 3556) .saturating_add(RocksDbWeight::get().reads(1_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } @@ -327,8 +324,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `42` // Estimated: `3556` - // Minimum execution time: 13_673_000 picoseconds. - Weight::from_parts(14_210_000, 3556) + // Minimum execution time: 13_568_000 picoseconds. + Weight::from_parts(14_161_000, 3556) .saturating_add(RocksDbWeight::get().reads(1_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } @@ -338,8 +335,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `106` // Estimated: `3556` - // Minimum execution time: 8_810_000 picoseconds. - Weight::from_parts(9_044_000, 3556) + // Minimum execution time: 8_538_000 picoseconds. + Weight::from_parts(8_933_000, 3556) .saturating_add(RocksDbWeight::get().reads(1_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } @@ -351,8 +348,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `144` // Estimated: `3556` - // Minimum execution time: 21_236_000 picoseconds. - Weight::from_parts(21_684_000, 3556) + // Minimum execution time: 20_692_000 picoseconds. + Weight::from_parts(21_770_000, 3556) .saturating_add(RocksDbWeight::get().reads(1_u64)) .saturating_add(RocksDbWeight::get().writes(2_u64)) } @@ -362,8 +359,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `106` // Estimated: `3556` - // Minimum execution time: 8_709_000 picoseconds. - Weight::from_parts(8_970_000, 3556) + // Minimum execution time: 8_572_000 picoseconds. + Weight::from_parts(8_795_000, 3556) .saturating_add(RocksDbWeight::get().reads(1_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } @@ -373,8 +370,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `106` // Estimated: `3556` - // Minimum execution time: 8_976_000 picoseconds. - Weight::from_parts(9_164_000, 3556) + // Minimum execution time: 8_266_000 picoseconds. + Weight::from_parts(8_721_000, 3556) .saturating_add(RocksDbWeight::get().reads(1_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } diff --git a/frame/proxy/src/weights.rs b/frame/proxy/src/weights.rs index 28e24c468..5a6352fc7 100644 --- a/frame/proxy/src/weights.rs +++ b/frame/proxy/src/weights.rs @@ -18,9 +18,9 @@ //! Autogenerated weights for pallet_proxy //! //! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev -//! DATE: 2023-03-16, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! DATE: 2023-04-06, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` //! WORST CASE MAP SIZE: `1000000` -//! HOSTNAME: `bm3`, CPU: `Intel(R) Core(TM) i7-7700K CPU @ 4.20GHz` +//! HOSTNAME: `bm2`, CPU: `Intel(R) Core(TM) i7-7700K CPU @ 4.20GHz` //! EXECUTION: Some(Wasm), WASM-EXECUTION: Compiled, CHAIN: Some("dev"), DB CACHE: 1024 // Executed Command: @@ -31,9 +31,6 @@ // --steps=50 // --repeat=20 // --pallet=pallet_proxy -// --no-storage-info -// --no-median-slopes -// --no-min-squares // --extrinsic=* // --execution=wasm // --wasm-execution=compiled @@ -73,10 +70,10 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `161 + p * (37 ±0)` // Estimated: `4706` - // Minimum execution time: 16_407_000 picoseconds. - Weight::from_parts(17_044_509, 4706) - // Standard Error: 1_177 - .saturating_add(Weight::from_parts(36_533, 0).saturating_mul(p.into())) + // Minimum execution time: 16_542_000 picoseconds. + Weight::from_parts(17_131_651, 4706) + // Standard Error: 1_279 + .saturating_add(Weight::from_parts(31_622, 0).saturating_mul(p.into())) .saturating_add(T::DbWeight::get().reads(1_u64)) } /// Storage: Proxy Proxies (r:1 w:0) @@ -90,13 +87,13 @@ impl WeightInfo for SubstrateWeight { fn proxy_announced(a: u32, p: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `488 + a * (68 ±0) + p * (37 ±0)` - // Estimated: `13997` - // Minimum execution time: 38_523_000 picoseconds. - Weight::from_parts(39_227_232, 13997) - // Standard Error: 5_703 - .saturating_add(Weight::from_parts(136_003, 0).saturating_mul(a.into())) - // Standard Error: 5_893 - .saturating_add(Weight::from_parts(42_397, 0).saturating_mul(p.into())) + // Estimated: `5698` + // Minimum execution time: 41_702_000 picoseconds. + Weight::from_parts(41_868_091, 5698) + // Standard Error: 3_771 + .saturating_add(Weight::from_parts(135_604, 0).saturating_mul(a.into())) + // Standard Error: 3_896 + .saturating_add(Weight::from_parts(32_615, 0).saturating_mul(p.into())) .saturating_add(T::DbWeight::get().reads(3_u64)) .saturating_add(T::DbWeight::get().writes(2_u64)) } @@ -106,16 +103,14 @@ impl WeightInfo for SubstrateWeight { /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) /// The range of component `a` is `[0, 31]`. /// The range of component `p` is `[1, 31]`. - fn remove_announcement(a: u32, p: u32, ) -> Weight { + fn remove_announcement(a: u32, _p: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `403 + a * (68 ±0)` - // Estimated: `9291` - // Minimum execution time: 22_541_000 picoseconds. - Weight::from_parts(23_402_268, 9291) - // Standard Error: 3_242 - .saturating_add(Weight::from_parts(178_017, 0).saturating_mul(a.into())) - // Standard Error: 3_349 - .saturating_add(Weight::from_parts(6_230, 0).saturating_mul(p.into())) + // Estimated: `5698` + // Minimum execution time: 25_432_000 picoseconds. + Weight::from_parts(26_301_674, 5698) + // Standard Error: 1_413 + .saturating_add(Weight::from_parts(167_176, 0).saturating_mul(a.into())) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().writes(2_u64)) } @@ -128,13 +123,13 @@ impl WeightInfo for SubstrateWeight { fn reject_announcement(a: u32, p: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `403 + a * (68 ±0)` - // Estimated: `9291` - // Minimum execution time: 22_539_000 picoseconds. - Weight::from_parts(23_479_286, 9291) - // Standard Error: 1_329 - .saturating_add(Weight::from_parts(174_021, 0).saturating_mul(a.into())) - // Standard Error: 1_373 - .saturating_add(Weight::from_parts(6_465, 0).saturating_mul(p.into())) + // Estimated: `5698` + // Minimum execution time: 25_280_000 picoseconds. + Weight::from_parts(26_099_549, 5698) + // Standard Error: 1_458 + .saturating_add(Weight::from_parts(168_724, 0).saturating_mul(a.into())) + // Standard Error: 1_507 + .saturating_add(Weight::from_parts(2_212, 0).saturating_mul(p.into())) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().writes(2_u64)) } @@ -149,13 +144,13 @@ impl WeightInfo for SubstrateWeight { fn announce(a: u32, p: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `420 + a * (68 ±0) + p * (37 ±0)` - // Estimated: `13997` - // Minimum execution time: 32_049_000 picoseconds. - Weight::from_parts(35_352_453, 13997) - // Standard Error: 4_389 - .saturating_add(Weight::from_parts(144_573, 0).saturating_mul(a.into())) - // Standard Error: 4_535 - .saturating_add(Weight::from_parts(41_849, 0).saturating_mul(p.into())) + // Estimated: `5698` + // Minimum execution time: 35_889_000 picoseconds. + Weight::from_parts(37_535_424, 5698) + // Standard Error: 3_899 + .saturating_add(Weight::from_parts(138_757, 0).saturating_mul(a.into())) + // Standard Error: 4_028 + .saturating_add(Weight::from_parts(46_196, 0).saturating_mul(p.into())) .saturating_add(T::DbWeight::get().reads(3_u64)) .saturating_add(T::DbWeight::get().writes(2_u64)) } @@ -166,10 +161,10 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `161 + p * (37 ±0)` // Estimated: `4706` - // Minimum execution time: 24_491_000 picoseconds. - Weight::from_parts(25_291_582, 4706) - // Standard Error: 11_080 - .saturating_add(Weight::from_parts(68_024, 0).saturating_mul(p.into())) + // Minimum execution time: 26_876_000 picoseconds. + Weight::from_parts(27_356_694, 4706) + // Standard Error: 1_437 + .saturating_add(Weight::from_parts(68_994, 0).saturating_mul(p.into())) .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -180,10 +175,10 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `161 + p * (37 ±0)` // Estimated: `4706` - // Minimum execution time: 23_849_000 picoseconds. - Weight::from_parts(25_370_806, 4706) - // Standard Error: 8_763 - .saturating_add(Weight::from_parts(63_413, 0).saturating_mul(p.into())) + // Minimum execution time: 26_655_000 picoseconds. + Weight::from_parts(27_726_692, 4706) + // Standard Error: 1_980 + .saturating_add(Weight::from_parts(55_932, 0).saturating_mul(p.into())) .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -194,24 +189,22 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `161 + p * (37 ±0)` // Estimated: `4706` - // Minimum execution time: 19_488_000 picoseconds. - Weight::from_parts(20_219_817, 4706) - // Standard Error: 1_385 - .saturating_add(Weight::from_parts(30_354, 0).saturating_mul(p.into())) + // Minimum execution time: 23_716_000 picoseconds. + Weight::from_parts(24_660_737, 4706) + // Standard Error: 1_400 + .saturating_add(Weight::from_parts(31_679, 0).saturating_mul(p.into())) .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } /// Storage: Proxy Proxies (r:1 w:1) /// Proof: Proxy Proxies (max_values: None, max_size: Some(1241), added: 3716, mode: MaxEncodedLen) /// The range of component `p` is `[1, 31]`. - fn create_pure(p: u32, ) -> Weight { + fn create_pure(_p: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `173` // Estimated: `4706` - // Minimum execution time: 25_864_000 picoseconds. - Weight::from_parts(26_712_232, 4706) - // Standard Error: 1_331 - .saturating_add(Weight::from_parts(4_401, 0).saturating_mul(p.into())) + // Minimum execution time: 28_233_000 picoseconds. + Weight::from_parts(29_602_422, 4706) .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -222,10 +215,10 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `198 + p * (37 ±0)` // Estimated: `4706` - // Minimum execution time: 20_488_000 picoseconds. - Weight::from_parts(21_135_155, 4706) - // Standard Error: 1_255 - .saturating_add(Weight::from_parts(36_312, 0).saturating_mul(p.into())) + // Minimum execution time: 24_759_000 picoseconds. + Weight::from_parts(25_533_053, 4706) + // Standard Error: 1_254 + .saturating_add(Weight::from_parts(36_331, 0).saturating_mul(p.into())) .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -240,10 +233,10 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `161 + p * (37 ±0)` // Estimated: `4706` - // Minimum execution time: 16_407_000 picoseconds. - Weight::from_parts(17_044_509, 4706) - // Standard Error: 1_177 - .saturating_add(Weight::from_parts(36_533, 0).saturating_mul(p.into())) + // Minimum execution time: 16_542_000 picoseconds. + Weight::from_parts(17_131_651, 4706) + // Standard Error: 1_279 + .saturating_add(Weight::from_parts(31_622, 0).saturating_mul(p.into())) .saturating_add(RocksDbWeight::get().reads(1_u64)) } /// Storage: Proxy Proxies (r:1 w:0) @@ -257,13 +250,13 @@ impl WeightInfo for () { fn proxy_announced(a: u32, p: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `488 + a * (68 ±0) + p * (37 ±0)` - // Estimated: `13997` - // Minimum execution time: 38_523_000 picoseconds. - Weight::from_parts(39_227_232, 13997) - // Standard Error: 5_703 - .saturating_add(Weight::from_parts(136_003, 0).saturating_mul(a.into())) - // Standard Error: 5_893 - .saturating_add(Weight::from_parts(42_397, 0).saturating_mul(p.into())) + // Estimated: `5698` + // Minimum execution time: 41_702_000 picoseconds. + Weight::from_parts(41_868_091, 5698) + // Standard Error: 3_771 + .saturating_add(Weight::from_parts(135_604, 0).saturating_mul(a.into())) + // Standard Error: 3_896 + .saturating_add(Weight::from_parts(32_615, 0).saturating_mul(p.into())) .saturating_add(RocksDbWeight::get().reads(3_u64)) .saturating_add(RocksDbWeight::get().writes(2_u64)) } @@ -273,16 +266,14 @@ impl WeightInfo for () { /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) /// The range of component `a` is `[0, 31]`. /// The range of component `p` is `[1, 31]`. - fn remove_announcement(a: u32, p: u32, ) -> Weight { + fn remove_announcement(a: u32, _p: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `403 + a * (68 ±0)` - // Estimated: `9291` - // Minimum execution time: 22_541_000 picoseconds. - Weight::from_parts(23_402_268, 9291) - // Standard Error: 3_242 - .saturating_add(Weight::from_parts(178_017, 0).saturating_mul(a.into())) - // Standard Error: 3_349 - .saturating_add(Weight::from_parts(6_230, 0).saturating_mul(p.into())) + // Estimated: `5698` + // Minimum execution time: 25_432_000 picoseconds. + Weight::from_parts(26_301_674, 5698) + // Standard Error: 1_413 + .saturating_add(Weight::from_parts(167_176, 0).saturating_mul(a.into())) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().writes(2_u64)) } @@ -295,13 +286,13 @@ impl WeightInfo for () { fn reject_announcement(a: u32, p: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `403 + a * (68 ±0)` - // Estimated: `9291` - // Minimum execution time: 22_539_000 picoseconds. - Weight::from_parts(23_479_286, 9291) - // Standard Error: 1_329 - .saturating_add(Weight::from_parts(174_021, 0).saturating_mul(a.into())) - // Standard Error: 1_373 - .saturating_add(Weight::from_parts(6_465, 0).saturating_mul(p.into())) + // Estimated: `5698` + // Minimum execution time: 25_280_000 picoseconds. + Weight::from_parts(26_099_549, 5698) + // Standard Error: 1_458 + .saturating_add(Weight::from_parts(168_724, 0).saturating_mul(a.into())) + // Standard Error: 1_507 + .saturating_add(Weight::from_parts(2_212, 0).saturating_mul(p.into())) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().writes(2_u64)) } @@ -316,13 +307,13 @@ impl WeightInfo for () { fn announce(a: u32, p: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `420 + a * (68 ±0) + p * (37 ±0)` - // Estimated: `13997` - // Minimum execution time: 32_049_000 picoseconds. - Weight::from_parts(35_352_453, 13997) - // Standard Error: 4_389 - .saturating_add(Weight::from_parts(144_573, 0).saturating_mul(a.into())) - // Standard Error: 4_535 - .saturating_add(Weight::from_parts(41_849, 0).saturating_mul(p.into())) + // Estimated: `5698` + // Minimum execution time: 35_889_000 picoseconds. + Weight::from_parts(37_535_424, 5698) + // Standard Error: 3_899 + .saturating_add(Weight::from_parts(138_757, 0).saturating_mul(a.into())) + // Standard Error: 4_028 + .saturating_add(Weight::from_parts(46_196, 0).saturating_mul(p.into())) .saturating_add(RocksDbWeight::get().reads(3_u64)) .saturating_add(RocksDbWeight::get().writes(2_u64)) } @@ -333,10 +324,10 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `161 + p * (37 ±0)` // Estimated: `4706` - // Minimum execution time: 24_491_000 picoseconds. - Weight::from_parts(25_291_582, 4706) - // Standard Error: 11_080 - .saturating_add(Weight::from_parts(68_024, 0).saturating_mul(p.into())) + // Minimum execution time: 26_876_000 picoseconds. + Weight::from_parts(27_356_694, 4706) + // Standard Error: 1_437 + .saturating_add(Weight::from_parts(68_994, 0).saturating_mul(p.into())) .saturating_add(RocksDbWeight::get().reads(1_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } @@ -347,10 +338,10 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `161 + p * (37 ±0)` // Estimated: `4706` - // Minimum execution time: 23_849_000 picoseconds. - Weight::from_parts(25_370_806, 4706) - // Standard Error: 8_763 - .saturating_add(Weight::from_parts(63_413, 0).saturating_mul(p.into())) + // Minimum execution time: 26_655_000 picoseconds. + Weight::from_parts(27_726_692, 4706) + // Standard Error: 1_980 + .saturating_add(Weight::from_parts(55_932, 0).saturating_mul(p.into())) .saturating_add(RocksDbWeight::get().reads(1_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } @@ -361,24 +352,22 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `161 + p * (37 ±0)` // Estimated: `4706` - // Minimum execution time: 19_488_000 picoseconds. - Weight::from_parts(20_219_817, 4706) - // Standard Error: 1_385 - .saturating_add(Weight::from_parts(30_354, 0).saturating_mul(p.into())) + // Minimum execution time: 23_716_000 picoseconds. + Weight::from_parts(24_660_737, 4706) + // Standard Error: 1_400 + .saturating_add(Weight::from_parts(31_679, 0).saturating_mul(p.into())) .saturating_add(RocksDbWeight::get().reads(1_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } /// Storage: Proxy Proxies (r:1 w:1) /// Proof: Proxy Proxies (max_values: None, max_size: Some(1241), added: 3716, mode: MaxEncodedLen) /// The range of component `p` is `[1, 31]`. - fn create_pure(p: u32, ) -> Weight { + fn create_pure(_p: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `173` // Estimated: `4706` - // Minimum execution time: 25_864_000 picoseconds. - Weight::from_parts(26_712_232, 4706) - // Standard Error: 1_331 - .saturating_add(Weight::from_parts(4_401, 0).saturating_mul(p.into())) + // Minimum execution time: 28_233_000 picoseconds. + Weight::from_parts(29_602_422, 4706) .saturating_add(RocksDbWeight::get().reads(1_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } @@ -389,10 +378,10 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `198 + p * (37 ±0)` // Estimated: `4706` - // Minimum execution time: 20_488_000 picoseconds. - Weight::from_parts(21_135_155, 4706) - // Standard Error: 1_255 - .saturating_add(Weight::from_parts(36_312, 0).saturating_mul(p.into())) + // Minimum execution time: 24_759_000 picoseconds. + Weight::from_parts(25_533_053, 4706) + // Standard Error: 1_254 + .saturating_add(Weight::from_parts(36_331, 0).saturating_mul(p.into())) .saturating_add(RocksDbWeight::get().reads(1_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } diff --git a/frame/ranked-collective/src/weights.rs b/frame/ranked-collective/src/weights.rs index e25737a1d..754fcf70a 100644 --- a/frame/ranked-collective/src/weights.rs +++ b/frame/ranked-collective/src/weights.rs @@ -18,9 +18,9 @@ //! Autogenerated weights for pallet_ranked_collective //! //! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev -//! DATE: 2023-03-16, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! DATE: 2023-04-06, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` //! WORST CASE MAP SIZE: `1000000` -//! HOSTNAME: `bm3`, CPU: `Intel(R) Core(TM) i7-7700K CPU @ 4.20GHz` +//! HOSTNAME: `bm2`, CPU: `Intel(R) Core(TM) i7-7700K CPU @ 4.20GHz` //! EXECUTION: Some(Wasm), WASM-EXECUTION: Compiled, CHAIN: Some("dev"), DB CACHE: 1024 // Executed Command: @@ -31,9 +31,6 @@ // --steps=50 // --repeat=20 // --pallet=pallet_ranked_collective -// --no-storage-info -// --no-median-slopes -// --no-min-squares // --extrinsic=* // --execution=wasm // --wasm-execution=compiled @@ -73,9 +70,9 @@ impl WeightInfo for SubstrateWeight { fn add_member() -> Weight { // Proof Size summary in bytes: // Measured: `109` - // Estimated: `6986` - // Minimum execution time: 19_174_000 picoseconds. - Weight::from_parts(19_391_000, 6986) + // Estimated: `3507` + // Minimum execution time: 18_480_000 picoseconds. + Weight::from_parts(18_769_000, 3507) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().writes(4_u64)) } @@ -91,16 +88,16 @@ impl WeightInfo for SubstrateWeight { fn remove_member(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `583 + r * (281 ±0)` - // Estimated: `14024 + r * (7547 ±0)` - // Minimum execution time: 30_972_000 picoseconds. - Weight::from_parts(35_526_205, 14024) - // Standard Error: 47_832 - .saturating_add(Weight::from_parts(12_838_739, 0).saturating_mul(r.into())) + // Estimated: `3519 + r * (2529 ±0)` + // Minimum execution time: 30_087_000 picoseconds. + Weight::from_parts(33_646_239, 3519) + // Standard Error: 22_498 + .saturating_add(Weight::from_parts(12_524_289, 0).saturating_mul(r.into())) .saturating_add(T::DbWeight::get().reads(4_u64)) .saturating_add(T::DbWeight::get().reads((3_u64).saturating_mul(r.into()))) .saturating_add(T::DbWeight::get().writes(4_u64)) .saturating_add(T::DbWeight::get().writes((3_u64).saturating_mul(r.into()))) - .saturating_add(Weight::from_parts(0, 7547).saturating_mul(r.into())) + .saturating_add(Weight::from_parts(0, 2529).saturating_mul(r.into())) } /// Storage: RankedCollective Members (r:1 w:1) /// Proof: RankedCollective Members (max_values: None, max_size: Some(42), added: 2517, mode: MaxEncodedLen) @@ -114,11 +111,11 @@ impl WeightInfo for SubstrateWeight { fn promote_member(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `281 + r * (17 ±0)` - // Estimated: `6986` - // Minimum execution time: 21_573_000 picoseconds. - Weight::from_parts(22_184_210, 6986) - // Standard Error: 10_584 - .saturating_add(Weight::from_parts(289_535, 0).saturating_mul(r.into())) + // Estimated: `3507` + // Minimum execution time: 20_974_000 picoseconds. + Weight::from_parts(21_582_135, 3507) + // Standard Error: 4_965 + .saturating_add(Weight::from_parts(294_566, 0).saturating_mul(r.into())) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().writes(4_u64)) } @@ -134,11 +131,11 @@ impl WeightInfo for SubstrateWeight { fn demote_member(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `599 + r * (72 ±0)` - // Estimated: `14024` - // Minimum execution time: 30_249_000 picoseconds. - Weight::from_parts(32_702_502, 14024) - // Standard Error: 26_725 - .saturating_add(Weight::from_parts(651_139, 0).saturating_mul(r.into())) + // Estimated: `3519` + // Minimum execution time: 29_621_000 picoseconds. + Weight::from_parts(32_118_301, 3519) + // Standard Error: 27_596 + .saturating_add(Weight::from_parts(647_979, 0).saturating_mul(r.into())) .saturating_add(T::DbWeight::get().reads(4_u64)) .saturating_add(T::DbWeight::get().writes(4_u64)) } @@ -153,9 +150,9 @@ impl WeightInfo for SubstrateWeight { fn vote() -> Weight { // Proof Size summary in bytes: // Measured: `595` - // Estimated: `230816` - // Minimum execution time: 46_972_000 picoseconds. - Weight::from_parts(47_617_000, 230816) + // Estimated: `219984` + // Minimum execution time: 46_360_000 picoseconds. + Weight::from_parts(46_793_000, 219984) .saturating_add(T::DbWeight::get().reads(5_u64)) .saturating_add(T::DbWeight::get().writes(4_u64)) } @@ -169,11 +166,11 @@ impl WeightInfo for SubstrateWeight { fn cleanup_poll(n: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `429 + n * (50 ±0)` - // Estimated: `8364 + n * (2540 ±0)` - // Minimum execution time: 15_301_000 picoseconds. - Weight::from_parts(19_308_613, 8364) - // Standard Error: 1_792 - .saturating_add(Weight::from_parts(1_015_251, 0).saturating_mul(n.into())) + // Estimated: `3795 + n * (2540 ±0)` + // Minimum execution time: 14_869_000 picoseconds. + Weight::from_parts(18_545_013, 3795) + // Standard Error: 1_376 + .saturating_add(Weight::from_parts(1_005_397, 0).saturating_mul(n.into())) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().reads((1_u64).saturating_mul(n.into()))) .saturating_add(T::DbWeight::get().writes((1_u64).saturating_mul(n.into()))) @@ -194,9 +191,9 @@ impl WeightInfo for () { fn add_member() -> Weight { // Proof Size summary in bytes: // Measured: `109` - // Estimated: `6986` - // Minimum execution time: 19_174_000 picoseconds. - Weight::from_parts(19_391_000, 6986) + // Estimated: `3507` + // Minimum execution time: 18_480_000 picoseconds. + Weight::from_parts(18_769_000, 3507) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().writes(4_u64)) } @@ -212,16 +209,16 @@ impl WeightInfo for () { fn remove_member(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `583 + r * (281 ±0)` - // Estimated: `14024 + r * (7547 ±0)` - // Minimum execution time: 30_972_000 picoseconds. - Weight::from_parts(35_526_205, 14024) - // Standard Error: 47_832 - .saturating_add(Weight::from_parts(12_838_739, 0).saturating_mul(r.into())) + // Estimated: `3519 + r * (2529 ±0)` + // Minimum execution time: 30_087_000 picoseconds. + Weight::from_parts(33_646_239, 3519) + // Standard Error: 22_498 + .saturating_add(Weight::from_parts(12_524_289, 0).saturating_mul(r.into())) .saturating_add(RocksDbWeight::get().reads(4_u64)) .saturating_add(RocksDbWeight::get().reads((3_u64).saturating_mul(r.into()))) .saturating_add(RocksDbWeight::get().writes(4_u64)) .saturating_add(RocksDbWeight::get().writes((3_u64).saturating_mul(r.into()))) - .saturating_add(Weight::from_parts(0, 7547).saturating_mul(r.into())) + .saturating_add(Weight::from_parts(0, 2529).saturating_mul(r.into())) } /// Storage: RankedCollective Members (r:1 w:1) /// Proof: RankedCollective Members (max_values: None, max_size: Some(42), added: 2517, mode: MaxEncodedLen) @@ -235,11 +232,11 @@ impl WeightInfo for () { fn promote_member(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `281 + r * (17 ±0)` - // Estimated: `6986` - // Minimum execution time: 21_573_000 picoseconds. - Weight::from_parts(22_184_210, 6986) - // Standard Error: 10_584 - .saturating_add(Weight::from_parts(289_535, 0).saturating_mul(r.into())) + // Estimated: `3507` + // Minimum execution time: 20_974_000 picoseconds. + Weight::from_parts(21_582_135, 3507) + // Standard Error: 4_965 + .saturating_add(Weight::from_parts(294_566, 0).saturating_mul(r.into())) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().writes(4_u64)) } @@ -255,11 +252,11 @@ impl WeightInfo for () { fn demote_member(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `599 + r * (72 ±0)` - // Estimated: `14024` - // Minimum execution time: 30_249_000 picoseconds. - Weight::from_parts(32_702_502, 14024) - // Standard Error: 26_725 - .saturating_add(Weight::from_parts(651_139, 0).saturating_mul(r.into())) + // Estimated: `3519` + // Minimum execution time: 29_621_000 picoseconds. + Weight::from_parts(32_118_301, 3519) + // Standard Error: 27_596 + .saturating_add(Weight::from_parts(647_979, 0).saturating_mul(r.into())) .saturating_add(RocksDbWeight::get().reads(4_u64)) .saturating_add(RocksDbWeight::get().writes(4_u64)) } @@ -274,9 +271,9 @@ impl WeightInfo for () { fn vote() -> Weight { // Proof Size summary in bytes: // Measured: `595` - // Estimated: `230816` - // Minimum execution time: 46_972_000 picoseconds. - Weight::from_parts(47_617_000, 230816) + // Estimated: `219984` + // Minimum execution time: 46_360_000 picoseconds. + Weight::from_parts(46_793_000, 219984) .saturating_add(RocksDbWeight::get().reads(5_u64)) .saturating_add(RocksDbWeight::get().writes(4_u64)) } @@ -290,11 +287,11 @@ impl WeightInfo for () { fn cleanup_poll(n: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `429 + n * (50 ±0)` - // Estimated: `8364 + n * (2540 ±0)` - // Minimum execution time: 15_301_000 picoseconds. - Weight::from_parts(19_308_613, 8364) - // Standard Error: 1_792 - .saturating_add(Weight::from_parts(1_015_251, 0).saturating_mul(n.into())) + // Estimated: `3795 + n * (2540 ±0)` + // Minimum execution time: 14_869_000 picoseconds. + Weight::from_parts(18_545_013, 3795) + // Standard Error: 1_376 + .saturating_add(Weight::from_parts(1_005_397, 0).saturating_mul(n.into())) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().reads((1_u64).saturating_mul(n.into()))) .saturating_add(RocksDbWeight::get().writes((1_u64).saturating_mul(n.into()))) diff --git a/frame/recovery/src/weights.rs b/frame/recovery/src/weights.rs index 8245b9606..97d4c8b87 100644 --- a/frame/recovery/src/weights.rs +++ b/frame/recovery/src/weights.rs @@ -18,9 +18,9 @@ //! Autogenerated weights for pallet_recovery //! //! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev -//! DATE: 2023-03-16, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! DATE: 2023-04-06, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` //! WORST CASE MAP SIZE: `1000000` -//! HOSTNAME: `bm3`, CPU: `Intel(R) Core(TM) i7-7700K CPU @ 4.20GHz` +//! HOSTNAME: `bm2`, CPU: `Intel(R) Core(TM) i7-7700K CPU @ 4.20GHz` //! EXECUTION: Some(Wasm), WASM-EXECUTION: Compiled, CHAIN: Some("dev"), DB CACHE: 1024 // Executed Command: @@ -31,9 +31,6 @@ // --steps=50 // --repeat=20 // --pallet=pallet_recovery -// --no-storage-info -// --no-median-slopes -// --no-min-squares // --extrinsic=* // --execution=wasm // --wasm-execution=compiled @@ -71,8 +68,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `281` // Estimated: `3545` - // Minimum execution time: 9_937_000 picoseconds. - Weight::from_parts(10_213_000, 3545) + // Minimum execution time: 10_405_000 picoseconds. + Weight::from_parts(10_807_000, 3545) .saturating_add(T::DbWeight::get().reads(1_u64)) } /// Storage: Recovery Proxy (r:0 w:1) @@ -81,8 +78,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 9_704_000 picoseconds. - Weight::from_parts(9_997_000, 0) + // Minimum execution time: 11_198_000 picoseconds. + Weight::from_parts(11_459_000, 0) .saturating_add(T::DbWeight::get().writes(1_u64)) } /// Storage: Recovery Recoverable (r:1 w:1) @@ -92,10 +89,10 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `175` // Estimated: `3816` - // Minimum execution time: 23_260_000 picoseconds. - Weight::from_parts(24_408_081, 3816) - // Standard Error: 3_086 - .saturating_add(Weight::from_parts(41_628, 0).saturating_mul(n.into())) + // Minimum execution time: 28_009_000 picoseconds. + Weight::from_parts(28_755_652, 3816) + // Standard Error: 3_536 + .saturating_add(Weight::from_parts(78_348, 0).saturating_mul(n.into())) .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -106,9 +103,9 @@ impl WeightInfo for SubstrateWeight { fn initiate_recovery() -> Weight { // Proof Size summary in bytes: // Measured: `272` - // Estimated: `7670` - // Minimum execution time: 28_148_000 picoseconds. - Weight::from_parts(28_577_000, 7670) + // Estimated: `3854` + // Minimum execution time: 31_233_000 picoseconds. + Weight::from_parts(31_508_000, 3854) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -120,11 +117,11 @@ impl WeightInfo for SubstrateWeight { fn vouch_recovery(n: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `360 + n * (64 ±0)` - // Estimated: `7670` - // Minimum execution time: 20_395_000 picoseconds. - Weight::from_parts(21_287_972, 7670) - // Standard Error: 4_464 - .saturating_add(Weight::from_parts(170_214, 0).saturating_mul(n.into())) + // Estimated: `3854` + // Minimum execution time: 20_542_000 picoseconds. + Weight::from_parts(21_224_065, 3854) + // Standard Error: 3_018 + .saturating_add(Weight::from_parts(171_994, 0).saturating_mul(n.into())) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -138,11 +135,11 @@ impl WeightInfo for SubstrateWeight { fn claim_recovery(n: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `392 + n * (64 ±0)` - // Estimated: `11215` - // Minimum execution time: 24_902_000 picoseconds. - Weight::from_parts(25_662_621, 11215) - // Standard Error: 3_101 - .saturating_add(Weight::from_parts(60_748, 0).saturating_mul(n.into())) + // Estimated: `3854` + // Minimum execution time: 25_141_000 picoseconds. + Weight::from_parts(25_880_238, 3854) + // Standard Error: 3_156 + .saturating_add(Weight::from_parts(54_405, 0).saturating_mul(n.into())) .saturating_add(T::DbWeight::get().reads(3_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -154,11 +151,11 @@ impl WeightInfo for SubstrateWeight { fn close_recovery(n: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `513 + n * (32 ±0)` - // Estimated: `7447` - // Minimum execution time: 30_071_000 picoseconds. - Weight::from_parts(30_727_760, 7447) - // Standard Error: 4_442 - .saturating_add(Weight::from_parts(98_153, 0).saturating_mul(n.into())) + // Estimated: `3854` + // Minimum execution time: 35_314_000 picoseconds. + Weight::from_parts(36_380_338, 3854) + // Standard Error: 7_396 + .saturating_add(Weight::from_parts(3_861, 0).saturating_mul(n.into())) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().writes(2_u64)) } @@ -167,12 +164,14 @@ impl WeightInfo for SubstrateWeight { /// Storage: Recovery Recoverable (r:1 w:1) /// Proof: Recovery Recoverable (max_values: None, max_size: Some(351), added: 2826, mode: MaxEncodedLen) /// The range of component `n` is `[1, 9]`. - fn remove_recovery(_n: u32, ) -> Weight { + fn remove_recovery(n: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `270 + n * (32 ±0)` - // Estimated: `7670` - // Minimum execution time: 29_088_000 picoseconds. - Weight::from_parts(30_535_744, 7670) + // Estimated: `3854` + // Minimum execution time: 33_453_000 picoseconds. + Weight::from_parts(34_078_626, 3854) + // Standard Error: 2_563 + .saturating_add(Weight::from_parts(78_179, 0).saturating_mul(n.into())) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -182,8 +181,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `281` // Estimated: `3545` - // Minimum execution time: 12_712_000 picoseconds. - Weight::from_parts(12_980_000, 3545) + // Minimum execution time: 12_196_000 picoseconds. + Weight::from_parts(12_580_000, 3545) .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -197,8 +196,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `281` // Estimated: `3545` - // Minimum execution time: 9_937_000 picoseconds. - Weight::from_parts(10_213_000, 3545) + // Minimum execution time: 10_405_000 picoseconds. + Weight::from_parts(10_807_000, 3545) .saturating_add(RocksDbWeight::get().reads(1_u64)) } /// Storage: Recovery Proxy (r:0 w:1) @@ -207,8 +206,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 9_704_000 picoseconds. - Weight::from_parts(9_997_000, 0) + // Minimum execution time: 11_198_000 picoseconds. + Weight::from_parts(11_459_000, 0) .saturating_add(RocksDbWeight::get().writes(1_u64)) } /// Storage: Recovery Recoverable (r:1 w:1) @@ -218,10 +217,10 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `175` // Estimated: `3816` - // Minimum execution time: 23_260_000 picoseconds. - Weight::from_parts(24_408_081, 3816) - // Standard Error: 3_086 - .saturating_add(Weight::from_parts(41_628, 0).saturating_mul(n.into())) + // Minimum execution time: 28_009_000 picoseconds. + Weight::from_parts(28_755_652, 3816) + // Standard Error: 3_536 + .saturating_add(Weight::from_parts(78_348, 0).saturating_mul(n.into())) .saturating_add(RocksDbWeight::get().reads(1_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } @@ -232,9 +231,9 @@ impl WeightInfo for () { fn initiate_recovery() -> Weight { // Proof Size summary in bytes: // Measured: `272` - // Estimated: `7670` - // Minimum execution time: 28_148_000 picoseconds. - Weight::from_parts(28_577_000, 7670) + // Estimated: `3854` + // Minimum execution time: 31_233_000 picoseconds. + Weight::from_parts(31_508_000, 3854) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } @@ -246,11 +245,11 @@ impl WeightInfo for () { fn vouch_recovery(n: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `360 + n * (64 ±0)` - // Estimated: `7670` - // Minimum execution time: 20_395_000 picoseconds. - Weight::from_parts(21_287_972, 7670) - // Standard Error: 4_464 - .saturating_add(Weight::from_parts(170_214, 0).saturating_mul(n.into())) + // Estimated: `3854` + // Minimum execution time: 20_542_000 picoseconds. + Weight::from_parts(21_224_065, 3854) + // Standard Error: 3_018 + .saturating_add(Weight::from_parts(171_994, 0).saturating_mul(n.into())) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } @@ -264,11 +263,11 @@ impl WeightInfo for () { fn claim_recovery(n: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `392 + n * (64 ±0)` - // Estimated: `11215` - // Minimum execution time: 24_902_000 picoseconds. - Weight::from_parts(25_662_621, 11215) - // Standard Error: 3_101 - .saturating_add(Weight::from_parts(60_748, 0).saturating_mul(n.into())) + // Estimated: `3854` + // Minimum execution time: 25_141_000 picoseconds. + Weight::from_parts(25_880_238, 3854) + // Standard Error: 3_156 + .saturating_add(Weight::from_parts(54_405, 0).saturating_mul(n.into())) .saturating_add(RocksDbWeight::get().reads(3_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } @@ -280,11 +279,11 @@ impl WeightInfo for () { fn close_recovery(n: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `513 + n * (32 ±0)` - // Estimated: `7447` - // Minimum execution time: 30_071_000 picoseconds. - Weight::from_parts(30_727_760, 7447) - // Standard Error: 4_442 - .saturating_add(Weight::from_parts(98_153, 0).saturating_mul(n.into())) + // Estimated: `3854` + // Minimum execution time: 35_314_000 picoseconds. + Weight::from_parts(36_380_338, 3854) + // Standard Error: 7_396 + .saturating_add(Weight::from_parts(3_861, 0).saturating_mul(n.into())) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().writes(2_u64)) } @@ -293,12 +292,14 @@ impl WeightInfo for () { /// Storage: Recovery Recoverable (r:1 w:1) /// Proof: Recovery Recoverable (max_values: None, max_size: Some(351), added: 2826, mode: MaxEncodedLen) /// The range of component `n` is `[1, 9]`. - fn remove_recovery(_n: u32, ) -> Weight { + fn remove_recovery(n: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `270 + n * (32 ±0)` - // Estimated: `7670` - // Minimum execution time: 29_088_000 picoseconds. - Weight::from_parts(30_535_744, 7670) + // Estimated: `3854` + // Minimum execution time: 33_453_000 picoseconds. + Weight::from_parts(34_078_626, 3854) + // Standard Error: 2_563 + .saturating_add(Weight::from_parts(78_179, 0).saturating_mul(n.into())) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } @@ -308,8 +309,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `281` // Estimated: `3545` - // Minimum execution time: 12_712_000 picoseconds. - Weight::from_parts(12_980_000, 3545) + // Minimum execution time: 12_196_000 picoseconds. + Weight::from_parts(12_580_000, 3545) .saturating_add(RocksDbWeight::get().reads(1_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } diff --git a/frame/referenda/src/weights.rs b/frame/referenda/src/weights.rs index 3ac0f54ed..464e60dc5 100644 --- a/frame/referenda/src/weights.rs +++ b/frame/referenda/src/weights.rs @@ -18,9 +18,9 @@ //! Autogenerated weights for pallet_referenda //! //! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev -//! DATE: 2023-03-16, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! DATE: 2023-04-06, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` //! WORST CASE MAP SIZE: `1000000` -//! HOSTNAME: `bm3`, CPU: `Intel(R) Core(TM) i7-7700K CPU @ 4.20GHz` +//! HOSTNAME: `bm2`, CPU: `Intel(R) Core(TM) i7-7700K CPU @ 4.20GHz` //! EXECUTION: Some(Wasm), WASM-EXECUTION: Compiled, CHAIN: Some("dev"), DB CACHE: 1024 // Executed Command: @@ -31,9 +31,6 @@ // --steps=50 // --repeat=20 // --pallet=pallet_referenda -// --no-storage-info -// --no-median-slopes -// --no-min-squares // --extrinsic=* // --execution=wasm // --wasm-execution=compiled @@ -95,9 +92,9 @@ impl WeightInfo for SubstrateWeight { fn submit() -> Weight { // Proof Size summary in bytes: // Measured: `220` - // Estimated: `111976` - // Minimum execution time: 36_851_000 picoseconds. - Weight::from_parts(37_183_000, 111976) + // Estimated: `110487` + // Minimum execution time: 42_285_000 picoseconds. + Weight::from_parts(42_646_000, 110487) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) } @@ -108,9 +105,9 @@ impl WeightInfo for SubstrateWeight { fn place_decision_deposit_preparing() -> Weight { // Proof Size summary in bytes: // Measured: `473` - // Estimated: `223815` - // Minimum execution time: 49_435_000 picoseconds. - Weight::from_parts(50_056_000, 223815) + // Estimated: `219984` + // Minimum execution time: 53_455_000 picoseconds. + Weight::from_parts(54_034_000, 219984) .saturating_add(T::DbWeight::get().reads(3_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) } @@ -123,9 +120,9 @@ impl WeightInfo for SubstrateWeight { fn place_decision_deposit_queued() -> Weight { // Proof Size summary in bytes: // Measured: `3107` - // Estimated: `12787` - // Minimum execution time: 47_061_000 picoseconds. - Weight::from_parts(47_579_000, 12787) + // Estimated: `5477` + // Minimum execution time: 50_138_000 picoseconds. + Weight::from_parts(50_449_000, 5477) .saturating_add(T::DbWeight::get().reads(3_u64)) .saturating_add(T::DbWeight::get().writes(2_u64)) } @@ -138,9 +135,9 @@ impl WeightInfo for SubstrateWeight { fn place_decision_deposit_not_queued() -> Weight { // Proof Size summary in bytes: // Measured: `3127` - // Estimated: `12787` - // Minimum execution time: 46_513_000 picoseconds. - Weight::from_parts(47_212_000, 12787) + // Estimated: `5477` + // Minimum execution time: 49_707_000 picoseconds. + Weight::from_parts(50_246_000, 5477) .saturating_add(T::DbWeight::get().reads(3_u64)) .saturating_add(T::DbWeight::get().writes(2_u64)) } @@ -153,9 +150,9 @@ impl WeightInfo for SubstrateWeight { fn place_decision_deposit_passing() -> Weight { // Proof Size summary in bytes: // Measured: `473` - // Estimated: `227294` - // Minimum execution time: 59_255_000 picoseconds. - Weight::from_parts(60_114_000, 227294) + // Estimated: `219984` + // Minimum execution time: 62_880_000 picoseconds. + Weight::from_parts(63_579_000, 219984) .saturating_add(T::DbWeight::get().reads(4_u64)) .saturating_add(T::DbWeight::get().writes(4_u64)) } @@ -168,9 +165,9 @@ impl WeightInfo for SubstrateWeight { fn place_decision_deposit_failing() -> Weight { // Proof Size summary in bytes: // Measured: `473` - // Estimated: `227294` - // Minimum execution time: 57_927_000 picoseconds. - Weight::from_parts(58_625_000, 227294) + // Estimated: `219984` + // Minimum execution time: 60_827_000 picoseconds. + Weight::from_parts(61_392_000, 219984) .saturating_add(T::DbWeight::get().reads(4_u64)) .saturating_add(T::DbWeight::get().writes(4_u64)) } @@ -180,8 +177,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `351` // Estimated: `3831` - // Minimum execution time: 27_186_000 picoseconds. - Weight::from_parts(27_551_000, 3831) + // Minimum execution time: 31_991_000 picoseconds. + Weight::from_parts(32_474_000, 3831) .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -191,8 +188,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `341` // Estimated: `3831` - // Minimum execution time: 27_339_000 picoseconds. - Weight::from_parts(27_828_000, 3831) + // Minimum execution time: 32_162_000 picoseconds. + Weight::from_parts(32_776_000, 3831) .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -203,9 +200,9 @@ impl WeightInfo for SubstrateWeight { fn cancel() -> Weight { // Proof Size summary in bytes: // Measured: `381` - // Estimated: `223815` - // Minimum execution time: 37_081_000 picoseconds. - Weight::from_parts(38_110_000, 223815) + // Estimated: `219984` + // Minimum execution time: 37_493_000 picoseconds. + Weight::from_parts(37_979_000, 219984) .saturating_add(T::DbWeight::get().reads(3_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) } @@ -218,9 +215,9 @@ impl WeightInfo for SubstrateWeight { fn kill() -> Weight { // Proof Size summary in bytes: // Measured: `622` - // Estimated: `227332` - // Minimum execution time: 70_195_000 picoseconds. - Weight::from_parts(71_451_000, 227332) + // Estimated: `219984` + // Minimum execution time: 80_095_000 picoseconds. + Weight::from_parts(80_831_000, 219984) .saturating_add(T::DbWeight::get().reads(4_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) } @@ -231,9 +228,9 @@ impl WeightInfo for SubstrateWeight { fn one_fewer_deciding_queue_empty() -> Weight { // Proof Size summary in bytes: // Measured: `174` - // Estimated: `8956` - // Minimum execution time: 10_748_000 picoseconds. - Weight::from_parts(10_912_000, 8956) + // Estimated: `5477` + // Minimum execution time: 10_906_000 picoseconds. + Weight::from_parts(11_055_000, 5477) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -246,9 +243,9 @@ impl WeightInfo for SubstrateWeight { fn one_fewer_deciding_failing() -> Weight { // Proof Size summary in bytes: // Measured: `4567` - // Estimated: `229292` - // Minimum execution time: 89_144_000 picoseconds. - Weight::from_parts(90_552_000, 229292) + // Estimated: `219984` + // Minimum execution time: 90_747_000 picoseconds. + Weight::from_parts(91_407_000, 219984) .saturating_add(T::DbWeight::get().reads(4_u64)) .saturating_add(T::DbWeight::get().writes(4_u64)) } @@ -261,9 +258,9 @@ impl WeightInfo for SubstrateWeight { fn one_fewer_deciding_passing() -> Weight { // Proof Size summary in bytes: // Measured: `4567` - // Estimated: `229292` - // Minimum execution time: 92_164_000 picoseconds. - Weight::from_parts(92_947_000, 229292) + // Estimated: `219984` + // Minimum execution time: 93_615_000 picoseconds. + Weight::from_parts(94_245_000, 219984) .saturating_add(T::DbWeight::get().reads(4_u64)) .saturating_add(T::DbWeight::get().writes(4_u64)) } @@ -276,9 +273,9 @@ impl WeightInfo for SubstrateWeight { fn nudge_referendum_requeued_insertion() -> Weight { // Proof Size summary in bytes: // Measured: `4588` - // Estimated: `119795` - // Minimum execution time: 59_212_000 picoseconds. - Weight::from_parts(59_843_000, 119795) + // Estimated: `110487` + // Minimum execution time: 60_945_000 picoseconds. + Weight::from_parts(61_246_000, 110487) .saturating_add(T::DbWeight::get().reads(3_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) } @@ -291,9 +288,9 @@ impl WeightInfo for SubstrateWeight { fn nudge_referendum_requeued_slide() -> Weight { // Proof Size summary in bytes: // Measured: `4574` - // Estimated: `119795` - // Minimum execution time: 59_049_000 picoseconds. - Weight::from_parts(59_760_000, 119795) + // Estimated: `110487` + // Minimum execution time: 60_105_000 picoseconds. + Weight::from_parts(60_544_000, 110487) .saturating_add(T::DbWeight::get().reads(3_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) } @@ -308,9 +305,9 @@ impl WeightInfo for SubstrateWeight { fn nudge_referendum_queued() -> Weight { // Proof Size summary in bytes: // Measured: `4548` - // Estimated: `123274` - // Minimum execution time: 61_774_000 picoseconds. - Weight::from_parts(62_480_000, 123274) + // Estimated: `110487` + // Minimum execution time: 62_251_000 picoseconds. + Weight::from_parts(62_952_000, 110487) .saturating_add(T::DbWeight::get().reads(4_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) } @@ -325,9 +322,9 @@ impl WeightInfo for SubstrateWeight { fn nudge_referendum_not_queued() -> Weight { // Proof Size summary in bytes: // Measured: `4582` - // Estimated: `123274` - // Minimum execution time: 61_605_000 picoseconds. - Weight::from_parts(62_385_000, 123274) + // Estimated: `110487` + // Minimum execution time: 61_527_000 picoseconds. + Weight::from_parts(62_082_000, 110487) .saturating_add(T::DbWeight::get().reads(4_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) } @@ -338,9 +335,9 @@ impl WeightInfo for SubstrateWeight { fn nudge_referendum_no_deposit() -> Weight { // Proof Size summary in bytes: // Measured: `333` - // Estimated: `114318` - // Minimum execution time: 24_423_000 picoseconds. - Weight::from_parts(24_747_000, 114318) + // Estimated: `110487` + // Minimum execution time: 24_897_000 picoseconds. + Weight::from_parts(25_213_000, 110487) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().writes(2_u64)) } @@ -351,9 +348,9 @@ impl WeightInfo for SubstrateWeight { fn nudge_referendum_preparing() -> Weight { // Proof Size summary in bytes: // Measured: `381` - // Estimated: `114318` - // Minimum execution time: 24_487_000 picoseconds. - Weight::from_parts(24_941_000, 114318) + // Estimated: `110487` + // Minimum execution time: 25_077_000 picoseconds. + Weight::from_parts(25_385_000, 110487) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().writes(2_u64)) } @@ -363,8 +360,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `278` // Estimated: `3831` - // Minimum execution time: 17_454_000 picoseconds. - Weight::from_parts(17_697_000, 3831) + // Minimum execution time: 17_930_000 picoseconds. + Weight::from_parts(18_112_000, 3831) .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -377,9 +374,9 @@ impl WeightInfo for SubstrateWeight { fn nudge_referendum_begin_deciding_failing() -> Weight { // Proof Size summary in bytes: // Measured: `381` - // Estimated: `117797` - // Minimum execution time: 34_721_000 picoseconds. - Weight::from_parts(35_295_000, 117797) + // Estimated: `110487` + // Minimum execution time: 34_405_000 picoseconds. + Weight::from_parts(34_698_000, 110487) .saturating_add(T::DbWeight::get().reads(3_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) } @@ -392,9 +389,9 @@ impl WeightInfo for SubstrateWeight { fn nudge_referendum_begin_deciding_passing() -> Weight { // Proof Size summary in bytes: // Measured: `381` - // Estimated: `117797` - // Minimum execution time: 36_587_000 picoseconds. - Weight::from_parts(37_095_000, 117797) + // Estimated: `110487` + // Minimum execution time: 37_313_000 picoseconds. + Weight::from_parts(37_807_000, 110487) .saturating_add(T::DbWeight::get().reads(3_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) } @@ -405,9 +402,9 @@ impl WeightInfo for SubstrateWeight { fn nudge_referendum_begin_confirming() -> Weight { // Proof Size summary in bytes: // Measured: `434` - // Estimated: `114318` - // Minimum execution time: 30_003_000 picoseconds. - Weight::from_parts(30_541_000, 114318) + // Estimated: `110487` + // Minimum execution time: 30_552_000 picoseconds. + Weight::from_parts(30_817_000, 110487) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().writes(2_u64)) } @@ -418,9 +415,9 @@ impl WeightInfo for SubstrateWeight { fn nudge_referendum_end_confirming() -> Weight { // Proof Size summary in bytes: // Measured: `417` - // Estimated: `114318` - // Minimum execution time: 30_479_000 picoseconds. - Weight::from_parts(30_900_000, 114318) + // Estimated: `110487` + // Minimum execution time: 31_100_000 picoseconds. + Weight::from_parts(31_696_000, 110487) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().writes(2_u64)) } @@ -431,9 +428,9 @@ impl WeightInfo for SubstrateWeight { fn nudge_referendum_continue_not_confirming() -> Weight { // Proof Size summary in bytes: // Measured: `434` - // Estimated: `114318` - // Minimum execution time: 27_657_000 picoseconds. - Weight::from_parts(28_054_000, 114318) + // Estimated: `110487` + // Minimum execution time: 28_777_000 picoseconds. + Weight::from_parts(29_188_000, 110487) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().writes(2_u64)) } @@ -444,9 +441,9 @@ impl WeightInfo for SubstrateWeight { fn nudge_referendum_continue_confirming() -> Weight { // Proof Size summary in bytes: // Measured: `438` - // Estimated: `114318` - // Minimum execution time: 26_713_000 picoseconds. - Weight::from_parts(27_284_000, 114318) + // Estimated: `110487` + // Minimum execution time: 26_986_000 picoseconds. + Weight::from_parts(27_283_000, 110487) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().writes(2_u64)) } @@ -459,9 +456,9 @@ impl WeightInfo for SubstrateWeight { fn nudge_referendum_approved() -> Weight { // Proof Size summary in bytes: // Measured: `438` - // Estimated: `227328` - // Minimum execution time: 42_374_000 picoseconds. - Weight::from_parts(43_142_000, 227328) + // Estimated: `219984` + // Minimum execution time: 43_538_000 picoseconds. + Weight::from_parts(44_671_000, 219984) .saturating_add(T::DbWeight::get().reads(4_u64)) .saturating_add(T::DbWeight::get().writes(4_u64)) } @@ -472,9 +469,9 @@ impl WeightInfo for SubstrateWeight { fn nudge_referendum_rejected() -> Weight { // Proof Size summary in bytes: // Measured: `434` - // Estimated: `114318` - // Minimum execution time: 30_213_000 picoseconds. - Weight::from_parts(30_633_000, 114318) + // Estimated: `110487` + // Minimum execution time: 30_559_000 picoseconds. + Weight::from_parts(31_294_000, 110487) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().writes(2_u64)) } @@ -487,9 +484,9 @@ impl WeightInfo for SubstrateWeight { fn set_some_metadata() -> Weight { // Proof Size summary in bytes: // Measured: `422` - // Estimated: `7387` - // Minimum execution time: 20_887_000 picoseconds. - Weight::from_parts(21_242_000, 7387) + // Estimated: `3831` + // Minimum execution time: 21_196_000 picoseconds. + Weight::from_parts(21_593_000, 3831) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -500,9 +497,9 @@ impl WeightInfo for SubstrateWeight { fn clear_metadata() -> Weight { // Proof Size summary in bytes: // Measured: `355` - // Estimated: `7348` - // Minimum execution time: 18_702_000 picoseconds. - Weight::from_parts(18_880_000, 7348) + // Estimated: `3831` + // Minimum execution time: 18_827_000 picoseconds. + Weight::from_parts(19_171_000, 3831) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -519,9 +516,9 @@ impl WeightInfo for () { fn submit() -> Weight { // Proof Size summary in bytes: // Measured: `220` - // Estimated: `111976` - // Minimum execution time: 36_851_000 picoseconds. - Weight::from_parts(37_183_000, 111976) + // Estimated: `110487` + // Minimum execution time: 42_285_000 picoseconds. + Weight::from_parts(42_646_000, 110487) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) } @@ -532,9 +529,9 @@ impl WeightInfo for () { fn place_decision_deposit_preparing() -> Weight { // Proof Size summary in bytes: // Measured: `473` - // Estimated: `223815` - // Minimum execution time: 49_435_000 picoseconds. - Weight::from_parts(50_056_000, 223815) + // Estimated: `219984` + // Minimum execution time: 53_455_000 picoseconds. + Weight::from_parts(54_034_000, 219984) .saturating_add(RocksDbWeight::get().reads(3_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) } @@ -547,9 +544,9 @@ impl WeightInfo for () { fn place_decision_deposit_queued() -> Weight { // Proof Size summary in bytes: // Measured: `3107` - // Estimated: `12787` - // Minimum execution time: 47_061_000 picoseconds. - Weight::from_parts(47_579_000, 12787) + // Estimated: `5477` + // Minimum execution time: 50_138_000 picoseconds. + Weight::from_parts(50_449_000, 5477) .saturating_add(RocksDbWeight::get().reads(3_u64)) .saturating_add(RocksDbWeight::get().writes(2_u64)) } @@ -562,9 +559,9 @@ impl WeightInfo for () { fn place_decision_deposit_not_queued() -> Weight { // Proof Size summary in bytes: // Measured: `3127` - // Estimated: `12787` - // Minimum execution time: 46_513_000 picoseconds. - Weight::from_parts(47_212_000, 12787) + // Estimated: `5477` + // Minimum execution time: 49_707_000 picoseconds. + Weight::from_parts(50_246_000, 5477) .saturating_add(RocksDbWeight::get().reads(3_u64)) .saturating_add(RocksDbWeight::get().writes(2_u64)) } @@ -577,9 +574,9 @@ impl WeightInfo for () { fn place_decision_deposit_passing() -> Weight { // Proof Size summary in bytes: // Measured: `473` - // Estimated: `227294` - // Minimum execution time: 59_255_000 picoseconds. - Weight::from_parts(60_114_000, 227294) + // Estimated: `219984` + // Minimum execution time: 62_880_000 picoseconds. + Weight::from_parts(63_579_000, 219984) .saturating_add(RocksDbWeight::get().reads(4_u64)) .saturating_add(RocksDbWeight::get().writes(4_u64)) } @@ -592,9 +589,9 @@ impl WeightInfo for () { fn place_decision_deposit_failing() -> Weight { // Proof Size summary in bytes: // Measured: `473` - // Estimated: `227294` - // Minimum execution time: 57_927_000 picoseconds. - Weight::from_parts(58_625_000, 227294) + // Estimated: `219984` + // Minimum execution time: 60_827_000 picoseconds. + Weight::from_parts(61_392_000, 219984) .saturating_add(RocksDbWeight::get().reads(4_u64)) .saturating_add(RocksDbWeight::get().writes(4_u64)) } @@ -604,8 +601,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `351` // Estimated: `3831` - // Minimum execution time: 27_186_000 picoseconds. - Weight::from_parts(27_551_000, 3831) + // Minimum execution time: 31_991_000 picoseconds. + Weight::from_parts(32_474_000, 3831) .saturating_add(RocksDbWeight::get().reads(1_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } @@ -615,8 +612,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `341` // Estimated: `3831` - // Minimum execution time: 27_339_000 picoseconds. - Weight::from_parts(27_828_000, 3831) + // Minimum execution time: 32_162_000 picoseconds. + Weight::from_parts(32_776_000, 3831) .saturating_add(RocksDbWeight::get().reads(1_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } @@ -627,9 +624,9 @@ impl WeightInfo for () { fn cancel() -> Weight { // Proof Size summary in bytes: // Measured: `381` - // Estimated: `223815` - // Minimum execution time: 37_081_000 picoseconds. - Weight::from_parts(38_110_000, 223815) + // Estimated: `219984` + // Minimum execution time: 37_493_000 picoseconds. + Weight::from_parts(37_979_000, 219984) .saturating_add(RocksDbWeight::get().reads(3_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) } @@ -642,9 +639,9 @@ impl WeightInfo for () { fn kill() -> Weight { // Proof Size summary in bytes: // Measured: `622` - // Estimated: `227332` - // Minimum execution time: 70_195_000 picoseconds. - Weight::from_parts(71_451_000, 227332) + // Estimated: `219984` + // Minimum execution time: 80_095_000 picoseconds. + Weight::from_parts(80_831_000, 219984) .saturating_add(RocksDbWeight::get().reads(4_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) } @@ -655,9 +652,9 @@ impl WeightInfo for () { fn one_fewer_deciding_queue_empty() -> Weight { // Proof Size summary in bytes: // Measured: `174` - // Estimated: `8956` - // Minimum execution time: 10_748_000 picoseconds. - Weight::from_parts(10_912_000, 8956) + // Estimated: `5477` + // Minimum execution time: 10_906_000 picoseconds. + Weight::from_parts(11_055_000, 5477) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } @@ -670,9 +667,9 @@ impl WeightInfo for () { fn one_fewer_deciding_failing() -> Weight { // Proof Size summary in bytes: // Measured: `4567` - // Estimated: `229292` - // Minimum execution time: 89_144_000 picoseconds. - Weight::from_parts(90_552_000, 229292) + // Estimated: `219984` + // Minimum execution time: 90_747_000 picoseconds. + Weight::from_parts(91_407_000, 219984) .saturating_add(RocksDbWeight::get().reads(4_u64)) .saturating_add(RocksDbWeight::get().writes(4_u64)) } @@ -685,9 +682,9 @@ impl WeightInfo for () { fn one_fewer_deciding_passing() -> Weight { // Proof Size summary in bytes: // Measured: `4567` - // Estimated: `229292` - // Minimum execution time: 92_164_000 picoseconds. - Weight::from_parts(92_947_000, 229292) + // Estimated: `219984` + // Minimum execution time: 93_615_000 picoseconds. + Weight::from_parts(94_245_000, 219984) .saturating_add(RocksDbWeight::get().reads(4_u64)) .saturating_add(RocksDbWeight::get().writes(4_u64)) } @@ -700,9 +697,9 @@ impl WeightInfo for () { fn nudge_referendum_requeued_insertion() -> Weight { // Proof Size summary in bytes: // Measured: `4588` - // Estimated: `119795` - // Minimum execution time: 59_212_000 picoseconds. - Weight::from_parts(59_843_000, 119795) + // Estimated: `110487` + // Minimum execution time: 60_945_000 picoseconds. + Weight::from_parts(61_246_000, 110487) .saturating_add(RocksDbWeight::get().reads(3_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) } @@ -715,9 +712,9 @@ impl WeightInfo for () { fn nudge_referendum_requeued_slide() -> Weight { // Proof Size summary in bytes: // Measured: `4574` - // Estimated: `119795` - // Minimum execution time: 59_049_000 picoseconds. - Weight::from_parts(59_760_000, 119795) + // Estimated: `110487` + // Minimum execution time: 60_105_000 picoseconds. + Weight::from_parts(60_544_000, 110487) .saturating_add(RocksDbWeight::get().reads(3_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) } @@ -732,9 +729,9 @@ impl WeightInfo for () { fn nudge_referendum_queued() -> Weight { // Proof Size summary in bytes: // Measured: `4548` - // Estimated: `123274` - // Minimum execution time: 61_774_000 picoseconds. - Weight::from_parts(62_480_000, 123274) + // Estimated: `110487` + // Minimum execution time: 62_251_000 picoseconds. + Weight::from_parts(62_952_000, 110487) .saturating_add(RocksDbWeight::get().reads(4_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) } @@ -749,9 +746,9 @@ impl WeightInfo for () { fn nudge_referendum_not_queued() -> Weight { // Proof Size summary in bytes: // Measured: `4582` - // Estimated: `123274` - // Minimum execution time: 61_605_000 picoseconds. - Weight::from_parts(62_385_000, 123274) + // Estimated: `110487` + // Minimum execution time: 61_527_000 picoseconds. + Weight::from_parts(62_082_000, 110487) .saturating_add(RocksDbWeight::get().reads(4_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) } @@ -762,9 +759,9 @@ impl WeightInfo for () { fn nudge_referendum_no_deposit() -> Weight { // Proof Size summary in bytes: // Measured: `333` - // Estimated: `114318` - // Minimum execution time: 24_423_000 picoseconds. - Weight::from_parts(24_747_000, 114318) + // Estimated: `110487` + // Minimum execution time: 24_897_000 picoseconds. + Weight::from_parts(25_213_000, 110487) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().writes(2_u64)) } @@ -775,9 +772,9 @@ impl WeightInfo for () { fn nudge_referendum_preparing() -> Weight { // Proof Size summary in bytes: // Measured: `381` - // Estimated: `114318` - // Minimum execution time: 24_487_000 picoseconds. - Weight::from_parts(24_941_000, 114318) + // Estimated: `110487` + // Minimum execution time: 25_077_000 picoseconds. + Weight::from_parts(25_385_000, 110487) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().writes(2_u64)) } @@ -787,8 +784,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `278` // Estimated: `3831` - // Minimum execution time: 17_454_000 picoseconds. - Weight::from_parts(17_697_000, 3831) + // Minimum execution time: 17_930_000 picoseconds. + Weight::from_parts(18_112_000, 3831) .saturating_add(RocksDbWeight::get().reads(1_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } @@ -801,9 +798,9 @@ impl WeightInfo for () { fn nudge_referendum_begin_deciding_failing() -> Weight { // Proof Size summary in bytes: // Measured: `381` - // Estimated: `117797` - // Minimum execution time: 34_721_000 picoseconds. - Weight::from_parts(35_295_000, 117797) + // Estimated: `110487` + // Minimum execution time: 34_405_000 picoseconds. + Weight::from_parts(34_698_000, 110487) .saturating_add(RocksDbWeight::get().reads(3_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) } @@ -816,9 +813,9 @@ impl WeightInfo for () { fn nudge_referendum_begin_deciding_passing() -> Weight { // Proof Size summary in bytes: // Measured: `381` - // Estimated: `117797` - // Minimum execution time: 36_587_000 picoseconds. - Weight::from_parts(37_095_000, 117797) + // Estimated: `110487` + // Minimum execution time: 37_313_000 picoseconds. + Weight::from_parts(37_807_000, 110487) .saturating_add(RocksDbWeight::get().reads(3_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) } @@ -829,9 +826,9 @@ impl WeightInfo for () { fn nudge_referendum_begin_confirming() -> Weight { // Proof Size summary in bytes: // Measured: `434` - // Estimated: `114318` - // Minimum execution time: 30_003_000 picoseconds. - Weight::from_parts(30_541_000, 114318) + // Estimated: `110487` + // Minimum execution time: 30_552_000 picoseconds. + Weight::from_parts(30_817_000, 110487) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().writes(2_u64)) } @@ -842,9 +839,9 @@ impl WeightInfo for () { fn nudge_referendum_end_confirming() -> Weight { // Proof Size summary in bytes: // Measured: `417` - // Estimated: `114318` - // Minimum execution time: 30_479_000 picoseconds. - Weight::from_parts(30_900_000, 114318) + // Estimated: `110487` + // Minimum execution time: 31_100_000 picoseconds. + Weight::from_parts(31_696_000, 110487) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().writes(2_u64)) } @@ -855,9 +852,9 @@ impl WeightInfo for () { fn nudge_referendum_continue_not_confirming() -> Weight { // Proof Size summary in bytes: // Measured: `434` - // Estimated: `114318` - // Minimum execution time: 27_657_000 picoseconds. - Weight::from_parts(28_054_000, 114318) + // Estimated: `110487` + // Minimum execution time: 28_777_000 picoseconds. + Weight::from_parts(29_188_000, 110487) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().writes(2_u64)) } @@ -868,9 +865,9 @@ impl WeightInfo for () { fn nudge_referendum_continue_confirming() -> Weight { // Proof Size summary in bytes: // Measured: `438` - // Estimated: `114318` - // Minimum execution time: 26_713_000 picoseconds. - Weight::from_parts(27_284_000, 114318) + // Estimated: `110487` + // Minimum execution time: 26_986_000 picoseconds. + Weight::from_parts(27_283_000, 110487) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().writes(2_u64)) } @@ -883,9 +880,9 @@ impl WeightInfo for () { fn nudge_referendum_approved() -> Weight { // Proof Size summary in bytes: // Measured: `438` - // Estimated: `227328` - // Minimum execution time: 42_374_000 picoseconds. - Weight::from_parts(43_142_000, 227328) + // Estimated: `219984` + // Minimum execution time: 43_538_000 picoseconds. + Weight::from_parts(44_671_000, 219984) .saturating_add(RocksDbWeight::get().reads(4_u64)) .saturating_add(RocksDbWeight::get().writes(4_u64)) } @@ -896,9 +893,9 @@ impl WeightInfo for () { fn nudge_referendum_rejected() -> Weight { // Proof Size summary in bytes: // Measured: `434` - // Estimated: `114318` - // Minimum execution time: 30_213_000 picoseconds. - Weight::from_parts(30_633_000, 114318) + // Estimated: `110487` + // Minimum execution time: 30_559_000 picoseconds. + Weight::from_parts(31_294_000, 110487) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().writes(2_u64)) } @@ -911,9 +908,9 @@ impl WeightInfo for () { fn set_some_metadata() -> Weight { // Proof Size summary in bytes: // Measured: `422` - // Estimated: `7387` - // Minimum execution time: 20_887_000 picoseconds. - Weight::from_parts(21_242_000, 7387) + // Estimated: `3831` + // Minimum execution time: 21_196_000 picoseconds. + Weight::from_parts(21_593_000, 3831) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } @@ -924,9 +921,9 @@ impl WeightInfo for () { fn clear_metadata() -> Weight { // Proof Size summary in bytes: // Measured: `355` - // Estimated: `7348` - // Minimum execution time: 18_702_000 picoseconds. - Weight::from_parts(18_880_000, 7348) + // Estimated: `3831` + // Minimum execution time: 18_827_000 picoseconds. + Weight::from_parts(19_171_000, 3831) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } diff --git a/frame/remark/src/weights.rs b/frame/remark/src/weights.rs index bb2e91b5d..9aa56eb33 100644 --- a/frame/remark/src/weights.rs +++ b/frame/remark/src/weights.rs @@ -18,9 +18,9 @@ //! Autogenerated weights for pallet_remark //! //! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev -//! DATE: 2023-03-16, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! DATE: 2023-04-06, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` //! WORST CASE MAP SIZE: `1000000` -//! HOSTNAME: `bm3`, CPU: `Intel(R) Core(TM) i7-7700K CPU @ 4.20GHz` +//! HOSTNAME: `bm2`, CPU: `Intel(R) Core(TM) i7-7700K CPU @ 4.20GHz` //! EXECUTION: Some(Wasm), WASM-EXECUTION: Compiled, CHAIN: Some("dev"), DB CACHE: 1024 // Executed Command: @@ -31,9 +31,6 @@ // --steps=50 // --repeat=20 // --pallet=pallet_remark -// --no-storage-info -// --no-median-slopes -// --no-min-squares // --extrinsic=* // --execution=wasm // --wasm-execution=compiled @@ -62,10 +59,10 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 9_284_000 picoseconds. - Weight::from_parts(4_054_843, 0) - // Standard Error: 2 - .saturating_add(Weight::from_parts(1_094, 0).saturating_mul(l.into())) + // Minimum execution time: 9_301_000 picoseconds. + Weight::from_parts(2_516_065, 0) + // Standard Error: 1 + .saturating_add(Weight::from_parts(1_089, 0).saturating_mul(l.into())) } } @@ -76,9 +73,9 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 9_284_000 picoseconds. - Weight::from_parts(4_054_843, 0) - // Standard Error: 2 - .saturating_add(Weight::from_parts(1_094, 0).saturating_mul(l.into())) + // Minimum execution time: 9_301_000 picoseconds. + Weight::from_parts(2_516_065, 0) + // Standard Error: 1 + .saturating_add(Weight::from_parts(1_089, 0).saturating_mul(l.into())) } } diff --git a/frame/salary/src/weights.rs b/frame/salary/src/weights.rs index 7195a144a..074bff4da 100644 --- a/frame/salary/src/weights.rs +++ b/frame/salary/src/weights.rs @@ -18,9 +18,9 @@ //! Autogenerated weights for pallet_salary //! //! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev -//! DATE: 2023-03-16, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! DATE: 2023-04-06, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` //! WORST CASE MAP SIZE: `1000000` -//! HOSTNAME: `bm3`, CPU: `Intel(R) Core(TM) i7-7700K CPU @ 4.20GHz` +//! HOSTNAME: `bm2`, CPU: `Intel(R) Core(TM) i7-7700K CPU @ 4.20GHz` //! EXECUTION: Some(Wasm), WASM-EXECUTION: Compiled, CHAIN: Some("dev"), DB CACHE: 1024 // Executed Command: @@ -31,9 +31,6 @@ // --steps=50 // --repeat=20 // --pallet=pallet_salary -// --no-storage-info -// --no-median-slopes -// --no-min-squares // --extrinsic=* // --execution=wasm // --wasm-execution=compiled @@ -69,8 +66,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `4` // Estimated: `1541` - // Minimum execution time: 11_732_000 picoseconds. - Weight::from_parts(12_162_000, 1541) + // Minimum execution time: 11_785_000 picoseconds. + Weight::from_parts(12_086_000, 1541) .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -80,8 +77,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `86` // Estimated: `1541` - // Minimum execution time: 12_966_000 picoseconds. - Weight::from_parts(13_443_000, 1541) + // Minimum execution time: 12_721_000 picoseconds. + Weight::from_parts(13_033_000, 1541) .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -94,9 +91,9 @@ impl WeightInfo for SubstrateWeight { fn induct() -> Weight { // Proof Size summary in bytes: // Measured: `362` - // Estimated: `8591` - // Minimum execution time: 20_467_000 picoseconds. - Weight::from_parts(20_782_000, 8591) + // Estimated: `3543` + // Minimum execution time: 19_516_000 picoseconds. + Weight::from_parts(19_938_000, 3543) .saturating_add(T::DbWeight::get().reads(3_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -109,9 +106,9 @@ impl WeightInfo for SubstrateWeight { fn register() -> Weight { // Proof Size summary in bytes: // Measured: `429` - // Estimated: `8591` - // Minimum execution time: 23_547_000 picoseconds. - Weight::from_parts(23_911_000, 8591) + // Estimated: `3543` + // Minimum execution time: 23_145_000 picoseconds. + Weight::from_parts(23_804_000, 3543) .saturating_add(T::DbWeight::get().reads(3_u64)) .saturating_add(T::DbWeight::get().writes(2_u64)) } @@ -124,9 +121,9 @@ impl WeightInfo for SubstrateWeight { fn payout() -> Weight { // Proof Size summary in bytes: // Measured: `429` - // Estimated: `8591` - // Minimum execution time: 47_145_000 picoseconds. - Weight::from_parts(47_778_000, 8591) + // Estimated: `3543` + // Minimum execution time: 62_187_000 picoseconds. + Weight::from_parts(63_016_000, 3543) .saturating_add(T::DbWeight::get().reads(3_u64)) .saturating_add(T::DbWeight::get().writes(2_u64)) } @@ -141,9 +138,9 @@ impl WeightInfo for SubstrateWeight { fn payout_other() -> Weight { // Proof Size summary in bytes: // Measured: `429` - // Estimated: `12184` - // Minimum execution time: 48_763_000 picoseconds. - Weight::from_parts(49_853_000, 12184) + // Estimated: `3593` + // Minimum execution time: 63_828_000 picoseconds. + Weight::from_parts(64_791_000, 3593) .saturating_add(T::DbWeight::get().reads(4_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) } @@ -154,9 +151,9 @@ impl WeightInfo for SubstrateWeight { fn check_payment() -> Weight { // Proof Size summary in bytes: // Measured: `170` - // Estimated: `5084` - // Minimum execution time: 12_825_000 picoseconds. - Weight::from_parts(13_143_000, 5084) + // Estimated: `3543` + // Minimum execution time: 12_911_000 picoseconds. + Weight::from_parts(13_079_000, 3543) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().writes(2_u64)) } @@ -170,8 +167,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `4` // Estimated: `1541` - // Minimum execution time: 11_732_000 picoseconds. - Weight::from_parts(12_162_000, 1541) + // Minimum execution time: 11_785_000 picoseconds. + Weight::from_parts(12_086_000, 1541) .saturating_add(RocksDbWeight::get().reads(1_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } @@ -181,8 +178,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `86` // Estimated: `1541` - // Minimum execution time: 12_966_000 picoseconds. - Weight::from_parts(13_443_000, 1541) + // Minimum execution time: 12_721_000 picoseconds. + Weight::from_parts(13_033_000, 1541) .saturating_add(RocksDbWeight::get().reads(1_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } @@ -195,9 +192,9 @@ impl WeightInfo for () { fn induct() -> Weight { // Proof Size summary in bytes: // Measured: `362` - // Estimated: `8591` - // Minimum execution time: 20_467_000 picoseconds. - Weight::from_parts(20_782_000, 8591) + // Estimated: `3543` + // Minimum execution time: 19_516_000 picoseconds. + Weight::from_parts(19_938_000, 3543) .saturating_add(RocksDbWeight::get().reads(3_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } @@ -210,9 +207,9 @@ impl WeightInfo for () { fn register() -> Weight { // Proof Size summary in bytes: // Measured: `429` - // Estimated: `8591` - // Minimum execution time: 23_547_000 picoseconds. - Weight::from_parts(23_911_000, 8591) + // Estimated: `3543` + // Minimum execution time: 23_145_000 picoseconds. + Weight::from_parts(23_804_000, 3543) .saturating_add(RocksDbWeight::get().reads(3_u64)) .saturating_add(RocksDbWeight::get().writes(2_u64)) } @@ -225,9 +222,9 @@ impl WeightInfo for () { fn payout() -> Weight { // Proof Size summary in bytes: // Measured: `429` - // Estimated: `8591` - // Minimum execution time: 47_145_000 picoseconds. - Weight::from_parts(47_778_000, 8591) + // Estimated: `3543` + // Minimum execution time: 62_187_000 picoseconds. + Weight::from_parts(63_016_000, 3543) .saturating_add(RocksDbWeight::get().reads(3_u64)) .saturating_add(RocksDbWeight::get().writes(2_u64)) } @@ -242,9 +239,9 @@ impl WeightInfo for () { fn payout_other() -> Weight { // Proof Size summary in bytes: // Measured: `429` - // Estimated: `12184` - // Minimum execution time: 48_763_000 picoseconds. - Weight::from_parts(49_853_000, 12184) + // Estimated: `3593` + // Minimum execution time: 63_828_000 picoseconds. + Weight::from_parts(64_791_000, 3593) .saturating_add(RocksDbWeight::get().reads(4_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) } @@ -255,9 +252,9 @@ impl WeightInfo for () { fn check_payment() -> Weight { // Proof Size summary in bytes: // Measured: `170` - // Estimated: `5084` - // Minimum execution time: 12_825_000 picoseconds. - Weight::from_parts(13_143_000, 5084) + // Estimated: `3543` + // Minimum execution time: 12_911_000 picoseconds. + Weight::from_parts(13_079_000, 3543) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().writes(2_u64)) } diff --git a/frame/scheduler/src/weights.rs b/frame/scheduler/src/weights.rs index bccb50265..897363f13 100644 --- a/frame/scheduler/src/weights.rs +++ b/frame/scheduler/src/weights.rs @@ -18,9 +18,9 @@ //! Autogenerated weights for pallet_scheduler //! //! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev -//! DATE: 2023-03-16, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! DATE: 2023-04-06, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` //! WORST CASE MAP SIZE: `1000000` -//! HOSTNAME: `bm3`, CPU: `Intel(R) Core(TM) i7-7700K CPU @ 4.20GHz` +//! HOSTNAME: `bm2`, CPU: `Intel(R) Core(TM) i7-7700K CPU @ 4.20GHz` //! EXECUTION: Some(Wasm), WASM-EXECUTION: Compiled, CHAIN: Some("dev"), DB CACHE: 1024 // Executed Command: @@ -31,9 +31,6 @@ // --steps=50 // --repeat=20 // --pallet=pallet_scheduler -// --no-storage-info -// --no-median-slopes -// --no-min-squares // --extrinsic=* // --execution=wasm // --wasm-execution=compiled @@ -74,8 +71,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `31` // Estimated: `1489` - // Minimum execution time: 3_965_000 picoseconds. - Weight::from_parts(4_126_000, 1489) + // Minimum execution time: 3_776_000 picoseconds. + Weight::from_parts(3_992_000, 1489) .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -86,10 +83,10 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `81 + s * (177 ±0)` // Estimated: `110487` - // Minimum execution time: 3_662_000 picoseconds. - Weight::from_parts(8_746_597, 110487) - // Standard Error: 754 - .saturating_add(Weight::from_parts(309_350, 0).saturating_mul(s.into())) + // Minimum execution time: 3_418_000 picoseconds. + Weight::from_parts(8_606_012, 110487) + // Standard Error: 769 + .saturating_add(Weight::from_parts(309_376, 0).saturating_mul(s.into())) .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -97,8 +94,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 5_807_000 picoseconds. - Weight::from_parts(5_963_000, 0) + // Minimum execution time: 5_624_000 picoseconds. + Weight::from_parts(5_758_000, 0) } /// Storage: Preimage PreimageFor (r:1 w:1) /// Proof: Preimage PreimageFor (max_values: None, max_size: Some(4194344), added: 4196819, mode: Measured) @@ -108,11 +105,11 @@ impl WeightInfo for SubstrateWeight { fn service_task_fetched(s: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `179 + s * (1 ±0)` - // Estimated: `7200 + s * (1 ±0)` - // Minimum execution time: 20_188_000 picoseconds. - Weight::from_parts(20_422_000, 7200) - // Standard Error: 1 - .saturating_add(Weight::from_parts(1_143, 0).saturating_mul(s.into())) + // Estimated: `3644 + s * (1 ±0)` + // Minimum execution time: 20_150_000 picoseconds. + Weight::from_parts(20_271_000, 3644) + // Standard Error: 0 + .saturating_add(Weight::from_parts(1_132, 0).saturating_mul(s.into())) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().writes(2_u64)) .saturating_add(Weight::from_parts(0, 1).saturating_mul(s.into())) @@ -123,30 +120,30 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 7_787_000 picoseconds. - Weight::from_parts(8_046_000, 0) + // Minimum execution time: 7_451_000 picoseconds. + Weight::from_parts(7_693_000, 0) .saturating_add(T::DbWeight::get().writes(1_u64)) } fn service_task_periodic() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 5_730_000 picoseconds. - Weight::from_parts(5_901_000, 0) + // Minimum execution time: 5_477_000 picoseconds. + Weight::from_parts(5_733_000, 0) } fn execute_dispatch_signed() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 2_569_000 picoseconds. - Weight::from_parts(2_710_000, 0) + // Minimum execution time: 2_675_000 picoseconds. + Weight::from_parts(2_870_000, 0) } fn execute_dispatch_unsigned() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 2_611_000 picoseconds. - Weight::from_parts(2_765_000, 0) + // Minimum execution time: 2_697_000 picoseconds. + Weight::from_parts(2_807_000, 0) } /// Storage: Scheduler Agenda (r:1 w:1) /// Proof: Scheduler Agenda (max_values: None, max_size: Some(107022), added: 109497, mode: MaxEncodedLen) @@ -155,10 +152,10 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `81 + s * (177 ±0)` // Estimated: `110487` - // Minimum execution time: 14_237_000 picoseconds. - Weight::from_parts(18_553_929, 110487) - // Standard Error: 757 - .saturating_add(Weight::from_parts(327_347, 0).saturating_mul(s.into())) + // Minimum execution time: 13_921_000 picoseconds. + Weight::from_parts(18_717_223, 110487) + // Standard Error: 771 + .saturating_add(Weight::from_parts(333_102, 0).saturating_mul(s.into())) .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -171,10 +168,10 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `81 + s * (177 ±0)` // Estimated: `110487` - // Minimum execution time: 18_003_000 picoseconds. - Weight::from_parts(19_521_283, 110487) - // Standard Error: 901 - .saturating_add(Weight::from_parts(496_401, 0).saturating_mul(s.into())) + // Minimum execution time: 17_552_000 picoseconds. + Weight::from_parts(19_006_016, 110487) + // Standard Error: 1_115 + .saturating_add(Weight::from_parts(495_979, 0).saturating_mul(s.into())) .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(T::DbWeight::get().writes(2_u64)) } @@ -186,11 +183,11 @@ impl WeightInfo for SubstrateWeight { fn schedule_named(s: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `596 + s * (178 ±0)` - // Estimated: `114000` - // Minimum execution time: 17_713_000 picoseconds. - Weight::from_parts(24_442_945, 114000) - // Standard Error: 764 - .saturating_add(Weight::from_parts(330_307, 0).saturating_mul(s.into())) + // Estimated: `110487` + // Minimum execution time: 17_240_000 picoseconds. + Weight::from_parts(24_376_370, 110487) + // Standard Error: 928 + .saturating_add(Weight::from_parts(331_209, 0).saturating_mul(s.into())) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().writes(2_u64)) } @@ -202,11 +199,11 @@ impl WeightInfo for SubstrateWeight { fn cancel_named(s: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `709 + s * (177 ±0)` - // Estimated: `114000` - // Minimum execution time: 19_808_000 picoseconds. - Weight::from_parts(22_601_896, 114000) - // Standard Error: 991 - .saturating_add(Weight::from_parts(496_702, 0).saturating_mul(s.into())) + // Estimated: `110487` + // Minimum execution time: 19_731_000 picoseconds. + Weight::from_parts(23_787_948, 110487) + // Standard Error: 1_133 + .saturating_add(Weight::from_parts(503_805, 0).saturating_mul(s.into())) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().writes(2_u64)) } @@ -220,8 +217,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `31` // Estimated: `1489` - // Minimum execution time: 3_965_000 picoseconds. - Weight::from_parts(4_126_000, 1489) + // Minimum execution time: 3_776_000 picoseconds. + Weight::from_parts(3_992_000, 1489) .saturating_add(RocksDbWeight::get().reads(1_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } @@ -232,10 +229,10 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `81 + s * (177 ±0)` // Estimated: `110487` - // Minimum execution time: 3_662_000 picoseconds. - Weight::from_parts(8_746_597, 110487) - // Standard Error: 754 - .saturating_add(Weight::from_parts(309_350, 0).saturating_mul(s.into())) + // Minimum execution time: 3_418_000 picoseconds. + Weight::from_parts(8_606_012, 110487) + // Standard Error: 769 + .saturating_add(Weight::from_parts(309_376, 0).saturating_mul(s.into())) .saturating_add(RocksDbWeight::get().reads(1_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } @@ -243,8 +240,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 5_807_000 picoseconds. - Weight::from_parts(5_963_000, 0) + // Minimum execution time: 5_624_000 picoseconds. + Weight::from_parts(5_758_000, 0) } /// Storage: Preimage PreimageFor (r:1 w:1) /// Proof: Preimage PreimageFor (max_values: None, max_size: Some(4194344), added: 4196819, mode: Measured) @@ -254,11 +251,11 @@ impl WeightInfo for () { fn service_task_fetched(s: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `179 + s * (1 ±0)` - // Estimated: `7200 + s * (1 ±0)` - // Minimum execution time: 20_188_000 picoseconds. - Weight::from_parts(20_422_000, 7200) - // Standard Error: 1 - .saturating_add(Weight::from_parts(1_143, 0).saturating_mul(s.into())) + // Estimated: `3644 + s * (1 ±0)` + // Minimum execution time: 20_150_000 picoseconds. + Weight::from_parts(20_271_000, 3644) + // Standard Error: 0 + .saturating_add(Weight::from_parts(1_132, 0).saturating_mul(s.into())) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().writes(2_u64)) .saturating_add(Weight::from_parts(0, 1).saturating_mul(s.into())) @@ -269,30 +266,30 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 7_787_000 picoseconds. - Weight::from_parts(8_046_000, 0) + // Minimum execution time: 7_451_000 picoseconds. + Weight::from_parts(7_693_000, 0) .saturating_add(RocksDbWeight::get().writes(1_u64)) } fn service_task_periodic() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 5_730_000 picoseconds. - Weight::from_parts(5_901_000, 0) + // Minimum execution time: 5_477_000 picoseconds. + Weight::from_parts(5_733_000, 0) } fn execute_dispatch_signed() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 2_569_000 picoseconds. - Weight::from_parts(2_710_000, 0) + // Minimum execution time: 2_675_000 picoseconds. + Weight::from_parts(2_870_000, 0) } fn execute_dispatch_unsigned() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 2_611_000 picoseconds. - Weight::from_parts(2_765_000, 0) + // Minimum execution time: 2_697_000 picoseconds. + Weight::from_parts(2_807_000, 0) } /// Storage: Scheduler Agenda (r:1 w:1) /// Proof: Scheduler Agenda (max_values: None, max_size: Some(107022), added: 109497, mode: MaxEncodedLen) @@ -301,10 +298,10 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `81 + s * (177 ±0)` // Estimated: `110487` - // Minimum execution time: 14_237_000 picoseconds. - Weight::from_parts(18_553_929, 110487) - // Standard Error: 757 - .saturating_add(Weight::from_parts(327_347, 0).saturating_mul(s.into())) + // Minimum execution time: 13_921_000 picoseconds. + Weight::from_parts(18_717_223, 110487) + // Standard Error: 771 + .saturating_add(Weight::from_parts(333_102, 0).saturating_mul(s.into())) .saturating_add(RocksDbWeight::get().reads(1_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } @@ -317,10 +314,10 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `81 + s * (177 ±0)` // Estimated: `110487` - // Minimum execution time: 18_003_000 picoseconds. - Weight::from_parts(19_521_283, 110487) - // Standard Error: 901 - .saturating_add(Weight::from_parts(496_401, 0).saturating_mul(s.into())) + // Minimum execution time: 17_552_000 picoseconds. + Weight::from_parts(19_006_016, 110487) + // Standard Error: 1_115 + .saturating_add(Weight::from_parts(495_979, 0).saturating_mul(s.into())) .saturating_add(RocksDbWeight::get().reads(1_u64)) .saturating_add(RocksDbWeight::get().writes(2_u64)) } @@ -332,11 +329,11 @@ impl WeightInfo for () { fn schedule_named(s: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `596 + s * (178 ±0)` - // Estimated: `114000` - // Minimum execution time: 17_713_000 picoseconds. - Weight::from_parts(24_442_945, 114000) - // Standard Error: 764 - .saturating_add(Weight::from_parts(330_307, 0).saturating_mul(s.into())) + // Estimated: `110487` + // Minimum execution time: 17_240_000 picoseconds. + Weight::from_parts(24_376_370, 110487) + // Standard Error: 928 + .saturating_add(Weight::from_parts(331_209, 0).saturating_mul(s.into())) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().writes(2_u64)) } @@ -348,11 +345,11 @@ impl WeightInfo for () { fn cancel_named(s: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `709 + s * (177 ±0)` - // Estimated: `114000` - // Minimum execution time: 19_808_000 picoseconds. - Weight::from_parts(22_601_896, 114000) - // Standard Error: 991 - .saturating_add(Weight::from_parts(496_702, 0).saturating_mul(s.into())) + // Estimated: `110487` + // Minimum execution time: 19_731_000 picoseconds. + Weight::from_parts(23_787_948, 110487) + // Standard Error: 1_133 + .saturating_add(Weight::from_parts(503_805, 0).saturating_mul(s.into())) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().writes(2_u64)) } diff --git a/frame/session/src/weights.rs b/frame/session/src/weights.rs index 3515ff446..add7f3335 100644 --- a/frame/session/src/weights.rs +++ b/frame/session/src/weights.rs @@ -18,9 +18,9 @@ //! Autogenerated weights for pallet_session //! //! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev -//! DATE: 2023-03-16, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! DATE: 2023-04-06, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` //! WORST CASE MAP SIZE: `1000000` -//! HOSTNAME: `bm3`, CPU: `Intel(R) Core(TM) i7-7700K CPU @ 4.20GHz` +//! HOSTNAME: `bm2`, CPU: `Intel(R) Core(TM) i7-7700K CPU @ 4.20GHz` //! EXECUTION: Some(Wasm), WASM-EXECUTION: Compiled, CHAIN: Some("dev"), DB CACHE: 1024 // Executed Command: @@ -31,9 +31,6 @@ // --steps=50 // --repeat=20 // --pallet=pallet_session -// --no-storage-info -// --no-median-slopes -// --no-min-squares // --extrinsic=* // --execution=wasm // --wasm-execution=compiled @@ -67,9 +64,9 @@ impl WeightInfo for SubstrateWeight { fn set_keys() -> Weight { // Proof Size summary in bytes: // Measured: `1891` - // Estimated: `22693` - // Minimum execution time: 48_165_000 picoseconds. - Weight::from_parts(48_967_000, 22693) + // Estimated: `12781` + // Minimum execution time: 48_507_000 picoseconds. + Weight::from_parts(49_214_000, 12781) .saturating_add(T::DbWeight::get().reads(6_u64)) .saturating_add(T::DbWeight::get().writes(5_u64)) } @@ -82,9 +79,9 @@ impl WeightInfo for SubstrateWeight { fn purge_keys() -> Weight { // Proof Size summary in bytes: // Measured: `1758` - // Estimated: `11537` - // Minimum execution time: 35_824_000 picoseconds. - Weight::from_parts(36_267_000, 11537) + // Estimated: `5223` + // Minimum execution time: 35_388_000 picoseconds. + Weight::from_parts(35_763_000, 5223) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().writes(5_u64)) } @@ -101,9 +98,9 @@ impl WeightInfo for () { fn set_keys() -> Weight { // Proof Size summary in bytes: // Measured: `1891` - // Estimated: `22693` - // Minimum execution time: 48_165_000 picoseconds. - Weight::from_parts(48_967_000, 22693) + // Estimated: `12781` + // Minimum execution time: 48_507_000 picoseconds. + Weight::from_parts(49_214_000, 12781) .saturating_add(RocksDbWeight::get().reads(6_u64)) .saturating_add(RocksDbWeight::get().writes(5_u64)) } @@ -116,9 +113,9 @@ impl WeightInfo for () { fn purge_keys() -> Weight { // Proof Size summary in bytes: // Measured: `1758` - // Estimated: `11537` - // Minimum execution time: 35_824_000 picoseconds. - Weight::from_parts(36_267_000, 11537) + // Estimated: `5223` + // Minimum execution time: 35_388_000 picoseconds. + Weight::from_parts(35_763_000, 5223) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().writes(5_u64)) } diff --git a/frame/staking/src/weights.rs b/frame/staking/src/weights.rs index 01d874d55..34b01445d 100644 --- a/frame/staking/src/weights.rs +++ b/frame/staking/src/weights.rs @@ -18,9 +18,9 @@ //! Autogenerated weights for pallet_staking //! //! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev -//! DATE: 2023-03-16, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! DATE: 2023-04-06, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` //! WORST CASE MAP SIZE: `1000000` -//! HOSTNAME: `bm3`, CPU: `Intel(R) Core(TM) i7-7700K CPU @ 4.20GHz` +//! HOSTNAME: `bm2`, CPU: `Intel(R) Core(TM) i7-7700K CPU @ 4.20GHz` //! EXECUTION: Some(Wasm), WASM-EXECUTION: Compiled, CHAIN: Some("dev"), DB CACHE: 1024 // Executed Command: @@ -31,9 +31,6 @@ // --steps=50 // --repeat=20 // --pallet=pallet_staking -// --no-storage-info -// --no-median-slopes -// --no-min-squares // --extrinsic=* // --execution=wasm // --wasm-execution=compiled @@ -94,15 +91,17 @@ impl WeightInfo for SubstrateWeight { /// Proof: Staking CurrentEra (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) /// Storage: Balances Locks (r:1 w:1) /// Proof: Balances Locks (max_values: None, max_size: Some(1299), added: 3774, mode: MaxEncodedLen) + /// Storage: Balances Freezes (r:1 w:0) + /// Proof: Balances Freezes (max_values: None, max_size: Some(49), added: 2524, mode: MaxEncodedLen) /// Storage: Staking Payee (r:0 w:1) /// Proof: Staking Payee (max_values: None, max_size: Some(73), added: 2548, mode: MaxEncodedLen) fn bond() -> Weight { // Proof Size summary in bytes: // Measured: `1047` - // Estimated: `14346` - // Minimum execution time: 46_401_000 picoseconds. - Weight::from_parts(46_987_000, 14346) - .saturating_add(T::DbWeight::get().reads(4_u64)) + // Estimated: `4764` + // Minimum execution time: 54_907_000 picoseconds. + Weight::from_parts(55_685_000, 4764) + .saturating_add(T::DbWeight::get().reads(5_u64)) .saturating_add(T::DbWeight::get().writes(4_u64)) } /// Storage: Staking Bonded (r:1 w:0) @@ -111,6 +110,8 @@ impl WeightInfo for SubstrateWeight { /// Proof: Staking Ledger (max_values: None, max_size: Some(1091), added: 3566, mode: MaxEncodedLen) /// Storage: Balances Locks (r:1 w:1) /// Proof: Balances Locks (max_values: None, max_size: Some(1299), added: 3774, mode: MaxEncodedLen) + /// Storage: Balances Freezes (r:1 w:0) + /// Proof: Balances Freezes (max_values: None, max_size: Some(49), added: 2524, mode: MaxEncodedLen) /// Storage: VoterList ListNodes (r:3 w:3) /// Proof: VoterList ListNodes (max_values: None, max_size: Some(154), added: 2629, mode: MaxEncodedLen) /// Storage: VoterList ListBags (r:2 w:2) @@ -118,10 +119,10 @@ impl WeightInfo for SubstrateWeight { fn bond_extra() -> Weight { // Proof Size summary in bytes: // Measured: `2028` - // Estimated: `27838` - // Minimum execution time: 87_755_000 picoseconds. - Weight::from_parts(88_802_000, 27838) - .saturating_add(T::DbWeight::get().reads(8_u64)) + // Estimated: `8877` + // Minimum execution time: 94_779_000 picoseconds. + Weight::from_parts(95_455_000, 8877) + .saturating_add(T::DbWeight::get().reads(9_u64)) .saturating_add(T::DbWeight::get().writes(7_u64)) } /// Storage: Staking Ledger (r:1 w:1) @@ -134,6 +135,8 @@ impl WeightInfo for SubstrateWeight { /// Proof: Staking CurrentEra (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) /// Storage: Balances Locks (r:1 w:1) /// Proof: Balances Locks (max_values: None, max_size: Some(1299), added: 3774, mode: MaxEncodedLen) + /// Storage: Balances Freezes (r:1 w:0) + /// Proof: Balances Freezes (max_values: None, max_size: Some(49), added: 2524, mode: MaxEncodedLen) /// Storage: System Account (r:1 w:1) /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) /// Storage: VoterList ListNodes (r:3 w:3) @@ -145,10 +148,10 @@ impl WeightInfo for SubstrateWeight { fn unbond() -> Weight { // Proof Size summary in bytes: // Measured: `2233` - // Estimated: `38444` - // Minimum execution time: 95_698_000 picoseconds. - Weight::from_parts(96_971_000, 38444) - .saturating_add(T::DbWeight::get().reads(12_u64)) + // Estimated: `8877` + // Minimum execution time: 98_004_000 picoseconds. + Weight::from_parts(98_730_000, 8877) + .saturating_add(T::DbWeight::get().reads(13_u64)) .saturating_add(T::DbWeight::get().writes(8_u64)) } /// Storage: Staking Ledger (r:1 w:1) @@ -157,18 +160,20 @@ impl WeightInfo for SubstrateWeight { /// Proof: Staking CurrentEra (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) /// Storage: Balances Locks (r:1 w:1) /// Proof: Balances Locks (max_values: None, max_size: Some(1299), added: 3774, mode: MaxEncodedLen) + /// Storage: Balances Freezes (r:1 w:0) + /// Proof: Balances Freezes (max_values: None, max_size: Some(49), added: 2524, mode: MaxEncodedLen) /// Storage: System Account (r:1 w:1) /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) /// The range of component `s` is `[0, 100]`. fn withdraw_unbonded_update(s: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `1021` - // Estimated: `14402` - // Minimum execution time: 38_239_000 picoseconds. - Weight::from_parts(39_615_452, 14402) - // Standard Error: 447 - .saturating_add(Weight::from_parts(9_199, 0).saturating_mul(s.into())) - .saturating_add(T::DbWeight::get().reads(4_u64)) + // Estimated: `4764` + // Minimum execution time: 45_888_000 picoseconds. + Weight::from_parts(47_568_327, 4764) + // Standard Error: 402 + .saturating_add(Weight::from_parts(7_520, 0).saturating_mul(s.into())) + .saturating_add(T::DbWeight::get().reads(5_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) } /// Storage: Staking Ledger (r:1 w:1) @@ -195,6 +200,8 @@ impl WeightInfo for SubstrateWeight { /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) /// Storage: Balances Locks (r:1 w:1) /// Proof: Balances Locks (max_values: None, max_size: Some(1299), added: 3774, mode: MaxEncodedLen) + /// Storage: Balances Freezes (r:1 w:0) + /// Proof: Balances Freezes (max_values: None, max_size: Some(49), added: 2524, mode: MaxEncodedLen) /// Storage: Staking Payee (r:0 w:1) /// Proof: Staking Payee (max_values: None, max_size: Some(73), added: 2548, mode: MaxEncodedLen) /// Storage: Staking SpanSlash (r:0 w:100) @@ -203,12 +210,12 @@ impl WeightInfo for SubstrateWeight { fn withdraw_unbonded_kill(s: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `2294 + s * (4 ±0)` - // Estimated: `43999 + s * (4 ±0)` - // Minimum execution time: 84_251_000 picoseconds. - Weight::from_parts(90_113_319, 43999) - // Standard Error: 2_177 - .saturating_add(Weight::from_parts(1_278_840, 0).saturating_mul(s.into())) - .saturating_add(T::DbWeight::get().reads(13_u64)) + // Estimated: `6248 + s * (4 ±0)` + // Minimum execution time: 93_288_000 picoseconds. + Weight::from_parts(99_415_523, 6248) + // Standard Error: 3_291 + .saturating_add(Weight::from_parts(1_296_734, 0).saturating_mul(s.into())) + .saturating_add(T::DbWeight::get().reads(14_u64)) .saturating_add(T::DbWeight::get().writes(12_u64)) .saturating_add(T::DbWeight::get().writes((1_u64).saturating_mul(s.into()))) .saturating_add(Weight::from_parts(0, 4).saturating_mul(s.into())) @@ -238,9 +245,9 @@ impl WeightInfo for SubstrateWeight { fn validate() -> Weight { // Proof Size summary in bytes: // Measured: `1414` - // Estimated: `30249` - // Minimum execution time: 60_509_000 picoseconds. - Weight::from_parts(61_305_000, 30249) + // Estimated: `4556` + // Minimum execution time: 58_755_000 picoseconds. + Weight::from_parts(59_424_000, 4556) .saturating_add(T::DbWeight::get().reads(11_u64)) .saturating_add(T::DbWeight::get().writes(5_u64)) } @@ -252,11 +259,11 @@ impl WeightInfo for SubstrateWeight { fn kick(k: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `1260 + k * (569 ±0)` - // Estimated: `5546 + k * (3033 ±0)` - // Minimum execution time: 30_063_000 picoseconds. - Weight::from_parts(31_774_698, 5546) - // Standard Error: 11_088 - .saturating_add(Weight::from_parts(7_869_325, 0).saturating_mul(k.into())) + // Estimated: `4556 + k * (3033 ±0)` + // Minimum execution time: 29_399_000 picoseconds. + Weight::from_parts(30_443_621, 4556) + // Standard Error: 10_402 + .saturating_add(Weight::from_parts(7_890_220, 0).saturating_mul(k.into())) .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(T::DbWeight::get().reads((1_u64).saturating_mul(k.into()))) .saturating_add(T::DbWeight::get().writes((1_u64).saturating_mul(k.into()))) @@ -288,11 +295,11 @@ impl WeightInfo for SubstrateWeight { fn nominate(n: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `1888 + n * (105 ±0)` - // Estimated: `32878 + n * (2520 ±0)` - // Minimum execution time: 70_091_000 picoseconds. - Weight::from_parts(67_744_973, 32878) - // Standard Error: 12_799 - .saturating_add(Weight::from_parts(3_238_224, 0).saturating_mul(n.into())) + // Estimated: `6248 + n * (2520 ±0)` + // Minimum execution time: 68_471_000 picoseconds. + Weight::from_parts(65_972_990, 6248) + // Standard Error: 13_983 + .saturating_add(Weight::from_parts(3_255_731, 0).saturating_mul(n.into())) .saturating_add(T::DbWeight::get().reads(12_u64)) .saturating_add(T::DbWeight::get().reads((1_u64).saturating_mul(n.into()))) .saturating_add(T::DbWeight::get().writes(6_u64)) @@ -315,9 +322,9 @@ impl WeightInfo for SubstrateWeight { fn chill() -> Weight { // Proof Size summary in bytes: // Measured: `1748` - // Estimated: `24862` - // Minimum execution time: 60_582_000 picoseconds. - Weight::from_parts(61_208_000, 24862) + // Estimated: `6248` + // Minimum execution time: 59_537_000 picoseconds. + Weight::from_parts(60_446_000, 6248) .saturating_add(T::DbWeight::get().reads(8_u64)) .saturating_add(T::DbWeight::get().writes(6_u64)) } @@ -329,8 +336,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `808` // Estimated: `4556` - // Minimum execution time: 15_630_000 picoseconds. - Weight::from_parts(16_018_000, 4556) + // Minimum execution time: 15_403_000 picoseconds. + Weight::from_parts(15_676_000, 4556) .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -341,9 +348,9 @@ impl WeightInfo for SubstrateWeight { fn set_controller() -> Weight { // Proof Size summary in bytes: // Measured: `907` - // Estimated: `11659` - // Minimum execution time: 23_895_000 picoseconds. - Weight::from_parts(24_306_000, 11659) + // Estimated: `8122` + // Minimum execution time: 23_316_000 picoseconds. + Weight::from_parts(23_670_000, 8122) .saturating_add(T::DbWeight::get().reads(3_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) } @@ -353,8 +360,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 3_694_000 picoseconds. - Weight::from_parts(3_820_000, 0) + // Minimum execution time: 3_558_000 picoseconds. + Weight::from_parts(3_759_000, 0) .saturating_add(T::DbWeight::get().writes(1_u64)) } /// Storage: Staking ForceEra (r:0 w:1) @@ -363,8 +370,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 10_729_000 picoseconds. - Weight::from_parts(11_086_000, 0) + // Minimum execution time: 12_724_000 picoseconds. + Weight::from_parts(13_047_000, 0) .saturating_add(T::DbWeight::get().writes(1_u64)) } /// Storage: Staking ForceEra (r:0 w:1) @@ -373,8 +380,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 10_959_000 picoseconds. - Weight::from_parts(11_371_000, 0) + // Minimum execution time: 12_734_000 picoseconds. + Weight::from_parts(13_218_000, 0) .saturating_add(T::DbWeight::get().writes(1_u64)) } /// Storage: Staking ForceEra (r:0 w:1) @@ -383,8 +390,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 11_101_000 picoseconds. - Weight::from_parts(11_506_000, 0) + // Minimum execution time: 12_996_000 picoseconds. + Weight::from_parts(13_375_000, 0) .saturating_add(T::DbWeight::get().writes(1_u64)) } /// Storage: Staking Invulnerables (r:0 w:1) @@ -394,10 +401,10 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 4_016_000 picoseconds. - Weight::from_parts(4_454_020, 0) - // Standard Error: 82 - .saturating_add(Weight::from_parts(10_703, 0).saturating_mul(v.into())) + // Minimum execution time: 3_920_000 picoseconds. + Weight::from_parts(4_619_469, 0) + // Standard Error: 22 + .saturating_add(Weight::from_parts(10_108, 0).saturating_mul(v.into())) .saturating_add(T::DbWeight::get().writes(1_u64)) } /// Storage: Staking Bonded (r:1 w:1) @@ -420,6 +427,8 @@ impl WeightInfo for SubstrateWeight { /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) /// Storage: Balances Locks (r:1 w:1) /// Proof: Balances Locks (max_values: None, max_size: Some(1299), added: 3774, mode: MaxEncodedLen) + /// Storage: Balances Freezes (r:1 w:0) + /// Proof: Balances Freezes (max_values: None, max_size: Some(49), added: 2524, mode: MaxEncodedLen) /// Storage: Staking Ledger (r:0 w:1) /// Proof: Staking Ledger (max_values: None, max_size: Some(1091), added: 3566, mode: MaxEncodedLen) /// Storage: Staking Payee (r:0 w:1) @@ -430,12 +439,12 @@ impl WeightInfo for SubstrateWeight { fn force_unstake(s: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `2018 + s * (4 ±0)` - // Estimated: `37678 + s * (4 ±0)` - // Minimum execution time: 76_338_000 picoseconds. - Weight::from_parts(82_426_098, 37678) - // Standard Error: 3_761 - .saturating_add(Weight::from_parts(1_279_343, 0).saturating_mul(s.into())) - .saturating_add(T::DbWeight::get().reads(11_u64)) + // Estimated: `6248 + s * (4 ±0)` + // Minimum execution time: 86_516_000 picoseconds. + Weight::from_parts(92_324_464, 6248) + // Standard Error: 2_925 + .saturating_add(Weight::from_parts(1_286_284, 0).saturating_mul(s.into())) + .saturating_add(T::DbWeight::get().reads(12_u64)) .saturating_add(T::DbWeight::get().writes(12_u64)) .saturating_add(T::DbWeight::get().writes((1_u64).saturating_mul(s.into()))) .saturating_add(Weight::from_parts(0, 4).saturating_mul(s.into())) @@ -447,10 +456,10 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `66639` // Estimated: `70104` - // Minimum execution time: 91_160_000 picoseconds. - Weight::from_parts(794_662_495, 70104) - // Standard Error: 51_479 - .saturating_add(Weight::from_parts(4_346_694, 0).saturating_mul(s.into())) + // Minimum execution time: 90_193_000 picoseconds. + Weight::from_parts(821_522_318, 70104) + // Standard Error: 57_922 + .saturating_add(Weight::from_parts(4_554_659, 0).saturating_mul(s.into())) .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -476,16 +485,16 @@ impl WeightInfo for SubstrateWeight { fn payout_stakers_dead_controller(n: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `20217 + n * (143 ±0)` - // Estimated: `63416 + n * (8024 ±1)` - // Minimum execution time: 77_845_000 picoseconds. - Weight::from_parts(86_178_186, 63416) - // Standard Error: 21_971 - .saturating_add(Weight::from_parts(26_966_654, 0).saturating_mul(n.into())) + // Estimated: `19844 + n * (2603 ±1)` + // Minimum execution time: 80_329_000 picoseconds. + Weight::from_parts(97_340_643, 19844) + // Standard Error: 22_713 + .saturating_add(Weight::from_parts(29_087_425, 0).saturating_mul(n.into())) .saturating_add(T::DbWeight::get().reads(9_u64)) .saturating_add(T::DbWeight::get().reads((3_u64).saturating_mul(n.into()))) .saturating_add(T::DbWeight::get().writes(2_u64)) .saturating_add(T::DbWeight::get().writes((1_u64).saturating_mul(n.into()))) - .saturating_add(Weight::from_parts(0, 8024).saturating_mul(n.into())) + .saturating_add(Weight::from_parts(0, 2603).saturating_mul(n.into())) } /// Storage: Staking CurrentEra (r:1 w:0) /// Proof: Staking CurrentEra (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) @@ -507,25 +516,29 @@ impl WeightInfo for SubstrateWeight { /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) /// Storage: Balances Locks (r:257 w:257) /// Proof: Balances Locks (max_values: None, max_size: Some(1299), added: 3774, mode: MaxEncodedLen) + /// Storage: Balances Freezes (r:257 w:0) + /// Proof: Balances Freezes (max_values: None, max_size: Some(49), added: 2524, mode: MaxEncodedLen) /// The range of component `n` is `[0, 256]`. fn payout_stakers_alive_staked(n: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `34971 + n * (401 ±0)` - // Estimated: `93244 + n * (15898 ±0)` - // Minimum execution time: 98_209_000 picoseconds. - Weight::from_parts(102_121_432, 93244) - // Standard Error: 25_633 - .saturating_add(Weight::from_parts(39_244_200, 0).saturating_mul(n.into())) - .saturating_add(T::DbWeight::get().reads(10_u64)) - .saturating_add(T::DbWeight::get().reads((5_u64).saturating_mul(n.into()))) + // Estimated: `32376 + n * (3774 ±0)` + // Minimum execution time: 105_591_000 picoseconds. + Weight::from_parts(111_587_915, 32376) + // Standard Error: 15_598 + .saturating_add(Weight::from_parts(48_948_195, 0).saturating_mul(n.into())) + .saturating_add(T::DbWeight::get().reads(11_u64)) + .saturating_add(T::DbWeight::get().reads((6_u64).saturating_mul(n.into()))) .saturating_add(T::DbWeight::get().writes(3_u64)) .saturating_add(T::DbWeight::get().writes((3_u64).saturating_mul(n.into()))) - .saturating_add(Weight::from_parts(0, 15898).saturating_mul(n.into())) + .saturating_add(Weight::from_parts(0, 3774).saturating_mul(n.into())) } /// Storage: Staking Ledger (r:1 w:1) /// Proof: Staking Ledger (max_values: None, max_size: Some(1091), added: 3566, mode: MaxEncodedLen) /// Storage: Balances Locks (r:1 w:1) /// Proof: Balances Locks (max_values: None, max_size: Some(1299), added: 3774, mode: MaxEncodedLen) + /// Storage: Balances Freezes (r:1 w:0) + /// Proof: Balances Freezes (max_values: None, max_size: Some(49), added: 2524, mode: MaxEncodedLen) /// Storage: System Account (r:1 w:1) /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) /// Storage: VoterList ListNodes (r:3 w:3) @@ -538,12 +551,12 @@ impl WeightInfo for SubstrateWeight { fn rebond(l: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `2029 + l * (7 ±0)` - // Estimated: `31431` - // Minimum execution time: 87_067_000 picoseconds. - Weight::from_parts(87_971_483, 31431) - // Standard Error: 1_668 - .saturating_add(Weight::from_parts(56_024, 0).saturating_mul(l.into())) - .saturating_add(T::DbWeight::get().reads(9_u64)) + // Estimated: `8877` + // Minimum execution time: 89_420_000 picoseconds. + Weight::from_parts(90_743_615, 8877) + // Standard Error: 1_260 + .saturating_add(Weight::from_parts(50_832, 0).saturating_mul(l.into())) + .saturating_add(T::DbWeight::get().reads(10_u64)) .saturating_add(T::DbWeight::get().writes(8_u64)) } /// Storage: System Account (r:1 w:1) @@ -568,6 +581,8 @@ impl WeightInfo for SubstrateWeight { /// Proof: VoterList CounterForListNodes (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) /// Storage: Balances Locks (r:1 w:1) /// Proof: Balances Locks (max_values: None, max_size: Some(1299), added: 3774, mode: MaxEncodedLen) + /// Storage: Balances Freezes (r:1 w:0) + /// Proof: Balances Freezes (max_values: None, max_size: Some(49), added: 2524, mode: MaxEncodedLen) /// Storage: Staking Payee (r:0 w:1) /// Proof: Staking Payee (max_values: None, max_size: Some(73), added: 2548, mode: MaxEncodedLen) /// Storage: Staking SpanSlash (r:0 w:100) @@ -576,12 +591,12 @@ impl WeightInfo for SubstrateWeight { fn reap_stash(s: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `2294 + s * (4 ±0)` - // Estimated: `42515 + s * (4 ±0)` - // Minimum execution time: 90_421_000 picoseconds. - Weight::from_parts(91_873_866, 42515) - // Standard Error: 1_867 - .saturating_add(Weight::from_parts(1_275_581, 0).saturating_mul(s.into())) - .saturating_add(T::DbWeight::get().reads(12_u64)) + // Estimated: `6248 + s * (4 ±0)` + // Minimum execution time: 100_911_000 picoseconds. + Weight::from_parts(102_678_006, 6248) + // Standard Error: 2_349 + .saturating_add(Weight::from_parts(1_262_431, 0).saturating_mul(s.into())) + .saturating_add(T::DbWeight::get().reads(13_u64)) .saturating_add(T::DbWeight::get().writes(12_u64)) .saturating_add(T::DbWeight::get().writes((1_u64).saturating_mul(s.into()))) .saturating_add(Weight::from_parts(0, 4).saturating_mul(s.into())) @@ -624,21 +639,21 @@ impl WeightInfo for SubstrateWeight { /// The range of component `n` is `[0, 100]`. fn new_era(v: u32, n: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `0 + v * (3598 ±0) + n * (720 ±0)` - // Estimated: `537749 + v * (16699 ±39) + n * (12763 ±3)` - // Minimum execution time: 552_452_000 picoseconds. - Weight::from_parts(555_250_000, 537749) - // Standard Error: 1_893_951 - .saturating_add(Weight::from_parts(61_059_451, 0).saturating_mul(v.into())) - // Standard Error: 188_721 - .saturating_add(Weight::from_parts(17_220_679, 0).saturating_mul(n.into())) + // Measured: `0 + n * (720 ±0) + v * (3598 ±0)` + // Estimated: `512390 + n * (3566 ±0) + v * (3566 ±0)` + // Minimum execution time: 554_712_000 picoseconds. + Weight::from_parts(556_603_000, 512390) + // Standard Error: 1_925_251 + .saturating_add(Weight::from_parts(62_627_196, 0).saturating_mul(v.into())) + // Standard Error: 191_840 + .saturating_add(Weight::from_parts(16_681_790, 0).saturating_mul(n.into())) .saturating_add(T::DbWeight::get().reads(206_u64)) .saturating_add(T::DbWeight::get().reads((5_u64).saturating_mul(v.into()))) .saturating_add(T::DbWeight::get().reads((4_u64).saturating_mul(n.into()))) .saturating_add(T::DbWeight::get().writes(4_u64)) .saturating_add(T::DbWeight::get().writes((3_u64).saturating_mul(v.into()))) - .saturating_add(Weight::from_parts(0, 16699).saturating_mul(v.into())) - .saturating_add(Weight::from_parts(0, 12763).saturating_mul(n.into())) + .saturating_add(Weight::from_parts(0, 3566).saturating_mul(n.into())) + .saturating_add(Weight::from_parts(0, 3566).saturating_mul(v.into())) } /// Storage: VoterList CounterForListNodes (r:1 w:0) /// Proof: VoterList CounterForListNodes (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) @@ -660,20 +675,20 @@ impl WeightInfo for SubstrateWeight { /// The range of component `n` is `[500, 1000]`. fn get_npos_voters(v: u32, n: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `3135 + v * (395 ±0) + n * (911 ±0)` - // Estimated: `518829 + v * (14295 ±0) + n * (11775 ±0)` - // Minimum execution time: 32_870_065_000 picoseconds. - Weight::from_parts(33_295_716_000, 518829) - // Standard Error: 371_824 - .saturating_add(Weight::from_parts(5_148_979, 0).saturating_mul(v.into())) - // Standard Error: 371_824 - .saturating_add(Weight::from_parts(3_376_262, 0).saturating_mul(n.into())) + // Measured: `3135 + n * (911 ±0) + v * (395 ±0)` + // Estimated: `512390 + n * (3566 ±0) + v * (3566 ±0)` + // Minimum execution time: 31_770_670_000 picoseconds. + Weight::from_parts(31_839_042_000, 512390) + // Standard Error: 355_382 + .saturating_add(Weight::from_parts(5_044_540, 0).saturating_mul(v.into())) + // Standard Error: 355_382 + .saturating_add(Weight::from_parts(3_205_722, 0).saturating_mul(n.into())) .saturating_add(T::DbWeight::get().reads(201_u64)) .saturating_add(T::DbWeight::get().reads((5_u64).saturating_mul(v.into()))) .saturating_add(T::DbWeight::get().reads((4_u64).saturating_mul(n.into()))) .saturating_add(T::DbWeight::get().writes(1_u64)) - .saturating_add(Weight::from_parts(0, 14295).saturating_mul(v.into())) - .saturating_add(Weight::from_parts(0, 11775).saturating_mul(n.into())) + .saturating_add(Weight::from_parts(0, 3566).saturating_mul(n.into())) + .saturating_add(Weight::from_parts(0, 3566).saturating_mul(v.into())) } /// Storage: Staking CounterForValidators (r:1 w:0) /// Proof: Staking CounterForValidators (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) @@ -683,11 +698,11 @@ impl WeightInfo for SubstrateWeight { fn get_npos_targets(v: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `983 + v * (50 ±0)` - // Estimated: `4999 + v * (2520 ±0)` - // Minimum execution time: 2_321_389_000 picoseconds. - Weight::from_parts(70_122_933, 4999) - // Standard Error: 7_249 - .saturating_add(Weight::from_parts(4_632_666, 0).saturating_mul(v.into())) + // Estimated: `3510 + v * (2520 ±0)` + // Minimum execution time: 2_253_567_000 picoseconds. + Weight::from_parts(61_440_613, 3510) + // Standard Error: 5_276 + .saturating_add(Weight::from_parts(4_414_153, 0).saturating_mul(v.into())) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().reads((1_u64).saturating_mul(v.into()))) .saturating_add(Weight::from_parts(0, 2520).saturating_mul(v.into())) @@ -708,8 +723,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 9_581_000 picoseconds. - Weight::from_parts(9_802_000, 0) + // Minimum execution time: 9_292_000 picoseconds. + Weight::from_parts(9_587_000, 0) .saturating_add(T::DbWeight::get().writes(6_u64)) } /// Storage: Staking MinCommission (r:0 w:1) @@ -728,8 +743,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 8_883_000 picoseconds. - Weight::from_parts(9_151_000, 0) + // Minimum execution time: 8_294_000 picoseconds. + Weight::from_parts(8_597_000, 0) .saturating_add(T::DbWeight::get().writes(6_u64)) } /// Storage: Staking Ledger (r:1 w:0) @@ -755,9 +770,9 @@ impl WeightInfo for SubstrateWeight { fn chill_other() -> Weight { // Proof Size summary in bytes: // Measured: `1871` - // Estimated: `29338` - // Minimum execution time: 77_916_000 picoseconds. - Weight::from_parts(78_877_000, 29338) + // Estimated: `6248` + // Minimum execution time: 75_742_000 picoseconds. + Weight::from_parts(76_252_000, 6248) .saturating_add(T::DbWeight::get().reads(11_u64)) .saturating_add(T::DbWeight::get().writes(6_u64)) } @@ -768,9 +783,9 @@ impl WeightInfo for SubstrateWeight { fn force_apply_min_commission() -> Weight { // Proof Size summary in bytes: // Measured: `694` - // Estimated: `4999` - // Minimum execution time: 16_658_000 picoseconds. - Weight::from_parts(16_968_000, 4999) + // Estimated: `3510` + // Minimum execution time: 16_407_000 picoseconds. + Weight::from_parts(16_726_000, 3510) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -780,8 +795,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 4_982_000 picoseconds. - Weight::from_parts(5_232_000, 0) + // Minimum execution time: 4_977_000 picoseconds. + Weight::from_parts(5_224_000, 0) .saturating_add(T::DbWeight::get().writes(1_u64)) } } @@ -796,15 +811,17 @@ impl WeightInfo for () { /// Proof: Staking CurrentEra (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) /// Storage: Balances Locks (r:1 w:1) /// Proof: Balances Locks (max_values: None, max_size: Some(1299), added: 3774, mode: MaxEncodedLen) + /// Storage: Balances Freezes (r:1 w:0) + /// Proof: Balances Freezes (max_values: None, max_size: Some(49), added: 2524, mode: MaxEncodedLen) /// Storage: Staking Payee (r:0 w:1) /// Proof: Staking Payee (max_values: None, max_size: Some(73), added: 2548, mode: MaxEncodedLen) fn bond() -> Weight { // Proof Size summary in bytes: // Measured: `1047` - // Estimated: `14346` - // Minimum execution time: 46_401_000 picoseconds. - Weight::from_parts(46_987_000, 14346) - .saturating_add(RocksDbWeight::get().reads(4_u64)) + // Estimated: `4764` + // Minimum execution time: 54_907_000 picoseconds. + Weight::from_parts(55_685_000, 4764) + .saturating_add(RocksDbWeight::get().reads(5_u64)) .saturating_add(RocksDbWeight::get().writes(4_u64)) } /// Storage: Staking Bonded (r:1 w:0) @@ -813,6 +830,8 @@ impl WeightInfo for () { /// Proof: Staking Ledger (max_values: None, max_size: Some(1091), added: 3566, mode: MaxEncodedLen) /// Storage: Balances Locks (r:1 w:1) /// Proof: Balances Locks (max_values: None, max_size: Some(1299), added: 3774, mode: MaxEncodedLen) + /// Storage: Balances Freezes (r:1 w:0) + /// Proof: Balances Freezes (max_values: None, max_size: Some(49), added: 2524, mode: MaxEncodedLen) /// Storage: VoterList ListNodes (r:3 w:3) /// Proof: VoterList ListNodes (max_values: None, max_size: Some(154), added: 2629, mode: MaxEncodedLen) /// Storage: VoterList ListBags (r:2 w:2) @@ -820,10 +839,10 @@ impl WeightInfo for () { fn bond_extra() -> Weight { // Proof Size summary in bytes: // Measured: `2028` - // Estimated: `27838` - // Minimum execution time: 87_755_000 picoseconds. - Weight::from_parts(88_802_000, 27838) - .saturating_add(RocksDbWeight::get().reads(8_u64)) + // Estimated: `8877` + // Minimum execution time: 94_779_000 picoseconds. + Weight::from_parts(95_455_000, 8877) + .saturating_add(RocksDbWeight::get().reads(9_u64)) .saturating_add(RocksDbWeight::get().writes(7_u64)) } /// Storage: Staking Ledger (r:1 w:1) @@ -836,6 +855,8 @@ impl WeightInfo for () { /// Proof: Staking CurrentEra (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) /// Storage: Balances Locks (r:1 w:1) /// Proof: Balances Locks (max_values: None, max_size: Some(1299), added: 3774, mode: MaxEncodedLen) + /// Storage: Balances Freezes (r:1 w:0) + /// Proof: Balances Freezes (max_values: None, max_size: Some(49), added: 2524, mode: MaxEncodedLen) /// Storage: System Account (r:1 w:1) /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) /// Storage: VoterList ListNodes (r:3 w:3) @@ -847,10 +868,10 @@ impl WeightInfo for () { fn unbond() -> Weight { // Proof Size summary in bytes: // Measured: `2233` - // Estimated: `38444` - // Minimum execution time: 95_698_000 picoseconds. - Weight::from_parts(96_971_000, 38444) - .saturating_add(RocksDbWeight::get().reads(12_u64)) + // Estimated: `8877` + // Minimum execution time: 98_004_000 picoseconds. + Weight::from_parts(98_730_000, 8877) + .saturating_add(RocksDbWeight::get().reads(13_u64)) .saturating_add(RocksDbWeight::get().writes(8_u64)) } /// Storage: Staking Ledger (r:1 w:1) @@ -859,18 +880,20 @@ impl WeightInfo for () { /// Proof: Staking CurrentEra (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) /// Storage: Balances Locks (r:1 w:1) /// Proof: Balances Locks (max_values: None, max_size: Some(1299), added: 3774, mode: MaxEncodedLen) + /// Storage: Balances Freezes (r:1 w:0) + /// Proof: Balances Freezes (max_values: None, max_size: Some(49), added: 2524, mode: MaxEncodedLen) /// Storage: System Account (r:1 w:1) /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) /// The range of component `s` is `[0, 100]`. fn withdraw_unbonded_update(s: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `1021` - // Estimated: `14402` - // Minimum execution time: 38_239_000 picoseconds. - Weight::from_parts(39_615_452, 14402) - // Standard Error: 447 - .saturating_add(Weight::from_parts(9_199, 0).saturating_mul(s.into())) - .saturating_add(RocksDbWeight::get().reads(4_u64)) + // Estimated: `4764` + // Minimum execution time: 45_888_000 picoseconds. + Weight::from_parts(47_568_327, 4764) + // Standard Error: 402 + .saturating_add(Weight::from_parts(7_520, 0).saturating_mul(s.into())) + .saturating_add(RocksDbWeight::get().reads(5_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) } /// Storage: Staking Ledger (r:1 w:1) @@ -897,6 +920,8 @@ impl WeightInfo for () { /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) /// Storage: Balances Locks (r:1 w:1) /// Proof: Balances Locks (max_values: None, max_size: Some(1299), added: 3774, mode: MaxEncodedLen) + /// Storage: Balances Freezes (r:1 w:0) + /// Proof: Balances Freezes (max_values: None, max_size: Some(49), added: 2524, mode: MaxEncodedLen) /// Storage: Staking Payee (r:0 w:1) /// Proof: Staking Payee (max_values: None, max_size: Some(73), added: 2548, mode: MaxEncodedLen) /// Storage: Staking SpanSlash (r:0 w:100) @@ -905,12 +930,12 @@ impl WeightInfo for () { fn withdraw_unbonded_kill(s: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `2294 + s * (4 ±0)` - // Estimated: `43999 + s * (4 ±0)` - // Minimum execution time: 84_251_000 picoseconds. - Weight::from_parts(90_113_319, 43999) - // Standard Error: 2_177 - .saturating_add(Weight::from_parts(1_278_840, 0).saturating_mul(s.into())) - .saturating_add(RocksDbWeight::get().reads(13_u64)) + // Estimated: `6248 + s * (4 ±0)` + // Minimum execution time: 93_288_000 picoseconds. + Weight::from_parts(99_415_523, 6248) + // Standard Error: 3_291 + .saturating_add(Weight::from_parts(1_296_734, 0).saturating_mul(s.into())) + .saturating_add(RocksDbWeight::get().reads(14_u64)) .saturating_add(RocksDbWeight::get().writes(12_u64)) .saturating_add(RocksDbWeight::get().writes((1_u64).saturating_mul(s.into()))) .saturating_add(Weight::from_parts(0, 4).saturating_mul(s.into())) @@ -940,9 +965,9 @@ impl WeightInfo for () { fn validate() -> Weight { // Proof Size summary in bytes: // Measured: `1414` - // Estimated: `30249` - // Minimum execution time: 60_509_000 picoseconds. - Weight::from_parts(61_305_000, 30249) + // Estimated: `4556` + // Minimum execution time: 58_755_000 picoseconds. + Weight::from_parts(59_424_000, 4556) .saturating_add(RocksDbWeight::get().reads(11_u64)) .saturating_add(RocksDbWeight::get().writes(5_u64)) } @@ -954,11 +979,11 @@ impl WeightInfo for () { fn kick(k: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `1260 + k * (569 ±0)` - // Estimated: `5546 + k * (3033 ±0)` - // Minimum execution time: 30_063_000 picoseconds. - Weight::from_parts(31_774_698, 5546) - // Standard Error: 11_088 - .saturating_add(Weight::from_parts(7_869_325, 0).saturating_mul(k.into())) + // Estimated: `4556 + k * (3033 ±0)` + // Minimum execution time: 29_399_000 picoseconds. + Weight::from_parts(30_443_621, 4556) + // Standard Error: 10_402 + .saturating_add(Weight::from_parts(7_890_220, 0).saturating_mul(k.into())) .saturating_add(RocksDbWeight::get().reads(1_u64)) .saturating_add(RocksDbWeight::get().reads((1_u64).saturating_mul(k.into()))) .saturating_add(RocksDbWeight::get().writes((1_u64).saturating_mul(k.into()))) @@ -990,11 +1015,11 @@ impl WeightInfo for () { fn nominate(n: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `1888 + n * (105 ±0)` - // Estimated: `32878 + n * (2520 ±0)` - // Minimum execution time: 70_091_000 picoseconds. - Weight::from_parts(67_744_973, 32878) - // Standard Error: 12_799 - .saturating_add(Weight::from_parts(3_238_224, 0).saturating_mul(n.into())) + // Estimated: `6248 + n * (2520 ±0)` + // Minimum execution time: 68_471_000 picoseconds. + Weight::from_parts(65_972_990, 6248) + // Standard Error: 13_983 + .saturating_add(Weight::from_parts(3_255_731, 0).saturating_mul(n.into())) .saturating_add(RocksDbWeight::get().reads(12_u64)) .saturating_add(RocksDbWeight::get().reads((1_u64).saturating_mul(n.into()))) .saturating_add(RocksDbWeight::get().writes(6_u64)) @@ -1017,9 +1042,9 @@ impl WeightInfo for () { fn chill() -> Weight { // Proof Size summary in bytes: // Measured: `1748` - // Estimated: `24862` - // Minimum execution time: 60_582_000 picoseconds. - Weight::from_parts(61_208_000, 24862) + // Estimated: `6248` + // Minimum execution time: 59_537_000 picoseconds. + Weight::from_parts(60_446_000, 6248) .saturating_add(RocksDbWeight::get().reads(8_u64)) .saturating_add(RocksDbWeight::get().writes(6_u64)) } @@ -1031,8 +1056,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `808` // Estimated: `4556` - // Minimum execution time: 15_630_000 picoseconds. - Weight::from_parts(16_018_000, 4556) + // Minimum execution time: 15_403_000 picoseconds. + Weight::from_parts(15_676_000, 4556) .saturating_add(RocksDbWeight::get().reads(1_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } @@ -1043,9 +1068,9 @@ impl WeightInfo for () { fn set_controller() -> Weight { // Proof Size summary in bytes: // Measured: `907` - // Estimated: `11659` - // Minimum execution time: 23_895_000 picoseconds. - Weight::from_parts(24_306_000, 11659) + // Estimated: `8122` + // Minimum execution time: 23_316_000 picoseconds. + Weight::from_parts(23_670_000, 8122) .saturating_add(RocksDbWeight::get().reads(3_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) } @@ -1055,8 +1080,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 3_694_000 picoseconds. - Weight::from_parts(3_820_000, 0) + // Minimum execution time: 3_558_000 picoseconds. + Weight::from_parts(3_759_000, 0) .saturating_add(RocksDbWeight::get().writes(1_u64)) } /// Storage: Staking ForceEra (r:0 w:1) @@ -1065,8 +1090,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 10_729_000 picoseconds. - Weight::from_parts(11_086_000, 0) + // Minimum execution time: 12_724_000 picoseconds. + Weight::from_parts(13_047_000, 0) .saturating_add(RocksDbWeight::get().writes(1_u64)) } /// Storage: Staking ForceEra (r:0 w:1) @@ -1075,8 +1100,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 10_959_000 picoseconds. - Weight::from_parts(11_371_000, 0) + // Minimum execution time: 12_734_000 picoseconds. + Weight::from_parts(13_218_000, 0) .saturating_add(RocksDbWeight::get().writes(1_u64)) } /// Storage: Staking ForceEra (r:0 w:1) @@ -1085,8 +1110,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 11_101_000 picoseconds. - Weight::from_parts(11_506_000, 0) + // Minimum execution time: 12_996_000 picoseconds. + Weight::from_parts(13_375_000, 0) .saturating_add(RocksDbWeight::get().writes(1_u64)) } /// Storage: Staking Invulnerables (r:0 w:1) @@ -1096,10 +1121,10 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 4_016_000 picoseconds. - Weight::from_parts(4_454_020, 0) - // Standard Error: 82 - .saturating_add(Weight::from_parts(10_703, 0).saturating_mul(v.into())) + // Minimum execution time: 3_920_000 picoseconds. + Weight::from_parts(4_619_469, 0) + // Standard Error: 22 + .saturating_add(Weight::from_parts(10_108, 0).saturating_mul(v.into())) .saturating_add(RocksDbWeight::get().writes(1_u64)) } /// Storage: Staking Bonded (r:1 w:1) @@ -1122,6 +1147,8 @@ impl WeightInfo for () { /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) /// Storage: Balances Locks (r:1 w:1) /// Proof: Balances Locks (max_values: None, max_size: Some(1299), added: 3774, mode: MaxEncodedLen) + /// Storage: Balances Freezes (r:1 w:0) + /// Proof: Balances Freezes (max_values: None, max_size: Some(49), added: 2524, mode: MaxEncodedLen) /// Storage: Staking Ledger (r:0 w:1) /// Proof: Staking Ledger (max_values: None, max_size: Some(1091), added: 3566, mode: MaxEncodedLen) /// Storage: Staking Payee (r:0 w:1) @@ -1132,12 +1159,12 @@ impl WeightInfo for () { fn force_unstake(s: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `2018 + s * (4 ±0)` - // Estimated: `37678 + s * (4 ±0)` - // Minimum execution time: 76_338_000 picoseconds. - Weight::from_parts(82_426_098, 37678) - // Standard Error: 3_761 - .saturating_add(Weight::from_parts(1_279_343, 0).saturating_mul(s.into())) - .saturating_add(RocksDbWeight::get().reads(11_u64)) + // Estimated: `6248 + s * (4 ±0)` + // Minimum execution time: 86_516_000 picoseconds. + Weight::from_parts(92_324_464, 6248) + // Standard Error: 2_925 + .saturating_add(Weight::from_parts(1_286_284, 0).saturating_mul(s.into())) + .saturating_add(RocksDbWeight::get().reads(12_u64)) .saturating_add(RocksDbWeight::get().writes(12_u64)) .saturating_add(RocksDbWeight::get().writes((1_u64).saturating_mul(s.into()))) .saturating_add(Weight::from_parts(0, 4).saturating_mul(s.into())) @@ -1149,10 +1176,10 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `66639` // Estimated: `70104` - // Minimum execution time: 91_160_000 picoseconds. - Weight::from_parts(794_662_495, 70104) - // Standard Error: 51_479 - .saturating_add(Weight::from_parts(4_346_694, 0).saturating_mul(s.into())) + // Minimum execution time: 90_193_000 picoseconds. + Weight::from_parts(821_522_318, 70104) + // Standard Error: 57_922 + .saturating_add(Weight::from_parts(4_554_659, 0).saturating_mul(s.into())) .saturating_add(RocksDbWeight::get().reads(1_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } @@ -1178,16 +1205,16 @@ impl WeightInfo for () { fn payout_stakers_dead_controller(n: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `20217 + n * (143 ±0)` - // Estimated: `63416 + n * (8024 ±1)` - // Minimum execution time: 77_845_000 picoseconds. - Weight::from_parts(86_178_186, 63416) - // Standard Error: 21_971 - .saturating_add(Weight::from_parts(26_966_654, 0).saturating_mul(n.into())) + // Estimated: `19844 + n * (2603 ±1)` + // Minimum execution time: 80_329_000 picoseconds. + Weight::from_parts(97_340_643, 19844) + // Standard Error: 22_713 + .saturating_add(Weight::from_parts(29_087_425, 0).saturating_mul(n.into())) .saturating_add(RocksDbWeight::get().reads(9_u64)) .saturating_add(RocksDbWeight::get().reads((3_u64).saturating_mul(n.into()))) .saturating_add(RocksDbWeight::get().writes(2_u64)) .saturating_add(RocksDbWeight::get().writes((1_u64).saturating_mul(n.into()))) - .saturating_add(Weight::from_parts(0, 8024).saturating_mul(n.into())) + .saturating_add(Weight::from_parts(0, 2603).saturating_mul(n.into())) } /// Storage: Staking CurrentEra (r:1 w:0) /// Proof: Staking CurrentEra (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) @@ -1209,25 +1236,29 @@ impl WeightInfo for () { /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) /// Storage: Balances Locks (r:257 w:257) /// Proof: Balances Locks (max_values: None, max_size: Some(1299), added: 3774, mode: MaxEncodedLen) + /// Storage: Balances Freezes (r:257 w:0) + /// Proof: Balances Freezes (max_values: None, max_size: Some(49), added: 2524, mode: MaxEncodedLen) /// The range of component `n` is `[0, 256]`. fn payout_stakers_alive_staked(n: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `34971 + n * (401 ±0)` - // Estimated: `93244 + n * (15898 ±0)` - // Minimum execution time: 98_209_000 picoseconds. - Weight::from_parts(102_121_432, 93244) - // Standard Error: 25_633 - .saturating_add(Weight::from_parts(39_244_200, 0).saturating_mul(n.into())) - .saturating_add(RocksDbWeight::get().reads(10_u64)) - .saturating_add(RocksDbWeight::get().reads((5_u64).saturating_mul(n.into()))) + // Estimated: `32376 + n * (3774 ±0)` + // Minimum execution time: 105_591_000 picoseconds. + Weight::from_parts(111_587_915, 32376) + // Standard Error: 15_598 + .saturating_add(Weight::from_parts(48_948_195, 0).saturating_mul(n.into())) + .saturating_add(RocksDbWeight::get().reads(11_u64)) + .saturating_add(RocksDbWeight::get().reads((6_u64).saturating_mul(n.into()))) .saturating_add(RocksDbWeight::get().writes(3_u64)) .saturating_add(RocksDbWeight::get().writes((3_u64).saturating_mul(n.into()))) - .saturating_add(Weight::from_parts(0, 15898).saturating_mul(n.into())) + .saturating_add(Weight::from_parts(0, 3774).saturating_mul(n.into())) } /// Storage: Staking Ledger (r:1 w:1) /// Proof: Staking Ledger (max_values: None, max_size: Some(1091), added: 3566, mode: MaxEncodedLen) /// Storage: Balances Locks (r:1 w:1) /// Proof: Balances Locks (max_values: None, max_size: Some(1299), added: 3774, mode: MaxEncodedLen) + /// Storage: Balances Freezes (r:1 w:0) + /// Proof: Balances Freezes (max_values: None, max_size: Some(49), added: 2524, mode: MaxEncodedLen) /// Storage: System Account (r:1 w:1) /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) /// Storage: VoterList ListNodes (r:3 w:3) @@ -1240,12 +1271,12 @@ impl WeightInfo for () { fn rebond(l: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `2029 + l * (7 ±0)` - // Estimated: `31431` - // Minimum execution time: 87_067_000 picoseconds. - Weight::from_parts(87_971_483, 31431) - // Standard Error: 1_668 - .saturating_add(Weight::from_parts(56_024, 0).saturating_mul(l.into())) - .saturating_add(RocksDbWeight::get().reads(9_u64)) + // Estimated: `8877` + // Minimum execution time: 89_420_000 picoseconds. + Weight::from_parts(90_743_615, 8877) + // Standard Error: 1_260 + .saturating_add(Weight::from_parts(50_832, 0).saturating_mul(l.into())) + .saturating_add(RocksDbWeight::get().reads(10_u64)) .saturating_add(RocksDbWeight::get().writes(8_u64)) } /// Storage: System Account (r:1 w:1) @@ -1270,6 +1301,8 @@ impl WeightInfo for () { /// Proof: VoterList CounterForListNodes (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) /// Storage: Balances Locks (r:1 w:1) /// Proof: Balances Locks (max_values: None, max_size: Some(1299), added: 3774, mode: MaxEncodedLen) + /// Storage: Balances Freezes (r:1 w:0) + /// Proof: Balances Freezes (max_values: None, max_size: Some(49), added: 2524, mode: MaxEncodedLen) /// Storage: Staking Payee (r:0 w:1) /// Proof: Staking Payee (max_values: None, max_size: Some(73), added: 2548, mode: MaxEncodedLen) /// Storage: Staking SpanSlash (r:0 w:100) @@ -1278,12 +1311,12 @@ impl WeightInfo for () { fn reap_stash(s: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `2294 + s * (4 ±0)` - // Estimated: `42515 + s * (4 ±0)` - // Minimum execution time: 90_421_000 picoseconds. - Weight::from_parts(91_873_866, 42515) - // Standard Error: 1_867 - .saturating_add(Weight::from_parts(1_275_581, 0).saturating_mul(s.into())) - .saturating_add(RocksDbWeight::get().reads(12_u64)) + // Estimated: `6248 + s * (4 ±0)` + // Minimum execution time: 100_911_000 picoseconds. + Weight::from_parts(102_678_006, 6248) + // Standard Error: 2_349 + .saturating_add(Weight::from_parts(1_262_431, 0).saturating_mul(s.into())) + .saturating_add(RocksDbWeight::get().reads(13_u64)) .saturating_add(RocksDbWeight::get().writes(12_u64)) .saturating_add(RocksDbWeight::get().writes((1_u64).saturating_mul(s.into()))) .saturating_add(Weight::from_parts(0, 4).saturating_mul(s.into())) @@ -1326,21 +1359,21 @@ impl WeightInfo for () { /// The range of component `n` is `[0, 100]`. fn new_era(v: u32, n: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `0 + v * (3598 ±0) + n * (720 ±0)` - // Estimated: `537749 + v * (16699 ±39) + n * (12763 ±3)` - // Minimum execution time: 552_452_000 picoseconds. - Weight::from_parts(555_250_000, 537749) - // Standard Error: 1_893_951 - .saturating_add(Weight::from_parts(61_059_451, 0).saturating_mul(v.into())) - // Standard Error: 188_721 - .saturating_add(Weight::from_parts(17_220_679, 0).saturating_mul(n.into())) + // Measured: `0 + n * (720 ±0) + v * (3598 ±0)` + // Estimated: `512390 + n * (3566 ±0) + v * (3566 ±0)` + // Minimum execution time: 554_712_000 picoseconds. + Weight::from_parts(556_603_000, 512390) + // Standard Error: 1_925_251 + .saturating_add(Weight::from_parts(62_627_196, 0).saturating_mul(v.into())) + // Standard Error: 191_840 + .saturating_add(Weight::from_parts(16_681_790, 0).saturating_mul(n.into())) .saturating_add(RocksDbWeight::get().reads(206_u64)) .saturating_add(RocksDbWeight::get().reads((5_u64).saturating_mul(v.into()))) .saturating_add(RocksDbWeight::get().reads((4_u64).saturating_mul(n.into()))) .saturating_add(RocksDbWeight::get().writes(4_u64)) .saturating_add(RocksDbWeight::get().writes((3_u64).saturating_mul(v.into()))) - .saturating_add(Weight::from_parts(0, 16699).saturating_mul(v.into())) - .saturating_add(Weight::from_parts(0, 12763).saturating_mul(n.into())) + .saturating_add(Weight::from_parts(0, 3566).saturating_mul(n.into())) + .saturating_add(Weight::from_parts(0, 3566).saturating_mul(v.into())) } /// Storage: VoterList CounterForListNodes (r:1 w:0) /// Proof: VoterList CounterForListNodes (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) @@ -1362,20 +1395,20 @@ impl WeightInfo for () { /// The range of component `n` is `[500, 1000]`. fn get_npos_voters(v: u32, n: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `3135 + v * (395 ±0) + n * (911 ±0)` - // Estimated: `518829 + v * (14295 ±0) + n * (11775 ±0)` - // Minimum execution time: 32_870_065_000 picoseconds. - Weight::from_parts(33_295_716_000, 518829) - // Standard Error: 371_824 - .saturating_add(Weight::from_parts(5_148_979, 0).saturating_mul(v.into())) - // Standard Error: 371_824 - .saturating_add(Weight::from_parts(3_376_262, 0).saturating_mul(n.into())) + // Measured: `3135 + n * (911 ±0) + v * (395 ±0)` + // Estimated: `512390 + n * (3566 ±0) + v * (3566 ±0)` + // Minimum execution time: 31_770_670_000 picoseconds. + Weight::from_parts(31_839_042_000, 512390) + // Standard Error: 355_382 + .saturating_add(Weight::from_parts(5_044_540, 0).saturating_mul(v.into())) + // Standard Error: 355_382 + .saturating_add(Weight::from_parts(3_205_722, 0).saturating_mul(n.into())) .saturating_add(RocksDbWeight::get().reads(201_u64)) .saturating_add(RocksDbWeight::get().reads((5_u64).saturating_mul(v.into()))) .saturating_add(RocksDbWeight::get().reads((4_u64).saturating_mul(n.into()))) .saturating_add(RocksDbWeight::get().writes(1_u64)) - .saturating_add(Weight::from_parts(0, 14295).saturating_mul(v.into())) - .saturating_add(Weight::from_parts(0, 11775).saturating_mul(n.into())) + .saturating_add(Weight::from_parts(0, 3566).saturating_mul(n.into())) + .saturating_add(Weight::from_parts(0, 3566).saturating_mul(v.into())) } /// Storage: Staking CounterForValidators (r:1 w:0) /// Proof: Staking CounterForValidators (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) @@ -1385,11 +1418,11 @@ impl WeightInfo for () { fn get_npos_targets(v: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `983 + v * (50 ±0)` - // Estimated: `4999 + v * (2520 ±0)` - // Minimum execution time: 2_321_389_000 picoseconds. - Weight::from_parts(70_122_933, 4999) - // Standard Error: 7_249 - .saturating_add(Weight::from_parts(4_632_666, 0).saturating_mul(v.into())) + // Estimated: `3510 + v * (2520 ±0)` + // Minimum execution time: 2_253_567_000 picoseconds. + Weight::from_parts(61_440_613, 3510) + // Standard Error: 5_276 + .saturating_add(Weight::from_parts(4_414_153, 0).saturating_mul(v.into())) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().reads((1_u64).saturating_mul(v.into()))) .saturating_add(Weight::from_parts(0, 2520).saturating_mul(v.into())) @@ -1410,8 +1443,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 9_581_000 picoseconds. - Weight::from_parts(9_802_000, 0) + // Minimum execution time: 9_292_000 picoseconds. + Weight::from_parts(9_587_000, 0) .saturating_add(RocksDbWeight::get().writes(6_u64)) } /// Storage: Staking MinCommission (r:0 w:1) @@ -1430,8 +1463,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 8_883_000 picoseconds. - Weight::from_parts(9_151_000, 0) + // Minimum execution time: 8_294_000 picoseconds. + Weight::from_parts(8_597_000, 0) .saturating_add(RocksDbWeight::get().writes(6_u64)) } /// Storage: Staking Ledger (r:1 w:0) @@ -1457,9 +1490,9 @@ impl WeightInfo for () { fn chill_other() -> Weight { // Proof Size summary in bytes: // Measured: `1871` - // Estimated: `29338` - // Minimum execution time: 77_916_000 picoseconds. - Weight::from_parts(78_877_000, 29338) + // Estimated: `6248` + // Minimum execution time: 75_742_000 picoseconds. + Weight::from_parts(76_252_000, 6248) .saturating_add(RocksDbWeight::get().reads(11_u64)) .saturating_add(RocksDbWeight::get().writes(6_u64)) } @@ -1470,9 +1503,9 @@ impl WeightInfo for () { fn force_apply_min_commission() -> Weight { // Proof Size summary in bytes: // Measured: `694` - // Estimated: `4999` - // Minimum execution time: 16_658_000 picoseconds. - Weight::from_parts(16_968_000, 4999) + // Estimated: `3510` + // Minimum execution time: 16_407_000 picoseconds. + Weight::from_parts(16_726_000, 3510) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } @@ -1482,8 +1515,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 4_982_000 picoseconds. - Weight::from_parts(5_232_000, 0) + // Minimum execution time: 4_977_000 picoseconds. + Weight::from_parts(5_224_000, 0) .saturating_add(RocksDbWeight::get().writes(1_u64)) } } diff --git a/frame/state-trie-migration/src/weights.rs b/frame/state-trie-migration/src/weights.rs index 961cec7bc..8565dd73e 100644 --- a/frame/state-trie-migration/src/weights.rs +++ b/frame/state-trie-migration/src/weights.rs @@ -18,9 +18,9 @@ //! Autogenerated weights for pallet_state_trie_migration //! //! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev -//! DATE: 2023-03-16, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! DATE: 2023-04-06, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` //! WORST CASE MAP SIZE: `1000000` -//! HOSTNAME: `bm3`, CPU: `Intel(R) Core(TM) i7-7700K CPU @ 4.20GHz` +//! HOSTNAME: `bm2`, CPU: `Intel(R) Core(TM) i7-7700K CPU @ 4.20GHz` //! EXECUTION: Some(Wasm), WASM-EXECUTION: Compiled, CHAIN: Some("dev"), DB CACHE: 1024 // Executed Command: @@ -31,9 +31,6 @@ // --steps=50 // --repeat=20 // --pallet=pallet_state_trie_migration -// --no-storage-info -// --no-median-slopes -// --no-min-squares // --extrinsic=* // --execution=wasm // --wasm-execution=compiled @@ -70,9 +67,9 @@ impl WeightInfo for SubstrateWeight { fn continue_migrate() -> Weight { // Proof Size summary in bytes: // Measured: `108` - // Estimated: `4020` - // Minimum execution time: 15_449_000 picoseconds. - Weight::from_parts(16_040_000, 4020) + // Estimated: `2527` + // Minimum execution time: 17_385_000 picoseconds. + Weight::from_parts(17_766_000, 2527) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -82,16 +79,16 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `76` // Estimated: `1493` - // Minimum execution time: 4_632_000 picoseconds. - Weight::from_parts(4_768_000, 1493) + // Minimum execution time: 4_537_000 picoseconds. + Weight::from_parts(4_734_000, 1493) .saturating_add(T::DbWeight::get().reads(1_u64)) } fn migrate_custom_top_success() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 9_596_000 picoseconds. - Weight::from_parts(9_866_000, 0) + // Minimum execution time: 10_127_000 picoseconds. + Weight::from_parts(10_384_000, 0) } /// Storage: unknown `0x666f6f` (r:1 w:1) /// Proof Skipped: unknown `0x666f6f` (r:1 w:1) @@ -99,8 +96,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `113` // Estimated: `3578` - // Minimum execution time: 27_352_000 picoseconds. - Weight::from_parts(28_089_000, 3578) + // Minimum execution time: 31_113_000 picoseconds. + Weight::from_parts(31_833_000, 3578) .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -108,8 +105,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 10_286_000 picoseconds. - Weight::from_parts(10_761_000, 0) + // Minimum execution time: 10_445_000 picoseconds. + Weight::from_parts(10_726_000, 0) } /// Storage: unknown `0x666f6f` (r:1 w:1) /// Proof Skipped: unknown `0x666f6f` (r:1 w:1) @@ -117,8 +114,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `105` // Estimated: `3570` - // Minimum execution time: 27_355_000 picoseconds. - Weight::from_parts(28_092_000, 3570) + // Minimum execution time: 31_795_000 picoseconds. + Weight::from_parts(32_737_000, 3570) .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -129,10 +126,10 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `197 + v * (1 ±0)` // Estimated: `3662 + v * (1 ±0)` - // Minimum execution time: 6_004_000 picoseconds. - Weight::from_parts(6_188_000, 3662) - // Standard Error: 1 - .saturating_add(Weight::from_parts(1_073, 0).saturating_mul(v.into())) + // Minimum execution time: 5_933_000 picoseconds. + Weight::from_parts(6_040_000, 3662) + // Standard Error: 3 + .saturating_add(Weight::from_parts(1_336, 0).saturating_mul(v.into())) .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) .saturating_add(Weight::from_parts(0, 1).saturating_mul(v.into())) @@ -148,9 +145,9 @@ impl WeightInfo for () { fn continue_migrate() -> Weight { // Proof Size summary in bytes: // Measured: `108` - // Estimated: `4020` - // Minimum execution time: 15_449_000 picoseconds. - Weight::from_parts(16_040_000, 4020) + // Estimated: `2527` + // Minimum execution time: 17_385_000 picoseconds. + Weight::from_parts(17_766_000, 2527) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } @@ -160,16 +157,16 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `76` // Estimated: `1493` - // Minimum execution time: 4_632_000 picoseconds. - Weight::from_parts(4_768_000, 1493) + // Minimum execution time: 4_537_000 picoseconds. + Weight::from_parts(4_734_000, 1493) .saturating_add(RocksDbWeight::get().reads(1_u64)) } fn migrate_custom_top_success() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 9_596_000 picoseconds. - Weight::from_parts(9_866_000, 0) + // Minimum execution time: 10_127_000 picoseconds. + Weight::from_parts(10_384_000, 0) } /// Storage: unknown `0x666f6f` (r:1 w:1) /// Proof Skipped: unknown `0x666f6f` (r:1 w:1) @@ -177,8 +174,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `113` // Estimated: `3578` - // Minimum execution time: 27_352_000 picoseconds. - Weight::from_parts(28_089_000, 3578) + // Minimum execution time: 31_113_000 picoseconds. + Weight::from_parts(31_833_000, 3578) .saturating_add(RocksDbWeight::get().reads(1_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } @@ -186,8 +183,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 10_286_000 picoseconds. - Weight::from_parts(10_761_000, 0) + // Minimum execution time: 10_445_000 picoseconds. + Weight::from_parts(10_726_000, 0) } /// Storage: unknown `0x666f6f` (r:1 w:1) /// Proof Skipped: unknown `0x666f6f` (r:1 w:1) @@ -195,8 +192,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `105` // Estimated: `3570` - // Minimum execution time: 27_355_000 picoseconds. - Weight::from_parts(28_092_000, 3570) + // Minimum execution time: 31_795_000 picoseconds. + Weight::from_parts(32_737_000, 3570) .saturating_add(RocksDbWeight::get().reads(1_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } @@ -207,10 +204,10 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `197 + v * (1 ±0)` // Estimated: `3662 + v * (1 ±0)` - // Minimum execution time: 6_004_000 picoseconds. - Weight::from_parts(6_188_000, 3662) - // Standard Error: 1 - .saturating_add(Weight::from_parts(1_073, 0).saturating_mul(v.into())) + // Minimum execution time: 5_933_000 picoseconds. + Weight::from_parts(6_040_000, 3662) + // Standard Error: 3 + .saturating_add(Weight::from_parts(1_336, 0).saturating_mul(v.into())) .saturating_add(RocksDbWeight::get().reads(1_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) .saturating_add(Weight::from_parts(0, 1).saturating_mul(v.into())) diff --git a/frame/support/src/weights/block_weights.rs b/frame/support/src/weights/block_weights.rs index b8ac9a038..b358aa473 100644 --- a/frame/support/src/weights/block_weights.rs +++ b/frame/support/src/weights/block_weights.rs @@ -16,8 +16,8 @@ // limitations under the License. //! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev -//! DATE: 2023-03-15 (Y/M/D) -//! HOSTNAME: `bm3`, CPU: `Intel(R) Core(TM) i7-7700K CPU @ 4.20GHz` +//! DATE: 2023-04-06 (Y/M/D) +//! HOSTNAME: `bm2`, CPU: `Intel(R) Core(TM) i7-7700K CPU @ 4.20GHz` //! //! SHORT-NAME: `block`, LONG-NAME: `BlockExecution`, RUNTIME: `Development` //! WARMUPS: `10`, REPEAT: `100` @@ -44,17 +44,17 @@ parameter_types! { /// Calculated by multiplying the *Average* with `1.0` and adding `0`. /// /// Stats nanoseconds: - /// Min, Max: 402_748, 458_228 - /// Average: 412_772 - /// Median: 406_151 - /// Std-Dev: 13480.33 + /// Min, Max: 386_820, 419_676 + /// Average: 392_184 + /// Median: 389_668 + /// Std-Dev: 5285.57 /// /// Percentiles nanoseconds: - /// 99th: 450_080 - /// 95th: 445_111 - /// 75th: 414_170 + /// 99th: 406_316 + /// 95th: 399_697 + /// 75th: 396_532 pub const BlockExecutionWeight: Weight = - Weight::from_parts(WEIGHT_REF_TIME_PER_NANOS.saturating_mul(412_772), 0); + Weight::from_parts(WEIGHT_REF_TIME_PER_NANOS.saturating_mul(392_184), 0); } #[cfg(test)] diff --git a/frame/support/src/weights/extrinsic_weights.rs b/frame/support/src/weights/extrinsic_weights.rs index 44b1b94ae..1a6facc3d 100644 --- a/frame/support/src/weights/extrinsic_weights.rs +++ b/frame/support/src/weights/extrinsic_weights.rs @@ -16,8 +16,8 @@ // limitations under the License. //! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev -//! DATE: 2023-03-15 (Y/M/D) -//! HOSTNAME: `bm3`, CPU: `Intel(R) Core(TM) i7-7700K CPU @ 4.20GHz` +//! DATE: 2023-04-06 (Y/M/D) +//! HOSTNAME: `bm2`, CPU: `Intel(R) Core(TM) i7-7700K CPU @ 4.20GHz` //! //! SHORT-NAME: `extrinsic`, LONG-NAME: `ExtrinsicBase`, RUNTIME: `Development` //! WARMUPS: `10`, REPEAT: `100` @@ -44,17 +44,17 @@ parameter_types! { /// Calculated by multiplying the *Average* with `1.0` and adding `0`. /// /// Stats nanoseconds: - /// Min, Max: 109_595, 114_170 - /// Average: 110_536 - /// Median: 110_233 - /// Std-Dev: 933.39 + /// Min, Max: 113_246, 114_346 + /// Average: 113_638 + /// Median: 113_641 + /// Std-Dev: 188.44 /// /// Percentiles nanoseconds: - /// 99th: 114_120 - /// 95th: 112_680 - /// 75th: 110_858 + /// 99th: 114_181 + /// 95th: 113_961 + /// 75th: 113_703 pub const ExtrinsicBaseWeight: Weight = - Weight::from_parts(WEIGHT_REF_TIME_PER_NANOS.saturating_mul(110_536), 0); + Weight::from_parts(WEIGHT_REF_TIME_PER_NANOS.saturating_mul(113_638), 0); } #[cfg(test)] diff --git a/frame/system/src/weights.rs b/frame/system/src/weights.rs index 34c0b62dc..96fd7a197 100644 --- a/frame/system/src/weights.rs +++ b/frame/system/src/weights.rs @@ -18,9 +18,9 @@ //! Autogenerated weights for frame_system //! //! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev -//! DATE: 2023-03-15, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! DATE: 2023-04-06, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` //! WORST CASE MAP SIZE: `1000000` -//! HOSTNAME: `bm3`, CPU: `Intel(R) Core(TM) i7-7700K CPU @ 4.20GHz` +//! HOSTNAME: `bm2`, CPU: `Intel(R) Core(TM) i7-7700K CPU @ 4.20GHz` //! EXECUTION: Some(Wasm), WASM-EXECUTION: Compiled, CHAIN: Some("dev"), DB CACHE: 1024 // Executed Command: @@ -31,9 +31,6 @@ // --steps=50 // --repeat=20 // --pallet=frame_system -// --no-storage-info -// --no-median-slopes -// --no-min-squares // --extrinsic=* // --execution=wasm // --wasm-execution=compiled @@ -67,20 +64,20 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 2_519_000 picoseconds. - Weight::from_parts(2_576_000, 0) + // Minimum execution time: 2_430_000 picoseconds. + Weight::from_parts(2_522_000, 0) // Standard Error: 0 - .saturating_add(Weight::from_parts(365, 0).saturating_mul(b.into())) + .saturating_add(Weight::from_parts(362, 0).saturating_mul(b.into())) } /// The range of component `b` is `[0, 3932160]`. fn remark_with_event(b: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 8_687_000 picoseconds. - Weight::from_parts(8_875_000, 0) + // Minimum execution time: 8_738_000 picoseconds. + Weight::from_parts(8_963_000, 0) // Standard Error: 0 - .saturating_add(Weight::from_parts(1_121, 0).saturating_mul(b.into())) + .saturating_add(Weight::from_parts(1_113, 0).saturating_mul(b.into())) } /// Storage: System Digest (r:1 w:1) /// Proof Skipped: System Digest (max_values: Some(1), max_size: None, mode: Measured) @@ -90,8 +87,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `1485` - // Minimum execution time: 5_281_000 picoseconds. - Weight::from_parts(5_383_000, 1485) + // Minimum execution time: 5_109_000 picoseconds. + Weight::from_parts(5_336_000, 1485) .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(T::DbWeight::get().writes(2_u64)) } @@ -102,10 +99,10 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 2_346_000 picoseconds. - Weight::from_parts(2_491_000, 0) - // Standard Error: 815 - .saturating_add(Weight::from_parts(731_217, 0).saturating_mul(i.into())) + // Minimum execution time: 2_531_000 picoseconds. + Weight::from_parts(2_617_000, 0) + // Standard Error: 861 + .saturating_add(Weight::from_parts(761_465, 0).saturating_mul(i.into())) .saturating_add(T::DbWeight::get().writes((1_u64).saturating_mul(i.into()))) } /// Storage: Skipped Metadata (r:0 w:0) @@ -115,10 +112,10 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 2_394_000 picoseconds. - Weight::from_parts(2_567_000, 0) - // Standard Error: 935 - .saturating_add(Weight::from_parts(554_023, 0).saturating_mul(i.into())) + // Minimum execution time: 2_631_000 picoseconds. + Weight::from_parts(2_717_000, 0) + // Standard Error: 1_097 + .saturating_add(Weight::from_parts(570_803, 0).saturating_mul(i.into())) .saturating_add(T::DbWeight::get().writes((1_u64).saturating_mul(i.into()))) } /// Storage: Skipped Metadata (r:0 w:0) @@ -128,10 +125,10 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `116 + p * (69 ±0)` // Estimated: `121 + p * (70 ±0)` - // Minimum execution time: 4_706_000 picoseconds. - Weight::from_parts(4_858_000, 121) - // Standard Error: 1_291 - .saturating_add(Weight::from_parts(1_142_198, 0).saturating_mul(p.into())) + // Minimum execution time: 4_692_000 picoseconds. + Weight::from_parts(4_789_000, 121) + // Standard Error: 1_013 + .saturating_add(Weight::from_parts(1_143_616, 0).saturating_mul(p.into())) .saturating_add(T::DbWeight::get().reads((1_u64).saturating_mul(p.into()))) .saturating_add(T::DbWeight::get().writes((1_u64).saturating_mul(p.into()))) .saturating_add(Weight::from_parts(0, 70).saturating_mul(p.into())) @@ -145,20 +142,20 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 2_519_000 picoseconds. - Weight::from_parts(2_576_000, 0) + // Minimum execution time: 2_430_000 picoseconds. + Weight::from_parts(2_522_000, 0) // Standard Error: 0 - .saturating_add(Weight::from_parts(365, 0).saturating_mul(b.into())) + .saturating_add(Weight::from_parts(362, 0).saturating_mul(b.into())) } /// The range of component `b` is `[0, 3932160]`. fn remark_with_event(b: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 8_687_000 picoseconds. - Weight::from_parts(8_875_000, 0) + // Minimum execution time: 8_738_000 picoseconds. + Weight::from_parts(8_963_000, 0) // Standard Error: 0 - .saturating_add(Weight::from_parts(1_121, 0).saturating_mul(b.into())) + .saturating_add(Weight::from_parts(1_113, 0).saturating_mul(b.into())) } /// Storage: System Digest (r:1 w:1) /// Proof Skipped: System Digest (max_values: Some(1), max_size: None, mode: Measured) @@ -168,8 +165,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `0` // Estimated: `1485` - // Minimum execution time: 5_281_000 picoseconds. - Weight::from_parts(5_383_000, 1485) + // Minimum execution time: 5_109_000 picoseconds. + Weight::from_parts(5_336_000, 1485) .saturating_add(RocksDbWeight::get().reads(1_u64)) .saturating_add(RocksDbWeight::get().writes(2_u64)) } @@ -180,10 +177,10 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 2_346_000 picoseconds. - Weight::from_parts(2_491_000, 0) - // Standard Error: 815 - .saturating_add(Weight::from_parts(731_217, 0).saturating_mul(i.into())) + // Minimum execution time: 2_531_000 picoseconds. + Weight::from_parts(2_617_000, 0) + // Standard Error: 861 + .saturating_add(Weight::from_parts(761_465, 0).saturating_mul(i.into())) .saturating_add(RocksDbWeight::get().writes((1_u64).saturating_mul(i.into()))) } /// Storage: Skipped Metadata (r:0 w:0) @@ -193,10 +190,10 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 2_394_000 picoseconds. - Weight::from_parts(2_567_000, 0) - // Standard Error: 935 - .saturating_add(Weight::from_parts(554_023, 0).saturating_mul(i.into())) + // Minimum execution time: 2_631_000 picoseconds. + Weight::from_parts(2_717_000, 0) + // Standard Error: 1_097 + .saturating_add(Weight::from_parts(570_803, 0).saturating_mul(i.into())) .saturating_add(RocksDbWeight::get().writes((1_u64).saturating_mul(i.into()))) } /// Storage: Skipped Metadata (r:0 w:0) @@ -206,10 +203,10 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `116 + p * (69 ±0)` // Estimated: `121 + p * (70 ±0)` - // Minimum execution time: 4_706_000 picoseconds. - Weight::from_parts(4_858_000, 121) - // Standard Error: 1_291 - .saturating_add(Weight::from_parts(1_142_198, 0).saturating_mul(p.into())) + // Minimum execution time: 4_692_000 picoseconds. + Weight::from_parts(4_789_000, 121) + // Standard Error: 1_013 + .saturating_add(Weight::from_parts(1_143_616, 0).saturating_mul(p.into())) .saturating_add(RocksDbWeight::get().reads((1_u64).saturating_mul(p.into()))) .saturating_add(RocksDbWeight::get().writes((1_u64).saturating_mul(p.into()))) .saturating_add(Weight::from_parts(0, 70).saturating_mul(p.into())) diff --git a/frame/timestamp/src/weights.rs b/frame/timestamp/src/weights.rs index e1ada6f63..1c254bf22 100644 --- a/frame/timestamp/src/weights.rs +++ b/frame/timestamp/src/weights.rs @@ -18,9 +18,9 @@ //! Autogenerated weights for pallet_timestamp //! //! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev -//! DATE: 2023-03-16, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! DATE: 2023-04-06, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` //! WORST CASE MAP SIZE: `1000000` -//! HOSTNAME: `bm3`, CPU: `Intel(R) Core(TM) i7-7700K CPU @ 4.20GHz` +//! HOSTNAME: `bm2`, CPU: `Intel(R) Core(TM) i7-7700K CPU @ 4.20GHz` //! EXECUTION: Some(Wasm), WASM-EXECUTION: Compiled, CHAIN: Some("dev"), DB CACHE: 1024 // Executed Command: @@ -31,9 +31,6 @@ // --steps=50 // --repeat=20 // --pallet=pallet_timestamp -// --no-storage-info -// --no-median-slopes -// --no-min-squares // --extrinsic=* // --execution=wasm // --wasm-execution=compiled @@ -65,9 +62,9 @@ impl WeightInfo for SubstrateWeight { fn set() -> Weight { // Proof Size summary in bytes: // Measured: `312` - // Estimated: `2986` - // Minimum execution time: 10_913_000 picoseconds. - Weight::from_parts(11_453_000, 2986) + // Estimated: `1493` + // Minimum execution time: 10_834_000 picoseconds. + Weight::from_parts(11_099_000, 1493) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -75,8 +72,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `161` // Estimated: `0` - // Minimum execution time: 4_400_000 picoseconds. - Weight::from_parts(4_550_000, 0) + // Minimum execution time: 4_472_000 picoseconds. + Weight::from_parts(4_645_000, 0) } } @@ -89,9 +86,9 @@ impl WeightInfo for () { fn set() -> Weight { // Proof Size summary in bytes: // Measured: `312` - // Estimated: `2986` - // Minimum execution time: 10_913_000 picoseconds. - Weight::from_parts(11_453_000, 2986) + // Estimated: `1493` + // Minimum execution time: 10_834_000 picoseconds. + Weight::from_parts(11_099_000, 1493) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } @@ -99,7 +96,7 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `161` // Estimated: `0` - // Minimum execution time: 4_400_000 picoseconds. - Weight::from_parts(4_550_000, 0) + // Minimum execution time: 4_472_000 picoseconds. + Weight::from_parts(4_645_000, 0) } } diff --git a/frame/tips/src/weights.rs b/frame/tips/src/weights.rs index 205413d52..ec5eef8c8 100644 --- a/frame/tips/src/weights.rs +++ b/frame/tips/src/weights.rs @@ -18,9 +18,9 @@ //! Autogenerated weights for pallet_tips //! //! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev -//! DATE: 2023-03-16, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! DATE: 2023-04-06, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` //! WORST CASE MAP SIZE: `1000000` -//! HOSTNAME: `bm3`, CPU: `Intel(R) Core(TM) i7-7700K CPU @ 4.20GHz` +//! HOSTNAME: `bm2`, CPU: `Intel(R) Core(TM) i7-7700K CPU @ 4.20GHz` //! EXECUTION: Some(Wasm), WASM-EXECUTION: Compiled, CHAIN: Some("dev"), DB CACHE: 1024 // Executed Command: @@ -31,9 +31,6 @@ // --steps=50 // --repeat=20 // --pallet=pallet_tips -// --no-storage-info -// --no-median-slopes -// --no-min-squares // --extrinsic=* // --execution=wasm // --wasm-execution=compiled @@ -70,11 +67,11 @@ impl WeightInfo for SubstrateWeight { fn report_awesome(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `4` - // Estimated: `6938` - // Minimum execution time: 26_789_000 picoseconds. - Weight::from_parts(27_619_925, 6938) - // Standard Error: 168 - .saturating_add(Weight::from_parts(1_352, 0).saturating_mul(r.into())) + // Estimated: `3469` + // Minimum execution time: 30_728_000 picoseconds. + Weight::from_parts(31_794_924, 3469) + // Standard Error: 171 + .saturating_add(Weight::from_parts(1_020, 0).saturating_mul(r.into())) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().writes(2_u64)) } @@ -85,9 +82,9 @@ impl WeightInfo for SubstrateWeight { fn retract_tip() -> Weight { // Proof Size summary in bytes: // Measured: `221` - // Estimated: `3907` - // Minimum execution time: 25_322_000 picoseconds. - Weight::from_parts(26_107_000, 3907) + // Estimated: `3686` + // Minimum execution time: 29_183_000 picoseconds. + Weight::from_parts(30_017_000, 3686) .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(T::DbWeight::get().writes(2_u64)) } @@ -99,17 +96,19 @@ impl WeightInfo for SubstrateWeight { /// Proof Skipped: Tips Tips (max_values: None, max_size: None, mode: Measured) /// The range of component `r` is `[0, 300]`. /// The range of component `t` is `[1, 13]`. - fn tip_new(_r: u32, t: u32, ) -> Weight { + fn tip_new(r: u32, t: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `526 + t * (64 ±0)` - // Estimated: `6528 + t * (192 ±0)` - // Minimum execution time: 21_112_000 picoseconds. - Weight::from_parts(21_825_317, 6528) - // Standard Error: 11_230 - .saturating_add(Weight::from_parts(86_081, 0).saturating_mul(t.into())) + // Estimated: `3991 + t * (64 ±0)` + // Minimum execution time: 20_726_000 picoseconds. + Weight::from_parts(20_964_411, 3991) + // Standard Error: 119 + .saturating_add(Weight::from_parts(1_230, 0).saturating_mul(r.into())) + // Standard Error: 2_837 + .saturating_add(Weight::from_parts(81_831, 0).saturating_mul(t.into())) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().writes(2_u64)) - .saturating_add(Weight::from_parts(0, 192).saturating_mul(t.into())) + .saturating_add(Weight::from_parts(0, 64).saturating_mul(t.into())) } /// Storage: Elections Members (r:1 w:0) /// Proof Skipped: Elections Members (max_values: Some(1), max_size: None, mode: Measured) @@ -119,14 +118,14 @@ impl WeightInfo for SubstrateWeight { fn tip(t: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `747 + t * (112 ±0)` - // Estimated: `6444 + t * (224 ±0)` - // Minimum execution time: 16_703_000 picoseconds. - Weight::from_parts(16_679_429, 6444) - // Standard Error: 18_088 - .saturating_add(Weight::from_parts(281_667, 0).saturating_mul(t.into())) + // Estimated: `4212 + t * (112 ±0)` + // Minimum execution time: 16_048_000 picoseconds. + Weight::from_parts(16_694_981, 4212) + // Standard Error: 4_480 + .saturating_add(Weight::from_parts(179_723, 0).saturating_mul(t.into())) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) - .saturating_add(Weight::from_parts(0, 224).saturating_mul(t.into())) + .saturating_add(Weight::from_parts(0, 112).saturating_mul(t.into())) } /// Storage: Tips Tips (r:1 w:1) /// Proof Skipped: Tips Tips (max_values: None, max_size: None, mode: Measured) @@ -140,26 +139,28 @@ impl WeightInfo for SubstrateWeight { fn close_tip(t: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `786 + t * (112 ±0)` - // Estimated: `10874 + t * (336 ±0)` - // Minimum execution time: 46_106_000 picoseconds. - Weight::from_parts(47_844_269, 10874) - // Standard Error: 9_247 - .saturating_add(Weight::from_parts(87_804, 0).saturating_mul(t.into())) + // Estimated: `4242 + t * (112 ±0)` + // Minimum execution time: 61_319_000 picoseconds. + Weight::from_parts(62_217_195, 4242) + // Standard Error: 6_721 + .saturating_add(Weight::from_parts(186_620, 0).saturating_mul(t.into())) .saturating_add(T::DbWeight::get().reads(3_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) - .saturating_add(Weight::from_parts(0, 336).saturating_mul(t.into())) + .saturating_add(Weight::from_parts(0, 112).saturating_mul(t.into())) } /// Storage: Tips Tips (r:1 w:1) /// Proof Skipped: Tips Tips (max_values: None, max_size: None, mode: Measured) /// Storage: Tips Reasons (r:0 w:1) /// Proof Skipped: Tips Reasons (max_values: None, max_size: None, mode: Measured) /// The range of component `t` is `[1, 13]`. - fn slash_tip(_t: u32, ) -> Weight { + fn slash_tip(t: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `269` - // Estimated: `4003` - // Minimum execution time: 15_508_000 picoseconds. - Weight::from_parts(16_207_284, 4003) + // Estimated: `3734` + // Minimum execution time: 15_397_000 picoseconds. + Weight::from_parts(15_942_494, 3734) + // Standard Error: 1_657 + .saturating_add(Weight::from_parts(4_128, 0).saturating_mul(t.into())) .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(T::DbWeight::get().writes(2_u64)) } @@ -175,11 +176,11 @@ impl WeightInfo for () { fn report_awesome(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `4` - // Estimated: `6938` - // Minimum execution time: 26_789_000 picoseconds. - Weight::from_parts(27_619_925, 6938) - // Standard Error: 168 - .saturating_add(Weight::from_parts(1_352, 0).saturating_mul(r.into())) + // Estimated: `3469` + // Minimum execution time: 30_728_000 picoseconds. + Weight::from_parts(31_794_924, 3469) + // Standard Error: 171 + .saturating_add(Weight::from_parts(1_020, 0).saturating_mul(r.into())) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().writes(2_u64)) } @@ -190,9 +191,9 @@ impl WeightInfo for () { fn retract_tip() -> Weight { // Proof Size summary in bytes: // Measured: `221` - // Estimated: `3907` - // Minimum execution time: 25_322_000 picoseconds. - Weight::from_parts(26_107_000, 3907) + // Estimated: `3686` + // Minimum execution time: 29_183_000 picoseconds. + Weight::from_parts(30_017_000, 3686) .saturating_add(RocksDbWeight::get().reads(1_u64)) .saturating_add(RocksDbWeight::get().writes(2_u64)) } @@ -204,17 +205,19 @@ impl WeightInfo for () { /// Proof Skipped: Tips Tips (max_values: None, max_size: None, mode: Measured) /// The range of component `r` is `[0, 300]`. /// The range of component `t` is `[1, 13]`. - fn tip_new(_r: u32, t: u32, ) -> Weight { + fn tip_new(r: u32, t: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `526 + t * (64 ±0)` - // Estimated: `6528 + t * (192 ±0)` - // Minimum execution time: 21_112_000 picoseconds. - Weight::from_parts(21_825_317, 6528) - // Standard Error: 11_230 - .saturating_add(Weight::from_parts(86_081, 0).saturating_mul(t.into())) + // Estimated: `3991 + t * (64 ±0)` + // Minimum execution time: 20_726_000 picoseconds. + Weight::from_parts(20_964_411, 3991) + // Standard Error: 119 + .saturating_add(Weight::from_parts(1_230, 0).saturating_mul(r.into())) + // Standard Error: 2_837 + .saturating_add(Weight::from_parts(81_831, 0).saturating_mul(t.into())) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().writes(2_u64)) - .saturating_add(Weight::from_parts(0, 192).saturating_mul(t.into())) + .saturating_add(Weight::from_parts(0, 64).saturating_mul(t.into())) } /// Storage: Elections Members (r:1 w:0) /// Proof Skipped: Elections Members (max_values: Some(1), max_size: None, mode: Measured) @@ -224,14 +227,14 @@ impl WeightInfo for () { fn tip(t: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `747 + t * (112 ±0)` - // Estimated: `6444 + t * (224 ±0)` - // Minimum execution time: 16_703_000 picoseconds. - Weight::from_parts(16_679_429, 6444) - // Standard Error: 18_088 - .saturating_add(Weight::from_parts(281_667, 0).saturating_mul(t.into())) + // Estimated: `4212 + t * (112 ±0)` + // Minimum execution time: 16_048_000 picoseconds. + Weight::from_parts(16_694_981, 4212) + // Standard Error: 4_480 + .saturating_add(Weight::from_parts(179_723, 0).saturating_mul(t.into())) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) - .saturating_add(Weight::from_parts(0, 224).saturating_mul(t.into())) + .saturating_add(Weight::from_parts(0, 112).saturating_mul(t.into())) } /// Storage: Tips Tips (r:1 w:1) /// Proof Skipped: Tips Tips (max_values: None, max_size: None, mode: Measured) @@ -245,26 +248,28 @@ impl WeightInfo for () { fn close_tip(t: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `786 + t * (112 ±0)` - // Estimated: `10874 + t * (336 ±0)` - // Minimum execution time: 46_106_000 picoseconds. - Weight::from_parts(47_844_269, 10874) - // Standard Error: 9_247 - .saturating_add(Weight::from_parts(87_804, 0).saturating_mul(t.into())) + // Estimated: `4242 + t * (112 ±0)` + // Minimum execution time: 61_319_000 picoseconds. + Weight::from_parts(62_217_195, 4242) + // Standard Error: 6_721 + .saturating_add(Weight::from_parts(186_620, 0).saturating_mul(t.into())) .saturating_add(RocksDbWeight::get().reads(3_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) - .saturating_add(Weight::from_parts(0, 336).saturating_mul(t.into())) + .saturating_add(Weight::from_parts(0, 112).saturating_mul(t.into())) } /// Storage: Tips Tips (r:1 w:1) /// Proof Skipped: Tips Tips (max_values: None, max_size: None, mode: Measured) /// Storage: Tips Reasons (r:0 w:1) /// Proof Skipped: Tips Reasons (max_values: None, max_size: None, mode: Measured) /// The range of component `t` is `[1, 13]`. - fn slash_tip(_t: u32, ) -> Weight { + fn slash_tip(t: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `269` - // Estimated: `4003` - // Minimum execution time: 15_508_000 picoseconds. - Weight::from_parts(16_207_284, 4003) + // Estimated: `3734` + // Minimum execution time: 15_397_000 picoseconds. + Weight::from_parts(15_942_494, 3734) + // Standard Error: 1_657 + .saturating_add(Weight::from_parts(4_128, 0).saturating_mul(t.into())) .saturating_add(RocksDbWeight::get().reads(1_u64)) .saturating_add(RocksDbWeight::get().writes(2_u64)) } diff --git a/frame/transaction-storage/src/weights.rs b/frame/transaction-storage/src/weights.rs index 2845136b7..5103ac375 100644 --- a/frame/transaction-storage/src/weights.rs +++ b/frame/transaction-storage/src/weights.rs @@ -18,9 +18,9 @@ //! Autogenerated weights for pallet_transaction_storage //! //! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev -//! DATE: 2023-03-16, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! DATE: 2023-04-06, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` //! WORST CASE MAP SIZE: `1000000` -//! HOSTNAME: `bm3`, CPU: `Intel(R) Core(TM) i7-7700K CPU @ 4.20GHz` +//! HOSTNAME: `bm2`, CPU: `Intel(R) Core(TM) i7-7700K CPU @ 4.20GHz` //! EXECUTION: Some(Wasm), WASM-EXECUTION: Compiled, CHAIN: Some("dev"), DB CACHE: 1024 // Executed Command: @@ -31,9 +31,6 @@ // --steps=50 // --repeat=20 // --pallet=pallet_transaction_storage -// --no-storage-info -// --no-median-slopes -// --no-min-squares // --extrinsic=* // --execution=wasm // --wasm-execution=compiled @@ -69,11 +66,11 @@ impl WeightInfo for SubstrateWeight { fn store(l: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `176` - // Estimated: `41353` - // Minimum execution time: 33_286_000 picoseconds. - Weight::from_parts(33_596_000, 41353) + // Estimated: `38351` + // Minimum execution time: 36_983_000 picoseconds. + Weight::from_parts(37_296_000, 38351) // Standard Error: 2 - .saturating_add(Weight::from_parts(4_954, 0).saturating_mul(l.into())) + .saturating_add(Weight::from_parts(4_908, 0).saturating_mul(l.into())) .saturating_add(T::DbWeight::get().reads(3_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -88,9 +85,9 @@ impl WeightInfo for SubstrateWeight { fn renew() -> Weight { // Proof Size summary in bytes: // Measured: `326` - // Estimated: `81704` - // Minimum execution time: 41_892_000 picoseconds. - Weight::from_parts(42_802_000, 81704) + // Estimated: `40351` + // Minimum execution time: 44_637_000 picoseconds. + Weight::from_parts(45_464_000, 40351) .saturating_add(T::DbWeight::get().reads(4_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -107,9 +104,9 @@ impl WeightInfo for SubstrateWeight { fn check_proof_max() -> Weight { // Proof Size summary in bytes: // Measured: `37145` - // Estimated: `48332` - // Minimum execution time: 63_799_000 picoseconds. - Weight::from_parts(66_145_000, 48332) + // Estimated: `40351` + // Minimum execution time: 59_653_000 picoseconds. + Weight::from_parts(61_068_000, 40351) .saturating_add(T::DbWeight::get().reads(5_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -127,11 +124,11 @@ impl WeightInfo for () { fn store(l: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `176` - // Estimated: `41353` - // Minimum execution time: 33_286_000 picoseconds. - Weight::from_parts(33_596_000, 41353) + // Estimated: `38351` + // Minimum execution time: 36_983_000 picoseconds. + Weight::from_parts(37_296_000, 38351) // Standard Error: 2 - .saturating_add(Weight::from_parts(4_954, 0).saturating_mul(l.into())) + .saturating_add(Weight::from_parts(4_908, 0).saturating_mul(l.into())) .saturating_add(RocksDbWeight::get().reads(3_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } @@ -146,9 +143,9 @@ impl WeightInfo for () { fn renew() -> Weight { // Proof Size summary in bytes: // Measured: `326` - // Estimated: `81704` - // Minimum execution time: 41_892_000 picoseconds. - Weight::from_parts(42_802_000, 81704) + // Estimated: `40351` + // Minimum execution time: 44_637_000 picoseconds. + Weight::from_parts(45_464_000, 40351) .saturating_add(RocksDbWeight::get().reads(4_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } @@ -165,9 +162,9 @@ impl WeightInfo for () { fn check_proof_max() -> Weight { // Proof Size summary in bytes: // Measured: `37145` - // Estimated: `48332` - // Minimum execution time: 63_799_000 picoseconds. - Weight::from_parts(66_145_000, 48332) + // Estimated: `40351` + // Minimum execution time: 59_653_000 picoseconds. + Weight::from_parts(61_068_000, 40351) .saturating_add(RocksDbWeight::get().reads(5_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } diff --git a/frame/treasury/src/weights.rs b/frame/treasury/src/weights.rs index a7c093a80..edf1a674f 100644 --- a/frame/treasury/src/weights.rs +++ b/frame/treasury/src/weights.rs @@ -18,9 +18,9 @@ //! Autogenerated weights for pallet_treasury //! //! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev -//! DATE: 2023-03-16, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! DATE: 2023-04-06, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` //! WORST CASE MAP SIZE: `1000000` -//! HOSTNAME: `bm3`, CPU: `Intel(R) Core(TM) i7-7700K CPU @ 4.20GHz` +//! HOSTNAME: `bm2`, CPU: `Intel(R) Core(TM) i7-7700K CPU @ 4.20GHz` //! EXECUTION: Some(Wasm), WASM-EXECUTION: Compiled, CHAIN: Some("dev"), DB CACHE: 1024 // Executed Command: @@ -31,9 +31,6 @@ // --steps=50 // --repeat=20 // --pallet=pallet_treasury -// --no-storage-info -// --no-median-slopes -// --no-min-squares // --extrinsic=* // --execution=wasm // --wasm-execution=compiled @@ -71,9 +68,9 @@ impl WeightInfo for SubstrateWeight { fn spend() -> Weight { // Proof Size summary in bytes: // Measured: `42` - // Estimated: `3376` - // Minimum execution time: 17_010_000 picoseconds. - Weight::from_parts(17_247_000, 3376) + // Estimated: `1887` + // Minimum execution time: 16_592_000 picoseconds. + Weight::from_parts(16_959_000, 1887) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) } @@ -85,8 +82,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `143` // Estimated: `1489` - // Minimum execution time: 25_780_000 picoseconds. - Weight::from_parts(41_064_000, 1489) + // Minimum execution time: 29_742_000 picoseconds. + Weight::from_parts(30_359_000, 1489) .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(T::DbWeight::get().writes(2_u64)) } @@ -97,9 +94,9 @@ impl WeightInfo for SubstrateWeight { fn reject_proposal() -> Weight { // Proof Size summary in bytes: // Measured: `301` - // Estimated: `7166` - // Minimum execution time: 27_805_000 picoseconds. - Weight::from_parts(28_322_000, 7166) + // Estimated: `3593` + // Minimum execution time: 31_248_000 picoseconds. + Weight::from_parts(31_882_000, 3593) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().writes(2_u64)) } @@ -111,11 +108,11 @@ impl WeightInfo for SubstrateWeight { fn approve_proposal(p: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `470 + p * (8 ±0)` - // Estimated: `5460` - // Minimum execution time: 10_939_000 picoseconds. - Weight::from_parts(13_667_341, 5460) - // Standard Error: 907 - .saturating_add(Weight::from_parts(26_648, 0).saturating_mul(p.into())) + // Estimated: `3573` + // Minimum execution time: 10_441_000 picoseconds. + Weight::from_parts(13_061_079, 3573) + // Standard Error: 877 + .saturating_add(Weight::from_parts(26_940, 0).saturating_mul(p.into())) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -125,8 +122,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `127` // Estimated: `1887` - // Minimum execution time: 8_261_000 picoseconds. - Weight::from_parts(8_399_000, 1887) + // Minimum execution time: 7_935_000 picoseconds. + Weight::from_parts(8_153_000, 1887) .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -144,16 +141,16 @@ impl WeightInfo for SubstrateWeight { fn on_initialize_proposals(p: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `387 + p * (251 ±0)` - // Estimated: `7255 + p * (7789 ±0)` - // Minimum execution time: 43_781_000 picoseconds. - Weight::from_parts(68_521_487, 7255) - // Standard Error: 58_804 - .saturating_add(Weight::from_parts(33_271_211, 0).saturating_mul(p.into())) + // Estimated: `1887 + p * (5206 ±0)` + // Minimum execution time: 45_306_000 picoseconds. + Weight::from_parts(53_639_830, 1887) + // Standard Error: 32_330 + .saturating_add(Weight::from_parts(38_930_307, 0).saturating_mul(p.into())) .saturating_add(T::DbWeight::get().reads(3_u64)) .saturating_add(T::DbWeight::get().reads((3_u64).saturating_mul(p.into()))) .saturating_add(T::DbWeight::get().writes(3_u64)) .saturating_add(T::DbWeight::get().writes((3_u64).saturating_mul(p.into()))) - .saturating_add(Weight::from_parts(0, 7789).saturating_mul(p.into())) + .saturating_add(Weight::from_parts(0, 5206).saturating_mul(p.into())) } } @@ -168,9 +165,9 @@ impl WeightInfo for () { fn spend() -> Weight { // Proof Size summary in bytes: // Measured: `42` - // Estimated: `3376` - // Minimum execution time: 17_010_000 picoseconds. - Weight::from_parts(17_247_000, 3376) + // Estimated: `1887` + // Minimum execution time: 16_592_000 picoseconds. + Weight::from_parts(16_959_000, 1887) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) } @@ -182,8 +179,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `143` // Estimated: `1489` - // Minimum execution time: 25_780_000 picoseconds. - Weight::from_parts(41_064_000, 1489) + // Minimum execution time: 29_742_000 picoseconds. + Weight::from_parts(30_359_000, 1489) .saturating_add(RocksDbWeight::get().reads(1_u64)) .saturating_add(RocksDbWeight::get().writes(2_u64)) } @@ -194,9 +191,9 @@ impl WeightInfo for () { fn reject_proposal() -> Weight { // Proof Size summary in bytes: // Measured: `301` - // Estimated: `7166` - // Minimum execution time: 27_805_000 picoseconds. - Weight::from_parts(28_322_000, 7166) + // Estimated: `3593` + // Minimum execution time: 31_248_000 picoseconds. + Weight::from_parts(31_882_000, 3593) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().writes(2_u64)) } @@ -208,11 +205,11 @@ impl WeightInfo for () { fn approve_proposal(p: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `470 + p * (8 ±0)` - // Estimated: `5460` - // Minimum execution time: 10_939_000 picoseconds. - Weight::from_parts(13_667_341, 5460) - // Standard Error: 907 - .saturating_add(Weight::from_parts(26_648, 0).saturating_mul(p.into())) + // Estimated: `3573` + // Minimum execution time: 10_441_000 picoseconds. + Weight::from_parts(13_061_079, 3573) + // Standard Error: 877 + .saturating_add(Weight::from_parts(26_940, 0).saturating_mul(p.into())) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } @@ -222,8 +219,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `127` // Estimated: `1887` - // Minimum execution time: 8_261_000 picoseconds. - Weight::from_parts(8_399_000, 1887) + // Minimum execution time: 7_935_000 picoseconds. + Weight::from_parts(8_153_000, 1887) .saturating_add(RocksDbWeight::get().reads(1_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } @@ -241,15 +238,15 @@ impl WeightInfo for () { fn on_initialize_proposals(p: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `387 + p * (251 ±0)` - // Estimated: `7255 + p * (7789 ±0)` - // Minimum execution time: 43_781_000 picoseconds. - Weight::from_parts(68_521_487, 7255) - // Standard Error: 58_804 - .saturating_add(Weight::from_parts(33_271_211, 0).saturating_mul(p.into())) + // Estimated: `1887 + p * (5206 ±0)` + // Minimum execution time: 45_306_000 picoseconds. + Weight::from_parts(53_639_830, 1887) + // Standard Error: 32_330 + .saturating_add(Weight::from_parts(38_930_307, 0).saturating_mul(p.into())) .saturating_add(RocksDbWeight::get().reads(3_u64)) .saturating_add(RocksDbWeight::get().reads((3_u64).saturating_mul(p.into()))) .saturating_add(RocksDbWeight::get().writes(3_u64)) .saturating_add(RocksDbWeight::get().writes((3_u64).saturating_mul(p.into()))) - .saturating_add(Weight::from_parts(0, 7789).saturating_mul(p.into())) + .saturating_add(Weight::from_parts(0, 5206).saturating_mul(p.into())) } } diff --git a/frame/uniques/src/weights.rs b/frame/uniques/src/weights.rs index 1f0e37726..14a55d163 100644 --- a/frame/uniques/src/weights.rs +++ b/frame/uniques/src/weights.rs @@ -18,9 +18,9 @@ //! Autogenerated weights for pallet_uniques //! //! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev -//! DATE: 2023-03-16, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! DATE: 2023-04-06, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` //! WORST CASE MAP SIZE: `1000000` -//! HOSTNAME: `bm3`, CPU: `Intel(R) Core(TM) i7-7700K CPU @ 4.20GHz` +//! HOSTNAME: `bm2`, CPU: `Intel(R) Core(TM) i7-7700K CPU @ 4.20GHz` //! EXECUTION: Some(Wasm), WASM-EXECUTION: Compiled, CHAIN: Some("dev"), DB CACHE: 1024 // Executed Command: @@ -31,9 +31,6 @@ // --steps=50 // --repeat=20 // --pallet=pallet_uniques -// --no-storage-info -// --no-median-slopes -// --no-min-squares // --extrinsic=* // --execution=wasm // --wasm-execution=compiled @@ -90,8 +87,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `249` // Estimated: `3643` - // Minimum execution time: 27_805_000 picoseconds. - Weight::from_parts(28_303_000, 3643) + // Minimum execution time: 32_067_000 picoseconds. + Weight::from_parts(32_817_000, 3643) .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(T::DbWeight::get().writes(2_u64)) } @@ -103,8 +100,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `109` // Estimated: `3643` - // Minimum execution time: 16_257_000 picoseconds. - Weight::from_parts(16_626_000, 3643) + // Minimum execution time: 16_365_000 picoseconds. + Weight::from_parts(16_707_000, 3643) .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(T::DbWeight::get().writes(2_u64)) } @@ -129,16 +126,16 @@ impl WeightInfo for SubstrateWeight { /// The range of component `a` is `[0, 1000]`. fn destroy(n: u32, m: u32, a: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `418 + n * (76 ±0) + m * (56 ±0) + a * (107 ±0)` - // Estimated: `9210 + n * (2597 ±0) + a * (2839 ±0) + m * (2583 ±0)` - // Minimum execution time: 2_510_772_000 picoseconds. - Weight::from_parts(2_522_511_000, 9210) - // Standard Error: 27_455 - .saturating_add(Weight::from_parts(6_708_827, 0).saturating_mul(n.into())) - // Standard Error: 27_455 - .saturating_add(Weight::from_parts(375_591, 0).saturating_mul(m.into())) - // Standard Error: 27_455 - .saturating_add(Weight::from_parts(251_697, 0).saturating_mul(a.into())) + // Measured: `418 + a * (107 ±0) + m * (56 ±0) + n * (76 ±0)` + // Estimated: `3643 + a * (2839 ±0) + m * (2583 ±0) + n * (2597 ±0)` + // Minimum execution time: 2_498_918_000 picoseconds. + Weight::from_parts(2_516_809_000, 3643) + // Standard Error: 26_297 + .saturating_add(Weight::from_parts(6_648_035, 0).saturating_mul(n.into())) + // Standard Error: 26_297 + .saturating_add(Weight::from_parts(354_268, 0).saturating_mul(m.into())) + // Standard Error: 26_297 + .saturating_add(Weight::from_parts(223_770, 0).saturating_mul(a.into())) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().reads((1_u64).saturating_mul(n.into()))) .saturating_add(T::DbWeight::get().reads((1_u64).saturating_mul(m.into()))) @@ -147,9 +144,9 @@ impl WeightInfo for SubstrateWeight { .saturating_add(T::DbWeight::get().writes((2_u64).saturating_mul(n.into()))) .saturating_add(T::DbWeight::get().writes((1_u64).saturating_mul(m.into()))) .saturating_add(T::DbWeight::get().writes((1_u64).saturating_mul(a.into()))) - .saturating_add(Weight::from_parts(0, 2597).saturating_mul(n.into())) .saturating_add(Weight::from_parts(0, 2839).saturating_mul(a.into())) .saturating_add(Weight::from_parts(0, 2583).saturating_mul(m.into())) + .saturating_add(Weight::from_parts(0, 2597).saturating_mul(n.into())) } /// Storage: Uniques Asset (r:1 w:1) /// Proof: Uniques Asset (max_values: None, max_size: Some(122), added: 2597, mode: MaxEncodedLen) @@ -162,9 +159,9 @@ impl WeightInfo for SubstrateWeight { fn mint() -> Weight { // Proof Size summary in bytes: // Measured: `349` - // Estimated: `10719` - // Minimum execution time: 33_959_000 picoseconds. - Weight::from_parts(34_380_000, 10719) + // Estimated: `3643` + // Minimum execution time: 38_157_000 picoseconds. + Weight::from_parts(38_677_000, 3643) .saturating_add(T::DbWeight::get().reads(3_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) } @@ -179,9 +176,9 @@ impl WeightInfo for SubstrateWeight { fn burn() -> Weight { // Proof Size summary in bytes: // Measured: `495` - // Estimated: `7230` - // Minimum execution time: 34_194_000 picoseconds. - Weight::from_parts(34_808_000, 7230) + // Estimated: `3643` + // Minimum execution time: 39_069_000 picoseconds. + Weight::from_parts(40_442_000, 3643) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().writes(4_u64)) } @@ -196,9 +193,9 @@ impl WeightInfo for SubstrateWeight { fn transfer() -> Weight { // Proof Size summary in bytes: // Measured: `495` - // Estimated: `7230` - // Minimum execution time: 27_841_000 picoseconds. - Weight::from_parts(28_263_000, 7230) + // Estimated: `3643` + // Minimum execution time: 28_085_000 picoseconds. + Weight::from_parts(28_403_000, 3643) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().writes(4_u64)) } @@ -210,11 +207,11 @@ impl WeightInfo for SubstrateWeight { fn redeposit(i: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `805 + i * (76 ±0)` - // Estimated: `4633 + i * (2597 ±0)` - // Minimum execution time: 15_577_000 picoseconds. - Weight::from_parts(15_706_000, 4633) - // Standard Error: 17_679 - .saturating_add(Weight::from_parts(14_173_779, 0).saturating_mul(i.into())) + // Estimated: `3643 + i * (2597 ±0)` + // Minimum execution time: 16_202_000 picoseconds. + Weight::from_parts(16_380_000, 3643) + // Standard Error: 18_639 + .saturating_add(Weight::from_parts(16_047_161, 0).saturating_mul(i.into())) .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(T::DbWeight::get().reads((1_u64).saturating_mul(i.into()))) .saturating_add(T::DbWeight::get().writes(1_u64)) @@ -228,9 +225,9 @@ impl WeightInfo for SubstrateWeight { fn freeze() -> Weight { // Proof Size summary in bytes: // Measured: `495` - // Estimated: `7230` - // Minimum execution time: 19_474_000 picoseconds. - Weight::from_parts(19_807_000, 7230) + // Estimated: `3643` + // Minimum execution time: 20_131_000 picoseconds. + Weight::from_parts(20_535_000, 3643) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -241,9 +238,9 @@ impl WeightInfo for SubstrateWeight { fn thaw() -> Weight { // Proof Size summary in bytes: // Measured: `495` - // Estimated: `7230` - // Minimum execution time: 19_526_000 picoseconds. - Weight::from_parts(19_818_000, 7230) + // Estimated: `3643` + // Minimum execution time: 19_895_000 picoseconds. + Weight::from_parts(20_198_000, 3643) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -253,8 +250,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `349` // Estimated: `3643` - // Minimum execution time: 15_324_000 picoseconds. - Weight::from_parts(15_599_000, 3643) + // Minimum execution time: 15_312_000 picoseconds. + Weight::from_parts(15_555_000, 3643) .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -264,8 +261,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `349` // Estimated: `3643` - // Minimum execution time: 15_294_000 picoseconds. - Weight::from_parts(15_504_000, 3643) + // Minimum execution time: 15_145_000 picoseconds. + Weight::from_parts(15_371_000, 3643) .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -278,9 +275,9 @@ impl WeightInfo for SubstrateWeight { fn transfer_ownership() -> Weight { // Proof Size summary in bytes: // Measured: `423` - // Estimated: `7160` - // Minimum execution time: 24_007_000 picoseconds. - Weight::from_parts(24_555_000, 7160) + // Estimated: `3643` + // Minimum execution time: 23_800_000 picoseconds. + Weight::from_parts(23_991_000, 3643) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().writes(4_u64)) } @@ -290,8 +287,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `349` // Estimated: `3643` - // Minimum execution time: 15_616_000 picoseconds. - Weight::from_parts(15_897_000, 3643) + // Minimum execution time: 15_929_000 picoseconds. + Weight::from_parts(16_219_000, 3643) .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -303,8 +300,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `349` // Estimated: `3643` - // Minimum execution time: 18_459_000 picoseconds. - Weight::from_parts(18_705_000, 3643) + // Minimum execution time: 18_617_000 picoseconds. + Weight::from_parts(19_016_000, 3643) .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(T::DbWeight::get().writes(2_u64)) } @@ -317,9 +314,9 @@ impl WeightInfo for SubstrateWeight { fn set_attribute() -> Weight { // Proof Size summary in bytes: // Measured: `547` - // Estimated: `11045` - // Minimum execution time: 39_056_000 picoseconds. - Weight::from_parts(39_513_000, 11045) + // Estimated: `3829` + // Minimum execution time: 41_982_000 picoseconds. + Weight::from_parts(42_329_000, 3829) .saturating_add(T::DbWeight::get().reads(3_u64)) .saturating_add(T::DbWeight::get().writes(2_u64)) } @@ -332,9 +329,9 @@ impl WeightInfo for SubstrateWeight { fn clear_attribute() -> Weight { // Proof Size summary in bytes: // Measured: `936` - // Estimated: `11045` - // Minimum execution time: 37_441_000 picoseconds. - Weight::from_parts(37_859_000, 11045) + // Estimated: `3829` + // Minimum execution time: 39_921_000 picoseconds. + Weight::from_parts(40_499_000, 3829) .saturating_add(T::DbWeight::get().reads(3_u64)) .saturating_add(T::DbWeight::get().writes(2_u64)) } @@ -345,9 +342,9 @@ impl WeightInfo for SubstrateWeight { fn set_metadata() -> Weight { // Proof Size summary in bytes: // Measured: `415` - // Estimated: `7216` - // Minimum execution time: 29_456_000 picoseconds. - Weight::from_parts(29_930_000, 7216) + // Estimated: `3643` + // Minimum execution time: 31_774_000 picoseconds. + Weight::from_parts(32_327_000, 3643) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().writes(2_u64)) } @@ -358,9 +355,9 @@ impl WeightInfo for SubstrateWeight { fn clear_metadata() -> Weight { // Proof Size summary in bytes: // Measured: `547` - // Estimated: `7216` - // Minimum execution time: 29_817_000 picoseconds. - Weight::from_parts(30_364_000, 7216) + // Estimated: `3643` + // Minimum execution time: 32_551_000 picoseconds. + Weight::from_parts(32_891_000, 3643) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().writes(2_u64)) } @@ -371,9 +368,9 @@ impl WeightInfo for SubstrateWeight { fn set_collection_metadata() -> Weight { // Proof Size summary in bytes: // Measured: `349` - // Estimated: `7196` - // Minimum execution time: 29_392_000 picoseconds. - Weight::from_parts(29_878_000, 7196) + // Estimated: `3643` + // Minimum execution time: 33_490_000 picoseconds. + Weight::from_parts(34_617_000, 3643) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().writes(2_u64)) } @@ -384,9 +381,9 @@ impl WeightInfo for SubstrateWeight { fn clear_collection_metadata() -> Weight { // Proof Size summary in bytes: // Measured: `461` - // Estimated: `7196` - // Minimum execution time: 27_234_000 picoseconds. - Weight::from_parts(27_664_000, 7196) + // Estimated: `3643` + // Minimum execution time: 31_691_000 picoseconds. + Weight::from_parts(32_042_000, 3643) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -397,9 +394,9 @@ impl WeightInfo for SubstrateWeight { fn approve_transfer() -> Weight { // Proof Size summary in bytes: // Measured: `495` - // Estimated: `7230` - // Minimum execution time: 20_447_000 picoseconds. - Weight::from_parts(20_886_000, 7230) + // Estimated: `3643` + // Minimum execution time: 20_738_000 picoseconds. + Weight::from_parts(21_067_000, 3643) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -410,9 +407,9 @@ impl WeightInfo for SubstrateWeight { fn cancel_approval() -> Weight { // Proof Size summary in bytes: // Measured: `528` - // Estimated: `7230` - // Minimum execution time: 20_934_000 picoseconds. - Weight::from_parts(21_271_000, 7230) + // Estimated: `3643` + // Minimum execution time: 20_404_000 picoseconds. + Weight::from_parts(20_999_000, 3643) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -422,8 +419,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `109` // Estimated: `3517` - // Minimum execution time: 17_004_000 picoseconds. - Weight::from_parts(17_401_000, 3517) + // Minimum execution time: 17_047_000 picoseconds. + Weight::from_parts(17_307_000, 3517) .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -434,9 +431,9 @@ impl WeightInfo for SubstrateWeight { fn set_collection_max_supply() -> Weight { // Proof Size summary in bytes: // Measured: `349` - // Estimated: `7132` - // Minimum execution time: 17_371_000 picoseconds. - Weight::from_parts(18_103_000, 7132) + // Estimated: `3643` + // Minimum execution time: 17_829_000 picoseconds. + Weight::from_parts(18_194_000, 3643) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -448,8 +445,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `326` // Estimated: `3587` - // Minimum execution time: 17_624_000 picoseconds. - Weight::from_parts(17_866_000, 3587) + // Minimum execution time: 17_620_000 picoseconds. + Weight::from_parts(17_931_000, 3587) .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -464,9 +461,9 @@ impl WeightInfo for SubstrateWeight { fn buy_item() -> Weight { // Proof Size summary in bytes: // Measured: `607` - // Estimated: `10784` - // Minimum execution time: 39_736_000 picoseconds. - Weight::from_parts(40_855_000, 10784) + // Estimated: `3643` + // Minimum execution time: 39_550_000 picoseconds. + Weight::from_parts(40_052_000, 3643) .saturating_add(T::DbWeight::get().reads(3_u64)) .saturating_add(T::DbWeight::get().writes(4_u64)) } @@ -482,8 +479,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `249` // Estimated: `3643` - // Minimum execution time: 27_805_000 picoseconds. - Weight::from_parts(28_303_000, 3643) + // Minimum execution time: 32_067_000 picoseconds. + Weight::from_parts(32_817_000, 3643) .saturating_add(RocksDbWeight::get().reads(1_u64)) .saturating_add(RocksDbWeight::get().writes(2_u64)) } @@ -495,8 +492,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `109` // Estimated: `3643` - // Minimum execution time: 16_257_000 picoseconds. - Weight::from_parts(16_626_000, 3643) + // Minimum execution time: 16_365_000 picoseconds. + Weight::from_parts(16_707_000, 3643) .saturating_add(RocksDbWeight::get().reads(1_u64)) .saturating_add(RocksDbWeight::get().writes(2_u64)) } @@ -521,16 +518,16 @@ impl WeightInfo for () { /// The range of component `a` is `[0, 1000]`. fn destroy(n: u32, m: u32, a: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `418 + n * (76 ±0) + m * (56 ±0) + a * (107 ±0)` - // Estimated: `9210 + n * (2597 ±0) + a * (2839 ±0) + m * (2583 ±0)` - // Minimum execution time: 2_510_772_000 picoseconds. - Weight::from_parts(2_522_511_000, 9210) - // Standard Error: 27_455 - .saturating_add(Weight::from_parts(6_708_827, 0).saturating_mul(n.into())) - // Standard Error: 27_455 - .saturating_add(Weight::from_parts(375_591, 0).saturating_mul(m.into())) - // Standard Error: 27_455 - .saturating_add(Weight::from_parts(251_697, 0).saturating_mul(a.into())) + // Measured: `418 + a * (107 ±0) + m * (56 ±0) + n * (76 ±0)` + // Estimated: `3643 + a * (2839 ±0) + m * (2583 ±0) + n * (2597 ±0)` + // Minimum execution time: 2_498_918_000 picoseconds. + Weight::from_parts(2_516_809_000, 3643) + // Standard Error: 26_297 + .saturating_add(Weight::from_parts(6_648_035, 0).saturating_mul(n.into())) + // Standard Error: 26_297 + .saturating_add(Weight::from_parts(354_268, 0).saturating_mul(m.into())) + // Standard Error: 26_297 + .saturating_add(Weight::from_parts(223_770, 0).saturating_mul(a.into())) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().reads((1_u64).saturating_mul(n.into()))) .saturating_add(RocksDbWeight::get().reads((1_u64).saturating_mul(m.into()))) @@ -539,9 +536,9 @@ impl WeightInfo for () { .saturating_add(RocksDbWeight::get().writes((2_u64).saturating_mul(n.into()))) .saturating_add(RocksDbWeight::get().writes((1_u64).saturating_mul(m.into()))) .saturating_add(RocksDbWeight::get().writes((1_u64).saturating_mul(a.into()))) - .saturating_add(Weight::from_parts(0, 2597).saturating_mul(n.into())) .saturating_add(Weight::from_parts(0, 2839).saturating_mul(a.into())) .saturating_add(Weight::from_parts(0, 2583).saturating_mul(m.into())) + .saturating_add(Weight::from_parts(0, 2597).saturating_mul(n.into())) } /// Storage: Uniques Asset (r:1 w:1) /// Proof: Uniques Asset (max_values: None, max_size: Some(122), added: 2597, mode: MaxEncodedLen) @@ -554,9 +551,9 @@ impl WeightInfo for () { fn mint() -> Weight { // Proof Size summary in bytes: // Measured: `349` - // Estimated: `10719` - // Minimum execution time: 33_959_000 picoseconds. - Weight::from_parts(34_380_000, 10719) + // Estimated: `3643` + // Minimum execution time: 38_157_000 picoseconds. + Weight::from_parts(38_677_000, 3643) .saturating_add(RocksDbWeight::get().reads(3_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) } @@ -571,9 +568,9 @@ impl WeightInfo for () { fn burn() -> Weight { // Proof Size summary in bytes: // Measured: `495` - // Estimated: `7230` - // Minimum execution time: 34_194_000 picoseconds. - Weight::from_parts(34_808_000, 7230) + // Estimated: `3643` + // Minimum execution time: 39_069_000 picoseconds. + Weight::from_parts(40_442_000, 3643) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().writes(4_u64)) } @@ -588,9 +585,9 @@ impl WeightInfo for () { fn transfer() -> Weight { // Proof Size summary in bytes: // Measured: `495` - // Estimated: `7230` - // Minimum execution time: 27_841_000 picoseconds. - Weight::from_parts(28_263_000, 7230) + // Estimated: `3643` + // Minimum execution time: 28_085_000 picoseconds. + Weight::from_parts(28_403_000, 3643) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().writes(4_u64)) } @@ -602,11 +599,11 @@ impl WeightInfo for () { fn redeposit(i: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `805 + i * (76 ±0)` - // Estimated: `4633 + i * (2597 ±0)` - // Minimum execution time: 15_577_000 picoseconds. - Weight::from_parts(15_706_000, 4633) - // Standard Error: 17_679 - .saturating_add(Weight::from_parts(14_173_779, 0).saturating_mul(i.into())) + // Estimated: `3643 + i * (2597 ±0)` + // Minimum execution time: 16_202_000 picoseconds. + Weight::from_parts(16_380_000, 3643) + // Standard Error: 18_639 + .saturating_add(Weight::from_parts(16_047_161, 0).saturating_mul(i.into())) .saturating_add(RocksDbWeight::get().reads(1_u64)) .saturating_add(RocksDbWeight::get().reads((1_u64).saturating_mul(i.into()))) .saturating_add(RocksDbWeight::get().writes(1_u64)) @@ -620,9 +617,9 @@ impl WeightInfo for () { fn freeze() -> Weight { // Proof Size summary in bytes: // Measured: `495` - // Estimated: `7230` - // Minimum execution time: 19_474_000 picoseconds. - Weight::from_parts(19_807_000, 7230) + // Estimated: `3643` + // Minimum execution time: 20_131_000 picoseconds. + Weight::from_parts(20_535_000, 3643) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } @@ -633,9 +630,9 @@ impl WeightInfo for () { fn thaw() -> Weight { // Proof Size summary in bytes: // Measured: `495` - // Estimated: `7230` - // Minimum execution time: 19_526_000 picoseconds. - Weight::from_parts(19_818_000, 7230) + // Estimated: `3643` + // Minimum execution time: 19_895_000 picoseconds. + Weight::from_parts(20_198_000, 3643) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } @@ -645,8 +642,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `349` // Estimated: `3643` - // Minimum execution time: 15_324_000 picoseconds. - Weight::from_parts(15_599_000, 3643) + // Minimum execution time: 15_312_000 picoseconds. + Weight::from_parts(15_555_000, 3643) .saturating_add(RocksDbWeight::get().reads(1_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } @@ -656,8 +653,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `349` // Estimated: `3643` - // Minimum execution time: 15_294_000 picoseconds. - Weight::from_parts(15_504_000, 3643) + // Minimum execution time: 15_145_000 picoseconds. + Weight::from_parts(15_371_000, 3643) .saturating_add(RocksDbWeight::get().reads(1_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } @@ -670,9 +667,9 @@ impl WeightInfo for () { fn transfer_ownership() -> Weight { // Proof Size summary in bytes: // Measured: `423` - // Estimated: `7160` - // Minimum execution time: 24_007_000 picoseconds. - Weight::from_parts(24_555_000, 7160) + // Estimated: `3643` + // Minimum execution time: 23_800_000 picoseconds. + Weight::from_parts(23_991_000, 3643) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().writes(4_u64)) } @@ -682,8 +679,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `349` // Estimated: `3643` - // Minimum execution time: 15_616_000 picoseconds. - Weight::from_parts(15_897_000, 3643) + // Minimum execution time: 15_929_000 picoseconds. + Weight::from_parts(16_219_000, 3643) .saturating_add(RocksDbWeight::get().reads(1_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } @@ -695,8 +692,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `349` // Estimated: `3643` - // Minimum execution time: 18_459_000 picoseconds. - Weight::from_parts(18_705_000, 3643) + // Minimum execution time: 18_617_000 picoseconds. + Weight::from_parts(19_016_000, 3643) .saturating_add(RocksDbWeight::get().reads(1_u64)) .saturating_add(RocksDbWeight::get().writes(2_u64)) } @@ -709,9 +706,9 @@ impl WeightInfo for () { fn set_attribute() -> Weight { // Proof Size summary in bytes: // Measured: `547` - // Estimated: `11045` - // Minimum execution time: 39_056_000 picoseconds. - Weight::from_parts(39_513_000, 11045) + // Estimated: `3829` + // Minimum execution time: 41_982_000 picoseconds. + Weight::from_parts(42_329_000, 3829) .saturating_add(RocksDbWeight::get().reads(3_u64)) .saturating_add(RocksDbWeight::get().writes(2_u64)) } @@ -724,9 +721,9 @@ impl WeightInfo for () { fn clear_attribute() -> Weight { // Proof Size summary in bytes: // Measured: `936` - // Estimated: `11045` - // Minimum execution time: 37_441_000 picoseconds. - Weight::from_parts(37_859_000, 11045) + // Estimated: `3829` + // Minimum execution time: 39_921_000 picoseconds. + Weight::from_parts(40_499_000, 3829) .saturating_add(RocksDbWeight::get().reads(3_u64)) .saturating_add(RocksDbWeight::get().writes(2_u64)) } @@ -737,9 +734,9 @@ impl WeightInfo for () { fn set_metadata() -> Weight { // Proof Size summary in bytes: // Measured: `415` - // Estimated: `7216` - // Minimum execution time: 29_456_000 picoseconds. - Weight::from_parts(29_930_000, 7216) + // Estimated: `3643` + // Minimum execution time: 31_774_000 picoseconds. + Weight::from_parts(32_327_000, 3643) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().writes(2_u64)) } @@ -750,9 +747,9 @@ impl WeightInfo for () { fn clear_metadata() -> Weight { // Proof Size summary in bytes: // Measured: `547` - // Estimated: `7216` - // Minimum execution time: 29_817_000 picoseconds. - Weight::from_parts(30_364_000, 7216) + // Estimated: `3643` + // Minimum execution time: 32_551_000 picoseconds. + Weight::from_parts(32_891_000, 3643) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().writes(2_u64)) } @@ -763,9 +760,9 @@ impl WeightInfo for () { fn set_collection_metadata() -> Weight { // Proof Size summary in bytes: // Measured: `349` - // Estimated: `7196` - // Minimum execution time: 29_392_000 picoseconds. - Weight::from_parts(29_878_000, 7196) + // Estimated: `3643` + // Minimum execution time: 33_490_000 picoseconds. + Weight::from_parts(34_617_000, 3643) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().writes(2_u64)) } @@ -776,9 +773,9 @@ impl WeightInfo for () { fn clear_collection_metadata() -> Weight { // Proof Size summary in bytes: // Measured: `461` - // Estimated: `7196` - // Minimum execution time: 27_234_000 picoseconds. - Weight::from_parts(27_664_000, 7196) + // Estimated: `3643` + // Minimum execution time: 31_691_000 picoseconds. + Weight::from_parts(32_042_000, 3643) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } @@ -789,9 +786,9 @@ impl WeightInfo for () { fn approve_transfer() -> Weight { // Proof Size summary in bytes: // Measured: `495` - // Estimated: `7230` - // Minimum execution time: 20_447_000 picoseconds. - Weight::from_parts(20_886_000, 7230) + // Estimated: `3643` + // Minimum execution time: 20_738_000 picoseconds. + Weight::from_parts(21_067_000, 3643) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } @@ -802,9 +799,9 @@ impl WeightInfo for () { fn cancel_approval() -> Weight { // Proof Size summary in bytes: // Measured: `528` - // Estimated: `7230` - // Minimum execution time: 20_934_000 picoseconds. - Weight::from_parts(21_271_000, 7230) + // Estimated: `3643` + // Minimum execution time: 20_404_000 picoseconds. + Weight::from_parts(20_999_000, 3643) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } @@ -814,8 +811,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `109` // Estimated: `3517` - // Minimum execution time: 17_004_000 picoseconds. - Weight::from_parts(17_401_000, 3517) + // Minimum execution time: 17_047_000 picoseconds. + Weight::from_parts(17_307_000, 3517) .saturating_add(RocksDbWeight::get().reads(1_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } @@ -826,9 +823,9 @@ impl WeightInfo for () { fn set_collection_max_supply() -> Weight { // Proof Size summary in bytes: // Measured: `349` - // Estimated: `7132` - // Minimum execution time: 17_371_000 picoseconds. - Weight::from_parts(18_103_000, 7132) + // Estimated: `3643` + // Minimum execution time: 17_829_000 picoseconds. + Weight::from_parts(18_194_000, 3643) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } @@ -840,8 +837,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `326` // Estimated: `3587` - // Minimum execution time: 17_624_000 picoseconds. - Weight::from_parts(17_866_000, 3587) + // Minimum execution time: 17_620_000 picoseconds. + Weight::from_parts(17_931_000, 3587) .saturating_add(RocksDbWeight::get().reads(1_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } @@ -856,9 +853,9 @@ impl WeightInfo for () { fn buy_item() -> Weight { // Proof Size summary in bytes: // Measured: `607` - // Estimated: `10784` - // Minimum execution time: 39_736_000 picoseconds. - Weight::from_parts(40_855_000, 10784) + // Estimated: `3643` + // Minimum execution time: 39_550_000 picoseconds. + Weight::from_parts(40_052_000, 3643) .saturating_add(RocksDbWeight::get().reads(3_u64)) .saturating_add(RocksDbWeight::get().writes(4_u64)) } diff --git a/frame/utility/src/weights.rs b/frame/utility/src/weights.rs index 0c50de4f5..0ff261a33 100644 --- a/frame/utility/src/weights.rs +++ b/frame/utility/src/weights.rs @@ -18,9 +18,9 @@ //! Autogenerated weights for pallet_utility //! //! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev -//! DATE: 2023-03-16, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! DATE: 2023-04-06, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` //! WORST CASE MAP SIZE: `1000000` -//! HOSTNAME: `bm3`, CPU: `Intel(R) Core(TM) i7-7700K CPU @ 4.20GHz` +//! HOSTNAME: `bm2`, CPU: `Intel(R) Core(TM) i7-7700K CPU @ 4.20GHz` //! EXECUTION: Some(Wasm), WASM-EXECUTION: Compiled, CHAIN: Some("dev"), DB CACHE: 1024 // Executed Command: @@ -31,9 +31,6 @@ // --steps=50 // --repeat=20 // --pallet=pallet_utility -// --no-storage-info -// --no-median-slopes -// --no-min-squares // --extrinsic=* // --execution=wasm // --wasm-execution=compiled @@ -66,44 +63,44 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 7_828_000 picoseconds. - Weight::from_parts(13_806_712, 0) - // Standard Error: 2_750 - .saturating_add(Weight::from_parts(4_169_287, 0).saturating_mul(c.into())) + // Minimum execution time: 7_932_000 picoseconds. + Weight::from_parts(24_064_040, 0) + // Standard Error: 2_486 + .saturating_add(Weight::from_parts(4_238_449, 0).saturating_mul(c.into())) } fn as_derivative() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 5_835_000 picoseconds. - Weight::from_parts(6_305_000, 0) + // Minimum execution time: 5_536_000 picoseconds. + Weight::from_parts(5_963_000, 0) } /// The range of component `c` is `[0, 1000]`. fn batch_all(c: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 8_010_000 picoseconds. - Weight::from_parts(10_539_696, 0) - // Standard Error: 2_681 - .saturating_add(Weight::from_parts(4_368_716, 0).saturating_mul(c.into())) + // Minimum execution time: 7_820_000 picoseconds. + Weight::from_parts(18_969_535, 0) + // Standard Error: 2_228 + .saturating_add(Weight::from_parts(4_448_073, 0).saturating_mul(c.into())) } fn dispatch_as() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 10_139_000 picoseconds. - Weight::from_parts(10_679_000, 0) + // Minimum execution time: 9_811_000 picoseconds. + Weight::from_parts(10_162_000, 0) } /// The range of component `c` is `[0, 1000]`. fn force_batch(c: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 7_960_000 picoseconds. - Weight::from_parts(16_874_350, 0) - // Standard Error: 3_040 - .saturating_add(Weight::from_parts(4_151_507, 0).saturating_mul(c.into())) + // Minimum execution time: 7_829_000 picoseconds. + Weight::from_parts(12_960_288, 0) + // Standard Error: 2_222 + .saturating_add(Weight::from_parts(4_272_019, 0).saturating_mul(c.into())) } } @@ -114,43 +111,43 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 7_828_000 picoseconds. - Weight::from_parts(13_806_712, 0) - // Standard Error: 2_750 - .saturating_add(Weight::from_parts(4_169_287, 0).saturating_mul(c.into())) + // Minimum execution time: 7_932_000 picoseconds. + Weight::from_parts(24_064_040, 0) + // Standard Error: 2_486 + .saturating_add(Weight::from_parts(4_238_449, 0).saturating_mul(c.into())) } fn as_derivative() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 5_835_000 picoseconds. - Weight::from_parts(6_305_000, 0) + // Minimum execution time: 5_536_000 picoseconds. + Weight::from_parts(5_963_000, 0) } /// The range of component `c` is `[0, 1000]`. fn batch_all(c: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 8_010_000 picoseconds. - Weight::from_parts(10_539_696, 0) - // Standard Error: 2_681 - .saturating_add(Weight::from_parts(4_368_716, 0).saturating_mul(c.into())) + // Minimum execution time: 7_820_000 picoseconds. + Weight::from_parts(18_969_535, 0) + // Standard Error: 2_228 + .saturating_add(Weight::from_parts(4_448_073, 0).saturating_mul(c.into())) } fn dispatch_as() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 10_139_000 picoseconds. - Weight::from_parts(10_679_000, 0) + // Minimum execution time: 9_811_000 picoseconds. + Weight::from_parts(10_162_000, 0) } /// The range of component `c` is `[0, 1000]`. fn force_batch(c: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 7_960_000 picoseconds. - Weight::from_parts(16_874_350, 0) - // Standard Error: 3_040 - .saturating_add(Weight::from_parts(4_151_507, 0).saturating_mul(c.into())) + // Minimum execution time: 7_829_000 picoseconds. + Weight::from_parts(12_960_288, 0) + // Standard Error: 2_222 + .saturating_add(Weight::from_parts(4_272_019, 0).saturating_mul(c.into())) } } diff --git a/frame/vesting/src/weights.rs b/frame/vesting/src/weights.rs index 400984f06..4cf3b3dcf 100644 --- a/frame/vesting/src/weights.rs +++ b/frame/vesting/src/weights.rs @@ -18,9 +18,9 @@ //! Autogenerated weights for pallet_vesting //! //! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev -//! DATE: 2023-03-16, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! DATE: 2023-04-06, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` //! WORST CASE MAP SIZE: `1000000` -//! HOSTNAME: `bm3`, CPU: `Intel(R) Core(TM) i7-7700K CPU @ 4.20GHz` +//! HOSTNAME: `bm2`, CPU: `Intel(R) Core(TM) i7-7700K CPU @ 4.20GHz` //! EXECUTION: Some(Wasm), WASM-EXECUTION: Compiled, CHAIN: Some("dev"), DB CACHE: 1024 // Executed Command: @@ -31,9 +31,6 @@ // --steps=50 // --repeat=20 // --pallet=pallet_vesting -// --no-storage-info -// --no-median-slopes -// --no-min-squares // --extrinsic=* // --execution=wasm // --wasm-execution=compiled @@ -68,44 +65,50 @@ impl WeightInfo for SubstrateWeight { /// Proof: Vesting Vesting (max_values: None, max_size: Some(1057), added: 3532, mode: MaxEncodedLen) /// Storage: Balances Locks (r:1 w:1) /// Proof: Balances Locks (max_values: None, max_size: Some(1299), added: 3774, mode: MaxEncodedLen) + /// Storage: Balances Freezes (r:1 w:0) + /// Proof: Balances Freezes (max_values: None, max_size: Some(49), added: 2524, mode: MaxEncodedLen) /// The range of component `l` is `[0, 49]`. /// The range of component `s` is `[1, 28]`. fn vest_locked(l: u32, s: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `381 + l * (25 ±0) + s * (36 ±0)` - // Estimated: `9286` - // Minimum execution time: 31_657_000 picoseconds. - Weight::from_parts(30_569_947, 9286) - // Standard Error: 794 - .saturating_add(Weight::from_parts(63_114, 0).saturating_mul(l.into())) - // Standard Error: 1_413 - .saturating_add(Weight::from_parts(58_636, 0).saturating_mul(s.into())) - .saturating_add(T::DbWeight::get().reads(2_u64)) + // Estimated: `4764` + // Minimum execution time: 36_182_000 picoseconds. + Weight::from_parts(35_159_830, 4764) + // Standard Error: 952 + .saturating_add(Weight::from_parts(63_309, 0).saturating_mul(l.into())) + // Standard Error: 1_694 + .saturating_add(Weight::from_parts(62_244, 0).saturating_mul(s.into())) + .saturating_add(T::DbWeight::get().reads(3_u64)) .saturating_add(T::DbWeight::get().writes(2_u64)) } /// Storage: Vesting Vesting (r:1 w:1) /// Proof: Vesting Vesting (max_values: None, max_size: Some(1057), added: 3532, mode: MaxEncodedLen) /// Storage: Balances Locks (r:1 w:1) /// Proof: Balances Locks (max_values: None, max_size: Some(1299), added: 3774, mode: MaxEncodedLen) + /// Storage: Balances Freezes (r:1 w:0) + /// Proof: Balances Freezes (max_values: None, max_size: Some(49), added: 2524, mode: MaxEncodedLen) /// The range of component `l` is `[0, 49]`. /// The range of component `s` is `[1, 28]`. fn vest_unlocked(l: u32, s: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `381 + l * (25 ±0) + s * (36 ±0)` - // Estimated: `9286` - // Minimum execution time: 30_474_000 picoseconds. - Weight::from_parts(30_227_344, 9286) - // Standard Error: 1_005 - .saturating_add(Weight::from_parts(56_742, 0).saturating_mul(l.into())) - // Standard Error: 1_788 - .saturating_add(Weight::from_parts(33_890, 0).saturating_mul(s.into())) - .saturating_add(T::DbWeight::get().reads(2_u64)) + // Estimated: `4764` + // Minimum execution time: 39_344_000 picoseconds. + Weight::from_parts(38_921_936, 4764) + // Standard Error: 1_283 + .saturating_add(Weight::from_parts(61_531, 0).saturating_mul(l.into())) + // Standard Error: 2_283 + .saturating_add(Weight::from_parts(36_175, 0).saturating_mul(s.into())) + .saturating_add(T::DbWeight::get().reads(3_u64)) .saturating_add(T::DbWeight::get().writes(2_u64)) } /// Storage: Vesting Vesting (r:1 w:1) /// Proof: Vesting Vesting (max_values: None, max_size: Some(1057), added: 3532, mode: MaxEncodedLen) /// Storage: Balances Locks (r:1 w:1) /// Proof: Balances Locks (max_values: None, max_size: Some(1299), added: 3774, mode: MaxEncodedLen) + /// Storage: Balances Freezes (r:1 w:0) + /// Proof: Balances Freezes (max_values: None, max_size: Some(49), added: 2524, mode: MaxEncodedLen) /// Storage: System Account (r:1 w:1) /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) /// The range of component `l` is `[0, 49]`. @@ -113,20 +116,22 @@ impl WeightInfo for SubstrateWeight { fn vest_other_locked(l: u32, s: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `484 + l * (25 ±0) + s * (36 ±0)` - // Estimated: `12879` - // Minimum execution time: 33_681_000 picoseconds. - Weight::from_parts(32_540_534, 12879) - // Standard Error: 2_642 - .saturating_add(Weight::from_parts(62_200, 0).saturating_mul(l.into())) - // Standard Error: 4_701 - .saturating_add(Weight::from_parts(69_703, 0).saturating_mul(s.into())) - .saturating_add(T::DbWeight::get().reads(3_u64)) + // Estimated: `4764` + // Minimum execution time: 39_461_000 picoseconds. + Weight::from_parts(38_206_465, 4764) + // Standard Error: 743 + .saturating_add(Weight::from_parts(56_973, 0).saturating_mul(l.into())) + // Standard Error: 1_322 + .saturating_add(Weight::from_parts(65_059, 0).saturating_mul(s.into())) + .saturating_add(T::DbWeight::get().reads(4_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) } /// Storage: Vesting Vesting (r:1 w:1) /// Proof: Vesting Vesting (max_values: None, max_size: Some(1057), added: 3532, mode: MaxEncodedLen) /// Storage: Balances Locks (r:1 w:1) /// Proof: Balances Locks (max_values: None, max_size: Some(1299), added: 3774, mode: MaxEncodedLen) + /// Storage: Balances Freezes (r:1 w:0) + /// Proof: Balances Freezes (max_values: None, max_size: Some(49), added: 2524, mode: MaxEncodedLen) /// Storage: System Account (r:1 w:1) /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) /// The range of component `l` is `[0, 49]`. @@ -134,14 +139,14 @@ impl WeightInfo for SubstrateWeight { fn vest_other_unlocked(l: u32, s: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `484 + l * (25 ±0) + s * (36 ±0)` - // Estimated: `12879` - // Minimum execution time: 32_255_000 picoseconds. - Weight::from_parts(31_637_918, 12879) - // Standard Error: 3_135 - .saturating_add(Weight::from_parts(62_121, 0).saturating_mul(l.into())) - // Standard Error: 5_579 - .saturating_add(Weight::from_parts(61_055, 0).saturating_mul(s.into())) - .saturating_add(T::DbWeight::get().reads(3_u64)) + // Estimated: `4764` + // Minimum execution time: 42_029_000 picoseconds. + Weight::from_parts(42_153_438, 4764) + // Standard Error: 1_108 + .saturating_add(Weight::from_parts(50_058, 0).saturating_mul(l.into())) + // Standard Error: 1_971 + .saturating_add(Weight::from_parts(32_391, 0).saturating_mul(s.into())) + .saturating_add(T::DbWeight::get().reads(4_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) } /// Storage: Vesting Vesting (r:1 w:1) @@ -150,19 +155,21 @@ impl WeightInfo for SubstrateWeight { /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) /// Storage: Balances Locks (r:1 w:1) /// Proof: Balances Locks (max_values: None, max_size: Some(1299), added: 3774, mode: MaxEncodedLen) + /// Storage: Balances Freezes (r:1 w:0) + /// Proof: Balances Freezes (max_values: None, max_size: Some(49), added: 2524, mode: MaxEncodedLen) /// The range of component `l` is `[0, 49]`. /// The range of component `s` is `[0, 27]`. fn vested_transfer(l: u32, s: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `555 + l * (25 ±0) + s * (36 ±0)` - // Estimated: `12879` - // Minimum execution time: 51_697_000 picoseconds. - Weight::from_parts(52_048_055, 12879) - // Standard Error: 1_598 - .saturating_add(Weight::from_parts(60_508, 0).saturating_mul(l.into())) - // Standard Error: 2_843 - .saturating_add(Weight::from_parts(37_870, 0).saturating_mul(s.into())) - .saturating_add(T::DbWeight::get().reads(3_u64)) + // Estimated: `4764` + // Minimum execution time: 75_223_000 picoseconds. + Weight::from_parts(76_675_778, 4764) + // Standard Error: 2_534 + .saturating_add(Weight::from_parts(70_731, 0).saturating_mul(l.into())) + // Standard Error: 4_509 + .saturating_add(Weight::from_parts(108_866, 0).saturating_mul(s.into())) + .saturating_add(T::DbWeight::get().reads(4_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) } /// Storage: Vesting Vesting (r:1 w:1) @@ -171,25 +178,29 @@ impl WeightInfo for SubstrateWeight { /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) /// Storage: Balances Locks (r:1 w:1) /// Proof: Balances Locks (max_values: None, max_size: Some(1299), added: 3774, mode: MaxEncodedLen) + /// Storage: Balances Freezes (r:1 w:0) + /// Proof: Balances Freezes (max_values: None, max_size: Some(49), added: 2524, mode: MaxEncodedLen) /// The range of component `l` is `[0, 49]`. /// The range of component `s` is `[0, 27]`. fn force_vested_transfer(l: u32, s: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `658 + l * (25 ±0) + s * (36 ±0)` - // Estimated: `15482` - // Minimum execution time: 54_585_000 picoseconds. - Weight::from_parts(54_492_070, 15482) - // Standard Error: 1_694 - .saturating_add(Weight::from_parts(52_633, 0).saturating_mul(l.into())) - // Standard Error: 3_014 - .saturating_add(Weight::from_parts(45_485, 0).saturating_mul(s.into())) - .saturating_add(T::DbWeight::get().reads(4_u64)) + // Estimated: `6196` + // Minimum execution time: 76_922_000 picoseconds. + Weight::from_parts(78_634_098, 6196) + // Standard Error: 2_099 + .saturating_add(Weight::from_parts(68_218, 0).saturating_mul(l.into())) + // Standard Error: 3_736 + .saturating_add(Weight::from_parts(95_990, 0).saturating_mul(s.into())) + .saturating_add(T::DbWeight::get().reads(5_u64)) .saturating_add(T::DbWeight::get().writes(4_u64)) } /// Storage: Vesting Vesting (r:1 w:1) /// Proof: Vesting Vesting (max_values: None, max_size: Some(1057), added: 3532, mode: MaxEncodedLen) /// Storage: Balances Locks (r:1 w:1) /// Proof: Balances Locks (max_values: None, max_size: Some(1299), added: 3774, mode: MaxEncodedLen) + /// Storage: Balances Freezes (r:1 w:0) + /// Proof: Balances Freezes (max_values: None, max_size: Some(49), added: 2524, mode: MaxEncodedLen) /// Storage: System Account (r:1 w:1) /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) /// The range of component `l` is `[0, 49]`. @@ -197,20 +208,22 @@ impl WeightInfo for SubstrateWeight { fn not_unlocking_merge_schedules(l: u32, s: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `482 + l * (25 ±0) + s * (36 ±0)` - // Estimated: `12879` - // Minimum execution time: 34_312_000 picoseconds. - Weight::from_parts(33_740_101, 12879) - // Standard Error: 996 - .saturating_add(Weight::from_parts(62_123, 0).saturating_mul(l.into())) - // Standard Error: 1_841 - .saturating_add(Weight::from_parts(56_463, 0).saturating_mul(s.into())) - .saturating_add(T::DbWeight::get().reads(3_u64)) + // Estimated: `4764` + // Minimum execution time: 39_476_000 picoseconds. + Weight::from_parts(38_261_747, 4764) + // Standard Error: 1_794 + .saturating_add(Weight::from_parts(69_639, 0).saturating_mul(l.into())) + // Standard Error: 3_313 + .saturating_add(Weight::from_parts(73_202, 0).saturating_mul(s.into())) + .saturating_add(T::DbWeight::get().reads(4_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) } /// Storage: Vesting Vesting (r:1 w:1) /// Proof: Vesting Vesting (max_values: None, max_size: Some(1057), added: 3532, mode: MaxEncodedLen) /// Storage: Balances Locks (r:1 w:1) /// Proof: Balances Locks (max_values: None, max_size: Some(1299), added: 3774, mode: MaxEncodedLen) + /// Storage: Balances Freezes (r:1 w:0) + /// Proof: Balances Freezes (max_values: None, max_size: Some(49), added: 2524, mode: MaxEncodedLen) /// Storage: System Account (r:1 w:1) /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) /// The range of component `l` is `[0, 49]`. @@ -218,14 +231,14 @@ impl WeightInfo for SubstrateWeight { fn unlocking_merge_schedules(l: u32, s: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `482 + l * (25 ±0) + s * (36 ±0)` - // Estimated: `12879` - // Minimum execution time: 34_965_000 picoseconds. - Weight::from_parts(33_831_484, 12879) - // Standard Error: 1_530 - .saturating_add(Weight::from_parts(59_136, 0).saturating_mul(l.into())) - // Standard Error: 2_827 - .saturating_add(Weight::from_parts(58_493, 0).saturating_mul(s.into())) - .saturating_add(T::DbWeight::get().reads(3_u64)) + // Estimated: `4764` + // Minimum execution time: 43_764_000 picoseconds. + Weight::from_parts(42_679_386, 4764) + // Standard Error: 1_224 + .saturating_add(Weight::from_parts(65_857, 0).saturating_mul(l.into())) + // Standard Error: 2_261 + .saturating_add(Weight::from_parts(70_861, 0).saturating_mul(s.into())) + .saturating_add(T::DbWeight::get().reads(4_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) } } @@ -236,44 +249,50 @@ impl WeightInfo for () { /// Proof: Vesting Vesting (max_values: None, max_size: Some(1057), added: 3532, mode: MaxEncodedLen) /// Storage: Balances Locks (r:1 w:1) /// Proof: Balances Locks (max_values: None, max_size: Some(1299), added: 3774, mode: MaxEncodedLen) + /// Storage: Balances Freezes (r:1 w:0) + /// Proof: Balances Freezes (max_values: None, max_size: Some(49), added: 2524, mode: MaxEncodedLen) /// The range of component `l` is `[0, 49]`. /// The range of component `s` is `[1, 28]`. fn vest_locked(l: u32, s: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `381 + l * (25 ±0) + s * (36 ±0)` - // Estimated: `9286` - // Minimum execution time: 31_657_000 picoseconds. - Weight::from_parts(30_569_947, 9286) - // Standard Error: 794 - .saturating_add(Weight::from_parts(63_114, 0).saturating_mul(l.into())) - // Standard Error: 1_413 - .saturating_add(Weight::from_parts(58_636, 0).saturating_mul(s.into())) - .saturating_add(RocksDbWeight::get().reads(2_u64)) + // Estimated: `4764` + // Minimum execution time: 36_182_000 picoseconds. + Weight::from_parts(35_159_830, 4764) + // Standard Error: 952 + .saturating_add(Weight::from_parts(63_309, 0).saturating_mul(l.into())) + // Standard Error: 1_694 + .saturating_add(Weight::from_parts(62_244, 0).saturating_mul(s.into())) + .saturating_add(RocksDbWeight::get().reads(3_u64)) .saturating_add(RocksDbWeight::get().writes(2_u64)) } /// Storage: Vesting Vesting (r:1 w:1) /// Proof: Vesting Vesting (max_values: None, max_size: Some(1057), added: 3532, mode: MaxEncodedLen) /// Storage: Balances Locks (r:1 w:1) /// Proof: Balances Locks (max_values: None, max_size: Some(1299), added: 3774, mode: MaxEncodedLen) + /// Storage: Balances Freezes (r:1 w:0) + /// Proof: Balances Freezes (max_values: None, max_size: Some(49), added: 2524, mode: MaxEncodedLen) /// The range of component `l` is `[0, 49]`. /// The range of component `s` is `[1, 28]`. fn vest_unlocked(l: u32, s: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `381 + l * (25 ±0) + s * (36 ±0)` - // Estimated: `9286` - // Minimum execution time: 30_474_000 picoseconds. - Weight::from_parts(30_227_344, 9286) - // Standard Error: 1_005 - .saturating_add(Weight::from_parts(56_742, 0).saturating_mul(l.into())) - // Standard Error: 1_788 - .saturating_add(Weight::from_parts(33_890, 0).saturating_mul(s.into())) - .saturating_add(RocksDbWeight::get().reads(2_u64)) + // Estimated: `4764` + // Minimum execution time: 39_344_000 picoseconds. + Weight::from_parts(38_921_936, 4764) + // Standard Error: 1_283 + .saturating_add(Weight::from_parts(61_531, 0).saturating_mul(l.into())) + // Standard Error: 2_283 + .saturating_add(Weight::from_parts(36_175, 0).saturating_mul(s.into())) + .saturating_add(RocksDbWeight::get().reads(3_u64)) .saturating_add(RocksDbWeight::get().writes(2_u64)) } /// Storage: Vesting Vesting (r:1 w:1) /// Proof: Vesting Vesting (max_values: None, max_size: Some(1057), added: 3532, mode: MaxEncodedLen) /// Storage: Balances Locks (r:1 w:1) /// Proof: Balances Locks (max_values: None, max_size: Some(1299), added: 3774, mode: MaxEncodedLen) + /// Storage: Balances Freezes (r:1 w:0) + /// Proof: Balances Freezes (max_values: None, max_size: Some(49), added: 2524, mode: MaxEncodedLen) /// Storage: System Account (r:1 w:1) /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) /// The range of component `l` is `[0, 49]`. @@ -281,20 +300,22 @@ impl WeightInfo for () { fn vest_other_locked(l: u32, s: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `484 + l * (25 ±0) + s * (36 ±0)` - // Estimated: `12879` - // Minimum execution time: 33_681_000 picoseconds. - Weight::from_parts(32_540_534, 12879) - // Standard Error: 2_642 - .saturating_add(Weight::from_parts(62_200, 0).saturating_mul(l.into())) - // Standard Error: 4_701 - .saturating_add(Weight::from_parts(69_703, 0).saturating_mul(s.into())) - .saturating_add(RocksDbWeight::get().reads(3_u64)) + // Estimated: `4764` + // Minimum execution time: 39_461_000 picoseconds. + Weight::from_parts(38_206_465, 4764) + // Standard Error: 743 + .saturating_add(Weight::from_parts(56_973, 0).saturating_mul(l.into())) + // Standard Error: 1_322 + .saturating_add(Weight::from_parts(65_059, 0).saturating_mul(s.into())) + .saturating_add(RocksDbWeight::get().reads(4_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) } /// Storage: Vesting Vesting (r:1 w:1) /// Proof: Vesting Vesting (max_values: None, max_size: Some(1057), added: 3532, mode: MaxEncodedLen) /// Storage: Balances Locks (r:1 w:1) /// Proof: Balances Locks (max_values: None, max_size: Some(1299), added: 3774, mode: MaxEncodedLen) + /// Storage: Balances Freezes (r:1 w:0) + /// Proof: Balances Freezes (max_values: None, max_size: Some(49), added: 2524, mode: MaxEncodedLen) /// Storage: System Account (r:1 w:1) /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) /// The range of component `l` is `[0, 49]`. @@ -302,14 +323,14 @@ impl WeightInfo for () { fn vest_other_unlocked(l: u32, s: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `484 + l * (25 ±0) + s * (36 ±0)` - // Estimated: `12879` - // Minimum execution time: 32_255_000 picoseconds. - Weight::from_parts(31_637_918, 12879) - // Standard Error: 3_135 - .saturating_add(Weight::from_parts(62_121, 0).saturating_mul(l.into())) - // Standard Error: 5_579 - .saturating_add(Weight::from_parts(61_055, 0).saturating_mul(s.into())) - .saturating_add(RocksDbWeight::get().reads(3_u64)) + // Estimated: `4764` + // Minimum execution time: 42_029_000 picoseconds. + Weight::from_parts(42_153_438, 4764) + // Standard Error: 1_108 + .saturating_add(Weight::from_parts(50_058, 0).saturating_mul(l.into())) + // Standard Error: 1_971 + .saturating_add(Weight::from_parts(32_391, 0).saturating_mul(s.into())) + .saturating_add(RocksDbWeight::get().reads(4_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) } /// Storage: Vesting Vesting (r:1 w:1) @@ -318,19 +339,21 @@ impl WeightInfo for () { /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) /// Storage: Balances Locks (r:1 w:1) /// Proof: Balances Locks (max_values: None, max_size: Some(1299), added: 3774, mode: MaxEncodedLen) + /// Storage: Balances Freezes (r:1 w:0) + /// Proof: Balances Freezes (max_values: None, max_size: Some(49), added: 2524, mode: MaxEncodedLen) /// The range of component `l` is `[0, 49]`. /// The range of component `s` is `[0, 27]`. fn vested_transfer(l: u32, s: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `555 + l * (25 ±0) + s * (36 ±0)` - // Estimated: `12879` - // Minimum execution time: 51_697_000 picoseconds. - Weight::from_parts(52_048_055, 12879) - // Standard Error: 1_598 - .saturating_add(Weight::from_parts(60_508, 0).saturating_mul(l.into())) - // Standard Error: 2_843 - .saturating_add(Weight::from_parts(37_870, 0).saturating_mul(s.into())) - .saturating_add(RocksDbWeight::get().reads(3_u64)) + // Estimated: `4764` + // Minimum execution time: 75_223_000 picoseconds. + Weight::from_parts(76_675_778, 4764) + // Standard Error: 2_534 + .saturating_add(Weight::from_parts(70_731, 0).saturating_mul(l.into())) + // Standard Error: 4_509 + .saturating_add(Weight::from_parts(108_866, 0).saturating_mul(s.into())) + .saturating_add(RocksDbWeight::get().reads(4_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) } /// Storage: Vesting Vesting (r:1 w:1) @@ -339,25 +362,29 @@ impl WeightInfo for () { /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) /// Storage: Balances Locks (r:1 w:1) /// Proof: Balances Locks (max_values: None, max_size: Some(1299), added: 3774, mode: MaxEncodedLen) + /// Storage: Balances Freezes (r:1 w:0) + /// Proof: Balances Freezes (max_values: None, max_size: Some(49), added: 2524, mode: MaxEncodedLen) /// The range of component `l` is `[0, 49]`. /// The range of component `s` is `[0, 27]`. fn force_vested_transfer(l: u32, s: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `658 + l * (25 ±0) + s * (36 ±0)` - // Estimated: `15482` - // Minimum execution time: 54_585_000 picoseconds. - Weight::from_parts(54_492_070, 15482) - // Standard Error: 1_694 - .saturating_add(Weight::from_parts(52_633, 0).saturating_mul(l.into())) - // Standard Error: 3_014 - .saturating_add(Weight::from_parts(45_485, 0).saturating_mul(s.into())) - .saturating_add(RocksDbWeight::get().reads(4_u64)) + // Estimated: `6196` + // Minimum execution time: 76_922_000 picoseconds. + Weight::from_parts(78_634_098, 6196) + // Standard Error: 2_099 + .saturating_add(Weight::from_parts(68_218, 0).saturating_mul(l.into())) + // Standard Error: 3_736 + .saturating_add(Weight::from_parts(95_990, 0).saturating_mul(s.into())) + .saturating_add(RocksDbWeight::get().reads(5_u64)) .saturating_add(RocksDbWeight::get().writes(4_u64)) } /// Storage: Vesting Vesting (r:1 w:1) /// Proof: Vesting Vesting (max_values: None, max_size: Some(1057), added: 3532, mode: MaxEncodedLen) /// Storage: Balances Locks (r:1 w:1) /// Proof: Balances Locks (max_values: None, max_size: Some(1299), added: 3774, mode: MaxEncodedLen) + /// Storage: Balances Freezes (r:1 w:0) + /// Proof: Balances Freezes (max_values: None, max_size: Some(49), added: 2524, mode: MaxEncodedLen) /// Storage: System Account (r:1 w:1) /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) /// The range of component `l` is `[0, 49]`. @@ -365,20 +392,22 @@ impl WeightInfo for () { fn not_unlocking_merge_schedules(l: u32, s: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `482 + l * (25 ±0) + s * (36 ±0)` - // Estimated: `12879` - // Minimum execution time: 34_312_000 picoseconds. - Weight::from_parts(33_740_101, 12879) - // Standard Error: 996 - .saturating_add(Weight::from_parts(62_123, 0).saturating_mul(l.into())) - // Standard Error: 1_841 - .saturating_add(Weight::from_parts(56_463, 0).saturating_mul(s.into())) - .saturating_add(RocksDbWeight::get().reads(3_u64)) + // Estimated: `4764` + // Minimum execution time: 39_476_000 picoseconds. + Weight::from_parts(38_261_747, 4764) + // Standard Error: 1_794 + .saturating_add(Weight::from_parts(69_639, 0).saturating_mul(l.into())) + // Standard Error: 3_313 + .saturating_add(Weight::from_parts(73_202, 0).saturating_mul(s.into())) + .saturating_add(RocksDbWeight::get().reads(4_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) } /// Storage: Vesting Vesting (r:1 w:1) /// Proof: Vesting Vesting (max_values: None, max_size: Some(1057), added: 3532, mode: MaxEncodedLen) /// Storage: Balances Locks (r:1 w:1) /// Proof: Balances Locks (max_values: None, max_size: Some(1299), added: 3774, mode: MaxEncodedLen) + /// Storage: Balances Freezes (r:1 w:0) + /// Proof: Balances Freezes (max_values: None, max_size: Some(49), added: 2524, mode: MaxEncodedLen) /// Storage: System Account (r:1 w:1) /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) /// The range of component `l` is `[0, 49]`. @@ -386,14 +415,14 @@ impl WeightInfo for () { fn unlocking_merge_schedules(l: u32, s: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `482 + l * (25 ±0) + s * (36 ±0)` - // Estimated: `12879` - // Minimum execution time: 34_965_000 picoseconds. - Weight::from_parts(33_831_484, 12879) - // Standard Error: 1_530 - .saturating_add(Weight::from_parts(59_136, 0).saturating_mul(l.into())) - // Standard Error: 2_827 - .saturating_add(Weight::from_parts(58_493, 0).saturating_mul(s.into())) - .saturating_add(RocksDbWeight::get().reads(3_u64)) + // Estimated: `4764` + // Minimum execution time: 43_764_000 picoseconds. + Weight::from_parts(42_679_386, 4764) + // Standard Error: 1_224 + .saturating_add(Weight::from_parts(65_857, 0).saturating_mul(l.into())) + // Standard Error: 2_261 + .saturating_add(Weight::from_parts(70_861, 0).saturating_mul(s.into())) + .saturating_add(RocksDbWeight::get().reads(4_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) } } diff --git a/frame/whitelist/src/weights.rs b/frame/whitelist/src/weights.rs index 536cd1887..8636ea376 100644 --- a/frame/whitelist/src/weights.rs +++ b/frame/whitelist/src/weights.rs @@ -18,9 +18,9 @@ //! Autogenerated weights for pallet_whitelist //! //! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev -//! DATE: 2023-03-16, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! DATE: 2023-04-06, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` //! WORST CASE MAP SIZE: `1000000` -//! HOSTNAME: `bm3`, CPU: `Intel(R) Core(TM) i7-7700K CPU @ 4.20GHz` +//! HOSTNAME: `bm2`, CPU: `Intel(R) Core(TM) i7-7700K CPU @ 4.20GHz` //! EXECUTION: Some(Wasm), WASM-EXECUTION: Compiled, CHAIN: Some("dev"), DB CACHE: 1024 // Executed Command: @@ -31,9 +31,6 @@ // --steps=50 // --repeat=20 // --pallet=pallet_whitelist -// --no-storage-info -// --no-median-slopes -// --no-min-squares // --extrinsic=* // --execution=wasm // --wasm-execution=compiled @@ -67,9 +64,9 @@ impl WeightInfo for SubstrateWeight { fn whitelist_call() -> Weight { // Proof Size summary in bytes: // Measured: `217` - // Estimated: `7061` - // Minimum execution time: 21_530_000 picoseconds. - Weight::from_parts(21_986_000, 7061) + // Estimated: `3556` + // Minimum execution time: 21_370_000 picoseconds. + Weight::from_parts(21_834_000, 3556) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().writes(2_u64)) } @@ -80,9 +77,9 @@ impl WeightInfo for SubstrateWeight { fn remove_whitelisted_call() -> Weight { // Proof Size summary in bytes: // Measured: `346` - // Estimated: `7061` - // Minimum execution time: 18_980_000 picoseconds. - Weight::from_parts(19_474_000, 7061) + // Estimated: `3556` + // Minimum execution time: 19_222_000 picoseconds. + Weight::from_parts(19_582_000, 3556) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().writes(2_u64)) } @@ -96,11 +93,11 @@ impl WeightInfo for SubstrateWeight { fn dispatch_whitelisted_call(n: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `422 + n * (1 ±0)` - // Estimated: `10947 + n * (1 ±0)` - // Minimum execution time: 32_377_000 picoseconds. - Weight::from_parts(32_643_000, 10947) - // Standard Error: 0 - .saturating_add(Weight::from_parts(1_147, 0).saturating_mul(n.into())) + // Estimated: `3886 + n * (1 ±0)` + // Minimum execution time: 31_417_000 picoseconds. + Weight::from_parts(31_620_000, 3886) + // Standard Error: 1 + .saturating_add(Weight::from_parts(1_145, 0).saturating_mul(n.into())) .saturating_add(T::DbWeight::get().reads(3_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) .saturating_add(Weight::from_parts(0, 1).saturating_mul(n.into())) @@ -113,11 +110,11 @@ impl WeightInfo for SubstrateWeight { fn dispatch_whitelisted_call_with_preimage(n: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `346` - // Estimated: `7061` - // Minimum execution time: 23_421_000 picoseconds. - Weight::from_parts(24_488_523, 7061) + // Estimated: `3556` + // Minimum execution time: 23_092_000 picoseconds. + Weight::from_parts(24_043_432, 3556) // Standard Error: 5 - .saturating_add(Weight::from_parts(1_217, 0).saturating_mul(n.into())) + .saturating_add(Weight::from_parts(1_227, 0).saturating_mul(n.into())) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().writes(2_u64)) } @@ -132,9 +129,9 @@ impl WeightInfo for () { fn whitelist_call() -> Weight { // Proof Size summary in bytes: // Measured: `217` - // Estimated: `7061` - // Minimum execution time: 21_530_000 picoseconds. - Weight::from_parts(21_986_000, 7061) + // Estimated: `3556` + // Minimum execution time: 21_370_000 picoseconds. + Weight::from_parts(21_834_000, 3556) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().writes(2_u64)) } @@ -145,9 +142,9 @@ impl WeightInfo for () { fn remove_whitelisted_call() -> Weight { // Proof Size summary in bytes: // Measured: `346` - // Estimated: `7061` - // Minimum execution time: 18_980_000 picoseconds. - Weight::from_parts(19_474_000, 7061) + // Estimated: `3556` + // Minimum execution time: 19_222_000 picoseconds. + Weight::from_parts(19_582_000, 3556) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().writes(2_u64)) } @@ -161,11 +158,11 @@ impl WeightInfo for () { fn dispatch_whitelisted_call(n: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `422 + n * (1 ±0)` - // Estimated: `10947 + n * (1 ±0)` - // Minimum execution time: 32_377_000 picoseconds. - Weight::from_parts(32_643_000, 10947) - // Standard Error: 0 - .saturating_add(Weight::from_parts(1_147, 0).saturating_mul(n.into())) + // Estimated: `3886 + n * (1 ±0)` + // Minimum execution time: 31_417_000 picoseconds. + Weight::from_parts(31_620_000, 3886) + // Standard Error: 1 + .saturating_add(Weight::from_parts(1_145, 0).saturating_mul(n.into())) .saturating_add(RocksDbWeight::get().reads(3_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) .saturating_add(Weight::from_parts(0, 1).saturating_mul(n.into())) @@ -178,11 +175,11 @@ impl WeightInfo for () { fn dispatch_whitelisted_call_with_preimage(n: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `346` - // Estimated: `7061` - // Minimum execution time: 23_421_000 picoseconds. - Weight::from_parts(24_488_523, 7061) + // Estimated: `3556` + // Minimum execution time: 23_092_000 picoseconds. + Weight::from_parts(24_043_432, 3556) // Standard Error: 5 - .saturating_add(Weight::from_parts(1_217, 0).saturating_mul(n.into())) + .saturating_add(Weight::from_parts(1_227, 0).saturating_mul(n.into())) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().writes(2_u64)) } diff --git a/scripts/run_all_benchmarks.sh b/scripts/run_all_benchmarks.sh index b632cb5c1..727b49e26 100755 --- a/scripts/run_all_benchmarks.sh +++ b/scripts/run_all_benchmarks.sh @@ -59,7 +59,7 @@ done if [ "$skip_build" != true ] then echo "[+] Compiling Substrate benchmarks..." - cargo build --profile=production --locked --features=runtime-benchmarks + cargo build --profile=production --locked --features=runtime-benchmarks --bin substrate fi # The executable to use. diff --git a/utils/frame/benchmarking-cli/src/pallet/command.rs b/utils/frame/benchmarking-cli/src/pallet/command.rs index 0c3b6b3e8..08bcf3ee5 100644 --- a/utils/frame/benchmarking-cli/src/pallet/command.rs +++ b/utils/frame/benchmarking-cli/src/pallet/command.rs @@ -473,7 +473,7 @@ impl PalletCmd { log::info!( target: LOG_TARGET, - "Running Benchmark: {}.{}({} args) {}/{} {}/{}", + "Running benchmark: {}.{}({} args) {}/{} {}/{}", String::from_utf8(pallet.clone()) .expect("Encoded from String; qed"), String::from_utf8(extrinsic.clone()) diff --git a/utils/frame/benchmarking-cli/src/pallet/writer.rs b/utils/frame/benchmarking-cli/src/pallet/writer.rs index a609f9e38..89cce4562 100644 --- a/utils/frame/benchmarking-cli/src/pallet/writer.rs +++ b/utils/frame/benchmarking-cli/src/pallet/writer.rs @@ -278,6 +278,7 @@ fn get_benchmark_data( used_recorded_proof_size.push(ComponentSlope { name: name.clone(), slope, error }); } }); + used_recorded_proof_size.sort_by(|a, b| a.name.cmp(&b.name)); // We add additional comments showing which storage items were touched. // We find the worst case proof size, and use that as the final proof size result. @@ -315,12 +316,12 @@ fn get_benchmark_data( let mut base_calculated_proof_size = 0; // Sum up the proof sizes per component for (_, slope, base) in proof_size_per_components.iter() { - base_calculated_proof_size += base; + base_calculated_proof_size = base_calculated_proof_size.max(*base); for component in slope.iter() { let mut found = false; for used_component in used_calculated_proof_size.iter_mut() { if used_component.name == component.name { - used_component.slope += component.slope; + used_component.slope = used_component.slope.max(component.slope); found = true; break } @@ -337,6 +338,7 @@ fn get_benchmark_data( } } } + used_calculated_proof_size.sort_by(|a, b| a.name.cmp(&b.name)); // This puts a marker on any component which is entirely unused in the weight formula. let components = batch.time_results[0] @@ -626,7 +628,7 @@ pub(crate) fn process_storage_results( }, }; // Add the additional trie layer overhead for every new prefix. - if *reads > 0 { + if *reads > 0 && !is_all_ignored { prefix_result.proof_size += 15 * 33 * additional_trie_layers as u32; } storage_per_prefix.entry(prefix.clone()).or_default().push(prefix_result); From 7f930c23a8458f360625c9e5e0e1446edbcbb957 Mon Sep 17 00:00:00 2001 From: Sasha Gryaznov Date: Thu, 13 Apr 2023 16:57:28 +0300 Subject: [PATCH 372/558] stabilize call_runtime (#13901) --- frame/contracts/src/wasm/runtime.rs | 2 -- 1 file changed, 2 deletions(-) diff --git a/frame/contracts/src/wasm/runtime.rs b/frame/contracts/src/wasm/runtime.rs index 19eb0fb50..bfb991ae5 100644 --- a/frame/contracts/src/wasm/runtime.rs +++ b/frame/contracts/src/wasm/runtime.rs @@ -2404,8 +2404,6 @@ pub mod env { /// - Provide functionality **exclusively** to contracts. /// - Provide custom weights. /// - Avoid the need to keep the `Call` data structure stable. - #[unstable] - #[prefixed_alias] fn call_runtime( ctx: _, memory: _, From 186db7a7785ab74e15457d57072660344f237c2b Mon Sep 17 00:00:00 2001 From: Mira Ressel Date: Thu, 13 Apr 2023 19:14:44 +0200 Subject: [PATCH 373/558] fix .test-refs-check-benches condition (#13906) This check is meant to catch pipelines triggered by the scripts ci-linux staging tests. The correct CI_PIPELINE_SOURCE for multi-project pipelines such as this is "pipeline"; "parent_pipeline" is only set for child pipelines triggered /within the same repo/. cf https://docs.gitlab.com/ee/ci/jobs/job_control.html#common-if-clauses-for-rules --- .gitlab-ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 470ce2ce3..30a584635 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -179,7 +179,7 @@ variables: # exclude cargo-check-benches from such runs .test-refs-check-benches: rules: - - if: $CI_COMMIT_REF_NAME == "master" && $CI_PIPELINE_SOURCE == "parent_pipeline" && $CI_IMAGE =~ /staging$/ + - if: $CI_COMMIT_REF_NAME == "master" && $CI_PIPELINE_SOURCE == "pipeline" && $CI_IMAGE =~ /staging$/ when: never - if: $CI_PIPELINE_SOURCE == "web" - if: $CI_PIPELINE_SOURCE == "schedule" From b5b57513f64193bbe43b5bee69c950e78af9a5f6 Mon Sep 17 00:00:00 2001 From: Mira Ressel Date: Thu, 13 Apr 2023 19:14:55 +0200 Subject: [PATCH 374/558] Disable timestamping for the crate publishing jobs (#13910) --- .gitlab-ci.yml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 30a584635..a8687216f 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -259,6 +259,9 @@ variables: - artifacts/ variables: SPUB_TMP: artifacts + # disable timestamping for the crate publishing jobs, they leave stray child processes behind + # which don't interact well with the timestamping script + CI_DISABLE_TIMESTAMP: 1 #### stage: .pre From a49292aa6c1853ea70d22dbfda31e42159d1b43b Mon Sep 17 00:00:00 2001 From: Mira Ressel Date: Thu, 13 Apr 2023 20:17:12 +0200 Subject: [PATCH 375/558] Refactor the automatic-crate-publishing logic, make triggered pipelines uninterruptible as well (#13908) * Use a new approach to make automatic-crate-publishing uninterruptible It's not neccessary to dynamically change the `interruptible` setting for *all* jobs as the old approach does; gitlab already considers a pipeline uninterruptible as soon as a single uninterruptible job has started (cf https://docs.gitlab.com/ee/ci/yaml/#interruptible). IMO this approach is more readable, as it avoids dynamically loading the .defaults section from different files based on import conditions; the logic is now shorter and entirely contained in the main .gitlab-ci.yml. * Make triggered multi-project pipelines uninterruptible --- .gitlab-ci.yml | 42 +++++++++---------- .../ci/gitlab/crate-publishing-pipeline.yml | 1 - scripts/ci/gitlab/default-pipeline.yml | 1 - 3 files changed, 19 insertions(+), 25 deletions(-) delete mode 100644 scripts/ci/gitlab/crate-publishing-pipeline.yml delete mode 100644 scripts/ci/gitlab/default-pipeline.yml diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index a8687216f..e7b009318 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -58,7 +58,7 @@ variables: NEXTEST_SUCCESS_OUTPUT: final ZOMBIENET_IMAGE: "docker.io/paritytech/zombienet:v1.3.43" -.shared-default: &shared-default +default: retry: max: 2 when: @@ -66,21 +66,7 @@ variables: - unknown_failure - api_failure cache: {} - -.default-pipeline-definitions: - default: - <<: *shared-default - interruptible: true - -.crate-publishing-pipeline-definitions: - default: - <<: *shared-default - # The crate-publishing pipeline defaults to `interruptible: false` so that we'll be able to - # reach and run the publishing jobs despite the "Auto-cancel redundant pipelines" CI setting. - # The setting is relevant because the crate-publishing pipeline runs on `master`, thus future - # pipelines on `master` (e.g. created for new commits or other schedules) might unintendedly - # cancel the publishing jobs or its dependencies before we get to actually publish the crates. - interruptible: false + interruptible: true .collect-artifacts: artifacts: @@ -291,6 +277,23 @@ check-crates-publishing-pipeline: https://github.com/paritytech/releng-scripts.git - ONLY_CHECK_PIPELINE=true ./releng-scripts/publish-crates +# By default our pipelines are interruptible, but some special pipelines shouldn't be interrupted: +# * multi-project pipelines such as the ones triggered by the scripts repo +# * the scheduled automatic-crate-publishing pipeline +# +# In those cases, we add an uninterruptible .pre job; once that one has started, +# the entire pipeline becomes uninterruptible +uninterruptible-pipeline: + extends: .kubernetes-env + variables: + CI_IMAGE: "paritytech/tools:latest" + stage: .pre + interruptible: false + rules: + - if: $CI_PIPELINE_SOURCE == "pipeline" + - if: $CI_PIPELINE_SOURCE == "schedule" && $PIPELINE == "automatic-crate-publishing" + script: "true" + include: # check jobs - scripts/ci/gitlab/pipeline/check.yml @@ -311,13 +314,6 @@ include: # pipeline is made uninterruptible to ensure that test jobs also get a chance to run to # completion, because the publishing jobs depends on them AS INTENDED: crates should not be # published before their source code is checked. - - local: scripts/ci/gitlab/crate-publishing-pipeline.yml - rules: - - if: $PIPELINE == "automatic-crate-publishing" - # For normal pipelines: run it with defaults + `interruptible: true` - - local: scripts/ci/gitlab/default-pipeline.yml - rules: - - if: $PIPELINE != "automatic-crate-publishing" - project: parity/infrastructure/ci_cd/shared ref: v0.2 file: /common/timestamp.yml diff --git a/scripts/ci/gitlab/crate-publishing-pipeline.yml b/scripts/ci/gitlab/crate-publishing-pipeline.yml deleted file mode 100644 index 9d5303952..000000000 --- a/scripts/ci/gitlab/crate-publishing-pipeline.yml +++ /dev/null @@ -1 +0,0 @@ -default: !reference [.crate-publishing-pipeline-definitions, default] diff --git a/scripts/ci/gitlab/default-pipeline.yml b/scripts/ci/gitlab/default-pipeline.yml deleted file mode 100644 index 19f6c320c..000000000 --- a/scripts/ci/gitlab/default-pipeline.yml +++ /dev/null @@ -1 +0,0 @@ -default: !reference [.default-pipeline-definitions, default] From e155ab8aefbe5831cbd6100767bd111bbad32ee7 Mon Sep 17 00:00:00 2001 From: Oliver Tale-Yazdi Date: Thu, 13 Apr 2023 22:44:46 +0200 Subject: [PATCH 376/558] Update proc-macro-warning (#13876) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Update proc-macro-warning Closes https://github.com/paritytech/substrate/issues/13872 Signed-off-by: Oliver Tale-Yazdi * Update to v0.3.1 (including syn 2.0) Signed-off-by: Oliver Tale-Yazdi * Revert "Update to v0.3.1 (including syn 2.0)" CI is red since it Polkadot uses a different syn version… Going to update it lean. This reverts commit 3240d379b72f6f722eaf502ed49e9a1b0f79db4b. Signed-off-by: Oliver Tale-Yazdi --------- Signed-off-by: Oliver Tale-Yazdi --- Cargo.lock | 4 ++-- frame/support/procedural/Cargo.toml | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 45542b8c2..d5eb70361 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -7552,9 +7552,9 @@ dependencies = [ [[package]] name = "proc-macro-warning" -version = "0.2.0" +version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9d4f284d87b9cedc2ff57223cbc4e3937cd6063c01e92c8e2a8c080df0013933" +checksum = "f0e25495609acefcaeb5052edad8ac91017c9bc98fc38ef321ed524e50b68bac" dependencies = [ "proc-macro2", "quote", diff --git a/frame/support/procedural/Cargo.toml b/frame/support/procedural/Cargo.toml index d57b81a34..e508730f4 100644 --- a/frame/support/procedural/Cargo.toml +++ b/frame/support/procedural/Cargo.toml @@ -23,7 +23,7 @@ proc-macro2 = "1.0.56" quote = "1.0.26" syn = { version = "2.0.14", features = ["full"] } frame-support-procedural-tools = { version = "4.0.0-dev", path = "./tools" } -proc-macro-warning = { version = "0.2.0", default-features = false } +proc-macro-warning = { version = "0.3.0", default-features = false } [features] default = ["std"] From 4b212829fd62c180c23a90718a73c221f5b4f4ae Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 14 Apr 2023 09:32:24 +0200 Subject: [PATCH 377/558] Bump h2 from 0.3.16 to 0.3.17 (#13915) Bumps [h2](https://github.com/hyperium/h2) from 0.3.16 to 0.3.17. - [Release notes](https://github.com/hyperium/h2/releases) - [Changelog](https://github.com/hyperium/h2/blob/master/CHANGELOG.md) - [Commits](https://github.com/hyperium/h2/compare/v0.3.16...v0.3.17) --- updated-dependencies: - dependency-name: h2 dependency-type: indirect ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- Cargo.lock | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index d5eb70361..94fd66e0f 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2941,9 +2941,9 @@ dependencies = [ [[package]] name = "h2" -version = "0.3.16" +version = "0.3.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5be7b54589b581f624f566bf5d8eb2bab1db736c51528720b6bd36b96b55924d" +checksum = "66b91535aa35fea1523ad1b86cb6b53c28e0ae566ba4a460f4457e936cad7c6f" dependencies = [ "bytes", "fnv", From 6a560f85d24bb027a1909109b1f84a35b3348e8e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bastian=20K=C3=B6cher?= Date: Fri, 14 Apr 2023 10:21:06 +0200 Subject: [PATCH 378/558] Unqueue invalid transactions when skipping (#13918) The check was intially added by: https://github.com/paritytech/substrate/pull/5121 But a later pr fixed it properly: https://github.com/paritytech/substrate/pull/9789 TLDR: We don't need to check for skipped anymore, as we already remove all transactions that are being unlocked by an invalid transactions and thus, we will not tag them as invalid directly because the nonce for example is incorrect. Fixes: https://github.com/paritytech/substrate/issues/13911 --- client/basic-authorship/src/basic_authorship.rs | 13 ++++--------- 1 file changed, 4 insertions(+), 9 deletions(-) diff --git a/client/basic-authorship/src/basic_authorship.rs b/client/basic-authorship/src/basic_authorship.rs index 376278fa1..795288d0a 100644 --- a/client/basic-authorship/src/basic_authorship.rs +++ b/client/basic-authorship/src/basic_authorship.rs @@ -474,14 +474,6 @@ where break EndProposingReason::HitBlockWeightLimit } }, - Err(e) if skipped > 0 => { - pending_iterator.report_invalid(&pending_tx); - trace!( - "[{:?}] Ignoring invalid transaction when skipping: {}", - pending_tx_hash, - e - ); - }, Err(e) => { pending_iterator.report_invalid(&pending_tx); debug!("[{:?}] Invalid transaction: {}", pending_tx_hash, e); @@ -740,8 +732,11 @@ mod tests { ); } + // This test ensures that if one transaction of a user was rejected, because for example + // the weight limit was hit, we don't mark the other transactions of the user as invalid because + // the nonce is not matching. #[test] - fn should_not_remove_invalid_transactions_when_skipping() { + fn should_not_remove_invalid_transactions_from_the_same_sender_after_one_was_invalid() { // given let mut client = Arc::new(substrate_test_runtime_client::new()); let spawner = sp_core::testing::TaskExecutor::new(); From b23e52d340f1d8c6405c631e5e836f45966df326 Mon Sep 17 00:00:00 2001 From: Mira Ressel Date: Fri, 14 Apr 2023 14:16:17 +0200 Subject: [PATCH 379/558] Fail dependant checks on cargo check warnings (#13922) * Fail dependant checks on cargo check warnings * Adjust feature flags for check-dependent-polkadot --- scripts/ci/gitlab/pipeline/build.yml | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/scripts/ci/gitlab/pipeline/build.yml b/scripts/ci/gitlab/pipeline/build.yml index 806a4ef97..0c04abfd5 100644 --- a/scripts/ci/gitlab/pipeline/build.yml +++ b/scripts/ci/gitlab/pipeline/build.yml @@ -12,6 +12,8 @@ extends: - .docker-env - .test-refs-no-trigger-prs-only + variables: + RUSTFLAGS: "-D warnings" script: - git clone --depth=1 @@ -35,6 +37,10 @@ check-dependent-polkadot: COMPANION_OVERRIDES: | substrate: polkadot-v* polkadot: release-v* + # enable the same feature flags as polkadot's test-linux-stable + COMPANION_CHECK_COMMAND: > + cargo check --all-targets --workspace + --features=runtime-benchmarks,runtime-metrics,try-runtime rules: - if: $CI_COMMIT_REF_NAME =~ /^[0-9]+$/ #PRs From c2f664e1acff27dbc165d02ab1785829b23ac3f9 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 14 Apr 2023 14:52:51 +0200 Subject: [PATCH 380/558] Bump cargo_metadata from 0.15.3 to 0.15.4 (#13899) Bumps [cargo_metadata](https://github.com/oli-obk/cargo_metadata) from 0.15.3 to 0.15.4. - [Release notes](https://github.com/oli-obk/cargo_metadata/releases) - [Changelog](https://github.com/oli-obk/cargo_metadata/blob/main/CHANGELOG.md) - [Commits](https://github.com/oli-obk/cargo_metadata/compare/0.15.3...0.15.4) --- updated-dependencies: - dependency-name: cargo_metadata dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- Cargo.lock | 4 ++-- client/executor/wasmtime/Cargo.toml | 2 +- utils/wasm-builder/Cargo.toml | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 94fd66e0f..bb8081221 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -797,9 +797,9 @@ dependencies = [ [[package]] name = "cargo_metadata" -version = "0.15.3" +version = "0.15.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "08a1ec454bc3eead8719cb56e15dbbfecdbc14e4b3a3ae4936cc6e31f5fc0d07" +checksum = "eee4243f1f26fc7a42710e7439c149e2b10b05472f88090acce52632f231a73a" dependencies = [ "camino", "cargo-platform", diff --git a/client/executor/wasmtime/Cargo.toml b/client/executor/wasmtime/Cargo.toml index bffccd12b..fed284e91 100644 --- a/client/executor/wasmtime/Cargo.toml +++ b/client/executor/wasmtime/Cargo.toml @@ -48,4 +48,4 @@ sp-io = { version = "7.0.0", path = "../../../primitives/io" } tempfile = "3.3.0" paste = "1.0" codec = { package = "parity-scale-codec", version = "3.2.2" } -cargo_metadata = "0.15.2" +cargo_metadata = "0.15.4" diff --git a/utils/wasm-builder/Cargo.toml b/utils/wasm-builder/Cargo.toml index e1444a240..7f356294e 100644 --- a/utils/wasm-builder/Cargo.toml +++ b/utils/wasm-builder/Cargo.toml @@ -15,7 +15,7 @@ targets = ["x86_64-unknown-linux-gnu"] [dependencies] ansi_term = "0.12.1" build-helper = "0.1.1" -cargo_metadata = "0.15.2" +cargo_metadata = "0.15.4" strum = { version = "0.24.1", features = ["derive"] } tempfile = "3.1.0" toml = "0.7.3" From a9f67d0e59fc388c97efe8b1e6b2f928fae85b19 Mon Sep 17 00:00:00 2001 From: NingLin-P Date: Fri, 14 Apr 2023 22:15:31 +0800 Subject: [PATCH 381/558] Drain all the pending messages in the channel when TracingUnboundedReceiver is dropped (#13917) Signed-off-by: linning --- client/utils/src/mpsc.rs | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/client/utils/src/mpsc.rs b/client/utils/src/mpsc.rs index 3f783b100..7e06bd203 100644 --- a/client/utils/src/mpsc.rs +++ b/client/utils/src/mpsc.rs @@ -141,6 +141,7 @@ impl TracingUnboundedReceiver { impl Drop for TracingUnboundedReceiver { fn drop(&mut self) { + // Close the channel to prevent any further messages to be sent into the channel self.close(); // the number of messages about to be dropped let count = self.inner.len(); @@ -150,6 +151,10 @@ impl Drop for TracingUnboundedReceiver { .with_label_values(&[self.name, "dropped"]) .inc_by(count.saturated_into()); } + // Drain all the pending messages in the channel since they can never be accessed, + // this can be removed once https://github.com/smol-rs/async-channel/issues/23 is + // resolved + while let Ok(_) = self.inner.try_recv() {} } } @@ -177,3 +182,22 @@ impl FusedStream for TracingUnboundedReceiver { self.inner.is_terminated() } } + +#[cfg(test)] +mod tests { + use super::tracing_unbounded; + use async_channel::{self, RecvError, TryRecvError}; + + #[test] + fn test_tracing_unbounded_receiver_drop() { + let (tracing_unbounded_sender, tracing_unbounded_receiver) = + tracing_unbounded("test-receiver-drop", 10); + let (tx, rx) = async_channel::unbounded::(); + + tracing_unbounded_sender.unbounded_send(tx).unwrap(); + drop(tracing_unbounded_receiver); + + assert_eq!(rx.try_recv(), Err(TryRecvError::Closed)); + assert_eq!(rx.recv_blocking(), Err(RecvError)); + } +} From 3f8dc9ed594fdba455d0bf6780768e7176188514 Mon Sep 17 00:00:00 2001 From: gupnik Date: Fri, 14 Apr 2023 22:54:26 +0530 Subject: [PATCH 382/558] Updates Benchmark macro parsing to use Generic Argument (#13919) * WIP Signed-off-by: Oliver Tale-Yazdi * Removes POC code * Adds assertion and UT * adds runtime error * removes const_assert * ".git/.scripts/commands/fmt/fmt.sh" --------- Signed-off-by: Oliver Tale-Yazdi Co-authored-by: Oliver Tale-Yazdi Co-authored-by: command-bot <> --- frame/support/procedural/src/benchmark.rs | 32 ++++++++----------- .../tests/benchmark_ui/bad_param_range.rs | 16 ---------- .../tests/benchmark_ui/bad_param_range.stderr | 5 --- .../benchmark_ui/pass/valid_const_expr.rs | 28 ++++++++++++++++ 4 files changed, 41 insertions(+), 40 deletions(-) delete mode 100644 frame/support/test/tests/benchmark_ui/bad_param_range.rs delete mode 100644 frame/support/test/tests/benchmark_ui/bad_param_range.stderr create mode 100644 frame/support/test/tests/benchmark_ui/pass/valid_const_expr.rs diff --git a/frame/support/procedural/src/benchmark.rs b/frame/support/procedural/src/benchmark.rs index cf091e7cb..315a6000b 100644 --- a/frame/support/procedural/src/benchmark.rs +++ b/frame/support/procedural/src/benchmark.rs @@ -28,9 +28,9 @@ use syn::{ punctuated::Punctuated, spanned::Spanned, token::{Comma, Gt, Lt, PathSep}, - Attribute, Error, Expr, ExprBlock, ExprCall, ExprPath, FnArg, Item, ItemFn, ItemMod, LitInt, - Pat, Path, PathArguments, PathSegment, Result, ReturnType, Signature, Stmt, Token, Type, - TypePath, Visibility, WhereClause, + Attribute, Error, Expr, ExprBlock, ExprCall, ExprPath, FnArg, Item, ItemFn, ItemMod, Pat, Path, + PathArguments, PathSegment, Result, ReturnType, Signature, Stmt, Token, Type, TypePath, + Visibility, WhereClause, }; mod keywords { @@ -54,17 +54,17 @@ mod keywords { struct ParamDef { name: String, typ: Type, - start: u32, - end: u32, + start: syn::GenericArgument, + end: syn::GenericArgument, } /// Allows easy parsing of the `<10, 20>` component of `x: Linear<10, 20>`. #[derive(Parse)] struct RangeArgs { _lt_token: Lt, - start: LitInt, + start: syn::GenericArgument, _comma: Comma, - end: LitInt, + end: syn::GenericArgument, _gt_token: Gt, } @@ -228,17 +228,8 @@ fn parse_params(item_fn: &ItemFn) -> Result> { let Some(segment) = tpath.path.segments.last() else { return invalid_param(typ.span()) }; let args = segment.arguments.to_token_stream().into(); let Ok(args) = syn::parse::(args) else { return invalid_param(typ.span()) }; - let Ok(start) = args.start.base10_parse::() else { return invalid_param(args.start.span()) }; - let Ok(end) = args.end.base10_parse::() else { return invalid_param(args.end.span()) }; - if end < start { - return Err(Error::new( - args.start.span(), - "The start of a `ParamRange` must be less than or equal to the end", - )) - } - - params.push(ParamDef { name, typ: typ.clone(), start, end }); + params.push(ParamDef { name, typ: typ.clone(), start: args.start, end: args.end }); } Ok(params) } @@ -700,8 +691,8 @@ impl UnrolledParams { .iter() .map(|p| { let name = Ident::new(&p.name, Span::call_site()); - let start = p.start; - let end = p.end; + let start = &p.start; + let end = &p.end; quote!(#name, #start, #end) }) .collect(); @@ -987,6 +978,9 @@ fn expand_benchmark( // Test the lowest, highest (if its different from the lowest) // and up to num_values-2 more equidistant values in between. // For 0..10 and num_values=6 this would mean: [0, 2, 4, 6, 8, 10] + if high < low { + return Err("The start of a `ParamRange` must be less than or equal to the end".into()); + } let mut values = #krate::vec![low]; let diff = (high - low).min(num_values - 1); diff --git a/frame/support/test/tests/benchmark_ui/bad_param_range.rs b/frame/support/test/tests/benchmark_ui/bad_param_range.rs deleted file mode 100644 index 993f2a000..000000000 --- a/frame/support/test/tests/benchmark_ui/bad_param_range.rs +++ /dev/null @@ -1,16 +0,0 @@ -use frame_benchmarking::v2::*; -#[allow(unused_imports)] -use frame_support_test::Config; - -#[benchmarks] -mod benches { - use super::*; - - #[benchmark] - fn bench(x: Linear<3, 1>) { - #[block] - {} - } -} - -fn main() {} diff --git a/frame/support/test/tests/benchmark_ui/bad_param_range.stderr b/frame/support/test/tests/benchmark_ui/bad_param_range.stderr deleted file mode 100644 index 1347af0a0..000000000 --- a/frame/support/test/tests/benchmark_ui/bad_param_range.stderr +++ /dev/null @@ -1,5 +0,0 @@ -error: The start of a `ParamRange` must be less than or equal to the end - --> tests/benchmark_ui/bad_param_range.rs:10:21 - | -10 | fn bench(x: Linear<3, 1>) { - | ^ diff --git a/frame/support/test/tests/benchmark_ui/pass/valid_const_expr.rs b/frame/support/test/tests/benchmark_ui/pass/valid_const_expr.rs new file mode 100644 index 000000000..bead3bf27 --- /dev/null +++ b/frame/support/test/tests/benchmark_ui/pass/valid_const_expr.rs @@ -0,0 +1,28 @@ +use frame_benchmarking::v2::*; +use frame_support_test::Config; +use frame_support::parameter_types; + +#[benchmarks] +mod benches { + use super::*; + + const MY_CONST: u32 = 100; + + const fn my_fn() -> u32 { + 200 + } + + parameter_types! { + const MyConst: u32 = MY_CONST; + } + + #[benchmark(skip_meta, extra)] + fn bench(a: Linear<{MY_CONST * 2}, {my_fn() + MyConst::get()}>) { + let a = 2 + 2; + #[block] + {} + assert_eq!(a, 4); + } +} + +fn main() {} From f1e2fa43ef10f0abc4fc0620ff08979ac6a6e097 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bastian=20K=C3=B6cher?= Date: Sat, 15 Apr 2023 23:27:51 +0200 Subject: [PATCH 383/558] sc-allocator: Do not panic on invalid header pointer (#13925) We should not panic on an invalid header pointer and instead return an error. It is possible that the application modifies the header pointer illegally, but then we should return an error instead of panicking. --- client/allocator/src/freeing_bump.rs | 31 +++++++++++++++++++++++----- 1 file changed, 26 insertions(+), 5 deletions(-) diff --git a/client/allocator/src/freeing_bump.rs b/client/allocator/src/freeing_bump.rs index c3cb827af..144c07645 100644 --- a/client/allocator/src/freeing_bump.rs +++ b/client/allocator/src/freeing_bump.rs @@ -421,11 +421,11 @@ impl FreeingBumpHeapAllocator { let header_ptr: u32 = match self.free_lists[order] { Link::Ptr(header_ptr) => { - assert!( - u64::from(header_ptr + order.size() + HEADER_SIZE) <= mem.size(), - "Pointer is looked up in list of free entries, into which - only valid values are inserted; qed" - ); + if (u64::from(header_ptr) + u64::from(order.size()) + u64::from(HEADER_SIZE)) > + mem.size() + { + return Err(error("Invalid header pointer detected")) + } // Remove this header from the free list. let next_free = Header::read_from(mem, header_ptr)? @@ -1106,4 +1106,25 @@ mod tests { assert_eq!(3, mem.pages()); } + + #[test] + fn modifying_the_header_leads_to_an_error() { + let mut mem = MemoryInstance::with_pages(1); + let mut heap = FreeingBumpHeapAllocator::new(0); + + let ptr = heap.allocate(&mut mem, 5).unwrap(); + + heap.deallocate(&mut mem, ptr).unwrap(); + + Header::Free(Link::Ptr(u32::MAX - 1)) + .write_into(&mut mem, u32::from(ptr) - HEADER_SIZE) + .unwrap(); + + heap.allocate(&mut mem, 5).unwrap(); + assert!(heap + .allocate(&mut mem, 5) + .unwrap_err() + .to_string() + .contains("Invalid header pointer")); + } } From 35049b176e49ca23240d1ee3ec6c190804e3b4d1 Mon Sep 17 00:00:00 2001 From: Roman Useinov Date: Sun, 16 Apr 2023 00:25:56 +0200 Subject: [PATCH 384/558] [Fix] Remove redundant stash from Stake (#13907) * [Fix] Remove redundant stash from Stake * fix tests --- frame/nomination-pools/src/mock.rs | 6 +++--- frame/staking/src/pallet/impls.rs | 2 +- primitives/staking/src/lib.rs | 2 -- 3 files changed, 4 insertions(+), 6 deletions(-) diff --git a/frame/nomination-pools/src/mock.rs b/frame/nomination-pools/src/mock.rs index 6d83ef61d..3565ff14e 100644 --- a/frame/nomination-pools/src/mock.rs +++ b/frame/nomination-pools/src/mock.rs @@ -122,9 +122,9 @@ impl sp_staking::StakingInterface for StakingMock { BondedBalanceMap::get().get(who).copied(), ) { (None, None) => Err(DispatchError::Other("balance not found")), - (Some(v), None) => Ok(Stake { total: v, active: 0, stash: *who }), - (None, Some(v)) => Ok(Stake { total: v, active: v, stash: *who }), - (Some(a), Some(b)) => Ok(Stake { total: a + b, active: b, stash: *who }), + (Some(v), None) => Ok(Stake { total: v, active: 0 }), + (None, Some(v)) => Ok(Stake { total: v, active: v }), + (Some(a), Some(b)) => Ok(Stake { total: a + b, active: b }), } } diff --git a/frame/staking/src/pallet/impls.rs b/frame/staking/src/pallet/impls.rs index d5072476f..5143538c2 100644 --- a/frame/staking/src/pallet/impls.rs +++ b/frame/staking/src/pallet/impls.rs @@ -1611,7 +1611,7 @@ impl StakingInterface for Pallet { fn stake(who: &Self::AccountId) -> Result, DispatchError> { Self::bonded(who) .and_then(|c| Self::ledger(c)) - .map(|l| Stake { stash: l.stash, total: l.total, active: l.active }) + .map(|l| Stake { total: l.total, active: l.active }) .ok_or(Error::::NotStash.into()) } diff --git a/primitives/staking/src/lib.rs b/primitives/staking/src/lib.rs index a8d8e6a60..f32869655 100644 --- a/primitives/staking/src/lib.rs +++ b/primitives/staking/src/lib.rs @@ -58,8 +58,6 @@ impl OnStakerSlash for () { /// A struct that reflects stake that an account has in the staking system. Provides a set of /// methods to operate on it's properties. Aimed at making `StakingInterface` more concise. pub struct Stake { - /// The stash account whose balance is actually locked and at stake. - pub stash: T::AccountId, /// The total stake that `stash` has in the staking system. This includes the /// `active` stake, and any funds currently in the process of unbonding via /// [`StakingInterface::unbond`]. From 4e93782c16f741cde18a8b98bd791838e03788fb Mon Sep 17 00:00:00 2001 From: Sergej Sakac <73715684+Szegoo@users.noreply.github.com> Date: Mon, 17 Apr 2023 07:43:13 +0200 Subject: [PATCH 385/558] Try-state for Collective pallet (#13645) * write the try_state_ function * update tests * update check * fix benchmarking * fix nonsense * Update frame/collective/src/lib.rs Co-authored-by: Oliver Tale-Yazdi * Update frame/collective/src/lib.rs Co-authored-by: Oliver Tale-Yazdi * unique proposal index * prime must be a member of the collective * oops * Add new checks * use ensure * fix --------- Co-authored-by: Oliver Tale-Yazdi --- frame/collective/src/benchmarking.rs | 2 +- frame/collective/src/lib.rs | 118 ++++++++++++++++++++++++++- frame/collective/src/tests.rs | 99 ++++++++++++---------- 3 files changed, 175 insertions(+), 44 deletions(-) diff --git a/frame/collective/src/benchmarking.rs b/frame/collective/src/benchmarking.rs index d4b5af1bc..bcd203c38 100644 --- a/frame/collective/src/benchmarking.rs +++ b/frame/collective/src/benchmarking.rs @@ -646,5 +646,5 @@ benchmarks_instance_pallet! { assert_last_event::(Event::Disapproved { proposal_hash: last_hash }.into()); } - impl_benchmark_test_suite!(Collective, crate::tests::new_test_ext(), crate::tests::Test); + impl_benchmark_test_suite!(Collective, crate::tests::ExtBuilder::default().build(), crate::tests::Test); } diff --git a/frame/collective/src/lib.rs b/frame/collective/src/lib.rs index bde08f08c..0cbf7e972 100644 --- a/frame/collective/src/lib.rs +++ b/frame/collective/src/lib.rs @@ -50,8 +50,8 @@ use sp_std::{marker::PhantomData, prelude::*, result}; use frame_support::{ codec::{Decode, Encode, MaxEncodedLen}, dispatch::{ - DispatchError, DispatchResultWithPostInfo, Dispatchable, GetDispatchInfo, Pays, - PostDispatchInfo, + DispatchError, DispatchResult, DispatchResultWithPostInfo, Dispatchable, GetDispatchInfo, + Pays, PostDispatchInfo, }, ensure, traits::{ @@ -341,6 +341,15 @@ pub mod pallet { WrongProposalLength, } + #[pallet::hooks] + impl, I: 'static> Hooks> for Pallet { + #[cfg(feature = "try-runtime")] + fn try_state(_n: BlockNumberFor) -> Result<(), &'static str> { + Self::do_try_state()?; + Ok(()) + } + } + // Note that councillor operations are assigned to the operational class. #[pallet::call] impl, I: 'static> Pallet { @@ -916,6 +925,111 @@ impl, I: 'static> Pallet { }); num_proposals as u32 } + + /// Ensure the correctness of the state of this pallet. + /// + /// The following expectation must always apply. + /// + /// ## Expectations: + /// + /// Looking at proposals: + /// + /// * Each hash of a proposal that is stored inside `Proposals` must have a + /// call mapped to it inside the `ProposalOf` storage map. + /// * `ProposalCount` must always be more or equal to the number of + /// proposals inside the `Proposals` storage value. The reason why + /// `ProposalCount` can be more is because when a proposal is removed the + /// count is not deducted. + /// * Count of `ProposalOf` should match the count of `Proposals` + /// + /// Looking at votes: + /// * The sum of aye and nay votes for a proposal can never exceed + /// `MaxMembers`. + /// * The proposal index inside the `Voting` storage map must be unique. + /// * All proposal hashes inside `Voting` must exist in `Proposals`. + /// + /// Looking at members: + /// * The members count must never exceed `MaxMembers`. + /// * All the members must be sorted by value. + /// + /// Looking at prime account: + /// * The prime account must be a member of the collective. + #[cfg(any(feature = "try-runtime", test))] + fn do_try_state() -> DispatchResult { + Self::proposals().into_iter().try_for_each(|proposal| -> DispatchResult { + ensure!( + Self::proposal_of(proposal).is_some(), + DispatchError::Other( + "Proposal hash from `Proposals` is not found inside the `ProposalOf` mapping." + ) + ); + Ok(()) + })?; + + ensure!( + Self::proposals().into_iter().count() <= Self::proposal_count() as usize, + DispatchError::Other("The actual number of proposals is greater than `ProposalCount`") + ); + ensure!( + Self::proposals().into_iter().count() == >::iter_keys().count(), + DispatchError::Other("Proposal count inside `Proposals` is not equal to the proposal count in `ProposalOf`") + ); + + Self::proposals().into_iter().try_for_each(|proposal| -> DispatchResult { + if let Some(votes) = Self::voting(proposal) { + let ayes = votes.ayes.len(); + let nays = votes.nays.len(); + + ensure!( + ayes.saturating_add(nays) <= T::MaxMembers::get() as usize, + DispatchError::Other("The sum of ayes and nays is greater than `MaxMembers`") + ); + } + Ok(()) + })?; + + let mut proposal_indices = vec![]; + Self::proposals().into_iter().try_for_each(|proposal| -> DispatchResult { + if let Some(votes) = Self::voting(proposal) { + let proposal_index = votes.index; + ensure!( + !proposal_indices.contains(&proposal_index), + DispatchError::Other("The proposal index is not unique.") + ); + proposal_indices.push(proposal_index); + } + Ok(()) + })?; + + >::iter_keys().try_for_each(|proposal_hash| -> DispatchResult { + ensure!( + Self::proposals().contains(&proposal_hash), + DispatchError::Other( + "`Proposals` doesn't contain the proposal hash from the `Voting` storage map." + ) + ); + Ok(()) + })?; + + ensure!( + Self::members().len() <= T::MaxMembers::get() as usize, + DispatchError::Other("The member count is greater than `MaxMembers`.") + ); + + ensure!( + Self::members().windows(2).all(|members| members[0] <= members[1]), + DispatchError::Other("The members are not sorted by value.") + ); + + if let Some(prime) = Self::prime() { + ensure!( + Self::members().contains(&prime), + DispatchError::Other("Prime account is not a member.") + ); + } + + Ok(()) + } } impl, I: 'static> ChangeMembers for Pallet { diff --git a/frame/collective/src/tests.rs b/frame/collective/src/tests.rs index 2550ab3ed..79dc3becf 100644 --- a/frame/collective/src/tests.rs +++ b/frame/collective/src/tests.rs @@ -155,23 +155,40 @@ impl Config for Test { type SetMembersOrigin = EnsureRoot; } -pub fn new_test_ext() -> sp_io::TestExternalities { - let mut ext: sp_io::TestExternalities = GenesisConfig { - collective: pallet_collective::GenesisConfig { - members: vec![1, 2, 3], - phantom: Default::default(), - }, - collective_majority: pallet_collective::GenesisConfig { - members: vec![1, 2, 3, 4, 5], - phantom: Default::default(), - }, - default_collective: Default::default(), +pub struct ExtBuilder {} + +impl Default for ExtBuilder { + fn default() -> Self { + Self {} + } +} + +impl ExtBuilder { + pub fn build(self) -> sp_io::TestExternalities { + let mut ext: sp_io::TestExternalities = GenesisConfig { + collective: pallet_collective::GenesisConfig { + members: vec![1, 2, 3], + phantom: Default::default(), + }, + collective_majority: pallet_collective::GenesisConfig { + members: vec![1, 2, 3, 4, 5], + phantom: Default::default(), + }, + default_collective: Default::default(), + } + .build_storage() + .unwrap() + .into(); + ext.execute_with(|| System::set_block_number(1)); + ext + } + + pub fn build_and_execute(self, test: impl FnOnce() -> ()) { + self.build().execute_with(|| { + test(); + Collective::do_try_state().unwrap(); + }) } - .build_storage() - .unwrap() - .into(); - ext.execute_with(|| System::set_block_number(1)); - ext } fn make_proposal(value: u64) -> RuntimeCall { @@ -186,7 +203,7 @@ fn record(event: RuntimeEvent) -> EventRecord { #[test] fn motions_basic_environment_works() { - new_test_ext().execute_with(|| { + ExtBuilder::default().build_and_execute(|| { assert_eq!(Collective::members(), vec![1, 2, 3]); assert_eq!(*Collective::proposals(), Vec::::new()); }); @@ -194,7 +211,7 @@ fn motions_basic_environment_works() { #[test] fn close_works() { - new_test_ext().execute_with(|| { + ExtBuilder::default().build_and_execute(|| { let proposal = make_proposal(42); let proposal_len: u32 = proposal.using_encoded(|p| p.len() as u32); let proposal_weight = proposal.get_dispatch_info().weight; @@ -262,7 +279,7 @@ fn close_works() { #[test] fn proposal_weight_limit_works_on_approve() { - new_test_ext().execute_with(|| { + ExtBuilder::default().build_and_execute(|| { let proposal = RuntimeCall::Collective(crate::Call::set_members { new_members: vec![1, 2, 3], prime: None, @@ -304,7 +321,7 @@ fn proposal_weight_limit_works_on_approve() { #[test] fn proposal_weight_limit_ignored_on_disapprove() { - new_test_ext().execute_with(|| { + ExtBuilder::default().build_and_execute(|| { let proposal = RuntimeCall::Collective(crate::Call::set_members { new_members: vec![1, 2, 3], prime: None, @@ -334,7 +351,7 @@ fn proposal_weight_limit_ignored_on_disapprove() { #[test] fn close_with_prime_works() { - new_test_ext().execute_with(|| { + ExtBuilder::default().build_and_execute(|| { let proposal = make_proposal(42); let proposal_len: u32 = proposal.using_encoded(|p| p.len() as u32); let proposal_weight = proposal.get_dispatch_info().weight; @@ -402,7 +419,7 @@ fn close_with_prime_works() { #[test] fn close_with_voting_prime_works() { - new_test_ext().execute_with(|| { + ExtBuilder::default().build_and_execute(|| { let proposal = make_proposal(42); let proposal_len: u32 = proposal.using_encoded(|p| p.len() as u32); let proposal_weight = proposal.get_dispatch_info().weight; @@ -472,7 +489,7 @@ fn close_with_voting_prime_works() { #[test] fn close_with_no_prime_but_majority_works() { - new_test_ext().execute_with(|| { + ExtBuilder::default().build_and_execute(|| { let proposal = make_proposal(42); let proposal_len: u32 = proposal.using_encoded(|p| p.len() as u32); let proposal_weight = proposal.get_dispatch_info().weight; @@ -552,7 +569,7 @@ fn close_with_no_prime_but_majority_works() { #[test] fn removal_of_old_voters_votes_works() { - new_test_ext().execute_with(|| { + ExtBuilder::default().build_and_execute(|| { let proposal = make_proposal(42); let proposal_len: u32 = proposal.using_encoded(|p| p.len() as u32); let hash = BlakeTwo256::hash_of(&proposal); @@ -600,7 +617,7 @@ fn removal_of_old_voters_votes_works() { #[test] fn removal_of_old_voters_votes_works_with_set_members() { - new_test_ext().execute_with(|| { + ExtBuilder::default().build_and_execute(|| { let proposal = make_proposal(42); let proposal_len: u32 = proposal.using_encoded(|p| p.len() as u32); let hash = BlakeTwo256::hash_of(&proposal); @@ -658,7 +675,7 @@ fn removal_of_old_voters_votes_works_with_set_members() { #[test] fn propose_works() { - new_test_ext().execute_with(|| { + ExtBuilder::default().build_and_execute(|| { let proposal = make_proposal(42); let proposal_len: u32 = proposal.using_encoded(|p| p.len() as u32); let hash = proposal.blake2_256().into(); @@ -690,7 +707,7 @@ fn propose_works() { #[test] fn limit_active_proposals() { - new_test_ext().execute_with(|| { + ExtBuilder::default().build_and_execute(|| { for i in 0..MaxProposals::get() { let proposal = make_proposal(i as u64); let proposal_len: u32 = proposal.using_encoded(|p| p.len() as u32); @@ -717,7 +734,7 @@ fn limit_active_proposals() { #[test] fn correct_validate_and_get_proposal() { - new_test_ext().execute_with(|| { + ExtBuilder::default().build_and_execute(|| { let proposal = RuntimeCall::Collective(crate::Call::set_members { new_members: vec![1, 2, 3], prime: None, @@ -763,7 +780,7 @@ fn correct_validate_and_get_proposal() { #[test] fn motions_ignoring_non_collective_proposals_works() { - new_test_ext().execute_with(|| { + ExtBuilder::default().build_and_execute(|| { let proposal = make_proposal(42); let proposal_len: u32 = proposal.using_encoded(|p| p.len() as u32); assert_noop!( @@ -780,7 +797,7 @@ fn motions_ignoring_non_collective_proposals_works() { #[test] fn motions_ignoring_non_collective_votes_works() { - new_test_ext().execute_with(|| { + ExtBuilder::default().build_and_execute(|| { let proposal = make_proposal(42); let proposal_len: u32 = proposal.using_encoded(|p| p.len() as u32); let hash: H256 = proposal.blake2_256().into(); @@ -799,7 +816,7 @@ fn motions_ignoring_non_collective_votes_works() { #[test] fn motions_ignoring_bad_index_collective_vote_works() { - new_test_ext().execute_with(|| { + ExtBuilder::default().build_and_execute(|| { System::set_block_number(3); let proposal = make_proposal(42); let proposal_len: u32 = proposal.using_encoded(|p| p.len() as u32); @@ -819,7 +836,7 @@ fn motions_ignoring_bad_index_collective_vote_works() { #[test] fn motions_vote_after_works() { - new_test_ext().execute_with(|| { + ExtBuilder::default().build_and_execute(|| { let proposal = make_proposal(42); let proposal_len: u32 = proposal.using_encoded(|p| p.len() as u32); let hash: H256 = proposal.blake2_256().into(); @@ -888,7 +905,7 @@ fn motions_vote_after_works() { #[test] fn motions_all_first_vote_free_works() { - new_test_ext().execute_with(|| { + ExtBuilder::default().build_and_execute(|| { let proposal = make_proposal(42); let proposal_len: u32 = proposal.using_encoded(|p| p.len() as u32); let hash: H256 = proposal.blake2_256().into(); @@ -946,7 +963,7 @@ fn motions_all_first_vote_free_works() { #[test] fn motions_reproposing_disapproved_works() { - new_test_ext().execute_with(|| { + ExtBuilder::default().build_and_execute(|| { let proposal = make_proposal(42); let proposal_len: u32 = proposal.using_encoded(|p| p.len() as u32); let proposal_weight = proposal.get_dispatch_info().weight; @@ -978,7 +995,7 @@ fn motions_reproposing_disapproved_works() { #[test] fn motions_approval_with_enough_votes_and_lower_voting_threshold_works() { - new_test_ext().execute_with(|| { + ExtBuilder::default().build_and_execute(|| { let proposal = RuntimeCall::Democracy(mock_democracy::Call::external_propose_majority {}); let proposal_len: u32 = proposal.using_encoded(|p| p.len() as u32); let proposal_weight = proposal.get_dispatch_info().weight; @@ -1108,7 +1125,7 @@ fn motions_approval_with_enough_votes_and_lower_voting_threshold_works() { #[test] fn motions_disapproval_works() { - new_test_ext().execute_with(|| { + ExtBuilder::default().build_and_execute(|| { let proposal = make_proposal(42); let proposal_len: u32 = proposal.using_encoded(|p| p.len() as u32); let proposal_weight = proposal.get_dispatch_info().weight; @@ -1167,7 +1184,7 @@ fn motions_disapproval_works() { #[test] fn motions_approval_works() { - new_test_ext().execute_with(|| { + ExtBuilder::default().build_and_execute(|| { let proposal = make_proposal(42); let proposal_len: u32 = proposal.using_encoded(|p| p.len() as u32); let proposal_weight = proposal.get_dispatch_info().weight; @@ -1228,7 +1245,7 @@ fn motions_approval_works() { #[test] fn motion_with_no_votes_closes_with_disapproval() { - new_test_ext().execute_with(|| { + ExtBuilder::default().build_and_execute(|| { let proposal = make_proposal(42); let proposal_len: u32 = proposal.using_encoded(|p| p.len() as u32); let proposal_weight = proposal.get_dispatch_info().weight; @@ -1289,7 +1306,7 @@ fn close_disapprove_does_not_care_about_weight_or_len() { // This test confirms that if you close a proposal that would be disapproved, // we do not care about the proposal length or proposal weight since it will // not be read from storage or executed. - new_test_ext().execute_with(|| { + ExtBuilder::default().build_and_execute(|| { let proposal = make_proposal(42); let proposal_len: u32 = proposal.using_encoded(|p| p.len() as u32); let hash: H256 = proposal.blake2_256().into(); @@ -1321,7 +1338,7 @@ fn close_disapprove_does_not_care_about_weight_or_len() { #[test] fn disapprove_proposal_works() { - new_test_ext().execute_with(|| { + ExtBuilder::default().build_and_execute(|| { let proposal = make_proposal(42); let proposal_len: u32 = proposal.using_encoded(|p| p.len() as u32); let hash: H256 = proposal.blake2_256().into(); @@ -1380,7 +1397,7 @@ fn genesis_build_panics_with_duplicate_members() { #[test] fn migration_v4() { - new_test_ext().execute_with(|| { + ExtBuilder::default().build_and_execute(|| { use frame_support::traits::PalletInfoAccess; let old_pallet = "OldCollective"; From 15bb9c61aecf73a3a7a1853151a27587edb34b97 Mon Sep 17 00:00:00 2001 From: Mike Ruje Date: Mon, 17 Apr 2023 11:07:14 +0200 Subject: [PATCH 386/558] imp function comparison (#13928) --- bin/node/bench/src/core.rs | 23 ++++++----------------- 1 file changed, 6 insertions(+), 17 deletions(-) diff --git a/bin/node/bench/src/core.rs b/bin/node/bench/src/core.rs index 29cda7099..1f22d3db6 100644 --- a/bin/node/bench/src/core.rs +++ b/bin/node/bench/src/core.rs @@ -72,24 +72,13 @@ pub struct NsFormatter(pub u64); impl fmt::Display for NsFormatter { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { let v = self.0; - - if v < 100 { - return write!(f, "{} ns", v) - } - - if self.0 < 100_000 { - return write!(f, "{:.1} µs", v as f64 / 1000.0) - } - - if self.0 < 1_000_000 { - return write!(f, "{:.4} ms", v as f64 / 1_000_000.0) - } - - if self.0 < 100_000_000 { - return write!(f, "{:.1} ms", v as f64 / 1_000_000.0) + match v { + v if v < 100 => write!(f, "{} ns", v), + v if v < 100_000 => write!(f, "{:.1} µs", v as f64 / 1000.0), + v if v < 1_000_000 => write!(f, "{:.4} ms", v as f64 / 1_000_000.0), + v if v < 100_000_000 => write!(f, "{:.1} ms", v as f64 / 1_000_000.0), + _ => write!(f, "{:.4} s", v as f64 / 1_000_000_000.0), } - - write!(f, "{:.4} s", v as f64 / 1_000_000_000.0) } } From 915d22ccaa45b2b93b1eb4194b4349c1db7930d8 Mon Sep 17 00:00:00 2001 From: Przemek Rzad Date: Mon, 17 Apr 2023 12:22:59 +0200 Subject: [PATCH 387/558] Update links in the Referenda Readme (#13929) * Update links in the Referenda Readme * Update frame/referenda/README.md --------- Co-authored-by: Oliver Tale-Yazdi --- frame/referenda/README.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/frame/referenda/README.md b/frame/referenda/README.md index 85031a011..b9a8b022c 100644 --- a/frame/referenda/README.md +++ b/frame/referenda/README.md @@ -1,8 +1,8 @@ # Referenda Pallet -- [`assembly::Config`](https://docs.rs/pallet-assembly/latest/pallet_assembly/trait.Config.html) -- [`Call`](https://docs.rs/pallet-assembly/latest/pallet_assembly/enum.Call.html) +- [`Config`](https://docs.rs/pallet-referenda/latest/pallet_referenda/pallet/trait.Config.html) +- [`Call`](https://docs.rs/pallet-referenda/latest/pallet_referenda/pallet/enum.Call.html) ## Overview -The Assembly pallet handles the administration of general stakeholder voting. +The Referenda pallet handles the administration of general stakeholder voting. From 785115b3a13901b0c708af8166430bcc9c71f28f Mon Sep 17 00:00:00 2001 From: Gavin Wood Date: Mon, 17 Apr 2023 19:13:55 +0100 Subject: [PATCH 388/558] Balances: repatriate_reserved should respect freezes (#13885) * repatriate_reserved should respect freezes * Docs * Fix and clean * Formatting * Update frame/balances/src/types.rs Co-authored-by: Jegor Sidorenko <5252494+jsidorenko@users.noreply.github.com> * Fix * Simplify * Fixes * Fixes --------- Co-authored-by: Jegor Sidorenko <5252494+jsidorenko@users.noreply.github.com> --- frame/balances/src/impl_currency.rs | 9 +++- frame/balances/src/lib.rs | 70 +++++++++++++++-------------- frame/balances/src/types.rs | 6 +-- 3 files changed, 46 insertions(+), 39 deletions(-) diff --git a/frame/balances/src/impl_currency.rs b/frame/balances/src/impl_currency.rs index 790a29f00..329ea289f 100644 --- a/frame/balances/src/impl_currency.rs +++ b/frame/balances/src/impl_currency.rs @@ -22,7 +22,7 @@ use frame_support::{ ensure, pallet_prelude::DispatchResult, traits::{ - tokens::{fungible, BalanceStatus as Status}, + tokens::{fungible, BalanceStatus as Status, Fortitude::Polite, Precision::BestEffort}, Currency, DefensiveSaturating, ExistenceRequirement, ExistenceRequirement::AllowDeath, Get, Imbalance, LockIdentifier, LockableCurrency, NamedReservableCurrency, @@ -590,13 +590,18 @@ where /// Is a no-op if: /// - the value to be moved is zero; or /// - the `slashed` id equal to `beneficiary` and the `status` is `Reserved`. + /// + /// This is `Polite` and thus will not repatriate any funds which would lead the total balance + /// to be less than the frozen amount. Returns `Ok` with the actual amount of funds moved, + /// which may be less than `value` since the operation is done an a `BestEffort` basis. fn repatriate_reserved( slashed: &T::AccountId, beneficiary: &T::AccountId, value: Self::Balance, status: Status, ) -> Result { - let actual = Self::do_transfer_reserved(slashed, beneficiary, value, true, status)?; + let actual = + Self::do_transfer_reserved(slashed, beneficiary, value, BestEffort, Polite, status)?; Ok(value.saturating_sub(actual)) } } diff --git a/frame/balances/src/lib.rs b/frame/balances/src/lib.rs index 029a17053..74aec1f2d 100644 --- a/frame/balances/src/lib.rs +++ b/frame/balances/src/lib.rs @@ -205,7 +205,10 @@ type AccountIdLookupOf = <::Lookup as StaticLookup #[frame_support::pallet] pub mod pallet { use super::*; - use frame_support::{pallet_prelude::*, traits::fungible::Credit}; + use frame_support::{ + pallet_prelude::*, + traits::{fungible::Credit, tokens::Precision}, + }; use frame_system::pallet_prelude::*; pub type CreditOf = Credit<::AccountId, Pallet>; @@ -1100,59 +1103,58 @@ pub mod pallet { } /// Move the reserved balance of one account into the balance of another, according to - /// `status`. + /// `status`. This will respect freezes/locks only if `fortitude` is `Polite`. /// - /// Is a no-op if: - /// - the value to be moved is zero; or - /// - the `slashed` id equal to `beneficiary` and the `status` is `Reserved`. + /// Is a no-op if the value to be moved is zero. /// /// NOTE: returns actual amount of transferred value in `Ok` case. pub(crate) fn do_transfer_reserved( slashed: &T::AccountId, beneficiary: &T::AccountId, value: T::Balance, - best_effort: bool, + precision: Precision, + fortitude: Fortitude, status: Status, ) -> Result { if value.is_zero() { return Ok(Zero::zero()) } + let max = >::reducible_total_balance_on_hold( + slashed, fortitude, + ); + let actual = match precision { + Precision::BestEffort => value.min(max), + Precision::Exact => value, + }; + ensure!(actual <= max, TokenError::FundsUnavailable); if slashed == beneficiary { return match status { - Status::Free => Ok(value.saturating_sub(Self::unreserve(slashed, value))), - Status::Reserved => Ok(value.saturating_sub(Self::reserved_balance(slashed))), + Status::Free => Ok(actual.saturating_sub(Self::unreserve(slashed, actual))), + Status::Reserved => Ok(actual), } } - let ((actual, maybe_dust_1), maybe_dust_2) = Self::try_mutate_account( + let ((_, maybe_dust_1), maybe_dust_2) = Self::try_mutate_account( beneficiary, - |to_account, is_new| -> Result<(T::Balance, Option), DispatchError> { + |to_account, is_new| -> Result<((), Option), DispatchError> { ensure!(!is_new, Error::::DeadAccount); - Self::try_mutate_account( - slashed, - |from_account, _| -> Result { - let actual = cmp::min(from_account.reserved, value); - ensure!( - best_effort || actual == value, - Error::::InsufficientBalance - ); - match status { - Status::Free => - to_account.free = to_account - .free - .checked_add(&actual) - .ok_or(ArithmeticError::Overflow)?, - Status::Reserved => - to_account.reserved = to_account - .reserved - .checked_add(&actual) - .ok_or(ArithmeticError::Overflow)?, - } - from_account.reserved -= actual; - Ok(actual) - }, - ) + Self::try_mutate_account(slashed, |from_account, _| -> DispatchResult { + match status { + Status::Free => + to_account.free = to_account + .free + .checked_add(&actual) + .ok_or(ArithmeticError::Overflow)?, + Status::Reserved => + to_account.reserved = to_account + .reserved + .checked_add(&actual) + .ok_or(ArithmeticError::Overflow)?, + } + from_account.reserved.saturating_reduce(actual); + Ok(()) + }) }, )?; diff --git a/frame/balances/src/types.rs b/frame/balances/src/types.rs index c96e1e44b..389124402 100644 --- a/frame/balances/src/types.rs +++ b/frame/balances/src/types.rs @@ -102,9 +102,9 @@ pub struct AccountData { /// This is the sum of all individual holds together with any sums still under the (deprecated) /// reserves API. pub reserved: Balance, - /// The amount that `free` may not drop below when reducing the balance, except for actions - /// where the account owner cannot reasonably benefit from thr balance reduction, such as - /// slashing. + /// The amount that `free + reserved` may not drop below when reducing the balance, except for + /// actions where the account owner cannot reasonably benefit from the balance reduction, such + /// as slashing. pub frozen: Balance, /// Extra information about this account. The MSB is a flag indicating whether the new ref- /// counting logic is in place for this account. From af29c6f10ee84c862f6ba7c14fc302b9824f9d4f Mon Sep 17 00:00:00 2001 From: Aaro Altonen <48052676+altonen@users.noreply.github.com> Date: Tue, 18 Apr 2023 10:47:36 +0300 Subject: [PATCH 389/558] Poll the substream validation before polling `Notifications` (#13934) * Poll the substream validation before polling `Notifications` In tests, it can happen that `Notifications` doesn't produce any events which causes `poll()` to return `Poll::Pending` and the substream validation futures won't get polled. Poll the futures before calling `Notifications` so results for substream validations are received even if `Notifications` is not producing any events. * Remove `pending_messages` * Remove unused import --- client/network/src/protocol.rs | 44 +++++++++++++--------------------- 1 file changed, 16 insertions(+), 28 deletions(-) diff --git a/client/network/src/protocol.rs b/client/network/src/protocol.rs index a7e6f36ef..0075e856e 100644 --- a/client/network/src/protocol.rs +++ b/client/network/src/protocol.rs @@ -40,7 +40,7 @@ use sc_utils::mpsc::TracingUnboundedSender; use sp_runtime::traits::Block as BlockT; use std::{ - collections::{HashMap, HashSet, VecDeque}, + collections::{HashMap, HashSet}, future::Future, iter, pin::Pin, @@ -77,8 +77,6 @@ type PendingSyncSubstreamValidation = // Lock must always be taken in order declared here. pub struct Protocol { - /// Pending list of messages to return from `poll` as a priority. - pending_messages: VecDeque, /// Used to report reputation changes. peerset_handle: sc_peerset::PeersetHandle, /// Handles opening the unique substream and sending and receiving raw messages. @@ -181,7 +179,6 @@ impl Protocol { }; let protocol = Self { - pending_messages: VecDeque::new(), peerset_handle: peerset_handle.clone(), behaviour, notification_protocols: iter::once(block_announces_protocol.notifications_protocol) @@ -409,8 +406,21 @@ impl NetworkBehaviour for Protocol { cx: &mut std::task::Context, params: &mut impl PollParameters, ) -> Poll> { - if let Some(message) = self.pending_messages.pop_front() { - return Poll::Ready(NetworkBehaviourAction::GenerateEvent(message)) + while let Poll::Ready(Some(validation_result)) = + self.sync_substream_validations.poll_next_unpin(cx) + { + match validation_result { + Ok((peer, roles)) => { + self.peers.insert(peer, roles); + }, + Err(peer) => { + log::debug!( + target: "sub-libp2p", + "`SyncingEngine` rejected stream" + ); + self.behaviour.disconnect_peer(&peer, HARDCODED_PEERSETS_SYNC); + }, + } } let event = match self.behaviour.poll(cx, params) { @@ -430,23 +440,6 @@ impl NetworkBehaviour for Protocol { return Poll::Ready(NetworkBehaviourAction::CloseConnection { peer_id, connection }), }; - while let Poll::Ready(Some(validation_result)) = - self.sync_substream_validations.poll_next_unpin(cx) - { - match validation_result { - Ok((peer, roles)) => { - self.peers.insert(peer, roles); - }, - Err(peer) => { - log::debug!( - target: "sub-libp2p", - "`SyncingEngine` rejected stream" - ); - self.behaviour.disconnect_peer(&peer, HARDCODED_PEERSETS_SYNC); - }, - } - } - let outcome = match event { NotificationsOut::CustomProtocolOpen { peer_id, @@ -509,7 +502,6 @@ impl NetworkBehaviour for Protocol { ) { Ok(handshake) => { let roles = handshake.roles; - self.peers.insert(peer_id, roles); let (tx, rx) = oneshot::channel(); let _ = self.tx.unbounded_send( @@ -644,10 +636,6 @@ impl NetworkBehaviour for Protocol { return Poll::Ready(NetworkBehaviourAction::GenerateEvent(outcome)) } - if let Some(message) = self.pending_messages.pop_front() { - return Poll::Ready(NetworkBehaviourAction::GenerateEvent(message)) - } - // This block can only be reached if an event was pulled from the behaviour and that // resulted in `CustomMessageOutcome::None`. Since there might be another pending // message from the behaviour, the task is scheduled again. From cb954820a8d8d765ce75021e244223a3b4d5722d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Silva?= <123550+andresilva@users.noreply.github.com> Date: Tue, 18 Apr 2023 10:38:04 +0100 Subject: [PATCH 390/558] babe: replace usage of SharedEpochChanges with internal RPC (#13883) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * babe: replace usage of SharedEpochChanges with internal RPC * babe-rpc: fix tests * babe: use SinkExt::send instead of Sender::try_send SinkExt::send provides backpressure in case the channel is full * Update client/consensus/babe/src/lib.rs Co-authored-by: Bastian Köcher * babe: fix spawn * babe: send handles backpressure * babe: use testing::TaskExecutor * babe-rpc: better error handling --------- Co-authored-by: Bastian Köcher --- Cargo.lock | 1 - bin/node/cli/src/service.rs | 10 +- bin/node/rpc/Cargo.toml | 1 - bin/node/rpc/src/lib.rs | 24 ++--- client/consensus/babe/rpc/src/lib.rs | 135 +++++++++++++------------- client/consensus/babe/src/lib.rs | 140 ++++++++++++++------------- client/sync-state-rpc/src/lib.rs | 35 ++++--- 7 files changed, 171 insertions(+), 175 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index bb8081221..b7d3bd5ee 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -5142,7 +5142,6 @@ dependencies = [ "sc-client-api", "sc-consensus-babe", "sc-consensus-babe-rpc", - "sc-consensus-epochs", "sc-consensus-grandpa", "sc-consensus-grandpa-rpc", "sc-rpc", diff --git a/bin/node/cli/src/service.rs b/bin/node/cli/src/service.rs index 7b0dfcda6..de6401805 100644 --- a/bin/node/cli/src/service.rs +++ b/bin/node/cli/src/service.rs @@ -203,7 +203,7 @@ pub fn new_partial( )?; let slot_duration = babe_link.config().slot_duration(); - let import_queue = sc_consensus_babe::import_queue( + let (import_queue, babe_worker_handle) = sc_consensus_babe::import_queue( babe_link.clone(), block_import.clone(), Some(Box::new(justification_import)), @@ -228,7 +228,7 @@ pub fn new_partial( let import_setup = (block_import, grandpa_link, babe_link); let (rpc_extensions_builder, rpc_setup) = { - let (_, grandpa_link, babe_link) = &import_setup; + let (_, grandpa_link, _) = &import_setup; let justification_stream = grandpa_link.justification_stream(); let shared_authority_set = grandpa_link.shared_authority_set().clone(); @@ -240,9 +240,6 @@ pub fn new_partial( Some(shared_authority_set.clone()), ); - let babe_config = babe_link.config().clone(); - let shared_epoch_changes = babe_link.epoch_changes().clone(); - let client = client.clone(); let pool = transaction_pool.clone(); let select_chain = select_chain.clone(); @@ -258,9 +255,8 @@ pub fn new_partial( chain_spec: chain_spec.cloned_box(), deny_unsafe, babe: node_rpc::BabeDeps { - babe_config: babe_config.clone(), - shared_epoch_changes: shared_epoch_changes.clone(), keystore: keystore.clone(), + babe_worker_handle: babe_worker_handle.clone(), }, grandpa: node_rpc::GrandpaDeps { shared_voter_state: shared_voter_state.clone(), diff --git a/bin/node/rpc/Cargo.toml b/bin/node/rpc/Cargo.toml index 0ea6f49bd..724efbe9a 100644 --- a/bin/node/rpc/Cargo.toml +++ b/bin/node/rpc/Cargo.toml @@ -21,7 +21,6 @@ sc-chain-spec = { version = "4.0.0-dev", path = "../../../client/chain-spec" } sc-client-api = { version = "4.0.0-dev", path = "../../../client/api" } sc-consensus-babe = { version = "0.10.0-dev", path = "../../../client/consensus/babe" } sc-consensus-babe-rpc = { version = "0.10.0-dev", path = "../../../client/consensus/babe/rpc" } -sc-consensus-epochs = { version = "0.10.0-dev", path = "../../../client/consensus/epochs" } sc-consensus-grandpa = { version = "0.10.0-dev", path = "../../../client/consensus/grandpa" } sc-consensus-grandpa-rpc = { version = "0.10.0-dev", path = "../../../client/consensus/grandpa/rpc" } sc-rpc = { version = "4.0.0-dev", path = "../../../client/rpc" } diff --git a/bin/node/rpc/src/lib.rs b/bin/node/rpc/src/lib.rs index 9dcdf0f21..5f61fdcd5 100644 --- a/bin/node/rpc/src/lib.rs +++ b/bin/node/rpc/src/lib.rs @@ -36,8 +36,7 @@ use std::sync::Arc; use jsonrpsee::RpcModule; use node_primitives::{AccountId, Balance, Block, BlockNumber, Hash, Index}; use sc_client_api::AuxStore; -use sc_consensus_babe::{BabeConfiguration, Epoch}; -use sc_consensus_epochs::SharedEpochChanges; +use sc_consensus_babe::BabeWorkerHandle; use sc_consensus_grandpa::{ FinalityProofProvider, GrandpaJustificationStream, SharedAuthoritySet, SharedVoterState, }; @@ -53,10 +52,8 @@ use sp_keystore::KeystorePtr; /// Extra dependencies for BABE. pub struct BabeDeps { - /// BABE protocol config. - pub babe_config: BabeConfiguration, - /// BABE pending epoch changes. - pub shared_epoch_changes: SharedEpochChanges, + /// A handle to the BABE worker for issuing requests. + pub babe_worker_handle: BabeWorkerHandle, /// The keystore that manages the keys of the node. pub keystore: KeystorePtr, } @@ -130,7 +127,7 @@ where let mut io = RpcModule::new(()); let FullDeps { client, pool, select_chain, chain_spec, deny_unsafe, babe, grandpa } = deps; - let BabeDeps { keystore, babe_config, shared_epoch_changes } = babe; + let BabeDeps { keystore, babe_worker_handle } = babe; let GrandpaDeps { shared_voter_state, shared_authority_set, @@ -151,15 +148,8 @@ where io.merge(Mmr::new(client.clone()).into_rpc())?; io.merge(TransactionPayment::new(client.clone()).into_rpc())?; io.merge( - Babe::new( - client.clone(), - shared_epoch_changes.clone(), - keystore, - babe_config, - select_chain, - deny_unsafe, - ) - .into_rpc(), + Babe::new(client.clone(), babe_worker_handle.clone(), keystore, select_chain, deny_unsafe) + .into_rpc(), )?; io.merge( Grandpa::new( @@ -173,7 +163,7 @@ where )?; io.merge( - SyncState::new(chain_spec, client.clone(), shared_authority_set, shared_epoch_changes)? + SyncState::new(chain_spec, client.clone(), shared_authority_set, babe_worker_handle)? .into_rpc(), )?; diff --git a/client/consensus/babe/rpc/src/lib.rs b/client/consensus/babe/rpc/src/lib.rs index cdc8dbfd8..1ae15cc54 100644 --- a/client/consensus/babe/rpc/src/lib.rs +++ b/client/consensus/babe/rpc/src/lib.rs @@ -18,28 +18,29 @@ //! RPC api for babe. +use std::{collections::HashMap, sync::Arc}; + use futures::TryFutureExt; use jsonrpsee::{ core::{async_trait, Error as JsonRpseeError, RpcResult}, proc_macros::rpc, types::{error::CallError, ErrorObject}, }; +use serde::{Deserialize, Serialize}; -use sc_consensus_babe::{authorship, Epoch}; -use sc_consensus_epochs::{descendent_query, Epoch as EpochT, SharedEpochChanges}; +use sc_consensus_babe::{authorship, BabeWorkerHandle}; +use sc_consensus_epochs::Epoch as EpochT; use sc_rpc_api::DenyUnsafe; -use serde::{Deserialize, Serialize}; use sp_api::ProvideRuntimeApi; use sp_application_crypto::AppCrypto; use sp_blockchain::{Error as BlockChainError, HeaderBackend, HeaderMetadata}; use sp_consensus::{Error as ConsensusError, SelectChain}; -use sp_consensus_babe::{ - digests::PreDigest, AuthorityId, BabeApi as BabeRuntimeApi, BabeConfiguration, -}; +use sp_consensus_babe::{digests::PreDigest, AuthorityId, BabeApi as BabeRuntimeApi}; use sp_core::crypto::ByteArray; use sp_keystore::KeystorePtr; use sp_runtime::traits::{Block as BlockT, Header as _}; -use std::{collections::HashMap, sync::Arc}; + +const BABE_ERROR: i32 = 9000; /// Provides rpc methods for interacting with Babe. #[rpc(client, server)] @@ -54,12 +55,10 @@ pub trait BabeApi { pub struct Babe { /// shared reference to the client. client: Arc, - /// shared reference to EpochChanges - shared_epoch_changes: SharedEpochChanges, + /// A handle to the BABE worker for issuing requests. + babe_worker_handle: BabeWorkerHandle, /// shared reference to the Keystore keystore: KeystorePtr, - /// config (actually holds the slot duration) - babe_config: BabeConfiguration, /// The SelectChain strategy select_chain: SC, /// Whether to deny unsafe calls @@ -70,13 +69,12 @@ impl Babe { /// Creates a new instance of the Babe Rpc handler. pub fn new( client: Arc, - shared_epoch_changes: SharedEpochChanges, + babe_worker_handle: BabeWorkerHandle, keystore: KeystorePtr, - babe_config: BabeConfiguration, select_chain: SC, deny_unsafe: DenyUnsafe, ) -> Self { - Self { client, shared_epoch_changes, keystore, babe_config, select_chain, deny_unsafe } + Self { client, babe_worker_handle, keystore, select_chain, deny_unsafe } } } @@ -93,21 +91,21 @@ where { async fn epoch_authorship(&self) -> RpcResult> { self.deny_unsafe.check_if_safe()?; - let header = self.select_chain.best_chain().map_err(Error::Consensus).await?; + + let best_header = self.select_chain.best_chain().map_err(Error::SelectChain).await?; + let epoch_start = self .client .runtime_api() - .current_epoch_start(header.hash()) - .map_err(|err| Error::StringError(format!("{:?}", err)))?; - - let epoch = epoch_data( - &self.shared_epoch_changes, - &self.client, - &self.babe_config, - *epoch_start, - &self.select_chain, - ) - .await?; + .current_epoch_start(best_header.hash()) + .map_err(|_| Error::FetchEpoch)?; + + let epoch = self + .babe_worker_handle + .epoch_data_for_child_of(best_header.hash(), *best_header.number(), epoch_start) + .await + .map_err(|_| Error::FetchEpoch)?; + let (epoch_start, epoch_end) = (epoch.start_slot(), epoch.end_slot()); let mut claims: HashMap = HashMap::new(); @@ -159,59 +157,37 @@ pub struct EpochAuthorship { secondary_vrf: Vec, } -/// Errors encountered by the RPC +/// Top-level error type for the RPC handler. #[derive(Debug, thiserror::Error)] pub enum Error { - /// Consensus error - #[error(transparent)] - Consensus(#[from] ConsensusError), - /// Errors that can be formatted as a String - #[error("{0}")] - StringError(String), + /// Failed to fetch the current best header. + #[error("Failed to fetch the current best header: {0}")] + SelectChain(ConsensusError), + /// Failed to fetch epoch data. + #[error("Failed to fetch epoch data")] + FetchEpoch, } impl From for JsonRpseeError { fn from(error: Error) -> Self { + let error_code = match error { + Error::SelectChain(_) => 1, + Error::FetchEpoch => 2, + }; + JsonRpseeError::Call(CallError::Custom(ErrorObject::owned( - 1234, + BABE_ERROR + error_code, error.to_string(), - None::<()>, + Some(format!("{:?}", error)), ))) } } -/// Fetches the epoch data for a given slot. -async fn epoch_data( - epoch_changes: &SharedEpochChanges, - client: &Arc, - babe_config: &BabeConfiguration, - slot: u64, - select_chain: &SC, -) -> Result -where - B: BlockT, - C: HeaderBackend + HeaderMetadata + 'static, - SC: SelectChain, -{ - let parent = select_chain.best_chain().await?; - epoch_changes - .shared_data() - .epoch_data_for_child_of( - descendent_query(&**client), - &parent.hash(), - *parent.number(), - slot.into(), - |slot| Epoch::genesis(babe_config, slot), - ) - .map_err(|e| Error::Consensus(ConsensusError::ChainLookup(e.to_string())))? - .ok_or(Error::Consensus(ConsensusError::InvalidAuthoritiesSet)) -} - #[cfg(test)] mod tests { use super::*; - use sc_consensus_babe::block_import; - use sp_core::crypto::key_types::BABE; + use sp_consensus_babe::inherents::InherentDataProvider; + use sp_core::{crypto::key_types::BABE, testing::TaskExecutor}; use sp_keyring::Sr25519Keyring; use sp_keystore::{testing::MemoryKeystore, Keystore}; use substrate_test_runtime_client::{ @@ -233,14 +209,35 @@ mod tests { let builder = TestClientBuilder::new(); let (client, longest_chain) = builder.build_with_longest_chain(); let client = Arc::new(client); + let task_executor = TaskExecutor::new(); + let keystore = create_keystore(Sr25519Keyring::Alice); + let config = sc_consensus_babe::configuration(&*client).expect("config available"); - let (_, link) = block_import(config.clone(), client.clone(), client.clone()) - .expect("can initialize block-import"); + let slot_duration = config.slot_duration(); - let epoch_changes = link.epoch_changes().clone(); - let keystore = create_keystore(Sr25519Keyring::Alice); + let (block_import, link) = + sc_consensus_babe::block_import(config.clone(), client.clone(), client.clone()) + .expect("can initialize block-import"); + + let (_, babe_worker_handle) = sc_consensus_babe::import_queue( + link.clone(), + block_import.clone(), + None, + client.clone(), + longest_chain.clone(), + move |_, _| async move { + Ok((InherentDataProvider::from_timestamp_and_slot_duration( + 0.into(), + slot_duration, + ),)) + }, + &task_executor, + None, + None, + ) + .unwrap(); - Babe::new(client.clone(), epoch_changes, keystore, config, longest_chain, deny_unsafe) + Babe::new(client.clone(), babe_worker_handle, keystore, longest_chain, deny_unsafe) } #[tokio::test] diff --git a/client/consensus/babe/src/lib.rs b/client/consensus/babe/src/lib.rs index e540cae1f..6327c8c65 100644 --- a/client/consensus/babe/src/lib.rs +++ b/client/consensus/babe/src/lib.rs @@ -89,8 +89,8 @@ use prometheus_endpoint::Registry; use schnorrkel::SignatureError; use sc_client_api::{ - backend::AuxStore, AuxDataOperations, Backend as BackendT, BlockchainEvents, - FinalityNotification, PreCommitActions, ProvideUncles, UsageProvider, + backend::AuxStore, AuxDataOperations, Backend as BackendT, FinalityNotification, + PreCommitActions, UsageProvider, }; use sc_consensus::{ block_import::{ @@ -338,6 +338,9 @@ pub enum Error { /// Create inherents error. #[error("Creating inherents failed: {0}")] CreateInherents(sp_inherents::Error), + /// Background worker is not running and therefore requests cannot be answered. + #[error("Background worker is not running")] + BackgroundWorkerTerminated, /// Client error #[error(transparent)] Client(sp_blockchain::Error), @@ -475,9 +478,6 @@ pub fn start_babe( where B: BlockT, C: ProvideRuntimeApi - + ProvideUncles - + BlockchainEvents - + PreCommitActions + HeaderBackend + HeaderMetadata + Send @@ -498,8 +498,6 @@ where BS: BackoffAuthoringBlocksStrategy> + Send + Sync + 'static, Error: std::error::Error + Send + From + From + 'static, { - const HANDLE_BUFFER_SIZE: usize = 1024; - let slot_notification_sinks = Arc::new(Mutex::new(Vec::new())); let worker = BabeSlotWorker { @@ -529,17 +527,7 @@ where create_inherent_data_providers, ); - let (worker_tx, worker_rx) = channel(HANDLE_BUFFER_SIZE); - - let answer_requests = - answer_requests(worker_rx, babe_link.config, client, babe_link.epoch_changes); - - let inner = future::select(Box::pin(slot_worker), Box::pin(answer_requests)); - Ok(BabeWorker { - inner: Box::pin(inner.map(|_| ())), - slot_notification_sinks, - handle: BabeWorkerHandle(worker_tx), - }) + Ok(BabeWorker { inner: Box::pin(slot_worker), slot_notification_sinks }) } // Remove obsolete block's weight data by leveraging finality notifications. @@ -593,42 +581,26 @@ async fn answer_requests( client: Arc, epoch_changes: SharedEpochChanges, ) where - C: ProvideRuntimeApi - + ProvideUncles - + BlockchainEvents - + HeaderBackend - + HeaderMetadata - + Send - + Sync - + 'static, + C: HeaderBackend + HeaderMetadata, { while let Some(request) = request_rx.next().await { match request { - BabeRequest::EpochForChild(parent_hash, parent_number, slot_number, response) => { + BabeRequest::EpochData(response) => { + let _ = response.send(epoch_changes.shared_data().clone()); + }, + BabeRequest::EpochDataForChildOf(parent_hash, parent_number, slot, response) => { let lookup = || { let epoch_changes = epoch_changes.shared_data(); - let epoch_descriptor = epoch_changes - .epoch_descriptor_for_child_of( + epoch_changes + .epoch_data_for_child_of( descendent_query(&*client), &parent_hash, parent_number, - slot_number, + slot, + |slot| Epoch::genesis(&config, slot), ) .map_err(|e| Error::::ForkTree(Box::new(e)))? - .ok_or(Error::::FetchEpoch(parent_hash))?; - - let viable_epoch = epoch_changes - .viable_epoch(&epoch_descriptor, |slot| Epoch::genesis(&config, slot)) - .ok_or(Error::::FetchEpoch(parent_hash))?; - - Ok(sp_consensus_babe::Epoch { - epoch_index: viable_epoch.as_ref().epoch_index, - start_slot: viable_epoch.as_ref().start_slot, - duration: viable_epoch.as_ref().duration, - authorities: viable_epoch.as_ref().authorities.clone(), - randomness: viable_epoch.as_ref().randomness, - config: viable_epoch.as_ref().config.clone(), - }) + .ok_or(Error::::FetchEpoch(parent_hash)) }; let _ = response.send(lookup()); @@ -638,17 +610,13 @@ async fn answer_requests( } /// Requests to the BABE service. -#[non_exhaustive] -pub enum BabeRequest { +enum BabeRequest { + /// Request all available epoch data. + EpochData(oneshot::Sender>), /// Request the epoch that a child of the given block, with the given slot number would have. /// /// The parent block is identified by its hash and number. - EpochForChild( - B::Hash, - NumberFor, - Slot, - oneshot::Sender>>, - ), + EpochDataForChildOf(B::Hash, NumberFor, Slot, oneshot::Sender>>), } /// A handle to the BABE worker for issuing requests. @@ -656,11 +624,41 @@ pub enum BabeRequest { pub struct BabeWorkerHandle(Sender>); impl BabeWorkerHandle { - /// Send a request to the BABE service. - pub async fn send(&mut self, request: BabeRequest) { - // Failure to send means that the service is down. - // This will manifest as the receiver of the request being dropped. - let _ = self.0.send(request).await; + async fn send_request(&self, request: BabeRequest) -> Result<(), Error> { + match self.0.clone().send(request).await { + Err(err) if err.is_disconnected() => return Err(Error::BackgroundWorkerTerminated), + Err(err) => warn!( + target: LOG_TARGET, + "Unhandled error when sending request to worker: {:?}", err + ), + _ => {}, + } + + Ok(()) + } + + /// Fetch all available epoch data. + pub async fn epoch_data(&self) -> Result, Error> { + let (tx, rx) = oneshot::channel(); + self.send_request(BabeRequest::EpochData(tx)).await?; + + rx.await.or(Err(Error::BackgroundWorkerTerminated)) + } + + /// Fetch the epoch that a child of the given block, with the given slot number would have. + /// + /// The parent block is identified by its hash and number. + pub async fn epoch_data_for_child_of( + &self, + parent_hash: B::Hash, + parent_number: NumberFor, + slot: Slot, + ) -> Result> { + let (tx, rx) = oneshot::channel(); + self.send_request(BabeRequest::EpochDataForChildOf(parent_hash, parent_number, slot, tx)) + .await?; + + rx.await.or(Err(Error::BackgroundWorkerTerminated))? } } @@ -669,7 +667,6 @@ impl BabeWorkerHandle { pub struct BabeWorker { inner: Pin + Send + 'static>>, slot_notification_sinks: SlotNotificationSinks, - handle: BabeWorkerHandle, } impl BabeWorker { @@ -684,11 +681,6 @@ impl BabeWorker { self.slot_notification_sinks.lock().push(sink); stream } - - /// Get a handle to the worker. - pub fn handle(&self) -> BabeWorkerHandle { - self.handle.clone() - } } impl Future for BabeWorker { @@ -1790,7 +1782,7 @@ pub fn import_queue( spawner: &impl sp_core::traits::SpawnEssentialNamed, registry: Option<&Registry>, telemetry: Option, -) -> ClientResult> +) -> ClientResult<(DefaultImportQueue, BabeWorkerHandle)> where Inner: BlockImport< Block, @@ -1811,16 +1803,28 @@ where CIDP: CreateInherentDataProviders + Send + Sync + 'static, CIDP::InherentDataProviders: InherentDataProviderExt + Send + Sync, { + const HANDLE_BUFFER_SIZE: usize = 1024; + let verifier = BabeVerifier { select_chain, create_inherent_data_providers, - config: babe_link.config, - epoch_changes: babe_link.epoch_changes, + config: babe_link.config.clone(), + epoch_changes: babe_link.epoch_changes.clone(), telemetry, - client, + client: client.clone(), }; - Ok(BasicQueue::new(verifier, Box::new(block_import), justification_import, spawner, registry)) + let (worker_tx, worker_rx) = channel(HANDLE_BUFFER_SIZE); + + let answer_requests = + answer_requests(worker_rx, babe_link.config, client, babe_link.epoch_changes); + + spawner.spawn_essential("babe-worker", Some("babe"), answer_requests.boxed()); + + Ok(( + BasicQueue::new(verifier, Box::new(block_import), justification_import, spawner, registry), + BabeWorkerHandle(worker_tx), + )) } /// Reverts protocol aux data to at most the last finalized block. diff --git a/client/sync-state-rpc/src/lib.rs b/client/sync-state-rpc/src/lib.rs index 78d5cafa3..dda8a7edf 100644 --- a/client/sync-state-rpc/src/lib.rs +++ b/client/sync-state-rpc/src/lib.rs @@ -41,20 +41,21 @@ #![deny(unused_crate_dependencies)] +use std::sync::Arc; + use jsonrpsee::{ - core::{Error as JsonRpseeError, RpcResult}, + core::{async_trait, Error as JsonRpseeError, RpcResult}, proc_macros::rpc, types::{error::CallError, ErrorObject}, }; + use sc_client_api::StorageData; +use sc_consensus_babe::{BabeWorkerHandle, Error as BabeError}; use sp_blockchain::HeaderBackend; use sp_runtime::traits::{Block as BlockT, NumberFor}; -use std::sync::Arc; type SharedAuthoritySet = sc_consensus_grandpa::SharedAuthoritySet<::Hash, NumberFor>; -type SharedEpochChanges = - sc_consensus_epochs::SharedEpochChanges; /// Error type used by this crate. #[derive(Debug, thiserror::Error)] @@ -66,6 +67,9 @@ pub enum Error { #[error("Failed to load the block weight for block {0:?}")] LoadingBlockWeightFailed(Block::Hash), + #[error("Failed to load the BABE epoch data: {0}")] + LoadingEpochDataFailed(BabeError), + #[error("JsonRpc error: {0}")] JsonRpc(String), @@ -125,7 +129,7 @@ pub struct LightSyncState { pub trait SyncStateApi { /// Returns the JSON serialized chainspec running the node, with a sync state. #[method(name = "sync_state_genSyncSpec")] - fn system_gen_sync_spec(&self, raw: bool) -> RpcResult; + async fn system_gen_sync_spec(&self, raw: bool) -> RpcResult; } /// An api for sync state RPC calls. @@ -133,7 +137,7 @@ pub struct SyncState { chain_spec: Box, client: Arc, shared_authority_set: SharedAuthoritySet, - shared_epoch_changes: SharedEpochChanges, + babe_worker_handle: BabeWorkerHandle, } impl SyncState @@ -146,18 +150,24 @@ where chain_spec: Box, client: Arc, shared_authority_set: SharedAuthoritySet, - shared_epoch_changes: SharedEpochChanges, + babe_worker_handle: BabeWorkerHandle, ) -> Result> { if sc_chain_spec::get_extension::(chain_spec.extensions()) .is_some() { - Ok(Self { chain_spec, client, shared_authority_set, shared_epoch_changes }) + Ok(Self { chain_spec, client, shared_authority_set, babe_worker_handle }) } else { Err(Error::::LightSyncStateExtensionNotFound) } } - fn build_sync_state(&self) -> Result, Error> { + async fn build_sync_state(&self) -> Result, Error> { + let epoch_changes = self + .babe_worker_handle + .epoch_data() + .await + .map_err(Error::LoadingEpochDataFailed)?; + let finalized_hash = self.client.info().finalized_hash; let finalized_header = self .client @@ -170,20 +180,21 @@ where Ok(LightSyncState { finalized_block_header: finalized_header, - babe_epoch_changes: self.shared_epoch_changes.shared_data().clone(), + babe_epoch_changes: epoch_changes, babe_finalized_block_weight: finalized_block_weight, grandpa_authority_set: self.shared_authority_set.clone_inner(), }) } } +#[async_trait] impl SyncStateApiServer for SyncState where Block: BlockT, Backend: HeaderBackend + sc_client_api::AuxStore + 'static, { - fn system_gen_sync_spec(&self, raw: bool) -> RpcResult { - let current_sync_state = self.build_sync_state()?; + async fn system_gen_sync_spec(&self, raw: bool) -> RpcResult { + let current_sync_state = self.build_sync_state().await?; let mut chain_spec = self.chain_spec.cloned_box(); let extension = sc_chain_spec::get_extension_mut::( From 88e2c9e3935c3951da3c6e3bdb8fb1ae74dc1258 Mon Sep 17 00:00:00 2001 From: yjh Date: Tue, 18 Apr 2023 23:46:46 +0800 Subject: [PATCH 391/558] chore(docs): improve some comments (#13937) * chore(docs): improve some comments * Update client/network/common/src/sync/message.rs Co-authored-by: Koute * Update client/rpc-api/src/chain/mod.rs Co-authored-by: Koute * Update client/rpc/src/chain/mod.rs Co-authored-by: Koute * Update client/rpc/src/chain/mod.rs Co-authored-by: Koute * Update frame/staking/src/pallet/impls.rs Co-authored-by: Koute --------- Co-authored-by: Koute --- client/network/common/src/sync/message.rs | 2 +- client/network/common/src/sync/warp.rs | 4 ++-- client/rpc-api/src/chain/mod.rs | 2 +- client/rpc/src/chain/mod.rs | 4 ++-- frame/staking/src/pallet/impls.rs | 2 +- 5 files changed, 7 insertions(+), 7 deletions(-) diff --git a/client/network/common/src/sync/message.rs b/client/network/common/src/sync/message.rs index c651660c8..7cdb14172 100644 --- a/client/network/common/src/sync/message.rs +++ b/client/network/common/src/sync/message.rs @@ -185,7 +185,7 @@ pub mod generic { pub blocks: Vec>, } - /// Announce a new complete relay chain block on the network. + /// Announce a new complete block on the network. #[derive(Debug, PartialEq, Eq, Clone)] pub struct BlockAnnounce { /// New block header. diff --git a/client/network/common/src/sync/warp.rs b/client/network/common/src/sync/warp.rs index 5f2cacd2e..aef257af4 100644 --- a/client/network/common/src/sync/warp.rs +++ b/client/network/common/src/sync/warp.rs @@ -32,7 +32,7 @@ pub struct WarpProofRequest { /// The different types of warp syncing. pub enum WarpSyncParams { - /// Standard warp sync for the relay chain + /// Standard warp sync for the chain. WithProvider(Arc>), /// Skip downloading proofs and wait for a header of the state that should be downloaded. /// @@ -48,7 +48,7 @@ pub enum VerificationResult { Complete(SetId, AuthorityList, Block::Header), } -/// Warp sync backend. Handles retrieveing and verifying warp sync proofs. +/// Warp sync backend. Handles retrieving and verifying warp sync proofs. pub trait WarpSyncProvider: Send + Sync { /// Generate proof starting at given block hash. The proof is accumulated until maximum proof /// size is reached. diff --git a/client/rpc-api/src/chain/mod.rs b/client/rpc-api/src/chain/mod.rs index 2ed767c37..f215cd978 100644 --- a/client/rpc-api/src/chain/mod.rs +++ b/client/rpc-api/src/chain/mod.rs @@ -29,7 +29,7 @@ pub trait ChainApi { #[method(name = "chain_getHeader", blocking)] fn header(&self, hash: Option) -> RpcResult>; - /// Get header and body of a relay chain block. + /// Get header and body of a block. #[method(name = "chain_getBlock", blocking)] fn block(&self, hash: Option) -> RpcResult>; diff --git a/client/rpc/src/chain/mod.rs b/client/rpc/src/chain/mod.rs index d54811320..2b5994f21 100644 --- a/client/rpc/src/chain/mod.rs +++ b/client/rpc/src/chain/mod.rs @@ -59,10 +59,10 @@ where } } - /// Get header of a relay chain block. + /// Get header of a block. fn header(&self, hash: Option) -> Result, Error>; - /// Get header and body of a relay chain block. + /// Get header and body of a block. fn block(&self, hash: Option) -> Result>, Error>; /// Get hash of the n-th block in the canon chain. diff --git a/frame/staking/src/pallet/impls.rs b/frame/staking/src/pallet/impls.rs index 5143538c2..3058d3edc 100644 --- a/frame/staking/src/pallet/impls.rs +++ b/frame/staking/src/pallet/impls.rs @@ -1230,7 +1230,7 @@ impl historical::SessionManager pallet_authorship::EventHandler for Pallet where T: Config + pallet_authorship::Config + pallet_session::Config, From 917f7e38c58cbf98ef2a2ed460bab530b160bc1e Mon Sep 17 00:00:00 2001 From: William Freudenberger Date: Wed, 19 Apr 2023 09:49:42 +0200 Subject: [PATCH 392/558] feat: pallet asset-rate (#13608) * poc * fix: remove AssetIdParameter * tests: add * docs: add pallet description * feat: add benches * refactor: UnknownAssetId * fix: normalize mock cfg * fix: benchmarks * chore: add weights * refactor: remove storage getter * chore: apply suggestions from code review * docs: add native balance to calls * chore: apply suggestions from code review * chore: apply ConversionFromAssetBalance * tests: update balance mock * chore: apply suggestions from code review * ci: set publish to false * docs: fix missing rustdoc --------- Co-authored-by: parity-processbot <> --- Cargo.lock | 17 ++ Cargo.toml | 1 + bin/node/runtime/Cargo.toml | 4 + bin/node/runtime/src/lib.rs | 13 ++ frame/asset-rate/Cargo.toml | 52 ++++++ frame/asset-rate/src/benchmarking.rs | 86 ++++++++++ frame/asset-rate/src/lib.rs | 239 +++++++++++++++++++++++++++ frame/asset-rate/src/mock.rs | 100 +++++++++++ frame/asset-rate/src/tests.rs | 121 ++++++++++++++ frame/asset-rate/src/weights.rs | 129 +++++++++++++++ 10 files changed, 762 insertions(+) create mode 100644 frame/asset-rate/Cargo.toml create mode 100644 frame/asset-rate/src/benchmarking.rs create mode 100644 frame/asset-rate/src/lib.rs create mode 100644 frame/asset-rate/src/mock.rs create mode 100644 frame/asset-rate/src/tests.rs create mode 100644 frame/asset-rate/src/weights.rs diff --git a/Cargo.lock b/Cargo.lock index b7d3bd5ee..c0920e4dc 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3634,6 +3634,7 @@ dependencies = [ "log", "node-primitives", "pallet-alliance", + "pallet-asset-rate", "pallet-asset-tx-payment", "pallet-assets", "pallet-authority-discovery", @@ -5556,6 +5557,22 @@ dependencies = [ "sp-std", ] +[[package]] +name = "pallet-asset-rate" +version = "4.0.0-dev" +dependencies = [ + "frame-benchmarking", + "frame-support", + "frame-system", + "pallet-balances", + "parity-scale-codec", + "scale-info", + "sp-core", + "sp-io", + "sp-runtime", + "sp-std", +] + [[package]] name = "pallet-asset-tx-payment" version = "4.0.0-dev" diff --git a/Cargo.toml b/Cargo.toml index cae8976cc..ae89750dd 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -166,6 +166,7 @@ members = [ "frame/transaction-payment/rpc/runtime-api", "frame/transaction-storage", "frame/treasury", + "frame/asset-rate", "frame/tips", "frame/uniques", "frame/utility", diff --git a/bin/node/runtime/Cargo.toml b/bin/node/runtime/Cargo.toml index 65a8d208d..0fa037c65 100644 --- a/bin/node/runtime/Cargo.toml +++ b/bin/node/runtime/Cargo.toml @@ -53,6 +53,7 @@ frame-election-provider-support = { version = "4.0.0-dev", default-features = fa frame-system-rpc-runtime-api = { version = "4.0.0-dev", default-features = false, path = "../../../frame/system/rpc/runtime-api/" } frame-try-runtime = { version = "0.10.0-dev", default-features = false, path = "../../../frame/try-runtime", optional = true } pallet-alliance = { version = "4.0.0-dev", default-features = false, path = "../../../frame/alliance" } +pallet-asset-rate = { version = "4.0.0-dev", default-features = false, path = "../../../frame/asset-rate" } pallet-assets = { version = "4.0.0-dev", default-features = false, path = "../../../frame/assets" } pallet-authority-discovery = { version = "4.0.0-dev", default-features = false, path = "../../../frame/authority-discovery" } pallet-authorship = { version = "4.0.0-dev", default-features = false, path = "../../../frame/authorship" } @@ -201,6 +202,7 @@ std = [ "pallet-transaction-payment/std", "pallet-transaction-storage/std", "pallet-treasury/std", + "pallet-asset-rate/std", "sp-transaction-pool/std", "pallet-utility/std", "sp-version/std", @@ -272,6 +274,7 @@ runtime-benchmarks = [ "pallet-tips/runtime-benchmarks", "pallet-transaction-storage/runtime-benchmarks", "pallet-treasury/runtime-benchmarks", + "pallet-asset-rate/runtime-benchmarks", "pallet-utility/runtime-benchmarks", "pallet-uniques/runtime-benchmarks", "pallet-nfts/runtime-benchmarks", @@ -333,6 +336,7 @@ try-runtime = [ "pallet-timestamp/try-runtime", "pallet-tips/try-runtime", "pallet-treasury/try-runtime", + "pallet-asset-rate/try-runtime", "pallet-utility/try-runtime", "pallet-transaction-payment/try-runtime", "pallet-asset-tx-payment/try-runtime", diff --git a/bin/node/runtime/src/lib.rs b/bin/node/runtime/src/lib.rs index 433ca8e8b..cdb05d159 100644 --- a/bin/node/runtime/src/lib.rs +++ b/bin/node/runtime/src/lib.rs @@ -1131,6 +1131,17 @@ impl pallet_treasury::Config for Runtime { type SpendOrigin = EnsureWithSuccess, AccountId, MaxBalance>; } +impl pallet_asset_rate::Config for Runtime { + type CreateOrigin = EnsureRoot; + type RemoveOrigin = EnsureRoot; + type UpdateOrigin = EnsureRoot; + type Balance = Balance; + type Currency = Balances; + type AssetId = u32; + type RuntimeEvent = RuntimeEvent; + type WeightInfo = pallet_asset_rate::weights::SubstrateWeight; +} + parameter_types! { pub const BountyCuratorDeposit: Permill = Permill::from_percent(50); pub const BountyValueMinimum: Balance = 5 * DOLLARS; @@ -1765,6 +1776,7 @@ construct_runtime!( TechnicalMembership: pallet_membership::, Grandpa: pallet_grandpa, Treasury: pallet_treasury, + AssetRate: pallet_asset_rate, Contracts: pallet_contracts, Sudo: pallet_sudo, ImOnline: pallet_im_online, @@ -1922,6 +1934,7 @@ mod benches { [pallet_tips, Tips] [pallet_transaction_storage, TransactionStorage] [pallet_treasury, Treasury] + [pallet_asset_rate, AssetRate] [pallet_uniques, Uniques] [pallet_nfts, Nfts] [pallet_utility, Utility] diff --git a/frame/asset-rate/Cargo.toml b/frame/asset-rate/Cargo.toml new file mode 100644 index 000000000..36aabb12b --- /dev/null +++ b/frame/asset-rate/Cargo.toml @@ -0,0 +1,52 @@ +[package] +name = "pallet-asset-rate" +version = "4.0.0-dev" +description = "Whitelist non-native assets for treasury spending and provide conversion to native balance" +authors = ["William Freudenberger "] +homepage = "https://substrate.io" +edition = "2021" +license = "Apache-2.0" +repository = "https://github.com/paritytech/substrate/" +publish = false + +[package.metadata.docs.rs] +targets = ["x86_64-unknown-linux-gnu"] + +[dependencies] +codec = { package = "parity-scale-codec", version = "3.2.2", default-features = false, features = [ + "derive", +] } +scale-info = { version = "2.1.1", default-features = false, features = ["derive"] } +frame-benchmarking = { version = "4.0.0-dev", default-features = false, optional = true, path = "../benchmarking" } +frame-support = { version = "4.0.0-dev", default-features = false, path = "../support" } +frame-system = { version = "4.0.0-dev", default-features = false, path = "../system" } +sp-runtime = { version = "7.0.0", default-features = false, path = "../../primitives/runtime" } +sp-std = { version = "5.0.0", default-features = false, path = "../../primitives/std" } + +[dev-dependencies] +pallet-balances = { version = "4.0.0-dev", path = "../balances" } +sp-core = { version = "7.0.0", path = "../../primitives/core" } +sp-io = { version = "7.0.0", path = "../../primitives/io" } + +[features] +default = ["std"] +std = [ + "codec/std", + "frame-benchmarking?/std", + "frame-support/std", + "frame-system/std", + "scale-info/std", + "sp-runtime/std", + "sp-std/std", +] +runtime-benchmarks = [ + "frame-benchmarking/runtime-benchmarks", + "frame-support/runtime-benchmarks", + "frame-system/runtime-benchmarks", + "sp-runtime/runtime-benchmarks", +] +try-runtime = [ + "frame-support/try-runtime", + "frame-system/try-runtime", + "sp-runtime/try-runtime", +] diff --git a/frame/asset-rate/src/benchmarking.rs b/frame/asset-rate/src/benchmarking.rs new file mode 100644 index 000000000..dde0d764a --- /dev/null +++ b/frame/asset-rate/src/benchmarking.rs @@ -0,0 +1,86 @@ +// This file is part of Substrate. + +// Copyright (C) Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +//! The crate's benchmarks. + +use super::*; +use crate::{pallet as pallet_asset_rate, Pallet as AssetRate}; + +use frame_benchmarking::v2::*; +use frame_support::assert_ok; +use frame_system::RawOrigin; + +const ASSET_ID: u32 = 1; + +fn default_conversion_rate() -> FixedU128 { + FixedU128::from_u32(1u32) +} + +#[benchmarks(where ::AssetId: From)] +mod benchmarks { + use super::*; + + #[benchmark] + fn create() -> Result<(), BenchmarkError> { + let asset_id: T::AssetId = ASSET_ID.into(); + #[extrinsic_call] + _(RawOrigin::Root, asset_id, default_conversion_rate()); + + assert_eq!( + pallet_asset_rate::ConversionRateToNative::::get(asset_id), + Some(default_conversion_rate()) + ); + Ok(()) + } + + #[benchmark] + fn update() -> Result<(), BenchmarkError> { + let asset_id: T::AssetId = ASSET_ID.into(); + assert_ok!(AssetRate::::create( + RawOrigin::Root.into(), + asset_id, + default_conversion_rate() + )); + + #[extrinsic_call] + _(RawOrigin::Root, asset_id, FixedU128::from_u32(2)); + + assert_eq!( + pallet_asset_rate::ConversionRateToNative::::get(asset_id), + Some(FixedU128::from_u32(2)) + ); + Ok(()) + } + + #[benchmark] + fn remove() -> Result<(), BenchmarkError> { + let asset_id: T::AssetId = ASSET_ID.into(); + assert_ok!(AssetRate::::create( + RawOrigin::Root.into(), + ASSET_ID.into(), + default_conversion_rate() + )); + + #[extrinsic_call] + _(RawOrigin::Root, asset_id); + + assert!(pallet_asset_rate::ConversionRateToNative::::get(asset_id).is_none()); + Ok(()) + } + + impl_benchmark_test_suite! { AssetRate, crate::mock::new_test_ext(), crate::mock::Test } +} diff --git a/frame/asset-rate/src/lib.rs b/frame/asset-rate/src/lib.rs new file mode 100644 index 000000000..8c6597a38 --- /dev/null +++ b/frame/asset-rate/src/lib.rs @@ -0,0 +1,239 @@ +// This file is part of Substrate. + +// Copyright (C) Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +//! # Asset Rate Pallet +//! +//! - [`Config`] +//! - [`Call`] +//! +//! ## Overview +//! +//! The AssetRate pallet provides means of setting conversion rates for some asset to native +//! balance. +//! +//! The supported dispatchable functions are documented in the [`Call`] enum. +//! +//! ### Terminology +//! +//! * **Asset balance**: The balance type of an arbitrary asset. The network might only know about +//! the identifier of the asset and nothing more. +//! * **Native balance**: The balance type of the network's native currency. +//! +//! ### Goals +//! +//! The asset-rate system in Substrate is designed to make the following possible: +//! +//! * Providing a soft conversion for the balance of supported assets to a default asset class. +//! * Updating existing conversion rates. +//! +//! ## Interface +//! +//! ### Permissioned Functions +//! +//! * `create`: Creates a new asset conversion rate. +//! * `remove`: Removes an existing asset conversion rate. +//! * `update`: Overwrites an existing assert conversion rate. +//! +//! Please refer to the [`Call`] enum and its associated variants for documentation on each +//! function. +//! +//! ### Assumptions +//! +//! * Conversion rates are only used as estimates, and are not designed to be precise or closely +//! tracking real world values. +//! * All conversion rates reflect the ration of some asset to native, e.g. native = asset * rate. + +#![cfg_attr(not(feature = "std"), no_std)] + +use frame_support::traits::{ + fungible::Inspect, + tokens::{Balance, ConversionFromAssetBalance}, +}; +use sp_runtime::{traits::Zero, FixedPointNumber, FixedPointOperand, FixedU128}; + +pub use pallet::*; +pub use weights::WeightInfo; + +#[cfg(feature = "runtime-benchmarks")] +pub mod benchmarking; +#[cfg(test)] +mod mock; +#[cfg(test)] +mod tests; +pub mod weights; + +// Type alias for `frame_system`'s account id. +type AccountIdOf = ::AccountId; +// This pallet's asset id and balance type. +type AssetIdOf = ::AssetId; +// Generic fungible balance type. +type BalanceOf = <::Currency as Inspect>>::Balance; + +#[frame_support::pallet] +pub mod pallet { + use super::*; + use frame_support::pallet_prelude::*; + use frame_system::pallet_prelude::*; + + #[pallet::pallet] + pub struct Pallet(_); + + #[pallet::config] + pub trait Config: frame_system::Config { + /// The Weight information for extrinsics in this pallet. + type WeightInfo: WeightInfo; + + /// The runtime event type. + type RuntimeEvent: From> + IsType<::RuntimeEvent>; + + /// The origin permissioned to create a conversion rate for an asset. + type CreateOrigin: EnsureOrigin; + + /// The origin permissioned to remove an existing conversion rate for an asset. + type RemoveOrigin: EnsureOrigin; + + /// The origin permissioned to update an existiing conversion rate for an asset. + type UpdateOrigin: EnsureOrigin; + + /// The units in which we record balances. + type Balance: Balance + FixedPointOperand; + + /// The currency mechanism for this pallet. + type Currency: Inspect; + + /// The identifier for the class of asset. + type AssetId: frame_support::traits::tokens::AssetId; + } + + /// Maps an asset to its fixed point representation in the native balance. + /// + /// E.g. `native_amount = asset_amount * ConversionRateToNative::::get(asset_id)` + #[pallet::storage] + pub type ConversionRateToNative = + StorageMap<_, Blake2_128Concat, T::AssetId, FixedU128, OptionQuery>; + + #[pallet::event] + #[pallet::generate_deposit(pub(super) fn deposit_event)] + pub enum Event { + // Some `asset_id` conversion rate was created. + AssetRateCreated { asset_id: T::AssetId, rate: FixedU128 }, + // Some `asset_id` conversion rate was removed. + AssetRateRemoved { asset_id: T::AssetId }, + // Some existing `asset_id` conversion rate was updated from `old` to `new`. + AssetRateUpdated { asset_id: T::AssetId, old: FixedU128, new: FixedU128 }, + } + + #[pallet::error] + pub enum Error { + /// The given asset ID is unknown. + UnknownAssetId, + /// The given asset ID already has an assigned conversion rate and cannot be re-created. + AlreadyExists, + } + + #[pallet::call] + impl Pallet { + /// Initialize a conversion rate to native balance for the given asset. + /// + /// ## Complexity + /// - O(1) + #[pallet::call_index(0)] + #[pallet::weight(T::WeightInfo::create())] + pub fn create( + origin: OriginFor, + asset_id: T::AssetId, + rate: FixedU128, + ) -> DispatchResult { + T::CreateOrigin::ensure_origin(origin)?; + + ensure!( + !ConversionRateToNative::::contains_key(asset_id), + Error::::AlreadyExists + ); + ConversionRateToNative::::set(asset_id, Some(rate)); + + Self::deposit_event(Event::AssetRateCreated { asset_id, rate }); + Ok(()) + } + + /// Update the conversion rate to native balance for the given asset. + /// + /// ## Complexity + /// - O(1) + #[pallet::call_index(1)] + #[pallet::weight(T::WeightInfo::update())] + pub fn update( + origin: OriginFor, + asset_id: T::AssetId, + rate: FixedU128, + ) -> DispatchResult { + T::UpdateOrigin::ensure_origin(origin)?; + + let mut old = FixedU128::zero(); + ConversionRateToNative::::mutate(asset_id, |maybe_rate| { + if let Some(r) = maybe_rate { + old = *r; + *r = rate; + + Ok(()) + } else { + Err(Error::::UnknownAssetId) + } + })?; + + Self::deposit_event(Event::AssetRateUpdated { asset_id, old, new: rate }); + Ok(()) + } + + /// Remove an existing conversion rate to native balance for the given asset. + /// + /// ## Complexity + /// - O(1) + #[pallet::call_index(2)] + #[pallet::weight(T::WeightInfo::remove())] + pub fn remove(origin: OriginFor, asset_id: T::AssetId) -> DispatchResult { + T::RemoveOrigin::ensure_origin(origin)?; + + ensure!( + ConversionRateToNative::::contains_key(asset_id), + Error::::UnknownAssetId + ); + ConversionRateToNative::::remove(asset_id); + + Self::deposit_event(Event::AssetRateRemoved { asset_id }); + Ok(()) + } + } +} + +/// Exposes conversion of an arbitrary balance of an asset to native balance. +impl ConversionFromAssetBalance, AssetIdOf, BalanceOf> for Pallet +where + T: Config, + BalanceOf: FixedPointOperand + Zero, +{ + type Error = pallet::Error; + + fn from_asset_balance( + balance: BalanceOf, + asset_id: AssetIdOf, + ) -> Result, pallet::Error> { + let rate = pallet::ConversionRateToNative::::get(asset_id) + .ok_or(pallet::Error::::UnknownAssetId.into())?; + Ok(rate.saturating_mul_int(balance)) + } +} diff --git a/frame/asset-rate/src/mock.rs b/frame/asset-rate/src/mock.rs new file mode 100644 index 000000000..9775b7a74 --- /dev/null +++ b/frame/asset-rate/src/mock.rs @@ -0,0 +1,100 @@ +// This file is part of Substrate. + +// Copyright (C) Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +//! The crate's mock. + +use crate as pallet_asset_rate; +use frame_support::traits::{ConstU16, ConstU64}; +use sp_core::H256; +use sp_runtime::{ + testing::Header, + traits::{BlakeTwo256, IdentityLookup}, +}; + +type UncheckedExtrinsic = frame_system::mocking::MockUncheckedExtrinsic; +type Block = frame_system::mocking::MockBlock; + +frame_support::construct_runtime!( + pub enum Test where + Block = Block, + NodeBlock = Block, + UncheckedExtrinsic = UncheckedExtrinsic, + { + System: frame_system, + AssetRate: pallet_asset_rate, + Balances: pallet_balances, + } +); + +impl frame_system::Config for Test { + type BaseCallFilter = frame_support::traits::Everything; + type BlockWeights = (); + type BlockLength = (); + type DbWeight = (); + type RuntimeOrigin = RuntimeOrigin; + type RuntimeCall = RuntimeCall; + type Index = u64; + type BlockNumber = u64; + type Hash = H256; + type Hashing = BlakeTwo256; + type AccountId = u64; + type Lookup = IdentityLookup; + type Header = Header; + type RuntimeEvent = RuntimeEvent; + type BlockHashCount = ConstU64<250>; + type Version = (); + type PalletInfo = PalletInfo; + type AccountData = pallet_balances::AccountData; + type OnNewAccount = (); + type OnKilledAccount = (); + type SystemWeightInfo = (); + type SS58Prefix = ConstU16<42>; + type OnSetCode = (); + type MaxConsumers = frame_support::traits::ConstU32<16>; +} + +impl pallet_balances::Config for Test { + type Balance = u64; + type DustRemoval = (); + type RuntimeEvent = RuntimeEvent; + type ExistentialDeposit = ConstU64<1>; + type AccountStore = System; + type WeightInfo = (); + type MaxLocks = (); + type MaxReserves = (); + type ReserveIdentifier = [u8; 8]; + type HoldIdentifier = (); + type FreezeIdentifier = (); + type MaxHolds = (); + type MaxFreezes = (); +} + +impl pallet_asset_rate::Config for Test { + type WeightInfo = (); + type RuntimeEvent = RuntimeEvent; + type CreateOrigin = frame_system::EnsureRoot; + type RemoveOrigin = frame_system::EnsureRoot; + type UpdateOrigin = frame_system::EnsureRoot; + type Balance = u64; + type Currency = Balances; + type AssetId = u32; +} + +// Build genesis storage according to the mock runtime. +pub fn new_test_ext() -> sp_io::TestExternalities { + frame_system::GenesisConfig::default().build_storage::().unwrap().into() +} diff --git a/frame/asset-rate/src/tests.rs b/frame/asset-rate/src/tests.rs new file mode 100644 index 000000000..4e5a3167b --- /dev/null +++ b/frame/asset-rate/src/tests.rs @@ -0,0 +1,121 @@ +// This file is part of Substrate. + +// Copyright (C) Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +//! The crate's tests. + +use super::*; +use crate::pallet as pallet_asset_rate; +use frame_support::{assert_noop, assert_ok}; +use mock::{new_test_ext, AssetRate, RuntimeOrigin, Test}; +use sp_runtime::FixedU128; + +const ASSET_ID: u32 = 42; + +#[test] +fn create_works() { + new_test_ext().execute_with(|| { + assert!(pallet_asset_rate::ConversionRateToNative::::get(ASSET_ID).is_none()); + assert_ok!(AssetRate::create(RuntimeOrigin::root(), ASSET_ID, FixedU128::from_float(0.1))); + + assert_eq!( + pallet_asset_rate::ConversionRateToNative::::get(ASSET_ID), + Some(FixedU128::from_float(0.1)) + ); + }); +} + +#[test] +fn create_existing_throws() { + new_test_ext().execute_with(|| { + assert!(pallet_asset_rate::ConversionRateToNative::::get(ASSET_ID).is_none()); + assert_ok!(AssetRate::create(RuntimeOrigin::root(), ASSET_ID, FixedU128::from_float(0.1))); + + assert_noop!( + AssetRate::create(RuntimeOrigin::root(), ASSET_ID, FixedU128::from_float(0.1)), + Error::::AlreadyExists + ); + }); +} + +#[test] +fn remove_works() { + new_test_ext().execute_with(|| { + assert_ok!(AssetRate::create(RuntimeOrigin::root(), ASSET_ID, FixedU128::from_float(0.1))); + + assert_ok!(AssetRate::remove(RuntimeOrigin::root(), ASSET_ID,)); + assert!(pallet_asset_rate::ConversionRateToNative::::get(ASSET_ID).is_none()); + }); +} + +#[test] +fn remove_unknown_throws() { + new_test_ext().execute_with(|| { + assert_noop!( + AssetRate::remove(RuntimeOrigin::root(), ASSET_ID,), + Error::::UnknownAssetId + ); + }); +} + +#[test] +fn update_works() { + new_test_ext().execute_with(|| { + assert_ok!(AssetRate::create(RuntimeOrigin::root(), ASSET_ID, FixedU128::from_float(0.1))); + assert_ok!(AssetRate::update(RuntimeOrigin::root(), ASSET_ID, FixedU128::from_float(0.5))); + + assert_eq!( + pallet_asset_rate::ConversionRateToNative::::get(ASSET_ID), + Some(FixedU128::from_float(0.5)) + ); + }); +} + +#[test] +fn update_unknown_throws() { + new_test_ext().execute_with(|| { + assert_noop!( + AssetRate::update(RuntimeOrigin::root(), ASSET_ID, FixedU128::from_float(0.5)), + Error::::UnknownAssetId + ); + }); +} + +#[test] +fn convert_works() { + new_test_ext().execute_with(|| { + assert_ok!(AssetRate::create(RuntimeOrigin::root(), ASSET_ID, FixedU128::from_float(2.51))); + + let conversion = , + ::AssetId, + BalanceOf, + >>::from_asset_balance(10, ASSET_ID); + assert_eq!(conversion.expect("Conversion rate exists for asset"), 25); + }); +} + +#[test] +fn convert_unknown_throws() { + new_test_ext().execute_with(|| { + let conversion = , + ::AssetId, + BalanceOf, + >>::from_asset_balance(10, ASSET_ID); + assert!(conversion.is_err()); + }); +} diff --git a/frame/asset-rate/src/weights.rs b/frame/asset-rate/src/weights.rs new file mode 100644 index 000000000..4fae62634 --- /dev/null +++ b/frame/asset-rate/src/weights.rs @@ -0,0 +1,129 @@ +// This file is part of Substrate. + +// Copyright (C) Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +//! Autogenerated weights for pallet_asset_rate +//! +//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev +//! DATE: 2023-03-16, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! WORST CASE MAP SIZE: `1000000` +//! HOSTNAME: `local`, CPU: `` +//! EXECUTION: Some(Wasm), WASM-EXECUTION: Compiled, CHAIN: Some("dev"), DB CACHE: 1024 + +// Executed Command: +// ./target/production/substrate +// benchmark +// pallet +// --chain=dev +// --steps=50 +// --repeat=20 +// --pallet=pallet_asset_rate +// --extrinsic=* +// --execution=wasm +// --wasm-execution=compiled +// --heap-pages=4096 +// --output=./frame/asset-rate/src/weights.rs +// --header=./HEADER-APACHE2 +// --template=./.maintain/frame-weight-template.hbs + +#![cfg_attr(rustfmt, rustfmt_skip)] +#![allow(unused_parens)] +#![allow(unused_imports)] + +use frame_support::{traits::Get, weights::{Weight, constants::RocksDbWeight}}; +use sp_std::marker::PhantomData; + +/// Weight functions needed for pallet_asset_rate. +pub trait WeightInfo { + fn create() -> Weight; + fn update() -> Weight; + fn remove() -> Weight; +} + +/// Weights for pallet_asset_rate using the Substrate node and recommended hardware. +pub struct SubstrateWeight(PhantomData); +impl WeightInfo for SubstrateWeight { + /// Storage: AssetRate ConversionRateToNative (r:1 w:1) + /// Proof: AssetRate ConversionRateToNative (max_values: None, max_size: Some(36), added: 2511, mode: MaxEncodedLen) + fn create() -> Weight { + // Proof Size summary in bytes: + // Measured: `76` + // Estimated: `3501` + // Minimum execution time: 6_000_000 picoseconds. + Weight::from_parts(7_000_000, 3501) + .saturating_add(T::DbWeight::get().reads(1_u64)) + .saturating_add(T::DbWeight::get().writes(1_u64)) + } + /// Storage: AssetRate ConversionRateToNative (r:1 w:1) + /// Proof: AssetRate ConversionRateToNative (max_values: None, max_size: Some(36), added: 2511, mode: MaxEncodedLen) + fn update() -> Weight { + // Proof Size summary in bytes: + // Measured: `137` + // Estimated: `3501` + // Minimum execution time: 7_000_000 picoseconds. + Weight::from_parts(8_000_000, 3501) + .saturating_add(T::DbWeight::get().reads(1_u64)) + .saturating_add(T::DbWeight::get().writes(1_u64)) + } + /// Storage: AssetRate ConversionRateToNative (r:1 w:1) + /// Proof: AssetRate ConversionRateToNative (max_values: None, max_size: Some(36), added: 2511, mode: MaxEncodedLen) + fn remove() -> Weight { + // Proof Size summary in bytes: + // Measured: `137` + // Estimated: `3501` + // Minimum execution time: 7_000_000 picoseconds. + Weight::from_parts(8_000_000, 3501) + .saturating_add(T::DbWeight::get().reads(1_u64)) + .saturating_add(T::DbWeight::get().writes(1_u64)) + } +} + +// For backwards compatibility and tests +impl WeightInfo for () { + /// Storage: AssetRate ConversionRateToNative (r:1 w:1) + /// Proof: AssetRate ConversionRateToNative (max_values: None, max_size: Some(36), added: 2511, mode: MaxEncodedLen) + fn create() -> Weight { + // Proof Size summary in bytes: + // Measured: `76` + // Estimated: `3501` + // Minimum execution time: 6_000_000 picoseconds. + Weight::from_parts(7_000_000, 3501) + .saturating_add(RocksDbWeight::get().reads(1_u64)) + .saturating_add(RocksDbWeight::get().writes(1_u64)) + } + /// Storage: AssetRate ConversionRateToNative (r:1 w:1) + /// Proof: AssetRate ConversionRateToNative (max_values: None, max_size: Some(36), added: 2511, mode: MaxEncodedLen) + fn update() -> Weight { + // Proof Size summary in bytes: + // Measured: `137` + // Estimated: `3501` + // Minimum execution time: 7_000_000 picoseconds. + Weight::from_parts(8_000_000, 3501) + .saturating_add(RocksDbWeight::get().reads(1_u64)) + .saturating_add(RocksDbWeight::get().writes(1_u64)) + } + /// Storage: AssetRate ConversionRateToNative (r:1 w:1) + /// Proof: AssetRate ConversionRateToNative (max_values: None, max_size: Some(36), added: 2511, mode: MaxEncodedLen) + fn remove() -> Weight { + // Proof Size summary in bytes: + // Measured: `137` + // Estimated: `3501` + // Minimum execution time: 7_000_000 picoseconds. + Weight::from_parts(8_000_000, 3501) + .saturating_add(RocksDbWeight::get().reads(1_u64)) + .saturating_add(RocksDbWeight::get().writes(1_u64)) + } +} From 1fbf283120446b229ba2cc0da9e175f184949891 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bastian=20K=C3=B6cher?= Date: Wed, 19 Apr 2023 10:15:57 +0200 Subject: [PATCH 393/558] call_executor: Remove code deduplication (#13948) --- client/service/src/client/call_executor.rs | 34 +++++++++++++--------- 1 file changed, 20 insertions(+), 14 deletions(-) diff --git a/client/service/src/client/call_executor.rs b/client/service/src/client/call_executor.rs index 7979c139e..7f83d6287 100644 --- a/client/service/src/client/call_executor.rs +++ b/client/service/src/client/call_executor.rs @@ -81,13 +81,14 @@ where fn check_override<'a>( &'a self, onchain_code: RuntimeCode<'a>, - hash: ::Hash, + state: &B::State, + hash: Block::Hash, ) -> sp_blockchain::Result<(RuntimeCode<'a>, RuntimeVersion)> where Block: BlockT, B: backend::Backend, { - let on_chain_version = self.on_chain_runtime_version(hash)?; + let on_chain_version = self.on_chain_runtime_version(&onchain_code, state)?; let code_and_version = if let Some(d) = self.wasm_override.as_ref().as_ref().and_then(|o| { o.get( &on_chain_version.spec_version, @@ -115,17 +116,18 @@ where } /// Returns the on chain runtime version. - fn on_chain_runtime_version(&self, hash: Block::Hash) -> sp_blockchain::Result { + fn on_chain_runtime_version( + &self, + code: &RuntimeCode, + state: &B::State, + ) -> sp_blockchain::Result { let mut overlay = OverlayedChanges::default(); - let state = self.backend.state_at(hash)?; let mut cache = StorageTransactionCache::::default(); - let mut ext = Ext::new(&mut overlay, &mut cache, &state, None); - let state_runtime_code = sp_state_machine::backend::BackendRuntimeCode::new(&state); - let runtime_code = - state_runtime_code.runtime_code().map_err(sp_blockchain::Error::RuntimeCode)?; + let mut ext = Ext::new(&mut overlay, &mut cache, state, None); + self.executor - .runtime_version(&mut ext, &runtime_code) + .runtime_version(&mut ext, code) .map_err(|e| sp_blockchain::Error::VersionInvalid(e.to_string())) } } @@ -176,7 +178,7 @@ where let runtime_code = state_runtime_code.runtime_code().map_err(sp_blockchain::Error::RuntimeCode)?; - let runtime_code = self.check_override(runtime_code, at_hash)?.0; + let runtime_code = self.check_override(runtime_code, &state, at_hash)?.0; let extensions = self.execution_extensions.extensions( at_hash, @@ -233,7 +235,7 @@ where let runtime_code = state_runtime_code.runtime_code().map_err(sp_blockchain::Error::RuntimeCode)?; - let runtime_code = self.check_override(runtime_code, at_hash)?.0; + let runtime_code = self.check_override(runtime_code, &state, at_hash)?.0; match recorder { Some(recorder) => { @@ -282,7 +284,7 @@ where let runtime_code = state_runtime_code.runtime_code().map_err(sp_blockchain::Error::RuntimeCode)?; - self.check_override(runtime_code, at_hash).map(|(_, v)| v) + self.check_override(runtime_code, &state, at_hash).map(|(_, v)| v) } fn prove_execution( @@ -300,7 +302,7 @@ where let state_runtime_code = sp_state_machine::backend::BackendRuntimeCode::new(trie_backend); let runtime_code = state_runtime_code.runtime_code().map_err(sp_blockchain::Error::RuntimeCode)?; - let runtime_code = self.check_override(runtime_code, at_hash)?.0; + let runtime_code = self.check_override(runtime_code, &state, at_hash)?.0; sp_state_machine::prove_execution_on_trie_backend( trie_backend, @@ -436,7 +438,11 @@ mod tests { }; let check = call_executor - .check_override(onchain_code, backend.blockchain().info().genesis_hash) + .check_override( + onchain_code, + &backend.state_at(backend.blockchain().info().genesis_hash).unwrap(), + backend.blockchain().info().genesis_hash, + ) .expect("RuntimeCode override") .0; From d4802eb1ac31e574a7bcfa5afe63b8ae92e34d81 Mon Sep 17 00:00:00 2001 From: Mike Ruje Date: Wed, 19 Apr 2023 11:03:56 +0200 Subject: [PATCH 394/558] Improve has good jugement (#13952) * improve run_benchmark * Revert "improve run_benchmark" This reverts commit 4d9cf7a63e37fedca376d692f1461486d3dca659. * improve has_good_judgement * Update bin/node/runtime/src/impls.rs * ".git/.scripts/commands/fmt/fmt.sh" --------- Co-authored-by: Oliver Tale-Yazdi Co-authored-by: command-bot <> --- bin/node/runtime/src/impls.rs | 16 +++++++--------- 1 file changed, 7 insertions(+), 9 deletions(-) diff --git a/bin/node/runtime/src/impls.rs b/bin/node/runtime/src/impls.rs index 033549549..05531f47c 100644 --- a/bin/node/runtime/src/impls.rs +++ b/bin/node/runtime/src/impls.rs @@ -61,15 +61,13 @@ impl IdentityVerifier for AllianceIdentityVerifier { fn has_good_judgement(who: &AccountId) -> bool { use pallet_identity::Judgement; - if let Some(judgements) = - crate::Identity::identity(who).map(|registration| registration.judgements) - { - judgements - .iter() - .any(|(_, j)| matches!(j, Judgement::KnownGood | Judgement::Reasonable)) - } else { - false - } + crate::Identity::identity(who) + .map(|registration| registration.judgements) + .map_or(false, |judgements| { + judgements + .iter() + .any(|(_, j)| matches!(j, Judgement::KnownGood | Judgement::Reasonable)) + }) } fn super_account_id(who: &AccountId) -> Option { From 37f3d588423cbd295f69a24cae31f2a93936cc93 Mon Sep 17 00:00:00 2001 From: Davide Galassi Date: Wed, 19 Apr 2023 11:11:47 +0200 Subject: [PATCH 395/558] VRF refactory (#13889) * First iteration to encapsulate schnorrkel and merlin usage * Remove schnorkel direct dependency from BABE pallet * Remove schnorrkel direct dependency from BABE client * Trivial renaming for VrfTranscript data and value * Better errors * Expose a function to get a schnorrkel friendly transcript * Keep the vrf signature stuff together (preventing some clones around) * Fix tests * Remove vrf agnostic transcript and define it as an associated type for VrfSigner and VrfVerifier * Fix babe pallet mock * Inner types are required to be public for polkadot * Update client/consensus/babe/src/verification.rs Co-authored-by: Koute * Nit * Remove Deref implementations * make_bytes as a method * Trigger CI --------- Co-authored-by: Koute --- Cargo.lock | 20 -- Cargo.toml | 1 - client/consensus/babe/Cargo.toml | 3 - client/consensus/babe/src/authorship.rs | 65 +++--- client/consensus/babe/src/lib.rs | 21 +- client/consensus/babe/src/migration.rs | 4 +- client/consensus/babe/src/tests.rs | 67 ++---- client/consensus/babe/src/verification.rs | 84 ++++---- client/keystore/src/local.rs | 32 +-- frame/babe/Cargo.toml | 4 +- frame/babe/src/lib.rs | 66 +++--- frame/babe/src/mock.rs | 41 ++-- frame/babe/src/randomness.rs | 8 +- frame/babe/src/tests.rs | 22 +- primitives/consensus/babe/Cargo.toml | 4 - primitives/consensus/babe/src/digests.rs | 28 ++- primitives/consensus/babe/src/lib.rs | 48 ++--- primitives/consensus/vrf/Cargo.toml | 32 --- primitives/consensus/vrf/README.md | 3 - primitives/consensus/vrf/src/lib.rs | 21 -- primitives/consensus/vrf/src/schnorrkel.rs | 188 ----------------- primitives/core/Cargo.toml | 8 +- primitives/core/src/crypto.rs | 26 ++- primitives/core/src/sr25519.rs | 231 ++++++++++++++++++--- primitives/keystore/Cargo.toml | 4 - primitives/keystore/src/lib.rs | 7 +- primitives/keystore/src/testing.rs | 48 +++-- primitives/keystore/src/vrf.rs | 94 --------- 28 files changed, 468 insertions(+), 712 deletions(-) delete mode 100644 primitives/consensus/vrf/Cargo.toml delete mode 100644 primitives/consensus/vrf/README.md delete mode 100644 primitives/consensus/vrf/src/lib.rs delete mode 100644 primitives/consensus/vrf/src/schnorrkel.rs delete mode 100644 primitives/keystore/src/vrf.rs diff --git a/Cargo.lock b/Cargo.lock index c0920e4dc..26fb7c4ae 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -5695,7 +5695,6 @@ dependencies = [ "scale-info", "sp-application-crypto", "sp-consensus-babe", - "sp-consensus-vrf", "sp-core", "sp-io", "sp-runtime", @@ -8594,7 +8593,6 @@ dependencies = [ "fork-tree", "futures", "log", - "merlin", "num-bigint", "num-rational", "num-traits", @@ -8611,7 +8609,6 @@ dependencies = [ "sc-network-test", "sc-telemetry", "scale-info", - "schnorrkel", "sp-api", "sp-application-crypto", "sp-block-builder", @@ -8619,7 +8616,6 @@ dependencies = [ "sp-consensus", "sp-consensus-babe", "sp-consensus-slots", - "sp-consensus-vrf", "sp-core", "sp-inherents", "sp-keyring", @@ -10361,7 +10357,6 @@ name = "sp-consensus-babe" version = "0.10.0-dev" dependencies = [ "async-trait", - "merlin", "parity-scale-codec", "scale-info", "serde", @@ -10369,7 +10364,6 @@ dependencies = [ "sp-application-crypto", "sp-consensus", "sp-consensus-slots", - "sp-consensus-vrf", "sp-core", "sp-inherents", "sp-keystore", @@ -10437,18 +10431,6 @@ dependencies = [ "sp-timestamp", ] -[[package]] -name = "sp-consensus-vrf" -version = "0.10.0-dev" -dependencies = [ - "parity-scale-codec", - "scale-info", - "schnorrkel", - "sp-core", - "sp-runtime", - "sp-std", -] - [[package]] name = "sp-core" version = "7.0.0" @@ -10600,12 +10582,10 @@ name = "sp-keystore" version = "0.13.0" dependencies = [ "futures", - "merlin", "parity-scale-codec", "parking_lot 0.12.1", "rand 0.7.3", "rand_chacha 0.2.2", - "schnorrkel", "serde", "sp-core", "sp-externalities", diff --git a/Cargo.toml b/Cargo.toml index ae89750dd..507d76210 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -190,7 +190,6 @@ members = [ "primitives/consensus/grandpa", "primitives/consensus/pow", "primitives/consensus/slots", - "primitives/consensus/vrf", "primitives/core", "primitives/core/hashing", "primitives/core/hashing/proc-macro", diff --git a/client/consensus/babe/Cargo.toml b/client/consensus/babe/Cargo.toml index d10ef21c4..2382d064d 100644 --- a/client/consensus/babe/Cargo.toml +++ b/client/consensus/babe/Cargo.toml @@ -19,12 +19,10 @@ scale-info = { version = "2.5.0", features = ["derive"] } codec = { package = "parity-scale-codec", version = "3.2.2", features = ["derive"] } futures = "0.3.21" log = "0.4.17" -merlin = "2.0" num-bigint = "0.4.3" num-rational = "0.4.1" num-traits = "0.2.8" parking_lot = "0.12.1" -schnorrkel = { version = "0.9.1", features = ["preaudit_deprecated"] } thiserror = "1.0" fork-tree = { version = "3.0.0", path = "../../../utils/fork-tree" } prometheus-endpoint = { package = "substrate-prometheus-endpoint", version = "0.10.0-dev", path = "../../../utils/prometheus" } @@ -41,7 +39,6 @@ sp-blockchain = { version = "4.0.0-dev", path = "../../../primitives/blockchain" sp-consensus = { version = "0.10.0-dev", path = "../../../primitives/consensus/common" } sp-consensus-babe = { version = "0.10.0-dev", path = "../../../primitives/consensus/babe" } sp-consensus-slots = { version = "0.10.0-dev", path = "../../../primitives/consensus/slots" } -sp-consensus-vrf = { version = "0.10.0-dev", path = "../../../primitives/consensus/vrf" } sp-core = { version = "7.0.0", path = "../../../primitives/core" } sp-inherents = { version = "4.0.0-dev", path = "../../../primitives/inherents" } sp-keystore = { version = "0.13.0", path = "../../../primitives/keystore" } diff --git a/client/consensus/babe/src/authorship.rs b/client/consensus/babe/src/authorship.rs index 3ff4c393f..dc7cd1086 100644 --- a/client/consensus/babe/src/authorship.rs +++ b/client/consensus/babe/src/authorship.rs @@ -18,17 +18,19 @@ //! BABE authority selection and slot claiming. -use super::Epoch; +use super::{Epoch, AUTHORING_SCORE_LENGTH, AUTHORING_SCORE_VRF_CONTEXT}; use codec::Encode; use sc_consensus_epochs::Epoch as EpochT; -use schnorrkel::{keys::PublicKey, vrf::VRFInOut}; use sp_application_crypto::AppCrypto; use sp_consensus_babe::{ digests::{PreDigest, PrimaryPreDigest, SecondaryPlainPreDigest, SecondaryVRFPreDigest}, - make_transcript, make_transcript_data, AuthorityId, BabeAuthorityWeight, Slot, BABE_VRF_PREFIX, + make_transcript, AuthorityId, BabeAuthorityWeight, Randomness, Slot, +}; +use sp_core::{ + blake2_256, + crypto::{ByteArray, Wraps}, + U256, }; -use sp_consensus_vrf::schnorrkel::{VRFOutput, VRFProof}; -use sp_core::{blake2_256, crypto::ByteArray, U256}; use sp_keystore::KeystorePtr; /// Calculates the primary selection threshold for a given authority, taking @@ -95,19 +97,13 @@ pub(super) fn calculate_primary_threshold( ) } -/// Returns true if the given VRF output is lower than the given threshold, -/// false otherwise. -pub(super) fn check_primary_threshold(inout: &VRFInOut, threshold: u128) -> bool { - u128::from_le_bytes(inout.make_bytes::<[u8; 16]>(BABE_VRF_PREFIX)) < threshold -} - /// Get the expected secondary author for the given slot and with given /// authorities. This should always assign the slot to some authority unless the /// authorities list is empty. pub(super) fn secondary_slot_author( slot: Slot, authorities: &[(AuthorityId, BabeAuthorityWeight)], - randomness: [u8; 32], + randomness: Randomness, ) -> Option<&AuthorityId> { if authorities.is_empty() { return None @@ -152,18 +148,14 @@ fn claim_secondary_slot( for (authority_id, authority_index) in keys { if authority_id == expected_author { let pre_digest = if author_secondary_vrf { - let transcript_data = make_transcript_data(randomness, slot, epoch_index); - let result = keystore.sr25519_vrf_sign( - AuthorityId::ID, - authority_id.as_ref(), - transcript_data, - ); - if let Ok(Some(signature)) = result { + let transcript = make_transcript(randomness, slot, epoch_index); + let result = + keystore.sr25519_vrf_sign(AuthorityId::ID, authority_id.as_ref(), &transcript); + if let Ok(Some(vrf_signature)) = result { Some(PreDigest::SecondaryVRF(SecondaryVRFPreDigest { slot, - vrf_output: VRFOutput(signature.output), - vrf_proof: VRFProof(signature.proof), authority_index: *authority_index as u32, + vrf_signature, })) } else { None @@ -247,25 +239,28 @@ fn claim_primary_slot( epoch_index = epoch.clone_for_slot(slot).epoch_index; } - for (authority_id, authority_index) in keys { - let transcript = make_transcript(randomness, slot, epoch_index); - let transcript_data = make_transcript_data(randomness, slot, epoch_index); - let result = - keystore.sr25519_vrf_sign(AuthorityId::ID, authority_id.as_ref(), transcript_data); - if let Ok(Some(signature)) = result { - let public = PublicKey::from_bytes(&authority_id.to_raw_vec()).ok()?; - let inout = match signature.output.attach_input_hash(&public, transcript) { - Ok(inout) => inout, - Err(_) => continue, - }; + let transcript = make_transcript(randomness, slot, epoch_index); + for (authority_id, authority_index) in keys { + let result = keystore.sr25519_vrf_sign(AuthorityId::ID, authority_id.as_ref(), &transcript); + if let Ok(Some(vrf_signature)) = result { let threshold = calculate_primary_threshold(c, authorities, *authority_index); - if check_primary_threshold(&inout, threshold) { + + let can_claim = authority_id + .as_inner_ref() + .make_bytes::<[u8; AUTHORING_SCORE_LENGTH]>( + AUTHORING_SCORE_VRF_CONTEXT, + &transcript, + &vrf_signature.output, + ) + .map(|bytes| u128::from_le_bytes(bytes) < threshold) + .unwrap_or_default(); + + if can_claim { let pre_digest = PreDigest::Primary(PrimaryPreDigest { slot, - vrf_output: VRFOutput(signature.output), - vrf_proof: VRFProof(signature.proof), authority_index: *authority_index as u32, + vrf_signature, }); return Some((pre_digest, authority_id.clone())) diff --git a/client/consensus/babe/src/lib.rs b/client/consensus/babe/src/lib.rs index 6327c8c65..219b52294 100644 --- a/client/consensus/babe/src/lib.rs +++ b/client/consensus/babe/src/lib.rs @@ -86,7 +86,6 @@ use futures::{ use log::{debug, info, log, trace, warn}; use parking_lot::Mutex; use prometheus_endpoint::Registry; -use schnorrkel::SignatureError; use sc_client_api::{ backend::AuxStore, AuxDataOperations, Backend as BackendT, FinalityNotification, @@ -134,7 +133,7 @@ pub use sp_consensus_babe::{ PrimaryPreDigest, SecondaryPlainPreDigest, }, AuthorityId, AuthorityPair, AuthoritySignature, BabeApi, BabeAuthorityWeight, BabeBlockWeight, - BabeConfiguration, BabeEpochConfiguration, ConsensusLog, BABE_ENGINE_ID, VRF_OUTPUT_LENGTH, + BabeConfiguration, BabeEpochConfiguration, ConsensusLog, Randomness, BABE_ENGINE_ID, }; pub use aux_schema::load_block_weight as block_weight; @@ -149,6 +148,12 @@ mod tests; const LOG_TARGET: &str = "babe"; +/// VRF context used for slots claiming lottery. +const AUTHORING_SCORE_VRF_CONTEXT: &[u8] = b"substrate-babe-vrf"; + +/// VRF output length for slots claiming lottery. +const AUTHORING_SCORE_LENGTH: usize = 16; + /// BABE epoch information #[derive(Decode, Encode, PartialEq, Eq, Clone, Debug, scale_info::TypeInfo)] pub struct Epoch { @@ -161,7 +166,7 @@ pub struct Epoch { /// The authorities and their weights. pub authorities: Vec<(AuthorityId, BabeAuthorityWeight)>, /// Randomness for this epoch. - pub randomness: [u8; VRF_OUTPUT_LENGTH], + pub randomness: Randomness, /// Configuration of the epoch. pub config: BabeEpochConfiguration, } @@ -308,12 +313,12 @@ pub enum Error { /// No secondary author expected. #[error("No secondary author expected.")] NoSecondaryAuthorExpected, - /// VRF verification of block by author failed - #[error("VRF verification of block by author {0:?} failed: threshold {1} exceeded")] - VRFVerificationOfBlockFailed(AuthorityId, u128), /// VRF verification failed - #[error("VRF verification failed: {0:?}")] - VRFVerificationFailed(SignatureError), + #[error("VRF verification failed")] + VrfVerificationFailed, + /// Primary slot threshold too low + #[error("VRF output rejected, threshold {0} exceeded")] + VrfThresholdExceeded(u128), /// Could not fetch parent header #[error("Could not fetch parent header: {0}")] FetchParentHeader(sp_blockchain::Error), diff --git a/client/consensus/babe/src/migration.rs b/client/consensus/babe/src/migration.rs index ec864b8e5..2b1396c41 100644 --- a/client/consensus/babe/src/migration.rs +++ b/client/consensus/babe/src/migration.rs @@ -18,7 +18,7 @@ use crate::{ AuthorityId, BabeAuthorityWeight, BabeConfiguration, BabeEpochConfiguration, Epoch, - NextEpochDescriptor, VRF_OUTPUT_LENGTH, + NextEpochDescriptor, Randomness, }; use codec::{Decode, Encode}; use sc_consensus_epochs::Epoch as EpochT; @@ -36,7 +36,7 @@ pub struct EpochV0 { /// The authorities and their weights. pub authorities: Vec<(AuthorityId, BabeAuthorityWeight)>, /// Randomness for this epoch. - pub randomness: [u8; VRF_OUTPUT_LENGTH], + pub randomness: Randomness, } impl EpochT for EpochV0 { diff --git a/client/consensus/babe/src/tests.rs b/client/consensus/babe/src/tests.rs index 7f8448d91..740ce6367 100644 --- a/client/consensus/babe/src/tests.rs +++ b/client/consensus/babe/src/tests.rs @@ -20,10 +20,6 @@ use super::*; use authorship::claim_slot; -use rand_chacha::{ - rand_core::{RngCore, SeedableRng}, - ChaChaRng, -}; use sc_block_builder::{BlockBuilder, BlockBuilderProvider}; use sc_client_api::{backend::TransactionFor, BlockchainEvents, Finalizer}; use sc_consensus::{BoxBlockImport, BoxJustificationImport}; @@ -33,16 +29,13 @@ use sc_network_test::{Block as TestBlock, *}; use sp_application_crypto::key_types::BABE; use sp_consensus::{DisableProofRecording, NoNetwork as DummyOracle, Proposal}; use sp_consensus_babe::{ - inherents::InherentDataProvider, make_transcript, make_transcript_data, AllowedSlots, - AuthorityId, AuthorityPair, Slot, + inherents::InherentDataProvider, make_transcript, AllowedSlots, AuthorityId, AuthorityPair, + Slot, }; use sp_consensus_slots::SlotDuration; -use sp_consensus_vrf::schnorrkel::VRFOutput; use sp_core::crypto::Pair; use sp_keyring::Sr25519Keyring; -use sp_keystore::{ - testing::MemoryKeystore, vrf::make_transcript as transcript_from_data, Keystore, -}; +use sp_keystore::{testing::MemoryKeystore, Keystore}; use sp_runtime::{ generic::{Digest, DigestItem}, traits::Block as BlockT, @@ -637,24 +630,24 @@ fn claim_vrf_check() { PreDigest::Primary(d) => d, v => panic!("Unexpected pre-digest variant {:?}", v), }; - let transcript = make_transcript_data(&epoch.randomness.clone(), 0.into(), epoch.epoch_index); + let transcript = make_transcript(&epoch.randomness.clone(), 0.into(), epoch.epoch_index); let sign = keystore - .sr25519_vrf_sign(AuthorityId::ID, &public, transcript) + .sr25519_vrf_sign(AuthorityId::ID, &public, &transcript) .unwrap() .unwrap(); - assert_eq!(pre_digest.vrf_output, VRFOutput(sign.output)); + assert_eq!(pre_digest.vrf_signature.output, sign.output); // We expect a SecondaryVRF claim for slot 1 let pre_digest = match claim_slot(1.into(), &epoch, &keystore).unwrap().0 { PreDigest::SecondaryVRF(d) => d, v => panic!("Unexpected pre-digest variant {:?}", v), }; - let transcript = make_transcript_data(&epoch.randomness.clone(), 1.into(), epoch.epoch_index); + let transcript = make_transcript(&epoch.randomness.clone(), 1.into(), epoch.epoch_index); let sign = keystore - .sr25519_vrf_sign(AuthorityId::ID, &public, transcript) + .sr25519_vrf_sign(AuthorityId::ID, &public, &transcript) .unwrap() .unwrap(); - assert_eq!(pre_digest.vrf_output, VRFOutput(sign.output)); + assert_eq!(pre_digest.vrf_signature.output, sign.output); // Check that correct epoch index has been used if epochs are skipped (primary VRF) let slot = Slot::from(103); @@ -663,13 +656,13 @@ fn claim_vrf_check() { v => panic!("Unexpected claim variant {:?}", v), }; let fixed_epoch = epoch.clone_for_slot(slot); - let transcript = make_transcript_data(&epoch.randomness.clone(), slot, fixed_epoch.epoch_index); + let transcript = make_transcript(&epoch.randomness.clone(), slot, fixed_epoch.epoch_index); let sign = keystore - .sr25519_vrf_sign(AuthorityId::ID, &public, transcript) + .sr25519_vrf_sign(AuthorityId::ID, &public, &transcript) .unwrap() .unwrap(); assert_eq!(fixed_epoch.epoch_index, 11); - assert_eq!(claim.vrf_output, VRFOutput(sign.output)); + assert_eq!(claim.vrf_signature.output, sign.output); // Check that correct epoch index has been used if epochs are skipped (secondary VRF) let slot = Slot::from(100); @@ -678,13 +671,13 @@ fn claim_vrf_check() { v => panic!("Unexpected claim variant {:?}", v), }; let fixed_epoch = epoch.clone_for_slot(slot); - let transcript = make_transcript_data(&epoch.randomness.clone(), slot, fixed_epoch.epoch_index); + let transcript = make_transcript(&epoch.randomness.clone(), slot, fixed_epoch.epoch_index); let sign = keystore - .sr25519_vrf_sign(AuthorityId::ID, &public, transcript) + .sr25519_vrf_sign(AuthorityId::ID, &public, &transcript) .unwrap() .unwrap(); assert_eq!(fixed_epoch.epoch_index, 11); - assert_eq!(pre_digest.vrf_output, VRFOutput(sign.output)); + assert_eq!(pre_digest.vrf_signature.output, sign.output); } // Propose and import a new BABE block on top of the given parent. @@ -1084,36 +1077,6 @@ async fn verify_slots_are_strictly_increasing() { propose_and_import_block(&b1, Some(999.into()), &mut proposer_factory, &mut block_import).await; } -#[test] -fn babe_transcript_generation_match() { - sp_tracing::try_init_simple(); - - let authority = Sr25519Keyring::Alice; - let _keystore = create_keystore(authority); - - let epoch = Epoch { - start_slot: 0.into(), - authorities: vec![(authority.public().into(), 1)], - randomness: [0; 32], - epoch_index: 1, - duration: 100, - config: BabeEpochConfiguration { - c: (3, 10), - allowed_slots: AllowedSlots::PrimaryAndSecondaryPlainSlots, - }, - }; - - let orig_transcript = make_transcript(&epoch.randomness.clone(), 1.into(), epoch.epoch_index); - let new_transcript = make_transcript_data(&epoch.randomness, 1.into(), epoch.epoch_index); - - let test = |t: merlin::Transcript| -> [u8; 16] { - let mut b = [0u8; 16]; - t.build_rng().finalize(&mut ChaChaRng::from_seed([0u8; 32])).fill_bytes(&mut b); - b - }; - debug_assert!(test(orig_transcript) == test(transcript_from_data(new_transcript))); -} - #[tokio::test] async fn obsolete_blocks_aux_data_cleanup() { let mut net = BabeTestNet::new(1); diff --git a/client/consensus/babe/src/verification.rs b/client/consensus/babe/src/verification.rs index 96d3961d4..cadceb6a5 100644 --- a/client/consensus/babe/src/verification.rs +++ b/client/consensus/babe/src/verification.rs @@ -18,8 +18,9 @@ //! Verification for BABE headers. use crate::{ - authorship::{calculate_primary_threshold, check_primary_threshold, secondary_slot_author}, - babe_err, find_pre_digest, BlockT, Epoch, Error, LOG_TARGET, + authorship::{calculate_primary_threshold, secondary_slot_author}, + babe_err, find_pre_digest, BlockT, Epoch, Error, AUTHORING_SCORE_LENGTH, + AUTHORING_SCORE_VRF_CONTEXT, LOG_TARGET, }; use log::{debug, trace}; use sc_consensus_epochs::Epoch as EpochT; @@ -32,7 +33,10 @@ use sp_consensus_babe::{ make_transcript, AuthorityId, AuthorityPair, AuthoritySignature, }; use sp_consensus_slots::Slot; -use sp_core::{ByteArray, Pair}; +use sp_core::{ + crypto::{VrfVerifier, Wraps}, + Pair, +}; use sp_runtime::{traits::Header, DigestItem}; /// BABE verification parameters @@ -155,7 +159,7 @@ fn check_primary_header( epoch: &Epoch, c: (u64, u64), ) -> Result<(), Error> { - let author = &epoch.authorities[pre_digest.authority_index as usize].0; + let authority_id = &epoch.authorities[pre_digest.authority_index as usize].0; let mut epoch_index = epoch.epoch_index; if epoch.end_slot() <= pre_digest.slot { @@ -163,28 +167,34 @@ fn check_primary_header( epoch_index = epoch.clone_for_slot(pre_digest.slot).epoch_index; } - if AuthorityPair::verify(&signature, pre_hash, author) { - let (inout, _) = { - let transcript = make_transcript(&epoch.randomness, pre_digest.slot, epoch_index); - - schnorrkel::PublicKey::from_bytes(author.as_slice()) - .and_then(|p| { - p.vrf_verify(transcript, &pre_digest.vrf_output, &pre_digest.vrf_proof) - }) - .map_err(|s| babe_err(Error::VRFVerificationFailed(s)))? - }; + if !AuthorityPair::verify(&signature, pre_hash, authority_id) { + return Err(babe_err(Error::BadSignature(pre_hash))) + } - let threshold = - calculate_primary_threshold(c, &epoch.authorities, pre_digest.authority_index as usize); + let transcript = make_transcript(&epoch.randomness, pre_digest.slot, epoch_index); - if !check_primary_threshold(&inout, threshold) { - return Err(babe_err(Error::VRFVerificationOfBlockFailed(author.clone(), threshold))) - } + if !authority_id.as_inner_ref().vrf_verify(&transcript, &pre_digest.vrf_signature) { + return Err(babe_err(Error::VrfVerificationFailed)) + } - Ok(()) - } else { - Err(babe_err(Error::BadSignature(pre_hash))) + let threshold = + calculate_primary_threshold(c, &epoch.authorities, pre_digest.authority_index as usize); + + let score = authority_id + .as_inner_ref() + .make_bytes::<[u8; AUTHORING_SCORE_LENGTH]>( + AUTHORING_SCORE_VRF_CONTEXT, + &transcript, + &pre_digest.vrf_signature.output, + ) + .map(u128::from_le_bytes) + .map_err(|_| babe_err(Error::VrfVerificationFailed))?; + + if score >= threshold { + return Err(babe_err(Error::VrfThresholdExceeded(threshold))) } + + Ok(()) } /// Check a secondary slot proposal header. We validate that the given header is @@ -197,8 +207,7 @@ fn check_secondary_plain_header( signature: AuthoritySignature, epoch: &Epoch, ) -> Result<(), Error> { - // check the signature is valid under the expected authority and - // chain state. + // check the signature is valid under the expected authority and chain state. let expected_author = secondary_slot_author(pre_digest.slot, &epoch.authorities, epoch.randomness) .ok_or(Error::NoSecondaryAuthorExpected)?; @@ -209,11 +218,11 @@ fn check_secondary_plain_header( return Err(Error::InvalidAuthor(expected_author.clone(), author.clone())) } - if AuthorityPair::verify(&signature, pre_hash.as_ref(), author) { - Ok(()) - } else { - Err(Error::BadSignature(pre_hash)) + if !AuthorityPair::verify(&signature, pre_hash.as_ref(), author) { + return Err(Error::BadSignature(pre_hash)) } + + Ok(()) } /// Check a secondary VRF slot proposal header. @@ -223,8 +232,7 @@ fn check_secondary_vrf_header( signature: AuthoritySignature, epoch: &Epoch, ) -> Result<(), Error> { - // check the signature is valid under the expected authority and - // chain state. + // check the signature is valid under the expected authority and chain state. let expected_author = secondary_slot_author(pre_digest.slot, &epoch.authorities, epoch.randomness) .ok_or(Error::NoSecondaryAuthorExpected)?; @@ -241,15 +249,15 @@ fn check_secondary_vrf_header( epoch_index = epoch.clone_for_slot(pre_digest.slot).epoch_index; } - if AuthorityPair::verify(&signature, pre_hash.as_ref(), author) { - let transcript = make_transcript(&epoch.randomness, pre_digest.slot, epoch_index); + if !AuthorityPair::verify(&signature, pre_hash.as_ref(), author) { + return Err(Error::BadSignature(pre_hash)) + } - schnorrkel::PublicKey::from_bytes(author.as_slice()) - .and_then(|p| p.vrf_verify(transcript, &pre_digest.vrf_output, &pre_digest.vrf_proof)) - .map_err(|s| babe_err(Error::VRFVerificationFailed(s)))?; + let transcript = make_transcript(&epoch.randomness, pre_digest.slot, epoch_index); - Ok(()) - } else { - Err(Error::BadSignature(pre_hash)) + if !author.as_inner_ref().vrf_verify(&transcript, &pre_digest.vrf_signature) { + return Err(Error::VrfVerificationFailed) } + + Ok(()) } diff --git a/client/keystore/src/local.rs b/client/keystore/src/local.rs index 2486b2fd0..3551623f3 100644 --- a/client/keystore/src/local.rs +++ b/client/keystore/src/local.rs @@ -20,13 +20,10 @@ use parking_lot::RwLock; use sp_application_crypto::{AppCrypto, AppPair, IsWrappedBy}; use sp_core::{ - crypto::{ByteArray, ExposeSecret, KeyTypeId, Pair as CorePair, SecretString}, + crypto::{ByteArray, ExposeSecret, KeyTypeId, Pair as CorePair, SecretString, VrfSigner}, ecdsa, ed25519, sr25519, }; -use sp_keystore::{ - vrf::{make_transcript, VRFSignature, VRFTranscriptData}, - Error as TraitError, Keystore, KeystorePtr, -}; +use sp_keystore::{Error as TraitError, Keystore, KeystorePtr}; use std::{ collections::HashMap, fs::{self, File}, @@ -100,6 +97,20 @@ impl LocalKeystore { .map(|pair| pair.sign(msg)); Ok(signature) } + + fn vrf_sign( + &self, + key_type: KeyTypeId, + public: &T::Public, + transcript: &T::VrfInput, + ) -> std::result::Result, TraitError> { + let sig = self + .0 + .read() + .key_pair_by_type::(public, key_type)? + .map(|pair| pair.vrf_sign(transcript)); + Ok(sig) + } } impl Keystore for LocalKeystore { @@ -131,14 +142,9 @@ impl Keystore for LocalKeystore { &self, key_type: KeyTypeId, public: &sr25519::Public, - transcript_data: VRFTranscriptData, - ) -> std::result::Result, TraitError> { - let sig = self.0.read().key_pair_by_type::(public, key_type)?.map(|pair| { - let transcript = make_transcript(transcript_data); - let (inout, proof, _) = pair.as_ref().vrf_sign(transcript); - VRFSignature { output: inout.to_output(), proof } - }); - Ok(sig) + transcript: &sr25519::vrf::VrfTranscript, + ) -> std::result::Result, TraitError> { + self.vrf_sign::(key_type, public, transcript) } fn ed25519_public_keys(&self, key_type: KeyTypeId) -> Vec { diff --git a/frame/babe/Cargo.toml b/frame/babe/Cargo.toml index c4f6592db..ff9e4b3ae 100644 --- a/frame/babe/Cargo.toml +++ b/frame/babe/Cargo.toml @@ -24,7 +24,7 @@ pallet-session = { version = "4.0.0-dev", default-features = false, path = "../s pallet-timestamp = { version = "4.0.0-dev", default-features = false, path = "../timestamp" } sp-application-crypto = { version = "7.0.0", default-features = false, path = "../../primitives/application-crypto" } sp-consensus-babe = { version = "0.10.0-dev", default-features = false, path = "../../primitives/consensus/babe" } -sp-consensus-vrf = { version = "0.10.0-dev", default-features = false, path = "../../primitives/consensus/vrf" } +sp-core = { version = "7.0.0", default-features = false, path = "../../primitives/core" } sp-io = { version = "7.0.0", default-features = false, path = "../../primitives/io" } sp-runtime = { version = "7.0.0", default-features = false, path = "../../primitives/runtime" } sp-session = { version = "4.0.0-dev", default-features = false, path = "../../primitives/session" } @@ -53,7 +53,7 @@ std = [ "scale-info/std", "sp-application-crypto/std", "sp-consensus-babe/std", - "sp-consensus-vrf/std", + "sp-core/std", "sp-io/std", "sp-runtime/std", "sp-session/std", diff --git a/frame/babe/src/lib.rs b/frame/babe/src/lib.rs index 61284267b..e3ead424e 100644 --- a/frame/babe/src/lib.rs +++ b/frame/babe/src/lib.rs @@ -29,13 +29,13 @@ use frame_support::{ weights::Weight, BoundedVec, WeakBoundedVec, }; -use sp_application_crypto::ByteArray; use sp_consensus_babe::{ digests::{NextConfigDescriptor, NextEpochDescriptor, PreDigest}, AllowedSlots, BabeAuthorityWeight, BabeEpochConfiguration, ConsensusLog, Epoch, - EquivocationProof, Slot, BABE_ENGINE_ID, + EquivocationProof, Randomness as BabeRandomness, Slot, BABE_ENGINE_ID, RANDOMNESS_LENGTH, + RANDOMNESS_VRF_CONTEXT, }; -use sp_consensus_vrf::schnorrkel; +use sp_core::crypto::Wraps; use sp_runtime::{ generic::DigestItem, traits::{IsMember, One, SaturatedConversion, Saturating, Zero}, @@ -45,7 +45,7 @@ use sp_session::{GetSessionNumber, GetValidatorCount}; use sp_staking::{offence::OffenceReportSystem, SessionIndex}; use sp_std::prelude::*; -pub use sp_consensus_babe::{AuthorityId, PUBLIC_KEY_LENGTH, RANDOMNESS_LENGTH, VRF_OUTPUT_LENGTH}; +pub use sp_consensus_babe::AuthorityId; const LOG_TARGET: &str = "runtime::babe"; @@ -218,7 +218,7 @@ pub mod pallet { // variable to its underlying value. #[pallet::storage] #[pallet::getter(fn randomness)] - pub type Randomness = StorageValue<_, schnorrkel::Randomness, ValueQuery>; + pub type Randomness = StorageValue<_, BabeRandomness, ValueQuery>; /// Pending epoch configuration change that will be applied when the next epoch is enacted. #[pallet::storage] @@ -226,7 +226,7 @@ pub mod pallet { /// Next epoch randomness. #[pallet::storage] - pub(super) type NextRandomness = StorageValue<_, schnorrkel::Randomness, ValueQuery>; + pub(super) type NextRandomness = StorageValue<_, BabeRandomness, ValueQuery>; /// Next epoch authorities. #[pallet::storage] @@ -254,7 +254,7 @@ pub mod pallet { _, Twox64Concat, u32, - BoundedVec>, + BoundedVec>, ValueQuery, >; @@ -270,8 +270,7 @@ pub mod pallet { /// It is set in `on_finalize`, before it will contain the value from the last block. #[pallet::storage] #[pallet::getter(fn author_vrf_randomness)] - pub(super) type AuthorVrfRandomness = - StorageValue<_, Option, ValueQuery>; + pub(super) type AuthorVrfRandomness = StorageValue<_, Option, ValueQuery>; /// The block numbers when the last and current epoch have started, respectively `N-1` and /// `N`. @@ -358,31 +357,33 @@ pub mod pallet { ); } - if let Some((vrf_output, vrf_proof)) = pre_digest.vrf() { - let randomness: Option = Authorities::::get() + if let Some(vrf_signature) = pre_digest.vrf_signature() { + let randomness: Option = Authorities::::get() .get(authority_index as usize) .and_then(|(authority, _)| { - schnorrkel::PublicKey::from_bytes(authority.as_slice()).ok() - }) - .and_then(|pubkey| { - let current_slot = CurrentSlot::::get(); - + let public = authority.as_inner_ref(); let transcript = sp_consensus_babe::make_transcript( &Self::randomness(), - current_slot, + CurrentSlot::::get(), EpochIndex::::get(), ); // NOTE: this is verified by the client when importing the block, before - // execution. we don't run the verification again here to avoid slowing + // execution. We don't run the verification again here to avoid slowing // down the runtime. - debug_assert!(pubkey - .vrf_verify(transcript.clone(), vrf_output, vrf_proof) - .is_ok()); - - vrf_output.0.attach_input_hash(&pubkey, transcript).ok() - }) - .map(|inout| inout.make_bytes(sp_consensus_babe::BABE_VRF_INOUT_CONTEXT)); + debug_assert!({ + use sp_core::crypto::VrfVerifier; + public.vrf_verify(&transcript, &vrf_signature) + }); + + public + .make_bytes( + RANDOMNESS_VRF_CONTEXT, + &transcript, + &vrf_signature.output, + ) + .ok() + }); if let Some(randomness) = pre_digest.is_primary().then(|| randomness).flatten() { @@ -484,9 +485,6 @@ pub mod pallet { } } -/// A BABE public key -pub type BabeKey = [u8; PUBLIC_KEY_LENGTH]; - impl FindAuthor for Pallet { fn find_author<'a, I>(digests: I) -> Option where @@ -737,7 +735,7 @@ impl Pallet { >::deposit_log(log) } - fn deposit_randomness(randomness: &schnorrkel::Randomness) { + fn deposit_randomness(randomness: &BabeRandomness) { let segment_idx = SegmentIndex::::get(); let mut segment = UnderConstruction::::get(&segment_idx); if segment.try_push(*randomness).is_ok() { @@ -831,7 +829,7 @@ impl Pallet { /// Call this function exactly once when an epoch changes, to update the /// randomness. Returns the new randomness. - fn randomness_change_epoch(next_epoch_index: u64) -> schnorrkel::Randomness { + fn randomness_change_epoch(next_epoch_index: u64) -> BabeRandomness { let this_randomness = NextRandomness::::get(); let segment_idx: u32 = SegmentIndex::::mutate(|s| sp_std::mem::replace(s, 0)); @@ -990,12 +988,12 @@ where // // an optional size hint as to how many VRF outputs there were may be provided. fn compute_randomness( - last_epoch_randomness: schnorrkel::Randomness, + last_epoch_randomness: BabeRandomness, epoch_index: u64, - rho: impl Iterator, + rho: impl Iterator, rho_size_hint: Option, -) -> schnorrkel::Randomness { - let mut s = Vec::with_capacity(40 + rho_size_hint.unwrap_or(0) * VRF_OUTPUT_LENGTH); +) -> BabeRandomness { + let mut s = Vec::with_capacity(40 + rho_size_hint.unwrap_or(0) * RANDOMNESS_LENGTH); s.extend_from_slice(&last_epoch_randomness); s.extend_from_slice(&epoch_index.to_le_bytes()); diff --git a/frame/babe/src/mock.rs b/frame/babe/src/mock.rs index 94e748d0b..bccdb42c7 100644 --- a/frame/babe/src/mock.rs +++ b/frame/babe/src/mock.rs @@ -25,10 +25,9 @@ use frame_support::{ traits::{ConstU128, ConstU32, ConstU64, GenesisBuild, KeyOwnerProofSystem, OnInitialize}, }; use pallet_session::historical as pallet_session_historical; -use sp_consensus_babe::{AuthorityId, AuthorityPair, Slot}; -use sp_consensus_vrf::schnorrkel::{VRFOutput, VRFProof}; +use sp_consensus_babe::{AuthorityId, AuthorityPair, Randomness, Slot, VrfSignature}; use sp_core::{ - crypto::{IsWrappedBy, KeyTypeId, Pair}, + crypto::{KeyTypeId, Pair, VrfSigner}, H256, U256, }; use sp_io; @@ -283,16 +282,10 @@ pub fn start_era(era_index: EraIndex) { pub fn make_primary_pre_digest( authority_index: sp_consensus_babe::AuthorityIndex, slot: sp_consensus_babe::Slot, - vrf_output: VRFOutput, - vrf_proof: VRFProof, + vrf_signature: VrfSignature, ) -> Digest { let digest_data = sp_consensus_babe::digests::PreDigest::Primary( - sp_consensus_babe::digests::PrimaryPreDigest { - authority_index, - slot, - vrf_output, - vrf_proof, - }, + sp_consensus_babe::digests::PrimaryPreDigest { authority_index, slot, vrf_signature }, ); let log = DigestItem::PreRuntime(sp_consensus_babe::BABE_ENGINE_ID, digest_data.encode()); Digest { logs: vec![log] } @@ -312,16 +305,10 @@ pub fn make_secondary_plain_pre_digest( pub fn make_secondary_vrf_pre_digest( authority_index: sp_consensus_babe::AuthorityIndex, slot: sp_consensus_babe::Slot, - vrf_output: VRFOutput, - vrf_proof: VRFProof, + vrf_signature: VrfSignature, ) -> Digest { let digest_data = sp_consensus_babe::digests::PreDigest::SecondaryVRF( - sp_consensus_babe::digests::SecondaryVRFPreDigest { - authority_index, - slot, - vrf_output, - vrf_proof, - }, + sp_consensus_babe::digests::SecondaryVRFPreDigest { authority_index, slot, vrf_signature }, ); let log = DigestItem::PreRuntime(sp_consensus_babe::BABE_ENGINE_ID, digest_data.encode()); Digest { logs: vec![log] } @@ -330,16 +317,16 @@ pub fn make_secondary_vrf_pre_digest( pub fn make_vrf_output( slot: Slot, pair: &sp_consensus_babe::AuthorityPair, -) -> (VRFOutput, VRFProof, [u8; 32]) { - let pair = sp_core::sr25519::Pair::from_ref(pair).as_ref(); +) -> (VrfSignature, Randomness) { let transcript = sp_consensus_babe::make_transcript(&Babe::randomness(), slot, 0); - let vrf_inout = pair.vrf_sign(transcript); - let vrf_randomness: sp_consensus_vrf::schnorrkel::Randomness = - vrf_inout.0.make_bytes::<[u8; 32]>(&sp_consensus_babe::BABE_VRF_INOUT_CONTEXT); - let vrf_output = VRFOutput(vrf_inout.0.to_output()); - let vrf_proof = VRFProof(vrf_inout.1); - (vrf_output, vrf_proof, vrf_randomness) + let signature = pair.as_ref().vrf_sign(&transcript); + + let randomness = pair + .as_ref() + .make_bytes::(sp_consensus_babe::RANDOMNESS_VRF_CONTEXT, &transcript); + + (signature, randomness) } pub fn new_test_ext(authorities_len: usize) -> sp_io::TestExternalities { diff --git a/frame/babe/src/randomness.rs b/frame/babe/src/randomness.rs index b223df680..b9b24786b 100644 --- a/frame/babe/src/randomness.rs +++ b/frame/babe/src/randomness.rs @@ -19,7 +19,7 @@ //! randomness collected from VRF outputs. use super::{ - AuthorVrfRandomness, Config, EpochStart, NextRandomness, Randomness, VRF_OUTPUT_LENGTH, + AuthorVrfRandomness, Config, EpochStart, NextRandomness, Randomness, RANDOMNESS_LENGTH, }; use frame_support::traits::Randomness as RandomnessT; use sp_runtime::traits::{Hash, One, Saturating}; @@ -132,7 +132,7 @@ pub struct CurrentBlockRandomness(sp_std::marker::PhantomData); impl RandomnessT for RandomnessFromTwoEpochsAgo { fn random(subject: &[u8]) -> (T::Hash, T::BlockNumber) { let mut subject = subject.to_vec(); - subject.reserve(VRF_OUTPUT_LENGTH); + subject.reserve(RANDOMNESS_LENGTH); subject.extend_from_slice(&Randomness::::get()[..]); (T::Hashing::hash(&subject[..]), EpochStart::::get().0) @@ -142,7 +142,7 @@ impl RandomnessT for RandomnessFromTwoEpochs impl RandomnessT for RandomnessFromOneEpochAgo { fn random(subject: &[u8]) -> (T::Hash, T::BlockNumber) { let mut subject = subject.to_vec(); - subject.reserve(VRF_OUTPUT_LENGTH); + subject.reserve(RANDOMNESS_LENGTH); subject.extend_from_slice(&NextRandomness::::get()[..]); (T::Hashing::hash(&subject[..]), EpochStart::::get().1) @@ -153,7 +153,7 @@ impl RandomnessT, T::BlockNumber> for ParentBlockRand fn random(subject: &[u8]) -> (Option, T::BlockNumber) { let random = AuthorVrfRandomness::::get().map(|random| { let mut subject = subject.to_vec(); - subject.reserve(VRF_OUTPUT_LENGTH); + subject.reserve(RANDOMNESS_LENGTH); subject.extend_from_slice(&random); T::Hashing::hash(&subject[..]) diff --git a/frame/babe/src/tests.rs b/frame/babe/src/tests.rs index 8b63f41b3..8a9aa6fcc 100644 --- a/frame/babe/src/tests.rs +++ b/frame/babe/src/tests.rs @@ -25,11 +25,12 @@ use frame_support::{ }; use mock::*; use pallet_session::ShouldEndSession; -use sp_consensus_babe::{AllowedSlots, BabeEpochConfiguration, Slot}; -use sp_consensus_vrf::schnorrkel::{VRFOutput, VRFProof}; +use sp_consensus_babe::{ + AllowedSlots, BabeEpochConfiguration, Slot, VrfSignature, RANDOMNESS_LENGTH, +}; use sp_core::crypto::Pair; -const EMPTY_RANDOMNESS: [u8; 32] = [ +const EMPTY_RANDOMNESS: [u8; RANDOMNESS_LENGTH] = [ 74, 25, 49, 128, 53, 97, 244, 49, 222, 202, 176, 2, 231, 66, 95, 10, 133, 49, 213, 228, 86, 161, 164, 127, 217, 153, 138, 37, 48, 192, 248, 0, ]; @@ -62,10 +63,9 @@ fn first_block_epoch_zero_start() { ext.execute_with(|| { let genesis_slot = Slot::from(100); - let (vrf_output, vrf_proof, vrf_randomness) = make_vrf_output(genesis_slot, &pairs[0]); + let (vrf_signature, vrf_randomness) = make_vrf_output(genesis_slot, &pairs[0]); - let first_vrf = vrf_output; - let pre_digest = make_primary_pre_digest(0, genesis_slot, first_vrf.clone(), vrf_proof); + let pre_digest = make_primary_pre_digest(0, genesis_slot, vrf_signature); assert_eq!(Babe::genesis_slot(), Slot::from(0)); System::reset_events(); @@ -111,8 +111,8 @@ fn current_slot_is_processed_on_initialization() { ext.execute_with(|| { let genesis_slot = Slot::from(10); - let (vrf_output, vrf_proof, vrf_randomness) = make_vrf_output(genesis_slot, &pairs[0]); - let pre_digest = make_primary_pre_digest(0, genesis_slot, vrf_output, vrf_proof); + let (vrf_signature, vrf_randomness) = make_vrf_output(genesis_slot, &pairs[0]); + let pre_digest = make_primary_pre_digest(0, genesis_slot, vrf_signature); System::reset_events(); System::initialize(&1, &Default::default(), &pre_digest); @@ -134,14 +134,14 @@ fn current_slot_is_processed_on_initialization() { fn test_author_vrf_output(make_pre_digest: F) where - F: Fn(sp_consensus_babe::AuthorityIndex, Slot, VRFOutput, VRFProof) -> sp_runtime::Digest, + F: Fn(sp_consensus_babe::AuthorityIndex, Slot, VrfSignature) -> sp_runtime::Digest, { let (pairs, mut ext) = new_test_ext_with_pairs(1); ext.execute_with(|| { let genesis_slot = Slot::from(10); - let (vrf_output, vrf_proof, vrf_randomness) = make_vrf_output(genesis_slot, &pairs[0]); - let pre_digest = make_pre_digest(0, genesis_slot, vrf_output, vrf_proof); + let (vrf_signature, vrf_randomness) = make_vrf_output(genesis_slot, &pairs[0]); + let pre_digest = make_pre_digest(0, genesis_slot, vrf_signature); System::reset_events(); System::initialize(&1, &Default::default(), &pre_digest); diff --git a/primitives/consensus/babe/Cargo.toml b/primitives/consensus/babe/Cargo.toml index d106cd0d0..7e171811a 100644 --- a/primitives/consensus/babe/Cargo.toml +++ b/primitives/consensus/babe/Cargo.toml @@ -15,14 +15,12 @@ targets = ["x86_64-unknown-linux-gnu"] [dependencies] async-trait = { version = "0.1.57", optional = true } codec = { package = "parity-scale-codec", version = "3.2.2", default-features = false } -merlin = { version = "2.0", default-features = false } scale-info = { version = "2.5.0", default-features = false, features = ["derive"] } serde = { version = "1.0.136", features = ["derive"], optional = true } sp-api = { version = "4.0.0-dev", default-features = false, path = "../../api" } sp-application-crypto = { version = "7.0.0", default-features = false, path = "../../application-crypto" } sp-consensus = { version = "0.10.0-dev", optional = true, path = "../common" } sp-consensus-slots = { version = "0.10.0-dev", default-features = false, path = "../slots" } -sp-consensus-vrf = { version = "0.10.0-dev", default-features = false, path = "../vrf" } sp-core = { version = "7.0.0", default-features = false, path = "../../core" } sp-inherents = { version = "4.0.0-dev", default-features = false, path = "../../inherents" } sp-keystore = { version = "0.13.0", default-features = false, optional = true, path = "../../keystore" } @@ -35,14 +33,12 @@ default = ["std"] std = [ "async-trait", "codec/std", - "merlin/std", "scale-info/std", "serde", "sp-api/std", "sp-application-crypto/std", "sp-consensus", "sp-consensus-slots/std", - "sp-consensus-vrf/std", "sp-core/std", "sp-inherents/std", "sp-keystore", diff --git a/primitives/consensus/babe/src/digests.rs b/primitives/consensus/babe/src/digests.rs index 4364057a4..afc967e3a 100644 --- a/primitives/consensus/babe/src/digests.rs +++ b/primitives/consensus/babe/src/digests.rs @@ -19,14 +19,15 @@ use super::{ AllowedSlots, AuthorityId, AuthorityIndex, AuthoritySignature, BabeAuthorityWeight, - BabeEpochConfiguration, Slot, BABE_ENGINE_ID, + BabeEpochConfiguration, Randomness, Slot, BABE_ENGINE_ID, }; -use codec::{Decode, Encode, MaxEncodedLen}; -use scale_info::TypeInfo; + +use sp_core::sr25519::vrf::VrfSignature; use sp_runtime::{DigestItem, RuntimeDebug}; use sp_std::vec::Vec; -use sp_consensus_vrf::schnorrkel::{Randomness, VRFOutput, VRFProof}; +use codec::{Decode, Encode, MaxEncodedLen}; +use scale_info::TypeInfo; /// Raw BABE primary slot assignment pre-digest. #[derive(Clone, RuntimeDebug, Encode, Decode, MaxEncodedLen, TypeInfo)] @@ -35,10 +36,8 @@ pub struct PrimaryPreDigest { pub authority_index: super::AuthorityIndex, /// Slot pub slot: Slot, - /// VRF output - pub vrf_output: VRFOutput, - /// VRF proof - pub vrf_proof: VRFProof, + /// VRF signature + pub vrf_signature: VrfSignature, } /// BABE secondary slot assignment pre-digest. @@ -62,10 +61,8 @@ pub struct SecondaryVRFPreDigest { pub authority_index: super::AuthorityIndex, /// Slot pub slot: Slot, - /// VRF output - pub vrf_output: VRFOutput, - /// VRF proof - pub vrf_proof: VRFProof, + /// VRF signature + pub vrf_signature: VrfSignature, } /// A BABE pre-runtime digest. This contains all data required to validate a @@ -118,11 +115,10 @@ impl PreDigest { } /// Returns the VRF output and proof, if they exist. - pub fn vrf(&self) -> Option<(&VRFOutput, &VRFProof)> { + pub fn vrf_signature(&self) -> Option<&VrfSignature> { match self { - PreDigest::Primary(primary) => Some((&primary.vrf_output, &primary.vrf_proof)), - PreDigest::SecondaryVRF(secondary) => - Some((&secondary.vrf_output, &secondary.vrf_proof)), + PreDigest::Primary(primary) => Some(&primary.vrf_signature), + PreDigest::SecondaryVRF(secondary) => Some(&secondary.vrf_signature), PreDigest::SecondaryPlain(_) => None, } } diff --git a/primitives/consensus/babe/src/lib.rs b/primitives/consensus/babe/src/lib.rs index e7747ac4c..37cf0c3f0 100644 --- a/primitives/consensus/babe/src/lib.rs +++ b/primitives/consensus/babe/src/lib.rs @@ -23,22 +23,17 @@ pub mod digests; pub mod inherents; -pub use merlin::Transcript; -pub use sp_consensus_vrf::schnorrkel::{ - Randomness, RANDOMNESS_LENGTH, VRF_OUTPUT_LENGTH, VRF_PROOF_LENGTH, -}; - use codec::{Decode, Encode, MaxEncodedLen}; use scale_info::TypeInfo; #[cfg(feature = "std")] use serde::{Deserialize, Serialize}; -#[cfg(feature = "std")] -use sp_keystore::vrf::{VRFTranscriptData, VRFTranscriptValue}; use sp_runtime::{traits::Header, ConsensusEngineId, RuntimeDebug}; use sp_std::vec::Vec; use crate::digests::{NextConfigDescriptor, NextEpochDescriptor}; +pub use sp_core::sr25519::vrf::{VrfOutput, VrfProof, VrfSignature, VrfTranscript}; + /// Key type for BABE module. pub const KEY_TYPE: sp_core::crypto::KeyTypeId = sp_application_crypto::key_types::BABE; @@ -47,11 +42,14 @@ mod app { app_crypto!(sr25519, BABE); } -/// The prefix used by BABE for its VRF keys. -pub const BABE_VRF_PREFIX: &[u8] = b"substrate-babe-vrf"; +/// VRF context used for per-slot randomness generation. +pub const RANDOMNESS_VRF_CONTEXT: &[u8] = b"BabeVRFInOutContext"; -/// BABE VRFInOut context. -pub static BABE_VRF_INOUT_CONTEXT: &[u8] = b"BabeVRFInOutContext"; +/// VRF output length for per-slot randomness. +pub const RANDOMNESS_LENGTH: usize = 32; + +/// Randomness type required by BABE operations. +pub type Randomness = [u8; RANDOMNESS_LENGTH]; /// A Babe authority keypair. Necessarily equivalent to the schnorrkel public key used in /// the main Babe module. If that ever changes, then this must, too. @@ -96,26 +94,16 @@ pub type BabeAuthorityWeight = u64; /// of 0 (regardless of whether they are plain or vrf secondary blocks). pub type BabeBlockWeight = u32; -/// Make a VRF transcript from given randomness, slot number and epoch. -pub fn make_transcript(randomness: &Randomness, slot: Slot, epoch: u64) -> Transcript { - let mut transcript = Transcript::new(&BABE_ENGINE_ID); - transcript.append_u64(b"slot number", *slot); - transcript.append_u64(b"current epoch", epoch); - transcript.append_message(b"chain randomness", &randomness[..]); - transcript -} - /// Make a VRF transcript data container -#[cfg(feature = "std")] -pub fn make_transcript_data(randomness: &Randomness, slot: Slot, epoch: u64) -> VRFTranscriptData { - VRFTranscriptData { - label: &BABE_ENGINE_ID, - items: vec![ - ("slot number", VRFTranscriptValue::U64(*slot)), - ("current epoch", VRFTranscriptValue::U64(epoch)), - ("chain randomness", VRFTranscriptValue::Bytes(randomness.to_vec())), +pub fn make_transcript(randomness: &Randomness, slot: Slot, epoch: u64) -> VrfTranscript { + VrfTranscript::new( + &BABE_ENGINE_ID, + &[ + (b"slot number", &slot.to_le_bytes()), + (b"current epoch", &epoch.to_le_bytes()), + (b"chain randomness", randomness), ], - } + ) } /// An consensus log item for BABE. @@ -355,7 +343,7 @@ pub struct Epoch { /// The authorities and their weights. pub authorities: Vec<(AuthorityId, BabeAuthorityWeight)>, /// Randomness for this epoch. - pub randomness: [u8; VRF_OUTPUT_LENGTH], + pub randomness: Randomness, /// Configuration of the epoch. pub config: BabeEpochConfiguration, } diff --git a/primitives/consensus/vrf/Cargo.toml b/primitives/consensus/vrf/Cargo.toml deleted file mode 100644 index e7b4f0f0d..000000000 --- a/primitives/consensus/vrf/Cargo.toml +++ /dev/null @@ -1,32 +0,0 @@ -[package] -name = "sp-consensus-vrf" -version = "0.10.0-dev" -authors = ["Parity Technologies "] -description = "Primitives for VRF based consensus" -edition = "2021" -license = "Apache-2.0" -repository = "https://github.com/paritytech/substrate/" -homepage = "https://substrate.io" -readme = "README.md" - -[package.metadata.docs.rs] -targets = ["x86_64-unknown-linux-gnu"] - -[dependencies] -codec = { package = "parity-scale-codec", version = "3.2.2", default-features = false } -scale-info = { version = "2.5.0", default-features = false } -schnorrkel = { version = "0.9.1", default-features = false, features = ["preaudit_deprecated", "u64_backend"] } -sp-core = { version = "7.0.0", default-features = false, path = "../../core" } -sp-runtime = { version = "7.0.0", default-features = false, path = "../../runtime" } -sp-std = { version = "5.0.0", default-features = false, path = "../../std" } - -[features] -default = ["std"] -std = [ - "codec/std", - "scale-info/std", - "schnorrkel/std", - "sp-core/std", - "sp-runtime/std", - "sp-std/std", -] diff --git a/primitives/consensus/vrf/README.md b/primitives/consensus/vrf/README.md deleted file mode 100644 index d66490e02..000000000 --- a/primitives/consensus/vrf/README.md +++ /dev/null @@ -1,3 +0,0 @@ -Primitives for VRF-based consensus engines. - -License: Apache-2.0 \ No newline at end of file diff --git a/primitives/consensus/vrf/src/lib.rs b/primitives/consensus/vrf/src/lib.rs deleted file mode 100644 index 84040c185..000000000 --- a/primitives/consensus/vrf/src/lib.rs +++ /dev/null @@ -1,21 +0,0 @@ -// This file is part of Substrate. - -// Copyright (C) Parity Technologies (UK) Ltd. -// SPDX-License-Identifier: Apache-2.0 - -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -//! Primitives for VRF-based consensus engines. -#![cfg_attr(not(feature = "std"), no_std)] - -pub mod schnorrkel; diff --git a/primitives/consensus/vrf/src/schnorrkel.rs b/primitives/consensus/vrf/src/schnorrkel.rs deleted file mode 100644 index be145c5b1..000000000 --- a/primitives/consensus/vrf/src/schnorrkel.rs +++ /dev/null @@ -1,188 +0,0 @@ -// This file is part of Substrate. - -// Copyright (C) Parity Technologies (UK) Ltd. -// SPDX-License-Identifier: Apache-2.0 - -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -//! Schnorrkel-based VRF. - -use codec::{Decode, Encode, EncodeLike, MaxEncodedLen}; -use scale_info::TypeInfo; -use schnorrkel::errors::MultiSignatureStage; -use sp_core::U512; -use sp_std::{ - ops::{Deref, DerefMut}, - prelude::*, -}; - -pub use schnorrkel::{ - vrf::{VRF_OUTPUT_LENGTH, VRF_PROOF_LENGTH}, - PublicKey, SignatureError, -}; - -/// The length of the Randomness. -pub const RANDOMNESS_LENGTH: usize = VRF_OUTPUT_LENGTH; - -/// VRF output type available for `std` environment, suitable for schnorrkel operations. -#[derive(Clone, Debug, PartialEq, Eq)] -pub struct VRFOutput(pub schnorrkel::vrf::VRFOutput); - -impl Deref for VRFOutput { - type Target = schnorrkel::vrf::VRFOutput; - fn deref(&self) -> &Self::Target { - &self.0 - } -} - -impl DerefMut for VRFOutput { - fn deref_mut(&mut self) -> &mut Self::Target { - &mut self.0 - } -} - -impl Encode for VRFOutput { - fn encode(&self) -> Vec { - self.0.as_bytes().encode() - } -} - -impl EncodeLike for VRFOutput {} - -impl Decode for VRFOutput { - fn decode(i: &mut R) -> Result { - let decoded = <[u8; VRF_OUTPUT_LENGTH]>::decode(i)?; - Ok(Self(schnorrkel::vrf::VRFOutput::from_bytes(&decoded).map_err(convert_error)?)) - } -} - -impl MaxEncodedLen for VRFOutput { - fn max_encoded_len() -> usize { - <[u8; VRF_OUTPUT_LENGTH]>::max_encoded_len() - } -} - -impl TypeInfo for VRFOutput { - type Identity = [u8; VRF_OUTPUT_LENGTH]; - - fn type_info() -> scale_info::Type { - Self::Identity::type_info() - } -} - -impl TryFrom<[u8; VRF_OUTPUT_LENGTH]> for VRFOutput { - type Error = SignatureError; - - fn try_from(raw: [u8; VRF_OUTPUT_LENGTH]) -> Result { - schnorrkel::vrf::VRFOutput::from_bytes(&raw).map(VRFOutput) - } -} - -/// VRF proof type available for `std` environment, suitable for schnorrkel operations. -#[derive(Clone, Debug, PartialEq, Eq)] -pub struct VRFProof(pub schnorrkel::vrf::VRFProof); - -impl PartialOrd for VRFProof { - fn partial_cmp(&self, other: &Self) -> Option { - Some(self.cmp(other)) - } -} - -impl Ord for VRFProof { - fn cmp(&self, other: &Self) -> core::cmp::Ordering { - U512::from(self.0.to_bytes()).cmp(&U512::from(other.0.to_bytes())) - } -} - -impl Deref for VRFProof { - type Target = schnorrkel::vrf::VRFProof; - fn deref(&self) -> &Self::Target { - &self.0 - } -} - -impl DerefMut for VRFProof { - fn deref_mut(&mut self) -> &mut Self::Target { - &mut self.0 - } -} - -impl Encode for VRFProof { - fn encode(&self) -> Vec { - self.0.to_bytes().encode() - } -} - -impl EncodeLike for VRFProof {} - -impl Decode for VRFProof { - fn decode(i: &mut R) -> Result { - let decoded = <[u8; VRF_PROOF_LENGTH]>::decode(i)?; - Ok(Self(schnorrkel::vrf::VRFProof::from_bytes(&decoded).map_err(convert_error)?)) - } -} - -impl MaxEncodedLen for VRFProof { - fn max_encoded_len() -> usize { - <[u8; VRF_PROOF_LENGTH]>::max_encoded_len() - } -} - -impl TypeInfo for VRFProof { - type Identity = [u8; VRF_PROOF_LENGTH]; - - fn type_info() -> scale_info::Type { - Self::Identity::type_info() - } -} - -impl TryFrom<[u8; VRF_PROOF_LENGTH]> for VRFProof { - type Error = SignatureError; - - fn try_from(raw: [u8; VRF_PROOF_LENGTH]) -> Result { - schnorrkel::vrf::VRFProof::from_bytes(&raw).map(VRFProof) - } -} - -fn convert_error(e: SignatureError) -> codec::Error { - use MultiSignatureStage::*; - use SignatureError::*; - match e { - EquationFalse => "Signature error: `EquationFalse`".into(), - PointDecompressionError => "Signature error: `PointDecompressionError`".into(), - ScalarFormatError => "Signature error: `ScalarFormatError`".into(), - NotMarkedSchnorrkel => "Signature error: `NotMarkedSchnorrkel`".into(), - BytesLengthError { .. } => "Signature error: `BytesLengthError`".into(), - MuSigAbsent { musig_stage: Commitment } => - "Signature error: `MuSigAbsent` at stage `Commitment`".into(), - MuSigAbsent { musig_stage: Reveal } => - "Signature error: `MuSigAbsent` at stage `Reveal`".into(), - MuSigAbsent { musig_stage: Cosignature } => - "Signature error: `MuSigAbsent` at stage `Commitment`".into(), - MuSigInconsistent { musig_stage: Commitment, duplicate: true } => - "Signature error: `MuSigInconsistent` at stage `Commitment` on duplicate".into(), - MuSigInconsistent { musig_stage: Commitment, duplicate: false } => - "Signature error: `MuSigInconsistent` at stage `Commitment` on not duplicate".into(), - MuSigInconsistent { musig_stage: Reveal, duplicate: true } => - "Signature error: `MuSigInconsistent` at stage `Reveal` on duplicate".into(), - MuSigInconsistent { musig_stage: Reveal, duplicate: false } => - "Signature error: `MuSigInconsistent` at stage `Reveal` on not duplicate".into(), - MuSigInconsistent { musig_stage: Cosignature, duplicate: true } => - "Signature error: `MuSigInconsistent` at stage `Cosignature` on duplicate".into(), - MuSigInconsistent { musig_stage: Cosignature, duplicate: false } => - "Signature error: `MuSigInconsistent` at stage `Cosignature` on not duplicate".into(), - } -} - -/// Schnorrkel randomness value. Same size as `VRFOutput`. -pub type Randomness = [u8; RANDOMNESS_LENGTH]; diff --git a/primitives/core/Cargo.toml b/primitives/core/Cargo.toml index 1edd28b3f..302d52f2f 100644 --- a/primitives/core/Cargo.toml +++ b/primitives/core/Cargo.toml @@ -44,9 +44,9 @@ bitflags = "1.3" array-bytes = { version = "4.1", optional = true } ed25519-zebra = { version = "3.1.0", default-features = false, optional = true } blake2 = { version = "0.10.4", default-features = false, optional = true } -schnorrkel = { version = "0.9.1", features = ["preaudit_deprecated", "u64_backend"], default-features = false, optional = true } libsecp256k1 = { version = "0.7", default-features = false, features = ["static-context"], optional = true } -merlin = { version = "2.0", default-features = false, optional = true } +schnorrkel = { version = "0.9.1", features = ["preaudit_deprecated", "u64_backend"], default-features = false } +merlin = { version = "2.0", default-features = false } secp256k1 = { version = "0.24.0", default-features = false, features = ["recovery", "alloc"], optional = true } ss58-registry = { version = "1.34.0", default-features = false } sp-core-hashing = { version = "5.0.0", path = "./hashing", default-features = false, optional = true } @@ -69,7 +69,7 @@ bench = false [features] default = ["std"] std = [ - "merlin?/std", + "merlin/std", "full_crypto", "log/std", "thiserror", @@ -119,10 +119,8 @@ full_crypto = [ "array-bytes", "ed25519-zebra", "blake2", - "schnorrkel", "libsecp256k1", "secp256k1", "sp-core-hashing", "sp-runtime-interface/disable_target_static_assertions", - "merlin", ] diff --git a/primitives/core/src/crypto.rs b/primitives/core/src/crypto.rs index f77e952d8..4fec79fc6 100644 --- a/primitives/core/src/crypto.rs +++ b/primitives/core/src/crypto.rs @@ -28,11 +28,8 @@ use rand::{rngs::OsRng, RngCore}; #[cfg(feature = "std")] use regex::Regex; use scale_info::TypeInfo; -/// Trait for accessing reference to `SecretString`. -pub use secrecy::ExposeSecret; -/// A store for sensitive data. #[cfg(feature = "std")] -pub use secrecy::SecretString; +pub use secrecy::{ExposeSecret, SecretString}; use sp_runtime_interface::pass_by::PassByInner; #[doc(hidden)] pub use sp_std::ops::Deref; @@ -1102,6 +1099,27 @@ impl<'a> TryFrom<&'a str> for KeyTypeId { } } +/// Trait grouping types shared by a VRF signer and verifiers. +pub trait VrfCrypto { + /// Associated signature type. + type VrfSignature; + + /// Vrf input data. Generally some form of transcript. + type VrfInput; +} + +/// VRF Signer. +pub trait VrfSigner: VrfCrypto { + /// Sign input data. + fn vrf_sign(&self, data: &Self::VrfInput) -> Self::VrfSignature; +} + +/// VRF Verifier. +pub trait VrfVerifier: VrfCrypto { + /// Verify input data signature. + fn vrf_verify(&self, data: &Self::VrfInput, signature: &Self::VrfSignature) -> bool; +} + /// An identifier for a specific cryptographic algorithm used by a key pair #[derive(Debug, Copy, Clone, Default, PartialEq, Eq, PartialOrd, Ord, Hash, Encode, Decode)] #[cfg_attr(feature = "std", derive(serde::Serialize, serde::Deserialize))] diff --git a/primitives/core/src/sr25519.rs b/primitives/core/src/sr25519.rs index d7c1c79c3..fcd7f38a7 100644 --- a/primitives/core/src/sr25519.rs +++ b/primitives/core/src/sr25519.rs @@ -15,12 +15,11 @@ // See the License for the specific language governing permissions and // limitations under the License. -// tag::description[] //! Simple sr25519 (Schnorr-Ristretto) API. //! //! Note: `CHAIN_CODE_LENGTH` must be equal to `crate::crypto::JUNCTION_ID_LEN` //! for this to work. -// end::description[] + #[cfg(feature = "std")] use crate::crypto::Ss58Codec; #[cfg(feature = "full_crypto")] @@ -30,7 +29,6 @@ use schnorrkel::{ derive::{ChainCode, Derivation, CHAIN_CODE_LENGTH}, signing_context, ExpansionMode, Keypair, MiniSecretKey, PublicKey, SecretKey, }; -#[cfg(feature = "full_crypto")] use sp_std::vec::Vec; use crate::{ @@ -200,8 +198,6 @@ impl<'de> Deserialize<'de> for Public { } /// An Schnorrkel/Ristretto x25519 ("sr25519") signature. -/// -/// Instead of importing it for the local module, alias it to be available as a public type #[cfg_attr(feature = "full_crypto", derive(Hash))] #[derive(Encode, Decode, MaxEncodedLen, PassByInner, TypeInfo, PartialEq, Eq)] pub struct Signature(pub [u8; 64]); @@ -545,43 +541,194 @@ impl CryptoType for Pair { type Pair = Pair; } -/// Batch verification. -/// -/// `messages`, `signatures` and `pub_keys` should all have equal length. -/// -/// Returns `true` if all signatures are correct, `false` otherwise. -#[cfg(feature = "std")] -pub fn verify_batch( - messages: Vec<&[u8]>, - signatures: Vec<&Signature>, - pub_keys: Vec<&Public>, -) -> bool { - let mut sr_pub_keys = Vec::with_capacity(pub_keys.len()); - for pub_key in pub_keys { - match schnorrkel::PublicKey::from_bytes(pub_key.as_ref()) { - Ok(pk) => sr_pub_keys.push(pk), - Err(_) => return false, - }; +/// Schnorrkel VRF related types and operations. +pub mod vrf { + use super::*; + #[cfg(feature = "full_crypto")] + use crate::crypto::VrfSigner; + use crate::crypto::{VrfCrypto, VrfVerifier}; + use schnorrkel::{ + errors::MultiSignatureStage, + vrf::{VRF_OUTPUT_LENGTH, VRF_PROOF_LENGTH}, + SignatureError, + }; + + /// VRF transcript ready to be used for VRF sign/verify operations. + pub struct VrfTranscript(pub merlin::Transcript); + + impl VrfTranscript { + /// Build a new transcript ready to be used by a VRF signer/verifier. + pub fn new(label: &'static [u8], data: &[(&'static [u8], &[u8])]) -> Self { + let mut transcript = merlin::Transcript::new(label); + data.iter().for_each(|(l, b)| transcript.append_message(l, b)); + VrfTranscript(transcript) + } } - let mut sr_signatures = Vec::with_capacity(signatures.len()); - for signature in signatures { - match schnorrkel::Signature::from_bytes(signature.as_ref()) { - Ok(s) => sr_signatures.push(s), - Err(_) => return false, - }; + /// VRF signature data + #[derive(Clone, Debug, PartialEq, Eq, Encode, Decode, MaxEncodedLen, TypeInfo)] + pub struct VrfSignature { + /// The initial VRF configuration + pub output: VrfOutput, + /// The calculated VRF proof + pub proof: VrfProof, + } + + /// VRF output type suitable for schnorrkel operations. + #[derive(Clone, Debug, PartialEq, Eq)] + pub struct VrfOutput(pub schnorrkel::vrf::VRFOutput); + + impl Encode for VrfOutput { + fn encode(&self) -> Vec { + self.0.as_bytes().encode() + } + } + + impl Decode for VrfOutput { + fn decode(i: &mut R) -> Result { + let decoded = <[u8; VRF_OUTPUT_LENGTH]>::decode(i)?; + Ok(Self(schnorrkel::vrf::VRFOutput::from_bytes(&decoded).map_err(convert_error)?)) + } } - let mut messages: Vec = messages - .into_iter() - .map(|msg| signing_context(SIGNING_CTX).bytes(msg)) - .collect(); + impl MaxEncodedLen for VrfOutput { + fn max_encoded_len() -> usize { + <[u8; VRF_OUTPUT_LENGTH]>::max_encoded_len() + } + } + + impl TypeInfo for VrfOutput { + type Identity = [u8; VRF_OUTPUT_LENGTH]; + + fn type_info() -> scale_info::Type { + Self::Identity::type_info() + } + } + + /// VRF proof type suitable for schnorrkel operations. + #[derive(Clone, Debug, PartialEq, Eq)] + pub struct VrfProof(pub schnorrkel::vrf::VRFProof); + + impl Encode for VrfProof { + fn encode(&self) -> Vec { + self.0.to_bytes().encode() + } + } + + impl Decode for VrfProof { + fn decode(i: &mut R) -> Result { + let decoded = <[u8; VRF_PROOF_LENGTH]>::decode(i)?; + Ok(Self(schnorrkel::vrf::VRFProof::from_bytes(&decoded).map_err(convert_error)?)) + } + } + + impl MaxEncodedLen for VrfProof { + fn max_encoded_len() -> usize { + <[u8; VRF_PROOF_LENGTH]>::max_encoded_len() + } + } - schnorrkel::verify_batch(&mut messages, &sr_signatures, &sr_pub_keys, true).is_ok() + impl TypeInfo for VrfProof { + type Identity = [u8; VRF_PROOF_LENGTH]; + + fn type_info() -> scale_info::Type { + Self::Identity::type_info() + } + } + + #[cfg(feature = "full_crypto")] + impl VrfCrypto for Pair { + type VrfSignature = VrfSignature; + type VrfInput = VrfTranscript; + } + + #[cfg(feature = "full_crypto")] + impl VrfSigner for Pair { + fn vrf_sign(&self, transcript: &Self::VrfInput) -> Self::VrfSignature { + let (inout, proof, _) = self.0.vrf_sign(transcript.0.clone()); + VrfSignature { output: VrfOutput(inout.to_output()), proof: VrfProof(proof) } + } + } + + impl VrfCrypto for Public { + type VrfSignature = VrfSignature; + type VrfInput = VrfTranscript; + } + + impl VrfVerifier for Public { + fn vrf_verify(&self, transcript: &Self::VrfInput, signature: &Self::VrfSignature) -> bool { + schnorrkel::PublicKey::from_bytes(self) + .and_then(|public| { + public.vrf_verify(transcript.0.clone(), &signature.output.0, &signature.proof.0) + }) + .is_ok() + } + } + + fn convert_error(e: SignatureError) -> codec::Error { + use MultiSignatureStage::*; + use SignatureError::*; + match e { + EquationFalse => "Signature error: `EquationFalse`".into(), + PointDecompressionError => "Signature error: `PointDecompressionError`".into(), + ScalarFormatError => "Signature error: `ScalarFormatError`".into(), + NotMarkedSchnorrkel => "Signature error: `NotMarkedSchnorrkel`".into(), + BytesLengthError { .. } => "Signature error: `BytesLengthError`".into(), + MuSigAbsent { musig_stage: Commitment } => + "Signature error: `MuSigAbsent` at stage `Commitment`".into(), + MuSigAbsent { musig_stage: Reveal } => + "Signature error: `MuSigAbsent` at stage `Reveal`".into(), + MuSigAbsent { musig_stage: Cosignature } => + "Signature error: `MuSigAbsent` at stage `Commitment`".into(), + MuSigInconsistent { musig_stage: Commitment, duplicate: true } => + "Signature error: `MuSigInconsistent` at stage `Commitment` on duplicate".into(), + MuSigInconsistent { musig_stage: Commitment, duplicate: false } => + "Signature error: `MuSigInconsistent` at stage `Commitment` on not duplicate".into(), + MuSigInconsistent { musig_stage: Reveal, duplicate: true } => + "Signature error: `MuSigInconsistent` at stage `Reveal` on duplicate".into(), + MuSigInconsistent { musig_stage: Reveal, duplicate: false } => + "Signature error: `MuSigInconsistent` at stage `Reveal` on not duplicate".into(), + MuSigInconsistent { musig_stage: Cosignature, duplicate: true } => + "Signature error: `MuSigInconsistent` at stage `Cosignature` on duplicate".into(), + MuSigInconsistent { musig_stage: Cosignature, duplicate: false } => + "Signature error: `MuSigInconsistent` at stage `Cosignature` on not duplicate" + .into(), + } + } + + #[cfg(feature = "full_crypto")] + impl Pair { + /// Generate bytes from the given VRF configuration. + pub fn make_bytes>( + &self, + context: &[u8], + transcript: &VrfTranscript, + ) -> B { + let inout = self.0.vrf_create_hash(transcript.0.clone()); + inout.make_bytes::(context) + } + } + + impl Public { + /// Generate bytes from the given VRF configuration. + pub fn make_bytes>( + &self, + context: &[u8], + transcript: &VrfTranscript, + output: &VrfOutput, + ) -> Result { + let pubkey = schnorrkel::PublicKey::from_bytes(&self.0).map_err(convert_error)?; + let inout = output + .0 + .attach_input_hash(&pubkey, transcript.0.clone()) + .map_err(convert_error)?; + Ok(inout.make_bytes::(context)) + } + } } #[cfg(test)] -mod compatibility_test { +mod tests { use super::*; use crate::crypto::{Ss58Codec, DEV_ADDRESS, DEV_PHRASE}; use serde_json; @@ -811,4 +958,20 @@ mod compatibility_test { // Poorly-sized assert!(deserialize_signature("\"abc123\"").is_err()); } + + #[test] + fn vrf_make_bytes_matches() { + use super::vrf::*; + use crate::crypto::VrfSigner; + let pair = Pair::from_seed(b"12345678901234567890123456789012"); + let public = pair.public(); + let transcript = VrfTranscript::new(b"test", &[(b"foo", b"bar")]); + + let signature = pair.vrf_sign(&transcript); + + let ctx = b"randbytes"; + let b1 = pair.make_bytes::<[u8; 32]>(ctx, &transcript); + let b2 = public.make_bytes::<[u8; 32]>(ctx, &transcript, &signature.output).unwrap(); + assert_eq!(b1, b2); + } } diff --git a/primitives/keystore/Cargo.toml b/primitives/keystore/Cargo.toml index 543f01059..6b0194247 100644 --- a/primitives/keystore/Cargo.toml +++ b/primitives/keystore/Cargo.toml @@ -15,9 +15,7 @@ targets = ["x86_64-unknown-linux-gnu"] [dependencies] codec = { package = "parity-scale-codec", version = "3.2.2", default-features = false, features = ["derive"] } futures = "0.3.21" -merlin = { version = "2.0", default-features = false } parking_lot = { version = "0.12.1", default-features = false } -schnorrkel = { version = "0.9.1", default-features = false, features = ["preaudit_deprecated", "u64_backend"] } serde = { version = "1.0", optional = true } thiserror = "1.0" sp-core = { version = "7.0.0", default-features = false, path = "../core" } @@ -31,8 +29,6 @@ rand_chacha = "0.2.2" default = ["std"] std = [ "codec/std", - "merlin/std", - "schnorrkel/std", "serde", "sp-core/std", "sp-externalities/std", diff --git a/primitives/keystore/src/lib.rs b/primitives/keystore/src/lib.rs index 577969cdf..5b41f3b80 100644 --- a/primitives/keystore/src/lib.rs +++ b/primitives/keystore/src/lib.rs @@ -16,10 +16,9 @@ // limitations under the License. //! Keystore traits + pub mod testing; -pub mod vrf; -use crate::vrf::{VRFSignature, VRFTranscriptData}; use sp_core::{ crypto::{ByteArray, CryptoTypeId, KeyTypeId}, ecdsa, ed25519, sr25519, @@ -87,8 +86,8 @@ pub trait Keystore: Send + Sync { &self, key_type: KeyTypeId, public: &sr25519::Public, - transcript_data: VRFTranscriptData, - ) -> Result, Error>; + transcript: &sr25519::vrf::VrfTranscript, + ) -> Result, Error>; /// Returns all ed25519 public keys for the given key type. fn ed25519_public_keys(&self, key_type: KeyTypeId) -> Vec; diff --git a/primitives/keystore/src/testing.rs b/primitives/keystore/src/testing.rs index e5107cba7..a6fcd6e26 100644 --- a/primitives/keystore/src/testing.rs +++ b/primitives/keystore/src/testing.rs @@ -17,15 +17,13 @@ //! Types that should only be used for testing! +use crate::{Error, Keystore, KeystorePtr}; + use sp_core::{ - crypto::{ByteArray, KeyTypeId, Pair}, + crypto::{ByteArray, KeyTypeId, Pair, VrfSigner}, ecdsa, ed25519, sr25519, }; -use crate::{ - vrf::{make_transcript, VRFSignature, VRFTranscriptData}, - Error, Keystore, KeystorePtr, -}; use parking_lot::RwLock; use std::{collections::HashMap, sync::Arc}; @@ -100,6 +98,16 @@ impl MemoryKeystore { let sig = self.pair::(key_type, public).map(|pair| pair.sign(msg)); Ok(sig) } + + fn vrf_sign( + &self, + key_type: KeyTypeId, + public: &T::Public, + transcript: &T::VrfInput, + ) -> Result, Error> { + let sig = self.pair::(key_type, public).map(|pair| pair.vrf_sign(transcript)); + Ok(sig) + } } impl Keystore for MemoryKeystore { @@ -128,14 +136,9 @@ impl Keystore for MemoryKeystore { &self, key_type: KeyTypeId, public: &sr25519::Public, - transcript_data: VRFTranscriptData, - ) -> Result, Error> { - let sig = self.pair::(key_type, public).map(|pair| { - let transcript = make_transcript(transcript_data); - let (inout, proof, _) = pair.as_ref().vrf_sign(transcript); - VRFSignature { output: inout.to_output(), proof } - }); - Ok(sig) + transcript: &sr25519::vrf::VrfTranscript, + ) -> Result, Error> { + self.vrf_sign::(key_type, public, transcript) } fn ed25519_public_keys(&self, key_type: KeyTypeId) -> Vec { @@ -225,7 +228,6 @@ impl Into for MemoryKeystore { #[cfg(test)] mod tests { use super::*; - use crate::vrf::VRFTranscriptValue; use sp_core::{ sr25519, testing::{ECDSA, ED25519, SR25519}, @@ -265,23 +267,23 @@ mod tests { let secret_uri = "//Alice"; let key_pair = sr25519::Pair::from_string(secret_uri, None).expect("Generates key pair"); - let transcript_data = VRFTranscriptData { - label: b"Test", - items: vec![ - ("one", VRFTranscriptValue::U64(1)), - ("two", VRFTranscriptValue::U64(2)), - ("three", VRFTranscriptValue::Bytes("test".as_bytes().to_vec())), + let transcript = sr25519::vrf::VrfTranscript::new( + b"Test", + &[ + (b"one", &1_u64.to_le_bytes()), + (b"two", &2_u64.to_le_bytes()), + (b"three", "test".as_bytes()), ], - }; + ); - let result = store.sr25519_vrf_sign(SR25519, &key_pair.public(), transcript_data.clone()); + let result = store.sr25519_vrf_sign(SR25519, &key_pair.public(), &transcript); assert!(result.unwrap().is_none()); store .insert(SR25519, secret_uri, key_pair.public().as_ref()) .expect("Inserts unknown key"); - let result = store.sr25519_vrf_sign(SR25519, &key_pair.public(), transcript_data); + let result = store.sr25519_vrf_sign(SR25519, &key_pair.public(), &transcript); assert!(result.unwrap().is_some()); } diff --git a/primitives/keystore/src/vrf.rs b/primitives/keystore/src/vrf.rs deleted file mode 100644 index e089336c1..000000000 --- a/primitives/keystore/src/vrf.rs +++ /dev/null @@ -1,94 +0,0 @@ -// This file is part of Substrate. - -// Copyright (C) Parity Technologies (UK) Ltd. -// SPDX-License-Identifier: Apache-2.0 - -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -//! VRF-specifc data types and helpers - -use codec::Encode; -use merlin::Transcript; -use schnorrkel::vrf::{VRFOutput, VRFProof}; - -/// An enum whose variants represent possible -/// accepted values to construct the VRF transcript -#[derive(Clone, Encode)] -#[cfg_attr(feature = "std", derive(serde::Serialize, serde::Deserialize))] -pub enum VRFTranscriptValue { - /// Value is an array of bytes - Bytes(Vec), - /// Value is a u64 integer - U64(u64), -} -/// VRF Transcript data -#[derive(Clone, Encode)] -pub struct VRFTranscriptData { - /// The transcript's label - pub label: &'static [u8], - /// Additional data to be registered into the transcript - pub items: Vec<(&'static str, VRFTranscriptValue)>, -} -/// VRF signature data -pub struct VRFSignature { - /// The VRFOutput serialized - pub output: VRFOutput, - /// The calculated VRFProof - pub proof: VRFProof, -} - -/// Construct a `Transcript` object from data. -/// -/// Returns `merlin::Transcript` -pub fn make_transcript(data: VRFTranscriptData) -> Transcript { - let mut transcript = Transcript::new(data.label); - for (label, value) in data.items.into_iter() { - match value { - VRFTranscriptValue::Bytes(bytes) => { - transcript.append_message(label.as_bytes(), &bytes); - }, - VRFTranscriptValue::U64(val) => { - transcript.append_u64(label.as_bytes(), val); - }, - } - } - transcript -} - -#[cfg(test)] -mod tests { - use super::*; - use rand::RngCore; - use rand_chacha::{rand_core::SeedableRng, ChaChaRng}; - - #[test] - fn transcript_creation_matches() { - let mut orig_transcript = Transcript::new(b"My label"); - orig_transcript.append_u64(b"one", 1); - orig_transcript.append_message(b"two", "test".as_bytes()); - - let new_transcript = make_transcript(VRFTranscriptData { - label: b"My label", - items: vec![ - ("one", VRFTranscriptValue::U64(1)), - ("two", VRFTranscriptValue::Bytes("test".as_bytes().to_vec())), - ], - }); - let test = |t: Transcript| -> [u8; 16] { - let mut b = [0u8; 16]; - t.build_rng().finalize(&mut ChaChaRng::from_seed([0u8; 32])).fill_bytes(&mut b); - b - }; - debug_assert!(test(orig_transcript) == test(new_transcript)); - } -} From 54ce95eb1f11f4d30277f1ec47a4398cdf8896d8 Mon Sep 17 00:00:00 2001 From: gupnik <17176722+gupnik@users.noreply.github.com> Date: Wed, 19 Apr 2023 16:24:19 +0530 Subject: [PATCH 396/558] Adds example for dev_mode and updates doc (#13944) * Adds example for dev_mode and updates doc * Addresses review comments * Update frame/examples/dev-mode/src/lib.rs Co-authored-by: Kian Paimani <5588131+kianenigma@users.noreply.github.com> * Addresses review comment --------- Co-authored-by: Kian Paimani <5588131+kianenigma@users.noreply.github.com> --- Cargo.lock | 16 ++++ Cargo.toml | 1 + frame/examples/dev-mode/Cargo.toml | 42 +++++++++ frame/examples/dev-mode/README.md | 11 +++ frame/examples/dev-mode/src/lib.rs | 118 ++++++++++++++++++++++++ frame/examples/dev-mode/src/tests.rs | 130 +++++++++++++++++++++++++++ frame/support/procedural/src/lib.rs | 2 + 7 files changed, 320 insertions(+) create mode 100644 frame/examples/dev-mode/Cargo.toml create mode 100644 frame/examples/dev-mode/README.md create mode 100644 frame/examples/dev-mode/src/lib.rs create mode 100644 frame/examples/dev-mode/src/tests.rs diff --git a/Cargo.lock b/Cargo.lock index 26fb7c4ae..c7f74a495 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -5986,6 +5986,22 @@ dependencies = [ "sp-std", ] +[[package]] +name = "pallet-dev-mode" +version = "4.0.0-dev" +dependencies = [ + "frame-support", + "frame-system", + "log", + "pallet-balances", + "parity-scale-codec", + "scale-info", + "sp-core", + "sp-io", + "sp-runtime", + "sp-std", +] + [[package]] name = "pallet-election-provider-multi-phase" version = "4.0.0-dev" diff --git a/Cargo.toml b/Cargo.toml index 507d76210..82e2264a3 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -106,6 +106,7 @@ members = [ "frame/election-provider-support/solution-type/fuzzer", "frame/examples/basic", "frame/examples/offchain-worker", + "frame/examples/dev-mode", "frame/executive", "frame/nis", "frame/grandpa", diff --git a/frame/examples/dev-mode/Cargo.toml b/frame/examples/dev-mode/Cargo.toml new file mode 100644 index 000000000..a1f8de4b0 --- /dev/null +++ b/frame/examples/dev-mode/Cargo.toml @@ -0,0 +1,42 @@ +[package] +name = "pallet-dev-mode" +version = "4.0.0-dev" +authors = ["Parity Technologies "] +edition = "2021" +license = "MIT-0" +homepage = "https://substrate.io" +repository = "https://github.com/paritytech/substrate/" +description = "FRAME example pallet" +readme = "README.md" + +[package.metadata.docs.rs] +targets = ["x86_64-unknown-linux-gnu"] + +[dependencies] +codec = { package = "parity-scale-codec", version = "3.2.2", default-features = false } +log = { version = "0.4.17", default-features = false } +scale-info = { version = "2.5.0", default-features = false, features = ["derive"] } +frame-support = { version = "4.0.0-dev", default-features = false, path = "../../support" } +frame-system = { version = "4.0.0-dev", default-features = false, path = "../../system" } +pallet-balances = { version = "4.0.0-dev", default-features = false, path = "../../balances" } +sp-io = { version = "7.0.0", default-features = false, path = "../../../primitives/io" } +sp-runtime = { version = "7.0.0", default-features = false, path = "../../../primitives/runtime" } +sp-std = { version = "5.0.0", default-features = false, path = "../../../primitives/std" } + +[dev-dependencies] +sp-core = { version = "7.0.0", default-features = false, path = "../../../primitives/core" } + +[features] +default = ["std"] +std = [ + "codec/std", + "frame-support/std", + "frame-system/std", + "log/std", + "pallet-balances/std", + "scale-info/std", + "sp-io/std", + "sp-runtime/std", + "sp-std/std", +] +try-runtime = ["frame-support/try-runtime"] diff --git a/frame/examples/dev-mode/README.md b/frame/examples/dev-mode/README.md new file mode 100644 index 000000000..4c9ee8862 --- /dev/null +++ b/frame/examples/dev-mode/README.md @@ -0,0 +1,11 @@ + +# Dev Mode Example Pallet + +A simple example of a FRAME pallet demonstrating +the ease of requirements for a pallet in dev mode. + +Run `cargo doc --package pallet-dev-mode --open` to view this pallet's documentation. + +**Dev mode is not meant to be used in production.** + +License: MIT-0 diff --git a/frame/examples/dev-mode/src/lib.rs b/frame/examples/dev-mode/src/lib.rs new file mode 100644 index 000000000..dbc302f49 --- /dev/null +++ b/frame/examples/dev-mode/src/lib.rs @@ -0,0 +1,118 @@ +// This file is part of Substrate. + +// Copyright (C) Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +//! +//! # Dev Mode Example Pallet +//! +//! A simple example of a FRAME pallet demonstrating +//! the ease of requirements for a pallet in dev mode. +//! +//! Run `cargo doc --package pallet-dev-mode --open` to view this pallet's documentation. +//! +//! **Dev mode is not meant to be used in production.** + +// Ensure we're `no_std` when compiling for Wasm. +#![cfg_attr(not(feature = "std"), no_std)] + +use frame_support::dispatch::DispatchResult; +use frame_system::ensure_signed; + +// Re-export pallet items so that they can be accessed from the crate namespace. +pub use pallet::*; + +#[cfg(test)] +mod tests; + +/// A type alias for the balance type from this pallet's point of view. +type BalanceOf = ::Balance; + +/// Enable `dev_mode` for this pallet. +#[frame_support::pallet(dev_mode)] +pub mod pallet { + use super::*; + use frame_support::pallet_prelude::*; + use frame_system::pallet_prelude::*; + + #[pallet::config] + pub trait Config: pallet_balances::Config + frame_system::Config { + /// The overarching event type. + type RuntimeEvent: From> + IsType<::RuntimeEvent>; + } + + // Simple declaration of the `Pallet` type. It is placeholder we use to implement traits and + // method. + #[pallet::pallet] + pub struct Pallet(_); + + #[pallet::call] + impl Pallet { + #[pallet::call_index(0)] + /// No need to define a `weight` attribute here because of `dev_mode`. + pub fn add_dummy(origin: OriginFor, id: T::AccountId) -> DispatchResult { + ensure_root(origin)?; + + if let Some(mut dummies) = Dummy::::get() { + dummies.push(id.clone()); + Dummy::::set(Some(dummies)); + } else { + Dummy::::set(Some(vec![id.clone()])); + } + + // Let's deposit an event to let the outside world know this happened. + Self::deposit_event(Event::AddDummy { account: id }); + + Ok(()) + } + + #[pallet::call_index(1)] + /// No need to define a `weight` attribute here because of `dev_mode`. + pub fn set_bar( + origin: OriginFor, + #[pallet::compact] new_value: T::Balance, + ) -> DispatchResult { + let sender = ensure_signed(origin)?; + + // Put the new value into storage. + >::insert(&sender, new_value); + + Self::deposit_event(Event::SetBar { account: sender, balance: new_value }); + + Ok(()) + } + } + + #[pallet::event] + #[pallet::generate_deposit(pub(super) fn deposit_event)] + pub enum Event { + AddDummy { account: T::AccountId }, + SetBar { account: T::AccountId, balance: BalanceOf }, + } + + /// The MEL requirement for bounded pallets is skipped by `dev_mode`. + /// This means that all storages are marked as unbounded. + /// This is equivalent to specifying `#[pallet::unbounded]` on this type definitions. + /// When the dev_mode is removed, we would need to implement implement `MaxEncodedLen`. + #[pallet::storage] + pub type Dummy = StorageValue<_, Vec>; + + /// The Hasher requirement is skipped by `dev_mode`. So, second parameter can be `_` + /// and `Blake2_128Concat` is used as a default. + /// When the dev_mode is removed, we would need to specify the hasher like so: + /// `pub type Bar = StorageMap<_, Blake2_128Concat, T::AccountId, T::Balance>;`. + #[pallet::storage] + pub type Bar = StorageMap<_, _, T::AccountId, T::Balance>; +} diff --git a/frame/examples/dev-mode/src/tests.rs b/frame/examples/dev-mode/src/tests.rs new file mode 100644 index 000000000..e2f06ddda --- /dev/null +++ b/frame/examples/dev-mode/src/tests.rs @@ -0,0 +1,130 @@ +// This file is part of Substrate. + +// Copyright (C) Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +//! Tests for pallet-dev-mode. + +use crate::*; +use frame_support::{assert_ok, traits::ConstU64}; +use sp_core::H256; +use sp_runtime::{ + testing::Header, + traits::{BlakeTwo256, IdentityLookup}, + BuildStorage, +}; +// Reexport crate as its pallet name for construct_runtime. +use crate as pallet_dev_mode; + +type UncheckedExtrinsic = frame_system::mocking::MockUncheckedExtrinsic; +type Block = frame_system::mocking::MockBlock; + +// For testing the pallet, we construct a mock runtime. +frame_support::construct_runtime!( + pub enum Test where + Block = Block, + NodeBlock = Block, + UncheckedExtrinsic = UncheckedExtrinsic, + { + System: frame_system::{Pallet, Call, Config, Storage, Event}, + Balances: pallet_balances::{Pallet, Call, Storage, Config, Event}, + Example: pallet_dev_mode::{Pallet, Call, Storage, Event}, + } +); + +impl frame_system::Config for Test { + type BaseCallFilter = frame_support::traits::Everything; + type BlockWeights = (); + type BlockLength = (); + type DbWeight = (); + type RuntimeOrigin = RuntimeOrigin; + type Index = u64; + type BlockNumber = u64; + type Hash = H256; + type RuntimeCall = RuntimeCall; + type Hashing = BlakeTwo256; + type AccountId = u64; + type Lookup = IdentityLookup; + type Header = Header; + type RuntimeEvent = RuntimeEvent; + type BlockHashCount = ConstU64<250>; + type Version = (); + type PalletInfo = PalletInfo; + type AccountData = pallet_balances::AccountData; + type OnNewAccount = (); + type OnKilledAccount = (); + type SystemWeightInfo = (); + type SS58Prefix = (); + type OnSetCode = (); + type MaxConsumers = frame_support::traits::ConstU32<16>; +} + +impl pallet_balances::Config for Test { + type MaxLocks = (); + type MaxReserves = (); + type ReserveIdentifier = [u8; 8]; + type Balance = u64; + type DustRemoval = (); + type RuntimeEvent = RuntimeEvent; + type ExistentialDeposit = ConstU64<1>; + type AccountStore = System; + type WeightInfo = (); + type FreezeIdentifier = (); + type MaxFreezes = (); + type HoldIdentifier = (); + type MaxHolds = (); +} + +impl Config for Test { + type RuntimeEvent = RuntimeEvent; +} + +// This function basically just builds a genesis storage key/value store according to +// our desired mockup. +pub fn new_test_ext() -> sp_io::TestExternalities { + let t = GenesisConfig { + // We use default for brevity, but you can configure as desired if needed. + system: Default::default(), + balances: Default::default(), + } + .build_storage() + .unwrap(); + t.into() +} + +#[test] +fn it_works_for_optional_value() { + new_test_ext().execute_with(|| { + assert_eq!(Dummy::::get(), None); + + let val1 = 42; + assert_ok!(Example::add_dummy(RuntimeOrigin::root(), val1)); + assert_eq!(Dummy::::get(), Some(vec![val1])); + + // Check that accumulate works when we have Some value in Dummy already. + let val2 = 27; + assert_ok!(Example::add_dummy(RuntimeOrigin::root(), val2)); + assert_eq!(Dummy::::get(), Some(vec![val1, val2])); + }); +} + +#[test] +fn set_dummy_works() { + new_test_ext().execute_with(|| { + let test_val = 133; + assert_ok!(Example::set_bar(RuntimeOrigin::signed(1), test_val.into())); + assert_eq!(Bar::::get(1), Some(test_val)); + }); +} diff --git a/frame/support/procedural/src/lib.rs b/frame/support/procedural/src/lib.rs index ea997752c..33d9ce947 100644 --- a/frame/support/procedural/src/lib.rs +++ b/frame/support/procedural/src/lib.rs @@ -465,6 +465,8 @@ pub fn construct_runtime(input: TokenStream) -> TokenStream { /// * All storages are marked as unbounded, meaning you do not need to implement `MaxEncodedLen` on /// storage types. This is equivalent to specifying `#[pallet::unbounded]` on all storage type /// definitions. +/// * Storage hashers no longer need to be specified and can be replaced by `_`. In dev mode, these +/// will be replaced by `Blake2_128Concat`. /// /// Note that the `dev_mode` argument can only be supplied to the `#[pallet]` or /// `#[frame_support::pallet]` attribute macro that encloses your pallet module. This argument From 50de15d8740a129db9c18a6698fbd183b00326a2 Mon Sep 17 00:00:00 2001 From: Muharem Ismailov Date: Wed, 19 Apr 2023 13:00:27 +0200 Subject: [PATCH 397/558] Collective pallet: max proposal weight (#13771) * collective: max proposal weight * fix test --------- Co-authored-by: parity-processbot <> --- bin/node/runtime/src/lib.rs | 4 ++++ frame/alliance/src/mock.rs | 6 ++++- frame/collective/src/lib.rs | 14 ++++++++++++ frame/collective/src/tests.rs | 42 ++++++++++++++++++++++++++++++++++- frame/utility/src/tests.rs | 2 ++ 5 files changed, 66 insertions(+), 2 deletions(-) diff --git a/bin/node/runtime/src/lib.rs b/bin/node/runtime/src/lib.rs index cdb05d159..5722603fe 100644 --- a/bin/node/runtime/src/lib.rs +++ b/bin/node/runtime/src/lib.rs @@ -210,6 +210,7 @@ parameter_types! { }) .avg_block_initialization(AVERAGE_ON_INITIALIZE_RATIO) .build_or_panic(); + pub MaxCollectivesProposalWeight: Weight = Perbill::from_percent(50) * RuntimeBlockWeights::get().max_block; } const_assert!(NORMAL_DISPATCH_RATIO.deconstruct() >= AVERAGE_ON_INITIALIZE_RATIO.deconstruct()); @@ -1012,6 +1013,7 @@ impl pallet_collective::Config for Runtime { type DefaultVote = pallet_collective::PrimeDefaultVote; type WeightInfo = pallet_collective::weights::SubstrateWeight; type SetMembersOrigin = EnsureRoot; + type MaxProposalWeight = MaxCollectivesProposalWeight; } parameter_types! { @@ -1072,6 +1074,7 @@ impl pallet_collective::Config for Runtime { type DefaultVote = pallet_collective::PrimeDefaultVote; type WeightInfo = pallet_collective::weights::SubstrateWeight; type SetMembersOrigin = EnsureRoot; + type MaxProposalWeight = MaxCollectivesProposalWeight; } type EnsureRootOrHalfCouncil = EitherOfDiverse< @@ -1700,6 +1703,7 @@ impl pallet_collective::Config for Runtime { type DefaultVote = pallet_collective::PrimeDefaultVote; type WeightInfo = pallet_collective::weights::SubstrateWeight; type SetMembersOrigin = EnsureRoot; + type MaxProposalWeight = MaxCollectivesProposalWeight; } parameter_types! { diff --git a/frame/alliance/src/mock.rs b/frame/alliance/src/mock.rs index 848f6b2a0..c334a3943 100644 --- a/frame/alliance/src/mock.rs +++ b/frame/alliance/src/mock.rs @@ -43,10 +43,12 @@ type AccountId = u64; parameter_types! { pub const BlockHashCount: BlockNumber = 250; + pub BlockWeights: frame_system::limits::BlockWeights = + frame_system::limits::BlockWeights::simple_max(Weight::MAX); } impl frame_system::Config for Test { type BaseCallFilter = frame_support::traits::Everything; - type BlockWeights = (); + type BlockWeights = BlockWeights; type BlockLength = (); type RuntimeOrigin = RuntimeOrigin; type RuntimeCall = RuntimeCall; @@ -97,6 +99,7 @@ parameter_types! { pub const MotionDuration: BlockNumber = MOTION_DURATION_IN_BLOCKS; pub const MaxProposals: u32 = 100; pub const MaxMembers: u32 = 100; + pub MaxProposalWeight: Weight = sp_runtime::Perbill::from_percent(50) * BlockWeights::get().max_block; } type AllianceCollective = pallet_collective::Instance1; impl pallet_collective::Config for Test { @@ -109,6 +112,7 @@ impl pallet_collective::Config for Test { type DefaultVote = pallet_collective::PrimeDefaultVote; type WeightInfo = (); type SetMembersOrigin = EnsureRoot; + type MaxProposalWeight = MaxProposalWeight; } parameter_types! { diff --git a/frame/collective/src/lib.rs b/frame/collective/src/lib.rs index 0cbf7e972..0ecf008fe 100644 --- a/frame/collective/src/lib.rs +++ b/frame/collective/src/lib.rs @@ -217,6 +217,10 @@ pub mod pallet { /// Origin allowed to set collective members type SetMembersOrigin: EnsureOrigin<::RuntimeOrigin>; + + /// The maximum weight of a dispatch call that can be proposed and executed. + #[pallet::constant] + type MaxProposalWeight: Get; } #[pallet::genesis_config] @@ -667,6 +671,11 @@ impl, I: 'static> Pallet { ) -> Result<(u32, DispatchResultWithPostInfo), DispatchError> { let proposal_len = proposal.encoded_size(); ensure!(proposal_len <= length_bound as usize, Error::::WrongProposalLength); + let proposal_weight = proposal.get_dispatch_info().weight; + ensure!( + proposal_weight.all_lte(T::MaxProposalWeight::get()), + Error::::WrongProposalWeight + ); let proposal_hash = T::Hashing::hash_of(&proposal); ensure!(!>::contains_key(proposal_hash), Error::::DuplicateProposal); @@ -689,6 +698,11 @@ impl, I: 'static> Pallet { ) -> Result<(u32, u32), DispatchError> { let proposal_len = proposal.encoded_size(); ensure!(proposal_len <= length_bound as usize, Error::::WrongProposalLength); + let proposal_weight = proposal.get_dispatch_info().weight; + ensure!( + proposal_weight.all_lte(T::MaxProposalWeight::get()), + Error::::WrongProposalWeight + ); let proposal_hash = T::Hashing::hash_of(&proposal); ensure!(!>::contains_key(proposal_hash), Error::::DuplicateProposal); diff --git a/frame/collective/src/tests.rs b/frame/collective/src/tests.rs index 79dc3becf..4775133ff 100644 --- a/frame/collective/src/tests.rs +++ b/frame/collective/src/tests.rs @@ -90,10 +90,13 @@ pub type MaxMembers = ConstU32<100>; parameter_types! { pub const MotionDuration: u64 = 3; pub const MaxProposals: u32 = 257; + pub BlockWeights: frame_system::limits::BlockWeights = + frame_system::limits::BlockWeights::simple_max(Weight::MAX); + pub static MaxProposalWeight: Weight = default_max_proposal_weight(); } impl frame_system::Config for Test { type BaseCallFilter = frame_support::traits::Everything; - type BlockWeights = (); + type BlockWeights = BlockWeights; type BlockLength = (); type DbWeight = (); type RuntimeOrigin = RuntimeOrigin; @@ -127,6 +130,7 @@ impl Config for Test { type DefaultVote = PrimeDefaultVote; type WeightInfo = (); type SetMembersOrigin = EnsureRoot; + type MaxProposalWeight = MaxProposalWeight; } impl Config for Test { type RuntimeOrigin = RuntimeOrigin; @@ -138,6 +142,7 @@ impl Config for Test { type DefaultVote = MoreThanMajorityThenPrimeDefaultVote; type WeightInfo = (); type SetMembersOrigin = EnsureRoot; + type MaxProposalWeight = MaxProposalWeight; } impl mock_democracy::Config for Test { type RuntimeEvent = RuntimeEvent; @@ -153,6 +158,7 @@ impl Config for Test { type DefaultVote = PrimeDefaultVote; type WeightInfo = (); type SetMembersOrigin = EnsureRoot; + type MaxProposalWeight = MaxProposalWeight; } pub struct ExtBuilder {} @@ -201,6 +207,10 @@ fn record(event: RuntimeEvent) -> EventRecord { EventRecord { phase: Phase::Initialization, event, topics: vec![] } } +fn default_max_proposal_weight() -> Weight { + sp_runtime::Perbill::from_percent(80) * BlockWeights::get().max_block +} + #[test] fn motions_basic_environment_works() { ExtBuilder::default().build_and_execute(|| { @@ -209,6 +219,36 @@ fn motions_basic_environment_works() { }); } +#[test] +fn proposal_weight_limit_works() { + ExtBuilder::default().build_and_execute(|| { + let proposal = make_proposal(42); + let proposal_len: u32 = proposal.using_encoded(|p| p.len() as u32); + + assert_ok!(Collective::propose( + RuntimeOrigin::signed(1), + 2, + Box::new(proposal.clone()), + proposal_len + )); + + // set a small limit for max proposal weight. + MaxProposalWeight::set(Weight::from_parts(1, 1)); + assert_noop!( + Collective::propose( + RuntimeOrigin::signed(1), + 2, + Box::new(proposal.clone()), + proposal_len + ), + Error::::WrongProposalWeight + ); + + // reset the max weight to default. + MaxProposalWeight::set(default_max_proposal_weight()); + }); +} + #[test] fn close_works() { ExtBuilder::default().build_and_execute(|| { diff --git a/frame/utility/src/tests.rs b/frame/utility/src/tests.rs index d23c57e69..6f156e318 100644 --- a/frame/utility/src/tests.rs +++ b/frame/utility/src/tests.rs @@ -209,6 +209,7 @@ parameter_types! { pub const MotionDuration: BlockNumber = MOTION_DURATION_IN_BLOCKS; pub const MaxProposals: u32 = 100; pub const MaxMembers: u32 = 100; + pub MaxProposalWeight: Weight = sp_runtime::Perbill::from_percent(50) * BlockWeights::get().max_block; } type CouncilCollective = pallet_collective::Instance1; @@ -222,6 +223,7 @@ impl pallet_collective::Config for Test { type DefaultVote = pallet_collective::PrimeDefaultVote; type WeightInfo = (); type SetMembersOrigin = frame_system::EnsureRoot; + type MaxProposalWeight = MaxProposalWeight; } impl example::Config for Test {} From f9d10fabe04d598d68f8b097cc4905adbb1ad630 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bastian=20K=C3=B6cher?= Date: Thu, 20 Apr 2023 10:53:06 +0200 Subject: [PATCH 398/558] `build_network` doesn't require the `MaintainedTransactionPool` bound (#13959) --- client/service/src/builder.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/client/service/src/builder.rs b/client/service/src/builder.rs index 415fa03a1..2208c55cf 100644 --- a/client/service/src/builder.rs +++ b/client/service/src/builder.rs @@ -60,7 +60,7 @@ use sc_rpc::{ }; use sc_rpc_spec_v2::{chain_head::ChainHeadApiServer, transaction::TransactionApiServer}; use sc_telemetry::{telemetry, ConnectionMessage, Telemetry, TelemetryHandle, SUBSTRATE_INFO}; -use sc_transaction_pool_api::MaintainedTransactionPool; +use sc_transaction_pool_api::{MaintainedTransactionPool, TransactionPool}; use sc_utils::mpsc::{tracing_unbounded, TracingUnboundedSender}; use sp_api::{CallApiAt, ProvideRuntimeApi}; use sp_blockchain::{HeaderBackend, HeaderMetadata}; @@ -756,7 +756,7 @@ where + HeaderBackend + BlockchainEvents + 'static, - TExPool: MaintainedTransactionPool::Hash> + 'static, + TExPool: TransactionPool::Hash> + 'static, TImpQu: ImportQueue + 'static, { let BuildNetworkParams { From 87fd264d52ef8f23716e13b9abda34a63877ec63 Mon Sep 17 00:00:00 2001 From: Mira Ressel Date: Thu, 20 Apr 2023 14:02:40 +0200 Subject: [PATCH 399/558] ci: drop skip-if-draft job (#13961) It's been effectively disabled for half a year already, and according to internal discussion we don't want it back. --- .gitlab-ci.yml | 14 -------------- scripts/ci/gitlab/skip_if_draft.sh | 14 -------------- 2 files changed, 28 deletions(-) delete mode 100755 scripts/ci/gitlab/skip_if_draft.sh diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index e7b009318..d585d688a 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -251,20 +251,6 @@ default: #### stage: .pre -skip-if-draft: - extends: .kubernetes-env - variables: - CI_IMAGE: "paritytech/tools:latest" - stage: .pre - rules: - - if: $CI_COMMIT_REF_NAME =~ /^[0-9]+$/ # PRs - script: - - echo "Commit message is ${CI_COMMIT_MESSAGE}" - - echo "Ref is ${CI_COMMIT_REF_NAME}" - - echo "pipeline source is ${CI_PIPELINE_SOURCE}" - - ./scripts/ci/gitlab/skip_if_draft.sh - allow_failure: true - check-crates-publishing-pipeline: stage: .pre extends: diff --git a/scripts/ci/gitlab/skip_if_draft.sh b/scripts/ci/gitlab/skip_if_draft.sh deleted file mode 100755 index cf6ea6a5b..000000000 --- a/scripts/ci/gitlab/skip_if_draft.sh +++ /dev/null @@ -1,14 +0,0 @@ -#!/bin/sh -url="https://api.github.com/repos/paritytech/substrate/pulls/${CI_COMMIT_REF_NAME}" -echo "[+] API URL: $url" - -draft_state=$(curl -H "Authorization: token ${GITHUB_PR_TOKEN}" "$url" | jq -r .draft) -echo "[+] Draft state: $draft_state" - -if [ "$draft_state" = 'true' ]; then - echo "[!] PR is currently a draft, stopping pipeline" - exit 1 -else - echo "[+] PR is not a draft. Proceeding with CI pipeline" - exit 0 -fi From 532edcf9ac691bebd2959ca3fa4cd90d8171ccb3 Mon Sep 17 00:00:00 2001 From: Mira Ressel Date: Thu, 20 Apr 2023 14:03:05 +0200 Subject: [PATCH 400/558] replace unused cargo-deny check with a new cargo-deny-licenses job (#13956) * ci: remove the cargo-deny job It's been broken and disabled for quite a while, and per internal discussion there doesn't seem to be any interest in fixing it and actually using the job output for anything. * ci: add new cargo-deny-licenses job It'll run on all PRs and fail if external dependencies with unsuitable licenses are detected. --- .gitlab-ci.yml | 5 - scripts/ci/deny.toml | 212 +++++++++------------------- scripts/ci/gitlab/pipeline/test.yml | 12 +- 3 files changed, 73 insertions(+), 156 deletions(-) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index d585d688a..c6ce21c7e 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -215,11 +215,6 @@ default: .zombienet-refs: extends: .build-refs -.nightly-pipeline: - rules: - # this job runs only on nightly pipeline with the mentioned variable, against `master` branch - - if: $CI_COMMIT_REF_NAME == "master" && $CI_PIPELINE_SOURCE == "schedule" && $PIPELINE == "nightly" - .crates-publishing-variables: variables: CRATESIO_CRATES_OWNER: parity-crate-owner diff --git a/scripts/ci/deny.toml b/scripts/ci/deny.toml index bc41f746c..f93287593 100644 --- a/scripts/ci/deny.toml +++ b/scripts/ci/deny.toml @@ -1,87 +1,19 @@ -# This template contains all of the possible sections and their default values - -# Note that all fields that take a lint level have these possible values: -# * deny - An error will be produced and the check will fail -# * warn - A warning will be produced, but the check will not fail -# * allow - No warning or error will be produced, though in some cases a note -# will be - -# The values provided in this template are the default values that will be used -# when any section or field is not specified in your own configuration - -# If 1 or more target triples (and optionally, target_features) are specified, -# only the specified targets will be checked when running `cargo deny check`. -# This means, if a particular package is only ever used as a target specific -# dependency, such as, for example, the `nix` crate only being used via the -# `target_family = "unix"` configuration, that only having windows targets in -# this list would mean the nix crate, as well as any of its exclusive -# dependencies not shared by any other crates, would be ignored, as the target -# list here is effectively saying which targets you are building for. -targets = [ - # The triple can be any string, but only the target triples built in to - # rustc (as of 1.40) can be checked against actual config expressions - #{ triple = "x86_64-unknown-linux-musl" }, - # You can also specify which target_features you promise are enabled for a - # particular target. target_features are currently not validated against - # the actual valid features supported by the target architecture. - #{ triple = "wasm32-unknown-unknown", features = ["atomics"] }, -] - -# This section is considered when running `cargo deny check advisories` -# More documentation for the advisories section can be found here: -# https://embarkstudios.github.io/cargo-deny/checks/advisories/cfg.html -[advisories] -# The path where the advisory database is cloned/fetched into -db-path = "~/.cargo/advisory-db" -# The url of the advisory database to use -db-url = "https://github.com/rustsec/advisory-db" -# The lint level for security vulnerabilities -vulnerability = "deny" -# The lint level for unmaintained crates -unmaintained = "warn" -# The lint level for crates that have been yanked from their source registry -yanked = "warn" -# The lint level for crates with security notices. Note that as of -# 2019-12-17 there are no security notice advisories in -# https://github.com/rustsec/advisory-db -notice = "warn" -# A list of advisory IDs to ignore. Note that ignored advisories will still -# output a note when they are encountered. -ignore = [ - #"RUSTSEC-0000-0000", -] -# Threshold for security vulnerabilities, any vulnerability with a CVSS score -# lower than the range specified will be ignored. Note that ignored advisories -# will still output a note when they are encountered. -# * None - CVSS Score 0.0 -# * Low - CVSS Score 0.1 - 3.9 -# * Medium - CVSS Score 4.0 - 6.9 -# * High - CVSS Score 7.0 - 8.9 -# * Critical - CVSS Score 9.0 - 10.0 -#severity-threshold = - -# This section is considered when running `cargo deny check licenses` -# More documentation for the licenses section can be found here: -# https://embarkstudios.github.io/cargo-deny/checks/licenses/cfg.html [licenses] # The lint level for crates which do not have a detectable license unlicensed = "deny" -# List of explictly allowed licenses +# List of explicitly allowed licenses # See https://spdx.org/licenses/ for list of possible licenses -# [possible values: any SPDX 3.7 short identifier (+ optional exception)]. +# [possible values: any SPDX 3.11 short identifier (+ optional exception)]. allow = [ - #"MIT", - #"Apache-2.0", - #"Apache-2.0 WITH LLVM-exception", + "MPL-2.0", ] -# List of explictly disallowed licenses +# List of explicitly disallowed licenses # See https://spdx.org/licenses/ for list of possible licenses -# [possible values: any SPDX 3.7 short identifier (+ optional exception)]. +# [possible values: any SPDX 3.11 short identifier (+ optional exception)]. deny = [ - #"Nokia", ] # Lint level for licenses considered copyleft -copyleft = "allow" +copyleft = "deny" # Blanket approval or denial for OSI-approved or FSF Free/Libre licenses # * both - The license will be approved if it is both OSI-approved *AND* FSF # * either - The license will be approved if it is either OSI-approved *OR* FSF @@ -98,13 +30,69 @@ default = "deny" # The higher the value, the more closely the license text must be to the # canonical license text of a valid SPDX license file. # [possible values: any between 0.0 and 1.0]. -confidence-threshold = 0.9 +confidence-threshold = 0.8 # Allow 1 or more licenses on a per-crate basis, so that particular licenses # aren't accepted for every possible crate as with the normal allow list exceptions = [ - # Each entry is the crate and version constraint, and its specific allow - # list - #{ allow = ["Zlib"], name = "adler32", version = "*" }, + # Each entry is the crate and version constraint, and its specific allow list + { allow = ["GPL-3.0 WITH Classpath-exception-2.0"], name = "chain-spec-builder" }, + { allow = ["GPL-3.0 WITH Classpath-exception-2.0"], name = "mmr-gadget" }, + { allow = ["GPL-3.0 WITH Classpath-exception-2.0"], name = "node-bench" }, + { allow = ["GPL-3.0 WITH Classpath-exception-2.0"], name = "node-cli" }, + { allow = ["GPL-3.0 WITH Classpath-exception-2.0"], name = "node-inspect" }, + { allow = ["GPL-3.0 WITH Classpath-exception-2.0"], name = "node-testing" }, + { allow = ["GPL-3.0 WITH Classpath-exception-2.0"], name = "sc-authority-discovery" }, + { allow = ["GPL-3.0 WITH Classpath-exception-2.0"], name = "sc-basic-authorship" }, + { allow = ["GPL-3.0 WITH Classpath-exception-2.0"], name = "sc-block-builder" }, + { allow = ["GPL-3.0 WITH Classpath-exception-2.0"], name = "sc-chain-spec" }, + { allow = ["GPL-3.0 WITH Classpath-exception-2.0"], name = "sc-chain-spec-derive" }, + { allow = ["GPL-3.0 WITH Classpath-exception-2.0"], name = "sc-cli" }, + { allow = ["GPL-3.0 WITH Classpath-exception-2.0"], name = "sc-client-api" }, + { allow = ["GPL-3.0 WITH Classpath-exception-2.0"], name = "sc-client-db" }, + { allow = ["GPL-3.0 WITH Classpath-exception-2.0"], name = "sc-consensus" }, + { allow = ["GPL-3.0 WITH Classpath-exception-2.0"], name = "sc-consensus-aura" }, + { allow = ["GPL-3.0 WITH Classpath-exception-2.0"], name = "sc-consensus-babe" }, + { allow = ["GPL-3.0 WITH Classpath-exception-2.0"], name = "sc-consensus-babe-rpc" }, + { allow = ["GPL-3.0 WITH Classpath-exception-2.0"], name = "sc-consensus-beefy" }, + { allow = ["GPL-3.0 WITH Classpath-exception-2.0"], name = "sc-consensus-beefy-rpc" }, + { allow = ["GPL-3.0 WITH Classpath-exception-2.0"], name = "sc-consensus-epochs" }, + { allow = ["GPL-3.0 WITH Classpath-exception-2.0"], name = "sc-consensus-grandpa" }, + { allow = ["GPL-3.0 WITH Classpath-exception-2.0"], name = "sc-consensus-grandpa-rpc" }, + { allow = ["GPL-3.0 WITH Classpath-exception-2.0"], name = "sc-consensus-manual-seal" }, + { allow = ["GPL-3.0 WITH Classpath-exception-2.0"], name = "sc-consensus-pow" }, + { allow = ["GPL-3.0 WITH Classpath-exception-2.0"], name = "sc-consensus-slots" }, + { allow = ["GPL-3.0 WITH Classpath-exception-2.0"], name = "sc-executor" }, + { allow = ["GPL-3.0 WITH Classpath-exception-2.0"], name = "sc-executor-common" }, + { allow = ["GPL-3.0 WITH Classpath-exception-2.0"], name = "sc-executor-wasmi" }, + { allow = ["GPL-3.0 WITH Classpath-exception-2.0"], name = "sc-executor-wasmtime" }, + { allow = ["GPL-3.0 WITH Classpath-exception-2.0"], name = "sc-informant" }, + { allow = ["GPL-3.0 WITH Classpath-exception-2.0"], name = "sc-keystore" }, + { allow = ["GPL-3.0 WITH Classpath-exception-2.0"], name = "sc-network" }, + { allow = ["GPL-3.0 WITH Classpath-exception-2.0"], name = "sc-network-bitswap" }, + { allow = ["GPL-3.0 WITH Classpath-exception-2.0"], name = "sc-network-common" }, + { allow = ["GPL-3.0 WITH Classpath-exception-2.0"], name = "sc-network-gossip" }, + { allow = ["GPL-3.0 WITH Classpath-exception-2.0"], name = "sc-network-light" }, + { allow = ["GPL-3.0 WITH Classpath-exception-2.0"], name = "sc-network-sync" }, + { allow = ["GPL-3.0 WITH Classpath-exception-2.0"], name = "sc-network-test" }, + { allow = ["GPL-3.0 WITH Classpath-exception-2.0"], name = "sc-network-transactions" }, + { allow = ["GPL-3.0 WITH Classpath-exception-2.0"], name = "sc-offchain" }, + { allow = ["GPL-3.0 WITH Classpath-exception-2.0"], name = "sc-peerset" }, + { allow = ["GPL-3.0 WITH Classpath-exception-2.0"], name = "sc-proposer-metrics" }, + { allow = ["GPL-3.0 WITH Classpath-exception-2.0"], name = "sc-rpc" }, + { allow = ["GPL-3.0 WITH Classpath-exception-2.0"], name = "sc-rpc-api" }, + { allow = ["GPL-3.0 WITH Classpath-exception-2.0"], name = "sc-rpc-server" }, + { allow = ["GPL-3.0 WITH Classpath-exception-2.0"], name = "sc-rpc-spec-v2" }, + { allow = ["GPL-3.0 WITH Classpath-exception-2.0"], name = "sc-runtime-test" }, + { allow = ["GPL-3.0 WITH Classpath-exception-2.0"], name = "sc-service" }, + { allow = ["GPL-3.0 WITH Classpath-exception-2.0"], name = "sc-service-test" }, + { allow = ["GPL-3.0 WITH Classpath-exception-2.0"], name = "sc-state-db" }, + { allow = ["GPL-3.0 WITH Classpath-exception-2.0"], name = "sc-storage-monitor" }, + { allow = ["GPL-3.0 WITH Classpath-exception-2.0"], name = "sc-sysinfo" }, + { allow = ["GPL-3.0 WITH Classpath-exception-2.0"], name = "sc-telemetry" }, + { allow = ["GPL-3.0 WITH Classpath-exception-2.0"], name = "sc-tracing" }, + { allow = ["GPL-3.0 WITH Classpath-exception-2.0"], name = "sc-transaction-pool" }, + { allow = ["GPL-3.0 WITH Classpath-exception-2.0"], name = "sc-transaction-pool-api" }, + { allow = ["GPL-3.0 WITH Classpath-exception-2.0"], name = "subkey" }, ] # Some crates don't have (easily) machine readable licensing information, @@ -113,10 +101,8 @@ exceptions = [ [[licenses.clarify]] # The name of the crate the clarification applies to name = "ring" -# THe optional version constraint for the crate -#version = "*" # The SPDX expression for the license requirements of the crate -expression = "OpenSSL" +expression = "MIT AND ISC AND OpenSSL" # One or more files in the crate's source used as the "source of truth" for # the license expression. If the contents match, the clarification will be used # when running the license check, otherwise the clarification will be ignored @@ -126,67 +112,3 @@ license-files = [ # Each entry is a crate relative path, and the (opaque) hash of its contents { path = "LICENSE", hash = 0xbd0eed23 } ] -[[licenses.clarify]] -name = "webpki" -expression = "ISC" -license-files = [{ path = "LICENSE", hash = 0x001c7e6c }] - -[licenses.private] -# If true, ignores workspace crates that aren't published, or are only -# published to private registries -ignore = false -# One or more private registries that you might publish crates to, if a crate -# is only published to private registries, and ignore is true, the crate will -# not have its license(s) checked -registries = [ - #"https://sekretz.com/registry -] - -# This section is considered when running `cargo deny check bans`. -# More documentation about the 'bans' section can be found here: -# https://embarkstudios.github.io/cargo-deny/checks/bans/cfg.html -[bans] -# Lint level for when multiple versions of the same crate are detected -multiple-versions = "warn" -# The graph highlighting used when creating dotgraphs for crates -# with multiple versions -# * lowest-version - The path to the lowest versioned duplicate is highlighted -# * simplest-path - The path to the version with the fewest edges is highlighted -# * all - Both lowest-version and simplest-path are used -highlight = "lowest-version" -# List of crates that are allowed. Use with care! -allow = [ - #{ name = "ansi_term", version = "=0.11.0" }, -] -# List of crates to deny -deny = [ - # Each entry the name of a crate and a version range. If version is - # not specified, all versions will be matched. -] -# Certain crates/versions that will be skipped when doing duplicate detection. -skip = [ - #{ name = "ansi_term", version = "=0.11.0" }, -] -# Similarly to `skip` allows you to skip certain crates during duplicate -# detection. Unlike skip, it also includes the entire tree of transitive -# dependencies starting at the specified crate, up to a certain depth, which is -# by default infinite -skip-tree = [ - #{ name = "ansi_term", version = "=0.11.0", depth = 20 }, -] - -# This section is considered when running `cargo deny check sources`. -# More documentation about the 'sources' section can be found here: -# https://embarkstudios.github.io/cargo-deny/checks/sources/cfg.html -[sources] -# Lint level for what to happen when a crate from a crate registry that is not -# in the allow list is encountered -unknown-registry = "deny" -# Lint level for what to happen when a crate from a git repository that is not -# in the allow list is encountered -unknown-git = "warn" -# List of URLs for allowed crate registries. Defaults to the crates.io index -# if not specified. If it is specified but empty, no registries are allowed. -allow-registry = ["https://github.com/rust-lang/crates.io-index"] -# List of URLs for allowed Git repositories -allow-git = [] diff --git a/scripts/ci/gitlab/pipeline/test.yml b/scripts/ci/gitlab/pipeline/test.yml index bf7803d56..7e84694c2 100644 --- a/scripts/ci/gitlab/pipeline/test.yml +++ b/scripts/ci/gitlab/pipeline/test.yml @@ -21,27 +21,27 @@ find-fail-ci-phrase: exit 0; fi -cargo-deny: +cargo-deny-licenses: stage: test extends: - .docker-env - - .nightly-pipeline + - .test-refs + variables: + CARGO_DENY_CMD: "cargo deny --all-features check licenses -c ./scripts/ci/deny.toml" script: - rusty-cachier snapshot create - - cargo deny check --hide-inclusion-graph -c ./scripts/ci/deny.toml + - $CARGO_DENY_CMD --hide-inclusion-graph - rusty-cachier cache upload after_script: - !reference [.rusty-cachier, after_script] - echo "___The complete log is in the artifacts___" - - cargo deny check -c ./scripts/ci/deny.toml 2> deny.log + - $CARGO_DENY_CMD 2> deny.log artifacts: name: $CI_COMMIT_SHORT_SHA expire_in: 3 days when: always paths: - deny.log - # FIXME: Temporarily allow to fail. - allow_failure: true cargo-fmt: stage: test From d8c320941ad83bc8aa9b7136776553cd345e5c17 Mon Sep 17 00:00:00 2001 From: Jegor Sidorenko <5252494+jsidorenko@users.noreply.github.com> Date: Thu, 20 Apr 2023 15:38:45 +0300 Subject: [PATCH 401/558] [NFTs] Improve offchain signature validation (#13960) * Improve signature validation * Rework --- frame/nfts/src/common_functions.rs | 25 +++++++++++++++++++++++++ frame/nfts/src/lib.rs | 9 +++------ frame/nfts/src/tests.rs | 28 ++++++++++++++++++++++++++++ 3 files changed, 56 insertions(+), 6 deletions(-) diff --git a/frame/nfts/src/common_functions.rs b/frame/nfts/src/common_functions.rs index 1ef6591db..a3486edec 100644 --- a/frame/nfts/src/common_functions.rs +++ b/frame/nfts/src/common_functions.rs @@ -18,6 +18,7 @@ //! Various pieces of common functionality. use crate::*; +use frame_support::pallet_prelude::*; impl, I: 'static> Pallet { /// Get the owner of the item, if the item exists. @@ -30,6 +31,30 @@ impl, I: 'static> Pallet { Collection::::get(collection).map(|i| i.owner) } + /// Validate the `data` was signed by `signer` and the `signature` is correct. + pub fn validate_signature( + data: &Vec, + signature: &T::OffchainSignature, + signer: &T::AccountId, + ) -> DispatchResult { + if signature.verify(&**data, &signer) { + return Ok(()) + } + + // NOTE: for security reasons modern UIs implicitly wrap the data requested to sign into + // , that's why we support both wrapped and raw versions. + let prefix = b""; + let suffix = b""; + let mut wrapped: Vec = Vec::with_capacity(data.len() + prefix.len() + suffix.len()); + wrapped.extend(prefix); + wrapped.extend(data); + wrapped.extend(suffix); + + ensure!(signature.verify(&*wrapped, &signer), Error::::WrongSignature); + + Ok(()) + } + #[cfg(any(test, feature = "runtime-benchmarks"))] pub fn set_next_id(id: T::CollectionId) { NextCollectionId::::set(Some(id)); diff --git a/frame/nfts/src/lib.rs b/frame/nfts/src/lib.rs index f4d0e4159..4796819df 100644 --- a/frame/nfts/src/lib.rs +++ b/frame/nfts/src/lib.rs @@ -50,7 +50,7 @@ use frame_support::traits::{ }; use frame_system::Config as SystemConfig; use sp_runtime::{ - traits::{Saturating, StaticLookup, Zero}, + traits::{IdentifyAccount, Saturating, StaticLookup, Verify, Zero}, RuntimeDebug, }; use sp_std::prelude::*; @@ -69,7 +69,6 @@ pub mod pallet { use super::*; use frame_support::{pallet_prelude::*, traits::ExistenceRequirement}; use frame_system::pallet_prelude::*; - use sp_runtime::traits::{IdentifyAccount, Verify}; /// The current storage version. const STORAGE_VERSION: StorageVersion = StorageVersion::new(1); @@ -1841,8 +1840,7 @@ pub mod pallet { signer: T::AccountId, ) -> DispatchResult { let origin = ensure_signed(origin)?; - let msg = Encode::encode(&mint_data); - ensure!(signature.verify(&*msg, &signer), Error::::WrongSignature); + Self::validate_signature(&Encode::encode(&mint_data), &signature, &signer)?; Self::do_mint_pre_signed(origin, mint_data, signer) } @@ -1868,8 +1866,7 @@ pub mod pallet { signer: T::AccountId, ) -> DispatchResult { let origin = ensure_signed(origin)?; - let msg = Encode::encode(&data); - ensure!(signature.verify(&*msg, &signer), Error::::WrongSignature); + Self::validate_signature(&Encode::encode(&data), &signature, &signer)?; Self::do_set_attributes_pre_signed(origin, data, signer) } } diff --git a/frame/nfts/src/tests.rs b/frame/nfts/src/tests.rs index d36f8b54e..4ab12f050 100644 --- a/frame/nfts/src/tests.rs +++ b/frame/nfts/src/tests.rs @@ -3140,6 +3140,34 @@ fn add_remove_item_attributes_approval_should_work() { }) } +#[test] +fn validate_signature() { + new_test_ext().execute_with(|| { + let user_1_pair = sp_core::sr25519::Pair::from_string("//Alice", None).unwrap(); + let user_1_signer = MultiSigner::Sr25519(user_1_pair.public()); + let user_1 = user_1_signer.clone().into_account(); + let mint_data: PreSignedMint = PreSignedMint { + collection: 0, + item: 0, + attributes: vec![], + metadata: vec![], + only_account: None, + deadline: 100000, + }; + let encoded_data = Encode::encode(&mint_data); + let signature = MultiSignature::Sr25519(user_1_pair.sign(&encoded_data)); + assert_ok!(Nfts::validate_signature(&encoded_data, &signature, &user_1)); + + let mut wrapped_data: Vec = Vec::new(); + wrapped_data.extend(b""); + wrapped_data.extend(&encoded_data); + wrapped_data.extend(b""); + + let signature = MultiSignature::Sr25519(user_1_pair.sign(&wrapped_data)); + assert_ok!(Nfts::validate_signature(&encoded_data, &signature, &user_1)); + }) +} + #[test] fn pre_signed_mints_should_work() { new_test_ext().execute_with(|| { From 9f0d54a10a055d9809aebc60a0db9890e3530ce0 Mon Sep 17 00:00:00 2001 From: Aaro Altonen <48052676+altonen@users.noreply.github.com> Date: Thu, 20 Apr 2023 16:27:05 +0300 Subject: [PATCH 402/558] Keep track of the pending response for each peer individually (#13941) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Keep track of the pending response for each peer individually When peer disconnects or the syncing is restarted, remove the pending response so syncing won't start sending duplicate requests/receive stale responses from disconnected peers. Before this commit pending responses where stored in `FuturesUnordered` which made it hard to keep track of pending responses for each individual peer. * Update client/network/sync/src/lib.rs Co-authored-by: Bastian Köcher * ".git/.scripts/commands/fmt/fmt.sh" * Apply suggestions from code review Co-authored-by: Dmitry Markin Co-authored-by: Sebastian Kunert * Update client/network/sync/src/lib.rs --------- Co-authored-by: Bastian Köcher Co-authored-by: command-bot <> Co-authored-by: Dmitry Markin Co-authored-by: Sebastian Kunert --- client/network/sync/src/lib.rs | 116 ++++++++++++++++++++++++++++++--- 1 file changed, 108 insertions(+), 8 deletions(-) diff --git a/client/network/sync/src/lib.rs b/client/network/sync/src/lib.rs index e112a1715..29c4bc840 100644 --- a/client/network/sync/src/lib.rs +++ b/client/network/sync/src/lib.rs @@ -344,7 +344,7 @@ pub struct ChainSync { /// Protocol name used to send out warp sync requests warp_sync_protocol_name: Option, /// Pending responses - pending_responses: FuturesUnordered>, + pending_responses: HashMap>, /// Handle to import queue. import_queue: Box>, /// Metrics. @@ -1238,6 +1238,7 @@ where gap_sync.blocks.clear_peer_download(who) } self.peers.remove(who); + self.pending_responses.remove(who); self.extra_justifications.peer_disconnected(who); self.allowed_requests.set_all(); self.fork_targets.retain(|_, target| { @@ -1360,7 +1361,7 @@ where if self.peers.contains_key(&who) { self.pending_responses - .push(Box::pin(async move { (who, PeerRequest::Block(request), rx.await) })); + .insert(who, Box::pin(async move { (who, PeerRequest::Block(request), rx.await) })); } match self.encode_block_request(&opaque_req) { @@ -1457,7 +1458,7 @@ where .notifications_protocol .clone() .into(), - pending_responses: Default::default(), + pending_responses: HashMap::new(), import_queue, metrics: if let Some(r) = &metrics_registry { match SyncingMetrics::register(r) { @@ -1810,6 +1811,9 @@ where return None } + // since the request is not a justification, remove it from pending responses + self.pending_responses.remove(&id); + // handle peers that were in other states. match self.new_peer(id, p.best_hash, p.best_number) { Ok(None) => None, @@ -2009,7 +2013,7 @@ where if self.peers.contains_key(&who) { self.pending_responses - .push(Box::pin(async move { (who, PeerRequest::State, rx.await) })); + .insert(who, Box::pin(async move { (who, PeerRequest::State, rx.await) })); } match self.encode_state_request(&request) { @@ -2037,7 +2041,7 @@ where if self.peers.contains_key(&who) { self.pending_responses - .push(Box::pin(async move { (who, PeerRequest::WarpProof, rx.await) })); + .insert(who, Box::pin(async move { (who, PeerRequest::WarpProof, rx.await) })); } match &self.warp_sync_protocol_name { @@ -2175,9 +2179,20 @@ where } fn poll_pending_responses(&mut self, cx: &mut std::task::Context) -> Poll> { - while let Poll::Ready(Some((id, request, response))) = - self.pending_responses.poll_next_unpin(cx) - { + let ready_responses = self + .pending_responses + .values_mut() + .filter_map(|future| match future.poll_unpin(cx) { + Poll::Pending => None, + Poll::Ready(result) => Some(result), + }) + .collect::>(); + + for (id, request, response) in ready_responses { + self.pending_responses + .remove(&id) + .expect("Logic error: peer id from pending response is missing in the map."); + match response { Ok(Ok(resp)) => match request { PeerRequest::Block(req) => { @@ -4116,4 +4131,89 @@ mod test { let state = AncestorSearchState::::BinarySearch(1, 3); assert!(handle_ancestor_search_state(&state, 2, true).is_none()); } + + #[test] + fn sync_restart_removes_block_but_not_justification_requests() { + let mut client = Arc::new(TestClientBuilder::new().build()); + let block_announce_validator = Box::new(DefaultBlockAnnounceValidator); + let import_queue = Box::new(sc_consensus::import_queue::mock::MockImportQueueHandle::new()); + let (_chain_sync_network_provider, chain_sync_network_handle) = + NetworkServiceProvider::new(); + let (mut sync, _) = ChainSync::new( + SyncMode::Full, + client.clone(), + ProtocolId::from("test-protocol-name"), + &Some(String::from("test-fork-id")), + Roles::from(&Role::Full), + block_announce_validator, + 1, + 64, + None, + None, + chain_sync_network_handle, + import_queue, + ProtocolName::from("block-request"), + ProtocolName::from("state-request"), + None, + ) + .unwrap(); + + let peers = vec![PeerId::random(), PeerId::random()]; + + let mut new_blocks = |n| { + for _ in 0..n { + let block = client.new_block(Default::default()).unwrap().build().unwrap().block; + block_on(client.import(BlockOrigin::Own, block.clone())).unwrap(); + } + + let info = client.info(); + (info.best_hash, info.best_number) + }; + + let (b1_hash, b1_number) = new_blocks(50); + + // add new peer and request blocks from them + sync.new_peer(peers[0], Hash::random(), 42).unwrap(); + + // we wil send block requests to these peers + // for these blocks we don't know about + for (peer, request) in sync.block_requests() { + sync.send_block_request(peer, request); + } + + // add a new peer at a known block + sync.new_peer(peers[1], b1_hash, b1_number).unwrap(); + + // we request a justification for a block we have locally + sync.request_justification(&b1_hash, b1_number); + + // the justification request should be scheduled to the + // new peer which is at the given block + let mut requests = sync.justification_requests().collect::>(); + assert_eq!(requests.len(), 1); + let (peer, request) = requests.remove(0); + sync.send_block_request(peer, request); + + assert!(!std::matches!( + sync.peers.get(&peers[0]).unwrap().state, + PeerSyncState::DownloadingJustification(_), + )); + assert_eq!( + sync.peers.get(&peers[1]).unwrap().state, + PeerSyncState::DownloadingJustification(b1_hash), + ); + assert_eq!(sync.pending_responses.len(), 2); + + let requests = sync.restart().collect::>(); + assert!(requests.iter().any(|res| res.as_ref().unwrap().0 == peers[0])); + + assert_eq!(sync.pending_responses.len(), 1); + assert!(sync.pending_responses.get(&peers[1]).is_some()); + assert_eq!( + sync.peers.get(&peers[1]).unwrap().state, + PeerSyncState::DownloadingJustification(b1_hash), + ); + sync.peer_disconnected(&peers[1]); + assert_eq!(sync.pending_responses.len(), 0); + } } From e44b43e155b795b69595b32b1cbc6b52e9d4c9ac Mon Sep 17 00:00:00 2001 From: Aaro Altonen <48052676+altonen@users.noreply.github.com> Date: Fri, 21 Apr 2023 11:22:26 +0300 Subject: [PATCH 403/558] Evict inactive peers from `SyncingEngine` (#13829) * Evict inactive peers from `SyncingEngine` If both halves of the block announce notification stream have been inactive for 2 minutes, report the peer and disconnect it, allowing `SyncingEngine` to free up a slot for some other peer that hopefully is more active. This needs to be done because the node may falsely believe it has open connections to peers because the inbound substream can be closed without any notification and closed outbound substream is noticed only when node attempts to write to it which may not happen if the node has nothing to send. * zzz * wip * Evict peers only when timeout expires * Use `debug!()` --------- Co-authored-by: parity-processbot <> --- client/network/sync/src/engine.rs | 50 +++++++++++++++++++++++++++++++ 1 file changed, 50 insertions(+) diff --git a/client/network/sync/src/engine.rs b/client/network/sync/src/engine.rs index 6fb618a57..eca0ebfe4 100644 --- a/client/network/sync/src/engine.rs +++ b/client/network/sync/src/engine.rs @@ -67,6 +67,7 @@ use std::{ Arc, }, task::Poll, + time::{Duration, Instant}, }; /// Interval at which we perform time based maintenance @@ -75,12 +76,19 @@ const TICK_TIMEOUT: std::time::Duration = std::time::Duration::from_millis(1100) /// Maximum number of known block hashes to keep for a peer. const MAX_KNOWN_BLOCKS: usize = 1024; // ~32kb per peer + LruHashSet overhead +/// If the block announces stream to peer has been inactive for two minutes meaning local node +/// has not sent or received block announcements to/from the peer, report the node for inactivity, +/// disconnect it and attempt to establish connection to some other peer. +const INACTIVITY_EVICT_THRESHOLD: Duration = Duration::from_secs(30); + mod rep { use sc_peerset::ReputationChange as Rep; /// Peer has different genesis. pub const GENESIS_MISMATCH: Rep = Rep::new_fatal("Genesis mismatch"); /// Peer send us a block announcement that failed at validation. pub const BAD_BLOCK_ANNOUNCEMENT: Rep = Rep::new(-(1 << 12), "Bad block announcement"); + /// Block announce substream with the peer has been inactive too long + pub const INACTIVE_SUBSTREAM: Rep = Rep::new(-(1 << 10), "Inactive block announce substream"); } struct Metrics { @@ -160,6 +168,10 @@ pub struct Peer { pub known_blocks: LruHashSet, /// Notification sink. sink: NotificationsSink, + /// Instant when the last notification was sent to peer. + last_notification_sent: Instant, + /// Instant when the last notification was received from peer. + last_notification_received: Instant, } pub struct SyncingEngine { @@ -200,6 +212,9 @@ pub struct SyncingEngine { /// All connected peers. Contains both full and light node peers. peers: HashMap>, + /// Evicted peers + evicted: HashSet, + /// List of nodes for which we perform additional logging because they are important for the /// user. important_peers: HashSet, @@ -353,6 +368,7 @@ where chain_sync, network_service, peers: HashMap::new(), + evicted: HashSet::new(), block_announce_data_cache: LruCache::new(cache_capacity), block_announce_protocol_name, num_connected: num_connected.clone(), @@ -516,6 +532,7 @@ where }, }; peer.known_blocks.insert(hash); + peer.last_notification_received = Instant::now(); if peer.info.roles.is_full() { let is_best = match announce.state.unwrap_or(BlockState::Best) { @@ -566,6 +583,7 @@ where data: Some(data.clone()), }; + peer.last_notification_sent = Instant::now(); peer.sink.send_sync_notification(message.encode()); } } @@ -596,6 +614,35 @@ where while let Poll::Ready(()) = self.tick_timeout.poll_unpin(cx) { self.report_metrics(); + + // go over all connected peers and check if any of them have been idle for a while. Idle + // in this case means that we haven't sent or received block announcements to/from this + // peer. If that is the case, because of #5685, it could be that the block announces + // substream is not actually open and and this peer is just wasting a slot and is should + // be replaced with some other node that is willing to send us block announcements. + for (id, peer) in self.peers.iter() { + // because of a delay between disconnecting a peer in `SyncingEngine` and getting + // the response back from `Protocol`, a peer might be reported and disconnect + // multiple times. To prevent this from happening (until the underlying issue is + // fixed), keep track of evicted peers and report and disconnect them only once. + if self.evicted.contains(id) { + continue + } + + let last_received_late = + peer.last_notification_received.elapsed() > INACTIVITY_EVICT_THRESHOLD; + let last_sent_late = + peer.last_notification_sent.elapsed() > INACTIVITY_EVICT_THRESHOLD; + + if last_received_late && last_sent_late { + log::debug!(target: "sync", "evict peer {id} since it has been idling for too long"); + self.network_service.report_peer(*id, rep::INACTIVE_SUBSTREAM); + self.network_service + .disconnect_peer(*id, self.block_announce_protocol_name.clone()); + self.evicted.insert(*id); + } + } + self.tick_timeout.reset(TICK_TIMEOUT); } @@ -692,6 +739,7 @@ where }, }, sc_network::SyncEvent::NotificationStreamClosed { remote } => { + self.evicted.remove(&remote); if self.on_sync_peer_disconnected(remote).is_err() { log::trace!( target: "sync", @@ -844,6 +892,8 @@ where NonZeroUsize::new(MAX_KNOWN_BLOCKS).expect("Constant is nonzero"), ), sink, + last_notification_sent: Instant::now(), + last_notification_received: Instant::now(), }; let req = if peer.info.roles.is_full() { From dad40923604250c47d99f3801a61bde1dc36664e Mon Sep 17 00:00:00 2001 From: Liam Aharon Date: Fri, 21 Apr 2023 18:46:54 +1000 Subject: [PATCH 404/558] `balances` `impl_currency` WithdrawReasons note (#13964) * add withdrawreason note * improve comment * Update frame/balances/src/impl_currency.rs Co-authored-by: Kian Paimani <5588131+kianenigma@users.noreply.github.com> * cargo fmt +nightly --------- Co-authored-by: Kian Paimani <5588131+kianenigma@users.noreply.github.com> --- frame/balances/src/impl_currency.rs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/frame/balances/src/impl_currency.rs b/frame/balances/src/impl_currency.rs index 329ea289f..ff8cc71d6 100644 --- a/frame/balances/src/impl_currency.rs +++ b/frame/balances/src/impl_currency.rs @@ -16,6 +16,9 @@ // limitations under the License. //! Implementations for the `Currency` family of traits. +//! +//! Note that `WithdrawReasons` are intentionally not used for anything in this implementation and +//! are expected to be removed in the near future, once migration to `fungible::*` traits is done. use super::*; use frame_support::{ From 2059dca9764f2f28c91011f6db2e74516142cb32 Mon Sep 17 00:00:00 2001 From: Liam Aharon Date: Fri, 21 Apr 2023 19:16:40 +1000 Subject: [PATCH 405/558] try-runtime: dynamic storage query sizes (#13923) * improve batch rpc error message * wip aimd storage data fetch * complete aimd function refactor * make batch_request function async * improve function name * fix load_child_remote issue * slight efficiency improvement * improve logs and variable name * remove redundant comment * improve comment * address pr comments * Update utils/frame/remote-externalities/src/lib.rs Co-authored-by: Niklas Adolfsson * simplify client handling * fix type issue * fix clippy issue * try to trigger ci * try to trigger ci --------- Co-authored-by: Niklas Adolfsson --- Cargo.lock | 34 ++ utils/frame/remote-externalities/Cargo.toml | 2 + utils/frame/remote-externalities/src/lib.rs | 519 +++++++++++--------- utils/frame/try-runtime/cli/src/lib.rs | 12 - 4 files changed, 323 insertions(+), 244 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index c7f74a495..b31699318 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -395,6 +395,17 @@ dependencies = [ "futures-lite", ] +[[package]] +name = "async-recursion" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0e97ce7de6cf12de5d7226c73f5ba9811622f4db3a5b91b55c53e987e5f91cba" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.14", +] + [[package]] name = "async-stream" version = "0.3.4" @@ -2427,8 +2438,10 @@ dependencies = [ name = "frame-remote-externalities" version = "0.10.0-dev" dependencies = [ + "async-recursion", "frame-support", "futures", + "jsonrpsee", "log", "pallet-elections-phragmen", "parity-scale-codec", @@ -3202,6 +3215,7 @@ dependencies = [ "rustls-native-certs", "tokio", "tokio-rustls", + "webpki-roots", ] [[package]] @@ -3468,6 +3482,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7d291e3a5818a2384645fd9756362e6d89cf0541b0b916fa7702ea4a9833608e" dependencies = [ "jsonrpsee-core", + "jsonrpsee-http-client", "jsonrpsee-proc-macros", "jsonrpsee-server", "jsonrpsee-types", @@ -3524,6 +3539,25 @@ dependencies = [ "tracing", ] +[[package]] +name = "jsonrpsee-http-client" +version = "0.16.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cc345b0a43c6bc49b947ebeb936e886a419ee3d894421790c969cc56040542ad" +dependencies = [ + "async-trait", + "hyper", + "hyper-rustls", + "jsonrpsee-core", + "jsonrpsee-types", + "rustc-hash", + "serde", + "serde_json", + "thiserror", + "tokio", + "tracing", +] + [[package]] name = "jsonrpsee-proc-macros" version = "0.16.2" diff --git a/utils/frame/remote-externalities/Cargo.toml b/utils/frame/remote-externalities/Cargo.toml index 8611ae498..d3909af34 100644 --- a/utils/frame/remote-externalities/Cargo.toml +++ b/utils/frame/remote-externalities/Cargo.toml @@ -12,6 +12,7 @@ description = "An externalities provided environment that can load itself from r targets = ["x86_64-unknown-linux-gnu"] [dependencies] +jsonrpsee = { version = "0.16.2", features = ["http-client"] } codec = { package = "parity-scale-codec", version = "3.2.2" } log = "0.4.17" serde = "1.0.136" @@ -22,6 +23,7 @@ sp-runtime = { version = "7.0.0", path = "../../../primitives/runtime" } tokio = { version = "1.22.0", features = ["macros", "rt-multi-thread"] } substrate-rpc-client = { path = "../rpc/client" } futures = "0.3" +async-recursion = "1.0.4" [dev-dependencies] frame-support = { version = "4.0.0-dev", path = "../../../frame/support" } diff --git a/utils/frame/remote-externalities/src/lib.rs b/utils/frame/remote-externalities/src/lib.rs index ee3424088..b60e2bc75 100644 --- a/utils/frame/remote-externalities/src/lib.rs +++ b/utils/frame/remote-externalities/src/lib.rs @@ -20,8 +20,13 @@ //! An equivalent of `sp_io::TestExternalities` that can load its state from a remote substrate //! based chain, or a local state snapshot file. +use async_recursion::async_recursion; use codec::{Decode, Encode}; use futures::{channel::mpsc, stream::StreamExt}; +use jsonrpsee::{ + core::params::ArrayParams, + http_client::{HttpClient, HttpClientBuilder}, +}; use log::*; use serde::de::DeserializeOwned; use sp_core::{ @@ -35,6 +40,7 @@ use sp_core::{ pub use sp_io::TestExternalities; use sp_runtime::{traits::Block as BlockT, StateVersion}; use std::{ + cmp::max, fs, num::NonZeroUsize, ops::{Deref, DerefMut}, @@ -42,19 +48,14 @@ use std::{ sync::Arc, thread, }; -use substrate_rpc_client::{ - rpc_params, ws_client, BatchRequestBuilder, ChainApi, ClientT, StateApi, WsClient, -}; +use substrate_rpc_client::{rpc_params, BatchRequestBuilder, ChainApi, ClientT, StateApi}; type KeyValue = (StorageKey, StorageData); type TopKeyValues = Vec; type ChildKeyValues = Vec<(ChildInfo, Vec)>; const LOG_TARGET: &str = "remote-ext"; -const DEFAULT_WS_ENDPOINT: &str = "wss://rpc.polkadot.io:443"; -const DEFAULT_VALUE_DOWNLOAD_BATCH: usize = 4096; -// NOTE: increasing this value does not seem to impact speed all that much. -const DEFAULT_KEY_DOWNLOAD_PAGE: u32 = 1000; +const DEFAULT_HTTP_ENDPOINT: &str = "https://rpc.polkadot.io:443"; /// The snapshot that we store on disk. #[derive(Decode, Encode)] struct Snapshot { @@ -117,36 +118,51 @@ pub struct OfflineConfig { pub enum Transport { /// Use the `URI` to open a new WebSocket connection. Uri(String), - /// Use existing WebSocket connection. - RemoteClient(Arc), + /// Use HTTP connection. + RemoteClient(Arc), } impl Transport { - fn as_client(&self) -> Option<&WsClient> { + fn as_client(&self) -> Option<&HttpClient> { match self { Self::RemoteClient(client) => Some(client), _ => None, } } - fn as_client_cloned(&self) -> Option> { + fn as_client_cloned(&self) -> Option> { match self { Self::RemoteClient(client) => Some(client.clone()), _ => None, } } - // Open a new WebSocket connection if it's not connected. - async fn map_uri(&mut self) -> Result<(), &'static str> { + // Build an HttpClient from a URI. + async fn init(&mut self) -> Result<(), &'static str> { if let Self::Uri(uri) = self { log::debug!(target: LOG_TARGET, "initializing remote client to {:?}", uri); - let ws_client = ws_client(uri).await.map_err(|e| { + // If we have a ws uri, try to convert it to an http uri. + // We use an HTTP client rather than WS because WS starts to choke with "accumulated + // message length exceeds maximum" errors after processing ~10k keys when fetching + // from a node running a default configuration. + let uri = if uri.starts_with("ws://") { + let uri = uri.replace("ws://", "http://"); + log::info!(target: LOG_TARGET, "replacing ws:// in uri with http://: {:?} (ws is currently unstable for fetching remote storage, for more see https://github.com/paritytech/jsonrpsee/issues/1086)", uri); + uri + } else if uri.starts_with("wss://") { + let uri = uri.replace("wss://", "https://"); + log::info!(target: LOG_TARGET, "replacing wss:// in uri with https://: {:?} (ws is currently unstable for fetching remote storage, for more see https://github.com/paritytech/jsonrpsee/issues/1086)", uri); + uri + } else { + uri.clone() + }; + let http_client = HttpClientBuilder::default().build(uri).map_err(|e| { log::error!(target: LOG_TARGET, "error: {:?}", e); - "failed to build ws client" + "failed to build http client" })?; - *self = Self::RemoteClient(Arc::new(ws_client)) + *self = Self::RemoteClient(Arc::new(http_client)) } Ok(()) @@ -159,8 +175,8 @@ impl From for Transport { } } -impl From> for Transport { - fn from(client: Arc) -> Self { +impl From> for Transport { + fn from(client: Arc) -> Self { Transport::RemoteClient(client) } } @@ -189,18 +205,18 @@ pub struct OnlineConfig { } impl OnlineConfig { - /// Return rpc (ws) client reference. - fn rpc_client(&self) -> &WsClient { + /// Return rpc (http) client reference. + fn rpc_client(&self) -> &HttpClient { self.transport .as_client() - .expect("ws client must have been initialized by now; qed.") + .expect("http client must have been initialized by now; qed.") } - /// Return a cloned rpc (ws) client, suitable for being moved to threads. - fn rpc_client_cloned(&self) -> Arc { + /// Return a cloned rpc (http) client, suitable for being moved to threads. + fn rpc_client_cloned(&self) -> Arc { self.transport .as_client_cloned() - .expect("ws client must have been initialized by now; qed.") + .expect("http client must have been initialized by now; qed.") } fn at_expected(&self) -> B::Hash { @@ -211,7 +227,7 @@ impl OnlineConfig { impl Default for OnlineConfig { fn default() -> Self { Self { - transport: Transport::from(DEFAULT_WS_ENDPOINT.to_owned()), + transport: Transport::from(DEFAULT_HTTP_ENDPOINT.to_owned()), child_trie: true, at: None, state_snapshot: None, @@ -307,10 +323,16 @@ where B::Hash: DeserializeOwned, B::Header: DeserializeOwned, { + const BATCH_SIZE_INCREASE_FACTOR: f32 = 1.10; + const BATCH_SIZE_DECREASE_FACTOR: f32 = 0.50; + const INITIAL_BATCH_SIZE: usize = 5000; + // NOTE: increasing this value does not seem to impact speed all that much. + const DEFAULT_KEY_DOWNLOAD_PAGE: u32 = 1000; + /// Get the number of threads to use. fn threads() -> NonZeroUsize { thread::available_parallelism() - .unwrap_or(NonZeroUsize::new(4usize).expect("4 is non-zero; qed")) + .unwrap_or(NonZeroUsize::new(1usize).expect("4 is non-zero; qed")) } async fn rpc_get_storage( @@ -352,7 +374,7 @@ where .rpc_client() .storage_keys_paged( Some(prefix.clone()), - DEFAULT_KEY_DOWNLOAD_PAGE, + Self::DEFAULT_KEY_DOWNLOAD_PAGE, last_key.clone(), Some(at), ) @@ -365,7 +387,7 @@ where all_keys.extend(page); - if page_len < DEFAULT_KEY_DOWNLOAD_PAGE as usize { + if page_len < Self::DEFAULT_KEY_DOWNLOAD_PAGE as usize { log::debug!(target: LOG_TARGET, "last page received: {}", page_len); break all_keys } else { @@ -384,6 +406,123 @@ where Ok(keys) } + /// Fetches storage data from a node using a dynamic batch size. + /// + /// This function adjusts the batch size on the fly to help prevent overwhelming the node with + /// large batch requests, and stay within request size limits enforced by the node. + /// + /// # Arguments + /// + /// * `client` - An `Arc` wrapped `HttpClient` used for making the requests. + /// * `payloads` - A vector of tuples containing a JSONRPC method name and `ArrayParams` + /// * `batch_size` - The initial batch size to use for the request. The batch size will be + /// adjusted dynamically in case of failure. + /// + /// # Returns + /// + /// Returns a `Result` with a vector of `Option`, where each element corresponds to + /// the storage data for the given method and parameters. The result will be an `Err` with a + /// `String` error message if the request fails. + /// + /// # Errors + /// + /// This function will return an error if: + /// * The batch request fails and the batch size is less than 2. + /// * There are invalid batch params. + /// * There is an error in the batch response. + /// + /// # Example + /// + /// ```ignore + /// use your_crate::{get_storage_data_dynamic_batch_size, HttpClient, ArrayParams}; + /// use std::sync::Arc; + /// + /// async fn example() { + /// let client = Arc::new(HttpClient::new()); + /// let payloads = vec![ + /// ("some_method".to_string(), ArrayParams::new(vec![])), + /// ("another_method".to_string(), ArrayParams::new(vec![])), + /// ]; + /// let initial_batch_size = 10; + /// + /// let storage_data = get_storage_data_dynamic_batch_size(client, payloads, batch_size).await; + /// match storage_data { + /// Ok(data) => println!("Storage data: {:?}", data), + /// Err(e) => eprintln!("Error fetching storage data: {}", e), + /// } + /// } + /// ``` + #[async_recursion] + async fn get_storage_data_dynamic_batch_size( + client: &Arc, + payloads: Vec<(String, ArrayParams)>, + batch_size: usize, + ) -> Result>, String> { + // All payloads have been processed + if payloads.is_empty() { + return Ok(vec![]) + }; + + log::debug!( + target: LOG_TARGET, + "Remaining payloads: {} Batch request size: {}", + payloads.len(), + batch_size, + ); + + // Payloads to attempt to process this batch + let page = payloads.iter().take(batch_size).cloned().collect::>(); + + // Build the batch request + let mut batch = BatchRequestBuilder::new(); + for (method, params) in page.iter() { + batch + .insert(method, params.clone()) + .map_err(|_| "Invalid batch method and/or params")? + } + let batch_response = match client.batch_request::>(batch).await { + Ok(batch_response) => batch_response, + Err(e) => { + if batch_size < 2 { + return Err(e.to_string()) + } + + log::debug!( + target: LOG_TARGET, + "Batch request failed, trying again with smaller batch size. {}", + e.to_string() + ); + + return Self::get_storage_data_dynamic_batch_size( + client, + payloads, + max(1, (batch_size as f32 * Self::BATCH_SIZE_DECREASE_FACTOR) as usize), + ) + .await + }, + }; + + // Collect the data from this batch + let mut data: Vec> = vec![]; + for item in batch_response.into_iter() { + match item { + Ok(x) => data.push(x), + Err(e) => return Err(e.message().to_string()), + } + } + + // Return this data joined with the remaining keys + let payloads = payloads.iter().skip(batch_size).cloned().collect::>(); + let mut rest = Self::get_storage_data_dynamic_batch_size( + client, + payloads, + max(batch_size + 1, (batch_size as f32 * Self::BATCH_SIZE_INCREASE_FACTOR) as usize), + ) + .await?; + data.append(&mut rest); + Ok(data) + } + /// Synonym of `getPairs` that uses paged queries to first get the keys, and then /// map them to values one by one. /// @@ -428,81 +567,61 @@ where let (tx, mut rx) = mpsc::unbounded::(); for thread_keys in keys_chunked { - let thread_client = client.clone(); let thread_sender = tx.clone(); + let thread_client = client.clone(); let handle = std::thread::spawn(move || { - let rt = tokio::runtime::Runtime::new().unwrap(); - let mut thread_key_values = Vec::with_capacity(thread_keys.len()); - - for chunk_keys in thread_keys.chunks(DEFAULT_VALUE_DOWNLOAD_BATCH) { - let mut batch = BatchRequestBuilder::new(); - - for key in chunk_keys.iter() { - batch - .insert("state_getStorage", rpc_params![key, at]) - .map_err(|_| "Invalid batch params") - .unwrap(); - } - - let batch_response = rt - .block_on(thread_client.batch_request::>(batch)) - .map_err(|e| { - log::error!( - target: LOG_TARGET, - "failed to execute batch: {:?}. Error: {:?}", - chunk_keys.iter().map(HexDisplay::from).collect::>(), - e - ); - "batch failed." - }) - .unwrap(); + // Process the payloads in chunks so each thread can pass kvs back to the main + // thread to start inserting before all of the data has been queried from the node. + // Inserting data takes a very long time, so the earlier it can start the better. + let mut thread_key_values = vec![]; + let chunk_size = thread_keys.len() / 1; + for thread_keys_chunk in thread_keys.chunks(chunk_size) { + let mut thread_key_chunk_values = Vec::with_capacity(thread_keys_chunk.len()); + + let payloads = thread_keys_chunk + .iter() + .map(|key| ("state_getStorage".to_string(), rpc_params!(key, at))) + .collect::>(); + + let rt = tokio::runtime::Runtime::new().unwrap(); + let storage_data = match rt.block_on(Self::get_storage_data_dynamic_batch_size( + &thread_client, + payloads, + Self::INITIAL_BATCH_SIZE, + )) { + Ok(storage_data) => storage_data, + Err(e) => { + thread_sender.unbounded_send(Message::BatchFailed(e)).unwrap(); + return Default::default() + }, + }; // Check if we got responses for all submitted requests. - assert_eq!(chunk_keys.len(), batch_response.len()); + assert_eq!(thread_keys_chunk.len(), storage_data.len()); - let mut batch_kv = Vec::with_capacity(chunk_keys.len()); - for (key, maybe_value) in chunk_keys.into_iter().zip(batch_response) { + let mut batch_kv = Vec::with_capacity(thread_keys_chunk.len()); + for (key, maybe_value) in thread_keys_chunk.iter().zip(storage_data) { match maybe_value { - Ok(Some(data)) => { - thread_key_values.push((key.clone(), data.clone())); + Some(data) => { + thread_key_chunk_values.push((key.clone(), data.clone())); batch_kv.push((key.clone().0, data.0)); }, - Ok(None) => { + None => { log::warn!( target: LOG_TARGET, "key {:?} had none corresponding value.", &key ); let data = StorageData(vec![]); - thread_key_values.push((key.clone(), data.clone())); + thread_key_chunk_values.push((key.clone(), data.clone())); batch_kv.push((key.clone().0, data.0)); }, - Err(e) => { - let reason = format!("key {:?} failed: {:?}", &key, e); - log::error!(target: LOG_TARGET, "Reason: {}", reason); - // Signal failures to the main thread, stop aggregating (key, value) - // pairs and return immediately an error. - thread_sender.unbounded_send(Message::BatchFailed(reason)).unwrap(); - return Default::default() - }, }; - - if thread_key_values.len() % (thread_keys.len() / 10).max(1) == 0 { - let ratio: f64 = - thread_key_values.len() as f64 / thread_keys.len() as f64; - log::debug!( - target: LOG_TARGET, - "[thread = {:?}] progress = {:.2} [{} / {}]", - std::thread::current().id(), - ratio, - thread_key_values.len(), - thread_keys.len(), - ); - } } - // Send this batch to the main thread to start inserting. + // Send this chunk to the main thread to start inserting. thread_sender.unbounded_send(Message::Batch(batch_kv)).unwrap(); + thread_key_values.extend(thread_key_chunk_values); } thread_sender.unbounded_send(Message::Terminated).unwrap(); @@ -516,10 +635,21 @@ where // `pending_ext`. let mut terminated = 0usize; let mut batch_failed = false; + let mut processed = 0usize; loop { match rx.next().await.unwrap() { Message::Batch(kv) => { for (k, v) in kv { + processed += 1; + if processed % 50_000 == 0 || processed == keys.len() || processed == 1 { + log::info!( + target: LOG_TARGET, + "inserting keys progress = {:.0}% [{} / {}]", + (processed as f32 / keys.len() as f32) * 100f32, + processed, + keys.len(), + ); + } // skip writing the child root data. if is_default_child_storage_key(k.as_ref()) { continue @@ -554,73 +684,58 @@ where /// Get the values corresponding to `child_keys` at the given `prefixed_top_key`. pub(crate) async fn rpc_child_get_storage_paged( - client: &WsClient, + client: &Arc, prefixed_top_key: &StorageKey, child_keys: Vec, at: B::Hash, ) -> Result, &'static str> { - let mut child_kv_inner = vec![]; - let mut batch_success = true; - - for batch_child_key in child_keys.chunks(DEFAULT_VALUE_DOWNLOAD_BATCH) { - let mut batch_request = BatchRequestBuilder::new(); - - for key in batch_child_key { - batch_request - .insert( - "childstate_getStorage", - rpc_params![ - PrefixedStorageKey::new(prefixed_top_key.as_ref().to_vec()), - key, - at - ], - ) - .map_err(|_| "Invalid batch params")?; - } + let child_keys_len = child_keys.len(); - let batch_response = - client.batch_request::>(batch_request).await.map_err(|e| { - log::error!( - target: LOG_TARGET, - "failed to execute batch: {:?}. Error: {:?}", - batch_child_key, - e - ); - "batch failed." - })?; + let payloads = child_keys + .iter() + .map(|key| { + ( + "childstate_getStorage".to_string(), + rpc_params![ + PrefixedStorageKey::new(prefixed_top_key.as_ref().to_vec()), + key, + at + ], + ) + }) + .collect::>(); - assert_eq!(batch_child_key.len(), batch_response.len()); - - for (key, maybe_value) in batch_child_key.iter().zip(batch_response) { - match maybe_value { - Ok(Some(v)) => { - child_kv_inner.push((key.clone(), v)); - }, - Ok(None) => { - log::warn!( - target: LOG_TARGET, - "key {:?} had none corresponding value.", - &key - ); - child_kv_inner.push((key.clone(), StorageData(vec![]))); - }, - Err(e) => { - log::error!(target: LOG_TARGET, "key {:?} failed: {:?}", &key, e); - batch_success = false; - }, - }; - } - } + let storage_data = match Self::get_storage_data_dynamic_batch_size( + client, + payloads, + Self::INITIAL_BATCH_SIZE, + ) + .await + { + Ok(storage_data) => storage_data, + Err(e) => { + log::error!(target: LOG_TARGET, "batch processing failed: {:?}", e); + return Err("batch processing failed") + }, + }; - if batch_success { - Ok(child_kv_inner) - } else { - Err("batch failed.") - } + assert_eq!(child_keys_len, storage_data.len()); + + Ok(child_keys + .iter() + .zip(storage_data) + .map(|(key, maybe_value)| match maybe_value { + Some(v) => (key.clone(), v), + None => { + log::warn!(target: LOG_TARGET, "key {:?} had no corresponding value.", &key); + (key.clone(), StorageData(vec![])) + }, + }) + .collect::>()) } pub(crate) async fn rpc_child_get_keys( - client: &WsClient, + client: &HttpClient, prefixed_top_key: &StorageKey, child_prefix: StorageKey, at: B::Hash, @@ -678,107 +793,47 @@ where return Ok(Default::default()) } - // div-ceil simulation. - let threads = Self::threads().get(); - let child_roots_per_thread = (child_roots.len() + threads - 1) / threads; - info!( target: LOG_TARGET, - "👩‍👦 scraping child-tree data from {} top keys, split among {} threads, {} top keys per thread", + "👩‍👦 scraping child-tree data from {} top keys", child_roots.len(), - threads, - child_roots_per_thread, ); - // NOTE: the threading done here is the simpler, yet slightly un-elegant because we are - // splitting child root among threads, and it is very common for these root to have vastly - // different child tries underneath them, causing some threads to finish way faster than - // others. Certainly still better than single thread though. - let mut handles = vec![]; - let client = self.as_online().rpc_client_cloned(); let at = self.as_online().at_expected(); - enum Message { - Terminated, - Batch((ChildInfo, Vec<(Vec, Vec)>)), - } - let (tx, mut rx) = mpsc::unbounded::(); - - for thread_child_roots in child_roots - .chunks(child_roots_per_thread) - .map(|x| x.into()) - .collect::>>() - { - let thread_client = client.clone(); - let thread_sender = tx.clone(); - let handle = thread::spawn(move || { - let rt = tokio::runtime::Runtime::new().unwrap(); - let mut thread_child_kv = vec![]; - for prefixed_top_key in thread_child_roots { - let child_keys = rt.block_on(Self::rpc_child_get_keys( - &thread_client, - &prefixed_top_key, - StorageKey(vec![]), - at, - ))?; - let child_kv_inner = rt.block_on(Self::rpc_child_get_storage_paged( - &thread_client, - &prefixed_top_key, - child_keys, - at, - ))?; - - let prefixed_top_key = PrefixedStorageKey::new(prefixed_top_key.clone().0); - let un_prefixed = match ChildType::from_prefixed_key(&prefixed_top_key) { - Some((ChildType::ParentKeyId, storage_key)) => storage_key, - None => { - log::error!(target: LOG_TARGET, "invalid key: {:?}", prefixed_top_key); - return Err("Invalid child key") - }, - }; - - thread_sender - .unbounded_send(Message::Batch(( - ChildInfo::new_default(un_prefixed), - child_kv_inner - .iter() - .cloned() - .map(|(k, v)| (k.0, v.0)) - .collect::>(), - ))) - .unwrap(); - thread_child_kv.push((ChildInfo::new_default(un_prefixed), child_kv_inner)); - } - - thread_sender.unbounded_send(Message::Terminated).unwrap(); - Ok(thread_child_kv) - }); - handles.push(handle); - } - - // first, wait until all threads send a `Terminated` message, in the meantime populate - // `pending_ext`. - let mut terminated = 0usize; - loop { - match rx.next().await.unwrap() { - Message::Batch((info, kvs)) => - for (k, v) in kvs { - pending_ext.insert_child(info.clone(), k, v); - }, - Message::Terminated => { - terminated += 1; - if terminated == handles.len() { - break - } + let arc_client = self.as_online().rpc_client_cloned(); + let mut child_kv = vec![]; + for prefixed_top_key in child_roots { + let child_keys = Self::rpc_child_get_keys( + arc_client.as_ref(), + &prefixed_top_key, + StorageKey(vec![]), + at, + ) + .await?; + + let child_kv_inner = + Self::rpc_child_get_storage_paged(&arc_client, &prefixed_top_key, child_keys, at) + .await?; + + let prefixed_top_key = PrefixedStorageKey::new(prefixed_top_key.clone().0); + let un_prefixed = match ChildType::from_prefixed_key(&prefixed_top_key) { + Some((ChildType::ParentKeyId, storage_key)) => storage_key, + None => { + log::error!(target: LOG_TARGET, "invalid key: {:?}", prefixed_top_key); + return Err("Invalid child key") }, + }; + + let info = ChildInfo::new_default(un_prefixed); + let key_values = + child_kv_inner.iter().cloned().map(|(k, v)| (k.0, v.0)).collect::>(); + child_kv.push((info.clone(), child_kv_inner)); + for (k, v) in key_values { + pending_ext.insert_child(info.clone(), k, v); } } - let child_kv = handles - .into_iter() - .flat_map(|h| h.join().unwrap()) - .flatten() - .collect::>(); Ok(child_kv) } @@ -841,8 +896,8 @@ where /// /// initializes the remote client in `transport`, and sets the `at` field, if not specified. async fn init_remote_client(&mut self) -> Result<(), &'static str> { - // First, initialize the ws client. - self.as_online_mut().transport.map_uri().await?; + // First, initialize the http client. + self.as_online_mut().transport.init().await?; // Then, if `at` is not set, set it. if self.as_online().at.is_none() { diff --git a/utils/frame/try-runtime/cli/src/lib.rs b/utils/frame/try-runtime/cli/src/lib.rs index cd6c48f52..9268ef2ed 100644 --- a/utils/frame/try-runtime/cli/src/lib.rs +++ b/utils/frame/try-runtime/cli/src/lib.rs @@ -97,18 +97,6 @@ //! > If anything, in most cases, we expect spec-versions to NOT match, because try-runtime is all //! > about testing unreleased runtimes. //! -//! ## Note on nodes that respond to `try-runtime` requests. -//! -//! There are a number of flags that need to be preferably set on a running node in order to work -//! well with try-runtime's expensive RPC queries: -//! -//! - set `--rpc-max-response-size 1000` and -//! - `--rpc-max-request-size 1000` to ensure connections are not dropped in case the state is -//! large. -//! - set `--rpc-cors all` to ensure ws connections can come through. -//! -//! Note that *none* of the try-runtime operations need unsafe RPCs. -//! //! ## Note on signature and state-root checks //! //! All of the commands calling into `TryRuntime_execute_block` ([`Command::ExecuteBlock`] and From 471b49b09fd0d2a3057090ab0720f4e39faed5c5 Mon Sep 17 00:00:00 2001 From: Harald Heckmann Date: Fri, 21 Apr 2023 12:06:26 +0200 Subject: [PATCH 406/558] Add Freeze/Thaw events and tests (#13779) * Add Freeze/Thaw events and tests * Remove duplicate docstring * Correct spelling error * Cargo fmt * Use proper punctuation in docstring Co-authored-by: Jegor Sidorenko <5252494+jsidorenko@users.noreply.github.com> --------- Co-authored-by: Jegor Sidorenko <5252494+jsidorenko@users.noreply.github.com> Co-authored-by: parity-processbot <> --- frame/balances/src/lib.rs | 15 +++++++ frame/balances/src/tests/fungible_tests.rs | 46 ++++++++++++++++++++++ 2 files changed, 61 insertions(+) diff --git a/frame/balances/src/lib.rs b/frame/balances/src/lib.rs index 74aec1f2d..6835d3c81 100644 --- a/frame/balances/src/lib.rs +++ b/frame/balances/src/lib.rs @@ -337,6 +337,10 @@ pub mod pallet { Locked { who: T::AccountId, amount: T::Balance }, /// Some balance was unlocked. Unlocked { who: T::AccountId, amount: T::Balance }, + /// Some balance was frozen. + Frozen { who: T::AccountId, amount: T::Balance }, + /// Some balance was thawed. + Thawed { who: T::AccountId, amount: T::Balance }, } #[pallet::error] @@ -1084,7 +1088,10 @@ pub mod pallet { who: &T::AccountId, freezes: BoundedSlice, T::MaxFreezes>, ) -> DispatchResult { + let mut prev_frozen = Zero::zero(); + let mut after_frozen = Zero::zero(); let (_, maybe_dust) = Self::mutate_account(who, |b| { + prev_frozen = b.frozen; b.frozen = Zero::zero(); for l in Locks::::get(who).iter() { b.frozen = b.frozen.max(l.amount); @@ -1092,6 +1099,7 @@ pub mod pallet { for l in freezes.iter() { b.frozen = b.frozen.max(l.amount); } + after_frozen = b.frozen; })?; debug_assert!(maybe_dust.is_none(), "Not altering main balance; qed"); if freezes.is_empty() { @@ -1099,6 +1107,13 @@ pub mod pallet { } else { Freezes::::insert(who, freezes); } + if prev_frozen > after_frozen { + let amount = prev_frozen.saturating_sub(after_frozen); + Self::deposit_event(Event::Thawed { who: who.clone(), amount }); + } else if after_frozen > prev_frozen { + let amount = after_frozen.saturating_sub(prev_frozen); + Self::deposit_event(Event::Frozen { who: who.clone(), amount }); + } Ok(()) } diff --git a/frame/balances/src/tests/fungible_tests.rs b/frame/balances/src/tests/fungible_tests.rs index 128086885..185396019 100644 --- a/frame/balances/src/tests/fungible_tests.rs +++ b/frame/balances/src/tests/fungible_tests.rs @@ -397,3 +397,49 @@ fn unholding_frees_hold_slot() { assert_ok!(Balances::hold(&TestId::Baz, &1, 10)); }); } + +#[test] +fn emit_events_with_changing_freezes() { + ExtBuilder::default().build_and_execute_with(|| { + let _ = Balances::set_balance(&1, 100); + System::reset_events(); + + // Freeze = [] --> [10] + assert_ok!(Balances::set_freeze(&TestId::Foo, &1, 10)); + assert_eq!(events(), [RuntimeEvent::Balances(crate::Event::Frozen { who: 1, amount: 10 })]); + + // Freeze = [10] --> [15] + assert_ok!(Balances::set_freeze(&TestId::Foo, &1, 15)); + assert_eq!(events(), [RuntimeEvent::Balances(crate::Event::Frozen { who: 1, amount: 5 })]); + + // Freeze = [15] --> [15, 20] + assert_ok!(Balances::set_freeze(&TestId::Bar, &1, 20)); + assert_eq!(events(), [RuntimeEvent::Balances(crate::Event::Frozen { who: 1, amount: 5 })]); + + // Freeze = [15, 20] --> [17, 20] + assert_ok!(Balances::set_freeze(&TestId::Foo, &1, 17)); + for event in events() { + match event { + RuntimeEvent::Balances(crate::Event::Frozen { .. }) => { + assert!(false, "unexpected freeze event") + }, + RuntimeEvent::Balances(crate::Event::Thawed { .. }) => { + assert!(false, "unexpected thaw event") + }, + _ => continue, + } + } + + // Freeze = [17, 20] --> [17, 15] + assert_ok!(Balances::set_freeze(&TestId::Bar, &1, 15)); + assert_eq!(events(), [RuntimeEvent::Balances(crate::Event::Thawed { who: 1, amount: 3 })]); + + // Freeze = [17, 15] --> [15] + assert_ok!(Balances::thaw(&TestId::Foo, &1)); + assert_eq!(events(), [RuntimeEvent::Balances(crate::Event::Thawed { who: 1, amount: 2 })]); + + // Freeze = [15] --> [] + assert_ok!(Balances::thaw(&TestId::Bar, &1)); + assert_eq!(events(), [RuntimeEvent::Balances(crate::Event::Thawed { who: 1, amount: 15 })]); + }); +} From 35d04fb506b00128739b5bead363f0ca651a4ff3 Mon Sep 17 00:00:00 2001 From: PG Herveou Date: Fri, 21 Apr 2023 17:24:12 +0200 Subject: [PATCH 407/558] Update macro use and optimize storage collection (#13970) --- frame/contracts/src/lib.rs | 3 +-- frame/contracts/src/storage/meter.rs | 2 +- frame/contracts/src/tests.rs | 26 +++++++++++++------------- 3 files changed, 15 insertions(+), 16 deletions(-) diff --git a/frame/contracts/src/lib.rs b/frame/contracts/src/lib.rs index 26b16b329..fa230a8ca 100644 --- a/frame/contracts/src/lib.rs +++ b/frame/contracts/src/lib.rs @@ -83,11 +83,10 @@ #![cfg_attr(not(feature = "std"), no_std)] #![cfg_attr(feature = "runtime-benchmarks", recursion_limit = "1024")] -#[macro_use] -mod gas; mod address; mod benchmarking; mod exec; +mod gas; mod migration; mod schedule; mod storage; diff --git a/frame/contracts/src/storage/meter.rs b/frame/contracts/src/storage/meter.rs index ef3e01f6d..497ee03ac 100644 --- a/frame/contracts/src/storage/meter.rs +++ b/frame/contracts/src/storage/meter.rs @@ -293,8 +293,8 @@ where .total_deposit .saturating_add(&absorbed.total_deposit) .saturating_add(&own_deposit); + self.charges.extend_from_slice(&absorbed.charges); if !own_deposit.is_zero() { - self.charges.extend_from_slice(&absorbed.charges); self.charges.push(Charge { deposit_account, amount: own_deposit, diff --git a/frame/contracts/src/tests.rs b/frame/contracts/src/tests.rs index ac1fb07bf..4e6c468f1 100644 --- a/frame/contracts/src/tests.rs +++ b/frame/contracts/src/tests.rs @@ -73,7 +73,19 @@ frame_support::construct_runtime!( } ); -#[macro_use] +macro_rules! assert_return_code { + ( $x:expr , $y:expr $(,)? ) => {{ + assert_eq!(u32::from_le_bytes($x.data[..].try_into().unwrap()), $y as u32); + }}; +} + +macro_rules! assert_refcount { + ( $code_hash:expr , $should:expr $(,)? ) => {{ + let is = crate::OwnerInfoOf::::get($code_hash).map(|m| m.refcount()).unwrap(); + assert_eq!(is, $should); + }}; +} + pub mod test_utils { use super::{Balances, Hash, SysConfig, Test}; use crate::{exec::AccountIdOf, CodeHash, Config, ContractInfo, ContractInfoOf, Nonce}; @@ -105,18 +117,6 @@ pub mod test_utils { pub fn hash(s: &S) -> <::Hashing as Hash>::Output { <::Hashing as Hash>::hash_of(s) } - macro_rules! assert_return_code { - ( $x:expr , $y:expr $(,)? ) => {{ - assert_eq!(u32::from_le_bytes($x.data[..].try_into().unwrap()), $y as u32); - }}; - } - - macro_rules! assert_refcount { - ( $code_hash:expr , $should:expr $(,)? ) => {{ - let is = crate::OwnerInfoOf::::get($code_hash).map(|m| m.refcount()).unwrap(); - assert_eq!(is, $should); - }}; - } } impl Test { From 2fde3802da6256b5846ce33760685ca40008d00d Mon Sep 17 00:00:00 2001 From: ordian Date: Fri, 21 Apr 2023 21:58:05 +0200 Subject: [PATCH 408/558] cargo upgrade --workspace kvdb-rocksdb (#13973) --- Cargo.lock | 12 ++++++------ bin/node/bench/Cargo.toml | 2 +- client/db/Cargo.toml | 4 ++-- 3 files changed, 9 insertions(+), 9 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index b31699318..aa7d22dac 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3776,9 +3776,9 @@ dependencies = [ [[package]] name = "kvdb-rocksdb" -version = "0.17.0" +version = "0.18.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2182b8219fee6bd83aacaab7344e840179ae079d5216aa4e249b4d704646a844" +checksum = "fe7a749456510c45f795e8b04a6a3e0976d0139213ecbf465843830ad55e2217" dependencies = [ "kvdb", "num_cpus", @@ -4311,9 +4311,9 @@ dependencies = [ [[package]] name = "librocksdb-sys" -version = "0.8.3+7.4.4" +version = "0.10.0+7.9.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "557b255ff04123fcc176162f56ed0c9cd42d8f357cf55b3fabeb60f7413741b3" +checksum = "0fe4d5874f5ff2bc616e55e8c6086d478fcda13faf9495768a4aa1c22042d30b" dependencies = [ "bindgen", "bzip2-sys", @@ -8097,9 +8097,9 @@ dependencies = [ [[package]] name = "rocksdb" -version = "0.19.0" +version = "0.20.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7e9562ea1d70c0cc63a34a22d977753b50cca91cc6b6527750463bd5dd8697bc" +checksum = "015439787fce1e75d55f279078d33ff14b4af5d93d995e8838ee4631301c8a99" dependencies = [ "libc", "librocksdb-sys", diff --git a/bin/node/bench/Cargo.toml b/bin/node/bench/Cargo.toml index 153a721da..30266ab0d 100644 --- a/bin/node/bench/Cargo.toml +++ b/bin/node/bench/Cargo.toml @@ -25,7 +25,7 @@ serde = "1.0.136" serde_json = "1.0.85" derive_more = { version = "0.99.17", default-features = false, features = ["display"] } kvdb = "0.13.0" -kvdb-rocksdb = "0.17.0" +kvdb-rocksdb = "0.18.0" sp-trie = { version = "7.0.0", path = "../../../primitives/trie" } sp-core = { version = "7.0.0", path = "../../../primitives/core" } sp-consensus = { version = "0.10.0-dev", path = "../../../primitives/consensus/common" } diff --git a/client/db/Cargo.toml b/client/db/Cargo.toml index 8e4bcf18a..cd3a73dd4 100644 --- a/client/db/Cargo.toml +++ b/client/db/Cargo.toml @@ -19,7 +19,7 @@ codec = { package = "parity-scale-codec", version = "3.2.2", features = [ hash-db = "0.16.0" kvdb = "0.13.0" kvdb-memorydb = "0.13.0" -kvdb-rocksdb = { version = "0.17.0", optional = true } +kvdb-rocksdb = { version = "0.18.0", optional = true } linked-hash-map = "0.5.4" log = "0.4.17" parity-db = "0.4.6" @@ -37,7 +37,7 @@ sp-trie = { version = "7.0.0", path = "../../primitives/trie" } [dev-dependencies] criterion = "0.4.0" -kvdb-rocksdb = "0.17.0" +kvdb-rocksdb = "0.18.0" rand = "0.8.5" tempfile = "3.1.0" quickcheck = { version = "1.0.3", default-features = false } From c7d26dcf3d6c19fa7dcbd1f2633e337e5a5cbc40 Mon Sep 17 00:00:00 2001 From: Davide Galassi Date: Fri, 21 Apr 2023 22:32:32 +0200 Subject: [PATCH 409/558] Remove superflous Pair::verify_weak (#13972) --- primitives/application-crypto/src/lib.rs | 7 ------- primitives/core/src/crypto.rs | 15 -------------- primitives/core/src/ecdsa.rs | 18 ++-------------- primitives/core/src/ed25519.rs | 26 ++++++++---------------- primitives/core/src/sr25519.rs | 18 +++++----------- 5 files changed, 15 insertions(+), 69 deletions(-) diff --git a/primitives/application-crypto/src/lib.rs b/primitives/application-crypto/src/lib.rs index 3f12e06e1..5e7779519 100644 --- a/primitives/application-crypto/src/lib.rs +++ b/primitives/application-crypto/src/lib.rs @@ -154,13 +154,6 @@ macro_rules! app_crypto_pair { ) -> bool { <$pair>::verify(&sig.0, message, pubkey.as_ref()) } - fn verify_weak, M: AsRef<[u8]>>( - sig: &[u8], - message: M, - pubkey: P, - ) -> bool { - <$pair>::verify_weak(sig, message, pubkey) - } fn public(&self) -> Self::Public { Public(self.0.public()) } diff --git a/primitives/core/src/crypto.rs b/primitives/core/src/crypto.rs index 4fec79fc6..dd0d9e605 100644 --- a/primitives/core/src/crypto.rs +++ b/primitives/core/src/crypto.rs @@ -716,10 +716,6 @@ mod dummy { true } - fn verify_weak, M: AsRef<[u8]>>(_: &[u8], _: M, _: P) -> bool { - true - } - fn public(&self) -> Self::Public { Self } @@ -917,9 +913,6 @@ pub trait Pair: CryptoType + Sized + Clone + Send + Sync + 'static { /// Verify a signature on a message. Returns true if the signature is good. fn verify>(sig: &Self::Signature, message: M, pubkey: &Self::Public) -> bool; - /// Verify a signature on a message. Returns true if the signature is good. - fn verify_weak, M: AsRef<[u8]>>(sig: &[u8], message: M, pubkey: P) -> bool; - /// Get the public key. fn public(&self) -> Self::Public; @@ -1272,14 +1265,6 @@ mod tests { true } - fn verify_weak, M: AsRef<[u8]>>( - _sig: &[u8], - _message: M, - _pubkey: P, - ) -> bool { - true - } - fn public(&self) -> Self::Public { TestPublic } diff --git a/primitives/core/src/ecdsa.rs b/primitives/core/src/ecdsa.rs index 8dc4a5513..2474fa773 100644 --- a/primitives/core/src/ecdsa.rs +++ b/primitives/core/src/ecdsa.rs @@ -407,22 +407,8 @@ impl TraitPair for Pair { } /// Verify a signature on a message. Returns true if the signature is good. - fn verify>(sig: &Self::Signature, message: M, pubkey: &Self::Public) -> bool { - match sig.recover(message) { - Some(actual) => actual == *pubkey, - None => false, - } - } - - /// Verify a signature on a message. Returns true if the signature is good. - /// - /// This doesn't use the type system to ensure that `sig` and `pubkey` are the correct - /// size. Use it only if you're coming from byte buffers and need the speed. - fn verify_weak, M: AsRef<[u8]>>(sig: &[u8], message: M, pubkey: P) -> bool { - match Signature::from_slice(sig).and_then(|sig| sig.recover(message)) { - Some(actual) => actual.as_ref() == pubkey.as_ref(), - None => false, - } + fn verify>(sig: &Self::Signature, message: M, public: &Self::Public) -> bool { + sig.recover(message).map(|actual| actual == *public).unwrap_or_default() } /// Return a vec filled with raw data. diff --git a/primitives/core/src/ed25519.rs b/primitives/core/src/ed25519.rs index 884d29dc1..d0e6bef97 100644 --- a/primitives/core/src/ed25519.rs +++ b/primitives/core/src/ed25519.rs @@ -406,27 +406,17 @@ impl TraitPair for Pair { Signature::from_raw(self.secret.sign(message).into()) } - /// Verify a signature on a message. Returns true if the signature is good. - fn verify>(sig: &Self::Signature, message: M, pubkey: &Self::Public) -> bool { - Self::verify_weak(&sig.0[..], message.as_ref(), pubkey) - } - - /// Verify a signature on a message. Returns true if the signature is good. + /// Verify a signature on a message. /// - /// This doesn't use the type system to ensure that `sig` and `pubkey` are the correct - /// size. Use it only if you're coming from byte buffers and need the speed. - fn verify_weak, M: AsRef<[u8]>>(sig: &[u8], message: M, pubkey: P) -> bool { - let public_key = match VerificationKey::try_from(pubkey.as_ref()) { - Ok(pk) => pk, - Err(_) => return false, + /// Returns true if the signature is good. + fn verify>(sig: &Self::Signature, message: M, public: &Self::Public) -> bool { + let Ok(public) = VerificationKey::try_from(public.as_slice()) else { + return false }; - - let sig = match ed25519_zebra::Signature::try_from(sig) { - Ok(s) => s, - Err(_) => return false, + let Ok(signature) = ed25519_zebra::Signature::try_from(sig.as_ref()) else { + return false }; - - public_key.verify(&sig, message.as_ref()).is_ok() + public.verify(&signature, message.as_ref()).is_ok() } /// Return a vec filled with raw data. diff --git a/primitives/core/src/sr25519.rs b/primitives/core/src/sr25519.rs index fcd7f38a7..9b1c82205 100644 --- a/primitives/core/src/sr25519.rs +++ b/primitives/core/src/sr25519.rs @@ -487,21 +487,13 @@ impl TraitPair for Pair { } fn verify>(sig: &Self::Signature, message: M, pubkey: &Self::Public) -> bool { - Self::verify_weak(&sig.0[..], message, pubkey) - } - - fn verify_weak, M: AsRef<[u8]>>(sig: &[u8], message: M, pubkey: P) -> bool { - let signature = match schnorrkel::Signature::from_bytes(sig) { - Ok(signature) => signature, - Err(_) => return false, + let Ok(signature) = schnorrkel::Signature::from_bytes(sig.as_ref()) else { + return false }; - - let pub_key = match PublicKey::from_bytes(pubkey.as_ref()) { - Ok(pub_key) => pub_key, - Err(_) => return false, + let Ok(public) = PublicKey::from_bytes(pubkey.as_ref()) else { + return false }; - - pub_key.verify_simple(SIGNING_CTX, message.as_ref(), &signature).is_ok() + public.verify_simple(SIGNING_CTX, message.as_ref(), &signature).is_ok() } fn to_raw_vec(&self) -> Vec { From 9370f42ab35f7013f70b703884c9884888a3b153 Mon Sep 17 00:00:00 2001 From: gupnik <17176722+gupnik@users.noreply.github.com> Date: Mon, 24 Apr 2023 17:34:15 +0530 Subject: [PATCH 410/558] Adds force_origin support (#13845) * Adds force_origin support * Moves a couple of tests to showcase v2 with force_origin * Adds remaining tests * adds documentation * minor * adds test for invalid origin * ".git/.scripts/commands/fmt/fmt.sh" * updates param to use MaxCalls * Fixes compilation error * Updates doc comment * Fixes test outputs * Fixes test output * ".git/.scripts/commands/fmt/fmt.sh" --------- Co-authored-by: command-bot <> --- frame/benchmarking/src/lib.rs | 7 + frame/lottery/src/benchmarking.rs | 127 ++++++++++++------ frame/support/procedural/src/benchmark.rs | 35 +++-- .../test/tests/benchmark_ui/invalid_origin.rs | 17 +++ .../tests/benchmark_ui/invalid_origin.stderr | 16 +++ 5 files changed, 141 insertions(+), 61 deletions(-) create mode 100644 frame/support/test/tests/benchmark_ui/invalid_origin.rs create mode 100644 frame/support/test/tests/benchmark_ui/invalid_origin.stderr diff --git a/frame/benchmarking/src/lib.rs b/frame/benchmarking/src/lib.rs index 7110c378d..b11e164fe 100644 --- a/frame/benchmarking/src/lib.rs +++ b/frame/benchmarking/src/lib.rs @@ -160,6 +160,13 @@ pub use v1::*; /// The underscore will be substituted with the name of the benchmark (i.e. the name of the /// function in the benchmark function definition). /// +/// In case of a `force_origin` where you want to elevate the privileges of the provided origin, +/// this is the general syntax: +/// ```ignore +/// #[extrinsic_call] +/// _(force_origin as T::RuntimeOrigin, 0u32.into(), 0); +/// ``` +/// /// Regardless of whether `#[extrinsic_call]` or `#[block]` is used, this attribute also serves /// the purpose of designating the boundary between the setup code portion of the benchmark /// (everything before the `#[extrinsic_call]` or `#[block]` attribute) and the verification diff --git a/frame/lottery/src/benchmarking.rs b/frame/lottery/src/benchmarking.rs index 9216464b0..1510d250d 100644 --- a/frame/lottery/src/benchmarking.rs +++ b/frame/lottery/src/benchmarking.rs @@ -21,7 +21,12 @@ use super::*; -use frame_benchmarking::v1::{account, benchmarks, whitelisted_caller, BenchmarkError}; +use crate::Pallet as Lottery; +use frame_benchmarking::{ + impl_benchmark_test_suite, + v1::{account, whitelisted_caller, BenchmarkError}, + v2::*, +}; use frame_support::{ storage::bounded_vec::BoundedVec, traits::{EnsureOrigin, OnInitialize}, @@ -29,8 +34,6 @@ use frame_support::{ use frame_system::RawOrigin; use sp_runtime::traits::{Bounded, Zero}; -use crate::Pallet as Lottery; - // Set up and start a lottery fn setup_lottery(repeat: bool) -> Result<(), &'static str> { let price = T::Currency::minimum_balance(); @@ -50,72 +53,100 @@ fn setup_lottery(repeat: bool) -> Result<(), &'static str> { Ok(()) } -benchmarks! { - buy_ticket { +#[benchmarks] +mod benchmarks { + use super::*; + + #[benchmark] + fn buy_ticket() -> Result<(), BenchmarkError> { let caller = whitelisted_caller(); T::Currency::make_free_balance_be(&caller, BalanceOf::::max_value()); setup_lottery::(false)?; // force user to have a long vec of calls participating let set_code_index: CallIndex = Lottery::::call_to_index( - &frame_system::Call::::set_code{ code: vec![] }.into() + &frame_system::Call::::set_code { code: vec![] }.into(), )?; let already_called: (u32, BoundedVec) = ( LotteryIndex::::get(), BoundedVec::::try_from(vec![ set_code_index; - T::MaxCalls::get().saturating_sub(1) as usize - ]).unwrap(), + T::MaxCalls::get().saturating_sub(1) + as usize + ]) + .unwrap(), ); Participants::::insert(&caller, already_called); let call = frame_system::Call::::remark { remark: vec![] }; - }: _(RawOrigin::Signed(caller), Box::new(call.into())) - verify { + + #[extrinsic_call] + _(RawOrigin::Signed(caller), Box::new(call.into())); + assert_eq!(TicketsCount::::get(), 1); + + Ok(()) } - set_calls { - let n in 0 .. T::MaxCalls::get() as u32; + #[benchmark] + fn set_calls(n: Linear<0, { T::MaxCalls::get() }>) -> Result<(), BenchmarkError> { let calls = vec![frame_system::Call::::remark { remark: vec![] }.into(); n as usize]; let origin = T::ManagerOrigin::try_successful_origin().map_err(|_| BenchmarkError::Weightless)?; assert!(CallIndices::::get().is_empty()); - }: _(origin, calls) - verify { + + #[extrinsic_call] + _(origin as T::RuntimeOrigin, calls); + if !n.is_zero() { assert!(!CallIndices::::get().is_empty()); } + + Ok(()) } - start_lottery { + #[benchmark] + fn start_lottery() -> Result<(), BenchmarkError> { let price = BalanceOf::::max_value(); let end = 10u32.into(); let payout = 5u32.into(); let origin = T::ManagerOrigin::try_successful_origin().map_err(|_| BenchmarkError::Weightless)?; - }: _(origin, price, end, payout, true) - verify { + + #[extrinsic_call] + _(origin as T::RuntimeOrigin, price, end, payout, true); + assert!(crate::Lottery::::get().is_some()); + + Ok(()) } - stop_repeat { + #[benchmark] + fn stop_repeat() -> Result<(), BenchmarkError> { setup_lottery::(true)?; assert_eq!(crate::Lottery::::get().unwrap().repeat, true); let origin = T::ManagerOrigin::try_successful_origin().map_err(|_| BenchmarkError::Weightless)?; - }: _(origin) - verify { + + #[extrinsic_call] + _(origin as T::RuntimeOrigin); + assert_eq!(crate::Lottery::::get().unwrap().repeat, false); + + Ok(()) } - on_initialize_end { + #[benchmark] + fn on_initialize_end() -> Result<(), BenchmarkError> { setup_lottery::(false)?; let winner = account("winner", 0, 0); // User needs more than min balance to get ticket T::Currency::make_free_balance_be(&winner, T::Currency::minimum_balance() * 10u32.into()); // Make sure lottery account has at least min balance too let lottery_account = Lottery::::account_id(); - T::Currency::make_free_balance_be(&lottery_account, T::Currency::minimum_balance() * 10u32.into()); + T::Currency::make_free_balance_be( + &lottery_account, + T::Currency::minimum_balance() * 10u32.into(), + ); // Buy a ticket let call = frame_system::Call::::remark { remark: vec![] }; Lottery::::buy_ticket(RawOrigin::Signed(winner.clone()).into(), Box::new(call.into()))?; @@ -124,29 +155,37 @@ benchmarks! { // Assert that lotto is set up for winner assert_eq!(TicketsCount::::get(), 1); assert!(!Lottery::::pot().1.is_zero()); - }: { - // Generate `MaxGenerateRandom` numbers for worst case scenario - for i in 0 .. T::MaxGenerateRandom::get() { - Lottery::::generate_random_number(i); + + #[block] + { + // Generate `MaxGenerateRandom` numbers for worst case scenario + for i in 0..T::MaxGenerateRandom::get() { + Lottery::::generate_random_number(i); + } + // Start lottery has block 15 configured for payout + Lottery::::on_initialize(15u32.into()); } - // Start lottery has block 15 configured for payout - Lottery::::on_initialize(15u32.into()); - } - verify { + assert!(crate::Lottery::::get().is_none()); assert_eq!(TicketsCount::::get(), 0); assert_eq!(Lottery::::pot().1, 0u32.into()); - assert!(!T::Currency::free_balance(&winner).is_zero()) + assert!(!T::Currency::free_balance(&winner).is_zero()); + + Ok(()) } - on_initialize_repeat { + #[benchmark] + fn on_initialize_repeat() -> Result<(), BenchmarkError> { setup_lottery::(true)?; let winner = account("winner", 0, 0); // User needs more than min balance to get ticket T::Currency::make_free_balance_be(&winner, T::Currency::minimum_balance() * 10u32.into()); // Make sure lottery account has at least min balance too let lottery_account = Lottery::::account_id(); - T::Currency::make_free_balance_be(&lottery_account, T::Currency::minimum_balance() * 10u32.into()); + T::Currency::make_free_balance_be( + &lottery_account, + T::Currency::minimum_balance() * 10u32.into(), + ); // Buy a ticket let call = frame_system::Call::::remark { remark: vec![] }; Lottery::::buy_ticket(RawOrigin::Signed(winner.clone()).into(), Box::new(call.into()))?; @@ -155,20 +194,24 @@ benchmarks! { // Assert that lotto is set up for winner assert_eq!(TicketsCount::::get(), 1); assert!(!Lottery::::pot().1.is_zero()); - }: { - // Generate `MaxGenerateRandom` numbers for worst case scenario - for i in 0 .. T::MaxGenerateRandom::get() { - Lottery::::generate_random_number(i); + + #[block] + { + // Generate `MaxGenerateRandom` numbers for worst case scenario + for i in 0..T::MaxGenerateRandom::get() { + Lottery::::generate_random_number(i); + } + // Start lottery has block 15 configured for payout + Lottery::::on_initialize(15u32.into()); } - // Start lottery has block 15 configured for payout - Lottery::::on_initialize(15u32.into()); - } - verify { + assert!(crate::Lottery::::get().is_some()); assert_eq!(LotteryIndex::::get(), 2); assert_eq!(TicketsCount::::get(), 0); assert_eq!(Lottery::::pot().1, 0u32.into()); - assert!(!T::Currency::free_balance(&winner).is_zero()) + assert!(!T::Currency::free_balance(&winner).is_zero()); + + Ok(()) } impl_benchmark_test_suite!(Lottery, crate::mock::new_test_ext(), crate::mock::Test); diff --git a/frame/support/procedural/src/benchmark.rs b/frame/support/procedural/src/benchmark.rs index 315a6000b..28b5aa1b9 100644 --- a/frame/support/procedural/src/benchmark.rs +++ b/frame/support/procedural/src/benchmark.rs @@ -53,7 +53,7 @@ mod keywords { #[derive(Clone)] struct ParamDef { name: String, - typ: Type, + _typ: Type, start: syn::GenericArgument, end: syn::GenericArgument, } @@ -229,7 +229,7 @@ fn parse_params(item_fn: &ItemFn) -> Result> { let args = segment.arguments.to_token_stream().into(); let Ok(args) = syn::parse::(args) else { return invalid_param(typ.span()) }; - params.push(ParamDef { name, typ: typ.clone(), start: args.start, end: args.end }); + params.push(ParamDef { name, _typ: typ.clone(), start: args.start, end: args.end }); } Ok(params) } @@ -681,7 +681,6 @@ pub fn benchmarks( struct UnrolledParams { param_ranges: Vec, param_names: Vec, - param_types: Vec, } impl UnrolledParams { @@ -703,14 +702,7 @@ impl UnrolledParams { quote!(#name) }) .collect(); - let param_types: Vec = params - .iter() - .map(|p| { - let typ = &p.typ; - quote!(#typ) - }) - .collect(); - UnrolledParams { param_ranges, param_names, param_types } + UnrolledParams { param_ranges, param_names } } } @@ -726,7 +718,6 @@ fn expand_benchmark( Ok(ident) => ident, Err(err) => return err.to_compile_error().into(), }; - let home = quote!(#krate::v2); let codec = quote!(#krate::frame_support::codec); let traits = quote!(#krate::frame_support::traits); let setup_stmts = benchmark_def.setup_stmts; @@ -738,7 +729,6 @@ fn expand_benchmark( let unrolled = UnrolledParams::from(&benchmark_def.params); let param_names = unrolled.param_names; let param_ranges = unrolled.param_ranges; - let param_types = unrolled.param_types; let type_use_generics = match is_instance { false => quote!(T), @@ -763,6 +753,18 @@ fn expand_benchmark( } expr_call.args = final_args; + let origin = match origin { + Expr::Cast(t) => { + let ty = t.ty.clone(); + quote! { + <::RuntimeOrigin as From<#ty>>::from(#origin); + } + }, + _ => quote! { + #origin.into(); + }, + }; + // determine call name (handles `_` and normal call syntax) let expr_span = expr_call.span(); let call_err = || { @@ -803,7 +805,7 @@ fn expand_benchmark( let __call_decoded = as #codec::Decode> ::decode(&mut &__benchmarked_call_encoded[..]) .expect("call is encoded above, encoding must be correct"); - let __origin = #origin.into(); + let __origin = #origin; as #traits::UnfilteredDispatchable>::dispatch_bypass_filter( __call_decoded, __origin, @@ -877,11 +879,6 @@ fn expand_benchmark( // benchmark function definition #fn_def - // compile-time assertions that each referenced param type implements ParamRange - #( - #home::assert_impl_all!(#param_types: #home::ParamRange); - )* - #[allow(non_camel_case_types)] #( #fn_attrs diff --git a/frame/support/test/tests/benchmark_ui/invalid_origin.rs b/frame/support/test/tests/benchmark_ui/invalid_origin.rs new file mode 100644 index 000000000..81cfc3979 --- /dev/null +++ b/frame/support/test/tests/benchmark_ui/invalid_origin.rs @@ -0,0 +1,17 @@ +use frame_benchmarking::v2::*; +#[allow(unused_imports)] +use frame_support_test::Config; +use frame_support_test::Call; + +#[benchmarks] +mod benches { + use super::*; + + #[benchmark] + fn bench() { + #[extrinsic_call] + thing(1); + } +} + +fn main() {} diff --git a/frame/support/test/tests/benchmark_ui/invalid_origin.stderr b/frame/support/test/tests/benchmark_ui/invalid_origin.stderr new file mode 100644 index 000000000..ab8499d99 --- /dev/null +++ b/frame/support/test/tests/benchmark_ui/invalid_origin.stderr @@ -0,0 +1,16 @@ +error[E0599]: no variant or associated item named `new_call_variant_thing` found for enum `Call` in the current scope + --> tests/benchmark_ui/invalid_origin.rs:6:1 + | +6 | #[benchmarks] + | ^^^^^^^^^^^^^ variant or associated item not found in `Call` + | + = note: this error originates in the attribute macro `benchmarks` (in Nightly builds, run with -Z macro-backtrace for more info) + +error[E0277]: the trait bound `::RuntimeOrigin: From<{integer}>` is not satisfied + --> tests/benchmark_ui/invalid_origin.rs:6:1 + | +6 | #[benchmarks] + | ^^^^^^^^^^^^^ the trait `From<{integer}>` is not implemented for `::RuntimeOrigin` + | + = note: required for `{integer}` to implement `Into<::RuntimeOrigin>` + = note: this error originates in the attribute macro `benchmarks` (in Nightly builds, run with -Z macro-backtrace for more info) From 910bafbf6a43f16424188a52337715e881b4414e Mon Sep 17 00:00:00 2001 From: Muharem Ismailov Date: Mon, 24 Apr 2023 16:57:45 +0200 Subject: [PATCH 411/558] Vote locks for all reasons except RESERVE (#13914) * Vote locks tip * except reserve * reason for delegate * fix tests --------- Co-authored-by: parity-processbot <> --- frame/conviction-voting/src/lib.rs | 9 +++++++-- frame/democracy/src/lib.rs | 21 ++++++++++++++++++--- frame/democracy/src/tests/lock_voting.rs | 2 +- 3 files changed, 26 insertions(+), 6 deletions(-) diff --git a/frame/conviction-voting/src/lib.rs b/frame/conviction-voting/src/lib.rs index 072e57035..3ad81486e 100644 --- a/frame/conviction-voting/src/lib.rs +++ b/frame/conviction-voting/src/lib.rs @@ -639,7 +639,12 @@ impl, I: 'static> Pallet { }, } }); - T::Currency::extend_lock(CONVICTION_VOTING_ID, who, amount, WithdrawReasons::TRANSFER); + T::Currency::extend_lock( + CONVICTION_VOTING_ID, + who, + amount, + WithdrawReasons::except(WithdrawReasons::RESERVE), + ); } /// Rejig the lock on an account. It will never get more stringent (since that would indicate @@ -669,7 +674,7 @@ impl, I: 'static> Pallet { CONVICTION_VOTING_ID, who, lock_needed, - WithdrawReasons::TRANSFER, + WithdrawReasons::except(WithdrawReasons::RESERVE), ); } } diff --git a/frame/democracy/src/lib.rs b/frame/democracy/src/lib.rs index a3d7f103a..4cfd49584 100644 --- a/frame/democracy/src/lib.rs +++ b/frame/democracy/src/lib.rs @@ -1308,7 +1308,12 @@ impl Pallet { })?; // Extend the lock to `balance` (rather than setting it) since we don't know what other // votes are in place. - T::Currency::extend_lock(DEMOCRACY_ID, who, vote.balance(), WithdrawReasons::TRANSFER); + T::Currency::extend_lock( + DEMOCRACY_ID, + who, + vote.balance(), + WithdrawReasons::except(WithdrawReasons::RESERVE), + ); ReferendumInfoOf::::insert(ref_index, ReferendumInfo::Ongoing(status)); Ok(()) } @@ -1454,7 +1459,12 @@ impl Pallet { let votes = Self::increase_upstream_delegation(&target, conviction.votes(balance)); // Extend the lock to `balance` (rather than setting it) since we don't know what other // votes are in place. - T::Currency::extend_lock(DEMOCRACY_ID, &who, balance, WithdrawReasons::TRANSFER); + T::Currency::extend_lock( + DEMOCRACY_ID, + &who, + balance, + WithdrawReasons::except(WithdrawReasons::RESERVE), + ); Ok(votes) })?; Self::deposit_event(Event::::Delegated { who, target }); @@ -1499,7 +1509,12 @@ impl Pallet { if lock_needed.is_zero() { T::Currency::remove_lock(DEMOCRACY_ID, who); } else { - T::Currency::set_lock(DEMOCRACY_ID, who, lock_needed, WithdrawReasons::TRANSFER); + T::Currency::set_lock( + DEMOCRACY_ID, + who, + lock_needed, + WithdrawReasons::except(WithdrawReasons::RESERVE), + ); } } diff --git a/frame/democracy/src/tests/lock_voting.rs b/frame/democracy/src/tests/lock_voting.rs index 9b9172ff0..31f2e3f3d 100644 --- a/frame/democracy/src/tests/lock_voting.rs +++ b/frame/democracy/src/tests/lock_voting.rs @@ -34,7 +34,7 @@ fn nay(x: u8, balance: u64) -> AccountVote { } fn the_lock(amount: u64) -> BalanceLock { - BalanceLock { id: DEMOCRACY_ID, amount, reasons: pallet_balances::Reasons::Misc } + BalanceLock { id: DEMOCRACY_ID, amount, reasons: pallet_balances::Reasons::All } } #[test] From fff96aca38aa97d0c07a34fe0bba0b3c67513d22 Mon Sep 17 00:00:00 2001 From: Alexander Samusev <41779041+alvicsam@users.noreply.github.com> Date: Mon, 24 Apr 2023 17:33:21 +0200 Subject: [PATCH 412/558] [ci] Update buildah command and version (#13989) --- .gitlab-ci.yml | 3 ++- scripts/ci/gitlab/pipeline/publish.yml | 8 ++++---- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index c6ce21c7e..59d42936e 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -49,7 +49,8 @@ variables: DOCKER_OS: "debian:stretch" ARCH: "x86_64" CI_IMAGE: "paritytech/ci-linux:production" - BUILDAH_IMAGE: "quay.io/buildah/stable:v1.27" + BUILDAH_IMAGE: "quay.io/buildah/stable:v1.29" + BUILDAH_COMMAND: "buildah --storage-driver overlay2" RELENG_SCRIPTS_BRANCH: "master" RUSTY_CACHIER_SINGLE_BRANCH: master RUSTY_CACHIER_DONT_OPERATE_ON_MAIN_BRANCH: "true" diff --git a/scripts/ci/gitlab/pipeline/publish.yml b/scripts/ci/gitlab/pipeline/publish.yml index a77209644..5a69e876a 100644 --- a/scripts/ci/gitlab/pipeline/publish.yml +++ b/scripts/ci/gitlab/pipeline/publish.yml @@ -19,7 +19,7 @@ script: - test "$DOCKER_USER" -a "$DOCKER_PASS" || ( echo "no docker credentials provided"; exit 1 ) - - buildah bud + - $BUILDAH_COMMAND build --format=docker --build-arg VCS_REF="${CI_COMMIT_SHA}" --build-arg BUILD_DATE="$(date -u '+%Y-%m-%dT%H:%M:%SZ')" @@ -29,9 +29,9 @@ --file "$DOCKERFILE" . - echo "$DOCKER_PASS" | buildah login --username "$DOCKER_USER" --password-stdin docker.io - - buildah info - - buildah push --format=v2s2 "$IMAGE_NAME:$VERSION" - - buildah push --format=v2s2 "$IMAGE_NAME:latest" + - $BUILDAH_COMMAND info + - $BUILDAH_COMMAND push --format=v2s2 "$IMAGE_NAME:$VERSION" + - $BUILDAH_COMMAND push --format=v2s2 "$IMAGE_NAME:latest" after_script: - buildah logout --all - echo "SUBSTRATE_IMAGE_NAME=${IMAGE_NAME}" | tee -a ./artifacts/$PRODUCT/build.env From 42448c2339daf43d88ec37dc7b4383928f66750f Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 24 Apr 2023 21:31:13 +0200 Subject: [PATCH 413/558] Bump enumflags2 from 0.7.5 to 0.7.7 (#13995) Bumps [enumflags2](https://github.com/meithecatte/enumflags2) from 0.7.5 to 0.7.7. - [Release notes](https://github.com/meithecatte/enumflags2/releases) - [Commits](https://github.com/meithecatte/enumflags2/compare/v0.7.5...v0.7.7) --- updated-dependencies: - dependency-name: enumflags2 dependency-type: direct:production ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- Cargo.lock | 10 +++++----- frame/identity/Cargo.toml | 2 +- frame/nfts/Cargo.toml | 2 +- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index aa7d22dac..79b79e2b2 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1982,22 +1982,22 @@ dependencies = [ [[package]] name = "enumflags2" -version = "0.7.5" +version = "0.7.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e75d4cd21b95383444831539909fbb14b9dc3fdceb2a6f5d36577329a1f55ccb" +checksum = "c041f5090df68b32bcd905365fd51769c8b9d553fe87fde0b683534f10c01bd2" dependencies = [ "enumflags2_derive", ] [[package]] name = "enumflags2_derive" -version = "0.7.4" +version = "0.7.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f58dc3c5e468259f19f2d46304a6b28f1c3d034442e14b322d2b850e36f6d5ae" +checksum = "5e9a1f9f7d83e59740248a6e14ecf93929ade55027844dfcea78beafccc15745" dependencies = [ "proc-macro2", "quote", - "syn 1.0.109", + "syn 2.0.14", ] [[package]] diff --git a/frame/identity/Cargo.toml b/frame/identity/Cargo.toml index 23c690889..2479b0609 100644 --- a/frame/identity/Cargo.toml +++ b/frame/identity/Cargo.toml @@ -14,7 +14,7 @@ targets = ["x86_64-unknown-linux-gnu"] [dependencies] codec = { package = "parity-scale-codec", version = "3.2.2", default-features = false, features = ["derive", "max-encoded-len"] } -enumflags2 = { version = "0.7.4" } +enumflags2 = { version = "0.7.7" } scale-info = { version = "2.5.0", default-features = false, features = ["derive"] } frame-benchmarking = { version = "4.0.0-dev", default-features = false, optional = true, path = "../benchmarking" } frame-support = { version = "4.0.0-dev", default-features = false, path = "../support" } diff --git a/frame/nfts/Cargo.toml b/frame/nfts/Cargo.toml index 96d46dacb..73df7518e 100644 --- a/frame/nfts/Cargo.toml +++ b/frame/nfts/Cargo.toml @@ -14,7 +14,7 @@ targets = ["x86_64-unknown-linux-gnu"] [dependencies] codec = { package = "parity-scale-codec", version = "3.2.2", default-features = false } -enumflags2 = { version = "0.7.5" } +enumflags2 = { version = "0.7.7" } log = { version = "0.4.17", default-features = false } scale-info = { version = "2.5.0", default-features = false, features = ["derive"] } frame-benchmarking = { version = "4.0.0-dev", default-features = false, optional = true, path = "../benchmarking" } From 2b91202014e718525f90c17eb0545e2f2a3261ff Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bastian=20K=C3=B6cher?= Date: Mon, 24 Apr 2023 22:23:26 +0200 Subject: [PATCH 414/558] frame-support-procedural: Fix detection of the tuples feature (#13996) We didn't had the tuples features declared for the `frame-support-procedural` crate and thus, it could not properly detect that the feature was already enabled. --- frame/support/Cargo.toml | 4 ++-- frame/support/procedural/Cargo.toml | 4 ++++ 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/frame/support/Cargo.toml b/frame/support/Cargo.toml index f62609585..fe068d936 100644 --- a/frame/support/Cargo.toml +++ b/frame/support/Cargo.toml @@ -80,5 +80,5 @@ no-metadata-docs = ["frame-support-procedural/no-metadata-docs", "sp-api/no-meta full-metadata-docs = ["scale-info/docs"] # Generate impl-trait for tuples with the given number of tuples. Will be needed as the number of # pallets in a runtime grows. Does increase the compile time! -tuples-96 = [] -tuples-128 = [] +tuples-96 = ["frame-support-procedural/tuples-96"] +tuples-128 = ["frame-support-procedural/tuples-128"] diff --git a/frame/support/procedural/Cargo.toml b/frame/support/procedural/Cargo.toml index e508730f4..1a17924b4 100644 --- a/frame/support/procedural/Cargo.toml +++ b/frame/support/procedural/Cargo.toml @@ -29,3 +29,7 @@ proc-macro-warning = { version = "0.3.0", default-features = false } default = ["std"] std = [] no-metadata-docs = [] +# Generate impl-trait for tuples with the given number of tuples. Will be needed as the number of +# pallets in a runtime grows. Does increase the compile time! +tuples-96 = [] +tuples-128 = [] From 222d1189267db873be9466e5658d7e2b033e44ab Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bastian=20K=C3=B6cher?= Date: Tue, 25 Apr 2023 09:21:54 +0200 Subject: [PATCH 415/558] pallet-democracy: Do not request the proposal when scheduling (#13827) The requesting of the proposal is actually done now in `pallet-scheduler`. Fixes: https://github.com/paritytech/substrate/issues/13534 --- frame/democracy/src/lib.rs | 5 ----- frame/democracy/src/tests/metadata.rs | 22 +++++++++++----------- frame/support/src/traits/schedule.rs | 2 ++ 3 files changed, 13 insertions(+), 16 deletions(-) diff --git a/frame/democracy/src/lib.rs b/frame/democracy/src/lib.rs index 4cfd49584..69438eba9 100644 --- a/frame/democracy/src/lib.rs +++ b/frame/democracy/src/lib.rs @@ -1606,11 +1606,6 @@ impl Pallet { if approved { Self::deposit_event(Event::::Passed { ref_index: index }); - // Actually `hold` the proposal now since we didn't hold it when it came in via the - // submit extrinsic and we now know that it will be needed. This will be reversed by - // Scheduler pallet once it is executed which assumes that we will already have placed - // a `hold` on it. - T::Preimages::hold(&status.proposal); // Earliest it can be scheduled for is next block. let when = now.saturating_add(status.delay.max(One::one())); diff --git a/frame/democracy/src/tests/metadata.rs b/frame/democracy/src/tests/metadata.rs index 59229abfe..5a36d80b7 100644 --- a/frame/democracy/src/tests/metadata.rs +++ b/frame/democracy/src/tests/metadata.rs @@ -33,7 +33,7 @@ fn set_external_metadata_works() { Error::::NoProposal, ); // create an external proposal. - assert_ok!(Democracy::external_propose(RuntimeOrigin::signed(2), set_balance_proposal(2),)); + assert_ok!(Democracy::external_propose(RuntimeOrigin::signed(2), set_balance_proposal(2))); assert!(>::exists()); // fails to set metadata with non external origin. assert_noop!( @@ -47,7 +47,7 @@ fn set_external_metadata_works() { ); // set metadata successful. let hash = note_preimage(1); - assert_ok!(Democracy::set_metadata(RuntimeOrigin::signed(2), owner.clone(), Some(hash),),); + assert_ok!(Democracy::set_metadata(RuntimeOrigin::signed(2), owner.clone(), Some(hash))); System::assert_last_event(RuntimeEvent::Democracy(crate::Event::MetadataSet { owner, hash, @@ -61,11 +61,11 @@ fn clear_metadata_works() { // metadata owner is an external proposal. let owner = MetadataOwner::External; // create an external proposal. - assert_ok!(Democracy::external_propose(RuntimeOrigin::signed(2), set_balance_proposal(2),)); + assert_ok!(Democracy::external_propose(RuntimeOrigin::signed(2), set_balance_proposal(2))); assert!(>::exists()); // set metadata. let hash = note_preimage(1); - assert_ok!(Democracy::set_metadata(RuntimeOrigin::signed(2), owner.clone(), Some(hash),)); + assert_ok!(Democracy::set_metadata(RuntimeOrigin::signed(2), owner.clone(), Some(hash))); // fails to clear metadata with a wrong origin. assert_noop!( Democracy::set_metadata(RuntimeOrigin::signed(1), owner.clone(), None), @@ -92,18 +92,18 @@ fn set_proposal_metadata_works() { let owner = MetadataOwner::Proposal(Democracy::public_prop_count() - 1); // fails to set non-existing preimage. assert_noop!( - Democracy::set_metadata(RuntimeOrigin::signed(1), owner.clone(), Some(invalid_hash),), + Democracy::set_metadata(RuntimeOrigin::signed(1), owner.clone(), Some(invalid_hash)), Error::::PreimageNotExist, ); // note preimage. let hash = note_preimage(1); // fails to set a preimage if an origin is not a proposer. assert_noop!( - Democracy::set_metadata(RuntimeOrigin::signed(3), owner.clone(), Some(hash),), + Democracy::set_metadata(RuntimeOrigin::signed(3), owner.clone(), Some(hash)), Error::::NoPermission, ); // set metadata successful. - assert_ok!(Democracy::set_metadata(RuntimeOrigin::signed(1), owner.clone(), Some(hash),),); + assert_ok!(Democracy::set_metadata(RuntimeOrigin::signed(1), owner.clone(), Some(hash))); System::assert_last_event(RuntimeEvent::Democracy(crate::Event::MetadataSet { owner, hash, @@ -120,7 +120,7 @@ fn clear_proposal_metadata_works() { let owner = MetadataOwner::Proposal(Democracy::public_prop_count() - 1); // set metadata. let hash = note_preimage(1); - assert_ok!(Democracy::set_metadata(RuntimeOrigin::signed(1), owner.clone(), Some(hash),)); + assert_ok!(Democracy::set_metadata(RuntimeOrigin::signed(1), owner.clone(), Some(hash))); // fails to clear metadata with a wrong origin. assert_noop!( Democracy::set_metadata(RuntimeOrigin::signed(3), owner.clone(), None), @@ -150,16 +150,16 @@ fn set_referendum_metadata_by_root() { let hash = note_preimage(1); // fails to set if not a root. assert_noop!( - Democracy::set_metadata(RuntimeOrigin::signed(3), owner.clone(), Some(hash),), + Democracy::set_metadata(RuntimeOrigin::signed(3), owner.clone(), Some(hash)), Error::::NoPermission, ); // fails to clear if not a root. assert_noop!( - Democracy::set_metadata(RuntimeOrigin::signed(3), owner.clone(), None,), + Democracy::set_metadata(RuntimeOrigin::signed(3), owner.clone(), None), Error::::NoPermission, ); // succeed to set metadata by a root for an ongoing referendum. - assert_ok!(Democracy::set_metadata(RuntimeOrigin::root(), owner.clone(), Some(hash),)); + assert_ok!(Democracy::set_metadata(RuntimeOrigin::root(), owner.clone(), Some(hash))); System::assert_last_event(RuntimeEvent::Democracy(crate::Event::MetadataSet { owner: owner.clone(), hash, diff --git a/frame/support/src/traits/schedule.rs b/frame/support/src/traits/schedule.rs index 4e17092ae..74a595114 100644 --- a/frame/support/src/traits/schedule.rs +++ b/frame/support/src/traits/schedule.rs @@ -438,6 +438,8 @@ pub mod v3 { /// Schedule a dispatch to happen at the beginning of some block in the future. /// /// - `id`: The identity of the task. This must be unique and will return an error if not. + /// + /// NOTE: This will request `call` to be made available. fn schedule_named( id: TaskName, when: DispatchTime, From c92eb70d160b33861dc754831ca66d9920a58dad Mon Sep 17 00:00:00 2001 From: Alexander Samusev <41779041+alvicsam@users.noreply.github.com> Date: Tue, 25 Apr 2023 10:15:57 +0200 Subject: [PATCH 416/558] [ci] Add message to cargo-deny (#14001) * [ci] Add message to cargo-deny * fix cargo-deny-licenses --- scripts/ci/gitlab/pipeline/test.yml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/scripts/ci/gitlab/pipeline/test.yml b/scripts/ci/gitlab/pipeline/test.yml index 7e84694c2..c73268caa 100644 --- a/scripts/ci/gitlab/pipeline/test.yml +++ b/scripts/ci/gitlab/pipeline/test.yml @@ -36,6 +36,9 @@ cargo-deny-licenses: - !reference [.rusty-cachier, after_script] - echo "___The complete log is in the artifacts___" - $CARGO_DENY_CMD 2> deny.log + - if [ $CI_JOB_STATUS != 'success' ]; then + echo 'Please check license of your crate or add an exception to scripts/ci/deny.toml'; + fi artifacts: name: $CI_COMMIT_SHORT_SHA expire_in: 3 days From b5846ccc8480806aa6035ae4d2e89d61930f697e Mon Sep 17 00:00:00 2001 From: yjh Date: Tue, 25 Apr 2023 16:18:35 +0800 Subject: [PATCH 417/558] refactor(cli): Make some run params reusable (#13870) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * refactor: extract TelemetryParams and PrometheusParams * improve run_cmd docs * extract `RuntimeParams` * use `normalize` * keep params types same style * improve `max_runtime_instances` * fmt * add license and improve code * Update client/cli/src/params/runtime_params.rs Co-authored-by: Bastian Köcher --------- Co-authored-by: Bastian Köcher --- client/cli/src/commands/run_cmd.rs | 121 +++++---------------- client/cli/src/params/keystore_params.rs | 2 +- client/cli/src/params/message_params.rs | 5 +- client/cli/src/params/mod.rs | 7 +- client/cli/src/params/prometheus_params.rs | 63 +++++++++++ client/cli/src/params/pruning_params.rs | 2 +- client/cli/src/params/runtime_params.rs | 43 ++++++++ client/cli/src/params/shared_params.rs | 2 +- client/cli/src/params/telemetry_params.rs | 68 ++++++++++++ 9 files changed, 210 insertions(+), 103 deletions(-) create mode 100644 client/cli/src/params/prometheus_params.rs create mode 100644 client/cli/src/params/runtime_params.rs create mode 100644 client/cli/src/params/telemetry_params.rs diff --git a/client/cli/src/commands/run_cmd.rs b/client/cli/src/commands/run_cmd.rs index 9441acecc..a38ba6f49 100644 --- a/client/cli/src/commands/run_cmd.rs +++ b/client/cli/src/commands/run_cmd.rs @@ -23,7 +23,7 @@ use crate::{ ImportParams, KeystoreParams, NetworkParams, OffchainWorkerParams, SharedParams, TransactionPoolParams, }, - CliConfiguration, + CliConfiguration, PrometheusParams, RuntimeParams, TelemetryParams, }; use clap::Parser; use regex::Regex; @@ -116,12 +116,6 @@ pub struct RunCmd { #[arg(long)] pub rpc_max_subscriptions_per_connection: Option, - /// Expose Prometheus exporter on all interfaces. - /// - /// Default is local. - #[arg(long)] - pub prometheus_external: bool, - /// DEPRECATED, IPC support has been removed. #[arg(long, value_name = "PATH")] pub ipc_path: Option, @@ -151,36 +145,23 @@ pub struct RunCmd { #[arg(long, value_name = "ORIGINS", value_parser = parse_cors)] pub rpc_cors: Option, - /// Specify Prometheus exporter TCP Port. - #[arg(long, value_name = "PORT")] - pub prometheus_port: Option, - - /// Do not expose a Prometheus exporter endpoint. - /// - /// Prometheus metric endpoint is enabled by default. - #[arg(long)] - pub no_prometheus: bool, - /// The human-readable name for this node. /// - /// The node name will be reported to the telemetry server, if enabled. + /// It's used as network node name. #[arg(long, value_name = "NAME")] pub name: Option, - /// Disable connecting to the Substrate telemetry server. - /// - /// Telemetry is on by default on global chains. - #[arg(long)] - pub no_telemetry: bool, + #[allow(missing_docs)] + #[clap(flatten)] + pub telemetry_params: TelemetryParams, - /// The URL of the telemetry server to connect to. - /// - /// This flag can be passed multiple times as a means to specify multiple - /// telemetry endpoints. Verbosity levels range from 0-9, with 0 denoting - /// the least verbosity. - /// Expected format is 'URL VERBOSITY', e.g. `--telemetry-url 'wss://foo/bar 0'`. - #[arg(long = "telemetry-url", value_name = "URL VERBOSITY", value_parser = parse_telemetry_endpoints)] - pub telemetry_endpoints: Vec<(String, u8)>, + #[allow(missing_docs)] + #[clap(flatten)] + pub prometheus_params: PrometheusParams, + + #[allow(missing_docs)] + #[clap(flatten)] + pub runtime_params: RuntimeParams, #[allow(missing_docs)] #[clap(flatten)] @@ -202,6 +183,10 @@ pub struct RunCmd { #[clap(flatten)] pub pool_config: TransactionPoolParams, + #[allow(missing_docs)] + #[clap(flatten)] + pub keystore_params: KeystoreParams, + /// Shortcut for `--name Alice --validator` with session keys for `Alice` added to keystore. #[arg(long, conflicts_with_all = &["bob", "charlie", "dave", "eve", "ferdie", "one", "two"])] pub alice: bool, @@ -239,20 +224,6 @@ pub struct RunCmd { #[arg(long)] pub force_authoring: bool, - #[allow(missing_docs)] - #[clap(flatten)] - pub keystore_params: KeystoreParams, - - /// The size of the instances cache for each runtime. - /// - /// The default value is 8 and the values higher than 256 are ignored. - #[arg(long)] - pub max_runtime_instances: Option, - - /// Maximum number of different runtimes that can be cached. - #[arg(long, default_value_t = 2)] - pub runtime_cache_size: u8, - /// Run a temporary node. /// /// A temporary directory will be created to store the configuration and will be deleted @@ -345,11 +316,12 @@ impl CliConfiguration for RunCmd { &self, chain_spec: &Box, ) -> Result> { - Ok(if self.no_telemetry { + let params = &self.telemetry_params; + Ok(if params.no_telemetry { None - } else if !self.telemetry_endpoints.is_empty() { + } else if !params.telemetry_endpoints.is_empty() { Some( - TelemetryEndpoints::new(self.telemetry_endpoints.clone()) + TelemetryEndpoints::new(params.telemetry_endpoints.clone()) .map_err(|e| e.to_string())?, ) } else { @@ -361,7 +333,7 @@ impl CliConfiguration for RunCmd { let keyring = self.get_keyring(); let is_authority = self.validator || is_dev || keyring.is_some(); - Ok(if is_authority { sc_service::Role::Authority } else { sc_service::Role::Full }) + Ok(if is_authority { Role::Authority } else { Role::Full }) } fn force_authoring(&self) -> Result { @@ -374,20 +346,9 @@ impl CliConfiguration for RunCmd { default_listen_port: u16, chain_spec: &Box, ) -> Result> { - Ok(if self.no_prometheus { - None - } else { - let interface = - if self.prometheus_external { Ipv4Addr::UNSPECIFIED } else { Ipv4Addr::LOCALHOST }; - - Some(PrometheusConfig::new_with_default_registry( - SocketAddr::new( - interface.into(), - self.prometheus_port.unwrap_or(default_listen_port), - ), - chain_spec.id().into(), - )) - }) + Ok(self + .prometheus_params + .prometheus_config(default_listen_port, chain_spec.id().to_string())) } fn disable_grandpa(&self) -> Result { @@ -474,11 +435,11 @@ impl CliConfiguration for RunCmd { } fn max_runtime_instances(&self) -> Result> { - Ok(self.max_runtime_instances.map(|x| x.min(256))) + Ok(Some(self.runtime_params.max_runtime_instances)) } fn runtime_cache_size(&self) -> Result { - Ok(self.runtime_cache_size) + Ok(self.runtime_params.runtime_cache_size) } fn base_path(&self) -> Result> { @@ -546,36 +507,6 @@ fn rpc_interface( } } -#[derive(Debug)] -enum TelemetryParsingError { - MissingVerbosity, - VerbosityParsingError(std::num::ParseIntError), -} - -impl std::error::Error for TelemetryParsingError {} - -impl std::fmt::Display for TelemetryParsingError { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - match self { - TelemetryParsingError::MissingVerbosity => write!(f, "Verbosity level missing"), - TelemetryParsingError::VerbosityParsingError(e) => write!(f, "{}", e), - } - } -} - -fn parse_telemetry_endpoints(s: &str) -> std::result::Result<(String, u8), TelemetryParsingError> { - let pos = s.find(' '); - match pos { - None => Err(TelemetryParsingError::MissingVerbosity), - Some(pos_) => { - let url = s[..pos_].to_string(); - let verbosity = - s[pos_ + 1..].parse().map_err(TelemetryParsingError::VerbosityParsingError)?; - Ok((url, verbosity)) - }, - } -} - /// CORS setting /// /// The type is introduced to overcome `Option>` handling of `clap`. diff --git a/client/cli/src/params/keystore_params.rs b/client/cli/src/params/keystore_params.rs index 8933110c4..a2fdd6b22 100644 --- a/client/cli/src/params/keystore_params.rs +++ b/client/cli/src/params/keystore_params.rs @@ -61,7 +61,7 @@ pub struct KeystoreParams { pub password_filename: Option, } -/// Parse a sercret string, returning a displayable error. +/// Parse a secret string, returning a displayable error. pub fn secret_string_from_str(s: &str) -> std::result::Result { std::str::FromStr::from_str(s).map_err(|_| "Could not get SecretString".to_string()) } diff --git a/client/cli/src/params/message_params.rs b/client/cli/src/params/message_params.rs index a935bbb25..3fcb6f2c6 100644 --- a/client/cli/src/params/message_params.rs +++ b/client/cli/src/params/message_params.rs @@ -20,14 +20,13 @@ use crate::error::Error; use array_bytes::{hex2bytes, hex_bytes2hex_str}; -use clap::Parser; +use clap::Args; use std::io::BufRead; /// Params to configure how a message should be passed into a command. -#[derive(Parser, Debug, Clone)] +#[derive(Debug, Clone, Args)] pub struct MessageParams { /// Message to process. Will be read from STDIN otherwise. - /// /// The message is assumed to be raw bytes per default. Use `--hex` for hex input. Can /// optionally be prefixed with `0x` in the hex case. #[arg(long)] diff --git a/client/cli/src/params/mod.rs b/client/cli/src/params/mod.rs index f9c840d2d..247ffc0e0 100644 --- a/client/cli/src/params/mod.rs +++ b/client/cli/src/params/mod.rs @@ -22,8 +22,11 @@ mod message_params; mod network_params; mod node_key_params; mod offchain_worker_params; +mod prometheus_params; mod pruning_params; +mod runtime_params; mod shared_params; +mod telemetry_params; mod transaction_pool_params; use crate::arg_enums::{CryptoScheme, OutputType}; @@ -37,8 +40,8 @@ use std::{fmt::Debug, str::FromStr}; pub use crate::params::{ database_params::*, import_params::*, keystore_params::*, message_params::*, network_params::*, - node_key_params::*, offchain_worker_params::*, pruning_params::*, shared_params::*, - transaction_pool_params::*, + node_key_params::*, offchain_worker_params::*, prometheus_params::*, pruning_params::*, + runtime_params::*, shared_params::*, telemetry_params::*, transaction_pool_params::*, }; /// Parse Ss58AddressFormat diff --git a/client/cli/src/params/prometheus_params.rs b/client/cli/src/params/prometheus_params.rs new file mode 100644 index 000000000..69199ad5b --- /dev/null +++ b/client/cli/src/params/prometheus_params.rs @@ -0,0 +1,63 @@ +// This file is part of Substrate. + +// Copyright (C) Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +use clap::Args; +use sc_service::config::PrometheusConfig; +use std::net::{Ipv4Addr, SocketAddr}; + +/// Parameters used to config prometheus. +#[derive(Debug, Clone, Args)] +pub struct PrometheusParams { + /// Specify Prometheus exporter TCP Port. + #[arg(long, value_name = "PORT")] + pub prometheus_port: Option, + /// Expose Prometheus exporter on all interfaces. + /// + /// Default is local. + #[arg(long)] + pub prometheus_external: bool, + /// Do not expose a Prometheus exporter endpoint. + /// + /// Prometheus metric endpoint is enabled by default. + #[arg(long)] + pub no_prometheus: bool, +} + +impl PrometheusParams { + /// Creates [`PrometheusConfig`]. + pub fn prometheus_config( + &self, + default_listen_port: u16, + chain_id: String, + ) -> Option { + if self.no_prometheus { + None + } else { + let interface = + if self.prometheus_external { Ipv4Addr::UNSPECIFIED } else { Ipv4Addr::LOCALHOST }; + + Some(PrometheusConfig::new_with_default_registry( + SocketAddr::new( + interface.into(), + self.prometheus_port.unwrap_or(default_listen_port), + ), + chain_id, + )) + } + } +} diff --git a/client/cli/src/params/pruning_params.rs b/client/cli/src/params/pruning_params.rs index 59eeb13cd..757da2dd9 100644 --- a/client/cli/src/params/pruning_params.rs +++ b/client/cli/src/params/pruning_params.rs @@ -21,7 +21,7 @@ use clap::Args; use sc_service::{BlocksPruning, PruningMode}; /// Parameters to define the pruning mode -#[derive(Debug, Clone, PartialEq, Args)] +#[derive(Debug, Clone, Args)] pub struct PruningParams { /// Specify the state pruning mode. /// diff --git a/client/cli/src/params/runtime_params.rs b/client/cli/src/params/runtime_params.rs new file mode 100644 index 000000000..79035fc7d --- /dev/null +++ b/client/cli/src/params/runtime_params.rs @@ -0,0 +1,43 @@ +// This file is part of Substrate. + +// Copyright (C) Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +use clap::Args; +use std::str::FromStr; + +/// Parameters used to config runtime. +#[derive(Debug, Clone, Args)] +pub struct RuntimeParams { + /// The size of the instances cache for each runtime. The values higher than 256 are illegal. + #[arg(long, default_value_t = 8, value_parser = parse_max_runtime_instances)] + pub max_runtime_instances: usize, + + /// Maximum number of different runtimes that can be cached. + #[arg(long, default_value_t = 2)] + pub runtime_cache_size: u8, +} + +fn parse_max_runtime_instances(s: &str) -> Result { + let max_runtime_instances = usize::from_str(s) + .map_err(|_err| format!("Illegal `--max-runtime-instances` value: {s}"))?; + + if max_runtime_instances > 256 { + Err(format!("Illegal `--max-runtime-instances` value: {max_runtime_instances} is more than the allowed maximum of `256` ")) + } else { + Ok(max_runtime_instances) + } +} diff --git a/client/cli/src/params/shared_params.rs b/client/cli/src/params/shared_params.rs index 46f539003..913a6c436 100644 --- a/client/cli/src/params/shared_params.rs +++ b/client/cli/src/params/shared_params.rs @@ -22,7 +22,7 @@ use sc_service::config::BasePath; use std::path::PathBuf; /// Shared parameters used by all `CoreParams`. -#[derive(Debug, Clone, PartialEq, Args)] +#[derive(Debug, Clone, Args)] pub struct SharedParams { /// Specify the chain specification. /// diff --git a/client/cli/src/params/telemetry_params.rs b/client/cli/src/params/telemetry_params.rs new file mode 100644 index 000000000..ca0964198 --- /dev/null +++ b/client/cli/src/params/telemetry_params.rs @@ -0,0 +1,68 @@ +// This file is part of Substrate. + +// Copyright (C) Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +use clap::Args; + +/// Parameters used to config telemetry. +#[derive(Debug, Clone, Args)] +pub struct TelemetryParams { + /// Disable connecting to the Substrate telemetry server. + /// + /// Telemetry is on by default on global chains. + #[arg(long)] + pub no_telemetry: bool, + + /// The URL of the telemetry server to connect to. + /// + /// This flag can be passed multiple times as a means to specify multiple + /// telemetry endpoints. Verbosity levels range from 0-9, with 0 denoting + /// the least verbosity. + /// Expected format is 'URL VERBOSITY', e.g. `--telemetry-url 'wss://foo/bar 0'`. + #[arg(long = "telemetry-url", value_name = "URL VERBOSITY", value_parser = parse_telemetry_endpoints)] + pub telemetry_endpoints: Vec<(String, u8)>, +} + +#[derive(Debug)] +enum TelemetryParsingError { + MissingVerbosity, + VerbosityParsingError(std::num::ParseIntError), +} + +impl std::error::Error for TelemetryParsingError {} + +impl std::fmt::Display for TelemetryParsingError { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + TelemetryParsingError::MissingVerbosity => write!(f, "Verbosity level missing"), + TelemetryParsingError::VerbosityParsingError(e) => write!(f, "{}", e), + } + } +} + +fn parse_telemetry_endpoints(s: &str) -> Result<(String, u8), TelemetryParsingError> { + let pos = s.find(' '); + match pos { + None => Err(TelemetryParsingError::MissingVerbosity), + Some(pos_) => { + let url = s[..pos_].to_string(); + let verbosity = + s[pos_ + 1..].parse().map_err(TelemetryParsingError::VerbosityParsingError)?; + Ok((url, verbosity)) + }, + } +} From 8cb72571798a0d2d9ad089ba977c310ddb2b5c11 Mon Sep 17 00:00:00 2001 From: PG Herveou Date: Tue, 25 Apr 2023 13:46:20 +0200 Subject: [PATCH 418/558] contracts Add LOG_TARGET constant (#14002) * contracts Add LOG_TARGET constant * Update frame/contracts/src/lib.rs Co-authored-by: Sasha Gryaznov --------- Co-authored-by: Sasha Gryaznov --- frame/contracts/src/exec.rs | 6 +++--- frame/contracts/src/lib.rs | 7 +++++++ frame/contracts/src/storage/meter.rs | 6 +++--- frame/contracts/src/wasm/mod.rs | 6 +++--- frame/contracts/src/wasm/prepare.rs | 10 +++++----- 5 files changed, 21 insertions(+), 14 deletions(-) diff --git a/frame/contracts/src/exec.rs b/frame/contracts/src/exec.rs index 574f4495e..46a611dbf 100644 --- a/frame/contracts/src/exec.rs +++ b/frame/contracts/src/exec.rs @@ -19,7 +19,7 @@ use crate::{ gas::GasMeter, storage::{self, DepositAccount, WriteOutcome}, BalanceOf, CodeHash, Config, ContractInfo, ContractInfoOf, DebugBufferVec, Determinism, Error, - Event, Nonce, Pallet as Contracts, Schedule, System, + Event, Nonce, Pallet as Contracts, Schedule, System, LOG_TARGET, }; use frame_support::{ crypto::ecdsa::ECDSAExt, @@ -1002,7 +1002,7 @@ where } else { if let Some((msg, false)) = self.debug_message.as_ref().map(|m| (m, m.is_empty())) { log::debug!( - target: "runtime::contracts", + target: LOG_TARGET, "Execution finished with debug buffer: {}", core::str::from_utf8(msg).unwrap_or(""), ); @@ -1331,7 +1331,7 @@ where .try_extend(&mut msg.bytes()) .map_err(|_| { log::debug!( - target: "runtime::contracts", + target: LOG_TARGET, "Debug buffer (of {} bytes) exhausted!", DebugBufferVec::::bound(), ) diff --git a/frame/contracts/src/lib.rs b/frame/contracts/src/lib.rs index fa230a8ca..118da36ae 100644 --- a/frame/contracts/src/lib.rs +++ b/frame/contracts/src/lib.rs @@ -163,6 +163,13 @@ type OldWeight = u64; /// that this value makes sense for a memory location or length. const SENTINEL: u32 = u32::MAX; +/// The target that is used for the log output emitted by this crate. +/// +/// Hence you can use this target to selectively increase the log level for this crate. +/// +/// Example: `RUST_LOG=runtime::contracts=debug my_code --dev` +const LOG_TARGET: &str = "runtime::contracts"; + #[frame_support::pallet] pub mod pallet { use super::*; diff --git a/frame/contracts/src/storage/meter.rs b/frame/contracts/src/storage/meter.rs index 497ee03ac..51a0af574 100644 --- a/frame/contracts/src/storage/meter.rs +++ b/frame/contracts/src/storage/meter.rs @@ -19,7 +19,7 @@ use crate::{ storage::{ContractInfo, DepositAccount}, - BalanceOf, Config, Error, Inspect, Pallet, System, + BalanceOf, Config, Error, Inspect, Pallet, System, LOG_TARGET, }; use codec::Encode; use frame_support::{ @@ -502,7 +502,7 @@ impl Ext for ReservingExt { ); if let Err(err) = result { log::error!( - target: "runtime::contracts", + target: LOG_TARGET, "Failed to transfer storage deposit {:?} from origin {:?} to deposit account {:?}: {:?}", amount, origin, deposit_account, err, ); @@ -531,7 +531,7 @@ impl Ext for ReservingExt { ); if matches!(result, Err(_)) { log::error!( - target: "runtime::contracts", + target: LOG_TARGET, "Failed to refund storage deposit {:?} from deposit account {:?} to origin {:?}: {:?}", amount, deposit_account, origin, result, ); diff --git a/frame/contracts/src/wasm/mod.rs b/frame/contracts/src/wasm/mod.rs index c8f1be6dd..4723f0c83 100644 --- a/frame/contracts/src/wasm/mod.rs +++ b/frame/contracts/src/wasm/mod.rs @@ -43,7 +43,7 @@ use crate::{ exec::{ExecResult, Executable, ExportedFunction, Ext}, gas::GasMeter, AccountIdOf, BalanceOf, CodeHash, CodeVec, Config, Error, OwnerInfoOf, RelaxedCodeVec, - Schedule, + Schedule, LOG_TARGET, }; use codec::{Decode, Encode, MaxEncodedLen}; use frame_support::dispatch::{DispatchError, DispatchResult}; @@ -326,7 +326,7 @@ impl Executable for PrefabWasmModule { }, ) .map_err(|msg| { - log::debug!(target: "runtime::contracts", "failed to instantiate code: {}", msg); + log::debug!(target: LOG_TARGET, "failed to instantiate code: {}", msg); Error::::CodeRejected })?; store.data_mut().set_memory(memory); @@ -335,7 +335,7 @@ impl Executable for PrefabWasmModule { .get_export(&store, function.identifier()) .and_then(|export| export.into_func()) .ok_or_else(|| { - log::error!(target: "runtime::contracts", "failed to find entry point"); + log::error!(target: LOG_TARGET, "failed to find entry point"); Error::::CodeRejected })?; diff --git a/frame/contracts/src/wasm/prepare.rs b/frame/contracts/src/wasm/prepare.rs index f0065d77a..14fec8347 100644 --- a/frame/contracts/src/wasm/prepare.rs +++ b/frame/contracts/src/wasm/prepare.rs @@ -25,7 +25,7 @@ use crate::{ wasm::{ runtime::AllowDeprecatedInterface, Determinism, Environment, OwnerInfo, PrefabWasmModule, }, - AccountIdOf, CodeVec, Config, Error, Schedule, + AccountIdOf, CodeVec, Config, Error, Schedule, LOG_TARGET, }; use codec::{Encode, MaxEncodedLen}; use sp_runtime::{traits::Hash, DispatchError}; @@ -379,7 +379,7 @@ where }) .validate_all(original_code) .map_err(|err| { - log::debug!(target: "runtime::contracts", "{}", err); + log::debug!(target: LOG_TARGET, "{}", err); (Error::::CodeRejected.into(), "validation of new code failed") })?; @@ -403,7 +403,7 @@ where Ok((code, memory_limits)) })() .map_err(|msg: &str| { - log::debug!(target: "runtime::contracts", "new code rejected: {}", msg); + log::debug!(target: LOG_TARGET, "new code rejected: {}", msg); (Error::::CodeRejected.into(), msg) })?; @@ -426,7 +426,7 @@ where }, ) .map_err(|err| { - log::debug!(target: "runtime::contracts", "{}", err); + log::debug!(target: LOG_TARGET, "{}", err); (Error::::CodeRejected.into(), "new code rejected after instrumentation") })?; } @@ -518,7 +518,7 @@ where InstrumentReason::Reinstrument, ) .map_err(|(err, msg)| { - log::error!(target: "runtime::contracts", "CodeRejected during reinstrument: {}", msg); + log::error!(target: LOG_TARGET, "CodeRejected during reinstrument: {}", msg); err }) .map(|(code, _)| code) From 6f0f5a92739b92199b3345fc4a716211c8a8b46f Mon Sep 17 00:00:00 2001 From: Qinxuan Chen Date: Tue, 25 Apr 2023 19:58:21 +0800 Subject: [PATCH 419/558] frame-support: migrate some tests from `decl_*` macros to the new `pallet` macros (#12401) * frame-support: migrate some tests from decl macros to new pallet attribute macros * Remove useless type alias * Remove useless type alias * Work around for rust issue 52234 Signed-off-by: Oliver Tale-Yazdi * Fix tests Signed-off-by: Oliver Tale-Yazdi * Use polkadot-compatible paste version Signed-off-by: Oliver Tale-Yazdi * Fix crate access and add tests Signed-off-by: Oliver Tale-Yazdi * Typo Signed-off-by: Oliver Tale-Yazdi --------- Signed-off-by: Oliver Tale-Yazdi Co-authored-by: Oliver Tale-Yazdi Co-authored-by: parity-processbot <> --- Cargo.lock | 5 +- frame/support/src/dispatch.rs | 183 +++++++++----- frame/support/src/lib.rs | 237 ++++++++++++------ .../src/storage/generator/double_map.rs | 43 +--- frame/support/src/storage/generator/map.rs | 43 +--- frame/support/src/storage/generator/mod.rs | 128 ++++++++-- frame/support/src/storage/generator/nmap.rs | 61 ++--- primitives/core/Cargo.toml | 1 + primitives/core/src/lib.rs | 85 ++++--- primitives/runtime/src/lib.rs | 20 ++ 10 files changed, 490 insertions(+), 316 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 79b79e2b2..8d8d2d59a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -7239,9 +7239,9 @@ dependencies = [ [[package]] name = "paste" -version = "1.0.11" +version = "1.0.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d01a5bd0424d00070b0098dd17ebca6f961a959dead1dbcbbbc1d1cd8d3deeba" +checksum = "9f746c4065a8fa3fe23974dd82f15431cc8d40779821001404d10d2e79ca7d79" [[package]] name = "pbkdf2" @@ -10503,6 +10503,7 @@ dependencies = [ "merlin", "parity-scale-codec", "parking_lot 0.12.1", + "paste", "primitive-types", "rand 0.8.5", "regex", diff --git a/frame/support/src/dispatch.rs b/frame/support/src/dispatch.rs index 9e949ef6d..c75d75b41 100644 --- a/frame/support/src/dispatch.rs +++ b/frame/support/src/dispatch.rs @@ -3532,112 +3532,177 @@ mod tests { #[allow(dead_code)] mod weight_tests { use super::{tests::*, *}; - use sp_core::{parameter_types, Get}; + use sp_core::parameter_types; + use sp_runtime::{generic, traits::BlakeTwo256}; use sp_weights::RuntimeDbWeight; - pub trait Config: 'static { - type RuntimeOrigin; - type Balance; - type BlockNumber; - type DbWeight: Get; - type PalletInfo: crate::traits::PalletInfo; - } + pub use self::frame_system::{Call, Config, Pallet}; - pub struct TraitImpl {} + #[crate::pallet(dev_mode)] + pub mod frame_system { + use super::{frame_system, frame_system::pallet_prelude::*}; + pub use crate::dispatch::RawOrigin; + use crate::pallet_prelude::*; - parameter_types! { - pub const DbWeight: RuntimeDbWeight = RuntimeDbWeight { - read: 100, - write: 1000, - }; - } + #[pallet::pallet] + pub struct Pallet(PhantomData); - impl Config for TraitImpl { - type RuntimeOrigin = u32; - type BlockNumber = u32; - type Balance = u32; - type DbWeight = DbWeight; - type PalletInfo = crate::tests::PanicPalletInfo; - } + #[pallet::config] + #[pallet::disable_frame_system_supertrait_check] + pub trait Config: 'static { + type BlockNumber: Parameter + Default + MaxEncodedLen; + type AccountId; + type Balance; + type BaseCallFilter: crate::traits::Contains; + type RuntimeOrigin; + type RuntimeCall; + type PalletInfo: crate::traits::PalletInfo; + type DbWeight: Get; + } - decl_module! { - pub struct Module for enum Call where origin: T::RuntimeOrigin, system=self { + #[pallet::error] + pub enum Error { + /// Required by construct_runtime + CallFiltered, + } + + #[pallet::origin] + pub type Origin = RawOrigin<::AccountId>; + + #[pallet::call] + impl Pallet { // no arguments, fixed weight - #[weight = 1000] - fn f00(_origin) { unimplemented!(); } + #[pallet::weight(1000)] + pub fn f00(_origin: OriginFor) -> DispatchResult { + unimplemented!(); + } - #[weight = (1000, DispatchClass::Mandatory)] - fn f01(_origin) { unimplemented!(); } + #[pallet::weight((1000, DispatchClass::Mandatory))] + pub fn f01(_origin: OriginFor) -> DispatchResult { + unimplemented!(); + } - #[weight = (1000, Pays::No)] - fn f02(_origin) { unimplemented!(); } + #[pallet::weight((1000, Pays::No))] + pub fn f02(_origin: OriginFor) -> DispatchResult { + unimplemented!(); + } - #[weight = (1000, DispatchClass::Operational, Pays::No)] - fn f03(_origin) { unimplemented!(); } + #[pallet::weight((1000, DispatchClass::Operational, Pays::No))] + pub fn f03(_origin: OriginFor) -> DispatchResult { + unimplemented!(); + } // weight = a x 10 + b - #[weight = ((_a * 10 + _eb * 1) as u64, DispatchClass::Normal, Pays::Yes)] - fn f11(_origin, _a: u32, _eb: u32) { unimplemented!(); } + #[pallet::weight(((_a * 10 + _eb * 1) as u64, DispatchClass::Normal, Pays::Yes))] + pub fn f11(_origin: OriginFor, _a: u32, _eb: u32) -> DispatchResult { + unimplemented!(); + } - #[weight = (0, DispatchClass::Operational, Pays::Yes)] - fn f12(_origin, _a: u32, _eb: u32) { unimplemented!(); } + #[pallet::weight((0, DispatchClass::Operational, Pays::Yes))] + pub fn f12(_origin: OriginFor, _a: u32, _eb: u32) -> DispatchResult { + unimplemented!(); + } - #[weight = T::DbWeight::get().reads(3) + T::DbWeight::get().writes(2) + Weight::from_parts(10_000, 0)] - fn f20(_origin) { unimplemented!(); } + #[pallet::weight(T::DbWeight::get().reads(3) + T::DbWeight::get().writes(2) + Weight::from_all(10_000))] + pub fn f20(_origin: OriginFor) -> DispatchResult { + unimplemented!(); + } + + #[pallet::weight(T::DbWeight::get().reads_writes(6, 5) + Weight::from_all(40_000))] + pub fn f21(_origin: OriginFor) -> DispatchResult { + unimplemented!(); + } + } + + pub mod pallet_prelude { + pub type OriginFor = ::RuntimeOrigin; + } + } - #[weight = T::DbWeight::get().reads_writes(6, 5) + Weight::from_parts(40_000, 0)] - fn f21(_origin) { unimplemented!(); } + type BlockNumber = u32; + type AccountId = u32; + type Balance = u32; + type Header = generic::Header; + type UncheckedExtrinsic = generic::UncheckedExtrinsic; + type Block = generic::Block; + crate::construct_runtime!( + pub enum Runtime + where + Block = Block, + NodeBlock = Block, + UncheckedExtrinsic = UncheckedExtrinsic, + { + System: self::frame_system, } + ); + + parameter_types! { + pub const DbWeight: RuntimeDbWeight = RuntimeDbWeight { + read: 100, + write: 1000, + }; + } + + impl Config for Runtime { + type BlockNumber = BlockNumber; + type AccountId = AccountId; + type Balance = Balance; + type BaseCallFilter = crate::traits::Everything; + type RuntimeOrigin = RuntimeOrigin; + type RuntimeCall = RuntimeCall; + type DbWeight = DbWeight; + type PalletInfo = PalletInfo; } #[test] fn weights_are_correct() { - // #[weight = 1000] - let info = Call::::f00 {}.get_dispatch_info(); + // #[pallet::weight(1000)] + let info = Call::::f00 {}.get_dispatch_info(); assert_eq!(info.weight, Weight::from_parts(1000, 0)); assert_eq!(info.class, DispatchClass::Normal); assert_eq!(info.pays_fee, Pays::Yes); - // #[weight = (1000, DispatchClass::Mandatory)] - let info = Call::::f01 {}.get_dispatch_info(); + // #[pallet::weight((1000, DispatchClass::Mandatory))] + let info = Call::::f01 {}.get_dispatch_info(); assert_eq!(info.weight, Weight::from_parts(1000, 0)); assert_eq!(info.class, DispatchClass::Mandatory); assert_eq!(info.pays_fee, Pays::Yes); - // #[weight = (1000, Pays::No)] - let info = Call::::f02 {}.get_dispatch_info(); + // #[pallet::weight((1000, Pays::No))] + let info = Call::::f02 {}.get_dispatch_info(); assert_eq!(info.weight, Weight::from_parts(1000, 0)); assert_eq!(info.class, DispatchClass::Normal); assert_eq!(info.pays_fee, Pays::No); - // #[weight = (1000, DispatchClass::Operational, Pays::No)] - let info = Call::::f03 {}.get_dispatch_info(); + // #[pallet::weight((1000, DispatchClass::Operational, Pays::No))] + let info = Call::::f03 {}.get_dispatch_info(); assert_eq!(info.weight, Weight::from_parts(1000, 0)); assert_eq!(info.class, DispatchClass::Operational); assert_eq!(info.pays_fee, Pays::No); - // #[weight = ((_a * 10 + _eb * 1) as Weight, DispatchClass::Normal, Pays::Yes)] - let info = Call::::f11 { _a: 13, _eb: 20 }.get_dispatch_info(); + // #[pallet::weight(((_a * 10 + _eb * 1) as u64, DispatchClass::Normal, Pays::Yes))] + let info = Call::::f11 { a: 13, eb: 20 }.get_dispatch_info(); assert_eq!(info.weight, Weight::from_parts(150, 0)); // 13*10 + 20 assert_eq!(info.class, DispatchClass::Normal); assert_eq!(info.pays_fee, Pays::Yes); - // #[weight = (0, DispatchClass::Operational, Pays::Yes)] - let info = Call::::f12 { _a: 10, _eb: 20 }.get_dispatch_info(); - assert_eq!(info.weight, Weight::from_parts(0, 0)); + // #[pallet::weight((0, DispatchClass::Operational, Pays::Yes))] + let info = Call::::f12 { a: 10, eb: 20 }.get_dispatch_info(); + assert_eq!(info.weight, Weight::zero()); assert_eq!(info.class, DispatchClass::Operational); assert_eq!(info.pays_fee, Pays::Yes); - // #[weight = T::DbWeight::get().reads(3) + T::DbWeight::get().writes(2) + 10_000] - let info = Call::::f20 {}.get_dispatch_info(); - assert_eq!(info.weight, Weight::from_parts(12300, 0)); // 100*3 + 1000*2 + 10_1000 + // #[pallet::weight(T::DbWeight::get().reads(3) + T::DbWeight::get().writes(2) + + // Weight::from_all(10_000))] + let info = Call::::f20 {}.get_dispatch_info(); + assert_eq!(info.weight, Weight::from_parts(12300, 10000)); // 100*3 + 1000*2 + 10_1000 assert_eq!(info.class, DispatchClass::Normal); assert_eq!(info.pays_fee, Pays::Yes); - // #[weight = T::DbWeight::get().reads_writes(6, 5) + 40_000] - let info = Call::::f21 {}.get_dispatch_info(); - assert_eq!(info.weight, Weight::from_parts(45600, 0)); // 100*6 + 1000*5 + 40_1000 + // #[pallet::weight(T::DbWeight::get().reads_writes(6, 5) + Weight::from_all(40_000))] + let info = Call::::f21 {}.get_dispatch_info(); + assert_eq!(info.weight, Weight::from_parts(45600, 40000)); // 100*6 + 1000*5 + 40_1000 assert_eq!(info.class, DispatchClass::Normal); assert_eq!(info.pays_fee, Pays::Yes); } diff --git a/frame/support/src/lib.rs b/frame/support/src/lib.rs index 036bd9346..0ee82b9f1 100644 --- a/frame/support/src/lib.rs +++ b/frame/support/src/lib.rs @@ -829,77 +829,153 @@ pub mod tests { PalletStorageMetadataIR, StorageEntryMetadataIR, StorageEntryModifierIR, StorageEntryTypeIR, StorageHasherIR, }; - use codec::{Codec, EncodeLike}; - use frame_support::traits::CrateVersion; use sp_io::{MultiRemovalResults, TestExternalities}; + use sp_runtime::{generic, traits::BlakeTwo256, BuildStorage}; use sp_std::result; - /// A PalletInfo implementation which just panics. - pub struct PanicPalletInfo; + pub use self::frame_system::{Config, Pallet}; - impl crate::traits::PalletInfo for PanicPalletInfo { - fn index() -> Option { - unimplemented!("PanicPalletInfo mustn't be triggered by tests"); - } - fn name() -> Option<&'static str> { - unimplemented!("PanicPalletInfo mustn't be triggered by tests"); - } - fn module_name() -> Option<&'static str> { - unimplemented!("PanicPalletInfo mustn't be triggered by tests"); - } - fn crate_version() -> Option { - unimplemented!("PanicPalletInfo mustn't be triggered by tests"); + #[pallet] + pub mod frame_system { + #[allow(unused)] + use super::{frame_system, frame_system::pallet_prelude::*}; + pub use crate::dispatch::RawOrigin; + use crate::pallet_prelude::*; + + #[pallet::pallet] + pub struct Pallet(PhantomData); + + #[pallet::config] + #[pallet::disable_frame_system_supertrait_check] + pub trait Config: 'static { + type BlockNumber: Parameter + Default + MaxEncodedLen; + type AccountId; + type BaseCallFilter: crate::traits::Contains; + type RuntimeOrigin; + type RuntimeCall; + type PalletInfo: crate::traits::PalletInfo; + type DbWeight: Get; } - } - pub trait Config: 'static { - type BlockNumber: Codec + EncodeLike + Default + TypeInfo; - type RuntimeOrigin; - type PalletInfo: crate::traits::PalletInfo; - type DbWeight: crate::traits::Get; - } + #[pallet::error] + pub enum Error { + /// Required by construct_runtime + CallFiltered, + } - mod module { - #![allow(dead_code)] + #[pallet::origin] + pub type Origin = RawOrigin<::AccountId>; + + #[pallet::call] + impl Pallet {} + + #[pallet::storage] + pub type Data = StorageMap<_, Twox64Concat, u32, u64, ValueQuery>; + + #[pallet::storage] + pub type OptionLinkedMap = StorageMap<_, Blake2_128Concat, u32, u32, OptionQuery>; + + #[pallet::storage] + #[pallet::getter(fn generic_data)] + pub type GenericData = + StorageMap<_, Identity, T::BlockNumber, T::BlockNumber, ValueQuery>; + + #[pallet::storage] + #[pallet::getter(fn generic_data2)] + pub type GenericData2 = + StorageMap<_, Blake2_128Concat, T::BlockNumber, T::BlockNumber, OptionQuery>; + + #[pallet::storage] + pub type DataDM = + StorageDoubleMap<_, Twox64Concat, u32, Blake2_128Concat, u32, u64, ValueQuery>; + + #[pallet::storage] + pub type GenericDataDM = StorageDoubleMap< + _, + Blake2_128Concat, + T::BlockNumber, + Identity, + T::BlockNumber, + T::BlockNumber, + ValueQuery, + >; + + #[pallet::storage] + pub type GenericData2DM = StorageDoubleMap< + _, + Blake2_128Concat, + T::BlockNumber, + Twox64Concat, + T::BlockNumber, + T::BlockNumber, + OptionQuery, + >; + + #[pallet::storage] + #[pallet::unbounded] + pub type AppendableDM = StorageDoubleMap< + _, + Blake2_128Concat, + u32, + Blake2_128Concat, + T::BlockNumber, + Vec, + ValueQuery, + >; + + #[pallet::genesis_config] + pub struct GenesisConfig { + pub data: Vec<(u32, u64)>, + pub test_config: Vec<(u32, u32, u64)>, + } - use super::Config; + impl Default for GenesisConfig { + fn default() -> Self { + Self { data: vec![(15u32, 42u64)], test_config: vec![(15u32, 16u32, 42u64)] } + } + } - decl_module! { - pub struct Module for enum Call where origin: T::RuntimeOrigin, system=self {} + #[pallet::genesis_build] + impl GenesisBuild for GenesisConfig { + fn build(&self) { + for (k, v) in &self.data { + >::insert(k, v); + } + for (k1, k2, v) in &self.test_config { + >::insert(k1, k2, v); + } + } } - } - use self::module::Module; - - decl_storage! { - trait Store for Module as Test { - pub Value get(fn value): u64; - pub Data get(fn data) build(|_| vec![(15u32, 42u64)]): - map hasher(twox_64_concat) u32 => u64; - pub OptionLinkedMap: map hasher(blake2_128_concat) u32 => Option; - pub GenericData get(fn generic_data): - map hasher(identity) T::BlockNumber => T::BlockNumber; - pub GenericData2 get(fn generic_data2): - map hasher(blake2_128_concat) T::BlockNumber => Option; - pub DataDM config(test_config) build(|_| vec![(15u32, 16u32, 42u64)]): - double_map hasher(twox_64_concat) u32, hasher(blake2_128_concat) u32 => u64; - pub GenericDataDM: - double_map hasher(blake2_128_concat) T::BlockNumber, hasher(identity) T::BlockNumber - => T::BlockNumber; - pub GenericData2DM: - double_map hasher(blake2_128_concat) T::BlockNumber, hasher(twox_64_concat) T::BlockNumber - => Option; - pub AppendableDM: - double_map hasher(blake2_128_concat) u32, hasher(blake2_128_concat) T::BlockNumber => Vec; + pub mod pallet_prelude { + pub type OriginFor = ::RuntimeOrigin; } } - struct Test; + type BlockNumber = u32; + type AccountId = u32; + type Header = generic::Header; + type UncheckedExtrinsic = generic::UncheckedExtrinsic; + type Block = generic::Block; + + crate::construct_runtime!( + pub enum Runtime + where + Block = Block, + NodeBlock = Block, + UncheckedExtrinsic = UncheckedExtrinsic, + { + System: self::frame_system, + } + ); - impl Config for Test { - type BlockNumber = u32; - type RuntimeOrigin = u32; - type PalletInfo = PanicPalletInfo; + impl Config for Runtime { + type BlockNumber = BlockNumber; + type AccountId = AccountId; + type BaseCallFilter = crate::traits::Everything; + type RuntimeOrigin = RuntimeOrigin; + type RuntimeCall = RuntimeCall; + type PalletInfo = PalletInfo; type DbWeight = (); } @@ -907,8 +983,6 @@ pub mod tests { GenesisConfig::default().build_storage().unwrap().into() } - type Map = Data; - trait Sorted { fn sorted(self) -> Self; } @@ -925,15 +999,15 @@ pub mod tests { new_test_ext().execute_with(|| { #[crate::storage_alias] type GenericData2 = StorageMap< - Test, + System, Blake2_128Concat, ::BlockNumber, ::BlockNumber, >; - assert_eq!(Module::::generic_data2(5), None); - GenericData2::::insert(5, 5); - assert_eq!(Module::::generic_data2(5), Some(5)); + assert_eq!(Pallet::::generic_data2(5), None); + GenericData2::::insert(5, 5); + assert_eq!(Pallet::::generic_data2(5), Some(5)); /// Some random docs that ensure that docs are accepted #[crate::storage_alias] @@ -1004,6 +1078,8 @@ pub mod tests { #[test] fn map_issue_3318() { new_test_ext().execute_with(|| { + type OptionLinkedMap = self::frame_system::OptionLinkedMap; + OptionLinkedMap::insert(1, 1); assert_eq!(OptionLinkedMap::get(1), Some(1)); OptionLinkedMap::insert(1, 2); @@ -1014,6 +1090,8 @@ pub mod tests { #[test] fn map_swap_works() { new_test_ext().execute_with(|| { + type OptionLinkedMap = self::frame_system::OptionLinkedMap; + OptionLinkedMap::insert(0, 0); OptionLinkedMap::insert(1, 1); OptionLinkedMap::insert(2, 2); @@ -1043,6 +1121,8 @@ pub mod tests { #[test] fn double_map_swap_works() { new_test_ext().execute_with(|| { + type DataDM = self::frame_system::DataDM; + DataDM::insert(0, 1, 1); DataDM::insert(1, 0, 2); DataDM::insert(1, 1, 3); @@ -1075,6 +1155,8 @@ pub mod tests { #[test] fn map_basic_insert_remove_should_work() { new_test_ext().execute_with(|| { + type Map = self::frame_system::Data; + // initialized during genesis assert_eq!(Map::get(&15u32), 42u64); @@ -1101,6 +1183,8 @@ pub mod tests { #[test] fn map_iteration_should_work() { new_test_ext().execute_with(|| { + type Map = self::frame_system::Data; + assert_eq!(Map::iter().collect::>().sorted(), vec![(15, 42)]); // insert / remove let key = 17u32; @@ -1154,7 +1238,7 @@ pub mod tests { fn double_map_basic_insert_remove_remove_prefix_with_commit_should_work() { let key1 = 17u32; let key2 = 18u32; - type DoubleMap = DataDM; + type DoubleMap = self::frame_system::DataDM; let mut e = new_test_ext(); e.execute_with(|| { // initialized during genesis @@ -1199,7 +1283,7 @@ pub mod tests { new_test_ext().execute_with(|| { let key1 = 17u32; let key2 = 18u32; - type DoubleMap = DataDM; + type DoubleMap = self::frame_system::DataDM; // initialized during genesis assert_eq!(DoubleMap::get(&15u32, &16u32), 42u64); @@ -1247,7 +1331,7 @@ pub mod tests { #[test] fn double_map_append_should_work() { new_test_ext().execute_with(|| { - type DoubleMap = AppendableDM; + type DoubleMap = self::frame_system::AppendableDM; let key1 = 17u32; let key2 = 18u32; @@ -1261,7 +1345,7 @@ pub mod tests { #[test] fn double_map_mutate_exists_should_work() { new_test_ext().execute_with(|| { - type DoubleMap = DataDM; + type DoubleMap = self::frame_system::DataDM; let (key1, key2) = (11, 13); @@ -1278,8 +1362,8 @@ pub mod tests { #[test] fn double_map_try_mutate_exists_should_work() { new_test_ext().execute_with(|| { - type DoubleMap = DataDM; - type TestResult = result::Result<(), &'static str>; + type DoubleMap = self::frame_system::DataDM; + type TestResult = Result<(), &'static str>; let (key1, key2) = (11, 13); @@ -1310,15 +1394,8 @@ pub mod tests { fn expected_metadata() -> PalletStorageMetadataIR { PalletStorageMetadataIR { - prefix: "Test", + prefix: "System", entries: vec![ - StorageEntryMetadataIR { - name: "Value", - modifier: StorageEntryModifierIR::Default, - ty: StorageEntryTypeIR::Plain(scale_info::meta_type::()), - default: vec![0, 0, 0, 0, 0, 0, 0, 0], - docs: vec![], - }, StorageEntryMetadataIR { name: "Data", modifier: StorageEntryModifierIR::Default, @@ -1422,7 +1499,7 @@ pub mod tests { #[test] fn store_metadata() { - let metadata = Module::::storage_metadata(); + let metadata = Pallet::::storage_metadata(); pretty_assertions::assert_eq!(expected_metadata(), metadata); } @@ -1441,12 +1518,6 @@ pub mod tests { assert_eq!(300, StorageParameter::get()); }) } - - parameter_types! { - pub const BlockHashCount: u64 = 250; - pub static Members: Vec = vec![]; - pub const Foo: Option = None; - } } /// Prelude to be used alongside pallet macro, for ease of use. diff --git a/frame/support/src/storage/generator/double_map.rs b/frame/support/src/storage/generator/double_map.rs index 5763a472c..5da68873b 100644 --- a/frame/support/src/storage/generator/double_map.rs +++ b/frame/support/src/storage/generator/double_map.rs @@ -514,43 +514,12 @@ where mod test_iterators { use crate::{ hash::StorageHasher, - storage::{generator::StorageDoubleMap, unhashed, IterableStorageDoubleMap}, + storage::{ + generator::{tests::*, StorageDoubleMap}, + unhashed, + }, }; - use codec::{Decode, Encode}; - - pub trait Config: 'static { - type RuntimeOrigin; - type BlockNumber; - type PalletInfo: crate::traits::PalletInfo; - type DbWeight: crate::traits::Get; - } - - crate::decl_module! { - pub struct Module for enum Call where origin: T::RuntimeOrigin, system=self {} - } - - #[derive(PartialEq, Eq, Clone, Encode, Decode)] - struct NoDef(u32); - - crate::decl_storage! { - trait Store for Module as Test { - DoubleMap: double_map hasher(blake2_128_concat) u16, hasher(twox_64_concat) u32 => u64; - } - } - - fn key_before_prefix(mut prefix: Vec) -> Vec { - let last = prefix.iter_mut().last().unwrap(); - assert!(*last != 0, "mock function not implemented for this prefix"); - *last -= 1; - prefix - } - - fn key_after_prefix(mut prefix: Vec) -> Vec { - let last = prefix.iter_mut().last().unwrap(); - assert!(*last != 255, "mock function not implemented for this prefix"); - *last += 1; - prefix - } + use codec::Encode; #[test] fn double_map_iter_from() { @@ -589,6 +558,8 @@ mod test_iterators { #[test] fn double_map_reversible_reversible_iteration() { sp_io::TestExternalities::default().execute_with(|| { + type DoubleMap = self::frame_system::DoubleMap; + // All map iterator let prefix = DoubleMap::prefix_hash(); diff --git a/frame/support/src/storage/generator/map.rs b/frame/support/src/storage/generator/map.rs index fb499384f..3b36b9bdd 100644 --- a/frame/support/src/storage/generator/map.rs +++ b/frame/support/src/storage/generator/map.rs @@ -349,43 +349,12 @@ impl> storage::StorageMap mod test_iterators { use crate::{ hash::StorageHasher, - storage::{generator::StorageMap, unhashed, IterableStorageMap}, + storage::{ + generator::{tests::*, StorageMap}, + unhashed, + }, }; - use codec::{Decode, Encode}; - - pub trait Config: 'static { - type RuntimeOrigin; - type BlockNumber; - type PalletInfo: crate::traits::PalletInfo; - type DbWeight: crate::traits::Get; - } - - crate::decl_module! { - pub struct Module for enum Call where origin: T::RuntimeOrigin, system=self {} - } - - #[derive(PartialEq, Eq, Clone, Encode, Decode)] - struct NoDef(u32); - - crate::decl_storage! { - trait Store for Module as Test { - Map: map hasher(blake2_128_concat) u16 => u64; - } - } - - fn key_before_prefix(mut prefix: Vec) -> Vec { - let last = prefix.iter_mut().last().unwrap(); - assert!(*last != 0, "mock function not implemented for this prefix"); - *last -= 1; - prefix - } - - fn key_after_prefix(mut prefix: Vec) -> Vec { - let last = prefix.iter_mut().last().unwrap(); - assert!(*last != 255, "mock function not implemented for this prefix"); - *last += 1; - prefix - } + use codec::Encode; #[test] fn map_iter_from() { @@ -413,6 +382,8 @@ mod test_iterators { #[test] fn map_reversible_reversible_iteration() { sp_io::TestExternalities::default().execute_with(|| { + type Map = self::frame_system::Map; + // All map iterator let prefix = Map::prefix_hash(); diff --git a/frame/support/src/storage/generator/mod.rs b/frame/support/src/storage/generator/mod.rs index 2da612b24..568d40012 100644 --- a/frame/support/src/storage/generator/mod.rs +++ b/frame/support/src/storage/generator/mod.rs @@ -35,47 +35,123 @@ pub use nmap::StorageNMap; pub use value::StorageValue; #[cfg(test)] -#[allow(dead_code)] mod tests { + use codec::Encode; + use sp_io::TestExternalities; + use sp_runtime::{generic, traits::BlakeTwo256, BuildStorage}; + use crate::{ assert_noop, assert_ok, - storage::{generator::StorageValue, unhashed, IterableStorageMap}, + storage::{generator::StorageValue, unhashed}, }; - use codec::Encode; - use sp_io::TestExternalities; - struct Runtime; + #[crate::pallet] + pub mod frame_system { + #[allow(unused)] + use super::{frame_system, frame_system::pallet_prelude::*}; + pub use crate::dispatch::RawOrigin; + use crate::pallet_prelude::*; + + #[pallet::pallet] + pub struct Pallet(PhantomData); + + #[pallet::config] + #[pallet::disable_frame_system_supertrait_check] + pub trait Config: 'static { + type BlockNumber; + type AccountId; + type BaseCallFilter: crate::traits::Contains; + type RuntimeOrigin; + type RuntimeCall; + type PalletInfo: crate::traits::PalletInfo; + type DbWeight: Get; + } + + #[pallet::origin] + pub type Origin = RawOrigin<::AccountId>; + + #[pallet::error] + pub enum Error { + /// Required by construct_runtime + CallFiltered, + } + + #[pallet::call] + impl Pallet {} + + #[pallet::storage] + pub type Value = StorageValue<_, (u64, u64), ValueQuery>; - pub trait Config: 'static { - type RuntimeOrigin; - type BlockNumber; - type PalletInfo: crate::traits::PalletInfo; - type DbWeight: crate::traits::Get; + #[pallet::storage] + pub type Map = StorageMap<_, Blake2_128Concat, u16, u64, ValueQuery>; + + #[pallet::storage] + pub type NumberMap = StorageMap<_, Identity, u32, u64, ValueQuery>; + + #[pallet::storage] + pub type DoubleMap = + StorageDoubleMap<_, Blake2_128Concat, u16, Twox64Concat, u32, u64, ValueQuery>; + + #[pallet::storage] + pub type NMap = StorageNMap< + _, + (storage::Key, storage::Key), + u64, + ValueQuery, + >; + + pub mod pallet_prelude { + pub type OriginFor = ::RuntimeOrigin; + } } - impl Config for Runtime { - type RuntimeOrigin = u32; - type BlockNumber = u32; - type PalletInfo = crate::tests::PanicPalletInfo; + type BlockNumber = u32; + type AccountId = u32; + type Header = generic::Header; + type UncheckedExtrinsic = generic::UncheckedExtrinsic; + type Block = generic::Block; + + crate::construct_runtime!( + pub enum Runtime + where + Block = Block, + NodeBlock = Block, + UncheckedExtrinsic = UncheckedExtrinsic, + { + System: self::frame_system, + } + ); + + impl self::frame_system::Config for Runtime { + type BlockNumber = BlockNumber; + type AccountId = AccountId; + type BaseCallFilter = crate::traits::Everything; + type RuntimeOrigin = RuntimeOrigin; + type RuntimeCall = RuntimeCall; + type PalletInfo = PalletInfo; type DbWeight = (); } - decl_module! { - pub struct Module for enum Call where origin: T::RuntimeOrigin, system=self {} + pub fn key_before_prefix(mut prefix: Vec) -> Vec { + let last = prefix.iter_mut().last().unwrap(); + assert_ne!(*last, 0, "mock function not implemented for this prefix"); + *last -= 1; + prefix } - crate::decl_storage! { - trait Store for Module as Runtime { - Value get(fn value) config(): (u64, u64); - NumberMap: map hasher(identity) u32 => u64; - DoubleMap: double_map hasher(identity) u32, hasher(identity) u32 => u64; - } + pub fn key_after_prefix(mut prefix: Vec) -> Vec { + let last = prefix.iter_mut().last().unwrap(); + assert_ne!(*last, 255, "mock function not implemented for this prefix"); + *last += 1; + prefix } #[test] fn value_translate_works() { let t = GenesisConfig::default().build_storage().unwrap(); TestExternalities::new(t).execute_with(|| { + type Value = self::frame_system::Value; + // put the old value `1111u32` in the storage. let key = Value::storage_value_final_key(); unhashed::put_raw(&key, &1111u32.encode()); @@ -96,7 +172,9 @@ mod tests { fn map_translate_works() { let t = GenesisConfig::default().build_storage().unwrap(); TestExternalities::new(t).execute_with(|| { - // start with a map of u32 -> u32. + type NumberMap = self::frame_system::NumberMap; + + // start with a map of u32 -> u64. for i in 0u32..100u32 { unhashed::put(&NumberMap::hashed_key_for(&i), &(i as u64)); } @@ -125,6 +203,10 @@ mod tests { fn try_mutate_works() { let t = GenesisConfig::default().build_storage().unwrap(); TestExternalities::new(t).execute_with(|| { + type Value = self::frame_system::Value; + type NumberMap = self::frame_system::NumberMap; + type DoubleMap = self::frame_system::DoubleMap; + assert_eq!(Value::get(), (0, 0)); assert_eq!(NumberMap::get(0), 0); assert_eq!(DoubleMap::get(0, 0), 0); diff --git a/frame/support/src/storage/generator/nmap.rs b/frame/support/src/storage/generator/nmap.rs index 21fe7b41e..5d3d689aa 100755 --- a/frame/support/src/storage/generator/nmap.rs +++ b/frame/support/src/storage/generator/nmap.rs @@ -462,43 +462,12 @@ impl> mod test_iterators { use crate::{ hash::StorageHasher, - storage::{generator::StorageNMap, unhashed, IterableStorageNMap}, + storage::{ + generator::{tests::*, StorageNMap}, + unhashed, + }, }; - use codec::{Decode, Encode}; - - pub trait Config: 'static { - type RuntimeOrigin; - type BlockNumber; - type PalletInfo: crate::traits::PalletInfo; - type DbWeight: crate::traits::Get; - } - - crate::decl_module! { - pub struct Module for enum Call where origin: T::RuntimeOrigin, system=self {} - } - - #[derive(PartialEq, Eq, Clone, Encode, Decode)] - struct NoDef(u32); - - crate::decl_storage! { - trait Store for Module as Test { - NMap: nmap hasher(blake2_128_concat) u16, hasher(twox_64_concat) u32 => u64; - } - } - - fn key_before_prefix(mut prefix: Vec) -> Vec { - let last = prefix.iter_mut().last().unwrap(); - assert!(*last != 0, "mock function not implemented for this prefix"); - *last -= 1; - prefix - } - - fn key_after_prefix(mut prefix: Vec) -> Vec { - let last = prefix.iter_mut().last().unwrap(); - assert!(*last != 255, "mock function not implemented for this prefix"); - *last += 1; - prefix - } + use codec::Encode; #[test] fn n_map_iter_from() { @@ -545,22 +514,18 @@ mod test_iterators { #[test] fn n_map_double_map_identical_key() { sp_io::TestExternalities::default().execute_with(|| { + use crate::hash::{Blake2_128Concat, Twox64Concat}; + + type NMap = self::frame_system::NMap; + NMap::insert((1, 2), 50); let key_hash = NMap::hashed_key_for((1, 2)); { #[crate::storage_alias] - type NMap = StorageDoubleMap< - Test, - crate::Blake2_128Concat, - u16, - crate::Twox64Concat, - u32, - u64, - >; - - let value = NMap::get(1, 2).unwrap(); - assert_eq!(value, 50); + type NMap = StorageDoubleMap; + + assert_eq!(NMap::get(1, 2), Some(50)); assert_eq!(NMap::hashed_key_for(1, 2), key_hash); } }); @@ -569,6 +534,8 @@ mod test_iterators { #[test] fn n_map_reversible_reversible_iteration() { sp_io::TestExternalities::default().execute_with(|| { + type NMap = self::frame_system::NMap; + // All map iterator let prefix = NMap::prefix_hash(); diff --git a/primitives/core/Cargo.toml b/primitives/core/Cargo.toml index 302d52f2f..8eac09967 100644 --- a/primitives/core/Cargo.toml +++ b/primitives/core/Cargo.toml @@ -39,6 +39,7 @@ futures = { version = "0.3.21", optional = true } dyn-clonable = { version = "0.9.0", optional = true } thiserror = { version = "1.0.30", optional = true } bitflags = "1.3" +paste = "1.0.7" # full crypto array-bytes = { version = "4.1", optional = true } diff --git a/primitives/core/src/lib.rs b/primitives/core/src/lib.rs index efccd0378..04f44631c 100644 --- a/primitives/core/src/lib.rs +++ b/primitives/core/src/lib.rs @@ -53,6 +53,7 @@ pub mod hashing; pub use hashing::{blake2_128, blake2_256, keccak_256, twox_128, twox_256, twox_64}; pub mod crypto; pub mod hexdisplay; +pub use paste; pub mod defer; pub mod ecdsa; @@ -403,38 +404,62 @@ pub const MAX_POSSIBLE_ALLOCATION: u32 = 33554432; // 2^25 bytes, 32 MiB #[rustfmt::skip] macro_rules! generate_feature_enabled_macro { ( $macro_name:ident, $feature_name:meta, $d:tt ) => { - /// Enable/disable the given code depending on - #[doc = concat!("`", stringify!($feature_name), "`")] - /// being enabled for the crate or not. - /// - /// # Example - /// - /// ```nocompile - /// // Will add the code depending on the feature being enabled or not. - #[doc = concat!(stringify!($macro_name), "!( println!(\"Hello\") )")] - /// ``` - #[cfg($feature_name)] - #[macro_export] - macro_rules! $macro_name { - ( $d ( $d input:tt )* ) => { - $d ( $d input )* + $crate::paste::paste!{ + /// Enable/disable the given code depending on + #[doc = concat!("`", stringify!($feature_name), "`")] + /// being enabled for the crate or not. + /// + /// # Example + /// + /// ```nocompile + /// // Will add the code depending on the feature being enabled or not. + #[doc = concat!(stringify!($macro_name), "!( println!(\"Hello\") )")] + /// ``` + #[cfg($feature_name)] + #[macro_export] + macro_rules! [<_ $macro_name>] { + ( $d ( $d input:tt )* ) => { + $d ( $d input )* + } + } + + /// Enable/disable the given code depending on + #[doc = concat!("`", stringify!($feature_name), "`")] + /// being enabled for the crate or not. + /// + /// # Example + /// + /// ```nocompile + /// // Will add the code depending on the feature being enabled or not. + #[doc = concat!(stringify!($macro_name), "!( println!(\"Hello\") )")] + /// ``` + #[cfg(not($feature_name))] + #[macro_export] + macro_rules! [<_ $macro_name>] { + ( $d ( $d input:tt )* ) => {}; } - } - /// Enable/disable the given code depending on - #[doc = concat!("`", stringify!($feature_name), "`")] - /// being enabled for the crate or not. - /// - /// # Example - /// - /// ```nocompile - /// // Will add the code depending on the feature being enabled or not. - #[doc = concat!(stringify!($macro_name), "!( println!(\"Hello\") )")] - /// ``` - #[cfg(not($feature_name))] - #[macro_export] - macro_rules! $macro_name { - ( $d ( $d input:tt )* ) => {}; + // Work around for: + #[doc(hidden)] + pub use [<_ $macro_name>] as $macro_name; } }; } + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + #[should_panic] + fn generate_feature_enabled_macro_panics() { + generate_feature_enabled_macro!(if_test, test, $); + if_test!(panic!("This should panic")); + } + + #[test] + fn generate_feature_enabled_macro_works() { + generate_feature_enabled_macro!(if_not_test, not(test), $); + if_not_test!(panic!("This should not panic")); + } +} diff --git a/primitives/runtime/src/lib.rs b/primitives/runtime/src/lib.rs index 2d959425e..0994dc21b 100644 --- a/primitives/runtime/src/lib.rs +++ b/primitives/runtime/src/lib.rs @@ -1054,3 +1054,23 @@ mod tests { }); } } + +// NOTE: we have to test the sp_core stuff also from a different crate to check that the macro +// can access the sp_core crate. +#[cfg(test)] +mod sp_core_tests { + use super::*; + + #[test] + #[should_panic] + fn generate_feature_enabled_macro_panics() { + sp_core::generate_feature_enabled_macro!(if_test, test, $); + if_test!(panic!("This should panic")); + } + + #[test] + fn generate_feature_enabled_macro_works() { + sp_core::generate_feature_enabled_macro!(if_not_test, not(test), $); + if_not_test!(panic!("This should not panic")); + } +} From db6ebf564bcdfdfaf5fd026bb321de6f7d7e6fc0 Mon Sep 17 00:00:00 2001 From: gupnik <17176722+gupnik@users.noreply.github.com> Date: Tue, 25 Apr 2023 20:50:58 +0530 Subject: [PATCH 420/558] Removes ReportsByKindIndex (#13936) * Removes ReportsByKind * Minor build fixes * adds migration * Addresses review comment * Uses clear but weight check fails * Uses unique * Updates test to commit the change before migration * Uses reads_writes * ".git/.scripts/commands/fmt/fmt.sh" * Fixes build * Addresses review comments * ".git/.scripts/commands/fmt/fmt.sh" * fixes typo --------- Co-authored-by: command-bot <> --- frame/offences/src/lib.rs | 43 ++++--------- frame/offences/src/migration.rs | 103 ++++++++++++++++++++++++++++++-- frame/offences/src/mock.rs | 5 -- frame/offences/src/tests.rs | 49 +-------------- 4 files changed, 113 insertions(+), 87 deletions(-) diff --git a/frame/offences/src/lib.rs b/frame/offences/src/lib.rs index 96c7c119c..1c7ffeca7 100644 --- a/frame/offences/src/lib.rs +++ b/frame/offences/src/lib.rs @@ -26,7 +26,9 @@ pub mod migration; mod mock; mod tests; -use codec::{Decode, Encode}; +use core::marker::PhantomData; + +use codec::Encode; use frame_support::weights::Weight; use sp_runtime::{traits::Hash, Perbill}; use sp_staking::{ @@ -43,12 +45,17 @@ type OpaqueTimeSlot = Vec; /// A type alias for a report identifier. type ReportIdOf = ::Hash; +const LOG_TARGET: &str = "runtime::offences"; + #[frame_support::pallet] pub mod pallet { use super::*; use frame_support::pallet_prelude::*; + const STORAGE_VERSION: StorageVersion = StorageVersion::new(1); + #[pallet::pallet] + #[pallet::storage_version(STORAGE_VERSION)] #[pallet::without_storage_info] pub struct Pallet(_); @@ -85,21 +92,6 @@ pub mod pallet { ValueQuery, >; - /// Enumerates all reports of a kind along with the time they happened. - /// - /// All reports are sorted by the time of offence. - /// - /// Note that the actual type of this mapping is `Vec`, this is because values of - /// different types are not supported at the moment so we are doing the manual serialization. - #[pallet::storage] - pub type ReportsByKindIndex = StorageMap< - _, - Twox64Concat, - Kind, - Vec, // (O::TimeSlot, ReportIdOf) - ValueQuery, - >; - /// Events type. #[pallet::event] #[pallet::generate_deposit(pub(super) fn deposit_event)] @@ -190,7 +182,7 @@ impl Pallet { OffenceDetails { offender, reporters: reporters.clone() }, ); - storage.insert(time_slot, report_id); + storage.insert(report_id); } } @@ -225,7 +217,7 @@ struct TriageOutcome { struct ReportIndexStorage> { opaque_time_slot: OpaqueTimeSlot, concurrent_reports: Vec>, - same_kind_reports: Vec<(O::TimeSlot, ReportIdOf)>, + _phantom: PhantomData, } impl> ReportIndexStorage { @@ -233,30 +225,19 @@ impl> ReportIndexStorage { fn load(time_slot: &O::TimeSlot) -> Self { let opaque_time_slot = time_slot.encode(); - let same_kind_reports = ReportsByKindIndex::::get(&O::ID); - let same_kind_reports = - Vec::<(O::TimeSlot, ReportIdOf)>::decode(&mut &same_kind_reports[..]) - .unwrap_or_default(); - let concurrent_reports = >::get(&O::ID, &opaque_time_slot); - Self { opaque_time_slot, concurrent_reports, same_kind_reports } + Self { opaque_time_slot, concurrent_reports, _phantom: Default::default() } } /// Insert a new report to the index. - fn insert(&mut self, time_slot: &O::TimeSlot, report_id: ReportIdOf) { - // Insert the report id into the list while maintaining the ordering by the time - // slot. - let pos = self.same_kind_reports.partition_point(|(when, _)| when <= time_slot); - self.same_kind_reports.insert(pos, (time_slot.clone(), report_id)); - + fn insert(&mut self, report_id: ReportIdOf) { // Update the list of concurrent reports. self.concurrent_reports.push(report_id); } /// Dump the indexes to the storage. fn save(self) { - ReportsByKindIndex::::insert(&O::ID, self.same_kind_reports.encode()); >::insert( &O::ID, &self.opaque_time_slot, diff --git a/frame/offences/src/migration.rs b/frame/offences/src/migration.rs index 95b94ba87..07bd68407 100644 --- a/frame/offences/src/migration.rs +++ b/frame/offences/src/migration.rs @@ -15,11 +15,84 @@ // See the License for the specific language governing permissions and // limitations under the License. -use super::{Config, OffenceDetails, Perbill, SessionIndex}; -use frame_support::{pallet_prelude::ValueQuery, storage_alias, traits::Get, weights::Weight}; +use super::{Config, Kind, OffenceDetails, Pallet, Perbill, SessionIndex, LOG_TARGET}; +use frame_support::{ + dispatch::GetStorageVersion, + pallet_prelude::ValueQuery, + storage_alias, + traits::{Get, OnRuntimeUpgrade}, + weights::Weight, + Twox64Concat, +}; use sp_staking::offence::{DisableStrategy, OnOffenceHandler}; use sp_std::vec::Vec; +#[cfg(feature = "try-runtime")] +use frame_support::ensure; + +mod v0 { + use super::*; + + #[storage_alias] + pub type ReportsByKindIndex = StorageMap< + Pallet, + Twox64Concat, + Kind, + Vec, // (O::TimeSlot, ReportIdOf) + ValueQuery, + >; +} + +pub mod v1 { + use frame_support::traits::StorageVersion; + + use super::*; + + pub struct MigrateToV1(sp_std::marker::PhantomData); + impl OnRuntimeUpgrade for MigrateToV1 { + #[cfg(feature = "try-runtime")] + fn pre_upgrade() -> Result, &'static str> { + let onchain = Pallet::::on_chain_storage_version(); + ensure!(onchain < 1, "pallet_offences::MigrateToV1 migration can be deleted"); + + log::info!( + target: LOG_TARGET, + "Number of reports to refund and delete: {}", + v0::ReportsByKindIndex::::iter_keys().count() + ); + + Ok(Vec::new()) + } + + fn on_runtime_upgrade() -> Weight { + let onchain = Pallet::::on_chain_storage_version(); + + if onchain > 0 { + log::info!(target: LOG_TARGET, "pallet_offences::MigrateToV1 should be removed"); + return T::DbWeight::get().reads(1) + } + + let keys_removed = v0::ReportsByKindIndex::::clear(u32::MAX, None).unique as u64; + let weight = T::DbWeight::get().reads_writes(keys_removed, keys_removed); + + StorageVersion::new(1).put::>(); + + weight + } + + #[cfg(feature = "try-runtime")] + fn post_upgrade(_state: Vec) -> Result<(), &'static str> { + let onchain = Pallet::::on_chain_storage_version(); + ensure!(onchain == 1, "pallet_offences::MigrateToV1 needs to be run"); + ensure!( + v0::ReportsByKindIndex::::iter_keys().count() == 0, + "there are some dangling reports that need to be destroyed and refunded" + ); + Ok(()) + } + } +} + /// Type of data stored as a deferred offence type DeferredOffenceOf = ( Vec::AccountId, ::IdentificationTuple>>, @@ -36,7 +109,7 @@ type DeferredOffences = pub fn remove_deferred_storage() -> Weight { let mut weight = T::DbWeight::get().reads_writes(1, 1); let deferred = >::take(); - log::info!(target: "runtime::offences", "have {} deferred offences, applying.", deferred.len()); + log::info!(target: LOG_TARGET, "have {} deferred offences, applying.", deferred.len()); for (offences, perbill, session) in deferred.iter() { let consumed = T::OnOffenceHandler::on_offence( offences, @@ -53,10 +126,32 @@ pub fn remove_deferred_storage() -> Weight { #[cfg(test)] mod test { use super::*; - use crate::mock::{new_test_ext, with_on_offence_fractions, Runtime as T}; + use crate::mock::{new_test_ext, with_on_offence_fractions, Runtime as T, KIND}; + use codec::Encode; use sp_runtime::Perbill; use sp_staking::offence::OffenceDetails; + #[test] + fn migration_to_v1_works() { + let mut ext = new_test_ext(); + + ext.execute_with(|| { + >::insert(KIND, 2u32.encode()); + assert!(>::iter_values().count() > 0); + }); + + ext.commit_all().unwrap(); + + ext.execute_with(|| { + assert_eq!( + v1::MigrateToV1::::on_runtime_upgrade(), + ::DbWeight::get().reads_writes(1, 1), + ); + + assert!(>::iter_values().count() == 0); + }) + } + #[test] fn should_resubmit_deferred_offences() { new_test_ext().execute_with(|| { diff --git a/frame/offences/src/mock.rs b/frame/offences/src/mock.rs index 27e1da8c4..17480be76 100644 --- a/frame/offences/src/mock.rs +++ b/frame/offences/src/mock.rs @@ -164,8 +164,3 @@ impl offence::Offence for Offence { Perbill::from_percent(5 + offenders_count * 100 / self.validator_set_count) } } - -/// Create the report id for the given `offender` and `time_slot` combination. -pub fn report_id(time_slot: u128, offender: u64) -> H256 { - Offences::report_id::(&time_slot, &offender) -} diff --git a/frame/offences/src/tests.rs b/frame/offences/src/tests.rs index 81f0f44f1..d525c7c3a 100644 --- a/frame/offences/src/tests.rs +++ b/frame/offences/src/tests.rs @@ -21,8 +21,8 @@ use super::*; use crate::mock::{ - new_test_ext, offence_reports, report_id, with_on_offence_fractions, Offence, Offences, - RuntimeEvent, System, KIND, + new_test_ext, offence_reports, with_on_offence_fractions, Offence, Offences, RuntimeEvent, + System, KIND, }; use frame_system::{EventRecord, Phase}; use sp_runtime::Perbill; @@ -245,48 +245,3 @@ fn should_properly_count_offences() { ); }); } - -/// We insert offences in sorted order using the time slot in the `same_kind_reports`. -/// This test ensures that it works as expected. -#[test] -fn should_properly_sort_offences() { - new_test_ext().execute_with(|| { - // given - let time_slot = 42; - assert_eq!(offence_reports(KIND, time_slot), vec![]); - - let offence1 = Offence { validator_set_count: 5, time_slot, offenders: vec![5] }; - let offence2 = Offence { validator_set_count: 5, time_slot, offenders: vec![4] }; - let offence3 = - Offence { validator_set_count: 5, time_slot: time_slot + 1, offenders: vec![6, 7] }; - let offence4 = - Offence { validator_set_count: 5, time_slot: time_slot - 1, offenders: vec![3] }; - Offences::report_offence(vec![], offence1).unwrap(); - with_on_offence_fractions(|f| { - assert_eq!(f.clone(), vec![Perbill::from_percent(25)]); - f.clear(); - }); - - // when - // report for the second time - Offences::report_offence(vec![], offence2).unwrap(); - Offences::report_offence(vec![], offence3).unwrap(); - Offences::report_offence(vec![], offence4).unwrap(); - - // then - let same_kind_reports = Vec::<(u128, sp_core::H256)>::decode( - &mut &crate::ReportsByKindIndex::::get(KIND)[..], - ) - .unwrap(); - assert_eq!( - same_kind_reports, - vec![ - (time_slot - 1, report_id(time_slot - 1, 3)), - (time_slot, report_id(time_slot, 5)), - (time_slot, report_id(time_slot, 4)), - (time_slot + 1, report_id(time_slot + 1, 6)), - (time_slot + 1, report_id(time_slot + 1, 7)), - ] - ); - }); -} From c4f425b062a749010b724a3b74e8d93b6f9b01d8 Mon Sep 17 00:00:00 2001 From: Squirrel Date: Tue, 25 Apr 2023 21:48:37 +0100 Subject: [PATCH 421/558] Allow missing docs for autogen weights. (#14011) --- .maintain/frame-weight-template.hbs | 1 + utils/frame/benchmarking-cli/src/pallet/template.hbs | 1 + 2 files changed, 2 insertions(+) diff --git a/.maintain/frame-weight-template.hbs b/.maintain/frame-weight-template.hbs index 0df6bef5d..38bb4de26 100644 --- a/.maintain/frame-weight-template.hbs +++ b/.maintain/frame-weight-template.hbs @@ -15,6 +15,7 @@ #![cfg_attr(rustfmt, rustfmt_skip)] #![allow(unused_parens)] #![allow(unused_imports)] +#![allow(missing_docs)] use frame_support::{traits::Get, weights::{Weight, constants::RocksDbWeight}}; use core::marker::PhantomData; diff --git a/utils/frame/benchmarking-cli/src/pallet/template.hbs b/utils/frame/benchmarking-cli/src/pallet/template.hbs index f852e773c..85b0e86ca 100644 --- a/utils/frame/benchmarking-cli/src/pallet/template.hbs +++ b/utils/frame/benchmarking-cli/src/pallet/template.hbs @@ -15,6 +15,7 @@ #![cfg_attr(rustfmt, rustfmt_skip)] #![allow(unused_parens)] #![allow(unused_imports)] +#![allow(missing_docs)] use frame_support::{traits::Get, weights::Weight}; use core::marker::PhantomData; From af504bc374db82e0d764f1393f4faee42929e4ad Mon Sep 17 00:00:00 2001 From: Sasha Gryaznov Date: Wed, 26 Apr 2023 14:27:13 +0300 Subject: [PATCH 422/558] [contracts] Port host functions to Weight V2 and storage deposit limit (#13565) * added [unstable][seal2] call() * updated test to cover new seal_call proof_limit * docs updated * add [seal2][unstable] instantiate() and test * add [seal2][unstable] weight_to_fee() + docs and test * add [seal2][unstable] gas_left() + docs and test * update benchmarks * add DefaultDepositLimit to pallet Config * specify deposit limit for nested call add test for nested call deposit limit save: separate deposit limit for nested calls * specify deposit limit for nested instantiate save: works with test cleaned up debugging outputs * update benchmarks * added missing fixtures * fix benches * pass explicit deposit limit to storage bench * explicit deposit limit for another set_storage bench * add more deposit limit for storage benches * moving to simplified benchmarks * moved to simplified benchmarks * fix seal_weight_to_fee bench * fix seal_instantiate benchmark * doc typo fix * default dl for benchmarking more dl for tests dl for tests to max deposit_limit fix in instantiate bench fix instantiate bench fix instantiate benchmark fix instantiate bench again remove dbg fix seal bench again fixing it still seal_instantiate zero deposit less runs to check if deposit enough try try 2 try 3 try 4 * max_runtime_mem to Schedule limits * add default deposit limit fallback check to test * weight params renaming * fmt * Update frame/contracts/src/benchmarking/mod.rs Co-authored-by: PG Herveou * prettify inputs in tests * typestate param refactored --------- Co-authored-by: PG Herveou --- bin/node/runtime/src/lib.rs | 2 + frame/contracts/README.md | 8 +- frame/contracts/fixtures/call_with_limit.wat | 21 +- frame/contracts/fixtures/caller_contract.wat | 137 +++++--- .../fixtures/create_storage_and_call.wat | 13 +- .../create_storage_and_instantiate.wat | 66 ++++ .../fixtures/{store.wat => store_call.wat} | 0 frame/contracts/fixtures/store_deploy.wat | 45 +++ frame/contracts/src/benchmarking/mod.rs | 79 +++-- frame/contracts/src/exec.rs | 124 +++++-- frame/contracts/src/lib.rs | 18 +- frame/contracts/src/schedule.rs | 5 + frame/contracts/src/storage/meter.rs | 80 +++-- frame/contracts/src/tests.rs | 327 ++++++++++++++---- frame/contracts/src/wasm/mod.rs | 48 +-- frame/contracts/src/wasm/runtime.rs | 249 ++++++++++--- 16 files changed, 948 insertions(+), 274 deletions(-) create mode 100644 frame/contracts/fixtures/create_storage_and_instantiate.wat rename frame/contracts/fixtures/{store.wat => store_call.wat} (100%) create mode 100644 frame/contracts/fixtures/store_deploy.wat diff --git a/bin/node/runtime/src/lib.rs b/bin/node/runtime/src/lib.rs index 5722603fe..766bc9515 100644 --- a/bin/node/runtime/src/lib.rs +++ b/bin/node/runtime/src/lib.rs @@ -1215,6 +1215,7 @@ impl pallet_tips::Config for Runtime { parameter_types! { pub const DepositPerItem: Balance = deposit(1, 0); pub const DepositPerByte: Balance = deposit(0, 1); + pub const DefaultDepositLimit: Balance = deposit(1024, 1024 * 1024); pub Schedule: pallet_contracts::Schedule = Default::default(); } @@ -1233,6 +1234,7 @@ impl pallet_contracts::Config for Runtime { type CallFilter = Nothing; type DepositPerItem = DepositPerItem; type DepositPerByte = DepositPerByte; + type DefaultDepositLimit = DefaultDepositLimit; type CallStack = [pallet_contracts::Frame; 5]; type WeightPrice = pallet_transaction_payment::Pallet; type WeightInfo = pallet_contracts::weights::SubstrateWeight; diff --git a/frame/contracts/README.md b/frame/contracts/README.md index cf89c9514..13c5e7253 100644 --- a/frame/contracts/README.md +++ b/frame/contracts/README.md @@ -1,6 +1,6 @@ -# Contract Module +# Contracts Module -The Contract module provides functionality for the runtime to deploy and execute WebAssembly smart-contracts. +The Contracts module provides functionality for the runtime to deploy and execute WebAssembly smart-contracts. - [`Call`](https://paritytech.github.io/substrate/master/pallet_contracts/pallet/enum.Call.html) - [`Config`](https://paritytech.github.io/substrate/master/pallet_contracts/pallet/trait.Config.html) @@ -63,9 +63,9 @@ directly. This makes sure that by default `pallet-contracts` does not expose any When setting up the `Schedule` for your runtime make sure to set `InstructionWeights::fallback` to a non zero value. The default is `0` and prevents the upload of any non deterministic code. -An indeterministic code can be deployed on-chain by passing `Determinism::AllowIndeterministic` +An indeterministic code can be deployed on-chain by passing `Determinism::Relaxed` to `upload_code`. A deterministic contract can then delegate call into it if and only if it -is ran by using `bare_call` and passing `Determinism::AllowIndeterministic` to it. **Never use +is ran by using `bare_call` and passing `Determinism::Relaxed` to it. **Never use this argument when the contract is called from an on-chain transaction.** ## Interface diff --git a/frame/contracts/fixtures/call_with_limit.wat b/frame/contracts/fixtures/call_with_limit.wat index abb870826..04da59551 100644 --- a/frame/contracts/fixtures/call_with_limit.wat +++ b/frame/contracts/fixtures/call_with_limit.wat @@ -1,8 +1,8 @@ -;; This expects [account_id, gas_limit] as input and calls the account_id with the supplied gas_limit. +;; This expects [account_id, ref_time, proof_size] as input and calls the account_id with the supplied 2D Weight limit. ;; It returns the result of the call as output data. (module (import "seal0" "seal_input" (func $seal_input (param i32 i32))) - (import "seal0" "seal_call" (func $seal_call (param i32 i32 i64 i32 i32 i32 i32 i32 i32) (result i32))) + (import "seal2" "call" (func $seal_call (param i32 i32 i64 i64 i32 i32 i32 i32 i32 i32) (result i32))) (import "seal0" "seal_return" (func $seal_return (param i32 i32 i32))) (import "env" "memory" (memory 1 1)) @@ -13,24 +13,25 @@ (func (export "deploy")) (func (export "call") - ;; Receive the encoded call + gas_limit + ;; Receive the encoded account_id, ref_time, proof_size (call $seal_input (i32.const 4) ;; Pointer to the input buffer - (i32.const 0) ;; Size of the length buffer + (i32.const 0) ;; Pointer to the length of the input buffer ) (i32.store (i32.const 0) (call $seal_call + (i32.const 0) ;; Set no flag. (i32.const 4) ;; Pointer to "callee" address. - (i32.const 32) ;; Length of "callee" address. - (i64.load (i32.const 36)) ;; How much gas to devote for the execution. + (i64.load (i32.const 36)) ;; How much ref_time to devote for the execution. + (i64.load (i32.const 44)) ;; How much proof_size to devote for the execution. + (i32.const 0xffffffff) ;; u32 max sentinel value: pass no deposit limit. (i32.const 0) ;; Pointer to the buffer with value to transfer - (i32.const 0) ;; Length of the buffer with value to transfer. (i32.const 0) ;; Pointer to input data buffer address - (i32.const 0) ;; Length of input data buffer + (i32.const 0) ;; Length of input data buffer (i32.const 0xffffffff) ;; u32 max sentinel value: do not copy output - (i32.const 0) ;; Ptr to output buffer len - ) + (i32.const 0) ;; Length is ignored in this case + ) ) (call $seal_return (i32.const 0) (i32.const 0) (i32.const 4)) ) diff --git a/frame/contracts/fixtures/caller_contract.wat b/frame/contracts/fixtures/caller_contract.wat index f9caf49f2..929171b9a 100644 --- a/frame/contracts/fixtures/caller_contract.wat +++ b/frame/contracts/fixtures/caller_contract.wat @@ -1,9 +1,9 @@ (module (import "seal0" "seal_input" (func $seal_input (param i32 i32))) (import "seal0" "seal_balance" (func $seal_balance (param i32 i32))) - (import "seal0" "seal_call" (func $seal_call (param i32 i32 i64 i32 i32 i32 i32 i32 i32) (result i32))) - (import "seal0" "seal_instantiate" (func $seal_instantiate - (param i32 i32 i64 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32) (result i32) + (import "seal2" "call" (func $seal_call (param i32 i32 i64 i64 i32 i32 i32 i32 i32 i32) (result i32))) + (import "seal2" "instantiate" (func $seal_instantiate + (param i32 i64 i64 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32) (result i32) )) (import "env" "memory" (memory 1 1)) @@ -43,18 +43,18 @@ (set_local $exit_code (call $seal_instantiate (i32.const 24) ;; Pointer to the code hash. - (i32.const 32) ;; Length of the code hash. - (i64.const 0) ;; How much gas to devote for the execution. 0 = all. + (i64.const 0) ;; How much ref_time weight to devote for the execution. 0 = all. + (i64.const 0) ;; How much proof_size weight to devote for the execution. 0 = all. + (i32.const 0xffffffff) ;; u32 max sentinel value: pass no deposit limit. (i32.const 0) ;; Pointer to the buffer with value to transfer - (i32.const 8) ;; Length of the buffer with value to transfer. (i32.const 9) ;; Pointer to input data buffer address (i32.const 7) ;; Length of input data buffer - (i32.const 4294967295) ;; u32 max sentinel value: do not copy address - (i32.const 0) ;; Length is ignored in this case + (i32.const 4294967295) ;; u32 max sentinel value: do not copy address + (i32.const 0) ;; Length is ignored in this case (i32.const 4294967295) ;; u32 max sentinel value: do not copy output - (i32.const 0) ;; Length is ignored in this case - (i32.const 0) ;; salt_ptr - (i32.const 0) ;; salt_le + (i32.const 0) ;; Length is ignored in this case + (i32.const 0) ;; salt_ptr + (i32.const 0) ;; salt_le ) ) @@ -63,22 +63,47 @@ (i32.eq (get_local $exit_code) (i32.const 2)) ;; ReturnCode::CalleeReverted ) - ;; Fail to deploy the contract due to insufficient gas. + ;; Fail to deploy the contract due to insufficient ref_time weight. (set_local $exit_code (call $seal_instantiate (i32.const 24) ;; Pointer to the code hash. - (i32.const 32) ;; Length of the code hash. - (i64.const 1) ;; Supply too little gas + (i64.const 1) ;; Supply too little ref_time weight + (i64.const 0) ;; How much proof_size weight to devote for the execution. 0 = all. + (i32.const 0xffffffff) ;; u32 max sentinel value: pass no deposit limit. (i32.const 0) ;; Pointer to the buffer with value to transfer - (i32.const 8) ;; Length of the buffer with value to transfer. (i32.const 8) ;; Pointer to input data buffer address (i32.const 8) ;; Length of input data buffer (i32.const 4294967295) ;; u32 max sentinel value: do not copy address - (i32.const 0) ;; Length is ignored in this case + (i32.const 0) ;; Length is ignored in this case (i32.const 4294967295) ;; u32 max sentinel value: do not copy output - (i32.const 0) ;; Length is ignored in this case - (i32.const 0) ;; salt_ptr - (i32.const 0) ;; salt_le + (i32.const 0) ;; Length is ignored in this case + (i32.const 0) ;; salt_ptr + (i32.const 0) ;; salt_le + + ) + ) + + ;; Check for special trap exit status. + (call $assert + (i32.eq (get_local $exit_code) (i32.const 1)) ;; ReturnCode::CalleeTrapped + ) + + ;; Fail to deploy the contract due to insufficient ref_time weight. + (set_local $exit_code + (call $seal_instantiate + (i32.const 24) ;; Pointer to the code hash. + (i64.const 0) ;; How much ref_time weight to devote for the execution. 0 = all. + (i64.const 1) ;; Supply too little proof_size weight + (i32.const 0xffffffff) ;; u32 max sentinel value: pass no deposit limit. + (i32.const 0) ;; Pointer to the buffer with value to transfer + (i32.const 8) ;; Pointer to input data buffer address + (i32.const 8) ;; Length of input data buffer + (i32.const 4294967295) ;; u32 max sentinel value: do not copy address + (i32.const 0) ;; Length is ignored in this case + (i32.const 4294967295) ;; u32 max sentinel value: do not copy output + (i32.const 0) ;; Length is ignored in this case + (i32.const 0) ;; salt_ptr + (i32.const 0) ;; salt_le ) ) @@ -98,18 +123,18 @@ (set_local $exit_code (call $seal_instantiate (i32.const 24) ;; Pointer to the code hash. - (i32.const 32) ;; Length of the code hash. - (i64.const 0) ;; How much gas to devote for the execution. 0 = all. + (i64.const 0) ;; How much ref_time weight to devote for the execution. 0 = all. + (i64.const 0) ;; How much proof_size weight to devote for the execution. 0 = all. + (i32.const 0xffffffff) ;; u32 max sentinel value: pass no deposit limit. (i32.const 0) ;; Pointer to the buffer with value to transfer - (i32.const 8) ;; Length of the buffer with value to transfer. (i32.const 8) ;; Pointer to input data buffer address (i32.const 8) ;; Length of input data buffer - (i32.const 16) ;; Pointer to the address output buffer - (i32.sub (get_local $sp) (i32.const 4)) ;; Pointer to the address buffer length - (i32.const 4294967295) ;; u32 max sentinel value: do not copy output - (i32.const 0) ;; Length is ignored in this case - (i32.const 0) ;; salt_ptr - (i32.const 0) ;; salt_le + (i32.const 16) ;; Pointer to the address output buffer + (i32.sub (get_local $sp) (i32.const 4)) ;; Pointer to the address buffer length + (i32.const 4294967295) ;; u32 max sentinel value: do not copy output + (i32.const 0) ;; Length is ignored in this case + (i32.const 0) ;; salt_ptr + (i32.const 0) ;; salt_le ) ) @@ -139,15 +164,16 @@ ;; Call the new contract and expect it to return failing exit code. (set_local $exit_code (call $seal_call + (i32.const 0) ;; Set no flag (i32.const 16) ;; Pointer to "callee" address. - (i32.const 32) ;; Length of "callee" address. - (i64.const 0) ;; How much gas to devote for the execution. 0 = all. + (i64.const 0) ;; How much ref_time weight to devote for the execution. 0 = all. + (i64.const 0) ;; How much proof_size weight to devote for the execution. 0 = all. + (i32.const 0xffffffff) ;; u32 max sentinel value: pass no deposit limit. (i32.const 0) ;; Pointer to the buffer with value to transfer - (i32.const 8) ;; Length of the buffer with value to transfer. (i32.const 9) ;; Pointer to input data buffer address (i32.const 7) ;; Length of input data buffer - (i32.sub (get_local $sp) (i32.const 4)) ;; Ptr to output buffer - (i32.sub (get_local $sp) (i32.const 8)) ;; Ptr to output buffer len + (i32.sub (get_local $sp) (i32.const 4)) ;; Ptr to output buffer + (i32.sub (get_local $sp) (i32.const 8)) ;; Ptr to output buffer len ) ) @@ -167,18 +193,40 @@ ) ) - ;; Fail to call the contract due to insufficient gas. + ;; Fail to call the contract due to insufficient ref_time weight. (set_local $exit_code (call $seal_call + (i32.const 0) ;; Set no flag (i32.const 16) ;; Pointer to "callee" address. - (i32.const 32) ;; Length of "callee" address. - (i64.const 1) ;; Supply too little gas + (i64.const 1) ;; Supply too little ref_time weight + (i64.const 0) ;; How much proof_size weight to devote for the execution. 0 = all. + (i32.const 0xffffffff) ;; u32 max sentinel value: pass no deposit limit. (i32.const 0) ;; Pointer to the buffer with value to transfer - (i32.const 8) ;; Length of the buffer with value to transfer. (i32.const 8) ;; Pointer to input data buffer address (i32.const 8) ;; Length of input data buffer - (i32.const 4294967295) ;; u32 max sentinel value: do not copy output - (i32.const 0) ;; Length is ignored in this cas + (i32.const 4294967295) ;; u32 max sentinel value: do not copy output + (i32.const 0) ;; Length is ignored in this cas + ) + ) + + ;; Check for special trap exit status. + (call $assert + (i32.eq (get_local $exit_code) (i32.const 1)) ;; ReturnCode::CalleeTrapped + ) + + ;; Fail to call the contract due to insufficient proof_size weight. + (set_local $exit_code + (call $seal_call + (i32.const 0) ;; Set no flag + (i32.const 16) ;; Pointer to "callee" address. + (i64.const 0) ;; How much ref_time weight to devote for the execution. 0 = all. + (i64.const 1) ;; Supply too little proof_size weight + (i32.const 0xffffffff) ;; u32 max sentinel value: pass no deposit limit. + (i32.const 0) ;; Pointer to the buffer with value to transfer + (i32.const 8) ;; Pointer to input data buffer address + (i32.const 8) ;; Length of input data buffer + (i32.const 4294967295) ;; u32 max sentinel value: do not copy output + (i32.const 0) ;; Length is ignored in this cas ) ) @@ -202,15 +250,16 @@ ;; Call the contract successfully. (set_local $exit_code (call $seal_call + (i32.const 0) ;; Set no flag (i32.const 16) ;; Pointer to "callee" address. - (i32.const 32) ;; Length of "callee" address. - (i64.const 0) ;; How much gas to devote for the execution. 0 = all. + (i64.const 0) ;; How much ref_time weight to devote for the execution. 0 = all. + (i64.const 0) ;; How much proof_size weight to devote for the execution. 0 = all. + (i32.const 0xffffffff) ;; u32 max sentinel value: pass no deposit limit. (i32.const 0) ;; Pointer to the buffer with value to transfer - (i32.const 8) ;; Length of the buffer with value to transfer. (i32.const 8) ;; Pointer to input data buffer address (i32.const 8) ;; Length of input data buffer - (i32.sub (get_local $sp) (i32.const 4)) ;; Ptr to output buffer - (i32.sub (get_local $sp) (i32.const 8)) ;; Ptr to output buffer len + (i32.sub (get_local $sp) (i32.const 4)) ;; Ptr to output buffer + (i32.sub (get_local $sp) (i32.const 8)) ;; Ptr to output buffer len ) ) diff --git a/frame/contracts/fixtures/create_storage_and_call.wat b/frame/contracts/fixtures/create_storage_and_call.wat index 2a1e53f7c..5592e7e96 100644 --- a/frame/contracts/fixtures/create_storage_and_call.wat +++ b/frame/contracts/fixtures/create_storage_and_call.wat @@ -2,7 +2,7 @@ (module (import "seal0" "seal_input" (func $seal_input (param i32 i32))) (import "seal0" "seal_set_storage" (func $seal_set_storage (param i32 i32 i32))) - (import "seal1" "seal_call" (func $seal_call (param i32 i32 i64 i32 i32 i32 i32 i32) (result i32))) + (import "seal2" "call" (func $seal_call (param i32 i32 i64 i64 i32 i32 i32 i32 i32 i32) (result i32))) (import "env" "memory" (memory 1 1)) (func $assert (param i32) @@ -20,7 +20,10 @@ ;; store length of input buffer (i32.store (i32.const 0) (i32.const 512)) - ;; copy input at address 4 + ;; copy input at address 4: + ;; first 4 bytes for the size of the storage to be created in callee + ;; next 32 bytes are for the callee address + ;; next bytes for the encoded deposit limit (call $seal_input (i32.const 4) (i32.const 0)) ;; create 4 byte of storage before calling @@ -34,8 +37,10 @@ (call $assert (i32.eqz (call $seal_call (i32.const 0) ;; No flags - (i32.const 8) ;; Pointer to "callee" address. - (i64.const 0) ;; How much gas to devote for the execution. 0 = all. + (i32.const 8) ;; Pointer to "callee" address + (i64.const 0) ;; How much ref_time to devote for the execution. 0 = all + (i64.const 0) ;; How much proof_limit to devote for the execution. 0 = all + (i32.const 40) ;; Pointer to the storage deposit limit (i32.const 512) ;; Pointer to the buffer with value to transfer (i32.const 4) ;; Pointer to input data buffer address (i32.const 4) ;; Length of input data buffer diff --git a/frame/contracts/fixtures/create_storage_and_instantiate.wat b/frame/contracts/fixtures/create_storage_and_instantiate.wat new file mode 100644 index 000000000..cd7202478 --- /dev/null +++ b/frame/contracts/fixtures/create_storage_and_instantiate.wat @@ -0,0 +1,66 @@ +;; This instantiates another contract and passes some input to its constructor. +(module + (import "seal0" "seal_input" (func $seal_input (param i32 i32))) + (import "seal0" "seal_set_storage" (func $seal_set_storage (param i32 i32 i32))) + (import "seal2" "instantiate" (func $seal_instantiate + (param i32 i64 i64 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32) (result i32) + )) + (import "seal0" "seal_return" (func $seal_return (param i32 i32 i32))) + (import "env" "memory" (memory 1 1)) + + ;; [0, 8) send 10_000 balance + (data (i32.const 48) "\10\27\00\00\00\00\00\00") + + (func $assert (param i32) + (block $ok + (br_if $ok + (get_local 0) + ) + (unreachable) + ) + ) + + (func (export "deploy")) + + (func (export "call") + ;; store length of input buffer + (i32.store (i32.const 0) (i32.const 512)) + ;; store length of contract address + (i32.store (i32.const 84) (i32.const 32)) + + ;; copy input at address 4 + (call $seal_input (i32.const 4) (i32.const 0)) + + ;; memory layout is: + ;; [0,4): size of input buffer + ;; [4,8): size of the storage to be created in callee + ;; [8,40): the code hash of the contract to instantiate + ;; [40,48): for the encoded deposit limit + ;; [48,52): value to transfer + ;; [52,84): address of the deployed contract + ;; [84,88): len of the address + + ;; instantiate a contract + (call $assert (i32.eqz +;; (i32.store +;; (i32.const 64) + (call $seal_instantiate + (i32.const 8) ;; Pointer to the code hash. + (i64.const 0) ;; How much ref_time weight to devote for the execution. 0 = all. + (i64.const 0) ;; How much proof_size weight to devote for the execution. 0 = all. + (i32.const 40) ;; Pointer to the storage deposit limit + (i32.const 48) ;; Pointer to the buffer with value to transfer + (i32.const 4) ;; Pointer to input data buffer address + (i32.const 4) ;; Length of input data buffer + (i32.const 52) ;; Pointer to where to copy address + (i32.const 84) ;; Pointer to address len ptr + (i32.const 0xffffffff) ;; u32 max sentinel value: do not copy output + (i32.const 0) ;; Length is ignored in this case + (i32.const 0) ;; salt_ptr + (i32.const 0) ;; salt_len + ) + )) + ;; return the deployed contract address + (call $seal_return (i32.const 0) (i32.const 52) (i32.const 32)) + ) +) diff --git a/frame/contracts/fixtures/store.wat b/frame/contracts/fixtures/store_call.wat similarity index 100% rename from frame/contracts/fixtures/store.wat rename to frame/contracts/fixtures/store_call.wat diff --git a/frame/contracts/fixtures/store_deploy.wat b/frame/contracts/fixtures/store_deploy.wat new file mode 100644 index 000000000..cc428e962 --- /dev/null +++ b/frame/contracts/fixtures/store_deploy.wat @@ -0,0 +1,45 @@ +;; Stores a value of the passed size in constructor. +(module + (import "seal0" "seal_set_storage" (func $seal_set_storage (param i32 i32 i32))) + (import "seal0" "seal_input" (func $seal_input (param i32 i32))) + (import "env" "memory" (memory 16 16)) + + ;; [0, 32) storage key + (data (i32.const 0) "\01") + + ;; [32, 36) buffer where input is copied (expected size of storage item) + + ;; [36, 40) size of the input buffer + (data (i32.const 36) "\04") + + (func $assert (param i32) + (block $ok + (br_if $ok + (get_local 0) + ) + (unreachable) + ) + ) + + (func (export "deploy") + (call $seal_input (i32.const 32) (i32.const 36)) + + ;; assert input size == 4 + (call $assert + (i32.eq + (i32.load (i32.const 36)) + (i32.const 4) + ) + ) + + ;; place a value in storage, the size of which is specified by the call input. + ;; we don't care about the contents of the storage item + (call $seal_set_storage + (i32.const 0) ;; Pointer to storage key + (i32.const 0) ;; Pointer to value + (i32.load (i32.const 32)) ;; Size of value + ) + ) + + (func (export "call")) +) diff --git a/frame/contracts/src/benchmarking/mod.rs b/frame/contracts/src/benchmarking/mod.rs index 98a3dc36b..6ebfb1850 100644 --- a/frame/contracts/src/benchmarking/mod.rs +++ b/frame/contracts/src/benchmarking/mod.rs @@ -547,7 +547,7 @@ benchmarks! { seal_gas_left { let r in 0 .. API_BENCHMARK_RUNS; let instance = Contract::::new(WasmModule::getter( - "seal0", "seal_gas_left", r + "seal1", "gas_left", r ), vec![])?; let origin = RawOrigin::Signed(instance.caller.clone()); }: call(origin, instance.addr, 0u32.into(), Weight::MAX, None, vec![]) @@ -604,9 +604,9 @@ benchmarks! { let code = WasmModule::::from(ModuleDefinition { memory: Some(ImportedMemory::max::()), imported_functions: vec![ImportedFunction { - module: "seal0", - name: "seal_weight_to_fee", - params: vec![ValueType::I64, ValueType::I32, ValueType::I32], + module: "seal1", + name: "weight_to_fee", + params: vec![ValueType::I64, ValueType::I64, ValueType::I32, ValueType::I32], return_type: None, }], data_segments: vec![DataSegment { @@ -615,6 +615,7 @@ benchmarks! { }], call_body: Some(body::repeated(r, &[ Instruction::I64Const(500_000), + Instruction::I64Const(300_000), Instruction::I32Const(4), Instruction::I32Const(0), Instruction::Call(0), @@ -1584,16 +1585,26 @@ benchmarks! { let callee_bytes = callees.iter().flat_map(|x| x.account_id.encode()).collect(); let value: BalanceOf = 0u32.into(); let value_bytes = value.encode(); - let value_len = value_bytes.len(); + let value_len = BalanceOf::::max_encoded_len() as u32; + // Set an own limit every 2nd call + let own_limit = (u32::MAX - 100).into(); + let deposits = (0..r) + .map(|i| if i % 2 == 0 { 0u32.into() } else { own_limit } ) + .collect::>>(); + let deposits_bytes: Vec = deposits.iter().flat_map(|i| i.encode()).collect(); + let deposits_len = deposits_bytes.len() as u32; + let deposit_len = value_len.clone(); + let callee_offset = value_len + deposits_len; let code = WasmModule::::from(ModuleDefinition { memory: Some(ImportedMemory::max::()), imported_functions: vec![ImportedFunction { - module: "seal0", - name: "seal_call", + module: "seal2", + name: "call", params: vec![ ValueType::I32, ValueType::I32, ValueType::I64, + ValueType::I64, ValueType::I32, ValueType::I32, ValueType::I32, @@ -1609,16 +1620,21 @@ benchmarks! { value: value_bytes, }, DataSegment { - offset: value_len as u32, + offset: value_len, + value: deposits_bytes, + }, + DataSegment { + offset: callee_offset, value: callee_bytes, }, ], call_body: Some(body::repeated_dyn(r, vec![ - Counter(value_len as u32, callee_len as u32), // callee_ptr - Regular(Instruction::I32Const(callee_len as i32)), // callee_len - Regular(Instruction::I64Const(0)), // gas + Regular(Instruction::I32Const(0)), // flags + Counter(callee_offset, callee_len as u32), // callee_ptr + Regular(Instruction::I64Const(0)), // ref_time weight + Regular(Instruction::I64Const(0)), // proof_size weight + Counter(value_len, deposit_len as u32), // deposit_limit_ptr Regular(Instruction::I32Const(0)), // value_ptr - Regular(Instruction::I32Const(value_len as i32)), // value_len Regular(Instruction::I32Const(0)), // input_data_ptr Regular(Instruction::I32Const(0)), // input_data_len Regular(Instruction::I32Const(SENTINEL as i32)), // output_ptr @@ -1630,7 +1646,7 @@ benchmarks! { }); let instance = Contract::::new(code, vec![])?; let origin = RawOrigin::Signed(instance.caller.clone()); - }: call(origin, instance.addr, 0u32.into(), Weight::MAX, None, vec![]) + }: call(origin, instance.addr, 0u32.into(), Weight::MAX, Some(BalanceOf::::from(u32::MAX).into()), vec![]) // This is a slow call: We redeuce the number of runs. #[pov_mode = Measured] @@ -1742,17 +1758,17 @@ benchmarks! { }: call(origin, instance.addr, 0u32.into(), Weight::MAX, None, bytes) // We assume that every instantiate sends at least the minimum balance. - // This is a slow call: We redeuce the number of runs. + // This is a slow call: we reduce the number of runs. #[pov_mode = Measured] seal_instantiate { - let r in 0 .. API_BENCHMARK_RUNS / 2; + let r in 1 .. API_BENCHMARK_RUNS / 2; let hashes = (0..r) .map(|i| { let code = WasmModule::::from(ModuleDefinition { memory: Some(ImportedMemory::max::()), call_body: Some(body::plain(vec![ - // we need to add this in order to make contracts unique - // so that they can be deployed from the same sender + // We need to add this in order to make contracts unique, + // so that they can be deployed from the same sender. Instruction::I32Const(i as i32), Instruction::Drop, Instruction::End, @@ -1765,27 +1781,24 @@ benchmarks! { .collect::, &'static str>>()?; let hash_len = hashes.get(0).map(|x| x.encode().len()).unwrap_or(0); let hashes_bytes = hashes.iter().flat_map(|x| x.encode()).collect::>(); - let hashes_len = hashes_bytes.len(); + let hashes_len = &hashes_bytes.len(); let value = Pallet::::min_balance(); assert!(value > 0u32.into()); let value_bytes = value.encode(); - let value_len = value_bytes.len(); + let value_len = BalanceOf::::max_encoded_len(); let addr_len = T::AccountId::max_encoded_len(); - - // offsets where to place static data in contract memory - let value_offset = 0; - let hashes_offset = value_offset + value_len; + // Offsets where to place static data in contract memory. + let hashes_offset = value_len; let addr_len_offset = hashes_offset + hashes_len; let addr_offset = addr_len_offset + addr_len; - let code = WasmModule::::from(ModuleDefinition { memory: Some(ImportedMemory::max::()), imported_functions: vec![ImportedFunction { - module: "seal0", - name: "seal_instantiate", + module: "seal2", + name: "instantiate", params: vec![ ValueType::I32, - ValueType::I32, + ValueType::I64, ValueType::I64, ValueType::I32, ValueType::I32, @@ -1802,7 +1815,7 @@ benchmarks! { }], data_segments: vec![ DataSegment { - offset: value_offset as u32, + offset: 0, value: value_bytes, }, DataSegment { @@ -1816,10 +1829,10 @@ benchmarks! { ], call_body: Some(body::repeated_dyn(r, vec![ Counter(hashes_offset as u32, hash_len as u32), // code_hash_ptr - Regular(Instruction::I32Const(hash_len as i32)), // code_hash_len - Regular(Instruction::I64Const(0)), // gas - Regular(Instruction::I32Const(value_offset as i32)), // value_ptr - Regular(Instruction::I32Const(value_len as i32)), // value_len + Regular(Instruction::I64Const(0)), // ref_time weight + Regular(Instruction::I64Const(0)), // proof_size weight + Regular(Instruction::I32Const(SENTINEL as i32)), // deposit limit ptr: use parent's limit + Regular(Instruction::I32Const(0)), // value_ptr Regular(Instruction::I32Const(0)), // input_data_ptr Regular(Instruction::I32Const(0)), // input_data_len Regular(Instruction::I32Const(addr_offset as i32)), // address_ptr @@ -1827,7 +1840,7 @@ benchmarks! { Regular(Instruction::I32Const(SENTINEL as i32)), // output_ptr Regular(Instruction::I32Const(0)), // output_len_ptr Regular(Instruction::I32Const(0)), // salt_ptr - Regular(Instruction::I32Const(0)), // salt_ptr_len + Regular(Instruction::I32Const(0)), // salt_len_ptr Regular(Instruction::Call(0)), Regular(Instruction::Drop), ])), diff --git a/frame/contracts/src/exec.rs b/frame/contracts/src/exec.rs index 46a611dbf..d0a650e0a 100644 --- a/frame/contracts/src/exec.rs +++ b/frame/contracts/src/exec.rs @@ -23,7 +23,9 @@ use crate::{ }; use frame_support::{ crypto::ecdsa::ECDSAExt, - dispatch::{DispatchError, DispatchResult, DispatchResultWithPostInfo, Dispatchable}, + dispatch::{ + fmt::Debug, DispatchError, DispatchResult, DispatchResultWithPostInfo, Dispatchable, + }, storage::{with_transaction, TransactionOutcome}, traits::{ tokens::{Fortitude::Polite, Preservation::Expendable}, @@ -40,7 +42,7 @@ use sp_core::{ sr25519::{Public as SR25519Public, Signature as SR25519Signature}, }; use sp_io::{crypto::secp256k1_ecdsa_recover_compressed, hashing::blake2_256}; -use sp_runtime::traits::{Convert, Hash}; +use sp_runtime::traits::{Convert, Hash, Zero}; use sp_std::{marker::PhantomData, mem, prelude::*, vec::Vec}; pub type AccountIdOf = ::AccountId; @@ -137,6 +139,7 @@ pub trait Ext: sealing::Sealed { fn call( &mut self, gas_limit: Weight, + deposit_limit: BalanceOf, to: AccountIdOf, value: BalanceOf, input_data: Vec, @@ -160,6 +163,7 @@ pub trait Ext: sealing::Sealed { fn instantiate( &mut self, gas_limit: Weight, + deposit_limit: BalanceOf, code: CodeHash, value: BalanceOf, input_data: Vec, @@ -692,8 +696,9 @@ where args, value, gas_meter, - storage_meter, Weight::zero(), + storage_meter, + BalanceOf::::zero(), schedule, determinism, )?; @@ -719,12 +724,13 @@ where /// /// This does not take `self` because when constructing the first frame `self` is /// not initialized, yet. - fn new_frame( + fn new_frame( frame_args: FrameArgs, value_transferred: BalanceOf, gas_meter: &mut GasMeter, - storage_meter: &mut storage::meter::GenericMeter, gas_limit: Weight, + storage_meter: &mut storage::meter::GenericMeter, + deposit_limit: BalanceOf, schedule: &Schedule, determinism: Determinism, ) -> Result<(Frame, E, Option), ExecError> { @@ -781,7 +787,7 @@ where account_id, entry_point, nested_gas: gas_meter.nested(gas_limit)?, - nested_storage: storage_meter.nested(), + nested_storage: storage_meter.nested(deposit_limit), allows_reentry: true, }; @@ -794,6 +800,7 @@ where frame_args: FrameArgs, value_transferred: BalanceOf, gas_limit: Weight, + deposit_limit: BalanceOf, ) -> Result { if self.frames.len() == T::CallStack::size() { return Err(Error::::MaxCallDepthReached.into()) @@ -817,8 +824,9 @@ where frame_args, value_transferred, nested_gas, - nested_storage, gas_limit, + nested_storage, + deposit_limit, self.schedule, self.determinism, )?; @@ -859,8 +867,10 @@ where return Ok(output) } - // Storage limit is enforced as late as possible (when the last frame returns) so that - // the ordering of storage accesses does not matter. + // Storage limit is normally enforced as late as possible (when the last frame returns) + // so that the ordering of storage accesses does not matter. + // (However, if a special limit was set for a sub-call, it should be enforced right + // after the sub-call returned. See below for this case of enforcement). if self.frames.is_empty() { let frame = &mut self.first_frame; frame.contract_info.load(&frame.account_id); @@ -869,7 +879,7 @@ where } let frame = self.top_frame(); - let account_id = &frame.account_id; + let account_id = &frame.account_id.clone(); match (entry_point, delegated_code_hash) { (ExportedFunction::Constructor, _) => { // It is not allowed to terminate a contract inside its constructor. @@ -877,6 +887,13 @@ where return Err(Error::::TerminatedInConstructor.into()) } + // If a special limit was set for the sub-call, we enforce it here. + // This is needed because contract constructor might write to storage. + // The sub-call will be rolled back in case the limit is exhausted. + let frame = self.top_frame_mut(); + let contract = frame.contract_info.as_contract(); + frame.nested_storage.enforce_subcall_limit(contract)?; + // Deposit an instantiation event. Contracts::::deposit_event( vec![T::Hashing::hash_of(self.caller()), T::Hashing::hash_of(account_id)], @@ -893,9 +910,15 @@ where ); }, (ExportedFunction::Call, None) => { + // If a special limit was set for the sub-call, we enforce it here. + // The sub-call will be rolled back in case the limit is exhausted. + let frame = self.top_frame_mut(); + let contract = frame.contract_info.as_contract(); + frame.nested_storage.enforce_subcall_limit(contract)?; + let caller = self.caller(); Contracts::::deposit_event( - vec![T::Hashing::hash_of(caller), T::Hashing::hash_of(account_id)], + vec![T::Hashing::hash_of(caller), T::Hashing::hash_of(&account_id)], Event::Called { caller: caller.clone(), contract: account_id.clone() }, ); }, @@ -1107,6 +1130,7 @@ where fn call( &mut self, gas_limit: Weight, + deposit_limit: BalanceOf, to: T::AccountId, value: BalanceOf, input_data: Vec, @@ -1135,6 +1159,7 @@ where FrameArgs::Call { dest: to, cached_info, delegated_call: None }, value, gas_limit, + deposit_limit, )?; self.run(executable, input_data) }; @@ -1166,6 +1191,7 @@ where }, value, Weight::zero(), + BalanceOf::::zero(), )?; self.run(executable, input_data) } @@ -1173,6 +1199,7 @@ where fn instantiate( &mut self, gas_limit: Weight, + deposit_limit: BalanceOf, code_hash: CodeHash, value: BalanceOf, input_data: Vec, @@ -1190,6 +1217,7 @@ where }, value, gas_limit, + deposit_limit, )?; let account_id = self.top_frame().account_id.clone(); self.run(executable, input_data).map(|ret| (account_id, ret)) @@ -1917,7 +1945,7 @@ mod tests { let value = Default::default(); let recurse_ch = MockLoader::insert(Call, |ctx, _| { // Try to call into yourself. - let r = ctx.ext.call(Weight::zero(), BOB, 0, vec![], true); + let r = ctx.ext.call(Weight::zero(), BalanceOf::::zero(), BOB, 0, vec![], true); ReachedBottom::mutate(|reached_bottom| { if !*reached_bottom { @@ -1971,7 +1999,11 @@ mod tests { WitnessedCallerBob::mutate(|caller| *caller = Some(ctx.ext.caller().clone())); // Call into CHARLIE contract. - assert_matches!(ctx.ext.call(Weight::zero(), CHARLIE, 0, vec![], true), Ok(_)); + assert_matches!( + ctx.ext + .call(Weight::zero(), BalanceOf::::zero(), CHARLIE, 0, vec![], true), + Ok(_) + ); exec_success() }); let charlie_ch = MockLoader::insert(Call, |ctx, _| { @@ -2105,7 +2137,8 @@ mod tests { // ALICE is the origin of the call stack assert!(ctx.ext.caller_is_origin()); // BOB calls CHARLIE - ctx.ext.call(Weight::zero(), CHARLIE, 0, vec![], true) + ctx.ext + .call(Weight::zero(), BalanceOf::::zero(), CHARLIE, 0, vec![], true) }); ExtBuilder::default().build().execute_with(|| { @@ -2136,7 +2169,11 @@ mod tests { assert_eq!(*ctx.ext.address(), BOB); // Call into charlie contract. - assert_matches!(ctx.ext.call(Weight::zero(), CHARLIE, 0, vec![], true), Ok(_)); + assert_matches!( + ctx.ext + .call(Weight::zero(), BalanceOf::::zero(), CHARLIE, 0, vec![], true), + Ok(_) + ); exec_success() }); let charlie_ch = MockLoader::insert(Call, |ctx, _| { @@ -2287,6 +2324,7 @@ mod tests { .ext .instantiate( Weight::zero(), + BalanceOf::::zero(), dummy_ch, ::Currency::minimum_balance(), vec![], @@ -2351,6 +2389,7 @@ mod tests { assert_matches!( ctx.ext.instantiate( Weight::zero(), + BalanceOf::::zero(), dummy_ch, ::Currency::minimum_balance(), vec![], @@ -2443,13 +2482,26 @@ mod tests { let info = ctx.ext.contract_info(); assert_eq!(info.storage_byte_deposit, 0); info.storage_byte_deposit = 42; - assert_eq!(ctx.ext.call(Weight::zero(), CHARLIE, 0, vec![], true), exec_trapped()); + assert_eq!( + ctx.ext.call( + Weight::zero(), + BalanceOf::::zero(), + CHARLIE, + 0, + vec![], + true + ), + exec_trapped() + ); assert_eq!(ctx.ext.contract_info().storage_byte_deposit, 42); } exec_success() }); let code_charlie = MockLoader::insert(Call, |ctx, _| { - assert!(ctx.ext.call(Weight::zero(), BOB, 0, vec![99], true).is_ok()); + assert!(ctx + .ext + .call(Weight::zero(), BalanceOf::::zero(), BOB, 0, vec![99], true) + .is_ok()); exec_trapped() }); @@ -2479,7 +2531,7 @@ mod tests { fn recursive_call_during_constructor_fails() { let code = MockLoader::insert(Constructor, |ctx, _| { assert_matches!( - ctx.ext.call(Weight::zero(), ctx.ext.address().clone(), 0, vec![], true), + ctx.ext.call(Weight::zero(), BalanceOf::::zero(), ctx.ext.address().clone(), 0, vec![], true), Err(ExecError{error, ..}) if error == >::ContractNotFound.into() ); exec_success() @@ -2618,7 +2670,7 @@ mod tests { // call the contract passed as input with disabled reentry let code_bob = MockLoader::insert(Call, |ctx, _| { let dest = Decode::decode(&mut ctx.input_data.as_ref()).unwrap(); - ctx.ext.call(Weight::zero(), dest, 0, vec![], false) + ctx.ext.call(Weight::zero(), BalanceOf::::zero(), dest, 0, vec![], false) }); let code_charlie = MockLoader::insert(Call, |_, _| exec_success()); @@ -2665,15 +2717,17 @@ mod tests { fn call_deny_reentry() { let code_bob = MockLoader::insert(Call, |ctx, _| { if ctx.input_data[0] == 0 { - ctx.ext.call(Weight::zero(), CHARLIE, 0, vec![], false) + ctx.ext + .call(Weight::zero(), BalanceOf::::zero(), CHARLIE, 0, vec![], false) } else { exec_success() } }); // call BOB with input set to '1' - let code_charlie = - MockLoader::insert(Call, |ctx, _| ctx.ext.call(Weight::zero(), BOB, 0, vec![1], true)); + let code_charlie = MockLoader::insert(Call, |ctx, _| { + ctx.ext.call(Weight::zero(), BalanceOf::::zero(), BOB, 0, vec![1], true) + }); ExtBuilder::default().build().execute_with(|| { let schedule = ::Schedule::get(); @@ -2862,6 +2916,7 @@ mod tests { ctx.ext .instantiate( Weight::zero(), + BalanceOf::::zero(), fail_code, ctx.ext.minimum_balance() * 100, vec![], @@ -2875,6 +2930,7 @@ mod tests { .ext .instantiate( Weight::zero(), + BalanceOf::::zero(), success_code, ctx.ext.minimum_balance() * 100, vec![], @@ -2883,7 +2939,9 @@ mod tests { .unwrap(); // a plain call should not influence the account counter - ctx.ext.call(Weight::zero(), account_id, 0, vec![], false).unwrap(); + ctx.ext + .call(Weight::zero(), BalanceOf::::zero(), account_id, 0, vec![], false) + .unwrap(); exec_success() }); @@ -3387,7 +3445,14 @@ mod tests { assert_eq!(ctx.ext.nonce(), 1); // Should not change with a failed instantiation assert_err!( - ctx.ext.instantiate(Weight::zero(), fail_code, 0, vec![], &[],), + ctx.ext.instantiate( + Weight::zero(), + BalanceOf::::zero(), + fail_code, + 0, + vec![], + &[], + ), ExecError { error: >::ContractTrapped.into(), origin: ErrorOrigin::Callee @@ -3395,7 +3460,16 @@ mod tests { ); assert_eq!(ctx.ext.nonce(), 1); // Successful instantiation increments - ctx.ext.instantiate(Weight::zero(), success_code, 0, vec![], &[]).unwrap(); + ctx.ext + .instantiate( + Weight::zero(), + BalanceOf::::zero(), + success_code, + 0, + vec![], + &[], + ) + .unwrap(); assert_eq!(ctx.ext.nonce(), 2); exec_success() }); diff --git a/frame/contracts/src/lib.rs b/frame/contracts/src/lib.rs index 118da36ae..5a82a4a42 100644 --- a/frame/contracts/src/lib.rs +++ b/frame/contracts/src/lib.rs @@ -15,9 +15,9 @@ // See the License for the specific language governing permissions and // limitations under the License. -//! # Contract Pallet +//! # Contracts Pallet //! -//! The Contract module provides functionality for the runtime to deploy and execute WebAssembly +//! The Contracts module provides functionality for the runtime to deploy and execute WebAssembly //! smart-contracts. //! //! - [`Config`] @@ -73,7 +73,7 @@ //! //! ## Usage //! -//! The Contract module is a work in progress. The following examples show how this Contract module +//! The Contracts module is a work in progress. The following examples show how this module //! can be used to instantiate and call contracts. //! //! * [`ink!`](https://use.ink) is @@ -265,6 +265,10 @@ pub mod pallet { #[pallet::constant] type DepositPerByte: Get>; + /// Fallback value to limit the storage deposit if it's not being set by the caller. + #[pallet::constant] + type DefaultDepositLimit: Get>; + /// The amount of balance a caller has to pay for each storage item. /// /// # Note @@ -315,8 +319,8 @@ pub mod pallet { } fn integrity_test() { - // Total runtime memory is expected to have 128Mb upper limit - const MAX_RUNTIME_MEM: u32 = 1024 * 1024 * 128; + // Total runtime memory limit + let max_runtime_mem: u32 = T::Schedule::get().limits.runtime_memory; // Memory limits for a single contract: // Value stack size: 1Mb per contract, default defined in wasmi const MAX_STACK_SIZE: u32 = 1024 * 1024; @@ -350,10 +354,10 @@ pub mod pallet { // This gives us the following formula: // // `(MaxCodeLen * 18 * 4 + MAX_STACK_SIZE + max_heap_size) * max_call_depth < - // MAX_RUNTIME_MEM/2` + // max_runtime_mem/2` // // Hence the upper limit for the `MaxCodeLen` can be defined as follows: - let code_len_limit = MAX_RUNTIME_MEM + let code_len_limit = max_runtime_mem .saturating_div(2) .saturating_div(max_call_depth) .saturating_sub(max_heap_size) diff --git a/frame/contracts/src/schedule.rs b/frame/contracts/src/schedule.rs index 747540bce..aef2fa1ca 100644 --- a/frame/contracts/src/schedule.rs +++ b/frame/contracts/src/schedule.rs @@ -127,6 +127,10 @@ pub struct Limits { /// The maximum size of a storage value and event payload in bytes. pub payload_len: u32, + + /// The maximum node runtime memory. This is for integrity checks only and does not affect the + /// real setting. + pub runtime_memory: u32, } impl Limits { @@ -479,6 +483,7 @@ impl Default for Limits { br_table_size: 256, subject_len: 32, payload_len: 16 * 1024, + runtime_memory: 1024 * 1024 * 128, } } } diff --git a/frame/contracts/src/storage/meter.rs b/frame/contracts/src/storage/meter.rs index 51a0af574..b8bbd6dc4 100644 --- a/frame/contracts/src/storage/meter.rs +++ b/frame/contracts/src/storage/meter.rs @@ -23,7 +23,7 @@ use crate::{ }; use codec::Encode; use frame_support::{ - dispatch::DispatchError, + dispatch::{fmt::Debug, DispatchError}, ensure, traits::{ tokens::{Fortitude::Polite, Preservation::Protect, WithdrawConsequence}, @@ -32,7 +32,10 @@ use frame_support::{ DefaultNoBound, RuntimeDebugNoBound, }; use pallet_contracts_primitives::StorageDeposit as Deposit; -use sp_runtime::{traits::Saturating, FixedPointNumber, FixedU128}; +use sp_runtime::{ + traits::{Saturating, Zero}, + FixedPointNumber, FixedU128, +}; use sp_std::{marker::PhantomData, vec::Vec}; /// Deposit that uses the native currency's balance type. @@ -96,17 +99,24 @@ pub enum ReservingExt {} pub trait State: private::Sealed {} /// State parameter that constitutes a meter that is in its root state. -pub enum Root {} +#[derive(Default, Debug)] +pub struct Root; /// State parameter that constitutes a meter that is in its nested state. -pub enum Nested {} +/// Its value indicates whether the nested meter has its own limit. +#[derive(DefaultNoBound, RuntimeDebugNoBound)] +pub enum Nested { + #[default] + DerivedLimit, + OwnLimit, +} impl State for Root {} impl State for Nested {} /// A type that allows the metering of consumed or freed storage of a single contract call stack. #[derive(DefaultNoBound, RuntimeDebugNoBound)] -pub struct RawMeter { +pub struct RawMeter { /// The limit of how much balance this meter is allowed to consume. limit: BalanceOf, /// The amount of balance that was used in this meter and all of its already absorbed children. @@ -118,8 +128,10 @@ pub struct RawMeter { /// We only have one charge per contract hence the size of this vector is /// limited by the maximum call depth. charges: Vec>, - /// Type parameters are only used in impls. - _phantom: PhantomData<(E, S)>, + /// We store the nested state to determine if it has a special limit for sub-call. + nested: S, + /// Type parameter only used in impls. + _phantom: PhantomData, } /// This type is used to describe a storage change when charging from the meter. @@ -214,6 +226,9 @@ impl Diff { /// this we can do all the refunds before doing any charge. This way a plain account can use /// more deposit than it has balance as along as it is covered by a refund. This /// essentially makes the order of storage changes irrelevant with regard to the deposit system. +/// The only exception is when a special (tougher) deposit limit is specified for a cross-contract +/// call. In that case the limit is enforced once the call is returned, rolling it back if +/// exhausted. #[derive(RuntimeDebugNoBound, Clone)] struct Charge { deposit_account: DepositAccount, @@ -255,16 +270,24 @@ impl RawMeter where T: Config, E: Ext, - S: State, + S: State + Default + Debug, { - /// Create a new child that has its `limit` set to whatever is remaining of it. + /// Create a new child that has its `limit`. + /// Passing `0` as the limit is interpreted as to take whatever is remaining from its parent. /// /// This is called whenever a new subcall is initiated in order to track the storage /// usage for this sub call separately. This is necessary because we want to exchange balance /// with the current contract we are interacting with. - pub fn nested(&self) -> RawMeter { + pub fn nested(&self, limit: BalanceOf) -> RawMeter { debug_assert!(self.is_alive()); - RawMeter { limit: self.available(), ..Default::default() } + // If a special limit is specified higher than it is available, + // we want to enforce the lesser limit to the nested meter, to fail in the sub-call. + let limit = self.available().min(limit); + if limit.is_zero() { + RawMeter { limit: self.available(), ..Default::default() } + } else { + RawMeter { limit, nested: Nested::OwnLimit, ..Default::default() } + } } /// Absorb a child that was spawned to handle a sub call. @@ -397,7 +420,7 @@ where self.total_deposit = deposit.clone(); info.storage_base_deposit = deposit.charge_or_zero(); - // Usually, deposit charges are deferred to be able to coalesce them with refunds. + // Normally, deposit charges are deferred to be able to coalesce them with refunds. // However, we need to charge immediately so that the account is created before // charges possibly below the ed are collected and fail. E::charge( @@ -429,8 +452,10 @@ where /// /// # Note /// - /// We only need to call this **once** for every call stack and not for every cross contract - /// call. Hence this is only called when the last call frame returns. + /// We normally need to call this **once** for every call stack and not for every cross contract + /// call. However, if a dedicated limit is specified for a sub-call, this needs to be called + /// once the sub-call has returned. For this, the [`Self::enforce_subcall_limit`] wrapper is + /// used. pub fn enforce_limit( &mut self, info: Option<&mut ContractInfo>, @@ -448,6 +473,18 @@ where } Ok(()) } + + /// This is a wrapper around [`Self::enforce_limit`] to use on the exit from a sub-call to + /// enforce its special limit if needed. + pub fn enforce_subcall_limit( + &mut self, + info: Option<&mut ContractInfo>, + ) -> Result<(), DispatchError> { + match self.nested { + Nested::OwnLimit => self.enforce_limit(info), + Nested::DerivedLimit => Ok(()), + } + } } impl Ext for ReservingExt { @@ -462,7 +499,8 @@ impl Ext for ReservingExt { let max = T::Currency::reducible_balance(origin, Protect, Polite) .saturating_sub(min_leftover) .saturating_sub(Pallet::::min_balance()); - let limit = limit.unwrap_or(max); + let default = max.min(T::DefaultDepositLimit::get()); + let limit = limit.unwrap_or(default); ensure!( limit <= max && matches!(T::Currency::can_withdraw(origin, limit), WithdrawConsequence::Success), @@ -673,7 +711,7 @@ mod tests { assert_eq!(meter.available(), 1_000); // an empty charge does not create a `Charge` entry - let mut nested0 = meter.nested(); + let mut nested0 = meter.nested(BalanceOf::::zero()); nested0.charge(&Default::default()); meter.absorb(nested0, DepositAccount(BOB), None); @@ -695,7 +733,7 @@ mod tests { let mut nested0_info = new_info(StorageInfo { bytes: 100, items: 5, bytes_deposit: 100, items_deposit: 10 }); - let mut nested0 = meter.nested(); + let mut nested0 = meter.nested(BalanceOf::::zero()); nested0.charge(&Diff { bytes_added: 108, bytes_removed: 5, @@ -706,13 +744,13 @@ mod tests { let mut nested1_info = new_info(StorageInfo { bytes: 100, items: 10, bytes_deposit: 100, items_deposit: 20 }); - let mut nested1 = nested0.nested(); + let mut nested1 = nested0.nested(BalanceOf::::zero()); nested1.charge(&Diff { items_removed: 5, ..Default::default() }); nested0.absorb(nested1, DepositAccount(CHARLIE), Some(&mut nested1_info)); let mut nested2_info = new_info(StorageInfo { bytes: 100, items: 7, bytes_deposit: 100, items_deposit: 20 }); - let mut nested2 = nested0.nested(); + let mut nested2 = nested0.nested(BalanceOf::::zero()); nested2.charge(&Diff { items_removed: 7, ..Default::default() }); nested0.absorb(nested2, DepositAccount(CHARLIE), Some(&mut nested2_info)); @@ -760,7 +798,7 @@ mod tests { let mut meter = TestMeter::new(&ALICE, Some(1_000), 0).unwrap(); assert_eq!(meter.available(), 1_000); - let mut nested0 = meter.nested(); + let mut nested0 = meter.nested(BalanceOf::::zero()); nested0.charge(&Diff { bytes_added: 5, bytes_removed: 1, @@ -771,7 +809,7 @@ mod tests { let mut nested1_info = new_info(StorageInfo { bytes: 100, items: 10, bytes_deposit: 100, items_deposit: 20 }); - let mut nested1 = nested0.nested(); + let mut nested1 = nested0.nested(BalanceOf::::zero()); nested1.charge(&Diff { items_removed: 5, ..Default::default() }); nested1.charge(&Diff { bytes_added: 20, ..Default::default() }); nested1.terminate(&nested1_info); diff --git a/frame/contracts/src/tests.rs b/frame/contracts/src/tests.rs index 4e6c468f1..7b2bb0442 100644 --- a/frame/contracts/src/tests.rs +++ b/frame/contracts/src/tests.rs @@ -16,6 +16,7 @@ // limitations under the License. use self::test_utils::hash; +use crate as pallet_contracts; use crate::{ chain_extension::{ ChainExtension, Environment, Ext, InitState, RegisteredChainExtension, @@ -44,6 +45,7 @@ use frame_support::{ }; use frame_system::{EventRecord, Phase}; use pretty_assertions::{assert_eq, assert_ne}; +use sp_core::ByteArray; use sp_io::hashing::blake2_256; use sp_keystore::{testing::MemoryKeystore, KeystoreExt}; use sp_runtime::{ @@ -53,8 +55,6 @@ use sp_runtime::{ }; use std::ops::Deref; -use crate as pallet_contracts; - type UncheckedExtrinsic = frame_system::mocking::MockUncheckedExtrinsic; type Block = frame_system::mocking::MockBlock; @@ -345,6 +345,8 @@ parameter_types! { }; pub static DepositPerByte: BalanceOf = 1; pub const DepositPerItem: BalanceOf = 2; + // We need this one set high enough for running benchmarks. + pub static DefaultDepositLimit: BalanceOf = 10_000_000; } impl Convert> for Test { @@ -402,6 +404,7 @@ impl Config for Test { type Schedule = MySchedule; type DepositPerByte = DepositPerByte; type DepositPerItem = DepositPerItem; + type DefaultDepositLimit = DefaultDepositLimit; type AddressGenerator = DefaultAddressGenerator; type MaxCodeLen = ConstU32<{ 123 * 1024 }>; type MaxStorageKeyLen = ConstU32<128>; @@ -1110,7 +1113,7 @@ fn cannot_self_destruct_through_draning() { #[test] fn cannot_self_destruct_through_storage_refund_after_price_change() { - let (wasm, _code_hash) = compile_module::("store").unwrap(); + let (wasm, _code_hash) = compile_module::("store_call").unwrap(); ExtBuilder::default().existential_deposit(200).build().execute_with(|| { let _ = Balances::deposit_creating(&ALICE, 1_000_000); let min_balance = ::Currency::minimum_balance(); @@ -2628,7 +2631,6 @@ fn gas_estimation_nested_call_fixed_limit() { ExtBuilder::default().existential_deposit(50).build().execute_with(|| { let min_balance = ::Currency::minimum_balance(); let _ = Balances::deposit_creating(&ALICE, 1000 * min_balance); - let _ = Balances::deposit_creating(&CHARLIE, 1000 * min_balance); let addr_caller = Contracts::bare_instantiate( ALICE, @@ -2662,6 +2664,7 @@ fn gas_estimation_nested_call_fixed_limit() { .iter() .cloned() .chain((GAS_LIMIT / 5).ref_time().to_le_bytes()) + .chain((GAS_LIMIT / 5).proof_size().to_le_bytes()) .collect(); // Call in order to determine the gas that is required for this call @@ -2678,22 +2681,36 @@ fn gas_estimation_nested_call_fixed_limit() { assert_ok!(&result.result); // We have a subcall with a fixed gas limit. This constitutes precharging. - assert!(result.gas_required.ref_time() > result.gas_consumed.ref_time()); + assert!(result.gas_required.all_gt(result.gas_consumed)); // Make the same call using the estimated gas. Should succeed. assert_ok!( Contracts::bare_call( ALICE, - addr_caller, + addr_caller.clone(), 0, result.gas_required, Some(result.storage_deposit.charge_or_zero()), - input, + input.clone(), false, Determinism::Enforced, ) .result ); + + // Make the same call using proof_size a but less than estimated. Should fail with OutOfGas. + let result = Contracts::bare_call( + ALICE, + addr_caller, + 0, + result.gas_required.sub_proof_size(1), + Some(result.storage_deposit.charge_or_zero()), + input, + false, + Determinism::Enforced, + ) + .result; + assert_err!(result, >::OutOfGas); }); } @@ -2774,7 +2791,7 @@ fn gas_estimation_call_runtime() { } #[test] -fn gas_call_runtime_reentrancy_guarded() { +fn call_runtime_reentrancy_guarded() { let (caller_code, _caller_hash) = compile_module::("call_runtime").unwrap(); let (callee_code, _callee_hash) = compile_module::("dummy").unwrap(); ExtBuilder::default().existential_deposit(50).build().execute_with(|| { @@ -3968,7 +3985,7 @@ fn set_code_hash() { #[test] fn storage_deposit_limit_is_enforced() { - let (wasm, _code_hash) = compile_module::("store").unwrap(); + let (wasm, _code_hash) = compile_module::("store_call").unwrap(); ExtBuilder::default().existential_deposit(200).build().execute_with(|| { let _ = Balances::deposit_creating(&ALICE, 1_000_000); let min_balance = ::Currency::minimum_balance(); @@ -3992,15 +4009,47 @@ fn storage_deposit_limit_is_enforced() { assert_eq!(get_contract(&addr).total_deposit(), min_balance); assert_eq!(::Currency::total_balance(&addr), min_balance); - // Create 100 bytes of storage with a price of per byte + // Create 1 byte of storage with a price of per byte, + // setting insufficient deposit limit, as it requires 3 Balance: + // 2 for the item added + 1 for the new storage item. assert_err_ignore_postinfo!( Contracts::call( RuntimeOrigin::signed(ALICE), addr.clone(), 0, GAS_LIMIT, - Some(codec::Compact(1)), - 100u32.to_le_bytes().to_vec() + Some(codec::Compact(2)), + 1u32.to_le_bytes().to_vec() + ), + >::StorageDepositLimitExhausted, + ); + + // To check that deposit limit fallbacks to DefaultDepositLimit, + // we customize it here. + DEFAULT_DEPOSIT_LIMIT.with(|c| *c.borrow_mut() = 3); + + // Create 1 byte of storage, should cost 3 Balance: + // 2 for the item added + 1 for the new storage item. + // Should pass as it fallbacks to DefaultDepositLimit. + assert_ok!(Contracts::call( + RuntimeOrigin::signed(ALICE), + addr.clone(), + 0, + GAS_LIMIT, + None, + 1u32.to_le_bytes().to_vec() + )); + + // Use 4 more bytes of the storage for the same item, which requires 4 Balance. + // Should fail as DefaultDepositLimit is 3 and hence isn't enough. + assert_err_ignore_postinfo!( + Contracts::call( + RuntimeOrigin::signed(ALICE), + addr.clone(), + 0, + GAS_LIMIT, + None, + 5u32.to_le_bytes().to_vec() ), >::StorageDepositLimitExhausted, ); @@ -4008,10 +4057,10 @@ fn storage_deposit_limit_is_enforced() { } #[test] -fn storage_deposit_limit_is_enforced_late() { +fn deposit_limit_in_nested_calls() { let (wasm_caller, _code_hash_caller) = compile_module::("create_storage_and_call").unwrap(); - let (wasm_callee, _code_hash_callee) = compile_module::("store").unwrap(); + let (wasm_callee, _code_hash_callee) = compile_module::("store_call").unwrap(); ExtBuilder::default().existential_deposit(200).build().execute_with(|| { let _ = Balances::deposit_creating(&ALICE, 1_000_000); @@ -4054,27 +4103,28 @@ fn storage_deposit_limit_is_enforced_late() { 100u32.to_le_bytes().to_vec() )); - // We do not remove any storage but require 14 bytes of storage for the new - // storage created in the immediate contract. + // We do not remove any storage but add a storage item of 12 bytes in the caller + // contract. This would cost 12 + 2 = 14 Balance. + // The nested call doesn't get a special limit, which is set by passing 0 to it. + // This should fail as the specified parent's limit is less than the cost: 13 < + // 14. assert_err_ignore_postinfo!( Contracts::call( RuntimeOrigin::signed(ALICE), addr_caller.clone(), 0, GAS_LIMIT, - Some(codec::Compact(5)), - 100u32 - .to_le_bytes() - .as_ref() - .iter() - .chain(<_ as AsRef<[u8]>>::as_ref(&addr_callee)) - .cloned() - .collect(), + Some(codec::Compact(13)), + (100u32, &addr_callee, 0u64).encode(), ), >::StorageDepositLimitExhausted, ); - - // Allow for the additional 14 bytes but demand an additional byte in the callee contract. + // Now we specify the parent's limit high enough to cover the caller's storage additions. + // However, we use a single byte more in the callee, hence the storage deposit should be 15 + // Balance. + // The nested call doesn't get a special limit, which is set by passing 0 to it. + // This should fail as the specified parent's limit is less than the cost: 14 + // < 15. assert_err_ignore_postinfo!( Contracts::call( RuntimeOrigin::signed(ALICE), @@ -4082,19 +4132,31 @@ fn storage_deposit_limit_is_enforced_late() { 0, GAS_LIMIT, Some(codec::Compact(14)), - 101u32 - .to_le_bytes() - .as_ref() - .iter() - .chain(<_ as AsRef<[u8]>>::as_ref(&addr_callee)) - .cloned() - .collect(), + (101u32, &addr_callee, 0u64).encode(), ), >::StorageDepositLimitExhausted, ); - // Refund in the callee contract but not enough to cover the 14 balance required by the - // caller. + // Now we specify the parent's limit high enough to cover both the caller's and callee's + // storage additions. However, we set a special deposit limit of 1 Balance for the nested + // call. This should fail as callee adds up 2 bytes to the storage, meaning that the nested + // call should have a deposit limit of at least 2 Balance. The sub-call should be rolled + // back, which is covered by the next test case. + assert_err_ignore_postinfo!( + Contracts::call( + RuntimeOrigin::signed(ALICE), + addr_caller.clone(), + 0, + GAS_LIMIT, + Some(codec::Compact(16)), + (102u32, &addr_callee, 1u64).encode(), + ), + >::StorageDepositLimitExhausted, + ); + + // Refund in the callee contract but not enough to cover the 14 Balance required by the + // caller. Note that if previous sub-call wouldn't roll back, this call would pass making + // the test case fail. We don't set a special limit for the nested call here. assert_err_ignore_postinfo!( Contracts::call( RuntimeOrigin::signed(ALICE), @@ -4102,59 +4164,198 @@ fn storage_deposit_limit_is_enforced_late() { 0, GAS_LIMIT, Some(codec::Compact(0)), - 87u32 - .to_le_bytes() - .as_ref() - .iter() - .chain(<_ as AsRef<[u8]>>::as_ref(&addr_callee)) - .cloned() - .collect(), + (87u32, &addr_callee, 0u64).encode(), ), >::StorageDepositLimitExhausted, ); let _ = Balances::make_free_balance_be(&ALICE, 1_000); - // Send more than the sender has balance. + // Require more than the sender's balance. + // We don't set a special limit for the nested call. assert_err_ignore_postinfo!( Contracts::call( RuntimeOrigin::signed(ALICE), addr_caller.clone(), 0, GAS_LIMIT, - Some(codec::Compact(50)), - 1_200u32 - .to_le_bytes() - .as_ref() - .iter() - .chain(<_ as AsRef<[u8]>>::as_ref(&addr_callee)) - .cloned() - .collect(), + None, + (1200u32, &addr_callee, 1u64).encode(), ), >::StorageDepositLimitExhausted, ); - // Same as above but allow for the additional balance. + // Same as above but allow for the additional deposit of 1 Balance in parent. + // We set the special deposit limit of 1 Balance for the nested call, which isn't + // enforced as callee frees up storage. This should pass. assert_ok!(Contracts::call( RuntimeOrigin::signed(ALICE), addr_caller.clone(), 0, GAS_LIMIT, Some(codec::Compact(1)), - 87u32 - .to_le_bytes() - .as_ref() - .iter() - .chain(<_ as AsRef<[u8]>>::as_ref(&addr_callee)) - .cloned() - .collect(), + (87u32, &addr_callee, 1u64).encode(), )); }); } +#[test] +fn deposit_limit_in_nested_instantiate() { + let (wasm_caller, _code_hash_caller) = + compile_module::("create_storage_and_instantiate").unwrap(); + let (wasm_callee, code_hash_callee) = compile_module::("store_deploy").unwrap(); + const ED: u64 = 5; + ExtBuilder::default().existential_deposit(ED).build().execute_with(|| { + let _ = Balances::deposit_creating(&ALICE, 1_000_000); + let _ = Balances::deposit_creating(&BOB, 1_000_000); + // Create caller contract + let addr_caller = Contracts::bare_instantiate( + ALICE, + 10_000u64, // this balance is later passed to the deployed contract + GAS_LIMIT, + None, + Code::Upload(wasm_caller), + vec![], + vec![], + false, + ) + .result + .unwrap() + .account_id; + // Deploy a contract to get its occupied storage size + let addr = Contracts::bare_instantiate( + ALICE, + 0, + GAS_LIMIT, + None, + Code::Upload(wasm_callee), + vec![0, 0, 0, 0], + vec![], + false, + ) + .result + .unwrap() + .account_id; + + let callee_info_len = ContractInfoOf::::get(&addr).unwrap().encoded_size() as u64; + + // We don't set a special deposit limit for the nested instantiation. + // + // The deposit limit set for the parent is insufficient for the instantiation, which + // requires: + // - callee_info_len + 2 for storing the new contract info, + // - ED for deployed contract account, + // - 2 for the storage item of 0 bytes being created in the callee constructor + // or (callee_info_len + 2 + ED + 2) Balance in total. + // + // Provided the limit is set to be 1 Balance less, + // this call should fail on the return from the caller contract. + assert_err_ignore_postinfo!( + Contracts::call( + RuntimeOrigin::signed(BOB), + addr_caller.clone(), + 0, + GAS_LIMIT, + Some(codec::Compact(callee_info_len + 2 + ED + 1)), + (0u32, &code_hash_callee, 0u64).encode(), + ), + >::StorageDepositLimitExhausted, + ); + // The charges made on instantiation should be rolled back. + assert_eq!(::Currency::free_balance(&BOB), 1_000_000); + + // Now we give enough limit for the instantiation itself, but require for 1 more storage + // byte in the constructor. Hence +1 Balance to the limit is needed. This should fail on the + // return from constructor. + assert_err_ignore_postinfo!( + Contracts::call( + RuntimeOrigin::signed(BOB), + addr_caller.clone(), + 0, + GAS_LIMIT, + Some(codec::Compact(callee_info_len + 2 + ED + 2)), + (1u32, &code_hash_callee, 0u64).encode(), + ), + >::StorageDepositLimitExhausted, + ); + // The charges made on the instantiation should be rolled back. + assert_eq!(::Currency::free_balance(&BOB), 1_000_000); + + // Now we set enough limit in parent call, but an insufficient limit for child instantiate. + // This should fail during the charging for the instantiation in + // `RawMeter::charge_instantiate()` + assert_err_ignore_postinfo!( + Contracts::call( + RuntimeOrigin::signed(BOB), + addr_caller.clone(), + 0, + GAS_LIMIT, + Some(codec::Compact(callee_info_len + 2 + ED + 2)), + (0u32, &code_hash_callee, callee_info_len + 2 + ED + 1).encode(), + ), + >::StorageDepositLimitExhausted, + ); + // The charges made on the instantiation should be rolled back. + assert_eq!(::Currency::free_balance(&BOB), 1_000_000); + + // Same as above but requires for single added storage + // item of 1 byte to be covered by the limit, which implies 3 more Balance. + // Now we set enough limit for the parent call, but insufficient limit for child + // instantiate. This should fail right after the constructor execution. + assert_err_ignore_postinfo!( + Contracts::call( + RuntimeOrigin::signed(BOB), + addr_caller.clone(), + 0, + GAS_LIMIT, + Some(codec::Compact(callee_info_len + 2 + ED + 3)), // enough parent limit + (1u32, &code_hash_callee, callee_info_len + 2 + ED + 2).encode(), + ), + >::StorageDepositLimitExhausted, + ); + // The charges made on the instantiation should be rolled back. + assert_eq!(::Currency::free_balance(&BOB), 1_000_000); + + // Set enough deposit limit for the child instantiate. This should succeed. + let result = Contracts::bare_call( + BOB, + addr_caller.clone(), + 0, + GAS_LIMIT, + Some(codec::Compact(callee_info_len + 2 + ED + 4).into()), + (1u32, &code_hash_callee, callee_info_len + 2 + ED + 3).encode(), + false, + Determinism::Enforced, + ); + + let returned = result.result.unwrap(); + // All balance of the caller except ED has been transferred to the callee. + // No deposit has been taken from it. + assert_eq!(::Currency::free_balance(&addr_caller), ED); + // Get address of the deployed contract. + let addr_callee = AccountId32::from_slice(&returned.data[0..32]).unwrap(); + // 10_000 should be sent to callee from the caller contract, plus ED to be sent from the + // origin. + assert_eq!(::Currency::free_balance(&addr_callee), 10_000 + ED); + // The origin should be charged with: + // - callee instantiation deposit = (callee_info_len + 2) + // - callee account ED + // - for writing an item of 1 byte to storage = 3 Balance + // + // Still, the latter is to be charged at the end of the call stack, hence + // only (callee_info_len + 2 + ED) is charged so far + assert_eq!( + ::Currency::free_balance(&BOB), + 1_000_000 - (callee_info_len + 2 + ED) + ); + // Check that deposit due to be charged still includes these 3 Balance + assert_eq!(result.storage_deposit.charge_or_zero(), (callee_info_len + 2 + ED + 3),) + }); +} + #[test] fn deposit_limit_honors_liquidity_restrictions() { - let (wasm, _code_hash) = compile_module::("store").unwrap(); + let (wasm, _code_hash) = compile_module::("store_call").unwrap(); ExtBuilder::default().existential_deposit(200).build().execute_with(|| { let _ = Balances::deposit_creating(&ALICE, 1_000_000); let _ = Balances::deposit_creating(&BOB, 1_000); @@ -4198,7 +4399,7 @@ fn deposit_limit_honors_liquidity_restrictions() { #[test] fn deposit_limit_honors_existential_deposit() { - let (wasm, _code_hash) = compile_module::("store").unwrap(); + let (wasm, _code_hash) = compile_module::("store_call").unwrap(); ExtBuilder::default().existential_deposit(200).build().execute_with(|| { let _ = Balances::deposit_creating(&ALICE, 1_000_000); let _ = Balances::deposit_creating(&BOB, 1_000); @@ -4241,7 +4442,7 @@ fn deposit_limit_honors_existential_deposit() { #[test] fn deposit_limit_honors_min_leftover() { - let (wasm, _code_hash) = compile_module::("store").unwrap(); + let (wasm, _code_hash) = compile_module::("store_call").unwrap(); ExtBuilder::default().existential_deposit(200).build().execute_with(|| { let _ = Balances::deposit_creating(&ALICE, 1_000_000); let _ = Balances::deposit_creating(&BOB, 1_000); @@ -4266,7 +4467,7 @@ fn deposit_limit_honors_min_leftover() { assert_eq!(get_contract(&addr).total_deposit(), min_balance); assert_eq!(::Currency::total_balance(&addr), min_balance); - // check that the minumum leftover (value send) is considered + // check that the minimum leftover (value send) is considered assert_err_ignore_postinfo!( Contracts::call( RuntimeOrigin::signed(BOB), diff --git a/frame/contracts/src/wasm/mod.rs b/frame/contracts/src/wasm/mod.rs index 4723f0c83..5be28a301 100644 --- a/frame/contracts/src/wasm/mod.rs +++ b/frame/contracts/src/wasm/mod.rs @@ -370,7 +370,7 @@ mod tests { gas::GasMeter, storage::WriteOutcome, tests::{RuntimeCall, Test, ALICE, BOB}, - BalanceOf, CodeHash, Error, OldWeight, Pallet as Contracts, + BalanceOf, CodeHash, Error, Pallet as Contracts, }; use assert_matches::assert_matches; use frame_support::{ @@ -470,6 +470,7 @@ mod tests { fn call( &mut self, _gas_limit: Weight, + _deposit_limit: BalanceOf, to: AccountIdOf, value: u64, data: Vec, @@ -489,6 +490,7 @@ mod tests { fn instantiate( &mut self, gas_limit: Weight, + _deposit_limit: BalanceOf, code_hash: CodeHash, value: u64, data: Vec, @@ -587,7 +589,11 @@ mod tests { 16_384 } fn get_weight_price(&self, weight: Weight) -> BalanceOf { - BalanceOf::::from(1312_u32).saturating_mul(weight.ref_time().into()) + BalanceOf::::from(1312_u32) + .saturating_mul(weight.ref_time().into()) + .saturating_add( + BalanceOf::::from(103_u32).saturating_mul(weight.proof_size()), + ) } fn schedule(&self) -> &Schedule { &self.schedule @@ -1589,7 +1595,7 @@ mod tests { const CODE_GAS_PRICE: &str = r#" (module - (import "seal0" "seal_weight_to_fee" (func $seal_weight_to_fee (param i64 i32 i32))) + (import "seal1" "weight_to_fee" (func $seal_weight_to_fee (param i64 i64 i32 i32))) (import "env" "memory" (memory 1 1)) ;; size of our buffer is 32 bytes @@ -1606,7 +1612,7 @@ mod tests { (func (export "call") ;; This stores the gas price in the buffer - (call $seal_weight_to_fee (i64.const 2) (i32.const 0) (i32.const 32)) + (call $seal_weight_to_fee (i64.const 2) (i64.const 1) (i32.const 0) (i32.const 32)) ;; assert len == 8 (call $assert @@ -1616,11 +1622,11 @@ mod tests { ) ) - ;; assert that contents of the buffer is equal to the i64 value of 2 * 1312. + ;; assert that contents of the buffer is equal to the i64 value of 2 * 1312 + 103 = 2727. (call $assert (i64.eq (i64.load (i32.const 0)) - (i64.const 2624) + (i64.const 2727) ) ) ) @@ -1635,12 +1641,12 @@ mod tests { const CODE_GAS_LEFT: &str = r#" (module - (import "seal0" "seal_gas_left" (func $seal_gas_left (param i32 i32))) + (import "seal1" "gas_left" (func $seal_gas_left (param i32 i32))) (import "seal0" "seal_return" (func $seal_return (param i32 i32 i32))) (import "env" "memory" (memory 1 1)) - ;; size of our buffer is 32 bytes - (data (i32.const 32) "\20") + ;; Make output buffer size 20 bytes + (data (i32.const 20) "\14") (func $assert (param i32) (block $ok @@ -1652,19 +1658,19 @@ mod tests { ) (func (export "call") - ;; This stores the gas left in the buffer - (call $seal_gas_left (i32.const 0) (i32.const 32)) + ;; This stores the weight left to the buffer + (call $seal_gas_left (i32.const 0) (i32.const 20)) - ;; assert len == 8 + ;; Assert len <= 16 (max encoded Weight len) (call $assert - (i32.eq - (i32.load (i32.const 32)) - (i32.const 8) + (i32.le_u + (i32.load (i32.const 20)) + (i32.const 16) ) ) - ;; return gas left - (call $seal_return (i32.const 0) (i32.const 0) (i32.const 8)) + ;; Return weight left and its encoded value len + (call $seal_return (i32.const 0) (i32.const 0) (i32.load (i32.const 20))) (unreachable) ) @@ -1679,11 +1685,11 @@ mod tests { let output = execute(CODE_GAS_LEFT, vec![], &mut ext).unwrap(); - let gas_left = OldWeight::decode(&mut &*output.data).unwrap(); + let weight_left = Weight::decode(&mut &*output.data).unwrap(); let actual_left = ext.gas_meter.gas_left(); - // TODO: account for proof size weight - assert!(gas_left < gas_limit.ref_time(), "gas_left must be less than initial"); - assert!(gas_left > actual_left.ref_time(), "gas_left must be greater than final"); + + assert!(weight_left.all_lt(gas_limit), "gas_left must be less than initial"); + assert!(weight_left.all_gt(actual_left), "gas_left must be greater than final"); } /// Test that [`frame_support::weights::OldWeight`] en/decodes the same as our diff --git a/frame/contracts/src/wasm/runtime.rs b/frame/contracts/src/wasm/runtime.rs index bfb991ae5..92ae2e3e6 100644 --- a/frame/contracts/src/wasm/runtime.rs +++ b/frame/contracts/src/wasm/runtime.rs @@ -433,7 +433,7 @@ bitflags! { /// The kind of call that should be performed. enum CallType { /// Execute another instantiated contract - Call { callee_ptr: u32, value_ptr: u32, gas: u64 }, + Call { callee_ptr: u32, value_ptr: u32, deposit_ptr: u32, weight: Weight }, /// Execute deployed code in the context (storage, account ID, value) of the caller contract DelegateCall { code_hash_ptr: u32 }, } @@ -873,16 +873,22 @@ impl<'a, E: Ext + 'a> Runtime<'a, E> { }; let call_outcome = match call_type { - CallType::Call { callee_ptr, value_ptr, gas } => { + CallType::Call { callee_ptr, value_ptr, deposit_ptr, weight } => { let callee: <::T as frame_system::Config>::AccountId = self.read_sandbox_memory_as(memory, callee_ptr)?; + let deposit_limit: BalanceOf<::T> = if deposit_ptr == SENTINEL { + BalanceOf::<::T>::zero() + } else { + self.read_sandbox_memory_as(memory, deposit_ptr)? + }; let value: BalanceOf<::T> = self.read_sandbox_memory_as(memory, value_ptr)?; if value > 0u32.into() { self.charge_gas(RuntimeCosts::CallSurchargeTransfer)?; } self.ext.call( - Weight::from_parts(gas, 0), + weight, + deposit_limit, callee, value, input_data, @@ -926,7 +932,8 @@ impl<'a, E: Ext + 'a> Runtime<'a, E> { &mut self, memory: &mut [u8], code_hash_ptr: u32, - gas: u64, + weight: Weight, + deposit_ptr: u32, value_ptr: u32, input_data_ptr: u32, input_data_len: u32, @@ -937,8 +944,12 @@ impl<'a, E: Ext + 'a> Runtime<'a, E> { salt_ptr: u32, salt_len: u32, ) -> Result { - let gas = Weight::from_parts(gas, 0); self.charge_gas(RuntimeCosts::InstantiateBase { input_data_len, salt_len })?; + let deposit_limit: BalanceOf<::T> = if deposit_ptr == SENTINEL { + BalanceOf::<::T>::zero() + } else { + self.read_sandbox_memory_as(memory, deposit_ptr)? + }; let value: BalanceOf<::T> = self.read_sandbox_memory_as(memory, value_ptr)?; if value > 0u32.into() { self.charge_gas(RuntimeCosts::InstantiateSurchargeTransfer)?; @@ -947,7 +958,8 @@ impl<'a, E: Ext + 'a> Runtime<'a, E> { self.read_sandbox_memory_as(memory, code_hash_ptr)?; let input_data = self.read_sandbox_memory(memory, input_data_ptr, input_data_len)?; let salt = self.read_sandbox_memory(memory, salt_ptr, salt_len)?; - let instantiate_outcome = self.ext.instantiate(gas, code_hash, value, input_data, &salt); + let instantiate_outcome = + self.ext.instantiate(weight, deposit_limit, code_hash, value, input_data, &salt); if let Ok((address, output)) = &instantiate_outcome { if !output.flags.contains(ReturnFlags::REVERT) { self.write_sandbox_output( @@ -993,6 +1005,7 @@ pub mod env { /// /// NOTE: This is a implementation defined call and is NOT a part of the public API. /// This call is supposed to be called only by instrumentation injected code. + /// It deals only with the *ref_time* Weight. /// /// - `amount`: How much gas is used. fn gas(ctx: _, _memory: _, amount: u64) -> Result<(), TrapReason> { @@ -1002,8 +1015,9 @@ pub mod env { /// Set the value at the given key in the contract storage. /// - /// Equivalent to the newer version [`super::seal1::Api::set_storage`] with the exception of the - /// return type. Still a valid thing to call when not interested in the return value. + /// Equivalent to the newer [`seal1`][`super::api_doc::Version1::set_storage`] version with the + /// exception of the return type. Still a valid thing to call when not interested in the return + /// value. #[prefixed_alias] fn set_storage( ctx: _, @@ -1076,8 +1090,9 @@ pub mod env { /// Clear the value at the given key in the contract storage. /// - /// Equivalent to the newer version [`super::seal1::Api::clear_storage`] with the exception of - /// the return type. Still a valid thing to call when not interested in the return value. + /// Equivalent to the newer [`seal1`][`super::api_doc::Version1::clear_storage`] version with + /// the exception of the return type. Still a valid thing to call when not interested in the + /// return value. #[prefixed_alias] fn clear_storage(ctx: _, memory: _, key_ptr: u32) -> Result<(), TrapReason> { ctx.clear_storage(memory, KeyType::Fix, key_ptr).map(|_| ()) @@ -1299,7 +1314,47 @@ pub mod env { ctx.call( memory, CallFlags::ALLOW_REENTRY, - CallType::Call { callee_ptr, value_ptr, gas }, + CallType::Call { + callee_ptr, + value_ptr, + deposit_ptr: SENTINEL, + weight: Weight::from_parts(gas, 0), + }, + input_data_ptr, + input_data_len, + output_ptr, + output_len_ptr, + ) + } + + /// Make a call to another contract. + /// + /// Equivalent to the newer [`seal2`][`super::api_doc::Version2::call`] version but works with + /// *ref_time* Weight only. It is recommended to switch to the latest version, once it's + /// stabilized. + #[version(1)] + #[prefixed_alias] + fn call( + ctx: _, + memory: _, + flags: u32, + callee_ptr: u32, + gas: u64, + value_ptr: u32, + input_data_ptr: u32, + input_data_len: u32, + output_ptr: u32, + output_len_ptr: u32, + ) -> Result { + ctx.call( + memory, + CallFlags::from_bits(flags).ok_or(Error::::InvalidCallFlags)?, + CallType::Call { + callee_ptr, + value_ptr, + deposit_ptr: SENTINEL, + weight: Weight::from_parts(gas, 0), + }, input_data_ptr, input_data_len, output_ptr, @@ -1318,7 +1373,12 @@ pub mod env { /// - `flags`: See `crate::wasm::runtime::CallFlags` for a documentation of the supported flags. /// - `callee_ptr`: a pointer to the address of the callee contract. Should be decodable as an /// `T::AccountId`. Traps otherwise. - /// - `gas`: how much gas to devote to the execution. + /// - `ref_time_limit`: how much *ref_time* Weight to devote to the execution. + /// - `proof_size_limit`: how much *proof_size* Weight to devote to the execution. + /// - `deposit_ptr`: a pointer to the buffer with value of the storage deposit limit for the + /// call. Should be decodable as a `T::Balance`. Traps otherwise. Passing `SENTINEL` means + /// setting no specific limit for the call, which implies storage usage up to the limit of the + /// parent call. /// - `value_ptr`: a pointer to the buffer with value, how much value to send. Should be /// decodable as a `T::Balance`. Traps otherwise. /// - `input_data_ptr`: a pointer to a buffer to be used as input data to the callee. @@ -1336,14 +1396,16 @@ pub mod env { /// - `ReturnCode::CalleeTrapped` /// - `ReturnCode::TransferFailed` /// - `ReturnCode::NotCallable` - #[version(1)] - #[prefixed_alias] + #[version(2)] + #[unstable] fn call( ctx: _, memory: _, flags: u32, callee_ptr: u32, - gas: u64, + ref_time_limit: u64, + proof_size_limit: u64, + deposit_ptr: u32, value_ptr: u32, input_data_ptr: u32, input_data_len: u32, @@ -1353,7 +1415,12 @@ pub mod env { ctx.call( memory, CallFlags::from_bits(flags).ok_or(Error::::InvalidCallFlags)?, - CallType::Call { callee_ptr, value_ptr, gas }, + CallType::Call { + callee_ptr, + value_ptr, + deposit_ptr, + weight: Weight::from_parts(ref_time_limit, proof_size_limit), + }, input_data_ptr, input_data_len, output_ptr, @@ -1417,8 +1484,7 @@ pub mod env { /// # Note /// /// The values `_code_hash_len` and `_value_len` are ignored because the encoded sizes - /// of those types are fixed through - /// [`codec::MaxEncodedLen`]. The fields exist + /// of those types are fixed through [`codec::MaxEncodedLen`]. The fields exist /// for backwards compatibility. Consider switching to the newest version of this function. #[prefixed_alias] fn instantiate( @@ -1441,7 +1507,47 @@ pub mod env { ctx.instantiate( memory, code_hash_ptr, - gas, + Weight::from_parts(gas, 0), + SENTINEL, + value_ptr, + input_data_ptr, + input_data_len, + address_ptr, + address_len_ptr, + output_ptr, + output_len_ptr, + salt_ptr, + salt_len, + ) + } + + /// Instantiate a contract with the specified code hash. + /// + /// Equivalent to the newer [`seal2`][`super::api_doc::Version2::instantiate`] version but works + /// with *ref_time* Weight only. It is recommended to switch to the latest version, once it's + /// stabilized. + #[version(1)] + #[prefixed_alias] + fn instantiate( + ctx: _, + memory: _, + code_hash_ptr: u32, + gas: u64, + value_ptr: u32, + input_data_ptr: u32, + input_data_len: u32, + address_ptr: u32, + address_len_ptr: u32, + output_ptr: u32, + output_len_ptr: u32, + salt_ptr: u32, + salt_len: u32, + ) -> Result { + ctx.instantiate( + memory, + code_hash_ptr, + Weight::from_parts(gas, 0), + SENTINEL, value_ptr, input_data_ptr, input_data_len, @@ -1468,15 +1574,21 @@ pub mod env { /// # Parameters /// /// - `code_hash_ptr`: a pointer to the buffer that contains the initializer code. - /// - `gas`: how much gas to devote to the execution of the initializer code. + /// - `ref_time_limit`: how much *ref_time* Weight to devote to the execution. + /// - `proof_size_limit`: how much *proof_size* Weight to devote to the execution. + /// - `deposit_ptr`: a pointer to the buffer with value of the storage deposit limit for + /// instantiation. Should be decodable as a `T::Balance`. Traps otherwise. Passing `SENTINEL` + /// means setting no specific limit for the call, which implies storage usage up to the limit + /// of the parent call. /// - `value_ptr`: a pointer to the buffer with value, how much value to send. Should be /// decodable as a `T::Balance`. Traps otherwise. /// - `input_data_ptr`: a pointer to a buffer to be used as input data to the initializer code. /// - `input_data_len`: length of the input data buffer. - /// - `address_ptr`: a pointer where the new account's address is copied to. - /// - `address_len_ptr`: in-out pointer to where the length of the buffer is read from and the - /// actual length is written to. - /// - `output_ptr`: a pointer where the output buffer is copied to. + /// - `address_ptr`: a pointer where the new account's address is copied to. `SENTINEL` means + /// not to copy. + /// - `address_len_ptr`: pointer to where put the length of the address. + /// - `output_ptr`: a pointer where the output buffer is copied to. `SENTINEL` means not to + /// copy. /// - `output_len_ptr`: in-out pointer to where the length of the buffer is read from and the /// actual length is written to. /// - `salt_ptr`: Pointer to raw bytes used for address derivation. See `fn contract_address`. @@ -1494,13 +1606,15 @@ pub mod env { /// - `ReturnCode::CalleeTrapped` /// - `ReturnCode::TransferFailed` /// - `ReturnCode::CodeNotFound` - #[version(1)] - #[prefixed_alias] + #[version(2)] + #[unstable] fn instantiate( ctx: _, memory: _, code_hash_ptr: u32, - gas: u64, + ref_time_limit: u64, + proof_size_limit: u64, + deposit_ptr: u32, value_ptr: u32, input_data_ptr: u32, input_data_len: u32, @@ -1514,7 +1628,8 @@ pub mod env { ctx.instantiate( memory, code_hash_ptr, - gas, + Weight::from_parts(ref_time_limit, proof_size_limit), + deposit_ptr, value_ptr, input_data_ptr, input_data_len, @@ -1762,49 +1877,99 @@ pub mod env { /// Stores the price for the specified amount of gas into the supplied buffer. /// - /// The value is stored to linear memory at the address pointed to by `out_ptr`. - /// `out_len_ptr` must point to a u32 value that describes the available space at - /// `out_ptr`. This call overwrites it with the size of the value. If the available - /// space at `out_ptr` is less than the size of the value a trap is triggered. + /// Equivalent to the newer [`seal1`][`super::api_doc::Version2::weight_to_fee`] version but + /// works with *ref_time* Weight only. It is recommended to switch to the latest version, once + /// it's stabilized. + #[prefixed_alias] + fn weight_to_fee( + ctx: _, + memory: _, + gas: u64, + out_ptr: u32, + out_len_ptr: u32, + ) -> Result<(), TrapReason> { + let gas = Weight::from_parts(gas, 0); + ctx.charge_gas(RuntimeCosts::WeightToFee)?; + Ok(ctx.write_sandbox_output( + memory, + out_ptr, + out_len_ptr, + &ctx.ext.get_weight_price(gas).encode(), + false, + already_charged, + )?) + } + + /// Stores the price for the specified amount of weight into the supplied buffer. + /// + /// # Parameters + /// + /// - `out_ptr`: pointer to the linear memory where the returning value is written to. If the + /// available space at `out_ptr` is less than the size of the value a trap is triggered. + /// - `out_len_ptr`: in-out pointer into linear memory where the buffer length is read from and + /// the value length is written to. /// /// The data is encoded as `T::Balance`. /// /// # Note /// - /// It is recommended to avoid specifying very small values for `gas` as the prices for a single - /// gas can be smaller than one. - #[prefixed_alias] + /// It is recommended to avoid specifying very small values for `ref_time_limit` and + /// `proof_size_limit` as the prices for a single gas can be smaller than the basic balance + /// unit. + #[version(1)] + #[unstable] fn weight_to_fee( ctx: _, memory: _, - gas: u64, + ref_time_limit: u64, + proof_size_limit: u64, out_ptr: u32, out_len_ptr: u32, ) -> Result<(), TrapReason> { - let gas = Weight::from_parts(gas, 0); + let weight = Weight::from_parts(ref_time_limit, proof_size_limit); ctx.charge_gas(RuntimeCosts::WeightToFee)?; Ok(ctx.write_sandbox_output( memory, out_ptr, out_len_ptr, - &ctx.ext.get_weight_price(gas).encode(), + &ctx.ext.get_weight_price(weight).encode(), false, already_charged, )?) } - /// Stores the amount of gas left into the supplied buffer. + /// Stores the weight left into the supplied buffer. + /// + /// Equivalent to the newer [`seal1`][`super::api_doc::Version2::gas_left`] version but + /// works with *ref_time* Weight only. It is recommended to switch to the latest version, once + /// it's stabilized. + #[prefixed_alias] + fn gas_left(ctx: _, memory: _, out_ptr: u32, out_len_ptr: u32) -> Result<(), TrapReason> { + ctx.charge_gas(RuntimeCosts::GasLeft)?; + let gas_left = &ctx.ext.gas_meter().gas_left().ref_time().encode(); + Ok(ctx.write_sandbox_output( + memory, + out_ptr, + out_len_ptr, + gas_left, + false, + already_charged, + )?) + } + + /// Stores the amount of weight left into the supplied buffer. /// /// The value is stored to linear memory at the address pointed to by `out_ptr`. /// `out_len_ptr` must point to a u32 value that describes the available space at /// `out_ptr`. This call overwrites it with the size of the value. If the available /// space at `out_ptr` is less than the size of the value a trap is triggered. /// - /// The data is encoded as Gas. - #[prefixed_alias] + /// The data is encoded as Weight. + #[version(1)] + #[unstable] fn gas_left(ctx: _, memory: _, out_ptr: u32, out_len_ptr: u32) -> Result<(), TrapReason> { ctx.charge_gas(RuntimeCosts::GasLeft)?; - let gas_left = &ctx.ext.gas_meter().gas_left().ref_time().encode(); + let gas_left = &ctx.ext.gas_meter().gas_left().encode(); Ok(ctx.write_sandbox_output( memory, out_ptr, From 7ddfdfbdfab482866a9909ad4927dc2c9b77f8e2 Mon Sep 17 00:00:00 2001 From: Qinxuan Chen Date: Wed, 26 Apr 2023 19:37:44 +0800 Subject: [PATCH 423/558] sp-core: remove useless bounded module (#13865) --- primitives/core/src/bounded.rs | 28 ---------------------------- 1 file changed, 28 deletions(-) delete mode 100644 primitives/core/src/bounded.rs diff --git a/primitives/core/src/bounded.rs b/primitives/core/src/bounded.rs deleted file mode 100644 index c78f1f85a..000000000 --- a/primitives/core/src/bounded.rs +++ /dev/null @@ -1,28 +0,0 @@ -// This file is part of Substrate. - -// Copyright (C) Parity Technologies (UK) Ltd. -// SPDX-License-Identifier: Apache-2.0 - -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -//! Bounded collection types. - -pub mod bounded_btree_map; -pub mod bounded_btree_set; -pub mod bounded_vec; -pub mod weak_bounded_vec; - -pub use bounded_btree_map::BoundedBTreeMap; -pub use bounded_btree_set::BoundedBTreeSet; -pub use bounded_vec::{BoundedSlice, BoundedVec}; -pub use weak_bounded_vec::WeakBoundedVec; From 154448b77413de5eaed7479f847191df20c46a96 Mon Sep 17 00:00:00 2001 From: Sasha Gryaznov Date: Wed, 26 Apr 2023 16:46:44 +0300 Subject: [PATCH 424/558] fix a test (#14021) --- frame/contracts/src/tests.rs | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/frame/contracts/src/tests.rs b/frame/contracts/src/tests.rs index 7b2bb0442..3c1015bc9 100644 --- a/frame/contracts/src/tests.rs +++ b/frame/contracts/src/tests.rs @@ -4341,12 +4341,9 @@ fn deposit_limit_in_nested_instantiate() { // - callee instantiation deposit = (callee_info_len + 2) // - callee account ED // - for writing an item of 1 byte to storage = 3 Balance - // - // Still, the latter is to be charged at the end of the call stack, hence - // only (callee_info_len + 2 + ED) is charged so far assert_eq!( ::Currency::free_balance(&BOB), - 1_000_000 - (callee_info_len + 2 + ED) + 1_000_000 - (callee_info_len + 2 + ED + 3) ); // Check that deposit due to be charged still includes these 3 Balance assert_eq!(result.storage_deposit.charge_or_zero(), (callee_info_len + 2 + ED + 3),) From 943c520aa78fcfaf3509790009ad062e8d4c6990 Mon Sep 17 00:00:00 2001 From: Gavin Wood Date: Wed, 26 Apr 2023 15:26:07 +0100 Subject: [PATCH 425/558] Various minor fixes (#13945) * Fix: Incorrect implementation of can_reserve check * Fix: Incorrect migration of consumer counting for existing accounts with frozen amounts * Fix: Inconsistent implementation between assets can_deposit and new_account * Fixes * Fixes * Another fix * Update tests.rs * Update fungible_tests.rs * Use `can_accrue_consumers` in the body of `can_inc_consumer` --------- Co-authored-by: Keith Yeung Co-authored-by: Oliver Tale-Yazdi Co-authored-by: parity-processbot <> --- frame/assets/src/functions.rs | 2 +- frame/assets/src/tests.rs | 51 ++++++++++++++++++---- frame/balances/src/impl_currency.rs | 4 +- frame/balances/src/lib.rs | 2 +- frame/balances/src/tests/currency_tests.rs | 10 +++++ frame/balances/src/tests/fungible_tests.rs | 25 +++++++++++ frame/system/src/lib.rs | 20 +++++++-- 7 files changed, 98 insertions(+), 16 deletions(-) diff --git a/frame/assets/src/functions.rs b/frame/assets/src/functions.rs index af9e269ac..1e10e0066 100644 --- a/frame/assets/src/functions.rs +++ b/frame/assets/src/functions.rs @@ -138,7 +138,7 @@ impl, I: 'static> Pallet { if amount < details.min_balance { return DepositConsequence::BelowMinimum } - if !details.is_sufficient && !frame_system::Pallet::::can_inc_consumer(who) { + if !details.is_sufficient && !frame_system::Pallet::::can_accrue_consumers(who, 2) { return DepositConsequence::CannotCreate } if details.is_sufficient && details.sufficients.checked_add(1).is_none() { diff --git a/frame/assets/src/tests.rs b/frame/assets/src/tests.rs index afd224ad6..b47fb6486 100644 --- a/frame/assets/src/tests.rs +++ b/frame/assets/src/tests.rs @@ -21,7 +21,7 @@ use super::*; use crate::{mock::*, Error}; use frame_support::{ assert_noop, assert_ok, - traits::{fungibles::InspectEnumerable, Currency}, + traits::{fungibles::InspectEnumerable, tokens::Preservation::Protect, Currency}, }; use pallet_balances::Error as BalancesError; use sp_io::storage; @@ -33,6 +33,25 @@ fn asset_ids() -> Vec { s } +#[test] +fn transfer_should_never_burn() { + new_test_ext().execute_with(|| { + assert_ok!(Assets::force_create(RuntimeOrigin::root(), 0, 1, false, 1)); + Balances::make_free_balance_be(&1, 100); + Balances::make_free_balance_be(&2, 100); + + assert_ok!(Assets::mint(RuntimeOrigin::signed(1), 0, 1, 100)); + assert_eq!(Assets::balance(0, 1), 100); + + while System::inc_consumers(&2).is_ok() {} + let _ = System::dec_consumers(&2); + // Exactly one consumer ref remaining. + + let _ = >::transfer(0, &1, &2, 50, Protect); + assert_eq!(Assets::balance(0, 1) + Assets::balance(0, 2), 100); + }); +} + #[test] fn basic_minting_should_work() { new_test_ext().execute_with(|| { @@ -57,10 +76,7 @@ fn minting_too_many_insufficient_assets_fails() { Balances::make_free_balance_be(&1, 100); assert_ok!(Assets::mint(RuntimeOrigin::signed(1), 0, 1, 100)); assert_ok!(Assets::mint(RuntimeOrigin::signed(1), 1, 1, 100)); - assert_noop!( - Assets::mint(RuntimeOrigin::signed(1), 2, 1, 100), - Error::::UnavailableConsumer - ); + assert_noop!(Assets::mint(RuntimeOrigin::signed(1), 2, 1, 100), TokenError::CannotCreate); Balances::make_free_balance_be(&2, 1); assert_ok!(Assets::transfer(RuntimeOrigin::signed(1), 0, 2, 100)); @@ -78,10 +94,7 @@ fn minting_insufficient_asset_with_deposit_should_work_when_consumers_exhausted( Balances::make_free_balance_be(&1, 100); assert_ok!(Assets::mint(RuntimeOrigin::signed(1), 0, 1, 100)); assert_ok!(Assets::mint(RuntimeOrigin::signed(1), 1, 1, 100)); - assert_noop!( - Assets::mint(RuntimeOrigin::signed(1), 2, 1, 100), - Error::::UnavailableConsumer - ); + assert_noop!(Assets::mint(RuntimeOrigin::signed(1), 2, 1, 100), TokenError::CannotCreate); assert_ok!(Assets::touch(RuntimeOrigin::signed(1), 2)); assert_eq!(Balances::reserved_balance(&1), 10); @@ -1322,3 +1335,23 @@ fn asset_create_and_destroy_is_reverted_if_callback_fails() { ); }); } + +#[test] +fn multiple_transfer_alls_work_ok() { + new_test_ext().execute_with(|| { + // Only run PoC when the system pallet is enabled, since the underlying bug is in the + // system pallet it won't work with BalancesAccountStore + // Start with a balance of 100 + Balances::force_set_balance(RuntimeOrigin::root(), 1, 100).unwrap(); + // Emulate a sufficient, in reality this could be reached by transferring a sufficient + // asset to the account + assert_ok!(Assets::force_create(RuntimeOrigin::root(), 0, 1, true, 1)); + assert_ok!(Assets::mint(RuntimeOrigin::signed(1), 0, 1, 100)); + // Spend the same balance multiple times + assert_ok!(Balances::transfer_all(RuntimeOrigin::signed(1), 1337, false)); + assert_ok!(Balances::transfer_all(RuntimeOrigin::signed(1), 1337, false)); + + assert_eq!(Balances::free_balance(&1), 0); + assert_eq!(Balances::free_balance(&1337), 100); + }); +} diff --git a/frame/balances/src/impl_currency.rs b/frame/balances/src/impl_currency.rs index ff8cc71d6..9f764a37b 100644 --- a/frame/balances/src/impl_currency.rs +++ b/frame/balances/src/impl_currency.rs @@ -490,7 +490,9 @@ where return true } Self::account(who).free.checked_sub(&value).map_or(false, |new_balance| { - Self::ensure_can_withdraw(who, value, WithdrawReasons::RESERVE, new_balance).is_ok() + new_balance >= T::ExistentialDeposit::get() && + Self::ensure_can_withdraw(who, value, WithdrawReasons::RESERVE, new_balance) + .is_ok() }) } diff --git a/frame/balances/src/lib.rs b/frame/balances/src/lib.rs index 6835d3c81..c87b01d77 100644 --- a/frame/balances/src/lib.rs +++ b/frame/balances/src/lib.rs @@ -788,7 +788,7 @@ pub mod pallet { return false } a.flags.set_new_logic(); - if !a.reserved.is_zero() || !a.frozen.is_zero() { + if !a.reserved.is_zero() && a.frozen.is_zero() { if system::Pallet::::providers(who) == 0 { // Gah!! We have no provider refs :( // This shouldn't practically happen, but we need a failsafe anyway: let's give diff --git a/frame/balances/src/tests/currency_tests.rs b/frame/balances/src/tests/currency_tests.rs index 034c92f65..e25b122c1 100644 --- a/frame/balances/src/tests/currency_tests.rs +++ b/frame/balances/src/tests/currency_tests.rs @@ -1180,6 +1180,16 @@ fn named_reserve_should_work() { }); } +#[test] +fn reserve_must_succeed_if_can_reserve_does() { + ExtBuilder::default().build_and_execute_with(|| { + let _ = Balances::deposit_creating(&1, 1); + let _ = Balances::deposit_creating(&2, 2); + assert!(Balances::can_reserve(&1, 1) == Balances::reserve(&1, 1).is_ok()); + assert!(Balances::can_reserve(&2, 1) == Balances::reserve(&2, 1).is_ok()); + }); +} + #[test] fn reserved_named_to_yourself_should_work() { ExtBuilder::default().build_and_execute_with(|| { diff --git a/frame/balances/src/tests/fungible_tests.rs b/frame/balances/src/tests/fungible_tests.rs index 185396019..ab2606c53 100644 --- a/frame/balances/src/tests/fungible_tests.rs +++ b/frame/balances/src/tests/fungible_tests.rs @@ -398,6 +398,31 @@ fn unholding_frees_hold_slot() { }); } +#[test] +fn sufficients_work_properly_with_reference_counting() { + ExtBuilder::default() + .existential_deposit(1) + .monied(true) + .build_and_execute_with(|| { + // Only run PoC when the system pallet is enabled, since the underlying bug is in the + // system pallet it won't work with BalancesAccountStore + if UseSystem::get() { + // Start with a balance of 100 + >::set_balance(&1, 100); + // Emulate a sufficient, in reality this could be reached by transferring a + // sufficient asset to the account + System::inc_sufficients(&1); + // Spend the same balance multiple times + assert_ok!(>::transfer(&1, &1337, 100, Expendable)); + assert_eq!(Balances::free_balance(&1), 0); + assert_noop!( + >::transfer(&1, &1337, 100, Expendable), + TokenError::FundsUnavailable + ); + } + }); +} + #[test] fn emit_events_with_changing_freezes() { ExtBuilder::default().build_and_execute_with(|| { diff --git a/frame/system/src/lib.rs b/frame/system/src/lib.rs index 88291c326..6bbebb870 100644 --- a/frame/system/src/lib.rs +++ b/frame/system/src/lib.rs @@ -1222,10 +1222,20 @@ impl Pallet { a.consumers == 0 || a.providers > 1 } - /// True if the account has at least one provider reference. - pub fn can_inc_consumer(who: &T::AccountId) -> bool { + /// True if the account has at least one provider reference and adding `amount` consumer + /// references would not take it above the the maximum. + pub fn can_accrue_consumers(who: &T::AccountId, amount: u32) -> bool { let a = Account::::get(who); - a.providers > 0 && a.consumers < T::MaxConsumers::max_consumers() + match a.consumers.checked_add(amount) { + Some(c) => a.providers > 0 && c <= T::MaxConsumers::max_consumers(), + None => false, + } + } + + /// True if the account has at least one provider reference and fewer consumer references than + /// the maximum. + pub fn can_inc_consumer(who: &T::AccountId) -> bool { + Self::can_accrue_consumers(who, 1) } /// Deposits an event into this block's event record. @@ -1679,8 +1689,10 @@ impl StoredMap for Pallet { let is_default = account.data == T::AccountData::default(); let mut some_data = if is_default { None } else { Some(account.data) }; let result = f(&mut some_data)?; - if Self::providers(k) > 0 { + if Self::providers(k) > 0 || Self::sufficients(k) > 0 { Account::::mutate(k, |a| a.data = some_data.unwrap_or_default()); + } else { + Account::::remove(k) } Ok(result) } From 2e7e628ecdf96d279c4c3d5d1aea0dd55a02012e Mon Sep 17 00:00:00 2001 From: PG Herveou Date: Thu, 27 Apr 2023 08:23:01 +0200 Subject: [PATCH 426/558] contracts Add storage_deposit test (#14003) * contracts Add storage_deposit test * fix comments * PR comments --- frame/contracts/fixtures/call.wat | 39 +++++++++++++++++++++++ frame/contracts/src/tests.rs | 53 +++++++++++++++++++++++++++++++ 2 files changed, 92 insertions(+) create mode 100644 frame/contracts/fixtures/call.wat diff --git a/frame/contracts/fixtures/call.wat b/frame/contracts/fixtures/call.wat new file mode 100644 index 000000000..4558b2c64 --- /dev/null +++ b/frame/contracts/fixtures/call.wat @@ -0,0 +1,39 @@ +;; This calls another contract as passed as its account id. +(module + (import "seal0" "seal_input" (func $seal_input (param i32 i32))) + (import "seal1" "seal_call" (func $seal_call (param i32 i32 i64 i32 i32 i32 i32 i32) (result i32))) + (import "env" "memory" (memory 1 1)) + + (func $assert (param i32) + (block $ok + (br_if $ok + (get_local 0) + ) + (unreachable) + ) + ) + + (func (export "deploy")) + + (func (export "call") + ;; Store length of input buffer. + (i32.store (i32.const 0) (i32.const 512)) + + ;; Copy input at address 4. + (call $seal_input (i32.const 4) (i32.const 0)) + + ;; Call passed contract. + (call $assert (i32.eqz + (call $seal_call + (i32.const 0) ;; No flags + (i32.const 8) ;; Pointer to "callee" address. + (i64.const 0) ;; How much gas to devote for the execution. 0 = all. + (i32.const 512) ;; Pointer to the buffer with value to transfer + (i32.const 4) ;; Pointer to input data buffer address + (i32.const 4) ;; Length of input data buffer + (i32.const 4294967295) ;; u32 max value is the sentinel value: do not copy output + (i32.const 0) ;; Length is ignored in this case + ) + )) + ) +) diff --git a/frame/contracts/src/tests.rs b/frame/contracts/src/tests.rs index 3c1015bc9..dbe61cf33 100644 --- a/frame/contracts/src/tests.rs +++ b/frame/contracts/src/tests.rs @@ -3582,6 +3582,59 @@ fn storage_deposit_works() { }); } +#[test] +fn storage_deposit_callee_works() { + let (wasm_caller, _code_hash_caller) = compile_module::("call").unwrap(); + let (wasm_callee, _code_hash_callee) = compile_module::("store").unwrap(); + const ED: u64 = 200; + ExtBuilder::default().existential_deposit(ED).build().execute_with(|| { + let _ = Balances::deposit_creating(&ALICE, 1_000_000); + + // Create both contracts: Constructors do nothing. + let addr_caller = Contracts::bare_instantiate( + ALICE, + 0, + GAS_LIMIT, + None, + Code::Upload(wasm_caller), + vec![], + vec![], + false, + ) + .result + .unwrap() + .account_id; + let addr_callee = Contracts::bare_instantiate( + ALICE, + 0, + GAS_LIMIT, + None, + Code::Upload(wasm_callee), + vec![], + vec![], + false, + ) + .result + .unwrap() + .account_id; + + assert_ok!(Contracts::call( + RuntimeOrigin::signed(ALICE), + addr_caller, + 0, + GAS_LIMIT, + None, + (100u32, &addr_callee).encode() + )); + + let callee = get_contract(&addr_callee); + let deposit = ED + DepositPerByte::get() * 100 + DepositPerItem::get() * 1; + + assert_eq!(test_utils::get_balance(callee.deposit_account()), deposit); + assert_eq!(callee.total_deposit(), deposit); + }); +} + #[test] fn set_code_extrinsic() { let (wasm, code_hash) = compile_module::("dummy").unwrap(); From dce73feadc8e33983a055e06f56066665c218544 Mon Sep 17 00:00:00 2001 From: Liam Aharon Date: Thu, 27 Apr 2023 18:12:29 +1000 Subject: [PATCH 427/558] remote-externalities: batch insert key/values (#14004) * add batch inserting into remote externalities * use into_iter * remove redundant comment * redundant batch insert * avoid extra vec allocations --- primitives/state-machine/src/testing.rs | 11 +++++ utils/frame/remote-externalities/src/lib.rs | 49 +++++++++------------ 2 files changed, 32 insertions(+), 28 deletions(-) diff --git a/primitives/state-machine/src/testing.rs b/primitives/state-machine/src/testing.rs index 3a0165fe4..1921287a6 100644 --- a/primitives/state-machine/src/testing.rs +++ b/primitives/state-machine/src/testing.rs @@ -132,6 +132,17 @@ where self.offchain_db.clone() } + /// Batch insert key/values into backend + pub fn batch_insert(&mut self, kvs: I) + where + I: IntoIterator, + { + self.backend.insert( + Some((None, kvs.into_iter().map(|(k, v)| (k, Some(v))).collect())), + self.state_version, + ); + } + /// Insert key/value into backend pub fn insert(&mut self, k: StorageKey, v: StorageValue) { self.backend.insert(vec![(None, vec![(k, Some(v))])], self.state_version); diff --git a/utils/frame/remote-externalities/src/lib.rs b/utils/frame/remote-externalities/src/lib.rs index b60e2bc75..ea9b68ce2 100644 --- a/utils/frame/remote-externalities/src/lib.rs +++ b/utils/frame/remote-externalities/src/lib.rs @@ -638,24 +638,20 @@ where let mut processed = 0usize; loop { match rx.next().await.unwrap() { - Message::Batch(kv) => { - for (k, v) in kv { - processed += 1; - if processed % 50_000 == 0 || processed == keys.len() || processed == 1 { - log::info!( - target: LOG_TARGET, - "inserting keys progress = {:.0}% [{} / {}]", - (processed as f32 / keys.len() as f32) * 100f32, - processed, - keys.len(), - ); - } - // skip writing the child root data. - if is_default_child_storage_key(k.as_ref()) { - continue - } - pending_ext.insert(k, v); - } + Message::Batch(kvs) => { + let kvs = kvs + .into_iter() + .filter(|(k, _)| !is_default_child_storage_key(k)) + .collect::>(); + processed += kvs.len(); + pending_ext.batch_insert(kvs); + log::info!( + target: LOG_TARGET, + "inserting keys progress = {:.0}% [{} / {}]", + (processed as f32 / keys.len() as f32) * 100f32, + processed, + keys.len(), + ); }, Message::BatchFailed(error) => { log::error!(target: LOG_TARGET, "Batch processing failed: {:?}", error); @@ -1010,13 +1006,12 @@ where ); info!(target: LOG_TARGET, "injecting a total of {} top keys", top.len()); - for (k, v) in top { - // skip writing the child root data. - if is_default_child_storage_key(k.as_ref()) { - continue - } - inner_ext.insert(k.0, v.0); - } + let top = top + .into_iter() + .filter(|(k, _)| !is_default_child_storage_key(k.as_ref())) + .map(|(k, v)| (k.0, v.0)) + .collect::>(); + inner_ext.batch_insert(top); info!( target: LOG_TARGET, @@ -1052,9 +1047,7 @@ where "extending externalities with {} manually injected key-values", self.hashed_key_values.len() ); - for (k, v) in self.hashed_key_values { - ext.insert(k.0, v.0); - } + ext.batch_insert(self.hashed_key_values.into_iter().map(|(k, v)| (k.0, v.0))); } // exclude manual key values. From 587959e2287360bfe64b84fd983031d0bed9b892 Mon Sep 17 00:00:00 2001 From: Liam Aharon Date: Thu, 27 Apr 2023 18:55:01 +1000 Subject: [PATCH 428/558] collective pallet: sort genesis members and enforce max len constraint (#13988) * insert members in sorted order * improve variable name * enforce genesis members length constraint --- frame/collective/src/lib.rs | 6 ++++++ frame/collective/src/tests.rs | 40 +++++++++++++++++++++++++++++++---- 2 files changed, 42 insertions(+), 4 deletions(-) diff --git a/frame/collective/src/lib.rs b/frame/collective/src/lib.rs index 0ecf008fe..fd89a998a 100644 --- a/frame/collective/src/lib.rs +++ b/frame/collective/src/lib.rs @@ -246,6 +246,10 @@ pub mod pallet { self.members.len(), "Members cannot contain duplicate accounts." ); + assert!( + self.members.len() <= T::MaxMembers::get() as usize, + "Members length cannot exceed MaxMembers.", + ); Pallet::::initialize_members(&self.members) } @@ -1107,6 +1111,8 @@ impl, I: 'static> InitializeMembers for Pallet fn initialize_members(members: &[T::AccountId]) { if !members.is_empty() { assert!(>::get().is_empty(), "Members are already initialized!"); + let mut members = members.to_vec(); + members.sort(); >::put(members); } } diff --git a/frame/collective/src/tests.rs b/frame/collective/src/tests.rs index 4775133ff..1165ebcec 100644 --- a/frame/collective/src/tests.rs +++ b/frame/collective/src/tests.rs @@ -86,6 +86,7 @@ mod mock_democracy { } pub type MaxMembers = ConstU32<100>; +type AccountId = u64; parameter_types! { pub const MotionDuration: u64 = 3; @@ -105,7 +106,7 @@ impl frame_system::Config for Test { type RuntimeCall = RuntimeCall; type Hash = H256; type Hashing = BlakeTwo256; - type AccountId = u64; + type AccountId = AccountId; type Lookup = IdentityLookup; type Header = Header; type RuntimeEvent = RuntimeEvent; @@ -161,19 +162,26 @@ impl Config for Test { type MaxProposalWeight = MaxProposalWeight; } -pub struct ExtBuilder {} +pub struct ExtBuilder { + collective_members: Vec, +} impl Default for ExtBuilder { fn default() -> Self { - Self {} + Self { collective_members: vec![1, 2, 3] } } } impl ExtBuilder { + fn set_collective_members(mut self, collective_members: Vec) -> Self { + self.collective_members = collective_members; + self + } + pub fn build(self) -> sp_io::TestExternalities { let mut ext: sp_io::TestExternalities = GenesisConfig { collective: pallet_collective::GenesisConfig { - members: vec![1, 2, 3], + members: self.collective_members, phantom: Default::default(), }, collective_majority: pallet_collective::GenesisConfig { @@ -219,6 +227,17 @@ fn motions_basic_environment_works() { }); } +#[test] +fn initialize_members_sorts_members() { + let unsorted_members = vec![3, 2, 4, 1]; + let expected_members = vec![1, 2, 3, 4]; + ExtBuilder::default() + .set_collective_members(unsorted_members) + .build_and_execute(|| { + assert_eq!(Collective::members(), expected_members); + }); +} + #[test] fn proposal_weight_limit_works() { ExtBuilder::default().build_and_execute(|| { @@ -1424,6 +1443,19 @@ fn disapprove_proposal_works() { }) } +#[should_panic(expected = "Members length cannot exceed MaxMembers.")] +#[test] +fn genesis_build_panics_with_too_many_members() { + let max_members: u32 = MaxMembers::get(); + let too_many_members = (1..=max_members as u64 + 1).collect::>(); + pallet_collective::GenesisConfig:: { + members: too_many_members, + phantom: Default::default(), + } + .build_storage() + .unwrap(); +} + #[test] #[should_panic(expected = "Members cannot contain duplicate accounts.")] fn genesis_build_panics_with_duplicate_members() { From 1e4c01125c326e2cef0c3318b70eea5093b744bb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bastian=20K=C3=B6cher?= Date: Thu, 27 Apr 2023 11:28:03 +0200 Subject: [PATCH 429/558] sc-network-sync: Improve error reporting (#14025) Before this wasn't printing a warning for `VerificationFailed` when there wasn't a peer assigned to the error message. However, if there happens an error on importing the state there wouldn't be any error. This pr improves the situation to also print an error in this case. Besides that there are some other cleanups. --- client/network/sync/src/lib.rs | 43 +++++++++++++++------------------- 1 file changed, 19 insertions(+), 24 deletions(-) diff --git a/client/network/sync/src/lib.rs b/client/network/sync/src/lib.rs index 29c4bc840..3f1cbebd5 100644 --- a/client/network/sync/src/lib.rs +++ b/client/network/sync/src/lib.rs @@ -2708,9 +2708,7 @@ where break } - if result.is_err() { - has_error = true; - } + has_error |= result.is_err(); match result { Ok(BlockImportStatus::ImportedKnown(number, who)) => @@ -2721,9 +2719,7 @@ where if aux.clear_justification_requests { trace!( target: "sync", - "Block imported clears all pending justification requests {}: {:?}", - number, - hash, + "Block imported clears all pending justification requests {number}: {hash:?}", ); self.clear_justification_requests(); } @@ -2731,9 +2727,7 @@ where if aux.needs_justification { trace!( target: "sync", - "Block imported but requires justification {}: {:?}", - number, - hash, + "Block imported but requires justification {number}: {hash:?}", ); self.request_justification(&hash, number); } @@ -2793,25 +2787,26 @@ where output.push(Err(BadPeer(peer, rep::INCOMPLETE_HEADER))); output.extend(self.restart()); }, - Err(BlockImportError::VerificationFailed(who, e)) => + Err(BlockImportError::VerificationFailed(who, e)) => { + let extra_message = + who.map_or_else(|| "".into(), |peer| format!(" received from ({peer})")); + + warn!( + target: "sync", + "💔 Verification failed for block {hash:?}{extra_message}: {e:?}", + ); + if let Some(peer) = who { - warn!( - target: "sync", - "💔 Verification failed for block {:?} received from peer: {}, {:?}", - hash, - peer, - e, - ); output.push(Err(BadPeer(peer, rep::VERIFICATION_FAIL))); - output.extend(self.restart()); - }, + } + + output.extend(self.restart()); + }, Err(BlockImportError::BadBlock(who)) => if let Some(peer) = who { warn!( target: "sync", - "💔 Block {:?} received from peer {} has been blacklisted", - hash, - peer, + "💔 Block {hash:?} received from peer {peer} has been blacklisted", ); output.push(Err(BadPeer(peer, rep::BAD_BLOCK))); }, @@ -2819,10 +2814,10 @@ where // This may happen if the chain we were requesting upon has been discarded // in the meantime because other chain has been finalized. // Don't mark it as bad as it still may be synced if explicitly requested. - trace!(target: "sync", "Obsolete block {:?}", hash); + trace!(target: "sync", "Obsolete block {hash:?}"); }, e @ Err(BlockImportError::UnknownParent) | e @ Err(BlockImportError::Other(_)) => { - warn!(target: "sync", "💔 Error importing block {:?}: {}", hash, e.unwrap_err()); + warn!(target: "sync", "💔 Error importing block {hash:?}: {}", e.unwrap_err()); self.state_sync = None; self.warp_sync = None; output.extend(self.restart()); From d5d63e9b7f624de321032a033675387a73a66646 Mon Sep 17 00:00:00 2001 From: PG Herveou Date: Thu, 27 Apr 2023 12:01:12 +0200 Subject: [PATCH 430/558] contracts Fix store-call test path (#14028) --- frame/contracts/src/tests.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frame/contracts/src/tests.rs b/frame/contracts/src/tests.rs index dbe61cf33..697b58f15 100644 --- a/frame/contracts/src/tests.rs +++ b/frame/contracts/src/tests.rs @@ -3585,7 +3585,7 @@ fn storage_deposit_works() { #[test] fn storage_deposit_callee_works() { let (wasm_caller, _code_hash_caller) = compile_module::("call").unwrap(); - let (wasm_callee, _code_hash_callee) = compile_module::("store").unwrap(); + let (wasm_callee, _code_hash_callee) = compile_module::("store_call").unwrap(); const ED: u64 = 200; ExtBuilder::default().existential_deposit(ED).build().execute_with(|| { let _ = Balances::deposit_creating(&ALICE, 1_000_000); From 04248e293e5156a5468ed162a8565f192de325d7 Mon Sep 17 00:00:00 2001 From: yjh Date: Thu, 27 Apr 2023 18:34:15 +0800 Subject: [PATCH 431/558] chore(cli): make cli display docs correctly (#14017) * chore(cli): make cli display docs correctly * ".git/.scripts/commands/fmt/fmt.sh" --------- Co-authored-by: command-bot <> --- client/cli/src/commands/build_spec_cmd.rs | 1 - client/cli/src/commands/check_block_cmd.rs | 3 +- client/cli/src/commands/export_blocks_cmd.rs | 2 -- client/cli/src/commands/generate_node_key.rs | 2 -- client/cli/src/commands/import_blocks_cmd.rs | 1 - client/cli/src/commands/insert_key.rs | 2 +- client/cli/src/commands/inspect_key.rs | 5 --- client/cli/src/commands/inspect_node_key.rs | 2 -- client/cli/src/commands/run_cmd.rs | 11 ------- client/cli/src/params/import_params.rs | 7 ---- client/cli/src/params/network_params.rs | 6 ---- client/cli/src/params/node_key_params.rs | 12 ------- .../cli/src/params/offchain_worker_params.rs | 5 +-- client/cli/src/params/prometheus_params.rs | 2 -- client/cli/src/params/pruning_params.rs | 32 +++---------------- client/cli/src/params/shared_params.rs | 7 ---- client/cli/src/params/telemetry_params.rs | 2 -- 17 files changed, 8 insertions(+), 94 deletions(-) diff --git a/client/cli/src/commands/build_spec_cmd.rs b/client/cli/src/commands/build_spec_cmd.rs index a2cbfedb7..aa5314f9c 100644 --- a/client/cli/src/commands/build_spec_cmd.rs +++ b/client/cli/src/commands/build_spec_cmd.rs @@ -38,7 +38,6 @@ pub struct BuildSpecCmd { pub raw: bool, /// Disable adding the default bootnode to the specification. - /// /// By default the `/ip4/127.0.0.1/tcp/30333/p2p/NODE_PEER_ID` bootnode is added to the /// specification when no bootnode exists. #[arg(long)] diff --git a/client/cli/src/commands/check_block_cmd.rs b/client/cli/src/commands/check_block_cmd.rs index 897b61c8e..ba20376d9 100644 --- a/client/cli/src/commands/check_block_cmd.rs +++ b/client/cli/src/commands/check_block_cmd.rs @@ -29,12 +29,11 @@ use std::{fmt::Debug, str::FromStr, sync::Arc}; /// The `check-block` command used to validate blocks. #[derive(Debug, Clone, Parser)] pub struct CheckBlockCmd { - /// Block hash or number + /// Block hash or number. #[arg(value_name = "HASH or NUMBER")] pub input: BlockNumberOrHash, /// The default number of 64KB pages to ever allocate for Wasm execution. - /// /// Don't alter this unless you know what you're doing. #[arg(long, value_name = "COUNT")] pub default_heap_pages: Option, diff --git a/client/cli/src/commands/export_blocks_cmd.rs b/client/cli/src/commands/export_blocks_cmd.rs index 7e8a295f9..120d78898 100644 --- a/client/cli/src/commands/export_blocks_cmd.rs +++ b/client/cli/src/commands/export_blocks_cmd.rs @@ -36,13 +36,11 @@ pub struct ExportBlocksCmd { pub output: Option, /// Specify starting block number. - /// /// Default is 1. #[arg(long, value_name = "BLOCK")] pub from: Option, /// Specify last block number. - /// /// Default is best block. #[arg(long, value_name = "BLOCK")] pub to: Option, diff --git a/client/cli/src/commands/generate_node_key.rs b/client/cli/src/commands/generate_node_key.rs index 2288cd403..a16ba4990 100644 --- a/client/cli/src/commands/generate_node_key.rs +++ b/client/cli/src/commands/generate_node_key.rs @@ -35,13 +35,11 @@ use std::{ )] pub struct GenerateNodeKeyCmd { /// Name of file to save secret key to. - /// /// If not given, the secret key is printed to stdout. #[arg(long)] file: Option, /// The output is in raw binary format. - /// /// If not given, the output is written as an hex encoded string. #[arg(long)] bin: bool, diff --git a/client/cli/src/commands/import_blocks_cmd.rs b/client/cli/src/commands/import_blocks_cmd.rs index f76c72924..815c6ab18 100644 --- a/client/cli/src/commands/import_blocks_cmd.rs +++ b/client/cli/src/commands/import_blocks_cmd.rs @@ -41,7 +41,6 @@ pub struct ImportBlocksCmd { pub input: Option, /// The default number of 64KB pages to ever allocate for Wasm execution. - /// /// Don't alter this unless you know what you're doing. #[arg(long, value_name = "COUNT")] pub default_heap_pages: Option, diff --git a/client/cli/src/commands/insert_key.rs b/client/cli/src/commands/insert_key.rs index e80058d44..fa9d125d3 100644 --- a/client/cli/src/commands/insert_key.rs +++ b/client/cli/src/commands/insert_key.rs @@ -36,7 +36,7 @@ pub struct InsertKeyCmd { #[arg(long)] suri: Option, - /// Key type, examples: "gran", or "imon" + /// Key type, examples: "gran", or "imon". #[arg(long)] key_type: String, diff --git a/client/cli/src/commands/inspect_key.rs b/client/cli/src/commands/inspect_key.rs index de82fe71e..5aa8b0bdc 100644 --- a/client/cli/src/commands/inspect_key.rs +++ b/client/cli/src/commands/inspect_key.rs @@ -34,12 +34,9 @@ use std::str::FromStr; pub struct InspectKeyCmd { /// A Key URI to be inspected. May be a secret seed, secret URI /// (with derivation paths and password), SS58, public URI or a hex encoded public key. - /// /// If it is a hex encoded public key, `--public` needs to be given as argument. - /// /// If the given value is a file, the file content will be used /// as URI. - /// /// If omitted, you will be prompted for the URI. uri: Option, @@ -64,12 +61,10 @@ pub struct InspectKeyCmd { pub crypto_scheme: CryptoSchemeFlag, /// Expect that `--uri` has the given public key/account-id. - /// /// If `--uri` has any derivations, the public key is checked against the base `uri`, i.e. the /// `uri` without any derivation applied. However, if `uri` has a password or there is one /// given by `--password`, it will be used to decrypt `uri` before comparing the public /// key/account-id. - /// /// If there is no derivation in `--uri`, the public key will be checked against the public key /// of `--uri` directly. #[arg(long, conflicts_with = "public")] diff --git a/client/cli/src/commands/inspect_node_key.rs b/client/cli/src/commands/inspect_node_key.rs index 2370f4a09..733a1343a 100644 --- a/client/cli/src/commands/inspect_node_key.rs +++ b/client/cli/src/commands/inspect_node_key.rs @@ -34,13 +34,11 @@ use std::{ )] pub struct InspectNodeKeyCmd { /// Name of file to read the secret key from. - /// /// If not given, the secret key is read from stdin (up to EOF). #[arg(long)] file: Option, /// The input is in raw binary format. - /// /// If not given, the input is read as an hex encoded string. #[arg(long)] bin: bool, diff --git a/client/cli/src/commands/run_cmd.rs b/client/cli/src/commands/run_cmd.rs index a38ba6f49..7cc1e6c73 100644 --- a/client/cli/src/commands/run_cmd.rs +++ b/client/cli/src/commands/run_cmd.rs @@ -38,7 +38,6 @@ use std::net::{IpAddr, Ipv4Addr, SocketAddr}; #[derive(Debug, Clone, Parser)] pub struct RunCmd { /// Enable validator mode. - /// /// The node will be started with the authority role and actively /// participate in any consensus task that it can (e.g. depending on /// availability of local keys). @@ -51,7 +50,6 @@ pub struct RunCmd { pub no_grandpa: bool, /// Listen to all RPC interfaces. - /// /// Default is local. Note: not all RPC methods are safe to be exposed publicly. Use an RPC /// proxy server to filter out dangerous methods. More details: /// . @@ -60,13 +58,11 @@ pub struct RunCmd { pub rpc_external: bool, /// Listen to all RPC interfaces. - /// /// Same as `--rpc-external`. #[arg(long)] pub unsafe_rpc_external: bool, /// RPC methods to expose. - /// /// - `unsafe`: Exposes every RPC method. /// - `safe`: Exposes only a safe subset of RPC methods, denying unsafe RPC methods. /// - `auto`: Acts as `safe` if RPC is served externally, e.g. when `--{rpc,ws}-external` is @@ -82,7 +78,6 @@ pub struct RunCmd { pub rpc_methods: RpcMethods, /// Listen to all Websocket interfaces. - /// /// Default is local. Note: not all RPC methods are safe to be exposed publicly. Use an RPC /// proxy server to filter out dangerous methods. More details: /// . @@ -91,7 +86,6 @@ pub struct RunCmd { pub ws_external: bool, /// Listen to all Websocket interfaces. - /// /// Same as `--ws-external` but doesn't warn you about it. #[arg(long)] pub unsafe_ws_external: bool, @@ -137,7 +131,6 @@ pub struct RunCmd { pub ws_max_out_buffer_capacity: Option, /// Specify browser Origins allowed to access the HTTP & WS RPC servers. - /// /// A comma-separated list of origins (protocol://domain or special `null` /// value). Value of `all` will disable origin validation. Default is to /// allow localhost and origins. When running in @@ -146,7 +139,6 @@ pub struct RunCmd { pub rpc_cors: Option, /// The human-readable name for this node. - /// /// It's used as network node name. #[arg(long, value_name = "NAME")] pub name: Option, @@ -225,13 +217,10 @@ pub struct RunCmd { pub force_authoring: bool, /// Run a temporary node. - /// /// A temporary directory will be created to store the configuration and will be deleted /// at the end of the process. - /// /// Note: the directory is random per process execution. This directory is used as base path /// which includes: database, node key and keystore. - /// /// When `--dev` is given and no explicit `--base-path`, this option is implied. #[arg(long, conflicts_with = "base_path")] pub tmp: bool, diff --git a/client/cli/src/params/import_params.rs b/client/cli/src/params/import_params.rs index e36fbb5ff..9e57a017e 100644 --- a/client/cli/src/params/import_params.rs +++ b/client/cli/src/params/import_params.rs @@ -52,15 +52,11 @@ pub struct ImportParams { pub wasm_method: WasmExecutionMethod, /// The WASM instantiation method to use. - /// /// Only has an effect when `wasm-execution` is set to `compiled`. - /// /// The copy-on-write strategies are only supported on Linux. /// If the copy-on-write variant of a strategy is unsupported /// the executor will fall back to the non-CoW equivalent. - /// /// The fastest (and the default) strategy available is `pooling-copy-on-write`. - /// /// The `legacy-instance-reuse` strategy is deprecated and will /// be removed in the future. It should only be used in case of /// issues with the default instantiation strategy. @@ -73,7 +69,6 @@ pub struct ImportParams { pub wasmtime_instantiation_strategy: WasmtimeInstantiationStrategy, /// Specify the path where local WASM runtimes are stored. - /// /// These runtimes will override on-chain runtimes when the version matches. #[arg(long, value_name = "PATH")] pub wasm_runtime_overrides: Option, @@ -83,13 +78,11 @@ pub struct ImportParams { pub execution_strategies: ExecutionStrategiesParams, /// Specify the state cache size. - /// /// Providing `0` will disable the cache. #[arg(long, value_name = "Bytes", default_value_t = 67108864)] pub trie_cache_size: usize, /// DEPRECATED - /// /// Switch to `--trie-cache-size`. #[arg(long)] state_cache_size: Option, diff --git a/client/cli/src/params/network_params.rs b/client/cli/src/params/network_params.rs index 9d61e7204..13e433825 100644 --- a/client/cli/src/params/network_params.rs +++ b/client/cli/src/params/network_params.rs @@ -42,9 +42,7 @@ pub struct NetworkParams { pub reserved_nodes: Vec, /// Whether to only synchronize the chain with reserved nodes. - /// /// Also disables automatic peer discovery. - /// /// TCP connections might still be established with non-reserved nodes. /// In particular, if you are a validator your node might still connect to other /// validator nodes and collator nodes regardless of whether they are defined as @@ -95,14 +93,12 @@ pub struct NetworkParams { pub in_peers_light: u32, /// Disable mDNS discovery. - /// /// By default, the network will use mDNS to discover other nodes on the /// local network. This disables it. Automatically implied when using --dev. #[arg(long)] pub no_mdns: bool, /// Maximum number of peers from which to ask for the same blocks in parallel. - /// /// This allows downloading announced blocks from multiple peers. Decrease to save /// traffic and risk increased latency. #[arg(long, value_name = "COUNT", default_value_t = 5)] @@ -113,7 +109,6 @@ pub struct NetworkParams { pub node_key_params: NodeKeyParams, /// Enable peer discovery on local networks. - /// /// By default this option is `true` for `--dev` or when the chain type is /// `Local`/`Development` and false otherwise. #[arg(long)] @@ -121,7 +116,6 @@ pub struct NetworkParams { /// Require iterative Kademlia DHT queries to use disjoint paths for increased resiliency in /// the presence of potentially adversarial nodes. - /// /// See the S/Kademlia paper for more information on the high level design as well as its /// security improvements. #[arg(long)] diff --git a/client/cli/src/params/node_key_params.rs b/client/cli/src/params/node_key_params.rs index 074b95bea..e31963d2f 100644 --- a/client/cli/src/params/node_key_params.rs +++ b/client/cli/src/params/node_key_params.rs @@ -33,16 +33,12 @@ const NODE_KEY_ED25519_FILE: &str = "secret_ed25519"; #[derive(Debug, Clone, Args)] pub struct NodeKeyParams { /// The secret key to use for libp2p networking. - /// /// The value is a string that is parsed according to the choice of /// `--node-key-type` as follows: - /// /// `ed25519`: /// The value is parsed as a hex-encoded Ed25519 32 byte secret key, /// i.e. 64 hex characters. - /// /// The value of this option takes precedence over `--node-key-file`. - /// /// WARNING: Secrets provided as command-line arguments are easily exposed. /// Use of this option should be limited to development and testing. To use /// an externally managed secret key, use `--node-key-file` instead. @@ -50,33 +46,25 @@ pub struct NodeKeyParams { pub node_key: Option, /// The type of secret key to use for libp2p networking. - /// /// The secret key of the node is obtained as follows: - /// /// * If the `--node-key` option is given, the value is parsed as a secret key according to /// the type. See the documentation for `--node-key`. - /// /// * If the `--node-key-file` option is given, the secret key is read from the specified /// file. See the documentation for `--node-key-file`. - /// /// * Otherwise, the secret key is read from a file with a predetermined, type-specific name /// from the chain-specific network config directory inside the base directory specified by /// `--base-dir`. If this file does not exist, it is created with a newly generated secret /// key of the chosen type. - /// /// The node's secret key determines the corresponding public key and hence the /// node's peer ID in the context of libp2p. #[arg(long, value_name = "TYPE", value_enum, ignore_case = true, default_value_t = NodeKeyType::Ed25519)] pub node_key_type: NodeKeyType, /// The file from which to read the node's secret key to use for libp2p networking. - /// /// The contents of the file are parsed according to the choice of `--node-key-type` /// as follows: - /// /// `ed25519`: /// The file must contain an unencoded 32 byte or hex encoded Ed25519 secret key. - /// /// If the file does not exist, it is created with a newly generated secret key of /// the chosen type. #[arg(long, value_name = "FILE")] diff --git a/client/cli/src/params/offchain_worker_params.rs b/client/cli/src/params/offchain_worker_params.rs index 33fd8d609..d1fedab4c 100644 --- a/client/cli/src/params/offchain_worker_params.rs +++ b/client/cli/src/params/offchain_worker_params.rs @@ -33,7 +33,6 @@ use crate::{error, OffchainWorkerEnabled}; #[derive(Debug, Clone, Args)] pub struct OffchainWorkerParams { /// Should execute offchain workers on every block. - /// /// By default it's only enabled for nodes that are authoring new blocks. #[arg( long = "offchain-worker", @@ -45,9 +44,7 @@ pub struct OffchainWorkerParams { pub enabled: OffchainWorkerEnabled, /// Enable Offchain Indexing API, which allows block import to write to Offchain DB. - /// - /// Enables a runtime to write directly to a offchain workers - /// DB during block import. + /// Enables a runtime to write directly to a offchain workers DB during block import. #[arg(long = "enable-offchain-indexing", value_name = "ENABLE_OFFCHAIN_INDEXING", default_value_t = false, action = ArgAction::Set)] pub indexing_enabled: bool, } diff --git a/client/cli/src/params/prometheus_params.rs b/client/cli/src/params/prometheus_params.rs index 69199ad5b..4d234ea33 100644 --- a/client/cli/src/params/prometheus_params.rs +++ b/client/cli/src/params/prometheus_params.rs @@ -27,12 +27,10 @@ pub struct PrometheusParams { #[arg(long, value_name = "PORT")] pub prometheus_port: Option, /// Expose Prometheus exporter on all interfaces. - /// /// Default is local. #[arg(long)] pub prometheus_external: bool, /// Do not expose a Prometheus exporter endpoint. - /// /// Prometheus metric endpoint is enabled by default. #[arg(long)] pub no_prometheus: bool, diff --git a/client/cli/src/params/pruning_params.rs b/client/cli/src/params/pruning_params.rs index 757da2dd9..1b5bf247d 100644 --- a/client/cli/src/params/pruning_params.rs +++ b/client/cli/src/params/pruning_params.rs @@ -24,48 +24,26 @@ use sc_service::{BlocksPruning, PruningMode}; #[derive(Debug, Clone, Args)] pub struct PruningParams { /// Specify the state pruning mode. - /// /// This mode specifies when the block's state (ie, storage) /// should be pruned (ie, removed) from the database. - /// /// This setting can only be set on the first creation of the database. Every subsequent run /// will load the pruning mode from the database and will error if the stored mode doesn't /// match this CLI value. It is fine to drop this CLI flag for subsequent runs. - /// /// Possible values: - /// - /// - archive: - /// - /// Keep the state of all blocks. - /// - /// - 'archive-canonical' - /// - /// Keep only the state of finalized blocks. - /// - /// - number - /// - /// Keep the state of the last number of finalized blocks. - /// + /// - archive: Keep the state of all blocks. + /// - 'archive-canonical' Keep only the state of finalized blocks. + /// - number Keep the state of the last number of finalized blocks. /// [default: 256] #[arg(alias = "pruning", long, value_name = "PRUNING_MODE")] pub state_pruning: Option, /// Specify the blocks pruning mode. - /// /// This mode specifies when the block's body (including justifications) /// should be pruned (ie, removed) from the database. - /// /// Possible values: - /// - 'archive' - /// - /// Keep all blocks. - /// - /// - 'archive-canonical' - /// - /// Keep only finalized blocks. - /// + /// - 'archive' Keep all blocks. + /// - 'archive-canonical' Keep only finalized blocks. /// - number - /// /// Keep the last `number` of finalized blocks. #[arg( alias = "keep-blocks", diff --git a/client/cli/src/params/shared_params.rs b/client/cli/src/params/shared_params.rs index 913a6c436..3d20ca504 100644 --- a/client/cli/src/params/shared_params.rs +++ b/client/cli/src/params/shared_params.rs @@ -25,14 +25,12 @@ use std::path::PathBuf; #[derive(Debug, Clone, Args)] pub struct SharedParams { /// Specify the chain specification. - /// /// It can be one of the predefined ones (dev, local, or staging) or it can be a path to a file /// with the chainspec (such as one exported by the `build-spec` subcommand). #[arg(long, value_name = "CHAIN_SPEC")] pub chain: Option, /// Specify the development chain. - /// /// This flag sets `--chain=dev`, `--force-authoring`, `--rpc-cors=all`, /// `--alice`, and `--tmp` flags, unless explicitly overridden. #[arg(long, conflicts_with_all = &["chain"])] @@ -43,16 +41,13 @@ pub struct SharedParams { pub base_path: Option, /// Sets a custom logging filter. Syntax is `=`, e.g. -lsync=debug. - /// /// Log levels (least to most verbose) are error, warn, info, debug, and trace. /// By default, all targets log `info`. The global log level can be set with `-l`. #[arg(short = 'l', long, value_name = "LOG_PATTERN", num_args = 1..)] pub log: Vec, /// Enable detailed log output. - /// /// This includes displaying the log target, log level and thread name. - /// /// This is automatically enabled when something is logged with any higher level than `info`. #[arg(long)] pub detailed_log_output: bool, @@ -62,10 +57,8 @@ pub struct SharedParams { pub disable_log_color: bool, /// Enable feature to dynamically update and reload the log filter. - /// /// Be aware that enabling this feature can lead to a performance decrease up to factor six or /// more. Depending on the global logging level the performance decrease changes. - /// /// The `system_addLogFilter` and `system_resetLogFilter` RPCs will have no effect with this /// option not being set. #[arg(long)] diff --git a/client/cli/src/params/telemetry_params.rs b/client/cli/src/params/telemetry_params.rs index ca0964198..67f441071 100644 --- a/client/cli/src/params/telemetry_params.rs +++ b/client/cli/src/params/telemetry_params.rs @@ -22,13 +22,11 @@ use clap::Args; #[derive(Debug, Clone, Args)] pub struct TelemetryParams { /// Disable connecting to the Substrate telemetry server. - /// /// Telemetry is on by default on global chains. #[arg(long)] pub no_telemetry: bool, /// The URL of the telemetry server to connect to. - /// /// This flag can be passed multiple times as a means to specify multiple /// telemetry endpoints. Verbosity levels range from 0-9, with 0 denoting /// the least verbosity. From 58be496f61f98b8e04fbf11a46e56bb9c2b3d9c7 Mon Sep 17 00:00:00 2001 From: Kian Paimani <5588131+kianenigma@users.noreply.github.com> Date: Thu, 27 Apr 2023 13:27:12 +0200 Subject: [PATCH 432/558] improve staking interface methods (#14023) * improve staking interface methods * fix * fix * fix build * restart * fix --------- Co-authored-by: parity-processbot <> --- Cargo.lock | 1 + .../nomination-pools/benchmarking/src/lib.rs | 4 +- frame/nomination-pools/src/mock.rs | 12 +++++- frame/staking/src/lib.rs | 13 +------ frame/staking/src/pallet/impls.rs | 30 ++++++++++++++- frame/staking/src/tests.rs | 23 +++++++++++ primitives/staking/Cargo.toml | 2 + primitives/staking/src/lib.rs | 38 ++++++++++++++++--- 8 files changed, 99 insertions(+), 24 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 8d8d2d59a..c06bf41c2 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -10862,6 +10862,7 @@ version = "4.0.0-dev" dependencies = [ "parity-scale-codec", "scale-info", + "serde", "sp-core", "sp-runtime", "sp-std", diff --git a/frame/nomination-pools/benchmarking/src/lib.rs b/frame/nomination-pools/benchmarking/src/lib.rs index d58bbaf3d..137b9e9af 100644 --- a/frame/nomination-pools/benchmarking/src/lib.rs +++ b/frame/nomination-pools/benchmarking/src/lib.rs @@ -684,12 +684,12 @@ frame_benchmarking::benchmarks! { .collect(); assert_ok!(T::Staking::nominate(&pool_account, validators)); - assert!(T::Staking::nominations(Pools::::create_bonded_account(1)).is_some()); + assert!(T::Staking::nominations(&Pools::::create_bonded_account(1)).is_some()); whitelist_account!(depositor); }:_(RuntimeOrigin::Signed(depositor.clone()), 1) verify { - assert!(T::Staking::nominations(Pools::::create_bonded_account(1)).is_none()); + assert!(T::Staking::nominations(&Pools::::create_bonded_account(1)).is_none()); } set_commission { diff --git a/frame/nomination-pools/src/mock.rs b/frame/nomination-pools/src/mock.rs index 3565ff14e..3ab9be516 100644 --- a/frame/nomination-pools/src/mock.rs +++ b/frame/nomination-pools/src/mock.rs @@ -67,6 +67,14 @@ impl sp_staking::StakingInterface for StakingMock { BondingDuration::get() } + fn status( + _: &Self::AccountId, + ) -> Result, DispatchError> { + Nominations::get() + .map(|noms| sp_staking::StakerStatus::Nominator(noms)) + .ok_or(DispatchError::Other("NotStash")) + } + fn bond_extra(who: &Self::AccountId, extra: Self::Balance) -> DispatchResult { let mut x = BondedBalanceMap::get(); x.get_mut(who).map(|v| *v += extra); @@ -108,7 +116,7 @@ impl sp_staking::StakingInterface for StakingMock { } #[cfg(feature = "runtime-benchmarks")] - fn nominations(_: Self::AccountId) -> Option> { + fn nominations(_: &Self::AccountId) -> Option> { Nominations::get() } @@ -116,7 +124,7 @@ impl sp_staking::StakingInterface for StakingMock { unimplemented!("method currently not used in testing") } - fn stake(who: &Self::AccountId) -> Result, DispatchError> { + fn stake(who: &Self::AccountId) -> Result, DispatchError> { match ( UnbondingBalanceMap::get().get(who).copied(), BondedBalanceMap::get().get(who).copied(), diff --git a/frame/staking/src/lib.rs b/frame/staking/src/lib.rs index d6345c216..28d970b91 100644 --- a/frame/staking/src/lib.rs +++ b/frame/staking/src/lib.rs @@ -311,6 +311,7 @@ use sp_runtime::{ traits::{AtLeast32BitUnsigned, Convert, Saturating, StaticLookup, Zero}, Perbill, Perquintill, Rounding, RuntimeDebug, }; +pub use sp_staking::StakerStatus; use sp_staking::{ offence::{Offence, OffenceError, ReportOffence}, EraIndex, SessionIndex, @@ -381,18 +382,6 @@ impl Default for EraRewardPoints { } } -/// Indicates the initial status of the staker. -#[derive(RuntimeDebug, TypeInfo)] -#[cfg_attr(feature = "std", derive(serde::Serialize, serde::Deserialize, Clone))] -pub enum StakerStatus { - /// Chilling. - Idle, - /// Declared desire in validating or already participating in it. - Validator, - /// Nominating for a group of other stakers. - Nominator(Vec), -} - /// A destination account for payment. #[derive(PartialEq, Eq, Copy, Clone, Encode, Decode, RuntimeDebug, TypeInfo, MaxEncodedLen)] pub enum RewardDestination { diff --git a/frame/staking/src/pallet/impls.rs b/frame/staking/src/pallet/impls.rs index 3058d3edc..984871f7f 100644 --- a/frame/staking/src/pallet/impls.rs +++ b/frame/staking/src/pallet/impls.rs @@ -22,6 +22,7 @@ use frame_election_provider_support::{ SortedListProvider, VoteWeight, VoterOf, }; use frame_support::{ + defensive, dispatch::WithPostDispatchInfo, pallet_prelude::*, traits::{ @@ -1608,7 +1609,7 @@ impl StakingInterface for Pallet { Self::current_era().unwrap_or(Zero::zero()) } - fn stake(who: &Self::AccountId) -> Result, DispatchError> { + fn stake(who: &Self::AccountId) -> Result>, DispatchError> { Self::bonded(who) .and_then(|c| Self::ledger(c)) .map(|l| Stake { total: l.total, active: l.active }) @@ -1662,8 +1663,33 @@ impl StakingInterface for Pallet { Self::nominate(RawOrigin::Signed(ctrl).into(), targets) } + fn status( + who: &Self::AccountId, + ) -> Result, DispatchError> { + let is_bonded = Self::bonded(who).is_some(); + if !is_bonded { + return Err(Error::::NotStash.into()) + } + + let is_validator = Validators::::contains_key(&who); + let is_nominator = Nominators::::get(&who); + + use sp_staking::StakerStatus; + match (is_validator, is_nominator.is_some()) { + (false, false) => Ok(StakerStatus::Idle), + (true, false) => Ok(StakerStatus::Validator), + (false, true) => Ok(StakerStatus::Nominator( + is_nominator.expect("is checked above; qed").targets.into_inner(), + )), + (true, true) => { + defensive!("cannot be both validators and nominator"); + Err(Error::::BadState.into()) + }, + } + } + sp_staking::runtime_benchmarks_enabled! { - fn nominations(who: Self::AccountId) -> Option> { + fn nominations(who: &Self::AccountId) -> Option> { Nominators::::get(who).map(|n| n.targets.into_inner()) } diff --git a/frame/staking/src/tests.rs b/frame/staking/src/tests.rs index d97eb3ef8..affee6002 100644 --- a/frame/staking/src/tests.rs +++ b/frame/staking/src/tests.rs @@ -5824,4 +5824,27 @@ mod staking_interface { )); }); } + + #[test] + fn status() { + ExtBuilder::default().build_and_execute(|| { + // stash of a validator is identified as a validator + assert_eq!(Staking::status(&11).unwrap(), StakerStatus::Validator); + // .. but not the controller. + assert!(Staking::status(&10).is_err()); + + // stash of nominator is identified as a nominator + assert_eq!(Staking::status(&101).unwrap(), StakerStatus::Nominator(vec![11, 21])); + // .. but not the controller. + assert!(Staking::status(&100).is_err()); + + // stash of chilled is identified as a chilled + assert_eq!(Staking::status(&41).unwrap(), StakerStatus::Idle); + // .. but not the controller. + assert!(Staking::status(&40).is_err()); + + // random other account. + assert!(Staking::status(&42).is_err()); + }) + } } diff --git a/primitives/staking/Cargo.toml b/primitives/staking/Cargo.toml index f92bca278..f383a5e88 100644 --- a/primitives/staking/Cargo.toml +++ b/primitives/staking/Cargo.toml @@ -13,6 +13,7 @@ readme = "README.md" targets = ["x86_64-unknown-linux-gnu"] [dependencies] +serde = { version = "1.0.136", optional = true } codec = { package = "parity-scale-codec", version = "3.2.2", default-features = false, features = ["derive"] } scale-info = { version = "2.5.0", default-features = false, features = ["derive"] } sp-core = { version = "7.0.0", default-features = false, path = "../core" } @@ -22,6 +23,7 @@ sp-std = { version = "5.0.0", default-features = false, path = "../std" } [features] default = ["std"] std = [ + "serde", "codec/std", "scale-info/std", "sp-core/std", diff --git a/primitives/staking/src/lib.rs b/primitives/staking/src/lib.rs index f32869655..57128bd32 100644 --- a/primitives/staking/src/lib.rs +++ b/primitives/staking/src/lib.rs @@ -20,6 +20,8 @@ //! A crate which contains primitives that are useful for implementation that uses staking //! approaches in general. Definitions related to sessions, slashing, etc go here. +use scale_info::TypeInfo; +use sp_core::RuntimeDebug; use sp_runtime::{DispatchError, DispatchResult}; use sp_std::{collections::btree_map::BTreeMap, vec::Vec}; @@ -31,6 +33,18 @@ pub type SessionIndex = u32; /// Counter for the number of eras that have passed. pub type EraIndex = u32; +/// Representation of the status of a staker. +#[derive(RuntimeDebug, TypeInfo)] +#[cfg_attr(feature = "std", derive(serde::Serialize, serde::Deserialize, PartialEq, Eq, Clone))] +pub enum StakerStatus { + /// Chilling. + Idle, + /// Declaring desire in validate, i.e author blocks. + Validator, + /// Declaring desire to nominate, delegate, or generally approve of the given set of others. + Nominator(Vec), +} + /// Trait describing something that implements a hook for any operations to perform when a staker is /// slashed. pub trait OnStakerSlash { @@ -57,7 +71,7 @@ impl OnStakerSlash for () { /// A struct that reflects stake that an account has in the staking system. Provides a set of /// methods to operate on it's properties. Aimed at making `StakingInterface` more concise. -pub struct Stake { +pub struct Stake { /// The total stake that `stash` has in the staking system. This includes the /// `active` stake, and any funds currently in the process of unbonding via /// [`StakingInterface::unbond`]. @@ -67,10 +81,10 @@ pub struct Stake { /// This is only guaranteed to reflect the amount locked by the staking system. If there are /// non-staking locks on the bonded pair's balance this amount is going to be larger in /// reality. - pub total: T::Balance, + pub total: Balance, /// The total amount of the stash's balance that will be at stake in any forthcoming /// rounds. - pub active: T::Balance, + pub active: Balance, } /// A generic representation of a staking implementation. @@ -109,21 +123,25 @@ pub trait StakingInterface { /// This should be the latest planned era that the staking system knows about. fn current_era() -> EraIndex; - /// Returns the stake of `who`. - fn stake(who: &Self::AccountId) -> Result, DispatchError>; + /// Returns the [`Stake`] of `who`. + fn stake(who: &Self::AccountId) -> Result, DispatchError>; + /// Total stake of a staker, `Err` if not a staker. fn total_stake(who: &Self::AccountId) -> Result { Self::stake(who).map(|s| s.total) } + /// Total active portion of a staker's [`Stake`], `Err` if not a staker. fn active_stake(who: &Self::AccountId) -> Result { Self::stake(who).map(|s| s.active) } + /// Returns whether a staker is unbonding, `Err` if not a staker at all. fn is_unbonding(who: &Self::AccountId) -> Result { Self::stake(who).map(|s| s.active != s.total) } + /// Returns whether a staker is FULLY unbonding, `Err` if not a staker at all. fn fully_unbond(who: &Self::AccountId) -> DispatchResult { Self::unbond(who, Self::stake(who)?.active) } @@ -174,9 +192,17 @@ pub trait StakingInterface { /// Checks whether an account `staker` has been exposed in an era. fn is_exposed_in_era(who: &Self::AccountId, era: &EraIndex) -> bool; + /// Return the status of the given staker, `None` if not staked at all. + fn status(who: &Self::AccountId) -> Result, DispatchError>; + /// Get the nominations of a stash, if they are a nominator, `None` otherwise. #[cfg(feature = "runtime-benchmarks")] - fn nominations(who: Self::AccountId) -> Option>; + fn nominations(who: &Self::AccountId) -> Option> { + match Self::status(who) { + Ok(StakerStatus::Nominator(t)) => Some(t), + _ => None, + } + } #[cfg(feature = "runtime-benchmarks")] fn add_era_stakers( From 5d1b74c29b51d5e18e525347c3ee57e86a20140a Mon Sep 17 00:00:00 2001 From: Liam Aharon Date: Fri, 28 Apr 2023 00:03:47 +1000 Subject: [PATCH 433/558] try-runtime-cli: improve ci stability (#14030) * hardcode 1 thread * ci stability * fix comment * improve comment * kick ci * kick ci * update expect message * improve comment * kick ci * kick ci * configure threads with env var * fix threads env var * fix threads env var --- utils/frame/remote-externalities/src/lib.rs | 31 ++++++++++++++++----- 1 file changed, 24 insertions(+), 7 deletions(-) diff --git a/utils/frame/remote-externalities/src/lib.rs b/utils/frame/remote-externalities/src/lib.rs index ea9b68ce2..283d2c280 100644 --- a/utils/frame/remote-externalities/src/lib.rs +++ b/utils/frame/remote-externalities/src/lib.rs @@ -40,7 +40,7 @@ use sp_core::{ pub use sp_io::TestExternalities; use sp_runtime::{traits::Block as BlockT, StateVersion}; use std::{ - cmp::max, + cmp::{max, min}, fs, num::NonZeroUsize, ops::{Deref, DerefMut}, @@ -157,10 +157,14 @@ impl Transport { } else { uri.clone() }; - let http_client = HttpClientBuilder::default().build(uri).map_err(|e| { - log::error!(target: LOG_TARGET, "error: {:?}", e); - "failed to build http client" - })?; + let http_client = HttpClientBuilder::default() + .max_request_body_size(u32::MAX) + .request_timeout(std::time::Duration::from_secs(60 * 5)) + .build(uri) + .map_err(|e| { + log::error!(target: LOG_TARGET, "error: {:?}", e); + "failed to build http client" + })?; *self = Self::RemoteClient(Arc::new(http_client)) } @@ -323,6 +327,7 @@ where B::Hash: DeserializeOwned, B::Header: DeserializeOwned, { + const DEFAULT_PARALLELISM: usize = 4; const BATCH_SIZE_INCREASE_FACTOR: f32 = 1.10; const BATCH_SIZE_DECREASE_FACTOR: f32 = 0.50; const INITIAL_BATCH_SIZE: usize = 5000; @@ -330,9 +335,21 @@ where const DEFAULT_KEY_DOWNLOAD_PAGE: u32 = 1000; /// Get the number of threads to use. + /// Cap the number of threads. Performance improvement beyond a small number of threads is + /// negligible, and too many threads can create issues with the HttpClient. fn threads() -> NonZeroUsize { - thread::available_parallelism() - .unwrap_or(NonZeroUsize::new(1usize).expect("4 is non-zero; qed")) + let avaliable = thread::available_parallelism() + .unwrap_or(NonZeroUsize::new(1usize).expect("1 is non-zero; qed")) + .get(); + assert!(avaliable > 0, "avaliable parallelism must be greater than 0"); + + let requested: usize = match std::env::var("TRY_RUNTIME_MAX_THREADS") { + Ok(n) => n.parse::().expect("TRY_RUNTIME_MAX_THREADS must be a number"), + Err(_) => Self::DEFAULT_PARALLELISM, + }; + assert!(requested > 0, "TRY_RUNTIME_MAX_THREADS must be greater than 0"); + return NonZeroUsize::new(min(requested, avaliable)) + .expect("requested and avaliable are non-zero; qed") } async fn rpc_get_storage( From f2b668074331d975a9ab06540d01713dc7c67086 Mon Sep 17 00:00:00 2001 From: Oliver Tale-Yazdi Date: Thu, 27 Apr 2023 16:08:08 +0200 Subject: [PATCH 434/558] FRAME: inherited call weight syntax (#13932) * First approach on pallet::call_weight Signed-off-by: Oliver Tale-Yazdi * Use attr on pallet::call instead Signed-off-by: Oliver Tale-Yazdi * Ui tests Signed-off-by: Oliver Tale-Yazdi * Rename to weight(prefix = ...)) Signed-off-by: Oliver Tale-Yazdi * Simplify to #[pallet::call(weight(T))] Signed-off-by: Oliver Tale-Yazdi * Add stray token error Signed-off-by: Oliver Tale-Yazdi * Cleanup Signed-off-by: Oliver Tale-Yazdi * Migrate remaining pallets Using script from https://github.com/ggwpez/substrate-scripts/blob/e1b5ea5b5b4018867f3e869fce6f448b4ba9d71f/frame-code-migration/src/call_weight.rs Signed-off-by: Oliver Tale-Yazdi * Try to add some docs Signed-off-by: Oliver Tale-Yazdi * Revert "Migrate remaining pallets" Lets do this as a follow-up, I dont want to bloat this small MR. This reverts commit 331d4b42d72de1dacaed714d69166fa1bc9c92dd. Signed-off-by: Oliver Tale-Yazdi * Renames Signed-off-by: Oliver Tale-Yazdi * Review fixes Co-authored-by: Sam Johnson Signed-off-by: Oliver Tale-Yazdi * Test weights Signed-off-by: Oliver Tale-Yazdi * Update UI tests Signed-off-by: Oliver Tale-Yazdi * Update frame/support/procedural/src/pallet/parse/mod.rs Co-authored-by: Muharem Ismailov * Remove old code Signed-off-by: Oliver Tale-Yazdi * Update docs Signed-off-by: Oliver Tale-Yazdi --------- Signed-off-by: Oliver Tale-Yazdi Co-authored-by: parity-processbot <> Co-authored-by: Muharem Ismailov --- frame/alliance/src/lib.rs | 12 +-- frame/alliance/src/tests.rs | 9 ++ frame/assets/src/lib.rs | 25 +----- frame/assets/src/tests.rs | 10 +++ frame/balances/src/lib.rs | 7 +- frame/balances/src/tests/mod.rs | 11 ++- frame/examples/basic/src/lib.rs | 5 +- frame/examples/basic/src/tests.rs | 1 + .../procedural/src/pallet/expand/call.rs | 39 ++++++--- .../procedural/src/pallet/parse/call.rs | 53 ++++++++---- .../procedural/src/pallet/parse/mod.rs | 83 +++++++++++++++++-- .../src/pallet/parse/pallet_struct.rs | 4 +- frame/support/src/lib.rs | 2 +- .../pallet_ui/call_missing_weight.stderr | 6 +- .../call_weight_inherited_invalid.rs | 50 +++++++++++ .../call_weight_inherited_invalid.stderr | 11 +++ .../call_weight_inherited_invalid2.rs | 53 ++++++++++++ .../call_weight_inherited_invalid2.stderr | 11 +++ .../call_weight_inherited_invalid3.rs | 53 ++++++++++++ .../call_weight_inherited_invalid3.stderr | 19 +++++ .../call_weight_inherited_invalid4.rs | 52 ++++++++++++ .../call_weight_inherited_invalid4.stderr | 11 +++ .../call_weight_inherited_invalid5.rs | 44 ++++++++++ .../call_weight_inherited_invalid5.stderr | 11 +++ .../tests/pallet_ui/dev_mode_without_arg.rs | 2 - .../pallet_ui/dev_mode_without_arg.stderr | 14 ++-- .../pallet_ui/pass/inherited_call_weight.rs | 51 ++++++++++++ .../pallet_ui/pass/inherited_call_weight2.rs | 57 +++++++++++++ .../pallet_ui/pass/inherited_call_weight3.rs | 54 ++++++++++++ .../pass/inherited_call_weight_dev_mode.rs | 30 +++++++ 30 files changed, 698 insertions(+), 92 deletions(-) create mode 100644 frame/support/test/tests/pallet_ui/call_weight_inherited_invalid.rs create mode 100644 frame/support/test/tests/pallet_ui/call_weight_inherited_invalid.stderr create mode 100644 frame/support/test/tests/pallet_ui/call_weight_inherited_invalid2.rs create mode 100644 frame/support/test/tests/pallet_ui/call_weight_inherited_invalid2.stderr create mode 100644 frame/support/test/tests/pallet_ui/call_weight_inherited_invalid3.rs create mode 100644 frame/support/test/tests/pallet_ui/call_weight_inherited_invalid3.stderr create mode 100644 frame/support/test/tests/pallet_ui/call_weight_inherited_invalid4.rs create mode 100644 frame/support/test/tests/pallet_ui/call_weight_inherited_invalid4.stderr create mode 100644 frame/support/test/tests/pallet_ui/call_weight_inherited_invalid5.rs create mode 100644 frame/support/test/tests/pallet_ui/call_weight_inherited_invalid5.stderr create mode 100644 frame/support/test/tests/pallet_ui/pass/inherited_call_weight.rs create mode 100644 frame/support/test/tests/pallet_ui/pass/inherited_call_weight2.rs create mode 100644 frame/support/test/tests/pallet_ui/pass/inherited_call_weight3.rs create mode 100644 frame/support/test/tests/pallet_ui/pass/inherited_call_weight_dev_mode.rs diff --git a/frame/alliance/src/lib.rs b/frame/alliance/src/lib.rs index d50e20ba0..86a64caaf 100644 --- a/frame/alliance/src/lib.rs +++ b/frame/alliance/src/lib.rs @@ -497,7 +497,7 @@ pub mod pallet { pub type UnscrupulousWebsites, I: 'static = ()> = StorageValue<_, BoundedVec, T::MaxUnscrupulousItems>, ValueQuery>; - #[pallet::call] + #[pallet::call(weight(>::WeightInfo))] impl, I: 'static> Pallet { /// Add a new proposal to be voted on. /// @@ -649,7 +649,6 @@ pub mod pallet { /// Set a new IPFS CID to the alliance rule. #[pallet::call_index(5)] - #[pallet::weight(T::WeightInfo::set_rule())] pub fn set_rule(origin: OriginFor, rule: Cid) -> DispatchResult { T::AdminOrigin::ensure_origin(origin)?; @@ -661,7 +660,6 @@ pub mod pallet { /// Make an announcement of a new IPFS CID about alliance issues. #[pallet::call_index(6)] - #[pallet::weight(T::WeightInfo::announce())] pub fn announce(origin: OriginFor, announcement: Cid) -> DispatchResult { T::AnnouncementOrigin::ensure_origin(origin)?; @@ -677,7 +675,6 @@ pub mod pallet { /// Remove an announcement. #[pallet::call_index(7)] - #[pallet::weight(T::WeightInfo::remove_announcement())] pub fn remove_announcement(origin: OriginFor, announcement: Cid) -> DispatchResult { T::AnnouncementOrigin::ensure_origin(origin)?; @@ -695,7 +692,6 @@ pub mod pallet { /// Submit oneself for candidacy. A fixed deposit is reserved. #[pallet::call_index(8)] - #[pallet::weight(T::WeightInfo::join_alliance())] pub fn join_alliance(origin: OriginFor) -> DispatchResult { let who = ensure_signed(origin)?; @@ -732,7 +728,6 @@ pub mod pallet { /// A Fellow can nominate someone to join the alliance as an Ally. There is no deposit /// required from the nominator or nominee. #[pallet::call_index(9)] - #[pallet::weight(T::WeightInfo::nominate_ally())] pub fn nominate_ally(origin: OriginFor, who: AccountIdLookupOf) -> DispatchResult { let nominator = ensure_signed(origin)?; ensure!(Self::has_voting_rights(&nominator), Error::::NoVotingRights); @@ -757,7 +752,6 @@ pub mod pallet { /// Elevate an Ally to Fellow. #[pallet::call_index(10)] - #[pallet::weight(T::WeightInfo::elevate_ally())] pub fn elevate_ally(origin: OriginFor, ally: AccountIdLookupOf) -> DispatchResult { T::MembershipManager::ensure_origin(origin)?; let ally = T::Lookup::lookup(ally)?; @@ -774,7 +768,6 @@ pub mod pallet { /// As a member, give a retirement notice and start a retirement period required to pass in /// order to retire. #[pallet::call_index(11)] - #[pallet::weight(T::WeightInfo::give_retirement_notice())] pub fn give_retirement_notice(origin: OriginFor) -> DispatchResult { let who = ensure_signed(origin)?; let role = Self::member_role_of(&who).ok_or(Error::::NotMember)?; @@ -797,7 +790,6 @@ pub mod pallet { /// This can only be done once you have called `give_retirement_notice` and the /// `RetirementPeriod` has passed. #[pallet::call_index(12)] - #[pallet::weight(T::WeightInfo::retire())] pub fn retire(origin: OriginFor) -> DispatchResult { let who = ensure_signed(origin)?; let retirement_period_end = RetiringMembers::::get(&who) @@ -820,7 +812,6 @@ pub mod pallet { /// Kick a member from the Alliance and slash its deposit. #[pallet::call_index(13)] - #[pallet::weight(T::WeightInfo::kick_member())] pub fn kick_member(origin: OriginFor, who: AccountIdLookupOf) -> DispatchResult { T::MembershipManager::ensure_origin(origin)?; let member = T::Lookup::lookup(who)?; @@ -922,7 +913,6 @@ pub mod pallet { /// who do not want to leave the Alliance but do not have the capacity to participate /// operationally for some time. #[pallet::call_index(17)] - #[pallet::weight(T::WeightInfo::abdicate_fellow_status())] pub fn abdicate_fellow_status(origin: OriginFor) -> DispatchResult { let who = ensure_signed(origin)?; let role = Self::member_role_of(&who).ok_or(Error::::NotMember)?; diff --git a/frame/alliance/src/tests.rs b/frame/alliance/src/tests.rs index 594259546..de7cda471 100644 --- a/frame/alliance/src/tests.rs +++ b/frame/alliance/src/tests.rs @@ -629,3 +629,12 @@ fn remove_unscrupulous_items_works() { assert_eq!(Alliance::unscrupulous_accounts(), Vec::::new()); }); } + +#[test] +fn weights_sane() { + let info = crate::Call::::join_alliance {}.get_dispatch_info(); + assert_eq!(<() as crate::WeightInfo>::join_alliance(), info.weight); + + let info = crate::Call::::nominate_ally { who: 10 }.get_dispatch_info(); + assert_eq!(<() as crate::WeightInfo>::nominate_ally(), info.weight); +} diff --git a/frame/assets/src/lib.rs b/frame/assets/src/lib.rs index 859a13243..d32b407e6 100644 --- a/frame/assets/src/lib.rs +++ b/frame/assets/src/lib.rs @@ -568,7 +568,7 @@ pub mod pallet { CallbackFailed, } - #[pallet::call] + #[pallet::call(weight(>::WeightInfo))] impl, I: 'static> Pallet { /// Issue a new class of fungible assets from a public origin. /// @@ -590,7 +590,6 @@ pub mod pallet { /// /// Weight: `O(1)` #[pallet::call_index(0)] - #[pallet::weight(T::WeightInfo::create())] pub fn create( origin: OriginFor, id: T::AssetIdParameter, @@ -654,7 +653,6 @@ pub mod pallet { /// /// Weight: `O(1)` #[pallet::call_index(1)] - #[pallet::weight(T::WeightInfo::force_create())] pub fn force_create( origin: OriginFor, id: T::AssetIdParameter, @@ -680,7 +678,6 @@ pub mod pallet { /// /// The asset class must be frozen before calling `start_destroy`. #[pallet::call_index(2)] - #[pallet::weight(T::WeightInfo::start_destroy())] pub fn start_destroy(origin: OriginFor, id: T::AssetIdParameter) -> DispatchResult { let maybe_check_owner = match T::ForceOrigin::try_origin(origin) { Ok(_) => None, @@ -749,7 +746,6 @@ pub mod pallet { /// /// Each successful call emits the `Event::Destroyed` event. #[pallet::call_index(5)] - #[pallet::weight(T::WeightInfo::finish_destroy())] pub fn finish_destroy(origin: OriginFor, id: T::AssetIdParameter) -> DispatchResult { let _ = ensure_signed(origin)?; let id: T::AssetId = id.into(); @@ -769,7 +765,6 @@ pub mod pallet { /// Weight: `O(1)` /// Modes: Pre-existing balance of `beneficiary`; Account pre-existence of `beneficiary`. #[pallet::call_index(6)] - #[pallet::weight(T::WeightInfo::mint())] pub fn mint( origin: OriginFor, id: T::AssetIdParameter, @@ -799,7 +794,6 @@ pub mod pallet { /// Weight: `O(1)` /// Modes: Post-existence of `who`; Pre & post Zombie-status of `who`. #[pallet::call_index(7)] - #[pallet::weight(T::WeightInfo::burn())] pub fn burn( origin: OriginFor, id: T::AssetIdParameter, @@ -834,7 +828,6 @@ pub mod pallet { /// Modes: Pre-existence of `target`; Post-existence of sender; Account pre-existence of /// `target`. #[pallet::call_index(8)] - #[pallet::weight(T::WeightInfo::transfer())] pub fn transfer( origin: OriginFor, id: T::AssetIdParameter, @@ -868,7 +861,6 @@ pub mod pallet { /// Modes: Pre-existence of `target`; Post-existence of sender; Account pre-existence of /// `target`. #[pallet::call_index(9)] - #[pallet::weight(T::WeightInfo::transfer_keep_alive())] pub fn transfer_keep_alive( origin: OriginFor, id: T::AssetIdParameter, @@ -903,7 +895,6 @@ pub mod pallet { /// Modes: Pre-existence of `dest`; Post-existence of `source`; Account pre-existence of /// `dest`. #[pallet::call_index(10)] - #[pallet::weight(T::WeightInfo::force_transfer())] pub fn force_transfer( origin: OriginFor, id: T::AssetIdParameter, @@ -931,7 +922,6 @@ pub mod pallet { /// /// Weight: `O(1)` #[pallet::call_index(11)] - #[pallet::weight(T::WeightInfo::freeze())] pub fn freeze( origin: OriginFor, id: T::AssetIdParameter, @@ -968,7 +958,6 @@ pub mod pallet { /// /// Weight: `O(1)` #[pallet::call_index(12)] - #[pallet::weight(T::WeightInfo::thaw())] pub fn thaw( origin: OriginFor, id: T::AssetIdParameter, @@ -1004,7 +993,6 @@ pub mod pallet { /// /// Weight: `O(1)` #[pallet::call_index(13)] - #[pallet::weight(T::WeightInfo::freeze_asset())] pub fn freeze_asset(origin: OriginFor, id: T::AssetIdParameter) -> DispatchResult { let origin = ensure_signed(origin)?; let id: T::AssetId = id.into(); @@ -1031,7 +1019,6 @@ pub mod pallet { /// /// Weight: `O(1)` #[pallet::call_index(14)] - #[pallet::weight(T::WeightInfo::thaw_asset())] pub fn thaw_asset(origin: OriginFor, id: T::AssetIdParameter) -> DispatchResult { let origin = ensure_signed(origin)?; let id: T::AssetId = id.into(); @@ -1059,7 +1046,6 @@ pub mod pallet { /// /// Weight: `O(1)` #[pallet::call_index(15)] - #[pallet::weight(T::WeightInfo::transfer_ownership())] pub fn transfer_ownership( origin: OriginFor, id: T::AssetIdParameter, @@ -1103,7 +1089,6 @@ pub mod pallet { /// /// Weight: `O(1)` #[pallet::call_index(16)] - #[pallet::weight(T::WeightInfo::set_team())] pub fn set_team( origin: OriginFor, id: T::AssetIdParameter, @@ -1173,7 +1158,6 @@ pub mod pallet { /// /// Weight: `O(1)` #[pallet::call_index(18)] - #[pallet::weight(T::WeightInfo::clear_metadata())] pub fn clear_metadata(origin: OriginFor, id: T::AssetIdParameter) -> DispatchResult { let origin = ensure_signed(origin)?; let id: T::AssetId = id.into(); @@ -1257,7 +1241,6 @@ pub mod pallet { /// /// Weight: `O(1)` #[pallet::call_index(20)] - #[pallet::weight(T::WeightInfo::force_clear_metadata())] pub fn force_clear_metadata( origin: OriginFor, id: T::AssetIdParameter, @@ -1297,7 +1280,6 @@ pub mod pallet { /// /// Weight: `O(1)` #[pallet::call_index(21)] - #[pallet::weight(T::WeightInfo::force_asset_status())] pub fn force_asset_status( origin: OriginFor, id: T::AssetIdParameter, @@ -1354,7 +1336,6 @@ pub mod pallet { /// /// Weight: `O(1)` #[pallet::call_index(22)] - #[pallet::weight(T::WeightInfo::approve_transfer())] pub fn approve_transfer( origin: OriginFor, id: T::AssetIdParameter, @@ -1381,7 +1362,6 @@ pub mod pallet { /// /// Weight: `O(1)` #[pallet::call_index(23)] - #[pallet::weight(T::WeightInfo::cancel_approval())] pub fn cancel_approval( origin: OriginFor, id: T::AssetIdParameter, @@ -1418,7 +1398,6 @@ pub mod pallet { /// /// Weight: `O(1)` #[pallet::call_index(24)] - #[pallet::weight(T::WeightInfo::force_cancel_approval())] pub fn force_cancel_approval( origin: OriginFor, id: T::AssetIdParameter, @@ -1468,7 +1447,6 @@ pub mod pallet { /// /// Weight: `O(1)` #[pallet::call_index(25)] - #[pallet::weight(T::WeightInfo::transfer_approved())] pub fn transfer_approved( origin: OriginFor, id: T::AssetIdParameter, @@ -1531,7 +1509,6 @@ pub mod pallet { /// /// Emits `AssetMinBalanceChanged` event when successful. #[pallet::call_index(28)] - #[pallet::weight(T::WeightInfo::set_min_balance())] pub fn set_min_balance( origin: OriginFor, id: T::AssetIdParameter, diff --git a/frame/assets/src/tests.rs b/frame/assets/src/tests.rs index b47fb6486..88ba92d06 100644 --- a/frame/assets/src/tests.rs +++ b/frame/assets/src/tests.rs @@ -21,6 +21,7 @@ use super::*; use crate::{mock::*, Error}; use frame_support::{ assert_noop, assert_ok, + dispatch::GetDispatchInfo, traits::{fungibles::InspectEnumerable, tokens::Preservation::Protect, Currency}, }; use pallet_balances::Error as BalancesError; @@ -1355,3 +1356,12 @@ fn multiple_transfer_alls_work_ok() { assert_eq!(Balances::free_balance(&1337), 100); }); } + +#[test] +fn weights_sane() { + let info = crate::Call::::create { id: 10, admin: 4, min_balance: 3 }.get_dispatch_info(); + assert_eq!(<() as crate::WeightInfo>::create(), info.weight); + + let info = crate::Call::::finish_destroy { id: 10 }.get_dispatch_info(); + assert_eq!(<() as crate::WeightInfo>::finish_destroy(), info.weight); +} diff --git a/frame/balances/src/lib.rs b/frame/balances/src/lib.rs index c87b01d77..b3dc77a9b 100644 --- a/frame/balances/src/lib.rs +++ b/frame/balances/src/lib.rs @@ -526,7 +526,7 @@ pub mod pallet { } } - #[pallet::call] + #[pallet::call(weight(>::WeightInfo))] impl, I: 'static> Pallet { /// Transfer some liquid free balance to another account. /// @@ -536,7 +536,6 @@ pub mod pallet { /// /// The dispatch origin for this call must be `Signed` by the transactor. #[pallet::call_index(0)] - #[pallet::weight(T::WeightInfo::transfer_allow_death())] pub fn transfer_allow_death( origin: OriginFor, dest: AccountIdLookupOf, @@ -598,7 +597,6 @@ pub mod pallet { /// Exactly as `transfer_allow_death`, except the origin must be root and the source account /// may be specified. #[pallet::call_index(2)] - #[pallet::weight(T::WeightInfo::force_transfer())] pub fn force_transfer( origin: OriginFor, source: AccountIdLookupOf, @@ -619,7 +617,6 @@ pub mod pallet { /// /// [`transfer_allow_death`]: struct.Pallet.html#method.transfer #[pallet::call_index(3)] - #[pallet::weight(T::WeightInfo::transfer_keep_alive())] pub fn transfer_keep_alive( origin: OriginFor, dest: AccountIdLookupOf, @@ -647,7 +644,6 @@ pub mod pallet { /// transfer everything except at least the existential deposit, which will guarantee to /// keep the sender account alive (true). #[pallet::call_index(4)] - #[pallet::weight(T::WeightInfo::transfer_all())] pub fn transfer_all( origin: OriginFor, dest: AccountIdLookupOf, @@ -674,7 +670,6 @@ pub mod pallet { /// /// Can only be called by ROOT. #[pallet::call_index(5)] - #[pallet::weight(T::WeightInfo::force_unreserve())] pub fn force_unreserve( origin: OriginFor, who: AccountIdLookupOf, diff --git a/frame/balances/src/tests/mod.rs b/frame/balances/src/tests/mod.rs index 68e7e8203..9b451f36d 100644 --- a/frame/balances/src/tests/mod.rs +++ b/frame/balances/src/tests/mod.rs @@ -23,7 +23,7 @@ use crate::{self as pallet_balances, AccountData, Config, CreditOf, Error, Palle use codec::{Decode, Encode, MaxEncodedLen}; use frame_support::{ assert_err, assert_noop, assert_ok, assert_storage_noop, - dispatch::DispatchInfo, + dispatch::{DispatchInfo, GetDispatchInfo}, parameter_types, traits::{ tokens::fungible, ConstU32, ConstU64, ConstU8, Imbalance as ImbalanceT, OnUnbalanced, @@ -294,3 +294,12 @@ pub fn events() -> Vec { pub fn info_from_weight(w: Weight) -> DispatchInfo { DispatchInfo { weight: w, ..Default::default() } } + +#[test] +fn weights_sane() { + let info = crate::Call::::transfer_allow_death { dest: 10, value: 4 }.get_dispatch_info(); + assert_eq!(<() as crate::WeightInfo>::transfer_allow_death(), info.weight); + + let info = crate::Call::::force_unreserve { who: 10, amount: 4 }.get_dispatch_info(); + assert_eq!(<() as crate::WeightInfo>::force_unreserve(), info.weight); +} diff --git a/frame/examples/basic/src/lib.rs b/frame/examples/basic/src/lib.rs index 6665c3b78..bbeaac19f 100644 --- a/frame/examples/basic/src/lib.rs +++ b/frame/examples/basic/src/lib.rs @@ -441,7 +441,7 @@ pub mod pallet { // against them as the first thing you do in your function. There are three convenience calls // in system that do the matching for you and return a convenient result: `ensure_signed`, // `ensure_root` and `ensure_none`. - #[pallet::call] + #[pallet::call(weight(::WeightInfo))] impl Pallet { /// This is your public interface. Be extremely careful. /// This is just a simple example of how to interact with the pallet from the external @@ -497,9 +497,6 @@ pub mod pallet { // The weight for this extrinsic we rely on the auto-generated `WeightInfo` from the // benchmark toolchain. #[pallet::call_index(0)] - #[pallet::weight( - ::WeightInfo::accumulate_dummy() - )] pub fn accumulate_dummy(origin: OriginFor, increase_by: T::Balance) -> DispatchResult { // This is a public call, so we ensure that the origin is some signed account. let _sender = ensure_signed(origin)?; diff --git a/frame/examples/basic/src/tests.rs b/frame/examples/basic/src/tests.rs index d9a8a4e8e..1d9cf81a5 100644 --- a/frame/examples/basic/src/tests.rs +++ b/frame/examples/basic/src/tests.rs @@ -192,6 +192,7 @@ fn weights_work() { // aka. `let info = as GetDispatchInfo>::get_dispatch_info(&default_call);` // TODO: account for proof size weight assert!(info1.weight.ref_time() > 0); + assert_eq!(info1.weight, ::WeightInfo::accumulate_dummy()); // `set_dummy` is simpler than `accumulate_dummy`, and the weight // should be less. diff --git a/frame/support/procedural/src/pallet/expand/call.rs b/frame/support/procedural/src/pallet/expand/call.rs index aa5af9d54..f17fdc81a 100644 --- a/frame/support/procedural/src/pallet/expand/call.rs +++ b/frame/support/procedural/src/pallet/expand/call.rs @@ -15,8 +15,12 @@ // See the License for the specific language governing permissions and // limitations under the License. -use crate::{pallet::Def, COUNTER}; -use quote::ToTokens; +use crate::{ + pallet::{parse::call::CallWeightDef, Def}, + COUNTER, +}; +use proc_macro2::TokenStream as TokenStream2; +use quote::{quote, ToTokens}; use syn::spanned::Spanned; /// @@ -74,15 +78,12 @@ pub fn expand_call(def: &mut Def) -> proc_macro2::TokenStream { call_index_warnings.push(warning); } - let fn_weight = methods.iter().map(|method| &method.weight); + let mut fn_weight = Vec::::new(); let mut weight_warnings = Vec::new(); - for weight in fn_weight.clone() { - if def.dev_mode { - continue - } - - match weight { - syn::Expr::Lit(lit) => { + for method in &methods { + match &method.weight { + CallWeightDef::DevModeDefault => fn_weight.push(syn::parse_quote!(0)), + CallWeightDef::Immediate(e @ syn::Expr::Lit(lit)) if !def.dev_mode => { let warning = proc_macro_warning::Warning::new_deprecated("ConstantWeight") .index(weight_warnings.len()) .old("use hard-coded constant as call weight") @@ -91,10 +92,26 @@ pub fn expand_call(def: &mut Def) -> proc_macro2::TokenStream { .span(lit.span()) .build(); weight_warnings.push(warning); + fn_weight.push(e.into_token_stream()); + }, + CallWeightDef::Immediate(e) => fn_weight.push(e.into_token_stream()), + CallWeightDef::Inherited => { + let pallet_weight = def + .call + .as_ref() + .expect("we have methods; we have calls; qed") + .inherited_call_weight + .as_ref() + .expect("the parser prevents this"); + + // Expand `<::WeightInfo>::call_name()`. + let t = &pallet_weight.typename; + let n = &method.name; + fn_weight.push(quote!({ < #t > :: #n () })); }, - _ => {}, } } + debug_assert_eq!(fn_weight.len(), methods.len()); let fn_doc = methods.iter().map(|method| &method.docs).collect::>(); diff --git a/frame/support/procedural/src/pallet/parse/call.rs b/frame/support/procedural/src/pallet/parse/call.rs index ae1c039c9..90631f264 100644 --- a/frame/support/procedural/src/pallet/parse/call.rs +++ b/frame/support/procedural/src/pallet/parse/call.rs @@ -15,7 +15,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -use super::helper; +use super::{helper, InheritedCallWeightAttr}; use frame_support_procedural_tools::get_doc_literals; use quote::ToTokens; use std::collections::HashMap; @@ -46,6 +46,23 @@ pub struct CallDef { pub attr_span: proc_macro2::Span, /// Docs, specified on the impl Block. pub docs: Vec, + /// The optional `weight` attribute on the `pallet::call`. + pub inherited_call_weight: Option, +} + +/// The weight of a call. +#[derive(Clone)] +pub enum CallWeightDef { + /// Explicitly set on the call itself with `#[pallet::weight(…)]`. This value is used. + Immediate(syn::Expr), + + /// The default value that should be set for dev-mode pallets. Usually zero. + DevModeDefault, + + /// Inherits whatever value is configured on the pallet level. + /// + /// The concrete value is not known at this point. + Inherited, } /// Definition of dispatchable typically: `#[weight...] fn foo(origin .., param1: ...) -> ..` @@ -55,8 +72,8 @@ pub struct CallVariantDef { pub name: syn::Ident, /// Information on args: `(is_compact, name, type)` pub args: Vec<(bool, syn::Ident, Box)>, - /// Weight formula. - pub weight: syn::Expr, + /// Weight for the call. + pub weight: CallWeightDef, /// Call index of the dispatchable. pub call_index: u8, /// Whether an explicit call index was specified. @@ -151,6 +168,7 @@ impl CallDef { index: usize, item: &mut syn::Item, dev_mode: bool, + inherited_call_weight: Option, ) -> syn::Result { let item_impl = if let syn::Item::Impl(item) = item { item @@ -228,17 +246,23 @@ impl CallDef { weight_attrs.push(FunctionAttr::Weight(empty_weight)); } - if weight_attrs.len() != 1 { - let msg = if weight_attrs.is_empty() { - "Invalid pallet::call, requires weight attribute i.e. `#[pallet::weight($expr)]`" - } else { - "Invalid pallet::call, too many weight attributes given" - }; - return Err(syn::Error::new(method.sig.span(), msg)) - } - let weight = match weight_attrs.pop().unwrap() { - FunctionAttr::Weight(w) => w, - _ => unreachable!("checked during creation of the let binding"), + let weight = match weight_attrs.len() { + 0 if inherited_call_weight.is_some() => CallWeightDef::Inherited, + 0 if dev_mode => CallWeightDef::DevModeDefault, + 0 => return Err(syn::Error::new( + method.sig.span(), + "A pallet::call requires either a concrete `#[pallet::weight($expr)]` or an + inherited weight from the `#[pallet:call(weight($type))]` attribute, but + none were given.", + )), + 1 => match weight_attrs.pop().unwrap() { + FunctionAttr::Weight(w) => CallWeightDef::Immediate(w), + _ => unreachable!("checked during creation of the let binding"), + }, + _ => { + let msg = "Invalid pallet::call, too many weight attributes given"; + return Err(syn::Error::new(method.sig.span(), msg)) + }, }; if call_idx_attrs.len() > 1 { @@ -321,6 +345,7 @@ impl CallDef { methods, where_clause: item_impl.generics.where_clause.clone(), docs: get_doc_literals(&item_impl.attrs), + inherited_call_weight, }) } } diff --git a/frame/support/procedural/src/pallet/parse/mod.rs b/frame/support/procedural/src/pallet/parse/mod.rs index 06b9899d0..770cba68c 100644 --- a/frame/support/procedural/src/pallet/parse/mod.rs +++ b/frame/support/procedural/src/pallet/parse/mod.rs @@ -110,8 +110,8 @@ impl Def { let m = hooks::HooksDef::try_from(span, index, item)?; hooks = Some(m); }, - Some(PalletAttr::RuntimeCall(span)) if call.is_none() => - call = Some(call::CallDef::try_from(span, index, item, dev_mode)?), + Some(PalletAttr::RuntimeCall(cw, span)) if call.is_none() => + call = Some(call::CallDef::try_from(span, index, item, dev_mode, cw)?), Some(PalletAttr::Error(span)) if error.is_none() => error = Some(error::ErrorDef::try_from(span, index, item)?), Some(PalletAttr::RuntimeEvent(span)) if event.is_none() => @@ -402,6 +402,7 @@ impl GenericKind { mod keyword { syn::custom_keyword!(origin); syn::custom_keyword!(call); + syn::custom_keyword!(weight); syn::custom_keyword!(event); syn::custom_keyword!(config); syn::custom_keyword!(hooks); @@ -425,7 +426,44 @@ enum PalletAttr { Config(proc_macro2::Span), Pallet(proc_macro2::Span), Hooks(proc_macro2::Span), - RuntimeCall(proc_macro2::Span), + /// A `#[pallet::call]` with optional attributes to specialize the behaviour. + /// + /// # Attributes + /// + /// Each attribute `attr` can take the form of `#[pallet::call(attr = …)]` or + /// `#[pallet::call(attr(…))]`. The possible attributes are: + /// + /// ## `weight` + /// + /// Can be used to reduce the repetitive weight annotation in the trivial case. It accepts one + /// argument that is expected to be an implementation of the `WeightInfo` or something that + /// behaves syntactically equivalent. This allows to annotate a `WeightInfo` for all the calls. + /// Now each call does not need to specify its own `#[pallet::weight]` but can instead use the + /// one from the `#[pallet::call]` definition. So instead of having to write it on each call: + /// + /// ```ignore + /// #[pallet::call] + /// impl Pallet { + /// #[pallet::weight(T::WeightInfo::create())] + /// pub fn create( + /// ``` + /// you can now omit it on the call itself, if the name of the weigh function matches the call: + /// + /// ```ignore + /// #[pallet::call(weight = ::WeightInfo)] + /// impl Pallet { + /// pub fn create( + /// ``` + /// + /// It is possible to use this syntax together with instantiated pallets by using `Config` + /// instead. + /// + /// ### Dev Mode + /// + /// Normally the `dev_mode` sets all weights of calls without a `#[pallet::weight]` annotation + /// to zero. Now when there is a `weight` attribute on the `#[pallet::call]`, then that is used + /// instead of the zero weight. So to say: it works together with `dev_mode`. + RuntimeCall(Option, proc_macro2::Span), Error(proc_macro2::Span), RuntimeEvent(proc_macro2::Span), RuntimeOrigin(proc_macro2::Span), @@ -445,7 +483,7 @@ impl PalletAttr { Self::Config(span) => *span, Self::Pallet(span) => *span, Self::Hooks(span) => *span, - Self::RuntimeCall(span) => *span, + Self::RuntimeCall(_, span) => *span, Self::Error(span) => *span, Self::RuntimeEvent(span) => *span, Self::RuntimeOrigin(span) => *span, @@ -477,7 +515,12 @@ impl syn::parse::Parse for PalletAttr { } else if lookahead.peek(keyword::hooks) { Ok(PalletAttr::Hooks(content.parse::()?.span())) } else if lookahead.peek(keyword::call) { - Ok(PalletAttr::RuntimeCall(content.parse::()?.span())) + let span = content.parse::().expect("peeked").span(); + let attr = match content.is_empty() { + true => None, + false => Some(InheritedCallWeightAttr::parse(&content)?), + }; + Ok(PalletAttr::RuntimeCall(attr, span)) } else if lookahead.peek(keyword::error) { Ok(PalletAttr::Error(content.parse::()?.span())) } else if lookahead.peek(keyword::event) { @@ -505,3 +548,33 @@ impl syn::parse::Parse for PalletAttr { } } } + +/// The optional weight annotation on a `#[pallet::call]` like `#[pallet::call(weight($type))]`. +#[derive(Clone)] +pub struct InheritedCallWeightAttr { + pub typename: syn::Type, + pub span: proc_macro2::Span, +} + +impl syn::parse::Parse for InheritedCallWeightAttr { + // Parses `(weight($type))` or `(weight = $type)`. + fn parse(input: syn::parse::ParseStream) -> syn::Result { + let content; + syn::parenthesized!(content in input); + content.parse::()?; + let lookahead = content.lookahead1(); + + let buffer = if lookahead.peek(syn::token::Paren) { + let inner; + syn::parenthesized!(inner in content); + inner + } else if lookahead.peek(syn::Token![=]) { + content.parse::().expect("peeked"); + content + } else { + return Err(lookahead.error()) + }; + + Ok(Self { typename: buffer.parse()?, span: input.span() }) + } +} diff --git a/frame/support/procedural/src/pallet/parse/pallet_struct.rs b/frame/support/procedural/src/pallet/parse/pallet_struct.rs index 98312d065..f4af86aa3 100644 --- a/frame/support/procedural/src/pallet/parse/pallet_struct.rs +++ b/frame/support/procedural/src/pallet/parse/pallet_struct.rs @@ -61,8 +61,8 @@ pub enum PalletStructAttr { impl PalletStructAttr { fn span(&self) -> proc_macro2::Span { match self { - Self::GenerateStore { span, .. } => *span, - Self::WithoutStorageInfoTrait(span) => *span, + Self::GenerateStore { span, .. } | + Self::WithoutStorageInfoTrait(span) | Self::StorageVersion { span, .. } => *span, } } diff --git a/frame/support/src/lib.rs b/frame/support/src/lib.rs index 0ee82b9f1..138b7b55b 100644 --- a/frame/support/src/lib.rs +++ b/frame/support/src/lib.rs @@ -2600,7 +2600,7 @@ pub mod pallet_prelude { /// #[pallet::extra_constants] /// impl, I: 'static> Pallet { /// /// Some description -/// fn exra_constant_name() -> u128 { 4u128 } +/// fn extra_constant_name() -> u128 { 4u128 } /// } /// /// #[pallet::pallet] diff --git a/frame/support/test/tests/pallet_ui/call_missing_weight.stderr b/frame/support/test/tests/pallet_ui/call_missing_weight.stderr index ec45d4788..0a6cf1657 100644 --- a/frame/support/test/tests/pallet_ui/call_missing_weight.stderr +++ b/frame/support/test/tests/pallet_ui/call_missing_weight.stderr @@ -1,5 +1,7 @@ -error: Invalid pallet::call, requires weight attribute i.e. `#[pallet::weight($expr)]` - --> $DIR/call_missing_weight.rs:17:7 +error: A pallet::call requires either a concrete `#[pallet::weight($expr)]` or an + inherited weight from the `#[pallet:call(weight($type))]` attribute, but + none were given. + --> tests/pallet_ui/call_missing_weight.rs:17:7 | 17 | pub fn foo(origin: OriginFor) -> DispatchResultWithPostInfo {} | ^^ diff --git a/frame/support/test/tests/pallet_ui/call_weight_inherited_invalid.rs b/frame/support/test/tests/pallet_ui/call_weight_inherited_invalid.rs new file mode 100644 index 000000000..ff235e986 --- /dev/null +++ b/frame/support/test/tests/pallet_ui/call_weight_inherited_invalid.rs @@ -0,0 +1,50 @@ +use frame_support::pallet_prelude::*; + +pub trait WeightInfo { + fn foo() -> Weight; +} + +#[frame_support::pallet] +mod pallet { + use super::*; + + #[pallet::config] + pub trait Config: frame_system::Config { + type WeightInfo: crate::WeightInfo; + } + + #[pallet::pallet] + pub struct Pallet(core::marker::PhantomData); + + #[pallet::call(invalid)] + impl Pallet { + #[pallet::call_index(0)] + pub fn foo(_: OriginFor) -> DispatchResult { + Ok(()) + } + } +} + +#[frame_support::pallet] +mod assign { + use super::*; + + #[pallet::config] + pub trait Config: frame_system::Config { + type WeightInfo: crate::WeightInfo; + } + + #[pallet::pallet] + pub struct Pallet(core::marker::PhantomData); + + #[pallet::call = invalid] + impl Pallet { + #[pallet::call_index(0)] + pub fn foo(_: OriginFor) -> DispatchResult { + Ok(()) + } + } +} + +fn main() { +} diff --git a/frame/support/test/tests/pallet_ui/call_weight_inherited_invalid.stderr b/frame/support/test/tests/pallet_ui/call_weight_inherited_invalid.stderr new file mode 100644 index 000000000..7eed646e7 --- /dev/null +++ b/frame/support/test/tests/pallet_ui/call_weight_inherited_invalid.stderr @@ -0,0 +1,11 @@ +error: expected `weight` + --> tests/pallet_ui/call_weight_inherited_invalid.rs:19:17 + | +19 | #[pallet::call(invalid)] + | ^^^^^^^ + +error: expected parentheses + --> tests/pallet_ui/call_weight_inherited_invalid.rs:40:17 + | +40 | #[pallet::call = invalid] + | ^ diff --git a/frame/support/test/tests/pallet_ui/call_weight_inherited_invalid2.rs b/frame/support/test/tests/pallet_ui/call_weight_inherited_invalid2.rs new file mode 100644 index 000000000..76ccf5db2 --- /dev/null +++ b/frame/support/test/tests/pallet_ui/call_weight_inherited_invalid2.rs @@ -0,0 +1,53 @@ +// Weight is an ident instead of a type. + +use frame_support::pallet_prelude::*; +use frame_system::pallet_prelude::*; + +pub trait WeightInfo { + fn foo() -> Weight; +} + +#[frame_support::pallet] +mod parentheses { + use super::*; + + #[pallet::config] + pub trait Config: frame_system::Config { + type WeightInfo: crate::WeightInfo; + } + + #[pallet::pallet] + pub struct Pallet(core::marker::PhantomData); + + #[pallet::call(weight(prefix))] + impl Pallet { + #[pallet::call_index(0)] + pub fn foo(_: OriginFor) -> DispatchResult { + Ok(()) + } + } +} + +#[frame_support::pallet] +mod assign { + use super::*; + + #[pallet::config] + pub trait Config: frame_system::Config { + type WeightInfo: crate::WeightInfo; + } + + #[pallet::pallet] + pub struct Pallet(core::marker::PhantomData); + + #[pallet::call(weight = prefix)] + impl Pallet { + #[pallet::call_index(0)] + pub fn foo(_: OriginFor) -> DispatchResult { + Ok(()) + } + } +} + +fn main() { +} diff --git a/frame/support/test/tests/pallet_ui/call_weight_inherited_invalid2.stderr b/frame/support/test/tests/pallet_ui/call_weight_inherited_invalid2.stderr new file mode 100644 index 000000000..29f3b6bfd --- /dev/null +++ b/frame/support/test/tests/pallet_ui/call_weight_inherited_invalid2.stderr @@ -0,0 +1,11 @@ +error[E0412]: cannot find type `prefix` in this scope + --> tests/pallet_ui/call_weight_inherited_invalid2.rs:22:24 + | +22 | #[pallet::call(weight(prefix))] + | ^^^^^^ not found in this scope + +error[E0412]: cannot find type `prefix` in this scope + --> tests/pallet_ui/call_weight_inherited_invalid2.rs:43:26 + | +43 | #[pallet::call(weight = prefix)] + | ^^^^^^ not found in this scope diff --git a/frame/support/test/tests/pallet_ui/call_weight_inherited_invalid3.rs b/frame/support/test/tests/pallet_ui/call_weight_inherited_invalid3.rs new file mode 100644 index 000000000..b31bc0ae2 --- /dev/null +++ b/frame/support/test/tests/pallet_ui/call_weight_inherited_invalid3.rs @@ -0,0 +1,53 @@ +// Call weight is an LitInt instead of a type. + +use frame_support::pallet_prelude::*; +use frame_system::pallet_prelude::*; + +pub trait WeightInfo { + fn foo() -> Weight; +} + +#[frame_support::pallet] +mod parentheses { + use super::*; + + #[pallet::config] + pub trait Config: frame_system::Config { + type WeightInfo: crate::WeightInfo; + } + + #[pallet::pallet] + pub struct Pallet(core::marker::PhantomData); + + #[pallet::call(weight(123))] + impl Pallet { + #[pallet::call_index(0)] + pub fn foo(_: OriginFor) -> DispatchResult { + Ok(()) + } + } +} + +#[frame_support::pallet] +mod assign { + use super::*; + + #[pallet::config] + pub trait Config: frame_system::Config { + type WeightInfo: crate::WeightInfo; + } + + #[pallet::pallet] + pub struct Pallet(core::marker::PhantomData); + + #[pallet::call(weight = 123)] + impl Pallet { + #[pallet::call_index(0)] + pub fn foo(_: OriginFor) -> DispatchResult { + Ok(()) + } + } +} + +fn main() { +} diff --git a/frame/support/test/tests/pallet_ui/call_weight_inherited_invalid3.stderr b/frame/support/test/tests/pallet_ui/call_weight_inherited_invalid3.stderr new file mode 100644 index 000000000..fab7acb90 --- /dev/null +++ b/frame/support/test/tests/pallet_ui/call_weight_inherited_invalid3.stderr @@ -0,0 +1,19 @@ +error: expected one of: `for`, parentheses, `fn`, `unsafe`, `extern`, identifier, `::`, `<`, `dyn`, square brackets, `*`, `&`, `!`, `impl`, `_`, lifetime + --> tests/pallet_ui/call_weight_inherited_invalid3.rs:22:24 + | +22 | #[pallet::call(weight(123))] + | ^^^ + +error: expected one of: `for`, parentheses, `fn`, `unsafe`, `extern`, identifier, `::`, `<`, `dyn`, square brackets, `*`, `&`, `!`, `impl`, `_`, lifetime + --> tests/pallet_ui/call_weight_inherited_invalid3.rs:43:26 + | +43 | #[pallet::call(weight = 123)] + | ^^^ + +error: unused import: `frame_system::pallet_prelude::*` + --> tests/pallet_ui/call_weight_inherited_invalid3.rs:4:5 + | +4 | use frame_system::pallet_prelude::*; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: `-D unused-imports` implied by `-D warnings` diff --git a/frame/support/test/tests/pallet_ui/call_weight_inherited_invalid4.rs b/frame/support/test/tests/pallet_ui/call_weight_inherited_invalid4.rs new file mode 100644 index 000000000..39c0929d6 --- /dev/null +++ b/frame/support/test/tests/pallet_ui/call_weight_inherited_invalid4.rs @@ -0,0 +1,52 @@ +// Function does not exist in the trait. + +use frame_support::pallet_prelude::*; +use frame_system::pallet_prelude::*; + +pub trait WeightInfo { +} + +#[frame_support::pallet] +mod parentheses { + use super::*; + + #[pallet::config] + pub trait Config: frame_system::Config { + type WeightInfo: crate::WeightInfo; + } + + #[pallet::pallet] + pub struct Pallet(core::marker::PhantomData); + + #[pallet::call(weight(::WeightInfo))] + impl Pallet { + #[pallet::call_index(0)] + pub fn foo(_: OriginFor) -> DispatchResult { + Ok(()) + } + } +} + +#[frame_support::pallet] +mod assign { + use super::*; + + #[pallet::config] + pub trait Config: frame_system::Config { + type WeightInfo: crate::WeightInfo; + } + + #[pallet::pallet] + pub struct Pallet(core::marker::PhantomData); + + #[pallet::call(weight = ::WeightInfo)] + impl Pallet { + #[pallet::call_index(0)] + pub fn foo(_: OriginFor) -> DispatchResult { + Ok(()) + } + } +} + +fn main() { +} diff --git a/frame/support/test/tests/pallet_ui/call_weight_inherited_invalid4.stderr b/frame/support/test/tests/pallet_ui/call_weight_inherited_invalid4.stderr new file mode 100644 index 000000000..46872fc7c --- /dev/null +++ b/frame/support/test/tests/pallet_ui/call_weight_inherited_invalid4.stderr @@ -0,0 +1,11 @@ +error[E0599]: no function or associated item named `foo` found for associated type `::WeightInfo` in the current scope + --> tests/pallet_ui/call_weight_inherited_invalid4.rs:24:10 + | +24 | pub fn foo(_: OriginFor) -> DispatchResult { + | ^^^ function or associated item not found in `::WeightInfo` + +error[E0599]: no function or associated item named `foo` found for associated type `::WeightInfo` in the current scope + --> tests/pallet_ui/call_weight_inherited_invalid4.rs:45:10 + | +45 | pub fn foo(_: OriginFor) -> DispatchResult { + | ^^^ function or associated item not found in `::WeightInfo` diff --git a/frame/support/test/tests/pallet_ui/call_weight_inherited_invalid5.rs b/frame/support/test/tests/pallet_ui/call_weight_inherited_invalid5.rs new file mode 100644 index 000000000..a5b2f5c7f --- /dev/null +++ b/frame/support/test/tests/pallet_ui/call_weight_inherited_invalid5.rs @@ -0,0 +1,44 @@ +// Stray tokens after good input. + +#[frame_support::pallet] +mod parentheses { + use super::*; + + #[pallet::config] + pub trait Config: frame_system::Config { + } + + #[pallet::pallet] + pub struct Pallet(core::marker::PhantomData); + + #[pallet::call(weight(::WeightInfo straycat))] + impl Pallet { + #[pallet::call_index(0)] + pub fn foo(_: OriginFor) -> DispatchResult { + Ok(()) + } + } +} + +#[frame_support::pallet] +mod assign { + use super::*; + + #[pallet::config] + pub trait Config: frame_system::Config { + } + + #[pallet::pallet] + pub struct Pallet(core::marker::PhantomData); + + #[pallet::call(weight = ::WeightInfo straycat)] + impl Pallet { + #[pallet::call_index(0)] + pub fn foo(_: OriginFor) -> DispatchResult { + Ok(()) + } + } +} + +fn main() { +} diff --git a/frame/support/test/tests/pallet_ui/call_weight_inherited_invalid5.stderr b/frame/support/test/tests/pallet_ui/call_weight_inherited_invalid5.stderr new file mode 100644 index 000000000..c0e9ef2d9 --- /dev/null +++ b/frame/support/test/tests/pallet_ui/call_weight_inherited_invalid5.stderr @@ -0,0 +1,11 @@ +error: unexpected token + --> tests/pallet_ui/call_weight_inherited_invalid5.rs:14:50 + | +14 | #[pallet::call(weight(::WeightInfo straycat))] + | ^^^^^^^^ + +error: unexpected token + --> tests/pallet_ui/call_weight_inherited_invalid5.rs:34:52 + | +34 | #[pallet::call(weight = ::WeightInfo straycat)] + | ^^^^^^^^ diff --git a/frame/support/test/tests/pallet_ui/dev_mode_without_arg.rs b/frame/support/test/tests/pallet_ui/dev_mode_without_arg.rs index f044ae6d7..2a413eea9 100644 --- a/frame/support/test/tests/pallet_ui/dev_mode_without_arg.rs +++ b/frame/support/test/tests/pallet_ui/dev_mode_without_arg.rs @@ -1,7 +1,5 @@ #![cfg_attr(not(feature = "std"), no_std)] -pub use pallet::*; - #[frame_support::pallet] pub mod pallet { use frame_support::pallet_prelude::*; diff --git a/frame/support/test/tests/pallet_ui/dev_mode_without_arg.stderr b/frame/support/test/tests/pallet_ui/dev_mode_without_arg.stderr index fac7fd77d..1e2011b2a 100644 --- a/frame/support/test/tests/pallet_ui/dev_mode_without_arg.stderr +++ b/frame/support/test/tests/pallet_ui/dev_mode_without_arg.stderr @@ -1,11 +1,7 @@ -error: Invalid pallet::call, requires weight attribute i.e. `#[pallet::weight($expr)]` - --> tests/pallet_ui/dev_mode_without_arg.rs:24:7 +error: A pallet::call requires either a concrete `#[pallet::weight($expr)]` or an + inherited weight from the `#[pallet:call(weight($type))]` attribute, but + none were given. + --> tests/pallet_ui/dev_mode_without_arg.rs:22:7 | -24 | pub fn my_call(_origin: OriginFor) -> DispatchResult { +22 | pub fn my_call(_origin: OriginFor) -> DispatchResult { | ^^ - -error[E0432]: unresolved import `pallet` - --> tests/pallet_ui/dev_mode_without_arg.rs:3:9 - | -3 | pub use pallet::*; - | ^^^^^^ help: a similar path exists: `test_pallet::pallet` diff --git a/frame/support/test/tests/pallet_ui/pass/inherited_call_weight.rs b/frame/support/test/tests/pallet_ui/pass/inherited_call_weight.rs new file mode 100644 index 000000000..355a1c978 --- /dev/null +++ b/frame/support/test/tests/pallet_ui/pass/inherited_call_weight.rs @@ -0,0 +1,51 @@ +use frame_support::pallet_prelude::*; +use frame_system::pallet_prelude::*; + +pub trait WeightInfo { + fn foo() -> Weight; +} + +#[frame_support::pallet] +mod parentheses { + use super::*; + + #[pallet::config] + pub trait Config: frame_system::Config { + type WeightInfo: crate::WeightInfo; + } + + #[pallet::pallet] + pub struct Pallet(core::marker::PhantomData); + + #[pallet::call(weight(::WeightInfo))] + impl Pallet { + #[pallet::call_index(0)] + pub fn foo(_: OriginFor) -> DispatchResult { + Ok(()) + } + } +} + +#[frame_support::pallet] +mod assign { + use super::*; + + #[pallet::config] + pub trait Config: frame_system::Config { + type WeightInfo: crate::WeightInfo; + } + + #[pallet::pallet] + pub struct Pallet(core::marker::PhantomData); + + #[pallet::call(weight = ::WeightInfo)] + impl Pallet { + #[pallet::call_index(0)] + pub fn foo(_: OriginFor) -> DispatchResult { + Ok(()) + } + } +} + +fn main() { +} diff --git a/frame/support/test/tests/pallet_ui/pass/inherited_call_weight2.rs b/frame/support/test/tests/pallet_ui/pass/inherited_call_weight2.rs new file mode 100644 index 000000000..ae70c295d --- /dev/null +++ b/frame/support/test/tests/pallet_ui/pass/inherited_call_weight2.rs @@ -0,0 +1,57 @@ +use frame_support::pallet_prelude::*; +use frame_system::pallet_prelude::*; + +pub trait WeightInfo { + fn foo() -> Weight; +} + +impl WeightInfo for () { + fn foo() -> Weight { + Weight::zero() + } +} + +#[frame_support::pallet] +mod parentheses { + use super::*; + + #[pallet::config] + pub trait Config: frame_system::Config { + } + + #[pallet::pallet] + pub struct Pallet(core::marker::PhantomData); + + // Crazy man just uses `()`, but it still works ;) + #[pallet::call(weight(()))] + impl Pallet { + #[pallet::call_index(0)] + pub fn foo(_: OriginFor) -> DispatchResult { + Ok(()) + } + } +} + +#[frame_support::pallet] +mod assign { + use super::*; + + #[pallet::config] + pub trait Config: frame_system::Config { + } + + #[pallet::pallet] + pub struct Pallet(core::marker::PhantomData); + + // Crazy man just uses `()`, but it still works ;) + #[pallet::call(weight = ())] + impl Pallet { + #[pallet::call_index(0)] + pub fn foo(_: OriginFor) -> DispatchResult { + Ok(()) + } + } +} + +fn main() { +} diff --git a/frame/support/test/tests/pallet_ui/pass/inherited_call_weight3.rs b/frame/support/test/tests/pallet_ui/pass/inherited_call_weight3.rs new file mode 100644 index 000000000..567fd2e5f --- /dev/null +++ b/frame/support/test/tests/pallet_ui/pass/inherited_call_weight3.rs @@ -0,0 +1,54 @@ +use frame_support::pallet_prelude::*; +use frame_system::pallet_prelude::*; + +// If, for whatever reason, you dont to not use a `WeightInfo` trait - it will still work. +struct Impl; + +impl Impl { + fn foo() -> Weight { + Weight::zero() + } +} + +#[frame_support::pallet] +mod parentheses { + use super::*; + + #[pallet::config] + pub trait Config: frame_system::Config { + } + + #[pallet::pallet] + pub struct Pallet(core::marker::PhantomData); + + #[pallet::call(weight(crate::Impl))] + impl Pallet { + #[pallet::call_index(0)] + pub fn foo(_: OriginFor) -> DispatchResult { + Ok(()) + } + } +} + +#[frame_support::pallet] +mod assign { + use super::*; + + #[pallet::config] + pub trait Config: frame_system::Config { + } + + #[pallet::pallet] + pub struct Pallet(core::marker::PhantomData); + + #[pallet::call(weight = crate::Impl)] + impl Pallet { + #[pallet::call_index(0)] + pub fn foo(_: OriginFor) -> DispatchResult { + Ok(()) + } + } +} + +fn main() { +} diff --git a/frame/support/test/tests/pallet_ui/pass/inherited_call_weight_dev_mode.rs b/frame/support/test/tests/pallet_ui/pass/inherited_call_weight_dev_mode.rs new file mode 100644 index 000000000..04ce49ee7 --- /dev/null +++ b/frame/support/test/tests/pallet_ui/pass/inherited_call_weight_dev_mode.rs @@ -0,0 +1,30 @@ +use frame_support::pallet_prelude::*; +use frame_system::pallet_prelude::*; + +pub trait WeightInfo { + fn foo() -> Weight; +} + +#[frame_support::pallet(dev_mode)] +mod pallet { + use super::*; + + #[pallet::config] + pub trait Config: frame_system::Config { + type WeightInfo: WeightInfo; + } + + #[pallet::pallet] + pub struct Pallet(core::marker::PhantomData); + + #[pallet::call(weight(::WeightInfo))] + impl Pallet { + #[pallet::call_index(0)] + pub fn foo(_: OriginFor) -> DispatchResult { + Ok(()) + } + } +} + +fn main() { +} From de917e9090af2f1f75d5b25d669a806030c780a2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gon=C3=A7alo=20Pestana?= Date: Thu, 27 Apr 2023 19:45:43 +0100 Subject: [PATCH 435/558] Implements `try_state` hook in elections and EPM pallets (#13979) * Adds try_state hook to elections pallets * Addresses PR review comments Co-authored-by: Liam Aharon * remove unecessary println * ensures try-runtime does not mutate storage * Addresses PR comments * Fixes snapshot invariant checks; simplifies test infra --------- Co-authored-by: Liam Aharon Co-authored-by: parity-processbot <> --- .../election-provider-multi-phase/src/lib.rs | 95 +++++++++++ .../election-provider-multi-phase/src/mock.rs | 12 +- .../src/signed.rs | 5 + frame/elections-phragmen/src/lib.rs | 156 +++++++++++++----- 4 files changed, 223 insertions(+), 45 deletions(-) diff --git a/frame/election-provider-multi-phase/src/lib.rs b/frame/election-provider-multi-phase/src/lib.rs index 359903710..c5247e3c2 100644 --- a/frame/election-provider-multi-phase/src/lib.rs +++ b/frame/election-provider-multi-phase/src/lib.rs @@ -881,6 +881,11 @@ pub mod pallet { // configure this pallet. assert!(T::SignedMaxSubmissions::get() >= T::SignedMaxRefunds::get()); } + + #[cfg(feature = "try-runtime")] + fn try_state(_n: T::BlockNumber) -> Result<(), &'static str> { + Self::do_try_state() + } } #[pallet::call] @@ -1252,6 +1257,8 @@ pub mod pallet { pub type CurrentPhase = StorageValue<_, Phase, ValueQuery>; /// Current best solution, signed or unsigned, queued to be returned upon `elect`. + /// + /// Always sorted by score. #[pallet::storage] #[pallet::getter(fn queued_solution)] pub type QueuedSolution = @@ -1570,6 +1577,89 @@ impl Pallet { } } +#[cfg(feature = "try-runtime")] +impl Pallet { + fn do_try_state() -> Result<(), &'static str> { + Self::try_state_snapshot()?; + Self::try_state_signed_submissions_map()?; + Self::try_state_phase_off() + } + + // [`Snapshot`] state check. Invariants: + // - [`DesiredTargets`] exists if and only if [`Snapshot`] is present. + // - [`SnapshotMetadata`] exist if and only if [`Snapshot`] is present. + fn try_state_snapshot() -> Result<(), &'static str> { + if >::exists() && + >::exists() && + >::exists() + { + Ok(()) + } else if !>::exists() && + !>::exists() && + !>::exists() + { + Ok(()) + } else { + Err("If snapshot exists, metadata and desired targets should be set too. Otherwise, none should be set.") + } + } + + // [`SignedSubmissionsMap`] state check. Invariants: + // - All [`SignedSubmissionIndices`] are present in [`SignedSubmissionsMap`], and no more; + // - [`SignedSubmissionNextIndex`] is not present in [`SignedSubmissionsMap`]; + // - [`SignedSubmissionIndices`] is sorted by election score. + fn try_state_signed_submissions_map() -> Result<(), &'static str> { + let mut last_score: ElectionScore = Default::default(); + let indices = >::get(); + + for (i, indice) in indices.iter().enumerate() { + let submission = >::get(indice.2); + if submission.is_none() { + return Err("All signed submissions indices must be part of the submissions map") + } + + if i == 0 { + last_score = indice.0 + } else { + if last_score.strict_threshold_better(indice.0, Perbill::zero()) { + return Err("Signed submission indices vector must be ordered by election score") + } + last_score = indice.0; + } + } + + if >::iter().nth(indices.len()).is_some() { + return Err("Signed submissions map length should be the same as the indices vec length") + } + + match >::get() { + 0 => Ok(()), + next => + if >::get(next).is_some() { + return Err( + "The next submissions index should not be in the submissions maps already", + ) + } else { + Ok(()) + }, + } + } + + // [`Phase::Off`] state check. Invariants: + // - If phase is `Phase::Off`, [`Snapshot`] must be none. + fn try_state_phase_off() -> Result<(), &'static str> { + match Self::current_phase().is_off() { + false => Ok(()), + true => + if >::get().is_some() { + Err("Snapshot must be none when in Phase::Off") + } else { + Ok(()) + }, + } + } +} + impl ElectionProviderBase for Pallet { type AccountId = T::AccountId; type BlockNumber = T::BlockNumber; @@ -1642,6 +1732,11 @@ mod feasibility_check { MultiPhase::feasibility_check(solution, COMPUTE), FeasibilityError::SnapshotUnavailable ); + + // kill also `SnapshotMetadata` and `DesiredTargets` for the storage state to be + // consistent for the try_state checks to pass. + >::kill(); + >::kill(); }) } diff --git a/frame/election-provider-multi-phase/src/mock.rs b/frame/election-provider-multi-phase/src/mock.rs index 8c1877760..732a650ce 100644 --- a/frame/election-provider-multi-phase/src/mock.rs +++ b/frame/election-provider-multi-phase/src/mock.rs @@ -615,7 +615,17 @@ impl ExtBuilder { } pub fn build_and_execute(self, test: impl FnOnce() -> ()) { - self.build().execute_with(test) + sp_tracing::try_init_simple(); + + let mut ext = self.build(); + ext.execute_with(test); + + #[cfg(feature = "try-runtime")] + ext.execute_with(|| { + assert_ok!(>::try_state( + System::block_number() + )); + }); } } diff --git a/frame/election-provider-multi-phase/src/signed.rs b/frame/election-provider-multi-phase/src/signed.rs index b8a27fdfa..bde985518 100644 --- a/frame/election-provider-multi-phase/src/signed.rs +++ b/frame/election-provider-multi-phase/src/signed.rs @@ -554,6 +554,11 @@ mod tests { MultiPhase::submit(RuntimeOrigin::signed(10), Box::new(solution)), Error::::PreDispatchEarlySubmission, ); + + // make sure invariants hold true and post-test try state checks to pass. + >::kill(); + >::kill(); + >::kill(); }) } diff --git a/frame/elections-phragmen/src/lib.rs b/frame/elections-phragmen/src/lib.rs index d83c94db1..9c40e542e 100644 --- a/frame/elections-phragmen/src/lib.rs +++ b/frame/elections-phragmen/src/lib.rs @@ -325,6 +325,11 @@ pub mod pallet { T::MaxVotesPerVoter::get(), ); } + + #[cfg(feature = "try-runtime")] + fn try_state(_n: T::BlockNumber) -> Result<(), &'static str> { + Self::do_try_state() + } } #[pallet::call] @@ -893,7 +898,7 @@ impl Pallet { } /// Get the members' account ids. - fn members_ids() -> Vec { + pub(crate) fn members_ids() -> Vec { Self::members().into_iter().map(|m| m.who).collect::>() } @@ -1192,6 +1197,104 @@ impl ContainsLengthBound for Pallet { } } +#[cfg(any(feature = "try-runtime", test))] +impl Pallet { + fn do_try_state() -> Result<(), &'static str> { + Self::try_state_members()?; + Self::try_state_runners_up()?; + Self::try_state_candidates()?; + Self::try_state_candidates_runners_up_disjoint()?; + Self::try_state_members_disjoint()?; + Self::try_state_members_approval_stake() + } + + /// [`Members`] state checks. Invariants: + /// - Members are always sorted based on account ID. + fn try_state_members() -> Result<(), &'static str> { + let mut members = Members::::get().clone(); + members.sort_by_key(|m| m.who.clone()); + + if Members::::get() == members { + Ok(()) + } else { + Err("try_state checks: Members must be always sorted by account ID") + } + } + + // [`RunnersUp`] state checks. Invariants: + // - Elements are sorted based on weight (worst to best). + fn try_state_runners_up() -> Result<(), &'static str> { + let mut sorted = RunnersUp::::get(); + // worst stake first + sorted.sort_by(|a, b| a.stake.cmp(&b.stake)); + + if RunnersUp::::get() == sorted { + Ok(()) + } else { + Err("try_state checks: Runners Up must always be sorted by stake (worst to best)") + } + } + + // [`Candidates`] state checks. Invariants: + // - Always sorted based on account ID. + fn try_state_candidates() -> Result<(), &'static str> { + let mut candidates = Candidates::::get().clone(); + candidates.sort_by_key(|(c, _)| c.clone()); + + if Candidates::::get() == candidates { + Ok(()) + } else { + Err("try_state checks: Candidates must be always sorted by account ID") + } + } + // [`Candidates`] and [`RunnersUp`] state checks. Invariants: + // - Candidates and runners-ups sets are disjoint. + fn try_state_candidates_runners_up_disjoint() -> Result<(), &'static str> { + match Self::intersects(&Self::candidates_ids(), &Self::runners_up_ids()) { + true => Err("Candidates and runners up sets should always be disjoint"), + false => Ok(()), + } + } + + // [`Members`], [`Candidates`] and [`RunnersUp`] state checks. Invariants: + // - Members and candidates sets are disjoint; + // - Members and runners-ups sets are disjoint. + fn try_state_members_disjoint() -> Result<(), &'static str> { + match Self::intersects(&Pallet::::members_ids(), &Self::candidates_ids()) && + Self::intersects(&Pallet::::members_ids(), &Self::runners_up_ids()) + { + true => Err("Members set should be disjoint from candidates and runners-up sets"), + false => Ok(()), + } + } + + // [`Members`], [`RunnersUp`] and approval stake state checks. Invariants: + // - Selected members should have approval stake; + // - Selected RunnersUp should have approval stake. + fn try_state_members_approval_stake() -> Result<(), &'static str> { + match Members::::get() + .iter() + .chain(RunnersUp::::get().iter()) + .all(|s| s.stake != BalanceOf::::zero()) + { + true => Ok(()), + false => Err("Members and RunnersUp must have approval stake"), + } + } + + fn intersects(a: &[P], b: &[P]) -> bool { + a.iter().any(|e| b.contains(e)) + } + + fn candidates_ids() -> Vec { + Pallet::::candidates().iter().map(|(x, _)| x).cloned().collect::>() + } + + fn runners_up_ids() -> Vec { + Pallet::::runners_up().into_iter().map(|r| r.who).collect::>() + } +} + #[cfg(test)] mod tests { use super::*; @@ -1418,7 +1521,13 @@ mod tests { .into(); ext.execute_with(pre_conditions); ext.execute_with(test); - ext.execute_with(post_conditions) + + #[cfg(feature = "try-runtime")] + ext.execute_with(|| { + assert_ok!(>::try_state( + System::block_number() + )); + }); } } @@ -1475,54 +1584,13 @@ mod tests { .unwrap_or_default() } - fn intersects(a: &[T], b: &[T]) -> bool { - a.iter().any(|e| b.contains(e)) - } - - fn ensure_members_sorted() { - let mut members = Elections::members().clone(); - members.sort_by_key(|m| m.who); - assert_eq!(Elections::members(), members); - } - - fn ensure_candidates_sorted() { - let mut candidates = Elections::candidates().clone(); - candidates.sort_by_key(|(c, _)| *c); - assert_eq!(Elections::candidates(), candidates); - } - fn locked_stake_of(who: &u64) -> u64 { Voting::::get(who).stake } - fn ensure_members_has_approval_stake() { - // we filter members that have no approval state. This means that even we have more seats - // than candidates, we will never ever chose a member with no votes. - assert!(Elections::members() - .iter() - .chain(Elections::runners_up().iter()) - .all(|s| s.stake != u64::zero())); - } - - fn ensure_member_candidates_runners_up_disjoint() { - // members, candidates and runners-up must always be disjoint sets. - assert!(!intersects(&members_ids(), &candidate_ids())); - assert!(!intersects(&members_ids(), &runners_up_ids())); - assert!(!intersects(&candidate_ids(), &runners_up_ids())); - } - fn pre_conditions() { System::set_block_number(1); - ensure_members_sorted(); - ensure_candidates_sorted(); - ensure_member_candidates_runners_up_disjoint(); - } - - fn post_conditions() { - ensure_members_sorted(); - ensure_candidates_sorted(); - ensure_member_candidates_runners_up_disjoint(); - ensure_members_has_approval_stake(); + Elections::do_try_state().unwrap(); } fn submit_candidacy(origin: RuntimeOrigin) -> sp_runtime::DispatchResult { From 16b2e644487f7f767eeefd4debf11a7948bfa4db Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 27 Apr 2023 20:08:42 +0000 Subject: [PATCH 436/558] Bump wasmtime from 6.0.1 to 6.0.2 (#14037) Bumps [wasmtime](https://github.com/bytecodealliance/wasmtime) from 6.0.1 to 6.0.2. - [Release notes](https://github.com/bytecodealliance/wasmtime/releases) - [Changelog](https://github.com/bytecodealliance/wasmtime/blob/main/docs/WASI-some-possible-changes.md) - [Commits](https://github.com/bytecodealliance/wasmtime/compare/v6.0.1...v6.0.2) --- updated-dependencies: - dependency-name: wasmtime dependency-type: direct:production ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- Cargo.lock | 76 ++++++++++++++-------------- client/executor/wasmtime/Cargo.toml | 2 +- primitives/wasm-interface/Cargo.toml | 2 +- 3 files changed, 40 insertions(+), 40 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index c06bf41c2..420c90408 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1172,18 +1172,18 @@ dependencies = [ [[package]] name = "cranelift-bforest" -version = "0.93.1" +version = "0.93.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a7379abaacee0f14abf3204a7606118f0465785252169d186337bcb75030815a" +checksum = "2bc42ba2e232e5b20ff7dc299a812d53337dadce9a7e39a238e6a5cb82d2e57b" dependencies = [ "cranelift-entity", ] [[package]] name = "cranelift-codegen" -version = "0.93.1" +version = "0.93.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9489fa336927df749631f1008007ced2871068544f40a202ce6d93fbf2366a7b" +checksum = "253531aca9b6f56103c9420369db3263e784df39aa1c90685a1f69cfbba0623e" dependencies = [ "arrayvec 0.7.2", "bumpalo", @@ -1202,33 +1202,33 @@ dependencies = [ [[package]] name = "cranelift-codegen-meta" -version = "0.93.1" +version = "0.93.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "05bbb67da91ec721ed57cef2f7c5ef7728e1cd9bde9ffd3ef8601022e73e3239" +checksum = "72f2154365e2bff1b1b8537a7181591fdff50d8e27fa6e40d5c69c3bad0ca7c8" dependencies = [ "cranelift-codegen-shared", ] [[package]] name = "cranelift-codegen-shared" -version = "0.93.1" +version = "0.93.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "418ecb2f36032f6665dc1a5e2060a143dbab41d83b784882e97710e890a7a16d" +checksum = "687e14e3f5775248930e0d5a84195abef8b829958e9794bf8d525104993612b4" [[package]] name = "cranelift-entity" -version = "0.93.1" +version = "0.93.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7cf583f7b093f291005f9fb1323e2c37f6ee4c7909e39ce016b2e8360d461705" +checksum = "f42ea692c7b450ad18b8c9889661505d51c09ec4380cf1c2d278dbb2da22cae1" dependencies = [ "serde", ] [[package]] name = "cranelift-frontend" -version = "0.93.1" +version = "0.93.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0b66bf9e916f57fbbd0f7703ec6286f4624866bf45000111627c70d272c8dda1" +checksum = "8483c2db6f45fe9ace984e5adc5d058102227e4c62e5aa2054e16b0275fd3a6e" dependencies = [ "cranelift-codegen", "log", @@ -1238,15 +1238,15 @@ dependencies = [ [[package]] name = "cranelift-isle" -version = "0.93.1" +version = "0.93.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "649782a39ce99798dd6b4029e2bb318a2fbeaade1b4fa25330763c10c65bc358" +checksum = "e9793158837678902446c411741d87b43f57dadfb944f2440db4287cda8cbd59" [[package]] name = "cranelift-native" -version = "0.93.1" +version = "0.93.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "937e021e089c51f9749d09e7ad1c4f255c2f8686cb8c3df63a34b3ec9921bc41" +checksum = "72668c7755f2b880665cb422c8ad2d56db58a88b9bebfef0b73edc2277c13c49" dependencies = [ "cranelift-codegen", "libc", @@ -1255,9 +1255,9 @@ dependencies = [ [[package]] name = "cranelift-wasm" -version = "0.93.1" +version = "0.93.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d850cf6775477747c9dfda9ae23355dd70512ffebc70cf82b85a5b111ae668b5" +checksum = "3852ce4b088b44ac4e29459573943009a70d1b192c8d77ef949b4e814f656fc1" dependencies = [ "cranelift-codegen", "cranelift-entity", @@ -12635,9 +12635,9 @@ dependencies = [ [[package]] name = "wasmtime" -version = "6.0.1" +version = "6.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f6e89f9819523447330ffd70367ef4a18d8c832e24e8150fe054d1d912841632" +checksum = "76a222f5fa1e14b2cefc286f1b68494d7a965f4bf57ec04c59bb62673d639af6" dependencies = [ "anyhow", "bincode", @@ -12663,18 +12663,18 @@ dependencies = [ [[package]] name = "wasmtime-asm-macros" -version = "6.0.1" +version = "6.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9bd3a5e46c198032da934469f3a6e48649d1f9142438e4fd4617b68a35644b8a" +checksum = "4407a7246e7d2f3d8fb1cf0c72fda8dbafdb6dd34d555ae8bea0e5ae031089cc" dependencies = [ "cfg-if", ] [[package]] name = "wasmtime-cache" -version = "6.0.1" +version = "6.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b389ae9b678b9c3851091a4804f4182d688d27aff7abc9aa37fa7be37d8ecffa" +checksum = "5ceb3adf61d654be0be67fffdce42447b0880481348785be5fe40b5dd7663a4c" dependencies = [ "anyhow", "base64 0.13.1", @@ -12692,9 +12692,9 @@ dependencies = [ [[package]] name = "wasmtime-cranelift" -version = "6.0.1" +version = "6.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "59b2c92a08c0db6efffd88fdc97d7aa9c7c63b03edb0971dbca745469f820e8c" +checksum = "3c366bb8647e01fd08cb5589976284b00abfded5529b33d7e7f3f086c68304a4" dependencies = [ "anyhow", "cranelift-codegen", @@ -12713,9 +12713,9 @@ dependencies = [ [[package]] name = "wasmtime-environ" -version = "6.0.1" +version = "6.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9a6db9fc52985ba06ca601f2ff0ff1f526c5d724c7ac267b47326304b0c97883" +checksum = "47b8b50962eae38ee319f7b24900b7cf371f03eebdc17400c1dc8575fc10c9a7" dependencies = [ "anyhow", "cranelift-entity", @@ -12732,9 +12732,9 @@ dependencies = [ [[package]] name = "wasmtime-jit" -version = "6.0.1" +version = "6.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b77e3a52cd84d0f7f18554afa8060cfe564ccac61e3b0802d3fd4084772fa5f6" +checksum = "ffaed4f9a234ba5225d8e64eac7b4a5d13b994aeb37353cde2cbeb3febda9eaa" dependencies = [ "addr2line 0.17.0", "anyhow", @@ -12756,9 +12756,9 @@ dependencies = [ [[package]] name = "wasmtime-jit-debug" -version = "6.0.1" +version = "6.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d0245e8a9347017c7185a72e215218a802ff561545c242953c11ba00fccc930f" +checksum = "eed41cbcbf74ce3ff6f1d07d1b707888166dc408d1a880f651268f4f7c9194b2" dependencies = [ "object 0.29.0", "once_cell", @@ -12767,9 +12767,9 @@ dependencies = [ [[package]] name = "wasmtime-jit-icache-coherence" -version = "6.0.1" +version = "6.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "67d412e9340ab1c83867051d8d1d7c90aa8c9afc91da086088068e2734e25064" +checksum = "43a28ae1e648461bfdbb79db3efdaee1bca5b940872e4175390f465593a2e54c" dependencies = [ "cfg-if", "libc", @@ -12778,9 +12778,9 @@ dependencies = [ [[package]] name = "wasmtime-runtime" -version = "6.0.1" +version = "6.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d594e791b5fdd4dbaf8cf7ae62f2e4ff85018ce90f483ca6f42947688e48827d" +checksum = "e704b126e4252788ccfc3526d4d4511d4b23c521bf123e447ac726c14545217b" dependencies = [ "anyhow", "cc", @@ -12802,9 +12802,9 @@ dependencies = [ [[package]] name = "wasmtime-types" -version = "6.0.1" +version = "6.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a6688d6f96d4dbc1f89fab626c56c1778936d122b5f4ae7a57c2eb42b8d982e2" +checksum = "83e5572c5727c1ee7e8f28717aaa8400e4d22dcbd714ea5457d85b5005206568" dependencies = [ "cranelift-entity", "serde", diff --git a/client/executor/wasmtime/Cargo.toml b/client/executor/wasmtime/Cargo.toml index fed284e91..58a1ffde0 100644 --- a/client/executor/wasmtime/Cargo.toml +++ b/client/executor/wasmtime/Cargo.toml @@ -19,7 +19,7 @@ libc = "0.2.121" # When bumping wasmtime do not forget to also bump rustix # to exactly the same version as used by wasmtime! -wasmtime = { version = "6.0.1", default-features = false, features = [ +wasmtime = { version = "6.0.2", default-features = false, features = [ "cache", "cranelift", "jitdump", diff --git a/primitives/wasm-interface/Cargo.toml b/primitives/wasm-interface/Cargo.toml index 441d66f0f..c45ea7583 100644 --- a/primitives/wasm-interface/Cargo.toml +++ b/primitives/wasm-interface/Cargo.toml @@ -18,7 +18,7 @@ codec = { package = "parity-scale-codec", version = "3.2.2", default-features = impl-trait-for-tuples = "0.2.2" log = { version = "0.4.17", optional = true } wasmi = { version = "0.13.2", optional = true } -wasmtime = { version = "6.0.1", default-features = false, optional = true } +wasmtime = { version = "6.0.2", default-features = false, optional = true } anyhow = { version = "1.0.68", optional = true } sp-std = { version = "5.0.0", default-features = false, path = "../std" } From e94cb0dafd4f30ff29512c1c00ec513ada7d2b5d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bastian=20K=C3=B6cher?= Date: Fri, 28 Apr 2023 14:12:26 +0200 Subject: [PATCH 437/558] Improve contribution guidelines (#13902) --- docs/CONTRIBUTING.adoc | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/docs/CONTRIBUTING.adoc b/docs/CONTRIBUTING.adoc index 05a9434ac..759141a62 100644 --- a/docs/CONTRIBUTING.adoc +++ b/docs/CONTRIBUTING.adoc @@ -36,10 +36,11 @@ A Pull Request (PR) needs to be reviewed and approved by project maintainers unl *Process:* . Please tag each PR with exactly one `A`, `B`, `C` and `D` label at the minimum. +. When tagging a PR, it should be done while keeping all downstream users in mind. Downstream users are not just Polkadot or system parachains, but also all the other parachains and solo chains that are using Substrate. The labels are used by downstream users to track changes and to include these changes properly into their own releases. . Once a PR is ready for review please add the https://github.com/paritytech/substrate/pulls?q=is%3Apr+is%3Aopen+label%3AA0-please_review+[`A0-please_review`] label. Generally PRs should sit with this label for 48 hours in order to garner feedback. It may be merged before if all relevant parties had a look at it. . If the first review is not an approval, swap `A0-please_review` to any label `[A3, A5]` to indicate that the PR has received some feedback, but needs further work. For example. https://github.com/paritytech/substrate/labels/A3-in_progress[`A3-in_progress`] is a general indicator that the PR is work in progress. -. PRs must be tagged with their release notes requirements via the `B*` labels. If a PR should be mentioned in the release notes, use `B1` and add either: `T0-node`, `T1-runtime`or `T2-API` - this defines in which section of the release notes the PR will be shown. -. PRs must be tagged with their release importance via the `C1-C7` labels. +. PRs must be tagged with `B*` labels to signal if a change is note worthy for downstream users. The respective `T*` labels should be added to signal the component that was changed. `B0-silent` must only be used for changes that don't require any attention by downstream users. +. PRs must be tagged with their release importance via the `C1-C7` labels. The release importance is only informing about how important it is to apply a release that contains the change. . PRs must be tagged with their audit requirements via the `D1-D9` labels. . PRs that introduce runtime migrations must be tagged with https://github.com/paritytech/substrate/labels/E0-runtime_migration[`E0-runtime_migration`]. See the https://github.com/paritytech/substrate/blob/master/utils/frame/try-runtime/cli/src/lib.rs#L18[Migration Best Practices here] for more info about how to test runtime migrations. . PRs that introduce irreversible database migrations must be tagged with https://github.com/paritytech/substrate/labels/E1-database_migration[`E1-database_migration`]. @@ -50,7 +51,13 @@ A Pull Request (PR) needs to be reviewed and approved by project maintainers unl . PRs should be categorized into projects. . No PR should be merged until all reviews' comments are addressed and CI is successful. -*Reviewing pull requests*: +*Noting relevant changes:* + +When breaking APIs, it should be mentioned on what was changed in the PR description alongside some examples on how to change the code to make it work/compile. + +The PR description should also mention potential storage migrations and if they require some special setup aside adding it to the list of migrations in the runtime. + +*Reviewing pull requests:* When reviewing a pull request, the end-goal is to suggest useful changes to the author. Reviews should finish with approval unless there are issues that would result in: From de80d0107336a9c7a2efdc0199015e4d67fcbdb5 Mon Sep 17 00:00:00 2001 From: Oliver Tale-Yazdi Date: Sat, 29 Apr 2023 01:34:05 +0200 Subject: [PATCH 438/558] CI: Remove crate publish check (#14044) * Fix frame-support feature Signed-off-by: Oliver Tale-Yazdi * Remove publishing script Signed-off-by: Oliver Tale-Yazdi --------- Signed-off-by: Oliver Tale-Yazdi --- .gitlab-ci.yml | 12 ------------ frame/support/Cargo.toml | 6 +++++- 2 files changed, 5 insertions(+), 13 deletions(-) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 59d42936e..100dd0d3d 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -247,18 +247,6 @@ default: #### stage: .pre -check-crates-publishing-pipeline: - stage: .pre - extends: - - .kubernetes-env - - .crates-publishing-pipeline - script: - - git clone - --depth 1 - --branch "$RELENG_SCRIPTS_BRANCH" - https://github.com/paritytech/releng-scripts.git - - ONLY_CHECK_PIPELINE=true ./releng-scripts/publish-crates - # By default our pipelines are interruptible, but some special pipelines shouldn't be interrupted: # * multi-project pipelines such as the ones triggered by the scripts repo # * the scheduled automatic-crate-publishing pipeline diff --git a/frame/support/Cargo.toml b/frame/support/Cargo.toml index fe068d936..d8b1b9c24 100644 --- a/frame/support/Cargo.toml +++ b/frame/support/Cargo.toml @@ -70,7 +70,11 @@ std = [ "log/std", "environmental/std", ] -runtime-benchmarks = [] +runtime-benchmarks = [ + "frame-system/runtime-benchmarks", + "sp-runtime/runtime-benchmarks", + "sp-staking/runtime-benchmarks" +] try-runtime = [] # By default some types have documentation, `no-metadata-docs` allows to reduce the documentation # in the metadata. From 74b2c92066ec3abcb612faa9272f246ae339fab3 Mon Sep 17 00:00:00 2001 From: yjh Date: Sat, 29 Apr 2023 18:06:42 +0800 Subject: [PATCH 439/558] fix(in_mem): fix the clone logic (#14038) --- client/api/src/in_mem.rs | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/client/api/src/in_mem.rs b/client/api/src/in_mem.rs index 27a74ddd7..0a29a4dba 100644 --- a/client/api/src/in_mem.rs +++ b/client/api/src/in_mem.rs @@ -114,6 +114,7 @@ struct BlockchainStorage { } /// In-memory blockchain. Supports concurrent reads. +#[derive(Clone)] pub struct Blockchain { storage: Arc>>, } @@ -124,13 +125,6 @@ impl Default for Blockchain { } } -impl Clone for Blockchain { - fn clone(&self) -> Self { - let storage = Arc::new(RwLock::new(self.storage.read().clone())); - Blockchain { storage } - } -} - impl Blockchain { /// Get header hash of given block. pub fn id(&self, id: BlockId) -> Option { From bc8f2db2cca2bac2d780569050c9ba4d751be45c Mon Sep 17 00:00:00 2001 From: PG Herveou Date: Sat, 29 Apr 2023 18:07:55 +0200 Subject: [PATCH 440/558] Contracts: runtime_call and storage_deposit (#13990) * wip * add comments * fix comment * comments * comments * PR comment * field orders * Update frame/contracts/src/tests.rs * Update frame/contracts/fixtures/call_runtime_and_call.wat Co-authored-by: Sasha Gryaznov * Apply suggestions from code review Co-authored-by: Sasha Gryaznov * Apply suggestions from code review Co-authored-by: Sasha Gryaznov * Update frame/contracts/src/tests.rs Co-authored-by: Sasha Gryaznov * Validate fees of failed call * Update frame/contracts/src/tests.rs * Update frame/contracts/src/tests.rs * Update frame/contracts/src/tests.rs * bubble up refund error * rename fixture file --------- Co-authored-by: Sasha Gryaznov Co-authored-by: parity-processbot <> --- Cargo.lock | 1 + frame/contracts/Cargo.toml | 1 + .../fixtures/call_runtime_and_call.wat | 56 +++++++++++ frame/contracts/src/lib.rs | 13 ++- frame/contracts/src/storage/meter.rs | 86 +++++----------- frame/contracts/src/tests.rs | 99 ++++++++++++++++++- 6 files changed, 189 insertions(+), 67 deletions(-) create mode 100644 frame/contracts/fixtures/call_runtime_and_call.wat diff --git a/Cargo.lock b/Cargo.lock index 420c90408..a1244f271 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -5922,6 +5922,7 @@ dependencies = [ "pallet-contracts-primitives", "pallet-contracts-proc-macro", "pallet-insecure-randomness-collective-flip", + "pallet-proxy", "pallet-timestamp", "pallet-utility", "parity-scale-codec", diff --git a/frame/contracts/Cargo.toml b/frame/contracts/Cargo.toml index 6ab60846f..edb6d294c 100644 --- a/frame/contracts/Cargo.toml +++ b/frame/contracts/Cargo.toml @@ -59,6 +59,7 @@ pallet-balances = { version = "4.0.0-dev", path = "../balances" } pallet-timestamp = { version = "4.0.0-dev", path = "../timestamp" } pallet-insecure-randomness-collective-flip = { version = "4.0.0-dev", path = "../insecure-randomness-collective-flip" } pallet-utility = { version = "4.0.0-dev", path = "../utility" } +pallet-proxy = { version = "4.0.0-dev", path = "../proxy" } sp-keystore = { version = "0.13.0", path = "../../primitives/keystore" } [features] diff --git a/frame/contracts/fixtures/call_runtime_and_call.wat b/frame/contracts/fixtures/call_runtime_and_call.wat new file mode 100644 index 000000000..3320922d9 --- /dev/null +++ b/frame/contracts/fixtures/call_runtime_and_call.wat @@ -0,0 +1,56 @@ +(module + (import "seal0" "call_runtime" (func $call_runtime (param i32 i32) (result i32))) + (import "seal1" "seal_call" (func $seal_call (param i32 i32 i64 i32 i32 i32 i32 i32) (result i32))) + (import "seal0" "seal_input" (func $seal_input (param i32 i32))) + (import "seal0" "seal_return" (func $seal_return (param i32 i32 i32))) + (import "env" "memory" (memory 1 1)) + + (func $assert (param i32) + (block $ok + (br_if $ok (get_local 0)) + (unreachable) + ) + ) + + (func (export "call") + ;; Store available input size at offset 0. + (i32.store (i32.const 0) (i32.const 512)) + + ;; read input data + (call $seal_input (i32.const 4) (i32.const 0)) + + ;; Input data layout. + ;; [0..4) - size of the call + ;; [4..8) - how many bytes to add to storage + ;; [8..40) - address of the callee + ;; [40..n) - encoded runtime call + + ;; Invoke call_runtime with the encoded call passed to this contract. + (call $assert (i32.eqz + (call $call_runtime + (i32.const 40) ;; Pointer where the call is stored + (i32.sub + (i32.load (i32.const 0)) ;; Size of the call + (i32.const 36) ;; Subtract size of the subcall-related part: 4 bytes for storage length to add + 32 bytes of the callee address + ) + ) + )) + + ;; call passed contract + (call $assert (i32.eqz + (call $seal_call + (i32.const 0) ;; No flags + (i32.const 8) ;; Pointer to "callee" address. + (i64.const 0) ;; How much gas to devote for the execution. 0 = all. + (i32.const 512) ;; Pointer to the buffer with value to transfer + (i32.const 4) ;; Pointer to input data buffer address + (i32.const 4) ;; Length of input data buffer + (i32.const 4294967295) ;; u32 max value is the sentinel value: do not copy output + (i32.const 0) ;; Length is ignored in this case + ) + )) + ) + + (func (export "deploy")) +) + diff --git a/frame/contracts/src/lib.rs b/frame/contracts/src/lib.rs index 5a82a4a42..24653423b 100644 --- a/frame/contracts/src/lib.rs +++ b/frame/contracts/src/lib.rs @@ -1044,7 +1044,15 @@ impl Invokable for CallInput { debug_message, *determinism, ); - InternalOutput { gas_meter, storage_deposit: storage_meter.into_deposit(&origin), result } + + match storage_meter.try_into_deposit(&origin) { + Ok(storage_deposit) => InternalOutput { gas_meter, storage_deposit, result }, + Err(err) => InternalOutput { + gas_meter, + storage_deposit: Default::default(), + result: Err(err.into()), + }, + } } } @@ -1105,8 +1113,9 @@ impl Invokable for InstantiateInput { &salt, debug_message, ); + storage_deposit = storage_meter - .into_deposit(&origin) + .try_into_deposit(&origin)? .saturating_add(&StorageDeposit::Charge(extra_deposit)); result }; diff --git a/frame/contracts/src/storage/meter.rs b/frame/contracts/src/storage/meter.rs index b8bbd6dc4..73794b597 100644 --- a/frame/contracts/src/storage/meter.rs +++ b/frame/contracts/src/storage/meter.rs @@ -19,7 +19,7 @@ use crate::{ storage::{ContractInfo, DepositAccount}, - BalanceOf, Config, Error, Inspect, Pallet, System, LOG_TARGET, + BalanceOf, Config, Error, Inspect, Pallet, System, }; use codec::Encode; use frame_support::{ @@ -85,7 +85,7 @@ pub trait Ext { deposit_account: &DepositAccount, amount: &DepositOf, terminated: bool, - ); + ) -> Result<(), DispatchError>; } /// This [`Ext`] is used for actual on-chain execution when balance needs to be charged. @@ -366,14 +366,14 @@ where /// /// This drops the root meter in order to make sure it is only called when the whole /// execution did finish. - pub fn into_deposit(self, origin: &T::AccountId) -> DepositOf { + pub fn try_into_deposit(self, origin: &T::AccountId) -> Result, DispatchError> { for charge in self.charges.iter().filter(|c| matches!(c.amount, Deposit::Refund(_))) { - E::charge(origin, &charge.deposit_account, &charge.amount, charge.terminated); + E::charge(origin, &charge.deposit_account, &charge.amount, charge.terminated)?; } for charge in self.charges.iter().filter(|c| matches!(c.amount, Deposit::Charge(_))) { - E::charge(origin, &charge.deposit_account, &charge.amount, charge.terminated); + E::charge(origin, &charge.deposit_account, &charge.amount, charge.terminated)?; } - self.total_deposit + Ok(self.total_deposit) } } @@ -428,7 +428,8 @@ where info.deposit_account(), &deposit.saturating_sub(&Deposit::Charge(ed)), false, - ); + )?; + System::::inc_consumers(info.deposit_account())?; // We also need to make sure that the contract's account itself exists. @@ -514,71 +515,27 @@ impl Ext for ReservingExt { deposit_account: &DepositAccount, amount: &DepositOf, terminated: bool, - ) { - // There is nothing we can do when this fails as this constitutes a bug in the runtime. - // We need to settle for emitting an error log in this case. - // - // # Note - // - // This is infallible because it is called in a part of the execution where we cannot - // simply roll back. It might make sense to do some refactoring to move the deposit - // collection to the fallible part of execution. + ) -> Result<(), DispatchError> { match amount { - Deposit::Charge(amount) => { - // This will never fail because a deposit account is required to exist - // at all times. The pallet enforces this invariant by holding a consumer reference - // on the deposit account as long as the contract exists. - // - // The sender always has enough balance because we checked that it had enough - // balance when instantiating the storage meter. There is no way for the sender - // which is a plain account to send away this balance in the meantime. - let result = T::Currency::transfer( - origin, - deposit_account, - *amount, - ExistenceRequirement::KeepAlive, - ); - if let Err(err) = result { - log::error!( - target: LOG_TARGET, - "Failed to transfer storage deposit {:?} from origin {:?} to deposit account {:?}: {:?}", - amount, origin, deposit_account, err, - ); - if cfg!(debug_assertions) { - panic!("Unable to collect storage deposit. This is a bug."); - } - } - }, - // The receiver always exists because the initial value transfer from the - // origin to the contract has a keep alive existence requirement. When taking a deposit - // we make sure to leave at least the ed in the free balance. - // - // The sender always has enough balance because we track it in the `ContractInfo` and - // never send more back than we have. No one has access to the deposit account. Hence no - // other interaction with this account takes place. + Deposit::Charge(amount) => T::Currency::transfer( + origin, + deposit_account, + *amount, + ExistenceRequirement::KeepAlive, + ), Deposit::Refund(amount) => { if terminated { System::::dec_consumers(&deposit_account); } - let result = T::Currency::transfer( + T::Currency::transfer( deposit_account, origin, *amount, // We can safely use `AllowDeath` because our own consumer prevents an removal. ExistenceRequirement::AllowDeath, - ); - if matches!(result, Err(_)) { - log::error!( - target: LOG_TARGET, - "Failed to refund storage deposit {:?} from deposit account {:?} to origin {:?}: {:?}", - amount, deposit_account, origin, result, - ); - if cfg!(debug_assertions) { - panic!("Unable to refund storage deposit. This is a bug."); - } - } + ) }, - }; + } } } @@ -651,7 +608,7 @@ mod tests { contract: &DepositAccount, amount: &DepositOf, terminated: bool, - ) { + ) -> Result<(), DispatchError> { TestExtTestValue::mutate(|ext| { ext.charges.push(Charge { origin: origin.clone(), @@ -660,6 +617,7 @@ mod tests { terminated, }) }); + Ok(()) } } @@ -757,7 +715,7 @@ mod tests { nested0.enforce_limit(Some(&mut nested0_info)).unwrap(); meter.absorb(nested0, DepositAccount(BOB), Some(&mut nested0_info)); - meter.into_deposit(&ALICE); + meter.try_into_deposit(&ALICE).unwrap(); assert_eq!(nested0_info.extra_deposit(), 112); assert_eq!(nested1_info.extra_deposit(), 110); @@ -817,7 +775,7 @@ mod tests { nested0.absorb(nested1, DepositAccount(CHARLIE), None); meter.absorb(nested0, DepositAccount(BOB), None); - meter.into_deposit(&ALICE); + meter.try_into_deposit(&ALICE).unwrap(); assert_eq!( TestExtTestValue::get(), diff --git a/frame/contracts/src/tests.rs b/frame/contracts/src/tests.rs index 697b58f15..c202d3796 100644 --- a/frame/contracts/src/tests.rs +++ b/frame/contracts/src/tests.rs @@ -33,7 +33,7 @@ use crate::{ use assert_matches::assert_matches; use codec::Encode; use frame_support::{ - assert_err, assert_err_ignore_postinfo, assert_noop, assert_ok, + assert_err, assert_err_ignore_postinfo, assert_err_with_weight, assert_noop, assert_ok, dispatch::{DispatchErrorWithPostInfo, PostDispatchInfo}, parameter_types, storage::child, @@ -70,6 +70,7 @@ frame_support::construct_runtime!( Randomness: pallet_insecure_randomness_collective_flip::{Pallet, Storage}, Utility: pallet_utility::{Pallet, Call, Storage, Event}, Contracts: pallet_contracts::{Pallet, Call, Storage, Event}, + Proxy: pallet_proxy::{Pallet, Call, Storage, Event}, } ); @@ -337,6 +338,22 @@ impl pallet_utility::Config for Test { type PalletsOrigin = OriginCaller; type WeightInfo = (); } + +impl pallet_proxy::Config for Test { + type RuntimeEvent = RuntimeEvent; + type RuntimeCall = RuntimeCall; + type Currency = Balances; + type ProxyType = (); + type ProxyDepositBase = ConstU64<1>; + type ProxyDepositFactor = ConstU64<1>; + type MaxProxies = ConstU32<32>; + type WeightInfo = (); + type MaxPending = ConstU32<32>; + type CallHasher = BlakeTwo256; + type AnnouncementDepositBase = ConstU64<1>; + type AnnouncementDepositFactor = ConstU64<1>; +} + parameter_types! { pub MySchedule: Schedule = { let mut schedule = >::default(); @@ -2983,6 +3000,86 @@ fn sr25519_verify() { }) } +#[test] +fn failed_deposit_charge_should_roll_back_call() { + let (wasm_caller, _) = compile_module::("call_runtime_and_call").unwrap(); + let (wasm_callee, _) = compile_module::("store_call").unwrap(); + const ED: u64 = 200; + + let execute = || { + ExtBuilder::default().existential_deposit(ED).build().execute_with(|| { + let _ = Balances::deposit_creating(&ALICE, 1_000_000); + + // Instantiate both contracts. + let addr_caller = Contracts::bare_instantiate( + ALICE, + 0, + GAS_LIMIT, + None, + Code::Upload(wasm_caller.clone()), + vec![], + vec![], + false, + ) + .result + .unwrap() + .account_id; + let addr_callee = Contracts::bare_instantiate( + ALICE, + 0, + GAS_LIMIT, + None, + Code::Upload(wasm_callee.clone()), + vec![], + vec![], + false, + ) + .result + .unwrap() + .account_id; + + // Give caller proxy access to Alice. + assert_ok!(Proxy::add_proxy(RuntimeOrigin::signed(ALICE), addr_caller.clone(), (), 0)); + + // Create a Proxy call that will attempt to transfer away Alice's balance. + let transfer_call = + Box::new(RuntimeCall::Balances(pallet_balances::Call::transfer_allow_death { + dest: CHARLIE, + value: pallet_balances::Pallet::::free_balance(&ALICE) - 2 * ED, + })); + + // Wrap the transfer call in a proxy call. + let transfer_proxy_call = RuntimeCall::Proxy(pallet_proxy::Call::proxy { + real: ALICE, + force_proxy_type: Some(()), + call: transfer_call, + }); + + let data = ( + (ED - DepositPerItem::get()) as u32, // storage length + addr_callee, + transfer_proxy_call, + ); + + >::call( + RuntimeOrigin::signed(ALICE), + addr_caller.clone(), + 0, + GAS_LIMIT, + None, + data.encode(), + ) + }) + }; + + // With a low enough deposit per byte, the call should succeed. + let result = execute().unwrap(); + + // Bump the deposit per byte to a high value to trigger a FundsUnavailable error. + DEPOSIT_PER_BYTE.with(|c| *c.borrow_mut() = ED); + assert_err_with_weight!(execute(), TokenError::FundsUnavailable, result.actual_weight); +} + #[test] fn upload_code_works() { let (wasm, code_hash) = compile_module::("dummy").unwrap(); From e699560d2037d155e6054c05b6334a6e9fa64926 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bastian=20K=C3=B6cher?= Date: Mon, 1 May 2023 09:47:43 +0200 Subject: [PATCH 441/558] Fix bags-list tests execution (#14047) The `test` feature isn't propagated to dependencies in Rust, so we should just enable the method as well for `std`. --- frame/bags-list/src/lib.rs | 2 +- frame/bags-list/src/mock.rs | 7 ++++--- frame/election-provider-support/src/lib.rs | 15 ++++++++++++--- 3 files changed, 17 insertions(+), 7 deletions(-) diff --git a/frame/bags-list/src/lib.rs b/frame/bags-list/src/lib.rs index 87eb2d1b3..d4d54b9a1 100644 --- a/frame/bags-list/src/lib.rs +++ b/frame/bags-list/src/lib.rs @@ -397,7 +397,7 @@ impl, I: 'static> ScoreProvider for Pallet { Node::::get(id).map(|node| node.score()).unwrap_or_default() } - frame_election_provider_support::runtime_benchmarks_or_fuzz_enabled! { + frame_election_provider_support::runtime_benchmarks_fuzz_or_std_enabled! { fn set_score_of(id: &T::AccountId, new_score: T::Score) { ListNodes::::mutate(id, |maybe_node| { if let Some(node) = maybe_node.as_mut() { diff --git a/frame/bags-list/src/mock.rs b/frame/bags-list/src/mock.rs index a48b66b20..efbb2ed94 100644 --- a/frame/bags-list/src/mock.rs +++ b/frame/bags-list/src/mock.rs @@ -40,9 +40,10 @@ impl frame_election_provider_support::ScoreProvider for StakingMock { *NextVoteWeightMap::get().get(id).unwrap_or(&NextVoteWeight::get()) } - #[cfg(any(feature = "runtime-benchmarks", feature = "fuzz", test))] - fn set_score_of(id: &AccountId, weight: Self::Score) { - NEXT_VOTE_WEIGHT_MAP.with(|m| m.borrow_mut().insert(*id, weight)); + frame_election_provider_support::runtime_benchmarks_fuzz_or_std_enabled! { + fn set_score_of(id: &AccountId, weight: Self::Score) { + NEXT_VOTE_WEIGHT_MAP.with(|m| m.borrow_mut().insert(*id, weight)); + } } } diff --git a/frame/election-provider-support/src/lib.rs b/frame/election-provider-support/src/lib.rs index 750ccca46..ee0e41a90 100644 --- a/frame/election-provider-support/src/lib.rs +++ b/frame/election-provider-support/src/lib.rs @@ -582,7 +582,7 @@ pub trait ScoreProvider { fn score(who: &AccountId) -> Self::Score; /// For tests, benchmarks and fuzzing, set the `score`. - #[cfg(any(feature = "runtime-benchmarks", feature = "fuzz", test))] + #[cfg(any(feature = "runtime-benchmarks", feature = "fuzz", feature = "std"))] fn set_score_of(_: &AccountId, _: Self::Score) {} } @@ -673,5 +673,14 @@ pub type BoundedSupportsOf = BoundedSupports< ::MaxWinners, >; -sp_core::generate_feature_enabled_macro!(runtime_benchmarks_enabled, feature = "runtime-benchmarks", $); -sp_core::generate_feature_enabled_macro!(runtime_benchmarks_or_fuzz_enabled, any(feature = "runtime-benchmarks", feature = "fuzzing"), $); +sp_core::generate_feature_enabled_macro!( + runtime_benchmarks_enabled, + feature = "runtime-benchmarks", + $ +); + +sp_core::generate_feature_enabled_macro!( + runtime_benchmarks_fuzz_or_std_enabled, + any(feature = "runtime-benchmarks", feature = "fuzzing", feature = "std"), + $ +); From 890451221db37176e13cb1a306246f02de80590a Mon Sep 17 00:00:00 2001 From: Vladimir Istyufeev Date: Tue, 2 May 2023 13:34:14 +0400 Subject: [PATCH 442/558] CI: migrate to Google Cloud (#13994) --- .gitlab-ci.yml | 10 +++++++--- scripts/ci/gitlab/pipeline/test.yml | 2 -- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 100dd0d3d..f9b4763c1 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -33,9 +33,9 @@ stages: - test - build - publish + - notify - zombienet - deploy - - notify workflow: rules: @@ -46,15 +46,19 @@ variables: GIT_STRATEGY: fetch GIT_DEPTH: 100 CARGO_INCREMENTAL: 0 - DOCKER_OS: "debian:stretch" + DOCKER_OS: "debian:bullseye" ARCH: "x86_64" CI_IMAGE: "paritytech/ci-linux:production" BUILDAH_IMAGE: "quay.io/buildah/stable:v1.29" BUILDAH_COMMAND: "buildah --storage-driver overlay2" RELENG_SCRIPTS_BRANCH: "master" + RUSTY_CACHIER_SINGLE_BRANCH: master RUSTY_CACHIER_DONT_OPERATE_ON_MAIN_BRANCH: "true" + RUSTY_CACHIER_MINIO_ALIAS: rustycachier_gcs + RUSTY_CACHIER_MINIO_BUCKET: parity-build-rusty-cachier RUSTY_CACHIER_COMPRESSION_METHOD: zstd + NEXTEST_FAILURE_OUTPUT: immediate-final NEXTEST_SUCCESS_OUTPUT: final ZOMBIENET_IMAGE: "docker.io/paritytech/zombienet:v1.3.43" @@ -141,7 +145,7 @@ default: after_script: - !reference [.rusty-cachier, after_script] tags: - - linux-docker + - linux-docker-vm-c2 # rusty-cachier's hidden job. Parts of this job are used to instrument the pipeline's other real jobs with rusty-cachier # Description of the commands is available here - https://gitlab.parity.io/parity/infrastructure/ci_cd/rusty-cachier/client#description diff --git a/scripts/ci/gitlab/pipeline/test.yml b/scripts/ci/gitlab/pipeline/test.yml index c73268caa..82f024d35 100644 --- a/scripts/ci/gitlab/pipeline/test.yml +++ b/scripts/ci/gitlab/pipeline/test.yml @@ -115,8 +115,6 @@ cargo-check-benches: | tee ./artifacts/benches/$CI_COMMIT_REF_NAME-$CI_COMMIT_SHORT_SHA/::node::import::native::sr25519::transfer_keep_alive::paritydb::small.json ;; esac - tags: - - linux-docker-benches node-bench-regression-guard: # it's not belong to `build` semantically, but dag jobs can't depend on each other From cb5be456650d3b46a3bacf5eda290e680c968bb8 Mon Sep 17 00:00:00 2001 From: Alexandru Vasile <60601340+lexnv@users.noreply.github.com> Date: Tue, 2 May 2023 18:00:34 +0300 Subject: [PATCH 443/558] rpc: Use the blocks pinning API for chainHead methods (#13233) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * rpc/chain_head: Add backend to subscription management Signed-off-by: Alexandru Vasile * rpc/chain_head: Pin blocks internally and adjust testing Signed-off-by: Alexandru Vasile * client/in_mem: Reference for the number of pinned blocks Signed-off-by: Alexandru Vasile * rpc/tests: Check in-memory references to pinned blocks Signed-off-by: Alexandru Vasile * rpc/chain_head: Fix clippy Signed-off-by: Alexandru Vasile * rpc/chain_head: Remove unused comment Signed-off-by: Alexandru Vasile * rpc/chain_head: Place subscription handle under `Arc` and unpin blocks on drop Signed-off-by: Alexandru Vasile * rpc/tests: Check all pinned blocks are unpinned on drop Signed-off-by: Alexandru Vasile * Apply suggestions from code review Co-authored-by: Bastian Köcher * Update client/rpc-spec-v2/src/chain_head/subscription.rs Co-authored-by: Bastian Köcher * rpc/tests: Retry fetching the pinned references for CI correctness Signed-off-by: Alexandru Vasile * client/service: Use 512 as maximum number of pinned blocks Signed-off-by: Alexandru Vasile * chain_head: Fix merging conflicts Signed-off-by: Alexandru Vasile * rpc/chain_head: Adjust subscriptions to use pinning API Signed-off-by: Alexandru Vasile * rpc/chain_head/tests: Test subscription management Signed-off-by: Alexandru Vasile * rpc/chain_head: Adjust chain_head follow to the new API Signed-off-by: Alexandru Vasile * rpc/chain_head: Adjust chain_head.rs to the new API Signed-off-by: Alexandru Vasile * rpc/chain_head/tests: Adjust test.rs to the new API Signed-off-by: Alexandru Vasile * client/builder: Use new chainHead API Signed-off-by: Alexandru Vasile * rpc/chain_head: Fix documentation Signed-off-by: Alexandru Vasile * rpc/chain_head: Fix clippy Signed-off-by: Alexandru Vasile * client/in_mem: ChainHead no longer uses `in_mem::children` Signed-off-by: Alexandru Vasile * Update client/rpc-spec-v2/src/chain_head/subscription.rs Co-authored-by: Sebastian Kunert * Update client/rpc-spec-v2/src/chain_head/subscription.rs Co-authored-by: Sebastian Kunert * Update client/rpc-spec-v2/src/chain_head/subscription.rs Co-authored-by: Sebastian Kunert * Update client/rpc-spec-v2/src/chain_head/subscription.rs Co-authored-by: Sebastian Kunert * chain_head: Add block state machine Signed-off-by: Alexandru Vasile * Address feedback Signed-off-by: Alexandru Vasile * Use new_native_or_wasm_executor Signed-off-by: Alexandru Vasile * chain_head: Remove 'static on Backend Signed-off-by: Alexandru Vasile * chain_head: Add documentation Signed-off-by: Alexandru Vasile * chain_head: Lock blocks before async blocks Signed-off-by: Alexandru Vasile * chain_head_follower: Remove static on backend Signed-off-by: Alexandru Vasile * Update client/service/src/builder.rs Co-authored-by: Davide Galassi * Update client/service/src/builder.rs Co-authored-by: Davide Galassi * chain_head: Add BlockHeaderAbsent to the PartialEq impl Signed-off-by: Alexandru Vasile * client: Add better documentation around pinning constants Signed-off-by: Alexandru Vasile * chain_head: Move subscription to dedicated module Signed-off-by: Alexandru Vasile * subscription: Rename global pin / unpin functions Signed-off-by: Alexandru Vasile --------- Signed-off-by: Alexandru Vasile Co-authored-by: Bastian Köcher Co-authored-by: parity-processbot <> Co-authored-by: Sebastian Kunert Co-authored-by: Davide Galassi --- Cargo.lock | 1 + client/api/src/in_mem.rs | 25 +- client/rpc-spec-v2/Cargo.toml | 1 + .../rpc-spec-v2/src/chain_head/chain_head.rs | 152 +-- .../src/chain_head/chain_head_follow.rs | 24 +- .../src/chain_head/subscription.rs | 285 ------ .../src/chain_head/subscription/error.rs | 66 ++ .../src/chain_head/subscription/inner.rs | 940 ++++++++++++++++++ .../src/chain_head/subscription/mod.rs | 125 +++ client/rpc-spec-v2/src/chain_head/tests.rs | 151 ++- client/service/src/builder.rs | 22 +- 11 files changed, 1428 insertions(+), 364 deletions(-) delete mode 100644 client/rpc-spec-v2/src/chain_head/subscription.rs create mode 100644 client/rpc-spec-v2/src/chain_head/subscription/error.rs create mode 100644 client/rpc-spec-v2/src/chain_head/subscription/inner.rs create mode 100644 client/rpc-spec-v2/src/chain_head/subscription/mod.rs diff --git a/Cargo.lock b/Cargo.lock index a1244f271..f20baae36 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -9441,6 +9441,7 @@ dependencies = [ "sc-block-builder", "sc-chain-spec", "sc-client-api", + "sc-service", "sc-transaction-pool-api", "sc-utils", "serde", diff --git a/client/api/src/in_mem.rs b/client/api/src/in_mem.rs index 0a29a4dba..7c87b479d 100644 --- a/client/api/src/in_mem.rs +++ b/client/api/src/in_mem.rs @@ -618,6 +618,7 @@ where states: RwLock>>>, blockchain: Blockchain, import_lock: RwLock<()>, + pinned_blocks: RwLock>, } impl Backend @@ -625,13 +626,28 @@ where Block::Hash: Ord, { /// Create a new instance of in-mem backend. + /// + /// # Warning + /// + /// For testing purposes only! pub fn new() -> Self { Backend { states: RwLock::new(HashMap::new()), blockchain: Blockchain::new(), import_lock: Default::default(), + pinned_blocks: Default::default(), } } + + /// Return the number of references active for a pinned block. + /// + /// # Warning + /// + /// For testing purposes only! + pub fn pin_refs(&self, hash: &::Hash) -> Option { + let blocks = self.pinned_blocks.read(); + blocks.get(hash).map(|value| *value) + } } impl backend::AuxStore for Backend @@ -781,11 +797,16 @@ where false } - fn pin_block(&self, _: ::Hash) -> blockchain::Result<()> { + fn pin_block(&self, hash: ::Hash) -> blockchain::Result<()> { + let mut blocks = self.pinned_blocks.write(); + *blocks.entry(hash).or_default() += 1; Ok(()) } - fn unpin_block(&self, _: ::Hash) {} + fn unpin_block(&self, hash: ::Hash) { + let mut blocks = self.pinned_blocks.write(); + blocks.entry(hash).and_modify(|counter| *counter -= 1).or_insert(-1); + } } impl backend::LocalBackend for Backend where Block::Hash: Ord {} diff --git a/client/rpc-spec-v2/Cargo.toml b/client/rpc-spec-v2/Cargo.toml index 23b96877f..1f0cac18d 100644 --- a/client/rpc-spec-v2/Cargo.toml +++ b/client/rpc-spec-v2/Cargo.toml @@ -43,5 +43,6 @@ substrate-test-runtime = { version = "2.0.0", path = "../../test-utils/runtime" sp-consensus = { version = "0.10.0-dev", path = "../../primitives/consensus/common" } sp-maybe-compressed-blob = { version = "4.1.0-dev", path = "../../primitives/maybe-compressed-blob" } sc-block-builder = { version = "0.10.0-dev", path = "../block-builder" } +sc-service = { version = "0.10.0-dev", features = ["test-helpers"], path = "../service" } sc-utils = { version = "4.0.0-dev", path = "../utils" } assert_matches = "1.3.0" diff --git a/client/rpc-spec-v2/src/chain_head/chain_head.rs b/client/rpc-spec-v2/src/chain_head/chain_head.rs index c63e874c1..763fc5d9a 100644 --- a/client/rpc-spec-v2/src/chain_head/chain_head.rs +++ b/client/rpc-spec-v2/src/chain_head/chain_head.rs @@ -24,7 +24,7 @@ use crate::{ chain_head_follow::ChainHeadFollower, error::Error as ChainHeadRpcError, event::{ChainHeadEvent, ChainHeadResult, ErrorEvent, FollowEvent, NetworkConfig}, - subscription::SubscriptionManagement, + subscription::{SubscriptionManagement, SubscriptionManagementError}, }, SubscriptionTaskExecutor, }; @@ -44,12 +44,12 @@ use sp_api::CallApiAt; use sp_blockchain::{Error as BlockChainError, HeaderBackend, HeaderMetadata}; use sp_core::{hexdisplay::HexDisplay, storage::well_known_keys, traits::CallContext, Bytes}; use sp_runtime::traits::Block as BlockT; -use std::{marker::PhantomData, sync::Arc}; +use std::{marker::PhantomData, sync::Arc, time::Duration}; pub(crate) const LOG_TARGET: &str = "rpc-spec-v2"; /// An API for chain head RPC calls. -pub struct ChainHead { +pub struct ChainHead, Block: BlockT, Client> { /// Substrate client. client: Arc, /// Backend of the chain. @@ -57,16 +57,14 @@ pub struct ChainHead { /// Executor to spawn subscriptions. executor: SubscriptionTaskExecutor, /// Keep track of the pinned blocks for each subscription. - subscriptions: Arc>, + subscriptions: Arc>, /// The hexadecimal encoded hash of the genesis block. genesis_hash: String, - /// The maximum number of pinned blocks allowed per connection. - max_pinned_blocks: usize, /// Phantom member to pin the block type. _phantom: PhantomData, } -impl ChainHead { +impl, Block: BlockT, Client> ChainHead { /// Create a new [`ChainHead`]. pub fn new>( client: Arc, @@ -74,16 +72,20 @@ impl ChainHead { executor: SubscriptionTaskExecutor, genesis_hash: GenesisHash, max_pinned_blocks: usize, + max_pinned_duration: Duration, ) -> Self { let genesis_hash = format!("0x{:?}", HexDisplay::from(&genesis_hash.as_ref())); Self { client, - backend, + backend: backend.clone(), executor, - subscriptions: Arc::new(SubscriptionManagement::new()), + subscriptions: Arc::new(SubscriptionManagement::new( + max_pinned_blocks, + max_pinned_duration, + backend, + )), genesis_hash, - max_pinned_blocks, _phantom: PhantomData, } } @@ -159,9 +161,8 @@ where return Err(err) }, }; - // Keep track of the subscription. - let Some((rx_stop, sub_handle)) = self.subscriptions.insert_subscription(sub_id.clone(), runtime_updates, self.max_pinned_blocks) else { + let Some(rx_stop) = self.subscriptions.insert_subscription(sub_id.clone(), runtime_updates) else { // Inserting the subscription can only fail if the JsonRPSee // generated a duplicate subscription ID. debug!(target: LOG_TARGET, "[follow][id={:?}] Subscription already accepted", sub_id); @@ -177,7 +178,7 @@ where let mut chain_head_follow = ChainHeadFollower::new( client, backend, - sub_handle, + subscriptions.clone(), runtime_updates, sub_id.clone(), ); @@ -202,19 +203,28 @@ where let client = self.client.clone(); let subscriptions = self.subscriptions.clone(); - let fut = async move { - let Some(handle) = subscriptions.get_subscription(&follow_subscription) else { + let block_guard = match subscriptions.lock_block(&follow_subscription, hash) { + Ok(block) => block, + Err(SubscriptionManagementError::SubscriptionAbsent) => { // Invalid invalid subscription ID. let _ = sink.send(&ChainHeadEvent::::Disjoint); - return - }; - - // Block is not part of the subscription. - if !handle.contains_block(&hash) { + return Ok(()) + }, + Err(SubscriptionManagementError::BlockHashAbsent) => { + // Block is not part of the subscription. let _ = sink.reject(ChainHeadRpcError::InvalidBlock); - return - } + return Ok(()) + }, + Err(error) => { + let _ = sink.send(&ChainHeadEvent::::Error(ErrorEvent { + error: error.to_string(), + })); + return Ok(()) + }, + }; + let fut = async move { + let _block_guard = block_guard; let event = match client.block(hash) { Ok(Some(signed_block)) => { let extrinsics = signed_block.block.extrinsics(); @@ -226,10 +236,10 @@ where debug!( target: LOG_TARGET, "[body][id={:?}] Stopping subscription because hash={:?} was pruned", - follow_subscription, + &follow_subscription, hash ); - handle.stop(); + subscriptions.remove_subscription(&follow_subscription); ChainHeadEvent::::Disjoint }, Err(error) => ChainHeadEvent::Error(ErrorEvent { error: error.to_string() }), @@ -246,16 +256,19 @@ where follow_subscription: String, hash: Block::Hash, ) -> RpcResult> { - let Some(handle) = self.subscriptions.get_subscription(&follow_subscription) else { - // Invalid invalid subscription ID. - return Ok(None) + let _block_guard = match self.subscriptions.lock_block(&follow_subscription, hash) { + Ok(block) => block, + Err(SubscriptionManagementError::SubscriptionAbsent) => { + // Invalid invalid subscription ID. + return Ok(None) + }, + Err(SubscriptionManagementError::BlockHashAbsent) => { + // Block is not part of the subscription. + return Err(ChainHeadRpcError::InvalidBlock.into()) + }, + Err(_) => return Err(ChainHeadRpcError::InvalidBlock.into()), }; - // Block is not part of the subscription. - if !handle.contains_block(&hash) { - return Err(ChainHeadRpcError::InvalidBlock.into()) - } - self.client .header(hash) .map(|opt_header| opt_header.map(|h| format!("0x{:?}", HexDisplay::from(&h.encode())))) @@ -286,19 +299,28 @@ where let client = self.client.clone(); let subscriptions = self.subscriptions.clone(); - let fut = async move { - let Some(handle) = subscriptions.get_subscription(&follow_subscription) else { + let block_guard = match subscriptions.lock_block(&follow_subscription, hash) { + Ok(block) => block, + Err(SubscriptionManagementError::SubscriptionAbsent) => { // Invalid invalid subscription ID. let _ = sink.send(&ChainHeadEvent::::Disjoint); - return - }; - - // Block is not part of the subscription. - if !handle.contains_block(&hash) { + return Ok(()) + }, + Err(SubscriptionManagementError::BlockHashAbsent) => { + // Block is not part of the subscription. let _ = sink.reject(ChainHeadRpcError::InvalidBlock); - return - } + return Ok(()) + }, + Err(error) => { + let _ = sink.send(&ChainHeadEvent::::Error(ErrorEvent { + error: error.to_string(), + })); + return Ok(()) + }, + }; + let fut = async move { + let _block_guard = block_guard; // The child key is provided, use the key to query the child trie. if let Some(child_key) = child_key { // The child key must not be prefixed with ":child_storage:" nor @@ -367,21 +389,29 @@ where let client = self.client.clone(); let subscriptions = self.subscriptions.clone(); - let fut = async move { - let Some(handle) = subscriptions.get_subscription(&follow_subscription) else { + let block_guard = match subscriptions.lock_block(&follow_subscription, hash) { + Ok(block) => block, + Err(SubscriptionManagementError::SubscriptionAbsent) => { // Invalid invalid subscription ID. let _ = sink.send(&ChainHeadEvent::::Disjoint); - return - }; - - // Block is not part of the subscription. - if !handle.contains_block(&hash) { + return Ok(()) + }, + Err(SubscriptionManagementError::BlockHashAbsent) => { + // Block is not part of the subscription. let _ = sink.reject(ChainHeadRpcError::InvalidBlock); - return - } + return Ok(()) + }, + Err(error) => { + let _ = sink.send(&ChainHeadEvent::::Error(ErrorEvent { + error: error.to_string(), + })); + return Ok(()) + }, + }; + let fut = async move { // Reject subscription if runtime_updates is false. - if !handle.has_runtime_updates() { + if !block_guard.has_runtime_updates() { let _ = sink.reject(ChainHeadRpcError::InvalidParam( "The runtime updates flag must be set".into(), )); @@ -417,15 +447,17 @@ where follow_subscription: String, hash: Block::Hash, ) -> RpcResult<()> { - let Some(handle) = self.subscriptions.get_subscription(&follow_subscription) else { - // Invalid invalid subscription ID. - return Ok(()) - }; - - if !handle.unpin_block(&hash) { - return Err(ChainHeadRpcError::InvalidBlock.into()) + match self.subscriptions.unpin_block(&follow_subscription, hash) { + Ok(()) => Ok(()), + Err(SubscriptionManagementError::SubscriptionAbsent) => { + // Invalid invalid subscription ID. + Ok(()) + }, + Err(SubscriptionManagementError::BlockHashAbsent) => { + // Block is not part of the subscription. + Err(ChainHeadRpcError::InvalidBlock.into()) + }, + Err(_) => Err(ChainHeadRpcError::InvalidBlock.into()), } - - Ok(()) } } diff --git a/client/rpc-spec-v2/src/chain_head/chain_head_follow.rs b/client/rpc-spec-v2/src/chain_head/chain_head_follow.rs index a0d19654e..f496f07a3 100644 --- a/client/rpc-spec-v2/src/chain_head/chain_head_follow.rs +++ b/client/rpc-spec-v2/src/chain_head/chain_head_follow.rs @@ -24,7 +24,7 @@ use crate::chain_head::{ BestBlockChanged, Finalized, FollowEvent, Initialized, NewBlock, RuntimeEvent, RuntimeVersionEvent, }, - subscription::{SubscriptionHandle, SubscriptionManagementError}, + subscription::{SubscriptionManagement, SubscriptionManagementError}, }; use futures::{ channel::oneshot, @@ -44,13 +44,13 @@ use sp_runtime::traits::{Block as BlockT, Header as HeaderT, NumberFor}; use std::{collections::HashSet, sync::Arc}; /// Generates the events of the `chainHead_follow` method. -pub struct ChainHeadFollower { +pub struct ChainHeadFollower, Block: BlockT, Client> { /// Substrate client. client: Arc, /// Backend of the chain. backend: Arc, - /// Subscription handle. - sub_handle: SubscriptionHandle, + /// Subscriptions handle. + sub_handle: Arc>, /// Subscription was started with the runtime updates flag. runtime_updates: bool, /// Subscription ID. @@ -59,12 +59,12 @@ pub struct ChainHeadFollower { best_block_cache: Option, } -impl ChainHeadFollower { +impl, Block: BlockT, Client> ChainHeadFollower { /// Create a new [`ChainHeadFollower`]. pub fn new( client: Arc, backend: Arc, - sub_handle: SubscriptionHandle, + sub_handle: Arc>, runtime_updates: bool, sub_id: String, ) -> Self { @@ -221,7 +221,7 @@ where // The initialized event is the first one sent. let finalized_block_hash = startup_point.finalized_hash; - self.sub_handle.pin_block(finalized_block_hash)?; + self.sub_handle.pin_block(&self.sub_id, finalized_block_hash)?; let finalized_block_runtime = self.generate_runtime_event(finalized_block_hash, None); @@ -235,7 +235,7 @@ where finalized_block_descendants.push(initialized_event); for (child, parent) in initial_blocks.into_iter() { - self.sub_handle.pin_block(child)?; + self.sub_handle.pin_block(&self.sub_id, child)?; let new_runtime = self.generate_runtime_event(child, Some(parent)); @@ -310,7 +310,7 @@ where startup_point: &StartupPoint, ) -> Result>, SubscriptionManagementError> { // The block was already pinned by the initial block events or by the finalized event. - if !self.sub_handle.pin_block(notification.hash)? { + if !self.sub_handle.pin_block(&self.sub_id, notification.hash)? { return Ok(Default::default()) } @@ -352,7 +352,7 @@ where std::iter::once(first_header.parent_hash()).chain(finalized_block_hashes.iter()); for (i, (hash, parent)) in finalized_block_hashes.iter().zip(parents).enumerate() { // Check if the block was already reported and thus, is already pinned. - if !self.sub_handle.pin_block(*hash)? { + if !self.sub_handle.pin_block(&self.sub_id, *hash)? { continue } @@ -564,6 +564,10 @@ where stream_item = stream.next(); stop_event = next_stop_event; } + + // If we got here either the substrate streams have closed + // or the `Stop` receiver was triggered. + let _ = sink.send(&FollowEvent::::Stop); } /// Generate the block events for the `chainHead_follow` method. diff --git a/client/rpc-spec-v2/src/chain_head/subscription.rs b/client/rpc-spec-v2/src/chain_head/subscription.rs deleted file mode 100644 index 687374bba..000000000 --- a/client/rpc-spec-v2/src/chain_head/subscription.rs +++ /dev/null @@ -1,285 +0,0 @@ -// This file is part of Substrate. - -// Copyright (C) Parity Technologies (UK) Ltd. -// SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 - -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with this program. If not, see . - -//! Subscription management for tracking subscription IDs to pinned blocks. - -use futures::channel::oneshot; -use parking_lot::RwLock; -use sp_blockchain::Error; -use sp_runtime::traits::Block as BlockT; -use std::{ - collections::{hash_map::Entry, HashMap, HashSet}, - sync::Arc, -}; - -/// Subscription management error. -#[derive(Debug)] -pub enum SubscriptionManagementError { - /// The block cannot be pinned into memory because - /// the subscription has exceeded the maximum number - /// of blocks pinned. - ExceededLimits, - /// Error originated from the blockchain (client or backend). - Blockchain(Error), - /// The database does not contain a block header. - BlockHeaderAbsent, - /// Custom error. - Custom(String), -} - -impl From for SubscriptionManagementError { - fn from(err: Error) -> Self { - SubscriptionManagementError::Blockchain(err) - } -} - -/// Inner subscription data structure. -struct SubscriptionInner { - /// The `runtime_updates` parameter flag of the subscription. - runtime_updates: bool, - /// Signals the "Stop" event. - tx_stop: Option>, - /// The blocks pinned. - blocks: HashSet, - /// The maximum number of pinned blocks allowed per subscription. - max_pinned_blocks: usize, -} - -/// Manage the blocks of a specific subscription ID. -#[derive(Clone)] -pub struct SubscriptionHandle { - inner: Arc>>, -} - -impl SubscriptionHandle { - /// Construct a new [`SubscriptionHandle`]. - fn new(runtime_updates: bool, tx_stop: oneshot::Sender<()>, max_pinned_blocks: usize) -> Self { - SubscriptionHandle { - inner: Arc::new(RwLock::new(SubscriptionInner { - runtime_updates, - tx_stop: Some(tx_stop), - blocks: HashSet::new(), - max_pinned_blocks, - })), - } - } - - /// Trigger the stop event for the current subscription. - /// - /// This can happen on internal failure (ie, the pruning deleted the block from memory) - /// or if the user exceeded the amount of available pinned blocks. - pub fn stop(&self) { - let mut inner = self.inner.write(); - - if let Some(tx_stop) = inner.tx_stop.take() { - let _ = tx_stop.send(()); - } - } - - /// Pin a new block for the current subscription ID. - /// - /// Returns whether the value was newly inserted if the block can be pinned. - /// Otherwise, returns an error if the maximum number of blocks has been exceeded. - pub fn pin_block(&self, hash: Block::Hash) -> Result { - let mut inner = self.inner.write(); - - if inner.blocks.len() == inner.max_pinned_blocks { - // We have reached the limit. However, the block can be already inserted. - if inner.blocks.contains(&hash) { - return Ok(false) - } else { - return Err(SubscriptionManagementError::ExceededLimits) - } - } - - Ok(inner.blocks.insert(hash)) - } - - /// Unpin a new block for the current subscription ID. - /// - /// Returns whether the value was present in the set. - pub fn unpin_block(&self, hash: &Block::Hash) -> bool { - let mut inner = self.inner.write(); - inner.blocks.remove(hash) - } - - /// Check if the block hash is present for the provided subscription ID. - /// - /// Returns `true` if the set contains the block. - pub fn contains_block(&self, hash: &Block::Hash) -> bool { - let inner = self.inner.read(); - inner.blocks.contains(hash) - } - - /// Get the `runtime_updates` flag of this subscription. - pub fn has_runtime_updates(&self) -> bool { - let inner = self.inner.read(); - inner.runtime_updates - } -} - -/// Manage block pinning / unpinning for subscription IDs. -pub struct SubscriptionManagement { - /// Manage subscription by mapping the subscription ID - /// to a set of block hashes. - inner: RwLock>>, -} - -impl SubscriptionManagement { - /// Construct a new [`SubscriptionManagement`]. - pub fn new() -> Self { - SubscriptionManagement { inner: RwLock::new(HashMap::new()) } - } - - /// Insert a new subscription ID. - /// - /// If the subscription was not previously inserted, the method returns a tuple of - /// the receiver that is triggered upon the "Stop" event and the subscription - /// handle. Otherwise, when the subscription ID was already inserted returns none. - pub fn insert_subscription( - &self, - subscription_id: String, - runtime_updates: bool, - max_pinned_blocks: usize, - ) -> Option<(oneshot::Receiver<()>, SubscriptionHandle)> { - let mut subs = self.inner.write(); - - if let Entry::Vacant(entry) = subs.entry(subscription_id) { - let (tx_stop, rx_stop) = oneshot::channel(); - let handle = - SubscriptionHandle::::new(runtime_updates, tx_stop, max_pinned_blocks); - entry.insert(handle.clone()); - Some((rx_stop, handle)) - } else { - None - } - } - - /// Remove the subscription ID with associated pinned blocks. - pub fn remove_subscription(&self, subscription_id: &String) { - let mut subs = self.inner.write(); - subs.remove(subscription_id); - } - - /// Obtain the specific subscription handle. - pub fn get_subscription(&self, subscription_id: &String) -> Option> { - let subs = self.inner.write(); - subs.get(subscription_id).and_then(|handle| Some(handle.clone())) - } -} - -#[cfg(test)] -mod tests { - use super::*; - use sp_core::H256; - use substrate_test_runtime_client::runtime::Block; - - #[test] - fn subscription_check_id() { - let subs = SubscriptionManagement::::new(); - - let id = "abc".to_string(); - let hash = H256::random(); - - let handle = subs.get_subscription(&id); - assert!(handle.is_none()); - - let (_, handle) = subs.insert_subscription(id.clone(), false, 10).unwrap(); - assert!(!handle.contains_block(&hash)); - - subs.remove_subscription(&id); - - let handle = subs.get_subscription(&id); - assert!(handle.is_none()); - } - - #[test] - fn subscription_check_block() { - let subs = SubscriptionManagement::::new(); - - let id = "abc".to_string(); - let hash = H256::random(); - - // Check with subscription. - let (_, handle) = subs.insert_subscription(id.clone(), false, 10).unwrap(); - assert!(!handle.contains_block(&hash)); - assert!(!handle.unpin_block(&hash)); - - handle.pin_block(hash).unwrap(); - assert!(handle.contains_block(&hash)); - // Unpin an invalid block. - assert!(!handle.unpin_block(&H256::random())); - - // Unpin the valid block. - assert!(handle.unpin_block(&hash)); - assert!(!handle.contains_block(&hash)); - } - - #[test] - fn subscription_check_stop_event() { - let subs = SubscriptionManagement::::new(); - - let id = "abc".to_string(); - - // Check with subscription. - let (mut rx_stop, handle) = subs.insert_subscription(id.clone(), false, 10).unwrap(); - - // Check the stop signal was not received. - let res = rx_stop.try_recv().unwrap(); - assert!(res.is_none()); - - // Inserting a second time returns None. - let res = subs.insert_subscription(id.clone(), false, 10); - assert!(res.is_none()); - - handle.stop(); - - // Check the signal was received. - let res = rx_stop.try_recv().unwrap(); - assert!(res.is_some()); - } - - #[test] - fn subscription_check_data() { - let subs = SubscriptionManagement::::new(); - - let id = "abc".to_string(); - let (_, handle) = subs.insert_subscription(id.clone(), false, 10).unwrap(); - assert!(!handle.has_runtime_updates()); - - let id2 = "abcd".to_string(); - let (_, handle) = subs.insert_subscription(id2.clone(), true, 10).unwrap(); - assert!(handle.has_runtime_updates()); - } - - #[test] - fn subscription_check_max_pinned() { - let subs = SubscriptionManagement::::new(); - - let id = "abc".to_string(); - let hash = H256::random(); - let hash_2 = H256::random(); - let (_, handle) = subs.insert_subscription(id.clone(), false, 1).unwrap(); - - handle.pin_block(hash).unwrap(); - // The same block can be pinned multiple times. - handle.pin_block(hash).unwrap(); - // Exceeded number of pinned blocks. - handle.pin_block(hash_2).unwrap_err(); - } -} diff --git a/client/rpc-spec-v2/src/chain_head/subscription/error.rs b/client/rpc-spec-v2/src/chain_head/subscription/error.rs new file mode 100644 index 000000000..443ee9fb8 --- /dev/null +++ b/client/rpc-spec-v2/src/chain_head/subscription/error.rs @@ -0,0 +1,66 @@ +// This file is part of Substrate. + +// Copyright (C) Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +use sp_blockchain::Error; + +/// Subscription management error. +#[derive(Debug, thiserror::Error)] +pub enum SubscriptionManagementError { + /// The block cannot be pinned into memory because + /// the subscription has exceeded the maximum number + /// of blocks pinned. + #[error("Exceeded pinning limits")] + ExceededLimits, + /// Error originated from the blockchain (client or backend). + #[error("Blockchain error {0}")] + Blockchain(Error), + /// The database does not contain a block hash. + #[error("Block hash is absent")] + BlockHashAbsent, + /// The database does not contain a block header. + #[error("Block header is absent")] + BlockHeaderAbsent, + /// The specified subscription ID is not present. + #[error("Subscription is absent")] + SubscriptionAbsent, + /// Custom error. + #[error("Subscription error {0}")] + Custom(String), +} + +// Blockchain error does not implement `PartialEq` needed for testing. +impl PartialEq for SubscriptionManagementError { + fn eq(&self, other: &SubscriptionManagementError) -> bool { + match (self, other) { + (Self::ExceededLimits, Self::ExceededLimits) | + // Not needed for testing. + (Self::Blockchain(_), Self::Blockchain(_)) | + (Self::BlockHashAbsent, Self::BlockHashAbsent) | + (Self::BlockHeaderAbsent, Self::BlockHeaderAbsent) | + (Self::SubscriptionAbsent, Self::SubscriptionAbsent) => true, + (Self::Custom(lhs), Self::Custom(rhs)) => lhs == rhs, + _ => false, + } + } +} + +impl From for SubscriptionManagementError { + fn from(err: Error) -> Self { + SubscriptionManagementError::Blockchain(err) + } +} diff --git a/client/rpc-spec-v2/src/chain_head/subscription/inner.rs b/client/rpc-spec-v2/src/chain_head/subscription/inner.rs new file mode 100644 index 000000000..8865daa83 --- /dev/null +++ b/client/rpc-spec-v2/src/chain_head/subscription/inner.rs @@ -0,0 +1,940 @@ +// This file is part of Substrate. + +// Copyright (C) Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +use futures::channel::oneshot; +use sc_client_api::Backend; +use sp_runtime::traits::Block as BlockT; +use std::{ + collections::{hash_map::Entry, HashMap}, + sync::Arc, + time::{Duration, Instant}, +}; + +use crate::chain_head::subscription::SubscriptionManagementError; + +/// The state machine of a block of a single subscription ID. +/// +/// # Motivation +/// +/// Each block is registered twice: once from the `BestBlock` event +/// and once from the `Finalized` event. +/// +/// The state of a block must be tracked until both events register the +/// block and the user calls `unpin`. +/// +/// Otherwise, the following race might happen: +/// T0. BestBlock event: hash is tracked and pinned in backend. +/// T1. User calls unpin: hash is untracked and unpinned in backend. +/// T2. Finalized event: hash is tracked (no previous history) and pinned again. +/// +/// # State Machine Transition +/// +/// ```ignore +/// (register) +/// [ REGISTERED ] ---------------> [ FULLY REGISTERED ] +/// | | +/// | (unpin) | (unpin) +/// | | +/// V (register) V +/// [ UNPINNED ] -----------------> [ FULLY UNPINNED ] +/// ``` +#[derive(Debug, Clone, PartialEq)] +enum BlockStateMachine { + /// The block was registered by one event (either `Finalized` or `BestBlock` event). + /// + /// Unpin was not called. + Registered, + /// The block was registered by both events (`Finalized` and `BestBlock` events). + /// + /// Unpin was not called. + FullyRegistered, + /// The block was registered by one event (either `Finalized` or `BestBlock` event), + /// + /// Unpin __was__ called. + Unpinned, + /// The block was registered by both events (`Finalized` and `BestBlock` events). + /// + /// Unpin __was__ called. + FullyUnpinned, +} + +impl BlockStateMachine { + fn new() -> Self { + BlockStateMachine::Registered + } + + fn advance_register(&mut self) { + match self { + BlockStateMachine::Registered => *self = BlockStateMachine::FullyRegistered, + BlockStateMachine::Unpinned => *self = BlockStateMachine::FullyUnpinned, + _ => (), + } + } + + fn advance_unpin(&mut self) { + match self { + BlockStateMachine::Registered => *self = BlockStateMachine::Unpinned, + BlockStateMachine::FullyRegistered => *self = BlockStateMachine::FullyUnpinned, + _ => (), + } + } + + fn was_unpinned(&self) -> bool { + match self { + BlockStateMachine::Unpinned => true, + BlockStateMachine::FullyUnpinned => true, + _ => false, + } + } +} + +struct BlockState { + /// The state machine of this block. + state_machine: BlockStateMachine, + /// The timestamp when the block was inserted. + timestamp: Instant, +} + +/// The state of a single subscription ID. +struct SubscriptionState { + /// The `runtime_updates` parameter flag of the subscription. + runtime_updates: bool, + /// Signals the "Stop" event. + tx_stop: Option>, + /// Track the block hashes available for this subscription. + /// + /// This implementation assumes: + /// - most of the time subscriptions keep a few blocks of the chain's head pinned + /// - iteration through the blocks happens only when the hard limit is exceeded. + /// + /// Considering the assumption, iterating (in the unlike case) the hashmap O(N) is + /// more time efficient and code friendly than paying for: + /// - extra space: an extra BTreeMap to older hashes by oldest insertion + /// - extra time: O(log(N)) for insert/remove/find each `pin` block time per subscriptions + blocks: HashMap, +} + +impl SubscriptionState { + /// Trigger the stop event for the current subscription. + /// + /// This can happen on internal failure (ie, the pruning deleted the block from memory) + /// or if the subscription exceeded the available pinned blocks. + fn stop(&mut self) { + if let Some(tx_stop) = self.tx_stop.take() { + let _ = tx_stop.send(()); + } + } + + /// Keep track of the given block hash for this subscription. + /// + /// This does not handle pinning in the backend. + /// + /// Returns: + /// - true if this is the first time that the block is registered + /// - false if the block was already registered + fn register_block(&mut self, hash: Block::Hash) -> bool { + match self.blocks.entry(hash) { + Entry::Occupied(mut occupied) => { + let block_state = occupied.get_mut(); + + block_state.state_machine.advance_register(); + // Block was registered twice and unpin was called. + if block_state.state_machine == BlockStateMachine::FullyUnpinned { + occupied.remove(); + } + + // Second time we register this block. + false + }, + Entry::Vacant(vacant) => { + vacant.insert(BlockState { + state_machine: BlockStateMachine::new(), + timestamp: Instant::now(), + }); + + // First time we register this block. + true + }, + } + } + + /// A block is unregistered when the user calls `unpin`. + /// + /// Returns: + /// - true if the block can be unpinned. + /// - false if the subscription does not contain the block or it was unpinned. + fn unregister_block(&mut self, hash: Block::Hash) -> bool { + match self.blocks.entry(hash) { + Entry::Occupied(mut occupied) => { + let block_state = occupied.get_mut(); + + // Cannot unpin a block twice. + if block_state.state_machine.was_unpinned() { + return false + } + + block_state.state_machine.advance_unpin(); + // Block was registered twice and unpin was called. + if block_state.state_machine == BlockStateMachine::FullyUnpinned { + occupied.remove(); + } + + true + }, + // Block was not tracked. + Entry::Vacant(_) => false, + } + } + + /// A subscription contains a block when the block was + /// registered (`pin` was called) and the block was not `unpinned` yet. + /// + /// Returns `true` if the subscription contains the block. + fn contains_block(&self, hash: Block::Hash) -> bool { + let Some(state) = self.blocks.get(&hash) else { + // Block was not tracked. + return false + }; + + // Subscription no longer contains the block if `unpin` was called. + !state.state_machine.was_unpinned() + } + + /// Get the timestamp of the oldest inserted block. + /// + /// # Note + /// + /// This iterates over all the blocks of the subscription. + fn find_oldest_block_timestamp(&self) -> Instant { + let mut timestamp = Instant::now(); + for (_, state) in self.blocks.iter() { + timestamp = std::cmp::min(timestamp, state.timestamp); + } + timestamp + } +} + +/// Keeps a specific block pinned while the handle is alive. +/// This object ensures that the block is not unpinned while +/// executing an RPC method call. +pub struct BlockGuard> { + hash: Block::Hash, + runtime_updates: bool, + backend: Arc, +} + +// Custom implementation of Debug to avoid bounds on `backend: Debug` for `unwrap_err()` needed for +// testing. +impl> std::fmt::Debug for BlockGuard { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "BlockGuard hash {:?} runtime_updates {:?}", self.hash, self.runtime_updates) + } +} + +impl> BlockGuard { + /// Construct a new [`BlockGuard`] . + fn new( + hash: Block::Hash, + runtime_updates: bool, + backend: Arc, + ) -> Result { + backend + .pin_block(hash) + .map_err(|err| SubscriptionManagementError::Custom(err.to_string()))?; + + Ok(Self { hash, runtime_updates, backend }) + } + + /// The `runtime_updates` flag of the subscription. + pub fn has_runtime_updates(&self) -> bool { + self.runtime_updates + } +} + +impl> Drop for BlockGuard { + fn drop(&mut self) { + self.backend.unpin_block(self.hash); + } +} + +pub struct SubscriptionsInner> { + /// Reference count the block hashes across all subscriptions. + /// + /// The pinned blocks cannot exceed the [`Self::global_limit`] limit. + /// When the limit is exceeded subscriptions are stopped via the `Stop` event. + global_blocks: HashMap, + /// The maximum number of pinned blocks across all subscriptions. + global_max_pinned_blocks: usize, + /// The maximum duration that a block is allowed to be pinned per subscription. + local_max_pin_duration: Duration, + /// Map the subscription ID to internal details of the subscription. + subs: HashMap>, + /// Backend pinning / unpinning blocks. + /// + /// The `Arc` is handled one level-above, but substrate exposes the backend as Arc. + backend: Arc, +} + +impl> SubscriptionsInner { + /// Construct a new [`SubscriptionsInner`] from the specified limits. + pub fn new( + global_max_pinned_blocks: usize, + local_max_pin_duration: Duration, + backend: Arc, + ) -> Self { + SubscriptionsInner { + global_blocks: Default::default(), + global_max_pinned_blocks, + local_max_pin_duration, + subs: Default::default(), + backend, + } + } + + /// Insert a new subscription ID. + pub fn insert_subscription( + &mut self, + sub_id: String, + runtime_updates: bool, + ) -> Option> { + if let Entry::Vacant(entry) = self.subs.entry(sub_id) { + let (tx_stop, rx_stop) = oneshot::channel(); + let state = SubscriptionState:: { + runtime_updates, + tx_stop: Some(tx_stop), + blocks: Default::default(), + }; + entry.insert(state); + Some(rx_stop) + } else { + None + } + } + + /// Remove the subscription ID with associated pinned blocks. + pub fn remove_subscription(&mut self, sub_id: &str) { + let Some(mut sub) = self.subs.remove(sub_id) else { + return + }; + + // The `Stop` event can be generated only once. + sub.stop(); + + for (hash, state) in sub.blocks.iter() { + if !state.state_machine.was_unpinned() { + self.global_unregister_block(*hash); + } + } + } + + /// Ensure that a new block could be pinned. + /// + /// If the global number of blocks has been reached this method + /// will remove all subscriptions that have blocks older than the + /// specified pin duration. + /// + /// If after removing all subscriptions that exceed the pin duration + /// there is no space for pinning a new block, then all subscriptions + /// are terminated. + /// + /// Returns true if the given subscription is also terminated. + fn ensure_block_space(&mut self, request_sub_id: &str) -> bool { + if self.global_blocks.len() < self.global_max_pinned_blocks { + return false + } + + // Terminate all subscriptions that have blocks older than + // the specified pin duration. + let now = Instant::now(); + + let to_remove: Vec<_> = self + .subs + .iter_mut() + .filter_map(|(sub_id, sub)| { + let sub_time = sub.find_oldest_block_timestamp(); + // Subscriptions older than the specified pin duration should be removed. + let should_remove = match now.checked_duration_since(sub_time) { + Some(duration) => duration > self.local_max_pin_duration, + None => true, + }; + should_remove.then(|| sub_id.clone()) + }) + .collect(); + + let mut is_terminated = false; + for sub_id in to_remove { + if sub_id == request_sub_id { + is_terminated = true; + } + self.remove_subscription(&sub_id); + } + + // Make sure we have enough space after first pass of terminating subscriptions. + if self.global_blocks.len() < self.global_max_pinned_blocks { + return is_terminated + } + + // Sanity check: cannot uphold `chainHead` guarantees anymore. We have not + // found any subscriptions that have older pinned blocks to terminate. + let to_remove: Vec<_> = self.subs.keys().map(|sub_id| sub_id.clone()).collect(); + for sub_id in to_remove { + if sub_id == request_sub_id { + is_terminated = true; + } + self.remove_subscription(&sub_id); + } + return is_terminated + } + + pub fn pin_block( + &mut self, + sub_id: &str, + hash: Block::Hash, + ) -> Result { + let Some(sub) = self.subs.get_mut(sub_id) else { + return Err(SubscriptionManagementError::SubscriptionAbsent) + }; + + // Block was already registered for this subscription and therefore + // globally tracked. + if !sub.register_block(hash) { + return Ok(false) + } + + // Ensure we have enough space only if the hash is not globally registered. + if !self.global_blocks.contains_key(&hash) { + // Subscription ID was terminated while ensuring enough space. + if self.ensure_block_space(sub_id) { + return Err(SubscriptionManagementError::ExceededLimits) + } + } + + self.global_register_block(hash)?; + Ok(true) + } + + /// Register the block internally. + /// + /// If the block is present the reference counter is increased. + /// If this is a new block, the block is pinned in the backend. + fn global_register_block( + &mut self, + hash: Block::Hash, + ) -> Result<(), SubscriptionManagementError> { + match self.global_blocks.entry(hash) { + Entry::Occupied(mut occupied) => { + *occupied.get_mut() += 1; + }, + Entry::Vacant(vacant) => { + self.backend + .pin_block(hash) + .map_err(|err| SubscriptionManagementError::Custom(err.to_string()))?; + + vacant.insert(1); + }, + }; + Ok(()) + } + + /// Unregister the block internally. + /// + /// If the block is present the reference counter is decreased. + /// If this is the last reference of the block, the block + /// is unpinned from the backend and removed from internal tracking. + fn global_unregister_block(&mut self, hash: Block::Hash) { + if let Entry::Occupied(mut occupied) = self.global_blocks.entry(hash) { + let counter = occupied.get_mut(); + if *counter == 1 { + // Unpin the block from the backend. + self.backend.unpin_block(hash); + occupied.remove(); + } else { + *counter -= 1; + } + } + } + + pub fn unpin_block( + &mut self, + sub_id: &str, + hash: Block::Hash, + ) -> Result<(), SubscriptionManagementError> { + let Some(sub) = self.subs.get_mut(sub_id) else { + return Err(SubscriptionManagementError::SubscriptionAbsent) + }; + + // Check that unpin was not called before and the block was pinned + // for this subscription. + if !sub.unregister_block(hash) { + return Err(SubscriptionManagementError::BlockHashAbsent) + } + + self.global_unregister_block(hash); + Ok(()) + } + + pub fn lock_block( + &mut self, + sub_id: &str, + hash: Block::Hash, + ) -> Result, SubscriptionManagementError> { + let Some(sub) = self.subs.get(sub_id) else { + return Err(SubscriptionManagementError::SubscriptionAbsent) + }; + + if !sub.contains_block(hash) { + return Err(SubscriptionManagementError::BlockHashAbsent) + } + + BlockGuard::new(hash, sub.runtime_updates, self.backend.clone()) + } +} + +#[cfg(test)] +mod tests { + use super::*; + use sc_block_builder::BlockBuilderProvider; + use sc_service::client::new_in_mem; + use sp_consensus::BlockOrigin; + use sp_core::{testing::TaskExecutor, H256}; + use substrate_test_runtime_client::{ + prelude::*, + runtime::{Block, RuntimeApi}, + Client, ClientBlockImportExt, GenesisInit, + }; + + fn init_backend() -> ( + Arc>, + Arc>>, + ) { + let backend = Arc::new(sc_client_api::in_mem::Backend::new()); + let executor = substrate_test_runtime_client::new_native_or_wasm_executor(); + let client_config = sc_service::ClientConfig::default(); + let genesis_block_builder = sc_service::GenesisBlockBuilder::new( + &substrate_test_runtime_client::GenesisParameters::default().genesis_storage(), + !client_config.no_genesis, + backend.clone(), + executor.clone(), + ) + .unwrap(); + let client = Arc::new( + new_in_mem::<_, Block, _, RuntimeApi>( + backend.clone(), + executor, + genesis_block_builder, + None, + None, + None, + Box::new(TaskExecutor::new()), + client_config, + ) + .unwrap(), + ); + (backend, client) + } + + #[test] + fn block_state_machine_register_unpin() { + let mut state = BlockStateMachine::new(); + // Starts in `Registered` state. + assert_eq!(state, BlockStateMachine::Registered); + + state.advance_register(); + assert_eq!(state, BlockStateMachine::FullyRegistered); + + // Can call register multiple times. + state.advance_register(); + assert_eq!(state, BlockStateMachine::FullyRegistered); + + assert!(!state.was_unpinned()); + state.advance_unpin(); + assert_eq!(state, BlockStateMachine::FullyUnpinned); + assert!(state.was_unpinned()); + + // Can call unpin multiple times. + state.advance_unpin(); + assert_eq!(state, BlockStateMachine::FullyUnpinned); + assert!(state.was_unpinned()); + + // Nothing to advance. + state.advance_register(); + assert_eq!(state, BlockStateMachine::FullyUnpinned); + } + + #[test] + fn block_state_machine_unpin_register() { + let mut state = BlockStateMachine::new(); + // Starts in `Registered` state. + assert_eq!(state, BlockStateMachine::Registered); + + assert!(!state.was_unpinned()); + state.advance_unpin(); + assert_eq!(state, BlockStateMachine::Unpinned); + assert!(state.was_unpinned()); + + // Can call unpin multiple times. + state.advance_unpin(); + assert_eq!(state, BlockStateMachine::Unpinned); + assert!(state.was_unpinned()); + + state.advance_register(); + assert_eq!(state, BlockStateMachine::FullyUnpinned); + assert!(state.was_unpinned()); + + // Nothing to advance. + state.advance_register(); + assert_eq!(state, BlockStateMachine::FullyUnpinned); + // Nothing to unpin. + state.advance_unpin(); + assert_eq!(state, BlockStateMachine::FullyUnpinned); + assert!(state.was_unpinned()); + } + + #[test] + fn sub_state_register_twice() { + let mut sub_state = SubscriptionState:: { + runtime_updates: false, + tx_stop: None, + blocks: Default::default(), + }; + + let hash = H256::random(); + assert_eq!(sub_state.register_block(hash), true); + let block_state = sub_state.blocks.get(&hash).unwrap(); + // Did not call `register_block` twice. + assert_eq!(block_state.state_machine, BlockStateMachine::Registered); + + assert_eq!(sub_state.register_block(hash), false); + let block_state = sub_state.blocks.get(&hash).unwrap(); + assert_eq!(block_state.state_machine, BlockStateMachine::FullyRegistered); + + // Block is no longer tracked when: `register_block` is called twice and + // `unregister_block` is called once. + assert_eq!(sub_state.unregister_block(hash), true); + let block_state = sub_state.blocks.get(&hash); + assert!(block_state.is_none()); + } + + #[test] + fn sub_state_register_unregister() { + let mut sub_state = SubscriptionState:: { + runtime_updates: false, + tx_stop: None, + blocks: Default::default(), + }; + + let hash = H256::random(); + // Block was not registered before. + assert_eq!(sub_state.unregister_block(hash), false); + + assert_eq!(sub_state.register_block(hash), true); + let block_state = sub_state.blocks.get(&hash).unwrap(); + // Did not call `register_block` twice. + assert_eq!(block_state.state_machine, BlockStateMachine::Registered); + + // Unregister block before the second `register_block`. + assert_eq!(sub_state.unregister_block(hash), true); + let block_state = sub_state.blocks.get(&hash).unwrap(); + assert_eq!(block_state.state_machine, BlockStateMachine::Unpinned); + + assert_eq!(sub_state.register_block(hash), false); + let block_state = sub_state.blocks.get(&hash); + assert!(block_state.is_none()); + + // Block is no longer tracked when: `register_block` is called twice and + // `unregister_block` is called once. + assert_eq!(sub_state.unregister_block(hash), false); + let block_state = sub_state.blocks.get(&hash); + assert!(block_state.is_none()); + } + + #[test] + fn subscription_lock_block() { + let builder = TestClientBuilder::new(); + let backend = builder.backend(); + let mut subs = SubscriptionsInner::new(10, Duration::from_secs(10), backend); + + let id = "abc".to_string(); + let hash = H256::random(); + + // Subscription not inserted. + let err = subs.lock_block(&id, hash).unwrap_err(); + assert_eq!(err, SubscriptionManagementError::SubscriptionAbsent); + + let _stop = subs.insert_subscription(id.clone(), true).unwrap(); + // Cannot insert the same subscription ID twice. + assert!(subs.insert_subscription(id.clone(), true).is_none()); + + // No block hash. + let err = subs.lock_block(&id, hash).unwrap_err(); + assert_eq!(err, SubscriptionManagementError::BlockHashAbsent); + + subs.remove_subscription(&id); + + // No subscription. + let err = subs.lock_block(&id, hash).unwrap_err(); + assert_eq!(err, SubscriptionManagementError::SubscriptionAbsent); + } + + #[test] + fn subscription_check_block() { + let (backend, mut client) = init_backend(); + + let block = client.new_block(Default::default()).unwrap().build().unwrap().block; + let hash = block.header.hash(); + futures::executor::block_on(client.import(BlockOrigin::Own, block.clone())).unwrap(); + + let mut subs = SubscriptionsInner::new(10, Duration::from_secs(10), backend); + let id = "abc".to_string(); + + let _stop = subs.insert_subscription(id.clone(), true).unwrap(); + + // First time we are pinning the block. + assert_eq!(subs.pin_block(&id, hash).unwrap(), true); + + let block = subs.lock_block(&id, hash).unwrap(); + // Subscription started with runtime updates + assert_eq!(block.has_runtime_updates(), true); + + let invalid_id = "abc-invalid".to_string(); + let err = subs.unpin_block(&invalid_id, hash).unwrap_err(); + assert_eq!(err, SubscriptionManagementError::SubscriptionAbsent); + + // Unpin the block. + subs.unpin_block(&id, hash).unwrap(); + let err = subs.lock_block(&id, hash).unwrap_err(); + assert_eq!(err, SubscriptionManagementError::BlockHashAbsent); + } + + #[test] + fn subscription_ref_count() { + let (backend, mut client) = init_backend(); + let block = client.new_block(Default::default()).unwrap().build().unwrap().block; + let hash = block.header.hash(); + futures::executor::block_on(client.import(BlockOrigin::Own, block.clone())).unwrap(); + + let mut subs = SubscriptionsInner::new(10, Duration::from_secs(10), backend); + let id = "abc".to_string(); + + let _stop = subs.insert_subscription(id.clone(), true).unwrap(); + assert_eq!(subs.pin_block(&id, hash).unwrap(), true); + // Check the global ref count. + assert_eq!(*subs.global_blocks.get(&hash).unwrap(), 1); + // Ensure the block propagated to the subscription. + subs.subs.get(&id).unwrap().blocks.get(&hash).unwrap(); + + // Insert the block for the same subscription again (simulate NewBlock + Finalized pinning) + assert_eq!(subs.pin_block(&id, hash).unwrap(), false); + // Check the global ref count should not get incremented. + assert_eq!(*subs.global_blocks.get(&hash).unwrap(), 1); + + // Ensure the hash propagates for the second subscription. + let id_second = "abcd".to_string(); + let _stop = subs.insert_subscription(id_second.clone(), true).unwrap(); + assert_eq!(subs.pin_block(&id_second, hash).unwrap(), true); + // Check the global ref count. + assert_eq!(*subs.global_blocks.get(&hash).unwrap(), 2); + // Ensure the block propagated to the subscription. + subs.subs.get(&id_second).unwrap().blocks.get(&hash).unwrap(); + + subs.unpin_block(&id, hash).unwrap(); + assert_eq!(*subs.global_blocks.get(&hash).unwrap(), 1); + // Cannot unpin a block twice for the same subscription. + let err = subs.unpin_block(&id, hash).unwrap_err(); + assert_eq!(err, SubscriptionManagementError::BlockHashAbsent); + + subs.unpin_block(&id_second, hash).unwrap(); + // Block unregistered from the memory. + assert!(subs.global_blocks.get(&hash).is_none()); + } + + #[test] + fn subscription_remove_subscription() { + let (backend, mut client) = init_backend(); + let block = client.new_block(Default::default()).unwrap().build().unwrap().block; + let hash_1 = block.header.hash(); + futures::executor::block_on(client.import(BlockOrigin::Own, block.clone())).unwrap(); + let block = client.new_block(Default::default()).unwrap().build().unwrap().block; + let hash_2 = block.header.hash(); + futures::executor::block_on(client.import(BlockOrigin::Own, block.clone())).unwrap(); + let block = client.new_block(Default::default()).unwrap().build().unwrap().block; + let hash_3 = block.header.hash(); + futures::executor::block_on(client.import(BlockOrigin::Own, block.clone())).unwrap(); + + let mut subs = SubscriptionsInner::new(10, Duration::from_secs(10), backend); + let id_1 = "abc".to_string(); + let id_2 = "abcd".to_string(); + + // Pin all blocks for the first subscription. + let _stop = subs.insert_subscription(id_1.clone(), true).unwrap(); + assert_eq!(subs.pin_block(&id_1, hash_1).unwrap(), true); + assert_eq!(subs.pin_block(&id_1, hash_2).unwrap(), true); + assert_eq!(subs.pin_block(&id_1, hash_3).unwrap(), true); + + // Pin only block 2 for the second subscription. + let _stop = subs.insert_subscription(id_2.clone(), true).unwrap(); + assert_eq!(subs.pin_block(&id_2, hash_2).unwrap(), true); + + // Check reference count. + assert_eq!(*subs.global_blocks.get(&hash_1).unwrap(), 1); + assert_eq!(*subs.global_blocks.get(&hash_2).unwrap(), 2); + assert_eq!(*subs.global_blocks.get(&hash_3).unwrap(), 1); + + subs.remove_subscription(&id_1); + + assert!(subs.global_blocks.get(&hash_1).is_none()); + assert_eq!(*subs.global_blocks.get(&hash_2).unwrap(), 1); + assert!(subs.global_blocks.get(&hash_3).is_none()); + + subs.remove_subscription(&id_2); + + assert!(subs.global_blocks.get(&hash_2).is_none()); + assert_eq!(subs.global_blocks.len(), 0); + } + + #[test] + fn subscription_check_limits() { + let (backend, mut client) = init_backend(); + let block = client.new_block(Default::default()).unwrap().build().unwrap().block; + let hash_1 = block.header.hash(); + futures::executor::block_on(client.import(BlockOrigin::Own, block.clone())).unwrap(); + let block = client.new_block(Default::default()).unwrap().build().unwrap().block; + let hash_2 = block.header.hash(); + futures::executor::block_on(client.import(BlockOrigin::Own, block.clone())).unwrap(); + let block = client.new_block(Default::default()).unwrap().build().unwrap().block; + let hash_3 = block.header.hash(); + futures::executor::block_on(client.import(BlockOrigin::Own, block.clone())).unwrap(); + + // Maximum number of pinned blocks is 2. + let mut subs = SubscriptionsInner::new(2, Duration::from_secs(10), backend); + let id_1 = "abc".to_string(); + let id_2 = "abcd".to_string(); + + // Both subscriptions can pin the maximum limit. + let _stop = subs.insert_subscription(id_1.clone(), true).unwrap(); + assert_eq!(subs.pin_block(&id_1, hash_1).unwrap(), true); + assert_eq!(subs.pin_block(&id_1, hash_2).unwrap(), true); + + let _stop = subs.insert_subscription(id_2.clone(), true).unwrap(); + assert_eq!(subs.pin_block(&id_2, hash_1).unwrap(), true); + assert_eq!(subs.pin_block(&id_2, hash_2).unwrap(), true); + + // Check reference count. + assert_eq!(*subs.global_blocks.get(&hash_1).unwrap(), 2); + assert_eq!(*subs.global_blocks.get(&hash_2).unwrap(), 2); + + // Block 3 pinning will exceed the limit and both subscriptions + // are terminated because no subscription with older blocks than 10 + // seconds are present. + let err = subs.pin_block(&id_1, hash_3).unwrap_err(); + assert_eq!(err, SubscriptionManagementError::ExceededLimits); + + // Ensure both subscriptions are removed. + let err = subs.lock_block(&id_1, hash_1).unwrap_err(); + assert_eq!(err, SubscriptionManagementError::SubscriptionAbsent); + + let err = subs.lock_block(&id_2, hash_1).unwrap_err(); + assert_eq!(err, SubscriptionManagementError::SubscriptionAbsent); + + assert!(subs.global_blocks.get(&hash_1).is_none()); + assert!(subs.global_blocks.get(&hash_2).is_none()); + assert!(subs.global_blocks.get(&hash_3).is_none()); + assert_eq!(subs.global_blocks.len(), 0); + } + + #[test] + fn subscription_check_limits_with_duration() { + let (backend, mut client) = init_backend(); + let block = client.new_block(Default::default()).unwrap().build().unwrap().block; + let hash_1 = block.header.hash(); + futures::executor::block_on(client.import(BlockOrigin::Own, block.clone())).unwrap(); + let block = client.new_block(Default::default()).unwrap().build().unwrap().block; + let hash_2 = block.header.hash(); + futures::executor::block_on(client.import(BlockOrigin::Own, block.clone())).unwrap(); + let block = client.new_block(Default::default()).unwrap().build().unwrap().block; + let hash_3 = block.header.hash(); + futures::executor::block_on(client.import(BlockOrigin::Own, block.clone())).unwrap(); + + // Maximum number of pinned blocks is 2 and maximum pin duration is 5 second. + let mut subs = SubscriptionsInner::new(2, Duration::from_secs(5), backend); + let id_1 = "abc".to_string(); + let id_2 = "abcd".to_string(); + + let _stop = subs.insert_subscription(id_1.clone(), true).unwrap(); + assert_eq!(subs.pin_block(&id_1, hash_1).unwrap(), true); + assert_eq!(subs.pin_block(&id_1, hash_2).unwrap(), true); + + // Maximum pin duration is 5 second, sleep 5 seconds to ensure we clean up + // the first subscription. + std::thread::sleep(std::time::Duration::from_secs(5)); + + let _stop = subs.insert_subscription(id_2.clone(), true).unwrap(); + assert_eq!(subs.pin_block(&id_2, hash_1).unwrap(), true); + + // Check reference count. + assert_eq!(*subs.global_blocks.get(&hash_1).unwrap(), 2); + assert_eq!(*subs.global_blocks.get(&hash_2).unwrap(), 1); + + // Second subscription has only 1 block pinned. Only the first subscription is terminated. + let err = subs.pin_block(&id_1, hash_3).unwrap_err(); + assert_eq!(err, SubscriptionManagementError::ExceededLimits); + + // Ensure both subscriptions are removed. + let err = subs.lock_block(&id_1, hash_1).unwrap_err(); + assert_eq!(err, SubscriptionManagementError::SubscriptionAbsent); + + let _block_guard = subs.lock_block(&id_2, hash_1).unwrap(); + + assert_eq!(*subs.global_blocks.get(&hash_1).unwrap(), 1); + assert!(subs.global_blocks.get(&hash_2).is_none()); + assert!(subs.global_blocks.get(&hash_3).is_none()); + assert_eq!(subs.global_blocks.len(), 1); + + // Force second subscription to get terminated. + assert_eq!(subs.pin_block(&id_2, hash_2).unwrap(), true); + let err = subs.pin_block(&id_2, hash_3).unwrap_err(); + assert_eq!(err, SubscriptionManagementError::ExceededLimits); + + assert!(subs.global_blocks.get(&hash_1).is_none()); + assert!(subs.global_blocks.get(&hash_2).is_none()); + assert!(subs.global_blocks.get(&hash_3).is_none()); + assert_eq!(subs.global_blocks.len(), 0); + } + + #[test] + fn subscription_check_stop_event() { + let builder = TestClientBuilder::new(); + let backend = builder.backend(); + let mut subs = SubscriptionsInner::new(10, Duration::from_secs(10), backend); + + let id = "abc".to_string(); + + let mut rx_stop = subs.insert_subscription(id.clone(), true).unwrap(); + + // Check the stop signal was not received. + let res = rx_stop.try_recv().unwrap(); + assert!(res.is_none()); + + let sub = subs.subs.get_mut(&id).unwrap(); + sub.stop(); + + // Check the signal was received. + let res = rx_stop.try_recv().unwrap(); + assert!(res.is_some()); + } +} diff --git a/client/rpc-spec-v2/src/chain_head/subscription/mod.rs b/client/rpc-spec-v2/src/chain_head/subscription/mod.rs new file mode 100644 index 000000000..86e55acc4 --- /dev/null +++ b/client/rpc-spec-v2/src/chain_head/subscription/mod.rs @@ -0,0 +1,125 @@ +// This file is part of Substrate. + +// Copyright (C) Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +use futures::channel::oneshot; +use parking_lot::RwLock; +use sc_client_api::Backend; +use sp_runtime::traits::Block as BlockT; +use std::{sync::Arc, time::Duration}; + +mod error; +mod inner; + +pub use error::SubscriptionManagementError; +pub use inner::BlockGuard; +use inner::SubscriptionsInner; + +/// Manage block pinning / unpinning for subscription IDs. +pub struct SubscriptionManagement> { + /// Manage subscription by mapping the subscription ID + /// to a set of block hashes. + inner: RwLock>, +} + +impl> SubscriptionManagement { + /// Construct a new [`SubscriptionManagement`]. + pub fn new( + global_max_pinned_blocks: usize, + local_max_pin_duration: Duration, + backend: Arc, + ) -> Self { + SubscriptionManagement { + inner: RwLock::new(SubscriptionsInner::new( + global_max_pinned_blocks, + local_max_pin_duration, + backend, + )), + } + } + + /// Insert a new subscription ID. + /// + /// If the subscription was not previously inserted, returns the receiver that is + /// triggered upon the "Stop" event. Otherwise, if the subscription ID was already + /// inserted returns none. + pub fn insert_subscription( + &self, + sub_id: String, + runtime_updates: bool, + ) -> Option> { + let mut inner = self.inner.write(); + inner.insert_subscription(sub_id, runtime_updates) + } + + /// Remove the subscription ID with associated pinned blocks. + pub fn remove_subscription(&self, sub_id: &str) { + let mut inner = self.inner.write(); + inner.remove_subscription(sub_id) + } + + /// The block is pinned in the backend only once when the block's hash is first encountered. + /// + /// Each subscription is expected to call this method twice: + /// - once from the `NewBlock` import + /// - once from the `Finalized` import + /// + /// Returns + /// - Ok(true) if the subscription did not previously contain this block + /// - Ok(false) if the subscription already contained this this + /// - Error if the backend failed to pin the block or the subscription ID is invalid + pub fn pin_block( + &self, + sub_id: &str, + hash: Block::Hash, + ) -> Result { + let mut inner = self.inner.write(); + inner.pin_block(sub_id, hash) + } + + /// Unpin the block from the subscription. + /// + /// The last subscription that unpins the block is also unpinning the block + /// from the backend. + /// + /// This method is called only once per subscription. + /// + /// Returns an error if the block is not pinned for the subscription or + /// the subscription ID is invalid. + pub fn unpin_block( + &self, + sub_id: &str, + hash: Block::Hash, + ) -> Result<(), SubscriptionManagementError> { + let mut inner = self.inner.write(); + inner.unpin_block(sub_id, hash) + } + + /// Ensure the block remains pinned until the return object is dropped. + /// + /// Returns a [`BlockGuard`] that pins and unpins the block hash in RAII manner. + /// Returns an error if the block hash is not pinned for the subscription or + /// the subscription ID is invalid. + pub fn lock_block( + &self, + sub_id: &str, + hash: Block::Hash, + ) -> Result, SubscriptionManagementError> { + let mut inner = self.inner.write(); + inner.lock_block(sub_id, hash) + } +} diff --git a/client/rpc-spec-v2/src/chain_head/tests.rs b/client/rpc-spec-v2/src/chain_head/tests.rs index 1d5cb8da2..76644ccb4 100644 --- a/client/rpc-spec-v2/src/chain_head/tests.rs +++ b/client/rpc-spec-v2/src/chain_head/tests.rs @@ -11,6 +11,8 @@ use jsonrpsee::{ }; use sc_block_builder::BlockBuilderProvider; use sc_client_api::ChildInfo; +use sc_service::client::new_in_mem; +use sp_api::BlockT; use sp_blockchain::HeaderBackend; use sp_consensus::BlockOrigin; use sp_core::{ @@ -19,15 +21,17 @@ use sp_core::{ testing::TaskExecutor, }; use sp_version::RuntimeVersion; -use std::sync::Arc; +use std::{sync::Arc, time::Duration}; use substrate_test_runtime::Transfer; use substrate_test_runtime_client::{ - prelude::*, runtime, Backend, BlockBuilderExt, Client, ClientBlockImportExt, + prelude::*, runtime, runtime::RuntimeApi, Backend, BlockBuilderExt, Client, + ClientBlockImportExt, GenesisInit, }; type Header = substrate_test_runtime_client::runtime::Header; type Block = substrate_test_runtime_client::runtime::Block; const MAX_PINNED_BLOCKS: usize = 32; +const MAX_PINNED_SECS: u64 = 60; const CHAIN_GENESIS: [u8; 32] = [0; 32]; const INVALID_HASH: [u8; 32] = [1; 32]; const KEY: &[u8] = b":mock"; @@ -72,6 +76,7 @@ async fn setup_api() -> ( Arc::new(TaskExecutor::default()), CHAIN_GENESIS, MAX_PINNED_BLOCKS, + Duration::from_secs(MAX_PINNED_SECS), ) .into_rpc(); @@ -111,6 +116,7 @@ async fn follow_subscription_produces_blocks() { Arc::new(TaskExecutor::default()), CHAIN_GENESIS, MAX_PINNED_BLOCKS, + Duration::from_secs(MAX_PINNED_SECS), ) .into_rpc(); @@ -168,6 +174,7 @@ async fn follow_with_runtime() { Arc::new(TaskExecutor::default()), CHAIN_GENESIS, MAX_PINNED_BLOCKS, + Duration::from_secs(MAX_PINNED_SECS), ) .into_rpc(); @@ -273,6 +280,7 @@ async fn get_genesis() { Arc::new(TaskExecutor::default()), CHAIN_GENESIS, MAX_PINNED_BLOCKS, + Duration::from_secs(MAX_PINNED_SECS), ) .into_rpc(); @@ -457,6 +465,7 @@ async fn call_runtime_without_flag() { Arc::new(TaskExecutor::default()), CHAIN_GENESIS, MAX_PINNED_BLOCKS, + Duration::from_secs(MAX_PINNED_SECS), ) .into_rpc(); @@ -631,6 +640,7 @@ async fn follow_generates_initial_blocks() { Arc::new(TaskExecutor::default()), CHAIN_GENESIS, MAX_PINNED_BLOCKS, + Duration::from_secs(MAX_PINNED_SECS), ) .into_rpc(); @@ -758,6 +768,7 @@ async fn follow_exceeding_pinned_blocks() { Arc::new(TaskExecutor::default()), CHAIN_GENESIS, 2, + Duration::from_secs(MAX_PINNED_SECS), ) .into_rpc(); @@ -808,6 +819,7 @@ async fn follow_with_unpin() { Arc::new(TaskExecutor::default()), CHAIN_GENESIS, 2, + Duration::from_secs(MAX_PINNED_SECS), ) .into_rpc(); @@ -888,6 +900,7 @@ async fn follow_prune_best_block() { Arc::new(TaskExecutor::default()), CHAIN_GENESIS, MAX_PINNED_BLOCKS, + Duration::from_secs(MAX_PINNED_SECS), ) .into_rpc(); @@ -1044,6 +1057,7 @@ async fn follow_forks_pruned_block() { Arc::new(TaskExecutor::default()), CHAIN_GENESIS, MAX_PINNED_BLOCKS, + Duration::from_secs(MAX_PINNED_SECS), ) .into_rpc(); @@ -1157,6 +1171,7 @@ async fn follow_report_multiple_pruned_block() { Arc::new(TaskExecutor::default()), CHAIN_GENESIS, MAX_PINNED_BLOCKS, + Duration::from_secs(MAX_PINNED_SECS), ) .into_rpc(); @@ -1327,6 +1342,137 @@ async fn follow_report_multiple_pruned_block() { assert_eq!(event, expected); } +#[tokio::test] +async fn pin_block_references() { + // Manually construct an in-memory backend and client. + let backend = Arc::new(sc_client_api::in_mem::Backend::new()); + let executor = substrate_test_runtime_client::new_native_or_wasm_executor(); + let client_config = sc_service::ClientConfig::default(); + + let genesis_block_builder = sc_service::GenesisBlockBuilder::new( + &substrate_test_runtime_client::GenesisParameters::default().genesis_storage(), + !client_config.no_genesis, + backend.clone(), + executor.clone(), + ) + .unwrap(); + + let mut client = Arc::new( + new_in_mem::<_, Block, _, RuntimeApi>( + backend.clone(), + executor, + genesis_block_builder, + None, + None, + None, + Box::new(TaskExecutor::new()), + client_config, + ) + .unwrap(), + ); + + let api = ChainHead::new( + client.clone(), + backend.clone(), + Arc::new(TaskExecutor::default()), + CHAIN_GENESIS, + 3, + Duration::from_secs(MAX_PINNED_SECS), + ) + .into_rpc(); + + async fn wait_pinned_references( + backend: &Arc>, + hash: &Block::Hash, + target: i64, + ) { + // Retry for at most 2 minutes. + let mut retries = 120; + while backend.pin_refs(hash).unwrap() != target { + if retries == 0 { + panic!("Expected target={} pinned references for hash={:?}", target, hash); + } + retries -= 1; + + tokio::time::sleep(tokio::time::Duration::from_secs(1)).await; + } + } + + let mut sub = api.subscribe("chainHead_unstable_follow", [false]).await.unwrap(); + let sub_id = sub.subscription_id(); + let sub_id = serde_json::to_string(&sub_id).unwrap(); + + let block = client.new_block(Default::default()).unwrap().build().unwrap().block; + let hash = block.header.hash(); + let block_hash = format!("{:?}", hash); + client.import(BlockOrigin::Own, block.clone()).await.unwrap(); + + // Ensure the imported block is propagated for this subscription. + assert_matches!( + get_next_event::>(&mut sub).await, + FollowEvent::Initialized(_) + ); + assert_matches!( + get_next_event::>(&mut sub).await, + FollowEvent::NewBlock(_) + ); + assert_matches!( + get_next_event::>(&mut sub).await, + FollowEvent::BestBlockChanged(_) + ); + + // We need to wait a bit for: + // 1. `NewBlock` and `BestBlockChanged` notifications to propagate to the chainHead + // subscription. (pin_refs == 2) + // 2. The chainHead to call `pin_blocks` only once for the `NewBlock` + // notification (pin_refs == 3) + // 3. Both notifications to go out of scope (pin_refs == 1 (total 3 - dropped 2)). + wait_pinned_references(&backend, &hash, 1).await; + + // To not exceed the number of pinned blocks, we need to unpin before the next import. + let _res: () = api.call("chainHead_unstable_unpin", [&sub_id, &block_hash]).await.unwrap(); + + // Make sure unpin clears out the reference. + let refs = backend.pin_refs(&hash).unwrap(); + assert_eq!(refs, 0); + + // Add another 2 blocks and make sure we drop the subscription with the blocks pinned. + let mut hashes = Vec::new(); + for _ in 0..2 { + let block = client.new_block(Default::default()).unwrap().build().unwrap().block; + let hash = block.header.hash(); + client.import(BlockOrigin::Own, block.clone()).await.unwrap(); + + // Ensure the imported block is propagated for this subscription. + assert_matches!( + get_next_event::>(&mut sub).await, + FollowEvent::NewBlock(_) + ); + assert_matches!( + get_next_event::>(&mut sub).await, + FollowEvent::BestBlockChanged(_) + ); + + hashes.push(hash); + } + + // Make sure the pin was propagated. + for hash in &hashes { + wait_pinned_references(&backend, hash, 1).await; + } + + // Drop the subscription and expect the pinned blocks to be released. + drop(sub); + // The `chainHead` detects the subscription was terminated when it tries + // to send another block. + let block = client.new_block(Default::default()).unwrap().build().unwrap().block; + client.import(BlockOrigin::Own, block.clone()).await.unwrap(); + + for hash in &hashes { + wait_pinned_references(&backend, &hash, 0).await; + } +} + #[tokio::test] async fn follow_finalized_before_new_block() { let builder = TestClientBuilder::new(); @@ -1341,6 +1487,7 @@ async fn follow_finalized_before_new_block() { Arc::new(TaskExecutor::default()), CHAIN_GENESIS, MAX_PINNED_BLOCKS, + Duration::from_secs(MAX_PINNED_SECS), ) .into_rpc(); diff --git a/client/service/src/builder.rs b/client/service/src/builder.rs index 2208c55cf..d338ceb78 100644 --- a/client/service/src/builder.rs +++ b/client/service/src/builder.rs @@ -70,7 +70,11 @@ use sp_consensus::block_validation::{ use sp_core::traits::{CodeExecutor, SpawnNamed}; use sp_keystore::KeystorePtr; use sp_runtime::traits::{Block as BlockT, BlockIdTo, NumberFor, Zero}; -use std::{str::FromStr, sync::Arc, time::SystemTime}; +use std::{ + str::FromStr, + sync::Arc, + time::{Duration, SystemTime}, +}; /// Full client type. pub type TFullClient = @@ -667,16 +671,24 @@ where ) .into_rpc(); - // Maximum pinned blocks per connection. - // This number is large enough to consider immediate blocks, - // but it will change to facilitate adequate limits for the pinning API. - const MAX_PINNED_BLOCKS: usize = 4096; + // Maximum pinned blocks across all connections. + // This number is large enough to consider immediate blocks. + // Note: This should never exceed the `PINNING_CACHE_SIZE` from client/db. + const MAX_PINNED_BLOCKS: usize = 512; + + // Any block of any subscription should not be pinned more than + // this constant. When a subscription contains a block older than this, + // the subscription becomes subject to termination. + // Note: This should be enough for immediate blocks. + const MAX_PINNED_SECONDS: u64 = 60; + let chain_head_v2 = sc_rpc_spec_v2::chain_head::ChainHead::new( client.clone(), backend.clone(), task_executor.clone(), client.info().genesis_hash, MAX_PINNED_BLOCKS, + Duration::from_secs(MAX_PINNED_SECONDS), ) .into_rpc(); From f49b178839925639ec92fbd8fbd81337d6fb6e3e Mon Sep 17 00:00:00 2001 From: juangirini Date: Tue, 2 May 2023 18:45:14 +0200 Subject: [PATCH 444/558] contracts: Make Origin information available (#13708) * contracts: allow root calls * contracts: update ContractOrigin * contracts: test allow root calls * contracts: rustfmt * contracts: test root caller traps * contracts: add Caller enum * contracts: improve some comments * contracts: improve some comments * contracts: format code with +nightly * contracts: fix failing tests * contracts: improve charte instantiate call when root origin * contract: refactor common, call and instantiate inputs * contracts: fix some failing test * contracts: trap caller when there is no account id * Update frame/contracts/src/lib.rs Co-authored-by: PG Herveou * Update frame/contracts/src/exec.rs Co-authored-by: PG Herveou * contracts: make `into_deposit` return zero when the origin is root * contracts: cargo fmt * contracts: charging and terminating tests refactored * contracts: improved errors * contracts: refactor & merge ContractOrigin cand Caller enums * Update frame/contracts/src/lib.rs Co-authored-by: Sasha Gryaznov * contracts: apply suggested pr changes * contracts: rename Caller to Origin * contracts: refactor run fn * contracts: cr improvements * contracts: allow root to use delegate call * contracts: add caller_is_root weight * ".git/.scripts/commands/bench/bench.sh" pallet dev pallet_contracts * contracts: add caller_is_root benchmarking * ".git/.scripts/commands/bench/bench.sh" pallet dev pallet_contracts * contracts: add caller_is_root benchmarking * ".git/.scripts/commands/bench/bench.sh" pallet dev pallet_contracts * contracts: add some minor improvements * contracts: make caller_is_root runtime fn unstable * contracts: fix failing wasm test * contracts: update benchmarking for origin_is_root * contracts: improve seal_caller_is_root benchmarking * ".git/.scripts/commands/bench/bench.sh" pallet dev pallet_contracts * contracts: add small pr improvements * contracts: fix broken tests after master merge * contracts: acknowledge the default storage deposit limit * ".git/.scripts/commands/bench/bench.sh" pallet dev pallet_contracts * contracts: move origin to CommonInput * contracts: add some extra tests * contracts: move ensure origin logic inside invokable::run_guarded * contracts: add minor improvements * contracts: fix caller_is_root benchmarking * contracts: improve function description * Update frame/contracts/src/lib.rs Co-authored-by: Sasha Gryaznov * Update frame/contracts/src/lib.rs Co-authored-by: Sasha Gryaznov * Update frame/contracts/src/wasm/runtime.rs Co-authored-by: Sasha Gryaznov * contracts: improve function description --------- Co-authored-by: parity-processbot <> Co-authored-by: PG Herveou Co-authored-by: Sasha Gryaznov --- frame/contracts/src/benchmarking/mod.rs | 22 + frame/contracts/src/exec.rs | 423 +++-- frame/contracts/src/lib.rs | 111 +- frame/contracts/src/schedule.rs | 4 + frame/contracts/src/storage/meter.rs | 279 ++-- frame/contracts/src/tests.rs | 169 +- frame/contracts/src/wasm/mod.rs | 60 +- frame/contracts/src/wasm/runtime.rs | 24 +- frame/contracts/src/weights.rs | 1985 ++++++++++++----------- primitives/runtime/src/lib.rs | 4 + 10 files changed, 1841 insertions(+), 1240 deletions(-) diff --git a/frame/contracts/src/benchmarking/mod.rs b/frame/contracts/src/benchmarking/mod.rs index 6ebfb1850..2c60c1501 100644 --- a/frame/contracts/src/benchmarking/mod.rs +++ b/frame/contracts/src/benchmarking/mod.rs @@ -534,6 +534,28 @@ benchmarks! { let origin = RawOrigin::Signed(instance.caller.clone()); }: call(origin, instance.addr, 0u32.into(), Weight::MAX, None, vec![]) + #[pov_mode = Measured] + seal_caller_is_root { + let r in 0 .. API_BENCHMARK_RUNS; + + let code = WasmModule::::from(ModuleDefinition { + memory: Some(ImportedMemory::max::()), + imported_functions: vec![ImportedFunction { + module: "seal0", + name: "caller_is_root", + params: vec![], + return_type: Some(ValueType::I32), + }], + call_body: Some(body::repeated(r, &[ + Instruction::Call(0), + Instruction::Drop, + ])), + .. Default::default() + }); + let instance = Contract::::new(code, vec![])?; + let origin = RawOrigin::Root; + }: call(origin, instance.addr, 0u32.into(), Weight::MAX, None, vec![]) + #[pov_mode = Measured] seal_address { let r in 0 .. API_BENCHMARK_RUNS; diff --git a/frame/contracts/src/exec.rs b/frame/contracts/src/exec.rs index d0a650e0a..a81a633f7 100644 --- a/frame/contracts/src/exec.rs +++ b/frame/contracts/src/exec.rs @@ -19,7 +19,7 @@ use crate::{ gas::GasMeter, storage::{self, DepositAccount, WriteOutcome}, BalanceOf, CodeHash, Config, ContractInfo, ContractInfoOf, DebugBufferVec, Determinism, Error, - Event, Nonce, Pallet as Contracts, Schedule, System, LOG_TARGET, + Event, Nonce, Origin, Pallet as Contracts, Schedule, System, LOG_TARGET, }; use frame_support::{ crypto::ecdsa::ECDSAExt, @@ -203,8 +203,8 @@ pub trait Ext: sealing::Sealed { take_old: bool, ) -> Result; - /// Returns a reference to the account id of the caller. - fn caller(&self) -> &AccountIdOf; + /// Returns the caller. + fn caller(&self) -> Origin; /// Check if a contract lives at the specified `address`. fn is_contract(&self, address: &AccountIdOf) -> bool; @@ -223,6 +223,9 @@ pub trait Ext: sealing::Sealed { /// However, this function does not require any storage lookup and therefore uses less weight. fn caller_is_origin(&self) -> bool; + /// Check if the caller is origin, and this origin is root. + fn caller_is_root(&self) -> bool; + /// Returns a reference to the account id of the current contract. fn address(&self) -> &AccountIdOf; @@ -373,14 +376,15 @@ pub trait Executable: Sized { /// This type implements `Ext` and by that exposes the business logic of contract execution to /// the runtime module which interfaces with the contract (the wasm blob) itself. pub struct Stack<'a, T: Config, E> { - /// The account id of a plain account that initiated the call stack. + /// The origin that initiated the call stack. It could either be a Signed plain account that + /// holds an account id or Root. /// /// # Note /// - /// Please note that it is possible that the id belongs to a contract rather than a plain - /// account when being called through one of the contract RPCs where the client can freely - /// choose the origin. This usually makes no sense but is still possible. - origin: T::AccountId, + /// Please note that it is possible that the id of a Signed origin belongs to a contract rather + /// than a plain account when being called through one of the contract RPCs where the + /// client can freely choose the origin. This usually makes no sense but is still possible. + origin: Origin, /// The cost schedule used when charging from the gas meter. schedule: &'a Schedule, /// The gas meter where costs are charged to. @@ -436,15 +440,15 @@ pub struct Frame { /// If `false` the contract enabled its defense against reentrance attacks. allows_reentry: bool, /// The caller of the currently executing frame which was spawned by `delegate_call`. - delegate_caller: Option, + delegate_caller: Option>, } /// Used in a delegate call frame arguments in order to override the executable and caller. struct DelegatedCall { /// The executable which is run instead of the contracts own `executable`. executable: E, - /// The account id of the caller contract. - caller: T::AccountId, + /// The caller of the contract. + caller: Origin, } /// Parameter passed in when creating a new `Frame`. @@ -617,7 +621,7 @@ where /// /// Result<(ExecReturnValue, CodeSize), (ExecError, CodeSize)> pub fn run_call( - origin: T::AccountId, + origin: Origin, dest: T::AccountId, gas_meter: &'a mut GasMeter, storage_meter: &'a mut storage::meter::Meter, @@ -669,7 +673,7 @@ where salt, input_data: input_data.as_ref(), }, - origin, + Origin::from_account_id(origin), gas_meter, storage_meter, schedule, @@ -684,7 +688,7 @@ where /// Create a new call stack. fn new( args: FrameArgs, - origin: T::AccountId, + origin: Origin, gas_meter: &'a mut GasMeter, storage_meter: &'a mut storage::meter::Meter, schedule: &'a Schedule, @@ -846,9 +850,12 @@ where // We need to charge the storage deposit before the initial transfer so that // it can create the account in case the initial transfer is < ed. if entry_point == ExportedFunction::Constructor { + // Root origin can't be used to instantiate a contract, so it is safe to assume that + // if we reached this point the origin has an associated account. + let origin = &self.origin.account_id()?; let frame = top_frame_mut!(self); frame.nested_storage.charge_instantiate( - &self.origin, + origin, &frame.account_id, frame.contract_info.get(&frame.account_id), )?; @@ -894,13 +901,12 @@ where let contract = frame.contract_info.as_contract(); frame.nested_storage.enforce_subcall_limit(contract)?; + let caller = self.caller().account_id()?.clone(); + // Deposit an instantiation event. Contracts::::deposit_event( - vec![T::Hashing::hash_of(self.caller()), T::Hashing::hash_of(account_id)], - Event::Instantiated { - deployer: self.caller().clone(), - contract: account_id.clone(), - }, + vec![T::Hashing::hash_of(&caller), T::Hashing::hash_of(account_id)], + Event::Instantiated { deployer: caller, contract: account_id.clone() }, ); }, (ExportedFunction::Call, Some(code_hash)) => { @@ -918,7 +924,7 @@ where let caller = self.caller(); Contracts::::deposit_event( - vec![T::Hashing::hash_of(caller), T::Hashing::hash_of(&account_id)], + vec![T::Hashing::hash_of(&caller), T::Hashing::hash_of(&account_id)], Event::Called { caller: caller.clone(), contract: account_id.clone() }, ); }, @@ -1076,7 +1082,16 @@ where } let value = frame.value_transferred; - Self::transfer(ExistenceRequirement::KeepAlive, self.caller(), &frame.account_id, value) + + // Get the account id from the caller. + // If the caller is root there is no account to transfer from, and therefore we can't take + // any `value` other than 0. + let caller = match self.caller() { + Origin::Signed(caller) => caller, + Origin::Root if value.is_zero() => return Ok(()), + Origin::Root => return DispatchError::RootNotAllowed.into(), + }; + Self::transfer(ExistenceRequirement::KeepAlive, &caller, &frame.account_id, value) } /// Reference to the current (top) frame. @@ -1282,11 +1297,14 @@ where &self.top_frame().account_id } - fn caller(&self) -> &T::AccountId { + fn caller(&self) -> Origin { if let Some(caller) = &self.top_frame().delegate_caller { - caller + caller.clone() } else { - self.frames().nth(1).map(|f| &f.account_id).unwrap_or(&self.origin) + self.frames() + .nth(1) + .map(|f| Origin::from_account_id(f.account_id.clone())) + .unwrap_or(self.origin.clone()) } } @@ -1303,7 +1321,12 @@ where } fn caller_is_origin(&self) -> bool { - self.caller() == &self.origin + self.origin == self.caller() + } + + fn caller_is_root(&self) -> bool { + // if the caller isn't origin, then it can't be root. + self.caller_is_origin() && self.origin == Origin::Root } fn balance(&self) -> BalanceOf { @@ -1637,11 +1660,13 @@ mod tests { ExtBuilder::default().build().execute_with(|| { let schedule = ::Schedule::get(); place_contract(&BOB, exec_ch); - let mut storage_meter = storage::meter::Meter::new(&ALICE, Some(0), value).unwrap(); + let mut storage_meter = + storage::meter::Meter::new(&Origin::from_account_id(ALICE), Some(0), value) + .unwrap(); assert_matches!( MockStack::run_call( - ALICE, + Origin::from_account_id(ALICE), BOB, &mut gas_meter, &mut storage_meter, @@ -1692,10 +1717,12 @@ mod tests { place_contract(&dest, success_ch); set_balance(&origin, 100); let balance = get_balance(&dest); - let mut storage_meter = storage::meter::Meter::new(&origin, Some(0), value).unwrap(); + let contract_origin = Origin::from_account_id(origin.clone()); + let mut storage_meter = + storage::meter::Meter::new(&contract_origin, Some(0), value).unwrap(); let _ = MockStack::run_call( - origin.clone(), + contract_origin.clone(), dest.clone(), &mut GasMeter::::new(GAS_LIMIT), &mut storage_meter, @@ -1734,10 +1761,12 @@ mod tests { place_contract(&dest, delegate_ch); set_balance(&origin, 100); let balance = get_balance(&dest); - let mut storage_meter = storage::meter::Meter::new(&origin, Some(0), 55).unwrap(); + let contract_origin = Origin::from_account_id(origin.clone()); + let mut storage_meter = + storage::meter::Meter::new(&contract_origin, Some(0), 55).unwrap(); let _ = MockStack::run_call( - origin.clone(), + contract_origin.clone(), dest.clone(), &mut GasMeter::::new(GAS_LIMIT), &mut storage_meter, @@ -1770,10 +1799,12 @@ mod tests { place_contract(&dest, return_ch); set_balance(&origin, 100); let balance = get_balance(&dest); - let mut storage_meter = storage::meter::Meter::new(&origin, Some(0), 55).unwrap(); + let contract_origin = Origin::from_account_id(origin.clone()); + let mut storage_meter = + storage::meter::Meter::new(&contract_origin, Some(0), 55).unwrap(); let output = MockStack::run_call( - origin.clone(), + contract_origin.clone(), dest.clone(), &mut GasMeter::::new(GAS_LIMIT), &mut storage_meter, @@ -1821,11 +1852,13 @@ mod tests { ExtBuilder::default().build().execute_with(|| { let schedule = ::Schedule::get(); - let mut storage_meter = storage::meter::Meter::new(&origin, Some(0), 0).unwrap(); + let contract_origin = Origin::from_account_id(origin); + let mut storage_meter = + storage::meter::Meter::new(&contract_origin, Some(0), 0).unwrap(); place_contract(&BOB, return_ch); let result = MockStack::run_call( - origin, + contract_origin, dest, &mut GasMeter::::new(GAS_LIMIT), &mut storage_meter, @@ -1855,10 +1888,12 @@ mod tests { ExtBuilder::default().build().execute_with(|| { let schedule = ::Schedule::get(); place_contract(&BOB, return_ch); - let mut storage_meter = storage::meter::Meter::new(&origin, Some(0), 0).unwrap(); + let contract_origin = Origin::from_account_id(origin); + let mut storage_meter = + storage::meter::Meter::new(&contract_origin, Some(0), 0).unwrap(); let result = MockStack::run_call( - origin, + contract_origin, dest, &mut GasMeter::::new(GAS_LIMIT), &mut storage_meter, @@ -1886,10 +1921,12 @@ mod tests { ExtBuilder::default().build().execute_with(|| { let schedule = ::Schedule::get(); place_contract(&BOB, input_data_ch); - let mut storage_meter = storage::meter::Meter::new(&ALICE, Some(0), 0).unwrap(); + let contract_origin = Origin::from_account_id(ALICE); + let mut storage_meter = + storage::meter::Meter::new(&contract_origin, Some(0), 0).unwrap(); let result = MockStack::run_call( - ALICE, + contract_origin, BOB, &mut GasMeter::::new(GAS_LIMIT), &mut storage_meter, @@ -1918,7 +1955,9 @@ mod tests { let executable = MockExecutable::from_storage(input_data_ch, &schedule, &mut gas_meter).unwrap(); set_balance(&ALICE, min_balance * 10_000); - let mut storage_meter = storage::meter::Meter::new(&ALICE, None, min_balance).unwrap(); + let contract_origin = Origin::from_account_id(ALICE); + let mut storage_meter = + storage::meter::Meter::new(&contract_origin, None, min_balance).unwrap(); let result = MockStack::run_instantiate( ALICE, @@ -1966,10 +2005,12 @@ mod tests { let schedule = ::Schedule::get(); set_balance(&BOB, 1); place_contract(&BOB, recurse_ch); - let mut storage_meter = storage::meter::Meter::new(&ALICE, Some(0), value).unwrap(); + let contract_origin = Origin::from_account_id(ALICE); + let mut storage_meter = + storage::meter::Meter::new(&contract_origin, Some(0), value).unwrap(); let result = MockStack::run_call( - ALICE, + contract_origin, BOB, &mut GasMeter::::new(GAS_LIMIT), &mut storage_meter, @@ -1996,7 +2037,9 @@ mod tests { let bob_ch = MockLoader::insert(Call, |ctx, _| { // Record the caller for bob. - WitnessedCallerBob::mutate(|caller| *caller = Some(ctx.ext.caller().clone())); + WitnessedCallerBob::mutate(|caller| { + *caller = Some(ctx.ext.caller().account_id().unwrap().clone()) + }); // Call into CHARLIE contract. assert_matches!( @@ -2008,7 +2051,9 @@ mod tests { }); let charlie_ch = MockLoader::insert(Call, |ctx, _| { // Record the caller for charlie. - WitnessedCallerCharlie::mutate(|caller| *caller = Some(ctx.ext.caller().clone())); + WitnessedCallerCharlie::mutate(|caller| { + *caller = Some(ctx.ext.caller().account_id().unwrap().clone()) + }); exec_success() }); @@ -2016,10 +2061,12 @@ mod tests { let schedule = ::Schedule::get(); place_contract(&dest, bob_ch); place_contract(&CHARLIE, charlie_ch); - let mut storage_meter = storage::meter::Meter::new(&origin, Some(0), 0).unwrap(); + let contract_origin = Origin::from_account_id(origin.clone()); + let mut storage_meter = + storage::meter::Meter::new(&contract_origin, Some(0), 0).unwrap(); let result = MockStack::run_call( - origin.clone(), + contract_origin.clone(), dest.clone(), &mut GasMeter::::new(GAS_LIMIT), &mut storage_meter, @@ -2051,9 +2098,11 @@ mod tests { let schedule = ::Schedule::get(); place_contract(&BOB, bob_ch); - let mut storage_meter = storage::meter::Meter::new(&ALICE, Some(0), 0).unwrap(); + let contract_origin = Origin::from_account_id(ALICE); + let mut storage_meter = + storage::meter::Meter::new(&contract_origin, Some(0), 0).unwrap(); let result = MockStack::run_call( - ALICE, + contract_origin, BOB, &mut GasMeter::::new(GAS_LIMIT), &mut storage_meter, @@ -2080,10 +2129,12 @@ mod tests { ExtBuilder::default().build().execute_with(|| { let schedule = ::Schedule::get(); place_contract(&BOB, code_bob); - let mut storage_meter = storage::meter::Meter::new(&ALICE, Some(0), 0).unwrap(); + let contract_origin = Origin::from_account_id(ALICE); + let mut storage_meter = + storage::meter::Meter::new(&contract_origin, Some(0), 0).unwrap(); // ALICE (not contract) -> BOB (contract) let result = MockStack::run_call( - ALICE, + contract_origin, BOB, &mut GasMeter::::new(GAS_LIMIT), &mut storage_meter, @@ -2108,10 +2159,12 @@ mod tests { ExtBuilder::default().build().execute_with(|| { let schedule = ::Schedule::get(); place_contract(&BOB, bob_ch); - let mut storage_meter = storage::meter::Meter::new(&ALICE, Some(0), 0).unwrap(); + let contract_origin = Origin::from_account_id(ALICE); + let mut storage_meter = + storage::meter::Meter::new(&contract_origin, Some(0), 0).unwrap(); // ALICE (not contract) -> BOB (contract) let result = MockStack::run_call( - ALICE, + contract_origin, BOB, &mut GasMeter::::new(GAS_LIMIT), &mut storage_meter, @@ -2145,10 +2198,111 @@ mod tests { let schedule = ::Schedule::get(); place_contract(&BOB, code_bob); place_contract(&CHARLIE, code_charlie); - let mut storage_meter = storage::meter::Meter::new(&ALICE, Some(0), 0).unwrap(); + let contract_origin = Origin::from_account_id(ALICE); + let mut storage_meter = + storage::meter::Meter::new(&contract_origin, Some(0), 0).unwrap(); // ALICE -> BOB (caller is origin) -> CHARLIE (caller is not origin) let result = MockStack::run_call( - ALICE, + contract_origin, + BOB, + &mut GasMeter::::new(GAS_LIMIT), + &mut storage_meter, + &schedule, + 0, + vec![0], + None, + Determinism::Enforced, + ); + assert_matches!(result, Ok(_)); + }); + } + + #[test] + fn root_caller_succeeds() { + let code_bob = MockLoader::insert(Call, |ctx, _| { + // root is the origin of the call stack. + assert!(ctx.ext.caller_is_root()); + exec_success() + }); + + ExtBuilder::default().build().execute_with(|| { + let schedule = ::Schedule::get(); + place_contract(&BOB, code_bob); + let contract_origin = Origin::Root; + let mut storage_meter = + storage::meter::Meter::new(&contract_origin, Some(0), 0).unwrap(); + // root -> BOB (caller is root) + let result = MockStack::run_call( + contract_origin, + BOB, + &mut GasMeter::::new(GAS_LIMIT), + &mut storage_meter, + &schedule, + 0, + vec![0], + None, + Determinism::Enforced, + ); + assert_matches!(result, Ok(_)); + }); + } + + #[test] + fn root_caller_does_not_succeed_when_value_not_zero() { + let code_bob = MockLoader::insert(Call, |ctx, _| { + // root is the origin of the call stack. + assert!(ctx.ext.caller_is_root()); + exec_success() + }); + + ExtBuilder::default().build().execute_with(|| { + let schedule = ::Schedule::get(); + place_contract(&BOB, code_bob); + let contract_origin = Origin::Root; + let mut storage_meter = + storage::meter::Meter::new(&contract_origin, Some(0), 0).unwrap(); + // root -> BOB (caller is root) + let result = MockStack::run_call( + contract_origin, + BOB, + &mut GasMeter::::new(GAS_LIMIT), + &mut storage_meter, + &schedule, + 1, + vec![0], + None, + Determinism::Enforced, + ); + assert_matches!(result, Err(_)); + }); + } + + #[test] + fn root_caller_succeeds_with_consecutive_calls() { + let code_charlie = MockLoader::insert(Call, |ctx, _| { + // BOB is not root, even though the origin is root. + assert!(!ctx.ext.caller_is_root()); + exec_success() + }); + + let code_bob = MockLoader::insert(Call, |ctx, _| { + // root is the origin of the call stack. + assert!(ctx.ext.caller_is_root()); + // BOB calls CHARLIE. + ctx.ext + .call(Weight::zero(), BalanceOf::::zero(), CHARLIE, 0, vec![], true) + }); + + ExtBuilder::default().build().execute_with(|| { + let schedule = ::Schedule::get(); + place_contract(&BOB, code_bob); + place_contract(&CHARLIE, code_charlie); + let contract_origin = Origin::Root; + let mut storage_meter = + storage::meter::Meter::new(&contract_origin, Some(0), 0).unwrap(); + // root -> BOB (caller is root) -> CHARLIE (caller is not root) + let result = MockStack::run_call( + contract_origin, BOB, &mut GasMeter::::new(GAS_LIMIT), &mut storage_meter, @@ -2185,10 +2339,12 @@ mod tests { let schedule = ::Schedule::get(); place_contract(&BOB, bob_ch); place_contract(&CHARLIE, charlie_ch); - let mut storage_meter = storage::meter::Meter::new(&ALICE, Some(0), 0).unwrap(); + let contract_origin = Origin::from_account_id(ALICE); + let mut storage_meter = + storage::meter::Meter::new(&contract_origin, Some(0), 0).unwrap(); let result = MockStack::run_call( - ALICE, + contract_origin, BOB, &mut GasMeter::::new(GAS_LIMIT), &mut storage_meter, @@ -2212,7 +2368,9 @@ mod tests { let mut gas_meter = GasMeter::::new(GAS_LIMIT); let executable = MockExecutable::from_storage(dummy_ch, &schedule, &mut gas_meter).unwrap(); - let mut storage_meter = storage::meter::Meter::new(&ALICE, Some(0), 0).unwrap(); + let contract_origin = Origin::from_account_id(ALICE); + let mut storage_meter = + storage::meter::Meter::new(&contract_origin, Some(0), 0).unwrap(); assert_matches!( MockStack::run_instantiate( @@ -2244,8 +2402,10 @@ mod tests { let executable = MockExecutable::from_storage(dummy_ch, &schedule, &mut gas_meter).unwrap(); set_balance(&ALICE, min_balance * 1000); + let contract_origin = Origin::from_account_id(ALICE); let mut storage_meter = - storage::meter::Meter::new(&ALICE, Some(min_balance * 100), min_balance).unwrap(); + storage::meter::Meter::new(&contract_origin, Some(min_balance * 100), min_balance) + .unwrap(); let instantiated_contract_address = assert_matches!( MockStack::run_instantiate( @@ -2288,8 +2448,10 @@ mod tests { let executable = MockExecutable::from_storage(dummy_ch, &schedule, &mut gas_meter).unwrap(); set_balance(&ALICE, min_balance * 1000); + let contract_origin = Origin::from_account_id(ALICE); let mut storage_meter = - storage::meter::Meter::new(&ALICE, Some(min_balance * 100), min_balance).unwrap(); + storage::meter::Meter::new(&contract_origin, Some(min_balance * 100), min_balance) + .unwrap(); let instantiated_contract_address = assert_matches!( MockStack::run_instantiate( @@ -2342,13 +2504,17 @@ mod tests { let min_balance = ::Currency::minimum_balance(); set_balance(&ALICE, min_balance * 100); place_contract(&BOB, instantiator_ch); - let mut storage_meter = - storage::meter::Meter::new(&ALICE, Some(min_balance * 10), min_balance * 10) - .unwrap(); + let contract_origin = Origin::from_account_id(ALICE); + let mut storage_meter = storage::meter::Meter::new( + &contract_origin, + Some(min_balance * 10), + min_balance * 10, + ) + .unwrap(); assert_matches!( MockStack::run_call( - ALICE, + contract_origin, BOB, &mut GasMeter::::new(GAS_LIMIT), &mut storage_meter, @@ -2374,7 +2540,7 @@ mod tests { &events(), &[ Event::Instantiated { deployer: BOB, contract: instantiated_contract_address }, - Event::Called { caller: ALICE, contract: BOB }, + Event::Called { caller: Origin::from_account_id(ALICE), contract: BOB }, ] ); }); @@ -2410,11 +2576,13 @@ mod tests { set_balance(&ALICE, 1000); set_balance(&BOB, 100); place_contract(&BOB, instantiator_ch); - let mut storage_meter = storage::meter::Meter::new(&ALICE, Some(200), 0).unwrap(); + let contract_origin = Origin::from_account_id(ALICE); + let mut storage_meter = + storage::meter::Meter::new(&contract_origin, Some(200), 0).unwrap(); assert_matches!( MockStack::run_call( - ALICE, + contract_origin, BOB, &mut GasMeter::::new(GAS_LIMIT), &mut storage_meter, @@ -2429,7 +2597,10 @@ mod tests { // The contract wasn't instantiated so we don't expect to see an instantiation // event here. - assert_eq!(&events(), &[Event::Called { caller: ALICE, contract: BOB },]); + assert_eq!( + &events(), + &[Event::Called { caller: Origin::from_account_id(ALICE), contract: BOB },] + ); }); } @@ -2446,7 +2617,9 @@ mod tests { let executable = MockExecutable::from_storage(terminate_ch, &schedule, &mut gas_meter).unwrap(); set_balance(&ALICE, 10_000); - let mut storage_meter = storage::meter::Meter::new(&ALICE, None, 100).unwrap(); + let contract_origin = Origin::from_account_id(ALICE); + let mut storage_meter = + storage::meter::Meter::new(&contract_origin, None, 100).unwrap(); assert_eq!( MockStack::run_instantiate( @@ -2510,10 +2683,12 @@ mod tests { let schedule = ::Schedule::get(); place_contract(&BOB, code_bob); place_contract(&CHARLIE, code_charlie); - let mut storage_meter = storage::meter::Meter::new(&ALICE, Some(0), 0).unwrap(); + let contract_origin = Origin::from_account_id(ALICE); + let mut storage_meter = + storage::meter::Meter::new(&contract_origin, Some(0), 0).unwrap(); let result = MockStack::run_call( - ALICE, + contract_origin, BOB, &mut GasMeter::::new(GAS_LIMIT), &mut storage_meter, @@ -2544,7 +2719,9 @@ mod tests { let mut gas_meter = GasMeter::::new(GAS_LIMIT); let executable = MockExecutable::from_storage(code, &schedule, &mut gas_meter).unwrap(); set_balance(&ALICE, min_balance * 10_000); - let mut storage_meter = storage::meter::Meter::new(&ALICE, None, min_balance).unwrap(); + let contract_origin = Origin::from_account_id(ALICE); + let mut storage_meter = + storage::meter::Meter::new(&contract_origin, None, min_balance).unwrap(); let result = MockStack::run_instantiate( ALICE, @@ -2577,9 +2754,11 @@ mod tests { let mut gas_meter = GasMeter::::new(GAS_LIMIT); set_balance(&ALICE, min_balance * 10); place_contract(&BOB, code_hash); - let mut storage_meter = storage::meter::Meter::new(&ALICE, Some(0), 0).unwrap(); + let contract_origin = Origin::from_account_id(ALICE); + let mut storage_meter = + storage::meter::Meter::new(&contract_origin, Some(0), 0).unwrap(); MockStack::run_call( - ALICE, + contract_origin, BOB, &mut gas_meter, &mut storage_meter, @@ -2611,9 +2790,11 @@ mod tests { let mut gas_meter = GasMeter::::new(GAS_LIMIT); set_balance(&ALICE, min_balance * 10); place_contract(&BOB, code_hash); - let mut storage_meter = storage::meter::Meter::new(&ALICE, Some(0), 0).unwrap(); + let contract_origin = Origin::from_account_id(ALICE); + let mut storage_meter = + storage::meter::Meter::new(&contract_origin, Some(0), 0).unwrap(); let result = MockStack::run_call( - ALICE, + contract_origin, BOB, &mut gas_meter, &mut storage_meter, @@ -2648,9 +2829,11 @@ mod tests { let mut gas_meter = GasMeter::::new(GAS_LIMIT); set_balance(&ALICE, min_balance * 10); place_contract(&BOB, code_hash); - let mut storage_meter = storage::meter::Meter::new(&ALICE, Some(0), 0).unwrap(); + let contract_origin = Origin::from_account_id(ALICE); + let mut storage_meter = + storage::meter::Meter::new(&contract_origin, Some(0), 0).unwrap(); MockStack::run_call( - ALICE, + contract_origin, BOB, &mut gas_meter, &mut storage_meter, @@ -2679,11 +2862,13 @@ mod tests { let schedule = ::Schedule::get(); place_contract(&BOB, code_bob); place_contract(&CHARLIE, code_charlie); - let mut storage_meter = storage::meter::Meter::new(&ALICE, Some(0), 0).unwrap(); + let contract_origin = Origin::from_account_id(ALICE); + let mut storage_meter = + storage::meter::Meter::new(&contract_origin, Some(0), 0).unwrap(); // Calling another contract should succeed assert_ok!(MockStack::run_call( - ALICE, + contract_origin.clone(), BOB, &mut GasMeter::::new(GAS_LIMIT), &mut storage_meter, @@ -2697,7 +2882,7 @@ mod tests { // Calling into oneself fails assert_err!( MockStack::run_call( - ALICE, + contract_origin, BOB, &mut GasMeter::::new(GAS_LIMIT), &mut storage_meter, @@ -2733,12 +2918,14 @@ mod tests { let schedule = ::Schedule::get(); place_contract(&BOB, code_bob); place_contract(&CHARLIE, code_charlie); - let mut storage_meter = storage::meter::Meter::new(&ALICE, Some(0), 0).unwrap(); + let contract_origin = Origin::from_account_id(ALICE); + let mut storage_meter = + storage::meter::Meter::new(&contract_origin, Some(0), 0).unwrap(); // BOB -> CHARLIE -> BOB fails as BOB denies reentry. assert_err!( MockStack::run_call( - ALICE, + contract_origin, BOB, &mut GasMeter::::new(GAS_LIMIT), &mut storage_meter, @@ -2770,10 +2957,12 @@ mod tests { let mut gas_meter = GasMeter::::new(GAS_LIMIT); set_balance(&ALICE, min_balance * 10); place_contract(&BOB, code_hash); - let mut storage_meter = storage::meter::Meter::new(&ALICE, Some(0), 0).unwrap(); + let contract_origin = Origin::from_account_id(ALICE); + let mut storage_meter = + storage::meter::Meter::new(&contract_origin, Some(0), 0).unwrap(); System::reset_events(); MockStack::run_call( - ALICE, + contract_origin, BOB, &mut gas_meter, &mut storage_meter, @@ -2800,10 +2989,10 @@ mod tests { EventRecord { phase: Phase::Initialization, event: MetaEvent::Contracts(crate::Event::Called { - caller: ALICE, + caller: Origin::from_account_id(ALICE), contract: BOB, }), - topics: vec![hash(&ALICE), hash(&BOB)], + topics: vec![hash(&Origin::::from_account_id(ALICE)), hash(&BOB)], }, ] ); @@ -2855,10 +3044,12 @@ mod tests { let mut gas_meter = GasMeter::::new(GAS_LIMIT); set_balance(&ALICE, min_balance * 10); place_contract(&BOB, code_hash); - let mut storage_meter = storage::meter::Meter::new(&ALICE, Some(0), 0).unwrap(); + let contract_origin = Origin::from_account_id(ALICE); + let mut storage_meter = + storage::meter::Meter::new(&contract_origin, Some(0), 0).unwrap(); System::reset_events(); MockStack::run_call( - ALICE, + contract_origin, BOB, &mut gas_meter, &mut storage_meter, @@ -2898,10 +3089,10 @@ mod tests { EventRecord { phase: Phase::Initialization, event: MetaEvent::Contracts(crate::Event::Called { - caller: ALICE, + caller: Origin::from_account_id(ALICE), contract: BOB, }), - topics: vec![hash(&ALICE), hash(&BOB)], + topics: vec![hash(&Origin::::from_account_id(ALICE)), hash(&BOB)], }, ] ); @@ -2959,8 +3150,9 @@ mod tests { let succ_succ_executable = MockExecutable::from_storage(succ_succ_code, &schedule, &mut gas_meter).unwrap(); set_balance(&ALICE, min_balance * 10_000); + let contract_origin = Origin::from_account_id(ALICE); let mut storage_meter = - storage::meter::Meter::new(&ALICE, None, min_balance * 100).unwrap(); + storage::meter::Meter::new(&contract_origin, None, min_balance * 100).unwrap(); MockStack::run_instantiate( ALICE, @@ -3069,9 +3261,10 @@ mod tests { let mut gas_meter = GasMeter::::new(GAS_LIMIT); set_balance(&ALICE, min_balance * 1000); place_contract(&BOB, code_hash); - let mut storage_meter = storage::meter::Meter::new(&ALICE, None, 0).unwrap(); + let contract_origin = Origin::from_account_id(ALICE); + let mut storage_meter = storage::meter::Meter::new(&contract_origin, None, 0).unwrap(); assert_ok!(MockStack::run_call( - ALICE, + contract_origin, BOB, &mut gas_meter, &mut storage_meter, @@ -3196,9 +3389,10 @@ mod tests { let mut gas_meter = GasMeter::::new(GAS_LIMIT); set_balance(&ALICE, min_balance * 1000); place_contract(&BOB, code_hash); - let mut storage_meter = storage::meter::Meter::new(&ALICE, None, 0).unwrap(); + let contract_origin = Origin::from_account_id(ALICE); + let mut storage_meter = storage::meter::Meter::new(&contract_origin, None, 0).unwrap(); assert_ok!(MockStack::run_call( - ALICE, + contract_origin, BOB, &mut gas_meter, &mut storage_meter, @@ -3235,9 +3429,10 @@ mod tests { let mut gas_meter = GasMeter::::new(GAS_LIMIT); set_balance(&ALICE, min_balance * 1000); place_contract(&BOB, code_hash); - let mut storage_meter = storage::meter::Meter::new(&ALICE, None, 0).unwrap(); + let contract_origin = Origin::from_account_id(ALICE); + let mut storage_meter = storage::meter::Meter::new(&contract_origin, None, 0).unwrap(); assert_ok!(MockStack::run_call( - ALICE, + contract_origin, BOB, &mut gas_meter, &mut storage_meter, @@ -3274,9 +3469,10 @@ mod tests { let mut gas_meter = GasMeter::::new(GAS_LIMIT); set_balance(&ALICE, min_balance * 1000); place_contract(&BOB, code_hash); - let mut storage_meter = storage::meter::Meter::new(&ALICE, None, 0).unwrap(); + let contract_origin = Origin::from_account_id(ALICE); + let mut storage_meter = storage::meter::Meter::new(&contract_origin, None, 0).unwrap(); assert_ok!(MockStack::run_call( - ALICE, + contract_origin, BOB, &mut gas_meter, &mut storage_meter, @@ -3330,9 +3526,10 @@ mod tests { let mut gas_meter = GasMeter::::new(GAS_LIMIT); set_balance(&ALICE, min_balance * 1000); place_contract(&BOB, code_hash); - let mut storage_meter = storage::meter::Meter::new(&ALICE, None, 0).unwrap(); + let contract_origin = Origin::from_account_id(ALICE); + let mut storage_meter = storage::meter::Meter::new(&contract_origin, None, 0).unwrap(); assert_ok!(MockStack::run_call( - ALICE, + contract_origin, BOB, &mut gas_meter, &mut storage_meter, @@ -3386,9 +3583,10 @@ mod tests { let mut gas_meter = GasMeter::::new(GAS_LIMIT); set_balance(&ALICE, min_balance * 1000); place_contract(&BOB, code_hash); - let mut storage_meter = storage::meter::Meter::new(&ALICE, None, 0).unwrap(); + let contract_origin = Origin::from_account_id(ALICE); + let mut storage_meter = storage::meter::Meter::new(&contract_origin, None, 0).unwrap(); assert_ok!(MockStack::run_call( - ALICE, + contract_origin, BOB, &mut gas_meter, &mut storage_meter, @@ -3418,9 +3616,11 @@ mod tests { let schedule = ::Schedule::get(); place_contract(&BOB, bob_ch); - let mut storage_meter = storage::meter::Meter::new(&ALICE, Some(0), 0).unwrap(); + let contract_origin = Origin::from_account_id(ALICE); + let mut storage_meter = + storage::meter::Meter::new(&contract_origin, Some(0), 0).unwrap(); let result = MockStack::run_call( - ALICE, + contract_origin, BOB, &mut GasMeter::::new(GAS_LIMIT), &mut storage_meter, @@ -3480,9 +3680,10 @@ mod tests { let mut gas_meter = GasMeter::::new(GAS_LIMIT); set_balance(&ALICE, min_balance * 1000); place_contract(&BOB, code_hash); - let mut storage_meter = storage::meter::Meter::new(&ALICE, None, 0).unwrap(); + let contract_origin = Origin::from_account_id(ALICE); + let mut storage_meter = storage::meter::Meter::new(&contract_origin, None, 0).unwrap(); assert_ok!(MockStack::run_call( - ALICE, + contract_origin, BOB, &mut gas_meter, &mut storage_meter, @@ -3510,9 +3711,11 @@ mod tests { let schedule = ::Schedule::get(); place_contract(&BOB, code_hash); - let mut storage_meter = storage::meter::Meter::new(&ALICE, Some(0), 0).unwrap(); + let contract_origin = Origin::from_account_id(ALICE); + let mut storage_meter = + storage::meter::Meter::new(&contract_origin, Some(0), 0).unwrap(); let result = MockStack::run_call( - ALICE, + contract_origin, BOB, &mut GasMeter::::new(GAS_LIMIT), &mut storage_meter, diff --git a/frame/contracts/src/lib.rs b/frame/contracts/src/lib.rs index 24653423b..97fa28457 100644 --- a/frame/contracts/src/lib.rs +++ b/frame/contracts/src/lib.rs @@ -97,7 +97,6 @@ pub mod weights; #[cfg(test)] mod tests; - use crate::{ exec::{AccountIdOf, ErrorOrigin, ExecError, Executable, Key, Stack as ExecStack}, gas::GasMeter, @@ -105,19 +104,20 @@ use crate::{ wasm::{OwnerInfo, PrefabWasmModule, TryInstantiate}, weights::WeightInfo, }; -use codec::{Codec, Encode, HasCompact}; +use codec::{Codec, Decode, Encode, HasCompact}; use environmental::*; use frame_support::{ - dispatch::{Dispatchable, GetDispatchInfo, Pays, PostDispatchInfo}, + dispatch::{DispatchError, Dispatchable, GetDispatchInfo, Pays, PostDispatchInfo, RawOrigin}, ensure, + error::BadOrigin, traits::{ tokens::fungible::Inspect, ConstU32, Contains, Currency, Get, Randomness, ReservableCurrency, Time, }, weights::Weight, - BoundedVec, WeakBoundedVec, + BoundedVec, RuntimeDebugNoBound, WeakBoundedVec, }; -use frame_system::Pallet as System; +use frame_system::{ensure_signed, pallet_prelude::OriginFor, Pallet as System}; use pallet_contracts_primitives::{ Code, CodeUploadResult, CodeUploadReturnValue, ContractAccessError, ContractExecResult, ContractInstantiateResult, ExecReturnValue, GetStorageResult, InstantiateReturnValue, @@ -584,17 +584,15 @@ pub mod pallet { storage_deposit_limit: Option< as codec::HasCompact>::Type>, data: Vec, ) -> DispatchResultWithPostInfo { - let gas_limit: Weight = gas_limit.into(); - let origin = ensure_signed(origin)?; - let dest = T::Lookup::lookup(dest)?; let common = CommonInput { - origin, + origin: Origin::from_runtime_origin(origin)?, value, data, - gas_limit, + gas_limit: gas_limit.into(), storage_deposit_limit: storage_deposit_limit.map(Into::into), debug_message: None, }; + let dest = T::Lookup::lookup(dest)?; let mut output = CallInput:: { dest, determinism: Determinism::Enforced }.run_guarded(common); if let Ok(retval) = &output.result { @@ -645,12 +643,11 @@ pub mod pallet { data: Vec, salt: Vec, ) -> DispatchResultWithPostInfo { - let origin = ensure_signed(origin)?; let code_len = code.len() as u32; let data_len = data.len() as u32; let salt_len = salt.len() as u32; let common = CommonInput { - origin, + origin: Origin::from_runtime_origin(origin)?, value, data, gas_limit, @@ -688,11 +685,10 @@ pub mod pallet { data: Vec, salt: Vec, ) -> DispatchResultWithPostInfo { - let origin = ensure_signed(origin)?; let data_len = data.len() as u32; let salt_len = salt.len() as u32; let common = CommonInput { - origin, + origin: Origin::from_runtime_origin(origin)?, value, data, gas_limit, @@ -764,8 +760,8 @@ pub mod pallet { /// calls. This is because on failure all storage changes including events are /// rolled back. Called { - /// The account that called the `contract`. - caller: T::AccountId, + /// The caller of the `contract`. + caller: Origin, /// The contract that was called. contract: T::AccountId, }, @@ -924,9 +920,38 @@ pub mod pallet { StorageValue<_, DeletionQueueManager, ValueQuery>; } +/// The type of origins supported by the contracts pallet. +#[derive(Clone, Encode, Decode, PartialEq, TypeInfo, RuntimeDebugNoBound)] +pub enum Origin { + Root, + Signed(T::AccountId), +} + +impl Origin { + /// Creates a new Signed Caller from an AccountId. + pub fn from_account_id(account_id: T::AccountId) -> Self { + Origin::Signed(account_id) + } + /// Creates a new Origin from a `RuntimeOrigin`. + pub fn from_runtime_origin(o: OriginFor) -> Result { + match o.into() { + Ok(RawOrigin::Root) => Ok(Self::Root), + Ok(RawOrigin::Signed(t)) => Ok(Self::Signed(t)), + _ => Err(BadOrigin.into()), + } + } + /// Returns the AccountId of a Signed Origin or an error if the origin is Root. + pub fn account_id(&self) -> Result<&T::AccountId, DispatchError> { + match self { + Origin::Signed(id) => Ok(id), + Origin::Root => Err(DispatchError::RootNotAllowed), + } + } +} + /// Context of a contract invocation. struct CommonInput<'a, T: Config> { - origin: T::AccountId, + origin: Origin, value: BalanceOf, data: Vec, gas_limit: Weight, @@ -975,6 +1000,19 @@ trait Invokable { environmental!(executing_contract: bool); let gas_limit = common.gas_limit; + + // Check whether the origin is allowed here. The logic of the access rules + // is in the `ensure_origin`, this could vary for different implementations of this + // trait. For example, some actions might not allow Root origin as they could require an + // AccountId associated with the origin. + if let Err(e) = self.ensure_origin(common.origin.clone()) { + return InternalOutput { + gas_meter: GasMeter::new(gas_limit), + storage_deposit: Default::default(), + result: Err(ExecError { error: e.into(), origin: ErrorOrigin::Caller }), + } + } + executing_contract::using_once(&mut false, || { executing_contract::with(|f| { // Fail if already entered contract execution @@ -1010,6 +1048,11 @@ trait Invokable { common: CommonInput, gas_meter: GasMeter, ) -> InternalOutput; + + /// This method ensures that the given `origin` is allowed to invoke the current `Invokable`. + /// + /// Called by dispatchables and public functions through the [`Invokable::run_guarded`]. + fn ensure_origin(&self, origin: Origin) -> Result<(), DispatchError>; } impl Invokable for CallInput { @@ -1020,8 +1063,10 @@ impl Invokable for CallInput { common: CommonInput, mut gas_meter: GasMeter, ) -> InternalOutput { + let CallInput { dest, determinism } = self; + let CommonInput { origin, value, data, debug_message, .. } = common; let mut storage_meter = - match StorageMeter::new(&common.origin, common.storage_deposit_limit, common.value) { + match StorageMeter::new(&origin, common.storage_deposit_limit, common.value) { Ok(meter) => meter, Err(err) => return InternalOutput { @@ -1031,8 +1076,6 @@ impl Invokable for CallInput { }, }; let schedule = T::Schedule::get(); - let CallInput { dest, determinism } = self; - let CommonInput { origin, value, data, debug_message, .. } = common; let result = ExecStack::>::run_call( origin.clone(), dest.clone(), @@ -1054,6 +1097,10 @@ impl Invokable for CallInput { }, } } + + fn ensure_origin(&self, _origin: Origin) -> Result<(), DispatchError> { + Ok(()) + } } impl Invokable for InstantiateInput { @@ -1067,12 +1114,15 @@ impl Invokable for InstantiateInput { let mut storage_deposit = Default::default(); let try_exec = || { let schedule = T::Schedule::get(); + let InstantiateInput { salt, .. } = self; + let CommonInput { origin: contract_origin, .. } = common; + let origin = contract_origin.account_id()?; let (extra_deposit, executable) = match &self.code { Code::Upload(binary) => { let executable = PrefabWasmModule::from_code( binary.clone(), &schedule, - common.origin.clone(), + origin.clone(), Determinism::Enforced, TryInstantiate::Skip, ) @@ -1094,14 +1144,13 @@ impl Invokable for InstantiateInput { PrefabWasmModule::from_storage(*hash, &schedule, &mut gas_meter)?, ), }; + let contract_origin = Origin::from_account_id(origin.clone()); let mut storage_meter = StorageMeter::new( - &common.origin, + &contract_origin, common.storage_deposit_limit, common.value.saturating_add(extra_deposit), )?; - - let InstantiateInput { salt, .. } = self; - let CommonInput { origin, value, data, debug_message, .. } = common; + let CommonInput { value, data, debug_message, .. } = common; let result = ExecStack::>::run_instantiate( origin.clone(), executable, @@ -1115,12 +1164,19 @@ impl Invokable for InstantiateInput { ); storage_deposit = storage_meter - .try_into_deposit(&origin)? + .try_into_deposit(&contract_origin)? .saturating_add(&StorageDeposit::Charge(extra_deposit)); result }; InternalOutput { result: try_exec(), gas_meter, storage_deposit } } + + fn ensure_origin(&self, origin: Origin) -> Result<(), DispatchError> { + match origin { + Origin::Signed(_) => Ok(()), + Origin::Root => Err(DispatchError::RootNotAllowed), + } + } } impl Pallet { @@ -1147,6 +1203,7 @@ impl Pallet { determinism: Determinism, ) -> ContractExecResult> { let mut debug_message = if debug { Some(DebugBufferVec::::default()) } else { None }; + let origin = Origin::from_account_id(origin); let common = CommonInput { origin, value, @@ -1189,7 +1246,7 @@ impl Pallet { ) -> ContractInstantiateResult> { let mut debug_message = if debug { Some(DebugBufferVec::::default()) } else { None }; let common = CommonInput { - origin, + origin: Origin::from_account_id(origin), value, data, gas_limit, diff --git a/frame/contracts/src/schedule.rs b/frame/contracts/src/schedule.rs index aef2fa1ca..c6eedb155 100644 --- a/frame/contracts/src/schedule.rs +++ b/frame/contracts/src/schedule.rs @@ -260,6 +260,9 @@ pub struct HostFnWeights { /// Weight of calling `seal_caller_is_origin`. pub caller_is_origin: Weight, + /// Weight of calling `seal_caller_is_root`. + pub caller_is_root: Weight, + /// Weight of calling `seal_address`. pub address: Weight, @@ -557,6 +560,7 @@ impl Default for HostFnWeights { code_hash: cost!(seal_code_hash), own_code_hash: cost!(seal_own_code_hash), caller_is_origin: cost!(seal_caller_is_origin), + caller_is_root: cost!(seal_caller_is_root), address: cost!(seal_address), gas_left: cost!(seal_gas_left), balance: cost!(seal_balance), diff --git a/frame/contracts/src/storage/meter.rs b/frame/contracts/src/storage/meter.rs index 73794b597..506f4f0d8 100644 --- a/frame/contracts/src/storage/meter.rs +++ b/frame/contracts/src/storage/meter.rs @@ -19,7 +19,7 @@ use crate::{ storage::{ContractInfo, DepositAccount}, - BalanceOf, Config, Error, Inspect, Pallet, System, + BalanceOf, Config, Error, Inspect, Origin, Pallet, System, }; use codec::Encode; use frame_support::{ @@ -352,12 +352,21 @@ where /// /// This tries to [`Ext::check_limit`] on `origin` and fails if this is not possible. pub fn new( - origin: &T::AccountId, + origin: &Origin, limit: Option>, min_leftover: BalanceOf, ) -> Result { - let limit = E::check_limit(origin, limit, min_leftover)?; - Ok(Self { limit, ..Default::default() }) + // Check the limit only if the origin is not root. + return match origin { + Origin::Root => Ok(Self { + limit: limit.unwrap_or(T::DefaultDepositLimit::get()), + ..Default::default() + }), + Origin::Signed(o) => { + let limit = E::check_limit(o, limit, min_leftover)?; + Ok(Self { limit, ..Default::default() }) + }, + } } /// The total amount of deposit that should change hands as result of the execution @@ -366,7 +375,13 @@ where /// /// This drops the root meter in order to make sure it is only called when the whole /// execution did finish. - pub fn try_into_deposit(self, origin: &T::AccountId) -> Result, DispatchError> { + + pub fn try_into_deposit(self, origin: &Origin) -> Result, DispatchError> { + // Only refund or charge deposit if the origin is not root. + let origin = match origin { + Origin::Root => return Ok(Deposit::Charge(Zero::zero())), + Origin::Signed(o) => o, + }; for charge in self.charges.iter().filter(|c| matches!(c.amount, Deposit::Refund(_))) { E::charge(origin, &charge.deposit_account, &charge.amount, charge.terminated)?; } @@ -625,6 +640,12 @@ mod tests { TestExtTestValue::mutate(|ext| ext.clear()) } + struct ChargingTestCase { + origin: Origin, + deposit: DepositOf, + expected: TestExt, + } + #[derive(Default)] struct StorageInfo { bytes: u32, @@ -650,7 +671,7 @@ mod tests { fn new_reserves_balance_works() { clear_ext(); - TestMeter::new(&ALICE, Some(1_000), 0).unwrap(); + TestMeter::new(&Origin::from_account_id(ALICE), Some(1_000), 0).unwrap(); assert_eq!( TestExtTestValue::get(), @@ -665,7 +686,7 @@ mod tests { fn empty_charge_works() { clear_ext(); - let mut meter = TestMeter::new(&ALICE, Some(1_000), 0).unwrap(); + let mut meter = TestMeter::new(&Origin::from_account_id(ALICE), Some(1_000), 0).unwrap(); assert_eq!(meter.available(), 1_000); // an empty charge does not create a `Charge` entry @@ -684,118 +705,158 @@ mod tests { #[test] fn charging_works() { - clear_ext(); + let test_cases = vec![ + ChargingTestCase { + origin: Origin::::from_account_id(ALICE), + deposit: Deposit::Refund(28), + expected: TestExt { + limit_checks: vec![LimitCheck { origin: ALICE, limit: 100, min_leftover: 0 }], + charges: vec![ + Charge { + origin: ALICE, + contract: DepositAccount(CHARLIE), + amount: Deposit::Refund(10), + terminated: false, + }, + Charge { + origin: ALICE, + contract: DepositAccount(CHARLIE), + amount: Deposit::Refund(20), + terminated: false, + }, + Charge { + origin: ALICE, + contract: DepositAccount(BOB), + amount: Deposit::Charge(2), + terminated: false, + }, + ], + }, + }, + ChargingTestCase { + origin: Origin::::Root, + deposit: Deposit::Charge(0), + expected: TestExt { limit_checks: vec![], charges: vec![] }, + }, + ]; - let mut meter = TestMeter::new(&ALICE, Some(100), 0).unwrap(); - assert_eq!(meter.available(), 100); + for test_case in test_cases { + clear_ext(); - let mut nested0_info = - new_info(StorageInfo { bytes: 100, items: 5, bytes_deposit: 100, items_deposit: 10 }); - let mut nested0 = meter.nested(BalanceOf::::zero()); - nested0.charge(&Diff { - bytes_added: 108, - bytes_removed: 5, - items_added: 1, - items_removed: 2, - }); - nested0.charge(&Diff { bytes_removed: 99, ..Default::default() }); - - let mut nested1_info = - new_info(StorageInfo { bytes: 100, items: 10, bytes_deposit: 100, items_deposit: 20 }); - let mut nested1 = nested0.nested(BalanceOf::::zero()); - nested1.charge(&Diff { items_removed: 5, ..Default::default() }); - nested0.absorb(nested1, DepositAccount(CHARLIE), Some(&mut nested1_info)); - - let mut nested2_info = - new_info(StorageInfo { bytes: 100, items: 7, bytes_deposit: 100, items_deposit: 20 }); - let mut nested2 = nested0.nested(BalanceOf::::zero()); - nested2.charge(&Diff { items_removed: 7, ..Default::default() }); - nested0.absorb(nested2, DepositAccount(CHARLIE), Some(&mut nested2_info)); - - nested0.enforce_limit(Some(&mut nested0_info)).unwrap(); - meter.absorb(nested0, DepositAccount(BOB), Some(&mut nested0_info)); - - meter.try_into_deposit(&ALICE).unwrap(); - - assert_eq!(nested0_info.extra_deposit(), 112); - assert_eq!(nested1_info.extra_deposit(), 110); - assert_eq!(nested2_info.extra_deposit(), 100); + let mut meter = TestMeter::new(&test_case.origin, Some(100), 0).unwrap(); + assert_eq!(meter.available(), 100); - assert_eq!( - TestExtTestValue::get(), - TestExt { - limit_checks: vec![LimitCheck { origin: ALICE, limit: 100, min_leftover: 0 }], - charges: vec![ - Charge { - origin: ALICE, - contract: DepositAccount(CHARLIE), - amount: Deposit::Refund(10), - terminated: false - }, - Charge { - origin: ALICE, - contract: DepositAccount(CHARLIE), - amount: Deposit::Refund(20), - terminated: false - }, - Charge { - origin: ALICE, - contract: DepositAccount(BOB), - amount: Deposit::Charge(2), - terminated: false - } - ] - } - ) + let mut nested0_info = new_info(StorageInfo { + bytes: 100, + items: 5, + bytes_deposit: 100, + items_deposit: 10, + }); + let mut nested0 = meter.nested(BalanceOf::::zero()); + nested0.charge(&Diff { + bytes_added: 108, + bytes_removed: 5, + items_added: 1, + items_removed: 2, + }); + nested0.charge(&Diff { bytes_removed: 99, ..Default::default() }); + + let mut nested1_info = new_info(StorageInfo { + bytes: 100, + items: 10, + bytes_deposit: 100, + items_deposit: 20, + }); + let mut nested1 = nested0.nested(BalanceOf::::zero()); + nested1.charge(&Diff { items_removed: 5, ..Default::default() }); + nested0.absorb(nested1, DepositAccount(CHARLIE), Some(&mut nested1_info)); + + let mut nested2_info = new_info(StorageInfo { + bytes: 100, + items: 7, + bytes_deposit: 100, + items_deposit: 20, + }); + let mut nested2 = nested0.nested(BalanceOf::::zero()); + nested2.charge(&Diff { items_removed: 7, ..Default::default() }); + nested0.absorb(nested2, DepositAccount(CHARLIE), Some(&mut nested2_info)); + + nested0.enforce_limit(Some(&mut nested0_info)).unwrap(); + meter.absorb(nested0, DepositAccount(BOB), Some(&mut nested0_info)); + + assert_eq!(meter.try_into_deposit(&test_case.origin).unwrap(), test_case.deposit); + + assert_eq!(nested0_info.extra_deposit(), 112); + assert_eq!(nested1_info.extra_deposit(), 110); + assert_eq!(nested2_info.extra_deposit(), 100); + + assert_eq!(TestExtTestValue::get(), test_case.expected) + } } #[test] fn termination_works() { - clear_ext(); + let test_cases = vec![ + ChargingTestCase { + origin: Origin::::from_account_id(ALICE), + deposit: Deposit::Refund(107), + expected: TestExt { + limit_checks: vec![LimitCheck { origin: ALICE, limit: 1_000, min_leftover: 0 }], + charges: vec![ + Charge { + origin: ALICE, + contract: DepositAccount(CHARLIE), + amount: Deposit::Refund(119), + terminated: true, + }, + Charge { + origin: ALICE, + contract: DepositAccount(BOB), + amount: Deposit::Charge(12), + terminated: false, + }, + ], + }, + }, + ChargingTestCase { + origin: Origin::::Root, + deposit: Deposit::Charge(0), + expected: TestExt { limit_checks: vec![], charges: vec![] }, + }, + ]; - let mut meter = TestMeter::new(&ALICE, Some(1_000), 0).unwrap(); - assert_eq!(meter.available(), 1_000); + for test_case in test_cases { + clear_ext(); - let mut nested0 = meter.nested(BalanceOf::::zero()); - nested0.charge(&Diff { - bytes_added: 5, - bytes_removed: 1, - items_added: 3, - items_removed: 1, - }); - nested0.charge(&Diff { items_added: 2, ..Default::default() }); - - let mut nested1_info = - new_info(StorageInfo { bytes: 100, items: 10, bytes_deposit: 100, items_deposit: 20 }); - let mut nested1 = nested0.nested(BalanceOf::::zero()); - nested1.charge(&Diff { items_removed: 5, ..Default::default() }); - nested1.charge(&Diff { bytes_added: 20, ..Default::default() }); - nested1.terminate(&nested1_info); - nested0.enforce_limit(Some(&mut nested1_info)).unwrap(); - nested0.absorb(nested1, DepositAccount(CHARLIE), None); + let mut meter = TestMeter::new(&test_case.origin, Some(1_000), 0).unwrap(); + assert_eq!(meter.available(), 1_000); - meter.absorb(nested0, DepositAccount(BOB), None); - meter.try_into_deposit(&ALICE).unwrap(); + let mut nested0 = meter.nested(BalanceOf::::zero()); + nested0.charge(&Diff { + bytes_added: 5, + bytes_removed: 1, + items_added: 3, + items_removed: 1, + }); + nested0.charge(&Diff { items_added: 2, ..Default::default() }); - assert_eq!( - TestExtTestValue::get(), - TestExt { - limit_checks: vec![LimitCheck { origin: ALICE, limit: 1_000, min_leftover: 0 }], - charges: vec![ - Charge { - origin: ALICE, - contract: DepositAccount(CHARLIE), - amount: Deposit::Refund(119), - terminated: true - }, - Charge { - origin: ALICE, - contract: DepositAccount(BOB), - amount: Deposit::Charge(12), - terminated: false - } - ] - } - ) + let mut nested1_info = new_info(StorageInfo { + bytes: 100, + items: 10, + bytes_deposit: 100, + items_deposit: 20, + }); + let mut nested1 = nested0.nested(BalanceOf::::zero()); + nested1.charge(&Diff { items_removed: 5, ..Default::default() }); + nested1.charge(&Diff { bytes_added: 20, ..Default::default() }); + nested1.terminate(&nested1_info); + nested0.enforce_limit(Some(&mut nested1_info)).unwrap(); + nested0.absorb(nested1, DepositAccount(CHARLIE), None); + + meter.absorb(nested0, DepositAccount(BOB), None); + assert_eq!(meter.try_into_deposit(&test_case.origin).unwrap(), test_case.deposit); + + assert_eq!(TestExtTestValue::get(), test_case.expected) + } } } diff --git a/frame/contracts/src/tests.rs b/frame/contracts/src/tests.rs index c202d3796..a07176e9f 100644 --- a/frame/contracts/src/tests.rs +++ b/frame/contracts/src/tests.rs @@ -28,13 +28,13 @@ use crate::{ wasm::{Determinism, PrefabWasmModule, ReturnCode as RuntimeReturnCode}, weights::WeightInfo, BalanceOf, Code, CodeStorage, Config, ContractInfo, ContractInfoOf, DefaultAddressGenerator, - DeletionQueueCounter, Error, Pallet, Schedule, + DeletionQueueCounter, Error, Origin, Pallet, Schedule, }; use assert_matches::assert_matches; use codec::Encode; use frame_support::{ assert_err, assert_err_ignore_postinfo, assert_err_with_weight, assert_noop, assert_ok, - dispatch::{DispatchErrorWithPostInfo, PostDispatchInfo}, + dispatch::{DispatchError, DispatchErrorWithPostInfo, PostDispatchInfo}, parameter_types, storage::child, traits::{ @@ -517,6 +517,11 @@ impl<'a> From> for Vec { } } +impl Default for Origin { + fn default() -> Self { + Self::Signed(ALICE) + } +} // Perform a call to a plain account. // The actual transfer fails because we can only call contracts. // Then we check that at least the base costs where charged (no runtime gas costs.) @@ -986,18 +991,21 @@ fn deploy_and_call_other_contract() { EventRecord { phase: Phase::Initialization, event: RuntimeEvent::Contracts(crate::Event::Called { - caller: caller_addr.clone(), + caller: Origin::from_account_id(caller_addr.clone()), contract: callee_addr.clone(), }), - topics: vec![hash(&caller_addr), hash(&callee_addr)], + topics: vec![ + hash(&Origin::::from_account_id(caller_addr.clone())), + hash(&callee_addr) + ], }, EventRecord { phase: Phase::Initialization, event: RuntimeEvent::Contracts(crate::Event::Called { - caller: ALICE, + caller: Origin::from_account_id(ALICE), contract: caller_addr.clone(), }), - topics: vec![hash(&ALICE), hash(&caller_addr)], + topics: vec![hash(&Origin::::from_account_id(ALICE)), hash(&caller_addr)], }, ] ); @@ -1305,10 +1313,10 @@ fn self_destruct_works() { EventRecord { phase: Phase::Initialization, event: RuntimeEvent::Contracts(crate::Event::Called { - caller: ALICE, + caller: Origin::from_account_id(ALICE), contract: addr.clone(), }), - topics: vec![hash(&ALICE), hash(&addr)], + topics: vec![hash(&Origin::::from_account_id(ALICE)), hash(&addr)], }, EventRecord { phase: Phase::Initialization, @@ -3626,10 +3634,10 @@ fn storage_deposit_works() { EventRecord { phase: Phase::Initialization, event: RuntimeEvent::Contracts(crate::Event::Called { - caller: ALICE, + caller: Origin::from_account_id(ALICE), contract: addr.clone(), }), - topics: vec![hash(&ALICE), hash(&addr)], + topics: vec![hash(&Origin::::from_account_id(ALICE)), hash(&addr)], }, EventRecord { phase: Phase::Initialization, @@ -3643,10 +3651,10 @@ fn storage_deposit_works() { EventRecord { phase: Phase::Initialization, event: RuntimeEvent::Contracts(crate::Event::Called { - caller: ALICE, + caller: Origin::from_account_id(ALICE), contract: addr.clone(), }), - topics: vec![hash(&ALICE), hash(&addr)], + topics: vec![hash(&Origin::::from_account_id(ALICE)), hash(&addr)], }, EventRecord { phase: Phase::Initialization, @@ -3660,10 +3668,10 @@ fn storage_deposit_works() { EventRecord { phase: Phase::Initialization, event: RuntimeEvent::Contracts(crate::Event::Called { - caller: ALICE, + caller: Origin::from_account_id(ALICE), contract: addr.clone(), }), - topics: vec![hash(&ALICE), hash(&addr)], + topics: vec![hash(&Origin::::from_account_id(ALICE)), hash(&addr)], }, EventRecord { phase: Phase::Initialization, @@ -4115,18 +4123,24 @@ fn set_code_hash() { EventRecord { phase: Phase::Initialization, event: RuntimeEvent::Contracts(crate::Event::Called { - caller: ALICE, + caller: Origin::from_account_id(ALICE), contract: contract_addr.clone(), }), - topics: vec![hash(&ALICE), hash(&contract_addr)], + topics: vec![ + hash(&Origin::::from_account_id(ALICE)), + hash(&contract_addr) + ], }, EventRecord { phase: Phase::Initialization, event: RuntimeEvent::Contracts(crate::Event::Called { - caller: ALICE, + caller: Origin::from_account_id(ALICE), contract: contract_addr.clone(), }), - topics: vec![hash(&ALICE), hash(&contract_addr)], + topics: vec![ + hash(&Origin::::from_account_id(ALICE)), + hash(&contract_addr) + ], }, ], ); @@ -5015,3 +5029,122 @@ fn account_reentrance_count_works() { assert_eq!(result2.data, 0.encode()); }); } + +#[test] +fn root_cannot_upload_code() { + let (wasm, _) = compile_module::("dummy").unwrap(); + + ExtBuilder::default().build().execute_with(|| { + assert_noop!( + Contracts::upload_code(RuntimeOrigin::root(), wasm, None, Determinism::Enforced), + DispatchError::BadOrigin, + ); + }); +} + +#[test] +fn root_cannot_remove_code() { + let (_, code_hash) = compile_module::("dummy").unwrap(); + + ExtBuilder::default().build().execute_with(|| { + assert_noop!( + Contracts::remove_code(RuntimeOrigin::root(), code_hash), + DispatchError::BadOrigin, + ); + }); +} + +#[test] +fn signed_cannot_set_code() { + let (_, code_hash) = compile_module::("dummy").unwrap(); + + ExtBuilder::default().build().execute_with(|| { + assert_noop!( + Contracts::set_code(RuntimeOrigin::signed(ALICE), BOB, code_hash), + DispatchError::BadOrigin, + ); + }); +} + +#[test] +fn none_cannot_call_code() { + ExtBuilder::default().build().execute_with(|| { + assert_noop!( + Contracts::call(RuntimeOrigin::none(), BOB, 0, GAS_LIMIT, None, Vec::new()), + DispatchError::BadOrigin, + ); + }); +} + +#[test] +fn root_can_call() { + let (wasm, _) = compile_module::("dummy").unwrap(); + + ExtBuilder::default().existential_deposit(100).build().execute_with(|| { + let _ = Balances::deposit_creating(&ALICE, 1_000_000); + + let addr = Contracts::bare_instantiate( + ALICE, + 0, + GAS_LIMIT, + None, + Code::Upload(wasm), + vec![], + vec![], + false, + ) + .result + .unwrap() + .account_id; + + // Call the contract. + assert_ok!(Contracts::call( + RuntimeOrigin::root(), + addr.clone(), + 0, + GAS_LIMIT, + None, + vec![] + )); + }); +} + +#[test] +fn root_cannot_instantiate_with_code() { + let (wasm, _) = compile_module::("dummy").unwrap(); + + ExtBuilder::default().build().execute_with(|| { + assert_err_ignore_postinfo!( + Contracts::instantiate_with_code( + RuntimeOrigin::root(), + 0, + GAS_LIMIT, + None, + wasm, + vec![], + vec![], + ), + DispatchError::RootNotAllowed, + ); + }); +} + +#[test] +fn root_cannot_instantiate() { + let (_, code_hash) = compile_module::("dummy").unwrap(); + + ExtBuilder::default().build().execute_with(|| { + assert_err_ignore_postinfo!( + Contracts::instantiate( + RuntimeOrigin::root(), + 0, + GAS_LIMIT, + None, + code_hash, + vec![], + vec![], + ), + DispatchError::RootNotAllowed + ); + }); +} diff --git a/frame/contracts/src/wasm/mod.rs b/frame/contracts/src/wasm/mod.rs index 5be28a301..224c11694 100644 --- a/frame/contracts/src/wasm/mod.rs +++ b/frame/contracts/src/wasm/mod.rs @@ -370,7 +370,7 @@ mod tests { gas::GasMeter, storage::WriteOutcome, tests::{RuntimeCall, Test, ALICE, BOB}, - BalanceOf, CodeHash, Error, Pallet as Contracts, + BalanceOf, CodeHash, Error, Origin, Pallet as Contracts, }; use assert_matches::assert_matches; use frame_support::{ @@ -436,6 +436,7 @@ mod tests { ecdsa_recover: RefCell>, sr25519_verify: RefCell, [u8; 32])>>, code_hashes: Vec>, + caller: Origin, } /// The call is mocked and just returns this hardcoded value. @@ -459,6 +460,7 @@ mod tests { gas_meter: GasMeter::new(Weight::from_parts(10_000_000_000, 10 * 1024 * 1024)), debug_buffer: Default::default(), ecdsa_recover: Default::default(), + caller: Default::default(), sr25519_verify: Default::default(), } } @@ -545,8 +547,8 @@ mod tests { } Ok(result) } - fn caller(&self) -> &AccountIdOf { - &ALICE + fn caller(&self) -> Origin { + self.caller.clone() } fn is_contract(&self, _address: &AccountIdOf) -> bool { true @@ -561,6 +563,9 @@ mod tests { fn caller_is_origin(&self) -> bool { false } + fn caller_is_root(&self) -> bool { + &self.caller == &Origin::Root + } fn address(&self) -> &AccountIdOf { &BOB } @@ -1499,6 +1504,16 @@ mod tests { assert_ok!(execute(CODE_CALLER, vec![], MockExt::default())); } + #[test] + fn caller_traps_when_no_account_id() { + let mut ext = MockExt::default(); + ext.caller = Origin::Root; + assert_eq!( + execute(CODE_CALLER, vec![], ext), + Err(ExecError { error: DispatchError::RootNotAllowed, origin: ErrorOrigin::Caller }) + ); + } + /// calls `seal_address` and compares the result with the constant (BOB's address part). const CODE_ADDRESS: &str = r#" (module @@ -2977,6 +2992,45 @@ mod tests { assert_eq!(output, ExecReturnValue { flags: ReturnFlags::empty(), data: 0u32.encode() },); } + #[test] + fn caller_is_root_works() { + const CODE_CALLER_IS_ROOT: &str = r#" +;; This runs `caller_is_root` check on zero account address +(module + (import "seal0" "caller_is_root" (func $caller_is_root (result i32))) + (import "seal0" "seal_return" (func $seal_return (param i32 i32 i32))) + (import "env" "memory" (memory 1 1)) + + ;; [0, 4) here the return code of the `caller_is_root` will be stored + ;; we initialize it with non-zero value to be sure that it's being overwritten below + (data (i32.const 0) "\10\10\10\10") + + (func (export "deploy")) + + (func (export "call") + (i32.store + (i32.const 0) + (call $caller_is_root) + ) + ;; exit with success and take `caller_is_root` return code to the output buffer + (call $seal_return (i32.const 0) (i32.const 0) (i32.const 4)) + ) +) +"#; + // The default `caller` is ALICE. Therefore not root. + let output = execute(CODE_CALLER_IS_ROOT, vec![], MockExt::default()).unwrap(); + assert_eq!(output, ExecReturnValue { flags: ReturnFlags::empty(), data: 0u32.encode() },); + + // The caller is forced to be root instead of using the default ALICE. + let output = execute( + CODE_CALLER_IS_ROOT, + vec![], + MockExt { caller: Origin::Root, ..MockExt::default() }, + ) + .unwrap(); + assert_eq!(output, ExecReturnValue { flags: ReturnFlags::empty(), data: 1u32.encode() },); + } + #[test] fn set_code_hash() { const CODE: &str = r#" diff --git a/frame/contracts/src/wasm/runtime.rs b/frame/contracts/src/wasm/runtime.rs index 92ae2e3e6..330bf19d6 100644 --- a/frame/contracts/src/wasm/runtime.rs +++ b/frame/contracts/src/wasm/runtime.rs @@ -191,6 +191,8 @@ pub enum RuntimeCosts { OwnCodeHash, /// Weight of calling `seal_caller_is_origin`. CallerIsOrigin, + /// Weight of calling `caller_is_root`. + CallerIsRoot, /// Weight of calling `seal_address`. Address, /// Weight of calling `seal_gas_left`. @@ -283,6 +285,7 @@ impl RuntimeCosts { CodeHash => s.code_hash, OwnCodeHash => s.own_code_hash, CallerIsOrigin => s.caller_is_origin, + CallerIsRoot => s.caller_is_root, Address => s.address, GasLeft => s.gas_left, Balance => s.balance, @@ -1750,14 +1753,18 @@ pub mod env { /// If this is a top-level call (i.e. initiated by an extrinsic) the origin address of the /// extrinsic will be returned. Otherwise, if this call is initiated by another contract then /// the address of the contract will be returned. The value is encoded as T::AccountId. + /// + /// If there is no address associated with the caller (e.g. because the caller is root) then + /// it traps with `BadOrigin`. #[prefixed_alias] fn caller(ctx: _, memory: _, out_ptr: u32, out_len_ptr: u32) -> Result<(), TrapReason> { ctx.charge_gas(RuntimeCosts::Caller)?; + let caller = ctx.ext.caller().account_id()?.clone(); Ok(ctx.write_sandbox_output( memory, out_ptr, out_len_ptr, - &ctx.ext.caller().encode(), + &caller.encode(), false, already_charged, )?) @@ -1856,6 +1863,21 @@ pub mod env { Ok(ctx.ext.caller_is_origin() as u32) } + /// Checks whether the caller of the current contract is root. + /// + /// Note that only the origin of the call stack can be root. Hence this function returning + /// `true` implies that the contract is being called by the origin. + /// + /// A return value of `true` indicates that this contract is being called by a root origin, + /// and `false` indicates that the caller is a signed origin. + /// + /// Returned value is a `u32`-encoded boolean: (`0 = false`, `1 = true`). + #[unstable] + fn caller_is_root(ctx: _, _memory: _) -> Result { + ctx.charge_gas(RuntimeCosts::CallerIsRoot)?; + Ok(ctx.ext.caller_is_root() as u32) + } + /// Stores the address of the current contract into the supplied buffer. /// /// The value is stored to linear memory at the address pointed to by `out_ptr`. diff --git a/frame/contracts/src/weights.rs b/frame/contracts/src/weights.rs index cdaba7f59..0a7f3ddf1 100644 --- a/frame/contracts/src/weights.rs +++ b/frame/contracts/src/weights.rs @@ -18,30 +18,32 @@ //! Autogenerated weights for pallet_contracts //! //! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev -//! DATE: 2023-04-12, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! DATE: 2023-04-27, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` //! WORST CASE MAP SIZE: `1000000` -//! HOSTNAME: `bm2`, CPU: `Intel(R) Core(TM) i7-7700K CPU @ 4.20GHz` +//! HOSTNAME: `bm3`, CPU: `Intel(R) Core(TM) i7-7700K CPU @ 4.20GHz` //! EXECUTION: Some(Wasm), WASM-EXECUTION: Compiled, CHAIN: Some("dev"), DB CACHE: 1024 // Executed Command: -// ./target/production/substrate +// target/production/substrate // benchmark // pallet -// --chain=dev // --steps=50 // --repeat=20 -// --pallet=pallet_contracts // --extrinsic=* // --execution=wasm // --wasm-execution=compiled // --heap-pages=4096 -// --output=./frame/contracts/src/weights.rs +// --json-file=/var/lib/gitlab-runner/builds/zyw4fam_/0/parity/mirrors/substrate/.git/.artifacts/bench.json +// --pallet=pallet_contracts +// --chain=dev // --header=./HEADER-APACHE2 +// --output=./frame/contracts/src/weights.rs // --template=./.maintain/frame-weight-template.hbs #![cfg_attr(rustfmt, rustfmt_skip)] #![allow(unused_parens)] #![allow(unused_imports)] +#![allow(missing_docs)] use frame_support::{traits::Get, weights::{Weight, constants::RocksDbWeight}}; use core::marker::PhantomData; @@ -63,6 +65,7 @@ pub trait WeightInfo { fn seal_code_hash(r: u32, ) -> Weight; fn seal_own_code_hash(r: u32, ) -> Weight; fn seal_caller_is_origin(r: u32, ) -> Weight; + fn seal_caller_is_root(r: u32, ) -> Weight; fn seal_address(r: u32, ) -> Weight; fn seal_gas_left(r: u32, ) -> Weight; fn seal_balance(r: u32, ) -> Weight; @@ -177,8 +180,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `109` // Estimated: `1594` - // Minimum execution time: 2_565_000 picoseconds. - Weight::from_parts(2_759_000, 1594) + // Minimum execution time: 2_627_000 picoseconds. + Weight::from_parts(2_748_000, 1594) .saturating_add(T::DbWeight::get().reads(1_u64)) } /// Storage: Skipped Metadata (r:0 w:0) @@ -188,10 +191,10 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `488 + k * (69 ±0)` // Estimated: `478 + k * (70 ±0)` - // Minimum execution time: 13_480_000 picoseconds. - Weight::from_parts(10_153_869, 478) - // Standard Error: 427 - .saturating_add(Weight::from_parts(958_726, 0).saturating_mul(k.into())) + // Minimum execution time: 13_607_000 picoseconds. + Weight::from_parts(8_026_118, 478) + // Standard Error: 1_323 + .saturating_add(Weight::from_parts(980_583, 0).saturating_mul(k.into())) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().reads((1_u64).saturating_mul(k.into()))) .saturating_add(T::DbWeight::get().writes(2_u64)) @@ -207,10 +210,10 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `238 + c * (1 ±0)` // Estimated: `3708 + c * (1 ±0)` - // Minimum execution time: 30_406_000 picoseconds. - Weight::from_parts(28_467_370, 3708) - // Standard Error: 46 - .saturating_add(Weight::from_parts(53_724, 0).saturating_mul(c.into())) + // Minimum execution time: 30_563_000 picoseconds. + Weight::from_parts(22_292_544, 3708) + // Standard Error: 60 + .saturating_add(Weight::from_parts(54_541, 0).saturating_mul(c.into())) .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) .saturating_add(Weight::from_parts(0, 1).saturating_mul(c.into())) @@ -230,10 +233,10 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `707` // Estimated: `6656 + c * (1 ±0)` - // Minimum execution time: 263_198_000 picoseconds. - Weight::from_parts(276_162_279, 6656) - // Standard Error: 18 - .saturating_add(Weight::from_parts(37_378, 0).saturating_mul(c.into())) + // Minimum execution time: 268_884_000 picoseconds. + Weight::from_parts(277_799_331, 6656) + // Standard Error: 23 + .saturating_add(Weight::from_parts(37_876, 0).saturating_mul(c.into())) .saturating_add(T::DbWeight::get().reads(6_u64)) .saturating_add(T::DbWeight::get().writes(4_u64)) .saturating_add(Weight::from_parts(0, 1).saturating_mul(c.into())) @@ -261,14 +264,14 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `270` // Estimated: `8659` - // Minimum execution time: 3_132_259_000 picoseconds. - Weight::from_parts(513_284_834, 8659) - // Standard Error: 280 - .saturating_add(Weight::from_parts(106_723, 0).saturating_mul(c.into())) - // Standard Error: 16 - .saturating_add(Weight::from_parts(1_166, 0).saturating_mul(i.into())) - // Standard Error: 16 - .saturating_add(Weight::from_parts(1_436, 0).saturating_mul(s.into())) + // Minimum execution time: 3_159_921_000 picoseconds. + Weight::from_parts(594_826_134, 8659) + // Standard Error: 290 + .saturating_add(Weight::from_parts(106_471, 0).saturating_mul(c.into())) + // Standard Error: 17 + .saturating_add(Weight::from_parts(1_160, 0).saturating_mul(i.into())) + // Standard Error: 17 + .saturating_add(Weight::from_parts(1_417, 0).saturating_mul(s.into())) .saturating_add(T::DbWeight::get().reads(9_u64)) .saturating_add(T::DbWeight::get().writes(10_u64)) } @@ -292,12 +295,12 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `482` // Estimated: `6408` - // Minimum execution time: 1_646_604_000 picoseconds. - Weight::from_parts(271_369_256, 6408) - // Standard Error: 7 - .saturating_add(Weight::from_parts(1_426, 0).saturating_mul(i.into())) - // Standard Error: 7 - .saturating_add(Weight::from_parts(1_438, 0).saturating_mul(s.into())) + // Minimum execution time: 1_653_811_000 picoseconds. + Weight::from_parts(296_038_081, 6408) + // Standard Error: 9 + .saturating_add(Weight::from_parts(1_461, 0).saturating_mul(i.into())) + // Standard Error: 9 + .saturating_add(Weight::from_parts(1_430, 0).saturating_mul(s.into())) .saturating_add(T::DbWeight::get().reads(9_u64)) .saturating_add(T::DbWeight::get().writes(7_u64)) } @@ -315,8 +318,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `759` // Estimated: `6699` - // Minimum execution time: 191_360_000 picoseconds. - Weight::from_parts(192_625_000, 6699) + // Minimum execution time: 195_916_000 picoseconds. + Weight::from_parts(196_706_000, 6699) .saturating_add(T::DbWeight::get().reads(6_u64)) .saturating_add(T::DbWeight::get().writes(4_u64)) } @@ -333,10 +336,10 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `109` // Estimated: `3574` - // Minimum execution time: 245_207_000 picoseconds. - Weight::from_parts(244_703_457, 3574) - // Standard Error: 61 - .saturating_add(Weight::from_parts(106_850, 0).saturating_mul(c.into())) + // Minimum execution time: 251_137_000 picoseconds. + Weight::from_parts(252_985_435, 3574) + // Standard Error: 88 + .saturating_add(Weight::from_parts(108_141, 0).saturating_mul(c.into())) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().writes(4_u64)) } @@ -352,8 +355,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `255` // Estimated: `3720` - // Minimum execution time: 33_560_000 picoseconds. - Weight::from_parts(33_833_000, 3720) + // Minimum execution time: 33_521_000 picoseconds. + Weight::from_parts(34_039_000, 3720) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().writes(4_u64)) } @@ -367,8 +370,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `570` // Estimated: `8985` - // Minimum execution time: 33_288_000 picoseconds. - Weight::from_parts(33_775_000, 8985) + // Minimum execution time: 33_477_000 picoseconds. + Weight::from_parts(33_890_000, 8985) .saturating_add(T::DbWeight::get().reads(6_u64)) .saturating_add(T::DbWeight::get().writes(6_u64)) } @@ -387,10 +390,10 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `781 + r * (6 ±0)` // Estimated: `6722 + r * (6 ±0)` - // Minimum execution time: 234_292_000 picoseconds. - Weight::from_parts(235_941_911, 6722) - // Standard Error: 413 - .saturating_add(Weight::from_parts(339_913, 0).saturating_mul(r.into())) + // Minimum execution time: 239_374_000 picoseconds. + Weight::from_parts(246_017_099, 6722) + // Standard Error: 539 + .saturating_add(Weight::from_parts(323_826, 0).saturating_mul(r.into())) .saturating_add(T::DbWeight::get().reads(6_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) .saturating_add(Weight::from_parts(0, 6).saturating_mul(r.into())) @@ -410,10 +413,10 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `839 + r * (240 ±0)` // Estimated: `6743 + r * (2715 ±0)` - // Minimum execution time: 236_273_000 picoseconds. - Weight::from_parts(74_380_906, 6743) - // Standard Error: 5_745 - .saturating_add(Weight::from_parts(3_331_781, 0).saturating_mul(r.into())) + // Minimum execution time: 240_656_000 picoseconds. + Weight::from_parts(87_361_934, 6743) + // Standard Error: 5_912 + .saturating_add(Weight::from_parts(3_329_840, 0).saturating_mul(r.into())) .saturating_add(T::DbWeight::get().reads(6_u64)) .saturating_add(T::DbWeight::get().reads((1_u64).saturating_mul(r.into()))) .saturating_add(T::DbWeight::get().writes(3_u64)) @@ -434,10 +437,10 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `831 + r * (244 ±0)` // Estimated: `6747 + r * (2719 ±0)` - // Minimum execution time: 236_573_000 picoseconds. - Weight::from_parts(82_473_906, 6747) - // Standard Error: 5_510 - .saturating_add(Weight::from_parts(4_131_820, 0).saturating_mul(r.into())) + // Minimum execution time: 243_026_000 picoseconds. + Weight::from_parts(76_953_007, 6747) + // Standard Error: 6_640 + .saturating_add(Weight::from_parts(4_132_521, 0).saturating_mul(r.into())) .saturating_add(T::DbWeight::get().reads(6_u64)) .saturating_add(T::DbWeight::get().reads((1_u64).saturating_mul(r.into()))) .saturating_add(T::DbWeight::get().writes(3_u64)) @@ -458,10 +461,10 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `788 + r * (6 ±0)` // Estimated: `6730 + r * (6 ±0)` - // Minimum execution time: 235_878_000 picoseconds. - Weight::from_parts(238_387_359, 6730) - // Standard Error: 318 - .saturating_add(Weight::from_parts(409_923, 0).saturating_mul(r.into())) + // Minimum execution time: 242_736_000 picoseconds. + Weight::from_parts(243_136_007, 6730) + // Standard Error: 912 + .saturating_add(Weight::from_parts(414_717, 0).saturating_mul(r.into())) .saturating_add(T::DbWeight::get().reads(6_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) .saturating_add(Weight::from_parts(0, 6).saturating_mul(r.into())) @@ -481,14 +484,35 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `778 + r * (3 ±0)` // Estimated: `6723 + r * (3 ±0)` - // Minimum execution time: 233_476_000 picoseconds. - Weight::from_parts(238_014_452, 6723) - // Standard Error: 145 - .saturating_add(Weight::from_parts(165_823, 0).saturating_mul(r.into())) + // Minimum execution time: 240_130_000 picoseconds. + Weight::from_parts(244_517_187, 6723) + // Standard Error: 384 + .saturating_add(Weight::from_parts(167_431, 0).saturating_mul(r.into())) .saturating_add(T::DbWeight::get().reads(6_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) .saturating_add(Weight::from_parts(0, 3).saturating_mul(r.into())) } + /// Storage: Contracts ContractInfoOf (r:1 w:1) + /// Proof: Contracts ContractInfoOf (max_values: None, max_size: Some(290), added: 2765, mode: Measured) + /// Storage: Contracts CodeStorage (r:1 w:0) + /// Proof: Contracts CodeStorage (max_values: None, max_size: Some(126001), added: 128476, mode: Measured) + /// Storage: Timestamp Now (r:1 w:0) + /// Proof: Timestamp Now (max_values: Some(1), max_size: Some(8), added: 503, mode: Measured) + /// Storage: System EventTopics (r:2 w:2) + /// Proof Skipped: System EventTopics (max_values: None, max_size: None, mode: Measured) + /// The range of component `r` is `[0, 1600]`. + fn seal_caller_is_root(r: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `668 + r * (3 ±0)` + // Estimated: `6608 + r * (3 ±0)` + // Minimum execution time: 228_022_000 picoseconds. + Weight::from_parts(232_385_198, 6608) + // Standard Error: 300 + .saturating_add(Weight::from_parts(145_143, 0).saturating_mul(r.into())) + .saturating_add(T::DbWeight::get().reads(5_u64)) + .saturating_add(T::DbWeight::get().writes(3_u64)) + .saturating_add(Weight::from_parts(0, 3).saturating_mul(r.into())) + } /// Storage: System Account (r:1 w:0) /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: Measured) /// Storage: Contracts ContractInfoOf (r:1 w:1) @@ -504,10 +528,10 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `782 + r * (6 ±0)` // Estimated: `6724 + r * (6 ±0)` - // Minimum execution time: 235_490_000 picoseconds. - Weight::from_parts(240_039_685, 6724) - // Standard Error: 330 - .saturating_add(Weight::from_parts(332_291, 0).saturating_mul(r.into())) + // Minimum execution time: 240_250_000 picoseconds. + Weight::from_parts(240_268_824, 6724) + // Standard Error: 945 + .saturating_add(Weight::from_parts(329_577, 0).saturating_mul(r.into())) .saturating_add(T::DbWeight::get().reads(6_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) .saturating_add(Weight::from_parts(0, 6).saturating_mul(r.into())) @@ -525,12 +549,12 @@ impl WeightInfo for SubstrateWeight { /// The range of component `r` is `[0, 1600]`. fn seal_gas_left(r: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `783 + r * (6 ±0)` - // Estimated: `6721 + r * (6 ±0)` - // Minimum execution time: 236_093_000 picoseconds. - Weight::from_parts(238_513_328, 6721) - // Standard Error: 206 - .saturating_add(Weight::from_parts(328_899, 0).saturating_mul(r.into())) + // Measured: `778 + r * (6 ±0)` + // Estimated: `6719 + r * (6 ±0)` + // Minimum execution time: 242_370_000 picoseconds. + Weight::from_parts(242_389_500, 6719) + // Standard Error: 712 + .saturating_add(Weight::from_parts(518_380, 0).saturating_mul(r.into())) .saturating_add(T::DbWeight::get().reads(6_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) .saturating_add(Weight::from_parts(0, 6).saturating_mul(r.into())) @@ -550,10 +574,10 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `922 + r * (6 ±0)` // Estimated: `6846 + r * (6 ±0)` - // Minimum execution time: 234_801_000 picoseconds. - Weight::from_parts(243_519_159, 6846) - // Standard Error: 1_367 - .saturating_add(Weight::from_parts(1_449_599, 0).saturating_mul(r.into())) + // Minimum execution time: 238_563_000 picoseconds. + Weight::from_parts(253_511_314, 6846) + // Standard Error: 1_571 + .saturating_add(Weight::from_parts(1_454_089, 0).saturating_mul(r.into())) .saturating_add(T::DbWeight::get().reads(7_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) .saturating_add(Weight::from_parts(0, 6).saturating_mul(r.into())) @@ -573,10 +597,10 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `792 + r * (6 ±0)` // Estimated: `6741 + r * (6 ±0)` - // Minimum execution time: 236_765_000 picoseconds. - Weight::from_parts(237_843_244, 6741) - // Standard Error: 308 - .saturating_add(Weight::from_parts(329_911, 0).saturating_mul(r.into())) + // Minimum execution time: 242_995_000 picoseconds. + Weight::from_parts(240_061_456, 6741) + // Standard Error: 2_650 + .saturating_add(Weight::from_parts(326_813, 0).saturating_mul(r.into())) .saturating_add(T::DbWeight::get().reads(6_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) .saturating_add(Weight::from_parts(0, 6).saturating_mul(r.into())) @@ -596,10 +620,10 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `790 + r * (6 ±0)` // Estimated: `6739 + r * (6 ±0)` - // Minimum execution time: 236_690_000 picoseconds. - Weight::from_parts(241_743_164, 6739) - // Standard Error: 333 - .saturating_add(Weight::from_parts(324_693, 0).saturating_mul(r.into())) + // Minimum execution time: 241_342_000 picoseconds. + Weight::from_parts(240_875_314, 6739) + // Standard Error: 669 + .saturating_add(Weight::from_parts(324_519, 0).saturating_mul(r.into())) .saturating_add(T::DbWeight::get().reads(6_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) .saturating_add(Weight::from_parts(0, 6).saturating_mul(r.into())) @@ -619,10 +643,10 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `787 + r * (6 ±0)` // Estimated: `6737 + r * (6 ±0)` - // Minimum execution time: 236_149_000 picoseconds. - Weight::from_parts(239_090_707, 6737) - // Standard Error: 246 - .saturating_add(Weight::from_parts(344_488, 0).saturating_mul(r.into())) + // Minimum execution time: 238_954_000 picoseconds. + Weight::from_parts(242_269_896, 6737) + // Standard Error: 1_453 + .saturating_add(Weight::from_parts(317_998, 0).saturating_mul(r.into())) .saturating_add(T::DbWeight::get().reads(6_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) .saturating_add(Weight::from_parts(0, 6).saturating_mul(r.into())) @@ -642,10 +666,10 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `778 + r * (6 ±0)` // Estimated: `6723 + r * (6 ±0)` - // Minimum execution time: 235_057_000 picoseconds. - Weight::from_parts(237_752_870, 6723) - // Standard Error: 236 - .saturating_add(Weight::from_parts(328_235, 0).saturating_mul(r.into())) + // Minimum execution time: 240_935_000 picoseconds. + Weight::from_parts(242_938_271, 6723) + // Standard Error: 792 + .saturating_add(Weight::from_parts(316_782, 0).saturating_mul(r.into())) .saturating_add(T::DbWeight::get().reads(6_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) .saturating_add(Weight::from_parts(0, 6).saturating_mul(r.into())) @@ -665,15 +689,15 @@ impl WeightInfo for SubstrateWeight { /// The range of component `r` is `[0, 1600]`. fn seal_weight_to_fee(r: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `856 + r * (10 ±0)` - // Estimated: `6796 + r * (10 ±0)` - // Minimum execution time: 234_995_000 picoseconds. - Weight::from_parts(246_473_554, 6796) - // Standard Error: 1_015 - .saturating_add(Weight::from_parts(1_337_653, 0).saturating_mul(r.into())) + // Measured: `852 + r * (14 ±0)` + // Estimated: `6785 + r * (14 ±0)` + // Minimum execution time: 240_142_000 picoseconds. + Weight::from_parts(241_386_730, 6785) + // Standard Error: 2_116 + .saturating_add(Weight::from_parts(1_387_202, 0).saturating_mul(r.into())) .saturating_add(T::DbWeight::get().reads(7_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) - .saturating_add(Weight::from_parts(0, 10).saturating_mul(r.into())) + .saturating_add(Weight::from_parts(0, 14).saturating_mul(r.into())) } /// Storage: System Account (r:1 w:0) /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: Measured) @@ -690,10 +714,10 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `745 + r * (4 ±0)` // Estimated: `6687 + r * (4 ±0)` - // Minimum execution time: 160_445_000 picoseconds. - Weight::from_parts(165_558_135, 6687) - // Standard Error: 234 - .saturating_add(Weight::from_parts(133_607, 0).saturating_mul(r.into())) + // Minimum execution time: 165_617_000 picoseconds. + Weight::from_parts(170_794_127, 6687) + // Standard Error: 209 + .saturating_add(Weight::from_parts(127_931, 0).saturating_mul(r.into())) .saturating_add(T::DbWeight::get().reads(6_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) .saturating_add(Weight::from_parts(0, 4).saturating_mul(r.into())) @@ -713,10 +737,10 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `780 + r * (6 ±0)` // Estimated: `6724 + r * (6 ±0)` - // Minimum execution time: 235_065_000 picoseconds. - Weight::from_parts(237_797_177, 6724) - // Standard Error: 336 - .saturating_add(Weight::from_parts(267_302, 0).saturating_mul(r.into())) + // Minimum execution time: 238_832_000 picoseconds. + Weight::from_parts(237_110_694, 6724) + // Standard Error: 539 + .saturating_add(Weight::from_parts(280_610, 0).saturating_mul(r.into())) .saturating_add(T::DbWeight::get().reads(6_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) .saturating_add(Weight::from_parts(0, 6).saturating_mul(r.into())) @@ -736,10 +760,10 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `784` // Estimated: `6724` - // Minimum execution time: 236_215_000 picoseconds. - Weight::from_parts(239_347_313, 6724) - // Standard Error: 0 - .saturating_add(Weight::from_parts(587, 0).saturating_mul(n.into())) + // Minimum execution time: 241_070_000 picoseconds. + Weight::from_parts(242_162_279, 6724) + // Standard Error: 1 + .saturating_add(Weight::from_parts(595, 0).saturating_mul(n.into())) .saturating_add(T::DbWeight::get().reads(6_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) } @@ -758,10 +782,10 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `768 + r * (45 ±0)` // Estimated: `6708 + r * (45 ±0)` - // Minimum execution time: 231_571_000 picoseconds. - Weight::from_parts(233_477_918, 6708) - // Standard Error: 95_776 - .saturating_add(Weight::from_parts(1_733_181, 0).saturating_mul(r.into())) + // Minimum execution time: 236_337_000 picoseconds. + Weight::from_parts(238_883_828, 6708) + // Standard Error: 188_978 + .saturating_add(Weight::from_parts(926_671, 0).saturating_mul(r.into())) .saturating_add(T::DbWeight::get().reads(6_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) .saturating_add(Weight::from_parts(0, 45).saturating_mul(r.into())) @@ -781,10 +805,10 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `778` // Estimated: `6731` - // Minimum execution time: 234_956_000 picoseconds. - Weight::from_parts(236_785_051, 6731) + // Minimum execution time: 239_103_000 picoseconds. + Weight::from_parts(240_382_910, 6731) // Standard Error: 0 - .saturating_add(Weight::from_parts(177, 0).saturating_mul(n.into())) + .saturating_add(Weight::from_parts(181, 0).saturating_mul(n.into())) .saturating_add(T::DbWeight::get().reads(6_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) } @@ -809,10 +833,10 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `810 + r * (356 ±0)` // Estimated: `6750 + r * (7781 ±0)` - // Minimum execution time: 234_275_000 picoseconds. - Weight::from_parts(236_776_769, 6750) - // Standard Error: 137_203 - .saturating_add(Weight::from_parts(110_758_930, 0).saturating_mul(r.into())) + // Minimum execution time: 238_739_000 picoseconds. + Weight::from_parts(241_041_330, 6750) + // Standard Error: 176_820 + .saturating_add(Weight::from_parts(115_332_869, 0).saturating_mul(r.into())) .saturating_add(T::DbWeight::get().reads(6_u64)) .saturating_add(T::DbWeight::get().reads((6_u64).saturating_mul(r.into()))) .saturating_add(T::DbWeight::get().writes(3_u64)) @@ -836,10 +860,10 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `825 + r * (10 ±0)` // Estimated: `6769 + r * (10 ±0)` - // Minimum execution time: 235_593_000 picoseconds. - Weight::from_parts(253_731_242, 6769) - // Standard Error: 2_129 - .saturating_add(Weight::from_parts(1_771_297, 0).saturating_mul(r.into())) + // Minimum execution time: 240_888_000 picoseconds. + Weight::from_parts(259_901_113, 6769) + // Standard Error: 5_935 + .saturating_add(Weight::from_parts(1_764_269, 0).saturating_mul(r.into())) .saturating_add(T::DbWeight::get().reads(7_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) .saturating_add(Weight::from_parts(0, 10).saturating_mul(r.into())) @@ -859,10 +883,10 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `778 + r * (10 ±0)` // Estimated: `6723 + r * (10 ±0)` - // Minimum execution time: 232_124_000 picoseconds. - Weight::from_parts(245_904_447, 6723) - // Standard Error: 2_185 - .saturating_add(Weight::from_parts(3_470_410, 0).saturating_mul(r.into())) + // Minimum execution time: 237_478_000 picoseconds. + Weight::from_parts(264_915_436, 6723) + // Standard Error: 4_644 + .saturating_add(Weight::from_parts(3_452_918, 0).saturating_mul(r.into())) .saturating_add(T::DbWeight::get().reads(6_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) .saturating_add(Weight::from_parts(0, 10).saturating_mul(r.into())) @@ -883,12 +907,12 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `797 + t * (32 ±0)` // Estimated: `6744 + t * (2508 ±0)` - // Minimum execution time: 250_301_000 picoseconds. - Weight::from_parts(245_292_258, 6744) - // Standard Error: 29_864 - .saturating_add(Weight::from_parts(2_163_531, 0).saturating_mul(t.into())) - // Standard Error: 8 - .saturating_add(Weight::from_parts(583, 0).saturating_mul(n.into())) + // Minimum execution time: 255_720_000 picoseconds. + Weight::from_parts(247_945_758, 6744) + // Standard Error: 73_390 + .saturating_add(Weight::from_parts(2_483_239, 0).saturating_mul(t.into())) + // Standard Error: 20 + .saturating_add(Weight::from_parts(756, 0).saturating_mul(n.into())) .saturating_add(T::DbWeight::get().reads(6_u64)) .saturating_add(T::DbWeight::get().reads((1_u64).saturating_mul(t.into()))) .saturating_add(T::DbWeight::get().writes(3_u64)) @@ -910,10 +934,10 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `777 + r * (7 ±0)` // Estimated: `6721 + r * (7 ±0)` - // Minimum execution time: 165_711_000 picoseconds. - Weight::from_parts(168_792_571, 6721) - // Standard Error: 216 - .saturating_add(Weight::from_parts(230_285, 0).saturating_mul(r.into())) + // Minimum execution time: 172_214_000 picoseconds. + Weight::from_parts(177_306_567, 6721) + // Standard Error: 839 + .saturating_add(Weight::from_parts(230_558, 0).saturating_mul(r.into())) .saturating_add(T::DbWeight::get().reads(6_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) .saturating_add(Weight::from_parts(0, 7).saturating_mul(r.into())) @@ -933,10 +957,10 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `125728` // Estimated: `131670` - // Minimum execution time: 348_928_000 picoseconds. - Weight::from_parts(352_224_793, 131670) - // Standard Error: 0 - .saturating_add(Weight::from_parts(731, 0).saturating_mul(i.into())) + // Minimum execution time: 354_105_000 picoseconds. + Weight::from_parts(360_649_854, 131670) + // Standard Error: 2 + .saturating_add(Weight::from_parts(737, 0).saturating_mul(i.into())) .saturating_add(T::DbWeight::get().reads(6_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) } @@ -947,10 +971,10 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `845 + r * (292 ±0)` // Estimated: `843 + r * (293 ±0)` - // Minimum execution time: 236_418_000 picoseconds. - Weight::from_parts(129_862_840, 843) - // Standard Error: 9_733 - .saturating_add(Weight::from_parts(6_005_187, 0).saturating_mul(r.into())) + // Minimum execution time: 239_637_000 picoseconds. + Weight::from_parts(136_431_436, 843) + // Standard Error: 10_238 + .saturating_add(Weight::from_parts(6_070_221, 0).saturating_mul(r.into())) .saturating_add(T::DbWeight::get().reads(6_u64)) .saturating_add(T::DbWeight::get().reads((1_u64).saturating_mul(r.into()))) .saturating_add(T::DbWeight::get().writes(3_u64)) @@ -964,10 +988,10 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `1304` // Estimated: `1280` - // Minimum execution time: 251_599_000 picoseconds. - Weight::from_parts(285_284_665, 1280) - // Standard Error: 46 - .saturating_add(Weight::from_parts(410, 0).saturating_mul(n.into())) + // Minimum execution time: 256_198_000 picoseconds. + Weight::from_parts(289_972_802, 1280) + // Standard Error: 54 + .saturating_add(Weight::from_parts(438, 0).saturating_mul(n.into())) .saturating_add(T::DbWeight::get().reads(8_u64)) .saturating_add(T::DbWeight::get().writes(6_u64)) } @@ -978,10 +1002,10 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `1167 + n * (1 ±0)` // Estimated: `1167 + n * (1 ±0)` - // Minimum execution time: 251_309_000 picoseconds. - Weight::from_parts(253_555_552, 1167) - // Standard Error: 9 - .saturating_add(Weight::from_parts(27, 0).saturating_mul(n.into())) + // Minimum execution time: 255_519_000 picoseconds. + Weight::from_parts(257_668_217, 1167) + // Standard Error: 19 + .saturating_add(Weight::from_parts(105, 0).saturating_mul(n.into())) .saturating_add(T::DbWeight::get().reads(7_u64)) .saturating_add(T::DbWeight::get().writes(4_u64)) .saturating_add(Weight::from_parts(0, 1).saturating_mul(n.into())) @@ -993,10 +1017,10 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `841 + r * (288 ±0)` // Estimated: `845 + r * (289 ±0)` - // Minimum execution time: 235_441_000 picoseconds. - Weight::from_parts(132_980_942, 845) - // Standard Error: 9_421 - .saturating_add(Weight::from_parts(5_854_896, 0).saturating_mul(r.into())) + // Minimum execution time: 239_461_000 picoseconds. + Weight::from_parts(131_630_528, 845) + // Standard Error: 10_483 + .saturating_add(Weight::from_parts(5_910_066, 0).saturating_mul(r.into())) .saturating_add(T::DbWeight::get().reads(6_u64)) .saturating_add(T::DbWeight::get().reads((1_u64).saturating_mul(r.into()))) .saturating_add(T::DbWeight::get().writes(3_u64)) @@ -1010,10 +1034,10 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `1163 + n * (1 ±0)` // Estimated: `1163 + n * (1 ±0)` - // Minimum execution time: 249_967_000 picoseconds. - Weight::from_parts(252_122_186, 1163) - // Standard Error: 10 - .saturating_add(Weight::from_parts(74, 0).saturating_mul(n.into())) + // Minimum execution time: 254_904_000 picoseconds. + Weight::from_parts(261_213_399, 1163) + // Standard Error: 178 + .saturating_add(Weight::from_parts(125, 0).saturating_mul(n.into())) .saturating_add(T::DbWeight::get().reads(7_u64)) .saturating_add(T::DbWeight::get().writes(4_u64)) .saturating_add(Weight::from_parts(0, 1).saturating_mul(n.into())) @@ -1025,10 +1049,10 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `835 + r * (296 ±0)` // Estimated: `840 + r * (297 ±0)` - // Minimum execution time: 235_647_000 picoseconds. - Weight::from_parts(145_525_169, 840) - // Standard Error: 8_553 - .saturating_add(Weight::from_parts(4_948_021, 0).saturating_mul(r.into())) + // Minimum execution time: 239_995_000 picoseconds. + Weight::from_parts(151_326_508, 840) + // Standard Error: 8_960 + .saturating_add(Weight::from_parts(4_937_728, 0).saturating_mul(r.into())) .saturating_add(T::DbWeight::get().reads(6_u64)) .saturating_add(T::DbWeight::get().reads((1_u64).saturating_mul(r.into()))) .saturating_add(T::DbWeight::get().writes(3_u64)) @@ -1041,10 +1065,10 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `1179 + n * (1 ±0)` // Estimated: `1179 + n * (1 ±0)` - // Minimum execution time: 249_576_000 picoseconds. - Weight::from_parts(250_747_191, 1179) - // Standard Error: 8 - .saturating_add(Weight::from_parts(717, 0).saturating_mul(n.into())) + // Minimum execution time: 254_515_000 picoseconds. + Weight::from_parts(256_728_817, 1179) + // Standard Error: 22 + .saturating_add(Weight::from_parts(706, 0).saturating_mul(n.into())) .saturating_add(T::DbWeight::get().reads(7_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) .saturating_add(Weight::from_parts(0, 1).saturating_mul(n.into())) @@ -1056,10 +1080,10 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `856 + r * (288 ±0)` // Estimated: `857 + r * (289 ±0)` - // Minimum execution time: 236_110_000 picoseconds. - Weight::from_parts(148_420_625, 857) - // Standard Error: 8_175 - .saturating_add(Weight::from_parts(4_684_126, 0).saturating_mul(r.into())) + // Minimum execution time: 240_601_000 picoseconds. + Weight::from_parts(154_476_561, 857) + // Standard Error: 8_872 + .saturating_add(Weight::from_parts(4_805_043, 0).saturating_mul(r.into())) .saturating_add(T::DbWeight::get().reads(6_u64)) .saturating_add(T::DbWeight::get().reads((1_u64).saturating_mul(r.into()))) .saturating_add(T::DbWeight::get().writes(3_u64)) @@ -1072,10 +1096,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `1166 + n * (1 ±0)` // Estimated: `1166 + n * (1 ±0)` - // Minimum execution time: 247_800_000 picoseconds. - Weight::from_parts(249_410_575, 1166) - // Standard Error: 6 - .saturating_add(Weight::from_parts(99, 0).saturating_mul(n.into())) + // Minimum execution time: 253_654_000 picoseconds. + Weight::from_parts(257_288_586, 1166) .saturating_add(T::DbWeight::get().reads(7_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) .saturating_add(Weight::from_parts(0, 1).saturating_mul(n.into())) @@ -1087,10 +1109,10 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `829 + r * (296 ±0)` // Estimated: `836 + r * (297 ±0)` - // Minimum execution time: 235_251_000 picoseconds. - Weight::from_parts(128_816_707, 836) - // Standard Error: 9_887 - .saturating_add(Weight::from_parts(6_167_176, 0).saturating_mul(r.into())) + // Minimum execution time: 239_869_000 picoseconds. + Weight::from_parts(135_258_204, 836) + // Standard Error: 10_378 + .saturating_add(Weight::from_parts(6_144_770, 0).saturating_mul(r.into())) .saturating_add(T::DbWeight::get().reads(6_u64)) .saturating_add(T::DbWeight::get().reads((1_u64).saturating_mul(r.into()))) .saturating_add(T::DbWeight::get().writes(3_u64)) @@ -1104,10 +1126,10 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `1180 + n * (1 ±0)` // Estimated: `1180 + n * (1 ±0)` - // Minimum execution time: 250_401_000 picoseconds. - Weight::from_parts(253_298_243, 1180) - // Standard Error: 9 - .saturating_add(Weight::from_parts(667, 0).saturating_mul(n.into())) + // Minimum execution time: 258_153_000 picoseconds. + Weight::from_parts(260_068_186, 1180) + // Standard Error: 25 + .saturating_add(Weight::from_parts(744, 0).saturating_mul(n.into())) .saturating_add(T::DbWeight::get().reads(7_u64)) .saturating_add(T::DbWeight::get().writes(4_u64)) .saturating_add(Weight::from_parts(0, 1).saturating_mul(n.into())) @@ -1127,10 +1149,10 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `1373 + r * (45 ±0)` // Estimated: `7270 + r * (2520 ±0)` - // Minimum execution time: 236_470_000 picoseconds. - Weight::from_parts(98_898_727, 7270) - // Standard Error: 33_316 - .saturating_add(Weight::from_parts(35_149_946, 0).saturating_mul(r.into())) + // Minimum execution time: 243_189_000 picoseconds. + Weight::from_parts(243_465_000, 7270) + // Standard Error: 30_961 + .saturating_add(Weight::from_parts(35_376_623, 0).saturating_mul(r.into())) .saturating_add(T::DbWeight::get().reads(7_u64)) .saturating_add(T::DbWeight::get().reads((1_u64).saturating_mul(r.into()))) .saturating_add(T::DbWeight::get().writes(4_u64)) @@ -1145,22 +1167,22 @@ impl WeightInfo for SubstrateWeight { /// Proof: Contracts CodeStorage (max_values: None, max_size: Some(126001), added: 128476, mode: Measured) /// Storage: Timestamp Now (r:1 w:0) /// Proof: Timestamp Now (max_values: Some(1), max_size: Some(8), added: 503, mode: Measured) - /// Storage: System EventTopics (r:802 w:802) + /// Storage: System EventTopics (r:803 w:803) /// Proof Skipped: System EventTopics (max_values: None, max_size: None, mode: Measured) /// The range of component `r` is `[0, 800]`. fn seal_call(r: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `1237 + r * (256 ±0)` - // Estimated: `7125 + r * (2732 ±0)` - // Minimum execution time: 238_303_000 picoseconds. - Weight::from_parts(239_024_000, 7125) - // Standard Error: 65_907 - .saturating_add(Weight::from_parts(209_419_071, 0).saturating_mul(r.into())) - .saturating_add(T::DbWeight::get().reads(7_u64)) + // Measured: `1140 + r * (276 ±0)` + // Estimated: `9332 + r * (2752 ±0)` + // Minimum execution time: 243_656_000 picoseconds. + Weight::from_parts(244_221_000, 9332) + // Standard Error: 69_762 + .saturating_add(Weight::from_parts(216_905_619, 0).saturating_mul(r.into())) + .saturating_add(T::DbWeight::get().reads(8_u64)) .saturating_add(T::DbWeight::get().reads((2_u64).saturating_mul(r.into()))) - .saturating_add(T::DbWeight::get().writes(3_u64)) + .saturating_add(T::DbWeight::get().writes(4_u64)) .saturating_add(T::DbWeight::get().writes((2_u64).saturating_mul(r.into()))) - .saturating_add(Weight::from_parts(0, 2732).saturating_mul(r.into())) + .saturating_add(Weight::from_parts(0, 2752).saturating_mul(r.into())) } /// Storage: System Account (r:1 w:0) /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: Measured) @@ -1177,10 +1199,10 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `0 + r * (502 ±0)` // Estimated: `6727 + r * (2572 ±10)` - // Minimum execution time: 235_961_000 picoseconds. - Weight::from_parts(236_939_000, 6727) - // Standard Error: 83_087 - .saturating_add(Weight::from_parts(205_646_517, 0).saturating_mul(r.into())) + // Minimum execution time: 242_632_000 picoseconds. + Weight::from_parts(243_068_000, 6727) + // Standard Error: 126_218 + .saturating_add(Weight::from_parts(213_096_291, 0).saturating_mul(r.into())) .saturating_add(T::DbWeight::get().reads(6_u64)) .saturating_add(T::DbWeight::get().reads((2_u64).saturating_mul(r.into()))) .saturating_add(T::DbWeight::get().writes(3_u64)) @@ -1195,23 +1217,23 @@ impl WeightInfo for SubstrateWeight { /// Proof: Contracts CodeStorage (max_values: None, max_size: Some(126001), added: 128476, mode: Measured) /// Storage: Timestamp Now (r:1 w:0) /// Proof: Timestamp Now (max_values: Some(1), max_size: Some(8), added: 503, mode: Measured) - /// Storage: System EventTopics (r:3 w:3) + /// Storage: System EventTopics (r:4 w:4) /// Proof Skipped: System EventTopics (max_values: None, max_size: None, mode: Measured) /// The range of component `t` is `[0, 1]`. /// The range of component `c` is `[0, 1048576]`. fn seal_call_per_transfer_clone_byte(t: u32, c: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `1154 + t * (204 ±0)` - // Estimated: `9569 + t * (5154 ±0)` - // Minimum execution time: 410_156_000 picoseconds. - Weight::from_parts(378_378_143, 9569) - // Standard Error: 285_172 - .saturating_add(Weight::from_parts(34_736_740, 0).saturating_mul(t.into())) - // Standard Error: 0 - .saturating_add(Weight::from_parts(591, 0).saturating_mul(c.into())) - .saturating_add(T::DbWeight::get().reads(9_u64)) + // Estimated: `12044 + t * (5154 ±0)` + // Minimum execution time: 421_691_000 picoseconds. + Weight::from_parts(394_587_369, 12044) + // Standard Error: 1_104_014 + .saturating_add(Weight::from_parts(30_461_758, 0).saturating_mul(t.into())) + // Standard Error: 1 + .saturating_add(Weight::from_parts(601, 0).saturating_mul(c.into())) + .saturating_add(T::DbWeight::get().reads(10_u64)) .saturating_add(T::DbWeight::get().reads((2_u64).saturating_mul(t.into()))) - .saturating_add(T::DbWeight::get().writes(5_u64)) + .saturating_add(T::DbWeight::get().writes(6_u64)) .saturating_add(T::DbWeight::get().writes((2_u64).saturating_mul(t.into()))) .saturating_add(Weight::from_parts(0, 5154).saturating_mul(t.into())) } @@ -1229,15 +1251,15 @@ impl WeightInfo for SubstrateWeight { /// Proof: Contracts OwnerInfoOf (max_values: None, max_size: Some(88), added: 2563, mode: Measured) /// Storage: System EventTopics (r:802 w:802) /// Proof Skipped: System EventTopics (max_values: None, max_size: None, mode: Measured) - /// The range of component `r` is `[0, 800]`. + /// The range of component `r` is `[1, 800]`. fn seal_instantiate(r: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `1301 + r * (254 ±0)` - // Estimated: `7131 + r * (5205 ±0)` - // Minimum execution time: 236_748_000 picoseconds. - Weight::from_parts(237_129_000, 7131) - // Standard Error: 280_059 - .saturating_add(Weight::from_parts(341_428_013, 0).saturating_mul(r.into())) + // Measured: `1322 + r * (254 ±0)` + // Estimated: `7146 + r * (5205 ±0)` + // Minimum execution time: 581_252_000 picoseconds. + Weight::from_parts(582_275_000, 7146) + // Standard Error: 279_771 + .saturating_add(Weight::from_parts(349_770_967, 0).saturating_mul(r.into())) .saturating_add(T::DbWeight::get().reads(8_u64)) .saturating_add(T::DbWeight::get().reads((6_u64).saturating_mul(r.into()))) .saturating_add(T::DbWeight::get().writes(6_u64)) @@ -1265,14 +1287,14 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `1071 + t * (187 ±0)` // Estimated: `9492 + t * (2634 ±2)` - // Minimum execution time: 1_613_796_000 picoseconds. - Weight::from_parts(340_002_206, 9492) - // Standard Error: 4_296_381 - .saturating_add(Weight::from_parts(115_239_834, 0).saturating_mul(t.into())) - // Standard Error: 6 - .saturating_add(Weight::from_parts(1_145, 0).saturating_mul(i.into())) - // Standard Error: 6 - .saturating_add(Weight::from_parts(1_315, 0).saturating_mul(s.into())) + // Minimum execution time: 1_623_241_000 picoseconds. + Weight::from_parts(317_076_173, 9492) + // Standard Error: 4_549_416 + .saturating_add(Weight::from_parts(125_360_446, 0).saturating_mul(t.into())) + // Standard Error: 7 + .saturating_add(Weight::from_parts(1_183, 0).saturating_mul(i.into())) + // Standard Error: 7 + .saturating_add(Weight::from_parts(1_352, 0).saturating_mul(s.into())) .saturating_add(T::DbWeight::get().reads(13_u64)) .saturating_add(T::DbWeight::get().reads((1_u64).saturating_mul(t.into()))) .saturating_add(T::DbWeight::get().writes(10_u64)) @@ -1294,10 +1316,10 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `777 + r * (8 ±0)` // Estimated: `6718 + r * (8 ±0)` - // Minimum execution time: 233_111_000 picoseconds. - Weight::from_parts(238_643_933, 6718) - // Standard Error: 184 - .saturating_add(Weight::from_parts(572_296, 0).saturating_mul(r.into())) + // Minimum execution time: 238_262_000 picoseconds. + Weight::from_parts(243_093_288, 6718) + // Standard Error: 870 + .saturating_add(Weight::from_parts(573_939, 0).saturating_mul(r.into())) .saturating_add(T::DbWeight::get().reads(6_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) .saturating_add(Weight::from_parts(0, 8).saturating_mul(r.into())) @@ -1317,10 +1339,10 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `785` // Estimated: `6725` - // Minimum execution time: 234_746_000 picoseconds. - Weight::from_parts(229_815_552, 6725) - // Standard Error: 1 - .saturating_add(Weight::from_parts(3_892, 0).saturating_mul(n.into())) + // Minimum execution time: 239_888_000 picoseconds. + Weight::from_parts(242_849_333, 6725) + // Standard Error: 3 + .saturating_add(Weight::from_parts(3_949, 0).saturating_mul(n.into())) .saturating_add(T::DbWeight::get().reads(6_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) } @@ -1339,10 +1361,10 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `779 + r * (8 ±0)` // Estimated: `6721 + r * (8 ±0)` - // Minimum execution time: 232_732_000 picoseconds. - Weight::from_parts(239_007_209, 6721) - // Standard Error: 256 - .saturating_add(Weight::from_parts(733_879, 0).saturating_mul(r.into())) + // Minimum execution time: 237_288_000 picoseconds. + Weight::from_parts(242_510_631, 6721) + // Standard Error: 977 + .saturating_add(Weight::from_parts(742_726, 0).saturating_mul(r.into())) .saturating_add(T::DbWeight::get().reads(6_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) .saturating_add(Weight::from_parts(0, 8).saturating_mul(r.into())) @@ -1362,10 +1384,10 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `787` // Estimated: `6729` - // Minimum execution time: 234_184_000 picoseconds. - Weight::from_parts(227_603_375, 6729) - // Standard Error: 1 - .saturating_add(Weight::from_parts(3_127, 0).saturating_mul(n.into())) + // Minimum execution time: 240_006_000 picoseconds. + Weight::from_parts(233_802_510, 6729) + // Standard Error: 2 + .saturating_add(Weight::from_parts(3_161, 0).saturating_mul(n.into())) .saturating_add(T::DbWeight::get().reads(6_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) } @@ -1384,10 +1406,10 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `779 + r * (8 ±0)` // Estimated: `6724 + r * (8 ±0)` - // Minimum execution time: 233_038_000 picoseconds. - Weight::from_parts(238_515_817, 6724) - // Standard Error: 255 - .saturating_add(Weight::from_parts(413_343, 0).saturating_mul(r.into())) + // Minimum execution time: 237_532_000 picoseconds. + Weight::from_parts(243_087_565, 6724) + // Standard Error: 656 + .saturating_add(Weight::from_parts(417_850, 0).saturating_mul(r.into())) .saturating_add(T::DbWeight::get().reads(6_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) .saturating_add(Weight::from_parts(0, 8).saturating_mul(r.into())) @@ -1407,10 +1429,10 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `787` // Estimated: `6733` - // Minimum execution time: 232_996_000 picoseconds. - Weight::from_parts(226_706_997, 6733) + // Minimum execution time: 241_429_000 picoseconds. + Weight::from_parts(233_528_258, 6733) // Standard Error: 1 - .saturating_add(Weight::from_parts(908, 0).saturating_mul(n.into())) + .saturating_add(Weight::from_parts(913, 0).saturating_mul(n.into())) .saturating_add(T::DbWeight::get().reads(6_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) } @@ -1429,10 +1451,10 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `779 + r * (8 ±0)` // Estimated: `6725 + r * (8 ±0)` - // Minimum execution time: 232_292_000 picoseconds. - Weight::from_parts(237_997_001, 6725) - // Standard Error: 219 - .saturating_add(Weight::from_parts(410_177, 0).saturating_mul(r.into())) + // Minimum execution time: 237_622_000 picoseconds. + Weight::from_parts(240_476_401, 6725) + // Standard Error: 795 + .saturating_add(Weight::from_parts(416_869, 0).saturating_mul(r.into())) .saturating_add(T::DbWeight::get().reads(6_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) .saturating_add(Weight::from_parts(0, 8).saturating_mul(r.into())) @@ -1452,10 +1474,10 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `787` // Estimated: `6727` - // Minimum execution time: 234_815_000 picoseconds. - Weight::from_parts(226_317_150, 6727) - // Standard Error: 1 - .saturating_add(Weight::from_parts(911, 0).saturating_mul(n.into())) + // Minimum execution time: 241_134_000 picoseconds. + Weight::from_parts(234_043_271, 6727) + // Standard Error: 3 + .saturating_add(Weight::from_parts(919, 0).saturating_mul(n.into())) .saturating_add(T::DbWeight::get().reads(6_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) } @@ -1473,11 +1495,11 @@ impl WeightInfo for SubstrateWeight { fn seal_sr25519_verify_per_byte(n: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `912 + n * (1 ±0)` - // Estimated: `6848 + n * (1 ±0)` - // Minimum execution time: 286_323_000 picoseconds. - Weight::from_parts(290_287_955, 6848) - // Standard Error: 1 - .saturating_add(Weight::from_parts(4_693, 0).saturating_mul(n.into())) + // Estimated: `6849 + n * (1 ±0)` + // Minimum execution time: 292_699_000 picoseconds. + Weight::from_parts(301_523_608, 6849) + // Standard Error: 14 + .saturating_add(Weight::from_parts(4_676, 0).saturating_mul(n.into())) .saturating_add(T::DbWeight::get().reads(6_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) .saturating_add(Weight::from_parts(0, 1).saturating_mul(n.into())) @@ -1497,10 +1519,10 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `727 + r * (112 ±0)` // Estimated: `6666 + r * (112 ±0)` - // Minimum execution time: 235_938_000 picoseconds. - Weight::from_parts(242_728_358, 6666) - // Standard Error: 9_725 - .saturating_add(Weight::from_parts(47_527_740, 0).saturating_mul(r.into())) + // Minimum execution time: 241_126_000 picoseconds. + Weight::from_parts(248_796_458, 6666) + // Standard Error: 21_501 + .saturating_add(Weight::from_parts(48_091_265, 0).saturating_mul(r.into())) .saturating_add(T::DbWeight::get().reads(6_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) .saturating_add(Weight::from_parts(0, 112).saturating_mul(r.into())) @@ -1519,11 +1541,11 @@ impl WeightInfo for SubstrateWeight { fn seal_ecdsa_recover(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `822 + r * (76 ±0)` - // Estimated: `6716 + r * (77 ±0)` - // Minimum execution time: 236_108_000 picoseconds. - Weight::from_parts(248_577_226, 6716) - // Standard Error: 9_565 - .saturating_add(Weight::from_parts(36_733_552, 0).saturating_mul(r.into())) + // Estimated: `6717 + r * (77 ±0)` + // Minimum execution time: 242_379_000 picoseconds. + Weight::from_parts(261_355_525, 6717) + // Standard Error: 18_862 + .saturating_add(Weight::from_parts(37_603_073, 0).saturating_mul(r.into())) .saturating_add(T::DbWeight::get().reads(6_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) .saturating_add(Weight::from_parts(0, 77).saturating_mul(r.into())) @@ -1543,10 +1565,10 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `792 + r * (42 ±0)` // Estimated: `6731 + r * (42 ±0)` - // Minimum execution time: 236_440_000 picoseconds. - Weight::from_parts(240_771_418, 6731) - // Standard Error: 1_849 - .saturating_add(Weight::from_parts(9_185_896, 0).saturating_mul(r.into())) + // Minimum execution time: 241_270_000 picoseconds. + Weight::from_parts(245_135_291, 6731) + // Standard Error: 10_757 + .saturating_add(Weight::from_parts(9_344_876, 0).saturating_mul(r.into())) .saturating_add(T::DbWeight::get().reads(6_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) .saturating_add(Weight::from_parts(0, 42).saturating_mul(r.into())) @@ -1567,11 +1589,11 @@ impl WeightInfo for SubstrateWeight { fn seal_set_code_hash(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0 + r * (964 ±0)` - // Estimated: `8190 + r * (3090 ±10)` - // Minimum execution time: 235_056_000 picoseconds. - Weight::from_parts(235_743_000, 8190) - // Standard Error: 46_122 - .saturating_add(Weight::from_parts(21_447_984, 0).saturating_mul(r.into())) + // Estimated: `8190 + r * (3090 ±7)` + // Minimum execution time: 240_506_000 picoseconds. + Weight::from_parts(241_653_000, 8190) + // Standard Error: 46_785 + .saturating_add(Weight::from_parts(22_107_816, 0).saturating_mul(r.into())) .saturating_add(T::DbWeight::get().reads(6_u64)) .saturating_add(T::DbWeight::get().reads((3_u64).saturating_mul(r.into()))) .saturating_add(T::DbWeight::get().writes(3_u64)) @@ -1593,10 +1615,10 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `773 + r * (3 ±0)` // Estimated: `6723 + r * (3 ±0)` - // Minimum execution time: 235_213_000 picoseconds. - Weight::from_parts(239_456_464, 6723) - // Standard Error: 130 - .saturating_add(Weight::from_parts(159_851, 0).saturating_mul(r.into())) + // Minimum execution time: 241_539_000 picoseconds. + Weight::from_parts(245_471_045, 6723) + // Standard Error: 416 + .saturating_add(Weight::from_parts(159_577, 0).saturating_mul(r.into())) .saturating_add(T::DbWeight::get().reads(6_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) .saturating_add(Weight::from_parts(0, 3).saturating_mul(r.into())) @@ -1616,10 +1638,10 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `1975 + r * (39 ±0)` // Estimated: `7805 + r * (40 ±0)` - // Minimum execution time: 237_886_000 picoseconds. - Weight::from_parts(262_430_157, 7805) - // Standard Error: 939 - .saturating_add(Weight::from_parts(260_005, 0).saturating_mul(r.into())) + // Minimum execution time: 242_702_000 picoseconds. + Weight::from_parts(274_518_595, 7805) + // Standard Error: 1_138 + .saturating_add(Weight::from_parts(256_973, 0).saturating_mul(r.into())) .saturating_add(T::DbWeight::get().reads(6_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) .saturating_add(Weight::from_parts(0, 40).saturating_mul(r.into())) @@ -1641,10 +1663,10 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `776 + r * (3 ±0)` // Estimated: `6723 + r * (3 ±0)` - // Minimum execution time: 234_014_000 picoseconds. - Weight::from_parts(240_042_671, 6723) - // Standard Error: 152 - .saturating_add(Weight::from_parts(138_382, 0).saturating_mul(r.into())) + // Minimum execution time: 239_360_000 picoseconds. + Weight::from_parts(245_990_810, 6723) + // Standard Error: 3_188 + .saturating_add(Weight::from_parts(143_408, 0).saturating_mul(r.into())) .saturating_add(T::DbWeight::get().reads(7_u64)) .saturating_add(T::DbWeight::get().writes(4_u64)) .saturating_add(Weight::from_parts(0, 3).saturating_mul(r.into())) @@ -1654,510 +1676,510 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_533_000 picoseconds. - Weight::from_parts(1_846_015, 0) + // Minimum execution time: 1_617_000 picoseconds. + Weight::from_parts(1_900_268, 0) // Standard Error: 2 - .saturating_add(Weight::from_parts(2_935, 0).saturating_mul(r.into())) + .saturating_add(Weight::from_parts(2_950, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 5000]`. fn instr_i64load(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_671_000 picoseconds. - Weight::from_parts(2_197_197, 0) - // Standard Error: 3 - .saturating_add(Weight::from_parts(6_335, 0).saturating_mul(r.into())) + // Minimum execution time: 1_739_000 picoseconds. + Weight::from_parts(2_109_373, 0) + // Standard Error: 43 + .saturating_add(Weight::from_parts(6_586, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 5000]`. fn instr_i64store(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_665_000 picoseconds. - Weight::from_parts(2_200_545, 0) - // Standard Error: 3 - .saturating_add(Weight::from_parts(6_011, 0).saturating_mul(r.into())) + // Minimum execution time: 1_726_000 picoseconds. + Weight::from_parts(2_268_507, 0) + // Standard Error: 4 + .saturating_add(Weight::from_parts(6_022, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 5000]`. fn instr_select(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_545_000 picoseconds. - Weight::from_parts(1_977_462, 0) - // Standard Error: 2 - .saturating_add(Weight::from_parts(7_901, 0).saturating_mul(r.into())) + // Minimum execution time: 1_628_000 picoseconds. + Weight::from_parts(2_042_521, 0) + // Standard Error: 3 + .saturating_add(Weight::from_parts(7_935, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 5000]`. fn instr_if(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_515_000 picoseconds. - Weight::from_parts(1_866_184, 0) - // Standard Error: 4 - .saturating_add(Weight::from_parts(10_514, 0).saturating_mul(r.into())) + // Minimum execution time: 1_648_000 picoseconds. + Weight::from_parts(1_902_691, 0) + // Standard Error: 7 + .saturating_add(Weight::from_parts(10_572, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 5000]`. fn instr_br(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_618_000 picoseconds. - Weight::from_parts(1_895_104, 0) - // Standard Error: 12 - .saturating_add(Weight::from_parts(4_523, 0).saturating_mul(r.into())) + // Minimum execution time: 1_626_000 picoseconds. + Weight::from_parts(1_891_843, 0) + // Standard Error: 14 + .saturating_add(Weight::from_parts(4_612, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 5000]`. fn instr_br_if(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_510_000 picoseconds. - Weight::from_parts(1_779_998, 0) - // Standard Error: 8 - .saturating_add(Weight::from_parts(6_832, 0).saturating_mul(r.into())) + // Minimum execution time: 1_581_000 picoseconds. + Weight::from_parts(1_139_823, 0) + // Standard Error: 74 + .saturating_add(Weight::from_parts(8_008, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 5000]`. fn instr_br_table(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_529_000 picoseconds. - Weight::from_parts(1_726_996, 0) - // Standard Error: 26 - .saturating_add(Weight::from_parts(9_199, 0).saturating_mul(r.into())) + // Minimum execution time: 1_591_000 picoseconds. + Weight::from_parts(1_258_400, 0) + // Standard Error: 34 + .saturating_add(Weight::from_parts(9_706, 0).saturating_mul(r.into())) } /// The range of component `e` is `[1, 256]`. fn instr_br_table_per_entry(e: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_682_000 picoseconds. - Weight::from_parts(1_789_910, 0) - // Standard Error: 16 - .saturating_add(Weight::from_parts(42, 0).saturating_mul(e.into())) + // Minimum execution time: 1_701_000 picoseconds. + Weight::from_parts(1_876_118, 0) + // Standard Error: 23 + .saturating_add(Weight::from_parts(4, 0).saturating_mul(e.into())) } /// The range of component `r` is `[0, 5000]`. fn instr_call(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_539_000 picoseconds. - Weight::from_parts(2_093_056, 0) - // Standard Error: 27 - .saturating_add(Weight::from_parts(18_917, 0).saturating_mul(r.into())) + // Minimum execution time: 1_617_000 picoseconds. + Weight::from_parts(1_565_613, 0) + // Standard Error: 629 + .saturating_add(Weight::from_parts(19_575, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 5000]`. fn instr_call_indirect(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_851_000 picoseconds. - Weight::from_parts(3_134_610, 0) - // Standard Error: 34 - .saturating_add(Weight::from_parts(24_714, 0).saturating_mul(r.into())) + // Minimum execution time: 1_875_000 picoseconds. + Weight::from_parts(4_549_584, 0) + // Standard Error: 278 + .saturating_add(Weight::from_parts(24_336, 0).saturating_mul(r.into())) } /// The range of component `l` is `[0, 1024]`. fn instr_call_per_local(l: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_654_000 picoseconds. - Weight::from_parts(1_885_921, 0) - // Standard Error: 14 - .saturating_add(Weight::from_parts(1_243, 0).saturating_mul(l.into())) + // Minimum execution time: 1_742_000 picoseconds. + Weight::from_parts(2_087_387, 0) + // Standard Error: 26 + .saturating_add(Weight::from_parts(1_041, 0).saturating_mul(l.into())) } /// The range of component `r` is `[0, 5000]`. fn instr_local_get(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 2_744_000 picoseconds. - Weight::from_parts(3_014_725, 0) - // Standard Error: 1 - .saturating_add(Weight::from_parts(2_447, 0).saturating_mul(r.into())) + // Minimum execution time: 2_861_000 picoseconds. + Weight::from_parts(3_552_428, 0) + // Standard Error: 23 + .saturating_add(Weight::from_parts(2_339, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 5000]`. fn instr_local_set(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 2_881_000 picoseconds. - Weight::from_parts(3_137_711, 0) - // Standard Error: 3 - .saturating_add(Weight::from_parts(3_608, 0).saturating_mul(r.into())) + // Minimum execution time: 2_866_000 picoseconds. + Weight::from_parts(3_151_948, 0) + // Standard Error: 4 + .saturating_add(Weight::from_parts(3_667, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 5000]`. fn instr_local_tee(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 2_809_000 picoseconds. - Weight::from_parts(3_142_066, 0) - // Standard Error: 3 - .saturating_add(Weight::from_parts(3_841, 0).saturating_mul(r.into())) + // Minimum execution time: 2_919_000 picoseconds. + Weight::from_parts(3_214_587, 0) + // Standard Error: 5 + .saturating_add(Weight::from_parts(3_867, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 5000]`. fn instr_global_get(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_704_000 picoseconds. - Weight::from_parts(2_083_619, 0) - // Standard Error: 2 - .saturating_add(Weight::from_parts(8_366, 0).saturating_mul(r.into())) + // Minimum execution time: 1_764_000 picoseconds. + Weight::from_parts(1_815_683, 0) + // Standard Error: 123 + .saturating_add(Weight::from_parts(8_733, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 5000]`. fn instr_global_set(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_652_000 picoseconds. - Weight::from_parts(2_048_256, 0) - // Standard Error: 5 - .saturating_add(Weight::from_parts(8_826, 0).saturating_mul(r.into())) + // Minimum execution time: 1_783_000 picoseconds. + Weight::from_parts(2_437_152, 0) + // Standard Error: 13 + .saturating_add(Weight::from_parts(8_839, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 5000]`. fn instr_memory_current(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_671_000 picoseconds. - Weight::from_parts(1_924_626, 0) - // Standard Error: 1 - .saturating_add(Weight::from_parts(3_746, 0).saturating_mul(r.into())) + // Minimum execution time: 1_745_000 picoseconds. + Weight::from_parts(2_018_078, 0) + // Standard Error: 2 + .saturating_add(Weight::from_parts(3_756, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 16]`. fn instr_memory_grow(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_585_000 picoseconds. - Weight::from_parts(490_856, 0) - // Standard Error: 133_673 - .saturating_add(Weight::from_parts(13_182_582, 0).saturating_mul(r.into())) + // Minimum execution time: 1_648_000 picoseconds. + Weight::from_parts(648_059, 0) + // Standard Error: 142_299 + .saturating_add(Weight::from_parts(13_313_060, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 5000]`. fn instr_i64clz(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_533_000 picoseconds. - Weight::from_parts(1_851_563, 0) + // Minimum execution time: 1_652_000 picoseconds. + Weight::from_parts(1_953_179, 0) // Standard Error: 2 - .saturating_add(Weight::from_parts(3_820, 0).saturating_mul(r.into())) + .saturating_add(Weight::from_parts(3_828, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 5000]`. fn instr_i64ctz(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_564_000 picoseconds. - Weight::from_parts(1_914_178, 0) + // Minimum execution time: 1_607_000 picoseconds. + Weight::from_parts(1_924_759, 0) // Standard Error: 2 - .saturating_add(Weight::from_parts(3_732, 0).saturating_mul(r.into())) + .saturating_add(Weight::from_parts(3_762, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 5000]`. fn instr_i64popcnt(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_559_000 picoseconds. - Weight::from_parts(1_886_992, 0) + // Minimum execution time: 1_687_000 picoseconds. + Weight::from_parts(1_959_683, 0) // Standard Error: 2 - .saturating_add(Weight::from_parts(3_731, 0).saturating_mul(r.into())) + .saturating_add(Weight::from_parts(3_754, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 5000]`. fn instr_i64eqz(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_553_000 picoseconds. - Weight::from_parts(1_886_545, 0) + // Minimum execution time: 1_641_000 picoseconds. + Weight::from_parts(1_975_838, 0) // Standard Error: 2 - .saturating_add(Weight::from_parts(3_658, 0).saturating_mul(r.into())) + .saturating_add(Weight::from_parts(3_681, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 5000]`. fn instr_i64extendsi32(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_507_000 picoseconds. - Weight::from_parts(1_853_647, 0) + // Minimum execution time: 1_689_000 picoseconds. + Weight::from_parts(1_980_109, 0) // Standard Error: 2 - .saturating_add(Weight::from_parts(3_852, 0).saturating_mul(r.into())) + .saturating_add(Weight::from_parts(3_880, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 5000]`. fn instr_i64extendui32(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_554_000 picoseconds. - Weight::from_parts(1_868_877, 0) - // Standard Error: 2 - .saturating_add(Weight::from_parts(3_806, 0).saturating_mul(r.into())) + // Minimum execution time: 1_671_000 picoseconds. + Weight::from_parts(1_912_089, 0) + // Standard Error: 29 + .saturating_add(Weight::from_parts(3_896, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 5000]`. fn instr_i32wrapi64(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_514_000 picoseconds. - Weight::from_parts(1_882_233, 0) + // Minimum execution time: 1_643_000 picoseconds. + Weight::from_parts(1_951_485, 0) // Standard Error: 2 - .saturating_add(Weight::from_parts(3_700, 0).saturating_mul(r.into())) + .saturating_add(Weight::from_parts(3_725, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 5000]`. fn instr_i64eq(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_529_000 picoseconds. - Weight::from_parts(1_897_247, 0) - // Standard Error: 2 - .saturating_add(Weight::from_parts(5_955, 0).saturating_mul(r.into())) + // Minimum execution time: 1_649_000 picoseconds. + Weight::from_parts(1_937_598, 0) + // Standard Error: 4 + .saturating_add(Weight::from_parts(6_045, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 5000]`. fn instr_i64ne(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_513_000 picoseconds. - Weight::from_parts(1_922_333, 0) - // Standard Error: 2 - .saturating_add(Weight::from_parts(5_933, 0).saturating_mul(r.into())) + // Minimum execution time: 1_651_000 picoseconds. + Weight::from_parts(2_202_977, 0) + // Standard Error: 313 + .saturating_add(Weight::from_parts(6_299, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 5000]`. fn instr_i64lts(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_512_000 picoseconds. - Weight::from_parts(1_848_668, 0) + // Minimum execution time: 1_589_000 picoseconds. + Weight::from_parts(1_946_304, 0) // Standard Error: 2 - .saturating_add(Weight::from_parts(5_966, 0).saturating_mul(r.into())) + .saturating_add(Weight::from_parts(6_019, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 5000]`. fn instr_i64ltu(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_522_000 picoseconds. - Weight::from_parts(1_875_257, 0) + // Minimum execution time: 1_614_000 picoseconds. + Weight::from_parts(1_933_375, 0) // Standard Error: 2 - .saturating_add(Weight::from_parts(5_965, 0).saturating_mul(r.into())) + .saturating_add(Weight::from_parts(6_020, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 5000]`. fn instr_i64gts(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_546_000 picoseconds. - Weight::from_parts(1_836_691, 0) - // Standard Error: 4 - .saturating_add(Weight::from_parts(5_842, 0).saturating_mul(r.into())) + // Minimum execution time: 1_678_000 picoseconds. + Weight::from_parts(2_003_850, 0) + // Standard Error: 2 + .saturating_add(Weight::from_parts(5_816, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 5000]`. fn instr_i64gtu(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_505_000 picoseconds. - Weight::from_parts(1_907_551, 0) - // Standard Error: 2 - .saturating_add(Weight::from_parts(6_075, 0).saturating_mul(r.into())) + // Minimum execution time: 1_651_000 picoseconds. + Weight::from_parts(1_971_321, 0) + // Standard Error: 3 + .saturating_add(Weight::from_parts(6_114, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 5000]`. fn instr_i64les(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_527_000 picoseconds. - Weight::from_parts(1_891_008, 0) + // Minimum execution time: 1_647_000 picoseconds. + Weight::from_parts(2_017_232, 0) // Standard Error: 2 - .saturating_add(Weight::from_parts(5_971, 0).saturating_mul(r.into())) + .saturating_add(Weight::from_parts(5_990, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 5000]`. fn instr_i64leu(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_556_000 picoseconds. - Weight::from_parts(1_910_864, 0) - // Standard Error: 2 - .saturating_add(Weight::from_parts(6_059, 0).saturating_mul(r.into())) + // Minimum execution time: 1_635_000 picoseconds. + Weight::from_parts(3_232_848, 0) + // Standard Error: 105 + .saturating_add(Weight::from_parts(5_816, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 5000]`. fn instr_i64ges(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_544_000 picoseconds. - Weight::from_parts(1_912_650, 0) - // Standard Error: 2 - .saturating_add(Weight::from_parts(5_943, 0).saturating_mul(r.into())) + // Minimum execution time: 1_623_000 picoseconds. + Weight::from_parts(1_996_165, 0) + // Standard Error: 3 + .saturating_add(Weight::from_parts(5_964, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 5000]`. fn instr_i64geu(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_513_000 picoseconds. - Weight::from_parts(1_855_260, 0) - // Standard Error: 2 - .saturating_add(Weight::from_parts(5_975, 0).saturating_mul(r.into())) + // Minimum execution time: 1_668_000 picoseconds. + Weight::from_parts(1_973_238, 0) + // Standard Error: 20 + .saturating_add(Weight::from_parts(6_021, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 5000]`. fn instr_i64add(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_521_000 picoseconds. - Weight::from_parts(1_867_259, 0) - // Standard Error: 2 - .saturating_add(Weight::from_parts(5_846, 0).saturating_mul(r.into())) + // Minimum execution time: 1_674_000 picoseconds. + Weight::from_parts(1_981_762, 0) + // Standard Error: 3 + .saturating_add(Weight::from_parts(5_898, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 5000]`. fn instr_i64sub(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_509_000 picoseconds. - Weight::from_parts(1_893_018, 0) - // Standard Error: 2 - .saturating_add(Weight::from_parts(6_096, 0).saturating_mul(r.into())) + // Minimum execution time: 1_632_000 picoseconds. + Weight::from_parts(1_935_700, 0) + // Standard Error: 3 + .saturating_add(Weight::from_parts(6_154, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 5000]`. fn instr_i64mul(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_496_000 picoseconds. - Weight::from_parts(1_886_659, 0) + // Minimum execution time: 1_607_000 picoseconds. + Weight::from_parts(1_942_734, 0) // Standard Error: 2 - .saturating_add(Weight::from_parts(5_754, 0).saturating_mul(r.into())) + .saturating_add(Weight::from_parts(5_797, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 5000]`. fn instr_i64divs(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_527_000 picoseconds. - Weight::from_parts(1_890_548, 0) - // Standard Error: 2 - .saturating_add(Weight::from_parts(11_842, 0).saturating_mul(r.into())) + // Minimum execution time: 1_611_000 picoseconds. + Weight::from_parts(2_960_454, 0) + // Standard Error: 177 + .saturating_add(Weight::from_parts(11_666, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 5000]`. fn instr_i64divu(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_518_000 picoseconds. - Weight::from_parts(1_891_903, 0) - // Standard Error: 4 - .saturating_add(Weight::from_parts(10_613, 0).saturating_mul(r.into())) + // Minimum execution time: 1_641_000 picoseconds. + Weight::from_parts(2_104_200, 0) + // Standard Error: 5 + .saturating_add(Weight::from_parts(10_540, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 5000]`. fn instr_i64rems(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_504_000 picoseconds. - Weight::from_parts(1_632_694, 0) - // Standard Error: 7 - .saturating_add(Weight::from_parts(12_281, 0).saturating_mul(r.into())) + // Minimum execution time: 1_643_000 picoseconds. + Weight::from_parts(2_602_908, 0) + // Standard Error: 24 + .saturating_add(Weight::from_parts(11_900, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 5000]`. fn instr_i64remu(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_507_000 picoseconds. - Weight::from_parts(1_878_413, 0) - // Standard Error: 2 - .saturating_add(Weight::from_parts(10_737, 0).saturating_mul(r.into())) + // Minimum execution time: 1_584_000 picoseconds. + Weight::from_parts(2_056_817, 0) + // Standard Error: 3 + .saturating_add(Weight::from_parts(10_722, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 5000]`. fn instr_i64and(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_534_000 picoseconds. - Weight::from_parts(1_898_519, 0) - // Standard Error: 2 - .saturating_add(Weight::from_parts(5_645, 0).saturating_mul(r.into())) + // Minimum execution time: 1_652_000 picoseconds. + Weight::from_parts(1_988_892, 0) + // Standard Error: 4 + .saturating_add(Weight::from_parts(5_683, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 5000]`. fn instr_i64or(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_503_000 picoseconds. - Weight::from_parts(1_895_532, 0) - // Standard Error: 2 - .saturating_add(Weight::from_parts(5_745, 0).saturating_mul(r.into())) + // Minimum execution time: 1_660_000 picoseconds. + Weight::from_parts(2_148_537, 0) + // Standard Error: 38 + .saturating_add(Weight::from_parts(5_756, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 5000]`. fn instr_i64xor(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_507_000 picoseconds. - Weight::from_parts(1_868_720, 0) - // Standard Error: 2 - .saturating_add(Weight::from_parts(5_873, 0).saturating_mul(r.into())) + // Minimum execution time: 1_629_000 picoseconds. + Weight::from_parts(1_955_010, 0) + // Standard Error: 3 + .saturating_add(Weight::from_parts(5_931, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 5000]`. fn instr_i64shl(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_513_000 picoseconds. - Weight::from_parts(1_894_207, 0) + // Minimum execution time: 1_569_000 picoseconds. + Weight::from_parts(1_982_403, 0) // Standard Error: 2 - .saturating_add(Weight::from_parts(5_843, 0).saturating_mul(r.into())) + .saturating_add(Weight::from_parts(5_867, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 5000]`. fn instr_i64shrs(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_473_000 picoseconds. - Weight::from_parts(1_880_224, 0) - // Standard Error: 2 - .saturating_add(Weight::from_parts(6_107, 0).saturating_mul(r.into())) + // Minimum execution time: 1_615_000 picoseconds. + Weight::from_parts(1_989_920, 0) + // Standard Error: 3 + .saturating_add(Weight::from_parts(6_137, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 5000]`. fn instr_i64shru(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_447_000 picoseconds. - Weight::from_parts(1_884_551, 0) + // Minimum execution time: 1_646_000 picoseconds. + Weight::from_parts(2_020_935, 0) // Standard Error: 2 - .saturating_add(Weight::from_parts(5_849, 0).saturating_mul(r.into())) + .saturating_add(Weight::from_parts(5_863, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 5000]`. fn instr_i64rotl(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_538_000 picoseconds. - Weight::from_parts(1_908_813, 0) - // Standard Error: 2 - .saturating_add(Weight::from_parts(5_987, 0).saturating_mul(r.into())) + // Minimum execution time: 1_661_000 picoseconds. + Weight::from_parts(2_320_710, 0) + // Standard Error: 27 + .saturating_add(Weight::from_parts(5_922, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 5000]`. fn instr_i64rotr(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_538_000 picoseconds. - Weight::from_parts(1_878_015, 0) + // Minimum execution time: 1_674_000 picoseconds. + Weight::from_parts(2_044_188, 0) // Standard Error: 2 - .saturating_add(Weight::from_parts(5_848, 0).saturating_mul(r.into())) + .saturating_add(Weight::from_parts(5_855, 0).saturating_mul(r.into())) } } @@ -2169,8 +2191,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `109` // Estimated: `1594` - // Minimum execution time: 2_565_000 picoseconds. - Weight::from_parts(2_759_000, 1594) + // Minimum execution time: 2_627_000 picoseconds. + Weight::from_parts(2_748_000, 1594) .saturating_add(RocksDbWeight::get().reads(1_u64)) } /// Storage: Skipped Metadata (r:0 w:0) @@ -2180,10 +2202,10 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `488 + k * (69 ±0)` // Estimated: `478 + k * (70 ±0)` - // Minimum execution time: 13_480_000 picoseconds. - Weight::from_parts(10_153_869, 478) - // Standard Error: 427 - .saturating_add(Weight::from_parts(958_726, 0).saturating_mul(k.into())) + // Minimum execution time: 13_607_000 picoseconds. + Weight::from_parts(8_026_118, 478) + // Standard Error: 1_323 + .saturating_add(Weight::from_parts(980_583, 0).saturating_mul(k.into())) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().reads((1_u64).saturating_mul(k.into()))) .saturating_add(RocksDbWeight::get().writes(2_u64)) @@ -2199,10 +2221,10 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `238 + c * (1 ±0)` // Estimated: `3708 + c * (1 ±0)` - // Minimum execution time: 30_406_000 picoseconds. - Weight::from_parts(28_467_370, 3708) - // Standard Error: 46 - .saturating_add(Weight::from_parts(53_724, 0).saturating_mul(c.into())) + // Minimum execution time: 30_563_000 picoseconds. + Weight::from_parts(22_292_544, 3708) + // Standard Error: 60 + .saturating_add(Weight::from_parts(54_541, 0).saturating_mul(c.into())) .saturating_add(RocksDbWeight::get().reads(1_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) .saturating_add(Weight::from_parts(0, 1).saturating_mul(c.into())) @@ -2222,10 +2244,10 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `707` // Estimated: `6656 + c * (1 ±0)` - // Minimum execution time: 263_198_000 picoseconds. - Weight::from_parts(276_162_279, 6656) - // Standard Error: 18 - .saturating_add(Weight::from_parts(37_378, 0).saturating_mul(c.into())) + // Minimum execution time: 268_884_000 picoseconds. + Weight::from_parts(277_799_331, 6656) + // Standard Error: 23 + .saturating_add(Weight::from_parts(37_876, 0).saturating_mul(c.into())) .saturating_add(RocksDbWeight::get().reads(6_u64)) .saturating_add(RocksDbWeight::get().writes(4_u64)) .saturating_add(Weight::from_parts(0, 1).saturating_mul(c.into())) @@ -2253,14 +2275,14 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `270` // Estimated: `8659` - // Minimum execution time: 3_132_259_000 picoseconds. - Weight::from_parts(513_284_834, 8659) - // Standard Error: 280 - .saturating_add(Weight::from_parts(106_723, 0).saturating_mul(c.into())) - // Standard Error: 16 - .saturating_add(Weight::from_parts(1_166, 0).saturating_mul(i.into())) - // Standard Error: 16 - .saturating_add(Weight::from_parts(1_436, 0).saturating_mul(s.into())) + // Minimum execution time: 3_159_921_000 picoseconds. + Weight::from_parts(594_826_134, 8659) + // Standard Error: 290 + .saturating_add(Weight::from_parts(106_471, 0).saturating_mul(c.into())) + // Standard Error: 17 + .saturating_add(Weight::from_parts(1_160, 0).saturating_mul(i.into())) + // Standard Error: 17 + .saturating_add(Weight::from_parts(1_417, 0).saturating_mul(s.into())) .saturating_add(RocksDbWeight::get().reads(9_u64)) .saturating_add(RocksDbWeight::get().writes(10_u64)) } @@ -2284,12 +2306,12 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `482` // Estimated: `6408` - // Minimum execution time: 1_646_604_000 picoseconds. - Weight::from_parts(271_369_256, 6408) - // Standard Error: 7 - .saturating_add(Weight::from_parts(1_426, 0).saturating_mul(i.into())) - // Standard Error: 7 - .saturating_add(Weight::from_parts(1_438, 0).saturating_mul(s.into())) + // Minimum execution time: 1_653_811_000 picoseconds. + Weight::from_parts(296_038_081, 6408) + // Standard Error: 9 + .saturating_add(Weight::from_parts(1_461, 0).saturating_mul(i.into())) + // Standard Error: 9 + .saturating_add(Weight::from_parts(1_430, 0).saturating_mul(s.into())) .saturating_add(RocksDbWeight::get().reads(9_u64)) .saturating_add(RocksDbWeight::get().writes(7_u64)) } @@ -2307,8 +2329,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `759` // Estimated: `6699` - // Minimum execution time: 191_360_000 picoseconds. - Weight::from_parts(192_625_000, 6699) + // Minimum execution time: 195_916_000 picoseconds. + Weight::from_parts(196_706_000, 6699) .saturating_add(RocksDbWeight::get().reads(6_u64)) .saturating_add(RocksDbWeight::get().writes(4_u64)) } @@ -2325,10 +2347,10 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `109` // Estimated: `3574` - // Minimum execution time: 245_207_000 picoseconds. - Weight::from_parts(244_703_457, 3574) - // Standard Error: 61 - .saturating_add(Weight::from_parts(106_850, 0).saturating_mul(c.into())) + // Minimum execution time: 251_137_000 picoseconds. + Weight::from_parts(252_985_435, 3574) + // Standard Error: 88 + .saturating_add(Weight::from_parts(108_141, 0).saturating_mul(c.into())) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().writes(4_u64)) } @@ -2344,8 +2366,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `255` // Estimated: `3720` - // Minimum execution time: 33_560_000 picoseconds. - Weight::from_parts(33_833_000, 3720) + // Minimum execution time: 33_521_000 picoseconds. + Weight::from_parts(34_039_000, 3720) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().writes(4_u64)) } @@ -2359,8 +2381,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `570` // Estimated: `8985` - // Minimum execution time: 33_288_000 picoseconds. - Weight::from_parts(33_775_000, 8985) + // Minimum execution time: 33_477_000 picoseconds. + Weight::from_parts(33_890_000, 8985) .saturating_add(RocksDbWeight::get().reads(6_u64)) .saturating_add(RocksDbWeight::get().writes(6_u64)) } @@ -2379,10 +2401,10 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `781 + r * (6 ±0)` // Estimated: `6722 + r * (6 ±0)` - // Minimum execution time: 234_292_000 picoseconds. - Weight::from_parts(235_941_911, 6722) - // Standard Error: 413 - .saturating_add(Weight::from_parts(339_913, 0).saturating_mul(r.into())) + // Minimum execution time: 239_374_000 picoseconds. + Weight::from_parts(246_017_099, 6722) + // Standard Error: 539 + .saturating_add(Weight::from_parts(323_826, 0).saturating_mul(r.into())) .saturating_add(RocksDbWeight::get().reads(6_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) .saturating_add(Weight::from_parts(0, 6).saturating_mul(r.into())) @@ -2402,10 +2424,10 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `839 + r * (240 ±0)` // Estimated: `6743 + r * (2715 ±0)` - // Minimum execution time: 236_273_000 picoseconds. - Weight::from_parts(74_380_906, 6743) - // Standard Error: 5_745 - .saturating_add(Weight::from_parts(3_331_781, 0).saturating_mul(r.into())) + // Minimum execution time: 240_656_000 picoseconds. + Weight::from_parts(87_361_934, 6743) + // Standard Error: 5_912 + .saturating_add(Weight::from_parts(3_329_840, 0).saturating_mul(r.into())) .saturating_add(RocksDbWeight::get().reads(6_u64)) .saturating_add(RocksDbWeight::get().reads((1_u64).saturating_mul(r.into()))) .saturating_add(RocksDbWeight::get().writes(3_u64)) @@ -2426,10 +2448,10 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `831 + r * (244 ±0)` // Estimated: `6747 + r * (2719 ±0)` - // Minimum execution time: 236_573_000 picoseconds. - Weight::from_parts(82_473_906, 6747) - // Standard Error: 5_510 - .saturating_add(Weight::from_parts(4_131_820, 0).saturating_mul(r.into())) + // Minimum execution time: 243_026_000 picoseconds. + Weight::from_parts(76_953_007, 6747) + // Standard Error: 6_640 + .saturating_add(Weight::from_parts(4_132_521, 0).saturating_mul(r.into())) .saturating_add(RocksDbWeight::get().reads(6_u64)) .saturating_add(RocksDbWeight::get().reads((1_u64).saturating_mul(r.into()))) .saturating_add(RocksDbWeight::get().writes(3_u64)) @@ -2450,10 +2472,10 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `788 + r * (6 ±0)` // Estimated: `6730 + r * (6 ±0)` - // Minimum execution time: 235_878_000 picoseconds. - Weight::from_parts(238_387_359, 6730) - // Standard Error: 318 - .saturating_add(Weight::from_parts(409_923, 0).saturating_mul(r.into())) + // Minimum execution time: 242_736_000 picoseconds. + Weight::from_parts(243_136_007, 6730) + // Standard Error: 912 + .saturating_add(Weight::from_parts(414_717, 0).saturating_mul(r.into())) .saturating_add(RocksDbWeight::get().reads(6_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) .saturating_add(Weight::from_parts(0, 6).saturating_mul(r.into())) @@ -2473,14 +2495,35 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `778 + r * (3 ±0)` // Estimated: `6723 + r * (3 ±0)` - // Minimum execution time: 233_476_000 picoseconds. - Weight::from_parts(238_014_452, 6723) - // Standard Error: 145 - .saturating_add(Weight::from_parts(165_823, 0).saturating_mul(r.into())) + // Minimum execution time: 240_130_000 picoseconds. + Weight::from_parts(244_517_187, 6723) + // Standard Error: 384 + .saturating_add(Weight::from_parts(167_431, 0).saturating_mul(r.into())) .saturating_add(RocksDbWeight::get().reads(6_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) .saturating_add(Weight::from_parts(0, 3).saturating_mul(r.into())) } + /// Storage: Contracts ContractInfoOf (r:1 w:1) + /// Proof: Contracts ContractInfoOf (max_values: None, max_size: Some(290), added: 2765, mode: Measured) + /// Storage: Contracts CodeStorage (r:1 w:0) + /// Proof: Contracts CodeStorage (max_values: None, max_size: Some(126001), added: 128476, mode: Measured) + /// Storage: Timestamp Now (r:1 w:0) + /// Proof: Timestamp Now (max_values: Some(1), max_size: Some(8), added: 503, mode: Measured) + /// Storage: System EventTopics (r:2 w:2) + /// Proof Skipped: System EventTopics (max_values: None, max_size: None, mode: Measured) + /// The range of component `r` is `[0, 1600]`. + fn seal_caller_is_root(r: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `668 + r * (3 ±0)` + // Estimated: `6608 + r * (3 ±0)` + // Minimum execution time: 228_022_000 picoseconds. + Weight::from_parts(232_385_198, 6608) + // Standard Error: 300 + .saturating_add(Weight::from_parts(145_143, 0).saturating_mul(r.into())) + .saturating_add(RocksDbWeight::get().reads(5_u64)) + .saturating_add(RocksDbWeight::get().writes(3_u64)) + .saturating_add(Weight::from_parts(0, 3).saturating_mul(r.into())) + } /// Storage: System Account (r:1 w:0) /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: Measured) /// Storage: Contracts ContractInfoOf (r:1 w:1) @@ -2496,10 +2539,10 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `782 + r * (6 ±0)` // Estimated: `6724 + r * (6 ±0)` - // Minimum execution time: 235_490_000 picoseconds. - Weight::from_parts(240_039_685, 6724) - // Standard Error: 330 - .saturating_add(Weight::from_parts(332_291, 0).saturating_mul(r.into())) + // Minimum execution time: 240_250_000 picoseconds. + Weight::from_parts(240_268_824, 6724) + // Standard Error: 945 + .saturating_add(Weight::from_parts(329_577, 0).saturating_mul(r.into())) .saturating_add(RocksDbWeight::get().reads(6_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) .saturating_add(Weight::from_parts(0, 6).saturating_mul(r.into())) @@ -2517,12 +2560,12 @@ impl WeightInfo for () { /// The range of component `r` is `[0, 1600]`. fn seal_gas_left(r: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `783 + r * (6 ±0)` - // Estimated: `6721 + r * (6 ±0)` - // Minimum execution time: 236_093_000 picoseconds. - Weight::from_parts(238_513_328, 6721) - // Standard Error: 206 - .saturating_add(Weight::from_parts(328_899, 0).saturating_mul(r.into())) + // Measured: `778 + r * (6 ±0)` + // Estimated: `6719 + r * (6 ±0)` + // Minimum execution time: 242_370_000 picoseconds. + Weight::from_parts(242_389_500, 6719) + // Standard Error: 712 + .saturating_add(Weight::from_parts(518_380, 0).saturating_mul(r.into())) .saturating_add(RocksDbWeight::get().reads(6_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) .saturating_add(Weight::from_parts(0, 6).saturating_mul(r.into())) @@ -2542,10 +2585,10 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `922 + r * (6 ±0)` // Estimated: `6846 + r * (6 ±0)` - // Minimum execution time: 234_801_000 picoseconds. - Weight::from_parts(243_519_159, 6846) - // Standard Error: 1_367 - .saturating_add(Weight::from_parts(1_449_599, 0).saturating_mul(r.into())) + // Minimum execution time: 238_563_000 picoseconds. + Weight::from_parts(253_511_314, 6846) + // Standard Error: 1_571 + .saturating_add(Weight::from_parts(1_454_089, 0).saturating_mul(r.into())) .saturating_add(RocksDbWeight::get().reads(7_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) .saturating_add(Weight::from_parts(0, 6).saturating_mul(r.into())) @@ -2565,10 +2608,10 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `792 + r * (6 ±0)` // Estimated: `6741 + r * (6 ±0)` - // Minimum execution time: 236_765_000 picoseconds. - Weight::from_parts(237_843_244, 6741) - // Standard Error: 308 - .saturating_add(Weight::from_parts(329_911, 0).saturating_mul(r.into())) + // Minimum execution time: 242_995_000 picoseconds. + Weight::from_parts(240_061_456, 6741) + // Standard Error: 2_650 + .saturating_add(Weight::from_parts(326_813, 0).saturating_mul(r.into())) .saturating_add(RocksDbWeight::get().reads(6_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) .saturating_add(Weight::from_parts(0, 6).saturating_mul(r.into())) @@ -2588,10 +2631,10 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `790 + r * (6 ±0)` // Estimated: `6739 + r * (6 ±0)` - // Minimum execution time: 236_690_000 picoseconds. - Weight::from_parts(241_743_164, 6739) - // Standard Error: 333 - .saturating_add(Weight::from_parts(324_693, 0).saturating_mul(r.into())) + // Minimum execution time: 241_342_000 picoseconds. + Weight::from_parts(240_875_314, 6739) + // Standard Error: 669 + .saturating_add(Weight::from_parts(324_519, 0).saturating_mul(r.into())) .saturating_add(RocksDbWeight::get().reads(6_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) .saturating_add(Weight::from_parts(0, 6).saturating_mul(r.into())) @@ -2611,10 +2654,10 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `787 + r * (6 ±0)` // Estimated: `6737 + r * (6 ±0)` - // Minimum execution time: 236_149_000 picoseconds. - Weight::from_parts(239_090_707, 6737) - // Standard Error: 246 - .saturating_add(Weight::from_parts(344_488, 0).saturating_mul(r.into())) + // Minimum execution time: 238_954_000 picoseconds. + Weight::from_parts(242_269_896, 6737) + // Standard Error: 1_453 + .saturating_add(Weight::from_parts(317_998, 0).saturating_mul(r.into())) .saturating_add(RocksDbWeight::get().reads(6_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) .saturating_add(Weight::from_parts(0, 6).saturating_mul(r.into())) @@ -2634,10 +2677,10 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `778 + r * (6 ±0)` // Estimated: `6723 + r * (6 ±0)` - // Minimum execution time: 235_057_000 picoseconds. - Weight::from_parts(237_752_870, 6723) - // Standard Error: 236 - .saturating_add(Weight::from_parts(328_235, 0).saturating_mul(r.into())) + // Minimum execution time: 240_935_000 picoseconds. + Weight::from_parts(242_938_271, 6723) + // Standard Error: 792 + .saturating_add(Weight::from_parts(316_782, 0).saturating_mul(r.into())) .saturating_add(RocksDbWeight::get().reads(6_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) .saturating_add(Weight::from_parts(0, 6).saturating_mul(r.into())) @@ -2657,15 +2700,15 @@ impl WeightInfo for () { /// The range of component `r` is `[0, 1600]`. fn seal_weight_to_fee(r: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `856 + r * (10 ±0)` - // Estimated: `6796 + r * (10 ±0)` - // Minimum execution time: 234_995_000 picoseconds. - Weight::from_parts(246_473_554, 6796) - // Standard Error: 1_015 - .saturating_add(Weight::from_parts(1_337_653, 0).saturating_mul(r.into())) + // Measured: `852 + r * (14 ±0)` + // Estimated: `6785 + r * (14 ±0)` + // Minimum execution time: 240_142_000 picoseconds. + Weight::from_parts(241_386_730, 6785) + // Standard Error: 2_116 + .saturating_add(Weight::from_parts(1_387_202, 0).saturating_mul(r.into())) .saturating_add(RocksDbWeight::get().reads(7_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) - .saturating_add(Weight::from_parts(0, 10).saturating_mul(r.into())) + .saturating_add(Weight::from_parts(0, 14).saturating_mul(r.into())) } /// Storage: System Account (r:1 w:0) /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: Measured) @@ -2682,10 +2725,10 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `745 + r * (4 ±0)` // Estimated: `6687 + r * (4 ±0)` - // Minimum execution time: 160_445_000 picoseconds. - Weight::from_parts(165_558_135, 6687) - // Standard Error: 234 - .saturating_add(Weight::from_parts(133_607, 0).saturating_mul(r.into())) + // Minimum execution time: 165_617_000 picoseconds. + Weight::from_parts(170_794_127, 6687) + // Standard Error: 209 + .saturating_add(Weight::from_parts(127_931, 0).saturating_mul(r.into())) .saturating_add(RocksDbWeight::get().reads(6_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) .saturating_add(Weight::from_parts(0, 4).saturating_mul(r.into())) @@ -2705,10 +2748,10 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `780 + r * (6 ±0)` // Estimated: `6724 + r * (6 ±0)` - // Minimum execution time: 235_065_000 picoseconds. - Weight::from_parts(237_797_177, 6724) - // Standard Error: 336 - .saturating_add(Weight::from_parts(267_302, 0).saturating_mul(r.into())) + // Minimum execution time: 238_832_000 picoseconds. + Weight::from_parts(237_110_694, 6724) + // Standard Error: 539 + .saturating_add(Weight::from_parts(280_610, 0).saturating_mul(r.into())) .saturating_add(RocksDbWeight::get().reads(6_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) .saturating_add(Weight::from_parts(0, 6).saturating_mul(r.into())) @@ -2728,10 +2771,10 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `784` // Estimated: `6724` - // Minimum execution time: 236_215_000 picoseconds. - Weight::from_parts(239_347_313, 6724) - // Standard Error: 0 - .saturating_add(Weight::from_parts(587, 0).saturating_mul(n.into())) + // Minimum execution time: 241_070_000 picoseconds. + Weight::from_parts(242_162_279, 6724) + // Standard Error: 1 + .saturating_add(Weight::from_parts(595, 0).saturating_mul(n.into())) .saturating_add(RocksDbWeight::get().reads(6_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) } @@ -2750,10 +2793,10 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `768 + r * (45 ±0)` // Estimated: `6708 + r * (45 ±0)` - // Minimum execution time: 231_571_000 picoseconds. - Weight::from_parts(233_477_918, 6708) - // Standard Error: 95_776 - .saturating_add(Weight::from_parts(1_733_181, 0).saturating_mul(r.into())) + // Minimum execution time: 236_337_000 picoseconds. + Weight::from_parts(238_883_828, 6708) + // Standard Error: 188_978 + .saturating_add(Weight::from_parts(926_671, 0).saturating_mul(r.into())) .saturating_add(RocksDbWeight::get().reads(6_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) .saturating_add(Weight::from_parts(0, 45).saturating_mul(r.into())) @@ -2773,10 +2816,10 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `778` // Estimated: `6731` - // Minimum execution time: 234_956_000 picoseconds. - Weight::from_parts(236_785_051, 6731) + // Minimum execution time: 239_103_000 picoseconds. + Weight::from_parts(240_382_910, 6731) // Standard Error: 0 - .saturating_add(Weight::from_parts(177, 0).saturating_mul(n.into())) + .saturating_add(Weight::from_parts(181, 0).saturating_mul(n.into())) .saturating_add(RocksDbWeight::get().reads(6_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) } @@ -2801,10 +2844,10 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `810 + r * (356 ±0)` // Estimated: `6750 + r * (7781 ±0)` - // Minimum execution time: 234_275_000 picoseconds. - Weight::from_parts(236_776_769, 6750) - // Standard Error: 137_203 - .saturating_add(Weight::from_parts(110_758_930, 0).saturating_mul(r.into())) + // Minimum execution time: 238_739_000 picoseconds. + Weight::from_parts(241_041_330, 6750) + // Standard Error: 176_820 + .saturating_add(Weight::from_parts(115_332_869, 0).saturating_mul(r.into())) .saturating_add(RocksDbWeight::get().reads(6_u64)) .saturating_add(RocksDbWeight::get().reads((6_u64).saturating_mul(r.into()))) .saturating_add(RocksDbWeight::get().writes(3_u64)) @@ -2828,10 +2871,10 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `825 + r * (10 ±0)` // Estimated: `6769 + r * (10 ±0)` - // Minimum execution time: 235_593_000 picoseconds. - Weight::from_parts(253_731_242, 6769) - // Standard Error: 2_129 - .saturating_add(Weight::from_parts(1_771_297, 0).saturating_mul(r.into())) + // Minimum execution time: 240_888_000 picoseconds. + Weight::from_parts(259_901_113, 6769) + // Standard Error: 5_935 + .saturating_add(Weight::from_parts(1_764_269, 0).saturating_mul(r.into())) .saturating_add(RocksDbWeight::get().reads(7_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) .saturating_add(Weight::from_parts(0, 10).saturating_mul(r.into())) @@ -2851,10 +2894,10 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `778 + r * (10 ±0)` // Estimated: `6723 + r * (10 ±0)` - // Minimum execution time: 232_124_000 picoseconds. - Weight::from_parts(245_904_447, 6723) - // Standard Error: 2_185 - .saturating_add(Weight::from_parts(3_470_410, 0).saturating_mul(r.into())) + // Minimum execution time: 237_478_000 picoseconds. + Weight::from_parts(264_915_436, 6723) + // Standard Error: 4_644 + .saturating_add(Weight::from_parts(3_452_918, 0).saturating_mul(r.into())) .saturating_add(RocksDbWeight::get().reads(6_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) .saturating_add(Weight::from_parts(0, 10).saturating_mul(r.into())) @@ -2875,12 +2918,12 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `797 + t * (32 ±0)` // Estimated: `6744 + t * (2508 ±0)` - // Minimum execution time: 250_301_000 picoseconds. - Weight::from_parts(245_292_258, 6744) - // Standard Error: 29_864 - .saturating_add(Weight::from_parts(2_163_531, 0).saturating_mul(t.into())) - // Standard Error: 8 - .saturating_add(Weight::from_parts(583, 0).saturating_mul(n.into())) + // Minimum execution time: 255_720_000 picoseconds. + Weight::from_parts(247_945_758, 6744) + // Standard Error: 73_390 + .saturating_add(Weight::from_parts(2_483_239, 0).saturating_mul(t.into())) + // Standard Error: 20 + .saturating_add(Weight::from_parts(756, 0).saturating_mul(n.into())) .saturating_add(RocksDbWeight::get().reads(6_u64)) .saturating_add(RocksDbWeight::get().reads((1_u64).saturating_mul(t.into()))) .saturating_add(RocksDbWeight::get().writes(3_u64)) @@ -2902,10 +2945,10 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `777 + r * (7 ±0)` // Estimated: `6721 + r * (7 ±0)` - // Minimum execution time: 165_711_000 picoseconds. - Weight::from_parts(168_792_571, 6721) - // Standard Error: 216 - .saturating_add(Weight::from_parts(230_285, 0).saturating_mul(r.into())) + // Minimum execution time: 172_214_000 picoseconds. + Weight::from_parts(177_306_567, 6721) + // Standard Error: 839 + .saturating_add(Weight::from_parts(230_558, 0).saturating_mul(r.into())) .saturating_add(RocksDbWeight::get().reads(6_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) .saturating_add(Weight::from_parts(0, 7).saturating_mul(r.into())) @@ -2925,10 +2968,10 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `125728` // Estimated: `131670` - // Minimum execution time: 348_928_000 picoseconds. - Weight::from_parts(352_224_793, 131670) - // Standard Error: 0 - .saturating_add(Weight::from_parts(731, 0).saturating_mul(i.into())) + // Minimum execution time: 354_105_000 picoseconds. + Weight::from_parts(360_649_854, 131670) + // Standard Error: 2 + .saturating_add(Weight::from_parts(737, 0).saturating_mul(i.into())) .saturating_add(RocksDbWeight::get().reads(6_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) } @@ -2939,10 +2982,10 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `845 + r * (292 ±0)` // Estimated: `843 + r * (293 ±0)` - // Minimum execution time: 236_418_000 picoseconds. - Weight::from_parts(129_862_840, 843) - // Standard Error: 9_733 - .saturating_add(Weight::from_parts(6_005_187, 0).saturating_mul(r.into())) + // Minimum execution time: 239_637_000 picoseconds. + Weight::from_parts(136_431_436, 843) + // Standard Error: 10_238 + .saturating_add(Weight::from_parts(6_070_221, 0).saturating_mul(r.into())) .saturating_add(RocksDbWeight::get().reads(6_u64)) .saturating_add(RocksDbWeight::get().reads((1_u64).saturating_mul(r.into()))) .saturating_add(RocksDbWeight::get().writes(3_u64)) @@ -2956,10 +2999,10 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `1304` // Estimated: `1280` - // Minimum execution time: 251_599_000 picoseconds. - Weight::from_parts(285_284_665, 1280) - // Standard Error: 46 - .saturating_add(Weight::from_parts(410, 0).saturating_mul(n.into())) + // Minimum execution time: 256_198_000 picoseconds. + Weight::from_parts(289_972_802, 1280) + // Standard Error: 54 + .saturating_add(Weight::from_parts(438, 0).saturating_mul(n.into())) .saturating_add(RocksDbWeight::get().reads(8_u64)) .saturating_add(RocksDbWeight::get().writes(6_u64)) } @@ -2970,10 +3013,10 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `1167 + n * (1 ±0)` // Estimated: `1167 + n * (1 ±0)` - // Minimum execution time: 251_309_000 picoseconds. - Weight::from_parts(253_555_552, 1167) - // Standard Error: 9 - .saturating_add(Weight::from_parts(27, 0).saturating_mul(n.into())) + // Minimum execution time: 255_519_000 picoseconds. + Weight::from_parts(257_668_217, 1167) + // Standard Error: 19 + .saturating_add(Weight::from_parts(105, 0).saturating_mul(n.into())) .saturating_add(RocksDbWeight::get().reads(7_u64)) .saturating_add(RocksDbWeight::get().writes(4_u64)) .saturating_add(Weight::from_parts(0, 1).saturating_mul(n.into())) @@ -2985,10 +3028,10 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `841 + r * (288 ±0)` // Estimated: `845 + r * (289 ±0)` - // Minimum execution time: 235_441_000 picoseconds. - Weight::from_parts(132_980_942, 845) - // Standard Error: 9_421 - .saturating_add(Weight::from_parts(5_854_896, 0).saturating_mul(r.into())) + // Minimum execution time: 239_461_000 picoseconds. + Weight::from_parts(131_630_528, 845) + // Standard Error: 10_483 + .saturating_add(Weight::from_parts(5_910_066, 0).saturating_mul(r.into())) .saturating_add(RocksDbWeight::get().reads(6_u64)) .saturating_add(RocksDbWeight::get().reads((1_u64).saturating_mul(r.into()))) .saturating_add(RocksDbWeight::get().writes(3_u64)) @@ -3002,10 +3045,10 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `1163 + n * (1 ±0)` // Estimated: `1163 + n * (1 ±0)` - // Minimum execution time: 249_967_000 picoseconds. - Weight::from_parts(252_122_186, 1163) - // Standard Error: 10 - .saturating_add(Weight::from_parts(74, 0).saturating_mul(n.into())) + // Minimum execution time: 254_904_000 picoseconds. + Weight::from_parts(261_213_399, 1163) + // Standard Error: 178 + .saturating_add(Weight::from_parts(125, 0).saturating_mul(n.into())) .saturating_add(RocksDbWeight::get().reads(7_u64)) .saturating_add(RocksDbWeight::get().writes(4_u64)) .saturating_add(Weight::from_parts(0, 1).saturating_mul(n.into())) @@ -3017,10 +3060,10 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `835 + r * (296 ±0)` // Estimated: `840 + r * (297 ±0)` - // Minimum execution time: 235_647_000 picoseconds. - Weight::from_parts(145_525_169, 840) - // Standard Error: 8_553 - .saturating_add(Weight::from_parts(4_948_021, 0).saturating_mul(r.into())) + // Minimum execution time: 239_995_000 picoseconds. + Weight::from_parts(151_326_508, 840) + // Standard Error: 8_960 + .saturating_add(Weight::from_parts(4_937_728, 0).saturating_mul(r.into())) .saturating_add(RocksDbWeight::get().reads(6_u64)) .saturating_add(RocksDbWeight::get().reads((1_u64).saturating_mul(r.into()))) .saturating_add(RocksDbWeight::get().writes(3_u64)) @@ -3033,10 +3076,10 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `1179 + n * (1 ±0)` // Estimated: `1179 + n * (1 ±0)` - // Minimum execution time: 249_576_000 picoseconds. - Weight::from_parts(250_747_191, 1179) - // Standard Error: 8 - .saturating_add(Weight::from_parts(717, 0).saturating_mul(n.into())) + // Minimum execution time: 254_515_000 picoseconds. + Weight::from_parts(256_728_817, 1179) + // Standard Error: 22 + .saturating_add(Weight::from_parts(706, 0).saturating_mul(n.into())) .saturating_add(RocksDbWeight::get().reads(7_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) .saturating_add(Weight::from_parts(0, 1).saturating_mul(n.into())) @@ -3048,10 +3091,10 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `856 + r * (288 ±0)` // Estimated: `857 + r * (289 ±0)` - // Minimum execution time: 236_110_000 picoseconds. - Weight::from_parts(148_420_625, 857) - // Standard Error: 8_175 - .saturating_add(Weight::from_parts(4_684_126, 0).saturating_mul(r.into())) + // Minimum execution time: 240_601_000 picoseconds. + Weight::from_parts(154_476_561, 857) + // Standard Error: 8_872 + .saturating_add(Weight::from_parts(4_805_043, 0).saturating_mul(r.into())) .saturating_add(RocksDbWeight::get().reads(6_u64)) .saturating_add(RocksDbWeight::get().reads((1_u64).saturating_mul(r.into()))) .saturating_add(RocksDbWeight::get().writes(3_u64)) @@ -3064,10 +3107,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `1166 + n * (1 ±0)` // Estimated: `1166 + n * (1 ±0)` - // Minimum execution time: 247_800_000 picoseconds. - Weight::from_parts(249_410_575, 1166) - // Standard Error: 6 - .saturating_add(Weight::from_parts(99, 0).saturating_mul(n.into())) + // Minimum execution time: 253_654_000 picoseconds. + Weight::from_parts(257_288_586, 1166) .saturating_add(RocksDbWeight::get().reads(7_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) .saturating_add(Weight::from_parts(0, 1).saturating_mul(n.into())) @@ -3079,10 +3120,10 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `829 + r * (296 ±0)` // Estimated: `836 + r * (297 ±0)` - // Minimum execution time: 235_251_000 picoseconds. - Weight::from_parts(128_816_707, 836) - // Standard Error: 9_887 - .saturating_add(Weight::from_parts(6_167_176, 0).saturating_mul(r.into())) + // Minimum execution time: 239_869_000 picoseconds. + Weight::from_parts(135_258_204, 836) + // Standard Error: 10_378 + .saturating_add(Weight::from_parts(6_144_770, 0).saturating_mul(r.into())) .saturating_add(RocksDbWeight::get().reads(6_u64)) .saturating_add(RocksDbWeight::get().reads((1_u64).saturating_mul(r.into()))) .saturating_add(RocksDbWeight::get().writes(3_u64)) @@ -3096,10 +3137,10 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `1180 + n * (1 ±0)` // Estimated: `1180 + n * (1 ±0)` - // Minimum execution time: 250_401_000 picoseconds. - Weight::from_parts(253_298_243, 1180) - // Standard Error: 9 - .saturating_add(Weight::from_parts(667, 0).saturating_mul(n.into())) + // Minimum execution time: 258_153_000 picoseconds. + Weight::from_parts(260_068_186, 1180) + // Standard Error: 25 + .saturating_add(Weight::from_parts(744, 0).saturating_mul(n.into())) .saturating_add(RocksDbWeight::get().reads(7_u64)) .saturating_add(RocksDbWeight::get().writes(4_u64)) .saturating_add(Weight::from_parts(0, 1).saturating_mul(n.into())) @@ -3119,10 +3160,10 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `1373 + r * (45 ±0)` // Estimated: `7270 + r * (2520 ±0)` - // Minimum execution time: 236_470_000 picoseconds. - Weight::from_parts(98_898_727, 7270) - // Standard Error: 33_316 - .saturating_add(Weight::from_parts(35_149_946, 0).saturating_mul(r.into())) + // Minimum execution time: 243_189_000 picoseconds. + Weight::from_parts(243_465_000, 7270) + // Standard Error: 30_961 + .saturating_add(Weight::from_parts(35_376_623, 0).saturating_mul(r.into())) .saturating_add(RocksDbWeight::get().reads(7_u64)) .saturating_add(RocksDbWeight::get().reads((1_u64).saturating_mul(r.into()))) .saturating_add(RocksDbWeight::get().writes(4_u64)) @@ -3137,22 +3178,22 @@ impl WeightInfo for () { /// Proof: Contracts CodeStorage (max_values: None, max_size: Some(126001), added: 128476, mode: Measured) /// Storage: Timestamp Now (r:1 w:0) /// Proof: Timestamp Now (max_values: Some(1), max_size: Some(8), added: 503, mode: Measured) - /// Storage: System EventTopics (r:802 w:802) + /// Storage: System EventTopics (r:803 w:803) /// Proof Skipped: System EventTopics (max_values: None, max_size: None, mode: Measured) /// The range of component `r` is `[0, 800]`. fn seal_call(r: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `1237 + r * (256 ±0)` - // Estimated: `7125 + r * (2732 ±0)` - // Minimum execution time: 238_303_000 picoseconds. - Weight::from_parts(239_024_000, 7125) - // Standard Error: 65_907 - .saturating_add(Weight::from_parts(209_419_071, 0).saturating_mul(r.into())) - .saturating_add(RocksDbWeight::get().reads(7_u64)) + // Measured: `1140 + r * (276 ±0)` + // Estimated: `9332 + r * (2752 ±0)` + // Minimum execution time: 243_656_000 picoseconds. + Weight::from_parts(244_221_000, 9332) + // Standard Error: 69_762 + .saturating_add(Weight::from_parts(216_905_619, 0).saturating_mul(r.into())) + .saturating_add(RocksDbWeight::get().reads(8_u64)) .saturating_add(RocksDbWeight::get().reads((2_u64).saturating_mul(r.into()))) - .saturating_add(RocksDbWeight::get().writes(3_u64)) + .saturating_add(RocksDbWeight::get().writes(4_u64)) .saturating_add(RocksDbWeight::get().writes((2_u64).saturating_mul(r.into()))) - .saturating_add(Weight::from_parts(0, 2732).saturating_mul(r.into())) + .saturating_add(Weight::from_parts(0, 2752).saturating_mul(r.into())) } /// Storage: System Account (r:1 w:0) /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: Measured) @@ -3169,10 +3210,10 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `0 + r * (502 ±0)` // Estimated: `6727 + r * (2572 ±10)` - // Minimum execution time: 235_961_000 picoseconds. - Weight::from_parts(236_939_000, 6727) - // Standard Error: 83_087 - .saturating_add(Weight::from_parts(205_646_517, 0).saturating_mul(r.into())) + // Minimum execution time: 242_632_000 picoseconds. + Weight::from_parts(243_068_000, 6727) + // Standard Error: 126_218 + .saturating_add(Weight::from_parts(213_096_291, 0).saturating_mul(r.into())) .saturating_add(RocksDbWeight::get().reads(6_u64)) .saturating_add(RocksDbWeight::get().reads((2_u64).saturating_mul(r.into()))) .saturating_add(RocksDbWeight::get().writes(3_u64)) @@ -3187,23 +3228,23 @@ impl WeightInfo for () { /// Proof: Contracts CodeStorage (max_values: None, max_size: Some(126001), added: 128476, mode: Measured) /// Storage: Timestamp Now (r:1 w:0) /// Proof: Timestamp Now (max_values: Some(1), max_size: Some(8), added: 503, mode: Measured) - /// Storage: System EventTopics (r:3 w:3) + /// Storage: System EventTopics (r:4 w:4) /// Proof Skipped: System EventTopics (max_values: None, max_size: None, mode: Measured) /// The range of component `t` is `[0, 1]`. /// The range of component `c` is `[0, 1048576]`. fn seal_call_per_transfer_clone_byte(t: u32, c: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `1154 + t * (204 ±0)` - // Estimated: `9569 + t * (5154 ±0)` - // Minimum execution time: 410_156_000 picoseconds. - Weight::from_parts(378_378_143, 9569) - // Standard Error: 285_172 - .saturating_add(Weight::from_parts(34_736_740, 0).saturating_mul(t.into())) - // Standard Error: 0 - .saturating_add(Weight::from_parts(591, 0).saturating_mul(c.into())) - .saturating_add(RocksDbWeight::get().reads(9_u64)) + // Estimated: `12044 + t * (5154 ±0)` + // Minimum execution time: 421_691_000 picoseconds. + Weight::from_parts(394_587_369, 12044) + // Standard Error: 1_104_014 + .saturating_add(Weight::from_parts(30_461_758, 0).saturating_mul(t.into())) + // Standard Error: 1 + .saturating_add(Weight::from_parts(601, 0).saturating_mul(c.into())) + .saturating_add(RocksDbWeight::get().reads(10_u64)) .saturating_add(RocksDbWeight::get().reads((2_u64).saturating_mul(t.into()))) - .saturating_add(RocksDbWeight::get().writes(5_u64)) + .saturating_add(RocksDbWeight::get().writes(6_u64)) .saturating_add(RocksDbWeight::get().writes((2_u64).saturating_mul(t.into()))) .saturating_add(Weight::from_parts(0, 5154).saturating_mul(t.into())) } @@ -3221,15 +3262,15 @@ impl WeightInfo for () { /// Proof: Contracts OwnerInfoOf (max_values: None, max_size: Some(88), added: 2563, mode: Measured) /// Storage: System EventTopics (r:802 w:802) /// Proof Skipped: System EventTopics (max_values: None, max_size: None, mode: Measured) - /// The range of component `r` is `[0, 800]`. + /// The range of component `r` is `[1, 800]`. fn seal_instantiate(r: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `1301 + r * (254 ±0)` - // Estimated: `7131 + r * (5205 ±0)` - // Minimum execution time: 236_748_000 picoseconds. - Weight::from_parts(237_129_000, 7131) - // Standard Error: 280_059 - .saturating_add(Weight::from_parts(341_428_013, 0).saturating_mul(r.into())) + // Measured: `1322 + r * (254 ±0)` + // Estimated: `7146 + r * (5205 ±0)` + // Minimum execution time: 581_252_000 picoseconds. + Weight::from_parts(582_275_000, 7146) + // Standard Error: 279_771 + .saturating_add(Weight::from_parts(349_770_967, 0).saturating_mul(r.into())) .saturating_add(RocksDbWeight::get().reads(8_u64)) .saturating_add(RocksDbWeight::get().reads((6_u64).saturating_mul(r.into()))) .saturating_add(RocksDbWeight::get().writes(6_u64)) @@ -3257,14 +3298,14 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `1071 + t * (187 ±0)` // Estimated: `9492 + t * (2634 ±2)` - // Minimum execution time: 1_613_796_000 picoseconds. - Weight::from_parts(340_002_206, 9492) - // Standard Error: 4_296_381 - .saturating_add(Weight::from_parts(115_239_834, 0).saturating_mul(t.into())) - // Standard Error: 6 - .saturating_add(Weight::from_parts(1_145, 0).saturating_mul(i.into())) - // Standard Error: 6 - .saturating_add(Weight::from_parts(1_315, 0).saturating_mul(s.into())) + // Minimum execution time: 1_623_241_000 picoseconds. + Weight::from_parts(317_076_173, 9492) + // Standard Error: 4_549_416 + .saturating_add(Weight::from_parts(125_360_446, 0).saturating_mul(t.into())) + // Standard Error: 7 + .saturating_add(Weight::from_parts(1_183, 0).saturating_mul(i.into())) + // Standard Error: 7 + .saturating_add(Weight::from_parts(1_352, 0).saturating_mul(s.into())) .saturating_add(RocksDbWeight::get().reads(13_u64)) .saturating_add(RocksDbWeight::get().reads((1_u64).saturating_mul(t.into()))) .saturating_add(RocksDbWeight::get().writes(10_u64)) @@ -3286,10 +3327,10 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `777 + r * (8 ±0)` // Estimated: `6718 + r * (8 ±0)` - // Minimum execution time: 233_111_000 picoseconds. - Weight::from_parts(238_643_933, 6718) - // Standard Error: 184 - .saturating_add(Weight::from_parts(572_296, 0).saturating_mul(r.into())) + // Minimum execution time: 238_262_000 picoseconds. + Weight::from_parts(243_093_288, 6718) + // Standard Error: 870 + .saturating_add(Weight::from_parts(573_939, 0).saturating_mul(r.into())) .saturating_add(RocksDbWeight::get().reads(6_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) .saturating_add(Weight::from_parts(0, 8).saturating_mul(r.into())) @@ -3309,10 +3350,10 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `785` // Estimated: `6725` - // Minimum execution time: 234_746_000 picoseconds. - Weight::from_parts(229_815_552, 6725) - // Standard Error: 1 - .saturating_add(Weight::from_parts(3_892, 0).saturating_mul(n.into())) + // Minimum execution time: 239_888_000 picoseconds. + Weight::from_parts(242_849_333, 6725) + // Standard Error: 3 + .saturating_add(Weight::from_parts(3_949, 0).saturating_mul(n.into())) .saturating_add(RocksDbWeight::get().reads(6_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) } @@ -3331,10 +3372,10 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `779 + r * (8 ±0)` // Estimated: `6721 + r * (8 ±0)` - // Minimum execution time: 232_732_000 picoseconds. - Weight::from_parts(239_007_209, 6721) - // Standard Error: 256 - .saturating_add(Weight::from_parts(733_879, 0).saturating_mul(r.into())) + // Minimum execution time: 237_288_000 picoseconds. + Weight::from_parts(242_510_631, 6721) + // Standard Error: 977 + .saturating_add(Weight::from_parts(742_726, 0).saturating_mul(r.into())) .saturating_add(RocksDbWeight::get().reads(6_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) .saturating_add(Weight::from_parts(0, 8).saturating_mul(r.into())) @@ -3354,10 +3395,10 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `787` // Estimated: `6729` - // Minimum execution time: 234_184_000 picoseconds. - Weight::from_parts(227_603_375, 6729) - // Standard Error: 1 - .saturating_add(Weight::from_parts(3_127, 0).saturating_mul(n.into())) + // Minimum execution time: 240_006_000 picoseconds. + Weight::from_parts(233_802_510, 6729) + // Standard Error: 2 + .saturating_add(Weight::from_parts(3_161, 0).saturating_mul(n.into())) .saturating_add(RocksDbWeight::get().reads(6_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) } @@ -3376,10 +3417,10 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `779 + r * (8 ±0)` // Estimated: `6724 + r * (8 ±0)` - // Minimum execution time: 233_038_000 picoseconds. - Weight::from_parts(238_515_817, 6724) - // Standard Error: 255 - .saturating_add(Weight::from_parts(413_343, 0).saturating_mul(r.into())) + // Minimum execution time: 237_532_000 picoseconds. + Weight::from_parts(243_087_565, 6724) + // Standard Error: 656 + .saturating_add(Weight::from_parts(417_850, 0).saturating_mul(r.into())) .saturating_add(RocksDbWeight::get().reads(6_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) .saturating_add(Weight::from_parts(0, 8).saturating_mul(r.into())) @@ -3399,10 +3440,10 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `787` // Estimated: `6733` - // Minimum execution time: 232_996_000 picoseconds. - Weight::from_parts(226_706_997, 6733) + // Minimum execution time: 241_429_000 picoseconds. + Weight::from_parts(233_528_258, 6733) // Standard Error: 1 - .saturating_add(Weight::from_parts(908, 0).saturating_mul(n.into())) + .saturating_add(Weight::from_parts(913, 0).saturating_mul(n.into())) .saturating_add(RocksDbWeight::get().reads(6_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) } @@ -3421,10 +3462,10 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `779 + r * (8 ±0)` // Estimated: `6725 + r * (8 ±0)` - // Minimum execution time: 232_292_000 picoseconds. - Weight::from_parts(237_997_001, 6725) - // Standard Error: 219 - .saturating_add(Weight::from_parts(410_177, 0).saturating_mul(r.into())) + // Minimum execution time: 237_622_000 picoseconds. + Weight::from_parts(240_476_401, 6725) + // Standard Error: 795 + .saturating_add(Weight::from_parts(416_869, 0).saturating_mul(r.into())) .saturating_add(RocksDbWeight::get().reads(6_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) .saturating_add(Weight::from_parts(0, 8).saturating_mul(r.into())) @@ -3444,10 +3485,10 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `787` // Estimated: `6727` - // Minimum execution time: 234_815_000 picoseconds. - Weight::from_parts(226_317_150, 6727) - // Standard Error: 1 - .saturating_add(Weight::from_parts(911, 0).saturating_mul(n.into())) + // Minimum execution time: 241_134_000 picoseconds. + Weight::from_parts(234_043_271, 6727) + // Standard Error: 3 + .saturating_add(Weight::from_parts(919, 0).saturating_mul(n.into())) .saturating_add(RocksDbWeight::get().reads(6_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) } @@ -3465,11 +3506,11 @@ impl WeightInfo for () { fn seal_sr25519_verify_per_byte(n: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `912 + n * (1 ±0)` - // Estimated: `6848 + n * (1 ±0)` - // Minimum execution time: 286_323_000 picoseconds. - Weight::from_parts(290_287_955, 6848) - // Standard Error: 1 - .saturating_add(Weight::from_parts(4_693, 0).saturating_mul(n.into())) + // Estimated: `6849 + n * (1 ±0)` + // Minimum execution time: 292_699_000 picoseconds. + Weight::from_parts(301_523_608, 6849) + // Standard Error: 14 + .saturating_add(Weight::from_parts(4_676, 0).saturating_mul(n.into())) .saturating_add(RocksDbWeight::get().reads(6_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) .saturating_add(Weight::from_parts(0, 1).saturating_mul(n.into())) @@ -3489,10 +3530,10 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `727 + r * (112 ±0)` // Estimated: `6666 + r * (112 ±0)` - // Minimum execution time: 235_938_000 picoseconds. - Weight::from_parts(242_728_358, 6666) - // Standard Error: 9_725 - .saturating_add(Weight::from_parts(47_527_740, 0).saturating_mul(r.into())) + // Minimum execution time: 241_126_000 picoseconds. + Weight::from_parts(248_796_458, 6666) + // Standard Error: 21_501 + .saturating_add(Weight::from_parts(48_091_265, 0).saturating_mul(r.into())) .saturating_add(RocksDbWeight::get().reads(6_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) .saturating_add(Weight::from_parts(0, 112).saturating_mul(r.into())) @@ -3511,11 +3552,11 @@ impl WeightInfo for () { fn seal_ecdsa_recover(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `822 + r * (76 ±0)` - // Estimated: `6716 + r * (77 ±0)` - // Minimum execution time: 236_108_000 picoseconds. - Weight::from_parts(248_577_226, 6716) - // Standard Error: 9_565 - .saturating_add(Weight::from_parts(36_733_552, 0).saturating_mul(r.into())) + // Estimated: `6717 + r * (77 ±0)` + // Minimum execution time: 242_379_000 picoseconds. + Weight::from_parts(261_355_525, 6717) + // Standard Error: 18_862 + .saturating_add(Weight::from_parts(37_603_073, 0).saturating_mul(r.into())) .saturating_add(RocksDbWeight::get().reads(6_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) .saturating_add(Weight::from_parts(0, 77).saturating_mul(r.into())) @@ -3535,10 +3576,10 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `792 + r * (42 ±0)` // Estimated: `6731 + r * (42 ±0)` - // Minimum execution time: 236_440_000 picoseconds. - Weight::from_parts(240_771_418, 6731) - // Standard Error: 1_849 - .saturating_add(Weight::from_parts(9_185_896, 0).saturating_mul(r.into())) + // Minimum execution time: 241_270_000 picoseconds. + Weight::from_parts(245_135_291, 6731) + // Standard Error: 10_757 + .saturating_add(Weight::from_parts(9_344_876, 0).saturating_mul(r.into())) .saturating_add(RocksDbWeight::get().reads(6_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) .saturating_add(Weight::from_parts(0, 42).saturating_mul(r.into())) @@ -3559,11 +3600,11 @@ impl WeightInfo for () { fn seal_set_code_hash(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0 + r * (964 ±0)` - // Estimated: `8190 + r * (3090 ±10)` - // Minimum execution time: 235_056_000 picoseconds. - Weight::from_parts(235_743_000, 8190) - // Standard Error: 46_122 - .saturating_add(Weight::from_parts(21_447_984, 0).saturating_mul(r.into())) + // Estimated: `8190 + r * (3090 ±7)` + // Minimum execution time: 240_506_000 picoseconds. + Weight::from_parts(241_653_000, 8190) + // Standard Error: 46_785 + .saturating_add(Weight::from_parts(22_107_816, 0).saturating_mul(r.into())) .saturating_add(RocksDbWeight::get().reads(6_u64)) .saturating_add(RocksDbWeight::get().reads((3_u64).saturating_mul(r.into()))) .saturating_add(RocksDbWeight::get().writes(3_u64)) @@ -3585,10 +3626,10 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `773 + r * (3 ±0)` // Estimated: `6723 + r * (3 ±0)` - // Minimum execution time: 235_213_000 picoseconds. - Weight::from_parts(239_456_464, 6723) - // Standard Error: 130 - .saturating_add(Weight::from_parts(159_851, 0).saturating_mul(r.into())) + // Minimum execution time: 241_539_000 picoseconds. + Weight::from_parts(245_471_045, 6723) + // Standard Error: 416 + .saturating_add(Weight::from_parts(159_577, 0).saturating_mul(r.into())) .saturating_add(RocksDbWeight::get().reads(6_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) .saturating_add(Weight::from_parts(0, 3).saturating_mul(r.into())) @@ -3608,10 +3649,10 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `1975 + r * (39 ±0)` // Estimated: `7805 + r * (40 ±0)` - // Minimum execution time: 237_886_000 picoseconds. - Weight::from_parts(262_430_157, 7805) - // Standard Error: 939 - .saturating_add(Weight::from_parts(260_005, 0).saturating_mul(r.into())) + // Minimum execution time: 242_702_000 picoseconds. + Weight::from_parts(274_518_595, 7805) + // Standard Error: 1_138 + .saturating_add(Weight::from_parts(256_973, 0).saturating_mul(r.into())) .saturating_add(RocksDbWeight::get().reads(6_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) .saturating_add(Weight::from_parts(0, 40).saturating_mul(r.into())) @@ -3633,10 +3674,10 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `776 + r * (3 ±0)` // Estimated: `6723 + r * (3 ±0)` - // Minimum execution time: 234_014_000 picoseconds. - Weight::from_parts(240_042_671, 6723) - // Standard Error: 152 - .saturating_add(Weight::from_parts(138_382, 0).saturating_mul(r.into())) + // Minimum execution time: 239_360_000 picoseconds. + Weight::from_parts(245_990_810, 6723) + // Standard Error: 3_188 + .saturating_add(Weight::from_parts(143_408, 0).saturating_mul(r.into())) .saturating_add(RocksDbWeight::get().reads(7_u64)) .saturating_add(RocksDbWeight::get().writes(4_u64)) .saturating_add(Weight::from_parts(0, 3).saturating_mul(r.into())) @@ -3646,509 +3687,509 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_533_000 picoseconds. - Weight::from_parts(1_846_015, 0) + // Minimum execution time: 1_617_000 picoseconds. + Weight::from_parts(1_900_268, 0) // Standard Error: 2 - .saturating_add(Weight::from_parts(2_935, 0).saturating_mul(r.into())) + .saturating_add(Weight::from_parts(2_950, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 5000]`. fn instr_i64load(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_671_000 picoseconds. - Weight::from_parts(2_197_197, 0) - // Standard Error: 3 - .saturating_add(Weight::from_parts(6_335, 0).saturating_mul(r.into())) + // Minimum execution time: 1_739_000 picoseconds. + Weight::from_parts(2_109_373, 0) + // Standard Error: 43 + .saturating_add(Weight::from_parts(6_586, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 5000]`. fn instr_i64store(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_665_000 picoseconds. - Weight::from_parts(2_200_545, 0) - // Standard Error: 3 - .saturating_add(Weight::from_parts(6_011, 0).saturating_mul(r.into())) + // Minimum execution time: 1_726_000 picoseconds. + Weight::from_parts(2_268_507, 0) + // Standard Error: 4 + .saturating_add(Weight::from_parts(6_022, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 5000]`. fn instr_select(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_545_000 picoseconds. - Weight::from_parts(1_977_462, 0) - // Standard Error: 2 - .saturating_add(Weight::from_parts(7_901, 0).saturating_mul(r.into())) + // Minimum execution time: 1_628_000 picoseconds. + Weight::from_parts(2_042_521, 0) + // Standard Error: 3 + .saturating_add(Weight::from_parts(7_935, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 5000]`. fn instr_if(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_515_000 picoseconds. - Weight::from_parts(1_866_184, 0) - // Standard Error: 4 - .saturating_add(Weight::from_parts(10_514, 0).saturating_mul(r.into())) + // Minimum execution time: 1_648_000 picoseconds. + Weight::from_parts(1_902_691, 0) + // Standard Error: 7 + .saturating_add(Weight::from_parts(10_572, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 5000]`. fn instr_br(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_618_000 picoseconds. - Weight::from_parts(1_895_104, 0) - // Standard Error: 12 - .saturating_add(Weight::from_parts(4_523, 0).saturating_mul(r.into())) + // Minimum execution time: 1_626_000 picoseconds. + Weight::from_parts(1_891_843, 0) + // Standard Error: 14 + .saturating_add(Weight::from_parts(4_612, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 5000]`. fn instr_br_if(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_510_000 picoseconds. - Weight::from_parts(1_779_998, 0) - // Standard Error: 8 - .saturating_add(Weight::from_parts(6_832, 0).saturating_mul(r.into())) + // Minimum execution time: 1_581_000 picoseconds. + Weight::from_parts(1_139_823, 0) + // Standard Error: 74 + .saturating_add(Weight::from_parts(8_008, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 5000]`. fn instr_br_table(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_529_000 picoseconds. - Weight::from_parts(1_726_996, 0) - // Standard Error: 26 - .saturating_add(Weight::from_parts(9_199, 0).saturating_mul(r.into())) + // Minimum execution time: 1_591_000 picoseconds. + Weight::from_parts(1_258_400, 0) + // Standard Error: 34 + .saturating_add(Weight::from_parts(9_706, 0).saturating_mul(r.into())) } /// The range of component `e` is `[1, 256]`. fn instr_br_table_per_entry(e: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_682_000 picoseconds. - Weight::from_parts(1_789_910, 0) - // Standard Error: 16 - .saturating_add(Weight::from_parts(42, 0).saturating_mul(e.into())) + // Minimum execution time: 1_701_000 picoseconds. + Weight::from_parts(1_876_118, 0) + // Standard Error: 23 + .saturating_add(Weight::from_parts(4, 0).saturating_mul(e.into())) } /// The range of component `r` is `[0, 5000]`. fn instr_call(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_539_000 picoseconds. - Weight::from_parts(2_093_056, 0) - // Standard Error: 27 - .saturating_add(Weight::from_parts(18_917, 0).saturating_mul(r.into())) + // Minimum execution time: 1_617_000 picoseconds. + Weight::from_parts(1_565_613, 0) + // Standard Error: 629 + .saturating_add(Weight::from_parts(19_575, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 5000]`. fn instr_call_indirect(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_851_000 picoseconds. - Weight::from_parts(3_134_610, 0) - // Standard Error: 34 - .saturating_add(Weight::from_parts(24_714, 0).saturating_mul(r.into())) + // Minimum execution time: 1_875_000 picoseconds. + Weight::from_parts(4_549_584, 0) + // Standard Error: 278 + .saturating_add(Weight::from_parts(24_336, 0).saturating_mul(r.into())) } /// The range of component `l` is `[0, 1024]`. fn instr_call_per_local(l: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_654_000 picoseconds. - Weight::from_parts(1_885_921, 0) - // Standard Error: 14 - .saturating_add(Weight::from_parts(1_243, 0).saturating_mul(l.into())) + // Minimum execution time: 1_742_000 picoseconds. + Weight::from_parts(2_087_387, 0) + // Standard Error: 26 + .saturating_add(Weight::from_parts(1_041, 0).saturating_mul(l.into())) } /// The range of component `r` is `[0, 5000]`. fn instr_local_get(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 2_744_000 picoseconds. - Weight::from_parts(3_014_725, 0) - // Standard Error: 1 - .saturating_add(Weight::from_parts(2_447, 0).saturating_mul(r.into())) + // Minimum execution time: 2_861_000 picoseconds. + Weight::from_parts(3_552_428, 0) + // Standard Error: 23 + .saturating_add(Weight::from_parts(2_339, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 5000]`. fn instr_local_set(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 2_881_000 picoseconds. - Weight::from_parts(3_137_711, 0) - // Standard Error: 3 - .saturating_add(Weight::from_parts(3_608, 0).saturating_mul(r.into())) + // Minimum execution time: 2_866_000 picoseconds. + Weight::from_parts(3_151_948, 0) + // Standard Error: 4 + .saturating_add(Weight::from_parts(3_667, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 5000]`. fn instr_local_tee(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 2_809_000 picoseconds. - Weight::from_parts(3_142_066, 0) - // Standard Error: 3 - .saturating_add(Weight::from_parts(3_841, 0).saturating_mul(r.into())) + // Minimum execution time: 2_919_000 picoseconds. + Weight::from_parts(3_214_587, 0) + // Standard Error: 5 + .saturating_add(Weight::from_parts(3_867, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 5000]`. fn instr_global_get(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_704_000 picoseconds. - Weight::from_parts(2_083_619, 0) - // Standard Error: 2 - .saturating_add(Weight::from_parts(8_366, 0).saturating_mul(r.into())) + // Minimum execution time: 1_764_000 picoseconds. + Weight::from_parts(1_815_683, 0) + // Standard Error: 123 + .saturating_add(Weight::from_parts(8_733, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 5000]`. fn instr_global_set(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_652_000 picoseconds. - Weight::from_parts(2_048_256, 0) - // Standard Error: 5 - .saturating_add(Weight::from_parts(8_826, 0).saturating_mul(r.into())) + // Minimum execution time: 1_783_000 picoseconds. + Weight::from_parts(2_437_152, 0) + // Standard Error: 13 + .saturating_add(Weight::from_parts(8_839, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 5000]`. fn instr_memory_current(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_671_000 picoseconds. - Weight::from_parts(1_924_626, 0) - // Standard Error: 1 - .saturating_add(Weight::from_parts(3_746, 0).saturating_mul(r.into())) + // Minimum execution time: 1_745_000 picoseconds. + Weight::from_parts(2_018_078, 0) + // Standard Error: 2 + .saturating_add(Weight::from_parts(3_756, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 16]`. fn instr_memory_grow(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_585_000 picoseconds. - Weight::from_parts(490_856, 0) - // Standard Error: 133_673 - .saturating_add(Weight::from_parts(13_182_582, 0).saturating_mul(r.into())) + // Minimum execution time: 1_648_000 picoseconds. + Weight::from_parts(648_059, 0) + // Standard Error: 142_299 + .saturating_add(Weight::from_parts(13_313_060, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 5000]`. fn instr_i64clz(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_533_000 picoseconds. - Weight::from_parts(1_851_563, 0) + // Minimum execution time: 1_652_000 picoseconds. + Weight::from_parts(1_953_179, 0) // Standard Error: 2 - .saturating_add(Weight::from_parts(3_820, 0).saturating_mul(r.into())) + .saturating_add(Weight::from_parts(3_828, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 5000]`. fn instr_i64ctz(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_564_000 picoseconds. - Weight::from_parts(1_914_178, 0) + // Minimum execution time: 1_607_000 picoseconds. + Weight::from_parts(1_924_759, 0) // Standard Error: 2 - .saturating_add(Weight::from_parts(3_732, 0).saturating_mul(r.into())) + .saturating_add(Weight::from_parts(3_762, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 5000]`. fn instr_i64popcnt(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_559_000 picoseconds. - Weight::from_parts(1_886_992, 0) + // Minimum execution time: 1_687_000 picoseconds. + Weight::from_parts(1_959_683, 0) // Standard Error: 2 - .saturating_add(Weight::from_parts(3_731, 0).saturating_mul(r.into())) + .saturating_add(Weight::from_parts(3_754, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 5000]`. fn instr_i64eqz(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_553_000 picoseconds. - Weight::from_parts(1_886_545, 0) + // Minimum execution time: 1_641_000 picoseconds. + Weight::from_parts(1_975_838, 0) // Standard Error: 2 - .saturating_add(Weight::from_parts(3_658, 0).saturating_mul(r.into())) + .saturating_add(Weight::from_parts(3_681, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 5000]`. fn instr_i64extendsi32(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_507_000 picoseconds. - Weight::from_parts(1_853_647, 0) + // Minimum execution time: 1_689_000 picoseconds. + Weight::from_parts(1_980_109, 0) // Standard Error: 2 - .saturating_add(Weight::from_parts(3_852, 0).saturating_mul(r.into())) + .saturating_add(Weight::from_parts(3_880, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 5000]`. fn instr_i64extendui32(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_554_000 picoseconds. - Weight::from_parts(1_868_877, 0) - // Standard Error: 2 - .saturating_add(Weight::from_parts(3_806, 0).saturating_mul(r.into())) + // Minimum execution time: 1_671_000 picoseconds. + Weight::from_parts(1_912_089, 0) + // Standard Error: 29 + .saturating_add(Weight::from_parts(3_896, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 5000]`. fn instr_i32wrapi64(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_514_000 picoseconds. - Weight::from_parts(1_882_233, 0) + // Minimum execution time: 1_643_000 picoseconds. + Weight::from_parts(1_951_485, 0) // Standard Error: 2 - .saturating_add(Weight::from_parts(3_700, 0).saturating_mul(r.into())) + .saturating_add(Weight::from_parts(3_725, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 5000]`. fn instr_i64eq(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_529_000 picoseconds. - Weight::from_parts(1_897_247, 0) - // Standard Error: 2 - .saturating_add(Weight::from_parts(5_955, 0).saturating_mul(r.into())) + // Minimum execution time: 1_649_000 picoseconds. + Weight::from_parts(1_937_598, 0) + // Standard Error: 4 + .saturating_add(Weight::from_parts(6_045, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 5000]`. fn instr_i64ne(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_513_000 picoseconds. - Weight::from_parts(1_922_333, 0) - // Standard Error: 2 - .saturating_add(Weight::from_parts(5_933, 0).saturating_mul(r.into())) + // Minimum execution time: 1_651_000 picoseconds. + Weight::from_parts(2_202_977, 0) + // Standard Error: 313 + .saturating_add(Weight::from_parts(6_299, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 5000]`. fn instr_i64lts(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_512_000 picoseconds. - Weight::from_parts(1_848_668, 0) + // Minimum execution time: 1_589_000 picoseconds. + Weight::from_parts(1_946_304, 0) // Standard Error: 2 - .saturating_add(Weight::from_parts(5_966, 0).saturating_mul(r.into())) + .saturating_add(Weight::from_parts(6_019, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 5000]`. fn instr_i64ltu(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_522_000 picoseconds. - Weight::from_parts(1_875_257, 0) + // Minimum execution time: 1_614_000 picoseconds. + Weight::from_parts(1_933_375, 0) // Standard Error: 2 - .saturating_add(Weight::from_parts(5_965, 0).saturating_mul(r.into())) + .saturating_add(Weight::from_parts(6_020, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 5000]`. fn instr_i64gts(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_546_000 picoseconds. - Weight::from_parts(1_836_691, 0) - // Standard Error: 4 - .saturating_add(Weight::from_parts(5_842, 0).saturating_mul(r.into())) + // Minimum execution time: 1_678_000 picoseconds. + Weight::from_parts(2_003_850, 0) + // Standard Error: 2 + .saturating_add(Weight::from_parts(5_816, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 5000]`. fn instr_i64gtu(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_505_000 picoseconds. - Weight::from_parts(1_907_551, 0) - // Standard Error: 2 - .saturating_add(Weight::from_parts(6_075, 0).saturating_mul(r.into())) + // Minimum execution time: 1_651_000 picoseconds. + Weight::from_parts(1_971_321, 0) + // Standard Error: 3 + .saturating_add(Weight::from_parts(6_114, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 5000]`. fn instr_i64les(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_527_000 picoseconds. - Weight::from_parts(1_891_008, 0) + // Minimum execution time: 1_647_000 picoseconds. + Weight::from_parts(2_017_232, 0) // Standard Error: 2 - .saturating_add(Weight::from_parts(5_971, 0).saturating_mul(r.into())) + .saturating_add(Weight::from_parts(5_990, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 5000]`. fn instr_i64leu(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_556_000 picoseconds. - Weight::from_parts(1_910_864, 0) - // Standard Error: 2 - .saturating_add(Weight::from_parts(6_059, 0).saturating_mul(r.into())) + // Minimum execution time: 1_635_000 picoseconds. + Weight::from_parts(3_232_848, 0) + // Standard Error: 105 + .saturating_add(Weight::from_parts(5_816, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 5000]`. fn instr_i64ges(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_544_000 picoseconds. - Weight::from_parts(1_912_650, 0) - // Standard Error: 2 - .saturating_add(Weight::from_parts(5_943, 0).saturating_mul(r.into())) + // Minimum execution time: 1_623_000 picoseconds. + Weight::from_parts(1_996_165, 0) + // Standard Error: 3 + .saturating_add(Weight::from_parts(5_964, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 5000]`. fn instr_i64geu(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_513_000 picoseconds. - Weight::from_parts(1_855_260, 0) - // Standard Error: 2 - .saturating_add(Weight::from_parts(5_975, 0).saturating_mul(r.into())) + // Minimum execution time: 1_668_000 picoseconds. + Weight::from_parts(1_973_238, 0) + // Standard Error: 20 + .saturating_add(Weight::from_parts(6_021, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 5000]`. fn instr_i64add(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_521_000 picoseconds. - Weight::from_parts(1_867_259, 0) - // Standard Error: 2 - .saturating_add(Weight::from_parts(5_846, 0).saturating_mul(r.into())) + // Minimum execution time: 1_674_000 picoseconds. + Weight::from_parts(1_981_762, 0) + // Standard Error: 3 + .saturating_add(Weight::from_parts(5_898, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 5000]`. fn instr_i64sub(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_509_000 picoseconds. - Weight::from_parts(1_893_018, 0) - // Standard Error: 2 - .saturating_add(Weight::from_parts(6_096, 0).saturating_mul(r.into())) + // Minimum execution time: 1_632_000 picoseconds. + Weight::from_parts(1_935_700, 0) + // Standard Error: 3 + .saturating_add(Weight::from_parts(6_154, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 5000]`. fn instr_i64mul(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_496_000 picoseconds. - Weight::from_parts(1_886_659, 0) + // Minimum execution time: 1_607_000 picoseconds. + Weight::from_parts(1_942_734, 0) // Standard Error: 2 - .saturating_add(Weight::from_parts(5_754, 0).saturating_mul(r.into())) + .saturating_add(Weight::from_parts(5_797, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 5000]`. fn instr_i64divs(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_527_000 picoseconds. - Weight::from_parts(1_890_548, 0) - // Standard Error: 2 - .saturating_add(Weight::from_parts(11_842, 0).saturating_mul(r.into())) + // Minimum execution time: 1_611_000 picoseconds. + Weight::from_parts(2_960_454, 0) + // Standard Error: 177 + .saturating_add(Weight::from_parts(11_666, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 5000]`. fn instr_i64divu(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_518_000 picoseconds. - Weight::from_parts(1_891_903, 0) - // Standard Error: 4 - .saturating_add(Weight::from_parts(10_613, 0).saturating_mul(r.into())) + // Minimum execution time: 1_641_000 picoseconds. + Weight::from_parts(2_104_200, 0) + // Standard Error: 5 + .saturating_add(Weight::from_parts(10_540, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 5000]`. fn instr_i64rems(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_504_000 picoseconds. - Weight::from_parts(1_632_694, 0) - // Standard Error: 7 - .saturating_add(Weight::from_parts(12_281, 0).saturating_mul(r.into())) + // Minimum execution time: 1_643_000 picoseconds. + Weight::from_parts(2_602_908, 0) + // Standard Error: 24 + .saturating_add(Weight::from_parts(11_900, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 5000]`. fn instr_i64remu(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_507_000 picoseconds. - Weight::from_parts(1_878_413, 0) - // Standard Error: 2 - .saturating_add(Weight::from_parts(10_737, 0).saturating_mul(r.into())) + // Minimum execution time: 1_584_000 picoseconds. + Weight::from_parts(2_056_817, 0) + // Standard Error: 3 + .saturating_add(Weight::from_parts(10_722, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 5000]`. fn instr_i64and(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_534_000 picoseconds. - Weight::from_parts(1_898_519, 0) - // Standard Error: 2 - .saturating_add(Weight::from_parts(5_645, 0).saturating_mul(r.into())) + // Minimum execution time: 1_652_000 picoseconds. + Weight::from_parts(1_988_892, 0) + // Standard Error: 4 + .saturating_add(Weight::from_parts(5_683, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 5000]`. fn instr_i64or(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_503_000 picoseconds. - Weight::from_parts(1_895_532, 0) - // Standard Error: 2 - .saturating_add(Weight::from_parts(5_745, 0).saturating_mul(r.into())) + // Minimum execution time: 1_660_000 picoseconds. + Weight::from_parts(2_148_537, 0) + // Standard Error: 38 + .saturating_add(Weight::from_parts(5_756, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 5000]`. fn instr_i64xor(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_507_000 picoseconds. - Weight::from_parts(1_868_720, 0) - // Standard Error: 2 - .saturating_add(Weight::from_parts(5_873, 0).saturating_mul(r.into())) + // Minimum execution time: 1_629_000 picoseconds. + Weight::from_parts(1_955_010, 0) + // Standard Error: 3 + .saturating_add(Weight::from_parts(5_931, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 5000]`. fn instr_i64shl(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_513_000 picoseconds. - Weight::from_parts(1_894_207, 0) + // Minimum execution time: 1_569_000 picoseconds. + Weight::from_parts(1_982_403, 0) // Standard Error: 2 - .saturating_add(Weight::from_parts(5_843, 0).saturating_mul(r.into())) + .saturating_add(Weight::from_parts(5_867, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 5000]`. fn instr_i64shrs(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_473_000 picoseconds. - Weight::from_parts(1_880_224, 0) - // Standard Error: 2 - .saturating_add(Weight::from_parts(6_107, 0).saturating_mul(r.into())) + // Minimum execution time: 1_615_000 picoseconds. + Weight::from_parts(1_989_920, 0) + // Standard Error: 3 + .saturating_add(Weight::from_parts(6_137, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 5000]`. fn instr_i64shru(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_447_000 picoseconds. - Weight::from_parts(1_884_551, 0) + // Minimum execution time: 1_646_000 picoseconds. + Weight::from_parts(2_020_935, 0) // Standard Error: 2 - .saturating_add(Weight::from_parts(5_849, 0).saturating_mul(r.into())) + .saturating_add(Weight::from_parts(5_863, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 5000]`. fn instr_i64rotl(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_538_000 picoseconds. - Weight::from_parts(1_908_813, 0) - // Standard Error: 2 - .saturating_add(Weight::from_parts(5_987, 0).saturating_mul(r.into())) + // Minimum execution time: 1_661_000 picoseconds. + Weight::from_parts(2_320_710, 0) + // Standard Error: 27 + .saturating_add(Weight::from_parts(5_922, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 5000]`. fn instr_i64rotr(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_538_000 picoseconds. - Weight::from_parts(1_878_015, 0) + // Minimum execution time: 1_674_000 picoseconds. + Weight::from_parts(2_044_188, 0) // Standard Error: 2 - .saturating_add(Weight::from_parts(5_848, 0).saturating_mul(r.into())) + .saturating_add(Weight::from_parts(5_855, 0).saturating_mul(r.into())) } } diff --git a/primitives/runtime/src/lib.rs b/primitives/runtime/src/lib.rs index 0994dc21b..e30146918 100644 --- a/primitives/runtime/src/lib.rs +++ b/primitives/runtime/src/lib.rs @@ -553,6 +553,8 @@ pub enum DispatchError { Corruption, /// Some resource (e.g. a preimage) is unavailable right now. This might fix itself later. Unavailable, + /// Root origin is not allowed. + RootNotAllowed, } /// Result of a `Dispatchable` which contains the `DispatchResult` and additional information about @@ -678,6 +680,7 @@ impl From for &'static str { Exhausted => "Resources exhausted", Corruption => "State corrupt", Unavailable => "Resource unavailable", + RootNotAllowed => "Root not allowed", } } } @@ -724,6 +727,7 @@ impl traits::Printable for DispatchError { Exhausted => "Resources exhausted".print(), Corruption => "State corrupt".print(), Unavailable => "Resource unavailable".print(), + RootNotAllowed => "Root not allowed".print(), } } } From 7212e91d59f3621d1dd53081d90e59a8d05c217e Mon Sep 17 00:00:00 2001 From: Mira Ressel Date: Tue, 2 May 2023 19:54:18 +0200 Subject: [PATCH 445/558] Bump clap to 4.2.5 (#14061) Co-authored-by: parity-processbot <> --- Cargo.lock | 206 ++++++++++++++---- bin/node-template/node/Cargo.toml | 2 +- bin/node/bench/Cargo.toml | 2 +- bin/node/cli/Cargo.toml | 4 +- bin/node/inspect/Cargo.toml | 2 +- bin/utils/chain-spec-builder/Cargo.toml | 2 +- bin/utils/subkey/Cargo.toml | 2 +- client/cli/Cargo.toml | 2 +- client/storage-monitor/Cargo.toml | 2 +- .../solution-type/fuzzer/Cargo.toml | 2 +- primitives/npos-elections/fuzzer/Cargo.toml | 2 +- utils/frame/benchmarking-cli/Cargo.toml | 2 +- utils/frame/frame-utilities-cli/Cargo.toml | 2 +- .../generate-bags/node-runtime/Cargo.toml | 2 +- utils/frame/try-runtime/cli/Cargo.toml | 2 +- 15 files changed, 184 insertions(+), 52 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index f20baae36..dc5b2576a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -203,12 +203,61 @@ dependencies = [ "winapi", ] +[[package]] +name = "anstream" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ca84f3628370c59db74ee214b3263d58f9aadd9b4fe7e711fd87dc452b7f163" +dependencies = [ + "anstyle 1.0.0", + "anstyle-parse", + "anstyle-query", + "anstyle-wincon", + "colorchoice", + "is-terminal", + "utf8parse", +] + [[package]] name = "anstyle" version = "0.3.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "23ea9e81bd02e310c216d080f6223c179012256e5151c41db88d12c88a1684d2" +[[package]] +name = "anstyle" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41ed9a86bf92ae6580e0a31281f65a1b1d867c0cc68d5346e2ae128dddfa6a7d" + +[[package]] +name = "anstyle-parse" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e765fd216e48e067936442276d1d57399e37bce53c264d6fefbe298080cb57ee" +dependencies = [ + "utf8parse", +] + +[[package]] +name = "anstyle-query" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5ca11d4be1bab0c8bc8734a9aa7bf4ee8316d462a08c6ac5052f888fef5b494b" +dependencies = [ + "windows-sys 0.48.0", +] + +[[package]] +name = "anstyle-wincon" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "180abfa45703aebe0093f79badacc01b8fd4ea2e35118747e5811127f926e188" +dependencies = [ + "anstyle 1.0.0", + "windows-sys 0.48.0", +] + [[package]] name = "anyhow" version = "1.0.69" @@ -339,7 +388,7 @@ version = "2.0.10" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ec0b2340f55d9661d76793b2bfc2eb0e62689bd79d067a95707ea762afd5e9dd" dependencies = [ - "anstyle", + "anstyle 0.3.5", "bstr", "doc-comment", "predicates 3.0.2", @@ -906,7 +955,7 @@ name = "chain-spec-builder" version = "2.0.0" dependencies = [ "ansi_term", - "clap 4.1.8", + "clap 4.2.5", "node-cli", "rand 0.8.5", "sc-chain-spec", @@ -1032,17 +1081,26 @@ dependencies = [ [[package]] name = "clap" -version = "4.1.8" +version = "4.2.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c3d7ae14b20b94cb02149ed21a86c423859cbe18dc7ed69845cace50e52b40a5" +checksum = "8a1f23fa97e1d1641371b51f35535cb26959b8e27ab50d167a8b996b5bada819" dependencies = [ - "bitflags", + "clap_builder", "clap_derive", - "clap_lex 0.3.2", - "is-terminal", "once_cell", +] + +[[package]] +name = "clap_builder" +version = "4.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fdc5d93c358224b4d6867ef1356d740de2303e9892edc06c5340daeccd96bab" +dependencies = [ + "anstream", + "anstyle 1.0.0", + "bitflags", + "clap_lex 0.4.1", "strsim", - "termcolor", ] [[package]] @@ -1051,20 +1109,19 @@ version = "4.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "501ff0a401473ea1d4c3b125ff95506b62c5bc5768d818634195fbb7c4ad5ff4" dependencies = [ - "clap 4.1.8", + "clap 4.2.5", ] [[package]] name = "clap_derive" -version = "4.1.8" +version = "4.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "44bec8e5c9d09e439c4335b1af0abaab56dcf3b94999a936e1bb47b9134288f0" +checksum = "3f9644cd56d6b87dbe899ef8b053e331c0637664e9e21a33dfcdc36093f5c5c4" dependencies = [ "heck", - "proc-macro-error", "proc-macro2", "quote", - "syn 1.0.109", + "syn 2.0.14", ] [[package]] @@ -1078,12 +1135,9 @@ dependencies = [ [[package]] name = "clap_lex" -version = "0.3.2" +version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "350b9cf31731f9957399229e9b2adc51eeabdfbe9d71d9a0552275fd12710d09" -dependencies = [ - "os_str_bytes", -] +checksum = "8a2dd5a6fe8c6e3502f568a6353e5273bbb15193ad9a89e457b9970798efbea1" [[package]] name = "codespan-reporting" @@ -1095,6 +1149,12 @@ dependencies = [ "unicode-width", ] +[[package]] +name = "colorchoice" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "acbf1af155f9b9ef647e42cdc158db4b64a1b61f743629225fde6f3e0be2a7c7" + [[package]] name = "comfy-table" version = "6.1.4" @@ -2297,7 +2357,7 @@ dependencies = [ "Inflector", "array-bytes", "chrono", - "clap 4.1.8", + "clap 4.2.5", "comfy-table", "frame-benchmarking", "frame-support", @@ -2388,7 +2448,7 @@ dependencies = [ name = "frame-election-solution-type-fuzzer" version = "2.0.0-alpha.5" dependencies = [ - "clap 4.1.8", + "clap 4.2.5", "frame-election-provider-solution-type", "frame-election-provider-support", "frame-support", @@ -4982,7 +5042,7 @@ name = "node-bench" version = "0.9.0-dev" dependencies = [ "array-bytes", - "clap 4.1.8", + "clap 4.2.5", "derive_more", "fs_extra", "futures", @@ -5019,7 +5079,7 @@ version = "3.0.0-dev" dependencies = [ "array-bytes", "assert_cmd", - "clap 4.1.8", + "clap 4.2.5", "clap_complete", "criterion", "frame-benchmarking-cli", @@ -5141,7 +5201,7 @@ dependencies = [ name = "node-inspect" version = "0.9.0-dev" dependencies = [ - "clap 4.1.8", + "clap 4.2.5", "parity-scale-codec", "sc-cli", "sc-client-api", @@ -5199,7 +5259,7 @@ dependencies = [ name = "node-runtime-generate-bags" version = "3.0.0" dependencies = [ - "clap 4.1.8", + "clap 4.2.5", "generate-bags", "kitchensink-runtime", ] @@ -5208,7 +5268,7 @@ dependencies = [ name = "node-template" version = "4.0.0-dev" dependencies = [ - "clap 4.1.8", + "clap 4.2.5", "frame-benchmarking", "frame-benchmarking-cli", "frame-system", @@ -7525,7 +7585,7 @@ version = "3.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c575290b64d24745b6c57a12a31465f0a66f3a4799686a6921526a33b0797965" dependencies = [ - "anstyle", + "anstyle 0.3.5", "difflib", "itertools", "predicates-core", @@ -8476,7 +8536,7 @@ version = "0.10.0-dev" dependencies = [ "array-bytes", "chrono", - "clap 4.1.8", + "clap 4.2.5", "fdlimit", "futures", "futures-timer", @@ -9590,7 +9650,7 @@ dependencies = [ name = "sc-storage-monitor" version = "0.1.0" dependencies = [ - "clap 4.1.8", + "clap 4.2.5", "fs4", "futures", "log", @@ -10700,7 +10760,7 @@ dependencies = [ name = "sp-npos-elections-fuzzer" version = "2.0.0-alpha.5" dependencies = [ - "clap 4.1.8", + "clap 4.2.5", "honggfuzz", "parity-scale-codec", "rand 0.8.5", @@ -11189,7 +11249,7 @@ dependencies = [ name = "subkey" version = "3.0.0" dependencies = [ - "clap 4.1.8", + "clap 4.2.5", "sc-cli", ] @@ -11231,7 +11291,7 @@ dependencies = [ name = "substrate-frame-cli" version = "4.0.0-dev" dependencies = [ - "clap 4.1.8", + "clap 4.2.5", "frame-support", "frame-system", "sc-cli", @@ -12114,7 +12174,7 @@ version = "0.10.0-dev" dependencies = [ "assert_cmd", "async-trait", - "clap 4.1.8", + "clap 4.2.5", "frame-remote-externalities", "frame-try-runtime", "hex", @@ -12307,6 +12367,12 @@ dependencies = [ "percent-encoding", ] +[[package]] +name = "utf8parse" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "711b9620af191e0cdc7468a8d14e709c3dcdb115b36f838e601583af800a370a" + [[package]] name = "uuid" version = "1.3.0" @@ -13172,12 +13238,12 @@ version = "0.42.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5a3e1820f08b8513f676f7ab6c1f99ff312fb97b553d30ff4dd86f9f15728aa7" dependencies = [ - "windows_aarch64_gnullvm", + "windows_aarch64_gnullvm 0.42.1", "windows_aarch64_msvc 0.42.1", "windows_i686_gnu 0.42.1", "windows_i686_msvc 0.42.1", "windows_x86_64_gnu 0.42.1", - "windows_x86_64_gnullvm", + "windows_x86_64_gnullvm 0.42.1", "windows_x86_64_msvc 0.42.1", ] @@ -13187,7 +13253,16 @@ version = "0.45.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "75283be5efb2831d37ea142365f009c02ec203cd29a3ebecbc093d52315b66d0" dependencies = [ - "windows-targets", + "windows-targets 0.42.1", +] + +[[package]] +name = "windows-sys" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9" +dependencies = [ + "windows-targets 0.48.0", ] [[package]] @@ -13196,21 +13271,42 @@ version = "0.42.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8e2522491fbfcd58cc84d47aeb2958948c4b8982e9a2d8a2a35bbaed431390e7" dependencies = [ - "windows_aarch64_gnullvm", + "windows_aarch64_gnullvm 0.42.1", "windows_aarch64_msvc 0.42.1", "windows_i686_gnu 0.42.1", "windows_i686_msvc 0.42.1", "windows_x86_64_gnu 0.42.1", - "windows_x86_64_gnullvm", + "windows_x86_64_gnullvm 0.42.1", "windows_x86_64_msvc 0.42.1", ] +[[package]] +name = "windows-targets" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7b1eb6f0cd7c80c79759c929114ef071b87354ce476d9d94271031c0497adfd5" +dependencies = [ + "windows_aarch64_gnullvm 0.48.0", + "windows_aarch64_msvc 0.48.0", + "windows_i686_gnu 0.48.0", + "windows_i686_msvc 0.48.0", + "windows_x86_64_gnu 0.48.0", + "windows_x86_64_gnullvm 0.48.0", + "windows_x86_64_msvc 0.48.0", +] + [[package]] name = "windows_aarch64_gnullvm" version = "0.42.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8c9864e83243fdec7fc9c5444389dcbbfd258f745e7853198f365e3c4968a608" +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "91ae572e1b79dba883e0d315474df7305d12f569b400fcf90581b06062f7e1bc" + [[package]] name = "windows_aarch64_msvc" version = "0.34.0" @@ -13223,6 +13319,12 @@ version = "0.42.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4c8b1b673ffc16c47a9ff48570a9d85e25d265735c503681332589af6253c6c7" +[[package]] +name = "windows_aarch64_msvc" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b2ef27e0d7bdfcfc7b868b317c1d32c641a6fe4629c171b8928c7b08d98d7cf3" + [[package]] name = "windows_i686_gnu" version = "0.34.0" @@ -13235,6 +13337,12 @@ version = "0.42.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "de3887528ad530ba7bdbb1faa8275ec7a1155a45ffa57c37993960277145d640" +[[package]] +name = "windows_i686_gnu" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "622a1962a7db830d6fd0a69683c80a18fda201879f0f447f065a3b7467daa241" + [[package]] name = "windows_i686_msvc" version = "0.34.0" @@ -13247,6 +13355,12 @@ version = "0.42.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bf4d1122317eddd6ff351aa852118a2418ad4214e6613a50e0191f7004372605" +[[package]] +name = "windows_i686_msvc" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4542c6e364ce21bf45d69fdd2a8e455fa38d316158cfd43b3ac1c5b1b19f8e00" + [[package]] name = "windows_x86_64_gnu" version = "0.34.0" @@ -13259,12 +13373,24 @@ version = "0.42.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c1040f221285e17ebccbc2591ffdc2d44ee1f9186324dd3e84e99ac68d699c45" +[[package]] +name = "windows_x86_64_gnu" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ca2b8a661f7628cbd23440e50b05d705db3686f894fc9580820623656af974b1" + [[package]] name = "windows_x86_64_gnullvm" version = "0.42.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "628bfdf232daa22b0d64fdb62b09fcc36bb01f05a3939e20ab73aaf9470d0463" +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7896dbc1f41e08872e9d5e8f8baa8fdd2677f29468c4e156210174edc7f7b953" + [[package]] name = "windows_x86_64_msvc" version = "0.34.0" @@ -13277,6 +13403,12 @@ version = "0.42.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "447660ad36a13288b1db4d4248e857b510e8c3a225c822ba4fb748c0aafecffd" +[[package]] +name = "windows_x86_64_msvc" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a515f5799fe4961cb532f983ce2b23082366b898e52ffbce459c86f67c8378a" + [[package]] name = "winnow" version = "0.4.1" diff --git a/bin/node-template/node/Cargo.toml b/bin/node-template/node/Cargo.toml index ac64b8881..42011075b 100644 --- a/bin/node-template/node/Cargo.toml +++ b/bin/node-template/node/Cargo.toml @@ -17,7 +17,7 @@ targets = ["x86_64-unknown-linux-gnu"] name = "node-template" [dependencies] -clap = { version = "4.0.9", features = ["derive"] } +clap = { version = "4.2.5", features = ["derive"] } futures = { version = "0.3.21", features = ["thread-pool"]} sc-cli = { version = "0.10.0-dev", path = "../../../client/cli" } diff --git a/bin/node/bench/Cargo.toml b/bin/node/bench/Cargo.toml index 30266ab0d..fa497a2fd 100644 --- a/bin/node/bench/Cargo.toml +++ b/bin/node/bench/Cargo.toml @@ -13,7 +13,7 @@ publish = false [dependencies] array-bytes = "4.1" -clap = { version = "4.0.9", features = ["derive"] } +clap = { version = "4.2.5", features = ["derive"] } log = "0.4.17" node-primitives = { version = "2.0.0", path = "../primitives" } node-testing = { version = "3.0.0-dev", path = "../testing" } diff --git a/bin/node/cli/Cargo.toml b/bin/node/cli/Cargo.toml index 77e49ff07..e37e2eb94 100644 --- a/bin/node/cli/Cargo.toml +++ b/bin/node/cli/Cargo.toml @@ -36,7 +36,7 @@ crate-type = ["cdylib", "rlib"] [dependencies] # third-party dependencies array-bytes = "4.1" -clap = { version = "4.0.9", features = ["derive"], optional = true } +clap = { version = "4.2.5", features = ["derive"], optional = true } codec = { package = "parity-scale-codec", version = "3.2.2" } serde = { version = "1.0.136", features = ["derive"] } jsonrpsee = { version = "0.16.2", features = ["server"] } @@ -130,7 +130,7 @@ pallet-timestamp = { version = "4.0.0-dev", path = "../../../frame/timestamp" } substrate-cli-test-utils = { path = "../../../test-utils/cli" } [build-dependencies] -clap = { version = "4.0.9", optional = true } +clap = { version = "4.2.5", optional = true } clap_complete = { version = "4.0.2", optional = true } node-inspect = { version = "0.9.0-dev", optional = true, path = "../inspect" } frame-benchmarking-cli = { version = "4.0.0-dev", optional = true, path = "../../../utils/frame/benchmarking-cli" } diff --git a/bin/node/inspect/Cargo.toml b/bin/node/inspect/Cargo.toml index d6a875517..f77c52aa5 100644 --- a/bin/node/inspect/Cargo.toml +++ b/bin/node/inspect/Cargo.toml @@ -13,7 +13,7 @@ publish = false targets = ["x86_64-unknown-linux-gnu"] [dependencies] -clap = { version = "4.0.9", features = ["derive"] } +clap = { version = "4.2.5", features = ["derive"] } codec = { package = "parity-scale-codec", version = "3.2.2" } thiserror = "1.0" sc-cli = { version = "0.10.0-dev", path = "../../../client/cli" } diff --git a/bin/utils/chain-spec-builder/Cargo.toml b/bin/utils/chain-spec-builder/Cargo.toml index e1d720f67..cb80963cc 100644 --- a/bin/utils/chain-spec-builder/Cargo.toml +++ b/bin/utils/chain-spec-builder/Cargo.toml @@ -15,7 +15,7 @@ targets = ["x86_64-unknown-linux-gnu"] [dependencies] ansi_term = "0.12.1" -clap = { version = "4.0.9", features = ["derive"] } +clap = { version = "4.2.5", features = ["derive"] } rand = "0.8" node-cli = { version = "3.0.0-dev", path = "../../node/cli" } sc-chain-spec = { version = "4.0.0-dev", path = "../../../client/chain-spec" } diff --git a/bin/utils/subkey/Cargo.toml b/bin/utils/subkey/Cargo.toml index 5d2d0f777..9e3abb804 100644 --- a/bin/utils/subkey/Cargo.toml +++ b/bin/utils/subkey/Cargo.toml @@ -17,5 +17,5 @@ path = "src/main.rs" name = "subkey" [dependencies] -clap = { version = "4.0.9", features = ["derive"] } +clap = { version = "4.2.5", features = ["derive"] } sc-cli = { version = "0.10.0-dev", path = "../../../client/cli" } diff --git a/client/cli/Cargo.toml b/client/cli/Cargo.toml index c0da53ad1..6f755a356 100644 --- a/client/cli/Cargo.toml +++ b/client/cli/Cargo.toml @@ -15,7 +15,7 @@ targets = ["x86_64-unknown-linux-gnu"] [dependencies] array-bytes = "4.1" chrono = "0.4.10" -clap = { version = "4.0.9", features = ["derive", "string"] } +clap = { version = "4.2.5", features = ["derive", "string"] } fdlimit = "0.2.1" futures = "0.3.21" libp2p = "0.50.0" diff --git a/client/storage-monitor/Cargo.toml b/client/storage-monitor/Cargo.toml index 52f34a296..5f6825502 100644 --- a/client/storage-monitor/Cargo.toml +++ b/client/storage-monitor/Cargo.toml @@ -9,7 +9,7 @@ description = "Storage monitor service for substrate" homepage = "https://substrate.io" [dependencies] -clap = { version = "4.0.9", features = ["derive", "string"] } +clap = { version = "4.2.5", features = ["derive", "string"] } futures = "0.3.21" log = "0.4.17" fs4 = "0.6.3" diff --git a/frame/election-provider-support/solution-type/fuzzer/Cargo.toml b/frame/election-provider-support/solution-type/fuzzer/Cargo.toml index 580e5eba9..3060b861e 100644 --- a/frame/election-provider-support/solution-type/fuzzer/Cargo.toml +++ b/frame/election-provider-support/solution-type/fuzzer/Cargo.toml @@ -13,7 +13,7 @@ publish = false targets = ["x86_64-unknown-linux-gnu"] [dependencies] -clap = { version = "4.0.9", features = ["derive"] } +clap = { version = "4.2.5", features = ["derive"] } honggfuzz = "0.5" rand = { version = "0.8", features = ["std", "small_rng"] } diff --git a/primitives/npos-elections/fuzzer/Cargo.toml b/primitives/npos-elections/fuzzer/Cargo.toml index d7cc68454..4caba796c 100644 --- a/primitives/npos-elections/fuzzer/Cargo.toml +++ b/primitives/npos-elections/fuzzer/Cargo.toml @@ -14,7 +14,7 @@ publish = false targets = ["x86_64-unknown-linux-gnu"] [dependencies] -clap = { version = "4.0.9", features = ["derive"] } +clap = { version = "4.2.5", features = ["derive"] } codec = { package = "parity-scale-codec", version = "3.2.2", default-features = false, features = ["derive"] } honggfuzz = "0.5" rand = { version = "0.8", features = ["std", "small_rng"] } diff --git a/utils/frame/benchmarking-cli/Cargo.toml b/utils/frame/benchmarking-cli/Cargo.toml index fc23d07b6..e3ea5682d 100644 --- a/utils/frame/benchmarking-cli/Cargo.toml +++ b/utils/frame/benchmarking-cli/Cargo.toml @@ -15,7 +15,7 @@ targets = ["x86_64-unknown-linux-gnu"] [dependencies] array-bytes = "4.1" chrono = "0.4" -clap = { version = "4.0.9", features = ["derive"] } +clap = { version = "4.2.5", features = ["derive"] } codec = { package = "parity-scale-codec", version = "3.2.2" } comfy-table = { version = "6.0.0", default-features = false } handlebars = "4.2.2" diff --git a/utils/frame/frame-utilities-cli/Cargo.toml b/utils/frame/frame-utilities-cli/Cargo.toml index 26e07c79d..3e736b7f5 100644 --- a/utils/frame/frame-utilities-cli/Cargo.toml +++ b/utils/frame/frame-utilities-cli/Cargo.toml @@ -11,7 +11,7 @@ documentation = "https://docs.rs/substrate-frame-cli" readme = "README.md" [dependencies] -clap = { version = "4.0.9", features = ["derive"] } +clap = { version = "4.2.5", features = ["derive"] } frame-support = { version = "4.0.0-dev", path = "../../../frame/support" } frame-system = { version = "4.0.0-dev", path = "../../../frame/system" } sc-cli = { version = "0.10.0-dev", path = "../../../client/cli" } diff --git a/utils/frame/generate-bags/node-runtime/Cargo.toml b/utils/frame/generate-bags/node-runtime/Cargo.toml index 46d999dab..373a31e79 100644 --- a/utils/frame/generate-bags/node-runtime/Cargo.toml +++ b/utils/frame/generate-bags/node-runtime/Cargo.toml @@ -14,4 +14,4 @@ kitchensink-runtime = { version = "3.0.0-dev", path = "../../../../bin/node/runt generate-bags = { version = "4.0.0-dev", path = "../" } # third-party -clap = { version = "4.0.9", features = ["derive"] } +clap = { version = "4.2.5", features = ["derive"] } diff --git a/utils/frame/try-runtime/cli/Cargo.toml b/utils/frame/try-runtime/cli/Cargo.toml index 686d27eef..7107cb15a 100644 --- a/utils/frame/try-runtime/cli/Cargo.toml +++ b/utils/frame/try-runtime/cli/Cargo.toml @@ -36,7 +36,7 @@ frame-try-runtime = { optional = true, path = "../../../../frame/try-runtime" } substrate-rpc-client = { path = "../../rpc/client" } async-trait = "0.1.57" -clap = { version = "4.0.9", features = ["derive"] } +clap = { version = "4.2.5", features = ["derive"] } hex = { version = "0.4.3", default-features = false } log = "0.4.17" parity-scale-codec = "3.2.2" From c800a2c0c0a19f2600dfca1e7ea4608e3494e944 Mon Sep 17 00:00:00 2001 From: Shunsuke Watanabe Date: Wed, 3 May 2023 05:40:46 +0900 Subject: [PATCH 446/558] Manual seal delayed finalize (#13999) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * up * up * added test * remove unncessary dep * cargo fmt * cargo fmt * up * Update client/consensus/manual-seal/src/lib.rs Co-authored-by: Bastian Köcher * fix test * cargo fmt * added docs * updated doc --------- Co-authored-by: Bastian Köcher Co-authored-by: parity-processbot <> --- Cargo.lock | 1 + client/consensus/manual-seal/Cargo.toml | 1 + client/consensus/manual-seal/src/lib.rs | 158 +++++++++++++++++++++++- client/consensus/manual-seal/src/rpc.rs | 7 +- 4 files changed, 160 insertions(+), 7 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index dc5b2576a..5c1d52e9e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -8920,6 +8920,7 @@ dependencies = [ "assert_matches", "async-trait", "futures", + "futures-timer", "jsonrpsee", "log", "parity-scale-codec", diff --git a/client/consensus/manual-seal/Cargo.toml b/client/consensus/manual-seal/Cargo.toml index 19c4b2224..2ddcf0d77 100644 --- a/client/consensus/manual-seal/Cargo.toml +++ b/client/consensus/manual-seal/Cargo.toml @@ -18,6 +18,7 @@ assert_matches = "1.3.0" async-trait = "0.1.57" codec = { package = "parity-scale-codec", version = "3.2.2" } futures = "0.3.21" +futures-timer = "3.0.1" log = "0.4.17" serde = { version = "1.0", features = ["derive"] } thiserror = "1.0" diff --git a/client/consensus/manual-seal/src/lib.rs b/client/consensus/manual-seal/src/lib.rs index b277b3436..03c9418b5 100644 --- a/client/consensus/manual-seal/src/lib.rs +++ b/client/consensus/manual-seal/src/lib.rs @@ -20,17 +20,22 @@ //! This is suitable for a testing environment. use futures::prelude::*; +use futures_timer::Delay; use prometheus_endpoint::Registry; -use sc_client_api::backend::{Backend as ClientBackend, Finalizer}; +use sc_client_api::{ + backend::{Backend as ClientBackend, Finalizer}, + client::BlockchainEvents, +}; use sc_consensus::{ block_import::{BlockImport, BlockImportParams, ForkChoiceStrategy}, import_queue::{BasicQueue, BoxBlockImport, Verifier}, }; use sp_blockchain::HeaderBackend; use sp_consensus::{Environment, Proposer, SelectChain}; +use sp_core::traits::SpawnNamed; use sp_inherents::CreateInherentDataProviders; use sp_runtime::{traits::Block as BlockT, ConsensusEngineId}; -use std::{marker::PhantomData, sync::Arc}; +use std::{marker::PhantomData, sync::Arc, time::Duration}; mod error; mod finalize_block; @@ -84,7 +89,7 @@ where /// Params required to start the instant sealing authorship task. pub struct ManualSealParams, TP, SC, CS, CIDP, P> { - /// Block import instance for well. importing blocks. + /// Block import instance. pub block_import: BI, /// The environment we are producing blocks for. @@ -136,7 +141,19 @@ pub struct InstantSealParams, TP, SC, pub create_inherent_data_providers: CIDP, } -/// Creates the background authorship task for the manual seal engine. +/// Params required to start the delayed finalization task. +pub struct DelayedFinalizeParams { + /// Block import instance. + pub client: Arc, + + /// Handle for spawning delayed finalization tasks. + pub spawn_handle: S, + + /// The delay in seconds before a block is finalized. + pub delay_sec: u64, +} + +/// Creates the background authorship task for the manually seal engine. pub async fn run_manual_seal( ManualSealParams { mut block_import, @@ -303,6 +320,44 @@ pub async fn run_instant_seal_and_finalize( .await } +/// Creates a future for delayed finalization of manual sealed blocks. +/// +/// The future needs to be spawned in the background alongside the +/// [`run_manual_seal`]/[`run_instant_seal`] future. It is required that +/// [`EngineCommand::SealNewBlock`] is send with `finalize = false` to not finalize blocks directly +/// after building them. This also means that delayed finality can not be used with +/// [`run_instant_seal_and_finalize`]. +pub async fn run_delayed_finalize( + DelayedFinalizeParams { client, spawn_handle, delay_sec }: DelayedFinalizeParams, +) where + B: BlockT + 'static, + CB: ClientBackend + 'static, + C: HeaderBackend + Finalizer + ProvideRuntimeApi + BlockchainEvents + 'static, + S: SpawnNamed, +{ + let mut block_import_stream = client.import_notification_stream(); + + while let Some(notification) = block_import_stream.next().await { + let delay = Delay::new(Duration::from_secs(delay_sec)); + let cloned_client = client.clone(); + spawn_handle.spawn( + "delayed-finalize", + None, + Box::pin(async move { + delay.await; + finalize_block(FinalizeBlockParams { + hash: notification.hash, + sender: None, + justification: None, + finalizer: cloned_client, + _phantom: PhantomData, + }) + .await + }), + ); + } +} + #[cfg(test)] mod tests { use super::*; @@ -428,6 +483,101 @@ mod tests { assert_eq!(client.header(created_block.hash).unwrap().unwrap().number, 1) } + #[tokio::test] + async fn instant_seal_delayed_finalize() { + let builder = TestClientBuilder::new(); + let (client, select_chain) = builder.build_with_longest_chain(); + let client = Arc::new(client); + let spawner = sp_core::testing::TaskExecutor::new(); + let genesis_hash = client.info().genesis_hash; + let pool = Arc::new(BasicPool::with_revalidation_type( + Options::default(), + true.into(), + api(), + None, + RevalidationType::Full, + spawner.clone(), + 0, + genesis_hash, + genesis_hash, + )); + let env = ProposerFactory::new(spawner.clone(), client.clone(), pool.clone(), None, None); + // this test checks that blocks are created as soon as transactions are imported into the + // pool. + let (sender, receiver) = futures::channel::oneshot::channel(); + let mut sender = Arc::new(Some(sender)); + let commands_stream = + pool.pool().validated_pool().import_notification_stream().map(move |_| { + // we're only going to submit one tx so this fn will only be called once. + let mut_sender = Arc::get_mut(&mut sender).unwrap(); + let sender = std::mem::take(mut_sender); + EngineCommand::SealNewBlock { + create_empty: false, + // set to `false`, expecting to be finalized by delayed finalize + finalize: false, + parent_hash: None, + sender, + } + }); + + let future_instant_seal = run_manual_seal(ManualSealParams { + block_import: client.clone(), + commands_stream, + env, + client: client.clone(), + pool: pool.clone(), + select_chain, + create_inherent_data_providers: |_, _| async { Ok(()) }, + consensus_data_provider: None, + }); + std::thread::spawn(|| { + let rt = tokio::runtime::Runtime::new().unwrap(); + // spawn the background authorship task + rt.block_on(future_instant_seal); + }); + + let delay_sec = 5; + let future_delayed_finalize = run_delayed_finalize(DelayedFinalizeParams { + client: client.clone(), + delay_sec, + spawn_handle: spawner, + }); + std::thread::spawn(|| { + let rt = tokio::runtime::Runtime::new().unwrap(); + // spawn the background authorship task + rt.block_on(future_delayed_finalize); + }); + + let mut finality_stream = client.finality_notification_stream(); + // submit a transaction to pool. + let result = pool.submit_one(&BlockId::Number(0), SOURCE, uxt(Alice, 0)).await; + // assert that it was successfully imported + assert!(result.is_ok()); + // assert that the background task returns ok + let created_block = receiver.await.unwrap().unwrap(); + assert_eq!( + created_block, + CreatedBlock { + hash: created_block.hash, + aux: ImportedAux { + header_only: false, + clear_justification_requests: false, + needs_justification: false, + bad_justification: false, + is_new_best: true, + } + } + ); + // assert that there's a new block in the db. + assert!(client.header(created_block.hash).unwrap().is_some()); + assert_eq!(client.header(created_block.hash).unwrap().unwrap().number, 1); + + assert_eq!(client.info().finalized_hash, client.info().genesis_hash); + + let finalized = finality_stream.select_next_some().await; + assert_eq!(finalized.hash, created_block.hash); + } + #[tokio::test] async fn manual_seal_and_finalization() { let builder = TestClientBuilder::new(); diff --git a/client/consensus/manual-seal/src/rpc.rs b/client/consensus/manual-seal/src/rpc.rs index db92b9fd2..85abcdc08 100644 --- a/client/consensus/manual-seal/src/rpc.rs +++ b/client/consensus/manual-seal/src/rpc.rs @@ -160,10 +160,11 @@ pub fn send_result( } } } else { - // instant seal doesn't report errors over rpc, simply log them. + // Sealing/Finalization with no RPC sender such as instant seal or delayed finalize doesn't + // report errors over rpc, simply log them. match result { - Ok(r) => log::info!("Instant Seal success: {:?}", r), - Err(e) => log::error!("Instant Seal encountered an error: {}", e), + Ok(r) => log::info!("Consensus with no RPC sender success: {:?}", r), + Err(e) => log::error!("Consensus with no RPC sender encountered an error: {}", e), } } } From f78bccc907792b769fcaf0ac2750f5ca9cc7b32b Mon Sep 17 00:00:00 2001 From: Mira Ressel Date: Wed, 3 May 2023 12:47:22 +0200 Subject: [PATCH 447/558] Don't run `check-crates-publishing` job on prs (#14064) * Restore `check-crates-publishing-pipeline` job This job was errorneously deleted in lieu of the `check-crates-publishing` job. This reverts commit de80d0107336a9c7a2efdc0199015e4d67fcbdb5. * Don't run `check-crate-publishing` job on prs. Originally attempted in #14044. It's been broken for a while and we're not sure yet how to move forward in the longer term. --- .gitlab-ci.yml | 12 ++++++++++++ scripts/ci/gitlab/pipeline/publish.yml | 2 +- 2 files changed, 13 insertions(+), 1 deletion(-) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index f9b4763c1..508f082d8 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -251,6 +251,18 @@ default: #### stage: .pre +check-crates-publishing-pipeline: + stage: .pre + extends: + - .kubernetes-env + - .crates-publishing-pipeline + script: + - git clone + --depth 1 + --branch "$RELENG_SCRIPTS_BRANCH" + https://github.com/paritytech/releng-scripts.git + - ONLY_CHECK_PIPELINE=true ./releng-scripts/publish-crates + # By default our pipelines are interruptible, but some special pipelines shouldn't be interrupted: # * multi-project pipelines such as the ones triggered by the scripts repo # * the scheduled automatic-crate-publishing pipeline diff --git a/scripts/ci/gitlab/pipeline/publish.yml b/scripts/ci/gitlab/pipeline/publish.yml index 5a69e876a..c90af7ba3 100644 --- a/scripts/ci/gitlab/pipeline/publish.yml +++ b/scripts/ci/gitlab/pipeline/publish.yml @@ -254,8 +254,8 @@ publish-crates-manual: check-crate-publishing: stage: publish extends: - - .test-refs - .crates-publishing-template + - .crates-publishing-pipeline # When lots of crates are taken into account (for example on master where all crates are tested) # the job might take a long time, as evidenced by: # https://gitlab.parity.io/parity/mirrors/substrate/-/jobs/2269364 From 5a81bad33e3cd7ceb0b5f91bb47f76e06af2f614 Mon Sep 17 00:00:00 2001 From: Sebastian Kunert Date: Wed, 3 May 2023 13:41:51 +0200 Subject: [PATCH 448/558] Only calculate tree route during finalization when there are multiple leaves (#14067) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Only calculate tree route when there are multiple leaves * Update client/service/src/client/client.rs Co-authored-by: Bastian Köcher --------- Co-authored-by: Bastian Köcher --- client/service/src/client/client.rs | 31 +++++++++++++++++------------ 1 file changed, 18 insertions(+), 13 deletions(-) diff --git a/client/service/src/client/client.rs b/client/service/src/client/client.rs index eee7e6b82..91c59cdba 100644 --- a/client/service/src/client/client.rs +++ b/client/service/src/client/client.rs @@ -936,19 +936,24 @@ where return Err(sp_blockchain::Error::NotInFinalizedChain) } - let route_from_best = - sp_blockchain::tree_route(self.backend.blockchain(), best_block, block)?; - - // if the block is not a direct ancestor of the current best chain, - // then some other block is the common ancestor. - if route_from_best.common_block().hash != block { - // NOTE: we're setting the finalized block as best block, this might - // be slightly inaccurate since we might have a "better" block - // further along this chain, but since best chain selection logic is - // plugable we cannot make a better choice here. usages that need - // an accurate "best" block need to go through `SelectChain` - // instead. - operation.op.mark_head(block)?; + // If there is only one leaf, best block is guaranteed to be + // a descendant of the new finalized block. If not, + // we need to check. + if self.backend.blockchain().leaves()?.len() > 1 { + let route_from_best = + sp_blockchain::tree_route(self.backend.blockchain(), best_block, block)?; + + // if the block is not a direct ancestor of the current best chain, + // then some other block is the common ancestor. + if route_from_best.common_block().hash != block { + // NOTE: we're setting the finalized block as best block, this might + // be slightly inaccurate since we might have a "better" block + // further along this chain, but since best chain selection logic is + // plugable we cannot make a better choice here. usages that need + // an accurate "best" block need to go through `SelectChain` + // instead. + operation.op.mark_head(block)?; + } } let enacted = route_from_finalized.enacted(); From 34301a960af3204ba005ceff213620e3b6684d28 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bastian=20K=C3=B6cher?= Date: Wed, 3 May 2023 15:15:49 +0200 Subject: [PATCH 449/558] test-staking-e2e: Add to main `Cargo.toml`. (#14062) This ensures that it is actually tested as part of the CI. There is also no need to have it separate. --- Cargo.lock | 25 + Cargo.toml | 1 + .../test-staking-e2e/Cargo.lock | 3609 ----------------- .../test-staking-e2e/Cargo.toml | 2 - .../test-staking-e2e/src/lib.rs | 4 +- .../test-staking-e2e/src/mock.rs | 4 + 6 files changed, 32 insertions(+), 3613 deletions(-) delete mode 100644 frame/election-provider-multi-phase/test-staking-e2e/Cargo.lock diff --git a/Cargo.lock b/Cargo.lock index 5c1d52e9e..42ad18552 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -6097,6 +6097,31 @@ dependencies = [ "sp-std", ] +[[package]] +name = "pallet-election-provider-e2e-test" +version = "1.0.0" +dependencies = [ + "frame-election-provider-support", + "frame-support", + "frame-system", + "log", + "pallet-bags-list", + "pallet-balances", + "pallet-election-provider-multi-phase", + "pallet-session", + "pallet-staking", + "pallet-timestamp", + "parity-scale-codec", + "scale-info", + "sp-core", + "sp-io", + "sp-npos-elections", + "sp-runtime", + "sp-staking", + "sp-std", + "sp-tracing", +] + [[package]] name = "pallet-election-provider-multi-phase" version = "4.0.0-dev" diff --git a/Cargo.toml b/Cargo.toml index 82e2264a3..2f3c0e946 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -100,6 +100,7 @@ members = [ "frame/try-runtime", "frame/elections-phragmen", "frame/election-provider-multi-phase", + "frame/election-provider-multi-phase/test-staking-e2e", "frame/election-provider-support", "frame/election-provider-support/benchmarking", "frame/election-provider-support/solution-type", diff --git a/frame/election-provider-multi-phase/test-staking-e2e/Cargo.lock b/frame/election-provider-multi-phase/test-staking-e2e/Cargo.lock deleted file mode 100644 index b179ab705..000000000 --- a/frame/election-provider-multi-phase/test-staking-e2e/Cargo.lock +++ /dev/null @@ -1,3609 +0,0 @@ -# This file is automatically @generated by Cargo. -# It is not intended for manual editing. -version = 3 - -[[package]] -name = "Inflector" -version = "0.11.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fe438c63458706e03479442743baae6c88256498e6431708f6dfc520a26515d3" -dependencies = [ - "lazy_static", - "regex", -] - -[[package]] -name = "addr2line" -version = "0.17.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b9ecd88a8c8378ca913a680cd98f0f13ac67383d35993f86c90a70e3f137816b" -dependencies = [ - "gimli", -] - -[[package]] -name = "adler" -version = "1.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" - -[[package]] -name = "ahash" -version = "0.7.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fcb51a0695d8f838b1ee009b3fbf66bda078cd64590202a864a8f3e8c4315c47" -dependencies = [ - "getrandom 0.2.8", - "once_cell", - "version_check", -] - -[[package]] -name = "ahash" -version = "0.8.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2c99f64d1e06488f620f932677e24bc6e2897582980441ae90a671415bd7ec2f" -dependencies = [ - "cfg-if", - "getrandom 0.2.8", - "once_cell", - "version_check", -] - -[[package]] -name = "aho-corasick" -version = "0.7.20" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cc936419f96fa211c1b9166887b38e5e40b19958e5b895be7c1f93adec7071ac" -dependencies = [ - "memchr", -] - -[[package]] -name = "android_system_properties" -version = "0.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "819e7219dbd41043ac279b19830f2efc897156490d7fd6ea916720117ee66311" -dependencies = [ - "libc", -] - -[[package]] -name = "ansi_term" -version = "0.12.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d52a9bb7ec0cf484c551830a7ce27bd20d67eac647e1befb56b0be4ee39a55d2" -dependencies = [ - "winapi", -] - -[[package]] -name = "anyhow" -version = "1.0.69" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "224afbd727c3d6e4b90103ece64b8d1b67fbb1973b1046c2281eed3f3803f800" - -[[package]] -name = "approx" -version = "0.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cab112f0a86d568ea0e627cc1d6be74a1e9cd55214684db5561995f6dad897c6" -dependencies = [ - "num-traits", -] - -[[package]] -name = "array-bytes" -version = "4.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f52f63c5c1316a16a4b35eaac8b76a98248961a533f061684cb2a7cb0eafb6c6" - -[[package]] -name = "arrayref" -version = "0.3.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a4c527152e37cf757a3f78aae5a06fbeefdb07ccc535c980a3208ee3060dd544" - -[[package]] -name = "arrayvec" -version = "0.5.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "23b62fc65de8e4e7f52534fb52b0f3ed04746ae267519eef2a83941e8085068b" - -[[package]] -name = "arrayvec" -version = "0.7.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8da52d66c7071e2e3fa2a1e5c6d088fec47b593032b254f5e980de8ea54454d6" - -[[package]] -name = "async-trait" -version = "0.1.59" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "31e6e93155431f3931513b243d371981bb2770112b370c82745a1d19d2f99364" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "autocfg" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" - -[[package]] -name = "backtrace" -version = "0.3.66" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cab84319d616cfb654d03394f38ab7e6f0919e181b1b57e1fd15e7fb4077d9a7" -dependencies = [ - "addr2line", - "cc", - "cfg-if", - "libc", - "miniz_oxide", - "object", - "rustc-demangle", -] - -[[package]] -name = "base16ct" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "349a06037c7bf932dd7e7d1f653678b2038b9ad46a74102f1fc7bd7872678cce" - -[[package]] -name = "base58" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6107fe1be6682a68940da878d9e9f5e90ca5745b3dec9fd1bb393c8777d4f581" - -[[package]] -name = "base64" -version = "0.13.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9e1b586273c5702936fe7b7d6896644d8be71e6314cfe09d3167c95f712589e8" - -[[package]] -name = "base64ct" -version = "1.5.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b645a089122eccb6111b4f81cbc1a49f5900ac4666bb93ac027feaecf15607bf" - -[[package]] -name = "bincode" -version = "1.3.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b1f45e9417d87227c7a56d22e471c6206462cba514c7590c09aff4cf6d1ddcad" -dependencies = [ - "serde", -] - -[[package]] -name = "bitflags" -version = "1.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" - -[[package]] -name = "bitvec" -version = "1.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1bc2832c24239b0141d5674bb9174f9d68a8b5b3f2753311927c172ca46f7e9c" -dependencies = [ - "funty", - "radium", - "tap", - "wyz", -] - -[[package]] -name = "blake2" -version = "0.10.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b12e5fd123190ce1c2e559308a94c9bacad77907d4c6005d9e58fe1a0689e55e" -dependencies = [ - "digest 0.10.6", -] - -[[package]] -name = "blake2b_simd" -version = "1.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3c2f0dc9a68c6317d884f97cc36cf5a3d20ba14ce404227df55e1af708ab04bc" -dependencies = [ - "arrayref", - "arrayvec 0.7.2", - "constant_time_eq", -] - -[[package]] -name = "block-buffer" -version = "0.7.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c0940dc441f31689269e10ac70eb1002a3a1d3ad1390e030043662eb7fe4688b" -dependencies = [ - "block-padding", - "byte-tools", - "byteorder", - "generic-array 0.12.4", -] - -[[package]] -name = "block-buffer" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4152116fd6e9dadb291ae18fc1ec3575ed6d84c29642d97890f4b4a3417297e4" -dependencies = [ - "generic-array 0.14.6", -] - -[[package]] -name = "block-buffer" -version = "0.10.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "69cce20737498f97b993470a6e536b8523f0af7892a4f928cceb1ac5e52ebe7e" -dependencies = [ - "generic-array 0.14.6", -] - -[[package]] -name = "block-padding" -version = "0.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fa79dedbb091f449f1f39e53edf88d5dbe95f895dae6135a8d7b881fb5af73f5" -dependencies = [ - "byte-tools", -] - -[[package]] -name = "bounded-collections" -version = "0.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a071c348a5ef6da1d3a87166b408170b46002382b1dda83992b5c2208cefb370" -dependencies = [ - "log", - "parity-scale-codec", - "scale-info", - "serde", -] - -[[package]] -name = "bumpalo" -version = "3.11.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "572f695136211188308f16ad2ca5c851a712c464060ae6974944458eb83880ba" - -[[package]] -name = "byte-slice-cast" -version = "1.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c3ac9f8b63eca6fd385229b3675f6cc0dc5c8a5c8a54a59d4f52ffd670d87b0c" - -[[package]] -name = "byte-tools" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e3b5ca7a04898ad4bcd41c90c5285445ff5b791899bb1b0abdd2a2aa791211d7" - -[[package]] -name = "bytemuck" -version = "1.13.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "17febce684fd15d89027105661fec94afb475cb995fbc59d2865198446ba2eea" - -[[package]] -name = "byteorder" -version = "1.4.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610" - -[[package]] -name = "bytes" -version = "1.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dfb24e866b15a1af2a1b663f10c6b6b8f397a84aadb828f12e5b289ec23a3a3c" - -[[package]] -name = "cc" -version = "1.0.77" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e9f73505338f7d905b19d18738976aae232eb46b8efc15554ffc56deb5d9ebe4" - -[[package]] -name = "cfg-expr" -version = "0.10.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0aacacf4d96c24b2ad6eb8ee6df040e4f27b0d0b39a5710c30091baa830485db" -dependencies = [ - "smallvec", -] - -[[package]] -name = "cfg-if" -version = "1.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" - -[[package]] -name = "chrono" -version = "0.4.23" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "16b0a3d9ed01224b22057780a37bb8c5dbfe1be8ba48678e7bf57ec4b385411f" -dependencies = [ - "iana-time-zone", - "num-integer", - "num-traits", - "winapi", -] - -[[package]] -name = "codespan-reporting" -version = "0.11.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3538270d33cc669650c4b093848450d380def10c331d38c768e34cac80576e6e" -dependencies = [ - "termcolor", - "unicode-width", -] - -[[package]] -name = "const-oid" -version = "0.9.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cec318a675afcb6a1ea1d4340e2d377e56e47c266f28043ceccbf4412ddfdd3b" - -[[package]] -name = "constant_time_eq" -version = "0.2.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "13418e745008f7349ec7e449155f419a61b92b58a99cc3616942b926825ec76b" - -[[package]] -name = "core-foundation-sys" -version = "0.8.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5827cebf4670468b8772dd191856768aedcb1b0278a04f989f7766351917b9dc" - -[[package]] -name = "cpp_demangle" -version = "0.3.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eeaa953eaad386a53111e47172c2fedba671e5684c8dd601a5f474f4f118710f" -dependencies = [ - "cfg-if", -] - -[[package]] -name = "cpufeatures" -version = "0.2.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "28d997bd5e24a5928dd43e46dc529867e207907fe0b239c3477d924f7f2ca320" -dependencies = [ - "libc", -] - -[[package]] -name = "cranelift-entity" -version = "0.93.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7cf583f7b093f291005f9fb1323e2c37f6ee4c7909e39ce016b2e8360d461705" -dependencies = [ - "serde", -] - -[[package]] -name = "crc32fast" -version = "1.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b540bd8bc810d3885c6ea91e2018302f68baba2129ab3e88f32389ee9370880d" -dependencies = [ - "cfg-if", -] - -[[package]] -name = "crunchy" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a81dae078cea95a014a339291cec439d2f232ebe854a9d672b796c6afafa9b7" - -[[package]] -name = "crypto-bigint" -version = "0.4.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ef2b4b23cddf68b89b8f8069890e8c270d54e2d5fe1b143820234805e4cb17ef" -dependencies = [ - "generic-array 0.14.6", - "rand_core 0.6.4", - "subtle", - "zeroize", -] - -[[package]] -name = "crypto-common" -version = "0.1.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3" -dependencies = [ - "generic-array 0.14.6", - "typenum", -] - -[[package]] -name = "crypto-mac" -version = "0.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b584a330336237c1eecd3e94266efb216c56ed91225d634cb2991c5f3fd1aeab" -dependencies = [ - "generic-array 0.14.6", - "subtle", -] - -[[package]] -name = "crypto-mac" -version = "0.11.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b1d1a86f49236c215f271d40892d5fc950490551400b02ef360692c29815c714" -dependencies = [ - "generic-array 0.14.6", - "subtle", -] - -[[package]] -name = "curve25519-dalek" -version = "2.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4a9b85542f99a2dfa2a1b8e192662741c9859a846b296bef1c92ef9b58b5a216" -dependencies = [ - "byteorder", - "digest 0.8.1", - "rand_core 0.5.1", - "subtle", - "zeroize", -] - -[[package]] -name = "curve25519-dalek" -version = "3.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0b9fdf9972b2bd6af2d913799d9ebc165ea4d2e65878e329d9c6b372c4491b61" -dependencies = [ - "byteorder", - "digest 0.9.0", - "rand_core 0.5.1", - "subtle", - "zeroize", -] - -[[package]] -name = "cxx" -version = "1.0.83" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bdf07d07d6531bfcdbe9b8b739b104610c6508dcc4d63b410585faf338241daf" -dependencies = [ - "cc", - "cxxbridge-flags", - "cxxbridge-macro", - "link-cplusplus", -] - -[[package]] -name = "cxx-build" -version = "1.0.83" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d2eb5b96ecdc99f72657332953d4d9c50135af1bac34277801cc3937906ebd39" -dependencies = [ - "cc", - "codespan-reporting", - "once_cell", - "proc-macro2", - "quote", - "scratch", - "syn", -] - -[[package]] -name = "cxxbridge-flags" -version = "1.0.83" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ac040a39517fd1674e0f32177648334b0f4074625b5588a64519804ba0553b12" - -[[package]] -name = "cxxbridge-macro" -version = "1.0.83" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1362b0ddcfc4eb0a1f57b68bd77dd99f0e826958a96abd0ae9bd092e114ffed6" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "der" -version = "0.6.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f1a467a65c5e759bce6e65eaf91cc29f466cdc57cb65777bd646872a8a1fd4de" -dependencies = [ - "const-oid", - "zeroize", -] - -[[package]] -name = "derive-syn-parse" -version = "0.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e79116f119dd1dba1abf1f3405f03b9b0e79a27a3883864bfebded8a3dc768cd" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "derive_more" -version = "0.99.17" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4fb810d30a7c1953f91334de7244731fc3f3c10d7fe163338a35b9f640960321" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "digest" -version = "0.8.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f3d0c8c8752312f9713efd397ff63acb9f85585afbf179282e720e7704954dd5" -dependencies = [ - "generic-array 0.12.4", -] - -[[package]] -name = "digest" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d3dd60d1080a57a05ab032377049e0591415d2b31afd7028356dbf3cc6dcb066" -dependencies = [ - "generic-array 0.14.6", -] - -[[package]] -name = "digest" -version = "0.10.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8168378f4e5023e7218c89c891c0fd8ecdb5e5e4f18cb78f38cf245dd021e76f" -dependencies = [ - "block-buffer 0.10.3", - "crypto-common", - "subtle", -] - -[[package]] -name = "downcast-rs" -version = "1.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9ea835d29036a4087793836fa931b08837ad5e957da9e23886b29586fb9b6650" - -[[package]] -name = "dyn-clonable" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4e9232f0e607a262ceb9bd5141a3dfb3e4db6994b31989bbfd845878cba59fd4" -dependencies = [ - "dyn-clonable-impl", - "dyn-clone", -] - -[[package]] -name = "dyn-clonable-impl" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "558e40ea573c374cf53507fd240b7ee2f5477df7cfebdb97323ec61c719399c5" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "dyn-clone" -version = "1.0.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4f94fa09c2aeea5b8839e414b7b841bf429fd25b9c522116ac97ee87856d88b2" - -[[package]] -name = "ecdsa" -version = "0.14.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "413301934810f597c1d19ca71c8710e99a3f1ba28a0d2ebc01551a2daeea3c5c" -dependencies = [ - "der", - "elliptic-curve", - "rfc6979", - "signature", -] - -[[package]] -name = "ed25519" -version = "1.5.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e9c280362032ea4203659fc489832d0204ef09f247a0506f170dafcac08c369" -dependencies = [ - "signature", -] - -[[package]] -name = "ed25519-dalek" -version = "1.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c762bae6dcaf24c4c84667b8579785430908723d5c889f469d76a41d59cc7a9d" -dependencies = [ - "curve25519-dalek 3.2.0", - "ed25519", - "sha2 0.9.9", - "zeroize", -] - -[[package]] -name = "ed25519-zebra" -version = "3.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7c24f403d068ad0b359e577a77f92392118be3f3c927538f2bb544a5ecd828c6" -dependencies = [ - "curve25519-dalek 3.2.0", - "hashbrown 0.12.3", - "hex", - "rand_core 0.6.4", - "sha2 0.9.9", - "zeroize", -] - -[[package]] -name = "either" -version = "1.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "90e5c1c8368803113bf0c9584fc495a58b86dc8a29edbf8fe877d21d9507e797" - -[[package]] -name = "elliptic-curve" -version = "0.12.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e7bb888ab5300a19b8e5bceef25ac745ad065f3c9f7efc6de1b91958110891d3" -dependencies = [ - "base16ct", - "crypto-bigint", - "der", - "digest 0.10.6", - "ff", - "generic-array 0.14.6", - "group", - "rand_core 0.6.4", - "sec1", - "subtle", - "zeroize", -] - -[[package]] -name = "environmental" -version = "1.1.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e48c92028aaa870e83d51c64e5d4e0b6981b360c522198c23959f219a4e1b15b" - -[[package]] -name = "errno" -version = "0.2.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f639046355ee4f37944e44f60642c6f3a7efa3cf6b78c78a0d989a8ce6c396a1" -dependencies = [ - "errno-dragonfly", - "libc", - "winapi", -] - -[[package]] -name = "errno-dragonfly" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "aa68f1b12764fab894d2755d2518754e71b4fd80ecfb822714a1206c2aab39bf" -dependencies = [ - "cc", - "libc", -] - -[[package]] -name = "expander" -version = "1.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f360349150728553f92e4c997a16af8915f418d3a0f21b440d34c5632f16ed84" -dependencies = [ - "blake2", - "fs-err", - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "fake-simd" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e88a8acf291dafb59c2d96e8f59828f3838bb1a70398823ade51a84de6a6deed" - -[[package]] -name = "fallible-iterator" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4443176a9f2c162692bd3d352d745ef9413eec5782a80d8fd6f8a1ac692a07f7" - -[[package]] -name = "ff" -version = "0.12.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d013fc25338cc558c5c2cfbad646908fb23591e2404481826742b651c9af7160" -dependencies = [ - "rand_core 0.6.4", - "subtle", -] - -[[package]] -name = "fixed-hash" -version = "0.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "835c052cb0c08c1acf6ffd71c022172e18723949c8282f2b9f27efbc51e64534" -dependencies = [ - "byteorder", - "rand 0.8.5", - "rustc-hex", - "static_assertions", -] - -[[package]] -name = "form_urlencoded" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a9c384f161156f5260c24a097c56119f9be8c798586aecc13afbcbe7b7e26bf8" -dependencies = [ - "percent-encoding", -] - -[[package]] -name = "frame-benchmarking" -version = "4.0.0-dev" -dependencies = [ - "frame-support", - "frame-support-procedural", - "frame-system", - "linregress", - "log", - "parity-scale-codec", - "paste", - "scale-info", - "serde", - "sp-api", - "sp-application-crypto", - "sp-core", - "sp-io", - "sp-runtime", - "sp-runtime-interface", - "sp-std", - "sp-storage", - "static_assertions", -] - -[[package]] -name = "frame-election-provider-solution-type" -version = "4.0.0-dev" -dependencies = [ - "proc-macro-crate", - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "frame-election-provider-support" -version = "4.0.0-dev" -dependencies = [ - "frame-election-provider-solution-type", - "frame-support", - "frame-system", - "parity-scale-codec", - "scale-info", - "sp-arithmetic", - "sp-core", - "sp-npos-elections", - "sp-runtime", - "sp-std", -] - -[[package]] -name = "frame-metadata" -version = "15.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "df6bb8542ef006ef0de09a5c4420787d79823c0ed7924225822362fd2bf2ff2d" -dependencies = [ - "cfg-if", - "parity-scale-codec", - "scale-info", - "serde", -] - -[[package]] -name = "frame-support" -version = "4.0.0-dev" -dependencies = [ - "bitflags", - "environmental", - "frame-metadata", - "frame-support-procedural", - "impl-trait-for-tuples", - "k256", - "log", - "once_cell", - "parity-scale-codec", - "paste", - "scale-info", - "serde", - "smallvec", - "sp-api", - "sp-arithmetic", - "sp-core", - "sp-core-hashing-proc-macro", - "sp-inherents", - "sp-io", - "sp-runtime", - "sp-staking", - "sp-state-machine", - "sp-std", - "sp-tracing", - "sp-weights", - "tt-call", -] - -[[package]] -name = "frame-support-procedural" -version = "4.0.0-dev" -dependencies = [ - "Inflector", - "cfg-expr", - "derive-syn-parse", - "frame-support-procedural-tools", - "itertools", - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "frame-support-procedural-tools" -version = "4.0.0-dev" -dependencies = [ - "frame-support-procedural-tools-derive", - "proc-macro-crate", - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "frame-support-procedural-tools-derive" -version = "3.0.0" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "frame-system" -version = "4.0.0-dev" -dependencies = [ - "frame-support", - "log", - "parity-scale-codec", - "scale-info", - "serde", - "sp-core", - "sp-io", - "sp-runtime", - "sp-std", - "sp-version", - "sp-weights", -] - -[[package]] -name = "fs-err" -version = "2.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0845fa252299212f0389d64ba26f34fa32cfe41588355f21ed507c59a0f64541" - -[[package]] -name = "funty" -version = "2.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e6d5a32815ae3f33302d95fdcb2ce17862f8c65363dcfd29360480ba1001fc9c" - -[[package]] -name = "futures" -version = "0.3.25" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "38390104763dc37a5145a53c29c63c1290b5d316d6086ec32c293f6736051bb0" -dependencies = [ - "futures-channel", - "futures-core", - "futures-executor", - "futures-io", - "futures-sink", - "futures-task", - "futures-util", -] - -[[package]] -name = "futures-channel" -version = "0.3.25" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "52ba265a92256105f45b719605a571ffe2d1f0fea3807304b522c1d778f79eed" -dependencies = [ - "futures-core", - "futures-sink", -] - -[[package]] -name = "futures-core" -version = "0.3.25" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "04909a7a7e4633ae6c4a9ab280aeb86da1236243a77b694a49eacd659a4bd3ac" - -[[package]] -name = "futures-executor" -version = "0.3.25" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7acc85df6714c176ab5edf386123fafe217be88c0840ec11f199441134a074e2" -dependencies = [ - "futures-core", - "futures-task", - "futures-util", - "num_cpus", -] - -[[package]] -name = "futures-io" -version = "0.3.25" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "00f5fb52a06bdcadeb54e8d3671f8888a39697dcb0b81b23b55174030427f4eb" - -[[package]] -name = "futures-macro" -version = "0.3.25" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bdfb8ce053d86b91919aad980c220b1fb8401a9394410e1c289ed7e66b61835d" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "futures-sink" -version = "0.3.25" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "39c15cf1a4aa79df40f1bb462fb39676d0ad9e366c2a33b590d7c66f4f81fcf9" - -[[package]] -name = "futures-task" -version = "0.3.25" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2ffb393ac5d9a6eaa9d3fdf37ae2776656b706e200c8e16b1bdb227f5198e6ea" - -[[package]] -name = "futures-timer" -version = "3.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e64b03909df88034c26dc1547e8970b91f98bdb65165d6a4e9110d94263dbb2c" - -[[package]] -name = "futures-util" -version = "0.3.25" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "197676987abd2f9cadff84926f410af1c183608d36641465df73ae8211dc65d6" -dependencies = [ - "futures-channel", - "futures-core", - "futures-io", - "futures-macro", - "futures-sink", - "futures-task", - "memchr", - "pin-project-lite", - "pin-utils", - "slab", -] - -[[package]] -name = "generic-array" -version = "0.12.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ffdf9f34f1447443d37393cc6c2b8313aebddcd96906caf34e54c68d8e57d7bd" -dependencies = [ - "typenum", -] - -[[package]] -name = "generic-array" -version = "0.14.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bff49e947297f3312447abdca79f45f4738097cc82b06e72054d2223f601f1b9" -dependencies = [ - "typenum", - "version_check", -] - -[[package]] -name = "getrandom" -version = "0.1.16" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8fc3cb4d91f53b50155bdcfd23f6a4c39ae1969c2ae85982b135750cccaf5fce" -dependencies = [ - "cfg-if", - "libc", - "wasi 0.9.0+wasi-snapshot-preview1", -] - -[[package]] -name = "getrandom" -version = "0.2.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c05aeb6a22b8f62540c194aac980f2115af067bfe15a0734d7277a768d396b31" -dependencies = [ - "cfg-if", - "libc", - "wasi 0.11.0+wasi-snapshot-preview1", -] - -[[package]] -name = "gimli" -version = "0.26.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "22030e2c5a68ec659fde1e949a745124b48e6fa8b045b7ed5bd1fe4ccc5c4e5d" -dependencies = [ - "fallible-iterator", - "stable_deref_trait", -] - -[[package]] -name = "group" -version = "0.12.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5dfbfb3a6cfbd390d5c9564ab283a0349b9b9fcd46a706c1eb10e0db70bfbac7" -dependencies = [ - "ff", - "rand_core 0.6.4", - "subtle", -] - -[[package]] -name = "hash-db" -version = "0.16.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8e7d7786361d7425ae2fe4f9e407eb0efaa0840f5212d109cc018c40c35c6ab4" - -[[package]] -name = "hash256-std-hasher" -version = "0.15.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "92c171d55b98633f4ed3860808f004099b36c1cc29c42cfc53aa8591b21efcf2" -dependencies = [ - "crunchy", -] - -[[package]] -name = "hashbrown" -version = "0.12.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" -dependencies = [ - "ahash 0.7.6", -] - -[[package]] -name = "hashbrown" -version = "0.13.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "43a3c133739dddd0d2990f9a4bdf8eb4b21ef50e4851ca85ab661199821d510e" -dependencies = [ - "ahash 0.8.3", -] - -[[package]] -name = "heck" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2540771e65fc8cb83cd6e8a237f70c319bd5c29f78ed1084ba5d50eeac86f7f9" - -[[package]] -name = "hermit-abi" -version = "0.1.19" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "62b467343b94ba476dcb2500d242dadbb39557df889310ac77c5d99100aaac33" -dependencies = [ - "libc", -] - -[[package]] -name = "hex" -version = "0.4.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" - -[[package]] -name = "hmac" -version = "0.8.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "126888268dcc288495a26bf004b38c5fdbb31682f992c84ceb046a1f0fe38840" -dependencies = [ - "crypto-mac 0.8.0", - "digest 0.9.0", -] - -[[package]] -name = "hmac" -version = "0.11.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2a2a2320eb7ec0ebe8da8f744d7812d9fc4cb4d09344ac01898dbcb6a20ae69b" -dependencies = [ - "crypto-mac 0.11.1", - "digest 0.9.0", -] - -[[package]] -name = "hmac" -version = "0.12.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6c49c37c09c17a53d937dfbb742eb3a961d65a994e6bcdcf37e7399d0cc8ab5e" -dependencies = [ - "digest 0.10.6", -] - -[[package]] -name = "hmac-drbg" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "17ea0a1394df5b6574da6e0c1ade9e78868c9fb0a4e5ef4428e32da4676b85b1" -dependencies = [ - "digest 0.9.0", - "generic-array 0.14.6", - "hmac 0.8.1", -] - -[[package]] -name = "iana-time-zone" -version = "0.1.53" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "64c122667b287044802d6ce17ee2ddf13207ed924c712de9a66a5814d5b64765" -dependencies = [ - "android_system_properties", - "core-foundation-sys", - "iana-time-zone-haiku", - "js-sys", - "wasm-bindgen", - "winapi", -] - -[[package]] -name = "iana-time-zone-haiku" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0703ae284fc167426161c2e3f1da3ea71d94b21bedbcc9494e92b28e334e3dca" -dependencies = [ - "cxx", - "cxx-build", -] - -[[package]] -name = "idna" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e14ddfc70884202db2244c223200c204c2bda1bc6e0998d11b5e024d657209e6" -dependencies = [ - "unicode-bidi", - "unicode-normalization", -] - -[[package]] -name = "impl-codec" -version = "0.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ba6a270039626615617f3f36d15fc827041df3b78c439da2cadfa47455a77f2f" -dependencies = [ - "parity-scale-codec", -] - -[[package]] -name = "impl-serde" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ebc88fc67028ae3db0c853baa36269d398d5f45b6982f95549ff5def78c935cd" -dependencies = [ - "serde", -] - -[[package]] -name = "impl-trait-for-tuples" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "11d7a9f6330b71fea57921c9b61c47ee6e84f72d394754eff6163ae67e7395eb" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "indexmap" -version = "1.9.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1885e79c1fc4b10f0e172c475f458b7f7b93061064d98c3293e98c5ba0c8b399" -dependencies = [ - "autocfg", - "hashbrown 0.12.3", - "serde", -] - -[[package]] -name = "integer-sqrt" -version = "0.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "276ec31bcb4a9ee45f58bec6f9ec700ae4cf4f4f8f2fa7e06cb406bd5ffdd770" -dependencies = [ - "num-traits", -] - -[[package]] -name = "io-lifetimes" -version = "1.0.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cfa919a82ea574332e2de6e74b4c36e74d41982b335080fa59d4ef31be20fdf3" -dependencies = [ - "libc", - "windows-sys 0.45.0", -] - -[[package]] -name = "itertools" -version = "0.10.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b0fd2260e829bddf4cb6ea802289de2f86d6a7a690192fbe91b3f46e0f2c8473" -dependencies = [ - "either", -] - -[[package]] -name = "itoa" -version = "1.0.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4217ad341ebadf8d8e724e264f13e593e0648f5b3e94b3896a5df283be015ecc" - -[[package]] -name = "js-sys" -version = "0.3.60" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "49409df3e3bf0856b916e2ceaca09ee28e6871cf7d9ce97a692cacfdb2a25a47" -dependencies = [ - "wasm-bindgen", -] - -[[package]] -name = "k256" -version = "0.11.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "72c1e0b51e7ec0a97369623508396067a486bd0cbed95a2659a4b863d28cfc8b" -dependencies = [ - "cfg-if", - "ecdsa", - "elliptic-curve", - "sha2 0.10.6", -] - -[[package]] -name = "keccak" -version = "0.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3afef3b6eff9ce9d8ff9b3601125eec7f0c8cbac7abd14f355d053fa56c98768" -dependencies = [ - "cpufeatures", -] - -[[package]] -name = "lazy_static" -version = "1.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" - -[[package]] -name = "libc" -version = "0.2.138" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "db6d7e329c562c5dfab7a46a2afabc8b987ab9a4834c9d1ca04dc54c1546cef8" - -[[package]] -name = "libm" -version = "0.2.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "348108ab3fba42ec82ff6e9564fc4ca0247bdccdc68dd8af9764bbc79c3c8ffb" - -[[package]] -name = "libsecp256k1" -version = "0.7.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "95b09eff1b35ed3b33b877ced3a691fc7a481919c7e29c53c906226fcf55e2a1" -dependencies = [ - "arrayref", - "base64", - "digest 0.9.0", - "hmac-drbg", - "libsecp256k1-core", - "libsecp256k1-gen-ecmult", - "libsecp256k1-gen-genmult", - "rand 0.8.5", - "serde", - "sha2 0.9.9", - "typenum", -] - -[[package]] -name = "libsecp256k1-core" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5be9b9bb642d8522a44d533eab56c16c738301965504753b03ad1de3425d5451" -dependencies = [ - "crunchy", - "digest 0.9.0", - "subtle", -] - -[[package]] -name = "libsecp256k1-gen-ecmult" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3038c808c55c87e8a172643a7d87187fc6c4174468159cb3090659d55bcb4809" -dependencies = [ - "libsecp256k1-core", -] - -[[package]] -name = "libsecp256k1-gen-genmult" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3db8d6ba2cec9eacc40e6e8ccc98931840301f1006e95647ceb2dd5c3aa06f7c" -dependencies = [ - "libsecp256k1-core", -] - -[[package]] -name = "link-cplusplus" -version = "1.0.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9272ab7b96c9046fbc5bc56c06c117cb639fe2d509df0c421cad82d2915cf369" -dependencies = [ - "cc", -] - -[[package]] -name = "linregress" -version = "0.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "475015a7f8f017edb28d2e69813be23500ad4b32cfe3421c4148efc97324ee52" -dependencies = [ - "nalgebra", -] - -[[package]] -name = "linux-raw-sys" -version = "0.1.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f051f77a7c8e6957c0696eac88f26b0117e54f52d3fc682ab19397a8812846a4" - -[[package]] -name = "lock_api" -version = "0.4.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "435011366fe56583b16cf956f9df0095b405b82d76425bc8981c0e22e60ec4df" -dependencies = [ - "autocfg", - "scopeguard", -] - -[[package]] -name = "log" -version = "0.4.17" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "abb12e687cfb44aa40f41fc3978ef76448f9b6038cad6aef4259d3c095a2382e" -dependencies = [ - "cfg-if", -] - -[[package]] -name = "mach" -version = "0.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b823e83b2affd8f40a9ee8c29dbc56404c1e34cd2710921f2801e2cf29527afa" -dependencies = [ - "libc", -] - -[[package]] -name = "matchers" -version = "0.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f099785f7595cc4b4553a174ce30dd7589ef93391ff414dbb67f62392b9e0ce1" -dependencies = [ - "regex-automata", -] - -[[package]] -name = "matrixmultiply" -version = "0.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "add85d4dd35074e6fedc608f8c8f513a3548619a9024b751949ef0e8e45a4d84" -dependencies = [ - "rawpointer", -] - -[[package]] -name = "memchr" -version = "2.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d" - -[[package]] -name = "memfd" -version = "0.6.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b20a59d985586e4a5aef64564ac77299f8586d8be6cf9106a5a40207e8908efb" -dependencies = [ - "rustix", -] - -[[package]] -name = "memoffset" -version = "0.6.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5aa361d4faea93603064a027415f07bd8e1d5c88c9fbf68bf56a285428fd79ce" -dependencies = [ - "autocfg", -] - -[[package]] -name = "memory-db" -version = "0.32.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "808b50db46293432a45e63bc15ea51e0ab4c0a1647b8eb114e31a3e698dd6fbe" -dependencies = [ - "hash-db", -] - -[[package]] -name = "memory_units" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8452105ba047068f40ff7093dd1d9da90898e63dd61736462e9cdda6a90ad3c3" - -[[package]] -name = "merlin" -version = "2.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4e261cf0f8b3c42ded9f7d2bb59dea03aa52bc8a1cbc7482f9fc3fd1229d3b42" -dependencies = [ - "byteorder", - "keccak", - "rand_core 0.5.1", - "zeroize", -] - -[[package]] -name = "miniz_oxide" -version = "0.5.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "96590ba8f175222643a85693f33d26e9c8a015f599c216509b1a6894af675d34" -dependencies = [ - "adler", -] - -[[package]] -name = "nalgebra" -version = "0.32.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d68d47bba83f9e2006d117a9a33af1524e655516b8919caac694427a6fb1e511" -dependencies = [ - "approx", - "matrixmultiply", - "nalgebra-macros", - "num-complex", - "num-rational", - "num-traits", - "simba", - "typenum", -] - -[[package]] -name = "nalgebra-macros" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d232c68884c0c99810a5a4d333ef7e47689cfd0edc85efc9e54e1e6bf5212766" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "nohash-hasher" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2bf50223579dc7cdcfb3bfcacf7069ff68243f8c363f62ffa99cf000a6b9c451" - -[[package]] -name = "num-bigint" -version = "0.4.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f93ab6289c7b344a8a9f60f88d80aa20032336fe78da341afc91c8a2341fc75f" -dependencies = [ - "autocfg", - "num-integer", - "num-traits", -] - -[[package]] -name = "num-complex" -version = "0.4.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7ae39348c8bc5fbd7f40c727a9925f03517afd2ab27d46702108b6a7e5414c19" -dependencies = [ - "num-traits", -] - -[[package]] -name = "num-format" -version = "0.4.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a652d9771a63711fd3c3deb670acfbe5c30a4072e664d7a3bf5a9e1056ac72c3" -dependencies = [ - "arrayvec 0.7.2", - "itoa", -] - -[[package]] -name = "num-integer" -version = "0.1.45" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "225d3389fb3509a24c93f5c29eb6bde2586b98d9f016636dff58d7c6f7569cd9" -dependencies = [ - "autocfg", - "num-traits", -] - -[[package]] -name = "num-rational" -version = "0.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0638a1c9d0a3c0914158145bc76cff373a75a627e6ecbfb71cbe6f453a5a19b0" -dependencies = [ - "autocfg", - "num-bigint", - "num-integer", - "num-traits", -] - -[[package]] -name = "num-traits" -version = "0.2.15" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "578ede34cf02f8924ab9447f50c28075b4d3e5b269972345e7e0372b38c6cdcd" -dependencies = [ - "autocfg", -] - -[[package]] -name = "num_cpus" -version = "1.14.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f6058e64324c71e02bc2b150e4f3bc8286db6c83092132ffa3f6b1eab0f9def5" -dependencies = [ - "hermit-abi", - "libc", -] - -[[package]] -name = "object" -version = "0.29.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "21158b2c33aa6d4561f1c0a6ea283ca92bc54802a93b263e910746d679a7eb53" -dependencies = [ - "crc32fast", - "hashbrown 0.12.3", - "indexmap", - "memchr", -] - -[[package]] -name = "once_cell" -version = "1.16.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "86f0b0d4bf799edbc74508c1e8bf170ff5f41238e5f8225603ca7caaae2b7860" - -[[package]] -name = "opaque-debug" -version = "0.2.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2839e79665f131bdb5782e51f2c6c9599c133c6098982a54c794358bf432529c" - -[[package]] -name = "opaque-debug" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "624a8340c38c1b80fd549087862da4ba43e08858af025b236e509b6649fc13d5" - -[[package]] -name = "pallet-authorship" -version = "4.0.0-dev" -dependencies = [ - "frame-support", - "frame-system", - "impl-trait-for-tuples", - "parity-scale-codec", - "scale-info", - "sp-runtime", - "sp-std", -] - -[[package]] -name = "pallet-bags-list" -version = "4.0.0-dev" -dependencies = [ - "frame-benchmarking", - "frame-election-provider-support", - "frame-support", - "frame-system", - "log", - "pallet-balances", - "parity-scale-codec", - "scale-info", - "sp-core", - "sp-io", - "sp-runtime", - "sp-std", - "sp-tracing", -] - -[[package]] -name = "pallet-balances" -version = "4.0.0-dev" -dependencies = [ - "frame-benchmarking", - "frame-support", - "frame-system", - "log", - "parity-scale-codec", - "scale-info", - "sp-runtime", - "sp-std", -] - -[[package]] -name = "pallet-election-provider-e2e-test" -version = "1.0.0" -dependencies = [ - "frame-election-provider-support", - "frame-support", - "frame-system", - "log", - "pallet-bags-list", - "pallet-balances", - "pallet-election-provider-multi-phase", - "pallet-session", - "pallet-staking", - "pallet-timestamp", - "parity-scale-codec", - "scale-info", - "sp-core", - "sp-io", - "sp-npos-elections", - "sp-runtime", - "sp-staking", - "sp-std", - "sp-tracing", -] - -[[package]] -name = "pallet-election-provider-multi-phase" -version = "4.0.0-dev" -dependencies = [ - "frame-benchmarking", - "frame-election-provider-support", - "frame-support", - "frame-system", - "log", - "pallet-election-provider-support-benchmarking", - "parity-scale-codec", - "rand 0.8.5", - "scale-info", - "sp-arithmetic", - "sp-core", - "sp-io", - "sp-npos-elections", - "sp-runtime", - "sp-std", - "strum", -] - -[[package]] -name = "pallet-election-provider-support-benchmarking" -version = "4.0.0-dev" -dependencies = [ - "frame-benchmarking", - "frame-election-provider-support", - "frame-system", - "parity-scale-codec", - "sp-npos-elections", - "sp-runtime", -] - -[[package]] -name = "pallet-session" -version = "4.0.0-dev" -dependencies = [ - "frame-support", - "frame-system", - "impl-trait-for-tuples", - "log", - "pallet-timestamp", - "parity-scale-codec", - "scale-info", - "sp-core", - "sp-io", - "sp-runtime", - "sp-session", - "sp-staking", - "sp-std", - "sp-trie", -] - -[[package]] -name = "pallet-staking" -version = "4.0.0-dev" -dependencies = [ - "frame-benchmarking", - "frame-election-provider-support", - "frame-support", - "frame-system", - "log", - "pallet-authorship", - "pallet-session", - "parity-scale-codec", - "scale-info", - "serde", - "sp-application-crypto", - "sp-io", - "sp-runtime", - "sp-staking", - "sp-std", -] - -[[package]] -name = "pallet-timestamp" -version = "4.0.0-dev" -dependencies = [ - "frame-benchmarking", - "frame-support", - "frame-system", - "log", - "parity-scale-codec", - "scale-info", - "sp-inherents", - "sp-io", - "sp-runtime", - "sp-std", - "sp-timestamp", -] - -[[package]] -name = "parity-scale-codec" -version = "3.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "637935964ff85a605d114591d4d2c13c5d1ba2806dae97cea6bf180238a749ac" -dependencies = [ - "arrayvec 0.7.2", - "bitvec", - "byte-slice-cast", - "bytes", - "impl-trait-for-tuples", - "parity-scale-codec-derive", - "serde", -] - -[[package]] -name = "parity-scale-codec-derive" -version = "3.1.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "86b26a931f824dd4eca30b3e43bb4f31cd5f0d3a403c5f5ff27106b805bfde7b" -dependencies = [ - "proc-macro-crate", - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "parity-wasm" -version = "0.45.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e1ad0aff30c1da14b1254fcb2af73e1fa9a28670e584a626f53a369d0e157304" - -[[package]] -name = "parking_lot" -version = "0.12.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3742b2c103b9f06bc9fff0a37ff4912935851bee6d36f3c02bcc755bcfec228f" -dependencies = [ - "lock_api", - "parking_lot_core", -] - -[[package]] -name = "parking_lot_core" -version = "0.9.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7ff9f3fef3968a3ec5945535ed654cb38ff72d7495a25619e2247fb15a2ed9ba" -dependencies = [ - "cfg-if", - "libc", - "redox_syscall", - "smallvec", - "windows-sys 0.42.0", -] - -[[package]] -name = "paste" -version = "1.0.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cf1c2c742266c2f1041c914ba65355a83ae8747b05f208319784083583494b4b" - -[[package]] -name = "pbkdf2" -version = "0.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d95f5254224e617595d2cc3cc73ff0a5eaf2637519e25f03388154e9378b6ffa" -dependencies = [ - "crypto-mac 0.11.1", -] - -[[package]] -name = "pbkdf2" -version = "0.11.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "83a0692ec44e4cf1ef28ca317f14f8f07da2d95ec3fa01f86e4467b725e60917" -dependencies = [ - "digest 0.10.6", -] - -[[package]] -name = "percent-encoding" -version = "2.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "478c572c3d73181ff3c2539045f6eb99e5491218eae919370993b890cdbdd98e" - -[[package]] -name = "pin-project-lite" -version = "0.2.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e0a7ae3ac2f1173085d398531c705756c94a4c56843785df85a60c1a0afac116" - -[[package]] -name = "pin-utils" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" - -[[package]] -name = "pkcs8" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9eca2c590a5f85da82668fa685c09ce2888b9430e83299debf1f34b65fd4a4ba" -dependencies = [ - "der", - "spki", -] - -[[package]] -name = "ppv-lite86" -version = "0.2.17" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de" - -[[package]] -name = "primitive-types" -version = "0.12.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9f3486ccba82358b11a77516035647c34ba167dfa53312630de83b12bd4f3d66" -dependencies = [ - "fixed-hash", - "impl-codec", - "impl-serde", - "scale-info", - "uint", -] - -[[package]] -name = "proc-macro-crate" -version = "1.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eda0fc3b0fb7c975631757e14d9049da17374063edb6ebbcbc54d880d4fe94e9" -dependencies = [ - "once_cell", - "thiserror", - "toml", -] - -[[package]] -name = "proc-macro2" -version = "1.0.47" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5ea3d908b0e36316caf9e9e2c4625cdde190a7e6f440d794667ed17a1855e725" -dependencies = [ - "unicode-ident", -] - -[[package]] -name = "psm" -version = "0.1.21" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5787f7cda34e3033a72192c018bc5883100330f362ef279a8cbccfce8bb4e874" -dependencies = [ - "cc", -] - -[[package]] -name = "quote" -version = "1.0.21" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bbe448f377a7d6961e30f5955f9b8d106c3f5e449d493ee1b125c1d43c2b5179" -dependencies = [ - "proc-macro2", -] - -[[package]] -name = "radium" -version = "0.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dc33ff2d4973d518d823d61aa239014831e521c75da58e3df4840d3f47749d09" - -[[package]] -name = "rand" -version = "0.7.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6a6b1679d49b24bbfe0c803429aa1874472f50d9b363131f0e89fc356b544d03" -dependencies = [ - "getrandom 0.1.16", - "libc", - "rand_chacha 0.2.2", - "rand_core 0.5.1", - "rand_hc", -] - -[[package]] -name = "rand" -version = "0.8.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" -dependencies = [ - "libc", - "rand_chacha 0.3.1", - "rand_core 0.6.4", -] - -[[package]] -name = "rand_chacha" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f4c8ed856279c9737206bf725bf36935d8666ead7aa69b52be55af369d193402" -dependencies = [ - "ppv-lite86", - "rand_core 0.5.1", -] - -[[package]] -name = "rand_chacha" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" -dependencies = [ - "ppv-lite86", - "rand_core 0.6.4", -] - -[[package]] -name = "rand_core" -version = "0.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "90bde5296fc891b0cef12a6d03ddccc162ce7b2aff54160af9338f8d40df6d19" -dependencies = [ - "getrandom 0.1.16", -] - -[[package]] -name = "rand_core" -version = "0.6.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" -dependencies = [ - "getrandom 0.2.8", -] - -[[package]] -name = "rand_hc" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ca3129af7b92a17112d59ad498c6f81eaf463253766b90396d39ea7a39d6613c" -dependencies = [ - "rand_core 0.5.1", -] - -[[package]] -name = "rawpointer" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "60a357793950651c4ed0f3f52338f53b2f809f32d83a07f72909fa13e4c6c1e3" - -[[package]] -name = "redox_syscall" -version = "0.2.16" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fb5a58c1855b4b6819d59012155603f0b22ad30cad752600aadfcb695265519a" -dependencies = [ - "bitflags", -] - -[[package]] -name = "ref-cast" -version = "1.0.13" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "53b15debb4f9d60d767cd8ca9ef7abb2452922f3214671ff052defc7f3502c44" -dependencies = [ - "ref-cast-impl", -] - -[[package]] -name = "ref-cast-impl" -version = "1.0.13" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "abfa8511e9e94fd3de6585a3d3cd00e01ed556dc9814829280af0e8dc72a8f36" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "regex" -version = "1.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e076559ef8e241f2ae3479e36f97bd5741c0330689e217ad51ce2c76808b868a" -dependencies = [ - "aho-corasick", - "memchr", - "regex-syntax", -] - -[[package]] -name = "regex-automata" -version = "0.1.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6c230d73fb8d8c1b9c0b3135c5142a8acee3a0558fb8db5cf1cb65f8d7862132" -dependencies = [ - "regex-syntax", -] - -[[package]] -name = "regex-syntax" -version = "0.6.28" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "456c603be3e8d448b072f410900c09faf164fbce2d480456f50eea6e25f9c848" - -[[package]] -name = "rfc6979" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7743f17af12fa0b03b803ba12cd6a8d9483a587e89c69445e3909655c0b9fabb" -dependencies = [ - "crypto-bigint", - "hmac 0.12.1", - "zeroize", -] - -[[package]] -name = "rustc-demangle" -version = "0.1.21" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7ef03e0a2b150c7a90d01faf6254c9c48a41e95fb2a8c2ac1c6f0d2b9aefc342" - -[[package]] -name = "rustc-hash" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2" - -[[package]] -name = "rustc-hex" -version = "2.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3e75f6a532d0fd9f7f13144f392b6ad56a32696bfcd9c78f797f16bbb6f072d6" - -[[package]] -name = "rustix" -version = "0.36.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fd5c6ff11fecd55b40746d1995a02f2eb375bf8c00d192d521ee09f42bef37bc" -dependencies = [ - "bitflags", - "errno", - "io-lifetimes", - "libc", - "linux-raw-sys", - "windows-sys 0.45.0", -] - -[[package]] -name = "rustversion" -version = "1.0.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "97477e48b4cf8603ad5f7aaf897467cf42ab4218a38ef76fb14c2d6773a6d6a8" - -[[package]] -name = "ryu" -version = "1.0.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4501abdff3ae82a1c1b477a17252eb69cee9e66eb915c1abaa4f44d873df9f09" - -[[package]] -name = "safe_arch" -version = "0.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "794821e4ccb0d9f979512f9c1973480123f9bd62a90d74ab0f9426fcf8f4a529" -dependencies = [ - "bytemuck", -] - -[[package]] -name = "scale-info" -version = "2.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "001cf62ece89779fd16105b5f515ad0e5cedcd5440d3dd806bb067978e7c3608" -dependencies = [ - "bitvec", - "cfg-if", - "derive_more", - "parity-scale-codec", - "scale-info-derive", - "serde", -] - -[[package]] -name = "scale-info-derive" -version = "2.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "303959cf613a6f6efd19ed4b4ad5bf79966a13352716299ad532cfb115f4205c" -dependencies = [ - "proc-macro-crate", - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "schnellru" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "772575a524feeb803e5b0fcbc6dd9f367e579488197c94c6e4023aad2305774d" -dependencies = [ - "ahash 0.8.3", - "cfg-if", - "hashbrown 0.13.2", -] - -[[package]] -name = "schnorrkel" -version = "0.9.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "021b403afe70d81eea68f6ea12f6b3c9588e5d536a94c3bf80f15e7faa267862" -dependencies = [ - "arrayref", - "arrayvec 0.5.2", - "curve25519-dalek 2.1.3", - "getrandom 0.1.16", - "merlin", - "rand 0.7.3", - "rand_core 0.5.1", - "sha2 0.8.2", - "subtle", - "zeroize", -] - -[[package]] -name = "scopeguard" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd" - -[[package]] -name = "scratch" -version = "1.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c8132065adcfd6e02db789d9285a0deb2f3fcb04002865ab67d5fb103533898" - -[[package]] -name = "sec1" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3be24c1842290c45df0a7bf069e0c268a747ad05a192f2fd7dcfdbc1cba40928" -dependencies = [ - "base16ct", - "der", - "generic-array 0.14.6", - "pkcs8", - "subtle", - "zeroize", -] - -[[package]] -name = "secp256k1" -version = "0.24.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d9512ffd81e3a3503ed401f79c33168b9148c75038956039166cd750eaa037c3" -dependencies = [ - "secp256k1-sys", -] - -[[package]] -name = "secp256k1-sys" -version = "0.6.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "83080e2c2fc1006e625be82e5d1eb6a43b7fd9578b617fcc55814daf286bba4b" -dependencies = [ - "cc", -] - -[[package]] -name = "secrecy" -version = "0.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9bd1c54ea06cfd2f6b63219704de0b9b4f72dcc2b8fdef820be6cd799780e91e" -dependencies = [ - "zeroize", -] - -[[package]] -name = "serde" -version = "1.0.150" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e326c9ec8042f1b5da33252c8a37e9ffbd2c9bef0155215b6e6c80c790e05f91" -dependencies = [ - "serde_derive", -] - -[[package]] -name = "serde_derive" -version = "1.0.150" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "42a3df25b0713732468deadad63ab9da1f1fd75a48a15024b50363f128db627e" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "serde_json" -version = "1.0.89" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "020ff22c755c2ed3f8cf162dbb41a7268d934702f3ed3631656ea597e08fc3db" -dependencies = [ - "itoa", - "ryu", - "serde", -] - -[[package]] -name = "sha2" -version = "0.8.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a256f46ea78a0c0d9ff00077504903ac881a1dafdc20da66545699e7776b3e69" -dependencies = [ - "block-buffer 0.7.3", - "digest 0.8.1", - "fake-simd", - "opaque-debug 0.2.3", -] - -[[package]] -name = "sha2" -version = "0.9.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4d58a1e1bf39749807d89cf2d98ac2dfa0ff1cb3faa38fbb64dd88ac8013d800" -dependencies = [ - "block-buffer 0.9.0", - "cfg-if", - "cpufeatures", - "digest 0.9.0", - "opaque-debug 0.3.0", -] - -[[package]] -name = "sha2" -version = "0.10.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "82e6b795fe2e3b1e845bafcb27aa35405c4d47cdfc92af5fc8d3002f76cebdc0" -dependencies = [ - "cfg-if", - "cpufeatures", - "digest 0.10.6", -] - -[[package]] -name = "sha3" -version = "0.10.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bdf0c33fae925bdc080598b84bc15c55e7b9a4a43b3c704da051f977469691c9" -dependencies = [ - "digest 0.10.6", - "keccak", -] - -[[package]] -name = "sharded-slab" -version = "0.1.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "900fba806f70c630b0a382d0d825e17a0f19fcd059a2ade1ff237bcddf446b31" -dependencies = [ - "lazy_static", -] - -[[package]] -name = "signature" -version = "1.6.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "74233d3b3b2f6d4b006dc19dee745e73e2a6bfb6f93607cd3b02bd5b00797d7c" -dependencies = [ - "digest 0.10.6", - "rand_core 0.6.4", -] - -[[package]] -name = "simba" -version = "0.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "50582927ed6f77e4ac020c057f37a268fc6aebc29225050365aacbb9deeeddc4" -dependencies = [ - "approx", - "num-complex", - "num-traits", - "paste", - "wide", -] - -[[package]] -name = "slab" -version = "0.4.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4614a76b2a8be0058caa9dbbaf66d988527d86d003c11a94fbd335d7661edcef" -dependencies = [ - "autocfg", -] - -[[package]] -name = "smallvec" -version = "1.10.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a507befe795404456341dfab10cef66ead4c041f62b8b11bbb92bffe5d0953e0" - -[[package]] -name = "sp-api" -version = "4.0.0-dev" -dependencies = [ - "hash-db", - "log", - "parity-scale-codec", - "sp-api-proc-macro", - "sp-core", - "sp-runtime", - "sp-state-machine", - "sp-std", - "sp-trie", - "sp-version", - "thiserror", -] - -[[package]] -name = "sp-api-proc-macro" -version = "4.0.0-dev" -dependencies = [ - "Inflector", - "blake2", - "expander", - "proc-macro-crate", - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "sp-application-crypto" -version = "7.0.0" -dependencies = [ - "parity-scale-codec", - "scale-info", - "serde", - "sp-core", - "sp-io", - "sp-std", -] - -[[package]] -name = "sp-arithmetic" -version = "6.0.0" -dependencies = [ - "integer-sqrt", - "num-traits", - "parity-scale-codec", - "scale-info", - "serde", - "sp-std", - "static_assertions", -] - -[[package]] -name = "sp-core" -version = "7.0.0" -dependencies = [ - "array-bytes", - "base58", - "bitflags", - "blake2", - "bounded-collections", - "dyn-clonable", - "ed25519-zebra", - "futures", - "hash-db", - "hash256-std-hasher", - "impl-serde", - "lazy_static", - "libsecp256k1", - "log", - "merlin", - "parity-scale-codec", - "parking_lot", - "primitive-types", - "rand 0.8.5", - "regex", - "scale-info", - "schnorrkel", - "secp256k1", - "secrecy", - "serde", - "sp-core-hashing", - "sp-debug-derive", - "sp-externalities", - "sp-runtime-interface", - "sp-std", - "sp-storage", - "ss58-registry", - "substrate-bip39", - "thiserror", - "tiny-bip39", - "zeroize", -] - -[[package]] -name = "sp-core-hashing" -version = "5.0.0" -dependencies = [ - "blake2b_simd", - "byteorder", - "digest 0.10.6", - "sha2 0.10.6", - "sha3", - "sp-std", - "twox-hash", -] - -[[package]] -name = "sp-core-hashing-proc-macro" -version = "5.0.0" -dependencies = [ - "proc-macro2", - "quote", - "sp-core-hashing", - "syn", -] - -[[package]] -name = "sp-debug-derive" -version = "5.0.0" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "sp-externalities" -version = "0.13.0" -dependencies = [ - "environmental", - "parity-scale-codec", - "sp-std", - "sp-storage", -] - -[[package]] -name = "sp-inherents" -version = "4.0.0-dev" -dependencies = [ - "async-trait", - "impl-trait-for-tuples", - "parity-scale-codec", - "scale-info", - "sp-core", - "sp-runtime", - "sp-std", - "thiserror", -] - -[[package]] -name = "sp-io" -version = "7.0.0" -dependencies = [ - "bytes", - "ed25519", - "ed25519-dalek", - "futures", - "libsecp256k1", - "log", - "parity-scale-codec", - "secp256k1", - "sp-core", - "sp-externalities", - "sp-keystore", - "sp-runtime-interface", - "sp-state-machine", - "sp-std", - "sp-tracing", - "sp-trie", - "tracing", - "tracing-core", -] - -[[package]] -name = "sp-keystore" -version = "0.13.0" -dependencies = [ - "async-trait", - "futures", - "merlin", - "parity-scale-codec", - "parking_lot", - "schnorrkel", - "sp-core", - "sp-externalities", - "thiserror", -] - -[[package]] -name = "sp-npos-elections" -version = "4.0.0-dev" -dependencies = [ - "parity-scale-codec", - "scale-info", - "serde", - "sp-arithmetic", - "sp-core", - "sp-runtime", - "sp-std", -] - -[[package]] -name = "sp-panic-handler" -version = "5.0.0" -dependencies = [ - "backtrace", - "lazy_static", - "regex", -] - -[[package]] -name = "sp-runtime" -version = "7.0.0" -dependencies = [ - "either", - "hash256-std-hasher", - "impl-trait-for-tuples", - "log", - "parity-scale-codec", - "paste", - "rand 0.8.5", - "scale-info", - "serde", - "sp-application-crypto", - "sp-arithmetic", - "sp-core", - "sp-io", - "sp-std", - "sp-weights", -] - -[[package]] -name = "sp-runtime-interface" -version = "7.0.0" -dependencies = [ - "bytes", - "impl-trait-for-tuples", - "parity-scale-codec", - "primitive-types", - "sp-externalities", - "sp-runtime-interface-proc-macro", - "sp-std", - "sp-storage", - "sp-tracing", - "sp-wasm-interface", - "static_assertions", -] - -[[package]] -name = "sp-runtime-interface-proc-macro" -version = "6.0.0" -dependencies = [ - "Inflector", - "proc-macro-crate", - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "sp-session" -version = "4.0.0-dev" -dependencies = [ - "parity-scale-codec", - "scale-info", - "sp-api", - "sp-core", - "sp-runtime", - "sp-staking", - "sp-std", -] - -[[package]] -name = "sp-staking" -version = "4.0.0-dev" -dependencies = [ - "parity-scale-codec", - "scale-info", - "sp-core", - "sp-runtime", - "sp-std", -] - -[[package]] -name = "sp-state-machine" -version = "0.13.0" -dependencies = [ - "hash-db", - "log", - "parity-scale-codec", - "parking_lot", - "rand 0.8.5", - "smallvec", - "sp-core", - "sp-externalities", - "sp-panic-handler", - "sp-std", - "sp-trie", - "thiserror", - "tracing", -] - -[[package]] -name = "sp-std" -version = "5.0.0" - -[[package]] -name = "sp-storage" -version = "7.0.0" -dependencies = [ - "impl-serde", - "parity-scale-codec", - "ref-cast", - "serde", - "sp-debug-derive", - "sp-std", -] - -[[package]] -name = "sp-timestamp" -version = "4.0.0-dev" -dependencies = [ - "async-trait", - "futures-timer", - "log", - "parity-scale-codec", - "sp-inherents", - "sp-runtime", - "sp-std", - "thiserror", -] - -[[package]] -name = "sp-tracing" -version = "6.0.0" -dependencies = [ - "parity-scale-codec", - "sp-std", - "tracing", - "tracing-core", - "tracing-subscriber", -] - -[[package]] -name = "sp-trie" -version = "7.0.0" -dependencies = [ - "ahash 0.8.3", - "hash-db", - "hashbrown 0.12.3", - "lazy_static", - "memory-db", - "nohash-hasher", - "parity-scale-codec", - "parking_lot", - "scale-info", - "schnellru", - "sp-core", - "sp-std", - "thiserror", - "tracing", - "trie-db", - "trie-root", -] - -[[package]] -name = "sp-version" -version = "5.0.0" -dependencies = [ - "impl-serde", - "parity-scale-codec", - "parity-wasm", - "scale-info", - "serde", - "sp-core-hashing-proc-macro", - "sp-runtime", - "sp-std", - "sp-version-proc-macro", - "thiserror", -] - -[[package]] -name = "sp-version-proc-macro" -version = "4.0.0-dev" -dependencies = [ - "parity-scale-codec", - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "sp-wasm-interface" -version = "7.0.0" -dependencies = [ - "anyhow", - "impl-trait-for-tuples", - "log", - "parity-scale-codec", - "sp-std", - "wasmi", - "wasmtime", -] - -[[package]] -name = "sp-weights" -version = "4.0.0" -dependencies = [ - "parity-scale-codec", - "scale-info", - "serde", - "smallvec", - "sp-arithmetic", - "sp-core", - "sp-debug-derive", - "sp-std", -] - -[[package]] -name = "spki" -version = "0.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "67cf02bbac7a337dc36e4f5a693db6c21e7863f45070f7064577eb4367a3212b" -dependencies = [ - "base64ct", - "der", -] - -[[package]] -name = "ss58-registry" -version = "1.36.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "23d92659e7d18d82b803824a9ba5a6022cff101c3491d027c1c1d8d30e749284" -dependencies = [ - "Inflector", - "num-format", - "proc-macro2", - "quote", - "serde", - "serde_json", - "unicode-xid", -] - -[[package]] -name = "stable_deref_trait" -version = "1.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3" - -[[package]] -name = "static_assertions" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" - -[[package]] -name = "strum" -version = "0.24.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "063e6045c0e62079840579a7e47a355ae92f60eb74daaf156fb1e84ba164e63f" -dependencies = [ - "strum_macros", -] - -[[package]] -name = "strum_macros" -version = "0.24.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e385be0d24f186b4ce2f9982191e7101bb737312ad61c1f2f984f34bcf85d59" -dependencies = [ - "heck", - "proc-macro2", - "quote", - "rustversion", - "syn", -] - -[[package]] -name = "substrate-bip39" -version = "0.4.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "49eee6965196b32f882dd2ee85a92b1dbead41b04e53907f269de3b0dc04733c" -dependencies = [ - "hmac 0.11.0", - "pbkdf2 0.8.0", - "schnorrkel", - "sha2 0.9.9", - "zeroize", -] - -[[package]] -name = "subtle" -version = "2.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6bdef32e8150c2a081110b42772ffe7d7c9032b606bc226c8260fd97e0976601" - -[[package]] -name = "syn" -version = "1.0.105" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "60b9b43d45702de4c839cb9b51d9f529c5dd26a4aff255b42b1ebc03e88ee908" -dependencies = [ - "proc-macro2", - "quote", - "unicode-ident", -] - -[[package]] -name = "synstructure" -version = "0.12.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f36bdaa60a83aca3921b5259d5400cbf5e90fc51931376a9bd4a0eb79aa7210f" -dependencies = [ - "proc-macro2", - "quote", - "syn", - "unicode-xid", -] - -[[package]] -name = "tap" -version = "1.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "55937e1799185b12863d447f42597ed69d9928686b8d88a1df17376a097d8369" - -[[package]] -name = "target-lexicon" -version = "0.12.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9410d0f6853b1d94f0e519fb95df60f29d2c1eff2d921ffdf01a4c8a3b54f12d" - -[[package]] -name = "termcolor" -version = "1.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bab24d30b911b2376f3a13cc2cd443142f0c81dda04c118693e35b3835757755" -dependencies = [ - "winapi-util", -] - -[[package]] -name = "thiserror" -version = "1.0.37" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "10deb33631e3c9018b9baf9dcbbc4f737320d2b576bac10f6aefa048fa407e3e" -dependencies = [ - "thiserror-impl", -] - -[[package]] -name = "thiserror-impl" -version = "1.0.37" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "982d17546b47146b28f7c22e3d08465f6b8903d0ea13c1660d9d84a6e7adcdbb" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "thread_local" -version = "1.1.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5516c27b78311c50bf42c071425c560ac799b11c30b31f87e3081965fe5e0180" -dependencies = [ - "once_cell", -] - -[[package]] -name = "tiny-bip39" -version = "1.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "62cc94d358b5a1e84a5cb9109f559aa3c4d634d2b1b4de3d0fa4adc7c78e2861" -dependencies = [ - "anyhow", - "hmac 0.12.1", - "once_cell", - "pbkdf2 0.11.0", - "rand 0.8.5", - "rustc-hash", - "sha2 0.10.6", - "thiserror", - "unicode-normalization", - "wasm-bindgen", - "zeroize", -] - -[[package]] -name = "tinyvec" -version = "1.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "87cc5ceb3875bb20c2890005a4e226a4651264a5c75edb2421b52861a0a0cb50" -dependencies = [ - "tinyvec_macros", -] - -[[package]] -name = "tinyvec_macros" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cda74da7e1a664f795bb1f8a87ec406fb89a02522cf6e50620d016add6dbbf5c" - -[[package]] -name = "toml" -version = "0.5.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8d82e1a7758622a465f8cee077614c73484dac5b836c02ff6a40d5d1010324d7" -dependencies = [ - "serde", -] - -[[package]] -name = "tracing" -version = "0.1.37" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8ce8c33a8d48bd45d624a6e523445fd21ec13d3653cd51f681abf67418f54eb8" -dependencies = [ - "cfg-if", - "pin-project-lite", - "tracing-attributes", - "tracing-core", -] - -[[package]] -name = "tracing-attributes" -version = "0.1.23" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4017f8f45139870ca7e672686113917c71c7a6e02d4924eda67186083c03081a" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "tracing-core" -version = "0.1.30" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "24eb03ba0eab1fd845050058ce5e616558e8f8d8fca633e6b163fe25c797213a" -dependencies = [ - "once_cell", - "valuable", -] - -[[package]] -name = "tracing-log" -version = "0.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "78ddad33d2d10b1ed7eb9d1f518a5674713876e97e5bb9b7345a7984fbb4f922" -dependencies = [ - "lazy_static", - "log", - "tracing-core", -] - -[[package]] -name = "tracing-serde" -version = "0.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bc6b213177105856957181934e4920de57730fc69bf42c37ee5bb664d406d9e1" -dependencies = [ - "serde", - "tracing-core", -] - -[[package]] -name = "tracing-subscriber" -version = "0.2.25" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0e0d2eaa99c3c2e41547cfa109e910a68ea03823cccad4a0525dcbc9b01e8c71" -dependencies = [ - "ansi_term", - "chrono", - "lazy_static", - "matchers", - "regex", - "serde", - "serde_json", - "sharded-slab", - "smallvec", - "thread_local", - "tracing", - "tracing-core", - "tracing-log", - "tracing-serde", -] - -[[package]] -name = "trie-db" -version = "0.27.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "634d75c77ea43f2ad8ea9d9c58de49dfc9c3995bdef32b503df7883ff054e7f1" -dependencies = [ - "hash-db", - "hashbrown 0.13.2", - "log", - "rustc-hex", - "smallvec", -] - -[[package]] -name = "trie-root" -version = "0.18.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d4ed310ef5ab98f5fa467900ed906cb9232dd5376597e00fd4cba2a449d06c0b" -dependencies = [ - "hash-db", -] - -[[package]] -name = "tt-call" -version = "1.0.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5e66dcbec4290c69dd03c57e76c2469ea5c7ce109c6dd4351c13055cf71ea055" - -[[package]] -name = "twox-hash" -version = "1.6.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "97fee6b57c6a41524a810daee9286c02d7752c4253064d0b05472833a438f675" -dependencies = [ - "cfg-if", - "digest 0.10.6", - "rand 0.8.5", - "static_assertions", -] - -[[package]] -name = "typenum" -version = "1.16.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "497961ef93d974e23eb6f433eb5fe1b7930b659f06d12dec6fc44a8f554c0bba" - -[[package]] -name = "uint" -version = "0.9.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "76f64bba2c53b04fcab63c01a7d7427eadc821e3bc48c34dc9ba29c501164b52" -dependencies = [ - "byteorder", - "crunchy", - "hex", - "static_assertions", -] - -[[package]] -name = "unicode-bidi" -version = "0.3.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "524b68aca1d05e03fdf03fcdce2c6c94b6daf6d16861ddaa7e4f2b6638a9052c" - -[[package]] -name = "unicode-ident" -version = "1.0.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6ceab39d59e4c9499d4e5a8ee0e2735b891bb7308ac83dfb4e80cad195c9f6f3" - -[[package]] -name = "unicode-normalization" -version = "0.1.22" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5c5713f0fc4b5db668a2ac63cdb7bb4469d8c9fed047b1d0292cc7b0ce2ba921" -dependencies = [ - "tinyvec", -] - -[[package]] -name = "unicode-width" -version = "0.1.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c0edd1e5b14653f783770bce4a4dabb4a5108a5370a5f5d8cfe8710c361f6c8b" - -[[package]] -name = "unicode-xid" -version = "0.2.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f962df74c8c05a667b5ee8bcf162993134c104e96440b663c8daa176dc772d8c" - -[[package]] -name = "url" -version = "2.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0d68c799ae75762b8c3fe375feb6600ef5602c883c5d21eb51c09f22b83c4643" -dependencies = [ - "form_urlencoded", - "idna", - "percent-encoding", -] - -[[package]] -name = "valuable" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "830b7e5d4d90034032940e4ace0d9a9a057e7a45cd94e6c007832e39edb82f6d" - -[[package]] -name = "version_check" -version = "0.9.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" - -[[package]] -name = "wasi" -version = "0.9.0+wasi-snapshot-preview1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cccddf32554fecc6acb585f82a32a72e28b48f8c4c1883ddfeeeaa96f7d8e519" - -[[package]] -name = "wasi" -version = "0.11.0+wasi-snapshot-preview1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" - -[[package]] -name = "wasm-bindgen" -version = "0.2.83" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eaf9f5aceeec8be17c128b2e93e031fb8a4d469bb9c4ae2d7dc1888b26887268" -dependencies = [ - "cfg-if", - "wasm-bindgen-macro", -] - -[[package]] -name = "wasm-bindgen-backend" -version = "0.2.83" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4c8ffb332579b0557b52d268b91feab8df3615f265d5270fec2a8c95b17c1142" -dependencies = [ - "bumpalo", - "log", - "once_cell", - "proc-macro2", - "quote", - "syn", - "wasm-bindgen-shared", -] - -[[package]] -name = "wasm-bindgen-macro" -version = "0.2.83" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "052be0f94026e6cbc75cdefc9bae13fd6052cdcaf532fa6c45e7ae33a1e6c810" -dependencies = [ - "quote", - "wasm-bindgen-macro-support", -] - -[[package]] -name = "wasm-bindgen-macro-support" -version = "0.2.83" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "07bc0c051dc5f23e307b13285f9d75df86bfdf816c5721e573dec1f9b8aa193c" -dependencies = [ - "proc-macro2", - "quote", - "syn", - "wasm-bindgen-backend", - "wasm-bindgen-shared", -] - -[[package]] -name = "wasm-bindgen-shared" -version = "0.2.83" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1c38c045535d93ec4f0b4defec448e4291638ee608530863b1e2ba115d4fff7f" - -[[package]] -name = "wasmi" -version = "0.13.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "06c326c93fbf86419608361a2c925a31754cf109da1b8b55737070b4d6669422" -dependencies = [ - "parity-wasm", - "wasmi-validation", - "wasmi_core", -] - -[[package]] -name = "wasmi-validation" -version = "0.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "91ff416ad1ff0c42e5a926ed5d5fab74c0f098749aa0ad8b2a34b982ce0e867b" -dependencies = [ - "parity-wasm", -] - -[[package]] -name = "wasmi_core" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "57d20cb3c59b788653d99541c646c561c9dd26506f25c0cebfe810659c54c6d7" -dependencies = [ - "downcast-rs", - "libm", - "memory_units", - "num-rational", - "num-traits", -] - -[[package]] -name = "wasmparser" -version = "0.100.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "64b20236ab624147dfbb62cf12a19aaf66af0e41b8398838b66e997d07d269d4" -dependencies = [ - "indexmap", - "url", -] - -[[package]] -name = "wasmtime" -version = "6.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f6e89f9819523447330ffd70367ef4a18d8c832e24e8150fe054d1d912841632" -dependencies = [ - "anyhow", - "bincode", - "cfg-if", - "indexmap", - "libc", - "log", - "object", - "once_cell", - "paste", - "psm", - "serde", - "target-lexicon", - "wasmparser", - "wasmtime-environ", - "wasmtime-jit", - "wasmtime-runtime", - "windows-sys 0.42.0", -] - -[[package]] -name = "wasmtime-asm-macros" -version = "6.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9bd3a5e46c198032da934469f3a6e48649d1f9142438e4fd4617b68a35644b8a" -dependencies = [ - "cfg-if", -] - -[[package]] -name = "wasmtime-environ" -version = "6.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9a6db9fc52985ba06ca601f2ff0ff1f526c5d724c7ac267b47326304b0c97883" -dependencies = [ - "anyhow", - "cranelift-entity", - "gimli", - "indexmap", - "log", - "object", - "serde", - "target-lexicon", - "thiserror", - "wasmparser", - "wasmtime-types", -] - -[[package]] -name = "wasmtime-jit" -version = "6.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b77e3a52cd84d0f7f18554afa8060cfe564ccac61e3b0802d3fd4084772fa5f6" -dependencies = [ - "addr2line", - "anyhow", - "bincode", - "cfg-if", - "cpp_demangle", - "gimli", - "log", - "object", - "rustc-demangle", - "serde", - "target-lexicon", - "wasmtime-environ", - "wasmtime-jit-icache-coherence", - "wasmtime-runtime", - "windows-sys 0.42.0", -] - -[[package]] -name = "wasmtime-jit-debug" -version = "6.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d0245e8a9347017c7185a72e215218a802ff561545c242953c11ba00fccc930f" -dependencies = [ - "once_cell", -] - -[[package]] -name = "wasmtime-jit-icache-coherence" -version = "6.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "67d412e9340ab1c83867051d8d1d7c90aa8c9afc91da086088068e2734e25064" -dependencies = [ - "cfg-if", - "libc", - "windows-sys 0.42.0", -] - -[[package]] -name = "wasmtime-runtime" -version = "6.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d594e791b5fdd4dbaf8cf7ae62f2e4ff85018ce90f483ca6f42947688e48827d" -dependencies = [ - "anyhow", - "cc", - "cfg-if", - "indexmap", - "libc", - "log", - "mach", - "memfd", - "memoffset", - "paste", - "rand 0.8.5", - "rustix", - "wasmtime-asm-macros", - "wasmtime-environ", - "wasmtime-jit-debug", - "windows-sys 0.42.0", -] - -[[package]] -name = "wasmtime-types" -version = "6.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a6688d6f96d4dbc1f89fab626c56c1778936d122b5f4ae7a57c2eb42b8d982e2" -dependencies = [ - "cranelift-entity", - "serde", - "thiserror", - "wasmparser", -] - -[[package]] -name = "wide" -version = "0.7.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b689b6c49d6549434bf944e6b0f39238cf63693cb7a147e9d887507fffa3b223" -dependencies = [ - "bytemuck", - "safe_arch", -] - -[[package]] -name = "winapi" -version = "0.3.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" -dependencies = [ - "winapi-i686-pc-windows-gnu", - "winapi-x86_64-pc-windows-gnu", -] - -[[package]] -name = "winapi-i686-pc-windows-gnu" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" - -[[package]] -name = "winapi-util" -version = "0.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "70ec6ce85bb158151cae5e5c87f95a8e97d2c0c4b001223f33a334e3ce5de178" -dependencies = [ - "winapi", -] - -[[package]] -name = "winapi-x86_64-pc-windows-gnu" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" - -[[package]] -name = "windows-sys" -version = "0.42.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5a3e1820f08b8513f676f7ab6c1f99ff312fb97b553d30ff4dd86f9f15728aa7" -dependencies = [ - "windows_aarch64_gnullvm", - "windows_aarch64_msvc", - "windows_i686_gnu", - "windows_i686_msvc", - "windows_x86_64_gnu", - "windows_x86_64_gnullvm", - "windows_x86_64_msvc", -] - -[[package]] -name = "windows-sys" -version = "0.45.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "75283be5efb2831d37ea142365f009c02ec203cd29a3ebecbc093d52315b66d0" -dependencies = [ - "windows-targets", -] - -[[package]] -name = "windows-targets" -version = "0.42.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8e5180c00cd44c9b1c88adb3693291f1cd93605ded80c250a75d472756b4d071" -dependencies = [ - "windows_aarch64_gnullvm", - "windows_aarch64_msvc", - "windows_i686_gnu", - "windows_i686_msvc", - "windows_x86_64_gnu", - "windows_x86_64_gnullvm", - "windows_x86_64_msvc", -] - -[[package]] -name = "windows_aarch64_gnullvm" -version = "0.42.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "597a5118570b68bc08d8d59125332c54f1ba9d9adeedeef5b99b02ba2b0698f8" - -[[package]] -name = "windows_aarch64_msvc" -version = "0.42.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e08e8864a60f06ef0d0ff4ba04124db8b0fb3be5776a5cd47641e942e58c4d43" - -[[package]] -name = "windows_i686_gnu" -version = "0.42.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c61d927d8da41da96a81f029489353e68739737d3beca43145c8afec9a31a84f" - -[[package]] -name = "windows_i686_msvc" -version = "0.42.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "44d840b6ec649f480a41c8d80f9c65108b92d89345dd94027bfe06ac444d1060" - -[[package]] -name = "windows_x86_64_gnu" -version = "0.42.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8de912b8b8feb55c064867cf047dda097f92d51efad5b491dfb98f6bbb70cb36" - -[[package]] -name = "windows_x86_64_gnullvm" -version = "0.42.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "26d41b46a36d453748aedef1486d5c7a85db22e56aff34643984ea85514e94a3" - -[[package]] -name = "windows_x86_64_msvc" -version = "0.42.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9aec5da331524158c6d1a4ac0ab1541149c0b9505fde06423b02f5ef0106b9f0" - -[[package]] -name = "wyz" -version = "0.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "05f360fc0b24296329c78fda852a1e9ae82de9cf7b27dae4b7f62f118f77b9ed" -dependencies = [ - "tap", -] - -[[package]] -name = "zeroize" -version = "1.5.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c394b5bd0c6f669e7275d9c20aa90ae064cb22e75a1cad54e1b34088034b149f" -dependencies = [ - "zeroize_derive", -] - -[[package]] -name = "zeroize_derive" -version = "1.3.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "44bf07cb3e50ea2003396695d58bf46bc9887a1f362260446fad6bc4e79bd36c" -dependencies = [ - "proc-macro2", - "quote", - "syn", - "synstructure", -] diff --git a/frame/election-provider-multi-phase/test-staking-e2e/Cargo.toml b/frame/election-provider-multi-phase/test-staking-e2e/Cargo.toml index 432b732b4..00dd8708e 100644 --- a/frame/election-provider-multi-phase/test-staking-e2e/Cargo.toml +++ b/frame/election-provider-multi-phase/test-staking-e2e/Cargo.toml @@ -12,8 +12,6 @@ publish = false [package.metadata.docs.rs] targets = ["x86_64-unknown-linux-gnu"] -[workspace] - [dev-dependencies] codec = { package = "parity-scale-codec", version = "3.0.0", features = ["derive"] } scale-info = { version = "2.0.1", features = ["derive"] } diff --git a/frame/election-provider-multi-phase/test-staking-e2e/src/lib.rs b/frame/election-provider-multi-phase/test-staking-e2e/src/lib.rs index c8f7d48a8..c396b008f 100644 --- a/frame/election-provider-multi-phase/test-staking-e2e/src/lib.rs +++ b/frame/election-provider-multi-phase/test-staking-e2e/src/lib.rs @@ -34,8 +34,8 @@ macro_rules! log { log::$level!( target: crate::LOG_TARGET, concat!("🛠️ ", $patter) $(, $values)* - ) - }; + ) + }; } fn log_current_time() { diff --git a/frame/election-provider-multi-phase/test-staking-e2e/src/mock.rs b/frame/election-provider-multi-phase/test-staking-e2e/src/mock.rs index 19f568737..c0ad6936f 100644 --- a/frame/election-provider-multi-phase/test-staking-e2e/src/mock.rs +++ b/frame/election-provider-multi-phase/test-staking-e2e/src/mock.rs @@ -125,6 +125,10 @@ impl pallet_balances::Config for Runtime { type ExistentialDeposit = ExistentialDeposit; type AccountStore = System; type WeightInfo = (); + type HoldIdentifier = (); + type FreezeIdentifier = (); + type MaxHolds = traits::ConstU32<1>; + type MaxFreezes = traits::ConstU32<1>; } impl pallet_timestamp::Config for Runtime { From 44130d53f222a0efdd976e76e32460680637c5fe Mon Sep 17 00:00:00 2001 From: juangirini Date: Wed, 3 May 2023 15:45:04 +0200 Subject: [PATCH 450/558] contracts: add events to ContractResult (#13807) * contracts: add events to ContractResult * contracts: add encoded events to ContractResult * contracts: add generic Event to ContractResult * contracts: test bare_call events * contracts: update bare_call test name * contracts: add better comments to dry run events * contracts: fix pallet contracts primitives implementation * contracts: add EventRecord generic to ContractInstantiateResult * contracts: make event collection optional * contracts: impreved notes on `collect_events` * contracts: update benchmarking calls * contracts: change bare_call and bare_instantiate to accept enums instead of bools * contracts: add partial eq to new enums * contracts: improve comments * contracts: improve comments * contracts: fix bare_call benchmarking * contracts: fix bare call and instantiate in impl_runtime_apis * contracts: add api versioning to new ContractsApi functions * contracts: modify api versioning to new ContractsApi functions * contracts: create new contracts api trait * contracts: clean up code * contracts: remove the contract results with events * contracts: undo contracts api v3 * contracts: remove commented out code * contracts: add new test bare call result does not return events * contracts: add code review improvements * contracts: remove type imports * contracts: minor code review improvements --- bin/node/runtime/src/lib.rs | 17 +- frame/contracts/primitives/src/lib.rs | 26 +- frame/contracts/src/benchmarking/mod.rs | 12 +- frame/contracts/src/lib.rs | 93 ++++- frame/contracts/src/tests.rs | 497 ++++++++++++++++++------ 5 files changed, 487 insertions(+), 158 deletions(-) diff --git a/bin/node/runtime/src/lib.rs b/bin/node/runtime/src/lib.rs index 766bc9515..60d954913 100644 --- a/bin/node/runtime/src/lib.rs +++ b/bin/node/runtime/src/lib.rs @@ -1880,6 +1880,11 @@ type Migrations = ( pallet_contracts::Migration, ); +type EventRecord = frame_system::EventRecord< + ::RuntimeEvent, + ::Hash, +>; + /// MMR helper types. mod mmr { use super::Runtime; @@ -2141,7 +2146,7 @@ impl_runtime_apis! { } } - impl pallet_contracts::ContractsApi for Runtime + impl pallet_contracts::ContractsApi for Runtime { fn call( origin: AccountId, @@ -2150,7 +2155,7 @@ impl_runtime_apis! { gas_limit: Option, storage_deposit_limit: Option, input_data: Vec, - ) -> pallet_contracts_primitives::ContractExecResult { + ) -> pallet_contracts_primitives::ContractExecResult { let gas_limit = gas_limit.unwrap_or(RuntimeBlockWeights::get().max_block); Contracts::bare_call( origin, @@ -2159,7 +2164,8 @@ impl_runtime_apis! { gas_limit, storage_deposit_limit, input_data, - true, + pallet_contracts::DebugInfo::UnsafeDebug, + pallet_contracts::CollectEvents::UnsafeCollect, pallet_contracts::Determinism::Enforced, ) } @@ -2172,7 +2178,7 @@ impl_runtime_apis! { code: pallet_contracts_primitives::Code, data: Vec, salt: Vec, - ) -> pallet_contracts_primitives::ContractInstantiateResult + ) -> pallet_contracts_primitives::ContractInstantiateResult { let gas_limit = gas_limit.unwrap_or(RuntimeBlockWeights::get().max_block); Contracts::bare_instantiate( @@ -2183,7 +2189,8 @@ impl_runtime_apis! { code, data, salt, - true + pallet_contracts::DebugInfo::UnsafeDebug, + pallet_contracts::CollectEvents::UnsafeCollect, ) } diff --git a/frame/contracts/primitives/src/lib.rs b/frame/contracts/primitives/src/lib.rs index e73ceb031..cc21e29e6 100644 --- a/frame/contracts/primitives/src/lib.rs +++ b/frame/contracts/primitives/src/lib.rs @@ -29,11 +29,18 @@ use sp_runtime::{ use sp_std::prelude::*; use sp_weights::Weight; -/// Result type of a `bare_call` or `bare_instantiate` call. +/// Result type of a `bare_call` or `bare_instantiate` call as well as `ContractsApi::call` and +/// `ContractsApi::instantiate`. /// /// It contains the execution result together with some auxiliary information. +/// +/// #Note +/// +/// It has been extended to include `events` at the end of the struct while not bumping the +/// `ContractsApi` version. Therefore when SCALE decoding a `ContractResult` its trailing data +/// should be ignored to avoid any potential compatibility issues. #[derive(Eq, PartialEq, Encode, Decode, RuntimeDebug, TypeInfo)] -pub struct ContractResult { +pub struct ContractResult { /// How much weight was consumed during execution. pub gas_consumed: Weight, /// How much weight is required as gas limit in order to execute this call. @@ -71,15 +78,18 @@ pub struct ContractResult { pub debug_message: Vec, /// The execution result of the wasm code. pub result: R, + /// The events that were emitted during execution. It is an option as event collection is + /// optional. + pub events: Option>, } -/// Result type of a `bare_call` call. -pub type ContractExecResult = - ContractResult, Balance>; +/// Result type of a `bare_call` call as well as `ContractsApi::call`. +pub type ContractExecResult = + ContractResult, Balance, EventRecord>; -/// Result type of a `bare_instantiate` call. -pub type ContractInstantiateResult = - ContractResult, DispatchError>, Balance>; +/// Result type of a `bare_instantiate` call as well as `ContractsApi::instantiate`. +pub type ContractInstantiateResult = + ContractResult, DispatchError>, Balance, EventRecord>; /// Result type of a `bare_code_upload` call. pub type CodeUploadResult = diff --git a/frame/contracts/src/benchmarking/mod.rs b/frame/contracts/src/benchmarking/mod.rs index 2c60c1501..fa9417a59 100644 --- a/frame/contracts/src/benchmarking/mod.rs +++ b/frame/contracts/src/benchmarking/mod.rs @@ -951,7 +951,8 @@ benchmarks! { Weight::MAX, None, vec![], - true, + DebugInfo::UnsafeDebug, + CollectEvents::Skip, Determinism::Enforced, ) .result?; @@ -1000,7 +1001,8 @@ benchmarks! { Weight::MAX, None, vec![], - true, + DebugInfo::UnsafeDebug, + CollectEvents::Skip, Determinism::Enforced, ) .result?; @@ -3192,7 +3194,8 @@ benchmarks! { Weight::MAX, None, data, - false, + DebugInfo::Skip, + CollectEvents::Skip, Determinism::Enforced, ) .result?; @@ -3241,7 +3244,8 @@ benchmarks! { Weight::MAX, None, data, - false, + DebugInfo::Skip, + CollectEvents::Skip, Determinism::Enforced, ) .result?; diff --git a/frame/contracts/src/lib.rs b/frame/contracts/src/lib.rs index 97fa28457..e0e8e2d15 100644 --- a/frame/contracts/src/lib.rs +++ b/frame/contracts/src/lib.rs @@ -117,7 +117,7 @@ use frame_support::{ weights::Weight, BoundedVec, RuntimeDebugNoBound, WeakBoundedVec, }; -use frame_system::{ensure_signed, pallet_prelude::OriginFor, Pallet as System}; +use frame_system::{ensure_signed, pallet_prelude::OriginFor, EventRecord, Pallet as System}; use pallet_contracts_primitives::{ Code, CodeUploadResult, CodeUploadReturnValue, ContractAccessError, ContractExecResult, ContractInstantiateResult, ExecReturnValue, GetStorageResult, InstantiateReturnValue, @@ -148,6 +148,8 @@ type CodeVec = BoundedVec::MaxCodeLen>; type RelaxedCodeVec = WeakBoundedVec::MaxCodeLen>; type AccountIdLookupOf = <::Lookup as StaticLookup>::Source; type DebugBufferVec = BoundedVec::MaxDebugBufferLen>; +type EventRecordOf = + EventRecord<::RuntimeEvent, ::Hash>; /// The old weight type. /// @@ -971,6 +973,35 @@ struct InstantiateInput { salt: Vec, } +/// Determines whether events should be collected during execution. +#[derive(PartialEq)] +pub enum CollectEvents { + /// Collect events. + /// + /// # Note + /// + /// Events should only be collected when called off-chain, as this would otherwise + /// collect all the Events emitted in the block so far and put them into the PoV. + /// + /// **Never** use this mode for on-chain execution. + UnsafeCollect, + /// Skip event collection. + Skip, +} + +/// Determines whether debug messages will be collected. +#[derive(PartialEq)] +pub enum DebugInfo { + /// Collect debug messages. + /// # Note + /// + /// This should only ever be set to `UnsafeDebug` when executing as an RPC because + /// it adds allocations and could be abused to drive the runtime into an OOM panic. + UnsafeDebug, + /// Skip collection of debug messages. + Skip, +} + /// Return type of private helper functions. struct InternalOutput { /// The gas meter that was used to execute the call. @@ -1187,11 +1218,11 @@ impl Pallet { /// /// # Note /// - /// `debug` should only ever be set to `true` when executing as an RPC because - /// it adds allocations and could be abused to drive the runtime into an OOM panic. - /// If set to `true` it returns additional human readable debugging information. + /// If `debug` is set to `DebugInfo::UnsafeDebug` it returns additional human readable debugging + /// information. /// - /// It returns the execution result and the amount of used weight. + /// If `collect_events` is set to `CollectEvents::UnsafeCollect` it collects all the Events + /// emitted in the block so far and the ones emitted during the execution of this contract. pub fn bare_call( origin: T::AccountId, dest: T::AccountId, @@ -1199,10 +1230,15 @@ impl Pallet { gas_limit: Weight, storage_deposit_limit: Option>, data: Vec, - debug: bool, + debug: DebugInfo, + collect_events: CollectEvents, determinism: Determinism, - ) -> ContractExecResult> { - let mut debug_message = if debug { Some(DebugBufferVec::::default()) } else { None }; + ) -> ContractExecResult, EventRecordOf> { + let mut debug_message = if matches!(debug, DebugInfo::UnsafeDebug) { + Some(DebugBufferVec::::default()) + } else { + None + }; let origin = Origin::from_account_id(origin); let common = CommonInput { origin, @@ -1213,12 +1249,19 @@ impl Pallet { debug_message: debug_message.as_mut(), }; let output = CallInput:: { dest, determinism }.run_guarded(common); + let events = if matches!(collect_events, CollectEvents::UnsafeCollect) { + Some(System::::read_events_no_consensus().map(|e| *e).collect()) + } else { + None + }; + ContractExecResult { result: output.result.map_err(|r| r.error), gas_consumed: output.gas_meter.gas_consumed(), gas_required: output.gas_meter.gas_required(), storage_deposit: output.storage_deposit, debug_message: debug_message.unwrap_or_default().to_vec(), + events, } } @@ -1231,9 +1274,11 @@ impl Pallet { /// /// # Note /// - /// `debug` should only ever be set to `true` when executing as an RPC because - /// it adds allocations and could be abused to drive the runtime into an OOM panic. - /// If set to `true` it returns additional human readable debugging information. + /// If `debug` is set to `DebugInfo::UnsafeDebug` it returns additional human readable debugging + /// information. + /// + /// If `collect_events` is set to `CollectEvents::UnsafeCollect` it collects all the Events + /// emitted in the block so far. pub fn bare_instantiate( origin: T::AccountId, value: BalanceOf, @@ -1242,9 +1287,14 @@ impl Pallet { code: Code>, data: Vec, salt: Vec, - debug: bool, - ) -> ContractInstantiateResult> { - let mut debug_message = if debug { Some(DebugBufferVec::::default()) } else { None }; + debug: DebugInfo, + collect_events: CollectEvents, + ) -> ContractInstantiateResult, EventRecordOf> { + let mut debug_message = if debug == DebugInfo::UnsafeDebug { + Some(DebugBufferVec::::default()) + } else { + None + }; let common = CommonInput { origin: Origin::from_account_id(origin), value, @@ -1254,6 +1304,12 @@ impl Pallet { debug_message: debug_message.as_mut(), }; let output = InstantiateInput:: { code, salt }.run_guarded(common); + // collect events if CollectEvents is UnsafeCollect + let events = if collect_events == CollectEvents::UnsafeCollect { + Some(System::::read_events_no_consensus().map(|e| *e).collect()) + } else { + None + }; ContractInstantiateResult { result: output .result @@ -1263,6 +1319,7 @@ impl Pallet { gas_required: output.gas_meter.gas_required(), storage_deposit: output.storage_deposit, debug_message: debug_message.unwrap_or_default().to_vec(), + events, } } @@ -1370,11 +1427,12 @@ impl Pallet { sp_api::decl_runtime_apis! { /// The API used to dry-run contract interactions. #[api_version(2)] - pub trait ContractsApi where + pub trait ContractsApi where AccountId: Codec, Balance: Codec, BlockNumber: Codec, Hash: Codec, + EventRecord: Codec, { /// Perform a call from a specified account to a given contract. /// @@ -1386,7 +1444,7 @@ sp_api::decl_runtime_apis! { gas_limit: Option, storage_deposit_limit: Option, input_data: Vec, - ) -> ContractExecResult; + ) -> ContractExecResult; /// Instantiate a new contract. /// @@ -1399,8 +1457,7 @@ sp_api::decl_runtime_apis! { code: Code, data: Vec, salt: Vec, - ) -> ContractInstantiateResult; - + ) -> ContractInstantiateResult; /// Upload new code without instantiating a contract from it. /// diff --git a/frame/contracts/src/tests.rs b/frame/contracts/src/tests.rs index a07176e9f..c32999d0a 100644 --- a/frame/contracts/src/tests.rs +++ b/frame/contracts/src/tests.rs @@ -27,8 +27,8 @@ use crate::{ tests::test_utils::{get_contract, get_contract_checked}, wasm::{Determinism, PrefabWasmModule, ReturnCode as RuntimeReturnCode}, weights::WeightInfo, - BalanceOf, Code, CodeStorage, Config, ContractInfo, ContractInfoOf, DefaultAddressGenerator, - DeletionQueueCounter, Error, Origin, Pallet, Schedule, + BalanceOf, Code, CodeStorage, CollectEvents, Config, ContractInfo, ContractInfoOf, DebugInfo, + DefaultAddressGenerator, DeletionQueueCounter, Error, Origin, Pallet, Schedule, }; use assert_matches::assert_matches; use codec::Encode; @@ -574,7 +574,8 @@ fn instantiate_and_call_and_deposit_event() { Code::Existing(code_hash), vec![], vec![], - false, + DebugInfo::Skip, + CollectEvents::Skip, ) .result .unwrap() @@ -680,7 +681,8 @@ fn deposit_event_max_value_limit() { Code::Upload(wasm), vec![], vec![], - false, + DebugInfo::Skip, + CollectEvents::Skip, ) .result .unwrap() @@ -726,7 +728,8 @@ fn run_out_of_gas() { Code::Upload(wasm), vec![], vec![], - false, + DebugInfo::Skip, + CollectEvents::Skip, ) .result .unwrap() @@ -768,7 +771,8 @@ fn instantiate_unique_trie_id() { Code::Existing(code_hash), vec![], vec![], - false, + DebugInfo::Skip, + CollectEvents::Skip, ) .result .unwrap() @@ -830,7 +834,8 @@ fn storage_max_value_limit() { Code::Upload(wasm), vec![], vec![], - false, + DebugInfo::Skip, + CollectEvents::Skip, ) .result .unwrap() @@ -880,7 +885,8 @@ fn deploy_and_call_other_contract() { Code::Upload(caller_wasm), vec![], vec![], - false, + DebugInfo::Skip, + CollectEvents::Skip, ) .result .unwrap() @@ -1029,7 +1035,8 @@ fn delegate_call() { Code::Upload(caller_wasm), vec![], vec![], - false, + DebugInfo::Skip, + CollectEvents::Skip, ) .result .unwrap() @@ -1068,7 +1075,8 @@ fn transfer_allow_death_cannot_kill_account() { Code::Upload(wasm), vec![], vec![], - false, + DebugInfo::Skip, + CollectEvents::Skip, ) .result .unwrap() @@ -1108,7 +1116,8 @@ fn cannot_self_destruct_through_draning() { Code::Upload(wasm), vec![], vec![], - false, + DebugInfo::Skip, + CollectEvents::Skip, ) .result .unwrap() @@ -1152,7 +1161,8 @@ fn cannot_self_destruct_through_storage_refund_after_price_change() { Code::Upload(wasm), vec![], vec![], - false, + DebugInfo::Skip, + CollectEvents::Skip, ) .result .unwrap() @@ -1210,7 +1220,8 @@ fn cannot_self_destruct_while_live() { Code::Upload(wasm), vec![], vec![], - false, + DebugInfo::Skip, + CollectEvents::Skip, ) .result .unwrap() @@ -1254,7 +1265,8 @@ fn self_destruct_works() { Code::Upload(wasm), vec![], vec![], - false, + DebugInfo::Skip, + CollectEvents::Skip, ) .result .unwrap() @@ -1361,7 +1373,8 @@ fn destroy_contract_and_transfer_funds() { Code::Upload(caller_wasm), callee_code_hash.as_ref().to_vec(), vec![], - false, + DebugInfo::Skip, + CollectEvents::Skip, ) .result .unwrap() @@ -1425,7 +1438,8 @@ fn crypto_hashes() { Code::Upload(wasm), vec![], vec![], - false, + DebugInfo::Skip, + CollectEvents::Skip, ) .result .unwrap() @@ -1458,7 +1472,8 @@ fn crypto_hashes() { GAS_LIMIT, None, params, - false, + DebugInfo::Skip, + CollectEvents::Skip, Determinism::Enforced, ) .result @@ -1485,7 +1500,8 @@ fn transfer_return_code() { Code::Upload(wasm), vec![], vec![], - false, + DebugInfo::Skip, + CollectEvents::Skip, ) .result .unwrap() @@ -1500,7 +1516,8 @@ fn transfer_return_code() { GAS_LIMIT, None, vec![], - false, + DebugInfo::Skip, + CollectEvents::Skip, Determinism::Enforced, ) .result @@ -1526,7 +1543,8 @@ fn call_return_code() { Code::Upload(caller_code), vec![0], vec![], - false, + DebugInfo::Skip, + CollectEvents::Skip, ) .result .unwrap() @@ -1541,7 +1559,8 @@ fn call_return_code() { GAS_LIMIT, None, AsRef::<[u8]>::as_ref(&DJANGO).to_vec(), - false, + DebugInfo::Skip, + CollectEvents::Skip, Determinism::Enforced, ) .result @@ -1556,7 +1575,8 @@ fn call_return_code() { Code::Upload(callee_code), vec![0], vec![], - false, + DebugInfo::Skip, + CollectEvents::Skip, ) .result .unwrap() @@ -1575,7 +1595,8 @@ fn call_return_code() { .chain(&0u32.to_le_bytes()) .cloned() .collect(), - false, + DebugInfo::Skip, + CollectEvents::Skip, Determinism::Enforced, ) .result @@ -1595,7 +1616,8 @@ fn call_return_code() { .chain(&1u32.to_le_bytes()) .cloned() .collect(), - false, + DebugInfo::Skip, + CollectEvents::Skip, Determinism::Enforced, ) .result @@ -1614,7 +1636,8 @@ fn call_return_code() { .chain(&2u32.to_le_bytes()) .cloned() .collect(), - false, + DebugInfo::Skip, + CollectEvents::Skip, Determinism::Enforced, ) .result @@ -1651,7 +1674,8 @@ fn instantiate_return_code() { Code::Upload(caller_code), vec![], vec![], - false, + DebugInfo::Skip, + CollectEvents::Skip, ) .result .unwrap() @@ -1666,7 +1690,8 @@ fn instantiate_return_code() { GAS_LIMIT, None, callee_hash.clone(), - false, + DebugInfo::Skip, + CollectEvents::Skip, Determinism::Enforced, ) .result @@ -1682,7 +1707,8 @@ fn instantiate_return_code() { GAS_LIMIT, None, vec![0; 33], - false, + DebugInfo::Skip, + CollectEvents::Skip, Determinism::Enforced, ) .result @@ -1697,7 +1723,8 @@ fn instantiate_return_code() { GAS_LIMIT, None, callee_hash.iter().chain(&1u32.to_le_bytes()).cloned().collect(), - false, + DebugInfo::Skip, + CollectEvents::Skip, Determinism::Enforced, ) .result @@ -1712,7 +1739,8 @@ fn instantiate_return_code() { GAS_LIMIT, None, callee_hash.iter().chain(&2u32.to_le_bytes()).cloned().collect(), - false, + DebugInfo::Skip, + CollectEvents::Skip, Determinism::Enforced, ) .result @@ -1757,7 +1785,8 @@ fn disabled_chain_extension_errors_on_call() { Code::Upload(code), vec![], vec![], - false, + DebugInfo::Skip, + CollectEvents::Skip, ) .result .unwrap() @@ -1784,7 +1813,8 @@ fn chain_extension_works() { Code::Upload(code), vec![], vec![], - false, + DebugInfo::Skip, + CollectEvents::Skip, ) .result .unwrap() @@ -1799,7 +1829,8 @@ fn chain_extension_works() { GAS_LIMIT, None, input.clone(), - false, + DebugInfo::Skip, + CollectEvents::Skip, Determinism::Enforced, ); assert_eq!(TestExtension::last_seen_buffer(), input); @@ -1813,7 +1844,8 @@ fn chain_extension_works() { GAS_LIMIT, None, ExtensionInput { extension_id: 0, func_id: 1, extra: &[] }.into(), - false, + DebugInfo::Skip, + CollectEvents::Skip, Determinism::Enforced, ) .result @@ -1829,7 +1861,8 @@ fn chain_extension_works() { GAS_LIMIT, None, ExtensionInput { extension_id: 0, func_id: 2, extra: &[0] }.into(), - false, + DebugInfo::Skip, + CollectEvents::Skip, Determinism::Enforced, ); assert_ok!(result.result); @@ -1841,7 +1874,8 @@ fn chain_extension_works() { GAS_LIMIT, None, ExtensionInput { extension_id: 0, func_id: 2, extra: &[42] }.into(), - false, + DebugInfo::Skip, + CollectEvents::Skip, Determinism::Enforced, ); assert_ok!(result.result); @@ -1853,7 +1887,8 @@ fn chain_extension_works() { GAS_LIMIT, None, ExtensionInput { extension_id: 0, func_id: 2, extra: &[95] }.into(), - false, + DebugInfo::Skip, + CollectEvents::Skip, Determinism::Enforced, ); assert_ok!(result.result); @@ -1867,7 +1902,8 @@ fn chain_extension_works() { GAS_LIMIT, None, ExtensionInput { extension_id: 0, func_id: 3, extra: &[] }.into(), - false, + DebugInfo::Skip, + CollectEvents::Skip, Determinism::Enforced, ) .result @@ -1885,7 +1921,8 @@ fn chain_extension_works() { GAS_LIMIT, None, ExtensionInput { extension_id: 1, func_id: 0, extra: &[] }.into(), - false, + DebugInfo::Skip, + CollectEvents::Skip, Determinism::Enforced, ) .result @@ -1923,7 +1960,8 @@ fn chain_extension_temp_storage_works() { Code::Upload(code), vec![], vec![], - false, + DebugInfo::Skip, + CollectEvents::Skip, ) .result .unwrap() @@ -1946,8 +1984,9 @@ fn chain_extension_temp_storage_works() { GAS_LIMIT, None, input.clone(), - false, - Determinism::Enforced + DebugInfo::Skip, + CollectEvents::Skip, + Determinism::Enforced, ) .result ); @@ -1969,7 +2008,8 @@ fn lazy_removal_works() { Code::Upload(code), vec![], vec![], - false, + DebugInfo::Skip, + CollectEvents::Skip, ) .result .unwrap() @@ -2022,7 +2062,8 @@ fn lazy_batch_removal_works() { Code::Upload(code.clone()), vec![], vec![i], - false, + DebugInfo::Skip, + CollectEvents::Skip, ) .result .unwrap() @@ -2087,7 +2128,8 @@ fn lazy_removal_partial_remove_works() { Code::Upload(code), vec![], vec![], - false, + DebugInfo::Skip, + CollectEvents::Skip, ) .result .unwrap() @@ -2168,7 +2210,8 @@ fn lazy_removal_does_no_run_on_low_remaining_weight() { Code::Upload(code), vec![], vec![], - false, + DebugInfo::Skip, + CollectEvents::Skip, ) .result .unwrap() @@ -2239,7 +2282,8 @@ fn lazy_removal_does_not_use_all_weight() { Code::Upload(code), vec![], vec![], - false, + DebugInfo::Skip, + CollectEvents::Skip, ) .result .unwrap() @@ -2329,7 +2373,8 @@ fn deletion_queue_ring_buffer_overflow() { Code::Upload(code.clone()), vec![], vec![i], - false, + DebugInfo::Skip, + CollectEvents::Skip, ) .result .unwrap() @@ -2386,7 +2431,8 @@ fn refcounter() { Code::Upload(wasm.clone()), vec![], vec![0], - false, + DebugInfo::Skip, + CollectEvents::Skip, ) .result .unwrap() @@ -2399,7 +2445,8 @@ fn refcounter() { Code::Upload(wasm.clone()), vec![], vec![1], - false, + DebugInfo::Skip, + CollectEvents::Skip, ) .result .unwrap() @@ -2415,7 +2462,8 @@ fn refcounter() { Code::Existing(code_hash), vec![], vec![2], - false, + DebugInfo::Skip, + CollectEvents::Skip, ) .result .unwrap() @@ -2481,7 +2529,8 @@ fn reinstrument_does_charge() { Code::Upload(wasm), zero.clone(), vec![], - false, + DebugInfo::Skip, + CollectEvents::Skip, ) .result .unwrap() @@ -2496,7 +2545,8 @@ fn reinstrument_does_charge() { GAS_LIMIT, None, zero.clone(), - false, + DebugInfo::Skip, + CollectEvents::Skip, Determinism::Enforced, ); assert!(!result0.result.unwrap().did_revert()); @@ -2508,7 +2558,8 @@ fn reinstrument_does_charge() { GAS_LIMIT, None, zero.clone(), - false, + DebugInfo::Skip, + CollectEvents::Skip, Determinism::Enforced, ); assert!(!result1.result.unwrap().did_revert()); @@ -2530,7 +2581,8 @@ fn reinstrument_does_charge() { GAS_LIMIT, None, zero.clone(), - false, + DebugInfo::Skip, + CollectEvents::Skip, Determinism::Enforced, ); assert!(!result2.result.unwrap().did_revert()); @@ -2557,7 +2609,8 @@ fn debug_message_works() { Code::Upload(wasm), vec![], vec![], - false, + DebugInfo::Skip, + CollectEvents::Skip, ) .result .unwrap() @@ -2569,7 +2622,8 @@ fn debug_message_works() { GAS_LIMIT, None, vec![], - true, + DebugInfo::UnsafeDebug, + CollectEvents::Skip, Determinism::Enforced, ); @@ -2592,7 +2646,8 @@ fn debug_message_logging_disabled() { Code::Upload(wasm), vec![], vec![], - false, + DebugInfo::Skip, + CollectEvents::Skip, ) .result .unwrap() @@ -2605,7 +2660,8 @@ fn debug_message_logging_disabled() { GAS_LIMIT, None, vec![], - false, + DebugInfo::Skip, + CollectEvents::Skip, Determinism::Enforced, ); assert_matches!(result.result, Ok(_)); @@ -2629,7 +2685,8 @@ fn debug_message_invalid_utf8() { Code::Upload(wasm), vec![], vec![], - false, + DebugInfo::Skip, + CollectEvents::Skip, ) .result .unwrap() @@ -2641,7 +2698,8 @@ fn debug_message_invalid_utf8() { GAS_LIMIT, None, vec![], - true, + DebugInfo::UnsafeDebug, + CollectEvents::Skip, Determinism::Enforced, ); assert_ok!(result.result); @@ -2665,7 +2723,8 @@ fn gas_estimation_nested_call_fixed_limit() { Code::Upload(caller_code), vec![], vec![0], - false, + DebugInfo::Skip, + CollectEvents::Skip, ) .result .unwrap() @@ -2679,7 +2738,8 @@ fn gas_estimation_nested_call_fixed_limit() { Code::Upload(callee_code), vec![], vec![1], - false, + DebugInfo::Skip, + CollectEvents::Skip, ) .result .unwrap() @@ -2700,7 +2760,8 @@ fn gas_estimation_nested_call_fixed_limit() { GAS_LIMIT, None, input.clone(), - false, + DebugInfo::Skip, + CollectEvents::Skip, Determinism::Enforced, ); assert_ok!(&result.result); @@ -2717,7 +2778,8 @@ fn gas_estimation_nested_call_fixed_limit() { result.gas_required, Some(result.storage_deposit.charge_or_zero()), input.clone(), - false, + DebugInfo::Skip, + CollectEvents::Skip, Determinism::Enforced, ) .result @@ -2731,7 +2793,8 @@ fn gas_estimation_nested_call_fixed_limit() { result.gas_required.sub_proof_size(1), Some(result.storage_deposit.charge_or_zero()), input, - false, + DebugInfo::Skip, + CollectEvents::Skip, Determinism::Enforced, ) .result; @@ -2757,7 +2820,8 @@ fn gas_estimation_call_runtime() { Code::Upload(caller_code), vec![], vec![0], - false, + DebugInfo::Skip, + CollectEvents::Skip, ) .result .unwrap() @@ -2771,7 +2835,8 @@ fn gas_estimation_call_runtime() { Code::Upload(callee_code), vec![], vec![1], - false, + DebugInfo::Skip, + CollectEvents::Skip, ) .result .unwrap() @@ -2790,7 +2855,8 @@ fn gas_estimation_call_runtime() { GAS_LIMIT, None, call.encode(), - false, + DebugInfo::Skip, + CollectEvents::Skip, Determinism::Enforced, ); // contract encodes the result of the dispatch runtime @@ -2807,7 +2873,8 @@ fn gas_estimation_call_runtime() { result.gas_required, None, call.encode(), - false, + DebugInfo::Skip, + CollectEvents::Skip, Determinism::Enforced, ) .result @@ -2832,7 +2899,8 @@ fn call_runtime_reentrancy_guarded() { Code::Upload(caller_code), vec![], vec![0], - false, + DebugInfo::Skip, + CollectEvents::Skip, ) .result .unwrap() @@ -2846,7 +2914,8 @@ fn call_runtime_reentrancy_guarded() { Code::Upload(callee_code), vec![], vec![1], - false, + DebugInfo::Skip, + CollectEvents::Skip, ) .result .unwrap() @@ -2870,7 +2939,8 @@ fn call_runtime_reentrancy_guarded() { GAS_LIMIT, None, call.encode(), - false, + DebugInfo::Skip, + CollectEvents::Skip, Determinism::Enforced, ) .result @@ -2896,7 +2966,8 @@ fn ecdsa_recover() { Code::Upload(wasm), vec![], vec![], - false, + DebugInfo::Skip, + CollectEvents::Skip, ) .result .unwrap() @@ -2932,7 +3003,8 @@ fn ecdsa_recover() { GAS_LIMIT, None, params, - false, + DebugInfo::Skip, + CollectEvents::Skip, Determinism::Enforced, ) .result @@ -2942,6 +3014,138 @@ fn ecdsa_recover() { }) } +#[test] +fn bare_instantiate_returns_events() { + let (wasm, _code_hash) = compile_module::("transfer_return_code").unwrap(); + ExtBuilder::default().existential_deposit(50).build().execute_with(|| { + let min_balance = ::Currency::minimum_balance(); + let _ = Balances::deposit_creating(&ALICE, 1000 * min_balance); + + let result = Contracts::bare_instantiate( + ALICE, + min_balance * 100, + GAS_LIMIT, + None, + Code::Upload(wasm), + vec![], + vec![], + DebugInfo::Skip, + CollectEvents::UnsafeCollect, + ); + + let events = result.events.unwrap(); + assert!(!events.is_empty()); + assert_eq!(events, System::events()); + }); +} + +#[test] +fn bare_instantiate_does_not_return_events() { + let (wasm, _code_hash) = compile_module::("transfer_return_code").unwrap(); + ExtBuilder::default().existential_deposit(50).build().execute_with(|| { + let min_balance = ::Currency::minimum_balance(); + let _ = Balances::deposit_creating(&ALICE, 1000 * min_balance); + + let result = Contracts::bare_instantiate( + ALICE, + min_balance * 100, + GAS_LIMIT, + None, + Code::Upload(wasm), + vec![], + vec![], + DebugInfo::Skip, + CollectEvents::Skip, + ); + + let events = result.events; + assert!(!System::events().is_empty()); + assert!(events.is_none()); + }); +} + +#[test] +fn bare_call_returns_events() { + let (wasm, _code_hash) = compile_module::("transfer_return_code").unwrap(); + ExtBuilder::default().existential_deposit(50).build().execute_with(|| { + let min_balance = ::Currency::minimum_balance(); + let _ = Balances::deposit_creating(&ALICE, 1000 * min_balance); + + let addr = Contracts::bare_instantiate( + ALICE, + min_balance * 100, + GAS_LIMIT, + None, + Code::Upload(wasm), + vec![], + vec![], + DebugInfo::Skip, + CollectEvents::Skip, + ) + .result + .unwrap() + .account_id; + + let result = Contracts::bare_call( + ALICE, + addr.clone(), + 0, + GAS_LIMIT, + None, + vec![], + DebugInfo::Skip, + CollectEvents::UnsafeCollect, + Determinism::Enforced, + ); + + let events = result.events.unwrap(); + assert_return_code!(&result.result.unwrap(), RuntimeReturnCode::Success); + assert!(!events.is_empty()); + assert_eq!(events, System::events()); + }); +} + +#[test] +fn bare_call_does_not_return_events() { + let (wasm, _code_hash) = compile_module::("transfer_return_code").unwrap(); + ExtBuilder::default().existential_deposit(50).build().execute_with(|| { + let min_balance = ::Currency::minimum_balance(); + let _ = Balances::deposit_creating(&ALICE, 1000 * min_balance); + + let addr = Contracts::bare_instantiate( + ALICE, + min_balance * 100, + GAS_LIMIT, + None, + Code::Upload(wasm), + vec![], + vec![], + DebugInfo::Skip, + CollectEvents::Skip, + ) + .result + .unwrap() + .account_id; + + let result = Contracts::bare_call( + ALICE, + addr.clone(), + 0, + GAS_LIMIT, + None, + vec![], + DebugInfo::Skip, + CollectEvents::Skip, + Determinism::Enforced, + ); + + let events = result.events; + assert_return_code!(&result.result.unwrap(), RuntimeReturnCode::Success); + assert!(!System::events().is_empty()); + assert!(events.is_none()); + }); +} + #[test] fn sr25519_verify() { let (wasm, _code_hash) = compile_module::("sr25519_verify").unwrap(); @@ -2958,7 +3162,8 @@ fn sr25519_verify() { Code::Upload(wasm), vec![], vec![], - false, + DebugInfo::Skip, + CollectEvents::Skip, ) .result .unwrap() @@ -2993,7 +3198,8 @@ fn sr25519_verify() { GAS_LIMIT, None, params, - false, + DebugInfo::Skip, + CollectEvents::Skip, Determinism::Enforced, ) .result @@ -3005,7 +3211,7 @@ fn sr25519_verify() { // verification should fail for other messages assert_return_code!(call_with(&b"hello worlD"), RuntimeReturnCode::Sr25519VerifyFailed); - }) + }); } #[test] @@ -3027,7 +3233,8 @@ fn failed_deposit_charge_should_roll_back_call() { Code::Upload(wasm_caller.clone()), vec![], vec![], - false, + DebugInfo::Skip, + CollectEvents::Skip, ) .result .unwrap() @@ -3040,7 +3247,8 @@ fn failed_deposit_charge_should_roll_back_call() { Code::Upload(wasm_callee.clone()), vec![], vec![], - false, + DebugInfo::Skip, + CollectEvents::Skip, ) .result .unwrap() @@ -3341,7 +3549,8 @@ fn instantiate_with_zero_balance_works() { Code::Upload(wasm), vec![], vec![], - false, + DebugInfo::Skip, + CollectEvents::Skip, ) .result .unwrap() @@ -3451,7 +3660,8 @@ fn instantiate_with_below_existential_deposit_works() { Code::Upload(wasm), vec![], vec![], - false, + DebugInfo::Skip, + CollectEvents::Skip, ) .result .unwrap() @@ -3566,7 +3776,8 @@ fn storage_deposit_works() { Code::Upload(wasm), vec![], vec![], - false, + DebugInfo::Skip, + CollectEvents::Skip, ) .result .unwrap() @@ -3704,7 +3915,8 @@ fn storage_deposit_callee_works() { Code::Upload(wasm_caller), vec![], vec![], - false, + DebugInfo::Skip, + CollectEvents::Skip, ) .result .unwrap() @@ -3717,7 +3929,8 @@ fn storage_deposit_callee_works() { Code::Upload(wasm_callee), vec![], vec![], - false, + DebugInfo::Skip, + CollectEvents::Skip, ) .result .unwrap() @@ -3758,7 +3971,8 @@ fn set_code_extrinsic() { Code::Upload(wasm), vec![], vec![], - false, + DebugInfo::Skip, + CollectEvents::Skip, ) .result .unwrap() @@ -3842,7 +4056,8 @@ fn slash_cannot_kill_account() { Code::Upload(wasm), vec![], vec![], - false, + DebugInfo::Skip, + CollectEvents::Skip, ) .result .unwrap() @@ -3931,7 +4146,8 @@ fn contract_reverted() { Code::Existing(code_hash), input.clone(), vec![], - false, + DebugInfo::Skip, + CollectEvents::Skip, ) .result .unwrap(); @@ -3948,7 +4164,8 @@ fn contract_reverted() { Code::Existing(code_hash), ReturnFlags::empty().bits().encode(), vec![], - false, + DebugInfo::Skip, + CollectEvents::Skip, ) .result .unwrap() @@ -3975,7 +4192,8 @@ fn contract_reverted() { GAS_LIMIT, None, input, - false, + DebugInfo::Skip, + CollectEvents::Skip, Determinism::Enforced, ) .result @@ -4008,7 +4226,8 @@ fn code_rejected_error_works() { Code::Upload(wasm), vec![], vec![], - true, + DebugInfo::UnsafeDebug, + CollectEvents::Skip, ); assert_err!(result.result, >::CodeRejected); assert_eq!( @@ -4035,7 +4254,8 @@ fn code_rejected_error_works() { Code::Upload(wasm), vec![], vec![], - true, + DebugInfo::UnsafeDebug, + CollectEvents::Skip, ); assert_err!(result.result, >::CodeRejected); assert_eq!( @@ -4062,7 +4282,8 @@ fn set_code_hash() { Code::Upload(wasm), vec![], vec![], - false, + DebugInfo::Skip, + CollectEvents::Skip, ) .result .unwrap() @@ -4085,7 +4306,8 @@ fn set_code_hash() { GAS_LIMIT, None, new_code_hash.as_ref().to_vec(), - true, + DebugInfo::UnsafeDebug, + CollectEvents::Skip, Determinism::Enforced, ) .result @@ -4100,7 +4322,8 @@ fn set_code_hash() { GAS_LIMIT, None, vec![], - true, + DebugInfo::UnsafeDebug, + CollectEvents::Skip, Determinism::Enforced, ) .result @@ -4163,7 +4386,8 @@ fn storage_deposit_limit_is_enforced() { Code::Upload(wasm), vec![], vec![], - false, + DebugInfo::Skip, + CollectEvents::Skip, ) .result .unwrap() @@ -4237,7 +4461,8 @@ fn deposit_limit_in_nested_calls() { Code::Upload(wasm_caller), vec![], vec![], - false, + DebugInfo::Skip, + CollectEvents::Skip, ) .result .unwrap() @@ -4250,7 +4475,8 @@ fn deposit_limit_in_nested_calls() { Code::Upload(wasm_callee), vec![], vec![], - false, + DebugInfo::Skip, + CollectEvents::Skip, ) .result .unwrap() @@ -4381,7 +4607,8 @@ fn deposit_limit_in_nested_instantiate() { Code::Upload(wasm_caller), vec![], vec![], - false, + DebugInfo::Skip, + CollectEvents::Skip, ) .result .unwrap() @@ -4395,7 +4622,8 @@ fn deposit_limit_in_nested_instantiate() { Code::Upload(wasm_callee), vec![0, 0, 0, 0], vec![], - false, + DebugInfo::Skip, + CollectEvents::Skip, ) .result .unwrap() @@ -4488,7 +4716,8 @@ fn deposit_limit_in_nested_instantiate() { GAS_LIMIT, Some(codec::Compact(callee_info_len + 2 + ED + 4).into()), (1u32, &code_hash_callee, callee_info_len + 2 + ED + 3).encode(), - false, + DebugInfo::Skip, + CollectEvents::Skip, Determinism::Enforced, ); @@ -4531,7 +4760,8 @@ fn deposit_limit_honors_liquidity_restrictions() { Code::Upload(wasm), vec![], vec![], - false, + DebugInfo::Skip, + CollectEvents::Skip, ) .result .unwrap() @@ -4575,7 +4805,8 @@ fn deposit_limit_honors_existential_deposit() { Code::Upload(wasm), vec![], vec![], - false, + DebugInfo::Skip, + CollectEvents::Skip, ) .result .unwrap() @@ -4618,7 +4849,8 @@ fn deposit_limit_honors_min_leftover() { Code::Upload(wasm), vec![], vec![], - false, + DebugInfo::Skip, + CollectEvents::Skip, ) .result .unwrap() @@ -4673,7 +4905,8 @@ fn cannot_instantiate_indeterministic_code() { Code::Upload(wasm.clone()), vec![], vec![], - false, + DebugInfo::Skip, + CollectEvents::Skip, ) .result, >::CodeRejected, @@ -4718,7 +4951,8 @@ fn cannot_instantiate_indeterministic_code() { Code::Existing(code_hash), vec![], vec![], - false, + DebugInfo::Skip, + CollectEvents::Skip, ) .result, >::Indeterministic, @@ -4733,7 +4967,8 @@ fn cannot_instantiate_indeterministic_code() { Code::Upload(caller_wasm), vec![], vec![], - false, + DebugInfo::Skip, + CollectEvents::Skip, ) .result .unwrap() @@ -4748,7 +4983,8 @@ fn cannot_instantiate_indeterministic_code() { GAS_LIMIT, None, code_hash.encode(), - false, + DebugInfo::Skip, + CollectEvents::Skip, Determinism::Enforced, ) .result, @@ -4764,7 +5000,8 @@ fn cannot_instantiate_indeterministic_code() { GAS_LIMIT, None, code_hash.encode(), - false, + DebugInfo::Skip, + CollectEvents::Skip, Determinism::Relaxed, ) .result, @@ -4797,7 +5034,8 @@ fn cannot_set_code_indeterministic_code() { Code::Upload(caller_wasm), vec![], vec![], - false, + DebugInfo::Skip, + CollectEvents::Skip, ) .result .unwrap() @@ -4812,7 +5050,8 @@ fn cannot_set_code_indeterministic_code() { GAS_LIMIT, None, code_hash.encode(), - false, + DebugInfo::Skip, + CollectEvents::Skip, Determinism::Relaxed, ) .result, @@ -4845,7 +5084,8 @@ fn delegate_call_indeterministic_code() { Code::Upload(caller_wasm), vec![], vec![], - false, + DebugInfo::Skip, + CollectEvents::Skip, ) .result .unwrap() @@ -4860,7 +5100,8 @@ fn delegate_call_indeterministic_code() { GAS_LIMIT, None, code_hash.encode(), - false, + DebugInfo::Skip, + CollectEvents::Skip, Determinism::Enforced, ) .result, @@ -4876,7 +5117,8 @@ fn delegate_call_indeterministic_code() { GAS_LIMIT, None, code_hash.encode(), - false, + DebugInfo::Skip, + CollectEvents::Skip, Determinism::Relaxed, ) .result @@ -4899,7 +5141,8 @@ fn reentrance_count_works_with_call() { Code::Upload(wasm), vec![], vec![], - false, + DebugInfo::Skip, + CollectEvents::Skip, ) .result .unwrap() @@ -4915,7 +5158,8 @@ fn reentrance_count_works_with_call() { GAS_LIMIT, None, input, - true, + DebugInfo::UnsafeDebug, + CollectEvents::Skip, Determinism::Enforced, ) .result @@ -4938,7 +5182,8 @@ fn reentrance_count_works_with_delegated_call() { Code::Upload(wasm), vec![], vec![], - false, + DebugInfo::Skip, + CollectEvents::Skip, ) .result .unwrap() @@ -4954,7 +5199,8 @@ fn reentrance_count_works_with_delegated_call() { GAS_LIMIT, None, input, - true, + DebugInfo::UnsafeDebug, + CollectEvents::Skip, Determinism::Enforced, ) .result @@ -4979,7 +5225,8 @@ fn account_reentrance_count_works() { Code::Upload(wasm), vec![], vec![], - false, + DebugInfo::Skip, + CollectEvents::Skip, ) .result .unwrap() @@ -4993,7 +5240,8 @@ fn account_reentrance_count_works() { Code::Upload(wasm_reentrance_count), vec![], vec![], - false, + DebugInfo::Skip, + CollectEvents::Skip, ) .result .unwrap() @@ -5006,7 +5254,8 @@ fn account_reentrance_count_works() { GAS_LIMIT, None, contract_addr.encode(), - true, + DebugInfo::UnsafeDebug, + CollectEvents::Skip, Determinism::Enforced, ) .result @@ -5019,7 +5268,8 @@ fn account_reentrance_count_works() { GAS_LIMIT, None, another_contract_addr.encode(), - true, + DebugInfo::UnsafeDebug, + CollectEvents::Skip, Determinism::Enforced, ) .result @@ -5091,7 +5341,8 @@ fn root_can_call() { Code::Upload(wasm), vec![], vec![], - false, + DebugInfo::Skip, + CollectEvents::Skip, ) .result .unwrap() From 71d749c74e43c7a840c94fcbbdd2b0172a21d473 Mon Sep 17 00:00:00 2001 From: Niklas Adolfsson Date: Wed, 3 May 2023 17:18:25 +0200 Subject: [PATCH 451/558] rpc server: break legacy CLI options and remove "backward compatible HTTP server" (#13384) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * jsonrpsee v0.16 * breaking: remove old CLI configs * remove patch.crates-io * fix bad merge * fix clippy * fix bad merge * fix grumbles * Update client/service/src/lib.rs Co-authored-by: Bastian Köcher * revert block_in_place * add issue link in todo * Update client/cli/src/config.rs Co-authored-by: Dmitry Markin * grumbles: add ipv6 loopback address * Revert "grumbles: add ipv6 loopback address" This reverts commit 3a0b1ece6c4e36055d666896c29d1da55ffa1c4f. * remove nits * bump zombienet version * adress grumbles: provide structopt default_val_t * remove duplicate from structopt * bump zombienet v1.3.47 * bump zombienet version --------- Co-authored-by: Bastian Köcher Co-authored-by: Dmitry Markin Co-authored-by: Javier Viola --- .gitlab-ci.yml | 2 +- bin/node/cli/benches/block_production.rs | 16 +- bin/node/cli/benches/transaction_pool.rs | 16 +- client/cli/src/commands/run_cmd.rs | 98 +++---------- client/cli/src/config.rs | 69 ++------- client/cli/src/runner.rs | 16 +- client/rpc-servers/src/lib.rs | 178 ++++++++--------------- client/service/src/config.rs | 26 +--- client/service/src/lib.rs | 105 +++---------- client/service/test/src/lib.rs | 16 +- test-utils/cli/src/lib.rs | 2 +- 11 files changed, 150 insertions(+), 394 deletions(-) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 508f082d8..cc950865c 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -61,7 +61,7 @@ variables: NEXTEST_FAILURE_OUTPUT: immediate-final NEXTEST_SUCCESS_OUTPUT: final - ZOMBIENET_IMAGE: "docker.io/paritytech/zombienet:v1.3.43" + ZOMBIENET_IMAGE: "docker.io/paritytech/zombienet:v1.3.48" default: retry: diff --git a/bin/node/cli/benches/block_production.rs b/bin/node/cli/benches/block_production.rs index c7f0cd20e..36e807692 100644 --- a/bin/node/cli/benches/block_production.rs +++ b/bin/node/cli/benches/block_production.rs @@ -84,18 +84,14 @@ fn new_node(tokio_handle: Handle) -> node_cli::service::NewFullBase { offchain_worker: execution_strategy, other: execution_strategy, }, - rpc_http: None, - rpc_ws: None, - rpc_ipc: None, - rpc_ws_max_connections: None, + rpc_addr: None, + rpc_max_connections: Default::default(), rpc_cors: None, rpc_methods: Default::default(), - rpc_max_payload: None, - rpc_max_request_size: None, - rpc_max_response_size: None, - rpc_id_provider: None, - rpc_max_subs_per_conn: None, - ws_max_out_buffer_capacity: None, + rpc_max_request_size: Default::default(), + rpc_max_response_size: Default::default(), + rpc_id_provider: Default::default(), + rpc_max_subs_per_conn: Default::default(), prometheus_config: None, telemetry_endpoints: None, default_heap_pages: None, diff --git a/bin/node/cli/benches/transaction_pool.rs b/bin/node/cli/benches/transaction_pool.rs index abee9c846..14d99c37a 100644 --- a/bin/node/cli/benches/transaction_pool.rs +++ b/bin/node/cli/benches/transaction_pool.rs @@ -78,18 +78,14 @@ fn new_node(tokio_handle: Handle) -> node_cli::service::NewFullBase { offchain_worker: sc_client_api::ExecutionStrategy::NativeWhenPossible, other: sc_client_api::ExecutionStrategy::NativeWhenPossible, }, - rpc_http: None, - rpc_ws: None, - rpc_ipc: None, - rpc_ws_max_connections: None, + rpc_addr: None, + rpc_max_connections: Default::default(), rpc_cors: None, rpc_methods: Default::default(), - rpc_max_payload: None, - rpc_max_request_size: None, - rpc_max_response_size: None, - rpc_id_provider: None, - rpc_max_subs_per_conn: None, - ws_max_out_buffer_capacity: None, + rpc_max_request_size: Default::default(), + rpc_max_response_size: Default::default(), + rpc_id_provider: Default::default(), + rpc_max_subs_per_conn: Default::default(), prometheus_config: None, telemetry_endpoints: None, default_heap_pages: None, diff --git a/client/cli/src/commands/run_cmd.rs b/client/cli/src/commands/run_cmd.rs index 7cc1e6c73..8176643d7 100644 --- a/client/cli/src/commands/run_cmd.rs +++ b/client/cli/src/commands/run_cmd.rs @@ -65,7 +65,7 @@ pub struct RunCmd { /// RPC methods to expose. /// - `unsafe`: Exposes every RPC method. /// - `safe`: Exposes only a safe subset of RPC methods, denying unsafe RPC methods. - /// - `auto`: Acts as `safe` if RPC is served externally, e.g. when `--{rpc,ws}-external` is + /// - `auto`: Acts as `safe` if RPC is served externally, e.g. when `--rpc--external` is /// passed, otherwise acts as `unsafe`. #[arg( long, @@ -77,58 +77,25 @@ pub struct RunCmd { )] pub rpc_methods: RpcMethods, - /// Listen to all Websocket interfaces. - /// Default is local. Note: not all RPC methods are safe to be exposed publicly. Use an RPC - /// proxy server to filter out dangerous methods. More details: - /// . - /// Use `--unsafe-ws-external` to suppress the warning if you understand the risks. - #[arg(long)] - pub ws_external: bool, - - /// Listen to all Websocket interfaces. - /// Same as `--ws-external` but doesn't warn you about it. - #[arg(long)] - pub unsafe_ws_external: bool, - - /// DEPRECATED, this has no affect anymore. Use `rpc_max_request_size` or - /// `rpc_max_response_size` instead. - #[arg(long)] - pub rpc_max_payload: Option, - /// Set the the maximum RPC request payload size for both HTTP and WS in megabytes. - /// Default is 15MiB. - #[arg(long)] - pub rpc_max_request_size: Option, + #[arg(long, default_value_t = 15)] + pub rpc_max_request_size: u32, /// Set the the maximum RPC response payload size for both HTTP and WS in megabytes. - /// Default is 15MiB. - #[arg(long)] - pub rpc_max_response_size: Option, + #[arg(long, default_value_t = 15)] + pub rpc_max_response_size: u32, /// Set the the maximum concurrent subscriptions per connection. - /// Default is 1024. - #[arg(long)] - pub rpc_max_subscriptions_per_connection: Option, + #[arg(long, default_value_t = 1024)] + pub rpc_max_subscriptions_per_connection: u32, - /// DEPRECATED, IPC support has been removed. - #[arg(long, value_name = "PATH")] - pub ipc_path: Option, + /// Specify JSON-RPC server TCP port. + #[arg(long, value_name = "PORT", default_value_t = 9944)] + pub rpc_port: u16, - /// Specify HTTP RPC server TCP port. - #[arg(long, value_name = "PORT")] - pub rpc_port: Option, - - /// Specify WebSockets RPC server TCP port. - #[arg(long, value_name = "PORT")] - pub ws_port: Option, - - /// Maximum number of WS RPC server connections. - #[arg(long, value_name = "COUNT")] - pub ws_max_connections: Option, - - /// DEPRECATED, this has no affect anymore. Use `rpc_max_response_size` instead. - #[arg(long)] - pub ws_max_out_buffer_capacity: Option, + /// Maximum number of RPC server connections. + #[arg(long, value_name = "COUNT", default_value_t = 100)] + pub rpc_max_connections: u32, /// Specify browser Origins allowed to access the HTTP & WS RPC servers. /// A comma-separated list of origins (protocol://domain or special `null` @@ -344,8 +311,8 @@ impl CliConfiguration for RunCmd { Ok(self.no_grandpa) } - fn rpc_ws_max_connections(&self) -> Result> { - Ok(self.ws_max_connections) + fn rpc_max_connections(&self) -> Result { + Ok(self.rpc_max_connections) } fn rpc_cors(&self, is_dev: bool) -> Result>> { @@ -369,7 +336,7 @@ impl CliConfiguration for RunCmd { .into()) } - fn rpc_http(&self, default_listen_port: u16) -> Result> { + fn rpc_addr(&self, _default_listen_port: u16) -> Result> { let interface = rpc_interface( self.rpc_external, self.unsafe_rpc_external, @@ -377,48 +344,25 @@ impl CliConfiguration for RunCmd { self.validator, )?; - Ok(Some(SocketAddr::new(interface, self.rpc_port.unwrap_or(default_listen_port)))) - } - - fn rpc_ipc(&self) -> Result> { - Ok(self.ipc_path.clone()) - } - - fn rpc_ws(&self, default_listen_port: u16) -> Result> { - let interface = rpc_interface( - self.ws_external, - self.unsafe_ws_external, - self.rpc_methods, - self.validator, - )?; - - Ok(Some(SocketAddr::new(interface, self.ws_port.unwrap_or(default_listen_port)))) + Ok(Some(SocketAddr::new(interface, self.rpc_port))) } fn rpc_methods(&self) -> Result { Ok(self.rpc_methods.into()) } - fn rpc_max_payload(&self) -> Result> { - Ok(self.rpc_max_payload) - } - - fn rpc_max_request_size(&self) -> Result> { + fn rpc_max_request_size(&self) -> Result { Ok(self.rpc_max_request_size) } - fn rpc_max_response_size(&self) -> Result> { + fn rpc_max_response_size(&self) -> Result { Ok(self.rpc_max_response_size) } - fn rpc_max_subscriptions_per_connection(&self) -> Result> { + fn rpc_max_subscriptions_per_connection(&self) -> Result { Ok(self.rpc_max_subscriptions_per_connection) } - fn ws_max_out_buffer_capacity(&self) -> Result> { - Ok(self.ws_max_out_buffer_capacity) - } - fn transaction_pool(&self, is_dev: bool) -> Result { Ok(self.pool_config.transaction_pool(is_dev)) } @@ -475,7 +419,7 @@ fn rpc_interface( ) -> Result { if is_external && is_validator && rpc_methods != RpcMethods::Unsafe { return Err(Error::Input( - "--rpc-external and --ws-external options shouldn't be used if the node is running as \ + "--rpc-external option shouldn't be used if the node is running as \ a validator. Use `--unsafe-rpc-external` or `--rpc-methods=unsafe` if you understand \ the risks. See the options description for more information." .to_owned(), diff --git a/client/cli/src/config.rs b/client/cli/src/config.rs index 063b2c398..92b1eea7a 100644 --- a/client/cli/src/config.rs +++ b/client/cli/src/config.rs @@ -57,20 +57,13 @@ pub trait DefaultConfigurationValues { 30333 } - /// The port Substrate should listen on for websocket connections. + /// The port Substrate should listen on for JSON-RPC connections. /// /// By default this is `9944`. - fn rpc_ws_listen_port() -> u16 { + fn rpc_listen_port() -> u16 { 9944 } - /// The port Substrate should listen on for http connections. - /// - /// By default this is `9933`. - fn rpc_http_listen_port() -> u16 { - 9933 - } - /// The port Substrate should listen on for prometheus connections. /// /// By default this is `9615`. @@ -302,24 +295,8 @@ pub trait CliConfiguration: Sized { .unwrap_or_default()) } - /// Get the RPC HTTP address (`None` if disabled). - /// - /// By default this is `None`. - fn rpc_http(&self, _default_listen_port: u16) -> Result> { - Ok(None) - } - - /// Get the RPC IPC path (`None` if disabled). - /// - /// By default this is `None`. - fn rpc_ipc(&self) -> Result> { - Ok(None) - } - - /// Get the RPC websocket address (`None` if disabled). - /// - /// By default this is `None`. - fn rpc_ws(&self, _default_listen_port: u16) -> Result> { + /// Get the RPC address. + fn rpc_addr(&self, _default_listen_port: u16) -> Result> { Ok(None) } @@ -331,11 +308,9 @@ pub trait CliConfiguration: Sized { Ok(Default::default()) } - /// Get the RPC websockets maximum connections (`None` if unlimited). - /// - /// By default this is `None`. - fn rpc_ws_max_connections(&self) -> Result> { - Ok(None) + /// Get the maximum number of RPC server connections. + fn rpc_max_connections(&self) -> Result { + Ok(Default::default()) } /// Get the RPC cors (`None` if disabled) @@ -345,29 +320,19 @@ pub trait CliConfiguration: Sized { Ok(Some(Vec::new())) } - /// Get maximum RPC payload. - fn rpc_max_payload(&self) -> Result> { - Ok(None) - } - /// Get maximum RPC request payload size. - fn rpc_max_request_size(&self) -> Result> { - Ok(None) + fn rpc_max_request_size(&self) -> Result { + Ok(Default::default()) } /// Get maximum RPC response payload size. - fn rpc_max_response_size(&self) -> Result> { - Ok(None) + fn rpc_max_response_size(&self) -> Result { + Ok(Default::default()) } /// Get maximum number of subscriptions per connection. - fn rpc_max_subscriptions_per_connection(&self) -> Result> { - Ok(None) - } - - /// Get maximum WS output buffer capacity. - fn ws_max_out_buffer_capacity(&self) -> Result> { - Ok(None) + fn rpc_max_subscriptions_per_connection(&self) -> Result { + Ok(Default::default()) } /// Get the prometheus configuration (`None` if disabled) @@ -532,18 +497,14 @@ pub trait CliConfiguration: Sized { wasm_method: self.wasm_method()?, wasm_runtime_overrides: self.wasm_runtime_overrides(), execution_strategies: self.execution_strategies(is_dev, is_validator)?, - rpc_http: self.rpc_http(DCV::rpc_http_listen_port())?, - rpc_ws: self.rpc_ws(DCV::rpc_ws_listen_port())?, - rpc_ipc: self.rpc_ipc()?, + rpc_addr: self.rpc_addr(DCV::rpc_listen_port())?, rpc_methods: self.rpc_methods()?, - rpc_ws_max_connections: self.rpc_ws_max_connections()?, + rpc_max_connections: self.rpc_max_connections()?, rpc_cors: self.rpc_cors(is_dev)?, - rpc_max_payload: self.rpc_max_payload()?, rpc_max_request_size: self.rpc_max_request_size()?, rpc_max_response_size: self.rpc_max_response_size()?, rpc_id_provider: None, rpc_max_subs_per_conn: self.rpc_max_subscriptions_per_connection()?, - ws_max_out_buffer_capacity: self.ws_max_out_buffer_capacity()?, prometheus_config: self .prometheus_config(DCV::prometheus_listen_port(), &chain_spec)?, telemetry_endpoints, diff --git a/client/cli/src/runner.rs b/client/cli/src/runner.rs index a8b75f266..9db078046 100644 --- a/client/cli/src/runner.rs +++ b/client/cli/src/runner.rs @@ -287,18 +287,14 @@ mod tests { wasm_method: Default::default(), wasm_runtime_overrides: None, execution_strategies: Default::default(), - rpc_http: None, - rpc_ws: None, - rpc_ipc: None, - rpc_ws_max_connections: None, + rpc_addr: None, + rpc_max_connections: Default::default(), rpc_cors: None, rpc_methods: Default::default(), - rpc_max_payload: None, - rpc_max_request_size: None, - rpc_max_response_size: None, - rpc_id_provider: None, - rpc_max_subs_per_conn: None, - ws_max_out_buffer_capacity: None, + rpc_max_request_size: Default::default(), + rpc_max_response_size: Default::default(), + rpc_id_provider: Default::default(), + rpc_max_subs_per_conn: Default::default(), prometheus_config: None, telemetry_endpoints: None, default_heap_pages: None, diff --git a/client/rpc-servers/src/lib.rs b/client/rpc-servers/src/lib.rs index 62a9b4fc4..92b31937a 100644 --- a/client/rpc-servers/src/lib.rs +++ b/client/rpc-servers/src/lib.rs @@ -20,6 +20,9 @@ #![warn(missing_docs)] +pub mod middleware; + +use http::header::HeaderValue; use jsonrpsee::{ server::{ middleware::proxy_get_request::ProxyGetRequestLayer, AllowHosts, ServerBuilder, @@ -28,119 +31,62 @@ use jsonrpsee::{ RpcModule, }; use std::{error::Error as StdError, net::SocketAddr}; +use tower_http::cors::{AllowOrigin, CorsLayer}; pub use crate::middleware::RpcMetrics; -use http::header::HeaderValue; pub use jsonrpsee::core::{ id_providers::{RandomIntegerIdProvider, RandomStringIdProvider}, traits::IdProvider, }; -use tower_http::cors::{AllowOrigin, CorsLayer}; - -const MEGABYTE: usize = 1024 * 1024; - -/// Maximal payload accepted by RPC servers. -pub const RPC_MAX_PAYLOAD_DEFAULT: usize = 15 * MEGABYTE; - -/// Default maximum number of connections for WS RPC servers. -const WS_MAX_CONNECTIONS: usize = 100; -/// Default maximum number subscriptions per connection for WS RPC servers. -const WS_MAX_SUBS_PER_CONN: usize = 1024; +const MEGABYTE: u32 = 1024 * 1024; -pub mod middleware; - -/// Type alias JSON-RPC server +/// Type alias for the JSON-RPC server. pub type Server = ServerHandle; -/// Server config. -#[derive(Debug, Clone)] -pub struct WsConfig { +/// RPC server configuration. +#[derive(Debug)] +pub struct Config<'a, M: Send + Sync + 'static> { + /// Socket addresses. + pub addrs: [SocketAddr; 2], + /// CORS. + pub cors: Option<&'a Vec>, /// Maximum connections. - pub max_connections: Option, + pub max_connections: u32, /// Maximum subscriptions per connection. - pub max_subs_per_conn: Option, + pub max_subs_per_conn: u32, /// Maximum rpc request payload size. - pub max_payload_in_mb: Option, + pub max_payload_in_mb: u32, /// Maximum rpc response payload size. - pub max_payload_out_mb: Option, -} - -impl WsConfig { - // Deconstructs the config to get the finalized inner values. - // - // `Payload size` or `max subs per connection` bigger than u32::MAX will be truncated. - fn deconstruct(self) -> (u32, u32, u32, u32) { - let max_conns = self.max_connections.unwrap_or(WS_MAX_CONNECTIONS) as u32; - let max_payload_in_mb = payload_size_or_default(self.max_payload_in_mb) as u32; - let max_payload_out_mb = payload_size_or_default(self.max_payload_out_mb) as u32; - let max_subs_per_conn = self.max_subs_per_conn.unwrap_or(WS_MAX_SUBS_PER_CONN) as u32; - - (max_payload_in_mb, max_payload_out_mb, max_conns, max_subs_per_conn) - } -} - -/// Start HTTP server listening on given address. -pub async fn start_http( - addrs: [SocketAddr; 2], - cors: Option<&Vec>, - max_payload_in_mb: Option, - max_payload_out_mb: Option, - metrics: Option, - rpc_api: RpcModule, - rt: tokio::runtime::Handle, -) -> Result> { - let max_payload_in = payload_size_or_default(max_payload_in_mb) as u32; - let max_payload_out = payload_size_or_default(max_payload_out_mb) as u32; - let host_filter = hosts_filter(cors.is_some(), &addrs); - - let middleware = tower::ServiceBuilder::new() - // Proxy `GET /health` requests to internal `system_health` method. - .layer(ProxyGetRequestLayer::new("/health", "system_health")?) - .layer(try_into_cors(cors)?); - - let builder = ServerBuilder::new() - .max_request_body_size(max_payload_in) - .max_response_body_size(max_payload_out) - .set_host_filtering(host_filter) - .set_middleware(middleware) - .custom_tokio_runtime(rt) - .http_only(); - - let rpc_api = build_rpc_api(rpc_api); - let (handle, addr) = if let Some(metrics) = metrics { - let server = builder.set_logger(metrics).build(&addrs[..]).await?; - let addr = server.local_addr(); - (server.start(rpc_api)?, addr) - } else { - let server = builder.build(&addrs[..]).await?; - let addr = server.local_addr(); - (server.start(rpc_api)?, addr) - }; - - log::info!( - "Running JSON-RPC HTTP server: addr={}, allowed origins={}", - addr.map_or_else(|_| "unknown".to_string(), |a| a.to_string()), - format_cors(cors) - ); - - Ok(handle) + pub max_payload_out_mb: u32, + /// Metrics. + pub metrics: Option, + /// RPC API. + pub rpc_api: RpcModule, + /// Subscription ID provider. + pub id_provider: Option>, + /// Tokio runtime handle. + pub tokio_handle: tokio::runtime::Handle, } -/// Start a JSON-RPC server listening on given address that supports both HTTP and WS. -pub async fn start( - addrs: [SocketAddr; 2], - cors: Option<&Vec>, - ws_config: WsConfig, - metrics: Option, - rpc_api: RpcModule, - rt: tokio::runtime::Handle, - id_provider: Option>, +/// Start RPC server listening on given address. +pub async fn start_server( + config: Config<'_, M>, ) -> Result> { - let (max_payload_in, max_payload_out, max_connections, max_subs_per_conn) = - ws_config.deconstruct(); - - let host_filter = hosts_filter(cors.is_some(), &addrs); + let Config { + addrs, + cors, + max_payload_in_mb, + max_payload_out_mb, + max_connections, + max_subs_per_conn, + metrics, + id_provider, + tokio_handle, + rpc_api, + } = config; + + let host_filter = hosts_filtering(cors.is_some(), &addrs); let middleware = tower::ServiceBuilder::new() // Proxy `GET /health` requests to internal `system_health` method. @@ -148,14 +94,14 @@ pub async fn start( .layer(try_into_cors(cors)?); let mut builder = ServerBuilder::new() - .max_request_body_size(max_payload_in) - .max_response_body_size(max_payload_out) + .max_request_body_size(max_payload_in_mb.saturating_mul(MEGABYTE)) + .max_response_body_size(max_payload_out_mb.saturating_mul(MEGABYTE)) .max_connections(max_connections) .max_subscriptions_per_connection(max_subs_per_conn) .ping_interval(std::time::Duration::from_secs(30)) .set_host_filtering(host_filter) .set_middleware(middleware) - .custom_tokio_runtime(rt); + .custom_tokio_runtime(tokio_handle); if let Some(provider) = id_provider { builder = builder.set_id_provider(provider); @@ -175,7 +121,7 @@ pub async fn start( }; log::info!( - "Running JSON-RPC WS server: addr={}, allowed origins={}", + "Running JSON-RPC server: addr={}, allowed origins={}", addr.map_or_else(|_| "unknown".to_string(), |a| a.to_string()), format_cors(cors) ); @@ -183,6 +129,20 @@ pub async fn start( Ok(handle) } +fn hosts_filtering(enabled: bool, addrs: &[SocketAddr]) -> AllowHosts { + if enabled { + // NOTE The listening addresses are whitelisted by default. + let mut hosts = Vec::with_capacity(addrs.len() * 2); + for addr in addrs { + hosts.push(format!("localhost:{}", addr.port()).into()); + hosts.push(format!("127.0.0.1:{}", addr.port()).into()); + } + AllowHosts::Only(hosts) + } else { + AllowHosts::Any + } +} + fn build_rpc_api(mut rpc_api: RpcModule) -> RpcModule { let mut available_methods = rpc_api.method_names().collect::>(); available_methods.sort(); @@ -198,24 +158,6 @@ fn build_rpc_api(mut rpc_api: RpcModule) -> RpcModu rpc_api } -fn payload_size_or_default(size_mb: Option) -> usize { - size_mb.map_or(RPC_MAX_PAYLOAD_DEFAULT, |mb| mb.saturating_mul(MEGABYTE)) -} - -fn hosts_filter(enabled: bool, addrs: &[SocketAddr]) -> AllowHosts { - if enabled { - // NOTE The listening addresses are whitelisted by default. - let mut hosts = Vec::with_capacity(addrs.len() * 2); - for addr in addrs { - hosts.push(format!("localhost:{}", addr.port()).into()); - hosts.push(format!("127.0.0.1:{}", addr.port()).into()); - } - AllowHosts::Only(hosts) - } else { - AllowHosts::Any - } -} - fn try_into_cors( maybe_cors: Option<&Vec>, ) -> Result> { diff --git a/client/service/src/config.rs b/client/service/src/config.rs index 6550fcdd8..40f8c6a4f 100644 --- a/client/service/src/config.rs +++ b/client/service/src/config.rs @@ -83,34 +83,24 @@ pub struct Configuration { pub wasm_runtime_overrides: Option, /// Execution strategies. pub execution_strategies: ExecutionStrategies, - /// RPC over HTTP binding address. `None` if disabled. - pub rpc_http: Option, - /// RPC over Websockets binding address. `None` if disabled. - pub rpc_ws: Option, - /// RPC over IPC binding path. `None` if disabled. - pub rpc_ipc: Option, - /// Maximum number of connections for WebSockets RPC server. `None` if default. - pub rpc_ws_max_connections: Option, + /// JSON-RPC server binding address. + pub rpc_addr: Option, + /// Maximum number of connections for JSON-RPC server. + pub rpc_max_connections: u32, /// CORS settings for HTTP & WS servers. `None` if all origins are allowed. pub rpc_cors: Option>, /// RPC methods to expose (by default only a safe subset or all of them). pub rpc_methods: RpcMethods, - /// Maximum payload of rpc request/responses. - pub rpc_max_payload: Option, /// Maximum payload of a rpc request - pub rpc_max_request_size: Option, - /// Maximum payload of a rpc request - pub rpc_max_response_size: Option, + pub rpc_max_request_size: u32, + /// Maximum payload of a rpc response. + pub rpc_max_response_size: u32, /// Custom JSON-RPC subscription ID provider. /// /// Default: [`crate::RandomStringSubscriptionId`]. pub rpc_id_provider: Option>, /// Maximum allowed subscriptions per rpc connection - /// - /// Default: 1024. - pub rpc_max_subs_per_conn: Option, - /// Maximum size of the output buffer capacity for websocket connections. - pub ws_max_out_buffer_capacity: Option, + pub rpc_max_subs_per_conn: u32, /// Prometheus endpoint configuration. `None` if disabled. pub prometheus_config: Option, /// Telemetry service URL. `None` if disabled. diff --git a/client/service/src/lib.rs b/client/service/src/lib.rs index c0c7c537c..a56677170 100644 --- a/client/service/src/lib.rs +++ b/client/service/src/lib.rs @@ -380,57 +380,37 @@ where } } - let (max_request_size, ws_max_response_size, http_max_response_size) = - legacy_cli_parsing(config); - - let random_port = |mut addr: SocketAddr| { + // if binding the specified port failed then a random port is assigned by the OS. + let backup_port = |mut addr: SocketAddr| { addr.set_port(0); addr }; - let ws_addr = config - .rpc_ws - .unwrap_or_else(|| "127.0.0.1:9944".parse().expect("valid sockaddr; qed")); - let ws_addr2 = random_port(ws_addr); - - let http_addr = config - .rpc_http - .unwrap_or_else(|| "127.0.0.1:9933".parse().expect("valid sockaddr; qed")); - let http_addr2 = random_port(http_addr); - + let addr = config.rpc_addr.unwrap_or(([127, 0, 0, 1], 9944).into()); + let backup_addr = backup_port(addr); let metrics = sc_rpc_server::RpcMetrics::new(config.prometheus_registry())?; - let server_config = sc_rpc_server::WsConfig { - max_connections: config.rpc_ws_max_connections, - max_payload_in_mb: max_request_size, - max_payload_out_mb: ws_max_response_size, + let server_config = sc_rpc_server::Config { + addrs: [addr, backup_addr], + max_connections: config.rpc_max_connections, + max_payload_in_mb: config.rpc_max_request_size, + max_payload_out_mb: config.rpc_max_response_size, max_subs_per_conn: config.rpc_max_subs_per_conn, + rpc_api: gen_rpc_module(deny_unsafe(addr, &config.rpc_methods))?, + metrics, + id_provider: rpc_id_provider, + cors: config.rpc_cors.as_ref(), + tokio_handle: config.tokio_handle.clone(), }; - let http_fut = sc_rpc_server::start_http( - [http_addr, http_addr2], - config.rpc_cors.as_ref(), - max_request_size, - http_max_response_size, - metrics.clone(), - gen_rpc_module(deny_unsafe(http_addr, &config.rpc_methods))?, - config.tokio_handle.clone(), - ); - - let ws_fut = sc_rpc_server::start( - [ws_addr, ws_addr2], - config.rpc_cors.as_ref(), - server_config.clone(), - metrics.clone(), - gen_rpc_module(deny_unsafe(ws_addr, &config.rpc_methods))?, - config.tokio_handle.clone(), - rpc_id_provider, - ); - + // TODO: https://github.com/paritytech/substrate/issues/13773 + // + // `block_in_place` is a hack to allow callers to call `block_on` prior to + // calling `start_rpc_servers`. match tokio::task::block_in_place(|| { - config.tokio_handle.block_on(futures::future::try_join(http_fut, ws_fut)) + config.tokio_handle.block_on(sc_rpc_server::start_server(server_config)) }) { - Ok((http, ws)) => Ok(Box::new((waiting::Server(Some(http)), waiting::Server(Some(ws))))), + Ok(server) => Ok(Box::new(waiting::Server(Some(server)))), Err(e) => Err(Error::Application(e)), } } @@ -534,51 +514,6 @@ where } } -fn legacy_cli_parsing(config: &Configuration) -> (Option, Option, Option) { - let ws_max_response_size = match ( - config.ws_max_out_buffer_capacity, - config.rpc_max_response_size, - ) { - (Some(legacy_max), max) => { - eprintln!("DEPRECATED: `--ws_max_out_buffer_capacity` has been removed; use `rpc-max-response-size or rpc-max-request-size` instead"); - eprintln!("Setting WS `rpc-max-response-size` to `max(ws_max_out_buffer_capacity, rpc_max_response_size)`"); - Some(std::cmp::max(legacy_max, max.unwrap_or(0))) - }, - (None, Some(m)) => Some(m), - (None, None) => None, - }; - - let max_request_size = match (config.rpc_max_payload, config.rpc_max_request_size) { - (Some(legacy_max), max) => { - eprintln!("DEPRECATED: `--rpc_max_payload` has been removed use `rpc-max-response-size or rpc-max-request-size` instead"); - eprintln!( - "Setting `rpc-max-response-size` to `max(rpc_max_payload, rpc_max_request_size)`" - ); - Some(std::cmp::max(legacy_max, max.unwrap_or(0))) - }, - (None, Some(max)) => Some(max), - (None, None) => None, - }; - - let http_max_response_size = match (config.rpc_max_payload, config.rpc_max_response_size) { - (Some(legacy_max), max) => { - eprintln!("DEPRECATED: `--rpc_max_payload` has been removed use `rpc-max-response-size or rpc-max-request-size` instead"); - eprintln!( - "Setting HTTP `rpc-max-response-size` to `max(rpc_max_payload, rpc_max_response_size)`" - ); - Some(std::cmp::max(legacy_max, max.unwrap_or(0))) - }, - (None, Some(max)) => Some(max), - (None, None) => None, - }; - - if config.rpc_ipc.is_some() { - eprintln!("DEPRECATED: `--ipc-path` has no effect anymore IPC support has been removed"); - } - - (max_request_size, ws_max_response_size, http_max_response_size) -} - #[cfg(test)] mod tests { use super::*; diff --git a/client/service/test/src/lib.rs b/client/service/test/src/lib.rs index 29e68261a..0eb4489ad 100644 --- a/client/service/test/src/lib.rs +++ b/client/service/test/src/lib.rs @@ -246,18 +246,14 @@ fn node_config< wasm_method: Default::default(), wasm_runtime_overrides: Default::default(), execution_strategies: Default::default(), - rpc_http: None, - rpc_ipc: None, - rpc_ws: None, - rpc_ws_max_connections: None, + rpc_addr: Default::default(), + rpc_max_connections: Default::default(), rpc_cors: None, rpc_methods: Default::default(), - rpc_max_payload: None, - rpc_max_request_size: None, - rpc_max_response_size: None, - rpc_id_provider: None, - rpc_max_subs_per_conn: None, - ws_max_out_buffer_capacity: None, + rpc_max_request_size: Default::default(), + rpc_max_response_size: Default::default(), + rpc_id_provider: Default::default(), + rpc_max_subs_per_conn: Default::default(), prometheus_config: None, telemetry_endpoints: None, default_heap_pages: None, diff --git a/test-utils/cli/src/lib.rs b/test-utils/cli/src/lib.rs index 1846f1909..f225cc135 100644 --- a/test-utils/cli/src/lib.rs +++ b/test-utils/cli/src/lib.rs @@ -282,7 +282,7 @@ pub fn extract_info_from_output(read: impl Read + Send) -> (NodeInfo, String) { data.push_str("\n"); // does the line contain our port (we expect this specific output from substrate). - let sock_addr = match line.split_once("Running JSON-RPC WS server: addr=") { + let sock_addr = match line.split_once("Running JSON-RPC server: addr=") { None => return None, Some((_, after)) => after.split_once(",").unwrap().0, }; From d3ce947c365c2677d7e84557236362a56efb4e42 Mon Sep 17 00:00:00 2001 From: Arkadiy Paronyan Date: Thu, 4 May 2023 12:24:32 +0200 Subject: [PATCH 452/558] Statement store (#13701) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * WIP Statement store * Sync with networking changes in master * WIP statement pallet * Statement validation * pallet tests * Validation queue * Store maintenance * Basic statement refactoring + tests + docs * Store metrics * Store tests * Store maintenance test * cargo fmt * Build fix * OCW Api * Offchain worker * Enable host functions * fmt * Minor tweaks * Fixed a warning * Removed tracing * Manual expiration * Reworked constraint management * Updated pallet constraint calculation * Added small test * Added remove function to the APIs * Copy-paste spec into readme * Comments * Made the store optional * Removed network protocol controller * fmt * Clippy fixes * fmt * fmt * More clippy fixes * More clippy fixes * More clippy fixes * Update client/statement-store/README.md Co-authored-by: cheme * Apply suggestions from code review Co-authored-by: Bastian Köcher * Removed sstore from node-template * Sort out data path * Added offline check * Removed dispatch_statement * Renamed into_generic * Fixed commit placement * Use HashSet for tracking peers/statements * fmt * Use ExtendedHostFunctions * Fixed benches * Tweaks * Apply suggestions from code review Co-authored-by: cheme * Fixed priority mixup * Rename * newtypes for priorities * Added MAX_TOPICS * Fixed key filtering logic * Remove empty entrie * Removed prefix from signing * More documentation * fmt * Moved store setup from sc-service to node * Handle maintenance task in sc-statement-store * Use statement iterator * Renamed runtime API mod * fmt * Remove dump_encoded * fmt * Apply suggestions from code review Co-authored-by: Bastian Köcher * Apply suggestions from code review Co-authored-by: Bastian Köcher * Fixed build after applying review suggestions * License exceptions * fmt * Store options * Moved pallet consts to config trait * Removed global priority * Validate fields when decoding * Limit validation channel size * Made a comment into module doc * Removed submit_encoded --------- Co-authored-by: cheme Co-authored-by: Bastian Köcher --- Cargo.lock | 88 ++ Cargo.toml | 4 + bin/node-template/node/Cargo.toml | 1 + bin/node/cli/Cargo.toml | 2 + bin/node/cli/benches/block_production.rs | 3 +- bin/node/cli/benches/transaction_pool.rs | 3 +- bin/node/cli/src/service.rs | 49 +- bin/node/executor/Cargo.toml | 1 + bin/node/executor/src/lib.rs | 5 +- bin/node/rpc/Cargo.toml | 1 + bin/node/rpc/src/lib.rs | 21 +- bin/node/runtime/Cargo.toml | 5 + bin/node/runtime/src/lib.rs | 30 + client/api/Cargo.toml | 1 + client/api/src/execution_extensions.rs | 15 +- client/cli/src/config.rs | 3 +- client/cli/src/runner.rs | 6 +- client/network/statement/Cargo.toml | 29 + client/network/statement/src/config.rs | 33 + client/network/statement/src/lib.rs | 486 +++++++ client/rpc-api/src/lib.rs | 1 + client/rpc-api/src/statement/error.rs | 55 + client/rpc-api/src/statement/mod.rs | 60 + client/rpc/Cargo.toml | 1 + client/rpc/src/lib.rs | 1 + client/rpc/src/statement/mod.rs | 108 ++ client/service/src/builder.rs | 2 +- client/service/src/config.rs | 6 +- client/service/test/src/lib.rs | 3 +- client/statement-store/Cargo.toml | 36 + client/statement-store/README.md | 4 + client/statement-store/src/lib.rs | 1226 +++++++++++++++++ client/statement-store/src/metrics.rs | 79 ++ frame/statement/Cargo.toml | 46 + frame/statement/src/lib.rs | 222 +++ frame/statement/src/mock.rs | 126 ++ frame/statement/src/tests.rs | 159 +++ frame/system/src/lib.rs | 8 + primitives/application-crypto/src/lib.rs | 14 + primitives/blockchain/src/error.rs | 3 + primitives/core/src/crypto.rs | 2 + primitives/core/src/offchain/mod.rs | 12 +- primitives/statement-store/Cargo.toml | 41 + primitives/statement-store/README.md | 39 + primitives/statement-store/src/lib.rs | 618 +++++++++ primitives/statement-store/src/runtime_api.rs | 187 +++ primitives/statement-store/src/store_api.rs | 90 ++ scripts/ci/deny.toml | 2 + 48 files changed, 3911 insertions(+), 26 deletions(-) create mode 100644 client/network/statement/Cargo.toml create mode 100644 client/network/statement/src/config.rs create mode 100644 client/network/statement/src/lib.rs create mode 100644 client/rpc-api/src/statement/error.rs create mode 100644 client/rpc-api/src/statement/mod.rs create mode 100644 client/rpc/src/statement/mod.rs create mode 100644 client/statement-store/Cargo.toml create mode 100644 client/statement-store/README.md create mode 100644 client/statement-store/src/lib.rs create mode 100644 client/statement-store/src/metrics.rs create mode 100644 frame/statement/Cargo.toml create mode 100644 frame/statement/src/lib.rs create mode 100644 frame/statement/src/mock.rs create mode 100644 frame/statement/src/tests.rs create mode 100644 primitives/statement-store/Cargo.toml create mode 100644 primitives/statement-store/README.md create mode 100644 primitives/statement-store/src/lib.rs create mode 100644 primitives/statement-store/src/runtime_api.rs create mode 100644 primitives/statement-store/src/store_api.rs diff --git a/Cargo.lock b/Cargo.lock index 42ad18552..e65562a56 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3783,6 +3783,7 @@ dependencies = [ "pallet-staking-reward-curve", "pallet-staking-runtime-api", "pallet-state-trie-migration", + "pallet-statement", "pallet-sudo", "pallet-timestamp", "pallet-tips", @@ -3808,6 +3809,7 @@ dependencies = [ "sp-runtime", "sp-session", "sp-staking", + "sp-statement-store", "sp-std", "sp-transaction-pool", "sp-version", @@ -5120,10 +5122,12 @@ dependencies = [ "sc-keystore", "sc-network", "sc-network-common", + "sc-network-statement", "sc-network-sync", "sc-rpc", "sc-service", "sc-service-test", + "sc-statement-store", "sc-storage-monitor", "sc-sync-state-rpc", "sc-sysinfo", @@ -5192,6 +5196,7 @@ dependencies = [ "sp-keystore", "sp-runtime", "sp-state-machine", + "sp-statement-store", "sp-tracing", "sp-trie", "wat", @@ -5251,6 +5256,7 @@ dependencies = [ "sp-consensus-babe", "sp-keystore", "sp-runtime", + "sp-statement-store", "substrate-frame-rpc-system", "substrate-state-trie-migration-rpc", ] @@ -5288,6 +5294,7 @@ dependencies = [ "sc-rpc", "sc-rpc-api", "sc-service", + "sc-statement-store", "sc-telemetry", "sc-transaction-pool", "sc-transaction-pool-api", @@ -6990,6 +6997,24 @@ dependencies = [ "zstd 0.12.3+zstd.1.5.2", ] +[[package]] +name = "pallet-statement" +version = "4.0.0-dev" +dependencies = [ + "frame-support", + "frame-system", + "log", + "pallet-balances", + "parity-scale-codec", + "scale-info", + "sp-api", + "sp-core", + "sp-io", + "sp-runtime", + "sp-statement-store", + "sp-std", +] + [[package]] name = "pallet-sudo" version = "4.0.0-dev" @@ -8618,6 +8643,7 @@ dependencies = [ "sp-keystore", "sp-runtime", "sp-state-machine", + "sp-statement-store", "sp-storage", "sp-test-primitives", "substrate-prometheus-endpoint", @@ -9293,6 +9319,26 @@ dependencies = [ "thiserror", ] +[[package]] +name = "sc-network-statement" +version = "0.10.0-dev" +dependencies = [ + "array-bytes", + "async-channel", + "futures", + "libp2p", + "log", + "parity-scale-codec", + "pin-project", + "sc-network", + "sc-network-common", + "sc-peerset", + "sp-consensus", + "sp-runtime", + "sp-statement-store", + "substrate-prometheus-endpoint", +] + [[package]] name = "sc-network-sync" version = "0.10.0-dev" @@ -9474,6 +9520,7 @@ dependencies = [ "sp-rpc", "sp-runtime", "sp-session", + "sp-statement-store", "sp-version", "substrate-test-runtime-client", "tokio", @@ -9672,6 +9719,30 @@ dependencies = [ "sp-core", ] +[[package]] +name = "sc-statement-store" +version = "4.0.0-dev" +dependencies = [ + "async-trait", + "env_logger 0.9.3", + "futures", + "futures-timer", + "log", + "parity-db", + "parity-scale-codec", + "parking_lot 0.12.1", + "sc-client-api", + "sp-api", + "sp-blockchain", + "sp-core", + "sp-runtime", + "sp-statement-store", + "sp-tracing", + "substrate-prometheus-endpoint", + "tempfile", + "tokio", +] + [[package]] name = "sc-storage-monitor" version = "0.1.0" @@ -10980,6 +11051,23 @@ dependencies = [ "trie-db", ] +[[package]] +name = "sp-statement-store" +version = "4.0.0-dev" +dependencies = [ + "log", + "parity-scale-codec", + "scale-info", + "sp-api", + "sp-application-crypto", + "sp-core", + "sp-externalities", + "sp-runtime", + "sp-runtime-interface", + "sp-std", + "thiserror", +] + [[package]] name = "sp-std" version = "5.0.0" diff --git a/Cargo.toml b/Cargo.toml index 2f3c0e946..da5e1d532 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -46,6 +46,7 @@ members = [ "client/merkle-mountain-range/rpc", "client/network", "client/network/transactions", + "client/network/statement", "client/network-gossip", "client/network/bitswap", "client/network/common", @@ -63,6 +64,7 @@ members = [ "client/service", "client/service/test", "client/state-db", + "client/statement-store", "client/storage-monitor", "client/sysinfo", "client/sync-state-rpc", @@ -151,6 +153,7 @@ members = [ "frame/sudo", "frame/root-offences", "frame/root-testing", + "frame/statement", "frame/support", "frame/support/procedural", "frame/support/procedural/tools", @@ -220,6 +223,7 @@ members = [ "primitives/session", "primitives/staking", "primitives/state-machine", + "primitives/statement-store", "primitives/std", "primitives/storage", "primitives/test-primitives", diff --git a/bin/node-template/node/Cargo.toml b/bin/node-template/node/Cargo.toml index 42011075b..b729667c5 100644 --- a/bin/node-template/node/Cargo.toml +++ b/bin/node-template/node/Cargo.toml @@ -28,6 +28,7 @@ sc-telemetry = { version = "4.0.0-dev", path = "../../../client/telemetry" } sc-keystore = { version = "4.0.0-dev", path = "../../../client/keystore" } sc-transaction-pool = { version = "4.0.0-dev", path = "../../../client/transaction-pool" } sc-transaction-pool-api = { version = "4.0.0-dev", path = "../../../client/transaction-pool/api" } +sc-statement-store = { version = "4.0.0-dev", path = "../../../client/statement-store" } sc-consensus-aura = { version = "0.10.0-dev", path = "../../../client/consensus/aura" } sp-consensus-aura = { version = "0.10.0-dev", path = "../../../primitives/consensus/aura" } sp-consensus = { version = "0.10.0-dev", path = "../../../primitives/consensus/common" } diff --git a/bin/node/cli/Cargo.toml b/bin/node/cli/Cargo.toml index e37e2eb94..ca9b25b6e 100644 --- a/bin/node/cli/Cargo.toml +++ b/bin/node/cli/Cargo.toml @@ -66,9 +66,11 @@ sc-chain-spec = { version = "4.0.0-dev", path = "../../../client/chain-spec" } sc-consensus = { version = "0.10.0-dev", path = "../../../client/consensus/common" } sc-transaction-pool = { version = "4.0.0-dev", path = "../../../client/transaction-pool" } sc-transaction-pool-api = { version = "4.0.0-dev", path = "../../../client/transaction-pool/api" } +sc-statement-store = { version = "4.0.0-dev", path = "../../../client/statement-store" } sc-network = { version = "0.10.0-dev", path = "../../../client/network" } sc-network-common = { version = "0.10.0-dev", path = "../../../client/network/common" } sc-network-sync = { version = "0.10.0-dev", path = "../../../client/network/sync" } +sc-network-statement = { version = "0.10.0-dev", path = "../../../client/network/statement" } sc-consensus-slots = { version = "0.10.0-dev", path = "../../../client/consensus/slots" } sc-consensus-babe = { version = "0.10.0-dev", path = "../../../client/consensus/babe" } grandpa = { version = "0.10.0-dev", package = "sc-consensus-grandpa", path = "../../../client/consensus/grandpa" } diff --git a/bin/node/cli/benches/block_production.rs b/bin/node/cli/benches/block_production.rs index 36e807692..fd0bbb71d 100644 --- a/bin/node/cli/benches/block_production.rs +++ b/bin/node/cli/benches/block_production.rs @@ -104,7 +104,8 @@ fn new_node(tokio_handle: Handle) -> node_cli::service::NewFullBase { max_runtime_instances: 8, runtime_cache_size: 2, announce_block: true, - base_path: Some(base_path), + data_path: base_path.path().into(), + base_path, informant_output_format: Default::default(), wasm_runtime_overrides: None, }; diff --git a/bin/node/cli/benches/transaction_pool.rs b/bin/node/cli/benches/transaction_pool.rs index 14d99c37a..dc0025530 100644 --- a/bin/node/cli/benches/transaction_pool.rs +++ b/bin/node/cli/benches/transaction_pool.rs @@ -98,7 +98,8 @@ fn new_node(tokio_handle: Handle) -> node_cli::service::NewFullBase { max_runtime_instances: 8, runtime_cache_size: 2, announce_block: true, - base_path: Some(base_path), + data_path: base_path.path().into(), + base_path, informant_output_format: Default::default(), wasm_runtime_overrides: None, }; diff --git a/bin/node/cli/src/service.rs b/bin/node/cli/src/service.rs index de6401805..b704bf029 100644 --- a/bin/node/cli/src/service.rs +++ b/bin/node/cli/src/service.rs @@ -35,6 +35,7 @@ use sc_network::{event::Event, NetworkEventStream, NetworkService}; use sc_network_common::sync::warp::WarpSyncParams; use sc_network_sync::SyncingService; use sc_service::{config::Configuration, error::Error as ServiceError, RpcHandlers, TaskManager}; +use sc_statement_store::Store as StatementStore; use sc_telemetry::{Telemetry, TelemetryWorker}; use sp_api::ProvideRuntimeApi; use sp_core::crypto::Pair; @@ -148,6 +149,7 @@ pub fn new_partial( ), grandpa::SharedVoterState, Option, + Arc, ), >, ServiceError, @@ -227,6 +229,15 @@ pub fn new_partial( let import_setup = (block_import, grandpa_link, babe_link); + let statement_store = sc_statement_store::Store::new_shared( + &config.data_path, + Default::default(), + client.clone(), + config.prometheus_registry(), + &task_manager.spawn_handle(), + ) + .map_err(|e| ServiceError::Other(format!("Statement store error: {:?}", e)))?; + let (rpc_extensions_builder, rpc_setup) = { let (_, grandpa_link, _) = &import_setup; @@ -247,6 +258,7 @@ pub fn new_partial( let chain_spec = config.chain_spec.cloned_box(); let rpc_backend = backend.clone(); + let rpc_statement_store = statement_store.clone(); let rpc_extensions_builder = move |deny_unsafe, subscription_executor| { let deps = node_rpc::FullDeps { client: client.clone(), @@ -265,6 +277,7 @@ pub fn new_partial( subscription_executor, finality_provider: finality_proof_provider.clone(), }, + statement_store: rpc_statement_store.clone(), }; node_rpc::create_full(deps, rpc_backend.clone()).map_err(Into::into) @@ -281,7 +294,7 @@ pub fn new_partial( select_chain, import_queue, transaction_pool, - other: (rpc_extensions_builder, import_setup, rpc_setup, telemetry), + other: (rpc_extensions_builder, import_setup, rpc_setup, telemetry, statement_store), }) } @@ -325,7 +338,7 @@ pub fn new_full_base( keystore_container, select_chain, transaction_pool, - other: (rpc_builder, import_setup, rpc_setup, mut telemetry), + other: (rpc_builder, import_setup, rpc_setup, mut telemetry, statement_store), } = new_partial(&config)?; let shared_voter_state = rpc_setup; @@ -335,6 +348,16 @@ pub fn new_full_base( &config.chain_spec, ); + let statement_handler_proto = sc_network_statement::StatementHandlerPrototype::new( + client + .block_hash(0u32.into()) + .ok() + .flatten() + .expect("Genesis block exists; qed"), + config.chain_spec.fork_id(), + ); + config.network.extra_sets.push(statement_handler_proto.set_config()); + config .network .extra_sets @@ -526,7 +549,7 @@ pub fn new_full_base( sync: Arc::new(sync_service.clone()), telemetry: telemetry.as_ref().map(|x| x.handle()), voting_rule: grandpa::VotingRulesBuilder::default().build(), - prometheus_registry, + prometheus_registry: prometheus_registry.clone(), shared_voter_state, }; @@ -539,6 +562,26 @@ pub fn new_full_base( ); } + // Spawn statement protocol worker + let statement_protocol_executor = { + let spawn_handle = task_manager.spawn_handle(); + Box::new(move |fut| { + spawn_handle.spawn("network-statement-validator", Some("networking"), fut); + }) + }; + let statement_handler = statement_handler_proto.build( + network.clone(), + sync_service.clone(), + statement_store.clone(), + prometheus_registry.as_ref(), + statement_protocol_executor, + )?; + task_manager.spawn_handle().spawn( + "network-statement-handler", + Some("networking"), + statement_handler.run(), + ); + network_starter.start_network(); Ok(NewFullBase { task_manager, diff --git a/bin/node/executor/Cargo.toml b/bin/node/executor/Cargo.toml index c4711adcb..5f11d513c 100644 --- a/bin/node/executor/Cargo.toml +++ b/bin/node/executor/Cargo.toml @@ -24,6 +24,7 @@ sp-keystore = { version = "0.13.0", path = "../../../primitives/keystore" } sp-state-machine = { version = "0.13.0", path = "../../../primitives/state-machine" } sp-tracing = { version = "6.0.0", path = "../../../primitives/tracing" } sp-trie = { version = "7.0.0", path = "../../../primitives/trie" } +sp-statement-store = { version = "4.0.0-dev", path = "../../../primitives/statement-store" } [dev-dependencies] criterion = "0.4.0" diff --git a/bin/node/executor/src/lib.rs b/bin/node/executor/src/lib.rs index 4e3ec9a0b..3557a1674 100644 --- a/bin/node/executor/src/lib.rs +++ b/bin/node/executor/src/lib.rs @@ -25,7 +25,10 @@ pub use sc_executor::NativeElseWasmExecutor; pub struct ExecutorDispatch; impl sc_executor::NativeExecutionDispatch for ExecutorDispatch { - type ExtendHostFunctions = frame_benchmarking::benchmarking::HostFunctions; + type ExtendHostFunctions = ( + frame_benchmarking::benchmarking::HostFunctions, + sp_statement_store::runtime_api::HostFunctions, + ); fn dispatch(method: &str, data: &[u8]) -> Option> { kitchensink_runtime::api::dispatch(method, data) diff --git a/bin/node/rpc/Cargo.toml b/bin/node/rpc/Cargo.toml index 724efbe9a..8a336242c 100644 --- a/bin/node/rpc/Cargo.toml +++ b/bin/node/rpc/Cargo.toml @@ -35,5 +35,6 @@ sp-consensus = { version = "0.10.0-dev", path = "../../../primitives/consensus/c sp-consensus-babe = { version = "0.10.0-dev", path = "../../../primitives/consensus/babe" } sp-keystore = { version = "0.13.0", path = "../../../primitives/keystore" } sp-runtime = { version = "7.0.0", path = "../../../primitives/runtime" } +sp-statement-store = { version = "4.0.0-dev", path = "../../../primitives/statement-store" } substrate-frame-rpc-system = { version = "4.0.0-dev", path = "../../../utils/frame/rpc/system" } substrate-state-trie-migration-rpc = { version = "4.0.0-dev", path = "../../../utils/frame/rpc/state-trie-migration-rpc/" } diff --git a/bin/node/rpc/src/lib.rs b/bin/node/rpc/src/lib.rs index 5f61fdcd5..5ab96bf1c 100644 --- a/bin/node/rpc/src/lib.rs +++ b/bin/node/rpc/src/lib.rs @@ -88,6 +88,8 @@ pub struct FullDeps { pub babe: BabeDeps, /// GRANDPA specific dependencies. pub grandpa: GrandpaDeps, + /// Shared statement store reference. + pub statement_store: Arc, } /// Instantiate all Full RPC extensions. @@ -118,14 +120,26 @@ where use pallet_transaction_payment_rpc::{TransactionPayment, TransactionPaymentApiServer}; use sc_consensus_babe_rpc::{Babe, BabeApiServer}; use sc_consensus_grandpa_rpc::{Grandpa, GrandpaApiServer}; - use sc_rpc::dev::{Dev, DevApiServer}; + use sc_rpc::{ + dev::{Dev, DevApiServer}, + statement::StatementApiServer, + }; use sc_rpc_spec_v2::chain_spec::{ChainSpec, ChainSpecApiServer}; use sc_sync_state_rpc::{SyncState, SyncStateApiServer}; use substrate_frame_rpc_system::{System, SystemApiServer}; use substrate_state_trie_migration_rpc::{StateMigration, StateMigrationApiServer}; let mut io = RpcModule::new(()); - let FullDeps { client, pool, select_chain, chain_spec, deny_unsafe, babe, grandpa } = deps; + let FullDeps { + client, + pool, + select_chain, + chain_spec, + deny_unsafe, + babe, + grandpa, + statement_store, + } = deps; let BabeDeps { keystore, babe_worker_handle } = babe; let GrandpaDeps { @@ -169,6 +183,9 @@ where io.merge(StateMigration::new(client.clone(), backend, deny_unsafe).into_rpc())?; io.merge(Dev::new(client, deny_unsafe).into_rpc())?; + let statement_store = + sc_rpc::statement::StatementStore::new(statement_store, deny_unsafe).into_rpc(); + io.merge(statement_store)?; Ok(io) } diff --git a/bin/node/runtime/Cargo.toml b/bin/node/runtime/Cargo.toml index 0fa037c65..dadd868e1 100644 --- a/bin/node/runtime/Cargo.toml +++ b/bin/node/runtime/Cargo.toml @@ -39,6 +39,7 @@ sp-runtime = { version = "7.0.0", default-features = false, path = "../../../pri sp-staking = { version = "4.0.0-dev", default-features = false, path = "../../../primitives/staking" } sp-session = { version = "4.0.0-dev", default-features = false, path = "../../../primitives/session" } sp-transaction-pool = { version = "4.0.0-dev", default-features = false, path = "../../../primitives/transaction-pool" } +sp-statement-store = { version = "4.0.0-dev", default-features = false, path = "../../../primitives/statement-store" } sp-version = { version = "5.0.0", default-features = false, path = "../../../primitives/version" } sp-io = { version = "7.0.0", default-features = false, path = "../../../primitives/io" } @@ -105,6 +106,7 @@ pallet-staking = { version = "4.0.0-dev", default-features = false, path = "../. pallet-staking-reward-curve = { version = "4.0.0-dev", default-features = false, path = "../../../frame/staking/reward-curve" } pallet-staking-runtime-api = { version = "4.0.0-dev", default-features = false, path = "../../../frame/staking/runtime-api" } pallet-state-trie-migration = { version = "4.0.0-dev", default-features = false, path = "../../../frame/state-trie-migration" } +pallet-statement = { version = "4.0.0-dev", default-features = false, path = "../../../frame/statement" } pallet-scheduler = { version = "4.0.0-dev", default-features = false, path = "../../../frame/scheduler" } pallet-society = { version = "4.0.0-dev", default-features = false, path = "../../../frame/society" } pallet-sudo = { version = "4.0.0-dev", default-features = false, path = "../../../frame/sudo" } @@ -187,6 +189,7 @@ std = [ "pallet-staking/std", "pallet-staking-runtime-api/std", "pallet-state-trie-migration/std", + "pallet-statement/std", "pallet-salary/std", "sp-session/std", "pallet-sudo/std", @@ -204,6 +207,7 @@ std = [ "pallet-treasury/std", "pallet-asset-rate/std", "sp-transaction-pool/std", + "sp-statement-store/std", "pallet-utility/std", "sp-version/std", "pallet-society/std", @@ -330,6 +334,7 @@ try-runtime = [ "pallet-session/try-runtime", "pallet-staking/try-runtime", "pallet-state-trie-migration/try-runtime", + "pallet-statement/try-runtime", "pallet-scheduler/try-runtime", "pallet-society/try-runtime", "pallet-sudo/try-runtime", diff --git a/bin/node/runtime/src/lib.rs b/bin/node/runtime/src/lib.rs index 60d954913..5ce55ba8b 100644 --- a/bin/node/runtime/src/lib.rs +++ b/bin/node/runtime/src/lib.rs @@ -1755,6 +1755,26 @@ impl frame_benchmarking_pallet_pov::Config for Runtime { type RuntimeEvent = RuntimeEvent; } +parameter_types! { + pub StatementCost: Balance = 1 * DOLLARS; + pub StatementByteCost: Balance = 100 * MILLICENTS; + pub const MinAllowedStatements: u32 = 4; + pub const MaxAllowedStatements: u32 = 10; + pub const MinAllowedBytes: u32 = 1024; + pub const MaxAllowedBytes: u32 = 4096; +} + +impl pallet_statement::Config for Runtime { + type RuntimeEvent = RuntimeEvent; + type Currency = Balances; + type StatementCost = StatementCost; + type ByteCost = StatementByteCost; + type MinAllowedStatements = MinAllowedStatements; + type MaxAllowedStatements = MaxAllowedStatements; + type MinAllowedBytes = MinAllowedBytes; + type MaxAllowedBytes = MaxAllowedBytes; +} + construct_runtime!( pub struct Runtime where Block = Block, @@ -1826,6 +1846,7 @@ construct_runtime!( FastUnstake: pallet_fast_unstake, MessageQueue: pallet_message_queue, Pov: frame_benchmarking_pallet_pov, + Statement: pallet_statement, } ); @@ -2011,6 +2032,15 @@ impl_runtime_apis! { } } + impl sp_statement_store::runtime_api::ValidateStatement for Runtime { + fn validate_statement( + source: sp_statement_store::runtime_api::StatementSource, + statement: sp_statement_store::Statement, + ) -> Result { + Statement::validate_statement(source, statement) + } + } + impl sp_offchain::OffchainWorkerApi for Runtime { fn offchain_worker(header: &::Header) { Executive::offchain_worker(header) diff --git a/client/api/Cargo.toml b/client/api/Cargo.toml index f49420085..02f4292aa 100644 --- a/client/api/Cargo.toml +++ b/client/api/Cargo.toml @@ -34,6 +34,7 @@ sp-externalities = { version = "0.13.0", path = "../../primitives/externalities" sp-keystore = { version = "0.13.0", default-features = false, path = "../../primitives/keystore" } sp-runtime = { version = "7.0.0", default-features = false, path = "../../primitives/runtime" } sp-state-machine = { version = "0.13.0", path = "../../primitives/state-machine" } +sp-statement-store = { version = "4.0.0-dev", path = "../../primitives/statement-store" } sp-storage = { version = "7.0.0", path = "../../primitives/storage" } [dev-dependencies] diff --git a/client/api/src/execution_extensions.rs b/client/api/src/execution_extensions.rs index 9344afbd3..9ff4b6db4 100644 --- a/client/api/src/execution_extensions.rs +++ b/client/api/src/execution_extensions.rs @@ -166,7 +166,7 @@ pub struct ExecutionExtensions { strategies: ExecutionStrategies, keystore: Option, offchain_db: Option>, - // FIXME: these two are only RwLock because of https://github.com/paritytech/substrate/issues/4587 + // FIXME: these three are only RwLock because of https://github.com/paritytech/substrate/issues/4587 // remove when fixed. // To break retain cycle between `Client` and `TransactionPool` we require this // extension to be a `Weak` reference. @@ -174,6 +174,7 @@ pub struct ExecutionExtensions { // during initialization. transaction_pool: RwLock>>>, extensions_factory: RwLock>>, + statement_store: RwLock>>, read_runtime_version: Arc, } @@ -186,6 +187,7 @@ impl ExecutionExtensions { read_runtime_version: Arc, ) -> Self { let transaction_pool = RwLock::new(None); + let statement_store = RwLock::new(None); let extensions_factory = Box::new(()); Self { strategies, @@ -193,6 +195,7 @@ impl ExecutionExtensions { offchain_db, extensions_factory: RwLock::new(extensions_factory), transaction_pool, + statement_store, read_runtime_version, } } @@ -215,6 +218,11 @@ impl ExecutionExtensions { *self.transaction_pool.write() = Some(Arc::downgrade(pool) as _); } + /// Register statement store extension. + pub fn register_statement_store(&self, store: Arc) { + *self.statement_store.write() = Some(Arc::downgrade(&store) as _); + } + /// Based on the execution context and capabilities it produces /// the extensions object to support desired set of APIs. pub fn extensions( @@ -245,6 +253,11 @@ impl ExecutionExtensions { } } + if capabilities.contains(offchain::Capabilities::STATEMENT_STORE) { + if let Some(store) = self.statement_store.read().as_ref().and_then(|x| x.upgrade()) { + extensions.register(sp_statement_store::runtime_api::StatementStoreExt(store)); + } + } if capabilities.contains(offchain::Capabilities::OFFCHAIN_DB_READ) || capabilities.contains(offchain::Capabilities::OFFCHAIN_DB_WRITE) { diff --git a/client/cli/src/config.rs b/client/cli/src/config.rs index 92b1eea7a..01f6adbba 100644 --- a/client/cli/src/config.rs +++ b/client/cli/src/config.rs @@ -491,6 +491,7 @@ pub trait CliConfiguration: Sized { )?, keystore, database: self.database_config(&config_dir, database_cache_size, database)?, + data_path: config_dir, trie_cache_maximum_size: self.trie_cache_maximum_size()?, state_pruning: self.state_pruning()?, blocks_pruning: self.blocks_pruning()?, @@ -519,7 +520,7 @@ pub trait CliConfiguration: Sized { max_runtime_instances, announce_block: self.announce_block()?, role, - base_path: Some(base_path), + base_path, informant_output_format: Default::default(), runtime_cache_size, }) diff --git a/client/cli/src/runner.rs b/client/cli/src/runner.rs index 9db078046..1fa12e2a0 100644 --- a/client/cli/src/runner.rs +++ b/client/cli/src/runner.rs @@ -259,6 +259,7 @@ mod tests { fn create_runner() -> Runner { let runtime = build_runtime().unwrap(); + let root = PathBuf::from("db"); let runner = Runner::new( Configuration { impl_name: "spec".into(), @@ -268,7 +269,7 @@ mod tests { transaction_pool: Default::default(), network: NetworkConfiguration::new_memory(), keystore: sc_service::config::KeystoreConfig::InMemory, - database: sc_client_db::DatabaseSource::ParityDb { path: PathBuf::from("db") }, + database: sc_client_db::DatabaseSource::ParityDb { path: root.clone() }, trie_cache_maximum_size: None, state_pruning: None, blocks_pruning: sc_client_db::BlocksPruning::KeepAll, @@ -306,7 +307,8 @@ mod tests { tracing_receiver: Default::default(), max_runtime_instances: 8, announce_block: true, - base_path: None, + base_path: sc_service::BasePath::new(root.clone()), + data_path: root, informant_output_format: Default::default(), runtime_cache_size: 2, }, diff --git a/client/network/statement/Cargo.toml b/client/network/statement/Cargo.toml new file mode 100644 index 000000000..36d8cb077 --- /dev/null +++ b/client/network/statement/Cargo.toml @@ -0,0 +1,29 @@ +[package] +description = "Substrate statement protocol" +name = "sc-network-statement" +version = "0.10.0-dev" +license = "GPL-3.0-or-later WITH Classpath-exception-2.0" +authors = ["Parity Technologies "] +edition = "2021" +homepage = "https://substrate.io" +repository = "https://github.com/paritytech/substrate/" +documentation = "https://docs.rs/sc-network-statement" + +[package.metadata.docs.rs] +targets = ["x86_64-unknown-linux-gnu"] + +[dependencies] +array-bytes = "4.1" +async-channel = "1.8.0" +codec = { package = "parity-scale-codec", version = "3.2.2", features = ["derive"] } +futures = "0.3.21" +libp2p = "0.50.0" +log = "0.4.17" +pin-project = "1.0.12" +prometheus-endpoint = { package = "substrate-prometheus-endpoint", version = "0.10.0-dev", path = "../../../utils/prometheus" } +sc-network-common = { version = "0.10.0-dev", path = "../common" } +sc-network = { version = "0.10.0-dev", path = "../" } +sc-peerset = { version = "4.0.0-dev", path = "../../peerset" } +sp-runtime = { version = "7.0.0", path = "../../../primitives/runtime" } +sp-consensus = { version = "0.10.0-dev", path = "../../../primitives/consensus/common" } +sp-statement-store = { version = "4.0.0-dev", path = "../../../primitives/statement-store" } diff --git a/client/network/statement/src/config.rs b/client/network/statement/src/config.rs new file mode 100644 index 000000000..159998a0f --- /dev/null +++ b/client/network/statement/src/config.rs @@ -0,0 +1,33 @@ +// This file is part of Substrate. + +// Copyright (C) Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +//! Configuration of the statement protocol + +use std::time; + +/// Interval at which we propagate statements; +pub(crate) const PROPAGATE_TIMEOUT: time::Duration = time::Duration::from_millis(1000); + +/// Maximum number of known statement hashes to keep for a peer. +pub(crate) const MAX_KNOWN_STATEMENTS: usize = 10240; + +/// Maximum allowed size for a statement notification. +pub(crate) const MAX_STATEMENT_SIZE: u64 = 256 * 1024; + +/// Maximum number of statement validation request we keep at any moment. +pub(crate) const MAX_PENDING_STATEMENTS: usize = 8192; diff --git a/client/network/statement/src/lib.rs b/client/network/statement/src/lib.rs new file mode 100644 index 000000000..02cbab27a --- /dev/null +++ b/client/network/statement/src/lib.rs @@ -0,0 +1,486 @@ +// This file is part of Substrate. + +// Copyright (C) Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +//! Statement handling to plug on top of the network service. +//! +//! Usage: +//! +//! - Use [`StatementHandlerPrototype::new`] to create a prototype. +//! - Pass the return value of [`StatementHandlerPrototype::set_config`] to the network +//! configuration as an extra peers set. +//! - Use [`StatementHandlerPrototype::build`] then [`StatementHandler::run`] to obtain a +//! `Future` that processes statements. + +use crate::config::*; +use codec::{Decode, Encode}; +use futures::{channel::oneshot, prelude::*, stream::FuturesUnordered, FutureExt}; +use libp2p::{multiaddr, PeerId}; +use prometheus_endpoint::{register, Counter, PrometheusError, Registry, U64}; +use sc_network::{ + config::{NonDefaultSetConfig, NonReservedPeerMode, SetConfig}, + error, + event::Event, + types::ProtocolName, + utils::{interval, LruHashSet}, + NetworkEventStream, NetworkNotification, NetworkPeers, +}; +use sc_network_common::{ + role::ObservedRole, + sync::{SyncEvent, SyncEventStream}, +}; +use sp_statement_store::{ + Hash, NetworkPriority, Statement, StatementSource, StatementStore, SubmitResult, +}; +use std::{ + collections::{hash_map::Entry, HashMap, HashSet}, + iter, + num::NonZeroUsize, + pin::Pin, + sync::Arc, +}; + +pub mod config; + +/// A set of statements. +pub type Statements = Vec; +/// Future resolving to statement import result. +pub type StatementImportFuture = oneshot::Receiver; + +mod rep { + use sc_peerset::ReputationChange as Rep; + /// Reputation change when a peer sends us any statement. + /// + /// This forces node to verify it, thus the negative value here. Once statement is verified, + /// reputation change should be refunded with `ANY_STATEMENT_REFUND` + pub const ANY_STATEMENT: Rep = Rep::new(-(1 << 4), "Any statement"); + /// Reputation change when a peer sends us any statement that is not invalid. + pub const ANY_STATEMENT_REFUND: Rep = Rep::new(1 << 4, "Any statement (refund)"); + /// Reputation change when a peer sends us an statement that we didn't know about. + pub const GOOD_STATEMENT: Rep = Rep::new(1 << 7, "Good statement"); + /// Reputation change when a peer sends us a bad statement. + pub const BAD_STATEMENT: Rep = Rep::new(-(1 << 12), "Bad statement"); + /// Reputation change when a peer sends us a duplicate statement. + pub const DUPLICATE_STATEMENT: Rep = Rep::new(-(1 << 7), "Duplicate statement"); + /// Reputation change when a peer sends us particularly useful statement + pub const EXCELLENT_STATEMENT: Rep = Rep::new(1 << 8, "High priority statement"); +} + +const LOG_TARGET: &str = "statement-gossip"; + +struct Metrics { + propagated_statements: Counter, +} + +impl Metrics { + fn register(r: &Registry) -> Result { + Ok(Self { + propagated_statements: register( + Counter::new( + "substrate_sync_propagated_statements", + "Number of statements propagated to at least one peer", + )?, + r, + )?, + }) + } +} + +/// Prototype for a [`StatementHandler`]. +pub struct StatementHandlerPrototype { + protocol_name: ProtocolName, +} + +impl StatementHandlerPrototype { + /// Create a new instance. + pub fn new>(genesis_hash: Hash, fork_id: Option<&str>) -> Self { + let genesis_hash = genesis_hash.as_ref(); + let protocol_name = if let Some(fork_id) = fork_id { + format!("/{}/{}/statement/1", array_bytes::bytes2hex("", genesis_hash), fork_id) + } else { + format!("/{}/statement/1", array_bytes::bytes2hex("", genesis_hash)) + }; + + Self { protocol_name: protocol_name.into() } + } + + /// Returns the configuration of the set to put in the network configuration. + pub fn set_config(&self) -> NonDefaultSetConfig { + NonDefaultSetConfig { + notifications_protocol: self.protocol_name.clone(), + fallback_names: Vec::new(), + max_notification_size: MAX_STATEMENT_SIZE, + handshake: None, + set_config: SetConfig { + in_peers: 0, + out_peers: 0, + reserved_nodes: Vec::new(), + non_reserved_mode: NonReservedPeerMode::Deny, + }, + } + } + + /// Turns the prototype into the actual handler. + /// + /// Important: the statements handler is initially disabled and doesn't gossip statements. + /// Gossiping is enabled when major syncing is done. + pub fn build< + N: NetworkPeers + NetworkEventStream + NetworkNotification, + S: SyncEventStream + sp_consensus::SyncOracle, + >( + self, + network: N, + sync: S, + statement_store: Arc, + metrics_registry: Option<&Registry>, + executor: impl Fn(Pin + Send>>) + Send, + ) -> error::Result> { + let net_event_stream = network.event_stream("statement-handler-net"); + let sync_event_stream = sync.event_stream("statement-handler-sync"); + let (queue_sender, mut queue_receiver) = async_channel::bounded(100_000); + + let store = statement_store.clone(); + executor( + async move { + loop { + let task: Option<(Statement, oneshot::Sender)> = + queue_receiver.next().await; + match task { + None => return, + Some((statement, completion)) => { + let result = store.submit(statement, StatementSource::Network); + if completion.send(result).is_err() { + log::debug!( + target: LOG_TARGET, + "Error sending validation completion" + ); + } + }, + } + } + } + .boxed(), + ); + + let handler = StatementHandler { + protocol_name: self.protocol_name, + propagate_timeout: (Box::pin(interval(PROPAGATE_TIMEOUT)) + as Pin + Send>>) + .fuse(), + pending_statements: FuturesUnordered::new(), + pending_statements_peers: HashMap::new(), + network, + sync, + net_event_stream: net_event_stream.fuse(), + sync_event_stream: sync_event_stream.fuse(), + peers: HashMap::new(), + statement_store, + queue_sender, + metrics: if let Some(r) = metrics_registry { + Some(Metrics::register(r)?) + } else { + None + }, + }; + + Ok(handler) + } +} + +/// Handler for statements. Call [`StatementHandler::run`] to start the processing. +pub struct StatementHandler< + N: NetworkPeers + NetworkEventStream + NetworkNotification, + S: SyncEventStream + sp_consensus::SyncOracle, +> { + protocol_name: ProtocolName, + /// Interval at which we call `propagate_statements`. + propagate_timeout: stream::Fuse + Send>>>, + /// Pending statements verification tasks. + pending_statements: + FuturesUnordered)> + Send>>>, + /// As multiple peers can send us the same statement, we group + /// these peers using the statement hash while the statement is + /// imported. This prevents that we import the same statement + /// multiple times concurrently. + pending_statements_peers: HashMap>, + /// Network service to use to send messages and manage peers. + network: N, + /// Syncing service. + sync: S, + /// Stream of networking events. + net_event_stream: stream::Fuse + Send>>>, + /// Receiver for syncing-related events. + sync_event_stream: stream::Fuse + Send>>>, + // All connected peers + peers: HashMap, + statement_store: Arc, + queue_sender: async_channel::Sender<(Statement, oneshot::Sender)>, + /// Prometheus metrics. + metrics: Option, +} + +/// Peer information +#[derive(Debug)] +struct Peer { + /// Holds a set of statements known to this peer. + known_statements: LruHashSet, + role: ObservedRole, +} + +impl StatementHandler +where + N: NetworkPeers + NetworkEventStream + NetworkNotification, + S: SyncEventStream + sp_consensus::SyncOracle, +{ + /// Turns the [`StatementHandler`] into a future that should run forever and not be + /// interrupted. + pub async fn run(mut self) { + loop { + futures::select! { + _ = self.propagate_timeout.next() => { + self.propagate_statements(); + }, + (hash, result) = self.pending_statements.select_next_some() => { + if let Some(peers) = self.pending_statements_peers.remove(&hash) { + if let Some(result) = result { + peers.into_iter().for_each(|p| self.on_handle_statement_import(p, &result)); + } + } else { + log::warn!(target: LOG_TARGET, "Inconsistent state, no peers for pending statement!"); + } + }, + network_event = self.net_event_stream.next() => { + if let Some(network_event) = network_event { + self.handle_network_event(network_event).await; + } else { + // Networking has seemingly closed. Closing as well. + return; + } + }, + sync_event = self.sync_event_stream.next() => { + if let Some(sync_event) = sync_event { + self.handle_sync_event(sync_event); + } else { + // Syncing has seemingly closed. Closing as well. + return; + } + } + } + } + } + + fn handle_sync_event(&mut self, event: SyncEvent) { + match event { + SyncEvent::PeerConnected(remote) => { + let addr = iter::once(multiaddr::Protocol::P2p(remote.into())) + .collect::(); + let result = self.network.add_peers_to_reserved_set( + self.protocol_name.clone(), + iter::once(addr).collect(), + ); + if let Err(err) = result { + log::error!(target: LOG_TARGET, "Add reserved peer failed: {}", err); + } + }, + SyncEvent::PeerDisconnected(remote) => { + self.network.remove_peers_from_reserved_set( + self.protocol_name.clone(), + iter::once(remote).collect(), + ); + }, + } + } + + async fn handle_network_event(&mut self, event: Event) { + match event { + Event::Dht(_) => {}, + Event::NotificationStreamOpened { remote, protocol, role, .. } + if protocol == self.protocol_name => + { + let _was_in = self.peers.insert( + remote, + Peer { + known_statements: LruHashSet::new( + NonZeroUsize::new(MAX_KNOWN_STATEMENTS).expect("Constant is nonzero"), + ), + role, + }, + ); + debug_assert!(_was_in.is_none()); + }, + Event::NotificationStreamClosed { remote, protocol } + if protocol == self.protocol_name => + { + let _peer = self.peers.remove(&remote); + debug_assert!(_peer.is_some()); + }, + + Event::NotificationsReceived { remote, messages } => { + for (protocol, message) in messages { + if protocol != self.protocol_name { + continue + } + // Accept statements only when node is not major syncing + if self.sync.is_major_syncing() { + log::trace!( + target: LOG_TARGET, + "{remote}: Ignoring statements while major syncing or offline" + ); + continue + } + if let Ok(statements) = ::decode(&mut message.as_ref()) { + self.on_statements(remote, statements); + } else { + log::debug!( + target: LOG_TARGET, + "Failed to decode statement list from {remote}" + ); + } + } + }, + + // Not our concern. + Event::NotificationStreamOpened { .. } | Event::NotificationStreamClosed { .. } => {}, + } + } + + /// Called when peer sends us new statements + fn on_statements(&mut self, who: PeerId, statements: Statements) { + log::trace!(target: LOG_TARGET, "Received {} statements from {}", statements.len(), who); + if let Some(ref mut peer) = self.peers.get_mut(&who) { + for s in statements { + if self.pending_statements.len() > MAX_PENDING_STATEMENTS { + log::debug!( + target: LOG_TARGET, + "Ignoring any further statements that exceed `MAX_PENDING_STATEMENTS`({}) limit", + MAX_PENDING_STATEMENTS, + ); + break + } + + let hash = s.hash(); + peer.known_statements.insert(hash); + + self.network.report_peer(who, rep::ANY_STATEMENT); + + match self.pending_statements_peers.entry(hash) { + Entry::Vacant(entry) => { + let (completion_sender, completion_receiver) = oneshot::channel(); + match self.queue_sender.try_send((s, completion_sender)) { + Ok(()) => { + self.pending_statements.push( + async move { + let res = completion_receiver.await; + (hash, res.ok()) + } + .boxed(), + ); + entry.insert(HashSet::from_iter([who])); + }, + Err(async_channel::TrySendError::Full(_)) => { + log::debug!( + target: LOG_TARGET, + "Dropped statement because validation channel is full", + ); + }, + Err(async_channel::TrySendError::Closed(_)) => { + log::trace!( + target: LOG_TARGET, + "Dropped statement because validation channel is closed", + ); + }, + } + }, + Entry::Occupied(mut entry) => { + if !entry.get_mut().insert(who) { + // Already received this from the same peer. + self.network.report_peer(who, rep::DUPLICATE_STATEMENT); + } + }, + } + } + } + } + + fn on_handle_statement_import(&mut self, who: PeerId, import: &SubmitResult) { + match import { + SubmitResult::New(NetworkPriority::High) => + self.network.report_peer(who, rep::EXCELLENT_STATEMENT), + SubmitResult::New(NetworkPriority::Low) => + self.network.report_peer(who, rep::GOOD_STATEMENT), + SubmitResult::Known => self.network.report_peer(who, rep::ANY_STATEMENT_REFUND), + SubmitResult::KnownExpired => {}, + SubmitResult::Ignored => {}, + SubmitResult::Bad(_) => self.network.report_peer(who, rep::BAD_STATEMENT), + SubmitResult::InternalError(_) => {}, + } + } + + /// Propagate one statement. + pub fn propagate_statement(&mut self, hash: &Hash) { + // Accept statements only when node is not major syncing + if self.sync.is_major_syncing() { + return + } + + log::debug!(target: LOG_TARGET, "Propagating statement [{:?}]", hash); + if let Ok(Some(statement)) = self.statement_store.statement(hash) { + self.do_propagate_statements(&[(*hash, statement)]); + } + } + + fn do_propagate_statements(&mut self, statements: &[(Hash, Statement)]) { + let mut propagated_statements = 0; + + for (who, peer) in self.peers.iter_mut() { + // never send statements to light nodes + if matches!(peer.role, ObservedRole::Light) { + continue + } + + let to_send = statements + .iter() + .filter_map(|(hash, stmt)| peer.known_statements.insert(*hash).then(|| stmt)) + .collect::>(); + + propagated_statements += to_send.len(); + + if !to_send.is_empty() { + log::trace!(target: LOG_TARGET, "Sending {} statements to {}", to_send.len(), who); + self.network + .write_notification(*who, self.protocol_name.clone(), to_send.encode()); + } + } + + if let Some(ref metrics) = self.metrics { + metrics.propagated_statements.inc_by(propagated_statements as _) + } + } + + /// Call when we must propagate ready statements to peers. + fn propagate_statements(&mut self) { + // Send out statements only when node is not major syncing + if self.sync.is_major_syncing() { + return + } + + log::debug!(target: LOG_TARGET, "Propagating statements"); + if let Ok(statements) = self.statement_store.statements() { + self.do_propagate_statements(&statements); + } + } +} diff --git a/client/rpc-api/src/lib.rs b/client/rpc-api/src/lib.rs index bc76d029a..830545843 100644 --- a/client/rpc-api/src/lib.rs +++ b/client/rpc-api/src/lib.rs @@ -32,4 +32,5 @@ pub mod child_state; pub mod dev; pub mod offchain; pub mod state; +pub mod statement; pub mod system; diff --git a/client/rpc-api/src/statement/error.rs b/client/rpc-api/src/statement/error.rs new file mode 100644 index 000000000..549b14711 --- /dev/null +++ b/client/rpc-api/src/statement/error.rs @@ -0,0 +1,55 @@ +// This file is part of Substrate. + +// Copyright (C) Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +//! Statement RPC errors. + +use jsonrpsee::{ + core::Error as JsonRpseeError, + types::error::{CallError, ErrorObject}, +}; + +/// Statement RPC Result type. +pub type Result = std::result::Result; + +/// Statement RPC errors. +#[derive(Debug, thiserror::Error)] +pub enum Error { + /// Statement store internal error. + #[error("Statement store error")] + StatementStore(String), + /// Call to an unsafe RPC was denied. + #[error(transparent)] + UnsafeRpcCalled(#[from] crate::policy::UnsafeRpcError), +} + +/// Base error code for all statement errors. +const BASE_ERROR: i32 = 6000; + +impl From for JsonRpseeError { + fn from(e: Error) -> Self { + match e { + Error::StatementStore(message) => CallError::Custom(ErrorObject::owned( + BASE_ERROR + 1, + format!("Statement store error: {message}"), + None::<()>, + )) + .into(), + Error::UnsafeRpcCalled(e) => e.into(), + } + } +} diff --git a/client/rpc-api/src/statement/mod.rs b/client/rpc-api/src/statement/mod.rs new file mode 100644 index 000000000..39ec52cbe --- /dev/null +++ b/client/rpc-api/src/statement/mod.rs @@ -0,0 +1,60 @@ +// This file is part of Substrate. + +// Copyright (C) Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +//! Substrate Statement Store RPC API. + +use jsonrpsee::{core::RpcResult, proc_macros::rpc}; +use sp_core::Bytes; + +pub mod error; + +/// Substrate statement RPC API +#[rpc(client, server)] +pub trait StatementApi { + /// Return all statements, SCALE-encoded. + #[method(name = "statement_dump")] + fn dump(&self) -> RpcResult>; + + /// Return the data of all known statements which include all topics and have no `DecryptionKey` + /// field. + #[method(name = "statement_broadcasts")] + fn broadcasts(&self, match_all_topics: Vec<[u8; 32]>) -> RpcResult>; + + /// Return the data of all known statements whose decryption key is identified as `dest` (this + /// will generally be the public key or a hash thereof for symmetric ciphers, or a hash of the + /// private key for symmetric ciphers). + #[method(name = "statement_posted")] + fn posted(&self, match_all_topics: Vec<[u8; 32]>, dest: [u8; 32]) -> RpcResult>; + + /// Return the decrypted data of all known statements whose decryption key is identified as + /// `dest`. The key must be available to the client. + #[method(name = "statement_postedClear")] + fn posted_clear( + &self, + match_all_topics: Vec<[u8; 32]>, + dest: [u8; 32], + ) -> RpcResult>; + + /// Submit a pre-encoded statement. + #[method(name = "statement_submit")] + fn submit(&self, encoded: Bytes) -> RpcResult<()>; + + /// Remove a statement from the store. + #[method(name = "statement_remove")] + fn remove(&self, statement_hash: [u8; 32]) -> RpcResult<()>; +} diff --git a/client/rpc/Cargo.toml b/client/rpc/Cargo.toml index a22f65787..5a6e3e108 100644 --- a/client/rpc/Cargo.toml +++ b/client/rpc/Cargo.toml @@ -35,6 +35,7 @@ sp-rpc = { version = "6.0.0", path = "../../primitives/rpc" } sp-runtime = { version = "7.0.0", path = "../../primitives/runtime" } sp-session = { version = "4.0.0-dev", path = "../../primitives/session" } sp-version = { version = "5.0.0", path = "../../primitives/version" } +sp-statement-store = { version = "4.0.0-dev", path = "../../primitives/statement-store" } tokio = "1.22.0" diff --git a/client/rpc/src/lib.rs b/client/rpc/src/lib.rs index 6230ef664..475fc77a9 100644 --- a/client/rpc/src/lib.rs +++ b/client/rpc/src/lib.rs @@ -36,6 +36,7 @@ pub mod chain; pub mod dev; pub mod offchain; pub mod state; +pub mod statement; pub mod system; #[cfg(any(test, feature = "test-helpers"))] diff --git a/client/rpc/src/statement/mod.rs b/client/rpc/src/statement/mod.rs new file mode 100644 index 000000000..b4f432bbb --- /dev/null +++ b/client/rpc/src/statement/mod.rs @@ -0,0 +1,108 @@ +// This file is part of Substrate. + +// Copyright (C) Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +//! Substrate statement store API. + +use codec::{Decode, Encode}; +use jsonrpsee::core::{async_trait, RpcResult}; +/// Re-export the API for backward compatibility. +pub use sc_rpc_api::statement::{error::Error, StatementApiServer}; +use sc_rpc_api::DenyUnsafe; +use sp_core::Bytes; +use sp_statement_store::{StatementSource, SubmitResult}; +use std::sync::Arc; + +/// Statement store API +pub struct StatementStore { + store: Arc, + deny_unsafe: DenyUnsafe, +} + +impl StatementStore { + /// Create new instance of Offchain API. + pub fn new( + store: Arc, + deny_unsafe: DenyUnsafe, + ) -> Self { + StatementStore { store, deny_unsafe } + } +} + +#[async_trait] +impl StatementApiServer for StatementStore { + fn dump(&self) -> RpcResult> { + self.deny_unsafe.check_if_safe()?; + + let statements = + self.store.statements().map_err(|e| Error::StatementStore(e.to_string()))?; + Ok(statements.into_iter().map(|(_, s)| s.encode().into()).collect()) + } + + fn broadcasts(&self, match_all_topics: Vec<[u8; 32]>) -> RpcResult> { + Ok(self + .store + .broadcasts(&match_all_topics) + .map_err(|e| Error::StatementStore(e.to_string()))? + .into_iter() + .map(Into::into) + .collect()) + } + + fn posted(&self, match_all_topics: Vec<[u8; 32]>, dest: [u8; 32]) -> RpcResult> { + Ok(self + .store + .posted(&match_all_topics, dest) + .map_err(|e| Error::StatementStore(e.to_string()))? + .into_iter() + .map(Into::into) + .collect()) + } + + fn posted_clear( + &self, + match_all_topics: Vec<[u8; 32]>, + dest: [u8; 32], + ) -> RpcResult> { + Ok(self + .store + .posted_clear(&match_all_topics, dest) + .map_err(|e| Error::StatementStore(e.to_string()))? + .into_iter() + .map(Into::into) + .collect()) + } + + fn submit(&self, encoded: Bytes) -> RpcResult<()> { + let statement = Decode::decode(&mut &*encoded) + .map_err(|e| Error::StatementStore(format!("Eror decoding statement: {:?}", e)))?; + match self.store.submit(statement, StatementSource::Local) { + SubmitResult::New(_) | SubmitResult::Known => Ok(()), + // `KnownExpired` should not happen. Expired statements submitted with + // `StatementSource::Rpc` should be renewed. + SubmitResult::KnownExpired => + Err(Error::StatementStore("Submitted an expired statement.".into()).into()), + SubmitResult::Bad(e) => Err(Error::StatementStore(e.into()).into()), + SubmitResult::Ignored => Err(Error::StatementStore("Store is full.".into()).into()), + SubmitResult::InternalError(e) => Err(Error::StatementStore(e.to_string()).into()), + } + } + + fn remove(&self, hash: [u8; 32]) -> RpcResult<()> { + Ok(self.store.remove(&hash).map_err(|e| Error::StatementStore(e.to_string()))?) + } +} diff --git a/client/service/src/builder.rs b/client/service/src/builder.rs index d338ceb78..cb8986a5d 100644 --- a/client/service/src/builder.rs +++ b/client/service/src/builder.rs @@ -938,8 +938,8 @@ where Arc::new(TransactionPoolAdapter { pool: transaction_pool, client: client.clone() }), config.prometheus_config.as_ref().map(|config| &config.registry), )?; - spawn_handle.spawn("network-transactions-handler", Some("networking"), tx_handler.run()); + spawn_handle.spawn_blocking( "chain-sync-network-service-provider", Some("networking"), diff --git a/client/service/src/config.rs b/client/service/src/config.rs index 40f8c6a4f..0ff2c96d8 100644 --- a/client/service/src/config.rs +++ b/client/service/src/config.rs @@ -130,8 +130,10 @@ pub struct Configuration { pub max_runtime_instances: usize, /// Announce block automatically after they have been imported pub announce_block: bool, - /// Base path of the configuration - pub base_path: Option, + /// Data path root for the configured chain. + pub data_path: PathBuf, + /// Base path of the configuration. This is shared between chains. + pub base_path: BasePath, /// Configuration of the output format that the informant uses. pub informant_output_format: sc_informant::OutputFormat, /// Maximum number of different runtime versions that can be cached. diff --git a/client/service/test/src/lib.rs b/client/service/test/src/lib.rs index 0eb4489ad..db8432e44 100644 --- a/client/service/test/src/lib.rs +++ b/client/service/test/src/lib.rs @@ -265,7 +265,8 @@ fn node_config< tracing_receiver: Default::default(), max_runtime_instances: 8, announce_block: true, - base_path: Some(BasePath::new(root)), + base_path: BasePath::new(root.clone()), + data_path: root, informant_output_format: Default::default(), runtime_cache_size: 2, } diff --git a/client/statement-store/Cargo.toml b/client/statement-store/Cargo.toml new file mode 100644 index 000000000..d9c9f238d --- /dev/null +++ b/client/statement-store/Cargo.toml @@ -0,0 +1,36 @@ +[package] +name = "sc-statement-store" +version = "4.0.0-dev" +authors = ["Parity Technologies "] +edition = "2021" +license = "GPL-3.0-or-later WITH Classpath-exception-2.0" +homepage = "https://substrate.io" +repository = "https://github.com/paritytech/substrate/" +description = "Substrate statement store." +readme = "README.md" + +[package.metadata.docs.rs] +targets = ["x86_64-unknown-linux-gnu"] + +[dependencies] +async-trait = "0.1.57" +codec = { package = "parity-scale-codec", version = "3.2.2" } +futures = "0.3.21" +futures-timer = "3.0.2" +log = "0.4.17" +parking_lot = "0.12.1" +parity-db = "0.4.6" +tokio = { version = "1.22.0", features = ["time"] } +sp-statement-store = { version = "4.0.0-dev", path = "../../primitives/statement-store" } +prometheus-endpoint = { package = "substrate-prometheus-endpoint", version = "0.10.0-dev", path = "../../utils/prometheus" } +sp-api = { version = "4.0.0-dev", path = "../../primitives/api" } +sp-blockchain = { version = "4.0.0-dev", path = "../../primitives/blockchain" } +sp-core = { version = "7.0.0", path = "../../primitives/core" } +sp-runtime = { version = "7.0.0", path = "../../primitives/runtime" } +sp-tracing = { version = "6.0.0", path = "../../primitives/tracing" } +sc-client-api = { version = "4.0.0-dev", path = "../api" } + +[dev-dependencies] +tempfile = "3.1.0" +env_logger = "0.9" + diff --git a/client/statement-store/README.md b/client/statement-store/README.md new file mode 100644 index 000000000..41e268f4e --- /dev/null +++ b/client/statement-store/README.md @@ -0,0 +1,4 @@ +Substrate statement store implementation. + +License: GPL-3.0-or-later WITH Classpath-exception-2.0 + diff --git a/client/statement-store/src/lib.rs b/client/statement-store/src/lib.rs new file mode 100644 index 000000000..2e2bb3bd3 --- /dev/null +++ b/client/statement-store/src/lib.rs @@ -0,0 +1,1226 @@ +// This file is part of Substrate. + +// Copyright (C) Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +//! Disk-backed statement store. +//! +//! This module contains an implementation of `sp_statement_store::StatementStore` which is backed +//! by a database. +//! +//! Constraint management. +//! +//! Each time a new statement is inserted into the store, it is first validated with the runtime +//! Validation function computes `global_priority`, 'max_count' and `max_size` for a statement. +//! The following constraints are then checked: +//! * For a given account id, there may be at most `max_count` statements with `max_size` total data +//! size. To satisfy this, statements for this account ID are removed from the store starting with +//! the lowest priority until a constraint is satisfied. +//! * There may not be more than `MAX_TOTAL_STATEMENTS` total statements with `MAX_TOTAL_SIZE` size. +//! To satisfy this, statements are removed from the store starting with the lowest +//! `global_priority` until a constraint is satisfied. +//! +//! When a new statement is inserted that would not satisfy constraints in the first place, no +//! statements are deleted and `Ignored` result is returned. +//! The order in which statements with the same priority are deleted is unspecified. +//! +//! Statement expiration. +//! +//! Each time a statement is removed from the store (Either evicted by higher priority statement or +//! explicitly with the `remove` function) the statement is marked as expired. Expired statements +//! can't be added to the store for `Options::purge_after_sec` seconds. This is to prevent old +//! statements from being propagated on the network. + +#![warn(missing_docs)] +#![warn(unused_extern_crates)] + +mod metrics; + +pub use sp_statement_store::{Error, StatementStore, MAX_TOPICS}; + +use metrics::MetricsLink as PrometheusMetrics; +use parking_lot::RwLock; +use prometheus_endpoint::Registry as PrometheusRegistry; +use sp_api::ProvideRuntimeApi; +use sp_blockchain::HeaderBackend; +use sp_core::{hexdisplay::HexDisplay, traits::SpawnNamed, Decode, Encode}; +use sp_runtime::traits::Block as BlockT; +use sp_statement_store::{ + runtime_api::{InvalidStatement, StatementSource, ValidStatement, ValidateStatement}, + AccountId, BlockHash, Channel, DecryptionKey, Hash, NetworkPriority, Proof, Result, Statement, + SubmitResult, Topic, +}; +use std::{ + collections::{BTreeMap, HashMap, HashSet}, + sync::Arc, +}; + +const KEY_VERSION: &[u8] = b"version".as_slice(); +const CURRENT_VERSION: u32 = 1; + +const LOG_TARGET: &str = "statement-store"; + +const DEFAULT_PURGE_AFTER_SEC: u64 = 2 * 24 * 60 * 60; //48h +const DEFAULT_MAX_TOTAL_STATEMENTS: usize = 8192; +const DEFAULT_MAX_TOTAL_SIZE: usize = 64 * 1024 * 1024; + +const MAINTENANCE_PERIOD: std::time::Duration = std::time::Duration::from_secs(30); + +mod col { + pub const META: u8 = 0; + pub const STATEMENTS: u8 = 1; + pub const EXPIRED: u8 = 2; + + pub const COUNT: u8 = 3; +} + +#[derive(Eq, PartialEq, Debug, Ord, PartialOrd, Clone, Copy)] +struct Priority(u32); + +#[derive(PartialEq, Eq)] +struct PriorityKey { + hash: Hash, + priority: Priority, +} + +impl PartialOrd for PriorityKey { + fn partial_cmp(&self, other: &Self) -> Option { + Some(self.cmp(other)) + } +} + +impl Ord for PriorityKey { + fn cmp(&self, other: &Self) -> std::cmp::Ordering { + self.priority.cmp(&other.priority).then_with(|| self.hash.cmp(&other.hash)) + } +} + +#[derive(PartialEq, Eq)] +struct ChannelEntry { + hash: Hash, + priority: Priority, +} + +#[derive(Default)] +struct StatementsForAccount { + // Statements ordered by priority. + by_priority: BTreeMap, usize)>, + // Channel to statement map. Only one statement per channel is allowed. + channels: HashMap, + // Sum of all `Data` field sizes. + data_size: usize, +} + +/// Store configuration +pub struct Options { + /// Maximum statement allowed in the store. Once this limit is reached lower-priority + /// statements may be evicted. + max_total_statements: usize, + /// Maximum total data size allowed in the store. Once this limit is reached lower-priority + /// statements may be evicted. + max_total_size: usize, + /// Number of seconds for which removed statements won't be allowed to be added back in. + purge_after_sec: u64, +} + +impl Default for Options { + fn default() -> Self { + Options { + max_total_statements: DEFAULT_MAX_TOTAL_STATEMENTS, + max_total_size: DEFAULT_MAX_TOTAL_SIZE, + purge_after_sec: DEFAULT_PURGE_AFTER_SEC, + } + } +} + +#[derive(Default)] +struct Index { + by_topic: HashMap>, + by_dec_key: HashMap, HashSet>, + topics_and_keys: HashMap; MAX_TOPICS], Option)>, + entries: HashMap, + expired: HashMap, // Value is expiration timestamp. + accounts: HashMap, + options: Options, + total_size: usize, +} + +struct ClientWrapper { + client: Arc, + _block: std::marker::PhantomData, +} + +impl ClientWrapper +where + Block: BlockT, + Block::Hash: From, + Client: ProvideRuntimeApi + HeaderBackend + Send + Sync + 'static, + Client::Api: ValidateStatement, +{ + fn validate_statement( + &self, + block: Option, + source: StatementSource, + statement: Statement, + ) -> std::result::Result { + let api = self.client.runtime_api(); + let block = block.map(Into::into).unwrap_or_else(|| { + // Validate against the finalized state. + self.client.info().finalized_hash + }); + api.validate_statement(block, source, statement) + .map_err(|_| InvalidStatement::InternalError)? + } +} + +/// Statement store. +pub struct Store { + db: parity_db::Db, + index: RwLock, + validate_fn: Box< + dyn Fn( + Option, + StatementSource, + Statement, + ) -> std::result::Result + + Send + + Sync, + >, + // Used for testing + time_override: Option, + metrics: PrometheusMetrics, +} + +enum IndexQuery { + Unknown, + Exists, + Expired, +} + +enum MaybeInserted { + Inserted(HashSet), + Ignored, +} + +impl Index { + fn new(options: Options) -> Index { + Index { options, ..Default::default() } + } + + fn insert_new(&mut self, hash: Hash, account: AccountId, statement: &Statement) { + let mut all_topics = [None; MAX_TOPICS]; + let mut nt = 0; + while let Some(t) = statement.topic(nt) { + self.by_topic.entry(t).or_default().insert(hash); + all_topics[nt] = Some(t); + nt += 1; + } + let key = statement.decryption_key(); + self.by_dec_key.entry(key).or_default().insert(hash); + if nt > 0 || key.is_some() { + self.topics_and_keys.insert(hash, (all_topics, key)); + } + let priority = Priority(statement.priority().unwrap_or(0)); + self.entries.insert(hash, (account, priority, statement.data_len())); + self.total_size += statement.data_len(); + let mut account_info = self.accounts.entry(account).or_default(); + account_info.data_size += statement.data_len(); + if let Some(channel) = statement.channel() { + account_info.channels.insert(channel, ChannelEntry { hash, priority }); + } + account_info + .by_priority + .insert(PriorityKey { hash, priority }, (statement.channel(), statement.data_len())); + } + + fn query(&self, hash: &Hash) -> IndexQuery { + if self.entries.contains_key(hash) { + return IndexQuery::Exists + } + if self.expired.contains_key(hash) { + return IndexQuery::Expired + } + IndexQuery::Unknown + } + + fn insert_expired(&mut self, hash: Hash, timestamp: u64) { + self.expired.insert(hash, timestamp); + } + + fn iterate_with( + &self, + key: Option, + match_all_topics: &[Topic], + mut f: impl FnMut(&Hash) -> Result<()>, + ) -> Result<()> { + let empty = HashSet::new(); + let mut sets: [&HashSet; MAX_TOPICS + 1] = [∅ MAX_TOPICS + 1]; + if match_all_topics.len() > MAX_TOPICS { + return Ok(()) + } + let key_set = self.by_dec_key.get(&key); + if key_set.map_or(0, |s| s.len()) == 0 { + // Key does not exist in the index. + return Ok(()) + } + sets[0] = key_set.expect("Function returns if key_set is None"); + for (i, t) in match_all_topics.iter().enumerate() { + let set = self.by_topic.get(t); + if set.map_or(0, |s| s.len()) == 0 { + // At least one of the match_all_topics does not exist in the index. + return Ok(()) + } + sets[i + 1] = set.expect("Function returns if set is None"); + } + let sets = &mut sets[0..match_all_topics.len() + 1]; + // Start with the smallest topic set or the key set. + sets.sort_by_key(|s| s.len()); + for item in sets[0] { + if sets[1..].iter().all(|set| set.contains(item)) { + log::trace!( + target: LOG_TARGET, + "Iterating by topic/key: statement {:?}", + HexDisplay::from(item) + ); + f(item)? + } + } + Ok(()) + } + + fn maintain(&mut self, current_time: u64) -> Vec { + // Purge previously expired messages. + let mut purged = Vec::new(); + self.expired.retain(|hash, timestamp| { + if *timestamp + self.options.purge_after_sec <= current_time { + purged.push(*hash); + log::trace!(target: LOG_TARGET, "Purged statement {:?}", HexDisplay::from(hash)); + false + } else { + true + } + }); + purged + } + + fn make_expired(&mut self, hash: &Hash, current_time: u64) -> bool { + if let Some((account, priority, len)) = self.entries.remove(hash) { + self.total_size -= len; + if let Some((topics, key)) = self.topics_and_keys.remove(hash) { + for t in topics.into_iter().flatten() { + if let std::collections::hash_map::Entry::Occupied(mut set) = + self.by_topic.entry(t) + { + set.get_mut().remove(hash); + if set.get().is_empty() { + set.remove_entry(); + } + } + } + if let std::collections::hash_map::Entry::Occupied(mut set) = + self.by_dec_key.entry(key) + { + set.get_mut().remove(hash); + if set.get().is_empty() { + set.remove_entry(); + } + } + } + self.expired.insert(*hash, current_time); + if let std::collections::hash_map::Entry::Occupied(mut account_rec) = + self.accounts.entry(account) + { + let key = PriorityKey { hash: *hash, priority }; + if let Some((channel, len)) = account_rec.get_mut().by_priority.remove(&key) { + account_rec.get_mut().data_size -= len; + if let Some(channel) = channel { + account_rec.get_mut().channels.remove(&channel); + } + } + if account_rec.get().by_priority.is_empty() { + account_rec.remove_entry(); + } + } + log::trace!(target: LOG_TARGET, "Expired statement {:?}", HexDisplay::from(hash)); + true + } else { + false + } + } + + fn insert( + &mut self, + hash: Hash, + statement: &Statement, + account: &AccountId, + validation: &ValidStatement, + current_time: u64, + ) -> MaybeInserted { + let statement_len = statement.data_len(); + if statement_len > validation.max_size as usize { + log::debug!( + target: LOG_TARGET, + "Ignored oversize message: {:?} ({} bytes)", + HexDisplay::from(&hash), + statement_len, + ); + return MaybeInserted::Ignored + } + + let mut evicted = HashSet::new(); + let mut would_free_size = 0; + let priority = Priority(statement.priority().unwrap_or(0)); + let (max_size, max_count) = (validation.max_size as usize, validation.max_count as usize); + // It may happen that we can't delete enough lower priority messages + // to satisfy size constraints. We check for that before deleting anything, + // taking into account channel message replacement. + if let Some(account_rec) = self.accounts.get(account) { + if let Some(channel) = statement.channel() { + if let Some(channel_record) = account_rec.channels.get(&channel) { + if priority <= channel_record.priority { + // Trying to replace channel message with lower priority + log::debug!( + target: LOG_TARGET, + "Ignored lower priority channel message: {:?} {:?} <= {:?}", + HexDisplay::from(&hash), + priority, + channel_record.priority, + ); + return MaybeInserted::Ignored + } else { + // Would replace channel message. Still need to check for size constraints + // below. + log::debug!( + target: LOG_TARGET, + "Replacing higher priority channel message: {:?} ({:?}) > {:?} ({:?})", + HexDisplay::from(&hash), + priority, + HexDisplay::from(&channel_record.hash), + channel_record.priority, + ); + let key = PriorityKey { + hash: channel_record.hash, + priority: channel_record.priority, + }; + if let Some((_channel, len)) = account_rec.by_priority.get(&key) { + would_free_size += *len; + evicted.insert(channel_record.hash); + } + } + } + } + // Check if we can evict enough lower priority statements to satisfy constraints + for (entry, (_, len)) in account_rec.by_priority.iter() { + if (account_rec.data_size - would_free_size + statement_len <= max_size) && + account_rec.by_priority.len() + 1 - evicted.len() <= max_count + { + // Satisfied + break + } + if evicted.contains(&entry.hash) { + // Already accounted for above + continue + } + if entry.priority >= priority { + log::debug!( + target: LOG_TARGET, + "Ignored message due to constraints {:?} {:?} < {:?}", + HexDisplay::from(&hash), + priority, + entry.priority, + ); + return MaybeInserted::Ignored + } + evicted.insert(entry.hash); + would_free_size += len; + } + } + // Now check global constraints as well. + if !((self.total_size - would_free_size + statement_len <= self.options.max_total_size) && + self.entries.len() + 1 - evicted.len() <= self.options.max_total_statements) + { + log::debug!( + target: LOG_TARGET, + "Ignored statement {} because the store is full (size={}, count={})", + HexDisplay::from(&hash), + self.total_size, + self.entries.len(), + ); + return MaybeInserted::Ignored + } + + for h in &evicted { + self.make_expired(h, current_time); + } + self.insert_new(hash, *account, statement); + MaybeInserted::Inserted(evicted) + } +} + +impl Store { + /// Create a new shared store instance. There should only be one per process. + /// `path` will be used to open a statement database or create a new one if it does not exist. + pub fn new_shared( + path: &std::path::Path, + options: Options, + client: Arc, + prometheus: Option<&PrometheusRegistry>, + task_spawner: &dyn SpawnNamed, + ) -> Result> + where + Block: BlockT, + Block::Hash: From, + Client: ProvideRuntimeApi + + HeaderBackend + + sc_client_api::ExecutorProvider + + Send + + Sync + + 'static, + Client::Api: ValidateStatement, + { + let store = Arc::new(Self::new(path, options, client.clone(), prometheus)?); + client.execution_extensions().register_statement_store(store.clone()); + + // Perform periodic statement store maintenance + let worker_store = store.clone(); + task_spawner.spawn( + "statement-store-maintenance", + Some("statement-store"), + Box::pin(async move { + let mut interval = tokio::time::interval(MAINTENANCE_PERIOD); + loop { + interval.tick().await; + worker_store.maintain(); + } + }), + ); + + Ok(store) + } + + /// Create a new instance. + /// `path` will be used to open a statement database or create a new one if it does not exist. + fn new( + path: &std::path::Path, + options: Options, + client: Arc, + prometheus: Option<&PrometheusRegistry>, + ) -> Result + where + Block: BlockT, + Block::Hash: From, + Client: ProvideRuntimeApi + HeaderBackend + Send + Sync + 'static, + Client::Api: ValidateStatement, + { + let mut path: std::path::PathBuf = path.into(); + path.push("statements"); + + let mut config = parity_db::Options::with_columns(&path, col::COUNT); + + let mut statement_col = &mut config.columns[col::STATEMENTS as usize]; + statement_col.ref_counted = false; + statement_col.preimage = true; + statement_col.uniform = true; + let db = parity_db::Db::open_or_create(&config).map_err(|e| Error::Db(e.to_string()))?; + match db.get(col::META, &KEY_VERSION).map_err(|e| Error::Db(e.to_string()))? { + Some(version) => { + let version = u32::from_le_bytes( + version + .try_into() + .map_err(|_| Error::Db("Error reading database version".into()))?, + ); + if version != CURRENT_VERSION { + return Err(Error::Db(format!("Unsupported database version: {version}"))) + } + }, + None => { + db.commit([( + col::META, + KEY_VERSION.to_vec(), + Some(CURRENT_VERSION.to_le_bytes().to_vec()), + )]) + .map_err(|e| Error::Db(e.to_string()))?; + }, + } + + let validator = ClientWrapper { client, _block: Default::default() }; + let validate_fn = Box::new(move |block, source, statement| { + validator.validate_statement(block, source, statement) + }); + + let store = Store { + db, + index: RwLock::new(Index::new(options)), + validate_fn, + time_override: None, + metrics: PrometheusMetrics::new(prometheus), + }; + store.populate()?; + Ok(store) + } + + /// Create memory index from the data. + // This may be moved to a background thread if it slows startup too much. + // This function should only be used on startup. There should be no other DB operations when + // iterating the index. + fn populate(&self) -> Result<()> { + { + let mut index = self.index.write(); + self.db + .iter_column_while(col::STATEMENTS, |item| { + let statement = item.value; + if let Ok(statement) = Statement::decode(&mut statement.as_slice()) { + let hash = statement.hash(); + log::trace!( + target: LOG_TARGET, + "Statement loaded {:?}", + HexDisplay::from(&hash) + ); + if let Some(account_id) = statement.account_id() { + index.insert_new(hash, account_id, &statement); + } else { + log::debug!( + target: LOG_TARGET, + "Error decoding statement loaded from the DB: {:?}", + HexDisplay::from(&hash) + ); + } + } + true + }) + .map_err(|e| Error::Db(e.to_string()))?; + self.db + .iter_column_while(col::EXPIRED, |item| { + let expired_info = item.value; + if let Ok((hash, timestamp)) = + <(Hash, u64)>::decode(&mut expired_info.as_slice()) + { + log::trace!( + target: LOG_TARGET, + "Statement loaded (expired): {:?}", + HexDisplay::from(&hash) + ); + index.insert_expired(hash, timestamp); + } + true + }) + .map_err(|e| Error::Db(e.to_string()))?; + } + + self.maintain(); + Ok(()) + } + + fn collect_statements( + &self, + key: Option, + match_all_topics: &[Topic], + mut f: impl FnMut(Statement) -> Option, + ) -> Result> { + let mut result = Vec::new(); + let index = self.index.read(); + index.iterate_with(key, match_all_topics, |hash| { + match self.db.get(col::STATEMENTS, hash).map_err(|e| Error::Db(e.to_string()))? { + Some(entry) => { + if let Ok(statement) = Statement::decode(&mut entry.as_slice()) { + if let Some(data) = f(statement) { + result.push(data); + } + } else { + // DB inconsistency + log::warn!( + target: LOG_TARGET, + "Corrupt statement {:?}", + HexDisplay::from(hash) + ); + } + }, + None => { + // DB inconsistency + log::warn!( + target: LOG_TARGET, + "Missing statement {:?}", + HexDisplay::from(hash) + ); + }, + } + Ok(()) + })?; + Ok(result) + } + + /// Perform periodic store maintenance + pub fn maintain(&self) { + log::trace!(target: LOG_TARGET, "Started store maintenance"); + let deleted = self.index.write().maintain(self.timestamp()); + let deleted: Vec<_> = + deleted.into_iter().map(|hash| (col::EXPIRED, hash.to_vec(), None)).collect(); + let count = deleted.len() as u64; + if let Err(e) = self.db.commit(deleted) { + log::warn!(target: LOG_TARGET, "Error writing to the statement database: {:?}", e); + } else { + self.metrics.report(|metrics| metrics.statements_pruned.inc_by(count)); + } + log::trace!( + target: LOG_TARGET, + "Completed store maintenance. Purged: {}, Active: {}, Expired: {}", + count, + self.index.read().entries.len(), + self.index.read().expired.len() + ); + } + + fn timestamp(&self) -> u64 { + self.time_override.unwrap_or_else(|| { + std::time::SystemTime::now() + .duration_since(std::time::UNIX_EPOCH) + .unwrap_or_default() + .as_secs() + }) + } + + #[cfg(test)] + fn set_time(&mut self, time: u64) { + self.time_override = Some(time); + } +} + +impl StatementStore for Store { + /// Return all statements. + fn statements(&self) -> Result> { + let index = self.index.read(); + let mut result = Vec::with_capacity(index.entries.len()); + for h in self.index.read().entries.keys() { + let encoded = self.db.get(col::STATEMENTS, h).map_err(|e| Error::Db(e.to_string()))?; + if let Some(encoded) = encoded { + if let Ok(statement) = Statement::decode(&mut encoded.as_slice()) { + let hash = statement.hash(); + result.push((hash, statement)); + } + } + } + Ok(result) + } + + /// Returns a statement by hash. + fn statement(&self, hash: &Hash) -> Result> { + Ok( + match self + .db + .get(col::STATEMENTS, hash.as_slice()) + .map_err(|e| Error::Db(e.to_string()))? + { + Some(entry) => { + log::trace!( + target: LOG_TARGET, + "Queried statement {:?}", + HexDisplay::from(hash) + ); + Some( + Statement::decode(&mut entry.as_slice()) + .map_err(|e| Error::Decode(e.to_string()))?, + ) + }, + None => { + log::trace!( + target: LOG_TARGET, + "Queried missing statement {:?}", + HexDisplay::from(hash) + ); + None + }, + }, + ) + } + + /// Return the data of all known statements which include all topics and have no `DecryptionKey` + /// field. + fn broadcasts(&self, match_all_topics: &[Topic]) -> Result>> { + self.collect_statements(None, match_all_topics, |statement| statement.into_data()) + } + + /// Return the data of all known statements whose decryption key is identified as `dest` (this + /// will generally be the public key or a hash thereof for symmetric ciphers, or a hash of the + /// private key for symmetric ciphers). + fn posted(&self, match_all_topics: &[Topic], dest: [u8; 32]) -> Result>> { + self.collect_statements(Some(dest), match_all_topics, |statement| statement.into_data()) + } + + /// Return the decrypted data of all known statements whose decryption key is identified as + /// `dest`. The key must be available to the client. + fn posted_clear(&self, match_all_topics: &[Topic], dest: [u8; 32]) -> Result>> { + self.collect_statements(Some(dest), match_all_topics, |statement| statement.into_data()) + } + + /// Submit a statement to the store. Validates the statement and returns validation result. + fn submit(&self, statement: Statement, source: StatementSource) -> SubmitResult { + let hash = statement.hash(); + match self.index.read().query(&hash) { + IndexQuery::Expired => + if !source.can_be_resubmitted() { + return SubmitResult::KnownExpired + }, + IndexQuery::Exists => + if !source.can_be_resubmitted() { + return SubmitResult::Known + }, + IndexQuery::Unknown => {}, + } + + let Some(account_id) = statement.account_id() else { + log::debug!( + target: LOG_TARGET, + "Statement validation failed: Missing proof ({:?})", + HexDisplay::from(&hash), + ); + self.metrics.report(|metrics| metrics.validations_invalid.inc()); + return SubmitResult::Bad("No statement proof") + }; + + // Validate. + let at_block = if let Some(Proof::OnChain { block_hash, .. }) = statement.proof() { + Some(*block_hash) + } else { + None + }; + let validation_result = (self.validate_fn)(at_block, source, statement.clone()); + let validation = match validation_result { + Ok(validation) => validation, + Err(InvalidStatement::BadProof) => { + log::debug!( + target: LOG_TARGET, + "Statement validation failed: BadProof, {:?}", + HexDisplay::from(&hash), + ); + self.metrics.report(|metrics| metrics.validations_invalid.inc()); + return SubmitResult::Bad("Bad statement proof") + }, + Err(InvalidStatement::NoProof) => { + log::debug!( + target: LOG_TARGET, + "Statement validation failed: NoProof, {:?}", + HexDisplay::from(&hash), + ); + self.metrics.report(|metrics| metrics.validations_invalid.inc()); + return SubmitResult::Bad("Missing statement proof") + }, + Err(InvalidStatement::InternalError) => + return SubmitResult::InternalError(Error::Runtime), + }; + + let current_time = self.timestamp(); + let mut commit = Vec::new(); + { + let mut index = self.index.write(); + + let evicted = + match index.insert(hash, &statement, &account_id, &validation, current_time) { + MaybeInserted::Ignored => return SubmitResult::Ignored, + MaybeInserted::Inserted(evicted) => evicted, + }; + + commit.push((col::STATEMENTS, hash.to_vec(), Some(statement.encode()))); + for hash in evicted { + commit.push((col::STATEMENTS, hash.to_vec(), None)); + commit.push((col::EXPIRED, hash.to_vec(), Some((hash, current_time).encode()))); + } + if let Err(e) = self.db.commit(commit) { + log::debug!( + target: LOG_TARGET, + "Statement validation failed: database error {}, {:?}", + e, + statement + ); + return SubmitResult::InternalError(Error::Db(e.to_string())) + } + } // Release index lock + self.metrics.report(|metrics| metrics.submitted_statements.inc()); + let network_priority = NetworkPriority::High; + log::trace!(target: LOG_TARGET, "Statement submitted: {:?}", HexDisplay::from(&hash)); + SubmitResult::New(network_priority) + } + + /// Remove a statement by hash. + fn remove(&self, hash: &Hash) -> Result<()> { + let current_time = self.timestamp(); + { + let mut index = self.index.write(); + if index.make_expired(hash, current_time) { + let commit = [ + (col::STATEMENTS, hash.to_vec(), None), + (col::EXPIRED, hash.to_vec(), Some((hash, current_time).encode())), + ]; + if let Err(e) = self.db.commit(commit) { + log::debug!( + target: LOG_TARGET, + "Error removing statement: database error {}, {:?}", + e, + HexDisplay::from(hash), + ); + return Err(Error::Db(e.to_string())) + } + } + } + Ok(()) + } +} + +#[cfg(test)] +mod tests { + use crate::Store; + use sp_core::Pair; + use sp_statement_store::{ + runtime_api::{InvalidStatement, ValidStatement, ValidateStatement}, + AccountId, Channel, DecryptionKey, NetworkPriority, Proof, SignatureVerificationResult, + Statement, StatementSource, StatementStore, SubmitResult, Topic, + }; + + type Extrinsic = sp_runtime::OpaqueExtrinsic; + type Hash = sp_core::H256; + type Hashing = sp_runtime::traits::BlakeTwo256; + type BlockNumber = u64; + type Header = sp_runtime::generic::Header; + type Block = sp_runtime::generic::Block; + + const CORRECT_BLOCK_HASH: [u8; 32] = [1u8; 32]; + + #[derive(Clone)] + pub(crate) struct TestClient; + + pub(crate) struct RuntimeApi { + _inner: TestClient, + } + + impl sp_api::ProvideRuntimeApi for TestClient { + type Api = RuntimeApi; + fn runtime_api(&self) -> sp_api::ApiRef { + RuntimeApi { _inner: self.clone() }.into() + } + } + sp_api::mock_impl_runtime_apis! { + impl ValidateStatement for RuntimeApi { + fn validate_statement( + _source: StatementSource, + statement: Statement, + ) -> std::result::Result { + use crate::tests::account; + match statement.verify_signature() { + SignatureVerificationResult::Valid(_) => Ok(ValidStatement{max_count: 100, max_size: 1000}), + SignatureVerificationResult::Invalid => Err(InvalidStatement::BadProof), + SignatureVerificationResult::NoSignature => { + if let Some(Proof::OnChain { block_hash, .. }) = statement.proof() { + if block_hash == &CORRECT_BLOCK_HASH { + let (max_count, max_size) = match statement.account_id() { + Some(a) if a == account(1) => (1, 1000), + Some(a) if a == account(2) => (2, 1000), + Some(a) if a == account(3) => (3, 1000), + Some(a) if a == account(4) => (4, 1000), + _ => (2, 2000), + }; + Ok(ValidStatement{ max_count, max_size }) + } else { + Err(InvalidStatement::BadProof) + } + } else { + Err(InvalidStatement::BadProof) + } + } + } + } + } + } + + impl sp_blockchain::HeaderBackend for TestClient { + fn header(&self, _hash: Hash) -> sp_blockchain::Result> { + unimplemented!() + } + fn info(&self) -> sp_blockchain::Info { + sp_blockchain::Info { + best_hash: CORRECT_BLOCK_HASH.into(), + best_number: 0, + genesis_hash: Default::default(), + finalized_hash: CORRECT_BLOCK_HASH.into(), + finalized_number: 1, + finalized_state: None, + number_leaves: 0, + block_gap: None, + } + } + fn status(&self, _hash: Hash) -> sp_blockchain::Result { + unimplemented!() + } + fn number(&self, _hash: Hash) -> sp_blockchain::Result> { + unimplemented!() + } + fn hash(&self, _number: BlockNumber) -> sp_blockchain::Result> { + unimplemented!() + } + } + + fn test_store() -> (Store, tempfile::TempDir) { + let _ = env_logger::try_init(); + let temp_dir = tempfile::Builder::new().tempdir().expect("Error creating test dir"); + + let client = std::sync::Arc::new(TestClient); + let mut path: std::path::PathBuf = temp_dir.path().into(); + path.push("db"); + let store = Store::new(&path, Default::default(), client, None).unwrap(); + (store, temp_dir) // return order is important. Store must be dropped before TempDir + } + + fn signed_statement(data: u8) -> Statement { + signed_statement_with_topics(data, &[], None) + } + + fn signed_statement_with_topics( + data: u8, + topics: &[Topic], + dec_key: Option, + ) -> Statement { + let mut statement = Statement::new(); + statement.set_plain_data(vec![data]); + for i in 0..topics.len() { + statement.set_topic(i, topics[i]); + } + if let Some(key) = dec_key { + statement.set_decryption_key(key); + } + let kp = sp_core::ed25519::Pair::from_string("//Alice", None).unwrap(); + statement.sign_ed25519_private(&kp); + statement + } + + fn topic(data: u64) -> Topic { + let mut topic: Topic = Default::default(); + topic[0..8].copy_from_slice(&data.to_le_bytes()); + topic + } + + fn dec_key(data: u64) -> DecryptionKey { + let mut dec_key: DecryptionKey = Default::default(); + dec_key[0..8].copy_from_slice(&data.to_le_bytes()); + dec_key + } + + fn account(id: u64) -> AccountId { + let mut account: AccountId = Default::default(); + account[0..8].copy_from_slice(&id.to_le_bytes()); + account + } + + fn channel(id: u64) -> Channel { + let mut channel: Channel = Default::default(); + channel[0..8].copy_from_slice(&id.to_le_bytes()); + channel + } + + fn statement(account_id: u64, priority: u32, c: Option, data_len: usize) -> Statement { + let mut statement = Statement::new(); + let mut data = Vec::new(); + data.resize(data_len, 0); + statement.set_plain_data(data); + statement.set_priority(priority); + if let Some(c) = c { + statement.set_channel(channel(c)); + } + statement.set_proof(Proof::OnChain { + block_hash: CORRECT_BLOCK_HASH, + who: account(account_id), + event_index: 0, + }); + statement + } + + #[test] + fn submit_one() { + let (store, _temp) = test_store(); + let statement0 = signed_statement(0); + assert_eq!( + store.submit(statement0, StatementSource::Network), + SubmitResult::New(NetworkPriority::High) + ); + let unsigned = statement(0, 1, None, 0); + assert_eq!( + store.submit(unsigned, StatementSource::Network), + SubmitResult::New(NetworkPriority::High) + ); + } + + #[test] + fn save_and_load_statements() { + let (store, temp) = test_store(); + let statement0 = signed_statement(0); + let statement1 = signed_statement(1); + let statement2 = signed_statement(2); + assert_eq!( + store.submit(statement0.clone(), StatementSource::Network), + SubmitResult::New(NetworkPriority::High) + ); + assert_eq!( + store.submit(statement1.clone(), StatementSource::Network), + SubmitResult::New(NetworkPriority::High) + ); + assert_eq!( + store.submit(statement2.clone(), StatementSource::Network), + SubmitResult::New(NetworkPriority::High) + ); + assert_eq!(store.statements().unwrap().len(), 3); + assert_eq!(store.broadcasts(&[]).unwrap().len(), 3); + assert_eq!(store.statement(&statement1.hash()).unwrap(), Some(statement1.clone())); + drop(store); + + let client = std::sync::Arc::new(TestClient); + let mut path: std::path::PathBuf = temp.path().into(); + path.push("db"); + let store = Store::new(&path, Default::default(), client, None).unwrap(); + assert_eq!(store.statements().unwrap().len(), 3); + assert_eq!(store.broadcasts(&[]).unwrap().len(), 3); + assert_eq!(store.statement(&statement1.hash()).unwrap(), Some(statement1)); + } + + #[test] + fn search_by_topic_and_key() { + let (store, _temp) = test_store(); + let statement0 = signed_statement(0); + let statement1 = signed_statement_with_topics(1, &[topic(0)], None); + let statement2 = signed_statement_with_topics(2, &[topic(0), topic(1)], Some(dec_key(2))); + let statement3 = signed_statement_with_topics(3, &[topic(0), topic(1), topic(2)], None); + let statement4 = + signed_statement_with_topics(4, &[topic(0), topic(42), topic(2), topic(3)], None); + let statements = vec![statement0, statement1, statement2, statement3, statement4]; + for s in &statements { + store.submit(s.clone(), StatementSource::Network); + } + + let assert_topics = |topics: &[u64], key: Option, expected: &[u8]| { + let key = key.map(dec_key); + let topics: Vec<_> = topics.iter().map(|t| topic(*t)).collect(); + let mut got_vals: Vec<_> = if let Some(key) = key { + store.posted(&topics, key).unwrap().into_iter().map(|d| d[0]).collect() + } else { + store.broadcasts(&topics).unwrap().into_iter().map(|d| d[0]).collect() + }; + got_vals.sort(); + assert_eq!(expected.to_vec(), got_vals); + }; + + assert_topics(&[], None, &[0, 1, 3, 4]); + assert_topics(&[], Some(2), &[2]); + assert_topics(&[0], None, &[1, 3, 4]); + assert_topics(&[1], None, &[3]); + assert_topics(&[2], None, &[3, 4]); + assert_topics(&[3], None, &[4]); + assert_topics(&[42], None, &[4]); + + assert_topics(&[0, 1], None, &[3]); + assert_topics(&[0, 1], Some(2), &[2]); + assert_topics(&[0, 1, 99], Some(2), &[]); + assert_topics(&[1, 2], None, &[3]); + assert_topics(&[99], None, &[]); + assert_topics(&[0, 99], None, &[]); + assert_topics(&[0, 1, 2, 3, 42], None, &[]); + } + + #[test] + fn constraints() { + let (store, _temp) = test_store(); + + store.index.write().options.max_total_size = 3000; + let source = StatementSource::Network; + let ok = SubmitResult::New(NetworkPriority::High); + let ignored = SubmitResult::Ignored; + + // Account 1 (limit = 1 msg, 1000 bytes) + + // Oversized statement is not allowed. Limit for account 1 is 1 msg, 1000 bytes + assert_eq!(store.submit(statement(1, 1, Some(1), 2000), source), ignored); + assert_eq!(store.submit(statement(1, 1, Some(1), 500), source), ok); + // Would not replace channel message with same priority + assert_eq!(store.submit(statement(1, 1, Some(1), 200), source), ignored); + assert_eq!(store.submit(statement(1, 2, Some(1), 600), source), ok); + // Submit another message to another channel with lower priority. Should not be allowed + // because msg count limit is 1 + assert_eq!(store.submit(statement(1, 1, Some(2), 100), source), ignored); + assert_eq!(store.index.read().expired.len(), 1); + + // Account 2 (limit = 2 msg, 1000 bytes) + + assert_eq!(store.submit(statement(2, 1, None, 500), source), ok); + assert_eq!(store.submit(statement(2, 2, None, 100), source), ok); + // Should evict priority 1 + assert_eq!(store.submit(statement(2, 3, None, 500), source), ok); + assert_eq!(store.index.read().expired.len(), 2); + // Should evict all + assert_eq!(store.submit(statement(2, 4, None, 1000), source), ok); + assert_eq!(store.index.read().expired.len(), 4); + + // Account 3 (limit = 3 msg, 1000 bytes) + + assert_eq!(store.submit(statement(3, 2, Some(1), 300), source), ok); + assert_eq!(store.submit(statement(3, 3, Some(2), 300), source), ok); + assert_eq!(store.submit(statement(3, 4, Some(3), 300), source), ok); + // Should evict 2 and 3 + assert_eq!(store.submit(statement(3, 5, None, 500), source), ok); + assert_eq!(store.index.read().expired.len(), 6); + + assert_eq!(store.index.read().total_size, 2400); + assert_eq!(store.index.read().entries.len(), 4); + + // Should be over the global size limit + assert_eq!(store.submit(statement(1, 1, None, 700), source), ignored); + // Should be over the global count limit + store.index.write().options.max_total_statements = 4; + assert_eq!(store.submit(statement(1, 1, None, 100), source), ignored); + + let mut expected_statements = vec![ + statement(1, 2, Some(1), 600).hash(), + statement(2, 4, None, 1000).hash(), + statement(3, 4, Some(3), 300).hash(), + statement(3, 5, None, 500).hash(), + //statement(4, 6, None, 100).hash(), + ]; + expected_statements.sort(); + let mut statements: Vec<_> = + store.statements().unwrap().into_iter().map(|(hash, _)| hash).collect(); + statements.sort(); + assert_eq!(expected_statements, statements); + } + + #[test] + fn expired_statements_are_purged() { + use super::DEFAULT_PURGE_AFTER_SEC; + let (mut store, temp) = test_store(); + let mut statement = statement(1, 1, Some(3), 100); + store.set_time(0); + statement.set_topic(0, topic(4)); + store.submit(statement.clone(), StatementSource::Network); + assert_eq!(store.index.read().entries.len(), 1); + store.remove(&statement.hash()).unwrap(); + assert_eq!(store.index.read().entries.len(), 0); + assert_eq!(store.index.read().accounts.len(), 0); + store.set_time(DEFAULT_PURGE_AFTER_SEC + 1); + store.maintain(); + assert_eq!(store.index.read().expired.len(), 0); + drop(store); + + let client = std::sync::Arc::new(TestClient); + let mut path: std::path::PathBuf = temp.path().into(); + path.push("db"); + let store = Store::new(&path, Default::default(), client, None).unwrap(); + assert_eq!(store.statements().unwrap().len(), 0); + assert_eq!(store.index.read().expired.len(), 0); + } +} diff --git a/client/statement-store/src/metrics.rs b/client/statement-store/src/metrics.rs new file mode 100644 index 000000000..cf191b797 --- /dev/null +++ b/client/statement-store/src/metrics.rs @@ -0,0 +1,79 @@ +// This file is part of Substrate. + +// Copyright (C) Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +//! Statement store Prometheus metrics. + +use std::sync::Arc; + +use prometheus_endpoint::{register, Counter, PrometheusError, Registry, U64}; + +#[derive(Clone, Default)] +pub struct MetricsLink(Arc>); + +impl MetricsLink { + pub fn new(registry: Option<&Registry>) -> Self { + Self(Arc::new(registry.and_then(|registry| { + Metrics::register(registry) + .map_err(|err| { + log::warn!("Failed to register prometheus metrics: {}", err); + }) + .ok() + }))) + } + + pub fn report(&self, do_this: impl FnOnce(&Metrics)) { + if let Some(metrics) = self.0.as_ref() { + do_this(metrics); + } + } +} + +/// Statement store Prometheus metrics. +pub struct Metrics { + pub submitted_statements: Counter, + pub validations_invalid: Counter, + pub statements_pruned: Counter, +} + +impl Metrics { + pub fn register(registry: &Registry) -> Result { + Ok(Self { + submitted_statements: register( + Counter::new( + "substrate_sub_statement_store_submitted_statements", + "Total number of statements submitted", + )?, + registry, + )?, + validations_invalid: register( + Counter::new( + "substrate_sub_statement_store_validations_invalid", + "Total number of statements that were removed from the pool as invalid", + )?, + registry, + )?, + statements_pruned: register( + Counter::new( + "substrate_sub_statement_store_block_statements", + "Total number of statements that was requested to be pruned by block events", + )?, + registry, + )?, + }) + } +} diff --git a/frame/statement/Cargo.toml b/frame/statement/Cargo.toml new file mode 100644 index 000000000..8f9a62695 --- /dev/null +++ b/frame/statement/Cargo.toml @@ -0,0 +1,46 @@ +[package] +name = "pallet-statement" +version = "4.0.0-dev" +authors = ["Parity Technologies "] +edition = "2021" +license = "Apache-2.0" +homepage = "https://substrate.io" +repository = "https://github.com/paritytech/substrate/" +description = "FRAME pallet for statement store" + +[package.metadata.docs.rs] +targets = ["x86_64-unknown-linux-gnu"] + +[dependencies] +codec = { package = "parity-scale-codec", version = "3.2.2", default-features = false, features = ["derive"]} +scale-info = { version = "2.1.1", default-features = false, features = ["derive"] } +frame-support = { version = "4.0.0-dev", default-features = false, path = "../support" } +frame-system = { version = "4.0.0-dev", default-features = false, path = "../system" } +sp-statement-store = { version = "4.0.0-dev", default-features = false, path = "../../primitives/statement-store" } +sp-api = { version = "4.0.0-dev", default-features = false, path = "../../primitives/api" } +sp-runtime = { version = "7.0.0", default-features = false, path = "../../primitives/runtime" } +sp-std = { version = "5.0.0", default-features = false, path = "../../primitives/std" } +sp-io = { version = "7.0.0", default-features = false, path = "../../primitives/io" } +sp-core = { version = "7.0.0", default-features = false, path = "../../primitives/core" } +log = { version = "0.4.17", default-features = false } + +[dev-dependencies] +pallet-balances = { version = "4.0.0-dev", path = "../balances" } + +[features] +default = [ "std" ] +std = [ + "codec/std", + "scale-info/std", + "frame-support/std", + "frame-system/std", + "sp-api/std", + "sp-runtime/std", + "sp-std/std", + "sp-io/std", + "sp-core/std", + "sp-statement-store/std", +] +try-runtime = [ + "frame-support/try-runtime", +] diff --git a/frame/statement/src/lib.rs b/frame/statement/src/lib.rs new file mode 100644 index 000000000..c68dac2d2 --- /dev/null +++ b/frame/statement/src/lib.rs @@ -0,0 +1,222 @@ +// This file is part of Substrate. + +// Copyright (C) Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +//! Supporting pallet for the statement store. +//! +//! - [`Pallet`] +//! +//! ## Overview +//! +//! The Statement pallet provides means to create and validate statements for the statement store. +//! +//! For each statement validation function calculates the following three values based on the +//! statement author balance: +//! `max_count`: Maximum number of statements allowed for the author (signer) of this statement. +//! `max_size`: Maximum total size of statements allowed for the author (signer) of this statement. +//! +//! This pallet also contains an offchain worker that turns on-chain statement events into +//! statements. These statements are placed in the store and propagated over the network. + +#![cfg_attr(not(feature = "std"), no_std)] + +use frame_support::{ + pallet_prelude::*, + sp_runtime::{traits::CheckedDiv, SaturatedConversion}, + traits::fungible::Inspect, +}; +use frame_system::pallet_prelude::*; +use sp_statement_store::{ + runtime_api::{InvalidStatement, StatementSource, ValidStatement}, + Proof, SignatureVerificationResult, Statement, +}; + +#[cfg(test)] +mod mock; +#[cfg(test)] +mod tests; + +pub use pallet::*; + +const LOG_TARGET: &str = "runtime::statement"; + +#[frame_support::pallet] +pub mod pallet { + use super::*; + + pub type BalanceOf = + <::Currency as Inspect<::AccountId>>::Balance; + + pub type AccountIdOf = ::AccountId; + + #[pallet::config] + pub trait Config: frame_system::Config + where + ::AccountId: From, + { + /// The overarching event type. + type RuntimeEvent: From> + IsType<::RuntimeEvent>; + /// The currency which is used to calculate account limits. + type Currency: Inspect; + /// Min balance for priority statements. + #[pallet::constant] + type StatementCost: Get>; + /// Cost of data byte used for priority calculation. + #[pallet::constant] + type ByteCost: Get>; + /// Minimum number of statements allowed per account. + #[pallet::constant] + type MinAllowedStatements: Get; + /// Maximum number of statements allowed per account. + #[pallet::constant] + type MaxAllowedStatements: Get; + /// Minimum data bytes allowed per account. + #[pallet::constant] + type MinAllowedBytes: Get; + /// Maximum data bytes allowed per account. + #[pallet::constant] + type MaxAllowedBytes: Get; + } + + #[pallet::pallet] + pub struct Pallet(sp_std::marker::PhantomData); + + #[pallet::event] + #[pallet::generate_deposit(pub(super) fn deposit_event)] + pub enum Event + where + ::AccountId: From, + { + /// A new statement is submitted + NewStatement { account: T::AccountId, statement: Statement }, + } + + #[pallet::hooks] + impl Hooks> for Pallet + where + ::AccountId: From, + sp_statement_store::AccountId: From<::AccountId>, + ::RuntimeEvent: From>, + ::RuntimeEvent: TryInto>, + sp_statement_store::BlockHash: From<::Hash>, + { + fn offchain_worker(now: BlockNumberFor) { + log::trace!(target: LOG_TARGET, "Collecting statements at #{:?}", now); + Pallet::::collect_statements(); + } + } +} + +impl Pallet +where + ::AccountId: From, + sp_statement_store::AccountId: From<::AccountId>, + ::RuntimeEvent: From>, + ::RuntimeEvent: TryInto>, + sp_statement_store::BlockHash: From<::Hash>, +{ + /// Validate a statement against current state. This is supposed to be called by the statement + /// store on the host side. + pub fn validate_statement( + _source: StatementSource, + mut statement: Statement, + ) -> Result { + sp_io::init_tracing(); + log::debug!(target: LOG_TARGET, "Validating statement {:?}", statement); + let account: T::AccountId = match statement.proof() { + Some(Proof::OnChain { who, block_hash, event_index }) => { + if frame_system::Pallet::::parent_hash().as_ref() != block_hash.as_slice() { + log::debug!(target: LOG_TARGET, "Bad block hash."); + return Err(InvalidStatement::BadProof) + } + let account: T::AccountId = (*who).into(); + match frame_system::Pallet::::event_no_consensus(*event_index as usize) { + Some(e) => { + statement.remove_proof(); + if let Ok(Event::NewStatement { account: a, statement: s }) = e.try_into() { + if a != account || s != statement { + log::debug!(target: LOG_TARGET, "Event data mismatch"); + return Err(InvalidStatement::BadProof) + } + } else { + log::debug!(target: LOG_TARGET, "Event type mismatch"); + return Err(InvalidStatement::BadProof) + } + }, + _ => { + log::debug!(target: LOG_TARGET, "Bad event index"); + return Err(InvalidStatement::BadProof) + }, + } + account + }, + _ => match statement.verify_signature() { + SignatureVerificationResult::Valid(account) => account.into(), + SignatureVerificationResult::Invalid => { + log::debug!(target: LOG_TARGET, "Bad statement signature."); + return Err(InvalidStatement::BadProof) + }, + SignatureVerificationResult::NoSignature => { + log::debug!(target: LOG_TARGET, "Missing statement signature."); + return Err(InvalidStatement::NoProof) + }, + }, + }; + let statement_cost = T::StatementCost::get(); + let byte_cost = T::ByteCost::get(); + let balance = >>::balance(&account); + let min_allowed_statements = T::MinAllowedStatements::get(); + let max_allowed_statements = T::MaxAllowedStatements::get(); + let min_allowed_bytes = T::MinAllowedBytes::get(); + let max_allowed_bytes = T::MaxAllowedBytes::get(); + let max_count = balance + .checked_div(&statement_cost) + .unwrap_or_default() + .saturated_into::() + .clamp(min_allowed_statements, max_allowed_statements); + let max_size = balance + .checked_div(&byte_cost) + .unwrap_or_default() + .saturated_into::() + .clamp(min_allowed_bytes, max_allowed_bytes); + + Ok(ValidStatement { max_count, max_size }) + } + + /// Submit a statement event. The statement will be picked up by the offchain worker and + /// broadcast to the network. + pub fn submit_statement(account: T::AccountId, statement: Statement) { + Self::deposit_event(Event::NewStatement { account, statement }); + } + + fn collect_statements() { + // Find `NewStatement` events and submit them to the store + for (index, event) in frame_system::Pallet::::read_events_no_consensus().enumerate() { + if let Ok(Event::::NewStatement { account, mut statement }) = event.event.try_into() + { + if statement.proof().is_none() { + let proof = Proof::OnChain { + who: account.into(), + block_hash: frame_system::Pallet::::parent_hash().into(), + event_index: index as u64, + }; + statement.set_proof(proof); + } + sp_statement_store::runtime_api::statement_store::submit_statement(statement); + } + } + } +} diff --git a/frame/statement/src/mock.rs b/frame/statement/src/mock.rs new file mode 100644 index 000000000..f4d9360c9 --- /dev/null +++ b/frame/statement/src/mock.rs @@ -0,0 +1,126 @@ +// This file is part of Substrate. + +// Copyright (C) Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +//! # Statement pallet test environment. + +use super::*; + +use crate as pallet_statement; +use frame_support::{ + ord_parameter_types, + traits::{ConstU32, ConstU64, Everything}, + weights::constants::RocksDbWeight, +}; +use sp_core::{Pair, H256}; +use sp_runtime::{ + testing::Header, + traits::{BlakeTwo256, IdentityLookup}, + AccountId32, +}; + +type UncheckedExtrinsic = frame_system::mocking::MockUncheckedExtrinsic; +type Block = frame_system::mocking::MockBlock; + +pub const MIN_ALLOWED_STATEMENTS: u32 = 4; +pub const MAX_ALLOWED_STATEMENTS: u32 = 10; +pub const MIN_ALLOWED_BYTES: u32 = 1024; +pub const MAX_ALLOWED_BYTES: u32 = 4096; + +frame_support::construct_runtime!( + pub enum Test where + Block = Block, + NodeBlock = Block, + UncheckedExtrinsic = UncheckedExtrinsic, + { + System: frame_system, + Balances: pallet_balances, + Statement: pallet_statement, + } +); + +impl frame_system::Config for Test { + type BaseCallFilter = Everything; + type BlockWeights = (); + type BlockLength = (); + type DbWeight = RocksDbWeight; + type RuntimeOrigin = RuntimeOrigin; + type RuntimeCall = RuntimeCall; + type Index = u64; + type BlockNumber = u64; + type Hash = H256; + type Hashing = BlakeTwo256; + type AccountId = AccountId32; + type Lookup = IdentityLookup; + type Header = Header; + type RuntimeEvent = RuntimeEvent; + type BlockHashCount = ConstU64<250>; + type Version = (); + type PalletInfo = PalletInfo; + type AccountData = pallet_balances::AccountData; + type OnNewAccount = (); + type OnKilledAccount = (); + type SystemWeightInfo = (); + type SS58Prefix = (); + type OnSetCode = (); + type MaxConsumers = ConstU32<16>; +} + +impl pallet_balances::Config for Test { + type Balance = u64; + type RuntimeEvent = RuntimeEvent; + type DustRemoval = (); + type ExistentialDeposit = ConstU64<5>; + type AccountStore = System; + type WeightInfo = (); + type MaxLocks = (); + type MaxReserves = ConstU32<50>; + type ReserveIdentifier = [u8; 8]; + type FreezeIdentifier = (); + type MaxFreezes = (); + type HoldIdentifier = (); + type MaxHolds = (); +} + +ord_parameter_types! { + pub const One: u64 = 1; +} + +impl Config for Test { + type RuntimeEvent = RuntimeEvent; + type Currency = Balances; + type StatementCost = ConstU64<1000>; + type ByteCost = ConstU64<2>; + type MinAllowedStatements = ConstU32; + type MaxAllowedStatements = ConstU32; + type MinAllowedBytes = ConstU32; + type MaxAllowedBytes = ConstU32; +} + +pub fn new_test_ext() -> sp_io::TestExternalities { + let mut t = frame_system::GenesisConfig::default().build_storage::().unwrap(); + let balances = pallet_balances::GenesisConfig:: { + balances: vec![ + (sp_core::sr25519::Pair::from_string("//Alice", None).unwrap().public().into(), 6000), + ( + sp_core::sr25519::Pair::from_string("//Charlie", None).unwrap().public().into(), + 500000, + ), + ], + }; + balances.assimilate_storage(&mut t).unwrap(); + t.into() +} diff --git a/frame/statement/src/tests.rs b/frame/statement/src/tests.rs new file mode 100644 index 000000000..51103caca --- /dev/null +++ b/frame/statement/src/tests.rs @@ -0,0 +1,159 @@ +// This file is part of Substrate. + +// Copyright (C) Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +//! # Statement runtime support tests. + +#![cfg(test)] + +use super::*; +use crate::mock::*; +use sp_core::Pair; +use sp_runtime::AccountId32; +use sp_statement_store::{ + runtime_api::{InvalidStatement, StatementSource, ValidStatement}, + Proof, Statement, +}; + +#[test] +fn sign_and_validate_no_balance() { + new_test_ext().execute_with(|| { + let pair = sp_core::sr25519::Pair::from_string("//Bob", None).unwrap(); + let mut statement = Statement::new(); + statement.sign_sr25519_private(&pair); + let result = Pallet::::validate_statement(StatementSource::Chain, statement); + assert_eq!( + Ok(ValidStatement { max_count: MIN_ALLOWED_STATEMENTS, max_size: MIN_ALLOWED_BYTES }), + result + ); + + let pair = sp_core::ed25519::Pair::from_string("//Bob", None).unwrap(); + let mut statement = Statement::new(); + statement.sign_ed25519_private(&pair); + let result = Pallet::::validate_statement(StatementSource::Chain, statement); + assert_eq!( + Ok(ValidStatement { max_count: MIN_ALLOWED_STATEMENTS, max_size: MIN_ALLOWED_BYTES }), + result + ); + + let pair = sp_core::ecdsa::Pair::from_string("//Bob", None).unwrap(); + let mut statement = Statement::new(); + statement.sign_ecdsa_private(&pair); + let result = Pallet::::validate_statement(StatementSource::Chain, statement); + assert_eq!( + Ok(ValidStatement { max_count: MIN_ALLOWED_STATEMENTS, max_size: MIN_ALLOWED_BYTES }), + result + ); + }); +} + +#[test] +fn validate_with_balance() { + new_test_ext().execute_with(|| { + let pair = sp_core::sr25519::Pair::from_string("//Alice", None).unwrap(); + let mut statement = Statement::new(); + statement.sign_sr25519_private(&pair); + let result = Pallet::::validate_statement(StatementSource::Chain, statement); + assert_eq!(Ok(ValidStatement { max_count: 6, max_size: 3000 }), result); + + let pair = sp_core::sr25519::Pair::from_string("//Charlie", None).unwrap(); + let mut statement = Statement::new(); + statement.sign_sr25519_private(&pair); + let result = Pallet::::validate_statement(StatementSource::Chain, statement); + assert_eq!( + Ok(ValidStatement { max_count: MAX_ALLOWED_STATEMENTS, max_size: MAX_ALLOWED_BYTES }), + result + ); + }); +} + +#[test] +fn validate_no_proof_fails() { + new_test_ext().execute_with(|| { + let statement = Statement::new(); + let result = Pallet::::validate_statement(StatementSource::Chain, statement); + assert_eq!(Err(InvalidStatement::NoProof), result); + }); +} + +#[test] +fn validate_bad_signature_fails() { + new_test_ext().execute_with(|| { + let statement = Statement::new_with_proof(Proof::Sr25519 { + signature: [0u8; 64], + signer: Default::default(), + }); + let result = Pallet::::validate_statement(StatementSource::Chain, statement); + assert_eq!(Err(InvalidStatement::BadProof), result); + }); +} + +#[test] +fn validate_event() { + new_test_ext().execute_with(|| { + let parent_hash = sp_core::H256::random(); + System::reset_events(); + System::initialize(&1, &parent_hash, &Default::default()); + let mut statement = Statement::new(); + let pair = sp_core::sr25519::Pair::from_string("//Alice", None).unwrap(); + let account: AccountId32 = pair.public().into(); + Pallet::::submit_statement(account.clone(), statement.clone()); + statement.set_proof(Proof::OnChain { + who: account.clone().into(), + event_index: 0, + block_hash: parent_hash.into(), + }); + let result = Pallet::::validate_statement(StatementSource::Chain, statement.clone()); + assert_eq!(Ok(ValidStatement { max_count: 6, max_size: 3000 }), result); + + // Use wrong event index + statement.set_proof(Proof::OnChain { + who: account.clone().into(), + event_index: 1, + block_hash: parent_hash.into(), + }); + let result = Pallet::::validate_statement(StatementSource::Chain, statement.clone()); + assert_eq!(Err(InvalidStatement::BadProof), result); + + // Use wrong block hash + statement.set_proof(Proof::OnChain { + who: account.clone().into(), + event_index: 0, + block_hash: sp_core::H256::random().into(), + }); + let result = Pallet::::validate_statement(StatementSource::Chain, statement.clone()); + assert_eq!(Err(InvalidStatement::BadProof), result); + }); +} + +#[test] +fn validate_no_event_fails() { + new_test_ext().execute_with(|| { + let parent_hash = sp_core::H256::random(); + System::reset_events(); + System::initialize(&1, &parent_hash, &Default::default()); + let mut statement = Statement::new(); + let pair = sp_core::sr25519::Pair::from_string("//Alice", None).unwrap(); + let account: AccountId32 = pair.public().into(); + statement.set_proof(Proof::OnChain { + who: account.into(), + event_index: 0, + block_hash: parent_hash.into(), + }); + let result = Pallet::::validate_statement(StatementSource::Chain, statement); + assert_eq!(Err(InvalidStatement::BadProof), result); + }); +} diff --git a/frame/system/src/lib.rs b/frame/system/src/lib.rs index 6bbebb870..81bead4f8 100644 --- a/frame/system/src/lib.rs +++ b/frame/system/src/lib.rs @@ -1452,6 +1452,14 @@ impl Pallet { Self::read_events_no_consensus().map(|e| *e).collect() } + /// Get a single event at specified index. + /// + /// Should only be called if you know what you are doing and outside of the runtime block + /// execution else it can have a large impact on the PoV size of a block. + pub fn event_no_consensus(index: usize) -> Option { + Self::read_events_no_consensus().nth(index).map(|e| e.event.clone()) + } + /// Get the current events deposited by the runtime. /// /// Should only be called if you know what you are doing and outside of the runtime block diff --git a/primitives/application-crypto/src/lib.rs b/primitives/application-crypto/src/lib.rs index 5e7779519..fa92e427a 100644 --- a/primitives/application-crypto/src/lib.rs +++ b/primitives/application-crypto/src/lib.rs @@ -309,6 +309,13 @@ macro_rules! app_crypto_public_common { <$public>::try_from(data).map(Into::into) } } + + impl Public { + /// Convert into wrapped generic public key type. + pub fn into_inner(self) -> $public { + self.0 + } + } }; } @@ -470,6 +477,13 @@ macro_rules! app_crypto_signature_common { Self::try_from(&data[..]) } } + + impl Signature { + /// Convert into wrapped generic signature type. + pub fn into_inner(self) -> $sig { + self.0 + } + } }; } diff --git a/primitives/blockchain/src/error.rs b/primitives/blockchain/src/error.rs index 41e5cda9c..d7f708638 100644 --- a/primitives/blockchain/src/error.rs +++ b/primitives/blockchain/src/error.rs @@ -159,6 +159,9 @@ pub enum Error { #[error("State Database error: {0}")] StateDatabase(String), + #[error("Statement store error: {0}")] + StatementStore(String), + #[error("Failed to set the chain head to a block that's too old.")] SetHeadTooOld, diff --git a/primitives/core/src/crypto.rs b/primitives/core/src/crypto.rs index dd0d9e605..9d63cbc51 100644 --- a/primitives/core/src/crypto.rs +++ b/primitives/core/src/crypto.rs @@ -1140,6 +1140,8 @@ pub mod key_types { pub const AUTHORITY_DISCOVERY: KeyTypeId = KeyTypeId(*b"audi"); /// Key type for staking, built-in. Identified as `stak`. pub const STAKING: KeyTypeId = KeyTypeId(*b"stak"); + /// A key type for signing statements + pub const STATEMENT: KeyTypeId = KeyTypeId(*b"stmt"); /// A key type ID useful for tests. pub const DUMMY: KeyTypeId = KeyTypeId(*b"dumy"); } diff --git a/primitives/core/src/offchain/mod.rs b/primitives/core/src/offchain/mod.rs index 5a77e19a3..a9e663980 100644 --- a/primitives/core/src/offchain/mod.rs +++ b/primitives/core/src/offchain/mod.rs @@ -278,16 +278,8 @@ bitflags::bitflags! { const NODE_AUTHORIZATION = 0b0000_1000_0000; /// Access time related functionality const TIME = 0b0001_0000_0000; - } -} - -impl Capabilities { - /// Return capabilities for rich offchain calls. - /// - /// Those calls should be allowed to sign and submit transactions - /// and access offchain workers database (but read only!). - pub fn rich_offchain_call() -> Self { - Capabilities::TRANSACTION_POOL | Capabilities::KEYSTORE | Capabilities::OFFCHAIN_DB_READ + /// Access the statement store. + const STATEMENT_STORE = 0b0010_0000_0000; } } diff --git a/primitives/statement-store/Cargo.toml b/primitives/statement-store/Cargo.toml new file mode 100644 index 000000000..5aa4d8336 --- /dev/null +++ b/primitives/statement-store/Cargo.toml @@ -0,0 +1,41 @@ +[package] +name = "sp-statement-store" +version = "4.0.0-dev" +authors = ["Parity Technologies "] +edition = "2021" +license = "Apache-2.0" +homepage = "https://substrate.io" +repository = "https://github.com/paritytech/substrate/" +description = "A crate which contains primitives related to the statement store" +readme = "README.md" + +[package.metadata.docs.rs] +targets = ["x86_64-unknown-linux-gnu"] + +[dependencies] +codec = { package = "parity-scale-codec", version = "3.2.2", default-features = false, features = ["derive"] } +scale-info = { version = "2.1.1", default-features = false, features = ["derive"] } +sp-core = { version = "7.0.0", default-features = false, path = "../core" } +sp-runtime = { version = "7.0.0", default-features = false, path = "../runtime" } +sp-std = { version = "5.0.0", default-features = false, path = "../std" } +sp-api = { version = "4.0.0-dev", default-features = false, path = "../api" } +sp-application-crypto = { version = "7.0.0", default-features = false, path = "../application-crypto" } +sp-runtime-interface = { version = "7.0.0", default-features = false, path = "../runtime-interface" } +sp-externalities = { version = "0.13.0", default-features = false, path = "../externalities" } +thiserror = { version = "1.0", optional = true } +log = { version = "0.4.17", optional = true } + +[features] +default = ["std"] +std = [ + "codec/std", + "scale-info/std", + "sp-core/std", + "sp-runtime/std", + "sp-runtime-interface/std", + "sp-std/std", + "sp-api/std", + "sp-application-crypto/std", + "thiserror", + "log", +] diff --git a/primitives/statement-store/README.md b/primitives/statement-store/README.md new file mode 100644 index 000000000..fb88aaa4e --- /dev/null +++ b/primitives/statement-store/README.md @@ -0,0 +1,39 @@ +Statement store is an off-chain data-store for signed statements accessible via RPC and OCW. + +Nodes hold a number of statements with a proof of authenticity owing to an account ID. OCWs can place items in the data-store (with valid signatures) for any accounts whose keys they control. Users can also submit pre-signed statements via RPC. Statements can also be submitted from on-chain logic through an on-chain event. + +A new system event `NewStatement` is added to the runtime. This event allows any account on-chain to declare that they want to make a statement for the store. Within the node store and for broadcasting, the statement would be accompanied with the hash of the block and index of the event within it, essentially taking the place of a real signature. + +Statements comprise an optional proof of authenticity (e.g. a signature) and a number of fields. For statements without a proof, nodes would gossip statements randomly with a rate-limiter to minimise the chance of being overrun by a misbehaving node. These will generally be disregarded by nodes unless they are gossiped by several different peers or if a peer pays for it somehow (e.g. gossiping something valuable). + +Each field is effectively a key/value pair. Fields must be sorted and the same field type may not be repeated. Depending on which keys are present, clients may index the message for ease of retrieval. + +Formally, `Statement` is equivalent to the type `Vec` and `Field` is the SCALE-encoded enumeration: +- 0: `AuthenticityProof(Proof)`: The signature of the message. For cryptography where the public key cannot be derived from the signature together with the message data, then this will also include the signer's public key. The message data is all fields of the messages fields except the signature concatenated together *without the length prefix that a `Vec` would usually imply*. This is so that the signature can be derived without needing to re-encode the statement. +- 1: `DecryptionKey([u8; 32])`: The decryption key identifier which should be used to decrypt the statement's data. In the absense of this field `Data` should be treated as not encrypted. +- 2: `Priority(u32)`: Priority specifier. Higher priority statements should be kept around at the cost of lower priority statements if multiple statements from the same sender are competing for persistence or transport. Nodes should disregard when considering unsigned statements. +- 3: `Channel([u8; 32])`: The channel identifier. Only one message of a given channel should be retained at once (the one of highest priority). Nodes should disregard when considering unsigned statements. +- 4: `Topic1([u8; 32]))`: First topic identifier. +- 5: `Topic2([u8; 32]))`: Second topic identifier. +- 6: `Topic3([u8; 32]))`: Third topic identifier. +- 7: `Topic4([u8; 32]))`: Fourth topic identifier. +- 8: `Data(Vec)`: General data segment. No special meaning. + +`Proof` is defined as the SCALE-encoded enumeration: +- 0: `Sr25519 { signature: [u8; 64], signer: [u8; 32] }` +- 1: `Ed25519 { signature: [u8; 64], signer: [u8; 32] )` +- 2: `Secp256k1Ecdsa { signature: [u8; 65], signer: [u8; 33] )` +- 3: `OnChain { who: [u8; 32], block_hash: [u8; 32], event_index: u64 }` + +### Potential uses + +Potential use-cases are various and include: +- ring-signature aggregation; +- messaging; +- state-channels; +- deferral of the initial "advertising" phase of multi-party transactions; +- publication of preimage data whose hash is referenced on-chain; +- effective transferal of fee payment to a second-party. + + +License: Apache-2.0 diff --git a/primitives/statement-store/src/lib.rs b/primitives/statement-store/src/lib.rs new file mode 100644 index 000000000..e5c642d24 --- /dev/null +++ b/primitives/statement-store/src/lib.rs @@ -0,0 +1,618 @@ +// This file is part of Substrate. + +// Copyright (C) Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#![cfg_attr(not(feature = "std"), no_std)] +#![warn(missing_docs)] + +//! A crate which contains statement-store primitives. + +use codec::{Decode, Encode}; +use scale_info::TypeInfo; +use sp_application_crypto::RuntimeAppPublic; +#[cfg(feature = "std")] +use sp_core::Pair; +use sp_runtime_interface::pass_by::PassByCodec; +use sp_std::vec::Vec; + +/// Statement topic. +pub type Topic = [u8; 32]; +/// Decryption key identifier. +pub type DecryptionKey = [u8; 32]; +/// Statement hash. +pub type Hash = [u8; 32]; +/// Block hash. +pub type BlockHash = [u8; 32]; +/// Account id +pub type AccountId = [u8; 32]; +/// Statement channel. +pub type Channel = [u8; 32]; + +/// Total number of topic fields allowed. +pub const MAX_TOPICS: usize = 4; + +#[cfg(feature = "std")] +pub use store_api::{ + Error, NetworkPriority, Result, StatementSource, StatementStore, SubmitResult, +}; + +pub mod runtime_api; +#[cfg(feature = "std")] +mod store_api; + +mod sr25519 { + mod app_sr25519 { + use sp_application_crypto::{app_crypto, key_types::STATEMENT, sr25519}; + app_crypto!(sr25519, STATEMENT); + } + pub type Public = app_sr25519::Public; +} + +mod ed25519 { + mod app_ed25519 { + use sp_application_crypto::{app_crypto, ed25519, key_types::STATEMENT}; + app_crypto!(ed25519, STATEMENT); + } + pub type Public = app_ed25519::Public; +} + +mod ecdsa { + mod app_ecdsa { + use sp_application_crypto::{app_crypto, ecdsa, key_types::STATEMENT}; + app_crypto!(ecdsa, STATEMENT); + } + pub type Public = app_ecdsa::Public; +} + +/// Returns blake2-256 hash for the encoded statement. +#[cfg(feature = "std")] +pub fn hash_encoded(data: &[u8]) -> [u8; 32] { + sp_core::hashing::blake2_256(data) +} + +/// Statement proof. +#[derive(Encode, Decode, TypeInfo, sp_core::RuntimeDebug, Clone, PartialEq, Eq)] +pub enum Proof { + /// Sr25519 Signature. + Sr25519 { + /// Signature. + signature: [u8; 64], + /// Public key. + signer: [u8; 32], + }, + /// Ed25519 Signature. + Ed25519 { + /// Signature. + signature: [u8; 64], + /// Public key. + signer: [u8; 32], + }, + /// Secp256k1 Signature. + Secp256k1Ecdsa { + /// Signature. + signature: [u8; 65], + /// Public key. + signer: [u8; 33], + }, + /// On-chain event proof. + OnChain { + /// Account identifier associated with the event. + who: AccountId, + /// Hash of block that contains the event. + block_hash: BlockHash, + /// Index of the event in the event list. + event_index: u64, + }, +} + +impl Proof { + /// Return account id for the proof creator. + pub fn account_id(&self) -> AccountId { + match self { + Proof::Sr25519 { signer, .. } => *signer, + Proof::Ed25519 { signer, .. } => *signer, + Proof::Secp256k1Ecdsa { signer, .. } => + ::hash(signer).into(), + Proof::OnChain { who, .. } => *who, + } + } +} + +/// Statement attributes. Each statement is a list of 0 or more fields. Fields may only appear once +/// and in the order declared here. +#[derive(Encode, Decode, TypeInfo, sp_core::RuntimeDebug, Clone, PartialEq, Eq)] +#[repr(u8)] +pub enum Field { + /// Statement proof. + AuthenticityProof(Proof) = 0, + /// An identifier for the key that `Data` field may be decrypted with. + DecryptionKey(DecryptionKey) = 1, + /// Priority when competing with other messages from the same sender. + Priority(u32) = 2, + /// Account channel to use. Only one message per `(account, channel)` pair is allowed. + Channel(Channel) = 3, + /// First statement topic. + Topic1(Topic) = 4, + /// Second statement topic. + Topic2(Topic) = 5, + /// Third statement topic. + Topic3(Topic) = 6, + /// Fourth statement topic. + Topic4(Topic) = 7, + /// Additional data. + Data(Vec) = 8, +} + +impl Field { + fn discriminant(&self) -> u8 { + // This is safe for repr(u8) + // see https://doc.rust-lang.org/reference/items/enumerations.html#pointer-casting + unsafe { *(self as *const Self as *const u8) } + } +} + +/// Statement structure. +#[derive(TypeInfo, sp_core::RuntimeDebug, PassByCodec, Clone, PartialEq, Eq, Default)] +pub struct Statement { + proof: Option, + decryption_key: Option, + channel: Option, + priority: Option, + num_topics: u8, + topics: [Topic; MAX_TOPICS], + data: Option>, +} + +impl Decode for Statement { + fn decode(input: &mut I) -> core::result::Result { + // Encoding matches that of Vec. Basically this just means accepting that there + // will be a prefix of vector length. + let num_fields: codec::Compact = Decode::decode(input)?; + let mut tag = 0; + let mut statement = Statement::new(); + for i in 0..num_fields.into() { + let field: Field = Decode::decode(input)?; + if i > 0 && field.discriminant() <= tag { + return Err("Invalid field order or duplicate fields".into()) + } + tag = field.discriminant(); + match field { + Field::AuthenticityProof(p) => statement.set_proof(p), + Field::DecryptionKey(key) => statement.set_decryption_key(key), + Field::Priority(p) => statement.set_priority(p), + Field::Channel(c) => statement.set_channel(c), + Field::Topic1(t) => statement.set_topic(0, t), + Field::Topic2(t) => statement.set_topic(1, t), + Field::Topic3(t) => statement.set_topic(2, t), + Field::Topic4(t) => statement.set_topic(3, t), + Field::Data(data) => statement.set_plain_data(data), + } + } + Ok(statement) + } +} + +impl Encode for Statement { + fn encode(&self) -> Vec { + self.encoded(false) + } +} + +#[derive(Clone, Copy, PartialEq, Eq, Debug)] +/// Result returned by `Statement::verify_signature` +pub enum SignatureVerificationResult { + /// Signature is valid and matches this account id. + Valid(AccountId), + /// Signature has failed verification. + Invalid, + /// No signature in the proof or no proof. + NoSignature, +} + +impl Statement { + /// Create a new empty statement with no proof. + pub fn new() -> Statement { + Default::default() + } + + /// Create a new statement with a proof. + pub fn new_with_proof(proof: Proof) -> Statement { + let mut statement = Self::new(); + statement.set_proof(proof); + statement + } + + /// Sign with a key that matches given public key in the keystore. + /// + /// Returns `true` if signing worked (private key present etc). + /// + /// NOTE: This can only be called from the runtime. + pub fn sign_sr25519_public(&mut self, key: &sr25519::Public) -> bool { + let to_sign = self.signature_material(); + if let Some(signature) = key.sign(&to_sign) { + let proof = Proof::Sr25519 { + signature: signature.into_inner().into(), + signer: key.clone().into_inner().into(), + }; + self.set_proof(proof); + true + } else { + false + } + } + + /// Sign with a given private key and add the signature proof field. + #[cfg(feature = "std")] + pub fn sign_sr25519_private(&mut self, key: &sp_core::sr25519::Pair) { + let to_sign = self.signature_material(); + let proof = + Proof::Sr25519 { signature: key.sign(&to_sign).into(), signer: key.public().into() }; + self.set_proof(proof); + } + + /// Sign with a key that matches given public key in the keystore. + /// + /// Returns `true` if signing worked (private key present etc). + /// + /// NOTE: This can only be called from the runtime. + pub fn sign_ed25519_public(&mut self, key: &ed25519::Public) -> bool { + let to_sign = self.signature_material(); + if let Some(signature) = key.sign(&to_sign) { + let proof = Proof::Ed25519 { + signature: signature.into_inner().into(), + signer: key.clone().into_inner().into(), + }; + self.set_proof(proof); + true + } else { + false + } + } + + /// Sign with a given private key and add the signature proof field. + #[cfg(feature = "std")] + pub fn sign_ed25519_private(&mut self, key: &sp_core::ed25519::Pair) { + let to_sign = self.signature_material(); + let proof = + Proof::Ed25519 { signature: key.sign(&to_sign).into(), signer: key.public().into() }; + self.set_proof(proof); + } + + /// Sign with a key that matches given public key in the keystore. + /// + /// Returns `true` if signing worked (private key present etc). + /// + /// NOTE: This can only be called from the runtime. + /// + /// Returns `true` if signing worked (private key present etc). + /// + /// NOTE: This can only be called from the runtime. + pub fn sign_ecdsa_public(&mut self, key: &ecdsa::Public) -> bool { + let to_sign = self.signature_material(); + if let Some(signature) = key.sign(&to_sign) { + let proof = Proof::Secp256k1Ecdsa { + signature: signature.into_inner().into(), + signer: key.clone().into_inner().0, + }; + self.set_proof(proof); + true + } else { + false + } + } + + /// Sign with a given private key and add the signature proof field. + #[cfg(feature = "std")] + pub fn sign_ecdsa_private(&mut self, key: &sp_core::ecdsa::Pair) { + let to_sign = self.signature_material(); + let proof = + Proof::Secp256k1Ecdsa { signature: key.sign(&to_sign).into(), signer: key.public().0 }; + self.set_proof(proof); + } + + /// Check proof signature, if any. + pub fn verify_signature(&self) -> SignatureVerificationResult { + use sp_runtime::traits::Verify; + + match self.proof() { + Some(Proof::OnChain { .. }) | None => SignatureVerificationResult::NoSignature, + Some(Proof::Sr25519 { signature, signer }) => { + let to_sign = self.signature_material(); + let signature = sp_core::sr25519::Signature(*signature); + let public = sp_core::sr25519::Public(*signer); + if signature.verify(to_sign.as_slice(), &public) { + SignatureVerificationResult::Valid(*signer) + } else { + SignatureVerificationResult::Invalid + } + }, + Some(Proof::Ed25519 { signature, signer }) => { + let to_sign = self.signature_material(); + let signature = sp_core::ed25519::Signature(*signature); + let public = sp_core::ed25519::Public(*signer); + if signature.verify(to_sign.as_slice(), &public) { + SignatureVerificationResult::Valid(*signer) + } else { + SignatureVerificationResult::Invalid + } + }, + Some(Proof::Secp256k1Ecdsa { signature, signer }) => { + let to_sign = self.signature_material(); + let signature = sp_core::ecdsa::Signature(*signature); + let public = sp_core::ecdsa::Public(*signer); + if signature.verify(to_sign.as_slice(), &public) { + let sender_hash = + ::hash(signer); + SignatureVerificationResult::Valid(sender_hash.into()) + } else { + SignatureVerificationResult::Invalid + } + }, + } + } + + /// Calculate statement hash. + #[cfg(feature = "std")] + pub fn hash(&self) -> [u8; 32] { + self.using_encoded(hash_encoded) + } + + /// Returns a topic by topic index. + pub fn topic(&self, index: usize) -> Option { + if index < self.num_topics as usize { + Some(self.topics[index]) + } else { + None + } + } + + /// Returns decryption key if any. + pub fn decryption_key(&self) -> Option { + self.decryption_key + } + + /// Convert to internal data. + pub fn into_data(self) -> Option> { + self.data + } + + /// Get a reference to the statement proof, if any. + pub fn proof(&self) -> Option<&Proof> { + self.proof.as_ref() + } + + /// Get proof account id, if any + pub fn account_id(&self) -> Option { + self.proof.as_ref().map(Proof::account_id) + } + + /// Get plain data. + pub fn data(&self) -> Option<&Vec> { + self.data.as_ref() + } + + /// Get plain data len. + pub fn data_len(&self) -> usize { + self.data().map_or(0, Vec::len) + } + + /// Get channel, if any. + pub fn channel(&self) -> Option { + self.channel + } + + /// Get priority, if any. + pub fn priority(&self) -> Option { + self.priority + } + + /// Return encoded fields that can be signed to construct or verify a proof + fn signature_material(&self) -> Vec { + self.encoded(true) + } + + /// Remove the proof of this statement. + pub fn remove_proof(&mut self) { + self.proof = None; + } + + /// Set statement proof. Any existing proof is overwritten. + pub fn set_proof(&mut self, proof: Proof) { + self.proof = Some(proof) + } + + /// Set statement priority. + pub fn set_priority(&mut self, priority: u32) { + self.priority = Some(priority) + } + + /// Set statement channel. + pub fn set_channel(&mut self, channel: Channel) { + self.channel = Some(channel) + } + + /// Set topic by index. Does noting if index is over `MAX_TOPICS`. + pub fn set_topic(&mut self, index: usize, topic: Topic) { + if index < MAX_TOPICS { + self.topics[index] = topic; + self.num_topics = self.num_topics.max(index as u8 + 1); + } + } + + /// Set decryption key. + pub fn set_decryption_key(&mut self, key: DecryptionKey) { + self.decryption_key = Some(key); + } + + /// Set unencrypted statement data. + pub fn set_plain_data(&mut self, data: Vec) { + self.data = Some(data) + } + + fn encoded(&self, for_signing: bool) -> Vec { + // Encoding matches that of Vec. Basically this just means accepting that there + // will be a prefix of vector length. + let num_fields = if !for_signing && self.proof.is_some() { 1 } else { 0 } + + if self.decryption_key.is_some() { 1 } else { 0 } + + if self.priority.is_some() { 1 } else { 0 } + + if self.channel.is_some() { 1 } else { 0 } + + if self.data.is_some() { 1 } else { 0 } + + self.num_topics as u32; + + let mut output = Vec::new(); + // When encoding signature payload, the length prefix is omitted. + // This is so that the signature for encoded statement can potentially be derived without + // needing to re-encode the statement. + if !for_signing { + let compact_len = codec::Compact::(num_fields); + compact_len.encode_to(&mut output); + + if let Some(proof) = &self.proof { + 0u8.encode_to(&mut output); + proof.encode_to(&mut output); + } + } + if let Some(decryption_key) = &self.decryption_key { + 1u8.encode_to(&mut output); + decryption_key.encode_to(&mut output); + } + if let Some(priority) = &self.priority { + 2u8.encode_to(&mut output); + priority.encode_to(&mut output); + } + if let Some(channel) = &self.channel { + 3u8.encode_to(&mut output); + channel.encode_to(&mut output); + } + for t in 0..self.num_topics { + (4u8 + t).encode_to(&mut output); + self.topics[t as usize].encode_to(&mut output); + } + if let Some(data) = &self.data { + 8u8.encode_to(&mut output); + data.encode_to(&mut output); + } + output + } +} + +#[cfg(test)] +mod test { + use crate::{hash_encoded, Field, Proof, SignatureVerificationResult, Statement}; + use codec::{Decode, Encode}; + use sp_application_crypto::Pair; + + #[test] + fn statement_encoding_matches_vec() { + let mut statement = Statement::new(); + assert!(statement.proof().is_none()); + let proof = Proof::OnChain { who: [42u8; 32], block_hash: [24u8; 32], event_index: 66 }; + + let decryption_key = [0xde; 32]; + let topic1 = [0x01; 32]; + let topic2 = [0x02; 32]; + let data = vec![55, 99]; + let priority = 999; + let channel = [0xcc; 32]; + + statement.set_proof(proof.clone()); + statement.set_decryption_key(decryption_key); + statement.set_priority(priority); + statement.set_channel(channel); + statement.set_topic(0, topic1); + statement.set_topic(1, topic2); + statement.set_plain_data(data.clone()); + + statement.set_topic(5, [0x55; 32]); + assert_eq!(statement.topic(5), None); + + let fields = vec![ + Field::AuthenticityProof(proof.clone()), + Field::DecryptionKey(decryption_key), + Field::Priority(priority), + Field::Channel(channel), + Field::Topic1(topic1), + Field::Topic2(topic2), + Field::Data(data.clone()), + ]; + + let encoded = statement.encode(); + assert_eq!(statement.hash(), hash_encoded(&encoded)); + assert_eq!(encoded, fields.encode()); + + let decoded = Statement::decode(&mut encoded.as_slice()).unwrap(); + assert_eq!(decoded, statement); + } + + #[test] + fn decode_checks_fields() { + let topic1 = [0x01; 32]; + let topic2 = [0x02; 32]; + let priority = 999; + + let fields = vec![ + Field::Priority(priority), + Field::Topic1(topic1), + Field::Topic1(topic1), + Field::Topic2(topic2), + ] + .encode(); + + assert!(Statement::decode(&mut fields.as_slice()).is_err()); + + let fields = + vec![Field::Topic1(topic1), Field::Priority(priority), Field::Topic2(topic2)].encode(); + + assert!(Statement::decode(&mut fields.as_slice()).is_err()); + } + + #[test] + fn sign_and_verify() { + let mut statement = Statement::new(); + statement.set_plain_data(vec![42]); + + let sr25519_kp = sp_core::sr25519::Pair::from_string("//Alice", None).unwrap(); + let ed25519_kp = sp_core::ed25519::Pair::from_string("//Alice", None).unwrap(); + let secp256k1_kp = sp_core::ecdsa::Pair::from_string("//Alice", None).unwrap(); + + statement.sign_sr25519_private(&sr25519_kp); + assert_eq!( + statement.verify_signature(), + SignatureVerificationResult::Valid(sr25519_kp.public().0) + ); + + statement.sign_ed25519_private(&ed25519_kp); + assert_eq!( + statement.verify_signature(), + SignatureVerificationResult::Valid(ed25519_kp.public().0) + ); + + statement.sign_ecdsa_private(&secp256k1_kp); + assert_eq!( + statement.verify_signature(), + SignatureVerificationResult::Valid(sp_core::hashing::blake2_256( + &secp256k1_kp.public().0 + )) + ); + + // set an invalid signature + statement.set_proof(Proof::Sr25519 { signature: [0u8; 64], signer: [0u8; 32] }); + assert_eq!(statement.verify_signature(), SignatureVerificationResult::Invalid); + + statement.remove_proof(); + assert_eq!(statement.verify_signature(), SignatureVerificationResult::NoSignature); + } +} diff --git a/primitives/statement-store/src/runtime_api.rs b/primitives/statement-store/src/runtime_api.rs new file mode 100644 index 000000000..13f88bc97 --- /dev/null +++ b/primitives/statement-store/src/runtime_api.rs @@ -0,0 +1,187 @@ +// This file is part of Substrate. + +// Copyright (C) Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +//! Runtime support for the statement store. + +use crate::{Hash, Statement, Topic}; +use codec::{Decode, Encode}; +use scale_info::TypeInfo; +use sp_runtime::RuntimeDebug; +use sp_runtime_interface::{pass_by::PassByEnum, runtime_interface}; +use sp_std::vec::Vec; + +#[cfg(feature = "std")] +use sp_externalities::ExternalitiesExt; + +/// Information concerning a valid statement. +#[derive(Clone, PartialEq, Eq, Encode, Decode, RuntimeDebug, TypeInfo)] +pub struct ValidStatement { + /// Max statement count for this account, as calculated by the runtime. + pub max_count: u32, + /// Max total data size for this account, as calculated by the runtime. + pub max_size: u32, +} + +/// An reason for an invalid statement. +#[derive(Clone, PartialEq, Eq, Encode, Decode, Copy, RuntimeDebug, TypeInfo)] +pub enum InvalidStatement { + /// Failed proof validation. + BadProof, + /// Missing proof. + NoProof, + /// Validity could not be checked because of internal error. + InternalError, +} + +/// The source of the statement. +/// +/// Depending on the source we might apply different validation schemes. +#[derive(Copy, Clone, PartialEq, Eq, Encode, Decode, RuntimeDebug, TypeInfo)] +pub enum StatementSource { + /// Statement is coming from the on-chain worker. + Chain, + /// Statement has been received from the gossip network. + Network, + /// Statement has been submitted over the local api. + Local, +} + +impl StatementSource { + /// Check if the source allows the statement to be resubmitted to the store, extending its + /// expiration date. + pub fn can_be_resubmitted(&self) -> bool { + match self { + StatementSource::Chain | StatementSource::Local => true, + StatementSource::Network => false, + } + } +} + +sp_api::decl_runtime_apis! { + /// Runtime API trait for statement validation. + pub trait ValidateStatement { + /// Validate the statement. + fn validate_statement( + source: StatementSource, + statement: Statement, + ) -> Result; + } +} + +#[cfg(feature = "std")] +sp_externalities::decl_extension! { + /// The offchain database extension that will be registered at the Substrate externalities. + pub struct StatementStoreExt(std::sync::Arc); +} + +// Host extensions for the runtime. +#[cfg(feature = "std")] +impl StatementStoreExt { + /// Create new instance of externalities extensions. + pub fn new(store: std::sync::Arc) -> Self { + Self(store) + } +} + +/// Submission result. +#[derive(Debug, Eq, PartialEq, Clone, Copy, Encode, Decode, PassByEnum)] +pub enum SubmitResult { + /// Accepted as new. + OkNew, + /// Known statement + OkKnown, + /// Statement failed validation. + Bad, + /// The store is not available. + NotAvailable, + /// Statement could not be inserted because of priority or size checks. + Full, +} + +/// Export functions for the WASM host. +#[cfg(feature = "std")] +pub type HostFunctions = (statement_store::HostFunctions,); + +/// Host interface +#[runtime_interface] +pub trait StatementStore { + /// Submit a new new statement. The statement will be broadcast to the network. + /// This is meant to be used by the offchain worker. + fn submit_statement(&mut self, statement: Statement) -> SubmitResult { + if let Some(StatementStoreExt(store)) = self.extension::() { + match store.submit(statement, StatementSource::Chain) { + crate::SubmitResult::New(_) => SubmitResult::OkNew, + crate::SubmitResult::Known => SubmitResult::OkKnown, + crate::SubmitResult::Ignored => SubmitResult::Full, + // This should not happen for `StatementSource::Chain`. An existing statement will + // be overwritten. + crate::SubmitResult::KnownExpired => SubmitResult::Bad, + crate::SubmitResult::Bad(_) => SubmitResult::Bad, + crate::SubmitResult::InternalError(_) => SubmitResult::Bad, + } + } else { + SubmitResult::NotAvailable + } + } + + /// Return all statements. + fn statements(&mut self) -> Vec<(Hash, Statement)> { + if let Some(StatementStoreExt(store)) = self.extension::() { + store.statements().unwrap_or_default() + } else { + Vec::default() + } + } + + /// Return the data of all known statements which include all topics and have no `DecryptionKey` + /// field. + fn broadcasts(&mut self, match_all_topics: &[Topic]) -> Vec> { + if let Some(StatementStoreExt(store)) = self.extension::() { + store.broadcasts(match_all_topics).unwrap_or_default() + } else { + Vec::default() + } + } + + /// Return the data of all known statements whose decryption key is identified as `dest` (this + /// will generally be the public key or a hash thereof for symmetric ciphers, or a hash of the + /// private key for symmetric ciphers). + fn posted(&mut self, match_all_topics: &[Topic], dest: [u8; 32]) -> Vec> { + if let Some(StatementStoreExt(store)) = self.extension::() { + store.posted(match_all_topics, dest).unwrap_or_default() + } else { + Vec::default() + } + } + + /// Return the decrypted data of all known statements whose decryption key is identified as + /// `dest`. The key must be available to the client. + fn posted_clear(&mut self, match_all_topics: &[Topic], dest: [u8; 32]) -> Vec> { + if let Some(StatementStoreExt(store)) = self.extension::() { + store.posted_clear(match_all_topics, dest).unwrap_or_default() + } else { + Vec::default() + } + } + + /// Remove a statement from the store by hash. + fn remove(&mut self, hash: &Hash) { + if let Some(StatementStoreExt(store)) = self.extension::() { + store.remove(hash).unwrap_or_default() + } + } +} diff --git a/primitives/statement-store/src/store_api.rs b/primitives/statement-store/src/store_api.rs new file mode 100644 index 000000000..89daa3e96 --- /dev/null +++ b/primitives/statement-store/src/store_api.rs @@ -0,0 +1,90 @@ +// This file is part of Substrate. + +// Copyright (C) Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +pub use crate::runtime_api::StatementSource; +use crate::{Hash, Statement, Topic}; + +/// Statement store error. +#[derive(Debug, Eq, PartialEq, thiserror::Error)] +pub enum Error { + /// Database error. + #[error("Database error: {0:?}")] + Db(String), + /// Error decoding statement structure. + #[error("Error decoding statement: {0:?}")] + Decode(String), + /// Error making runtime call. + #[error("Error calling into the runtime")] + Runtime, +} + +#[derive(Debug, PartialEq, Eq)] +/// Network propagation priority. +pub enum NetworkPriority { + /// High priority. Statement should be broadcast to all peers. + High, + /// Low priority. + Low, +} + +/// Statement submission outcome +#[derive(Debug, Eq, PartialEq)] +pub enum SubmitResult { + /// Accepted as new with given score + New(NetworkPriority), + /// Known statement + Known, + /// Known statement that's already expired. + KnownExpired, + /// Priority is too low or the size is too big. + Ignored, + /// Statement failed validation. + Bad(&'static str), + /// Internal store error. + InternalError(Error), +} + +/// Result type for `Error` +pub type Result = std::result::Result; + +/// Statement store API. +pub trait StatementStore: Send + Sync { + /// Return all statements. + fn statements(&self) -> Result>; + + /// Get statement by hash. + fn statement(&self, hash: &Hash) -> Result>; + + /// Return the data of all known statements which include all topics and have no `DecryptionKey` + /// field. + fn broadcasts(&self, match_all_topics: &[Topic]) -> Result>>; + + /// Return the data of all known statements whose decryption key is identified as `dest` (this + /// will generally be the public key or a hash thereof for symmetric ciphers, or a hash of the + /// private key for symmetric ciphers). + fn posted(&self, match_all_topics: &[Topic], dest: [u8; 32]) -> Result>>; + + /// Return the decrypted data of all known statements whose decryption key is identified as + /// `dest`. The key must be available to the client. + fn posted_clear(&self, match_all_topics: &[Topic], dest: [u8; 32]) -> Result>>; + + /// Submit a statement. + fn submit(&self, statement: Statement, source: StatementSource) -> SubmitResult; + + /// Remove a statement from the store. + fn remove(&self, hash: &Hash) -> Result<()>; +} diff --git a/scripts/ci/deny.toml b/scripts/ci/deny.toml index f93287593..91822c831 100644 --- a/scripts/ci/deny.toml +++ b/scripts/ci/deny.toml @@ -75,6 +75,7 @@ exceptions = [ { allow = ["GPL-3.0 WITH Classpath-exception-2.0"], name = "sc-network-sync" }, { allow = ["GPL-3.0 WITH Classpath-exception-2.0"], name = "sc-network-test" }, { allow = ["GPL-3.0 WITH Classpath-exception-2.0"], name = "sc-network-transactions" }, + { allow = ["GPL-3.0 WITH Classpath-exception-2.0"], name = "sc-network-statement" }, { allow = ["GPL-3.0 WITH Classpath-exception-2.0"], name = "sc-offchain" }, { allow = ["GPL-3.0 WITH Classpath-exception-2.0"], name = "sc-peerset" }, { allow = ["GPL-3.0 WITH Classpath-exception-2.0"], name = "sc-proposer-metrics" }, @@ -86,6 +87,7 @@ exceptions = [ { allow = ["GPL-3.0 WITH Classpath-exception-2.0"], name = "sc-service" }, { allow = ["GPL-3.0 WITH Classpath-exception-2.0"], name = "sc-service-test" }, { allow = ["GPL-3.0 WITH Classpath-exception-2.0"], name = "sc-state-db" }, + { allow = ["GPL-3.0 WITH Classpath-exception-2.0"], name = "sc-statement-store" }, { allow = ["GPL-3.0 WITH Classpath-exception-2.0"], name = "sc-storage-monitor" }, { allow = ["GPL-3.0 WITH Classpath-exception-2.0"], name = "sc-sysinfo" }, { allow = ["GPL-3.0 WITH Classpath-exception-2.0"], name = "sc-telemetry" }, From 82f783d902a11e4bf05a02acd07a70ba6d834d8c Mon Sep 17 00:00:00 2001 From: Vladimir Istyufeev Date: Thu, 4 May 2023 14:32:16 +0400 Subject: [PATCH 453/558] CI: `cargo-check-benches`: don't merge in `master` if base ref isn't `master` (#14071) --- scripts/ci/gitlab/pipeline/test.yml | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/scripts/ci/gitlab/pipeline/test.yml b/scripts/ci/gitlab/pipeline/test.yml index 82f024d35..ecfcf9ff3 100644 --- a/scripts/ci/gitlab/pipeline/test.yml +++ b/scripts/ci/gitlab/pipeline/test.yml @@ -88,13 +88,17 @@ cargo-check-benches: - !reference [.job-switcher, before_script] - !reference [.rusty-cachier, before_script] - !reference [.pipeline-stopper-vars, script] - # merges in the master branch on PRs + # merges in the master branch on PRs. skip if base is not master - 'if [ $CI_COMMIT_REF_NAME != "master" ]; then - BASE=$(curl -s -H "Authorization: Bearer ${GITHUB_PR_TOKEN}" https://api.github.com/repos/paritytech/substrate/pulls/${CI_COMMIT_REF_NAME} | jq -r .base.ref); - printf "Merging base branch %s\n" "${BASE:=master}"; - git config user.email "ci@gitlab.parity.io"; - git fetch origin "refs/heads/${BASE}"; - git merge --verbose --no-edit FETCH_HEAD; + BASE=$(curl -s -H "Authorization: Bearer ${GITHUB_PR_TOKEN}" https://api.github.com/repos/paritytech/substrate/pulls/${CI_COMMIT_REF_NAME} | jq -r .base.ref); + printf "Merging base branch %s\n" "${BASE:=master}"; + if [ $BASE != "master" ]; then + echo "$BASE is not master, skipping merge"; + else + git config user.email "ci@gitlab.parity.io"; + git fetch origin "refs/heads/${BASE}"; + git merge --verbose --no-edit FETCH_HEAD; + fi fi' parallel: 2 script: From 7d6084b5774ee13d0b33810c2830aa5a8506cbae Mon Sep 17 00:00:00 2001 From: Davide Galassi Date: Thu, 4 May 2023 15:41:59 +0200 Subject: [PATCH 454/558] Refinements to VRF types (#14036) * Allow extra signing data * Fix tests after renaming * Rename VrfSecret/VrfVerifier to VrfSecret/VrfPublic * Further encrapsulation of 'transcript' type to the sr25519 implementation * Keystore sr25519 pre-output * Leave additional custom input field hidden in the associated VrfInput type * Fix test * More ergonomic output_bytes * Trigger pipeline * Define a separated type for vrf signature data * Fix docs * Fix doc * Remove annotation * Directly use dleq_proove and dleq_verify in sr25519 * Trigger CI * Remove cruft before merge --- client/consensus/babe/src/authorship.rs | 14 +- client/consensus/babe/src/tests.rs | 30 +-- client/consensus/babe/src/verification.rs | 16 +- client/keystore/src/local.rs | 35 ++- frame/babe/src/lib.rs | 14 +- frame/babe/src/mock.rs | 13 +- frame/babe/src/tests.rs | 9 +- primitives/consensus/babe/src/lib.rs | 15 +- primitives/core/src/crypto.rs | 28 ++- primitives/core/src/sr25519.rs | 254 ++++++++++++++++++---- primitives/keystore/src/lib.rs | 21 +- primitives/keystore/src/testing.rs | 69 +++++- 12 files changed, 382 insertions(+), 136 deletions(-) diff --git a/client/consensus/babe/src/authorship.rs b/client/consensus/babe/src/authorship.rs index dc7cd1086..758d5321a 100644 --- a/client/consensus/babe/src/authorship.rs +++ b/client/consensus/babe/src/authorship.rs @@ -24,7 +24,7 @@ use sc_consensus_epochs::Epoch as EpochT; use sp_application_crypto::AppCrypto; use sp_consensus_babe::{ digests::{PreDigest, PrimaryPreDigest, SecondaryPlainPreDigest, SecondaryVRFPreDigest}, - make_transcript, AuthorityId, BabeAuthorityWeight, Randomness, Slot, + make_vrf_sign_data, AuthorityId, BabeAuthorityWeight, Randomness, Slot, }; use sp_core::{ blake2_256, @@ -148,9 +148,9 @@ fn claim_secondary_slot( for (authority_id, authority_index) in keys { if authority_id == expected_author { let pre_digest = if author_secondary_vrf { - let transcript = make_transcript(randomness, slot, epoch_index); + let data = make_vrf_sign_data(randomness, slot, epoch_index); let result = - keystore.sr25519_vrf_sign(AuthorityId::ID, authority_id.as_ref(), &transcript); + keystore.sr25519_vrf_sign(AuthorityId::ID, authority_id.as_ref(), &data); if let Ok(Some(vrf_signature)) = result { Some(PreDigest::SecondaryVRF(SecondaryVRFPreDigest { slot, @@ -239,18 +239,18 @@ fn claim_primary_slot( epoch_index = epoch.clone_for_slot(slot).epoch_index; } - let transcript = make_transcript(randomness, slot, epoch_index); + let data = make_vrf_sign_data(randomness, slot, epoch_index); for (authority_id, authority_index) in keys { - let result = keystore.sr25519_vrf_sign(AuthorityId::ID, authority_id.as_ref(), &transcript); + let result = keystore.sr25519_vrf_sign(AuthorityId::ID, authority_id.as_ref(), &data); if let Ok(Some(vrf_signature)) = result { let threshold = calculate_primary_threshold(c, authorities, *authority_index); let can_claim = authority_id .as_inner_ref() - .make_bytes::<[u8; AUTHORING_SCORE_LENGTH]>( + .make_bytes::( AUTHORING_SCORE_VRF_CONTEXT, - &transcript, + &data.as_ref(), &vrf_signature.output, ) .map(|bytes| u128::from_le_bytes(bytes) < threshold) diff --git a/client/consensus/babe/src/tests.rs b/client/consensus/babe/src/tests.rs index 740ce6367..aa3e32fa0 100644 --- a/client/consensus/babe/src/tests.rs +++ b/client/consensus/babe/src/tests.rs @@ -29,7 +29,7 @@ use sc_network_test::{Block as TestBlock, *}; use sp_application_crypto::key_types::BABE; use sp_consensus::{DisableProofRecording, NoNetwork as DummyOracle, Proposal}; use sp_consensus_babe::{ - inherents::InherentDataProvider, make_transcript, AllowedSlots, AuthorityId, AuthorityPair, + inherents::InherentDataProvider, make_vrf_sign_data, AllowedSlots, AuthorityId, AuthorityPair, Slot, }; use sp_consensus_slots::SlotDuration; @@ -630,11 +630,8 @@ fn claim_vrf_check() { PreDigest::Primary(d) => d, v => panic!("Unexpected pre-digest variant {:?}", v), }; - let transcript = make_transcript(&epoch.randomness.clone(), 0.into(), epoch.epoch_index); - let sign = keystore - .sr25519_vrf_sign(AuthorityId::ID, &public, &transcript) - .unwrap() - .unwrap(); + let data = make_vrf_sign_data(&epoch.randomness.clone(), 0.into(), epoch.epoch_index); + let sign = keystore.sr25519_vrf_sign(AuthorityId::ID, &public, &data).unwrap().unwrap(); assert_eq!(pre_digest.vrf_signature.output, sign.output); // We expect a SecondaryVRF claim for slot 1 @@ -642,11 +639,8 @@ fn claim_vrf_check() { PreDigest::SecondaryVRF(d) => d, v => panic!("Unexpected pre-digest variant {:?}", v), }; - let transcript = make_transcript(&epoch.randomness.clone(), 1.into(), epoch.epoch_index); - let sign = keystore - .sr25519_vrf_sign(AuthorityId::ID, &public, &transcript) - .unwrap() - .unwrap(); + let data = make_vrf_sign_data(&epoch.randomness.clone(), 1.into(), epoch.epoch_index); + let sign = keystore.sr25519_vrf_sign(AuthorityId::ID, &public, &data).unwrap().unwrap(); assert_eq!(pre_digest.vrf_signature.output, sign.output); // Check that correct epoch index has been used if epochs are skipped (primary VRF) @@ -656,11 +650,8 @@ fn claim_vrf_check() { v => panic!("Unexpected claim variant {:?}", v), }; let fixed_epoch = epoch.clone_for_slot(slot); - let transcript = make_transcript(&epoch.randomness.clone(), slot, fixed_epoch.epoch_index); - let sign = keystore - .sr25519_vrf_sign(AuthorityId::ID, &public, &transcript) - .unwrap() - .unwrap(); + let data = make_vrf_sign_data(&epoch.randomness.clone(), slot, fixed_epoch.epoch_index); + let sign = keystore.sr25519_vrf_sign(AuthorityId::ID, &public, &data).unwrap().unwrap(); assert_eq!(fixed_epoch.epoch_index, 11); assert_eq!(claim.vrf_signature.output, sign.output); @@ -671,11 +662,8 @@ fn claim_vrf_check() { v => panic!("Unexpected claim variant {:?}", v), }; let fixed_epoch = epoch.clone_for_slot(slot); - let transcript = make_transcript(&epoch.randomness.clone(), slot, fixed_epoch.epoch_index); - let sign = keystore - .sr25519_vrf_sign(AuthorityId::ID, &public, &transcript) - .unwrap() - .unwrap(); + let data = make_vrf_sign_data(&epoch.randomness.clone(), slot, fixed_epoch.epoch_index); + let sign = keystore.sr25519_vrf_sign(AuthorityId::ID, &public, &data).unwrap().unwrap(); assert_eq!(fixed_epoch.epoch_index, 11); assert_eq!(pre_digest.vrf_signature.output, sign.output); } diff --git a/client/consensus/babe/src/verification.rs b/client/consensus/babe/src/verification.rs index cadceb6a5..3de5eacc2 100644 --- a/client/consensus/babe/src/verification.rs +++ b/client/consensus/babe/src/verification.rs @@ -30,11 +30,11 @@ use sp_consensus_babe::{ CompatibleDigestItem, PreDigest, PrimaryPreDigest, SecondaryPlainPreDigest, SecondaryVRFPreDigest, }, - make_transcript, AuthorityId, AuthorityPair, AuthoritySignature, + make_vrf_sign_data, AuthorityId, AuthorityPair, AuthoritySignature, }; use sp_consensus_slots::Slot; use sp_core::{ - crypto::{VrfVerifier, Wraps}, + crypto::{VrfPublic, Wraps}, Pair, }; use sp_runtime::{traits::Header, DigestItem}; @@ -171,9 +171,9 @@ fn check_primary_header( return Err(babe_err(Error::BadSignature(pre_hash))) } - let transcript = make_transcript(&epoch.randomness, pre_digest.slot, epoch_index); + let data = make_vrf_sign_data(&epoch.randomness, pre_digest.slot, epoch_index); - if !authority_id.as_inner_ref().vrf_verify(&transcript, &pre_digest.vrf_signature) { + if !authority_id.as_inner_ref().vrf_verify(&data, &pre_digest.vrf_signature) { return Err(babe_err(Error::VrfVerificationFailed)) } @@ -182,9 +182,9 @@ fn check_primary_header( let score = authority_id .as_inner_ref() - .make_bytes::<[u8; AUTHORING_SCORE_LENGTH]>( + .make_bytes::( AUTHORING_SCORE_VRF_CONTEXT, - &transcript, + &data.as_ref(), &pre_digest.vrf_signature.output, ) .map(u128::from_le_bytes) @@ -253,9 +253,9 @@ fn check_secondary_vrf_header( return Err(Error::BadSignature(pre_hash)) } - let transcript = make_transcript(&epoch.randomness, pre_digest.slot, epoch_index); + let data = make_vrf_sign_data(&epoch.randomness, pre_digest.slot, epoch_index); - if !author.as_inner_ref().vrf_verify(&transcript, &pre_digest.vrf_signature) { + if !author.as_inner_ref().vrf_verify(&data, &pre_digest.vrf_signature) { return Err(Error::VrfVerificationFailed) } diff --git a/client/keystore/src/local.rs b/client/keystore/src/local.rs index 3551623f3..1e785113a 100644 --- a/client/keystore/src/local.rs +++ b/client/keystore/src/local.rs @@ -20,7 +20,7 @@ use parking_lot::RwLock; use sp_application_crypto::{AppCrypto, AppPair, IsWrappedBy}; use sp_core::{ - crypto::{ByteArray, ExposeSecret, KeyTypeId, Pair as CorePair, SecretString, VrfSigner}, + crypto::{ByteArray, ExposeSecret, KeyTypeId, Pair as CorePair, SecretString, VrfSecret}, ecdsa, ed25519, sr25519, }; use sp_keystore::{Error as TraitError, Keystore, KeystorePtr}; @@ -98,19 +98,33 @@ impl LocalKeystore { Ok(signature) } - fn vrf_sign( + fn vrf_sign( &self, key_type: KeyTypeId, public: &T::Public, - transcript: &T::VrfInput, + data: &T::VrfSignData, ) -> std::result::Result, TraitError> { let sig = self .0 .read() .key_pair_by_type::(public, key_type)? - .map(|pair| pair.vrf_sign(transcript)); + .map(|pair| pair.vrf_sign(data)); Ok(sig) } + + fn vrf_output( + &self, + key_type: KeyTypeId, + public: &T::Public, + input: &T::VrfInput, + ) -> std::result::Result, TraitError> { + let preout = self + .0 + .read() + .key_pair_by_type::(public, key_type)? + .map(|pair| pair.vrf_output(input)); + Ok(preout) + } } impl Keystore for LocalKeystore { @@ -142,9 +156,18 @@ impl Keystore for LocalKeystore { &self, key_type: KeyTypeId, public: &sr25519::Public, - transcript: &sr25519::vrf::VrfTranscript, + data: &sr25519::vrf::VrfSignData, ) -> std::result::Result, TraitError> { - self.vrf_sign::(key_type, public, transcript) + self.vrf_sign::(key_type, public, data) + } + + fn sr25519_vrf_output( + &self, + key_type: KeyTypeId, + public: &sr25519::Public, + input: &sr25519::vrf::VrfInput, + ) -> std::result::Result, TraitError> { + self.vrf_output::(key_type, public, input) } fn ed25519_public_keys(&self, key_type: KeyTypeId) -> Vec { diff --git a/frame/babe/src/lib.rs b/frame/babe/src/lib.rs index e3ead424e..15580457c 100644 --- a/frame/babe/src/lib.rs +++ b/frame/babe/src/lib.rs @@ -357,12 +357,12 @@ pub mod pallet { ); } - if let Some(vrf_signature) = pre_digest.vrf_signature() { + if let Some(signature) = pre_digest.vrf_signature() { let randomness: Option = Authorities::::get() .get(authority_index as usize) .and_then(|(authority, _)| { let public = authority.as_inner_ref(); - let transcript = sp_consensus_babe::make_transcript( + let transcript = sp_consensus_babe::make_vrf_transcript( &Self::randomness(), CurrentSlot::::get(), EpochIndex::::get(), @@ -372,16 +372,12 @@ pub mod pallet { // execution. We don't run the verification again here to avoid slowing // down the runtime. debug_assert!({ - use sp_core::crypto::VrfVerifier; - public.vrf_verify(&transcript, &vrf_signature) + use sp_core::crypto::VrfPublic; + public.vrf_verify(&transcript.clone().into_sign_data(), &signature) }); public - .make_bytes( - RANDOMNESS_VRF_CONTEXT, - &transcript, - &vrf_signature.output, - ) + .make_bytes(RANDOMNESS_VRF_CONTEXT, &transcript, &signature.output) .ok() }); diff --git a/frame/babe/src/mock.rs b/frame/babe/src/mock.rs index bccdb42c7..96ebd818b 100644 --- a/frame/babe/src/mock.rs +++ b/frame/babe/src/mock.rs @@ -27,7 +27,7 @@ use frame_support::{ use pallet_session::historical as pallet_session_historical; use sp_consensus_babe::{AuthorityId, AuthorityPair, Randomness, Slot, VrfSignature}; use sp_core::{ - crypto::{KeyTypeId, Pair, VrfSigner}, + crypto::{KeyTypeId, Pair, VrfSecret}, H256, U256, }; use sp_io; @@ -314,17 +314,16 @@ pub fn make_secondary_vrf_pre_digest( Digest { logs: vec![log] } } -pub fn make_vrf_output( +pub fn make_vrf_signature_and_randomness( slot: Slot, pair: &sp_consensus_babe::AuthorityPair, ) -> (VrfSignature, Randomness) { - let transcript = sp_consensus_babe::make_transcript(&Babe::randomness(), slot, 0); + let transcript = sp_consensus_babe::make_vrf_transcript(&Babe::randomness(), slot, 0); - let signature = pair.as_ref().vrf_sign(&transcript); + let randomness = + pair.as_ref().make_bytes(sp_consensus_babe::RANDOMNESS_VRF_CONTEXT, &transcript); - let randomness = pair - .as_ref() - .make_bytes::(sp_consensus_babe::RANDOMNESS_VRF_CONTEXT, &transcript); + let signature = pair.as_ref().vrf_sign(&transcript.into()); (signature, randomness) } diff --git a/frame/babe/src/tests.rs b/frame/babe/src/tests.rs index 8a9aa6fcc..38edc2af7 100644 --- a/frame/babe/src/tests.rs +++ b/frame/babe/src/tests.rs @@ -63,7 +63,8 @@ fn first_block_epoch_zero_start() { ext.execute_with(|| { let genesis_slot = Slot::from(100); - let (vrf_signature, vrf_randomness) = make_vrf_output(genesis_slot, &pairs[0]); + let (vrf_signature, vrf_randomness) = + make_vrf_signature_and_randomness(genesis_slot, &pairs[0]); let pre_digest = make_primary_pre_digest(0, genesis_slot, vrf_signature); @@ -111,7 +112,8 @@ fn current_slot_is_processed_on_initialization() { ext.execute_with(|| { let genesis_slot = Slot::from(10); - let (vrf_signature, vrf_randomness) = make_vrf_output(genesis_slot, &pairs[0]); + let (vrf_signature, vrf_randomness) = + make_vrf_signature_and_randomness(genesis_slot, &pairs[0]); let pre_digest = make_primary_pre_digest(0, genesis_slot, vrf_signature); System::reset_events(); @@ -140,7 +142,8 @@ where ext.execute_with(|| { let genesis_slot = Slot::from(10); - let (vrf_signature, vrf_randomness) = make_vrf_output(genesis_slot, &pairs[0]); + let (vrf_signature, vrf_randomness) = + make_vrf_signature_and_randomness(genesis_slot, &pairs[0]); let pre_digest = make_pre_digest(0, genesis_slot, vrf_signature); System::reset_events(); diff --git a/primitives/consensus/babe/src/lib.rs b/primitives/consensus/babe/src/lib.rs index 37cf0c3f0..dc161525a 100644 --- a/primitives/consensus/babe/src/lib.rs +++ b/primitives/consensus/babe/src/lib.rs @@ -32,7 +32,9 @@ use sp_std::vec::Vec; use crate::digests::{NextConfigDescriptor, NextEpochDescriptor}; -pub use sp_core::sr25519::vrf::{VrfOutput, VrfProof, VrfSignature, VrfTranscript}; +pub use sp_core::sr25519::vrf::{ + VrfInput, VrfOutput, VrfProof, VrfSignData, VrfSignature, VrfTranscript, +}; /// Key type for BABE module. pub const KEY_TYPE: sp_core::crypto::KeyTypeId = sp_application_crypto::key_types::BABE; @@ -94,9 +96,9 @@ pub type BabeAuthorityWeight = u64; /// of 0 (regardless of whether they are plain or vrf secondary blocks). pub type BabeBlockWeight = u32; -/// Make a VRF transcript data container -pub fn make_transcript(randomness: &Randomness, slot: Slot, epoch: u64) -> VrfTranscript { - VrfTranscript::new( +/// Make VRF input suitable for BABE's randomness generation. +pub fn make_vrf_transcript(randomness: &Randomness, slot: Slot, epoch: u64) -> VrfInput { + VrfInput::new( &BABE_ENGINE_ID, &[ (b"slot number", &slot.to_le_bytes()), @@ -106,6 +108,11 @@ pub fn make_transcript(randomness: &Randomness, slot: Slot, epoch: u64) -> VrfTr ) } +/// Make VRF signing data suitable for BABE's protocol. +pub fn make_vrf_sign_data(randomness: &Randomness, slot: Slot, epoch: u64) -> VrfSignData { + make_vrf_transcript(randomness, slot, epoch).into() +} + /// An consensus log item for BABE. #[derive(Decode, Encode, Clone, PartialEq, Eq)] pub enum ConsensusLog { diff --git a/primitives/core/src/crypto.rs b/primitives/core/src/crypto.rs index 9d63cbc51..27b24057b 100644 --- a/primitives/core/src/crypto.rs +++ b/primitives/core/src/crypto.rs @@ -1094,23 +1094,29 @@ impl<'a> TryFrom<&'a str> for KeyTypeId { /// Trait grouping types shared by a VRF signer and verifiers. pub trait VrfCrypto { - /// Associated signature type. - type VrfSignature; - - /// Vrf input data. Generally some form of transcript. + /// VRF input. type VrfInput; + /// VRF output. + type VrfOutput; + /// VRF signing data. + type VrfSignData; + /// VRF signature. + type VrfSignature; } -/// VRF Signer. -pub trait VrfSigner: VrfCrypto { - /// Sign input data. - fn vrf_sign(&self, data: &Self::VrfInput) -> Self::VrfSignature; +/// VRF Secret Key. +pub trait VrfSecret: VrfCrypto { + /// Get VRF-specific output . + fn vrf_output(&self, data: &Self::VrfInput) -> Self::VrfOutput; + + /// Sign VRF-specific data. + fn vrf_sign(&self, input: &Self::VrfSignData) -> Self::VrfSignature; } -/// VRF Verifier. -pub trait VrfVerifier: VrfCrypto { +/// VRF Public Key. +pub trait VrfPublic: VrfCrypto { /// Verify input data signature. - fn vrf_verify(&self, data: &Self::VrfInput, signature: &Self::VrfSignature) -> bool; + fn vrf_verify(&self, data: &Self::VrfSignData, signature: &Self::VrfSignature) -> bool; } /// An identifier for a specific cryptographic algorithm used by a key pair diff --git a/primitives/core/src/sr25519.rs b/primitives/core/src/sr25519.rs index 9b1c82205..fead9e2a0 100644 --- a/primitives/core/src/sr25519.rs +++ b/primitives/core/src/sr25519.rs @@ -537,32 +537,86 @@ impl CryptoType for Pair { pub mod vrf { use super::*; #[cfg(feature = "full_crypto")] - use crate::crypto::VrfSigner; - use crate::crypto::{VrfCrypto, VrfVerifier}; + use crate::crypto::VrfSecret; + use crate::crypto::{VrfCrypto, VrfPublic}; use schnorrkel::{ errors::MultiSignatureStage, vrf::{VRF_OUTPUT_LENGTH, VRF_PROOF_LENGTH}, SignatureError, }; - /// VRF transcript ready to be used for VRF sign/verify operations. + const DEFAULT_EXTRA_DATA_LABEL: &[u8] = b"VRF"; + + /// Transcript ready to be used for VRF related operations. + #[derive(Clone)] pub struct VrfTranscript(pub merlin::Transcript); impl VrfTranscript { - /// Build a new transcript ready to be used by a VRF signer/verifier. + /// Build a new transcript instance. + /// + /// Each `data` element is a tuple `(domain, message)` composing the transcipt. pub fn new(label: &'static [u8], data: &[(&'static [u8], &[u8])]) -> Self { let mut transcript = merlin::Transcript::new(label); data.iter().for_each(|(l, b)| transcript.append_message(l, b)); VrfTranscript(transcript) } + + /// Map transcript to `VrfSignData`. + pub fn into_sign_data(self) -> VrfSignData { + self.into() + } + } + + /// VRF input. + /// + /// Technically a transcript used by the Fiat-Shamir transform. + pub type VrfInput = VrfTranscript; + + /// VRF input ready to be used for VRF sign and verify operations. + #[derive(Clone)] + pub struct VrfSignData { + /// Transcript data contributing to VRF output. + pub(super) transcript: VrfTranscript, + /// Extra transcript data to be signed by the VRF. + pub(super) extra: Option, + } + + impl From for VrfSignData { + fn from(transcript: VrfInput) -> Self { + VrfSignData { transcript, extra: None } + } + } + + // Get a reference to the inner VRF input. + impl AsRef for VrfSignData { + fn as_ref(&self) -> &VrfInput { + &self.transcript + } + } + + impl VrfSignData { + /// Build a new instance ready to be used for VRF signer and verifier. + /// + /// `input` will contribute to the VRF output bytes. + pub fn new(input: VrfTranscript) -> Self { + input.into() + } + + /// Add some extra data to be signed. + /// + /// `extra` will not contribute to the VRF output bytes. + pub fn with_extra(mut self, extra: VrfTranscript) -> Self { + self.extra = Some(extra); + self + } } /// VRF signature data #[derive(Clone, Debug, PartialEq, Eq, Encode, Decode, MaxEncodedLen, TypeInfo)] pub struct VrfSignature { - /// The initial VRF configuration + /// VRF output. pub output: VrfOutput, - /// The calculated VRF proof + /// VRF proof. pub proof: VrfProof, } @@ -630,30 +684,58 @@ pub mod vrf { #[cfg(feature = "full_crypto")] impl VrfCrypto for Pair { - type VrfSignature = VrfSignature; type VrfInput = VrfTranscript; + type VrfOutput = VrfOutput; + type VrfSignData = VrfSignData; + type VrfSignature = VrfSignature; } #[cfg(feature = "full_crypto")] - impl VrfSigner for Pair { - fn vrf_sign(&self, transcript: &Self::VrfInput) -> Self::VrfSignature { - let (inout, proof, _) = self.0.vrf_sign(transcript.0.clone()); + impl VrfSecret for Pair { + fn vrf_sign(&self, data: &Self::VrfSignData) -> Self::VrfSignature { + let inout = self.0.vrf_create_hash(data.transcript.0.clone()); + + let extra = data + .extra + .as_ref() + .map(|e| e.0.clone()) + .unwrap_or_else(|| merlin::Transcript::new(DEFAULT_EXTRA_DATA_LABEL)); + + let proof = self.0.dleq_proove(extra, &inout, true).0; + VrfSignature { output: VrfOutput(inout.to_output()), proof: VrfProof(proof) } } + + fn vrf_output(&self, input: &Self::VrfInput) -> Self::VrfOutput { + let output = self.0.vrf_create_hash(input.0.clone()).to_output(); + VrfOutput(output) + } } impl VrfCrypto for Public { - type VrfSignature = VrfSignature; type VrfInput = VrfTranscript; + type VrfOutput = VrfOutput; + type VrfSignData = VrfSignData; + type VrfSignature = VrfSignature; } - impl VrfVerifier for Public { - fn vrf_verify(&self, transcript: &Self::VrfInput, signature: &Self::VrfSignature) -> bool { - schnorrkel::PublicKey::from_bytes(self) - .and_then(|public| { - public.vrf_verify(transcript.0.clone(), &signature.output.0, &signature.proof.0) - }) - .is_ok() + impl VrfPublic for Public { + fn vrf_verify(&self, data: &Self::VrfSignData, signature: &Self::VrfSignature) -> bool { + let do_verify = || { + let public = schnorrkel::PublicKey::from_bytes(self)?; + + let inout = + signature.output.0.attach_input_hash(&public, data.transcript.0.clone())?; + + let extra = data + .extra + .as_ref() + .map(|e| e.0.clone()) + .unwrap_or_else(|| merlin::Transcript::new(DEFAULT_EXTRA_DATA_LABEL)); + + public.dleq_verify(extra, &inout, &signature.proof.0, true) + }; + do_verify().is_ok() } } @@ -690,39 +772,54 @@ pub mod vrf { #[cfg(feature = "full_crypto")] impl Pair { - /// Generate bytes from the given VRF configuration. - pub fn make_bytes>( - &self, - context: &[u8], - transcript: &VrfTranscript, - ) -> B { - let inout = self.0.vrf_create_hash(transcript.0.clone()); - inout.make_bytes::(context) + /// Generate output bytes from the given VRF configuration. + pub fn make_bytes(&self, context: &[u8], input: &VrfInput) -> [u8; N] + where + [u8; N]: Default, + { + let inout = self.0.vrf_create_hash(input.0.clone()); + inout.make_bytes::<[u8; N]>(context) } } impl Public { - /// Generate bytes from the given VRF configuration. - pub fn make_bytes>( + /// Generate output bytes from the given VRF configuration. + pub fn make_bytes( &self, context: &[u8], - transcript: &VrfTranscript, + input: &VrfInput, output: &VrfOutput, - ) -> Result { + ) -> Result<[u8; N], codec::Error> + where + [u8; N]: Default, + { let pubkey = schnorrkel::PublicKey::from_bytes(&self.0).map_err(convert_error)?; - let inout = output - .0 - .attach_input_hash(&pubkey, transcript.0.clone()) - .map_err(convert_error)?; - Ok(inout.make_bytes::(context)) + let inout = + output.0.attach_input_hash(&pubkey, input.0.clone()).map_err(convert_error)?; + Ok(inout.make_bytes::<[u8; N]>(context)) + } + } + + impl VrfOutput { + /// Generate output bytes from the given VRF configuration. + pub fn make_bytes( + &self, + context: &[u8], + input: &VrfInput, + public: &Public, + ) -> Result<[u8; N], codec::Error> + where + [u8; N]: Default, + { + public.make_bytes(context, input, self) } } } #[cfg(test)] mod tests { - use super::*; - use crate::crypto::{Ss58Codec, DEV_ADDRESS, DEV_PHRASE}; + use super::{vrf::*, *}; + use crate::crypto::{Ss58Codec, VrfPublic, VrfSecret, DEV_ADDRESS, DEV_PHRASE}; use serde_json; #[test] @@ -951,19 +1048,86 @@ mod tests { assert!(deserialize_signature("\"abc123\"").is_err()); } + #[test] + fn vrf_sign_verify() { + let pair = Pair::from_seed(b"12345678901234567890123456789012"); + let public = pair.public(); + + let data = VrfTranscript::new(b"label", &[(b"domain1", b"data1")]).into(); + + let signature = pair.vrf_sign(&data); + + assert!(public.vrf_verify(&data, &signature)); + } + + #[test] + fn vrf_sign_verify_with_extra() { + let pair = Pair::from_seed(b"12345678901234567890123456789012"); + let public = pair.public(); + + let extra = VrfTranscript::new(b"extra", &[(b"domain2", b"data2")]); + let data = VrfTranscript::new(b"label", &[(b"domain1", b"data1")]) + .into_sign_data() + .with_extra(extra); + + let signature = pair.vrf_sign(&data); + + assert!(public.vrf_verify(&data, &signature)); + } + #[test] fn vrf_make_bytes_matches() { - use super::vrf::*; - use crate::crypto::VrfSigner; let pair = Pair::from_seed(b"12345678901234567890123456789012"); let public = pair.public(); - let transcript = VrfTranscript::new(b"test", &[(b"foo", b"bar")]); + let ctx = b"vrfbytes"; + + let input = VrfTranscript::new(b"label", &[(b"domain1", b"data1")]); - let signature = pair.vrf_sign(&transcript); + let output = pair.vrf_output(&input); - let ctx = b"randbytes"; - let b1 = pair.make_bytes::<[u8; 32]>(ctx, &transcript); - let b2 = public.make_bytes::<[u8; 32]>(ctx, &transcript, &signature.output).unwrap(); - assert_eq!(b1, b2); + let out1 = pair.make_bytes::<32>(ctx, &input); + let out2 = output.make_bytes::<32>(ctx, &input, &public).unwrap(); + assert_eq!(out1, out2); + + let extra = VrfTranscript::new(b"extra", &[(b"domain2", b"data2")]); + let data = input.clone().into_sign_data().with_extra(extra); + let signature = pair.vrf_sign(&data); + assert!(public.vrf_verify(&data, &signature)); + + let out3 = public.make_bytes::<32>(ctx, &input, &signature.output).unwrap(); + assert_eq!(out2, out3); + } + + #[test] + fn vrf_backend_compat() { + let pair = Pair::from_seed(b"12345678901234567890123456789012"); + let public = pair.public(); + let ctx = b"vrfbytes"; + + let input = VrfInput::new(b"label", &[(b"domain1", b"data1")]); + let extra = VrfTranscript::new(b"extra", &[(b"domain2", b"data2")]); + + let data = input.clone().into_sign_data().with_extra(extra.clone()); + let signature = pair.vrf_sign(&data); + assert!(public.vrf_verify(&data, &signature)); + + let out1 = pair.make_bytes::<32>(ctx, &input); + let out2 = public.make_bytes::<32>(ctx, &input, &signature.output).unwrap(); + assert_eq!(out1, out2); + + // Direct call to backend version of sign after check with extra params + let (inout, proof, _) = pair + .0 + .vrf_sign_extra_after_check(input.0.clone(), |inout| { + let out3 = inout.make_bytes::<[u8; 32]>(ctx); + assert_eq!(out2, out3); + Some(extra.0.clone()) + }) + .unwrap(); + let signature2 = + VrfSignature { output: VrfOutput(inout.to_output()), proof: VrfProof(proof) }; + + assert!(public.vrf_verify(&data, &signature2)); + assert_eq!(signature.output, signature2.output); } } diff --git a/primitives/keystore/src/lib.rs b/primitives/keystore/src/lib.rs index 5b41f3b80..d6165088f 100644 --- a/primitives/keystore/src/lib.rs +++ b/primitives/keystore/src/lib.rs @@ -72,23 +72,34 @@ pub trait Keystore: Send + Sync { msg: &[u8], ) -> Result, Error>; - /// Generate an sr25519 VRF signature for a given transcript data. + /// Generate an sr25519 VRF signature for the given data. /// /// Receives [`KeyTypeId`] and an [`sr25519::Public`] key to be able to map /// them to a private key that exists in the keystore. /// - /// Returns a result containing the signature data. - /// Namely, VRFOutput and VRFProof which are returned inside the `VRFSignature` - /// container struct. /// Returns `None` if the given `key_type` and `public` combination doesn't /// exist in the keystore or an `Err` when something failed. fn sr25519_vrf_sign( &self, key_type: KeyTypeId, public: &sr25519::Public, - transcript: &sr25519::vrf::VrfTranscript, + data: &sr25519::vrf::VrfSignData, ) -> Result, Error>; + /// Generate an sr25519 VRF output for a given input data. + /// + /// Receives [`KeyTypeId`] and an [`sr25519::Public`] key to be able to map + /// them to a private key that exists in the keystore. + /// + /// Returns `None` if the given `key_type` and `public` combination doesn't + /// exist in the keystore or an `Err` when something failed. + fn sr25519_vrf_output( + &self, + key_type: KeyTypeId, + public: &sr25519::Public, + input: &sr25519::vrf::VrfInput, + ) -> Result, Error>; + /// Returns all ed25519 public keys for the given key type. fn ed25519_public_keys(&self, key_type: KeyTypeId) -> Vec; diff --git a/primitives/keystore/src/testing.rs b/primitives/keystore/src/testing.rs index a6fcd6e26..dd3254e93 100644 --- a/primitives/keystore/src/testing.rs +++ b/primitives/keystore/src/testing.rs @@ -20,7 +20,7 @@ use crate::{Error, Keystore, KeystorePtr}; use sp_core::{ - crypto::{ByteArray, KeyTypeId, Pair, VrfSigner}, + crypto::{ByteArray, KeyTypeId, Pair, VrfSecret}, ecdsa, ed25519, sr25519, }; @@ -99,15 +99,25 @@ impl MemoryKeystore { Ok(sig) } - fn vrf_sign( + fn vrf_sign( &self, key_type: KeyTypeId, public: &T::Public, - transcript: &T::VrfInput, + data: &T::VrfSignData, ) -> Result, Error> { - let sig = self.pair::(key_type, public).map(|pair| pair.vrf_sign(transcript)); + let sig = self.pair::(key_type, public).map(|pair| pair.vrf_sign(data)); Ok(sig) } + + fn vrf_output( + &self, + key_type: KeyTypeId, + public: &T::Public, + input: &T::VrfInput, + ) -> Result, Error> { + let preout = self.pair::(key_type, public).map(|pair| pair.vrf_output(input)); + Ok(preout) + } } impl Keystore for MemoryKeystore { @@ -136,9 +146,18 @@ impl Keystore for MemoryKeystore { &self, key_type: KeyTypeId, public: &sr25519::Public, - transcript: &sr25519::vrf::VrfTranscript, + data: &sr25519::vrf::VrfSignData, ) -> Result, Error> { - self.vrf_sign::(key_type, public, transcript) + self.vrf_sign::(key_type, public, data) + } + + fn sr25519_vrf_output( + &self, + key_type: KeyTypeId, + public: &sr25519::Public, + input: &sr25519::vrf::VrfInput, + ) -> Result, Error> { + self.vrf_output::(key_type, public, input) } fn ed25519_public_keys(&self, key_type: KeyTypeId) -> Vec { @@ -267,27 +286,57 @@ mod tests { let secret_uri = "//Alice"; let key_pair = sr25519::Pair::from_string(secret_uri, None).expect("Generates key pair"); - let transcript = sr25519::vrf::VrfTranscript::new( + let data = sr25519::vrf::VrfInput::new( b"Test", &[ (b"one", &1_u64.to_le_bytes()), (b"two", &2_u64.to_le_bytes()), (b"three", "test".as_bytes()), ], - ); + ) + .into_sign_data(); - let result = store.sr25519_vrf_sign(SR25519, &key_pair.public(), &transcript); + let result = store.sr25519_vrf_sign(SR25519, &key_pair.public(), &data); assert!(result.unwrap().is_none()); store .insert(SR25519, secret_uri, key_pair.public().as_ref()) .expect("Inserts unknown key"); - let result = store.sr25519_vrf_sign(SR25519, &key_pair.public(), &transcript); + let result = store.sr25519_vrf_sign(SR25519, &key_pair.public(), &data); assert!(result.unwrap().is_some()); } + #[test] + fn vrf_output() { + let store = MemoryKeystore::new(); + + let secret_uri = "//Alice"; + let pair = sr25519::Pair::from_string(secret_uri, None).expect("Generates key pair"); + + let input = sr25519::vrf::VrfInput::new( + b"Test", + &[ + (b"one", &1_u64.to_le_bytes()), + (b"two", &2_u64.to_le_bytes()), + (b"three", "test".as_bytes()), + ], + ); + + let result = store.sr25519_vrf_output(SR25519, &pair.public(), &input); + assert!(result.unwrap().is_none()); + + store + .insert(SR25519, secret_uri, pair.public().as_ref()) + .expect("Inserts unknown key"); + + let preout = store.sr25519_vrf_output(SR25519, &pair.public(), &input).unwrap().unwrap(); + + let result = preout.make_bytes::<32>(b"rand", &input, &pair.public()); + assert!(result.is_ok()); + } + #[test] fn ecdsa_sign_prehashed_works() { let store = MemoryKeystore::new(); From ebe0c089d60f0aae9503ef61e84728f9032dd392 Mon Sep 17 00:00:00 2001 From: Michal Kucharczyk <1728078+michalkucharczyk@users.noreply.github.com> Date: Thu, 4 May 2023 18:20:22 +0200 Subject: [PATCH 455/558] substrate-test-runtime migrated to "pure" frame runtime (#13737) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * substrate-test-runtime migrated to pure-frame based * test block builder: helpers added * simple renaming * basic_authorship test adjusted * block_building storage_proof test adjusted * babe: tests: should_panic expected added * babe: tests adjusted ConsensusLog::NextEpochData is now added by pallet_babe as pallet_babe::SameAuthoritiesForever trigger is used in runtime config. * beefy: tests adjusted test-substrate-runtime is now using frame::executive to finalize the block. during finalization the digests stored during block execution are checked against header digests: https://github.com/paritytech/substrate/blob/aaa9a3631d29f757552ffcc8b97aa7091c0b27b0/frame/executive/src/lib.rs#L585-L591 It makes impossible to directly manipulate header's digets, w/o depositing logs into system pallet storage `Digest`. Instead of this dedicated extrinsic allowing to store logs items (MmrRoot / AuthoritiesChange) is used. * grandpa: tests adjusted test-substrate-runtime is now using frame::executive to finalize the block. during finalization the digest logs stored during block execution are checked against header digest logs: https://github.com/paritytech/substrate/blob/aaa9a3631d29f757552ffcc8b97aa7091c0b27b0/frame/executive/src/lib.rs#L585-L591 It makes impossible to directly manipulate header's digets, w/o depositing logs into system pallet storage `Digest`. Instead of this dedicated extrinsic allowing to store logs items (ScheduledChange / ForcedChange and DigestItem::Other) is used. * network:bitswap: test adjusted The size of unchecked extrinsic was increased. The pattern used in test will be placed at the end of scale-encoded buffer. * runtime apis versions adjusted * storage keys used in runtime adjusted * wasm vs native tests removed * rpc tests: adjusted Transfer transaction processing was slightly improved, test was adjusted. * tests: sizes adjusted Runtime extrinsic size was increased. Size of data read during block execution was also increased due to usage of new pallets in runtime. Sizes were adjusted in tests. * cargo.lock update cargo update -p substrate-test-runtime -p substrate-test-runtime-client * warnings fixed * builders cleanup: includes / std * extrinsic validation cleanup * txpool: benches performance fixed * fmt * spelling * Apply suggestions from code review Co-authored-by: Davide Galassi * Apply code review suggestions * Apply code review suggestions * get rid of 1063 const * renaming: UncheckedExtrinsic -> Extrinsic * test-utils-runtime: further step to pure-frame * basic-authorship: tests OK * CheckSubstrateCall added + tests fixes * test::Transfer call removed * priority / propagate / no sudo+root-testing * fixing warnings + format * cleanup: build2/nonce + format * final tests fixes all tests are passing * logs/comments removal * should_not_accept_old_signatures test removed * make txpool benches work again * Cargo.lock reset * format * sudo hack removed * txpool benches fix+cleanup * .gitignore reverted * rebase fixing + unsigned cleanup * Cargo.toml/Cargo.lock cleanup * force-debug feature removed * mmr tests fixed * make cargo-clippy happy * network sync test uses unsigned extrinsic * cleanup * ".git/.scripts/commands/fmt/fmt.sh" * push_storage_change signed call remove * GenesisConfig cleanup * fix * fix * GenesisConfig simplified * storage_keys_works: reworked * storage_keys_works: expected keys in vec * storage keys list moved to substrate-test-runtime * substrate-test: some sanity tests + GenesisConfigBuilder rework * Apply suggestions from code review Co-authored-by: Bastian Köcher * Apply suggestions from code review * Review suggestions * fix * fix * beefy: generate_blocks_and_sync block_num sync with actaul value * Apply suggestions from code review Co-authored-by: Davide Galassi * Update test-utils/runtime/src/genesismap.rs Co-authored-by: Davide Galassi * cargo update -p sc-rpc -p sc-transaction-pool * Review suggestions * fix * doc added * slot_duration adjusted for Babe::slot_duration * small doc fixes * array_bytes::hex used instead of hex * tiny -> medium name fix * Apply suggestions from code review Co-authored-by: Sebastian Kunert * TransferData::try_from_unchecked_extrinsic -> try_from * Update Cargo.lock --------- Co-authored-by: parity-processbot <> Co-authored-by: Davide Galassi Co-authored-by: Bastian Köcher Co-authored-by: Sebastian Kunert --- Cargo.lock | 80 +- .../basic-authorship/src/basic_authorship.rs | 180 +- client/basic-authorship/src/lib.rs | 2 +- client/block-builder/src/lib.rs | 19 +- client/consensus/babe/src/tests.rs | 61 +- client/consensus/beefy/src/tests.rs | 42 +- client/consensus/grandpa/src/tests.rs | 117 +- client/consensus/grandpa/src/warp_proof.rs | 12 +- client/network/bitswap/src/lib.rs | 8 +- client/network/test/src/lib.rs | 16 +- client/network/test/src/sync.rs | 8 +- client/offchain/src/lib.rs | 26 +- client/rpc-spec-v2/src/chain_head/tests.rs | 7 +- client/rpc/src/author/tests.rs | 31 +- client/rpc/src/dev/tests.rs | 22 +- client/rpc/src/state/tests.rs | 63 +- client/service/src/lib.rs | 11 +- client/service/test/src/client/mod.rs | 225 +-- client/transaction-pool/benches/basics.rs | 21 +- client/transaction-pool/src/graph/pool.rs | 95 +- client/transaction-pool/src/revalidation.rs | 3 +- client/transaction-pool/src/tests.rs | 26 +- client/transaction-pool/tests/pool.rs | 115 +- primitives/api/test/tests/runtime_calls.rs | 51 +- test-utils/runtime/Cargo.toml | 18 +- .../runtime/client/src/block_builder_ext.rs | 21 +- test-utils/runtime/client/src/lib.rs | 66 +- test-utils/runtime/src/extrinsic.rs | 207 ++ test-utils/runtime/src/genesismap.rs | 148 +- test-utils/runtime/src/lib.rs | 1765 +++++++---------- .../runtime/src/substrate_test_pallet.rs | 244 +++ test-utils/runtime/src/system.rs | 587 ------ .../runtime/transaction-pool/src/lib.rs | 10 +- utils/frame/rpc/system/src/lib.rs | 8 +- 34 files changed, 1951 insertions(+), 2364 deletions(-) create mode 100644 test-utils/runtime/src/extrinsic.rs create mode 100644 test-utils/runtime/src/substrate_test_pallet.rs delete mode 100644 test-utils/runtime/src/system.rs diff --git a/Cargo.lock b/Cargo.lock index e65562a56..3f1fd0bd5 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -291,6 +291,12 @@ version = "4.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f52f63c5c1316a16a4b35eaac8b76a98248961a533f061684cb2a7cb0eafb6c6" +[[package]] +name = "array-bytes" +version = "6.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d9b1c5a481ec30a5abd8dfbd94ab5cf1bb4e9a66be7f1b3b322f2f1170c200fd" + [[package]] name = "arrayref" version = "0.3.6" @@ -597,7 +603,7 @@ dependencies = [ name = "binary-merkle-tree" version = "4.0.0-dev" dependencies = [ - "array-bytes", + "array-bytes 4.2.0", "env_logger 0.9.3", "hash-db", "log", @@ -2327,7 +2333,7 @@ checksum = "6c2141d6d6c8512188a7891b4b01590a45f6dac67afb4f255c4124dbb86d4eaa" name = "frame-benchmarking" version = "4.0.0-dev" dependencies = [ - "array-bytes", + "array-bytes 4.2.0", "frame-support", "frame-support-procedural", "frame-system", @@ -2355,7 +2361,7 @@ name = "frame-benchmarking-cli" version = "4.0.0-dev" dependencies = [ "Inflector", - "array-bytes", + "array-bytes 4.2.0", "chrono", "clap 4.2.5", "comfy-table", @@ -2465,7 +2471,7 @@ dependencies = [ name = "frame-executive" version = "4.0.0-dev" dependencies = [ - "array-bytes", + "array-bytes 4.2.0", "frame-support", "frame-system", "frame-try-runtime", @@ -5043,7 +5049,7 @@ dependencies = [ name = "node-bench" version = "0.9.0-dev" dependencies = [ - "array-bytes", + "array-bytes 4.2.0", "clap 4.2.5", "derive_more", "fs_extra", @@ -5079,7 +5085,7 @@ dependencies = [ name = "node-cli" version = "3.0.0-dev" dependencies = [ - "array-bytes", + "array-bytes 4.2.0", "assert_cmd", "clap 4.2.5", "clap_complete", @@ -5641,7 +5647,7 @@ dependencies = [ name = "pallet-alliance" version = "4.0.0-dev" dependencies = [ - "array-bytes", + "array-bytes 4.2.0", "frame-benchmarking", "frame-support", "frame-system", @@ -5898,7 +5904,7 @@ dependencies = [ name = "pallet-beefy-mmr" version = "4.0.0-dev" dependencies = [ - "array-bytes", + "array-bytes 4.2.0", "binary-merkle-tree", "frame-support", "frame-system", @@ -5975,7 +5981,7 @@ dependencies = [ name = "pallet-contracts" version = "4.0.0-dev" dependencies = [ - "array-bytes", + "array-bytes 4.2.0", "assert_matches", "bitflags", "env_logger 0.9.3", @@ -6420,7 +6426,7 @@ dependencies = [ name = "pallet-mmr" version = "4.0.0-dev" dependencies = [ - "array-bytes", + "array-bytes 4.2.0", "env_logger 0.9.3", "frame-benchmarking", "frame-support", @@ -7128,7 +7134,7 @@ dependencies = [ name = "pallet-transaction-storage" version = "4.0.0-dev" dependencies = [ - "array-bytes", + "array-bytes 4.2.0", "frame-benchmarking", "frame-support", "frame-system", @@ -8584,7 +8590,7 @@ dependencies = [ name = "sc-cli" version = "0.10.0-dev" dependencies = [ - "array-bytes", + "array-bytes 4.2.0", "chrono", "clap 4.2.5", "fdlimit", @@ -8655,7 +8661,7 @@ dependencies = [ name = "sc-client-db" version = "0.10.0-dev" dependencies = [ - "array-bytes", + "array-bytes 4.2.0", "criterion", "hash-db", "kitchensink-runtime", @@ -8822,7 +8828,7 @@ dependencies = [ name = "sc-consensus-beefy" version = "4.0.0-dev" dependencies = [ - "array-bytes", + "array-bytes 4.2.0", "async-trait", "fnv", "futures", @@ -8899,7 +8905,7 @@ name = "sc-consensus-grandpa" version = "0.10.0-dev" dependencies = [ "ahash 0.8.3", - "array-bytes", + "array-bytes 4.2.0", "assert_matches", "async-trait", "dyn-clone", @@ -9053,7 +9059,7 @@ dependencies = [ name = "sc-executor" version = "0.10.0-dev" dependencies = [ - "array-bytes", + "array-bytes 4.2.0", "assert_matches", "criterion", "env_logger 0.9.3", @@ -9156,7 +9162,7 @@ dependencies = [ name = "sc-keystore" version = "4.0.0-dev" dependencies = [ - "array-bytes", + "array-bytes 4.2.0", "async-trait", "parking_lot 0.12.1", "serde_json", @@ -9171,7 +9177,7 @@ dependencies = [ name = "sc-network" version = "0.10.0-dev" dependencies = [ - "array-bytes", + "array-bytes 4.2.0", "assert_matches", "async-channel", "async-trait", @@ -9253,7 +9259,7 @@ dependencies = [ name = "sc-network-common" version = "0.10.0-dev" dependencies = [ - "array-bytes", + "array-bytes 4.2.0", "async-trait", "bitflags", "bytes", @@ -9302,7 +9308,7 @@ dependencies = [ name = "sc-network-light" version = "0.10.0-dev" dependencies = [ - "array-bytes", + "array-bytes 4.2.0", "futures", "libp2p", "log", @@ -9323,7 +9329,7 @@ dependencies = [ name = "sc-network-statement" version = "0.10.0-dev" dependencies = [ - "array-bytes", + "array-bytes 4.2.0", "async-channel", "futures", "libp2p", @@ -9343,7 +9349,7 @@ dependencies = [ name = "sc-network-sync" version = "0.10.0-dev" dependencies = [ - "array-bytes", + "array-bytes 4.2.0", "async-trait", "fork-tree", "futures", @@ -9413,7 +9419,7 @@ dependencies = [ name = "sc-network-transactions" version = "0.10.0-dev" dependencies = [ - "array-bytes", + "array-bytes 4.2.0", "futures", "libp2p", "log", @@ -9432,7 +9438,7 @@ dependencies = [ name = "sc-offchain" version = "4.0.0-dev" dependencies = [ - "array-bytes", + "array-bytes 4.2.0", "bytes", "fnv", "futures", @@ -9562,7 +9568,7 @@ dependencies = [ name = "sc-rpc-spec-v2" version = "0.10.0-dev" dependencies = [ - "array-bytes", + "array-bytes 4.2.0", "assert_matches", "futures", "futures-util", @@ -9676,7 +9682,7 @@ dependencies = [ name = "sc-service-test" version = "2.0.0" dependencies = [ - "array-bytes", + "array-bytes 4.2.0", "async-channel", "fdlimit", "futures", @@ -9858,7 +9864,7 @@ dependencies = [ name = "sc-transaction-pool" version = "4.0.0-dev" dependencies = [ - "array-bytes", + "array-bytes 4.2.0", "assert_matches", "async-trait", "criterion", @@ -10585,7 +10591,7 @@ dependencies = [ name = "sp-consensus-beefy" version = "4.0.0-dev" dependencies = [ - "array-bytes", + "array-bytes 4.2.0", "lazy_static", "parity-scale-codec", "scale-info", @@ -10644,7 +10650,7 @@ dependencies = [ name = "sp-core" version = "7.0.0" dependencies = [ - "array-bytes", + "array-bytes 4.2.0", "bitflags", "blake2", "bounded-collections", @@ -10824,7 +10830,7 @@ dependencies = [ name = "sp-mmr-primitives" version = "4.0.0-dev" dependencies = [ - "array-bytes", + "array-bytes 4.2.0", "ckb-merkle-mountain-range", "log", "parity-scale-codec", @@ -11031,7 +11037,7 @@ dependencies = [ name = "sp-state-machine" version = "0.13.0" dependencies = [ - "array-bytes", + "array-bytes 4.2.0", "assert_matches", "hash-db", "log", @@ -11149,7 +11155,7 @@ name = "sp-trie" version = "7.0.0" dependencies = [ "ahash 0.8.3", - "array-bytes", + "array-bytes 4.2.0", "criterion", "hash-db", "hashbrown 0.13.2", @@ -11501,7 +11507,7 @@ dependencies = [ name = "substrate-test-client" version = "2.0.1" dependencies = [ - "array-bytes", + "array-bytes 4.2.0", "async-trait", "futures", "parity-scale-codec", @@ -11526,7 +11532,8 @@ dependencies = [ name = "substrate-test-runtime" version = "2.0.0" dependencies = [ - "cfg-if", + "array-bytes 6.1.0", + "frame-executive", "frame-support", "frame-system", "frame-system-rpc-runtime-api", @@ -11534,7 +11541,10 @@ dependencies = [ "log", "memory-db", "pallet-babe", + "pallet-balances", "pallet-beefy-mmr", + "pallet-root-testing", + "pallet-sudo", "pallet-timestamp", "parity-scale-codec", "sc-block-builder", @@ -11551,6 +11561,7 @@ dependencies = [ "sp-consensus-beefy", "sp-consensus-grandpa", "sp-core", + "sp-debug-derive", "sp-externalities", "sp-inherents", "sp-io", @@ -11561,6 +11572,7 @@ dependencies = [ "sp-session", "sp-state-machine", "sp-std", + "sp-tracing", "sp-transaction-pool", "sp-trie", "sp-version", diff --git a/client/basic-authorship/src/basic_authorship.rs b/client/basic-authorship/src/basic_authorship.rs index 795288d0a..642900d2f 100644 --- a/client/basic-authorship/src/basic_authorship.rs +++ b/client/basic-authorship/src/basic_authorship.rs @@ -547,37 +547,29 @@ mod tests { use sp_api::Core; use sp_blockchain::HeaderBackend; use sp_consensus::{BlockOrigin, Environment, Proposer}; - use sp_core::Pair; - use sp_runtime::{generic::BlockId, traits::NumberFor}; + use sp_runtime::{generic::BlockId, traits::NumberFor, Perbill}; use substrate_test_runtime_client::{ prelude::*, - runtime::{Extrinsic, Transfer}, + runtime::{Block as TestBlock, Extrinsic, ExtrinsicBuilder, Transfer}, TestClientBuilder, TestClientBuilderExt, }; const SOURCE: TransactionSource = TransactionSource::External; - fn extrinsic(nonce: u64) -> Extrinsic { - Transfer { - amount: Default::default(), - nonce, - from: AccountKeyring::Alice.into(), - to: AccountKeyring::Bob.into(), - } - .into_signed_tx() - } + // Note: + // Maximum normal extrinsic size for `substrate_test_runtime` is ~65% of max_block (refer to + // `substrate_test_runtime::RuntimeBlockWeights` for details). + // This extrinsic sizing allows for: + // - one huge xts + a lot of tiny dust + // - one huge, no medium, + // - two medium xts + // This is widely exploited in following tests. + const HUGE: u32 = 649000000; + const MEDIUM: u32 = 250000000; + const TINY: u32 = 1000; - fn exhausts_resources_extrinsic_from(who: usize) -> Extrinsic { - let pair = AccountKeyring::numeric(who); - let transfer = Transfer { - // increase the amount to bump priority - amount: 1, - nonce: 0, - from: pair.public(), - to: AccountKeyring::Bob.into(), - }; - let signature = pair.sign(&transfer.encode()).into(); - Extrinsic::Transfer { transfer, signature, exhaust_resources_when_not_first: true } + fn extrinsic(nonce: u64) -> Extrinsic { + ExtrinsicBuilder::new_fill_block(Perbill::from_parts(TINY)).nonce(nonce).build() } fn chain_event(header: B::Header) -> ChainEvent @@ -738,7 +730,7 @@ mod tests { #[test] fn should_not_remove_invalid_transactions_from_the_same_sender_after_one_was_invalid() { // given - let mut client = Arc::new(substrate_test_runtime_client::new()); + let client = Arc::new(substrate_test_runtime_client::new()); let spawner = sp_core::testing::TaskExecutor::new(); let txpool = BasicPool::new_full( Default::default(), @@ -748,38 +740,29 @@ mod tests { client.clone(), ); + let medium = |nonce| { + ExtrinsicBuilder::new_fill_block(Perbill::from_parts(MEDIUM)) + .nonce(nonce) + .build() + }; + let huge = |nonce| { + ExtrinsicBuilder::new_fill_block(Perbill::from_parts(HUGE)).nonce(nonce).build() + }; + block_on(txpool.submit_at( &BlockId::number(0), SOURCE, - vec![ - extrinsic(0), - extrinsic(1), - Transfer { - amount: Default::default(), - nonce: 2, - from: AccountKeyring::Alice.into(), - to: AccountKeyring::Bob.into(), - }.into_resources_exhausting_tx(), - extrinsic(3), - Transfer { - amount: Default::default(), - nonce: 4, - from: AccountKeyring::Alice.into(), - to: AccountKeyring::Bob.into(), - }.into_resources_exhausting_tx(), - extrinsic(5), - extrinsic(6), - ], + vec![medium(0), medium(1), huge(2), medium(3), huge(4), medium(5), medium(6)], )) .unwrap(); let mut proposer_factory = ProposerFactory::new(spawner.clone(), client.clone(), txpool.clone(), None, None); let mut propose_block = |client: &TestClient, - number, + parent_number, expected_block_extrinsics, expected_pool_transactions| { - let hash = client.expect_block_hash_from_id(&BlockId::Number(number)).unwrap(); + let hash = client.expect_block_hash_from_id(&BlockId::Number(parent_number)).unwrap(); let proposer = proposer_factory.init_with_now( &client.expect_header(hash).unwrap(), Box::new(move || time::Instant::now()), @@ -794,12 +777,30 @@ mod tests { // then // block should have some extrinsics although we have some more in the pool. - assert_eq!(txpool.ready().count(), expected_pool_transactions); - assert_eq!(block.extrinsics().len(), expected_block_extrinsics); + assert_eq!( + txpool.ready().count(), + expected_pool_transactions, + "at block: {}", + block.header.number + ); + assert_eq!( + block.extrinsics().len(), + expected_block_extrinsics, + "at block: {}", + block.header.number + ); block }; + let import_and_maintain = |mut client: Arc, block: TestBlock| { + let hash = block.hash(); + block_on(client.import(BlockOrigin::Own, block)).unwrap(); + block_on(txpool.maintain(chain_event( + client.expect_header(hash).expect("there should be header"), + ))); + }; + block_on( txpool.maintain(chain_event( client @@ -811,19 +812,28 @@ mod tests { // let's create one block and import it let block = propose_block(&client, 0, 2, 7); - let hashof1 = block.hash(); - block_on(client.import(BlockOrigin::Own, block)).unwrap(); - - block_on( - txpool.maintain(chain_event( - client.expect_header(hashof1).expect("there should be header"), - )), - ); + import_and_maintain(client.clone(), block); assert_eq!(txpool.ready().count(), 5); // now let's make sure that we can still make some progress - let block = propose_block(&client, 1, 2, 5); - block_on(client.import(BlockOrigin::Own, block)).unwrap(); + let block = propose_block(&client, 1, 1, 5); + import_and_maintain(client.clone(), block); + assert_eq!(txpool.ready().count(), 4); + + // again let's make sure that we can still make some progress + let block = propose_block(&client, 2, 1, 4); + import_and_maintain(client.clone(), block); + assert_eq!(txpool.ready().count(), 3); + + // again let's make sure that we can still make some progress + let block = propose_block(&client, 3, 1, 3); + import_and_maintain(client.clone(), block); + assert_eq!(txpool.ready().count(), 2); + + // again let's make sure that we can still make some progress + let block = propose_block(&client, 4, 2, 2); + import_and_maintain(client.clone(), block); + assert_eq!(txpool.ready().count(), 0); } #[test] @@ -849,9 +859,9 @@ mod tests { amount: 100, nonce: 0, } - .into_signed_tx(), + .into_unchecked_extrinsic(), ) - .chain((0..extrinsics_num - 1).map(|v| Extrinsic::IncludeData(vec![v as u8; 10]))) + .chain((1..extrinsics_num as u64).map(extrinsic)) .collect::>(); let block_limit = genesis_header.encoded_size() + @@ -862,7 +872,7 @@ mod tests { .sum::() + Vec::::new().encoded_size(); - block_on(txpool.submit_at(&BlockId::number(0), SOURCE, extrinsics)).unwrap(); + block_on(txpool.submit_at(&BlockId::number(0), SOURCE, extrinsics.clone())).unwrap(); block_on(txpool.maintain(chain_event(genesis_header.clone()))); @@ -905,7 +915,13 @@ mod tests { let proposer = block_on(proposer_factory.init(&genesis_header)).unwrap(); - // Give it enough time + // Exact block_limit, which includes: + // 99 (header_size) + 718 (proof@initialize_block) + 246 (one Transfer extrinsic) + let block_limit = { + let builder = + client.new_block_at(genesis_header.hash(), Default::default(), true).unwrap(); + builder.estimate_block_size(true) + extrinsics[0].encoded_size() + }; let block = block_on(proposer.propose( Default::default(), Default::default(), @@ -915,7 +931,7 @@ mod tests { .map(|r| r.block) .unwrap(); - // The block limit didn't changed, but we now include the proof in the estimation of the + // The block limit was increased, but we now include the proof in the estimation of the // block size and thus, only the `Transfer` will fit into the block. It reads more data // than we have reserved in the block limit. assert_eq!(block.extrinsics().len(), 1); @@ -934,6 +950,15 @@ mod tests { client.clone(), ); + let tiny = |nonce| { + ExtrinsicBuilder::new_fill_block(Perbill::from_parts(TINY)).nonce(nonce).build() + }; + let huge = |who| { + ExtrinsicBuilder::new_fill_block(Perbill::from_parts(HUGE)) + .signer(AccountKeyring::numeric(who)) + .build() + }; + block_on( txpool.submit_at( &BlockId::number(0), @@ -941,9 +966,9 @@ mod tests { // add 2 * MAX_SKIPPED_TRANSACTIONS that exhaust resources (0..MAX_SKIPPED_TRANSACTIONS * 2) .into_iter() - .map(|i| exhausts_resources_extrinsic_from(i)) + .map(huge) // and some transactions that are okay. - .chain((0..MAX_SKIPPED_TRANSACTIONS).into_iter().map(|i| extrinsic(i as _))) + .chain((0..MAX_SKIPPED_TRANSACTIONS as u64).into_iter().map(tiny)) .collect(), ), ) @@ -997,15 +1022,27 @@ mod tests { client.clone(), ); + let tiny = |who| { + ExtrinsicBuilder::new_fill_block(Perbill::from_parts(TINY)) + .signer(AccountKeyring::numeric(who)) + .nonce(1) + .build() + }; + let huge = |who| { + ExtrinsicBuilder::new_fill_block(Perbill::from_parts(HUGE)) + .signer(AccountKeyring::numeric(who)) + .build() + }; + block_on( txpool.submit_at( &BlockId::number(0), SOURCE, (0..MAX_SKIPPED_TRANSACTIONS + 2) .into_iter() - .map(|i| exhausts_resources_extrinsic_from(i)) + .map(huge) // and some transactions that are okay. - .chain((0..MAX_SKIPPED_TRANSACTIONS).into_iter().map(|i| extrinsic(i as _))) + .chain((0..MAX_SKIPPED_TRANSACTIONS + 2).into_iter().map(tiny)) .collect(), ), ) @@ -1018,7 +1055,7 @@ mod tests { .expect("there should be header"), )), ); - assert_eq!(txpool.ready().count(), MAX_SKIPPED_TRANSACTIONS * 2 + 2); + assert_eq!(txpool.ready().count(), MAX_SKIPPED_TRANSACTIONS * 2 + 4); let mut proposer_factory = ProposerFactory::new(spawner.clone(), client.clone(), txpool.clone(), None, None); @@ -1049,8 +1086,13 @@ mod tests { .map(|r| r.block) .unwrap(); - // then the block should have no transactions despite some in the pool - assert_eq!(block.extrinsics().len(), 1); + // then the block should have one or two transactions. This maybe random as they are + // processed in parallel. The same signer and consecutive nonces for huge and tiny + // transactions guarantees that max two transactions will get to the block. + assert!( + (1..3).contains(&block.extrinsics().len()), + "Block shall contain one or two extrinsics." + ); assert!( cell2.lock().0 > MAX_SKIPPED_TRANSACTIONS, "Not enough calls to current time, which indicates the test might have ended because of deadline, not soft deadline" diff --git a/client/basic-authorship/src/lib.rs b/client/basic-authorship/src/lib.rs index 23a2120ac..8f47c2ea0 100644 --- a/client/basic-authorship/src/lib.rs +++ b/client/basic-authorship/src/lib.rs @@ -26,7 +26,7 @@ //! # use sp_runtime::generic::BlockId; //! # use std::{sync::Arc, time::Duration}; //! # use substrate_test_runtime_client::{ -//! # runtime::{Extrinsic, Transfer}, AccountKeyring, +//! # runtime::Transfer, AccountKeyring, //! # DefaultTestClientBuilderExt, TestClientBuilderExt, //! # }; //! # use sc_transaction_pool::{BasicPool, FullChainApi}; diff --git a/client/block-builder/src/lib.rs b/client/block-builder/src/lib.rs index 21f8e6fdd..f055d4688 100644 --- a/client/block-builder/src/lib.rs +++ b/client/block-builder/src/lib.rs @@ -313,7 +313,7 @@ mod tests { use sp_core::Blake2Hasher; use sp_state_machine::Backend; use substrate_test_runtime_client::{ - runtime::Extrinsic, DefaultTestClientBuilderExt, TestClientBuilderExt, + runtime::ExtrinsicBuilder, DefaultTestClientBuilderExt, TestClientBuilderExt, }; #[test] @@ -322,9 +322,11 @@ mod tests { let backend = builder.backend(); let client = builder.build(); + let genesis_hash = client.info().best_hash; + let block = BlockBuilder::new( &client, - client.info().best_hash, + genesis_hash, client.info().best_number, RecordProof::Yes, Default::default(), @@ -335,12 +337,11 @@ mod tests { .unwrap(); let proof = block.proof.expect("Proof is build on request"); + let genesis_state_root = client.header(genesis_hash).unwrap().unwrap().state_root; - let backend = sp_state_machine::create_proof_check_backend::( - block.storage_changes.transaction_storage_root, - proof, - ) - .unwrap(); + let backend = + sp_state_machine::create_proof_check_backend::(genesis_state_root, proof) + .unwrap(); assert!(backend .storage(&sp_core::storage::well_known_keys::CODE) @@ -364,7 +365,7 @@ mod tests { ) .unwrap(); - block_builder.push(Extrinsic::ReadAndPanic(8)).unwrap_err(); + block_builder.push(ExtrinsicBuilder::new_read_and_panic(8).build()).unwrap_err(); let block = block_builder.build().unwrap(); @@ -380,7 +381,7 @@ mod tests { ) .unwrap(); - block_builder.push(Extrinsic::Read(8)).unwrap(); + block_builder.push(ExtrinsicBuilder::new_read(8).build()).unwrap(); let block = block_builder.build().unwrap(); diff --git a/client/consensus/babe/src/tests.rs b/client/consensus/babe/src/tests.rs index aa3e32fa0..59b4076e2 100644 --- a/client/consensus/babe/src/tests.rs +++ b/client/consensus/babe/src/tests.rs @@ -71,15 +71,12 @@ const SLOT_DURATION_MS: u64 = 1000; struct DummyFactory { client: Arc, epoch_changes: SharedEpochChanges, - config: BabeConfiguration, mutator: Mutator, } struct DummyProposer { factory: DummyFactory, parent_hash: Hash, - parent_number: u64, - parent_slot: Slot, } impl Environment for DummyFactory { @@ -88,15 +85,9 @@ impl Environment for DummyFactory { type Error = Error; fn init(&mut self, parent_header: &::Header) -> Self::CreateProposer { - let parent_slot = crate::find_pre_digest::(parent_header) - .expect("parent header has a pre-digest") - .slot(); - future::ready(Ok(DummyProposer { factory: self.clone(), parent_hash: parent_header.hash(), - parent_number: *parent_header.number(), - parent_slot, })) } } @@ -123,39 +114,6 @@ impl DummyProposer { Err(e) => return future::ready(Err(e)), }; - let this_slot = crate::find_pre_digest::(block.header()) - .expect("baked block has valid pre-digest") - .slot(); - - // figure out if we should add a consensus digest, since the test runtime - // doesn't. - let epoch_changes = self.factory.epoch_changes.shared_data(); - let epoch = epoch_changes - .epoch_data_for_child_of( - descendent_query(&*self.factory.client), - &self.parent_hash, - self.parent_number, - this_slot, - |slot| Epoch::genesis(&self.factory.config, slot), - ) - .expect("client has data to find epoch") - .expect("can compute epoch for baked block"); - - let first_in_epoch = self.parent_slot < epoch.start_slot; - if first_in_epoch { - // push a `Consensus` digest signalling next change. - // we just reuse the same randomness and authorities as the prior - // epoch. this will break when we add light client support, since - // that will re-check the randomness logic off-chain. - let digest_data = ConsensusLog::NextEpochData(NextEpochDescriptor { - authorities: epoch.authorities.clone(), - randomness: epoch.randomness, - }) - .encode(); - let digest = DigestItem::Consensus(BABE_ENGINE_ID, digest_data); - block.header.digest_mut().push(digest) - } - // mutate the block header according to the mutator. (self.factory.mutator)(&mut block.header, Stage::PreSeal); @@ -351,7 +309,7 @@ impl TestNetFactory for BabeTestNet { } #[tokio::test] -#[should_panic] +#[should_panic(expected = "No BABE pre-runtime digest found")] async fn rejects_empty_block() { sp_tracing::try_init_simple(); let mut net = BabeTestNet::new(3); @@ -398,7 +356,6 @@ async fn run_one_test(mutator: impl Fn(&mut TestHeader, Stage) + Send + Sync + ' let environ = DummyFactory { client: client.clone(), - config: data.link.config.clone(), epoch_changes: data.link.epoch_changes.clone(), mutator: mutator.clone(), }; @@ -483,7 +440,7 @@ async fn authoring_blocks() { } #[tokio::test] -#[should_panic] +#[should_panic(expected = "valid babe headers must contain a predigest")] async fn rejects_missing_inherent_digest() { run_one_test(|header: &mut TestHeader, stage| { let v = std::mem::take(&mut header.digest_mut().logs); @@ -496,7 +453,7 @@ async fn rejects_missing_inherent_digest() { } #[tokio::test] -#[should_panic] +#[should_panic(expected = "has a bad seal")] async fn rejects_missing_seals() { run_one_test(|header: &mut TestHeader, stage| { let v = std::mem::take(&mut header.digest_mut().logs); @@ -509,7 +466,7 @@ async fn rejects_missing_seals() { } #[tokio::test] -#[should_panic] +#[should_panic(expected = "Expected epoch change to happen")] async fn rejects_missing_consensus_digests() { run_one_test(|header: &mut TestHeader, stage| { let v = std::mem::take(&mut header.digest_mut().logs); @@ -770,7 +727,6 @@ async fn importing_block_one_sets_genesis_epoch() { let mut proposer_factory = DummyFactory { client: client.clone(), - config: data.link.config.clone(), epoch_changes: data.link.epoch_changes.clone(), mutator: Arc::new(|_, _| ()), }; @@ -814,7 +770,6 @@ async fn revert_prunes_epoch_changes_and_removes_weights() { let mut proposer_factory = DummyFactory { client: client.clone(), - config: data.link.config.clone(), epoch_changes: data.link.epoch_changes.clone(), mutator: Arc::new(|_, _| ()), }; @@ -902,7 +857,6 @@ async fn revert_not_allowed_for_finalized() { let mut proposer_factory = DummyFactory { client: client.clone(), - config: data.link.config.clone(), epoch_changes: data.link.epoch_changes.clone(), mutator: Arc::new(|_, _| ()), }; @@ -943,7 +897,6 @@ async fn importing_epoch_change_block_prunes_tree() { let mut proposer_factory = DummyFactory { client: client.clone(), - config: data.link.config.clone(), epoch_changes: data.link.epoch_changes.clone(), mutator: Arc::new(|_, _| ()), }; @@ -1030,7 +983,7 @@ async fn importing_epoch_change_block_prunes_tree() { } #[tokio::test] -#[should_panic] +#[should_panic(expected = "Slot number must increase: parent slot: 999, this slot: 999")] async fn verify_slots_are_strictly_increasing() { let mut net = BabeTestNet::new(1); @@ -1042,7 +995,6 @@ async fn verify_slots_are_strictly_increasing() { let mut proposer_factory = DummyFactory { client: client.clone(), - config: data.link.config.clone(), epoch_changes: data.link.epoch_changes.clone(), mutator: Arc::new(|_, _| ()), }; @@ -1082,7 +1034,6 @@ async fn obsolete_blocks_aux_data_cleanup() { let mut proposer_factory = DummyFactory { client: client.clone(), - config: data.link.config.clone(), epoch_changes: data.link.epoch_changes.clone(), mutator: Arc::new(|_, _| ()), }; @@ -1168,7 +1119,6 @@ async fn allows_skipping_epochs() { let mut proposer_factory = DummyFactory { client: client.clone(), - config: data.link.config.clone(), epoch_changes: data.link.epoch_changes.clone(), mutator: Arc::new(|_, _| ()), }; @@ -1298,7 +1248,6 @@ async fn allows_skipping_epochs_on_some_forks() { let mut proposer_factory = DummyFactory { client: client.clone(), - config: data.link.config.clone(), epoch_changes: data.link.epoch_changes.clone(), mutator: Arc::new(|_, _| ()), }; diff --git a/client/consensus/beefy/src/tests.rs b/client/consensus/beefy/src/tests.rs index 48ecebdac..288a9fde5 100644 --- a/client/consensus/beefy/src/tests.rs +++ b/client/consensus/beefy/src/tests.rs @@ -66,7 +66,7 @@ use sp_runtime::{ BuildStorage, DigestItem, EncodedJustification, Justifications, Storage, }; use std::{marker::PhantomData, sync::Arc, task::Poll}; -use substrate_test_runtime_client::{runtime::Header, ClientExt}; +use substrate_test_runtime_client::{BlockBuilderExt, ClientExt}; use tokio::time::Duration; const GENESIS_HASH: H256 = H256::zero(); @@ -165,20 +165,22 @@ impl BeefyTestNet { // push genesis to make indexing human readable (index equals to block number) all_hashes.push(self.peer(0).client().info().genesis_hash); - let built_hashes = self.peer(0).generate_blocks(count, BlockOrigin::File, |builder| { - let mut block = builder.build().unwrap().block; - + let mut block_num: NumberFor = self.peer(0).client().info().best_number; + let built_hashes = self.peer(0).generate_blocks(count, BlockOrigin::File, |mut builder| { + block_num = block_num.saturating_add(1).try_into().unwrap(); if include_mmr_digest { - let block_num = *block.header.number(); let num_byte = block_num.to_le_bytes().into_iter().next().unwrap(); let mmr_root = MmrRootHash::repeat_byte(num_byte); - add_mmr_digest(&mut block.header, mmr_root); + add_mmr_digest(&mut builder, mmr_root); } - if *block.header.number() % session_length == 0 { - add_auth_change_digest(&mut block.header, validator_set.clone()); + if block_num % session_length == 0 { + add_auth_change_digest(&mut builder, validator_set.clone()); } + let block = builder.build().unwrap().block; + assert_eq!(block.header.number, block_num); + block }); all_hashes.extend(built_hashes); @@ -325,18 +327,22 @@ sp_api::mock_impl_runtime_apis! { } } -fn add_mmr_digest(header: &mut Header, mmr_hash: MmrRootHash) { - header.digest_mut().push(DigestItem::Consensus( - BEEFY_ENGINE_ID, - ConsensusLog::::MmrRoot(mmr_hash).encode(), - )); +fn add_mmr_digest(builder: &mut impl BlockBuilderExt, mmr_hash: MmrRootHash) { + builder + .push_deposit_log_digest_item(DigestItem::Consensus( + BEEFY_ENGINE_ID, + ConsensusLog::::MmrRoot(mmr_hash).encode(), + )) + .unwrap(); } -fn add_auth_change_digest(header: &mut Header, new_auth_set: BeefyValidatorSet) { - header.digest_mut().push(DigestItem::Consensus( - BEEFY_ENGINE_ID, - ConsensusLog::::AuthoritiesChange(new_auth_set).encode(), - )); +fn add_auth_change_digest(builder: &mut impl BlockBuilderExt, new_auth_set: BeefyValidatorSet) { + builder + .push_deposit_log_digest_item(DigestItem::Consensus( + BEEFY_ENGINE_ID, + ConsensusLog::::AuthoritiesChange(new_auth_set).encode(), + )) + .unwrap(); } pub(crate) fn make_beefy_ids(keys: &[BeefyKeyring]) -> Vec { diff --git a/client/consensus/grandpa/src/tests.rs b/client/consensus/grandpa/src/tests.rs index 7a3f862d3..c46e249be 100644 --- a/client/consensus/grandpa/src/tests.rs +++ b/client/consensus/grandpa/src/tests.rs @@ -48,7 +48,7 @@ use sp_runtime::{ Justifications, }; use std::{collections::HashSet, pin::Pin}; -use substrate_test_runtime_client::runtime::BlockNumber; +use substrate_test_runtime_client::{runtime::BlockNumber, BlockBuilderExt}; use tokio::runtime::Handle; use authorities::AuthoritySet; @@ -399,22 +399,27 @@ async fn run_to_completion( run_to_completion_with(blocks, net, peers, |_| None).await } -fn add_scheduled_change(block: &mut Block, change: ScheduledChange) { - block.header.digest_mut().push(DigestItem::Consensus( - GRANDPA_ENGINE_ID, - sp_consensus_grandpa::ConsensusLog::ScheduledChange(change).encode(), - )); +fn add_scheduled_change(builder: &mut impl BlockBuilderExt, change: ScheduledChange) { + builder + .push_deposit_log_digest_item(DigestItem::Consensus( + GRANDPA_ENGINE_ID, + sp_consensus_grandpa::ConsensusLog::ScheduledChange(change).encode(), + )) + .unwrap(); } fn add_forced_change( - block: &mut Block, + builder: &mut impl BlockBuilderExt, median_last_finalized: BlockNumber, change: ScheduledChange, ) { - block.header.digest_mut().push(DigestItem::Consensus( - GRANDPA_ENGINE_ID, - sp_consensus_grandpa::ConsensusLog::ForcedChange(median_last_finalized, change).encode(), - )); + builder + .push_deposit_log_digest_item(DigestItem::Consensus( + GRANDPA_ENGINE_ID, + sp_consensus_grandpa::ConsensusLog::ForcedChange(median_last_finalized, change) + .encode(), + )) + .unwrap(); } #[tokio::test] @@ -605,28 +610,24 @@ async fn transition_3_voters_twice_1_full_observer() { }, 14 => { // generate transition at block 15, applied at 20. - net.lock().peer(0).generate_blocks(1, BlockOrigin::File, |builder| { - let mut block = builder.build().unwrap().block; + net.lock().peer(0).generate_blocks(1, BlockOrigin::File, |mut builder| { add_scheduled_change( - &mut block, + &mut builder, ScheduledChange { next_authorities: make_ids(peers_b), delay: 4 }, ); - - block + builder.build().unwrap().block }); net.lock().peer(0).push_blocks(5, false); }, 20 => { // at block 21 we do another transition, but this time instant. // add more until we have 30. - net.lock().peer(0).generate_blocks(1, BlockOrigin::File, |builder| { - let mut block = builder.build().unwrap().block; + net.lock().peer(0).generate_blocks(1, BlockOrigin::File, |mut builder| { add_scheduled_change( - &mut block, + &mut builder, ScheduledChange { next_authorities: make_ids(&peers_c), delay: 0 }, ); - - block + builder.build().unwrap().block }); net.lock().peer(0).push_blocks(9, false); }, @@ -708,13 +709,12 @@ async fn sync_justifications_on_change_blocks() { // at block 21 we do add a transition which is instant let hashof21 = net .peer(0) - .generate_blocks(1, BlockOrigin::File, |builder| { - let mut block = builder.build().unwrap().block; + .generate_blocks(1, BlockOrigin::File, |mut builder| { add_scheduled_change( - &mut block, + &mut builder, ScheduledChange { next_authorities: make_ids(peers_b), delay: 0 }, ); - block + builder.build().unwrap().block }) .pop() .unwrap(); @@ -778,26 +778,24 @@ async fn finalizes_multiple_pending_changes_in_order() { net.peer(0).push_blocks(20, false); // at block 21 we do add a transition which is instant - net.peer(0).generate_blocks(1, BlockOrigin::File, |builder| { - let mut block = builder.build().unwrap().block; + net.peer(0).generate_blocks(1, BlockOrigin::File, |mut builder| { add_scheduled_change( - &mut block, + &mut builder, ScheduledChange { next_authorities: make_ids(peers_b), delay: 0 }, ); - block + builder.build().unwrap().block }); // add more blocks on top of it (until we have 25) net.peer(0).push_blocks(4, false); // at block 26 we add another which is enacted at block 30 - net.peer(0).generate_blocks(1, BlockOrigin::File, |builder| { - let mut block = builder.build().unwrap().block; + net.peer(0).generate_blocks(1, BlockOrigin::File, |mut builder| { add_scheduled_change( - &mut block, + &mut builder, ScheduledChange { next_authorities: make_ids(peers_c), delay: 4 }, ); - block + builder.build().unwrap().block }); // add more blocks on top of it (until we have 30) @@ -833,23 +831,21 @@ async fn force_change_to_new_set() { let voters_future = initialize_grandpa(&mut net, peers_a); let net = Arc::new(Mutex::new(net)); - net.lock().peer(0).generate_blocks(1, BlockOrigin::File, |builder| { - let mut block = builder.build().unwrap().block; - + net.lock().peer(0).generate_blocks(1, BlockOrigin::File, |mut builder| { // add a forced transition at block 12. add_forced_change( - &mut block, + &mut builder, 0, ScheduledChange { next_authorities: voters.clone(), delay: 10 }, ); // add a normal transition too to ensure that forced changes take priority. add_scheduled_change( - &mut block, + &mut builder, ScheduledChange { next_authorities: make_ids(genesis_authorities), delay: 5 }, ); - block + builder.build().unwrap().block }); net.lock().peer(0).push_blocks(25, false); @@ -885,14 +881,15 @@ async fn allows_reimporting_change_blocks() { let (mut block_import, ..) = net.make_block_import(client.clone()); let full_client = client.as_client(); - let builder = full_client + let mut builder = full_client .new_block_at(full_client.chain_info().genesis_hash, Default::default(), false) .unwrap(); - let mut block = builder.build().unwrap().block; + add_scheduled_change( - &mut block, + &mut builder, ScheduledChange { next_authorities: make_ids(peers_b), delay: 0 }, ); + let block = builder.build().unwrap().block; let block = || { let block = block.clone(); @@ -929,16 +926,17 @@ async fn test_bad_justification() { let (mut block_import, ..) = net.make_block_import(client.clone()); let full_client = client.as_client(); - let builder = full_client + let mut builder = full_client .new_block_at(full_client.chain_info().genesis_hash, Default::default(), false) .unwrap(); - let mut block = builder.build().unwrap().block; add_scheduled_change( - &mut block, + &mut builder, ScheduledChange { next_authorities: make_ids(peers_b), delay: 0 }, ); + let block = builder.build().unwrap().block; + let block = || { let block = block.clone(); let mut import = BlockImportParams::new(BlockOrigin::File, block.header); @@ -1629,6 +1627,7 @@ async fn grandpa_environment_passes_actual_best_block_to_voting_rules() { #[tokio::test] async fn grandpa_environment_checks_if_best_block_is_descendent_of_finality_target() { + sp_tracing::try_init_simple(); use finality_grandpa::voter::Environment; let peers = &[Ed25519Keyring::Alice]; @@ -1662,10 +1661,9 @@ async fn grandpa_environment_checks_if_best_block_is_descendent_of_finality_targ BlockId::Number(4), 6, BlockOrigin::File, - |builder| { - let mut block = builder.build().unwrap().block; - block.header.digest_mut().push(DigestItem::Other(vec![1])); - block + |mut builder| { + builder.push_deposit_log_digest_item(DigestItem::Other(vec![1])).unwrap(); + builder.build().unwrap().block }, false, false, @@ -1999,13 +1997,12 @@ async fn revert_prunes_authority_changes() { type TestBlockBuilder<'a> = BlockBuilder<'a, Block, PeersFullClient, substrate_test_runtime_client::Backend>; - let edit_block = |builder: TestBlockBuilder| { - let mut block = builder.build().unwrap().block; + let edit_block = |mut builder: TestBlockBuilder| { add_scheduled_change( - &mut block, + &mut builder, ScheduledChange { next_authorities: make_ids(peers), delay: 0 }, ); - block + builder.build().unwrap().block }; let api = TestApi::new(make_ids(peers)); @@ -2047,10 +2044,9 @@ async fn revert_prunes_authority_changes() { BlockId::Number(23), 3, BlockOrigin::File, - |builder| { - let mut block = builder.build().unwrap().block; - block.header.digest_mut().push(DigestItem::Other(vec![1])); - block + |mut builder| { + builder.push_deposit_log_digest_item(DigestItem::Other(vec![1])).unwrap(); + builder.build().unwrap().block }, false, false, @@ -2079,10 +2075,9 @@ async fn revert_prunes_authority_changes() { BlockId::Number(25), 3, BlockOrigin::File, - |builder| { - let mut block = builder.build().unwrap().block; - block.header.digest_mut().push(DigestItem::Other(vec![2])); - block + |mut builder| { + builder.push_deposit_log_digest_item(DigestItem::Other(vec![2])).unwrap(); + builder.build().unwrap().block }, false, false, diff --git a/client/consensus/grandpa/src/warp_proof.rs b/client/consensus/grandpa/src/warp_proof.rs index cd4fedf96..ec2d25c32 100644 --- a/client/consensus/grandpa/src/warp_proof.rs +++ b/client/consensus/grandpa/src/warp_proof.rs @@ -326,11 +326,10 @@ mod tests { use sp_consensus::BlockOrigin; use sp_consensus_grandpa::GRANDPA_ENGINE_ID; use sp_keyring::Ed25519Keyring; - use sp_runtime::traits::Header as _; use std::sync::Arc; use substrate_test_runtime_client::{ - ClientBlockImportExt, ClientExt, DefaultTestClientBuilderExt, TestClientBuilder, - TestClientBuilderExt, + BlockBuilderExt, ClientBlockImportExt, ClientExt, DefaultTestClientBuilderExt, + TestClientBuilder, TestClientBuilderExt, }; #[test] @@ -348,8 +347,7 @@ mod tests { let mut authority_set_changes = Vec::new(); for n in 1..=100 { - let mut block = client.new_block(Default::default()).unwrap().build().unwrap().block; - + let mut builder = client.new_block(Default::default()).unwrap(); let mut new_authorities = None; // we will trigger an authority set change every 10 blocks @@ -376,9 +374,11 @@ mod tests { .encode(), ); - block.header.digest_mut().logs.push(digest); + builder.push_deposit_log_digest_item(digest).unwrap(); } + let block = builder.build().unwrap().block; + futures::executor::block_on(client.import(BlockOrigin::Own, block)).unwrap(); if let Some(new_authorities) = new_authorities { diff --git a/client/network/bitswap/src/lib.rs b/client/network/bitswap/src/lib.rs index 5a7a7b513..db73023bd 100644 --- a/client/network/bitswap/src/lib.rs +++ b/client/network/bitswap/src/lib.rs @@ -297,7 +297,7 @@ mod tests { }; use sp_consensus::BlockOrigin; use sp_runtime::codec::Encode; - use substrate_test_runtime::Extrinsic; + use substrate_test_runtime::ExtrinsicBuilder; use substrate_test_runtime_client::{self, prelude::*, TestClientBuilder}; #[tokio::test] @@ -470,7 +470,9 @@ mod tests { let mut client = TestClientBuilder::with_tx_storage(u32::MAX).build(); let mut block_builder = client.new_block(Default::default()).unwrap(); - let ext = Extrinsic::Store(vec![0x13, 0x37, 0x13, 0x38]); + // encoded extrinsic: [161, .. , 2, 6, 16, 19, 55, 19, 56] + let ext = ExtrinsicBuilder::new_indexed_call(vec![0x13, 0x37, 0x13, 0x38]).build(); + let pattern_index = ext.encoded_size() - 4; block_builder.push(ext.clone()).unwrap(); let block = block_builder.build().unwrap().block; @@ -494,7 +496,7 @@ mod tests { 0x70, cid::multihash::Multihash::wrap( u64::from(cid::multihash::Code::Blake2b256), - &sp_core::hashing::blake2_256(&ext.encode()[2..]), + &sp_core::hashing::blake2_256(&ext.encode()[pattern_index..]), ) .unwrap(), ) diff --git a/client/network/test/src/lib.rs b/client/network/test/src/lib.rs index f85d6ed63..9e740d085 100644 --- a/client/network/test/src/lib.rs +++ b/client/network/test/src/lib.rs @@ -88,13 +88,11 @@ use sp_runtime::{ }; use substrate_test_runtime_client::AccountKeyring; pub use substrate_test_runtime_client::{ - runtime::{Block, Extrinsic, Hash, Header, Transfer}, + runtime::{Block, ExtrinsicBuilder, Hash, Header, Transfer}, TestClient, TestClientBuilder, TestClientBuilderExt, }; use tokio::time::timeout; -type AuthorityId = sp_consensus_babe::AuthorityId; - /// A Verifier that accepts all blocks and passes them on with the configured /// finality to be imported. #[derive(Clone)] @@ -474,7 +472,7 @@ where amount: 1, nonce, }; - builder.push(transfer.into_signed_tx()).unwrap(); + builder.push(transfer.into_unchecked_extrinsic()).unwrap(); nonce += 1; builder.build().unwrap().block }, @@ -497,16 +495,6 @@ where } } - pub fn push_authorities_change_block( - &mut self, - new_authorities: Vec, - ) -> Vec { - self.generate_blocks(1, BlockOrigin::File, |mut builder| { - builder.push(Extrinsic::AuthoritiesChange(new_authorities.clone())).unwrap(); - builder.build().unwrap().block - }) - } - /// Get a reference to the client. pub fn client(&self) -> &PeersClient { &self.client diff --git a/client/network/test/src/sync.rs b/client/network/test/src/sync.rs index af46d15a2..81707445d 100644 --- a/client/network/test/src/sync.rs +++ b/client/network/test/src/sync.rs @@ -1183,7 +1183,7 @@ async fn syncs_indexed_blocks() { 64, BlockOrigin::Own, |mut builder| { - let ex = Extrinsic::Store(n.to_le_bytes().to_vec()); + let ex = ExtrinsicBuilder::new_indexed_call(n.to_le_bytes().to_vec()).nonce(n).build(); n += 1; builder.push(ex).unwrap(); builder.build().unwrap().block @@ -1305,11 +1305,13 @@ async fn syncs_huge_blocks() { builder.build().unwrap().block }); + let mut nonce = 0; net.peer(0).generate_blocks(32, BlockOrigin::Own, |mut builder| { // Add 32 extrinsics 32k each = 1MiB total - for _ in 0..32 { - let ex = Extrinsic::IncludeData([42u8; 32 * 1024].to_vec()); + for _ in 0..32u64 { + let ex = ExtrinsicBuilder::new_include_data(vec![42u8; 32 * 1024]).nonce(nonce).build(); builder.push(ex).unwrap(); + nonce += 1; } builder.build().unwrap().block }); diff --git a/client/offchain/src/lib.rs b/client/offchain/src/lib.rs index 677d89267..f46fb637a 100644 --- a/client/offchain/src/lib.rs +++ b/client/offchain/src/lib.rs @@ -254,8 +254,10 @@ mod tests { use sp_runtime::generic::BlockId; use std::{collections::HashSet, sync::Arc}; use substrate_test_runtime_client::{ - runtime::Block, ClientBlockImportExt, DefaultTestClientBuilderExt, TestClient, - TestClientBuilderExt, + runtime::{ + substrate_test_pallet::pallet::Call as PalletCall, Block, ExtrinsicBuilder, RuntimeCall, + }, + ClientBlockImportExt, DefaultTestClientBuilderExt, TestClient, TestClientBuilderExt, }; struct TestNetwork(); @@ -385,7 +387,10 @@ mod tests { // then assert_eq!(pool.0.status().ready, 1); - assert_eq!(pool.0.ready().next().unwrap().is_propagable(), false); + assert!(matches!( + pool.0.ready().next().unwrap().data().function, + RuntimeCall::SubstrateTest(PalletCall::storage_change { .. }) + )); } #[test] @@ -403,12 +408,8 @@ mod tests { let key = &b"hello"[..]; let value = &b"world"[..]; let mut block_builder = client.new_block(Default::default()).unwrap(); - block_builder - .push(substrate_test_runtime_client::runtime::Extrinsic::OffchainIndexSet( - key.to_vec(), - value.to_vec(), - )) - .unwrap(); + let ext = ExtrinsicBuilder::new_offchain_index_set(key.to_vec(), value.to_vec()).build(); + block_builder.push(ext).unwrap(); let block = block_builder.build().unwrap().block; block_on(client.import(BlockOrigin::Own, block)).unwrap(); @@ -416,11 +417,8 @@ mod tests { assert_eq!(value, &offchain_db.get(sp_offchain::STORAGE_PREFIX, &key).unwrap()); let mut block_builder = client.new_block(Default::default()).unwrap(); - block_builder - .push(substrate_test_runtime_client::runtime::Extrinsic::OffchainIndexClear( - key.to_vec(), - )) - .unwrap(); + let ext = ExtrinsicBuilder::new_offchain_index_clear(key.to_vec()).nonce(1).build(); + block_builder.push(ext).unwrap(); let block = block_builder.build().unwrap().block; block_on(client.import(BlockOrigin::Own, block)).unwrap(); diff --git a/client/rpc-spec-v2/src/chain_head/tests.rs b/client/rpc-spec-v2/src/chain_head/tests.rs index 76644ccb4..d3d4fc649 100644 --- a/client/rpc-spec-v2/src/chain_head/tests.rs +++ b/client/rpc-spec-v2/src/chain_head/tests.rs @@ -184,12 +184,13 @@ async fn follow_with_runtime() { // Initialized must always be reported first. let event: FollowEvent = get_next_event(&mut sub).await; + // it is basically json-encoded substrate_test_runtime_client::runtime::VERSION let runtime_str = "{\"specName\":\"test\",\"implName\":\"parity-test\",\"authoringVersion\":1,\ \"specVersion\":2,\"implVersion\":2,\"apis\":[[\"0xdf6acb689907609b\",4],\ [\"0x37e397fc7c91f5e4\",2],[\"0xd2bc9897eed08f15\",3],[\"0x40fe3ad401f8959a\",6],\ - [\"0xc6e9a76309f39b09\",1],[\"0xdd718d5cc53262d4\",1],[\"0xcbca25e39f142387\",2],\ - [\"0xf78b278be53f454c\",2],[\"0xab3c0572291feb8b\",1],[\"0xbc9d89904f5b923f\",1]],\ - \"transactionVersion\":1,\"stateVersion\":1}"; + [\"0xbc9d89904f5b923f\",1],[\"0xc6e9a76309f39b09\",2],[\"0xdd718d5cc53262d4\",1],\ + [\"0xcbca25e39f142387\",2],[\"0xf78b278be53f454c\",2],[\"0xab3c0572291feb8b\",1],\ + [\"0xed99c5acb25eedf5\",3]],\"transactionVersion\":1,\"stateVersion\":1}"; let runtime: RuntimeVersion = serde_json::from_str(runtime_str).unwrap(); let finalized_block_runtime = diff --git a/client/rpc/src/author/tests.rs b/client/rpc/src/author/tests.rs index fbbd1a92f..1f688e8e8 100644 --- a/client/rpc/src/author/tests.rs +++ b/client/rpc/src/author/tests.rs @@ -37,10 +37,11 @@ use sp_core::{ H256, }; use sp_keystore::{testing::MemoryKeystore, Keystore}; +use sp_runtime::Perbill; use std::sync::Arc; use substrate_test_runtime_client::{ self, - runtime::{Block, Extrinsic, SessionKeys, Transfer}, + runtime::{Block, Extrinsic, ExtrinsicBuilder, SessionKeys, Transfer}, AccountKeyring, Backend, Client, DefaultTestClientBuilderExt, TestClientBuilderExt, }; @@ -51,7 +52,7 @@ fn uxt(sender: AccountKeyring, nonce: u64) -> Extrinsic { from: sender.into(), to: AccountKeyring::Bob.into(), }; - tx.into_signed_tx() + ExtrinsicBuilder::new_transfer(tx).build() } type FullTransactionPool = BasicPool, Block>, Block>; @@ -111,7 +112,13 @@ async fn author_submit_transaction_should_not_cause_error() { #[tokio::test] async fn author_should_watch_extrinsic() { let api = TestSetup::into_rpc(); - let xt = to_hex(&uxt(AccountKeyring::Alice, 0).encode(), true); + let xt = to_hex( + &ExtrinsicBuilder::new_call_with_priority(0) + .signer(AccountKeyring::Alice.into()) + .build() + .encode(), + true, + ); let mut sub = api.subscribe("author_submitAndWatchExtrinsic", [xt]).await.unwrap(); let (tx, sub_id) = timeout_secs(10, sub.next::>()) @@ -125,15 +132,11 @@ async fn author_should_watch_extrinsic() { // Replace the extrinsic and observe the subscription is notified. let (xt_replacement, xt_hash) = { - let tx = Transfer { - amount: 5, - nonce: 0, - from: AccountKeyring::Alice.into(), - to: AccountKeyring::Bob.into(), - }; - let tx = tx.into_signed_tx().encode(); + let tx = ExtrinsicBuilder::new_call_with_priority(1) + .signer(AccountKeyring::Alice.into()) + .build() + .encode(); let hash = blake2_256(&tx); - (to_hex(&tx, true), hash) }; @@ -152,10 +155,10 @@ async fn author_should_watch_extrinsic() { async fn author_should_return_watch_validation_error() { const METHOD: &'static str = "author_submitAndWatchExtrinsic"; + let invalid_xt = ExtrinsicBuilder::new_fill_block(Perbill::from_percent(100)).build(); + let api = TestSetup::into_rpc(); - let failed_sub = api - .subscribe(METHOD, [to_hex(&uxt(AccountKeyring::Alice, 179).encode(), true)]) - .await; + let failed_sub = api.subscribe(METHOD, [to_hex(&invalid_xt.encode(), true)]).await; assert_matches!( failed_sub, diff --git a/client/rpc/src/dev/tests.rs b/client/rpc/src/dev/tests.rs index 9beb01182..db6a9a119 100644 --- a/client/rpc/src/dev/tests.rs +++ b/client/rpc/src/dev/tests.rs @@ -28,6 +28,22 @@ async fn block_stats_work() { let api = >::new(client.clone(), DenyUnsafe::No).into_rpc(); let block = client.new_block(Default::default()).unwrap().build().unwrap().block; + + let (expected_witness_len, expected_witness_compact_len, expected_block_len) = { + let genesis_hash = client.chain_info().genesis_hash; + let mut runtime_api = client.runtime_api(); + runtime_api.record_proof(); + runtime_api.execute_block(genesis_hash, block.clone()).unwrap(); + let witness = runtime_api.extract_proof().unwrap(); + let pre_root = *client.header(genesis_hash).unwrap().unwrap().state_root(); + + ( + witness.clone().encoded_size() as u64, + witness.into_compact_proof::>(pre_root).unwrap().encoded_size() as u64, + block.encoded_size() as u64, + ) + }; + client.import(BlockOrigin::Own, block).await.unwrap(); // Can't gather stats for a block without a parent. @@ -43,9 +59,9 @@ async fn block_stats_work() { .await .unwrap(), Some(BlockStats { - witness_len: 630, - witness_compact_len: 534, - block_len: 99, + witness_len: expected_witness_len, + witness_compact_len: expected_witness_compact_len, + block_len: expected_block_len, num_extrinsics: 0, }), ); diff --git a/client/rpc/src/state/tests.rs b/client/rpc/src/state/tests.rs index ae193e662..9e00a04ab 100644 --- a/client/rpc/src/state/tests.rs +++ b/client/rpc/src/state/tests.rs @@ -29,9 +29,11 @@ use sc_block_builder::BlockBuilderProvider; use sc_rpc_api::DenyUnsafe; use sp_consensus::BlockOrigin; use sp_core::{hash::H256, storage::ChildInfo}; -use sp_io::hashing::blake2_256; use std::sync::Arc; -use substrate_test_runtime_client::{prelude::*, runtime}; +use substrate_test_runtime_client::{ + prelude::*, + runtime::{ExtrinsicBuilder, Transfer}, +}; const STORAGE_KEY: &[u8] = b"child"; @@ -218,7 +220,7 @@ async fn should_notify_about_storage_changes() { // Cause a change: let mut builder = client.new_block(Default::default()).unwrap(); builder - .push_transfer(runtime::Transfer { + .push_transfer(Transfer { from: AccountKeyring::Alice.into(), to: AccountKeyring::Ferdie.into(), amount: 42, @@ -244,18 +246,26 @@ async fn should_send_initial_storage_changes_and_notifications() { let mut client = Arc::new(substrate_test_runtime_client::new()); let (api, _child) = new_full(client.clone(), test_executor(), DenyUnsafe::No); - let alice_balance_key = - blake2_256(&runtime::system::balance_of_key(AccountKeyring::Alice.into())); + let alice_balance_key = [ + sp_core::hashing::twox_128(b"System"), + sp_core::hashing::twox_128(b"Account"), + sp_core::hashing::blake2_128(&AccountKeyring::Alice.public()), + ] + .concat() + .iter() + .chain(AccountKeyring::Alice.public().0.iter()) + .cloned() + .collect::>(); let api_rpc = api.into_rpc(); let sub = api_rpc - .subscribe("state_subscribeStorage", [[StorageKey(alice_balance_key.to_vec())]]) + .subscribe("state_subscribeStorage", [[StorageKey(alice_balance_key)]]) .await .unwrap(); let mut builder = client.new_block(Default::default()).unwrap(); builder - .push_transfer(runtime::Transfer { + .push_transfer(Transfer { from: AccountKeyring::Alice.into(), to: AccountKeyring::Ferdie.into(), amount: 42, @@ -280,22 +290,42 @@ async fn should_query_storage() { async fn run_tests(mut client: Arc) { let (api, _child) = new_full(client.clone(), test_executor(), DenyUnsafe::No); - let mut add_block = |nonce| { + let mut add_block = |index| { let mut builder = client.new_block(Default::default()).unwrap(); // fake change: None -> None -> None - builder.push_storage_change(vec![1], None).unwrap(); + builder + .push(ExtrinsicBuilder::new_storage_change(vec![1], None).build()) + .unwrap(); // fake change: None -> Some(value) -> Some(value) - builder.push_storage_change(vec![2], Some(vec![2])).unwrap(); + builder + .push(ExtrinsicBuilder::new_storage_change(vec![2], Some(vec![2])).build()) + .unwrap(); // actual change: None -> Some(value) -> None builder - .push_storage_change(vec![3], if nonce == 0 { Some(vec![3]) } else { None }) + .push( + ExtrinsicBuilder::new_storage_change( + vec![3], + if index == 0 { Some(vec![3]) } else { None }, + ) + .build(), + ) .unwrap(); // actual change: None -> Some(value) builder - .push_storage_change(vec![4], if nonce == 0 { None } else { Some(vec![4]) }) + .push( + ExtrinsicBuilder::new_storage_change( + vec![4], + if index == 0 { None } else { Some(vec![4]) }, + ) + .build(), + ) .unwrap(); // actual change: Some(value1) -> Some(value2) - builder.push_storage_change(vec![5], Some(vec![nonce as u8])).unwrap(); + builder + .push( + ExtrinsicBuilder::new_storage_change(vec![5], Some(vec![index as u8])).build(), + ) + .unwrap(); let block = builder.build().unwrap().block; let hash = block.header.hash(); executor::block_on(client.import(BlockOrigin::Own, block)).unwrap(); @@ -482,12 +512,13 @@ async fn should_return_runtime_version() { let client = Arc::new(substrate_test_runtime_client::new()); let (api, _child) = new_full(client.clone(), test_executor(), DenyUnsafe::No); + // it is basically json-encoded substrate_test_runtime_client::runtime::VERSION let result = "{\"specName\":\"test\",\"implName\":\"parity-test\",\"authoringVersion\":1,\ \"specVersion\":2,\"implVersion\":2,\"apis\":[[\"0xdf6acb689907609b\",4],\ [\"0x37e397fc7c91f5e4\",2],[\"0xd2bc9897eed08f15\",3],[\"0x40fe3ad401f8959a\",6],\ - [\"0xc6e9a76309f39b09\",1],[\"0xdd718d5cc53262d4\",1],[\"0xcbca25e39f142387\",2],\ - [\"0xf78b278be53f454c\",2],[\"0xab3c0572291feb8b\",1],[\"0xbc9d89904f5b923f\",1]],\ - \"transactionVersion\":1,\"stateVersion\":1}"; + [\"0xbc9d89904f5b923f\",1],[\"0xc6e9a76309f39b09\",2],[\"0xdd718d5cc53262d4\",1],\ + [\"0xcbca25e39f142387\",2],[\"0xf78b278be53f454c\",2],[\"0xab3c0572291feb8b\",1],\ + [\"0xed99c5acb25eedf5\",3]],\"transactionVersion\":1,\"stateVersion\":1}"; let runtime_version = api.runtime_version(None.into()).unwrap(); let serialized = serde_json::to_string(&runtime_version).unwrap(); diff --git a/client/service/src/lib.rs b/client/service/src/lib.rs index a56677170..b90eb71c1 100644 --- a/client/service/src/lib.rs +++ b/client/service/src/lib.rs @@ -520,10 +520,9 @@ mod tests { use futures::executor::block_on; use sc_transaction_pool::BasicPool; use sp_consensus::SelectChain; - use sp_runtime::traits::BlindCheckable; use substrate_test_runtime_client::{ prelude::*, - runtime::{Extrinsic, Transfer}, + runtime::{ExtrinsicBuilder, Transfer, TransferData}, }; #[test] @@ -542,13 +541,13 @@ mod tests { from: AccountKeyring::Alice.into(), to: AccountKeyring::Bob.into(), } - .into_signed_tx(); + .into_unchecked_extrinsic(); block_on(pool.submit_one(&BlockId::hash(best.hash()), source, transaction.clone())) .unwrap(); block_on(pool.submit_one( &BlockId::hash(best.hash()), source, - Extrinsic::IncludeData(vec![1]), + ExtrinsicBuilder::new_call_do_not_propagate().nonce(1).build(), )) .unwrap(); assert_eq!(pool.status().ready, 2); @@ -558,8 +557,6 @@ mod tests { // then assert_eq!(transactions.len(), 1); - assert!(transactions[0].1.clone().check().is_ok()); - // this should not panic - let _ = transactions[0].1.transfer(); + assert!(TransferData::try_from(&transactions[0].1).is_ok()); } } diff --git a/client/service/test/src/client/mod.rs b/client/service/test/src/client/mod.rs index 520a9b52f..a33bce50a 100644 --- a/client/service/test/src/client/mod.rs +++ b/client/service/test/src/client/mod.rs @@ -48,7 +48,8 @@ use substrate_test_runtime_client::{ new_native_or_wasm_executor, prelude::*, runtime::{ - genesismap::{insert_genesis_block, GenesisConfig}, + currency::DOLLARS, + genesismap::{insert_genesis_block, GenesisStorageBuilder}, Block, BlockNumber, Digest, Hash, Header, RuntimeApi, Transfer, }, AccountKeyring, BlockBuilderExt, ClientBlockImportExt, ClientExt, DefaultTestClientBuilderExt, @@ -66,7 +67,7 @@ fn construct_block( state_root: Hash, txs: Vec, ) -> (Vec, Hash) { - let transactions = txs.into_iter().map(|tx| tx.into_signed_tx()).collect::>(); + let transactions = txs.into_iter().map(|tx| tx.into_unchecked_extrinsic()).collect::>(); let iter = transactions.iter().map(Encode::encode); let extrinsics_root = LayoutV0::::ordered_trie_root(iter).into(); @@ -137,9 +138,9 @@ fn block1(genesis_hash: Hash, backend: &InMemoryBackend) -> (Vec = client .storage_keys( @@ -1762,54 +1734,32 @@ fn storage_keys_prefix_and_start_key_works() { #[test] fn storage_keys_works() { - let client = substrate_test_runtime_client::new(); + sp_tracing::try_init_simple(); - let block_hash = client.info().best_hash; + let expected_keys = + substrate_test_runtime::storage_key_generator::get_expected_storage_hashed_keys(); + let client = substrate_test_runtime_client::new(); + let block_hash = client.info().best_hash; let prefix = StorageKey(array_bytes::hex2bytes_unchecked("")); let res: Vec<_> = client .storage_keys(block_hash, Some(&prefix), None) .unwrap() - .take(9) + .take(19) .map(|x| array_bytes::bytes2hex("", &x.0)) .collect(); - assert_eq!( - res, - [ - "00c232cf4e70a5e343317016dc805bf80a6a8cd8ad39958d56f99891b07851e0", - "085b2407916e53a86efeb8b72dbe338c4b341dab135252f96b6ed8022209b6cb", - "0befda6e1ca4ef40219d588a727f1271", - "1a560ecfd2a62c2b8521ef149d0804eb621050e3988ed97dca55f0d7c3e6aa34", - "1d66850d32002979d67dd29dc583af5b2ae2a1f71c1f35ad90fff122be7a3824", - "237498b98d8803334286e9f0483ef513098dd3c1c22ca21c4dc155b4ef6cc204", - "26aa394eea5630e07c48ae0c9558cef75e0621c4869aa60c02be9adcc98a0d1d", - "29b9db10ec5bf7907d8f74b5e60aa8140c4fbdd8127a1ee5600cb98e5ec01729", - "3a636f6465", - ] - ); + + assert_eq!(res, expected_keys[0..19],); // Starting at an empty key nothing gets skipped. let res: Vec<_> = client .storage_keys(block_hash, Some(&prefix), Some(&StorageKey("".into()))) .unwrap() - .take(9) + .take(19) .map(|x| array_bytes::bytes2hex("", &x.0)) .collect(); - assert_eq!( - res, - [ - "00c232cf4e70a5e343317016dc805bf80a6a8cd8ad39958d56f99891b07851e0", - "085b2407916e53a86efeb8b72dbe338c4b341dab135252f96b6ed8022209b6cb", - "0befda6e1ca4ef40219d588a727f1271", - "1a560ecfd2a62c2b8521ef149d0804eb621050e3988ed97dca55f0d7c3e6aa34", - "1d66850d32002979d67dd29dc583af5b2ae2a1f71c1f35ad90fff122be7a3824", - "237498b98d8803334286e9f0483ef513098dd3c1c22ca21c4dc155b4ef6cc204", - "26aa394eea5630e07c48ae0c9558cef75e0621c4869aa60c02be9adcc98a0d1d", - "29b9db10ec5bf7907d8f74b5e60aa8140c4fbdd8127a1ee5600cb98e5ec01729", - "3a636f6465", - ] - ); + assert_eq!(res, expected_keys[0..19],); // Starting at an incomplete key nothing gets skipped. let res: Vec<_> = client @@ -1824,16 +1774,12 @@ fn storage_keys_works() { .collect(); assert_eq!( res, - [ - "3a636f6465", - "3a686561707061676573", - "52008686cc27f6e5ed83a216929942f8bcd32a396f09664a5698f81371934b56", - "5348d72ac6cc66e5d8cbecc27b0e0677503b845fe2382d819f83001781788fd5", - "5c2d5fda66373dabf970e4fb13d277ce91c5233473321129d32b5a8085fa8133", - "6644b9b8bc315888ac8e41a7968dc2b4141a5403c58acdf70b7e8f7e07bf5081", - "66484000ed3f75c95fc7b03f39c20ca1e1011e5999278247d3b2f5e3c3273808", - "7d5007603a7f5dd729d51d93cf695d6465789443bb967c0d1fe270e388c96eaa", - ] + expected_keys + .iter() + .filter(|&i| i > &"3a636f64".to_string()) + .take(8) + .cloned() + .collect::>() ); // Starting at a complete key the first key is skipped. @@ -1849,38 +1795,33 @@ fn storage_keys_works() { .collect(); assert_eq!( res, - [ - "3a686561707061676573", - "52008686cc27f6e5ed83a216929942f8bcd32a396f09664a5698f81371934b56", - "5348d72ac6cc66e5d8cbecc27b0e0677503b845fe2382d819f83001781788fd5", - "5c2d5fda66373dabf970e4fb13d277ce91c5233473321129d32b5a8085fa8133", - "6644b9b8bc315888ac8e41a7968dc2b4141a5403c58acdf70b7e8f7e07bf5081", - "66484000ed3f75c95fc7b03f39c20ca1e1011e5999278247d3b2f5e3c3273808", - "7d5007603a7f5dd729d51d93cf695d6465789443bb967c0d1fe270e388c96eaa", - ] + expected_keys + .iter() + .filter(|&i| i > &"3a636f6465".to_string()) + .take(8) + .cloned() + .collect::>() ); + const SOME_BALANCE_KEY : &str = "26aa394eea5630e07c48ae0c9558cef7b99d880ec681799c0cf30e8886371da9e2c1dc507e2035edbbd8776c440d870460c57f0008067cc01c5ff9eb2e2f9b3a94299a915a91198bd1021a6c55596f57"; let res: Vec<_> = client .storage_keys( block_hash, Some(&prefix), - Some(&StorageKey(array_bytes::hex2bytes_unchecked( - "7d5007603a7f5dd729d51d93cf695d6465789443bb967c0d1fe270e388c96eaa", - ))), + Some(&StorageKey(array_bytes::hex2bytes_unchecked(SOME_BALANCE_KEY))), ) .unwrap() - .take(5) + .take(8) .map(|x| array_bytes::bytes2hex("", &x.0)) .collect(); assert_eq!( res, - [ - "811ecfaadcf5f2ee1d67393247e2f71a1662d433e8ce7ff89fb0d4aa9561820b", - "a93d74caa7ec34ea1b04ce1e5c090245f867d333f0f88278a451e45299654dc5", - "a9ee1403384afbfc13f13be91ff70bfac057436212e53b9733914382ac942892", - "cf722c0832b5231d35e29f319ff27389f5032bfc7bfc3ba5ed7839f2042fb99f", - "e3b47b6c84c0493481f97c5197d2554f", - ] + expected_keys + .iter() + .filter(|&i| i > &SOME_BALANCE_KEY.to_string()) + .take(8) + .cloned() + .collect::>() ); } @@ -1999,7 +1940,7 @@ fn reorg_triggers_a_notification_even_for_sources_that_should_not_trigger_notifi b1.push_transfer(Transfer { from: AccountKeyring::Alice.into(), to: AccountKeyring::Ferdie.into(), - amount: 1, + amount: 1 * DOLLARS, nonce: 0, }) .unwrap(); diff --git a/client/transaction-pool/benches/basics.rs b/client/transaction-pool/benches/basics.rs index f7739ef73..d114acc34 100644 --- a/client/transaction-pool/benches/basics.rs +++ b/client/transaction-pool/benches/basics.rs @@ -18,7 +18,7 @@ use criterion::{criterion_group, criterion_main, Criterion}; -use codec::{Decode, Encode}; +use codec::Encode; use futures::{ executor::block_on, future::{ready, Ready}, @@ -33,7 +33,7 @@ use sp_runtime::{ ValidTransaction, }, }; -use substrate_test_runtime::{AccountId, Block, Extrinsic, Transfer, H256}; +use substrate_test_runtime::{AccountId, Block, Extrinsic, ExtrinsicBuilder, TransferData, H256}; #[derive(Clone, Debug, Default)] struct TestApi { @@ -65,8 +65,10 @@ impl ChainApi for TestApi { _source: TransactionSource, uxt: ::Extrinsic, ) -> Self::ValidationFuture { - let nonce = uxt.transfer().nonce; - let from = uxt.transfer().from; + let transfer = TransferData::try_from(&uxt) + .expect("uxt is expected to be bench_call (carrying TransferData)"); + let nonce = transfer.nonce; + let from = transfer.from; match self.block_id_to_number(at) { Ok(Some(num)) if num > 5 => return ready(Ok(Err(InvalidTransaction::Stale.into()))), @@ -131,13 +133,8 @@ impl ChainApi for TestApi { } } -fn uxt(transfer: Transfer) -> Extrinsic { - Extrinsic::Transfer { - transfer, - signature: Decode::decode(&mut sp_runtime::traits::TrailingZeroInput::zeroes()) - .expect("infinite input; no dead input space; qed"), - exhaust_resources_when_not_first: false, - } +fn uxt(transfer: TransferData) -> Extrinsic { + ExtrinsicBuilder::new_bench_call(transfer).build() } fn bench_configured(pool: Pool, number: u64) { @@ -146,7 +143,7 @@ fn bench_configured(pool: Pool, number: u64) { let mut tags = Vec::new(); for nonce in 1..=number { - let xt = uxt(Transfer { + let xt = uxt(TransferData { from: AccountId::from_h256(H256::from_low_u64_be(1)), to: AccountId::from_h256(H256::from_low_u64_be(2)), amount: 5, diff --git a/client/transaction-pool/src/graph/pool.rs b/client/transaction-pool/src/graph/pool.rs index 54534df82..4d34737a7 100644 --- a/client/transaction-pool/src/graph/pool.rs +++ b/client/transaction-pool/src/graph/pool.rs @@ -463,7 +463,8 @@ mod tests { use sc_transaction_pool_api::TransactionStatus; use sp_runtime::transaction_validity::TransactionSource; use std::{collections::HashMap, time::Instant}; - use substrate_test_runtime::{AccountId, Extrinsic, Transfer, H256}; + use substrate_test_runtime::{AccountId, ExtrinsicBuilder, Transfer, H256}; + use substrate_test_runtime_client::AccountKeyring::{Alice, Bob}; const SOURCE: TransactionSource = TransactionSource::External; @@ -477,7 +478,7 @@ mod tests { &BlockId::Number(0), SOURCE, uxt(Transfer { - from: AccountId::from_h256(H256::from_low_u64_be(1)), + from: Alice.into(), to: AccountId::from_h256(H256::from_low_u64_be(2)), amount: 5, nonce: 0, @@ -494,7 +495,7 @@ mod tests { // given let pool = pool(); let uxt = uxt(Transfer { - from: AccountId::from_h256(H256::from_low_u64_be(1)), + from: Alice.into(), to: AccountId::from_h256(H256::from_low_u64_be(2)), amount: 5, nonce: 0, @@ -520,8 +521,8 @@ mod tests { TestApi::default().into(), ); - // after validation `IncludeData` will be set to non-propagable - let uxt = Extrinsic::IncludeData(vec![42]); + // after validation `IncludeData` will be set to non-propagable (validate_transaction mock) + let uxt = ExtrinsicBuilder::new_include_data(vec![42]).build(); // when let res = block_on(pool.submit_one(&BlockId::Number(0), SOURCE, uxt)); @@ -542,7 +543,7 @@ mod tests { &BlockId::Number(0), SOURCE, uxt(Transfer { - from: AccountId::from_h256(H256::from_low_u64_be(1)), + from: Alice.into(), to: AccountId::from_h256(H256::from_low_u64_be(2)), amount: 5, nonce: 0, @@ -553,7 +554,7 @@ mod tests { &BlockId::Number(0), SOURCE, uxt(Transfer { - from: AccountId::from_h256(H256::from_low_u64_be(1)), + from: Alice.into(), to: AccountId::from_h256(H256::from_low_u64_be(2)), amount: 5, nonce: 1, @@ -565,7 +566,7 @@ mod tests { &BlockId::Number(0), SOURCE, uxt(Transfer { - from: AccountId::from_h256(H256::from_low_u64_be(1)), + from: Alice.into(), to: AccountId::from_h256(H256::from_low_u64_be(2)), amount: 5, nonce: 3, @@ -594,7 +595,7 @@ mod tests { &BlockId::Number(0), SOURCE, uxt(Transfer { - from: AccountId::from_h256(H256::from_low_u64_be(1)), + from: Alice.into(), to: AccountId::from_h256(H256::from_low_u64_be(2)), amount: 5, nonce: 0, @@ -605,7 +606,7 @@ mod tests { &BlockId::Number(0), SOURCE, uxt(Transfer { - from: AccountId::from_h256(H256::from_low_u64_be(1)), + from: Alice.into(), to: AccountId::from_h256(H256::from_low_u64_be(2)), amount: 5, nonce: 1, @@ -616,7 +617,7 @@ mod tests { &BlockId::Number(0), SOURCE, uxt(Transfer { - from: AccountId::from_h256(H256::from_low_u64_be(1)), + from: Alice.into(), to: AccountId::from_h256(H256::from_low_u64_be(2)), amount: 5, nonce: 3, @@ -645,7 +646,7 @@ mod tests { &BlockId::Number(0), SOURCE, uxt(Transfer { - from: AccountId::from_h256(H256::from_low_u64_be(1)), + from: Alice.into(), to: AccountId::from_h256(H256::from_low_u64_be(2)), amount: 5, nonce: 0, @@ -659,27 +660,27 @@ mod tests { // then assert!(pool.validated_pool.is_banned(&hash1)); } + use codec::Encode; #[test] fn should_limit_futures() { + sp_tracing::try_init_simple(); + + let xt = uxt(Transfer { + from: Alice.into(), + to: AccountId::from_h256(H256::from_low_u64_be(2)), + amount: 5, + nonce: 1, + }); + // given - let limit = Limit { count: 100, total_bytes: 200 }; + let limit = Limit { count: 100, total_bytes: xt.encoded_size() }; let options = Options { ready: limit.clone(), future: limit.clone(), ..Default::default() }; let pool = Pool::new(options, true.into(), TestApi::default().into()); - let hash1 = block_on(pool.submit_one( - &BlockId::Number(0), - SOURCE, - uxt(Transfer { - from: AccountId::from_h256(H256::from_low_u64_be(1)), - to: AccountId::from_h256(H256::from_low_u64_be(2)), - amount: 5, - nonce: 1, - }), - )) - .unwrap(); + let hash1 = block_on(pool.submit_one(&BlockId::Number(0), SOURCE, xt)).unwrap(); assert_eq!(pool.validated_pool().status().future, 1); // when @@ -687,7 +688,7 @@ mod tests { &BlockId::Number(0), SOURCE, uxt(Transfer { - from: AccountId::from_h256(H256::from_low_u64_be(2)), + from: Bob.into(), to: AccountId::from_h256(H256::from_low_u64_be(2)), amount: 5, nonce: 10, @@ -715,7 +716,7 @@ mod tests { &BlockId::Number(0), SOURCE, uxt(Transfer { - from: AccountId::from_h256(H256::from_low_u64_be(1)), + from: Alice.into(), to: AccountId::from_h256(H256::from_low_u64_be(2)), amount: 5, nonce: 1, @@ -738,7 +739,7 @@ mod tests { &BlockId::Number(0), SOURCE, uxt(Transfer { - from: AccountId::from_h256(H256::from_low_u64_be(1)), + from: Alice.into(), to: AccountId::from_h256(H256::from_low_u64_be(2)), amount: 5, nonce: INVALID_NONCE, @@ -763,7 +764,7 @@ mod tests { &BlockId::Number(0), SOURCE, uxt(Transfer { - from: AccountId::from_h256(H256::from_low_u64_be(1)), + from: Alice.into(), to: AccountId::from_h256(H256::from_low_u64_be(2)), amount: 5, nonce: 0, @@ -795,7 +796,7 @@ mod tests { &BlockId::Number(0), SOURCE, uxt(Transfer { - from: AccountId::from_h256(H256::from_low_u64_be(1)), + from: Alice.into(), to: AccountId::from_h256(H256::from_low_u64_be(2)), amount: 5, nonce: 0, @@ -828,7 +829,7 @@ mod tests { &BlockId::Number(0), SOURCE, uxt(Transfer { - from: AccountId::from_h256(H256::from_low_u64_be(1)), + from: Alice.into(), to: AccountId::from_h256(H256::from_low_u64_be(2)), amount: 5, nonce: 1, @@ -843,7 +844,7 @@ mod tests { &BlockId::Number(0), SOURCE, uxt(Transfer { - from: AccountId::from_h256(H256::from_low_u64_be(1)), + from: Alice.into(), to: AccountId::from_h256(H256::from_low_u64_be(2)), amount: 5, nonce: 0, @@ -863,7 +864,7 @@ mod tests { // given let pool = pool(); let uxt = uxt(Transfer { - from: AccountId::from_h256(H256::from_low_u64_be(1)), + from: Alice.into(), to: AccountId::from_h256(H256::from_low_u64_be(2)), amount: 5, nonce: 0, @@ -887,7 +888,7 @@ mod tests { // given let pool = pool(); let uxt = uxt(Transfer { - from: AccountId::from_h256(H256::from_low_u64_be(1)), + from: Alice.into(), to: AccountId::from_h256(H256::from_low_u64_be(2)), amount: 5, nonce: 0, @@ -918,7 +919,7 @@ mod tests { let pool = Pool::new(options, true.into(), TestApi::default().into()); let xt = uxt(Transfer { - from: AccountId::from_h256(H256::from_low_u64_be(1)), + from: Alice.into(), to: AccountId::from_h256(H256::from_low_u64_be(2)), amount: 5, nonce: 0, @@ -928,7 +929,7 @@ mod tests { // when let xt = uxt(Transfer { - from: AccountId::from_h256(H256::from_low_u64_be(2)), + from: Bob.into(), to: AccountId::from_h256(H256::from_low_u64_be(1)), amount: 4, nonce: 1, @@ -952,13 +953,17 @@ mod tests { let pool = Pool::new(options, true.into(), TestApi::default().into()); - let xt = Extrinsic::IncludeData(Vec::new()); + // after validation `IncludeData` will have priority set to 9001 + // (validate_transaction mock) + let xt = ExtrinsicBuilder::new_include_data(Vec::new()).build(); block_on(pool.submit_one(&BlockId::Number(0), SOURCE, xt)).unwrap(); assert_eq!(pool.validated_pool().status().ready, 1); // then + // after validation `Transfer` will have priority set to 4 (validate_transaction + // mock) let xt = uxt(Transfer { - from: AccountId::from_h256(H256::from_low_u64_be(2)), + from: Bob.into(), to: AccountId::from_h256(H256::from_low_u64_be(1)), amount: 4, nonce: 1, @@ -977,12 +982,16 @@ mod tests { let pool = Pool::new(options, true.into(), TestApi::default().into()); - let xt = Extrinsic::IncludeData(Vec::new()); + // after validation `IncludeData` will have priority set to 9001 + // (validate_transaction mock) + let xt = ExtrinsicBuilder::new_include_data(Vec::new()).build(); block_on(pool.submit_and_watch(&BlockId::Number(0), SOURCE, xt)).unwrap(); assert_eq!(pool.validated_pool().status().ready, 1); + // after validation `Transfer` will have priority set to 4 (validate_transaction + // mock) let xt = uxt(Transfer { - from: AccountId::from_h256(H256::from_low_u64_be(1)), + from: Alice.into(), to: AccountId::from_h256(H256::from_low_u64_be(2)), amount: 5, nonce: 0, @@ -992,7 +1001,9 @@ mod tests { assert_eq!(pool.validated_pool().status().ready, 2); // when - let xt = Extrinsic::Store(Vec::new()); + // after validation `Store` will have priority set to 9001 (validate_transaction + // mock) + let xt = ExtrinsicBuilder::new_indexed_call(Vec::new()).build(); block_on(pool.submit_one(&BlockId::Number(1), SOURCE, xt)).unwrap(); assert_eq!(pool.validated_pool().status().ready, 2); @@ -1014,7 +1025,7 @@ mod tests { // when let xt = uxt(Transfer { - from: AccountId::from_h256(H256::from_low_u64_be(1)), + from: Alice.into(), to: AccountId::from_h256(H256::from_low_u64_be(2)), amount: 5, nonce: 1, @@ -1030,7 +1041,7 @@ mod tests { // But now before the previous one is imported we import // the one that it depends on. let xt = uxt(Transfer { - from: AccountId::from_h256(H256::from_low_u64_be(1)), + from: Alice.into(), to: AccountId::from_h256(H256::from_low_u64_be(2)), amount: 4, nonce: 0, diff --git a/client/transaction-pool/src/revalidation.rs b/client/transaction-pool/src/revalidation.rs index bd8f3dd64..b2c41be92 100644 --- a/client/transaction-pool/src/revalidation.rs +++ b/client/transaction-pool/src/revalidation.rs @@ -362,6 +362,7 @@ mod tests { use sc_transaction_pool_api::TransactionSource; use sp_runtime::generic::BlockId; use substrate_test_runtime::{AccountId, Transfer, H256}; + use substrate_test_runtime_client::AccountKeyring::Alice; #[test] fn revalidation_queue_works() { @@ -370,7 +371,7 @@ mod tests { let queue = Arc::new(RevalidationQueue::new(api.clone(), pool.clone())); let uxt = uxt(Transfer { - from: AccountId::from_h256(H256::from_low_u64_be(1)), + from: Alice.into(), to: AccountId::from_h256(H256::from_low_u64_be(2)), amount: 5, nonce: 0, diff --git a/client/transaction-pool/src/tests.rs b/client/transaction-pool/src/tests.rs index 3945c88d4..62911d5cb 100644 --- a/client/transaction-pool/src/tests.rs +++ b/client/transaction-pool/src/tests.rs @@ -31,7 +31,10 @@ use sp_runtime::{ }, }; use std::{collections::HashSet, sync::Arc}; -use substrate_test_runtime::{Block, Extrinsic, Hashing, Transfer, H256}; +use substrate_test_runtime::{ + substrate_test_pallet::pallet::Call as PalletCall, BalancesCall, Block, Extrinsic, + ExtrinsicBuilder, Hashing, RuntimeCall, Transfer, TransferData, H256, +}; pub(crate) const INVALID_NONCE: u64 = 254; @@ -70,9 +73,11 @@ impl ChainApi for TestApi { let block_number = self.block_id_to_number(at).unwrap().unwrap(); let res = match uxt { - Extrinsic::Transfer { transfer, .. } => { - let nonce = transfer.nonce; - + Extrinsic { + function: RuntimeCall::Balances(BalancesCall::transfer_allow_death { .. }), + .. + } => { + let TransferData { nonce, .. } = (&uxt).try_into().unwrap(); // This is used to control the test flow. if nonce > 0 { let opt = self.delay.lock().take(); @@ -115,14 +120,20 @@ impl ChainApi for TestApi { Ok(transaction) } }, - Extrinsic::IncludeData(_) => Ok(ValidTransaction { + Extrinsic { + function: RuntimeCall::SubstrateTest(PalletCall::include_data { .. }), + .. + } => Ok(ValidTransaction { priority: 9001, requires: vec![], provides: vec![vec![42]], longevity: 9001, propagate: false, }), - Extrinsic::Store(_) => Ok(ValidTransaction { + Extrinsic { + function: RuntimeCall::SubstrateTest(PalletCall::indexed_call { .. }), + .. + } => Ok(ValidTransaction { priority: 9001, requires: vec![], provides: vec![vec![43]], @@ -185,8 +196,7 @@ impl ChainApi for TestApi { } pub(crate) fn uxt(transfer: Transfer) -> Extrinsic { - let signature = TryFrom::try_from(&[0; 64][..]).unwrap(); - Extrinsic::Transfer { transfer, signature, exhaust_resources_when_not_first: false } + ExtrinsicBuilder::new_transfer(transfer).build() } pub(crate) fn pool() -> Pool { diff --git a/client/transaction-pool/tests/pool.rs b/client/transaction-pool/tests/pool.rs index 284c777a6..ac029d717 100644 --- a/client/transaction-pool/tests/pool.rs +++ b/client/transaction-pool/tests/pool.rs @@ -35,11 +35,11 @@ use sp_consensus::BlockOrigin; use sp_runtime::{ generic::BlockId, traits::Block as _, - transaction_validity::{InvalidTransaction, TransactionSource, ValidTransaction}, + transaction_validity::{TransactionSource, ValidTransaction}, }; use std::{collections::BTreeSet, pin::Pin, sync::Arc}; use substrate_test_runtime_client::{ - runtime::{Block, Extrinsic, Hash, Header, Index, Transfer}, + runtime::{Block, Extrinsic, ExtrinsicBuilder, Hash, Header, Index, Transfer, TransferData}, AccountKeyring::*, ClientBlockImportExt, }; @@ -86,7 +86,11 @@ fn submission_should_work() { let pool = pool(); block_on(pool.submit_one(&BlockId::number(0), SOURCE, uxt(Alice, 209))).unwrap(); - let pending: Vec<_> = pool.validated_pool().ready().map(|a| a.data.transfer().nonce).collect(); + let pending: Vec<_> = pool + .validated_pool() + .ready() + .map(|a| TransferData::try_from(&a.data).unwrap().nonce) + .collect(); assert_eq!(pending, vec![209]); } @@ -96,16 +100,25 @@ fn multiple_submission_should_work() { block_on(pool.submit_one(&BlockId::number(0), SOURCE, uxt(Alice, 209))).unwrap(); block_on(pool.submit_one(&BlockId::number(0), SOURCE, uxt(Alice, 210))).unwrap(); - let pending: Vec<_> = pool.validated_pool().ready().map(|a| a.data.transfer().nonce).collect(); + let pending: Vec<_> = pool + .validated_pool() + .ready() + .map(|a| TransferData::try_from(&a.data).unwrap().nonce) + .collect(); assert_eq!(pending, vec![209, 210]); } #[test] fn early_nonce_should_be_culled() { + sp_tracing::try_init_simple(); let pool = pool(); block_on(pool.submit_one(&BlockId::number(0), SOURCE, uxt(Alice, 208))).unwrap(); - let pending: Vec<_> = pool.validated_pool().ready().map(|a| a.data.transfer().nonce).collect(); + let pending: Vec<_> = pool + .validated_pool() + .ready() + .map(|a| TransferData::try_from(&a.data).unwrap().nonce) + .collect(); assert_eq!(pending, Vec::::new()); } @@ -114,11 +127,19 @@ fn late_nonce_should_be_queued() { let pool = pool(); block_on(pool.submit_one(&BlockId::number(0), SOURCE, uxt(Alice, 210))).unwrap(); - let pending: Vec<_> = pool.validated_pool().ready().map(|a| a.data.transfer().nonce).collect(); + let pending: Vec<_> = pool + .validated_pool() + .ready() + .map(|a| TransferData::try_from(&a.data).unwrap().nonce) + .collect(); assert_eq!(pending, Vec::::new()); block_on(pool.submit_one(&BlockId::number(0), SOURCE, uxt(Alice, 209))).unwrap(); - let pending: Vec<_> = pool.validated_pool().ready().map(|a| a.data.transfer().nonce).collect(); + let pending: Vec<_> = pool + .validated_pool() + .ready() + .map(|a| TransferData::try_from(&a.data).unwrap().nonce) + .collect(); assert_eq!(pending, vec![209, 210]); } @@ -128,14 +149,22 @@ fn prune_tags_should_work() { let hash209 = block_on(pool.submit_one(&BlockId::number(0), SOURCE, uxt(Alice, 209))).unwrap(); block_on(pool.submit_one(&BlockId::number(0), SOURCE, uxt(Alice, 210))).unwrap(); - let pending: Vec<_> = pool.validated_pool().ready().map(|a| a.data.transfer().nonce).collect(); + let pending: Vec<_> = pool + .validated_pool() + .ready() + .map(|a| TransferData::try_from(&a.data).unwrap().nonce) + .collect(); assert_eq!(pending, vec![209, 210]); pool.validated_pool().api().push_block(1, Vec::new(), true); block_on(pool.prune_tags(&BlockId::number(1), vec![vec![209]], vec![hash209])) .expect("Prune tags"); - let pending: Vec<_> = pool.validated_pool().ready().map(|a| a.data.transfer().nonce).collect(); + let pending: Vec<_> = pool + .validated_pool() + .ready() + .map(|a| TransferData::try_from(&a.data).unwrap().nonce) + .collect(); assert_eq!(pending, vec![210]); } @@ -148,7 +177,11 @@ fn should_ban_invalid_transactions() { block_on(pool.submit_one(&BlockId::number(0), SOURCE, uxt.clone())).unwrap_err(); // when - let pending: Vec<_> = pool.validated_pool().ready().map(|a| a.data.transfer().nonce).collect(); + let pending: Vec<_> = pool + .validated_pool() + .ready() + .map(|a| TransferData::try_from(&a.data).unwrap().nonce) + .collect(); assert_eq!(pending, Vec::::new()); // then @@ -197,7 +230,11 @@ fn should_correctly_prune_transactions_providing_more_than_one_tag() { block_on(pool.submit_one(&BlockId::number(2), SOURCE, xt.clone())).expect("2. Imported"); assert_eq!(pool.validated_pool().status().ready, 1); assert_eq!(pool.validated_pool().status().future, 1); - let pending: Vec<_> = pool.validated_pool().ready().map(|a| a.data.transfer().nonce).collect(); + let pending: Vec<_> = pool + .validated_pool() + .ready() + .map(|a| TransferData::try_from(&a.data).unwrap().nonce) + .collect(); assert_eq!(pending, vec![211]); // prune it and make sure the pool is empty @@ -472,6 +509,7 @@ fn finalization() { #[test] fn fork_aware_finalization() { + sp_tracing::try_init_simple(); let api = TestApi::empty(); // starting block A1 (last finalized.) let a_header = api.push_block(1, vec![], true); @@ -888,48 +926,6 @@ fn ready_set_should_eventually_resolve_when_block_update_arrives() { } } -#[test] -fn should_not_accept_old_signatures() { - let client = Arc::new(substrate_test_runtime_client::new()); - let best_hash = client.info().best_hash; - let finalized_hash = client.info().finalized_hash; - let pool = Arc::new( - BasicPool::new_test( - Arc::new(FullChainApi::new(client, None, &sp_core::testing::TaskExecutor::new())), - best_hash, - finalized_hash, - ) - .0, - ); - - let transfer = Transfer { from: Alice.into(), to: Bob.into(), nonce: 0, amount: 1 }; - let _bytes: sp_core::sr25519::Signature = transfer.using_encoded(|e| Alice.sign(e)).into(); - - // generated with schnorrkel 0.1.1 from `_bytes` - let old_singature = sp_core::sr25519::Signature::try_from( - &array_bytes::hex2bytes( - "c427eb672e8c441c86d31f1a81b22b43102058e9ce237cabe9897ea5099ffd426\ - cd1c6a1f4f2869c3df57901d36bedcb295657adb3a4355add86ed234eb83108", - ) - .expect("hex invalid")[..], - ) - .expect("signature construction failed"); - - let xt = Extrinsic::Transfer { - transfer, - signature: old_singature, - exhaust_resources_when_not_first: false, - }; - - assert_matches::assert_matches!( - block_on(pool.submit_one(&BlockId::number(0), SOURCE, xt.clone())), - Err(error::Error::Pool(sc_transaction_pool_api::error::Error::InvalidTransaction( - InvalidTransaction::BadProof - ))), - "Should be invalid transaction with bad proof", - ); -} - #[test] fn import_notification_to_pool_maintain_works() { let mut client = Arc::new(substrate_test_runtime_client::new()); @@ -975,7 +971,7 @@ fn import_notification_to_pool_maintain_works() { fn pruning_a_transaction_should_remove_it_from_best_transaction() { let (pool, api, _guard) = maintained_pool(); - let xt1 = Extrinsic::IncludeData(Vec::new()); + let xt1 = ExtrinsicBuilder::new_include_data(Vec::new()).build(); block_on(pool.submit_one(&BlockId::number(0), SOURCE, xt1.clone())).expect("1. Imported"); assert_eq!(pool.status().ready, 1); @@ -1001,7 +997,7 @@ fn stale_transactions_are_pruned() { let (pool, api, _guard) = maintained_pool(); xts.into_iter().for_each(|xt| { - block_on(pool.submit_one(&BlockId::number(0), SOURCE, xt.into_signed_tx())) + block_on(pool.submit_one(&BlockId::number(0), SOURCE, xt.into_unchecked_extrinsic())) .expect("1. Imported"); }); assert_eq!(pool.status().ready, 0); @@ -1010,9 +1006,12 @@ fn stale_transactions_are_pruned() { // Almost the same as our initial transactions, but with some different `amount`s to make them // generate a different hash let xts = vec![ - Transfer { from: Alice.into(), to: Bob.into(), nonce: 1, amount: 2 }.into_signed_tx(), - Transfer { from: Alice.into(), to: Bob.into(), nonce: 2, amount: 2 }.into_signed_tx(), - Transfer { from: Alice.into(), to: Bob.into(), nonce: 3, amount: 2 }.into_signed_tx(), + Transfer { from: Alice.into(), to: Bob.into(), nonce: 1, amount: 2 } + .into_unchecked_extrinsic(), + Transfer { from: Alice.into(), to: Bob.into(), nonce: 2, amount: 2 } + .into_unchecked_extrinsic(), + Transfer { from: Alice.into(), to: Bob.into(), nonce: 3, amount: 2 } + .into_unchecked_extrinsic(), ]; // Import block diff --git a/primitives/api/test/tests/runtime_calls.rs b/primitives/api/test/tests/runtime_calls.rs index 956a6a25f..a591bc0b7 100644 --- a/primitives/api/test/tests/runtime_calls.rs +++ b/primitives/api/test/tests/runtime_calls.rs @@ -60,55 +60,6 @@ fn calling_native_runtime_signature_changed_function() { assert_eq!(runtime_api.function_signature_changed(best_hash).unwrap(), 1); } -#[test] -fn calling_wasm_runtime_signature_changed_old_function() { - let client = TestClientBuilder::new() - .set_execution_strategy(ExecutionStrategy::AlwaysWasm) - .build(); - let runtime_api = client.runtime_api(); - let best_hash = client.chain_info().best_hash; - - #[allow(deprecated)] - let res = runtime_api.function_signature_changed_before_version_2(best_hash).unwrap(); - assert_eq!(&res, &[1, 2]); -} - -#[test] -fn calling_with_both_strategy_and_fail_on_wasm_should_return_error() { - let client = TestClientBuilder::new().set_execution_strategy(ExecutionStrategy::Both).build(); - let runtime_api = client.runtime_api(); - let best_hash = client.chain_info().best_hash; - assert!(runtime_api.fail_on_wasm(best_hash).is_err()); -} - -#[test] -fn calling_with_both_strategy_and_fail_on_native_should_work() { - let client = TestClientBuilder::new().set_execution_strategy(ExecutionStrategy::Both).build(); - let runtime_api = client.runtime_api(); - let best_hash = client.chain_info().best_hash; - assert_eq!(runtime_api.fail_on_native(best_hash).unwrap(), 1); -} - -#[test] -fn calling_with_native_else_wasm_and_fail_on_wasm_should_work() { - let client = TestClientBuilder::new() - .set_execution_strategy(ExecutionStrategy::NativeElseWasm) - .build(); - let runtime_api = client.runtime_api(); - let best_hash = client.chain_info().best_hash; - assert_eq!(runtime_api.fail_on_wasm(best_hash).unwrap(), 1); -} - -#[test] -fn calling_with_native_else_wasm_and_fail_on_native_should_work() { - let client = TestClientBuilder::new() - .set_execution_strategy(ExecutionStrategy::NativeElseWasm) - .build(); - let runtime_api = client.runtime_api(); - let best_hash = client.chain_info().best_hash; - assert_eq!(runtime_api.fail_on_native(best_hash).unwrap(), 1); -} - #[test] fn use_trie_function() { let client = TestClientBuilder::new() @@ -162,7 +113,7 @@ fn record_proof_works() { from: AccountKeyring::Alice.into(), to: AccountKeyring::Bob.into(), } - .into_signed_tx(); + .into_unchecked_extrinsic(); // Build the block and record proof let mut builder = client diff --git a/test-utils/runtime/Cargo.toml b/test-utils/runtime/Cargo.toml index 68267e91d..2bcdafed9 100644 --- a/test-utils/runtime/Cargo.toml +++ b/test-utils/runtime/Cargo.toml @@ -35,6 +35,10 @@ sp-session = { version = "4.0.0-dev", default-features = false, path = "../../pr sp-api = { version = "4.0.0-dev", default-features = false, path = "../../primitives/api" } sp-runtime = { version = "7.0.0", default-features = false, path = "../../primitives/runtime" } pallet-babe = { version = "4.0.0-dev", default-features = false, path = "../../frame/babe" } +pallet-balances = { version = "4.0.0-dev", default-features = false, path = "../../frame/balances" } +pallet-root-testing = { version = "1.0.0-dev", default-features = false, path = "../../frame/root-testing" } +pallet-sudo = { version = "4.0.0-dev", default-features = false, path = "../../frame/sudo" } +frame-executive = { version = "4.0.0-dev", default-features = false, path = "../../frame/executive" } frame-system = { version = "4.0.0-dev", default-features = false, path = "../../frame/system" } frame-system-rpc-runtime-api = { version = "4.0.0-dev", default-features = false, path = "../../frame/system/rpc/runtime-api" } pallet-timestamp = { version = "4.0.0-dev", default-features = false, path = "../../frame/timestamp" } @@ -45,18 +49,20 @@ trie-db = { version = "0.27.0", default-features = false } sc-service = { version = "0.10.0-dev", default-features = false, optional = true, features = ["test-helpers"], path = "../../client/service" } sp-state-machine = { version = "0.13.0", default-features = false, path = "../../primitives/state-machine" } sp-externalities = { version = "0.13.0", default-features = false, path = "../../primitives/externalities" } +sp-debug-derive = { path = "../../primitives/debug-derive" } # 3rd party -cfg-if = "1.0" +array-bytes = { version = "6.1", optional = true } log = { version = "0.4.17", default-features = false } serde = { version = "1.0.136", optional = true, features = ["derive"] } [dev-dependencies] +futures = "0.3.21" sc-block-builder = { version = "0.10.0-dev", path = "../../client/block-builder" } sc-executor = { version = "0.10.0-dev", path = "../../client/executor" } sp-consensus = { version = "0.10.0-dev", path = "../../primitives/consensus/common" } substrate-test-runtime-client = { version = "2.0.0", path = "./client" } -futures = "0.3.21" +sp-tracing = { version = "6.0.0", path = "../../primitives/tracing" } [build-dependencies] substrate-wasm-builder = { version = "5.0.0-dev", path = "../../utils/wasm-builder", optional = true } @@ -66,7 +72,7 @@ default = [ "std", ] std = [ - "pallet-beefy-mmr/std", + "array-bytes", "sp-application-crypto/std", "sp-consensus-aura/std", "sp-consensus-babe/std", @@ -93,9 +99,13 @@ std = [ "sp-externalities/std", "sp-state-machine/std", "pallet-babe/std", + "pallet-beefy-mmr/std", + "pallet-timestamp/std", + "pallet-balances/std", + "pallet-sudo/std", + "pallet-root-testing/std", "frame-system-rpc-runtime-api/std", "frame-system/std", - "pallet-timestamp/std", "sc-service", "sp-consensus-grandpa/std", "sp-trie/std", diff --git a/test-utils/runtime/client/src/block_builder_ext.rs b/test-utils/runtime/client/src/block_builder_ext.rs index 3c5e1122f..a9b0d49f3 100644 --- a/test-utils/runtime/client/src/block_builder_ext.rs +++ b/test-utils/runtime/client/src/block_builder_ext.rs @@ -21,6 +21,7 @@ use sc_client_api::backend; use sp_api::{ApiExt, ProvideRuntimeApi}; use sc_block_builder::BlockBuilderApi; +use substrate_test_runtime::*; /// Extension trait for test block builder. pub trait BlockBuilderExt { @@ -29,12 +30,19 @@ pub trait BlockBuilderExt { &mut self, transfer: substrate_test_runtime::Transfer, ) -> Result<(), sp_blockchain::Error>; - /// Add storage change extrinsic to the block. + + /// Add unsigned storage change extrinsic to the block. fn push_storage_change( &mut self, key: Vec, value: Option>, ) -> Result<(), sp_blockchain::Error>; + + /// Adds an extrinsic which pushes DigestItem to header's log + fn push_deposit_log_digest_item( + &mut self, + log: sp_runtime::generic::DigestItem, + ) -> Result<(), sp_blockchain::Error>; } impl<'a, A, B> BlockBuilderExt @@ -52,7 +60,7 @@ where &mut self, transfer: substrate_test_runtime::Transfer, ) -> Result<(), sp_blockchain::Error> { - self.push(transfer.into_signed_tx()) + self.push(transfer.into_unchecked_extrinsic()) } fn push_storage_change( @@ -60,6 +68,13 @@ where key: Vec, value: Option>, ) -> Result<(), sp_blockchain::Error> { - self.push(substrate_test_runtime::Extrinsic::StorageChange(key, value)) + self.push(ExtrinsicBuilder::new_storage_change(key, value).build()) + } + + fn push_deposit_log_digest_item( + &mut self, + log: sp_runtime::generic::DigestItem, + ) -> Result<(), sp_blockchain::Error> { + self.push(ExtrinsicBuilder::new_deposit_log_digest_item(log).build()) } } diff --git a/test-utils/runtime/client/src/lib.rs b/test-utils/runtime/client/src/lib.rs index 5e4b5d6a1..b2c1e8f47 100644 --- a/test-utils/runtime/client/src/lib.rs +++ b/test-utils/runtime/client/src/lib.rs @@ -30,16 +30,9 @@ pub use substrate_test_runtime as runtime; pub use self::block_builder_ext::BlockBuilderExt; -use sc_chain_spec::construct_genesis_block; -use sp_api::StateVersion; -use sp_core::{ - sr25519, - storage::{ChildInfo, Storage, StorageChild}, - Pair, -}; -use sp_runtime::traits::{Block as BlockT, Hash as HashT, Header as HeaderT}; +use sp_core::storage::{ChildInfo, Storage, StorageChild}; use substrate_test_client::sc_executor::WasmExecutor; -use substrate_test_runtime::genesismap::{additional_storage_with_genesis, GenesisConfig}; +use substrate_test_runtime::genesismap::GenesisStorageBuilder; /// A prelude to import in tests. pub mod prelude { @@ -92,28 +85,6 @@ pub struct GenesisParameters { } impl GenesisParameters { - fn genesis_config(&self) -> GenesisConfig { - GenesisConfig::new( - vec![ - sr25519::Public::from(Sr25519Keyring::Alice).into(), - sr25519::Public::from(Sr25519Keyring::Bob).into(), - sr25519::Public::from(Sr25519Keyring::Charlie).into(), - ], - (0..16_usize) - .into_iter() - .map(|i| AccountKeyring::numeric(i).public()) - .chain(vec![ - AccountKeyring::Alice.into(), - AccountKeyring::Bob.into(), - AccountKeyring::Charlie.into(), - ]) - .collect(), - 1000, - self.heap_pages_override, - self.extra_storage.clone(), - ) - } - /// Set the wasm code that should be used at genesis. pub fn set_wasm_code(&mut self, code: Vec) { self.wasm_code = Some(code); @@ -127,34 +98,11 @@ impl GenesisParameters { impl GenesisInit for GenesisParameters { fn genesis_storage(&self) -> Storage { - use codec::Encode; - - let mut storage = self.genesis_config().genesis_map(); - - if let Some(ref code) = self.wasm_code { - storage - .top - .insert(sp_core::storage::well_known_keys::CODE.to_vec(), code.clone()); - } - - let child_roots = storage.children_default.values().map(|child_content| { - let state_root = - <<::Header as HeaderT>::Hashing as HashT>::trie_root( - child_content.data.clone().into_iter().collect(), - sp_runtime::StateVersion::V1, - ); - let prefixed_storage_key = child_content.child_info.prefixed_storage_key(); - (prefixed_storage_key.into_inner(), state_root.encode()) - }); - let state_root = - <<::Header as HeaderT>::Hashing as HashT>::trie_root( - storage.top.clone().into_iter().chain(child_roots).collect(), - sp_runtime::StateVersion::V1, - ); - let block: runtime::Block = construct_genesis_block(state_root, StateVersion::V1); - storage.top.extend(additional_storage_with_genesis(&block)); - - storage + GenesisStorageBuilder::default() + .with_heap_pages(self.heap_pages_override) + .with_wasm_code(&self.wasm_code) + .with_extra_storage(self.extra_storage.clone()) + .build_storage() } } diff --git a/test-utils/runtime/src/extrinsic.rs b/test-utils/runtime/src/extrinsic.rs new file mode 100644 index 000000000..a6e13226f --- /dev/null +++ b/test-utils/runtime/src/extrinsic.rs @@ -0,0 +1,207 @@ +// This file is part of Substrate. + +// Copyright (C) Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +//! Provides utils for building the `Extrinsic` instances used with `substrate-test-runtime`. + +use crate::{ + substrate_test_pallet::pallet::Call as PalletCall, AccountId, Balance, BalancesCall, + CheckSubstrateCall, Extrinsic, Index, Pair, RuntimeCall, SignedPayload, TransferData, +}; +use codec::Encode; +use frame_system::{CheckNonce, CheckWeight}; +use sp_core::crypto::Pair as TraitPair; +use sp_keyring::AccountKeyring; +use sp_runtime::{transaction_validity::TransactionPriority, Perbill}; +use sp_std::prelude::*; + +/// Transfer used in test substrate pallet. Extrinsic is created and signed using this data. +#[derive(Clone)] +pub struct Transfer { + /// Transfer sender and signer of created extrinsic + pub from: Pair, + /// The recipient of the transfer + pub to: AccountId, + /// Amount of transfer + pub amount: Balance, + /// Sender's account nonce at which transfer is valid + pub nonce: u64, +} + +impl Transfer { + /// Convert into a signed unchecked extrinsic. + pub fn into_unchecked_extrinsic(self) -> Extrinsic { + ExtrinsicBuilder::new_transfer(self).build() + } +} + +impl Default for TransferData { + fn default() -> Self { + Self { + from: AccountKeyring::Alice.into(), + to: AccountKeyring::Bob.into(), + amount: 0, + nonce: 0, + } + } +} + +/// If feasible converts given `Extrinsic` to `TransferData` +impl TryFrom<&Extrinsic> for TransferData { + type Error = (); + fn try_from(uxt: &Extrinsic) -> Result { + match uxt { + Extrinsic { + function: RuntimeCall::Balances(BalancesCall::transfer_allow_death { dest, value }), + signature: Some((from, _, (CheckNonce(nonce), ..))), + } => Ok(TransferData { from: *from, to: *dest, amount: *value, nonce: *nonce }), + Extrinsic { + function: RuntimeCall::SubstrateTest(PalletCall::bench_call { transfer }), + signature: None, + } => Ok(transfer.clone()), + _ => Err(()), + } + } +} + +/// Generates `Extrinsic` +pub struct ExtrinsicBuilder { + function: RuntimeCall, + signer: Option, + nonce: Option, +} + +impl ExtrinsicBuilder { + /// Create builder for given `RuntimeCall`. By default `Extrinsic` will be signed by `Alice`. + pub fn new(function: impl Into) -> Self { + Self { function: function.into(), signer: Some(AccountKeyring::Alice.pair()), nonce: None } + } + + /// Create builder for given `RuntimeCall`. `Extrinsic` will be unsigned. + pub fn new_unsigned(function: impl Into) -> Self { + Self { function: function.into(), signer: None, nonce: None } + } + + /// Create builder for `pallet_call::bench_transfer` from given `TransferData`. + pub fn new_bench_call(transfer: TransferData) -> Self { + Self::new_unsigned(PalletCall::bench_call { transfer }) + } + + /// Create builder for given `Transfer`. Transfer `nonce` will be used as `Extrinsic` nonce. + /// Transfer `from` will be used as Extrinsic signer. + pub fn new_transfer(transfer: Transfer) -> Self { + Self { + nonce: Some(transfer.nonce), + signer: Some(transfer.from.clone()), + ..Self::new(BalancesCall::transfer_allow_death { + dest: transfer.to, + value: transfer.amount, + }) + } + } + + /// Create builder for `PalletCall::include_data` call using given parameters + pub fn new_include_data(data: Vec) -> Self { + Self::new(PalletCall::include_data { data }) + } + + /// Create builder for `PalletCall::storage_change` call using given parameters. Will + /// create unsigned Extrinsic. + pub fn new_storage_change(key: Vec, value: Option>) -> Self { + Self::new_unsigned(PalletCall::storage_change { key, value }) + } + + /// Create builder for `PalletCall::offchain_index_set` call using given parameters + pub fn new_offchain_index_set(key: Vec, value: Vec) -> Self { + Self::new(PalletCall::offchain_index_set { key, value }) + } + + /// Create builder for `PalletCall::offchain_index_clear` call using given parameters + pub fn new_offchain_index_clear(key: Vec) -> Self { + Self::new(PalletCall::offchain_index_clear { key }) + } + + /// Create builder for `PalletCall::indexed_call` call using given parameters + pub fn new_indexed_call(data: Vec) -> Self { + Self::new(PalletCall::indexed_call { data }) + } + + /// Create builder for `PalletCall::new_deposit_log_digest_item` call using given `log` + pub fn new_deposit_log_digest_item(log: sp_runtime::generic::DigestItem) -> Self { + Self::new_unsigned(PalletCall::deposit_log_digest_item { log }) + } + + /// Create builder for `PalletCall::Call::new_deposit_log_digest_item` + pub fn new_fill_block(ratio: Perbill) -> Self { + Self::new(PalletCall::fill_block { ratio }) + } + + /// Create builder for `PalletCall::call_do_not_propagate` call using given parameters + pub fn new_call_do_not_propagate() -> Self { + Self::new(PalletCall::call_do_not_propagate {}) + } + + /// Create builder for `PalletCall::call_with_priority` call using given parameters + pub fn new_call_with_priority(priority: TransactionPriority) -> Self { + Self::new(PalletCall::call_with_priority { priority }) + } + + /// Create builder for `PalletCall::read` call using given parameters + pub fn new_read(count: u32) -> Self { + Self::new_unsigned(PalletCall::read { count }) + } + + /// Create builder for `PalletCall::read` call using given parameters + pub fn new_read_and_panic(count: u32) -> Self { + Self::new_unsigned(PalletCall::read_and_panic { count }) + } + + /// Unsigned `Extrinsic` will be created + pub fn unsigned(mut self) -> Self { + self.signer = None; + self + } + + /// Given `nonce` will be set in `Extrinsic` + pub fn nonce(mut self, nonce: Index) -> Self { + self.nonce = Some(nonce); + self + } + + /// Extrinsic will be signed by `signer` + pub fn signer(mut self, signer: Pair) -> Self { + self.signer = Some(signer); + self + } + + /// Build `Extrinsic` using embedded parameters + pub fn build(self) -> Extrinsic { + if let Some(signer) = self.signer { + let extra = ( + CheckNonce::from(self.nonce.unwrap_or(0)), + CheckWeight::new(), + CheckSubstrateCall {}, + ); + let raw_payload = + SignedPayload::from_raw(self.function.clone(), extra.clone(), ((), (), ())); + let signature = raw_payload.using_encoded(|e| signer.sign(e)); + + Extrinsic::new_signed(self.function, signer.public(), signature, extra) + } else { + Extrinsic::new_unsigned(self.function) + } + } +} diff --git a/test-utils/runtime/src/genesismap.rs b/test-utils/runtime/src/genesismap.rs index 9e00dd299..62ce26c55 100644 --- a/test-utils/runtime/src/genesismap.rs +++ b/test-utils/runtime/src/genesismap.rs @@ -17,75 +17,119 @@ //! Tool for creating the genesis block. -use super::{system, wasm_binary_unwrap, AccountId, AuthorityId, Runtime}; -use codec::{Encode, Joiner, KeyedVec}; -use frame_support::traits::GenesisBuild; +use super::{ + currency, substrate_test_pallet, wasm_binary_unwrap, AccountId, AuthorityId, Balance, + GenesisConfig, +}; +use codec::Encode; use sc_service::construct_genesis_block; use sp_core::{ - map, + sr25519, storage::{well_known_keys, StateVersion, Storage}, + Pair, +}; +use sp_keyring::{AccountKeyring, Sr25519Keyring}; +use sp_runtime::{ + traits::{Block as BlockT, Hash as HashT, Header as HeaderT}, + BuildStorage, }; -use sp_io::hashing::{blake2_256, twox_128}; -use sp_runtime::traits::{Block as BlockT, Hash as HashT, Header as HeaderT}; -use std::collections::BTreeMap; -/// Configuration of a general Substrate test genesis block. -pub struct GenesisConfig { +/// Builder for generating storage from substrate-test-runtime genesis config. Default storage can +/// be extended with additional key-value pairs. +pub struct GenesisStorageBuilder { authorities: Vec, balances: Vec<(AccountId, u64)>, heap_pages_override: Option, /// Additional storage key pairs that will be added to the genesis map. extra_storage: Storage, + wasm_code: Option>, +} + +impl Default for GenesisStorageBuilder { + /// Creates a builder with default settings for `substrate_test_runtime`. + fn default() -> Self { + Self::new( + vec![ + sr25519::Public::from(Sr25519Keyring::Alice).into(), + sr25519::Public::from(Sr25519Keyring::Bob).into(), + sr25519::Public::from(Sr25519Keyring::Charlie).into(), + ], + (0..16_usize) + .into_iter() + .map(|i| AccountKeyring::numeric(i).public()) + .chain(vec![ + AccountKeyring::Alice.into(), + AccountKeyring::Bob.into(), + AccountKeyring::Charlie.into(), + ]) + .collect(), + 1000 * currency::DOLLARS, + ) + } } -impl GenesisConfig { +impl GenesisStorageBuilder { + /// Creates a storage builder for genesis config. `substrage test runtime` `GenesisConfig` is + /// initialized with provided `authorities`, `endowed_accounts` with given balance. Key-pairs + /// from `extra_storage` will be injected into built storage. `HEAP_PAGES` key and value will + /// also be placed into storage. pub fn new( authorities: Vec, endowed_accounts: Vec, - balance: u64, - heap_pages_override: Option, - extra_storage: Storage, + balance: Balance, ) -> Self { - GenesisConfig { + GenesisStorageBuilder { authorities, balances: endowed_accounts.into_iter().map(|a| (a, balance)).collect(), - heap_pages_override, - extra_storage, + heap_pages_override: None, + extra_storage: Default::default(), + wasm_code: None, } } - pub fn genesis_map(&self) -> Storage { - let wasm_runtime = wasm_binary_unwrap().to_vec(); - let mut map: BTreeMap, Vec> = self - .balances - .iter() - .map(|&(ref account, balance)| { - (account.to_keyed_vec(b"balance:"), vec![].and(&balance)) - }) - .map(|(k, v)| (blake2_256(&k[..])[..].to_vec(), v.to_vec())) - .chain( - vec![ - (well_known_keys::CODE.into(), wasm_runtime), - ( - well_known_keys::HEAP_PAGES.into(), - vec![].and(&(self.heap_pages_override.unwrap_or(16_u64))), - ), - ] - .into_iter(), - ) - .collect(); - map.insert(twox_128(&b"sys:auth"[..])[..].to_vec(), self.authorities.encode()); - // Add the extra storage entries. - map.extend(self.extra_storage.top.clone().into_iter()); - - // Assimilate the system genesis config. - let mut storage = - Storage { top: map, children_default: self.extra_storage.children_default.clone() }; - >::assimilate_storage( - &system::GenesisConfig { authorities: self.authorities.clone() }, - &mut storage, - ) - .expect("Adding `system::GensisConfig` to the genesis"); + /// Override default wasm code to be placed into GenesisConfig. + pub fn with_wasm_code(mut self, wasm_code: &Option>) -> Self { + self.wasm_code = wasm_code.clone(); + self + } + + pub fn with_heap_pages(mut self, heap_pages_override: Option) -> Self { + self.heap_pages_override = heap_pages_override; + self + } + + pub fn with_extra_storage(mut self, storage: Storage) -> Self { + self.extra_storage = storage; + self + } + + /// Builds the `GenesisConfig` and returns its storage. + pub fn build_storage(&mut self) -> Storage { + let genesis_config = GenesisConfig { + system: frame_system::GenesisConfig { + code: self.wasm_code.clone().unwrap_or(wasm_binary_unwrap().to_vec()), + }, + babe: pallet_babe::GenesisConfig { + authorities: self.authorities.clone().into_iter().map(|x| (x, 1)).collect(), + epoch_config: Some(crate::TEST_RUNTIME_BABE_EPOCH_CONFIGURATION), + }, + substrate_test: substrate_test_pallet::GenesisConfig { + authorities: self.authorities.clone(), + }, + balances: pallet_balances::GenesisConfig { balances: self.balances.clone() }, + }; + + let mut storage = genesis_config + .build_storage() + .expect("Build storage from substrate-test-runtime GenesisConfig"); + + storage.top.insert( + well_known_keys::HEAP_PAGES.into(), + self.heap_pages_override.unwrap_or(16_u64).encode(), + ); + + storage.top.extend(self.extra_storage.top.clone()); + storage.children_default.extend(self.extra_storage.children_default.clone()); storage } @@ -108,12 +152,6 @@ pub fn insert_genesis_block(storage: &mut Storage) -> sp_core::hash::H256 { ); let block: crate::Block = construct_genesis_block(state_root, StateVersion::V1); let genesis_hash = block.header.hash(); - storage.top.extend(additional_storage_with_genesis(&block)); - genesis_hash -} -pub fn additional_storage_with_genesis(genesis_block: &crate::Block) -> BTreeMap, Vec> { - map![ - twox_128(&b"latest"[..]).to_vec() => genesis_block.hash().as_fixed_bytes().to_vec() - ] + genesis_hash } diff --git a/test-utils/runtime/src/lib.rs b/test-utils/runtime/src/lib.rs index b5600843c..00db6a745 100644 --- a/test-utils/runtime/src/lib.rs +++ b/test-utils/runtime/src/lib.rs @@ -19,13 +19,29 @@ #![cfg_attr(not(feature = "std"), no_std)] +#[cfg(feature = "std")] +pub mod extrinsic; #[cfg(feature = "std")] pub mod genesismap; -pub mod system; +pub mod substrate_test_pallet; -use codec::{Decode, Encode, Error, Input, MaxEncodedLen}; +use codec::{Decode, Encode}; +use frame_support::{ + construct_runtime, + dispatch::DispatchClass, + parameter_types, + traits::{ConstU32, ConstU64}, + weights::{ + constants::{BlockExecutionWeight, ExtrinsicBaseWeight, WEIGHT_REF_TIME_PER_SECOND}, + Weight, + }, +}; +use frame_system::{ + limits::{BlockLength, BlockWeights}, + CheckNonce, CheckWeight, +}; use scale_info::TypeInfo; -use sp_std::{marker::PhantomData, prelude::*}; +use sp_std::prelude::*; use sp_application_crypto::{ecdsa, ed25519, sr25519, RuntimeAppPublic}; use sp_core::{OpaqueMetadata, RuntimeDebug}; @@ -35,29 +51,13 @@ use sp_trie::{ }; use trie_db::{Trie, TrieMut}; -use cfg_if::cfg_if; -use frame_support::{ - dispatch::RawOrigin, - parameter_types, - traits::{CallerTrait, ConstU32, ConstU64, CrateVersion}, - weights::{RuntimeDbWeight, Weight}, -}; -use frame_system::limits::{BlockLength, BlockWeights}; use sp_api::{decl_runtime_apis, impl_runtime_apis}; pub use sp_core::hash::H256; use sp_inherents::{CheckInherentsResult, InherentData}; -#[cfg(feature = "std")] -use sp_runtime::traits::NumberFor; use sp_runtime::{ create_runtime_str, impl_opaque_keys, - traits::{ - BlakeTwo256, BlindCheckable, Block as BlockT, Extrinsic as ExtrinsicT, GetNodeBlockType, - GetRuntimeBlockType, IdentityLookup, Verify, - }, - transaction_validity::{ - InvalidTransaction, TransactionSource, TransactionValidity, TransactionValidityError, - ValidTransaction, - }, + traits::{BlakeTwo256, Block as BlockT, DispatchInfoOf, NumberFor, Verify}, + transaction_validity::{TransactionSource, TransactionValidity, TransactionValidityError}, ApplyExtrinsicResult, Perbill, }; #[cfg(any(feature = "std", test))] @@ -65,10 +65,17 @@ use sp_version::NativeVersion; use sp_version::RuntimeVersion; // Ensure Babe and Aura use the same crypto to simplify things a bit. -pub use sp_consensus_babe::{AllowedSlots, AuthorityId, Slot}; +pub use sp_consensus_babe::{AllowedSlots, AuthorityId, BabeEpochConfiguration, Slot}; + +pub use pallet_balances::Call as BalancesCall; pub type AuraId = sp_consensus_aura::sr25519::AuthorityId; +#[cfg(feature = "std")] +pub use extrinsic::{ExtrinsicBuilder, Transfer}; + +const LOG_TARGET: &str = "substrate-test-runtime"; + // Include the WASM binary #[cfg(feature = "std")] include!(concat!(env!("OUT_DIR"), "/wasm_binary.rs")); @@ -119,164 +126,31 @@ pub fn native_version() -> NativeVersion { NativeVersion { runtime_version: VERSION, can_author_with: Default::default() } } -/// Calls in transactions. +/// Transfer data extracted from Extrinsic containing `Balances::transfer_allow_death`. #[derive(Clone, PartialEq, Eq, Encode, Decode, RuntimeDebug, TypeInfo)] -pub struct Transfer { +pub struct TransferData { pub from: AccountId, pub to: AccountId, - pub amount: u64, - pub nonce: u64, -} - -impl Transfer { - /// Convert into a signed extrinsic. - #[cfg(feature = "std")] - pub fn into_signed_tx(self) -> Extrinsic { - let signature = sp_keyring::AccountKeyring::from_public(&self.from) - .expect("Creates keyring from public key.") - .sign(&self.encode()); - Extrinsic::Transfer { transfer: self, signature, exhaust_resources_when_not_first: false } - } - - /// Convert into a signed extrinsic, which will only end up included in the block - /// if it's the first transaction. Otherwise it will cause `ResourceExhaustion` error - /// which should be considered as block being full. - #[cfg(feature = "std")] - pub fn into_resources_exhausting_tx(self) -> Extrinsic { - let signature = sp_keyring::AccountKeyring::from_public(&self.from) - .expect("Creates keyring from public key.") - .sign(&self.encode()); - Extrinsic::Transfer { transfer: self, signature, exhaust_resources_when_not_first: true } - } -} - -/// Extrinsic for test-runtime. -#[derive(Clone, PartialEq, Eq, Encode, Decode, RuntimeDebug, TypeInfo)] -pub enum Extrinsic { - AuthoritiesChange(Vec), - Transfer { - transfer: Transfer, - signature: AccountSignature, - exhaust_resources_when_not_first: bool, - }, - IncludeData(Vec), - StorageChange(Vec, Option>), - OffchainIndexSet(Vec, Vec), - OffchainIndexClear(Vec), - Store(Vec), - /// Read X times from the state some data and then panic! - /// - /// Returns `Ok` if it didn't read anything. - ReadAndPanic(u32), - /// Read X times from the state some data. - /// - /// Panics if it can not read `X` times. - Read(u32), + pub amount: Balance, + pub nonce: Index, } +/// The address format for describing accounts. +pub type Address = sp_core::sr25519::Public; +pub type Signature = sr25519::Signature; #[cfg(feature = "std")] -impl serde::Serialize for Extrinsic { - fn serialize(&self, seq: S) -> Result - where - S: serde::Serializer, - { - self.using_encoded(|bytes| seq.serialize_bytes(bytes)) - } -} +pub type Pair = sp_core::sr25519::Pair; -// rustc can't deduce this trait bound https://github.com/rust-lang/rust/issues/48214 -#[cfg(feature = "std")] -impl<'a> serde::Deserialize<'a> for Extrinsic { - fn deserialize(de: D) -> Result - where - D: serde::Deserializer<'a>, - { - let r = sp_core::bytes::deserialize(de)?; - Decode::decode(&mut &r[..]) - .map_err(|e| serde::de::Error::custom(format!("Decode error: {}", e))) - } -} +/// The SignedExtension to the basic transaction logic. +pub type SignedExtra = (CheckNonce, CheckWeight, CheckSubstrateCall); +/// The payload being signed in transactions. +pub type SignedPayload = sp_runtime::generic::SignedPayload; +/// Unchecked extrinsic type as expected by this runtime. +pub type Extrinsic = + sp_runtime::generic::UncheckedExtrinsic; -impl BlindCheckable for Extrinsic { - type Checked = Self; - - fn check(self) -> Result { - match self { - Extrinsic::AuthoritiesChange(new_auth) => Ok(Extrinsic::AuthoritiesChange(new_auth)), - Extrinsic::Transfer { transfer, signature, exhaust_resources_when_not_first } => - if sp_runtime::verify_encoded_lazy(&signature, &transfer, &transfer.from) { - Ok(Extrinsic::Transfer { - transfer, - signature, - exhaust_resources_when_not_first, - }) - } else { - Err(InvalidTransaction::BadProof.into()) - }, - Extrinsic::IncludeData(v) => Ok(Extrinsic::IncludeData(v)), - Extrinsic::StorageChange(key, value) => Ok(Extrinsic::StorageChange(key, value)), - Extrinsic::OffchainIndexSet(key, value) => Ok(Extrinsic::OffchainIndexSet(key, value)), - Extrinsic::OffchainIndexClear(key) => Ok(Extrinsic::OffchainIndexClear(key)), - Extrinsic::Store(data) => Ok(Extrinsic::Store(data)), - Extrinsic::ReadAndPanic(i) => Ok(Extrinsic::ReadAndPanic(i)), - Extrinsic::Read(i) => Ok(Extrinsic::Read(i)), - } - } -} - -impl ExtrinsicT for Extrinsic { - type Call = Extrinsic; - type SignaturePayload = (); - - fn is_signed(&self) -> Option { - if let Extrinsic::IncludeData(_) = *self { - Some(false) - } else { - Some(true) - } - } - - fn new(call: Self::Call, _signature_payload: Option) -> Option { - Some(call) - } -} - -impl sp_runtime::traits::Dispatchable for Extrinsic { - type RuntimeOrigin = RuntimeOrigin; - type Config = (); - type Info = (); - type PostInfo = (); - fn dispatch( - self, - _origin: Self::RuntimeOrigin, - ) -> sp_runtime::DispatchResultWithInfo { - panic!("This implementation should not be used for actual dispatch."); - } -} - -impl Extrinsic { - /// Convert `&self` into `&Transfer`. - /// - /// Panics if this is no `Transfer` extrinsic. - pub fn transfer(&self) -> &Transfer { - self.try_transfer().expect("cannot convert to transfer ref") - } - - /// Try to convert `&self` into `&Transfer`. - /// - /// Returns `None` if this is no `Transfer` extrinsic. - pub fn try_transfer(&self) -> Option<&Transfer> { - match self { - Extrinsic::Transfer { ref transfer, .. } => Some(transfer), - _ => None, - } - } -} - -/// The signature type used by accounts/transactions. -pub type AccountSignature = sr25519::Signature; /// An identifier for an account on this system. -pub type AccountId = ::Signer; +pub type AccountId = ::Signer; /// A simple hash type for all our hashing. pub type Hash = H256; /// The hashing algorithm used. @@ -293,351 +167,200 @@ pub type Digest = sp_runtime::generic::Digest; pub type Block = sp_runtime::generic::Block; /// A test block's header. pub type Header = sp_runtime::generic::Header; - -/// Run whatever tests we have. -pub fn run_tests(mut input: &[u8]) -> Vec { - use sp_runtime::print; - - print("run_tests..."); - let block = Block::decode(&mut input).unwrap(); - print("deserialized block."); - let stxs = block.extrinsics.iter().map(Encode::encode); - print("reserialized transactions."); - [stxs.count() as u8].encode() -} - -/// A type that can not be decoded. -#[derive(PartialEq, TypeInfo)] -pub struct DecodeFails { - _phantom: PhantomData, -} - -impl Encode for DecodeFails { - fn encode(&self) -> Vec { - Vec::new() - } -} - -impl codec::EncodeLike for DecodeFails {} - -impl Default for DecodeFails { - /// Create a default instance. - fn default() -> DecodeFails { - DecodeFails { _phantom: Default::default() } - } -} - -impl Decode for DecodeFails { - fn decode(_: &mut I) -> Result { - Err("DecodeFails always fails".into()) - } -} - -cfg_if! { - if #[cfg(feature = "std")] { - decl_runtime_apis! { - #[api_version(2)] - pub trait TestAPI { - /// Return the balance of the given account id. - fn balance_of(id: AccountId) -> u64; - /// A benchmark function that adds one to the given value and returns the result. - fn benchmark_add_one(val: &u64) -> u64; - /// A benchmark function that adds one to each value in the given vector and returns the - /// result. - fn benchmark_vector_add_one(vec: &Vec) -> Vec; - /// A function that always fails to convert a parameter between runtime and node. - fn fail_convert_parameter(param: DecodeFails); - /// A function that always fails to convert its return value between runtime and node. - fn fail_convert_return_value() -> DecodeFails; - /// A function for that the signature changed in version `2`. - #[changed_in(2)] - fn function_signature_changed() -> Vec; - /// The new signature. - fn function_signature_changed() -> u64; - fn fail_on_native() -> u64; - fn fail_on_wasm() -> u64; - /// trie no_std testing - fn use_trie() -> u64; - fn benchmark_indirect_call() -> u64; - fn benchmark_direct_call() -> u64; - fn vec_with_capacity(size: u32) -> Vec; - /// Returns the initialized block number. - fn get_block_number() -> u64; - /// Takes and returns the initialized block number. - fn take_block_number() -> Option; - /// Test that `ed25519` crypto works in the runtime. - /// - /// Returns the signature generated for the message `ed25519` and the public key. - fn test_ed25519_crypto() -> (ed25519::AppSignature, ed25519::AppPublic); - /// Test that `sr25519` crypto works in the runtime. - /// - /// Returns the signature generated for the message `sr25519`. - fn test_sr25519_crypto() -> (sr25519::AppSignature, sr25519::AppPublic); - /// Test that `ecdsa` crypto works in the runtime. - /// - /// Returns the signature generated for the message `ecdsa`. - fn test_ecdsa_crypto() -> (ecdsa::AppSignature, ecdsa::AppPublic); - /// Run various tests against storage. - fn test_storage(); - /// Check a witness. - fn test_witness(proof: StorageProof, root: crate::Hash); - /// Test that ensures that we can call a function that takes multiple - /// arguments. - fn test_multiple_arguments(data: Vec, other: Vec, num: u32); - /// Traces log "Hey I'm runtime." - fn do_trace_log(); - /// Verify the given signature, public & message bundle. - fn verify_ed25519(sig: ed25519::Signature, public: ed25519::Public, message: Vec) -> bool; - } - } - } else { - decl_runtime_apis! { - pub trait TestAPI { - /// Return the balance of the given account id. - fn balance_of(id: AccountId) -> u64; - /// A benchmark function that adds one to the given value and returns the result. - fn benchmark_add_one(val: &u64) -> u64; - /// A benchmark function that adds one to each value in the given vector and returns the - /// result. - fn benchmark_vector_add_one(vec: &Vec) -> Vec; - /// A function that always fails to convert a parameter between runtime and node. - fn fail_convert_parameter(param: DecodeFails); - /// A function that always fails to convert its return value between runtime and node. - fn fail_convert_return_value() -> DecodeFails; - /// In wasm we just emulate the old behavior. - fn function_signature_changed() -> Vec; - fn fail_on_native() -> u64; - fn fail_on_wasm() -> u64; - /// trie no_std testing - fn use_trie() -> u64; - fn benchmark_indirect_call() -> u64; - fn benchmark_direct_call() -> u64; - fn vec_with_capacity(size: u32) -> Vec; - /// Returns the initialized block number. - fn get_block_number() -> u64; - /// Takes and returns the initialized block number. - fn take_block_number() -> Option; - /// Test that `ed25519` crypto works in the runtime. - /// - /// Returns the signature generated for the message `ed25519` and the public key. - fn test_ed25519_crypto() -> (ed25519::AppSignature, ed25519::AppPublic); - /// Test that `sr25519` crypto works in the runtime. - /// - /// Returns the signature generated for the message `sr25519`. - fn test_sr25519_crypto() -> (sr25519::AppSignature, sr25519::AppPublic); - /// Test that `ecdsa` crypto works in the runtime. - /// - /// Returns the signature generated for the message `ecdsa`. - fn test_ecdsa_crypto() -> (ecdsa::AppSignature, ecdsa::AppPublic); - /// Run various tests against storage. - fn test_storage(); - /// Check a witness. - fn test_witness(proof: StorageProof, root: crate::Hash); - /// Test that ensures that we can call a function that takes multiple - /// arguments. - fn test_multiple_arguments(data: Vec, other: Vec, num: u32); - /// Traces log "Hey I'm runtime." - fn do_trace_log(); - /// Verify the given signature, public & message bundle. - fn verify_ed25519(sig: ed25519::Signature, public: ed25519::Public, message: Vec) -> bool; - } - } - } -} - -#[derive(Clone, Eq, PartialEq, TypeInfo)] -pub struct Runtime; -impl GetNodeBlockType for Runtime { - type NodeBlock = Block; -} - -impl GetRuntimeBlockType for Runtime { - type RuntimeBlock = Block; -} - -#[derive(Clone, RuntimeDebug, Encode, Decode, PartialEq, Eq, TypeInfo, MaxEncodedLen)] -pub struct RuntimeOrigin; - -impl From::AccountId>> for RuntimeOrigin { - fn from(_: RawOrigin<::AccountId>) -> Self { - unimplemented!("Not required in tests!") +/// Balance of an account. +pub type Balance = u64; + +decl_runtime_apis! { + #[api_version(2)] + pub trait TestAPI { + /// Return the balance of the given account id. + fn balance_of(id: AccountId) -> u64; + /// A benchmark function that adds one to the given value and returns the result. + fn benchmark_add_one(val: &u64) -> u64; + /// A benchmark function that adds one to each value in the given vector and returns the + /// result. + fn benchmark_vector_add_one(vec: &Vec) -> Vec; + /// A function for that the signature changed in version `2`. + #[changed_in(2)] + fn function_signature_changed() -> Vec; + /// The new signature. + fn function_signature_changed() -> u64; + /// trie no_std testing + fn use_trie() -> u64; + /// Calls function in the loop using never-inlined function pointer + fn benchmark_indirect_call() -> u64; + /// Calls function in the loop + fn benchmark_direct_call() -> u64; + /// Allocates vector with given capacity. + fn vec_with_capacity(size: u32) -> Vec; + /// Returns the initialized block number. + fn get_block_number() -> u64; + + /// Test that `ed25519` crypto works in the runtime. + /// + /// Returns the signature generated for the message `ed25519` and the public key. + fn test_ed25519_crypto() -> (ed25519::AppSignature, ed25519::AppPublic); + /// Test that `sr25519` crypto works in the runtime. + /// + /// Returns the signature generated for the message `sr25519`. + fn test_sr25519_crypto() -> (sr25519::AppSignature, sr25519::AppPublic); + /// Test that `ecdsa` crypto works in the runtime. + /// + /// Returns the signature generated for the message `ecdsa`. + fn test_ecdsa_crypto() -> (ecdsa::AppSignature, ecdsa::AppPublic); + /// Run various tests against storage. + fn test_storage(); + /// Check a witness. + fn test_witness(proof: StorageProof, root: crate::Hash); + /// Test that ensures that we can call a function that takes multiple + /// arguments. + fn test_multiple_arguments(data: Vec, other: Vec, num: u32); + /// Traces log "Hey I'm runtime." + fn do_trace_log(); + /// Verify the given signature, public & message bundle. + fn verify_ed25519(sig: ed25519::Signature, public: ed25519::Public, message: Vec) -> bool; } } -impl CallerTrait<::AccountId> for RuntimeOrigin { - fn into_system(self) -> Option::AccountId>> { - unimplemented!("Not required in tests!") - } +pub type Executive = frame_executive::Executive< + Runtime, + Block, + frame_system::ChainContext, + Runtime, + AllPalletsWithSystem, +>; - fn as_system_ref(&self) -> Option<&RawOrigin<::AccountId>> { - unimplemented!("Not required in tests!") - } -} +#[derive(Copy, Clone, PartialEq, Eq, Encode, Decode, RuntimeDebug, TypeInfo)] +pub struct CheckSubstrateCall; -impl From for Result, RuntimeOrigin> { - fn from(_origin: RuntimeOrigin) -> Result, RuntimeOrigin> { - unimplemented!("Not required in tests!") +impl sp_runtime::traits::Printable for CheckSubstrateCall { + fn print(&self) { + "CheckSubstrateCall".print() } } -impl frame_support::traits::OriginTrait for RuntimeOrigin { - type Call = ::RuntimeCall; - type PalletsOrigin = RuntimeOrigin; - type AccountId = ::AccountId; - - fn add_filter(&mut self, _filter: impl Fn(&Self::Call) -> bool + 'static) { - unimplemented!("Not required in tests!") - } - - fn reset_filter(&mut self) { - unimplemented!("Not required in tests!") - } - - fn set_caller_from(&mut self, _other: impl Into) { - unimplemented!("Not required in tests!") - } - - fn filter_call(&self, _call: &Self::Call) -> bool { - unimplemented!("Not required in tests!") - } - - fn caller(&self) -> &Self::PalletsOrigin { - unimplemented!("Not required in tests!") - } +impl sp_runtime::traits::Dispatchable for CheckSubstrateCall { + type RuntimeOrigin = CheckSubstrateCall; + type Config = CheckSubstrateCall; + type Info = CheckSubstrateCall; + type PostInfo = CheckSubstrateCall; - fn into_caller(self) -> Self::PalletsOrigin { - unimplemented!("Not required in tests!") - } - - fn try_with_caller( + fn dispatch( self, - _f: impl FnOnce(Self::PalletsOrigin) -> Result, - ) -> Result { - unimplemented!("Not required in tests!") - } - - fn none() -> Self { - unimplemented!("Not required in tests!") - } - fn root() -> Self { - unimplemented!("Not required in tests!") - } - fn signed(_by: Self::AccountId) -> Self { - unimplemented!("Not required in tests!") - } - fn as_signed(self) -> Option { - unimplemented!("Not required in tests!") - } - fn as_system_ref(&self) -> Option<&RawOrigin> { - unimplemented!("Not required in tests!") + _origin: Self::RuntimeOrigin, + ) -> sp_runtime::DispatchResultWithInfo { + panic!("This implementation should not be used for actual dispatch."); } } -#[derive(Clone, Encode, Decode, Eq, PartialEq, RuntimeDebug, TypeInfo)] -pub struct RuntimeEvent; - -impl From> for RuntimeEvent { - fn from(_evt: frame_system::Event) -> Self { - unimplemented!("Not required in tests!") +impl sp_runtime::traits::SignedExtension for CheckSubstrateCall { + type AccountId = AccountId; + type Call = RuntimeCall; + type AdditionalSigned = (); + type Pre = (); + const IDENTIFIER: &'static str = "CheckSubstrateCall"; + + fn additional_signed( + &self, + ) -> sp_std::result::Result { + Ok(()) } -} -impl frame_support::traits::PalletInfo for Runtime { - fn index() -> Option { - let type_id = sp_std::any::TypeId::of::

(); - if type_id == sp_std::any::TypeId::of::>() { - return Some(0) - } - if type_id == sp_std::any::TypeId::of::>() { - return Some(1) + fn validate( + &self, + _who: &Self::AccountId, + call: &Self::Call, + _info: &DispatchInfoOf, + _len: usize, + ) -> TransactionValidity { + log::trace!(target: LOG_TARGET, "validate"); + match call { + RuntimeCall::SubstrateTest(ref substrate_test_call) => + substrate_test_pallet::validate_runtime_call(substrate_test_call), + _ => Ok(Default::default()), } - if type_id == sp_std::any::TypeId::of::>() { - return Some(2) - } - - None } - fn name() -> Option<&'static str> { - let type_id = sp_std::any::TypeId::of::

(); - if type_id == sp_std::any::TypeId::of::>() { - return Some("System") - } - if type_id == sp_std::any::TypeId::of::>() { - return Some("Timestamp") - } - if type_id == sp_std::any::TypeId::of::>() { - return Some("Babe") - } - - None - } - fn module_name() -> Option<&'static str> { - let type_id = sp_std::any::TypeId::of::

(); - if type_id == sp_std::any::TypeId::of::>() { - return Some("system") - } - if type_id == sp_std::any::TypeId::of::>() { - return Some("pallet_timestamp") - } - if type_id == sp_std::any::TypeId::of::>() { - return Some("pallet_babe") - } - None + fn pre_dispatch( + self, + who: &Self::AccountId, + call: &Self::Call, + info: &sp_runtime::traits::DispatchInfoOf, + len: usize, + ) -> Result { + self.validate(who, call, info, len).map(drop) } - fn crate_version() -> Option { - use frame_support::traits::PalletInfoAccess as _; - let type_id = sp_std::any::TypeId::of::

(); - if type_id == sp_std::any::TypeId::of::>() { - return Some(system::Pallet::::crate_version()) - } - if type_id == sp_std::any::TypeId::of::>() { - return Some(pallet_timestamp::Pallet::::crate_version()) - } - if type_id == sp_std::any::TypeId::of::>() { - return Some(pallet_babe::Pallet::::crate_version()) - } +} - None +construct_runtime!( + pub enum Runtime where + Block = Block, + NodeBlock = Block, + UncheckedExtrinsic = Extrinsic + { + System: frame_system, + Babe: pallet_babe, + SubstrateTest: substrate_test_pallet::pallet, + Balances: pallet_balances, } -} +); + +/// We assume that ~10% of the block weight is consumed by `on_initialize` handlers. +/// This is used to limit the maximal weight of a single extrinsic. +const AVERAGE_ON_INITIALIZE_RATIO: Perbill = Perbill::from_percent(10); +/// We allow `Normal` extrinsics to fill up the block up to 75%, the rest can be used +/// by Operational extrinsics. +const NORMAL_DISPATCH_RATIO: Perbill = Perbill::from_percent(75); +/// Max weight, actual value does not matter for test runtime. +const MAXIMUM_BLOCK_WEIGHT: Weight = + Weight::from_parts(WEIGHT_REF_TIME_PER_SECOND.saturating_mul(2), u64::MAX); parameter_types! { - pub const DbWeight: RuntimeDbWeight = RuntimeDbWeight { - read: 100, - write: 1000, - }; - pub RuntimeBlockLength: BlockLength = - BlockLength::max(4 * 1024 * 1024); - pub RuntimeBlockWeights: BlockWeights = - BlockWeights::with_sensible_defaults(Weight::from_parts(4 * 1024 * 1024, 0), Perbill::from_percent(75)); -} + pub const BlockHashCount: BlockNumber = 2400; + pub const Version: RuntimeVersion = VERSION; -impl From> for Extrinsic { - fn from(_: frame_system::Call) -> Self { - unimplemented!("Not required in tests!") - } + pub RuntimeBlockLength: BlockLength = + BlockLength::max_with_normal_ratio(5 * 1024 * 1024, NORMAL_DISPATCH_RATIO); + + pub RuntimeBlockWeights: BlockWeights = BlockWeights::builder() + .base_block(BlockExecutionWeight::get()) + .for_class(DispatchClass::all(), |weights| { + weights.base_extrinsic = ExtrinsicBaseWeight::get(); + }) + .for_class(DispatchClass::Normal, |weights| { + weights.max_total = Some(NORMAL_DISPATCH_RATIO * MAXIMUM_BLOCK_WEIGHT); + }) + .for_class(DispatchClass::Operational, |weights| { + weights.max_total = Some(MAXIMUM_BLOCK_WEIGHT); + // Operational transactions have some extra reserved space, so that they + // are included even if block reached `MAXIMUM_BLOCK_WEIGHT`. + weights.reserved = Some( + MAXIMUM_BLOCK_WEIGHT - NORMAL_DISPATCH_RATIO * MAXIMUM_BLOCK_WEIGHT + ); + }) + .avg_block_initialization(AVERAGE_ON_INITIALIZE_RATIO) + .build_or_panic(); } impl frame_system::pallet::Config for Runtime { type BaseCallFilter = frame_support::traits::Everything; type BlockWeights = RuntimeBlockWeights; - type BlockLength = RuntimeBlockLength; + type BlockLength = (); type RuntimeOrigin = RuntimeOrigin; - type RuntimeCall = Extrinsic; - type Index = u64; - type BlockNumber = u64; + type RuntimeCall = RuntimeCall; + type Index = Index; + type BlockNumber = BlockNumber; type Hash = H256; type Hashing = Hashing; - type AccountId = u64; - type Lookup = IdentityLookup; + type AccountId = AccountId; + type Lookup = sp_runtime::traits::IdentityLookup; type Header = Header; type RuntimeEvent = RuntimeEvent; type BlockHashCount = ConstU64<2400>; type DbWeight = (); type Version = (); - type PalletInfo = Self; - type AccountData = (); + type PalletInfo = PalletInfo; + type AccountData = pallet_balances::AccountData; type OnNewAccount = (); type OnKilledAccount = (); type SystemWeightInfo = (); @@ -646,14 +369,45 @@ impl frame_system::pallet::Config for Runtime { type MaxConsumers = ConstU32<16>; } -impl system::Config for Runtime {} +pub mod currency { + use crate::Balance; + const MILLICENTS: Balance = 1_000_000_000; + const CENTS: Balance = 1_000 * MILLICENTS; // assume this is worth about a cent. + pub const DOLLARS: Balance = 100 * CENTS; +} +parameter_types! { + pub const ExistentialDeposit: Balance = 1 * currency::DOLLARS; + // For weight estimation, we assume that the most locks on an individual account will be 50. + // This number may need to be adjusted in the future if this assumption no longer holds true. + pub const MaxLocks: u32 = 50; + pub const MaxReserves: u32 = 50; +} + +impl pallet_balances::Config for Runtime { + type MaxLocks = MaxLocks; + type MaxReserves = MaxReserves; + type ReserveIdentifier = [u8; 8]; + type Balance = Balance; + type DustRemoval = (); + type RuntimeEvent = RuntimeEvent; + type ExistentialDeposit = ExistentialDeposit; + type AccountStore = System; + type WeightInfo = pallet_balances::weights::SubstrateWeight; + type FreezeIdentifier = (); + type MaxFreezes = (); + type HoldIdentifier = (); + type MaxHolds = ConstU32<1>; +} + +impl substrate_test_pallet::Config for Runtime {} + +// Required for `pallet_babe::Config`. impl pallet_timestamp::Config for Runtime { - /// A timestamp: milliseconds since the unix epoch. type Moment = u64; - type OnTimestampSet = (); - type MinimumPeriod = ConstU64<5>; - type WeightInfo = (); + type OnTimestampSet = Babe; + type MinimumPeriod = ConstU64<500>; + type WeightInfo = pallet_timestamp::weights::SubstrateWeight; } parameter_types! { @@ -663,15 +417,12 @@ parameter_types! { impl pallet_babe::Config for Runtime { type EpochDuration = EpochDuration; type ExpectedBlockTime = ConstU64<10_000>; - // there is no actual runtime in this test-runtime, so testing crates - // are manually adding the digests. normally in this situation you'd use - // pallet_babe::SameAuthoritiesForever. - type EpochChangeTrigger = pallet_babe::ExternalTrigger; + type EpochChangeTrigger = pallet_babe::SameAuthoritiesForever; type DisabledValidators = (); - type WeightInfo = (); - type MaxAuthorities = ConstU32<10>; type KeyOwnerProof = sp_core::Void; type EquivocationReportSystem = (); + type WeightInfo = (); + type MaxAuthorities = ConstU32<10>; } /// Adds one to the given input and returns the final result. @@ -680,11 +431,6 @@ fn benchmark_add_one(i: u64) -> u64 { i + 1 } -/// The `benchmark_add_one` function as function pointer. -#[cfg(not(feature = "std"))] -static BENCHMARK_ADD_ONE: sp_runtime_interface::wasm::ExchangeableFunction u64> = - sp_runtime_interface::wasm::ExchangeableFunction::new(benchmark_add_one); - fn code_using_trie() -> u64 { let pairs = [ (b"0103000000000000000464".to_vec(), b"0400000000".to_vec()), @@ -717,549 +463,257 @@ impl_opaque_keys! { } } -cfg_if! { - if #[cfg(feature = "std")] { - impl_runtime_apis! { - impl sp_api::Core for Runtime { - fn version() -> RuntimeVersion { - version() - } - - fn execute_block(block: Block) { - system::execute_block(block); - } - - fn initialize_block(header: &::Header) { - system::initialize_block(header) - } - } +pub(crate) const TEST_RUNTIME_BABE_EPOCH_CONFIGURATION: BabeEpochConfiguration = + BabeEpochConfiguration { + c: (3, 10), + allowed_slots: AllowedSlots::PrimaryAndSecondaryPlainSlots, + }; - impl sp_api::Metadata for Runtime { - fn metadata() -> OpaqueMetadata { - unimplemented!() - } +impl_runtime_apis! { + impl sp_api::Core for Runtime { + fn version() -> RuntimeVersion { + version() + } - fn metadata_at_version(_version: u32) -> Option { - unimplemented!() - } + fn execute_block(block: Block) { + log::trace!(target: LOG_TARGET, "execute_block: {block:#?}"); + Executive::execute_block(block); + } - fn metadata_versions() -> sp_std::vec::Vec { - unimplemented!() - } - } + fn initialize_block(header: &::Header) { + log::trace!(target: LOG_TARGET, "initialize_block: {header:#?}"); + Executive::initialize_block(header); + } + } - impl sp_transaction_pool::runtime_api::TaggedTransactionQueue for Runtime { - fn validate_transaction( - _source: TransactionSource, - utx: ::Extrinsic, - _: ::Hash, - ) -> TransactionValidity { - if let Extrinsic::IncludeData(data) = utx { - return Ok(ValidTransaction { - priority: data.len() as u64, - requires: vec![], - provides: vec![data], - longevity: 1, - propagate: false, - }); - } - - system::validate_transaction(utx) - } - } + impl sp_api::Metadata for Runtime { + fn metadata() -> OpaqueMetadata { + unimplemented!() + } - impl sp_block_builder::BlockBuilder for Runtime { - fn apply_extrinsic(extrinsic: ::Extrinsic) -> ApplyExtrinsicResult { - system::execute_transaction(extrinsic) - } + fn metadata_at_version(_version: u32) -> Option { + unimplemented!() + } + fn metadata_versions() -> sp_std::vec::Vec { + unimplemented!() + } + } - fn finalize_block() -> ::Header { - system::finalize_block() - } + impl sp_transaction_pool::runtime_api::TaggedTransactionQueue for Runtime { + fn validate_transaction( + source: TransactionSource, + utx: ::Extrinsic, + block_hash: ::Hash, + ) -> TransactionValidity { + let validity = Executive::validate_transaction(source, utx.clone(), block_hash); + log::trace!(target: LOG_TARGET, "validate_transaction {:?} {:?}", utx, validity); + validity + } + } - fn inherent_extrinsics(_data: InherentData) -> Vec<::Extrinsic> { - vec![] - } + impl sp_block_builder::BlockBuilder for Runtime { + fn apply_extrinsic(extrinsic: ::Extrinsic) -> ApplyExtrinsicResult { + Executive::apply_extrinsic(extrinsic) + } - fn check_inherents(_block: Block, _data: InherentData) -> CheckInherentsResult { - CheckInherentsResult::new() - } - } + fn finalize_block() -> ::Header { + log::trace!(target: LOG_TARGET, "finalize_block"); + Executive::finalize_block() + } - impl self::TestAPI for Runtime { - fn balance_of(id: AccountId) -> u64 { - system::balance_of(id) - } - - fn benchmark_add_one(val: &u64) -> u64 { - val + 1 - } - - fn benchmark_vector_add_one(vec: &Vec) -> Vec { - let mut vec = vec.clone(); - vec.iter_mut().for_each(|v| *v += 1); - vec - } - - fn fail_convert_parameter(_: DecodeFails) {} - - fn fail_convert_return_value() -> DecodeFails { - DecodeFails::default() - } - - fn function_signature_changed() -> u64 { - 1 - } - - fn fail_on_native() -> u64 { - panic!("Failing because we are on native") - } - fn fail_on_wasm() -> u64 { - 1 - } - - fn use_trie() -> u64 { - code_using_trie() - } - - fn benchmark_indirect_call() -> u64 { - let function = benchmark_add_one; - (0..1000).fold(0, |p, i| p + function(i)) - } - fn benchmark_direct_call() -> u64 { - (0..1000).fold(0, |p, i| p + benchmark_add_one(i)) - } - - fn vec_with_capacity(_size: u32) -> Vec { - unimplemented!("is not expected to be invoked from non-wasm builds"); - } - - fn get_block_number() -> u64 { - system::get_block_number().expect("Block number is initialized") - } - - fn take_block_number() -> Option { - system::take_block_number() - } - - fn test_ed25519_crypto() -> (ed25519::AppSignature, ed25519::AppPublic) { - test_ed25519_crypto() - } - - fn test_sr25519_crypto() -> (sr25519::AppSignature, sr25519::AppPublic) { - test_sr25519_crypto() - } - - fn test_ecdsa_crypto() -> (ecdsa::AppSignature, ecdsa::AppPublic) { - test_ecdsa_crypto() - } - - fn test_storage() { - test_read_storage(); - test_read_child_storage(); - } - - fn test_witness(proof: StorageProof, root: crate::Hash) { - test_witness(proof, root); - } - - fn test_multiple_arguments(data: Vec, other: Vec, num: u32) { - assert_eq!(&data[..], &other[..]); - assert_eq!(data.len(), num as usize); - } - - fn do_trace_log() { - log::trace!("Hey I'm runtime"); - } - - fn verify_ed25519(sig: ed25519::Signature, public: ed25519::Public, message: Vec) -> bool { - sp_io::crypto::ed25519_verify(&sig, &message, &public) - } - } + fn inherent_extrinsics(_data: InherentData) -> Vec<::Extrinsic> { + vec![] + } - impl sp_consensus_aura::AuraApi for Runtime { - fn slot_duration() -> sp_consensus_aura::SlotDuration { - sp_consensus_aura::SlotDuration::from_millis(1000) - } - - fn authorities() -> Vec { - system::authorities().into_iter().map(|a| { - let authority: sr25519::Public = a.into(); - AuraId::from(authority) - }).collect() - } - } + fn check_inherents(_block: Block, _data: InherentData) -> CheckInherentsResult { + CheckInherentsResult::new() + } + } - impl sp_consensus_babe::BabeApi for Runtime { - fn configuration() -> sp_consensus_babe::BabeConfiguration { - sp_consensus_babe::BabeConfiguration { - slot_duration: 1000, - epoch_length: EpochDuration::get(), - c: (3, 10), - authorities: system::authorities() - .into_iter().map(|x|(x, 1)).collect(), - randomness: >::randomness(), - allowed_slots: AllowedSlots::PrimaryAndSecondaryPlainSlots, - } - } - - fn current_epoch_start() -> Slot { - >::current_epoch_start() - } - - fn current_epoch() -> sp_consensus_babe::Epoch { - >::current_epoch() - } - - fn next_epoch() -> sp_consensus_babe::Epoch { - >::next_epoch() - } - - fn submit_report_equivocation_unsigned_extrinsic( - _equivocation_proof: sp_consensus_babe::EquivocationProof< - ::Header, - >, - _key_owner_proof: sp_consensus_babe::OpaqueKeyOwnershipProof, - ) -> Option<()> { - None - } - - fn generate_key_ownership_proof( - _slot: sp_consensus_babe::Slot, - _authority_id: sp_consensus_babe::AuthorityId, - ) -> Option { - None - } - } + impl frame_system_rpc_runtime_api::AccountNonceApi for Runtime { + fn account_nonce(account: AccountId) -> Index { + System::account_nonce(account) + } + } - impl sp_offchain::OffchainWorkerApi for Runtime { - fn offchain_worker(header: &::Header) { - let ex = Extrinsic::IncludeData(header.number.encode()); - sp_io::offchain::submit_transaction(ex.encode()).unwrap(); - } - } + impl self::TestAPI for Runtime { + fn balance_of(id: AccountId) -> u64 { + Balances::free_balance(id) + } - impl sp_session::SessionKeys for Runtime { - fn generate_session_keys(_: Option>) -> Vec { - SessionKeys::generate(None) - } + fn benchmark_add_one(val: &u64) -> u64 { + val + 1 + } - fn decode_session_keys( - encoded: Vec, - ) -> Option, sp_core::crypto::KeyTypeId)>> { - SessionKeys::decode_into_raw_public_keys(&encoded) - } - } + fn benchmark_vector_add_one(vec: &Vec) -> Vec { + let mut vec = vec.clone(); + vec.iter_mut().for_each(|v| *v += 1); + vec + } - impl sp_consensus_grandpa::GrandpaApi for Runtime { - fn grandpa_authorities() -> sp_consensus_grandpa::AuthorityList { - Vec::new() - } - - fn current_set_id() -> sp_consensus_grandpa::SetId { - 0 - } - - fn submit_report_equivocation_unsigned_extrinsic( - _equivocation_proof: sp_consensus_grandpa::EquivocationProof< - ::Hash, - NumberFor, - >, - _key_owner_proof: sp_consensus_grandpa::OpaqueKeyOwnershipProof, - ) -> Option<()> { - None - } - - fn generate_key_ownership_proof( - _set_id: sp_consensus_grandpa::SetId, - _authority_id: sp_consensus_grandpa::AuthorityId, - ) -> Option { - None - } - } + fn function_signature_changed() -> u64 { + 1 + } - impl sp_consensus_beefy::BeefyApi for Runtime { - fn beefy_genesis() -> Option { - None - } - - fn validator_set() -> Option> { - None - } - - fn submit_report_equivocation_unsigned_extrinsic( - _equivocation_proof: sp_consensus_beefy::EquivocationProof< - NumberFor, - sp_consensus_beefy::crypto::AuthorityId, - sp_consensus_beefy::crypto::Signature - >, - _key_owner_proof: sp_consensus_beefy::OpaqueKeyOwnershipProof, - ) -> Option<()> { None } - - fn generate_key_ownership_proof( - _set_id: sp_consensus_beefy::ValidatorSetId, - _authority_id: sp_consensus_beefy::crypto::AuthorityId, - ) -> Option { None } - } + fn use_trie() -> u64 { + code_using_trie() + } - impl pallet_beefy_mmr::BeefyMmrApi for Runtime { - fn authority_set_proof() -> sp_consensus_beefy::mmr::BeefyAuthoritySet { - Default::default() - } + fn benchmark_indirect_call() -> u64 { + let function = benchmark_add_one; + (0..1000).fold(0, |p, i| p + function(i)) + } + fn benchmark_direct_call() -> u64 { + (0..1000).fold(0, |p, i| p + benchmark_add_one(i)) + } - fn next_authority_set_proof() -> sp_consensus_beefy::mmr::BeefyNextAuthoritySet { - Default::default() - } - } + fn vec_with_capacity(size: u32) -> Vec { + Vec::with_capacity(size as usize) + } - impl frame_system_rpc_runtime_api::AccountNonceApi for Runtime { - fn account_nonce(_account: AccountId) -> Index { - 0 - } - } + fn get_block_number() -> u64 { + System::block_number() + } + + fn test_ed25519_crypto() -> (ed25519::AppSignature, ed25519::AppPublic) { + test_ed25519_crypto() } - } else { - impl_runtime_apis! { - impl sp_api::Core for Runtime { - fn version() -> RuntimeVersion { - version() - } - - fn execute_block(block: Block) { - system::execute_block(block); - } - - fn initialize_block(header: &::Header) { - system::initialize_block(header) - } - } - impl sp_api::Metadata for Runtime { - fn metadata() -> OpaqueMetadata { - unimplemented!() - } + fn test_sr25519_crypto() -> (sr25519::AppSignature, sr25519::AppPublic) { + test_sr25519_crypto() + } - fn metadata_at_version(_version: u32) -> Option { - unimplemented!() - } + fn test_ecdsa_crypto() -> (ecdsa::AppSignature, ecdsa::AppPublic) { + test_ecdsa_crypto() + } - fn metadata_versions() -> sp_std::vec::Vec { - unimplemented!() - } - } + fn test_storage() { + test_read_storage(); + test_read_child_storage(); + } - impl sp_transaction_pool::runtime_api::TaggedTransactionQueue for Runtime { - fn validate_transaction( - _source: TransactionSource, - utx: ::Extrinsic, - _: ::Hash, - ) -> TransactionValidity { - if let Extrinsic::IncludeData(data) = utx { - return Ok(ValidTransaction{ - priority: data.len() as u64, - requires: vec![], - provides: vec![data], - longevity: 1, - propagate: false, - }); - } - - system::validate_transaction(utx) - } - } + fn test_witness(proof: StorageProof, root: crate::Hash) { + test_witness(proof, root); + } + + fn test_multiple_arguments(data: Vec, other: Vec, num: u32) { + assert_eq!(&data[..], &other[..]); + assert_eq!(data.len(), num as usize); + } - impl sp_block_builder::BlockBuilder for Runtime { - fn apply_extrinsic(extrinsic: ::Extrinsic) -> ApplyExtrinsicResult { - system::execute_transaction(extrinsic) - } + fn do_trace_log() { + log::trace!("Hey I'm runtime"); + } - fn finalize_block() -> ::Header { - system::finalize_block() - } + fn verify_ed25519(sig: ed25519::Signature, public: ed25519::Public, message: Vec) -> bool { + sp_io::crypto::ed25519_verify(&sig, &message, &public) + } + } - fn inherent_extrinsics(_data: InherentData) -> Vec<::Extrinsic> { - vec![] - } + impl sp_consensus_aura::AuraApi for Runtime { + fn slot_duration() -> sp_consensus_aura::SlotDuration { + sp_consensus_aura::SlotDuration::from_millis(1000) + } - fn check_inherents(_block: Block, _data: InherentData) -> CheckInherentsResult { - CheckInherentsResult::new() - } - } + fn authorities() -> Vec { + SubstrateTest::authorities().into_iter().map(|a| { + let authority: sr25519::Public = a.into(); + AuraId::from(authority) + }).collect() + } + } - impl self::TestAPI for Runtime { - fn balance_of(id: AccountId) -> u64 { - system::balance_of(id) - } - - fn benchmark_add_one(val: &u64) -> u64 { - val + 1 - } - - fn benchmark_vector_add_one(vec: &Vec) -> Vec { - let mut vec = vec.clone(); - vec.iter_mut().for_each(|v| *v += 1); - vec - } - - fn fail_convert_parameter(_: DecodeFails) {} - - fn fail_convert_return_value() -> DecodeFails { - DecodeFails::default() - } - - fn function_signature_changed() -> Vec { - let mut vec = Vec::new(); - vec.push(1); - vec.push(2); - vec - } - - fn fail_on_native() -> u64 { - 1 - } - - fn fail_on_wasm() -> u64 { - panic!("Failing because we are on wasm") - } - - fn use_trie() -> u64 { - code_using_trie() - } - - fn benchmark_indirect_call() -> u64 { - (0..10000).fold(0, |p, i| p + BENCHMARK_ADD_ONE.get()(i)) - } - - fn benchmark_direct_call() -> u64 { - (0..10000).fold(0, |p, i| p + benchmark_add_one(i)) - } - - fn vec_with_capacity(size: u32) -> Vec { - Vec::with_capacity(size as usize) - } - - fn get_block_number() -> u64 { - system::get_block_number().expect("Block number is initialized") - } - - fn take_block_number() -> Option { - system::take_block_number() - } - - fn test_ed25519_crypto() -> (ed25519::AppSignature, ed25519::AppPublic) { - test_ed25519_crypto() - } - - fn test_sr25519_crypto() -> (sr25519::AppSignature, sr25519::AppPublic) { - test_sr25519_crypto() - } - - fn test_ecdsa_crypto() -> (ecdsa::AppSignature, ecdsa::AppPublic) { - test_ecdsa_crypto() - } - - fn test_storage() { - test_read_storage(); - test_read_child_storage(); - } - - fn test_witness(proof: StorageProof, root: crate::Hash) { - test_witness(proof, root); - } - - fn test_multiple_arguments(data: Vec, other: Vec, num: u32) { - assert_eq!(&data[..], &other[..]); - assert_eq!(data.len(), num as usize); - } - - fn do_trace_log() { - log::trace!("Hey I'm runtime: {}", log::STATIC_MAX_LEVEL); - } - - fn verify_ed25519(sig: ed25519::Signature, public: ed25519::Public, message: Vec) -> bool { - sp_io::crypto::ed25519_verify(&sig, &message, &public) - } + impl sp_consensus_babe::BabeApi for Runtime { + fn configuration() -> sp_consensus_babe::BabeConfiguration { + let epoch_config = Babe::epoch_config().unwrap_or(TEST_RUNTIME_BABE_EPOCH_CONFIGURATION); + sp_consensus_babe::BabeConfiguration { + slot_duration: Babe::slot_duration(), + epoch_length: EpochDuration::get(), + c: epoch_config.c, + authorities: SubstrateTest::authorities() + .into_iter().map(|x|(x, 1)).collect(), + randomness: Babe::randomness(), + allowed_slots: epoch_config.allowed_slots, } + } - impl sp_consensus_aura::AuraApi for Runtime { - fn slot_duration() -> sp_consensus_aura::SlotDuration { - sp_consensus_aura::SlotDuration::from_millis(1000) - } - - fn authorities() -> Vec { - system::authorities().into_iter().map(|a| { - let authority: sr25519::Public = a.into(); - AuraId::from(authority) - }).collect() - } - } + fn current_epoch_start() -> Slot { + Babe::current_epoch_start() + } - impl sp_consensus_babe::BabeApi for Runtime { - fn configuration() -> sp_consensus_babe::BabeConfiguration { - sp_consensus_babe::BabeConfiguration { - slot_duration: 1000, - epoch_length: EpochDuration::get(), - c: (3, 10), - authorities: system::authorities() - .into_iter().map(|x|(x, 1)).collect(), - randomness: >::randomness(), - allowed_slots: AllowedSlots::PrimaryAndSecondaryPlainSlots, - } - } - - fn current_epoch_start() -> Slot { - >::current_epoch_start() - } - - fn current_epoch() -> sp_consensus_babe::Epoch { - >::current_epoch() - } - - fn next_epoch() -> sp_consensus_babe::Epoch { - >::next_epoch() - } - - fn submit_report_equivocation_unsigned_extrinsic( - _equivocation_proof: sp_consensus_babe::EquivocationProof< - ::Header, - >, - _key_owner_proof: sp_consensus_babe::OpaqueKeyOwnershipProof, - ) -> Option<()> { - None - } - - fn generate_key_ownership_proof( - _slot: sp_consensus_babe::Slot, - _authority_id: sp_consensus_babe::AuthorityId, - ) -> Option { - None - } - } + fn current_epoch() -> sp_consensus_babe::Epoch { + Babe::current_epoch() + } - impl sp_offchain::OffchainWorkerApi for Runtime { - fn offchain_worker(header: &::Header) { - let ex = Extrinsic::IncludeData(header.number.encode()); - sp_io::offchain::submit_transaction(ex.encode()).unwrap() - } - } + fn next_epoch() -> sp_consensus_babe::Epoch { + Babe::next_epoch() + } - impl sp_session::SessionKeys for Runtime { - fn generate_session_keys(_: Option>) -> Vec { - SessionKeys::generate(None) - } + fn submit_report_equivocation_unsigned_extrinsic( + _equivocation_proof: sp_consensus_babe::EquivocationProof< + ::Header, + >, + _key_owner_proof: sp_consensus_babe::OpaqueKeyOwnershipProof, + ) -> Option<()> { + None + } - fn decode_session_keys( - encoded: Vec, - ) -> Option, sp_core::crypto::KeyTypeId)>> { - SessionKeys::decode_into_raw_public_keys(&encoded) - } - } + fn generate_key_ownership_proof( + _slot: sp_consensus_babe::Slot, + _authority_id: sp_consensus_babe::AuthorityId, + ) -> Option { + None + } + } - impl frame_system_rpc_runtime_api::AccountNonceApi for Runtime { - fn account_nonce(_account: AccountId) -> Index { - 0 - } - } + impl sp_offchain::OffchainWorkerApi for Runtime { + fn offchain_worker(header: &::Header) { + let ext = Extrinsic::new_unsigned( + substrate_test_pallet::pallet::Call::storage_change{ + key:b"some_key".encode(), + value:Some(header.number.encode()) + }.into(), + ); + sp_io::offchain::submit_transaction(ext.encode()).unwrap(); + } + } + + impl sp_session::SessionKeys for Runtime { + fn generate_session_keys(_: Option>) -> Vec { + SessionKeys::generate(None) + } + + fn decode_session_keys( + encoded: Vec, + ) -> Option, sp_core::crypto::KeyTypeId)>> { + SessionKeys::decode_into_raw_public_keys(&encoded) + } + } + + impl sp_consensus_grandpa::GrandpaApi for Runtime { + fn grandpa_authorities() -> sp_consensus_grandpa::AuthorityList { + Vec::new() + } + + fn current_set_id() -> sp_consensus_grandpa::SetId { + 0 + } + + fn submit_report_equivocation_unsigned_extrinsic( + _equivocation_proof: sp_consensus_grandpa::EquivocationProof< + ::Hash, + NumberFor, + >, + _key_owner_proof: sp_consensus_grandpa::OpaqueKeyOwnershipProof, + ) -> Option<()> { + None + } + + fn generate_key_ownership_proof( + _set_id: sp_consensus_grandpa::SetId, + _authority_id: sp_consensus_grandpa::AuthorityId, + ) -> Option { + None } } } @@ -1360,13 +814,194 @@ fn test_witness(proof: StorageProof, root: crate::Hash) { assert!(ext.storage_root(Default::default()).as_slice() != &root[..]); } +/// Some tests require the hashed keys of the storage. As the values of hashed keys are not trivial +/// to guess, this small module provides the values of the keys, and the code which is required to +/// generate the keys. +#[cfg(feature = "std")] +pub mod storage_key_generator { + use super::*; + use sp_core::Pair; + use sp_keyring::AccountKeyring; + + /// Generate hex string without prefix + pub(super) fn hex(x: T) -> String + where + T: array_bytes::Hex, + { + x.hex(Default::default()) + } + + fn concat_hashes(input: &Vec<&[u8]>) -> String { + input.iter().map(|s| sp_core::hashing::twox_128(s)).map(hex).collect() + } + + fn twox_64_concat(x: &[u8]) -> Vec { + sp_core::hashing::twox_64(x).iter().chain(x.iter()).cloned().collect::>() + } + + /// Generate the hashed storage keys from the raw literals. These keys are expected to be be in + /// storage with given substrate-test runtime. + pub fn generate_expected_storage_hashed_keys() -> Vec { + let literals: Vec<&[u8]> = vec![b":code", b":extrinsic_index", b":heappages"]; + + let keys: Vec> = vec![ + vec![b"Babe", b"Authorities"], + vec![b"Babe", b"EpochConfig"], + vec![b"Babe", b"NextAuthorities"], + vec![b"Babe", b"SegmentIndex"], + vec![b"Babe", b":__STORAGE_VERSION__:"], + vec![b"Balances", b":__STORAGE_VERSION__:"], + vec![b"Balances", b"TotalIssuance"], + vec![b"SubstrateTest", b"Authorities"], + vec![b"SubstrateTest", b":__STORAGE_VERSION__:"], + vec![b"System", b"LastRuntimeUpgrade"], + vec![b"System", b"ParentHash"], + vec![b"System", b":__STORAGE_VERSION__:"], + vec![b"System", b"UpgradedToTripleRefCount"], + vec![b"System", b"UpgradedToU32RefCount"], + ]; + + let mut expected_keys = keys.iter().map(concat_hashes).collect::>(); + + expected_keys.extend(literals.into_iter().map(hex)); + + let balances_map_keys = (0..16_usize) + .into_iter() + .map(|i| AccountKeyring::numeric(i).public().to_vec()) + .chain(vec![ + AccountKeyring::Alice.public().to_vec(), + AccountKeyring::Bob.public().to_vec(), + AccountKeyring::Charlie.public().to_vec(), + ]) + .map(|pubkey| { + sp_core::hashing::blake2_128(&pubkey) + .iter() + .chain(pubkey.iter()) + .cloned() + .collect::>() + }) + .map(|hash_pubkey| { + [concat_hashes(&vec![b"System", b"Account"]), hex(hash_pubkey)].concat() + }); + + expected_keys.extend(balances_map_keys); + + expected_keys.push( + [ + concat_hashes(&vec![b"System", b"BlockHash"]), + hex(0u64.using_encoded(twox_64_concat)), + ] + .concat(), + ); + + expected_keys.sort(); + expected_keys + } + + /// Provides the commented list of hashed keys. This contains a hard-coded list of hashed keys + /// that would be generated by `generate_expected_storage_hashed_keys`. This list is provided + /// for the debugging convenience only. Value of each hex-string is documented with the literal + /// origin. + pub fn get_expected_storage_hashed_keys() -> Vec { + [ + //System|:__STORAGE_VERSION__: + "00771836bebdd29870ff246d305c578c4e7b9012096b41c4eb3aaf947f6ea429", + //SubstrateTest|Authorities + "00771836bebdd29870ff246d305c578c5e0621c4869aa60c02be9adcc98a0d1d", + //Babe|:__STORAGE_VERSION__: + "1cb6f36e027abb2091cfb5110ab5087f4e7b9012096b41c4eb3aaf947f6ea429", + //Babe|Authorities + "1cb6f36e027abb2091cfb5110ab5087f5e0621c4869aa60c02be9adcc98a0d1d", + //Babe|SegmentIndex + "1cb6f36e027abb2091cfb5110ab5087f66e8f035c8adbe7f1547b43c51e6f8a4", + //Babe|NextAuthorities + "1cb6f36e027abb2091cfb5110ab5087faacf00b9b41fda7a9268821c2a2b3e4c", + //Babe|EpochConfig + "1cb6f36e027abb2091cfb5110ab5087fdc6b171b77304263c292cc3ea5ed31ef", + //System|:__STORAGE_VERSION__: + "26aa394eea5630e07c48ae0c9558cef74e7b9012096b41c4eb3aaf947f6ea429", + //System|UpgradedToU32RefCount + "26aa394eea5630e07c48ae0c9558cef75684a022a34dd8bfa2baaf44f172b710", + //System|ParentHash + "26aa394eea5630e07c48ae0c9558cef78a42f33323cb5ced3b44dd825fda9fcc", + //System::BlockHash|0 + "26aa394eea5630e07c48ae0c9558cef7a44704b568d21667356a5a050c118746bb1bdbcacd6ac9340000000000000000", + //System|UpgradedToTripleRefCount + "26aa394eea5630e07c48ae0c9558cef7a7fd6c28836b9a28522dc924110cf439", + + // System|Account|blake2_128Concat("//11") + "26aa394eea5630e07c48ae0c9558cef7b99d880ec681799c0cf30e8886371da901cae4e3edfbb32c91ed3f01ab964f4eeeab50338d8e5176d3141802d7b010a55dadcd5f23cf8aaafa724627e967e90e", + // System|Account|blake2_128Concat("//4") + "26aa394eea5630e07c48ae0c9558cef7b99d880ec681799c0cf30e8886371da91b614bd4a126f2d5d294e9a8af9da25248d7e931307afb4b68d8d565d4c66e00d856c6d65f5fed6bb82dcfb60e936c67", + // System|Account|blake2_128Concat("//7") + "26aa394eea5630e07c48ae0c9558cef7b99d880ec681799c0cf30e8886371da94b21aff9fe1e8b2fc4b0775b8cbeff28ba8e2c7594dd74730f3ca835e95455d199261897edc9735d602ea29615e2b10b", + // System|Account|blake2_128Concat("//Bob") + "26aa394eea5630e07c48ae0c9558cef7b99d880ec681799c0cf30e8886371da94f9aea1afa791265fae359272badc1cf8eaf04151687736326c9fea17e25fc5287613693c912909cb226aa4794f26a48", + // System|Account|blake2_128Concat("//3") + "26aa394eea5630e07c48ae0c9558cef7b99d880ec681799c0cf30e8886371da95786a2916fcb81e1bd5dcd81e0d2452884617f575372edb5a36d85c04cdf2e4699f96fe33eb5f94a28c041b88e398d0c", + // System|Account|blake2_128Concat("//14") + "26aa394eea5630e07c48ae0c9558cef7b99d880ec681799c0cf30e8886371da95b8542d9672c7b7e779cc7c1e6b605691c2115d06120ea2bee32dd601d02f36367564e7ddf84ae2717ca3f097459652e", + // System|Account|blake2_128Concat("//6") + "26aa394eea5630e07c48ae0c9558cef7b99d880ec681799c0cf30e8886371da996c30bdbfab640838e6b6d3c33ab4adb4211b79e34ee8072eab506edd4b93a7b85a14c9a05e5cdd056d98e7dbca87730", + // System|Account|blake2_128Concat("//9") + "26aa394eea5630e07c48ae0c9558cef7b99d880ec681799c0cf30e8886371da99dc65b1339ec388fbf2ca0cdef51253512c6cfd663203ea16968594f24690338befd906856c4d2f4ef32dad578dba20c", + // System|Account|blake2_128Concat("//8") + "26aa394eea5630e07c48ae0c9558cef7b99d880ec681799c0cf30e8886371da99e6eb5abd62f5fd54793da91a47e6af6125d57171ff9241f07acaa1bb6a6103517965cf2cd00e643b27e7599ebccba70", + // System|Account|blake2_128Concat("//Charlie") + "26aa394eea5630e07c48ae0c9558cef7b99d880ec681799c0cf30e8886371da9b0edae20838083f2cde1c4080db8cf8090b5ab205c6974c9ea841be688864633dc9ca8a357843eeacf2314649965fe22", + // System|Account|blake2_128Concat("//10") + "26aa394eea5630e07c48ae0c9558cef7b99d880ec681799c0cf30e8886371da9d0052993b6f3bd0544fd1f5e4125b9fbde3e789ecd53431fe5c06c12b72137153496dace35c695b5f4d7b41f7ed5763b", + // System|Account|blake2_128Concat("//1") + "26aa394eea5630e07c48ae0c9558cef7b99d880ec681799c0cf30e8886371da9d6b7e9a5f12bc571053265dade10d3b4b606fc73f57f03cdb4c932d475ab426043e429cecc2ffff0d2672b0df8398c48", + // System|Account|blake2_128Concat("//Alice") + "26aa394eea5630e07c48ae0c9558cef7b99d880ec681799c0cf30e8886371da9de1e86a9a8c739864cf3cc5ec2bea59fd43593c715fdd31c61141abd04a99fd6822c8558854ccde39a5684e7a56da27d", + // System|Account|blake2_128Concat("//2") + "26aa394eea5630e07c48ae0c9558cef7b99d880ec681799c0cf30e8886371da9e1a35f56ee295d39287cbffcfc60c4b346f136b564e1fad55031404dd84e5cd3fa76bfe7cc7599b39d38fd06663bbc0a", + // System|Account|blake2_128Concat("//5") + "26aa394eea5630e07c48ae0c9558cef7b99d880ec681799c0cf30e8886371da9e2c1dc507e2035edbbd8776c440d870460c57f0008067cc01c5ff9eb2e2f9b3a94299a915a91198bd1021a6c55596f57", + // System|Account|blake2_128Concat("//0") + "26aa394eea5630e07c48ae0c9558cef7b99d880ec681799c0cf30e8886371da9eca0e653a94f4080f6311b4e7b6934eb2afba9278e30ccf6a6ceb3a8b6e336b70068f045c666f2e7f4f9cc5f47db8972", + // System|Account|blake2_128Concat("//13") + "26aa394eea5630e07c48ae0c9558cef7b99d880ec681799c0cf30e8886371da9ee8bf7ef90fc56a8aa3b90b344c599550c29b161e27ff8ba45bf6bad4711f326fc506a8803453a4d7e3158e993495f10", + // System|Account|blake2_128Concat("//12") + "26aa394eea5630e07c48ae0c9558cef7b99d880ec681799c0cf30e8886371da9f5d6f1c082fe63eec7a71fcad00f4a892e3d43b7b0d04e776e69e7be35247cecdac65504c579195731eaf64b7940966e", + // System|Account|blake2_128Concat("//15") + "26aa394eea5630e07c48ae0c9558cef7b99d880ec681799c0cf30e8886371da9fbf0818841edf110e05228a6379763c4fc3c37459d9bdc61f58a5ebc01e9e2305a19d390c0543dc733861ec3cf1de01f", + // System|LastRuntimeUpgrade + "26aa394eea5630e07c48ae0c9558cef7f9cce9c888469bb1a0dceaa129672ef8", + // :code + "3a636f6465", + // :extrinsic_index + "3a65787472696e7369635f696e646578", + // :heappages + "3a686561707061676573", + // Balances|:__STORAGE_VERSION__: + "c2261276cc9d1f8598ea4b6a74b15c2f4e7b9012096b41c4eb3aaf947f6ea429", + // Balances|TotalIssuance + "c2261276cc9d1f8598ea4b6a74b15c2f57c875e4cff74148e4628f264b974c80", + ].into_iter().map(String::from).collect::>() + } + + #[test] + fn expected_keys_vec_are_matching() { + assert_eq!( + storage_key_generator::get_expected_storage_hashed_keys(), + storage_key_generator::generate_expected_storage_hashed_keys(), + ); + } +} + #[cfg(test)] mod tests { + use super::*; use codec::Encode; + use frame_support::dispatch::DispatchInfo; use sc_block_builder::BlockBuilderProvider; use sp_api::ProvideRuntimeApi; use sp_consensus::BlockOrigin; use sp_core::{storage::well_known_keys::HEAP_PAGES, ExecutionContext}; + use sp_keyring::AccountKeyring; + use sp_runtime::{traits::SignedExtension, transaction_validity::InvalidTransaction}; use sp_state_machine::ExecutionStrategy; use substrate_test_runtime_client::{ prelude::*, runtime::TestAPI, DefaultTestClientBuilderExt, TestClientBuilder, @@ -1374,7 +1009,7 @@ mod tests { #[test] fn heap_pages_is_respected() { - // This tests that the on-chain HEAP_PAGES parameter is respected. + // This tests that the on-chain `HEAP_PAGES` parameter is respected. // Create a client devoting only 8 pages of wasm memory. This gives us ~512k of heap memory. let mut client = TestClientBuilder::new() @@ -1421,7 +1056,6 @@ mod tests { } fn witness_backend() -> (sp_trie::MemoryDB, crate::Hash) { - use sp_trie::TrieMut; let mut root = crate::Hash::default(); let mut mdb = sp_trie::MemoryDB::::default(); { @@ -1446,4 +1080,127 @@ mod tests { runtime_api.test_witness(best_hash, proof, root).unwrap(); } + + pub fn new_test_ext() -> sp_io::TestExternalities { + genesismap::GenesisStorageBuilder::new( + vec![AccountKeyring::One.public().into(), AccountKeyring::Two.public().into()], + vec![AccountKeyring::One.into(), AccountKeyring::Two.into()], + 1000 * currency::DOLLARS, + ) + .build_storage() + .into() + } + + #[test] + fn validate_storage_keys() { + assert_eq!( + genesismap::GenesisStorageBuilder::default() + .build_storage() + .top + .keys() + .cloned() + .map(storage_key_generator::hex) + .collect::>(), + storage_key_generator::get_expected_storage_hashed_keys() + ); + } + + #[test] + fn validate_unsigned_works() { + sp_tracing::try_init_simple(); + new_test_ext().execute_with(|| { + assert_eq!( + ::validate_unsigned( + TransactionSource::External, + &substrate_test_pallet::Call::bench_call { transfer: Default::default() }, + ), + InvalidTransaction::Call.into(), + ); + + assert_eq!( + ::validate_unsigned( + TransactionSource::External, + &substrate_test_pallet::Call::include_data { data: vec![] }, + ), + InvalidTransaction::Call.into(), + ); + + assert_eq!( + ::validate_unsigned( + TransactionSource::External, + &substrate_test_pallet::Call::fill_block { ratio: Perbill::from_percent(50) }, + ), + InvalidTransaction::Call.into(), + ); + + assert_eq!( + ::validate_unsigned( + TransactionSource::External, + &substrate_test_pallet::Call::deposit_log_digest_item { + log: DigestItem::Other(vec![]) + }, + ), + Ok(Default::default()), + ); + + assert_eq!( + ::validate_unsigned( + TransactionSource::External, + &substrate_test_pallet::Call::storage_change { key: vec![], value: None }, + ), + Ok(Default::default()), + ); + + assert_eq!( + ::validate_unsigned( + TransactionSource::External, + &substrate_test_pallet::Call::read { count: 0 }, + ), + Ok(Default::default()), + ); + + assert_eq!( + ::validate_unsigned( + TransactionSource::External, + &substrate_test_pallet::Call::read_and_panic { count: 0 }, + ), + Ok(Default::default()), + ); + }); + } + + #[test] + fn check_substrate_check_signed_extension_works() { + sp_tracing::try_init_simple(); + new_test_ext().execute_with(|| { + let x = sp_keyring::AccountKeyring::Alice.into(); + let info = DispatchInfo::default(); + let len = 0_usize; + assert_eq!( + CheckSubstrateCall {} + .validate( + &x, + &ExtrinsicBuilder::new_call_with_priority(16).build().function, + &info, + len + ) + .unwrap() + .priority, + 16 + ); + + assert_eq!( + CheckSubstrateCall {} + .validate( + &x, + &ExtrinsicBuilder::new_call_do_not_propagate().build().function, + &info, + len + ) + .unwrap() + .propagate, + false + ); + }) + } } diff --git a/test-utils/runtime/src/substrate_test_pallet.rs b/test-utils/runtime/src/substrate_test_pallet.rs new file mode 100644 index 000000000..98ebd550b --- /dev/null +++ b/test-utils/runtime/src/substrate_test_pallet.rs @@ -0,0 +1,244 @@ +// This file is part of Substrate. + +// Copyright (C) Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +//! # substrate-test pallet +//! +//! Provides functionality used in unit-tests of numerous modules across substrate that require +//! functioning runtime. Some calls are allowed to be submitted as unsigned extrinsics, however most +//! of them requires signing. Refer to `pallet::Call` for further details. + +use crate::AuthorityId; +use frame_support::{pallet_prelude::*, storage}; +use sp_runtime::transaction_validity::{ + InvalidTransaction, TransactionSource, TransactionValidity, ValidTransaction, +}; +use sp_std::prelude::*; + +pub use self::pallet::*; + +const LOG_TARGET: &str = "substrate_test_pallet"; + +#[frame_support::pallet(dev_mode)] +pub mod pallet { + use super::*; + use crate::TransferData; + use frame_system::pallet_prelude::*; + use sp_core::storage::well_known_keys; + use sp_runtime::{transaction_validity::TransactionPriority, Perbill}; + + #[pallet::pallet] + #[pallet::without_storage_info] + pub struct Pallet(PhantomData); + + #[pallet::config] + pub trait Config: frame_system::Config {} + + #[pallet::storage] + #[pallet::getter(fn authorities)] + pub type Authorities = StorageValue<_, Vec, ValueQuery>; + + #[pallet::genesis_config] + #[cfg_attr(feature = "std", derive(Default))] + pub struct GenesisConfig { + pub authorities: Vec, + } + + #[pallet::genesis_build] + impl GenesisBuild for GenesisConfig { + fn build(&self) { + >::put(self.authorities.clone()); + } + } + + #[pallet::call] + impl Pallet { + /// Legacy call used in transaction pool benchmarks. + #[pallet::call_index(0)] + #[pallet::weight(100)] + pub fn bench_call(_origin: OriginFor, _transfer: TransferData) -> DispatchResult { + Ok(()) + } + + /// Implicitly fill a block body with some data. + #[pallet::call_index(1)] + #[pallet::weight(100)] + pub fn include_data(origin: OriginFor, _data: Vec) -> DispatchResult { + frame_system::ensure_signed(origin)?; + Ok(()) + } + + /// Put/delete some data from storage. Intended to use as an unsigned extrinsic. + #[pallet::call_index(2)] + #[pallet::weight(100)] + pub fn storage_change( + _origin: OriginFor, + key: Vec, + value: Option>, + ) -> DispatchResult { + match value { + Some(value) => storage::unhashed::put_raw(&key, &value), + None => storage::unhashed::kill(&key), + } + Ok(()) + } + + /// Write a key value pair to the offchain database. + #[pallet::call_index(3)] + #[pallet::weight(100)] + pub fn offchain_index_set( + origin: OriginFor, + key: Vec, + value: Vec, + ) -> DispatchResult { + frame_system::ensure_signed(origin)?; + sp_io::offchain_index::set(&key, &value); + Ok(()) + } + + /// Remove a key and an associated value from the offchain database. + #[pallet::call_index(4)] + #[pallet::weight(100)] + pub fn offchain_index_clear(origin: OriginFor, key: Vec) -> DispatchResult { + frame_system::ensure_signed(origin)?; + sp_io::offchain_index::clear(&key); + Ok(()) + } + + /// Create an index for this call. + #[pallet::call_index(5)] + #[pallet::weight(100)] + pub fn indexed_call(origin: OriginFor, data: Vec) -> DispatchResult { + frame_system::ensure_signed(origin)?; + let content_hash = sp_io::hashing::blake2_256(&data); + let extrinsic_index: u32 = + storage::unhashed::get(well_known_keys::EXTRINSIC_INDEX).unwrap(); + sp_io::transaction_index::index(extrinsic_index, data.len() as u32, content_hash); + Ok(()) + } + + /// Deposit given digest items into the system storage. They will be included in a header + /// during finalization. + #[pallet::call_index(6)] + #[pallet::weight(100)] + pub fn deposit_log_digest_item( + _origin: OriginFor, + log: sp_runtime::generic::DigestItem, + ) -> DispatchResult { + >::deposit_log(log); + Ok(()) + } + + /// This call is validated as `ValidTransaction` with given priority. + #[pallet::call_index(7)] + #[pallet::weight(100)] + pub fn call_with_priority( + _origin: OriginFor, + _priority: TransactionPriority, + ) -> DispatchResult { + Ok(()) + } + + /// This call is validated as non-propagable `ValidTransaction`. + #[pallet::call_index(8)] + #[pallet::weight(100)] + pub fn call_do_not_propagate(_origin: OriginFor) -> DispatchResult { + Ok(()) + } + + /// Fill the block weight up to the given ratio. + #[pallet::call_index(9)] + #[pallet::weight(*_ratio * T::BlockWeights::get().max_block)] + pub fn fill_block(origin: OriginFor, _ratio: Perbill) -> DispatchResult { + ensure_signed(origin)?; + Ok(()) + } + + /// Read X times from the state some data. + /// + /// Panics if it can not read `X` times. + #[pallet::call_index(10)] + #[pallet::weight(100)] + pub fn read(_origin: OriginFor, count: u32) -> DispatchResult { + Self::execute_read(count, false) + } + + /// Read X times from the state some data and then panic! + /// + /// Returns `Ok` if it didn't read anything. + #[pallet::call_index(11)] + #[pallet::weight(100)] + pub fn read_and_panic(_origin: OriginFor, count: u32) -> DispatchResult { + Self::execute_read(count, true) + } + } + + impl Pallet { + fn execute_read(read: u32, panic_at_end: bool) -> DispatchResult { + let mut next_key = vec![]; + for _ in 0..(read as usize) { + if let Some(next) = sp_io::storage::next_key(&next_key) { + // Read the value + sp_io::storage::get(&next); + + next_key = next; + } else { + if panic_at_end { + return Ok(()) + } else { + panic!("Could not read {read} times from the state"); + } + } + } + + if panic_at_end { + panic!("BYE") + } else { + Ok(()) + } + } + } + + #[pallet::validate_unsigned] + impl ValidateUnsigned for Pallet { + type Call = Call; + + fn validate_unsigned(_source: TransactionSource, call: &Self::Call) -> TransactionValidity { + log::trace!(target: LOG_TARGET, "validate_unsigned {call:?}"); + match call { + // Some tests do not need to be complicated with signer and nonce, some need + // reproducible block hash (call signature can't be there). + // Offchain testing requires storage_change. + Call::deposit_log_digest_item { .. } | + Call::storage_change { .. } | + Call::read { .. } | + Call::read_and_panic { .. } => Ok(Default::default()), + _ => Err(TransactionValidityError::Invalid(InvalidTransaction::Call)), + } + } + } +} + +pub fn validate_runtime_call(call: &pallet::Call) -> TransactionValidity { + log::trace!(target: LOG_TARGET, "validate_runtime_call {call:?}"); + match call { + Call::call_do_not_propagate {} => + Ok(ValidTransaction { propagate: false, ..Default::default() }), + Call::call_with_priority { priority } => + Ok(ValidTransaction { priority: *priority, ..Default::default() }), + _ => Ok(Default::default()), + } +} diff --git a/test-utils/runtime/src/system.rs b/test-utils/runtime/src/system.rs deleted file mode 100644 index caabad336..000000000 --- a/test-utils/runtime/src/system.rs +++ /dev/null @@ -1,587 +0,0 @@ -// This file is part of Substrate. - -// Copyright (C) Parity Technologies (UK) Ltd. -// SPDX-License-Identifier: Apache-2.0 - -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -//! System manager: Handles all of the top-level stuff; executing block/transaction, setting code -//! and depositing logs. - -use crate::{ - AccountId, AuthorityId, Block, BlockNumber, Digest, Extrinsic, Header, Runtime, Transfer, - H256 as Hash, -}; -use codec::{Decode, Encode, KeyedVec}; -use frame_support::storage; -use sp_core::storage::well_known_keys; -use sp_io::{hashing::blake2_256, storage::root as storage_root, trie}; -use sp_runtime::{ - generic, - traits::Header as _, - transaction_validity::{ - InvalidTransaction, TransactionValidity, TransactionValidityError, ValidTransaction, - }, - ApplyExtrinsicResult, -}; -use sp_std::prelude::*; - -const NONCE_OF: &[u8] = b"nonce:"; -const BALANCE_OF: &[u8] = b"balance:"; - -pub use self::pallet::*; - -#[frame_support::pallet] -mod pallet { - use super::*; - use frame_support::pallet_prelude::*; - - #[pallet::pallet] - #[pallet::without_storage_info] - pub struct Pallet(PhantomData); - - #[pallet::config] - pub trait Config: frame_system::Config {} - - #[pallet::storage] - pub type ExtrinsicData = StorageMap<_, Blake2_128Concat, u32, Vec, ValueQuery>; - - // The current block number being processed. Set by `execute_block`. - #[pallet::storage] - pub type Number = StorageValue<_, BlockNumber, OptionQuery>; - - #[pallet::storage] - pub type ParentHash = StorageValue<_, Hash, ValueQuery>; - - #[pallet::storage] - pub type NewAuthorities = StorageValue<_, Vec, OptionQuery>; - - #[pallet::storage] - pub type StorageDigest = StorageValue<_, Digest, OptionQuery>; - - #[pallet::storage] - pub type Authorities = StorageValue<_, Vec, ValueQuery>; - - #[pallet::genesis_config] - #[cfg_attr(feature = "std", derive(Default))] - pub struct GenesisConfig { - pub authorities: Vec, - } - - #[pallet::genesis_build] - impl GenesisBuild for GenesisConfig { - fn build(&self) { - >::put(self.authorities.clone()); - } - } -} - -pub fn balance_of_key(who: AccountId) -> Vec { - who.to_keyed_vec(BALANCE_OF) -} - -pub fn balance_of(who: AccountId) -> u64 { - storage::hashed::get_or(&blake2_256, &balance_of_key(who), 0) -} - -pub fn nonce_of(who: AccountId) -> u64 { - storage::hashed::get_or(&blake2_256, &who.to_keyed_vec(NONCE_OF), 0) -} - -pub fn initialize_block(header: &Header) { - // populate environment. - >::put(&header.number); - >::put(&header.parent_hash); - >::put(header.digest()); - storage::unhashed::put(well_known_keys::EXTRINSIC_INDEX, &0u32); - - // try to read something that depends on current header digest - // so that it'll be included in execution proof - if let Some(generic::DigestItem::Other(v)) = header.digest().logs().iter().next() { - let _: Option = storage::unhashed::get(v); - } -} - -pub fn authorities() -> Vec { - >::get() -} - -pub fn get_block_number() -> Option { - >::get() -} - -pub fn take_block_number() -> Option { - >::take() -} - -#[derive(Copy, Clone)] -enum Mode { - Verify, - Overwrite, -} - -/// Actually execute all transitioning for `block`. -pub fn polish_block(block: &mut Block) { - execute_block_with_state_root_handler(block, Mode::Overwrite); -} - -pub fn execute_block(mut block: Block) -> Header { - execute_block_with_state_root_handler(&mut block, Mode::Verify) -} - -fn execute_block_with_state_root_handler(block: &mut Block, mode: Mode) -> Header { - let header = &mut block.header; - - initialize_block(header); - - // execute transactions - block.extrinsics.iter().for_each(|e| { - let _ = execute_transaction(e.clone()).unwrap_or_else(|_| panic!("Invalid transaction")); - }); - - let new_header = finalize_block(); - - if let Mode::Overwrite = mode { - header.state_root = new_header.state_root; - } else { - info_expect_equal_hash(&new_header.state_root, &header.state_root); - assert_eq!( - new_header.state_root, header.state_root, - "Storage root must match that calculated.", - ); - } - - if let Mode::Overwrite = mode { - header.extrinsics_root = new_header.extrinsics_root; - } else { - info_expect_equal_hash(&new_header.extrinsics_root, &header.extrinsics_root); - assert_eq!( - new_header.extrinsics_root, header.extrinsics_root, - "Transaction trie root must be valid.", - ); - } - - new_header -} - -/// The block executor. -pub struct BlockExecutor; - -impl frame_support::traits::ExecuteBlock for BlockExecutor { - fn execute_block(block: Block) { - execute_block(block); - } -} - -/// Execute a transaction outside of the block execution function. -/// This doesn't attempt to validate anything regarding the block. -pub fn validate_transaction(utx: Extrinsic) -> TransactionValidity { - if check_signature(&utx).is_err() { - return InvalidTransaction::BadProof.into() - } - - let tx = utx.transfer(); - let nonce_key = tx.from.to_keyed_vec(NONCE_OF); - let expected_nonce: u64 = storage::hashed::get_or(&blake2_256, &nonce_key, 0); - if tx.nonce < expected_nonce { - return InvalidTransaction::Stale.into() - } - if tx.nonce > expected_nonce + 64 { - return InvalidTransaction::Future.into() - } - - let encode = |from: &AccountId, nonce: u64| (from, nonce).encode(); - let requires = if tx.nonce != expected_nonce && tx.nonce > 0 { - vec![encode(&tx.from, tx.nonce - 1)] - } else { - vec![] - }; - - let provides = vec![encode(&tx.from, tx.nonce)]; - - Ok(ValidTransaction { priority: tx.amount, requires, provides, longevity: 64, propagate: true }) -} - -/// Execute a transaction outside of the block execution function. -/// This doesn't attempt to validate anything regarding the block. -pub fn execute_transaction(utx: Extrinsic) -> ApplyExtrinsicResult { - let extrinsic_index: u32 = - storage::unhashed::get(well_known_keys::EXTRINSIC_INDEX).unwrap_or_default(); - let result = execute_transaction_backend(&utx, extrinsic_index); - >::insert(extrinsic_index, utx.encode()); - storage::unhashed::put(well_known_keys::EXTRINSIC_INDEX, &(extrinsic_index + 1)); - result -} - -/// Finalize the block. -pub fn finalize_block() -> Header { - use sp_core::storage::StateVersion; - let extrinsic_index: u32 = storage::unhashed::take(well_known_keys::EXTRINSIC_INDEX).unwrap(); - let txs: Vec<_> = (0..extrinsic_index).map(>::take).collect(); - let extrinsics_root = trie::blake2_256_ordered_root(txs, StateVersion::V0); - let number = >::take().expect("Number is set by `initialize_block`"); - let parent_hash = >::take(); - let mut digest = - >::take().expect("StorageDigest is set by `initialize_block`"); - - let o_new_authorities = >::take(); - - // This MUST come after all changes to storage are done. Otherwise we will fail the - // “Storage root does not match that calculated” assertion. - let storage_root = Hash::decode(&mut &storage_root(StateVersion::V1)[..]) - .expect("`storage_root` is a valid hash"); - - if let Some(new_authorities) = o_new_authorities { - digest.push(generic::DigestItem::Consensus(*b"aura", new_authorities.encode())); - digest.push(generic::DigestItem::Consensus(*b"babe", new_authorities.encode())); - } - - Header { number, extrinsics_root, state_root: storage_root, parent_hash, digest } -} - -#[inline(always)] -fn check_signature(utx: &Extrinsic) -> Result<(), TransactionValidityError> { - use sp_runtime::traits::BlindCheckable; - utx.clone().check().map_err(|_| InvalidTransaction::BadProof.into()).map(|_| ()) -} - -fn execute_transaction_backend(utx: &Extrinsic, extrinsic_index: u32) -> ApplyExtrinsicResult { - check_signature(utx)?; - match utx { - Extrinsic::Transfer { exhaust_resources_when_not_first: true, .. } - if extrinsic_index != 0 => - Err(InvalidTransaction::ExhaustsResources.into()), - Extrinsic::Transfer { ref transfer, .. } => execute_transfer_backend(transfer), - Extrinsic::AuthoritiesChange(ref new_auth) => execute_new_authorities_backend(new_auth), - Extrinsic::IncludeData(_) => Ok(Ok(())), - Extrinsic::StorageChange(key, value) => - execute_storage_change(key, value.as_ref().map(|v| &**v)), - Extrinsic::OffchainIndexSet(key, value) => { - sp_io::offchain_index::set(key, value); - Ok(Ok(())) - }, - Extrinsic::OffchainIndexClear(key) => { - sp_io::offchain_index::clear(key); - Ok(Ok(())) - }, - Extrinsic::Store(data) => execute_store(data.clone()), - Extrinsic::ReadAndPanic(i) => execute_read(*i, true), - Extrinsic::Read(i) => execute_read(*i, false), - } -} - -fn execute_read(read: u32, panic_at_end: bool) -> ApplyExtrinsicResult { - let mut next_key = vec![]; - for _ in 0..(read as usize) { - if let Some(next) = sp_io::storage::next_key(&next_key) { - // Read the value - sp_io::storage::get(&next); - - next_key = next; - } else { - if panic_at_end { - return Ok(Ok(())) - } else { - panic!("Could not read {read} times from the state"); - } - } - } - - if panic_at_end { - panic!("BYE") - } else { - Ok(Ok(())) - } -} - -fn execute_transfer_backend(tx: &Transfer) -> ApplyExtrinsicResult { - // check nonce - let nonce_key = tx.from.to_keyed_vec(NONCE_OF); - let expected_nonce: u64 = storage::hashed::get_or(&blake2_256, &nonce_key, 0); - if tx.nonce != expected_nonce { - return Err(InvalidTransaction::Stale.into()) - } - - // increment nonce in storage - storage::hashed::put(&blake2_256, &nonce_key, &(expected_nonce + 1)); - - // check sender balance - let from_balance_key = tx.from.to_keyed_vec(BALANCE_OF); - let from_balance: u64 = storage::hashed::get_or(&blake2_256, &from_balance_key, 0); - - // enact transfer - if tx.amount > from_balance { - return Err(InvalidTransaction::Payment.into()) - } - let to_balance_key = tx.to.to_keyed_vec(BALANCE_OF); - let to_balance: u64 = storage::hashed::get_or(&blake2_256, &to_balance_key, 0); - storage::hashed::put(&blake2_256, &from_balance_key, &(from_balance - tx.amount)); - storage::hashed::put(&blake2_256, &to_balance_key, &(to_balance + tx.amount)); - Ok(Ok(())) -} - -fn execute_store(data: Vec) -> ApplyExtrinsicResult { - let content_hash = sp_io::hashing::blake2_256(&data); - let extrinsic_index: u32 = storage::unhashed::get(well_known_keys::EXTRINSIC_INDEX).unwrap(); - sp_io::transaction_index::index(extrinsic_index, data.len() as u32, content_hash); - Ok(Ok(())) -} - -fn execute_new_authorities_backend(new_authorities: &[AuthorityId]) -> ApplyExtrinsicResult { - >::put(new_authorities.to_vec()); - Ok(Ok(())) -} - -fn execute_storage_change(key: &[u8], value: Option<&[u8]>) -> ApplyExtrinsicResult { - match value { - Some(value) => storage::unhashed::put_raw(key, value), - None => storage::unhashed::kill(key), - } - Ok(Ok(())) -} - -#[cfg(feature = "std")] -fn info_expect_equal_hash(given: &Hash, expected: &Hash) { - use sp_core::hexdisplay::HexDisplay; - if given != expected { - println!( - "Hash: given={}, expected={}", - HexDisplay::from(given.as_fixed_bytes()), - HexDisplay::from(expected.as_fixed_bytes()), - ); - } -} - -#[cfg(not(feature = "std"))] -fn info_expect_equal_hash(given: &Hash, expected: &Hash) { - if given != expected { - sp_runtime::print("Hash not equal"); - sp_runtime::print(given.as_bytes()); - sp_runtime::print(expected.as_bytes()); - } -} - -#[cfg(test)] -mod tests { - use super::*; - - use crate::{wasm_binary_unwrap, Header, Transfer}; - use sc_executor::{NativeElseWasmExecutor, WasmExecutor}; - use sp_core::{ - map, - traits::{CallContext, CodeExecutor, RuntimeCode}, - }; - use sp_io::{hashing::twox_128, TestExternalities}; - use substrate_test_runtime_client::{AccountKeyring, Sr25519Keyring}; - - // Declare an instance of the native executor dispatch for the test runtime. - pub struct NativeDispatch; - - impl sc_executor::NativeExecutionDispatch for NativeDispatch { - type ExtendHostFunctions = (); - - fn dispatch(method: &str, data: &[u8]) -> Option> { - crate::api::dispatch(method, data) - } - - fn native_version() -> sc_executor::NativeVersion { - crate::native_version() - } - } - - fn executor() -> NativeElseWasmExecutor { - NativeElseWasmExecutor::new_with_wasm_executor(WasmExecutor::builder().build()) - } - - fn new_test_ext() -> TestExternalities { - let authorities = vec![ - Sr25519Keyring::Alice.to_raw_public(), - Sr25519Keyring::Bob.to_raw_public(), - Sr25519Keyring::Charlie.to_raw_public(), - ]; - - TestExternalities::new_with_code( - wasm_binary_unwrap(), - sp_core::storage::Storage { - top: map![ - twox_128(b"latest").to_vec() => vec![69u8; 32], - twox_128(b"sys:auth").to_vec() => authorities.encode(), - blake2_256(&AccountKeyring::Alice.to_raw_public().to_keyed_vec(b"balance:")).to_vec() => { - vec![111u8, 0, 0, 0, 0, 0, 0, 0] - }, - ], - children_default: map![], - }, - ) - } - - fn block_import_works(block_executor: F) - where - F: Fn(Block, &mut TestExternalities), - { - let h = Header { - parent_hash: [69u8; 32].into(), - number: 1, - state_root: Default::default(), - extrinsics_root: Default::default(), - digest: Default::default(), - }; - let mut b = Block { header: h, extrinsics: vec![] }; - - new_test_ext().execute_with(|| polish_block(&mut b)); - - block_executor(b, &mut new_test_ext()); - } - - #[test] - fn block_import_works_native() { - block_import_works(|b, ext| { - ext.execute_with(|| { - execute_block(b); - }) - }); - } - - #[test] - fn block_import_works_wasm() { - block_import_works(|b, ext| { - let mut ext = ext.ext(); - let runtime_code = RuntimeCode { - code_fetcher: &sp_core::traits::WrappedRuntimeCode(wasm_binary_unwrap().into()), - hash: Vec::new(), - heap_pages: None, - }; - - executor() - .call( - &mut ext, - &runtime_code, - "Core_execute_block", - &b.encode(), - false, - CallContext::Offchain, - ) - .0 - .unwrap(); - }) - } - - fn block_import_with_transaction_works(block_executor: F) - where - F: Fn(Block, &mut TestExternalities), - { - let mut b1 = Block { - header: Header { - parent_hash: [69u8; 32].into(), - number: 1, - state_root: Default::default(), - extrinsics_root: Default::default(), - digest: Default::default(), - }, - extrinsics: vec![Transfer { - from: AccountKeyring::Alice.into(), - to: AccountKeyring::Bob.into(), - amount: 69, - nonce: 0, - } - .into_signed_tx()], - }; - - let mut dummy_ext = new_test_ext(); - dummy_ext.execute_with(|| polish_block(&mut b1)); - - let mut b2 = Block { - header: Header { - parent_hash: b1.header.hash(), - number: 2, - state_root: Default::default(), - extrinsics_root: Default::default(), - digest: Default::default(), - }, - extrinsics: vec![ - Transfer { - from: AccountKeyring::Bob.into(), - to: AccountKeyring::Alice.into(), - amount: 27, - nonce: 0, - } - .into_signed_tx(), - Transfer { - from: AccountKeyring::Alice.into(), - to: AccountKeyring::Charlie.into(), - amount: 69, - nonce: 1, - } - .into_signed_tx(), - ], - }; - - dummy_ext.execute_with(|| polish_block(&mut b2)); - drop(dummy_ext); - - let mut t = new_test_ext(); - - t.execute_with(|| { - assert_eq!(balance_of(AccountKeyring::Alice.into()), 111); - assert_eq!(balance_of(AccountKeyring::Bob.into()), 0); - }); - - block_executor(b1, &mut t); - - t.execute_with(|| { - assert_eq!(balance_of(AccountKeyring::Alice.into()), 42); - assert_eq!(balance_of(AccountKeyring::Bob.into()), 69); - }); - - block_executor(b2, &mut t); - - t.execute_with(|| { - assert_eq!(balance_of(AccountKeyring::Alice.into()), 0); - assert_eq!(balance_of(AccountKeyring::Bob.into()), 42); - assert_eq!(balance_of(AccountKeyring::Charlie.into()), 69); - }); - } - - #[test] - fn block_import_with_transaction_works_native() { - block_import_with_transaction_works(|b, ext| { - ext.execute_with(|| { - execute_block(b); - }) - }); - } - - #[test] - fn block_import_with_transaction_works_wasm() { - block_import_with_transaction_works(|b, ext| { - let mut ext = ext.ext(); - let runtime_code = RuntimeCode { - code_fetcher: &sp_core::traits::WrappedRuntimeCode(wasm_binary_unwrap().into()), - hash: Vec::new(), - heap_pages: None, - }; - - executor() - .call( - &mut ext, - &runtime_code, - "Core_execute_block", - &b.encode(), - false, - CallContext::Offchain, - ) - .0 - .unwrap(); - }) - } -} diff --git a/test-utils/runtime/transaction-pool/src/lib.rs b/test-utils/runtime/transaction-pool/src/lib.rs index 8a39b8295..8e2844966 100644 --- a/test-utils/runtime/transaction-pool/src/lib.rs +++ b/test-utils/runtime/transaction-pool/src/lib.rs @@ -35,7 +35,10 @@ use sp_runtime::{ }; use std::collections::{BTreeMap, HashMap, HashSet}; use substrate_test_runtime_client::{ - runtime::{AccountId, Block, BlockNumber, Extrinsic, Hash, Header, Index, Transfer}, + runtime::{ + AccountId, Block, BlockNumber, Extrinsic, ExtrinsicBuilder, Hash, Header, Index, Transfer, + TransferData, + }, AccountKeyring::{self, *}, }; @@ -276,7 +279,7 @@ impl sc_transaction_pool::ChainApi for TestApi { Err(e) => return ready(Err(e)), } - let (requires, provides) = if let Some(transfer) = uxt.try_transfer() { + let (requires, provides) = if let Ok(transfer) = TransferData::try_from(&uxt) { let chain_nonce = self.chain.read().nonces.get(&transfer.from).cloned().unwrap_or(0); let requires = if chain_nonce == transfer.nonce { vec![] } else { vec![vec![chain_nonce as u8]] }; @@ -377,6 +380,5 @@ impl sp_blockchain::HeaderMetadata for TestApi { pub fn uxt(who: AccountKeyring, nonce: Index) -> Extrinsic { let dummy = codec::Decode::decode(&mut TrailingZeroInput::zeroes()).unwrap(); let transfer = Transfer { from: who.into(), to: dummy, nonce, amount: 1 }; - let signature = transfer.using_encoded(|e| who.sign(e)); - Extrinsic::Transfer { transfer, signature, exhaust_resources_when_not_first: false } + ExtrinsicBuilder::new_transfer(transfer).build() } diff --git a/utils/frame/rpc/system/src/lib.rs b/utils/frame/rpc/system/src/lib.rs index 46d8472b2..26efa0297 100644 --- a/utils/frame/rpc/system/src/lib.rs +++ b/utils/frame/rpc/system/src/lib.rs @@ -243,7 +243,7 @@ mod tests { amount: 5, nonce, }; - t.into_signed_tx() + t.into_unchecked_extrinsic() }; // Populate the pool let ext0 = new_transaction(0); @@ -297,7 +297,7 @@ mod tests { amount: 5, nonce: 0, } - .into_signed_tx(); + .into_unchecked_extrinsic(); // when let bytes = accounts.dry_run(tx.encode().into(), None).await.expect("Call is successful"); @@ -325,13 +325,13 @@ mod tests { amount: 5, nonce: 100, } - .into_signed_tx(); + .into_unchecked_extrinsic(); // when let bytes = accounts.dry_run(tx.encode().into(), None).await.expect("Call is successful"); // then let apply_res: ApplyExtrinsicResult = Decode::decode(&mut bytes.as_ref()).unwrap(); - assert_eq!(apply_res, Err(TransactionValidityError::Invalid(InvalidTransaction::Stale))); + assert_eq!(apply_res, Err(TransactionValidityError::Invalid(InvalidTransaction::Future))); } } From 6be90cf90b3675268a8fdc0f1e468348169f699d Mon Sep 17 00:00:00 2001 From: Liam Aharon Date: Fri, 5 May 2023 02:21:40 +1000 Subject: [PATCH 456/558] fungible conformance tests: Inspect and Mutate (#13852) * typo * - create test files for each fungile trait - begin implementing tests for the Inspect trait * wrap inspect tests in a macro * first run of mutate tests * move test implementation out of ballances * make tests more generic * transfer tests * combine inspect and mutate tests * set balance failing tests * can_deposit tests * can_withdraw tests * test reducible_balance * remove balanced stub * revert set_balance return val fix * typo * macro and dust trap tests * disable test when it doesn't make sense * remove debug comment * reduce macro boilerplate * improved var naming * improve variable naming * remove redundant comment * remove placeholder tests * remove placeholder tests * simplify macro * Update frame/balances/src/tests/fungible_conformance_tests.rs Co-authored-by: Kian Paimani <5588131+kianenigma@users.noreply.github.com> * use Balance from T * fix copyright * add test doc comments * improve test naming * clippy * fix rustdoc errors * fix rustdoc * improve macro * improve variable naming * remove redundant comment * use path --------- Co-authored-by: Kian Paimani <5588131+kianenigma@users.noreply.github.com> --- Cargo.lock | 1 + frame/balances/Cargo.toml | 1 + .../src/tests/fungible_conformance_tests.rs | 88 ++ frame/balances/src/tests/mod.rs | 1 + .../conformance_tests/inspect_mutate.rs | 975 ++++++++++++++++++ .../tokens/fungible/conformance_tests/mod.rs | 1 + .../support/src/traits/tokens/fungible/mod.rs | 1 + .../src/traits/tokens/fungible/regular.rs | 2 +- 8 files changed, 1069 insertions(+), 1 deletion(-) create mode 100644 frame/balances/src/tests/fungible_conformance_tests.rs create mode 100644 frame/support/src/traits/tokens/fungible/conformance_tests/inspect_mutate.rs create mode 100644 frame/support/src/traits/tokens/fungible/conformance_tests/mod.rs diff --git a/Cargo.lock b/Cargo.lock index 3f1fd0bd5..41e059366 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -5867,6 +5867,7 @@ dependencies = [ "log", "pallet-transaction-payment", "parity-scale-codec", + "paste", "scale-info", "sp-core", "sp-io", diff --git a/frame/balances/Cargo.toml b/frame/balances/Cargo.toml index b3457dd8d..53ea04fc1 100644 --- a/frame/balances/Cargo.toml +++ b/frame/balances/Cargo.toml @@ -26,6 +26,7 @@ sp-std = { version = "5.0.0", default-features = false, path = "../../primitives pallet-transaction-payment = { version = "4.0.0-dev", path = "../transaction-payment" } sp-core = { version = "7.0.0", path = "../../primitives/core" } sp-io = { version = "7.0.0", path = "../../primitives/io" } +paste = "1.0.12" [features] default = ["std"] diff --git a/frame/balances/src/tests/fungible_conformance_tests.rs b/frame/balances/src/tests/fungible_conformance_tests.rs new file mode 100644 index 000000000..6262aa04d --- /dev/null +++ b/frame/balances/src/tests/fungible_conformance_tests.rs @@ -0,0 +1,88 @@ +// This file is part of Substrate. + +// Copyright (C) Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +use super::*; +use frame_support::traits::fungible::{conformance_tests, Inspect, Mutate}; +use paste::paste; + +macro_rules! run_tests { + ($path:path, $ext_deposit:expr, $($name:ident),*) => { + $( + paste! { + #[test] + fn [< $name _existential_deposit_ $ext_deposit _dust_trap_on >]() { + let trap_account = ::AccountId::from(65174286u64); + let builder = ExtBuilder::default().existential_deposit($ext_deposit).dust_trap(trap_account); + builder.build_and_execute_with(|| { + Balances::set_balance(&trap_account, Balances::minimum_balance()); + $path::$name::< + Balances, + ::AccountId, + >(Some(trap_account)); + }); + } + + #[test] + fn [< $name _existential_deposit_ $ext_deposit _dust_trap_off >]() { + let builder = ExtBuilder::default().existential_deposit($ext_deposit); + builder.build_and_execute_with(|| { + $path::$name::< + Balances, + ::AccountId, + >(None); + }); + } + } + )* + }; + ($path:path, $ext_deposit:expr) => { + run_tests!( + $path, + $ext_deposit, + mint_into_success, + mint_into_overflow, + mint_into_below_minimum, + burn_from_exact_success, + burn_from_best_effort_success, + burn_from_exact_insufficient_funds, + restore_success, + restore_overflow, + restore_below_minimum, + shelve_success, + shelve_insufficient_funds, + transfer_success, + transfer_expendable_all, + transfer_expendable_dust, + transfer_protect_preserve, + set_balance_mint_success, + set_balance_burn_success, + can_deposit_success, + can_deposit_below_minimum, + can_deposit_overflow, + can_withdraw_success, + can_withdraw_reduced_to_zero, + can_withdraw_balance_low, + reducible_balance_expendable, + reducible_balance_protect_preserve + ); + }; +} + +run_tests!(conformance_tests::inspect_mutate, 1); +run_tests!(conformance_tests::inspect_mutate, 2); +run_tests!(conformance_tests::inspect_mutate, 5); +run_tests!(conformance_tests::inspect_mutate, 1000); diff --git a/frame/balances/src/tests/mod.rs b/frame/balances/src/tests/mod.rs index 9b451f36d..259051ed4 100644 --- a/frame/balances/src/tests/mod.rs +++ b/frame/balances/src/tests/mod.rs @@ -45,6 +45,7 @@ use sp_runtime::{ mod currency_tests; mod dispatchable_tests; +mod fungible_conformance_tests; mod fungible_tests; mod reentrancy_tests; diff --git a/frame/support/src/traits/tokens/fungible/conformance_tests/inspect_mutate.rs b/frame/support/src/traits/tokens/fungible/conformance_tests/inspect_mutate.rs new file mode 100644 index 000000000..732742cca --- /dev/null +++ b/frame/support/src/traits/tokens/fungible/conformance_tests/inspect_mutate.rs @@ -0,0 +1,975 @@ +// This file is part of Substrate. + +// Copyright (C) Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +use crate::traits::{ + fungible::{Inspect, Mutate}, + tokens::{ + DepositConsequence, Fortitude, Precision, Preservation, Provenance, WithdrawConsequence, + }, +}; +use core::fmt::Debug; +use sp_arithmetic::traits::AtLeast8BitUnsigned; +use sp_runtime::traits::{Bounded, Zero}; + +/// Test the `mint_into` function for successful token minting. +/// +/// This test checks the `mint_into` function in the `Mutate` trait implementation for type `T`. +/// It ensures that account balances and total issuance values are updated correctly after minting +/// tokens into two distinct accounts. +/// +/// # Type Parameters +/// +/// ```text +/// - `T`: Implements `Mutate`. +/// - `AccountId`: Account identifier implementing `AtLeast8BitUnsigned`. +/// ``` +pub fn mint_into_success(_dust_trap: Option) +where + T: Mutate, + >::Balance: AtLeast8BitUnsigned + Debug, + AccountId: AtLeast8BitUnsigned, +{ + let initial_total_issuance = T::total_issuance(); + let initial_active_issuance = T::active_issuance(); + let account_0 = AccountId::from(0); + let account_1 = AccountId::from(1); + + // Test: Mint an amount into each account + let amount_0 = T::minimum_balance(); + let amount_1 = T::minimum_balance() + 5.into(); + T::mint_into(&account_0, amount_0).unwrap(); + T::mint_into(&account_1, amount_1).unwrap(); + + // Verify: Account balances are updated correctly + assert_eq!(T::total_balance(&account_0), amount_0); + assert_eq!(T::total_balance(&account_1), amount_1); + assert_eq!(T::balance(&account_0), amount_0); + assert_eq!(T::balance(&account_1), amount_1); + + // Verify: Total issuance is updated correctly + assert_eq!(T::total_issuance(), initial_total_issuance + amount_0 + amount_1); + assert_eq!(T::active_issuance(), initial_active_issuance + amount_0 + amount_1); +} + +/// Test the `mint_into` function for overflow prevention. +/// +/// This test ensures that minting tokens beyond the maximum balance value for an account +/// returns an error and does not change the account balance or total issuance values. +/// +/// # Type Parameters +/// +/// ```text +/// - `T`: Implements `Mutate`. +/// - `AccountId`: Account identifier implementing `AtLeast8BitUnsigned`. +/// ``` +pub fn mint_into_overflow(_dust_trap: Option) +where + T: Mutate, + >::Balance: AtLeast8BitUnsigned + Debug, + AccountId: AtLeast8BitUnsigned, +{ + let initial_total_issuance = T::total_issuance(); + let initial_active_issuance = T::active_issuance(); + let account = AccountId::from(10); + let amount = T::Balance::max_value() - 5.into() - initial_total_issuance; + + // Mint just below the maximum balance + T::mint_into(&account, amount).unwrap(); + + // Verify: Minting beyond the maximum balance value returns an Err + T::mint_into(&account, 10.into()).unwrap_err(); + + // Verify: The balance did not change + assert_eq!(T::total_balance(&account), amount); + assert_eq!(T::balance(&account), amount); + + // Verify: The total issuance did not change + assert_eq!(T::total_issuance(), initial_total_issuance + amount); + assert_eq!(T::active_issuance(), initial_active_issuance + amount); +} + +/// Test the `mint_into` function for handling balances below the minimum value. +/// +/// This test verifies that minting tokens below the minimum balance for an account +/// returns an error and has no impact on the account balance or total issuance values. +/// +/// # Type Parameters +/// +/// ```text +/// - `T`: Implements `Mutate`. +/// - `AccountId`: Account identifier implementing `AtLeast8BitUnsigned`. +/// ``` +pub fn mint_into_below_minimum(_dust_trap: Option) +where + T: Mutate, + >::Balance: AtLeast8BitUnsigned + Debug, + AccountId: AtLeast8BitUnsigned, +{ + // Skip if there is no minimum balance + if T::minimum_balance() == T::Balance::zero() { + return + } + + let initial_total_issuance = T::total_issuance(); + let initial_active_issuance = T::active_issuance(); + let account = AccountId::from(10); + let amount = T::minimum_balance() - 1.into(); + + // Verify: Minting below the minimum balance returns Err + T::mint_into(&account, amount).unwrap_err(); + + // Verify: noop + assert_eq!(T::total_balance(&account), T::Balance::zero()); + assert_eq!(T::balance(&account), T::Balance::zero()); + assert_eq!(T::total_issuance(), initial_total_issuance); + assert_eq!(T::active_issuance(), initial_active_issuance); +} + +/// Test the `burn_from` function for successfully burning an exact amount of tokens. +/// +/// This test checks that the `burn_from` function with `Precision::Exact` correctly +/// reduces the account balance and total issuance values by the burned amount. +/// +/// # Type Parameters +/// +/// ```text +/// - `T`: Implements `Mutate` for `AccountId`. +/// - `AccountId`: Account identifier implementing `AtLeast8BitUnsigned`. +/// ``` +pub fn burn_from_exact_success(_dust_trap: Option) +where + T: Mutate, + >::Balance: AtLeast8BitUnsigned + Debug, + AccountId: AtLeast8BitUnsigned, +{ + let initial_total_issuance = T::total_issuance(); + let initial_active_issuance = T::active_issuance(); + + // Setup account + let account = AccountId::from(5); + let initial_balance = T::minimum_balance() + 10.into(); + T::mint_into(&account, initial_balance).unwrap(); + + // Test: Burn an exact amount from the account + let amount_to_burn = T::Balance::from(5); + let precision = Precision::Exact; + let force = Fortitude::Polite; + T::burn_from(&account, amount_to_burn, precision, force).unwrap(); + + // Verify: The balance and total issuance should be reduced by the burned amount + assert_eq!(T::balance(&account), initial_balance - amount_to_burn); + assert_eq!(T::total_balance(&account), initial_balance - amount_to_burn); + assert_eq!(T::total_issuance(), initial_total_issuance + initial_balance - amount_to_burn); + assert_eq!(T::active_issuance(), initial_active_issuance + initial_balance - amount_to_burn); +} + +/// Test the `burn_from` function for successfully burning tokens with a best-effort approach. +/// +/// This test verifies that the `burn_from` function with `Precision::BestEffort` correctly +/// reduces the account balance and total issuance values by the reducible balance when +/// attempting to burn an amount greater than the reducible balance. +/// +/// # Type Parameters +/// +/// ```text +/// - `T`: Implements `Mutate` for `AccountId`. +/// - `AccountId`: Account identifier implementing `AtLeast8BitUnsigned`. +/// ``` +pub fn burn_from_best_effort_success(_dust_trap: Option) +where + T: Mutate, + >::Balance: AtLeast8BitUnsigned + Debug, + AccountId: AtLeast8BitUnsigned, +{ + let initial_total_issuance = T::total_issuance(); + let initial_active_issuance = T::active_issuance(); + + // Setup account + let account = AccountId::from(5); + let initial_balance = T::minimum_balance() + 10.into(); + T::mint_into(&account, initial_balance).unwrap(); + + // Get reducible balance + let force = Fortitude::Polite; + let reducible_balance = T::reducible_balance(&account, Preservation::Expendable, force); + + // Test: Burn a best effort amount from the account that is greater than the reducible balance + let amount_to_burn = reducible_balance + 5.into(); + let precision = Precision::BestEffort; + assert!(amount_to_burn > reducible_balance); + assert!(amount_to_burn > T::balance(&account)); + T::burn_from(&account, amount_to_burn, precision, force).unwrap(); + + // Verify: The balance and total issuance should be reduced by the reducible_balance + assert_eq!(T::balance(&account), initial_balance - reducible_balance); + assert_eq!(T::total_balance(&account), initial_balance - reducible_balance); + assert_eq!(T::total_issuance(), initial_total_issuance + initial_balance - reducible_balance); + assert_eq!(T::active_issuance(), initial_active_issuance + initial_balance - reducible_balance); +} + +/// Test the `burn_from` function for handling insufficient funds with `Precision::Exact`. +/// +/// This test verifies that burning an amount greater than the account's balance with +/// `Precision::Exact` returns an error and does not change the account balance or total issuance +/// values. +/// +/// # Type Parameters +/// +/// ```text +/// - `T`: Implements `Mutate`. +/// - `AccountId`: Account identifier implementing `AtLeast8BitUnsigned`. +/// ``` +pub fn burn_from_exact_insufficient_funds(_dust_trap: Option) +where + T: Mutate, + >::Balance: AtLeast8BitUnsigned + Debug, + AccountId: AtLeast8BitUnsigned, +{ + // Set up the initial conditions and parameters for the test + let account = AccountId::from(5); + let initial_balance = T::minimum_balance() + 10.into(); + T::mint_into(&account, initial_balance).unwrap(); + let initial_total_issuance = T::total_issuance(); + let initial_active_issuance = T::active_issuance(); + + // Verify: Burn an amount greater than the account's balance with Exact precision returns Err + let amount_to_burn = initial_balance + 10.into(); + let precision = Precision::Exact; + let force = Fortitude::Polite; + T::burn_from(&account, amount_to_burn, precision, force).unwrap_err(); + + // Verify: The balance and total issuance should remain unchanged + assert_eq!(T::balance(&account), initial_balance); + assert_eq!(T::total_balance(&account), initial_balance); + assert_eq!(T::total_issuance(), initial_total_issuance); + assert_eq!(T::active_issuance(), initial_active_issuance); +} + +/// Test the `restore` function for successful restoration. +/// +/// This test verifies that restoring an amount into each account updates their balances and the +/// total issuance values correctly. +/// +/// # Type Parameters +/// +/// ```text +/// - `T`: Implements `Mutate`. +/// - `AccountId`: Account identifier implementing `AtLeast8BitUnsigned`. +/// ``` +pub fn restore_success(_dust_trap: Option) +where + T: Mutate, + >::Balance: AtLeast8BitUnsigned + Debug, + AccountId: AtLeast8BitUnsigned, +{ + let account_0 = AccountId::from(0); + let account_1 = AccountId::from(1); + + // Test: Restore an amount into each account + let amount_0 = T::minimum_balance(); + let amount_1 = T::minimum_balance() + 5.into(); + let initial_total_issuance = T::total_issuance(); + let initial_active_issuance = T::active_issuance(); + T::restore(&account_0, amount_0).unwrap(); + T::restore(&account_1, amount_1).unwrap(); + + // Verify: Account balances are updated correctly + assert_eq!(T::total_balance(&account_0), amount_0); + assert_eq!(T::total_balance(&account_1), amount_1); + assert_eq!(T::balance(&account_0), amount_0); + assert_eq!(T::balance(&account_1), amount_1); + + // Verify: Total issuance is updated correctly + assert_eq!(T::total_issuance(), initial_total_issuance + amount_0 + amount_1); + assert_eq!(T::active_issuance(), initial_active_issuance + amount_0 + amount_1); +} + +/// Test the `restore` function for handling balance overflow. +/// +/// This test verifies that restoring an amount beyond the maximum balance returns an error and +/// does not change the account balance or total issuance values. +/// +/// # Type Parameters +/// +/// ```text +/// - `T`: Implements `Mutate`. +/// - `AccountId`: Account identifier implementing `AtLeast8BitUnsigned`. +/// ``` +pub fn restore_overflow(_dust_trap: Option) +where + T: Mutate, + >::Balance: AtLeast8BitUnsigned + Debug, + AccountId: AtLeast8BitUnsigned, +{ + let initial_total_issuance = T::total_issuance(); + let initial_active_issuance = T::active_issuance(); + let account = AccountId::from(10); + let amount = T::Balance::max_value() - 5.into() - initial_total_issuance; + + // Restore just below the maximum balance + T::restore(&account, amount).unwrap(); + + // Verify: Restoring beyond the maximum balance returns an Err + T::restore(&account, 10.into()).unwrap_err(); + + // Verify: The balance and total issuance did not change + assert_eq!(T::total_balance(&account), amount); + assert_eq!(T::balance(&account), amount); + assert_eq!(T::total_issuance(), initial_total_issuance + amount); + assert_eq!(T::active_issuance(), initial_active_issuance + amount); +} + +/// Test the `restore` function for handling restoration below the minimum balance. +/// +/// This test verifies that restoring an amount below the minimum balance returns an error and +/// does not change the account balance or total issuance values. +/// +/// # Type Parameters +/// +/// ```text +/// - `T`: Implements `Mutate`. +/// - `AccountId`: Account identifier implementing `AtLeast8BitUnsigned`. +/// ``` +pub fn restore_below_minimum(_dust_trap: Option) +where + T: Mutate, + >::Balance: AtLeast8BitUnsigned + Debug, + AccountId: AtLeast8BitUnsigned, +{ + // Skip if there is no minimum balance + if T::minimum_balance() == T::Balance::zero() { + return + } + + let account = AccountId::from(10); + let amount = T::minimum_balance() - 1.into(); + let initial_total_issuance = T::total_issuance(); + let initial_active_issuance = T::active_issuance(); + + // Verify: Restoring below the minimum balance returns Err + T::restore(&account, amount).unwrap_err(); + + // Verify: noop + assert_eq!(T::total_balance(&account), T::Balance::zero()); + assert_eq!(T::balance(&account), T::Balance::zero()); + assert_eq!(T::total_issuance(), initial_total_issuance); + assert_eq!(T::active_issuance(), initial_active_issuance); +} + +/// Test the `shelve` function for successful shelving. +/// +/// This test verifies that shelving an amount from an account reduces the account balance and +/// total issuance values by the shelved amount. +/// +/// # Type Parameters +/// +/// ```text +/// - `T`: Implements `Mutate`. +/// - `AccountId`: Account identifier implementing `AtLeast8BitUnsigned`. +/// ``` +pub fn shelve_success(_dust_trap: Option) +where + T: Mutate, + >::Balance: AtLeast8BitUnsigned + Debug, + AccountId: AtLeast8BitUnsigned, +{ + let initial_total_issuance = T::total_issuance(); + let initial_active_issuance = T::active_issuance(); + + // Setup account + let account = AccountId::from(5); + let initial_balance = T::minimum_balance() + 10.into(); + + T::restore(&account, initial_balance).unwrap(); + + // Test: Shelve an amount from the account + let amount_to_shelve = T::Balance::from(5); + T::shelve(&account, amount_to_shelve).unwrap(); + + // Verify: The balance and total issuance should be reduced by the shelved amount + assert_eq!(T::balance(&account), initial_balance - amount_to_shelve); + assert_eq!(T::total_balance(&account), initial_balance - amount_to_shelve); + assert_eq!(T::total_issuance(), initial_total_issuance + initial_balance - amount_to_shelve); + assert_eq!(T::active_issuance(), initial_active_issuance + initial_balance - amount_to_shelve); +} + +/// Test the `shelve` function for handling insufficient funds. +/// +/// This test verifies that attempting to shelve an amount greater than the account's balance +/// returns an error and does not change the account balance or total issuance values. +/// +/// # Type Parameters +/// +/// ```text +/// - `T`: Implements `Mutate`. +/// - `AccountId`: Account identifier implementing `AtLeast8BitUnsigned`. +/// ``` +pub fn shelve_insufficient_funds(_dust_trap: Option) +where + T: Mutate, + >::Balance: AtLeast8BitUnsigned + Debug, + AccountId: AtLeast8BitUnsigned, +{ + let initial_total_issuance = T::total_issuance(); + let initial_active_issuance = T::active_issuance(); + + // Set up the initial conditions and parameters for the test + let account = AccountId::from(5); + let initial_balance = T::minimum_balance() + 10.into(); + T::restore(&account, initial_balance).unwrap(); + + // Verify: Shelving greater than the balance with Exact precision returns Err + let amount_to_shelve = initial_balance + 10.into(); + T::shelve(&account, amount_to_shelve).unwrap_err(); + + // Verify: The balance and total issuance should remain unchanged + assert_eq!(T::balance(&account), initial_balance); + assert_eq!(T::total_balance(&account), initial_balance); + assert_eq!(T::total_issuance(), initial_total_issuance + initial_balance); + assert_eq!(T::active_issuance(), initial_active_issuance + initial_balance); +} + +/// Test the `transfer` function for a successful transfer. +/// +/// This test verifies that transferring an amount between two accounts with +/// `Preservation::Expendable` updates the account balances and maintains the total issuance and +/// active issuance values. +/// +/// # Type Parameters +/// +/// ```text +/// - `T`: Implements `Mutate`. +/// - `AccountId`: Account identifier implementing `AtLeast8BitUnsigned`. +/// ``` +pub fn transfer_success(_dust_trap: Option) +where + T: Mutate, + >::Balance: AtLeast8BitUnsigned + Debug, + AccountId: AtLeast8BitUnsigned, +{ + let initial_total_issuance = T::total_issuance(); + let initial_active_issuance = T::active_issuance(); + let account_0 = AccountId::from(0); + let account_1 = AccountId::from(1); + let initial_balance = T::minimum_balance() + 10.into(); + T::set_balance(&account_0, initial_balance); + T::set_balance(&account_1, initial_balance); + + // Test: Transfer an amount from account_0 to account_1 + let transfer_amount = T::Balance::from(3); + T::transfer(&account_0, &account_1, transfer_amount, Preservation::Expendable).unwrap(); + + // Verify: Account balances are updated correctly + assert_eq!(T::total_balance(&account_0), initial_balance - transfer_amount); + assert_eq!(T::total_balance(&account_1), initial_balance + transfer_amount); + assert_eq!(T::balance(&account_0), initial_balance - transfer_amount); + assert_eq!(T::balance(&account_1), initial_balance + transfer_amount); + + // Verify: Total issuance doesn't change + assert_eq!(T::total_issuance(), initial_total_issuance + initial_balance * 2.into()); + assert_eq!(T::active_issuance(), initial_active_issuance + initial_balance * 2.into()); +} + +/// Test the `transfer` function with `Preservation::Expendable` for transferring the entire +/// balance. +/// +/// This test verifies that transferring the entire balance from one account to another with +/// `Preservation::Expendable` updates the account balances and maintains the total issuance and +/// active issuance values. +/// +/// # Type Parameters +/// +/// ```text +/// - `T`: Implements `Mutate`. +/// - `AccountId`: Account identifier implementing `AtLeast8BitUnsigned`. +/// ``` +pub fn transfer_expendable_all(_dust_trap: Option) +where + T: Mutate, + >::Balance: AtLeast8BitUnsigned + Debug, + AccountId: AtLeast8BitUnsigned, +{ + let initial_total_issuance = T::total_issuance(); + let initial_active_issuance = T::active_issuance(); + let account_0 = AccountId::from(0); + let account_1 = AccountId::from(1); + let initial_balance = T::minimum_balance() + 10.into(); + T::set_balance(&account_0, initial_balance); + T::set_balance(&account_1, initial_balance); + + // Test: Transfer entire balance from account_0 to account_1 + let preservation = Preservation::Expendable; + let transfer_amount = initial_balance; + T::transfer(&account_0, &account_1, transfer_amount, preservation).unwrap(); + + // Verify: Account balances are updated correctly + assert_eq!(T::total_balance(&account_0), T::Balance::zero()); + assert_eq!(T::total_balance(&account_1), initial_balance * 2.into()); + assert_eq!(T::balance(&account_0), T::Balance::zero()); + assert_eq!(T::balance(&account_1), initial_balance * 2.into()); + + // Verify: Total issuance doesn't change + assert_eq!(T::total_issuance(), initial_total_issuance + initial_balance * 2.into()); + assert_eq!(T::active_issuance(), initial_active_issuance + initial_balance * 2.into()); +} + +/// Test the transfer function with Preservation::Expendable for transferring amounts that leaves +/// an account with less than the minimum balance. +/// +/// This test verifies that when transferring an amount using Preservation::Expendable and an +/// account will be left with less than the minimum balance, the account balances are updated, dust +/// is collected properly depending on whether a dust_trap exists, and the total issuance and active +/// issuance values remain consistent. +/// +/// # Parameters +/// +/// - dust_trap: An optional account identifier to which dust will be collected. If None, dust will +/// be removed from the total and active issuance. +/// +/// # Type Parameters +/// +/// ```text +/// - T: Implements Mutate. +/// - AccountId: Account identifier implementing AtLeast8BitUnsigned. +/// ``` +pub fn transfer_expendable_dust(dust_trap: Option) +where + T: Mutate, + >::Balance: AtLeast8BitUnsigned + Debug, + AccountId: AtLeast8BitUnsigned, +{ + if T::minimum_balance() == T::Balance::zero() { + return + } + + let account_0 = AccountId::from(10); + let account_1 = AccountId::from(20); + let initial_balance = T::minimum_balance() + 10.into(); + T::set_balance(&account_0, initial_balance); + T::set_balance(&account_1, initial_balance); + + let initial_total_issuance = T::total_issuance(); + let initial_active_issuance = T::active_issuance(); + let initial_dust_trap_balance = match dust_trap.clone() { + Some(dust_trap) => T::total_balance(&dust_trap), + None => T::Balance::zero(), + }; + + // Test: Transfer balance + let preservation = Preservation::Expendable; + let transfer_amount = T::Balance::from(11); + T::transfer(&account_0, &account_1, transfer_amount, preservation).unwrap(); + + // Verify: Account balances are updated correctly + assert_eq!(T::total_balance(&account_0), T::Balance::zero()); + assert_eq!(T::total_balance(&account_1), initial_balance + transfer_amount); + assert_eq!(T::balance(&account_0), T::Balance::zero()); + assert_eq!(T::balance(&account_1), initial_balance + transfer_amount); + + match dust_trap { + Some(dust_trap) => { + // Verify: Total issuance and active issuance don't change + assert_eq!(T::total_issuance(), initial_total_issuance); + assert_eq!(T::active_issuance(), initial_active_issuance); + // Verify: Dust is collected into dust trap + assert_eq!( + T::total_balance(&dust_trap), + initial_dust_trap_balance + T::minimum_balance() - 1.into() + ); + assert_eq!( + T::balance(&dust_trap), + initial_dust_trap_balance + T::minimum_balance() - 1.into() + ); + }, + None => { + // Verify: Total issuance and active issuance are reduced by the dust amount + assert_eq!( + T::total_issuance(), + initial_total_issuance - T::minimum_balance() + 1.into() + ); + assert_eq!( + T::active_issuance(), + initial_active_issuance - T::minimum_balance() + 1.into() + ); + }, + } +} + +/// Test the `transfer` function with `Preservation::Protect` and `Preservation::Preserve` for +/// transferring the entire balance. +/// +/// This test verifies that attempting to transfer the entire balance with `Preservation::Protect` +/// or `Preservation::Preserve` returns an error, and the account balances, total issuance, and +/// active issuance values remain unchanged. +/// +/// # Type Parameters +/// +/// ```text +/// - `T`: Implements `Mutate`. +/// - `AccountId`: Account identifier implementing `AtLeast8BitUnsigned`. +/// ``` +pub fn transfer_protect_preserve(_dust_trap: Option) +where + T: Mutate, + >::Balance: AtLeast8BitUnsigned + Debug, + AccountId: AtLeast8BitUnsigned, +{ + // This test means nothing if there is no minimum balance + if T::minimum_balance() == T::Balance::zero() { + return + } + + let initial_total_issuance = T::total_issuance(); + let initial_active_issuance = T::active_issuance(); + let account_0 = AccountId::from(0); + let account_1 = AccountId::from(1); + let initial_balance = T::minimum_balance() + 10.into(); + T::set_balance(&account_0, initial_balance); + T::set_balance(&account_1, initial_balance); + + // Verify: Transfer Protect entire balance from account_0 to account_1 should Err + let preservation = Preservation::Protect; + let transfer_amount = initial_balance; + T::transfer(&account_0, &account_1, transfer_amount, preservation).unwrap_err(); + + // Verify: Noop + assert_eq!(T::total_balance(&account_0), initial_balance); + assert_eq!(T::total_balance(&account_1), initial_balance); + assert_eq!(T::balance(&account_0), initial_balance); + assert_eq!(T::balance(&account_1), initial_balance); + assert_eq!(T::total_issuance(), initial_total_issuance + initial_balance * 2.into()); + assert_eq!(T::active_issuance(), initial_active_issuance + initial_balance * 2.into()); + + // Verify: Transfer Preserve entire balance from account_0 to account_1 should Err + let preservation = Preservation::Preserve; + T::transfer(&account_0, &account_1, transfer_amount, preservation).unwrap_err(); + + // Verify: Noop + assert_eq!(T::total_balance(&account_0), initial_balance); + assert_eq!(T::total_balance(&account_1), initial_balance); + assert_eq!(T::balance(&account_0), initial_balance); + assert_eq!(T::balance(&account_1), initial_balance); + assert_eq!(T::total_issuance(), initial_total_issuance + initial_balance * 2.into()); + assert_eq!(T::active_issuance(), initial_active_issuance + initial_balance * 2.into()); +} + +/// Test the set_balance function for successful minting. +/// +/// This test verifies that minting a balance using set_balance updates the account balance, total +/// issuance, and active issuance correctly. +/// +/// # Type Parameters +/// +/// ```text +/// - T: Implements Mutate. +/// - AccountId: Account identifier implementing AtLeast8BitUnsigned. +/// ``` +pub fn set_balance_mint_success(_dust_trap: Option) +where + T: Mutate, + >::Balance: AtLeast8BitUnsigned + Debug, + AccountId: AtLeast8BitUnsigned, +{ + let initial_total_issuance = T::total_issuance(); + let initial_active_issuance = T::active_issuance(); + let account = AccountId::from(10); + let initial_balance = T::minimum_balance() + 10.into(); + T::mint_into(&account, initial_balance).unwrap(); + + // Test: Increase the account balance with set_balance + let increase_amount: T::Balance = 5.into(); + let new = T::set_balance(&account, initial_balance + increase_amount); + + // Verify: set_balance returned the new balance + let expected_new = initial_balance + increase_amount; + assert_eq!(new, expected_new); + + // Verify: Balance and issuance is updated correctly + assert_eq!(T::total_balance(&account), expected_new); + assert_eq!(T::balance(&account), expected_new); + assert_eq!(T::total_issuance(), initial_total_issuance + expected_new); + assert_eq!(T::active_issuance(), initial_active_issuance + expected_new); +} + +/// Test the set_balance function for successful burning. +/// +/// This test verifies that burning a balance using set_balance updates the account balance, total +/// issuance, and active issuance correctly. +/// +/// # Type Parameters +/// +/// ```text +/// - T: Implements Mutate. +/// - AccountId: Account identifier implementing AtLeast8BitUnsigned. +/// ``` +pub fn set_balance_burn_success(_dust_trap: Option) +where + T: Mutate, + >::Balance: AtLeast8BitUnsigned + Debug, + AccountId: AtLeast8BitUnsigned, +{ + let initial_total_issuance = T::total_issuance(); + let initial_active_issuance = T::active_issuance(); + let account = AccountId::from(10); + let initial_balance = T::minimum_balance() + 10.into(); + T::mint_into(&account, initial_balance).unwrap(); + + // Test: Increase the account balance with set_balance + let burn_amount: T::Balance = 5.into(); + let new = T::set_balance(&account, initial_balance - burn_amount); + + // Verify: set_balance returned the new balance + let expected_new = initial_balance - burn_amount; + assert_eq!(new, expected_new); + + // Verify: Balance and issuance is updated correctly + assert_eq!(T::total_balance(&account), expected_new); + assert_eq!(T::balance(&account), expected_new); + assert_eq!(T::total_issuance(), initial_total_issuance + expected_new); + assert_eq!(T::active_issuance(), initial_active_issuance + expected_new); +} + +/// Test the can_deposit function for returning a success value. +/// +/// This test verifies that the can_deposit function returns DepositConsequence::Success when +/// depositing a reasonable amount. +/// +/// # Type Parameters +/// +/// ```text +/// - T: Implements Mutate. +/// - AccountId: Account identifier implementing AtLeast8BitUnsigned. +/// ``` +pub fn can_deposit_success(_dust_trap: Option) +where + T: Mutate, + >::Balance: AtLeast8BitUnsigned + Debug, + AccountId: AtLeast8BitUnsigned, +{ + let account = AccountId::from(10); + let initial_balance = T::minimum_balance() + 10.into(); + T::mint_into(&account, initial_balance).unwrap(); + + // Test: can_deposit a reasonable amount + let ret = T::can_deposit(&account, 5.into(), Provenance::Minted); + + // Verify: Returns success + assert_eq!(ret, DepositConsequence::Success); +} + +/// Test the can_deposit function for returning a minimum balance error. +/// +/// This test verifies that the can_deposit function returns DepositConsequence::BelowMinimum when +/// depositing below the minimum balance. +/// +/// # Type Parameters +/// +/// ```text +/// - T: Implements Mutate. +/// - AccountId: Account identifier implementing AtLeast8BitUnsigned. +/// ``` +pub fn can_deposit_below_minimum(_dust_trap: Option) +where + T: Mutate, + >::Balance: AtLeast8BitUnsigned + Debug, + AccountId: AtLeast8BitUnsigned, +{ + // can_deposit always returns Success for amount 0 + if T::minimum_balance() < 2.into() { + return + } + + let account = AccountId::from(10); + + // Test: can_deposit below the minimum + let ret = T::can_deposit(&account, T::minimum_balance() - 1.into(), Provenance::Minted); + + // Verify: Returns success + assert_eq!(ret, DepositConsequence::BelowMinimum); +} + +/// Test the can_deposit function for returning an overflow error. +/// +/// This test verifies that the can_deposit function returns DepositConsequence::Overflow when +/// depositing an amount that would cause an overflow. +/// +/// # Type Parameters +/// +/// ```text +/// - T: Implements Mutate. +/// - AccountId: Account identifier implementing AtLeast8BitUnsigned. +/// ``` +pub fn can_deposit_overflow(_dust_trap: Option) +where + T: Mutate, + >::Balance: AtLeast8BitUnsigned + Debug, + AccountId: AtLeast8BitUnsigned, +{ + let account = AccountId::from(10); + + // Test: Try deposit over the max balance + let initial_balance = T::Balance::max_value() - 5.into() - T::total_issuance(); + T::mint_into(&account, initial_balance).unwrap(); + let ret = T::can_deposit(&account, 10.into(), Provenance::Minted); + + // Verify: Returns success + assert_eq!(ret, DepositConsequence::Overflow); +} + +/// Test the can_withdraw function for returning a success value. +/// +/// This test verifies that the can_withdraw function returns WithdrawConsequence::Success when +/// withdrawing a reasonable amount. +/// +/// # Type Parameters +/// +/// ```text +/// - T: Implements Mutate. +/// - AccountId: Account identifier implementing AtLeast8BitUnsigned. +/// ``` +pub fn can_withdraw_success(_dust_trap: Option) +where + T: Mutate, + >::Balance: AtLeast8BitUnsigned + Debug, + AccountId: AtLeast8BitUnsigned, +{ + let account = AccountId::from(10); + let initial_balance = T::minimum_balance() + 10.into(); + T::mint_into(&account, initial_balance).unwrap(); + + // Test: can_withdraw a reasonable amount + let ret = T::can_withdraw(&account, 5.into()); + + // Verify: Returns success + assert_eq!(ret, WithdrawConsequence::Success); +} + +/// Test the can_withdraw function for withdrawal resulting in a reduced balance of zero. +/// +/// This test verifies that the can_withdraw function returns WithdrawConsequence::ReducedToZero +/// when withdrawing an amount that would reduce the account balance below the minimum balance. +/// +/// # Type Parameters +/// +/// ```text +/// - T: Implements Mutate. +/// - AccountId: Account identifier implementing AtLeast8BitUnsigned. +/// ``` +pub fn can_withdraw_reduced_to_zero(_dust_trap: Option) +where + T: Mutate, + >::Balance: AtLeast8BitUnsigned + Debug, + AccountId: AtLeast8BitUnsigned, +{ + if T::minimum_balance() == T::Balance::zero() { + return + } + + let account = AccountId::from(10); + let initial_balance = T::minimum_balance(); + T::mint_into(&account, initial_balance).unwrap(); + + // Verify: can_withdraw below the minimum balance returns ReducedToZero + let ret = T::can_withdraw(&account, 1.into()); + assert_eq!(ret, WithdrawConsequence::ReducedToZero(T::minimum_balance() - 1.into())); +} + +/// Test the can_withdraw function for returning a low balance error. +/// +/// This test verifies that the can_withdraw function returns WithdrawConsequence::BalanceLow when +/// withdrawing an amount that would result in an account balance below the current balance. +/// +/// # Type Parameters +/// +/// ```text +/// - T: Implements Mutate. +/// - AccountId: Account identifier implementing AtLeast8BitUnsigned. +/// ``` +pub fn can_withdraw_balance_low(_dust_trap: Option) +where + T: Mutate, + >::Balance: AtLeast8BitUnsigned + Debug, + AccountId: AtLeast8BitUnsigned, +{ + if T::minimum_balance() == T::Balance::zero() { + return + } + + let account = AccountId::from(10); + let other_account = AccountId::from(100); + let initial_balance = T::minimum_balance() + 5.into(); + T::mint_into(&account, initial_balance).unwrap(); + T::mint_into(&other_account, initial_balance * 2.into()).unwrap(); + + // Verify: can_withdraw below the account balance returns BalanceLow + let ret = T::can_withdraw(&account, initial_balance + 1.into()); + assert_eq!(ret, WithdrawConsequence::BalanceLow); +} + +/// Test the reducible_balance function with Preservation::Expendable. +/// +/// This test verifies that the reducible_balance function returns the full account balance when +/// using Preservation::Expendable. +/// +/// # Type Parameters +/// +/// ```text +/// - T: Implements Mutate. +/// - AccountId: Account identifier implementing AtLeast8BitUnsigned. +/// ``` +pub fn reducible_balance_expendable(_dust_trap: Option) +where + T: Mutate, + >::Balance: AtLeast8BitUnsigned + Debug, + AccountId: AtLeast8BitUnsigned, +{ + let account = AccountId::from(10); + let initial_balance = T::minimum_balance() + 10.into(); + T::mint_into(&account, initial_balance).unwrap(); + + // Verify: reducible_balance returns the full balance + let ret = T::reducible_balance(&account, Preservation::Expendable, Fortitude::Polite); + assert_eq!(ret, initial_balance); +} + +/// Test the reducible_balance function with Preservation::Protect and Preservation::Preserve. +/// +/// This test verifies that the reducible_balance function returns the account balance minus the +/// minimum balance when using either Preservation::Protect or Preservation::Preserve. +/// +/// # Type Parameters +/// +/// ```text +/// - T: Implements Mutate. +/// - AccountId: Account identifier implementing AtLeast8BitUnsigned. +/// ``` +pub fn reducible_balance_protect_preserve(_dust_trap: Option) +where + T: Mutate, + >::Balance: AtLeast8BitUnsigned + Debug, + AccountId: AtLeast8BitUnsigned, +{ + let account = AccountId::from(10); + let initial_balance = T::minimum_balance() + 10.into(); + T::mint_into(&account, initial_balance).unwrap(); + + // Verify: reducible_balance returns the full balance - min balance + let ret = T::reducible_balance(&account, Preservation::Protect, Fortitude::Polite); + assert_eq!(ret, initial_balance - T::minimum_balance()); + let ret = T::reducible_balance(&account, Preservation::Preserve, Fortitude::Polite); + assert_eq!(ret, initial_balance - T::minimum_balance()); +} diff --git a/frame/support/src/traits/tokens/fungible/conformance_tests/mod.rs b/frame/support/src/traits/tokens/fungible/conformance_tests/mod.rs new file mode 100644 index 000000000..88ba56a6f --- /dev/null +++ b/frame/support/src/traits/tokens/fungible/conformance_tests/mod.rs @@ -0,0 +1 @@ +pub mod inspect_mutate; diff --git a/frame/support/src/traits/tokens/fungible/mod.rs b/frame/support/src/traits/tokens/fungible/mod.rs index 204b85ac2..8ab63ad36 100644 --- a/frame/support/src/traits/tokens/fungible/mod.rs +++ b/frame/support/src/traits/tokens/fungible/mod.rs @@ -38,6 +38,7 @@ //! funds must be removed from an account before it is known precisely what should be done with //! them. +pub mod conformance_tests; pub mod freeze; pub mod hold; mod imbalance; diff --git a/frame/support/src/traits/tokens/fungible/regular.rs b/frame/support/src/traits/tokens/fungible/regular.rs index 5a201b33b..347654946 100644 --- a/frame/support/src/traits/tokens/fungible/regular.rs +++ b/frame/support/src/traits/tokens/fungible/regular.rs @@ -122,7 +122,7 @@ impl> Dust { /// **WARNING** /// Do not use this directly unless you want trouble, since it allows you to alter account balances /// without keeping the issuance up to date. It has no safeguards against accidentally creating -/// token imbalances in your system leading to accidental imflation or deflation. It's really just +/// token imbalances in your system leading to accidental inflation or deflation. It's really just /// for the underlying datatype to implement so the user gets the much safer `Balanced` trait to /// use. pub trait Unbalanced: Inspect { From b01f3405cc9c7f9f7f0dbc6830acd83ded6beeff Mon Sep 17 00:00:00 2001 From: Ross Bulat Date: Thu, 4 May 2023 18:27:02 +0100 Subject: [PATCH 457/558] add fast unstake (#14077) Co-authored-by: parity-processbot <> --- bin/node/runtime/src/lib.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/bin/node/runtime/src/lib.rs b/bin/node/runtime/src/lib.rs index 5ce55ba8b..02a126cc4 100644 --- a/bin/node/runtime/src/lib.rs +++ b/bin/node/runtime/src/lib.rs @@ -324,7 +324,8 @@ impl InstanceFilter for ProxyType { RuntimeCall::Elections(..) | RuntimeCall::Treasury(..) ), - ProxyType::Staking => matches!(c, RuntimeCall::Staking(..)), + ProxyType::Staking => + matches!(c, RuntimeCall::Staking(..) | RuntimeCall::FastUnstake(..)), } } fn is_superset(&self, o: &Self) -> bool { From 133157a54d6602981b8a7a40c70da28b7cc60716 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bastian=20K=C3=B6cher?= Date: Thu, 4 May 2023 21:18:53 +0200 Subject: [PATCH 458/558] Improve handling of unset `StorageVersion` (#13417) * Improve handling of unset `StorageVersion` When a user is forgetting to set the storage version in a pallet and calls `current_storage_version` to compare it against the `on_chain_storage_version` it will now fail to compile the code. Before the pallet macro just returned `StorageVersion::default()` for `current_storage_version` leading to potential issues with migrations. Besides that it also checks in `post_upgrade` that the pallet storage version was upgraded and thus, no migration was missed. * Use correct `Cargo.lock` * Fixes * Fix test * Update frame/support/test/tests/pallet.rs * Ensure we don't set a storage version when the pallet is missing the attribute * Fix merge conflict * Update frame/support/procedural/src/pallet/expand/hooks.rs Co-authored-by: Roman Useinov * Update frame/support/procedural/src/pallet/expand/hooks.rs Co-authored-by: Roman Useinov * Fix compilation * Do not run everything with `try-runtime` * Fix test * Apply suggestions from code review Co-authored-by: Oliver Tale-Yazdi * Fix `no-metadata-docs` --------- Co-authored-by: Roman Useinov Co-authored-by: Oliver Tale-Yazdi Co-authored-by: parity-processbot <> --- Cargo.lock | 1 + .../procedural/src/pallet/expand/hooks.rs | 53 +++++++ .../src/pallet/expand/pallet_struct.rs | 20 ++- frame/support/src/dispatch.rs | 38 +++-- frame/support/src/migrations.rs | 34 ++++- frame/support/src/traits.rs | 4 +- frame/support/src/traits/metadata.rs | 22 ++- frame/support/test/Cargo.toml | 10 +- frame/support/test/tests/derive_no_bound.rs | 63 ++++++-- frame/support/test/tests/pallet.rs | 144 +++++++++++++++--- .../compare_unset_storage_version.rs | 27 ++++ .../compare_unset_storage_version.stderr | 7 + frame/support/test/tests/runtime_metadata.rs | 2 +- .../src/extensions/check_non_zero_sender.rs | 6 +- primitives/api/Cargo.toml | 2 +- primitives/api/proc-macro/Cargo.toml | 3 +- scripts/ci/gitlab/pipeline/test.yml | 4 +- 17 files changed, 368 insertions(+), 72 deletions(-) create mode 100644 frame/support/test/tests/pallet_ui/compare_unset_storage_version.rs create mode 100644 frame/support/test/tests/pallet_ui/compare_unset_storage_version.stderr diff --git a/Cargo.lock b/Cargo.lock index 41e059366..edde0fed2 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2596,6 +2596,7 @@ name = "frame-support-test" version = "3.0.0" dependencies = [ "frame-benchmarking", + "frame-executive", "frame-support", "frame-support-test-pallet", "frame-system", diff --git a/frame/support/procedural/src/pallet/expand/hooks.rs b/frame/support/procedural/src/pallet/expand/hooks.rs index 711b78e64..df07040b8 100644 --- a/frame/support/procedural/src/pallet/expand/hooks.rs +++ b/frame/support/procedural/src/pallet/expand/hooks.rs @@ -82,6 +82,57 @@ pub fn expand_hooks(def: &mut Def) -> proc_macro2::TokenStream { proc_macro2::TokenStream::new() }; + // If a storage version is set, we should ensure that the storage version on chain matches the + // current storage version. This assumes that `Executive` is running custom migrations before + // the pallets are called. + let post_storage_version_check = if def.pallet_struct.storage_version.is_some() { + quote::quote! { + let on_chain_version = ::on_chain_storage_version(); + let current_version = ::current_storage_version(); + + if on_chain_version != current_version { + let pallet_name = < + ::PalletInfo + as + #frame_support::traits::PalletInfo + >::name::().unwrap_or(""); + + #frame_support::log::error!( + target: #frame_support::LOG_TARGET, + "{}: On chain storage version {:?} doesn't match current storage version {:?}.", + pallet_name, + on_chain_version, + current_version, + ); + + return Err("On chain and current storage version do not match. Missing runtime upgrade?"); + } + } + } else { + quote::quote! { + let on_chain_version = ::on_chain_storage_version(); + + if on_chain_version != #frame_support::traits::StorageVersion::new(0) { + let pallet_name = < + ::PalletInfo + as + #frame_support::traits::PalletInfo + >::name::().unwrap_or(""); + + #frame_support::log::error!( + target: #frame_support::LOG_TARGET, + "{}: On chain storage version {:?} is set to non zero,\ + while the pallet is missing the `#[pallet::storage_version(VERSION)]` attribute.", + pallet_name, + on_chain_version, + ); + + return Err("On chain storage version set, while the pallet doesn't \ + have the `#[pallet::storage_version(VERSION)]` attribute."); + } + } + }; + quote::quote_spanned!(span => #hooks_impl @@ -170,6 +221,8 @@ pub fn expand_hooks(def: &mut Def) -> proc_macro2::TokenStream { #[cfg(feature = "try-runtime")] fn post_upgrade(state: #frame_support::sp_std::vec::Vec) -> Result<(), &'static str> { + #post_storage_version_check + < Self as diff --git a/frame/support/procedural/src/pallet/expand/pallet_struct.rs b/frame/support/procedural/src/pallet/expand/pallet_struct.rs index 7acfb9090..99d2d79f2 100644 --- a/frame/support/procedural/src/pallet/expand/pallet_struct.rs +++ b/frame/support/procedural/src/pallet/expand/pallet_struct.rs @@ -160,11 +160,15 @@ pub fn expand_pallet_struct(def: &mut Def) -> proc_macro2::TokenStream { } ); - let storage_version = if let Some(v) = def.pallet_struct.storage_version.as_ref() { - quote::quote! { #v } - } else { - quote::quote! { #frame_support::traits::StorageVersion::default() } - }; + let (storage_version, current_storage_version_ty) = + if let Some(v) = def.pallet_struct.storage_version.as_ref() { + (quote::quote! { #v }, quote::quote! { #frame_support::traits::StorageVersion }) + } else { + ( + quote::quote! { core::default::Default::default() }, + quote::quote! { #frame_support::traits::NoStorageVersionSet }, + ) + }; let whitelisted_storage_idents: Vec = def .storages @@ -199,7 +203,9 @@ pub fn expand_pallet_struct(def: &mut Def) -> proc_macro2::TokenStream { for #pallet_ident<#type_use_gen> #config_where_clause { - fn current_storage_version() -> #frame_support::traits::StorageVersion { + type CurrentStorageVersion = #current_storage_version_ty; + + fn current_storage_version() -> Self::CurrentStorageVersion { #storage_version } @@ -214,7 +220,7 @@ pub fn expand_pallet_struct(def: &mut Def) -> proc_macro2::TokenStream { #config_where_clause { fn on_genesis() { - let storage_version = #storage_version; + let storage_version: #frame_support::traits::StorageVersion = #storage_version; storage_version.put::(); } } diff --git a/frame/support/src/dispatch.rs b/frame/support/src/dispatch.rs index c75d75b41..db9405245 100644 --- a/frame/support/src/dispatch.rs +++ b/frame/support/src/dispatch.rs @@ -2500,7 +2500,9 @@ macro_rules! decl_module { impl<$trait_instance: $trait_name $(, $instance: $instantiable)?> $crate::traits::GetStorageVersion for $module<$trait_instance $(, $instance)?> where $( $other_where_bounds )* { - fn current_storage_version() -> $crate::traits::StorageVersion { + type CurrentStorageVersion = $crate::traits::StorageVersion; + + fn current_storage_version() -> Self::CurrentStorageVersion { $( $storage_version )* } @@ -2508,6 +2510,16 @@ macro_rules! decl_module { $crate::traits::StorageVersion::get::() } } + + // Implement `OnGenesis` for `Module` + impl<$trait_instance: $trait_name $(, $instance: $instantiable)?> $crate::traits::OnGenesis + for $module<$trait_instance $(, $instance)?> where $( $other_where_bounds )* + { + fn on_genesis() { + let storage_version = ::current_storage_version(); + storage_version.put::(); + } + } }; // Implementation for `GetStorageVersion` when no storage version is passed. @@ -2519,7 +2531,9 @@ macro_rules! decl_module { impl<$trait_instance: $trait_name $(, $instance: $instantiable)?> $crate::traits::GetStorageVersion for $module<$trait_instance $(, $instance)?> where $( $other_where_bounds )* { - fn current_storage_version() -> $crate::traits::StorageVersion { + type CurrentStorageVersion = $crate::traits::NoStorageVersionSet; + + fn current_storage_version() -> Self::CurrentStorageVersion { Default::default() } @@ -2527,6 +2541,16 @@ macro_rules! decl_module { $crate::traits::StorageVersion::get::() } } + + // Implement `OnGenesis` for `Module` + impl<$trait_instance: $trait_name $(, $instance: $instantiable)?> $crate::traits::OnGenesis + for $module<$trait_instance $(, $instance)?> where $( $other_where_bounds )* + { + fn on_genesis() { + let storage_version = $crate::traits::StorageVersion::default(); + storage_version.put::(); + } + } }; // The main macro expansion that actually renders the module code. @@ -2814,16 +2838,6 @@ macro_rules! decl_module { } } - // Implement `OnGenesis` for `Module` - impl<$trait_instance: $trait_name $(, $instance: $instantiable)?> $crate::traits::OnGenesis - for $mod_type<$trait_instance $(, $instance)?> where $( $other_where_bounds )* - { - fn on_genesis() { - let storage_version = ::current_storage_version(); - storage_version.put::(); - } - } - // manual implementation of clone/eq/partialeq because using derive erroneously requires // clone/eq/partialeq from T. impl<$trait_instance: $trait_name $(, $instance: $instantiable)?> $crate::dispatch::Clone diff --git a/frame/support/src/migrations.rs b/frame/support/src/migrations.rs index f54b73aad..381f1feda 100644 --- a/frame/support/src/migrations.rs +++ b/frame/support/src/migrations.rs @@ -18,7 +18,7 @@ #[cfg(feature = "try-runtime")] use crate::storage::unhashed::contains_prefixed_key; use crate::{ - traits::{GetStorageVersion, PalletInfoAccess}, + traits::{GetStorageVersion, NoStorageVersionSet, PalletInfoAccess, StorageVersion}, weights::{RuntimeDbWeight, Weight}, }; use impl_trait_for_tuples::impl_for_tuples; @@ -28,12 +28,38 @@ use sp_std::marker::PhantomData; #[cfg(feature = "try-runtime")] use sp_std::vec::Vec; +/// Can store the current pallet version in storage. +pub trait StoreCurrentStorageVersion { + /// Write the current storage version to the storage. + fn store_current_storage_version(); +} + +impl + PalletInfoAccess> + StoreCurrentStorageVersion for StorageVersion +{ + fn store_current_storage_version() { + let version = ::current_storage_version(); + version.put::(); + } +} + +impl + PalletInfoAccess> + StoreCurrentStorageVersion for NoStorageVersionSet +{ + fn store_current_storage_version() { + StorageVersion::default().put::(); + } +} + /// Trait used by [`migrate_from_pallet_version_to_storage_version`] to do the actual migration. pub trait PalletVersionToStorageVersionHelper { fn migrate(db_weight: &RuntimeDbWeight) -> Weight; } -impl PalletVersionToStorageVersionHelper for T { +impl PalletVersionToStorageVersionHelper for T +where + T::CurrentStorageVersion: StoreCurrentStorageVersion, +{ fn migrate(db_weight: &RuntimeDbWeight) -> Weight { const PALLET_VERSION_STORAGE_KEY_POSTFIX: &[u8] = b":__PALLET_VERSION__:"; @@ -43,8 +69,8 @@ impl PalletVersionToStorageVersionHelpe sp_io::storage::clear(&pallet_version_key(::name())); - let version = ::current_storage_version(); - version.put::(); + >::store_current_storage_version( + ); db_weight.writes(2) } diff --git a/frame/support/src/traits.rs b/frame/support/src/traits.rs index 17d2fb06c..b2e6a6138 100644 --- a/frame/support/src/traits.rs +++ b/frame/support/src/traits.rs @@ -75,8 +75,8 @@ pub use randomness::Randomness; mod metadata; pub use metadata::{ CallMetadata, CrateVersion, GetCallIndex, GetCallMetadata, GetCallName, GetStorageVersion, - PalletInfo, PalletInfoAccess, PalletInfoData, PalletsInfoAccess, StorageVersion, - STORAGE_VERSION_STORAGE_KEY_POSTFIX, + NoStorageVersionSet, PalletInfo, PalletInfoAccess, PalletInfoData, PalletsInfoAccess, + StorageVersion, STORAGE_VERSION_STORAGE_KEY_POSTFIX, }; mod hooks; diff --git a/frame/support/src/traits/metadata.rs b/frame/support/src/traits/metadata.rs index 20ddb1d34..54d264ec6 100644 --- a/frame/support/src/traits/metadata.rs +++ b/frame/support/src/traits/metadata.rs @@ -232,6 +232,16 @@ impl PartialOrd for StorageVersion { } } +/// Special marker struct if no storage version is set for a pallet. +/// +/// If you (the reader) end up here, it probably means that you tried to compare +/// [`GetStorageVersion::on_chain_storage_version`] against +/// [`GetStorageVersion::current_storage_version`]. This basically means that the +/// [`storage_version`](crate::pallet_macros::storage_version) is missing in the pallet where the +/// mentioned functions are being called. +#[derive(Debug, Default)] +pub struct NoStorageVersionSet; + /// Provides information about the storage version of a pallet. /// /// It differentiates between current and on-chain storage version. Both should be only out of sync @@ -244,8 +254,18 @@ impl PartialOrd for StorageVersion { /// /// It is required to update the on-chain storage version manually when a migration was applied. pub trait GetStorageVersion { + /// This will be filled out by the [`pallet`](crate::pallet) macro. + /// + /// If the [`storage_version`](crate::pallet_macros::storage_version) attribute isn't given + /// this is set to [`NoStorageVersionSet`] to inform the user that the attribute is missing. + /// This should prevent that the user forgets to set a storage version when required. However, + /// this will only work when the user actually tries to call [`Self::current_storage_version`] + /// to compare it against the [`Self::on_chain_storage_version`]. If the attribute is given, + /// this will be set to [`StorageVersion`]. + type CurrentStorageVersion; + /// Returns the current storage version as supported by the pallet. - fn current_storage_version() -> StorageVersion; + fn current_storage_version() -> Self::CurrentStorageVersion; /// Returns the on-chain storage version of the pallet as stored in the storage. fn on_chain_storage_version() -> StorageVersion; } diff --git a/frame/support/test/Cargo.toml b/frame/support/test/Cargo.toml index 9564b6a04..68411210f 100644 --- a/frame/support/test/Cargo.toml +++ b/frame/support/test/Cargo.toml @@ -26,9 +26,10 @@ sp-core = { version = "7.0.0", default-features = false, path = "../../../primit sp-std = { version = "5.0.0", default-features = false, path = "../../../primitives/std" } sp-version = { version = "5.0.0", default-features = false, path = "../../../primitives/version" } trybuild = { version = "1.0.74", features = [ "diff" ] } -pretty_assertions = "1.2.1" +pretty_assertions = "1.3.0" rustversion = "1.0.6" frame-system = { version = "4.0.0-dev", default-features = false, path = "../../system" } +frame-executive = { version = "4.0.0-dev", default-features = false, path = "../../executive" } # The "std" feature for this pallet is never activated on purpose, in order to test construct_runtime error message test-pallet = { package = "frame-support-test-pallet", default-features = false, path = "pallet" } @@ -39,6 +40,7 @@ std = [ "codec/std", "scale-info/std", "frame-benchmarking/std", + "frame-executive/std", "frame-support/std", "frame-system/std", "sp-core/std", @@ -50,7 +52,11 @@ std = [ "sp-version/std", "sp-api/std", ] -try-runtime = ["frame-support/try-runtime"] +try-runtime = [ + "frame-support/try-runtime", + "frame-system/try-runtime", + "frame-executive/try-runtime", +] # WARNING: # Only CI runs with this feature enabled. This feature is for testing stuff related to the FRAME macros # in conjunction with rust features. diff --git a/frame/support/test/tests/derive_no_bound.rs b/frame/support/test/tests/derive_no_bound.rs index 8b1394fcd..dc78027f2 100644 --- a/frame/support/test/tests/derive_no_bound.rs +++ b/frame/support/test/tests/derive_no_bound.rs @@ -50,6 +50,23 @@ struct StructNamed { phantom: core::marker::PhantomData<(U, V)>, } +#[rustversion::attr(not(stable), ignore)] +#[cfg(not(feature = "disable-ui-tests"))] +#[test] +fn test_struct_named_debug_print() { + let a_1 = StructNamed:: { + a: 1, + b: 2, + c: 3, + phantom: Default::default(), + }; + + assert_eq!( + format!("{:?}", a_1), + String::from("StructNamed { a: 1, b: 2, c: 3, phantom: PhantomData<(derive_no_bound::ImplNone, derive_no_bound::ImplNone)> }") + ); +} + #[test] fn test_struct_named() { let a_1 = StructNamed:: { @@ -70,10 +87,6 @@ fn test_struct_named() { assert_eq!(a_2.b, 2); assert_eq!(a_2.c, 3); assert_eq!(a_2, a_1); - assert_eq!( - format!("{:?}", a_1), - String::from("StructNamed { a: 1, b: 2, c: 3, phantom: PhantomData<(derive_no_bound::ImplNone, derive_no_bound::ImplNone)> }") - ); let b = StructNamed:: { a: 1, @@ -88,6 +101,14 @@ fn test_struct_named() { #[derive(DebugNoBound, CloneNoBound, EqNoBound, PartialEqNoBound, DefaultNoBound)] struct StructUnnamed(u32, u64, T::C, core::marker::PhantomData<(U, V)>); +#[rustversion::attr(not(stable), ignore)] +#[cfg(not(feature = "disable-ui-tests"))] +#[test] +fn test_struct_unnamed_debug_print() { + let a_1 = StructUnnamed::(1, 2, 3, Default::default()); + assert_eq!(format!("{:?}", a_1), String::from("StructUnnamed(1, 2, 3, PhantomData<(derive_no_bound::ImplNone, derive_no_bound::ImplNone)>)")); +} + #[test] fn test_struct_unnamed() { let a_1 = StructUnnamed::(1, 2, 3, Default::default()); @@ -103,7 +124,6 @@ fn test_struct_unnamed() { assert_eq!(a_2.1, 2); assert_eq!(a_2.2, 3); assert_eq!(a_2, a_1); - assert_eq!(format!("{:?}", a_1), String::from("StructUnnamed(1, 2, 3, PhantomData<(derive_no_bound::ImplNone, derive_no_bound::ImplNone)>)")); let b = StructUnnamed::(1, 2, 4, Default::default()); @@ -169,6 +189,28 @@ enum Enum3 { VariantUnit2, } +#[rustversion::attr(not(stable), ignore)] +#[cfg(not(feature = "disable-ui-tests"))] +#[test] +fn test_enum_debug_print() { + type TestEnum = Enum; + let variant_0 = TestEnum::VariantUnnamed(1, 2, 3, Default::default()); + let variant_1 = TestEnum::VariantNamed { a: 1, b: 2, c: 3, phantom: Default::default() }; + let variant_2 = TestEnum::VariantUnit; + let variant_3 = TestEnum::VariantUnit2; + + assert_eq!( + format!("{:?}", variant_0), + String::from("Enum::VariantUnnamed(1, 2, 3, PhantomData<(derive_no_bound::ImplNone, derive_no_bound::ImplNone)>)"), + ); + assert_eq!( + format!("{:?}", variant_1), + String::from("Enum::VariantNamed { a: 1, b: 2, c: 3, phantom: PhantomData<(derive_no_bound::ImplNone, derive_no_bound::ImplNone)> }"), + ); + assert_eq!(format!("{:?}", variant_2), String::from("Enum::VariantUnit")); + assert_eq!(format!("{:?}", variant_3), String::from("Enum::VariantUnit2")); +} + #[test] fn test_enum() { type TestEnum = Enum; @@ -208,15 +250,4 @@ fn test_enum() { assert!(variant_1.clone() == variant_1); assert!(variant_2.clone() == variant_2); assert!(variant_3.clone() == variant_3); - - assert_eq!( - format!("{:?}", variant_0), - String::from("Enum::VariantUnnamed(1, 2, 3, PhantomData<(derive_no_bound::ImplNone, derive_no_bound::ImplNone)>)"), - ); - assert_eq!( - format!("{:?}", variant_1), - String::from("Enum::VariantNamed { a: 1, b: 2, c: 3, phantom: PhantomData<(derive_no_bound::ImplNone, derive_no_bound::ImplNone)> }"), - ); - assert_eq!(format!("{:?}", variant_2), String::from("Enum::VariantUnit")); - assert_eq!(format!("{:?}", variant_3), String::from("Enum::VariantUnit2")); } diff --git a/frame/support/test/tests/pallet.rs b/frame/support/test/tests/pallet.rs index f3c4e0740..7f15ad1f9 100644 --- a/frame/support/test/tests/pallet.rs +++ b/frame/support/test/tests/pallet.rs @@ -23,6 +23,7 @@ use frame_support::{ }, dispatch_context::with_context, pallet_prelude::{StorageInfoTrait, ValueQuery}, + parameter_types, storage::unhashed, traits::{ ConstU32, GetCallIndex, GetCallName, GetStorageVersion, OnFinalize, OnGenesis, @@ -37,6 +38,11 @@ use sp_io::{ }; use sp_runtime::{DispatchError, ModuleError}; +parameter_types! { + /// Used to control if the storage version should be updated. + storage UpdateStorageVersion: bool = false; +} + /// Latest stable metadata version used for testing. const LATEST_METADATA_VERSION: u32 = 14; @@ -500,10 +506,12 @@ pub mod pallet { // and that a pallet with the attribute without_storage_info is correctly handled. #[frame_support::pallet] pub mod pallet2 { - use super::{SomeAssociation1, SomeType1}; + use super::{SomeAssociation1, SomeType1, UpdateStorageVersion}; use frame_support::pallet_prelude::*; use frame_system::pallet_prelude::*; + pub(crate) const STORAGE_VERSION: StorageVersion = StorageVersion::new(2); + #[pallet::config] pub trait Config: frame_system::Config where @@ -513,6 +521,7 @@ pub mod pallet2 { } #[pallet::pallet] + #[pallet::storage_version(STORAGE_VERSION)] #[pallet::without_storage_info] pub struct Pallet(_); @@ -530,6 +539,11 @@ pub mod pallet2 { } fn on_runtime_upgrade() -> Weight { Self::deposit_event(Event::Something(31)); + + if UpdateStorageVersion::get() { + Self::current_storage_version().put::(); + } + Weight::zero() } } @@ -683,7 +697,8 @@ impl pallet5::Config for Runtime { pub type Header = sp_runtime::generic::Header; pub type Block = sp_runtime::generic::Block; -pub type UncheckedExtrinsic = sp_runtime::generic::UncheckedExtrinsic; +pub type UncheckedExtrinsic = + sp_runtime::testing::TestXt>; frame_support::construct_runtime!( pub struct Runtime where @@ -812,7 +827,7 @@ fn inherent_expand() { let inherents = InherentData::new().create_extrinsics(); let expected = vec![UncheckedExtrinsic { - function: RuntimeCall::Example(pallet::Call::foo_no_post_info {}), + call: RuntimeCall::Example(pallet::Call::foo_no_post_info {}), signature: None, }]; assert_eq!(expected, inherents); @@ -827,11 +842,11 @@ fn inherent_expand() { ), vec![ UncheckedExtrinsic { - function: RuntimeCall::Example(pallet::Call::foo_no_post_info {}), + call: RuntimeCall::Example(pallet::Call::foo_no_post_info {}), signature: None, }, UncheckedExtrinsic { - function: RuntimeCall::Example(pallet::Call::foo { foo: 1, bar: 0 }), + call: RuntimeCall::Example(pallet::Call::foo { foo: 1, bar: 0 }), signature: None, }, ], @@ -849,11 +864,11 @@ fn inherent_expand() { ), vec![ UncheckedExtrinsic { - function: RuntimeCall::Example(pallet::Call::foo_no_post_info {}), + call: RuntimeCall::Example(pallet::Call::foo_no_post_info {}), signature: None, }, UncheckedExtrinsic { - function: RuntimeCall::Example(pallet::Call::foo { foo: 0, bar: 0 }), + call: RuntimeCall::Example(pallet::Call::foo { foo: 0, bar: 0 }), signature: None, }, ], @@ -870,7 +885,7 @@ fn inherent_expand() { Digest::default(), ), vec![UncheckedExtrinsic { - function: RuntimeCall::Example(pallet::Call::foo_storage_layer { foo: 0 }), + call: RuntimeCall::Example(pallet::Call::foo_storage_layer { foo: 0 }), signature: None, }], ); @@ -888,8 +903,8 @@ fn inherent_expand() { Digest::default(), ), vec![UncheckedExtrinsic { - function: RuntimeCall::Example(pallet::Call::foo_no_post_info {}), - signature: Some((1, (), ())), + call: RuntimeCall::Example(pallet::Call::foo_no_post_info {}), + signature: Some((1, Default::default())), }], ); @@ -907,11 +922,11 @@ fn inherent_expand() { ), vec![ UncheckedExtrinsic { - function: RuntimeCall::Example(pallet::Call::foo { foo: 1, bar: 1 }), + call: RuntimeCall::Example(pallet::Call::foo { foo: 1, bar: 1 }), signature: None, }, UncheckedExtrinsic { - function: RuntimeCall::Example(pallet::Call::foo_storage_layer { foo: 0 }), + call: RuntimeCall::Example(pallet::Call::foo_storage_layer { foo: 0 }), signature: None, }, ], @@ -929,15 +944,15 @@ fn inherent_expand() { ), vec![ UncheckedExtrinsic { - function: RuntimeCall::Example(pallet::Call::foo { foo: 1, bar: 1 }), + call: RuntimeCall::Example(pallet::Call::foo { foo: 1, bar: 1 }), signature: None, }, UncheckedExtrinsic { - function: RuntimeCall::Example(pallet::Call::foo_storage_layer { foo: 0 }), + call: RuntimeCall::Example(pallet::Call::foo_storage_layer { foo: 0 }), signature: None, }, UncheckedExtrinsic { - function: RuntimeCall::Example(pallet::Call::foo_no_post_info {}), + call: RuntimeCall::Example(pallet::Call::foo_no_post_info {}), signature: None, }, ], @@ -955,15 +970,15 @@ fn inherent_expand() { ), vec![ UncheckedExtrinsic { - function: RuntimeCall::Example(pallet::Call::foo { foo: 1, bar: 1 }), + call: RuntimeCall::Example(pallet::Call::foo { foo: 1, bar: 1 }), signature: None, }, UncheckedExtrinsic { - function: RuntimeCall::Example(pallet::Call::foo { foo: 1, bar: 0 }), - signature: Some((1, (), ())), + call: RuntimeCall::Example(pallet::Call::foo { foo: 1, bar: 0 }), + signature: Some((1, Default::default())), }, UncheckedExtrinsic { - function: RuntimeCall::Example(pallet::Call::foo_no_post_info {}), + call: RuntimeCall::Example(pallet::Call::foo_no_post_info {}), signature: None, }, ], @@ -1278,7 +1293,7 @@ fn migrate_from_pallet_version_to_storage_version() { assert!(sp_io::storage::get(&pallet_version_key(System::name())).is_none()); assert_eq!(Example::on_chain_storage_version(), pallet::STORAGE_VERSION); - assert_eq!(Example2::on_chain_storage_version(), StorageVersion::new(0)); + assert_eq!(Example2::on_chain_storage_version(), pallet2::STORAGE_VERSION); assert_eq!(System::on_chain_storage_version(), StorageVersion::new(0)); }); } @@ -2040,6 +2055,95 @@ fn test_storage_alias() { }) } +#[cfg(feature = "try-runtime")] +#[test] +fn post_runtime_upgrade_detects_storage_version_issues() { + use frame_support::traits::UpgradeCheckSelect; + + struct CustomUpgrade; + + impl OnRuntimeUpgrade for CustomUpgrade { + fn on_runtime_upgrade() -> Weight { + Example2::current_storage_version().put::(); + + Default::default() + } + } + + struct CustomUpgradePallet4; + + impl OnRuntimeUpgrade for CustomUpgradePallet4 { + fn on_runtime_upgrade() -> Weight { + StorageVersion::new(100).put::(); + + Default::default() + } + } + + type Executive = frame_executive::Executive< + Runtime, + Block, + frame_system::ChainContext, + Runtime, + AllPalletsWithSystem, + >; + + type ExecutiveWithUpgrade = frame_executive::Executive< + Runtime, + Block, + frame_system::ChainContext, + Runtime, + AllPalletsWithSystem, + CustomUpgrade, + >; + + type ExecutiveWithUpgradePallet4 = frame_executive::Executive< + Runtime, + Block, + frame_system::ChainContext, + Runtime, + AllPalletsWithSystem, + CustomUpgradePallet4, + >; + + TestExternalities::default().execute_with(|| { + // Call `on_genesis` to put the storage version of `Example` into the storage. + Example::on_genesis(); + // The version isn't changed, we should detect it. + assert!(Executive::try_runtime_upgrade(UpgradeCheckSelect::PreAndPost) + .unwrap_err() + .contains("On chain and current storage version do not match")); + }); + + TestExternalities::default().execute_with(|| { + // Call `on_genesis` to put the storage version of `Example` into the storage. + Example::on_genesis(); + // We set the new storage version in the pallet and that should be detected. + UpdateStorageVersion::set(&true); + Executive::try_runtime_upgrade(UpgradeCheckSelect::PreAndPost).unwrap(); + }); + + TestExternalities::default().execute_with(|| { + // Call `on_genesis` to put the storage version of `Example` into the storage. + Example::on_genesis(); + // We set the new storage version in the custom upgrade and that should be detected. + ExecutiveWithUpgrade::try_runtime_upgrade(UpgradeCheckSelect::PreAndPost).unwrap(); + }); + + TestExternalities::default().execute_with(|| { + // Call `on_genesis` to put the storage version of `Example` into the storage. + Example::on_genesis(); + // We need to set the correct storage version for `Example2` + UpdateStorageVersion::set(&true); + + // `CustomUpgradePallet4` will set a storage version for `Example4` while this doesn't has + // any storage version "enabled". + assert!(ExecutiveWithUpgradePallet4::try_runtime_upgrade(UpgradeCheckSelect::PreAndPost) + .unwrap_err() + .contains("On chain storage version set, while the pallet doesn't")); + }); +} + #[test] fn test_dispatch_context() { TestExternalities::default().execute_with(|| { diff --git a/frame/support/test/tests/pallet_ui/compare_unset_storage_version.rs b/frame/support/test/tests/pallet_ui/compare_unset_storage_version.rs new file mode 100644 index 000000000..e417c619f --- /dev/null +++ b/frame/support/test/tests/pallet_ui/compare_unset_storage_version.rs @@ -0,0 +1,27 @@ +#[frame_support::pallet] +mod pallet { + use frame_support::pallet_prelude::*; + use frame_system::pallet_prelude::*; + + #[pallet::config] + pub trait Config: frame_system::Config {} + + #[pallet::pallet] + pub struct Pallet(core::marker::PhantomData); + + #[pallet::hooks] + impl Hooks> for Pallet { + fn on_runtime_upgrade() -> Weight { + if Self::current_storage_version() != Self::on_chain_storage_version() { + + } + + Default::default() + } + } + + #[pallet::call] + impl Pallet {} +} + +fn main() {} diff --git a/frame/support/test/tests/pallet_ui/compare_unset_storage_version.stderr b/frame/support/test/tests/pallet_ui/compare_unset_storage_version.stderr new file mode 100644 index 000000000..e75aa5226 --- /dev/null +++ b/frame/support/test/tests/pallet_ui/compare_unset_storage_version.stderr @@ -0,0 +1,7 @@ +error[E0369]: binary operation `!=` cannot be applied to type `NoStorageVersionSet` + --> tests/pallet_ui/compare_unset_storage_version.rs:15:39 + | +15 | if Self::current_storage_version() != Self::on_chain_storage_version() { + | ------------------------------- ^^ -------------------------------- StorageVersion + | | + | NoStorageVersionSet diff --git a/frame/support/test/tests/runtime_metadata.rs b/frame/support/test/tests/runtime_metadata.rs index 8c04c785a..70ca307d4 100644 --- a/frame/support/test/tests/runtime_metadata.rs +++ b/frame/support/test/tests/runtime_metadata.rs @@ -218,5 +218,5 @@ fn runtime_metadata() { let rt = Runtime; let runtime_metadata = (&rt).runtime_metadata(); - assert_eq!(runtime_metadata, expected_runtime_metadata); + pretty_assertions::assert_eq!(runtime_metadata, expected_runtime_metadata); } diff --git a/frame/system/src/extensions/check_non_zero_sender.rs b/frame/system/src/extensions/check_non_zero_sender.rs index b0b6704fe..92eed60fc 100644 --- a/frame/system/src/extensions/check_non_zero_sender.rs +++ b/frame/system/src/extensions/check_non_zero_sender.rs @@ -17,7 +17,7 @@ use crate::Config; use codec::{Decode, Encode}; -use frame_support::dispatch::DispatchInfo; +use frame_support::{dispatch::DispatchInfo, DefaultNoBound}; use scale_info::TypeInfo; use sp_runtime::{ traits::{DispatchInfoOf, Dispatchable, SignedExtension}, @@ -28,9 +28,9 @@ use sp_runtime::{ use sp_std::{marker::PhantomData, prelude::*}; /// Check to ensure that the sender is not the zero address. -#[derive(Encode, Decode, Clone, Eq, PartialEq, TypeInfo)] +#[derive(Encode, Decode, DefaultNoBound, Clone, Eq, PartialEq, TypeInfo)] #[scale_info(skip_type_params(T))] -pub struct CheckNonZeroSender(PhantomData); +pub struct CheckNonZeroSender(PhantomData); impl sp_std::fmt::Debug for CheckNonZeroSender { #[cfg(feature = "std")] diff --git a/primitives/api/Cargo.toml b/primitives/api/Cargo.toml index 55e399346..1d140854f 100644 --- a/primitives/api/Cargo.toml +++ b/primitives/api/Cargo.toml @@ -55,4 +55,4 @@ std = [ # This sets the max logging level to `off` for `log`. disable-logging = ["log/max_level_off"] # Do not report the documentation in the metadata. -no-metadata-docs = [] +no-metadata-docs = ["sp-api-proc-macro/no-metadata-docs"] diff --git a/primitives/api/proc-macro/Cargo.toml b/primitives/api/proc-macro/Cargo.toml index 594c20e82..9d721950f 100644 --- a/primitives/api/proc-macro/Cargo.toml +++ b/primitives/api/proc-macro/Cargo.toml @@ -27,7 +27,8 @@ Inflector = "0.11.4" [dev-dependencies] assert_matches = "1.3.0" -# Required for the doc tests [features] +# Required for the doc tests default = ["std"] std = [] +no-metadata-docs = [] diff --git a/scripts/ci/gitlab/pipeline/test.yml b/scripts/ci/gitlab/pipeline/test.yml index ecfcf9ff3..52130f5b7 100644 --- a/scripts/ci/gitlab/pipeline/test.yml +++ b/scripts/ci/gitlab/pipeline/test.yml @@ -246,8 +246,8 @@ test-frame-support: script: - rusty-cachier snapshot create - cat /cargo_target_dir/debug/.fingerprint/memory_units-759eddf317490d2b/lib-memory_units.json || true - - time cargo test --verbose --locked -p frame-support-test --features=frame-feature-testing,no-metadata-docs --manifest-path ./frame/support/test/Cargo.toml --test pallet - - time cargo test --verbose --locked -p frame-support-test --features=frame-feature-testing,frame-feature-testing-2,no-metadata-docs --manifest-path ./frame/support/test/Cargo.toml --test pallet + - time cargo test --verbose --locked -p frame-support-test --features=frame-feature-testing,no-metadata-docs,try-runtime --manifest-path ./frame/support/test/Cargo.toml + - time cargo test --verbose --locked -p frame-support-test --features=frame-feature-testing,frame-feature-testing-2,no-metadata-docs,try-runtime --manifest-path ./frame/support/test/Cargo.toml - SUBSTRATE_TEST_TIMEOUT=1 time cargo test -p substrate-test-utils --release --verbose --locked -- --ignored timeout - cat /cargo_target_dir/debug/.fingerprint/memory_units-759eddf317490d2b/lib-memory_units.json || true - rusty-cachier cache upload From fb98ee1debf254b888ef20a2bd3ddedf9445c335 Mon Sep 17 00:00:00 2001 From: Liam Aharon Date: Fri, 5 May 2023 17:15:40 +1000 Subject: [PATCH 459/558] try-runtime-cli: 'instant' snapshots, threading refactor, better progress logs (#14057) * remote externalities refactor * remove redundant logs * use const for parallel requests * prefer functional * improve variable naming * handle requests error * use overlayedchanges * Revert "use overlayedchanges" This reverts commit c0ddb87a5abdd52207597f5df66cbbdf9d79badc. * Revert "Revert "use overlayedchanges"" This reverts commit 1d49362d9b999c045c8f970a0ab8b486bc47a90a. * Revert "Revert "Revert "use overlayedchanges""" This reverts commit 06df786488d94f249e9abccffac4af445f76e5a7. * backup/load raw storage values * test raw storage drain and restore * update snapshot tests * improve logs * clippy suggestions * address comments * fix example * fix test * clippy --- Cargo.lock | 62 +++ primitives/state-machine/src/testing.rs | 70 +++- primitives/state-machine/src/trie_backend.rs | 10 + utils/frame/remote-externalities/Cargo.toml | 2 + utils/frame/remote-externalities/src/lib.rs | 363 +++++++----------- .../remote-externalities/test_data/proxy_test | Bin 70206 -> 183839 bytes 6 files changed, 277 insertions(+), 230 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index edde0fed2..7c0c3da0c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1181,6 +1181,19 @@ dependencies = [ "crossbeam-utils", ] +[[package]] +name = "console" +version = "0.15.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c3d79fbe8970a77e3e34151cc13d3b3e248aa0faaecb9f6091fa07ebefe5ad60" +dependencies = [ + "encode_unicode", + "lazy_static", + "libc", + "unicode-width", + "windows-sys 0.42.0", +] + [[package]] name = "const-oid" version = "0.9.2" @@ -2034,6 +2047,12 @@ dependencies = [ "zeroize", ] +[[package]] +name = "encode_unicode" +version = "0.3.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a357d28ed41a50f9c765dbfe56cbc04a64e53e5fc58ba79fbc34c10ef3df831f" + [[package]] name = "enum-as-inner" version = "0.5.1" @@ -2507,6 +2526,7 @@ dependencies = [ "async-recursion", "frame-support", "futures", + "indicatif", "jsonrpsee", "log", "pallet-elections-phragmen", @@ -2515,6 +2535,7 @@ dependencies = [ "sp-core", "sp-io", "sp-runtime", + "spinners", "substrate-rpc-client", "tokio", "tracing-subscriber 0.3.16", @@ -3411,6 +3432,18 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8e04e2fd2b8188ea827b32ef11de88377086d690286ab35747ef7f9bf3ccb590" +[[package]] +name = "indicatif" +version = "0.17.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cef509aa9bc73864d6756f0d34d35504af3cf0844373afe9b8669a5b8005a729" +dependencies = [ + "console", + "number_prefix", + "portable-atomic", + "unicode-width", +] + [[package]] name = "inout" version = "0.1.3" @@ -4588,6 +4621,12 @@ dependencies = [ "libc", ] +[[package]] +name = "maplit" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3e2e65a1a2e43cfcb47a895c4c8b10d1f4a61097f9f254f183aee60cad9c651d" + [[package]] name = "match_cfg" version = "0.1.0" @@ -5522,6 +5561,12 @@ dependencies = [ "libc", ] +[[package]] +name = "number_prefix" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "830b246a0e5f20af87141b25c173cd1b609bd7779a4617d6ec582abaf90870f3" + [[package]] name = "object" version = "0.29.0" @@ -7617,6 +7662,12 @@ dependencies = [ "universal-hash 0.5.0", ] +[[package]] +name = "portable-atomic" +version = "0.3.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "26f6a7b87c2e435a3241addceeeff740ff8b7e76b74c13bf9acb17fa454ea00b" + [[package]] name = "ppv-lite86" version = "0.2.17" @@ -11245,6 +11296,17 @@ version = "0.9.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7dccf47db1b41fa1573ed27ccf5e08e3ca771cb994f776668c5ebda893b248fc" +[[package]] +name = "spinners" +version = "4.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "08615eea740067d9899969bc2891c68a19c315cb1f66640af9a9ecb91b13bcab" +dependencies = [ + "lazy_static", + "maplit", + "strum", +] + [[package]] name = "spki" version = "0.6.0" diff --git a/primitives/state-machine/src/testing.rs b/primitives/state-machine/src/testing.rs index 1921287a6..78fec43cd 100644 --- a/primitives/state-machine/src/testing.rs +++ b/primitives/state-machine/src/testing.rs @@ -27,7 +27,7 @@ use crate::{ StorageTransactionCache, StorageValue, TrieBackendBuilder, }; -use hash_db::Hasher; +use hash_db::{HashDB, Hasher}; use sp_core::{ offchain::testing::TestPersistentOffchainDB, storage::{ @@ -160,6 +160,34 @@ where self.extensions.register(ext); } + /// Sets raw storage key/values and a root. + /// + /// This can be used as a fast way to restore the storage state from a backup because the trie + /// does not need to be computed. + pub fn from_raw_snapshot(&mut self, raw_storage: Vec<(H::Out, Vec)>, storage_root: H::Out) { + for (k, v) in raw_storage { + self.backend.backend_storage_mut().emplace(k, hash_db::EMPTY_PREFIX, v); + } + self.backend.set_root(storage_root); + } + + /// Drains the underlying raw storage key/values and returns the root hash. + /// + /// Useful for backing up the storage in a format that can be quickly re-loaded. + /// + /// Note: This DB will be inoperable after this call. + pub fn into_raw_snapshot(mut self) -> (Vec<(H::Out, Vec)>, H::Out) { + let raw_key_values = self + .backend + .backend_storage_mut() + .drain() + .into_iter() + .map(|(k, v)| (k, v.0)) + .collect::)>>(); + + (raw_key_values, *self.backend.root()) + } + /// Return a new backend with all pending changes. /// /// In contrast to [`commit_all`](Self::commit_all) this will not panic if there are open @@ -362,6 +390,46 @@ mod tests { assert_eq!(H256::from_slice(ext.storage_root(Default::default()).as_slice()), root); } + #[test] + fn raw_storage_drain_and_restore() { + // Create a TestExternalities with some data in it. + let mut original_ext = + TestExternalities::::from((Default::default(), Default::default())); + original_ext.insert(b"doe".to_vec(), b"reindeer".to_vec()); + original_ext.insert(b"dog".to_vec(), b"puppy".to_vec()); + original_ext.insert(b"dogglesworth".to_vec(), b"cat".to_vec()); + let child_info = ChildInfo::new_default(&b"test_child"[..]); + original_ext.insert_child(child_info.clone(), b"cattytown".to_vec(), b"is_dark".to_vec()); + original_ext.insert_child(child_info.clone(), b"doggytown".to_vec(), b"is_sunny".to_vec()); + + // Drain the raw storage and root. + let root = *original_ext.backend.root(); + let (raw_storage, storage_root) = original_ext.into_raw_snapshot(); + + // Load the raw storage and root into a new TestExternalities. + let mut recovered_ext = + TestExternalities::::from((Default::default(), Default::default())); + recovered_ext.from_raw_snapshot(raw_storage, storage_root); + + // Check the storage root is the same as the original + assert_eq!(root, *recovered_ext.backend.root()); + + // Check the original storage key/values were recovered correctly + assert_eq!(recovered_ext.backend.storage(b"doe").unwrap(), Some(b"reindeer".to_vec())); + assert_eq!(recovered_ext.backend.storage(b"dog").unwrap(), Some(b"puppy".to_vec())); + assert_eq!(recovered_ext.backend.storage(b"dogglesworth").unwrap(), Some(b"cat".to_vec())); + + // Check the original child storage key/values were recovered correctly + assert_eq!( + recovered_ext.backend.child_storage(&child_info, b"cattytown").unwrap(), + Some(b"is_dark".to_vec()) + ); + assert_eq!( + recovered_ext.backend.child_storage(&child_info, b"doggytown").unwrap(), + Some(b"is_sunny".to_vec()) + ); + } + #[test] fn set_and_retrieve_code() { let mut ext = TestExternalities::::default(); diff --git a/primitives/state-machine/src/trie_backend.rs b/primitives/state-machine/src/trie_backend.rs index afbe6cbbe..abd58b383 100644 --- a/primitives/state-machine/src/trie_backend.rs +++ b/primitives/state-machine/src/trie_backend.rs @@ -242,11 +242,21 @@ where &self.essence } + /// Get backend storage reference. + pub fn backend_storage_mut(&mut self) -> &mut S { + self.essence.backend_storage_mut() + } + /// Get backend storage reference. pub fn backend_storage(&self) -> &S { self.essence.backend_storage() } + /// Set trie root. + pub fn set_root(&mut self, root: H::Out) { + self.essence.set_root(root) + } + /// Get trie root. pub fn root(&self) -> &H::Out { self.essence.root() diff --git a/utils/frame/remote-externalities/Cargo.toml b/utils/frame/remote-externalities/Cargo.toml index d3909af34..21b652014 100644 --- a/utils/frame/remote-externalities/Cargo.toml +++ b/utils/frame/remote-externalities/Cargo.toml @@ -24,6 +24,8 @@ tokio = { version = "1.22.0", features = ["macros", "rt-multi-thread"] } substrate-rpc-client = { path = "../rpc/client" } futures = "0.3" async-recursion = "1.0.4" +indicatif = "0.17.3" +spinners = "4.1.0" [dev-dependencies] frame-support = { version = "4.0.0-dev", path = "../../../frame/support" } diff --git a/utils/frame/remote-externalities/src/lib.rs b/utils/frame/remote-externalities/src/lib.rs index 283d2c280..01c673884 100644 --- a/utils/frame/remote-externalities/src/lib.rs +++ b/utils/frame/remote-externalities/src/lib.rs @@ -22,7 +22,7 @@ use async_recursion::async_recursion; use codec::{Decode, Encode}; -use futures::{channel::mpsc, stream::StreamExt}; +use indicatif::{ProgressBar, ProgressStyle}; use jsonrpsee::{ core::params::ArrayParams, http_client::{HttpClient, HttpClientBuilder}, @@ -36,17 +36,17 @@ use sp_core::{ well_known_keys::{is_default_child_storage_key, DEFAULT_CHILD_STORAGE_KEY_PREFIX}, ChildInfo, ChildType, PrefixedStorageKey, StorageData, StorageKey, }, + H256, }; pub use sp_io::TestExternalities; use sp_runtime::{traits::Block as BlockT, StateVersion}; +use spinners::{Spinner, Spinners}; use std::{ - cmp::{max, min}, + cmp::max, fs, - num::NonZeroUsize, ops::{Deref, DerefMut}, path::{Path, PathBuf}, - sync::Arc, - thread, + time::{Duration, Instant}, }; use substrate_rpc_client::{rpc_params, BatchRequestBuilder, ChainApi, ClientT, StateApi}; @@ -61,8 +61,8 @@ const DEFAULT_HTTP_ENDPOINT: &str = "https://rpc.polkadot.io:443"; struct Snapshot { state_version: StateVersion, block_hash: B::Hash, - top: TopKeyValues, - child: ChildKeyValues, + raw_storage: Vec<(H256, Vec)>, + storage_root: H256, } /// An externalities that acts exactly the same as [`sp_io::TestExternalities`] but has a few extra @@ -119,7 +119,7 @@ pub enum Transport { /// Use the `URI` to open a new WebSocket connection. Uri(String), /// Use HTTP connection. - RemoteClient(Arc), + RemoteClient(HttpClient), } impl Transport { @@ -130,13 +130,6 @@ impl Transport { } } - fn as_client_cloned(&self) -> Option> { - match self { - Self::RemoteClient(client) => Some(client.clone()), - _ => None, - } - } - // Build an HttpClient from a URI. async fn init(&mut self) -> Result<(), &'static str> { if let Self::Uri(uri) = self { @@ -166,7 +159,7 @@ impl Transport { "failed to build http client" })?; - *self = Self::RemoteClient(Arc::new(http_client)) + *self = Self::RemoteClient(http_client) } Ok(()) @@ -179,8 +172,8 @@ impl From for Transport { } } -impl From> for Transport { - fn from(client: Arc) -> Self { +impl From for Transport { + fn from(client: HttpClient) -> Self { Transport::RemoteClient(client) } } @@ -216,13 +209,6 @@ impl OnlineConfig { .expect("http client must have been initialized by now; qed.") } - /// Return a cloned rpc (http) client, suitable for being moved to threads. - fn rpc_client_cloned(&self) -> Arc { - self.transport - .as_client_cloned() - .expect("http client must have been initialized by now; qed.") - } - fn at_expected(&self) -> B::Hash { self.at.expect("block at must be initialized; qed") } @@ -327,31 +313,13 @@ where B::Hash: DeserializeOwned, B::Header: DeserializeOwned, { - const DEFAULT_PARALLELISM: usize = 4; + const PARALLEL_REQUESTS: usize = 4; const BATCH_SIZE_INCREASE_FACTOR: f32 = 1.10; const BATCH_SIZE_DECREASE_FACTOR: f32 = 0.50; const INITIAL_BATCH_SIZE: usize = 5000; // NOTE: increasing this value does not seem to impact speed all that much. const DEFAULT_KEY_DOWNLOAD_PAGE: u32 = 1000; - /// Get the number of threads to use. - /// Cap the number of threads. Performance improvement beyond a small number of threads is - /// negligible, and too many threads can create issues with the HttpClient. - fn threads() -> NonZeroUsize { - let avaliable = thread::available_parallelism() - .unwrap_or(NonZeroUsize::new(1usize).expect("1 is non-zero; qed")) - .get(); - assert!(avaliable > 0, "avaliable parallelism must be greater than 0"); - - let requested: usize = match std::env::var("TRY_RUNTIME_MAX_THREADS") { - Ok(n) => n.parse::().expect("TRY_RUNTIME_MAX_THREADS must be a number"), - Err(_) => Self::DEFAULT_PARALLELISM, - }; - assert!(requested > 0, "TRY_RUNTIME_MAX_THREADS must be greater than 0"); - return NonZeroUsize::new(min(requested, avaliable)) - .expect("requested and avaliable are non-zero; qed") - } - async fn rpc_get_storage( &self, key: StorageKey, @@ -455,7 +423,7 @@ where /// use std::sync::Arc; /// /// async fn example() { - /// let client = Arc::new(HttpClient::new()); + /// let client = HttpClient::new(); /// let payloads = vec![ /// ("some_method".to_string(), ArrayParams::new(vec![])), /// ("another_method".to_string(), ArrayParams::new(vec![])), @@ -471,9 +439,10 @@ where /// ``` #[async_recursion] async fn get_storage_data_dynamic_batch_size( - client: &Arc, + client: &HttpClient, payloads: Vec<(String, ArrayParams)>, batch_size: usize, + bar: &ProgressBar, ) -> Result>, String> { // All payloads have been processed if payloads.is_empty() { @@ -514,6 +483,7 @@ where client, payloads, max(1, (batch_size as f32 * Self::BATCH_SIZE_DECREASE_FACTOR) as usize), + bar, ) .await }, @@ -521,19 +491,22 @@ where // Collect the data from this batch let mut data: Vec> = vec![]; + let batch_response_len = batch_response.len(); for item in batch_response.into_iter() { match item { Ok(x) => data.push(x), Err(e) => return Err(e.message().to_string()), } } + bar.inc(batch_response_len as u64); // Return this data joined with the remaining keys - let payloads = payloads.iter().skip(batch_size).cloned().collect::>(); + let remaining_payloads = payloads.iter().skip(batch_size).cloned().collect::>(); let mut rest = Self::get_storage_data_dynamic_batch_size( client, - payloads, + remaining_payloads, max(batch_size + 1, (batch_size as f32 * Self::BATCH_SIZE_INCREASE_FACTOR) as usize), + bar, ) .await?; data.append(&mut rest); @@ -550,154 +523,91 @@ where at: B::Hash, pending_ext: &mut TestExternalities, ) -> Result, &'static str> { - let keys = self.rpc_get_keys_paged(prefix.clone(), at).await?; + let start = Instant::now(); + let mut sp = Spinner::with_timer(Spinners::Dots, "Scraping keys...".into()); + let keys = self + .rpc_get_keys_paged(prefix.clone(), at) + .await? + .into_iter() + .filter(|k| !is_default_child_storage_key(&k.0)) + .collect::>(); + sp.stop_with_message(format!( + "✅ Found {} keys ({:.2}s)", + keys.len(), + start.elapsed().as_secs_f32() + )); if keys.is_empty() { return Ok(Default::default()) } - let client = self.as_online().rpc_client_cloned(); - let threads = Self::threads().get(); - let thread_chunk_size = (keys.len() + threads - 1) / threads; + let client = self.as_online().rpc_client(); + let payloads = keys + .iter() + .map(|key| ("state_getStorage".to_string(), rpc_params!(key, at))) + .collect::>(); - log::info!( - target: LOG_TARGET, - "Querying a total of {} keys from prefix {:?}, splitting among {} threads, {} keys per thread", - keys.len(), - HexDisplay::from(&prefix), - threads, - thread_chunk_size, + let bar = ProgressBar::new(payloads.len() as u64); + bar.enable_steady_tick(Duration::from_secs(1)); + bar.set_message("Downloading key values".to_string()); + bar.set_style( + ProgressStyle::with_template( + "[{elapsed_precise}] {msg} {per_sec} [{wide_bar}] {pos}/{len} ({eta})", + ) + .unwrap() + .progress_chars("=>-"), ); + let payloads_chunked = payloads.chunks(&payloads.len() / Self::PARALLEL_REQUESTS); + let requests = payloads_chunked.map(|payload_chunk| { + Self::get_storage_data_dynamic_batch_size( + &client, + payload_chunk.to_vec(), + Self::INITIAL_BATCH_SIZE, + &bar, + ) + }); + // Execute the requests and move the Result outside. + let storage_data_result: Result, _> = + futures::future::join_all(requests).await.into_iter().collect(); + // Handle the Result. + let storage_data = match storage_data_result { + Ok(storage_data) => storage_data.into_iter().flatten().collect::>(), + Err(e) => { + log::error!(target: LOG_TARGET, "Error while getting storage data: {}", e); + return Err("Error while getting storage data") + }, + }; + bar.finish_with_message("✅ Downloaded key values"); + print!("\n"); - let mut handles = Vec::new(); - let keys_chunked: Vec> = - keys.chunks(thread_chunk_size).map(|s| s.into()).collect::>(); - - enum Message { - /// This thread completed the assigned work. - Terminated, - /// The thread produced the following batch response. - Batch(Vec<(Vec, Vec)>), - /// A request from the batch failed. - BatchFailed(String), - } - - let (tx, mut rx) = mpsc::unbounded::(); - - for thread_keys in keys_chunked { - let thread_sender = tx.clone(); - let thread_client = client.clone(); - let handle = std::thread::spawn(move || { - // Process the payloads in chunks so each thread can pass kvs back to the main - // thread to start inserting before all of the data has been queried from the node. - // Inserting data takes a very long time, so the earlier it can start the better. - let mut thread_key_values = vec![]; - let chunk_size = thread_keys.len() / 1; - for thread_keys_chunk in thread_keys.chunks(chunk_size) { - let mut thread_key_chunk_values = Vec::with_capacity(thread_keys_chunk.len()); - - let payloads = thread_keys_chunk - .iter() - .map(|key| ("state_getStorage".to_string(), rpc_params!(key, at))) - .collect::>(); - - let rt = tokio::runtime::Runtime::new().unwrap(); - let storage_data = match rt.block_on(Self::get_storage_data_dynamic_batch_size( - &thread_client, - payloads, - Self::INITIAL_BATCH_SIZE, - )) { - Ok(storage_data) => storage_data, - Err(e) => { - thread_sender.unbounded_send(Message::BatchFailed(e)).unwrap(); - return Default::default() - }, - }; - - // Check if we got responses for all submitted requests. - assert_eq!(thread_keys_chunk.len(), storage_data.len()); - - let mut batch_kv = Vec::with_capacity(thread_keys_chunk.len()); - for (key, maybe_value) in thread_keys_chunk.iter().zip(storage_data) { - match maybe_value { - Some(data) => { - thread_key_chunk_values.push((key.clone(), data.clone())); - batch_kv.push((key.clone().0, data.0)); - }, - None => { - log::warn!( - target: LOG_TARGET, - "key {:?} had none corresponding value.", - &key - ); - let data = StorageData(vec![]); - thread_key_chunk_values.push((key.clone(), data.clone())); - batch_kv.push((key.clone().0, data.0)); - }, - }; - } - - // Send this chunk to the main thread to start inserting. - thread_sender.unbounded_send(Message::Batch(batch_kv)).unwrap(); - thread_key_values.extend(thread_key_chunk_values); - } - - thread_sender.unbounded_send(Message::Terminated).unwrap(); - thread_key_values - }); - - handles.push(handle); - } + // Check if we got responses for all submitted requests. + assert_eq!(keys.len(), storage_data.len()); - // first, wait until all threads send a `Terminated` message, in the meantime populate - // `pending_ext`. - let mut terminated = 0usize; - let mut batch_failed = false; - let mut processed = 0usize; - loop { - match rx.next().await.unwrap() { - Message::Batch(kvs) => { - let kvs = kvs - .into_iter() - .filter(|(k, _)| !is_default_child_storage_key(k)) - .collect::>(); - processed += kvs.len(); - pending_ext.batch_insert(kvs); - log::info!( - target: LOG_TARGET, - "inserting keys progress = {:.0}% [{} / {}]", - (processed as f32 / keys.len() as f32) * 100f32, - processed, - keys.len(), - ); - }, - Message::BatchFailed(error) => { - log::error!(target: LOG_TARGET, "Batch processing failed: {:?}", error); - batch_failed = true; - break - }, - Message::Terminated => { - terminated += 1; - if terminated == handles.len() { - break - } + let key_values = keys + .iter() + .zip(storage_data) + .map(|(key, maybe_value)| match maybe_value { + Some(data) => (key.clone(), data), + None => { + log::warn!(target: LOG_TARGET, "key {:?} had none corresponding value.", &key); + let data = StorageData(vec![]); + (key.clone(), data) }, - } - } - - // Ensure all threads finished execution before returning. - let keys_and_values = - handles.into_iter().flat_map(|h| h.join().unwrap()).collect::>(); - - if batch_failed { - return Err("Batch failed.") - } + }) + .collect::>(); - Ok(keys_and_values) + let mut sp = Spinner::with_timer(Spinners::Dots, "Inserting keys into DB...".into()); + let start = Instant::now(); + pending_ext.batch_insert(key_values.clone().into_iter().map(|(k, v)| (k.0, v.0))); + sp.stop_with_message(format!( + "✅ Inserted keys into DB ({:.2}s)", + start.elapsed().as_secs_f32() + )); + Ok(key_values) } /// Get the values corresponding to `child_keys` at the given `prefixed_top_key`. pub(crate) async fn rpc_child_get_storage_paged( - client: &Arc, + client: &HttpClient, prefixed_top_key: &StorageKey, child_keys: Vec, at: B::Hash, @@ -718,10 +628,12 @@ where }) .collect::>(); + let bar = ProgressBar::new(payloads.len() as u64); let storage_data = match Self::get_storage_data_dynamic_batch_size( client, payloads, Self::INITIAL_BATCH_SIZE, + &bar, ) .await { @@ -814,19 +726,15 @@ where let at = self.as_online().at_expected(); - let arc_client = self.as_online().rpc_client_cloned(); + let client = self.as_online().rpc_client(); let mut child_kv = vec![]; for prefixed_top_key in child_roots { - let child_keys = Self::rpc_child_get_keys( - arc_client.as_ref(), - &prefixed_top_key, - StorageKey(vec![]), - at, - ) - .await?; + let child_keys = + Self::rpc_child_get_keys(&client, &prefixed_top_key, StorageKey(vec![]), at) + .await?; let child_kv_inner = - Self::rpc_child_get_storage_paged(&arc_client, &prefixed_top_key, child_keys, at) + Self::rpc_child_get_storage_paged(&client, &prefixed_top_key, child_keys, at) .await?; let prefixed_top_key = PrefixedStorageKey::new(prefixed_top_key.clone().0); @@ -873,9 +781,9 @@ where let elapsed = now.elapsed(); log::info!( target: LOG_TARGET, - "adding data for hashed prefix: {:?}, took {:?}s", + "adding data for hashed prefix: {:?}, took {:.2}s", HexDisplay::from(prefix), - elapsed.as_secs() + elapsed.as_secs_f32() ); keys_and_values.extend(additional_key_values); } @@ -970,18 +878,22 @@ where Default::default(), self.overwrite_state_version.unwrap_or(state_version), ); + + // Load data from the remote into `pending_ext`. let top_kv = self.load_top_remote(&mut pending_ext).await?; - let child_kv = self.load_child_remote(&top_kv, &mut pending_ext).await?; + self.load_child_remote(&top_kv, &mut pending_ext).await?; + // If we need to save a snapshot, save the raw storage and root hash to the snapshot. if let Some(path) = self.as_online().state_snapshot.clone().map(|c| c.path) { + let (raw_storage, storage_root) = pending_ext.into_raw_snapshot(); let snapshot = Snapshot:: { state_version, - top: top_kv, - child: child_kv, block_hash: self .as_online() .at .expect("set to `Some` in `init_remote_client`; must be called before; qed"), + raw_storage: raw_storage.clone(), + storage_root, }; let encoded = snapshot.encode(); log::info!( @@ -991,6 +903,15 @@ where path ); std::fs::write(path, encoded).map_err(|_| "fs::write failed")?; + + // pending_ext was consumed when creating the snapshot, need to reinitailize it + let mut pending_ext = TestExternalities::new_with_code_and_state( + Default::default(), + Default::default(), + self.overwrite_state_version.unwrap_or(state_version), + ); + pending_ext.from_raw_snapshot(raw_storage, storage_root); + return Ok(pending_ext) } Ok(pending_ext) @@ -1013,7 +934,9 @@ where &mut self, config: OfflineConfig, ) -> Result, &'static str> { - let Snapshot { block_hash, top, child, state_version } = + let mut sp = Spinner::with_timer(Spinners::Dots, "Loading snapshot...".into()); + let start = Instant::now(); + let Snapshot { block_hash, state_version, raw_storage, storage_root } = self.load_snapshot(config.state_snapshot.path.clone())?; let mut inner_ext = TestExternalities::new_with_code_and_state( @@ -1021,26 +944,8 @@ where Default::default(), self.overwrite_state_version.unwrap_or(state_version), ); - - info!(target: LOG_TARGET, "injecting a total of {} top keys", top.len()); - let top = top - .into_iter() - .filter(|(k, _)| !is_default_child_storage_key(k.as_ref())) - .map(|(k, v)| (k.0, v.0)) - .collect::>(); - inner_ext.batch_insert(top); - - info!( - target: LOG_TARGET, - "injecting a total of {} child keys", - child.iter().flat_map(|(_, kv)| kv).count() - ); - - for (info, key_values) in child { - for (k, v) in key_values { - inner_ext.insert_child(info.clone(), k.0, v.0); - } - } + inner_ext.from_raw_snapshot(raw_storage, storage_root); + sp.stop_with_message(format!("✅ Loaded snapshot ({:.2}s)", start.elapsed().as_secs_f32())); Ok(RemoteExternalities { inner_ext, block_hash }) } @@ -1156,7 +1061,7 @@ mod test_prelude { mod tests { use super::test_prelude::*; - #[tokio::test(flavor = "multi_thread")] + #[tokio::test] async fn can_load_state_snapshot() { init_logger(); Builder::::new() @@ -1169,7 +1074,7 @@ mod tests { .execute_with(|| {}); } - #[tokio::test(flavor = "multi_thread")] + #[tokio::test] async fn can_exclude_from_snapshot() { init_logger(); @@ -1205,7 +1110,7 @@ mod remote_tests { use super::test_prelude::*; use std::os::unix::fs::MetadataExt; - #[tokio::test(flavor = "multi_thread")] + #[tokio::test] async fn state_version_is_kept_and_can_be_altered() { const CACHE: &'static str = "state_version_is_kept_and_can_be_altered"; init_logger(); @@ -1246,7 +1151,7 @@ mod remote_tests { assert_eq!(cached_ext.state_version, other); } - #[tokio::test(flavor = "multi_thread")] + #[tokio::test] async fn snapshot_block_hash_works() { const CACHE: &'static str = "snapshot_block_hash_works"; init_logger(); @@ -1273,7 +1178,7 @@ mod remote_tests { assert_eq!(ext.block_hash, cached_ext.block_hash); } - #[tokio::test(flavor = "multi_thread")] + #[tokio::test] async fn offline_else_online_works() { const CACHE: &'static str = "offline_else_online_works_data"; init_logger(); @@ -1318,7 +1223,7 @@ mod remote_tests { std::fs::remove_file(to_delete[0].path()).unwrap(); } - #[tokio::test(flavor = "multi_thread")] + #[tokio::test] async fn can_build_one_small_pallet() { init_logger(); Builder::::new() @@ -1333,7 +1238,7 @@ mod remote_tests { .execute_with(|| {}); } - #[tokio::test(flavor = "multi_thread")] + #[tokio::test] async fn can_build_few_pallet() { init_logger(); Builder::::new() @@ -1373,7 +1278,7 @@ mod remote_tests { .collect::>(); let snap: Snapshot = Builder::::new().load_snapshot(CACHE.into()).unwrap(); - assert!(matches!(snap, Snapshot { top, child, .. } if top.len() > 0 && child.len() == 0)); + assert!(matches!(snap, Snapshot { raw_storage, .. } if raw_storage.len() > 0)); assert!(to_delete.len() == 1); let to_delete = to_delete.first().unwrap(); @@ -1381,7 +1286,7 @@ mod remote_tests { std::fs::remove_file(to_delete.path()).unwrap(); } - #[tokio::test(flavor = "multi_thread")] + #[tokio::test] async fn can_create_child_snapshot() { const CACHE: &'static str = "can_create_child_snapshot"; init_logger(); @@ -1405,7 +1310,7 @@ mod remote_tests { .collect::>(); let snap: Snapshot = Builder::::new().load_snapshot(CACHE.into()).unwrap(); - assert!(matches!(snap, Snapshot { top, child, .. } if top.len() > 0 && child.len() > 0)); + assert!(matches!(snap, Snapshot { raw_storage, .. } if raw_storage.len() > 0)); assert!(to_delete.len() == 1); let to_delete = to_delete.first().unwrap(); @@ -1413,7 +1318,7 @@ mod remote_tests { std::fs::remove_file(to_delete.path()).unwrap(); } - #[tokio::test(flavor = "multi_thread")] + #[tokio::test] async fn can_build_big_pallet() { if std::option_env!("TEST_WS").is_none() { return @@ -1432,7 +1337,7 @@ mod remote_tests { .execute_with(|| {}); } - #[tokio::test(flavor = "multi_thread")] + #[tokio::test] async fn can_fetch_all() { if std::option_env!("TEST_WS").is_none() { return diff --git a/utils/frame/remote-externalities/test_data/proxy_test b/utils/frame/remote-externalities/test_data/proxy_test index 6673bd6765ad8df2a9e74f210d8795acfeb54a30..f749531a8a9d767b14e1869bbfa16c390cacfa68 100644 GIT binary patch literal 183839 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�rgqyB12J)SC29x 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π-drTw$itePL8?O_e4^(}1AC4qhq7AL$G)=h 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≪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

44PKZDy21+0e#13*J^7a~5(h|1LMC;b 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≤2SZcdgm^Le@~{46&tnSsR`!esmpmh2!ixJ3 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%0AS)>pp5TfMYWy z+t$~xP!lEAins;Ej{XdUctEYt>UvIx?gHZpbc@Kexuhv%ufM`qNoQ*YhQq=^9$r^% zzru?N8h>wQOTU9ardv>rn@~UH*F?}%(z^(ajeU!JyDueJz4vjrLY(ZZ(cyNbfR(?a zGBG{8(aL%~>oD}2_ssf$pt<`W1dox_H))v7ptBCl5&H|d*7w|M&~|q?fNYkuB!s<( zzG&9%N6z|3lui6TFvX85P}cY{!$__Jjsr4Lw!-oh5=Dg>gnPgi+A812UM5;|FmD5h zqwo!ARSYKXFM6JxC|O)PO;{|)d<+qu%swwGFF7(MP+$+9yjdXbJP&x^p^gb@+1ig$ z^)av7DVnFK*un>*m~!mydU(;i*m*T~B?H)bkpqY2!isIy7`U*pFLWw~&}Aq_8ggvz zLTWwbJ?3%X6@3j>I;?5${(X~bCo0x3FfB#{iein(U?k>v)r3y!l4tXCXqXh zp{b@3&sB3!!Eb#O∓|K{_GMY7wcZ&NYN@E4saWZ_3#3kfmv!*p#Gsxf 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 JWU_6^b^so%Zm|FW literal 70206 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 V3ArEafJTFa^6EoE{wI_h?O!-)7>WP@ From d712bd1c585f321f35c3ab19b9a0674b075c432b Mon Sep 17 00:00:00 2001 From: Brian Anderson Date: Fri, 5 May 2023 02:10:34 -0600 Subject: [PATCH 460/558] Upgrade wasm-opt to 0.112.0 (#13574) Co-authored-by: parity-processbot <> --- Cargo.lock | 13 ++++++------- utils/wasm-builder/Cargo.toml | 2 +- 2 files changed, 7 insertions(+), 8 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 7c0c3da0c..b8e42c103 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -12754,9 +12754,9 @@ dependencies = [ [[package]] name = "wasm-opt" -version = "0.111.0" +version = "0.112.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "84a303793cbc01fb96551badfc7367db6007396bba6bac97936b3c8b6f7fdb41" +checksum = "87fef6d0d508f08334e0ab0e6877feb4c0ecb3956bcf2cb950699b22fedf3e9c" dependencies = [ "anyhow", "libc", @@ -12770,9 +12770,9 @@ dependencies = [ [[package]] name = "wasm-opt-cxx-sys" -version = "0.111.0" +version = "0.112.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d9c9deb56f8a9f2ec177b3bd642a8205621835944ed5da55f2388ef216aca5a4" +checksum = "bc816bbc1596c8f2e8127e137a760c798023ef3d378f2ae51f0f1840e2dfa445" dependencies = [ "anyhow", "cxx", @@ -12782,15 +12782,14 @@ dependencies = [ [[package]] name = "wasm-opt-sys" -version = "0.111.0" +version = "0.112.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4432e28b542738a9776cedf92e8a99d8991c7b4667ee2c7ccddfb479dd2856a7" +checksum = "40199e4f68ef1071b3c6d0bd8026a12b481865d4b9e49c156932ea9a6234dd14" dependencies = [ "anyhow", "cc", "cxx", "cxx-build", - "regex", ] [[package]] diff --git a/utils/wasm-builder/Cargo.toml b/utils/wasm-builder/Cargo.toml index 7f356294e..5228f26d5 100644 --- a/utils/wasm-builder/Cargo.toml +++ b/utils/wasm-builder/Cargo.toml @@ -22,4 +22,4 @@ toml = "0.7.3" walkdir = "2.3.2" sp-maybe-compressed-blob = { version = "4.1.0-dev", path = "../../primitives/maybe-compressed-blob" } filetime = "0.2.16" -wasm-opt = "0.111" \ No newline at end of file +wasm-opt = "0.112" \ No newline at end of file From 9ee70a18adc29e70898936236ecbd4dea271bb7d Mon Sep 17 00:00:00 2001 From: Oleg Plakida <112385193+oleg-plakida@users.noreply.github.com> Date: Fri, 5 May 2023 13:16:52 +0100 Subject: [PATCH 461/558] Add flawky tests report (#13654) --- .config/nextest.toml | 6 +++--- scripts/ci/gitlab/pipeline/test.yml | 22 ++++++++++++++++++++++ 2 files changed, 25 insertions(+), 3 deletions(-) diff --git a/.config/nextest.toml b/.config/nextest.toml index c111b0abe..eb0ed09ca 100644 --- a/.config/nextest.toml +++ b/.config/nextest.toml @@ -16,7 +16,7 @@ dir = "target/nextest" # * retries = 3 # * retries = { backoff = "fixed", count = 2, delay = "1s" } # * retries = { backoff = "exponential", count = 10, delay = "1s", jitter = true, max-delay = "10s" } -retries = 0 +retries = 5 # The number of threads to run tests with. Supported values are either an integer or # the string "num-cpus". Can be overridden through the `--test-threads` option. @@ -89,12 +89,12 @@ leak-timeout = "100ms" # Output a JUnit report into the given file inside 'store.dir/'. # If unspecified, JUnit is not written out. -# path = "junit.xml" +path = "junit.xml" # The name of the top-level "report" element in JUnit report. If aggregating # reports across different test runs, it may be useful to provide separate names # for each report. -report-name = "nextest-run" +report-name = "substrate" # Whether standard output and standard error for passing tests should be stored in the JUnit report. # Output is stored in the and elements of the element. diff --git a/scripts/ci/gitlab/pipeline/test.yml b/scripts/ci/gitlab/pipeline/test.yml index 52130f5b7..29f21bfe5 100644 --- a/scripts/ci/gitlab/pipeline/test.yml +++ b/scripts/ci/gitlab/pipeline/test.yml @@ -228,6 +228,28 @@ test-linux-stable: --partition count:${CI_NODE_INDEX}/${CI_NODE_TOTAL} # we need to update cache only from one job - if [ ${CI_NODE_INDEX} == 1 ]; then rusty-cachier cache upload; fi + artifacts: + when: always + paths: + - target/nextest/default/junit.xml + reports: + junit: target/nextest/default/junit.xml + +test-linux-stable-upload-test-results: + stage: test + needs: + - job: test-linux-stable + artifacts: true + extends: + - .docker-env + - .test-refs + script: + - cat target/nextest/default/junit.xml | xq . > target/nextest/default/junit.json + - "curl -v -XPOST --http1.1 + -u ${ELASTIC_USERNAME}:${ELASTIC_PASSWORD} + https://elasticsearch.parity-build.parity.io/unit-tests/_doc/${CI_JOB_ID} + -H 'Content-Type: application/json' + -d @target/nextest/default/junit.json" test-frame-support: stage: test From 1a6fc1e1050901cd6fd3b4e76a282cfc06bed3a2 Mon Sep 17 00:00:00 2001 From: juangirini Date: Fri, 5 May 2023 14:39:43 +0200 Subject: [PATCH 462/558] Update codeowners file (#14078) * update codeowners file * reorganise codeowners file * Update docs/CODEOWNERS Co-authored-by: Kian Paimani <5588131+kianenigma@users.noreply.github.com> * add nfts and uniques ownership * sort codeowners alphabetically --------- Co-authored-by: Kian Paimani <5588131+kianenigma@users.noreply.github.com> --- docs/CODEOWNERS | 38 +++++++++++++++++--------------------- 1 file changed, 17 insertions(+), 21 deletions(-) diff --git a/docs/CODEOWNERS b/docs/CODEOWNERS index 95ee8cc66..0f45a593d 100644 --- a/docs/CODEOWNERS +++ b/docs/CODEOWNERS @@ -19,54 +19,50 @@ # - The latest matching rule, if multiple, takes precedence. # CI -/scripts/ci/ @paritytech/ci /.github/ @paritytech/ci /.gitlab-ci.yml @paritytech/ci - -# Sandboxing capability of Substrate Runtime -/primitives/sandbox/ @pepyakin @koute +/scripts/ci/ @paritytech/ci # WASM executor, low-level client <-> WASM interface and other WASM-related code -/client/executor/ @koute /client/allocator/ @koute -/primitives/wasm-interface/ @koute -/primitives/runtime-interface/ @koute +/client/executor/ @koute /primitives/panic-handler/ @koute +/primitives/runtime-interface/ @koute +/primitives/wasm-interface/ @koute /utils/wasm-builder/ @koute # Systems-related bits and bobs on the client side /client/sysinfo/ @koute /client/tracing/ @koute +# FRAME +/frame/ @paritytech/frame-coders +/frame/nfts/ @jsidorenko +/frame/state-trie-migration/ @paritytech/frame-coders @cheme +/frame/uniques/ @jsidorenko + # GRANDPA, BABE, consensus stuff -/frame/babe/ @andresilva -/frame/grandpa/ @andresilva -/client/consensus/grandpa/ @andresilva /client/consensus/babe/ @andresilva -/client/consensus/slots/ @andresilva +/client/consensus/grandpa/ @andresilva /client/consensus/pow/ @sorpaas +/client/consensus/slots/ @andresilva +/frame/babe/ @andresilva +/frame/grandpa/ @andresilva /primitives/consensus/pow/ @sorpaas # BEEFY, MMR -/client/beefy/ @acatangiu /frame/beefy/ @acatangiu /frame/beefy-mmr/ @acatangiu /frame/merkle-mountain-range/ @acatangiu -/primitives/beefy/ @acatangiu /primitives/merkle-mountain-range/ @acatangiu # Contracts /frame/contracts/ @athei # NPoS and election +/frame/election-provider-multi-phase/ @paritytech/staking-core /frame/election-provider-support/ @paritytech/staking-core -/frame/staking/ @paritytech/staking-core -/frame/nomination-pools/ @paritytech/staking-core /frame/elections-phragmen/ @paritytech/staking-core +/frame/nomination-pools/ @paritytech/staking-core +/frame/staking/ @paritytech/staking-core /primitives/npos-elections/ @paritytech/staking-core - -# Fixed point arithmetic -/primitives/sp-arithmetic/ @kianenigma - -# Transaction weight stuff -/frame/support/src/weights.rs @shawntabrizi From bc8a35079546efd12673294fc66b0143a1c5f468 Mon Sep 17 00:00:00 2001 From: Mira Ressel Date: Fri, 5 May 2023 16:00:41 +0200 Subject: [PATCH 463/558] Bump ci-linux to rust 1.69 (#14060) * Pin ci-linux image for rust 1.69 * Update ui tests for rust 1.69 * Address new rust 1.69 clippy lints * `derive_hash_xor_eq` has been renamed to `derived_hash_with_manual_eq` * The new `extra-unused-type-parameters` complains about a bunch of callsites where extraneous type parameters are used for consistency with other functions. --- .cargo/config.toml | 1 + .gitlab-ci.yml | 2 +- .../network/sync/src/block_request_handler.rs | 2 +- .../network/sync/src/state_request_handler.rs | 2 +- .../tests/benchmark_ui/invalid_origin.stderr | 2 +- .../undefined_inherent_part.stderr | 25 ++++++++------ .../undefined_validate_unsigned_part.stderr | 33 +++++++++++-------- .../call_weight_inherited_invalid4.stderr | 4 +-- .../type_value_forgotten_where_clause.stderr | 6 ++-- .../ui/impl_incorrect_method_signature.stderr | 4 +-- 10 files changed, 46 insertions(+), 35 deletions(-) diff --git a/.cargo/config.toml b/.cargo/config.toml index 66b28b348..e29275804 100644 --- a/.cargo/config.toml +++ b/.cargo/config.toml @@ -29,4 +29,5 @@ rustflags = [ "-Aclippy::needless_option_as_deref", # false positives "-Aclippy::derivable_impls", # false positives "-Aclippy::stable_sort_primitive", # prefer stable sort + "-Aclippy::extra-unused-type-parameters" # stylistic ] diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index cc950865c..39ac5204f 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -48,7 +48,7 @@ variables: CARGO_INCREMENTAL: 0 DOCKER_OS: "debian:bullseye" ARCH: "x86_64" - CI_IMAGE: "paritytech/ci-linux:production" + CI_IMAGE: "paritytech/ci-linux@sha256:f6cdc1e289e40f3eb1f93efb11f961d4f0e51f4c2adf80b31300ab5b35ef1386" # staging 2023-05-02 BUILDAH_IMAGE: "quay.io/buildah/stable:v1.29" BUILDAH_COMMAND: "buildah --storage-driver overlay2" RELENG_SCRIPTS_BRANCH: "master" diff --git a/client/network/sync/src/block_request_handler.rs b/client/network/sync/src/block_request_handler.rs index ece565aad..256c0ad38 100644 --- a/client/network/sync/src/block_request_handler.rs +++ b/client/network/sync/src/block_request_handler.rs @@ -110,7 +110,7 @@ struct SeenRequestsKey { support_multiple_justifications: bool, } -#[allow(clippy::derive_hash_xor_eq)] +#[allow(clippy::derived_hash_with_manual_eq)] impl Hash for SeenRequestsKey { fn hash(&self, state: &mut H) { self.peer.hash(state); diff --git a/client/network/sync/src/state_request_handler.rs b/client/network/sync/src/state_request_handler.rs index 0ce2c541b..93597453a 100644 --- a/client/network/sync/src/state_request_handler.rs +++ b/client/network/sync/src/state_request_handler.rs @@ -94,7 +94,7 @@ struct SeenRequestsKey { start: Vec>, } -#[allow(clippy::derive_hash_xor_eq)] +#[allow(clippy::derived_hash_with_manual_eq)] impl Hash for SeenRequestsKey { fn hash(&self, state: &mut H) { self.peer.hash(state); diff --git a/frame/support/test/tests/benchmark_ui/invalid_origin.stderr b/frame/support/test/tests/benchmark_ui/invalid_origin.stderr index ab8499d99..1818442b6 100644 --- a/frame/support/test/tests/benchmark_ui/invalid_origin.stderr +++ b/frame/support/test/tests/benchmark_ui/invalid_origin.stderr @@ -1,4 +1,4 @@ -error[E0599]: no variant or associated item named `new_call_variant_thing` found for enum `Call` in the current scope +error[E0599]: no variant or associated item named `new_call_variant_thing` found for enum `frame_support_test::Call` in the current scope --> tests/benchmark_ui/invalid_origin.rs:6:1 | 6 | #[benchmarks] diff --git a/frame/support/test/tests/construct_runtime_ui/undefined_inherent_part.stderr b/frame/support/test/tests/construct_runtime_ui/undefined_inherent_part.stderr index 180137913..a068cab4c 100644 --- a/frame/support/test/tests/construct_runtime_ui/undefined_inherent_part.stderr +++ b/frame/support/test/tests/construct_runtime_ui/undefined_inherent_part.stderr @@ -15,13 +15,14 @@ error: `Pallet` does not have #[pallet::inherent] defined, perhaps you should re | = note: this error originates in the macro `pallet::__substrate_inherent_check::is_inherent_part_defined` which comes from the expansion of the macro `construct_runtime` (in Nightly builds, run with -Z macro-backtrace for more info) -error[E0599]: no function or associated item named `create_inherent` found for struct `Pallet` in the current scope +error[E0599]: no function or associated item named `create_inherent` found for struct `pallet::Pallet` in the current scope --> tests/construct_runtime_ui/undefined_inherent_part.rs:49:1 | 11 | pub struct Pallet(_); | -------------------- function or associated item `create_inherent` not found for this struct ... -49 | / construct_runtime! { +49 | construct_runtime! { + | _^ 50 | | pub struct Runtime where 51 | | Block = Block, 52 | | NodeBlock = Block, @@ -35,13 +36,14 @@ error[E0599]: no function or associated item named `create_inherent` found for s candidate #1: `ProvideInherent` = note: this error originates in the macro `construct_runtime` (in Nightly builds, run with -Z macro-backtrace for more info) -error[E0599]: no function or associated item named `is_inherent` found for struct `Pallet` in the current scope +error[E0599]: no function or associated item named `is_inherent` found for struct `pallet::Pallet` in the current scope --> tests/construct_runtime_ui/undefined_inherent_part.rs:49:1 | 11 | pub struct Pallet(_); | -------------------- function or associated item `is_inherent` not found for this struct ... -49 | / construct_runtime! { +49 | construct_runtime! { + | _^ 50 | | pub struct Runtime where 51 | | Block = Block, 52 | | NodeBlock = Block, @@ -55,13 +57,14 @@ error[E0599]: no function or associated item named `is_inherent` found for struc candidate #1: `ProvideInherent` = note: this error originates in the macro `construct_runtime` (in Nightly builds, run with -Z macro-backtrace for more info) -error[E0599]: no function or associated item named `check_inherent` found for struct `Pallet` in the current scope +error[E0599]: no function or associated item named `check_inherent` found for struct `pallet::Pallet` in the current scope --> tests/construct_runtime_ui/undefined_inherent_part.rs:49:1 | 11 | pub struct Pallet(_); | -------------------- function or associated item `check_inherent` not found for this struct ... -49 | / construct_runtime! { +49 | construct_runtime! { + | _^ 50 | | pub struct Runtime where 51 | | Block = Block, 52 | | NodeBlock = Block, @@ -75,13 +78,14 @@ error[E0599]: no function or associated item named `check_inherent` found for st candidate #1: `ProvideInherent` = note: this error originates in the macro `construct_runtime` (in Nightly builds, run with -Z macro-backtrace for more info) -error[E0599]: no associated item named `INHERENT_IDENTIFIER` found for struct `Pallet` in the current scope +error[E0599]: no associated item named `INHERENT_IDENTIFIER` found for struct `pallet::Pallet` in the current scope --> tests/construct_runtime_ui/undefined_inherent_part.rs:49:1 | 11 | pub struct Pallet(_); | -------------------- associated item `INHERENT_IDENTIFIER` not found for this struct ... -49 | / construct_runtime! { +49 | construct_runtime! { + | _^ 50 | | pub struct Runtime where 51 | | Block = Block, 52 | | NodeBlock = Block, @@ -95,13 +99,14 @@ error[E0599]: no associated item named `INHERENT_IDENTIFIER` found for struct `P candidate #1: `ProvideInherent` = note: this error originates in the macro `construct_runtime` (in Nightly builds, run with -Z macro-backtrace for more info) -error[E0599]: no function or associated item named `is_inherent_required` found for struct `Pallet` in the current scope +error[E0599]: no function or associated item named `is_inherent_required` found for struct `pallet::Pallet` in the current scope --> tests/construct_runtime_ui/undefined_inherent_part.rs:49:1 | 11 | pub struct Pallet(_); | -------------------- function or associated item `is_inherent_required` not found for this struct ... -49 | / construct_runtime! { +49 | construct_runtime! { + | _^ 50 | | pub struct Runtime where 51 | | Block = Block, 52 | | NodeBlock = Block, diff --git a/frame/support/test/tests/construct_runtime_ui/undefined_validate_unsigned_part.stderr b/frame/support/test/tests/construct_runtime_ui/undefined_validate_unsigned_part.stderr index fa52ce227..84f1e54d5 100644 --- a/frame/support/test/tests/construct_runtime_ui/undefined_validate_unsigned_part.stderr +++ b/frame/support/test/tests/construct_runtime_ui/undefined_validate_unsigned_part.stderr @@ -18,24 +18,28 @@ error: `Pallet` does not have #[pallet::validate_unsigned] defined, perhaps you error[E0599]: no variant or associated item named `Pallet` found for enum `RuntimeCall` in the current scope --> tests/construct_runtime_ui/undefined_validate_unsigned_part.rs:56:3 | -49 | / construct_runtime! { -50 | | pub struct Runtime where -51 | | Block = Block, -52 | | NodeBlock = Block, -... | -56 | | Pallet: pallet::{Pallet, ValidateUnsigned}, - | | ^^^^^^ variant or associated item not found in `RuntimeCall` -57 | | } -58 | | } - | |_- variant or associated item `Pallet` not found for this enum +49 | // construct_runtime! { +50 | || pub struct Runtime where +51 | || Block = Block, +52 | || NodeBlock = Block, +... || +55 | || System: frame_system::{Pallet, Call, Storage, Config, Event}, +56 | || Pallet: pallet::{Pallet, ValidateUnsigned}, + | || -^^^^^^ variant or associated item not found in `RuntimeCall` + | ||________| + | | +57 | | } +58 | | } + | |__- variant or associated item `Pallet` not found for this enum -error[E0599]: no function or associated item named `pre_dispatch` found for struct `Pallet` in the current scope +error[E0599]: no function or associated item named `pre_dispatch` found for struct `pallet::Pallet` in the current scope --> tests/construct_runtime_ui/undefined_validate_unsigned_part.rs:49:1 | 11 | pub struct Pallet(_); | -------------------- function or associated item `pre_dispatch` not found for this struct ... -49 | / construct_runtime! { +49 | construct_runtime! { + | _^ 50 | | pub struct Runtime where 51 | | Block = Block, 52 | | NodeBlock = Block, @@ -50,13 +54,14 @@ error[E0599]: no function or associated item named `pre_dispatch` found for stru candidate #2: `ValidateUnsigned` = note: this error originates in the macro `construct_runtime` (in Nightly builds, run with -Z macro-backtrace for more info) -error[E0599]: no function or associated item named `validate_unsigned` found for struct `Pallet` in the current scope +error[E0599]: no function or associated item named `validate_unsigned` found for struct `pallet::Pallet` in the current scope --> tests/construct_runtime_ui/undefined_validate_unsigned_part.rs:49:1 | 11 | pub struct Pallet(_); | -------------------- function or associated item `validate_unsigned` not found for this struct ... -49 | / construct_runtime! { +49 | construct_runtime! { + | _^ 50 | | pub struct Runtime where 51 | | Block = Block, 52 | | NodeBlock = Block, diff --git a/frame/support/test/tests/pallet_ui/call_weight_inherited_invalid4.stderr b/frame/support/test/tests/pallet_ui/call_weight_inherited_invalid4.stderr index 46872fc7c..fbde5c691 100644 --- a/frame/support/test/tests/pallet_ui/call_weight_inherited_invalid4.stderr +++ b/frame/support/test/tests/pallet_ui/call_weight_inherited_invalid4.stderr @@ -1,10 +1,10 @@ -error[E0599]: no function or associated item named `foo` found for associated type `::WeightInfo` in the current scope +error[E0599]: no function or associated item named `foo` found for associated type `::WeightInfo` in the current scope --> tests/pallet_ui/call_weight_inherited_invalid4.rs:24:10 | 24 | pub fn foo(_: OriginFor) -> DispatchResult { | ^^^ function or associated item not found in `::WeightInfo` -error[E0599]: no function or associated item named `foo` found for associated type `::WeightInfo` in the current scope +error[E0599]: no function or associated item named `foo` found for associated type `::WeightInfo` in the current scope --> tests/pallet_ui/call_weight_inherited_invalid4.rs:45:10 | 45 | pub fn foo(_: OriginFor) -> DispatchResult { diff --git a/frame/support/test/tests/pallet_ui/type_value_forgotten_where_clause.stderr b/frame/support/test/tests/pallet_ui/type_value_forgotten_where_clause.stderr index 4b4c78b14..d955960c3 100644 --- a/frame/support/test/tests/pallet_ui/type_value_forgotten_where_clause.stderr +++ b/frame/support/test/tests/pallet_ui/type_value_forgotten_where_clause.stderr @@ -8,7 +8,7 @@ note: required by a bound in `pallet::Config` --> tests/pallet_ui/type_value_forgotten_where_clause.rs:8:51 | 7 | pub trait Config: frame_system::Config - | ------ required by a bound in this + | ------ required by a bound in this trait 8 | where ::AccountId: From | ^^^^^^^^^ required by this bound in `Config` help: consider further restricting the associated type @@ -26,7 +26,7 @@ note: required by a bound in `pallet::Config` --> tests/pallet_ui/type_value_forgotten_where_clause.rs:8:51 | 7 | pub trait Config: frame_system::Config - | ------ required by a bound in this + | ------ required by a bound in this trait 8 | where ::AccountId: From | ^^^^^^^^^ required by this bound in `Config` help: consider further restricting the associated type @@ -44,7 +44,7 @@ note: required by a bound in `pallet::Config` --> tests/pallet_ui/type_value_forgotten_where_clause.rs:8:51 | 7 | pub trait Config: frame_system::Config - | ------ required by a bound in this + | ------ required by a bound in this trait 8 | where ::AccountId: From | ^^^^^^^^^ required by this bound in `Config` help: consider further restricting the associated type diff --git a/primitives/api/test/tests/ui/impl_incorrect_method_signature.stderr b/primitives/api/test/tests/ui/impl_incorrect_method_signature.stderr index 5b3624822..ef8258207 100644 --- a/primitives/api/test/tests/ui/impl_incorrect_method_signature.stderr +++ b/primitives/api/test/tests/ui/impl_incorrect_method_signature.stderr @@ -4,7 +4,7 @@ error[E0053]: method `test` has an incompatible type for trait 19 | fn test(data: String) {} | ^^^^^^ | | - | expected `u64`, found struct `String` + | expected `u64`, found `std::string::String` | help: change the parameter type to match the trait: `u64` | note: type in trait @@ -21,7 +21,7 @@ error[E0308]: mismatched types 17 | / sp_api::impl_runtime_apis! { 18 | | impl self::Api for Runtime { 19 | | fn test(data: String) {} - | | ^^^^ expected `u64`, found struct `String` + | | ^^^^ expected `u64`, found `String` 20 | | } ... | 32 | | } From 1073f458389c31b434f1302a90242e28ca3f8f09 Mon Sep 17 00:00:00 2001 From: Sasha Gryaznov Date: Fri, 5 May 2023 17:34:21 +0300 Subject: [PATCH 464/558] add error logging to call_runtime (#14082) --- frame/contracts/src/wasm/runtime.rs | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/frame/contracts/src/wasm/runtime.rs b/frame/contracts/src/wasm/runtime.rs index 330bf19d6..ae02e5bad 100644 --- a/frame/contracts/src/wasm/runtime.rs +++ b/frame/contracts/src/wasm/runtime.rs @@ -1235,6 +1235,10 @@ pub mod env { out_len_ptr: u32, ) -> Result { let charged = ctx.charge_gas(RuntimeCosts::TakeStorage(ctx.ext.max_value_size()))?; + ensure!( + key_len <= <::T as Config>::MaxStorageKeyLen::get(), + Error::::DecodingFailed + ); let key = ctx.read_sandbox_memory(memory, key_ptr, key_len)?; if let crate::storage::WriteOutcome::Taken(value) = ctx.ext.set_storage( &Key::::try_from_var(key).map_err(|_| Error::::DecodingFailed)?, @@ -2608,7 +2612,13 @@ pub mod env { ctx.adjust_gas(charged, RuntimeCosts::CallRuntime(actual_weight)); match result { Ok(_) => Ok(ReturnCode::Success), - Err(_) => Ok(ReturnCode::CallRuntimeFailed), + Err(e) => { + if ctx.ext.append_debug_buffer("") { + ctx.ext.append_debug_buffer("seal0::call_runtime failed with: "); + ctx.ext.append_debug_buffer(e.into()); + }; + Ok(ReturnCode::CallRuntimeFailed) + }, } } From 9957da3cbb027f9b754c453a4d58a62665e532ef Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bastian=20K=C3=B6cher?= Date: Fri, 5 May 2023 19:25:45 +0200 Subject: [PATCH 465/558] Improve `try-runtime::on_runtime_upgrade` and fix some storage version issues (#14083) We now run all `try_on_runtime_upgrade` and collect the errors, instead of bailing on the first error. This makes it easier to see directly all the things that are failing instead of fixing one, recompiling and then checking the next one. Then this pr also sets the correct storage version for `pallet-bounties` that was already correctly set in the storage with the appropriate migration. --- frame/bounties/src/lib.rs | 3 ++ .../procedural/src/pallet/expand/hooks.rs | 2 +- frame/support/src/traits/hooks.rs | 30 ++++++++++++++++++- 3 files changed, 33 insertions(+), 2 deletions(-) diff --git a/frame/bounties/src/lib.rs b/frame/bounties/src/lib.rs index e7a3e2053..14f7b45cb 100644 --- a/frame/bounties/src/lib.rs +++ b/frame/bounties/src/lib.rs @@ -187,7 +187,10 @@ pub trait ChildBountyManager { pub mod pallet { use super::*; + const STORAGE_VERSION: StorageVersion = StorageVersion::new(4); + #[pallet::pallet] + #[pallet::storage_version(STORAGE_VERSION)] pub struct Pallet(_); #[pallet::config] diff --git a/frame/support/procedural/src/pallet/expand/hooks.rs b/frame/support/procedural/src/pallet/expand/hooks.rs index df07040b8..d7d8bbded 100644 --- a/frame/support/procedural/src/pallet/expand/hooks.rs +++ b/frame/support/procedural/src/pallet/expand/hooks.rs @@ -121,7 +121,7 @@ pub fn expand_hooks(def: &mut Def) -> proc_macro2::TokenStream { #frame_support::log::error!( target: #frame_support::LOG_TARGET, - "{}: On chain storage version {:?} is set to non zero,\ + "{}: On chain storage version {:?} is set to non zero, \ while the pallet is missing the `#[pallet::storage_version(VERSION)]` attribute.", pallet_name, on_chain_version, diff --git a/frame/support/src/traits/hooks.rs b/frame/support/src/traits/hooks.rs index 5fea98ee0..a4c677657 100644 --- a/frame/support/src/traits/hooks.rs +++ b/frame/support/src/traits/hooks.rs @@ -203,7 +203,35 @@ impl OnRuntimeUpgrade for Tuple { #[cfg(feature = "try-runtime")] fn try_on_runtime_upgrade(checks: bool) -> Result { let mut weight = Weight::zero(); - for_tuples!( #( weight = weight.saturating_add(Tuple::try_on_runtime_upgrade(checks)?); )* ); + + let mut errors = Vec::new(); + + for_tuples!(#( + match Tuple::try_on_runtime_upgrade(checks) { + Ok(weight) => { weight.saturating_add(weight); }, + Err(err) => { errors.push(err); }, + } + )*); + + if errors.len() == 1 { + return Err(errors[0]) + } else if !errors.is_empty() { + log::error!( + target: "try-runtime", + "Detected multiple errors while executing `try_on_runtime_upgrade`:", + ); + + errors.iter().for_each(|err| { + log::error!( + target: "try-runtime", + "{}", + err + ); + }); + + return Err("Detected multiple errors while executing `try_on_runtime_upgrade`, check the logs!") + } + Ok(weight) } } From da3821e6c65e0896f5d1d03de7d98fedf37d8605 Mon Sep 17 00:00:00 2001 From: Ankan <10196091+Ank4n@users.noreply.github.com> Date: Sun, 7 May 2023 18:06:06 +0200 Subject: [PATCH 466/558] divide payload into atleast one chunk (#14086) --- utils/frame/remote-externalities/src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/utils/frame/remote-externalities/src/lib.rs b/utils/frame/remote-externalities/src/lib.rs index 01c673884..d4ddacbcb 100644 --- a/utils/frame/remote-externalities/src/lib.rs +++ b/utils/frame/remote-externalities/src/lib.rs @@ -556,7 +556,7 @@ where .unwrap() .progress_chars("=>-"), ); - let payloads_chunked = payloads.chunks(&payloads.len() / Self::PARALLEL_REQUESTS); + let payloads_chunked = payloads.chunks((&payloads.len() / Self::PARALLEL_REQUESTS).max(1)); let requests = payloads_chunked.map(|payload_chunk| { Self::get_storage_data_dynamic_batch_size( &client, From 529e24ab7762f6d225aa383902cd6886cff20989 Mon Sep 17 00:00:00 2001 From: Niklas Adolfsson Date: Sun, 7 May 2023 21:11:03 +0200 Subject: [PATCH 467/558] fix cli: make port Option again (#14088) --- bin/node/cli/benches/block_production.rs | 1 + bin/node/cli/benches/transaction_pool.rs | 1 + client/cli/src/commands/run_cmd.rs | 18 ++++++++++-------- client/cli/src/config.rs | 24 ++++++++++++++++++------ client/cli/src/runner.rs | 1 + client/service/src/config.rs | 2 ++ client/service/src/lib.rs | 2 +- client/service/test/src/lib.rs | 1 + 8 files changed, 35 insertions(+), 15 deletions(-) diff --git a/bin/node/cli/benches/block_production.rs b/bin/node/cli/benches/block_production.rs index fd0bbb71d..527b145c6 100644 --- a/bin/node/cli/benches/block_production.rs +++ b/bin/node/cli/benches/block_production.rs @@ -92,6 +92,7 @@ fn new_node(tokio_handle: Handle) -> node_cli::service::NewFullBase { rpc_max_response_size: Default::default(), rpc_id_provider: Default::default(), rpc_max_subs_per_conn: Default::default(), + rpc_port: 9944, prometheus_config: None, telemetry_endpoints: None, default_heap_pages: None, diff --git a/bin/node/cli/benches/transaction_pool.rs b/bin/node/cli/benches/transaction_pool.rs index dc0025530..44ebe1e7d 100644 --- a/bin/node/cli/benches/transaction_pool.rs +++ b/bin/node/cli/benches/transaction_pool.rs @@ -86,6 +86,7 @@ fn new_node(tokio_handle: Handle) -> node_cli::service::NewFullBase { rpc_max_response_size: Default::default(), rpc_id_provider: Default::default(), rpc_max_subs_per_conn: Default::default(), + rpc_port: 9944, prometheus_config: None, telemetry_endpoints: None, default_heap_pages: None, diff --git a/client/cli/src/commands/run_cmd.rs b/client/cli/src/commands/run_cmd.rs index 8176643d7..975f76381 100644 --- a/client/cli/src/commands/run_cmd.rs +++ b/client/cli/src/commands/run_cmd.rs @@ -24,6 +24,8 @@ use crate::{ TransactionPoolParams, }, CliConfiguration, PrometheusParams, RuntimeParams, TelemetryParams, + RPC_DEFAULT_MAX_CONNECTIONS, RPC_DEFAULT_MAX_REQUEST_SIZE_MB, RPC_DEFAULT_MAX_RESPONSE_SIZE_MB, + RPC_DEFAULT_MAX_SUBS_PER_CONN, }; use clap::Parser; use regex::Regex; @@ -78,23 +80,23 @@ pub struct RunCmd { pub rpc_methods: RpcMethods, /// Set the the maximum RPC request payload size for both HTTP and WS in megabytes. - #[arg(long, default_value_t = 15)] + #[arg(long, default_value_t = RPC_DEFAULT_MAX_REQUEST_SIZE_MB)] pub rpc_max_request_size: u32, /// Set the the maximum RPC response payload size for both HTTP and WS in megabytes. - #[arg(long, default_value_t = 15)] + #[arg(long, default_value_t = RPC_DEFAULT_MAX_RESPONSE_SIZE_MB)] pub rpc_max_response_size: u32, /// Set the the maximum concurrent subscriptions per connection. - #[arg(long, default_value_t = 1024)] + #[arg(long, default_value_t = RPC_DEFAULT_MAX_SUBS_PER_CONN)] pub rpc_max_subscriptions_per_connection: u32, /// Specify JSON-RPC server TCP port. - #[arg(long, value_name = "PORT", default_value_t = 9944)] - pub rpc_port: u16, + #[arg(long, value_name = "PORT")] + pub rpc_port: Option, /// Maximum number of RPC server connections. - #[arg(long, value_name = "COUNT", default_value_t = 100)] + #[arg(long, value_name = "COUNT", default_value_t = RPC_DEFAULT_MAX_CONNECTIONS)] pub rpc_max_connections: u32, /// Specify browser Origins allowed to access the HTTP & WS RPC servers. @@ -336,7 +338,7 @@ impl CliConfiguration for RunCmd { .into()) } - fn rpc_addr(&self, _default_listen_port: u16) -> Result> { + fn rpc_addr(&self, default_listen_port: u16) -> Result> { let interface = rpc_interface( self.rpc_external, self.unsafe_rpc_external, @@ -344,7 +346,7 @@ impl CliConfiguration for RunCmd { self.validator, )?; - Ok(Some(SocketAddr::new(interface, self.rpc_port))) + Ok(Some(SocketAddr::new(interface, self.rpc_port.unwrap_or(default_listen_port)))) } fn rpc_methods(&self) -> Result { diff --git a/client/cli/src/config.rs b/client/cli/src/config.rs index 01f6adbba..04c62a73b 100644 --- a/client/cli/src/config.rs +++ b/client/cli/src/config.rs @@ -45,6 +45,17 @@ pub(crate) const DEFAULT_NETWORK_CONFIG_PATH: &str = "network"; /// The recommended open file descriptor limit to be configured for the process. const RECOMMENDED_OPEN_FILE_DESCRIPTOR_LIMIT: u64 = 10_000; +/// The default port. +pub const RPC_DEFAULT_PORT: u16 = 9944; +/// The default max number of subscriptions per connection. +pub const RPC_DEFAULT_MAX_SUBS_PER_CONN: u32 = 1024; +/// The default max request size in MB. +pub const RPC_DEFAULT_MAX_REQUEST_SIZE_MB: u32 = 15; +/// The default max response size in MB. +pub const RPC_DEFAULT_MAX_RESPONSE_SIZE_MB: u32 = 15; +/// The default number of connection.. +pub const RPC_DEFAULT_MAX_CONNECTIONS: u32 = 100; + /// Default configuration values used by Substrate /// /// These values will be used by [`CliConfiguration`] to set @@ -61,7 +72,7 @@ pub trait DefaultConfigurationValues { /// /// By default this is `9944`. fn rpc_listen_port() -> u16 { - 9944 + RPC_DEFAULT_PORT } /// The port Substrate should listen on for prometheus connections. @@ -303,14 +314,14 @@ pub trait CliConfiguration: Sized { /// Returns the RPC method set to expose. /// /// By default this is `RpcMethods::Auto` (unsafe RPCs are denied iff - /// `{rpc,ws}_external` returns true, respectively). + /// `rpc_external` returns true, respectively). fn rpc_methods(&self) -> Result { Ok(Default::default()) } /// Get the maximum number of RPC server connections. fn rpc_max_connections(&self) -> Result { - Ok(Default::default()) + Ok(RPC_DEFAULT_MAX_CONNECTIONS) } /// Get the RPC cors (`None` if disabled) @@ -322,17 +333,17 @@ pub trait CliConfiguration: Sized { /// Get maximum RPC request payload size. fn rpc_max_request_size(&self) -> Result { - Ok(Default::default()) + Ok(RPC_DEFAULT_MAX_REQUEST_SIZE_MB) } /// Get maximum RPC response payload size. fn rpc_max_response_size(&self) -> Result { - Ok(Default::default()) + Ok(RPC_DEFAULT_MAX_RESPONSE_SIZE_MB) } /// Get maximum number of subscriptions per connection. fn rpc_max_subscriptions_per_connection(&self) -> Result { - Ok(Default::default()) + Ok(RPC_DEFAULT_MAX_SUBS_PER_CONN) } /// Get the prometheus configuration (`None` if disabled) @@ -506,6 +517,7 @@ pub trait CliConfiguration: Sized { rpc_max_response_size: self.rpc_max_response_size()?, rpc_id_provider: None, rpc_max_subs_per_conn: self.rpc_max_subscriptions_per_connection()?, + rpc_port: DCV::rpc_listen_port(), prometheus_config: self .prometheus_config(DCV::prometheus_listen_port(), &chain_spec)?, telemetry_endpoints, diff --git a/client/cli/src/runner.rs b/client/cli/src/runner.rs index 1fa12e2a0..7b534b371 100644 --- a/client/cli/src/runner.rs +++ b/client/cli/src/runner.rs @@ -296,6 +296,7 @@ mod tests { rpc_max_response_size: Default::default(), rpc_id_provider: Default::default(), rpc_max_subs_per_conn: Default::default(), + rpc_port: 9944, prometheus_config: None, telemetry_endpoints: None, default_heap_pages: None, diff --git a/client/service/src/config.rs b/client/service/src/config.rs index 0ff2c96d8..c0fb2dc9c 100644 --- a/client/service/src/config.rs +++ b/client/service/src/config.rs @@ -101,6 +101,8 @@ pub struct Configuration { pub rpc_id_provider: Option>, /// Maximum allowed subscriptions per rpc connection pub rpc_max_subs_per_conn: u32, + /// JSON-RPC server default port. + pub rpc_port: u16, /// Prometheus endpoint configuration. `None` if disabled. pub prometheus_config: Option, /// Telemetry service URL. `None` if disabled. diff --git a/client/service/src/lib.rs b/client/service/src/lib.rs index b90eb71c1..e658c7f7d 100644 --- a/client/service/src/lib.rs +++ b/client/service/src/lib.rs @@ -386,7 +386,7 @@ where addr }; - let addr = config.rpc_addr.unwrap_or(([127, 0, 0, 1], 9944).into()); + let addr = config.rpc_addr.unwrap_or_else(|| ([127, 0, 0, 1], config.rpc_port).into()); let backup_addr = backup_port(addr); let metrics = sc_rpc_server::RpcMetrics::new(config.prometheus_registry())?; diff --git a/client/service/test/src/lib.rs b/client/service/test/src/lib.rs index db8432e44..11c672db8 100644 --- a/client/service/test/src/lib.rs +++ b/client/service/test/src/lib.rs @@ -254,6 +254,7 @@ fn node_config< rpc_max_response_size: Default::default(), rpc_id_provider: Default::default(), rpc_max_subs_per_conn: Default::default(), + rpc_port: 9944, prometheus_config: None, telemetry_endpoints: None, default_heap_pages: None, From cf1fb8953c8aa2ca9bb2197fae40c31e10bcf45f Mon Sep 17 00:00:00 2001 From: joe petrowski <25483142+joepetrowski@users.noreply.github.com> Date: Mon, 8 May 2023 12:17:35 +0200 Subject: [PATCH 468/558] Allow Creation of Asset Accounts That Don't Exist Yet and Add `Blocked` Status (#13843) * prevent frozen accounts from receiving assets * refund deposits correctly * plus refund_other * add benchmarks * start migration work * docs * add migration logic * fix freeze_creating benchmark * support instanced migrations * review * correct deposit refund * only allow depositor, admin, or account origin to refund deposits * make sure refund actually removes account * do refund changes * Asset's account deposit owner (#13874) * assets deposit owner * doc typo * remove migration * empty commit * can transfer to frozen account * remove allow_burn from refund_other * storage version back to 1 * update doc * fix benches * update docs * more tests * Update frame/assets/src/types.rs * refund updating the reason * refactor * separate refund and refund_foreign * refunds, touch_other, tests * fixes * fmt * ".git/.scripts/commands/bench/bench.sh" pallet dev pallet_assets * tests: asserts asset account counts * Account touch trait (#14063) * assets touch trait * docs * move touch trait into support/traits * permissionless flag for do_touch * Apply suggestions from code review Co-authored-by: Kian Paimani <5588131+kianenigma@users.noreply.github.com> * move trait to misc, drop option * Apply suggestions from code review Co-authored-by: Gavin Wood * correct doc * Update frame/assets/src/functions.rs --------- Co-authored-by: Kian Paimani <5588131+kianenigma@users.noreply.github.com> Co-authored-by: Gavin Wood Co-authored-by: joe petrowski <25483142+joepetrowski@users.noreply.github.com> * Block asset account (#14070) * replace is_fronzen flag by status enum * block asset account * remove redundant brackets * fix typo * fmt * Apply suggestions from code review Co-authored-by: Jegor Sidorenko <5252494+jsidorenko@users.noreply.github.com> * rename permissionless to check_depositor * doc fix * use account id lookup instead account id * add benchmark for touch_other --------- Co-authored-by: muharem Co-authored-by: command-bot <> Co-authored-by: Kian Paimani <5588131+kianenigma@users.noreply.github.com> Co-authored-by: Gavin Wood Co-authored-by: Jegor Sidorenko <5252494+jsidorenko@users.noreply.github.com> --- frame/assets/src/benchmarking.rs | 69 ++++ frame/assets/src/functions.rs | 111 ++++-- frame/assets/src/lib.rs | 139 ++++++- frame/assets/src/migration.rs | 5 +- frame/assets/src/tests.rs | 349 ++++++++++++++++- frame/assets/src/types.rs | 78 +++- frame/assets/src/weights.rs | 483 ++++++++++++++++-------- frame/support/src/traits.rs | 4 +- frame/support/src/traits/misc.rs | 15 +- frame/support/src/traits/tokens/misc.rs | 3 + primitives/runtime/src/lib.rs | 3 + 11 files changed, 1040 insertions(+), 219 deletions(-) diff --git a/frame/assets/src/benchmarking.rs b/frame/assets/src/benchmarking.rs index 047ede2ce..a24836507 100644 --- a/frame/assets/src/benchmarking.rs +++ b/frame/assets/src/benchmarking.rs @@ -482,5 +482,74 @@ benchmarks_instance_pallet! { assert_last_event::(Event::AssetMinBalanceChanged { asset_id: asset_id.into(), new_min_balance: 50u32.into() }.into()); } + touch { + let (asset_id, asset_owner, asset_owner_lookup) = create_default_asset::(false); + let new_account: T::AccountId = account("newaccount", 1, SEED); + T::Currency::make_free_balance_be(&new_account, DepositBalanceOf::::max_value()); + assert_ne!(asset_owner, new_account); + assert!(!Account::::contains_key(asset_id.into(), &new_account)); + }: _(SystemOrigin::Signed(new_account.clone()), asset_id) + verify { + assert!(Account::::contains_key(asset_id.into(), &new_account)); + } + + touch_other { + let (asset_id, asset_owner, asset_owner_lookup) = create_default_asset::(false); + let new_account: T::AccountId = account("newaccount", 1, SEED); + let new_account_lookup = T::Lookup::unlookup(new_account.clone()); + T::Currency::make_free_balance_be(&asset_owner, DepositBalanceOf::::max_value()); + assert_ne!(asset_owner, new_account); + assert!(!Account::::contains_key(asset_id.into(), &new_account)); + }: _(SystemOrigin::Signed(asset_owner.clone()), asset_id, new_account_lookup) + verify { + assert!(Account::::contains_key(asset_id.into(), &new_account)); + } + + refund { + let (asset_id, asset_owner, asset_owner_lookup) = create_default_asset::(false); + let new_account: T::AccountId = account("newaccount", 1, SEED); + T::Currency::make_free_balance_be(&new_account, DepositBalanceOf::::max_value()); + assert_ne!(asset_owner, new_account); + assert!(Assets::::touch( + SystemOrigin::Signed(new_account.clone()).into(), + asset_id + ).is_ok()); + // `touch` should reserve some balance of the caller... + assert!(!T::Currency::reserved_balance(&new_account).is_zero()); + // ...and also create an `Account` entry. + assert!(Account::::contains_key(asset_id.into(), &new_account)); + }: _(SystemOrigin::Signed(new_account.clone()), asset_id, true) + verify { + // `refund`ing should of course repatriate the reserve + assert!(T::Currency::reserved_balance(&new_account).is_zero()); + } + + refund_other { + let (asset_id, asset_owner, asset_owner_lookup) = create_default_asset::(false); + let new_account: T::AccountId = account("newaccount", 1, SEED); + let new_account_lookup = T::Lookup::unlookup(new_account.clone()); + T::Currency::make_free_balance_be(&asset_owner, DepositBalanceOf::::max_value()); + assert_ne!(asset_owner, new_account); + assert!(Assets::::touch_other( + SystemOrigin::Signed(asset_owner.clone()).into(), + asset_id, + new_account_lookup.clone() + ).is_ok()); + // `touch_other` should reserve balance of the freezer + assert!(!T::Currency::reserved_balance(&asset_owner).is_zero()); + assert!(Account::::contains_key(asset_id.into(), &new_account)); + }: _(SystemOrigin::Signed(asset_owner.clone()), asset_id, new_account_lookup.clone()) + verify { + // this should repatriate the reserved balance of the freezer + assert!(T::Currency::reserved_balance(&asset_owner).is_zero()); + } + + block { + let (asset_id, caller, caller_lookup) = create_default_minted_asset::(true, 100u32.into()); + }: _(SystemOrigin::Signed(caller.clone()), asset_id, caller_lookup) + verify { + assert_last_event::(Event::Blocked { asset_id: asset_id.into(), who: caller }.into()); + } + impl_benchmark_test_suite!(Assets, crate::mock::new_test_ext(), crate::mock::Test) } diff --git a/frame/assets/src/functions.rs b/frame/assets/src/functions.rs index 1e10e0066..d7c5bbe95 100644 --- a/frame/assets/src/functions.rs +++ b/frame/assets/src/functions.rs @@ -66,11 +66,15 @@ impl, I: 'static> Pallet { pub(super) fn new_account( who: &T::AccountId, d: &mut AssetDetails>, - maybe_deposit: Option>, - ) -> Result>, DispatchError> { + maybe_deposit: Option<(&T::AccountId, DepositBalanceOf)>, + ) -> Result, DispatchError> { let accounts = d.accounts.checked_add(1).ok_or(ArithmeticError::Overflow)?; - let reason = if let Some(deposit) = maybe_deposit { - ExistenceReason::DepositHeld(deposit) + let reason = if let Some((depositor, deposit)) = maybe_deposit { + if depositor == who { + ExistenceReason::DepositHeld(deposit) + } else { + ExistenceReason::DepositFrom(depositor.clone(), deposit) + } } else if d.is_sufficient { frame_system::Pallet::::inc_sufficients(who); d.sufficients += 1; @@ -93,18 +97,19 @@ impl, I: 'static> Pallet { pub(super) fn dead_account( who: &T::AccountId, d: &mut AssetDetails>, - reason: &ExistenceReason>, + reason: &ExistenceReasonOf, force: bool, ) -> DeadConsequence { + use ExistenceReason::*; match *reason { - ExistenceReason::Consumer => frame_system::Pallet::::dec_consumers(who), - ExistenceReason::Sufficient => { + Consumer => frame_system::Pallet::::dec_consumers(who), + Sufficient => { d.sufficients = d.sufficients.saturating_sub(1); frame_system::Pallet::::dec_sufficients(who); }, - ExistenceReason::DepositRefunded => {}, - ExistenceReason::DepositHeld(_) if !force => return Keep, - ExistenceReason::DepositHeld(_) => {}, + DepositRefunded => {}, + DepositHeld(_) | DepositFrom(..) if !force => return Keep, + DepositHeld(_) | DepositFrom(..) => {}, } d.accounts = d.accounts.saturating_sub(1); Remove @@ -130,8 +135,11 @@ impl, I: 'static> Pallet { if increase_supply && details.supply.checked_add(&amount).is_none() { return DepositConsequence::Overflow } - if let Some(balance) = Self::maybe_balance(id, who) { - if balance.checked_add(&amount).is_none() { + if let Some(account) = Account::::get(id, who) { + if account.status.is_blocked() { + return DepositConsequence::Blocked + } + if account.balance.checked_add(&amount).is_none() { return DepositConsequence::Overflow } } else { @@ -174,7 +182,7 @@ impl, I: 'static> Pallet { Some(a) => a, None => return BalanceLow, }; - if account.is_frozen { + if account.status.is_frozen() { return Frozen } if let Some(rest) = account.balance.checked_sub(&amount) { @@ -215,7 +223,7 @@ impl, I: 'static> Pallet { ensure!(details.status == AssetStatus::Live, Error::::AssetNotLive); let account = Account::::get(id, who).ok_or(Error::::NoAccount)?; - ensure!(!account.is_frozen, Error::::Frozen); + ensure!(!account.status.is_frozen(), Error::::Frozen); let amount = if let Some(frozen) = T::Freezer::frozen_balance(id, who) { // Frozen balance: account CANNOT be deleted @@ -302,43 +310,63 @@ impl, I: 'static> Pallet { Ok((credit, maybe_burn)) } - /// Creates a account for `who` to hold asset `id` with a zero balance and takes a deposit. - pub(super) fn do_touch(id: T::AssetId, who: T::AccountId) -> DispatchResult { + /// Creates an account for `who` to hold asset `id` with a zero balance and takes a deposit. + /// + /// When `check_depositor` is set to true, the depositor must be either the asset's Admin or + /// Freezer, otherwise the depositor can be any account. + pub(super) fn do_touch( + id: T::AssetId, + who: T::AccountId, + depositor: T::AccountId, + check_depositor: bool, + ) -> DispatchResult { ensure!(!Account::::contains_key(id, &who), Error::::AlreadyExists); let deposit = T::AssetAccountDeposit::get(); let mut details = Asset::::get(&id).ok_or(Error::::Unknown)?; ensure!(details.status == AssetStatus::Live, Error::::AssetNotLive); - let reason = Self::new_account(&who, &mut details, Some(deposit))?; - T::Currency::reserve(&who, deposit)?; + ensure!( + !check_depositor || &depositor == &details.admin || &depositor == &details.freezer, + Error::::NoPermission + ); + let reason = Self::new_account(&who, &mut details, Some((&depositor, deposit)))?; + T::Currency::reserve(&depositor, deposit)?; Asset::::insert(&id, details); Account::::insert( id, &who, AssetAccountOf:: { balance: Zero::zero(), - is_frozen: false, + status: AccountStatus::Liquid, reason, extra: T::Extra::default(), }, ); + Self::deposit_event(Event::Touched { asset_id: id, who, depositor }); Ok(()) } - /// Returns a deposit, destroying an asset-account. + /// Returns a deposit or a consumer reference, destroying an asset-account. + /// Non-zero balance accounts refunded and destroyed only if `allow_burn` is true. pub(super) fn do_refund(id: T::AssetId, who: T::AccountId, allow_burn: bool) -> DispatchResult { + use AssetStatus::*; + use ExistenceReason::*; let mut account = Account::::get(id, &who).ok_or(Error::::NoDeposit)?; - let deposit = account.reason.take_deposit().ok_or(Error::::NoDeposit)?; + ensure!(matches!(account.reason, Consumer | DepositHeld(..)), Error::::NoDeposit); let mut details = Asset::::get(&id).ok_or(Error::::Unknown)?; - ensure!(details.status == AssetStatus::Live, Error::::AssetNotLive); + ensure!(matches!(details.status, Live | Frozen), Error::::IncorrectStatus); ensure!(account.balance.is_zero() || allow_burn, Error::::WouldBurn); - ensure!(!account.is_frozen, Error::::Frozen); - T::Currency::unreserve(&who, deposit); + if let Some(deposit) = account.reason.take_deposit() { + T::Currency::unreserve(&who, deposit); + } if let Remove = Self::dead_account(&who, &mut details, &account.reason, false) { Account::::remove(id, &who); } else { debug_assert!(false, "refund did not result in dead account?!"); + // deposit may have been refunded, need to update `Account` + Account::::insert(id, &who, account); + return Ok(()) } Asset::::insert(&id, details); // Executing a hook here is safe, since it is not in a `mutate`. @@ -346,6 +374,37 @@ impl, I: 'static> Pallet { Ok(()) } + /// Returns a `DepositFrom` of an account only if balance is zero. + pub(super) fn do_refund_other( + id: T::AssetId, + who: &T::AccountId, + caller: &T::AccountId, + ) -> DispatchResult { + let mut account = Account::::get(id, &who).ok_or(Error::::NoDeposit)?; + let (depositor, deposit) = + account.reason.take_deposit_from().ok_or(Error::::NoDeposit)?; + let mut details = Asset::::get(&id).ok_or(Error::::Unknown)?; + ensure!(details.status == AssetStatus::Live, Error::::AssetNotLive); + ensure!(!account.status.is_frozen(), Error::::Frozen); + ensure!(caller == &depositor || caller == &details.admin, Error::::NoPermission); + ensure!(account.balance.is_zero(), Error::::WouldBurn); + + T::Currency::unreserve(&depositor, deposit); + + if let Remove = Self::dead_account(&who, &mut details, &account.reason, false) { + Account::::remove(id, &who); + } else { + debug_assert!(false, "refund did not result in dead account?!"); + // deposit may have been refunded, need to update `Account` + Account::::insert(id, &who, account); + return Ok(()) + } + Asset::::insert(&id, details); + // Executing a hook here is safe, since it is not in a `mutate`. + T::Freezer::died(id, &who); + return Ok(()) + } + /// Increases the asset `id` balance of `beneficiary` by `amount`. /// /// This alters the registered supply of the asset and emits an event. @@ -408,7 +467,7 @@ impl, I: 'static> Pallet { *maybe_account = Some(AssetAccountOf:: { balance: amount, reason: Self::new_account(beneficiary, details, None)?, - is_frozen: false, + status: AccountStatus::Liquid, extra: T::Extra::default(), }); }, @@ -602,7 +661,7 @@ impl, I: 'static> Pallet { maybe_account @ None => { *maybe_account = Some(AssetAccountOf:: { balance: credit, - is_frozen: false, + status: AccountStatus::Liquid, reason: Self::new_account(dest, details, None)?, extra: T::Extra::default(), }); diff --git a/frame/assets/src/lib.rs b/frame/assets/src/lib.rs index d32b407e6..e6b59bba5 100644 --- a/frame/assets/src/lib.rs +++ b/frame/assets/src/lib.rs @@ -82,6 +82,10 @@ //! * `approve_transfer`: Create or increase an delegated transfer. //! * `cancel_approval`: Rescind a previous approval. //! * `transfer_approved`: Transfer third-party's assets to another account. +//! * `touch`: Create an asset account for non-provider assets. Caller must place a deposit. +//! * `refund`: Return the deposit (if any) of the caller's asset account or a consumer reference +//! (if any) of the caller's account. +//! * `refund_other`: Return the deposit (if any) of a specified asset account. //! //! ### Permissioned Functions //! @@ -98,12 +102,16 @@ //! * `burn`: Decreases the asset balance of an account; called by the asset class's Admin. //! * `force_transfer`: Transfers between arbitrary accounts; called by the asset class's Admin. //! * `freeze`: Disallows further `transfer`s from an account; called by the asset class's Freezer. -//! * `thaw`: Allows further `transfer`s from an account; called by the asset class's Admin. +//! * `thaw`: Allows further `transfer`s to and from an account; called by the asset class's Admin. //! * `transfer_ownership`: Changes an asset class's Owner; called by the asset class's Owner. //! * `set_team`: Changes an asset class's Admin, Freezer and Issuer; called by the asset class's //! Owner. //! * `set_metadata`: Set the metadata of an asset class; called by the asset class's Owner. //! * `clear_metadata`: Remove the metadata of an asset class; called by the asset class's Owner. +//! * `touch_other`: Create an asset account for specified account. Caller must place a deposit; +//! called by the asset class's Freezer or Admin. +//! * `block`: Disallows further `transfer`s to and from an account; called by the asset class's +//! Freezer. //! //! Please refer to the [`Call`] enum and its associated variants for documentation on each //! function. @@ -194,7 +202,7 @@ impl AssetsCallback for () {} #[frame_support::pallet] pub mod pallet { use super::*; - use frame_support::pallet_prelude::*; + use frame_support::{pallet_prelude::*, traits::AccountTouch}; use frame_system::pallet_prelude::*; /// The current storage version. @@ -519,6 +527,10 @@ pub mod pallet { AssetStatusChanged { asset_id: T::AssetId }, /// The min_balance of an asset has been updated by the asset owner. AssetMinBalanceChanged { asset_id: T::AssetId, new_min_balance: T::Balance }, + /// Some account `who` was created with a deposit from `depositor`. + Touched { asset_id: T::AssetId, who: T::AccountId, depositor: T::AccountId }, + /// Some account `who` was blocked. + Blocked { asset_id: T::AssetId, who: T::AccountId }, } #[pallet::error] @@ -911,7 +923,9 @@ pub mod pallet { Self::do_transfer(id, &source, &dest, amount, Some(origin), f).map(|_| ()) } - /// Disallow further unprivileged transfers from an account. + /// Disallow further unprivileged transfers of an asset `id` from an account `who`. `who` + /// must already exist as an entry in `Account`s of the asset. If you want to freeze an + /// account that does not have an entry, use `touch_other` first. /// /// Origin must be Signed and the sender should be the Freezer of the asset `id`. /// @@ -939,7 +953,8 @@ pub mod pallet { let who = T::Lookup::lookup(who)?; Account::::try_mutate(id, &who, |maybe_account| -> DispatchResult { - maybe_account.as_mut().ok_or(Error::::NoAccount)?.is_frozen = true; + maybe_account.as_mut().ok_or(Error::::NoAccount)?.status = + AccountStatus::Frozen; Ok(()) })?; @@ -947,7 +962,7 @@ pub mod pallet { Ok(()) } - /// Allow unprivileged transfers from an account again. + /// Allow unprivileged transfers to and from an account again. /// /// Origin must be Signed and the sender should be the Admin of the asset `id`. /// @@ -975,7 +990,8 @@ pub mod pallet { let who = T::Lookup::lookup(who)?; Account::::try_mutate(id, &who, |maybe_account| -> DispatchResult { - maybe_account.as_mut().ok_or(Error::::NoAccount)?.is_frozen = false; + maybe_account.as_mut().ok_or(Error::::NoAccount)?.status = + AccountStatus::Liquid; Ok(()) })?; @@ -1471,22 +1487,25 @@ pub mod pallet { /// /// Emits `Touched` event when successful. #[pallet::call_index(26)] - #[pallet::weight(T::WeightInfo::mint())] + #[pallet::weight(T::WeightInfo::touch())] pub fn touch(origin: OriginFor, id: T::AssetIdParameter) -> DispatchResult { + let who = ensure_signed(origin)?; let id: T::AssetId = id.into(); - Self::do_touch(id, ensure_signed(origin)?) + Self::do_touch(id, who.clone(), who, false) } - /// Return the deposit (if any) of an asset account. + /// Return the deposit (if any) of an asset account or a consumer reference (if any) of an + /// account. /// /// The origin must be Signed. /// - /// - `id`: The identifier of the asset for the account to be created. + /// - `id`: The identifier of the asset for which the caller would like the deposit + /// refunded. /// - `allow_burn`: If `true` then assets may be destroyed in order to complete the refund. /// /// Emits `Refunded` event when successful. #[pallet::call_index(27)] - #[pallet::weight(T::WeightInfo::mint())] + #[pallet::weight(T::WeightInfo::refund())] pub fn refund( origin: OriginFor, id: T::AssetIdParameter, @@ -1541,6 +1560,104 @@ pub mod pallet { }); Ok(()) } + + /// Create an asset account for `who`. + /// + /// A deposit will be taken from the signer account. + /// + /// - `origin`: Must be Signed by `Freezer` or `Admin` of the asset `id`; the signer account + /// must have sufficient funds for a deposit to be taken. + /// - `id`: The identifier of the asset for the account to be created. + /// - `who`: The account to be created. + /// + /// Emits `Touched` event when successful. + #[pallet::call_index(29)] + #[pallet::weight(T::WeightInfo::touch_other())] + pub fn touch_other( + origin: OriginFor, + id: T::AssetIdParameter, + who: AccountIdLookupOf, + ) -> DispatchResult { + let origin = ensure_signed(origin)?; + let who = T::Lookup::lookup(who)?; + let id: T::AssetId = id.into(); + Self::do_touch(id, who, origin, true) + } + + /// Return the deposit (if any) of a target asset account. Useful if you are the depositor. + /// + /// The origin must be Signed and either the account owner, depositor, or asset `Admin`. In + /// order to burn a non-zero balance of the asset, the caller must be the account and should + /// use `refund`. + /// + /// - `id`: The identifier of the asset for the account holding a deposit. + /// - `who`: The account to refund. + /// + /// Emits `Refunded` event when successful. + #[pallet::call_index(30)] + #[pallet::weight(T::WeightInfo::refund_other())] + pub fn refund_other( + origin: OriginFor, + id: T::AssetIdParameter, + who: AccountIdLookupOf, + ) -> DispatchResult { + let origin = ensure_signed(origin)?; + let who = T::Lookup::lookup(who)?; + let id: T::AssetId = id.into(); + Self::do_refund_other(id, &who, &origin) + } + + /// Disallow further unprivileged transfers of an asset `id` to and from an account `who`. + /// + /// Origin must be Signed and the sender should be the Freezer of the asset `id`. + /// + /// - `id`: The identifier of the account's asset. + /// - `who`: The account to be unblocked. + /// + /// Emits `Blocked`. + /// + /// Weight: `O(1)` + #[pallet::call_index(31)] + pub fn block( + origin: OriginFor, + id: T::AssetIdParameter, + who: AccountIdLookupOf, + ) -> DispatchResult { + let origin = ensure_signed(origin)?; + let id: T::AssetId = id.into(); + + let d = Asset::::get(id).ok_or(Error::::Unknown)?; + ensure!( + d.status == AssetStatus::Live || d.status == AssetStatus::Frozen, + Error::::AssetNotLive + ); + ensure!(origin == d.freezer, Error::::NoPermission); + let who = T::Lookup::lookup(who)?; + + Account::::try_mutate(id, &who, |maybe_account| -> DispatchResult { + maybe_account.as_mut().ok_or(Error::::NoAccount)?.status = + AccountStatus::Blocked; + Ok(()) + })?; + + Self::deposit_event(Event::::Blocked { asset_id: id, who }); + Ok(()) + } + } + + /// Implements [AccountTouch] trait. + /// Note that a depositor can be any account, without any specific privilege. + /// This implementation is supposed to be used only for creation of system accounts. + impl, I: 'static> AccountTouch for Pallet { + type Balance = DepositBalanceOf; + + fn deposit_required() -> Self::Balance { + T::AssetAccountDeposit::get() + } + + fn touch(asset: T::AssetId, who: T::AccountId, depositor: T::AccountId) -> DispatchResult { + Self::do_touch(asset, who, depositor, false) + } } } diff --git a/frame/assets/src/migration.rs b/frame/assets/src/migration.rs index 71da8823e..90400ef5b 100644 --- a/frame/assets/src/migration.rs +++ b/frame/assets/src/migration.rs @@ -122,7 +122,10 @@ pub mod v1 { ); Asset::::iter().for_each(|(_id, asset)| { - assert!(asset.status == AssetStatus::Live || asset.status == AssetStatus::Frozen, "assets should only be live or frozen. None should be in destroying status, or undefined state") + assert!( + asset.status == AssetStatus::Live || asset.status == AssetStatus::Frozen, + "assets should only be live or frozen. None should be in destroying status, or undefined state" + ) }); Ok(()) } diff --git a/frame/assets/src/tests.rs b/frame/assets/src/tests.rs index 88ba92d06..009d0180f 100644 --- a/frame/assets/src/tests.rs +++ b/frame/assets/src/tests.rs @@ -34,6 +34,12 @@ fn asset_ids() -> Vec { s } +/// returns tuple of asset's account and sufficient counts +fn asset_account_counts(asset_id: u32) -> (u32, u32) { + let asset = Asset::::get(asset_id).unwrap(); + (asset.accounts, asset.sufficients) +} + #[test] fn transfer_should_never_burn() { new_test_ext().execute_with(|| { @@ -154,9 +160,11 @@ fn refunding_asset_deposit_without_burn_should_work() { assert_eq!(Assets::balance(0, 2), 100); assert_eq!(Assets::balance(0, 1), 0); assert_eq!(Balances::reserved_balance(&1), 10); + assert_eq!(asset_account_counts(0), (2, 0)); assert_ok!(Assets::refund(RuntimeOrigin::signed(1), 0, false)); assert_eq!(Balances::reserved_balance(&1), 0); assert_eq!(Assets::balance(1, 0), 0); + assert_eq!(asset_account_counts(0), (1, 0)); }); } @@ -176,6 +184,99 @@ fn refunding_calls_died_hook() { }); } +#[test] +fn refunding_with_sufficient_existence_reason_should_fail() { + new_test_ext().execute_with(|| { + // create sufficient asset + assert_ok!(Assets::force_create(RuntimeOrigin::root(), 0, 1, true, 1)); + assert_ok!(Assets::mint(RuntimeOrigin::signed(1), 0, 1, 100)); + // create an asset account with sufficient existence reason + // by transferring some sufficient assets + assert_ok!(Assets::transfer(RuntimeOrigin::signed(1), 0, 2, 50)); + assert_eq!(Assets::balance(0, 1), 50); + assert_eq!(Assets::balance(0, 2), 50); + assert_eq!(asset_account_counts(0), (2, 2)); + // fails to refund + assert_noop!(Assets::refund(RuntimeOrigin::signed(2), 0, true), Error::::NoDeposit); + }); +} + +#[test] +fn refunding_with_deposit_from_should_fail() { + new_test_ext().execute_with(|| { + assert_ok!(Assets::force_create(RuntimeOrigin::root(), 0, 1, false, 1)); + Balances::make_free_balance_be(&1, 100); + // create asset account `2` with deposit from `1` + assert_ok!(Assets::touch_other(RuntimeOrigin::signed(1), 0, 2)); + assert_eq!(Balances::reserved_balance(&1), 10); + // fails to refund + assert_noop!(Assets::refund(RuntimeOrigin::signed(2), 0, true), Error::::NoDeposit); + assert!(Account::::contains_key(0, &2)); + }); +} + +#[test] +fn refunding_frozen_with_consumer_ref_works() { + new_test_ext().execute_with(|| { + // 1 will be an admin + // 2 will be a frozen account + Balances::make_free_balance_be(&1, 100); + Balances::make_free_balance_be(&2, 100); + // create non-sufficient asset + assert_ok!(Assets::force_create(RuntimeOrigin::root(), 0, 1, false, 1)); + assert_ok!(Assets::mint(RuntimeOrigin::signed(1), 0, 1, 100)); + assert_eq!(System::consumers(&2), 0); + // create asset account `2` with a consumer reference by transferring + // non-sufficient funds into + assert_ok!(Assets::transfer(RuntimeOrigin::signed(1), 0, 2, 50)); + assert_eq!(System::consumers(&2), 1); + assert_eq!(Assets::balance(0, 1), 50); + assert_eq!(Assets::balance(0, 2), 50); + assert_eq!(asset_account_counts(0), (2, 0)); + // freeze asset account `2` and asset `0` + assert_ok!(Assets::freeze(RuntimeOrigin::signed(1), 0, 2)); + assert_ok!(Assets::freeze_asset(RuntimeOrigin::signed(1), 0)); + // refund works + assert_ok!(Assets::refund(RuntimeOrigin::signed(2), 0, true)); + assert!(!Account::::contains_key(0, &2)); + assert_eq!(System::consumers(&2), 0); + assert_eq!(asset_account_counts(0), (1, 0)); + }); +} + +#[test] +fn refunding_frozen_with_deposit_works() { + new_test_ext().execute_with(|| { + // 1 will be an asset admin + // 2 will be a frozen account + Balances::make_free_balance_be(&1, 100); + Balances::make_free_balance_be(&2, 100); + assert_ok!(Assets::force_create(RuntimeOrigin::root(), 0, 1, false, 1)); + assert_ok!(Assets::mint(RuntimeOrigin::signed(1), 0, 1, 100)); + assert_eq!(System::consumers(&2), 0); + assert_ok!(Assets::touch(RuntimeOrigin::signed(2), 0)); + // reserve deposit holds one consumer ref + assert_eq!(System::consumers(&2), 1); + assert_eq!(Balances::reserved_balance(&2), 10); + assert!(Account::::contains_key(0, &2)); + // transfer some assets to `2` + assert_ok!(Assets::transfer(RuntimeOrigin::signed(1), 0, 2, 50)); + assert_eq!(System::consumers(&2), 1); + assert_eq!(Assets::balance(0, 1), 50); + assert_eq!(Assets::balance(0, 2), 50); + assert_eq!(asset_account_counts(0), (2, 0)); + // ensure refundable even if asset account and asset is frozen + assert_ok!(Assets::freeze(RuntimeOrigin::signed(1), 0, 2)); + assert_ok!(Assets::freeze_asset(RuntimeOrigin::signed(1), 0)); + // success + assert_ok!(Assets::refund(RuntimeOrigin::signed(2), 0, true)); + assert!(!Account::::contains_key(0, &2)); + assert_eq!(Balances::reserved_balance(&2), 0); + assert_eq!(System::consumers(&2), 0); + assert_eq!(asset_account_counts(0), (1, 0)); + }); +} + #[test] fn approval_lifecycle_works() { new_test_ext().execute_with(|| { @@ -637,6 +738,37 @@ fn approve_transfer_frozen_asset_should_not_work() { }); } +#[test] +fn transferring_from_blocked_account_should_not_work() { + new_test_ext().execute_with(|| { + assert_ok!(Assets::force_create(RuntimeOrigin::root(), 0, 1, true, 1)); + assert_ok!(Assets::mint(RuntimeOrigin::signed(1), 0, 1, 100)); + assert_eq!(Assets::balance(0, 1), 100); + assert_ok!(Assets::block(RuntimeOrigin::signed(1), 0, 1)); + // behaves as frozen when transferring from blocked + assert_noop!(Assets::transfer(RuntimeOrigin::signed(1), 0, 2, 50), Error::::Frozen); + assert_ok!(Assets::thaw(RuntimeOrigin::signed(1), 0, 1)); + assert_ok!(Assets::transfer(RuntimeOrigin::signed(1), 0, 2, 50)); + assert_ok!(Assets::transfer(RuntimeOrigin::signed(2), 0, 1, 50)); + }); +} + +#[test] +fn transferring_to_blocked_account_should_not_work() { + new_test_ext().execute_with(|| { + assert_ok!(Assets::force_create(RuntimeOrigin::root(), 0, 1, true, 1)); + assert_ok!(Assets::mint(RuntimeOrigin::signed(1), 0, 1, 100)); + assert_ok!(Assets::mint(RuntimeOrigin::signed(1), 0, 2, 100)); + assert_eq!(Assets::balance(0, 1), 100); + assert_eq!(Assets::balance(0, 2), 100); + assert_ok!(Assets::block(RuntimeOrigin::signed(1), 0, 1)); + assert_noop!(Assets::transfer(RuntimeOrigin::signed(2), 0, 1, 50), TokenError::Blocked); + assert_ok!(Assets::thaw(RuntimeOrigin::signed(1), 0, 1)); + assert_ok!(Assets::transfer(RuntimeOrigin::signed(2), 0, 1, 50)); + assert_ok!(Assets::transfer(RuntimeOrigin::signed(1), 0, 2, 50)); + }); +} + #[test] fn origin_guards_should_work() { new_test_ext().execute_with(|| { @@ -719,7 +851,7 @@ fn set_team_should_work() { } #[test] -fn transferring_to_frozen_account_should_work() { +fn transferring_from_frozen_account_should_not_work() { new_test_ext().execute_with(|| { assert_ok!(Assets::force_create(RuntimeOrigin::root(), 0, 1, true, 1)); assert_ok!(Assets::mint(RuntimeOrigin::signed(1), 0, 1, 100)); @@ -727,11 +859,226 @@ fn transferring_to_frozen_account_should_work() { assert_eq!(Assets::balance(0, 1), 100); assert_eq!(Assets::balance(0, 2), 100); assert_ok!(Assets::freeze(RuntimeOrigin::signed(1), 0, 2)); + // can transfer to `2` assert_ok!(Assets::transfer(RuntimeOrigin::signed(1), 0, 2, 50)); + // cannot transfer from `2` + assert_noop!(Assets::transfer(RuntimeOrigin::signed(2), 0, 1, 25), Error::::Frozen); + assert_eq!(Assets::balance(0, 1), 50); assert_eq!(Assets::balance(0, 2), 150); }); } +#[test] +fn touching_and_freezing_account_with_zero_asset_balance_should_work() { + new_test_ext().execute_with(|| { + // need some deposit for the touch + Balances::make_free_balance_be(&2, 100); + assert_ok!(Assets::force_create(RuntimeOrigin::root(), 0, 1, true, 1)); + assert_ok!(Assets::mint(RuntimeOrigin::signed(1), 0, 1, 100)); + assert_eq!(Assets::balance(0, 1), 100); + assert_eq!(Assets::balance(0, 2), 0); + // cannot freeze an account that doesn't have an `Assets` entry + assert_noop!(Assets::freeze(RuntimeOrigin::signed(1), 0, 2), Error::::NoAccount); + assert_ok!(Assets::touch(RuntimeOrigin::signed(2), 0)); + // now it can be frozen + assert_ok!(Assets::freeze(RuntimeOrigin::signed(1), 0, 2)); + // can transfer to `2` even though its frozen + assert_ok!(Assets::transfer(RuntimeOrigin::signed(1), 0, 2, 50)); + // cannot transfer from `2` + assert_noop!(Assets::transfer(RuntimeOrigin::signed(2), 0, 1, 25), Error::::Frozen); + assert_eq!(Assets::balance(0, 1), 50); + assert_eq!(Assets::balance(0, 2), 50); + }); +} + +#[test] +fn touch_other_works() { + new_test_ext().execute_with(|| { + // 1 will be admin + // 2 will be freezer + // 4 will be an account attempting to execute `touch_other` + Balances::make_free_balance_be(&1, 100); + Balances::make_free_balance_be(&2, 100); + Balances::make_free_balance_be(&4, 100); + assert_ok!(Assets::force_create(RuntimeOrigin::root(), 0, 1, false, 1)); + assert_ok!(Assets::set_team(RuntimeOrigin::signed(1), 0, 1, 1, 2)); + assert_ok!(Assets::mint(RuntimeOrigin::signed(1), 0, 1, 100)); + assert_eq!(Assets::balance(0, 1), 100); + // account `3` does not exist + assert!(!Account::::contains_key(0, &3)); + // creation of asset account `3` by account `4` fails + assert_noop!( + Assets::touch_other(RuntimeOrigin::signed(4), 0, 3), + Error::::NoPermission + ); + // creation of asset account `3` by admin `1` works + assert!(!Account::::contains_key(0, &3)); + assert_ok!(Assets::touch_other(RuntimeOrigin::signed(1), 0, 3)); + assert!(Account::::contains_key(0, &3)); + // creation of asset account `4` by freezer `2` works + assert!(!Account::::contains_key(0, &4)); + assert_ok!(Assets::touch_other(RuntimeOrigin::signed(2), 0, 4)); + assert!(Account::::contains_key(0, &4)); + }); +} + +#[test] +fn touch_other_and_freeze_works() { + new_test_ext().execute_with(|| { + Balances::make_free_balance_be(&1, 100); + assert_ok!(Assets::force_create(RuntimeOrigin::root(), 0, 1, true, 1)); + assert_ok!(Assets::mint(RuntimeOrigin::signed(1), 0, 1, 100)); + assert_eq!(Assets::balance(0, 1), 100); + // account `2` does not exist + assert!(!Account::::contains_key(0, &2)); + // create account `2` with touch_other + assert_ok!(Assets::touch_other(RuntimeOrigin::signed(1), 0, 2)); + assert!(Account::::contains_key(0, &2)); + // now it can be frozen + assert_ok!(Assets::freeze(RuntimeOrigin::signed(1), 0, 2)); + // can transfer to `2` even though its frozen + assert_ok!(Assets::transfer(RuntimeOrigin::signed(1), 0, 2, 50)); + // cannot transfer from `2` + assert_noop!(Assets::transfer(RuntimeOrigin::signed(2), 0, 1, 25), Error::::Frozen); + assert_eq!(Assets::balance(0, 1), 50); + assert_eq!(Assets::balance(0, 2), 50); + }); +} + +#[test] +fn account_with_deposit_not_destroyed() { + new_test_ext().execute_with(|| { + // 1 will be the asset admin + // 2 will exist without balance but with deposit + Balances::make_free_balance_be(&1, 100); + Balances::make_free_balance_be(&2, 100); + assert_ok!(Assets::force_create(RuntimeOrigin::root(), 0, 1, false, 1)); + assert_ok!(Assets::mint(RuntimeOrigin::signed(1), 0, 1, 100)); + assert_eq!(Assets::balance(0, 1), 100); + assert_eq!(Assets::balance(0, 2), 0); + // case 1; account `2` not destroyed with a holder's deposit + assert_ok!(Assets::touch(RuntimeOrigin::signed(2), 0)); + assert_eq!(Balances::reserved_balance(&2), 10); + assert!(Account::::contains_key(0, &2)); + assert_ok!(Assets::transfer(RuntimeOrigin::signed(1), 0, 2, 50)); + assert_ok!(Assets::transfer(RuntimeOrigin::signed(2), 0, 1, 50)); + assert_eq!(Assets::balance(0, 2), 0); + assert!(Account::::contains_key(0, &2)); + + // destroy account `2` + assert_ok!(Assets::refund(RuntimeOrigin::signed(2), 0, false)); + assert!(!Account::::contains_key(0, &2)); + + // case 2; account `2` not destroyed with a deposit from `1` + assert_ok!(Assets::touch_other(RuntimeOrigin::signed(1), 0, 2)); + assert_eq!(Balances::reserved_balance(&1), 10); + assert_ok!(Assets::transfer(RuntimeOrigin::signed(1), 0, 2, 50)); + assert_ok!(Assets::transfer(RuntimeOrigin::signed(2), 0, 1, 50)); + assert!(Account::::contains_key(0, &2)); + }); +} + +#[test] +fn refund_other_should_fails() { + new_test_ext().execute_with(|| { + // 1 will be the asset admin + // 2 will be the asset freezer + // 3 will be created with deposit of 2 + Balances::make_free_balance_be(&1, 100); + Balances::make_free_balance_be(&2, 100); + Balances::make_free_balance_be(&3, 0); + assert_ok!(Assets::force_create(RuntimeOrigin::root(), 0, 1, true, 1)); + assert_ok!(Assets::set_team(RuntimeOrigin::signed(1), 0, 1, 1, 2)); + assert!(!Account::::contains_key(0, &3)); + + // create asset account `3` with a deposit from freezer `2` + assert_ok!(Assets::touch_other(RuntimeOrigin::signed(2), 0, 3)); + assert_eq!(Balances::reserved_balance(&2), 10); + + // fail case; non-existing asset account `10` + assert_noop!( + Assets::refund_other(RuntimeOrigin::signed(2), 0, 10), + Error::::NoDeposit + ); + // fail case; non-existing asset `3` + assert_noop!( + Assets::refund_other(RuntimeOrigin::signed(2), 1, 3), + Error::::NoDeposit + ); + // fail case; no `DepositFrom` for asset account `1` + assert_noop!( + Assets::refund_other(RuntimeOrigin::signed(2), 0, 1), + Error::::NoDeposit + ); + // fail case; asset `0` is frozen + assert_ok!(Assets::freeze_asset(RuntimeOrigin::signed(2), 0)); + assert_noop!( + Assets::refund_other(RuntimeOrigin::signed(2), 0, 3), + Error::::AssetNotLive + ); + assert_ok!(Assets::thaw_asset(RuntimeOrigin::signed(1), 0)); + // fail case; asset `1` is being destroyed + assert_ok!(Assets::force_create(RuntimeOrigin::root(), 10, 1, true, 1)); + assert_ok!(Assets::touch_other(RuntimeOrigin::signed(1), 10, 3)); + assert_ok!(Assets::start_destroy(RuntimeOrigin::signed(1), 10)); + assert_noop!( + Assets::refund_other(RuntimeOrigin::signed(2), 10, 3), + Error::::AssetNotLive + ); + assert_ok!(Assets::destroy_accounts(RuntimeOrigin::signed(1), 10)); + assert_ok!(Assets::finish_destroy(RuntimeOrigin::signed(1), 10)); + // fail case; account is frozen + assert_ok!(Assets::freeze(RuntimeOrigin::signed(2), 0, 3)); + assert_noop!(Assets::refund_other(RuntimeOrigin::signed(2), 0, 3), Error::::Frozen); + assert_ok!(Assets::thaw(RuntimeOrigin::signed(1), 0, 3)); + // fail case; not a freezer or an admin + assert_noop!( + Assets::refund_other(RuntimeOrigin::signed(4), 0, 3), + Error::::NoPermission + ); + // fail case; would burn + assert_ok!(Assets::mint(RuntimeOrigin::signed(1), 0, 3, 100)); + assert_noop!( + Assets::refund_other(RuntimeOrigin::signed(1), 0, 3), + Error::::WouldBurn + ); + assert_ok!(Assets::burn(RuntimeOrigin::signed(1), 0, 3, 100)); + }) +} + +#[test] +fn refund_other_works() { + new_test_ext().execute_with(|| { + // 1 will be the asset admin + // 2 will be the asset freezer + // 3 will be created with deposit of 2 + Balances::make_free_balance_be(&1, 100); + Balances::make_free_balance_be(&2, 100); + assert_ok!(Assets::force_create(RuntimeOrigin::root(), 0, 1, true, 1)); + assert_ok!(Assets::set_team(RuntimeOrigin::signed(1), 0, 1, 1, 2)); + assert!(!Account::::contains_key(0, &3)); + assert_eq!(asset_account_counts(0), (0, 0)); + + // success case; freezer is depositor + assert_ok!(Assets::touch_other(RuntimeOrigin::signed(2), 0, 3)); + assert_eq!(Balances::reserved_balance(&2), 10); + assert_eq!(asset_account_counts(0), (1, 0)); + assert_ok!(Assets::refund_other(RuntimeOrigin::signed(2), 0, 3)); + assert_eq!(Balances::reserved_balance(&2), 0); + assert!(!Account::::contains_key(0, &3)); + assert_eq!(asset_account_counts(0), (0, 0)); + + // success case; admin is depositor + assert_ok!(Assets::touch_other(RuntimeOrigin::signed(1), 0, 3)); + assert_eq!(Balances::reserved_balance(&1), 10); + assert_eq!(asset_account_counts(0), (1, 0)); + assert_ok!(Assets::refund_other(RuntimeOrigin::signed(1), 0, 3)); + assert_eq!(Balances::reserved_balance(&1), 0); + assert!(!Account::::contains_key(0, &3)); + assert_eq!(asset_account_counts(0), (0, 0)); + }) +} + #[test] fn transferring_amount_more_than_available_balance_should_not_work() { new_test_ext().execute_with(|| { diff --git a/frame/assets/src/types.rs b/frame/assets/src/types.rs index c83e764f4..559afccb9 100644 --- a/frame/assets/src/types.rs +++ b/frame/assets/src/types.rs @@ -26,8 +26,14 @@ use sp_runtime::{traits::Convert, FixedPointNumber, FixedPointOperand, FixedU128 pub(super) type DepositBalanceOf = <>::Currency as Currency<::AccountId>>::Balance; -pub(super) type AssetAccountOf = - AssetAccount<>::Balance, DepositBalanceOf, >::Extra>; +pub(super) type AssetAccountOf = AssetAccount< + >::Balance, + DepositBalanceOf, + >::Extra, + ::AccountId, +>; +pub(super) type ExistenceReasonOf = + ExistenceReason, ::AccountId>; /// AssetStatus holds the current state of the asset. It could either be Live and available for use, /// or in a Destroying state. @@ -83,23 +89,35 @@ pub struct Approval { #[test] fn ensure_bool_decodes_to_consumer_or_sufficient() { - assert_eq!(false.encode(), ExistenceReason::<()>::Consumer.encode()); - assert_eq!(true.encode(), ExistenceReason::<()>::Sufficient.encode()); + assert_eq!(false.encode(), ExistenceReason::<(), ()>::Consumer.encode()); + assert_eq!(true.encode(), ExistenceReason::<(), ()>::Sufficient.encode()); } +/// The reason for an account's existence within an asset class. #[derive(Clone, Encode, Decode, Eq, PartialEq, RuntimeDebug, MaxEncodedLen, TypeInfo)] -pub enum ExistenceReason { +pub enum ExistenceReason { + /// A consumer reference was used to create this account. #[codec(index = 0)] Consumer, + /// The asset class is `sufficient` for account existence. #[codec(index = 1)] Sufficient, + /// The account holder has placed a deposit to exist within an asset class. #[codec(index = 2)] DepositHeld(Balance), + /// A deposit was placed for this account to exist, but it has been refunded. #[codec(index = 3)] DepositRefunded, + /// Some other `AccountId` has placed a deposit to make this account exist. + /// An account with such a reason might not be referenced in `system`. + #[codec(index = 4)] + DepositFrom(AccountId, Balance), } -impl ExistenceReason { +impl ExistenceReason +where + AccountId: Clone, +{ pub(crate) fn take_deposit(&mut self) -> Option { if !matches!(self, ExistenceReason::DepositHeld(_)) { return None @@ -112,16 +130,56 @@ impl ExistenceReason { None } } + + pub(crate) fn take_deposit_from(&mut self) -> Option<(AccountId, Balance)> { + if !matches!(self, ExistenceReason::DepositFrom(..)) { + return None + } + if let ExistenceReason::DepositFrom(depositor, deposit) = + sp_std::mem::replace(self, ExistenceReason::DepositRefunded) + { + Some((depositor, deposit)) + } else { + None + } + } } +#[test] +fn ensure_bool_decodes_to_liquid_or_frozen() { + assert_eq!(false.encode(), AccountStatus::Liquid.encode()); + assert_eq!(true.encode(), AccountStatus::Frozen.encode()); +} + +/// The status of an asset account. #[derive(Clone, Encode, Decode, Eq, PartialEq, RuntimeDebug, MaxEncodedLen, TypeInfo)] -pub struct AssetAccount { +pub enum AccountStatus { + /// Asset account can receive and transfer the assets. + Liquid, + /// Asset account cannot transfer the assets. + Frozen, + /// Asset account cannot receive and transfer the assets. + Blocked, +} +impl AccountStatus { + /// Returns `true` if frozen or blocked. + pub(crate) fn is_frozen(&self) -> bool { + matches!(self, AccountStatus::Frozen | AccountStatus::Blocked) + } + /// Returns `true` if blocked. + pub(crate) fn is_blocked(&self) -> bool { + matches!(self, AccountStatus::Blocked) + } +} + +#[derive(Clone, Encode, Decode, Eq, PartialEq, RuntimeDebug, MaxEncodedLen, TypeInfo)] +pub struct AssetAccount { /// The balance. pub(super) balance: Balance, - /// Whether the account is frozen. - pub(super) is_frozen: bool, + /// The status of the account. + pub(super) status: AccountStatus, /// The reason for the existence of the account. - pub(super) reason: ExistenceReason, + pub(super) reason: ExistenceReason, /// Additional "sidecar" data, in case some other pallet wants to use this storage item. pub(super) extra: Extra, } diff --git a/frame/assets/src/weights.rs b/frame/assets/src/weights.rs index 4cc90e25f..76ac585ff 100644 --- a/frame/assets/src/weights.rs +++ b/frame/assets/src/weights.rs @@ -18,33 +18,35 @@ //! Autogenerated weights for pallet_assets //! //! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev -//! DATE: 2023-04-06, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! DATE: 2023-05-01, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` //! WORST CASE MAP SIZE: `1000000` -//! HOSTNAME: `bm2`, CPU: `Intel(R) Core(TM) i7-7700K CPU @ 4.20GHz` +//! HOSTNAME: `bm3`, CPU: `Intel(R) Core(TM) i7-7700K CPU @ 4.20GHz` //! EXECUTION: Some(Wasm), WASM-EXECUTION: Compiled, CHAIN: Some("dev"), DB CACHE: 1024 // Executed Command: -// ./target/production/substrate +// target/production/substrate // benchmark // pallet -// --chain=dev // --steps=50 // --repeat=20 -// --pallet=pallet_assets // --extrinsic=* // --execution=wasm // --wasm-execution=compiled // --heap-pages=4096 -// --output=./frame/assets/src/weights.rs +// --json-file=/var/lib/gitlab-runner/builds/zyw4fam_/0/parity/mirrors/substrate/.git/.artifacts/bench.json +// --pallet=pallet_assets +// --chain=dev // --header=./HEADER-APACHE2 +// --output=./frame/assets/src/weights.rs // --template=./.maintain/frame-weight-template.hbs #![cfg_attr(rustfmt, rustfmt_skip)] #![allow(unused_parens)] #![allow(unused_imports)] +#![allow(missing_docs)] use frame_support::{traits::Get, weights::{Weight, constants::RocksDbWeight}}; -use sp_std::marker::PhantomData; +use core::marker::PhantomData; /// Weight functions needed for pallet_assets. pub trait WeightInfo { @@ -75,6 +77,11 @@ pub trait WeightInfo { fn cancel_approval() -> Weight; fn force_cancel_approval() -> Weight; fn set_min_balance() -> Weight; + fn touch() -> Weight; + fn touch_other() -> Weight; + fn refund() -> Weight; + fn refund_other() -> Weight; + fn block() -> Weight; } /// Weights for pallet_assets using the Substrate node and recommended hardware. @@ -88,8 +95,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `293` // Estimated: `3675` - // Minimum execution time: 33_678_000 picoseconds. - Weight::from_parts(34_320_000, 3675) + // Minimum execution time: 31_668_000 picoseconds. + Weight::from_parts(32_079_000, 3675) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().writes(2_u64)) } @@ -99,8 +106,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `153` // Estimated: `3675` - // Minimum execution time: 15_521_000 picoseconds. - Weight::from_parts(16_035_000, 3675) + // Minimum execution time: 14_885_000 picoseconds. + Weight::from_parts(15_358_000, 3675) .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -110,31 +117,31 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `385` // Estimated: `3675` - // Minimum execution time: 15_921_000 picoseconds. - Weight::from_parts(16_153_000, 3675) + // Minimum execution time: 15_295_000 picoseconds. + Weight::from_parts(15_639_000, 3675) .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } /// Storage: Assets Asset (r:1 w:1) /// Proof: Assets Asset (max_values: None, max_size: Some(210), added: 2685, mode: MaxEncodedLen) /// Storage: Assets Account (r:1001 w:1000) - /// Proof: Assets Account (max_values: None, max_size: Some(102), added: 2577, mode: MaxEncodedLen) + /// Proof: Assets Account (max_values: None, max_size: Some(134), added: 2609, mode: MaxEncodedLen) /// Storage: System Account (r:1000 w:1000) /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) /// The range of component `c` is `[0, 1000]`. fn destroy_accounts(c: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0 + c * (208 ±0)` - // Estimated: `3675 + c * (2603 ±0)` - // Minimum execution time: 21_242_000 picoseconds. - Weight::from_parts(21_532_000, 3675) - // Standard Error: 6_449 - .saturating_add(Weight::from_parts(13_150_845, 0).saturating_mul(c.into())) + // Estimated: `3675 + c * (2609 ±0)` + // Minimum execution time: 19_916_000 picoseconds. + Weight::from_parts(20_220_000, 3675) + // Standard Error: 7_298 + .saturating_add(Weight::from_parts(12_553_976, 0).saturating_mul(c.into())) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().reads((2_u64).saturating_mul(c.into()))) .saturating_add(T::DbWeight::get().writes(1_u64)) .saturating_add(T::DbWeight::get().writes((2_u64).saturating_mul(c.into()))) - .saturating_add(Weight::from_parts(0, 2603).saturating_mul(c.into())) + .saturating_add(Weight::from_parts(0, 2609).saturating_mul(c.into())) } /// Storage: Assets Asset (r:1 w:1) /// Proof: Assets Asset (max_values: None, max_size: Some(210), added: 2685, mode: MaxEncodedLen) @@ -145,10 +152,10 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `522 + a * (86 ±0)` // Estimated: `3675 + a * (2623 ±0)` - // Minimum execution time: 21_604_000 picoseconds. - Weight::from_parts(21_881_000, 3675) - // Standard Error: 4_225 - .saturating_add(Weight::from_parts(15_968_205, 0).saturating_mul(a.into())) + // Minimum execution time: 20_322_000 picoseconds. + Weight::from_parts(20_744_000, 3675) + // Standard Error: 12_314 + .saturating_add(Weight::from_parts(14_767_353, 0).saturating_mul(a.into())) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().reads((1_u64).saturating_mul(a.into()))) .saturating_add(T::DbWeight::get().writes(1_u64)) @@ -163,105 +170,105 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `351` // Estimated: `3675` - // Minimum execution time: 16_465_000 picoseconds. - Weight::from_parts(16_775_000, 3675) + // Minimum execution time: 15_668_000 picoseconds. + Weight::from_parts(16_016_000, 3675) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } /// Storage: Assets Asset (r:1 w:1) /// Proof: Assets Asset (max_values: None, max_size: Some(210), added: 2685, mode: MaxEncodedLen) /// Storage: Assets Account (r:1 w:1) - /// Proof: Assets Account (max_values: None, max_size: Some(102), added: 2577, mode: MaxEncodedLen) + /// Proof: Assets Account (max_values: None, max_size: Some(134), added: 2609, mode: MaxEncodedLen) fn mint() -> Weight { // Proof Size summary in bytes: // Measured: `351` // Estimated: `3675` - // Minimum execution time: 30_709_000 picoseconds. - Weight::from_parts(31_159_000, 3675) + // Minimum execution time: 28_227_000 picoseconds. + Weight::from_parts(28_769_000, 3675) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().writes(2_u64)) } /// Storage: Assets Asset (r:1 w:1) /// Proof: Assets Asset (max_values: None, max_size: Some(210), added: 2685, mode: MaxEncodedLen) /// Storage: Assets Account (r:1 w:1) - /// Proof: Assets Account (max_values: None, max_size: Some(102), added: 2577, mode: MaxEncodedLen) + /// Proof: Assets Account (max_values: None, max_size: Some(134), added: 2609, mode: MaxEncodedLen) fn burn() -> Weight { // Proof Size summary in bytes: // Measured: `459` // Estimated: `3675` - // Minimum execution time: 36_944_000 picoseconds. - Weight::from_parts(38_166_000, 3675) + // Minimum execution time: 34_672_000 picoseconds. + Weight::from_parts(34_902_000, 3675) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().writes(2_u64)) } /// Storage: Assets Asset (r:1 w:1) /// Proof: Assets Asset (max_values: None, max_size: Some(210), added: 2685, mode: MaxEncodedLen) /// Storage: Assets Account (r:2 w:2) - /// Proof: Assets Account (max_values: None, max_size: Some(102), added: 2577, mode: MaxEncodedLen) + /// Proof: Assets Account (max_values: None, max_size: Some(134), added: 2609, mode: MaxEncodedLen) /// Storage: System Account (r:1 w:1) /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) fn transfer() -> Weight { // Proof Size summary in bytes: // Measured: `498` - // Estimated: `6144` - // Minimum execution time: 52_497_000 picoseconds. - Weight::from_parts(53_147_000, 6144) + // Estimated: `6208` + // Minimum execution time: 49_003_000 picoseconds. + Weight::from_parts(49_345_000, 6208) .saturating_add(T::DbWeight::get().reads(4_u64)) .saturating_add(T::DbWeight::get().writes(4_u64)) } /// Storage: Assets Asset (r:1 w:1) /// Proof: Assets Asset (max_values: None, max_size: Some(210), added: 2685, mode: MaxEncodedLen) /// Storage: Assets Account (r:2 w:2) - /// Proof: Assets Account (max_values: None, max_size: Some(102), added: 2577, mode: MaxEncodedLen) + /// Proof: Assets Account (max_values: None, max_size: Some(134), added: 2609, mode: MaxEncodedLen) /// Storage: System Account (r:1 w:1) /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) fn transfer_keep_alive() -> Weight { // Proof Size summary in bytes: // Measured: `498` - // Estimated: `6144` - // Minimum execution time: 46_203_000 picoseconds. - Weight::from_parts(46_853_000, 6144) + // Estimated: `6208` + // Minimum execution time: 43_429_000 picoseconds. + Weight::from_parts(43_936_000, 6208) .saturating_add(T::DbWeight::get().reads(4_u64)) .saturating_add(T::DbWeight::get().writes(4_u64)) } /// Storage: Assets Asset (r:1 w:1) /// Proof: Assets Asset (max_values: None, max_size: Some(210), added: 2685, mode: MaxEncodedLen) /// Storage: Assets Account (r:2 w:2) - /// Proof: Assets Account (max_values: None, max_size: Some(102), added: 2577, mode: MaxEncodedLen) + /// Proof: Assets Account (max_values: None, max_size: Some(134), added: 2609, mode: MaxEncodedLen) /// Storage: System Account (r:1 w:1) /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) fn force_transfer() -> Weight { // Proof Size summary in bytes: // Measured: `498` - // Estimated: `6144` - // Minimum execution time: 52_911_000 picoseconds. - Weight::from_parts(55_511_000, 6144) + // Estimated: `6208` + // Minimum execution time: 49_177_000 picoseconds. + Weight::from_parts(49_548_000, 6208) .saturating_add(T::DbWeight::get().reads(4_u64)) .saturating_add(T::DbWeight::get().writes(4_u64)) } /// Storage: Assets Asset (r:1 w:0) /// Proof: Assets Asset (max_values: None, max_size: Some(210), added: 2685, mode: MaxEncodedLen) /// Storage: Assets Account (r:1 w:1) - /// Proof: Assets Account (max_values: None, max_size: Some(102), added: 2577, mode: MaxEncodedLen) + /// Proof: Assets Account (max_values: None, max_size: Some(134), added: 2609, mode: MaxEncodedLen) fn freeze() -> Weight { // Proof Size summary in bytes: // Measured: `459` // Estimated: `3675` - // Minimum execution time: 20_308_000 picoseconds. - Weight::from_parts(20_524_000, 3675) + // Minimum execution time: 19_323_000 picoseconds. + Weight::from_parts(19_945_000, 3675) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } /// Storage: Assets Asset (r:1 w:0) /// Proof: Assets Asset (max_values: None, max_size: Some(210), added: 2685, mode: MaxEncodedLen) /// Storage: Assets Account (r:1 w:1) - /// Proof: Assets Account (max_values: None, max_size: Some(102), added: 2577, mode: MaxEncodedLen) + /// Proof: Assets Account (max_values: None, max_size: Some(134), added: 2609, mode: MaxEncodedLen) fn thaw() -> Weight { // Proof Size summary in bytes: // Measured: `459` // Estimated: `3675` - // Minimum execution time: 20_735_000 picoseconds. - Weight::from_parts(21_026_000, 3675) + // Minimum execution time: 19_543_000 picoseconds. + Weight::from_parts(19_747_000, 3675) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -271,8 +278,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `385` // Estimated: `3675` - // Minimum execution time: 16_148_000 picoseconds. - Weight::from_parts(16_404_000, 3675) + // Minimum execution time: 15_623_000 picoseconds. + Weight::from_parts(15_833_000, 3675) .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -282,8 +289,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `385` // Estimated: `3675` - // Minimum execution time: 16_075_000 picoseconds. - Weight::from_parts(16_542_000, 3675) + // Minimum execution time: 15_396_000 picoseconds. + Weight::from_parts(15_704_000, 3675) .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -295,8 +302,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `351` // Estimated: `3675` - // Minimum execution time: 17_866_000 picoseconds. - Weight::from_parts(18_129_000, 3675) + // Minimum execution time: 17_205_000 picoseconds. + Weight::from_parts(17_546_000, 3675) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -306,8 +313,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `351` // Estimated: `3675` - // Minimum execution time: 16_637_000 picoseconds. - Weight::from_parts(16_918_000, 3675) + // Minimum execution time: 16_049_000 picoseconds. + Weight::from_parts(16_317_000, 3675) .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -317,14 +324,16 @@ impl WeightInfo for SubstrateWeight { /// Proof: Assets Metadata (max_values: None, max_size: Some(140), added: 2615, mode: MaxEncodedLen) /// The range of component `n` is `[0, 50]`. /// The range of component `s` is `[0, 50]`. - fn set_metadata(_n: u32, s: u32, ) -> Weight { + fn set_metadata(n: u32, s: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `351` // Estimated: `3675` - // Minimum execution time: 33_304_000 picoseconds. - Weight::from_parts(34_764_544, 3675) - // Standard Error: 634 - .saturating_add(Weight::from_parts(4_733, 0).saturating_mul(s.into())) + // Minimum execution time: 31_574_000 picoseconds. + Weight::from_parts(32_447_787, 3675) + // Standard Error: 904 + .saturating_add(Weight::from_parts(653, 0).saturating_mul(n.into())) + // Standard Error: 904 + .saturating_add(Weight::from_parts(271, 0).saturating_mul(s.into())) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -336,8 +345,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `515` // Estimated: `3675` - // Minimum execution time: 33_640_000 picoseconds. - Weight::from_parts(34_100_000, 3675) + // Minimum execution time: 31_865_000 picoseconds. + Weight::from_parts(32_160_000, 3675) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -351,12 +360,12 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `190` // Estimated: `3675` - // Minimum execution time: 16_549_000 picoseconds. - Weight::from_parts(17_337_982, 3675) - // Standard Error: 586 - .saturating_add(Weight::from_parts(4_584, 0).saturating_mul(n.into())) - // Standard Error: 586 - .saturating_add(Weight::from_parts(4_288, 0).saturating_mul(s.into())) + // Minimum execution time: 16_203_000 picoseconds. + Weight::from_parts(16_432_499, 3675) + // Standard Error: 1_563 + .saturating_add(Weight::from_parts(5_818, 0).saturating_mul(n.into())) + // Standard Error: 1_563 + .saturating_add(Weight::from_parts(9_660, 0).saturating_mul(s.into())) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -368,8 +377,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `515` // Estimated: `3675` - // Minimum execution time: 34_183_000 picoseconds. - Weight::from_parts(34_577_000, 3675) + // Minimum execution time: 33_443_000 picoseconds. + Weight::from_parts(56_533_000, 3675) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -379,8 +388,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `351` // Estimated: `3675` - // Minimum execution time: 15_505_000 picoseconds. - Weight::from_parts(15_784_000, 3675) + // Minimum execution time: 20_636_000 picoseconds. + Weight::from_parts(23_960_000, 3675) .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -392,8 +401,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `385` // Estimated: `3675` - // Minimum execution time: 38_762_000 picoseconds. - Weight::from_parts(39_138_000, 3675) + // Minimum execution time: 35_987_000 picoseconds. + Weight::from_parts(36_429_000, 3675) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().writes(2_u64)) } @@ -402,15 +411,15 @@ impl WeightInfo for SubstrateWeight { /// Storage: Assets Approvals (r:1 w:1) /// Proof: Assets Approvals (max_values: None, max_size: Some(148), added: 2623, mode: MaxEncodedLen) /// Storage: Assets Account (r:2 w:2) - /// Proof: Assets Account (max_values: None, max_size: Some(102), added: 2577, mode: MaxEncodedLen) + /// Proof: Assets Account (max_values: None, max_size: Some(134), added: 2609, mode: MaxEncodedLen) /// Storage: System Account (r:1 w:1) /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) fn transfer_approved() -> Weight { // Proof Size summary in bytes: // Measured: `668` - // Estimated: `6144` - // Minimum execution time: 73_396_000 picoseconds. - Weight::from_parts(73_986_000, 6144) + // Estimated: `6208` + // Minimum execution time: 68_059_000 picoseconds. + Weight::from_parts(69_845_000, 6208) .saturating_add(T::DbWeight::get().reads(5_u64)) .saturating_add(T::DbWeight::get().writes(5_u64)) } @@ -422,8 +431,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `555` // Estimated: `3675` - // Minimum execution time: 39_707_000 picoseconds. - Weight::from_parts(40_222_000, 3675) + // Minimum execution time: 38_066_000 picoseconds. + Weight::from_parts(38_450_000, 3675) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().writes(2_u64)) } @@ -435,8 +444,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `555` // Estimated: `3675` - // Minimum execution time: 40_357_000 picoseconds. - Weight::from_parts(40_731_000, 3675) + // Minimum execution time: 38_500_000 picoseconds. + Weight::from_parts(38_953_000, 3675) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().writes(2_u64)) } @@ -446,11 +455,80 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `351` // Estimated: `3675` - // Minimum execution time: 16_937_000 picoseconds. - Weight::from_parts(17_219_000, 3675) + // Minimum execution time: 16_268_000 picoseconds. + Weight::from_parts(16_764_000, 3675) .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } + /// Storage: Assets Account (r:1 w:1) + /// Proof: Assets Account (max_values: None, max_size: Some(134), added: 2609, mode: MaxEncodedLen) + /// Storage: Assets Asset (r:1 w:1) + /// Proof: Assets Asset (max_values: None, max_size: Some(210), added: 2685, mode: MaxEncodedLen) + /// Storage: System Account (r:1 w:1) + /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) + fn touch() -> Weight { + // Proof Size summary in bytes: + // Measured: `453` + // Estimated: `3675` + // Minimum execution time: 37_468_000 picoseconds. + Weight::from_parts(37_957_000, 3675) + .saturating_add(T::DbWeight::get().reads(3_u64)) + .saturating_add(T::DbWeight::get().writes(3_u64)) + } + /// Storage: Assets Account (r:1 w:1) + /// Proof: Assets Account (max_values: None, max_size: Some(134), added: 2609, mode: MaxEncodedLen) + /// Storage: Assets Asset (r:1 w:1) + /// Proof: Assets Asset (max_values: None, max_size: Some(210), added: 2685, mode: MaxEncodedLen) + fn touch_other() -> Weight { + // Proof Size summary in bytes: + // Measured: `351` + // Estimated: `3675` + // Minimum execution time: 383_408_000 picoseconds. + Weight::from_parts(392_036_000, 3675) + .saturating_add(T::DbWeight::get().reads(2)) + .saturating_add(T::DbWeight::get().writes(2)) + } + /// Storage: Assets Account (r:1 w:1) + /// Proof: Assets Account (max_values: None, max_size: Some(134), added: 2609, mode: MaxEncodedLen) + /// Storage: Assets Asset (r:1 w:1) + /// Proof: Assets Asset (max_values: None, max_size: Some(210), added: 2685, mode: MaxEncodedLen) + /// Storage: System Account (r:1 w:1) + /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) + fn refund() -> Weight { + // Proof Size summary in bytes: + // Measured: `579` + // Estimated: `3675` + // Minimum execution time: 34_066_000 picoseconds. + Weight::from_parts(34_347_000, 3675) + .saturating_add(T::DbWeight::get().reads(3_u64)) + .saturating_add(T::DbWeight::get().writes(3_u64)) + } + /// Storage: Assets Account (r:1 w:1) + /// Proof: Assets Account (max_values: None, max_size: Some(134), added: 2609, mode: MaxEncodedLen) + /// Storage: Assets Asset (r:1 w:1) + /// Proof: Assets Asset (max_values: None, max_size: Some(210), added: 2685, mode: MaxEncodedLen) + fn refund_other() -> Weight { + // Proof Size summary in bytes: + // Measured: `510` + // Estimated: `3675` + // Minimum execution time: 32_060_000 picoseconds. + Weight::from_parts(32_519_000, 3675) + .saturating_add(T::DbWeight::get().reads(2_u64)) + .saturating_add(T::DbWeight::get().writes(2_u64)) + } + /// Storage: Assets Asset (r:1 w:0) + /// Proof: Assets Asset (max_values: None, max_size: Some(210), added: 2685, mode: MaxEncodedLen) + /// Storage: Assets Account (r:1 w:1) + /// Proof: Assets Account (max_values: None, max_size: Some(134), added: 2609, mode: MaxEncodedLen) + fn block() -> Weight { + // Proof Size summary in bytes: + // Measured: `459` + // Estimated: `3675` + // Minimum execution time: 115_000_000 picoseconds. + Weight::from_parts(163_000_000, 3675) + .saturating_add(T::DbWeight::get().reads(2_u64)) + .saturating_add(T::DbWeight::get().writes(1_u64)) + } } // For backwards compatibility and tests @@ -463,8 +541,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `293` // Estimated: `3675` - // Minimum execution time: 33_678_000 picoseconds. - Weight::from_parts(34_320_000, 3675) + // Minimum execution time: 31_668_000 picoseconds. + Weight::from_parts(32_079_000, 3675) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().writes(2_u64)) } @@ -474,8 +552,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `153` // Estimated: `3675` - // Minimum execution time: 15_521_000 picoseconds. - Weight::from_parts(16_035_000, 3675) + // Minimum execution time: 14_885_000 picoseconds. + Weight::from_parts(15_358_000, 3675) .saturating_add(RocksDbWeight::get().reads(1_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } @@ -485,31 +563,31 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `385` // Estimated: `3675` - // Minimum execution time: 15_921_000 picoseconds. - Weight::from_parts(16_153_000, 3675) + // Minimum execution time: 15_295_000 picoseconds. + Weight::from_parts(15_639_000, 3675) .saturating_add(RocksDbWeight::get().reads(1_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } /// Storage: Assets Asset (r:1 w:1) /// Proof: Assets Asset (max_values: None, max_size: Some(210), added: 2685, mode: MaxEncodedLen) /// Storage: Assets Account (r:1001 w:1000) - /// Proof: Assets Account (max_values: None, max_size: Some(102), added: 2577, mode: MaxEncodedLen) + /// Proof: Assets Account (max_values: None, max_size: Some(134), added: 2609, mode: MaxEncodedLen) /// Storage: System Account (r:1000 w:1000) /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) /// The range of component `c` is `[0, 1000]`. fn destroy_accounts(c: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0 + c * (208 ±0)` - // Estimated: `3675 + c * (2603 ±0)` - // Minimum execution time: 21_242_000 picoseconds. - Weight::from_parts(21_532_000, 3675) - // Standard Error: 6_449 - .saturating_add(Weight::from_parts(13_150_845, 0).saturating_mul(c.into())) + // Estimated: `3675 + c * (2609 ±0)` + // Minimum execution time: 19_916_000 picoseconds. + Weight::from_parts(20_220_000, 3675) + // Standard Error: 7_298 + .saturating_add(Weight::from_parts(12_553_976, 0).saturating_mul(c.into())) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().reads((2_u64).saturating_mul(c.into()))) .saturating_add(RocksDbWeight::get().writes(1_u64)) .saturating_add(RocksDbWeight::get().writes((2_u64).saturating_mul(c.into()))) - .saturating_add(Weight::from_parts(0, 2603).saturating_mul(c.into())) + .saturating_add(Weight::from_parts(0, 2609).saturating_mul(c.into())) } /// Storage: Assets Asset (r:1 w:1) /// Proof: Assets Asset (max_values: None, max_size: Some(210), added: 2685, mode: MaxEncodedLen) @@ -520,10 +598,10 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `522 + a * (86 ±0)` // Estimated: `3675 + a * (2623 ±0)` - // Minimum execution time: 21_604_000 picoseconds. - Weight::from_parts(21_881_000, 3675) - // Standard Error: 4_225 - .saturating_add(Weight::from_parts(15_968_205, 0).saturating_mul(a.into())) + // Minimum execution time: 20_322_000 picoseconds. + Weight::from_parts(20_744_000, 3675) + // Standard Error: 12_314 + .saturating_add(Weight::from_parts(14_767_353, 0).saturating_mul(a.into())) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().reads((1_u64).saturating_mul(a.into()))) .saturating_add(RocksDbWeight::get().writes(1_u64)) @@ -538,105 +616,105 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `351` // Estimated: `3675` - // Minimum execution time: 16_465_000 picoseconds. - Weight::from_parts(16_775_000, 3675) + // Minimum execution time: 15_668_000 picoseconds. + Weight::from_parts(16_016_000, 3675) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } /// Storage: Assets Asset (r:1 w:1) /// Proof: Assets Asset (max_values: None, max_size: Some(210), added: 2685, mode: MaxEncodedLen) /// Storage: Assets Account (r:1 w:1) - /// Proof: Assets Account (max_values: None, max_size: Some(102), added: 2577, mode: MaxEncodedLen) + /// Proof: Assets Account (max_values: None, max_size: Some(134), added: 2609, mode: MaxEncodedLen) fn mint() -> Weight { // Proof Size summary in bytes: // Measured: `351` // Estimated: `3675` - // Minimum execution time: 30_709_000 picoseconds. - Weight::from_parts(31_159_000, 3675) + // Minimum execution time: 28_227_000 picoseconds. + Weight::from_parts(28_769_000, 3675) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().writes(2_u64)) } /// Storage: Assets Asset (r:1 w:1) /// Proof: Assets Asset (max_values: None, max_size: Some(210), added: 2685, mode: MaxEncodedLen) /// Storage: Assets Account (r:1 w:1) - /// Proof: Assets Account (max_values: None, max_size: Some(102), added: 2577, mode: MaxEncodedLen) + /// Proof: Assets Account (max_values: None, max_size: Some(134), added: 2609, mode: MaxEncodedLen) fn burn() -> Weight { // Proof Size summary in bytes: // Measured: `459` // Estimated: `3675` - // Minimum execution time: 36_944_000 picoseconds. - Weight::from_parts(38_166_000, 3675) + // Minimum execution time: 34_672_000 picoseconds. + Weight::from_parts(34_902_000, 3675) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().writes(2_u64)) } /// Storage: Assets Asset (r:1 w:1) /// Proof: Assets Asset (max_values: None, max_size: Some(210), added: 2685, mode: MaxEncodedLen) /// Storage: Assets Account (r:2 w:2) - /// Proof: Assets Account (max_values: None, max_size: Some(102), added: 2577, mode: MaxEncodedLen) + /// Proof: Assets Account (max_values: None, max_size: Some(134), added: 2609, mode: MaxEncodedLen) /// Storage: System Account (r:1 w:1) /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) fn transfer() -> Weight { // Proof Size summary in bytes: // Measured: `498` - // Estimated: `6144` - // Minimum execution time: 52_497_000 picoseconds. - Weight::from_parts(53_147_000, 6144) + // Estimated: `6208` + // Minimum execution time: 49_003_000 picoseconds. + Weight::from_parts(49_345_000, 6208) .saturating_add(RocksDbWeight::get().reads(4_u64)) .saturating_add(RocksDbWeight::get().writes(4_u64)) } /// Storage: Assets Asset (r:1 w:1) /// Proof: Assets Asset (max_values: None, max_size: Some(210), added: 2685, mode: MaxEncodedLen) /// Storage: Assets Account (r:2 w:2) - /// Proof: Assets Account (max_values: None, max_size: Some(102), added: 2577, mode: MaxEncodedLen) + /// Proof: Assets Account (max_values: None, max_size: Some(134), added: 2609, mode: MaxEncodedLen) /// Storage: System Account (r:1 w:1) /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) fn transfer_keep_alive() -> Weight { // Proof Size summary in bytes: // Measured: `498` - // Estimated: `6144` - // Minimum execution time: 46_203_000 picoseconds. - Weight::from_parts(46_853_000, 6144) + // Estimated: `6208` + // Minimum execution time: 43_429_000 picoseconds. + Weight::from_parts(43_936_000, 6208) .saturating_add(RocksDbWeight::get().reads(4_u64)) .saturating_add(RocksDbWeight::get().writes(4_u64)) } /// Storage: Assets Asset (r:1 w:1) /// Proof: Assets Asset (max_values: None, max_size: Some(210), added: 2685, mode: MaxEncodedLen) /// Storage: Assets Account (r:2 w:2) - /// Proof: Assets Account (max_values: None, max_size: Some(102), added: 2577, mode: MaxEncodedLen) + /// Proof: Assets Account (max_values: None, max_size: Some(134), added: 2609, mode: MaxEncodedLen) /// Storage: System Account (r:1 w:1) /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) fn force_transfer() -> Weight { // Proof Size summary in bytes: // Measured: `498` - // Estimated: `6144` - // Minimum execution time: 52_911_000 picoseconds. - Weight::from_parts(55_511_000, 6144) + // Estimated: `6208` + // Minimum execution time: 49_177_000 picoseconds. + Weight::from_parts(49_548_000, 6208) .saturating_add(RocksDbWeight::get().reads(4_u64)) .saturating_add(RocksDbWeight::get().writes(4_u64)) } /// Storage: Assets Asset (r:1 w:0) /// Proof: Assets Asset (max_values: None, max_size: Some(210), added: 2685, mode: MaxEncodedLen) /// Storage: Assets Account (r:1 w:1) - /// Proof: Assets Account (max_values: None, max_size: Some(102), added: 2577, mode: MaxEncodedLen) + /// Proof: Assets Account (max_values: None, max_size: Some(134), added: 2609, mode: MaxEncodedLen) fn freeze() -> Weight { // Proof Size summary in bytes: // Measured: `459` // Estimated: `3675` - // Minimum execution time: 20_308_000 picoseconds. - Weight::from_parts(20_524_000, 3675) + // Minimum execution time: 19_323_000 picoseconds. + Weight::from_parts(19_945_000, 3675) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } /// Storage: Assets Asset (r:1 w:0) /// Proof: Assets Asset (max_values: None, max_size: Some(210), added: 2685, mode: MaxEncodedLen) /// Storage: Assets Account (r:1 w:1) - /// Proof: Assets Account (max_values: None, max_size: Some(102), added: 2577, mode: MaxEncodedLen) + /// Proof: Assets Account (max_values: None, max_size: Some(134), added: 2609, mode: MaxEncodedLen) fn thaw() -> Weight { // Proof Size summary in bytes: // Measured: `459` // Estimated: `3675` - // Minimum execution time: 20_735_000 picoseconds. - Weight::from_parts(21_026_000, 3675) + // Minimum execution time: 19_543_000 picoseconds. + Weight::from_parts(19_747_000, 3675) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } @@ -646,8 +724,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `385` // Estimated: `3675` - // Minimum execution time: 16_148_000 picoseconds. - Weight::from_parts(16_404_000, 3675) + // Minimum execution time: 15_623_000 picoseconds. + Weight::from_parts(15_833_000, 3675) .saturating_add(RocksDbWeight::get().reads(1_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } @@ -657,8 +735,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `385` // Estimated: `3675` - // Minimum execution time: 16_075_000 picoseconds. - Weight::from_parts(16_542_000, 3675) + // Minimum execution time: 15_396_000 picoseconds. + Weight::from_parts(15_704_000, 3675) .saturating_add(RocksDbWeight::get().reads(1_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } @@ -670,8 +748,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `351` // Estimated: `3675` - // Minimum execution time: 17_866_000 picoseconds. - Weight::from_parts(18_129_000, 3675) + // Minimum execution time: 17_205_000 picoseconds. + Weight::from_parts(17_546_000, 3675) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } @@ -681,8 +759,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `351` // Estimated: `3675` - // Minimum execution time: 16_637_000 picoseconds. - Weight::from_parts(16_918_000, 3675) + // Minimum execution time: 16_049_000 picoseconds. + Weight::from_parts(16_317_000, 3675) .saturating_add(RocksDbWeight::get().reads(1_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } @@ -692,14 +770,16 @@ impl WeightInfo for () { /// Proof: Assets Metadata (max_values: None, max_size: Some(140), added: 2615, mode: MaxEncodedLen) /// The range of component `n` is `[0, 50]`. /// The range of component `s` is `[0, 50]`. - fn set_metadata(_n: u32, s: u32, ) -> Weight { + fn set_metadata(n: u32, s: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `351` // Estimated: `3675` - // Minimum execution time: 33_304_000 picoseconds. - Weight::from_parts(34_764_544, 3675) - // Standard Error: 634 - .saturating_add(Weight::from_parts(4_733, 0).saturating_mul(s.into())) + // Minimum execution time: 31_574_000 picoseconds. + Weight::from_parts(32_447_787, 3675) + // Standard Error: 904 + .saturating_add(Weight::from_parts(653, 0).saturating_mul(n.into())) + // Standard Error: 904 + .saturating_add(Weight::from_parts(271, 0).saturating_mul(s.into())) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } @@ -711,8 +791,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `515` // Estimated: `3675` - // Minimum execution time: 33_640_000 picoseconds. - Weight::from_parts(34_100_000, 3675) + // Minimum execution time: 31_865_000 picoseconds. + Weight::from_parts(32_160_000, 3675) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } @@ -726,12 +806,12 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `190` // Estimated: `3675` - // Minimum execution time: 16_549_000 picoseconds. - Weight::from_parts(17_337_982, 3675) - // Standard Error: 586 - .saturating_add(Weight::from_parts(4_584, 0).saturating_mul(n.into())) - // Standard Error: 586 - .saturating_add(Weight::from_parts(4_288, 0).saturating_mul(s.into())) + // Minimum execution time: 16_203_000 picoseconds. + Weight::from_parts(16_432_499, 3675) + // Standard Error: 1_563 + .saturating_add(Weight::from_parts(5_818, 0).saturating_mul(n.into())) + // Standard Error: 1_563 + .saturating_add(Weight::from_parts(9_660, 0).saturating_mul(s.into())) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } @@ -743,8 +823,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `515` // Estimated: `3675` - // Minimum execution time: 34_183_000 picoseconds. - Weight::from_parts(34_577_000, 3675) + // Minimum execution time: 33_443_000 picoseconds. + Weight::from_parts(56_533_000, 3675) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } @@ -754,8 +834,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `351` // Estimated: `3675` - // Minimum execution time: 15_505_000 picoseconds. - Weight::from_parts(15_784_000, 3675) + // Minimum execution time: 20_636_000 picoseconds. + Weight::from_parts(23_960_000, 3675) .saturating_add(RocksDbWeight::get().reads(1_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } @@ -767,8 +847,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `385` // Estimated: `3675` - // Minimum execution time: 38_762_000 picoseconds. - Weight::from_parts(39_138_000, 3675) + // Minimum execution time: 35_987_000 picoseconds. + Weight::from_parts(36_429_000, 3675) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().writes(2_u64)) } @@ -777,15 +857,15 @@ impl WeightInfo for () { /// Storage: Assets Approvals (r:1 w:1) /// Proof: Assets Approvals (max_values: None, max_size: Some(148), added: 2623, mode: MaxEncodedLen) /// Storage: Assets Account (r:2 w:2) - /// Proof: Assets Account (max_values: None, max_size: Some(102), added: 2577, mode: MaxEncodedLen) + /// Proof: Assets Account (max_values: None, max_size: Some(134), added: 2609, mode: MaxEncodedLen) /// Storage: System Account (r:1 w:1) /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) fn transfer_approved() -> Weight { // Proof Size summary in bytes: // Measured: `668` - // Estimated: `6144` - // Minimum execution time: 73_396_000 picoseconds. - Weight::from_parts(73_986_000, 6144) + // Estimated: `6208` + // Minimum execution time: 68_059_000 picoseconds. + Weight::from_parts(69_845_000, 6208) .saturating_add(RocksDbWeight::get().reads(5_u64)) .saturating_add(RocksDbWeight::get().writes(5_u64)) } @@ -797,8 +877,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `555` // Estimated: `3675` - // Minimum execution time: 39_707_000 picoseconds. - Weight::from_parts(40_222_000, 3675) + // Minimum execution time: 38_066_000 picoseconds. + Weight::from_parts(38_450_000, 3675) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().writes(2_u64)) } @@ -810,8 +890,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `555` // Estimated: `3675` - // Minimum execution time: 40_357_000 picoseconds. - Weight::from_parts(40_731_000, 3675) + // Minimum execution time: 38_500_000 picoseconds. + Weight::from_parts(38_953_000, 3675) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().writes(2_u64)) } @@ -821,9 +901,78 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `351` // Estimated: `3675` - // Minimum execution time: 16_937_000 picoseconds. - Weight::from_parts(17_219_000, 3675) + // Minimum execution time: 16_268_000 picoseconds. + Weight::from_parts(16_764_000, 3675) .saturating_add(RocksDbWeight::get().reads(1_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } + /// Storage: Assets Account (r:1 w:1) + /// Proof: Assets Account (max_values: None, max_size: Some(134), added: 2609, mode: MaxEncodedLen) + /// Storage: Assets Asset (r:1 w:1) + /// Proof: Assets Asset (max_values: None, max_size: Some(210), added: 2685, mode: MaxEncodedLen) + /// Storage: System Account (r:1 w:1) + /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) + fn touch() -> Weight { + // Proof Size summary in bytes: + // Measured: `453` + // Estimated: `3675` + // Minimum execution time: 37_468_000 picoseconds. + Weight::from_parts(37_957_000, 3675) + .saturating_add(RocksDbWeight::get().reads(3_u64)) + .saturating_add(RocksDbWeight::get().writes(3_u64)) + } + /// Storage: Assets Account (r:1 w:1) + /// Proof: Assets Account (max_values: None, max_size: Some(134), added: 2609, mode: MaxEncodedLen) + /// Storage: Assets Asset (r:1 w:1) + /// Proof: Assets Asset (max_values: None, max_size: Some(210), added: 2685, mode: MaxEncodedLen) + fn touch_other() -> Weight { + // Proof Size summary in bytes: + // Measured: `351` + // Estimated: `3675` + // Minimum execution time: 383_408_000 picoseconds. + Weight::from_parts(392_036_000, 3675) + .saturating_add(RocksDbWeight::get().reads(2)) + .saturating_add(RocksDbWeight::get().writes(2)) + } + /// Storage: Assets Account (r:1 w:1) + /// Proof: Assets Account (max_values: None, max_size: Some(134), added: 2609, mode: MaxEncodedLen) + /// Storage: Assets Asset (r:1 w:1) + /// Proof: Assets Asset (max_values: None, max_size: Some(210), added: 2685, mode: MaxEncodedLen) + /// Storage: System Account (r:1 w:1) + /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) + fn refund() -> Weight { + // Proof Size summary in bytes: + // Measured: `579` + // Estimated: `3675` + // Minimum execution time: 34_066_000 picoseconds. + Weight::from_parts(34_347_000, 3675) + .saturating_add(RocksDbWeight::get().reads(3_u64)) + .saturating_add(RocksDbWeight::get().writes(3_u64)) + } + /// Storage: Assets Account (r:1 w:1) + /// Proof: Assets Account (max_values: None, max_size: Some(134), added: 2609, mode: MaxEncodedLen) + /// Storage: Assets Asset (r:1 w:1) + /// Proof: Assets Asset (max_values: None, max_size: Some(210), added: 2685, mode: MaxEncodedLen) + fn refund_other() -> Weight { + // Proof Size summary in bytes: + // Measured: `510` + // Estimated: `3675` + // Minimum execution time: 32_060_000 picoseconds. + Weight::from_parts(32_519_000, 3675) + .saturating_add(RocksDbWeight::get().reads(2_u64)) + .saturating_add(RocksDbWeight::get().writes(2_u64)) + } + /// Storage: Assets Asset (r:1 w:0) + /// Proof: Assets Asset (max_values: None, max_size: Some(210), added: 2685, mode: MaxEncodedLen) + /// Storage: Assets Account (r:1 w:1) + /// Proof: Assets Account (max_values: None, max_size: Some(134), added: 2609, mode: MaxEncodedLen) + fn block() -> Weight { + // Proof Size summary in bytes: + // Measured: `459` + // Estimated: `3675` + // Minimum execution time: 115_000_000 picoseconds. + Weight::from_parts(163_000_000, 3675) + .saturating_add(RocksDbWeight::get().reads(2_u64)) + .saturating_add(RocksDbWeight::get().writes(1_u64)) + } } diff --git a/frame/support/src/traits.rs b/frame/support/src/traits.rs index b2e6a6138..8d0f8aa88 100644 --- a/frame/support/src/traits.rs +++ b/frame/support/src/traits.rs @@ -55,8 +55,8 @@ pub use filter::{ClearFilterGuard, FilterStack, FilterStackGuard, InstanceFilter mod misc; pub use misc::{ defensive_prelude::{self, *}, - Backing, ConstBool, ConstI128, ConstI16, ConstI32, ConstI64, ConstI8, ConstU128, ConstU16, - ConstU32, ConstU64, ConstU8, DefensiveMax, DefensiveMin, DefensiveSaturating, + AccountTouch, Backing, ConstBool, ConstI128, ConstI16, ConstI32, ConstI64, ConstI8, ConstU128, + ConstU16, ConstU32, ConstU64, ConstU8, DefensiveMax, DefensiveMin, DefensiveSaturating, DefensiveTruncateFrom, EnsureInherentsAreFirst, EqualPrivilegeOnly, EstimateCallFee, ExecuteBlock, ExtrinsicCall, Get, GetBacking, GetDefault, HandleLifetime, IsSubType, IsType, Len, OffchainWorker, OnKilledAccount, OnNewAccount, PrivilegeCmp, SameOrOther, Time, diff --git a/frame/support/src/traits/misc.rs b/frame/support/src/traits/misc.rs index a320b85ea..e21fcfbb2 100644 --- a/frame/support/src/traits/misc.rs +++ b/frame/support/src/traits/misc.rs @@ -17,7 +17,7 @@ //! Smaller traits used in FRAME which don't need their own file. -use crate::dispatch::Parameter; +use crate::dispatch::{DispatchResult, Parameter}; use codec::{CompactLen, Decode, DecodeLimit, Encode, EncodeLike, Input, MaxEncodedLen}; use impl_trait_for_tuples::impl_for_tuples; use scale_info::{build::Fields, meta_type, Path, Type, TypeInfo, TypeParameter}; @@ -1154,6 +1154,19 @@ impl PreimageRecipient for () { fn unnote_preimage(_: &Hash) {} } +/// Trait for creating an asset account with a deposit taken from a designated depositor specified +/// by the client. +pub trait AccountTouch { + /// The type for currency units of the deposit. + type Balance; + + /// The deposit amount of a native currency required for creating an asset account. + fn deposit_required() -> Self::Balance; + + /// Create an account for `who` of the `asset` with a deposit taken from the `depositor`. + fn touch(asset: AssetId, who: AccountId, depositor: AccountId) -> DispatchResult; +} + #[cfg(test)] mod test { use super::*; diff --git a/frame/support/src/traits/tokens/misc.rs b/frame/support/src/traits/tokens/misc.rs index 75aef0e04..0ba900e95 100644 --- a/frame/support/src/traits/tokens/misc.rs +++ b/frame/support/src/traits/tokens/misc.rs @@ -141,6 +141,8 @@ pub enum DepositConsequence { Overflow, /// Account continued in existence. Success, + /// Account cannot receive the assets. + Blocked, } impl DepositConsequence { @@ -152,6 +154,7 @@ impl DepositConsequence { CannotCreate => TokenError::CannotCreate.into(), UnknownAsset => TokenError::UnknownAsset.into(), Overflow => ArithmeticError::Overflow.into(), + Blocked => TokenError::Blocked.into(), Success => return Ok(()), }) } diff --git a/primitives/runtime/src/lib.rs b/primitives/runtime/src/lib.rs index e30146918..13ef157df 100644 --- a/primitives/runtime/src/lib.rs +++ b/primitives/runtime/src/lib.rs @@ -626,6 +626,8 @@ pub enum TokenError { CannotCreateHold, /// Withdrawal would cause unwanted loss of account. NotExpendable, + /// Account cannot receive the assets. + Blocked, } impl From for &'static str { @@ -641,6 +643,7 @@ impl From for &'static str { TokenError::CannotCreateHold => "Account cannot be created for recording amount on hold", TokenError::NotExpendable => "Account that is desired to remain would die", + TokenError::Blocked => "Account cannot receive the assets", } } } From 81d732b783e8575b63ee02bee409e4ae8c7a13a2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bastian=20K=C3=B6cher?= Date: Mon, 8 May 2023 16:19:38 +0200 Subject: [PATCH 469/558] Re-enable `node-cli` tests in CI and fix them (#14093) With the recent change of re-running flaky tests this should be fine! --- scripts/ci/gitlab/pipeline/test.yml | 2 -- test-utils/cli/src/lib.rs | 2 +- 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/scripts/ci/gitlab/pipeline/test.yml b/scripts/ci/gitlab/pipeline/test.yml index 29f21bfe5..6e86a01b4 100644 --- a/scripts/ci/gitlab/pipeline/test.yml +++ b/scripts/ci/gitlab/pipeline/test.yml @@ -216,7 +216,6 @@ test-linux-stable: - rusty-cachier snapshot create # this job runs all tests in former runtime-benchmarks, frame-staking and wasmtime tests # tests are partitioned by nextest and executed in parallel on $CI_NODE_TOTAL runners - # node-cli is excluded until https://github.com/paritytech/substrate/issues/11321 fixed - echo "Node index - ${CI_NODE_INDEX}. Total amount - ${CI_NODE_TOTAL}" - time cargo nextest run --workspace --locked @@ -224,7 +223,6 @@ test-linux-stable: --verbose --features runtime-benchmarks --manifest-path ./bin/node/cli/Cargo.toml - --exclude node-cli --partition count:${CI_NODE_INDEX}/${CI_NODE_TOTAL} # we need to update cache only from one job - if [ ${CI_NODE_INDEX} == 1 ]; then rusty-cachier cache upload; fi diff --git a/test-utils/cli/src/lib.rs b/test-utils/cli/src/lib.rs index f225cc135..526bc1f37 100644 --- a/test-utils/cli/src/lib.rs +++ b/test-utils/cli/src/lib.rs @@ -64,7 +64,7 @@ pub fn start_node() -> Child { Command::new(cargo_bin("substrate")) .stdout(process::Stdio::piped()) .stderr(process::Stdio::piped()) - .args(&["--dev", "--tmp", "--ws-port=45789", "--no-hardware-benchmarks"]) + .args(&["--dev", "--tmp", "--rpc-port=45789", "--no-hardware-benchmarks"]) .spawn() .unwrap() } From 33d94c2d26f18272f103f831c40e35895a273ca9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bastian=20K=C3=B6cher?= Date: Mon, 8 May 2023 16:42:17 +0200 Subject: [PATCH 470/558] sc-informant: Do not show `Block history` if doing major sync (#14094) After warp syncing a node it still downloads the block history. If the node is stopped and then started later while downloading the block history, the node first needs to do a major sync to sync to the tip of the chain. Before informant was showing `Block history` as sync state, while we actually were doing a major sync. This pr is fixing this. --- client/informant/src/display.rs | 29 ++++++++++++++++++++--------- 1 file changed, 20 insertions(+), 9 deletions(-) diff --git a/client/informant/src/display.rs b/client/informant/src/display.rs index 2f1013072..722cf56d7 100644 --- a/client/informant/src/display.rs +++ b/client/informant/src/display.rs @@ -98,25 +98,36 @@ impl InformantDisplay { let (level, status, target) = match (sync_status.state, sync_status.state_sync, sync_status.warp_sync) { + // Do not set status to "Block history" when we are doing a major sync. + // + // A node could for example have been warp synced to the tip of the chain and + // shutdown. At the next start we still need to download the block history, but + // first will sync to the tip of the chain. ( - _, + sync_status, _, Some(WarpSyncProgress { phase: WarpSyncPhase::DownloadingBlocks(n), .. }), - ) => ("⏩", "Block history".into(), format!(", #{}", n)), + ) if !sync_status.is_major_syncing() => ("⏩", "Block history".into(), format!(", #{}", n)), ( _, _, Some(WarpSyncProgress { phase: WarpSyncPhase::AwaitingTargetBlock, .. }), ) => ("⏩", "Waiting for pending target block".into(), "".into()), - (_, _, Some(warp)) => ( - "⏩", - "Warping".into(), - format!( - ", {}, {:.2} Mib", + // Handle all phases besides the two phases we already handle above. + (_, _, Some(warp)) + if !matches!( warp.phase, - (warp.total_bytes as f32) / (1024f32 * 1024f32) + WarpSyncPhase::AwaitingTargetBlock | WarpSyncPhase::DownloadingBlocks(_) + ) => + ( + "⏩", + "Warping".into(), + format!( + ", {}, {:.2} Mib", + warp.phase, + (warp.total_bytes as f32) / (1024f32 * 1024f32) + ), ), - ), (_, Some(state), _) => ( "⚙️ ", "Downloading state".into(), From dc398dee295ee97f3a71e7d4c1a9444d6c2ce5b8 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 8 May 2023 16:28:00 +0000 Subject: [PATCH 471/558] Bump cfg-expr from 0.10.3 to 0.15.1 (#14092) Bumps [cfg-expr](https://github.com/EmbarkStudios/cfg-expr) from 0.10.3 to 0.15.1. - [Release notes](https://github.com/EmbarkStudios/cfg-expr/releases) - [Changelog](https://github.com/EmbarkStudios/cfg-expr/blob/main/CHANGELOG.md) - [Commits](https://github.com/EmbarkStudios/cfg-expr/compare/0.10.3...0.15.1) --- updated-dependencies: - dependency-name: cfg-expr dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- Cargo.lock | 4 ++-- frame/support/procedural/Cargo.toml | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index b8e42c103..e9bc2a996 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -912,9 +912,9 @@ dependencies = [ [[package]] name = "cfg-expr" -version = "0.10.3" +version = "0.15.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0aacacf4d96c24b2ad6eb8ee6df040e4f27b0d0b39a5710c30091baa830485db" +checksum = "c8790cf1286da485c72cf5fc7aeba308438800036ec67d89425924c4807268c9" dependencies = [ "smallvec", ] diff --git a/frame/support/procedural/Cargo.toml b/frame/support/procedural/Cargo.toml index 1a17924b4..8e18376ba 100644 --- a/frame/support/procedural/Cargo.toml +++ b/frame/support/procedural/Cargo.toml @@ -17,7 +17,7 @@ proc-macro = true [dependencies] derive-syn-parse = "0.1.5" Inflector = "0.11.4" -cfg-expr = "0.10.3" +cfg-expr = "0.15.1" itertools = "0.10.3" proc-macro2 = "1.0.56" quote = "1.0.26" From 00a7ee7094d92fded19ae7588a9668afed42c269 Mon Sep 17 00:00:00 2001 From: Sebastian Kunert Date: Mon, 8 May 2023 22:31:39 +0200 Subject: [PATCH 472/558] Remove manual implementation (#14096) --- primitives/runtime/src/curve.rs | 33 ++------------------------------- 1 file changed, 2 insertions(+), 31 deletions(-) diff --git a/primitives/runtime/src/curve.rs b/primitives/runtime/src/curve.rs index 7cea92293..2fc032cfa 100644 --- a/primitives/runtime/src/curve.rs +++ b/primitives/runtime/src/curve.rs @@ -22,9 +22,10 @@ use crate::{ Perbill, }; use core::ops::Sub; +use scale_info::TypeInfo; /// Piecewise Linear function in [0, 1] -> [0, 1]. -#[derive(PartialEq, Eq, sp_core::RuntimeDebug)] +#[derive(PartialEq, Eq, sp_core::RuntimeDebug, TypeInfo)] pub struct PiecewiseLinear<'a> { /// Array of points. Must be in order from the lowest abscissas to the highest. pub points: &'a [(Perbill, Perbill)], @@ -32,36 +33,6 @@ pub struct PiecewiseLinear<'a> { pub maximum: Perbill, } -// This can be replaced with -// #[derive(scale_info::TypeInfo)] -// #[scale_info(skip_type_params(S))] -// again once this issue is fixed in the rust compiler: https://github.com/rust-lang/rust/issues/96956 -// Tracking issues: https://github.com/paritytech/substrate/issues/11915 -impl scale_info::TypeInfo for PiecewiseLinear<'static> { - type Identity = Self; - fn type_info() -> ::scale_info::Type { - scale_info::Type::builder() - .path(scale_info::Path::new("PiecewiseLinear", "sp_runtime::curve")) - .type_params(crate::Vec::new()) - .docs(&["Piecewise Linear function in [0, 1] -> [0, 1]."]) - .composite( - scale_info::build::Fields::named() - .field(|f| { - f.ty::<&'static[(Perbill, Perbill)]>() - .name("points") - .type_name("&'static[(Perbill, Perbill)]") - .docs(&["Array of points. Must be in order from the lowest abscissas to the highest."]) - }) - .field(|f| { - f.ty::() - .name("maximum") - .type_name("Perbill") - .docs(&["The maximum value that can be returned."]) - }), - ) - } -} - fn abs_sub + Clone>(a: N, b: N) -> N where { a.clone().max(b.clone()) - a.min(b) } From ab8330cd421027d0ebfd613111702fd4fa5e9487 Mon Sep 17 00:00:00 2001 From: Sebastian Kunert Date: Mon, 8 May 2023 22:32:01 +0200 Subject: [PATCH 473/558] Remove manual implementation (#14096) From f0c14f387baba0688afa9422a9bc24068d431716 Mon Sep 17 00:00:00 2001 From: Qinxuan Chen Date: Tue, 9 May 2023 17:22:55 +0800 Subject: [PATCH 474/558] frame-support-test: migrate tests from `decl_*` macros to the new `pallet` macros (#12445) * frame-support: migrate some tests from decl macros to new pallet attribute macros * Remove useless type alias * Remove useless type alias * frame-support-test: migrate old decl_macros to new pallet attribute macros * fmt Signed-off-by: Oliver Tale-Yazdi * Fix tests Signed-off-by: Oliver Tale-Yazdi * Fix features Signed-off-by: Oliver Tale-Yazdi * Remove deprecated stuff Signed-off-by: Oliver Tale-Yazdi * Update UI tests Signed-off-by: Oliver Tale-Yazdi * Fix UI test Signed-off-by: Oliver Tale-Yazdi * Fix test Signed-off-by: Oliver Tale-Yazdi * Update UI tests Signed-off-by: Oliver Tale-Yazdi * Cleanup Signed-off-by: Oliver Tale-Yazdi --------- Signed-off-by: Oliver Tale-Yazdi Co-authored-by: Oliver Tale-Yazdi Co-authored-by: parity-processbot <> --- frame/benchmarking/src/tests.rs | 7 - frame/benchmarking/src/tests_instance.rs | 1 - frame/collective/src/tests.rs | 2 - frame/executive/src/lib.rs | 12 - frame/support/test/compile_pass/Cargo.toml | 4 +- frame/support/test/compile_pass/src/lib.rs | 12 +- frame/support/test/pallet/src/lib.rs | 7 +- frame/support/test/src/lib.rs | 121 ++- frame/support/test/src/pallet_version.rs | 29 - .../test/tests/benchmark_ui/invalid_origin.rs | 2 +- .../tests/benchmark_ui/invalid_origin.stderr | 8 - frame/support/test/tests/construct_runtime.rs | 430 +++++---- .../old_unsupported_pallet_decl.rs | 26 - .../old_unsupported_pallet_decl.stderr | 31 - .../undefined_event_part.stderr | 4 +- .../undefined_origin_part.stderr | 4 +- frame/support/test/tests/decl_module_ui.rs | 32 - ...served_keyword_two_times_integrity_test.rs | 7 - ...ed_keyword_two_times_integrity_test.stderr | 19 - ...eserved_keyword_two_times_on_initialize.rs | 13 - ...ved_keyword_two_times_on_initialize.stderr | 13 - frame/support/test/tests/decl_storage.rs | 881 ------------------ frame/support/test/tests/decl_storage_ui.rs | 32 - .../tests/decl_storage_ui/config_duplicate.rs | 31 - .../decl_storage_ui/config_duplicate.stderr | 5 - .../decl_storage_ui/config_get_duplicate.rs | 31 - .../config_get_duplicate.stderr | 5 - .../tests/decl_storage_ui/get_duplicate.rs | 31 - .../decl_storage_ui/get_duplicate.stderr | 5 - frame/support/test/tests/final_keys.rs | 276 ++++-- frame/support/test/tests/genesisconfig.rs | 82 +- frame/support/test/tests/instance.rs | 350 +++---- frame/support/test/tests/issue2219.rs | 174 ++-- frame/support/test/tests/origin.rs | 208 +++-- .../test/tests/pallet_compatibility.rs | 371 -------- .../tests/pallet_compatibility_instance.rs | 371 -------- frame/support/test/tests/pallet_instance.rs | 10 +- .../tests/pallet_with_name_trait_is_valid.rs | 158 ---- frame/support/test/tests/storage_layers.rs | 72 +- .../support/test/tests/storage_transaction.rs | 115 ++- frame/support/test/tests/system.rs | 81 -- 41 files changed, 1112 insertions(+), 2961 deletions(-) delete mode 100644 frame/support/test/src/pallet_version.rs delete mode 100644 frame/support/test/tests/construct_runtime_ui/old_unsupported_pallet_decl.rs delete mode 100644 frame/support/test/tests/construct_runtime_ui/old_unsupported_pallet_decl.stderr delete mode 100644 frame/support/test/tests/decl_module_ui.rs delete mode 100644 frame/support/test/tests/decl_module_ui/reserved_keyword_two_times_integrity_test.rs delete mode 100644 frame/support/test/tests/decl_module_ui/reserved_keyword_two_times_integrity_test.stderr delete mode 100644 frame/support/test/tests/decl_module_ui/reserved_keyword_two_times_on_initialize.rs delete mode 100644 frame/support/test/tests/decl_module_ui/reserved_keyword_two_times_on_initialize.stderr delete mode 100644 frame/support/test/tests/decl_storage.rs delete mode 100644 frame/support/test/tests/decl_storage_ui.rs delete mode 100644 frame/support/test/tests/decl_storage_ui/config_duplicate.rs delete mode 100644 frame/support/test/tests/decl_storage_ui/config_duplicate.stderr delete mode 100644 frame/support/test/tests/decl_storage_ui/config_get_duplicate.rs delete mode 100644 frame/support/test/tests/decl_storage_ui/config_get_duplicate.stderr delete mode 100644 frame/support/test/tests/decl_storage_ui/get_duplicate.rs delete mode 100644 frame/support/test/tests/decl_storage_ui/get_duplicate.stderr delete mode 100644 frame/support/test/tests/pallet_compatibility.rs delete mode 100644 frame/support/test/tests/pallet_compatibility_instance.rs delete mode 100644 frame/support/test/tests/pallet_with_name_trait_is_valid.rs delete mode 100644 frame/support/test/tests/system.rs diff --git a/frame/benchmarking/src/tests.rs b/frame/benchmarking/src/tests.rs index 80b631bb7..7e240ee04 100644 --- a/frame/benchmarking/src/tests.rs +++ b/frame/benchmarking/src/tests.rs @@ -45,28 +45,21 @@ mod pallet_test { } #[pallet::storage] - #[pallet::getter(fn value)] pub(crate) type Value = StorageValue<_, u32, OptionQuery>; #[pallet::call] impl Pallet { - #[pallet::call_index(0)] - #[pallet::weight(0)] pub fn set_value(origin: OriginFor, n: u32) -> DispatchResult { let _sender = ensure_signed(origin)?; Value::::put(n); Ok(()) } - #[pallet::call_index(1)] - #[pallet::weight(0)] pub fn dummy(origin: OriginFor, _n: u32) -> DispatchResult { let _sender = ensure_none(origin)?; Ok(()) } - #[pallet::call_index(2)] - #[pallet::weight(0)] pub fn always_error(_origin: OriginFor) -> DispatchResult { return Err("I always fail".into()) } diff --git a/frame/benchmarking/src/tests_instance.rs b/frame/benchmarking/src/tests_instance.rs index 92d78b9ec..d017fc679 100644 --- a/frame/benchmarking/src/tests_instance.rs +++ b/frame/benchmarking/src/tests_instance.rs @@ -49,7 +49,6 @@ mod pallet_test { } #[pallet::storage] - #[pallet::getter(fn value)] pub(crate) type Value, I: 'static = ()> = StorageValue<_, u32, OptionQuery>; #[pallet::event] diff --git a/frame/collective/src/tests.rs b/frame/collective/src/tests.rs index 1165ebcec..99aa7a57e 100644 --- a/frame/collective/src/tests.rs +++ b/frame/collective/src/tests.rs @@ -68,8 +68,6 @@ mod mock_democracy { #[pallet::call] impl Pallet { - #[pallet::call_index(0)] - #[pallet::weight(0)] pub fn external_propose_majority(origin: OriginFor) -> DispatchResult { T::ExternalMajorityOrigin::ensure_origin(origin)?; Self::deposit_event(Event::::ExternalProposed); diff --git a/frame/executive/src/lib.rs b/frame/executive/src/lib.rs index aad1de11d..fd76fefad 100644 --- a/frame/executive/src/lib.rs +++ b/frame/executive/src/lib.rs @@ -735,51 +735,39 @@ mod tests { #[pallet::call] impl Pallet { - #[pallet::call_index(0)] - #[pallet::weight(100)] pub fn some_function(origin: OriginFor) -> DispatchResult { // NOTE: does not make any different. frame_system::ensure_signed(origin)?; Ok(()) } - #[pallet::call_index(1)] #[pallet::weight((200, DispatchClass::Operational))] pub fn some_root_operation(origin: OriginFor) -> DispatchResult { frame_system::ensure_root(origin)?; Ok(()) } - #[pallet::call_index(2)] - #[pallet::weight(0)] pub fn some_unsigned_message(origin: OriginFor) -> DispatchResult { frame_system::ensure_none(origin)?; Ok(()) } - #[pallet::call_index(3)] - #[pallet::weight(0)] pub fn allowed_unsigned(origin: OriginFor) -> DispatchResult { frame_system::ensure_root(origin)?; Ok(()) } - #[pallet::call_index(4)] - #[pallet::weight(0)] pub fn unallowed_unsigned(origin: OriginFor) -> DispatchResult { frame_system::ensure_root(origin)?; Ok(()) } - #[pallet::call_index(5)] #[pallet::weight((0, DispatchClass::Mandatory))] pub fn inherent_call(origin: OriginFor) -> DispatchResult { frame_system::ensure_none(origin)?; Ok(()) } - #[pallet::call_index(6)] - #[pallet::weight(0)] pub fn calculate_storage_root(_origin: OriginFor) -> DispatchResult { let root = sp_io::storage::root(sp_runtime::StateVersion::V1); sp_io::storage::set("storage_root".as_bytes(), &root); diff --git a/frame/support/test/compile_pass/Cargo.toml b/frame/support/test/compile_pass/Cargo.toml index 4466c24d6..353847b4b 100644 --- a/frame/support/test/compile_pass/Cargo.toml +++ b/frame/support/test/compile_pass/Cargo.toml @@ -14,7 +14,7 @@ targets = ["x86_64-unknown-linux-gnu"] [dependencies] codec = { package = "parity-scale-codec", version = "3.2.2", default-features = false, features = ["derive"] } scale-info = { version = "2.5.0", default-features = false, features = ["derive"] } -frame-support = { version = "4.0.0-dev", default-features = false, path = "../../" } +renamed-frame-support = { package = "frame-support", version = "4.0.0-dev", default-features = false, path = "../../" } frame-system = { version = "4.0.0-dev", default-features = false, path = "../../../system" } sp-core = { version = "7.0.0", default-features = false, path = "../../../../primitives/core" } sp-runtime = { version = "7.0.0", default-features = false, path = "../../../../primitives/runtime" } @@ -24,7 +24,7 @@ sp-version = { version = "5.0.0", default-features = false, path = "../../../../ default = ["std"] std = [ "codec/std", - "frame-support/std", + "renamed-frame-support/std", "frame-system/std", "scale-info/std", "sp-core/std", diff --git a/frame/support/test/compile_pass/src/lib.rs b/frame/support/test/compile_pass/src/lib.rs index 7e9fdaff2..4eaa657b1 100644 --- a/frame/support/test/compile_pass/src/lib.rs +++ b/frame/support/test/compile_pass/src/lib.rs @@ -16,15 +16,13 @@ // You should have received a copy of the GNU General Public License // along with this program. If not, see . +//! Test that `construct_runtime!` also works when `frame-support` is renamed in the `Cargo.toml`. + #![cfg_attr(not(feature = "std"), no_std)] -// `construct_runtime!` does a lot of recursion and requires us to increase the limit to 256. -#![recursion_limit = "256"] -//! This crate tests that `construct_runtime!` expands the pallet parts -//! correctly even when frame-support is renamed in Cargo.toml -use frame_support::{ +use renamed_frame_support::{ construct_runtime, parameter_types, - traits::{ConstU16, ConstU32, ConstU64}, + traits::{ConstU16, ConstU32, ConstU64, Everything}, }; use sp_core::{sr25519, H256}; use sp_runtime::{ @@ -54,7 +52,7 @@ parameter_types! { } impl frame_system::Config for Runtime { - type BaseCallFilter = frame_support::traits::Everything; + type BaseCallFilter = Everything; type BlockWeights = (); type BlockLength = (); type Index = u128; diff --git a/frame/support/test/pallet/src/lib.rs b/frame/support/test/pallet/src/lib.rs index 82f12ea95..2dfc94d83 100644 --- a/frame/support/test/pallet/src/lib.rs +++ b/frame/support/test/pallet/src/lib.rs @@ -15,7 +15,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -//! Testing pallet macro +//! A basic pallet that can be used to test `construct_runtime!`. // Ensure docs are propagated properly by the macros. #![warn(missing_docs)] @@ -24,10 +24,7 @@ pub use pallet::*; #[frame_support::pallet] pub mod pallet { - #[allow(unused_imports)] use frame_support::pallet_prelude::*; - #[allow(unused_imports)] - use frame_system::pallet_prelude::*; #[pallet::pallet] pub struct Pallet(_); @@ -37,7 +34,7 @@ pub mod pallet { /// I'm the documentation #[pallet::storage] - pub type Value = StorageValue; + pub type Value = StorageValue<_, u32>; #[pallet::genesis_config] #[cfg_attr(feature = "std", derive(Default))] diff --git a/frame/support/test/src/lib.rs b/frame/support/test/src/lib.rs index c0b025380..5dccc8847 100644 --- a/frame/support/test/src/lib.rs +++ b/frame/support/test/src/lib.rs @@ -15,48 +15,113 @@ // See the License for the specific language governing permissions and // limitations under the License. -//! Test crate for frame_support. Allow to make use of `frame_support::decl_storage`. -//! See tests directory. +//! Minimal pallet without `frame_system::Config`-super trait. // Make sure we fail compilation on warnings #![warn(missing_docs)] #![deny(warnings)] -/// The configuration trait -pub trait Config: 'static { - /// The runtime origin type. - type RuntimeOrigin: codec::Codec + codec::EncodeLike + Default + scale_info::TypeInfo; - /// The block number type. - type BlockNumber: codec::Codec + codec::EncodeLike + Default + scale_info::TypeInfo; - /// The information about the pallet setup in the runtime. - type PalletInfo: frame_support::traits::PalletInfo; - /// The db weights. - type DbWeight: frame_support::traits::Get; -} +pub use frame_support::dispatch::RawOrigin; -frame_support::decl_module! { - /// Some test module - pub struct Module for enum Call where origin: T::RuntimeOrigin, system=self {} -} +pub use self::pallet::*; + +#[frame_support::pallet(dev_mode)] +pub mod pallet { + use super::*; + use crate::{self as frame_system, pallet_prelude::*}; + use frame_support::pallet_prelude::*; -/// A PalletInfo implementation which just panics. -pub struct PanicPalletInfo; + #[pallet::pallet] + pub struct Pallet(_); -impl frame_support::traits::PalletInfo for PanicPalletInfo { - fn index() -> Option { - unimplemented!("PanicPalletInfo mustn't be triggered by tests"); + /// The configuration trait + #[pallet::config] + #[pallet::disable_frame_system_supertrait_check] + pub trait Config: 'static + Eq + Clone { + /// The block number type. + type BlockNumber: Parameter + Member + Default + MaybeSerializeDeserialize + MaxEncodedLen; + /// The account type. + type AccountId: Parameter + Member + MaxEncodedLen; + /// The basic call filter to use in Origin. + type BaseCallFilter: frame_support::traits::Contains; + /// The runtime origin type. + type RuntimeOrigin: Into, Self::RuntimeOrigin>> + + From>; + /// The runtime call type. + type RuntimeCall; + /// The runtime event type. + type RuntimeEvent: Parameter + + Member + + IsType<::RuntimeEvent> + + From>; + /// The information about the pallet setup in the runtime. + type PalletInfo: frame_support::traits::PalletInfo; + /// The db weights. + type DbWeight: Get; } - fn name() -> Option<&'static str> { - unimplemented!("PanicPalletInfo mustn't be triggered by tests"); + + #[pallet::call] + impl Pallet { + /// A noop call. + pub fn noop(_origin: OriginFor) -> DispatchResult { + Ok(()) + } } - fn module_name() -> Option<&'static str> { - unimplemented!("PanicPalletInfo mustn't be triggered by tests"); + + impl Pallet { + /// A empty method. + pub fn deposit_event(_event: impl Into) {} } - fn crate_version() -> Option { - unimplemented!("PanicPalletInfo mustn't be triggered by tests"); + + /// The origin type. + #[pallet::origin] + pub type Origin = RawOrigin<::AccountId>; + + /// The error type. + #[pallet::error] + pub enum Error { + /// Test error documentation + TestError, + /// Error documentation + /// with multiple lines + AnotherError, + /// Required by construct_runtime + CallFiltered, + } + + /// The event type. + #[pallet::event] + pub enum Event { + /// The extrinsic is successful + ExtrinsicSuccess, + /// The extrinsic is failed + ExtrinsicFailed, + /// The ignored error + Ignore(::BlockNumber), } } +/// Ensure that the origin `o` represents the root. Returns `Ok` or an `Err` otherwise. +pub fn ensure_root(o: OuterOrigin) -> Result<(), &'static str> +where + OuterOrigin: Into, OuterOrigin>>, +{ + o.into().map(|_| ()).map_err(|_| "bad origin: expected to be a root origin") +} + +/// Same semantic as [`frame_system`]. +// Note: we cannot use [`frame_system`] here since the pallet does not depend on +// [`frame_system::Config`]. +pub mod pallet_prelude { + pub use crate::ensure_root; + + /// Type alias for the `Origin` associated type of system config. + pub type OriginFor = ::RuntimeOrigin; + + /// Type alias for the `BlockNumber` associated type of system config. + pub type BlockNumberFor = ::BlockNumber; +} + /// Provides an implementation of [`frame_support::traits::Randomness`] that should only be used in /// tests! pub struct TestRandomness(sp_std::marker::PhantomData); diff --git a/frame/support/test/src/pallet_version.rs b/frame/support/test/src/pallet_version.rs deleted file mode 100644 index b01569968..000000000 --- a/frame/support/test/src/pallet_version.rs +++ /dev/null @@ -1,29 +0,0 @@ -// This file is part of Substrate. - -// Copyright (C) Parity Technologies (UK) Ltd. -// SPDX-License-Identifier: Apache-2.0 - -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -use frame_support::{crate_to_pallet_version, traits::PalletVersion}; - -#[test] -fn ensure_that_current_pallet_version_is_correct() { - let expected = PalletVersion { - major: env!("CARGO_PKG_VERSION_MAJOR").parse().unwrap(), - minor: env!("CARGO_PKG_VERSION_MINOR").parse().unwrap(), - patch: env!("CARGO_PKG_VERSION_PATCH").parse().unwrap(), - }; - - assert_eq!(expected, crate_to_pallet_version!()) -} diff --git a/frame/support/test/tests/benchmark_ui/invalid_origin.rs b/frame/support/test/tests/benchmark_ui/invalid_origin.rs index 81cfc3979..cfb00e88c 100644 --- a/frame/support/test/tests/benchmark_ui/invalid_origin.rs +++ b/frame/support/test/tests/benchmark_ui/invalid_origin.rs @@ -10,7 +10,7 @@ mod benches { #[benchmark] fn bench() { #[extrinsic_call] - thing(1); + noop(1); } } diff --git a/frame/support/test/tests/benchmark_ui/invalid_origin.stderr b/frame/support/test/tests/benchmark_ui/invalid_origin.stderr index 1818442b6..115a8206f 100644 --- a/frame/support/test/tests/benchmark_ui/invalid_origin.stderr +++ b/frame/support/test/tests/benchmark_ui/invalid_origin.stderr @@ -1,11 +1,3 @@ -error[E0599]: no variant or associated item named `new_call_variant_thing` found for enum `frame_support_test::Call` in the current scope - --> tests/benchmark_ui/invalid_origin.rs:6:1 - | -6 | #[benchmarks] - | ^^^^^^^^^^^^^ variant or associated item not found in `Call` - | - = note: this error originates in the attribute macro `benchmarks` (in Nightly builds, run with -Z macro-backtrace for more info) - error[E0277]: the trait bound `::RuntimeOrigin: From<{integer}>` is not satisfied --> tests/benchmark_ui/invalid_origin.rs:6:1 | diff --git a/frame/support/test/tests/construct_runtime.rs b/frame/support/test/tests/construct_runtime.rs index d0b31d71a..85e790095 100644 --- a/frame/support/test/tests/construct_runtime.rs +++ b/frame/support/test/tests/construct_runtime.rs @@ -24,234 +24,233 @@ use codec::MaxEncodedLen; use frame_support::{parameter_types, traits::PalletInfo as _}; use scale_info::TypeInfo; -use sp_core::{sr25519, H256}; +use sp_core::sr25519; use sp_runtime::{ generic, traits::{BlakeTwo256, Verify}, DispatchError, ModuleError, }; -mod system; - -pub trait Currency {} - parameter_types! { pub static IntegrityTestExec: u32 = 0; } +#[frame_support::pallet(dev_mode)] mod module1 { - use super::*; + use self::frame_system::pallet_prelude::*; + use frame_support::pallet_prelude::*; + use frame_support_test as frame_system; - pub trait Config: system::Config {} + #[pallet::pallet] + pub struct Pallet(_); - frame_support::decl_module! { - pub struct Module, I: Instance = DefaultInstance> for enum Call - where origin: ::RuntimeOrigin, system=system - { - #[weight = 0] - pub fn fail(_origin) -> frame_support::dispatch::DispatchResult { - Err(Error::::Something.into()) - } - } + #[pallet::config] + pub trait Config: frame_system::Config { + type RuntimeEvent: From> + + IsType<::RuntimeEvent>; } - #[derive( - Clone, PartialEq, Eq, Debug, codec::Encode, codec::Decode, TypeInfo, MaxEncodedLen, - )] - pub struct Origin(pub core::marker::PhantomData<(T, I)>); - - frame_support::decl_event! { - pub enum Event where - ::AccountId - { - A(AccountId), + #[pallet::call] + impl, I: 'static> Pallet { + pub fn fail(_origin: OriginFor) -> DispatchResult { + Err(Error::::Something.into()) } } - frame_support::decl_error! { - pub enum Error for Module, I: Instance> { - Something - } + #[pallet::origin] + #[derive(Clone, PartialEq, Eq, RuntimeDebug, Encode, Decode, MaxEncodedLen, TypeInfo)] + #[scale_info(skip_type_params(I))] + pub struct Origin(pub PhantomData<(T, I)>); + + #[pallet::event] + pub enum Event, I: 'static = ()> { + A(::AccountId), } - frame_support::decl_storage! { - trait Store for Module, I: Instance=DefaultInstance> as Module {} + #[pallet::error] + pub enum Error { + Something, } } +#[frame_support::pallet(dev_mode)] mod module2 { + use self::frame_system::pallet_prelude::*; use super::*; + use frame_support::pallet_prelude::*; + use frame_support_test as frame_system; - pub trait Config: system::Config {} + #[pallet::pallet] + pub struct Pallet(_); - frame_support::decl_module! { - pub struct Module for enum Call - where origin: ::RuntimeOrigin, system=system - { - #[weight = 0] - pub fn fail(_origin) -> frame_support::dispatch::DispatchResult { - Err(Error::::Something.into()) - } + #[pallet::config] + pub trait Config: frame_system::Config { + type RuntimeEvent: From> + IsType<::RuntimeEvent>; + } - fn integrity_test() { - IntegrityTestExec::mutate(|i| *i += 1); - } + #[pallet::hooks] + impl Hooks> for Pallet { + fn integrity_test() { + IntegrityTestExec::mutate(|i| *i += 1); } } - #[derive( - Clone, PartialEq, Eq, Debug, codec::Encode, codec::Decode, TypeInfo, MaxEncodedLen, - )] - pub struct Origin; - - frame_support::decl_event! { - pub enum Event { - A, + #[pallet::call] + impl Pallet { + pub fn fail(_origin: OriginFor) -> DispatchResult { + Err(Error::::Something.into()) } } - frame_support::decl_error! { - pub enum Error for Module { - Something - } + #[pallet::origin] + #[derive(Clone, PartialEq, Eq, RuntimeDebug, Encode, Decode, MaxEncodedLen, TypeInfo)] + pub struct Origin; + + #[pallet::event] + pub enum Event { + A, } - frame_support::decl_storage! { - trait Store for Module as Module {} + #[pallet::error] + pub enum Error { + Something, } } mod nested { use super::*; + #[frame_support::pallet(dev_mode)] pub mod module3 { + use self::frame_system::pallet_prelude::*; use super::*; + use frame_support::pallet_prelude::*; + use frame_support_test as frame_system; + + #[pallet::pallet] + pub struct Pallet(_); - pub trait Config: system::Config {} + #[pallet::config] + pub trait Config: frame_system::Config { + type RuntimeEvent: From> + + IsType<::RuntimeEvent>; + } - frame_support::decl_module! { - pub struct Module for enum Call - where origin: ::RuntimeOrigin, system=system - { - #[weight = 0] - pub fn fail(_origin) -> frame_support::dispatch::DispatchResult { - Err(Error::::Something.into()) - } + #[pallet::hooks] + impl Hooks> for Pallet { + fn integrity_test() { + IntegrityTestExec::mutate(|i| *i += 1); + } + } - fn integrity_test() { - IntegrityTestExec::mutate(|i| *i += 1); - } + #[pallet::call] + impl Pallet { + pub fn fail(_origin: OriginFor) -> DispatchResult { + Err(Error::::Something.into()) } } - #[derive( - Clone, PartialEq, Eq, Debug, codec::Encode, codec::Decode, TypeInfo, MaxEncodedLen, - )] + #[pallet::origin] + #[derive(Clone, PartialEq, Eq, RuntimeDebug, Encode, Decode, MaxEncodedLen, TypeInfo)] pub struct Origin; - frame_support::decl_event! { - pub enum Event { - A, - } + #[pallet::event] + pub enum Event { + A, } - frame_support::decl_error! { - pub enum Error for Module { - Something - } + #[pallet::error] + pub enum Error { + Something, } - frame_support::decl_storage! { - trait Store for Module as Module {} - add_extra_genesis { - build(|_config| {}) - } + #[pallet::genesis_config] + #[derive(Default)] + pub struct GenesisConfig {} + + #[pallet::genesis_build] + impl GenesisBuild for GenesisConfig { + fn build(&self) {} } } } +#[frame_support::pallet(dev_mode)] pub mod module3 { + use self::frame_system::pallet_prelude::*; use super::*; + use frame_support::pallet_prelude::*; + use frame_support_test as frame_system; - pub trait Config: system::Config {} + #[pallet::pallet] + pub struct Pallet(_); - frame_support::decl_module! { - pub struct Module for enum Call - where origin: ::RuntimeOrigin, system=system - { - #[weight = 0] - pub fn fail(_origin) -> frame_support::dispatch::DispatchResult { - Err(Error::::Something.into()) - } - #[weight = 0] - pub fn aux_1(_origin, #[compact] _data: u32) -> frame_support::dispatch::DispatchResult { - unreachable!() - } - #[weight = 0] - pub fn aux_2(_origin, _data: i32, #[compact] _data2: u32) -> frame_support::dispatch::DispatchResult { - unreachable!() - } - #[weight = 0] - fn aux_3(_origin, _data: i32, _data2: String) -> frame_support::dispatch::DispatchResult { - unreachable!() - } - #[weight = 3] - fn aux_4(_origin) -> frame_support::dispatch::DispatchResult { unreachable!() } - #[weight = (5, frame_support::dispatch::DispatchClass::Operational)] - fn operational(_origin) { unreachable!() } + #[pallet::config] + pub trait Config: frame_system::Config { + type RuntimeEvent: From> + IsType<::RuntimeEvent>; + } + + #[pallet::call] + impl Pallet { + pub fn fail(_origin: OriginFor) -> DispatchResult { + Err(Error::::Something.into()) + } + pub fn aux_1(_origin: OriginFor, #[pallet::compact] _data: u32) -> DispatchResult { + unreachable!() + } + pub fn aux_2( + _origin: OriginFor, + _data: i32, + #[pallet::compact] _data2: u32, + ) -> DispatchResult { + unreachable!() + } + #[pallet::weight(0)] + pub fn aux_3(_origin: OriginFor, _data: i32, _data2: String) -> DispatchResult { + unreachable!() + } + #[pallet::weight(3)] + pub fn aux_4(_origin: OriginFor) -> DispatchResult { + unreachable!() + } + #[pallet::weight((5, DispatchClass::Operational))] + pub fn operational(_origin: OriginFor) -> DispatchResult { + unreachable!() } } - #[derive( - Clone, PartialEq, Eq, Debug, codec::Encode, codec::Decode, TypeInfo, MaxEncodedLen, - )] - pub struct Origin(pub core::marker::PhantomData); + #[pallet::origin] + #[derive(Clone, PartialEq, Eq, RuntimeDebug, Encode, Decode, MaxEncodedLen, TypeInfo)] + pub struct Origin(pub PhantomData); - frame_support::decl_event! { - pub enum Event { - A, - } + #[pallet::event] + pub enum Event { + A, } - frame_support::decl_error! { - pub enum Error for Module { - Something - } + #[pallet::error] + pub enum Error { + Something, } - frame_support::decl_storage! { - trait Store for Module as Module {} - add_extra_genesis { - build(|_config| {}) - } + #[pallet::genesis_config] + #[derive(Default)] + pub struct GenesisConfig {} + + #[pallet::genesis_build] + impl GenesisBuild for GenesisConfig { + fn build(&self) {} } } -impl module1::Config for Runtime {} -impl module2::Config for Runtime {} -impl nested::module3::Config for Runtime {} -impl module3::Config for Runtime {} - +pub type BlockNumber = u64; pub type Signature = sr25519::Signature; pub type AccountId = ::Signer; -pub type BlockNumber = u64; -pub type Index = u64; - -fn test_pub() -> AccountId { - AccountId::from_raw([0; 32]) -} +pub type Header = generic::Header; +pub type UncheckedExtrinsic = generic::UncheckedExtrinsic; +pub type Block = generic::Block; -impl system::Config for Runtime { - type BaseCallFilter = frame_support::traits::Everything; - type Hash = H256; - type RuntimeOrigin = RuntimeOrigin; - type BlockNumber = BlockNumber; - type AccountId = AccountId; - type RuntimeEvent = RuntimeEvent; - type PalletInfo = PalletInfo; - type RuntimeCall = RuntimeCall; - type DbWeight = (); -} +use frame_support_test as system; frame_support::construct_runtime!( pub struct Runtime where @@ -261,12 +260,12 @@ frame_support::construct_runtime!( { System: system::{Pallet, Call, Event, Origin} = 30, Module1_1: module1::::{Pallet, Call, Storage, Event, Origin}, - Module2: module2::{Pallet, Call, Storage, Event, Origin}, + Module2: module2::{Pallet, Call, Storage, Event, Origin}, Module1_2: module1::::{Pallet, Call, Storage, Event, Origin}, - NestedModule3: nested::module3::{Pallet, Call, Config, Storage, Event, Origin}, - Module3: self::module3::{Pallet, Call, Config, Storage, Event, Origin}, - Module1_3: module1::::{Pallet, Storage} = 6, - Module1_4: module1::::{Pallet, Call} = 3, + NestedModule3: nested::module3::{Pallet, Call, Config, Storage, Event, Origin}, + Module3: self::module3::{Pallet, Call, Config, Storage, Event, Origin}, + Module1_3: module1::::{Pallet, Storage, Event } = 6, + Module1_4: module1::::{Pallet, Call, Event } = 3, Module1_5: module1::::{Pallet, Event}, Module1_6: module1::::{Pallet, Call, Storage, Event, Origin} = 1, Module1_7: module1::::{Pallet, Call, Storage, Event, Origin}, @@ -275,9 +274,57 @@ frame_support::construct_runtime!( } ); -pub type Header = generic::Header; -pub type Block = generic::Block; -pub type UncheckedExtrinsic = generic::UncheckedExtrinsic; +impl frame_support_test::Config for Runtime { + type BlockNumber = BlockNumber; + type AccountId = AccountId; + type BaseCallFilter = frame_support::traits::Everything; + type RuntimeOrigin = RuntimeOrigin; + type RuntimeCall = RuntimeCall; + type RuntimeEvent = RuntimeEvent; + type PalletInfo = PalletInfo; + type DbWeight = (); +} + +impl module1::Config for Runtime { + type RuntimeEvent = RuntimeEvent; +} +impl module1::Config for Runtime { + type RuntimeEvent = RuntimeEvent; +} +impl module1::Config for Runtime { + type RuntimeEvent = RuntimeEvent; +} +impl module1::Config for Runtime { + type RuntimeEvent = RuntimeEvent; +} +impl module1::Config for Runtime { + type RuntimeEvent = RuntimeEvent; +} +impl module1::Config for Runtime { + type RuntimeEvent = RuntimeEvent; +} +impl module1::Config for Runtime { + type RuntimeEvent = RuntimeEvent; +} +impl module1::Config for Runtime { + type RuntimeEvent = RuntimeEvent; +} +impl module1::Config for Runtime { + type RuntimeEvent = RuntimeEvent; +} +impl module2::Config for Runtime { + type RuntimeEvent = RuntimeEvent; +} +impl nested::module3::Config for Runtime { + type RuntimeEvent = RuntimeEvent; +} +impl module3::Config for Runtime { + type RuntimeEvent = RuntimeEvent; +} + +fn test_pub() -> AccountId { + AccountId::from_raw([0; 32]) +} #[test] fn check_modules_error_type() { @@ -471,12 +518,12 @@ fn call_codec() { #[test] fn call_compact_attr() { use codec::Encode; - let call: module3::Call = module3::Call::aux_1 { _data: 1 }; + let call: module3::Call = module3::Call::aux_1 { data: 1 }; let encoded = call.encode(); assert_eq!(2, encoded.len()); assert_eq!(vec![1, 4], encoded); - let call: module3::Call = module3::Call::aux_2 { _data: 1, _data2: 2 }; + let call: module3::Call = module3::Call::aux_2 { data: 1, data2: 2 }; let encoded = call.encode(); assert_eq!(6, encoded.len()); assert_eq!(vec![2, 1, 0, 0, 0, 8], encoded); @@ -491,7 +538,7 @@ fn call_encode_is_correct_and_decode_works() { let decoded = module3::Call::::decode(&mut &encoded[..]).unwrap(); assert_eq!(decoded, call); - let call: module3::Call = module3::Call::aux_3 { _data: 32, _data2: "hello".into() }; + let call: module3::Call = module3::Call::aux_3 { data: 32, data2: "hello".into() }; let encoded = call.encode(); assert_eq!(vec![3, 32, 0, 0, 0, 20, 104, 101, 108, 108, 111], encoded); let decoded = module3::Call::::decode(&mut &encoded[..]).unwrap(); @@ -594,70 +641,70 @@ fn test_metadata() { calls: Some(meta_type::>().into()), event: Some(meta_type::>().into()), constants: vec![], - error: None, + error: Some(meta_type::>().into()), index: 30, }, PalletMetadata { name: "Module1_1", - storage: Some(PalletStorageMetadata { prefix: "Instance1Module", entries: vec![] }), + storage: Some(PalletStorageMetadata { prefix: "Module1_1", entries: vec![] }), calls: Some(meta_type::>().into()), event: Some(meta_type::>().into()), constants: vec![], - error: None, + error: Some(meta_type::>().into()), index: 31, }, PalletMetadata { name: "Module2", - storage: Some(PalletStorageMetadata { prefix: "Module", entries: vec![] }), + storage: Some(PalletStorageMetadata { prefix: "Module2", entries: vec![] }), calls: Some(meta_type::>().into()), - event: Some(meta_type::().into()), + event: Some(meta_type::>().into()), constants: vec![], - error: None, + error: Some(meta_type::>().into()), index: 32, }, PalletMetadata { name: "Module1_2", - storage: Some(PalletStorageMetadata { prefix: "Instance2Module", entries: vec![] }), + storage: Some(PalletStorageMetadata { prefix: "Module1_2", entries: vec![] }), calls: Some(meta_type::>().into()), event: Some(meta_type::>().into()), constants: vec![], - error: None, + error: Some(meta_type::>().into()), index: 33, }, PalletMetadata { name: "NestedModule3", - storage: Some(PalletStorageMetadata { prefix: "Module", entries: vec![] }), + storage: Some(PalletStorageMetadata { prefix: "NestedModule3", entries: vec![] }), calls: Some(meta_type::>().into()), - event: Some(meta_type::().into()), + event: Some(meta_type::>().into()), constants: vec![], - error: None, + error: Some(meta_type::>().into()), index: 34, }, PalletMetadata { name: "Module3", - storage: Some(PalletStorageMetadata { prefix: "Module", entries: vec![] }), + storage: Some(PalletStorageMetadata { prefix: "Module3", entries: vec![] }), calls: Some(meta_type::>().into()), - event: Some(meta_type::().into()), + event: Some(meta_type::>().into()), constants: vec![], - error: None, + error: Some(meta_type::>().into()), index: 35, }, PalletMetadata { name: "Module1_3", - storage: Some(PalletStorageMetadata { prefix: "Instance3Module", entries: vec![] }), + storage: Some(PalletStorageMetadata { prefix: "Module1_3", entries: vec![] }), calls: None, - event: None, + event: Some(meta_type::>().into()), constants: vec![], - error: None, + error: Some(meta_type::>().into()), index: 6, }, PalletMetadata { name: "Module1_4", storage: None, calls: Some(meta_type::>().into()), - event: None, + event: Some(meta_type::>().into()), constants: vec![], - error: None, + error: Some(meta_type::>().into()), index: 3, }, PalletMetadata { @@ -666,45 +713,43 @@ fn test_metadata() { calls: None, event: Some(meta_type::>().into()), constants: vec![], - error: None, + error: Some(meta_type::>().into()), index: 4, }, PalletMetadata { name: "Module1_6", - storage: Some(PalletStorageMetadata { prefix: "Instance6Module", entries: vec![] }), + storage: Some(PalletStorageMetadata { prefix: "Module1_6", entries: vec![] }), calls: Some(meta_type::>().into()), event: Some(meta_type::>().into()), constants: vec![], - error: None, + error: Some(meta_type::>().into()), index: 1, }, PalletMetadata { name: "Module1_7", - storage: Some(PalletStorageMetadata { prefix: "Instance7Module", entries: vec![] }), + storage: Some(PalletStorageMetadata { prefix: "Module1_7", entries: vec![] }), calls: Some(meta_type::>().into()), - event: Some(PalletEventMetadata { - ty: meta_type::>(), - }), + event: Some(meta_type::>().into()), constants: vec![], - error: None, + error: Some(meta_type::>().into()), index: 2, }, PalletMetadata { name: "Module1_8", - storage: Some(PalletStorageMetadata { prefix: "Instance8Module", entries: vec![] }), + storage: Some(PalletStorageMetadata { prefix: "Module1_8", entries: vec![] }), calls: Some(meta_type::>().into()), event: Some(meta_type::>().into()), constants: vec![], - error: None, + error: Some(meta_type::>().into()), index: 12, }, PalletMetadata { name: "Module1_9", - storage: Some(PalletStorageMetadata { prefix: "Instance9Module", entries: vec![] }), + storage: Some(PalletStorageMetadata { prefix: "Module1_9", entries: vec![] }), calls: Some(meta_type::>().into()), event: Some(meta_type::>().into()), constants: vec![], - error: None, + error: Some(meta_type::>().into()), index: 13, }, ]; @@ -722,6 +767,7 @@ fn test_metadata() { let expected_metadata: RuntimeMetadataPrefixed = RuntimeMetadataLastVersion::new(pallets, extrinsic, meta_type::()).into(); let actual_metadata = Runtime::metadata(); + pretty_assertions::assert_eq!(actual_metadata, expected_metadata); } diff --git a/frame/support/test/tests/construct_runtime_ui/old_unsupported_pallet_decl.rs b/frame/support/test/tests/construct_runtime_ui/old_unsupported_pallet_decl.rs deleted file mode 100644 index 477ca3cc0..000000000 --- a/frame/support/test/tests/construct_runtime_ui/old_unsupported_pallet_decl.rs +++ /dev/null @@ -1,26 +0,0 @@ -use frame_support::construct_runtime; - -mod pallet_old { - pub trait Config: frame_system::Config {} - - decl_storage! { - trait Store for Module as Example {} - } - - decl_module! { - pub struct Module for enum Call where origin: T::RuntimeOrigin {} - } - -} -construct_runtime! { - pub struct Runtime where - UncheckedExtrinsic = UncheckedExtrinsic, - Block = Block, - NodeBlock = Block, - { - System: frame_system, - OldPallet: pallet_old, - } -} - -fn main() {} diff --git a/frame/support/test/tests/construct_runtime_ui/old_unsupported_pallet_decl.stderr b/frame/support/test/tests/construct_runtime_ui/old_unsupported_pallet_decl.stderr deleted file mode 100644 index b1338e892..000000000 --- a/frame/support/test/tests/construct_runtime_ui/old_unsupported_pallet_decl.stderr +++ /dev/null @@ -1,31 +0,0 @@ -error[E0433]: failed to resolve: could not find `tt_default_parts` in `pallet_old` - --> tests/construct_runtime_ui/old_unsupported_pallet_decl.rs:15:1 - | -15 | / construct_runtime! { -16 | | pub struct Runtime where -17 | | UncheckedExtrinsic = UncheckedExtrinsic, -18 | | Block = Block, -... | -23 | | } -24 | | } - | |_^ could not find `tt_default_parts` in `pallet_old` - | - = note: this error originates in the macro `construct_runtime` (in Nightly builds, run with -Z macro-backtrace for more info) - -error: cannot find macro `decl_storage` in this scope - --> tests/construct_runtime_ui/old_unsupported_pallet_decl.rs:6:2 - | -6 | decl_storage! { - | ^^^^^^^^^^^^ - | - = help: consider importing this macro: - frame_support::decl_storage - -error: cannot find macro `decl_module` in this scope - --> tests/construct_runtime_ui/old_unsupported_pallet_decl.rs:10:2 - | -10 | decl_module! { - | ^^^^^^^^^^^ - | - = help: consider importing this macro: - frame_support::decl_module diff --git a/frame/support/test/tests/construct_runtime_ui/undefined_event_part.stderr b/frame/support/test/tests/construct_runtime_ui/undefined_event_part.stderr index 1ca64f381..daf027a50 100644 --- a/frame/support/test/tests/construct_runtime_ui/undefined_event_part.stderr +++ b/frame/support/test/tests/construct_runtime_ui/undefined_event_part.stderr @@ -28,7 +28,9 @@ error[E0412]: cannot find type `Event` in module `pallet` | |_^ not found in `pallet` | = note: this error originates in the macro `construct_runtime` (in Nightly builds, run with -Z macro-backtrace for more info) -help: consider importing this enum +help: consider importing one of these items + | +1 | use frame_support_test::Event; | 1 | use frame_system::Event; | diff --git a/frame/support/test/tests/construct_runtime_ui/undefined_origin_part.stderr b/frame/support/test/tests/construct_runtime_ui/undefined_origin_part.stderr index dbade9fed..3e4326d3f 100644 --- a/frame/support/test/tests/construct_runtime_ui/undefined_origin_part.stderr +++ b/frame/support/test/tests/construct_runtime_ui/undefined_origin_part.stderr @@ -28,7 +28,9 @@ error[E0412]: cannot find type `Origin` in module `pallet` | |_^ not found in `pallet` | = note: this error originates in the macro `construct_runtime` (in Nightly builds, run with -Z macro-backtrace for more info) -help: consider importing this type alias +help: consider importing one of these items + | +1 | use frame_support_test::Origin; | 1 | use frame_system::Origin; | diff --git a/frame/support/test/tests/decl_module_ui.rs b/frame/support/test/tests/decl_module_ui.rs deleted file mode 100644 index 296ae3e58..000000000 --- a/frame/support/test/tests/decl_module_ui.rs +++ /dev/null @@ -1,32 +0,0 @@ -// This file is part of Substrate. - -// Copyright (C) Parity Technologies (UK) Ltd. -// SPDX-License-Identifier: Apache-2.0 - -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#[rustversion::attr(not(stable), ignore)] -#[cfg(not(feature = "disable-ui-tests"))] -#[test] -fn decl_module_ui() { - // Only run the ui tests when `RUN_UI_TESTS` is set. - if std::env::var("RUN_UI_TESTS").is_err() { - return - } - - // As trybuild is using `cargo check`, we don't need the real WASM binaries. - std::env::set_var("SKIP_WASM_BUILD", "1"); - - let t = trybuild::TestCases::new(); - t.compile_fail("tests/decl_module_ui/*.rs"); -} diff --git a/frame/support/test/tests/decl_module_ui/reserved_keyword_two_times_integrity_test.rs b/frame/support/test/tests/decl_module_ui/reserved_keyword_two_times_integrity_test.rs deleted file mode 100644 index 0f662b96e..000000000 --- a/frame/support/test/tests/decl_module_ui/reserved_keyword_two_times_integrity_test.rs +++ /dev/null @@ -1,7 +0,0 @@ -frame_support::decl_module! { - pub struct Module for enum Call where origin: T::RuntimeOrigin, system=self { - fn integrity_test() {} - - fn integrity_test() {} - } -} diff --git a/frame/support/test/tests/decl_module_ui/reserved_keyword_two_times_integrity_test.stderr b/frame/support/test/tests/decl_module_ui/reserved_keyword_two_times_integrity_test.stderr deleted file mode 100644 index 421270759..000000000 --- a/frame/support/test/tests/decl_module_ui/reserved_keyword_two_times_integrity_test.stderr +++ /dev/null @@ -1,19 +0,0 @@ -error: `integrity_test` can only be passed once as input. - --> tests/decl_module_ui/reserved_keyword_two_times_integrity_test.rs:1:1 - | -1 | / frame_support::decl_module! { -2 | | pub struct Module for enum Call where origin: T::RuntimeOrigin, system=self { -3 | | fn integrity_test() {} -4 | | -5 | | fn integrity_test() {} -6 | | } -7 | | } - | |_^ - | - = note: this error originates in the macro `$crate::decl_module` which comes from the expansion of the macro `frame_support::decl_module` (in Nightly builds, run with -Z macro-backtrace for more info) - -error[E0601]: `main` function not found in crate `$CRATE` - --> tests/decl_module_ui/reserved_keyword_two_times_integrity_test.rs:7:2 - | -7 | } - | ^ consider adding a `main` function to `$DIR/tests/decl_module_ui/reserved_keyword_two_times_integrity_test.rs` diff --git a/frame/support/test/tests/decl_module_ui/reserved_keyword_two_times_on_initialize.rs b/frame/support/test/tests/decl_module_ui/reserved_keyword_two_times_on_initialize.rs deleted file mode 100644 index ea0746b1c..000000000 --- a/frame/support/test/tests/decl_module_ui/reserved_keyword_two_times_on_initialize.rs +++ /dev/null @@ -1,13 +0,0 @@ -frame_support::decl_module! { - pub struct Module for enum Call where origin: T::RuntimeOrigin, system=self { - fn on_initialize() -> Weight { - 0 - } - - fn on_initialize() -> Weight { - 0 - } - } -} - -fn main() {} diff --git a/frame/support/test/tests/decl_module_ui/reserved_keyword_two_times_on_initialize.stderr b/frame/support/test/tests/decl_module_ui/reserved_keyword_two_times_on_initialize.stderr deleted file mode 100644 index 94bde853e..000000000 --- a/frame/support/test/tests/decl_module_ui/reserved_keyword_two_times_on_initialize.stderr +++ /dev/null @@ -1,13 +0,0 @@ -error: `on_initialize` can only be passed once as input. - --> tests/decl_module_ui/reserved_keyword_two_times_on_initialize.rs:1:1 - | -1 | / frame_support::decl_module! { -2 | | pub struct Module for enum Call where origin: T::RuntimeOrigin, system=self { -3 | | fn on_initialize() -> Weight { -4 | | 0 -... | -10 | | } -11 | | } - | |_^ - | - = note: this error originates in the macro `$crate::decl_module` which comes from the expansion of the macro `frame_support::decl_module` (in Nightly builds, run with -Z macro-backtrace for more info) diff --git a/frame/support/test/tests/decl_storage.rs b/frame/support/test/tests/decl_storage.rs deleted file mode 100644 index bee1dc83d..000000000 --- a/frame/support/test/tests/decl_storage.rs +++ /dev/null @@ -1,881 +0,0 @@ -// This file is part of Substrate. - -// Copyright (C) Parity Technologies (UK) Ltd. -// SPDX-License-Identifier: Apache-2.0 - -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#[cfg(test)] -// Do not complain about unused `dispatch` and `dispatch_aux`. -#[allow(dead_code)] -mod tests { - use frame_support::metadata_ir::*; - use sp_io::TestExternalities; - - frame_support::decl_module! { - pub struct Module for enum Call where origin: T::RuntimeOrigin, system=frame_support_test {} - } - - pub trait Config: frame_support_test::Config { - type Origin2: codec::Codec - + codec::EncodeLike - + Default - + codec::MaxEncodedLen - + scale_info::TypeInfo; - } - - frame_support::decl_storage! { - generate_storage_info - trait Store for Module as TestStorage { - // non-getters: pub / $default - - /// Hello, this is doc! - U32: Option; - pub PUBU32: Option; - U32MYDEF: Option; - pub PUBU32MYDEF: Option; - - // getters: pub / $default - // we need at least one type which uses T, otherwise GenesisConfig will complain. - GETU32 get(fn u32_getter): T::Origin2; - pub PUBGETU32 get(fn pub_u32_getter): u32; - GETU32WITHCONFIG get(fn u32_getter_with_config) config(): u32; - pub PUBGETU32WITHCONFIG get(fn pub_u32_getter_with_config) config(): u32; - GETU32MYDEF get(fn u32_getter_mydef): Option; - pub PUBGETU32MYDEF get(fn pub_u32_getter_mydef) config(): u32 = 3; - GETU32WITHCONFIGMYDEF get(fn u32_getter_with_config_mydef) config(): u32 = 2; - pub PUBGETU32WITHCONFIGMYDEF get(fn pub_u32_getter_with_config_mydef) config(): u32 = 1; - PUBGETU32WITHCONFIGMYDEFOPT get(fn pub_u32_getter_with_config_mydef_opt) config(): Option; - - GetU32WithBuilder get(fn u32_with_builder) build(|_| 1): u32; - GetOptU32WithBuilderSome get(fn opt_u32_with_builder_some) build(|_| Some(1)): Option; - GetOptU32WithBuilderNone get(fn opt_u32_with_builder_none) build(|_| None): Option; - - // map non-getters: pub / $default - MAPU32 max_values(3): map hasher(blake2_128_concat) u32 => Option<[u8; 4]>; - pub PUBMAPU32: map hasher(blake2_128_concat) u32 => Option<[u8; 4]>; - - // map getters: pub / $default - GETMAPU32 get(fn map_u32_getter): map hasher(blake2_128_concat) u32 => [u8; 4]; - pub PUBGETMAPU32 get(fn pub_map_u32_getter): map hasher(blake2_128_concat) u32 => [u8; 4]; - GETMAPU32MYDEF get(fn map_u32_getter_mydef): - map hasher(blake2_128_concat) u32 => [u8; 4] = *b"mapd"; - pub PUBGETMAPU32MYDEF get(fn pub_map_u32_getter_mydef): - map hasher(blake2_128_concat) u32 => [u8; 4] = *b"pubm"; - - DOUBLEMAP max_values(3): double_map - hasher(blake2_128_concat) u32, hasher(blake2_128_concat) u32 => Option<[u8; 4]>; - - DOUBLEMAP2: double_map - hasher(blake2_128_concat) u32, hasher(blake2_128_concat) u32 => Option<[u8; 4]>; - - COMPLEXTYPE1: (::std::option::Option,); - COMPLEXTYPE2: ([[(u16, Option<()>); 32]; 12], u32); - COMPLEXTYPE3: [u32; 25]; - - NMAP: nmap hasher(blake2_128_concat) u32, hasher(twox_64_concat) u16 => u8; - NMAP2: nmap hasher(blake2_128_concat) u32 => u8; - } - add_extra_genesis { - build(|_| {}); - } - } - - struct TraitImpl {} - - impl frame_support_test::Config for TraitImpl { - type RuntimeOrigin = u32; - type BlockNumber = u32; - type PalletInfo = frame_support_test::PanicPalletInfo; - type DbWeight = (); - } - - impl Config for TraitImpl { - type Origin2 = u32; - } - - fn expected_metadata() -> PalletStorageMetadataIR { - PalletStorageMetadataIR { - prefix: "TestStorage", - entries: vec![ - StorageEntryMetadataIR { - name: "U32", - modifier: StorageEntryModifierIR::Optional, - ty: StorageEntryTypeIR::Plain(scale_info::meta_type::()), - default: vec![0], - docs: vec![" Hello, this is doc!"], - }, - StorageEntryMetadataIR { - name: "PUBU32", - modifier: StorageEntryModifierIR::Optional, - ty: StorageEntryTypeIR::Plain(scale_info::meta_type::()), - default: vec![0], - docs: vec![], - }, - StorageEntryMetadataIR { - name: "U32MYDEF", - modifier: StorageEntryModifierIR::Optional, - ty: StorageEntryTypeIR::Plain(scale_info::meta_type::()), - default: vec![0], - docs: vec![], - }, - StorageEntryMetadataIR { - name: "PUBU32MYDEF", - modifier: StorageEntryModifierIR::Optional, - ty: StorageEntryTypeIR::Plain(scale_info::meta_type::()), - default: vec![0], - docs: vec![], - }, - StorageEntryMetadataIR { - name: "GETU32", - modifier: StorageEntryModifierIR::Default, - ty: StorageEntryTypeIR::Plain(scale_info::meta_type::()), - default: vec![0, 0, 0, 0], - docs: vec![], - }, - StorageEntryMetadataIR { - name: "PUBGETU32", - modifier: StorageEntryModifierIR::Default, - ty: StorageEntryTypeIR::Plain(scale_info::meta_type::()), - default: vec![0, 0, 0, 0], - docs: vec![], - }, - StorageEntryMetadataIR { - name: "GETU32WITHCONFIG", - modifier: StorageEntryModifierIR::Default, - ty: StorageEntryTypeIR::Plain(scale_info::meta_type::()), - default: vec![0, 0, 0, 0], - docs: vec![], - }, - StorageEntryMetadataIR { - name: "PUBGETU32WITHCONFIG", - modifier: StorageEntryModifierIR::Default, - ty: StorageEntryTypeIR::Plain(scale_info::meta_type::()), - default: vec![0, 0, 0, 0], - docs: vec![], - }, - StorageEntryMetadataIR { - name: "GETU32MYDEF", - modifier: StorageEntryModifierIR::Optional, - ty: StorageEntryTypeIR::Plain(scale_info::meta_type::()), - default: vec![0], - docs: vec![], - }, - StorageEntryMetadataIR { - name: "PUBGETU32MYDEF", - modifier: StorageEntryModifierIR::Default, - ty: StorageEntryTypeIR::Plain(scale_info::meta_type::()), - default: vec![3, 0, 0, 0], - docs: vec![], - }, - StorageEntryMetadataIR { - name: "GETU32WITHCONFIGMYDEF", - modifier: StorageEntryModifierIR::Default, - ty: StorageEntryTypeIR::Plain(scale_info::meta_type::()), - default: vec![2, 0, 0, 0], - docs: vec![], - }, - StorageEntryMetadataIR { - name: "PUBGETU32WITHCONFIGMYDEF", - modifier: StorageEntryModifierIR::Default, - ty: StorageEntryTypeIR::Plain(scale_info::meta_type::()), - default: vec![1, 0, 0, 0], - docs: vec![], - }, - StorageEntryMetadataIR { - name: "PUBGETU32WITHCONFIGMYDEFOPT", - modifier: StorageEntryModifierIR::Optional, - ty: StorageEntryTypeIR::Plain(scale_info::meta_type::()), - default: vec![0], - docs: vec![], - }, - StorageEntryMetadataIR { - name: "GetU32WithBuilder", - modifier: StorageEntryModifierIR::Default, - ty: StorageEntryTypeIR::Plain(scale_info::meta_type::()), - default: vec![0, 0, 0, 0], - docs: vec![], - }, - StorageEntryMetadataIR { - name: "GetOptU32WithBuilderSome", - modifier: StorageEntryModifierIR::Optional, - ty: StorageEntryTypeIR::Plain(scale_info::meta_type::()), - default: vec![0], - docs: vec![], - }, - StorageEntryMetadataIR { - name: "GetOptU32WithBuilderNone", - modifier: StorageEntryModifierIR::Optional, - ty: StorageEntryTypeIR::Plain(scale_info::meta_type::()), - default: vec![0], - docs: vec![], - }, - StorageEntryMetadataIR { - name: "MAPU32", - modifier: StorageEntryModifierIR::Optional, - ty: StorageEntryTypeIR::Map { - hashers: vec![StorageHasherIR::Blake2_128Concat], - key: scale_info::meta_type::(), - value: scale_info::meta_type::<[u8; 4]>(), - }, - default: vec![0], - docs: vec![], - }, - StorageEntryMetadataIR { - name: "PUBMAPU32", - modifier: StorageEntryModifierIR::Optional, - ty: StorageEntryTypeIR::Map { - hashers: vec![StorageHasherIR::Blake2_128Concat], - key: scale_info::meta_type::(), - value: scale_info::meta_type::<[u8; 4]>(), - }, - default: vec![0], - docs: vec![], - }, - StorageEntryMetadataIR { - name: "GETMAPU32", - modifier: StorageEntryModifierIR::Default, - ty: StorageEntryTypeIR::Map { - hashers: vec![StorageHasherIR::Blake2_128Concat], - key: scale_info::meta_type::(), - value: scale_info::meta_type::<[u8; 4]>(), - }, - default: vec![0, 0, 0, 0], - docs: vec![], - }, - StorageEntryMetadataIR { - name: "PUBGETMAPU32", - modifier: StorageEntryModifierIR::Default, - ty: StorageEntryTypeIR::Map { - hashers: vec![StorageHasherIR::Blake2_128Concat], - key: scale_info::meta_type::(), - value: scale_info::meta_type::<[u8; 4]>(), - }, - default: vec![0, 0, 0, 0], - docs: vec![], - }, - StorageEntryMetadataIR { - name: "GETMAPU32MYDEF", - modifier: StorageEntryModifierIR::Default, - ty: StorageEntryTypeIR::Map { - hashers: vec![StorageHasherIR::Blake2_128Concat], - key: scale_info::meta_type::(), - value: scale_info::meta_type::<[u8; 4]>(), - }, - default: vec![109, 97, 112, 100], // "map" - docs: vec![], - }, - StorageEntryMetadataIR { - name: "PUBGETMAPU32MYDEF", - modifier: StorageEntryModifierIR::Default, - ty: StorageEntryTypeIR::Map { - hashers: vec![StorageHasherIR::Blake2_128Concat], - key: scale_info::meta_type::(), - value: scale_info::meta_type::<[u8; 4]>(), - }, - default: vec![112, 117, 98, 109], // "pubmap" - docs: vec![], - }, - StorageEntryMetadataIR { - name: "DOUBLEMAP", - modifier: StorageEntryModifierIR::Optional, - ty: StorageEntryTypeIR::Map { - hashers: vec![ - StorageHasherIR::Blake2_128Concat, - StorageHasherIR::Blake2_128Concat, - ], - key: scale_info::meta_type::<(u32, u32)>(), - value: scale_info::meta_type::<[u8; 4]>(), - }, - default: vec![0], - docs: vec![], - }, - StorageEntryMetadataIR { - name: "DOUBLEMAP2", - modifier: StorageEntryModifierIR::Optional, - ty: StorageEntryTypeIR::Map { - hashers: vec![ - StorageHasherIR::Blake2_128Concat, - StorageHasherIR::Blake2_128Concat, - ], - key: scale_info::meta_type::<(u32, u32)>(), - value: scale_info::meta_type::<[u8; 4]>(), - }, - default: vec![0], - docs: vec![], - }, - StorageEntryMetadataIR { - name: "COMPLEXTYPE1", - modifier: StorageEntryModifierIR::Default, - ty: StorageEntryTypeIR::Plain(scale_info::meta_type::<(Option,)>()), - default: vec![0], - docs: vec![], - }, - StorageEntryMetadataIR { - name: "COMPLEXTYPE2", - modifier: StorageEntryModifierIR::Default, - ty: StorageEntryTypeIR::Plain(scale_info::meta_type::<( - [[(u16, Option<()>); 32]; 12], - u32, - )>()), - default: [0u8; 1156].to_vec(), - docs: vec![], - }, - StorageEntryMetadataIR { - name: "COMPLEXTYPE3", - modifier: StorageEntryModifierIR::Default, - ty: StorageEntryTypeIR::Plain(scale_info::meta_type::<[u32; 25]>()), - default: [0u8; 100].to_vec(), - docs: vec![], - }, - StorageEntryMetadataIR { - name: "NMAP", - modifier: StorageEntryModifierIR::Default, - ty: StorageEntryTypeIR::Map { - key: scale_info::meta_type::<(u32, u16)>(), - hashers: vec![ - StorageHasherIR::Blake2_128Concat, - StorageHasherIR::Twox64Concat, - ], - value: scale_info::meta_type::(), - }, - default: vec![0], - docs: vec![], - }, - StorageEntryMetadataIR { - name: "NMAP2", - modifier: StorageEntryModifierIR::Default, - ty: StorageEntryTypeIR::Map { - key: scale_info::meta_type::(), - hashers: vec![StorageHasherIR::Blake2_128Concat], - value: scale_info::meta_type::(), - }, - default: vec![0], - docs: vec![], - }, - ], - } - } - - #[test] - fn storage_info() { - use frame_support::{ - storage::storage_prefix as prefix, - traits::{StorageInfo, StorageInfoTrait}, - }; - - pretty_assertions::assert_eq!( - >::storage_info(), - vec![ - StorageInfo { - pallet_name: b"TestStorage".to_vec(), - storage_name: b"U32".to_vec(), - prefix: prefix(b"TestStorage", b"U32").to_vec(), - max_values: Some(1), - max_size: Some(4), - }, - StorageInfo { - pallet_name: b"TestStorage".to_vec(), - storage_name: b"PUBU32".to_vec(), - prefix: prefix(b"TestStorage", b"PUBU32").to_vec(), - max_values: Some(1), - max_size: Some(4), - }, - StorageInfo { - pallet_name: b"TestStorage".to_vec(), - storage_name: b"U32MYDEF".to_vec(), - prefix: prefix(b"TestStorage", b"U32MYDEF").to_vec(), - max_values: Some(1), - max_size: Some(4), - }, - StorageInfo { - pallet_name: b"TestStorage".to_vec(), - storage_name: b"PUBU32MYDEF".to_vec(), - prefix: prefix(b"TestStorage", b"PUBU32MYDEF").to_vec(), - max_values: Some(1), - max_size: Some(4), - }, - StorageInfo { - pallet_name: b"TestStorage".to_vec(), - storage_name: b"GETU32".to_vec(), - prefix: prefix(b"TestStorage", b"GETU32").to_vec(), - max_values: Some(1), - max_size: Some(4), - }, - StorageInfo { - pallet_name: b"TestStorage".to_vec(), - storage_name: b"PUBGETU32".to_vec(), - prefix: prefix(b"TestStorage", b"PUBGETU32").to_vec(), - max_values: Some(1), - max_size: Some(4), - }, - StorageInfo { - pallet_name: b"TestStorage".to_vec(), - storage_name: b"GETU32WITHCONFIG".to_vec(), - prefix: prefix(b"TestStorage", b"GETU32WITHCONFIG").to_vec(), - max_values: Some(1), - max_size: Some(4), - }, - StorageInfo { - pallet_name: b"TestStorage".to_vec(), - storage_name: b"PUBGETU32WITHCONFIG".to_vec(), - prefix: prefix(b"TestStorage", b"PUBGETU32WITHCONFIG").to_vec(), - max_values: Some(1), - max_size: Some(4), - }, - StorageInfo { - pallet_name: b"TestStorage".to_vec(), - storage_name: b"GETU32MYDEF".to_vec(), - prefix: prefix(b"TestStorage", b"GETU32MYDEF").to_vec(), - max_values: Some(1), - max_size: Some(4), - }, - StorageInfo { - pallet_name: b"TestStorage".to_vec(), - storage_name: b"PUBGETU32MYDEF".to_vec(), - prefix: prefix(b"TestStorage", b"PUBGETU32MYDEF").to_vec(), - max_values: Some(1), - max_size: Some(4), - }, - StorageInfo { - pallet_name: b"TestStorage".to_vec(), - storage_name: b"GETU32WITHCONFIGMYDEF".to_vec(), - prefix: prefix(b"TestStorage", b"GETU32WITHCONFIGMYDEF").to_vec(), - max_values: Some(1), - max_size: Some(4), - }, - StorageInfo { - pallet_name: b"TestStorage".to_vec(), - storage_name: b"PUBGETU32WITHCONFIGMYDEF".to_vec(), - prefix: prefix(b"TestStorage", b"PUBGETU32WITHCONFIGMYDEF").to_vec(), - max_values: Some(1), - max_size: Some(4), - }, - StorageInfo { - pallet_name: b"TestStorage".to_vec(), - storage_name: b"PUBGETU32WITHCONFIGMYDEFOPT".to_vec(), - prefix: prefix(b"TestStorage", b"PUBGETU32WITHCONFIGMYDEFOPT").to_vec(), - max_values: Some(1), - max_size: Some(4), - }, - StorageInfo { - pallet_name: b"TestStorage".to_vec(), - storage_name: b"GetU32WithBuilder".to_vec(), - prefix: prefix(b"TestStorage", b"GetU32WithBuilder").to_vec(), - max_values: Some(1), - max_size: Some(4), - }, - StorageInfo { - pallet_name: b"TestStorage".to_vec(), - storage_name: b"GetOptU32WithBuilderSome".to_vec(), - prefix: prefix(b"TestStorage", b"GetOptU32WithBuilderSome").to_vec(), - max_values: Some(1), - max_size: Some(4), - }, - StorageInfo { - pallet_name: b"TestStorage".to_vec(), - storage_name: b"GetOptU32WithBuilderNone".to_vec(), - prefix: prefix(b"TestStorage", b"GetOptU32WithBuilderNone").to_vec(), - max_values: Some(1), - max_size: Some(4), - }, - StorageInfo { - pallet_name: b"TestStorage".to_vec(), - storage_name: b"MAPU32".to_vec(), - prefix: prefix(b"TestStorage", b"MAPU32").to_vec(), - max_values: Some(3), - max_size: Some(8 + 16), - }, - StorageInfo { - pallet_name: b"TestStorage".to_vec(), - storage_name: b"PUBMAPU32".to_vec(), - prefix: prefix(b"TestStorage", b"PUBMAPU32").to_vec(), - max_values: None, - max_size: Some(8 + 16), - }, - StorageInfo { - pallet_name: b"TestStorage".to_vec(), - storage_name: b"GETMAPU32".to_vec(), - prefix: prefix(b"TestStorage", b"GETMAPU32").to_vec(), - max_values: None, - max_size: Some(8 + 16), - }, - StorageInfo { - pallet_name: b"TestStorage".to_vec(), - storage_name: b"PUBGETMAPU32".to_vec(), - prefix: prefix(b"TestStorage", b"PUBGETMAPU32").to_vec(), - max_values: None, - max_size: Some(8 + 16), - }, - StorageInfo { - pallet_name: b"TestStorage".to_vec(), - storage_name: b"GETMAPU32MYDEF".to_vec(), - prefix: prefix(b"TestStorage", b"GETMAPU32MYDEF").to_vec(), - max_values: None, - max_size: Some(8 + 16), - }, - StorageInfo { - pallet_name: b"TestStorage".to_vec(), - storage_name: b"PUBGETMAPU32MYDEF".to_vec(), - prefix: prefix(b"TestStorage", b"PUBGETMAPU32MYDEF").to_vec(), - max_values: None, - max_size: Some(8 + 16), - }, - StorageInfo { - pallet_name: b"TestStorage".to_vec(), - storage_name: b"DOUBLEMAP".to_vec(), - prefix: prefix(b"TestStorage", b"DOUBLEMAP").to_vec(), - max_values: Some(3), - max_size: Some(12 + 16 + 16), - }, - StorageInfo { - pallet_name: b"TestStorage".to_vec(), - storage_name: b"DOUBLEMAP2".to_vec(), - prefix: prefix(b"TestStorage", b"DOUBLEMAP2").to_vec(), - max_values: None, - max_size: Some(12 + 16 + 16), - }, - StorageInfo { - pallet_name: b"TestStorage".to_vec(), - storage_name: b"COMPLEXTYPE1".to_vec(), - prefix: prefix(b"TestStorage", b"COMPLEXTYPE1").to_vec(), - max_values: Some(1), - max_size: Some(5), - }, - StorageInfo { - pallet_name: b"TestStorage".to_vec(), - storage_name: b"COMPLEXTYPE2".to_vec(), - prefix: prefix(b"TestStorage", b"COMPLEXTYPE2").to_vec(), - max_values: Some(1), - max_size: Some(1156), - }, - StorageInfo { - pallet_name: b"TestStorage".to_vec(), - storage_name: b"COMPLEXTYPE3".to_vec(), - prefix: prefix(b"TestStorage", b"COMPLEXTYPE3").to_vec(), - max_values: Some(1), - max_size: Some(100), - }, - StorageInfo { - pallet_name: b"TestStorage".to_vec(), - storage_name: b"NMAP".to_vec(), - prefix: prefix(b"TestStorage", b"NMAP").to_vec(), - max_values: None, - max_size: Some(16 + 4 + 8 + 2 + 1), - }, - StorageInfo { - pallet_name: b"TestStorage".to_vec(), - storage_name: b"NMAP2".to_vec(), - prefix: prefix(b"TestStorage", b"NMAP2").to_vec(), - max_values: None, - max_size: Some(16 + 4 + 1), - }, - ], - ); - } - - #[test] - fn store_metadata() { - let metadata = Module::::storage_metadata(); - pretty_assertions::assert_eq!(expected_metadata(), metadata); - } - - #[test] - fn check_genesis_config() { - let config = GenesisConfig::default(); - assert_eq!(config.u32_getter_with_config, 0u32); - assert_eq!(config.pub_u32_getter_with_config, 0u32); - - assert_eq!(config.pub_u32_getter_mydef, 3u32); - assert_eq!(config.u32_getter_with_config_mydef, 2u32); - assert_eq!(config.pub_u32_getter_with_config_mydef, 1u32); - assert_eq!(config.pub_u32_getter_with_config_mydef_opt, 0u32); - } - - #[test] - fn check_builder_config() { - let config = GenesisConfig::default(); - let storage = config.build_storage().unwrap(); - TestExternalities::from(storage).execute_with(|| { - assert_eq!(Module::::u32_with_builder(), 1); - assert_eq!(Module::::opt_u32_with_builder_some(), Some(1)); - assert_eq!(Module::::opt_u32_with_builder_none(), None); - }) - } -} - -#[cfg(test)] -#[allow(dead_code)] -mod test2 { - pub trait Config: frame_support_test::Config {} - - frame_support::decl_module! { - pub struct Module for enum Call where origin: T::RuntimeOrigin, system=frame_support_test {} - } - - type PairOf = (T, T); - - frame_support::decl_storage! { - trait Store for Module as TestStorage { - SingleDef : u32; - PairDef : PairOf; - Single : Option; - Pair : (u32, u32); - } - add_extra_genesis { - config(_marker) : ::std::marker::PhantomData; - config(extra_field) : u32 = 32; - build(|_| {}); - } - } - - struct TraitImpl {} - - impl frame_support_test::Config for TraitImpl { - type RuntimeOrigin = u32; - type BlockNumber = u32; - type PalletInfo = frame_support_test::PanicPalletInfo; - type DbWeight = (); - } - - impl Config for TraitImpl {} - - #[test] - fn storage_info() { - use frame_support::{ - storage::storage_prefix as prefix, - traits::{StorageInfo, StorageInfoTrait}, - }; - pretty_assertions::assert_eq!( - >::storage_info(), - vec![ - StorageInfo { - pallet_name: b"TestStorage".to_vec(), - storage_name: b"SingleDef".to_vec(), - prefix: prefix(b"TestStorage", b"SingleDef").to_vec(), - max_values: Some(1), - max_size: None, - }, - StorageInfo { - pallet_name: b"TestStorage".to_vec(), - storage_name: b"PairDef".to_vec(), - prefix: prefix(b"TestStorage", b"PairDef").to_vec(), - max_values: Some(1), - max_size: None, - }, - StorageInfo { - pallet_name: b"TestStorage".to_vec(), - storage_name: b"Single".to_vec(), - prefix: prefix(b"TestStorage", b"Single").to_vec(), - max_values: Some(1), - max_size: None, - }, - StorageInfo { - pallet_name: b"TestStorage".to_vec(), - storage_name: b"Pair".to_vec(), - prefix: prefix(b"TestStorage", b"Pair").to_vec(), - max_values: Some(1), - max_size: None, - }, - ], - ); - } -} - -#[cfg(test)] -#[allow(dead_code)] -mod test3 { - pub trait Config: frame_support_test::Config {} - - frame_support::decl_module! { - pub struct Module for enum Call where origin: T::RuntimeOrigin, system=frame_support_test {} - } - frame_support::decl_storage! { - trait Store for Module as Test { - Foo get(fn foo) config(initial_foo): u32; - } - } - - type PairOf = (T, T); - - struct TraitImpl {} - - impl frame_support_test::Config for TraitImpl { - type RuntimeOrigin = u32; - type BlockNumber = u32; - type PalletInfo = frame_support_test::PanicPalletInfo; - type DbWeight = (); - } - - impl Config for TraitImpl {} -} - -#[cfg(test)] -#[allow(dead_code)] -mod test_append_and_len { - use codec::{Decode, Encode}; - use sp_io::TestExternalities; - - pub trait Config: frame_support_test::Config {} - - frame_support::decl_module! { - pub struct Module for enum Call where origin: T::RuntimeOrigin, system=frame_support_test {} - } - - #[derive(PartialEq, Eq, Clone, Encode, Decode, scale_info::TypeInfo)] - struct NoDef(u32); - - frame_support::decl_storage! { - trait Store for Module as Test { - NoDefault: Option; - - JustVec: Vec; - JustVecWithDefault: Vec = vec![6, 9]; - OptionVec: Option>; - - MapVec: map hasher(blake2_128_concat) u32 => Vec; - MapVecWithDefault: map hasher(blake2_128_concat) u32 => Vec = vec![6, 9]; - OptionMapVec: map hasher(blake2_128_concat) u32 => Option>; - - DoubleMapVec: double_map hasher(blake2_128_concat) u32, hasher(blake2_128_concat) u32 => Vec; - DoubleMapVecWithDefault: double_map hasher(blake2_128_concat) u32, hasher(blake2_128_concat) u32 => Vec = vec![6, 9]; - OptionDoubleMapVec: double_map hasher(blake2_128_concat) u32, hasher(blake2_128_concat) u32 => Option>; - } - } - - struct Test {} - - impl frame_support_test::Config for Test { - type RuntimeOrigin = u32; - type BlockNumber = u32; - type PalletInfo = frame_support_test::PanicPalletInfo; - type DbWeight = (); - } - - impl Config for Test {} - - #[test] - fn default_for_option() { - TestExternalities::default().execute_with(|| { - assert_eq!(OptionVec::get(), None); - assert!(JustVec::get().is_empty()); - }); - } - - #[test] - fn append_works() { - TestExternalities::default().execute_with(|| { - for val in &[1, 2, 3, 4, 5] { - MapVec::append(1, val); - } - assert_eq!(MapVec::get(1), vec![1, 2, 3, 4, 5]); - - MapVec::remove(1); - MapVec::append(1, 1); - assert_eq!(MapVec::get(1), vec![1]); - - for val in &[1, 2, 3, 4, 5] { - JustVec::append(val); - } - assert_eq!(JustVec::get(), vec![1, 2, 3, 4, 5]); - - JustVec::kill(); - JustVec::append(1); - assert_eq!(JustVec::get(), vec![1]); - }); - } - - #[test] - fn append_overwrites_invalid_data() { - TestExternalities::default().execute_with(|| { - let key = JustVec::hashed_key(); - // Set it to some invalid value. - frame_support::storage::unhashed::put_raw(&key, b"1"); - assert!(JustVec::get().is_empty()); - assert_eq!(frame_support::storage::unhashed::get_raw(&key), Some(b"1".to_vec())); - - JustVec::append(1); - JustVec::append(2); - assert_eq!(JustVec::get(), vec![1, 2]); - }); - } - - #[test] - fn append_overwrites_default() { - TestExternalities::default().execute_with(|| { - assert_eq!(JustVecWithDefault::get(), vec![6, 9]); - JustVecWithDefault::append(1); - assert_eq!(JustVecWithDefault::get(), vec![1]); - - assert_eq!(MapVecWithDefault::get(0), vec![6, 9]); - MapVecWithDefault::append(0, 1); - assert_eq!(MapVecWithDefault::get(0), vec![1]); - - assert_eq!(OptionVec::get(), None); - OptionVec::append(1); - assert_eq!(OptionVec::get(), Some(vec![1])); - }); - } - - #[test] - fn len_works() { - TestExternalities::default().execute_with(|| { - JustVec::put(&vec![1, 2, 3, 4]); - OptionVec::put(&vec![1, 2, 3, 4, 5]); - MapVec::insert(1, &vec![1, 2, 3, 4, 5, 6]); - DoubleMapVec::insert(0, 1, &vec![1, 2]); - - assert_eq!(JustVec::decode_len().unwrap(), 4); - assert_eq!(OptionVec::decode_len().unwrap(), 5); - assert_eq!(MapVec::decode_len(1).unwrap(), 6); - assert_eq!(DoubleMapVec::decode_len(0, 1).unwrap(), 2); - }); - } - - // `decode_len` should always return `None` for default assigments - // in `decl_storage!`. - #[test] - fn len_works_ignores_default_assignment() { - TestExternalities::default().execute_with(|| { - // vec - assert!(JustVec::get().is_empty()); - assert_eq!(JustVec::decode_len(), None); - - assert_eq!(JustVecWithDefault::get(), vec![6, 9]); - assert_eq!(JustVecWithDefault::decode_len(), None); - - assert_eq!(OptionVec::get(), None); - assert_eq!(OptionVec::decode_len(), None); - - // map - assert!(MapVec::get(0).is_empty()); - assert_eq!(MapVec::decode_len(0), None); - - assert_eq!(MapVecWithDefault::get(0), vec![6, 9]); - assert_eq!(MapVecWithDefault::decode_len(0), None); - - assert_eq!(OptionMapVec::get(0), None); - assert_eq!(OptionMapVec::decode_len(0), None); - - // Double map - assert!(DoubleMapVec::get(0, 0).is_empty()); - assert_eq!(DoubleMapVec::decode_len(0, 1), None); - - assert_eq!(DoubleMapVecWithDefault::get(0, 0), vec![6, 9]); - assert_eq!(DoubleMapVecWithDefault::decode_len(0, 1), None); - - assert_eq!(OptionDoubleMapVec::get(0, 0), None); - assert_eq!(OptionDoubleMapVec::decode_len(0, 1), None); - }); - } -} diff --git a/frame/support/test/tests/decl_storage_ui.rs b/frame/support/test/tests/decl_storage_ui.rs deleted file mode 100644 index 1414c8a99..000000000 --- a/frame/support/test/tests/decl_storage_ui.rs +++ /dev/null @@ -1,32 +0,0 @@ -// This file is part of Substrate. - -// Copyright (C) Parity Technologies (UK) Ltd. -// SPDX-License-Identifier: Apache-2.0 - -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#[rustversion::attr(not(stable), ignore)] -#[cfg(not(feature = "disable-ui-tests"))] -#[test] -fn decl_storage_ui() { - // Only run the ui tests when `RUN_UI_TESTS` is set. - if std::env::var("RUN_UI_TESTS").is_err() { - return - } - - // As trybuild is using `cargo check`, we don't need the real WASM binaries. - std::env::set_var("SKIP_WASM_BUILD", "1"); - - let t = trybuild::TestCases::new(); - t.compile_fail("tests/decl_storage_ui/*.rs"); -} diff --git a/frame/support/test/tests/decl_storage_ui/config_duplicate.rs b/frame/support/test/tests/decl_storage_ui/config_duplicate.rs deleted file mode 100644 index a6e1bfad5..000000000 --- a/frame/support/test/tests/decl_storage_ui/config_duplicate.rs +++ /dev/null @@ -1,31 +0,0 @@ -// This file is part of Substrate. - -// Copyright (C) Parity Technologies (UK) Ltd. -// SPDX-License-Identifier: Apache-2.0 - -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -pub trait Config: frame_support_test::Config {} - -frame_support::decl_module! { - pub struct Module for enum Call where origin: T::RuntimeOrigin, system=frame_support_test {} -} - -frame_support::decl_storage!{ - trait Store for Module as FinalKeysNone { - pub Value config(value): u32; - pub Value2 config(value): u32; - } -} - -fn main() {} diff --git a/frame/support/test/tests/decl_storage_ui/config_duplicate.stderr b/frame/support/test/tests/decl_storage_ui/config_duplicate.stderr deleted file mode 100644 index f6303f277..000000000 --- a/frame/support/test/tests/decl_storage_ui/config_duplicate.stderr +++ /dev/null @@ -1,5 +0,0 @@ -error: `config()`/`get()` with the same name already defined. - --> $DIR/config_duplicate.rs:27:21 - | -27 | pub Value2 config(value): u32; - | ^^^^^ diff --git a/frame/support/test/tests/decl_storage_ui/config_get_duplicate.rs b/frame/support/test/tests/decl_storage_ui/config_get_duplicate.rs deleted file mode 100644 index c40dd0e44..000000000 --- a/frame/support/test/tests/decl_storage_ui/config_get_duplicate.rs +++ /dev/null @@ -1,31 +0,0 @@ -// This file is part of Substrate. - -// Copyright (C) Parity Technologies (UK) Ltd. -// SPDX-License-Identifier: Apache-2.0 - -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -pub trait Config: frame_support_test::Config {} - -frame_support::decl_module! { - pub struct Module for enum Call where origin: T::RuntimeOrigin, system=frame_support_test {} -} - -frame_support::decl_storage!{ - trait Store for Module as FinalKeysNone { - pub Value get(fn value) config(): u32; - pub Value2 config(value): u32; - } -} - -fn main() {} diff --git a/frame/support/test/tests/decl_storage_ui/config_get_duplicate.stderr b/frame/support/test/tests/decl_storage_ui/config_get_duplicate.stderr deleted file mode 100644 index 9377b718c..000000000 --- a/frame/support/test/tests/decl_storage_ui/config_get_duplicate.stderr +++ /dev/null @@ -1,5 +0,0 @@ -error: `config()`/`get()` with the same name already defined. - --> $DIR/config_get_duplicate.rs:27:21 - | -27 | pub Value2 config(value): u32; - | ^^^^^ diff --git a/frame/support/test/tests/decl_storage_ui/get_duplicate.rs b/frame/support/test/tests/decl_storage_ui/get_duplicate.rs deleted file mode 100644 index 23c89d276..000000000 --- a/frame/support/test/tests/decl_storage_ui/get_duplicate.rs +++ /dev/null @@ -1,31 +0,0 @@ -// This file is part of Substrate. - -// Copyright (C) Parity Technologies (UK) Ltd. -// SPDX-License-Identifier: Apache-2.0 - -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -pub trait Config: frame_support_test::Config {} - -frame_support::decl_module! { - pub struct Module for enum Call where origin: T::RuntimeOrigin, system=frame_support_test {} -} - -frame_support::decl_storage!{ - trait Store for Module as FinalKeysNone { - pub Value get(fn value) config(): u32; - pub Value2 get(fn value) config(): u32; - } -} - -fn main() {} diff --git a/frame/support/test/tests/decl_storage_ui/get_duplicate.stderr b/frame/support/test/tests/decl_storage_ui/get_duplicate.stderr deleted file mode 100644 index 0039b10fb..000000000 --- a/frame/support/test/tests/decl_storage_ui/get_duplicate.stderr +++ /dev/null @@ -1,5 +0,0 @@ -error: `config()`/`get()` with the same name already defined. - --> $DIR/get_duplicate.rs:27:21 - | -27 | pub Value2 get(fn value) config(): u32; - | ^^^^^ diff --git a/frame/support/test/tests/final_keys.rs b/frame/support/test/tests/final_keys.rs index 9e1d6c087..610f6532c 100644 --- a/frame/support/test/tests/final_keys.rs +++ b/frame/support/test/tests/final_keys.rs @@ -16,65 +16,166 @@ // limitations under the License. use codec::Encode; -use frame_support::{ - storage::unhashed, StorageDoubleMap, StorageMap, StoragePrefixedMap, StorageValue, -}; +use frame_support::{storage::unhashed, StoragePrefixedMap}; +use sp_core::sr25519; use sp_io::{ hashing::{blake2_128, twox_128, twox_64}, TestExternalities, }; +use sp_runtime::{ + generic, + traits::{BlakeTwo256, Verify}, +}; +#[frame_support::pallet] mod no_instance { - pub trait Config: frame_support_test::Config {} - - frame_support::decl_module! { - pub struct Module for enum Call where origin: T::RuntimeOrigin, system=frame_support_test {} + use super::*; + use frame_support::pallet_prelude::*; + use frame_support_test as frame_system; + + #[pallet::pallet] + pub struct Pallet(PhantomData); + + #[pallet::config] + pub trait Config: frame_system::Config {} + + #[pallet::call] + impl Pallet {} + + #[pallet::storage] + pub type Value = StorageValue<_, u32, ValueQuery>; + + #[pallet::storage] + pub type Map = StorageMap<_, Blake2_128Concat, u32, u32, ValueQuery>; + #[pallet::storage] + pub type Map2 = StorageMap<_, Twox64Concat, u32, u32, ValueQuery>; + + #[pallet::storage] + pub type DoubleMap = + StorageDoubleMap<_, Blake2_128Concat, u32, Blake2_128Concat, u32, u32, ValueQuery>; + #[pallet::storage] + pub type DoubleMap2 = + StorageDoubleMap<_, Twox64Concat, u32, Twox64Concat, u32, u32, ValueQuery>; + + #[pallet::storage] + #[pallet::getter(fn test_generic_value)] + pub type TestGenericValue = StorageValue<_, T::BlockNumber, OptionQuery>; + #[pallet::storage] + #[pallet::getter(fn foo2)] + pub type TestGenericDoubleMap = StorageDoubleMap< + _, + Blake2_128Concat, + u32, + Blake2_128Concat, + T::BlockNumber, + u32, + ValueQuery, + >; + + #[pallet::genesis_config] + pub struct GenesisConfig { + pub value: u32, + pub test_generic_value: T::BlockNumber, + pub test_generic_double_map: Vec<(u32, T::BlockNumber, u32)>, } - frame_support::decl_storage! { - trait Store for Module as FinalKeysNone { - pub Value config(value): u32; - - pub Map: map hasher(blake2_128_concat) u32 => u32; - pub Map2: map hasher(twox_64_concat) u32 => u32; - - pub DoubleMap: double_map hasher(blake2_128_concat) u32, hasher(blake2_128_concat) u32 => u32; - pub DoubleMap2: double_map hasher(twox_64_concat) u32, hasher(twox_64_concat) u32 => u32; + impl Default for GenesisConfig { + fn default() -> Self { + Self { + value: Default::default(), + test_generic_value: Default::default(), + test_generic_double_map: Default::default(), + } + } + } - pub TestGenericValue get(fn test_generic_value) config(): Option; - pub TestGenericDoubleMap get(fn foo2) config(test_generic_double_map): - double_map hasher(blake2_128_concat) u32, hasher(blake2_128_concat) T::BlockNumber => Option; + #[pallet::genesis_build] + impl GenesisBuild for GenesisConfig { + fn build(&self) { + >::put(self.value); + >::put(&self.test_generic_value); + for (k1, k2, v) in &self.test_generic_double_map { + >::insert(k1, k2, v); + } } } } +#[frame_support::pallet] mod instance { - pub trait Config: frame_support_test::Config {} - - frame_support::decl_module! { - pub struct Module, I: Instance = DefaultInstance> - for enum Call where origin: T::RuntimeOrigin, system=frame_support_test {} + use super::*; + use frame_support::pallet_prelude::*; + use frame_support_test as frame_system; + + #[pallet::pallet] + pub struct Pallet(PhantomData<(T, I)>); + + #[pallet::config] + pub trait Config: frame_system::Config {} + + #[pallet::call] + impl, I: 'static> Pallet {} + + #[pallet::storage] + pub type Value, I: 'static = ()> = StorageValue<_, u32, ValueQuery>; + + #[pallet::storage] + pub type Map, I: 'static = ()> = + StorageMap<_, Blake2_128Concat, u32, u32, ValueQuery>; + #[pallet::storage] + pub type Map2, I: 'static = ()> = + StorageMap<_, Twox64Concat, u32, u32, ValueQuery>; + + #[pallet::storage] + pub type DoubleMap, I: 'static = ()> = + StorageDoubleMap<_, Blake2_128Concat, u32, Blake2_128Concat, u32, u32, ValueQuery>; + #[pallet::storage] + pub type DoubleMap2, I: 'static = ()> = + StorageDoubleMap<_, Twox64Concat, u32, Twox64Concat, u32, u32, ValueQuery>; + + #[pallet::storage] + #[pallet::getter(fn test_generic_value)] + pub type TestGenericValue, I: 'static = ()> = + StorageValue<_, T::BlockNumber, OptionQuery>; + #[pallet::storage] + #[pallet::getter(fn foo2)] + pub type TestGenericDoubleMap, I: 'static = ()> = StorageDoubleMap< + _, + Blake2_128Concat, + u32, + Blake2_128Concat, + T::BlockNumber, + u32, + ValueQuery, + >; + + #[pallet::genesis_config] + pub struct GenesisConfig, I: 'static = ()> { + pub value: u32, + pub test_generic_value: T::BlockNumber, + pub test_generic_double_map: Vec<(u32, T::BlockNumber, u32)>, + pub phantom: PhantomData, } - frame_support::decl_storage! { - trait Store for Module, I: Instance = DefaultInstance> - as FinalKeysSome - { - pub Value config(value): u32; - - pub Map: map hasher(blake2_128_concat) u32 => u32; - pub Map2: map hasher(twox_64_concat) u32 => u32; - - pub DoubleMap: double_map hasher(blake2_128_concat) u32, hasher(blake2_128_concat) u32 => u32; - pub DoubleMap2: double_map hasher(twox_64_concat) u32, hasher(twox_64_concat) u32 => u32; - - pub TestGenericValue get(fn test_generic_value) config(): Option; - pub TestGenericDoubleMap get(fn foo2) config(test_generic_double_map): - double_map hasher(blake2_128_concat) u32, hasher(blake2_128_concat) T::BlockNumber => Option; + impl, I: 'static> Default for GenesisConfig { + fn default() -> Self { + Self { + value: Default::default(), + test_generic_value: Default::default(), + test_generic_double_map: Default::default(), + phantom: Default::default(), + } } - add_extra_genesis { - // See `decl_storage` limitation. - config(phantom): core::marker::PhantomData; + } + + #[pallet::genesis_build] + impl, I: 'static> GenesisBuild for GenesisConfig { + fn build(&self) { + >::put(self.value); + >::put(&self.test_generic_value); + for (k1, k2, v) in &self.test_generic_double_map { + >::insert(k1, k2, v); + } } } } @@ -91,107 +192,144 @@ fn blake2_128_concat(d: &[u8]) -> Vec { v } +pub type BlockNumber = u32; +pub type Signature = sr25519::Signature; +pub type AccountId = ::Signer; +pub type Header = generic::Header; +pub type UncheckedExtrinsic = generic::UncheckedExtrinsic; +pub type Block = generic::Block; + +frame_support::construct_runtime!( + pub enum Runtime + where + Block = Block, + NodeBlock = Block, + UncheckedExtrinsic = UncheckedExtrinsic, + { + System: frame_support_test, + FinalKeysNone: no_instance, + FinalKeysSome: instance, + Instance2FinalKeysSome: instance::, + } +); + +impl frame_support_test::Config for Runtime { + type BlockNumber = BlockNumber; + type AccountId = AccountId; + type BaseCallFilter = frame_support::traits::Everything; + type RuntimeOrigin = RuntimeOrigin; + type RuntimeCall = RuntimeCall; + type RuntimeEvent = RuntimeEvent; + type PalletInfo = PalletInfo; + type DbWeight = (); +} + +impl no_instance::Config for Runtime {} + +impl instance::Config for Runtime {} +impl instance::Config for Runtime {} + #[test] fn final_keys_no_instance() { TestExternalities::default().execute_with(|| { - no_instance::Value::put(1); + >::put(1); let k = [twox_128(b"FinalKeysNone"), twox_128(b"Value")].concat(); assert_eq!(unhashed::get::(&k), Some(1u32)); - no_instance::Map::insert(1, 2); + >::insert(1, 2); let mut k = [twox_128(b"FinalKeysNone"), twox_128(b"Map")].concat(); k.extend(1u32.using_encoded(blake2_128_concat)); assert_eq!(unhashed::get::(&k), Some(2u32)); - assert_eq!(&k[..32], &::final_prefix()); + assert_eq!(&k[..32], &>::final_prefix()); - no_instance::Map2::insert(1, 2); + >::insert(1, 2); let mut k = [twox_128(b"FinalKeysNone"), twox_128(b"Map2")].concat(); k.extend(1u32.using_encoded(twox_64_concat)); assert_eq!(unhashed::get::(&k), Some(2u32)); - assert_eq!(&k[..32], &::final_prefix()); + assert_eq!(&k[..32], &>::final_prefix()); - no_instance::DoubleMap::insert(&1, &2, &3); + >::insert(&1, &2, &3); let mut k = [twox_128(b"FinalKeysNone"), twox_128(b"DoubleMap")].concat(); k.extend(1u32.using_encoded(blake2_128_concat)); k.extend(2u32.using_encoded(blake2_128_concat)); assert_eq!(unhashed::get::(&k), Some(3u32)); - assert_eq!(&k[..32], &::final_prefix()); + assert_eq!(&k[..32], &>::final_prefix()); - no_instance::DoubleMap2::insert(&1, &2, &3); + >::insert(&1, &2, &3); let mut k = [twox_128(b"FinalKeysNone"), twox_128(b"DoubleMap2")].concat(); k.extend(1u32.using_encoded(twox_64_concat)); k.extend(2u32.using_encoded(twox_64_concat)); assert_eq!(unhashed::get::(&k), Some(3u32)); - assert_eq!(&k[..32], &::final_prefix()); + assert_eq!(&k[..32], &>::final_prefix()); }); } #[test] fn final_keys_default_instance() { TestExternalities::default().execute_with(|| { - >::put(1); + >::put(1); let k = [twox_128(b"FinalKeysSome"), twox_128(b"Value")].concat(); assert_eq!(unhashed::get::(&k), Some(1u32)); - >::insert(1, 2); + >::insert(1, 2); let mut k = [twox_128(b"FinalKeysSome"), twox_128(b"Map")].concat(); k.extend(1u32.using_encoded(blake2_128_concat)); assert_eq!(unhashed::get::(&k), Some(2u32)); - assert_eq!(&k[..32], &>::final_prefix()); + assert_eq!(&k[..32], &>::final_prefix()); - >::insert(1, 2); + >::insert(1, 2); let mut k = [twox_128(b"FinalKeysSome"), twox_128(b"Map2")].concat(); k.extend(1u32.using_encoded(twox_64_concat)); assert_eq!(unhashed::get::(&k), Some(2u32)); - assert_eq!(&k[..32], &>::final_prefix()); + assert_eq!(&k[..32], &>::final_prefix()); - >::insert(&1, &2, &3); + >::insert(&1, &2, &3); let mut k = [twox_128(b"FinalKeysSome"), twox_128(b"DoubleMap")].concat(); k.extend(1u32.using_encoded(blake2_128_concat)); k.extend(2u32.using_encoded(blake2_128_concat)); assert_eq!(unhashed::get::(&k), Some(3u32)); - assert_eq!(&k[..32], &>::final_prefix()); + assert_eq!(&k[..32], &>::final_prefix()); - >::insert(&1, &2, &3); + >::insert(&1, &2, &3); let mut k = [twox_128(b"FinalKeysSome"), twox_128(b"DoubleMap2")].concat(); k.extend(1u32.using_encoded(twox_64_concat)); k.extend(2u32.using_encoded(twox_64_concat)); assert_eq!(unhashed::get::(&k), Some(3u32)); - assert_eq!(&k[..32], &>::final_prefix()); + assert_eq!(&k[..32], &>::final_prefix()); }); } #[test] fn final_keys_instance_2() { TestExternalities::default().execute_with(|| { - >::put(1); + >::put(1); let k = [twox_128(b"Instance2FinalKeysSome"), twox_128(b"Value")].concat(); assert_eq!(unhashed::get::(&k), Some(1u32)); - >::insert(1, 2); + >::insert(1, 2); let mut k = [twox_128(b"Instance2FinalKeysSome"), twox_128(b"Map")].concat(); k.extend(1u32.using_encoded(blake2_128_concat)); assert_eq!(unhashed::get::(&k), Some(2u32)); - assert_eq!(&k[..32], &>::final_prefix()); + assert_eq!(&k[..32], &>::final_prefix()); - >::insert(1, 2); + >::insert(1, 2); let mut k = [twox_128(b"Instance2FinalKeysSome"), twox_128(b"Map2")].concat(); k.extend(1u32.using_encoded(twox_64_concat)); assert_eq!(unhashed::get::(&k), Some(2u32)); - assert_eq!(&k[..32], &>::final_prefix()); + assert_eq!(&k[..32], &>::final_prefix()); - >::insert(&1, &2, &3); + >::insert(&1, &2, &3); let mut k = [twox_128(b"Instance2FinalKeysSome"), twox_128(b"DoubleMap")].concat(); k.extend(1u32.using_encoded(blake2_128_concat)); k.extend(2u32.using_encoded(blake2_128_concat)); assert_eq!(unhashed::get::(&k), Some(3u32)); - assert_eq!(&k[..32], &>::final_prefix()); + assert_eq!(&k[..32], &>::final_prefix()); - >::insert(&1, &2, &3); + >::insert(&1, &2, &3); let mut k = [twox_128(b"Instance2FinalKeysSome"), twox_128(b"DoubleMap2")].concat(); k.extend(1u32.using_encoded(twox_64_concat)); k.extend(2u32.using_encoded(twox_64_concat)); assert_eq!(unhashed::get::(&k), Some(3u32)); - assert_eq!(&k[..32], &>::final_prefix()); + assert_eq!(&k[..32], &>::final_prefix()); }); } diff --git a/frame/support/test/tests/genesisconfig.rs b/frame/support/test/tests/genesisconfig.rs index 869641df8..48904baf1 100644 --- a/frame/support/test/tests/genesisconfig.rs +++ b/frame/support/test/tests/genesisconfig.rs @@ -15,30 +15,86 @@ // See the License for the specific language governing permissions and // limitations under the License. -pub trait Config: frame_support_test::Config {} +use sp_core::sr25519; +use sp_runtime::{ + generic, + traits::{BlakeTwo256, Verify}, +}; -frame_support::decl_module! { - pub struct Module for enum Call where origin: T::RuntimeOrigin, system=frame_support_test {} -} +#[frame_support::pallet] +pub mod pallet { + use super::*; + use frame_support::pallet_prelude::*; + use frame_support_test as frame_system; + + #[pallet::pallet] + pub struct Pallet(PhantomData); + + #[pallet::config] + pub trait Config: frame_system::Config {} -frame_support::decl_storage! { - trait Store for Module as Test { - pub AppendableDM config(t): double_map hasher(identity) u32, hasher(identity) T::BlockNumber => Vec; + #[pallet::call] + impl Pallet {} + + #[pallet::storage] + #[pallet::unbounded] + pub type AppendableDM = + StorageDoubleMap<_, Identity, u32, Identity, T::BlockNumber, Vec>; + + #[pallet::genesis_config] + pub struct GenesisConfig { + pub t: Vec<(u32, T::BlockNumber, Vec)>, + } + + impl Default for GenesisConfig { + fn default() -> Self { + Self { t: Default::default() } + } + } + + #[pallet::genesis_build] + impl GenesisBuild for GenesisConfig { + fn build(&self) { + for (k1, k2, v) in &self.t { + >::insert(k1, k2, v); + } + } } } -struct Test; +pub type BlockNumber = u32; +pub type Signature = sr25519::Signature; +pub type AccountId = ::Signer; +pub type Header = generic::Header; +pub type UncheckedExtrinsic = generic::UncheckedExtrinsic; +pub type Block = generic::Block; + +frame_support::construct_runtime!( + pub enum Test + where + Block = Block, + NodeBlock = Block, + UncheckedExtrinsic = UncheckedExtrinsic, + { + System: frame_support_test, + MyPallet: pallet, + } +); impl frame_support_test::Config for Test { - type BlockNumber = u32; - type RuntimeOrigin = (); - type PalletInfo = frame_support_test::PanicPalletInfo; + type BlockNumber = BlockNumber; + type AccountId = AccountId; + type BaseCallFilter = frame_support::traits::Everything; + type RuntimeOrigin = RuntimeOrigin; + type RuntimeCall = RuntimeCall; + type RuntimeEvent = RuntimeEvent; + type PalletInfo = PalletInfo; type DbWeight = (); } -impl Config for Test {} +impl pallet::Config for Test {} #[test] fn init_genesis_config() { - GenesisConfig::::default(); + pallet::GenesisConfig::::default(); } diff --git a/frame/support/test/tests/instance.rs b/frame/support/test/tests/instance.rs index 9b08e175b..5d17a40f8 100644 --- a/frame/support/test/tests/instance.rs +++ b/frame/support/test/tests/instance.rs @@ -17,111 +17,111 @@ #![recursion_limit = "128"] -use codec::{Codec, Decode, Encode, EncodeLike, MaxEncodedLen}; use frame_support::{ inherent::{InherentData, InherentIdentifier, MakeFatalError, ProvideInherent}, metadata_ir::{ PalletStorageMetadataIR, StorageEntryMetadataIR, StorageEntryModifierIR, StorageEntryTypeIR, StorageHasherIR, }, - traits::{ConstU32, Get}, - Parameter, StorageDoubleMap, StorageMap, StorageValue, + traits::ConstU32, }; -use scale_info::TypeInfo; -use sp_core::{sr25519, H256}; +use sp_core::sr25519; use sp_runtime::{ generic, traits::{BlakeTwo256, Verify}, BuildStorage, }; -mod system; - pub trait Currency {} // Test for: // * No default instance // * Origin, Inherent, Event +#[frame_support::pallet(dev_mode)] mod module1 { + use self::frame_system::pallet_prelude::*; use super::*; - use sp_std::ops::Add; + use frame_support::pallet_prelude::*; + use frame_support_test as frame_system; - pub trait Config: system::Config - where - ::BlockNumber: From, - { - type RuntimeEvent: From> + Into<::RuntimeEvent>; + #[pallet::pallet] + pub struct Pallet(_); + + #[pallet::config] + pub trait Config: frame_system::Config { + type RuntimeEvent: From> + + IsType<::RuntimeEvent>; type RuntimeOrigin: From>; type SomeParameter: Get; - type GenericType: Default + Clone + Codec + EncodeLike + TypeInfo; + type GenericType: Parameter + Member + MaybeSerializeDeserialize + Default + MaxEncodedLen; } - frame_support::decl_module! { - pub struct Module, I: Instance> for enum Call where - origin: ::RuntimeOrigin, - system = system, - T::BlockNumber: From - { - fn offchain_worker() {} + #[pallet::call] + impl, I: 'static> Pallet { + #[pallet::weight(0)] + pub fn one(origin: OriginFor) -> DispatchResult { + ensure_root(origin)?; + Self::deposit_event(Event::AnotherVariant(3)); + Ok(()) + } + } - fn deposit_event() = default; + #[pallet::storage] + #[pallet::getter(fn value)] + pub type Value, I: 'static = ()> = StorageValue<_, T::GenericType, ValueQuery>; - #[weight = 0] - fn one(origin) { - system::ensure_root(origin)?; - Self::deposit_event(RawEvent::AnotherVariant(3)); - } - } + #[pallet::storage] + #[pallet::getter(fn map)] + pub type Map, I: 'static = ()> = StorageMap<_, Identity, u32, u64, ValueQuery>; + + #[pallet::genesis_config] + pub struct GenesisConfig, I: 'static = ()> { + pub value: >::GenericType, + pub test: ::BlockNumber, } - frame_support::decl_storage! { - trait Store for Module, I: Instance> as Module1 where - T::BlockNumber: From + std::fmt::Display - { - pub Value config(value): T::GenericType; - pub Map: map hasher(identity) u32 => u64; + impl, I: 'static> Default for GenesisConfig { + fn default() -> Self { + Self { value: Default::default(), test: Default::default() } } + } - add_extra_genesis { - config(test) : T::BlockNumber; - build(|config: &Self| { - println!("{}", config.test); - }); + #[pallet::genesis_build] + impl, I: 'static> GenesisBuild for GenesisConfig + where + T::BlockNumber: std::fmt::Display, + { + fn build(&self) { + >::put(self.value.clone()); + println!("{}", self.test); } } - frame_support::decl_error! { - pub enum Error for Module, I: Instance> where - T::BlockNumber: From, - T::BlockNumber: Add, - T::AccountId: AsRef<[u8]>, - { - /// Test - Test, - } + #[pallet::error] + pub enum Error { + /// Test + Test, } - frame_support::decl_event! { - pub enum Event where Phantom = std::marker::PhantomData { - _Phantom(Phantom), - AnotherVariant(u32), - } + #[pallet::event] + #[pallet::generate_deposit(pub(super) fn deposit_event)] + pub enum Event, I: 'static = ()> { + _Phantom(PhantomData), + AnotherVariant(u32), } - #[derive( - PartialEq, Eq, Clone, sp_runtime::RuntimeDebug, Encode, Decode, TypeInfo, MaxEncodedLen, - )] - pub enum Origin, I> - where - T::BlockNumber: From, - { + #[pallet::origin] + #[derive(Clone, PartialEq, Eq, RuntimeDebug, Encode, Decode, MaxEncodedLen, TypeInfo)] + #[scale_info(skip_type_params(I))] + pub enum Origin { Members(u32), - _Phantom(std::marker::PhantomData<(T, I)>), + _Phantom(PhantomData<(T, I)>), } pub const INHERENT_IDENTIFIER: InherentIdentifier = *b"12345678"; - impl, I: Instance> ProvideInherent for Module + #[pallet::inherent] + impl, I: 'static> ProvideInherent for Pallet where T::BlockNumber: From, { @@ -133,10 +133,7 @@ mod module1 { unimplemented!(); } - fn check_inherent( - _: &Self::Call, - _: &InherentData, - ) -> std::result::Result<(), Self::Error> { + fn check_inherent(_: &Self::Call, _: &InherentData) -> Result<(), Self::Error> { unimplemented!(); } @@ -149,51 +146,88 @@ mod module1 { // Test for: // * default instance // * use of no_genesis_config_phantom_data +#[frame_support::pallet] mod module2 { use super::*; + use frame_support::pallet_prelude::*; + use frame_support_test as frame_system; - pub trait Config: system::Config { - type Amount: Parameter + Default; - type RuntimeEvent: From> + Into<::RuntimeEvent>; + #[pallet::pallet] + pub struct Pallet(PhantomData<(T, I)>); + + #[pallet::config] + pub trait Config: frame_system::Config { + type Amount: Parameter + MaybeSerializeDeserialize + Default + MaxEncodedLen; + type RuntimeEvent: From> + + IsType<::RuntimeEvent>; type RuntimeOrigin: From>; } - impl, I: Instance> Currency for Module {} + impl, I: 'static> Currency for Pallet {} - frame_support::decl_module! { - pub struct Module, I: Instance=DefaultInstance> for enum Call where - origin: ::RuntimeOrigin, - system = system - { - fn deposit_event() = default; - } + #[pallet::call] + impl, I: 'static> Pallet {} + + #[pallet::storage] + pub type Value, I: 'static = ()> = StorageValue<_, T::Amount, ValueQuery>; + + #[pallet::storage] + pub type Map, I: 'static = ()> = StorageMap<_, Identity, u64, u64, ValueQuery>; + + #[pallet::storage] + pub type DoubleMap, I: 'static = ()> = + StorageDoubleMap<_, Identity, u64, Identity, u64, u64, ValueQuery>; + + #[pallet::genesis_config] + pub struct GenesisConfig, I: 'static = ()> { + pub value: T::Amount, + pub map: Vec<(u64, u64)>, + pub double_map: Vec<(u64, u64, u64)>, } - frame_support::decl_storage! { - trait Store for Module, I: Instance=DefaultInstance> as Module2 { - pub Value config(value): T::Amount; - pub Map config(map): map hasher(identity) u64 => u64; - pub DoubleMap config(double_map): double_map hasher(identity) u64, hasher(identity) u64 => u64; + impl, I: 'static> Default for GenesisConfig { + fn default() -> Self { + Self { + value: Default::default(), + map: Default::default(), + double_map: Default::default(), + } } } - frame_support::decl_event! { - pub enum Event where Amount = >::Amount { - Variant(Amount), + #[pallet::genesis_build] + impl, I: 'static> GenesisBuild for GenesisConfig + where + T::BlockNumber: std::fmt::Display, + { + fn build(&self) { + >::put(self.value.clone()); + for (k, v) in &self.map { + >::insert(k, v); + } + for (k1, k2, v) in &self.double_map { + >::insert(k1, k2, v); + } } } - #[derive( - PartialEq, Eq, Clone, sp_runtime::RuntimeDebug, Encode, Decode, TypeInfo, MaxEncodedLen, - )] - pub enum Origin, I = DefaultInstance> { + #[pallet::event] + pub enum Event, I: 'static = ()> { + Variant(T::Amount), + } + + #[pallet::origin] + #[derive(Clone, PartialEq, Eq, RuntimeDebug, Encode, Decode, MaxEncodedLen, TypeInfo)] + #[scale_info(skip_type_params(I))] + pub enum Origin { Members(u32), - _Phantom(std::marker::PhantomData<(T, I)>), + _Phantom(PhantomData<(T, I)>), } pub const INHERENT_IDENTIFIER: InherentIdentifier = *b"12345678"; - impl, I: Instance> ProvideInherent for Module { + #[pallet::inherent] + impl, I: 'static> ProvideInherent for Pallet { type Call = Call; type Error = MakeFatalError<()>; const INHERENT_IDENTIFIER: InherentIdentifier = INHERENT_IDENTIFIER; @@ -202,10 +236,7 @@ mod module2 { unimplemented!(); } - fn check_inherent( - _call: &Self::Call, - _data: &InherentData, - ) -> std::result::Result<(), Self::Error> { + fn check_inherent(_call: &Self::Call, _data: &InherentData) -> Result<(), Self::Error> { unimplemented!(); } @@ -217,19 +248,70 @@ mod module2 { // Test for: // * Depends on multiple instances of a module with instances +#[frame_support::pallet] mod module3 { use super::*; + use frame_support::pallet_prelude::*; + use frame_support_test as frame_system; + + #[pallet::pallet] + pub struct Pallet(PhantomData<(T, I)>); - pub trait Config: - module2::Config + module2::Config + system::Config + #[pallet::config] + pub trait Config: + frame_system::Config + module2::Config + module2::Config { type Currency: Currency; type Currency2: Currency; } - frame_support::decl_module! { - pub struct Module for enum Call where origin: ::RuntimeOrigin, system=system {} + #[pallet::call] + impl, I: 'static> Pallet {} +} + +pub type BlockNumber = u32; +pub type Signature = sr25519::Signature; +pub type AccountId = ::Signer; +pub type Header = generic::Header; +pub type UncheckedExtrinsic = generic::UncheckedExtrinsic; +pub type Block = generic::Block; + +frame_support::construct_runtime!( + pub enum Runtime where + Block = Block, + NodeBlock = Block, + UncheckedExtrinsic = UncheckedExtrinsic + { + System: frame_support_test::{Pallet, Call, Event}, + Module1_1: module1::::{ + Pallet, Call, Storage, Event, Config, Origin, Inherent + }, + Module1_2: module1::::{ + Pallet, Call, Storage, Event, Config, Origin, Inherent + }, + Module2: module2::{Pallet, Call, Storage, Event, Config, Origin, Inherent}, + Module2_1: module2::::{ + Pallet, Call, Storage, Event, Config, Origin, Inherent + }, + Module2_2: module2::::{ + Pallet, Call, Storage, Event, Config, Origin, Inherent + }, + Module2_3: module2::::{ + Pallet, Call, Storage, Event, Config, Origin, Inherent + }, + Module3: module3::{Pallet, Call}, } +); + +impl frame_support_test::Config for Runtime { + type BlockNumber = BlockNumber; + type AccountId = AccountId; + type BaseCallFilter = frame_support::traits::Everything; + type RuntimeOrigin = RuntimeOrigin; + type RuntimeCall = RuntimeCall; + type RuntimeEvent = RuntimeEvent; + type PalletInfo = PalletInfo; + type DbWeight = (); } impl module1::Config for Runtime { @@ -269,54 +351,6 @@ impl module3::Config for Runtime { type Currency2 = Module2_3; } -pub type Signature = sr25519::Signature; -pub type AccountId = ::Signer; -pub type BlockNumber = u64; -pub type Index = u64; - -impl system::Config for Runtime { - type BaseCallFilter = frame_support::traits::Everything; - type Hash = H256; - type RuntimeOrigin = RuntimeOrigin; - type BlockNumber = BlockNumber; - type AccountId = AccountId; - type RuntimeEvent = RuntimeEvent; - type PalletInfo = PalletInfo; - type RuntimeCall = RuntimeCall; - type DbWeight = (); -} - -frame_support::construct_runtime!( - pub struct Runtime where - Block = Block, - NodeBlock = Block, - UncheckedExtrinsic = UncheckedExtrinsic - { - System: system::{Pallet, Call, Event}, - Module1_1: module1::::{ - Pallet, Call, Storage, Event, Config, Origin, Inherent - }, - Module1_2: module1::::{ - Pallet, Call, Storage, Event, Config, Origin, Inherent - }, - Module2: module2::{Pallet, Call, Storage, Event, Config, Origin, Inherent}, - Module2_1: module2::::{ - Pallet, Call, Storage, Event, Config, Origin, Inherent - }, - Module2_2: module2::::{ - Pallet, Call, Storage, Event, Config, Origin, Inherent - }, - Module2_3: module2::::{ - Pallet, Call, Storage, Event, Config, Origin, Inherent - }, - Module3: module3::{Pallet, Call}, - } -); - -pub type Header = generic::Header; -pub type Block = generic::Block; -pub type UncheckedExtrinsic = generic::UncheckedExtrinsic; - fn new_test_ext() -> sp_io::TestExternalities { GenesisConfig { module_1_1: module1::GenesisConfig { value: 3, test: 2 }, @@ -350,14 +384,14 @@ fn storage_instance_independence() { module2::Value::::put(0); module2::Value::::put(0); module2::Value::::put(0); - module2::Map::::insert(0, 0); - module2::Map::::insert(0, 0); - module2::Map::::insert(0, 0); - module2::Map::::insert(0, 0); - module2::DoubleMap::::insert(&0, &0, &0); - module2::DoubleMap::::insert(&0, &0, &0); - module2::DoubleMap::::insert(&0, &0, &0); - module2::DoubleMap::::insert(&0, &0, &0); + module2::Map::::insert(0, 0); + module2::Map::::insert(0, 0); + module2::Map::::insert(0, 0); + module2::Map::::insert(0, 0); + module2::DoubleMap::::insert(&0, &0, &0); + module2::DoubleMap::::insert(&0, &0, &0); + module2::DoubleMap::::insert(&0, &0, &0); + module2::DoubleMap::::insert(&0, &0, &0); }); // 12 storage values. assert_eq!(storage.top.len(), 12); @@ -367,8 +401,8 @@ fn storage_instance_independence() { fn storage_with_instance_basic_operation() { new_test_ext().execute_with(|| { type Value = module2::Value; - type Map = module2::Map; - type DoubleMap = module2::DoubleMap; + type Map = module2::Map; + type DoubleMap = module2::DoubleMap; assert_eq!(Value::exists(), true); assert_eq!(Value::get(), 4); @@ -412,7 +446,7 @@ fn storage_with_instance_basic_operation() { fn expected_metadata() -> PalletStorageMetadataIR { PalletStorageMetadataIR { - prefix: "Instance2Module2", + prefix: "Module2_2", entries: vec![ StorageEntryMetadataIR { name: "Value", diff --git a/frame/support/test/tests/issue2219.rs b/frame/support/test/tests/issue2219.rs index ce16c402d..ff3e0bd95 100644 --- a/frame/support/test/tests/issue2219.rs +++ b/frame/support/test/tests/issue2219.rs @@ -15,32 +15,28 @@ // See the License for the specific language governing permissions and // limitations under the License. -use frame_support::{ - codec::{Decode, Encode}, - scale_info::TypeInfo, - sp_runtime::{ - generic, - traits::{BlakeTwo256, Verify}, - }, +use sp_core::{sr25519, ConstU64}; +use sp_runtime::{ + generic, + traits::{BlakeTwo256, Verify}, }; -use serde::{Deserialize, Serialize}; -use sp_core::{sr25519, H256}; - -mod system; +#[frame_support::pallet] mod module { use super::*; + use frame_support::pallet_prelude::*; + use frame_support_test as frame_system; pub type Request = - (::AccountId, Role, ::BlockNumber); + (::AccountId, Role, ::BlockNumber); pub type Requests = Vec>; - #[derive(Encode, Decode, Copy, Clone, Eq, PartialEq, Debug, TypeInfo)] + #[derive(Copy, Clone, Eq, PartialEq, Debug, Encode, Decode, MaxEncodedLen, TypeInfo)] pub enum Role { Storage, } - #[derive(Encode, Decode, Copy, Clone, Eq, PartialEq, Debug, TypeInfo)] + #[derive(Copy, Clone, Eq, PartialEq, Debug, Encode, Decode, MaxEncodedLen, TypeInfo)] pub struct RoleParameters { // minimum actors to maintain - if role is unstaking // and remaining actors would be less that this value - prevent or punish for unstaking @@ -83,102 +79,108 @@ mod module { } } - pub trait Config: system::Config + TypeInfo {} - - frame_support::decl_module! { - pub struct Module for enum Call where origin: T::RuntimeOrigin, system=system {} - } - - #[derive(Encode, Decode, Copy, Clone, Serialize, Deserialize)] - pub struct Data { - pub data: T::BlockNumber, - } - - impl Default for Data { - fn default() -> Self { - Self { data: T::BlockNumber::default() } - } + #[pallet::pallet] + pub struct Pallet(PhantomData); + + #[pallet::config] + pub trait Config: frame_system::Config + TypeInfo {} + + #[pallet::call] + impl Pallet {} + + /// requirements to enter and maintain status in roles + #[pallet::storage] + #[pallet::getter(fn parameters)] + pub type Parameters = + StorageMap<_, Blake2_128Concat, Role, RoleParameters, OptionQuery>; + + /// the roles members can enter into + #[pallet::storage] + #[pallet::getter(fn available_roles)] + #[pallet::unbounded] + pub type AvailableRoles = StorageValue<_, Vec, ValueQuery>; + + /// Actors list + #[pallet::storage] + #[pallet::getter(fn actor_account_ids)] + #[pallet::unbounded] + pub type ActorAccountIds = StorageValue<_, Vec>; + + /// actor accounts associated with a role + #[pallet::storage] + #[pallet::getter(fn account_ids_by_role)] + #[pallet::unbounded] + pub type AccountIdsByRole = StorageMap<_, Blake2_128Concat, Role, Vec>; + + /// tokens locked until given block number + #[pallet::storage] + #[pallet::getter(fn bondage)] + pub type Bondage = StorageMap<_, Blake2_128Concat, T::AccountId, T::BlockNumber>; + + /// First step before enter a role is registering intent with a new account/key. + /// This is done by sending a role_entry_request() from the new account. + /// The member must then send a stake() transaction to approve the request and enter the desired + /// role. The account making the request will be bonded and must have + /// sufficient balance to cover the minimum stake for the role. + /// Bonding only occurs after successful entry into a role. + #[pallet::storage] + #[pallet::getter(fn role_entry_requests)] + #[pallet::unbounded] + pub type RoleEntryRequests = StorageValue<_, Requests>; + + /// Entry request expires after this number of blocks + #[pallet::storage] + #[pallet::getter(fn request_life_time)] + pub type RequestLifeTime = StorageValue<_, u64, ValueQuery, ConstU64<0>>; + + #[pallet::genesis_config] + #[derive(Default)] + pub struct GenesisConfig { + pub enable_storage_role: bool, + pub request_life_time: u64, } - frame_support::decl_storage! { - trait Store for Module as Actors { - /// requirements to enter and maintain status in roles - pub Parameters get(fn parameters) build(|config: &GenesisConfig| { - if config.enable_storage_role { - let storage_params: RoleParameters = Default::default(); - vec![(Role::Storage, storage_params)] - } else { - vec![] - } - }): map hasher(blake2_128_concat) Role => Option>; - - /// the roles members can enter into - pub AvailableRoles get(fn available_roles) build(|config: &GenesisConfig| { - if config.enable_storage_role { - vec![(Role::Storage)] - } else { - vec![] - } - }): Vec; - - /// Actors list - pub ActorAccountIds get(fn actor_account_ids) : Vec; - - /// actor accounts associated with a role - pub AccountIdsByRole get(fn account_ids_by_role): - map hasher(blake2_128_concat) Role => Vec; - - /// tokens locked until given block number - pub Bondage get(fn bondage): - map hasher(blake2_128_concat) T::AccountId => T::BlockNumber; - - /// First step before enter a role is registering intent with a new account/key. - /// This is done by sending a role_entry_request() from the new account. - /// The member must then send a stake() transaction to approve the request and enter the desired role. - /// The account making the request will be bonded and must have - /// sufficient balance to cover the minimum stake for the role. - /// Bonding only occurs after successful entry into a role. - pub RoleEntryRequests get(fn role_entry_requests) : Requests; - - /// Entry request expires after this number of blocks - pub RequestLifeTime get(fn request_life_time) config(request_life_time) : u64 = 0; - } - add_extra_genesis { - config(enable_storage_role): bool; + #[pallet::genesis_build] + impl GenesisBuild for GenesisConfig { + fn build(&self) { + if self.enable_storage_role { + >::insert(Role::Storage, >::default()); + >::put(vec![Role::Storage]); + } + >::put(self.request_life_time); } } } +pub type BlockNumber = u64; pub type Signature = sr25519::Signature; pub type AccountId = ::Signer; -pub type BlockNumber = u64; -pub type Index = u64; pub type Header = generic::Header; -pub type Block = generic::Block; pub type UncheckedExtrinsic = generic::UncheckedExtrinsic; +pub type Block = generic::Block; -impl system::Config for Runtime { - type BaseCallFilter = frame_support::traits::Everything; - type Hash = H256; - type RuntimeOrigin = RuntimeOrigin; +impl frame_support_test::Config for Runtime { type BlockNumber = BlockNumber; type AccountId = AccountId; + type BaseCallFilter = frame_support::traits::Everything; + type RuntimeOrigin = RuntimeOrigin; + type RuntimeCall = RuntimeCall; type RuntimeEvent = RuntimeEvent; type PalletInfo = PalletInfo; - type RuntimeCall = RuntimeCall; type DbWeight = (); } impl module::Config for Runtime {} frame_support::construct_runtime!( - pub struct Runtime where + pub struct Runtime + where Block = Block, NodeBlock = Block, - UncheckedExtrinsic = UncheckedExtrinsic + UncheckedExtrinsic = UncheckedExtrinsic, { - System: system::{Pallet, Call, Event}, - Module: module::{Pallet, Call, Storage, Config}, + System: frame_support_test, + Module: module, } ); diff --git a/frame/support/test/tests/origin.rs b/frame/support/test/tests/origin.rs index 996b996c8..47451157b 100644 --- a/frame/support/test/tests/origin.rs +++ b/frame/support/test/tests/origin.rs @@ -19,120 +19,124 @@ #![recursion_limit = "128"] -use codec::MaxEncodedLen; use frame_support::traits::{Contains, OriginTrait}; -use scale_info::TypeInfo; -use sp_core::{sr25519, H256}; use sp_runtime::{generic, traits::BlakeTwo256}; -mod system; - mod nested { - use super::*; - + #[frame_support::pallet(dev_mode)] pub mod module { + use self::frame_system::pallet_prelude::*; + use frame_support::pallet_prelude::*; + use frame_support_test as frame_system; - use super::*; + #[pallet::pallet] + pub struct Pallet(_); - pub trait Config: system::Config {} + #[pallet::config] + pub trait Config: frame_system::Config { + type RuntimeEvent: From> + + IsType<::RuntimeEvent>; + } - frame_support::decl_module! { - pub struct Module for enum Call - where origin: ::RuntimeOrigin, system=system - { - #[weight = 0] - pub fn fail(_origin) -> frame_support::dispatch::DispatchResult { - Err(Error::::Something.into()) - } + #[pallet::call] + impl Pallet { + pub fn fail(_origin: OriginFor) -> DispatchResult { + Err(Error::::Something.into()) } } - #[derive( - Clone, PartialEq, Eq, Debug, codec::Encode, codec::Decode, TypeInfo, MaxEncodedLen, - )] + #[pallet::origin] + #[derive(Clone, PartialEq, Eq, RuntimeDebug, Encode, Decode, MaxEncodedLen, TypeInfo)] pub struct Origin; - frame_support::decl_event! { - pub enum Event { - A, - } + #[pallet::event] + pub enum Event { + A, } - frame_support::decl_error! { - pub enum Error for Module { - Something - } + #[pallet::error] + pub enum Error { + Something, } - frame_support::decl_storage! { - trait Store for Module as Module {} - add_extra_genesis { - build(|_config| {}) - } + #[pallet::genesis_config] + #[derive(Default)] + pub struct GenesisConfig {} + + #[pallet::genesis_build] + impl GenesisBuild for GenesisConfig { + fn build(&self) {} } } } +#[frame_support::pallet(dev_mode)] pub mod module { - use super::*; + use self::frame_system::pallet_prelude::*; + use frame_support::pallet_prelude::*; + use frame_support_test as frame_system; - pub trait Config: system::Config {} + #[pallet::pallet] + pub struct Pallet(_); - frame_support::decl_module! { - pub struct Module for enum Call - where origin: ::RuntimeOrigin, system=system - { - #[weight = 0] - pub fn fail(_origin) -> frame_support::dispatch::DispatchResult { - Err(Error::::Something.into()) - } - #[weight = 0] - pub fn aux_1(_origin, #[compact] _data: u32) -> frame_support::dispatch::DispatchResult { - unreachable!() - } - #[weight = 0] - pub fn aux_2(_origin, _data: i32, #[compact] _data2: u32) -> frame_support::dispatch::DispatchResult { - unreachable!() - } - #[weight = 0] - fn aux_3(_origin, _data: i32, _data2: String) -> frame_support::dispatch::DispatchResult { - unreachable!() - } - #[weight = 3] - fn aux_4(_origin) -> frame_support::dispatch::DispatchResult { unreachable!() } - #[weight = (5, frame_support::dispatch::DispatchClass::Operational)] - fn operational(_origin) { unreachable!() } + #[pallet::config] + pub trait Config: frame_system::Config { + type RuntimeEvent: From> + IsType<::RuntimeEvent>; + } + + #[pallet::call] + impl Pallet { + pub fn fail(_origin: OriginFor) -> DispatchResult { + Err(Error::::Something.into()) + } + pub fn aux_1(_origin: OriginFor, #[pallet::compact] _data: u32) -> DispatchResult { + unreachable!() + } + pub fn aux_2( + _origin: OriginFor, + _data: i32, + #[pallet::compact] _data2: u32, + ) -> DispatchResult { + unreachable!() + } + #[pallet::weight(0)] + pub fn aux_3(_origin: OriginFor, _data: i32, _data2: String) -> DispatchResult { + unreachable!() + } + #[pallet::weight(3)] + pub fn aux_4(_origin: OriginFor) -> DispatchResult { + unreachable!() + } + #[pallet::weight((5, DispatchClass::Operational))] + pub fn operational(_origin: OriginFor) -> DispatchResult { + unreachable!() } } - #[derive( - Clone, PartialEq, Eq, Debug, codec::Encode, codec::Decode, TypeInfo, MaxEncodedLen, - )] - pub struct Origin(pub core::marker::PhantomData); + #[pallet::origin] + #[derive(Clone, PartialEq, Eq, RuntimeDebug, Encode, Decode, MaxEncodedLen, TypeInfo)] + pub struct Origin(pub PhantomData); - frame_support::decl_event! { - pub enum Event { - A, - } + #[pallet::event] + pub enum Event { + A, } - frame_support::decl_error! { - pub enum Error for Module { - Something - } + #[pallet::error] + pub enum Error { + Something, } - frame_support::decl_storage! { - trait Store for Module as Module {} - add_extra_genesis { - build(|_config| {}) - } + #[pallet::genesis_config] + #[derive(Default)] + pub struct GenesisConfig {} + + #[pallet::genesis_build] + impl GenesisBuild for GenesisConfig { + fn build(&self) {} } } -impl nested::module::Config for RuntimeOriginTest {} -impl module::Config for RuntimeOriginTest {} - pub struct BaseCallFilter; impl Contains for BaseCallFilter { fn contains(c: &RuntimeCall) -> bool { @@ -143,17 +147,11 @@ impl Contains for BaseCallFilter { } } -impl system::Config for RuntimeOriginTest { - type BaseCallFilter = BaseCallFilter; - type Hash = H256; - type RuntimeOrigin = RuntimeOrigin; - type BlockNumber = BlockNumber; - type AccountId = u32; - type RuntimeEvent = RuntimeEvent; - type PalletInfo = PalletInfo; - type RuntimeCall = RuntimeCall; - type DbWeight = (); -} +pub type BlockNumber = u32; +pub type AccountId = u32; +pub type Header = generic::Header; +pub type UncheckedExtrinsic = generic::UncheckedExtrinsic; +pub type Block = generic::Block; frame_support::construct_runtime!( pub enum RuntimeOriginTest where @@ -161,17 +159,29 @@ frame_support::construct_runtime!( NodeBlock = Block, UncheckedExtrinsic = UncheckedExtrinsic { - System: system::{Pallet, Event, Origin}, - NestedModule: nested::module::{Pallet, Origin, Call}, - Module: module::{Pallet, Origin, Call}, + System: frame_support_test, + NestedModule: nested::module, + Module: module, } ); -pub type Signature = sr25519::Signature; -pub type BlockNumber = u64; -pub type Header = generic::Header; -pub type UncheckedExtrinsic = generic::UncheckedExtrinsic; -pub type Block = generic::Block; +impl frame_support_test::Config for RuntimeOriginTest { + type BlockNumber = BlockNumber; + type AccountId = AccountId; + type BaseCallFilter = BaseCallFilter; + type RuntimeOrigin = RuntimeOrigin; + type RuntimeCall = RuntimeCall; + type RuntimeEvent = RuntimeEvent; + type PalletInfo = PalletInfo; + type DbWeight = (); +} + +impl nested::module::Config for RuntimeOriginTest { + type RuntimeEvent = RuntimeEvent; +} +impl module::Config for RuntimeOriginTest { + type RuntimeEvent = RuntimeEvent; +} #[test] fn origin_default_filter() { @@ -199,7 +209,7 @@ fn origin_default_filter() { // Now test for root origin and filters: let mut origin = RuntimeOrigin::from(Some(0)); origin.set_caller_from(RuntimeOrigin::root()); - assert!(matches!(origin.caller, OriginCaller::system(system::RawOrigin::Root))); + assert!(matches!(origin.caller, OriginCaller::system(frame_support_test::RawOrigin::Root))); // Root origin bypass all filter. assert_eq!(origin.filter_call(&accepted_call), true); diff --git a/frame/support/test/tests/pallet_compatibility.rs b/frame/support/test/tests/pallet_compatibility.rs deleted file mode 100644 index 610316ecf..000000000 --- a/frame/support/test/tests/pallet_compatibility.rs +++ /dev/null @@ -1,371 +0,0 @@ -// This file is part of Substrate. - -// Copyright (C) Parity Technologies (UK) Ltd. -// SPDX-License-Identifier: Apache-2.0 - -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -// Old macros don't support the flag `no-metadata-docs` so the result differs when the feature is -// activated. -#![cfg(not(feature = "no-metadata-docs"))] - -use frame_support::traits::{ConstU32, ConstU64}; - -pub trait SomeAssociation { - type A: frame_support::dispatch::Parameter + Default; -} -impl SomeAssociation for u64 { - type A = u64; -} - -mod pallet_old { - use super::SomeAssociation; - use frame_support::{ - decl_error, decl_event, decl_module, decl_storage, traits::Get, weights::Weight, Parameter, - }; - use frame_system::ensure_root; - - pub trait Config: frame_system::Config { - type SomeConst: Get; - type Balance: Parameter - + codec::HasCompact - + From - + Into - + Default - + SomeAssociation; - type RuntimeEvent: From> + Into<::RuntimeEvent>; - } - - decl_storage! { - trait Store for Module as Example { - /// Some documentation - Dummy get(fn dummy) config(): Option; - Bar get(fn bar) config(): map hasher(blake2_128_concat) T::AccountId => T::Balance; - Foo get(fn foo) config(): T::Balance = 3.into(); - Double get(fn double): double_map - hasher(blake2_128_concat) u32, - hasher(twox_64_concat) u64 - => ::A; - } - } - - decl_event!( - pub enum Event - where - Balance = ::Balance, - { - /// Dummy event, just here so there's a generic type that's used. - Dummy(Balance), - } - ); - - decl_module! { - pub struct Module for enum Call where origin: T::RuntimeOrigin { - type Error = Error; - fn deposit_event() = default; - const SomeConst: T::Balance = T::SomeConst::get(); - - #[weight = >::into(new_value.clone())] - fn set_dummy(origin, #[compact] new_value: T::Balance) { - ensure_root(origin)?; - - >::put(&new_value); - Self::deposit_event(RawEvent::Dummy(new_value)); - } - - fn on_initialize(_n: T::BlockNumber) -> Weight { - >::put(T::Balance::from(10)); - Weight::from_parts(10, 0) - } - - fn on_finalize(_n: T::BlockNumber) { - >::put(T::Balance::from(11)); - } - } - } - - decl_error! { - pub enum Error for Module { - /// Some wrong behavior - Wrong, - } - } -} - -#[frame_support::pallet] -pub mod pallet { - use super::SomeAssociation; - use frame_support::{pallet_prelude::*, scale_info}; - use frame_system::{ensure_root, pallet_prelude::*}; - - #[pallet::config] - pub trait Config: frame_system::Config { - type Balance: Parameter - + codec::HasCompact - + From - + Into - + Default - + MaybeSerializeDeserialize - + SomeAssociation - + scale_info::StaticTypeInfo; - #[pallet::constant] - type SomeConst: Get; - type RuntimeEvent: From> + IsType<::RuntimeEvent>; - } - - #[pallet::pallet] - #[pallet::without_storage_info] - pub struct Pallet(_); - - #[pallet::hooks] - impl Hooks for Pallet { - fn on_initialize(_n: T::BlockNumber) -> Weight { - >::put(T::Balance::from(10)); - Weight::from_parts(10, 0) - } - - fn on_finalize(_n: T::BlockNumber) { - >::put(T::Balance::from(11)); - } - } - - #[pallet::call] - impl Pallet { - #[pallet::call_index(0)] - #[pallet::weight(>::into(new_value.clone()))] - pub fn set_dummy( - origin: OriginFor, - #[pallet::compact] new_value: T::Balance, - ) -> DispatchResultWithPostInfo { - ensure_root(origin)?; - - >::put(&new_value); - Self::deposit_event(Event::Dummy(new_value)); - - Ok(().into()) - } - } - - #[pallet::error] - pub enum Error { - /// Some wrong behavior - Wrong, - } - - #[pallet::event] - #[pallet::generate_deposit(fn deposit_event)] - pub enum Event { - /// Dummy event, just here so there's a generic type that's used. - Dummy(T::Balance), - } - - #[pallet::storage] - /// Some documentation - type Dummy = StorageValue<_, T::Balance, OptionQuery>; - - #[pallet::storage] - type Bar = StorageMap<_, Blake2_128Concat, T::AccountId, T::Balance, ValueQuery>; - - #[pallet::type_value] - pub fn OnFooEmpty() -> T::Balance { - 3.into() - } - #[pallet::storage] - type Foo = StorageValue<_, T::Balance, ValueQuery, OnFooEmpty>; - - #[pallet::storage] - type Double = StorageDoubleMap< - _, - Blake2_128Concat, - u32, - Twox64Concat, - u64, - ::A, - ValueQuery, - >; - - #[pallet::genesis_config] - pub struct GenesisConfig { - dummy: Option, - bar: Vec<(T::AccountId, T::Balance)>, - foo: T::Balance, - } - - impl Default for GenesisConfig { - fn default() -> Self { - GenesisConfig { - dummy: Default::default(), - bar: Default::default(), - foo: OnFooEmpty::::get(), - } - } - } - - #[pallet::genesis_build] - impl GenesisBuild for GenesisConfig { - fn build(&self) { - if let Some(dummy) = self.dummy.as_ref() { - >::put(dummy); - } - for (k, v) in &self.bar { - >::insert(k, v); - } - >::put(&self.foo); - } - } -} - -impl frame_system::Config for Runtime { - type BaseCallFilter = frame_support::traits::Everything; - type RuntimeOrigin = RuntimeOrigin; - type Index = u64; - type BlockNumber = u32; - type RuntimeCall = RuntimeCall; - type Hash = sp_runtime::testing::H256; - type Hashing = sp_runtime::traits::BlakeTwo256; - type AccountId = u64; - type Lookup = sp_runtime::traits::IdentityLookup; - type Header = Header; - type RuntimeEvent = RuntimeEvent; - type BlockHashCount = ConstU32<250>; - type BlockWeights = (); - type BlockLength = (); - type DbWeight = (); - type Version = (); - type PalletInfo = PalletInfo; - type AccountData = (); - type OnNewAccount = (); - type OnKilledAccount = (); - type SystemWeightInfo = (); - type SS58Prefix = (); - type OnSetCode = (); - type MaxConsumers = ConstU32<16>; -} -impl pallet::Config for Runtime { - type RuntimeEvent = RuntimeEvent; - type SomeConst = ConstU64<10>; - type Balance = u64; -} -impl pallet_old::Config for Runtime { - type RuntimeEvent = RuntimeEvent; - type SomeConst = ConstU64<10>; - type Balance = u64; -} - -pub type Header = sp_runtime::generic::Header; -pub type Block = sp_runtime::generic::Block; -pub type UncheckedExtrinsic = sp_runtime::generic::UncheckedExtrinsic; - -frame_support::construct_runtime!( - pub struct Runtime where - Block = Block, - NodeBlock = Block, - UncheckedExtrinsic = UncheckedExtrinsic - { - System: frame_system::{Pallet, Call, Event}, - // NOTE: name Example here is needed in order to have same module prefix - Example: pallet::{Pallet, Call, Event, Config, Storage}, - PalletOld: pallet_old::{Pallet, Call, Event, Config, Storage}, - } -); - -#[cfg(test)] -mod test { - use super::{pallet, pallet_old, Runtime}; - use codec::{Decode, Encode}; - use scale_info::{form::PortableForm, Variant}; - - #[test] - fn metadata() { - let metadata = Runtime::metadata(); - let (pallets, types) = match metadata.1 { - frame_support::metadata::RuntimeMetadata::V14(metadata) => - (metadata.pallets, metadata.types), - _ => unreachable!(), - }; - - let assert_meta_types = |ty_id1, ty_id2| { - let ty1 = types.resolve(ty_id1).map(|ty| ty.type_def.clone()); - let ty2 = types.resolve(ty_id2).map(|ty| ty.type_def.clone()); - pretty_assertions::assert_eq!(ty1, ty2); - }; - - let get_enum_variants = |ty_id| match types.resolve(ty_id).map(|ty| ty.type_def.clone()) { - Some(ty) => match ty { - scale_info::TypeDef::Variant(var) => var.variants, - _ => panic!("Expected variant type"), - }, - _ => panic!("No type found"), - }; - - let assert_enum_variants = |vs1: &[Variant], - vs2: &[Variant]| { - assert_eq!(vs1.len(), vs2.len()); - for i in 0..vs1.len() { - let v1 = &vs2[i]; - let v2 = &vs2[i]; - assert_eq!(v1.fields.len(), v2.fields.len()); - for f in 0..v1.fields.len() { - let f1 = &v1.fields[f]; - let f2 = &v2.fields[f]; - pretty_assertions::assert_eq!(f1.name, f2.name); - pretty_assertions::assert_eq!(f1.ty, f2.ty); - } - } - }; - - pretty_assertions::assert_eq!(pallets[1].storage, pallets[2].storage); - - let calls1 = pallets[1].calls.as_ref().unwrap(); - let calls2 = pallets[2].calls.as_ref().unwrap(); - assert_meta_types(calls1.ty.id, calls2.ty.id); - - // event: check variants and fields but ignore the type name which will be different - let event1_variants = get_enum_variants(pallets[1].event.as_ref().unwrap().ty.id); - let event2_variants = get_enum_variants(pallets[2].event.as_ref().unwrap().ty.id); - assert_enum_variants(&event1_variants, &event2_variants); - - let err1 = get_enum_variants(pallets[1].error.as_ref().unwrap().ty.id) - .iter() - .filter(|v| v.name == "__Ignore") - .cloned() - .collect::>(); - let err2 = get_enum_variants(pallets[2].error.as_ref().unwrap().ty.id) - .iter() - .filter(|v| v.name == "__Ignore") - .cloned() - .collect::>(); - assert_enum_variants(&err1, &err2); - - pretty_assertions::assert_eq!(pallets[1].constants, pallets[2].constants); - } - - #[test] - fn types() { - assert_eq!( - pallet_old::Event::::decode( - &mut &pallet::Event::::Dummy(10).encode()[..] - ) - .unwrap(), - pallet_old::Event::::Dummy(10), - ); - - assert_eq!( - pallet_old::Call::::decode( - &mut &pallet::Call::::set_dummy { new_value: 10 }.encode()[..] - ) - .unwrap(), - pallet_old::Call::::set_dummy { new_value: 10 }, - ); - } -} diff --git a/frame/support/test/tests/pallet_compatibility_instance.rs b/frame/support/test/tests/pallet_compatibility_instance.rs deleted file mode 100644 index 73014d996..000000000 --- a/frame/support/test/tests/pallet_compatibility_instance.rs +++ /dev/null @@ -1,371 +0,0 @@ -// This file is part of Substrate. - -// Copyright (C) Parity Technologies (UK) Ltd. -// SPDX-License-Identifier: Apache-2.0 - -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -// Old macros don't support the flag `no-metadata-docs` so the result differs when the feature is -// activated. -#![cfg(not(feature = "no-metadata-docs"))] - -use frame_support::traits::{ConstU32, ConstU64}; - -mod pallet_old { - use frame_support::{ - decl_error, decl_event, decl_module, decl_storage, traits::Get, weights::Weight, Parameter, - }; - use frame_system::ensure_root; - - pub trait Config: frame_system::Config { - type SomeConst: Get; - type Balance: Parameter + codec::HasCompact + From + Into + Default; - type RuntimeEvent: From> + Into<::RuntimeEvent>; - } - - decl_storage! { - trait Store for Module, I: Instance = DefaultInstance> as Example { - /// Some documentation - Dummy get(fn dummy) config(): Option; - Bar get(fn bar) config(): map hasher(blake2_128_concat) T::AccountId => T::Balance; - Foo get(fn foo) config(): T::Balance = 3.into(); - Double get(fn double): - double_map hasher(blake2_128_concat) u32, hasher(twox_64_concat) u64 => u16; - } - } - - decl_event!( - pub enum Event - where - Balance = >::Balance, - { - /// Dummy event, just here so there's a generic type that's used. - Dummy(Balance), - } - ); - - decl_module! { - pub struct Module, I: Instance = DefaultInstance> for enum Call - where origin: T::RuntimeOrigin - { - type Error = Error; - fn deposit_event() = default; - const SomeConst: T::Balance = T::SomeConst::get(); - - #[weight = >::into(new_value.clone())] - fn set_dummy(origin, #[compact] new_value: T::Balance) { - ensure_root(origin)?; - - >::put(&new_value); - Self::deposit_event(RawEvent::Dummy(new_value)); - } - - fn on_initialize(_n: T::BlockNumber) -> Weight { - >::put(T::Balance::from(10)); - Weight::from_parts(10, 0) - } - - fn on_finalize(_n: T::BlockNumber) { - >::put(T::Balance::from(11)); - } - } - } - - decl_error! { - pub enum Error for Module, I: Instance> { - /// Some wrong behavior - Wrong, - } - } -} - -#[frame_support::pallet] -pub mod pallet { - use frame_support::{pallet_prelude::*, scale_info}; - use frame_system::{ensure_root, pallet_prelude::*}; - - #[pallet::config] - pub trait Config: frame_system::Config { - type Balance: Parameter - + codec::HasCompact - + From - + Into - + Default - + MaybeSerializeDeserialize - + scale_info::StaticTypeInfo; - #[pallet::constant] - type SomeConst: Get; - type RuntimeEvent: From> - + IsType<::RuntimeEvent>; - } - - #[pallet::pallet] - #[pallet::without_storage_info] - pub struct Pallet(PhantomData<(T, I)>); - - #[pallet::hooks] - impl, I: 'static> Hooks for Pallet { - fn on_initialize(_n: T::BlockNumber) -> Weight { - >::put(T::Balance::from(10)); - Weight::from_parts(10, 0) - } - - fn on_finalize(_n: T::BlockNumber) { - >::put(T::Balance::from(11)); - } - } - - #[pallet::call] - impl, I: 'static> Pallet { - #[pallet::call_index(0)] - #[pallet::weight(>::into(new_value.clone()))] - pub fn set_dummy( - origin: OriginFor, - #[pallet::compact] new_value: T::Balance, - ) -> DispatchResultWithPostInfo { - ensure_root(origin)?; - - >::put(&new_value); - Self::deposit_event(Event::Dummy(new_value)); - - Ok(().into()) - } - } - - #[pallet::error] - pub enum Error { - /// Some wrong behavior - Wrong, - } - - #[pallet::event] - #[pallet::generate_deposit(fn deposit_event)] - pub enum Event, I: 'static = ()> { - /// Dummy event, just here so there's a generic type that's used. - Dummy(T::Balance), - } - - #[pallet::storage] - /// Some documentation - type Dummy, I: 'static = ()> = StorageValue<_, T::Balance, OptionQuery>; - - #[pallet::storage] - type Bar, I: 'static = ()> = - StorageMap<_, Blake2_128Concat, T::AccountId, T::Balance, ValueQuery>; - - #[pallet::storage] - type Foo, I: 'static = ()> = - StorageValue<_, T::Balance, ValueQuery, OnFooEmpty>; - #[pallet::type_value] - pub fn OnFooEmpty, I: 'static>() -> T::Balance { - 3.into() - } - - #[pallet::storage] - type Double = - StorageDoubleMap<_, Blake2_128Concat, u32, Twox64Concat, u64, u16, ValueQuery>; - - #[pallet::genesis_config] - pub struct GenesisConfig, I: 'static = ()> { - dummy: Option, - bar: Vec<(T::AccountId, T::Balance)>, - foo: T::Balance, - } - - impl, I: 'static> Default for GenesisConfig { - fn default() -> Self { - GenesisConfig { - dummy: Default::default(), - bar: Default::default(), - foo: OnFooEmpty::::get(), - } - } - } - - #[pallet::genesis_build] - impl, I: 'static> GenesisBuild for GenesisConfig { - fn build(&self) { - if let Some(dummy) = self.dummy.as_ref() { - >::put(dummy); - } - for (k, v) in &self.bar { - >::insert(k, v); - } - >::put(&self.foo); - } - } -} - -impl frame_system::Config for Runtime { - type BlockWeights = (); - type BlockLength = (); - type DbWeight = (); - type BaseCallFilter = frame_support::traits::Everything; - type RuntimeOrigin = RuntimeOrigin; - type Index = u64; - type BlockNumber = u32; - type RuntimeCall = RuntimeCall; - type Hash = sp_runtime::testing::H256; - type Hashing = sp_runtime::traits::BlakeTwo256; - type AccountId = u64; - type Lookup = sp_runtime::traits::IdentityLookup; - type Header = Header; - type RuntimeEvent = RuntimeEvent; - type BlockHashCount = ConstU32<250>; - type Version = (); - type PalletInfo = PalletInfo; - type AccountData = (); - type OnNewAccount = (); - type OnKilledAccount = (); - type SystemWeightInfo = (); - type SS58Prefix = (); - type OnSetCode = (); - type MaxConsumers = ConstU32<16>; -} -impl pallet::Config for Runtime { - type RuntimeEvent = RuntimeEvent; - type SomeConst = ConstU64<10>; - type Balance = u64; -} -impl pallet::Config for Runtime { - type RuntimeEvent = RuntimeEvent; - type SomeConst = ConstU64<10>; - type Balance = u64; -} -impl pallet::Config for Runtime { - type RuntimeEvent = RuntimeEvent; - type SomeConst = ConstU64<10>; - type Balance = u64; -} -impl pallet_old::Config for Runtime { - type RuntimeEvent = RuntimeEvent; - type SomeConst = ConstU64<10>; - type Balance = u64; -} -impl pallet_old::Config for Runtime { - type RuntimeEvent = RuntimeEvent; - type SomeConst = ConstU64<10>; - type Balance = u64; -} -impl pallet_old::Config for Runtime { - type RuntimeEvent = RuntimeEvent; - type SomeConst = ConstU64<10>; - type Balance = u64; -} - -pub type Header = sp_runtime::generic::Header; -pub type Block = sp_runtime::generic::Block; -pub type UncheckedExtrinsic = sp_runtime::generic::UncheckedExtrinsic; - -frame_support::construct_runtime!( - pub struct Runtime where - Block = Block, - NodeBlock = Block, - UncheckedExtrinsic = UncheckedExtrinsic - { - System: frame_system::{Pallet, Call, Event}, - Example: pallet::{Pallet, Call, Event, Config, Storage}, - PalletOld: pallet_old::{Pallet, Call, Event, Config, Storage}, - Instance2Example: pallet::::{Pallet, Call, Event, Config, Storage}, - PalletOld2: pallet_old::::{Pallet, Call, Event, Config, Storage}, - Instance3Example: pallet::::{Pallet, Call, Event, Config, Storage}, - PalletOld3: pallet_old::::{Pallet, Call, Event, Config, Storage}, - } -); - -#[cfg(test)] -mod test { - use super::{pallet, pallet_old, Runtime}; - use codec::{Decode, Encode}; - use scale_info::{form::PortableForm, Variant}; - - #[test] - fn metadata() { - let metadata = Runtime::metadata(); - let (pallets, types) = match metadata.1 { - frame_support::metadata::RuntimeMetadata::V14(metadata) => - (metadata.pallets, metadata.types), - _ => unreachable!(), - }; - - let get_enum_variants = |ty_id| match types.resolve(ty_id).map(|ty| ty.type_def.clone()) { - Some(ty) => match ty { - scale_info::TypeDef::Variant(var) => var.variants, - _ => panic!("Expected variant type"), - }, - _ => panic!("No type found"), - }; - - let assert_enum_variants = |vs1: &[Variant], - vs2: &[Variant]| { - assert_eq!(vs1.len(), vs2.len()); - for i in 0..vs1.len() { - let v1 = &vs2[i]; - let v2 = &vs2[i]; - assert_eq!(v1.fields.len(), v2.fields.len()); - for f in 0..v1.fields.len() { - let f1 = &v1.fields[f]; - let f2 = &v2.fields[f]; - pretty_assertions::assert_eq!(f1.name, f2.name); - pretty_assertions::assert_eq!(f1.ty, f2.ty); - } - } - }; - - for i in vec![1, 3, 5].into_iter() { - pretty_assertions::assert_eq!(pallets[i].storage, pallets[i + 1].storage); - - let call1_variants = get_enum_variants(pallets[i].calls.as_ref().unwrap().ty.id); - let call2_variants = get_enum_variants(pallets[i + 1].calls.as_ref().unwrap().ty.id); - assert_enum_variants(&call1_variants, &call2_variants); - - // event: check variants and fields but ignore the type name which will be different - let event1_variants = get_enum_variants(pallets[i].event.as_ref().unwrap().ty.id); - let event2_variants = get_enum_variants(pallets[i + 1].event.as_ref().unwrap().ty.id); - assert_enum_variants(&event1_variants, &event2_variants); - - let err1 = get_enum_variants(pallets[i].error.as_ref().unwrap().ty.id) - .iter() - .filter(|v| v.name == "__Ignore") - .cloned() - .collect::>(); - let err2 = get_enum_variants(pallets[i + 1].error.as_ref().unwrap().ty.id) - .iter() - .filter(|v| v.name == "__Ignore") - .cloned() - .collect::>(); - assert_enum_variants(&err1, &err2); - - pretty_assertions::assert_eq!(pallets[i].constants, pallets[i + 1].constants); - } - } - - #[test] - fn types() { - assert_eq!( - pallet_old::Event::::decode( - &mut &pallet::Event::::Dummy(10).encode()[..] - ) - .unwrap(), - pallet_old::Event::::Dummy(10), - ); - - assert_eq!( - pallet_old::Call::::decode( - &mut &pallet::Call::::set_dummy { new_value: 10 }.encode()[..] - ) - .unwrap(), - pallet_old::Call::::set_dummy { new_value: 10 }, - ); - } -} diff --git a/frame/support/test/tests/pallet_instance.rs b/frame/support/test/tests/pallet_instance.rs index 241492a59..074775328 100644 --- a/frame/support/test/tests/pallet_instance.rs +++ b/frame/support/test/tests/pallet_instance.rs @@ -18,21 +18,23 @@ use frame_support::{ dispatch::{DispatchClass, DispatchInfo, GetDispatchInfo, Pays, UnfilteredDispatchable}, pallet_prelude::ValueQuery, + parameter_types, storage::unhashed, traits::{ConstU32, GetCallName, OnFinalize, OnGenesis, OnInitialize, OnRuntimeUpgrade}, + weights::Weight, }; use sp_io::{ hashing::{blake2_128, twox_128, twox_64}, TestExternalities, }; use sp_runtime::{DispatchError, ModuleError}; +use sp_std::any::TypeId; #[frame_support::pallet(dev_mode)] pub mod pallet { - use codec::MaxEncodedLen; - use frame_support::{pallet_prelude::*, parameter_types, scale_info}; + use super::*; + use frame_support::pallet_prelude::*; use frame_system::pallet_prelude::*; - use sp_std::any::TypeId; type BalanceOf = >::Balance; @@ -346,8 +348,6 @@ frame_support::construct_runtime!( } ); -use frame_support::weights::Weight; - #[test] fn call_expand() { let call_foo = pallet::Call::::foo { foo: 3 }; diff --git a/frame/support/test/tests/pallet_with_name_trait_is_valid.rs b/frame/support/test/tests/pallet_with_name_trait_is_valid.rs deleted file mode 100644 index 8cd3c79cc..000000000 --- a/frame/support/test/tests/pallet_with_name_trait_is_valid.rs +++ /dev/null @@ -1,158 +0,0 @@ -// This file is part of Substrate. - -// Copyright (C) Parity Technologies (UK) Ltd. -// SPDX-License-Identifier: Apache-2.0 - -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -pub trait Trait: frame_system::Config { - type Balance: frame_support::dispatch::Parameter; - /// The overarching event type. - type RuntimeEvent: From> + Into<::RuntimeEvent>; -} - -frame_support::decl_storage! { - trait Store for Module as Example { - Dummy get(fn dummy) config(): Option; - } -} - -frame_support::decl_event!( - pub enum Event - where - B = ::Balance, - { - Dummy(B), - } -); - -frame_support::decl_error!( - pub enum Error for Module { - Dummy, - } -); - -frame_support::decl_module! { - pub struct Module for enum Call where origin: T::RuntimeOrigin { - fn deposit_event() = default; - type Error = Error; - const Foo: u32 = u32::MAX; - - #[weight = 0] - fn accumulate_dummy(_origin, _increase_by: T::Balance) { - unimplemented!(); - } - - fn on_initialize(_n: T::BlockNumber) -> frame_support::weights::Weight { - frame_support::weights::Weight::zero() - } - } -} - -impl sp_runtime::traits::ValidateUnsigned for Module { - type Call = Call; - - fn validate_unsigned( - _source: sp_runtime::transaction_validity::TransactionSource, - _call: &Self::Call, - ) -> sp_runtime::transaction_validity::TransactionValidity { - unimplemented!(); - } -} - -pub const INHERENT_IDENTIFIER: frame_support::inherent::InherentIdentifier = *b"12345678"; - -impl frame_support::inherent::ProvideInherent for Module { - type Call = Call; - type Error = frame_support::inherent::MakeFatalError<()>; - const INHERENT_IDENTIFIER: frame_support::inherent::InherentIdentifier = INHERENT_IDENTIFIER; - - fn create_inherent(_data: &frame_support::inherent::InherentData) -> Option { - unimplemented!(); - } - - fn check_inherent( - _: &Self::Call, - _: &frame_support::inherent::InherentData, - ) -> std::result::Result<(), Self::Error> { - unimplemented!(); - } - - fn is_inherent(_call: &Self::Call) -> bool { - unimplemented!(); - } -} - -#[cfg(test)] -mod tests { - use crate as pallet_test; - - use frame_support::traits::ConstU64; - - type SignedExtra = ( - frame_system::CheckEra, - frame_system::CheckNonce, - frame_system::CheckWeight, - ); - type TestBlock = sp_runtime::generic::Block; - type TestHeader = sp_runtime::generic::Header; - type TestUncheckedExtrinsic = sp_runtime::generic::UncheckedExtrinsic< - ::AccountId, - ::RuntimeCall, - (), - SignedExtra, - >; - - frame_support::construct_runtime!( - pub struct Runtime where - Block = TestBlock, - NodeBlock = TestBlock, - UncheckedExtrinsic = TestUncheckedExtrinsic - { - System: frame_system::{Pallet, Call, Config, Storage, Event}, - PalletTest: pallet_test::{Pallet, Call, Storage, Event, Config, ValidateUnsigned, Inherent}, - } - ); - - impl frame_system::Config for Runtime { - type BaseCallFilter = frame_support::traits::Everything; - type RuntimeOrigin = RuntimeOrigin; - type Index = u64; - type BlockNumber = u64; - type Hash = sp_core::H256; - type RuntimeCall = RuntimeCall; - type Hashing = sp_runtime::traits::BlakeTwo256; - type AccountId = u64; - type Lookup = sp_runtime::traits::IdentityLookup; - type Header = TestHeader; - type RuntimeEvent = (); - type BlockHashCount = ConstU64<250>; - type DbWeight = (); - type BlockWeights = (); - type BlockLength = (); - type Version = (); - type PalletInfo = PalletInfo; - type AccountData = (); - type OnNewAccount = (); - type OnKilledAccount = (); - type SystemWeightInfo = (); - type SS58Prefix = (); - type OnSetCode = (); - type MaxConsumers = frame_support::traits::ConstU32<16>; - } - - impl pallet_test::Trait for Runtime { - type Balance = u32; - type RuntimeEvent = (); - } -} diff --git a/frame/support/test/tests/storage_layers.rs b/frame/support/test/tests/storage_layers.rs index 82174bf9d..3e3068348 100644 --- a/frame/support/test/tests/storage_layers.rs +++ b/frame/support/test/tests/storage_layers.rs @@ -16,7 +16,7 @@ // limitations under the License. use frame_support::{ - assert_noop, assert_ok, dispatch::DispatchResult, pallet_prelude::ConstU32, + assert_noop, assert_ok, dispatch::DispatchResult, ensure, pallet_prelude::ConstU32, storage::with_storage_layer, }; use pallet::*; @@ -24,7 +24,8 @@ use sp_io::TestExternalities; #[frame_support::pallet(dev_mode)] pub mod pallet { - use frame_support::{ensure, pallet_prelude::*}; + use super::*; + use frame_support::pallet_prelude::*; use frame_system::pallet_prelude::*; #[pallet::pallet] @@ -56,48 +57,29 @@ pub mod pallet { } } -pub mod decl_pallet { - pub trait Config: frame_system::Config {} - - frame_support::decl_module! { - pub struct Module for enum Call where origin: T::RuntimeOrigin { - #[weight = 0] - pub fn set_value(_origin, value: u32) { - DeclValue::put(value); - frame_support::ensure!(value != 1, "Revert!"); - } - } - } - - frame_support::decl_storage! { - trait Store for Module as StorageTransactions { - pub DeclValue: u32; - } - } -} - -pub type BlockNumber = u64; +pub type BlockNumber = u32; pub type Index = u64; -pub type Header = sp_runtime::generic::Header; -pub type Block = sp_runtime::generic::Block; +pub type AccountId = u64; +pub type Header = sp_runtime::generic::Header; pub type UncheckedExtrinsic = sp_runtime::generic::UncheckedExtrinsic; +pub type Block = sp_runtime::generic::Block; impl frame_system::Config for Runtime { + type BaseCallFilter = frame_support::traits::Everything; type BlockWeights = (); type BlockLength = (); - type DbWeight = (); - type BaseCallFilter = frame_support::traits::Everything; type RuntimeOrigin = RuntimeOrigin; - type Index = u64; - type BlockNumber = u32; type RuntimeCall = RuntimeCall; + type Index = Index; + type BlockNumber = BlockNumber; type Hash = sp_runtime::testing::H256; type Hashing = sp_runtime::traits::BlakeTwo256; - type AccountId = u64; + type AccountId = AccountId; type Lookup = sp_runtime::traits::IdentityLookup; type Header = Header; type RuntimeEvent = RuntimeEvent; type BlockHashCount = ConstU32<250>; + type DbWeight = (); type Version = (); type PalletInfo = PalletInfo; type AccountData = (); @@ -109,19 +91,17 @@ impl frame_system::Config for Runtime { type MaxConsumers = ConstU32<16>; } -impl pallet::Config for Runtime {} - -impl decl_pallet::Config for Runtime {} +impl Config for Runtime {} frame_support::construct_runtime!( - pub struct Runtime where + pub struct Runtime + where Block = Block, NodeBlock = Block, - UncheckedExtrinsic = UncheckedExtrinsic + UncheckedExtrinsic = UncheckedExtrinsic, { System: frame_system, MyPallet: pallet, - DeclPallet: decl_pallet::{Call, Storage}, } ); @@ -264,23 +244,3 @@ fn storage_layer_in_pallet_call() { assert_noop!(call2.dispatch(RuntimeOrigin::signed(0)), Error::::Revert); }); } - -#[test] -fn storage_layer_in_decl_pallet_call() { - TestExternalities::default().execute_with(|| { - use frame_support::StorageValue; - use sp_runtime::traits::Dispatchable; - - let call1 = RuntimeCall::DeclPallet(decl_pallet::Call::set_value { value: 2 }); - assert_ok!(call1.dispatch(RuntimeOrigin::signed(0))); - assert_eq!(decl_pallet::DeclValue::get(), 2); - - let call2 = RuntimeCall::DeclPallet(decl_pallet::Call::set_value { value: 1 }); - assert_noop!(call2.dispatch(RuntimeOrigin::signed(0)), "Revert!"); - // Calling the function directly also works with storage layers. - assert_noop!( - decl_pallet::Module::::set_value(RuntimeOrigin::signed(1), 1), - "Revert!" - ); - }); -} diff --git a/frame/support/test/tests/storage_transaction.rs b/frame/support/test/tests/storage_transaction.rs index 769ecb29a..5fc4ba7cc 100644 --- a/frame/support/test/tests/storage_transaction.rs +++ b/frame/support/test/tests/storage_transaction.rs @@ -20,46 +20,86 @@ use frame_support::{ assert_noop, assert_ok, assert_storage_noop, - dispatch::{DispatchError, DispatchResult}, + dispatch::DispatchResult, storage::{with_transaction, TransactionOutcome::*}, - transactional, StorageMap, StorageValue, + transactional, }; +use sp_core::sr25519; use sp_io::TestExternalities; -use sp_runtime::TransactionOutcome; -use sp_std::result; +use sp_runtime::{ + generic, + traits::{BlakeTwo256, Verify}, + TransactionOutcome, +}; + +pub use self::pallet::*; + +#[frame_support::pallet] +pub mod pallet { + use self::frame_system::pallet_prelude::*; + use super::*; + use frame_support::pallet_prelude::*; + use frame_support_test as frame_system; -pub trait Config: frame_support_test::Config {} + #[pallet::pallet] + #[pallet::generate_store(pub (super) trait Store)] + pub struct Pallet(_); -frame_support::decl_module! { - pub struct Module for enum Call where origin: T::RuntimeOrigin, system=frame_support_test { - #[weight = 0] + #[pallet::config] + pub trait Config: frame_system::Config {} + + #[pallet::call] + impl Pallet { + #[pallet::weight(0)] #[transactional] - fn value_commits(_origin, v: u32) { - Value::set(v); + pub fn value_commits(_origin: OriginFor, v: u32) -> DispatchResult { + >::set(v); + Ok(()) } - #[weight = 0] + #[pallet::weight(0)] #[transactional] - fn value_rollbacks(_origin, v: u32) -> DispatchResult { - Value::set(v); + pub fn value_rollbacks(_origin: OriginFor, v: u32) -> DispatchResult { + >::set(v); Err(DispatchError::Other("nah")) } } -} -frame_support::decl_storage! { - trait Store for Module as StorageTransactions { - pub Value: u32; - pub Map: map hasher(twox_64_concat) String => u32; - } + #[pallet::storage] + pub type Value = StorageValue<_, u32, ValueQuery>; + + #[pallet::storage] + #[pallet::unbounded] + pub type Map = StorageMap<_, Twox64Concat, String, u32, ValueQuery>; } -struct Runtime; +pub type BlockNumber = u32; +pub type Signature = sr25519::Signature; +pub type AccountId = ::Signer; +pub type Header = generic::Header; +pub type UncheckedExtrinsic = generic::UncheckedExtrinsic; +pub type Block = generic::Block; + +frame_support::construct_runtime!( + pub enum Runtime + where + Block = Block, + NodeBlock = Block, + UncheckedExtrinsic = UncheckedExtrinsic, + { + System: frame_support_test, + MyPallet: pallet, + } +); impl frame_support_test::Config for Runtime { - type RuntimeOrigin = u32; - type BlockNumber = u32; - type PalletInfo = frame_support_test::PanicPalletInfo; + type BlockNumber = BlockNumber; + type AccountId = AccountId; + type BaseCallFilter = frame_support::traits::Everything; + type RuntimeOrigin = RuntimeOrigin; + type RuntimeCall = RuntimeCall; + type RuntimeEvent = RuntimeEvent; + type PalletInfo = PalletInfo; type DbWeight = (); } @@ -68,6 +108,9 @@ impl Config for Runtime {} #[test] fn storage_transaction_basic_commit() { TestExternalities::default().execute_with(|| { + type Value = pallet::Value; + type Map = pallet::Map; + assert_eq!(Value::get(), 0); assert!(!Map::contains_key("val0")); @@ -87,6 +130,9 @@ fn storage_transaction_basic_commit() { #[test] fn storage_transaction_basic_rollback() { TestExternalities::default().execute_with(|| { + type Value = pallet::Value; + type Map = pallet::Map; + assert_eq!(Value::get(), 0); assert_eq!(Map::get("val0"), 0); @@ -119,6 +165,9 @@ fn storage_transaction_basic_rollback() { #[test] fn storage_transaction_rollback_then_commit() { TestExternalities::default().execute_with(|| { + type Value = pallet::Value; + type Map = pallet::Map; + Value::set(1); Map::insert("val1", 1); @@ -162,6 +211,9 @@ fn storage_transaction_rollback_then_commit() { #[test] fn storage_transaction_commit_then_rollback() { TestExternalities::default().execute_with(|| { + type Value = pallet::Value; + type Map = pallet::Map; + Value::set(1); Map::insert("val1", 1); @@ -204,19 +256,21 @@ fn storage_transaction_commit_then_rollback() { #[test] fn transactional_annotation() { + type Value = pallet::Value; + fn set_value(v: u32) -> DispatchResult { Value::set(v); Ok(()) } #[transactional] - fn value_commits(v: u32) -> result::Result { + fn value_commits(v: u32) -> Result { set_value(v)?; Ok(v) } #[transactional] - fn value_rollbacks(v: u32) -> result::Result { + fn value_rollbacks(v: u32) -> Result { set_value(v)?; Err("nah")?; Ok(v) @@ -229,14 +283,3 @@ fn transactional_annotation() { assert_noop!(value_rollbacks(3), "nah"); }); } - -#[test] -fn transactional_annotation_in_decl_module() { - TestExternalities::default().execute_with(|| { - let origin = 0; - assert_ok!(>::value_commits(origin, 2)); - assert_eq!(Value::get(), 2); - - assert_noop!(>::value_rollbacks(origin, 3), "nah"); - }); -} diff --git a/frame/support/test/tests/system.rs b/frame/support/test/tests/system.rs deleted file mode 100644 index 1a938ad4e..000000000 --- a/frame/support/test/tests/system.rs +++ /dev/null @@ -1,81 +0,0 @@ -// This file is part of Substrate. - -// Copyright (C) Parity Technologies (UK) Ltd. -// SPDX-License-Identifier: Apache-2.0 - -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -use frame_support::{ - codec::{Decode, Encode, EncodeLike}, - traits::Get, - weights::RuntimeDbWeight, -}; - -pub trait Config: 'static + Eq + Clone { - type RuntimeOrigin: Into, Self::RuntimeOrigin>> - + From>; - - type BaseCallFilter: frame_support::traits::Contains; - type BlockNumber: Decode + Encode + EncodeLike + Clone + Default + scale_info::TypeInfo; - type Hash; - type AccountId: Encode + EncodeLike + Decode + scale_info::TypeInfo; - type RuntimeCall; - type RuntimeEvent: From>; - type PalletInfo: frame_support::traits::PalletInfo; - type DbWeight: Get; -} - -frame_support::decl_module! { - pub struct Module for enum Call where origin: T::RuntimeOrigin, system=self { - #[weight = 0] - fn noop(_origin) {} - } -} - -impl Module { - pub fn deposit_event(_event: impl Into) {} -} - -frame_support::decl_event!( - pub enum Event - where - BlockNumber = ::BlockNumber, - { - ExtrinsicSuccess, - ExtrinsicFailed, - Ignore(BlockNumber), - } -); - -frame_support::decl_error! { - pub enum Error for Module { - /// Test error documentation - TestError, - /// Error documentation - /// with multiple lines - AnotherError, - // Required by construct_runtime - CallFiltered, - } -} - -pub use frame_support::dispatch::RawOrigin; -pub type Origin = RawOrigin<::AccountId>; - -#[allow(dead_code)] -pub fn ensure_root(o: OuterOrigin) -> Result<(), &'static str> -where - OuterOrigin: Into, OuterOrigin>>, -{ - o.into().map(|_| ()).map_err(|_| "bad origin: expected to be a root origin") -} From 65e7ab604d109e316a69b8801c3b182a7fa46bcb Mon Sep 17 00:00:00 2001 From: Keith Yeung Date: Tue, 9 May 2023 20:28:58 +0800 Subject: [PATCH 475/558] Emit events related to asset mutations (#14099) * Emit events related to asset mutations * Fixes * Improve unit tests * cargo fmt --- frame/assets/src/impl_fungibles.rs | 33 +++++++++++++++++++++++++++++- frame/assets/src/tests.rs | 29 ++++++++++++++++++++++++++ 2 files changed, 61 insertions(+), 1 deletion(-) diff --git a/frame/assets/src/impl_fungibles.rs b/frame/assets/src/impl_fungibles.rs index 7bec884f4..893d74b6a 100644 --- a/frame/assets/src/impl_fungibles.rs +++ b/frame/assets/src/impl_fungibles.rs @@ -81,7 +81,38 @@ impl, I: 'static> fungibles::Inspect<::AccountId } } -impl, I: 'static> fungibles::Mutate<::AccountId> for Pallet {} +impl, I: 'static> fungibles::Mutate<::AccountId> for Pallet { + fn done_mint_into( + asset_id: Self::AssetId, + beneficiary: &::AccountId, + amount: Self::Balance, + ) { + Self::deposit_event(Event::Issued { asset_id, owner: beneficiary.clone(), amount }) + } + + fn done_burn_from( + asset_id: Self::AssetId, + target: &::AccountId, + balance: Self::Balance, + ) { + Self::deposit_event(Event::Burned { asset_id, owner: target.clone(), balance }); + } + + fn done_transfer( + asset_id: Self::AssetId, + source: &::AccountId, + dest: &::AccountId, + amount: Self::Balance, + ) { + Self::deposit_event(Event::Transferred { + asset_id, + from: source.clone(), + to: dest.clone(), + amount, + }); + } +} + impl, I: 'static> fungibles::Balanced<::AccountId> for Pallet { diff --git a/frame/assets/src/tests.rs b/frame/assets/src/tests.rs index 009d0180f..9eb1107aa 100644 --- a/frame/assets/src/tests.rs +++ b/frame/assets/src/tests.rs @@ -52,9 +52,18 @@ fn transfer_should_never_burn() { while System::inc_consumers(&2).is_ok() {} let _ = System::dec_consumers(&2); + let _ = System::dec_consumers(&2); // Exactly one consumer ref remaining. + assert_eq!(System::consumers(&2), 1); let _ = >::transfer(0, &1, &2, 50, Protect); + System::assert_has_event(RuntimeEvent::Assets(crate::Event::Transferred { + asset_id: 0, + from: 1, + to: 2, + amount: 50, + })); + assert_eq!(Assets::balance(0, 1), 50); assert_eq!(Assets::balance(0, 1) + Assets::balance(0, 2), 100); }); } @@ -65,11 +74,26 @@ fn basic_minting_should_work() { assert_ok!(Assets::force_create(RuntimeOrigin::root(), 0, 1, true, 1)); assert_ok!(Assets::force_create(RuntimeOrigin::root(), 1, 1, true, 1)); assert_ok!(Assets::mint(RuntimeOrigin::signed(1), 0, 1, 100)); + System::assert_last_event(RuntimeEvent::Assets(crate::Event::Issued { + asset_id: 0, + owner: 1, + amount: 100, + })); assert_eq!(Assets::balance(0, 1), 100); assert_ok!(Assets::mint(RuntimeOrigin::signed(1), 0, 2, 100)); + System::assert_last_event(RuntimeEvent::Assets(crate::Event::Issued { + asset_id: 0, + owner: 2, + amount: 100, + })); assert_eq!(Assets::balance(0, 2), 100); assert_eq!(asset_ids(), vec![0, 1, 999]); assert_ok!(Assets::mint(RuntimeOrigin::signed(1), 1, 1, 100)); + System::assert_last_event(RuntimeEvent::Assets(crate::Event::Issued { + asset_id: 1, + owner: 1, + amount: 100, + })); assert_eq!(Assets::account_balances(1), vec![(0, 100), (999, 100), (1, 100)]); }); } @@ -1133,6 +1157,11 @@ fn burning_asset_balance_with_positive_balance_should_work() { assert_ok!(Assets::mint(RuntimeOrigin::signed(1), 0, 1, 100)); assert_eq!(Assets::balance(0, 1), 100); assert_ok!(Assets::burn(RuntimeOrigin::signed(1), 0, 1, u64::MAX)); + System::assert_last_event(RuntimeEvent::Assets(crate::Event::Burned { + asset_id: 0, + owner: 1, + balance: 100, + })); assert_eq!(Assets::balance(0, 1), 0); }); } From 2a148e38a191d12d0e5b76d72845a844b7cad543 Mon Sep 17 00:00:00 2001 From: Oliver Tale-Yazdi Date: Tue, 9 May 2023 16:28:47 +0200 Subject: [PATCH 476/558] Deprecate Pallet `decl_*` Macros (#13705) * Deprecate decl_ macros Signed-off-by: Oliver Tale-Yazdi * Allow deprecated in tests Signed-off-by: Oliver Tale-Yazdi * Revert "Allow deprecated in tests" This reverts commit ce36080f9bb4079e9069af8ee02e0954753ae412. * Deprecate all Signed-off-by: Oliver Tale-Yazdi * Push missing files Signed-off-by: Oliver Tale-Yazdi * fmt Signed-off-by: Oliver Tale-Yazdi * Move decl_module to own file Otherwise i have to spam allow(deprecated) for all recursive calls. Signed-off-by: Oliver Tale-Yazdi * Revert "Move decl_module to own file" This reverts commit 543a31185cda708d417127d6cb89cdc0c31d1f34. * Fix tests Signed-off-by: Oliver Tale-Yazdi --------- Signed-off-by: Oliver Tale-Yazdi --- frame/support/procedural/src/lib.rs | 2 ++ frame/support/src/dispatch.rs | 3 +++ frame/support/src/error.rs | 2 ++ frame/support/src/event.rs | 2 ++ 4 files changed, 9 insertions(+) diff --git a/frame/support/procedural/src/lib.rs b/frame/support/procedural/src/lib.rs index 33d9ce947..abfa21b33 100644 --- a/frame/support/procedural/src/lib.rs +++ b/frame/support/procedural/src/lib.rs @@ -296,6 +296,8 @@ fn counter_prefix(prefix: &str) -> String { /// } /// ``` #[proc_macro] +#[deprecated(note = "Will be removed soon; use the attribute `#[pallet]` macro instead. + For more info, see: ")] pub fn decl_storage(input: TokenStream) -> TokenStream { storage::decl_storage_impl(input) } diff --git a/frame/support/src/dispatch.rs b/frame/support/src/dispatch.rs index db9405245..071fb7c9d 100644 --- a/frame/support/src/dispatch.rs +++ b/frame/support/src/dispatch.rs @@ -879,6 +879,8 @@ impl PaysFee for (u64, Pays) { /// in an externalities-provided environment. Implement /// [`IntegrityTest`](./trait.IntegrityTest.html) trait. #[macro_export] +#[deprecated(note = "Will be removed soon; use the attribute `#[pallet]` macro instead. + For more info, see: ")] macro_rules! decl_module { // Entry point #1. ( @@ -3197,6 +3199,7 @@ macro_rules! __check_reserved_fn_name { #[cfg(test)] // Do not complain about unused `dispatch` and `dispatch_aux`. #[allow(dead_code)] +#[allow(deprecated)] mod tests { use super::*; use crate::{ diff --git a/frame/support/src/error.rs b/frame/support/src/error.rs index 72d13dfce..fa711389a 100644 --- a/frame/support/src/error.rs +++ b/frame/support/src/error.rs @@ -67,6 +67,8 @@ pub use sp_runtime::traits::{BadOrigin, LookupError}; /// For instantiable modules you also need to give the instance generic type and bound to the /// error declaration. #[macro_export] +#[deprecated(note = "Will be removed soon; use the attribute `#[pallet]` macro instead. + For more info, see: ")] macro_rules! decl_error { ( $(#[$attr:meta])* diff --git a/frame/support/src/event.rs b/frame/support/src/event.rs index ce9e0dbb7..ba3e5a275 100644 --- a/frame/support/src/event.rs +++ b/frame/support/src/event.rs @@ -101,6 +101,8 @@ /// # fn main() {} /// ``` #[macro_export] +#[deprecated(note = "Will be removed soon; use the attribute `#[pallet]` macro instead. + For more info, see: ")] macro_rules! decl_event { ( $(#[$attr:meta])* From 0dff96ee91462956ea0020bc62e74ee85c90813e Mon Sep 17 00:00:00 2001 From: PG Herveou Date: Tue, 9 May 2023 17:24:41 +0200 Subject: [PATCH 477/558] Add non_camel_case_types for storage_alias (#14104) --- frame/support/procedural/src/storage_alias.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/frame/support/procedural/src/storage_alias.rs b/frame/support/procedural/src/storage_alias.rs index 769956872..b44a7ee99 100644 --- a/frame/support/procedural/src/storage_alias.rs +++ b/frame/support/procedural/src/storage_alias.rs @@ -626,6 +626,7 @@ fn generate_storage_instance( // Implement `StorageInstance` trait. let code = quote! { + #[allow(non_camel_case_types)] #visibility struct #name< #impl_generics >( #crate_::sp_std::marker::PhantomData<(#type_generics)> ) #where_clause; From c014d4a876b1b9db9affabfc19ba1707831c8c3f Mon Sep 17 00:00:00 2001 From: Mira Ressel Date: Tue, 9 May 2023 17:38:57 +0200 Subject: [PATCH 478/558] Move back to ci-linux:production image after upgrade (#14105) There are still a bunch of open questions on how to handle image pinning; in the meantime, let's keep using the production tag. --- .gitlab-ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 39ac5204f..cc950865c 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -48,7 +48,7 @@ variables: CARGO_INCREMENTAL: 0 DOCKER_OS: "debian:bullseye" ARCH: "x86_64" - CI_IMAGE: "paritytech/ci-linux@sha256:f6cdc1e289e40f3eb1f93efb11f961d4f0e51f4c2adf80b31300ab5b35ef1386" # staging 2023-05-02 + CI_IMAGE: "paritytech/ci-linux:production" BUILDAH_IMAGE: "quay.io/buildah/stable:v1.29" BUILDAH_COMMAND: "buildah --storage-driver overlay2" RELENG_SCRIPTS_BRANCH: "master" From e92613d9a4f4e85b57a2dd891765c407f0ad2549 Mon Sep 17 00:00:00 2001 From: drskalman <35698397+drskalman@users.noreply.github.com> Date: Tue, 9 May 2023 21:09:43 +0000 Subject: [PATCH 479/558] BLS Core Crypto attempt #2 (#13618) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Cherry pick all crypto related changes from pull-request #13311 applied to master's head * Import some stuff just if 'full_crypto' is on * Remove copyright year * Cleanup * First generic BLS draft * Finalize generic implementation * Restore tests * Fix rust docs * Fix after master merge * Fix after master merge * Use double bls with G1 as signature group and verify individual signatures using DLEQ proof. * Fix inclusions and types used within substrate * Remove unused cruft * Restore usage of upstream crates * Fix test * Reduce the diff by aligning Cargo.lock to master * Application-crypto provides bls381 * Implement bls381 for local keystore * Use new generic keystore features * import DoublePublickey[Scheme] from the bls-like root to be less confusing. * fix compilation * Apply suggestions from code review Co-authored-by: Robert Hambrock * Clean leftovers * - update bls test vector after applying spec change recommendation. - send message as ref. * Different hard junction ids for different bls12 types * update to new bls-like * bls-like → w3f-bls * Make clippy happy * update test vector after replacing hash and crop with hash to field. * cargo fmt * account for #13972 * hide BLS behind "bls_non_production" feature flag * Remove Cargo.lock entries duplicated in merge * add bls377 to primitives/keystore and client/keystore add bls377 to primitives/application-crypto/ add bls_non_production to primitives/keystore and client/keystore bump up w3f-bls version * rename feature `bls_non_production` to `bls-experimental` --------- Co-authored-by: Davide Galassi Co-authored-by: André Silva Co-authored-by: Robert Hambrock --- Cargo.lock | 1380 +++++++++++-------- client/keystore/Cargo.toml | 9 +- client/keystore/src/local.rs | 62 +- primitives/application-crypto/src/bls377.rs | 28 + primitives/application-crypto/src/bls381.rs | 28 + primitives/application-crypto/src/lib.rs | 4 + primitives/core/Cargo.toml | 7 + primitives/core/src/bls.rs | 675 +++++++++ primitives/core/src/hexdisplay.rs | 2 +- primitives/core/src/lib.rs | 5 + primitives/core/src/testing.rs | 4 + primitives/keystore/Cargo.toml | 4 + primitives/keystore/src/lib.rs | 86 +- primitives/keystore/src/testing.rs | 50 + 14 files changed, 1730 insertions(+), 614 deletions(-) create mode 100644 primitives/application-crypto/src/bls377.rs create mode 100644 primitives/application-crypto/src/bls381.rs create mode 100644 primitives/core/src/bls.rs diff --git a/Cargo.lock b/Cargo.lock index e9bc2a996..3c6c24969 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -42,7 +42,7 @@ version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7fc95d1bdb8e6666b2b217308eeeb09f2d6728d104be3e31916cc74d15420331" dependencies = [ - "generic-array 0.14.6", + "generic-array 0.14.7", ] [[package]] @@ -51,18 +51,18 @@ version = "0.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0b613b8e1e3cf911a086f53f03bf286f52fd7a7258e4fa606f0ef220d39d8877" dependencies = [ - "generic-array 0.14.6", + "generic-array 0.14.7", "rand_core 0.6.4", ] [[package]] name = "aead" -version = "0.5.1" +version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5c192eb8f11fc081b0fe4259ba5af04217d4e0faddd02417310a927911abd7c8" +checksum = "d122413f284cf2d62fb1b7db97e02edb8cda96d769b16e443a4f6195e35662b0" dependencies = [ "crypto-common", - "generic-array 0.14.6", + "generic-array 0.14.7", ] [[package]] @@ -95,7 +95,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "433cfd6710c9986c576a25ca913c39d66a6474107b406f34f91d4a8923395241" dependencies = [ "cfg-if", - "cipher 0.4.3", + "cipher 0.4.4", "cpufeatures", ] @@ -119,9 +119,9 @@ version = "0.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "82e1366e0c69c9f927b1fa5ce2c7bf9eafc8f9268c0b9800729e8b267612447c" dependencies = [ - "aead 0.5.1", + "aead 0.5.2", "aes 0.8.2", - "cipher 0.4.3", + "cipher 0.4.4", "ctr 0.9.2", "ghash 0.5.0", "subtle", @@ -153,7 +153,7 @@ version = "0.7.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fcb51a0695d8f838b1ee009b3fbf66bda078cd64590202a864a8f3e8c4315c47" dependencies = [ - "getrandom 0.2.8", + "getrandom 0.2.9", "once_cell", "version_check", ] @@ -165,7 +165,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2c99f64d1e06488f620f932677e24bc6e2897582980441ae90a671415bd7ec2f" dependencies = [ "cfg-if", - "getrandom 0.2.8", + "getrandom 0.2.9", "once_cell", "version_check", ] @@ -179,6 +179,15 @@ dependencies = [ "memchr", ] +[[package]] +name = "aho-corasick" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "67fc08ce920c31afb70f013dcce1bfc3a3195de6a228474e45e1f145b36f8d04" +dependencies = [ + "memchr", +] + [[package]] name = "android_system_properties" version = "0.1.5" @@ -209,7 +218,7 @@ version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0ca84f3628370c59db74ee214b3263d58f9aadd9b4fe7e711fd87dc452b7f163" dependencies = [ - "anstyle 1.0.0", + "anstyle", "anstyle-parse", "anstyle-query", "anstyle-wincon", @@ -218,12 +227,6 @@ dependencies = [ "utf8parse", ] -[[package]] -name = "anstyle" -version = "0.3.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "23ea9e81bd02e310c216d080f6223c179012256e5151c41db88d12c88a1684d2" - [[package]] name = "anstyle" version = "1.0.0" @@ -254,15 +257,15 @@ version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "180abfa45703aebe0093f79badacc01b8fd4ea2e35118747e5811127f926e188" dependencies = [ - "anstyle 1.0.0", + "anstyle", "windows-sys 0.48.0", ] [[package]] name = "anyhow" -version = "1.0.69" +version = "1.0.71" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "224afbd727c3d6e4b90103ece64b8d1b67fbb1973b1046c2281eed3f3803f800" +checksum = "9c7d0618f0e0b7e8ff11427422b64564d5fb0be1940354bfe2e0529b18a9d9b8" [[package]] name = "approx" @@ -285,6 +288,135 @@ version = "1.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bddcadddf5e9015d310179a59bb28c4d4b9920ad0f11e8e14dbadf654890c9a6" +[[package]] +name = "ark-bls12-377" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fb00293ba84f51ce3bd026bd0de55899c4e68f0a39a5728cebae3a73ffdc0a4f" +dependencies = [ + "ark-ec", + "ark-ff", + "ark-std", +] + +[[package]] +name = "ark-bls12-381" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c775f0d12169cba7aae4caeb547bb6a50781c7449a8aa53793827c9ec4abf488" +dependencies = [ + "ark-ec", + "ark-ff", + "ark-serialize", + "ark-std", +] + +[[package]] +name = "ark-ec" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "defd9a439d56ac24968cca0571f598a61bc8c55f71d50a89cda591cb750670ba" +dependencies = [ + "ark-ff", + "ark-poly", + "ark-serialize", + "ark-std", + "derivative", + "hashbrown 0.13.2", + "itertools", + "num-traits", + "zeroize", +] + +[[package]] +name = "ark-ff" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec847af850f44ad29048935519032c33da8aa03340876d351dfab5660d2966ba" +dependencies = [ + "ark-ff-asm", + "ark-ff-macros", + "ark-serialize", + "ark-std", + "derivative", + "digest 0.10.6", + "itertools", + "num-bigint", + "num-traits", + "paste", + "rustc_version 0.4.0", + "zeroize", +] + +[[package]] +name = "ark-ff-asm" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3ed4aa4fe255d0bc6d79373f7e31d2ea147bcf486cba1be5ba7ea85abdb92348" +dependencies = [ + "quote", + "syn 1.0.109", +] + +[[package]] +name = "ark-ff-macros" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7abe79b0e4288889c4574159ab790824d0033b9fdcb2a112a3182fac2e514565" +dependencies = [ + "num-bigint", + "num-traits", + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "ark-poly" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d320bfc44ee185d899ccbadfa8bc31aab923ce1558716e1997a1e74057fe86bf" +dependencies = [ + "ark-ff", + "ark-serialize", + "ark-std", + "derivative", + "hashbrown 0.13.2", +] + +[[package]] +name = "ark-serialize" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "adb7b85a02b83d2f22f89bd5cac66c9c89474240cb6207cb1efc16d098e822a5" +dependencies = [ + "ark-serialize-derive", + "ark-std", + "digest 0.10.6", + "num-bigint", +] + +[[package]] +name = "ark-serialize-derive" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ae3281bc6d0fd7e549af32b52511e1302185bd688fd3359fa36423346ff682ea" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "ark-std" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94893f1e0c6eeab764ade8dc4c0db24caf4fe7cbbaafc0eba0a9030f447b5185" +dependencies = [ + "num-traits", + "rand 0.8.5", +] + [[package]] name = "array-bytes" version = "4.2.0" @@ -299,9 +431,9 @@ checksum = "d9b1c5a481ec30a5abd8dfbd94ab5cf1bb4e9a66be7f1b3b322f2f1170c200fd" [[package]] name = "arrayref" -version = "0.3.6" +version = "0.3.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a4c527152e37cf757a3f78aae5a06fbeefdb07ccc535c980a3208ee3060dd544" +checksum = "6b4930d2cb77ce62f89ee5d5289b4ac049559b1c45539271f5ed4fdc7db34545" [[package]] name = "arrayvec" @@ -328,14 +460,14 @@ dependencies = [ "num-traits", "rusticata-macros", "thiserror", - "time 0.3.20", + "time 0.3.21", ] [[package]] name = "asn1-rs" -version = "0.5.1" +version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cf6690c370453db30743b373a60ba498fc0d6d83b11f4abfd87a84a075db5dd4" +checksum = "7f6fd5ddaf0351dff5b8da21b2fb4ff8e08ddd02857f0bf69c47639106c0fff0" dependencies = [ "asn1-rs-derive 0.4.0", "asn1-rs-impl", @@ -344,7 +476,7 @@ dependencies = [ "num-traits", "rusticata-macros", "thiserror", - "time 0.3.20", + "time 0.3.21", ] [[package]] @@ -384,20 +516,20 @@ dependencies = [ [[package]] name = "asn1_der" -version = "0.7.5" +version = "0.7.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e22d1f4b888c298a027c99dc9048015fac177587de20fc30232a057dfbe24a21" +checksum = "155a5a185e42c6b77ac7b88a15143d930a9e9727a5b7b77eed417404ab15c247" [[package]] name = "assert_cmd" -version = "2.0.10" +version = "2.0.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ec0b2340f55d9661d76793b2bfc2eb0e62689bd79d067a95707ea762afd5e9dd" +checksum = "86d6b683edf8d1119fe420a94f8a7e389239666aa72e65495d91c00462510151" dependencies = [ - "anstyle 0.3.5", + "anstyle", "bstr", "doc-comment", - "predicates 3.0.2", + "predicates 3.0.3", "predicates-core", "predicates-tree", "wait-timeout", @@ -422,32 +554,31 @@ dependencies = [ [[package]] name = "async-io" -version = "1.12.0" +version = "1.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8c374dda1ed3e7d8f0d9ba58715f924862c63eae6849c92d3a18e7fbde9e2794" +checksum = "0fc5b45d93ef0529756f812ca52e44c221b35341892d3dcc34132ac02f3dd2af" dependencies = [ "async-lock", "autocfg", + "cfg-if", "concurrent-queue", "futures-lite", - "libc", "log", "parking", "polling", + "rustix 0.37.19", "slab", "socket2", "waker-fn", - "windows-sys 0.42.0", ] [[package]] name = "async-lock" -version = "2.6.0" +version = "2.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c8101efe8695a6c17e02911402145357e718ac92d3ff88ae8419e84b1707b685" +checksum = "fa24f727524730b077666307f2734b4a1a1c57acb79193127dcc8914d5242dd7" dependencies = [ "event-listener", - "futures-lite", ] [[package]] @@ -458,14 +589,14 @@ checksum = "0e97ce7de6cf12de5d7226c73f5ba9811622f4db3a5b91b55c53e987e5f91cba" dependencies = [ "proc-macro2", "quote", - "syn 2.0.14", + "syn 2.0.15", ] [[package]] name = "async-stream" -version = "0.3.4" +version = "0.3.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ad445822218ce64be7a341abfb0b1ea43b5c23aa83902542a4542e78309d8e5e" +checksum = "cd56dd203fef61ac097dd65721a419ddccb106b2d2b70ba60a6b529f03961a51" dependencies = [ "async-stream-impl", "futures-core", @@ -474,24 +605,24 @@ dependencies = [ [[package]] name = "async-stream-impl" -version = "0.3.4" +version = "0.3.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e4655ae1a7b0cdf149156f780c5bf3f1352bc53cbd9e0a361a7ef7b22947e965" +checksum = "16e62a023e7c117e27523144c5d2459f4397fcc3cab0085af8e2224f643a0193" dependencies = [ "proc-macro2", "quote", - "syn 1.0.109", + "syn 2.0.15", ] [[package]] name = "async-trait" -version = "0.1.64" +version = "0.1.68" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1cd7fce9ba8c3c042128ce72d8b2ddbf3a05747efb67ea0313c635e10bda47a2" +checksum = "b9ccdd8f2a161be9bd5c023df56f1b2a0bd1d83872ae53b71a84a12c9bf6e842" dependencies = [ "proc-macro2", "quote", - "syn 1.0.109", + "syn 2.0.15", ] [[package]] @@ -509,9 +640,9 @@ dependencies = [ [[package]] name = "atomic-waker" -version = "1.1.0" +version = "1.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "debc29dde2e69f9e47506b525f639ed42300fc014a3e007832592448fa8e4599" +checksum = "1181e1e0d1fce796a03db1ae795d67167da795f9cf4a39c37589e85ef57f26d3" [[package]] name = "atty" @@ -540,7 +671,7 @@ dependencies = [ "cc", "cfg-if", "libc", - "miniz_oxide", + "miniz_oxide 0.6.2", "object 0.30.3", "rustc-demangle", ] @@ -583,9 +714,9 @@ checksum = "8c3c1a368f70d6cf7302d78f8f7093da241fb8e8807c05cc9e51a125895a6d5b" [[package]] name = "basic-toml" -version = "0.1.1" +version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2e819b667739967cd44d308b8c7b71305d8bb0729ac44a248aa08f33d01950b4" +checksum = "5c0de75129aa8d0cceaf750b89013f0e08804d6ec61416da787b35ad0d7cddf1" dependencies = [ "serde", ] @@ -720,7 +851,7 @@ version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4152116fd6e9dadb291ae18fc1ec3575ed6d84c29642d97890f4b4a3417297e4" dependencies = [ - "generic-array 0.14.6", + "generic-array 0.14.7", ] [[package]] @@ -729,7 +860,7 @@ version = "0.10.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71" dependencies = [ - "generic-array 0.14.6", + "generic-array 0.14.7", ] [[package]] @@ -759,9 +890,9 @@ checksum = "8d696c370c750c948ada61c69a0ee2cbbb9c50b1019ddb86d9317157a99c2cae" [[package]] name = "bounded-collections" -version = "0.1.5" +version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a071c348a5ef6da1d3a87166b408170b46002382b1dda83992b5c2208cefb370" +checksum = "e3888522b497857eb606bf51695988dba7096941822c1bcf676e3a929a9ae7a0" dependencies = [ "log", "parity-scale-codec", @@ -777,9 +908,9 @@ checksum = "771fe0050b883fcc3ea2359b1a96bcfbc090b7116eae7c3c512c7a083fdf23d3" [[package]] name = "bstr" -version = "1.3.0" +version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5ffdb39cb703212f3c11973452c2861b972f757b021158f3516ba10f2fa8b2c1" +checksum = "c3d4260bcc2e8fc9df1eac4919a720effeb63a3f0952f5bf4944adfa18897f09" dependencies = [ "memchr", "once_cell", @@ -798,9 +929,9 @@ dependencies = [ [[package]] name = "bumpalo" -version = "3.12.0" +version = "3.12.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0d261e256854913907f67ed06efbc3338dfe6179796deefc1ff763fc1aee5535" +checksum = "9b1ce199063694f33ffb7dd4e0ee620741495c32833cde5aa08f02a0bf96f0c8" [[package]] name = "byte-slice-cast" @@ -816,9 +947,9 @@ checksum = "e3b5ca7a04898ad4bcd41c90c5285445ff5b791899bb1b0abdd2a2aa791211d7" [[package]] name = "bytemuck" -version = "1.13.0" +version = "1.13.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c041d3eab048880cb0b86b256447da3f18859a163c3b8d8893f4e6368abe6393" +checksum = "17febce684fd15d89027105661fec94afb475cb995fbc59d2865198446ba2eea" [[package]] name = "byteorder" @@ -845,9 +976,9 @@ dependencies = [ [[package]] name = "camino" -version = "1.1.3" +version = "1.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6031a462f977dd38968b6f23378356512feeace69cef817e1a4475108093cec3" +checksum = "c530edf18f37068ac2d977409ed5cd50d53d73bc653c7647b48eb78976ac9ae2" dependencies = [ "serde", ] @@ -869,7 +1000,7 @@ checksum = "eee4243f1f26fc7a42710e7439c149e2b10b05472f88090acce52632f231a73a" dependencies = [ "camino", "cargo-platform", - "semver 1.0.16", + "semver 1.0.17", "serde", "serde_json", "thiserror", @@ -961,7 +1092,7 @@ name = "chain-spec-builder" version = "2.0.0" dependencies = [ "ansi_term", - "clap 4.2.5", + "clap 4.2.7", "node-cli", "rand 0.8.5", "sc-chain-spec", @@ -972,9 +1103,9 @@ dependencies = [ [[package]] name = "chrono" -version = "0.4.23" +version = "0.4.24" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "16b0a3d9ed01224b22057780a37bb8c5dbfe1be8ba48678e7bf57ec4b385411f" +checksum = "4e3c5919066adf22df73762e50cffcde3a758f2a848b113b586d1f86728b673b" dependencies = [ "iana-time-zone", "js-sys", @@ -987,9 +1118,9 @@ dependencies = [ [[package]] name = "ciborium" -version = "0.2.0" +version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b0c137568cc60b904a7724001b35ce2630fd00d5d84805fbb608ab89509d788f" +checksum = "effd91f6c78e5a4ace8a5d3c0b6bfaec9e2baaef55f3efc00e45fb2e477ee926" dependencies = [ "ciborium-io", "ciborium-ll", @@ -998,15 +1129,15 @@ dependencies = [ [[package]] name = "ciborium-io" -version = "0.2.0" +version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "346de753af073cc87b52b2083a506b38ac176a44cfb05497b622e27be899b369" +checksum = "cdf919175532b369853f5d5e20b26b43112613fd6fe7aee757e35f7a44642656" [[package]] name = "ciborium-ll" -version = "0.2.0" +version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "213030a2b5a4e0c0892b6652260cf6ccac84827b83a85a534e178e3906c4cf1b" +checksum = "defaa24ecc093c77630e6c15e17c51f5e187bf35ee514f4e2d67baaa96dae22b" dependencies = [ "ciborium-io", "half", @@ -1031,7 +1162,7 @@ version = "0.2.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "12f8e7987cbd042a63249497f41aed09f8e65add917ea6566effbc56578d6801" dependencies = [ - "generic-array 0.14.6", + "generic-array 0.14.7", ] [[package]] @@ -1040,14 +1171,14 @@ version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7ee52072ec15386f770805afd189a01c8841be8696bed250fa2f13c4c0d6dfb7" dependencies = [ - "generic-array 0.14.6", + "generic-array 0.14.7", ] [[package]] name = "cipher" -version = "0.4.3" +version = "0.4.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d1873270f8f7942c191139cb8a40fd228da6c3fd2fc376d7e92d47aa14aeb59e" +checksum = "773f3b9af64447d2ce9850330c473515014aa235e6a783b02db81ff39e4a3dad" dependencies = [ "crypto-common", "inout", @@ -1064,9 +1195,9 @@ dependencies = [ [[package]] name = "clang-sys" -version = "1.6.0" +version = "1.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "77ed9a53e5d4d9c573ae844bfac6872b159cb1d1585a83b29e7a64b7eef7332a" +checksum = "c688fc74432808e3eb684cae8830a86be1d66a2bd58e1f248ed0960a590baf6f" dependencies = [ "glob", "libc", @@ -1075,9 +1206,9 @@ dependencies = [ [[package]] name = "clap" -version = "3.2.23" +version = "3.2.25" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "71655c45cb9845d3270c9d6df84ebe72b4dad3c2ba3f7023ad47c144e4e473a5" +checksum = "4ea181bf566f71cb9a5d17a59e1871af638180a18fb0035c92ae62b705207123" dependencies = [ "bitflags", "clap_lex 0.2.4", @@ -1087,9 +1218,9 @@ dependencies = [ [[package]] name = "clap" -version = "4.2.5" +version = "4.2.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8a1f23fa97e1d1641371b51f35535cb26959b8e27ab50d167a8b996b5bada819" +checksum = "34d21f9bf1b425d2968943631ec91202fe5e837264063503708b83013f8fc938" dependencies = [ "clap_builder", "clap_derive", @@ -1098,12 +1229,12 @@ dependencies = [ [[package]] name = "clap_builder" -version = "4.2.5" +version = "4.2.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0fdc5d93c358224b4d6867ef1356d740de2303e9892edc06c5340daeccd96bab" +checksum = "914c8c79fb560f238ef6429439a30023c862f7a28e688c58f7203f12b29970bd" dependencies = [ "anstream", - "anstyle 1.0.0", + "anstyle", "bitflags", "clap_lex 0.4.1", "strsim", @@ -1111,11 +1242,11 @@ dependencies = [ [[package]] name = "clap_complete" -version = "4.1.4" +version = "4.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "501ff0a401473ea1d4c3b125ff95506b62c5bc5768d818634195fbb7c4ad5ff4" +checksum = "1a19591b2ab0e3c04b588a0e04ddde7b9eaa423646d1b4a8092879216bf47473" dependencies = [ - "clap 4.2.5", + "clap 4.2.7", ] [[package]] @@ -1127,7 +1258,7 @@ dependencies = [ "heck", "proc-macro2", "quote", - "syn 2.0.14", + "syn 2.0.15", ] [[package]] @@ -1174,9 +1305,9 @@ dependencies = [ [[package]] name = "concurrent-queue" -version = "2.1.0" +version = "2.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c278839b831783b70278b14df4d45e1beb1aad306c07bb796637de9a0e323e8e" +checksum = "62ec6771ecfa0762d24683ee5a32ad78487a3d3afdc0fb8cae19d2c5deb50b7c" dependencies = [ "crossbeam-utils", ] @@ -1202,9 +1333,15 @@ checksum = "520fbf3c07483f94e3e3ca9d0cfd913d7718ef2483d2cfd91c0d9e91474ab913" [[package]] name = "constant_time_eq" -version = "0.2.4" +version = "0.2.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f3ad85c1f65dc7b37604eb0e89748faf0b9653065f2a8ef69f96a687ec1e9279" +checksum = "13418e745008f7349ec7e449155f419a61b92b58a99cc3616942b926825ec76b" + +[[package]] +name = "constcat" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f272d0c4cf831b4fa80ee529c7707f76585986e910e1fbce1d7921970bc1a241" [[package]] name = "core-foundation" @@ -1218,9 +1355,9 @@ dependencies = [ [[package]] name = "core-foundation-sys" -version = "0.8.3" +version = "0.8.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5827cebf4670468b8772dd191856768aedcb1b0278a04f989f7766351917b9dc" +checksum = "e496a50fda8aacccc86d7529e2c1e0892dbd0f898a6b5645b5561b89c3210efa" [[package]] name = "core2" @@ -1242,9 +1379,9 @@ dependencies = [ [[package]] name = "cpufeatures" -version = "0.2.5" +version = "0.2.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "28d997bd5e24a5928dd43e46dc529867e207907fe0b239c3477d924f7f2ca320" +checksum = "3e4c1eaa2012c47becbbad2ab175484c2a84d1185b566fb2cc5b8707343dfe58" dependencies = [ "libc", ] @@ -1382,7 +1519,7 @@ dependencies = [ "atty", "cast", "ciborium", - "clap 3.2.23", + "clap 3.2.25", "criterion-plot", "futures", "itertools", @@ -1412,9 +1549,9 @@ dependencies = [ [[package]] name = "crossbeam-channel" -version = "0.5.7" +version = "0.5.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cf2b3e8478797446514c91ef04bafcb59faba183e621ad488df88983cc14128c" +checksum = "a33c2bf77f2df06183c3aa30d1e96c0695a313d4f9c453cc3762a6db39f99200" dependencies = [ "cfg-if", "crossbeam-utils", @@ -1465,7 +1602,7 @@ version = "0.4.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ef2b4b23cddf68b89b8f8069890e8c270d54e2d5fe1b143820234805e4cb17ef" dependencies = [ - "generic-array 0.14.6", + "generic-array 0.14.7", "rand_core 0.6.4", "subtle", "zeroize", @@ -1473,11 +1610,11 @@ dependencies = [ [[package]] name = "crypto-bigint" -version = "0.5.1" +version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7c2538c4e68e52548bacb3e83ac549f903d44f011ac9d5abb5e132e67d0808f7" +checksum = "cf4c2f4e1afd912bc40bfd6fed5d9dc1f288e0ba01bfcc835cc5bc3eb13efe15" dependencies = [ - "generic-array 0.14.6", + "generic-array 0.14.7", "rand_core 0.6.4", "subtle", "zeroize", @@ -1489,7 +1626,7 @@ version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3" dependencies = [ - "generic-array 0.14.6", + "generic-array 0.14.7", "rand_core 0.6.4", "typenum", ] @@ -1500,7 +1637,7 @@ version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b584a330336237c1eecd3e94266efb216c56ed91225d634cb2991c5f3fd1aeab" dependencies = [ - "generic-array 0.14.6", + "generic-array 0.14.7", "subtle", ] @@ -1510,7 +1647,7 @@ version = "0.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b1d1a86f49236c215f271d40892d5fc950490551400b02ef360692c29815c714" dependencies = [ - "generic-array 0.14.6", + "generic-array 0.14.7", "subtle", ] @@ -1539,7 +1676,7 @@ version = "0.9.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0369ee1ad671834580515889b80f2ea915f23b8be8d0daa4bbaf2ac5c7590835" dependencies = [ - "cipher 0.4.3", + "cipher 0.4.4", ] [[package]] @@ -1584,9 +1721,9 @@ dependencies = [ [[package]] name = "cxx" -version = "1.0.91" +version = "1.0.94" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "86d3488e7665a7a483b57e25bdd90d0aeb2bc7608c8d0346acf2ad3f1caf1d62" +checksum = "f61f1b6389c3fe1c316bf8a4dccc90a38208354b330925bce1f74a6c4756eb93" dependencies = [ "cc", "cxxbridge-flags", @@ -1596,9 +1733,9 @@ dependencies = [ [[package]] name = "cxx-build" -version = "1.0.91" +version = "1.0.94" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "48fcaf066a053a41a81dfb14d57d99738b767febb8b735c3016e469fac5da690" +checksum = "12cee708e8962df2aeb38f594aae5d827c022b6460ac71a7a3e2c3c2aae5a07b" dependencies = [ "cc", "codespan-reporting", @@ -1606,31 +1743,31 @@ dependencies = [ "proc-macro2", "quote", "scratch", - "syn 1.0.109", + "syn 2.0.15", ] [[package]] name = "cxxbridge-flags" -version = "1.0.91" +version = "1.0.94" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a2ef98b8b717a829ca5603af80e1f9e2e48013ab227b68ef37872ef84ee479bf" +checksum = "7944172ae7e4068c533afbb984114a56c46e9ccddda550499caa222902c7f7bb" [[package]] name = "cxxbridge-macro" -version = "1.0.91" +version = "1.0.94" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "086c685979a698443656e5cf7856c95c642295a38599f12fb1ff76fb28d19892" +checksum = "2345488264226bf682893e25de0769f3360aac9957980ec49361b083ddaa5bc5" dependencies = [ "proc-macro2", "quote", - "syn 1.0.109", + "syn 2.0.15", ] [[package]] name = "darling" -version = "0.14.3" +version = "0.14.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c0808e1bd8671fb44a113a14e13497557533369847788fa2ae912b6ebfce9fa8" +checksum = "7b750cb3417fd1b327431a470f388520309479ab0bf5e323505daf0290cd3850" dependencies = [ "darling_core", "darling_macro", @@ -1638,9 +1775,9 @@ dependencies = [ [[package]] name = "darling_core" -version = "0.14.3" +version = "0.14.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "001d80444f28e193f30c2f293455da62dcf9a6b29918a4253152ae2b1de592cb" +checksum = "109c1ca6e6b7f82cc233a97004ea8ed7ca123a9af07a8230878fcfda9b158bf0" dependencies = [ "fnv", "ident_case", @@ -1652,9 +1789,9 @@ dependencies = [ [[package]] name = "darling_macro" -version = "0.14.3" +version = "0.14.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b36230598a2d5de7ec1c6f51f72d8a99a9208daff41de2084d06e3fd3ea56685" +checksum = "a4aab4dbc9f7611d8b55048a3a16d2d010c2c8334e46304b40ac1cc14bf3b48e" dependencies = [ "darling_core", "quote", @@ -1700,9 +1837,9 @@ dependencies = [ [[package]] name = "der" -version = "0.7.1" +version = "0.7.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bc906908ea6458456e5eaa160a9c08543ec3d1e6f71e2235cedd660cb65f9df0" +checksum = "05e58dffcdcc8ee7b22f0c1f71a69243d7c2d9ad87b5a14361f2424a1565c219" dependencies = [ "const-oid", "zeroize", @@ -1724,11 +1861,11 @@ dependencies = [ [[package]] name = "der-parser" -version = "8.1.0" +version = "8.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "42d4bc9b0db0a0df9ae64634ac5bdefb7afcb534e182275ca0beadbe486701c1" +checksum = "dbd676fbbab537128ef0278adb5576cf363cff6aa22a7b24effe97347cfab61e" dependencies = [ - "asn1-rs 0.5.1", + "asn1-rs 0.5.2", "displaydoc", "nom", "num-bigint", @@ -1736,6 +1873,17 @@ dependencies = [ "rusticata-macros", ] +[[package]] +name = "derivative" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fcc3dd5e9e9c0b295d6e1e4d811fb6f157d5ffd784b8d202fc62eac8035a770b" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] + [[package]] name = "derive-syn-parse" version = "0.1.5" @@ -1816,7 +1964,7 @@ version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d3dd60d1080a57a05ab032377049e0591415d2b31afd7028356dbf3cc6dcb066" dependencies = [ - "generic-array 0.14.6", + "generic-array 0.14.7", ] [[package]] @@ -1826,6 +1974,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8168378f4e5023e7218c89c891c0fd8ecdb5e5e4f18cb78f38cf245dd021e76f" dependencies = [ "block-buffer 0.10.4", + "const-oid", "crypto-common", "subtle", ] @@ -1873,13 +2022,13 @@ dependencies = [ [[package]] name = "displaydoc" -version = "0.2.3" +version = "0.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3bf95dc3f046b9da4f2d51833c0d3547d8564ef6910f5c1ed130306a75b92886" +checksum = "487585f4d0c6655fe74905e2504d8ad6908e4db67f744eb140876906c2f3175d" dependencies = [ "proc-macro2", "quote", - "syn 1.0.109", + "syn 2.0.15", ] [[package]] @@ -1908,9 +2057,9 @@ checksum = "9ea835d29036a4087793836fa931b08837ad5e957da9e23886b29586fb9b6650" [[package]] name = "dtoa" -version = "1.0.5" +version = "1.0.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c00704156a7de8df8da0911424e30c2049957b0a714542a44e05fe693dd85313" +checksum = "65d09067bfacaa79114679b279d7f5885b53295b1e2cfb4e79c8e4bd3d633169" [[package]] name = "dyn-clonable" @@ -1953,14 +2102,15 @@ dependencies = [ [[package]] name = "ecdsa" -version = "0.16.1" +version = "0.16.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d1b0a1222f8072619e8a6b667a854020a03d363738303203c09468b3424a420a" +checksum = "a48e5d537b8a30c0b023116d981b16334be1485af7ca68db3a2b7024cbc957fd" dependencies = [ - "der 0.7.1", - "elliptic-curve 0.13.2", + "der 0.7.5", + "digest 0.10.6", + "elliptic-curve 0.13.4", "rfc6979 0.4.0", - "signature 2.0.0", + "signature 2.1.0", ] [[package]] @@ -2017,7 +2167,7 @@ dependencies = [ "der 0.6.1", "digest 0.10.6", "ff 0.12.1", - "generic-array 0.14.6", + "generic-array 0.14.7", "group 0.12.1", "hkdf", "pem-rfc7468", @@ -2030,19 +2180,19 @@ dependencies = [ [[package]] name = "elliptic-curve" -version = "0.13.2" +version = "0.13.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6ea5a92946e8614bb585254898bb7dd1ddad241ace60c52149e3765e34cc039d" +checksum = "75c71eaa367f2e5d556414a8eea812bc62985c879748d6403edabd9cb03f16e7" dependencies = [ "base16ct 0.2.0", - "crypto-bigint 0.5.1", + "crypto-bigint 0.5.2", "digest 0.10.6", "ff 0.13.0", - "generic-array 0.14.6", + "generic-array 0.14.7", "group 0.13.0", - "pkcs8 0.10.1", + "pkcs8 0.10.2", "rand_core 0.6.4", - "sec1 0.7.1", + "sec1 0.7.2", "subtle", "zeroize", ] @@ -2082,7 +2232,7 @@ checksum = "5e9a1f9f7d83e59740248a6e14ecf93929ade55027844dfcea78beafccc15745" dependencies = [ "proc-macro2", "quote", - "syn 2.0.14", + "syn 2.0.15", ] [[package]] @@ -2119,24 +2269,13 @@ checksum = "e48c92028aaa870e83d51c64e5d4e0b6981b360c522198c23959f219a4e1b15b" [[package]] name = "errno" -version = "0.2.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f639046355ee4f37944e44f60642c6f3a7efa3cf6b78c78a0d989a8ce6c396a1" -dependencies = [ - "errno-dragonfly", - "libc", - "winapi", -] - -[[package]] -name = "errno" -version = "0.3.0" +version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "50d6a0976c999d473fe89ad888d5a284e55366d9dc9038b1ba2aa15128c4afa0" +checksum = "4bcfec3a70f97c962c307b2d2c56e358cf1d00b558d74262b5f929ee8cc7e73a" dependencies = [ "errno-dragonfly", "libc", - "windows-sys 0.45.0", + "windows-sys 0.48.0", ] [[package]] @@ -2229,9 +2368,9 @@ dependencies = [ [[package]] name = "fiat-crypto" -version = "0.1.17" +version = "0.1.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a214f5bb88731d436478f3ae1f8a277b62124089ba9fb67f4f93fb100ef73c90" +checksum = "e825f6987101665dea6ec934c09ec6d721de7bc1bf92248e1d5810c8cd636b77" [[package]] name = "file-per-thread-logger" @@ -2245,14 +2384,14 @@ dependencies = [ [[package]] name = "filetime" -version = "0.2.20" +version = "0.2.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8a3de6e8d11b22ff9edc6d916f890800597d60f8b2da1caf2955c274638d6412" +checksum = "5cbc844cecaee9d4443931972e1289c8ff485cb4cc2767cb03ca139ed6885153" dependencies = [ "cfg-if", "libc", "redox_syscall 0.2.16", - "windows-sys 0.45.0", + "windows-sys 0.48.0", ] [[package]] @@ -2292,13 +2431,13 @@ checksum = "0ce7134b9999ecaf8bcd65542e436736ef32ddca1b3e06094cb6ec5755203b80" [[package]] name = "flate2" -version = "1.0.25" +version = "1.0.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a8a2db397cb1c8772f31494cb8917e48cd1e64f0fa7efac59fbd741a0a8ce841" +checksum = "3b9429470923de8e8cbd4d2dc513535400b4b3fef0319fb5c4e1f520a7bef743" dependencies = [ "crc32fast", "libz-sys", - "miniz_oxide", + "miniz_oxide 0.7.1", ] [[package]] @@ -2382,7 +2521,7 @@ dependencies = [ "Inflector", "array-bytes 4.2.0", "chrono", - "clap 4.2.5", + "clap 4.2.7", "comfy-table", "frame-benchmarking", "frame-support", @@ -2447,7 +2586,7 @@ dependencies = [ "quote", "scale-info", "sp-arithmetic", - "syn 2.0.14", + "syn 2.0.15", "trybuild", ] @@ -2473,7 +2612,7 @@ dependencies = [ name = "frame-election-solution-type-fuzzer" version = "2.0.0-alpha.5" dependencies = [ - "clap 4.2.5", + "clap 4.2.7", "frame-election-provider-solution-type", "frame-election-provider-support", "frame-support", @@ -2538,7 +2677,7 @@ dependencies = [ "spinners", "substrate-rpc-client", "tokio", - "tracing-subscriber 0.3.16", + "tracing-subscriber 0.3.17", ] [[package]] @@ -2589,7 +2728,7 @@ dependencies = [ "proc-macro-warning", "proc-macro2", "quote", - "syn 2.0.14", + "syn 2.0.15", ] [[package]] @@ -2600,7 +2739,7 @@ dependencies = [ "proc-macro-crate", "proc-macro2", "quote", - "syn 2.0.14", + "syn 2.0.15", ] [[package]] @@ -2609,7 +2748,7 @@ version = "3.0.0" dependencies = [ "proc-macro2", "quote", - "syn 2.0.14", + "syn 2.0.15", ] [[package]] @@ -2732,13 +2871,12 @@ dependencies = [ [[package]] name = "fs4" -version = "0.6.3" +version = "0.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8ea55201cc351fdb478217c0fb641b59813da9b4efe4c414a9d8f989a657d149" +checksum = "a7f5b6908aecca5812a4569056285e58c666588c9573ee59765bf1d3692699e2" dependencies = [ - "libc", - "rustix 0.35.13", - "winapi", + "rustix 0.37.19", + "windows-sys 0.48.0", ] [[package]] @@ -2804,9 +2942,9 @@ checksum = "4fff74096e71ed47f8e023204cfd0aa1289cd54ae5430a9523be060cdb849964" [[package]] name = "futures-lite" -version = "1.12.0" +version = "1.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7694489acd39452c77daa48516b894c153f192c3578d5a839b62c58099fcbf48" +checksum = "49a9d51ce47660b1e808d3c990b4709f2f415d928835a17dfd16991515c46bce" dependencies = [ "fastrand", "futures-core", @@ -2825,7 +2963,7 @@ checksum = "89ca545a94061b6365f2c7355b4b32bd20df3ff95f02da9329b34ccc3bd6ee72" dependencies = [ "proc-macro2", "quote", - "syn 2.0.14", + "syn 2.0.15", ] [[package]] @@ -2908,9 +3046,9 @@ dependencies = [ [[package]] name = "generic-array" -version = "0.14.6" +version = "0.14.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bff49e947297f3312447abdca79f45f4738097cc82b06e72054d2223f601f1b9" +checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a" dependencies = [ "typenum", "version_check", @@ -2940,9 +3078,9 @@ dependencies = [ [[package]] name = "getrandom" -version = "0.2.8" +version = "0.2.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c05aeb6a22b8f62540c194aac980f2115af067bfe15a0734d7277a768d396b31" +checksum = "c85e1d9ab2eadba7e5040d4e09cbd6d072b76a557ad64e797c2cb9d4da21d7e4" dependencies = [ "cfg-if", "libc", @@ -3011,7 +3149,7 @@ version = "0.4.10" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "029d74589adefde59de1a0c4f4732695c32805624aec7b68d91503d4dba79afc" dependencies = [ - "aho-corasick", + "aho-corasick 0.7.20", "bstr", "fnv", "log", @@ -3042,9 +3180,9 @@ dependencies = [ [[package]] name = "h2" -version = "0.3.17" +version = "0.3.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "66b91535aa35fea1523ad1b86cb6b53c28e0ae566ba4a460f4457e936cad7c6f" +checksum = "17f8a914c2987b688368b5138aa05321db91f4090cf26118185672ad588bce21" dependencies = [ "bytes", "fnv", @@ -3148,6 +3286,12 @@ version = "0.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" +[[package]] +name = "hex-literal" +version = "0.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7ebdb29d2ea9ed0083cd8cece49bbd968021bd99b0849edb4a9a7ee0fdf6a4e0" + [[package]] name = "hkdf" version = "0.12.3" @@ -3193,7 +3337,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "17ea0a1394df5b6574da6e0c1ade9e78868c9fb0a4e5ef4428e32da4676b85b1" dependencies = [ "digest 0.9.0", - "generic-array 0.14.6", + "generic-array 0.14.7", "hmac 0.8.1", ] @@ -3268,9 +3412,9 @@ checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4" [[package]] name = "hyper" -version = "0.14.24" +version = "0.14.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5e011372fa0b68db8350aa7a248930ecc7839bf46d8485577d69f117a75f164c" +checksum = "ab302d72a6f11a3b910431ff93aae7e773078c769f0a3ef15fb9ec692ed147d4" dependencies = [ "bytes", "futures-channel", @@ -3308,16 +3452,16 @@ dependencies = [ [[package]] name = "iana-time-zone" -version = "0.1.53" +version = "0.1.56" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "64c122667b287044802d6ce17ee2ddf13207ed924c712de9a66a5814d5b64765" +checksum = "0722cd7114b7de04316e7ea5456a0bbb20e4adb46fd27a3697adb812cff0f37c" dependencies = [ "android_system_properties", "core-foundation-sys", "iana-time-zone-haiku", "js-sys", "wasm-bindgen", - "winapi", + "windows 0.48.0", ] [[package]] @@ -3369,9 +3513,9 @@ dependencies = [ [[package]] name = "if-watch" -version = "3.0.0" +version = "3.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ba7abdbb86e485125dad06c2691e1e393bf3b08c7b743b43aa162a00fd39062e" +checksum = "a9465340214b296cd17a0009acdb890d6160010b8adf8f78a00d0d7ab270f79f" dependencies = [ "async-io", "core-foundation", @@ -3383,7 +3527,7 @@ dependencies = [ "rtnetlink", "system-configuration", "tokio", - "windows", + "windows 0.34.0", ] [[package]] @@ -3417,9 +3561,9 @@ dependencies = [ [[package]] name = "indexmap" -version = "1.9.2" +version = "1.9.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1885e79c1fc4b10f0e172c475f458b7f7b93061064d98c3293e98c5ba0c8b399" +checksum = "bd070e393353796e801d209ad339e89596eb4c8d430d18ede6a1cced8fafbd99" dependencies = [ "autocfg", "hashbrown 0.12.3", @@ -3440,7 +3584,7 @@ checksum = "cef509aa9bc73864d6756f0d34d35504af3cf0844373afe9b8669a5b8005a729" dependencies = [ "console", "number_prefix", - "portable-atomic", + "portable-atomic 0.3.20", "unicode-width", ] @@ -3450,7 +3594,7 @@ version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a0c10553d664a4d0bcff9f4215d0aac67a639cc68ef660840afe309b807bc9f5" dependencies = [ - "generic-array 0.14.6", + "generic-array 0.14.7", ] [[package]] @@ -3492,18 +3636,13 @@ dependencies = [ [[package]] name = "io-lifetimes" -version = "0.7.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "59ce5ef949d49ee85593fc4d3f3f95ad61657076395cbbce23e2121fc5542074" - -[[package]] -name = "io-lifetimes" -version = "1.0.5" +version = "1.0.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1abeb7a0dd0f8181267ff8adc397075586500b81b28a73e8a0208b00fc170fb3" +checksum = "9c66c74d2ae7e79a5a8f7ac924adbe38ee42a859c6539ad869eb51f0b52dc220" dependencies = [ + "hermit-abi 0.3.1", "libc", - "windows-sys 0.45.0", + "windows-sys 0.48.0", ] [[package]] @@ -3526,20 +3665,20 @@ dependencies = [ [[package]] name = "ipnet" -version = "2.7.1" +version = "2.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "30e22bd8629359895450b59ea7a776c850561b96a3b1d31321c1949d9e6c9146" +checksum = "12b6ee2129af8d4fb011108c73d99a1b83a85977f23b82460c0ae2e25bb4b57f" [[package]] name = "is-terminal" -version = "0.4.4" +version = "0.4.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "21b6b32576413a8e69b90e952e4a026476040d81017b80445deda5f2d3921857" +checksum = "adcf93614601c8129ddf72e2d5633df827ba6551541c6d8c59520a371475be1f" dependencies = [ "hermit-abi 0.3.1", - "io-lifetimes 1.0.5", - "rustix 0.36.8", - "windows-sys 0.45.0", + "io-lifetimes", + "rustix 0.37.19", + "windows-sys 0.48.0", ] [[package]] @@ -3553,9 +3692,9 @@ dependencies = [ [[package]] name = "itoa" -version = "1.0.5" +version = "1.0.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fad582f4b9e86b6caa621cabeb0963332d92eea04729ab12892c2533951e6440" +checksum = "453ad9f582a441959e5f0d088b02ce04cfe8d51a8eaf077f12ac6d3e94164ca6" [[package]] name = "jobserver" @@ -3568,9 +3707,9 @@ dependencies = [ [[package]] name = "js-sys" -version = "0.3.61" +version = "0.3.62" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "445dde2150c55e483f3d8416706b97ec8e8237c307e5b7b4b8dd15e6af2a0730" +checksum = "68c16e1bfd491478ab155fd8b4896b86f9ede344949b641e61501e07c2b8b4d5" dependencies = [ "wasm-bindgen", ] @@ -3721,22 +3860,22 @@ dependencies = [ [[package]] name = "k256" -version = "0.13.0" +version = "0.13.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "955890845095ccf31ef83ad41a05aabb4d8cc23dc3cac5a9f5c89cf26dd0da75" +checksum = "cadb76004ed8e97623117f3df85b17aaa6626ab0b0831e6573f104df16cd1bcc" dependencies = [ "cfg-if", - "ecdsa 0.16.1", - "elliptic-curve 0.13.2", + "ecdsa 0.16.6", + "elliptic-curve 0.13.4", "once_cell", "sha2 0.10.6", ] [[package]] name = "keccak" -version = "0.1.3" +version = "0.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3afef3b6eff9ce9d8ff9b3601125eec7f0c8cbac7abd14f355d053fa56c98768" +checksum = "8f6d5ed8676d904364de097082f4e7d240b571b67989ced0240f08b7f966f940" dependencies = [ "cpufeatures", ] @@ -3910,9 +4049,9 @@ checksum = "884e2677b40cc8c339eaefcb701c32ef1fd2493d71118dc0ca4b6a736c93bd67" [[package]] name = "libc" -version = "0.2.140" +version = "0.2.144" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "99227334921fae1a979cf0bfdfcc6b3e5ce376ef57e16fb6fb3ea2ed6095f80c" +checksum = "2b00cc1c228a6782d0f076e7b232802e0c5689d41bb5df366f2a6b6621cfdfe1" [[package]] name = "libgit2-sys" @@ -3950,14 +4089,14 @@ checksum = "348108ab3fba42ec82ff6e9564fc4ca0247bdccdc68dd8af9764bbc79c3c8ffb" [[package]] name = "libp2p" -version = "0.50.0" +version = "0.50.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2e0a0d2f693675f49ded13c5d510c48b78069e23cbd9108d7ccd59f6dc568819" +checksum = "9c7b0104790be871edcf97db9bd2356604984e623a08d825c3f27852290266b8" dependencies = [ "bytes", "futures", "futures-timer", - "getrandom 0.2.8", + "getrandom 0.2.9", "instant", "libp2p-core 0.38.0", "libp2p-dns", @@ -3966,7 +4105,7 @@ dependencies = [ "libp2p-mdns", "libp2p-metrics", "libp2p-mplex", - "libp2p-noise 0.41.0", + "libp2p-noise", "libp2p-ping", "libp2p-quic", "libp2p-request-response", @@ -4018,36 +4157,30 @@ dependencies = [ [[package]] name = "libp2p-core" -version = "0.39.0" +version = "0.39.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "881d9a54e97d97cdaa4125d48269d97ca8c40e5fefec6b85b30440dc60cc551f" +checksum = "3c1df63c0b582aa434fb09b2d86897fa2b419ffeccf934b36f87fcedc8e835c2" dependencies = [ - "asn1_der", - "bs58", - "ed25519-dalek", "either", "fnv", "futures", "futures-timer", "instant", + "libp2p-identity", "log", - "multiaddr 0.17.0", + "multiaddr 0.17.1", "multihash 0.17.0", "multistream-select", "once_cell", "parking_lot 0.12.1", "pin-project", - "prost", - "prost-build", + "quick-protobuf", "rand 0.8.5", "rw-stream-sink", - "sec1 0.3.0", - "sha2 0.10.6", "smallvec", "thiserror", "unsigned-varint", "void", - "zeroize", ] [[package]] @@ -4085,6 +4218,24 @@ dependencies = [ "void", ] +[[package]] +name = "libp2p-identity" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9e2d584751cecb2aabaa56106be6be91338a60a0f4e420cf2af639204f596fc1" +dependencies = [ + "bs58", + "ed25519-dalek", + "log", + "multiaddr 0.17.1", + "multihash 0.17.0", + "quick-protobuf", + "rand 0.8.5", + "sha2 0.10.6", + "thiserror", + "zeroize", +] + [[package]] name = "libp2p-kad" version = "0.42.1" @@ -4188,29 +4339,6 @@ dependencies = [ "zeroize", ] -[[package]] -name = "libp2p-noise" -version = "0.42.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1216f9ec823ac7a2289b954674c54cbce81c9e45920b4fcf173018ede4295246" -dependencies = [ - "bytes", - "curve25519-dalek 3.2.0", - "futures", - "libp2p-core 0.39.0", - "log", - "once_cell", - "prost", - "prost-build", - "rand 0.8.5", - "sha2 0.10.6", - "snow", - "static_assertions", - "thiserror", - "x25519-dalek 1.1.1", - "zeroize", -] - [[package]] name = "libp2p-ping" version = "0.41.0" @@ -4229,15 +4357,15 @@ dependencies = [ [[package]] name = "libp2p-quic" -version = "0.7.0-alpha.2" +version = "0.7.0-alpha" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5971f629ff7519f4d4889a7c981f0dc09c6ad493423cd8a13ee442de241bc8c8" +checksum = "01e7c867e95c8130667b24409d236d37598270e6da69b3baf54213ba31ffca59" dependencies = [ "bytes", "futures", "futures-timer", "if-watch", - "libp2p-core 0.39.0", + "libp2p-core 0.38.0", "libp2p-tls", "log", "parking_lot 0.12.1", @@ -4317,13 +4445,14 @@ dependencies = [ [[package]] name = "libp2p-tls" -version = "0.1.0-alpha.2" +version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e9baf6f6292149e124ee737d9a79dbee783f29473fc368c7faad9d157841078a" +checksum = "ff08d13d0dc66e5e9ba6279c1de417b84fa0d0adc3b03e5732928c180ec02781" dependencies = [ "futures", "futures-rustls", - "libp2p-core 0.39.0", + "libp2p-core 0.39.2", + "libp2p-identity", "rcgen 0.10.0", "ring", "rustls 0.20.8", @@ -4349,9 +4478,9 @@ dependencies = [ [[package]] name = "libp2p-webrtc" -version = "0.4.0-alpha.2" +version = "0.4.0-alpha" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "db4401ec550d36f413310ba5d4bf564bb21f89fb1601cadb32b2300f8bc1eb5b" +checksum = "cdb6cd86dd68cba72308ea05de1cebf3ba0ae6e187c40548167955d4e3970f6a" dependencies = [ "async-trait", "asynchronous-codec", @@ -4360,10 +4489,10 @@ dependencies = [ "futures-timer", "hex", "if-watch", - "libp2p-core 0.39.0", - "libp2p-noise 0.42.0", + "libp2p-core 0.38.0", + "libp2p-noise", "log", - "multihash 0.17.0", + "multihash 0.16.3", "prost", "prost-build", "prost-codec", @@ -4476,9 +4605,9 @@ dependencies = [ [[package]] name = "libz-sys" -version = "1.1.8" +version = "1.1.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9702761c3935f8cc2f101793272e202c72b99da8f4224a19ddcf1279a6450bbf" +checksum = "56ee889ecc9568871456d42f603d6a0ce59ff328d291063a45cbdf0036baf6db" dependencies = [ "cc", "libc", @@ -4519,12 +4648,6 @@ dependencies = [ "nalgebra", ] -[[package]] -name = "linux-raw-sys" -version = "0.0.46" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d4d2456c373231a208ad294c33dc5bff30051eafd954cd4caae83a712b12854d" - [[package]] name = "linux-raw-sys" version = "0.1.4" @@ -4533,9 +4656,9 @@ checksum = "f051f77a7c8e6957c0696eac88f26b0117e54f52d3fc682ab19397a8812846a4" [[package]] name = "linux-raw-sys" -version = "0.3.1" +version = "0.3.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d59d8c75012853d2e872fb56bc8a2e53718e2cafe1a4c823143141c6d90c322f" +checksum = "ece97ea872ece730aed82664c424eb4c8291e1ff2480247ccf7409044bc6479f" [[package]] name = "lite-json" @@ -4659,10 +4782,11 @@ checksum = "2532096657941c2fea9c289d370a250971c689d4f143798ff67113ec042024a5" [[package]] name = "matrixmultiply" -version = "0.3.2" +version = "0.3.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "add85d4dd35074e6fedc608f8c8f513a3548619a9024b751949ef0e8e45a4d84" +checksum = "090126dc04f95dc0d1c1c91f61bdd474b3930ca064c1edc8a849da2c6cbe1e77" dependencies = [ + "autocfg", "rawpointer", ] @@ -4683,11 +4807,11 @@ checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d" [[package]] name = "memfd" -version = "0.6.2" +version = "0.6.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b20a59d985586e4a5aef64564ac77299f8586d8be6cf9106a5a40207e8908efb" +checksum = "ffc89ccdc6e10d6907450f753537ebc5c5d3460d2e4e62ea74bd571db62c0f9e" dependencies = [ - "rustix 0.36.8", + "rustix 0.37.19", ] [[package]] @@ -4768,6 +4892,15 @@ dependencies = [ "adler", ] +[[package]] +name = "miniz_oxide" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e7810e0be55b428ada41041c41f32c9f1a42817901b4ccf45fa3d4b6561e74c7" +dependencies = [ + "adler", +] + [[package]] name = "mio" version = "0.8.6" @@ -4821,9 +4954,9 @@ dependencies = [ [[package]] name = "mockall" -version = "0.11.3" +version = "0.11.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "50e4a1c770583dac7ab5e2f6c139153b783a53a1bbee9729613f193e59828326" +checksum = "4c84490118f2ee2d74570d114f3d0493cbf02790df303d2707606c3e14e07c96" dependencies = [ "cfg-if", "downcast", @@ -4836,9 +4969,9 @@ dependencies = [ [[package]] name = "mockall_derive" -version = "0.11.3" +version = "0.11.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "832663583d5fa284ca8810bf7015e46c9fff9622d3cf34bd1eea5003fec06dd0" +checksum = "22ce75669015c4f47b289fd4d4f56e894e4c96003ffdf3ac51313126f94c6cbb" dependencies = [ "cfg-if", "proc-macro2", @@ -4866,13 +4999,14 @@ dependencies = [ [[package]] name = "multiaddr" -version = "0.17.0" +version = "0.17.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3b53e0cc5907a5c216ba6584bf74be8ab47d6d6289f72793b2dddbf15dc3bf8c" +checksum = "2b36f567c7099511fa8612bbbb52dda2419ce0bdbacf31714e3a5ffdb766d3bd" dependencies = [ "arrayref", "byteorder", "data-encoding", + "log", "multibase", "multihash 0.17.0", "percent-encoding", @@ -4917,9 +5051,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "835d6ff01d610179fbce3de1694d007e500bf33a7f29689838941d6bf783ae40" dependencies = [ "core2", - "digest 0.10.6", "multihash-derive", - "sha2 0.10.6", "unsigned-varint", ] @@ -4959,9 +5091,9 @@ dependencies = [ [[package]] name = "nalgebra" -version = "0.32.1" +version = "0.32.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f6515c882ebfddccaa73ead7320ca28036c4bc84c9bcca3cc0cbba8efe89223a" +checksum = "d68d47bba83f9e2006d117a9a33af1524e655516b8919caac694427a6fb1e511" dependencies = [ "approx", "matrixmultiply", @@ -5048,9 +5180,9 @@ dependencies = [ [[package]] name = "netlink-sys" -version = "0.8.4" +version = "0.8.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "260e21fbb6f3d253a14df90eb0000a6066780a15dd901a7519ce02d77a94985b" +checksum = "6471bf08e7ac0135876a9581bf3217ef0333c191c128d34878079f42ee150411" dependencies = [ "bytes", "futures", @@ -5090,7 +5222,7 @@ name = "node-bench" version = "0.9.0-dev" dependencies = [ "array-bytes 4.2.0", - "clap 4.2.5", + "clap 4.2.7", "derive_more", "fs_extra", "futures", @@ -5127,7 +5259,7 @@ version = "3.0.0-dev" dependencies = [ "array-bytes 4.2.0", "assert_cmd", - "clap 4.2.5", + "clap 4.2.7", "clap_complete", "criterion", "frame-benchmarking-cli", @@ -5252,7 +5384,7 @@ dependencies = [ name = "node-inspect" version = "0.9.0-dev" dependencies = [ - "clap 4.2.5", + "clap 4.2.7", "parity-scale-codec", "sc-cli", "sc-client-api", @@ -5311,7 +5443,7 @@ dependencies = [ name = "node-runtime-generate-bags" version = "3.0.0" dependencies = [ - "clap 4.2.5", + "clap 4.2.7", "generate-bags", "kitchensink-runtime", ] @@ -5320,7 +5452,7 @@ dependencies = [ name = "node-template" version = "4.0.0-dev" dependencies = [ - "clap 4.2.5", + "clap 4.2.7", "frame-benchmarking", "frame-benchmarking-cli", "frame-system", @@ -5603,7 +5735,7 @@ version = "0.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9bedf36ffb6ba96c2eb7144ef6270557b52e54b20c0a8e1eb2ff99a6c6959bff" dependencies = [ - "asn1-rs 0.5.1", + "asn1-rs 0.5.2", ] [[package]] @@ -5638,9 +5770,9 @@ checksum = "ff011a302c396a5197692431fc1948019154afc178baf7d8e37367442a4601cf" [[package]] name = "os_str_bytes" -version = "6.4.1" +version = "6.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9b7820b9daea5457c9f21c69448905d723fbd21136ccf521748f23fd49e723ee" +checksum = "ceedf44fb00f2d1984b0bc98102627ce622e083e49a5bacdb3e514fa4238e267" [[package]] name = "output_vt100" @@ -6082,7 +6214,7 @@ version = "4.0.0-dev" dependencies = [ "proc-macro2", "quote", - "syn 2.0.14", + "syn 2.0.15", ] [[package]] @@ -7006,7 +7138,7 @@ dependencies = [ "proc-macro2", "quote", "sp-runtime", - "syn 2.0.14", + "syn 2.0.15", ] [[package]] @@ -7290,9 +7422,9 @@ dependencies = [ [[package]] name = "parity-db" -version = "0.4.6" +version = "0.4.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "00bfb81cf5c90a222db2fb7b3a7cbf8cc7f38dfb6647aca4d98edf8281f56ed5" +checksum = "bd4572a52711e2ccff02b4973ec7e4a5b5c23387ebbfbd6cd42b34755714cefc" dependencies = [ "blake2", "crc32fast", @@ -7310,9 +7442,9 @@ dependencies = [ [[package]] name = "parity-scale-codec" -version = "3.4.0" +version = "3.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "637935964ff85a605d114591d4d2c13c5d1ba2806dae97cea6bf180238a749ac" +checksum = "5ddb756ca205bd108aee3c62c6d3c994e1df84a59b9d6d4a5ea42ee1fd5a9a28" dependencies = [ "arrayvec 0.7.2", "bitvec", @@ -7349,9 +7481,9 @@ checksum = "e1ad0aff30c1da14b1254fcb2af73e1fa9a28670e584a626f53a369d0e157304" [[package]] name = "parking" -version = "2.0.0" +version = "2.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "427c3892f9e783d91cc128285287e70a59e206ca452770ece88a76f7a3eddd72" +checksum = "14f2252c834a40ed9bb5422029649578e63aa341ac401f74e719dd1afda8394e" [[package]] name = "parking_lot" @@ -7457,9 +7589,9 @@ checksum = "478c572c3d73181ff3c2539045f6eb99e5491218eae919370993b890cdbdd98e" [[package]] name = "pest" -version = "2.5.5" +version = "2.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "028accff104c4e513bad663bbcd2ad7cfd5304144404c31ed0a77ac103d00660" +checksum = "e68e84bfb01f0507134eac1e9b410a12ba379d064eab48c50ba4ce329a527b70" dependencies = [ "thiserror", "ucd-trie", @@ -7467,9 +7599,9 @@ dependencies = [ [[package]] name = "pest_derive" -version = "2.5.5" +version = "2.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2ac3922aac69a40733080f53c1ce7f91dcf57e1a5f6c52f421fadec7fbdc4b69" +checksum = "6b79d4c71c865a25a4322296122e3924d30bc8ee0834c8bfc8b95f7f054afbfb" dependencies = [ "pest", "pest_generator", @@ -7477,22 +7609,22 @@ dependencies = [ [[package]] name = "pest_generator" -version = "2.5.5" +version = "2.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d06646e185566b5961b4058dd107e0a7f56e77c3f484549fb119867773c0f202" +checksum = "6c435bf1076437b851ebc8edc3a18442796b30f1728ffea6262d59bbe28b077e" dependencies = [ "pest", "pest_meta", "proc-macro2", "quote", - "syn 1.0.109", + "syn 2.0.15", ] [[package]] name = "pest_meta" -version = "2.5.5" +version = "2.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e6f60b2ba541577e2a0c307c8f39d1439108120eb7903adeb6497fa880c59616" +checksum = "745a452f8eb71e39ffd8ee32b3c5f51d03845f99786fa9b68db6ff509c505411" dependencies = [ "once_cell", "pest", @@ -7559,19 +7691,19 @@ dependencies = [ [[package]] name = "pkcs8" -version = "0.10.1" +version = "0.10.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3d2820d87d2b008616e5c27212dd9e0e694fb4c6b522de06094106813328cb49" +checksum = "f950b2377845cebe5cf8b5165cb3cc1a5e0fa5cfa3e1f7f55707d8fd82e0a7b7" dependencies = [ - "der 0.7.1", - "spki 0.7.0", + "der 0.7.5", + "spki 0.7.2", ] [[package]] name = "pkg-config" -version = "0.3.26" +version = "0.3.27" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6ac9a59f73473f1b8d852421e59e64809f025994837ef743615c6d0c5b305160" +checksum = "26072860ba924cbfa98ea39c8c19b4dd6a4a25423dbdf219c1eca91aa0cf6964" [[package]] name = "platforms" @@ -7615,16 +7747,18 @@ dependencies = [ [[package]] name = "polling" -version = "2.5.2" +version = "2.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "22122d5ec4f9fe1b3916419b76be1e80bcb93f618d071d2edf841b137b2a2bd6" +checksum = "4b2d323e8ca7996b3e23126511a523f7e62924d93ecd5ae73b333815b0eb3dce" dependencies = [ "autocfg", + "bitflags", "cfg-if", + "concurrent-queue", "libc", "log", - "wepoll-ffi", - "windows-sys 0.42.0", + "pin-project-lite 0.2.9", + "windows-sys 0.48.0", ] [[package]] @@ -7664,9 +7798,18 @@ dependencies = [ [[package]] name = "portable-atomic" -version = "0.3.19" +version = "0.3.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e30165d31df606f5726b090ec7592c308a0eaf61721ff64c9a3018e344a8753e" +dependencies = [ + "portable-atomic 1.3.1", +] + +[[package]] +name = "portable-atomic" +version = "1.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "26f6a7b87c2e435a3241addceeeff740ff8b7e76b74c13bf9acb17fa454ea00b" +checksum = "1bbda379e6e462c97ea6afe9f6233619b202bbc4968d7caa6917788d2070a044" [[package]] name = "ppv-lite86" @@ -7690,11 +7833,11 @@ dependencies = [ [[package]] name = "predicates" -version = "3.0.2" +version = "3.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c575290b64d24745b6c57a12a31465f0a66f3a4799686a6921526a33b0797965" +checksum = "09963355b9f467184c04017ced4a2ba2d75cbcb4e7462690d388233253d4b1a9" dependencies = [ - "anstyle 0.3.5", + "anstyle", "difflib", "itertools", "predicates-core", @@ -7708,9 +7851,9 @@ checksum = "b794032607612e7abeb4db69adb4e33590fa6cf1149e95fd7cb00e634b92f174" [[package]] name = "predicates-tree" -version = "1.0.7" +version = "1.0.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "54ff541861505aabf6ea722d2131ee980b8276e10a1297b94e896dd8b621850d" +checksum = "368ba315fb8c5052ab692e68a0eefec6ec57b23a36959c14496f0b0df2c0cecf" dependencies = [ "predicates-core", "termtree", @@ -7730,9 +7873,9 @@ dependencies = [ [[package]] name = "prettyplease" -version = "0.1.23" +version = "0.1.25" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e97e3215779627f01ee256d2fad52f3d95e8e1c11e9fc6fd08f7cd455d5d5c78" +checksum = "6c8646e95016a7a6c4adea95bafa8a16baab64b583356217f2c85db4a39d9a86" dependencies = [ "proc-macro2", "syn 1.0.109", @@ -7787,13 +7930,13 @@ dependencies = [ [[package]] name = "proc-macro-warning" -version = "0.3.0" +version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f0e25495609acefcaeb5052edad8ac91017c9bc98fc38ef321ed524e50b68bac" +checksum = "0e99670bafb56b9a106419397343bdbc8b8742c3cc449fec6345f86173f47cd4" dependencies = [ "proc-macro2", "quote", - "syn 1.0.109", + "syn 2.0.15", ] [[package]] @@ -7844,9 +7987,9 @@ dependencies = [ [[package]] name = "prost" -version = "0.11.8" +version = "0.11.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e48e50df39172a3e7eb17e14642445da64996989bc212b583015435d39a58537" +checksum = "0b82eaa1d779e9a4bc1c3217db8ffbeabaae1dca241bf70183242128d48681cd" dependencies = [ "bytes", "prost-derive", @@ -7854,9 +7997,9 @@ dependencies = [ [[package]] name = "prost-build" -version = "0.11.8" +version = "0.11.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2c828f93f5ca4826f97fedcbd3f9a536c16b12cff3dbbb4a007f932bbad95b12" +checksum = "119533552c9a7ffacc21e099c24a0ac8bb19c2a2a3f363de84cd9b844feab270" dependencies = [ "bytes", "heck", @@ -7889,9 +8032,9 @@ dependencies = [ [[package]] name = "prost-derive" -version = "0.11.8" +version = "0.11.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4ea9b0f8cbe5e15a8a042d030bd96668db28ecb567ec37d691971ff5731d2b1b" +checksum = "e5d2d8d10f3c6ded6da8b05b5fb3b8a5082514344d56c9f871412d29b4e075b4" dependencies = [ "anyhow", "itertools", @@ -7902,9 +8045,9 @@ dependencies = [ [[package]] name = "prost-types" -version = "0.11.8" +version = "0.11.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "379119666929a1afd7a043aa6cf96fa67a6dce9af60c88095a4686dbce4c9c88" +checksum = "213622a1460818959ac1181aaeb2dc9c7f63df720db7d788b3e24eacd1983e13" dependencies = [ "prost", ] @@ -7924,6 +8067,15 @@ version = "1.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a1d01941d82fa2ab50be1e79e6714289dd7cde78eba4c074bc5a4374f650dfe0" +[[package]] +name = "quick-protobuf" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9d6da84cc204722a989e01ba2f6e1e276e190f22263d0cb6ce8526fcdb0d2e1f" +dependencies = [ + "byteorder", +] + [[package]] name = "quickcheck" version = "1.0.3" @@ -7946,9 +8098,9 @@ dependencies = [ [[package]] name = "quinn-proto" -version = "0.9.2" +version = "0.9.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "72ef4ced82a24bb281af338b9e8f94429b6eca01b4e66d899f40031f074e74c9" +checksum = "67c10f662eee9c94ddd7135043e544f3c82fa839a1e7b865911331961b53186c" dependencies = [ "bytes", "rand 0.8.5", @@ -7964,9 +8116,9 @@ dependencies = [ [[package]] name = "quote" -version = "1.0.26" +version = "1.0.27" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4424af4bf778aae2051a77b60283332f386554255d722233d09fbfc7e30da2fc" +checksum = "8f4f29d145265ec1c483c7c654450edde0bfe043d3938d6972630663356d9500" dependencies = [ "proc-macro2", ] @@ -8036,7 +8188,7 @@ version = "0.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" dependencies = [ - "getrandom 0.2.8", + "getrandom 0.2.9", ] [[package]] @@ -8075,9 +8227,9 @@ checksum = "60a357793950651c4ed0f3f52338f53b2f809f32d83a07f72909fa13e4c6c1e3" [[package]] name = "rayon" -version = "1.6.1" +version = "1.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6db3a213adf02b3bcfd2d3846bb41cb22857d131789e01df434fb7e7bc0759b7" +checksum = "1d2df5196e37bcc87abebc0053e20787d73847bb33134a69841207dd0a47f03b" dependencies = [ "either", "rayon-core", @@ -8085,9 +8237,9 @@ dependencies = [ [[package]] name = "rayon-core" -version = "1.10.2" +version = "1.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "356a0625f1954f730c0201cdab48611198dc6ce21f4acff55089b5a78e6e835b" +checksum = "4b8f95bd6966f5c87776639160a66bd8ab9895d9d4ab01ddba9fc60661aebe8d" dependencies = [ "crossbeam-channel", "crossbeam-deque", @@ -8103,7 +8255,7 @@ checksum = "6413f3de1edee53342e6138e75b56d32e7bc6e332b3bd62d497b1929d4cfbcdd" dependencies = [ "pem", "ring", - "time 0.3.20", + "time 0.3.21", "x509-parser 0.13.2", "yasna", ] @@ -8116,7 +8268,7 @@ checksum = "ffbe84efe2f38dea12e9bfc1f65377fdf03e53a18cb3b995faedf7934c7e785b" dependencies = [ "pem", "ring", - "time 0.3.20", + "time 0.3.21", "yasna", ] @@ -8144,29 +8296,29 @@ version = "0.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b033d837a7cf162d7993aded9304e30a83213c648b6e389db233191f891e5c2b" dependencies = [ - "getrandom 0.2.8", + "getrandom 0.2.9", "redox_syscall 0.2.16", "thiserror", ] [[package]] name = "ref-cast" -version = "1.0.14" +version = "1.0.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8c78fb8c9293bcd48ef6fce7b4ca950ceaf21210de6e105a883ee280c0f7b9ed" +checksum = "f43faa91b1c8b36841ee70e97188a869d37ae21759da6846d4be66de5bf7b12c" dependencies = [ "ref-cast-impl", ] [[package]] name = "ref-cast-impl" -version = "1.0.14" +version = "1.0.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9f9c0c92af03644e4806106281fe2e068ac5bc0ae74a707266d06ea27bccee5f" +checksum = "8d2275aab483050ab2a7364c1a46604865ee7d6906684e08db0f090acf74f9e7" dependencies = [ "proc-macro2", "quote", - "syn 1.0.109", + "syn 2.0.15", ] [[package]] @@ -8183,13 +8335,13 @@ dependencies = [ [[package]] name = "regex" -version = "1.7.3" +version = "1.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8b1f693b24f6ac912f4893ef08244d70b6067480d2f1a46e950c9691e6749d1d" +checksum = "af83e617f331cc6ae2da5443c602dfa5af81e517212d9d611a5b3ba1777b5370" dependencies = [ - "aho-corasick", + "aho-corasick 1.0.1", "memchr", - "regex-syntax", + "regex-syntax 0.7.1", ] [[package]] @@ -8198,7 +8350,7 @@ version = "0.1.10" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6c230d73fb8d8c1b9c0b3135c5142a8acee3a0558fb8db5cf1cb65f8d7862132" dependencies = [ - "regex-syntax", + "regex-syntax 0.6.29", ] [[package]] @@ -8207,6 +8359,12 @@ version = "0.6.29" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f162c6dd7b008981e4d40210aca20b4bd0f9b60ca9271061b07f78537722f2e1" +[[package]] +name = "regex-syntax" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a5996294f19bd3aae0453a862ad728f60e6600695733dd5df01da90c54363a3c" + [[package]] name = "region" version = "3.0.0" @@ -8338,9 +8496,9 @@ dependencies = [ [[package]] name = "rustc-demangle" -version = "0.1.21" +version = "0.1.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7ef03e0a2b150c7a90d01faf6254c9c48a41e95fb2a8c2ac1c6f0d2b9aefc342" +checksum = "d626bb9dae77e28219937af045c257c28bfd3f69333c512553507f5f9798cb76" [[package]] name = "rustc-hash" @@ -8369,7 +8527,7 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bfa0f585226d2e68097d4f95d113b15b83a82e819ab25717ec0590d9584ef366" dependencies = [ - "semver 1.0.16", + "semver 1.0.17", ] [[package]] @@ -8383,27 +8541,13 @@ dependencies = [ [[package]] name = "rustix" -version = "0.35.13" +version = "0.36.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "727a1a6d65f786ec22df8a81ca3121107f235970dc1705ed681d3e6e8b9cd5f9" +checksum = "3a38f9520be93aba504e8ca974197f46158de5dcaa9fa04b57c57cd6a679d658" dependencies = [ "bitflags", - "errno 0.2.8", - "io-lifetimes 0.7.5", - "libc", - "linux-raw-sys 0.0.46", - "windows-sys 0.42.0", -] - -[[package]] -name = "rustix" -version = "0.36.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f43abb88211988493c1abb44a70efa56ff0ce98f233b7b276146f1f3f7ba9644" -dependencies = [ - "bitflags", - "errno 0.2.8", - "io-lifetimes 1.0.5", + "errno", + "io-lifetimes", "libc", "linux-raw-sys 0.1.4", "windows-sys 0.45.0", @@ -8411,16 +8555,16 @@ dependencies = [ [[package]] name = "rustix" -version = "0.37.7" +version = "0.37.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2aae838e49b3d63e9274e1c01833cc8139d3fec468c3b84688c628f44b1ae11d" +checksum = "acf8729d8542766f1b2cf77eb034d52f40d375bb8b615d0b147089946e16613d" dependencies = [ "bitflags", - "errno 0.3.0", - "io-lifetimes 1.0.5", + "errno", + "io-lifetimes", "libc", - "linux-raw-sys 0.3.1", - "windows-sys 0.45.0", + "linux-raw-sys 0.3.7", + "windows-sys 0.48.0", ] [[package]] @@ -8471,9 +8615,9 @@ dependencies = [ [[package]] name = "rustversion" -version = "1.0.11" +version = "1.0.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5583e89e108996506031660fe09baa5011b9dd0341b89029313006d1fb508d70" +checksum = "4f3208ce4d8448b3f3e7d168a73f5e0c43a61e32930de3bceeccedb388b6bf06" [[package]] name = "rusty-fork" @@ -8499,9 +8643,9 @@ dependencies = [ [[package]] name = "ryu" -version = "1.0.12" +version = "1.0.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7b4b9743ed687d4b4bcedf9ff5eaa7398495ae14e61cba0a295704edbc7decde" +checksum = "f91339c0467de62360649f8d3e185ca8de4224ff281f66000de5eb2a77a79041" [[package]] name = "safe-mix" @@ -8636,7 +8780,7 @@ dependencies = [ "proc-macro-crate", "proc-macro2", "quote", - "syn 2.0.14", + "syn 2.0.15", ] [[package]] @@ -8645,7 +8789,7 @@ version = "0.10.0-dev" dependencies = [ "array-bytes 4.2.0", "chrono", - "clap 4.2.5", + "clap 4.2.7", "fdlimit", "futures", "futures-timer", @@ -9184,7 +9328,7 @@ dependencies = [ "once_cell", "parity-scale-codec", "paste", - "rustix 0.36.8", + "rustix 0.36.13", "sc-allocator", "sc-executor-common", "sc-runtime-test", @@ -9216,7 +9360,6 @@ name = "sc-keystore" version = "4.0.0-dev" dependencies = [ "array-bytes 4.2.0", - "async-trait", "parking_lot 0.12.1", "serde_json", "sp-application-crypto", @@ -9806,7 +9949,7 @@ dependencies = [ name = "sc-storage-monitor" version = "0.1.0" dependencies = [ - "clap 4.2.5", + "clap 4.2.7", "fs4", "futures", "log", @@ -9910,7 +10053,7 @@ dependencies = [ "proc-macro-crate", "proc-macro2", "quote", - "syn 2.0.14", + "syn 2.0.15", ] [[package]] @@ -9978,9 +10121,9 @@ dependencies = [ [[package]] name = "scale-info" -version = "2.5.0" +version = "2.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0cfdffd972d76b22f3d7f81c8be34b2296afd3a25e0a547bd9abe340a4dbbe97" +checksum = "dfdef77228a4c05dc94211441595746732131ad7f6530c6c18f045da7b7ab937" dependencies = [ "bitvec", "cfg-if", @@ -9992,9 +10135,9 @@ dependencies = [ [[package]] name = "scale-info-derive" -version = "2.5.0" +version = "2.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "61fa974aea2d63dd18a4ec3a49d59af9f34178c73a4f56d2f18205628d00681e" +checksum = "53012eae69e5aa5c14671942a5dd47de59d4cdcff8532a6dd0e081faf1119482" dependencies = [ "proc-macro-crate", "proc-macro2", @@ -10048,9 +10191,9 @@ checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd" [[package]] name = "scratch" -version = "1.0.3" +version = "1.0.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ddccb15bcce173023b3fedd9436f882a0739b8dfb45e4f6b6002bee5929f61b2" +checksum = "1792db035ce95be60c3f8853017b3999209281c24e2ba5bc8e59bf97a0c590c1" [[package]] name = "sct" @@ -10092,7 +10235,7 @@ checksum = "3be24c1842290c45df0a7bf069e0c268a747ad05a192f2fd7dcfdbc1cba40928" dependencies = [ "base16ct 0.1.1", "der 0.6.1", - "generic-array 0.14.6", + "generic-array 0.14.7", "pkcs8 0.9.0", "subtle", "zeroize", @@ -10100,14 +10243,14 @@ dependencies = [ [[package]] name = "sec1" -version = "0.7.1" +version = "0.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "48518a2b5775ba8ca5b46596aae011caa431e6ce7e4a67ead66d92f08884220e" +checksum = "f0aec48e813d6b90b15f0b8948af3c63483992dee44c03e9930b3eebdabe046e" dependencies = [ "base16ct 0.2.0", - "der 0.7.1", - "generic-array 0.14.6", - "pkcs8 0.10.1", + "der 0.7.5", + "generic-array 0.14.7", + "pkcs8 0.10.2", "subtle", "zeroize", ] @@ -10182,9 +10325,9 @@ dependencies = [ [[package]] name = "semver" -version = "1.0.16" +version = "1.0.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "58bc9567378fc7690d6b2addae4e60ac2eeea07becb2c64b9f218b53865cba2a" +checksum = "bebd363326d05ec3e2f532ab7660680f3b02130d780c299bca73469d521bc0ed" dependencies = [ "serde", ] @@ -10197,29 +10340,29 @@ checksum = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3" [[package]] name = "serde" -version = "1.0.152" +version = "1.0.162" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bb7d1f0d3021d347a83e556fc4683dea2ea09d87bccdf88ff5c12545d89d5efb" +checksum = "71b2f6e1ab5c2b98c05f0f35b236b22e8df7ead6ffbf51d7808da7f8817e7ab6" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.152" +version = "1.0.162" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "af487d118eecd09402d70a5d72551860e788df87b464af30e5ea6a38c75c541e" +checksum = "a2a0814352fd64b58489904a44ea8d90cb1a91dcb6b4f5ebabc32c8318e93cb6" dependencies = [ "proc-macro2", "quote", - "syn 1.0.109", + "syn 2.0.15", ] [[package]] name = "serde_json" -version = "1.0.93" +version = "1.0.96" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cad406b69c91885b5107daf2c29572f6c8cdb3c66826821e286c533490c0bc76" +checksum = "057d394a50403bcac12672b2b18fb387ab6d289d957dab67dd201875391e52f1" dependencies = [ "itoa", "ryu", @@ -10297,9 +10440,9 @@ dependencies = [ [[package]] name = "sha3" -version = "0.10.6" +version = "0.10.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bdf0c33fae925bdc080598b84bc15c55e7b9a4a43b3c704da051f977469691c9" +checksum = "75872d278a8f37ef87fa0ddbda7802605cb18344497949862c0d4dcb291eba60" dependencies = [ "digest 0.10.6", "keccak", @@ -10341,9 +10484,9 @@ dependencies = [ [[package]] name = "signature" -version = "2.0.0" +version = "2.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8fe458c98333f9c8152221191a77e2a44e8325d0193484af2e9421a53019e57d" +checksum = "5e1788eed21689f9cf370582dfc467ef36ed9c707f073528ddafa8d83e3b8500" dependencies = [ "digest 0.10.6", "rand_core 0.6.4", @@ -10351,9 +10494,9 @@ dependencies = [ [[package]] name = "simba" -version = "0.8.0" +version = "0.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "50582927ed6f77e4ac020c057f37a268fc6aebc29225050365aacbb9deeeddc4" +checksum = "061507c94fc6ab4ba1c9a0305018408e312e17c041eb63bef8aa726fa33aceae" dependencies = [ "approx", "num-complex", @@ -10379,9 +10522,9 @@ dependencies = [ [[package]] name = "slice-group-by" -version = "0.3.0" +version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "03b634d87b960ab1a38c4fe143b508576f075e7c978bfad18217645ebfdfa2ec" +checksum = "826167069c09b99d56f31e9ae5c99049e932a98c9dc2dac47645b08dbbf76ba7" [[package]] name = "smallvec" @@ -10470,7 +10613,7 @@ dependencies = [ "proc-macro-crate", "proc-macro2", "quote", - "syn 2.0.14", + "syn 2.0.15", ] [[package]] @@ -10714,6 +10857,7 @@ dependencies = [ "futures", "hash-db", "hash256-std-hasher", + "hex-literal", "impl-serde", "lazy_static", "libsecp256k1", @@ -10743,6 +10887,7 @@ dependencies = [ "substrate-bip39", "thiserror", "tiny-bip39", + "w3f-bls", "zeroize", ] @@ -10766,7 +10911,7 @@ dependencies = [ "proc-macro2", "quote", "sp-core-hashing", - "syn 2.0.14", + "syn 2.0.15", ] [[package]] @@ -10783,7 +10928,7 @@ version = "5.0.0" dependencies = [ "proc-macro2", "quote", - "syn 2.0.14", + "syn 2.0.15", ] [[package]] @@ -10916,7 +11061,7 @@ dependencies = [ name = "sp-npos-elections-fuzzer" version = "2.0.0-alpha.5" dependencies = [ - "clap 4.2.5", + "clap 4.2.7", "honggfuzz", "parity-scale-codec", "rand 0.8.5", @@ -11011,7 +11156,7 @@ dependencies = [ "proc-macro-crate", "proc-macro2", "quote", - "syn 2.0.14", + "syn 2.0.15", ] [[package]] @@ -11254,7 +11399,7 @@ dependencies = [ "proc-macro2", "quote", "sp-version", - "syn 2.0.14", + "syn 2.0.15", ] [[package]] @@ -11292,9 +11437,9 @@ checksum = "6e63cff320ae2c57904679ba7cb63280a3dc4613885beafb148ee7bf9aa9042d" [[package]] name = "spin" -version = "0.9.5" +version = "0.9.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7dccf47db1b41fa1573ed27ccf5e08e3ca771cb994f776668c5ebda893b248fc" +checksum = "6980e8d7511241f8acf4aebddbb1ff938df5eebe98691418c4468d0b72a96a67" [[package]] name = "spinners" @@ -11319,19 +11464,19 @@ dependencies = [ [[package]] name = "spki" -version = "0.7.0" +version = "0.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c0445c905640145c7ea8c1993555957f65e7c46d0535b91ba501bc9bfc85522f" +checksum = "9d1e996ef02c474957d681f1b05213dfb0abab947b446a62d37770b23500184a" dependencies = [ "base64ct", - "der 0.7.1", + "der 0.7.5", ] [[package]] name = "ss58-registry" -version = "1.39.0" +version = "1.40.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ecf0bd63593ef78eca595a7fc25e9a443ca46fe69fd472f8f09f5245cdcd769d" +checksum = "eb47a8ad42e5fc72d5b1eb104a5546937eaf39843499948bb666d6e93c62423b" dependencies = [ "Inflector", "num-format", @@ -11433,7 +11578,7 @@ dependencies = [ name = "subkey" version = "3.0.0" dependencies = [ - "clap 4.2.5", + "clap 4.2.7", "sc-cli", ] @@ -11475,7 +11620,7 @@ dependencies = [ name = "substrate-frame-cli" version = "4.0.0-dev" dependencies = [ - "clap 4.2.5", + "clap 4.2.7", "frame-support", "frame-system", "sc-cli", @@ -11697,7 +11842,7 @@ dependencies = [ "proc-macro-crate", "proc-macro2", "quote", - "syn 2.0.14", + "syn 2.0.15", ] [[package]] @@ -11753,9 +11898,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.14" +version = "2.0.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fcf316d5356ed6847742d036f8a39c3b8435cac10bd528a4bd461928a6ab34d5" +checksum = "a34fcf3e8b60f57e6a14301a2e916d323af98b0ea63c599441eec8558660c822" dependencies = [ "proc-macro2", "quote", @@ -11803,9 +11948,9 @@ checksum = "55937e1799185b12863d447f42597ed69d9928686b8d88a1df17376a097d8369" [[package]] name = "target-lexicon" -version = "0.12.6" +version = "0.12.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8ae9980cab1db3fceee2f6c6f643d5d8de2997c58ee8d25fb0cc8a9e9e7348e5" +checksum = "fd1ba337640d60c3e96bc6f0638a939b9c9a7f2c316a1598c279828b3d1dc8c5" [[package]] name = "tempfile" @@ -11816,7 +11961,7 @@ dependencies = [ "cfg-if", "fastrand", "redox_syscall 0.3.5", - "rustix 0.37.7", + "rustix 0.37.19", "windows-sys 0.45.0", ] @@ -11831,9 +11976,9 @@ dependencies = [ [[package]] name = "termtree" -version = "0.4.0" +version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "95059e91184749cb66be6dc994f67f182b6d897cb3df74a5bf66b5e709295fd8" +checksum = "3369f5ac52d5eb6ab48c6b4ffdc8efbcad6b89c765749064ba298f2c68a16a76" [[package]] name = "textwrap" @@ -11843,22 +11988,22 @@ checksum = "222a222a5bfe1bba4a77b45ec488a741b3cb8872e5e499451fd7d0129c9c7c3d" [[package]] name = "thiserror" -version = "1.0.38" +version = "1.0.40" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6a9cd18aa97d5c45c6603caea1da6628790b37f7a34b6ca89522331c5180fed0" +checksum = "978c9a314bd8dc99be594bc3c175faaa9794be04a5a5e153caba6915336cebac" dependencies = [ "thiserror-impl", ] [[package]] name = "thiserror-impl" -version = "1.0.38" +version = "1.0.40" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1fb327af4685e4d03fa8cbcf1716380da910eeb2bb8be417e7f9fd3fb164f36f" +checksum = "f9456a42c5b0d803c8cd86e73dd7cc9edd429499f37a3550d286d5e86720569f" dependencies = [ "proc-macro2", "quote", - "syn 1.0.109", + "syn 2.0.15", ] [[package]] @@ -11909,9 +12054,9 @@ dependencies = [ [[package]] name = "time" -version = "0.3.20" +version = "0.3.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cd0cbfecb4d19b5ea75bb31ad904eb5b9fa13f21079c3b92017ebdf4999a5890" +checksum = "8f3403384eaacbca9923fa06940178ac13e4edb725486d70e8e15881d0c836cc" dependencies = [ "itoa", "serde", @@ -11921,15 +12066,15 @@ dependencies = [ [[package]] name = "time-core" -version = "0.1.0" +version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2e153e1f1acaef8acc537e68b44906d2db6436e2b35ac2c6b42640fff91f00fd" +checksum = "7300fbefb4dadc1af235a9cef3737cea692a9d97e1b9cbcd4ebdae6f8868e6fb" [[package]] name = "time-macros" -version = "0.2.8" +version = "0.2.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fd80a657e71da814b8e5d60d3374fc6d35045062245d80224748ae522dd76f36" +checksum = "372950940a5f07bf38dbe211d7283c9e6d7327df53794992d293e534c733d09b" dependencies = [ "time-core", ] @@ -11989,9 +12134,9 @@ checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" [[package]] name = "tokio" -version = "1.27.0" +version = "1.28.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d0de47a4eecbe11f498978a9b29d792f0d2692d1dd003650c24c76510e3bc001" +checksum = "c3c786bf8134e5a3a166db9b29ab8f48134739014a3eca7bc6bfa95d673b136f" dependencies = [ "autocfg", "bytes", @@ -12003,18 +12148,18 @@ dependencies = [ "signal-hook-registry", "socket2", "tokio-macros", - "windows-sys 0.45.0", + "windows-sys 0.48.0", ] [[package]] name = "tokio-macros" -version = "2.0.0" +version = "2.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "61a573bdc87985e9d6ddeed1b3d864e8a302c847e40d647746df2f1de209d1ce" +checksum = "630bdcf245f78637c13ec01ffae6187cca34625e8c63150d424b59e55af2675e" dependencies = [ "proc-macro2", "quote", - "syn 2.0.14", + "syn 2.0.15", ] [[package]] @@ -12030,9 +12175,9 @@ dependencies = [ [[package]] name = "tokio-stream" -version = "0.1.12" +version = "0.1.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8fb52b74f05dbf495a8fba459fdc331812b96aa086d9eb78101fa0d4569c3313" +checksum = "397c988d37662c7dda6d2208364a706264bf3d6138b11d436cbac0ad38832842" dependencies = [ "futures-core", "pin-project-lite 0.2.9", @@ -12055,9 +12200,9 @@ dependencies = [ [[package]] name = "tokio-util" -version = "0.7.7" +version = "0.7.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5427d89453009325de0d8f342c9490009f76e999cb7672d77e46267448f7e6b2" +checksum = "806fe8c2c87eccc8b3267cbae29ed3ab2d0bd37fca70ab622e46aaa9375ddb7d" dependencies = [ "bytes", "futures-core", @@ -12167,13 +12312,13 @@ dependencies = [ [[package]] name = "tracing-attributes" -version = "0.1.23" +version = "0.1.24" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4017f8f45139870ca7e672686113917c71c7a6e02d4924eda67186083c03081a" +checksum = "0f57e3ca2a01450b1a921183a9c9cbfda207fd822cef4ccb00a65402cbba7a74" dependencies = [ "proc-macro2", "quote", - "syn 1.0.109", + "syn 2.0.15", ] [[package]] @@ -12242,9 +12387,9 @@ dependencies = [ [[package]] name = "tracing-subscriber" -version = "0.3.16" +version = "0.3.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a6176eae26dd70d0c919749377897b54a9276bd7061339665dd68777926b5a70" +checksum = "30a651bc37f915e81f087d86e62a18eec5f79550c7faff886f7090b4ea757c77" dependencies = [ "matchers 0.1.0", "nu-ansi-term", @@ -12364,7 +12509,7 @@ version = "0.10.0-dev" dependencies = [ "assert_cmd", "async-trait", - "clap 4.2.5", + "clap 4.2.7", "frame-remote-externalities", "frame-try-runtime", "hex", @@ -12400,9 +12545,9 @@ dependencies = [ [[package]] name = "trybuild" -version = "1.0.77" +version = "1.0.80" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a44da5a6f2164c8e14d3bbc0657d69c5966af9f5f6930d4f600b1f5c4a673413" +checksum = "501dbdbb99861e4ab6b60eb6a7493956a9defb644fd034bc4a5ef27c693c8a3a" dependencies = [ "basic-toml", "dissimilar", @@ -12477,15 +12622,15 @@ dependencies = [ [[package]] name = "unicode-bidi" -version = "0.3.10" +version = "0.3.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d54675592c1dbefd78cbd98db9bacd89886e1ca50692a0692baefffdeb92dd58" +checksum = "92888ba5573ff080736b3648696b70cafad7d250551175acbaa4e0385b3e1460" [[package]] name = "unicode-ident" -version = "1.0.6" +version = "1.0.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "84a22b9f218b40614adcb3f4ff08b703773ad44fa9423e4e0d346d5db86e4ebc" +checksum = "e5464a87b239f13a63a501f2701565754bae92d243d4bb7eb12f6d57d2269bf4" [[package]] name = "unicode-normalization" @@ -12514,7 +12659,7 @@ version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9f214e8f697e925001e66ec2c6e37a4ef93f0f78c2eed7814394e10c62025b05" dependencies = [ - "generic-array 0.14.6", + "generic-array 0.14.7", "subtle", ] @@ -12565,11 +12710,11 @@ checksum = "711b9620af191e0cdc7468a8d14e709c3dcdb115b36f838e601583af800a370a" [[package]] name = "uuid" -version = "1.3.0" +version = "1.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1674845326ee10d37ca60470760d4288a6f80f304007d92e5c53bab78c9cfd79" +checksum = "4dad5567ad0cf5b760e5665964bec1b47dfd077ba8a2544b513f3556d3d239a2" dependencies = [ - "getrandom 0.2.8", + "getrandom 0.2.9", ] [[package]] @@ -12596,6 +12741,30 @@ version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6a02e4885ed3bc0f2de90ea6dd45ebcbb66dacffe03547fadbb0eeae2770887d" +[[package]] +name = "w3f-bls" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7335e4c132c28cc43caef6adb339789e599e39adbe78da0c4d547fad48cbc331" +dependencies = [ + "ark-bls12-377", + "ark-bls12-381", + "ark-ec", + "ark-ff", + "ark-serialize", + "ark-serialize-derive", + "arrayref", + "constcat", + "digest 0.10.6", + "rand 0.8.5", + "rand_chacha 0.3.1", + "rand_core 0.6.4", + "sha2 0.10.6", + "sha3", + "thiserror", + "zeroize", +] + [[package]] name = "wait-timeout" version = "0.2.0" @@ -12622,12 +12791,11 @@ checksum = "9d5b2c62b4012a3e1eca5a7e077d13b3bf498c4073e33ccd58626607748ceeca" [[package]] name = "walkdir" -version = "2.3.2" +version = "2.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "808cf2735cd4b6866113f648b791c6adc5714537bc222d9347bb203386ffda56" +checksum = "36df944cda56c7d8d8b7496af378e6b16de9284591917d307c9b4d313c44e698" dependencies = [ "same-file", - "winapi", "winapi-util", ] @@ -12661,9 +12829,9 @@ checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" [[package]] name = "wasm-bindgen" -version = "0.2.84" +version = "0.2.85" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "31f8dcbc21f30d9b8f2ea926ecb58f6b91192c17e9d33594b3df58b2007ca53b" +checksum = "5b6cb788c4e39112fbe1822277ef6fb3c55cd86b95cb3d3c4c1c9597e4ac74b4" dependencies = [ "cfg-if", "wasm-bindgen-macro", @@ -12671,24 +12839,24 @@ dependencies = [ [[package]] name = "wasm-bindgen-backend" -version = "0.2.84" +version = "0.2.85" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "95ce90fd5bcc06af55a641a86428ee4229e44e07033963a2290a8e241607ccb9" +checksum = "35e522ed4105a9d626d885b35d62501b30d9666283a5c8be12c14a8bdafe7822" dependencies = [ "bumpalo", "log", "once_cell", "proc-macro2", "quote", - "syn 1.0.109", + "syn 2.0.15", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-futures" -version = "0.4.34" +version = "0.4.35" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f219e0d211ba40266969f6dbdd90636da12f75bee4fc9d6c23d1260dadb51454" +checksum = "083abe15c5d88556b77bdf7aef403625be9e327ad37c62c4e4129af740168163" dependencies = [ "cfg-if", "js-sys", @@ -12698,9 +12866,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro" -version = "0.2.84" +version = "0.2.85" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4c21f77c0bedc37fd5dc21f897894a5ca01e7bb159884559461862ae90c0b4c5" +checksum = "358a79a0cb89d21db8120cbfb91392335913e4890665b1a7981d9e956903b434" dependencies = [ "quote", "wasm-bindgen-macro-support", @@ -12708,28 +12876,28 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro-support" -version = "0.2.84" +version = "0.2.85" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2aff81306fcac3c7515ad4e177f521b5c9a15f2b08f4e32d823066102f35a5f6" +checksum = "4783ce29f09b9d93134d41297aded3a712b7b979e9c6f28c32cb88c973a94869" dependencies = [ "proc-macro2", "quote", - "syn 1.0.109", + "syn 2.0.15", "wasm-bindgen-backend", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-shared" -version = "0.2.84" +version = "0.2.85" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0046fef7e28c3804e5e38bfa31ea2a0f73905319b677e57ebe37e49358989b5d" +checksum = "a901d592cafaa4d711bc324edfaff879ac700b19c3dfd60058d2b445be2691eb" [[package]] name = "wasm-encoder" -version = "0.24.1" +version = "0.26.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "68f7d56227d910901ce12dfd19acc40c12687994dfb3f57c90690f80be946ec5" +checksum = "d05d0b6fcd0aeb98adf16e7975331b3c17222aa815148f5b976370ce589d80ef" dependencies = [ "leb128", ] @@ -12824,7 +12992,7 @@ version = "0.28.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8e61a7006b0fdf24f6bbe8dcfdad5ca1b350de80061fb2827f31c82fbbb9565a" dependencies = [ - "spin 0.9.5", + "spin 0.9.8", "wasmi_arena", "wasmi_core 0.12.0", "wasmparser-nostd", @@ -12939,7 +13107,7 @@ dependencies = [ "directories-next", "file-per-thread-logger", "log", - "rustix 0.36.8", + "rustix 0.36.13", "serde", "sha2 0.10.6", "toml 0.5.11", @@ -13019,7 +13187,7 @@ checksum = "eed41cbcbf74ce3ff6f1d07d1b707888166dc408d1a880f651268f4f7c9194b2" dependencies = [ "object 0.29.0", "once_cell", - "rustix 0.36.8", + "rustix 0.36.13", ] [[package]] @@ -13050,7 +13218,7 @@ dependencies = [ "memoffset 0.6.5", "paste", "rand 0.8.5", - "rustix 0.36.8", + "rustix 0.36.13", "wasmtime-asm-macros", "wasmtime-environ", "wasmtime-jit-debug", @@ -13071,9 +13239,9 @@ dependencies = [ [[package]] name = "wast" -version = "54.0.1" +version = "57.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3d48d9d731d835f4f8dacbb8de7d47be068812cb9877f5c60d408858778d8d2a" +checksum = "6eb0f5ed17ac4421193c7477da05892c2edafd67f9639e3c11a82086416662dc" dependencies = [ "leb128", "memchr", @@ -13083,18 +13251,18 @@ dependencies = [ [[package]] name = "wat" -version = "1.0.60" +version = "1.0.63" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d1db2e3ed05ea31243761439194bec3af6efbbaf87c4c8667fb879e4f23791a0" +checksum = "ab9ab0d87337c3be2bb6fc5cd331c4ba9fd6bcb4ee85048a0dd59ed9ecf92e53" dependencies = [ "wast", ] [[package]] name = "web-sys" -version = "0.3.61" +version = "0.3.62" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e33b99f4b23ba3eec1a53ac264e35a755f00e966e0065077d6027c0f575b0b97" +checksum = "16b5f940c7edfdc6d12126d98c9ef4d1b3d470011c47c76a6581df47ad9ba721" dependencies = [ "js-sys", "wasm-bindgen", @@ -13155,7 +13323,7 @@ dependencies = [ "sha2 0.10.6", "stun", "thiserror", - "time 0.3.20", + "time 0.3.21", "tokio", "turn", "url", @@ -13199,7 +13367,7 @@ dependencies = [ "byteorder", "ccm", "curve25519-dalek 3.2.0", - "der-parser 8.1.0", + "der-parser 8.2.0", "elliptic-curve 0.12.3", "hkdf", "hmac 0.12.1", @@ -13265,18 +13433,15 @@ dependencies = [ [[package]] name = "webrtc-media" -version = "0.5.0" +version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ee2a3c157a040324e5049bcbd644ffc9079e6738fa2cfab2bcff64e5cc4c00d7" +checksum = "f72e1650a8ae006017d1a5280efb49e2610c19ccc3c0905b03b648aee9554991" dependencies = [ "byteorder", "bytes", - "derive_builder", - "displaydoc", "rand 0.8.5", "rtp", "thiserror", - "webrtc-util", ] [[package]] @@ -13341,15 +13506,6 @@ dependencies = [ "winapi", ] -[[package]] -name = "wepoll-ffi" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d743fdedc5c64377b5fc2bc036b01c7fd642205a0d96356034ae3404d49eb7fb" -dependencies = [ - "cc", -] - [[package]] name = "which" version = "4.4.0" @@ -13421,19 +13577,28 @@ dependencies = [ "windows_x86_64_msvc 0.34.0", ] +[[package]] +name = "windows" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e686886bc078bc1b0b600cac0147aadb815089b6e4da64016cbd754b6342700f" +dependencies = [ + "windows-targets 0.48.0", +] + [[package]] name = "windows-sys" version = "0.42.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5a3e1820f08b8513f676f7ab6c1f99ff312fb97b553d30ff4dd86f9f15728aa7" dependencies = [ - "windows_aarch64_gnullvm 0.42.1", - "windows_aarch64_msvc 0.42.1", - "windows_i686_gnu 0.42.1", - "windows_i686_msvc 0.42.1", - "windows_x86_64_gnu 0.42.1", - "windows_x86_64_gnullvm 0.42.1", - "windows_x86_64_msvc 0.42.1", + "windows_aarch64_gnullvm 0.42.2", + "windows_aarch64_msvc 0.42.2", + "windows_i686_gnu 0.42.2", + "windows_i686_msvc 0.42.2", + "windows_x86_64_gnu 0.42.2", + "windows_x86_64_gnullvm 0.42.2", + "windows_x86_64_msvc 0.42.2", ] [[package]] @@ -13442,7 +13607,7 @@ version = "0.45.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "75283be5efb2831d37ea142365f009c02ec203cd29a3ebecbc093d52315b66d0" dependencies = [ - "windows-targets 0.42.1", + "windows-targets 0.42.2", ] [[package]] @@ -13456,17 +13621,17 @@ dependencies = [ [[package]] name = "windows-targets" -version = "0.42.1" +version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8e2522491fbfcd58cc84d47aeb2958948c4b8982e9a2d8a2a35bbaed431390e7" +checksum = "8e5180c00cd44c9b1c88adb3693291f1cd93605ded80c250a75d472756b4d071" dependencies = [ - "windows_aarch64_gnullvm 0.42.1", - "windows_aarch64_msvc 0.42.1", - "windows_i686_gnu 0.42.1", - "windows_i686_msvc 0.42.1", - "windows_x86_64_gnu 0.42.1", - "windows_x86_64_gnullvm 0.42.1", - "windows_x86_64_msvc 0.42.1", + "windows_aarch64_gnullvm 0.42.2", + "windows_aarch64_msvc 0.42.2", + "windows_i686_gnu 0.42.2", + "windows_i686_msvc 0.42.2", + "windows_x86_64_gnu 0.42.2", + "windows_x86_64_gnullvm 0.42.2", + "windows_x86_64_msvc 0.42.2", ] [[package]] @@ -13486,9 +13651,9 @@ dependencies = [ [[package]] name = "windows_aarch64_gnullvm" -version = "0.42.1" +version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8c9864e83243fdec7fc9c5444389dcbbfd258f745e7853198f365e3c4968a608" +checksum = "597a5118570b68bc08d8d59125332c54f1ba9d9adeedeef5b99b02ba2b0698f8" [[package]] name = "windows_aarch64_gnullvm" @@ -13504,9 +13669,9 @@ checksum = "17cffbe740121affb56fad0fc0e421804adf0ae00891205213b5cecd30db881d" [[package]] name = "windows_aarch64_msvc" -version = "0.42.1" +version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4c8b1b673ffc16c47a9ff48570a9d85e25d265735c503681332589af6253c6c7" +checksum = "e08e8864a60f06ef0d0ff4ba04124db8b0fb3be5776a5cd47641e942e58c4d43" [[package]] name = "windows_aarch64_msvc" @@ -13522,9 +13687,9 @@ checksum = "2564fde759adb79129d9b4f54be42b32c89970c18ebf93124ca8870a498688ed" [[package]] name = "windows_i686_gnu" -version = "0.42.1" +version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "de3887528ad530ba7bdbb1faa8275ec7a1155a45ffa57c37993960277145d640" +checksum = "c61d927d8da41da96a81f029489353e68739737d3beca43145c8afec9a31a84f" [[package]] name = "windows_i686_gnu" @@ -13540,9 +13705,9 @@ checksum = "9cd9d32ba70453522332c14d38814bceeb747d80b3958676007acadd7e166956" [[package]] name = "windows_i686_msvc" -version = "0.42.1" +version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bf4d1122317eddd6ff351aa852118a2418ad4214e6613a50e0191f7004372605" +checksum = "44d840b6ec649f480a41c8d80f9c65108b92d89345dd94027bfe06ac444d1060" [[package]] name = "windows_i686_msvc" @@ -13558,9 +13723,9 @@ checksum = "cfce6deae227ee8d356d19effc141a509cc503dfd1f850622ec4b0f84428e1f4" [[package]] name = "windows_x86_64_gnu" -version = "0.42.1" +version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c1040f221285e17ebccbc2591ffdc2d44ee1f9186324dd3e84e99ac68d699c45" +checksum = "8de912b8b8feb55c064867cf047dda097f92d51efad5b491dfb98f6bbb70cb36" [[package]] name = "windows_x86_64_gnu" @@ -13570,9 +13735,9 @@ checksum = "ca2b8a661f7628cbd23440e50b05d705db3686f894fc9580820623656af974b1" [[package]] name = "windows_x86_64_gnullvm" -version = "0.42.1" +version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "628bfdf232daa22b0d64fdb62b09fcc36bb01f05a3939e20ab73aaf9470d0463" +checksum = "26d41b46a36d453748aedef1486d5c7a85db22e56aff34643984ea85514e94a3" [[package]] name = "windows_x86_64_gnullvm" @@ -13588,9 +13753,9 @@ checksum = "d19538ccc21819d01deaf88d6a17eae6596a12e9aafdbb97916fb49896d89de9" [[package]] name = "windows_x86_64_msvc" -version = "0.42.1" +version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "447660ad36a13288b1db4d4248e857b510e8c3a225c822ba4fb748c0aafecffd" +checksum = "9aec5da331524158c6d1a4ac0ab1541149c0b9505fde06423b02f5ef0106b9f0" [[package]] name = "windows_x86_64_msvc" @@ -13600,9 +13765,9 @@ checksum = "1a515f5799fe4961cb532f983ce2b23082366b898e52ffbce459c86f67c8378a" [[package]] name = "winnow" -version = "0.4.1" +version = "0.4.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ae8970b36c66498d8ff1d66685dc86b91b29db0c7739899012f63a63814b4b28" +checksum = "61de7bac303dc551fe038e2b3cef0f571087a47571ea6e79a87692ac99b99699" dependencies = [ "memchr", ] @@ -13663,7 +13828,7 @@ dependencies = [ "ring", "rusticata-macros", "thiserror", - "time 0.3.20", + "time 0.3.21", ] [[package]] @@ -13672,16 +13837,16 @@ version = "0.14.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e0ecbeb7b67ce215e40e3cc7f2ff902f94a223acf44995934763467e7b1febc8" dependencies = [ - "asn1-rs 0.5.1", + "asn1-rs 0.5.2", "base64 0.13.1", "data-encoding", - "der-parser 8.1.0", + "der-parser 8.2.0", "lazy_static", "nom", "oid-registry 0.6.1", "rusticata-macros", "thiserror", - "time 0.3.20", + "time 0.3.21", ] [[package]] @@ -13706,32 +13871,31 @@ checksum = "09041cd90cf85f7f8b2df60c646f853b7f535ce68f85244eb6731cf89fa498ec" [[package]] name = "yasna" -version = "0.5.1" +version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "aed2e7a52e3744ab4d0c05c20aa065258e84c49fd4226f5191b2ed29712710b4" +checksum = "e17bb3549cc1321ae1296b9cdc2698e2b6cb1992adfa19a8c72e5b7a738f44cd" dependencies = [ - "time 0.3.20", + "time 0.3.21", ] [[package]] name = "zeroize" -version = "1.5.7" +version = "1.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c394b5bd0c6f669e7275d9c20aa90ae064cb22e75a1cad54e1b34088034b149f" +checksum = "2a0956f1ba7c7909bfb66c2e9e4124ab6f6482560f6628b5aaeba39207c9aad9" dependencies = [ "zeroize_derive", ] [[package]] name = "zeroize_derive" -version = "1.3.3" +version = "1.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "44bf07cb3e50ea2003396695d58bf46bc9887a1f362260446fad6bc4e79bd36c" +checksum = "ce36e65b0d2999d2aafac989fb249189a141aee1f53c612c1f37d72631959f69" dependencies = [ "proc-macro2", "quote", - "syn 1.0.109", - "synstructure", + "syn 2.0.15", ] [[package]] @@ -13774,9 +13938,9 @@ dependencies = [ [[package]] name = "zstd-sys" -version = "2.0.7+zstd.1.5.4" +version = "2.0.8+zstd.1.5.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "94509c3ba2fe55294d752b79842c530ccfab760192521df74a081a78d2b3c7f5" +checksum = "5556e6ee25d32df2586c098bbfa278803692a20d0ab9565e049480d52707ec8c" dependencies = [ "cc", "libc", diff --git a/client/keystore/Cargo.toml b/client/keystore/Cargo.toml index 8766ee801..81e047e90 100644 --- a/client/keystore/Cargo.toml +++ b/client/keystore/Cargo.toml @@ -15,7 +15,6 @@ targets = ["x86_64-unknown-linux-gnu"] [dependencies] array-bytes = "4.1" -async-trait = "0.1.57" parking_lot = "0.12.1" serde_json = "1.0.85" thiserror = "1.0" @@ -25,3 +24,11 @@ sp-keystore = { version = "0.13.0", path = "../../primitives/keystore" } [dev-dependencies] tempfile = "3.1.0" + +[features] +# This feature adds BLS crypto primitives. It should not be used in production since +# the BLS implementation and interface may still be subject to significant change. +bls-experimental = [ + "sp-core/bls-experimental", + "sp-keystore/bls-experimental", +] diff --git a/client/keystore/src/local.rs b/client/keystore/src/local.rs index 1e785113a..4167e486e 100644 --- a/client/keystore/src/local.rs +++ b/client/keystore/src/local.rs @@ -19,6 +19,8 @@ use parking_lot::RwLock; use sp_application_crypto::{AppCrypto, AppPair, IsWrappedBy}; +#[cfg(feature = "bls-experimental")] +use sp_core::{bls377, bls381}; use sp_core::{ crypto::{ByteArray, ExposeSecret, KeyTypeId, Pair as CorePair, SecretString, VrfSecret}, ecdsa, ed25519, sr25519, @@ -134,7 +136,7 @@ impl Keystore for LocalKeystore { /// Generate a new pair compatible with the 'ed25519' signature scheme. /// - /// If the `[seed]` is `Some` then the key will be ephemeral and stored in memory. + /// If `[seed]` is `Some` then the key will be ephemeral and stored in memory. fn sr25519_generate_new( &self, key_type: KeyTypeId, @@ -176,7 +178,7 @@ impl Keystore for LocalKeystore { /// Generate a new pair compatible with the 'sr25519' signature scheme. /// - /// If the `[seed]` is `Some` then the key will be ephemeral and stored in memory. + /// If `[seed]` is `Some` then the key will be ephemeral and stored in memory. fn ed25519_generate_new( &self, key_type: KeyTypeId, @@ -200,7 +202,7 @@ impl Keystore for LocalKeystore { /// Generate a new pair compatible with the 'ecdsa' signature scheme. /// - /// If the `[seed]` is `Some` then the key will be ephemeral and stored in memory. + /// If `[seed]` is `Some` then the key will be ephemeral and stored in memory. fn ecdsa_generate_new( &self, key_type: KeyTypeId, @@ -232,6 +234,60 @@ impl Keystore for LocalKeystore { Ok(sig) } + #[cfg(feature = "bls-experimental")] + fn bls381_public_keys(&self, key_type: KeyTypeId) -> Vec { + self.public_keys::(key_type) + } + + #[cfg(feature = "bls-experimental")] + /// Generate a new pair compatible with the 'bls381' signature scheme. + /// + /// If `[seed]` is `Some` then the key will be ephemeral and stored in memory. + fn bls381_generate_new( + &self, + key_type: KeyTypeId, + seed: Option<&str>, + ) -> std::result::Result { + self.generate_new::(key_type, seed) + } + + #[cfg(feature = "bls-experimental")] + fn bls381_sign( + &self, + key_type: KeyTypeId, + public: &bls381::Public, + msg: &[u8], + ) -> std::result::Result, TraitError> { + self.sign::(key_type, public, msg) + } + + #[cfg(feature = "bls-experimental")] + fn bls377_public_keys(&self, key_type: KeyTypeId) -> Vec { + self.public_keys::(key_type) + } + + #[cfg(feature = "bls-experimental")] + /// Generate a new pair compatible with the 'bls377' signature scheme. + /// + /// If `[seed]` is `Some` then the key will be ephemeral and stored in memory. + fn bls377_generate_new( + &self, + key_type: KeyTypeId, + seed: Option<&str>, + ) -> std::result::Result { + self.generate_new::(key_type, seed) + } + + #[cfg(feature = "bls-experimental")] + fn bls377_sign( + &self, + key_type: KeyTypeId, + public: &bls377::Public, + msg: &[u8], + ) -> std::result::Result, TraitError> { + self.sign::(key_type, public, msg) + } + fn insert( &self, key_type: KeyTypeId, diff --git a/primitives/application-crypto/src/bls377.rs b/primitives/application-crypto/src/bls377.rs new file mode 100644 index 000000000..7fbbec546 --- /dev/null +++ b/primitives/application-crypto/src/bls377.rs @@ -0,0 +1,28 @@ +// This file is part of Substrate. + +// Copyright (C) Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +//! BLS12-377 crypto applications. + +pub use sp_core::bls::bls377::*; + +mod app { + crate::app_crypto!(super, sp_core::testing::BLS377); +} + +#[cfg(feature = "full_crypto")] +pub use app::Pair as AppPair; +pub use app::{Public as AppPublic, Signature as AppSignature}; diff --git a/primitives/application-crypto/src/bls381.rs b/primitives/application-crypto/src/bls381.rs new file mode 100644 index 000000000..d990f2e14 --- /dev/null +++ b/primitives/application-crypto/src/bls381.rs @@ -0,0 +1,28 @@ +// This file is part of Substrate. + +// Copyright (C) Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +//! BLS12-381 crypto applications. + +pub use sp_core::bls::bls381::*; + +mod app { + crate::app_crypto!(super, sp_core::testing::BLS381); +} + +#[cfg(feature = "full_crypto")] +pub use app::Pair as AppPair; +pub use app::{Public as AppPublic, Signature as AppSignature}; diff --git a/primitives/application-crypto/src/lib.rs b/primitives/application-crypto/src/lib.rs index fa92e427a..3e8f2f5a7 100644 --- a/primitives/application-crypto/src/lib.rs +++ b/primitives/application-crypto/src/lib.rs @@ -41,6 +41,10 @@ pub use serde; #[doc(hidden)] pub use sp_std::{ops::Deref, vec::Vec}; +#[cfg(feature = "bls-experimental")] +pub mod bls377; +#[cfg(feature = "bls-experimental")] +pub mod bls381; pub mod ecdsa; pub mod ed25519; pub mod sr25519; diff --git a/primitives/core/Cargo.toml b/primitives/core/Cargo.toml index 8eac09967..0c203a03d 100644 --- a/primitives/core/Cargo.toml +++ b/primitives/core/Cargo.toml @@ -52,6 +52,8 @@ secp256k1 = { version = "0.24.0", default-features = false, features = ["recover ss58-registry = { version = "1.34.0", default-features = false } sp-core-hashing = { version = "5.0.0", path = "./hashing", default-features = false, optional = true } sp-runtime-interface = { version = "7.0.0", default-features = false, path = "../runtime-interface" } +# bls crypto +w3f-bls = { version = "0.1.3", default-features = false, optional = true} [dev-dependencies] sp-serializer = { version = "4.0.0-dev", path = "../serializer" } @@ -59,6 +61,7 @@ rand = "0.8.5" criterion = "0.4.0" serde_json = "1.0" sp-core-hashing-proc-macro = { version = "5.0.0", path = "./hashing/proc-macro" } +hex-literal = "0.3.4" [[bench]] name = "bench" @@ -125,3 +128,7 @@ full_crypto = [ "sp-core-hashing", "sp-runtime-interface/disable_target_static_assertions", ] + +# This feature adds BLS crypto primitives. It should not be used in production since +# the BLS implementation and interface may still be subject to significant change. +bls-experimental = ["w3f-bls"] diff --git a/primitives/core/src/bls.rs b/primitives/core/src/bls.rs new file mode 100644 index 000000000..86db4525f --- /dev/null +++ b/primitives/core/src/bls.rs @@ -0,0 +1,675 @@ +// This file is part of Substrate. + +// Copyright (C) Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +//! Simple BLS (Boneh–Lynn–Shacham) Signature API. + +#[cfg(feature = "std")] +use crate::crypto::Ss58Codec; +use crate::crypto::{ByteArray, CryptoType, Derive, Public as TraitPublic, UncheckedFrom}; +#[cfg(feature = "full_crypto")] +use crate::crypto::{DeriveError, DeriveJunction, Pair as TraitPair, SecretStringError}; + +#[cfg(feature = "full_crypto")] +use sp_std::vec::Vec; + +use codec::{Decode, Encode, MaxEncodedLen}; +use scale_info::TypeInfo; +#[cfg(feature = "std")] +use serde::{de, Deserialize, Deserializer, Serialize, Serializer}; +use w3f_bls::{DoublePublicKey, DoubleSignature, EngineBLS, SerializableToBytes, TinyBLS381}; +#[cfg(feature = "full_crypto")] +use w3f_bls::{DoublePublicKeyScheme, Keypair, Message, SecretKey}; + +use sp_runtime_interface::pass_by::PassByInner; +use sp_std::{convert::TryFrom, marker::PhantomData, ops::Deref}; + +/// BLS-377 specialized types +pub mod bls377 { + use crate::crypto::CryptoTypeId; + use w3f_bls::TinyBLS377; + + /// An identifier used to match public keys against BLS12-377 keys + pub const CRYPTO_ID: CryptoTypeId = CryptoTypeId(*b"bls7"); + + /// BLS12-377 key pair. + #[cfg(feature = "full_crypto")] + pub type Pair = super::Pair; + /// BLS12-377 public key. + pub type Public = super::Public; + /// BLS12-377 signature. + pub type Signature = super::Signature; + + impl super::HardJunctionId for TinyBLS377 { + const ID: &'static str = "BLS12377HDKD"; + } +} + +/// BLS-381 specialized types +pub mod bls381 { + use crate::crypto::CryptoTypeId; + use w3f_bls::TinyBLS381; + + /// An identifier used to match public keys against BLS12-381 keys + pub const CRYPTO_ID: CryptoTypeId = CryptoTypeId(*b"bls8"); + + /// BLS12-381 key pair. + #[cfg(feature = "full_crypto")] + pub type Pair = super::Pair; + /// BLS12-381 public key. + pub type Public = super::Public; + /// BLS12-381 signature. + pub type Signature = super::Signature; + + impl super::HardJunctionId for TinyBLS381 { + const ID: &'static str = "BLS12381HDKD"; + } +} + +trait BlsBound: EngineBLS + HardJunctionId + Send + Sync + 'static {} + +impl BlsBound for T {} + +// Secret key serialized size +#[cfg(feature = "full_crypto")] +const SECRET_KEY_SERIALIZED_SIZE: usize = + as SerializableToBytes>::SERIALIZED_BYTES_SIZE; + +// Public key serialized size +const PUBLIC_KEY_SERIALIZED_SIZE: usize = + as SerializableToBytes>::SERIALIZED_BYTES_SIZE; + +// Signature serialized size +const SIGNATURE_SERIALIZED_SIZE: usize = + as SerializableToBytes>::SERIALIZED_BYTES_SIZE; + +/// A secret seed. +/// +/// It's not called a "secret key" because ring doesn't expose the secret keys +/// of the key pair (yeah, dumb); as such we're forced to remember the seed manually if we +/// will need it later (such as for HDKD). +#[cfg(feature = "full_crypto")] +type Seed = [u8; SECRET_KEY_SERIALIZED_SIZE]; + +/// A public key. +#[derive(Copy, Encode, Decode, MaxEncodedLen, TypeInfo)] +#[scale_info(skip_type_params(T))] +pub struct Public { + inner: [u8; PUBLIC_KEY_SERIALIZED_SIZE], + _phantom: PhantomData T>, +} + +impl Clone for Public { + fn clone(&self) -> Self { + Self { inner: self.inner, _phantom: PhantomData } + } +} + +impl PartialEq for Public { + fn eq(&self, other: &Self) -> bool { + self.inner == other.inner + } +} + +impl Eq for Public {} + +impl PartialOrd for Public { + fn partial_cmp(&self, other: &Self) -> Option { + self.inner.partial_cmp(&other.inner) + } +} + +impl Ord for Public { + fn cmp(&self, other: &Self) -> sp_std::cmp::Ordering { + self.inner.cmp(&other.inner) + } +} + +#[cfg(feature = "full_crypto")] +impl sp_std::hash::Hash for Public { + fn hash(&self, state: &mut H) { + self.inner.hash(state) + } +} + +impl ByteArray for Public { + const LEN: usize = PUBLIC_KEY_SERIALIZED_SIZE; +} + +impl PassByInner for Public { + type Inner = [u8; PUBLIC_KEY_SERIALIZED_SIZE]; + + fn into_inner(self) -> Self::Inner { + self.inner + } + + fn inner(&self) -> &Self::Inner { + &self.inner + } + + fn from_inner(inner: Self::Inner) -> Self { + Self { inner, _phantom: PhantomData } + } +} + +impl AsRef<[u8; PUBLIC_KEY_SERIALIZED_SIZE]> for Public { + fn as_ref(&self) -> &[u8; PUBLIC_KEY_SERIALIZED_SIZE] { + &self.inner + } +} + +impl AsRef<[u8]> for Public { + fn as_ref(&self) -> &[u8] { + &self.inner[..] + } +} + +impl AsMut<[u8]> for Public { + fn as_mut(&mut self) -> &mut [u8] { + &mut self.inner[..] + } +} + +impl Deref for Public { + type Target = [u8]; + + fn deref(&self) -> &Self::Target { + &self.inner + } +} + +impl TryFrom<&[u8]> for Public { + type Error = (); + + fn try_from(data: &[u8]) -> Result { + if data.len() != PUBLIC_KEY_SERIALIZED_SIZE { + return Err(()) + } + let mut r = [0u8; PUBLIC_KEY_SERIALIZED_SIZE]; + r.copy_from_slice(data); + Ok(Self::unchecked_from(r)) + } +} + +impl From> for [u8; PUBLIC_KEY_SERIALIZED_SIZE] { + fn from(x: Public) -> Self { + x.inner + } +} + +#[cfg(feature = "full_crypto")] +impl From> for Public { + fn from(x: Pair) -> Self { + x.public() + } +} + +impl UncheckedFrom<[u8; PUBLIC_KEY_SERIALIZED_SIZE]> for Public { + fn unchecked_from(data: [u8; PUBLIC_KEY_SERIALIZED_SIZE]) -> Self { + Public { inner: data, _phantom: PhantomData } + } +} + +#[cfg(feature = "std")] +impl std::str::FromStr for Public { + type Err = crate::crypto::PublicError; + + fn from_str(s: &str) -> Result { + Self::from_ss58check(s) + } +} + +#[cfg(feature = "std")] +impl std::fmt::Display for Public { + fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { + write!(f, "{}", self.to_ss58check()) + } +} + +#[cfg(feature = "std")] +impl sp_std::fmt::Debug for Public { + fn fmt(&self, f: &mut sp_std::fmt::Formatter) -> sp_std::fmt::Result { + let s = self.to_ss58check(); + write!(f, "{} ({}...)", crate::hexdisplay::HexDisplay::from(&self.inner), &s[0..8]) + } +} + +#[cfg(not(feature = "std"))] +impl sp_std::fmt::Debug for Public { + fn fmt(&self, _: &mut sp_std::fmt::Formatter) -> sp_std::fmt::Result { + Ok(()) + } +} + +#[cfg(feature = "std")] +impl Serialize for Public { + fn serialize(&self, serializer: S) -> Result + where + S: Serializer, + { + serializer.serialize_str(&self.to_ss58check()) + } +} + +#[cfg(feature = "std")] +impl<'de, T: BlsBound> Deserialize<'de> for Public { + fn deserialize(deserializer: D) -> Result + where + D: Deserializer<'de>, + { + Public::from_ss58check(&String::deserialize(deserializer)?) + .map_err(|e| de::Error::custom(format!("{:?}", e))) + } +} + +impl TraitPublic for Public {} + +impl Derive for Public {} + +impl CryptoType for Public { + #[cfg(feature = "full_crypto")] + type Pair = Pair; +} + +/// A generic BLS signature. +#[derive(Copy, Encode, Decode, MaxEncodedLen, TypeInfo)] +#[scale_info(skip_type_params(T))] +pub struct Signature { + inner: [u8; SIGNATURE_SERIALIZED_SIZE], + _phantom: PhantomData T>, +} + +impl Clone for Signature { + fn clone(&self) -> Self { + Self { inner: self.inner, _phantom: PhantomData } + } +} + +impl PartialEq for Signature { + fn eq(&self, other: &Self) -> bool { + self.inner == other.inner + } +} + +impl Eq for Signature {} + +#[cfg(feature = "full_crypto")] +impl sp_std::hash::Hash for Signature { + fn hash(&self, state: &mut H) { + self.inner.hash(state) + } +} + +impl TryFrom<&[u8]> for Signature { + type Error = (); + + fn try_from(data: &[u8]) -> Result { + if data.len() != SIGNATURE_SERIALIZED_SIZE { + return Err(()) + } + let mut inner = [0u8; SIGNATURE_SERIALIZED_SIZE]; + inner.copy_from_slice(data); + Ok(Signature::unchecked_from(inner)) + } +} + +#[cfg(feature = "std")] +impl Serialize for Signature { + fn serialize(&self, serializer: S) -> Result + where + S: Serializer, + { + serializer.serialize_str(&array_bytes::bytes2hex("", self.as_ref())) + } +} + +#[cfg(feature = "std")] +impl<'de, T> Deserialize<'de> for Signature { + fn deserialize(deserializer: D) -> Result + where + D: Deserializer<'de>, + { + let signature_hex = array_bytes::hex2bytes(&String::deserialize(deserializer)?) + .map_err(|e| de::Error::custom(format!("{:?}", e)))?; + Signature::try_from(signature_hex.as_ref()) + .map_err(|e| de::Error::custom(format!("{:?}", e))) + } +} + +impl From> for [u8; SIGNATURE_SERIALIZED_SIZE] { + fn from(signature: Signature) -> [u8; SIGNATURE_SERIALIZED_SIZE] { + signature.inner + } +} + +impl AsRef<[u8; SIGNATURE_SERIALIZED_SIZE]> for Signature { + fn as_ref(&self) -> &[u8; SIGNATURE_SERIALIZED_SIZE] { + &self.inner + } +} + +impl AsRef<[u8]> for Signature { + fn as_ref(&self) -> &[u8] { + &self.inner[..] + } +} + +impl AsMut<[u8]> for Signature { + fn as_mut(&mut self) -> &mut [u8] { + &mut self.inner[..] + } +} + +impl sp_std::fmt::Debug for Signature { + #[cfg(feature = "std")] + fn fmt(&self, f: &mut sp_std::fmt::Formatter) -> sp_std::fmt::Result { + write!(f, "{}", crate::hexdisplay::HexDisplay::from(&self.inner)) + } + + #[cfg(not(feature = "std"))] + fn fmt(&self, _: &mut sp_std::fmt::Formatter) -> sp_std::fmt::Result { + Ok(()) + } +} + +impl UncheckedFrom<[u8; SIGNATURE_SERIALIZED_SIZE]> for Signature { + fn unchecked_from(data: [u8; SIGNATURE_SERIALIZED_SIZE]) -> Self { + Signature { inner: data, _phantom: PhantomData } + } +} + +impl CryptoType for Signature { + #[cfg(feature = "full_crypto")] + type Pair = Pair; +} + +/// A key pair. +#[cfg(feature = "full_crypto")] +pub struct Pair(Keypair); + +#[cfg(feature = "full_crypto")] +impl Clone for Pair { + fn clone(&self) -> Self { + Pair(self.0.clone()) + } +} + +trait HardJunctionId { + const ID: &'static str; +} + +/// Derive a single hard junction. +#[cfg(feature = "full_crypto")] +fn derive_hard_junction(secret_seed: &Seed, cc: &[u8; 32]) -> Seed { + (T::ID, secret_seed, cc).using_encoded(sp_core_hashing::blake2_256) +} + +#[cfg(feature = "full_crypto")] +impl Pair {} + +#[cfg(feature = "full_crypto")] +impl TraitPair for Pair { + type Seed = Seed; + type Public = Public; + type Signature = Signature; + + fn from_seed_slice(seed_slice: &[u8]) -> Result { + if seed_slice.len() != SECRET_KEY_SERIALIZED_SIZE { + return Err(SecretStringError::InvalidSeedLength) + } + let secret = w3f_bls::SecretKey::from_seed(seed_slice); + let public = secret.into_public(); + Ok(Pair(w3f_bls::Keypair { secret, public })) + } + + fn derive>( + &self, + path: Iter, + _seed: Option, + ) -> Result<(Self, Option), DeriveError> { + let mut acc: [u8; SECRET_KEY_SERIALIZED_SIZE] = + self.0.secret.to_bytes().try_into().expect( + "Secret key serializer returns a vector of SECRET_KEY_SERIALIZED_SIZE size", + ); + for j in path { + match j { + DeriveJunction::Soft(_cc) => return Err(DeriveError::SoftKeyInPath), + DeriveJunction::Hard(cc) => acc = derive_hard_junction::(&acc, &cc), + } + } + Ok((Self::from_seed(&acc), Some(acc))) + } + + fn public(&self) -> Self::Public { + let mut raw = [0u8; PUBLIC_KEY_SERIALIZED_SIZE]; + let pk = DoublePublicKeyScheme::into_double_public_key(&self.0).to_bytes(); + raw.copy_from_slice(pk.as_slice()); + Self::Public::unchecked_from(raw) + } + + fn sign(&self, message: &[u8]) -> Self::Signature { + let mut mutable_self = self.clone(); + let r: [u8; SIGNATURE_SERIALIZED_SIZE] = + DoublePublicKeyScheme::sign(&mut mutable_self.0, &Message::new(b"", message)) + .to_bytes() + .try_into() + .expect("Signature serializer returns vectors of SIGNATURE_SERIALIZED_SIZE size"); + Self::Signature::unchecked_from(r) + } + + fn verify>(sig: &Self::Signature, message: M, pubkey: &Self::Public) -> bool { + let pubkey_array: [u8; PUBLIC_KEY_SERIALIZED_SIZE] = + match <[u8; PUBLIC_KEY_SERIALIZED_SIZE]>::try_from(pubkey.as_ref()) { + Ok(pk) => pk, + Err(_) => return false, + }; + let public_key = match w3f_bls::double::DoublePublicKey::::from_bytes(&pubkey_array) { + Ok(pk) => pk, + Err(_) => return false, + }; + + let sig_array = match sig.inner[..].try_into() { + Ok(s) => s, + Err(_) => return false, + }; + let sig = match w3f_bls::double::DoubleSignature::from_bytes(sig_array) { + Ok(s) => s, + Err(_) => return false, + }; + + sig.verify(&Message::new(b"", message.as_ref()), &public_key) + } + + /// Get the seed for this key. + fn to_raw_vec(&self) -> Vec { + self.0 + .secret + .to_bytes() + .try_into() + .expect("Secret key serializer returns a vector of SECRET_KEY_SERIALIZED_SIZE size") + } +} + +#[cfg(feature = "full_crypto")] +impl CryptoType for Pair { + type Pair = Pair; +} + +// Test set exercising the BLS12-377 implementation +#[cfg(test)] +mod test { + use super::*; + use crate::crypto::DEV_PHRASE; + use bls377::{Pair, Signature}; + use hex_literal::hex; + + #[test] + fn default_phrase_should_be_used() { + assert_eq!( + Pair::from_string("//Alice///password", None).unwrap().public(), + Pair::from_string(&format!("{}//Alice", DEV_PHRASE), Some("password")) + .unwrap() + .public(), + ); + } + + // Only passes if the seed = (seed mod ScalarField) + #[test] + fn seed_and_derive_should_work() { + let seed = hex!("9d61b19deffd5a60ba844af492ec2cc44449c5697b326919703bac031cae7f00"); + let pair = Pair::from_seed(&seed); + // we are using hash to field so this is not going to work + // assert_eq!(pair.seed(), seed); + let path = vec![DeriveJunction::Hard([0u8; 32])]; + let derived = pair.derive(path.into_iter(), None).ok().unwrap().0; + assert_eq!( + derived.to_raw_vec(), + hex!("a4f2269333b3e87c577aa00c4a2cd650b3b30b2e8c286a47c251279ff3a26e0d") + ); + } + + #[test] + fn test_vector_should_work() { + let pair = Pair::from_seed(&hex!( + "9d61b19deffd5a60ba844af492ec2cc44449c5697b326919703bac031cae7f60" + )); + let public = pair.public(); + assert_eq!( + public, + Public::unchecked_from(hex!( + "7a84ca8ce4c37c93c95ecee6a3c0c9a7b9c225093cf2f12dc4f69cbfb847ef9424a18f5755d5a742247d386ff2aabb806bcf160eff31293ea9616976628f77266c8a8cc1d8753be04197bd6cdd8c5c87a148f782c4c1568d599b48833fd539001e580cff64bbc71850605433fcd051f3afc3b74819786f815ffb5272030a8d03e5df61e6183f8fd8ea85f26defa83400" + )) + ); + let message = b""; + let signature = + hex!("d1e3013161991e142d8751017d4996209c2ff8a9ee160f373733eda3b4b785ba6edce9f45f87104bbe07aa6aa6eb2780aa705efb2c13d3b317d6409d159d23bdc7cdd5c2a832d1551cf49d811d49c901495e527dbd532e3a462335ce2686009104aba7bc11c5b22be78f3198d2727a0b" + ); + let signature = Signature::unchecked_from(signature); + assert!(pair.sign(&message[..]) == signature); + assert!(Pair::verify(&signature, &message[..], &public)); + } + + #[test] + fn test_vector_by_string_should_work() { + let pair = Pair::from_string( + "0x9d61b19deffd5a60ba844af492ec2cc44449c5697b326919703bac031cae7f60", + None, + ) + .unwrap(); + let public = pair.public(); + assert_eq!( + public, + Public::unchecked_from(hex!( + "6dc6be608fab3c6bd894a606be86db346cc170db85c733853a371f3db54ae1b12052c0888d472760c81b537572a26f00db865e5963aef8634f9917571c51b538b564b2a9ceda938c8b930969ee3b832448e08e33a79e9ddd28af419a3ce45300f5dbc768b067781f44f3fe05a19e6b07b1c4196151ec3f8ea37e4f89a8963030d2101e931276bb9ebe1f20102239d780" + )) + ); + let message = b""; + let signature = + hex!("bbb395bbdee1a35930912034f5fde3b36df2835a0536c865501b0675776a1d5931a3bea2e66eff73b2546c6af2061a8019223e4ebbbed661b2538e0f5823f2c708eb89c406beca8fcb53a5c13dbc7c0c42e4cf2be2942bba96ea29297915a06bd2b1b979c0e2ac8fd4ec684a6b5d110c" + ); + let expected_signature = Signature::unchecked_from(signature); + println!("signature is {:?}", pair.sign(&message[..])); + let signature = pair.sign(&message[..]); + assert!(signature == expected_signature); + assert!(Pair::verify(&signature, &message[..], &public)); + } + #[test] + fn generated_pair_should_work() { + let (pair, _) = Pair::generate(); + let public = pair.public(); + let message = b"Something important"; + let signature = pair.sign(&message[..]); + assert!(Pair::verify(&signature, &message[..], &public)); + assert!(!Pair::verify(&signature, b"Something else", &public)); + } + + #[test] + fn seeded_pair_should_work() { + let pair = Pair::from_seed(b"12345678901234567890123456789012"); + let public = pair.public(); + assert_eq!( + public, + Public::unchecked_from( + hex!( + "754d2f2bbfa67df54d7e0e951979a18a1e0f45948857752cc2bac6bbb0b1d05e8e48bcc453920bf0c4bbd5993212480112a1fb433f04d74af0a8b700d93dc957ab3207f8d071e948f5aca1a7632c00bdf6d06be05b43e2e6216dccc8a5d55a0071cb2313cfd60b7e9114619cd17c06843b352f0b607a99122f6651df8f02e1ad3697bd208e62af047ddd7b942ba80080") + ) + ); + let message = + hex!("2f8c6129d816cf51c374bc7f08c3e63ed156cf78aefb4a6550d97b87997977ee00000000000000000200d75a980182b10ab7d54bfed3c964073a0ee172f3daa62325af021a68f707511a4500000000000000" + ); + let signature = pair.sign(&message[..]); + println!("Correct signature: {:?}", signature); + assert!(Pair::verify(&signature, &message[..], &public)); + assert!(!Pair::verify(&signature, "Other message", &public)); + } + + #[test] + fn generate_with_phrase_recovery_possible() { + let (pair1, phrase, _) = Pair::generate_with_phrase(None); + let (pair2, _) = Pair::from_phrase(&phrase, None).unwrap(); + + assert_eq!(pair1.public(), pair2.public()); + } + + #[test] + fn generate_with_password_phrase_recovery_possible() { + let (pair1, phrase, _) = Pair::generate_with_phrase(Some("password")); + let (pair2, _) = Pair::from_phrase(&phrase, Some("password")).unwrap(); + + assert_eq!(pair1.public(), pair2.public()); + } + + #[test] + fn password_does_something() { + let (pair1, phrase, _) = Pair::generate_with_phrase(Some("password")); + let (pair2, _) = Pair::from_phrase(&phrase, None).unwrap(); + + assert_ne!(pair1.public(), pair2.public()); + } + + #[test] + fn ss58check_roundtrip_works() { + let pair = Pair::from_seed(b"12345678901234567890123456789012"); + let public = pair.public(); + let s = public.to_ss58check(); + println!("Correct: {}", s); + let cmp = Public::from_ss58check(&s).unwrap(); + assert_eq!(cmp, public); + } + + #[test] + fn signature_serialization_works() { + let pair = Pair::from_seed(b"12345678901234567890123456789012"); + let message = b"Something important"; + let signature = pair.sign(&message[..]); + let serialized_signature = serde_json::to_string(&signature).unwrap(); + // Signature is 112 bytes, hexify * 2, so 224 chars + 2 quote chars + assert_eq!(serialized_signature.len(), 226); + let signature = serde_json::from_str(&serialized_signature).unwrap(); + assert!(Pair::verify(&signature, &message[..], &pair.public())); + } + + #[test] + fn signature_serialization_doesnt_panic() { + fn deserialize_signature(text: &str) -> Result { + serde_json::from_str(text) + } + assert!(deserialize_signature("Not valid json.").is_err()); + assert!(deserialize_signature("\"Not an actual signature.\"").is_err()); + // Poorly-sized + assert!(deserialize_signature("\"abc123\"").is_err()); + } +} diff --git a/primitives/core/src/hexdisplay.rs b/primitives/core/src/hexdisplay.rs index 9f35e5ec7..30e045dfc 100644 --- a/primitives/core/src/hexdisplay.rs +++ b/primitives/core/src/hexdisplay.rs @@ -96,7 +96,7 @@ macro_rules! impl_non_endians { impl_non_endians!( [u8; 1], [u8; 2], [u8; 3], [u8; 4], [u8; 5], [u8; 6], [u8; 7], [u8; 8], [u8; 10], [u8; 12], [u8; 14], [u8; 16], [u8; 20], [u8; 24], [u8; 28], [u8; 32], [u8; 40], [u8; 48], [u8; 56], - [u8; 64], [u8; 65], [u8; 80], [u8; 96], [u8; 112], [u8; 128] + [u8; 64], [u8; 65], [u8; 80], [u8; 96], [u8; 112], [u8; 128], [u8; 144] ); /// Format into ASCII + # + hex, suitable for storage key preimages. diff --git a/primitives/core/src/lib.rs b/primitives/core/src/lib.rs index 04f44631c..f9541b02e 100644 --- a/primitives/core/src/lib.rs +++ b/primitives/core/src/lib.rs @@ -55,6 +55,8 @@ pub mod crypto; pub mod hexdisplay; pub use paste; +#[cfg(feature = "bls-experimental")] +pub mod bls; pub mod defer; pub mod ecdsa; pub mod ed25519; @@ -68,6 +70,9 @@ pub mod testing; pub mod traits; pub mod uint; +#[cfg(feature = "bls-experimental")] +pub use bls::{bls377, bls381}; + pub use self::{ hash::{convert_hash, H160, H256, H512}, uint::{U256, U512}, diff --git a/primitives/core/src/testing.rs b/primitives/core/src/testing.rs index 756275aed..6faf4ffa3 100644 --- a/primitives/core/src/testing.rs +++ b/primitives/core/src/testing.rs @@ -25,6 +25,10 @@ pub const ED25519: KeyTypeId = KeyTypeId(*b"ed25"); pub const SR25519: KeyTypeId = KeyTypeId(*b"sr25"); /// Key type for generic ECDSA key. pub const ECDSA: KeyTypeId = KeyTypeId(*b"ecds"); +/// Key type for generic BLS12-377 key. +pub const BLS377: KeyTypeId = KeyTypeId(*b"bls7"); +/// Key type for generic BLS12-381 key. +pub const BLS381: KeyTypeId = KeyTypeId(*b"bls8"); /// Macro for exporting functions from wasm in with the expected signature for using it with the /// wasm executor. This is useful for tests where you need to call a function in wasm. diff --git a/primitives/keystore/Cargo.toml b/primitives/keystore/Cargo.toml index 6b0194247..a749b95a4 100644 --- a/primitives/keystore/Cargo.toml +++ b/primitives/keystore/Cargo.toml @@ -33,3 +33,7 @@ std = [ "sp-core/std", "sp-externalities/std", ] + +# This feature adds BLS crypto primitives. It should not be used in production since +# the BLS implementation and interface may still be subject to significant change. +bls-experimental = ["sp-core/bls-experimental"] diff --git a/primitives/keystore/src/lib.rs b/primitives/keystore/src/lib.rs index d6165088f..1d2a27cb8 100644 --- a/primitives/keystore/src/lib.rs +++ b/primitives/keystore/src/lib.rs @@ -19,10 +19,13 @@ pub mod testing; +#[cfg(feature = "bls-experimental")] +use sp_core::{bls377, bls381}; use sp_core::{ crypto::{ByteArray, CryptoTypeId, KeyTypeId}, ecdsa, ed25519, sr25519, }; + use std::sync::Arc; /// Keystore error @@ -171,6 +174,68 @@ pub trait Keystore: Send + Sync { msg: &[u8; 32], ) -> Result, Error>; + #[cfg(feature = "bls-experimental")] + /// Returns all bls12-381 public keys for the given key type. + fn bls381_public_keys(&self, id: KeyTypeId) -> Vec; + + #[cfg(feature = "bls-experimental")] + /// Returns all bls12-377 public keys for the given key type. + fn bls377_public_keys(&self, id: KeyTypeId) -> Vec; + + #[cfg(feature = "bls-experimental")] + /// Generate a new bls381 key pair for the given key type and an optional seed. + /// + /// Returns an `bls381::Public` key of the generated key pair or an `Err` if + /// something failed during key generation. + fn bls381_generate_new( + &self, + key_type: KeyTypeId, + seed: Option<&str>, + ) -> Result; + + #[cfg(feature = "bls-experimental")] + /// Generate a new bls377 key pair for the given key type and an optional seed. + /// + /// Returns an `bls377::Public` key of the generated key pair or an `Err` if + /// something failed during key generation. + fn bls377_generate_new( + &self, + key_type: KeyTypeId, + seed: Option<&str>, + ) -> Result; + + #[cfg(feature = "bls-experimental")] + /// Generate a bls381 signature for a given message. + /// + /// Receives [`KeyTypeId`] and a [`bls381::Public`] key to be able to map + /// them to a private key that exists in the keystore. + /// + /// Returns an [`bls381::Signature`] or `None` in case the given `key_type` + /// and `public` combination doesn't exist in the keystore. + /// An `Err` will be returned if generating the signature itself failed. + fn bls381_sign( + &self, + key_type: KeyTypeId, + public: &bls381::Public, + msg: &[u8], + ) -> Result, Error>; + + #[cfg(feature = "bls-experimental")] + /// Generate a bls377 signature for a given message. + /// + /// Receives [`KeyTypeId`] and a [`bls377::Public`] key to be able to map + /// them to a private key that exists in the keystore. + /// + /// Returns an [`bls377::Signature`] or `None` in case the given `key_type` + /// and `public` combination doesn't exist in the keystore. + /// An `Err` will be returned if generating the signature itself failed. + fn bls377_sign( + &self, + key_type: KeyTypeId, + public: &bls377::Public, + msg: &[u8], + ) -> Result, Error>; + /// Insert a new secret key. fn insert(&self, key_type: KeyTypeId, suri: &str, public: &[u8]) -> Result<(), ()>; @@ -189,7 +254,13 @@ pub trait Keystore: Send + Sync { /// /// The message is signed using the cryptographic primitive specified by `crypto_id`. /// - /// Schemes supported by the default trait implementation: sr25519, ed25519 and ecdsa. + /// Schemes supported by the default trait implementation: + /// - sr25519 + /// - ed25519 + /// - ecdsa + /// - bls381 + /// - bls377 + /// /// To support more schemes you can overwrite this method. /// /// Returns the SCALE encoded signature if key is found and supported, `None` if the key doesn't @@ -217,8 +288,21 @@ pub trait Keystore: Send + Sync { ecdsa::CRYPTO_ID => { let public = ecdsa::Public::from_slice(public) .map_err(|_| Error::ValidationError("Invalid public key format".into()))?; + self.ecdsa_sign(id, &public, msg)?.map(|s| s.encode()) }, + #[cfg(feature = "bls-experimental")] + bls381::CRYPTO_ID => { + let public = bls381::Public::from_slice(public) + .map_err(|_| Error::ValidationError("Invalid public key format".into()))?; + self.bls381_sign(id, &public, msg)?.map(|s| s.encode()) + }, + #[cfg(feature = "bls-experimental")] + bls377::CRYPTO_ID => { + let public = bls377::Public::from_slice(public) + .map_err(|_| Error::ValidationError("Invalid public key format".into()))?; + self.bls377_sign(id, &public, msg)?.map(|s| s.encode()) + }, _ => return Err(Error::KeyNotSupported(id)), }; Ok(signature) diff --git a/primitives/keystore/src/testing.rs b/primitives/keystore/src/testing.rs index dd3254e93..e18931a7a 100644 --- a/primitives/keystore/src/testing.rs +++ b/primitives/keystore/src/testing.rs @@ -19,6 +19,8 @@ use crate::{Error, Keystore, KeystorePtr}; +#[cfg(feature = "bls-experimental")] +use sp_core::{bls377, bls381}; use sp_core::{ crypto::{ByteArray, KeyTypeId, Pair, VrfSecret}, ecdsa, ed25519, sr25519, @@ -212,6 +214,54 @@ impl Keystore for MemoryKeystore { Ok(sig) } + #[cfg(feature = "bls-experimental")] + fn bls381_public_keys(&self, key_type: KeyTypeId) -> Vec { + self.public_keys::(key_type) + } + + #[cfg(feature = "bls-experimental")] + fn bls381_generate_new( + &self, + key_type: KeyTypeId, + seed: Option<&str>, + ) -> Result { + self.generate_new::(key_type, seed) + } + + #[cfg(feature = "bls-experimental")] + fn bls381_sign( + &self, + key_type: KeyTypeId, + public: &bls381::Public, + msg: &[u8], + ) -> Result, Error> { + self.sign::(key_type, public, msg) + } + + #[cfg(feature = "bls-experimental")] + fn bls377_public_keys(&self, key_type: KeyTypeId) -> Vec { + self.public_keys::(key_type) + } + + #[cfg(feature = "bls-experimental")] + fn bls377_generate_new( + &self, + key_type: KeyTypeId, + seed: Option<&str>, + ) -> Result { + self.generate_new::(key_type, seed) + } + + #[cfg(feature = "bls-experimental")] + fn bls377_sign( + &self, + key_type: KeyTypeId, + public: &bls377::Public, + msg: &[u8], + ) -> Result, Error> { + self.sign::(key_type, public, msg) + } + fn insert(&self, key_type: KeyTypeId, suri: &str, public: &[u8]) -> Result<(), ()> { self.keys .write() From 725208a5b87ae8388f3a9fa6590301c93a8a5c69 Mon Sep 17 00:00:00 2001 From: Koute Date: Wed, 10 May 2023 18:39:43 +0900 Subject: [PATCH 480/558] Remove the `Copy` bound on `CollectionId` in the uniques pallet (#14111) * Remove the `Copy` bound on `CollectionId` in the uniques pallet * Also add `clone`s in benchmarks --- frame/uniques/src/benchmarking.rs | 106 ++++++++++++------------- frame/uniques/src/functions.rs | 17 ++-- frame/uniques/src/impl_nonfungibles.rs | 14 ++-- frame/uniques/src/lib.rs | 44 +++++----- 4 files changed, 96 insertions(+), 85 deletions(-) diff --git a/frame/uniques/src/benchmarking.rs b/frame/uniques/src/benchmarking.rs index 6d1795142..4e63f6928 100644 --- a/frame/uniques/src/benchmarking.rs +++ b/frame/uniques/src/benchmarking.rs @@ -44,7 +44,7 @@ fn create_collection, I: 'static>( T::Currency::make_free_balance_be(&caller, DepositBalanceOf::::max_value()); assert!(Uniques::::force_create( SystemOrigin::Root.into(), - collection, + collection.clone(), caller_lookup.clone(), false, ) @@ -173,26 +173,26 @@ benchmarks_instance_pallet! { for i in 0..a { add_item_attribute::(T::Helper::item(i as u16)); } - let witness = Collection::::get(collection).unwrap().destroy_witness(); - }: _(SystemOrigin::Signed(caller), collection, witness) + let witness = Collection::::get(collection.clone()).unwrap().destroy_witness(); + }: _(SystemOrigin::Signed(caller), collection.clone(), witness) verify { - assert_last_event::(Event::Destroyed { collection }.into()); + assert_last_event::(Event::Destroyed { collection: collection.clone() }.into()); } mint { let (collection, caller, caller_lookup) = create_collection::(); let item = T::Helper::item(0); - }: _(SystemOrigin::Signed(caller.clone()), collection, item, caller_lookup) + }: _(SystemOrigin::Signed(caller.clone()), collection.clone(), item, caller_lookup) verify { - assert_last_event::(Event::Issued { collection, item, owner: caller }.into()); + assert_last_event::(Event::Issued { collection: collection.clone(), item, owner: caller }.into()); } burn { let (collection, caller, caller_lookup) = create_collection::(); let (item, ..) = mint_item::(0); - }: _(SystemOrigin::Signed(caller.clone()), collection, item, Some(caller_lookup)) + }: _(SystemOrigin::Signed(caller.clone()), collection.clone(), item, Some(caller_lookup)) verify { - assert_last_event::(Event::Burned { collection, item, owner: caller }.into()); + assert_last_event::(Event::Burned { collection: collection.clone(), item, owner: caller }.into()); } transfer { @@ -201,9 +201,9 @@ benchmarks_instance_pallet! { let target: T::AccountId = account("target", 0, SEED); let target_lookup = T::Lookup::unlookup(target.clone()); - }: _(SystemOrigin::Signed(caller.clone()), collection, item, target_lookup) + }: _(SystemOrigin::Signed(caller.clone()), collection.clone(), item, target_lookup) verify { - assert_last_event::(Event::Transferred { collection, item, from: caller, to: target }.into()); + assert_last_event::(Event::Transferred { collection: collection.clone(), item, from: caller, to: target }.into()); } redeposit { @@ -212,7 +212,7 @@ benchmarks_instance_pallet! { let items = (0..i).map(|x| mint_item::(x as u16).0).collect::>(); Uniques::::force_item_status( SystemOrigin::Root.into(), - collection, + collection.clone(), caller_lookup.clone(), caller_lookup.clone(), caller_lookup.clone(), @@ -220,9 +220,9 @@ benchmarks_instance_pallet! { true, false, )?; - }: _(SystemOrigin::Signed(caller.clone()), collection, items.clone()) + }: _(SystemOrigin::Signed(caller.clone()), collection.clone(), items.clone()) verify { - assert_last_event::(Event::Redeposited { collection, successful_items: items }.into()); + assert_last_event::(Event::Redeposited { collection: collection.clone(), successful_items: items }.into()); } freeze { @@ -238,28 +238,28 @@ benchmarks_instance_pallet! { let (item, ..) = mint_item::(0); Uniques::::freeze( SystemOrigin::Signed(caller.clone()).into(), - collection, + collection.clone(), item, )?; - }: _(SystemOrigin::Signed(caller.clone()), collection, item) + }: _(SystemOrigin::Signed(caller.clone()), collection.clone(), item) verify { - assert_last_event::(Event::Thawed { collection, item }.into()); + assert_last_event::(Event::Thawed { collection: collection.clone(), item }.into()); } freeze_collection { let (collection, caller, caller_lookup) = create_collection::(); - }: _(SystemOrigin::Signed(caller.clone()), collection) + }: _(SystemOrigin::Signed(caller.clone()), collection.clone()) verify { - assert_last_event::(Event::CollectionFrozen { collection }.into()); + assert_last_event::(Event::CollectionFrozen { collection: collection.clone() }.into()); } thaw_collection { let (collection, caller, caller_lookup) = create_collection::(); let origin = SystemOrigin::Signed(caller.clone()).into(); - Uniques::::freeze_collection(origin, collection)?; - }: _(SystemOrigin::Signed(caller.clone()), collection) + Uniques::::freeze_collection(origin, collection.clone())?; + }: _(SystemOrigin::Signed(caller.clone()), collection.clone()) verify { - assert_last_event::(Event::CollectionThawed { collection }.into()); + assert_last_event::(Event::CollectionThawed { collection: collection.clone() }.into()); } transfer_ownership { @@ -268,10 +268,10 @@ benchmarks_instance_pallet! { let target_lookup = T::Lookup::unlookup(target.clone()); T::Currency::make_free_balance_be(&target, T::Currency::minimum_balance()); let origin = SystemOrigin::Signed(target.clone()).into(); - Uniques::::set_accept_ownership(origin, Some(collection))?; - }: _(SystemOrigin::Signed(caller), collection, target_lookup) + Uniques::::set_accept_ownership(origin, Some(collection.clone()))?; + }: _(SystemOrigin::Signed(caller), collection.clone(), target_lookup) verify { - assert_last_event::(Event::OwnerChanged { collection, new_owner: target }.into()); + assert_last_event::(Event::OwnerChanged { collection: collection.clone(), new_owner: target }.into()); } set_team { @@ -279,10 +279,10 @@ benchmarks_instance_pallet! { let target0 = T::Lookup::unlookup(account("target", 0, SEED)); let target1 = T::Lookup::unlookup(account("target", 1, SEED)); let target2 = T::Lookup::unlookup(account("target", 2, SEED)); - }: _(SystemOrigin::Signed(caller), collection, target0, target1, target2) + }: _(SystemOrigin::Signed(caller), collection.clone(), target0, target1, target2) verify { assert_last_event::(Event::TeamChanged{ - collection, + collection: collection.clone(), issuer: account("target", 0, SEED), admin: account("target", 1, SEED), freezer: account("target", 2, SEED), @@ -294,7 +294,7 @@ benchmarks_instance_pallet! { let origin = T::ForceOrigin::try_successful_origin().map_err(|_| BenchmarkError::Weightless)?; let call = Call::::force_item_status { - collection, + collection: collection.clone(), owner: caller_lookup.clone(), issuer: caller_lookup.clone(), admin: caller_lookup.clone(), @@ -304,7 +304,7 @@ benchmarks_instance_pallet! { }; }: { call.dispatch_bypass_filter(origin)? } verify { - assert_last_event::(Event::ItemStatusChanged { collection }.into()); + assert_last_event::(Event::ItemStatusChanged { collection: collection.clone() }.into()); } set_attribute { @@ -314,9 +314,9 @@ benchmarks_instance_pallet! { let (collection, caller, _) = create_collection::(); let (item, ..) = mint_item::(0); add_item_metadata::(item); - }: _(SystemOrigin::Signed(caller), collection, Some(item), key.clone(), value.clone()) + }: _(SystemOrigin::Signed(caller), collection.clone(), Some(item), key.clone(), value.clone()) verify { - assert_last_event::(Event::AttributeSet { collection, maybe_item: Some(item), key, value }.into()); + assert_last_event::(Event::AttributeSet { collection: collection.clone(), maybe_item: Some(item), key, value }.into()); } clear_attribute { @@ -324,9 +324,9 @@ benchmarks_instance_pallet! { let (item, ..) = mint_item::(0); add_item_metadata::(item); let (key, ..) = add_item_attribute::(item); - }: _(SystemOrigin::Signed(caller), collection, Some(item), key.clone()) + }: _(SystemOrigin::Signed(caller), collection.clone(), Some(item), key.clone()) verify { - assert_last_event::(Event::AttributeCleared { collection, maybe_item: Some(item), key }.into()); + assert_last_event::(Event::AttributeCleared { collection: collection.clone(), maybe_item: Some(item), key }.into()); } set_metadata { @@ -334,35 +334,35 @@ benchmarks_instance_pallet! { let (collection, caller, _) = create_collection::(); let (item, ..) = mint_item::(0); - }: _(SystemOrigin::Signed(caller), collection, item, data.clone(), false) + }: _(SystemOrigin::Signed(caller), collection.clone(), item, data.clone(), false) verify { - assert_last_event::(Event::MetadataSet { collection, item, data, is_frozen: false }.into()); + assert_last_event::(Event::MetadataSet { collection: collection.clone(), item, data, is_frozen: false }.into()); } clear_metadata { let (collection, caller, _) = create_collection::(); let (item, ..) = mint_item::(0); add_item_metadata::(item); - }: _(SystemOrigin::Signed(caller), collection, item) + }: _(SystemOrigin::Signed(caller), collection.clone(), item) verify { - assert_last_event::(Event::MetadataCleared { collection, item }.into()); + assert_last_event::(Event::MetadataCleared { collection: collection.clone(), item }.into()); } set_collection_metadata { let data: BoundedVec<_, _> = vec![0u8; T::StringLimit::get() as usize].try_into().unwrap(); let (collection, caller, _) = create_collection::(); - }: _(SystemOrigin::Signed(caller), collection, data.clone(), false) + }: _(SystemOrigin::Signed(caller), collection.clone(), data.clone(), false) verify { - assert_last_event::(Event::CollectionMetadataSet { collection, data, is_frozen: false }.into()); + assert_last_event::(Event::CollectionMetadataSet { collection: collection.clone(), data, is_frozen: false }.into()); } clear_collection_metadata { let (collection, caller, _) = create_collection::(); add_collection_metadata::(); - }: _(SystemOrigin::Signed(caller), collection) + }: _(SystemOrigin::Signed(caller), collection.clone()) verify { - assert_last_event::(Event::CollectionMetadataCleared { collection }.into()); + assert_last_event::(Event::CollectionMetadataCleared { collection: collection.clone() }.into()); } approve_transfer { @@ -370,9 +370,9 @@ benchmarks_instance_pallet! { let (item, ..) = mint_item::(0); let delegate: T::AccountId = account("delegate", 0, SEED); let delegate_lookup = T::Lookup::unlookup(delegate.clone()); - }: _(SystemOrigin::Signed(caller.clone()), collection, item, delegate_lookup) + }: _(SystemOrigin::Signed(caller.clone()), collection.clone(), item, delegate_lookup) verify { - assert_last_event::(Event::ApprovedTransfer { collection, item, owner: caller, delegate }.into()); + assert_last_event::(Event::ApprovedTransfer { collection: collection.clone(), item, owner: caller, delegate }.into()); } cancel_approval { @@ -381,17 +381,17 @@ benchmarks_instance_pallet! { let delegate: T::AccountId = account("delegate", 0, SEED); let delegate_lookup = T::Lookup::unlookup(delegate.clone()); let origin = SystemOrigin::Signed(caller.clone()).into(); - Uniques::::approve_transfer(origin, collection, item, delegate_lookup.clone())?; - }: _(SystemOrigin::Signed(caller.clone()), collection, item, Some(delegate_lookup)) + Uniques::::approve_transfer(origin, collection.clone(), item, delegate_lookup.clone())?; + }: _(SystemOrigin::Signed(caller.clone()), collection.clone(), item, Some(delegate_lookup)) verify { - assert_last_event::(Event::ApprovalCancelled { collection, item, owner: caller, delegate }.into()); + assert_last_event::(Event::ApprovalCancelled { collection: collection.clone(), item, owner: caller, delegate }.into()); } set_accept_ownership { let caller: T::AccountId = whitelisted_caller(); T::Currency::make_free_balance_be(&caller, DepositBalanceOf::::max_value()); let collection = T::Helper::collection(0); - }: _(SystemOrigin::Signed(caller.clone()), Some(collection)) + }: _(SystemOrigin::Signed(caller.clone()), Some(collection.clone())) verify { assert_last_event::(Event::OwnershipAcceptanceChanged { who: caller, @@ -401,10 +401,10 @@ benchmarks_instance_pallet! { set_collection_max_supply { let (collection, caller, _) = create_collection::(); - }: _(SystemOrigin::Signed(caller.clone()), collection, u32::MAX) + }: _(SystemOrigin::Signed(caller.clone()), collection.clone(), u32::MAX) verify { assert_last_event::(Event::CollectionMaxSupplySet { - collection, + collection: collection.clone(), max_supply: u32::MAX, }.into()); } @@ -415,10 +415,10 @@ benchmarks_instance_pallet! { let delegate: T::AccountId = account("delegate", 0, SEED); let delegate_lookup = T::Lookup::unlookup(delegate.clone()); let price = ItemPrice::::from(100u32); - }: _(SystemOrigin::Signed(caller.clone()), collection, item, Some(price), Some(delegate_lookup)) + }: _(SystemOrigin::Signed(caller.clone()), collection.clone(), item, Some(price), Some(delegate_lookup)) verify { assert_last_event::(Event::ItemPriceSet { - collection, + collection: collection.clone(), item, price, whitelisted_buyer: Some(delegate), @@ -432,12 +432,12 @@ benchmarks_instance_pallet! { let buyer_lookup = T::Lookup::unlookup(buyer.clone()); let price = ItemPrice::::from(0u32); let origin = SystemOrigin::Signed(seller.clone()).into(); - Uniques::::set_price(origin, collection, item, Some(price.clone()), Some(buyer_lookup))?; + Uniques::::set_price(origin, collection.clone(), item, Some(price.clone()), Some(buyer_lookup))?; T::Currency::make_free_balance_be(&buyer, DepositBalanceOf::::max_value()); - }: _(SystemOrigin::Signed(buyer.clone()), collection, item, price.clone()) + }: _(SystemOrigin::Signed(buyer.clone()), collection.clone(), item, price.clone()) verify { assert_last_event::(Event::ItemBought { - collection, + collection: collection.clone(), item, price, seller, diff --git a/frame/uniques/src/functions.rs b/frame/uniques/src/functions.rs index 1aa79134d..681ad06a8 100644 --- a/frame/uniques/src/functions.rs +++ b/frame/uniques/src/functions.rs @@ -37,7 +37,7 @@ impl, I: 'static> Pallet { let collection_details = Collection::::get(&collection).ok_or(Error::::UnknownCollection)?; ensure!(!collection_details.is_frozen, Error::::Frozen); - ensure!(!T::Locker::is_locked(collection, item), Error::::Locked); + ensure!(!T::Locker::is_locked(collection.clone(), item), Error::::Locked); let mut details = Item::::get(&collection, &item).ok_or(Error::::UnknownCollection)?; @@ -74,12 +74,12 @@ impl, I: 'static> Pallet { free_holding: bool, event: Event, ) -> DispatchResult { - ensure!(!Collection::::contains_key(collection), Error::::InUse); + ensure!(!Collection::::contains_key(collection.clone()), Error::::InUse); T::Currency::reserve(&owner, deposit)?; Collection::::insert( - collection, + collection.clone(), CollectionDetails { owner: owner.clone(), issuer: admin.clone(), @@ -104,7 +104,7 @@ impl, I: 'static> Pallet { witness: DestroyWitness, maybe_check_owner: Option, ) -> Result { - Collection::::try_mutate_exists(collection, |maybe_details| { + Collection::::try_mutate_exists(collection.clone(), |maybe_details| { let collection_details = maybe_details.take().ok_or(Error::::UnknownCollection)?; if let Some(check_owner) = maybe_check_owner { @@ -147,7 +147,10 @@ impl, I: 'static> Pallet { owner: T::AccountId, with_details: impl FnOnce(&CollectionDetailsFor) -> DispatchResult, ) -> DispatchResult { - ensure!(!Item::::contains_key(collection, item), Error::::AlreadyExists); + ensure!( + !Item::::contains_key(collection.clone(), item), + Error::::AlreadyExists + ); Collection::::try_mutate( &collection, @@ -189,7 +192,7 @@ impl, I: 'static> Pallet { item: T::ItemId, with_details: impl FnOnce(&CollectionDetailsFor, &ItemDetailsFor) -> DispatchResult, ) -> DispatchResult { - ensure!(!T::Locker::is_locked(collection, item), Error::::Locked); + ensure!(!T::Locker::is_locked(collection.clone(), item), Error::::Locked); let owner = Collection::::try_mutate( &collection, |maybe_collection_details| -> Result { @@ -268,7 +271,7 @@ impl, I: 'static> Pallet { let old_owner = details.owner.clone(); - Self::do_transfer(collection, item, buyer.clone(), |_, _| Ok(()))?; + Self::do_transfer(collection.clone(), item, buyer.clone(), |_, _| Ok(()))?; Self::deposit_event(Event::ItemBought { collection, diff --git a/frame/uniques/src/impl_nonfungibles.rs b/frame/uniques/src/impl_nonfungibles.rs index e8bef4e3f..0ae055a98 100644 --- a/frame/uniques/src/impl_nonfungibles.rs +++ b/frame/uniques/src/impl_nonfungibles.rs @@ -94,12 +94,16 @@ impl, I: 'static> Create<::AccountId> for Pallet admin: &T::AccountId, ) -> DispatchResult { Self::do_create_collection( - *collection, + collection.clone(), who.clone(), admin.clone(), T::CollectionDeposit::get(), false, - Event::Created { collection: *collection, creator: who.clone(), owner: admin.clone() }, + Event::Created { + collection: collection.clone(), + creator: who.clone(), + owner: admin.clone(), + }, ) } } @@ -126,7 +130,7 @@ impl, I: 'static> Mutate<::AccountId> for Pallet item: &Self::ItemId, who: &T::AccountId, ) -> DispatchResult { - Self::do_mint(*collection, *item, who.clone(), |_| Ok(())) + Self::do_mint(collection.clone(), *item, who.clone(), |_| Ok(())) } fn burn( @@ -134,7 +138,7 @@ impl, I: 'static> Mutate<::AccountId> for Pallet item: &Self::ItemId, maybe_check_owner: Option<&T::AccountId>, ) -> DispatchResult { - Self::do_burn(*collection, *item, |_, d| { + Self::do_burn(collection.clone(), *item, |_, d| { if let Some(check_owner) = maybe_check_owner { if &d.owner != check_owner { return Err(Error::::NoPermission.into()) @@ -151,7 +155,7 @@ impl, I: 'static> Transfer for Pallet { item: &Self::ItemId, destination: &T::AccountId, ) -> DispatchResult { - Self::do_transfer(*collection, *item, destination.clone(), |_, _| Ok(())) + Self::do_transfer(collection.clone(), *item, destination.clone(), |_, _| Ok(())) } } diff --git a/frame/uniques/src/lib.rs b/frame/uniques/src/lib.rs index fd94bd3a9..72ec02cf1 100644 --- a/frame/uniques/src/lib.rs +++ b/frame/uniques/src/lib.rs @@ -96,7 +96,7 @@ pub mod pallet { + IsType<::RuntimeEvent>; /// Identifier for the collection of item. - type CollectionId: Member + Parameter + MaxEncodedLen + Copy; + type CollectionId: Member + Parameter + MaxEncodedLen; /// The type used to identify a unique item within a collection. type ItemId: Member + Parameter + MaxEncodedLen + Copy; @@ -461,7 +461,7 @@ pub mod pallet { let admin = T::Lookup::lookup(admin)?; Self::do_create_collection( - collection, + collection.clone(), owner.clone(), admin.clone(), T::CollectionDeposit::get(), @@ -499,7 +499,7 @@ pub mod pallet { let owner = T::Lookup::lookup(owner)?; Self::do_create_collection( - collection, + collection.clone(), owner.clone(), owner.clone(), Zero::zero(), @@ -799,7 +799,7 @@ pub mod pallet { ) -> DispatchResult { let origin = ensure_signed(origin)?; - Collection::::try_mutate(collection, |maybe_details| { + Collection::::try_mutate(collection.clone(), |maybe_details| { let details = maybe_details.as_mut().ok_or(Error::::UnknownCollection)?; ensure!(origin == details.freezer, Error::::NoPermission); @@ -827,7 +827,7 @@ pub mod pallet { ) -> DispatchResult { let origin = ensure_signed(origin)?; - Collection::::try_mutate(collection, |maybe_details| { + Collection::::try_mutate(collection.clone(), |maybe_details| { let details = maybe_details.as_mut().ok_or(Error::::UnknownCollection)?; ensure!(origin == details.admin, Error::::NoPermission); @@ -862,7 +862,7 @@ pub mod pallet { let acceptable_collection = OwnershipAcceptance::::get(&owner); ensure!(acceptable_collection.as_ref() == Some(&collection), Error::::Unaccepted); - Collection::::try_mutate(collection, |maybe_details| { + Collection::::try_mutate(collection.clone(), |maybe_details| { let details = maybe_details.as_mut().ok_or(Error::::UnknownCollection)?; ensure!(origin == details.owner, Error::::NoPermission); if details.owner == owner { @@ -912,7 +912,7 @@ pub mod pallet { let admin = T::Lookup::lookup(admin)?; let freezer = T::Lookup::lookup(freezer)?; - Collection::::try_mutate(collection, |maybe_details| { + Collection::::try_mutate(collection.clone(), |maybe_details| { let details = maybe_details.as_mut().ok_or(Error::::UnknownCollection)?; ensure!(origin == details.owner, Error::::NoPermission); @@ -1060,7 +1060,7 @@ pub mod pallet { ) -> DispatchResult { T::ForceOrigin::ensure_origin(origin)?; - Collection::::try_mutate(collection, |maybe_item| { + Collection::::try_mutate(collection.clone(), |maybe_item| { let mut item = maybe_item.take().ok_or(Error::::UnknownCollection)?; let old_owner = item.owner; let new_owner = T::Lookup::lookup(owner)?; @@ -1115,12 +1115,13 @@ pub mod pallet { ensure!(check_owner == &collection_details.owner, Error::::NoPermission); } let maybe_is_frozen = match maybe_item { - None => CollectionMetadataOf::::get(collection).map(|v| v.is_frozen), - Some(item) => ItemMetadataOf::::get(collection, item).map(|v| v.is_frozen), + None => CollectionMetadataOf::::get(collection.clone()).map(|v| v.is_frozen), + Some(item) => + ItemMetadataOf::::get(collection.clone(), item).map(|v| v.is_frozen), }; ensure!(!maybe_is_frozen.unwrap_or(false), Error::::Frozen); - let attribute = Attribute::::get((collection, maybe_item, &key)); + let attribute = Attribute::::get((collection.clone(), maybe_item, &key)); if attribute.is_none() { collection_details.attributes.saturating_inc(); } @@ -1140,7 +1141,7 @@ pub mod pallet { } Attribute::::insert((&collection, maybe_item, &key), (&value, deposit)); - Collection::::insert(collection, &collection_details); + Collection::::insert(collection.clone(), &collection_details); Self::deposit_event(Event::AttributeSet { collection, maybe_item, key, value }); Ok(()) } @@ -1177,16 +1178,19 @@ pub mod pallet { ensure!(check_owner == &collection_details.owner, Error::::NoPermission); } let maybe_is_frozen = match maybe_item { - None => CollectionMetadataOf::::get(collection).map(|v| v.is_frozen), - Some(item) => ItemMetadataOf::::get(collection, item).map(|v| v.is_frozen), + None => CollectionMetadataOf::::get(collection.clone()).map(|v| v.is_frozen), + Some(item) => + ItemMetadataOf::::get(collection.clone(), item).map(|v| v.is_frozen), }; ensure!(!maybe_is_frozen.unwrap_or(false), Error::::Frozen); - if let Some((_, deposit)) = Attribute::::take((collection, maybe_item, &key)) { + if let Some((_, deposit)) = + Attribute::::take((collection.clone(), maybe_item, &key)) + { collection_details.attributes.saturating_dec(); collection_details.total_deposit.saturating_reduce(deposit); T::Currency::unreserve(&collection_details.owner, deposit); - Collection::::insert(collection, &collection_details); + Collection::::insert(collection.clone(), &collection_details); Self::deposit_event(Event::AttributeCleared { collection, maybe_item, key }); } Ok(()) @@ -1229,7 +1233,7 @@ pub mod pallet { ensure!(check_owner == &collection_details.owner, Error::::NoPermission); } - ItemMetadataOf::::try_mutate_exists(collection, item, |metadata| { + ItemMetadataOf::::try_mutate_exists(collection.clone(), item, |metadata| { let was_frozen = metadata.as_ref().map_or(false, |m| m.is_frozen); ensure!(maybe_check_owner.is_none() || !was_frozen, Error::::Frozen); @@ -1289,7 +1293,7 @@ pub mod pallet { ensure!(check_owner == &collection_details.owner, Error::::NoPermission); } - ItemMetadataOf::::try_mutate_exists(collection, item, |metadata| { + ItemMetadataOf::::try_mutate_exists(collection.clone(), item, |metadata| { let was_frozen = metadata.as_ref().map_or(false, |m| m.is_frozen); ensure!(maybe_check_owner.is_none() || !was_frozen, Error::::Frozen); @@ -1340,7 +1344,7 @@ pub mod pallet { ensure!(check_owner == &details.owner, Error::::NoPermission); } - CollectionMetadataOf::::try_mutate_exists(collection, |metadata| { + CollectionMetadataOf::::try_mutate_exists(collection.clone(), |metadata| { let was_frozen = metadata.as_ref().map_or(false, |m| m.is_frozen); ensure!(maybe_check_owner.is_none() || !was_frozen, Error::::Frozen); @@ -1396,7 +1400,7 @@ pub mod pallet { ensure!(check_owner == &details.owner, Error::::NoPermission); } - CollectionMetadataOf::::try_mutate_exists(collection, |metadata| { + CollectionMetadataOf::::try_mutate_exists(collection.clone(), |metadata| { let was_frozen = metadata.as_ref().map_or(false, |m| m.is_frozen); ensure!(maybe_check_owner.is_none() || !was_frozen, Error::::Frozen); From 14855e63b879a26f23186ed2a3ec4a8ad8a7c019 Mon Sep 17 00:00:00 2001 From: Gavin Wood Date: Wed, 10 May 2023 10:41:46 +0100 Subject: [PATCH 481/558] Timeout only if the referendum is not queued (#14106) * Timeout only if the referendum is not queued * ".git/.scripts/commands/fmt/fmt.sh" --------- Co-authored-by: command-bot <> --- frame/referenda/src/lib.rs | 20 ++++++++++++++------ frame/referenda/src/tests.rs | 2 ++ 2 files changed, 16 insertions(+), 6 deletions(-) diff --git a/frame/referenda/src/lib.rs b/frame/referenda/src/lib.rs index 68837376c..dd352d0af 100644 --- a/frame/referenda/src/lib.rs +++ b/frame/referenda/src/lib.rs @@ -79,7 +79,7 @@ use frame_support::{ }; use scale_info::TypeInfo; use sp_runtime::{ - traits::{AtLeast32BitUnsigned, Dispatchable, One, Saturating, Zero}, + traits::{AtLeast32BitUnsigned, Bounded, Dispatchable, One, Saturating, Zero}, DispatchError, Perbill, }; use sp_std::{fmt::Debug, prelude::*}; @@ -1054,9 +1054,9 @@ impl, I: 'static> Pallet { Some(x) => x, None => return (ReferendumInfo::Ongoing(status), false, ServiceBranch::Fail), }; + // Default the alarm to the end of the world. let timeout = status.submitted + T::UndecidingTimeout::get(); - // Default the alarm to the submission timeout. - let mut alarm = timeout; + let mut alarm = T::BlockNumber::max_value(); let branch; match &mut status.deciding { None => { @@ -1097,11 +1097,12 @@ impl, I: 'static> Pallet { ServiceBranch::Preparing } } else { + alarm = timeout; ServiceBranch::NoDeposit } } // If we didn't move into being decided, then check the timeout. - if status.deciding.is_none() && now >= timeout { + if status.deciding.is_none() && now >= timeout && !status.in_queue { // Too long without being decided - end it. Self::ensure_no_alarm(&mut status); Self::deposit_event(Event::::TimedOut { index, tally: status.tally }); @@ -1186,7 +1187,11 @@ impl, I: 'static> Pallet { }, } - let dirty_alarm = Self::ensure_alarm_at(&mut status, index, alarm); + let dirty_alarm = if alarm < T::BlockNumber::max_value() { + Self::ensure_alarm_at(&mut status, index, alarm) + } else { + Self::ensure_no_alarm(&mut status) + }; (ReferendumInfo::Ongoing(status), dirty_alarm || dirty, branch) } @@ -1210,10 +1215,13 @@ impl, I: 'static> Pallet { } /// Cancel the alarm in `status`, if one exists. - fn ensure_no_alarm(status: &mut ReferendumStatusOf) { + fn ensure_no_alarm(status: &mut ReferendumStatusOf) -> bool { if let Some((_, last_alarm)) = status.alarm.take() { // Incorrect alarm - cancel it. let _ = T::Scheduler::cancel(last_alarm); + true + } else { + false } } diff --git a/frame/referenda/src/tests.rs b/frame/referenda/src/tests.rs index 0a1561d00..39f1945bf 100644 --- a/frame/referenda/src/tests.rs +++ b/frame/referenda/src/tests.rs @@ -190,6 +190,8 @@ fn queueing_works() { set_balance_proposal_bounded(i), DispatchTime::After(0), )); + } + for i in [1, 2, 4] { assert_ok!(Referenda::place_decision_deposit(RuntimeOrigin::signed(i), i as u32)); // TODO: decision deposit after some initial votes with a non-highest voted coming // first. From 3283a0879e3536ae6e7fe4e02a3b145cf520ffe9 Mon Sep 17 00:00:00 2001 From: Oliver Tale-Yazdi Date: Wed, 10 May 2023 12:46:52 +0200 Subject: [PATCH 482/558] Include `node-template-release` in workspace (#14103) * Add node-template-release to workspace Signed-off-by: Oliver Tale-Yazdi * Remove empty workspace Signed-off-by: Oliver Tale-Yazdi * Manually update deps Signed-off-by: Oliver Tale-Yazdi * Update Cargo.lock Signed-off-by: Oliver Tale-Yazdi * Update .gitignore Signed-off-by: Oliver Tale-Yazdi * Update license Signed-off-by: Oliver Tale-Yazdi --------- Signed-off-by: Oliver Tale-Yazdi --- .gitignore | 1 - Cargo.lock | 64 +++++++++++++++++++++ Cargo.toml | 1 + scripts/ci/deny.toml | 1 + scripts/ci/node-template-release/Cargo.toml | 14 ++--- 5 files changed, 72 insertions(+), 9 deletions(-) diff --git a/.gitignore b/.gitignore index f30103c62..2961fd68f 100644 --- a/.gitignore +++ b/.gitignore @@ -24,6 +24,5 @@ rls*.log .cargo-remote.toml *.bin *.iml -scripts/ci/node-template-release/Cargo.lock bin/node-template/Cargo.lock substrate.code-workspace diff --git a/Cargo.lock b/Cargo.lock index 3c6c24969..17ad4e2a3 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3134,6 +3134,8 @@ dependencies = [ "libc", "libgit2-sys", "log", + "openssl-probe", + "openssl-sys", "url", ] @@ -4061,7 +4063,9 @@ checksum = "7f3d95f6b51075fe9810a7ae22c7095f12b98005ab364d8544797a825ce946a4" dependencies = [ "cc", "libc", + "libssh2-sys", "libz-sys", + "openssl-sys", "pkg-config", ] @@ -4603,6 +4607,20 @@ dependencies = [ "libsecp256k1-core", ] +[[package]] +name = "libssh2-sys" +version = "0.2.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b094a36eb4b8b8c8a7b4b8ae43b2944502be3e59cd87687595cf6b0a71b3f4ca" +dependencies = [ + "cc", + "libc", + "libz-sys", + "openssl-sys", + "pkg-config", + "vcpkg", +] + [[package]] name = "libz-sys" version = "1.1.9" @@ -5493,6 +5511,20 @@ dependencies = [ "try-runtime-cli", ] +[[package]] +name = "node-template-release" +version = "3.0.0" +dependencies = [ + "clap 4.2.5", + "flate2", + "fs_extra", + "git2", + "glob", + "tar", + "tempfile", + "toml 0.7.3", +] + [[package]] name = "node-template-runtime" version = "4.0.0-dev" @@ -5768,6 +5800,18 @@ version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ff011a302c396a5197692431fc1948019154afc178baf7d8e37367442a4601cf" +[[package]] +name = "openssl-sys" +version = "0.9.87" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e17f59264b2809d77ae94f0e1ebabc434773f370d6ca667bd223ea10e06cc7e" +dependencies = [ + "cc", + "libc", + "pkg-config", + "vcpkg", +] + [[package]] name = "os_str_bytes" version = "6.5.0" @@ -11946,6 +11990,17 @@ version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "55937e1799185b12863d447f42597ed69d9928686b8d88a1df17376a097d8369" +[[package]] +name = "tar" +version = "0.4.38" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4b55807c0344e1e6c04d7c965f5289c39a8d94ae23ed5c0b57aabac549f871c6" +dependencies = [ + "filetime", + "libc", + "xattr", +] + [[package]] name = "target-lexicon" version = "0.12.7" @@ -13849,6 +13904,15 @@ dependencies = [ "time 0.3.21", ] +[[package]] +name = "xattr" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6d1526bbe5aaeb5eb06885f4d987bcdfa5e23187055de9b83fe00156a821fabc" +dependencies = [ + "libc", +] + [[package]] name = "yamux" version = "0.10.2" diff --git a/Cargo.toml b/Cargo.toml index da5e1d532..7563a4a64 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -236,6 +236,7 @@ members = [ "primitives/version/proc-macro", "primitives/wasm-interface", "primitives/weights", + "scripts/ci/node-template-release", "test-utils", "test-utils/client", "test-utils/derive", diff --git a/scripts/ci/deny.toml b/scripts/ci/deny.toml index 91822c831..408a9e55b 100644 --- a/scripts/ci/deny.toml +++ b/scripts/ci/deny.toml @@ -40,6 +40,7 @@ exceptions = [ { allow = ["GPL-3.0 WITH Classpath-exception-2.0"], name = "node-bench" }, { allow = ["GPL-3.0 WITH Classpath-exception-2.0"], name = "node-cli" }, { allow = ["GPL-3.0 WITH Classpath-exception-2.0"], name = "node-inspect" }, + { allow = ["GPL-3.0 WITH Classpath-exception-2.0"], name = "node-template-release" }, { allow = ["GPL-3.0 WITH Classpath-exception-2.0"], name = "node-testing" }, { allow = ["GPL-3.0 WITH Classpath-exception-2.0"], name = "sc-authority-discovery" }, { allow = ["GPL-3.0 WITH Classpath-exception-2.0"], name = "sc-basic-authorship" }, diff --git a/scripts/ci/node-template-release/Cargo.toml b/scripts/ci/node-template-release/Cargo.toml index 668e0f3f6..aca8d6e1f 100644 --- a/scripts/ci/node-template-release/Cargo.toml +++ b/scripts/ci/node-template-release/Cargo.toml @@ -3,7 +3,7 @@ name = "node-template-release" version = "3.0.0" authors = ["Parity Technologies "] edition = "2021" -license = "GPL-3.0" +license = "GPL-3.0 WITH Classpath-exception-2.0" homepage = "https://substrate.io" publish = false @@ -11,13 +11,11 @@ publish = false targets = ["x86_64-unknown-linux-gnu"] [dependencies] -clap = { version = "4.0.9", features = ["derive"] } +clap = { version = "4.2.5", features = ["derive"] } flate2 = "1.0" -fs_extra = "1" -git2 = "0.8" -glob = "0.2" +fs_extra = "1.3" +git2 = "0.16" +glob = "0.3" tar = "0.4" tempfile = "3" -toml = "0.4" - -[workspace] +toml = "0.7" From e2c5a564e5a83e1f63d47a4fa300567901f00afc Mon Sep 17 00:00:00 2001 From: Tsvetomir Dimitrov Date: Wed, 10 May 2023 17:11:30 +0300 Subject: [PATCH 483/558] Bump `kvdb-rocksdb` to 0.19.0 (#14113) --- Cargo.lock | 31 +++++++++++++++++++++---------- bin/node/bench/Cargo.toml | 2 +- client/db/Cargo.toml | 4 ++-- 3 files changed, 24 insertions(+), 13 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 17ad4e2a3..289e0c2c0 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -753,9 +753,9 @@ dependencies = [ [[package]] name = "bindgen" -version = "0.64.0" +version = "0.65.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c4243e6031260db77ede97ad86c27e501d646a27ab57b59a574f725d98ab1fb4" +checksum = "cfdf7b466f9a4903edc73f95d6d2bcd5baf8ae620638762244d3f60143643cc5" dependencies = [ "bitflags", "cexpr", @@ -763,12 +763,13 @@ dependencies = [ "lazy_static", "lazycell", "peeking_take_while", + "prettyplease 0.2.4", "proc-macro2", "quote", "regex", "rustc-hash", "shlex", - "syn 1.0.109", + "syn 2.0.15", ] [[package]] @@ -4019,9 +4020,9 @@ dependencies = [ [[package]] name = "kvdb-rocksdb" -version = "0.18.0" +version = "0.19.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fe7a749456510c45f795e8b04a6a3e0976d0139213ecbf465843830ad55e2217" +checksum = "b644c70b92285f66bfc2032922a79000ea30af7bc2ab31902992a5dcb9b434f6" dependencies = [ "kvdb", "num_cpus", @@ -4546,9 +4547,9 @@ dependencies = [ [[package]] name = "librocksdb-sys" -version = "0.10.0+7.9.2" +version = "0.11.0+8.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0fe4d5874f5ff2bc616e55e8c6086d478fcda13faf9495768a4aa1c22042d30b" +checksum = "d3386f101bcb4bd252d8e9d2fb41ec3b0862a15a62b478c355b2982efa469e3e" dependencies = [ "bindgen", "bzip2-sys", @@ -7925,6 +7926,16 @@ dependencies = [ "syn 1.0.109", ] +[[package]] +name = "prettyplease" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1ceca8aaf45b5c46ec7ed39fff75f57290368c1846d33d24a122ca81416ab058" +dependencies = [ + "proc-macro2", + "syn 2.0.15", +] + [[package]] name = "primitive-types" version = "0.12.1" @@ -8052,7 +8063,7 @@ dependencies = [ "log", "multimap", "petgraph", - "prettyplease", + "prettyplease 0.1.25", "prost", "prost-types", "regex", @@ -8469,9 +8480,9 @@ dependencies = [ [[package]] name = "rocksdb" -version = "0.20.1" +version = "0.21.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "015439787fce1e75d55f279078d33ff14b4af5d93d995e8838ee4631301c8a99" +checksum = "bb6f170a4041d50a0ce04b0d2e14916d6ca863ea2e422689a5b694395d299ffe" dependencies = [ "libc", "librocksdb-sys", diff --git a/bin/node/bench/Cargo.toml b/bin/node/bench/Cargo.toml index fa497a2fd..2cbe7577f 100644 --- a/bin/node/bench/Cargo.toml +++ b/bin/node/bench/Cargo.toml @@ -25,7 +25,7 @@ serde = "1.0.136" serde_json = "1.0.85" derive_more = { version = "0.99.17", default-features = false, features = ["display"] } kvdb = "0.13.0" -kvdb-rocksdb = "0.18.0" +kvdb-rocksdb = "0.19.0" sp-trie = { version = "7.0.0", path = "../../../primitives/trie" } sp-core = { version = "7.0.0", path = "../../../primitives/core" } sp-consensus = { version = "0.10.0-dev", path = "../../../primitives/consensus/common" } diff --git a/client/db/Cargo.toml b/client/db/Cargo.toml index cd3a73dd4..fe164793d 100644 --- a/client/db/Cargo.toml +++ b/client/db/Cargo.toml @@ -19,7 +19,7 @@ codec = { package = "parity-scale-codec", version = "3.2.2", features = [ hash-db = "0.16.0" kvdb = "0.13.0" kvdb-memorydb = "0.13.0" -kvdb-rocksdb = { version = "0.18.0", optional = true } +kvdb-rocksdb = { version = "0.19.0", optional = true } linked-hash-map = "0.5.4" log = "0.4.17" parity-db = "0.4.6" @@ -37,7 +37,7 @@ sp-trie = { version = "7.0.0", path = "../../primitives/trie" } [dev-dependencies] criterion = "0.4.0" -kvdb-rocksdb = "0.18.0" +kvdb-rocksdb = "0.19.0" rand = "0.8.5" tempfile = "3.1.0" quickcheck = { version = "1.0.3", default-features = false } From 49154cc7b30d2519f03c020bf884b310f8aee4c6 Mon Sep 17 00:00:00 2001 From: Oliver Tale-Yazdi Date: Wed, 10 May 2023 22:29:00 +0200 Subject: [PATCH 484/558] Remove `#[pallet::generate_storage_info]` from docs (#14116) * Fix docs Signed-off-by: Oliver Tale-Yazdi * Add UI test Signed-off-by: Oliver Tale-Yazdi --------- Signed-off-by: Oliver Tale-Yazdi --- frame/support/procedural/src/lib.rs | 18 ------------- frame/support/src/lib.rs | 25 +++---------------- .../pallet_ui/pallet_struct_invalid_attr.rs | 15 +++++++++++ .../pallet_struct_invalid_attr.stderr | 5 ++++ 4 files changed, 23 insertions(+), 40 deletions(-) create mode 100644 frame/support/test/tests/pallet_ui/pallet_struct_invalid_attr.rs create mode 100644 frame/support/test/tests/pallet_ui/pallet_struct_invalid_attr.stderr diff --git a/frame/support/procedural/src/lib.rs b/frame/support/procedural/src/lib.rs index abfa21b33..895b09a17 100644 --- a/frame/support/procedural/src/lib.rs +++ b/frame/support/procedural/src/lib.rs @@ -869,24 +869,6 @@ pub fn generate_store(_: TokenStream, _: TokenStream) -> TokenStream { pallet_macro_stub() } -/// To generate the full storage info (used for PoV calculation) use the attribute -/// `#[pallet::generate_storage_info]`, e.g.: -/// -/// ```ignore -/// #[pallet::pallet] -/// #[pallet::generate_storage_info] -/// pub struct Pallet(_); -/// ``` -/// -/// This requires all storage items to implement the trait `StorageInfoTrait`, thus all keys -/// and value types must be bound by `MaxEncodedLen`. Individual storages can opt-out from this -/// constraint by using [`#[pallet::unbounded]`](`macro@unbounded`) (see -/// [`#[pallet::storage]`](`macro@storage`) for more info). -#[proc_macro_attribute] -pub fn generate_storage_info(_: TokenStream, _: TokenStream) -> TokenStream { - pallet_macro_stub() -} - /// Because the `pallet::pallet` macro implements `GetStorageVersion`, the current storage /// version needs to be communicated to the macro. This can be done by using the /// `pallet::storage_version` attribute: diff --git a/frame/support/src/lib.rs b/frame/support/src/lib.rs index 138b7b55b..3cd8378be 100644 --- a/frame/support/src/lib.rs +++ b/frame/support/src/lib.rs @@ -1597,7 +1597,6 @@ pub mod pallet_prelude { /// * [`pallet::constant`](#palletconstant) /// * [`pallet::disable_frame_system_supertrait_check`](#disable_supertrait_check) /// * [`pallet::generate_store($vis trait Store)`](#palletgenerate_storevis-trait-store) -/// * [`pallet::generate_storage_info`](#palletgenerate_storage_info) /// * [`pallet::storage_version`](#palletstorage_version) /// * [`pallet::hooks`](#hooks-pallethooks-optional) /// * [`pallet::call`](#call-palletcall-optional) @@ -1801,24 +1800,6 @@ pub mod pallet_prelude { /// /// Also see [`pallet::generate_store`](`frame_support::pallet_macros::generate_store`). /// -/// # `pallet::generate_storage_info` -/// -/// To generate the full storage info (used for PoV calculation) use the attribute -/// `#[pallet::generate_storage_info]`, e.g.: -/// -/// ```ignore -/// #[pallet::pallet] -/// #[pallet::generate_storage_info] -/// pub struct Pallet(_); -/// ``` -/// -/// This requires all storage items to implement the trait [`traits::StorageInfoTrait`], thus -/// all keys and value types must be bound by [`pallet_prelude::MaxEncodedLen`]. Individual -/// storages can opt-out from this constraint by using `#[pallet::unbounded]` (see -/// `#[pallet::storage]` for more info). -/// -/// Also see [`pallet::generate_storage_info`](`frame_support::pallet_macros::generate_storage_info`) -/// /// # `pallet::storage_version` /// /// Because the [`pallet::pallet`](#pallet-struct-placeholder-palletpallet-mandatory) macro @@ -2903,9 +2884,9 @@ pub mod pallet_macros { pub use frame_support_procedural::{ call_index, compact, composite_enum, config, constant, disable_frame_system_supertrait_check, error, event, extra_constants, generate_deposit, - generate_storage_info, generate_store, genesis_build, genesis_config, getter, hooks, - inherent, origin, storage, storage_prefix, storage_version, type_value, unbounded, - validate_unsigned, weight, whitelist_storage, + generate_store, genesis_build, genesis_config, getter, hooks, inherent, origin, storage, + storage_prefix, storage_version, type_value, unbounded, validate_unsigned, weight, + whitelist_storage, }; } diff --git a/frame/support/test/tests/pallet_ui/pallet_struct_invalid_attr.rs b/frame/support/test/tests/pallet_ui/pallet_struct_invalid_attr.rs new file mode 100644 index 000000000..ac52e75a5 --- /dev/null +++ b/frame/support/test/tests/pallet_ui/pallet_struct_invalid_attr.rs @@ -0,0 +1,15 @@ +#[frame_support::pallet] +mod pallet { + #[pallet::config] + pub trait Config: frame_system::Config {} + + #[pallet::pallet] + #[pallet::generate_storage_info] // invalid + pub struct Pallet(_); + + #[pallet::call] + impl Pallet {} +} + +fn main() { +} diff --git a/frame/support/test/tests/pallet_ui/pallet_struct_invalid_attr.stderr b/frame/support/test/tests/pallet_ui/pallet_struct_invalid_attr.stderr new file mode 100644 index 000000000..301a73c00 --- /dev/null +++ b/frame/support/test/tests/pallet_ui/pallet_struct_invalid_attr.stderr @@ -0,0 +1,5 @@ +error: expected one of: `generate_store`, `without_storage_info`, `storage_version` + --> tests/pallet_ui/pallet_struct_invalid_attr.rs:7:12 + | +7 | #[pallet::generate_storage_info] // invalid + | ^^^^^^^^^^^^^^^^^^^^^ From aee8f7641a41971959373d44e2e0e4038b179376 Mon Sep 17 00:00:00 2001 From: Vladimir Istyufeev Date: Thu, 11 May 2023 02:35:24 +0400 Subject: [PATCH 485/558] Bump `Cargo.lock` (#14121) * Bump `Cargo.lock` * Use master Cargo.lock Signed-off-by: Oliver Tale-Yazdi * Update Cargo.lock Signed-off-by: Oliver Tale-Yazdi --------- Signed-off-by: Oliver Tale-Yazdi Co-authored-by: Oliver Tale-Yazdi --- Cargo.lock | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.lock b/Cargo.lock index 289e0c2c0..0e6e8e847 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -5516,7 +5516,7 @@ dependencies = [ name = "node-template-release" version = "3.0.0" dependencies = [ - "clap 4.2.5", + "clap 4.2.7", "flate2", "fs_extra", "git2", From 0c696fc1ce7a60dbcf13f176b9d2dcb9492e6335 Mon Sep 17 00:00:00 2001 From: Aaro Altonen <48052676+altonen@users.noreply.github.com> Date: Thu, 11 May 2023 13:27:21 +0300 Subject: [PATCH 486/558] Prepare `sc-network` for `ProtocolController`/`NotificationService` (#14080) * Prepare `sc-network` for `ProtocolController`/`NotificationService` The upcoming notification protocol refactoring requires that protocols are able to communicate with `sc-network` over unique and direct links. This means that `sc-network` side of the link has to be created before `sc-network` is initialized and that it is allowed to consume the object as the receiver half of the link may not implement `Clone`. Remove request-response and notification protocols from `NetworkConfiguration` and create a new object that contains the configurations of these protocols and which is consumable by `sc-network`. This is needed needed because, e.g., the receiver half of `NotificationService` is not clonable so `sc-network` must consume it when it's initializing the protocols in `Notifications`. Similar principe applies to `PeerStore`/`ProtocolController`: as per current design, protocols are created before the network so `Protocol` cannot be the one creating the `PeerStore` object. `FullNetworkConfiguration` will be used to store the objects that `sc-network` will use to communicate with protocols and it will also allow protocols to allocate handles so they can directly communicate with `sc-network`. * Fixes * Update client/service/src/builder.rs Co-authored-by: Dmitry Markin * Updates * Doc updates + cargo-fmt --------- Co-authored-by: Dmitry Markin --- Cargo.lock | 1 + bin/node-template/node/Cargo.toml | 1 + bin/node-template/node/src/service.rs | 12 +- bin/node/cli/src/service.rs | 14 +- client/cli/src/params/network_params.rs | 2 - .../consensus/beefy/src/communication/mod.rs | 2 +- client/consensus/grandpa/src/lib.rs | 2 +- client/network/src/config.rs | 54 +++-- client/network/src/protocol.rs | 9 +- client/network/src/service.rs | 185 ++++++++---------- client/network/sync/src/engine.rs | 47 +++-- client/network/test/src/lib.rs | 52 ++--- client/network/test/src/service.rs | 65 +++--- client/service/src/builder.rs | 108 +++++----- 14 files changed, 302 insertions(+), 252 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 0e6e8e847..80fbf030f 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -5488,6 +5488,7 @@ dependencies = [ "sc-consensus-grandpa", "sc-executor", "sc-keystore", + "sc-network", "sc-rpc", "sc-rpc-api", "sc-service", diff --git a/bin/node-template/node/Cargo.toml b/bin/node-template/node/Cargo.toml index b729667c5..1801caad6 100644 --- a/bin/node-template/node/Cargo.toml +++ b/bin/node-template/node/Cargo.toml @@ -23,6 +23,7 @@ futures = { version = "0.3.21", features = ["thread-pool"]} sc-cli = { version = "0.10.0-dev", path = "../../../client/cli" } sp-core = { version = "7.0.0", path = "../../../primitives/core" } sc-executor = { version = "0.10.0-dev", path = "../../../client/executor" } +sc-network = { version = "0.10.0-dev", path = "../../../client/network" } sc-service = { version = "0.10.0-dev", path = "../../../client/service" } sc-telemetry = { version = "4.0.0-dev", path = "../../../client/telemetry" } sc-keystore = { version = "4.0.0-dev", path = "../../../client/keystore" } diff --git a/bin/node-template/node/src/service.rs b/bin/node-template/node/src/service.rs index 723d1db3e..ca827001b 100644 --- a/bin/node-template/node/src/service.rs +++ b/bin/node-template/node/src/service.rs @@ -138,7 +138,7 @@ pub fn new_partial( } /// Builds a new service for a full client. -pub fn new_full(mut config: Configuration) -> Result { +pub fn new_full(config: Configuration) -> Result { let sc_service::PartialComponents { client, backend, @@ -150,15 +150,16 @@ pub fn new_full(mut config: Configuration) -> Result other: (block_import, grandpa_link, mut telemetry), } = new_partial(&config)?; + let mut net_config = sc_network::config::FullNetworkConfiguration::new(&config.network); + let grandpa_protocol_name = sc_consensus_grandpa::protocol_standard_name( &client.block_hash(0).ok().flatten().expect("Genesis block exists; qed"), &config.chain_spec, ); + net_config.add_notification_protocol(sc_consensus_grandpa::grandpa_peers_set_config( + grandpa_protocol_name.clone(), + )); - config - .network - .extra_sets - .push(sc_consensus_grandpa::grandpa_peers_set_config(grandpa_protocol_name.clone())); let warp_sync = Arc::new(sc_consensus_grandpa::warp_proof::NetworkProvider::new( backend.clone(), grandpa_link.shared_authority_set().clone(), @@ -168,6 +169,7 @@ pub fn new_full(mut config: Configuration) -> Result let (network, system_rpc_tx, tx_handler_controller, network_starter, sync_service) = sc_service::build_network(sc_service::BuildNetworkParams { config: &config, + net_config, client: client.clone(), transaction_pool: transaction_pool.clone(), spawn_handle: task_manager.spawn_handle(), diff --git a/bin/node/cli/src/service.rs b/bin/node/cli/src/service.rs index b704bf029..8fc44c7c5 100644 --- a/bin/node/cli/src/service.rs +++ b/bin/node/cli/src/service.rs @@ -316,7 +316,7 @@ pub struct NewFullBase { /// Creates a full service from the configuration. pub fn new_full_base( - mut config: Configuration, + config: Configuration, disable_hardware_benchmarks: bool, with_startup_data: impl FnOnce( &sc_consensus_babe::BabeBlockImport, @@ -343,10 +343,15 @@ pub fn new_full_base( let shared_voter_state = rpc_setup; let auth_disc_publish_non_global_ips = config.network.allow_non_globals_in_dht; + let mut net_config = sc_network::config::FullNetworkConfiguration::new(&config.network); + let grandpa_protocol_name = grandpa::protocol_standard_name( &client.block_hash(0).ok().flatten().expect("Genesis block exists; qed"), &config.chain_spec, ); + net_config.add_notification_protocol(grandpa::grandpa_peers_set_config( + grandpa_protocol_name.clone(), + )); let statement_handler_proto = sc_network_statement::StatementHandlerPrototype::new( client @@ -356,12 +361,8 @@ pub fn new_full_base( .expect("Genesis block exists; qed"), config.chain_spec.fork_id(), ); - config.network.extra_sets.push(statement_handler_proto.set_config()); + net_config.add_notification_protocol(statement_handler_proto.set_config()); - config - .network - .extra_sets - .push(grandpa::grandpa_peers_set_config(grandpa_protocol_name.clone())); let warp_sync = Arc::new(grandpa::warp_proof::NetworkProvider::new( backend.clone(), import_setup.1.shared_authority_set().clone(), @@ -371,6 +372,7 @@ pub fn new_full_base( let (network, system_rpc_tx, tx_handler_controller, network_starter, sync_service) = sc_service::build_network(sc_service::BuildNetworkParams { config: &config, + net_config, client: client.clone(), transaction_pool: transaction_pool.clone(), spawn_handle: task_manager.spawn_handle(), diff --git a/client/cli/src/params/network_params.rs b/client/cli/src/params/network_params.rs index 13e433825..a974b8602 100644 --- a/client/cli/src/params/network_params.rs +++ b/client/cli/src/params/network_params.rs @@ -221,8 +221,6 @@ impl NetworkParams { default_peers_set_num_full: self.in_peers + self.out_peers, listen_addresses, public_addresses, - extra_sets: Vec::new(), - request_response_protocols: Vec::new(), node_key, node_name: node_name.to_string(), client_version: client_id.to_string(), diff --git a/client/consensus/beefy/src/communication/mod.rs b/client/consensus/beefy/src/communication/mod.rs index d8e4d2205..0de67f606 100644 --- a/client/consensus/beefy/src/communication/mod.rs +++ b/client/consensus/beefy/src/communication/mod.rs @@ -63,7 +63,7 @@ pub(crate) mod beefy_protocol_name { } /// Returns the configuration value to put in -/// [`sc_network::config::NetworkConfiguration::extra_sets`]. +/// [`sc_network::config::FullNetworkConfiguration`]. /// For standard protocol name see [`beefy_protocol_name::gossip_protocol_name`]. pub fn beefy_peers_set_config( gossip_protocol_name: sc_network::ProtocolName, diff --git a/client/consensus/grandpa/src/lib.rs b/client/consensus/grandpa/src/lib.rs index 90cb8a51f..c11e873ec 100644 --- a/client/consensus/grandpa/src/lib.rs +++ b/client/consensus/grandpa/src/lib.rs @@ -691,7 +691,7 @@ pub struct GrandpaParams { } /// Returns the configuration value to put in -/// [`sc_network::config::NetworkConfiguration::extra_sets`]. +/// [`sc_network::config::FullNetworkConfiguration`]. /// For standard protocol name see [`crate::protocol_standard_name`]. pub fn grandpa_peers_set_config( protocol_name: ProtocolName, diff --git a/client/network/src/config.rs b/client/network/src/config.rs index e00bfac79..e80de1382 100644 --- a/client/network/src/config.rs +++ b/client/network/src/config.rs @@ -32,15 +32,16 @@ pub use crate::{ use codec::Encode; use libp2p::{identity::Keypair, multiaddr, Multiaddr, PeerId}; use prometheus_endpoint::Registry; +use zeroize::Zeroize; + pub use sc_network_common::{ role::{Role, Roles}, sync::warp::WarpSyncProvider, ExHashT, }; use sc_utils::mpsc::TracingUnboundedSender; -use zeroize::Zeroize; - use sp_runtime::traits::Block as BlockT; + use std::{ error::Error, fmt, fs, @@ -564,9 +565,6 @@ pub struct NetworkConfiguration { /// The node key configuration, which determines the node's network identity keypair. pub node_key: NodeKeyConfig, - /// List of request-response protocols that the node supports. - pub request_response_protocols: Vec, - /// Configuration for the default set of nodes used for block syncing and transactions. pub default_peers_set: SetConfig, @@ -576,9 +574,6 @@ pub struct NetworkConfiguration { /// This value is implicitly capped to `default_set.out_peers + default_set.in_peers`. pub default_peers_set_num_full: u32, - /// Configuration for extra sets of nodes. - pub extra_sets: Vec, - /// Client identifier. Sent over the wire for debugging purposes. pub client_version: String, @@ -649,10 +644,8 @@ impl NetworkConfiguration { public_addresses: Vec::new(), boot_nodes: Vec::new(), node_key, - request_response_protocols: Vec::new(), default_peers_set_num_full: default_peers_set.in_peers + default_peers_set.out_peers, default_peers_set, - extra_sets: Vec::new(), client_version: client_version.into(), node_name: node_name.into(), transport: TransportConfig::Normal { enable_mdns: false, allow_private_ip: true }, @@ -707,7 +700,7 @@ pub struct Params { pub executor: Box + Send>>) + Send>, /// Network layer configuration. - pub network_config: NetworkConfiguration, + pub network_config: FullNetworkConfiguration, /// Legacy name of the protocol to use on the wire. Should be different for each chain. pub protocol_id: ProtocolId, @@ -727,9 +720,44 @@ pub struct Params { /// TX channel for direct communication with `SyncingEngine` and `Protocol`. pub tx: TracingUnboundedSender>, +} + +/// Full network configuration. +pub struct FullNetworkConfiguration { + /// Installed notification protocols. + pub(crate) notification_protocols: Vec, + + /// List of request-response protocols that the node supports. + pub(crate) request_response_protocols: Vec, - /// Request response protocol configurations - pub request_response_protocol_configs: Vec, + /// Network configuration. + pub network_config: NetworkConfiguration, +} + +impl FullNetworkConfiguration { + /// Create new [`FullNetworkConfiguration`]. + pub fn new(network_config: &NetworkConfiguration) -> Self { + Self { + notification_protocols: Vec::new(), + request_response_protocols: Vec::new(), + network_config: network_config.clone(), + } + } + + /// Add a notification protocol. + pub fn add_notification_protocol(&mut self, config: NonDefaultSetConfig) { + self.notification_protocols.push(config); + } + + /// Get reference to installed notification protocols. + pub fn notification_protocols(&self) -> &Vec { + &self.notification_protocols + } + + /// Add a request-response protocol. + pub fn add_request_response_protocol(&mut self, config: RequestResponseConfig) { + self.request_response_protocols.push(config); + } } #[cfg(test)] diff --git a/client/network/src/protocol.rs b/client/network/src/protocol.rs index 0075e856e..29a90c0bc 100644 --- a/client/network/src/protocol.rs +++ b/client/network/src/protocol.rs @@ -102,6 +102,7 @@ impl Protocol { pub fn new( roles: Roles, network_config: &config::NetworkConfiguration, + notification_protocols: Vec, block_announces_protocol: config::NonDefaultSetConfig, tx: TracingUnboundedSender>, ) -> error::Result<(Self, sc_peerset::PeersetHandle, Vec<(PeerId, Multiaddr)>)> { @@ -109,7 +110,7 @@ impl Protocol { let (peerset, peerset_handle) = { let mut sets = - Vec::with_capacity(NUM_HARDCODED_PEERSETS + network_config.extra_sets.len()); + Vec::with_capacity(NUM_HARDCODED_PEERSETS + notification_protocols.len()); let mut default_sets_reserved = HashSet::new(); for reserved in network_config.default_peers_set.reserved_nodes.iter() { @@ -135,7 +136,7 @@ impl Protocol { NonReservedPeerMode::Deny, }); - for set_cfg in &network_config.extra_sets { + for set_cfg in ¬ification_protocols { let mut reserved_nodes = HashSet::new(); for reserved in set_cfg.set_config.reserved_nodes.iter() { reserved_nodes.insert(reserved.peer_id); @@ -169,7 +170,7 @@ impl Protocol { handshake: block_announces_protocol.handshake.as_ref().unwrap().to_vec(), max_notification_size: block_announces_protocol.max_notification_size, }) - .chain(network_config.extra_sets.iter().map(|s| notifications::ProtocolConfig { + .chain(notification_protocols.iter().map(|s| notifications::ProtocolConfig { name: s.notifications_protocol.clone(), fallback_names: s.fallback_names.clone(), handshake: s.handshake.as_ref().map_or(roles.encode(), |h| (*h).to_vec()), @@ -182,7 +183,7 @@ impl Protocol { peerset_handle: peerset_handle.clone(), behaviour, notification_protocols: iter::once(block_announces_protocol.notifications_protocol) - .chain(network_config.extra_sets.iter().map(|s| s.notifications_protocol.clone())) + .chain(notification_protocols.iter().map(|s| s.notifications_protocol.clone())) .collect(), bad_handshake_substreams: Default::default(), peers: HashMap::new(), diff --git a/client/network/src/service.rs b/client/network/src/service.rs index 9708b24d2..75c9d6692 100644 --- a/client/network/src/service.rs +++ b/client/network/src/service.rs @@ -29,7 +29,7 @@ use crate::{ behaviour::{self, Behaviour, BehaviourOut}, - config::{MultiaddrWithPeerId, Params, TransportConfig}, + config::{FullNetworkConfiguration, MultiaddrWithPeerId, Params, TransportConfig}, discovery::DiscoveryConfig, error::Error, event::{DhtEvent, Event}, @@ -147,25 +147,24 @@ where /// Returns a `NetworkWorker` that implements `Future` and must be regularly polled in order /// for the network processing to advance. From it, you can extract a `NetworkService` using /// `worker.service()`. The `NetworkService` can be shared through the codebase. - pub fn new(mut params: Params) -> Result { + pub fn new(params: Params) -> Result { + let FullNetworkConfiguration { + notification_protocols, + request_response_protocols, + mut network_config, + } = params.network_config; + // Private and public keys configuration. - let local_identity = params.network_config.node_key.clone().into_keypair()?; + let local_identity = network_config.node_key.clone().into_keypair()?; let local_public = local_identity.public(); let local_peer_id = local_public.to_peer_id(); - params - .network_config - .request_response_protocols - .extend(params.request_response_protocol_configs); - - params.network_config.boot_nodes = params - .network_config + network_config.boot_nodes = network_config .boot_nodes .into_iter() .filter(|boot_node| boot_node.peer_id != local_peer_id) .collect(); - params.network_config.default_peers_set.reserved_nodes = params - .network_config + network_config.default_peers_set.reserved_nodes = network_config .default_peers_set .reserved_nodes .into_iter() @@ -185,36 +184,31 @@ where // Ensure the listen addresses are consistent with the transport. ensure_addresses_consistent_with_transport( - params.network_config.listen_addresses.iter(), - ¶ms.network_config.transport, + network_config.listen_addresses.iter(), + &network_config.transport, )?; ensure_addresses_consistent_with_transport( - params.network_config.boot_nodes.iter().map(|x| &x.multiaddr), - ¶ms.network_config.transport, + network_config.boot_nodes.iter().map(|x| &x.multiaddr), + &network_config.transport, )?; ensure_addresses_consistent_with_transport( - params - .network_config - .default_peers_set - .reserved_nodes - .iter() - .map(|x| &x.multiaddr), - ¶ms.network_config.transport, + network_config.default_peers_set.reserved_nodes.iter().map(|x| &x.multiaddr), + &network_config.transport, )?; - for extra_set in ¶ms.network_config.extra_sets { + for notification_protocol in ¬ification_protocols { ensure_addresses_consistent_with_transport( - extra_set.set_config.reserved_nodes.iter().map(|x| &x.multiaddr), - ¶ms.network_config.transport, + notification_protocol.set_config.reserved_nodes.iter().map(|x| &x.multiaddr), + &network_config.transport, )?; } ensure_addresses_consistent_with_transport( - params.network_config.public_addresses.iter(), - ¶ms.network_config.transport, + network_config.public_addresses.iter(), + &network_config.transport, )?; let (to_worker, from_service) = tracing_unbounded("mpsc_network_worker", 100_000); - if let Some(path) = ¶ms.network_config.net_config_path { + if let Some(path) = &network_config.net_config_path { fs::create_dir_all(path)?; } @@ -224,9 +218,58 @@ where local_peer_id.to_base58(), ); + let (transport, bandwidth) = { + let config_mem = match network_config.transport { + TransportConfig::MemoryOnly => true, + TransportConfig::Normal { .. } => false, + }; + + // The yamux buffer size limit is configured to be equal to the maximum frame size + // of all protocols. 10 bytes are added to each limit for the length prefix that + // is not included in the upper layer protocols limit but is still present in the + // yamux buffer. These 10 bytes correspond to the maximum size required to encode + // a variable-length-encoding 64bits number. In other words, we make the + // assumption that no notification larger than 2^64 will ever be sent. + let yamux_maximum_buffer_size = { + let requests_max = request_response_protocols + .iter() + .map(|cfg| usize::try_from(cfg.max_request_size).unwrap_or(usize::MAX)); + let responses_max = request_response_protocols + .iter() + .map(|cfg| usize::try_from(cfg.max_response_size).unwrap_or(usize::MAX)); + let notifs_max = notification_protocols + .iter() + .map(|cfg| usize::try_from(cfg.max_notification_size).unwrap_or(usize::MAX)); + + // A "default" max is added to cover all the other protocols: ping, identify, + // kademlia, block announces, and transactions. + let default_max = cmp::max( + 1024 * 1024, + usize::try_from(protocol::BLOCK_ANNOUNCES_TRANSACTIONS_SUBSTREAM_SIZE) + .unwrap_or(usize::MAX), + ); + + iter::once(default_max) + .chain(requests_max) + .chain(responses_max) + .chain(notifs_max) + .max() + .expect("iterator known to always yield at least one element; qed") + .saturating_add(10) + }; + + transport::build_transport( + local_identity.clone(), + config_mem, + network_config.yamux_window_size, + yamux_maximum_buffer_size, + ) + }; + let (protocol, peerset_handle, mut known_addresses) = Protocol::new( From::from(¶ms.role), - ¶ms.network_config, + &network_config, + notification_protocols, params.block_announce_config, params.tx, )?; @@ -235,7 +278,7 @@ where let mut boot_node_ids = HashSet::new(); // Process the bootnodes. - for bootnode in params.network_config.boot_nodes.iter() { + for bootnode in network_config.boot_nodes.iter() { boot_node_ids.insert(bootnode.peer_id); known_addresses.push((bootnode.peer_id, bootnode.multiaddr.clone())); } @@ -243,9 +286,8 @@ where let boot_node_ids = Arc::new(boot_node_ids); // Check for duplicate bootnodes. - params.network_config.boot_nodes.iter().try_for_each(|bootnode| { - if let Some(other) = params - .network_config + network_config.boot_nodes.iter().try_for_each(|bootnode| { + if let Some(other) = network_config .boot_nodes .iter() .filter(|o| o.multiaddr == bootnode.multiaddr) @@ -265,29 +307,25 @@ where // Build the swarm. let (mut swarm, bandwidth): (Swarm>, _) = { - let user_agent = format!( - "{} ({})", - params.network_config.client_version, params.network_config.node_name - ); + let user_agent = + format!("{} ({})", network_config.client_version, network_config.node_name); let discovery_config = { let mut config = DiscoveryConfig::new(local_public.clone()); config.with_permanent_addresses(known_addresses); - config.discovery_limit( - u64::from(params.network_config.default_peers_set.out_peers) + 15, - ); + config.discovery_limit(u64::from(network_config.default_peers_set.out_peers) + 15); config.with_kademlia( params.genesis_hash, params.fork_id.as_deref(), ¶ms.protocol_id, ); - config.with_dht_random_walk(params.network_config.enable_dht_random_walk); - config.allow_non_globals_in_dht(params.network_config.allow_non_globals_in_dht); + config.with_dht_random_walk(network_config.enable_dht_random_walk); + config.allow_non_globals_in_dht(network_config.allow_non_globals_in_dht); config.use_kademlia_disjoint_query_paths( - params.network_config.kademlia_disjoint_query_paths, + network_config.kademlia_disjoint_query_paths, ); - match params.network_config.transport { + match network_config.transport { TransportConfig::MemoryOnly => { config.with_mdns(false); config.allow_private_ip(false); @@ -305,64 +343,13 @@ where config }; - let (transport, bandwidth) = { - let config_mem = match params.network_config.transport { - TransportConfig::MemoryOnly => true, - TransportConfig::Normal { .. } => false, - }; - - // The yamux buffer size limit is configured to be equal to the maximum frame size - // of all protocols. 10 bytes are added to each limit for the length prefix that - // is not included in the upper layer protocols limit but is still present in the - // yamux buffer. These 10 bytes correspond to the maximum size required to encode - // a variable-length-encoding 64bits number. In other words, we make the - // assumption that no notification larger than 2^64 will ever be sent. - let yamux_maximum_buffer_size = { - let requests_max = params - .network_config - .request_response_protocols - .iter() - .map(|cfg| usize::try_from(cfg.max_request_size).unwrap_or(usize::MAX)); - let responses_max = - params.network_config.request_response_protocols.iter().map(|cfg| { - usize::try_from(cfg.max_response_size).unwrap_or(usize::MAX) - }); - let notifs_max = params.network_config.extra_sets.iter().map(|cfg| { - usize::try_from(cfg.max_notification_size).unwrap_or(usize::MAX) - }); - - // A "default" max is added to cover all the other protocols: ping, identify, - // kademlia, block announces, and transactions. - let default_max = cmp::max( - 1024 * 1024, - usize::try_from(protocol::BLOCK_ANNOUNCES_TRANSACTIONS_SUBSTREAM_SIZE) - .unwrap_or(usize::MAX), - ); - - iter::once(default_max) - .chain(requests_max) - .chain(responses_max) - .chain(notifs_max) - .max() - .expect("iterator known to always yield at least one element; qed") - .saturating_add(10) - }; - - transport::build_transport( - local_identity.clone(), - config_mem, - params.network_config.yamux_window_size, - yamux_maximum_buffer_size, - ) - }; - let behaviour = { let result = Behaviour::new( protocol, user_agent, local_public, discovery_config, - params.network_config.request_response_protocols, + request_response_protocols, peerset_handle.clone(), ); @@ -416,14 +403,14 @@ where }; // Listen on multiaddresses. - for addr in ¶ms.network_config.listen_addresses { + for addr in &network_config.listen_addresses { if let Err(err) = Swarm::>::listen_on(&mut swarm, addr.clone()) { warn!(target: "sub-libp2p", "Can't listen on {} because: {:?}", addr, err) } } // Add external addresses. - for addr in ¶ms.network_config.public_addresses { + for addr in &network_config.public_addresses { Swarm::>::add_external_address( &mut swarm, addr.clone(), diff --git a/client/network/sync/src/engine.rs b/client/network/sync/src/engine.rs index eca0ebfe4..a6db5a5d5 100644 --- a/client/network/sync/src/engine.rs +++ b/client/network/sync/src/engine.rs @@ -37,7 +37,7 @@ use sc_client_api::{BlockBackend, HeaderBackend, ProofProvider}; use sc_consensus::import_queue::ImportQueueService; use sc_network::{ config::{ - NetworkConfiguration, NonDefaultSetConfig, ProtocolId, SyncMode as SyncOperationMode, + FullNetworkConfiguration, NonDefaultSetConfig, ProtocolId, SyncMode as SyncOperationMode, }, utils::LruHashSet, NotificationsSink, ProtocolName, @@ -260,7 +260,7 @@ where roles: Roles, client: Arc, metrics_registry: Option<&Registry>, - network_config: &NetworkConfiguration, + net_config: &FullNetworkConfiguration, protocol_id: ProtocolId, fork_id: &Option, block_announce_validator: Box + Send>, @@ -272,52 +272,56 @@ where warp_sync_protocol_name: Option, rx: sc_utils::mpsc::TracingUnboundedReceiver>, ) -> Result<(Self, SyncingService, NonDefaultSetConfig), ClientError> { - let mode = match network_config.sync_mode { + let mode = match net_config.network_config.sync_mode { SyncOperationMode::Full => SyncMode::Full, SyncOperationMode::Fast { skip_proofs, storage_chain_mode } => SyncMode::LightState { skip_proofs, storage_chain_mode }, SyncOperationMode::Warp => SyncMode::Warp, }; - let max_parallel_downloads = network_config.max_parallel_downloads; - let max_blocks_per_request = if network_config.max_blocks_per_request > + let max_parallel_downloads = net_config.network_config.max_parallel_downloads; + let max_blocks_per_request = if net_config.network_config.max_blocks_per_request > crate::MAX_BLOCKS_IN_RESPONSE as u32 { log::info!(target: "sync", "clamping maximum blocks per request to {}", crate::MAX_BLOCKS_IN_RESPONSE); crate::MAX_BLOCKS_IN_RESPONSE as u32 } else { - network_config.max_blocks_per_request + net_config.network_config.max_blocks_per_request }; let cache_capacity = NonZeroUsize::new( - (network_config.default_peers_set.in_peers as usize + - network_config.default_peers_set.out_peers as usize) + (net_config.network_config.default_peers_set.in_peers as usize + + net_config.network_config.default_peers_set.out_peers as usize) .max(1), ) .expect("cache capacity is not zero"); let important_peers = { let mut imp_p = HashSet::new(); - for reserved in &network_config.default_peers_set.reserved_nodes { + for reserved in &net_config.network_config.default_peers_set.reserved_nodes { imp_p.insert(reserved.peer_id); } - for reserved in network_config - .extra_sets - .iter() - .flat_map(|s| s.set_config.reserved_nodes.iter()) - { - imp_p.insert(reserved.peer_id); + for config in net_config.notification_protocols() { + let peer_ids = config + .set_config + .reserved_nodes + .iter() + .map(|info| info.peer_id) + .collect::>(); + imp_p.extend(peer_ids); } + imp_p.shrink_to_fit(); imp_p }; let boot_node_ids = { let mut list = HashSet::new(); - for node in &network_config.boot_nodes { + for node in &net_config.network_config.boot_nodes { list.insert(node.peer_id); } list.shrink_to_fit(); list }; let default_peers_set_no_slot_peers = { - let mut no_slot_p: HashSet = network_config + let mut no_slot_p: HashSet = net_config + .network_config .default_peers_set .reserved_nodes .iter() @@ -326,11 +330,12 @@ where no_slot_p.shrink_to_fit(); no_slot_p }; - let default_peers_set_num_full = network_config.default_peers_set_num_full as usize; + let default_peers_set_num_full = + net_config.network_config.default_peers_set_num_full as usize; let default_peers_set_num_light = { - let total = network_config.default_peers_set.out_peers + - network_config.default_peers_set.in_peers; - total.saturating_sub(network_config.default_peers_set_num_full) as usize + let total = net_config.network_config.default_peers_set.out_peers + + net_config.network_config.default_peers_set.in_peers; + total.saturating_sub(net_config.network_config.default_peers_set_num_full) as usize }; let (chain_sync, block_announce_config) = ChainSync::new( diff --git a/client/network/test/src/lib.rs b/client/network/test/src/lib.rs index 9e740d085..a9ff38e4e 100644 --- a/client/network/test/src/lib.rs +++ b/client/network/test/src/lib.rs @@ -50,8 +50,8 @@ use sc_consensus::{ }; use sc_network::{ config::{ - MultiaddrWithPeerId, NetworkConfiguration, NonDefaultSetConfig, NonReservedPeerMode, - ProtocolId, Role, SyncMode, TransportConfig, + FullNetworkConfiguration, MultiaddrWithPeerId, NetworkConfiguration, NonDefaultSetConfig, + NonReservedPeerMode, ProtocolId, Role, SyncMode, TransportConfig, }, request_responses::ProtocolConfig as RequestResponseConfig, types::ProtocolName, @@ -800,20 +800,6 @@ where network_config.transport = TransportConfig::MemoryOnly; network_config.listen_addresses = vec![listen_addr.clone()]; network_config.allow_non_globals_in_dht = true; - network_config - .request_response_protocols - .extend(config.request_response_protocols); - network_config.extra_sets = config - .notifications_protocols - .into_iter() - .map(|p| NonDefaultSetConfig { - notifications_protocol: p, - fallback_names: Vec::new(), - max_notification_size: 1024 * 1024, - handshake: None, - set_config: Default::default(), - }) - .collect(); if let Some(connect_to) = config.connect_to_peers { let addrs = connect_to .iter() @@ -826,6 +812,7 @@ where network_config.default_peers_set.reserved_nodes = addrs; network_config.default_peers_set.non_reserved_mode = NonReservedPeerMode::Deny; } + let mut full_net_config = FullNetworkConfiguration::new(&network_config); let protocol_id = ProtocolId::from("test-protocol-name"); @@ -890,7 +877,7 @@ where Roles::from(if config.is_authority { &Role::Authority } else { &Role::Full }), client.clone(), None, - &network_config, + &full_net_config, protocol_id.clone(), &fork_id, block_announce_validator, @@ -906,6 +893,28 @@ where let sync_service_import_queue = Box::new(sync_service.clone()); let sync_service = Arc::new(sync_service.clone()); + for config in config.request_response_protocols { + full_net_config.add_request_response_protocol(config); + } + for config in [ + block_request_protocol_config, + state_request_protocol_config, + light_client_request_protocol_config, + warp_protocol_config, + ] { + full_net_config.add_request_response_protocol(config); + } + + for protocol in config.notifications_protocols { + full_net_config.add_notification_protocol(NonDefaultSetConfig { + notifications_protocol: protocol, + fallback_names: Vec::new(), + max_notification_size: 1024 * 1024, + handshake: None, + set_config: Default::default(), + }); + } + let genesis_hash = client.hash(Zero::zero()).ok().flatten().expect("Genesis block exists; qed"); let network = NetworkWorker::new(sc_network::config::Params { @@ -913,20 +922,13 @@ where executor: Box::new(|f| { tokio::spawn(f); }), - network_config, + network_config: full_net_config, genesis_hash, protocol_id, fork_id, metrics_registry: None, block_announce_config, tx, - request_response_protocol_configs: [ - block_request_protocol_config, - state_request_protocol_config, - light_client_request_protocol_config, - warp_protocol_config, - ] - .to_vec(), }) .unwrap(); diff --git a/client/network/test/src/service.rs b/client/network/test/src/service.rs index 5871860a7..8c15d6b09 100644 --- a/client/network/test/src/service.rs +++ b/client/network/test/src/service.rs @@ -21,7 +21,7 @@ use libp2p::{Multiaddr, PeerId}; use sc_consensus::{ImportQueue, Link}; use sc_network::{ - config::{self, MultiaddrWithPeerId, ProtocolId, TransportConfig}, + config::{self, FullNetworkConfiguration, MultiaddrWithPeerId, ProtocolId, TransportConfig}, event::Event, NetworkEventStream, NetworkNotification, NetworkPeers, NetworkService, NetworkStateInfo, NetworkWorker, @@ -77,6 +77,7 @@ struct TestNetworkBuilder { listen_addresses: Vec, set_config: Option, chain_sync_network: Option<(NetworkServiceProvider, NetworkServiceHandle)>, + notification_protocols: Vec, config: Option, } @@ -89,6 +90,7 @@ impl TestNetworkBuilder { listen_addresses: Vec::new(), set_config: None, chain_sync_network: None, + notification_protocols: Vec::new(), config: None, } } @@ -98,6 +100,11 @@ impl TestNetworkBuilder { self } + pub fn with_notification_protocol(mut self, config: config::NonDefaultSetConfig) -> Self { + self.notification_protocols.push(config); + self + } + pub fn with_listen_addresses(mut self, addresses: Vec) -> Self { self.listen_addresses = addresses; self @@ -115,13 +122,6 @@ impl TestNetworkBuilder { ); let network_config = self.config.unwrap_or(config::NetworkConfiguration { - extra_sets: vec![config::NonDefaultSetConfig { - notifications_protocol: PROTOCOL_NAME.into(), - fallback_names: Vec::new(), - max_notification_size: 1024 * 1024, - handshake: None, - set_config: self.set_config.unwrap_or_default(), - }], listen_addresses: self.listen_addresses, transport: TransportConfig::MemoryOnly, ..config::NetworkConfiguration::new_local() @@ -153,6 +153,7 @@ impl TestNetworkBuilder { let protocol_id = ProtocolId::from("test-protocol-name"); let fork_id = Some(String::from("test-fork-id")); + let mut full_net_config = FullNetworkConfiguration::new(&network_config); let block_request_protocol_config = { let (handler, protocol_config) = @@ -178,12 +179,11 @@ impl TestNetworkBuilder { let (chain_sync_network_provider, chain_sync_network_handle) = self.chain_sync_network.unwrap_or(NetworkServiceProvider::new()); let (tx, rx) = sc_utils::mpsc::tracing_unbounded("mpsc_syncing_engine_protocol", 100_000); - let (engine, chain_sync_service, block_announce_config) = SyncingEngine::new( Roles::from(&config::Role::Full), client.clone(), None, - &network_config, + &full_net_config, protocol_id.clone(), &None, Box::new(sp_consensus::block_validation::DefaultBlockAnnounceValidator), @@ -197,6 +197,29 @@ impl TestNetworkBuilder { ) .unwrap(); let mut link = self.link.unwrap_or(Box::new(chain_sync_service.clone())); + + if !self.notification_protocols.is_empty() { + for config in self.notification_protocols { + full_net_config.add_notification_protocol(config); + } + } else { + full_net_config.add_notification_protocol(config::NonDefaultSetConfig { + notifications_protocol: PROTOCOL_NAME.into(), + fallback_names: Vec::new(), + max_notification_size: 1024 * 1024, + handshake: None, + set_config: self.set_config.unwrap_or_default(), + }); + } + + for config in [ + block_request_protocol_config, + state_request_protocol_config, + light_client_request_protocol_config, + ] { + full_net_config.add_request_response_protocol(config); + } + let genesis_hash = client.hash(Zero::zero()).ok().flatten().expect("Genesis block exists; qed"); let worker = NetworkWorker::< @@ -209,16 +232,10 @@ impl TestNetworkBuilder { tokio::spawn(f); }), genesis_hash, - network_config, + network_config: full_net_config, protocol_id, fork_id, metrics_registry: None, - request_response_protocol_configs: [ - block_request_protocol_config, - state_request_protocol_config, - light_client_request_protocol_config, - ] - .to_vec(), tx, }) .unwrap(); @@ -553,14 +570,14 @@ async fn fallback_name_working() { let listen_addr = config::build_multiaddr![Memory(rand::random::())]; let (node1, mut events_stream1) = TestNetworkBuilder::new() + .with_notification_protocol(config::NonDefaultSetConfig { + notifications_protocol: NEW_PROTOCOL_NAME.into(), + fallback_names: vec![PROTOCOL_NAME.into()], + max_notification_size: 1024 * 1024, + handshake: None, + set_config: Default::default(), + }) .with_config(config::NetworkConfiguration { - extra_sets: vec![config::NonDefaultSetConfig { - notifications_protocol: NEW_PROTOCOL_NAME.into(), - fallback_names: vec![PROTOCOL_NAME.into()], - max_notification_size: 1024 * 1024, - handshake: None, - set_config: Default::default(), - }], listen_addresses: vec![listen_addr.clone()], transport: TransportConfig::MemoryOnly, ..config::NetworkConfiguration::new_local() diff --git a/client/service/src/builder.rs b/client/service/src/builder.rs index cb8986a5d..5237a6166 100644 --- a/client/service/src/builder.rs +++ b/client/service/src/builder.rs @@ -41,7 +41,10 @@ use sc_executor::{ NativeExecutionDispatch, RuntimeVersionOf, WasmExecutor, DEFAULT_HEAP_ALLOC_STRATEGY, }; use sc_keystore::LocalKeystore; -use sc_network::{config::SyncMode, NetworkService, NetworkStateInfo, NetworkStatusProvider}; +use sc_network::{ + config::{FullNetworkConfiguration, SyncMode}, + NetworkService, NetworkStateInfo, NetworkStatusProvider, +}; use sc_network_bitswap::BitswapRequestHandler; use sc_network_common::{role::Roles, sync::warp::WarpSyncParams}; use sc_network_light::light_client_requests::handler::LightClientRequestHandler; @@ -730,6 +733,8 @@ where pub struct BuildNetworkParams<'a, TBl: BlockT, TExPool, TImpQu, TCl> { /// The service configuration. pub config: &'a Configuration, + /// Full network configuration. + pub net_config: FullNetworkConfiguration, /// A shared client returned by `new_full_parts`. pub client: Arc, /// A shared transaction pool. @@ -773,6 +778,7 @@ where { let BuildNetworkParams { config, + mut net_config, client, transaction_pool, spawn_handle, @@ -781,8 +787,6 @@ where warp_sync_params, } = params; - let mut request_response_protocol_configs = Vec::new(); - if warp_sync_params.is_none() && config.network.sync_mode.is_warp() { return Err("Warp sync enabled, but no warp sync provider configured.".into()) } @@ -803,32 +807,35 @@ where Box::new(DefaultBlockAnnounceValidator) }; - let block_request_protocol_config = { + let (block_request_protocol_config, block_request_protocol_name) = { // Allow both outgoing and incoming requests. let (handler, protocol_config) = BlockRequestHandler::new( &protocol_id, config.chain_spec.fork_id(), client.clone(), - config.network.default_peers_set.in_peers as usize + - config.network.default_peers_set.out_peers as usize, + net_config.network_config.default_peers_set.in_peers as usize + + net_config.network_config.default_peers_set.out_peers as usize, ); + let config_name = protocol_config.name.clone(); spawn_handle.spawn("block-request-handler", Some("networking"), handler.run()); - protocol_config + (protocol_config, config_name) }; - let state_request_protocol_config = { + let (state_request_protocol_config, state_request_protocol_name) = { // Allow both outgoing and incoming requests. let (handler, protocol_config) = StateRequestHandler::new( &protocol_id, config.chain_spec.fork_id(), client.clone(), - config.network.default_peers_set_num_full as usize, + net_config.network_config.default_peers_set_num_full as usize, ); + let config_name = protocol_config.name.clone(); + spawn_handle.spawn("state-request-handler", Some("networking"), handler.run()); - protocol_config + (protocol_config, config_name) }; - let warp_sync_protocol_config = match warp_sync_params.as_ref() { + let (warp_sync_protocol_config, warp_request_protocol_name) = match warp_sync_params.as_ref() { Some(WarpSyncParams::WithProvider(warp_with_provider)) => { // Allow both outgoing and incoming requests. let (handler, protocol_config) = WarpSyncRequestHandler::new( @@ -841,10 +848,12 @@ where config.chain_spec.fork_id(), warp_with_provider.clone(), ); + let config_name = protocol_config.name.clone(); + spawn_handle.spawn("warp-sync-request-handler", Some("networking"), handler.run()); - Some(protocol_config) + (Some(protocol_config), Some(config_name)) }, - _ => None, + _ => (None, None), }; let light_client_request_protocol_config = { @@ -858,35 +867,57 @@ where protocol_config }; + // install request handlers to `FullNetworkConfiguration` + net_config.add_request_response_protocol(block_request_protocol_config); + net_config.add_request_response_protocol(state_request_protocol_config); + net_config.add_request_response_protocol(light_client_request_protocol_config); + + if let Some(config) = warp_sync_protocol_config { + net_config.add_request_response_protocol(config); + } + + if config.network.ipfs_server { + let (handler, protocol_config) = BitswapRequestHandler::new(client.clone()); + spawn_handle.spawn("bitswap-request-handler", Some("networking"), handler.run()); + net_config.add_request_response_protocol(protocol_config); + } + + // create transactions protocol and add it to the list of supported protocols of + // `network_params` + let transactions_handler_proto = sc_network_transactions::TransactionsHandlerPrototype::new( + protocol_id.clone(), + client + .block_hash(0u32.into()) + .ok() + .flatten() + .expect("Genesis block exists; qed"), + config.chain_spec.fork_id(), + ); + net_config.add_notification_protocol(transactions_handler_proto.set_config()); + let (tx, rx) = sc_utils::mpsc::tracing_unbounded("mpsc_syncing_engine_protocol", 100_000); let (chain_sync_network_provider, chain_sync_network_handle) = NetworkServiceProvider::new(); let (engine, sync_service, block_announce_config) = SyncingEngine::new( Roles::from(&config.role), client.clone(), config.prometheus_config.as_ref().map(|config| config.registry.clone()).as_ref(), - &config.network, + &net_config, protocol_id.clone(), &config.chain_spec.fork_id().map(ToOwned::to_owned), block_announce_validator, warp_sync_params, chain_sync_network_handle, import_queue.service(), - block_request_protocol_config.name.clone(), - state_request_protocol_config.name.clone(), - warp_sync_protocol_config.as_ref().map(|config| config.name.clone()), + block_request_protocol_name, + state_request_protocol_name, + warp_request_protocol_name, rx, )?; let sync_service_import_queue = sync_service.clone(); let sync_service = Arc::new(sync_service); - request_response_protocol_configs.push(config.network.ipfs_server.then(|| { - let (handler, protocol_config) = BitswapRequestHandler::new(client.clone()); - spawn_handle.spawn("bitswap-request-handler", Some("networking"), handler.run()); - protocol_config - })); - let genesis_hash = client.hash(Zero::zero()).ok().flatten().expect("Genesis block exists; qed"); - let mut network_params = sc_network::config::Params:: { + let network_params = sc_network::config::Params:: { role: config.role.clone(), executor: { let spawn_handle = Clone::clone(&spawn_handle); @@ -894,41 +925,16 @@ where spawn_handle.spawn("libp2p-node", Some("networking"), fut); }) }, - network_config: config.network.clone(), + network_config: net_config, genesis_hash, protocol_id: protocol_id.clone(), fork_id: config.chain_spec.fork_id().map(ToOwned::to_owned), metrics_registry: config.prometheus_config.as_ref().map(|config| config.registry.clone()), block_announce_config, tx, - request_response_protocol_configs: request_response_protocol_configs - .into_iter() - .chain([ - Some(block_request_protocol_config), - Some(state_request_protocol_config), - Some(light_client_request_protocol_config), - warp_sync_protocol_config, - ]) - .flatten() - .collect::>(), }; - // crate transactions protocol and add it to the list of supported protocols of `network_params` - let transactions_handler_proto = sc_network_transactions::TransactionsHandlerPrototype::new( - protocol_id.clone(), - client - .block_hash(0u32.into()) - .ok() - .flatten() - .expect("Genesis block exists; qed"), - config.chain_spec.fork_id(), - ); - network_params - .network_config - .extra_sets - .insert(0, transactions_handler_proto.set_config()); - - let has_bootnodes = !network_params.network_config.boot_nodes.is_empty(); + let has_bootnodes = !network_params.network_config.network_config.boot_nodes.is_empty(); let network_mut = sc_network::NetworkWorker::new(network_params)?; let network = network_mut.service().clone(); From a87e56f1696cae44593cdc069a01d8bed0bab636 Mon Sep 17 00:00:00 2001 From: Doordashcon Date: Thu, 11 May 2023 14:28:46 +0100 Subject: [PATCH 487/558] Benchmark pallet sudo (#13880) * set_key * v2 * update runtime * cargo fmt * remove commenting * ".git/.scripts/commands/bench/bench.sh" pallet dev pallet_sudo * impl weights * sudo & sudo_as * where * ".git/.scripts/commands/bench/bench.sh" pallet dev pallet_sudo * update weights * cargo fmt * Update Cargo.lock Signed-off-by: Oliver Tale-Yazdi --------- Signed-off-by: Oliver Tale-Yazdi Co-authored-by: command-bot <> Co-authored-by: Oliver Tale-Yazdi --- Cargo.lock | 1 + bin/node-template/runtime/Cargo.toml | 1 + bin/node-template/runtime/src/lib.rs | 2 + bin/node/runtime/Cargo.toml | 1 + bin/node/runtime/src/lib.rs | 2 + frame/sudo/Cargo.toml | 7 ++ frame/sudo/src/benchmarking.rs | 79 +++++++++++++++++ frame/sudo/src/lib.rs | 19 ++-- frame/sudo/src/mock.rs | 6 ++ frame/sudo/src/weights.rs | 127 +++++++++++++++++++++++++++ 10 files changed, 240 insertions(+), 5 deletions(-) create mode 100644 frame/sudo/src/benchmarking.rs create mode 100644 frame/sudo/src/weights.rs diff --git a/Cargo.lock b/Cargo.lock index 80fbf030f..b46cf17e6 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -7250,6 +7250,7 @@ dependencies = [ name = "pallet-sudo" version = "4.0.0-dev" dependencies = [ + "frame-benchmarking", "frame-support", "frame-system", "parity-scale-codec", diff --git a/bin/node-template/runtime/Cargo.toml b/bin/node-template/runtime/Cargo.toml index 930c5b5c4..d7f7ca4d8 100644 --- a/bin/node-template/runtime/Cargo.toml +++ b/bin/node-template/runtime/Cargo.toml @@ -95,6 +95,7 @@ runtime-benchmarks = [ "frame-system/runtime-benchmarks", "pallet-balances/runtime-benchmarks", "pallet-grandpa/runtime-benchmarks", + "pallet-sudo/runtime-benchmarks", "pallet-template/runtime-benchmarks", "pallet-timestamp/runtime-benchmarks", "sp-runtime/runtime-benchmarks", diff --git a/bin/node-template/runtime/src/lib.rs b/bin/node-template/runtime/src/lib.rs index 3dcf1b556..c138b390b 100644 --- a/bin/node-template/runtime/src/lib.rs +++ b/bin/node-template/runtime/src/lib.rs @@ -265,6 +265,7 @@ impl pallet_transaction_payment::Config for Runtime { impl pallet_sudo::Config for Runtime { type RuntimeEvent = RuntimeEvent; type RuntimeCall = RuntimeCall; + type WeightInfo = pallet_sudo::weights::SubstrateWeight; } /// Configure the pallet-template in pallets/template. @@ -336,6 +337,7 @@ mod benches { [frame_system, SystemBench::] [pallet_balances, Balances] [pallet_timestamp, Timestamp] + [pallet_sudo, Sudo] [pallet_template, TemplateModule] ); } diff --git a/bin/node/runtime/Cargo.toml b/bin/node/runtime/Cargo.toml index dadd868e1..3909eff2a 100644 --- a/bin/node/runtime/Cargo.toml +++ b/bin/node/runtime/Cargo.toml @@ -274,6 +274,7 @@ runtime-benchmarks = [ "pallet-society/runtime-benchmarks", "pallet-staking/runtime-benchmarks", "pallet-state-trie-migration/runtime-benchmarks", + "pallet-sudo/runtime-benchmarks", "pallet-timestamp/runtime-benchmarks", "pallet-tips/runtime-benchmarks", "pallet-transaction-storage/runtime-benchmarks", diff --git a/bin/node/runtime/src/lib.rs b/bin/node/runtime/src/lib.rs index 02a126cc4..180e4e247 100644 --- a/bin/node/runtime/src/lib.rs +++ b/bin/node/runtime/src/lib.rs @@ -1251,6 +1251,7 @@ impl pallet_contracts::Config for Runtime { impl pallet_sudo::Config for Runtime { type RuntimeEvent = RuntimeEvent; type RuntimeCall = RuntimeCall; + type WeightInfo = pallet_sudo::weights::SubstrateWeight; } parameter_types! { @@ -1962,6 +1963,7 @@ mod benches { [pallet_session, SessionBench::] [pallet_staking, Staking] [pallet_state_trie_migration, StateTrieMigration] + [pallet_sudo, Sudo] [frame_system, SystemBench::] [pallet_timestamp, Timestamp] [pallet_tips, Tips] diff --git a/frame/sudo/Cargo.toml b/frame/sudo/Cargo.toml index e1161497b..c50b3da26 100644 --- a/frame/sudo/Cargo.toml +++ b/frame/sudo/Cargo.toml @@ -14,6 +14,7 @@ targets = ["x86_64-unknown-linux-gnu"] [dependencies] codec = { package = "parity-scale-codec", version = "3.2.2", default-features = false, features = ["derive"] } +frame-benchmarking = { version = "4.0.0-dev", default-features = false, optional = true, path = "../benchmarking" } scale-info = { version = "2.5.0", default-features = false, features = ["derive"] } frame-support = { version = "4.0.0-dev", default-features = false, path = "../support" } frame-system = { version = "4.0.0-dev", default-features = false, path = "../system" } @@ -28,6 +29,7 @@ sp-core = { version = "7.0.0", path = "../../primitives/core" } default = ["std"] std = [ "codec/std", + "frame-benchmarking?/std", "frame-support/std", "frame-system/std", "scale-info/std", @@ -35,4 +37,9 @@ std = [ "sp-runtime/std", "sp-std/std", ] +runtime-benchmarks = [ + "frame-benchmarking/runtime-benchmarks", + "frame-system/runtime-benchmarks", + "sp-runtime/runtime-benchmarks", +] try-runtime = ["frame-support/try-runtime"] diff --git a/frame/sudo/src/benchmarking.rs b/frame/sudo/src/benchmarking.rs new file mode 100644 index 000000000..6a365c187 --- /dev/null +++ b/frame/sudo/src/benchmarking.rs @@ -0,0 +1,79 @@ +// This file is part of Substrate. + +// Copyright (C) Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +//! Benchmarks for Sudo Pallet + +use super::*; +use crate::Pallet; +use frame_benchmarking::v2::*; +use frame_system::RawOrigin; + +const SEED: u32 = 0; + +fn assert_last_event(generic_event: ::RuntimeEvent) { + frame_system::Pallet::::assert_last_event(generic_event.into()); +} + +#[benchmarks( where ::RuntimeCall: From>)] +mod benchmarks { + use super::*; + + #[benchmark] + fn set_key() { + let caller: T::AccountId = whitelisted_caller(); + Key::::put(caller.clone()); + + let new_sudoer: T::AccountId = account("sudoer", 0, SEED); + let new_sudoer_lookup = T::Lookup::unlookup(new_sudoer.clone()); + + #[extrinsic_call] + _(RawOrigin::Signed(caller.clone()), new_sudoer_lookup); + + assert_last_event::(Event::KeyChanged { old_sudoer: Some(caller) }.into()); + } + + #[benchmark] + fn sudo() { + let caller: T::AccountId = whitelisted_caller(); + Key::::put(caller.clone()); + + let call: ::RuntimeCall = frame_system::Call::remark { remark: vec![] }.into(); + + #[extrinsic_call] + _(RawOrigin::Signed(caller.clone()), Box::new(call.clone())); + + assert_last_event::(Event::Sudid { sudo_result: Ok(()) }.into()) + } + + #[benchmark] + fn sudo_as() { + let caller: T::AccountId = whitelisted_caller(); + Key::::put(caller.clone()); + + let call: ::RuntimeCall = frame_system::Call::remark { remark: vec![] }.into(); + + let who: T::AccountId = account("as", 0, SEED); + let who_lookup = T::Lookup::unlookup(who.clone()); + + #[extrinsic_call] + _(RawOrigin::Signed(caller), who_lookup, Box::new(call.clone())); + + assert_last_event::(Event::SudoAsDone { sudo_result: Ok(()) }.into()) + } + + impl_benchmark_test_suite!(Pallet, crate::mock::new_bench_ext(), crate::mock::Test); +} diff --git a/frame/sudo/src/lib.rs b/frame/sudo/src/lib.rs index 1bc206e00..a35b870ed 100644 --- a/frame/sudo/src/lib.rs +++ b/frame/sudo/src/lib.rs @@ -110,6 +110,11 @@ mod mock; #[cfg(test)] mod tests; +#[cfg(feature = "runtime-benchmarks")] +mod benchmarking; +pub mod weights; +pub use weights::WeightInfo; + pub use extension::CheckOnlySudoAccount; pub use pallet::*; @@ -130,6 +135,9 @@ pub mod pallet { type RuntimeCall: Parameter + UnfilteredDispatchable + GetDispatchInfo; + + /// Type representing the weight of this pallet + type WeightInfo: WeightInfo; } #[pallet::pallet] @@ -146,7 +154,10 @@ pub mod pallet { #[pallet::call_index(0)] #[pallet::weight({ let dispatch_info = call.get_dispatch_info(); - (dispatch_info.weight, dispatch_info.class) + ( + T::WeightInfo::sudo().saturating_add(dispatch_info.weight), + dispatch_info.class + ) })] pub fn sudo( origin: OriginFor, @@ -195,7 +206,7 @@ pub mod pallet { /// ## Complexity /// - O(1). #[pallet::call_index(2)] - #[pallet::weight({0})] // FIXME + #[pallet::weight(T::WeightInfo::set_key())] pub fn set_key( origin: OriginFor, new: AccountIdLookupOf, @@ -222,9 +233,7 @@ pub mod pallet { #[pallet::weight({ let dispatch_info = call.get_dispatch_info(); ( - dispatch_info.weight - // AccountData for inner call origin accountdata. - .saturating_add(T::DbWeight::get().reads_writes(1, 1)), + T::WeightInfo::sudo_as().saturating_add(dispatch_info.weight), dispatch_info.class, ) })] diff --git a/frame/sudo/src/mock.rs b/frame/sudo/src/mock.rs index 6d6043cfd..95a6507c1 100644 --- a/frame/sudo/src/mock.rs +++ b/frame/sudo/src/mock.rs @@ -148,6 +148,7 @@ impl logger::Config for Test { impl Config for Test { type RuntimeEvent = RuntimeEvent; type RuntimeCall = RuntimeCall; + type WeightInfo = (); } // New types for dispatchable functions. @@ -162,3 +163,8 @@ pub fn new_test_ext(root_key: u64) -> sp_io::TestExternalities { .unwrap(); t.into() } + +#[cfg(feature = "runtime-benchmarks")] +pub fn new_bench_ext() -> sp_io::TestExternalities { + frame_system::GenesisConfig::default().build_storage::().unwrap().into() +} diff --git a/frame/sudo/src/weights.rs b/frame/sudo/src/weights.rs new file mode 100644 index 000000000..bc81cd607 --- /dev/null +++ b/frame/sudo/src/weights.rs @@ -0,0 +1,127 @@ +// This file is part of Substrate. + +// Copyright (C) Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +//! Autogenerated weights for pallet_sudo +//! +//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev +//! DATE: 2023-05-09, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! WORST CASE MAP SIZE: `1000000` +//! HOSTNAME: `bm3`, CPU: `Intel(R) Core(TM) i7-7700K CPU @ 4.20GHz` +//! EXECUTION: Some(Wasm), WASM-EXECUTION: Compiled, CHAIN: Some("dev"), DB CACHE: 1024 + +// Executed Command: +// target/production/substrate +// benchmark +// pallet +// --steps=50 +// --repeat=20 +// --extrinsic=* +// --execution=wasm +// --wasm-execution=compiled +// --heap-pages=4096 +// --json-file=/var/lib/gitlab-runner/builds/zyw4fam_/0/parity/mirrors/substrate/.git/.artifacts/bench.json +// --pallet=pallet_sudo +// --chain=dev +// --header=./HEADER-APACHE2 +// --output=./frame/sudo/src/weights.rs +// --template=./.maintain/frame-weight-template.hbs + +#![cfg_attr(rustfmt, rustfmt_skip)] +#![allow(unused_parens)] +#![allow(unused_imports)] +#![allow(missing_docs)] + +use frame_support::{traits::Get, weights::{Weight, constants::RocksDbWeight}}; +use core::marker::PhantomData; + +/// Weight functions needed for pallet_sudo. +pub trait WeightInfo { + fn set_key() -> Weight; + fn sudo() -> Weight; + fn sudo_as() -> Weight; +} + +/// Weights for pallet_sudo using the Substrate node and recommended hardware. +pub struct SubstrateWeight(PhantomData); +impl WeightInfo for SubstrateWeight { + /// Storage: Sudo Key (r:1 w:1) + /// Proof: Sudo Key (max_values: Some(1), max_size: Some(32), added: 527, mode: MaxEncodedLen) + fn set_key() -> Weight { + // Proof Size summary in bytes: + // Measured: `165` + // Estimated: `1517` + // Minimum execution time: 13_962_000 picoseconds. + Weight::from_parts(14_283_000, 1517) + .saturating_add(T::DbWeight::get().reads(1_u64)) + .saturating_add(T::DbWeight::get().writes(1_u64)) + } + /// Storage: Sudo Key (r:1 w:0) + /// Proof: Sudo Key (max_values: Some(1), max_size: Some(32), added: 527, mode: MaxEncodedLen) + fn sudo() -> Weight { + // Proof Size summary in bytes: + // Measured: `165` + // Estimated: `1517` + // Minimum execution time: 14_009_000 picoseconds. + Weight::from_parts(14_400_000, 1517) + .saturating_add(T::DbWeight::get().reads(1_u64)) + } + /// Storage: Sudo Key (r:1 w:0) + /// Proof: Sudo Key (max_values: Some(1), max_size: Some(32), added: 527, mode: MaxEncodedLen) + fn sudo_as() -> Weight { + // Proof Size summary in bytes: + // Measured: `165` + // Estimated: `1517` + // Minimum execution time: 13_954_000 picoseconds. + Weight::from_parts(14_248_000, 1517) + .saturating_add(T::DbWeight::get().reads(1_u64)) + } +} + +// For backwards compatibility and tests +impl WeightInfo for () { + /// Storage: Sudo Key (r:1 w:1) + /// Proof: Sudo Key (max_values: Some(1), max_size: Some(32), added: 527, mode: MaxEncodedLen) + fn set_key() -> Weight { + // Proof Size summary in bytes: + // Measured: `165` + // Estimated: `1517` + // Minimum execution time: 13_962_000 picoseconds. + Weight::from_parts(14_283_000, 1517) + .saturating_add(RocksDbWeight::get().reads(1_u64)) + .saturating_add(RocksDbWeight::get().writes(1_u64)) + } + /// Storage: Sudo Key (r:1 w:0) + /// Proof: Sudo Key (max_values: Some(1), max_size: Some(32), added: 527, mode: MaxEncodedLen) + fn sudo() -> Weight { + // Proof Size summary in bytes: + // Measured: `165` + // Estimated: `1517` + // Minimum execution time: 14_009_000 picoseconds. + Weight::from_parts(14_400_000, 1517) + .saturating_add(RocksDbWeight::get().reads(1_u64)) + } + /// Storage: Sudo Key (r:1 w:0) + /// Proof: Sudo Key (max_values: Some(1), max_size: Some(32), added: 527, mode: MaxEncodedLen) + fn sudo_as() -> Weight { + // Proof Size summary in bytes: + // Measured: `165` + // Estimated: `1517` + // Minimum execution time: 13_954_000 picoseconds. + Weight::from_parts(14_248_000, 1517) + .saturating_add(RocksDbWeight::get().reads(1_u64)) + } +} From c09f0bc5e57fde3904da4600ff3ba23b1b00022e Mon Sep 17 00:00:00 2001 From: Muharem Ismailov Date: Thu, 11 May 2023 17:56:01 +0200 Subject: [PATCH 488/558] Assets: impl ContainsPair for asset and account IDs (#14119) * Assets impl ContainsPair for asset and account IDs * clap version --- frame/assets/src/lib.rs | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/frame/assets/src/lib.rs b/frame/assets/src/lib.rs index e6b59bba5..c25f33ae3 100644 --- a/frame/assets/src/lib.rs +++ b/frame/assets/src/lib.rs @@ -202,7 +202,10 @@ impl AssetsCallback for () {} #[frame_support::pallet] pub mod pallet { use super::*; - use frame_support::{pallet_prelude::*, traits::AccountTouch}; + use frame_support::{ + pallet_prelude::*, + traits::{AccountTouch, ContainsPair}, + }; use frame_system::pallet_prelude::*; /// The current storage version. @@ -1645,7 +1648,7 @@ pub mod pallet { } } - /// Implements [AccountTouch] trait. + /// Implements [`AccountTouch`] trait. /// Note that a depositor can be any account, without any specific privilege. /// This implementation is supposed to be used only for creation of system accounts. impl, I: 'static> AccountTouch for Pallet { @@ -1659,6 +1662,14 @@ pub mod pallet { Self::do_touch(asset, who, depositor, false) } } + + /// Implements [`ContainsPair`] trait for a pair of asset and account IDs. + impl, I: 'static> ContainsPair for Pallet { + /// Check if an account with the given asset ID and account address exists. + fn contains(asset: &T::AssetId, who: &T::AccountId) -> bool { + Account::::contains_key(asset, who) + } + } } sp_core::generate_feature_enabled_macro!(runtime_benchmarks_enabled, feature = "runtime-benchmarks", $); From e6a13b807a88d25aa1cd0d320edb9412c3692c67 Mon Sep 17 00:00:00 2001 From: Falco Hirschenberger Date: Thu, 11 May 2023 18:25:50 +0200 Subject: [PATCH 489/558] Create benchmark for the `system::set_code` instrisic (#13373) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Still WIP # Conflicts: # frame/system/src/weights.rs * Still WIP * Add benchmark for system::set_code intrinsic fixes #13192 * Fix format * Add missing benchmark runtime * Fix lint warning * Consume the rest of the block and add test verification after the benchmark * Rewrite set_code function * Try to fix benchmarks and tests * Remove weight tags * Update frame/system/src/tests.rs Co-authored-by: Bastian Köcher * Register ReadRuntimeVersionExt for benches Signed-off-by: Oliver Tale-Yazdi * Fix tests * Fix deprecations * Fix deprecations * ".git/.scripts/commands/bench/bench.sh" pallet dev frame_system * Add update info and remove obsolete complexity comments. * ".git/.scripts/commands/fmt/fmt.sh" * Update frame/system/src/lib.rs Co-authored-by: Bastian Köcher * Update frame/system/src/lib.rs Co-authored-by: Bastian Köcher * Update frame/system/src/lib.rs Co-authored-by: Bastian Köcher * Update frame/system/src/lib.rs Co-authored-by: Bastian Köcher * Update frame/system/benchmarking/src/lib.rs Co-authored-by: Bastian Köcher * ".git/.scripts/commands/fmt/fmt.sh" * Update README.md Just trigger CI rebuild * Update README.md Trigger CI --------- Signed-off-by: Oliver Tale-Yazdi Co-authored-by: Bastian Köcher Co-authored-by: Oliver Tale-Yazdi Co-authored-by: command-bot <> --- Cargo.lock | 3 + frame/system/Cargo.toml | 1 + frame/system/benchmarking/Cargo.toml | 2 + frame/system/benchmarking/res/README.md | 5 + ...itchensink_runtime.compact.compressed.wasm | Bin 0 -> 4250564 bytes frame/system/benchmarking/src/lib.rs | 9 +- frame/system/benchmarking/src/mock.rs | 25 +++- frame/system/src/lib.rs | 41 +++--- frame/system/src/tests.rs | 7 +- frame/system/src/weights.rs | 122 +++++++++++------- frame/utility/src/tests.rs | 8 +- 11 files changed, 148 insertions(+), 75 deletions(-) create mode 100644 frame/system/benchmarking/res/README.md create mode 100644 frame/system/benchmarking/res/kitchensink_runtime.compact.compressed.wasm diff --git a/Cargo.lock b/Cargo.lock index b46cf17e6..740e45a0d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2804,6 +2804,7 @@ dependencies = [ name = "frame-system" version = "4.0.0-dev" dependencies = [ + "cfg-if", "criterion", "frame-support", "log", @@ -2830,9 +2831,11 @@ dependencies = [ "parity-scale-codec", "scale-info", "sp-core", + "sp-externalities", "sp-io", "sp-runtime", "sp-std", + "sp-version", ] [[package]] diff --git a/frame/system/Cargo.toml b/frame/system/Cargo.toml index c97eb6638..1fa52de10 100644 --- a/frame/system/Cargo.toml +++ b/frame/system/Cargo.toml @@ -13,6 +13,7 @@ readme = "README.md" targets = ["x86_64-unknown-linux-gnu"] [dependencies] +cfg-if = "1.0" codec = { package = "parity-scale-codec", version = "3.2.2", default-features = false, features = ["derive"] } log = { version = "0.4.17", default-features = false } scale-info = { version = "2.5.0", default-features = false, features = ["derive"] } diff --git a/frame/system/benchmarking/Cargo.toml b/frame/system/benchmarking/Cargo.toml index 50009387a..8b189848d 100644 --- a/frame/system/benchmarking/Cargo.toml +++ b/frame/system/benchmarking/Cargo.toml @@ -24,6 +24,8 @@ sp-std = { version = "5.0.0", default-features = false, path = "../../../primiti [dev-dependencies] sp-io = { version = "7.0.0", path = "../../../primitives/io" } +sp-externalities = { version = "0.13.0", path = "../../../primitives/externalities" } +sp-version = { version = "5.0.0", path = "../../../primitives/version" } [features] default = ["std"] diff --git a/frame/system/benchmarking/res/README.md b/frame/system/benchmarking/res/README.md new file mode 100644 index 000000000..43bb2b5c2 --- /dev/null +++ b/frame/system/benchmarking/res/README.md @@ -0,0 +1,5 @@ +These runtimes are used for benchmarking the `set_code` intrinsic. + +**Don't use them in production environments!** + +To update the just copy the new runtime from `target/release/wbuild/kitchensink-runtime/kitchensink_runtime.compact.compressed.wasm` to here. diff --git a/frame/system/benchmarking/res/kitchensink_runtime.compact.compressed.wasm b/frame/system/benchmarking/res/kitchensink_runtime.compact.compressed.wasm new file mode 100644 index 0000000000000000000000000000000000000000..a0d2a4bb04b9132da91d825d844e7d67f5a55408 GIT binary patch literal 4250564 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%0Ak6qKkU2wi208(-(ZJpo-f)249AQPw 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ăJblNF@zy@NV`(u23 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�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

aNQUcmNw?vIH3EVjgRF{sX@VA#rd#b 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

rOEq8#mbk=^gS!Dq9AF?fdNK-yLK)%Q zqdt9)XC9BINBw^6;|?^-T;n+Z@}>ZIGtW!usTAIn&DevW0~E@ZM2c^=hyk7$3@@&H z*q*#Oe8a8wGQgK_7$;@Bupigh%sC%h!7yxyD?niRV*j6av+UnZ(xb8yw?Dn6U`97PpH&K1Ve3}HCGv}x4V0&`YVf%pnn5L$N zCP;gs%jhPKY8KEG(oSG=wh__RG;?R3^iB%ONvL;vCr3#Uu2{j66?{R1)oGLuBaWB} zvcpYT7sERu9qVeTcxA;U2bh%>1jImq2NPEq*MG{G(_+#^*Y|c_WO!F{L_n{#@BtA) z?0kr^#N~E)&BD(B5RlbnPb7Mm+mf9~WUaHuDv!rUv-Cw_U2u1IXmf)F9r(v^wtW`0 z7>|J`cO@WBlsz??V;RGViA=y4E)*HDQN-Vpu%D-P7!^}d2t2WyK;Nv^ahAwYcIPE> zRUFn9#PWN}cA%f>APe;I`v~^y)aC)3rLxY2xGD)&$35zFpd*(%BR4lUtRXG}SHguf zi}srnxzWfJEGt-8-1yD%l(#(PDNlLIQ>m27j*(?^)+!QpOPT6!8E^bAzT|{xuU3&r z{tQtE!fHjZbMAv)bmtwJnQy%D=?++eA7gUdyDxrsvAufZTxsM=-Ro3@vb&4d-l#o$fw~`ifx4SW z#4K+)%RTOBJj8gj-nQ2`&vj{=>vaA_dyy8wyMDn^tZ@^wXb^1HMGAzQ8zV%zO~AyE zEn0zvk{h4KEoxvXeoc)kw(8mw8?vDI*|d`9)~&VHqCh~9kt3y@OLTXR!dpaOch698 z=Lpow<8>cZt&d<_rS$y>+KqttQOpbynfmlcGEY&bUmM}%jT}GUkK0|dV2N$q{RqB!HA`e>c2{Ld9DzuZCWtg~({X#0lUB@=(YPkF zUX=2Y7R{_O#z90%41J@-^Us*UF%?>iMez9=-+~};d&<>#%Y8A| zzK8zvZYcEcCKZ(q^j!BS`;&_ybTU&Ox(SovJs6D?6z+rg6E36znp@YS#($`VHMSZy z{_}YX>OLSB1m}BbpoC_4=%)EHcxcSfg=AO5(m3%#KYHe}ZTr~x&!pT3rqyi+`tz}E0dxvGP@Oxe&SjKW>k4Au zCEx!)2oIwFos{X^`Lr9c%ycx%>_*)27drF{*3-e{(4KtSq>tRHL;U`CG0N2`m&@gH zxm=?rfDsFxwbAhCjKt^FI-sD_~$61I)1l+Q1Lt6D~KMO=;8FUJ4XJ z3qAjeb%z6%aELAuGby*bBVy*pT3htBwqyz_=8jc1LTiX%1fICMR7xp4&2Z^6ZB&X= z+5>r^&no|(LBd^wUi%sEKHm@IJ|?Mk@zsk3M}SQUspn(|+o| zaw!gbC^w}xIJ67Gp6=tEOW&qq9dq>-fhGE7*sitKiqZi~)W*hjD5cbfRvH_mV)&68 zf?2ga6nL89dM5chOr^5NFTXvn-D4g(&-3Tm@m`EE#>lDodw$b4?Zql7ZK|P3o`ZuY z8eqv~+H*|{JEx|FogQP1ky|wI=imu@{|=t&`_lkT7s%iXMN?@Gj1D7s0iMfU^%!G}9n8P{<>TW-Zx_YcL6)J(KJ-ph zK`e5UqoSLfW~Ib+DO57BHJAaOrg)|SJk8e$f~yC&b8wrA>NIZZEnHIE)`DO`Qdk^V z>esZe{8e)lJRt#duo37UfF_OsB=w`9WcOoM2D41lw6J_XM}0PaARo<9oTF17&X=@j zA_C)_iX3< zMrYEVE3uw0B3{qO_iPKm=1sHsJCobY$&1y;wAeKgj=)KAa^of(Ced%)1Yi2Qi}w06 zw>hFeAoy1&1EcOtnuUwq-k29n#qQB{mB7; z=CX{VT{Gc~;M+Ov^@q3%jMrbn#OGhA~QX<*LB z&DQYpsKCVul-Rt?Z~!`C20?06!3&gDf|?*VKqV|u1e*;7NmM+Y>$=k7t-L3aPLj{E zgG|H@W*$~95k7pZR5mO=!pu!qJ&ADV1${QqXLE5=E5St5NG*%TLN>JNnRO=N9IzF9 z1&EpO5+$*E0%wNtak#WgitGiSFj60lSb-lpF}nuR8#8V67H>2J{VwAyfK&-ZMg z)ARBdCxNCXZ#-nL1QVB1n!8;Zi?l~0t8btm1c{8R$f8w(o(94G1NFrQNwvJ#|3G~S zJ$*TeKHa%ee75alD!ybjR7Ct}TC=o}_B0LKw5S9$xfqzgJQ}TCOu-UOU7Er+3Iz)< z=ttRO+sm9#8P;(U-TsIF=YiN_(_oww+lRPRefhlQf_$JQRbRHQQ!h%tO3QFp=Py{I z@2?v-jq2OC-H-U62X!C%Km0ewx%9JIY1VlqX;cD;)^uoUBGH0Hq5 z7GFS}r<@d{M*+R~(N)sMO}*o!){R}TS{KW(UyL7X;(JkJ&5dH<#GKxVIYHVBT}C%S zHOm&sfTdVOj8$-S#he@CW3h@gO;}%H(GyQ%$bw>Ycj;oeZ0Mizl)r!BI_`28jMA9^ zZ@N>SszEvkEQLA=F7&Vk%*~Cjq>>4%dtR$MM5{po%|1!5B(4OC;velP1|(9Uejhv z&0658zn1tvLXYx4PTu|paefFr%FBehhfYHI*vWWKns2>3<5Wd15&>~>g+iG@Qk^sa zAAFvtHX4Da*1llle=RrO>|?e5g7w_5U zp7(SNOWJFhmU+4wuu6Lj1KiocjPcoe>uMDR^NaST%W=Z^JGIWkfTfkqIvzr_H){*5 ze`BA0{O>*c`F#YQkiZ-+1SnL%b<5NF5?G2er>emA?{L!mIp^KsCT3#JIiEM#L=o7; zDA)cI01epWI5$qN=YHLSrzzlxCk=T1MBNZ^g{AqdYE7>}J%I`=d?ajeAXisrn#H|~E9E=)RKOE=0e~gRYBf!-xB=K$nl($6*Pg7Q zy%o+pPtwRC?Mc=38u{=u&}-d{=RrTS{pW$a*!gtRq2Kev^Vv%ep4i3kmuK|D_Syrn zz4n9l+XJEf)_ER?>^&RZY7>^i7ap`*I%DxYmBDdli@eOYO(U7bP|DW#N-fz?`Z zyk1Orp#a7p@i`hd{)`jp7~coS*jGv^C4Cs~2qTbKRD9?t8@=9vZ_6V(BPtU6c`?Qa zeotZbWwQn)fOHFB-O{kQKAG3hy@Nfcpk^uun`JjI5gHjW;w zMWV{VenrtcMo=ks=pVX+-=;Crf+JnuqxigtvzjFYPaxPD6}Mg@I1C%SPSjexey#7) ztETaluY6T&g)U$D%5ss`Po=+At!C9zvA9}+dweYiHhMrMV=8hX=wHYJmt$v}Eu|V6 z-jaVsb}OwN$SHhG1PK?u10W02z#rhLKEwitTT=kI0zV+GxnX`@f>UFYBno;h9SODR{mCrT)?sIZq1n%cy-1KSgJe4VIf7aL$ zBP7WoB7_JNZ@y%XofkcJvHPY^_cT_&hR_8rpumNYIXgcAOYTn79;_9@hmfUXI>@T_ zoU8oJL}1D1ob%`RqrQW1!U^Z>poH#SM^}&q72Vy;%*_b62ylt=-(6hV@W2aENcd5< zjIYnW_2}{hmWD;ipi0)#LQ^Ut99A>&Xp;QT!y($FeJS+;@DyLmJUBqFvSCD>S0LPS z6XQsU1F-Q80N@Wr#{UWe(?nKAR?GR;9-XI4fUPtBJI^WzkWZ_sJg>V74Dd{uk}-FWt9B*s9L|0z%mlN zy=IP^`JI0VmG=Az(dBymB@%`ZB_V@3@cce4glcNY8umKToXNe?AHS{;~b1^Y3n?ZQMxfMnCv( z4@Cc``|7dIQ zSno#K`+vSaKhioG|4-My{Ya*_ZU_;~TFX4GWtyhJ7SCrOR!^n9xC1*oQA&I9H62(+ z%^GMn6^ZtawX-14Q;D2Pj4{4-k`+NER31FlmrsJG9$z<%&%!p(>Fst5DU zQr%?T1oDVH^|Q}BoQCqXeiq_Jo)3At_dJ9l&*!n%(JZ4+w?p5LhXxdGR9C*t6K-$? z59EI^&y_fOU^gDN>%o%zrh%>Z^EBT^KY$+PWk8S8%^>moNjDQ8)Is-jGWJ8=jKA$( z+jiJKdr;Qf0t#@mua)Dl|8yfB;Qe$1u)@~MFhhuL20R@0HEWjb!r*8Yt&`oz)A@jP zG1?9^9oQ`6b z#tVFbsUNUkld$vQ%ZTaE?#FyOpLSmjgWXuw6$C2(xUsW;7H%=~V_@KkedQz{APW!a z0kSX?(DQedr+QU)z&1@q!uETf2Xn*B%&K;IXIfa{tVrn|B@O10-pY!bs>ONHI1(iF zXI#JJJ{)+s{4o%+1}ZFd}_aWM+M92JDeUh8VdEhkBm{Eu5$Hv%HQccb~(Ki2&~BGZrk4}T!< z`O)2oJKD+2^j8by1?#Z)JP_D>x*tenviJOt5x>+2!t*2RM%<0C2O@hd>qh+9!xtd1 z*IvWA5qB}$YY#`Wlfv`x1zWUzBqAll-7U%QJRKymZL6FC9+Y+5EKt(Yg%w$EqNGSDh|t+mD$?z3@j zJy0-Q+JmW93vD0zRPI~hU~;f=iD{gE3~~?lO0K0n`9s9q9ALO*hk%;0!kSKh_V2;9 zLQmyaYH;bVc8vRpE?hBzkQDB@cl}_BoIBqi_KRq|Qy-q(19{oL%KUTP3=;Y?-M`E~ z_gpta!+%#rqFWmP%0S%LF}OXp2LTZ=Gcz+YGgCTg*JA_yr_%|6<=?)?_I>2`*gj+J z!yUPsoLaMpUcOCf6Akae@l9FJV;J1{i}vJe+!u4Tw=*aT4N*#iPk z-6v1&H_r9|y^VKiYD%HOE=glX3d%`{>w-{$I2Y7?*2Zhx*JFs4JLZeGmd? zhwH{foBzBU?UEI)axPqjrS?3Ivtc8I(XOQq!7>dGiR>z-i5bGGr)d?<5k|0TNVD_@ zo3IMSs3v!2hx2Ntpc>&a_7JkfK+h4t6XQ8~Vl2uYYF!AH>UUCsOSAuQp-Z1@-;DeC z0=X39B>XT9TuCGM$-99oe{vAY?f~1bxM}OY1`&>C;m-E2kkC^3DQV{PpE7!#6tq@R zY0OOobRWXX2U^%X(JGrI zHc!kNR^aQvY;fPo4j5v10S5Hq0nqdVB+2)zi!oxqdW4hYd%7Ll19!P=SsBBwe8u{+ zK2}y-egkf-wVC6`L)W_);|$7$px5KJg{9Dq+-HH?D=siAEeOZ~0z6_M6Bx!9wF=vL zr?D5TU%t_WN6W62r9Y3B+%WCMA(rrn=_Nii3;&9LE%*Iju?m?;1W(NZPti21lcdC= z0635eAZx(ctNUJ)v)1-$~tyzFDyAvyr#4XTc z&rMJ70dDMwn7RF|;oznUnsw(HIDXCQ-ZaRS$B8G7`rgS z7TRweMiygaj_EoTEe^_w4i1j>thLr!w_)NY1WlaiA_ZiSTDN|-T15eS#fAHMueyS# z?p7SPJ|C&A{>XJ8wF*lw^weI7aNPQ}E4Mr-CocA@tcYD{ zufC_Np*>zP4(BurTw(QlHD9h)MOZgjwX!c{&`tAW%veo$nH>_Bu?0@&p%W_f*NwroH3wVN8s$4~LwG#LSN2uFeWizO-vL?Th68r7^uHL6jKaviL2L?uCi5)_AQsDr66up~cY7o|rIR=BNfP>O8Nhfq-rJNn> zz>@rwr{sI9ia;*UT`ADZ_#TPLDyf_jbz+3q#T>(U3rNBq^DbjC5WDICFAJwkIg>DG%bQAnnmqd+pJW={)7A zPKf4qv`6iwY;U0Nk@5|7l#{5udHGXD+AcBe>-Tsv&u3ZY!7xnoboN|N1_UH@I9yQJQC}(h^`7|EB zq;M!GG`b%)rE8ayXzN`1E0@X+^$w6bcj$*=&84x34a@QkC#h1{*r)HQ+D)!WKD0*! z-}$fwR&w8OWrgDb-uSWeIad=33)_Qf54JZmr|g2KCjAANBR&Cbpihfn&QY*LWfzIS z5{<0(=*y}C%QW!h1T@`5akI(6v`5*7Qd&k1yP9ADo1)A8KLK=rO>uSOgiX+M={pke z#9qJ>ecBy}EBe%3H%?@?`cxu)9wHtp7?n%u$H+k7=?l>;^Qj2#cofwvo{G)pq6+;N z)-+T>f-zuh4lb!lDW$BhwbUvSG*_O}ND^q>R zm)VUplbN2V%liCbQ;o6HU<9EVtaoCPVXoB|=t+ z53Sb}ouJb7Yr!)o0LNPZsg?x? zI(M~RSilDGU3nu_o|xbo;t*OrI&|pWW)IK+As4TKpy+feEh?d2hf7@Q94^2Ad`k7k z?Fr-t%z709VPm;u+4wB*(;<0;vOv zpj2rGAFivxjr&FB@2GB@5v4k+PD6Pgz=)*N2dR1E&HXM3t1CPdj3`pyr&6S~WcZKl zsHr|}m-$RhG@joFXhlMMJv8L7DZgv6%n`|>-sqyCNCHsbx()4Ng#NgOrLwr6%PXix zBg|Quno5s9PYpkumA9BL_`|!wvLjKNc9|DUPQ}<05-7 zUBmbAO(~J$-yo+#%p^l;6ZT?$=wT^&26D({n9+UO$FLq*fQb#uEHZYXn0_DNTZN0{ zwuL>mT1kto0qKroIe#mmbNE$N=hF(QxbK`T!)xfuvM#6<60_DEteSqjW8X$Hq(VV?(FE{}*oheYwL6UHSp`0{+4ai6L$66h#6}N;mC&FW7~PF=`I^=4(le5*4pk z<2}xVpG;5~qLYD;9eaRkajd(t?Ldw)Y4|NC1J@8t90$p!phrCydX-mYa^9x^Q9!Q0 zlzk|CsHQYr?~QK6gI9#47>sOsweF*CoeVAQ zE6ZR3tfZi8Sg|Z~L#Rm6Vqeg)e(`V0aL?zx5?4zQb>14UDbqm0!li!;{avzxzMnz< zo)Ec-F)~~hF{Pgzpu+!e93@6#>9Xk3!L>H?Rc!I|CUtMrUxb6uOp8YN1&}%yGbINz zJsHi}${*&(tP`XcLpGH@Z@C;Q@;+itAhB)Q-nal=@Zss_t0vHg)B3s=AZlBRE%GIH zCpdY+;RN5HaFLV}TFdikpFPNcP%(!W;NzyHI~WD0suHW@shrv;Fv4P28-`nH;gaHH!^8c zcBs*uGO}fePqn`A#V4~}++n+5D{7b9WXGp^VjupnA!E!BGVOQ8s>YGC zJtji)#1i5OE1Ia_gTsp-K*kRTi%muI&i0OQdi9<=e}%ek&9(BqonsM^LT{JG1RZ5$IV*zQ8}KR{$$V zhaa6k5yU}tcN412X!)w72L-LsU>PqGJA^*WVn%JHLlq#pbfB+|@a>IUv6b`_jT@Nv z385LX4JsvppaePN%(aPV5WvgQoB0 zf;TeTl0zRFqCE!5Cs(A}Vz)7LDd^S~l=D7fkWqd#5oOGz`{s<^GHwyW=Z<{Rof{MH z;CX^cYW7ZQ*p^c4FEt%0&&{t19MkR2FF|C9yN4Wg?1)SD7^tqgc+GBD^bxlMRxv&K zjRumv#lZ1{!~J$Uj!%on9vbkIbj}Y3E(I-orOLH4llUNI5+jeZqcE`x6f+L!;Fi~5 z)@9IRL{l22`gB!WmtW>fo};Vuc(8oV>aCGTNH19a6h&56E%|F@p0twDBVu3BU_Pr@ zT;He8gZgcA+i4LW9ZyLEqfD$%Nx$e|#W zVpwMkA`s=5s6RBV}>T}WT2SOsUFMuz#N~#h=)Mi1y3J9enY7Mu}#^C z#3{ja%dm^Dq>s#Tsh?eD0gRt&)PU?2EzxX`ZpH3$Vj;Sj

t*|#J&U%Az%>(C5y_**M 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%0D286Y{bMx1zk1$N0Xq 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

oQ#;+0fiClD-O7taKWsiQkR^gl++YzfnFF- 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

QK*8yxl_6FarsaA8R$jPifYHO!zNaTGF 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%0A@V!mWV697bRYF{IuBvCQWKdhA@gu21t?sCe3O( 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 z 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�bI)Ssb6Anjf<4FG$X0&KexfRt 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

)ZP^|1vvef`x=PiAFPW_>~$8Y{j+nKX( zmR_)!cNwwM80^f~q^~V9Pwy3|g;E_eF~&Sz_S=JK^J)A<+~#G!JOZ`)5{X;(Ewy&( z*~>mksP){Wm|)=uQb3nrPG0sM$CETd<-(}3q7E)7uxY;g0(gPK)Y~v5awKzV3*@VE zkWWMHy$!u^!Hl6Nd=hQ98Y2p1#>+WS&vW$;3PdUE(zS2j=EQNxu@=`3OlYbz z`$y4Vg49MaP9mi)t{mDgK04OLhN|oyo!j&RvKhd!2oq}lD1?GOd7$RN+)?BL5Id}{ z6#x*8-N%|Rp!Sq%dGG17<$1^&hji_>*CCD@Z~e+WHH~>#!~(e9#D&=jtUPc+^6fu; z`hI6=J+(gxZ!njO^&s=RTDz}r`csI`gVK9wP)o%@GIWI7s0FEBmJvOySPP~-gC^cu zggdv7h9=4S!WrFDZ9Zv}WMwHRJYoUM$Zrp5C$x$rpi?)zw#y*%{vWjWRwq{K?g)iA z7bJ3%VBE6|YasMJ9~b;r3Pnt9@=sU>w;sXtJf>&9gD(i+6Sd@jTNUG zeQ$?4Fd`nkuR9EbP1!*~75>53<{jE8M7HJz+M~^%Zw+F8+!<+;RyCtGQB@=PhiCD) zdCu>172?xztppc$jMmH6!X!fCz7l*m>XKJ7UcP?uFo2&slpeuyax6UCM!hr7h8U8C7-N*KR?HG_P+O7j5DHk>x3Yc0Xe*xu2 zmdw4OQP}DkOz5G*(>}PY3R6bR{T2Dch09#WG2#q`c1Z2UA~)_f<5m!QmErgo1H%v* z6sjF`JEiA%Sf81vkb(Rr6lxdks(1j&i%kUtK5n@l4V$VN=TIxZ|at9`$7L5#3+ppZfcriJU`I8TUW zMq&dj7-Zq|!@HAyknMW%fWlD@>2w4K3iM=^;~6WE@tKFt0Hy+p<9exuhL-tjMZ}gY zvVaS4A27=HQlJQ9zL$kpNRqU%B=!5x5B`gfAIv<9(I{z*tG4nOHAF5j%(9Iz_7FC{ zI>j^Wm`sy~{EcxnlJ}-3tR7DG~?U089_R+JcUDu}iqI^;vU-!H?3_!04G*yNj{G*n#RHTxMBWjcV za$rqyn-C#EX&{%r`_IT9GIFW_q|PNySILk`*h+~R6s5(po>J=(I=}$HbLn7Zj8ZfX zOlOOfe(Jcf^~@U7)M%cRH?AW77freBGVIBU%5h0)0=z355V7I&L5C>0GaCW5ng#Zu zan2g_=`I8JZ~N;y4yU{4*bC$*R}jz@pGgb9)((Y`BziFOBd=tlmUOt26}XaQVg(6G2g z4xH68;sxtD{m3g&@8Ib^7Rp74ek1G2ml^ly^8In`$9C!V%od}o(X|)qf>JtYbr(jyH8vI0ezR%XkQ&@(4 zQt>R(^?NiQi2Vvw7ig zLl6o;u|+)oM~tjDX>MjNMw}Fz-_Ka+ZY!PB8Q743Rqw$WtMo)Yy#PLGn=#u78$#RT zWYE%uWDl=q=RPEC2T&tP>sD?MNaF=i7pAOB`Ygaq98g1fE)9dql>k(+fK|4p+-L+f z+V@zH4GmO5Z70?4Vemw+*2kFu^?O6M+!hr|y(F-K8>EWWc-m|si2%%44Y$AJ8976a zcrHYwmvc2Q)0v!8u$)P8gMezn7PO)N=A!R61``*_-3CA^a3-!D0FG}%-upb?=@x(v z-vW^lgf-;6^B-DA;O0EUZ7vpc*o7ty%=uC~)?l&v`Bku*l(_1)c-95rufd0j?wL!aRi@?mBX#f_LCX;3b>0Af6PS z*tBpxVV_<&%iL>}Jj|s2Pzc)T-hf0I-m}c^%MWWKr)d;!FL;YQQmzoq77+PJKV8C2 z;yoP7cwTJfr{e%wK&HPw0TjRE6BjzUbbs1Cg5Lz;C%X$N8AKzgd=h_BnYn2p2AD6^ z_x+!xotY|KOk731i!_LvaRQ`O`Ax4ruN4jA11lol-DY|$29aEG@IXvfGHAEwC(0

8J9$-hzQv8tvA)pDm; zIME8lY}JFF_pI2h_NjW{h`8};gVArJmeNJc1w5XB#Y)3LXCRx>c03}cUpxR3l@S22 zFoP(Wr0F3qB{#*Y=(5x|ZkZvtesR17Y%fU0Ss>l1&UKeUdp(B+xyV0SNIg;stkM+` zgd1h<-Yj6TL-yUqo{lE#B+Q{q>5o5o{1wMsG%;B)0;~5N_O4{3DuVeoayX4xg>=)HK3dEMf5|zm3kzo|aG&Dd;lW zs0q4uF1^xfVl>8ka*^TRk8t;$Y*H_Hch z`!=1FhvQtO@~U`^y@Lf1_Z;L#C6Gcm7&lEgu8x)fIu}rsWV~~z@G~R%HqL6Ce~TGg zE7;X*vU2b-z?xcfv6evdrhQ}$T<;*;1iZ)vk~l;M*l@`%C(n`hsRqJkMuW(&?Zwb` zdRJ)!nH?!w=!Gspe1|*4UpSScA+_e3EarPo3E7qGy){#_<_yCNePR=dVKxdS$`Y4dZxY(}bFm`0_}Un=Gjf&W>9EH-o#Z4~ z2~0h2J5+tl65B}4%Meq$*B0euCo)G*_al-CLvY@!{><5QnvPnsh|&$U1%JC&;2aTk z*hn&k+;IMpjO> zxk}8^c8i3cRKP4ZuOh?BUNr5cKj%#!3yH{#k8h-?F2>z0S#mc4Xo2Ahm3k6J<-ByhaQQ-2>w3mZ)cpUVjt^ zAuo{uA%o6uAdqP?2p}0Cddhlkyb%RWfT6x_If!a!+2^}{t3=b_OhGBYm~_$$$yHDG;?BBiE0VDPgesUC zpghSHvTGM;fRmcfC4YTlJ_V8HYwt~H=WEM{Bb^U!R924PZE0Q_DNRXJ|5p1?+I1JF z8{vou?0XmpTJd7Q`1I_DHTRIpRJ`z*yCH}<)84ow1u+ngNZ1Uw=2ub7p%=2jgkF0^ z0Zff^C78Yv-XNn7biIqh$eunJ7P`w3*WFY~p=SPV z7e0kpA~63v*wmqvoauJ}QHbO&+|c~Y8k-IwE|{*8SzT4@D06=pD1}X+SISyZsPU%3 z?bWC5Q!@b5cx*TkZ&UDWI`#%Rz%~&%)b)-`%iLSn#jE{^<7wNi!9TbfEozz{3U@x+WUl_7d)6BqBM3(6BI#G57!A%> zLPkCJ@|a)KOCU63Q;;<7D1bgI>)?KZ_cGHR&2DTKA`??ITIUZEtW~ywxb5H20t=>I z61VoB6i9Eq=z00xF;I<75^*N7P@&n57gl#Q z&*ni@yARsc)dr?a5L9Ql0ZGcYV_*6ig_l1H{iIQB=)V3bZ+-82HNK1I9tnhS?dh| zK=kWRC-a|mO>>B zLSyHTt4NR{+#i>lrtZ_{GaOL8wUaAvUMj-iKQYUbkl5f>#8RZ0&+Lwlop9Ab9C9ku zp0)~z+lNbi#;6BY$5wj-ktE)qec|gZY$`xmQeZ*{GbVtV82F z08oIb+|JffzCe|}g)PK$JsMo@;8a)&SMj{=0qd4sM&6L42M-`Ov(guSIxZ?$XuC1&;nJwy(`hqwnKxwi>N{ezf z7af0p8ysvkaXffv?B=STZ-?>gKWj-V@ArHiMWU~TBH7%k=z?cAEpMJYwVMFt8?c-u z6+7}Qg-yvQloX-XM=F=g4nM_0LUJNi8;IW5tsyNB`w>)xyztfFD)c@o?`))(lofcO zcIEeEbdS5wKi^HL`U7gWufY(XK;aRR`%WwTgT=* zdWX)4QX5jWm%e##6u^8h6D!1wu!(x?!cp+PV568*tX})jx>QFpGQ@#n&Wc(p6Pv9j z>~_Y)XY@C+eY&P~jSfXP^1Y*_GS@W{HV&keAlEyKi>kO_KrYIhEBz(TpZj|ioEL8)i5-$WgVfNl?mP(R) zNA~kBN7RYDarxNYcHlw1%nPeEe`ccjriV-wc4%KhtO!@6uI9EZZk6_NZI;Njr}J-$ z`FJQif+Ou7^8TSk!a(zpgUe#kEYcClIK6@{#DSx`f4lCKW*%4ejkMJg|3yV3iyw$z zp(3pTlojptB^8Y}5zD%XBJ7Y?fo$_vdi!9(`bi!c*Neq8;d_`5h_+t)%#Sgp{0d9+y((MW+ZA=dzYG z#;|HRbY%#bl8Lo47}3NNswYew!DFR_R=pg;qArt%q2duswM|<&De~)~0l9WN1MT-W3MD5MTWryL9I*l@67>N>ACa_-f%eExP$ zfRJ09Li7Jdivh{4iR1^uG2$v_|B6Utmz6L{#UYMGb2?1-uKCS3>Ls2F)%*6A*%@U% zoV}w9lr+8s@D4MC(;jgOE&PQx2kqu{`>PAqkFA00$3QMSYr1Eh{BO z50nyL_m5yqIg(ZG4ze(H5}1R4BudV)^XTn5o5YiVt_K3)S`>M7`Qxtc_|qaaynbH7 z%%Z8yCK3X99rrYjrQIUZfw9pw#f-|V=MJIvYALx9TOg8)X&=Vk=FP!OP^`gnkyj)d z3Qj7!EeTa3xN8ezp%dip+LBpRrck}nO7{FLf|hyH4%H1eFYf zu}e~|sBm)COPMJaQpQqpmhvceMe%OgDg~a#&MAHty2?wWGVpD;NIhSht8Rj>=sM^4 zcF?%aj`8JzEH-pcTzr3|(J7*;on?OM3btRbj-*UCng>nN*$pTM+1)acV6U+7ERTSe zM1t)WjMi9$K$9Uy^p3UwqMLjYlf&^xVY|50NgOAgK|eQm_HjzXkPrOO(VQK^D*8o} zn2aD1pRo&Vc;|WtjR$x=ldls;RDv@TXhTE=(xzHK^Bm3znMNz}&<$MOs8#wkAo)#F zka_P1j%QAC5*=3{5uBWHmOGGbA9`Vj>k4Rr&_(~{?pSo~3mu*Kq@gou+EZo@A^C*$ zkQ&L0EGP$NZD2IG4ppmqD##JXS7y4hlF))Kw6B+}5ppFLm-b1rJhA4wy6eut`7`;k zLoOH^ih#s&MM$3j^GIwzV?zz{AL^TE(FI65$kG}zSGghF?ZCBTzk7zM!i#E)u7JVp z;$hND{2o&b_YN|&I}06i23L`}(K;;|RN7yvh{xTTNmv`;=>*FEF0#Mu71+CXKu%Iq zQsk>p>57ZbzUE*8YzVNu%We;|*fA~6aoVc^v&Gdn9_*k)*c`2f=gpWIQ@HNd&s5=s zs{m8-Gh1S&@rpt|SzERbXfkG<-VL?sDq`==rss5)t+=8I zCT`sj!L@pW=o3?|k@+8wl&&xwnedqV1RhF&v&iHJyA56!vhuhc1-RZi)1ZRmsXT&?k8o1S#Xz*F8f%9RLVW9Wdi$b||)5dSCWO~!Tw!@i{lKfSy4-bCUM|v9NRk>IT~!2DECq^JBzhGE z$Q(gsWIcs3vVXJmaXV!+>}gz|903Wd zxG}J#51M2rNM4#prs=WXJ!N#XaUgSew9_tOsd?%}gTv~=!##5ijW=G{FyJ&U*R&;e z>(@1VE>uenu!cHTEj2GZ0(loG(JI|LgE|5&bEw4Ft@3A{D(oHXA*n{bUpP)`mJp&! z+xfEj$ zg^1m!sYF@V9ChY>PL3dgEI6GbAB3G+`ctV<>!bYxImX$uiVqA=3^H*1^bGUEKt-qS zSE0upL=z5wI0cCB9sH0WJ7^doO2ILTw+gjTbP;*R@8x!JNB8G^G0t;I-atTWkgF$j zl+@jNLVrt1k7wYj&xf)P9l0go1rbo~ne9E5g|0U&*5d&}58p0pK@t+qqy_R(Xxcpj ztt5$hPCLn!>KclpxT9;-yzrqvpst)30%*k{BMz->YgRyEUnlN|dKp@1V9-6^gcA?K z3c*5EgBl4<^Q3{y7wHGI?1S*uL(*O;sa6=9^(E5$_QV zJzO@TlHT>;@n+bHxr8diD@dbcy6w=Obb<+|Rg`LNKLn5n0a!~~Qc(&y0DSfMfL?i* z?psPQl-PEuQmZeWyFiB3S~LMBcqIF!_k`do=8}{VQ@lWG%PbM#83|T5ht=tTYNE_G zOHUmW%h-d;BrV(79(u9*JU-A%yUN$dy2=sn8)t)0*Q+QtjXALi6? zy`S?VhKmF(LFRd#U(XcsE00F0lo;p%C-e!Nc!T>DLfV_|A*72)LEnjmX3L^^Q&C2F zMc{+9qx0dG{=aM79e``2QttBmNagdU^>|V=nqg(RATs4cq&@Y=O(5r4iQ~K z>02Q8EOmJc@&tw6wY}$y5A`Cxf)XJCoLx_4aAgg8@k^v z|3Hy!M^@VOt60`eNa0qzxOJVBCgRENpQ$!T;&CwHu7NSGtPJE5ZwlmefSpuMbg1N1 zbwqpbO@m&uOg;yyBA;;WLrIGLvjdeS({%d^-M6IF*8W*XTlB*vA>Y&|PI!AID;Ls* zfeMvEC9Q%49GwJ;xZk``5O{@@4p39<4A`r)6E7ljPVpDQG6BGQ>X&h@*O~(|uYbrD9R`q!B`5$tGLMy~&nxOPP`k^9yeaoeSN? zZ(z=x;)#^Jx~m(}M*Ovpm_)V~R~qDayM>Mz5i$hw*!L&WHTwO7HYCW?H5HTknBUxM z_d*~Ki|ij{B<{c){`F%&Esh(%Cq^i%w>Kze6^7DQB$iIMaCFpDVB8b0GGn-tTBwCO zOObr9-$q_k^s+fLyJNn6leP7MBJSZ~szpXeNmw5d!Lm>GKa9w>qaG|)h+Oc*wRz>F z3C!`faPXE;R*zjMtbVczV)g#r1oAj}0`l;{1wrD&p_3oG$d6!-5INj)DrLlC#7RoY~A+r2=erNkNMBD7PGoSbf0|=y0iNJuGQ8h#3pZ!Ho&8v9PYUmEIKvo zEbiIU6_n71=Xa8q+mQSgs_!qvy&WbU-Izv414Rr32e~J`m7t(XOWX_^W+}6FiFA(X zq#RvCFg4r%cDKG;Zto?VD!nF|GPFPaL4X)>FNc=0YtG?!RxG^gq$JGr7JDR_rn6EC zkyq&4i9eLpZ?F^Mo&#asB!6Rtd&u4-JKg;bdFprkcD;?iPDy&`4MIYzXWgMBe`B?+ zsH;1~`jwqupLM6uUtK2t#!5;nho7 zAyRIeluzvr)7!Ugbx2I3<6B55OAJ(hJ`nb%)*z4k`bzeP?o_lBvOk3WUo#=2LP8m4 zAVQM3CuDRf9moSOkM8TF><+Vkckx#@lD~jJo{mYWs*G;A9r3aMx|K*$LWyk~!V^Uq zK4AqqVAHY5(7TFVEmuJ=^Oud!x<4Xv!eq%%7$aVSC>g;Obu^p?JksJxq*w7E7-jgL z#Dm~fyb3n-urq>l(mCnGV8fRX`k-aNm+88$Fp7NB82#>I6Cj@?(SB0gzHG=%KZ_G zlt%>BF3?EkFGw{q@&Og-10y5YG$38Uw!!X45kIyJL4<#I_Ym%3IeOfKk3_?U&I}L7 zrsc}8Jx17d5S8^p2Lwp$xr9pym7p}Dr1V3Fd?gG*C5XX?4W$pZu1e|u1B;dm9mMiG zbN5U_3$2fKqGj}jE#vDXS-xAJ#y{MXI@*brAs_2+CGK@k96~ep*qu>kjlqG5EL6C{ zL=q-bU_xk$CWw-B6DWl#nrOnr35_e;3EFJ(v+aA*%%ss9Z){RGV+PFWZzld~e@v>O zigjum11(tS+?UpO&#Wc=s($Lu%DQm8CRGJ-JisBggK1yZ=Y;K`LN8d4P}lz3Ra zwAN%x{-72hT$*HounAI}aB%^cF9RaExWG#ns5Am6p9drgSxzwmC0Rlt!_k>RBksg_ z`LCKk8=7(M%zm{j<4(ao#vPh+Sya?JUV+80mnLE&JD_{)S1ZgknZGKQZ?zSz zWybpSmiqD1XkW2(&uSRTiW%!8w(L)o(cItW!&=;dJ94CN#CUtZ>Md-K`d{l=pE+IG zpMI6?;Sb|Jj2L&yeS3q{e1F~`&;C0y_r4Ym(#kXVyN|-K&i-g7+(+`#_}@R-d!cl8 zNX!|`aUa^de2ud|n!B|;`%?QI9@-xb$9PS9 z6Xc<*7#GGHS?%?#T_`c0UMV??Gnk`&k6R!0wa@&e zVfpoH_NU@jvOTo#C5g9t{ge5b`?+%V*rm~-y}yQwbOu`SUr^`O%e?Db_^Y|~_G;f- z>{BgUbjY$r?!?esCi|PTwrjLLn8bp*ti`zPMZ-{%{0$j9GO+ZU*2`~YRc4wNKl6=b zUmhXZR6*K_VZZ6j{c4`k-juzj;i^^q8Ob%Nwrm`Fy+e%T`X!;=Av9NP|D$6r-M+LI z&&;SYdp{)FZ7hA|K6JRD-W|rnSs$ljNN;QN;#_-abl@T}1I3h~gIsoylwtK81EoGD z$0b4}D8qRoK^=_GG#i|}sQ`L%iF>UA`R!%fV9cjhuX_75=&QN@M(@q{^?svh*dfc8 z_NDjY`{wYQNp8_0%NBZFd|&*Vt@Gy43nj+)rLWzXx2KeLBiU3za(e&!*4RMl2{sst zTdv$7i$(~{(3EXUSsyvS#aJKu)9$d? zzUg!9_w9nXGA6ec+Qo!=JhVe_z<7&IFvmU)oNt`L9GiWQQ`^Jeeuw#)Z}E(^zfIWKlw5y|_ZV3E zO?{ku19RlVxI4fc`E)bg88~F77KhAUR;T^J9?YpW44veDhkJ9sUiw<;>nvYy`C7~8 z%3l+GHu-BJq7NryK>XE$1)g!y;Fc`{vsS2H=4W2zV*OXKj!m_!6`r9Zg){6Ts$S^& ziF@*`#TGY_d#YNoUFtCHWmUO+M%Hk$rqze;ZS`(dbf}+^vqyc8*Nd`|Gi$pmUA6O< zPwZuFWH>|SvkB8OGqOl0){0& z{e|vpqV}4bDDM1-H}bt}y{Driv;1b#IkP@>i=FKkx({l}QhqXtzdkQB!e2m4uW2uX z)VzJI{{?lPM>rk#V$3AQu)o}ecJLct*r1K&(%I(O+ShT45i(S~{@InXgW(OEJIWc7D@X&(~Gk$pO&;Wv`h7AA& z5rCe2LjXef;P}a*V*opN^dNwbAqrrC0}z3L1SsIg0vOPMA{_7l2pACo2~6M!3RFN^ z-~#=@uGvf%lypE;-4jUAwFO+Q7$G>(^;f{=Q8ySt*G~Z)j|feYbp1!n(3z>CiLNJL zgT@$`=vo3o^r)^Ng04RTJaVXzFk}P~L&WT0iLM_4Mf^Y!g`n#P7%_wrM4?32?|>lg z#Rnp@1}SWWx-KF}h|C(QXn>Izr6}USfFU6%;Lvsa1t>{!szR8q{|A#LtRlQ&Nsg}o zCrwm1a6i zHVj|7{vonpiMc`=x_%#9v{Wacq3ierP)b_);0#^&=&}^^#7o!D;G}mgrgz~F?5aN36T_G==y!6 zMCquo)U^;)ieiiq1sJ*(KoujCkrZFJfplatih|2gj-Rhsu{cobI@Sh`zrK#fyO5;T zOXgW8`bW4IGdjO75Sc4asmtLoJ=UygahN{!^Ky^f6zX;SWkS6Qg?bx6fE;0jH3EoF z%wyIEC4{v^X9Nf34`OlF5RT3aAx8WKb0?Sl^sunEMAxkBJ7bb9=_XB>EXjUJlZgW< zj!=fY{U&pFcPf@C!#VYmB`6NSn+qd;$J?8I)pz9IB zUorLLMA-~QX_nn0G&eC#%sn$<*s9u=&DK>;iSF}+NQs0i1NBS%r7$Bw5bm^4H(_O~ z?!q{^-#>^e%kPNXSqJ)XU1F&`gi1}D^g^d)6|e4HZ@ zdn5M<_B6(`%lcdrfAhCgMZLXSJMq^KOwr_kS@lHVQ63V2j6EhbfF!Fv%2e7uJp}HY z;7yDYBiWFjy_b>jvi+_n-kjjh&*t%eZs1M)=-8{GqrWrr5qin8nMf@CzkclLEkZMG z;u7x|0weY#YmCV>-L$n6*7rx&Z%tUwdZ7MhVzF;0jwi;#E$C&xx*hbg->wRJ*{g0t zoa|HAXX%UgurBSA!hXm-2dZ`-xkY`|PFFq4HPOSd}v*RGGs;RN4NDzrVq*)ynrbY@2X82gSzsxD9*+ z$qgGbMPvq=uX>g0`~QpCe#;kcKbv;j@Z~JT@8a$Eco2AE4Bd0Vz3YBmcD;?;H-fnM zX70IU-o}MDp#71t@sB0+vIoG;;LVhh>CAI28`p2RxL?h8=3Te-Hm=`**8UeHRrY6U zYVPN7>!xu*G3($sTfWB5XVW@R2F=ZD5-0Vg7&wY31x$ zMyze46N_%z=A}{GV_|^@Nj6#MZT%Lj%lb@AUDjtvSCMCan@VVsYzh66c`A);FwGq4o;TIXdZNcfHib-tP;5M=u@CcvlH_mLz)4KWo@((QSl>qeviP%3WpNu(!9fcdWjEw+S@zJ! z8p+(xn`K?gn;gb{k7q+@<}SS3HnD1Q+-851*3O$*?hg4>`F@Yr+^@^`eiyfRZ0FYe z20Iy)v!4@MAN)UC#bN2*od4pBWQMy+L+oEvc|k)3S|1?nC-#kdWbc1JB>7!mZGUIi zZ5J-^SEt26t&7j@hXe?lRNAPksIqtQT6gos-_I^|4}YEi^^0!EhxRdaujbw_%QcVh zS91-fnJs>{_>Dr^R7R)VXCyI*~;O^_rwg*KMTMUzOhF{irY4g(cGKZHmFW=>_ zo#)|Kyr;v<8T|KK|1Vz%f7PU3w0QYM_)EqG_ADSlgajEmBBjjIb!l8Lv+HRt4b96; z^D8dRikErC&s>_BmwDz}m!|bHw?1`gQZIArWiE}(OB&Y8%=*=(S-s4wS6v#_%dC1E zo6q6WxE|&l{^sU)E)C7Yyu-r zo>S)S4Wi0EJhVPOR39$)GPhrc`{G4&J)~|Mb(J1o{B+TCaxza-4w5chr2YjDK0Ksg z*LuzR=)7iqS~6;d-ON|(6XUD(ndvI-t$5D*;5=u2R(v*LbQQBV@8a5gU)OUthaE0Q zW9^*N-XLDyVTV-R;c`!N`){uI(9(R5d$XdfkIj4c=3iVLeqPtapDWMldYi+$R5gcw z8`t-^i`!J(lickps@hGYjsG$L{MQBXUn>Crea}Oe-#zMkya0Rjnrv|BI@oE@ zRJ#b@pAX?q&HNTqbzxq~T$o!huW)tvemdk9fA9CX#sB?Uxy9GqFUu`3k(Qrg0W%HE zFvnb&QCH^f!rV*Z4m@gd%5KgNFS9nwe$Mylleyb0$Ywvg9JX`L_aB*J3=0j{TuLrkFE~gewC@{JC;`&$g|7`p4>*Vw`V?JYgmx z`%fDze(890pGSiUFg8v$C&<%p^R*I5*&hsH@~Ybk&N>;L%N%HjrnFyi+(%LNk$g%g z4h%?s#5CegkS|*h&%S@BV*qckbPW6grrfpw&&1z=kcY2(1>KN))&dCv(fUh==M2?NX#1I!si7c#({GQb=&z#PY>AWB*5O(RoRH+OfmN2&_zmNtkg z$&h~$5}LIBW(ty}>PS`P?w~AhU$1HQjCadh%Z*wb^LEeczO@n*vpq^Vr)f$#tIQo; zeKq(*j2mU$DcP^?b4fPICBf{+ON9Fqw$-iHEyf^e>cZWq$;Kj5tkYuJMya-$WDRVW zSm6?l-ZOacVW#%2d39&#Y};mI>sj3*esnq9vq3{=h7ZsRJir=6Y(M(ztTjkZ)vUE% z?M+&ckXr=I;ybKAW%=~o#Ba1{eL6}6w1N(e&Em+Odn5d{K+Adl+12qXFnT7DR@ zx<}CNF_n=c9t7)ajCNW(B^1{8c#u(2B2gXl+#W+EGu5$Wl+1A3w*S#ibdAnR^46BS&^^nAM&gY^b@znlm9N_vwqcuZ z?^cI@rXaX(G=HceM)F7V_iee|k#zU4`M~dS*g;e|zN(!dvu=-3x~dhs-S7SU zi^vQV-)|9_fpBJgzkMX-OZUgLY)}Z(g*RP8ZYY$;yhWd%`E0LX$hOXR!8vz}%qViC zL{LWjhdh?2etg_DQ1f&B zFDqHxYyZqQOo^zJ8ng@=z?>&AC;kf_bdySfIWf{HFem=x(tTJF_ZzJ#ELZ$BEtVje z1xX$BC;qsn6lalF@|N76q-BeiDx`C-!4)V>GW^2O6R@I(Jl!S9{j&RWD0SIMevx3j zeDQ*MQUfFu6)Y+wm?<2El41Nszr(n9U9{Itjd(kK$-DRx6fj(%c!4QT(otz+&`nB| z^bysyT5*e&W|SytVny8r8L{}wMHzB@|5JBq885bUof9Y8ESg29=_`bY^6BZX;Yq$3 zDy=~%tHDdg87h5I%0geh7zJ_>>as+X{P_|_1eKN`3gcghp7@J~U@2oy{WJPb>57-6 zR4CErC)3k(+j=s+pX%wpAVRmr)G93_{);W$CnXY+6y53cFs#pM^Oh*dhB6^AU*eW6 z-3PZQ{eN(&!?^gbg_7XjT3gWvBlq9H8nGXz`+l4MXqPTU-BfCeDOQ9l?i#%mm%2uG ziAF6WqHvcSKN@unPwJRt#PyNwqoY1L>K9t*W>P$QA%$*Y5?{F_-o(FGI`NzMe`U>; zRQx7RERm8j2y>L=7ox5qnJB@Aa(vFpF?e=V_Y{9Ys?F&?SN4;Ry+F^Ep>JY)8G5JY z-~Rl+-Z$LWT-Y~Xmbn~ci($*)>gl=P`l3)-y3?ah4}`*A_G~1hUiu4VyAudv6fVa4 zI6dVo42stFpxTVj#V&dmU()Oj$KEBw?nSycUutnLX6fNrVwqTO$wK$d_irot!;0pn za$#X9c8N>=Iz3MtU=6whC?o!pRI1jZla|wG*r$}t>Tt)t)LFL@Ccj0wKTTR9qbv_t z1{@#Q&umuz?S7`Omp-9Uq~P#;x|HaJ6}k^ATKc9@j?>D-@-xZw1*_sOPvU<2cm!p{ z|E3!9WJ8YMY%-lpq8w+G75JM{a{O8JO)5r9G<0;l|5l5-XUW)=xuIy_EBeHrQ!2w! z{wNVA#mxZ@9XyIvvIvyq&zGW6l^kD?%g|7LA<1zCTJn64qA1Tlz4e(!7bDs+G4>?N z4?5`HhY{uZRn(`-^P$Hs?nN(d==_&r7%WWm0`nKYgU;Vn#iM;{WHcw|9M0UM zFRIYd&z5{vh_nQz`=Loi5yPMN`gBZNkgzI_A!TT#3+f}xLbEvCl5fDA*wZp%l(q4Ay$jkeN4ShRHa!WV1=foxbgh{cG*#uwIu=vtzKOz4IsQ`D@SzO7xECMH zmmD#r95F3(_D}ch6gp>uUCHrj5;_zHjF&D-Nm{a8v2s*}N>P+2PMR#4L-~aiI_wH| z#l1+Kz?^90p1T+M%bh?TC}0g!2_hvjDWOC}cLq(0mgE?vqA09-sQLycXC*f+5W3@wnX-Sus&9Y zN@9ELPC>)n+Gy{Bj z6VQwALh-!`9uSMuC{5_J4HLn#CZpD{MpT?Lsxqt&`|}-3D$~ErDi&kH57=9;0dss8 zkO$_N?adC*eUuVWq8yjvQtT4F#4nJCeq=+AAJqVBIEIqMyHWG;x1C9g{6()UVnOGR za+JyLu+L?RpSw_MFpUy#p1iQqoxby_{?L zjFC)>N)<0(zRYxyJ2hYN7oeQq!iBc*jSUz!YO6!B-DPPY07jvy& z{RL}qU+YILTfK2h{fSv{+~QtWt>z9*q1-J(MYnzS*lRvmtO!cY&x=v1!7q&J^$bzJ z?Nl!2EjLz#9braT6%1)oA}VrxY7}?=M|l&>~8O{=jsL+#+3`|FS=l zNX$bbCxrE(9A;E8R>WP53RVQI^$uTK+ z6j_w}RrixlY;*&W8<%8!vp&<$ydfQyLx4J^g+G(1_!bdy&WILX?O`kwU4skiVc8a^yZzJLBo; z1)3ekQGb5(?T(l^3}Mc+JE|8T;!^BVgpTsm>d`0`QzH80#SpseKS~6XFe+FTe^Dch zRdFxAmSIH!s6JR9|Hi({l6&zf(cCDJzxw0TC;keO9^^qWV@qt4+|RPopQp*piW(UU z(-N2W3V5(_sF!__@G=nUyG$s{>_up{7>13^d4oRag;F}RjD6wd+7#4odK-ME&QVNjKT~ne5-NTP4h< z2_Ff%Mz9d~Fn6q5W;ui~!JQ!#0B>T7QtD3We?_8|f#}y)0Nk+vyoozT7!fUiQg!aG z%I-ZxNRM|D9QP4>nKc1<{3Fe*hpcWCwmoWPdU;KMB$F>SfFx)CJ75*C3M1~tSR+D0 zLP{86(n}l-o#!tj?qS(6&5oeXE;B44PRzAOju_E`g!t=7rrM-?t?sqU@`!ZDgGUY_ zvK}Gek=C8+vT9pOS&?vMAlo-XIq8r6^ZO}KKjoMh>LiX)%vmw0+qFJdZtbb|_G(*q z)o8cDZy2NMseFpREMpj+sL!s3Ng4C<2C2DrM;MWcnX$dhF@7uV%-|uS5kQXc4Tr|| z)AQ3qD<^{PX9&EB0e1*I$`V8Ar?&}u!v-6KFDc_rf0kvSAvOqYBw-@s! z_~J*89^DG0wNM(}hn!4Yn}5_J9&2_5Rj;`l6a$0_Oj zC+9>5k+vOR6aj|_IYWq1MTQV`g1`e1dNA_g2Qz%|gA^|+J3XMm10Z(5Q&d9-HgW)f zDM3gH01kR?kZo`yiU6PpQ3D?u;CS*ggB(%lkih_mcEo@O40=F804F}47~%y5aDf4u z9H0e6umAxoNB|QBsNhEm7JvcJ&f(k|#1^b_r*p=cO%44SBA&{6aZFxgL<2C-|SUgKCG#m|=I^H`royk2sL18EF0{JF^x^mhtVW>rUAI25pv|B*l08DPQ}l;cRAa za?dc#xRzARZ&`%u$a+o;liyN=VVb`9+HX;dFD*5_R>UMtIP0cjlO5$PLNokKP6#oH z{YoUX+X-~`_S=7^uURx{07-5fa(+*orp!IxVsy~ME7-Hm!rh;R(EVli4l=GO6{xsOlES^OgJMV8%h~t3AQ-rl(pdzxKufV_OWuy zmFbOHGkIrJ$`v!ZL+J8b%7{BtM-lwMUrVbmWN*`!IuoV95tc)f(eE)wa=~G`Y$owUdBeueG9T+c5lEnA|DKFzTGsB@Cg}bvmw9K9CPm8Dd zj^;fk=6@(<)~CC+JjH#NksRSr-$~q~WG>L@lgQkR?3a=0iCm**WWGavbFSweS|2jl zg+I@$09rt$zpmjhhq@cr9C!LgAvwZzofp&vlHoqKI8OB_luG6~)vHjg`XJA%njQQ# z(CTHr|5MfA?v7irc<$k;!(Zpsa~IQ`9`!1Y<36dAk?CHA`WE-=+~QUC=Vd260+DzD4n57=J4fTfM&?$2QT1~1R^9b`GHDjkn)2cWN71I=)spB zbm74h9ysZEpu~eMJ&=+;e|Z?PLoPjdlHK)Lb{pnM=~=$CaaXiBS}qlaqr+5~gf%{F z$F8U*RXz-lJaG#3l|emNpUfO6em}*m}H!Q1121M3+#g1VJ@*{-J~kE34Zw(`b$mi4jv?T=Woer11Fs^3)CM$ga8DNN%H z-S<#xe!JQC`08tJQeLH0N;0piTda-x4fU-18OfT=hPprYQSWw#MXgYnX29E-(bVH%kSV*U0qtlyp`^Y)SC)%~)(`qV|;PiBg${S69{ z)hsi;N#d_JyF<&ZHJ0=P!@f-yR+W2LWwHJ$v9J9#$S)AhYW-otGSg#U(D&z=K8HVV z?7O$dy6I!GA~}M+l-Nrz`HO#n>?e`2`^6l;)?Si6x-tBP#|*%{iyc)Z!%jrvKp{ zhU0VHqcoVems}<}41~cg+J8Z+>08z}wdqBBSov8+eUx92n(cEa4CAPaubob+EB$7} zABR7pM=s5S_X8H^UPV#5S1q!0PakcCBXYRpG z)9{ABu6Bo(O`H8u{pq2<;QxSG!o11fK3bn`+o+~#T9DHV)IqCh^(kd()(3slYhQuv zcPk&=ukh#Dek;j!#ll~Z=i0^8Z^N2tA~}*o-o9c}{XmfOH($Fz^pnWj{bDvf%Ib)@ zxBJOFUo*YdONPV@gt9(9B~|;cf7|faO157C1F6%nXNzT68V0gYf3D_N)^9z!`X>Bc zOw+yg)ASqubeMkgVbZ>SC+Qm7R}AylO0-wqll`f`VW92lb5&b`XEaTlvD#NHtCBa{ zS!=EBDBixu`(+^X8O%!vKUDF`rE0t6J29&+5X!q2GSn&o2jCH`pet) zsJNYI8C%vO4gK!t_S5vX!!VAzpxE8VT*r|(J zGBhIs002+`lN10T7!(YM#6sbKG)-htkq;Dr5TdS5VxS9?AjV-3GKLs}h#`g`0s;a6 zGctk>_ckVxC(8m)j5!556eXohG69&Jo!NA8i!fOUSmYAP*z5*qiX1?amoCest7I1I z`e3}$yTRsS-(7z9-(^jw)_H=#ShN#l$e}3|>HeE%<`9u# zWiTPsN7ClUb&oT>WZ5hV88h7609-b!@rb4W=@r0mZpu{mL8oLv4=9nBj5k1ArQKLe z3oH%rN~H+VeFXBY-~+|CgrI`DI*R#21P6Q!lZ(t|nmm?+zR>ex=s0JE7Oamh)ixW| z_#GHuff;`c>i`c3=DfW=_$<_?)%C|5tRk8#_81R*u+lzFN3JOxpp0FKPi7b5lesHm zM3Qu|IghX4EGK4I4B|wjfy7!AacO}V6-!(iAcmrdm==jq9HP>OVk<&~G)Sz)BbEk; zQL#j&4Pq%0v1ySQiXbWtD7GTRNrS|;I3j5PF%?U^v>=8e5t{~ytr#Mug<>m$h%`tH zizAXY5L0o9O9Nt16tQU|v5Fv4S}3OCh)Dy9wKyUq%|Y>U$eh;kN69cdCV{7I5QKmu z37R+~HCzGVXF^J#;6rod0Zroi1IU_9i zq+SVW0s_t$LnxbbjpJ?_;wYq2p-N-P zB8SMe0%-)IQB*`lM1{gcB#~4ERR{&sh@^s)h>A!A-I2AHWE#B&g+!CEoHhbaQcKEu zz+J_WpwoZ70OJWCXFO3LUr&aBPMJpw;i}Iki`(jrD_7OeO_(kQu^D1FLQN49ECIT! zcM}gG_L=@zn<-De04+XqPrbk;xr(4^lbMPGGO+@&?Z-LzK7fd2->Yy6p9Vp!V28!lg@_D5OD8p6} zAQ`+q7vm|P9sPaEkwZ%6>?qc2;V4?B4^?6GR)+rd29`2V0gn{`8Q;;6l|(97>LqVu zB2IInH%jDBR!fK0J-wz=A+$0?HJR|I@bC-o$#7azlD+Y0_;{caq{3h3Jq&)c!9XSH zvm5^YHR?zo6fIjeZ>aUWXd?1~wIZ)KQ#24T=&mvk{j^9FvPsW;5IXp8xr`4{`W7@7SXPGToR?1ImtCJI&32)+_h)(=)mhD?(Fj&RcZZ0IFpHB9zGW(eH`Dp}$y8IVm+ zw!tDCr7(%n7Z-8%$>q`ZEsRGD%5tlf0ST_6vTkq*17HOnfGoF$bZHs%;-Na*Rzeyw z%)^-MMtS<~*!cb#({bKIaFY&!4-=cEQ4vA;~0`$)4~ZS3!-vADDZ+Hx}NyuhBlCf+uDyZ>{TI+rAY$#gdt?#Bh9Pqz^%}hG{hv zs^%u76yZV`c^NZ25&phx)aSw)wx^=3BS5K!Fx6H1(^{*@N>Qv*JXb|V)NmvNX*%-> zR&R3f;B%XX4b^&P{|XMqoK2Y<*dCkP@N9qh^(+D6b7C(HJ-j2tm@{`L=(l~~^`|H% zusokRgg-5#6Yx`e&r`?%Y-L=t2lnAyx)~lolB6QM9IS;}R|5`$=4UNL8;}sIA61(T zK+G3;7Y52={~C6stZw8!^j6We4C~e4GMk$C$qUoIvOi~?U|>fi$*gJiNq7@!s&J*8 z``S8ds&iS}(CUn`Jz=lo1S;-b)G8`xDeFaVa!lNPj7An{;U~v^l_I9sfqI@b8~ScN z(QAb&z3waz{UJGbZH^9GWTWq`)yTn*R00P$uWmP(U**VfPMf+GM(-EnVTM_n!9;yk`V)f!1lqNC|z;2`Wva zZz&;j8T_gsZrp~VUY%b+it}r!KMOgf#(9NsxH7oojHiyfSxCvz;&B0oTV*YF`fhYE zGyJ2>8Zm|xY_4PGD?;+%s+k1UHp50EtBqR0P9Z$1#KZ!Pf-X%_gPF;IR9o!xN0F4c zRRlERD=Pf!v!vNyg%D19$7PrhA*%G@A%c7ls4rqD%d`0jUABS;pVQZ`_Q#C(kK+3w zDGId~#cLkiyMq<%^H?fnjtcF=d9ZOZc zguQ(&Xi@8V1ai}&5Mi|zci`>&;8-tgJlQ<9OQF=>wfy48ZUMQDD%dE^@R0;2TM8m8 zE|TsrR4pp%^H3G49KZ1PQ@Ni}o?euc+lAP=%|j@n9asDip`k6Egb&9Wuz6_t?zl3U zhlFcU&MzxqHI&0ra4pI(Y-hTRE)zLz9FFJ^pFh-bu|@sY`YefmfR0)ENfbqVjBPZj zLo-6nOz8B&B5EcBR-w@T2!Q?Q`Q(T*>p?^Fkiq0uh?XW zjc15ihzRO}SACHJQd)>&{;ikfC1jt+i*$YRT#&|<5f1=PkJl=6?UlZvGpQ%%m}yaE zUCE;T2y%HP8TqOfC8)uqDufR{=FVu8$cNUzyWGEGQ+VR8!Ja-_o$(bQ$tbZKxP@5) z>=+d)8OB&Vz1nmYX(}2>c({{`YjghPx`$H?gaCBm!Dpd{>Sjt+i>L&|M;-Y?D<%q1 z#q4tx_*ELSF~LUjD-Y(|7thw^rZeP@maQtNdpow#=ko+!Dl+NNpfDpEk})XHygnbx zl}*RUNIbo|vI!swcDu7?asC;WWu37PEaN!Iai2S}a(iIVIF@;Q8}msBQ*X1{Cmrse z^7f-B)Tbu!M|m??hZQ-y#FP#VLpRyH$boc;HQ|N(WO~R%(46`V<5@}YUSbw8e^X3);(t6MA(?5@TUILce z+pYV4m1wi2j>Ya9q=uA{*jEqL{UWhD;;M2diqnSTo|%HSSf^j0#Y_WNsqALp(bJ@@ zYZ<3*nYknD#2SjANB#hY^LJ*b)lPFoREuRtZVYIngjVGF6%i8&=|1j^qXj+h89{A= z>{9yjZHTR7uiid>LDu?A2&8U%N{UPgo^{T$Ab zlP`3ZbGeFu^B7!Qirb24%jz9POyQ#`QUIY<+ebog2C|9wXQGNoBfWm7@%iO}#vudr ze$*|AI8rBe(Q|1tZ5azr?0$wt1VepN&&9#HkP+77IjfQPapE$dINM@P@1W9CbzyR! zTXX}*1?Od^KOCO;R_6)PU^<9NmZ}AnM&Bi2ZDhsF7cAmnJO+{D_h)#a;<1*o^;jj6 z%@oAw*S25AYoj87W^m@#AJ)yZ$0p}t9FAvxk4x+-NeLE;Qm6hd<2qw;Crm{obG)|^ z=~Nb3>z9u<{#~(U+RuVn$E18vl!PWnuosM9x&Bm*mRw&pSGzMrcln>w^`G;H>38kd?CD` z_d>-fay#oD$JdrU$S_$f&UMSijrRN}z1kTVYvmid$%H;8?8d~|(}$&_-KMx$4ztV# z>z)-WFOj4V$Z*>;(DfH%dXPS-I}%JJS9;;J*+TU-BR1Mzy_~~x4Np7bW9hY37OtHB z@}FdL;7%|Nm5G?rchG!uHQ1S*Br-q>nZu2WFCwcBG80bIKCLbhJCx}AP-HXd8LU`l z^uQ8g@zjF|X59B7wpW4KH;Wk}@Ut9lj3$q6J|Ia4+yfi&K4rl5q=>{uN|(`AM{V-1 zk@7{SfP(`!NDZLW?(LI96S8}Wt$_KBcg4Wk2c)AeDZrtOQ&$pkI*Yw z;Rjju(ZZ(Bwh^kgK`DL=$lbe;2e8MTPut0luM(}$w{ORjR>ZVQjZZ;N>KC2OfQirK zc)l&PineM9ClPz1(RPrq_`ukA)xg2m`HXN)X%VB`DOyRW_x%7F27$GZ&&A=8Lh`GS zC>xC9(Y8eu(8kEdnARZ$Dj_ghAP5HbE!u{kl6@r>BA@P<4_x~}8IcC>Qq{VHtQQt3 zBbu{+=oV1i81me-E~IVii@po%1QSozF(el0Ky%l}!4tg=wpMKfsW5wvG$|T&kh2PpkmRYvD)_-#3ot6Y_;=^RP^BqV#{b5{E$MpnwyFQLQCYUvBHH?s(t< z@G5hvn-m!G;$I8f4##pnKe0{$MoVh%23(1{lgsMU){j|-7C;a7rcfLve3xhvMP_;) z+vAjR+(gIsD($Wl64lt+N%IYrGiWjqI+SQX`aYm4G=5(lE)cQqt1kAplYUDVmfJgx zzidm$Q?Gm!&YXZ_%$<Y3J+`c=)Q#Ucof^Psi-?JJa0>( zUg9eQHL>UL4=U9I5d-o_1DLIIi^UyDV;HHS#dJR{Kl4`Yc?RV=p*(sBRR%U$Zpv!8 zwQWJ9y&oxLj$$fixxUzzQ=4Wyb~YXS%vlK41uWIrTk+RKA34}j1xtNO*jlSXBs?KY zi#qHUIZ32y2BP5XeHxys9@m~BLIsv1M0P|tFHv+njv8bz8Sn;dYK6ngahi$bvF*9p zKW#X&Xf?xj|08NnE>9Yuh;K+_uRDiNc3@lkrAzd|Y>=-)#L$udfb??`+xY!laYZ~7 zSrD?6c1pL9%$=%S1FTsL17A)mIX>Ugv}uT1xPt``(%6W*akct$*%@p zmYC+0aqOvGvH7JMH=gE(sdrQ8)(p$Kvy|FfjcT^J}fX3+vlosfE#TmP;homNvS4a8*PpE#zw zHzH@D?e;G#vp)o~q+W#C8BST7?S(pFP!=vQ$`#CUd7(mg2>azi9$5ygdU3@fGkU+B z)aaKljBK-M=j&kW8@G#3u|}M?Ah7o)v>SkbG=*NFv=v|f(RB{wp42A$9c5|y5DfJFMuqp>6g000@J{AU>db(J#U+)m z5h+ToQiJnJBSO(_t-s~DxrjoXccNInw!1;X4t}c5z^Z%)@G?egoFY`F&sobNN3LW< zj3g<{6P_P38M}cXCRKSV#i+_*WBDiC0FyR#RXI87wlC*A!9Fmd_CYveK`S2HcGQ2^ zzO8*k-gJl&@0)kY>v&o_Hbn-GwI5?JTNzgqmW~jRMi08m;ESCkF8?$loO4Jne?}%@ zIM|;^K8_!ITL9Z;o9jYwKqUL&5xDKLe;&{M?*ncO3EAI6vi)q@#(yF+v#kYLBSX1L z^J0{9AShdb0jv&p7@%-hz>b^!Z9o-X)iAnB^Rd(WbI+EgB(`fHv%_%Lcow3Q_=DOY z*XNq4NTMzjWDG&UGyHO^kE=JGkmrefGbH=cT zI`fcCZmAhqH4$oXU<8o>SP>l8t)&_Cvh|t@df~hl5Jtt(3e8(ovEVZ!L(d?+fgE|_ zgo76_iqO)m5vi)DnF({e>2kqZ-Xv_R-*VL$YmcKu@3th;MNQ;IK6u*>1);qY>5nIs zpyp;Ezgo~4mSye#$!N2^E^k(nshO29QbQOMU#FUssBs#F;E+>sF;#5(>pcq*5pu?x zTO7uy2rh2x;pof2Y)DVTHTu4S-Xs#Xx%I6yESaF(nPFTMq2?QAW!WG~D8g+BS!(|Q zhOKD;wc^gGCli0U9SO`n-P1O^;((B1em~YiVRkg?{(fS5SsaXW4PnMhn7UXJTp7?q zsKN@tU95=Ix{;eGAMN={Tk^auZWa+rWPxUsi1&>G!T?LeBBz`~dv-!Nv*uD>R~5NQ zIQ=@S35?)}z+B~=Dn4Ho)7zHCiA5nAi)&QuKnSVkoj>d|u$_LxvyL8-BZaiAYoqzC zFkY4wgpwZN_hm`hE(XDMv%I{Od(1LFsuCxN1q{%@$#vGvo|!dpsrd7C9v1~!OQ{ef zt;1lnKrUK#g&YUj^R1wTT*QXL%Sa%lw1r?@G7oIG;#DF8f10^Q49P5EgwOp{#H9&Y z%d>tbKwCs&l-G|YlICI}u=1&L!U@}yyjt0?5tmc|B}M?ldlZ;=_Xg?l30prlp#bKj zBpA6OH7toA81{b`ctIXY+KV_rJMfr89%F_B;=$G8GYjB54~03YS=n7EI|@lt=-?zL zV-hMt+I-9TOv2HuEvRlt0J@SV+6Q@H)HS}WELdD0`}X8EoszD8YdEYCUPtl6)aC=U zZDmX*vvttPxeM4!1o7XdTKu5zdq!vm`ul+zI{MrJz-RB|+_U`CK!w5Z9ZJFSzDCVv~B6kQcZY%F*W2Fc>o8^nqNX< zx?A1;t|tVVjEJe5P|@DJ3B^gvoZEV{`}wVjVwMY$$)e1Ut3dcDG^3~Wr0@%;MiUu| zddL{YCUjj%Lqk318_Pb|=lum5;0{TVa3ta7RAnA9W>)Bg9^e(#q0qka#0B{~4zxi` zyjTF=ox@?<6}V zwkBSuZ%=^5XtHx+t%&bUUz$|~qyZi{N+>|eYhi_i#wng!Lf^g;BLG*qZ4ms8N_Bam zHAzH@m0j^(q|_EjdjuUk;zKHSURh!6g_ExQ5ScFA|DxcGIUOx543D?Jk-{ZVCm80C;o zP;xKZ=&Q1T_~WuFN-PdE%2ffkx!GrN6cOOBMh?sozrh)P9CDT+`wBo7VeLO%&X^JZ zH+p|Bo(92khx zfuV%-iPd$N<-V%$h1bd=k?Lsk?s(f602*;;vU;QBR|s@SiPzIE`Q_@aowaqMcd7p& zd#2xURKbs~VSym|5s#hz3{~Cc0EcI>V6VP5gu|BHHm^8q>m-4^)jMZQuRnQtt8w4t z-(e5O#Y|sw*+Oh{%Wksy;H>@61FY*Mht>Oz8N@(ksyEIVHG~9&F;>p5VgsOF=8m49e$vfJDZ61cK z3gj}DN)6gYlUEIK!W~>4?lKRb=z+G1*Z{<{eFfEUKaJU?a)lNi)X&?HMUEfyB*%cM z8MYh>9|OM98RH+r;s=yS>X1pbzwiWUi#h2Yk(jp&j#;=9$JXdbA~6a(_vJNr=Vg2w?B{0eu# zPUxX9u3?0UP+e6$d_J$&jdZq|{cOYz$j2x>6Pg2N z!5(#ean8E9Ewdv9he?Q_Mb}ckUk+R`Fz6gRk&*Y%u7O~d{P6Iv-fWTvF5|YbKgbk$ zY{d7c54D}}aB1|nrEq4&dRDpUpEibZh%!KrJNU((>Wj9j#Ue@A1QZ31W!^Q(hO!V& zM=8y@K((SrDLrvg>B?fhaI)ytMOrO@(4_)O7gEz*v#gdJkYHBaA)gEg9BGswbQmM! z$j+Av`0&2nrl|j^krHr%|6>rAUXcqcA*-Gb(r=_taPRJ-QZBw!8=|#%RDKL`oT2^4G*E~kteg-9|JM?RITx{mkf4Lo^KP)sR!$s zj<^iZBm&e7_tz0>BGJ39Mn`Re=L(BqL0Z@PJ(3kGA;Z`3X3;Z;{jnKD_N1PN(XgYk8Qcg)3_n$E3h{Gs|k zIa@+@qOmj!2(Y=Ji1kQ7`k^SY64>A%y62-t7Vh(;q6_R|4Szm(RFhw5hW~zGlGvdA z83tHbe~CTs22vg5NC_|qq&>(={*xCEQkdyd@o$V>a@!O?I_9QA)j5T$Lg8(YJm`e= zazvR6VM>Z)L3*KaMS4y0O+ZZiB>(Ug5@sCW)a(TTE?(~;UrgG7WcL2(Sk=u5FaU18 z@f`-nFV~6=I*HhAKA z++1-@e*(dyqI0?exWn*$_1ypaSbZa)WDahx#fuX9D_aX zU{5&>j-}{SN6IHAkKRy;p4sKM1JJbM0hPJftW4x7RvyNpu^w`Z-WKK6e;Oup0T&+v zM&3XjSHM@Lb^*K$G!mJpCa&x zbHwWj{fMGDUAi%$LWwwIBu`7Hia}`PXFA#Y3wY3}Yyk#p0$P{)U!!N@4Z2*S_pGBz)eS?nU9x|+soi`$4 z13Zd{^cmafUyhU}rxNQ*t#!reuSRs(M&{etFc_xj>=I7nn9UWz!+t&Jx zR&Q=w3Ue?CMdbN>VOMJE+Zxp$vzJ+9ypOi6I~`uT+I+_HijSD<|1)W=WJSpFr4m?b z;c)9v;1648w{8gQgF1NNuJP%v-<9n|gwr;+atBtU&2U8i8j-r*Qq$2JZo=9Gwb)pwNF@$p4pI zopgf~%Nc*hSyxT6iUpVvANeYd?@7(e^oMRoY1?GA`SIOwwDDgE2qL$-Pp{BbfUiPv5oK10dA80-Gjc`YSvrde*Feja+~Nki-2Ox69Nw?Aw8+nQShQ5W z_9DlCTQ`qgG=<ze zw3N-kMBl5Kq0%drhRX;S%o3{^_mr*25R?b~5AiYQ8~qjFK!%XwfoP5n%tjr4on-}oZz~s+#KCZ++vy9#LiMapiNa=C(@4h{3pHEmhN?*>k^hfeahryPYGmX1GI<9ntu!G7NTWuS$70_%VCm!udyguiw8WIP#_k@|n^1 zEh^>7CbCZ>Ba5yBUYDz_E50Ocwr9DBFXte7Cb9%arbnZm^;y!z=AR0RIei9pzDHYl zbPswEu_Rklh9u4beURXAk{9qX0|PituAvw6(egxNB54|8`#^{%*!*z~;~NwiXDhci zn=q>-qpN{p%?$ugKgUWrlw7kCAi-HD3+*t(2X4m^m65|DCUkER*?>T#P?Xq@h zk&nMznV|O3$@sa_piNozaanR*z%R{aP=L!Kx>LRntYn-qFcE#UthPmB^S0~w*BWJ= z45*-kQkLNzM$o*$p~J#d`{|!yj5Xl?z$hrr5rSSUCKa6+!M6ayCLOLmhxhbJ62)TY zvZO*vc5@z^BpI}CV2ha=xTqKcsXZu}Sy4JE#itkO|I$XHMOwGG)7 zd-5TN3uqMracrMp60oOki!NI}PRa?UN(@O+B(~WbA_wsie1!8L`rdCX14R)&$V{&E zHE+L_x4~h=`_dRM1XXd*!{RzZu55+JUrbLZT6SZa{|nU_$(}L`xQYhYF-m?Q&KKeQ zgQ2h8@m{sz1@DnSsm^PH1NlhWDx$hGVHEXUs4lj}Yk)blWPpR){CkSgLPv&C5@UAG z5d}87Q}8ax>pB_NikO13n(3Qsyat6a`oNSi9n#1v6if;Kq$GE z8!gx`hnLa>9H7Vb#iozrjIz+c!%ueBR6cH;?!xns_{8n9xzML{Zz(;^uv^hzy+ev4Vz^IXggb>l2-90)$%Xo6za z>bU05vFUpItA4I0u=4yUj{Apjd2supw(wP)u}FQXG}be%BKOuZL};|_sYNy_prN>u zjc)3;;OV%~)3&8)_w=u={XfHnvoRA+Im1K@g(6b#<6t!~AD-l)tf=VmtsGni1cMH4 zc=|VLP7#er5H)nwCR@^9)rmnD%5Z-WUn>KUDQdr-?LEK*3kKN^Y{SOd12Zm^U>G4k zbd9?I^A)8Is_g|4jzP+I5wUEUabw~DMiAI^vZDhe{E{C-%;K9^j8sNn;9sBaiopif zP$)a?fOEiD=G1qr^HH2)`Y4Zvh!nnH`;6E%Uo>LX^0;KzJN~d~L!Y8}IeCcm`5OK6 z!*WZocA)!OE)KRrIyeKRF{xQ9niq$zBXUA0ynA&8NIMgg-UU(WoPf6ghFMvfU*IuB z*h!&i;74ebu&O(5P}O00X;4y$*+3wLyMIgt?pZI?VgCNxNo;9L;2M+4!}L#1n3X3= zyMN8|4=8B-cr_NHb`kXVG9ASDJ<{o8Fg1{`Cphq&6SOBU(_;q$gWlmJUUsHTGsDEmvtXe#;%tpe+Fdg6o8q=yIArd{PMA=r9*wz{QhcI z=E3CS`I6NKaKF26*y`Q^tHf57{BZ9Vj(&i+3WvmepS~(kkmwyLz;{r88^0?*sjQU` zwL7P@eL-P?E4u?ALO4BixTcbLW2`ayVjumDWI98eTSg2g3(dr###Dp=);yj2QK zHX3ECSmLfNL-#mSNseNK8b@27JZ5R|_?xF_F9j=!b6@idh2PjjkI(Sco7{GcuJqtI z+;<$!zV?JxI_9I@m}*;dbz)#Tc9ZEtAfzjIsLM1{WLDD4N_V7z>i=jP_9N{cYGCJl zB)S8xd`rdogB>Q;KPOPFPOFp=8m7I$mq`4mp2zHbYM!j01txt)3F%1i>9fRYjpx=wX0}3`{;ubLLHWvX>yp`xL9;Re5)y$ z)MB%nHuSOTFr3jKr^hnF2GGuY0~#e+yR@fn^oKNL3GP0{s$K8U9*SXYUrHqMEe^f| zRnh(phVX{x!o7g|^D_0s!uz5Xq{vrHQFxKdm&bqx@sSabeCYkCk4WK+#tVt$^ZodY zZ3oTd`d3K#L$VtM7Z}5>;x6)>1kTsoYl$B~s$*0)aSFm|!ynMgMQ)L{13=lXYE=zX z)9w#24Tfoc(phssHcAWMuEG*ts3~|6;EF>zh#Zc|7+-^4*GjWi!XMDoy;12mz6hzZ zO?-`^DNcKZY(4v_`M>}N#9g;HTzpva&0qFXSXk%>0Yq@4P;wA0L#HT(!<9n z%4NCyz>GAcB#2bvoW@}Gy)-Y+LMH00Ul;j4;@yv?-TU%PAsjUAx@+H zniJMVQ$_}bDY;7rR`o^aMczEWt1h@*n35e22q%U*9IRXeiDoZWCAnMuFXRF~gx=g> zzghzC|0Gc-B}l>=6Of3i=_igRS4Hi??H&Mv*7vM6MO)cO3SFt{bdgADQxX7T8UV&- zwSk?&aN}rnmo>VRFd!Rh>WZ8Mmr{Z4zXJp3)sQP-My`)$`D!Nyh&)Q+>caw6rk5mh zKk75eI4!v+iiM&;Ioo21@*3}=jAyjepMI8iiSNo}EspouY#`D0-2RsA5SSJ;jIc$d zd!eg?q7cAw6PRGIsIr*l?Y(WlC>ZXB3OXVv!>?Q1Ee?R-92OL%i?1fqcBZUV6WDG6 zjo|Lcu`YKY0aOmHVF>I86b}N;baJb{1&5bue1k4Rs0>o+Wk9qG>J-(KEFPj8wnVxQ zDw~&0g(KPHi0v8s(ywSyq7c7+L@-{ipc(ow+OgV3+KQzpE{zsD!uAv_( zr72X#AN72uo@H1O~33%5qC(LrOp*paJ*aQ9;6HsPslg z$3I~qyzh?yeBn7$N2=cs8GsytD7aHy!?F5wj)SqAYyKc6?|9kluMy%nEQ9`AHqi|? z$BZl!btwZf1=Hy(*A`3?lfDEr5ZzE2WH!+VwT6r|Y%#Kl&4piQtU}fjQ{k1MaL5N> zHM9F;-SAct!s=g&x19v;ExYgeP)n3C$j4maVbWed3?Dmryc|wP!~XU1ASfR+OAq}5 z`IGIH8Z1Q$SjoV^-~D2TrNjMp32b=guf_y}iXd3;JVjsX^I!;hakT}nD@8tIHWMmM z6^-&EF>IWI(943r6D&M0Bh|YTxXy}sT&`qXkcs=w9Lc4ax9O@HCMS-QlS55h*xoP! z=bqlQ8xSbCCHgCQED(A~ZG37^y&qzLO{S^#5g&7b>6=N6w7N*1;mKbFYCSFc(KJj2 z3JP@=i3onPoqPHf9eJ1__)S8tT$enciu*kIKEOi;L}-O94*BlvhTm@w-*jui9$mLw z1|<(}9W#_MWK0hCqXhyQ%Hv;jnWg`0si6PCQ=FeNPCR76ZFW_ObN_Ko&V!1U}5fGQy@d!v;wvoK%Tr4Y!HkSK{M{t6nxC_3gmFDvI zF5=Laz`&6M0s0C{lftHp-(8AjB;NR#amY7VpBv|Nz&fXAn@ES^5b}AyL0Nf1;G=O9 zvcyi*E0y{bpoH9gJ<@I3Ls4m47&dk4+Dzm0j`OsuBl-vRAEPb>rSllpPWRwI%5^4}M4nYBpHMq|h`s$b3fZ55<#?Ba2C!I|uQbPkgN*4cV$MAeZwztsrl2&6*lZ2VG1$qv ztrjp@>TMo@NN*JDtCoipkU_oXfB^TE{-6f|0OWU>T%+|w2dS-!fe-}u+`J3`^x*p{qU7T$F?aQ6J43VGr7Cy%{ky3h)7;(MA>k-^|TS6Gr;-G-RW6tJp`mP z=RV;3R~`%bKT9@hqT|`3l7@&kD$!6!<#fFpV)+1X_{`bWHk1Y?A^_@t$I93blp#pS zImRWsKU3d+=b~yRrPr-g`6r(JKmh@b%zncN2h6@38XtR8NlgrQ69Xax1=JV!J`Cv; zHAX67wg{EV7#rJ#N+Cee8r4&cM{mAPs5gri&ydW1xTglD;$&d&RGz>G+#XrI^f-(? z;neX^!!HQJfPGm)B0!M<(-@JM6xg$gU;zzK_UE8@98>7DkQiwue4~r7$Vv|=}F_wQzom+65+5p$yYXqr`Yq}MT^$$x0u z>R_m=Z)OvkB(^ z2w0J7<id-_Eaj~DQ81+p(K_6^`0sXyTbEZk2FQ$sB1^>42F!2k+wX&!+@^r zi#sGmPjU^ZX4FV!N-qf!`R81z0|gnOp6s0kVdpQYS&)>`Drh)R=&hFCr!eeL$yg{9 zU2}LOPZN%9UTh~D+qP}nwr$&XF6KoSZEV|ie#u>ONxuF5*qN=ax8JJi?V0D9?XId@ zL6(m3wrvv4d+vV{hqIuR9ZoM~4{T-k!A>+@X$0^+iA-6iS{m7PE2#AWxjS-FcB1*C z1=#7a4T@ct@9ZS`s%I(~7AkZn^v+4pI9i0SOxBvIXz7S=AKtSW$!3&do=zDYYQ6Qj zSVL=@UiJ`KI^s=8Cy3L@=76_wYF=VgjMXRX7I=e5stFpq6to_!rXMiH?SY<|dc3V% zFC&zl1_-gHbHC(2oE13qAet3uaWad;RS}A7SSfnrgg5Y!wCW6|J{W*+7Q{(}=O$6I z(iyWTNRGDBND(qLTQ1Btp2shiK9$2coR`Un#1nD-*_PmUS@Bmy9;$Df&~Cu^t;9pp z_+iovDpZl8A%>SC#HK*F*OeE!AThQLdNflDRt1{<{Mu?>W%2 zXJUgECPS<&|NaXo_^07!2U~W`_hKZ4tgv&QTN?ABpI=Wh&FBFSAT1a9S4=K~bvu_s zt0FfnTmWt%40GEMVrKfqGwY2h0;mz$w6WOp8FyJFh>0DWJhKdx7oScDo?^7&gg^0p zS8*wDxP@)tDY=B?1h46d{XoGWa+VlPEs1NNXD+gi+zUQr{T;GHZFNrI%oR5~p?WBv z8v?_60gH=&oxEef?DZ;``m*RIv%NpH5O+?pQ!;c=pMM#5FlF_&AXoiYF@Z%gDP+ED z1+Vx>kJQ(9411UsFo7NkYwMIxL7VXr1N*_3wocx$xk2c}^4S#GZll~6m{D4i8Ow!o ztLv9U@VKrZxdo8dcG`M^Hf$%gpt=ZLVL_hdzNWkSB0>%-PkI5ke2h$*`pFN>eX~Jm zW=TWTcG}Ka=kNkMp@ld;EsJH+q+bSE2om4QZoKsU16gc#e(*E;hE^32mPp6twJ+dn zCHzZEQjEm7N!yKwTm(33Nu~P6{A3(54yJUxkp~x5y-zV&0?R;A|MMlU{ z^ou}|$`fUPJW88bu0XYioyx*W%Z!H#0^)&HaZsZN+y)LhQg4vK6{8J2;oU?D%vI^H zY-uY&*qM)-|L}>3cBRh1n&5>2HAKyT{B4fbHY)$IMm2*&i8hSjI^FH;LUT7X3NB?2 zBzX=3HC?FV+yh@zwM}5wB<0(soN2-fz|%7mK8}Fa_B=ZzjYT35xKtyWo!^ zz=xS)57}C;dn}BL)CjY)b+@cWT6uN(HwIEZSQs=^v+Lm`oJ?M0o=7(xaAWPeF9d0nD+UR&~ zDdF~FSh1e6CFyl)fxY(e!A84VA5;P1We8N!HdyAc@;LCqfOU-EgPX@`SpC7m_}#@H z+(>$jlD{s@;_`x*et3U*KtZ#r{GH4|{#U0A-b68O`@RKjGS=O6o6yfBaWZo8khNlZlvxdyj}MO(Tf?!%4H<+F@;QJZMZk0)(PCxIR%jTbj%~A7 zOc!Db2tQCINQ2`=T)p%L(qg6!U#=HYOr4WoC?+pvU?iL-4HcyX4WbtVSDjAie_o}= zZuvI;c?flO$-jB85(!c4PfZn-VTQf%f2SwYpr>o2~QPr`w^%slq${m(>{`ZQJB1(y+S3U00xPLdv!8nRLtXeNOTE!%~ z_ayUW=;!Az|GgSqb)y^;NWvZ0{Mbg(dOr>|v~)>LreFpJm#KvV@ppw~PGoT2WoMOm zH#otcbnBj)Fl&U{MqwU3U&#EgnZehfnYCmiy&Od3^t#Q8O>kF=KYvYl4(pLUZD!9`rpUojE z9$AB;B5fovtOl$2tmQ97(7xj zJf)6hHDvuGLZb)--rA3&*8DgQMzlceZ8DT9^mfr*;@j8-S#sjBC^S>UwX_@;*0 z)Q=FJ%(;HC3?E*j5M0c#BK0)*Pp{S^k3aCEIgQn}y7P2VR-sw)>G<0-Sdn5)V&ti;9oKDRev*5@11c}IkN3O>YNA za5g#WG>W_aI4-~Wop>Z%m^Zrce5gor@b}?bOhW^f1neI-C^S@52fvMn3QF}o(u5pY z|6ND`1d?6+2zTAeJQ340>);OdeF>OUgHX*{+iGn`2F%Qwj@4hi<2af*VAovZf-K9ui9^- z25jC~VET-n?QCcnc3 zk~ypKYIuK>wT^|-K4Jam6WKpK*+nb2k=+Wn=)T~_5cNm(o-x)LnKJyc&=7@~F~(B8 zATLQI#SNNSk%a!t2)VQB{yE^25LN7jax0?lxB;%$2pLwsa+(Af0FO{) zD~~{|O=g0w06QGxZK~bI(C$V zX)Z{mP8lVOTT+{oK=?-J?mt*J0J|T#c#GRw`VSqt+r1M&vH(ZyKfcTVIugGpmXWZE zcE~Z&Cp142l6%>3J>WM{w!0(UVQj%CbZnOcgTDd}dFUXODzI2qhS9O*I|1}r)MR$W zg0-h6A%RaqDb~@Q2tA|!hmrSr_&I^F1Fd?b$QUQ}H_ zMTGD~8hx^8m=OpIJ6GN6lH1W-Z63Co+gV|?i}sG5Rq18CPVaV^Bjt`t=S-ofG<}VY zbnsA*xRqgd^4~v6<>tBujT(RbGqwR2v?FvLHn-tA{sFdYCzS!z0TYAhvgR!OWg*f& z0QbcHTD)Snhb3dYoNUegY?8g8gGAhxNYMZHma^z>S!wlF=srZRX|Vj~qycFnLutA{ z7&B1f9%5JXZ&ke&eH>%mMAFWPXIM3?H{j1hI-6|0&NM>mt}~K~he{>Kn8<-De0;8J zX#maxvk%UOAZ!SsP%y%Z6C#_K%k01HH|T_zkb2<+Q86mshU)LWAt$&@Gv(b}m|?@` z#?DJF3x8t4(AGYBIpx~^;e>A!pvkA)b_Gg#BsUmacP**8c2)PQ6TioC)H+NVccRjt zzgiQ>nHR(eMlj`pr09L&pNiZ(6lUPZj&S`lqV2b*B~QzGC42vxkTMH@ookAS1Vq)b z8{c|TW)+@Z05|aOw-Xtk7)Z)x`Z4quCo+DeC@P}+i(^MOmQVpd32KmT9kZ0ck#r60 z5o3m8vev957*LE$em<~Echb_!DB)}BXN2$~7$jXRxZHQxW`K)2 zI~%7tT=ag^WXjrLNP@N|_J|AVPoMl}_4)N;qNP3gI_gS9qqglLhIuubO_;{lmh$_= zjlKcj1y-S?r|~9)_2p>ZB>_TsiGO<0*O1-vGtHu^cew^^?)XS+^~qQBIunhx0unkQ zQ&pdf5-MF=YV*ImJknJKhDGrftH%MZC2I_rrMyqNk`=*-k6t&?Ky#jEK_(qMRhC8q z98i5C(|96RGhuhguuArnntY~o`9&<<(#mjMLpQ;FH($cK$Wj@S^hV0g2Y&4>$9_>#=^Sk@RlR&L_zzL?xEyYCYk9FgY?f z8yDkNB)hF5_lzGw_wYF>E$-z2m)3mk;@?2L=^e1Z-a zi=a+8=Gn1?Am7ZPc;jd?H{`$H#nje^62f35gB%KO%~c~8#2x77z3x)Hp&=d!2(7OL zmF>;oiKJQ21azi&=z&4yMj-{7d2=0G+qLoZwRcRE>Df31onS6Td`!iO5?(J%yi#EW z_Dyp>bPzY?%W78t$<&^)lUT_@3^Qa-&|f^J+}UPyPNF^m$^2>XXKsG+p2jBCw8ff3d?~|Eq)!NPojH5X&xsF^>L>R3ppEI~a+7pVzLvH-qN?Pmwy*s5#dG}n2Jm@6Dc`leDt!kf)qDDA@8UXdY} zFw#eAqfIGd?yyp~Ouc4fuubSugY~A9I&Hq|p^v;WB7aK8&6xhRzG906w+d_!VW0igXIku`8_-wjiE&@~=qn^!2; zqb=0yUf$K!JVO_YGkU|&M7b5dmx6hJ1r`I$KXB`K^SUHUI%6P&5ztnmm9Nbws(4U- zE^HqgM6W-3Ls*W{u}r4|*a$Z?{9hu6c{lC-Az#`#CMx7OEZ-1WI=pu%Zuh>MtIskE zoFbeEVLjF1h)rywSbP2)b^^&x{~X1pu)K(hSoe5^DVqH=U(q>5Ha_b^hqsehKM~cCrKB44#med|cx3z_9x6;;-tzI~1l7fQ4M#uNM*OpO7YU5OfFr&g%^SE`$xxj``B0{N#BLOTP zu^10PDPF4!uMn$=`S{I=EfT)B#Vkcuv=;5Bl&5&dV0@r38fqi-zTE{A@+hwXK751W z^B=gcbOyPl8gtTV}mReX%#ywdJ8J8zvV-;oUiG2eP@z8XDVST-~U zLncQQ1hT_b72pd+n0y~@w1!&Q^!M`CWg$l>$~hwo1dJXVOK&RqG`hppr~39kum`@P z86WMTZDOY19ROfU5-vt*ebH^UH`l85?1CJmtX^$;DC(~Gt}YD@geAEAS)KHRkAUJrA{GDl2W{FW zXkA>f2m>n{nyZq$D80&`F%-rKQ!K^dmj{OQ+Np|YMwx94h`|!C4Mmmmp-{&nO|=09 zD|M|yNpV;PqotR}C~N;A{!?>Or=n@*0WEbY{$$rJVj=0FFYz8&=}y&#d1Z-Rp})l}(GpA>Be5DVOm>nnK2xvOnv^JWPi}>IYwCi;{hf6% zuIzQeC&&oJ2MX^ut2V<96mWOrh6lY$etFfWkGdc{x*EyVDxNCSwl2PX=soJ7neU?{r>%? z)kTu)Y#B*hVXjbxAzeD7RSeG@1x2}_toO`b?KykJ>4wI8jbhzBPm(hli71>QSzK47 zZ`b&O87vdzoIcoa8_9s@me$exMnNOif6tH#W7CvePp!KlK2JQnM?IFc9NEZ)$xERx zxzz*~037yUoSB?sY(Izo;0tEv6}>-SlOx=mdA@{DvzUXy$7*5eK}m)Al%IVLW(%Sl zC@`POu#Yc+!MDDb66<0me$8wD`HSEg*QA*edhLygb){kE>Wk<@^3f1Za>M#nwyt9~ z_{T1r648_Tier(;Aw@FYkqSrioJ!&FEbC~9@>yQ7MrOQdO@h~j_m^Q+;?lK_S~jly z+GeEVR+N6qJ&W3T@!?q)n(uJxzcV&ZII&bwh<|@cH^iXe))TH>kfB-Ts57-yj|kJT zt*xrZNd4m4O}VaUOxDtUASyf&z)XLyCRU%8B(^)orSa6j+`tlJ!NPWff)+SWf0XYXg9 znBT5G5AMrJP{|YemBcA>+ms0l^+ z!bF1p`#ya1X}+gGUonk64|F{K^HL|I3r%X8t86RGDrN0*JaZQ%B9cCRVto%^&jm$P z(!j%ntN4^f5=`6kgtE{T$R2SeJ!6bv$2+x3RC{tTL~#b6oMTDBAe)OL*-w(%%U*!D z$D{BI9b1r_PuxY;Ck18sNt8R*vA}i%?@UO(T3KpC{*RFko!5Zhh&ZKmlLiDe; z&jbxJyHica11Q3R&AzTUMTlQ#n7O&?n0N#jXy77hnXMPT4H2&33;1;L>5)6-UFL>A zM~D`Ejd3W>wft31LM~eZD06{&$5yU-?fwfMtuFc~*%kcj2;2561rvwMa|jUA9~25D z7xjxZnTriV&L{7uwrV?Za(SWFpTfX}xvx^FX;VKL(mScq$}WG_xVgK_-%gp`s0;z` zv?Xr;{bF^&A*;!!)Wng9WVZ7t_|QVxzN|gQfapPV+@@AbxTW{*+R&Ppt5( zA{l)TFgRzhq$JfL=LL+(YO~V`p{OG^sc&}`kY*;v1esEICVl$*SC6ERQyde!`GkG+ zr2oKFpkM#{Nv~5Y4YUo>wOnP42@+?|mS;j8!bk*o7>}NG=rrENTX~4RYYbM~FqvjN ztpiS`TwVvVc!HQ18hM;L)CEG)&N2~u&%S~+eZkZXV5z{dlS#yY8I8btPF`i)%xPY) zNMZQ`F_VW{{J`)FEXgl}33d#nh2

S)^AHgSj&ef4b}`7$PU``oS5ML`1{h--v>Z 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 z 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%0DF%9h6Ta5NZ=HCUX9 zF44Q&V7hA=oB59%db_$Hp(#uE?$&p6iN6;J^{p__?ul1f9EM{iqJcv+76zK~ntr>R z=Uk7(6M902fT1~(MrIqqV?3?)lLe|YEb#shk;?xD2ski)QF=p9FY*IeweBwVzI%ra^Ov4ZmTD0GbGqjmf zML2kwrvSRyoUeqmaY!2KGnkfrI6#>lyO^2Tm>$qa4m33xPn~vDIb}R~41ay8pH!BY z)5+5PVq8q=DKj@J?I3eSZ_$iWU zdGm4f(LjOW7mWv3rnvC$*{LL#FnDp?tGNaF&o z#TO1%VSx(gQlQ)SyC^v*1Nx-ttWiu;lAuR5`zOg5OwD2G_xGqjoXxFGrnSaoVS;d% zfA$8D^Cg3|g!a$$P#=Img3TXH-%c=AId#66wZ(?Q;{e=)&JL``QVUCJyoNMXpAn4JxEsou3QvSC;hs;b*x(D`EguIMR=V$JL=C^~`>I!=SnOC`Z1pzwan*H1GH|SKTOH_Q zJR`ZbIJN_){awL`TH|_N*m|IwtBS;B9?2T8qZXWPTNN<+4=p%u?)^U;s3SN0oiiAG z2V1dX-S&lxdP z-_zzT4%@m)G@4G?5~IsqBcG^R+HE4?ft0Mbh(@U;LR52Uu_kEem@F-X8e}DC zQG89??J}#cmGJZLEwRkEFCY*(==(c8fA^*ZpR$nT;)8t>Wg!T(1JCq*8Wea3l6VaW z19i9w`fh(4;e;w&8dJhr*}Vf_tl`r2s?5=Fja71+HziKYpoGuG@hV z)Nm=GxCiPdFqEQg@Gz@Zd^`V(t);{+foSkDDLB4956p(gz2^7|6&fY2DNC9Z0TJv+ zZCC#U|84LI0mcP24B{OWIAePG@$ciizF79&ZRF?oUwes%F>gOWPQMEEqXG!k$FdP&pX$NFCkWJM_1!@1&J@4}xpM|8Gz`lD2}@lNbbTP0yn)?!VHp~kA! zC2A#M^VNRp{CV{I46HFGfQayvNpCcPm^QPBPB*gw`)|=kPqWrj)9Df=q1fPSN#icn zUcKUaDFt9bTn{hh%tf$RYa%3&LUfv8gjimVCAw$CRH@(pmc^RTTFbyE)zYmnyvX1~ zy#ge|O*1o*a_?-sy>#3rtEbSHHC57ZX_8BpJf7lvDH~_IFgitk1H7F!xB?V|&%n0e zx$xTiZN>@c}(ustwa_oU%?-*7etcSACXiU*$&xB72uAq?VedN%%46 zbAkR;y>=gxmDv0l@FHC*b~=IfAuuo5TrD<+v0TW#ADR3q424^?Q0OX-64fk*52+al zcG_t@W5+9tAxJ~Fz=ifs)Ir|3)lheQB7Vhm?4L&rkI&^4NC-D1#HC5=_ zuAz|G?P=#;aeb>F^a43XjWozdAns?E{kK-QoA=wXm6u(j*V`bZcQGVtHNBwk5n)g7 z37^&<QBqV^MD?oK^Wgdj8|g`u=wygwFz>Xqf~j19%PY0NLUT+~Z&mcCPjIHFyZq=_Z8PC1JM@_e1q38^skKf*?V3mq_PLV$q^7T|uFYU+BriD3WnKG5f2ESg2fe^bX=1A?; z7$Wp=y0MQr{QX-<$>DW&l(hzB=h>#c&+AwH<&2XiUThbeKJI*LLWW2odd z$Dik2>rP5r^hCmIye_k++jm?&HJzMcI$BG6Eu`ZVR3j6b1R6f4U!>dfa-p&sFHNmk z@)zAu2A4tOp@o+Y^-puOzwa!g;!9I>l8Llw*ZTq+YTH)DzQCt5G4q7*-HBWJG5PJy z0ogn0xfRlB7`4W!$&}O>-NaRvkGk(-_PfE|_{*x84`^3x4cNA7;M%uy%JJ6bz0>!S z=;&j)FT$3(EJ)K-BC&FK{BpBv;JBlXTEk?J5K|+L3jnksork@Z)YQCb0Sbr`7nkd6 zYH|SSr7D1Mjr1B#09acB0Gt7UotjAv3oUCvS&a4ysy1)sg*K_W9Kf|oDBU&vFwF~a zT+1m}4%vdb?^Nw6H0U zu9TFlEhf|QtX~K_k#9H{ELS|4w&H0}Dlyb7tb^AwG1PGB)3$aw{;X5eY5P@_q8a?_`}5XYwbu96 zS=v;`CUmHmDlUWTp;_AGXtZHfk;%5rT}PCrQ+fg9esPM=KW^q%I7L^=ot{(rKbAzj z5sf#Lw`6gQQ?o3V$|b{geO@y$sB9Rh$n14+i_f4f33JmIiKDb;grX3df8mOIRw0Zf z0**Tzb_iej!Ul^DSA<`4JM}Euzps$BLC6-byy^6A=KlG-1JF}6ps;CRSV!hmcl~n6 z+HQBVWkumI{ZzAbcDK|4VP@y{HL5z(>NXWVd{$>y|HeUbGd}KRjsz0A59%Ftebi!O z_O9t_ArGwtR=l(DICNKhB~b(YI5@gZy^Pb zH^xaEIyyNs?X#=1nGnZ*b6S!hq%5i{KEtyS|%TN z@1d%F-(Dz@q?y{@Y+h~h@MPpNY}FGmL?Q6$Ow2M|JN|O#4*%9R){^Dhr#9(FRL8g9 znVpAKkW45jBI&J#BNEmnZ1!j3Uz)ssN4?09rPyy9r^pb@K}l^fWJqFMw!O8~n0l)dRKs}V6hG$@x?d2;M@$wLfVyeZ&;Eq>HB$B8PQ zyzN6O8B331$>v0DK)oJu=_!y2qwvm1-yWd;yta2V{mLvvfEiaWfqcg+Mb)^GZM~$e};U=?Qk2>ea1iEJ~`;i6zwm*3bvX} z9l3$ay-LXye}ak`8_uL1*m#D{ngySE6rZ92@2uc3R&BewUge4Log$w=oNiOfR)lvz z+Ft4N-7n9V1tZt>GlS8Y2TpZ5jWFoiO*cfSyb52Mp4!*+zm{CIF7iq3TmiKRjZDja z`K(H#p=ypz6g{jhi4x?-59_=F9Qi=80RCbz5j%FhLU(wDwE7QT<5G#?S2=5nP(Jaz z#S2k!BQ#H?&{1d9y(XtHSXu^$Yu!o&3;BC$2R7RY@qgB|oAn6PGqMF{lAH{T>ejYqi#uey0~f@tNh=DdR4Em#Ir zZy}jSp&R)tK2J5bzWhpKHcR}byJLroz4xYCqhJ$}P5Qm`ER$~O9*!AZMt>aplPxD4 z-FZEWq8cK7*&`6g8J(qbqT(HDvctHp71NQ5BVAi7Q30JTe*Ds*4x$*JjxIpll?ir71pf_ zrRDpO^v?_Hg#}H&3B2){R1i}3=eejj1kz-8EnX*T`?zkO)F0>%j4H7MCCXhMr;uq! zJB`)G;0nBJqTz^bNJ)Gb2J|k+`w~R9^x-6q1SyH->Bc&Af z>fvh@=l!J{P;t<=!|CMtXNN%pSaLs{Ta0~#vBkTz6Vuz2`~nA)TWPTDyO>NF zZ2L&{_eRWC`ODu@&-@8PEXbf{DbX|LE}|d_;)&-81?QEPScXmdIEkhV>$@moDs5$H z&Mkvlnn2#%DWaXy3ZvEfTFM_*zn4GVh*kJDrnW!A=knfr!kFZ)DmWsPFj^45EIN8` zz{IG}mHm6FeqcjXb^>O8Zd(e@5ww%7QXn+?S}_9uRjZHlD%p=_lXWg~+CB;JvMW$~ zso|eEpy-*0O&+7&jT=8EM#(?c;(zQ45nU|MD&FkDyyil{VBtzOvsik+)-xmpZ%T`P zoV;msI_YI5C_YPbGy`}iK+(p;m70;%Jc6Rm(RrJRwHc=V_z{$V4uq0^2mHyRD|BguRh*cpgI&|(iM zZ_h({+KO^0fBlS2#`^nKrVBmYi5QK8#ylIt#<0Vo77D04L;b}}hv=^s>oDh?S@N$Epe#|2&I!31Hv)T-=%g59MUeMXUluI|yNY7&Y{@lUB?EM-w z15$QP_FgervXC?b$8w;%Jeq@W%^%mfFu?*wBlcrw(&}&;_}Ey zAsiv9Hk8aM=5A>TLe_&1{JpNfPRz_&sT0GH?p&w>`|P_2&&InE%Z@#OW90c{wN)#= zRi1z_$TLwEq%Zl}pmT<$cru3IP5HFkiWoQQ(NUStoPEq zb-B9M5@sBh(G^~gtuLI-)pn^lX8StXg^))j?9BO(x2C}D5t2ePd7#&A>RyYx1B*?j zCy9KKxo1=T6zL7?^SNliG^{}P(C|IB2g-J3BU^{Ahysfj`Y^ z{u&M@t0NmEAcfxGG~E!S+pD3tE;&IT^eus?K9=PWmON;zfdo*K*iIBHt1eXKn)86a zPr9M_51jqr67f_LM#9crD86@^4{pZy6jCP_ixvx$-hR!6I6SkSb+OJ_Oa0N$U(17_ ze?-iy4KuSi-T_#UADCs?Ob;}(zUwr5=ul>eEu@nR>62@z2`*ZmTLY&jJreI|h~uP#+>h5kYzNY21ypxXMZD@aCF=Nfkp`}bk( zj$CdrRu0W!#VnHMJ7UJ4)DFR;d-0v1w#;T-vqR=wUAH^zMy2wq%b!Q~M41$U4!gl; z^B>3wy6OU|bx<<7X1-pI7I>v4q`ZTi#K99&=mqf*=eCP|tR(}H_%(Gpi~1w+p@0Yao)u!N}BR`qnILGUL?8BeJ^cksmb>$t6z8wEe6JUGc!~J0DF+tkZ zmdJeG_duk)u}lX^HN8H?z-M~slne|ftF~8sdrmm=NG=*qQ@<0mPgpuOnck&v_$n*I z_N-FSUWla%d;Ajbxe&?dE-(-w{EIqLiCgxW^wI+Y+r&CKFJlJ&Cj}xjzBN{XWmp3- zO}Z4iQ9c(SVnCA$8K*jWA-=89tEMiDHa^eb{Dezgdpf1XIOL>GI zd7MnHu$JOC(WWqSW3Atdy+o5{5g{r=BzR$volGRC0A;jQIeOPFy zyUj`i<#3}_E$w{H;BqgV$y9Z@X0WslJv1e3DoJsZLgv5xi387jC?cP#BU*#lDYKWv zm71R_mu?sT#2=wLBd{uvDK(r6MLbaH!jPU(BTkXADg5U+81)6D0wbj*uztEwK0F7X zcJLX4oQ)U1z7U0SEd4ADm^h4CUtT&chqY&&xzG(i4^mWe&IC<7K9FwNFw$b4^L#^c zV}97GOg(PJSHg$M+5PZq+_`{Gj`{#C6ez?+@rr3m!XIhJUOA!1)JAf+CHiW2gX26)W|5_m0VA&AKD|iP z5b~6MXmScn^ixU&{6Hjy#P|tW+#8xmsASlIm;gBpRv=wdHv7*jL>8A^gS&uejGi$Y z$}aOBAVOxksa}pM;wYB(<{j$FfOx6rJRO%1Dz;Edc_?rz}W*` z;N{|Qgh|tfw-EE+fabMXontt8@q8c+(zmo`uCb)C*upD8xyZ}Ti=?Lpc+3+VRURdw zXOp`LBs9&X{Da<`|v_((_py zEgp_-j1UPn@!H`wtraYcm{V(gxV%PBV}AC*8N%a2XNgACt`)m=L{X>I=yNlPg$?`( z>crlKOp(71Y1gAI1YX$mQ=#2-W6r*OrTwI2+}BQO9B!AD%x9mHLwkL>CwmEGXx$}7 zK+ZEZDz3N$4R#4ds}(nuf9n|P9u+8;)(%5an*%be=U1%%^K#t%{g&oK2E@Gl%Gx{A zpXeD$CMGq043t|~a%A2)hY^XkYL}O#DSvZT#rbLgQ1le!`cPSouDxp8I9a8{?rhtX|6|z8g^pegB;j9nvTthqWX!pz8H;HG-{qJUA>sQwBGV zgwPt5X>{|}V#MJ_9Js=-|6$z#?a+>wJzKQ-G^t^7g19q>rS_W1G3<#tVz2%H*pcs2 zCdbXwnesU=yB|NV`*%A)JJeCLXfIO{S%=;!vd%ZZ9{J(tltYYkkX}wQq1|hsMfgJG z4B`ZhRnuyImy1%nV3@dV!J(-|;lG0yAN`WESDc`}*Ae4=dTd}ULn74yp+Z_Dhnx$e zbI9}yytCVGPL+jJK-rnhZiBFMw#c8{hCk{X$<#TTvnd_3CC_Q(PcoM-((@x`>Y!8gbIno+&pm%6%N zpf10dYSbw@{NTQ)Fuk}niu~y#Br>;$!qN3R(non5Ku0NJe)$gwP-Cj55JMN+mJ!%NT7| zC%;d#jfx=UjpE9h0W-sdmn^R=fOYV>{;h%I%Md>BA@BVMB#IRwZRQn727Q=bzw=8%mMHRs&R>bNre*GuRB0FOX$zZ4XepH@9s zasK4<>1(+r0sKQyBX&roQ2#R-IuveyHl>_*PTKb0J=RY-?e0clL;4d#W?9mwXMex0 z=9Way=sQ^iEX`!;w`J=#!LJ9rRex-2@D2JIM7mT9;sTGZe@|egn7^Z^x-Jx$2NuC& zL#S-f3c?6kzg`Fe!#kgw2R67A6=|jz^Ad^Fi)N|l9z&;jlrxln4hq_I$mC4MRnkh; zX)O4<)T5z-D}j&pP3Bo`FO5J_CB+UY4Nej9s*WTD#+~!5lA>U@w5Fh={~CH0Ww{(j z*fl9Po`V0picIvfP6}Ci#CHh0UM78DhqY|H8m{O`CB=ag{i5!S%-(uj9Z7g->O(Umrz zOk7;S7TR7%b1moE?Mm>YJVPW{?+9WWHv42dCID{})JNy=WDxFETSA~EDIY@(7yrAA zk#IP@^c>}@m&6t@Qj`KA;l{zQ24IDkbe~xNaGR+61TZ>ZBPHX24XY=`Rt6D50F%RJ zp)_gyg)fT-VCfeH(dk?%545^70#Dk(k~mb#QCNQ+C?_3@{RFGRut8Kk090yXbXMG{ zFy!r5M5rzr70FN!bb7sI^Q6oB8Eu?p)dYUxn>V~o#LdW?jd+O2bC;}XcqW?!-#4zT z(|rRd?PfKRE2;N}K|b_)7vIYO@%^{Ef~Dw4z0uG=a1&cR8RF$~J&s^)k1Dc)`n04S zg2d8^`-g0j@S@0tG(|DuVBWYAZo$jXpsI>3vw_IDQYBOYyVB(Y(W zT%yw$pqQL8UN-~mJ;NJUOt7MU6-TU+T5>%DkL=hGsuZ;yIM`%+~eE_jqz4 zLmO|I(sO#9l;TjP*dlL_vyJnX-c5*BKF|$!Nm>xY@-PB=d)EYEu`zG4Hw!_e zHyB9A)_s%1#iEZyPql1Xj9ymN@};!KrzHs1&+9-}^}jhRVB~<4vYQ*lL$+_e1ezUr%4 zl83hc2tYW+=S6rP9on)q_R9nq(h<1N0y7Jh#9~dkl4e#m5@#DljlEdn6S^*Vs0CjC zLXFvAEY^EG9&Yd+b+Q1YImM~yhF=+jQk9g=vI=7u#c*wS%CqbZIN0&gQ22FoOpka? z5Vl;)_ypOY#!Ja(3zLh4#ZzN?^<=(7=&r+Tafo67B1Ju7EhCn^3QM0#YnAug>5i(B ze@{@LrzBFz?x&(--s-m&^s3z+#|CbfKRK#E3xmYMJ6SSHkX+?S>l-D!FdPay7ae^P z^ej7Xp4qZ+mvv>zT*epjw$hMPA!EjMqMa(n#59doa)7O(^=?R9%{N{b=y|y*0Fi1z zolrP{Yt$O2w1wUX!t*`6vk*wOpg?Ly+7;@~M;$+qB12)p;=n<&ku-ikXtb&s{;d!L zP9j*(96iFq#trTy`Vjak zbrgKTWjYEb^u|pg32>eNsIcutTmR0le+wNZ#a^;PP4c}jK7&oIlt^u1$uOhMb-*m_ z|5X>L85bN8l)ohm;JB+VRq)Lk9) zvoA1D`A1sbXdmlOUm$-qOW<}xU133)8qWoi0$?vm5!-)pd7TzGGIcx{qN4gTCTqCO zx0kCT+ybYDWD+lPBDwp*8sY9A4f9S-bQv)2U zC-2q)JCQJ(S$Rz*zZ96jn;-y%RgOqk(+X2Pvzj9KvLnaGL z$P{M!^_}y?MjKg#U;xSTa_dkSs%&9G*3MXD6yM_;8EVW&XOSM}O#`FVACLci`&++j3;18v9ops~;euP-+hX+%mxt>fW)+tMIt@ zsmebD<$eiiSm3WV1393E5+jVv`8=XQhFfv)qJbfU;HF@KMlMW>1bEu!D4}F_qW0I- z3XdSK+G|c}^PQfh4dd&ER`}cT`$S}z36sLuE7GL0A{!_e_!dVyRkOjKkuvOf=YOcD z#9R6@T)7pF4KNf=h1M|rMIh#ML;N3AhUfLhZ#jTwd=?scM{*Fu(S_GXIXWjwu}$7`W&L&DV*^L!V!c|Z-EG2LXAdLzP8013xC0=;@ z^Q)&T^5_|~)otbi_j1TFR=iJr5ISJ^ZfdrHL32+*;H1Qt;&Y&Y0mKUhlo)J3A$tSE3ZZeVp=bv^{;Uu`KO$Ol26$A_>p@z+4&oc90gXv}Vu1)!sdt)>w21UYRU^B!X(&;x5~$_oGqhN)LOEC} z6hHj8C@Whpu=BlBuGp`P5DUCg35Bi?{2SN%xui2EMGXILN%m)JlMgyB#Pcm(*`DoB z`)=q|yVf-(op1tt%3V>_JUt{Hs-U(hPt^-IRU8GAz+6#}#7u%Yt+L@sV0?@8h;}Od zt+3h)0FLa^4~(D7!kz%dq7R^-twTb`W$qH}J3uE4LQblP$>u-8Fe5CzsU>lOGnRP| z(cJ1+4_2u&6Y4T;C%F2GT^g(Nb5}xG<$cglOH4@kALAJ2hg0mse56A0x!Xm{v%}(} zoHGEqy^eK&HeKA*Mge$Po1^9b1lms8HG2-^%)^9}=}~fsbtxs^Q@`Fd<;IW!ppyP+ zOI{BzKD-o)qp(^fWl*z6?ML3Ld@K+h>=#tFz+ltlz7l6@!hp-p<97Tn0X_F(dD7}M zfu`{=y>8W_51_zGvmDiN$ACV8I4TWLaW@)xcH$x~xY4Q!MGNTpL0dH z^D5Oje&^ty_6E&b3YIG3{;Uw2m;mMeKn|Gc=4y~n&MpE3*TZ5SV?vIMWx z&rY}1iPzggk!V%J3JuG}1A7;!vI%gizyWdHw6IB)VTzoWxm=;rxU}<{3ePZ6+Mq@4 zwMc$8X0A4sWyoE~UM1=ILYsaaumPN*U8-MM@xUdQ&Y z8SX?Y&x}Qi{Nw`A8D}O~V)(-=MpZ*^BYo2F=rtBXBX*sgDtvTDIoe`xc(V zne#>vzgeey%_rVy6|0+cn@w#I1b0U$wxkmGy*!)NA<`Y|WC^lN^f05RsZ+MJO+@wl zhC@Rp&LsYX#<(e5eP%9WZQs@zhyKuToqL-pXWv*C&4H#cPpM%fQoh9|mpsZs51P7$ z%aTZXxTxX?2O!>HiJ*%ZWio$%MPN(Lo9ufxy+p6ehnh0g@hg}*U-u~sXQn1v{AW|g!!1d{2ojY9(}R8T#J4uM@1n?5Hp^s00`x{M5$t`mK`?uCG0 zv4-{cAng7xag9>K05U9YN8^_24#5@x;{!e_HuJ-zTVq%!j62HXey!VdbMh~`fc)pl zQ&ckGit}HU?sQa9Co*ICs|Sh&`~3QpZ}3g!!k&jd={Y;aVVx|=Gv(=5+}l9BktH!L z;nA^4W3*8d8cIsJ=d~P4h3>KH}F~9Nr;QVp&9Yx zZKbzl0Rtz9med-^4 z&;-1sX_(M+Bu7MvCV5N`*KwEp+2zzudS8>yo10Dt5kfos2b70PD6bGPex7eE*}rj(1TaRo!}PDrRpJ`Eu=(Q7?LCJtDT2e~Tqe9Wi8&N&AWuyKbKroe9{Xl3 z$6+vg(`F<<;-s2_XIS(w$MTwUz}KSITd0Ds92;#>iHJ&rbTJR$VpzI1{|15Miy)=z z{OP6+h+v3Ks*Fkbb~caFbDG!3)<{+<<7sY=KiVBixIs)BnCMIST9f{D5c?Q>l9D2l zPAZV8UD+`SExDBFG|sK2FR!8c;M(;CMjce7$eJv7$pvR;M!`mHoX78)O!5f(aUPK0 zV{U4>vxUB-afU3{S`ce)q_%HTyO4dBMPCz*>Pm?_5Et^pcuax>@rXViIFntPT9&Oc zr#L`Zh0TSokh=PI*{&u!p4um;y5xbb8p5qxz+QMgyj)n>qDT8cq#f)amidJ8Kh^j> zmPO=t02hy(6PoVnzNL*W>gJUvLDUw+m7Z2PK*-#W`pdJ2hQ3F)St4(e}PfZ19JbMeP01ooPd>})n=oRNJ`XPcLUz-5hFM2JU|CA@PO)J zFIblXo$x#7NAy{HyGN6G#GaDkdR}pu!q<}b%J_d!zao2sdBYv#j&4Y1c0@mHY#W^%%gp8t)n67+SPm z(~Sl@j74)_*^L`;vlZqH=PxVU1NPFZ)=&;!!t4|A5zots7;1lJ2@M3ho6KXhA8`r| z%9IYhXjy*ggbT^#o>4<8-Rq>->wLZ(6Ogi(AHjA%9HgWu%WIq;G&td{>@Q#9>>jUw zlGnkzX1VCFB3<~qUiPC&^RpC``e>JZ*`%i8?|^O(x({$!9;9JY_G}JOg*on!rhHZU z&VRS08Dgr)13GH%>VW8=-|f6 zzf51lZUz^%1LRiAalDU)2u}4P0>o}NO63GB7m-`EeQV{EB+T}>Z zzZwNS^_@Gm)Gs-l?jrIN;P>jfF&TSIu$1QnSk6}JA~}DPqxdD5Vx2}1LgG^RSQ&QO za?i@Lh2AE&RS6LOE!^mSGbckb#xZPo%C}aWdJxutV;4a!wAH(~k`V+3?bP{VEa+PL zH~kOv%pw*;iQp%168kE6l?ySxP}_E!eBhNsvA5>oJ->upXSJx-`-tTf`>UJ+CoAL* z*dz5nDh1Ards&u8ji(EA{;2S|G@b7&$`>)ydj+G@QO(eBR6kznp4q(u(aE@G zTsW#7uhgE=xr4}LTsJ5b*N#_u&+J@*=wwtk7LIF&JH2Cet}GCX4N~t^^pwrw%{4W4 zM>%M4r<tt`7ZmANpN^rN6F= zepe3tbsySYxpdcc(eBEjyRJjOE0^xN7yXjebLeH=vuH59f&>eCJMa#x= zpj6foV?a~y#`liI%@lPr_tkO*T2a+{#Q3Wih3M+$U?J4Ca)npli_R|a*i-`3Pb3ym z2}#LvzMtBs z$|G?#p^m8os+1FDtA9lqtF^Y0yIsAj(@|ZtPBN8Hu455fIUyMQOgxf3VUQ&fT+&BM ztBct3j+Br$ZG87;pc<4|vafkC_LmI|r2xdQrck;LchZ+oEIo@CavFoyx=`rcj~{v< z1OBq9%d$@JPb)Ytsl$E#;vIg%bcrQT_EUgG^7f6z5csl`x+z zojZMRwaW~J=*FHGa!u17$>0%c!!R(!#_Je}yb*zbuYQGuWVKElgrC}wGbAxOuK}5* z0VC%cVd$5Ub@nBMgyK&}B}2J9LmQ9#(35?YKPG(2e$e7%Twmzwz`(={r4ZlixL>kM z5+`4AZ%)O(J9&UCBcEWh>ad z+q020Yaj?7!-zC(u%E>)u5m3w6_zgc6q^19 z8=TVl1AQ909TrvVQGn;e6|B+u!(*6=XH}v21wU%g`VVv`Y2iKfAm9lJeO%TMDX?vv z0xeugkVgmxI0I1d2|^2niPsk@!-tFmmXO%`6NBAuMx-cK5WAQa_xKD>*mAB93l#l2 zRa8|6aup&Er3;StM5T`vb|V(X@<+?FuYZP#Rv2-BfMGQLeOSXnK$sBe=}Wbqk*4a0 zM7;`HE})=5mKDG8!f?*XU~kg?|BR73@mB|M`>HZ*53v&l%?sp?n?Y)P!kz*uZQ?!J z@?mLOsvwYvh_y=jyK-T-Btu_COMC{nnCNWOPnvApkVR+Y}V2FC93uwiCv<+QrN5(AFuux93CiJhgDN*Lb| z^f(d%S;7&x40-W6kAU+zZ6wn)Q>5`3;?HGFCln2gcJuQRV0F0<1WeWzH)Y9UTjMK9+J|e1sq&?CXJ3N7h7@E0`Oge=oxJ(S}*~-y|f7 z-4;^lkYnhLGDz0DLF`-2>T(are3~`~hsg9$rTt~a|D3xcGi_rq{-zN^Z~hH{ZvJ@e zGUR2a2r@VOtsizzQhR+Ro-cz_0)52!pT7y4rMzXPo(clazg>IQ7wf`Ivm`T>85PY&58hTc;XS zwnXgm%gt!Eb+-Yt&PD=Suq~m^3>+^4i(^y_$=Ul7Qt>TPWIYReM`>!q9W>a2H$iuC zJ$Q{#K4M8}qwGF?+VEeBngQT;rl}D$+>(_Z00e_FzkUO!&ZQr!3_c|0I*i`2F4t=vA5oOt%15m^#H*Wu6rG0}TC zaTd*84y2l^@zv0p*f6!ML@2bTv@A)1FA zb=(TfW%HIY-{WL!8BHeKj~5M8a;PyGLutSo71D@+lt}_@F0*q!r>tcNKJMyr!sJf! zmA+b@aBj96HE9i3^%$bUj%eip=IXNoMgT$O1~6n^wpBr5KPV7V9j3IsLNB_atHm!= zuB5ALyz1`YUKH2&<^B4auAYh{+pxqoHnG%FliOTNj9Edo{B3BGZW~--uxwQ9bX)7` z+*n>HwcWPkcH1Wl!#2@T!K1s)io*iBr6%WX`;NCwM&oVc@k`#u$uMH+Ka#gi6y7Er zg#nYdT`rOHwpkkoqbh;&Q%3tm@pyY!Eo3N|#?x()wv-6Ti zG)*Nh_HXgFyFj~rk3hG-w{dav;Ju#WmvEwKW2F+@+cFWS@PvEAuuHz6C<~T6wh`5y zi=B^^ykX8swg+n^Ccjn)3TOoeGqaBo;}osb1}>BDe+^sP3ugmwIV=9+*wsmdkOPR6 z#Q!?2lU=f7+oOr<0MjuR7D}?JxHi|xikj48 z&#B*`#DE-}J>B#&6ccY|oidwPv%BVxm1$!rza$Nc$|u1!K%x55a~oIOpn!06y99nC z64K^pqiEZgeG!lXd0HsvEJCE+D3*X5_|3~`SF-P{tTB5kwHj4g#N?`Em7G$wm97(% zUb{B_l=-0MoV{2gkkrEUyw>E$((95Z|Fr1_L0qWTjj%4((t+bBE*F5VGc{uPxR4Go zx*{9@xTU{RoUr*rG+=><1wysh!poi8g({!8X0<)$l}ng?W5vDR0D znz#xnj4wpgn>)1`MzFlDM#GbFBS;hAf|&l54FYNb9tuXmMW|Uc z-eb32IRq@X=Xzz=CaIKx6>m#G4Xxg`QhwXz`!QCb5x@vPTB!yZ?WvJ#K@SZDhwq#! z9qI^hzN60oQ;5YqLX~)bttS*rTCX~*W+Fe5w-(~=q9>dCCR9Yz> zp5pUaf{iVuDP@3)ee;>XrbD)}|2AzqV}Jp_P_X&`HlJRJx`?qKC}yPq%ZNHRo%ArL z($KHy&WfNTXu-jQq#Y=uEUZVgm2dAMPEphA8%M_Zh~ak6oAtfg^*=+FH-of7fNb02 z;A9GV@P>At=ib5sjo5;0u|^Y^d(mk&JT`$Zf)SGb#tAO$Fla_nXK2M9YLX*fK^bQh z(US=_{xh)8Qchqj>T%b+0oVl`q4)XZx8Vcj4|Jw1``RCHuLwaqII%4#=PI>O0rx1-6NQb;lpq`EG#WC zJBN$7AvWKMXL2qpbrOt^bo1b3=8%8GYfLjiJqDj?G^TCkky{I+Xq2R`EygtCF937= zwgH*8f|Q}l^r`#SosxUuMrqyB&gG|>MuulhzvxhlUK4gZpplQ;~ zaZ%G#fhl(K2U02m z?}bv>+sc_uz@)#)1~{!Dnt@}{s@z->Y{1I86Y1H_RT24z3ZEM2y?Am)fM3{5a!iE7g2vYMu6|4CVj#E-9QO0qNnDtK=2B|0WuSwhBk$?`*ylQ*szX zo(3VKfj|ZUmOnwgn6IBQ237u>C1_KlTE^8nmvE|G1@;txu-?IZX!BUD()n#!R*$U; z4+5g{G50$<+ z<~$DaNz%oH4qy97;gc-_2-WKvh7V9SzlUX28KmXp@i(&cyl^lHr*fcxvrQ%sDqmry z&{RSFyO=2&nxteW3}HXGmp%J|bD;j~G}50Vlabap<)2oA?h+m5(NA^TXICdov)yLy za26LhCgPFpr>hjN?!`o{yiFx~ERP4ZGU#tk>Hn|OsZS=4BFO!g0E)8vW+;ci-bJhE z{p^1lFJAk`hDQQJ&^OhvZf-E?l*z;jYsiZaC7T{(j7@nzA4SdB)TwNG1x2A_nSv65 zde|bYRM)$2X%d7;bQG;fV!cBs6I!%DkqEe>s=T^j|HdYx=&`fH2qm>Oie4l!&yj$d zGrw6YY{*Ed>f7;kS(R&|6RCUkc`TxH2BIL=w2c0-68w#vPItJ&JK7z;*gUig`LTc^ z0<}ec!cA8au{+qzn*X9$T-h@eHzNwQtIg$`M0XZS-(IrySc`h6ZE6Mi5fcW!ju^Myk7EZz-Dz1_pxI?RkR%$ z`OIA>OE!L!c(Y-0-c|KYN?8wHeg3UAR&}VrU8sEzodbfpU<4ljbE3>W4adv}Y4c7% z1p=SwlUpDY-OnC9G3$I3AHf6*qHW|35WP_~xCAD!cFV?vr-nvb?1N65B{Yj>Ig@0Y z=Kt6oDb?MlTluJjdP%@q2hT-i1~e#)m1@H6Fh}qt(U56q9j|~Ap;&ewS_(#EyWI%> zWN9a|{=|nw< z&7wvLB?tzC%2R=5#6+U+(2Ht6%n}T;@A==bWqohJyxV16Eo$6gD4jC_F@+{SB+9V| zk#3_oJj8zJBwkkb}HZ zfWC|riI+!3!3xKqvK1hU*KQI8%nT7#U$LRH)Oo!JA5%U#&(bzOpT8){UckE zD{v?_yGUn&sZ z%Y?GJPS4);Ie$v`o^NFBO{6lZv*Z{m8S~RUNPT1P?KkVgRSB1veFSrJHLQ)MhC9C1 zJZzcNfm4crn`n6dgsQHMpgdh-sOlPkg0o!>E{6-B{$~hRFJ|zCwipEe;uh6#JO3)T zR(Ms~%1?`%`LLO4$vZN>k69MQKPXFSjveLIP+LXFW_#IIB{9Bu-pPTB`4tB$mbo7Z z?*cIGWKEFXF^N4QAxS5+i=5v`()_Rz@c`Zz=e7Z~#1lP=hUHoBvt3k^VE}hCZ%$bC zJ!X@^S+J8FX@9SeN$^1%TV8O=9}uWkP9Zv+b*$o?;UQlK82a!~Zp zcXKWTD5aG$qam?|z6<>8WnlZeb3Z*>yB^WqP6NrP-ezYwc*AjIcJc{kgyUF_QkC*5 z1d0*=B7wwuAKnFsdCWY4>t$x%YPK0(p#T%5V0pmoPGb8$m;$42F&ycr)~qT3(>B?O4Y z;^8Tz1+w^s8<#oC1w=t!@ghwuFNi!I^KgaZ-$R3vMs)`!(TZ2x-iEh(B#_dmOXz0s zfQsaw1F}oatrD}d>{*aZq{l-*@^TD+npdi{f|4u`17}n03Yy!dEljvxDI_C@pN&rd zE6w&f)*sD}KpCQ;m<`T+St03yDIE(3|G)^%bsL)*+ZPs^_8ur-mq< zY>-1uJx6Qz2l*g+It(f)PZYiL29{Vd$tI7PdgI_B?@4J*lozeDxvDKAZrea2Y1UI# zBdP;Cx|0}&GQOnfw(n@I@HJeeOqS@MZYb#Pgod#_loeOj}gtLz4<1+*yFzx#&wjP>Vv@w{lv^ zl_ObCW3QjRB`>76h-{!OBE?cLxZ}+k2~2{F4!A@OgFbq?(eD|Vq~}Y3j5cL;(3dP; z8H$6)_pGcD^i@Z?*cRx0V+#CB#5gj3sbk^mv_%K9mTi^cgkYE)!s^;u@!PX%L;_tgFAO+9lpPZn7isNW z34{DsY@4o~0X5T5uqFX**#PKTj#3MoL0SWU$-9r($ed2OI|u%Al*rDcCkoCN%~#}7 zV4zz}O_1q`hE%xDq)8+o8n^f+KA11SNM{S>1G3Fx(lpe11yN@++$lT#YKGrWt&F#6 zOgb@7t7Jm5W3pu$*PGFZv^Y-BSB|SBSw^w3?1OZhWeO$)4 z!I+^k!pHv#wbgLab!?Qv?Z^*`K;;NkNYyI72c;SWW7(gzvTyggBb~&coe9di%3H>= zt;-R(v`IDGXG7k|j|a~uyB;hU55JlbhbzC(qXzC95awFPFuTkeMg7_*znTg^HRa1( zWAWN+5tv~5HCx-}p!tLQcEdZX_Kh;rs&P7Rr#gHa`45hZ=Wj2?PKjwc?_sg|z(CzaLD=|h}0w_qpbTpgRcWy*n6~73>;RZ#%n}67{EsL1G3|wY%-T4$e$llK6IkwmZl1j6VFVmL?gD%6G);uB}uQU#tGLW2I`Zz)m-7JAqo~wbTG1_gJWE^927gIs6U5p zc@4Cf%gs|E6Pt>Qr)S0(fY>EXJQi9BfrTHs%5HY*`I;(bJnkOL>YHU)#Ci%rmSk!} z(^N>f;5x<_h_-mjg^X42LhK+wJqM*5ea<$STHwL#j}g0%FciAPUlpzZOS&3j!GH+b z042dVkpM~}Lpms^Row8f>EoI4YdTkU-GytOWRoAP9`Rp8!fB{s6{*_5ckY=|0v1!_ zLgjknLdufn2)Awq%rK)AwNBde@up_{#Pw>Q3<^#(ZP6x#mCX3-ALnq6o?cC4dQqG& z?0B=fzhg$jS_|{+IpoZ#-HeUzD$Jav!s6JvkW6uMLCOE zf-W+?Po~75mmDl-pqTnRL1dlZyl65S7_TD-1`8$=ag9&5{{h9x=qZZ;bh?WQ)dZAI zwPgxgB`LRdn9oSxLdi4fC2o{pQooHPNT2D8>wL6Ty7z) z0N%X(d9%s=&31Xm)#n#V_3vO+WQFP8al>V~3@HcTuow-& z#1a${#9u?l8rpL2y|>&ucnXFJSPBzJpvW^31RI{F>lo?tGu{oB&OlFqA~pGr?yY>fuunm@Ysbo#_tr0*~< zPi-x@qM&a_<_^OieTQ+91?8|knUv6)JB-nH7%80{^X5Cu6lU+Wj(T);te##Id52lP zhQaV1#`S@Ld6CXo!qb9|?dPrN`^y5@Y*;OwZ%$creC<6^V2GN$kTrpJz3wkxFU||!U!Kz!=G2C*A)%DF^Hi6^g@gmf_m?IXLoa@sYy|Di z{blI;OCJy~JhvZVM$);z2z`H1E?=hfzQ0htSdrwf&&AZDq8jVU{a~CAG zM)f+PT4F)L;$oU#e0Nb!XezpkQz3*;$J||9e0ND3`?9)`qzB5~++A9HcWHh(>ogRI z%McKvp4TouuU)!J+jME0E-gDidroFu5~?rgdrJwPKTXDb@Nibly#?rdivyNOolCy1 zDzmw_u=w60rE9T!``$8b*_+W)HKWC<<*8+g!uJ-cWeZYoQA(#RNuMCF?=3>FTUsTS zR*8j$rL(};uzcX11uH4UqHKO=S-ze-%T%kQ*Xr!S*}djn!nE~?N{}+i3KBE&ai)ZB z?h=;&^IZZ}^GkyB2lKmxrvxb5M8Xi;L;{h79tlA(-z5l5CD?Qc$>vii75=#KT`1fm z6IPK~J-rqRU%m^Y!l-bikA{$qF|!#;I()vbWT39cUKL+e9NOGhR(xL(v2~+$WTpf+ zH+P|m@4}>yFsKR2ccGAwhy}7j#sXP^u5_(9U-y+~la*;|zGB1Hlt2X$swy2(7tuMz z_mv+ftRm1T6MJrRU+MY20_TIr(gz2RmeAZ+ke=uJigG#igU0t2suimldNcOmUaQdA zz2;qIx{NM0S3zQH0&t__2BbFxN)o&CU4?B-PKjbDhYPv6t1NZCt5EH|D5`41(WI0! zcNJ#xyGm0!RLlBEhIoZ;H38E9*n+;??EY@-H)N~!D(<$FIc7bEAK(8 zr`N%tgzrJkfl;!iu7s zd(h@_z6X=RA1uBHgG4$FNT$<(P+-?<=L4mzj8t==4O^qeLHC%@d<;Fe*qiUbB42MSHikPBi|2erdw?!dn9K;?1@NCmXg`sr!y!M*;`*}d++=~whi z^B)phbfil&+G`%gK65@*UML`-R1bN@y8{<}dNkMn5`Cy3G9e|z75&99#m z@B8ohgGT*^qJBibo?p=G{u82NWvDbyVX`$A2x8)~y*Y&}NofvX51J%M=}gjN?kQg> zc0$O~x=P$39hrMdR7#g($M!vCnzE;dQmXV&idD)}N>9;9DHH+j|>s+r8#}Pw944lW4K$V=c*q&8lg>`@YwJ$euv*jzGzn`(D1? z_f3UdvO}Uo$zC#T?wjf4`)+*;HoBwI5Ag-!~b zA)*^jb>CqEH0zw|o>Q5%xE!*t(aDNG03X~9)mR!_XcOE~@x zf5Do+QMQJJRg&D!S(6DKNanu3B1k&wkz@k}6m9OW-1m37W6MhikPsbNG3E}(eTS1i zK{zx_6yzlf0_|^?bANxI&)3Sx`-_!i3PUSfGkao$IrrD+;l95@ud?a;yR1=}`#Uv5 zZ^jrfxw}gy-(9NamlH_4(APii(E9T$?=Df#Yu#O; z_oqmWpRXlYck|o#wyA<8FznP?c*$kWz5R02!4O^+R1G9hJm%g?zqe94J087fgSJY~ z>gl!Cyth=ZZCWHvflpb4iISaB)VxFkf#ICkakm!(Xzr|%@2pml!>N=~vc;Ht+xERp z`UG8-_}&V^-&>*(uw-e_uGjkYi?eUYkhP>961MMbk`?NEs$c;lik8`mxwA;W_MKHO zr=C1()c`$J>sP*N-gVd05m8No2N^TRSken1d}`mdQa8VxO|*u3pocsl(ps;~oIN{b zQJdSG_I+)NsTkVP7nj_jHTO08(eJC2&Ms~bsUBC}*D9pdYpq|JnurDjY>fw`rxgyk zGqmTNOhpkg>oXEfBWm!I}gT5?t79<~5B%ai)i6w2_frWpWQ11xYwdCT`SdkSTzU$H->v2%p%qjmyoY zF&bq6$g&3y#Z{=8MI+L~Sl8N*h9-pdEboWL=wU6uk5gSvP!wjF9mBZl*Th8&o}a+k@F*sQI?UiEX#5eAvi7B#-OL#qg;3ct|W$k?VoiQ3^4>(AqQR^JgfD-4!j5^VW zuG07K{HyV$Gz0vq0wB^0GhrRdSd+)tBa__zO-h0#R zz4w-|cg%6`z4zXG@9jaBWm%SGS(as4mStI%Wmz^FjYgxQrl1hq_vtr&}zc*5rH_hHNGe)CPo{{%9?!EWkd+!`52tqDGb_f5fvU%`iG=_9^ETdd#`d(69>WxuZr2;PA;0^lhiiYhDf(h~a! zu{8qVE02*hhzWeY_hKL6z}aRnQASD$_TA%}@B(S^6f2^;wA9$w%WK7au8BLB&;4GR)mBLJ3H;57o^ zFfcZc`C5Um8HlPr3W-4Ch1UX+Sb>;Ad|QaB>Os}Vn4#)I>?D*=gY+?c1F_Ta814o| zD84HE4L%?TS}d5BPg6|6Zv_t9SA!}=26P}6Kp$W3fha;BUminDfIhxFgg(ApgticZ zh$L=(d^rfBkvOa&a#rT$L9T%a0eyTq1>y*OeEE@B0eyVA1mXy)hvEl)e2YY`xeT$d zPb3o6|3K`T*n!_;8xV-S15*S56jxye>3=|6!SPYx0q4M9p!$VPzrbk_>V=mkTch5= zT`~1w{Gd`Yul(gpgJ3xjSq{X0d=7jKim>&O0~f(^AbP-$ef`!0t_okb7yx2y6M&)_ z*o)|jByeS0b8aqJT1TRBOgLLfIU~QAC{7hzL~~C)asP7R+k}{DMD)0MpV#{L^G<4*6qCaAMEty$2C)o?*wPo3T z{J7{rF~v=&veMsbQFV)b=#=`l*!jP5O1AcpjC@?pFOSh6SoRNfs2ci}_YPK1uRAO| zOug^EHzB8i}-FYYs3(2#=jCD2y$( z??|1fu`eZ{7gbAhN3MNGB0vo&h7vqD8|d7TYTuE{C2~sZJCX`%wRvRpHjm7n44jPJ z>n<~WEfak$BZI`&kbo-k*5jGfBh;7nUFN7s5skUIEg5HX0E$3$zn58l^t%idObuNS2D(eg4DAv`J+EbaWP)CI8KKuQHm_xpulbS5)<~cY zDasBLb10mSN`7#z%+$=HPUe0@`+gL_kR(<^H>AtHxgU>GI=dU+kJIB`lw(yCtEbn; z_?>6N9Aj7jIuQ}NJc@?)1otzn$Nx)rAbCQ_MXfswYeLi-;HTWM?j=4cE*zC zel+`jO!@@9J_@})65$!i@P6gp2<_^|FQXHL@5bkABlESf`I;MT*cvs@MHPyu6GrTy zW#5ewl6K~*u1=td&D}`$-6-6;ic@j3guyg-wxtx0O-AHA$n$w%xgR}eKJBR6O z4pA5BYt8|Qtx>TtEh38h(SitvA$4#*m^OIYZp@v-k^_|kq&Pu6uGErubLYV1(EM`h zeCP0d%>gRqFeK$b#G!{n(Cf}Y=rsqM*Bp|s`35Fi)57yc1PkXTii@%D8_1Mg>_GXF zl;*xc?E8ieE1LXZp5URvXYL!qzHg9j0Allf!-l=I4OMAYPp>y@n0nneP`%!O)Hf(! z^Oz`GBSH@;o4Y46hy*#m$GmNDYHw6rYtXsJAo(6Mr9%N@?u~cbV-qmyy_-VA_}wU)Bi~Q}fq%S@~M+iNdRd z7Lca7%U<8*8$Br0?9OV~;F!B?2jb--}O`rHmAJJx=6sGWR0a_o9@p zXeai)I4$lCDOL@!dU{G$6a3zGqD^=b1&L7zZa7}y(m;9x+0QYLAzcPzlhG6@5JXOGB>gLniFl< znwTU@a!Epna4cngC+0vXN=VO%H3l|!B9ZUJ4780!?e?6gwauM4xX!;kM8q#C*g4SO>*RL!t@ zdaYskTEmpgcMV+w(rXRM*E~a%ts#Ml6>Cmz$nCgdufAuH&gu9tgIwM4%{>F%`}m$= ziUFK7B1tit7)hJE2CMHHl0HFA-!%wnAcB7~kikFDuGbm9GB{uN3{O}n1G8snzUCQh z*c!F3Du@`3CNH-gbC2&ClJgR;th_QdBT43-!RJ{X-!ljaG2?riQN$)S_Y76vGboo+ zM&C0~Ww6rI)7yi4je+hp20P<-3{%)93fo9ua|}pqO^2pdlr*0vNwJygI|k*87A~Sr zL-tI}9m9_A7^rFx#72}GCvjjc=8i$fcMQ!h49*zeF+8DZ7>3e_dR})7J{UNB#~{TZ z#c+Yi)*K*6bL*)pT9lXL`vsM+2#%7mp*5W*bH6b4{lW`+lvmTUuc+AEFW~roK}y#z zOfT%cS{JHbtqWEco~AZ@zd-eM0qKHLYGL|1=@TUM{esX1q9_YwQP8f}7QQSvW4>E> zQdwZWel6IrH9JR1Dz1ss1Vrk)g`@(ebaWNr!09rZy9J-9?-qpc)~w#Hikx7~-NN#_ z1?6(;El^po8qyougL_?pu7aKXy}}eJiX?r_DJ^?9pzlqEq4$QUr&sW)5cIlN5PFjp zY+k<=lCL=hCR-yT>^qoSp$6Xf`9z17-qj$Y<^p zXndz2rL*f4rh9G_s@$xeUh__2Nnz?W?-Z!$C?LIdDJWm_VU(@m;KP@+I)TE=ZpQZ^ z4ZP;$Oh}zKI&&W?zYm{gK0;0|n3QlacM4A5DI|S@UN^r}5TZbIWJq=d+Vy()HFWat z!{>9zd^TV6p$%K3B56ML$jWdmxsdUFcw@;08Ixx!gJ|x;ZYtj$Z`1MmC3FAT%) z`@SxCMW%H)GT^Y5=JzNsWerF&Wdl)y`uBa`H;(t-FJIHq2k-YNeB-khhG7{l$e75& z;uWz)HjKg@BwVWF^v zK^D}tL*RJvBeArz^kWAb5iA=#=2$kJdX-npreNRs2jB`vwS+-X%;nfn5yJ+Mp*o@? zT}5q9Gjg;-(>~N10y}vsO5o&8)#mku3-tmTnk$6p)v;;)%&H3vK&>ioYh0F9N3bo1 zsIWhlW!F`^Il~XVJlkXj)L} zV6nZp(Noc+T|vZ#(7hABpnVuEq3g8unzv4OLY8IEvU*xvfP|ZGuFh*y2J>|gC!C?{ zj&P?u%o~kt+S6Jee?Kf1v6`3}5U`L=B{>dDLC&Q&rX(km)Y;VZvg^yTY!eSL8CY)1 zAv4>mcvkGEwbm;;g5E8!zzAzw96=vf^-CAk4##p0$Br05gW#OyT)#*pz6?o3&Llgh zl2O~bY}SkGYJK6zsO#wMH6J1c;ccsqQ2XTgkOW1k;VsQ<3L`|~brtJSv;>4vk&%7G=Cdr{P0u=8^l+i$g6)M5p&=o=XE_5#3{5@DN9cLh z31i8#+ypq!I%AwY%U7GEk9x)e)a(ih(yiAJ7p=8s*$ynst`kVI1TvC@Mz_QBYziE6 zJnG}BdT?Qp$Fi$1#?g$o=o2TZ9W!@?k5CQ4b+vY~xOzQYSDl}yBNJaxS{)%#meoIT zN493!mE~ecn1U_;0Se)#$neMy7NO7!t4InT6nkB@x{8Ft6+fE$Rb4g=itAlp*9AcZ z*K4g=mStHlqu>hHvfqKP^Ir2-J5v;StpaJ-x=-b^9E5hOPp8*gukAX5%}k1~&K{fB zI|Nw5S9Rf6*=j(8HcTa9<*kBRfj3+Q6s$s8slxf-t87$vZ4}EAx!W&uOKjk1?hu1( zlW9~uz$h;_-uR7Ss3r~+EMKhEk8q2^O(PQLx=?)Ya+je5c9@`WeGT1cGP~}~t7$}l zyztdDB2h0I%%;|MCRYSW=LCtWB2!FBU<6U&)0z;f+l5(oyD$vH>>80JINp5kHtE-I z_%{+KIetr+_lDs(jyW8|afTISMrcIZ-o8q#5vh97R*tY{AXtL)V#iimjiWXvT4w^L zTYcR?L)Ryt%zq*pCY(N4>0no)zVrsepoeSp_^$XCq9=;M*|FLL0&!XatD zU$>mdS%7wx6N4--3u=Dbn}2U?{QJJgnB!U*+PlKL2*W@Q1B;wSjFA7zUD9rpJmwhl z>e`ULcVG45I`W;j@ps)&dUjoRzr_6(onrpF?wXm5cjMW0UH5g@%zA#m%9ex0DrwPZ z(awl|{3;ub9uh1GaZ57$lt8sML9(7Gk%M7##nYD|+0?^ng2IIwpE?a9WqZPdDtQ+r zNs61>4~Er#9_B9a8^1BUH*-$LZ|*YOd&BQ)BbE2VwVyn$9`}igRzI@r%J<5JBmi6T z#6^vs=ha?TQe4%Ujv#I<6paT=k?<t?=pFZ&#P66kDR|?zZK^;gMh9!?sOqS<}ufchOu8L5(>UnF<&Ja&rnHdhVFn zC(o`y5nyNt-`9HW3FOxg?v1S+8CFbFCoHh^Tnw+SN#T6Tt7}nHp0dWg3u`>g+YQHj zN^uukeO!SoQv|WmW&?_yE~$dcA;6)FaK{z$TG~@a32S5XmNr(l<&Bh~TXY##c$Mu2 z+`GKWwtUe>vDyPZSe94WqS~U;TEIR@NuDN%pic9~`;GLjNgb_4yr7Dda7oSX-TK8> z)qH~ZOzAw}hB2T;1<|hbMl)(*;UcpcF_y->yo%=64yZ+Y0i&US0@ScX!jyeqw13|q zykQnF_i8*Ds#DUPHRH1*!LzGZd6n&K89Rlqdgijf*L~ghU42B!qA}Q%SJ|S>UwM@+ zy2Z};sWB~g)mMb-p#X2O1OJWTc*8|jh2NqDn7`a*S2&@{wS^hAWka;B`huX3EvvHX z2Yp;wE&&v_~MX)j|5Aosn`o%Sz&x7zN9*S)#$#qZ6B+nx7@U#_0-$C-Csw~6HIUG6dzp+#um zt6p3-UR!tZ3d2PI<^2N!upq58x@J|vb7U)SR)MbyUv+tP`059|$Gkf8Dhty37Q5y` zmqg58m?L5T@I3_}XiaQ)%h#5Dxf*O^wv}6HSnjGTjc(;unwPsQr_)CgE`VFtUEdhE z28;Y9x|}!y3~TMWTDy7&Z(m;xN;w)|Boy)s>dHm1EyVR-hz58e@dYl`-a?ySI}DZb3C2RP(|}N+*pD9g z1`5SK#PC3_s=)_xSFK?|0`gW3;YEa1*b)QX(xPFI^s)WlOEE5?dRVy$b;`>|vx!+m z)gr~OFh#-_v@k?M7#|Hnmq-R_5G`Lhj1UZx24R6MzSw_cY$6I`S!xgg;45dL(jY$I0Xn3%k1QRaB-L0` zZOt>I>*47-0zzuj@@lw0H)Z_+UzNxaj3pjoQ*C2&9Q`;64x|q6b}C*4@q*@QjYE>FlN%e41v!gt7t$-q#Fgi#A~?RQZG@9bCKBw%uym z!Yl2D)s}U6e_x{dzD3R5t_h$~psqA4-+J#^b?@D<5AR;#l)d9$9H)I8$8ntU0)HEa zY_~2cMKd!VG20Hey2+k;=vO$5Ul`tFL`U|!Pl)Y!k|U+ILdg7{{kufoJC5V`&55$2 z4lK)U$JrdW(2rwi&d$ecX5YDwAoND+U1CCu&aUkHYNHqAU3V2)kyfT5Tfb5Rilc*s z%@YWWIH(&*AeV!n25ilUt)DhGoGB2g+2yOxs${N(VGY-m)262$H%~MqWbc#=n+Llv z8qZm9%*y!-@k8TTfyOg~P57$2EU$H6_kG!wSJ%p#Fn{ihZ;!9Cc#~;vnnC zE(u;4bV>V^0;5v9pv!Qrtj7mmjn?(dsWWyA$9voOy$*g3sPvY&v-HNS(>?)TW zE!UBj3medvZhEPyMO-uM51CaIkj_?+8dPpgY0j8j38*JiLobSKh;6N45E)x3a!5VF z2z#nbRE%9QUG$}a&9L*~?F0B#L#YHdcX}z5Tp3A`>@)>;qyWIQF!vfSn zn`ytkhuC2PqhX8Z2AV_zs&OTd=m1f3!qZ}QIRm0Q(G)WX1Cm6=2`Ce$)=LdY#ZP`! zjRlimc~y-C)X4W^h+kDS8b9oO=+xoOEB~M}yV@djBz!1YK4Ee7z~N?L-@w1Fn-fPV zbd->4YrA;hc<;S;497R{4czLmY|C{6!&`C8#BI60^P8>jA-4y7^xFx+9?^O>0YnyUB6RuGVnP%ZzNRY&w_SHdNM(bTzwL?5)~z3BR(c z>b}}ZJj+GsbzNV`D)r%!Wmk-vyk$XgJ|VJ1mQm^Cy*&*<>42|pow#f1S|AI1@2&D< zd36g#7>jtrD(x1cb#!&6Lu-cH=EFNr!d5bQWLXZSQXp>ma#HS8*tIBhE_(#n##kyZ zU+nV5@yn}gQOsV#t6NDTAarGac2h217{0KZQ$0WaLvxYbfRX&*B1%B5boIN2-H+Mi zL=(qO7LPZk9kI3*Sxq50N-&j(+=yrsR5-|i65@f9r}s7l3@(WSE-hMzrHjCvOK^i#ofOi- zDx3%N<4g~ce12D%Dps-!1|@1V=c;Vzlx>g{?@!JzW@DmX%*4h|!4~KT$FNFl^eVxC zp}35kT)r5oD31AY=9x{GSJ!@2Jn+>cIyWmv7&Ot2q*e)6(H;(ky}K3|$}CArrDzR!|(nMV4S!HFi9!y5`M%!K8gL2eU)O z?u)PAuRdQi;T#$y$vV@~OM!Ou!wqU?L~INNncTcy{mO-F3t0d3n-a%&AIEVV!!QiP zalEwyBZgu4C>8oRj^lXmy;rWFz^%wZw`Jo4rn?XqdHe0X_h#F1vK!_+-mTJa_*sO{ zWf&JR`{1k3B1U2@O-B?33D6R?9ad1BT2?zkN!Wb!V9P1fY&l&dYV-ju(Xq&i@uTIj zy3rj&l0SGpa&46rECd#_o2|jZe8WOu7=HfdEu5i?sIwTPdsC6K24w2@|wSZmzB2 zq^G6K$7XmH7Q?*$$6Bx*@G0RXmhC?ToCYoF*7eroNw6 zDU8+q?!8HYMJ_-emPr%~>+lhm1p)fO>I*9-CW@P`8+18Zz-u{(-l@F@zFPJMjcs9sV$T;j3g!?3_rs zT3SOSysB#M^0jr_qJ1q$CX68vRW{l1Wt&nGV@_AYx?f1N#L%O4DeK8<77ffb5AMH%t+B&7f+0!HVKAd z_{XI(R;HKLBAL3Wh#9f^wGk=diWP|xbP;x; zurG-+*W`smu1y;OvPEyA6WMmg!Z+seCXc;v^2hSl&~P zPUwLXZ|$z__dz{!=Y;O15q{kznTW*zP>a~XkPCVcZaGobvCL%EMdkH~I|#9w2$7q? zf>eRfb>%g+GFNkJZtzjZE3lHwM1Cyj^PWsmLyrAHP-_a01ZgU*s!Q@BngK@DGFjBhC&#OVJHL;LVzN~D8mqR zSpZg0bQdVn%~udMcT8#^U3%(0Xrj-;=7K_HSb~x>QAk&&&is;N&VAPlC*e`w%NERq zM>sTkm<4fI6?~caoC$iiqc^9enTRD^rnN!ty@i~sCe>Pk^h+P|q}&-$%Wuae==CS0 zxJ3raZ$N~tI+t7s57$R7DmG|EWtJy;z}t~vGOJKW#f5~lQu%I?vYd)-QMcG*bi zg*hOkn2u$j>4CMHo<_TpCWnY{Kk(_Q&Kc4gWWjTka0?jYL?cvORbXADXi70pqhS^H z@`|+YRzhJ@Ssxn2`Hh?#vY*R$I-%Wwkne(x3Kk|e&R;m@pCRyJfir^jk`e@LE_3=<|a5)M}o@?fyBp zySbHP!3e&D^p{hQQN=D-+P%bl{HwSEbq&?iuV_O6{~e{ zU%v=g9^_aZju?gDWvW-q20B(Z?bhZ+^>tGG?mU6{bUY;tZxsf+A7twB$ z65H6#)i|iilGe zd;jrt!zd_dKwC^6q=D8SGAL!nHt{az(|~*#Mr@(epIF?Xh_3Hc`GI!CqHb;V`!$Af zr9U7r6(Mj6otMeOCO5!7AedK~_4-L^8_r8qL)m^Qy8h@NYQ4!|DIB2L8s_Tn7 zA|ml8g3Nk*72rTT#sla}P| z_Er1u0M9b)`1N<NB>#7r z|0d*&Se<#-ev@X*QPEmL!^AncqlDQeSbX)oaa;5#>Ip@e=1uaYgV6D2XE zgQcXOT*pmp-_(RS%2SrlWm^@IBuU*G17spQBVr>i_R)7iAObn)#>(${P^?%yfG6nY z*kCdc&_-zHtCe>CnofMF=k~=?xl$lnJGqk^szGD>CY^m>M}wy97{qv8)<7BJB)A~j zDqKFf2=a?$Iyl0=TR+Q@(ZRC9rIY>EId?Ew1)YqdOhy)}J+tgY!6(|l`eK5UxEmyh z&M<&Tpj)%AU{*h!x2>4$g<$-T5r5>*TvV`au{FrnR}+6P^x$xwwC2rFgA;ICXINag zgY4V&jT|kodLv`Rkt!M+8DlxAnrIcndY?vPmE}o`3nDLEn;i?o$QY3(e(CPvNnz1! zc~`&Lm-t#k#WOe*l`%=JEUfpx=2uvSb;WE?HM>QYP6bi(l zCgpxEP4pAe9wZbmcRSB;J^9a*kr4!j3`}Pc4N5~HoFh+DDl+BKIs2^G0Gqv>&c+X> z;^had#YJAtmWPs%45KTeo-r2|G)8GAT|=sw*J;gi&?e-;4f{*D!19a_zskLm?teaX zUJ39QUn}Rqm0}1{1dY+a(ZzhF+|I<_e&Vy7!(7=T?a&z@bxDl2^XukslX1HQP4fI- z#Sdfw>zzOkQJ{np7^mm8g8V!$S8Oj%sZS6Mlx~{)UIfN+dLwea6@ee)A5meZ5Y3l$ zHXO6LuTCfjypcqsa*9?q!4S?c3 zU<9J1lmE(ok*D(%pqNd-gI6($fl~wJ6mfL&AR+3rYXtbVvyL`U582gf9V{`9u{Hsx zysiNFUO18cVMIeRK#QNX3Ej18_csA~x$=gO$|i~O94Rh3eX-9;N&d^cTGh(K0B^ZN z%Zcj^h+`;MYnhOoo}<4b6;pXGi6+n)>Q_>EQZ4}oLpb51Yxu=6Dnhq_#mn}x2%>b1 zWT1Bt29_N1FqhIpkHf7RHO66|{7-$w2G^zoMTI~PmP_Y}kq2l_8AC9-!c+2b*96J4 zHhgup5rj%7noQj-w)v_U`@`CdH#qG(!;5EzN;aJ~_+Z51lS=U$fK=Rb^$-xv6&*}5ucs)6% z-(T6b3JCRVd3?hE@#;Q+k-UuI4I}ikM4*d1mqz`b{Ky@|=mlV_qO534n5DjHN0g?( zd(iruZDnD>v<9gr2&4%G(28a3xn|Uwr59HgMeRf{UNb80-{!qDsHP6AYAlCKS^_Ct z_W9R{9i!Q#0bfxcsjJz3`>^ulOj8ySrhAdt=7Kb$@a~sqz?fl^C(8hCF`Jvw3_psE zJbr?j*IP$i^b7pig9>;&6sxKr59mZ79_v#eQXr^nFt^eRg8j~+)TxGA0SvSQZTO#D z3QjUMLf#^_#fiL?H)i==l=m7PC(*l(bdA}Sb8ksAV*|{sW3Z&Ed%hX*e7Akxmsl=( zS2Z;gLIMB71DKs!SzN_sCqhWzk@7FbN_NFW zSV~kD#Axerd{Cg%istWvqZrJ|TmQkLVV}!pbs6L(W0@yD3-Qvd)Hs6Z zH5`Pq{o-5Sn#q` zha4At+&=Mtjmf_S3R*Z^iJq{%XrwfXh!B!z zG%12s$ZMYbw#~t!{`bJ?FaGuLOy!du6d6%Z_J2Uu|EPe|B_?RrNrF1`4$c0@V6xA9FTgodI zz57lJyqVr-NYd-z{q>bnvx+IdETNux3IQFF4e0+NTI|V|&{nzw0k4HnmSWuf{k`@| zx6#afbdO&A?cJUI40gdb40-|6-}NZ2F=9Uk551e6%Zmb$m|U3vh(Rd&G6g9}4f*+x z2!wn~m6?kmcwG#<37(P)?(W0aYeUL^a49k`(LwvLbJvUqw>+`zkXak^yM6k2B61B2 zpG`1%+c#;FNtj^>z>CJj&BXi*KPM=7J!;fiCC(EY1~d&G{xrnF`J)>-&!hXK+;g5d zrBt#@)uS_B0M`YS6>!*tNxP7t7i@45R9Q$2t@*DyIe$F>tOlfYm{&q3Z<|r z#vMw*`n;3(AZ|I=;0h{Pb>hXn{EBM)n^^%dEzCQ2h@)gfCA<_z>dJ@^pq?Ekk4s%Om0Vx%W=;fR5&5P*HX+ z{=8z8D&Rb zvy{P=^+4Becwf?%OoJ1}_e~nzqNKAs(O-#=_C@oja>FtzSNBdZZ&5;ba|+HtG%UJC zV-j#%KFB(%S0wXewXvlQ^m+^`Nqm(KZ&(5sE5h&{_5iRM07J$IMV0_ZRt#G+RG^oQ zV6+AHu~}2%lWjOriHsa5sqG?kh=uq8Nh0)lRCN$mjp5&{;8!BEAL!qCmF+U#e_+5vE3tE6}_yK4=D zX9@$|fE1)y8sW&Mq@jC*{5nl$%C-e^Vw0qql>~Jek9~Q{l)N8eApuGG-yl9xzXR~% zd!%@w{Ti`6kckT#wm2ZaMe2V1%x){IQl3tX%RoRs2eEBOL+FPVX=xfzEx0fdA+~;_ zpZ^ixPG%}?+8?M4oRNso8@y59mylnZg-V%rKrUQv6fbylm4J*0(ZP*r0-60I+(vEm z=^y4J)q8=+;J!o%zG;ne`vG`J{SL&7uST(jx9e>Kq!b6T!BT*t6a*rjM#0B}7=R_o z9v+QiDmNxO4Mb@Sc*sHcjJh8OeA~=u%Gv^QW4fdn!L_SxV`vHk-a!{2S?ZvPT2gq= zGq)f;%_2vjsXE1sTrgt41KnOpVRWQR*PiK8*qHbs$Eac-WG@xha^2$U_Q4eE>rg22g?9KDF;CmNkhYdeE~j&uM|qqMuQKF*AD z99*hm@byI->|o`oEU^`snCO6Zk}4SW&`yko2QCAH5+BSaQjJg&TeMq(mDdiC4-Zl` zDtJD$B?Bw}&IZ~bV`&5>4=Zd%2(NrvddL-Zp>}PiM^v z(y!VltSbuG2V8)ml!G9=StDq(Hfb~rxD4(~c;Fi~D;T|~P0r>5H2AXbB!pgLVpm&C z(Lu5(D||@I$DpJ$1someP+KF6NB^cu+jaq>A;ek{xzE2oRI2uaiCW?BV*y)}hXs%T z>zYD2&u+8O4_y{7I2^i^9|Ws7&6~k*HutPxj)DJECZYkNU#3--a}cLUC=KTZcZpl& zjdbiGEit2@VZUtg7F0RRtMC!K$y$10mEs(v>8Xy;-2T)Ennd^k4G2`Qo$ECuUa&!rDkCGDOhz z9*jYw0&Yd~@$0L-_sn*-g-lDFYm1kNxFhz(0et0ufd+f9e)RegOq(S@#TK=oHR};zQvc8s$~*U!VFyp(yQ>}>79#2j9i8#{_{X7`Qp4W zh-ySi9$AGC!`=wdah5#?ojP1YVJ)C(tTpsD`jEEYgH2Pi9r~z12pFk!HTcMuT+pK9 zOl#qj+r4CGJ&xMpWRyB%AnUQkX!3n#+;Cer&;(N z7z@w{q_|kb6a)OhNEIqI+FF4NWeodQ9F?rqo-&^J$akt%!H$2jBPeHMuNTHaCoD_Z zl}S(f1#bTzzG-bgFWU5EU-btm) zFg9KwR5wXfs%DB=h?m_+WQN`m{|On&tNnQmp#nfgEcDmXPeTMUbixj>itzx@JeJa6 zVC&jE+y(apDL>R7i%gP3q?Yr+=A;P_TqLXs+=#QQIup=aNwS$hx(0kYE8+>oO&g3P zxf$W^QOG+xW{IXZ?n`1VV@xIlqy{qJit?o9^BvqQVNy2*iyuljfJrthGrc82NldxD zOiTYe&A)UUd8^Mk+%Ky_RXs?q?Rq4hRfR~v0OnpL5C;UjzkJ;Xv@%32l%If3;OIre zizBq6f<-N44fq;V-UV0xRI;WYg}S}^NyXlBV#>#6#9@dvd36JTP`(u1n*So5rGmtK zhhDea!t=TGj8~|1a|S+X6!n{sTClN0oJkb%9_|=B3%A1C2`ibvhRzZWKMSkmcusON zrStYb$}xi$d=N6jE*tS%i%Tgv$u_Bpi7qn}S;4W=0M{6faquZj!V{t!VkD$rtq=^{ zhop_SVv6kUV{zqEGR7Q@y$A{7DttTyb)|aCvl`L?S)eC@hab~OC=g`!VvTw*A^P~X zAX$@Vrpw)|jVQF_=4jAl#;<|bQV8`o+4%NWAa5 zI$1@$4g|{!&f%tCZJ1f^p3Pb;#>M#CwJdAK4a&V~ORFsi?Fqf`K6iEou`$lu9ujz+ z!wUq%#GUbmr*RomXZ6PBRpw)sqOP+k$%tX>MI-6xwqWA>pAbRFSM*u7A*;L*U-Ycj z9{7$w_|c&*F-)6%ZYQ7C%kNtBR1ppbiVO5m`*Az5LTz6FPvK8U#73OQ@f3q`=W=H4`8?zwO+z8v4x4;Q=XB$*+&(KA)x9UQaap#WyU^EyL6o}YQ zHXyV^_xROJTBd%0^{na!I6PFhpe8+O*)E;9$4C=P;kra|?1K3Re7sFFtk{wY`k;OK zE82co#XSSU?SWRo4y?PnY4pBeS z=?pRAmWYAAJR&F>`&LRd(%$&b3oHoAeLo+jL=TL%4=GX&8gm%BKZga>>dYpcP3b~( za4upJR!sRXWHxsbQHhQ2@z7E?dLCNQfZ`(Ylyo1Dg$FR+>R? zRbl|GWVCj)4-&m=xeu*X3eI!zo=%VjL@XwDK@^69MBC;KyRB|Sa~I4BKGvbfiITWN zm2ad2Q4!kskFk5=)*A+TMhbYem-0U-Gik_@{vp~YWXAk%8AJ(F<@tC_a74+)y+MZ(zx zo2mFLf3sJb?hnd%Hn1+I#(-x8VoP+i^KSbhc%R~=vny-1^63U(SVWM*I%M3gKMxm@ zhWQ*+&I4?Zc%dZ}_}2iLhTI#G{Kcv?&I3|rbn@4k7|AzGE<`KTT>ROr_16$qn%U|t;o9{J6G zQZa7!?$8f-3H>Hux=<6;y{-zJ4+B6<(YIMR#`KhlGO4=$o&rQ@Gv)cGw52x-c2&V8 z2Wj`_oIqd10wO8XWIs(AARP+qf8b-27!8gmF}ildg5YXZj~r%a1V%4H zMcpLkvNha#J9(5$kY3GMkoZ=Tw#S}j0V15AgInv4kk66bfbK$IKc$V9W)(g@r;oS? z7>!djnWLy?sw*M$&-E7pJ4k~oKO$DAi9@3q+D`C;P{hnIeK^Ln*@QvOe3{&WhdL;e zvd^X!y&etMAp#NFaxCv@k>JX7zDNw;Ej{CltW=zZVnpun)Aqu^a1&_fqNoa5N}zUU z+?2PbG*2)Sbn&VLGdS{c9K#asuaU6(=<03EvVz)l7)iQI`A)4wL)H|h5!D*|DApxS z(119+sN||8k}c76Fa2BL6zDj0k>sEi=)HwCX3oRX_C8z6{IYa3-v_2pF!N5)4qrLl!UP{X(bt1B5bbpG!7I$@KLaH4Euzq-d~V%(>u;h)ro z%e1h$Cj+?Kc6}M`ghjO*`_(dX!aK4NmY7)eIfb1yf-^M9;PUeBUg;#N)8Ss{3HtgK zC}sX6{fHJ@9qYk%n^#F8L|lNY#2z5@y&?6NTQ)di0Qaq}-zKH=RB(wGC_u@k^;`4j zMhN3qBr~}G z!2!W#vugvTS4$&WETt2B@b4_$t zx?#^IJmv+=2lztB3;y7|Kv+3*nIQ6X&F+6nF}oa078CrN@*ZwaUylYLZ~`%5P8~>8 zq#P#&=06hTixFeY)pdLcH^sLg+H*@qvq_x~I5hVA0zZi>KT;c>563HlLk&(1^!h;5lheR_6>dWu9~mgtz{+v#3)uz$ zHKBv{))QXIoVCLL4Y)>TU?A647h~_yxq)XdEuoe#KzRYl)DT>VZOx@d~Rdibs~d1%Doqqo$XIq(lV? z%fC?hFg<>mgrK4ikbEco^jv2D1ZW0dYT( z&uMX5Jp<)O<{;3M9ux*Z3GIz_pz%YEmNYg;=B1I^l^0F};qoAGC;}qA;|-)2N+-Pw z36`-rym%xFarCO=OHan%I@l<8YhQt?&*ygx-Cc3b8=*pw87ugV43s5v9p9Fy*8|c5 zqhMo3ypPgmVWOm@`_3y#4)xaLz={JPFu4hrkWL>!%id~9o;f(G6hY~0?`Rx{g6TwL zN(&QtT(aU`uvKm*j)-@3Jo3p-jVErib57n`(qu^tysN7qM3yf({V4OfL60jv-hRTL z-Cb{iBfzPSPu=3^-FFh?^U4rKQsNk~(&8%3Ln1wLOaeFj@U#VI>hTqA26W+$>y~$s z!cq+e?EHKsMoH#Lv;+ZjueQal*ejp(&Z|U!Z_9I zTXO7`Ift2{DK<=9xM)_*wS!e&dw0`jkr2I}8VBp4{gzM?4mJNZGv({HsFW)s z@QGxXc9C`23)LUcYk^mhb-vLbUVhCe>vQshQgWv05YIJQQ{T&n0KD@~a}r|X(r*`l zz=MZgsC;#FE_sPM1f{O5qGKb3R{)A+)Fe>=jX-k0NGx0fLIy#B2ossOk3;MgK4k7c z?#cG80o+ra5B{U$SXWIZ1`_&hl+x`bTFh4yh-JwfnXPi!;``Z`6n5%~zUz%f&x)KH zIW7Q1Lo(`hX~Nlk84VdRih#cs_rCWheZCjT=8)Yc+3P4zz!`bZE|(FgyzT0Q0x%W< zD+Ez0R%MnFNBj9@ynnd*rKt+SdJFqLGL1C>w6dnPmp%e=rHnYdSE%}&(4;IigqCtg zo`r^+fO}fnT1dBqaqir&c;bR*fBFS3TUvAnKs7-2n6y#O|AbIF&qcKKQJ^znwd5Tr zjsG7#^dubASS=`A2*z(55=5Gx-E@hmL~_iYzXeYU64>u-PAdEY#_H;&esG5;MAkCi zd%kp_k6^q2JfY=is^M$wwz+yaUbI?O!eSs0pi7OQC4#6rSev3YP=B`Khj{b$Q~Dk;6nC*5m&owR5)kQ=!DFss>a`meWB9l+7VJF;d$LgOPKyo}xBdHH z8~d8`0I_Ny`S$1TAky#7nUtNHQYfoc1gy&B5qyZcjwN($;I{N2$a-3D;Yc$WATd!d zlXlTpopC8*lXytXp6|-4(tbci5we4ZLT?C}YixHdze>~0^KJlTp&5xR4=pnLGt~2Z z4+WAZq+i>*CCa>slmaN=WyRws4++dMV_>NbOTmC|9qKPG#NGx!@wBcL{}X2xubzo= zbi+LLbOqbrlnxNfN$M2-2SM6%uSc|Kwy%USccNTW>#!3M0JtJwQ&u!`48wjpH{E|c z){S27BA^;BHBbpvazs0jon3wmTt7R9*ddUh&s+{x)crCMM`TsB>QG>2IrWqivr;%e zx|Q8SguL%qjZEjK!4`#hk}^^#Xe5hI8{pOuL&w^j?h^ZEP|Oc>zr0jrX{Y>-9z|rU z4Gx*G;&}~b)@8|41baoX;GFIaUNElgW2-$~x19}aV6O$aer%=RRhl5#)KQeoD&4+R zqcWzmP_b!6@t>z8C>`~(Wo7|SJFJAP8isIHU-Xj|=;fVeNma^2Ti5nQ*vyglhTEgs zCHwxwt7&bnm<2_Nr7E*`M^Y|3))i0d>>6i5O-%bc_{^s*D^7rmRAhF??M@eFDYrM4 zo&qYl<}LE0@TQj%OsL`%4S5huehd6JfWScJ(3(d-o>jUUFL_n12g@niza$23>sPRg z{=GhqN+!J&zJ*B%8G-Y3n|)4}e(G{sw}Pn6Fae$0Zb~H1*<@ZvPQ*p1HnAorTbT7J zFLS@THMbGO_CnVJ26B@y*L+F#aH5RG(@R2X`S+v1Xp1I75QD>x%4~;eUl5!!r4h$@ zX(pDtpKSO;wvbq?#qF`rQtCyqge+`^-gNf$6JZyxZ~$e-t6k(Y=%xQouvD+|l%*?C z&?9?nlA5uAA?js8YFDUxWDV|YtN|i?x2d)@hUUrgcfnhVU$Nrk7XXERQR--lHZLjQ zqo*f0lvOa`jYLGpi)d|JL30dt>E}|#XM8#(X0wq0NB_tQ>8N)o;vg%cZ{K!!amy#$ zUjItD{_M50M(P_$O29}bT4r5gydPYD06+z%$x6>9bi36WK^Fb1t!o6Ee!2_V-H0uy zdz-8a$fQ6NZMyYf?*do=xK2M0LV;qLQ8(APqy^4N&p3U}VJc_(Aw!tA8!uxx<{ELg zDy80MYzOpZtVK|OTbn;YCRrSd6Yan*Qo5|@tC@QGE$)Z0cJMq&C;Wk-Q5&$4PA_0W zT8qT(bQ09#c|_9-kI&nJ2{&e${8$>nybc>hNQRnr)Td|?`jw*-3ImwGr1nLx)<}FR zRWQ>c(LIN*maXV28+BdPaw4u&o0lpkdV}T7GikueHEw?Xz{JNL#>VwFtEh5a$%UsJ z4UvNY5?#(IZS5VnKAB~28_0xfq6ik(I z3~RI}`)t@q0M6PUmKsDb8?fZ&8VY_yrB25IzDJAC%`89^e3nTd$11alf4o>{e$!Ur z(J2(fYV0cX+L5MDG(zzYQBy`5fgB85KYDXCrJDiH}YFt$+j1IjX8D)?~He)X4G|A+j$R2I6S{JbHTE zeot=#X|dOx*BNv+bMSIxg|E|pY$YQbv~->ROY`*$AJXX<8on zCHYUcyNr8U3%YaF-?pvvLz$UE;I(utA3`&wY5;^AB?kpI8rQQjRi78sD=zeWOgLdB zFaYt-3F4!+mI34tD6i?pH=k9-tctQxifrtiVjfTn`o5Xr3lR{I2-IqkZ9vy9LA+9@+wK5$ODI`;4y)&~b)7?Q;RleN`8fL5cBayy+|i zD6d$-DmJsxgUC>wXdQ^WLrG6)JOI_L>0d+cq7Ifn0Xj3-(I;}A0lrsZVego$4no+S zKCx!2JTo3aPSzU>XiMG4U91HSOhD?49gwViN#j^v9u1d*YVWoBtc zIfG7~ki_;Z2b|617Y4XCbLu~}c!@vPjuX(d7c?;eqBF`jbkhE^JT`a2(M40J)UKBj z-PePQ(E{GY4qay=!o1k%0vJz&QBq_;4T#HE&BqD9#U~N_t9IG+f*x(=$Q5*Szy&n; zqVj77EO8{H1L7A3)G!$udDbx1S`SD9ixRYqf4ke$p+56m2XNHA%U7SIP8yUR93+z8?Kg#g#_ z$M=_eMimL;gc~gzmU2Cu3`SWKDl3FeaP(x)r7*55Ri{aC^dgvtODn?G3CNv*MHz9w z@$?Dr263|lK=2?;F(sW94uxZATY&;QKLR-n&6k2+=PLvB;H3xICjki)5Sr0c{U6%! zBeFY+AT$ly_n&1+6SJ;mEa(8V*f3=h?I=#)%9I>w`4BpE0GG>} z_{-{a>e1+o;1bFJN8}&tDh^~{2e25-Q{eqH21|-ouY)^+r3k~D|73j7tzM^71C(EN zhbN^YAdyja?C*9Sd$RV;BE)Vo-&4{Lq<-a44*Z~C%3AW3rwNPGN0Qyqt62gP=B0os znee+^)48qAU+uVo@S z-KlHS84LoEagRsl)4=zJYE7+{j$gwuC{IppAb;Uh(BuSYWc0J9lZD)MIk6h*{ki~t zXsz`Dmf{w73PAb2K#rp*o|8V5gb~w2ZBy)xX^154H*5~(vRKp>J2hia%un(~NV2!k znC%vRhn(~0ZQLES5uI353|GF`>@q0oo}|_7um2%9VZoL);7KBe?x)b!(cnn}o~!YQ zCdOPj4FR)H$9QX&WyakM=7P1|=sSRqm5v_! z9Q#JlKn7u9gIQ3&Ict-+ebOF^8rCFeD&h6m{l#j*3|-)JN+j6)@$Op%R=q|=KQNQ* z!e#i)!qyE(Ls_`~%}l_mjOe*o`cc}TG$%8GeLpcvLtyi#4h+r})@|9hb$-oh=ky`! zD2h&|H~lQi-@jwE|10^y5bVE*MUXsx)@HQyrpKtvqbdQc9sNJO_<<7* zuJ{}w4!V#N`TrD7xi`A}?>zH4b|~%Y|C3cjCUOo5rkV_^JmpEUO!=kWQMt1Nt%xYY z+oHc9yfN$z^$A)F|Q~9f%;`L z<1sf)4JKz?Z`Zspi9xRK{xY#DCs=;dnfmh1;Y+H)^%qQ^j3F=R5A9lY?&0 zZH_*%+I)X!AO+{B<*`<&OvXH4u`=w3`H02k7y~Hf}F+Po-q$9680vgnp`K{lwD}xGAG2A?z^b}C_;78)~*&W|Z#?{wT z57RZ^o21V1V~qBN4?_yD-FKch3xWjz3efbf?a!=-VS6g)gtHhZq`&uP)d9#90}HMEF1xhUHnqW(C>^3|Srz=Irq-bOX>cI#g)jd}Dy15KNCI_OAPnQI5gT zwe(H}EUY0|Zez;WTEYit@B>L})VwtX4rfqjjr~qxv_Iai3wj8@nIrQZDrH0l{fE-Y z9_EN;QFLC*7_-%#QJzVbGTe!hj_6sfluoiU1+HB5;BJgiSqHDYoVC~S{@l^glQVhG zs8jE!$Fbg~Q0DoMke^R}gmkO;AIV@ABMurGiX!(z%|UJWD7Gy|KPs$I9b<^NfjJ8c z(5qanV!RGpUI0)lBrt$yL_UZdeW>N_R#I#=aIA_Z++BTX)Bk+*4H?mHjqP_T7t z01@3nHIOIRw zzx!$(>FzWh#XV#U+Zou~TzW6IeXg_fZp9Hc6JgoH3&M;}@MD={`&FmHI$qdmSE08` z1y?v3W!t4A5j1zJ83*eRbSc?&l*ATf9x0cTu2-*LGfoy({WfvU8) zS^cvMqQ<`KgUYqDmH!B`1B$dFpEz?j_6264$M=!CO9LC(3MZ{S{?kYLG$dy|`HpMr zQK5}Pg^XbvAbBVEL0URxaGg&XK`zRVBVmJ0Ja zI!vlZmVpDgcdxw#zO2U~p}ow4#+i?ODB1By%@HVb_>#Bf zLBp!hn6iv5awXS{p%F+OnXiSqIu5al?=4_V3%4r7OZOW@RPC7V!G4yDaVp7Tfo^Sr z6^~#M+=JWYBb|QChpHkcegPH{I3W?1Yz%SMfjAvSO2p-6sH%;~{T$7-(}6*kr0vWH zrs+9iS)x%Be5SnXR{&N%1JwfW60QnN?PO2+I8|)L4G6*&Nzj1$^5v{j#gwiMgQ>J+ z)cB7kL4p@y6SkfxOTu4VX;e8{hb++0v{`^D=oJ#|^&kk2xX?%38KdPT9Rxa2aAi=d$IM1l* za4Qy&rcxnCR^*1-Hzcp^Z%2~vf41!CytoXzw`z=qKOl3!5|lBA zEVG%GFHcCSLKQsO85x$n{r3XDky~CddZzm%Q_#jMN6>kbjgo|KSCzX)bp^H|la^ly zywKdAYC%ak^#bIYYN}di|9@?Gr30<F!>DoGR|LAyb!|xWfRQ zT9hMQcy2hQ6`#4}MCw9JPlL=%A3d$4?lm&z=k)-WB|j~wr~q?XB5bFK9&P)pQC=)z zB4%VP^yYIaC24R())B_8s=EGEast-HF1WVYvb(!A5W?oZj#gc2YIpx$12W)$H*rZ% zKAFwtGXq_G$XM8^nATJmzyCuc?LP~9?+T2N^#g|ZxWG`!+3inR_k!|eUAHeAHV8gs zENmI&?=PR&vvqfOkAf9NE6NsD%{}sGo4w!6&;1!N5ZTEYg}mM53N^88=TZkCz*y09 znkuX8N?zYm3h+t^k{T90v@9Q4t(#MP>E-ua6Cj0lGVCx(%1n3pQ4-zg7*@4m5)|)} z`Qo_5at(WmPsz2UdAhrk-53jd9i?8a?RA1R#_o*gtX`Z~Q8#|T?N_$p6C0!nD>QWK zLW|m!rWr<%q3-1oEiyhrq{gYA`?Du=`y%TJpgyRH=4INrkid;z2RZOxXQ>_5+4V9#rLi77T z28|#a9N0Rm)>=;*fd{G;WMyWoxU^_7U^Tcz6$;B^19akF@1d8e)ZD9HrMd!tWGrlP zX-bSp^2k`&Bn<#Iw7OOSsv=b7rmExwVUGo|vvRU{MjsQrHps?U*s|7ajD;-*-L4Z% z07xTaVXMN{q7g@}*@jm(ELsdy)`f`PPo`!@AEdasV&#W5fUK+XsmBvD=$1TX6%}5Z zrmrm9P%10;qu~@U2I{Xh&67>jJliyPR;_7nFirD3&vQpr%2@4yv}qe!?QG_Zk_?hP zL}alML<@(9Z39m!qB>!4X7?r?Q>sc3hdEj8Y*JZ9t+%066&^jafCq#TT0po0f(a1| z$xI3fPTQfAEe}F?@k5KqO3D$I+ASqOYNw-hwV~AR2kM8&+Z~#3@{PREtjr8kkol8i zY>`!&WVCF>M5DA~D$$~CXw~D=Bl0+M5+sp9j};zViGcNinU9bk6eku^NUm6PF^Y_b zrAp6@tf44JTiT}WoU`kQfdflc77xX(4B_ZJl6D(T9a$lzibRosPU&PVGuqv)7GY90 zf!%IgWg!V+=c0si_8Te6I3`!cqz&s4bw_V$4@_8%`au$S~z3 zrNknGC7tp~5e-~LcocwyNRtSrh;UJvPP9mmr;0$GN-)}Q!zm&@C^1D?0D+<6#sv}( zK7hj^u_Q{A7(b+lKpYVo2TF-!b;4Gf7A=GcYIZHKP(YdWI2nsHSCP;)1#$cYRg_mj zN-G>I&Sv*%Me{*QV8;kZJAw$&87i$z*f8U8X~{OUq6N*}IuDD0h`%!0_5Y8f{os{c z_%Di=^jDgQ(PToo{{P>!)ugDZgT~b3!K=6Mg{dotAG+pW>o3!R58LE)HtZl73(Xqm zzT=bs*f`+D1yv;u`}Q-vJaTe*!g}P!Sg7N1=;6EOH;~p%nRRC7|9>ppv4pAX5N?cx z9ENgzt(>k>s0`3vk^V^f!1AFrHUDUD zDzvCZGtuTz#_oQzak$~EINIIqL`9i?^xXIB8CGUSLx{U_P|f|OhDrf9G(lZ&MBTVj z#~aEvaSqsjo!NrJinpvU&VR}w+NF&4^Zv>l(?1vhMsfkiQNBL)(bBc=`BrS-Nl;osze z;F_Wj)qcqKL%1Kx{gCc22G-_A23t2h9at~84DnPZLsb%*T}f^XyyL&7z>Sdy{M}TgkULv z2@x7v>O^EY^Z!4)yT4{+W+K!r@AzU)b+tvg07w;+nl2MA2tw0&T|iU=s_^3n1cD1Z&@r*`S-9W_?gAe*q@pR9 zAl##n9#{A60z-&;$>!HnLToIw|95wH_rI#Xzh7uWBpn_X8LMtoU`8GgJb;R9aFFN< zQ&knV`qU63bcLy^3R_j!inQB0J)jwzEb)4B1fww)?z#da^whI&UxsnN^t3=~|NsC0rsc+1sN?yne!vQ7!B2s{6AD?AunJwlsf17Ky4L-> zyIoY3Fblz^8TpDh{GxaFP7u}6X?4~4q1+bhb^#=~)H;jueep|}vg49KF6De^N{SLjql7W?>W5WnhKWl?HJ|pifEo>g>}Jr| z09R^3>O~=sxVVCS!%PV>-JRBkOGSKW?MrQPr`#W#0wq2u`f7eBf8E`E$=27K#U&+Q z>h8YeYjY`y=+)g#ewQ3Cf$lFZPlm(X(ZxV&3Y2)YO99VT)MAIT#GEWI(l+4YgIW+z zD5y!6oo6D$rNi=Hpgx(Oo9NtyU}o4&Xt{px4Ht`M*gM?)R!B;4sjeSd{QlkDT~a$f zmn=JMAb*jIFS|G;A-|JmK0yUnq?HwenQYLp_I+1;J{g|B4vdvt;I!G%_L_x1no zW_EXHS2`oZvaHMuqXFQ6W{h&872_OqClN#2Kn*^!;y=|4UWZo*k-} z6bdD`@8w@m9ALdp2xd?&1TJ42LKSKG%oqPm>r8oAJT2aqQjO9Hr5q)m6ZY?Ke$gyT z`zETcyPO@*4rnK|ySe%(4ZSimEIR{5WY#*%4cnk$udYxv+MU1K-Tg;c-^h4jU&Brk zO1dQxB^f2{l6px|o#FKzqz%Y=qGLJbY z##orsJSxyC#NK8+}}xGn@MjsDC#RSUQ9u&VOHi4a|rX;W0fMG3WJ zQ{lA&Xrfa0iq9*ZPG>@cV&c>Ba$GYqEX&Hwlt`~&0F@-VyG=!7bO-}alth1=VN>CB zxT&sDwi~BdhFyC#12O4AD(Iz8>uyyvl@@W{YlUm1i5{w|yqO27-QB5x{AByNppJ`l=_dw`lP$&w}QAc^O&pJpho2PHY47Y|H24q|+|+>3`N zosx$pUe(Kk@26!-dsxB&5dolweZH+GwXTZydr6DVm80_1u2NjO!V{&!WN}Hkw58)U zY#oc3rN$E}pBcgkOi$DVeyOZd zg1Y*A$O?Z^sVn~|EbPk^La24N+eDjvN*2>1$35alx zjj`}XWW<5CyZ1>;I%_3atdRZvZliUTNduow?aSm8ko3tH%-uDL5XXa6l(Cjl&Jj zb!wb9c(}y+|BZq+#=;$+nhlrZfI!iMn8-94u^1K&pxjg;(U3RzdD7;$AP3VPDm{OJ`+TpbkHw z=Bp|kR6ci?@t;7?r~8yvhpDZa6FD8Ic<#N-%uZ^2VuQ0XGq-0tgdkfYwS&65Tjj27 z=#nS2M%gMML@lyc1kBU9b91v6oR%AcvX63OEY$IQRX<>NI11)U7Z%#??(U{5Zo=Xw zEpFOkZ+L|xv)ihx;zdAV$qts<-Q69Ybj(j$wkUKU!c#$k?Cx%-A3FirDYyo;9dks- z&08L{Y{Der8Dk+(3#qrYLlPo(hTPhzl3)(C!xA8u4<>Yg0+PT9r!U^?zDnlw zDF#pLEc!R?{-H9Nw4r5zjjfs!IXpl@3do(`f2H=1>iYC^#Y>BRv|eF9@$7|e zb;aq(SlBa^hyou*%{}&#jhwc-duWR(z}2X_+6UqOEAspdGBYg8&d4y5v6InbNflF^ zG})gMV=Vm1=^q}|R1thrU45{8w0ySI7&U8*6D*9ICVet&AfhoA8tpy+^}4TC;P|Dv z-^{_eY`VMqjNljxWi#3il93Pzqv3PHZjLbP>&F-IV5YmyT^zcW8u|@7+4p^ z%FMuSG-Hr?goorm1ns~;ZOCXYvAf2PS*nJJv}Ti{5xh2jKr~}h4;rqKY2e&xwo)2E zBpM*INnb|O$HD%))2bU-t+QxDxg-_Y%ng&XLHaPIy2_|(o$c0gIrINN6Mad-nqj)^ zU|`599-!tx@Mvns6)O#LY;Y1p-l~X{=kGG5j_O)fs}kb1B)^-wE>cWU=6?n#IL1QR zj0R%H4uz6JRSpd`Ut);#csX~_tlMAGP`Nb2J|Q&5!W$8Y6GkU0!R;3$blo?HlLTYa zk{#$shHbMoqUMW)LO3N5Y3?9qw2_35B8d|ua#*vfM&|A%fEPrGplo(Qn4sMY!rj7v zm=ZP^b;n)E%PT~c64hk|lLq|*4IY&gD;9D%E(~n>y+Cs|UVuMhWIQA|-wOj=XiuI- zHH8Wc1P#pw1tG!*1!JHK5V(Vakkv4>A(9TE2_rN#%_&|NnzBVNrcBnQ_RY{K%IC9- zNKCuTu%obyIxT{cMP+7Y<|r<@mSxP${O|7mPaI@mWMksONQezQ;zz;||GT?CGjqz% z{VM-Oh8CdCXyF!GvB8xmQhoTsG#JPPIBZzGpkEcb=gtYhEgSJ za(sTfyRl$WIZW;gOXO)rAE$W!e|W)bcXxO1W@c^=nDMAIigt-j4VVCP8Uz>sa3BbR zsFl)j02F`;BM@1XsB|zMlg7b77$6RWF$@Gj48$-9fKUv^aTE#U@Jaz7S&!a6qf_l| zSv(Oa(kp7N+j1RcJ3Pi8N;E2e3SDAAK(MD44D}+@GsMU7TMK%%*SVi){cQcZA2jR1 ztsYgZNJW~j$%Co&nqpb^WT$KN4fR^06y~`aX^0JrEy4V^^&5#? z-n6cOR4AO)M&_)JlKey~UpRMT#^H4CsstJy0@m>g*&w88q>_&Snm zkb6bszs2g8y-{4_Lp0EY?XZtl^=(G_&%8qNknc>9j84LJ>{VcBkaDDw%pZv%Efs&C zR%AaF9rGD#h-kJ1#<kqk=KHqa@|VF z=xR2i z4!(i@Ui3lr;v8m}B$wcUfEfR72LvY;hNUNk6XeuVzbCVA;_t~*#`!0KQlr44hBxxs zfvx#u`SD5CfQ*b&A$mL~!cjxN|CAfjvlr0?c}KbEjR@FTS&T}8W6Z!%Fb(SHxSy{# z>KCmcx2N=JyQWk?vI!PI`%rdqaw@E9$z`JdD676~+~!Z6ziyIB3Kpj!A8K_s)OQ9S zEY%FLmP;%w?#bqACjuh48Ks;f)Qyvucby5D zieSd0J8`ML9t(=}I9bo?}kB|+%^@` z+RMi~EuP_(cH>KeGK8qf1W*a0mJSRUisgc3(|C&==T`vld=4nLW91@}A3M&&1mya{_$22gh?rf{!UG!-37zbMlHIe}F?sQm7`4|N%D@|H z0KwEzh76(avi&56R6;vQtt>rr7&VLdUY*3MZH-;nDKcF|=xIJ_{N7X%_nco?ftkRK zWXzZSk$NOmW*I#bGE$|`9C|Rlx??vHR4Gfjqu?tHj@(9*>!*`OxOt-QVGj|rWT>6r zA%uCPiXs%v_o)kX9}q8<;S?0wt_(HVgKIN$s62Gcyn?@4-yMIDb(h0ma9ENr*YrX+ zz1d7)pH+^LPGZ_`AIlfE?X#M^w5y%bbhavw4?-PSyy1<8mMi~Mdvf)EkN{Bg&`lC0 zGaZuTs@G>N{6ATjpI+`(gh>6!kCqkJ3~!mnr@qOiA|J4Q4+4*1=2yH?gsKv@PfLPM z1$Lr4sSLC{AoH<%rKO!JuAhoakWG-{f!glK<|3?K^rNg7+{zfzNw&Q60Vk z-5Q%ZHVtBfRnlOnYqP2pJ__SGQIp1MW>SO&=WOW*N1K;3%|Tb!*SvmPwWcCkEf!KiSZzVIsymR~*I~rcwJ}0SGIt>C9Dw;$gd&J9BCu08nu> zPpDqk;d44S+pN(MGsMLF!l+X_!Rm_ z9DhiJ=`GMNUyTk)t)qH>KVt5KnnTiCt4@7?Q%WxUJU^SU!cq4ECpqWJMH0{Zf#Mpq zTtW5JV-C`&mFHu4;;e$ey;m;5fL*MPTd2Lv*8CZHrb^ixM_PDV(#sfX_zGP#KYJVZ}bA6 z(@$5OsEItD*ty&2jqoOgB6q4&5=09wOd3`vJ&ZjY_gLVyQJ{_|NZk;sn52+Har<1qKw`hBykZ!9UcRty@iN0D0;3!W@%IC+oc|e-VQdpGKptp8yBi7Zyy-PrB7= zQZdhB5{cH^NC51~HQmW6AEqJxuZ2@C!*k*j2Wj*`Wb+;V_2x_nt&HP+9Gx|AZW7cnj;_!=4S2P zK9IH-Cy*=^q*zZ(9{F!c37|l!Xko}1{3K7(P?=z&dpacL23`wfUI~MgycIu6zTFn{`j`})m`PQ&aX!Sw-_433dnl%;j-!z^O5e+W0Zk1_ik2>7h8lPdgrOb z@KQ?w_gA)4?admf&DVwKy8*=741O()M_mqCkNnSq#7nXjvC{Ddw#-SL#w~L>mZAhS z{+azZVH*nV2?T>ZJark$Q!FpTydu2wa^6{L5lhKXpqSYTsiPQM3ueOgpWeWFX8tdYP)YZ;Gn!s%h2YIVQ^R$Qajjv86C;&BOp;Mp<>tX zsWmwi!is_W3B?srHyK1Y8m=RLG^~!1)3N(1IzIHItY0*10nWF z8`C{Nk5rTUtjeCmfV3u~Y1dDSUFvk#O}m;|DvP)Zhd?ARQm81fmAC*gJ43MY_VVr9 zwKCgOdxTMA*5Vv+C}f-QUdEMY%lG+|nb{kLgU*K686}kdnY;^KgP`lAS4}vO9(+PG z;T3K_Cm(nC%(N4%jQvKu%_=cbr`H7x12fC8J_h!!5A>VsQf$T^-g;o8HE&pl?P5&% zy6L)7U*}%ykfcAlXpH!M+=74V764wNWZ|rKbl3W%vB=UWsPnqkmh-SjUU!7+fVr6H z$sRcBT6^U2n&%%jluyaM*V8G19bQd*E1l`b>UG`(%Ff zXDsVPdcFl+{z!|)D%3UBDma8+d5E2c+v_$+HkI+Uq-?aN%60!hE?PsLNsQmtSjJ1kcfQ#ryWrtPG7frJo*7} zoI5`La}a}%tkuVoa<+=_m2&S!e($)vJ#h4a6%gnEQGO?$b;ya3P-Y@m%RIu>kGm`& zba9m|`dt-wA+#a-?I(T5P6YBZL~j$yf_F~bQiYi!lER3OPgg(PqG5K9tUll?&dTw7i*T|*vr z%@Qrocfu>Bf_kuK*ANVgJ+V4hl3koxvteIcEfpPwYDvkIU{PWso(PAs%24x*;1wya zPT!bAdnYeZe2&$~L+%>{pRo6!?~rOo(2UCiMFO6`nKAn1oJIw%3_Fn?^G18m!HceA z!RwjK3DdpMUYdr~{7==%`Y*#@gvF`C>!doL@eQoEp4~+Z`+HoJKIU(bLN0D`Xn%c6 zf#2uyzE+y05|nvjnPmCW4a*H3JQ{lN@j|AuNe5i2pguE z$VV+%gIRK1hHHMD!iitb9xI3ehP_-tU6ol>Lr$d4!~Z$IRJ+OWN>lUC%brP_BnX9@ z>CM7v&pr*7j4g-PnsoefdFNSWx#_4_(;qA*QyIoo!Zcwvy7-y~&`Wrh{=<3(Id*Qrs=VtXW5Q(EOawBnpFSef<_T z?eQiznmcp*2rdmExO+mGPMK7GW!=A?7pm^qUKADqWz!sbD~ssq19qy>e)~ukqIVU1 z{H^C#rE={5C6!aG`r-RXW~sYYd9q$U~(wwfEx-k%f0=$sxX<4BSvC-zrWpwYZ-GSR1?qO&gaj zmXH{gfX}`|CVSL=v)g+{6X@osDu9qTX77qCKH$&}_k^n!Zo)75-UZ7Pzhckj(<%w4hR^g^oI zbuYI)y=bYUIt7te3HqqTl2fJwD5v-uKvA@lAhbTGr?(|EbeWVS9nb~Pg54rA$YCOV zW-f*Wt0HbOv`RqK2QQW6>`jqhN=|y@`+&?OIcWjU2Xy+=$SMtDG1eNQ4@?gSY=!e% z=kfI;@OEmQSw1THhS2#qX$?trDE9w?UTs*6$u;0PbwP{YD0B(%=7e5J*XKYmmO!@H42;1j$^N(#lOUe$v8V&CQnw?`76ZiUL$3d~ zc{RZH?$R0=Dn{qwu{mxaBq`RPg}|q^1CsrMaV30D0`z8K@LW==2HgwZ_#X+Ou-J|f zN-6x0gqubDT)v!GK4zJ79Sl%9^wF3cz<&iri69$e+*K(l)UL$+pNcbBb^DM0a%hKZy8$DE;5IAxeKIJRWg*k1Eu#G8>PxvEnzw-V0vpq!4`e2gm5 z)eou~Qyc-Hdy3LSXaDKSAfU8~tAiP_V`ry2jH<6o4g|~&e_s^@6wtx54M1HV>0GKA z!=plYhkUB4Y$dXlUnctKCLH!T^OpdcXY3d?Lbym`*vLs0bA2i%Bhm3nbAJKz5p< z%SIL8wNC5Ab7_626S%T>mhCe4avAwUhr-r^^a=(IDE*o*BCSTA|MW%QK{-&$Cj%bi zUEjJKn?L4>Lv!p@HJG}=e3kV4ad5aN=;Tbo2T)jB=dNWf!AhwUR0x!YKLtficb!|d z{ug_V(F(baoAzsE?q+0X{>Q=rkBi?rQz6Maimn%FBanVx7N6aNg1|2&I|Y}V>_ay{ zoou<|I}C7HbIgh;iA--23^hw3=2Zf3Tw!i`KYTG9_&MDp=;$9w>j6y7{3xcC!KvH} zD94;&%nwkMM3+=yry^u~!uz+v9xJpUtuu_xV$Wc9Mw`K`u4m!pv=n63(5pHJCPCy! zNVHJrG0C>P-`blF%t&@5`n3XnnG=y z!mq7n6J{PGYhp}|CBwSx6a)X^h?>X5;9G1z8rHyWRwJ=BE;&94i^8~j0E5f;Y$A}t z-&knXm9uK{&A-eS{eloGh_Zj74I=7bAmdL(r1~T?36y4AX5> z_TSJ)BsQ4+$Ru^6z+VX}mwBVO{{<6LoXiR9`XT#|+(bd`K z7g-VQx$g^|0ER(Tky_37Hu*kW7qayJG7B>XU8enkiD1skms_^d;||$CjY|<<<6a~| zb1O0HxTZepx=EhSmUY4r_C2TC8WWf%XJ@)PPQe4yRR|6OGU1{ ziga%qtDWL9<59JI(SqzSrlegkrRjq;-FbzemQhU@iGsRtw)CUY6=0+>^ zk&I0koT{jgex7i~{16O(w%x6|ZB%IdZ;q$m81z9Lh!7ycy*OVvBigg&w*cx=hiJMf z)+~3{YefTI**LFbF>g53OO@bZ?DUAO?W>-IMaUZ8dAjW?HmhNzm+9`nyDQ+mkuR%( zdaf+c4bBdKGr>wXTUi(=550H<4(2e+@UJl&Y=Rj^GPH~)Cd0OLW`+bqO=z^yfJYPZ z$8A9y)t#Ee#{5$i6yDcMnhRL}4sas#Wr6cA`WI?4dvAD{3^dv)i--ye`Y-1y0@kRo zi%Qn?1M7orNEc)hCU$92l_l@zv30cix7lXELb!nd-yD*Xagc9}6c+P0{lBDLE%wKi%h(MJB{t8vR0taP9!e2lDgH2$zH#9z2!#aG@Bcg5!}O|@Fa^xk3E@gsPUjTr~fPS`F?ze0*>+N)9Cp zXq+K%?jtjBvEA{5Yt!8hR*Cb4mpuY;3G<(@x{3p~*oYFv@+|))Nb@2N69Qo3oZ2n+ z;0S{i3$5>*Un-C?_(a)3RoZj}boizyM*Gn!O2jx}bXrLqC`pD92E3vm)~|1jW4I9&7D2 zqgCUN3T}|0DFAH*HjAO+?_(pYz;VdP+!Y#**^+Jh9*++jn`WUb zMG+7eS~gmOzwxmv_~>ZpE}i7i8n3U!F`T`@`p*MBj~}H8L4aQT_{@b8+vNu+(zS9w zYKjlpC~ZPOB(f;zNsHBRUjn6vRUVaA{$xI?Yrb@hX(Us;qp9aMj#ZX%vy7=I>i*>d zU082Iv^&9TWO$V&V)(mYl*-o`MC3lHRA>7_i4Z3v79*&+9$(ekLK_*+pc??~S+}?T zzmDT6+J~;eGi)5bAp^}$Nm~Op{rjExt4LzXW29$iR)djYf@d&{9M%azrXKkyz~(%k zrar6TJ-j|VYGMzs4@kwF(s#1ffF4%f;360$ML z5-&cv#O&b~7jWkicLl*qe34KT+jnjuu+{feb1n6#-u5dLOgF zVBYiB?d#}y#Vu!*jp3M3fPBQvpWB!}a{4(R9 zB&!JAVc5!iI!;;!snYD5V5xv$m@kSVO{QLCEwFsPuRG*^KRSSu1%Z+1H6-*TQD$Ub zQ_opm!zXEHHbgyww#o+}$FRo|Hg_;NHHIPO{w>hoE)5^ZqpO46*UUVQ;6@k#blzT! zmKik%4ZDUt!G{zXH|+N@$GMol?Lq9cQ-~5Ztz=0=yd&1m*V-E zJvwEZMT4a74hxu(?B8V%aY{+%18Cqa;h~=f9Ju8&p2e;BkaZ|$HRQa(oodYnGqxWf zCKVdQ7(mUfh5>GHwML^eRMqfK4Ycz_iie$CjrPwz8iS;|2KTTEfYgn$#aQ7vs}sL_ zk(Rj{fBRvmr5O~=M$hD#)s`!qt*tleLq3TPqbu+V*Xy!Uk_0HmVvU&46A}`8LC*(Ce%x+B7$`(@jO?=r-7ebp0Tnqkq>_c$S=JK= zCW5>Yn^S1cNCTB(D=cbg@}brKVJXxf%{~}H;5o5!{!=UB0D*$+!lJFuawVYf#=cnO zpuoDkURQL_)S!lUqCSh@5D9g(T@}N}DAe}%g~G{;x8F&k8hsiT_sIRoOCFqg^Nr%* z3Jo=xl%ITOK={An9SZ>;Ld>hZ!wnDxl|ly|RTgO*m2a3$W!NlBgNOl2V|3I9E4Q1K0C&lqE+N*Q3rop0?jxm^WiLC#JM) zDaJu4bqZ(5wj(7#Mq{4{mh)7!u^<{=eAAH!-s*w|Lr4*JX8@;2Sj3nV<79<(CX-UQ zRLX!<(307pyrZQswM0G7>3p({xthAn^U~BfU$a_C4HR3-l8;o!HNZy)k+;kOW)DCW#nxJXhQu{IJ#So%S3*A*pKLS1k~O3VxEm7$1- ziuJ~?j>p6o-S2%HqhO2)fIFOFq5x7^VyN5DCttiz!uEWdr!k=eBB_z!us#e?wrxF)Ut3p&Zlx;E zs4VIVJu>sG#8wY0uPAWBgjG2Gr~bJ|8bf`fH2N*Cav~^zrO~VMo$XCy7VFi-gUCi_ zO{K9IYglDEoY`q(k31xnGKF-N4^d_rr6-i3wu=OX8|8@ZsFSuWC2CTf$gN(7wQG5=y7> zSu_(I<~4;t4V$Oa)hD#6+JtX)56mKhdN zffh2Ot(Xhv+uA!s6>0H=2bW%Rf{TUUJL%|{S6age&(Qe5){chK1#@(Y_POwo8(b1| zMzO(GoWNZHrv_%!t?Qwgqf4KH;KUKxb%g%#ZD z&$_|oqf1<)6Z@ccd4ODoex}M`r^v7{q*J~{I-<%O5=s#@X#Sb~dJ_(pJf(m)b)Hqr zLNY6m=qTPk6hSQAq{CB0^8Xj>{K@+^SFz}o2?eM!p7v?G1MEpoJ-P1B?N5>vblp|n zZ1T_9T)Y8A}*}B)- z?v(+LdILNr(_FDFQmC>?9b$Y186&}2Wv~S)8oZ5#<7R|qI$?;Ov_q-qM-k1+2o%#6 zPeJ}ci)xw!(Z{l37t+as4=swuL=@`psaDWXEFx4cIx>&6CNu_YZd4)MXWORrcoBvI z;dlNd9~8gV18B@J{ACd)(^7!}@m(t%@UPGN%IhB?P$~y0CPu{+#irH`XIUG^?uQO4 zyo#g2{q$IC42Zjdjbo1uZnL_U)G~K_m1OtiJfqBtI@8mPXF&Jk7g-hOLJ^kkEz#2n zU-$!uC3X9f%jcS|hrVE?7lQ>u|E=q%LxP2G8fz+?Ds4laXrRs`bAopJ(C^B~#c*4t3r zB#6)x!-c%u#N{=*mRu@g*2|4EtKb%K?he4LV9_{weXGrC#MYq^?Y`S2_J~5Um-f#@ z0BW5-6o|p@@0(kws@XeAHCD6S+*l8>NKE-U$v|nycdV6QrMSFJkYLUm-5z%97_skv zW^7r>i^IZ#&qNpj=9tyK1R}}ZiPVk?!#umkXIGl#{@uB6jxH;}uCY(ct~4iDVDw2w zUJ2o+y(=+IR$YcVpBjM5Oo6~XD;^9>1T=HwPWOJ@!>Ll38{r?QP84D{D#2nur_oW@ zbw>|08n+veNC2uQ$F2dP%=bGsvVGiBbR7i+(E02OPeW%IQlP8!97XROe&LjCi~00> zx0T{1p(4m) z(bB8SspVKY4<&EE4e+P^6@E`$T)KIwg?%ehxHRB=#pg(G<& zN2GCV#;}NJVxrDIbAz62DO1WMA|gTpy#vPsk^?*4g&R_kgxZS*|JVN0^3hXIt92^B z*>ds!Km2c}+Xt+g!6P|VEC3`GlFzMAcZTRA}K z*vA^Gk4rSXQDpIRucS;dfAwe@1%x|m%4>z?-MzxBYWD~eoXd%-$zkG1(2W62(x@Hf zF-a1-SryC&-fT2IN-C1T135cIkYxD0kT4%CN}TijHmATiO&dBlN_ z=uTpAey`Nd71Dzq3TJb_1RpCbVMNoTCfW$wtq>FBC##M`3u#NM)xt+qcB->5HA~1s z)Ae;Tb1IVQ`OwLLVqzlZ$@swpUM;!5`ud2;>SPJk$904X7t=N+ifJAlEvf#@;-G=8 zbzy-h%^$8b4ybJC-EG6`VAH$kL#b?|>51MFYC*2yS!*qAyhlMiYjl2zrsql4%*q|C z5lvCdPMS)jv8^e3Qfmwb=Zh0fQS5GW4cZ~a?D^hiaD}}r*x|(N#oJ;5gcr~z!U~&k zxl=Fzltk}Gr(iLh-qy{w9I(aC2Bil zi5QO2L=5Of5;dXjFr%~4G$2H(?(QIiLsKBxk%Se`A`y(mqM^{`E1E!&Qqit?XZhtb zNaYC8&FDeV(wMS{UPGA38rN}uGE?115E@K9bcA5=Ly~yH^Q0$H5^w+&O{CbAAW@OQ zjE*Ng%_bC)Fg)Sd%)j^=09?(QsNN(3-O(;ZxQx4q3YEf5^j2C9iJ-wqS(?(PlF=<_ zdjx0g%-?Q`HOZLDvV&Xb^5It9jt7AbdNbUAYphe=W?F}=NDo{`RuT69AIXadlTP(5 ze{WKSjo;oJDWiz z9w_eaZUIen`BY0m98U={Zpe`vz|nJep~`WqA)$dBt_Eu|W?B%mf!a+*?_VtFY_dR2 zNi~bLrqKg9&pUtre?b%dtqRHr7L|ymMl5t-?B62Q%*<@$Vy1HIpK}1s#I(_i3)sE8 z+ne2?@Gvto8^L^3ZaFhEdvmkNHYhcf&Cf!GHX`ApGPj^i{{mVjDER-+fQ;Gmwbn=i z-TCf5!!a;hjb^(3|8HqoXlY$2(&D#nBqEB%{!1`(r%6Q^>jWO6Db6gU!4iTvW+29j zzJ7oVNU$;hBX*tbZ}B}{ zE)YHuJAG2JZ%Cv^FAdVGS0iaJP9 zvQe4p6nn$nK@ z?8di;8J_KA4<%w5lxTV@c!;6tP7=2&G4atu4DZFSN8nyi4<=+ZrUw%11}xr#j1I<4 zi=~9yJX@sBv)d$+phxxAwIY9i3(o{@`IN}TPW9G*Xj7BKA`meHSCcVefS*uZg?|g+ zM+5SR?-deFla*K^@hGZsIWR|lusm@YbXlb=tm>}d6TtFa-(WAoe?DgkuuIlIf_wTIyvR|_?4$3hQc5@ zGI8-nG<70#B>tfI^@b)iA5z5x8KUWv&C|_LTWjqNX+VkLl9}JI#0Cg0Mf8MoiJEl0 zK}n2gDFi2A3QVZPyhL;N0Nufse2R(p0>V9ytRGNwX|4H-WETP3rWelmP3z2p{`HYs zjSWa_J5sOQ+2f#ZNbUxv8O+m$rkT0nNX%&`?1uLF0?A;U_oxO;bsw8$GDQFmV8SA2 z$+M3;$*+0bc$gL((R2sRnXR>!%hU~%Ly)Z7UL4i4+|wO9+x#g`3%WZ89<`t12wM8|tP!~h0ySr2PA+oZP%D75ae%$W* zNvMM%n$DP+&&w}_f}oF`=<+c$GxNW@`!n;uyZiG`&DS5G3E$sr#4mpj>+U~d*5*Hy zIP?EMKGVno}k^RiY^l#?9>R9$C$AQ6?46%#16cQ<-P3|Nr5CclT#z_U~q9{(lXaUuJPA zd_;Hm)0@j2^-IwL+R|#ZuuuwJz9lCp&%h$eGqeD4p*hE84nx>Nm+yvKb-N59c=oyf zS>N8n9UO5l9B2OjXZHtkV^**%X>k%Pxw}(vLbQSq0n|%ph(sYn33XyQ3js=AKnSjm z?K)dfhvpp=d2W0nInLSQbY33vX7uHpy1XSa(0|yf)O9s9_}}-2=jpIif8wbnISeqZp_O+=AdJUrbV&Py=aRhE+#TsfYolGw>~W7|8RSZ&+KZS27p!^SA1u-|ZLBtQ zHg-06HhR}uYrDJ8X8U9k+1=d@+;D3HH+0roYg=&8PL^r9R_u{+f6G8QakyF-(9Q1d z+}+*G?(S?0aoFf)aOOh^f<3+e^SgyD*xlW8>+Ystq=F>P?C#DgK&atD$LxjyS))`z z1*Z~@SIf5oJl4EjGb{hCwblhsgNvT)1&hfvyqMj$;dp|ij^AFxLplIz=-gB_HC9v; zWY}QkTNX1j^K+XuK`d{kXu1@0cjo_pCY#L@DO1ANxf?w!7bq)2E(y(<|NproAgLm- zP5(1eQkqcW@i3(cYhs| z|J~i6nO84KWe&(pjWhrMGb{92Q6Tr$R?<$6d4Hf zqA9IN+b7ui?N2DER~)7HI$g>@&z%`fd(l$^6L*=OGJ7~ zs9g=#Fy(b8SY5+YjK^z$x^SbhNvptccyHWZl37TK%ytYP(NyRIsF9yLzy5@p zWVSKF4E_jcD%1f_^;S1cog%{j|9KUnW>?_XLQglUx0sth1dsFGU1*7p>LQC<7Ft+6 z=KXp1mR&AqTprInL{kh^aHTU_Yb}eHn_2vW;MXt~3L#cDz-8+p*R5IQf(f1EnEM7U zJAxquSd+&<#TMmah`QYiFn-!zlg2VF;HloaUGfLre*QXV2>0x`+g_sSQ3X(zyTyI- zNCr*yR<~s}x7dwnx00tTqO@y>$Fd&k};ZPVP5`_d|AO>O>27&;9VmOTCP@K5(1_&DR z{Y=2jw#h$@mHz+D?E&UU=#x$$f>sY0;WV@x{@MRVD>8ySLxdSl$`EoXBlI@!XGr1s zgt}HkOIhCvDtoiflPjrDwPnz;Zg*6t(o6?GX(&R5Ssps1`$H8(v%GbIk^r7;A=j8L zIKqi6hfK)@_KNC42KCg7jOg9`5^J4Ipq(Xcvmf2pAD*2+CwqE8gwE~wEhDtWi+D91 z+3SvwkEiK(NVL0kbA%O=PYi*Uyh3O~PFM#42H%jA5)_%BrO$#Mh|v+iA|k=i?nKge{50txR=adMfpfmt1Vkro~yzc9Y@kK@Kh2C zWWroi@Io~DIlu3LV~!(pkODefJ(HEslJC>=X51hqTz&1f(5;Juo_;zXIMcbHQ%j{n zGVb62cVS%LnbTfvWM-qk&g~H1YR(&9zRFMZI8&GARi)1Rv!FMgcLvUAY&k%x1SAIJ zZ=JmOVR)}sq%ckr7*9c8#jDSdT1&{2`dt$QQ2E?_2Q*XQYeNtd?MM5PsJR>A$8;6R z<@s)|04inc527{yWP3hiKEymVe86DHxx0iIrJQ?GXfy_iE)_;PJK-3-1p+$id6MRc zB=nE8Jd8I->Ry46LFfmdEr+hvG7S~u8+^W}M*WBvSCexy9&#GLkZ2$EK9nqEtbe1T z2X!74P7VPDE&&3%uSo2Q9k`sQXYr$w0d%wj41pxS-g9VVZyhG0B@Np@s z)7;VUxiUbMfx~W!albb}0Joj6_y^QE{?*+sS?uX;O6UpdPRqUv5vi{{hf;vdkN-*;&b%1D?`K63H zU7bp=gQ3S~A|KLV$68`e9jHD||2o(~F%GZ-@=W~5$H#p)@TTGwrW(a!8Cy^YWkSoM zu!LsJCMmYxD=Tg)PcMzu7%ez*6}GOD8J}bgcnqsNsG(K$!@1c5ehOxwYeIkp8VJZ` zls@}j^e;8q !c!Ny+D_eD$50i$q#7gxpzu(zj@ydG|hfv9KB!%P3^Ch9i;oV|l- zxeZysNZnN+e?WVlHraIL0q!b*w|GgllhI@XUF!2e$5YR->0ac1<9E+`AZC+R#d@$U z>_~6J0P}*$2AF^-#f$l`9(*gxL1@D?Ud*5rnwnkk5~sE^GD9t9`y=U1--Z|Z{W-v= zUL3pTmhZq^7PFRvY@U;tqlHrKv;QmnJ1~%bw67EPv{f6Y~3O03y#p{W897{&6~Ry58-UUr|HcjQxb!i zlZ`XzK&Vc>R!RHBln!SgyC7xWc)xbOK{to~pXovmx^R|AsVxe+HZL4Ew}P-;x3()| zh>Mnv^jnvJ1CW#%!#vXA){xG}!D!-l236+kK0P@9nB=;zjV61Ur}?q6M0&PH&YLQB z#_JQ)2oJ^h2-Y_;_IG9cLG(k_2A@zX&7V8%GmRV#L*Z0lTF4<#rYd8mfp&f@TNSQi zo$+{pKR&c+Xk2C!#T@LvL1LreUhtid(#|`7x6k`-b3BreuwPXs=6(Q!IX>Txm$$hF zs(5z_q|u@p4e>c{uuk6j;p@^e2z0jcH(WMDgeFoZ#J@5EXq;Gm0X+F>y{lnFi+aq7eUhbhoQd2v+TchSpfVx3e-Yx84zoMNku54#j+|z;U`{V)RxwOe#zvmYC(b zdeKak14sNuR%lWTlJm|l+9V{kiuHH;6dHad8`&Q)0oTa809W;hP&<(vj=)_6d~2Ky z>I+}tC*V19)}{-gQE9)HTu40$XhaKV=}^_k($X%086#RzX(V?~;LTucZ1Bb@Nq|P? zBBy6gnkG?4x- z9-todqkCi9Yf%PDeam1Fau5_9Gu7~ znIOjH3tH3cV#G*%lDW`pAW4TG<>;U7_6*G-aw}KsiN|AZ1?boO2uoQl5#$!j+ogUA zqf0-tDrEb|>WdS1ZSckWRWl->hR{iKe3pBrbzrk9L>nP}Fmz-J% z5uX37u{ZsRQdrv^wWZ~mzSIgZm5S3GYQWDih6gbIMs)tIyDv;Y4JS3{Q7nkrtwC!V zA3KXxjX%cCGdslZ*;0@2MuO1?Fea>^vMXRycH{P#Y}3D2$j9Z_2Sz3!pyh6!SzDUw zrcpONv-8r$EA9Yj0`0PEfaUQiNC(3V5Qo+x()W~%QfSRcCUxzQ3Vu;477}qrsXeA8e_)J)L{|^iZPOq zp#>R6+_Nl-lZ2E71tmKvvgjoT%$tWaGfoOv8ch&OHrs9PPW>59Oiz+b4o4t}L==qA90YU7()peLxS-Hh7)CkjL3XH!r|t<7T{ol+LROJ#HNcS^8(Z6 zE&6vzp~f;4Cz-AL*zH;5v5-*<*<*=d`&MeORHQrXZ@%n+MYGaFNBCtlNung$FrJiM zsN)SdfZ>i411Vs>le0iPAL6+no{Wz}wB$oig|Ux5@1Sj03W;KrZYWYjC4!3v5hFXW zaZgbrOZU`e3Gdfy-7?Gs0nR8T%0HkTYZ^|`PmQy|N?=63I`4&(D88-`BX%`J_T>0} zprG24A6O8c&nlaZV<_YB#*AiwNAbn2?2IPQE)|y5M{eJ_ZLGk19RUm;W>b@D25kZU z)~>`tcb)z4+}*Fh(DjL=lz}-7h)LNo-GIEl^kO_6sSjtr)flaV=HLa^8KUPYUns#MD;G+(r)*B zswruPDAnHlN*$V}k>PlGBO=+gVJ_pL;n!#dOmUp$k^Z^I{% zwa2pV=GR;`S$UsY>94t30H!gBd)=V*6K!6hyh*4EN_9pYOCb+-N~A%ZXe|R0+bSBB zvNMrhXw=+}!%Xs9&Jl13vkw{rBU$DEyIUV-22%H_W2o0-1xf$APEGU*w=H~V% z4)$^Q=hE&9+@RTuG^cU}k_r=0*2VIRr|ww#V$i$m1fDi=$r(twKOIQCDz*tm>!jRs?{LRr`NOD}xk1}-vGE6@~Fts_q&;jq>O z)iKm^gVY|VE{?tI6vIc&JZ*EAsd*2JuLs2_-3Bok(yo_%s~UgMOlqdaUv;5j9Nm+1 zt({ow3S!gTiY+i{Iy!!^W0}eb)4>=YXy8FjOJbEc263e1$V3Pp!FeLf7ZlhCDUEd`X6pFkz4J}rpy_B2uj)6n|dYg3gYjrFeeqlRl^tUCxJ1xg9En7WRgA6q$^Yn zSzH52Q3$iy9Y{4_O;Gac_SySYLDy!x%4=CdQk(Xp#M)zwCs9{ibwL;g5ENpDX~>{r zZ0*fqN#myxh~gckiJJ^rkPIUoR~lP2>F3t%QvrK<$ZscP07qk#1pXe9XU5uc;sff4 z_R#sC%ic)LNJ60@qE9gi&kQeVyKo&eO(o2@z(d4i^D$rSM0?V?eKm=^Uyfm{-jxiA;fD@vE7+ZEg78jo$Lbn$+AS%?ML7Vsi zBomb#K2`j{OCpD+YP{OrWSGJngJ=IT>PC3L)01aCx>C&~oJU*t-Hq8yW2YWI9ZH-7 z_ceM#6EJb7-Q&10)rNvN2A}Vjk#~fcGFw#>rWYb5h9E=V*BsQ+9D=y5^O3wWPaj)| z)axTe5J4~J=>3 zX^jvqp@ZkPYJ_>tVW`d;S#5^XNFm`8+tmC>4*KYAm}{&cXyHe{Dj2Wa(_OkH7t57G zbaf13u`)ThzzSQV6e8tf4e`p1=>nqMs*PHQ(6wB^d$5DOXXZOVF}0qTH2DwbJGStJ zRp=gXIuSnk1&F~lfu^VlXBMOefjLBT-m4r9vwAO_Q;#FA*^13VA<97IH3vRB<;NQ7 z!kJsfe{xJ+2KX|A zJ<~-Eu|tFi*b!i1zNCa=oal!68e(Y>!H-sa(!gU*B;Fz5E~UG`@G!`mw#Ci3MnC>j zG)wF_=5KbJmyPe4pEPy5XV@e*(8z86t)RTIUgr}*JGl?Uy6lETi3pxM{9&t5oJoF+ zPCb@U@C-;di>pN%HFxe@?@$6^?|0k=CI7g9q*iN0o-K||*1@#D)jV@uCOjS-HVPRQ z`KWV;N3lHMC5UnU6wA$X`%Wd?Z`y>+*4XnVR2yn&G`m2byN07%w}R3cBTgJS_*of! z4O00e15*6c5{uPm6tWpd=-hJ|CBTYHey#(%i_zkQ$n(%(I`%O&^wt+ACo#kF%=XA9 z=_iYmH56j}2hoUfUssq_JcEUmF|aJdNEGC&*`?FaFc_T}AbY|=&Y=5-ESs?TJ{&I3 z2j^SQkd40Mg;6RbwrWsNcqFEKTvdUgVZ5(3;pv(K)liBHyQh~5L_oPi&%^T0@@s@e z{MaT}AprffdGzK1fv7DjqrsG289VDKpR}<;{)fdA>kuHy)i<{ zbEKFH4WNP3>sRZ8)9GM51|?Q7`Q{Eh@Q38p&g~&qtEmnBI7bzI}qU;Sdic zoOJ&|DTlPO-4AKG7F*nT|sb z+m)9A29Qk66|ECzbv>jdHh3Yx{#YP$^34dR|EGlfNcyR(+BhBSyz2s<&4btq>_A|A zmH*zhE&n>&6(GLk`j?@d@UtM>HxOatxA31Mg=XI_<6+C&`-wFMW^Hi3@rr?oN}cHl zO`bYo{ERdzZcE)}0V3Hn9Tt0Dh!~y6%nyl?#aCMY)U9=oF4@k$;2TfubGz*X@;|oX zLW28D)z+zR7=bIa+u4GMz;iOGnsVa%?pF+iz`Q&^PiNoz#&p84>Mc^S;{-AXFMm^Q z6X=eJk^{i=#|1Bs;@Lx>^4}4^CNW+uRpJBXYLFvXw%5t=^X=-jub@8)NKFb@OgC#v~ir7|1N&~|+fny5m5wUr3U9A)r7{4lY-4a$}`xo3?= zXWD3|L+hlJ>R-`{9aTeUbo>ZVW>VwV$Zbd#N=ej==S*D%x%*W?SEYLQV|f&XyVO`= zZM0G&Z3Bc|BaHE_XBpzFV~%4Tf^-d|LFV+Np;O5a{3wJp$q>N=KMcMCMd=FL-mI>! z=1`#O>qW&=2{8>-ceLEI)A&us+t>DH5!k%S?nzv6jcn`=huEQdT>2J*kp^U85Z)3d zLHMcXsoelIJ=DHv=%Jj_Hix?ra~cjINZSEZ_k?b<0-a2Psj6wzs~cmb1qOr zA;j?RZYm%~$5mffO(aiKkX7P>tv|RM(5|u+*96p)5@wNTEo^fURg=cL%xX@#|Kto3 zH)#sAg}LU$C2ZD`Y>%`~*&}9NDr9y2?$ZmN0vmyYNgNhZn#G){PIn!sQtg&m zoTz-W2&bR$f;?qE2(h65yfkr64mB^yzqf#4qzn|bvluqYu5ylQ(xw9gcxYzoE$=ww z3-%hr*-Z6|3~^9v`WXZmBgJ?k!;3P4oMEIK23fd9&+@M!uL7WP4kTbVCg4b5=XlmF zCYoAh=(;;2I0hOcL5LgCEprKc)F{sz8N*q|Ss}RAReQ~Mii>Ngdvp7hLX(fMX6z>> znf!>kcIdH7K1>k7*}Q4Z+!8354^XuOkMjD1$`4Ja4+_C@rIin5qx$D#M}kRgb5xsY zT5@V9?b};eF_6lnXF(F4`UCXbDw5Y|E1R!M>G&l+lpjc`+SguI_&tUBo@~%dfwfUXV^gg5t!n)7tZjTVIQ*@$C<2pOe^jDhaTyT!Y&!scbPelANGrRxHoN=mU&BIU^1OREB#2 zbbEjPs)UW2G9Rhn>XqHD>PaHxh@w${G#MQg_}+Cdb0+plphB7e-l%>%vhPA3Nmzmq z-#7@j>RCU(U`8g+I#nO-U#M1$|sh5kgcVk_kyvu!>GQbHfn zmP~9hXlI?121;c|w5pzjq%3+~)TP7%Em&TG9-_nwzu8zSK>*!8uJuDE^}Vt& zdin}vLLr-@F~qU4&u>c;Sj0g`9%`GK2{!IkG0)ZNqvBel%!y?C8F(Lo)?*TSYiXv? z0ChT$7Ietyq~Q1Ww|Igp2Leh;E8NS(>8=|H>h_#z&#F;{LzsT_5Cmb}!BAuY6Ene% z^PXtCsS}w--~dd#=6b9d6+ZyTz%}et$vccDn_=z)yz257wz-g>qyxC01%=(CGrH@q zhlX)ZvT&$6BN@2nI{_hr2YHNZKX_L%?x-34sd9H1Q`W;fVy=b$=);)MOe*l zPN))t%NJK?N9;%>gfugXQl){0ochEQU4y!bqhfhp$tZ?h7ab{=NEo=Th)a<_Q5r88 znS^e{6w4r{_IW0rTf;Zx*PQr_R# zXEeR;@hv!N7=cfBtcqh|mToz!%tL^uzP;bvx=&x6kf?FO81PTBoZlAEXWs(yCZPNF zu6+qO3(LmevAPK%piGpcD2HkD&rdF1&(r}})+8%O-_17a=Mn+6rn}xFu!HhPgjR%*; zADU#xKyhHlE1sOw>l3+UAEUdRqER_`af8XM<2XI^-Cm=~(c@lKd|iocdXS6TF`=IDAtu9KBukx!3G4H*pOO zO?)cFT&FxudE7CSml$U}k`pvz5NI~{rq2J-S24f@R3Zi{hu z{IAg-`{>ea59Z17G@{dwNt6)!S>j?O(E^OC(_4yF zsmM3n_CZ?3AC92+bE3W^e%D}iJ9l5to zjiBI&4bhH(A{(h18jVQlhZHKtjyFOD=9y;9PAj^!c)^pjV0?utk%6TrMMzl;&!B zJ1fRR;avQl3rLJK@~g#$~2WbvzoMa4kVEYkmXzsu9o`T_TmWZ3ry zb&iVzkL0QVxIjn0OJvV+z;vk@CsQ_>{oakb;nQ^SL_THoL_FpNRAvw3w&IgTDU99m z|HCN(Qer*dn}k}~l87Tg%HcrpJrXmnYOZKfiHis64@9hD;;MjX?lOeuAG*7S!?tS+ zW<@#>csKGlAO#=yZ-}G-2Z^(Qm`rR9?Yll^Ml~qL03{hV0+KURA)~BdIaj(-Bw&z= zZ2}8_o==6~_k(lfYr^ z!X`UFRsp!HiA1OMQl?|Xatz-G`49cg*DKE&ivf-AnEmLvizXp3dKfSPvCMX;L&D>6 zo1k9UvcO2G?;B)j+S_4J@BCxrcnnVkLrZu&cKj-u8J@$iNS>&=jV@60S7Wo|2$8Qj z@3nGp4n)`N$`*$vGNiVZtp&nQ{w?OqjIz z>vVM5VTm$Be&pI=pM>f7CTrgV!0!tWhPvi6VXVsiF^@$w#D_F*-l&fv9eR~`=yF*p zJjlLHit$8@DSxmvJ|u11IcLCI>qjt8(jVD0IIRMAh-{UPo?Rb^I>mb&gQ8%Ml1m=c zglwK1 zd__kh5L&{8_MvgLq-lSxZp;I2v{@2F$d*#cG_r&Q^8)n(F9T*w2eDicM0XF8 z_f|z4Hm61B_`HZ!g~$i#lnZ5M<{d(2W=8A(pUC(B|9=nf87?U<2?)2+XweVz-`#z( z$l6Ka!Ab4qN@c|wx>vna=eQ&UNf7@3|88dH{{O#KsmhG$MX517Y_|M=Mk`szeQBca zPGvnbbBf1x!fR?mOqb^47I)0d+&*;wt*vHeW(S_+&{mV>2sN}03I8`UbN~OlnVI|l zkG|F9N(lFNcb_$cQB_QiB5F2C#u(KWjVTI#*gkY$0g~5uMCKbEo%T%Kuu#!)e>XF{ zT)`eim6K;eCpB*tv}*9y9mV6L3|4nOMn4iKT^hCq)-sOZRwoC!BV@CQICk&()a*{dYxO2uxAtA?!IN)np=Oa3@tCq>kYz{yix?D#BGUuS6XOCSCo4=d}Tub1f zjeNP~A8>ar63vai(T#*|`FbSMk|ok^1v73CRtYy%3aZua9t}ktMWw_ZL>F8le7GK) zR74M&70kFQO17pec@`Ac7XfB7Bs?TNBt9g-S=a74Y2obMT@v1CJL}$ER@u9|uJ?P* zw?=Snkxo%mFr$qPCx?L|y6*a+xAux9x-K0l(wLd~gR7^_qIsS^bD?}13J0&YnYTji z?(ULe_YzcYcbR#WPzbmA$=u!DCAlRc(6zfWDBKLhOaKs-gCSS&mlG0WAPJ8NN=c8Y z1(WEjX(h#`5~q^dDjcPPqGkyoFcSI>QgXheGNU5QsRA%A5pH>6n3X(03NT+kI>pQ( z0ec5E{r~^-h^?a;EI$&kM*IJFGc))9catABWoBl^br?71i00kQ%>DoT4#L^MA(a^c zgTL`YhYch2Sen$3$~3r)yE1RU=pf_aTeIFcv7uYW?eVkxcXvN!No6?x|9>|#bN~O% z71j?Ec!krf#!^=WYEo&HagR`#hrLUEjk}T-c>tn)FB3FmX^SZnM=*3MsKGU(1|@a^NtiJ)cGV}?e4<8O7Qms{ zd%#zWi((9nT?^<8A^C#tp#xt{rhyU*GbCt4$tBi<=_6N_QN!f#= z$547-VMVAfNyLfjl;1f7t^A z{eQk;gr%idH%idJlFD-Ua#GSHo|_hCb4t2`gPEC`|L*QHGrve676Rsyo0b{}bbS%B zyPp(!)q}?66Dix3^sSI8=3=kAcr(qtO6a7wswcT8y9s1q@&gP(%7qkV?t~br@X6F_ z$+mLBvr$9t%<@fc%s(I(7L>17O@u!mE|rtp_dg1>pIjzA70l=Z28G0(VN(1dL7VVc z*!ASq-QLY)Q^AZlIXW3YLk}<+oo6K)#aQi~vV-VoOoU0Pf1EgT9}sdgi3q{5*>qcG zW}u4y?(VPI7MvqUHhE~sVm{5L+UV=?;HJlk(hQ>zT<*|#gCM*I_@H2X`&=Qd)>`ZT z85T57FaUy(LgfmZ9R)>_kA>yDWnU01c%D&#Tb zs@v+Vz@lvBa8kjH!;MO%WHULU%w~pcKpemtQESMPCaJ2mg7_hYJ%DGPG4m143T9l9 zOEn%jb0q9?^%Isb$Z7Ko)P9(8RH`oomF6KF0p%V@b&YsT>5FhfvsENCOJPuv6%+ z&$xbQff8d6)dU@lXUlx%Afa)PJ#&Yo4rqKFHneE?`PjJpcDVf$Q}#?blGmK*`M#r53hQ#ZHl(B_5vh^uHH~A%z_`U=VU6k^(hRGAVpngvWzF;h4LFJzkTPzikSR)QZn36z2}+Nflcqr*l+ z03|E}MJ#aH%7U@fN;LuO|5-7FVRl~t;+sbEGGIIAKjRiTs3Tr^>^QW?$-2Zxix+2Q=) z@V?ywAxZ@^N=31;$)v#9yMWSiRVu?dsbI!ol#~oEij52iHuSH&JVMBTWtSY+11A;C zIE<2#@gRzgP38j}W@fZo?6hJkh>IML9>DxU!V9ea^1BUyjp>+RQCP1Mj1yD?~JySsmP_h;aCcXxN+-Tm3_?(Xj2-Tk!pyDeNNS37Re z25VuWMU~2Mv)MKbE2K;aKIZNdDlalIh~3>b*o%yeUN`S6>7*-iclV23_wMj;@OqJv zaqDK{q|>X+{PaZz#?-sJ`$UND=9Q5lOaR3Q6_D%yT#lJLCo~yIq}O-kXE6m;TQWOB zrwdv*OX7%Tr7~TPgpJj+BXQfFAg+0EBwB_A%@d@QMm0|s8W_u-Pceivo|!2MSXz$z zgp;#FYj<}GrS+wQF*85eP-5o4yZh-&`m!4LX(v^2si;!fQ;QyrXS>YIoRUvI#D8V} z&CJ{{Id*7TgVLuFZv1z5zcgQ-atNg!Y&GPrgq3{Kfjg-J{{R1OX6F98nVI|lU*9Jq z*nu<4sw={lK-Qs;hYg_X*V?A}rmtF6_zL!Js|?D>GfnW1n+m7BD#kYfvtD2+ReFsZdx5mwACBD!B8>8xN` z3GoR53JEd^OKJj=AE-2|cu9v)000vkNCXiDf*c5;b$0+1fCU-`NtnP?C>)st;xHH_ z6a)l;FbD-P0D~X^1b`Gs!9>wgO8^m{kLt{*nT|cnUht2(bwvb(ng2XZg~HSs_#Y%u z?Ed|HhuqK%Ob5vkCk5kQjMI5+afwKELNvhc;9sr-U~rLo#=b5Vb6ZCy^!xnEc~h35 z6ytEvO|e5kUSy4R_P02q_3?MJsQ`H*lv}t%Mz4Dew zXxiNwbi+U*PIbP3@nnoA%y2n_2sJ()zC-20C%rZM{z{c2v5p;J<^iNxqscwkDe9Q5 zO7aA-3)>h5NH;MbD4QRks2+VdE*W1Z^Exfy3=88^A*6nMP$K$O_nKZ6EsE_wp$ zVR*tmrAj$e3HavIKbDMxIQ||--u5tp-#w+5!*?Lymlc;&3tVmiLu3pl^%@TdMemzJ zA;B!NFm*9trORg`-8|T6xjj-J1{QuOK?V~uvfRl%EqpHtl;OY!2tK4?i?wql(aTbM z)IWHRM2Hsj-6$WnelZdG=uyf_G+DOP`N791LyngESRX=m0f^|WI8M$dGkN&WQ)dBI zwv`QkxyNIw&Anul1%38*B68P~zM2MzT#--DV%L(6-^p6?9D}2o`tyN$^j@$S#|<*Y zeO=9OhJ!CU_dQO}@>7z>yp*8<| zfR|~(`?XZFdjhuj$0SIN?g?pNX?s9M?aS=-rcQ+%biNME`P+i3+i+g*E3@M59;4|V-@N)$TL_OAynhfM5qV6YrVeAz% z`F1u~6)KmynCa}YE;}+jluYnKL^5ER4C8>*nyp8DJ+eg08=drz;qUV-x#$OFcL8eI z$$rNep6HieSO};pEzL}4_armAQoTnZ8y!qybc4mE!{?fdfiLnUmlhOD)p}KtswlBU zvGixX(%P~!m(?*rm?sJlu?r=1?GepzOL#04wgiTK2|kDcOo7@^7bTvi5FM%A@NB#& z9Rh5y%pzoR*kfp}4{1d9+C*aDYv3MG&akxI)C{AW1&Q_xq%}AOO#6!ZZHxQW6o$KD zI5W#o+ILSyq&7@PDPK{hq7Z|t)B_y^xESlc9qf{KK-aLX-f^a255mj5l9<7hwk}G%#EWb&V4J&ynW2s4Oei9 zPQ2TrB5EsgW`~VJ4n|2z)aB5Irg;ls7~?hnNAw>lrtk3{VpBDI7dXxdVtRlf3gdgV zXqOX`|IJN}iR!6sX^yn*h3&P1U6MTS3zDR$>AJ14U*TxIN|Jm?Fe$7PDP6abSjU*2 ziGEp?$w%&8M!v{TIJzh~4OyT$tNBP3<)M<-x(9kkW;|44Lc0-iv1dspN-cmv?+k-G zY8HvT5*-7}x6ZH&VKut&gYR9Quq#6H^z(tQf~KeqNxDE5u>r+~^5&x?qk_pMOv9qXVus$x!rj`vU%-8qncNf@Ryh z1Z683L{hk8tIyJggWf7SM+L?_0!TeZVUWABCHSxtaGw3eQD_ENzr6(}zD@6t`}%H7 zXI@zd8U(VEIWLP_IDd?W)s5bLNHjl2?BdoCQ!LO9;_Z8WMc<8Mu|XAlpfNCx3aIdZ zHf403lbAD~ma>R`P>y$FZs^tV(2_>ur`5QErRPz4xa{3Zlt87IVD&}s^Kg;C_V|T| zs!%Ak0#UG+)Qc-~v!SR>JhEH~txBLqM^4`{h`^k$KKrTaFdEkiLUbvKxRJhBn89K> z14%6kXo@SC6BMJ(s0Czp7;6)H+Np)a-&Z^y9xOP zMLRtVg5Txa5nF>AGy~E9INOSRN*&`+%wd8BSpAow^gc6AS%LbpgZO_gtC#P%^*R_y znySQ( zRbA!~(HPzX$ea^Ul#=LgXC2z4W#z{A-SRDw=cf7?kELEFNl2hJ32FEjn`1!3EC`#P^(5E4 z*@AAPt%gR^ap*yo25zgI!$?0vdFcD>axCOJNC!}{l{0RQbCzq<_H@E6X{bP!jJO>Y zK0eIP{mF<+iY9=lm~HCJl5ymF0+=?JrI%M1sO-o zhyn~FDKkCcwWeP#>8G2Ai8Fv7-dxjoP3@6mngH4IRExHmeM9?D7b^t$Pi2_y!RRwR zqM~<_Fi4y+g^VZ5cmlLlG*%XtJs7WMD==~LsHhc&D;n(-MtV(cQ+N}6i6dyuVT77p z-A(~kLWCY+dA)1eybkgD2(rpM$etc(?sS;L>}DsLLJaeD&cZY(1tfOO>b$0;y7!Z-D(45aY z_dL5r4pE$y*huwag#r7xEb^(HcI7L8_A=G?fN{w8m=>3w=cyjh(&)amoi9k**1b0% zE(WvDO0GD0VY~F|45jkV(pL~uy_JA#I;Z`h!-BFx2V!R1b7?`_w{tX6xtdS}WB#OP zY}ua$N?)-@)mBpO_ih-)gd{yy@6vd*0}OC(16``$K3|5qBL{i&C*p?451jo*T}hvs z`jVW5iUI!QY2qMeWPtbtYf~~(d9DAMy|t)R7JDH7ZO`0&d#`Q{D^iJ{t6>7JwsV&3J`ooS&@ zhF?s2$cd)ZfpeSp(X&j%2RNK@;vlkrdih)_4VBut?zn*A0*otS1c8WAh?mA9{_w?c zK5QTOPuZq-S#qx;`H~b_87gsWni-9%=p_;vWMkBbc~4|MX3lz(7CqabjUL^2jU!Vw%=-%YF)q@t_sFO%eBxsNL<^mWm;HJa{!OY^BC<{nl?j)#?3ZEQwnW80a zuWz7kaco#edp_j%e(=I@W9~TwX$rtd}Cf^KUlI zVhUzC1XMULmFqUI&&u2mki7ulZx$cQBhxP>{ns>kSd7^v`%nCNl^jZ@d%~FyV#Lnr zeG7h_%KfZ~1FXrB%x7CBC#Jtxr(>2GZQxM&pPfe85$hIXpHkqVWG)PrpP%O-xnCId z69x-{d?v%fJXt367jG@;8Kib15p|5>-dZ(X-X~&xQsL=YQLt5$GO*u}4FJ{2{0@a=PB9_YrsO|;r zmbXccIt-(Z41)M(uH@UPBkqNos=EiRvSL?Q>v;&J&@+J7GO zmuV4iRnkO=o2P}2n-qY>maITq%P*KqCkZ?FwAk6Ue#0Rgsqmt<`F|JN%2KkVSBx?_ ze-@exze$2W{?AL02oK@%$nPUd=n+UX%;?LD{|>DxtQ5GZ7wkB)Y^hMI?rM6R6n zh?@E!)&c^3&YQhc2Gbe}iR{t`^zm~c^BpBW>1N@bIBnQvr_*=5w^rHMl0x$VLjm`w zpA@ym?F?r4gM+V7_iaCN`Lq|m?Fv=yl#qZ%V2q6q{S``|rwdD|mPIVEQQ6pe301_M z2r=#f;OmSXiOAA#l6+~N;{^MaKm6pBhFpN*L=>|)otB5(Ozh#U*qarX#hfObN>hSr zSqZrNHDK!!{~4PA737Q1=g%{D2oDAFkAp?|nL&M#Y{^&JFdkY@OiZ9fk3R4$ul`Bv z;d8o$61i8sh|l_&mVu8c4%T1SQLpOENBIkPAE6r%qoYj>WN z*(CG$UIv6tnU5ZUEeBmM54im5diS~P?Rhx*^1wYJ!WoTs+`Xxs(!pnGL4A?y5ymZD zpO^Fy5GbH|;C0G8&HfIklhH_{wLNIolJBdqG~>!i2!&bUE`Gv+j%B?6i`0WxGoJn{ z0)gWj)n;&~A0?w;EN-N&NvJww+!>u`uwVvD00l#FiB*xj@Q2CxeFdu21irQgdqMbB z@^a@2P57p0>I2Z}Lb-#hFg5Dvd-=*xDg>Qc#;TYfO-S;14U_E3_%}1+x?U7tA3i0f z?4cU~1w;!Ym5idE^F1Dtt1F(Mf4Mk*0!c90$i&qw@{q(&gcRU4i?sqE@*M8D5cEz( zh;T1ZsC4((QRDZ6lmv%}gs$K)V}FW{oIYbqU(DCu5{CbHt)HJ_)bv{}MQfBw4Ax2<=A+C_S zGF%;c+9zMChW9*)AZJ%i-DoiH4{DIbctas*uM-dCjh8C*z4IiH=%F#EfZTY=SSP1n6>UqP zP}wY!{&~{Uv#Xv0HO*>ZArjKFuMGT*Et~~>^qV8t-1Ui)>i&6@&-%-K6809Np8hxF z=pjRTu`qt_Z4?k?FIL}8YoIjMGnqf{*_?Qfdo(9CB9wjbEQ;TK#BhAzTBpF8GP=_g zmcFZM@UkB-`#wLm@MWlCiL&R4F*;`=n1|Gy6CaU!J3=Dy7N2MBNN7v~45URyn_#fU zX}nQl5~g32#A<7l@`oS&O>EC+um=W`{p+?U6l$oqTzwSjSfDdUNG3rqzc zSbGMyii6jj;O(Niy6Oz0JaV$Gr(eV#Mbgz?h485mt2va?;bPE0p@LReT~7mFdBus zPxo!(2{2u_K$Pia7CrgRTyRbvnhyc~)}thp4u*66PF!kb4$1-FoO%-4jpw|D7PDJ< zs9^V{i+y0D{@>1vHMZR3Np$}f;9&9R1Qa{X>KQ06$xsw5BPA3cbvz~J^Kwv94k*j) zccha1^h8pv>30x7@#Q%W8;=!o?570l4mf1V1QvmPsIDo`v>q9ZhrmW{G9(~wb%?bR z2aqc(&S+wuA2U}-4?O>b=#4=V*f4;Oq;9t%^<9w$qw5^zHfB8-m>6+UkXTZ-J%7>$ zkxyWD^wLSgl8KAYSNqW~yj95t9mp)Er)+*G@d zdkVS1)iw(VdWqH~3fn$G80`9DkX7jV5F|@H{40+T*3zLF{Ol!BQKwzj0P0%S8YF|L znl;emVj5%{V0BWB;M{q>xjNW@Nl_Vdd)CVEHYLKZ9FPW|Sbn`aN|Vr~C7E+zkN6?N z;MEvsUr$VU<6Es-vR`?G8d)^y<>8rRF9bxYTJZ3$C8&m(Uc`dXQ5 z2Zs$!4+LP+VLb#>9Uk!m&$U7HTPl6ILpqE~lRe)buEYJ*8ADW}l{=0Hk*jc^qEaBa z4+hzT53&o|_z8UW6U{;J))@<>-dkixm*3EVVK1m}fca)^Q6ZGxbI2PlPCA`B$i|MP zu>cf@U%+9sBrb{`tI=5X%2EM3u6^9?`+mFds$tkMjg11Hr8+-KG=~eX>97DLk)ja~ za7iz6!*iP%2I zur*;*`oAQzQ_+u{$=LRR!`tl`t6Hg5G1RKPKcIvu$amym!2Pj^@y2lgDS}qbu8!82 z-GSEyg%MjQS`smYcaJMp4S6YH$zz#ZwEf73nbL+1qPVX0vv0N?E&q0htkFy}-#A#k zrg;}$BJj^F(~C$|{g?XElg_4p>;-i4D|OqVk$uWUgm0?_P_@B}XcKMNV(zAC*yVM+ z;!~;U0qKi4U2;Pj^%{aP1bi9=w8wSc<$7sD2r@hAn}5UQ`t=|C;c#}tmXmM=iv{6m zI*x1ZZG)5H{WQ(=H-K!WQF3V>tR~n5v&pBl!QBq&s*X@|f2CMvb3wXYqwR89D>1{P z1~r~FBp)|+xc%X(8KCc%D->aKw~s7C=OIo;x2$-II;Jy~QH+!WCqhvuNxO26G>M{E zg`#7`AE)$)RH{1_L%?{PSm{)N1{ykMbK>0%v8)5KAu2tjQyIUr=)8WgtGyh4LWs^yK)DQr)E|K?efFnBD+T#UWZy9hL|oyE9;D3*~9NJiCM zxH+jw1GFpcibx0zHZDw7$?S7wSs6^GrvWO%pnvgoz)O8Sm=>1hASvB8R+f%4RUjo$ zaXh5)0}{Jk2j=1NG*g3J;H?TInz}G~haZEUs7^c|gC3fNw9*;^sldQT242#n?)Lxz zwF((X$sVm>G=8KiJHBC$7FhctP$D&E7UB>FoA;ZAf|@%KcX+ZpOBwJBkq+-4*I zY9kFbvS{?161Z2|hDKl7C}l~a`4fD23Qi;d|6t$R_BddrF(YVD+OIOs)#O@8@5Q!n zRRk4t#)~-@1;mpjwt&kWlOW|>nacy8l~gK710{PT(AB4T0;)7EMlLkMgQddtk6Z)2 z!Ne!#^|4(dbjTv=6N!^3)5$+)Ntz8{lf0`^WiY{FN~$?hEu`VU5Jr9msKd9ca=zJQ zUI^aIFxX?^YvZS326}g-pQ7sX*n>=~0?s!hlMCKJ#BhQx@q`@of{+$t#$9G_3j3AD zRct`BH0RJIBvBVR?p(mc7@4>#hD(j7buS1&aMW`3^+%k`x-cpXGPUXtC#P3I4KL12 zsjpV0{y&$u*Kt}>9iYQmdY%816FL#YHC_d{;6RpaGu<_+jGFz#hk;9{ zAg>4?LL0j=yFdF@zJz!l{xi3KybY`1{lEx_rhwHhg1NPZNn)-=H3m~EPofvy*@r<& z@PNpEQYJx!=}?$i036Xye$~I_OU#H5#_1&KO9~vMi(fl*4f!p7$PR&Vm!OY?@=Z=t zEYzTZFvE`XAFKKf%DG!6i1bDlt7t+la-P28%va1aNF-1lVh#g7kbhlINICkMznvbI zyO_s42UG9t;X9HK17u0%s_VS7XN_hO8_F?w^_ALa-=-zf0Rt%eK$5<`O4%sit|PJ@ z5Teu9(qt==pdWK9#5CVkCeJ`<xS|$WzM|e|AP0CoJ^;No3$z$vu(B_ zondY8+{E6&Uw_?|ZMZMeM}t!iZ9;P9hAfg=SPDKy246|=5lgh4>$;y_y0yLw&shMz zy$uf?*_i#Q8%F~AFMFSfTQotteZl#$iJt(k1jb+V<6w6YhYlButrreEZ`IS>cei1O z*_ntqL|q1Ryz|`%ALToi)O+YhK-Dz{$OCl=%TSj1m7Jrqv+Z{|;+{u`HVBQ|CH&OPG-i)H@_}4VmklL)p$` znXDls5CVFBnNVCnj9Dff6oR3qtl&0~+YLj;CRAA|{O0e(GC0g27z{5naVK&#gR_Wt z>KU;&!{GF5+&%NVWFgyuxp{pB>7`Wm_KNEtfGT>c?8RLM{fQ-H6SAe0G9|g?-ckk% z1qTUc4So_+N>Wx(#Fx$X_(W)3EoQSL;u169X z9U-O2vejEBR|>8SSYcty7LnT(q!ZQ?vaMHT`{(@U`++2(s4S|2Z@HY1Y<=903tQvey0HdmZbeQQG6~yPeAya>VsWqz4jW1M}m@ zUcai+tW|-?5`ki?ogP)H_^Yk%?(V`6*CUCHj*uS61si^hF|K~`ep4US!->z)>`}C zltGxRPsfXhti9oYI}kOo5CH9|>yg_&7lg}^k7Ec32hqJ?2rC$MVS@x_$%P3g-aFcV zrG?91i!{?Q)IlcSM^Kauw83IQT%tS-2!$*&{tZ!4Nz)+%@i3B+u?gP=YdMZ`AQ@!~ zWCBtZBJS?)DIjq@(x!9+N}-8~%)hY6D$ot9Do_3n96O3Yj;hKT4;(#Vi36Pg1(X5w zffQs0R69>m zSrpu*9&U6nOv=%{n1UO4e)Q#Abv^RCOH=4UbCefcj}*f36Qzh-VOtK1;L)0gNr(vt zC_}#Thq@m5L6pWt`8W&Kt}M9%1?*^BzHiELEuYC&!J3n&9l7eUC;v&%z1T*(&Ia!& zSgT^=dZf}%l^YEgbg*fGHpD$+$Z;h=TefrBvNy?+a4CXg*BATGqW=-vtcUj-?$zlBZ^~lmAoo6{TW~aT!^a z9I$4^)!8Zk2aM5<+;xFcy0o7u8#l#9hmv#xV1feMK|->{gU%BYGqny-HWx9PQBIN@ zwAoV&FqwQWL`gXy*OZ(XR}=`?Pt%i^{%o?mMM)4Deo|6mtKFlf9UG*?hIe}Uak2uH zc;!6Ji2Y>YB7*&Nv1-A8xZHlUd_PYtihTH~p^I&bho3D-V3;b<;*V{OF8kiw;d}25 z;ntUGyz$^VN;y^azO_!*#4wd33X6sQuIaJdDvVcjsr7l9tUqmZC@z4MSGmKs7nx-U{2@FD(d>&o8aIsmXzg!9#{u5H>SB z6(|-EVvi~-Lb9qB9G*Z~Y!_Q=t;Ldgc(e$frl9vMYlrYcxh_=gIe1XAin+ z71aYGIKZL@ZMEbU5bD=bDj-Oo2xYOXXFBJ6_jQqmq2zjA6hqp+dVVN66CBp~0}<~u z08GG<3mT$2h!f&S)LOm!u$s6NT8ROtb;2Bi`GzBHQjgvm&Rs(t9-!1*P`5#-In51) zq3n9H{0wqD6Q;p=VS2*o_F$q9jng@y->?JV#W^Q;IKyB%rp0mo*TLP{ zKMrKt{a(2*)mP&W#l_fuAJ*Tug0rCtQ2zH>+L!gG@hravb0}cNk=vlo8QNIHAOyU+e|n^IXjcvU#4CE|AXP&QoH~NMV%AI^elwQeZ>o0eDQx8+d8h`K3D(+yejgF4$mw<2)rIwbi3Oi zoWN&s)>oXsUi|5YX>mmt3tt?i)UMk5#BtI|1Ewzk`%u7g&N=6-uhsxZV$zr;=Ds?A zDDM2K_QEmGTz^6>Y72x~6lK@*=@9k}znY)A{i2D^3tgOfa!MgM4y@qOM?U9UhH-L( z&KYFR51DFdYty#8aUmm><;G@XELm=8J^)*0xn0>8%A6#UWG!ithDin^9@Z5fUC$IC zHvt&!Of-NkvrysMus0X**dUZAM{Jp;ij+Ou^2P;*sK@uaEfCG_MV#l@a_-70# z>+bF;WY~O$62*^y#uV%rTWc+LcXxM81)M2n1YHgod7>KPWlb3qydvI=EjhMQwb+_D zM-@ZKT5D}kQHmfS)PHyt%~*IDskWrqeTo=1cP}WGO96w+>q?z3kh&iEUQYk-!cB@7 zFF+7m-p~-`xFSs3z2Lax?!8e>T#tP6Aj&G=sW3r1eytnXd_o~{J<_IhYkKkk%4%lh zq{D2=>TA2^xk80}l=5ipWWKl_$s%az-X6K5=^6;uzMzPXzW1Te0~?6^P7ktf*&7q| zP1afq^RfqHSbSF^Oql0kvaH>L>C=?$}QOq~`PB>Z$wx$5P^YYps>Nf1Ug(qp71Qq$%5!($sCL3Cz(`j4`IY z9}#`e(WG8*JyHlq5fCCkQb|w4n930gusdxHLR^o;@kJnz8W9atLlTb<#uhuq7-I~@ zh3k=0SXyd;pxgjOy|AjGg7 zeKel3mX?+l{VZ#>i2U*A?(S1Iu16}}nx3puOl8%J-?6l^|Ns98H?BuEJy~UOInW4S z{ytGsogGo}EDPfLy zabbz|d#$zhdeu-A9WqO{*l59pjx9gDA!)usu&1R65T>pm0No1EPS0fiG6Q#t!n_W20sj1ii8NxQv1mu!HiS=X2o?2=y0wHWlLn$u^VRuq@ z;Yc+Es+JU>;wXX%93bg7!O;(d5WHw10*fsrkJMfVQ&Ule)SU=|2=>@*$=Rk-hXWHi zz4p}LP7#_6P-^M(iX$;{x>nZ|d#6osQg+?cvYS*&5+L9x-I(SHbhCzE0t9n&+@7-o z25kDFpp(WhVc4;yCI=cSFDGU^7(gq(`j_fmJUG+FwBL#rPfs0@Dw3hcn5!=&B;E^f zOsFkgz{6=8a+F5vSFnaAI*-$oay+urwY|a{pVBEkn3Nbu-OV__0i>QX0pNEp@YxSS zb6*E~=hLg6Rm$=)s{y#<&W}G!6g=5}G!_jAtlhUV@QjhC!NVPWIsuinCt4hknO-^c#Cj_6=1xl~> zsHum9mE?l;j_gr3d7ih0sLKUD&J=<~%QBfCG z+T69;W*r}(mGUjE);^v*M=-@v1p7*Hq@TemKXC-HuZRnF#W%|?jHneHLaiMZN>R=L{=QZDvmWQ zjye;~^cBYu8{1mz=hSLz8rK|Ah1E}^TeC#XvHCmEI#8~SCDbUjz~s{WO7pa+n>~6n zT0c#-s2UH~*-U4yEpO`aDVI-A=z&XSW%t$d>Q7(V6jfL~(^ro#u9!Bgf}kb~EnieF z0`}EXZm@x_htp#Q9rWm9?<|K^{JIS`KwZ8_w`$@Fae~*M_B@0TN4+DCJX+7hafGB= z)dD4OM|*mtT;IpRo#}kPnAYMr0@NYIk*5MtNAw`pM65mi!2+dRwFf==>~BY`;ku)5 zj)4xN)`K|uY){XcIMVH9tam*BP<-M9J)@1SSNG%nGf%uAG%ch+=Y*e@jo9OR2n)yB z_`iW4rPVls17XJ5AoOhFbQZ@EJUm+0Y9N?n`d)GL**PYvz}o5?)0oS2E2irl!Re6z z`)Z$kGmu!~)p*7<*0EVn@zR~e31a%w^+)jVXkDv;AeVLhK!a`d_<;s{>I(GeV|6r$ zP)ZD}Rxv^7`cWWntFPvKHt6vQc|2$tcg67o4R+QQh@6hi8pQW$??drnuVRdIh_Q~v zvgZnqalw#Llq! zC@mqdBt;&QAYGC&Uyp80Hi{{UA&cX7&0o0_DCJ{PVW_pLep^Oqys{ICLdR zbK?qAxth9Ycxb$Q>GHyrXuhAz`0Mf zL#hghVr7LGcu>)S#-noNUX!kp&irTsXV#?a!&_c8Y2vu48>DE^!cqfMke{jpF z9DK~FqjC?{IyzM06n$`LRO(l?rZ;7_Z`t(bN(Lq3ab#-k^FoduP9zX9Vqu8QbMnMt zC;?Ga^~O{$3roAGoWH-n<#J=u-C1jOTkX4XasHNErg&p$8`&hr1(2d2Qe%#mK$k}Q zty`zE=5ATT3_8GoK~qkYjPMR?<6&Ii z@(K!$PdfqdTb*tot`1sQ`3-A5d`0zYx#jNcQ-vwQxcsXDecnOp8{j+2}>F=Uf_5% ze5vqtWP#K6YOD2s&#ljI>GZ|J10tmt0tq*8kRhX{2Am&Eye8~buU79rBtm*z8f1SO zrXndvO<7NnocNiw-*bq;0=%VD4mZFm{|4$+uhyKcBP^&CAp+wqA+w$Smm&J6b@wU; z!TN1=_Xn$2TN`qJ5tJircl?j_V;I|=Cfm|!jhf3jlzpZwA7q-yB(dSe31^ce#wJPt zx{N_8fszq&AfobS=(21Skb{|;gM4+#|NprLnGS9s z>6&cIDJMRv5u_11&lA$Z;ibyU%ZB7>e|-a^^0En|q-IRhmoG9&N0#{@4ksPJCeG~u+ zI{0j;oMapgh{Av<423}$gFp-eK@7tH0ER#mMuDWInn?hC^OxNxb1dE`0DnM$zf9PD z(~Hq|S8HC>cSsr$mbvcsnLg2}MPZd>k*`lu=WVAe@ls8nXujU3BF%XJb>AedxDthZ zL{42_1l;x^pzq;!o*leCvoyNpjGj1Y;GXH*kF4;_u#? zw~w9uW7@{fEyl0?eOPSI(Hqsh^~htbdsWcx4JYevmO;N39x*%cmbm_{M(t2rm1@y? z0+Hk0)c8!bLz=3Ytp;4Kg8%(-o1{0l-u>GBT2&0*Iv=^(^ZGK1i2{*K@Zx7sRABV* z#6nQXuW8QGr@uFjChbxA+*zRiysJ~y=WN;2)4UyyVRX>oJEHLY@tfqdJ%friuhl6A zw|7`K7P>gWutpj%9^grvBQWf^9ypo`OR^~kO;-&h!DFz`rLeOYm1If?S|V@J{KY{| zRXWq(XP(lF^1f+&^RG9IpG3m`J4IGcQL|;5K3X)_8#IRR4HHS@Y%Vu(tuf@xon|@w z#qCp;B>U~S`xl!}g1qbVCsA7+-Z?aj+>trmgXIpVHtHx>36r zaS@OoW634$H0|n;gn@{qwl)SiK8yeCj_)OY5{&Sxz@X)=U#4X_84N=YO}{AegziMs zrzTg&t>OOy8xyP;)m*#Bd+|V)7{FY^7)$MBf6EUymPIqCsBi0Dd@P8+BR97P;Hb}5 zJ$~glTAC~rVzs#;=w{6{ks2})XLOCltZ-$B=SlN(3xyyt8TAdTS?sZ?)4M!b%=Mas z4N)aDKDZK>?gov*3eg;@t|o$7nazYk#aN0wY+><_;0|WSdl3xUa1_Vk4(XVvGt^Y;=OZ2ul-ayTE|>MLLb9#BZ1P*|YU(zP<}=Nejd zp`c|cM1tBydoILrs(b>7u%+9mA9Lg2VK!rN)Ft{C)*orT&=4hkN{0`hm+<3%;k))Sr@nS%v^ z`7BQ`Cjd6NV68Ad1;vHwEbWbfBD6o=F+)!_^>|p`E|DJO--mC(+-SJ>7|MhI5tfX; zCiyJ7mzR}F^k@xw+;es@K`a8us?nbCSv-EthqvWx0lR`sVI*MpghfQngM=nS4gkMp zwHdQOTv8b36qwTW)NGQhEqM&tFFO!?v*Mi`srCkUXv**?@YE}xb@b$lCsmF!L72Jq z(jp6b@0!93^!~~{Po;M(pUi=qqr9n-$F)r=AC~wTi7doN26W1M^&&XlxjWe%@q(#^ z;W~Xe__OS?6YGs_f;jsaw$mdse>A)6+#5F?g^4D!VY<<~4N4GIr*RSNyw^@_L5+0- zc{BBKz=`HGsl&IZ9R#Hy&z1#qaM#bN=K4D1gFva~m1CnyiI6`B)yBEZ+{lF|HXsI5 z+T9SdBv-sxsP^0^Xs4yfQt^MtdFkS6{JikQz%mUL%W4wnJVO(hB~Y|@kzy;Z+$4Lv z4GGioIW*$$lu#&-4Ug1UP7`upRoiu2s=!VkWy(LShm+lpQ@sQetOyVXwcU>~>x-CC zz-20LTrct?Vf>5lResAp7!4QWL=H{Y#}}6=?}-VIqpFS zcIUxo0qL$wGtxHo@onkw@q4Q1}7oAIkMjh28_hmuvlc?ZV172N5*JgHQQg9vt+Pt01YIA{b;I z?+6z4=;>Z$h&1ppZUq(AHpyx$ZvM~L*EP`+`a>_0w;T81JJpHkGy}s0s5r==fpgt+ zGn^J6l2|Ye9tIqpRwtBCdkN^AcRFixIK71_!VV@xxZ#)y3!yMNlD`6SL{He4;zX$= zjxG?`@#pw!%z8jFFQeV0dN{ko+mk_<0l7T~Njl+Q&|5Z8m>;kK{{tzJUWi={Ta=GJ zIDMmv+)f}dKe3G^gu2hVRX8rU(9CP;u`UXyTkj7z!E*w_)%fdUKB8FH+n5f^uVN!- z3gmOZNxa3dLZ1Dl#l@|eW911`uZQq{C9pp=oQshR=v#)yP z)ME(*AaFqCV=3(QN7Bv>`^FArdh7{od7(*wwx%o0jtBuAWd_Ra`Qf-~c|OLT9ia61 zIv^PEj!Xd|mb|PiRWC;@TBB5L$A)kyjj1mO%eqr}fzLp-ddnY+CxIzsF4Q2(7|9PX z|N0vmY$!Tk!P|R3_N+`Nsvc=9GL>*#Nv5< zm*YgB6eEqen@Y_w6nbHpp#YhgR#!Ag8CfVGv4@g))<@yqmGi<)&$?kT?GpTn+O zL8UZLRI7s#MoFUoY)7+jx)>|!-p}r*{kbuA-^w5lo-W$O%Jp+AkDp8mh|H;GEb7`+ zp~lKmod7(#>{N6(_HPr8fB&d_q(wMnttovyFV|fT;WI$GB3+FA z)?Meh{$F5=(rb#SOaej8urn9==23l$ehkeIX!U|u4C(cMgB!JKrqUmVyhj8>e{G$g zm0#oKp((Q)>w!GQ$rjl)z~ww30u>5^2q#7#3@L)6Q=xTLkp>ZjnnT9#qmP6O4tQ zf^RVHpz-o1rm2p{djBo=%GNspmNCpchC`dyP^5ATSgQ8MZ!j_ju<4SGs$bxBLMTmn z9#pO~wp{IrL<6L7;F(T<9ZX^KLv55GDM)ik0&y#9Y>?xqcXxT|9ef_54EV`_1c$ZZ z@WriEtPKsyW+tUR&ehS%EgY`PaJ4OR%*EJCR*%csA-K|$gS0r-O>w~(?CsOk%Rx~n+2eW z=jlZJH6Hcy5d7}2vu&!uL)T0Ki`%KbGcBdWX*;j)-w&%^e5k!|;wK58i9KeMT zgtIy{D1o6Ofa(6Qi+%Q?!4L^kOv9CH-Y=Y7z7SAXRgVck&Au2nReg!(dxG{%m~U0B z`$=I!mU;fm*`J=t#vdNC=SDB0`70-k6MMw)PPL1op)~=rm#vzbpW8!NWtt(Y;{U|= zp5-Ew8H@&IU5sXwwh4;Fy)}&q(_XC8i3Q*f*W*zMcY;LKW?8}=tF56-R=#*c)qYDE zeht@jlnbV?OZ8}MA^)xN49AJp_^9SY^Lv~r5#%G<$F%`qpp!8(mnr!U1!qhJ{%uYb z8%*zCcAOx^Dvo4XbS+&!{R=Eq4=5YCNj*(qm|kxI(Lun%&ITU@LteoKcFf~7_}YMW z{2vvCV~@l3zXcOhApiqlK^Y3oGG0R8Y6K!kX^#k4TUhrz!`&j4XCt%EQHVBYEkuf9 z;S^+g4kMoe9$wC&<;&MiD2gA);LX!8x5)B#;pFrwmLU17x%Ex#)14K;5H9HPnw*qZ zJU$D4XarTley3m5WpHw{m%p==n)r5TP7YTx;=<8y82ro>dHbC=raAih)JXn*3aHz4 zD%QGBxsWx0oa5X#V4BoGG~o+7NdAKKrh{Bbry;7=%s-yo*c44fVjE&aT~&miFYCJ#Yj#lUvox| zC4$7A3mSv~doNesljwr6z?i!^BbPTd!QW_K7>@o)=WMW+y5}C~-b8B|eV_@Ang|kl zzfj$x0(bkR4_*`^MyU|8-$;QY)YAu?$!kDPLsb^J23iqiS=*h44Tam4-e3;4TZ7A} zo%T4nm57eZ`_0rue6p_OED)=ALBz`78psYP6+6jQLbPs~x)lr!UhXu!>;pP$G>j>; z!r2J13F$k>9!i%0w-vG1l#NBDS4M`%r+2QXrAq2h3PPCPmHGKj49+URk#qBVf~a#<_pWp&$pdkr)&h zc^5K!g2z|7MstY`f`B2;FozRX6joxO{7c3hcHxLcdw=7^u_PpICYm|rpNo*JZui>U^cDMh+4O26Of)4d@(* z>%e5Y6_MPn3Dc?NrKQ)K&3s#R*>O=D0E-75$+*emuQj0QtrorfswWt1jeIct6AW^^ zZ9`@>l>yj%Z)Ov9nV(%^>|kP*h-IOX}EO} zF3n+QDDKNr(~5qMV4xqqT)sO?IKS&{2F;Dlm=FVQjg}W2V6k==<@SVIfG4|bh((r- z^MLi&N=|JSE%)`6YjCc6FJEVDrQM?=KG$|bax}QeUw- zyF7XRS=`?(v5E6^nuvE122$^Lr_U|Q?B^ev+GD3-3q`LW_tDxqO`t0o(9C;w^!G6pv6XWRiIL`g!qSl zh>7K+^}!h-e1(UkMi+@xR~pr|6VZH7Vk7?;=ytyuA;3;mfWB#p1m%U5*2Bl3sl@)2 zEB#sp1?5-Q8x>?y!JxL-4}5y7uTcAe)e}hMU^J$Qf(-gk2zfD5bAGLoPC!23tFyfr z5FoZ-@pdIz-i%Sv0~!1IHLU6N;$)jyH&PAAqp!ovx^s;s7HzxXj4ei;0e`K-VLX+I zC&w(Db~NZ+7-YPONT9(rc_sKq@vTnh!+7>n8F0}G!7sL}-wfm$04U}|LdxV`{m&+w z1FyQ#%FsbV4@qTB95q^<`=Be}TVm*T+?JabyW}@(=l6$84LA1agJay-JqF3K6`Iy^ z!XA7aR6Itx0u(AXViluoTYZFW!RLK>Cm3;xzmt<-{$nB~rifeMED{@$xk>>RGuKGz zHX>^0LVsjsyeKU>-u?+BwH8=b&hg{3Sv%tQukN(*IYbPICf4`iDQX#~5+iICVdwRr zIE1Rfeg20h%R_|{RK;J@^`YxDyj`Duu>z{MF@c5et8U&)NY%esy3keXOV_~opEPWD z;+E_kT}15M3Pw=`du;scoDl|9gidghJJ>%yT3zm4yk|FUZ~M7m-&A)pbCt;bGKlhW zpuILPYg|+wU{kCDQmCvZKu*we@VXq1ZQujYE~@d;$mksb0}y0xz)&jj2f*+UCyT00 zCVZ8gPR+EXjwN$6X@)-AWHpBMWBinOqmDO{91WyoI*b2%MUI>vePM`+PNXQWv5 z>fY`+pK#C%m&Hv=@cf3}zV@v`ujZ9JEZEFf#gqzS>Vq8V3`SeLDk+?}syl^Uu*zp3)r8(}_BgEX*l21Q4P~i&K5sa<&x2 z{MMA4*cYkbSACo2Ie|-t@=2YcMLJIBCRQY#i)swvCUIgcHff_Ik$<)rJ$-89sl}Fv zgzv4&0i5VvTvS|gwl4!PR)Gdn?}}7&UsZCs2K1`64^^m;^J>m=7jpC7P#*OQ2DC*! zw{`jJEK0pljpmG6ebkXp76&!4z93>!nE`w48B;NjjT0JG(qZDF%vo0E6lAdu3kSq zn=YGSDWOCg=;BkU8&?dXl0x93+C0vIUwFO>qN4lgEc!Nx_yzy^wq)8Oj@#exHhhE@ zA-ntm{&suTH^XO$SsNrbET#UO8TM~+H^O!5pF&pw&Arzi>uQscPt6$$bjF#^jIA?3 zcBV6DW*gq!ckkvLCKUKjKs$-HCuJi6p_aj@VOBE_6N=cNQ9G8(ohl4TQK7pQ&=j^( zXFZ1kkp?ETg~0b!{ej_th*K}tryoKVTc?u|fC;}Cr{?uOG}*XFt@X6OfTGxpT(pfR zn=wWk5c-7?>c?QQ;u&+0CwCfd1*^(KE-+urM?+-C-EIQ;dFw9kZs-npa$fB!m!lE# zir248L#lqc+mnxjM_{-I!kob^G-=%cHwU4rA8RuJ7r4TA4%|+-Sp(tJb*uKI?owN6 z$szO@$RS+-=zV%2$#d;eK=@y_n&qq=DCf3J5C`<%(^Y<0PyfPK)g8=ieVpC~_jUGx z0t_pYJqu&J;uLG_PJSmB z8)ZR2o5>q_;c?lS$#VUs=TrX0dCXih?Rq@1%H!aE?;8tQEgxGHiy z*;&^cR2Oo5`rf5jN~bLFge=TxkyJf!7gn%Vc7+V~4*hv@`c~axRVpsZ!)fO9imBaa zqHBZ~l>$FaRVW4hfK5fsMy&HF3c={VYPDw&*SSo%^?I9H^^Ue@Qf+p}{#i}GCWdrp z;?8{#S48?VtVsFUi&K!{cR&{AbRJn1uGdbu1eMvoDOTmt2V5$wivpA0d6#m0!a#IU zD22tk5x$4=KP2_glDvZP@mPBqLEsi8WjIKIm^FxT%3GxZ#TPRyW})B-=Tp;)6;Fq< z2_B>hGZ~RwkJ~L&1(2^Ng5Hoo6E`z`YyEz?K&5qyHufLReX6j*d_fG4V%!*8^Ozf) za+|Rfy+O1*Jc;H+iix|Hr=VL_3`MR&0O%byiQJCyj=`~ZLw^9*VkQ<X@gxUJilRLzyI=DD6~@r?SvX4x-DPqt=E* z+)l(lKdK}u3d97Axxj+Z{^=7gLQe6)Uf@jL#*mN=$mUgYiWsufk9}ud)=?#A^xt)^ z#h6#WDQj(3PCH0^!ybMk>Lv%YeZK~v-NUk3=gmTN71~mlB^4oq8L{p>%BP{k0+bgk zAsKzJxg20yIH&l|)mb+{LSY{w=d89$coQul`dGT-s8mTD5Y_35$ktZetgqd1rbhrm z0=jMi%w$nIDXVbw-GS=z@(Y%-{uRTl#p-WuA*^8?$I7-K+}^L$&ZG+jr769AjzM8* zb1+)ZS};|C=gGg23m0{c;-g@-&OZ>&SKT7Cen$3(w*5kmrJN{jX9s}GM0_2nSkyeI zwMcLhpdKsz_+OoBLayYaSsY0?I|SkcNatr`IfxfSj4&a`<2Wf$!|vbGdY2U2&X`|s zKjgl?G>aZldW>XK81OloZq~KfA3pmaVP&+Medv%F-ibHs5sS(h3D6V~76*Ggf(Dut zP7A;|%S1@ojE6F}Ed|O|L-@x5*~}kFLuS+zecaepR9>9aF^B-6Xl+Djw2j9?syc()xvY!G%rG&^R+vvGLlN@T*J{H zm*+y?ReJq#3-BIPCI675R~q_XKuSdV^9C!3;=qvNx}niI4D+lC!Zxvph^HW2JIlU; zs@(cP<1a}$i~TBwOh(pMhsBRO_n2k>uLeviA58^Yxc$)T&47bcY)BIn%QaRD&q=w>XRawSAj3YF^ zgrHmuVr)&P!?qd-Cj23;`e9ylU+QiuWE;56aApEWKB+;K3u50xki`0BBL(9DKsH6( z7?{!_qZ`MleU^JO4`Lc_IAP@~A06C{^{bqS$?%!R$WE!%F6s3t4bqY`oK6m{AF2E} z*(xI%L|F zL(l?71HNYwMZ2LMo==u$#v_964iTCd3T&>j)ZK$8eMdpF#}s5DO$-8=uRN0FT2rbt zEILEGo9GQSwEZJGhX7$nsz(UnxrID5KKw=MM1WlT=oY`>7A=8&(ngnY0$ZdTZ<=c% zYIw19Y&HHNaP_x>2X{lTF6_(u*$;rH-~eqKH#@%kh@~2Hk1Zt4rmN`NPZu*tEa{w0t3P4 zHm=C0WttfbY{In$Uia8@kQX9M+KN*C;iA2;@*@Aq0E^}QfQ?#MwCM9?UkG?>m-$!w z`+(HmQ7PP=lcP3sB&IJ0a^h))xFyAmzz?aL$=i6qKsiU&Fk-tdw&&(^T5gP@3t#1_=FT_OS>1p6t*#Y%&F_Q$Pk}Wb1 zR-rgH`XUYAN(`Y~$TC7CF^X=oulkxvtPbTXI~<9kje$eJARZXn#=*fvQmQD2mz{@Y z&$clcmm%x&&V?`bk~dBua7ZG%x$bd$lZ#+s--LCtusERKxFbf3u&0kQqA>zB!{C6A z>>9X&oh3%zs$&{~?aMz(`;F9nTgV{1^r0Lh23c|H9vMaP9Y?6#<|Hc6UC-!#Xt^y= zb?rhpmD%&QtvtEC{b3cHy*MP@UeysiISWkO#R@i56m?v93u)9M!61Ni$Z}!yx~;lF zg@3*qfEI0gejsKZM&!oH?&RZcnzLV9FH2FjAiJ2A+$Yd|5*?9Hyjsj01R5qq@UK}- zqe4NtD8_-o+Cu^Rg<)0mM~IQlQUZ`4Z`U@euu_(QRz^zz;+b@el)=Z(9iTX+fTnr;!Ds+Es^&>=mClXGWN%Ay^BqJX4OOO8$lZAQ1v- z;gk$fdgx_NOhw{OpLEJdJ%Adj9)VfA6(F*y?&Xg%pc(QJhtD!g}jaH?LGpK1Nszpe%9P0g#i(=)6hg0E=yvp zJGIA|oDx#-nT7jbsc;M{)S&+)Lnsx_k|9cmu1qi{__&l0qsQB@KUy-1Na@S9IUMdZ zKq{H;^jFXQaCAH0C5BQWgi^vG83ogxn>8=}w~A!2elDs{IS?#B-piKl>EJ>}W?*;z zLjXsH0w)GvWaMiAX(kmW9K{`nk&#NmKTC)Y`;9C~eiG+%#RJ0%1xcEbmFtC-Y--3o zL~WRH4SIbO;cr+&|MFi%Y_9ddG#R*!q8v4Mh*y{Tt~Z&43dx`)LpogPwO4>S3AR0r zn1)eQ0xzF80q{MedA5Auqe`b`QNmI!%2<;^u`7Z!4B_+PH%BEh6`yA!tNX{+4jc`M z<=`uBth~25*N-YfT*&$1GCh4rt?dq4vd%n;pH*w8#y%kd9th*7AdkXbJm{?2;S>pB zD)V2q)&j7rttBJ#ys5B8zKE~v;w5ib>sdzMvnM4ezQ#`UqM+H~Sp~=haFj0%E!Yt) z*WJKML)25#!w8D#F9rZ6=SW7Rlo@^o`lF*aMwuAkIVg@ym9BlA^QqNnj+@u94vS zC<4B5-9>xpiDAkdmy zBo+Xck_B`JJSwfEo@pGaqlqsm%gM7?+VV0-RF!-jFff8Nkhuj>W58w=#o4V~GH_if>lZlXVT@`vqEHv2KB+ z8~G?BgQ{fr5}^EXXGV#ItwP7w1PFO$uV3-T@IdVf`fn=*DCH*V+PiJVmAcSZwq!zHf}wjV zR6ghkr|nd%Ys=o?nYayc@el~1aydE08IR44OWHmlWmyS}lp`!9Ts&Ituff3%GsH+! z#nyx#lr9ddP8lux69C&G50%4`ayNqi(U|5x(B+q0h;dkFUc)I+{Vym+e9oYWzJ`aZ zHyaTywRV>~gyM!@WLs8cD=i4Tx#8&?Bsn{aZxlKmz>9%G=?!fvzdMv}zy^b~guph% zpPoyrAh9V&tQA|KPcZQXiYBf?Z9*{k7Lx{*ZQdnl^obzfpU@N^c!l<1=)Z280SJ~_zPU}Wi5224qwfidMvfxu`9Jm+HO1Z_UrgYbq>ggqK>;V0TF9ul z0()3+NCQJCe=H%e&}$vggbKOeSotf~cs61Jhpe}M+GkB-J=iE2EB}GKiEA63@TI@E zsk4-!P^P7cHl?~>+%S>i~7?o1I<~(bKkBFzLJCjkXG1gJ$ zqA}n~olZ-rK0)=fy!n}7qt0EqoITQ53)a+q(XuR0wbE1USW1Lff_M~eVi236nB6F5 z37oS6rCb79S0*EQ8)*dX>ak}>_W_nFDc}2xxH>A|O$BWc02pNMd!!J_?9!r?>eJ6=784ZvrNd(Tn(goMJt=gQ zw5Db5fQ;ikws)Tl?@MK7id>TaeYzB_DOp&<(V)fPqYSsNX=<`}{AMF7*h<|8DnP7# z*ecr4=A_G!t&uDfp{UK~ro5=o`I#;evRmLniKXn47+{n6s4%1q&&TbT8VWiy} zK{d%QRD|N_+{#>tqjq*EQBp?M*2v=-Ay$Iiz$gHqA%$jfw%Zb#JB1c+K}f=MT+pwC z1PNfA>{(r^=@{#Y*9!N}7%7K~rEX0Oa%4Bx^v&v^Zno37Du5SAzQd}I${8d6oDqeB z=0)xAz0mkIbN39^Bf)pNeDc08YNu&OwQEW-W9BfrN-J+c&C~ETsp}ZR=GM^jU;|+f z{|r;p>9zJ{a3?Xya<^8%W?<*zRuu)gVzqwnUlGZFKET;kZgOS{#=;JHHnY6pf$XY% zks9qBLqa)&9Qcyufvr*0vTnRe&C?DuNve^ZR_u-)S4*4ASa>{~4O+&=08CA%ly^Dn z!&VAg#GvI^2ltfLeDR?qmIt3V`<*<*Oy01-^Y-i|6+P2P8mFs+F<;RtTFU*tP`|VA}`%u4F@bXdducxB&Zka)@)DlD})lmGJsyp?sBr=xOEckO?aZgoR z5VE$i&nYKd!#%h)B&p?rmuB9aE0K+lB8aHKxZ)lI)?I1AgqY;pKE357vup^#?H|sg zaka*Sg!+Y2#=r^X)aDNpZny6~E5|W!kVY0qzHQu!=#$>#t8650wB)s)=s>9Lj`1c?no*JS$Q#Gw3?lXK;rZ>c)NJaY!KBLs5nwx>E9uH)JX% z=MqMrck^(~WVEIG$G};Pq(m95{QeX=*cb<#E7)QVA+B!177q z1YMahG_+6KTixc?Gc)L86*$vOkXt?@Q@^ms^E6GOKy5HogHSH^fyehFo*y-U=oS(^ zGrbwYHO@3BrnZIz2mv~}xGGX$PBC>iu)&H(zdr^;QRFX*Si;k|#tg8a0)_#?ceXIH zy96>6Hc0RnFStbg;6`fVk!L2JnR>)I-GLvZd9W_nT-dH{^#M)U5oU>$GReK0?6(4O z0&W8C`@T9wf8)myM3$e*H{gA9f<^c3@vj%(7!a`AzWQBu?Cy6%jFC-6?1;RQZLVJV z@DYQ^atdfmF(PbXeK~9=9*puajz82$dILLh%E`JuM>>4OAhKKor8yDSLgQ6k+v8xA zk8!MYN|9*^lJYpoxGUfz29aeIJS8!AO;#$Hb&dcHy3}06oh|2_YHkB`#c^#pbyU`n zVyqEkFlp8hosp=CyH=|L9I&K!a)#DIX{o7Z0T|_D9CHw3qNSAJ73HN#0cc?iIe59s zxOyqA>7YS3e8eEKd}Zof`J$LDW@k_B(QMMhEx822C7DfW4eb-`!R@Jrju`{JmMwLB z#2~W#q68~}p+=Q_ktK1kGFtS!RIYV=Ph&!KRxNOD@S14Ec7R6t7{^gOC7rZ0vofzRoZiy#3Gh&qDySznPGB;-B&es1YKaBy zEBP&?p^*AibF5`BTC{WJ<#nCgkndRgv#_J(4Rl6qHp z8Y@iC;|M5*!=%I7H-s+aOhMTMNnJ5T?5A|Y$b$#_%uZuO_jW%z0y3g;lfrQ7t09l7 z+036OIL$@bsL;A1MeyoUxu-FH5{Q{|$T0*+H9D>O%o?8AX;o$(U}mS(KAV=cYi{r% zD#^f9CFJnB(UvsiMs_nphuAXjco@5%$q9Wu-&?3(|L-xDT^*B|1(@0D)b4BV$^GxJ zm$H}apa(a1%q&$8EvcRRl8a-}f=jD0wKmn1yEchN9(=dXE%4Y2oL9Wns z?+fiM#88?7!=g&jq-mJ6b*fg`4^7l&qKOy#uM2k6uN%X02$*Q(z-P9K>y5YV+x+{V z8+2lgc^5u#2RY|KVDDeoS!>6m?Y;SaB0MBQ^O|OlnT6&B%^`67V_d_AnY}`J!9k?O z*Evp*T=&OWq)h3nV?&DInr`N4FIpV$P8W$E$d8Dt4~RzSb%7sq4!xJf;&oql3f^r0 zzWW>1;rEMS6l906_;|r!dj#&^Z}SiN-dp$|J+aRJ|G&RuW}_!z)MyjvlJo!jT}b5r z0fn92`2Rf4`QCd6`QCeT8PDIn_alM99tXdF|2H1RI$StDzOio7Dn)dx~)#O_Wn9qp7_{2&iw!X z`*qvuP1Ltl@15_N3r{)@3${mfIG8QMs$z2_Zv{F^_L~v!eUnkQeo}j?}Jtx z*kP7)&WU|ysbT&3`#qn0e+xWhchtEOTtwRK>(>{6-FrmB3qebZ(TlgSBO*P6HHNUz z5HkDzh<9eIE^zvvKf&F)F?V10Eye+RZ?0o7d3|?>@stLyC2dB=6IH~2%KTq*K}|Qm zaTkL{xC*u>91`Ox*8lIWz8EiTm8zKLt@z@=dFpcN+Fq`+iCj}ZTnOESpR~* zGA;ZC|AW8}KHnQ_JKsd;9@8wGS?C@MZGC&&-g)X*fB(LrJRLd|{6TkqDTp4nvii+i zdjI|VrAmU|ca+{sB|bfeZbo)?tWe(T?pxS*=fWzbD}_7Df1_*QRT?<&e7#I++6Rm( z?I_Y4m0kd0`^Un7M&aAYK>hqKJQg!u|wk2Xea^8D? zVA+?W5G10)c7~;AeUc%V%?wnij+5%^N0KTC_PuA;%&14-y~qnCT%t2WH8)PYrGN;# z3bY@Gw6WX9+*N>$`7fl6-D2CaurBJi;|D_c`?0TP-*%h4F6U-Sy{RP=G%GeDNFebv zk`F|W<8R+i-B;jwYPm^PWuko|yo86|_lODuRM^8QLdv4DmGSj>C>20*_Z#Uoh9n#E zcgK7`=X%3A%Np+s!;IF{iDY34+v8%1l7I!7QeiQnxFBVsT1^jF$KaFELJ@jGi?TE~ zen>@3Jxj8wEUgFCYC?g|o^B;Ya?=!fP!=;|GW49|37MDP}VTBWhs2Y zFDSmWQ+W2!D9OW-nf$|N`&h! z`UX|oBGdB9dSW@BN>@czJykYTGE7;mNLi6Y)qsj~%6KwsemP||MKLj#jF>B4;3%PR zQN@x}RnkHaY3}H{L^BFwu~jrh#58my1Qe75H1k8%vV!Wwhp&f*iZ&kVj#MX_Q4+;zSKg(u9=Ciqwb-UJ6_Z zD@xf33=^ypDC7$g79&1Ck((chn7$|zd{M}~JUzL*wHjx5V0T(}Qgur-mUKUJDdc40 zpiaO+nY`_}(am6MOJfteLf5EdG+B|ha8iN5iA2tOV9o)y2B;yX z40J8J8Okjxs&;dF|r2#O-CKV7(Byrw{IS*_ds2rvRbP@0z0E);IU>N`q06IhE z1{e<}6%9-za9+n82ipc!I1P4jAXb;+O*`p}^olpfg_Z3vCc-dCavR}e9Lp`p2S^;t z)uk2wAakC}J;_M-3w0rNKw;kw8-f39IR*>ZUK zEQdJCuIt8Z%QGa(d?vVwXPayC{;36p8Wp00$brV0f5BI3y%2Bn}1ypFM#q9_a^S)jZCvchxE*v1yd;T|8`*u-(T#>Y;v2^9xxd~Rb4<8Y6U zZEWH=T;pROn;3^{eC}f##=(mC+$%O=9Io-Pk1dSDJwEoajpJ~S&wXrS9Io-Xk8QX( zToWJr*v4_V$LBV-Fb>!F*v2M|gFQa?v5j%K#^*kY0caU87ixs5H3gFQa?u?gc)>iBOpZtcZyOkCe%+Ku}d*K17saTnw29@B2z<+!?I z+Ku}h*Vo0go4CtyeUE8B?qgi9G3~}(jH_!*`f;DRO1rA*xRr--;>O*mSl<;VZrqQt zy2r_l`#DzkIQ8Rxj`clGeB6(*zQ?H>cVn!sixU@dH^%B7CqC}SSl{E+jk_^c*En_K zKF0bQCvM!0u|8L>0*du8;AF1$svClVhtO^DPvZyDs%5D%#)w2~SM-jtKOXP#y~b)B zpW67#$6X#LehjVSUyV0mEcbC_jJu)Yxmo<<<9?3GHxAeFSB;xEF8Mfe#!eicZT!o| zejX=o9A3s>zj(tCmwJqx<1UQPef;O+E{&5vhS%}0#+xvf`#3Vj-7ubg{Nob$GsWZ^ zhwJ#O$88*!EEjzmX&is+H6F8ZKaa^b4%P9m$88*!cEe3Ti$*VkuGwn~!-V6!@iL>1 zYvn97AHUjwAXFM*<_TR2yRQu(nHcx$9BmLzyKv;-)CS^So;pq*YDL!u*5RGsG{!P7 zr1la6CqibmAsz!@i><>nBq`C)?IoXCbJK-%%il?U7Pybnh%_FJJ@Zg40$+0XNaLA3 zc(hP{8w4As{Pd%1I1!ye8IS*C2*^Sja$93^17*a+eU4%&0RNI$9`Fk79>R+mfIxJt z?>f`Hxth3Os|DSs!j4Y_tTV&`m6$*Q#b8j4Deeh{M%kFhof$e~C@4aH8&`STk|Z%o zkegeIPI}Qe!+gM}yN5_H06$~`cumYB5=>^4c}x+OIiIW(4gDHiC8Zmhi>KAIZB32- z-Vv+9Uob_I$=q}%?%{~Trchs=MkGqNIprxyWn>eK?^CF9WPwS`bj*BDH_C|0KXMnm ze)Wqd7OOGn{zP+agZ&~Rf&PhTgYX|8w>E)dl~}Pi=xGsOV582KtGe7;9jq(I&%dfP zz*E0KWJS11?uAvIa+&qLl=ifG$?|Tt>5a7RCMdl0T&!f90W!P@?JT!HK2XNli`Dm0 z)OYg;!t=!I{wZ`dkOUB!gKdcXK@s+5{i<4tk+G2qVlIqnVwE4%6-JhV`4PgQMcpiAe_0z(HH^+Igg zFYPI&I8331OE&uwznyzU*i}1+9KJCCV)a-~@-p={*Ka_il3m&+s{_V9Pz`d3C!k4tm+tG7mioe3@G^ltT0ZGg zd!CR*k_-AvgJ~)o&iA4X=?|{2SAVYrIU-IR_>#TUjO)t?rzFKBSkP@OA8x5S0kfB6 zy@*1gte4tZPAn|BQi*ySA_-&HY6T2-#X4FKs`Gh&mnPrqY zxl?h?rE0}FcAgAEM;-v|-@^c~L5w=zlpNd^c9mU$Bf7hJFvmjYVe}(8BIV~Q2pAB4 z+_^5nwlF$WXEm7vM=JzO$nDh4=7Rnz_1Ia=MTTih8Wzc$#->HKpgb0>xw6=W%wk0` zo-`jB`x&>yAmmeOETe|gDZN8a=T3QlS^Syd5$HC^UKDZnY%0}sa$JS|cU{2$pf$%( z=;J-=D@Qi-oV!9Zj3c~?;!C9(ed7e_#{f=DMZUtyGm#)uM(0xs+%2~Kt`kJ%?@BH5 zfJ=-a%GeVCmsQ``9TERf)m8!jEgR;($ldIW6O!fqYzcmbmfvap~VlgWDhpPA;Acu(+G-_{AAe$_fL zd74K25+E38i_on9k|JE$ny4PcMeL|kQ5D34iyTirsJY4tvzH4*?B^=5zXSd`=4?#5 zpJSZd>SXvY$$AS@9BdoGt7|hJQsGc$b>~4uqZ(I?@DVdKzr_EOd=rMI)te<59^9kjlWF5 z2-*6J>@)ImlLcO zU+H#Eu0L%ybn8FT+TK00Efi98!q5)o zw%N1es@({Bi$N*Tk4%LC(#dYR=B;zuA^Lfv3}g6LB*~;EnwNQ6^ribeKDwVo^-JrM zMw0%2oB2cS7ohJCj6(L(l1M_X5%3OTQO8Jgucj$nb^;j`TCG>f*Si2WHF+)y4q)HA zcODekf&z}36kwZwl(AJT>}R66tWm}dJrzoL558ssI|uFI^|OTDmC}?!7S$(Ujv>=VXBdLG?kxIKZI1rD~K5#*rz;0~`m6rK+po51X zcvHz%HeEAAO92@0dsPvSErxGnT+Q_7A0&E^C-cv!<=)>#{G+<$UNl{kGyyBw5>dls zz>sl+4tV5vVfuMyUZphrjIn{s;?e^;Ev7q?5fv0duIRh{Nww7@Kl-fZsHr;p{^Ai} zTJSeW+aHsB$Vmy98eTHfh8=Rh9RbtASKgGG2&X`d;ey2xcF9Fyp!HbeETZ$Ka>Uix z#Rq{0q$Io2z2sPNGNQMjjz-gyvP&g9fvZt zQ%)0(l#r+(O7a!!wVUQ!Jxln~l&gk|KM}4{@+dVxg)iRCfjEX0mqw+mHeJw#h4aU*vkM<>AA<$$he< zYU`kbV<*VF^YFYka?v-3)sBcKqZ7qw>r$;NIrg+GG{a`Dk})> z4e1C`HNZ5}x((D9{f8YYFA~bJC3qDSjV^+)Ylx+KM~Bb*-g|Qm#wvPUe_Hy-&3LS0 ztM_3!(%E^V9B&3e!uS`U9y1GKM2)!hmxhKm*bo-}+fQcumgeI@bN;VLIzVuVAoar= z=`_9EcbmC~T1NKL{)H2JxNcfueE3$#XbUdRXH{Q2{FB&*x;Wj(?R0;I^CkBSk|za| zRuwfQH4KC>M4 z(bwCzzU!WFu|xrm#rC1Ur2oNY7lxpC6cBYNKmduU|7T#DQ;4XT7CSp9j9dyk-tlU_ zG*||XP@Je82)h45O*Ht!3-s8340qFJL3Nq&57I2&+!}lnsMh_!#^&enV!s^mPZF0B z=>tf#@%o^T}^Ouu{r6JaECd3{9KYFrVnhiXJHu@gYsRQ?dh27y}Ev3IXgjlH@ z6}9z0QcjGGVu^E&ZV~vMPLKv`G#r09Z4kO47B|P>@yEoMTJAwpoy7H=A=3wrd2#}O zt$~txTe;XtkhPkf4^Puoh*1DFVsgp}iKb@IguyOuONN?XVZzN)9`t8Pd7v{8k17c! z4HiR~G#m=-Y$zjn9E>t?}5*v|m|+&6SbYou9()Q3ntT5QWH7k$c?0De0p zIhZ2~Yp!D69Nz4(#|imjmdD4R!JXRXr9U})h*o$)WNe~OXauHw=rKM{1{{MNRCo^q z>i9n&&nEo0lDOEgF!z{4MkwKa3+q6M=uCP%m}h2?Jn7c1i0$&{;CE3nSO<)au2=j)Lcqy_H;;rJ6RK0$ZCWOXv7y=U@B?732u5afk zIfR+Acp4asG_k;0%g|dOgNk#?$~v;$A7(>Lt^J~A-I!BXpMtoy*RGP;$U`h-o!?E;3mT?aWRw?L z?r)M^ew0C-H2&t-&e|Xx?_# zS^o;JIfXclp~kXfi8(+XXhAZ%;XniJOtTMhieK`M5Gt@Ml!y2<*_<3>XJfosZPz?X}-vcVDT!Ws8K8+DF)3h37_ zd~>+0M0dw@0(|nuB`(cVpSA@xBT9tRaP!N8Sy0KLsQ%_ArudPtGaZ9=*3vpL79wV+ z?ApveTOTzD2{bASY_OAEb$M9RUWo}>8WiweF8Gi9#M`0iYW{p)a!2qqFlDJ=EiRIU z2eC`NKVSz--m|>}wo+`ICcXN2gf=r z@`30Dkw-c)rX7J&Ik=w<3l^^)A1F&FuSw^`A8Cw{PCckV$Q|50v4Z}{It>x-J)(|Y zbek8)b=lFs&pg_nS*wtZ&{^ReP;gBG}orS`ffadP> zU|BkOjd~safH6jP3Vdh?N=yt4+g)+wK%@?II`_p+0GN5;SUmy0=wiDxzYPR6EK|dJ z6DVWdNK`?4h>3ubtKH1a84@WJl7dJ;JC3FLv@^RD6z_QpZ(heH7yRYh5U!B=4Q3`1 zd&NReGG0w2lV;K!KCcDF!oSohIax?5!Jc~|9DPkynLdMeW=jk>w8 z6MH6e7us$NFh`oKof>u>B$=~s2 zS#&hYABFtxagp{hT3%la)2coov8P`25W}=9Trs1F&(-K}SGm67HF^;WD5-|MDCXWP zbd0sRU^J+JEFfBu;uUlgt%UVl6xT5Y5y8T3;Bm3@q8!X zo9nZc82k+vXPV#qMtGTb@b#+>;VLz@0m{Brtrv7=(V4EHr*M`d&; z@ctu~qqAOYpN404&j&!mbx0ay!S7DHQ8B)Uz$P1*BW>`L zVrWr=PnyLayg8{z-2Z$b;^<;q&d1)6=pP}!g!I8ks1JUW9Q6rlRnP;ZeFN(6LYiAJ z4l_;%iZ_@2B}n~-Cxk)cP1r54n5JmqKm<8`^|~X5>+6=Rbh&bK3W+AH78vWiXK&Ak^KCf9Uq%eY%ghBO1Y(!Ny<42RtbIy?*sGczBqBh zFMGZQqR#wM%^^oDOK{nghzK!hzj&+|v2hG}wB(q~ZEnXdN!+#uX7)z#c8)rAN_L&2 z1QMr=Kr(Q*|2t~NTP!K{2)^Yo}A4 ze91);MHq$6m$vw$$X^jl3WnJ8WzZDjU+P+*F~;dJ#waP(G3xkpKGrcvnm<1kgv32x zNQ_P;R{*>|Do87(GQLzmd?WHvsDMkafVYmwzhem;Z29w1pqhy0@s^8#0B<=`2IEZv zMStXk`EyF7>;sHkh7@go`mK5E!b;Fe#(r2MzTBml=W6N<-h23Sw;X3yyX9KV)s2-t zgNWqFN`@u>)esq+Pv7HTZ@oL%%05MDPA^FImQ8Ho#MgVvAHNYSe#-*~NLLYm`INfI z(+$QiM})|C|32cc{F3hx=1*jvfP%kk?)bnQc7?=zb)K5VecT|D`Xh8T4 z85}TDLF-r3>6aDa%a`Ac?f6A5w&RCHZXq%cWjQ2DjQ}Fv!#|^u&ye9BGIvh!*u;r1 zQ;J@y05h(?!!;SFP3NkgNk)yXv69GQNU)%^_Sfb0y$ltWVIK7ls zKyo*I>N4t3L5`7=BH8}&iy!y?9Wm{gXX^aEwThBKT9PBk(OYh!-wJjkx!V+VJi08M zz$80HojN7E#{h@ji8D>~%fNNL5+cVq?FQ_I)N-~-!q`PmtQ);U-fVIR~k{O zYn5IJ-KOb@@tteyax~}WGt}}f`aPB&jDVx4# zj5495OLiS6@yk-FGh&Lj>y9QhnO^3ut5x2pr%7V`-Otl|v_3V}55kPZcLkAjCr=3K ztB30TgzDtqj#5;qyZ2&@O;U%QU(MhB)Ter$DNpd$Oufrw*~N5nin*8CNPW|H4^o%c zdgO@7^pfk|ByR$!YwmoEJE`yJbCtzC{`o&k^yd-Wy%Cef-Md_aM@%XtQ+AO+It}9M z1-bUSb#vV2YJHAs*+uG@KVz8vg3zPSk6Rxxkx?bJ;)vJo+$2m8Owj?ay%x1{m8%tu zHEuYI#m5&^6xkq=cWzV+wWO3@xeGG{AOe{llgkuYJhnmcmomCkgNvLNzIB4(zqF99BNZo(}r3NH)uG60}LXQCvF#9nhAm@!lyHFE_vby zmJ&Q*RfgdS4*X3i0fPgJnlCs}1$o-rq8I0MVD(L@+HMyXS5LI^Irti~7wIDr;y zifBYp%3-chp|M>g;|kCQQ*Pi4#)!ucryIX8k%R{{q-^#vn4kdwGF&c(5oX^XTXO{? zi+#JW;#(~7Lk3IWFo7qEkn(esF{2`(UE0y8s0VjBW!8j40Vjn_7ZVm-xG3Dkmk2DV zl(4X3;-p03c+6157bAeg=s*H2m~3vx8Wu7P&ESFQGr&M+P%@bywdVXHI4*{O@?nc* z@DOd-9l02SHotH}K2?UQlpxl{hcm9=guu6|A;uiTp^yMy&RTo2HPc>^ga*b}ByI7o zh7>I$P~(aqp5UTXx|&vK1srJMX9Yk!a3cT`)J04cCLd^G1)LC4QemyV(f|(BCr*$- zIQ)SF!r~xLVvC75q9pB(Td2&e7(sA%$&wm$@G=7pOEd{02m?UKdJv0^`(z0nlK8=b2Q|jPFz3U@W2CY16s8@wP{E=D z{b5KMT0jxy1Z7Bh!u3$k$XvWt0zXlnSn;JOOBOK8@vv=bN;sp_0~VkgxL~1%6EMrs zwzX4wFinYJr*PRK^GDD@C61am^{IW zK&C|&CBUqKf-O|AL#^BgJZy(JC4qxtYpqRP9egn5KucS28R7$8JVFY?go|qk3Q1g~ zv>~Mh*9xQtc&S@-_|_&=Bw2xFm?*>A(zTY-HZa9WLEE$=6-iE>WWYwFtwF>WI1GiL z0k9fkU;_#pe%P47$&)$fL_tl%T5GwBxsJnFz*rQyn1;Zhh5bO2k_$;&*wA7G4akVo zVT(&%lMHLP$(p9L!CCtPXX@f_T2RPf#s=S2RUjo8UKboE!_6hTk{FIF;UlbtmQ0MSAWxzX@YMVrHZQzfrJt!VI)xm%;G>~ zQlhqSWyXaiC(MQn*Fw>WvpLLrTL8nv0fz+*%cX@NqTp_*J!~**V+$#QsIY$69A3D=g(X~I zf@8Af1N(MiW$bDeF-2NfouM{-P~rp^CBSgAA~8~@9gT{Vut6q&Wq@uYe^Zz^@-=`$ z1&EyBe|cg^7vST94p@Z13mHRe$N;0-6=aUFdE7tH1+K){U~7yW!8lnk<-dHFd>;8k zro;W&z3XT9HiWeV@a`++& zCS9VCc`@eZ0h&dEebbvX*l89C^Ig-}`)r@h<0~=p+#m&Vxxx8S8?CLx$kAl;66EqS zB>U3*@Q_iI9K`jp;6UC(`jQ)dVI+Mz(5AnozkDb;a)ym;$}CU2&%|W;aZl@gjM-&8 z{Gx|_a(9!q8oZtcGu1FUDRoOTLPj7aVU?Rjmzz%o_@1sW!mQj7!pAC-<8wYo_plfc zVL7k!KCm2`#efLQ;RAd6O>fTWnSM?Uy1bvy74IrKoVI=Gm*e}EYvM#$gvrl8|L-yh#FJA<9r8=k^g&EWjPyxVg(!rt z)B>as2ErF%Ko|uXfAla4g+iTdsWV{||_fhZ8A&7l;v&7GVJ!{B96 z(ZrFi2jP2iKF6Ox&^AcM~4N`BRzI6ILhI|ro($`z4=kXHivn&=! ztxBb`w!@x~e>Wofi;5(2lo!Oh245=G(9qD(sJ`YiawD>6XliOYcohu`$DdR+f5U1V z8uWeRwFZ6HsGjC4scyV|t7o!(Rkp9XGV>Y1@hoinfs=%}X;d2Y*{VN%pqx4V%(URl zXBCf69Dn+g>hqT#yz$oFynXwi)j%7mc^q#ayrF5nBu%buZ64&Yt&ixHQ1duW-%S1z za6|PcZ9lLBjl_42O0#bK%;(cv4U%wr`}U=$fYV!*eU+y9J_xf1pZ^2p{y%v_RByEr z`#9d4>A(X`lRGe}N*EmNQq7GUU)(h{H8nLiaMTzPB-49^hQvc=5(@8Gc)ruUXU5p7q|WD zuHkl#rMsr)OKzerr}Ryq+$k>zElDqD)#s<7D3oTEkb2Y$Nj(b5wy#n>O7+!z^n^TM zgZ5`m-wmz$e75}_uxGv_rd^{Y6{#cpXP|GbG-3gRSKn2p;Rf(3qr|x`EwdEcXD)>M0?WZAE8;wIRC-B?DVJ9$Bp{8yh+LG=yx+9nK^gBNDIp%aN-1Yi>+|Z1rSWwjr))sVD2RAqemmrZsD=wYKvj zdq1DnOMRdH*C)x2iC7o~5><7A}WRULFD(9!Q_(LcDaMa7E zMROp(q7YqSWZQlYesh#~h+p>FetRe>YZXnye* zk0TTcg+ig`&P>8HsS=Un*7=-2MUTjj&gXndno_CM217SGbBY#(f&{1tH#(b1N&BQa zsZ=Kp!W0ZO;W!-Rt4T;4Qov9ACBl|Zqxg#>!iX#WieU07 zmOkdw>6CiuC17s*G&Xm1bdOLGozM9k-6I4-iG7Y{K?AR&rR;+ya)jk*q0>l^CslHOZQlShbhF|5R1WbUPmVp%F!(QIv-&qjEW+gL_#$i$<@l)1 zM{S=AT`24y4aNFXjU#ay!Lxl+}v5>#Jbi0bS6?9*pIuxHy(eV}Ql zN^U|~)aOH^C6%0+&!78#{H4;ZSmI39v(^Jz3WR7JXT2g&-hz;Zrtl#`6K-gO?!=#L z31%DNV}%4X-4oKb!xRYg5CA|6;e0jqc2qQXB5Vo?#J9{Mbw~+?)FD4c)chiUfi9zz-sTR@jn@1XZA-Lp@z>lk zQW53JmQTafn8X1F(l1-mTM`?jNi-^=e6hj$v*lBtF7p>iB@|n}1Z9>D6l;X{i$QBG;!qLoMqVQ(s%Cz^WF!BU$csw{{GcHSF?)T$B%UnHoXt* zG=sd4=NmF{s2?S31oSyXcqUpc%zqu?v)BNA}F7lBebTeau&a5YF){GeqPTJeKXXax{4&P#wC;l_6S=|G?_ zvs8{};>_p#`fKhp0G;)Dd`K7)!Y>ggZmdCnPRlDpc)T^ANX~kQ=E*Dn`9rK1#O=o_ zqI){cBGLs%X0tkzUIOTkfS-GwiBrH|@z-w^Cf_RF@}D?97T)o(ux2e_$J@LKI9}@{ z`fz#G#11=^%1{sKJ= z@)vlkMDZ8sv>|_im*~pw5`WE(wtf8mw+SsM^xCarlWxpkME(L<^GK?V*p5*LEFl_g zKZ-w$RDFT^Q;BVR81Y9m;_((Z!|Yn~mitltQdL2=kG%q6UAye6O_Dr`rX`3C&X^y8 zch~1}93Knsfa&i-f5qC|xq1K2Hg|Bme^)ejcD#Rgn>#$-zdQ0yiSCP=$9Ib+j&0)9 zCQdje=N+vW^96rHh~yvg$C}Oc!sgz74eOO!yW%Yteir_SwSV!WAToGE8|E;^NLE)b zg$gFN<>UAw?B zCyY3PX^p~4OJ)_ZdHpt;g+ET5*K5rxV)HkDqkC*LtH{mUyp8VJ@i*@S1L1vO`Xh{0 z1jpMv4$Z=Ud%TTinPBrc9*0iCDuUxf_z-jt4$Ufp<9R#}-NWN?*nEV6@%9SZ{yjKa zwN)Np4dJUpAXK95fAiHHU!5g=enf(dZ6sa_th%x8acLpYj~nx2_AHO zGM3|$GBQdzpVVLwZ=Na0Px{>>x-}z3$pa!^Vr0Zfyr3^j}Wdlf3aL{P;Rf$`t|+Dk^EJ)-&?gK`4>N`7<)nRJaQ!GbOq9P<}JTVY7=kO84~tz z+U!nqn$w(Sy&%}X_$!RRj{Jq>FVL@S{}~QF^cVLSTa4>d4(Nul)fHC_UYyi|T>Xn9 z3E>N{&BN#GWTAY&xk5^ z_sVe4$;=rfkoy~`G?39igT6a(Hqn3s4I*)s&D*C3zpc~mN!#H3`x%jW7B;3nbkD!_ z?*y_kUu{-Beyl;2zS>ClGLIibQac%z<>-~rz08!!;K9pUU77uAHB0sP=U3aLNug@J zZ}D4M_7S|Hf+$ZnYegJR$Xs*sj`?wi-lIQMz8%6_rY* zaW`lAHTY(pXI}8d?YAqNI~}~uXQ07*^DGVKzoGAJ?sTC2sMD((owav=!54Q5r?ip! zs+W0iI{xmlk^1>%{uVtF@*}A^?s(C}@t1j-r}4$T%-?8=FPs2X_b4q>ztb$#)6CWC z=x2&Xe-LI6N!_X$(2e#~$$YBXR8ggxa|W4D<@cMv;?7^=1<6|G*<_b$-KZ~aZlh#2 zDu0mq_p3IGy{PNHX!~@Id!LOz@Hgh_m*cLV&$eF@+P)u>y0ez~PS^|LeJ!e7>aFy} zU2WA?ZPiw7-U^d%OiW!tcAK}{TY$qH70aN96zdh_%%*Q|w$tX$wGwgsvD_;177wG2 zzx$BE8}6*N(5G$j2bSRsJLJM8%#K{R7;W$}u_G5Qw0%Q=UGZ1#$&m|}*`{Cp$Xou^ z<}I&UYLWNwuTU~rZT73J7WpgM@RkkUBJ8+?xWum7NCl6OV86yI8-K|?1#tMAm%jau z#4F$icBR_C_>fm$aw)c>4GS6gaNrUKHM;ylrTN{gsASM*$LxBwa)GO-^awappL=lr z)wDD}BDwxlK-DX6(Kj;no!KFSF)SItuk>jeGED}s-;tR;DG#MrdI<2@bk|yqvOt0N z)WyB6U&^Nc@mLgI><6Vu3xU7scOQ?pupVLnZ_yaa%|oe++8s8hK_8ob%-laM>8js` z$CTm?SaXC>#N~P)SO}~lIXRkD1caOiA-=+}#sZ+(5n4>$7dF(sAOcJ216vG5KS*RL zB{{?Fiz>Q=Kq8P~gK|)@KT~S zD?mY{fJ4#9z+y#)7_z`JNVcCEempw#1qw_M!?H!tl~f5y3?Q484$xo%+})(`q9lma zo$+;9 zD;%f-0~%77F`*hagJI*EXfd2I^Ku0jO{7t~9)LnCE@%%lBBI>d3}c*ujZ!qRHF&U6gA-iX8?z@>!Y^)Gbiyo>2+8n5 z3Tx+$35{k&hnbB|4Al^el`gs&mL+01!IT>lsRmmqRs#!uINc1 zkZN6y!ZdC&+&W|h2cVDzL(GD>)tD@yMkh04R-%Nq5DYA2;8SdCwW4H;&m}b3d=ktNP{<6KRL|p+ z`lNKCj$(9DpEICT3646UP9_xU^rh1Yg+iga`GLQu%=EtQ=NEVPa+;=_rV+loHfYVH zy&RpW!Kw4*e3EC@o%)hdG}@+5Zt107_01%2<(LYJWXwKO&kg0&TN?9MN`v$L>03R; z(3lGHe4lT`eOAk_{)h!q8n(c?Z!rUv&4Nh4t;;UTI+~JaB zh^tl6)SwV8>kw&})&x<<-y#up_*b!22xZy+SEA6gL?~OuhTcOyXo)^3rjI7wWBMd6 z;5TWO38wen(@9Ei(n9kitn~8p+4EyXZvhe3J&#k#Uz0?ZNODPY$66pR35ZKd+V%!C zcPtVS#~;&BL6&GcEyxa8B73B*k+=L;RC3%zL>zw%)*-MCd37l2_}3sZIAufIf8Lw( zY9*)4Ye1{nmNnVU?i_|>odyY*%&wDOXDf>t(tuvRO( z#&8>{mDXCN^x?zhYPG&nO1T(r?g_Asa8I9tIQFIPH&JJLAp6gK{UsUTFsmy-YrDGT zq7-ky((K160-+B&cLOKGoNVr37$S~?_vnW0Ot!!59!(r?ua3y_OPQ1TOKMj1=VY9W zo4p{%)K`*Ws0X>AY`a{|nP{?IHpA^8Hw3ykB({gK1#+?hoF3##AOP9JP7iV?m>(3G zSA8bOnV-M!UnXfgI&1&6{)aY@tg2gQ;zM3WhKx( zE9yIa{a*E@K7TSo;$9&}9oE!GFY~dA`++v5!rby@ zoGgEpa|h@5r!vGHTr_b!{_x(P!VrPf=1wXM z!xhl74x5JUG&C1H&C}>4G=BljH}fT#nW34Pd6}P~p`n>6aVF`FaDqG^>?X{=ByY~E z)qnofr*x9kXMIgTp>&)$Qhs3Pw7cEDBsodSTR@5d`A?tvSVd$$^)jE*Hdc|-=RbYc zU2Woc7fzh-q*I@azC{z~!?_dZWG%p9sv3flOiC%Gq_pcI;}0+55Bf!F$gqs#H8z3jc;oQMEESfuA>}E6@#6>e)H0!PgZ`R?8Nd`Iju}Y}(%fQ{9UI9s- z&7JOxh~q!TtTSWQ{m^xZI{xzOZf^Vhi{1*uxzpVyPSV_=8eFY%?rvQelr$vu3P|#% zGO1(2vZg>2Cke2-;U|vckE3jVscE4QjJe>O_i&l_DH)_PdcqT3M?mAO#HH;uV?3l|xgK+LY$(WnG7 znU5Zk(?5`ultyJ!$|Mh$w>$!&A3c;nsHQThV~O}fY8pe-@z39cNl!=+c_k%-R5ptt zrO}|dLt*}Os6?e+RiQ9M9sbiB!MD^w?wja$#I!j|@lywKLZ1YAD+(QoIwr_qcPrZd zm^OFp7sv7UpOu*HPun)kc6+q_yNTmZbi-_G+y8dbapHhBMyF&j+P0Lbk>|_jW|~`4 zg3M~=c+2I-!*%*vRCK!?9Z?w=LokN54N}>JopiM!kwGfkUfc3PPUeFs%LiSh8TK(b z0Dx9A@b9(BN|Nr@xAfFGyZZ-9vCS%C)58OZ>&N?e;Rr!T4glUpw>&t7INV?!y#8-T z0Csx3jlKaPhmOs6@EzzLoVU@eA{~729lZW7{pp?%7lNPr`r)@9tv^5e+}zO?bsX!e zg^jG==D{1*pWA*WZ>_{+@P;-vT8r>K6()jq0oS&T$M>0$|?$u!pxYzz?!uE zYn_>n=FhhISm?ySYLfTx*PjrJ1pF1LhVhoa&n}s2$XEqAW}9fp)rOU1{PwG{g})@L z6K}bt*JdfT5xhp*KY|y$VzzVkh~{ComtZ&OvnzgFXIFNcc+0D;+V&HFy{)$DH2grm z)f)MRH8nDLLyxi}?1pnk)VBdZXU9+dU21Y+?Jyv{Y-4s!2N%#-2&ve6bnWQEUcQZxiR#LTqQS;}tQZJpz62 zfi`D+WLSc5(r|BY3c4BoKT@R4}5y~ke$ zyo70lwKKq)ngmXRR@28{AAj6DlOj=JgN*^7O@aDy*V?QJkoVxkWbaQHV~%>C2l*4H zdCT9^A%lLjjo6KO^ z>?qZ**2E-5&sLzhvqSgncpSQih)lxtvx>Zp{!u~znAR*TlQ5gtZ-UKhokXYzj@O@L zhs{Tr2|5Xm*P4Yjk(<}xjNN>GWaC2l&h8R#d6axbOv{d1>}bUpF}sbV9e^FJ2tnJ^ z8$am7X%19tO+q@lg(wioAsHlPJTjJ~ZEm8C7Q-8D;}hHUZ$6tZM*YRe zGdUIFm@#9LHW%BcpV3pmlx?hn-JFljc|lHE4vD*JBy}elGiFR}V;!BxsHDr*ra_J} zrD#R~005H}03aw33Wo%v(P)-KK^9Ye6aWc$rdn!D7Dq!#5W^sbfglEA#}r=?|5Zn7G$80?rNQ zG(JO!h+gV*_$beZGQEox$&=*{;c6=P)2-e3z?Av0GYm7lt|bLFw#aXyyCorMtDBTZ z+Cn*gY>D_4c1`K{%>Xr$^7~i@r?3@dRx1cWLrw+D%sKBt#mKWVppCKOty~iAqD1>c z>1miE%3~i-p`nJU;)xX_)6|w^&-3*9bb8+{{dWJ z;6Sg)!M3tWxwVOgm^0Limz&2=k%X;|rVP^S-ZP;E^%yfKPuFH#uU#iZZ9)b+s1!*! z1cYF~HK&5bQf*Y+?i-QrNN1_<*?PJ8G?qh^gDfG*;2&H?a++>o_E4TALvU-b8d88N zKGUI+ml=0P5@sgXpA3j5i8-b*rQCvR%`K~8teQ_n(J`Y(2S-^}9Vy#-cao@QEM6hc zvHYA2ENUQ)?>(V>bKxc{u=q*Oc?0Z93*rPRrUVfO(CuL(NAF8595YrtYl*31Kt~ON z82nLxxzT(W23gb(uZ*yF<~CACrAv=;1m*8+KkBcd(MZ zzp#EVAaQhn$Q|MfNF$~C zNIBKcn``yk9866ub%uzr_>TaAb*^1gx9Uo;XHSA23SYUjh*RqOT`KIYImJM#XAGq6 zCL=!OGmCtk7J1Y8HfH9d3gLQ{C0^W@Q!628Gz`&QT8v$>V&qR{3Wk^1fG zB`#OkGmWNNDCJsnrDa=qD?@Vz>eG~TZ2;~IQO@nTKTULzVs|05%?6agfaDY361obo--J2Yf3#v*CD*z*Gq=9q#0GOTK?=FH}QNG>t(kct5Dafer%i=dHQ zZL~k=fg`mwFyB84Y79B9aTyGDZpFK_|3^;>Fk>6Q1(;CI{TbAEf$X>FaB~n!irknU z+spCsL&~Ecb5q3yPc9|(sor&1z8udJ{d80%`;oei}#@G?jK-k4ua z7Vta&{l`3GejOE*qc(^{BpS&X%g!;4B}`e5*OIGSD?~#RzWVvJLAd2|`7|uLSMJ>7TPlTP@(ELn%nP=fZ^4>QxiirlNOzL0r zkMHR$vt$@TvC?G8AWoX)sr8K@P-A_IXjGyBnTkA+Yh1w`JuF0Me z!$~9N$hUw_@hc|+6yH}!oRKB;5ri0p4xZW+0ewZ$O{Zl}mft$_;&}sOte=h;oSi z24-BM{{;?!GLZusO;*7IX&qX$eQHbUHlEsAFxpaUTgTR=K^UrWCb;k>*lM7dJ$ta) zW2gJ-RaC(WLy}P`Cq{bKieNt)Uhm!q99?D+Dc3_B296(s@R?XNR__{KJuv#df-qfM z%0y`8td-Dd=zIgsMrW~Al)*0w>3_dnhJbbSDIr2TA>lofyF|sXa%~k|IuQUIiTaLGaNTajjZz$C#I{=*h_VGn z-qZe7GHCVan6^%wmT{-KjT6!IF)4?dTpZgwU_!!>_chP4V@Y3%3cR;69Qj3QGq>+{ z@=_fj52aSwZT5sEm*8clep%PpIFrJK_fI-vtBb96#aW9sd0A@yVQj)YF zS;lId2tcuKp;axO!IgD#4=h}?Mr(RP+;#+uQTp5#^)}9E4;8UaILrR)>>(}m9s=3` z$#UpzNQEWzO|+Fx2gf+GSb{1QE+KLU`k19HEb@s23Y(u?Ziy)qz3g-DUh;hIW@eG)9d`ZG+vM7w3FbTq=oZUEruteb@=Cu7HD< zUe)UbN(PdXEJTXq6}$mUbFn}p@SM2g0>)X$%oG&&_eAGZ7Q{jq=@pu!pEFXilBQK@ zqU1%|-ePlsHHuH?#?Ld7y%jq*n-MRWE-kJ^ZiCltQNtgmI3 z8(a2O|9G!gT0>sjVmu6q*6jz3K1l@$#J!kJP0Q`xFI(TELy3Zs;S<9k7v! z`Cg@~e8Co0W(D4>jlqvhpNf8)x$rp}S1iz4p|z z3#->1_~-a~X)}6XRC^M_(mV?R@d@$y^iF3)5m|5yaTx|{g?rD*fo6VjWej_R+vzQi z8f|$f@#gB}#hU8G#W60=P56|~EAaY1vM)q`vy|ZsHVC2h5PGJRMS{qDcqcWEZ@~|R zh-651^#t(?Y{My+HZGGWj^0>~Q=<}IEKP-=t44ixZu^jLVanq+H2NHGdS|*8rDb6pr3EkFdd$?z1vhxGc%zr{{URyH=pT z1pA0V@8xmLJYOvGPd39}8RrhTYV=ekgs4O96!VcprnC+p`Zn0{3B$`lHvusKJ3z$0 zY~ySxwn~B5k%OK@ZK(l_*5~iSjuAIu?p+LwvW_Y4Vjd3fRUJy_V(v)_jIp04P=A5Y zfOX(k9L9+(Qv+g{Aw$AHfo0+fymk*dlYk;0&ge@F_k-ozYB7aeKbi$Y1Sc?h6S?mC zXkmZJL+gu`q=8u1?M!^zQ-AhahC^wkfNlJ$#NNxlXxJ9*sblb7JpQu(8?iW3moQHX z;7mcAwEs{?U5(`c2vgKDFR>vM%{b_1xFl)eFgg9&Oq^#xeqGVKWU$DmzA|c*hkX!j z-pQHlxOvmi#ONsBE3HQsEKGHoS6fB}Uh)Wv@{t+3juQ^zOd}_h1IJxC-jH}3$r@y4 z(_n9ZYq+A3aSs%0q0(s6hkN&Z*CGZ4PmRAm9HM}a7H_Gb9H4?MEH98kOtkag!aOY2 zRxKXlh6Twb> z@@G6uQ2ADNx-Pj6vmD<8W7J}AD&m(6rdSv}%CS#|S_oMqgpcNBQ5z5N_U=Ie{hz0I z5)y>Ptl#6ZaP|~keVCZ*cJ{EGRL{!P);*ke1XIu5Ggwk{zrvOfBdsgjTqEQ4%V7EG zh%tKgIbGC;!rG@IY`9J$jQ$NTmlS4_9yrZm=a{Ssm1e8D(66 z2xzIp?YnHAR%mxMkB91r}a{ONB&{1qRI1D3~4Gpb?;uwzj+@8x3}pkJV#j zyRExsYQ2CDs81<()T;Q9Qu7e6VRLOq0r7X~;b@0fPheyG!q;S_2dnmIq=88lSRpJ0 z*-UIS8u%475+^J0LO~de;6jbG8*a93j%-o;?&-CEZ9W%12j-9J9Jt5wtUMowI>B<_dYAca(g5>wPa*0H5Gx)x|Ma1uOe1?Ovn6rHkt4&rusLd} z#-d}+VYx2S2s|*6_4VzP?&&3KadF1YU*`ZRrU6G9r}i2wN(-l3pT?4vsR3f z!B1Yx{|jtb3A{?ek(##o@HF8YT+CkOWU7CXj&~k|)szX#wtZJ_Mb%?LR;$5boq;!p z=nn6d>7*B|oQT|oxIV#O5a1#Osv5{QQsHeO5NQa+Pt;?U3|M_qa#@->pIO#6RK}FT zg~ai}*soCri4vUffeSg>goU4KC9M4h3guX{KF*}OP=$l5lVg5S+77d-#|?tTbLp9x zrxtimAbPNP-1uMeol+MBF;N-XsRlP<1~gKT1T-Xa#n&Zp`#PRS*zxsNjJ!bLxT6zl zBY}XL<%&N-7Yqj>#`_VEOS4%O+c3jFTpbeGOtGF!l&k|$(xLuVhd*K+381l^ru19k z9rJ);J|kcWGI!h@P25rKX;)S56tZKH3XuRg;w&~?4{uaFXs0+iJm%Y+F{T3nz1bM+ z%&8g^m;y4|$n0hq$kOh|<-ZJ2kI-IwlR9OV9@JIC&7Ji@iG^$RUE0bjfQ<=x455lD zaQ!CuSQyodHXb2R;T}L~o4n0s8ySf$Z3^#tteeS)_C{O7#9rUjINZprX;$VDL;u{V z0nW;ZRP?CvKPYmIg|sGYElHcsk=g5~wF>cJ%(L%wgCb%;gO9*zvIwXqC;`MSJzkbj zRi%sXq@rv`fhYRtST1PaU#DT;`dV|OZ}cFt6cHsZ_11(Ib9sy}VaTED@E7ZQeo3d0 zdy0%zMp&0dUS6agUBbVGbAMt9k|<-u64oO2DmrBl@DIBf_J_2pR9|$Ty3u4k!5H>m zNBluVb6F57 zjz}xm52gM0363B16vks&kDp)ST-kxT zk4D3*5us18$WC@+TWKGCGLt)JQV4anNNpOEX&Yk}1h9cC${VGrxP z+clQc@|R=ea*{5cziZc2b;g$9GVvU&$Vq$?S4(}*P2Cr#zZIJN27duh)8z3rh^$7Z zv}Rey-q{N;?;EP!&jt)&VjdQ5pOsoGIFMwYvt7TJ0KfQ$Lj-bhK5rD$g_P++QU3LPW( zKUOwhm6ocaG z5&Y1S5R4qW?@cKJ{AWlqtyF8cbp!YXWXjZEC-1B5(Z`W}kS5MoaCQj$$wFQ%gjkxD z>L(Q&O>+3W2jierj$*GGAiyee)#aBFKD->;b&7{ajkH#ri;j_n0w)%(X=s*)wsCEJ zJpxHNq61gGFs5CGpzcQjQ}H@iwBE8BXff$KB?*h-V)q^+FXC%bU`qLvr_3j1Vv~y+ z{=srk1>ZKep>>Bvg4s}xB@(}HeEg&)ETRR&aODwV_LZ~*TgU^!)N z$#74n_6M|POK>Dsmcg2%K*`hn2~*)_sym^z~$Sg1nxhvmxoH)ruhWm#`9> zft|yJBCyb~KUIfNVw8foLp{B_5>NV03T`4nq4F@Aloo55V3m4b^c!q}U*6S%k%f(k zv^r6S#gzte&KzlVx^$Z%y4-jU4%5z&q86gTb!R>-G_xQWjmskpljybHr|omtIt>bB3bS4s;S&8LUj{s1-pDD-M0NC%6+ zLkRK7laUZ{P^s^1;(di3U+mdJ>=9Dj>f0S@1Wp0=%PGj$xQ4x_yo3IB#M1*@+MSo% zE1*Fe$D#4uit9~WQw+@DsF_RrWDw+U(0a&E_6jV%?)lqBjdKx4tKC6x8PwFHpMPcd zk=YdRDQ>ZuC+Eo&P--t5iG|H`dIkJ;MuKEpEUv@Sr8)U_fjuz^wm@AS?dsabbqPRH zd2eRn^Y#Y-;IoTH$p)^FBTh6L>Snjt5bT{f0tXmPiI=bc8&IFfH^k7j)JRn5t|0*| zsTdACZd93JoQ7sq;PS14u+qbL6~}t=@nzWB#C|(Opt=020^wWmYMizdk4*i8Px^p@ zkP|2nD}!8;hEx0+LU@$kD0dMc{inctfKdER1-{df6M>yT*=Z0uvuwQ4?ZMf}6+}c1 zG>a=RAQBE@vz(!bE0EW&AA#?4FW*8+z4#-4P%q9<8g3cBV+!wa0%pP}8_z@a!;xJT z{?5}2W4Pm%XByOi-hc5-(*}MOod!X_GaVMgR>aR)MBhvqUpAx*8(Bb|TJxh+mr<%u zY|zd*DABY!v?0Js;xiGyJv5LoSP^y!3r7ikhdD2E ztZySaGd1_t<|9~;HZb%BKZsV&j*98k7=V74=A>96GhDg&8pIxSfc8FIl67C-bz|t2 z7xtEoV=AC9$!6Cw>tw0|!ATYho02CVs(28n%ztTkxGFrAIsVpLCAVs`sx;J$icfRS zY;}PQ3nn063Uam)FhL>;A*%gOVHDIa#Gn$wGq6gZQjJfHg94x@5lYgvRd5XugA!FO zd!txbfzmzfI7FpL(4JQ!V9!aN1_NpJBEtu0YT4_gzF!;;zzfQo~^e2rM zdLIp41`4o#q1(?2I?K$!d*s51viq}e^GCto{Gsj_XAswaVNSuMf4pP<^Lhqd7|mGT zmxzV%jL?n8{P)<0tFxs6@`HpzvypEQ!)khOy}3Z)Y}(Ix_ox^tw*N%GfM~2Qg+u`m zudgAB&U84S+r*Vr0lhE3UsJh2OCtbu$~1UxLjJqfApvxO%JpxN1L5rAC#MMT2}5h$ z56J;8Hqh8mSY;oFdKp2;n|>UQu~2@9xL1EO?_)k5T~hMcCcpWFQftO+%2p!)4=jAL z;Mkk`^qHP{=bIEF(><~DeLAc8Zb9Du#kw0~o$NbOP3@JpO^0a~Lfmt>#lCq!U9YPm zH=BKIOOFD-q$Cm^RUlIFN1^T@ulK}#BI@M4`dcuU_5+qBuvHf|c%Yr(NTyTomh0%s zuHV>0B%o}X(qNPhJu$uSdTD18jA_2&j37chsV#^LjAI2DPcL}_A#n~S4E%IyB>1%8 zrAecVD-zw(8D>?jrwra*c0}hH#4Gn2QqNrkm0)>atJfhTOA_>}XwYCOtb;{X($Et9 z{Pn?bF>xvk#qOUMbI8s&oZ89!vTU;_zOw zeA{|@;0NA^Y&V9UpDPlcav=2sfcCM8Et>w@-Z;mn_5MfBx!y^SPT8WLbw@0H3^ zEpw*FQs_kGl`wpiEEpAnrV;_@TniMOF^$W+@_g_xw)`JI0(|FTX&}x2JMm^<-%Tyf!5O+( zC)fikMfwl3eysJ##nd;`!WART{zy7t2sBryj-)`YPhv8VF2|e?I5tNuqizIEPhw6Q zDR#+H9dHs#9Lz51vdY*4s~#{wsc?L^8FGRf3XiDY7jjmxh_13X*QA4+Cu9Kw(C*l4(-8nSUg(2_;^yQ|v*l)XD2cntUaOUhoa>(%K;) z*sO)U8g^04`7)IjKguJ3>*~}cfM~Up>pY_1}ms7EkSWC2Clpp@PloD zs3t9uy|_;g`j;Do5}z;0`!#X&9dd5`pb$pK)Ty_C7A2(PC9Aq-!MykQg2OX$`|!%5 zt_v)!UQZ}Nm6nhN#4=f8jQM^s?$W6Npm}PwSx94{YF)_P#(bi7Y}ijuuOLm3&EjVs zOr>skS$0`EHrKC$w0LK^u5JG>#n{z=MkpGePQSdKV{Qf|juK)@Da*$Vtc^MLB=Uxk zcyu(ZPJ%ERQj{^Qm}VTA*5P0R${0f6lcl>7KUXZ><*<~Hu`r4FFdbdW5w=;QxY*#T zcEJRvnTO>{LcNndq{+dQjb(SaY@HU7cekeOi96$t0LKqdKmYHXpRruTi&Obt9vC&A zme2qz+g(7X2O$C|fOQFhHQG(>%fLXoHBZzT-F1x}q5?W+U3l4V%lrQp5bxzxxbzN5 z6Vvg0yS$u6hg*3cPW|^m|3qa!rn*lT9g&%UB&HyRD8~Q~yzxE5;9AF}?7}Sbrr1~n z+}srsUqy`2V0--IXHv=t?EgHFpg|F;pw419XIUhq$5hnFAQF%P&-!Hh>%6-96J7^^ z<2iF?59#0Ru-QrJ6VtZS$K!scA56NJn%Jk-G&C=ls{e8`<3&F%7Fmb4jC!(>F>8X* z0WZU`X+YCso^3+m3Ec%Ul1%ZR|9eB)ctW^d8<9vtcf>Od zEGatxm<09k+t9oulcVyV5n-PM9W-SPqndMI9YAS~J>YmVb;Ah3{E&I^_5f&r>ZX`P zg`@UI_uZR0Mmw)>QK~4wKG5)9QAhja;(9*4!r>dloGAqUzQ6kL%jEh1#od-YCbf#7 zj6%;-Dg?IjF1|aSZ6Ou~gYu6R>7heN9XCIl*eRCy80TCBt4R|`d&#teF~(4%lLZuVnqs<=?}DN)Jd|vGm$iVr5^d%Uw^O!cj`FG_dgq4oDgkBmoYX#FC_0 zWHL;$$mOP~N(8WqfoFyBJ!N2mwBJ+6(`1baGV236P|+g?^P~*^RNSZ0 zYp&4>lCK~`DU9Ee?z&EwCz{o*W*Y2WR<-b8#)mpk-9<=hQ%!{*72)zjRoDk|Q3LwMCeHQ}r=K!sL+F2QLwQRz^63rkBxdrPk0^ME)x>h*WkpR0aZh`3eV zeRbsyK`kArHNA|BrK7?V{X?@YauCf<9iSnGfx$T=6IDLm0xh$>fQ3QST%m#`o+1u5 zPS>7$*lqA*IE{RU9hX~Wz(Xd!#V!l$I7VYdHEOO5Ou?cY?v6WJfua>w26b+#Mq6Gp z>N%?ZeMGaq7q}#Sde!!GI%ut+z$XtrLxq%uP^hb>~e7XP#0uK7akv7ek(7 zmaPy#SK=*4IE3MO-BvTy{8gRx0E&;HS>_G+9V)M+y(;PU*s|MZYX)yFES*(L*&tAp z5fkfZVe`Jxvr3(kRv(Z;#+QK~sgWAj1O@l@Le4Vfw;;Bkbe;3Q@Bcv1R*p!hIvA>k z6k({;MZ%h~c%Q)WiNnj8=q89vcrII2BCt!HApNNJ!B+NVHXPdcW!cEzoWGFoyh>eFO zz+j1DcDCI-wb-3enyFcf;-(LnoeEEEtK zebdp^Av*Lmrj?$qYTILich}4990V|`#7GH;#eg%;38ZhC!lInfmi>B556JD(+9OBz~2;{#!`=h?bq2U%AX$~ z#A}CM@?{kI5aLcV9@{yxn7<}FS-mrBxu0glbSPxxbyI~fQKfkI~5LCFT$UL90D2}QZy z<}8ct>l>Jjlx}XiX;aakEZA6C!2pV7gu~!(2vi%o;0g%ME=%>{?5VC-==eVqOM{-! z=YAwz0f$!-BT(w4N?go38o8hr)$s4Ugm&z%zQlKW>C!aL0r!SwKx*=2641s_}1r_>z`c7Lv{%-hh2UyM|P1053(>^auso zoU!|{B>%Y;7zn3=7}MN$CnDhcGF3Zp*ckHB{@0dBq7ky<2ilAMimA`mjHHV%JZayU2u7CVk+PybS?uFbdH^R)14Et zNh9JjQN1e%`5H&Lo@NE7QkGrZ`~HfGR8q1zv*n)Z$@^z{?FsPB_j*`cL^gHm&O=26C$oIl?uZz?}tRXV5l!(SqEW#}vVUelFB% zPiV>9DQUAz?GFYDEzP-zl15A_EeUCdC?&!LD0cOdTaApv4V^%WLTy|%BdTNm9#tOv z8icijMu6f0AYRTs7C>@ya`BSvtDg%|F7E^Sq_=fRQ6EFKw%S7R-dIz0g5mfGyB3a= z@7?eATwwnLD3`LGjR1$0(D_|v?{OB@HYhV2PEfwW2vmVMniSD2PRcc}mqNaT)m+QO zf=mwUTF4Bb`APMb%uh{Odb7;>Mcl61R#^O3a0S@+j#x7UX_0RO&%H35j6)_@8(K&c zn+sjo{wjeyXLI_TrUA@|QR{=0HYdKzKpnN{MAw`2e?6blC2)MGV1Hl!ga+~$Bq z4P|?JTnB%v3=eQ3OaceW43Dp-Rvd}c+m(Z7eIJuMwYC5l{-h5d7}wBSxIC=jA`%W1 zD@*5)S;{i~ai=H!_C6S3f;FWcftHBUW&vhCa)?jjxEGBjpc=tbnuhpV7KoqN%b6IS zf&a)igTFC8Atu$(Rxf0McN}At&Mgs$w=HKra{ITbQJ|T3M&5>7BwNoqRZif1gKq8} zj!|f7h8OvNK%sYEIgq1a?9RP8p!kqh8o&VN?U?d$?EV&XRyHhvim<6TYsgz)8c=BG z0N3jIsTxNwge`A_YEZ}PbmrTOi*}lC0$kRAx%i09ZRdVy7rDL0Z+mz_QC57_Eu(ip zaI6S1&|4he+X%#ga^+b8WvE_84R)|HE>qFiiU$$S5FkCs4XMK_#Le$ueDlzg9)h zj(ev`Ij#DEO&MffcdJ#lZPtYyEq!Vm2FWCA5b`9F0e<=Pl^;+0p|3dN&N7At zsvu0oF^1vC2DS6fH?y$81s+IHU^W^T+~nL{tVHd( zsSDl4g#HR;`j9j(fVSuM7}_#R1Q=%ipu>c~n2h<4Yk1=tx(L9o3szS=eOEc6dI zjD9cVUS-BQsRt_6eP6$F z$_}%3UF?uY)y4OK;wE61+rBiu$MbBBgy24$q4I7ySF2H7eqLS zq4<%@iG0Lk@)FfX>C}p>$?^ z2!R2*y#yG?IwidorB5#hmu&O8Ox+{ev*Q1d*k?(Wf5IRN#^^Dmj_D_`TAE=3l zSYw{QaK;t+XW~6k?N{`djG}G-Gmv`7UA=L9v4SyPZ0L|8{dd=15r@m%wA6^8rX>Yo z?lanXDYb;bw}zFcH%p&MqFRMd?FoCb2pDpD@-t4q7*P7tgWlqF;;+OsfQ*=Zf0Cx; zG#0CIRyXnc<<*rhD+;XC!M>&Hy~Yr#qiol_LcR!hm)xTNMd*CB!ipDmycXMRSx~6U z6-gH*fa*~KXtxE0I7BMKot*$`N&qI#E4*G8G+S~wEU`FkH(4c5BQH!kji#(x;cX)J zC;CBLoacN2_BEjU|NC@yH17ukjQcXZhtx%8H#s|Kn#?GGpD-dF@f`AE`1$WzR#4%y z(zTmWTT&{L^wHFoij>?;&I3w``y#i!uKA4N+l9NhKWR4pf4x?M}Y)2i|l)|SDP3Qt_zeNF^73)wJFxr7)=*I7y zPH+Gg_6@ywVB;-D^!L{~!4H50%9)h?o7e!YaxZF_XM@Qh9N30?!SLd`bI@VN+F=VO z!NuTPGY`LkT^#V_AXCvy@%Vax5YvFPD{P2#bX?!CwKjI<&n6Kn5rZO$*DS}Cb-%E=pS@;UV7eeyHH43HB%0gBsUw{sEL6@Lj?1*RXBE-Ofmx0Z zT4r4S#UV>%RHKi%P?f9M0pEO(bfA|~YA?7!ty?6N1Jv^28Z=tnhR{ye;?fFp5f{&D%;w z6p$`ur+s+wh0?d&_EFQmRd>J2H^lgTvr$Jum01mhZr1;ULUtnCwQF4v@>^wrJa=Xwr= z`zJiR9CV?h9CY$e_TCmd2#RU;Fc}t`d`k{Qf-~^N?X0wVy}Tts=jxN07=+4;K*0Qx zlyb7bvGN%dLf1UqjYB$`kF(dZmVYMu)QD3~3T%-YzM&XvX zKe&7SFTzCHEC>EYQoG~TWHbVBXn zJWG|`Vr##W)yq$QKg5?gZ(z_xm?2IA8azbVe<~xVsu*Vi{&8|eRpEyuIzWYkvYd!1 zN;D)LL{yL<@PfH+#;R^@i_ud)|j;A=%GBeH|s{E(QCj&`<0&?B!eT-}U;w+>o z0O_c*JCHA|M|GFW^<`-g`;F|;euLfZ)N_i@yz~@S&Q@v;ZnW{t`dqjDv(?$fzeOxD zW(^`FQM#1B1l>TvZdS(@s#wzBGyMi*#CZgl1->^5_a|)KGFmgCK1kQ1-W#Uhvg**} zPjtqvpq%~7NRG=#oFW}6*Ka?nZNM=3^UDoJjf)?C5v2!aAEE>f`X zTSz{4k)QcvZilL#`t1`55H*C0!#gr)k90QnFF27>H`IkBrd1XhSSUd8s{goiR6Xpo zxp|i=ySv(eoQe!=R^3S{6n5fp1sD~C?J8O2_l}WvrJ>qphh)qRz>E zpB*>VUqjdXy1-w94dbd&$jrH+xl;X{WP0|?NBFB8Is;9wbdVm`cFhqi>|+ZS;#xBsz{h`TMu^kn1;& z3y2f>-40U;RV%PtavP8+Y%<-vwp34lCjRMmHnXWdYhzt{e8hu@he|+A}lN>pO$O2Y&#&y(!-R`9! z6ps!%;gl)i541E*-DMTk*I{SqDwgw18zwuE=EkeWhC)Z zFC~u)M^iOlp!TX2pgdvkmg;f~LwoVk$dA7(7!fpIz>qDl0)@yON6?Tn{A5$x)PW45 z^02d8I=MmJX{HtcXFbax7>=Cj=3b2%+}GAeQblrDF?iJAK5d)NVJfgpb7#4m?r}bA?$%srP225q)1}PS`2mw(fc`c{d zpAerRGWf;vvT1P6p+xkO-?i+ius^y6pHfju?D+k;d5o5doynQ&;mBo1jh-ZR4>~m5 zK)ZFoD6I*LBDhO`-Iz*`hl!B#{K97) zXOijy?a^5znjdBJZsg~~)__HE^TX!$!wb*6^P`IJhWsV!ht+npO%OVIb}@=kAhKY1U~8II2YGE+91G^v-dVcen!%U!f5h0)g9Km_h|J%I z%yD}_&OIJ{GVGpo>bM$2ABy2_jrb)z3Vd*c1sA&~Sy}2!c)!M-CM`^CX5Ec+oA{C1 z?hfs3KeyYy>ZB1SiOh`yAAg&?kB8|hk|eS(by$>WLaI_Za3u2VYn_Lrl3ZpNgl2wl z8ySh5^q8ZP4!Ng#phY6-G~XtZ=lffsg4K>0+@>Ot1yAz}mHs_XoZ!V{7rYbDfi?_@ zv_URRed8s(vuZo$7gcQpLmWHk%|IdrdrHtGk8~fJ;0?%IfJ7qRGYrDaFQc&nd%GH> zs>;J?BIR-U-^n%PtyaM##}8(kn8?#!rrNv>i|p;3VqMAv?dupc_F*DDS1VnS+$=a1 z4Clx|UWJLY$f|cwED(Iv1#?H<984rpdJZnIC*K&w_ICP)@SzHrfNjf{Na@08tUYom zx3}wIJ}Du{*8#R(B6-)UFwwXR?(O>E!eljKEYt z2D5QdAhVC`IBxSMUj}qjYoB5%Hi^v zNf|*ZbPj|;QKVLn`rFYemfzb|tHTEsa01vih$6*3Q*FqN1^9NHq9m1s4n-PLZsmn) zEWx*HD+(zg$c3(fus9UyvsUSvC`kOH7+wnLF;@^9S%gcO{%KMh7wS}Ex&=&$RbF<^`2m?9Ldl7-lI*a9l`&ILw7 zQZ7d#NyFH7S%OjJg|#_$09ye@3KeK}eGrlOu@-V1(1A7pj5O*ku_UiyY&)Qz8MXfZ zBAKFzN3~`k+m2*urq;C zW3Mlgf3{1IJ}h`9nCGa0JmibCmJt{~3|>4#1^F{K=q>R@Qupj&F5XOicwL1$V1Y>6 zF4E&Zs^IGY2e;K;-lAXrre<#BWXn{kU2C}=|l0CpuG0o2Iw5oSeU<9DDE zD{_)xa9&!ATu>$*G@!1_N^s=$!HfT9m@521v|41Ne8eyBz&>N8`*ycHiV?il-^i$C ze!pu`R%yulc9#n6*^T51NL(Y)jQ_3_0LquyFC(E2t|(ZLE_VeZ*3|);bKr;+K%Wcb z8G}t5K3m1Y#Vok>Zhl%SIXwS`{3H`D-pawY>%Q3CGl+2w^`XBV?1S#0=@c`nm^s(+ zaG+0IA&j~0nlcR#V7!~*5P4|^b|QpI@aps4#0Xlht6S8m*=ED=X6PdJ4+P?*X3ers z$4yv4mN&%mVFo%!BGSYsDN~@+iPEf^SCDpQtiUd4^BvR3C7SnQxv<{P^Cch-X1FEMAn!NEyYLtdIo(gq)H=3VTrmlFc z0Q6mUUAy2l?!pd|)x{BO1~g+DE~l%J0m%>J3tAaJpIQ*LGO;r_>SnM4$U%7*@I3-1 z@Yu3T)vXDY@;aQ3U+&YV4_a7fs*~B*x(NcA&Xn{x144##n$TdheRgGUD?XAlZm=W% z>5kyX%B2w&lTPT&W~Gn}euT4;>$GS|ny1teB24D#C6}2ixu_+8LqL8?mS6vY0SP8O zxUUD}$nyyvdazSqBFy&pek&bnq#Oh~LhR2kz_Hlh$Ww z$Y+La_hu~2>((v_CJ8iB{YJ0o_YiNg5ErLLHny9*E5O99N;>N7y;sw;agIKir*yu> z{&EFLm3hv?yQ@)@K;yPGn&D||pxUo@rc5`rw;4oJtQF~T&a4^X)6fW8 zRXr=jl(P=uEZF;TfS-=$M%Kn*d@I!Gpo6Toa#OcM&DfZg+Lp4^kis9 znp!?SpsVB+)Y0nL1KvSQ+|&qh>H`x;;y|ag_t#S1_S?du0?8-6xUS-l%_|I!3@Nq{ zMnzoBN~xcMlP^v6Az?rB59%K}1AUQiM@B``6uqcGTA8J~P3Y(J-)Q|5Vs>+T5*!Ft z6BRwJEie6~6Y<1j0a1lKOLh*F4WzAkXXkz$+#|EBrXOJ^Vo0N2uT`U9JIJ zgw}NE6;;mW&OL{qj!62|0q7Ap?W!5Kd{Rn@2$yliOrF>%s$h#7gc%YY98ET9$*_@^ zH2pH>L>)B8XNZyuZ00Q4POWiUzD5XYF6;zCYQJJj_~Pj_=s{&H(-ipyUU5*8X3VHV9!F340z5s? z`;Cwi}Z1H_!};v)}oJ$7F`Jm{P}Veo{x zHwr(j5jg8>Os*5NVljOVwD)+(v%nyA8AuLY#o%F2V2cx8NK$MufZ$Tg53OhBb&Owk z)LcWM)<^Z!7c!jq%*aT~(u6b|k}op9nZ!dH@x6%Z0t9szLR&g4kMg01B1KL>K+q}( z&Gux1`jm+$Z0Njv!-_)a^u>YO3!;*VzL}ij4+SiQS{}aUl1&m25RFjIF}Z*fgJW`# zbFAylxr-^?^iCIvf3OXE?i|gN9>-K$1Gj*@&Yd5tN@j>Af%*yR9~?lS{=a>QvVN-$ zUJkGqyaIg09O`WI#=-be(P#>f;n2`f301y*E;DA>M2*kb9#uM^82Nb^Fcv5A*M(#a@X~S{b_lsuoB|MOoqD&Ne;RRQ<||3z*<;g zuXBM(bMk2pAEf#hkt&Lx^itKgxdcJU#U~=Hj)QQe!@Apx2#6sqQU&MkUbCW_RlO=l z(Sj?EiYw5_x`fDWwofbra*GF0UH=`8SY=Q5Q7%RIIvNAJvEy-c zl;gdAv1UU`V*RH)jp{wG9#oR%#EN(UtUb^>IR273zRWcX;5A=WCkj;7uyaBWXrLi> z?Osg^R1;MP>sbPEbrkw^<7{jFlw25w0&#UH^!bZa(~o(S13}%07vU*J5jVN}vr3xR zW@ALXI;0T%$3_9a4;U@*{rK!Hg`0@tAL>Q<0Yw8S)K2IVfi@r{nn z7M6ebvO6q=rGzyTdBttg@q=r!WDRL!8t&^0aC26h?dUxz-1oux2Ok!P`YZ0jOXYhl zH@h*kS&o9CF7kIrjAsaH0akB!E~lOch9R5^tae;2gFJiYXXy6B@CB6p?36fIB9hwbJY>YWAVps0?p4o(J%pL9cZLepob6^PQw=mA2{tnr-e(B zZ}L(@zcQc^CCf&}$WM-*G4~&57NJ0_A$Gh8$=RsvA{12*_ID-z~4O%ujpeWKg7-BJd@prt?MBj+ zX=A!GzvGMFw$cJUriPs+-LflT%dW;9e@B`IL0|7Kynn9)1B9UI{w!lV5nMwwb`SYF z86thhabb_pBmW=S_#7|Fo6n4pMuvb8bRcm`vZPo3R^l>mfB6+lKp%#uSD&GdwWkMW z{lI4)OM2|bv6hDe#WdYYbfcAC%5P|9LzEjZrCDG%wFGqU2To(q?P3L$Z23N#|1QX41dSj>ChVfO0Bn;kqDic|wK?q61{~eyMvo4}uzu{_w?)00i;sD)^rlTC7!y{2GY>w|BQqB!0Z{ zKSxn2`Y!>*OcDlt;Gj%dZ7Mz5E`bc<9FPhQ1_Avd4~40)yesvFc*2aAS}2*Bd@Qi= z@ZFSeT|y5qY=UBwlx2!}SoV`&-E|2E0hujl$?S!$Df}{}WTyqT$M*T*gIju-kLZ>z zE~R2sjrj~_I^+=y9Xy%<%-*W)B#FWuz2`aDsVmfms17|w#XE3^B;)r#!$&)cv zwo0Uq(~bGAYkCc^bZTASslI4k@AZ{~BR}Pd8!( z{4@A*6J0l|qJxz#m{q4C<1vVQO{ZHbKJ-9sn`0eTc0h51W~o$GqaIHD`t)(ti%|bv z*(n|Q6n!p%m4sujOri4@P{Pp#gj~4E(v>C*q{K+BQgCNqp|6h-y8_(fSh5{F+o}3j zxTas+$4&~Oi*e3h{Vo{HSy40(|26iMFjc(bMfZ%mB`R5~pt z%`^^7m&^0h2Rp<68kILIQ^GjkHp6%m&=l{Lrg8)1eQm74-bu^} zD5nVmrkA!e`JVShF0T(z{azvg0x64>1xlp2Awjc>(`7eakF(=? zvLCC5nX$dtjn&)i*k0_$>S<-%j_tvItR7~^_F^|yZ!=?iu^+3qnXx_DDOQhJi0x&6tR6FMrSFQA#{~{? z#rY6bAY4^VI?0%#84WB+EaZkOH$2YrfoEQM8?&Wx#uUwHfKNsSl(;1in^+x2DIg57 z(D~DS)XVDb+$U|0ut9;{R_nbyUwm}M=j&Qa2$4nz*?Y^q0~-;$5SDa`RlcP>#hwOIc>p(3(}u&Ssp^?K>Cs+A znNIcPZ9mHN)?52@VD^+uB7>xl{E=43q3%jkG(Gr``!P)Ym@ExTpC22_p4|O8S+Zx< zJ=*&+3d*uU3l(g9Cp&k_32c}`WW^Nn3TOy|96!VDRbxzb2m&50XPlk0Avto=QL^H5 zy+hl>(Fj8vrn#G*$qH<*Bp1Enu3aa~hulu)jAU8w%ERY=TMXWn`#HZL>bJUmI;8C( zGxJ!nq6|qgZA!o8_|a}==XSD;Gcq$eQ;2#Mo?=-pAq5ei-EP)MuQUsV-I8=letU5@ z#?0!U( z6r$Pv`qKsHb6BW%eN&23l+aV0Lgb?bbaT=G0d!zlNDe{(9augR0xgK%r^??m!X0MXbnjX+qG{3OfQ?zeuRMnp(x5Kbr+be&kkje-IQNl-s>0r%4)#}opuU1}fNsFA z9Eato16g^?TQCk7-T^VkIapFx9fbL~u^7uf?mY-Z=Ca7?FfVe#I6&Z!m6eq@zHvDV zZXq#M@*t5HkZchLAD4>-{zbOjA9YvGa$SD9K#jp@^zsuH(gM0U{VPYk@eRveaLeT^ zKfMKX)3UPS;&H((>EeMwApselaD^CrT&`$DatYuCN0`A1P_PA`H}JsR$ZENfg>tzL z&<*ORpH2sVb2@l}o&JJb(&vOF?DSCjv=1z@8V5RWm;%hj3fm|5s|qXQF)jmx{Tx;d z3^w+m#K{s%BJSBWqJd77B;p1jbH<94yCj!%ASohM|t?iFGJpPd2bTGRTvsp0g(m0|SMm zBUkJRdE$`c`Kdcy$}_YQGbst>zKk{{nAJe-H_QB@LKV0Wgrh74XI)mOSER^=AQW*% zI33hUN4+XN<8*+s?jg#H8zynVxkyK(BhDprR>GmoH-)HE@;X4O;~|J&Ar@d)Xy%!MXbKq$8_iciKkFF2Sa1;vqGs6s0I>gPqTnbd*BW ztBJvOr6}pA6!og&9n=&o=fWf|Iwb{5G1#%jqEj6UIi4jb>1f*6BBAL78x*DzY}#xn zl(i@!d)6x`OBm_s1v$%62n#C6VG0pSj!KYFA(L_x!op4tQ;1k*!0dE=@(Npm*;o|H z^7dt2)&t-yG!-B2t%C@a`dS5|t2N-I5C9%VFbP`1rl z6pB#^7LURh(@L<$B^b;U)beuq5=`iH`bz9WELgro#1eBEaK?GKyoqPpv{*_-)26i& zTfnp6CnO67%R>{4h2=3?9-4$?xI8q)TwI=dQ9U#b#lTNUR*WGTGT!nq5~Wgic^HdS z_zB5U{pDdaqPfQpZ^T^YVT8@VAP*S)2}`gnFLS||3kqGZXR1IL$O8sV6?imNpwU!; zMN=gN5=|BCfWZ0^>le60~x!#e!A9%d-Sc8xX4utza6DB`8i+hr=5Y z)+|9;-u_IRRud?!odB@#<*jSlv|8?RdAl=hTBpBU-ttVF*2-ZnZ+oUqi{pn@;sz_` zGB9GuNP$0He#L06sCby+(!*;m&*Eb3NbodsohMK%8rONkvS_aJ#Kx*|mdmdcO&bx+ zF1qMK=Ls5C4Muq)#X>0(YvgsFI15E5*2tr{+6GDsVvT%_ivmTgk!QImEQmGoG%kt@ zVvYRFMR`H2kq0jd3}TIZ4;N(wu||HzMIk|~k>}x}lpxl~t1gNOVvYRDMKKa<;(ri+ZZ!kS2#Hu13j< zvKZ2kgeBa5ZjZh+fGv-Tg3VD05~3V#KPV_znxhgVQpn-<(?di6$i?wZmh3o?t`kI$f|nvQ~VoIJ&EgjFR*T*R!xxOmmn*^g}wL!iL+=_Oty& z90u8b=Iu_WB-r1;##YC=oNxs#<`+bdPNL~gF(|}*2XgF=b#+QUYZ|sXH)azR*pPJO z;&z%Sha3;-sH+scnw<>l$#TO-PV#hlGRmAqVW7~uDC>ktN2n%P(h*P3AiFx!5jR!x zo3++;A{{;Ceom*KaO>nkVG#8d92W;uibLF*??%<`v1MRJUp zwH+g@?a;+u#nT_Sw#Y+-@{rCPK;W@;NP==$2?#rmjri#dptP zMGXERk#ft(e0i(kWN182rI;IHhwKtT^9E7_7CH{_RX7y~<8sPfU_$ zs^~Y50qmY zdvHV1sg{G4ICe@%#-SPsj(pd?+-M7q4^-Q5eLn*boxG;Hr*dK5sC};icG_ng1xjt+_qrD zK*G`tH?ZJ$(r><#a0^JjMf!J%Z%$tPBfy-c45C9v#5d<@|MGf>FAI!4iHYt zP5xmVARLu{*Li_)P)_;@#xdP{j?Q9WTR=Cazj!Oz+z%3LE{z$pW!NxZ! z80;aFqnvc`2IZuqV6gELHz-f}5u}?i_~YaRgB^t}6iI;(NFIcS-p5c8)R>CqZ4cK_ooWM#dl7 z)vS0DX=c$}GL8BUHk?JXXc|!=V0p%*HesErU+$unnCj3SE^J6$xzhS&ZhQQUpsemW zx_R0~`V$+=dwuUr8=ajSEbmWjER@&BrmB7@*GZJ?!zSG$Cf2^Yl+|S>nzsba9p~A0 zc@{NoFvwwy1BCP4yJ+ikbRXFr zKy<3M9PE7dsIW1Y{$bEZ>7WsjC0XA=Ad>Z6vOe59Q&swGskI8E2|dD97Qnc#N4WpU{_tnRzu3HBJZ>y_)J=c9S1sw%{rmVW@oS0Zs>GL{{v}swTGtR`B{*f7Rls!!l69PiVxpJH;uwi|5Q%)7w z949NfXpRD%_YdRrQQmri!7K&i0BOgW;E#FkH}f0@GY#MLQ81WcBO>br>*$zV$8??V z-F57BO4squbiH#o`!v!`opzvkqltw~ z)w{0iXe7^6Q4-d0tA$$`eTzHQs75QTRHGWzE_t|5d#eR>qy5uLxFugVq*vUMuusD+ z6Q*1BsklC}9$EcRzBCL@>_exB_U=VAj8lK}z2Y11HHTGkOA>`Lw%v5OYxjHUdaoB~ zV$$pOiltEt=%zcT{ripf&vfk*-<0m*me&Hhsqem=e&{*+|8+d&IQah)kPQ9NJvARy>3W6t;e= zF|-#Das(0;2+}K^Ud4JMkqZe$q|%30(NTJ1r(}+>A0s(-s*A3b<>O_MJ~lH#4!t3r zXN@>wv_cPH@kS^FK|j|pl*28fIwez&W_jlgTbcBFc|)5+o5 z%N}JC^hHn{+a|(vY;h(IU6dwXr)a0-e{x*uCh0?h^nD~Kx<8JT1BfAHwB6~a!%o!v zOidrC?n!X}9&$W5+*x~*Z@mJ0V9@lKOON@_Zt9NlAO0)~){#c_(Q@}OZ6b$1dy<{A z)FT~<*>ytt7x2-mJ_IT-q~F>x((g!KOPrC}=Q(`j-9T8ha z!lB)6n3AcyL4ZHd|E5j|Z!h-UKHOqVxQxO{v9)95GcNV<=m)`nBKC zMVYYj#5~4#F*mZToT5`Q2;V6MCyHRiin)>HBX^w*ZH7sC>=cv7PNCFBEQs2a&Z3oM zL5TFlqotH`Jr*X%FI2&a96ujAsQLpN<1!vnp_&EzJ?x>MX+$Zd^jn)uS(bfHA^O>U z7tAhm*!jbv1D(TGo>|PHPEC#nHte159V$eNrj3llyig&F!&JsG+$84akBs}28%`|p zyotR*XmS@nY6TF6dR3prChF9*BExbh3z=In3qY`!*n~nG`1uNO%KT~EX`du zZ9mB*tiH=0mav?nyAtMwPzH6ELD{95_NX?<)dm^lQBOJvhaKi_42xBcUx*p`yo|oP zlGZO=@yfJ|rjQaz^o1*U@{B38hrAq8p*eqg^_}LW0h(NK65CJSb*FbHH>AlYEGH`` zCnqQGBCTB&ia2OuGComR#by=C&a~NQqF^_Q|-H*{c9H$?HRgWx~&&IgP6q+&bUU|5xn}ZnGBnEfK7m2Bw?^|Cs zg=OFldxuCz)sgRoi1~Zh`N$s)LX(eRlk8xrRH9TUhD8>X)>`^9pV>2`m{L$V|IWnX zOYfySL-RP?OgeQT8%%G{7uwG?B zd4B#NdG)GNLBdpshBTYrd7q9ftF&gHrH5IT(O9oCJiE~5jQjVFyEI0T$o2G_->2i# zs8Fn(MW@2qF`-_)_)PhnJ+T-MgTpPGm~>}B5rL&RltV8!{l(Oe`FscU&a_E=zGd76 zhg*!d;Lw6NM0L@$N#V(Y!!3r*r?249o{qvrpQ_Vi?&&$^$=!asTarAla>Exl+;Ykd zY3{Dc4eiy5uvnglab+&b-7b~LS+9&mlbX?*Wz`4Q7IS8_4hhLvr=}2!MG=gu$*t&> z$>*hz>7>%vbr!=`o}yFpAL){Aa?&-q$%MvCu!)ctXer`|If?I0Y`9AzyGhiOvd)ym z_u7MAeplwMLs|coK0uU97$nk)M?N(8GWvbAhL07d$dl49qdPG=G#^Tz4wNpPPR(OM zv&fSOE=nn-R0hc@B#b6!Z=Shr)}m1VW~5WId)Paf^?n5-IesBVMGmJ>{K6G$$a8*L zF=JiT(Yb~)#e!NfcZ8_W6zXVlz1wCjYNuB;&+YTgV>gsNDz#0a?Qwp)gyoLb*g_Lr zMV*>TIyF0sPR%)mP+YJ*ltmLwqh4i0$n(8L#BnsY4@K)&GdzW+YQ7G|geIjzG=@c| z=DACUIyFzaG>cBnZRWs+Qz&P6#%NL>PN6KMLN&|kCl-@v+RWgYLh%b#Ajo17TawpEr z<6DhjTQUVtK*7ZVfxL}Chc+0oFm6RGZUBWMJFHd{_)TnA2heNV=8tEq*GI3xg%YjnmYXk zn_~*;Rd@P%A|9=@d+lh4K_1-cIqYe`%Ryq%1*T1Z{)lPQc*j)DPnvH!HJ@Xi=Eje> zXn2lzSWUz!uz&|FAPPh-5mZGFOQw9bQ`3gg2&|dn783>vrAx>bJXE?S_1a3Ii!!)1 zw>d8scD{9V^WD5r%C=UnPzED|3$ChPEs=qP8J3P=h>A#*illx5upA z!-0)My~<)E>J{nI4pz6Jji^&VowsF?NxOyha22Xl^bzcQSYds+H%F3Q%mNRxz=KSp z;Ixl;>mxRdLO;`H<3d!)iy_OOAt<*Y%WpwLaYhc+Si>r;1hd`!Xep(fHDQZ^!fi;- zq0YtAD@keBGi~G#Pv_d{RkXqtMtb#S`3{O+`SdDU!HNz^7nFj|C1I`{cCP{CuybX3 zIva_P*AYpOFv10nAOs0SdKJnkLMaCRU>qF1CWl?cTxA{RnN zlH^cEktG+aQ`nNWq*q3oQI@YM$cm~AX%Uq}#DTJc1!JdjSaGUKmp@s)G)1o@3<(iO z_na6+Daoj!-i(HNJ3_=!>x2OnVBlrm-A0Hw=rs_v$V$vExX3}GlB|IiLJV?dgtRlXs;8ufNn~cC;djAMAC3e`+TWXBC<%}>fP?#-RS!_^gVC9cevH} z9>GxS`Z}Tmn8~Q$d3+QIM z#1b}D_418dKsSAfTk?hL>BOxK%h*);^oxEzlrc%P<#cOPn6I-p*#|w@p4W5EIp>^n z&U>=w{G4;^*N;a#k)u=d^EYo8VW`*HB2S*u?Q)o^`NVB&X^b2&asQ>u~7>Y zY>`6^)vNl;DdhNinffhBuJ?M?D3Bu8r?m8{a}`1kd&u=2^`wyeLFV`(q)?Vph&ZI| zcjJg-15^relI0ZQOw=NdlU#-55ApKIkvJ2NV*^qOaVB2sAd^B7M;2$YXE@Aoj+R1k zCdNmZs_CGMO4z8y39!gg$P{HzQLue2QjkTap-~fuui*?!+|WZtK?pJ^A-l+(4Q#k& z92<~2S^gl-%;VUAyduubOCQ9U`B`<4Ys5iuC^L=?L@H#8!yYmUnPM4>GxIq*<^}d7 z$K($&&&w;wp|*hqgS4T*364aSTv6ZzKZvQALs^DAB@uGW;YkkV5Cu+PgP4l)2MU}Z z2eC$+K3);JC=*Ja+?D3$h#WhHKq3f{?)G5-5w&vLvU!qc5_M4~+>BxjdDC<57-}F(0uRFx!M}Qe3lb!!-RCazW-Uq^(q1v5 z+(p~ZD31HpHV1xpJRJd&$T;s={hfEkYSp*sC511-W9dYzS3}V%pG; zQ6qr@7|0L<6ik8D-(J&Zwtx;?9O1zYn!xIBLDNP)VFE66anywtSX~nZ7>uEdC#Ycc zH=$`Gi!cWk?7(3Ku9ozfHs1U7XWG1`4b1yBXxbQ+`t+!TaXw6i>~O{B`Y?`%Y0TU4 zFb>9a=>3W@4h*J57zc%E(NuL`Qh^FZZ79z4wV_;vrSB%DR|$Gqg$-gRoHAQsL+Zlb zS2CH{qu!@JCnzZ|R8B94d7ti}R#?E6!+4+eyukd*#K16cR%x_xU!5#HHS#If<-Wuk`I8G1YvfICDb~o_`^6f0(>28! z`O>wqM*h1du}0pzC$UBvaeXR-j}M*dhXJ@F9e7a0>Z2TQD8U~uF28xfEufbd+{FKK znE03%9NR`1$KV4cS$^*1_&f2PJr`%_-sOMH1%J5wEpp%!A{?TSh9_LC`yBl;VW7k_=-1^i7nJx2%p>dNorB0zWy|?; zr^~$;$K^18c@FF4ZM5Fb%WKUIedHjT+)CdtQ zjW=<^hB8lFja+agBDypdP2_Ajr<5un!onT0=-yyhutU*ox&81!(J9KqBefpcJ<2dr zk%?@(TnDJThw|Fa)pRtM=HjsC(sm@)b~GI!*pri!EPpU!Ne^O;m_v($sEXN$CGsTK z>vC&R+la3&S|Z=46h^7&RFrrLnuEkxo*EZlV16GyOy#D@0YqOW=Hh$rFU)-*nyMf% zMyY`12qT&{_Ynn$PiZb1&2{52FFjKgH=DPM>CthcOmK*@_`{#+YCfHcxpcQ|<5&7< zjJw3~hk3ZYJoaN4?1E?bAfEd+qV69 zIQ&LqG)8e3>s+q(^D}R^pBV3=Nm1Wm>rlm_QzkvO3Uw;RNi%87HE62hOwm z)Smayqj1}-#q=mg?uLzrR18WY&2{M%QS@{wUek#mLqMef=&x+2F<33;m{ zVX!P$fWR$Y)6sS`BN|Wj=~UEov?9@_5=G4wah&8>&5+|m%}9=~T4*~tKCEfT@heD} z@Y54TkLJ>JIu+w%{)yc~N?(aORn#jQx6NuU9M)TvbSn0W)}okwI%-#ezqJxjfzf19>gIvgYt0;Lb4_4Rb#L!p|=usDt_Ktv;(X0 zNHN&3_Rt@Sq*FhyHn5@QnsP{s+%A{PcSJcp)~fYPn;3Ixu3p6*m=P#6pI*f)ED=JY z1w~DY9OV_34CPRJ<)}@OBb`LO3h5L%KEB2&dIi!~rb(j46KCU4>&a1bMHeiw<|4gZ zT8=@d1ENJlG|y^$=K9{z=~SQQirW=uy67ZksS#b$sqBfOBDPOVM1!*$q(`Hiody|Y z=W=**{hTvMG|C!?Mmfx$gm>=K`}~6N-o2yVyBgR~USbh(%yvZ_)9PeCa*#ugUl5;W z0~^Yi0dkn}kRjM3*BmpMjxzl-UB*lgvmp6HE!runMLV@F{6t4 zf%7*w`@o5Rz&QR53>>El|3(I;@fk(1H6HUB@8MzU2Cac>JO;~rMiDH{XRbOACm%XA zpOJBKXk5l?)Lf>9%xh}Eph4r2C8OprenaCjUPA-7WNN^8j#*=8Cr0WXh#^xW##09= zzB|prkGVQe6o7@l)2x`&>jS@DBO(ibULcbda~cVh%59f*&w}g)vZ8 z6NM{FutyRVZy1!YPEi3f!XgGzh^R^_WUMJBnyQvXu?!{WrC+|#MVYRn^Cc;jVIYG2 zbbrbq9+I3QZ&FGrCG04-^dZ_Jx7>mUrle&Oa_Gq;p=l#uFQJ#vtB2&7Hd084CkP^l ztvF%(F8v(65&|1ap#bDsL?im|7Lw#6^RzOaNWa=&$YMUC`OAw|brE>{d#VMmd>ziIQ;BXx2^F-0SxUz4CvL_|c; zY=J5mMH~}9&h%F@oic(AUon{B9`ZaUcx30S?ngS@gcM={(dT6ODW8**K^|GAk2wj7 z!5$RkXw6-0*^!n|U}Xs2*zP&`NQZH}cbCt}V2agY@TQYOtnN9i96fJLh0P<6I@|(} z?C^18s#++A*E@X#5>wSfcwQb|AV|CVCQevTd(7EGc_d`JLyT(2(va!9BXV2aT`8s$ zlN!Yd7f_UyR$6JLl;T=zt+m!VD+*=jZBso``jkhMuCfjzY};rJZ{Tjq2+MPFIJ`k~ zlsam8r1zo?k*SKS?DX_>;y5XlSZ+aa;}DgpJlbqbnN;yhrQ!o$;?gD^7PL6UEj04U~R!g!_ ze)7nQJGEAMl$pfVS}CPevcM8)wT=-eqR25ykt3;H@u-PIl*{EaZ9EWF@zIGKXS`HF zj-NiS{1J&Wv0oC09hBRLUMW$m#9>9bmmTTWPe)tXQgR&Wg4?&e;dm_`<%IUxk?r$( z>H{@Zr6l&`f-F~O4U&@Q zCo6sv_fMzdTZ&G__A!F8h)h+Cup=Kb4n^6pd!dUm;dEuIbNK@)%XeS7#OU=ZN+KIJ zg=`qv^_4+MgyM|+D@fb0Od^9`GU(OXRC&Q%Y1B64vn!W$io|(TNEt3X&WMK>&=;HgDRjFuGHqNC?}Rp!g+sK0Y`^c6v(8>K{Y z1}~R%fjr%sCvw+&cI!zO%$}1od_f-DfpEFt_Lt{ubc>OCCga&l2IE=kg4t)h+?LB_ zyu_S@!}{&n1-Yv{yNtV0N_tqo;ZAaa$aw19{UPJ2XBUX-GhP&@E)EY*ea6L+xvJLp zt5dRrHWeN&Q1;ZtfpHn{Dp%`~3eN70%vBi7o_b`-xR-Gte+uKg{Fi}ofNoy?M-n(g#)m!yJINE^GnIH*)O47EfgP<&axg z7aKFob%5%tgDUv4<(K<4lJ90e-TL2073ueqNF-8cwX>|V+TFvHxpx%3;&6yW=191E zIwd*WQPiwSeimE$x{t6j7yTHIo-{j%OWNagZH#i7c-cPh7oI*H1|eyPOB zd7byd^MZP%flH>RNGs z*oTVKZP*ft>Mq|&I;f?}O->?_dPwq9d9;+|Y8&$Ot;NZEiA8z4RxYc}DJxvGZ3$^A zwMV3ToRmC#Nt9?8qO*NWn z8TD=$(%dCz%B7S}5=fp*t2Copj!8*bmStI%Ww|81)5-HZlu?hDQ4)#db0XS82c(Ru zygwbM>i~7~DB_4T>*m38);zE;uJR?4NA_e9aZs#;%qc&8rE>Y=^8M;LJ>)h~%XU%RuA|9K&B^^u5!P>xTdFE z`|T^Dv&Axoa&nTd_(8TO@3<(tM8ZSF8Iz;nFj66#=7v* zC*lz?Gl^s`FJmTH#yTY}mRNL(`$~@{EPFa7CC{hh!xD;4#w^=D9rQBRs|+7`xxB=p zR}{AtK1D^`r$cVBy?euwi%z{@`I?^&IfWcvT1LC}wGDSrzdFMm_Un1O&)n5olTo9Z zCSl#(Tjz3Z--U}#$qh>v+^(ro$$ltzjjB*~z4iMY07Eo4D4Y3IWA%26(JDHF+a;Ltd?__LT#wJ$#)!XAtAxe zr%*XH=T`(0ncmQ}4X3oXmf>)lu>?CGbx7N(eyWF9o|p2yV`^3hNcKNvxAbDf5xYJC z%r&c;R<*3&kn(ZH8gXKodu02k&4%qJW0sdZ6A6@)+@G+l?xB~GkIc_^cgM2&+|_x4 zhSfN>DvVg<`W?%fc!*YUua~au73KAE=fV}7O zib&h27lL(78_BAdJJW{cJA`D9^iko!n{t->edWG5>UQVsPfV_I!R&Ow?5vY+$Fz|` zJMaZ6K;(50Y!!;_{X3BP#yI`ipBU6|<~?t-9%{Q?XLn2+P^5)}$x~VE%IyC9Tl{{C5K&qsTP?jLqcM=8Ci8xaqKOy>MbtnODzmJp0PYL?$ z{R1l>za^-4m3Z-#>vaE4F}PtL9tJ_`{U+t!wW~ab)jqJ&XG&MZF>~!#xf5}0xk^_D z!Opw{&AcHZ4*Gf`qK$MSs_IFQ>%%Wgo-BfJ#hH3;2R21Ml&(()=)=1ak>y?G@dJIJ zyv6;+W4=MbEtGfPRd6ddai&<&`(m8Q@K(B!B|jZLXs!qT`0gFZhk>NvR;&AmTiiVy zvF(&jhuuWW^Sl@zf$icouZp(74Y~JBOZ_X@%R=Wk2hp`72>!6K7jua z7T^SUJblLFec<8oIkLwCp*?=b_V^)4kM~i;<8Ko1_#=pTJOqY#Xn@;1=D0ww!u%gX z)(P+!<1!vUq*tNLVLYBl%;Q_w#p!fAo{^8I2NbIw8&UY+)2r|RQuHbWLPr=x_;Q#a zmnm8vOOK|<(3(5~-~o7~dCg-;<24?`9zOFJ3UQmqaK{Dk81ncJk0An1fX5hyDSz>J z$wjZilcPMgC=1Br+x6orKmsS?76F0VxIG;38aDu!3GGJQ8Uw(g5lFaz1nxruM%=(*5$8byI8+i)O&tEX5fYGq50wNYVB-f4TT6XnCbo*1 z*y`;6h`-^Yz8;zT@v2n9UA zhzBUZi0c3qfx{ao;sH1wFoTDB1<)Ju1T+9QfQ7>ZTt)+mAmTbeSp&2N^vExw0SUk% zJ`oL&k@aZ613m-<5?}xvYKH-V960Rp7|g^50V19N2tZ(n7zkJh*dPLjo0f41<0Q<) zhA#s%v5_7?1P+V%5Dy??i0c5sz+ryO127I*Cp`cc(E*CV*8$paxSfb1a7g+PPk^@d z*s=f=(SQULk$~#}X*e{%vmCe402wf{7oj}?U)9io!<)BYCbl3hW@5Vz5Z5taW#e8) z56pzsv9i&C`{01p0UQ7X4!gTxCN>9fHokIz1AI6D4q@)%fX)G&4IFyf{fC*@TCM{# zv3-~c&jt>A+=m9(@Il928i2zf{xtv>(E)O4xekzrL+>_v;PAT-U%}w;b{h;AfcL2J z6=uTgvAPb>#o+-CgPE`$4uLp_nXnh;IzR!yA@?3Q?7n)J;go19HAl>VN37rw{0q#4 zJz}K>4y}d&9PYRY15nNbGqIU4pvDXXYRp^*Xa^2oFyK5;aA?AS7bw>OvN*J_Ve*+H z;5C>DEgvxn@f*ya5N zvVc71rX1gs-+P$*(kvi%xhZeCCkHRf%0;y%3kcQ$Qo;S=rr;wBILbX)0FF~-Vojyb z>p0S;W%6>l-pm9{Fd{>ycf!C4p*7@rwJLVH5QN+}9JGc>2@3J=_ik$5GNdNdD_r; zYsP8dRu7-SQRT5yC`ZAPa#2Xc@gg^oh-1rLYwg)lv=7kfUlca%o}OMs(W@D4W_-@i z(`_R&w|U}V`$VSAj5iPO3z;@Oyi-|b^5XJxV?Lr+Ex3O=VG49p<29!_&dlRfO_;|T z7>CAbFw8qt9PG^Cw~V5?4iHL62VR*&nPb3%Z~68v<}xvV%w=L?ET&q(mV1Qey)$iS zutdFoLjpTtJbpTGjK@z0*dWVwfMWcKvuYl56eiLumiJq}MH`y8;E?4Y9?~fy)`Yu- z9QCffmK);&ndL1vl)2@oOXK3yIE~L-93b#G zG@qgQ%x_SiIf!p+U>Yqq<``1uF*jtvK~P!dFpj$6mSMb`9*sLA^Kbbm@Q?Yv1HZjR zCZOqA>L z7Z>KXxIE>8FL5RuGP97DqqeXqm$`F)I$D0y@-W6)etP_ZKsj9>DmM(GGs;!|0_CD! zppL?F6ch?vL4hC&TtT5wSWr-2yh!xicM*vKa>y!52^X&1lvx@f7;yGEDo(H zm3z3Ks`(lBN3(dqG!|vjLsXtaZi-)APK?H8Fv>?pQ`N67AyRklHyy`uE$*G^Ii-%~ z##A+|)ZOZkxB!I~xsy4h-*Bx~4(gpH%6)K_Fb?V6OBe^7IHq$LoRLMTeEqbyleJ6W ztiQR;^Ejo6!HR!y?|#F*`#cWN#5ml$;{Z{7Q|cB%71_=`JwMzh60FHBID3|ULwc!n z6YiGj;E(R4gA?C$CxO|ymu@dO&%H)#UGxPJN4!zS z(syQe(t&U<&6DQQHIia*<;XHJia1Q!HF;vky?cMUKJO)E=fd z+=M!H)G^%sBV^P@v*(wi^@&I?lav?FRywX$j#kM@>AFG{y88@9UDwI3^&F>>x^3fn zmb{I_f#odIvXioRGIy6|)N^#+kW5M?r8IlYvUf*ypm?13t{q`MIWEBm{Z4afM!38E z%qusN3?kA_(N1!*N{Pd11}n3aboB+7Fo)E zohFu>?)M$`#6{!Dv1N_Ir}jjbY7}wIzg$vO^}Yc@rgo;98Kbk z2@`qCVNRAY&NSuXh>HuZK~!`qz6RxIfs3P+ARE#n`C@~s{=}Ig*q$vIAvb1$4K-J^ z*)}5Zbo2ytbeX7UN&uN3dWA(y$W2+}k1! z>@y73kv=?l!KSy|xNT}Khb8~$2aBpdRyFL>LmYSz0@(ff4N?bW#Y)F^6C?DHPv^7*cF zX?T8<{Ru(eW8Qw=zTED5H>_CbM3}2rsoW^kqm7KE@{lis@;TXJu*Pgt8q?Uc(VU2? zcoO7z736pg1}z02DQJ;JlrJf0p~UsmGDeAVjG>-TjfixRLsTVVp)_UV@~}^9oT_a@ zp(xS!zO{%0|2;%_H(Qtzj&ee&9#H z`h7#Fm{6}I_P`r6i#1_WnB8W#HukD?(@myIJgge9b z!{I2D`*xm}W|M2KX>y@#axa>EizcOq(kGixnoTmt`Dl9f=`d+7lSh*`Ho3$xn{>QD z;sR48VNJMo;gQoNUocP{!xC0-+P=rT;E2P!lyXWbdqXwtx;W>YwbojdNZGY$;%$B_ zLP1TGlyc6a?$nwf+Szz)EviORO8Kag@9uJARLP^d zhi=c=@h?s9(eyfuir9nw{x!Jk9ToJhZMM{^)+^rmz%efooWFIbA-igC4PZyV4 zB!@-HT)nE=)rg9&W~6P=si-Yk5rWCqQ%c``avY_f4eK43k$lCWNYtGRduo*BJI1mM zlEvJe%t%F^n2Oj@sP4{svYn}lm57L_5>bjX9iQ!C?UB}=yxRA6!M?piX6E6QK?pI( zrpIrHkWR(NST?=C9$p!Q00SQy+%2PAdQ@GViW`#c1thH#SkR%i;k^<~8%?KG@kWZE z$T3E7l*7!GqlpXbN)RmABYr2CapgK+AHjw*SEB95dx(Sh;>omoe&uDqkK*p0 zSjta_i3+TaQ>~|~1oy5Mp->Zt{aVzmQk85!vW4i(;Yjh2ny1#&^D0%kTv;9+85zx@ zqDe#qgUKEm^7&6nZW{_^^1dpcO2Bl{1s9nT`eF~3k^W-5Jhirc zKd4htP8aR)_A?(*sEIqS<2nvc>QyXiOoq}G=D3P@4s%?2Ugr38ILwjf$KyFfBF`GT zA$P+_^cvK4ie8yKzQTHCLZwemIsJNMLV$r4ImIMJUc)U7NS{1$CDN%_WA%v3<7Ng9(Z$XN_5e$2a=3(CWIksQ{`@df2&UL=R* z^6lr(VMUnB!@&3p24zd;E*R!HtVlm&=RmrolvGMN@yq`sTn_Ubjb-0!u4&_P@AhgN z@$zm+x?rczw1I&^d93kF8<+nc2+Z*>#sRuRL9)odkWbU#7@I=UW3+Tq>F?Yc|G*$8OFfPww$v6P!F29la%SnI1 zEg;--xx9&&cMwQ==dffjNWx3pz+C1=M3xM0H4Kh17-%Tyk<~)|lLW{^zQGs7Yu~K9;77tBpf>hBXb;(^uyfcF&O6sgWN$9zJZC6!61i!z})3v#Xw++ULF80 zK+(T*u){!J9`i1WKv~3>+XA|Ixr>VnZsLCluUn|pOLWGmq6V2lGXfX@000vd05C8Z z4hRLK;ebR@Rhd#g6o3tvvQBnb8HYIzW6B^x3=shU00000002M$BpUCIZ^LQi}a(8C$D_kqPPI|Y_yBEBslsSv@ z{oE3`bK1-ggS3V$=bjw-Ke6)>|FOioALu*g8@hUj9TfecG!{A?Qn!fP(eS$t$8+<% z3)cg=hf4D=Qq%##elH1h_i*moWVY&pSe-`|z-IwNp%F}yMFT*v!N71j>HS?;PRkk= zqCC+;t;E5yyrI6TA!|{1>omc+pYV212}cpGH)+3Dbnep&c|ECHXZb8_T&3NZYF8q| zFffM$uFmHk$mTs*5vMt4ip?IjR=Cy!opIJhoxFD;546Et48e- z5J+dN)*ItO2VrM&WsJt zDsv2|KJfW2%M0?>DUYGWp_;BI?v-A$x0NNa-FHUU-v?Z(~MfWPd#VxZH$G#bl(5I;S44w+nsdO-8Xo?E^7i_MZr%sjDPJ{ZO>+u}*|1*X`pkylwG+%x-{6+?@7J2(&hd0? z6@vMa?WhzEuWyD5s=*l}V8EbsAk9xV4!=qiH)7-Rgi8y*XrcslFp^$`C)FZI*+B{{ zkW_3m@#&1~9X9m&LJ}IGBE}i!;XPAjJnIZ>kALx}Sl4S&fNhkvQCdQ91&5(N~) zs<<>^AOo_1FTHZ2mg!(Afyk;wjKpu9JALb0wkyQ44{k5Ylz}d5s^~N%$4z`AzD&6QOAEqt|XcUA3kKraC6DoV+v^(?d|;3CRqWL7gETW&Ghy%#0qu2 zXh(H5;tSIDwnLrAVv*7y@SxYo0Dk4 z7F^XkiB;_CJ37vOEY4z`bt`qIDCI<0f!VAja8vhfIMICzVT1YrzI~js7&~_>V14j5 zk=S|QMQl=;`M{89cJSD-CnacE)ycO(NWSk+1LK7|zff$jt@R<^Cx2t@SZj-S(2z|x z*OMnYmfI)-=Kx)QlS z9~Z&$tx3{n=bYi*jA9HP$VGT#(G^fRtchO)^qkIB08b|om&Bs28BH6DX`6yJ696g~ zqENf_D}9fTR$f7Lx?XD`wCNXgcbK{hUF*sf0%GuV*KENT;;3}U?bb8=l;lh>O$`53 z&_L!s1Or`gqWaZ?aW3MJ_>EaXRbUh;HE>j@MQyb&N|qZnX98H*pFL>P^dRd|(jq_> zCGeV9aT@%lmJdHCE2wDnGGs<=I6OvTw3^g^P}X|6&^Bdx|fHt!kL<3bymY(kBbUz~%t(L<$dWR{R9&uNtMee1PfGN(3dlixm>|jT-L9 z6~qvtqN`D%h5u@(<(s9HNjOXrQ;&ZRl{6^b#u!O04n(F4T}~P&u&-q`BD;&~zeqO9 z)E;k=gAhoY3)I14t6f~EQ$=b{Ny{(fKju45(k+Kv1a)`3A*8OmlvS+KD5n3EI4GI4 zx3K#r)j`WC(fh;0L1JJ9ve&mnLu`c-r=RHBScNvH+w7<%ozn+Y!7#BX1`Iu)Q}GdK zZ_m#}$_qCF&pC9Jignb|5C-JUhA_YVvOl<~d?ySiAgE?QC*HP{tr80F`Dt*; zIDN~g!-(O{jOtXB5ft_B9s#H_Mau4W6-@1%OA$DBU?=Mc3=*XA#Fjf#jyIaZ$u)=*O-kY!Lul3BI6imB-Q`gq7f?(r6fU zb_i5duZUxbJkWy#fcwQ1X^t4T_cPPKW}&t#46J+{(A2$VV4al{{tQ?NfdEmC`>OQ% zPEl%`Rv?gX-0}-EW`CW3_Y#Wv?>X|{e!Y}oADWzzuRfWCfkp&2et1TtJLX_Zaaa5U z3cYrIQUB-mD=@gdacqy&hGV~9oyt#r)y#lS#CWRs@$XpJEm|d9OPHbc#8C(np;s-Z zYL%^HC?D*gi5b~x32ai`8&f!LE9;r26jz6Ak9t9=2E@jwK5S4dVa~fspL_)d>)(zEU2FfAhry5pZ!%L zadz2c0PFAU+J0A=MBj8KzQCZuT>}`yu^MZr?8zm)!>-#L|A%5G#>|l^5UBGYdhcLaMwAh3&87WN7)Nw<}olhyip~xzFusQ;VW@&Hrz{r;fkg^ETl+$Q0 z&m<=wAtRLSap}40n{g5Vwp{$~AP6q*{{7E1xLw3BnKgrl?kvm%*1WyDm?x=q@`Er* z*r8Cid>Kc_2O_0eH~vK8dwgo;9|~c%R*QEM0f&TVC(jRbOS^()dVWrCMMJq9k>wCD1d{D*tgryj7I49h3tM|A6ULV#1{P3gPJU@ z1qFS+z8UM4?xL`TT}z}zaFo~^j2;sUa+3Nrh{P>Z&nV|g4@puV{ml3x!OLrrU;Iwv z-GhcJBfWWu1}MG(Je*Qa6XS&g)CfHEALHjvc|hF^Rm5}qD+uLY08f0n-kB?Z@px%7 zh!RbIHH>2}ubX1*CNd@zKKZqji5O+G0;EV&ix3MY4~A~Q0dY8RqUAX`ukTg&KbBN> zLSNUi!tO{STn3hNLJH+~t%PeK{~%GUp9}w#EW1JkDH&GCASOEHazLi=lVo0Ywk#2g z$(qW9b!23g#yV9y!ljx|6L)>XgmjjO+yv%U{?&ZsYXGD4Y&Nqc9AUf(kpv~li0OIH zLYm4J*qvJdtm`uOIo11+=0_(Hr@0RO%3Ja}E%j*BCdh&Ni~5wQfeS?_IyvxbQKC_%3#n#tDC3 z^C-lRG3u(Bt3gn*=(9*~2=sm7<}6=bk(BbFi*%w1@x|LnSyiy*eP!DTlv+c04Fqo2W#h4IiT?Z)%Pn*Dv7zcy547PE{jLpd?Gc2QHLiyPSIJ2Z6Uz{=E5ULbUR$k}42eKUH2*K^<}6 zkzSe{`(}Pr&-90CzlN>C;B6l$=HNpy-^AHLC1(f?ON_X* zCq*!~9GFQsv|FB#&;v9l#!sk^QpXVFJ#`A;ABW&!ToX9QB=g-qW;JszDE3VssUH9$ z?63IM7SgL^#teVhrr`E8mD5iOh)@Zoa6ZUPHc+)wV5Pt;`O#dZLm68Yx*9V%5VCbG z4Ynp)#aXwi(mP}IUV>?=>K;GxVMPF~b8>4_BOLdI`P(y18k?O{{cg`P(PrJ2>95q4 ztEG-yjwwx9#LkYsFTgNDH&YYe5M0xfYHLW#+|t8DWD}qfiTVuLW9c}DefyxGs7P=H zeQ?e}&DM%?{!Elu*(5RLzMIrf4x$9(Ejc`I#QA3lcrkuk}ZZF=*Zl8m8qQn7MQxgd2$PcoD;qUh21FKPFPN`Q$h zgJVqv+7Li>xLusM(%JqM%s=sQR(~>kfDQz$U5_4|$$7T?ZkVE70iZ}{UeIMDBo2ld zt8T+Jf`K(RRZopVn-`1!F?uysP<*ab?@qR6R;#92=f#tako-uKdNe9UeWw~f5l{3p zi;|98C56{$&=kJKUhuqLJo9dD($a#@PfaBt6)(?jY7gle)7SS-d3y_$-vrd^~?-LXhrWSXKI(|!GRA%>ORx1CMAJnK7^rB7^JDG#Y^WTdXW3j5}u z*WT)f6X__&JYGrLGIjFTBmS5W&T?py8JOmP3kqf3tg!=SyKqKNXP3x2$#@E5IcCef zcXL&9GHe59R63l)dr#4_q?2eX-EtnHTrz>>%jqcP3m2MK{;(;ww6UwXb^7zbzn;ZV z6xJG~A?trmI6T9TNlQxcv!er?8irUJB9vg#2Q2#!ZQO_Dx<|)`oPf~}6}`D)D>dyO z9Q+0`6v!Iw0`w0kGiiS+Q-oJy=%GvLwNzicR*ZF_QUqzXv};yEKq(0D1_(M_)Y5SO(9AVjKRP~BpWF!3<*@Dy$Xq$ zii%j3n5b5OqLzw?P7L;SQZbtdB9QD|qKWET`oKC&xxAPHSp%E`evm21M+^-}8BlDM z(A6+Z6@u9+eg2Ox&H-7$6n~SvM4_yy&q#Wvr(L`)80eL;!~~0>}b+ zRn4{Hl7^6PLpStct$&uqh$Hh%rd(QA1slRe&QS@-gRNy692V}vrfreOeFTqu?lT)| z-&oK%JJsMez99&kzQXqSP+-*4r${!7PveocD=o7oKS#}lzE)$*US{Zb)(HJ<^h`b4 zkN9Hs8BytrM_)vi+>+br7a+Yp-fRT6KN$bc0feB;YceRtsW=0CKsjgl`}@x-PNvc- z2LxlajymD#{->RBsY=c?r?rQHwM|mQg)JK+?a$Qf?sopiTie!Fux^02rf7!Y^KSuC zaMRm$UA5+8FemadoXgK0mL8QOE=Z&;ToN3VOr*1^KA*Q}0zmA|3&ahxWt#m_ddLcsqS z3g1Xy%+>!xi!!whbwCU}WP*^Rz`DD?HEv{xT_7#_F$QOGlenlz01CA>dz4Yb1&I~K zz4kyg3l(A%Yii`;QG2CCvkkfQ$nGpR%0g-U%kvP_!tbIQ#?D}x<5I3Sgq6?AHiw^* z*jysB7_Y{38(;w(mm^(aw9I+{S?`-00O_0|fq5a)mG4ZKOA5D$&KfZvh?d zgk9e(E`m^3F89Fbwq;PZftBY7RJ*_SJ3BT1chUf@qS!L_JkdmGf%t1QV<$saLrCRO z3}#m6T=;L&K@Cf-CEl2(HmpQ_&R#An*JewPj(tiD6)$vnN=SJZJ%p2g$qtLVxS#)6 z$Y7a*n#UX{J(br>!{es4G7;F`&#Uy&zq>QpZEwvuc16=NK7i8T#ZD?pMN|6=+;e>K z*<^eu#Uchhz*IVt6{c94ea0*9L#fGzuMIKahd?U3^4=#p;P={E1QXsL{2eF-2R6}3 z4AN2_1pQz4!CwcvWB=n;bXFSTK-@TrZWJbY<0Gj{R%Jc_oD8h424B`9HYt)0o@`mC4}J>HJ`Ro!PILY)xEqDT zJa^F4Ls0p0XAZi^QN;4TN6uE$M14I4d{)Z#1JffRaMApJF)S(YlEbz_Au0PPJWUm{ zs^6nzXo1nlFXk_rJTOpdVsf+=l`buvj#x2uX3DTYtPB`xQjE^v29KO;jxP;XE*>70 zYT8l6wD;>6Jf6v3Ss?D4SHs0Xkr(s&vs+Vj-p3fuz-FG|bb|xtn1us~(4#_d=;6)1 zYU`ttT64UB6~5-DXuMaQ)7%{`L_izr5N>A~>8Qe`*N8RyKl_=D1uWx;8wr?iC(d#{ zO{fjkX&Au<9?`^?&sU0UqHFZgz}SdWup~VkrqJ7ZB7ym;>-&6<7*JdLKi#Z@QHq_qjnaK4ChrMP!kK zRlwQF(1nTg>_2+o0ZhLbSklcOiz8Y__xjBC3*LADM<6izxQL8+vb$EcKkozaL3D)8 zY;-z_TPP({D3Cgaak#lO!tmz^!&SD`Xv#ki1X+o2vh9oH+MJ<%SlVjv@1TVomP@?B zd#PJUmVB&Kcu3Bc^nA)SWOWoX5oEp_5*XPX*&&MrATo4W&m@O{bl!Vxociy$0cdPA ztn$=X8b0*;InW3R1!7Ps8Je@>AP(ly101F5zI>$RN`Q;=n`JVjB;D1I1Q5W>I3t#) zqldt4*-}!Qd>z#L(icTWBr&)}4DU_4iqC6QzM@+o5I4rA5ZHf_K6OYKa&a5J3vK)E zfD9i8gn47G2#-d03Cn+b2xQ0o@@10nlu;6>Zp43G4^(v0|K!0 zg@XyRKw~AXQQOe0$mf>&E~(m<@w6U93gu&Zj9TvVxagHkt94TL4N_q35XT@MyFmS# z^>LfrKLXe%#?+2OWcDvs?Rku{4F~FFQr6jS3%uMU1%*q%}?PJB49{E>~;>$({oGD|b z;2|FyJc}%Jc<`|@96S(!3BbpffJcp2;KTc&?XACHqAaWct5SW0wHJa2;LekBz@MlK zh5H5}O_Cs97_*1JK57)K2zZz)Rc4k~ew_MK5<8#0)`36sW6rnX3d;YWvbh4m=7*O! zP^!(0%fpJxS4I(I6n ztRW?s&VFq!zwd5WPI!QkHM5MQbq%C6_0W&GtXeb}vW}|I)kYaZ7YUyTtObY`p}I+z zvd*XO>8a$ul&jLrNfKrNn$+EWKuRKMa?8Mz4qmPjSFk&fi8>k3`~wJy54FI4-Q}|4 ztgFGsqGCoyD3^+WHE>QV&Gnb51@Y++NJutsj?{BxV0fJO52d9P5dn*!kw8xMzW>e3zETtt6701OMj$(e4(r<+6w_`TZ41eJ+vjg2a-hvzA zPE~T}c+n7y70_8$+;z~mNHCtY6fjuL&;008D*=#!k{otcI3w4Tga?~vU?^CKx*WRT zDnYbM*FG`@_?cMd2%Vr09rd~$CGSAM_cUCL3ECEmJmBdk4`Ba{%F?vq<_-hJP(0v|iVhkZ1s!!87A*I7@SJGAE)&*cq0hl-? zC>lWr#+b+P-;Sy>`c)Tv-v$cK{2CDsG%*`L+xSk%NyB%Lt6s$&MmEed3(pPAIpWTf z(~KXD38qYX(Z>b%7uA{+>jjN5wrcq zw`(duZd?zdPe0B2z`LME0}r9GdS!Ug6q{6vK5Z@A@Br7tn;xptp0cgre8ra_94NVk zqpe;*K+vvGFXXZ`34yRl`K};l@Kh7dhZwyfzydal)nc8 zkn5WPP2C2-ryQh-ISf@X#rNTS4}hTX z15w`)o$OKMMx_E;A{Tj1K;5^!=g}QrpgoWlhA}q2cQ-7kqgM|CL>~c9w8)= z-$fT!2W2V-wiz$E_b2u2&pV8<79qf|s2`UTQf)+OMY1~Cl~Aw(oj;-|w0c{L4a;|c z89sXNqd<78U?3DRsgtaZcS>p!RA$k?^n659yYWDj9NKY-#~q(S`9$%U=ux_)oCE98 znFyvFq?(}*Oh-BjGD`IG!k?6MKf=kva^^lK_IlSLyhMEc(rvS|kKE2?oYrZW=~U!Gv})uTWF?Ry4Qmghk}-g) zpss|-0Xt54A{@p)kHd9-c7F*Apvj^fK_=FLD~DCH8%V2h3Y6U;LjU%b)O;Wu;vQkQ z&sQB1f%UpuIlMd~jyk@!&AEi#Z~xY<;4##{-0}kp#&%#wlqtq?+K@r@a~x7mQjZl27rurt?z*0phlvhRl*&AC7fZ_G*oHIuQ@iTtlvYj zt14hY!b^xlibtyE6#X+2?GnN;_jTydT83xNB0dBfox*ivaIpiF04i)6vG%t8KKVlC zDx)uwW2Og!z=w{pmKlzad1^k~&(cY}4BZ2|CR$-7u`^eVF9|eNnFN#G2@7_Xqm(Iy z!^WYn%&Hp8oLD>;X5Z_e1{f+%(fDWO!F7;)R?|mAGyot`eBwNeoYn#d-krmXt$x?A6SfbsMh>AK3K0idcWxeZ-Rh3gW17)`_Y2kdihw{X8|k%@~Naa z{3tLjQ4ppC--6pq0(GiRZ10&aXv8q#qy}TJkt0{Xr}WUm`dE^lAU%1fjwP+06L)*WN(Nq7jhX1+YEe?PXM{P z9lmdi3vj$!O$ex8v*)6Y1Kt_dK!Lo*S?hIHEC(x`RY1mx4Lu1g_SYajY59uNR@k6% zq;vSMSrTy{WVD__Nw_EoG5K}Q<8VxqlU75(j zDrbJ^pgfIWT5*n00rsR9 z{11VI@exnf!JDQXR)L@VL&3{v86E;wR5fI{-U5wYdmlJ4*bIIsOPknSOGE;YGHIsn zygz%kP;4;knDTXjU3(= zMiuzu^WRPM^RzT2EUb?uRRP0`H^v`Wp(*uo56XhKX-Yu6-}_>ZUo9Q}ad}vCP~dw8Cn?d0~d(QnCfd8@1BSsWl*FeAnB}* zEz+tMG2r>#7&xvr7}hnoAdq2&8R!;3s%cI;uu8(Z9(8;`hFi0`k_xFy(108mefaY* zy_+`7-dbh=mr)D{kAZ-(3p4d9T$pCiffzkMAdi7oCFfvDBhxBomIzWv|FDQqz@+hz zka*)?3}nVcrB!45g=!I)5J|Z0z4>b+aLqV%x#p9)-n`u1)o0QR8W0@tipX^ug+^um2M1wIJV`#=h^|@Bd z2O`%Mo6el3Ipa%Cf@GSA>A+s2|1k^7slt*R%-X17b|Ljddd!8v!22wT+ZDI>$%DHh9kky&ET#5_SVXWJL#gu50LDpo_DS%5D9>*%e)QCc{U z+A^9ynYD<*gMFx61yO+uK(5%ioA{5OVqThLCIuDJbU{X=CL~kKfY`;sZ1^wc{yF3M z(6kHL1V+ks^Z zH3aZt**qp}hO^)hSQ6y{j^Y&6N(r)PEvI;8koLT2>)Gb$s zxX9}SJQcbg;6qGxulYZJk!be!uG2%tJy`l^7thk;ACC9JOM+H`NC`R$YU>HEB&DG* zwh`p0_N`X^Oq8Qp2fg1iW@vH|$7>i71ok+lf%-gGu?J}DI=z1?Tme~lXOQo+Vo+P7oO?T`mwH9ME17=;M~qx&U9& zx2L5DO2F?i?oW$74WT_I?Pa#<;byFC2tD*Em*JpM@Vr0-Xgrz0u^=40`(+sCEw)67 ztOd+$Mw#_`4Es!D?7N%}Gm~<^RDI~Xq#QxAD~5e&lmEf3lEEBCO);?F4{9!1lc=(V z35~p^4R$VBH9V7YdSbEjknZi`AS0?TS9eh-4n!ecj2ETcPqWY z-(`1GA)WETpwrYyoNj=zJCWEWyb!h$zoBMa6nOi!J|81aODm8*)+zvyqJ(0M2&6wP;LGdq$!Ll{A+6DiD(GZ>CO}mWC0t?LbL;{e^RFd>yh6s98n~&3v z=cN)Jr1L8+*R-F2SyPEzKC`c_*kGf4E(;bu5vIy!tNfYFNC6kRU#>_@waXCxMQQHC z*FZ{cm$`%5HGo7hd(%DWMuLKKD!;A7qZlPOjN7Ughh9<)y99Q`EDq%uX$&5(G;pp3 zd1ovpR-@a2UvUFZrc&g?jf5AD_a)L(P9w8nHh@vUv~;sZpukzdFP52DffoQ2**esH zD2}D}1Y_06lSnXh89ptey40i4q8F;nABTdQU@!^D+dqJc@ zMEm67Qb5Qu^ytBG!9G;5XKBDeN$62jE{nQB`5GmNAP6FcUtBigR;6^64>WU|st+S2 z)=&=5qQ*Kk&gk(@rG;kf(qzod0!sb8iH(jM3vmnWx7UF&xNB<^tU^p zeiyjJuzDvEu$kXynvpg2_2Ut*DuF}|-XJSjd@k_4DIrvn0>UBBez~`#CAZGP_+kE} zK(i|NrYUdR`E|IFSVG^~6u;stI$^ZKLAQRTJ+SzGC_YbA&04cGi0ew_AVc?4&*1Wc zQ5&2U?;a9WiqX=s;ltFj*ZG>H2h-lHLyKLq@M`cQms(OOh!|^2{fDJVR`6xmsn~TW z6}Kv)9?R$hm+^@Siad6T8_@iFCw$sSwZM=&wWgu(*(<#NGfa*H17CuH-1Dyn#C>(V zWBF59F5~T^97ppP4LOe{rMZyTytPE*+LwRV+4#P;nb?8AFEKr?C?HNJG@^uXbA8KIgD0SB+D8NB(He+O7x9zlQk$P5)(BwXmX(q zyU)AsLSa@(3^R`fs{KDRt=uOydVp&VN6X7`w@X{Vas(SKkCuqQy`TGnjk7(l1;;x+ zPk0|N6JS%cA4iWla55}mWmXf$as>Q*+452_w5RvgA(9`y?z|{fdsey*;!hx06qB&V zlRpBujwSa&mzpqT5eL-|mIbu zy(YHSTw%!LKQ;ULzHV14M67903}k>SxhPS^BmMNgNMt>#O_#T=%H0j*=vczd>e zr)RYUUe)dvM>cYC9h2;le*}-riZpSQuXXbnMhXuO;IUh7>7$;X?3yU#f?f#bd9-N1 zvQ|M>FMKZo>AjecI`lF0VQPD7KE<8r_~@>#DEc*tJlQ)$W-RCulUt{3_5P>NGXNl1bMFaV0(`A|++}5JPe3+JGWJbHr81FiPzjpEXUlTX@#5l;K@27K z{jw=JEF=X-&jk!E%*}1NXZC`iQP!kKrhgB^a4G#c1qW##_~}d}EjkPxOKHCpAY-sC zu7`bfVFsuD7JA;U<*kLRVno2_H8FC3CO->9RJ4wa~B3vyRM zvOyOPj44vg9SdJR;rc=_1BTlCb=d1J2x7Z<5=vW2Wyc(I734hdb6XNIxJrF97D*Ar zk75m9=2K3MtaN%I{+<7MY0_QT#Q-o$FK?+5DM@VYisgc)z-pg1>3?zc1+}HZ;uwhyYWwssFxq4lf*Q$AWCs9l z%~A+EvwjszqW=oJbT9*xa)2qY-~7@nh0&45m$N~k7P|7Ye;x+1>)DJpJBSFyM=`E2 z69`IU$k%6K3Ys~U8k3^*MO82)dzZMaps2`4p=)x}@W-t%KC2OE=4&-tIQP#exy3Jx z2U@*O3iZ+C($UXjdD5R)uQVnr0Don#NYAQ{gY#LI)LG0WzsavK}J(0sWmZYQCh}*pRAY(#n4*Kr`kfZdWzDmNT%$$ zj91kF7Ym%-mmH=nK46WrVA0WTCJ*XMoXaaBEy+-T!pu`3Tg~Orm$hJZCUc7!nIrwp z5)!8XRU3ZDu>tzksSr8=$b-gLX60s2h5lg!l4Xk56;^#)XlhQgh#X4&=Gw>8k5+F9 z%MjNccP}KVfuACz3E!C18XG4A6BN;+qGtSuHV{C zR_Iqx+|>T<0aC{P?gf$iZ4IN7oWW_-7G{ajUu0w4ZIdr?@SVk7{dL$WwWM04D@++<;+W6fqAtSbAo#eVbJ z^tmtZsG_1q{-z`OF2@v*b#b<#2`LI=l!y7VGb2Y#wy}oFqoF=6_Di8u0t+FfJ1Bf;)(1PhXvRQ0kOq~f;>Ol0lQxb!O=&aiE{%%DF&d=^3>6|P zfWl5421u-gG(GL8hV}#`s_AmJb<=BQJ|$<+h^w2aX_A;A3qWJ0in|f4Ud*>y*!xld zqXQ+2`!y9>bT#w$sAu8TLjQ#;pur#Jv@oqB1_9&ks|gU?u?%T%5_&gjwh}i8w>`K> zo#=Uu&%lddEeA%tiJAp8TnClOWFSXk4t}sog2suAiDLL?X>@)n40I|44U;!u#dqR9 z1A||9M8-lVvVzp^TaWgIkQo*)G-loD=S*`+`+T7VxhF;B+F%qfb{Z*tuy>CAK150#KmXcSDn5h?(;l$g8n$zcb^_j^Ab) zJ1sl#Fbn}Z?#dEXckB+dc#e8$9J8aJc#h%GW!l zN>L(tu&Zy<(WoNsIjuRH(&-y;4$G2RtPQbT0O(%|Be*o~6Xl60b9jbNPOJpVe5gq3gTKa9P;GV{TKPn1o0 zM+Z5iI`(}eN*vBo8AtM>Agi?5kbbl#StNYrmBTKW7u2$?mJXt}oFO~LG{V6*Pg;fD zCgxL|}Of27iazc-KLZ5{Qsb~MjJWWO*EbG{mzW4wt(-8DK^hPtEv zzS%I(y0;_s!|xgaA%c)QTk&^7_0tt6``;0)M->CNHoNi_{sN-cBnpcYgEEw0#OXe1 zqD%n`0OxF$@fAC~d#7h^=uTXEQen~KqyL{NDn(J=bRM={lovLC8JN4a@W8?Rv{uxz zJ#{6j-1lo*I$;|FB_RTyqolA{UYJ^MK<{Nn1Xi0e7s^Ep;_CjV?hqxSivqJCstXR+>$2!)r~{Z}T3C7m zaspsSy5pQf2q^zXFur(^t7XhA4U&2q@bQ3#v5+m!-30iFkghBn-;$dMXNxQJwkNBp)?yds$c zz7ZR6^T2aS1)qd*4}@|_@>(TopLs?vQuj#Oxs-5sI@5COJj}pWCw1UvQi`k zb4T_CfCx46FN*yVyJOVqw+S5f$znKa7P>6inHQJ_yNy_Vtzdzq;xqUX*DF{37h7 zMgmk(>K`u5!+pJz$o-)h-UT(<7$Xt$LOFcEi+CV^lIkA6*zxt&6}=jB-QerdTI5V4 z+_oX!{g)ChTQ3=$cW6<)=X)w>$X@oT%fkBG1EcOcA+5;``h`@@O87!&v1!^2UlD?f z#=T)L1AC(hEv(w1z{_1|<1oE+m*e;fg{cXhx?F07sB#woq9K;GaEq0$97tt^BnNfL z)^ntQ?hIXpfn>4yd$%Amkv5|Ys{G;rS}>Skuv&gD-+>FU3gv=Rd$|pMjzh^z_(a`W zD=3Ltk<)<(gbH2{mP&}qUXL2mZGa|UxZrd+Iad@HCH*GdMTf??v52z;5Pq}D`2M{fu5V#~E|jYVLPad zY^ZMReia>Kh?Dka)so|Oh}X|Tu>!a3ll5lyV&^KE8ojLqWOK9eECbw}07Iap?Da_R zN&?xB2&j{e^lP|<Kbwn;Y2l!0bCoTnYM68tPjsm-2_*E(7?`M;4fLki3k@9;zX%t-ydWn6*tpJ`qGb) z+8j$QV9U%dC%||z$FwV@ag)^eC~d>HuOmlWk+S?cG&vpeBfFcsBz{{H(GF-9>O&6x z6ntZrfA*g!2>Rf|TwrF9%D~_$2wMbkG&POtZU?ZTPqlgSYGJS+GQe7c;Mni|sxbF% zH)pGIXai*?<6+~%Ezm&{k75azIxQ`rvNHqElb)q(K1gh1tGb2@Y)5;s(eR*_FOo5y zui-#u%G|brS2_$nD0TIVczt&nX6n_+hz~3p2m^B%fQ%~luuYXj^{kJmZ8cZU&o}+$ zoN`xluZF_MN^TDB$G2c8^YgVnu2{>9=%*EA5~=rK{!(ALp3PN0fA_+?Eoo8SqQnA` ziCfPsS2?rMXLx3=zWB#nBYp`?5ayZ|ThYdjaF#lsT>iJzsEU^C^0q0+-B4n*eK+^F zx2)+{Rtj{w+zCa&j1UE}&Ml)_Mc12y(UKYeOTi~mg>H`1d3#-&xW(THpAt&sWuVFT zNVU*HgxS*v`9_68x#_SGY-fX8jOF3~8vmW^lv=#Is6fK>&NNw(E# zNLXSTHx{#7+n&ZJ{gZd4i$PA!V@*vN74FQ$&Hjpu1W z>%ihxa!5EZr8&2@N!#&54g7u;doD-!Ciu49&Qb-=`s$qN>k=jdmJ#%%W`Ji0_2aDF zKUDIOrd%w3N^G*HB1|yH!qXthsZ>058jUc=LDYN*;(wBZ}HWg)y|8hU8RAyt7B_v8z34VI0D)rXUVum=h0@VsQ`;2H^&Q zD@W;_)0LBD9+dkVsx|E83CRg8bwi%A$bHXRsqPM13f&;Tapcv_V?OF4b31r}vFl#j z1qKCb*KA?8X2DU?xQTE;HNv{_1HG*j?LwHggI+$T^-%qYR&^-#uR0Pq*m$i25j~Kp zY?1TM7UgMX>yQ1j04i??V`}{sqxRO-Dy;S}n@#JUPKsxes2n%fdM{KQXywrHx{h{? zSVke|pcBj#30()zDw+TEb@mE(OoV&0Zvj=ez;}xd{z8aUMf6H_l$Vu2pW`J_Bznm2 z!Ghlb*lAw6SPF+fgoIrS(R;dTW0vbr& z)J&eM50p?y3-`ho>l|djG}i-GA%YW_&A5l!qUwn2DPWd1p3&PkW8@shPWuV*d3xRo zlFas9MHLU&4_6LDZ8x}6`EE=1+K=Z$W^{k(YAUbuD~vb_PEZu2i7{jGyum=Afl6{ltCQh@I<6oZO<(K6jO+Q}ZT# zLo*Gr7OHzFq9R|%6W7_LVDQs>cE(FS-GfgQ1KyIF1F{TxMQ4`v_2MX1A#$&LS(6UofiHB!ksvQSC@3uYwmesK zFDuaFdNXo&$C&p>Bo)oa<=a2!dXl^S*J|)EA!<+@$f$AI$CpI@Eh_bf%FCbOgA&#J$)pR@pWS&H;c%)iSR< ztB&KzL^FTU?hzPSb{}2Il1J`QKbedM zN=eZADCPCH&*rEGo1Lp|u+FySLF$rU0xEh2wc$D&pjKA;&{(>f;S!!8>PkWQK3h3- zzA_8Qj9vvR6kMTReWd|woW;^BW|k7!Nl2hN;!uB>CE|OfK`FdaiG+Hkk;xmxaL!x% zeap}Cno_;3*qk#p8|GsbxH32Lo4m0*%C@mJoM{arh}YzNZ0%}(oEoAfD`{+BXUlU` z|2C!AIIT;b`ssOaSIa$=iX3c$UnMS@{r#Pg%nOb%Z?MB6*Iq~TV8;6cb;JlsT4zRt zEpJdhI}?~r121xlKLAh4G(uCDEZcJ1kx5Nx+O{Nt!Tf`#BZMy$^H;ekv0w|AsH0l2 z2btL$I7F=B$g8SpiC6>^cm+?Q(K31LAaE3PH`(BuL^vUZ(I6=g1YK?x+sDg7E3k zRy?t+30ZV%Qu%&}17zndWZ8Hj;L)fu1GHTz4e?{FPdY7H=EQ_`i6Z0c8xwJp&Q)Py ztS|c^DF0`~mGN@Jb5yMP>&PNbRrH2WlwQn#y`=|j#Zo^(8bM6a zINgde4c-j7lxukpEhP;XCA)CyyT!E`V4y6%8b9D0QoN>t=U< zo8^ZoVRR{k1z@$PVF2S_G9IYlrHL3U2Ye{qj6iEO%r_*96)X@cKM7VF)NI*ce2<_zfGx~|os5OF+?-n`Fg@UNHUh=n;veEBEe2H5oNO^WA5iv+RUi*L#CK?GXhEX$u#zU&dmw z65Jv4E^j3_8!wwFRh~nf_AlsT4@e*M*fX@Gf^Sl~#~jklZ97BBcD2&&jCQj^ohxFQ?OgFHRD zPI#9cIE{HcFdBnEWKibaCsO356rjQrt8}2~z?ZX_B6rSe(uD{th9@H^Icqi{%jNou z0FC;@rD;(3Ritf#uvo$JW``8J$6S94J-WGLJq~ zJNsklDnSplWCD(XZ+k&A|8v`JJa=s9#b$7hit^;}rEx<8ivg7jh&MbdjWtI7UerVp zEt9%%b!@B!m|LGQV;zDfJE^?e5RrhFBlcF`UOdI0a$m0UOU*;ToD9kMqh9d{_J9o> zs*vDg%!)rRsRAjUHPUg28A0bd5M| zEyH5K!jROXU4ry?5_*ZO<1?z+)xI|Dn*oPwy0nqEDuWi+B$iO=fa;r0vH~Iq4Iopd z7!*2K`|XEM$O*5rmkR^D%RyRZAC7=)nCnvsecuFf0n=fl4mTQHr~{|b4LE#o4l{h~ z*B}x?dNxp)Eni)Z$dlHe^hRz@vO@okn?*8ile4v=moR?%peLnv$?eFz1`ymgTu6|_ z1#n_f9KhLr49FSG`}tr{!~l%BB|91qbq{6tUJ|v(U^I1hx5L0g1mYo^y@e0pkL_hB zZ6bk)`55kNdqo;37&HoPfQmuCQPA;QwJjD+honsF3tnbm6CSX|;WFopxQ<7-X*-Z` z5zm(zyvQNhu#XczYpfLg%K%nU$ z3Q8YJ40)qM1O||3f{cQY0CNPb4#LW0ujTtL3J`_SZ-%DolHlOj~c)h|n z*+4sxWZ>qow#NMgt{$4p2(!Hr9WCu|hAX&r*9Ba+8oTes;9}#&hY~u#UhLj5uTY`% z5!%?jab1sCXc4j|*uCL}!dzt++#1n#H`PH+<}SuD;YbX?51=4{Vxlm_)3x31n8_~z zsE*ldM`Wgx+A99Ss?DtXdNUVnwxV*@XLa?C_MCusG1vam)LF-m5l=}ApkPbZKP>ja zL(?%8=Zj{1Sp>HA{Bp#S<84+MH20*T_W&NUvP>1x!`jR5-zy|7y>dBzTWd1$bB|2E z-mz>J2c|rrdHtryQho)}9KW?G%N8ZAn9P;umd!MC5Xs&$IMtKM7R#mg40PvouO+EW zFH}We8G>EqH<%#kQeGtxAYS<2p1ATwd(tA+r3ymA4*9?8Ak&V487zHq_0)w8xY*4t zocX$>{4(W+hGAp;+l;{4qXDxeIU-H3+7{qw(Y5TZD!QcKS7alj#yCxj#wuPQIFPWxV_iq8JA+cOZxK0;c)Nk3*eNR&n1(~*8X4Y=Wx$MG+yKUW%D$#VO1o*7$Ldk-(S`fYhH-m6F@p{xO>DMpsC{OAM$ zL57=Bv<=E;(A{U^$snm#1H62{2__Z1bDL~ zVM^i`_(dkdUD?~{*MhIR4@75&$or*m*1#xBc9N_0?`smpky7O?4v_G{=ch7wy@Yk3 zAx-2~BQKXJqnlJf9mUw0;5sZLZ;W&b*kwdXv(MREd^~Z9GMLBY{RyDRt#)2xn0ETy z@I<Pm;nBkQ}z z-#6ub&|p~ygu)kNNVv69MUwE4XmhK7yq4yQ0Q({((649S?W5b1-J6DW#|K4QIt;n7ZK zYiqV*_L@$^5Gv6pOm6{`=?s39b{G-1><7I(wri0J@YJhaReXHR#EmYD#FB}9K5EA; z4a*oK0XrIU1@Nq>>3((n2XOXJw0rFI)lbdPX zBgOW?Ppx8$1q`}VS;gO7Ecyugii6K|t8|n&Fdm~usg7K|7FkYnyJ|26!IS0Dr!Sz` z;eoB0derd9O!(v1G6suQ2uC;Q49YpIZn@g3bJ?h|M&)+E{xifdkD^po0P$va{WMf@ zH0+BU^4}%Th+nu?o{J_{WSZFgs>7(*CC(x3v7(JFEFTG5y3?Yt4Z3O^w)r*vbUFV+ z#gbFU0#WJIMMrG!J1<9~h%{&mAI0rOALYA;AF|#4NjGK&sT&9`d zjqwPic>n14B=Vzi8}|d1tj^>3VHAFB<|j*7^Zt(-SHhtA3RdDqcg@#d;f1HTg^HN1 z43G)c4f;+g&jnNtxkr%Qm?EwHEJsqL3e7D1Q7OSOM5XjIo#R9h}dJ2k#+ax7Bor*<;%kY zW47ax3XuE$Z&_Tkf>HRItYntO)}syfoMY6b>(Rq_9$b zxb{G^W3WYyFa&}7mgEPi>HGr-svw4CkLYG$)mVJ?0x|JfCcI&QPIT{&w@KLXetHIp zQi$jP$D4lkntgdbo70y$vtq!HUSvaHpBzPeT$fYeXtOtQds!LkZYeEh1mnJ%wvw~m zqGp?VBDJG1YYYEnV#t&sjVIWGEBPU8j9rd)Ng?|3kLBGF8tu#K8@+=(;_}9<0a5mK zvHI7l4j4(BvMQ!XTjIlj#Kjw5i{@7AE@{tkIqP$D=rS(#UwE3lVX2 zUXj8ffZC3iD0-JGkdX)t>PC%S28*+E$_%&)mkFT;sQJtNn_oMoVq5oPr@$? zYSprX(Y6CrJKY8zCGYUb5DnHWavPLXo^9W&2=?b7NX`u!sy}R%8sz-Y)rwJQPJrPx zz2z0cRy}eH(2%##2e51JdVj6LUMz9%U~RE1rF95w-wz;P2BJ)5&O-$_gJ7YTM}+n; zH_qrBTTmQ_HD=R$VZ_KxM$uXgCm$jEfjgV}9wdv!RO)RR29LysTbEA6VMC^Z>i^={ zTP4H$GIS$$e!7CfofBv%;c$#f)zgwcun*U8aAc8S9$%RQ6)rrr3^)$}VB;f=2rNF{ zva@@FkF+qhsn5tE1tVpIjjoi+qVVVS{+_tPFvEwIMUPT_qJBt%2f##NN}Yth=^K?F zoY>u7@k?LhbvEoAukG`F4$5g9D0pgbFauHT2cp5~FJkLj84Yd$S4NdFpcU;7=T-be zK(nYj4I;{lTL8n|b^n~e3f>BD#g&+3|R{)x^Xh^4#(6M0mkB-&M%SX3IhS77AIAqhK437FKf z3hs*08)Cp>n^Kw&#YmWfyf9NLM{=(1O`4FJ+q&NV0waJ>Ttv-X=3My2Tm|S*QShAQ zZ&77lH$#m z(PY&%E(=9pe(lGb+M9BC&Qd1VkVP1}16xT2c&Z|22w5;+lKwQQe9A#=6+Dj+<>a-Q z{!NrH%wc_C=4OtKRTzk?TR;;w;sX7mARRobnG7~7DF;G~s2G>O&@daL~S_(JEoVzX6-Bm_4MzTXFx3!3ZNxKk=+1YMcHicny?bwAo zwDRZ$U=W2rg9D56FakiIn~29usoG*Fz!+VbxBgC7f_Ic6F}92b48wC<$*U&>5^fn3 zf?{vLyoHQ>T4sEDTHh zSH{oQAqP!rA2Pun#_G?Bzb^Rj59Wumw@W9YQ|yWcf^{ftcNV9>Kzr-$ImfH~S<`N2 z8jo9k>4m%B&J8wgQrCdBfDAE&d!po3GSqb{L98@C{0B(T0#<;`1I2?$Hoxvq7fBhH zDXCG?!ClIfQVKW^oNL1j7`eT@$6Gv`f6VEE(~f{OncEfV+5j`=b_ON1h4$<5O7DB9 z_J{`QJq-V=azGX#@_nFy5kJ-$9yvfMZ3((JiXF_ni};`y$^RO_zbz{WX9QR4=-^*m|c ztI;-55nW#f9*;L0B=IKoU%b`kE{2-;OezhHz83`?lMca()&miT5ev%~p@E_+(n!;2 zp~yP`T~OvvLVO=J3GuuN`F@ZIs1! zYC`jbyYd7LU7-`uf>*$=V^&v366#3q3rqekiRxGwb)-(*4&LaQ6XFSrL|2^@#_ms! zM@QcV`0RH{IxFy|p@T8Y@|z(UJ!=uW-k{eOBUR;&peD5dNE}cR#JDgWX9XE65MFHx zT{sl|6@?#D|LH?|U~t zt<3h~hN=6`99$jgF>ztI_5Yz3*LOS8skOuDuqc=r6OJUb_fKkmcZn)YpktYyIol9M zM`9g`#(>h-mRD@t+~Q_1>Bz{x|5@+=D{37>c2TGAV!n4=or{pcG55T+Lk`0B=GyC@ z#g%@;sXFd=>3s>1kmbqKxq+oJLj1$f=-(Gn`s|$>Kt*ZN7P6tyKBlpyS*L2i8~W>T zn8QZ!M%<^8RYgVWW6x0s_h2-z#NY9N#J{qq#s?o%dj zVzXdbL#-W9#kPc!t9YToQu~VL?%qAMY$$PiH*U`4wr-V;7ikjlZK}J-kH(_XAI{c6A=-polLsN4qt(X(#?+Y&W@h3@429Hl+|+nm zsS`@n^}~uof805@YGAYVD8z?e_`~V3)LI+$@g4rR0+p1O7QTj=U5*fuLn%vPsW37m zWABx_b89X_ZYo{-8W!myg%u_gL-NT2bsCqfITxoR6f8Ncjg%!)ZVKH6Q*qpcjThTm zff@;64xs+_T#JJma=PI>j5uekra5Z}CmT4x;AE2v0Bm@JPx&aMBev-0hSN<0J^1TS z!{CB2n<65yUwS`Cmm@qUOQoSK#Gh*i*`rm4onmY5E`9~uWytI&hkDEv9$E&xpW7jR z<>@UF2{m+V*5Sy;kO8Bxo$&ITXEkt;sAs_^^==7FK%7lf0l>Pj^Ols2qh93$0-#ekxs{!BJlWK?IH zY0_%8CG(zHToQZ$G0CzL0(8d|c(aMvNY(z@6(Sp?ZbX&E8LAhr$pSj`i#MG}cQc5( zd!K;Sc?#$BCEv7G*o(sTK$R=3{L`O_4WmX>jeS+gKv@VzMXS$c5GbgXVlLC1LrsG- z>;LbovnNyg*%W4&eSjd)hdx9z>0Ij_cy80~?3|wGNaxvG81S^qj1vm!u(BLRhh4)c z)a{gg9M>|2PY{YR5MY*Jl=L7ZK`@0y^WL*$>JBx6$)CBzYdsmZ9>~^%GkMju)HZGE zc1pF)v)ImRTbo>MLt$-oR9ii_wnB%}?S7-;MsZ_B^UI7kN2*J59wLRItC zI4gbU#mglN;NJ)a_Pv$$<;4x-v?;CqwBhP#X|FJP>vEJQ1_e- z!HUzvVkpD-sxc$wLr{pqgYbjm`s@OJMG>EQ_UIO=>3j)7QXrjTQ`ENj?I(kn6QWU- zfC5=l8g*F$Dolok;H(1rXrDASqC6$2jJb8BvBT$UJ@EbJGr7Ut^uyKPGZ~C*2D5MX zPLBqd;Ho0;ZOdX24%QDzHz^>uArsy~Em#>|+FJOO6vqpJ{pq}f9+}*7xH2?Gw-YbU zK0@`0eze(j^A2M!iz~z$7EYmOT?)6V=o61@k#)4Qh^st7lwx9c|v%-u)lAbvG!o~(s@c-`1um5aFJ7kJ+O7vVLm&>xQ`aX8$%L-DS zp8ypOeloS86MXVM;3m46wdKLYJ^G&1=VVyFRKP+y95mUhm6O?)3xR9XDT2PbkcesK zQ@#4legaq&E53)En5G*8HWsfFU`VTe{k&2bRF-H8d>K5$M@`%qPOTf}8=eFG%rs&{ z8$@6${qm29Q`wt6oq{&$gI$9xZdY;Ix{^N7uFl{9V!ua(qIR^TA06 z)MN7DIzj(~h9rSzk4h;<;Y_n1dlfx)mP}R)^$@FBOgVonbsg=*a1z%QNh9NqNN^xI zF4rd3fpS;AKw?_~b}L8kP>{SkGD8Dbh(ur>EGpkXg`l@YSJ4D{T}8?SkF}j>*?+B! zgwEHLRukJ46))Hi@S4?2OaWz^VJ4Y2hWhvE$1*q*96KvR-I$yj)0kuNi*vz+lJ*Mo zQx3z3Mhp5L8yCKG;+N$r3pFW<=;dCK-P9J&m8?#%9)SoJL2cFL)%D|If1GWS1h4i> zE)MWs5Q#w|&hMY^8|0;u0Momc*;}z;R=ylmn$TU8=;1Cgzcv!1u>xCX_)v1|U;oWk z?}#8OlnNzN>iDuFyhuves8+am@*tU!QLh+2C?p-30%}$pElj~AsJSt9L*yLpldzuP zLhCtWSj-OdGF*S7HVIZSrVUn_oL$iT#bU`Vynwr zt@FU3AvL~8wCr0QGK|&EF9ccrFi($#DI+0{L?H$+?lHp6Hf)=7^ptl!C231946{@Q zN&k=)K>jCL^(HE`eoCpjuWJH$AlTWWhlnLWyhuqzf#6%bpne8`0C^O~3*@TC6vTk} zzU@KXB5_rp>sk?S8;E0#8CtT&K;u1BYasZDUx0z@o7#r&GsXAzw*lm~5)iy!aNbbV zwqLub1sY8^Eq(A5!(7J z%Bq>hHNj7=^arJpbgQxej}Rf;S(u7oO%jU4l_2M;z2P741Y~>kqL_%LyieBfSjZms z%j$8@1_krez1nU|WoYfv{#n>EN(~WYf_U?9cdw}tCa2v&$yLLZb{YZ*dPk*3Kx6ZJ zmV6A4Cnl5(&T~C#PlT`U4v962nNG-m;MOgK;BR(Sx0q}qpc`GEej0@3B! z?zloRs-R&sf5y0`u)&bObg z!T=GIA1GC@wV(}v4&s4Tjk0jlLgb{l*r48F3xvUOi2E!H_m3f^FR|vgrLal3{61DA zUlyxS$C6&(YPrKpl&@@aJK+Q|F&FWH4PnYCZhhZ^4qgXrnS2w=yE0QSKM}_gvzbla zWW!svWbs$&P_Cm^YZ!Dg$SJbnFxab;3QrtHW(YKb5XaYY&%ku{^V1$w4&YO*kg@xj zyzhG_gbJGe3Dm%xH#sVAgK;Qq5G#{hU9Pxy2#FA;>QSI~xZt8jiMQ%_f_C2sm9BND z0Aa}f-rz_4ndrP@TBgI0W9dVUcd-Ug7xBjNrMqTGKY^x-8b5~lm5F>^e=tU)Z2AJppO6=I_t>W?hb zJze6D1u*UA0kZK7ae*kkLmQ8rL*IP!#FD?|w%}V{*=QJDi3(oS(9hZ zHurY|i5@psq%+T&9t@+rV8Fs@R6t?D!k@_ah}Rs3kC~#W+ZGqPXO zK#&7rWn{ZNu@Tc^*$EVbn?!7G5@8D~o(3-d$~yu<@x@C^j(1M4MH_sXQ^eF402ksz ze=34>27Xy&Q3yJs!zvlIcR4bg?4_+#(ugINJVH4`9$>0h z*=S>#Jwi{R9aMW-S2YVW;ck`=p$j=;_}P8x+Et|EHpu2o&}Rq3-6o`Qh%!JSL>=U- z38J73qWmE0CgqFYmlT?D_L`@aR z*lq(UNHl=Bh?9TKezW8mm$p3|<0wjLlDIfpA1x|SoG@;K@|KTKga%yYl?2(#FnSLG zd+;c3Tse+A2*l9)Se^P3HA8a?RUi>TQ9LFVx-SFgZmFDXbR71_X!wj z088@n{zdS<$R}jtDJ{5eS2JTKL`spL{)t%@_7dc%?K)kqXvtF151Bk0w=8bR>M?Gg zL21C&O~)Yp%r!qd$j?M2qP~AjX?9xz{=G|gYpp3ZE_pZWx`zMrfJus*4Lsm*FdMOT za0wu;xRay-%1F?n#=KdTErNm;t|0Jkz>2t>6GJpfrI~BtG1$f(RirY;fv%N;MiaeZ z0V^;Gm-0p;q3@Z%2x?(@c~ap(OzY@PMxb+JIlKfq#LACvLF(#YM2EcPOd;$Hz#K*j zg!vH`H0=qB0a|o*G$9|%kfm4HTh=-Yn(X2IQMdG$zQPz?csU4=S}DPdwc0b-ihyXV zi>fd`~uf9JsQ?xtJxTXuKx z!MkY*k~2XTMS2HK41hPjF%lAU6l!DnVh#m$AY0t@ZdGzp=0>-0rxM#1;`zvIhBf)S zH5$v(???h~717?S6_;`844da3Rm0+bb9;nt@6eaKG6c*AOBmLY;5~2r0SsipG0)Z{KGDF7UR`+0tV{X1MF?n zLH<9GNf50FM4N*#0UQnw1KEl3)ho`XH4nC_pM%fKc#1V5#(xW&U*`!mtp{TM00>YG zcU9i^TH3dr0uy95Hz3?dr%T7z3U*4>S7UJ^V^wzq!B~}kgSL1c0 z-=XI9?=Wf!;&cxS9UcR;_O}Kj%q_|oOdx~=0C~iG=PDm7AVZI@Ak$y-q}nPm$J79xk@?aUVDdbV>% zxZg|#8U|Atv0fHFx?k2EwcYF;dW041k@77t4vFK};E*COp-#8wu7@0UCAOvNvAi;( z6cA$VW!2Z0eijq0)vD7AZc)CWD%(t#I&6N?}yDatWa6TVO7(%3w#G6)6#BU`lxj z0zpwe5>Dix5Sdg~SaQP67 zy;g8j%n3yBE8BWVoZ&ix0y`wzDfePinJL1x-vm?w{M7|U_Mjs9UPL$05k3p3`B)

B6DVNaJU000Ky%GOhMfl7N9QrnhpH*^u;%1n#G*2;HQ@FmY=wDT1r z7f)+OBG<}&$S* zYqHCRuW=SzO3lXcT%{!^D1NE#biL5-13O$48m>Snj>f# zx=%KCyh7xItkM2iE%mJEpvymM#TNXu`#b!YSN`ZjcQW)l{m2B}YpkF8vfc@eLrL;w zl2q!@0(NB$vW8xBCB(S?gpG>vchJQRCZj6RQc=|o=yk9TWvxr2B5){~mbfhbmg6ar zF|~Ec_dbJqQ}M*j2_Mq(M&*Et5&^dFV zu@njwmMC)bs`8D*jSB0#K-rS9Xy7Zs`Egs|#Vtv^zF;&Zeaf(e4PD&5!ejL#pz~VB zv@YE3jgpsfc=P`M7&+Anl1o7bpZfuTQX42Gi$AlI>i#oBCLnZR$It9@pp3SKP=k7; z3Az*lE$S#)4!C4ug!m5#b+6iO0MAl4vZR}(a%0ox6q8VTv)V5-=PzEac|X`hYMMlY zh@OC)ak`G81ne7g?~iGQm=dy3p)8X|v-&Al1~$MIsZ{c}!6oKP0)e&rVZs)oKzuQC z+%&Fqm%wCxk}jJih7YXO;cz)lHcu2lw#?nO72uvJd?5%F7ZVs__H^}tN(jJnHCU?+ zPB(%Mem--7WDI3#_DMVjNC=mK&5YX0F4~(CLU`9}z8HD8LW93{r8}k$Gp<0f&Bdmy zuK4$2NSVw3*h}~h(PPfKAT9!|x2j*eNyIqRz56yEGSCjPFJV>^JpoW%`(S7|5GpxD zPmZCo_6rzy!WQpBPY#U}r@dM&2QH2x;`b<22dQ=d$a2Z4f2|(WiSO*c?tcjRh#wt? zXEmY}$e*=zTg}@ogFy#^S|h)uzmNu;_$o1cJ3y}%PI3mD-4V2av76#UMf9kQ_Zo^Z z1S$#j^HZKjvwdhns4#VOcqpRG#?u%iF=@pktc*!)Si8-M7&KSaPJ^5@3nKJ)S(@Bt z382CF6&b;gtytifMN@0WoHDpY|4^b#SGfNgP2DAd)3v9^v~-q~g7xAsx(B>90&v-NW~>zF=)@Q0kM-$y;`=Do~TBL%H2TCsXWpZ1)>Fk8X3p~ zY;Yjlv_??Jjco-OE~j(KLeL1gWzo=3@mskw_>HQSqoY%V2HBL5mT;R;|GItTJp&w5 z5QQ=VZA}Rljt6&i)XmjNSkb=-IOa2*(=;~wJ`o@lf6laYBHV%AllDx`pS0R!+3G7@ zT9TrK_6gUKl}aPz+917JZ1c?_ZQ&V%eqM^^RWXMejCaHiAj=#>5Up*zYOc{nI#r-5 zdFtueJ+FEl+d-_tq1Kk37$OeoVjjBuZ9NiDH4hsZG{9A4V{prXQcV6zvNoDvj`Ss2@zkhV zPPY{wnszX5lmo}Y& z5Il4X8+oA%{_gM~U%3n$A!d#dBQvUse%nxdV^dYe`$VU95@-M@+#oe6h&fVWp76D%-0(m7fT*mIqXtJr z{t4=h>H}Fb#O6L@#=ee+!@?XK17k!1Cfw;e;%D9onxv8R^xIpo)~9!4LJetP01`%#%(1-#SA(4*xQ zSwU|?BN>bZ)HB_EVXi!+?GU!@*yzJo1rv3P%zqixC?(VweS-{eWY3ZBlYJ)pr`u)F zsI$1Z!?Kb_NL^&H5nF-2?Y=WJ>NQ+>4j_z*CyA@77!<9$K)ceGaxlTwop2bN0Kwm| zu1?7A_xX*;(xd3^#EBr6oU0+$;|AP#v>w7`KL}G7D+gFfb#zzT!p;zPAaSmi&#+(c zNQl?sjyaP6ziP``Ks4-f7-srZ2{+#;I{0?ck$NG28R*?X`b8odOG5|)ZKe1@M1P@h zkyBc$g#Us_PX=#dyALVw1jIUq&bkbN13Cv`Y)S`NltS1XjbeVJ5OIfom z2fVODcPErIzt`83ldjl_KUpNW@G`Z1aX~cvf(}APdU{C3?(Hft2p@#U6@vtD8dc-6 z4@U!v{jwcLfXd9>5KeY4Z&F$6l$M?*Q4AVX4Mh_imJVp@na>Ec%>`Z+#i|ciFj`qj zI<*Ew>D$4_FVI4>!$II3bBGQ6*N!Fgr-d zLJLG{G=_dFPbLs)?`Po(dr_uB9H6k0qE7umn*$cLPN}&e5ZRfi#Ae4H>1Ts&u9eo; z!8Xh`^8Fr~metEgHnxhzeDp*o%kYftH?+YNPB0>U%rF$$g(OsAFWl)zhW&mG_@ble zlnx>{*k}XtWi-v|Or!(yk&XVIU7_2t{FO4y*pfhz#nc$W} ze2B^`WSH9z+<=+}!YwGaYJRq#?MM?Sl59fTlC5Y{ayKH&TNBHN5vXaJ=E5RaA(o5; ztHc6H$s>dMW0~Y=-t8wwSQKN0L=rMt(jND4knC$lU`^|zuGX* zk>$c4ZJ)^E!=M~lhe@T5qG{w;-f{%@Q@zrjBor}B?ej=0mr$BUUMu2|`eP)k@{1j9 zPFRS{cv!iOE0Pc%9zN;HD3`=RiIhX)kmNa}rit$uEjgsvnp_fR#5LiNEdO#v_HsvD zl%~0y6Ap<>IvV19&dK#tI{hY^ZUf!^N#Ty99fERBR8}O=$7;H7en?OlFstlqC(y?51U9+284+OW0DqEiT&+ z_CyPF)z`7TDRI(r?q2j_u&A7)D%+9k!v^8L(*3_pGjDo(NO1SAOWG}u@sqoxp=pxJ|#VYx@ivB(G5 z=HR_UEcX;I2FownmitXh+x4bANVMFUd&)WP+mX$Q?T8ff+I~0&(sFo?my{(2i}oW* zXRb<{eWPCXl4i5+r7e4jUTL$sRoP!wPc~K_Eca%xpx!I)(L2P_Xo>Rd1!;RJpu?k| zQLll3fQwyQ?b|cjJ;Zwm%FW8Wt^4X-0cfK~W91=fv>fViu!}TjMn>VrmgSb7I#=)M zr5dD9uUChq7pkcZGv zg%3^-*THEa%o`vfXzuv9>`-A~ zYO2QzO!$E?I>Cq8q4Kf6zSjG?n$iDXZ_E2SEZ4Cs*RiJS*m)hRavhs;9qV0(yN)eg z$8OiL+I4Jp9m{-L&xF0s^M^@4|E0p9n+@HFB8xu+{c0!_imgr4pzVyV@E|8Qq$b+X9am+5$p*DrD_2gz#t*_`O_=f)r&x+bv?#(-Yuj(4JOHdU{bAQC5fJ-Lb1b^0M6jrRt{W4lMn3RaHpo}W7-oOOO19b znDSS=Lm-{0(h5o{m{;p$Wg4wY1+yV`Ez>9iuxB-gov;HVBpj@Av<%a-8wMP-0u#cp zgTpX)rrW@i>nYVjz-v$@+dEfU*B_kP?>A?M;M&>1)Iczs=fc$t?3d4sU(skD*c<4A z9uOS!s~em{GlmTa;qY~ERPL!%AUIML$E>riZtwt-rmz!{fX_Y@gC|ydhtzd1eWNoP z3GLkv$ibwB!nQ{_?e+~UkL}t;Zz{)fsq4C~q8~YTNRi!sM9ba4vPYwBV6~UD56tTu zkL?YG<*~hY`jgv<^e3UPTSH+r56s(1{CPQRPR@)hc_b?v^9f)a$+t1{gO!_{@OjBn zXMK=paYUBny28Px=84+QoI4_`NR-d0j{f8^`hfoAWh0>r4gpDz0sL8oWL3<|N4D#d z4|hJ7xnUkT^zon(A+eQ8rBYlF^4a;Cu>Ed@o$d8w+L0KAB=mVE11Cs7FS_ajkN9Lh zL9vx1ve@Q@ZTix|OyVRfB*(=`*k$Nb=&OZd!(2GWMZmZ$?%r zpksYVRSG8Co2D!Ilrs)TSCTsp?kOEtS6*GoYQsC{gZU#8KEY=9X`~aF-YPZrTaBp- znKk8c70J}0M1PzVXJS4bTVJqDbChwiHq4v)+1eWe$1qp^SO_)HdR|wqxg~%=>$@iN6J^6liiL8 z@GMfKpKt*TO>DHx1uzs@uAv~Z&_<*9#KKtAaaaevBUU`+bnfb)7o+VW-GWJxbvqV0 z$hV~I1XA|2BDOk7v*4j=rRnfO8;9q$6aQIejsq5>r|lWxa_n4W7-Py4FFWjT_Hs1N z3paUq5A@Gsr8H*)5T}jq#E6HOBnGt*@UT%Pp+=z^WwaCHP!m2VK$%)74q;201~Y<% zE~4rL^N%o-siIiaG~7i-=8ZtnsK3XeCPp66k4jl1mccQd1IK~sZWE0#H1@O*@G29` zzXX#-Tb#WlTKvChuA?v%;92^mjtWwBgI<&LLm;!5iOc^`3uBhQNCJ>%ey07PCM-#U0ny- zJVq`(@GX5*SL{GRiIEB^h)2l69`of~kNNY&766Apc)#4~NdV*gH+|DDH0MviKLi7` zu2_pK2(R*0A8J;MU>K%iqy!`gW0Y<&5zi87q+3KvU3h{M3y)z%u<$u-c8uPy$ac%w zJr+e-=Olu}rM8o!76M*1f_dAhFD*>*VssvJ%@gFJVSy^DsG`X9n|@~5Gu0Wr)w zmfH2#(o$1?6H_RQd-T58+dS|x0wV2gBiZkP@<53|_!P?GIZ&V5wMWVVfo*~SVPsJV zK_&ebWCEL57h1REa?sjY;}9 z0s-|0S!g`b=)@>C;qj78E7da{RYNe1Fbu9zARV^|Z2MCHDiZRA62(wT7Nq?}7`h!NFZKK#~9v&keBcY~|&_Ye?>YX#I zHJPrheEE$eX5fzj|T8qgraa)DX-S>MPTG>|h(0!}|!*G^#@w8#-r$jMkAh%EiPE+ zLffKTTyk`9H~{m;dHGGZ5!-Z5`shg`)jMZPG2d3%r~FI662lzlj|5 zjCjHVFOT&J3=H{);IL=orRFrw&-QeOPYML8v=I45}5jOtJ%*<8NmAuYM^?`X$c7{B5c??Hn z@vj@&3a(HFq?pm0y0ce5H4VqqcV}!&N!TOAM9RhuhbAiyj+;hrXy0ni0D3e4grilC zESMN|u9iZ;!JKi!HZ2xj2HR|@DW(*P=r8lY%}iNXdWvnJj1~gEX%TFge0UCP!{jV{ z{KaHm9eX{dV^ejJ=Gu9hS?Ufkd5Xdq!vIR&;o%LG80$|7GlA|vyctG zX`!t(D!6#cmeVrKODVnUJT1dgm`mRIuZb-##(G`$r>S6G=a_1o$5v4cTz3AVk?7Mx z^fM~V8+#ZD+ZMVt95jMPY{TN{$##SLR&$VC+_f2HQJ6ROK%*wuel!w&B2Q0EYuBol z7_ikGJ}lFj6DC$?vL;M;%903d)8|dox_IxVcic6)&3E6lSfAitdI}5i5@t>0Qm7VL z@+LZIjg%y45m>&~LdZAuY)S$ehr3!ZV~e|aw3#xp^17}o%dYFfF)I$0%B9pCuXa__ z&TTc3C0E^oDG1^82T*VXM+Zma!HhC3(E+FmD%KSY2H}6#}tL15MdRmlH@fJ9ndLqK!@14U0(9M>Y}Fz^(WM zj!4pOWH^BrLLkKk>{bOhz%8o4Zdi>zEVIEV?6!505)T|f^DKDdSiiQHVxS97v5;IbF#D1G)x0pt#W^j1L#D4JA?vk zMI;E3Y==;=O{Z)ZS|ODLyB!{3o0Q%DggFG*1G_z<0dC*4=mr|@F9SxT*e+t3?W!l{ zwvk|BzY)pCWhpFn4#Ji8?pi3X}AgU1MVUxG5dtWY>X%X=kn@d-fW(iO-&0Mr@Uy-9~I} zpGo#IlFhCRtCx#xHe;(MwmuVEU0q!)bTMCi2}PUTU;@W0y?gcUaB?AE?tDO&(y%|O6uNM zmZjh5Itd8YtK%{^Y zWVK&Bvd5}pPa|Ht*W~uzYSpwVQzXk^%pX?o#n=o!3F1s_eLbne#MBgFqlvJ6NhzQJ zogfR2ENrHN1xB{BJ3AJ7Gvp2vHeL)^`B<4hc!u>xS$Ukr0VW&C7I51chEu&@LMu~4 zkaYKIYK6no!SU+EiLtI`#P_@2%f{>2>pJ#=9t~(E%Ut#Lxj*O`A+O4#10}yc{Vwyi&!HqIo$zoS+OuIML4N0j|3*rl7nQ=pDhoR4~7qOp{TgX|TuN zXqsy)c`1+2(cS1T-bare9IcTf&Qzym{o6$Jchf?|b0(w4j;4{14`L&>CJo}F$BvbD z`5JVkRU=}wGq&PIqfN!ART-rf%&gAD^jA{B{1`T0>#yI_!A=S+*(RgVGMh3kW*nEj z;fIS^k48%}$bntcVq^cBW>Q6s5SbFap)lt5iofu22Csv#bC*2c_;=>Sh%A>2%E;!x~}_d#{$1X zC++&v-XEA+f7-na3^F21`%SB0FtMv#zP}yr+|Qc*T^2j?bwlzZuFR)ucfz4<3*UE;+Sc5smJbu7FNOGa(g zoh{tyPfMw*?%He`udX`nY`F?~`qQpuaF{mw)4tUj{b{P7$q-2EEDC*wnNg`?UTM4L z#mr32?H4tC(+mSE4;F$#3!>OQi>84PHjNVOkk|HO0Ve;A~qlqJyVS5{fj3&Oo zKVZ+I)uP4D1FIT$Dppgm5rgproCZ1mg_)UI{(@T5T)uofUmn89gMT)aitwiRYKKoY z&BTyZBbY6<2s;cyv?N3wR}$o!ky&0TyHjK#nZ+$@tQrQJ2=@!a!t$M`PC1Ml@CSa5 z8Sa{awwvLr^c2cH(h_&}jL@=~cq8+Ec`>9B{R0ZY27!n6Qex)D_D=Pk@oK%z(HZS7 zQDkyeHchF1Jvk<$LEO8 zLDKc|aKvX7zM1GM0)_1C>@Z=OA%v%<(d-p3X^&J8X!}OsVyrmB+*oqLjNGr6v|pur z-Z4yv#gwqv&0#TSq9B>oN*K-?9L+{;0d2nZ2`}1t{$6oiF zjy*5ev^i6$zr-GUeMJuMu3u*+fce#|4(D#&tbWa3?;y6JzurC9cld4l zGs92<{&4;0cvQ^UyCL%!j-fd-OK#AE6La051wA-icQExBngcZu#SeIJme7uHkf6?? z=!#wdM1-ssTMmFTv2~+a-oIeIyQo(ygZ175vM#T|dS`JQtoN_pTO02_)T`aZ)(a&d z3>N?3{ko1N)_V_^cOL4U-eIZ>>wSkN_VP~krhe6WXRLQhKbG;nqA~q>PtgXjjCYQc z9an8MP2V6HWX}lIu~QzVLwgf3nsOE$dwLrP_PP$#g$2D`275gR3Pjo$s#-x;G^YGK zTNcWeYasN(^D?J8aiBcsl&MykFt0kWnrmE=y0#{(BZ1eg9eG5 z4mrF_nfG$2H%RpAT~*m%s@~<;%L~0rSQeg#aV(B?EVz5eURd0v^J1XR!osVvtJej) z3kw!3sAHEbcR9yGp-U#kWMP3IK%HYC-I+T^QC~r?5WC{hi-9`F-6A4$4jd=uI-Jvi z8evjZud03%S)8nQ64~6(6P`%!n^4ly+W*s{(x234s}&;`3icMnBi4>Q?9B( zqV1%;L847PB~`DE?Vv@WeiKo2-i1orLT?W%#HJkb^sua)TUnX>Lx>I1cAn>3?7Rx? z?j81AXUR6)P4h*jFy}nSO}eWWa@4zt(+Id@IVmkH(ljZ*4tttLvW@8}DJ*l9upV0# zX*rMkyNMXw;cAe~YS$o7f7dm-%C56*pBYTp1B>)`J#%96L?IuVO2J7^LK7`Wf|Iia z9Si}dRFUwTh+MH9=Az$YtURvzY4tHyRvz-YF6gO_%t(1$XZ{7R>&VD!a6q zrqQoIm`7pCPoxppo02)>SP(zV|x@pH0T zZX+U(h%A`YLLiYJ!oBpkU9<64SWSX^=?vJe{RUS{l_N{i8(W1$l_RU?V+)a* zFkup$PbL!9G6#&bmB5K6Fp^LW+ju7$mh*KyfQwkd#BvZYwC($YJsZLkTQ8c`*R(lPssDQ+)WlDJ-P4;Xy-PEisoptL zD)ru`)cbvwX*%l*p1G=1gV_QdHojdfsh z(TkDdo!Q3QGJ2vb6!vfzB+{P>=2d;v9{piHv-HbH@wzzfrZ23KT$p9|aofF9sX4c; z-tJiK!JX@D(rz@}@Ae-3#rW7xV@H3@#jHnv8Xx7@VY`O|Lb4m& zxjtzM?#O^JcBH=Ux6ezgR<90s?kZI4kK<&xu6IBfkbmH~$UVC(S8oqqVzbA0e3V_X zS1N7q=!GXb`@7<5mBh+T$8kE#?`OZ*I@QWCek|JD-krO0VE5`yN1^3t65E59Sow|S zh^>{kR#z(AF~J=pHJI^i&msl$rg}%E=NhmMOwO~MV*zZloqID8Jkm5ewqI-kh;6p_ zjI2Hr3?l~q&}<{mI4LKZFmj;=VrgFGGb`_GyWi_Lu4E(SF^3Jd*=ADDwQ~HiVPgBuslTumax2Nplm#}cSly#t^Ul8(G@m__(U9t zh;VYax&&$nIyl&h3``w4Vv7ip5)mZY;b*p*7`w$tU)YPX5gUZ;_KTL@9vAJnE+4T4 zcL21rv%>^WvBby1KcI&Y$nX&U>I*Z|H1O~+O#`Dd$>f52esnl1VXJ zep{#ob<>S7%P(rW3%xe1%cB>=bdIA+)S{P4{jnAE)Hzu%t*h!TD$jRZE-9CC@%YoZ z=Tzz11$#MK)EqHPWI8k*Wpd%0X8BED@8KxaFO|xMl`5T=T4`~UO6S#-X{u-)=G9ZZ zPu2Te;8a*6!)a6C5j=dz;+)?zRSXL}o{dx-;a~Bi{ z6~z(r7H6SJwD?7F08#larqo$?p?8T@5-lEc94%rh4i}Xp>%ymMj~RQ8lvEqOMm9f-drZyesxfJ zN34#jsvNqzRbO$AXUk;^7coG{Q2t!BkhJB|i_vnfg~EBYu)I<4i{;I74t8-TqENpn z7E}(juc9orrnf74d#otAygOc{hS?WlBKE`|CH6+29VufMUHMD(dtH*KzoMo=J!;xc zWTDP-4n@1Dne-Q%#Y!&*3sG#%q6@+%3WCieVUW%ZtjnPlxC++gH?4v);a$$n08}I{ zb!7y!D3%{ZStt?x^|+{)zhH)ZAJU(f(hjx3iH(NE{D_!I3S zADGv9`ins9hymh3=!mR%APHcc9|>R_nJ0j8Vje`ijL3=yLI+3g0P)L5K%8-8ez^z$ zXPlU;YN}S3+Cb14n)0TRr(Y~578WTftnQEU@X*ChoT~)X5vG{%FW zR$&tnpoa$vq9UFjCz$lmL_bZM6(5z%jSA}nkG-&{Nq^Buc8!C%!!{Y)lx)1s6Nz>@ z@T7>5pJ@5f2^(XK!p2{+gKQ5BIs!$J*hXyJInvYZV`JRuxMQOwD$3!eRcV+P7TG9w zd}tkUz>4rhiY?}>ZR#nWYlnyo^LjF0?CGfUKnqVmffhD!lm;B^D2};%2z=u}!JmH9 z{Trw^s@_25piVT^pb~=uhpXv}KLtNhPIs|o>GY+5LavZ3;e9OW_(MMLs zZEKiUCXp<5+0#N`r%CSo$dGvY8>h)o7_EJj8M}=OU7%Ey_ z(eSSQ2=Cf;?5|D7W|Zw#`bAkhnnH>2V$53eg?S|pdofHrO`~O66HRkvtE|Q*(deSF zi2H*wNCH{x@d*3WQu9biND1u1T=}mRT7(a-jSRE&D~VF_vEDAb3jemdi<*qlu6FY69FcpR_~S6x?ENx=7SbPcr( zLRg0kIc4Y5>8iTwjzYbUdUy3ke{}<%-V}96IkK{@IXO9G2x%T3PU0ax+QCKbo5)kxGIHq;+> z6&jZ?4)@2_DmxuV>Q7Z1C)7{%I9H~kt1LUJu6U~EpuXZcs?Wq0uWX#20LD3e#ie!C*_z~@oXfMo*as5Og z4vMbkPvCWj}Ik%Jodgru>LD<($mU!HWj0;yr0=bwxvq!DPEy zWvAaj{Zw<1aL+j%RkL#y^;DgStgOZ+`opdhk67&SC$g|>r4wD1>MDuT>8O`VsI0W9 zl(ww|Wv2-CO{JujD)*bn(d#G1NufNhYv;P#vS*t19hQ4W=>fRE(l6~@%4$UJ^`liD zi@DG?!um}#&26C*Wt?O1*kh6vR}xtx{bA?1pfF_*c0FO<{;}BmC$`!M%)-uuBT}%~ zv0$;AvP<$m?jQvF@o`|I(g?Db2t)SD)|vii!Rzbke9+-j?7nrqu!fWDdQL|ngd@e+G& zT6U-K#L*WZSAx;9M4HlakZ&Fh?6SX_l)BZ+LSLpG0ax00)nwYg4Et&_t$r6)8L7ho zOvZHwf6hNY@W)CQK!7_&Yy{l-`C*y9Y@y~@t0pZ`q=I9ZdmZt>V-P#6cjc$4L|+oa zpp+QJo&%)Ub+s2`2=fGu*m~iyn@*)#nr1Mur;f#%s#lUuNu^S$RII5fjN!*J$NnTY zj8UdA%Lqj=uOZ3f;^N}s1QLj0if!ArGz=jNVSp_ojGRhEEyiPi!OSIX7HGX2X)f2~qQF{QoNlGIhdkf%q+Sa0g8dMQa!dI_a|FQHVuhU)cX$u$H+ z6XP4kN*=kSX;SY?B96<>@hU4m&+(=kc|0RZZ;?DK(=4ti^YUR)uQ#mf)wC5U2qA#r~58G=N7WTUVRoPrpT z?1fCYom}{YB~PCq1h#tvHuAv1#;s}>CZ*)Dy(7izu6J;y9eq@9iYKyqNGPT8T&=PZ zvsUNKp%R%RAY$wN7h_E;m>2R~L^^cghYUdIVflhn31RX~Y{__WNio?%FGdX^OtulT zD;iO9vKz|bLWl_yK2YeA^E3|-Atx!6Hfp5$OXav^V3#v7UiGq(l@Vo)=`V+rI${g< z>>RPZgImU$BAx>p$$&j2B0wRg5y<$aLE;V_4+z2Rx+sVbqLj0Iiab*Z1Vw+)gsotr zo;sJJD}Kv$a8Pu`F28ZQ%lR%f2MO03E^nzhEbfxy@{ zc{?$4sH^yvyPQQ`CDC%1mb-kl`su5m&MSVRs;?-8EwpKn3kiQkUG-6SxvH*`K;7jl zm(`8bPfz`n#N||Fq~KB1hAId%o_oyLBmD(o#=4?W?-E5e2<}9d4AWPJOtpy1MV7fw zdOsg{#KLn=p|=Oi*itrE!H5YDlo*9%?3y92X&P>l>hA8=v4gq#fU9V7wQ+@ceaP!- zs=+m-)}S_I$P^RyAhCvw8aA4_0+BXIv|3GnkQu^v5T@xqE+^^WpDTnx#|`$<{EEkw zhP>%8zVv_{JD41~!YKNiVYF`DHWOk`3)#D)|ETkkzwA-yPz8~N?*Vs;z}Ou0f>{Uii>Bcdu`~}ukiXy$>>-&XAr3NTn^t~| zeApf)BRiW&3f$@)X0Z)Rr+{cqVH*p!-x}bS>J0+hEVfw?TdG={h)L7%Q>fZpiSoRO zsbo=kijN>da%_oJeh9JieA6mFGCLnxvg{dUTDGQ3DlPQpOO)*r6i5g=IHhyxEQcNq z8n#sL5o~=-fCitX_F(yZiBcdT>{#}Ym}jXIC|QOqEJXCzHKrJt{u=gBXaxH7*K@L@ z%r{I^3dfT(mGiSE!b6pSu}tSnv(?lnZtk_XG zQ`K3RTPYTndig0=(G-f$pTCJJG-{Lk=E-0?zj+>e@kFKCOuA^UVcsU{CruPvIhK$H zW^o@a;SJ1U+eOHvD7H}V9vP9vMQ}CIc&Q?z9i=lqX^_iC?JU_(G`q&Z9wKaCR>S1} zSnAbU>Wxt;(ca;@Qi`SS1Y`Ls=a@E62PPB6mXuL?zXr>DE0Z zX<2HGxI%9qwNV#+r-`&mg_F7VfMF4IhA!|^YBvEGTVqH@(|G(QT6J{Q)h5EzJ5Xj9 z)n?LPFCh)~b`LYpQ-3NbW+ZCq6*jmjLPm_y^!TZyRPFLDlp|}~Rw$&}3_N!3F0_T7 zSXN5MxY-4Xc1DX0s&Io&dNSB{wa1JtU3xTHY)}Oo*pg=1E7fgMjw&>TUZdFiJ(%b- zO70~#9j>Jph={m3dD^FYVnd+In8Bj{x_e>^K9!mfs7sIt8dYfWnU|A3=(97EVt0Hh z2Q&M1WoKrt5{R@wSqd_E8A3HT@kP|g@cpqh~)r!LOCv|a+Kq^AbWiU#@nI9)eGZL*Nn$q5Qq!mHQ zid2fa2#USVvG4#*-L``&#JWwI$~LoX(zYf|MH*SrN-Y5zd3M0AdSMfL#U?>avRQ+e z*z26uGX9E?sK2(FPm+}8qxobtiM@_-Kd4rcC22+=j8YN&;rf+I%`C}YMkw4Ur(~#9 z1YvMS;2*AEoKcEH0?S;bB75D%Gor1jazas-qG*ON+7xSgg!;*dBb z4#{4J>0(c8aY8Wy#Z+oeElB9P&Y5WBjDbKKM6*DGO#4Hh6N)X4dxE|CLiT#eJK`2Q zmHkRlDAU!YG<`U2sW%*JU&Tw}Mz;FB@z8nei<<2=ovhaLjQL6PAdMSf;|~ z$8z1JVqjQoLK+%4jfklvwdBmC)YC*mda(&~EaG&2NOl_G1yY zNeKhDdWQhNeTD)2b{hon8<`+NB?;W>BQSpZ2{yl7g&}@hikjb6(lFpw6SaQ31~k8| z10jAJ2rqv7hiyDKaI2F7@Y@>*;P)g<+8Sg=erazc7jW zejyVUOoEF(>;bppP-I1zUkC(i(8Di;fe*h>1-1`Kz^#ZBS&?nD_hAp)Y&-nIB5(ur zm*Mcf1h^%AXm|?Sj0JmH3bsFkTT-Y8+hFg@V4Li#3&6Gzwq;sn+hm&uZb=xn;nsE( zSyIT>#FnOjdbI@9Bx-Eb`(ky()+U5Ry&3}Qod>8i<7DT-hh)z0U<7eu9iP_E%^=bg9cgp-^ zJ2pP%2P?vWA}d~NQSW5cn<}#L-;j2-IeDlz1!Uu4>V1hAj}(fn%V>vsUs7|%-$&D- z-kF0GGP3b3q>#OW;&v2U*Q(x^)QWmjGswoLq^3=mdS^?$seAR<_*1Yx5$8Q8X?ZMo8sB?tupMTx>? zJJ|~|pR6b2Y1pA^CY zQ58u@M=~rHbOoXzAsCi{+Y!seUp=w4g@IY|vBZ-D8;`WX#&`xBuY$tt_wBS33(GK@ zkTxSUL)0xTn-GLC!#}{shy)9d06hcf*z1>6^@It3 zw<9UxOG!W-MzU|0+vT}l0)5UB%5WO zB#Ug8WsgTZswGnISuk{8udP7Bo$#J!$^=KMRYuMK`_}D zV5b^_*Gyf1+iyIIT5QRq$jYu=$s@g)_PX}yFFRi*7X4{i_jnYaL?pH;J$pO@+i)_d zRymqd{lV;7>4%Sw9unHyqoGyVpLP^CB$|edRa0b^3K1Y@_{R2w8>Mv+(mQRQAWc zNv(D!dfUzl1l~@DRjEB{tVbt3^*3v%sF;_Dt}2@Fd?4~VWXPqO=DU7xBbMIP+oQ&E zJymg)xI@b4I7+XEj)lC%2r0DOuiLA;8!{0VFC#G^y;6zAF1bq1h_*a248t%iHpC=` z{hBssDs{iiGoo!)CCf!rkuu9%ZwkE_KUA#8GVoW^>sP}bOpf%T zJR%Yu)?Ad$MARS**r}$N+%-+xw3RUxb4C+>v6W^0 zeXO#q;=;rZuiC=AeofP6jj^>~mm4>8W}6=niTee@Ufu!XOEcnM`eJ6-#+wmKC0Hs| z)>tLQ#C|c+H6&GJ)9{FwoyRuU8T*CRt8#y~y#sXXfMXc0V@oQLFyW=_6{sM{G8HNS zvTP*jEo2$k9Gi|zrozXGWh#7{Sgs@Nn8Mt;r4NkzTXiR@G{nhV9H+YF8 zt5QOHz>cCn2t!~a+<=V?2OA%RZJ-)#RKmPXggt8A^inB@7aBq4eB6eO%GKIL)dyoH znEtq5wG$u)CCy@+05L4-FV?PzMe@iUNkkrzPDm&EgE3aQ!!j^{1ktSN_w=Sh218c6 z7Cjn6?CErF+N=V>fmkfTg4#>oThqp&EO!;lanB;tTreaQ_yaswugOGv z$n@Jnre6`VOp!?}1Ecf@p{&GNC6P+ym6s%x?9kpT31We5GPdCkyRPdxm8n8w5sgh| zZ8>)nt$=2K#5n9&jHAbLjIwQsw4*>SyOIRILgiQMA?_OFq17I`vTIFS33hncLOhtK z-xg}2Q|+PttO^Me5&^=HKNfq$updmmz{GM~5t9lQ09mZ#riomSrs0lZ7>4!ekGm=f zGoGDT?r{gVghUqn4iX1hrDR6#n}-DVSc5bNxyY%mxo{H7B~^E@kN!B`g)*~BDkMy) zLc(mIl*3<1uAplO%)w6KF&~6SJeDGXuoMY|eM%uJmB=BPWL6mj#Fe^8<5TEOId_lp zNO?S$oqGyBK=ybiUvOgMv-k=K$sLvfiddC9u_brpj@*$uJ5ibiPedy+jij0&a>BwG zB-tx>h-44Z&Q1|#(i13D)n@9E3K;}RAs`<3pr&CeVt`~JmgI#RG;umv)e){A3xBVl zBDP7=G%QbDM9qRX!o1f{Y=X37#B+r7rF1-J@L*$<{@}fWEPT9TyoxR+7RCrKAeMm_ znpY-Scplh;$Q>a`M3fy~^03Wyco_mNQtuu8p<=yyq2?VRp6h=7^cT;aFDEJhEmp51 zMVPm<@Pd&0BT;`qu9yTUUO=fOmlD`X&4S<2cxF$^$HL!pDqRmjjC+$N$`R5Z>>ej< z(-vmOI1c*{6^miNj?u7On5OB=Ds0SQX-eTb9|!|F!ErPPb`}0) zPoXmU2O9)

?w96>O3s4tXU>ASt&B&m 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

yfK!4 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

?N{;n_nyp031V2{2DRhzD#m(-@RQ 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ĩ(0R$N%Lo+mGXyO8Z(LjGlY?wYh#+IjD&syoG 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

Mb@o5;iWvP%1F(ezY3Q@7Q`gN%e52yva* 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%0Dw}J-2H;cS3>#pbl}B=}S|ODg_4WptH_1 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àPpjn0HOs!kc0V@0+G7?x8z9D^DJ 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űI987$Lv-0;h<3 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

Xcun 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%0Dh5kZS>T!U;-(r-8!xSQo3v&w@$_BT(hv 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
  • pn}VEM>osj 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%0DOpY%QZCH%$pKmP9nbQC6rNCA%v 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

    7w=3(N}WQ{$btKk5f2`1 zS#l6yyRld-aEcsqhg|~rlC1`>G;{9mFF0fZx;?UsY0eO=|9I`wufdPXEhWcnmq8mL zdccN?htQz;&Mgj*XOS{1 zCe{WF6&eQIQ2h!ph)wj{=5I~#?W+tBOm#iixBzJ_qP~oS_>m#_AX#(SBg#;Ohzv>@ zlIWxdmuU-ZUkAw<+FOt z1T1i4+&6VMI+Ci7q8pxN|RMn2!xc=@+zKg7IlbB$&C*C9S zw7`)6a!K<2;#wakdv53j#PTIV5*%^c@-b+zU3#|M~lJeM!b6T52kKG4s=++ z;RY!S(~5cm=jfO01#e3yoF5N?njE)}x(-Sds$ zB*ngQ^YyW9EJ4p_y_YhNMk=C(w;|L1HaeyEB6<7)-21W+!

  • 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�$}v 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

  • P=u`ag4joOb_ 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

    y9lWz~~!$dzIQ0 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 z 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°9W_@Xh;t!#5i#Ya_-h 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

    vHYz{tBtSQA3DRLp?jT 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 TKNpueMgz`p0zYn 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

    ?=5*?{(sIM#P2PN^_I6f4V zDFW0Ojn=LzG@mOVMSdbyKG3xii9E%4_&AN*R}?!|p3eCeG=@Q*r68#jPR{@(>p3Nb zpcsX~UQ0Mkm&K^SZ0c%oI&Osr-Bs@nm7|+K%e-!tMbG{<(pZnEyFMK{M^_Tuf=qC; z07JVHPWibQ)!;cJ4B^8vG)#hG0KAC6a{(1f5(`xxL+1Sq-57=u6VfO6Hi{4vf_r$2 zK}-5E962h{GAh9y6JiQEK`8ik43GY=>*zK`nDoO9E+(XKn2M4O9hzu_iW2zzlNtdX zIt?9K@dy)>BqmhW4RH#N6p#)b6KZ;hiAf*`8;KeddJzgG?iXauL9s3`1sWBWBg;t?8)HJ|WX#F)^@{6zNFMGUe}7LN7Am&>;#< zw=pNMAE|+d7)XR4skx8} zNy`tTWO&D1V}Zoz%ci4L#BPGJ$LKM(Cx@75T!OL{7%_IcV9q2;SSTa*7&c1R&EJ3+ zHXhf_BOtl}qMLSCR{;Pj1Bv#gn)1jw9=40g6kXasRDbFaGeLyZ{17fi|KV^k3Y!y! zAjSX+(i>V*>*mN&)!ri?XD!E+r1SsejxhAKFv7ITvK2T^MsOd{$1x6s- z$8vZU0icJ-Qw|?s>EHUm626fw#|%=liN4|cJiDaD{KL1oJpf>auz!B*ejYACp?>QY z94SFLhlu9)Km7tK3Iu0G0Nj)@$hQ+`!_w{3P!DB!zK|_h1EXiOBf=y-c z=i#~gwlrW|_}_*K(ByBeBgpn0Z^H#hR={krX#3%?Lq@4E&mL~aY={r|W6=BIym$Dr z@tu^~eQ;$*Y*Pq*a8hBNloq}yzhPXYE^hHFQTD^3j;`et0;ooIX0i4^7k3iSZ=*au zYT0v8x=u>PP>Hm?fmae(jr|V+IiEp?D+vO|{#-H29F<@WY`4oOa`t|Oh*`Y8XO@+t z1sEt&1R{9C8|f*|g;X=dVJkuvSE32r$fiSgyHPxLC*QE%U#_E=WR9+++xjjQ#4)NT zhfDOQOV%*xPED@1t5Kbr@TA*QLuF{_?wBhPn2ds8?06PYav?uaYRDwBrz~R(o1_tU zMS>%gPvs1yG)K+7C4@ooO_Vz2f~UGAj9?ER3PYd}mxKaGA*Ruw$B7P6Vf{=+gr`iy z6r^1N#KV|z zFGbh*uo;o`Ql?$LFd`t$Hc=wWCS%9pZy6qDS$ z>34(#3;eAO9a4y1>T@eLbckNP1Pj5fjof4j9{O`@IKYM9^Kdbq=%fC5s05GUxpf3C zL~jASh@2w9;tN58eltjmlwircwSh~LU@3iW1>C!iqWh8FF%S%LpmoaE!Ðs5{H8-k3Y6u8O))d$kLaF5va?B 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

    Bg3dSU9qXexvs8MgRMdK;hJ}LT1*qtQF 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

    9!%*Kg?Xr55D4GcEgy)nE00T*MuF)ki1gKgtG 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::assert_last_event(frame_system::Event::::CodeUpdated.into()); + } #[extra] set_code_without_checks { diff --git a/frame/system/benchmarking/src/mock.rs b/frame/system/benchmarking/src/mock.rs index 8da623a3a..8b05c5a8b 100644 --- a/frame/system/benchmarking/src/mock.rs +++ b/frame/system/benchmarking/src/mock.rs @@ -19,6 +19,7 @@ #![cfg(test)] +use codec::Encode; use sp_runtime::traits::IdentityLookup; type AccountId = u64; @@ -67,7 +68,29 @@ impl frame_system::Config for Test { impl crate::Config for Test {} +struct MockedReadRuntimeVersion(Vec); + +impl sp_core::traits::ReadRuntimeVersion for MockedReadRuntimeVersion { + fn read_runtime_version( + &self, + _wasm_code: &[u8], + _ext: &mut dyn sp_externalities::Externalities, + ) -> Result, String> { + Ok(self.0.clone()) + } +} + pub fn new_test_ext() -> sp_io::TestExternalities { let t = frame_system::GenesisConfig::default().build_storage::().unwrap(); - sp_io::TestExternalities::new(t) + + let version = sp_version::RuntimeVersion { + spec_name: "spec_name".into(), + spec_version: 123, + impl_version: 456, + ..Default::default() + }; + let read_runtime_version = MockedReadRuntimeVersion(version.encode()); + let mut ext = sp_io::TestExternalities::new(t); + ext.register_extension(sp_core::traits::ReadRuntimeVersionExt::new(read_runtime_version)); + ext } diff --git a/frame/system/src/lib.rs b/frame/system/src/lib.rs index 81bead4f8..1b830105b 100644 --- a/frame/system/src/lib.rs +++ b/frame/system/src/lib.rs @@ -373,7 +373,6 @@ pub mod pallet { impl Pallet { /// Make some on-chain remark. /// - /// ## Complexity /// - `O(1)` #[pallet::call_index(0)] #[pallet::weight(T::SystemWeightInfo::remark(_remark.len() as u32))] @@ -393,31 +392,26 @@ pub mod pallet { } /// Set the new runtime code. - /// - /// ## Complexity - /// - `O(C + S)` where `C` length of `code` and `S` complexity of `can_set_code` #[pallet::call_index(2)] - #[pallet::weight((T::BlockWeights::get().max_block, DispatchClass::Operational))] + #[pallet::weight((T::SystemWeightInfo::set_code(), DispatchClass::Operational))] pub fn set_code(origin: OriginFor, code: Vec) -> DispatchResultWithPostInfo { ensure_root(origin)?; Self::can_set_code(&code)?; T::OnSetCode::set_code(code)?; - Ok(().into()) + // consume the rest of the block to prevent further transactions + Ok(Some(T::BlockWeights::get().max_block).into()) } /// Set the new runtime code without doing any checks of the given `code`. - /// - /// ## Complexity - /// - `O(C)` where `C` length of `code` #[pallet::call_index(3)] - #[pallet::weight((T::BlockWeights::get().max_block, DispatchClass::Operational))] + #[pallet::weight((T::SystemWeightInfo::set_code(), DispatchClass::Operational))] pub fn set_code_without_checks( origin: OriginFor, code: Vec, ) -> DispatchResultWithPostInfo { ensure_root(origin)?; T::OnSetCode::set_code(code)?; - Ok(().into()) + Ok(Some(T::BlockWeights::get().max_block).into()) } /// Set some items of storage. @@ -1414,9 +1408,6 @@ impl Pallet { } /// Deposits a log and ensures it matches the block's log data. - /// - /// ## Complexity - /// - `O(1)` pub fn deposit_log(item: generic::DigestItem) { >::append(item); } @@ -1622,15 +1613,23 @@ impl Pallet { .and_then(|v| RuntimeVersion::decode(&mut &v[..]).ok()) .ok_or(Error::::FailedToExtractRuntimeVersion)?; - if new_version.spec_name != current_version.spec_name { - return Err(Error::::InvalidSpecName.into()) - } + cfg_if::cfg_if! { + if #[cfg(all(feature = "runtime-benchmarks", not(test)))] { + // Let's ensure the compiler doesn't optimize our fetching of the runtime version away. + core::hint::black_box((new_version, current_version)); + Ok(()) + } else { + if new_version.spec_name != current_version.spec_name { + return Err(Error::::InvalidSpecName.into()) + } - if new_version.spec_version <= current_version.spec_version { - return Err(Error::::SpecVersionNeedsToIncrease.into()) - } + if new_version.spec_version <= current_version.spec_version { + return Err(Error::::SpecVersionNeedsToIncrease.into()) + } - Ok(()) + Ok(()) + } + } } } diff --git a/frame/system/src/tests.rs b/frame/system/src/tests.rs index 19b8d6a48..128231ccf 100644 --- a/frame/system/src/tests.rs +++ b/frame/system/src/tests.rs @@ -550,7 +550,12 @@ fn set_code_checks_works() { ("test", 1, 2, Err(Error::::SpecVersionNeedsToIncrease)), ("test", 1, 1, Err(Error::::SpecVersionNeedsToIncrease)), ("test2", 1, 1, Err(Error::::InvalidSpecName)), - ("test", 2, 1, Ok(PostDispatchInfo::default())), + ( + "test", + 2, + 1, + Ok(Some(::BlockWeights::get().max_block).into()), + ), ("test", 0, 1, Err(Error::::SpecVersionNeedsToIncrease)), ("test", 1, 0, Err(Error::::SpecVersionNeedsToIncrease)), ]; diff --git a/frame/system/src/weights.rs b/frame/system/src/weights.rs index 96fd7a197..64dce5356 100644 --- a/frame/system/src/weights.rs +++ b/frame/system/src/weights.rs @@ -18,25 +18,26 @@ //! Autogenerated weights for frame_system //! //! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev -//! DATE: 2023-04-06, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! DATE: 2023-04-13, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` //! WORST CASE MAP SIZE: `1000000` -//! HOSTNAME: `bm2`, CPU: `Intel(R) Core(TM) i7-7700K CPU @ 4.20GHz` +//! HOSTNAME: `bm3`, CPU: `Intel(R) Core(TM) i7-7700K CPU @ 4.20GHz` //! EXECUTION: Some(Wasm), WASM-EXECUTION: Compiled, CHAIN: Some("dev"), DB CACHE: 1024 // Executed Command: -// ./target/production/substrate +// target/production/substrate // benchmark // pallet -// --chain=dev // --steps=50 // --repeat=20 -// --pallet=frame_system // --extrinsic=* // --execution=wasm // --wasm-execution=compiled // --heap-pages=4096 -// --output=./frame/system/src/weights.rs +// --json-file=/var/lib/gitlab-runner/builds/zyw4fam_/0/parity/mirrors/substrate/.git/.artifacts/bench.json +// --pallet=frame_system +// --chain=dev // --header=./HEADER-APACHE2 +// --output=./frame/system/src/weights.rs // --template=./.maintain/frame-weight-template.hbs #![cfg_attr(rustfmt, rustfmt_skip)] @@ -44,13 +45,14 @@ #![allow(unused_imports)] use frame_support::{traits::Get, weights::{Weight, constants::RocksDbWeight}}; -use sp_std::marker::PhantomData; +use core::marker::PhantomData; /// Weight functions needed for frame_system. pub trait WeightInfo { fn remark(b: u32, ) -> Weight; fn remark_with_event(b: u32, ) -> Weight; fn set_heap_pages() -> Weight; + fn set_code() -> Weight; fn set_storage(i: u32, ) -> Weight; fn kill_storage(i: u32, ) -> Weight; fn kill_prefix(p: u32, ) -> Weight; @@ -64,20 +66,20 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 2_430_000 picoseconds. - Weight::from_parts(2_522_000, 0) + // Minimum execution time: 2_344_000 picoseconds. + Weight::from_parts(2_471_000, 0) // Standard Error: 0 - .saturating_add(Weight::from_parts(362, 0).saturating_mul(b.into())) + .saturating_add(Weight::from_parts(364, 0).saturating_mul(b.into())) } /// The range of component `b` is `[0, 3932160]`. fn remark_with_event(b: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 8_738_000 picoseconds. - Weight::from_parts(8_963_000, 0) + // Minimum execution time: 8_815_000 picoseconds. + Weight::from_parts(9_140_000, 0) // Standard Error: 0 - .saturating_add(Weight::from_parts(1_113, 0).saturating_mul(b.into())) + .saturating_add(Weight::from_parts(1_122, 0).saturating_mul(b.into())) } /// Storage: System Digest (r:1 w:1) /// Proof Skipped: System Digest (max_values: Some(1), max_size: None, mode: Measured) @@ -87,8 +89,21 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `1485` - // Minimum execution time: 5_109_000 picoseconds. - Weight::from_parts(5_336_000, 1485) + // Minimum execution time: 5_233_000 picoseconds. + Weight::from_parts(5_462_000, 1485) + .saturating_add(T::DbWeight::get().reads(1_u64)) + .saturating_add(T::DbWeight::get().writes(2_u64)) + } + /// Storage: System Digest (r:1 w:1) + /// Proof Skipped: System Digest (max_values: Some(1), max_size: None, mode: Measured) + /// Storage: unknown `0x3a636f6465` (r:0 w:1) + /// Proof Skipped: unknown `0x3a636f6465` (r:0 w:1) + fn set_code() -> Weight { + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `1485` + // Minimum execution time: 58_606_683_000 picoseconds. + Weight::from_parts(59_115_121_000, 1485) .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(T::DbWeight::get().writes(2_u64)) } @@ -99,10 +114,10 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 2_531_000 picoseconds. - Weight::from_parts(2_617_000, 0) - // Standard Error: 861 - .saturating_add(Weight::from_parts(761_465, 0).saturating_mul(i.into())) + // Minimum execution time: 2_317_000 picoseconds. + Weight::from_parts(2_457_000, 0) + // Standard Error: 894 + .saturating_add(Weight::from_parts(750_850, 0).saturating_mul(i.into())) .saturating_add(T::DbWeight::get().writes((1_u64).saturating_mul(i.into()))) } /// Storage: Skipped Metadata (r:0 w:0) @@ -112,10 +127,10 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 2_631_000 picoseconds. - Weight::from_parts(2_717_000, 0) - // Standard Error: 1_097 - .saturating_add(Weight::from_parts(570_803, 0).saturating_mul(i.into())) + // Minimum execution time: 2_498_000 picoseconds. + Weight::from_parts(2_552_000, 0) + // Standard Error: 1_027 + .saturating_add(Weight::from_parts(566_064, 0).saturating_mul(i.into())) .saturating_add(T::DbWeight::get().writes((1_u64).saturating_mul(i.into()))) } /// Storage: Skipped Metadata (r:0 w:0) @@ -125,10 +140,10 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `116 + p * (69 ±0)` // Estimated: `121 + p * (70 ±0)` - // Minimum execution time: 4_692_000 picoseconds. - Weight::from_parts(4_789_000, 121) - // Standard Error: 1_013 - .saturating_add(Weight::from_parts(1_143_616, 0).saturating_mul(p.into())) + // Minimum execution time: 4_646_000 picoseconds. + Weight::from_parts(4_725_000, 121) + // Standard Error: 1_195 + .saturating_add(Weight::from_parts(1_144_884, 0).saturating_mul(p.into())) .saturating_add(T::DbWeight::get().reads((1_u64).saturating_mul(p.into()))) .saturating_add(T::DbWeight::get().writes((1_u64).saturating_mul(p.into()))) .saturating_add(Weight::from_parts(0, 70).saturating_mul(p.into())) @@ -142,20 +157,20 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 2_430_000 picoseconds. - Weight::from_parts(2_522_000, 0) + // Minimum execution time: 2_344_000 picoseconds. + Weight::from_parts(2_471_000, 0) // Standard Error: 0 - .saturating_add(Weight::from_parts(362, 0).saturating_mul(b.into())) + .saturating_add(Weight::from_parts(364, 0).saturating_mul(b.into())) } /// The range of component `b` is `[0, 3932160]`. fn remark_with_event(b: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 8_738_000 picoseconds. - Weight::from_parts(8_963_000, 0) + // Minimum execution time: 8_815_000 picoseconds. + Weight::from_parts(9_140_000, 0) // Standard Error: 0 - .saturating_add(Weight::from_parts(1_113, 0).saturating_mul(b.into())) + .saturating_add(Weight::from_parts(1_122, 0).saturating_mul(b.into())) } /// Storage: System Digest (r:1 w:1) /// Proof Skipped: System Digest (max_values: Some(1), max_size: None, mode: Measured) @@ -165,8 +180,21 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `0` // Estimated: `1485` - // Minimum execution time: 5_109_000 picoseconds. - Weight::from_parts(5_336_000, 1485) + // Minimum execution time: 5_233_000 picoseconds. + Weight::from_parts(5_462_000, 1485) + .saturating_add(RocksDbWeight::get().reads(1_u64)) + .saturating_add(RocksDbWeight::get().writes(2_u64)) + } + /// Storage: System Digest (r:1 w:1) + /// Proof Skipped: System Digest (max_values: Some(1), max_size: None, mode: Measured) + /// Storage: unknown `0x3a636f6465` (r:0 w:1) + /// Proof Skipped: unknown `0x3a636f6465` (r:0 w:1) + fn set_code() -> Weight { + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `1485` + // Minimum execution time: 58_606_683_000 picoseconds. + Weight::from_parts(59_115_121_000, 1485) .saturating_add(RocksDbWeight::get().reads(1_u64)) .saturating_add(RocksDbWeight::get().writes(2_u64)) } @@ -177,10 +205,10 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 2_531_000 picoseconds. - Weight::from_parts(2_617_000, 0) - // Standard Error: 861 - .saturating_add(Weight::from_parts(761_465, 0).saturating_mul(i.into())) + // Minimum execution time: 2_317_000 picoseconds. + Weight::from_parts(2_457_000, 0) + // Standard Error: 894 + .saturating_add(Weight::from_parts(750_850, 0).saturating_mul(i.into())) .saturating_add(RocksDbWeight::get().writes((1_u64).saturating_mul(i.into()))) } /// Storage: Skipped Metadata (r:0 w:0) @@ -190,10 +218,10 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 2_631_000 picoseconds. - Weight::from_parts(2_717_000, 0) - // Standard Error: 1_097 - .saturating_add(Weight::from_parts(570_803, 0).saturating_mul(i.into())) + // Minimum execution time: 2_498_000 picoseconds. + Weight::from_parts(2_552_000, 0) + // Standard Error: 1_027 + .saturating_add(Weight::from_parts(566_064, 0).saturating_mul(i.into())) .saturating_add(RocksDbWeight::get().writes((1_u64).saturating_mul(i.into()))) } /// Storage: Skipped Metadata (r:0 w:0) @@ -203,10 +231,10 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `116 + p * (69 ±0)` // Estimated: `121 + p * (70 ±0)` - // Minimum execution time: 4_692_000 picoseconds. - Weight::from_parts(4_789_000, 121) - // Standard Error: 1_013 - .saturating_add(Weight::from_parts(1_143_616, 0).saturating_mul(p.into())) + // Minimum execution time: 4_646_000 picoseconds. + Weight::from_parts(4_725_000, 121) + // Standard Error: 1_195 + .saturating_add(Weight::from_parts(1_144_884, 0).saturating_mul(p.into())) .saturating_add(RocksDbWeight::get().reads((1_u64).saturating_mul(p.into()))) .saturating_add(RocksDbWeight::get().writes((1_u64).saturating_mul(p.into()))) .saturating_add(Weight::from_parts(0, 70).saturating_mul(p.into())) diff --git a/frame/utility/src/tests.rs b/frame/utility/src/tests.rs index 6f156e318..ecc78ae6b 100644 --- a/frame/utility/src/tests.rs +++ b/frame/utility/src/tests.rs @@ -170,7 +170,7 @@ impl frame_system::Config for Test { type AccountData = pallet_balances::AccountData; type OnNewAccount = (); type OnKilledAccount = (); - type SystemWeightInfo = (); + type SystemWeightInfo = frame_system::weights::SubstrateWeight; type SS58Prefix = (); type OnSetCode = (); type MaxConsumers = ConstU32<16>; @@ -916,12 +916,16 @@ fn batch_all_works_with_council_origin() { #[test] fn with_weight_works() { new_test_ext().execute_with(|| { + use frame_system::WeightInfo; let upgrade_code_call = Box::new(RuntimeCall::System(frame_system::Call::set_code_without_checks { code: vec![], })); // Weight before is max. - assert_eq!(upgrade_code_call.get_dispatch_info().weight, Weight::MAX); + assert_eq!( + upgrade_code_call.get_dispatch_info().weight, + ::SystemWeightInfo::set_code() + ); assert_eq!( upgrade_code_call.get_dispatch_info().class, frame_support::dispatch::DispatchClass::Operational From 8796276b205351b91b2034999b3f20e5b318c77e Mon Sep 17 00:00:00 2001 From: Ross Bulat Date: Fri, 12 May 2023 02:22:15 +0800 Subject: [PATCH 490/558] Staking::{bond, set_controller} to set controllers to stash only. (#14039) * update set_controller * clone * bond uses `stash` * remove controller from bond(), chill_other test works * remove ctlr from testing_utils & dead ctlr -> dead payee * mvs controllers to stashes for 3 tests * migrate mock bond fns & fix 1 test * mvs controllers to stashes for 7 tests * mvs controllers to stashes for 9 tests * remove double_controlling_should_fail * remove double_staking_should_fail * mvs controllers to stashes for 10 tests * mvs controllers to stashes for 2 tests * remove payout_creates_controller * mvs controllers to stashes for 27 tests * remove println! * fix rewards_should_work * fix test_payout_stakers * fix bond benchmark * clone * rm unused import * rm unused var * rm controller from create_offender * fix GenesisConfig stakers * fix controllers in consensus pallets * fix unqiue controller in chain_spec * fmt * fix create_offender * fix set_controller benchmark * add TODO * create_unique_stash_controller * staking benchmarks working * fmt * fix args * rm println * import * import * fix fast unstake tests * fix staking-tests-e2e * fix root-offenses * fmt * differentiate controller to stash * bring back change_controller_works w. unique ctrl * bring back double_staking_should_fail * double_controlling_attempt_should_fail * bring back payout_creates_controller * add commnet to controller balances * + set_controller call description * fmt * rm clones * fmt * clippy fixes * fmt * update README * small fixes * use controller_to_be_deprecated * .comment * comment * bump zombienet version * ci --------- Co-authored-by: parity-processbot <> Co-authored-by: Javier Viola --- .gitlab-ci.yml | 2 +- bin/node/cli/src/chain_spec.rs | 2 +- bin/node/testing/src/genesis.rs | 6 +- frame/babe/src/mock.rs | 6 +- frame/beefy/src/mock.rs | 6 +- .../test-staking-e2e/src/mock.rs | 26 +- frame/fast-unstake/src/mock.rs | 17 +- frame/fast-unstake/src/tests.rs | 92 +- frame/grandpa/src/mock.rs | 6 +- frame/offences/benchmarking/src/lib.rs | 17 +- frame/root-offences/src/mock.rs | 10 +- frame/session/benchmarking/src/lib.rs | 4 +- frame/staking/README.md | 8 +- frame/staking/src/benchmarking.rs | 39 +- frame/staking/src/lib.rs | 2 +- frame/staking/src/mock.rs | 45 +- frame/staking/src/pallet/impls.rs | 1 - frame/staking/src/pallet/mod.rs | 33 +- frame/staking/src/testing_utils.rs | 71 +- frame/staking/src/tests.rs | 1012 ++++++++--------- 20 files changed, 695 insertions(+), 710 deletions(-) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index cc950865c..fe70d3532 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -61,7 +61,7 @@ variables: NEXTEST_FAILURE_OUTPUT: immediate-final NEXTEST_SUCCESS_OUTPUT: final - ZOMBIENET_IMAGE: "docker.io/paritytech/zombienet:v1.3.48" + ZOMBIENET_IMAGE: "docker.io/paritytech/zombienet:v1.3.52" default: retry: diff --git a/bin/node/cli/src/chain_spec.rs b/bin/node/cli/src/chain_spec.rs index 4732e12f9..85a08e71c 100644 --- a/bin/node/cli/src/chain_spec.rs +++ b/bin/node/cli/src/chain_spec.rs @@ -274,7 +274,7 @@ pub fn testnet_genesis( let mut rng = rand::thread_rng(); let stakers = initial_authorities .iter() - .map(|x| (x.0.clone(), x.1.clone(), STASH, StakerStatus::Validator)) + .map(|x| (x.0.clone(), x.0.clone(), STASH, StakerStatus::Validator)) .chain(initial_nominators.iter().map(|x| { use rand::{seq::SliceRandom, Rng}; let limit = (MaxNominations::get() as usize).min(initial_authorities.len()); diff --git a/bin/node/testing/src/genesis.rs b/bin/node/testing/src/genesis.rs index b8c80aeb1..d542bb29c 100644 --- a/bin/node/testing/src/genesis.rs +++ b/bin/node/testing/src/genesis.rs @@ -65,9 +65,9 @@ pub fn config_endowed(code: Option<&[u8]>, extra_endowed: Vec) -> Gen }, staking: StakingConfig { stakers: vec![ - (dave(), alice(), 111 * DOLLARS, StakerStatus::Validator), - (eve(), bob(), 100 * DOLLARS, StakerStatus::Validator), - (ferdie(), charlie(), 100 * DOLLARS, StakerStatus::Validator), + (dave(), dave(), 111 * DOLLARS, StakerStatus::Validator), + (eve(), eve(), 100 * DOLLARS, StakerStatus::Validator), + (ferdie(), ferdie(), 100 * DOLLARS, StakerStatus::Validator), ], validator_count: 3, minimum_validator_count: 0, diff --git a/frame/babe/src/mock.rs b/frame/babe/src/mock.rs index 96ebd818b..4278fa459 100644 --- a/frame/babe/src/mock.rs +++ b/frame/babe/src/mock.rs @@ -368,11 +368,9 @@ pub fn new_test_ext_raw_authorities(authorities: Vec) -> sp_io::Tes .assimilate_storage(&mut t) .unwrap(); - // controllers are the index + 1000 + // controllers are same as stash let stakers: Vec<_> = (0..authorities.len()) - .map(|i| { - (i as u64, i as u64 + 1000, 10_000, pallet_staking::StakerStatus::::Validator) - }) + .map(|i| (i as u64, i as u64, 10_000, pallet_staking::StakerStatus::::Validator)) .collect(); let staking_config = pallet_staking::GenesisConfig:: { diff --git a/frame/beefy/src/mock.rs b/frame/beefy/src/mock.rs index ceb95263e..6b6ffd675 100644 --- a/frame/beefy/src/mock.rs +++ b/frame/beefy/src/mock.rs @@ -284,11 +284,9 @@ pub fn new_test_ext_raw_authorities(authorities: Vec) -> TestExternalit .assimilate_storage(&mut t) .unwrap(); - // controllers are the index + 1000 + // controllers are same as stash let stakers: Vec<_> = (0..authorities.len()) - .map(|i| { - (i as u64, i as u64 + 1000, 10_000, pallet_staking::StakerStatus::::Validator) - }) + .map(|i| (i as u64, i as u64, 10_000, pallet_staking::StakerStatus::::Validator)) .collect(); let staking_config = pallet_staking::GenesisConfig:: { diff --git a/frame/election-provider-multi-phase/test-staking-e2e/src/mock.rs b/frame/election-provider-multi-phase/test-staking-e2e/src/mock.rs index c0ad6936f..e1abbb909 100644 --- a/frame/election-provider-multi-phase/test-staking-e2e/src/mock.rs +++ b/frame/election-provider-multi-phase/test-staking-e2e/src/mock.rs @@ -372,19 +372,19 @@ impl Default for StakingExtBuilder { let stakers = vec![ // (stash, ctrl, stake, status) // these two will be elected in the default test where we elect 2. - (11, 10, 1000, StakerStatus::::Validator), - (21, 20, 1000, StakerStatus::::Validator), - // loser validatos if validator_count() is default. - (31, 30, 500, StakerStatus::::Validator), - (41, 40, 500, StakerStatus::::Validator), - (51, 50, 500, StakerStatus::::Validator), - (61, 60, 500, StakerStatus::::Validator), - (71, 70, 500, StakerStatus::::Validator), - (81, 80, 500, StakerStatus::::Validator), - (91, 90, 500, StakerStatus::::Validator), - (101, 100, 500, StakerStatus::::Validator), + (11, 11, 1000, StakerStatus::::Validator), + (21, 21, 1000, StakerStatus::::Validator), + // loser validators if validator_count() is default. + (31, 31, 500, StakerStatus::::Validator), + (41, 41, 1500, StakerStatus::::Validator), + (51, 51, 1500, StakerStatus::::Validator), + (61, 61, 1500, StakerStatus::::Validator), + (71, 71, 1500, StakerStatus::::Validator), + (81, 81, 1500, StakerStatus::::Validator), + (91, 91, 1500, StakerStatus::::Validator), + (101, 101, 500, StakerStatus::::Validator), // an idle validator - (201, 200, 1000, StakerStatus::::Idle), + (201, 201, 1000, StakerStatus::::Idle), ]; Self { @@ -439,7 +439,7 @@ impl Default for BalancesExtBuilder { (2, 20), (3, 300), (4, 400), - // controllers + // controllers (still used in some tests. Soon to be deprecated). (10, 100), (20, 100), (30, 100), diff --git a/frame/fast-unstake/src/mock.rs b/frame/fast-unstake/src/mock.rs index fbe6c4592..d75c89380 100644 --- a/frame/fast-unstake/src/mock.rs +++ b/frame/fast-unstake/src/mock.rs @@ -234,11 +234,11 @@ impl Default for ExtBuilder { fn default() -> Self { Self { unexposed: vec![ - (1, 2, 7 + 100), - (3, 4, 7 + 100), - (5, 6, 7 + 100), - (7, 8, 7 + 100), - (9, 10, 7 + 100), + (1, 1, 7 + 100), + (3, 3, 7 + 100), + (5, 5, 7 + 100), + (7, 7, 7 + 100), + (9, 9, 7 + 100), ], } } @@ -290,12 +290,6 @@ impl ExtBuilder { .clone() .into_iter() .map(|(stash, _, balance)| (stash, balance * 2)) - .chain( - self.unexposed - .clone() - .into_iter() - .map(|(_, ctrl, balance)| (ctrl, balance * 2)), - ) .chain(validators_range.clone().map(|x| (x, 7 + 100))) .chain(nominators_range.clone().map(|x| (x, 7 + 100))) .collect::>(), @@ -377,7 +371,6 @@ pub fn create_exposed_nominator(exposed: AccountId, era: u32) { Balances::make_free_balance_be(&exposed, 100); assert_ok!(Staking::bond( RuntimeOrigin::signed(exposed), - exposed, 10, pallet_staking::RewardDestination::Staked )); diff --git a/frame/fast-unstake/src/tests.rs b/frame/fast-unstake/src/tests.rs index b4bf1f1cb..c51c817ec 100644 --- a/frame/fast-unstake/src/tests.rs +++ b/frame/fast-unstake/src/tests.rs @@ -37,7 +37,7 @@ fn register_works() { ExtBuilder::default().build_and_execute(|| { ErasToCheckPerBlock::::put(1); // Controller account registers for fast unstake. - assert_ok!(FastUnstake::register_fast_unstake(RuntimeOrigin::signed(2))); + assert_ok!(FastUnstake::register_fast_unstake(RuntimeOrigin::signed(1))); // Ensure stash is in the queue. assert_ne!(Queue::::get(1), None); }); @@ -52,7 +52,7 @@ fn register_insufficient_funds_fails() { // Controller account registers for fast unstake. assert_noop!( - FastUnstake::register_fast_unstake(RuntimeOrigin::signed(2)), + FastUnstake::register_fast_unstake(RuntimeOrigin::signed(1)), BalancesError::::InsufficientBalance, ); @@ -65,7 +65,7 @@ fn register_insufficient_funds_fails() { fn register_disabled_fails() { ExtBuilder::default().build_and_execute(|| { assert_noop!( - FastUnstake::register_fast_unstake(RuntimeOrigin::signed(2)), + FastUnstake::register_fast_unstake(RuntimeOrigin::signed(1)), Error::::CallNotAllowed ); }); @@ -81,7 +81,7 @@ fn cannot_register_if_not_bonded() { } // Attempt to fast unstake. assert_noop!( - FastUnstake::register_fast_unstake(RuntimeOrigin::signed(1)), + FastUnstake::register_fast_unstake(RuntimeOrigin::signed(2)), Error::::NotController ); }); @@ -95,7 +95,7 @@ fn cannot_register_if_in_queue() { Queue::::insert(1, 10); // Cannot re-register, already in queue assert_noop!( - FastUnstake::register_fast_unstake(RuntimeOrigin::signed(2)), + FastUnstake::register_fast_unstake(RuntimeOrigin::signed(1)), Error::::AlreadyQueued ); }); @@ -112,7 +112,7 @@ fn cannot_register_if_head() { }); // Controller attempts to regsiter assert_noop!( - FastUnstake::register_fast_unstake(RuntimeOrigin::signed(2)), + FastUnstake::register_fast_unstake(RuntimeOrigin::signed(1)), Error::::AlreadyHead ); }); @@ -123,10 +123,10 @@ fn cannot_register_if_has_unlocking_chunks() { ExtBuilder::default().build_and_execute(|| { ErasToCheckPerBlock::::put(1); // Start unbonding half of staked tokens - assert_ok!(Staking::unbond(RuntimeOrigin::signed(2), 50_u128)); + assert_ok!(Staking::unbond(RuntimeOrigin::signed(1), 50_u128)); // Cannot register for fast unstake with unlock chunks active assert_noop!( - FastUnstake::register_fast_unstake(RuntimeOrigin::signed(2)), + FastUnstake::register_fast_unstake(RuntimeOrigin::signed(1)), Error::::NotFullyBonded ); }); @@ -140,11 +140,11 @@ fn deregister_works() { assert_eq!(::Currency::reserved_balance(&1), 0); // Controller account registers for fast unstake. - assert_ok!(FastUnstake::register_fast_unstake(RuntimeOrigin::signed(2))); + assert_ok!(FastUnstake::register_fast_unstake(RuntimeOrigin::signed(1))); assert_eq!(::Currency::reserved_balance(&1), Deposit::get()); // Controller then changes mind and deregisters. - assert_ok!(FastUnstake::deregister(RuntimeOrigin::signed(2))); + assert_ok!(FastUnstake::deregister(RuntimeOrigin::signed(1))); assert_eq!(::Currency::reserved_balance(&1), 0); // Ensure stash no longer exists in the queue. @@ -156,9 +156,9 @@ fn deregister_works() { fn deregister_disabled_fails() { ExtBuilder::default().build_and_execute(|| { ErasToCheckPerBlock::::put(1); - assert_ok!(FastUnstake::register_fast_unstake(RuntimeOrigin::signed(2))); + assert_ok!(FastUnstake::register_fast_unstake(RuntimeOrigin::signed(1))); ErasToCheckPerBlock::::put(0); - assert_noop!(FastUnstake::deregister(RuntimeOrigin::signed(2)), Error::::CallNotAllowed); + assert_noop!(FastUnstake::deregister(RuntimeOrigin::signed(1)), Error::::CallNotAllowed); }); } @@ -166,10 +166,10 @@ fn deregister_disabled_fails() { fn cannot_deregister_if_not_controller() { ExtBuilder::default().build_and_execute(|| { ErasToCheckPerBlock::::put(1); - // Controller account registers for fast unstake. - assert_ok!(FastUnstake::register_fast_unstake(RuntimeOrigin::signed(2))); - // Stash tries to deregister. - assert_noop!(FastUnstake::deregister(RuntimeOrigin::signed(1)), Error::::NotController); + // Controller (same as stash) account registers for fast unstake. + assert_ok!(FastUnstake::register_fast_unstake(RuntimeOrigin::signed(1))); + // Another account tries to deregister. + assert_noop!(FastUnstake::deregister(RuntimeOrigin::signed(2)), Error::::NotController); }); } @@ -178,7 +178,7 @@ fn cannot_deregister_if_not_queued() { ExtBuilder::default().build_and_execute(|| { ErasToCheckPerBlock::::put(1); // Controller tries to deregister without first registering - assert_noop!(FastUnstake::deregister(RuntimeOrigin::signed(2)), Error::::NotQueued); + assert_noop!(FastUnstake::deregister(RuntimeOrigin::signed(1)), Error::::NotQueued); }); } @@ -187,14 +187,14 @@ fn cannot_deregister_already_head() { ExtBuilder::default().build_and_execute(|| { ErasToCheckPerBlock::::put(1); // Controller attempts to register, should fail - assert_ok!(FastUnstake::register_fast_unstake(RuntimeOrigin::signed(2))); + assert_ok!(FastUnstake::register_fast_unstake(RuntimeOrigin::signed(1))); // Insert some Head item for stash. Head::::put(UnstakeRequest { stashes: bounded_vec![(1, Deposit::get())], checked: bounded_vec![], }); // Controller attempts to deregister - assert_noop!(FastUnstake::deregister(RuntimeOrigin::signed(2)), Error::::AlreadyHead); + assert_noop!(FastUnstake::deregister(RuntimeOrigin::signed(1)), Error::::AlreadyHead); }); } @@ -210,7 +210,7 @@ fn control_works() { fn control_must_be_control_origin() { ExtBuilder::default().build_and_execute(|| { // account without control (root) origin wants to only check 1 era per block. - assert_noop!(FastUnstake::control(RuntimeOrigin::signed(1), 1_u32), BadOrigin); + assert_noop!(FastUnstake::control(RuntimeOrigin::signed(2), 1_u32), BadOrigin); }); } @@ -224,7 +224,7 @@ mod on_idle { CurrentEra::::put(BondingDuration::get()); // set up Queue item - assert_ok!(FastUnstake::register_fast_unstake(RuntimeOrigin::signed(2))); + assert_ok!(FastUnstake::register_fast_unstake(RuntimeOrigin::signed(1))); assert_eq!(Queue::::get(1), Some(Deposit::get())); // call on_idle with no remaining weight @@ -245,11 +245,11 @@ mod on_idle { // given assert_eq!(::Currency::reserved_balance(&1), 0); - assert_ok!(FastUnstake::register_fast_unstake(RuntimeOrigin::signed(2))); - assert_ok!(FastUnstake::register_fast_unstake(RuntimeOrigin::signed(4))); - assert_ok!(FastUnstake::register_fast_unstake(RuntimeOrigin::signed(6))); - assert_ok!(FastUnstake::register_fast_unstake(RuntimeOrigin::signed(8))); - assert_ok!(FastUnstake::register_fast_unstake(RuntimeOrigin::signed(10))); + assert_ok!(FastUnstake::register_fast_unstake(RuntimeOrigin::signed(1))); + assert_ok!(FastUnstake::register_fast_unstake(RuntimeOrigin::signed(3))); + assert_ok!(FastUnstake::register_fast_unstake(RuntimeOrigin::signed(5))); + assert_ok!(FastUnstake::register_fast_unstake(RuntimeOrigin::signed(7))); + assert_ok!(FastUnstake::register_fast_unstake(RuntimeOrigin::signed(9))); assert_eq!(::Currency::reserved_balance(&1), Deposit::get()); @@ -310,9 +310,9 @@ mod on_idle { CurrentEra::::put(BondingDuration::get()); // register multi accounts for fast unstake - assert_ok!(FastUnstake::register_fast_unstake(RuntimeOrigin::signed(2))); + assert_ok!(FastUnstake::register_fast_unstake(RuntimeOrigin::signed(1))); assert_eq!(Queue::::get(1), Some(Deposit::get())); - assert_ok!(FastUnstake::register_fast_unstake(RuntimeOrigin::signed(4))); + assert_ok!(FastUnstake::register_fast_unstake(RuntimeOrigin::signed(3))); assert_eq!(Queue::::get(3), Some(Deposit::get())); // assert 2 queue items are in Queue & None in Head to start with @@ -363,7 +363,7 @@ mod on_idle { CurrentEra::::put(BondingDuration::get()); // register for fast unstake - assert_ok!(FastUnstake::register_fast_unstake(RuntimeOrigin::signed(2))); + assert_ok!(FastUnstake::register_fast_unstake(RuntimeOrigin::signed(1))); assert_eq!(Queue::::get(1), Some(Deposit::get())); // process on idle @@ -405,7 +405,7 @@ mod on_idle { Balances::make_free_balance_be(&2, 100); // register for fast unstake - assert_ok!(FastUnstake::register_fast_unstake(RuntimeOrigin::signed(2))); + assert_ok!(FastUnstake::register_fast_unstake(RuntimeOrigin::signed(1))); assert_eq!(Queue::::get(1), Some(Deposit::get())); // process on idle @@ -446,7 +446,7 @@ mod on_idle { CurrentEra::::put(BondingDuration::get()); // register for fast unstake - assert_ok!(FastUnstake::register_fast_unstake(RuntimeOrigin::signed(2))); + assert_ok!(FastUnstake::register_fast_unstake(RuntimeOrigin::signed(1))); assert_eq!(Queue::::get(1), Some(Deposit::get())); // process on idle @@ -524,7 +524,7 @@ mod on_idle { CurrentEra::::put(BondingDuration::get()); // register for fast unstake - assert_ok!(FastUnstake::register_fast_unstake(RuntimeOrigin::signed(2))); + assert_ok!(FastUnstake::register_fast_unstake(RuntimeOrigin::signed(1))); assert_eq!(Queue::::get(1), Some(Deposit::get())); next_block(true); @@ -605,7 +605,7 @@ mod on_idle { CurrentEra::::put(BondingDuration::get()); // register for fast unstake - assert_ok!(FastUnstake::register_fast_unstake(RuntimeOrigin::signed(2))); + assert_ok!(FastUnstake::register_fast_unstake(RuntimeOrigin::signed(1))); // process 2 blocks next_block(true); @@ -812,7 +812,7 @@ mod on_idle { // create a new validator that 100% not exposed. Balances::make_free_balance_be(&42, 100 + Deposit::get()); - assert_ok!(Staking::bond(RuntimeOrigin::signed(42), 42, 10, RewardDestination::Staked)); + assert_ok!(Staking::bond(RuntimeOrigin::signed(42), 10, RewardDestination::Staked)); assert_ok!(Staking::validate(RuntimeOrigin::signed(42), Default::default())); // let them register: @@ -851,10 +851,10 @@ mod batched { ErasToCheckPerBlock::::put(BondingDuration::get() + 1); CurrentEra::::put(BondingDuration::get()); - assert_ok!(FastUnstake::register_fast_unstake(RuntimeOrigin::signed(2))); - assert_ok!(FastUnstake::register_fast_unstake(RuntimeOrigin::signed(4))); - assert_ok!(FastUnstake::register_fast_unstake(RuntimeOrigin::signed(6))); - assert_ok!(FastUnstake::register_fast_unstake(RuntimeOrigin::signed(8))); + assert_ok!(FastUnstake::register_fast_unstake(RuntimeOrigin::signed(1))); + assert_ok!(FastUnstake::register_fast_unstake(RuntimeOrigin::signed(3))); + assert_ok!(FastUnstake::register_fast_unstake(RuntimeOrigin::signed(5))); + assert_ok!(FastUnstake::register_fast_unstake(RuntimeOrigin::signed(7))); assert_eq!(Queue::::count(), 4); assert_eq!(Head::::get(), None); @@ -902,10 +902,10 @@ mod batched { ErasToCheckPerBlock::::put(2); CurrentEra::::put(BondingDuration::get()); - assert_ok!(FastUnstake::register_fast_unstake(RuntimeOrigin::signed(2))); - assert_ok!(FastUnstake::register_fast_unstake(RuntimeOrigin::signed(4))); - assert_ok!(FastUnstake::register_fast_unstake(RuntimeOrigin::signed(6))); - assert_ok!(FastUnstake::register_fast_unstake(RuntimeOrigin::signed(8))); + assert_ok!(FastUnstake::register_fast_unstake(RuntimeOrigin::signed(1))); + assert_ok!(FastUnstake::register_fast_unstake(RuntimeOrigin::signed(3))); + assert_ok!(FastUnstake::register_fast_unstake(RuntimeOrigin::signed(5))); + assert_ok!(FastUnstake::register_fast_unstake(RuntimeOrigin::signed(7))); assert_eq!(Queue::::count(), 4); assert_eq!(Head::::get(), None); @@ -969,8 +969,8 @@ mod batched { CurrentEra::::put(BondingDuration::get()); // register two good ones. - assert_ok!(FastUnstake::register_fast_unstake(RuntimeOrigin::signed(2))); - assert_ok!(FastUnstake::register_fast_unstake(RuntimeOrigin::signed(4))); + assert_ok!(FastUnstake::register_fast_unstake(RuntimeOrigin::signed(1))); + assert_ok!(FastUnstake::register_fast_unstake(RuntimeOrigin::signed(3))); create_exposed_nominator(666, 1); create_exposed_nominator(667, 3); @@ -1045,8 +1045,8 @@ mod batched { next_block(true); // ..and register two good ones. - assert_ok!(FastUnstake::register_fast_unstake(RuntimeOrigin::signed(2))); - assert_ok!(FastUnstake::register_fast_unstake(RuntimeOrigin::signed(4))); + assert_ok!(FastUnstake::register_fast_unstake(RuntimeOrigin::signed(1))); + assert_ok!(FastUnstake::register_fast_unstake(RuntimeOrigin::signed(3))); // then one of the bad ones is reaped. assert_eq!( diff --git a/frame/grandpa/src/mock.rs b/frame/grandpa/src/mock.rs index a7359f689..ffc566ffe 100644 --- a/frame/grandpa/src/mock.rs +++ b/frame/grandpa/src/mock.rs @@ -284,11 +284,9 @@ pub fn new_test_ext_raw_authorities(authorities: AuthorityList) -> sp_io::TestEx .assimilate_storage(&mut t) .unwrap(); - // controllers are the index + 1000 + // controllers are the same as stash let stakers: Vec<_> = (0..authorities.len()) - .map(|i| { - (i as u64, i as u64 + 1000, 10_000, pallet_staking::StakerStatus::::Validator) - }) + .map(|i| (i as u64, i as u64, 10_000, pallet_staking::StakerStatus::::Validator)) .collect(); let staking_config = pallet_staking::GenesisConfig:: { diff --git a/frame/offences/benchmarking/src/lib.rs b/frame/offences/benchmarking/src/lib.rs index 894a725b5..e7fc39657 100644 --- a/frame/offences/benchmarking/src/lib.rs +++ b/frame/offences/benchmarking/src/lib.rs @@ -107,8 +107,7 @@ fn bond_amount() -> BalanceOf { fn create_offender(n: u32, nominators: u32) -> Result, &'static str> { let stash: T::AccountId = account("stash", n, SEED); - let controller: T::AccountId = account("controller", n, SEED); - let controller_lookup: LookupSourceOf = T::Lookup::unlookup(controller.clone()); + let stash_lookup: LookupSourceOf = T::Lookup::unlookup(stash.clone()); let reward_destination = RewardDestination::Staked; let amount = bond_amount::(); // add twice as much balance to prevent the account from being killed. @@ -116,14 +115,13 @@ fn create_offender(n: u32, nominators: u32) -> Result, &' T::Currency::make_free_balance_be(&stash, free_amount); Staking::::bond( RawOrigin::Signed(stash.clone()).into(), - controller_lookup.clone(), amount, reward_destination.clone(), )?; let validator_prefs = ValidatorPrefs { commission: Perbill::from_percent(50), ..Default::default() }; - Staking::::validate(RawOrigin::Signed(controller.clone()).into(), validator_prefs)?; + Staking::::validate(RawOrigin::Signed(stash.clone()).into(), validator_prefs)?; let mut individual_exposures = vec![]; let mut nominator_stashes = vec![]; @@ -131,22 +129,17 @@ fn create_offender(n: u32, nominators: u32) -> Result, &' for i in 0..nominators { let nominator_stash: T::AccountId = account("nominator stash", n * MAX_NOMINATORS + i, SEED); - let nominator_controller: T::AccountId = - account("nominator controller", n * MAX_NOMINATORS + i, SEED); - let nominator_controller_lookup: LookupSourceOf = - T::Lookup::unlookup(nominator_controller.clone()); T::Currency::make_free_balance_be(&nominator_stash, free_amount); Staking::::bond( RawOrigin::Signed(nominator_stash.clone()).into(), - nominator_controller_lookup.clone(), amount, reward_destination.clone(), )?; - let selected_validators: Vec> = vec![controller_lookup.clone()]; + let selected_validators: Vec> = vec![stash_lookup.clone()]; Staking::::nominate( - RawOrigin::Signed(nominator_controller.clone()).into(), + RawOrigin::Signed(nominator_stash.clone()).into(), selected_validators, )?; @@ -159,7 +152,7 @@ fn create_offender(n: u32, nominators: u32) -> Result, &' let current_era = 0u32; Staking::::add_era_stakers(current_era, stash.clone(), exposure); - Ok(Offender { controller, stash, nominator_stashes }) + Ok(Offender { controller: stash.clone(), stash, nominator_stashes }) } fn make_offenders( diff --git a/frame/root-offences/src/mock.rs b/frame/root-offences/src/mock.rs index 828551e4d..e48360ed3 100644 --- a/frame/root-offences/src/mock.rs +++ b/frame/root-offences/src/mock.rs @@ -259,7 +259,7 @@ impl ExtBuilder { pallet_balances::GenesisConfig:: { balances: vec![ - //controllers + // controllers (still used in some tests. Soon to be deprecated). (10, self.balance_factor * 50), (20, self.balance_factor * 50), (30, self.balance_factor * 50), @@ -277,12 +277,12 @@ impl ExtBuilder { let stakers = vec![ // (stash, ctrl, stake, status) // these two will be elected in the default test where we elect 2. - (11, 10, 1000, StakerStatus::::Validator), - (21, 20, 1000, StakerStatus::::Validator), + (11, 11, 1000, StakerStatus::::Validator), + (21, 21, 1000, StakerStatus::::Validator), // a loser validator - (31, 30, 500, StakerStatus::::Validator), + (31, 31, 500, StakerStatus::::Validator), // an idle validator - (41, 40, 1000, StakerStatus::::Idle), + (41, 41, 1000, StakerStatus::::Idle), ]; let _ = pallet_staking::GenesisConfig:: { diff --git a/frame/session/benchmarking/src/lib.rs b/frame/session/benchmarking/src/lib.rs index 7f64dc70f..a7e326fb2 100644 --- a/frame/session/benchmarking/src/lib.rs +++ b/frame/session/benchmarking/src/lib.rs @@ -59,6 +59,7 @@ benchmarks! { n, ::MaxNominations::get(), false, + true, RewardDestination::Staked, )?; let v_controller = pallet_staking::Pallet::::bonded(&v_stash).ok_or("not stash")?; @@ -76,7 +77,8 @@ benchmarks! { n, ::MaxNominations::get(), false, - RewardDestination::Staked + true, + RewardDestination::Staked, )?; let v_controller = pallet_staking::Pallet::::bonded(&v_stash).ok_or("not stash")?; let keys = T::Keys::decode(&mut TrailingZeroInput::zeroes()).unwrap(); diff --git a/frame/staking/README.md b/frame/staking/README.md index bbd5bd18f..ccb9901a6 100644 --- a/frame/staking/README.md +++ b/frame/staking/README.md @@ -50,8 +50,12 @@ used. An account pair can become bonded using the [`bond`](https://docs.rs/pallet-staking/latest/pallet_staking/enum.Call.html#variant.bond) call. -Stash accounts can change their associated controller using the -[`set_controller`](https://docs.rs/pallet-staking/latest/pallet_staking/enum.Call.html#variant.set_controller) call. +Stash accounts can update their associated controller back to their stash account using the +[`set_controller`](https://docs.rs/pallet-staking/latest/pallet_staking/enum.Call.html#variant.set_controller) +call. + +Note: Controller accounts are being deprecated in favor of proxy accounts, so it is no longer +possible to set a unique address for a stash's controller. There are three possible roles that any staked account pair can be in: `Validator`, `Nominator` and `Idle` (defined in [`StakerStatus`](https://docs.rs/pallet-staking/latest/pallet_staking/enum.StakerStatus.html)). There are three diff --git a/frame/staking/src/benchmarking.rs b/frame/staking/src/benchmarking.rs index ad7aab984..53589ecfe 100644 --- a/frame/staking/src/benchmarking.rs +++ b/frame/staking/src/benchmarking.rs @@ -71,7 +71,8 @@ pub fn add_slashing_spans(who: &T::AccountId, spans: u32) { pub fn create_validator_with_nominators( n: u32, upper_bound: u32, - dead: bool, + dead_controller: bool, + unique_controller: bool, destination: RewardDestination, ) -> Result<(T::AccountId, Vec<(T::AccountId, T::AccountId)>), &'static str> { // Clean up any existing state. @@ -79,7 +80,12 @@ pub fn create_validator_with_nominators( let mut points_total = 0; let mut points_individual = Vec::new(); - let (v_stash, v_controller) = create_stash_controller::(0, 100, destination.clone())?; + let (v_stash, v_controller) = if unique_controller { + create_unique_stash_controller::(0, 100, destination.clone(), false)? + } else { + create_stash_controller::(0, 100, destination.clone())? + }; + let validator_prefs = ValidatorPrefs { commission: Perbill::from_percent(50), ..Default::default() }; Staking::::validate(RawOrigin::Signed(v_controller).into(), validator_prefs)?; @@ -93,10 +99,10 @@ pub fn create_validator_with_nominators( // Give the validator n nominators, but keep total users in the system the same. for i in 0..upper_bound { - let (n_stash, n_controller) = if !dead { + let (n_stash, n_controller) = if !dead_controller { create_stash_controller::(u32::MAX - i, 100, destination.clone())? } else { - create_stash_and_dead_controller::(u32::MAX - i, 100, destination.clone())? + create_unique_stash_controller::(u32::MAX - i, 100, destination.clone(), true)? }; if i < n { Staking::::nominate( @@ -217,15 +223,13 @@ const USER_SEED: u32 = 999666; benchmarks! { bond { let stash = create_funded_user::("stash", USER_SEED, 100); - let controller = create_funded_user::("controller", USER_SEED, 100); - let controller_lookup = T::Lookup::unlookup(controller.clone()); let reward_destination = RewardDestination::Staked; let amount = T::Currency::minimum_balance() * 10u32.into(); whitelist_account!(stash); - }: _(RawOrigin::Signed(stash.clone()), controller_lookup, amount, reward_destination) + }: _(RawOrigin::Signed(stash.clone()), amount, reward_destination) verify { - assert!(Bonded::::contains_key(stash)); - assert!(Ledger::::contains_key(controller)); + assert!(Bonded::::contains_key(stash.clone())); + assert!(Ledger::::contains_key(stash)); } bond_extra { @@ -470,13 +474,16 @@ benchmarks! { } set_controller { - let (stash, _) = create_stash_controller::(USER_SEED, 100, Default::default())?; - let new_controller = create_funded_user::("new_controller", USER_SEED, 100); - let new_controller_lookup = T::Lookup::unlookup(new_controller.clone()); + let (stash, ctlr) = create_unique_stash_controller::(9000, 100, Default::default(), false)?; + // ensure `ctlr` is the currently stored controller. + assert!(!Ledger::::contains_key(&stash)); + assert!(Ledger::::contains_key(&ctlr)); + assert_eq!(Bonded::::get(&stash), Some(ctlr.clone())); + whitelist_account!(stash); - }: _(RawOrigin::Signed(stash), new_controller_lookup) + }: _(RawOrigin::Signed(stash.clone())) verify { - assert!(Ledger::::contains_key(&new_controller)); + assert!(Ledger::::contains_key(&stash)); } set_validator_count { @@ -551,6 +558,7 @@ benchmarks! { n, T::MaxNominatorRewardedPerValidator::get() as u32, true, + true, RewardDestination::Controller, )?; @@ -584,6 +592,7 @@ benchmarks! { n, T::MaxNominatorRewardedPerValidator::get() as u32, false, + true, RewardDestination::Staked, )?; @@ -978,6 +987,7 @@ mod tests { n, <::MaxNominatorRewardedPerValidator as Get<_>>::get(), false, + false, RewardDestination::Staked, ) .unwrap(); @@ -1007,6 +1017,7 @@ mod tests { n, <::MaxNominatorRewardedPerValidator as Get<_>>::get(), false, + false, RewardDestination::Staked, ) .unwrap(); diff --git a/frame/staking/src/lib.rs b/frame/staking/src/lib.rs index 28d970b91..c87aeb681 100644 --- a/frame/staking/src/lib.rs +++ b/frame/staking/src/lib.rs @@ -67,7 +67,7 @@ //! //! An account pair can become bonded using the [`bond`](Call::bond) call. //! -//! Stash accounts can change their associated controller using the +//! Stash accounts can update their associated controller back to the stash account using the //! [`set_controller`](Call::set_controller) call. //! //! There are three possible roles that any staked account pair can be in: `Validator`, `Nominator` diff --git a/frame/staking/src/mock.rs b/frame/staking/src/mock.rs index c2f559a97..98b58010a 100644 --- a/frame/staking/src/mock.rs +++ b/frame/staking/src/mock.rs @@ -432,7 +432,7 @@ impl ExtBuilder { (2, 20 * self.balance_factor), (3, 300 * self.balance_factor), (4, 400 * self.balance_factor), - // controllers + // controllers (still used in some tests. Soon to be deprecated). (10, self.balance_factor), (20, self.balance_factor), (30, self.balance_factor), @@ -465,18 +465,18 @@ impl ExtBuilder { stakers = vec![ // (stash, ctrl, stake, status) // these two will be elected in the default test where we elect 2. - (11, 10, self.balance_factor * 1000, StakerStatus::::Validator), - (21, 20, self.balance_factor * 1000, StakerStatus::::Validator), + (11, 11, self.balance_factor * 1000, StakerStatus::::Validator), + (21, 21, self.balance_factor * 1000, StakerStatus::::Validator), // a loser validator - (31, 30, self.balance_factor * 500, StakerStatus::::Validator), + (31, 31, self.balance_factor * 500, StakerStatus::::Validator), // an idle validator - (41, 40, self.balance_factor * 1000, StakerStatus::::Idle), + (41, 41, self.balance_factor * 1000, StakerStatus::::Idle), ]; // optionally add a nominator if self.nominate { stakers.push(( 101, - 100, + 101, self.balance_factor * 500, StakerStatus::::Nominator(vec![11, 21]), )) @@ -563,35 +563,24 @@ pub(crate) fn current_era() -> EraIndex { Staking::current_era().unwrap() } -pub(crate) fn bond(stash: AccountId, ctrl: AccountId, val: Balance) { - let _ = Balances::make_free_balance_be(&stash, val); - let _ = Balances::make_free_balance_be(&ctrl, val); - assert_ok!(Staking::bond( - RuntimeOrigin::signed(stash), - ctrl, - val, - RewardDestination::Controller - )); +pub(crate) fn bond(who: AccountId, val: Balance) { + let _ = Balances::make_free_balance_be(&who, val); + assert_ok!(Staking::bond(RuntimeOrigin::signed(who), val, RewardDestination::Controller)); } -pub(crate) fn bond_validator(stash: AccountId, ctrl: AccountId, val: Balance) { - bond(stash, ctrl, val); - assert_ok!(Staking::validate(RuntimeOrigin::signed(ctrl), ValidatorPrefs::default())); +pub(crate) fn bond_validator(who: AccountId, val: Balance) { + bond(who, val); + assert_ok!(Staking::validate(RuntimeOrigin::signed(who), ValidatorPrefs::default())); assert_ok!(Session::set_keys( - RuntimeOrigin::signed(ctrl), - SessionKeys { other: ctrl.into() }, + RuntimeOrigin::signed(who), + SessionKeys { other: who.into() }, vec![] )); } -pub(crate) fn bond_nominator( - stash: AccountId, - ctrl: AccountId, - val: Balance, - target: Vec, -) { - bond(stash, ctrl, val); - assert_ok!(Staking::nominate(RuntimeOrigin::signed(ctrl), target)); +pub(crate) fn bond_nominator(who: AccountId, val: Balance, target: Vec) { + bond(who, val); + assert_ok!(Staking::nominate(RuntimeOrigin::signed(who), target)); } /// Progress to the given block, triggering session and era changes as we progress. diff --git a/frame/staking/src/pallet/impls.rs b/frame/staking/src/pallet/impls.rs index 984871f7f..1d9838572 100644 --- a/frame/staking/src/pallet/impls.rs +++ b/frame/staking/src/pallet/impls.rs @@ -1651,7 +1651,6 @@ impl StakingInterface for Pallet { ) -> DispatchResult { Self::bond( RawOrigin::Signed(who.clone()).into(), - T::Lookup::unlookup(who.clone()), value, RewardDestination::Account(payee.clone()), ) diff --git a/frame/staking/src/pallet/mod.rs b/frame/staking/src/pallet/mod.rs index d8f1855da..a725d4351 100644 --- a/frame/staking/src/pallet/mod.rs +++ b/frame/staking/src/pallet/mod.rs @@ -645,7 +645,6 @@ pub mod pallet { ); frame_support::assert_ok!(>::bond( T::RuntimeOrigin::from(Some(stash.clone()).into()), - T::Lookup::unlookup(controller.clone()), balance, RewardDestination::Staked, )); @@ -850,19 +849,17 @@ pub mod pallet { #[pallet::weight(T::WeightInfo::bond())] pub fn bond( origin: OriginFor, - controller: AccountIdLookupOf, #[pallet::compact] value: BalanceOf, payee: RewardDestination, ) -> DispatchResult { let stash = ensure_signed(origin)?; + let controller_to_be_deprecated = stash.clone(); if >::contains_key(&stash) { return Err(Error::::AlreadyBonded.into()) } - let controller = T::Lookup::lookup(controller)?; - - if >::contains_key(&controller) { + if >::contains_key(&controller_to_be_deprecated) { return Err(Error::::AlreadyPaired.into()) } @@ -875,7 +872,7 @@ pub mod pallet { // You're auto-bonded forever, here. We might improve this by only bonding when // you actually validate/nominate and remove once you unbond __everything__. - >::insert(&stash, &controller); + >::insert(&stash, &stash); >::insert(&stash, payee); let current_era = CurrentEra::::get().unwrap_or(0); @@ -886,7 +883,7 @@ pub mod pallet { let value = value.min(stash_balance); Self::deposit_event(Event::::Bonded { stash: stash.clone(), amount: value }); let item = StakingLedger { - stash, + stash: stash.clone(), total: value, active: value, unlocking: Default::default(), @@ -897,7 +894,7 @@ pub mod pallet { // satisfied. .defensive_map_err(|_| Error::::BoundNotMet)?, }; - Self::update_ledger(&controller, &item); + Self::update_ledger(&controller_to_be_deprecated, &item); Ok(()) } @@ -1237,7 +1234,10 @@ pub mod pallet { Ok(()) } - /// (Re-)set the controller of a stash. + /// (Re-)sets the controller of a stash to the stash itself. This function previously + /// accepted a `controller` argument to set the controller to an account other than the + /// stash itself. This functionality has now been removed, now only setting the controller + /// to the stash, if it is not already. /// /// Effects will be felt instantly (as soon as this function is completed successfully). /// @@ -1250,20 +1250,17 @@ pub mod pallet { /// - Writes are limited to the `origin` account key. #[pallet::call_index(8)] #[pallet::weight(T::WeightInfo::set_controller())] - pub fn set_controller( - origin: OriginFor, - controller: AccountIdLookupOf, - ) -> DispatchResult { + pub fn set_controller(origin: OriginFor) -> DispatchResult { let stash = ensure_signed(origin)?; let old_controller = Self::bonded(&stash).ok_or(Error::::NotStash)?; - let controller = T::Lookup::lookup(controller)?; - if >::contains_key(&controller) { + + if >::contains_key(&stash) { return Err(Error::::AlreadyPaired.into()) } - if controller != old_controller { - >::insert(&stash, &controller); + if old_controller != stash { + >::insert(&stash, &stash); if let Some(l) = >::take(&old_controller) { - >::insert(&controller, l); + >::insert(&stash, l); } } Ok(()) diff --git a/frame/staking/src/testing_utils.rs b/frame/staking/src/testing_utils.rs index 9bd231cce..28e08230d 100644 --- a/frame/staking/src/testing_utils.rs +++ b/frame/staking/src/testing_utils.rs @@ -75,17 +75,37 @@ pub fn create_stash_controller( n: u32, balance_factor: u32, destination: RewardDestination, +) -> Result<(T::AccountId, T::AccountId), &'static str> { + let staker = create_funded_user::("stash", n, balance_factor); + let amount = T::Currency::minimum_balance() * (balance_factor / 10).max(1).into(); + Staking::::bond(RawOrigin::Signed(staker.clone()).into(), amount, destination)?; + Ok((staker.clone(), staker)) +} + +/// Create a unique stash and controller pair. +pub fn create_unique_stash_controller( + n: u32, + balance_factor: u32, + destination: RewardDestination, + dead_controller: bool, ) -> Result<(T::AccountId, T::AccountId), &'static str> { let stash = create_funded_user::("stash", n, balance_factor); - let controller = create_funded_user::("controller", n, balance_factor); - let controller_lookup = T::Lookup::unlookup(controller.clone()); + + let controller = if dead_controller { + create_funded_user::("controller", n, 0) + } else { + create_funded_user::("controller", n, balance_factor) + }; let amount = T::Currency::minimum_balance() * (balance_factor / 10).max(1).into(); - Staking::::bond( - RawOrigin::Signed(stash.clone()).into(), - controller_lookup, - amount, - destination, - )?; + Staking::::bond(RawOrigin::Signed(stash.clone()).into(), amount, destination)?; + + // update ledger to be a *different* controller to stash + if let Some(l) = Ledger::::take(&stash) { + >::insert(&controller, l); + } + // update bonded account to be unique controller + >::insert(&stash, &controller); + Ok((stash, controller)) } @@ -95,38 +115,27 @@ pub fn create_stash_controller_with_balance( balance: crate::BalanceOf, destination: RewardDestination, ) -> Result<(T::AccountId, T::AccountId), &'static str> { - let stash = create_funded_user_with_balance::("stash", n, balance); - let controller = create_funded_user_with_balance::("controller", n, balance); - let controller_lookup = T::Lookup::unlookup(controller.clone()); - - Staking::::bond( - RawOrigin::Signed(stash.clone()).into(), - controller_lookup, - balance, - destination, - )?; - Ok((stash, controller)) + let staker = create_funded_user_with_balance::("stash", n, balance); + Staking::::bond(RawOrigin::Signed(staker.clone()).into(), balance, destination)?; + Ok((staker.clone(), staker)) } -/// Create a stash and controller pair, where the controller is dead, and payouts go to controller. -/// This is used to test worst case payout scenarios. -pub fn create_stash_and_dead_controller( +/// Create a stash and controller pair, where payouts go to a dead payee account. This is used to +/// test worst case payout scenarios. +pub fn create_stash_and_dead_payee( n: u32, balance_factor: u32, - destination: RewardDestination, ) -> Result<(T::AccountId, T::AccountId), &'static str> { - let stash = create_funded_user::("stash", n, balance_factor); - // controller has no funds - let controller = create_funded_user::("controller", n, 0); - let controller_lookup = T::Lookup::unlookup(controller.clone()); + let staker = create_funded_user::("stash", n, 0); + // payee has no funds + let payee = create_funded_user::("payee", n, 0); let amount = T::Currency::minimum_balance() * (balance_factor / 10).max(1).into(); Staking::::bond( - RawOrigin::Signed(stash.clone()).into(), - controller_lookup, + RawOrigin::Signed(staker.clone()).into(), amount, - destination, + RewardDestination::Account(payee), )?; - Ok((stash, controller)) + Ok((staker.clone(), staker)) } /// create `max` validators. diff --git a/frame/staking/src/tests.rs b/frame/staking/src/tests.rs index affee6002..e3ee4cd1a 100644 --- a/frame/staking/src/tests.rs +++ b/frame/staking/src/tests.rs @@ -92,8 +92,8 @@ fn set_staking_configs_works() { #[test] fn force_unstake_works() { ExtBuilder::default().build_and_execute(|| { - // Account 11 is stashed and locked, and account 10 is the controller - assert_eq!(Staking::bonded(&11), Some(10)); + // Account 11 (also controller) is stashed and locked + assert_eq!(Staking::bonded(&11), Some(11)); // Adds 2 slashing spans add_slash(&11); // Cant transfer @@ -120,8 +120,8 @@ fn force_unstake_works() { #[test] fn kill_stash_works() { ExtBuilder::default().build_and_execute(|| { - // Account 11 is stashed and locked, and account 10 is the controller - assert_eq!(Staking::bonded(&11), Some(10)); + // Account 11 (also controller) is stashed and locked + assert_eq!(Staking::bonded(&11), Some(11)); // Adds 2 slashing spans add_slash(&11); // Only can kill a stash account @@ -139,16 +139,16 @@ fn kill_stash_works() { fn basic_setup_works() { // Verifies initial conditions of mock ExtBuilder::default().build_and_execute(|| { - // Account 11 is stashed and locked, and account 10 is the controller - assert_eq!(Staking::bonded(&11), Some(10)); - // Account 21 is stashed and locked, and account 20 is the controller - assert_eq!(Staking::bonded(&21), Some(20)); + // Account 11 is stashed and locked, and is the controller + assert_eq!(Staking::bonded(&11), Some(11)); + // Account 21 is stashed and locked and is the controller + assert_eq!(Staking::bonded(&21), Some(21)); // Account 1 is not a stashed assert_eq!(Staking::bonded(&1), None); - // Account 10 controls the stash from account 11, which is 100 * balance_factor units + // Account 11 controls its own stash, which is 100 * balance_factor units assert_eq!( - Staking::ledger(&10).unwrap(), + Staking::ledger(&11).unwrap(), StakingLedger { stash: 11, total: 1000, @@ -157,9 +157,9 @@ fn basic_setup_works() { claimed_rewards: bounded_vec![], } ); - // Account 20 controls the stash from account 21, which is 200 * balance_factor units + // Account 21 controls its own stash, which is 200 * balance_factor units assert_eq!( - Staking::ledger(&20), + Staking::ledger(&21), Some(StakingLedger { stash: 21, total: 1000, @@ -182,7 +182,7 @@ fn basic_setup_works() { ); assert_eq!( - Staking::ledger(100), + Staking::ledger(101), Some(StakingLedger { stash: 101, total: 500, @@ -231,15 +231,49 @@ fn basic_setup_works() { #[test] fn change_controller_works() { ExtBuilder::default().build_and_execute(|| { - // 10 and 11 are bonded as stash controller. - assert_eq!(Staking::bonded(&11), Some(10)); + let (stash, controller) = testing_utils::create_unique_stash_controller::( + 0, + 100, + RewardDestination::Staked, + false, + ) + .unwrap(); + + // ensure `stash` and `controller` are bonded as stash controller pair. + assert_eq!(Staking::bonded(&stash), Some(controller)); + + // `controller` can control `stash` who is initially a validator. + assert_ok!(Staking::chill(RuntimeOrigin::signed(controller))); + + // sets controller back to `stash`. + assert_ok!(Staking::set_controller(RuntimeOrigin::signed(stash))); + assert_eq!(Staking::bonded(&stash), Some(stash)); + mock::start_active_era(1); + + // `controller` is no longer in control. `stash` is now controller. + assert_noop!( + Staking::validate(RuntimeOrigin::signed(controller), ValidatorPrefs::default()), + Error::::NotController, + ); + assert_ok!(Staking::validate(RuntimeOrigin::signed(stash), ValidatorPrefs::default())); + }) +} + +#[test] +fn change_controller_already_paired_once_stash() { + ExtBuilder::default().build_and_execute(|| { + // 10 and 11 are bonded as controller and stash respectively. + assert_eq!(Staking::bonded(&11), Some(11)); - // 10 can control 11 who is initially a validator. - assert_ok!(Staking::chill(RuntimeOrigin::signed(10))); + // 11 is initially a validator. + assert_ok!(Staking::chill(RuntimeOrigin::signed(11))); - // change controller - assert_ok!(Staking::set_controller(RuntimeOrigin::signed(11), 5)); - assert_eq!(Staking::bonded(&11), Some(5)); + // Controller cannot change once matching with stash. + assert_noop!( + Staking::set_controller(RuntimeOrigin::signed(11)), + Error::::AlreadyPaired + ); + assert_eq!(Staking::bonded(&11), Some(11)); mock::start_active_era(1); // 10 is no longer in control. @@ -247,18 +281,15 @@ fn change_controller_works() { Staking::validate(RuntimeOrigin::signed(10), ValidatorPrefs::default()), Error::::NotController, ); - assert_ok!(Staking::validate(RuntimeOrigin::signed(5), ValidatorPrefs::default())); + assert_ok!(Staking::validate(RuntimeOrigin::signed(11), ValidatorPrefs::default())); }) } #[test] fn rewards_should_work() { ExtBuilder::default().nominate(true).session_per_era(3).build_and_execute(|| { - let init_balance_10 = Balances::total_balance(&10); let init_balance_11 = Balances::total_balance(&11); - let init_balance_20 = Balances::total_balance(&20); let init_balance_21 = Balances::total_balance(&21); - let init_balance_100 = Balances::total_balance(&100); let init_balance_101 = Balances::total_balance(&101); // Set payees @@ -278,11 +309,8 @@ fn rewards_should_work() { start_session(1); assert_eq_uvec!(Session::validators(), vec![11, 21]); - assert_eq!(Balances::total_balance(&10), init_balance_10); assert_eq!(Balances::total_balance(&11), init_balance_11); - assert_eq!(Balances::total_balance(&20), init_balance_20); assert_eq!(Balances::total_balance(&21), init_balance_21); - assert_eq!(Balances::total_balance(&100), init_balance_100); assert_eq!(Balances::total_balance(&101), init_balance_101); assert_eq!( Staking::eras_reward_points(active_era()), @@ -291,10 +319,10 @@ fn rewards_should_work() { individual: vec![(11, 100), (21, 50)].into_iter().collect(), } ); - let part_for_10 = Perbill::from_rational::(1000, 1125); - let part_for_20 = Perbill::from_rational::(1000, 1375); - let part_for_100_from_10 = Perbill::from_rational::(125, 1125); - let part_for_100_from_20 = Perbill::from_rational::(375, 1375); + let part_for_11 = Perbill::from_rational::(1000, 1125); + let part_for_21 = Perbill::from_rational::(1000, 1375); + let part_for_101_from_11 = Perbill::from_rational::(125, 1125); + let part_for_101_from_21 = Perbill::from_rational::(375, 1375); start_session(2); start_session(3); @@ -312,25 +340,22 @@ fn rewards_should_work() { mock::make_all_reward_payment(0); assert_eq_error_rate!( - Balances::total_balance(&10), - init_balance_10 + part_for_10 * total_payout_0 * 2 / 3, + Balances::total_balance(&11), + init_balance_11 + part_for_11 * total_payout_0 * 2 / 3, 2, ); - assert_eq_error_rate!(Balances::total_balance(&11), init_balance_11, 2); assert_eq_error_rate!( - Balances::total_balance(&20), - init_balance_20 + part_for_20 * total_payout_0 * 1 / 3, + Balances::total_balance(&21), + init_balance_21 + part_for_21 * total_payout_0 * 1 / 3, 2, ); - assert_eq_error_rate!(Balances::total_balance(&21), init_balance_21, 2); assert_eq_error_rate!( - Balances::total_balance(&100), - init_balance_100 + - part_for_100_from_10 * total_payout_0 * 2 / 3 + - part_for_100_from_20 * total_payout_0 * 1 / 3, + Balances::total_balance(&101), + init_balance_101 + + part_for_101_from_11 * total_payout_0 * 2 / 3 + + part_for_101_from_21 * total_payout_0 * 1 / 3, 2 ); - assert_eq_error_rate!(Balances::total_balance(&101), init_balance_101, 2); assert_eq_uvec!(Session::validators(), vec![11, 21]); Pallet::::reward_by_ids(vec![(11, 1)]); @@ -354,25 +379,22 @@ fn rewards_should_work() { mock::make_all_reward_payment(1); assert_eq_error_rate!( - Balances::total_balance(&10), - init_balance_10 + part_for_10 * (total_payout_0 * 2 / 3 + total_payout_1), + Balances::total_balance(&11), + init_balance_11 + part_for_11 * (total_payout_0 * 2 / 3 + total_payout_1), 2, ); - assert_eq_error_rate!(Balances::total_balance(&11), init_balance_11, 2); assert_eq_error_rate!( - Balances::total_balance(&20), - init_balance_20 + part_for_20 * total_payout_0 * 1 / 3, + Balances::total_balance(&21), + init_balance_21 + part_for_21 * total_payout_0 * 1 / 3, 2, ); - assert_eq_error_rate!(Balances::total_balance(&21), init_balance_21, 2); assert_eq_error_rate!( - Balances::total_balance(&100), - init_balance_100 + - part_for_100_from_10 * (total_payout_0 * 2 / 3 + total_payout_1) + - part_for_100_from_20 * total_payout_0 * 1 / 3, + Balances::total_balance(&101), + init_balance_101 + + part_for_101_from_11 * (total_payout_0 * 2 / 3 + total_payout_1) + + part_for_101_from_21 * total_payout_0 * 1 / 3, 2 ); - assert_eq_error_rate!(Balances::total_balance(&101), init_balance_101, 2); }); } @@ -380,7 +402,7 @@ fn rewards_should_work() { fn staking_should_work() { ExtBuilder::default().nominate(false).build_and_execute(|| { // remember + compare this along with the test. - assert_eq_uvec!(validator_controllers(), vec![20, 10]); + assert_eq_uvec!(validator_controllers(), vec![21, 11]); // put some money in account that we'll use. for i in 1..5 { @@ -390,22 +412,22 @@ fn staking_should_work() { // --- Block 2: start_session(2); // add a new candidate for being a validator. account 3 controlled by 4. - assert_ok!(Staking::bond(RuntimeOrigin::signed(3), 4, 1500, RewardDestination::Controller)); - assert_ok!(Staking::validate(RuntimeOrigin::signed(4), ValidatorPrefs::default())); + assert_ok!(Staking::bond(RuntimeOrigin::signed(3), 1500, RewardDestination::Controller)); + assert_ok!(Staking::validate(RuntimeOrigin::signed(3), ValidatorPrefs::default())); assert_ok!(Session::set_keys( - RuntimeOrigin::signed(4), + RuntimeOrigin::signed(3), SessionKeys { other: 4.into() }, vec![] )); // No effects will be seen so far. - assert_eq_uvec!(validator_controllers(), vec![20, 10]); + assert_eq_uvec!(validator_controllers(), vec![21, 11]); // --- Block 3: start_session(3); // No effects will be seen so far. Era has not been yet triggered. - assert_eq_uvec!(validator_controllers(), vec![20, 10]); + assert_eq_uvec!(validator_controllers(), vec![21, 11]); // --- Block 4: the validators will now be queued. start_session(4); @@ -417,25 +439,25 @@ fn staking_should_work() { // --- Block 6: the validators will now be changed. start_session(6); - assert_eq_uvec!(validator_controllers(), vec![20, 4]); + assert_eq_uvec!(validator_controllers(), vec![21, 3]); // --- Block 6: Unstake 4 as a validator, freeing up the balance stashed in 3 // 4 will chill - Staking::chill(RuntimeOrigin::signed(4)).unwrap(); + Staking::chill(RuntimeOrigin::signed(3)).unwrap(); - // --- Block 7: nothing. 4 is still there. + // --- Block 7: nothing. 3 is still there. start_session(7); - assert_eq_uvec!(validator_controllers(), vec![20, 4]); + assert_eq_uvec!(validator_controllers(), vec![21, 3]); // --- Block 8: start_session(8); // --- Block 9: 4 will not be a validator. start_session(9); - assert_eq_uvec!(validator_controllers(), vec![20, 10]); + assert_eq_uvec!(validator_controllers(), vec![21, 11]); // Note: the stashed value of 4 is still lock assert_eq!( - Staking::ledger(&4), + Staking::ledger(&3), Some(StakingLedger { stash: 3, total: 1500, @@ -459,20 +481,20 @@ fn blocking_and_kicking_works() { .build_and_execute(|| { // block validator 10/11 assert_ok!(Staking::validate( - RuntimeOrigin::signed(10), + RuntimeOrigin::signed(11), ValidatorPrefs { blocked: true, ..Default::default() } )); // attempt to nominate from 100/101... - assert_ok!(Staking::nominate(RuntimeOrigin::signed(100), vec![11])); + assert_ok!(Staking::nominate(RuntimeOrigin::signed(101), vec![11])); // should have worked since we're already nominated them assert_eq!(Nominators::::get(&101).unwrap().targets, vec![11]); // kick the nominator - assert_ok!(Staking::kick(RuntimeOrigin::signed(10), vec![101])); + assert_ok!(Staking::kick(RuntimeOrigin::signed(11), vec![101])); // should have been kicked now assert!(Nominators::::get(&101).unwrap().targets.is_empty()); // attempt to nominate from 100/101... assert_noop!( - Staking::nominate(RuntimeOrigin::signed(100), vec![11]), + Staking::nominate(RuntimeOrigin::signed(101), vec![11]), Error::::BadTarget ); }); @@ -487,12 +509,12 @@ fn less_than_needed_candidates_works() { .build_and_execute(|| { assert_eq!(Staking::validator_count(), 4); assert_eq!(Staking::minimum_validator_count(), 1); - assert_eq_uvec!(validator_controllers(), vec![30, 20, 10]); + assert_eq_uvec!(validator_controllers(), vec![31, 21, 11]); mock::start_active_era(1); // Previous set is selected. NO election algorithm is even executed. - assert_eq_uvec!(validator_controllers(), vec![30, 20, 10]); + assert_eq_uvec!(validator_controllers(), vec![31, 21, 11]); // But the exposure is updated in a simple way. No external votes exists. // This is purely self-vote. @@ -510,21 +532,21 @@ fn no_candidate_emergency_condition() { .nominate(false) .build_and_execute(|| { // initial validators - assert_eq_uvec!(validator_controllers(), vec![10, 20, 30, 40]); + assert_eq_uvec!(validator_controllers(), vec![11, 21, 31, 41]); let prefs = ValidatorPrefs { commission: Perbill::one(), ..Default::default() }; Validators::::insert(11, prefs.clone()); // set the minimum validator count. - MinimumValidatorCount::::put(10); + MinimumValidatorCount::::put(11); // try to chill - let res = Staking::chill(RuntimeOrigin::signed(10)); + let res = Staking::chill(RuntimeOrigin::signed(11)); assert_ok!(res); let current_era = CurrentEra::::get(); // try trigger new era - mock::run_to_block(20); + mock::run_to_block(21); assert_eq!(*staking_events().last().unwrap(), Event::StakingElectionFailed); // No new era is created assert_eq!(current_era, CurrentEra::::get()); @@ -534,7 +556,7 @@ fn no_candidate_emergency_condition() { // Previous ones are elected. chill is not effective in active era (as era hasn't // changed) - assert_eq_uvec!(validator_controllers(), vec![10, 20, 30, 40]); + assert_eq_uvec!(validator_controllers(), vec![11, 21, 31, 41]); // The chill is still pending. assert!(!Validators::::contains_key(11)); // No new era is created. @@ -551,52 +573,50 @@ fn nominating_and_rewards_should_work() { .set_status(31, StakerStatus::Idle) .build_and_execute(|| { // initial validators. - assert_eq_uvec!(validator_controllers(), vec![40, 20]); + assert_eq_uvec!(validator_controllers(), vec![41, 21]); // re-validate with 11 and 31. - assert_ok!(Staking::validate(RuntimeOrigin::signed(10), Default::default())); - assert_ok!(Staking::validate(RuntimeOrigin::signed(30), Default::default())); + assert_ok!(Staking::validate(RuntimeOrigin::signed(11), Default::default())); + assert_ok!(Staking::validate(RuntimeOrigin::signed(31), Default::default())); // Set payee to controller. assert_ok!(Staking::set_payee( - RuntimeOrigin::signed(10), + RuntimeOrigin::signed(11), RewardDestination::Controller )); assert_ok!(Staking::set_payee( - RuntimeOrigin::signed(20), + RuntimeOrigin::signed(21), RewardDestination::Controller )); assert_ok!(Staking::set_payee( - RuntimeOrigin::signed(30), + RuntimeOrigin::signed(31), RewardDestination::Controller )); assert_ok!(Staking::set_payee( - RuntimeOrigin::signed(40), + RuntimeOrigin::signed(41), RewardDestination::Controller )); // give the man some money let initial_balance = 1000; - for i in [1, 2, 3, 4, 5, 10, 11, 20, 21].iter() { + for i in [1, 3, 5, 11, 21].iter() { let _ = Balances::make_free_balance_be(i, initial_balance); } // bond two account pairs and state interest in nomination. assert_ok!(Staking::bond( RuntimeOrigin::signed(1), - 2, 1000, RewardDestination::Controller )); - assert_ok!(Staking::nominate(RuntimeOrigin::signed(2), vec![11, 21, 31])); + assert_ok!(Staking::nominate(RuntimeOrigin::signed(1), vec![11, 21, 31])); assert_ok!(Staking::bond( RuntimeOrigin::signed(3), - 4, 1000, RewardDestination::Controller )); - assert_ok!(Staking::nominate(RuntimeOrigin::signed(4), vec![11, 21, 41])); + assert_ok!(Staking::nominate(RuntimeOrigin::signed(3), vec![11, 21, 41])); // the total reward for era 0 let total_payout_0 = current_total_payout_for_duration(reward_time_per_era()); @@ -606,15 +626,15 @@ fn nominating_and_rewards_should_work() { mock::start_active_era(1); // 10 and 20 have more votes, they will be chosen. - assert_eq_uvec!(validator_controllers(), vec![20, 10]); + assert_eq_uvec!(validator_controllers(), vec![21, 11]); // old validators must have already received some rewards. - let initial_balance_40 = Balances::total_balance(&40); - let mut initial_balance_20 = Balances::total_balance(&20); + let initial_balance_41 = Balances::total_balance(&41); + let mut initial_balance_21 = Balances::total_balance(&21); mock::make_all_reward_payment(0); - assert_eq!(Balances::total_balance(&40), initial_balance_40 + total_payout_0 / 2); - assert_eq!(Balances::total_balance(&20), initial_balance_20 + total_payout_0 / 2); - initial_balance_20 = Balances::total_balance(&20); + assert_eq!(Balances::total_balance(&41), initial_balance_41 + total_payout_0 / 2); + assert_eq!(Balances::total_balance(&21), initial_balance_21 + total_payout_0 / 2); + initial_balance_21 = Balances::total_balance(&21); assert_eq!(ErasStakers::::iter_prefix_values(active_era()).count(), 2); assert_eq!( @@ -651,34 +671,34 @@ fn nominating_and_rewards_should_work() { // nominators will also be paid. See below mock::make_all_reward_payment(1); - let payout_for_10 = total_payout_1 / 3; - let payout_for_20 = 2 * total_payout_1 / 3; - // Nominator 2: has [400/1800 ~ 2/9 from 10] + [600/2200 ~ 3/11 from 20]'s reward. ==> + let payout_for_11 = total_payout_1 / 3; + let payout_for_21 = 2 * total_payout_1 / 3; + // Nominator 2: has [400/1800 ~ 2/9 from 10] + [600/2200 ~ 3/11 from 21]'s reward. ==> // 2/9 + 3/11 assert_eq_error_rate!( - Balances::total_balance(&2), - initial_balance + (2 * payout_for_10 / 9 + 3 * payout_for_20 / 11), + Balances::total_balance(&1), + initial_balance + (2 * payout_for_11 / 9 + 3 * payout_for_21 / 11), 2, ); - // Nominator 4: has [400/1800 ~ 2/9 from 10] + [600/2200 ~ 3/11 from 20]'s reward. ==> + // Nominator 3: has [400/1800 ~ 2/9 from 10] + [600/2200 ~ 3/11 from 21]'s reward. ==> // 2/9 + 3/11 assert_eq_error_rate!( - Balances::total_balance(&4), - initial_balance + (2 * payout_for_10 / 9 + 3 * payout_for_20 / 11), + Balances::total_balance(&3), + initial_balance + (2 * payout_for_11 / 9 + 3 * payout_for_21 / 11), 2, ); - // Validator 10: got 800 / 1800 external stake => 8/18 =? 4/9 => Validator's share = 5/9 + // Validator 11: got 800 / 1800 external stake => 8/18 =? 4/9 => Validator's share = 5/9 assert_eq_error_rate!( - Balances::total_balance(&10), - initial_balance + 5 * payout_for_10 / 9, + Balances::total_balance(&11), + initial_balance + 5 * payout_for_11 / 9, 2, ); - // Validator 20: got 1200 / 2200 external stake => 12/22 =? 6/11 => Validator's share = + // Validator 21: got 1200 / 2200 external stake => 12/22 =? 6/11 => Validator's share = // 5/11 assert_eq_error_rate!( - Balances::total_balance(&20), - initial_balance_20 + 5 * payout_for_20 / 11, + Balances::total_balance(&21), + initial_balance_21 + 5 * payout_for_21 / 11, 2, ); }); @@ -694,9 +714,9 @@ fn nominators_also_get_slashed_pro_rata() { assert_eq!(initial_exposure.others.first().unwrap().who, 101); // staked values; - let nominator_stake = Staking::ledger(100).unwrap().active; + let nominator_stake = Staking::ledger(101).unwrap().active; let nominator_balance = balances(&101).0; - let validator_stake = Staking::ledger(10).unwrap().active; + let validator_stake = Staking::ledger(11).unwrap().active; let validator_balance = balances(&11).0; let exposed_stake = initial_exposure.total; let exposed_validator = initial_exposure.own; @@ -709,8 +729,8 @@ fn nominators_also_get_slashed_pro_rata() { ); // both stakes must have been decreased. - assert!(Staking::ledger(100).unwrap().active < nominator_stake); - assert!(Staking::ledger(10).unwrap().active < validator_stake); + assert!(Staking::ledger(101).unwrap().active < nominator_stake); + assert!(Staking::ledger(11).unwrap().active < validator_stake); let slash_amount = slash_percent * exposed_stake; let validator_share = @@ -723,8 +743,8 @@ fn nominators_also_get_slashed_pro_rata() { assert!(nominator_share > 0); // both stakes must have been decreased pro-rata. - assert_eq!(Staking::ledger(100).unwrap().active, nominator_stake - nominator_share); - assert_eq!(Staking::ledger(10).unwrap().active, validator_stake - validator_share); + assert_eq!(Staking::ledger(101).unwrap().active, nominator_stake - nominator_share); + assert_eq!(Staking::ledger(11).unwrap().active, validator_stake - validator_share); assert_eq!( balances(&101).0, // free balance nominator_balance - nominator_share, @@ -734,7 +754,7 @@ fn nominators_also_get_slashed_pro_rata() { validator_balance - validator_share, ); // Because slashing happened. - assert!(is_disabled(10)); + assert!(is_disabled(11)); }); } @@ -746,56 +766,56 @@ fn double_staking_should_fail() { // * an account already bonded as controller can nominate. ExtBuilder::default().build_and_execute(|| { let arbitrary_value = 5; - // 2 = controller, 1 stashed => ok - assert_ok!(Staking::bond( - RuntimeOrigin::signed(1), - 2, + let (stash, controller) = testing_utils::create_unique_stash_controller::( + 0, arbitrary_value, - RewardDestination::default() - )); - // 4 = not used so far, 1 stashed => not allowed. + RewardDestination::default(), + false, + ) + .unwrap(); + + // 4 = not used so far, stash => not allowed. assert_noop!( Staking::bond( - RuntimeOrigin::signed(1), - 4, - arbitrary_value, + RuntimeOrigin::signed(stash), + arbitrary_value.into(), RewardDestination::default() ), Error::::AlreadyBonded, ); - // 1 = stashed => attempting to nominate should fail. + // stash => attempting to nominate should fail. assert_noop!( - Staking::nominate(RuntimeOrigin::signed(1), vec![1]), + Staking::nominate(RuntimeOrigin::signed(stash), vec![1]), Error::::NotController ); - // 2 = controller => nominating should work. - assert_ok!(Staking::nominate(RuntimeOrigin::signed(2), vec![1])); + // controller => nominating should work. + assert_ok!(Staking::nominate(RuntimeOrigin::signed(controller), vec![1])); }); } #[test] -fn double_controlling_should_fail() { +fn double_controlling_attempt_should_fail() { // should test (in the same order): // * an account already bonded as controller CANNOT be reused as the controller of another // account. ExtBuilder::default().build_and_execute(|| { let arbitrary_value = 5; - // 2 = controller, 1 stashed => ok - assert_ok!(Staking::bond( - RuntimeOrigin::signed(1), - 2, + let (stash, _) = testing_utils::create_unique_stash_controller::( + 0, arbitrary_value, RewardDestination::default(), - )); - // 2 = controller, 3 stashed (Note that 2 is reused.) => no-op + false, + ) + .unwrap(); + + // Note that controller (same as stash) is reused => no-op. assert_noop!( Staking::bond( - RuntimeOrigin::signed(3), - 2, - arbitrary_value, + RuntimeOrigin::signed(stash), + arbitrary_value.into(), RewardDestination::default() ), - Error::::AlreadyPaired, + Error::::AlreadyBonded, ); }); } @@ -953,21 +973,21 @@ fn cannot_transfer_staked_balance() { // Tests that a stash account cannot transfer funds ExtBuilder::default().nominate(false).build_and_execute(|| { // Confirm account 11 is stashed - assert_eq!(Staking::bonded(&11), Some(10)); + assert_eq!(Staking::bonded(&11), Some(11)); // Confirm account 11 has some free balance assert_eq!(Balances::free_balance(11), 1000); - // Confirm account 11 (via controller 10) is totally staked + // Confirm account 11 (via controller) is totally staked assert_eq!(Staking::eras_stakers(active_era(), 11).total, 1000); // Confirm account 11 cannot transfer as a result assert_noop!( - Balances::transfer_allow_death(RuntimeOrigin::signed(11), 20, 1), + Balances::transfer_allow_death(RuntimeOrigin::signed(11), 21, 1), TokenError::Frozen, ); // Give account 11 extra free balance let _ = Balances::make_free_balance_be(&11, 10000); // Confirm that account 11 can now transfer some balance - assert_ok!(Balances::transfer_allow_death(RuntimeOrigin::signed(11), 20, 1)); + assert_ok!(Balances::transfer_allow_death(RuntimeOrigin::signed(11), 21, 1)); }); } @@ -978,17 +998,17 @@ fn cannot_transfer_staked_balance_2() { // 21 has 2000 free balance but 1000 at stake ExtBuilder::default().nominate(false).build_and_execute(|| { // Confirm account 21 is stashed - assert_eq!(Staking::bonded(&21), Some(20)); + assert_eq!(Staking::bonded(&21), Some(21)); // Confirm account 21 has some free balance assert_eq!(Balances::free_balance(21), 2000); - // Confirm account 21 (via controller 20) is totally staked + // Confirm account 21 (via controller) is totally staked assert_eq!(Staking::eras_stakers(active_era(), 21).total, 1000); // Confirm account 21 can transfer at most 1000 assert_noop!( - Balances::transfer_allow_death(RuntimeOrigin::signed(21), 20, 1001), + Balances::transfer_allow_death(RuntimeOrigin::signed(21), 21, 1001), TokenError::Frozen, ); - assert_ok!(Balances::transfer_allow_death(RuntimeOrigin::signed(21), 20, 1000)); + assert_ok!(Balances::transfer_allow_death(RuntimeOrigin::signed(21), 21, 1000)); }); } @@ -997,7 +1017,7 @@ fn cannot_reserve_staked_balance() { // Checks that a bonded account cannot reserve balance from free balance ExtBuilder::default().build_and_execute(|| { // Confirm account 11 is stashed - assert_eq!(Staking::bonded(&11), Some(10)); + assert_eq!(Staking::bonded(&11), Some(11)); // Confirm account 11 has some free balance assert_eq!(Balances::free_balance(11), 1000); // Confirm account 11 (via controller 10) is totally staked @@ -1024,7 +1044,7 @@ fn reward_destination_works() { assert_eq!(Balances::free_balance(11), 1000); // Check how much is at stake assert_eq!( - Staking::ledger(&10), + Staking::ledger(&11), Some(StakingLedger { stash: 11, total: 1000, @@ -1047,7 +1067,7 @@ fn reward_destination_works() { assert_eq!(Balances::free_balance(11), 1000 + total_payout_0); // Check that amount at stake increased accordingly assert_eq!( - Staking::ledger(&10), + Staking::ledger(&11), Some(StakingLedger { stash: 11, total: 1000 + total_payout_0, @@ -1071,11 +1091,9 @@ fn reward_destination_works() { assert_eq!(Staking::payee(&11), RewardDestination::Stash); // Check that reward went to the stash account assert_eq!(Balances::free_balance(11), 1000 + total_payout_0 + total_payout_1); - // Record this value - let recorded_stash_balance = 1000 + total_payout_0 + total_payout_1; // Check that amount at stake is NOT increased assert_eq!( - Staking::ledger(&10), + Staking::ledger(&11), Some(StakingLedger { stash: 11, total: 1000 + total_payout_0, @@ -1089,7 +1107,7 @@ fn reward_destination_works() { >::insert(&11, RewardDestination::Controller); // Check controller balance - assert_eq!(Balances::free_balance(10), 1); + assert_eq!(Balances::free_balance(11), 23150); // Compute total payout now for whole duration as other parameter won't change let total_payout_2 = current_total_payout_for_duration(reward_time_per_era()); @@ -1101,10 +1119,10 @@ fn reward_destination_works() { // Check that RewardDestination is Controller assert_eq!(Staking::payee(&11), RewardDestination::Controller); // Check that reward went to the controller account - assert_eq!(Balances::free_balance(10), 1 + total_payout_2); + assert_eq!(Balances::free_balance(11), 23150 + total_payout_2); // Check that amount at stake is NOT increased assert_eq!( - Staking::ledger(&10), + Staking::ledger(&11), Some(StakingLedger { stash: 11, total: 1000 + total_payout_0, @@ -1113,8 +1131,6 @@ fn reward_destination_works() { claimed_rewards: bounded_vec![0, 1, 2], }) ); - // Check that amount in staked account is NOT increased. - assert_eq!(Balances::free_balance(11), recorded_stash_balance); }); } @@ -1134,8 +1150,8 @@ fn validator_payment_prefs_work() { mock::start_active_era(1); mock::make_all_reward_payment(0); - let balance_era_1_10 = Balances::total_balance(&10); - let balance_era_1_100 = Balances::total_balance(&100); + let balance_era_1_11 = Balances::total_balance(&11); + let balance_era_1_101 = Balances::total_balance(&101); // Compute total payout now for whole duration as other parameter won't change let total_payout_1 = current_total_payout_for_duration(reward_time_per_era()); @@ -1149,8 +1165,8 @@ fn validator_payment_prefs_work() { let shared_cut = total_payout_1 - taken_cut; let reward_of_10 = shared_cut * exposure_1.own / exposure_1.total + taken_cut; let reward_of_100 = shared_cut * exposure_1.others[0].value / exposure_1.total; - assert_eq_error_rate!(Balances::total_balance(&10), balance_era_1_10 + reward_of_10, 2); - assert_eq_error_rate!(Balances::total_balance(&100), balance_era_1_100 + reward_of_100, 2); + assert_eq_error_rate!(Balances::total_balance(&11), balance_era_1_11 + reward_of_10, 2); + assert_eq_error_rate!(Balances::total_balance(&101), balance_era_1_101 + reward_of_100, 2); }); } @@ -1163,10 +1179,10 @@ fn bond_extra_works() { // Check that account 10 is a validator assert!(>::contains_key(11)); // Check that account 10 is bonded to account 11 - assert_eq!(Staking::bonded(&11), Some(10)); + assert_eq!(Staking::bonded(&11), Some(11)); // Check how much is at stake assert_eq!( - Staking::ledger(&10), + Staking::ledger(&11), Some(StakingLedger { stash: 11, total: 1000, @@ -1183,7 +1199,7 @@ fn bond_extra_works() { assert_ok!(Staking::bond_extra(RuntimeOrigin::signed(11), 100)); // There should be 100 more `total` and `active` in the ledger assert_eq!( - Staking::ledger(&10), + Staking::ledger(&11), Some(StakingLedger { stash: 11, total: 1000 + 100, @@ -1197,7 +1213,7 @@ fn bond_extra_works() { assert_ok!(Staking::bond_extra(RuntimeOrigin::signed(11), Balance::max_value())); // The full amount of the funds should now be in the total and active assert_eq!( - Staking::ledger(&10), + Staking::ledger(&11), Some(StakingLedger { stash: 11, total: 1000000, @@ -1219,7 +1235,7 @@ fn bond_extra_and_withdraw_unbonded_works() { // * Once the unbonding period is done, it can actually take the funds out of the stash. ExtBuilder::default().nominate(false).build_and_execute(|| { // Set payee to controller. avoids confusion - assert_ok!(Staking::set_payee(RuntimeOrigin::signed(10), RewardDestination::Controller)); + assert_ok!(Staking::set_payee(RuntimeOrigin::signed(11), RewardDestination::Controller)); // Give account 11 some large free balance greater than total let _ = Balances::make_free_balance_be(&11, 1000000); @@ -1228,14 +1244,14 @@ fn bond_extra_and_withdraw_unbonded_works() { assert_eq!(active_era(), 0); // check the balance of a validator accounts. - assert_eq!(Balances::total_balance(&10), 1); + assert_eq!(Balances::total_balance(&11), 1000000); // confirm that 10 is a normal validator and gets paid at the end of the era. mock::start_active_era(1); - // Initial state of 10 + // Initial state of 11 assert_eq!( - Staking::ledger(&10), + Staking::ledger(&11), Some(StakingLedger { stash: 11, total: 1000, @@ -1253,7 +1269,7 @@ fn bond_extra_and_withdraw_unbonded_works() { Staking::bond_extra(RuntimeOrigin::signed(11), 100).unwrap(); assert_eq!( - Staking::ledger(&10), + Staking::ledger(&11), Some(StakingLedger { stash: 11, total: 1000 + 100, @@ -1274,7 +1290,7 @@ fn bond_extra_and_withdraw_unbonded_works() { // ledger should be the same. assert_eq!( - Staking::ledger(&10), + Staking::ledger(&11), Some(StakingLedger { stash: 11, total: 1000 + 100, @@ -1290,9 +1306,9 @@ fn bond_extra_and_withdraw_unbonded_works() { ); // Unbond almost all of the funds in stash. - Staking::unbond(RuntimeOrigin::signed(10), 1000).unwrap(); + Staking::unbond(RuntimeOrigin::signed(11), 1000).unwrap(); assert_eq!( - Staking::ledger(&10), + Staking::ledger(&11), Some(StakingLedger { stash: 11, total: 1000 + 100, @@ -1303,9 +1319,9 @@ fn bond_extra_and_withdraw_unbonded_works() { ); // Attempting to free the balances now will fail. 2 eras need to pass. - assert_ok!(Staking::withdraw_unbonded(RuntimeOrigin::signed(10), 0)); + assert_ok!(Staking::withdraw_unbonded(RuntimeOrigin::signed(11), 0)); assert_eq!( - Staking::ledger(&10), + Staking::ledger(&11), Some(StakingLedger { stash: 11, total: 1000 + 100, @@ -1319,9 +1335,9 @@ fn bond_extra_and_withdraw_unbonded_works() { mock::start_active_era(3); // nothing yet - assert_ok!(Staking::withdraw_unbonded(RuntimeOrigin::signed(10), 0)); + assert_ok!(Staking::withdraw_unbonded(RuntimeOrigin::signed(11), 0)); assert_eq!( - Staking::ledger(&10), + Staking::ledger(&11), Some(StakingLedger { stash: 11, total: 1000 + 100, @@ -1334,10 +1350,10 @@ fn bond_extra_and_withdraw_unbonded_works() { // trigger next era. mock::start_active_era(5); - assert_ok!(Staking::withdraw_unbonded(RuntimeOrigin::signed(10), 0)); + assert_ok!(Staking::withdraw_unbonded(RuntimeOrigin::signed(11), 0)); // Now the value is free and the staking ledger is updated. assert_eq!( - Staking::ledger(&10), + Staking::ledger(&11), Some(StakingLedger { stash: 11, total: 100, @@ -1361,7 +1377,7 @@ fn many_unbond_calls_should_work() { // There is only 1 chunk per era, so we need to be in a new era to create a chunk. current_era = i as u32; mock::start_active_era(current_era); - assert_ok!(Staking::unbond(RuntimeOrigin::signed(10), 1)); + assert_ok!(Staking::unbond(RuntimeOrigin::signed(11), 1)); } current_era += 1; @@ -1369,9 +1385,9 @@ fn many_unbond_calls_should_work() { // This chunk is locked at `current_era` through `current_era + 2` (because // `BondingDuration` == 3). - assert_ok!(Staking::unbond(RuntimeOrigin::signed(10), 1)); + assert_ok!(Staking::unbond(RuntimeOrigin::signed(11), 1)); assert_eq!( - Staking::ledger(&10).map(|l| l.unlocking.len()).unwrap(), + Staking::ledger(&11).map(|l| l.unlocking.len()).unwrap(), <::MaxUnlockingChunks as Get>::get() as usize ); @@ -1381,12 +1397,12 @@ fn many_unbond_calls_should_work() { // There is only 1 chunk per era, so we need to be in a new era to create a chunk. current_era = i as u32; mock::start_active_era(current_era); - assert_ok!(Staking::unbond(RuntimeOrigin::signed(10), 1)); + assert_ok!(Staking::unbond(RuntimeOrigin::signed(11), 1)); } // only slots within last `BondingDuration` are filled. assert_eq!( - Staking::ledger(&10).map(|l| l.unlocking.len()).unwrap(), + Staking::ledger(&11).map(|l| l.unlocking.len()).unwrap(), <::BondingDuration>::get() as usize ); }) @@ -1404,7 +1420,7 @@ fn auto_withdraw_may_not_unlock_all_chunks() { // fills the chunking slots for account mock::start_active_era(current_era); - assert_ok!(Staking::unbond(RuntimeOrigin::signed(10), 1)); + assert_ok!(Staking::unbond(RuntimeOrigin::signed(11), 1)); current_era += 1; mock::start_active_era(current_era); @@ -1412,12 +1428,12 @@ fn auto_withdraw_may_not_unlock_all_chunks() { // unbonding will fail because i) there are no remaining chunks and ii) no filled chunks // can be released because current chunk hasn't stay in the queue for at least // `BondingDuration` - assert_noop!(Staking::unbond(RuntimeOrigin::signed(10), 1), Error::::NoMoreChunks); + assert_noop!(Staking::unbond(RuntimeOrigin::signed(11), 1), Error::::NoMoreChunks); // fast-forward a few eras for unbond to be successful with implicit withdraw current_era += 10; mock::start_active_era(current_era); - assert_ok!(Staking::unbond(RuntimeOrigin::signed(10), 1)); + assert_ok!(Staking::unbond(RuntimeOrigin::signed(11), 1)); }) } @@ -1430,7 +1446,7 @@ fn rebond_works() { // * it can re-bond a portion of the funds scheduled to unlock. ExtBuilder::default().nominate(false).build_and_execute(|| { // Set payee to controller. avoids confusion - assert_ok!(Staking::set_payee(RuntimeOrigin::signed(10), RewardDestination::Controller)); + assert_ok!(Staking::set_payee(RuntimeOrigin::signed(11), RewardDestination::Controller)); // Give account 11 some large free balance greater than total let _ = Balances::make_free_balance_be(&11, 1000000); @@ -1438,9 +1454,9 @@ fn rebond_works() { // confirm that 10 is a normal validator and gets paid at the end of the era. mock::start_active_era(1); - // Initial state of 10 + // Initial state of 11 assert_eq!( - Staking::ledger(&10), + Staking::ledger(&11), Some(StakingLedger { stash: 11, total: 1000, @@ -1454,12 +1470,12 @@ fn rebond_works() { assert_eq!(active_era(), 2); // Try to rebond some funds. We get an error since no fund is unbonded. - assert_noop!(Staking::rebond(RuntimeOrigin::signed(10), 500), Error::::NoUnlockChunk); + assert_noop!(Staking::rebond(RuntimeOrigin::signed(11), 500), Error::::NoUnlockChunk); // Unbond almost all of the funds in stash. - Staking::unbond(RuntimeOrigin::signed(10), 900).unwrap(); + Staking::unbond(RuntimeOrigin::signed(11), 900).unwrap(); assert_eq!( - Staking::ledger(&10), + Staking::ledger(&11), Some(StakingLedger { stash: 11, total: 1000, @@ -1470,9 +1486,9 @@ fn rebond_works() { ); // Re-bond all the funds unbonded. - Staking::rebond(RuntimeOrigin::signed(10), 900).unwrap(); + Staking::rebond(RuntimeOrigin::signed(11), 900).unwrap(); assert_eq!( - Staking::ledger(&10), + Staking::ledger(&11), Some(StakingLedger { stash: 11, total: 1000, @@ -1483,9 +1499,9 @@ fn rebond_works() { ); // Unbond almost all of the funds in stash. - Staking::unbond(RuntimeOrigin::signed(10), 900).unwrap(); + Staking::unbond(RuntimeOrigin::signed(11), 900).unwrap(); assert_eq!( - Staking::ledger(&10), + Staking::ledger(&11), Some(StakingLedger { stash: 11, total: 1000, @@ -1496,9 +1512,9 @@ fn rebond_works() { ); // Re-bond part of the funds unbonded. - Staking::rebond(RuntimeOrigin::signed(10), 500).unwrap(); + Staking::rebond(RuntimeOrigin::signed(11), 500).unwrap(); assert_eq!( - Staking::ledger(&10), + Staking::ledger(&11), Some(StakingLedger { stash: 11, total: 1000, @@ -1509,9 +1525,9 @@ fn rebond_works() { ); // Re-bond the remainder of the funds unbonded. - Staking::rebond(RuntimeOrigin::signed(10), 500).unwrap(); + Staking::rebond(RuntimeOrigin::signed(11), 500).unwrap(); assert_eq!( - Staking::ledger(&10), + Staking::ledger(&11), Some(StakingLedger { stash: 11, total: 1000, @@ -1522,11 +1538,11 @@ fn rebond_works() { ); // Unbond parts of the funds in stash. - Staking::unbond(RuntimeOrigin::signed(10), 300).unwrap(); - Staking::unbond(RuntimeOrigin::signed(10), 300).unwrap(); - Staking::unbond(RuntimeOrigin::signed(10), 300).unwrap(); + Staking::unbond(RuntimeOrigin::signed(11), 300).unwrap(); + Staking::unbond(RuntimeOrigin::signed(11), 300).unwrap(); + Staking::unbond(RuntimeOrigin::signed(11), 300).unwrap(); assert_eq!( - Staking::ledger(&10), + Staking::ledger(&11), Some(StakingLedger { stash: 11, total: 1000, @@ -1537,9 +1553,9 @@ fn rebond_works() { ); // Re-bond part of the funds unbonded. - Staking::rebond(RuntimeOrigin::signed(10), 500).unwrap(); + Staking::rebond(RuntimeOrigin::signed(11), 500).unwrap(); assert_eq!( - Staking::ledger(&10), + Staking::ledger(&11), Some(StakingLedger { stash: 11, total: 1000, @@ -1556,7 +1572,7 @@ fn rebond_is_fifo() { // Rebond should proceed by reversing the most recent bond operations. ExtBuilder::default().nominate(false).build_and_execute(|| { // Set payee to controller. avoids confusion - assert_ok!(Staking::set_payee(RuntimeOrigin::signed(10), RewardDestination::Controller)); + assert_ok!(Staking::set_payee(RuntimeOrigin::signed(11), RewardDestination::Controller)); // Give account 11 some large free balance greater than total let _ = Balances::make_free_balance_be(&11, 1000000); @@ -1566,7 +1582,7 @@ fn rebond_is_fifo() { // Initial state of 10 assert_eq!( - Staking::ledger(&10), + Staking::ledger(&11), Some(StakingLedger { stash: 11, total: 1000, @@ -1579,9 +1595,9 @@ fn rebond_is_fifo() { mock::start_active_era(2); // Unbond some of the funds in stash. - Staking::unbond(RuntimeOrigin::signed(10), 400).unwrap(); + Staking::unbond(RuntimeOrigin::signed(11), 400).unwrap(); assert_eq!( - Staking::ledger(&10), + Staking::ledger(&11), Some(StakingLedger { stash: 11, total: 1000, @@ -1594,9 +1610,9 @@ fn rebond_is_fifo() { mock::start_active_era(3); // Unbond more of the funds in stash. - Staking::unbond(RuntimeOrigin::signed(10), 300).unwrap(); + Staking::unbond(RuntimeOrigin::signed(11), 300).unwrap(); assert_eq!( - Staking::ledger(&10), + Staking::ledger(&11), Some(StakingLedger { stash: 11, total: 1000, @@ -1612,9 +1628,9 @@ fn rebond_is_fifo() { mock::start_active_era(4); // Unbond yet more of the funds in stash. - Staking::unbond(RuntimeOrigin::signed(10), 200).unwrap(); + Staking::unbond(RuntimeOrigin::signed(11), 200).unwrap(); assert_eq!( - Staking::ledger(&10), + Staking::ledger(&11), Some(StakingLedger { stash: 11, total: 1000, @@ -1629,9 +1645,9 @@ fn rebond_is_fifo() { ); // Re-bond half of the unbonding funds. - Staking::rebond(RuntimeOrigin::signed(10), 400).unwrap(); + Staking::rebond(RuntimeOrigin::signed(11), 400).unwrap(); assert_eq!( - Staking::ledger(&10), + Staking::ledger(&11), Some(StakingLedger { stash: 11, total: 1000, @@ -1652,7 +1668,7 @@ fn rebond_emits_right_value_in_event() { // and the rebond event emits the actual value rebonded. ExtBuilder::default().nominate(false).build_and_execute(|| { // Set payee to controller. avoids confusion - assert_ok!(Staking::set_payee(RuntimeOrigin::signed(10), RewardDestination::Controller)); + assert_ok!(Staking::set_payee(RuntimeOrigin::signed(11), RewardDestination::Controller)); // Give account 11 some large free balance greater than total let _ = Balances::make_free_balance_be(&11, 1000000); @@ -1661,9 +1677,9 @@ fn rebond_emits_right_value_in_event() { mock::start_active_era(1); // Unbond almost all of the funds in stash. - Staking::unbond(RuntimeOrigin::signed(10), 900).unwrap(); + Staking::unbond(RuntimeOrigin::signed(11), 900).unwrap(); assert_eq!( - Staking::ledger(&10), + Staking::ledger(&11), Some(StakingLedger { stash: 11, total: 1000, @@ -1674,9 +1690,9 @@ fn rebond_emits_right_value_in_event() { ); // Re-bond less than the total - Staking::rebond(RuntimeOrigin::signed(10), 100).unwrap(); + Staking::rebond(RuntimeOrigin::signed(11), 100).unwrap(); assert_eq!( - Staking::ledger(&10), + Staking::ledger(&11), Some(StakingLedger { stash: 11, total: 1000, @@ -1689,9 +1705,9 @@ fn rebond_emits_right_value_in_event() { assert_eq!(*staking_events().last().unwrap(), Event::Bonded { stash: 11, amount: 100 }); // Re-bond way more than available - Staking::rebond(RuntimeOrigin::signed(10), 100_000).unwrap(); + Staking::rebond(RuntimeOrigin::signed(11), 100_000).unwrap(); assert_eq!( - Staking::ledger(&10), + Staking::ledger(&11), Some(StakingLedger { stash: 11, total: 1000, @@ -1747,17 +1763,20 @@ fn reward_to_stake_works() { mock::make_all_reward_payment(0); assert_eq!(Staking::eras_stakers(active_era(), 11).total, 1000); - assert_eq!(Staking::eras_stakers(active_era(), 21).total, 69); + assert_eq!(Staking::eras_stakers(active_era(), 21).total, 2000); let _11_balance = Balances::free_balance(&11); + let _21_balance = Balances::free_balance(&21); + assert_eq!(_11_balance, 1000 + total_payout_0 / 2); + assert_eq!(_21_balance, 2000 + total_payout_0 / 2); // Trigger another new era as the info are frozen before the era start. mock::start_active_era(2); // -- new infos - assert_eq!(Staking::eras_stakers(active_era(), 11).total, 1000 + total_payout_0 / 2); - assert_eq!(Staking::eras_stakers(active_era(), 21).total, 69 + total_payout_0 / 2); + assert_eq!(Staking::eras_stakers(active_era(), 11).total, _11_balance); + assert_eq!(Staking::eras_stakers(active_era(), 21).total, _21_balance); }); } @@ -1768,11 +1787,10 @@ fn reap_stash_works() { .balance_factor(10) .build_and_execute(|| { // given - assert_eq!(Balances::free_balance(10), 10); assert_eq!(Balances::free_balance(11), 10 * 1000); - assert_eq!(Staking::bonded(&11), Some(10)); + assert_eq!(Staking::bonded(&11), Some(11)); - assert!(>::contains_key(&10)); + assert!(>::contains_key(&11)); assert!(>::contains_key(&11)); assert!(>::contains_key(&11)); assert!(>::contains_key(&11)); @@ -1782,16 +1800,11 @@ fn reap_stash_works() { Staking::reap_stash(RuntimeOrigin::signed(20), 11, 0), Error::::FundedTarget ); - // controller or any other account is not reapable - assert_noop!( - Staking::reap_stash(RuntimeOrigin::signed(20), 10, 0), - Error::::NotStash - ); // no easy way to cause an account to go below ED, we tweak their staking ledger // instead. Ledger::::insert( - 10, + 11, StakingLedger { stash: 11, total: 5, @@ -1805,7 +1818,7 @@ fn reap_stash_works() { assert_ok!(Staking::reap_stash(RuntimeOrigin::signed(20), 11, 0)); // then - assert!(!>::contains_key(&10)); + assert!(!>::contains_key(&11)); assert!(!>::contains_key(&11)); assert!(!>::contains_key(&11)); assert!(!>::contains_key(&11)); @@ -1818,14 +1831,14 @@ fn switching_roles() { // minimal overhead. ExtBuilder::default().nominate(false).build_and_execute(|| { // Reset reward destination - for i in &[10, 20] { + for i in &[11, 21] { assert_ok!(Staking::set_payee( RuntimeOrigin::signed(*i), RewardDestination::Controller )); } - assert_eq_uvec!(validator_controllers(), vec![20, 10]); + assert_eq_uvec!(validator_controllers(), vec![21, 11]); // put some money in account that we'll use. for i in 1..7 { @@ -1833,43 +1846,43 @@ fn switching_roles() { } // add 2 nominators - assert_ok!(Staking::bond(RuntimeOrigin::signed(1), 2, 2000, RewardDestination::Controller)); - assert_ok!(Staking::nominate(RuntimeOrigin::signed(2), vec![11, 5])); + assert_ok!(Staking::bond(RuntimeOrigin::signed(1), 2000, RewardDestination::Controller)); + assert_ok!(Staking::nominate(RuntimeOrigin::signed(1), vec![11, 5])); - assert_ok!(Staking::bond(RuntimeOrigin::signed(3), 4, 500, RewardDestination::Controller)); - assert_ok!(Staking::nominate(RuntimeOrigin::signed(4), vec![21, 1])); + assert_ok!(Staking::bond(RuntimeOrigin::signed(3), 500, RewardDestination::Controller)); + assert_ok!(Staking::nominate(RuntimeOrigin::signed(3), vec![21, 1])); // add a new validator candidate - assert_ok!(Staking::bond(RuntimeOrigin::signed(5), 6, 1000, RewardDestination::Controller)); - assert_ok!(Staking::validate(RuntimeOrigin::signed(6), ValidatorPrefs::default())); + assert_ok!(Staking::bond(RuntimeOrigin::signed(5), 1000, RewardDestination::Controller)); + assert_ok!(Staking::validate(RuntimeOrigin::signed(5), ValidatorPrefs::default())); assert_ok!(Session::set_keys( - RuntimeOrigin::signed(6), + RuntimeOrigin::signed(5), SessionKeys { other: 6.into() }, vec![] )); mock::start_active_era(1); - // with current nominators 10 and 5 have the most stake - assert_eq_uvec!(validator_controllers(), vec![6, 10]); + // with current nominators 11 and 5 have the most stake + assert_eq_uvec!(validator_controllers(), vec![5, 11]); // 2 decides to be a validator. Consequences: - assert_ok!(Staking::validate(RuntimeOrigin::signed(2), ValidatorPrefs::default())); + assert_ok!(Staking::validate(RuntimeOrigin::signed(1), ValidatorPrefs::default())); assert_ok!(Session::set_keys( - RuntimeOrigin::signed(2), + RuntimeOrigin::signed(1), SessionKeys { other: 2.into() }, vec![] )); // new stakes: - // 10: 1000 self vote - // 20: 1000 self vote + 250 vote - // 6 : 1000 self vote - // 2 : 2000 self vote + 250 vote. - // Winners: 20 and 2 + // 11: 1000 self vote + // 21: 1000 self vote + 250 vote + // 5 : 1000 self vote + // 1 : 2000 self vote + 250 vote. + // Winners: 21 and 1 mock::start_active_era(2); - assert_eq_uvec!(validator_controllers(), vec![2, 20]); + assert_eq_uvec!(validator_controllers(), vec![1, 21]); }); } @@ -1878,7 +1891,7 @@ fn wrong_vote_is_moot() { ExtBuilder::default() .add_staker( 61, - 60, + 61, 500, StakerStatus::Nominator(vec![ 11, 21, // good votes @@ -1890,7 +1903,7 @@ fn wrong_vote_is_moot() { mock::start_active_era(1); // new validators - assert_eq_uvec!(validator_controllers(), vec![20, 10]); + assert_eq_uvec!(validator_controllers(), vec![21, 11]); // our new voter is taken into account assert!(Staking::eras_stakers(active_era(), 11).others.iter().any(|i| i.who == 61)); @@ -1911,22 +1924,17 @@ fn bond_with_no_staked_value() { .build_and_execute(|| { // Can't bond with 1 assert_noop!( - Staking::bond(RuntimeOrigin::signed(1), 2, 1, RewardDestination::Controller), + Staking::bond(RuntimeOrigin::signed(1), 1, RewardDestination::Controller), Error::::InsufficientBond, ); // bonded with absolute minimum value possible. - assert_ok!(Staking::bond( - RuntimeOrigin::signed(1), - 2, - 5, - RewardDestination::Controller - )); + assert_ok!(Staking::bond(RuntimeOrigin::signed(1), 5, RewardDestination::Controller)); assert_eq!(Balances::locks(&1)[0].amount, 5); // unbonding even 1 will cause all to be unbonded. - assert_ok!(Staking::unbond(RuntimeOrigin::signed(2), 1)); + assert_ok!(Staking::unbond(RuntimeOrigin::signed(1), 1)); assert_eq!( - Staking::ledger(2), + Staking::ledger(1), Some(StakingLedger { stash: 1, active: 0, @@ -1940,15 +1948,15 @@ fn bond_with_no_staked_value() { mock::start_active_era(2); // not yet removed. - assert_ok!(Staking::withdraw_unbonded(RuntimeOrigin::signed(2), 0)); - assert!(Staking::ledger(2).is_some()); + assert_ok!(Staking::withdraw_unbonded(RuntimeOrigin::signed(1), 0)); + assert!(Staking::ledger(1).is_some()); assert_eq!(Balances::locks(&1)[0].amount, 5); mock::start_active_era(3); // poof. Account 1 is removed from the staking system. - assert_ok!(Staking::withdraw_unbonded(RuntimeOrigin::signed(2), 0)); - assert!(Staking::ledger(2).is_none()); + assert_ok!(Staking::withdraw_unbonded(RuntimeOrigin::signed(1), 0)); + assert!(Staking::ledger(1).is_none()); assert_eq!(Balances::locks(&1).len(), 0); }); } @@ -1961,25 +1969,20 @@ fn bond_with_little_staked_value_bounded() { .minimum_validator_count(1) .build_and_execute(|| { // setup - assert_ok!(Staking::chill(RuntimeOrigin::signed(30))); + assert_ok!(Staking::chill(RuntimeOrigin::signed(31))); assert_ok!(Staking::set_payee( - RuntimeOrigin::signed(10), + RuntimeOrigin::signed(11), RewardDestination::Controller )); - let init_balance_2 = Balances::free_balance(&2); - let init_balance_10 = Balances::free_balance(&10); + let init_balance_1 = Balances::free_balance(&1); + let init_balance_11 = Balances::free_balance(&11); // Stingy validator. - assert_ok!(Staking::bond( - RuntimeOrigin::signed(1), - 2, - 1, - RewardDestination::Controller - )); - assert_ok!(Staking::validate(RuntimeOrigin::signed(2), ValidatorPrefs::default())); + assert_ok!(Staking::bond(RuntimeOrigin::signed(1), 1, RewardDestination::Controller)); + assert_ok!(Staking::validate(RuntimeOrigin::signed(1), ValidatorPrefs::default())); assert_ok!(Session::set_keys( - RuntimeOrigin::signed(2), - SessionKeys { other: 2.into() }, + RuntimeOrigin::signed(1), + SessionKeys { other: 1.into() }, vec![] )); @@ -1992,17 +1995,17 @@ fn bond_with_little_staked_value_bounded() { mock::make_all_reward_payment(0); // 2 is elected. - assert_eq_uvec!(validator_controllers(), vec![20, 10, 2]); + assert_eq_uvec!(validator_controllers(), vec![21, 11, 1]); assert_eq!(Staking::eras_stakers(active_era(), 2).total, 0); // Old ones are rewarded. assert_eq_error_rate!( - Balances::free_balance(10), - init_balance_10 + total_payout_0 / 3, + Balances::free_balance(11), + init_balance_11 + total_payout_0 / 3, 1 ); // no rewards paid to 2. This was initial election. - assert_eq!(Balances::free_balance(2), init_balance_2); + assert_eq!(Balances::free_balance(1), init_balance_1); // reward era 2 let total_payout_1 = current_total_payout_for_duration(reward_time_per_era()); @@ -2010,18 +2013,18 @@ fn bond_with_little_staked_value_bounded() { mock::start_active_era(2); mock::make_all_reward_payment(1); - assert_eq_uvec!(validator_controllers(), vec![20, 10, 2]); + assert_eq_uvec!(validator_controllers(), vec![21, 11, 1]); assert_eq!(Staking::eras_stakers(active_era(), 2).total, 0); // 2 is now rewarded. assert_eq_error_rate!( - Balances::free_balance(2), - init_balance_2 + total_payout_1 / 3, + Balances::free_balance(1), + init_balance_1 + total_payout_1 / 3, 1 ); assert_eq_error_rate!( - Balances::free_balance(&10), - init_balance_10 + total_payout_0 / 3 + total_payout_1 / 3, + Balances::free_balance(&11), + init_balance_11 + total_payout_0 / 3 + total_payout_1 / 3, 2, ); }); @@ -2038,7 +2041,7 @@ fn bond_with_duplicate_vote_should_be_ignored_by_election_provider() { // ensure all have equal stake. assert_eq!( >::iter() - .map(|(v, _)| (v, Staking::ledger(v - 1).unwrap().total)) + .map(|(v, _)| (v, Staking::ledger(v).unwrap().total)) .collect::>(), vec![(31, 1000), (21, 1000), (11, 1000)], ); @@ -2053,19 +2056,17 @@ fn bond_with_duplicate_vote_should_be_ignored_by_election_provider() { assert_ok!(Staking::bond( RuntimeOrigin::signed(1), - 2, 1000, RewardDestination::Controller )); - assert_ok!(Staking::nominate(RuntimeOrigin::signed(2), vec![11, 11, 11, 21, 31])); + assert_ok!(Staking::nominate(RuntimeOrigin::signed(1), vec![11, 11, 11, 21, 31])); assert_ok!(Staking::bond( RuntimeOrigin::signed(3), - 4, 1000, RewardDestination::Controller )); - assert_ok!(Staking::nominate(RuntimeOrigin::signed(4), vec![21, 31])); + assert_ok!(Staking::nominate(RuntimeOrigin::signed(3), vec![21, 31])); // winners should be 21 and 31. Otherwise this election is taking duplicates into // account. @@ -2092,7 +2093,7 @@ fn bond_with_duplicate_vote_should_be_ignored_by_election_provider_elected() { // ensure all have equal stake. assert_eq!( >::iter() - .map(|(v, _)| (v, Staking::ledger(v - 1).unwrap().total)) + .map(|(v, _)| (v, Staking::ledger(v).unwrap().total)) .collect::>(), vec![(31, 1000), (21, 1000), (11, 1000)], ); @@ -2108,19 +2109,17 @@ fn bond_with_duplicate_vote_should_be_ignored_by_election_provider_elected() { assert_ok!(Staking::bond( RuntimeOrigin::signed(1), - 2, 1000, RewardDestination::Controller )); - assert_ok!(Staking::nominate(RuntimeOrigin::signed(2), vec![11, 11, 11, 21])); + assert_ok!(Staking::nominate(RuntimeOrigin::signed(1), vec![11, 11, 11, 21])); assert_ok!(Staking::bond( RuntimeOrigin::signed(3), - 4, 1000, RewardDestination::Controller )); - assert_ok!(Staking::nominate(RuntimeOrigin::signed(4), vec![21])); + assert_ok!(Staking::nominate(RuntimeOrigin::signed(3), vec![21])); // winners should be 21 and 11. let supports = ::ElectionProvider::elect().unwrap(); @@ -2155,15 +2154,15 @@ fn phragmen_should_not_overflow() { let _ = Staking::chill(RuntimeOrigin::signed(10)); let _ = Staking::chill(RuntimeOrigin::signed(20)); - bond_validator(3, 2, Votes::max_value() as Balance); - bond_validator(5, 4, Votes::max_value() as Balance); + bond_validator(3, Votes::max_value() as Balance); + bond_validator(5, Votes::max_value() as Balance); - bond_nominator(7, 6, Votes::max_value() as Balance, vec![3, 5]); - bond_nominator(9, 8, Votes::max_value() as Balance, vec![3, 5]); + bond_nominator(7, Votes::max_value() as Balance, vec![3, 5]); + bond_nominator(9, Votes::max_value() as Balance, vec![3, 5]); mock::start_active_era(1); - assert_eq_uvec!(validator_controllers(), vec![4, 2]); + assert_eq_uvec!(validator_controllers(), vec![3, 5]); // We can safely convert back to values within [u64, u128]. assert!(Staking::eras_stakers(active_era(), 3).total > Votes::max_value() as Balance); @@ -2202,8 +2201,7 @@ fn reward_validator_slashing_validator_does_not_overflow() { let _ = Balances::make_free_balance_be(&2, stake); // only slashes out of bonded stake are applied. without this line, it is 0. - Staking::bond(RuntimeOrigin::signed(2), 20000, stake - 1, RewardDestination::default()) - .unwrap(); + Staking::bond(RuntimeOrigin::signed(2), stake - 1, RewardDestination::default()).unwrap(); // Override exposure of 11 ErasStakers::::insert( 0, @@ -2274,7 +2272,7 @@ fn unbonded_balance_is_not_slashable() { // total amount staked is slashable. assert_eq!(Staking::slashable_balance_of(&11), 1000); - assert_ok!(Staking::unbond(RuntimeOrigin::signed(10), 800)); + assert_ok!(Staking::unbond(RuntimeOrigin::signed(11), 800)); // only the active portion. assert_eq!(Staking::slashable_balance_of(&11), 200); @@ -2411,7 +2409,7 @@ fn slash_in_old_span_does_not_deselect() { mock::start_active_era(2); - Staking::validate(RuntimeOrigin::signed(10), Default::default()).unwrap(); + Staking::validate(RuntimeOrigin::signed(11), Default::default()).unwrap(); assert_eq!(Staking::force_era(), Forcing::NotForcing); assert!(>::contains_key(11)); assert!(!Session::validators().contains(&11)); @@ -2452,7 +2450,7 @@ fn slash_in_old_span_does_not_deselect() { assert!(Validators::::iter().any(|(stash, _)| stash == 11)); // but it's disabled - assert!(is_disabled(10)); + assert!(is_disabled(11)); // and we are still forcing a new era assert_eq!(Staking::force_era(), Forcing::ForceNew); }); @@ -2836,7 +2834,7 @@ fn slashes_are_summed_across_spans() { assert_eq!(Balances::free_balance(21), 1900); // 21 has been force-chilled. re-signal intent to validate. - Staking::validate(RuntimeOrigin::signed(20), Default::default()).unwrap(); + Staking::validate(RuntimeOrigin::signed(21), Default::default()).unwrap(); mock::start_active_era(4); @@ -2967,8 +2965,8 @@ fn retroactive_deferred_slashes_one_before() { // unbond at slash era. mock::start_active_era(2); - assert_ok!(Staking::chill(RuntimeOrigin::signed(10))); - assert_ok!(Staking::unbond(RuntimeOrigin::signed(10), 100)); + assert_ok!(Staking::chill(RuntimeOrigin::signed(11))); + assert_ok!(Staking::unbond(RuntimeOrigin::signed(11), 100)); mock::start_active_era(3); System::reset_events(); @@ -2981,7 +2979,7 @@ fn retroactive_deferred_slashes_one_before() { mock::start_active_era(4); - assert_eq!(Staking::ledger(10).unwrap().total, 1000); + assert_eq!(Staking::ledger(11).unwrap().total, 1000); // slash happens after the next line. mock::start_active_era(5); @@ -2996,9 +2994,9 @@ fn retroactive_deferred_slashes_one_before() { )); // their ledger has already been slashed. - assert_eq!(Staking::ledger(10).unwrap().total, 900); - assert_ok!(Staking::unbond(RuntimeOrigin::signed(10), 1000)); - assert_eq!(Staking::ledger(10).unwrap().total, 900); + assert_eq!(Staking::ledger(11).unwrap().total, 900); + assert_ok!(Staking::unbond(RuntimeOrigin::signed(11), 1000)); + assert_eq!(Staking::ledger(11).unwrap().total, 900); }) } @@ -3023,14 +3021,14 @@ fn staker_cannot_bail_deferred_slash() { ); // now we chill - assert_ok!(Staking::chill(RuntimeOrigin::signed(100))); - assert_ok!(Staking::unbond(RuntimeOrigin::signed(100), 500)); + assert_ok!(Staking::chill(RuntimeOrigin::signed(101))); + assert_ok!(Staking::unbond(RuntimeOrigin::signed(101), 500)); assert_eq!(Staking::current_era().unwrap(), 1); assert_eq!(active_era(), 1); assert_eq!( - Ledger::::get(100).unwrap(), + Ledger::::get(101).unwrap(), StakingLedger { active: 0, total: 500, @@ -3060,10 +3058,10 @@ fn staker_cannot_bail_deferred_slash() { // and cannot yet unbond: assert_storage_noop!(assert!( - Staking::withdraw_unbonded(RuntimeOrigin::signed(100), 0).is_ok() + Staking::withdraw_unbonded(RuntimeOrigin::signed(101), 0).is_ok() )); assert_eq!( - Ledger::::get(100).unwrap().unlocking.into_inner(), + Ledger::::get(101).unwrap().unlocking.into_inner(), vec![UnlockChunk { era: 4u32, value: 500 as Balance }], ); @@ -3270,7 +3268,7 @@ fn slash_kicks_validators_not_nominators_and_disables_nominator_for_kicked_valid assert!(Validators::::iter().all(|(stash, _)| stash != 11)); // actually re-bond the slashed validator - assert_ok!(Staking::validate(RuntimeOrigin::signed(10), Default::default())); + assert_ok!(Staking::validate(RuntimeOrigin::signed(11), Default::default())); mock::start_active_era(2); let exposure_11 = Staking::eras_stakers(active_era(), &11); @@ -3336,9 +3334,9 @@ fn non_slashable_offence_doesnt_disable_validator() { ); // the offence for validator 10 wasn't slashable so it wasn't disabled - assert!(!is_disabled(10)); + assert!(!is_disabled(11)); // whereas validator 20 gets disabled - assert!(is_disabled(20)); + assert!(is_disabled(21)); }); } @@ -3399,9 +3397,9 @@ fn slashing_independent_of_disabling_validator() { ); // the offence for validator 10 was explicitly disabled - assert!(is_disabled(10)); - // whereas validator 20 is explicitly not disabled - assert!(!is_disabled(20)); + assert!(is_disabled(11)); + // whereas validator 21 is explicitly not disabled + assert!(!is_disabled(21)); }); } @@ -3475,18 +3473,18 @@ fn disabled_validators_are_kept_disabled_for_whole_era() { // nominations are not updated. assert_eq!(Staking::nominators(101).unwrap().targets, vec![11, 21]); - // validator 10 should not be disabled since the offence wasn't slashable - assert!(!is_disabled(10)); - // validator 20 gets disabled since it got slashed - assert!(is_disabled(20)); + // validator 11 should not be disabled since the offence wasn't slashable + assert!(!is_disabled(11)); + // validator 21 gets disabled since it got slashed + assert!(is_disabled(21)); advance_session(); // disabled validators should carry-on through all sessions in the era - assert!(!is_disabled(10)); - assert!(is_disabled(20)); + assert!(!is_disabled(11)); + assert!(is_disabled(21)); - // validator 10 should now get disabled + // validator 11 should now get disabled on_offence_now( &[OffenceDetails { offender: (11, exposure_11.clone()), reporters: vec![] }], &[Perbill::from_percent(25)], @@ -3498,14 +3496,14 @@ fn disabled_validators_are_kept_disabled_for_whole_era() { advance_session(); // and both are disabled in the last session of the era - assert!(is_disabled(10)); - assert!(is_disabled(20)); + assert!(is_disabled(11)); + assert!(is_disabled(21)); mock::start_active_era(2); // when a new era starts disabled validators get cleared - assert!(!is_disabled(10)); - assert!(!is_disabled(20)); + assert!(!is_disabled(11)); + assert!(!is_disabled(21)); }); } @@ -3519,11 +3517,11 @@ fn claim_reward_at_the_last_era_and_no_double_claim_and_invalid_claim() { // Consumed weight for all payout_stakers dispatches that fail let err_weight = ::WeightInfo::payout_stakers_alive_staked(0); - let init_balance_10 = Balances::total_balance(&10); - let init_balance_100 = Balances::total_balance(&100); + let init_balance_11 = Balances::total_balance(&11); + let init_balance_101 = Balances::total_balance(&101); - let part_for_10 = Perbill::from_rational::(1000, 1125); - let part_for_100 = Perbill::from_rational::(125, 1125); + let part_for_11 = Perbill::from_rational::(1000, 1125); + let part_for_101 = Perbill::from_rational::(125, 1125); // Check state Payee::::insert(11, RewardDestination::Controller); @@ -3583,12 +3581,12 @@ fn claim_reward_at_the_last_era_and_no_double_claim_and_invalid_claim() { // only era 1 and 2 can be rewarded. assert_eq!( - Balances::total_balance(&10), - init_balance_10 + part_for_10 * (total_payout_1 + total_payout_2), + Balances::total_balance(&11), + init_balance_11 + part_for_11 * (total_payout_1 + total_payout_2), ); assert_eq!( - Balances::total_balance(&100), - init_balance_100 + part_for_100 * (total_payout_1 + total_payout_2), + Balances::total_balance(&101), + init_balance_101 + part_for_101 * (total_payout_1 + total_payout_2), ); }); } @@ -3683,16 +3681,14 @@ fn test_max_nominator_rewarded_per_validator_and_cant_steal_someone_else_reward( ExtBuilder::default().build_and_execute(|| { for i in 0..=<::MaxNominatorRewardedPerValidator as Get<_>>::get() { let stash = 10_000 + i as AccountId; - let controller = 20_000 + i as AccountId; let balance = 10_000 + i as Balance; Balances::make_free_balance_be(&stash, balance); assert_ok!(Staking::bond( RuntimeOrigin::signed(stash), - controller, balance, RewardDestination::Stash )); - assert_ok!(Staking::nominate(RuntimeOrigin::signed(controller), vec![11])); + assert_ok!(Staking::nominate(RuntimeOrigin::signed(stash), vec![11])); } mock::start_active_era(1); @@ -3727,13 +3723,13 @@ fn test_payout_stakers() { // Track the exposure of the validator and the nominators that will get paid out. let mut payout_exposure = balance; // Create a validator: - bond_validator(11, 10, balance); // Default(64) + bond_validator(11, balance); // Default(64) assert_eq!(Validators::::count(), 1); // Create nominators, targeting stash of validators for i in 0..100 { let bond_amount = balance + i as Balance; - bond_nominator(1000 + i, 100 + i, bond_amount, vec![11]); + bond_nominator(1000 + i, bond_amount, vec![11]); total_exposure += bond_amount; if i >= 36 { payout_exposure += bond_amount; @@ -3762,18 +3758,18 @@ fn test_payout_stakers() { // Top 64 nominators of validator 11 automatically paid out, including the validator // Validator payout goes to controller. - assert!(Balances::free_balance(&10) > balance); + assert!(Balances::free_balance(&11) > balance); for i in 36..100 { - assert!(Balances::free_balance(&(100 + i)) > balance + i as Balance); + assert!(Balances::free_balance(&(1000 + i)) > balance + i as Balance); } // The bottom 36 do not for i in 0..36 { - assert_eq!(Balances::free_balance(&(100 + i)), balance + i as Balance); + assert_eq!(Balances::free_balance(&(1000 + i)), balance + i as Balance); } // We track rewards in `claimed_rewards` vec assert_eq!( - Staking::ledger(&10), + Staking::ledger(&11), Some(StakingLedger { stash: 11, total: 1000, @@ -3804,7 +3800,7 @@ fn test_payout_stakers() { // We track rewards in `claimed_rewards` vec assert_eq!( - Staking::ledger(&10), + Staking::ledger(&11), Some(StakingLedger { stash: 11, total: 1000, @@ -3837,7 +3833,7 @@ fn test_payout_stakers() { expected_last_reward_era )); assert_eq!( - Staking::ledger(&10), + Staking::ledger(&11), Some(StakingLedger { stash: 11, total: 1000, @@ -3852,7 +3848,7 @@ fn test_payout_stakers() { assert_ok!(Staking::payout_stakers(RuntimeOrigin::signed(1337), 11, 23)); assert_ok!(Staking::payout_stakers(RuntimeOrigin::signed(1337), 11, 42)); assert_eq!( - Staking::ledger(&10), + Staking::ledger(&11), Some(StakingLedger { stash: 11, total: 1000, @@ -3879,11 +3875,11 @@ fn payout_stakers_handles_basic_errors() { // Same setup as the test above let balance = 1000; - bond_validator(11, 10, balance); // Default(64) + bond_validator(11, balance); // Default(64) // Create nominators, targeting stash for i in 0..100 { - bond_nominator(1000 + i, 100 + i, balance + i as Balance, vec![11]); + bond_nominator(1000 + i, balance + i as Balance, vec![11]); } mock::start_active_era(1); @@ -3975,7 +3971,7 @@ fn payout_stakers_handles_weight_refund() { assert!(max_nom_rewarded_weight.any_gt(half_max_nom_rewarded_weight)); let balance = 1000; - bond_validator(11, 10, balance); + bond_validator(11, balance); // Era 1 start_active_era(1); @@ -3986,7 +3982,7 @@ fn payout_stakers_handles_weight_refund() { // Add some `half_max_nom_rewarded` nominators who will start backing the validator in the // next era. for i in 0..half_max_nom_rewarded { - bond_nominator((1000 + i).into(), (100 + i).into(), balance + i as Balance, vec![11]); + bond_nominator((1000 + i).into(), balance + i as Balance, vec![11]); } // Era 2 @@ -4028,7 +4024,7 @@ fn payout_stakers_handles_weight_refund() { // Add enough nominators so that we are at the limit. They will be active nominators // in the next era. for i in half_max_nom_rewarded..max_nom_rewarded { - bond_nominator((1000 + i).into(), (100 + i).into(), balance + i as Balance, vec![11]); + bond_nominator((1000 + i).into(), balance + i as Balance, vec![11]); } // Era 5 @@ -4062,9 +4058,9 @@ fn payout_stakers_handles_weight_refund() { fn bond_during_era_correctly_populates_claimed_rewards() { ExtBuilder::default().has_stakers(false).build_and_execute(|| { // Era = None - bond_validator(9, 8, 1000); + bond_validator(9, 1000); assert_eq!( - Staking::ledger(&8), + Staking::ledger(&9), Some(StakingLedger { stash: 9, total: 1000, @@ -4074,9 +4070,9 @@ fn bond_during_era_correctly_populates_claimed_rewards() { }) ); mock::start_active_era(5); - bond_validator(11, 10, 1000); + bond_validator(11, 1000); assert_eq!( - Staking::ledger(&10), + Staking::ledger(&11), Some(StakingLedger { stash: 11, total: 1000, @@ -4090,9 +4086,9 @@ fn bond_during_era_correctly_populates_claimed_rewards() { let current_era = 99; let last_reward_era = 99 - HistoryDepth::get(); mock::start_active_era(current_era); - bond_validator(13, 12, 1000); + bond_validator(13, 1000); assert_eq!( - Staking::ledger(&12), + Staking::ledger(&13), Some(StakingLedger { stash: 13, total: 1000, @@ -4181,24 +4177,31 @@ fn payout_creates_controller() { ExtBuilder::default().has_stakers(false).build_and_execute(|| { let balance = 1000; // Create a validator: - bond_validator(11, 10, balance); + bond_validator(11, balance); - // Create a stash/controller pair - bond_nominator(1234, 1337, 100, vec![11]); + // create a stash/controller pair and nominate + let (stash, controller) = testing_utils::create_unique_stash_controller::( + 0, + 100, + RewardDestination::Controller, + false, + ) + .unwrap(); + assert_ok!(Staking::nominate(RuntimeOrigin::signed(controller), vec![11])); // kill controller - assert_ok!(Balances::transfer_allow_death(RuntimeOrigin::signed(1337), 1234, 100)); - assert_eq!(Balances::free_balance(1337), 0); + assert_ok!(Balances::transfer_allow_death(RuntimeOrigin::signed(controller), stash, 100)); + assert_eq!(Balances::free_balance(controller), 0); mock::start_active_era(1); Staking::reward_by_ids(vec![(11, 1)]); // compute and ensure the reward amount is greater than zero. let _ = current_total_payout_for_duration(reward_time_per_era()); mock::start_active_era(2); - assert_ok!(Staking::payout_stakers(RuntimeOrigin::signed(1337), 11, 1)); + assert_ok!(Staking::payout_stakers(RuntimeOrigin::signed(controller), 11, 1)); // Controller is created - assert!(Balances::free_balance(1337) > 0); + assert!(Balances::free_balance(controller) > 0); }) } @@ -4207,13 +4210,13 @@ fn payout_to_any_account_works() { ExtBuilder::default().has_stakers(false).build_and_execute(|| { let balance = 1000; // Create a validator: - bond_validator(11, 10, balance); // Default(64) + bond_validator(11, balance); // Default(64) // Create a stash/controller pair - bond_nominator(1234, 1337, 100, vec![11]); + bond_nominator(1234, 100, vec![11]); // Update payout location - assert_ok!(Staking::set_payee(RuntimeOrigin::signed(1337), RewardDestination::Account(42))); + assert_ok!(Staking::set_payee(RuntimeOrigin::signed(1234), RewardDestination::Account(42))); // Reward Destination account doesn't exist assert_eq!(Balances::free_balance(42), 0); @@ -4335,38 +4338,38 @@ fn session_buffering_no_offset() { #[test] fn cannot_rebond_to_lower_than_ed() { ExtBuilder::default() - .existential_deposit(10) - .balance_factor(10) + .existential_deposit(11) + .balance_factor(11) .build_and_execute(|| { // initial stuff. assert_eq!( - Staking::ledger(&20).unwrap(), + Staking::ledger(&21).unwrap(), StakingLedger { stash: 21, - total: 10 * 1000, - active: 10 * 1000, + total: 11 * 1000, + active: 11 * 1000, unlocking: Default::default(), claimed_rewards: bounded_vec![], } ); // unbond all of it. must be chilled first. - assert_ok!(Staking::chill(RuntimeOrigin::signed(20))); - assert_ok!(Staking::unbond(RuntimeOrigin::signed(20), 10 * 1000)); + assert_ok!(Staking::chill(RuntimeOrigin::signed(21))); + assert_ok!(Staking::unbond(RuntimeOrigin::signed(21), 11 * 1000)); assert_eq!( - Staking::ledger(&20).unwrap(), + Staking::ledger(&21).unwrap(), StakingLedger { stash: 21, - total: 10 * 1000, + total: 11 * 1000, active: 0, - unlocking: bounded_vec![UnlockChunk { value: 10 * 1000, era: 3 }], + unlocking: bounded_vec![UnlockChunk { value: 11 * 1000, era: 3 }], claimed_rewards: bounded_vec![], } ); // now bond a wee bit more assert_noop!( - Staking::rebond(RuntimeOrigin::signed(20), 5), + Staking::rebond(RuntimeOrigin::signed(21), 5), Error::::InsufficientBond ); }) @@ -4375,31 +4378,31 @@ fn cannot_rebond_to_lower_than_ed() { #[test] fn cannot_bond_extra_to_lower_than_ed() { ExtBuilder::default() - .existential_deposit(10) - .balance_factor(10) + .existential_deposit(11) + .balance_factor(11) .build_and_execute(|| { // initial stuff. assert_eq!( - Staking::ledger(&20).unwrap(), + Staking::ledger(&21).unwrap(), StakingLedger { stash: 21, - total: 10 * 1000, - active: 10 * 1000, + total: 11 * 1000, + active: 11 * 1000, unlocking: Default::default(), claimed_rewards: bounded_vec![], } ); // unbond all of it. must be chilled first. - assert_ok!(Staking::chill(RuntimeOrigin::signed(20))); - assert_ok!(Staking::unbond(RuntimeOrigin::signed(20), 10 * 1000)); + assert_ok!(Staking::chill(RuntimeOrigin::signed(21))); + assert_ok!(Staking::unbond(RuntimeOrigin::signed(21), 11 * 1000)); assert_eq!( - Staking::ledger(&20).unwrap(), + Staking::ledger(&21).unwrap(), StakingLedger { stash: 21, - total: 10 * 1000, + total: 11 * 1000, active: 0, - unlocking: bounded_vec![UnlockChunk { value: 10 * 1000, era: 3 }], + unlocking: bounded_vec![UnlockChunk { value: 11 * 1000, era: 3 }], claimed_rewards: bounded_vec![], } ); @@ -4421,7 +4424,7 @@ fn do_not_die_when_active_is_ed() { .build_and_execute(|| { // given assert_eq!( - Staking::ledger(&20).unwrap(), + Staking::ledger(&21).unwrap(), StakingLedger { stash: 21, total: 1000 * ed, @@ -4432,13 +4435,13 @@ fn do_not_die_when_active_is_ed() { ); // when unbond all of it except ed. - assert_ok!(Staking::unbond(RuntimeOrigin::signed(20), 999 * ed)); + assert_ok!(Staking::unbond(RuntimeOrigin::signed(21), 999 * ed)); start_active_era(3); - assert_ok!(Staking::withdraw_unbonded(RuntimeOrigin::signed(20), 100)); + assert_ok!(Staking::withdraw_unbonded(RuntimeOrigin::signed(21), 100)); // then assert_eq!( - Staking::ledger(&20).unwrap(), + Staking::ledger(&21).unwrap(), StakingLedger { stash: 21, total: ed, @@ -4501,9 +4504,9 @@ mod election_data_provider { fn set_minimum_active_stake_is_correct() { ExtBuilder::default() .nominate(false) - .add_staker(61, 60, 2_000, StakerStatus::::Nominator(vec![21])) - .add_staker(71, 70, 10, StakerStatus::::Nominator(vec![21])) - .add_staker(81, 80, 50, StakerStatus::::Nominator(vec![21])) + .add_staker(61, 61, 2_000, StakerStatus::::Nominator(vec![21])) + .add_staker(71, 71, 10, StakerStatus::::Nominator(vec![21])) + .add_staker(81, 81, 50, StakerStatus::::Nominator(vec![21])) .build_and_execute(|| { assert_ok!(::electing_voters(None)); assert_eq!(MinimumActiveStake::::get(), 10); @@ -4575,19 +4578,19 @@ mod election_data_provider { // ppl, but then lower the MaxNomination limit. .add_staker( 61, - 60, + 61, 2_000, StakerStatus::::Nominator(vec![21, 22, 23, 24, 25]), ) .add_staker( 71, - 70, + 71, 2_000, StakerStatus::::Nominator(vec![21, 22, 23, 24, 25]), ) .add_staker( 81, - 80, + 81, 2_000, StakerStatus::::Nominator(vec![21, 22, 23, 24, 25]), ) @@ -4707,51 +4710,46 @@ fn min_bond_checks_work() { .min_validator_bond(1_500) .build_and_execute(|| { // 500 is not enough for any role - assert_ok!(Staking::bond( - RuntimeOrigin::signed(3), - 4, - 500, - RewardDestination::Controller - )); + assert_ok!(Staking::bond(RuntimeOrigin::signed(3), 500, RewardDestination::Controller)); assert_noop!( - Staking::nominate(RuntimeOrigin::signed(4), vec![1]), + Staking::nominate(RuntimeOrigin::signed(3), vec![1]), Error::::InsufficientBond ); assert_noop!( - Staking::validate(RuntimeOrigin::signed(4), ValidatorPrefs::default()), + Staking::validate(RuntimeOrigin::signed(3), ValidatorPrefs::default()), Error::::InsufficientBond, ); // 1000 is enough for nominator assert_ok!(Staking::bond_extra(RuntimeOrigin::signed(3), 500)); - assert_ok!(Staking::nominate(RuntimeOrigin::signed(4), vec![1])); + assert_ok!(Staking::nominate(RuntimeOrigin::signed(3), vec![1])); assert_noop!( - Staking::validate(RuntimeOrigin::signed(4), ValidatorPrefs::default()), + Staking::validate(RuntimeOrigin::signed(3), ValidatorPrefs::default()), Error::::InsufficientBond, ); // 1500 is enough for validator assert_ok!(Staking::bond_extra(RuntimeOrigin::signed(3), 500)); - assert_ok!(Staking::nominate(RuntimeOrigin::signed(4), vec![1])); - assert_ok!(Staking::validate(RuntimeOrigin::signed(4), ValidatorPrefs::default())); + assert_ok!(Staking::nominate(RuntimeOrigin::signed(3), vec![1])); + assert_ok!(Staking::validate(RuntimeOrigin::signed(3), ValidatorPrefs::default())); // Can't unbond anything as validator assert_noop!( - Staking::unbond(RuntimeOrigin::signed(4), 500), + Staking::unbond(RuntimeOrigin::signed(3), 500), Error::::InsufficientBond ); // Once they are a nominator, they can unbond 500 - assert_ok!(Staking::nominate(RuntimeOrigin::signed(4), vec![1])); - assert_ok!(Staking::unbond(RuntimeOrigin::signed(4), 500)); + assert_ok!(Staking::nominate(RuntimeOrigin::signed(3), vec![1])); + assert_ok!(Staking::unbond(RuntimeOrigin::signed(3), 500)); assert_noop!( - Staking::unbond(RuntimeOrigin::signed(4), 500), + Staking::unbond(RuntimeOrigin::signed(3), 500), Error::::InsufficientBond ); // Once they are chilled they can unbond everything - assert_ok!(Staking::chill(RuntimeOrigin::signed(4))); - assert_ok!(Staking::unbond(RuntimeOrigin::signed(4), 1000)); + assert_ok!(Staking::chill(RuntimeOrigin::signed(3))); + assert_ok!(Staking::unbond(RuntimeOrigin::signed(3), 1000)); }) } @@ -4767,31 +4765,27 @@ fn chill_other_works() { let initial_nominators = Nominators::::count(); for i in 0..15 { let a = 4 * i; - let b = 4 * i + 1; - let c = 4 * i + 2; - let d = 4 * i + 3; + let b = 4 * i + 2; + let c = 4 * i + 3; Balances::make_free_balance_be(&a, 100_000); Balances::make_free_balance_be(&b, 100_000); Balances::make_free_balance_be(&c, 100_000); - Balances::make_free_balance_be(&d, 100_000); // Nominator assert_ok!(Staking::bond( RuntimeOrigin::signed(a), - b, 1000, RewardDestination::Controller )); - assert_ok!(Staking::nominate(RuntimeOrigin::signed(b), vec![1])); + assert_ok!(Staking::nominate(RuntimeOrigin::signed(a), vec![1])); // Validator assert_ok!(Staking::bond( - RuntimeOrigin::signed(c), - d, + RuntimeOrigin::signed(b), 1500, RewardDestination::Controller )); - assert_ok!(Staking::validate(RuntimeOrigin::signed(d), ValidatorPrefs::default())); + assert_ok!(Staking::validate(RuntimeOrigin::signed(b), ValidatorPrefs::default())); } // To chill other users, we need to: @@ -4804,11 +4798,11 @@ fn chill_other_works() { // Can't chill these users assert_noop!( - Staking::chill_other(RuntimeOrigin::signed(1337), 1), + Staking::chill_other(RuntimeOrigin::signed(1337), 0), Error::::CannotChillOther ); assert_noop!( - Staking::chill_other(RuntimeOrigin::signed(1337), 3), + Staking::chill_other(RuntimeOrigin::signed(1337), 2), Error::::CannotChillOther ); @@ -4825,11 +4819,11 @@ fn chill_other_works() { // Still can't chill these users assert_noop!( - Staking::chill_other(RuntimeOrigin::signed(1337), 1), + Staking::chill_other(RuntimeOrigin::signed(1337), 0), Error::::CannotChillOther ); assert_noop!( - Staking::chill_other(RuntimeOrigin::signed(1337), 3), + Staking::chill_other(RuntimeOrigin::signed(1337), 2), Error::::CannotChillOther ); @@ -4846,11 +4840,11 @@ fn chill_other_works() { // Still can't chill these users assert_noop!( - Staking::chill_other(RuntimeOrigin::signed(1337), 1), + Staking::chill_other(RuntimeOrigin::signed(1337), 0), Error::::CannotChillOther ); assert_noop!( - Staking::chill_other(RuntimeOrigin::signed(1337), 3), + Staking::chill_other(RuntimeOrigin::signed(1337), 2), Error::::CannotChillOther ); @@ -4867,11 +4861,11 @@ fn chill_other_works() { // Still can't chill these users assert_noop!( - Staking::chill_other(RuntimeOrigin::signed(1337), 1), + Staking::chill_other(RuntimeOrigin::signed(1337), 0), Error::::CannotChillOther ); assert_noop!( - Staking::chill_other(RuntimeOrigin::signed(1337), 3), + Staking::chill_other(RuntimeOrigin::signed(1337), 2), Error::::CannotChillOther ); @@ -4893,8 +4887,8 @@ fn chill_other_works() { // Users can now be chilled down to 7 people, so we try to remove 9 of them (starting // with 16) for i in 6..15 { - let b = 4 * i + 1; - let d = 4 * i + 3; + let b = 4 * i; + let d = 4 * i + 2; assert_ok!(Staking::chill_other(RuntimeOrigin::signed(1337), b)); assert_ok!(Staking::chill_other(RuntimeOrigin::signed(1337), d)); } @@ -4902,12 +4896,12 @@ fn chill_other_works() { // chill a nominator. Limit is not reached, not chill-able assert_eq!(Nominators::::count(), 7); assert_noop!( - Staking::chill_other(RuntimeOrigin::signed(1337), 1), + Staking::chill_other(RuntimeOrigin::signed(1337), 0), Error::::CannotChillOther ); // chill a validator. Limit is reached, chill-able. assert_eq!(Validators::::count(), 9); - assert_ok!(Staking::chill_other(RuntimeOrigin::signed(1337), 3)); + assert_ok!(Staking::chill_other(RuntimeOrigin::signed(1337), 2)); }) } @@ -4973,7 +4967,7 @@ fn capped_stakers_works() { some_existing_nominator = controller; } - // one more is too many + // one more is too many. let (_, last_nominator) = testing_utils::create_stash_controller::( 30_000_000, 100, @@ -5014,9 +5008,9 @@ fn capped_stakers_works() { #[test] fn min_commission_works() { ExtBuilder::default().build_and_execute(|| { - // account 10 controls the stash from account 11 + // account 11 controls the stash of itself. assert_ok!(Staking::validate( - RuntimeOrigin::signed(10), + RuntimeOrigin::signed(11), ValidatorPrefs { commission: Perbill::from_percent(5), blocked: false } )); @@ -5042,7 +5036,7 @@ fn min_commission_works() { // can't make it less than 10 now assert_noop!( Staking::validate( - RuntimeOrigin::signed(10), + RuntimeOrigin::signed(11), ValidatorPrefs { commission: Perbill::from_percent(5), blocked: false } ), Error::::CommissionTooLow @@ -5050,12 +5044,12 @@ fn min_commission_works() { // can only change to higher. assert_ok!(Staking::validate( - RuntimeOrigin::signed(10), + RuntimeOrigin::signed(11), ValidatorPrefs { commission: Perbill::from_percent(10), blocked: false } )); assert_ok!(Staking::validate( - RuntimeOrigin::signed(10), + RuntimeOrigin::signed(11), ValidatorPrefs { commission: Perbill::from_percent(15), blocked: false } )); }) @@ -5065,8 +5059,8 @@ fn min_commission_works() { fn change_of_max_nominations() { use frame_election_provider_support::ElectionDataProvider; ExtBuilder::default() - .add_staker(60, 61, 10, StakerStatus::Nominator(vec![1])) - .add_staker(70, 71, 10, StakerStatus::Nominator(vec![1, 2, 3])) + .add_staker(61, 61, 10, StakerStatus::Nominator(vec![1])) + .add_staker(71, 71, 10, StakerStatus::Nominator(vec![1, 2, 3])) .balance_factor(10) .build_and_execute(|| { // pre-condition @@ -5076,7 +5070,7 @@ fn change_of_max_nominations() { Nominators::::iter() .map(|(k, n)| (k, n.targets.len())) .collect::>(), - vec![(70, 3), (101, 2), (60, 1)] + vec![(101, 2), (71, 3), (61, 1)] ); // 3 validators and 3 nominators assert_eq!(Staking::electing_voters(None).unwrap().len(), 3 + 3); @@ -5088,7 +5082,7 @@ fn change_of_max_nominations() { Nominators::::iter() .map(|(k, n)| (k, n.targets.len())) .collect::>(), - vec![(70, 3), (101, 2), (60, 1)] + vec![(101, 2), (71, 3), (61, 1)] ); assert_eq!(Staking::electing_voters(None).unwrap().len(), 3 + 3); @@ -5099,7 +5093,7 @@ fn change_of_max_nominations() { Nominators::::iter() .map(|(k, n)| (k, n.targets.len())) .collect::>(), - vec![(70, 3), (101, 2), (60, 1)] + vec![(101, 2), (71, 3), (61, 1)] ); assert_eq!(Staking::electing_voters(None).unwrap().len(), 3 + 3); @@ -5111,12 +5105,12 @@ fn change_of_max_nominations() { Nominators::::iter() .map(|(k, n)| (k, n.targets.len())) .collect::>(), - vec![(101, 2), (60, 1)] + vec![(101, 2), (61, 1)] ); // 70 is still in storage.. - assert!(Nominators::::contains_key(70)); + assert!(Nominators::::contains_key(71)); // but its value cannot be decoded and default is returned. - assert!(Nominators::::get(70).is_none()); + assert!(Nominators::::get(71).is_none()); assert_eq!(Staking::electing_voters(None).unwrap().len(), 3 + 2); assert!(Nominators::::contains_key(101)); @@ -5129,12 +5123,12 @@ fn change_of_max_nominations() { Nominators::::iter() .map(|(k, n)| (k, n.targets.len())) .collect::>(), - vec![(60, 1)] + vec![(61, 1)] ); - assert!(Nominators::::contains_key(70)); - assert!(Nominators::::contains_key(60)); - assert!(Nominators::::get(70).is_none()); - assert!(Nominators::::get(60).is_some()); + assert!(Nominators::::contains_key(71)); + assert!(Nominators::::contains_key(61)); + assert!(Nominators::::get(71).is_none()); + assert!(Nominators::::get(61).is_some()); assert_eq!(Staking::electing_voters(None).unwrap().len(), 3 + 1); // now one of them can revive themselves by re-nominating to a proper value. @@ -5143,13 +5137,13 @@ fn change_of_max_nominations() { Nominators::::iter() .map(|(k, n)| (k, n.targets.len())) .collect::>(), - vec![(70, 1), (60, 1)] + vec![(71, 1), (61, 1)] ); // or they can be chilled by any account. assert!(Nominators::::contains_key(101)); assert!(Nominators::::get(101).is_none()); - assert_ok!(Staking::chill_other(RuntimeOrigin::signed(70), 100)); + assert_ok!(Staking::chill_other(RuntimeOrigin::signed(71), 101)); assert!(!Nominators::::contains_key(101)); assert!(Nominators::::get(101).is_none()); }) @@ -5173,7 +5167,7 @@ mod sorted_list_provider { ); // when account 101 renominates - assert_ok!(Staking::nominate(RuntimeOrigin::signed(100), vec![41])); + assert_ok!(Staking::nominate(RuntimeOrigin::signed(101), vec![41])); // then counts don't change assert_eq!(::VoterList::count(), pre_insert_voter_count); @@ -5196,7 +5190,7 @@ mod sorted_list_provider { assert_eq!(::VoterList::iter().collect::>(), vec![11, 21, 31]); // when account 11 re-validates - assert_ok!(Staking::validate(RuntimeOrigin::signed(10), Default::default())); + assert_ok!(Staking::validate(RuntimeOrigin::signed(11), Default::default())); // then counts don't change assert_eq!(::VoterList::count(), pre_insert_voter_count); @@ -5211,8 +5205,8 @@ fn force_apply_min_commission_works() { let prefs = |c| ValidatorPrefs { commission: Perbill::from_percent(c), blocked: false }; let validators = || Validators::::iter().collect::>(); ExtBuilder::default().build_and_execute(|| { - assert_ok!(Staking::validate(RuntimeOrigin::signed(30), prefs(10))); - assert_ok!(Staking::validate(RuntimeOrigin::signed(20), prefs(5))); + assert_ok!(Staking::validate(RuntimeOrigin::signed(31), prefs(10))); + assert_ok!(Staking::validate(RuntimeOrigin::signed(21), prefs(5))); // Given assert_eq!(validators(), vec![(31, prefs(10)), (21, prefs(5)), (11, prefs(0))]); @@ -5504,12 +5498,12 @@ fn pre_bonding_era_cannot_be_claimed() { mock::start_active_era(current_era); // add a new candidate for being a validator. account 3 controlled by 4. - assert_ok!(Staking::bond(RuntimeOrigin::signed(3), 4, 1500, RewardDestination::Controller)); + assert_ok!(Staking::bond(RuntimeOrigin::signed(3), 1500, RewardDestination::Controller)); let claimed_rewards: BoundedVec<_, _> = (start_reward_era..=last_reward_era).collect::>().try_into().unwrap(); assert_eq!( - Staking::ledger(&4).unwrap(), + Staking::ledger(&3).unwrap(), StakingLedger { stash: 3, total: 1500, @@ -5524,14 +5518,14 @@ fn pre_bonding_era_cannot_be_claimed() { mock::start_active_era(current_era); // claiming reward for last era in which validator was active works - assert_ok!(Staking::payout_stakers(RuntimeOrigin::signed(4), 3, current_era - 1)); + assert_ok!(Staking::payout_stakers(RuntimeOrigin::signed(3), 3, current_era - 1)); // consumed weight for all payout_stakers dispatches that fail let err_weight = ::WeightInfo::payout_stakers_alive_staked(0); // cannot claim rewards for an era before bonding occured as it is // already marked as claimed. assert_noop!( - Staking::payout_stakers(RuntimeOrigin::signed(4), 3, current_era - 2), + Staking::payout_stakers(RuntimeOrigin::signed(3), 3, current_era - 2), Error::::AlreadyClaimed.with_weight(err_weight) ); @@ -5541,7 +5535,7 @@ fn pre_bonding_era_cannot_be_claimed() { // make sure stakers still cannot claim rewards that they are not meant to assert_noop!( - Staking::payout_stakers(RuntimeOrigin::signed(4), 3, current_era - 2), + Staking::payout_stakers(RuntimeOrigin::signed(3), 3, current_era - 2), Error::::NotController ); @@ -5559,7 +5553,7 @@ fn reducing_history_depth_abrupt() { let last_reward_era = current_era - 1; let start_reward_era = current_era - original_history_depth; - // put some money in (stash, controller)=(3,4),(5,6). + // put some money in (stash, controller)=(3,3),(5,5). for i in 3..7 { let _ = Balances::make_free_balance_be(&i, 2000); } @@ -5567,15 +5561,15 @@ fn reducing_history_depth_abrupt() { // start current era mock::start_active_era(current_era); - // add a new candidate for being a staker. account 3 controlled by 4. - assert_ok!(Staking::bond(RuntimeOrigin::signed(3), 4, 1500, RewardDestination::Controller)); + // add a new candidate for being a staker. account 3 controlled by 3. + assert_ok!(Staking::bond(RuntimeOrigin::signed(3), 1500, RewardDestination::Controller)); // all previous era before the bonding action should be marked as // claimed. let claimed_rewards: BoundedVec<_, _> = (start_reward_era..=last_reward_era).collect::>().try_into().unwrap(); assert_eq!( - Staking::ledger(&4).unwrap(), + Staking::ledger(&3).unwrap(), StakingLedger { stash: 3, total: 1500, @@ -5590,7 +5584,7 @@ fn reducing_history_depth_abrupt() { mock::start_active_era(current_era); // claiming reward for last era in which validator was active works - assert_ok!(Staking::payout_stakers(RuntimeOrigin::signed(4), 3, current_era - 1)); + assert_ok!(Staking::payout_stakers(RuntimeOrigin::signed(3), 3, current_era - 1)); // next era current_era = current_era + 1; @@ -5601,12 +5595,12 @@ fn reducing_history_depth_abrupt() { HistoryDepth::set(history_depth); // claiming reward does not work anymore assert_noop!( - Staking::payout_stakers(RuntimeOrigin::signed(4), 3, current_era - 1), + Staking::payout_stakers(RuntimeOrigin::signed(3), 3, current_era - 1), Error::::NotController ); // new stakers can still bond - assert_ok!(Staking::bond(RuntimeOrigin::signed(5), 6, 1200, RewardDestination::Controller)); + assert_ok!(Staking::bond(RuntimeOrigin::signed(5), 1200, RewardDestination::Controller)); // new staking ledgers created will be bounded by the current history depth let last_reward_era = current_era - 1; @@ -5614,7 +5608,7 @@ fn reducing_history_depth_abrupt() { let claimed_rewards: BoundedVec<_, _> = (start_reward_era..=last_reward_era).collect::>().try_into().unwrap(); assert_eq!( - Staking::ledger(&6).unwrap(), + Staking::ledger(&5).unwrap(), StakingLedger { stash: 5, total: 1200, @@ -5637,17 +5631,17 @@ fn reducing_max_unlocking_chunks_abrupt() { // given a staker at era=10 and MaxUnlockChunks set to 2 MaxUnlockingChunks::set(2); start_active_era(10); - assert_ok!(Staking::bond(RuntimeOrigin::signed(3), 4, 300, RewardDestination::Staked)); - assert!(matches!(Staking::ledger(4), Some(_))); + assert_ok!(Staking::bond(RuntimeOrigin::signed(3), 300, RewardDestination::Staked)); + assert!(matches!(Staking::ledger(3), Some(_))); // when staker unbonds - assert_ok!(Staking::unbond(RuntimeOrigin::signed(4), 20)); + assert_ok!(Staking::unbond(RuntimeOrigin::signed(3), 20)); // then an unlocking chunk is added at `current_era + bonding_duration` // => 10 + 3 = 13 let expected_unlocking: BoundedVec, MaxUnlockingChunks> = bounded_vec![UnlockChunk { value: 20 as Balance, era: 13 as EraIndex }]; - assert!(matches!(Staking::ledger(4), + assert!(matches!(Staking::ledger(3), Some(StakingLedger { unlocking, .. @@ -5655,11 +5649,11 @@ fn reducing_max_unlocking_chunks_abrupt() { // when staker unbonds at next era start_active_era(11); - assert_ok!(Staking::unbond(RuntimeOrigin::signed(4), 50)); + assert_ok!(Staking::unbond(RuntimeOrigin::signed(3), 50)); // then another unlock chunk is added let expected_unlocking: BoundedVec, MaxUnlockingChunks> = bounded_vec![UnlockChunk { value: 20, era: 13 }, UnlockChunk { value: 50, era: 14 }]; - assert!(matches!(Staking::ledger(4), + assert!(matches!(Staking::ledger(3), Some(StakingLedger { unlocking, .. @@ -5668,13 +5662,13 @@ fn reducing_max_unlocking_chunks_abrupt() { // when staker unbonds further start_active_era(12); // then further unbonding not possible - assert_noop!(Staking::unbond(RuntimeOrigin::signed(4), 20), Error::::NoMoreChunks); + assert_noop!(Staking::unbond(RuntimeOrigin::signed(3), 20), Error::::NoMoreChunks); // when max unlocking chunks is reduced abruptly to a low value MaxUnlockingChunks::set(1); // then unbond, rebond ops are blocked with ledger in corrupt state - assert_noop!(Staking::unbond(RuntimeOrigin::signed(4), 20), Error::::NotController); - assert_noop!(Staking::rebond(RuntimeOrigin::signed(4), 100), Error::::NotController); + assert_noop!(Staking::unbond(RuntimeOrigin::signed(3), 20), Error::::NotController); + assert_noop!(Staking::rebond(RuntimeOrigin::signed(3), 100), Error::::NotController); // reset the ledger corruption MaxUnlockingChunks::set(2); @@ -5761,7 +5755,7 @@ fn set_min_commission_works_with_admin_origin() { // setting commission below min_commission fails assert_noop!( Staking::validate( - RuntimeOrigin::signed(10), + RuntimeOrigin::signed(11), ValidatorPrefs { commission: Perbill::from_percent(14), blocked: false } ), Error::::CommissionTooLow @@ -5769,7 +5763,7 @@ fn set_min_commission_works_with_admin_origin() { // setting commission >= min_commission works assert_ok!(Staking::validate( - RuntimeOrigin::signed(10), + RuntimeOrigin::signed(11), ValidatorPrefs { commission: Perbill::from_percent(15), blocked: false } )); }) @@ -5787,13 +5781,13 @@ mod staking_interface { // without slash let _ = with_storage_layer::<(), _, _>(|| { // bond an account, can unstake - assert_eq!(Staking::bonded(&11), Some(10)); + assert_eq!(Staking::bonded(&11), Some(11)); assert_ok!(::force_unstake(11)); Err(DispatchError::from("revert")) }); // bond again and add a slash, still can unstake. - assert_eq!(Staking::bonded(&11), Some(10)); + assert_eq!(Staking::bonded(&11), Some(11)); add_slash(&11); assert_ok!(::force_unstake(11)); }); @@ -5810,16 +5804,16 @@ mod staking_interface { &[Perbill::from_percent(100)], ); - assert_eq!(Staking::bonded(&11), Some(10)); + assert_eq!(Staking::bonded(&11), Some(11)); assert_noop!( - Staking::withdraw_unbonded(RuntimeOrigin::signed(10), 0), + Staking::withdraw_unbonded(RuntimeOrigin::signed(11), 0), Error::::IncorrectSlashingSpans ); let num_slashing_spans = Staking::slashing_spans(&11).map_or(0, |s| s.iter().count()); assert_ok!(Staking::withdraw_unbonded( - RuntimeOrigin::signed(10), + RuntimeOrigin::signed(11), num_slashing_spans as u32 )); }); From 4ce0b74476c49d3f255736f16b1e4e176e526f66 Mon Sep 17 00:00:00 2001 From: Anton Date: Fri, 12 May 2023 11:12:51 +0400 Subject: [PATCH 491/558] Upgrade to libp2p 0.51.3 (#13587) * client/network: upgrade to libp2p 0.51.0 * make discovery.rs compile * make peer_info.rs compile * changes to notifications and request-response proto * make service.rs compile * towards making request_responses.rs compile * make request_responses.rs compile * make request_responses.rs compile * fix notifications/behaviour.rs tests * fix warnings * remove old code * allow deprecated code (temporary) * upgrade to libp2p 0.51.1 * add TODO for behaviour tests * return empty vec if peer_id is absent https://github.com/paritytech/substrate/pull/13587#discussion_r1141695167 fyi: I don't really know what the old behaviour was. * update comment to reflect new defaults Closes #13338 * Revert "update comment to reflect new defaults" This reverts commit 7a981abd69308e9d522ec94905f181439a1b1dba. * remove config.rs (from wrong merge) * upgrade to libp2p 0.51.2 * fix formatting * use handle_pending_outbound_connection in networt_state RPC * update deps * use re-exports when we use other libp2p packages * Apply suggestions from code review Co-authored-by: Dmitry Markin * format code * handle potential errors in network_state RPC * only update libp2p crate * update libp2p-core * fix docs * use libp2p-identity instead of libp2p where it's possible. libp2p-identity is much smaller, hence makes sense to use it instead of larger libp2p crate. * Update client/network/src/discovery.rs Co-authored-by: Aaro Altonen <48052676+altonen@users.noreply.github.com> * update Cargo.lock * add comment for per_connection_event_buffer_size current value is somewhat arbitrary and needs to be tweaked depending on memory usage and network worker sleep stats. * fix link format * update Cargo.lock * upgrade to libp2p 0.51.3 * deprecate mplex * Revert "deprecate mplex" This reverts commit 9e25820e706e464a0e962a8604861fcb2a7641eb. * Revert "upgrade to libp2p 0.51.3" This reverts commit 6544dd4138e2f89517bd7c7281fc78a638ec7040. * use new libp2p version in `statement` crate * pin version temporarily * libp2p 0.51.3 * deprecate mplex * deprecate legacy noise handshake * fix build error * update libp2p-identity * enable libp2p-identity:ed25519 feature in sc-consensus * enable ed25519 for peerset as well --------- Co-authored-by: Dmitry Markin Co-authored-by: Aaro Altonen <48052676+altonen@users.noreply.github.com> Co-authored-by: parity-processbot <> --- Cargo.lock | 310 +++++++-------- client/authority-discovery/Cargo.toml | 3 +- client/authority-discovery/src/error.rs | 2 +- client/authority-discovery/src/tests.rs | 17 +- client/authority-discovery/src/worker.rs | 18 +- .../src/worker/schema/tests.rs | 6 +- .../authority-discovery/src/worker/tests.rs | 2 +- client/cli/Cargo.toml | 2 +- client/cli/src/commands/generate_node_key.rs | 6 +- client/cli/src/commands/inspect_node_key.rs | 10 +- client/cli/src/params/node_key_params.rs | 13 +- client/consensus/common/Cargo.toml | 2 +- client/consensus/common/src/import_queue.rs | 2 +- .../common/src/import_queue/basic_queue.rs | 2 +- client/network-gossip/Cargo.toml | 2 +- client/network/Cargo.toml | 2 +- client/network/README.md | 2 - client/network/bitswap/Cargo.toml | 2 +- client/network/bitswap/src/lib.rs | 2 +- client/network/common/Cargo.toml | 2 +- client/network/common/src/sync.rs | 2 +- client/network/light/Cargo.toml | 2 +- .../src/light_client_requests/handler.rs | 2 +- client/network/src/behaviour.rs | 10 +- client/network/src/config.rs | 28 +- client/network/src/discovery.rs | 204 +++++----- client/network/src/event.rs | 2 +- client/network/src/lib.rs | 4 +- client/network/src/peer_info.rs | 167 +++++--- client/network/src/protocol.rs | 78 ++-- .../src/protocol/notifications/behaviour.rs | 357 ++++++++++-------- .../src/protocol/notifications/handler.rs | 96 ++--- .../src/protocol/notifications/tests.rs | 85 +++-- client/network/src/request_responses.rs | 292 +++++++------- client/network/src/service.rs | 112 +++--- client/network/src/service/signature.rs | 2 +- client/network/src/service/traits.rs | 2 +- client/network/src/transport.rs | 50 +-- client/network/statement/Cargo.toml | 2 +- client/network/sync/Cargo.toml | 2 +- client/network/test/Cargo.toml | 2 +- client/network/transactions/Cargo.toml | 2 +- client/offchain/Cargo.toml | 2 +- client/peerset/Cargo.toml | 2 +- client/peerset/src/lib.rs | 4 +- client/peerset/src/peersstate.rs | 4 +- client/peerset/tests/fuzz.rs | 2 +- client/telemetry/Cargo.toml | 2 +- 48 files changed, 1002 insertions(+), 924 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 740e45a0d..d9cb87af4 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -514,12 +514,6 @@ dependencies = [ "syn 1.0.109", ] -[[package]] -name = "asn1_der" -version = "0.7.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "155a5a185e42c6b77ac7b88a15143d930a9e9727a5b7b77eed417404ab15c247" - [[package]] name = "assert_cmd" version = "2.0.11" @@ -4097,22 +4091,24 @@ checksum = "348108ab3fba42ec82ff6e9564fc4ca0247bdccdc68dd8af9764bbc79c3c8ffb" [[package]] name = "libp2p" -version = "0.50.1" +version = "0.51.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c7b0104790be871edcf97db9bd2356604984e623a08d825c3f27852290266b8" +checksum = "f210d259724eae82005b5c48078619b7745edb7b76de370b03f8ba59ea103097" dependencies = [ "bytes", "futures", "futures-timer", "getrandom 0.2.9", "instant", - "libp2p-core 0.38.0", + "libp2p-allow-block-list", + "libp2p-connection-limits", + "libp2p-core", "libp2p-dns", "libp2p-identify", + "libp2p-identity", "libp2p-kad", "libp2p-mdns", "libp2p-metrics", - "libp2p-mplex", "libp2p-noise", "libp2p-ping", "libp2p-quic", @@ -4123,44 +4119,32 @@ dependencies = [ "libp2p-webrtc", "libp2p-websocket", "libp2p-yamux", - "multiaddr 0.16.0", - "parking_lot 0.12.1", + "multiaddr", "pin-project", - "smallvec", ] [[package]] -name = "libp2p-core" -version = "0.38.0" +name = "libp2p-allow-block-list" +version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b6a8fcd392ff67af6cc3f03b1426c41f7f26b6b9aff2dc632c1c56dd649e571f" +checksum = "510daa05efbc25184458db837f6f9a5143888f1caa742426d92e1833ddd38a50" dependencies = [ - "asn1_der", - "bs58", - "ed25519-dalek", - "either", - "fnv", - "futures", - "futures-timer", - "instant", - "log", - "multiaddr 0.16.0", - "multihash 0.16.3", - "multistream-select", - "once_cell", - "parking_lot 0.12.1", - "pin-project", - "prost", - "prost-build", - "rand 0.8.5", - "rw-stream-sink", - "sec1 0.3.0", - "sha2 0.10.6", - "smallvec", - "thiserror", - "unsigned-varint", + "libp2p-core", + "libp2p-identity", + "libp2p-swarm", + "void", +] + +[[package]] +name = "libp2p-connection-limits" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4caa33f1d26ed664c4fe2cca81a08c8e07d4c1c04f2f4ac7655c2dd85467fda0" +dependencies = [ + "libp2p-core", + "libp2p-identity", + "libp2p-swarm", "void", - "zeroize", ] [[package]] @@ -4176,7 +4160,7 @@ dependencies = [ "instant", "libp2p-identity", "log", - "multiaddr 0.17.1", + "multiaddr", "multihash 0.17.0", "multistream-select", "once_cell", @@ -4193,12 +4177,12 @@ dependencies = [ [[package]] name = "libp2p-dns" -version = "0.38.0" +version = "0.39.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8e42a271c1b49f789b92f7fc87749fa79ce5c7bdc88cbdfacb818a4bca47fec5" +checksum = "146ff7034daae62077c415c2376b8057368042df6ab95f5432ad5e88568b1554" dependencies = [ "futures", - "libp2p-core 0.38.0", + "libp2p-core", "log", "parking_lot 0.12.1", "smallvec", @@ -4207,20 +4191,21 @@ dependencies = [ [[package]] name = "libp2p-identify" -version = "0.41.1" +version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c052d0026f4817b44869bfb6810f4e1112f43aec8553f2cb38881c524b563abf" +checksum = "5455f472243e63b9c497ff320ded0314254a9eb751799a39c283c6f20b793f3c" dependencies = [ "asynchronous-codec", + "either", "futures", "futures-timer", - "libp2p-core 0.38.0", + "libp2p-core", + "libp2p-identity", "libp2p-swarm", "log", - "lru", - "prost", - "prost-build", - "prost-codec", + "lru 0.10.0", + "quick-protobuf", + "quick-protobuf-codec", "smallvec", "thiserror", "void", @@ -4235,7 +4220,7 @@ dependencies = [ "bs58", "ed25519-dalek", "log", - "multiaddr 0.17.1", + "multiaddr", "multihash 0.17.0", "quick-protobuf", "rand 0.8.5", @@ -4246,9 +4231,9 @@ dependencies = [ [[package]] name = "libp2p-kad" -version = "0.42.1" +version = "0.43.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2766dcd2be8c87d5e1f35487deb22d765f49c6ae1251b3633efe3b25698bd3d2" +checksum = "39d5ef876a2b2323d63c258e63c2f8e36f205fe5a11f0b3095d59635650790ff" dependencies = [ "arrayvec 0.7.2", "asynchronous-codec", @@ -4258,11 +4243,11 @@ dependencies = [ "futures", "futures-timer", "instant", - "libp2p-core 0.38.0", + "libp2p-core", + "libp2p-identity", "libp2p-swarm", "log", - "prost", - "prost-build", + "quick-protobuf", "rand 0.8.5", "sha2 0.10.6", "smallvec", @@ -4274,14 +4259,15 @@ dependencies = [ [[package]] name = "libp2p-mdns" -version = "0.42.0" +version = "0.43.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "04f378264aade9872d6ccd315c0accc18be3a35d15fc1b9c36e5b6f983b62b5b" +checksum = "19983e1f949f979a928f2c603de1cf180cc0dc23e4ac93a62651ccb18341460b" dependencies = [ "data-encoding", "futures", "if-watch", - "libp2p-core 0.38.0", + "libp2p-core", + "libp2p-identity", "libp2p-swarm", "log", "rand 0.8.5", @@ -4294,11 +4280,11 @@ dependencies = [ [[package]] name = "libp2p-metrics" -version = "0.11.0" +version = "0.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5ad8a64f29da86005c86a4d2728b8a0719e9b192f4092b609fd8790acb9dec55" +checksum = "a42ec91e227d7d0dafa4ce88b333cdf5f277253873ab087555c92798db2ddd46" dependencies = [ - "libp2p-core 0.38.0", + "libp2p-core", "libp2p-identify", "libp2p-kad", "libp2p-ping", @@ -4306,38 +4292,20 @@ dependencies = [ "prometheus-client", ] -[[package]] -name = "libp2p-mplex" -version = "0.38.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "03805b44107aa013e7cbbfa5627b31c36cbedfdfb00603c0311998882bc4bace" -dependencies = [ - "asynchronous-codec", - "bytes", - "futures", - "libp2p-core 0.38.0", - "log", - "nohash-hasher", - "parking_lot 0.12.1", - "rand 0.8.5", - "smallvec", - "unsigned-varint", -] - [[package]] name = "libp2p-noise" -version = "0.41.0" +version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a978cb57efe82e892ec6f348a536bfbd9fee677adbe5689d7a93ad3a9bffbf2e" +checksum = "9c3673da89d29936bc6435bafc638e2f184180d554ce844db65915113f86ec5e" dependencies = [ "bytes", "curve25519-dalek 3.2.0", "futures", - "libp2p-core 0.38.0", + "libp2p-core", + "libp2p-identity", "log", "once_cell", - "prost", - "prost-build", + "quick-protobuf", "rand 0.8.5", "sha2 0.10.6", "snow", @@ -4349,14 +4317,15 @@ dependencies = [ [[package]] name = "libp2p-ping" -version = "0.41.0" +version = "0.42.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "929fcace45a112536e22b3dcfd4db538723ef9c3cb79f672b98be2cc8e25f37f" +checksum = "3e57759c19c28a73ef1eb3585ca410cefb72c1a709fcf6de1612a378e4219202" dependencies = [ + "either", "futures", "futures-timer", "instant", - "libp2p-core 0.38.0", + "libp2p-core", "libp2p-swarm", "log", "rand 0.8.5", @@ -4365,15 +4334,16 @@ dependencies = [ [[package]] name = "libp2p-quic" -version = "0.7.0-alpha" +version = "0.7.0-alpha.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "01e7c867e95c8130667b24409d236d37598270e6da69b3baf54213ba31ffca59" +checksum = "c6b26abd81cd2398382a1edfe739b539775be8a90fa6914f39b2ab49571ec735" dependencies = [ "bytes", "futures", "futures-timer", "if-watch", - "libp2p-core 0.38.0", + "libp2p-core", + "libp2p-identity", "libp2p-tls", "log", "parking_lot 0.12.1", @@ -4386,49 +4356,46 @@ dependencies = [ [[package]] name = "libp2p-request-response" -version = "0.23.0" +version = "0.24.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3236168796727bfcf4927f766393415361e2c644b08bedb6a6b13d957c9a4884" +checksum = "7ffdb374267d42dc5ed5bc53f6e601d4a64ac5964779c6e40bb9e4f14c1e30d5" dependencies = [ "async-trait", - "bytes", "futures", "instant", - "libp2p-core 0.38.0", + "libp2p-core", + "libp2p-identity", "libp2p-swarm", - "log", "rand 0.8.5", "smallvec", - "unsigned-varint", ] [[package]] name = "libp2p-swarm" -version = "0.41.1" +version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b2a35472fe3276b3855c00f1c032ea8413615e030256429ad5349cdf67c6e1a0" +checksum = "903b3d592d7694e56204d211f29d31bc004be99386644ba8731fc3e3ef27b296" dependencies = [ "either", "fnv", "futures", "futures-timer", "instant", - "libp2p-core 0.38.0", + "libp2p-core", + "libp2p-identity", "libp2p-swarm-derive", "log", - "pin-project", "rand 0.8.5", "smallvec", - "thiserror", "tokio", "void", ] [[package]] name = "libp2p-swarm-derive" -version = "0.31.0" +version = "0.32.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9d527d5827582abd44a6d80c07ff8b50b4ee238a8979e05998474179e79dc400" +checksum = "0fba456131824ab6acd4c7bf61e9c0f0a3014b5fc9868ccb8e10d344594cdc4f" dependencies = [ "heck", "quote", @@ -4437,15 +4404,15 @@ dependencies = [ [[package]] name = "libp2p-tcp" -version = "0.38.0" +version = "0.39.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b4b257baf6df8f2df39678b86c578961d48cc8b68642a12f0f763f56c8e5858d" +checksum = "33d33698596d7722d85d3ab0c86c2c322254fce1241e91208e3679b4eb3026cf" dependencies = [ "futures", "futures-timer", "if-watch", "libc", - "libp2p-core 0.38.0", + "libp2p-core", "log", "socket2", "tokio", @@ -4459,7 +4426,7 @@ checksum = "ff08d13d0dc66e5e9ba6279c1de417b84fa0d0adc3b03e5732928c180ec02781" dependencies = [ "futures", "futures-rustls", - "libp2p-core 0.39.2", + "libp2p-core", "libp2p-identity", "rcgen 0.10.0", "ring", @@ -4472,13 +4439,13 @@ dependencies = [ [[package]] name = "libp2p-wasm-ext" -version = "0.38.0" +version = "0.39.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1bb1a35299860e0d4b3c02a3e74e3b293ad35ae0cee8a056363b0c862d082069" +checksum = "77dff9d32353a5887adb86c8afc1de1a94d9e8c3bc6df8b2201d7cdf5c848f43" dependencies = [ "futures", "js-sys", - "libp2p-core 0.38.0", + "libp2p-core", "parity-send-wrapper", "wasm-bindgen", "wasm-bindgen-futures", @@ -4486,9 +4453,9 @@ dependencies = [ [[package]] name = "libp2p-webrtc" -version = "0.4.0-alpha" +version = "0.4.0-alpha.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cdb6cd86dd68cba72308ea05de1cebf3ba0ae6e187c40548167955d4e3970f6a" +checksum = "dba48592edbc2f60b4bc7c10d65445b0c3964c07df26fdf493b6880d33be36f8" dependencies = [ "async-trait", "asynchronous-codec", @@ -4497,13 +4464,13 @@ dependencies = [ "futures-timer", "hex", "if-watch", - "libp2p-core 0.38.0", + "libp2p-core", + "libp2p-identity", "libp2p-noise", "log", - "multihash 0.16.3", - "prost", - "prost-build", - "prost-codec", + "multihash 0.17.0", + "quick-protobuf", + "quick-protobuf-codec", "rand 0.8.5", "rcgen 0.9.3", "serde", @@ -4517,14 +4484,14 @@ dependencies = [ [[package]] name = "libp2p-websocket" -version = "0.40.0" +version = "0.41.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1d705506030d5c0aaf2882437c70dab437605f21c5f9811978f694e6917a3b54" +checksum = "111273f7b3d3510524c752e8b7a5314b7f7a1fee7e68161c01a7d72cbb06db9f" dependencies = [ "either", "futures", "futures-rustls", - "libp2p-core 0.38.0", + "libp2p-core", "log", "parking_lot 0.12.1", "quicksink", @@ -4536,14 +4503,13 @@ dependencies = [ [[package]] name = "libp2p-yamux" -version = "0.42.0" +version = "0.43.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4f63594a0aa818642d9d4915c791945053877253f08a3626f13416b5cd928a29" +checksum = "4dcd21d950662700a385d4c6d68e2f5f54d778e97068cdd718522222ef513bda" dependencies = [ "futures", - "libp2p-core 0.38.0", + "libp2p-core", "log", - "parking_lot 0.12.1", "thiserror", "yamux", ] @@ -4728,6 +4694,15 @@ dependencies = [ "hashbrown 0.12.3", ] +[[package]] +name = "lru" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "03f1160296536f10c833a82dca22267d5486734230d47bf00bf435885814ba1e" +dependencies = [ + "hashbrown 0.13.2", +] + [[package]] name = "lru-cache" version = "0.1.2" @@ -5001,24 +4976,6 @@ dependencies = [ "syn 1.0.109", ] -[[package]] -name = "multiaddr" -version = "0.16.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a4aebdb21e90f81d13ed01dc84123320838e53963c2ca94b60b305d3fa64f31e" -dependencies = [ - "arrayref", - "byteorder", - "data-encoding", - "multibase", - "multihash 0.16.3", - "percent-encoding", - "serde", - "static_assertions", - "unsigned-varint", - "url", -] - [[package]] name = "multiaddr" version = "0.17.1" @@ -5073,7 +5030,9 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "835d6ff01d610179fbce3de1694d007e500bf33a7f29689838941d6bf783ae40" dependencies = [ "core2", + "digest 0.10.6", "multihash-derive", + "sha2 0.10.6", "unsigned-varint", ] @@ -8024,21 +7983,21 @@ dependencies = [ [[package]] name = "prometheus-client" -version = "0.18.1" +version = "0.19.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "83cd1b99916654a69008fd66b4f9397fbe08e6e51dfe23d4417acf5d3b8cb87c" +checksum = "5d6fa99d535dd930d1249e6c79cb3c2915f9172a540fe2b02a4c8f9ca954721e" dependencies = [ "dtoa", "itoa", "parking_lot 0.12.1", - "prometheus-client-derive-text-encode", + "prometheus-client-derive-encode", ] [[package]] -name = "prometheus-client-derive-text-encode" -version = "0.3.0" +name = "prometheus-client-derive-encode" +version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "66a455fbcb954c1a7decf3c586e860fd7889cddf4b8e164be736dbac95a953cd" +checksum = "72b6a5217beb0ad503ee7fa752d451c905113d70721b937126158f3106a48cc1" dependencies = [ "proc-macro2", "quote", @@ -8077,19 +8036,6 @@ dependencies = [ "which", ] -[[package]] -name = "prost-codec" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0dc34979ff898b6e141106178981ce2596c387ea6e62533facfc61a37fc879c0" -dependencies = [ - "asynchronous-codec", - "bytes", - "prost", - "thiserror", - "unsigned-varint", -] - [[package]] name = "prost-derive" version = "0.11.9" @@ -8136,6 +8082,19 @@ dependencies = [ "byteorder", ] +[[package]] +name = "quick-protobuf-codec" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1693116345026436eb2f10b677806169c1a1260c1c60eaaffe3fb5a29ae23d8b" +dependencies = [ + "asynchronous-codec", + "bytes", + "quick-protobuf", + "thiserror", + "unsigned-varint", +] + [[package]] name = "quickcheck" version = "1.0.3" @@ -8754,6 +8713,7 @@ dependencies = [ "ip_network", "libp2p", "log", + "multihash 0.17.0", "parity-scale-codec", "prost", "prost-build", @@ -8853,7 +8813,7 @@ dependencies = [ "fdlimit", "futures", "futures-timer", - "libp2p", + "libp2p-identity", "log", "names", "parity-scale-codec", @@ -8954,7 +8914,7 @@ dependencies = [ "async-trait", "futures", "futures-timer", - "libp2p", + "libp2p-identity", "log", "mockall", "parking_lot 0.12.1", @@ -9320,7 +9280,7 @@ dependencies = [ "assert_matches", "criterion", "env_logger 0.9.3", - "lru", + "lru 0.8.1", "num_cpus", "parity-scale-codec", "parking_lot 0.12.1", @@ -9447,7 +9407,7 @@ dependencies = [ "libp2p", "linked_hash_set", "log", - "lru", + "lru 0.8.1", "mockall", "multistream-select", "parity-scale-codec", @@ -9491,7 +9451,7 @@ version = "0.10.0-dev" dependencies = [ "cid", "futures", - "libp2p", + "libp2p-identity", "log", "prost", "prost-build", @@ -9521,7 +9481,7 @@ dependencies = [ "bytes", "futures", "futures-timer", - "libp2p", + "libp2p-identity", "parity-scale-codec", "prost-build", "sc-consensus", @@ -9548,7 +9508,7 @@ dependencies = [ "futures-timer", "libp2p", "log", - "lru", + "lru 0.8.1", "quickcheck", "sc-network", "sc-network-common", @@ -9566,7 +9526,7 @@ version = "0.10.0-dev" dependencies = [ "array-bytes 4.2.0", "futures", - "libp2p", + "libp2p-identity", "log", "parity-scale-codec", "prost", @@ -9612,7 +9572,7 @@ dependencies = [ "futures-timer", "libp2p", "log", - "lru", + "lru 0.8.1", "mockall", "parity-scale-codec", "prost", @@ -9734,7 +9694,7 @@ name = "sc-peerset" version = "4.0.0-dev" dependencies = [ "futures", - "libp2p", + "libp2p-identity", "log", "rand 0.8.5", "sc-utils", @@ -10780,7 +10740,7 @@ version = "4.0.0-dev" dependencies = [ "futures", "log", - "lru", + "lru 0.8.1", "parity-scale-codec", "parking_lot 0.12.1", "sp-api", diff --git a/client/authority-discovery/Cargo.toml b/client/authority-discovery/Cargo.toml index 900d9c59d..8b9fb743b 100644 --- a/client/authority-discovery/Cargo.toml +++ b/client/authority-discovery/Cargo.toml @@ -21,7 +21,8 @@ codec = { package = "parity-scale-codec", version = "3.2.2", default-features = futures = "0.3.21" futures-timer = "3.0.1" ip_network = "0.4.1" -libp2p = { version = "0.50.0", features = ["kad"] } +libp2p = { version = "0.51.3", features = ["kad", "ed25519"] } +multihash = { version = "0.17.0", default-features = false, features = ["std", "sha2"] } log = "0.4.17" prost = "0.11" rand = "0.8.5" diff --git a/client/authority-discovery/src/error.rs b/client/authority-discovery/src/error.rs index 13148bb33..ca685115d 100644 --- a/client/authority-discovery/src/error.rs +++ b/client/authority-discovery/src/error.rs @@ -56,7 +56,7 @@ pub enum Error { ParsingMultiaddress(#[from] libp2p::core::multiaddr::Error), #[error("Failed to parse a libp2p key.")] - ParsingLibp2pIdentity(#[from] libp2p::identity::error::DecodingError), + ParsingLibp2pIdentity(#[from] libp2p::identity::DecodingError), #[error("Failed to sign: {0}.")] CannotSign(String), diff --git a/client/authority-discovery/src/tests.rs b/client/authority-discovery/src/tests.rs index d354f6a96..4fbc196c5 100644 --- a/client/authority-discovery/src/tests.rs +++ b/client/authority-discovery/src/tests.rs @@ -25,8 +25,9 @@ use crate::{ }; use futures::{channel::mpsc::channel, executor::LocalPool, task::LocalSpawn}; -use libp2p::core::{ - multiaddr::{Multiaddr, Protocol}, +use libp2p::{ + core::multiaddr::{Multiaddr, Protocol}, + identity::ed25519, PeerId, }; use std::{collections::HashSet, sync::Arc}; @@ -86,18 +87,16 @@ fn get_addresses_and_authority_id() { fn cryptos_are_compatible() { use sp_core::crypto::Pair; - let libp2p_secret = libp2p::identity::Keypair::generate_ed25519(); - let libp2p_public = libp2p_secret.public(); + let libp2p_keypair = ed25519::Keypair::generate(); + let libp2p_public = libp2p_keypair.public(); - let sp_core_secret = { - let libp2p::identity::Keypair::Ed25519(libp2p_ed_secret) = libp2p_secret.clone(); - sp_core::ed25519::Pair::from_seed_slice(&libp2p_ed_secret.secret().as_ref()).unwrap() - }; + let sp_core_secret = + { sp_core::ed25519::Pair::from_seed_slice(&libp2p_keypair.secret().as_ref()).unwrap() }; let sp_core_public = sp_core_secret.public(); let message = b"we are more powerful than not to be better"; - let libp2p_signature = libp2p_secret.sign(message).unwrap(); + let libp2p_signature = libp2p_keypair.sign(message); let sp_core_signature = sp_core_secret.sign(message); // no error expected... assert!(sp_core::ed25519::Pair::verify( diff --git a/client/authority-discovery/src/worker.rs b/client/authority-discovery/src/worker.rs index 590d1dd19..a29e74df9 100644 --- a/client/authority-discovery/src/worker.rs +++ b/client/authority-discovery/src/worker.rs @@ -34,11 +34,9 @@ use futures::{channel::mpsc, future, stream::Fuse, FutureExt, Stream, StreamExt} use addr_cache::AddrCache; use codec::{Decode, Encode}; use ip_network::IpNetwork; -use libp2p::{ - core::multiaddr, - multihash::{Multihash, MultihashDigest}, - Multiaddr, PeerId, -}; +use libp2p::{core::multiaddr, identity::PublicKey, multihash::Multihash, Multiaddr, PeerId}; +use multihash::{Code, MultihashDigest}; + use log::{debug, error, log_enabled}; use prometheus_endpoint::{register, Counter, CounterVec, Gauge, Opts, U64}; use prost::Message; @@ -551,10 +549,8 @@ where // properly signed by the owner of the PeerId if let Some(peer_signature) = peer_signature { - let public_key = libp2p::identity::PublicKey::from_protobuf_encoding( - &peer_signature.public_key, - ) - .map_err(Error::ParsingLibp2pIdentity)?; + let public_key = PublicKey::try_decode_protobuf(&peer_signature.public_key) + .map_err(Error::ParsingLibp2pIdentity)?; let signature = Signature { public_key, bytes: peer_signature.signature }; if !signature.verify(record, &remote_peer_id) { @@ -625,7 +621,7 @@ pub trait NetworkProvider: NetworkDHTProvider + NetworkStateInfo + NetworkSigner impl NetworkProvider for T where T: NetworkDHTProvider + NetworkStateInfo + NetworkSigner {} fn hash_authority_id(id: &[u8]) -> KademliaKey { - KademliaKey::new(&libp2p::multihash::Code::Sha2_256.digest(id).digest()) + KademliaKey::new(&Code::Sha2_256.digest(id).digest()) } // Makes sure all values are the same and returns it @@ -662,7 +658,7 @@ fn sign_record_with_peer_id( let signature = network .sign_with_local_identity(serialized_record) .map_err(|e| Error::CannotSign(format!("{} (network packet)", e)))?; - let public_key = signature.public_key.to_protobuf_encoding(); + let public_key = signature.public_key.encode_protobuf(); let signature = signature.bytes; Ok(schema::PeerSignature { signature, public_key }) } diff --git a/client/authority-discovery/src/worker/schema/tests.rs b/client/authority-discovery/src/worker/schema/tests.rs index 89c921e0c..c765e4e53 100644 --- a/client/authority-discovery/src/worker/schema/tests.rs +++ b/client/authority-discovery/src/worker/schema/tests.rs @@ -21,7 +21,7 @@ mod schema_v1 { } use super::*; -use libp2p::{multiaddr::Multiaddr, PeerId}; +use libp2p::{identity::Keypair, multiaddr::Multiaddr, PeerId}; use prost::Message; #[test] @@ -55,7 +55,7 @@ fn v2_decodes_v1() { #[test] fn v1_decodes_v2() { - let peer_secret = libp2p::identity::Keypair::generate_ed25519(); + let peer_secret = Keypair::generate_ed25519(); let peer_public = peer_secret.public(); let peer_id = peer_public.to_peer_id(); let multiaddress: Multiaddr = @@ -67,7 +67,7 @@ fn v1_decodes_v2() { let record_v2 = AuthorityRecord { addresses: vec_addresses.clone() }; let mut vec_record_v2 = vec![]; record_v2.encode(&mut vec_record_v2).unwrap(); - let vec_peer_public = peer_public.to_protobuf_encoding(); + let vec_peer_public = peer_public.encode_protobuf(); let peer_signature_v2 = PeerSignature { public_key: vec_peer_public, signature: vec_peer_signature }; let signed_record_v2 = SignedAuthorityRecord { diff --git a/client/authority-discovery/src/worker/tests.rs b/client/authority-discovery/src/worker/tests.rs index 49055fec5..c29120881 100644 --- a/client/authority-discovery/src/worker/tests.rs +++ b/client/authority-discovery/src/worker/tests.rs @@ -31,7 +31,7 @@ use futures::{ }; use libp2p::{ core::multiaddr, - identity::{error::SigningError, Keypair}, + identity::{Keypair, SigningError}, kad::record::Key as KademliaKey, PeerId, }; diff --git a/client/cli/Cargo.toml b/client/cli/Cargo.toml index 6f755a356..49e0765dd 100644 --- a/client/cli/Cargo.toml +++ b/client/cli/Cargo.toml @@ -18,7 +18,7 @@ chrono = "0.4.10" clap = { version = "4.2.5", features = ["derive", "string"] } fdlimit = "0.2.1" futures = "0.3.21" -libp2p = "0.50.0" +libp2p-identity = { version = "0.1.2", features = ["peerid", "ed25519"]} log = "0.4.17" names = { version = "0.13.0", default-features = false } parity-scale-codec = "3.2.2" diff --git a/client/cli/src/commands/generate_node_key.rs b/client/cli/src/commands/generate_node_key.rs index a16ba4990..c3579f7dc 100644 --- a/client/cli/src/commands/generate_node_key.rs +++ b/client/cli/src/commands/generate_node_key.rs @@ -19,7 +19,7 @@ use crate::Error; use clap::Parser; -use libp2p::identity::{ed25519 as libp2p_ed25519, PublicKey}; +use libp2p_identity::{ed25519, Keypair}; use std::{ fs, io::{self, Write}, @@ -48,7 +48,7 @@ pub struct GenerateNodeKeyCmd { impl GenerateNodeKeyCmd { /// Run the command pub fn run(&self) -> Result<(), Error> { - let keypair = libp2p_ed25519::Keypair::generate(); + let keypair = ed25519::Keypair::generate(); let secret = keypair.secret(); @@ -63,7 +63,7 @@ impl GenerateNodeKeyCmd { None => io::stdout().lock().write_all(&file_data)?, } - eprintln!("{}", PublicKey::Ed25519(keypair.public()).to_peer_id()); + eprintln!("{}", Keypair::from(keypair).public().to_peer_id()); Ok(()) } diff --git a/client/cli/src/commands/inspect_node_key.rs b/client/cli/src/commands/inspect_node_key.rs index 733a1343a..19b5a31ca 100644 --- a/client/cli/src/commands/inspect_node_key.rs +++ b/client/cli/src/commands/inspect_node_key.rs @@ -19,7 +19,7 @@ use crate::Error; use clap::Parser; -use libp2p::identity::{ed25519, PublicKey}; +use libp2p_identity::Keypair; use std::{ fs, io::{self, Read}, @@ -68,12 +68,10 @@ impl InspectNodeKeyCmd { .map_err(|_| "failed to decode secret as hex")?; } - let secret = - ed25519::SecretKey::from_bytes(&mut file_data).map_err(|_| "Bad node key file")?; + let keypair = + Keypair::ed25519_from_bytes(&mut file_data).map_err(|_| "Bad node key file")?; - let keypair = ed25519::Keypair::from(secret); - - println!("{}", PublicKey::Ed25519(keypair.public()).to_peer_id()); + println!("{}", keypair.public().to_peer_id()); Ok(()) } diff --git a/client/cli/src/params/node_key_params.rs b/client/cli/src/params/node_key_params.rs index e31963d2f..8c5579eae 100644 --- a/client/cli/src/params/node_key_params.rs +++ b/client/cli/src/params/node_key_params.rs @@ -101,7 +101,7 @@ fn invalid_node_key(e: impl std::fmt::Display) -> error::Error { /// Parse a Ed25519 secret key from a hex string into a `sc_network::Secret`. fn parse_ed25519_secret(hex: &str) -> error::Result { H256::from_str(hex).map_err(invalid_node_key).and_then(|bytes| { - ed25519::SecretKey::from_bytes(bytes) + ed25519::SecretKey::try_from_bytes(bytes) .map(sc_network::config::Secret::Input) .map_err(invalid_node_key) }) @@ -111,7 +111,7 @@ fn parse_ed25519_secret(hex: &str) -> error::Result {}, - _ => panic!("Invalid key"), + if let Ok(pair) = node_key.try_into_ed25519() { + if pair.secret().as_ref() != key.as_ref() { + panic!("Invalid key") + } + } else { + panic!("Invalid key") } } diff --git a/client/consensus/common/Cargo.toml b/client/consensus/common/Cargo.toml index d9e80e1e5..e953d6796 100644 --- a/client/consensus/common/Cargo.toml +++ b/client/consensus/common/Cargo.toml @@ -16,7 +16,7 @@ targets = ["x86_64-unknown-linux-gnu"] async-trait = "0.1.57" futures = { version = "0.3.21", features = ["thread-pool"] } futures-timer = "3.0.1" -libp2p = "0.50.0" +libp2p-identity = { version = "0.1.2", features = ["peerid", "ed25519"] } log = "0.4.17" mockall = "0.11.3" parking_lot = "0.12.1" diff --git a/client/consensus/common/src/import_queue.rs b/client/consensus/common/src/import_queue.rs index cec9aca47..11ebbd403 100644 --- a/client/consensus/common/src/import_queue.rs +++ b/client/consensus/common/src/import_queue.rs @@ -66,7 +66,7 @@ pub type BoxJustificationImport = Box + Send + Sync>; /// Maps to the RuntimeOrigin used by the network. -pub type RuntimeOrigin = libp2p::PeerId; +pub type RuntimeOrigin = libp2p_identity::PeerId; /// Block data used by the queue. #[derive(Debug, PartialEq, Eq, Clone)] diff --git a/client/consensus/common/src/import_queue/basic_queue.rs b/client/consensus/common/src/import_queue/basic_queue.rs index 653c88321..b93913703 100644 --- a/client/consensus/common/src/import_queue/basic_queue.rs +++ b/client/consensus/common/src/import_queue/basic_queue.rs @@ -635,7 +635,7 @@ mod tests { let hash = Hash::random(); finality_sender .unbounded_send(worker_messages::ImportJustification( - libp2p::PeerId::random(), + libp2p_identity::PeerId::random(), hash, 1, (*b"TEST", Vec::new()), diff --git a/client/network-gossip/Cargo.toml b/client/network-gossip/Cargo.toml index 5c1bc91f1..40277c946 100644 --- a/client/network-gossip/Cargo.toml +++ b/client/network-gossip/Cargo.toml @@ -17,7 +17,7 @@ targets = ["x86_64-unknown-linux-gnu"] ahash = "0.8.2" futures = "0.3.21" futures-timer = "3.0.1" -libp2p = "0.50.0" +libp2p = "0.51.3" log = "0.4.17" lru = "0.8.1" tracing = "0.1.29" diff --git a/client/network/Cargo.toml b/client/network/Cargo.toml index 6fc4131f7..de4c4c14a 100644 --- a/client/network/Cargo.toml +++ b/client/network/Cargo.toml @@ -25,7 +25,7 @@ fnv = "1.0.6" futures = "0.3.21" futures-timer = "3.0.2" ip_network = "0.4.1" -libp2p = { version = "0.50.0", features = ["dns", "identify", "kad", "macros", "mdns", "mplex", "noise", "ping", "tcp", "tokio", "yamux", "websocket"] } +libp2p = { version = "0.51.3", features = ["dns", "identify", "kad", "macros", "mdns", "noise", "ping", "tcp", "tokio", "yamux", "websocket", "request-response"] } linked_hash_set = "0.1.3" log = "0.4.17" lru = "0.8.1" diff --git a/client/network/README.md b/client/network/README.md index b9c0e028f..cad46d059 100644 --- a/client/network/README.md +++ b/client/network/README.md @@ -66,8 +66,6 @@ negotiated and applied. The exact handshake protocol is experimental and is subj The following multiplexing protocols are supported: -- [Mplex](https://github.com/libp2p/specs/tree/master/mplex). Support for mplex will likely -be deprecated in the future. - [Yamux](https://github.com/hashicorp/yamux/blob/master/spec.md). ## Substreams diff --git a/client/network/bitswap/Cargo.toml b/client/network/bitswap/Cargo.toml index ee2e0cfc7..a953676ec 100644 --- a/client/network/bitswap/Cargo.toml +++ b/client/network/bitswap/Cargo.toml @@ -18,7 +18,7 @@ prost-build = "0.11" [dependencies] cid = "0.8.6" futures = "0.3.21" -libp2p = "0.50.0" +libp2p-identity = { version = "0.1.2", features = ["peerid"] } log = "0.4.17" prost = "0.11" thiserror = "1.0" diff --git a/client/network/bitswap/src/lib.rs b/client/network/bitswap/src/lib.rs index db73023bd..a7857f6ee 100644 --- a/client/network/bitswap/src/lib.rs +++ b/client/network/bitswap/src/lib.rs @@ -22,7 +22,7 @@ use cid::{self, Version}; use futures::{channel::mpsc, StreamExt}; -use libp2p::core::PeerId; +use libp2p_identity::PeerId; use log::{debug, error, trace}; use prost::Message; use sc_client_api::BlockBackend; diff --git a/client/network/common/Cargo.toml b/client/network/common/Cargo.toml index 983342b01..d9769413b 100644 --- a/client/network/common/Cargo.toml +++ b/client/network/common/Cargo.toml @@ -25,7 +25,7 @@ codec = { package = "parity-scale-codec", version = "3.2.2", features = [ ] } futures = "0.3.21" futures-timer = "3.0.2" -libp2p = { version = "0.50.0", features = ["request-response", "kad"] } +libp2p-identity = { version = "0.1.2", features = ["peerid"] } prometheus-endpoint = { package = "substrate-prometheus-endpoint", version = "0.10.0-dev", path = "../../../utils/prometheus" } smallvec = "1.8.0" sc-consensus = { version = "0.10.0-dev", path = "../../consensus/common" } diff --git a/client/network/common/src/sync.rs b/client/network/common/src/sync.rs index b01091ae0..404a1aff9 100644 --- a/client/network/common/src/sync.rs +++ b/client/network/common/src/sync.rs @@ -25,7 +25,7 @@ pub mod warp; use crate::role::Roles; use futures::Stream; -use libp2p::PeerId; +use libp2p_identity::PeerId; use message::{BlockAnnounce, BlockData, BlockRequest, BlockResponse}; use sc_consensus::{import_queue::RuntimeOrigin, IncomingBlock}; diff --git a/client/network/light/Cargo.toml b/client/network/light/Cargo.toml index ed2a5d6cb..cd0dfbca5 100644 --- a/client/network/light/Cargo.toml +++ b/client/network/light/Cargo.toml @@ -21,7 +21,7 @@ codec = { package = "parity-scale-codec", version = "3.2.2", features = [ "derive", ] } futures = "0.3.21" -libp2p = "0.50.0" +libp2p-identity = { version = "0.1.2", features = ["peerid"] } log = "0.4.16" prost = "0.11" sp-blockchain = { version = "4.0.0-dev", path = "../../../primitives/blockchain" } diff --git a/client/network/light/src/light_client_requests/handler.rs b/client/network/light/src/light_client_requests/handler.rs index db2630b79..2a68ebe9c 100644 --- a/client/network/light/src/light_client_requests/handler.rs +++ b/client/network/light/src/light_client_requests/handler.rs @@ -25,7 +25,7 @@ use crate::schema; use codec::{self, Decode, Encode}; use futures::{channel::mpsc, prelude::*}; -use libp2p::PeerId; +use libp2p_identity::PeerId; use log::{debug, trace}; use prost::Message; use sc_client_api::{BlockBackend, ProofProvider}; diff --git a/client/network/src/behaviour.rs b/client/network/src/behaviour.rs index f06809992..ef967eee9 100644 --- a/client/network/src/behaviour.rs +++ b/client/network/src/behaviour.rs @@ -28,10 +28,8 @@ use crate::{ use bytes::Bytes; use futures::channel::oneshot; use libp2p::{ - core::{Multiaddr, PeerId, PublicKey}, - identify::Info as IdentifyInfo, - kad::record, - swarm::NetworkBehaviour, + core::Multiaddr, identify::Info as IdentifyInfo, identity::PublicKey, kad::RecordKey, + swarm::NetworkBehaviour, PeerId, }; use sc_network_common::role::{ObservedRole, Roles}; @@ -256,13 +254,13 @@ impl Behaviour { /// Start querying a record from the DHT. Will later produce either a `ValueFound` or a /// `ValueNotFound` event. - pub fn get_value(&mut self, key: record::Key) { + pub fn get_value(&mut self, key: RecordKey) { self.discovery.get_value(key); } /// Starts putting a record into DHT. Will later produce either a `ValuePut` or a /// `ValuePutFailed` event. - pub fn put_value(&mut self, key: record::Key, value: Vec) { + pub fn put_value(&mut self, key: RecordKey, value: Vec) { self.discovery.put_value(key, value); } } diff --git a/client/network/src/config.rs b/client/network/src/config.rs index e80de1382..17ca83356 100644 --- a/client/network/src/config.rs +++ b/client/network/src/config.rs @@ -29,8 +29,9 @@ pub use crate::{ types::ProtocolName, }; +pub use libp2p::{identity::Keypair, multiaddr, Multiaddr, PeerId}; + use codec::Encode; -use libp2p::{identity::Keypair, multiaddr, Multiaddr, PeerId}; use prometheus_endpoint::Registry; use zeroize::Zeroize; @@ -367,7 +368,7 @@ impl NodeKeyConfig { match self { Ed25519(Secret::New) => Ok(Keypair::generate_ed25519()), - Ed25519(Secret::Input(k)) => Ok(Keypair::Ed25519(k.into())), + Ed25519(Secret::Input(k)) => Ok(ed25519::Keypair::from(k).into()), Ed25519(Secret::File(f)) => get_secret( f, @@ -378,14 +379,14 @@ impl NodeKeyConfig { None } }) { - Some(s) => ed25519::SecretKey::from_bytes(s), - _ => ed25519::SecretKey::from_bytes(&mut b), + Some(s) => ed25519::SecretKey::try_from_bytes(s), + _ => ed25519::SecretKey::try_from_bytes(&mut b), }, ed25519::SecretKey::generate, |b| b.as_ref().to_vec(), ) .map(ed25519::Keypair::from) - .map(Keypair::Ed25519), + .map(Keypair::from), } } } @@ -769,9 +770,14 @@ mod tests { tempfile::Builder::new().prefix(prefix).tempdir().unwrap() } - fn secret_bytes(kp: &Keypair) -> Vec { - let Keypair::Ed25519(p) = kp; - p.secret().as_ref().iter().cloned().collect() + fn secret_bytes(kp: Keypair) -> Vec { + kp.try_into_ed25519() + .expect("ed25519 keypair") + .secret() + .as_ref() + .iter() + .cloned() + .collect() } #[test] @@ -781,7 +787,7 @@ mod tests { let file = tmp.path().join("x").to_path_buf(); let kp1 = NodeKeyConfig::Ed25519(Secret::File(file.clone())).into_keypair().unwrap(); let kp2 = NodeKeyConfig::Ed25519(Secret::File(file.clone())).into_keypair().unwrap(); - assert!(file.is_file() && secret_bytes(&kp1) == secret_bytes(&kp2)) + assert!(file.is_file() && secret_bytes(kp1) == secret_bytes(kp2)) } #[test] @@ -789,13 +795,13 @@ mod tests { let sk = ed25519::SecretKey::generate(); let kp1 = NodeKeyConfig::Ed25519(Secret::Input(sk.clone())).into_keypair().unwrap(); let kp2 = NodeKeyConfig::Ed25519(Secret::Input(sk)).into_keypair().unwrap(); - assert!(secret_bytes(&kp1) == secret_bytes(&kp2)); + assert!(secret_bytes(kp1) == secret_bytes(kp2)); } #[test] fn test_secret_new() { let kp1 = NodeKeyConfig::Ed25519(Secret::New).into_keypair().unwrap(); let kp2 = NodeKeyConfig::Ed25519(Secret::New).into_keypair().unwrap(); - assert!(secret_bytes(&kp1) != secret_bytes(&kp2)); + assert!(secret_bytes(kp1) != secret_bytes(kp2)); } } diff --git a/client/network/src/discovery.rs b/client/network/src/discovery.rs index 7100c0c70..708406bd1 100644 --- a/client/network/src/discovery.rs +++ b/client/network/src/discovery.rs @@ -53,26 +53,24 @@ use futures::prelude::*; use futures_timer::Delay; use ip_network::IpNetwork; use libp2p::{ - core::{connection::ConnectionId, Multiaddr, PeerId, PublicKey}, + core::{Endpoint, Multiaddr}, kad::{ - handler::KademliaHandlerProto, - record::{ - self, - store::{MemoryStore, RecordStore}, - }, + handler::KademliaHandler, + record::store::{MemoryStore, RecordStore}, GetClosestPeersError, GetRecordOk, Kademlia, KademliaBucketInserts, KademliaConfig, - KademliaEvent, QueryId, QueryResult, Quorum, Record, + KademliaEvent, QueryId, QueryResult, Quorum, Record, RecordKey, }, mdns::{self, tokio::Behaviour as TokioMdns}, multiaddr::Protocol, swarm::{ behaviour::{ - toggle::{Toggle, ToggleIntoConnectionHandler}, + toggle::{Toggle, ToggleConnectionHandler}, DialFailure, FromSwarm, NewExternalAddr, }, - ConnectionHandler, DialError, IntoConnectionHandler, NetworkBehaviour, - NetworkBehaviourAction, PollParameters, + ConnectionDenied, ConnectionId, DialError, NetworkBehaviour, PollParameters, THandler, + THandlerInEvent, THandlerOutEvent, ToSwarm, }, + PeerId, }; use log::{debug, info, trace, warn}; use sp_core::hexdisplay::HexDisplay; @@ -107,9 +105,9 @@ pub struct DiscoveryConfig { impl DiscoveryConfig { /// Create a default configuration with the given public key. - pub fn new(local_public_key: PublicKey) -> Self { + pub fn new(local_peer_id: PeerId) -> Self { Self { - local_peer_id: local_public_key.to_peer_id(), + local_peer_id, permanent_addresses: Vec::new(), dht_random_walk: true, allow_private_ip: true, @@ -235,7 +233,7 @@ impl DiscoveryConfig { allow_private_ip, discovery_only_if_under_num, mdns: if enable_mdns { - match TokioMdns::new(mdns::Config::default()) { + match TokioMdns::new(mdns::Config::default(), local_peer_id) { Ok(mdns) => Some(mdns), Err(err) => { warn!(target: "sub-libp2p", "Failed to initialize mDNS: {:?}", err); @@ -374,7 +372,7 @@ impl DiscoveryBehaviour { /// Start fetching a record from the DHT. /// /// A corresponding `ValueFound` or `ValueNotFound` event will later be generated. - pub fn get_value(&mut self, key: record::Key) { + pub fn get_value(&mut self, key: RecordKey) { if let Some(k) = self.kademlia.as_mut() { k.get_record(key.clone()); } @@ -384,7 +382,7 @@ impl DiscoveryBehaviour { /// `get_value`. /// /// A corresponding `ValuePut` or `ValuePutFailed` event will later be generated. - pub fn put_value(&mut self, key: record::Key, value: Vec) { + pub fn put_value(&mut self, key: RecordKey, value: Vec) { if let Some(k) = self.kademlia.as_mut() { if let Err(e) = k.put_record(Record::new(key.clone(), value.clone()), Quorum::All) { warn!(target: "sub-libp2p", "Libp2p => Failed to put record: {:?}", e); @@ -460,22 +458,22 @@ pub enum DiscoveryOut { /// The DHT yielded results for the record request. /// /// Returning the result grouped in (key, value) pairs as well as the request duration. - ValueFound(Vec<(record::Key, Vec)>, Duration), + ValueFound(Vec<(RecordKey, Vec)>, Duration), /// The record requested was not found in the DHT. /// /// Returning the corresponding key as well as the request duration. - ValueNotFound(record::Key, Duration), + ValueNotFound(RecordKey, Duration), /// The record with a given key was successfully inserted into the DHT. /// /// Returning the corresponding key as well as the request duration. - ValuePut(record::Key, Duration), + ValuePut(RecordKey, Duration), /// Inserting a value into the DHT failed. /// /// Returning the corresponding key as well as the request duration. - ValuePutFailed(record::Key, Duration), + ValuePutFailed(RecordKey, Duration), /// Started a random Kademlia query. /// @@ -484,29 +482,83 @@ pub enum DiscoveryOut { } impl NetworkBehaviour for DiscoveryBehaviour { - type ConnectionHandler = ToggleIntoConnectionHandler>; + type ConnectionHandler = ToggleConnectionHandler>; type OutEvent = DiscoveryOut; - fn new_handler(&mut self) -> Self::ConnectionHandler { - self.kademlia.new_handler() + fn handle_established_inbound_connection( + &mut self, + connection_id: ConnectionId, + peer: PeerId, + local_addr: &Multiaddr, + remote_addr: &Multiaddr, + ) -> Result, ConnectionDenied> { + self.kademlia.handle_established_inbound_connection( + connection_id, + peer, + local_addr, + remote_addr, + ) + } + + fn handle_established_outbound_connection( + &mut self, + connection_id: ConnectionId, + peer: PeerId, + addr: &Multiaddr, + role_override: Endpoint, + ) -> Result, ConnectionDenied> { + self.kademlia.handle_established_outbound_connection( + connection_id, + peer, + addr, + role_override, + ) + } + + fn handle_pending_inbound_connection( + &mut self, + connection_id: ConnectionId, + local_addr: &Multiaddr, + remote_addr: &Multiaddr, + ) -> Result<(), ConnectionDenied> { + self.kademlia + .handle_pending_inbound_connection(connection_id, local_addr, remote_addr) } - fn addresses_of_peer(&mut self, peer_id: &PeerId) -> Vec { + fn handle_pending_outbound_connection( + &mut self, + connection_id: ConnectionId, + maybe_peer: Option, + addresses: &[Multiaddr], + effective_role: Endpoint, + ) -> Result, ConnectionDenied> { + let Some(peer_id) = maybe_peer else { return Ok(Vec::new()); }; + let mut list = self .permanent_addresses .iter() - .filter_map(|(p, a)| if p == peer_id { Some(a.clone()) } else { None }) + .filter_map(|(p, a)| (*p == peer_id).then_some(a.clone())) .collect::>(); - if let Some(ephemeral_addresses) = self.ephemeral_addresses.get(peer_id) { + if let Some(ephemeral_addresses) = self.ephemeral_addresses.get(&peer_id) { list.extend(ephemeral_addresses.clone()); } { - let mut list_to_filter = self.kademlia.addresses_of_peer(peer_id); + let mut list_to_filter = self.kademlia.handle_pending_outbound_connection( + connection_id, + maybe_peer, + addresses, + effective_role, + )?; if let Some(ref mut mdns) = self.mdns { - list_to_filter.extend(mdns.addresses_of_peer(peer_id)); + list_to_filter.extend(mdns.handle_pending_outbound_connection( + connection_id, + maybe_peer, + addresses, + effective_role, + )?); } if !self.allow_private_ip { @@ -522,7 +574,7 @@ impl NetworkBehaviour for DiscoveryBehaviour { trace!(target: "sub-libp2p", "Addresses of {:?}: {:?}", peer_id, list); - list + Ok(list) } fn on_swarm_event(&mut self, event: FromSwarm) { @@ -603,8 +655,7 @@ impl NetworkBehaviour for DiscoveryBehaviour { &mut self, peer_id: PeerId, connection_id: ConnectionId, - event: <::Handler as - ConnectionHandler>::OutEvent, + event: THandlerOutEvent, ) { self.kademlia.on_connection_handler_event(peer_id, connection_id, event); } @@ -613,10 +664,10 @@ impl NetworkBehaviour for DiscoveryBehaviour { &mut self, cx: &mut Context, params: &mut impl PollParameters, - ) -> Poll> { + ) -> Poll>> { // Immediately process the content of `discovered`. if let Some(ev) = self.pending_events.pop_front() { - return Poll::Ready(NetworkBehaviourAction::GenerateEvent(ev)) + return Poll::Ready(ToSwarm::GenerateEvent(ev)) } // Poll the stream that fires when we need to start a random Kademlia query. @@ -650,7 +701,7 @@ impl NetworkBehaviour for DiscoveryBehaviour { if actually_started { let ev = DiscoveryOut::RandomKademliaStarted; - return Poll::Ready(NetworkBehaviourAction::GenerateEvent(ev)) + return Poll::Ready(ToSwarm::GenerateEvent(ev)) } } } @@ -658,18 +709,18 @@ impl NetworkBehaviour for DiscoveryBehaviour { while let Poll::Ready(ev) = self.kademlia.poll(cx, params) { match ev { - NetworkBehaviourAction::GenerateEvent(ev) => match ev { + ToSwarm::GenerateEvent(ev) => match ev { KademliaEvent::RoutingUpdated { peer, .. } => { let ev = DiscoveryOut::Discovered(peer); - return Poll::Ready(NetworkBehaviourAction::GenerateEvent(ev)) + return Poll::Ready(ToSwarm::GenerateEvent(ev)) }, KademliaEvent::UnroutablePeer { peer, .. } => { let ev = DiscoveryOut::UnroutablePeer(peer); - return Poll::Ready(NetworkBehaviourAction::GenerateEvent(ev)) + return Poll::Ready(ToSwarm::GenerateEvent(ev)) }, KademliaEvent::RoutablePeer { peer, .. } => { let ev = DiscoveryOut::Discovered(peer); - return Poll::Ready(NetworkBehaviourAction::GenerateEvent(ev)) + return Poll::Ready(ToSwarm::GenerateEvent(ev)) }, KademliaEvent::PendingRoutablePeer { .. } | KademliaEvent::InboundRequest { .. } => { @@ -777,7 +828,7 @@ impl NetworkBehaviour for DiscoveryBehaviour { ) }, }; - return Poll::Ready(NetworkBehaviourAction::GenerateEvent(ev)) + return Poll::Ready(ToSwarm::GenerateEvent(ev)) }, KademliaEvent::OutboundQueryProgressed { result: QueryResult::PutRecord(res), @@ -799,7 +850,7 @@ impl NetworkBehaviour for DiscoveryBehaviour { ) }, }; - return Poll::Ready(NetworkBehaviourAction::GenerateEvent(ev)) + return Poll::Ready(ToSwarm::GenerateEvent(ev)) }, KademliaEvent::OutboundQueryProgressed { result: QueryResult::RepublishRecord(res), @@ -821,24 +872,13 @@ impl NetworkBehaviour for DiscoveryBehaviour { warn!(target: "sub-libp2p", "Libp2p => Unhandled Kademlia event: {:?}", e) }, }, - NetworkBehaviourAction::Dial { opts, handler } => - return Poll::Ready(NetworkBehaviourAction::Dial { opts, handler }), - NetworkBehaviourAction::NotifyHandler { peer_id, handler, event } => - return Poll::Ready(NetworkBehaviourAction::NotifyHandler { - peer_id, - handler, - event, - }), - NetworkBehaviourAction::ReportObservedAddr { address, score } => - return Poll::Ready(NetworkBehaviourAction::ReportObservedAddr { - address, - score, - }), - NetworkBehaviourAction::CloseConnection { peer_id, connection } => - return Poll::Ready(NetworkBehaviourAction::CloseConnection { - peer_id, - connection, - }), + ToSwarm::Dial { opts } => return Poll::Ready(ToSwarm::Dial { opts }), + ToSwarm::NotifyHandler { peer_id, handler, event } => + return Poll::Ready(ToSwarm::NotifyHandler { peer_id, handler, event }), + ToSwarm::ReportObservedAddr { address, score } => + return Poll::Ready(ToSwarm::ReportObservedAddr { address, score }), + ToSwarm::CloseConnection { peer_id, connection } => + return Poll::Ready(ToSwarm::CloseConnection { peer_id, connection }), } } @@ -846,7 +886,7 @@ impl NetworkBehaviour for DiscoveryBehaviour { if let Some(ref mut mdns) = self.mdns { while let Poll::Ready(ev) = mdns.poll(cx, params) { match ev { - NetworkBehaviourAction::GenerateEvent(event) => match event { + ToSwarm::GenerateEvent(event) => match event { mdns::Event::Discovered(list) => { if self.num_connections >= self.discovery_only_if_under_num { continue @@ -855,25 +895,21 @@ impl NetworkBehaviour for DiscoveryBehaviour { self.pending_events .extend(list.map(|(peer_id, _)| DiscoveryOut::Discovered(peer_id))); if let Some(ev) = self.pending_events.pop_front() { - return Poll::Ready(NetworkBehaviourAction::GenerateEvent(ev)) + return Poll::Ready(ToSwarm::GenerateEvent(ev)) } }, mdns::Event::Expired(_) => {}, }, - NetworkBehaviourAction::Dial { .. } => { + ToSwarm::Dial { .. } => { unreachable!("mDNS never dials!"); }, - NetworkBehaviourAction::NotifyHandler { event, .. } => match event {}, /* `event` is an enum with no variant */ - NetworkBehaviourAction::ReportObservedAddr { address, score } => - return Poll::Ready(NetworkBehaviourAction::ReportObservedAddr { - address, - score, - }), - NetworkBehaviourAction::CloseConnection { peer_id, connection } => - return Poll::Ready(NetworkBehaviourAction::CloseConnection { - peer_id, - connection, - }), + ToSwarm::NotifyHandler { event, .. } => match event {}, /* `event` is an */ + // enum with no + // variant + ToSwarm::ReportObservedAddr { address, score } => + return Poll::Ready(ToSwarm::ReportObservedAddr { address, score }), + ToSwarm::CloseConnection { peer_id, connection } => + return Poll::Ready(ToSwarm::CloseConnection { peer_id, connection }), } } } @@ -912,9 +948,9 @@ mod tests { transport::{MemoryTransport, Transport}, upgrade, }, - identity::{ed25519, Keypair}, + identity::Keypair, noise, - swarm::{Executor, Swarm, SwarmEvent}, + swarm::{Executor, Swarm, SwarmBuilder, SwarmEvent}, yamux, Multiaddr, }; use sp_core::hash::H256; @@ -941,17 +977,14 @@ mod tests { .map(|i| { let keypair = Keypair::generate_ed25519(); - let noise_keys = - noise::Keypair::::new().into_authentic(&keypair).unwrap(); - let transport = MemoryTransport::new() .upgrade(upgrade::Version::V1) - .authenticate(noise::NoiseConfig::xx(noise_keys).into_authenticated()) - .multiplex(yamux::YamuxConfig::default()) + .authenticate(noise::Config::new(&keypair).unwrap()) + .multiplex(yamux::Config::default()) .boxed(); let behaviour = { - let mut config = DiscoveryConfig::new(keypair.public()); + let mut config = DiscoveryConfig::new(keypair.public().to_peer_id()); config .with_permanent_addresses(first_swarm_peer_id_and_addr.clone()) .allow_private_ip(true) @@ -963,12 +996,13 @@ mod tests { }; let runtime = tokio::runtime::Runtime::new().unwrap(); - let mut swarm = Swarm::with_executor( + let mut swarm = SwarmBuilder::with_executor( transport, behaviour, keypair.public().to_peer_id(), TokioExecutor(runtime), - ); + ) + .build(); let listen_addr: Multiaddr = format!("/memory/{}", rand::random::()).parse().unwrap(); @@ -1070,7 +1104,7 @@ mod tests { let mut discovery = { let keypair = Keypair::generate_ed25519(); - let mut config = DiscoveryConfig::new(keypair.public()); + let mut config = DiscoveryConfig::new(keypair.public().to_peer_id()); config .allow_private_ip(true) .allow_non_globals_in_dht(true) @@ -1080,11 +1114,7 @@ mod tests { }; let predictable_peer_id = |bytes: &[u8; 32]| { - Keypair::Ed25519(ed25519::Keypair::from( - ed25519::SecretKey::from_bytes(bytes.to_owned()).unwrap(), - )) - .public() - .to_peer_id() + Keypair::ed25519_from_bytes(bytes.to_owned()).unwrap().public().to_peer_id() }; let remote_peer_id = predictable_peer_id(b"00000000000000000000000000000001"); diff --git a/client/network/src/event.rs b/client/network/src/event.rs index 975fde0e4..9c1034ea3 100644 --- a/client/network/src/event.rs +++ b/client/network/src/event.rs @@ -23,7 +23,7 @@ use crate::{types::ProtocolName, NotificationsSink}; use bytes::Bytes; use futures::channel::oneshot; -use libp2p::{core::PeerId, kad::record::Key}; +use libp2p::{kad::record::Key, PeerId}; use sc_network_common::{role::ObservedRole, sync::message::BlockAnnouncesHandshake}; use sp_runtime::traits::Block as BlockT; diff --git a/client/network/src/lib.rs b/client/network/src/lib.rs index 5374ac134..79023923e 100644 --- a/client/network/src/lib.rs +++ b/client/network/src/lib.rs @@ -87,8 +87,6 @@ //! //! The following multiplexing protocols are supported: //! -//! - [Mplex](https://github.com/libp2p/specs/tree/master/mplex). Support for mplex will likely -//! be deprecated in the future. //! - [Yamux](https://github.com/hashicorp/yamux/blob/master/spec.md). //! //! ## Substreams @@ -262,7 +260,7 @@ pub mod utils; pub use event::{DhtEvent, Event, SyncEvent}; #[doc(inline)] pub use libp2p::{multiaddr, Multiaddr, PeerId}; -pub use request_responses::{IfDisconnected, RequestFailure, RequestResponseConfig}; +pub use request_responses::{Config, IfDisconnected, RequestFailure}; pub use sc_network_common::{ role::ObservedRole, sync::{ diff --git a/client/network/src/peer_info.rs b/client/network/src/peer_info.rs index 3f769736f..e4a5c5753 100644 --- a/client/network/src/peer_info.rs +++ b/client/network/src/peer_info.rs @@ -17,25 +17,27 @@ // along with this program. If not, see . use crate::utils::interval; +use either::Either; use fnv::FnvHashMap; use futures::prelude::*; use libp2p::{ - core::{connection::ConnectionId, either::EitherOutput, ConnectedPoint, PeerId, PublicKey}, + core::{ConnectedPoint, Endpoint}, identify::{ Behaviour as Identify, Config as IdentifyConfig, Event as IdentifyEvent, Info as IdentifyInfo, }, + identity::PublicKey, ping::{Behaviour as Ping, Config as PingConfig, Event as PingEvent, Success as PingSuccess}, swarm::{ behaviour::{ AddressChange, ConnectionClosed, ConnectionEstablished, DialFailure, FromSwarm, ListenFailure, }, - ConnectionHandler, IntoConnectionHandler, IntoConnectionHandlerSelect, NetworkBehaviour, - NetworkBehaviourAction, PollParameters, + ConnectionDenied, ConnectionHandler, ConnectionId, IntoConnectionHandlerSelect, + NetworkBehaviour, PollParameters, THandler, THandlerInEvent, THandlerOutEvent, ToSwarm, }, - Multiaddr, + Multiaddr, PeerId, }; use log::{debug, error, trace}; use smallvec::SmallVec; @@ -182,14 +184,72 @@ impl NetworkBehaviour for PeerInfoBehaviour { >; type OutEvent = PeerInfoEvent; - fn new_handler(&mut self) -> Self::ConnectionHandler { - IntoConnectionHandler::select(self.ping.new_handler(), self.identify.new_handler()) + fn handle_pending_inbound_connection( + &mut self, + connection_id: ConnectionId, + local_addr: &Multiaddr, + remote_addr: &Multiaddr, + ) -> Result<(), ConnectionDenied> { + self.ping + .handle_pending_inbound_connection(connection_id, local_addr, remote_addr)?; + self.identify + .handle_pending_inbound_connection(connection_id, local_addr, remote_addr) } - fn addresses_of_peer(&mut self, _: &PeerId) -> Vec { - // Only `Discovery::addresses_of_peer` must be returning addresses to ensure that we - // don't return unwanted addresses. - Vec::new() + fn handle_pending_outbound_connection( + &mut self, + _connection_id: ConnectionId, + _maybe_peer: Option, + _addresses: &[Multiaddr], + _effective_role: Endpoint, + ) -> Result, ConnectionDenied> { + // Only `Discovery::handle_pending_outbound_connection` must be returning addresses to + // ensure that we don't return unwanted addresses. + Ok(Vec::new()) + } + + fn handle_established_inbound_connection( + &mut self, + connection_id: ConnectionId, + peer: PeerId, + local_addr: &Multiaddr, + remote_addr: &Multiaddr, + ) -> Result, ConnectionDenied> { + let ping_handler = self.ping.handle_established_inbound_connection( + connection_id, + peer, + local_addr, + remote_addr, + )?; + let identify_handler = self.identify.handle_established_inbound_connection( + connection_id, + peer, + local_addr, + remote_addr, + )?; + Ok(ping_handler.select(identify_handler)) + } + + fn handle_established_outbound_connection( + &mut self, + connection_id: ConnectionId, + peer: PeerId, + addr: &Multiaddr, + role_override: Endpoint, + ) -> Result, ConnectionDenied> { + let ping_handler = self.ping.handle_established_outbound_connection( + connection_id, + peer, + addr, + role_override, + )?; + let identify_handler = self.identify.handle_established_outbound_connection( + connection_id, + peer, + addr, + role_override, + )?; + Ok(ping_handler.select(identify_handler)) } fn on_swarm_event(&mut self, event: FromSwarm) { @@ -249,34 +309,39 @@ impl NetworkBehaviour for PeerInfoBehaviour { "Unknown connection to {:?} closed: {:?}", peer_id, endpoint); } }, - FromSwarm::DialFailure(DialFailure { peer_id, handler, error }) => { - let (ping_handler, identity_handler) = handler.into_inner(); + FromSwarm::DialFailure(DialFailure { peer_id, error, connection_id }) => { self.ping.on_swarm_event(FromSwarm::DialFailure(DialFailure { peer_id, - handler: ping_handler, error, + connection_id, })); self.identify.on_swarm_event(FromSwarm::DialFailure(DialFailure { peer_id, - handler: identity_handler, error, + connection_id, })); }, FromSwarm::ListenerClosed(e) => { self.ping.on_swarm_event(FromSwarm::ListenerClosed(e)); self.identify.on_swarm_event(FromSwarm::ListenerClosed(e)); }, - FromSwarm::ListenFailure(ListenFailure { local_addr, send_back_addr, handler }) => { - let (ping_handler, identity_handler) = handler.into_inner(); + FromSwarm::ListenFailure(ListenFailure { + local_addr, + send_back_addr, + error, + connection_id, + }) => { self.ping.on_swarm_event(FromSwarm::ListenFailure(ListenFailure { local_addr, send_back_addr, - handler: ping_handler, + error, + connection_id, })); self.identify.on_swarm_event(FromSwarm::ListenFailure(ListenFailure { local_addr, send_back_addr, - handler: identity_handler, + error, + connection_id, })); }, FromSwarm::ListenerError(e) => { @@ -326,13 +391,12 @@ impl NetworkBehaviour for PeerInfoBehaviour { &mut self, peer_id: PeerId, connection_id: ConnectionId, - event: <::Handler as - ConnectionHandler>::OutEvent, + event: THandlerOutEvent, ) { match event { - EitherOutput::First(event) => + Either::Left(event) => self.ping.on_connection_handler_event(peer_id, connection_id, event), - EitherOutput::Second(event) => + Either::Right(event) => self.identify.on_connection_handler_event(peer_id, connection_id, event), } } @@ -341,47 +405,37 @@ impl NetworkBehaviour for PeerInfoBehaviour { &mut self, cx: &mut Context, params: &mut impl PollParameters, - ) -> Poll> { + ) -> Poll>> { loop { match self.ping.poll(cx, params) { Poll::Pending => break, - Poll::Ready(NetworkBehaviourAction::GenerateEvent(ev)) => { + Poll::Ready(ToSwarm::GenerateEvent(ev)) => { if let PingEvent { peer, result: Ok(PingSuccess::Ping { rtt }) } = ev { self.handle_ping_report(&peer, rtt) } }, - Poll::Ready(NetworkBehaviourAction::Dial { opts, handler }) => { - let handler = - IntoConnectionHandler::select(handler, self.identify.new_handler()); - return Poll::Ready(NetworkBehaviourAction::Dial { opts, handler }) - }, - Poll::Ready(NetworkBehaviourAction::NotifyHandler { peer_id, handler, event }) => - return Poll::Ready(NetworkBehaviourAction::NotifyHandler { + Poll::Ready(ToSwarm::Dial { opts }) => return Poll::Ready(ToSwarm::Dial { opts }), + Poll::Ready(ToSwarm::NotifyHandler { peer_id, handler, event }) => + return Poll::Ready(ToSwarm::NotifyHandler { peer_id, handler, - event: EitherOutput::First(event), - }), - Poll::Ready(NetworkBehaviourAction::ReportObservedAddr { address, score }) => - return Poll::Ready(NetworkBehaviourAction::ReportObservedAddr { - address, - score, - }), - Poll::Ready(NetworkBehaviourAction::CloseConnection { peer_id, connection }) => - return Poll::Ready(NetworkBehaviourAction::CloseConnection { - peer_id, - connection, + event: Either::Left(event), }), + Poll::Ready(ToSwarm::ReportObservedAddr { address, score }) => + return Poll::Ready(ToSwarm::ReportObservedAddr { address, score }), + Poll::Ready(ToSwarm::CloseConnection { peer_id, connection }) => + return Poll::Ready(ToSwarm::CloseConnection { peer_id, connection }), } } loop { match self.identify.poll(cx, params) { Poll::Pending => break, - Poll::Ready(NetworkBehaviourAction::GenerateEvent(event)) => match event { + Poll::Ready(ToSwarm::GenerateEvent(event)) => match event { IdentifyEvent::Received { peer_id, info, .. } => { self.handle_identify_report(&peer_id, &info); let event = PeerInfoEvent::Identified { peer_id, info }; - return Poll::Ready(NetworkBehaviourAction::GenerateEvent(event)) + return Poll::Ready(ToSwarm::GenerateEvent(event)) }, IdentifyEvent::Error { peer_id, error } => { debug!(target: "sub-libp2p", "Identification with peer {:?} failed => {}", peer_id, error) @@ -389,26 +443,17 @@ impl NetworkBehaviour for PeerInfoBehaviour { IdentifyEvent::Pushed { .. } => {}, IdentifyEvent::Sent { .. } => {}, }, - Poll::Ready(NetworkBehaviourAction::Dial { opts, handler }) => { - let handler = IntoConnectionHandler::select(self.ping.new_handler(), handler); - return Poll::Ready(NetworkBehaviourAction::Dial { opts, handler }) - }, - Poll::Ready(NetworkBehaviourAction::NotifyHandler { peer_id, handler, event }) => - return Poll::Ready(NetworkBehaviourAction::NotifyHandler { + Poll::Ready(ToSwarm::Dial { opts }) => return Poll::Ready(ToSwarm::Dial { opts }), + Poll::Ready(ToSwarm::NotifyHandler { peer_id, handler, event }) => + return Poll::Ready(ToSwarm::NotifyHandler { peer_id, handler, - event: EitherOutput::Second(event), - }), - Poll::Ready(NetworkBehaviourAction::ReportObservedAddr { address, score }) => - return Poll::Ready(NetworkBehaviourAction::ReportObservedAddr { - address, - score, - }), - Poll::Ready(NetworkBehaviourAction::CloseConnection { peer_id, connection }) => - return Poll::Ready(NetworkBehaviourAction::CloseConnection { - peer_id, - connection, + event: Either::Right(event), }), + Poll::Ready(ToSwarm::ReportObservedAddr { address, score }) => + return Poll::Ready(ToSwarm::ReportObservedAddr { address, score }), + Poll::Ready(ToSwarm::CloseConnection { peer_id, connection }) => + return Poll::Ready(ToSwarm::CloseConnection { peer_id, connection }), } } diff --git a/client/network/src/protocol.rs b/client/network/src/protocol.rs index 29a90c0bc..e7214d814 100644 --- a/client/network/src/protocol.rs +++ b/client/network/src/protocol.rs @@ -26,10 +26,10 @@ use bytes::Bytes; use codec::{DecodeAll, Encode}; use futures::{channel::oneshot, stream::FuturesUnordered, StreamExt}; use libp2p::{ - core::connection::ConnectionId, + core::Endpoint, swarm::{ - behaviour::FromSwarm, ConnectionHandler, IntoConnectionHandler, NetworkBehaviour, - NetworkBehaviourAction, PollParameters, + behaviour::FromSwarm, ConnectionDenied, ConnectionId, NetworkBehaviour, PollParameters, + THandler, THandlerInEvent, THandlerOutEvent, ToSwarm, }, Multiaddr, PeerId, }; @@ -378,14 +378,46 @@ impl NetworkBehaviour for Protocol { type ConnectionHandler = ::ConnectionHandler; type OutEvent = CustomMessageOutcome; - fn new_handler(&mut self) -> Self::ConnectionHandler { - self.behaviour.new_handler() + fn handle_established_inbound_connection( + &mut self, + connection_id: ConnectionId, + peer: PeerId, + local_addr: &Multiaddr, + remote_addr: &Multiaddr, + ) -> Result, ConnectionDenied> { + self.behaviour.handle_established_inbound_connection( + connection_id, + peer, + local_addr, + remote_addr, + ) } - fn addresses_of_peer(&mut self, _: &PeerId) -> Vec { - // Only `Discovery::addresses_of_peer` must be returning addresses to ensure that we - // don't return unwanted addresses. - Vec::new() + fn handle_established_outbound_connection( + &mut self, + connection_id: ConnectionId, + peer: PeerId, + addr: &Multiaddr, + role_override: Endpoint, + ) -> Result, ConnectionDenied> { + self.behaviour.handle_established_outbound_connection( + connection_id, + peer, + addr, + role_override, + ) + } + + fn handle_pending_outbound_connection( + &mut self, + _connection_id: ConnectionId, + _maybe_peer: Option, + _addresses: &[Multiaddr], + _effective_role: Endpoint, + ) -> Result, ConnectionDenied> { + // Only `Discovery::handle_pending_outbound_connection` must be returning addresses to + // ensure that we don't return unwanted addresses. + Ok(Vec::new()) } fn on_swarm_event(&mut self, event: FromSwarm) { @@ -396,8 +428,7 @@ impl NetworkBehaviour for Protocol { &mut self, peer_id: PeerId, connection_id: ConnectionId, - event: <::Handler as - ConnectionHandler>::OutEvent, + event: THandlerOutEvent, ) { self.behaviour.on_connection_handler_event(peer_id, connection_id, event); } @@ -406,7 +437,7 @@ impl NetworkBehaviour for Protocol { &mut self, cx: &mut std::task::Context, params: &mut impl PollParameters, - ) -> Poll> { + ) -> Poll>> { while let Poll::Ready(Some(validation_result)) = self.sync_substream_validations.poll_next_unpin(cx) { @@ -426,19 +457,14 @@ impl NetworkBehaviour for Protocol { let event = match self.behaviour.poll(cx, params) { Poll::Pending => return Poll::Pending, - Poll::Ready(NetworkBehaviourAction::GenerateEvent(ev)) => ev, - Poll::Ready(NetworkBehaviourAction::Dial { opts, handler }) => - return Poll::Ready(NetworkBehaviourAction::Dial { opts, handler }), - Poll::Ready(NetworkBehaviourAction::NotifyHandler { peer_id, handler, event }) => - return Poll::Ready(NetworkBehaviourAction::NotifyHandler { - peer_id, - handler, - event, - }), - Poll::Ready(NetworkBehaviourAction::ReportObservedAddr { address, score }) => - return Poll::Ready(NetworkBehaviourAction::ReportObservedAddr { address, score }), - Poll::Ready(NetworkBehaviourAction::CloseConnection { peer_id, connection }) => - return Poll::Ready(NetworkBehaviourAction::CloseConnection { peer_id, connection }), + Poll::Ready(ToSwarm::GenerateEvent(ev)) => ev, + Poll::Ready(ToSwarm::Dial { opts }) => return Poll::Ready(ToSwarm::Dial { opts }), + Poll::Ready(ToSwarm::NotifyHandler { peer_id, handler, event }) => + return Poll::Ready(ToSwarm::NotifyHandler { peer_id, handler, event }), + Poll::Ready(ToSwarm::ReportObservedAddr { address, score }) => + return Poll::Ready(ToSwarm::ReportObservedAddr { address, score }), + Poll::Ready(ToSwarm::CloseConnection { peer_id, connection }) => + return Poll::Ready(ToSwarm::CloseConnection { peer_id, connection }), }; let outcome = match event { @@ -634,7 +660,7 @@ impl NetworkBehaviour for Protocol { }; if !matches!(outcome, CustomMessageOutcome::None) { - return Poll::Ready(NetworkBehaviourAction::GenerateEvent(outcome)) + return Poll::Ready(ToSwarm::GenerateEvent(outcome)) } // This block can only be reached if an event was pulled from the behaviour and that diff --git a/client/network/src/protocol/notifications/behaviour.rs b/client/network/src/protocol/notifications/behaviour.rs index 9e9338938..7e5679393 100644 --- a/client/network/src/protocol/notifications/behaviour.rs +++ b/client/network/src/protocol/notifications/behaviour.rs @@ -18,7 +18,7 @@ use crate::{ protocol::notifications::handler::{ - self, NotificationsSink, NotifsHandlerIn, NotifsHandlerOut, NotifsHandlerProto, + self, NotificationsSink, NotifsHandler, NotifsHandlerIn, NotifsHandlerOut, }, types::ProtocolName, }; @@ -27,13 +27,13 @@ use bytes::BytesMut; use fnv::FnvHashMap; use futures::prelude::*; use libp2p::{ - core::{connection::ConnectionId, Multiaddr, PeerId}, + core::{ConnectedPoint, Endpoint, Multiaddr}, swarm::{ behaviour::{ConnectionClosed, ConnectionEstablished, DialFailure, FromSwarm}, - handler::ConnectionHandler, - DialError, IntoConnectionHandler, NetworkBehaviour, NetworkBehaviourAction, NotifyHandler, - PollParameters, + ConnectionDenied, ConnectionId, DialError, NetworkBehaviour, NotifyHandler, PollParameters, + THandler, THandlerInEvent, THandlerOutEvent, ToSwarm, }, + PeerId, }; use log::{error, trace, warn}; use parking_lot::RwLock; @@ -136,7 +136,7 @@ pub struct Notifications { next_incoming_index: sc_peerset::IncomingIndex, /// Events to produce from `poll()`. - events: VecDeque>, + events: VecDeque>, } /// Configuration for a notifications protocol. @@ -454,14 +454,14 @@ impl Notifications { trace!(target: "sub-libp2p", "External API <= Closed({}, {:?})", peer_id, set_id); let event = NotificationsOut::CustomProtocolClosed { peer_id: *peer_id, set_id }; - self.events.push_back(NetworkBehaviourAction::GenerateEvent(event)); + self.events.push_back(ToSwarm::GenerateEvent(event)); } for (connec_id, connec_state) in connections.iter_mut().filter(|(_, s)| matches!(s, ConnectionState::Open(_))) { trace!(target: "sub-libp2p", "Handler({:?}, {:?}) <= Close({:?})", peer_id, *connec_id, set_id); - self.events.push_back(NetworkBehaviourAction::NotifyHandler { + self.events.push_back(ToSwarm::NotifyHandler { peer_id: *peer_id, handler: NotifyHandler::One(*connec_id), event: NotifsHandlerIn::Close { protocol_index: set_id.into() }, @@ -473,7 +473,7 @@ impl Notifications { connections.iter_mut().filter(|(_, s)| matches!(s, ConnectionState::Opening)) { trace!(target: "sub-libp2p", "Handler({:?}, {:?}) <= Close({:?})", peer_id, *connec_id, set_id); - self.events.push_back(NetworkBehaviourAction::NotifyHandler { + self.events.push_back(ToSwarm::NotifyHandler { peer_id: *peer_id, handler: NotifyHandler::One(*connec_id), event: NotifsHandlerIn::Close { protocol_index: set_id.into() }, @@ -515,7 +515,7 @@ impl Notifications { .filter(|(_, s)| matches!(s, ConnectionState::OpenDesiredByRemote)) { trace!(target: "sub-libp2p", "Handler({:?}, {:?}) <= Close({:?})", peer_id, *connec_id, set_id); - self.events.push_back(NetworkBehaviourAction::NotifyHandler { + self.events.push_back(ToSwarm::NotifyHandler { peer_id: *peer_id, handler: NotifyHandler::One(*connec_id), event: NotifsHandlerIn::Close { protocol_index: set_id.into() }, @@ -548,7 +548,6 @@ impl Notifications { /// Function that is called when the peerset wants us to connect to a peer. fn peerset_report_connect(&mut self, peer_id: PeerId, set_id: sc_peerset::SetId) { // If `PeerId` is unknown to us, insert an entry, start dialing, and return early. - let handler = self.new_handler(); let mut occ_entry = match self.peers.entry((peer_id, set_id)) { Entry::Occupied(entry) => entry, Entry::Vacant(entry) => { @@ -560,10 +559,7 @@ impl Notifications { set_id, ); trace!(target: "sub-libp2p", "Libp2p <= Dial {}", entry.key().0); - self.events.push_back(NetworkBehaviourAction::Dial { - opts: entry.key().0.into(), - handler, - }); + self.events.push_back(ToSwarm::Dial { opts: entry.key().0.into() }); entry.insert(PeerState::Requested); return }, @@ -595,10 +591,7 @@ impl Notifications { set_id, ); trace!(target: "sub-libp2p", "Libp2p <= Dial {:?}", occ_entry.key()); - self.events.push_back(NetworkBehaviourAction::Dial { - opts: occ_entry.key().0.into(), - handler, - }); + self.events.push_back(ToSwarm::Dial { opts: occ_entry.key().0.into() }); *occ_entry.into_mut() = PeerState::Requested; }, @@ -646,7 +639,7 @@ impl Notifications { trace!(target: "sub-libp2p", "PSM => Connect({}, {:?}): Enabling connections.", occ_entry.key().0, set_id); trace!(target: "sub-libp2p", "Handler({:?}, {:?}) <= Open({:?})", peer_id, *connec_id, set_id); - self.events.push_back(NetworkBehaviourAction::NotifyHandler { + self.events.push_back(ToSwarm::NotifyHandler { peer_id, handler: NotifyHandler::One(*connec_id), event: NotifsHandlerIn::Open { protocol_index: set_id.into() }, @@ -720,7 +713,7 @@ impl Notifications { { trace!(target: "sub-libp2p", "Handler({:?}, {:?}) <= Open({:?})", occ_entry.key(), *connec_id, set_id); - self.events.push_back(NetworkBehaviourAction::NotifyHandler { + self.events.push_back(ToSwarm::NotifyHandler { peer_id: occ_entry.key().0, handler: NotifyHandler::One(*connec_id), event: NotifsHandlerIn::Open { protocol_index: set_id.into() }, @@ -803,7 +796,7 @@ impl Notifications { trace!(target: "sub-libp2p", "External API <= Closed({}, {:?})", entry.key().0, set_id); let event = NotificationsOut::CustomProtocolClosed { peer_id: entry.key().0, set_id }; - self.events.push_back(NetworkBehaviourAction::GenerateEvent(event)); + self.events.push_back(ToSwarm::GenerateEvent(event)); } for (connec_id, connec_state) in @@ -811,7 +804,7 @@ impl Notifications { { trace!(target: "sub-libp2p", "Handler({:?}, {:?}) <= Close({:?})", entry.key(), *connec_id, set_id); - self.events.push_back(NetworkBehaviourAction::NotifyHandler { + self.events.push_back(ToSwarm::NotifyHandler { peer_id: entry.key().0, handler: NotifyHandler::One(*connec_id), event: NotifsHandlerIn::Close { protocol_index: set_id.into() }, @@ -824,7 +817,7 @@ impl Notifications { { trace!(target: "sub-libp2p", "Handler({:?}, {:?}) <= Close({:?})", entry.key(), *connec_id, set_id); - self.events.push_back(NetworkBehaviourAction::NotifyHandler { + self.events.push_back(ToSwarm::NotifyHandler { peer_id: entry.key().0, handler: NotifyHandler::One(*connec_id), event: NotifsHandlerIn::Close { protocol_index: set_id.into() }, @@ -915,7 +908,7 @@ impl Notifications { { trace!(target: "sub-libp2p", "Handler({:?}, {:?}) <= Open({:?})", incoming.peer_id, *connec_id, incoming.set_id); - self.events.push_back(NetworkBehaviourAction::NotifyHandler { + self.events.push_back(ToSwarm::NotifyHandler { peer_id: incoming.peer_id, handler: NotifyHandler::One(*connec_id), event: NotifsHandlerIn::Open { protocol_index: incoming.set_id.into() }, @@ -975,7 +968,7 @@ impl Notifications { { trace!(target: "sub-libp2p", "Handler({:?}, {:?}) <= Close({:?})", incoming.peer_id, connec_id, incoming.set_id); - self.events.push_back(NetworkBehaviourAction::NotifyHandler { + self.events.push_back(ToSwarm::NotifyHandler { peer_id: incoming.peer_id, handler: NotifyHandler::One(*connec_id), event: NotifsHandlerIn::Close { protocol_index: incoming.set_id.into() }, @@ -993,15 +986,57 @@ impl Notifications { } impl NetworkBehaviour for Notifications { - type ConnectionHandler = NotifsHandlerProto; + type ConnectionHandler = NotifsHandler; type OutEvent = NotificationsOut; - fn new_handler(&mut self) -> Self::ConnectionHandler { - NotifsHandlerProto::new(self.notif_protocols.clone()) + fn handle_pending_inbound_connection( + &mut self, + _connection_id: ConnectionId, + _local_addr: &Multiaddr, + _remote_addr: &Multiaddr, + ) -> Result<(), ConnectionDenied> { + Ok(()) } - fn addresses_of_peer(&mut self, _: &PeerId) -> Vec { - Vec::new() + fn handle_pending_outbound_connection( + &mut self, + _connection_id: ConnectionId, + _maybe_peer: Option, + _addresses: &[Multiaddr], + _effective_role: Endpoint, + ) -> Result, ConnectionDenied> { + Ok(Vec::new()) + } + + fn handle_established_inbound_connection( + &mut self, + _connection_id: ConnectionId, + peer: PeerId, + local_addr: &Multiaddr, + remote_addr: &Multiaddr, + ) -> Result, ConnectionDenied> { + Ok(NotifsHandler::new( + peer, + ConnectedPoint::Listener { + local_addr: local_addr.clone(), + send_back_addr: remote_addr.clone(), + }, + self.notif_protocols.clone(), + )) + } + + fn handle_established_outbound_connection( + &mut self, + _connection_id: ConnectionId, + peer: PeerId, + addr: &Multiaddr, + role_override: Endpoint, + ) -> Result, ConnectionDenied> { + Ok(NotifsHandler::new( + peer, + ConnectedPoint::Dialer { address: addr.clone(), role_override }, + self.notif_protocols.clone(), + )) } fn on_swarm_event(&mut self, event: FromSwarm) { @@ -1022,7 +1057,7 @@ impl NetworkBehaviour for Notifications { peer_id, set_id, endpoint ); trace!(target: "sub-libp2p", "Handler({:?}, {:?}) <= Open({:?})", peer_id, connection_id, set_id); - self.events.push_back(NetworkBehaviourAction::NotifyHandler { + self.events.push_back(ToSwarm::NotifyHandler { peer_id, handler: NotifyHandler::One(connection_id), event: NotifsHandlerIn::Open { protocol_index: set_id.into() }, @@ -1276,9 +1311,7 @@ impl NetworkBehaviour for Notifications { set_id, notifications_sink: replacement_sink, }; - self.events.push_back( - NetworkBehaviourAction::GenerateEvent(event), - ); + self.events.push_back(ToSwarm::GenerateEvent(event)); } } else { trace!( @@ -1289,9 +1322,7 @@ impl NetworkBehaviour for Notifications { peer_id, set_id, }; - self.events.push_back( - NetworkBehaviourAction::GenerateEvent(event), - ); + self.events.push_back(ToSwarm::GenerateEvent(event)); } } } else { @@ -1434,8 +1465,7 @@ impl NetworkBehaviour for Notifications { &mut self, peer_id: PeerId, connection_id: ConnectionId, - event: <::Handler as - ConnectionHandler>::OutEvent, + event: THandlerOutEvent, ) { match event { NotifsHandlerOut::OpenDesiredByRemote { protocol_index } => { @@ -1502,7 +1532,7 @@ impl NetworkBehaviour for Notifications { if let ConnectionState::Closed = *connec_state { trace!(target: "sub-libp2p", "Handler({:?}, {:?}) <= Open({:?})", peer_id, connection_id, set_id); - self.events.push_back(NetworkBehaviourAction::NotifyHandler { + self.events.push_back(ToSwarm::NotifyHandler { peer_id, handler: NotifyHandler::One(connection_id), event: NotifsHandlerIn::Open { protocol_index: set_id.into() }, @@ -1583,7 +1613,7 @@ impl NetworkBehaviour for Notifications { if let ConnectionState::Closed = *connec_state { trace!(target: "sub-libp2p", "Handler({:?}, {:?}) <= Open({:?})", peer_id, connection_id, set_id); - self.events.push_back(NetworkBehaviourAction::NotifyHandler { + self.events.push_back(ToSwarm::NotifyHandler { peer_id, handler: NotifyHandler::One(connection_id), event: NotifsHandlerIn::Open { protocol_index: set_id.into() }, @@ -1668,7 +1698,7 @@ impl NetworkBehaviour for Notifications { connections[pos].1 = ConnectionState::Closing; trace!(target: "sub-libp2p", "Handler({}, {:?}) <= Close({:?})", peer_id, connection_id, set_id); - self.events.push_back(NetworkBehaviourAction::NotifyHandler { + self.events.push_back(ToSwarm::NotifyHandler { peer_id, handler: NotifyHandler::One(connection_id), event: NotifsHandlerIn::Close { protocol_index: set_id.into() }, @@ -1686,7 +1716,7 @@ impl NetworkBehaviour for Notifications { set_id, notifications_sink: replacement_sink, }; - self.events.push_back(NetworkBehaviourAction::GenerateEvent(event)); + self.events.push_back(ToSwarm::GenerateEvent(event)); } *entry.into_mut() = PeerState::Enabled { connections }; @@ -1706,7 +1736,7 @@ impl NetworkBehaviour for Notifications { trace!(target: "sub-libp2p", "External API <= Closed({}, {:?})", peer_id, set_id); let event = NotificationsOut::CustomProtocolClosed { peer_id, set_id }; - self.events.push_back(NetworkBehaviourAction::GenerateEvent(event)); + self.events.push_back(ToSwarm::GenerateEvent(event)); } }, @@ -1790,7 +1820,7 @@ impl NetworkBehaviour for Notifications { received_handshake, notifications_sink: notifications_sink.clone(), }; - self.events.push_back(NetworkBehaviourAction::GenerateEvent(event)); + self.events.push_back(ToSwarm::GenerateEvent(event)); } *connec_state = ConnectionState::Open(notifications_sink); } else if let Some((_, connec_state)) = @@ -1937,7 +1967,7 @@ impl NetworkBehaviour for Notifications { ); let event = NotificationsOut::Notification { peer_id, set_id, message }; - self.events.push_back(NetworkBehaviourAction::GenerateEvent(event)); + self.events.push_back(ToSwarm::GenerateEvent(event)); } else { trace!( target: "sub-libp2p", @@ -1956,7 +1986,7 @@ impl NetworkBehaviour for Notifications { &mut self, cx: &mut Context, _params: &mut impl PollParameters, - ) -> Poll> { + ) -> Poll>> { if let Some(event) = self.events.pop_front() { return Poll::Ready(event) } @@ -1988,8 +2018,6 @@ impl NetworkBehaviour for Notifications { while let Poll::Ready(Some((delay_id, peer_id, set_id))) = Pin::new(&mut self.delays).poll_next(cx) { - let handler = self.new_handler(); - let peer_state = match self.peers.get_mut(&(peer_id, set_id)) { Some(s) => s, // We intentionally never remove elements from `delays`, and it may @@ -2005,8 +2033,7 @@ impl NetworkBehaviour for Notifications { PeerState::PendingRequest { timer, .. } if *timer == delay_id => { trace!(target: "sub-libp2p", "Libp2p <= Dial {:?} now that ban has expired", peer_id); - self.events - .push_back(NetworkBehaviourAction::Dial { opts: peer_id.into(), handler }); + self.events.push_back(ToSwarm::Dial { opts: peer_id.into() }); *peer_state = PeerState::Requested; }, @@ -2019,7 +2046,7 @@ impl NetworkBehaviour for Notifications { { trace!(target: "sub-libp2p", "Handler({}, {:?}) <= Open({:?}) (ban expired)", peer_id, *connec_id, set_id); - self.events.push_back(NetworkBehaviourAction::NotifyHandler { + self.events.push_back(ToSwarm::NotifyHandler { peer_id, handler: NotifyHandler::One(*connec_id), event: NotifsHandlerIn::Open { protocol_index: set_id.into() }, @@ -2055,13 +2082,11 @@ impl NetworkBehaviour for Notifications { } #[cfg(test)] +#[allow(deprecated)] mod tests { use super::*; use crate::protocol::notifications::handler::tests::*; - use libp2p::{ - core::ConnectedPoint, - swarm::{behaviour::FromSwarm, AddressRecord}, - }; + use libp2p::swarm::AddressRecord; use std::{collections::HashSet, iter}; impl PartialEq for ConnectionState { @@ -2223,7 +2248,7 @@ mod tests { fn remote_opens_connection_and_substream() { let (mut notif, _peerset) = development_notifs(); let peer = PeerId::random(); - let conn = ConnectionId::new(0usize); + let conn = ConnectionId::new_unchecked(0); let connected = ConnectedPoint::Listener { local_addr: Multiaddr::empty(), send_back_addr: Multiaddr::empty(), @@ -2273,7 +2298,7 @@ mod tests { async fn disconnect_remote_substream_before_handled_by_peerset() { let (mut notif, _peerset) = development_notifs(); let peer = PeerId::random(); - let conn = ConnectionId::new(0usize); + let conn = ConnectionId::new_unchecked(0); let connected = ConnectedPoint::Listener { local_addr: Multiaddr::empty(), send_back_addr: Multiaddr::empty(), @@ -2310,7 +2335,7 @@ mod tests { let (mut notif, _peerset) = development_notifs(); let set_id = sc_peerset::SetId::from(0); let peer = PeerId::random(); - let conn = ConnectionId::new(0usize); + let conn = ConnectionId::new_unchecked(0); let connected = ConnectedPoint::Listener { local_addr: Multiaddr::empty(), send_back_addr: Multiaddr::empty(), @@ -2344,8 +2369,8 @@ mod tests { libp2p::swarm::behaviour::ConnectionClosed { peer_id: peer, connection_id: conn, - endpoint: &connected, - handler: NotifsHandlerProto::new(vec![]).into_handler(&peer, &connected), + endpoint: &connected.clone(), + handler: NotifsHandler::new(peer, connected, vec![]), remaining_established: 0usize, }, )); @@ -2374,7 +2399,7 @@ mod tests { fn peerset_connect_incoming() { let (mut notif, _peerset) = development_notifs(); let peer = PeerId::random(); - let conn = ConnectionId::new(0usize); + let conn = ConnectionId::new_unchecked(0); let set_id = sc_peerset::SetId::from(0); let connected = ConnectedPoint::Listener { local_addr: Multiaddr::empty(), @@ -2409,7 +2434,7 @@ mod tests { let (mut notif, _peerset) = development_notifs(); let set_id = sc_peerset::SetId::from(0); let peer = PeerId::random(); - let conn = ConnectionId::new(0usize); + let conn = ConnectionId::new_unchecked(0); let connected = ConnectedPoint::Listener { local_addr: Multiaddr::empty(), send_back_addr: Multiaddr::empty(), @@ -2455,7 +2480,7 @@ mod tests { fn peerset_disconnect_enabled() { let (mut notif, _peerset) = development_notifs(); let peer = PeerId::random(); - let conn = ConnectionId::new(0usize); + let conn = ConnectionId::new_unchecked(0); let set_id = sc_peerset::SetId::from(0); let connected = ConnectedPoint::Listener { local_addr: Multiaddr::empty(), @@ -2505,7 +2530,7 @@ mod tests { let (mut notif, _peerset) = development_notifs(); let set_id = sc_peerset::SetId::from(0); let peer = PeerId::random(); - let conn = ConnectionId::new(0usize); + let conn = ConnectionId::new_unchecked(0); let connected = ConnectedPoint::Listener { local_addr: Multiaddr::empty(), send_back_addr: Multiaddr::empty(), @@ -2534,8 +2559,8 @@ mod tests { libp2p::swarm::behaviour::ConnectionClosed { peer_id: peer, connection_id: conn, - endpoint: &connected, - handler: NotifsHandlerProto::new(vec![]).into_handler(&peer, &connected), + endpoint: &connected.clone(), + handler: NotifsHandler::new(peer, connected, vec![]), remaining_established: 0usize, }, )); @@ -2557,7 +2582,7 @@ mod tests { fn peerset_accept_peer_not_alive() { let (mut notif, _peerset) = development_notifs(); let peer = PeerId::random(); - let conn = ConnectionId::new(0usize); + let conn = ConnectionId::new_unchecked(0); let set_id = sc_peerset::SetId::from(0); let connected = ConnectedPoint::Listener { local_addr: Multiaddr::empty(), @@ -2604,8 +2629,8 @@ mod tests { fn secondary_connection_peer_state_incoming() { let (mut notif, _peerset) = development_notifs(); let peer = PeerId::random(); - let conn = ConnectionId::new(0usize); - let conn2 = ConnectionId::new(1usize); + let conn = ConnectionId::new_unchecked(0); + let conn2 = ConnectionId::new_unchecked(1); let set_id = sc_peerset::SetId::from(0); let connected = ConnectedPoint::Listener { local_addr: Multiaddr::empty(), @@ -2659,7 +2684,7 @@ mod tests { fn close_connection_for_disabled_peer() { let (mut notif, _peerset) = development_notifs(); let peer = PeerId::random(); - let conn = ConnectionId::new(0usize); + let conn = ConnectionId::new_unchecked(0); let set_id = sc_peerset::SetId::from(0); let connected = ConnectedPoint::Listener { local_addr: Multiaddr::empty(), @@ -2681,8 +2706,8 @@ mod tests { libp2p::swarm::behaviour::ConnectionClosed { peer_id: peer, connection_id: conn, - endpoint: &connected, - handler: NotifsHandlerProto::new(vec![]).into_handler(&peer, &connected), + endpoint: &connected.clone(), + handler: NotifsHandler::new(peer, connected, vec![]), remaining_established: 0usize, }, )); @@ -2693,7 +2718,7 @@ mod tests { fn close_connection_for_incoming_peer_one_connection() { let (mut notif, _peerset) = development_notifs(); let peer = PeerId::random(); - let conn = ConnectionId::new(0usize); + let conn = ConnectionId::new_unchecked(0); let set_id = sc_peerset::SetId::from(0); let connected = ConnectedPoint::Listener { local_addr: Multiaddr::empty(), @@ -2722,8 +2747,8 @@ mod tests { libp2p::swarm::behaviour::ConnectionClosed { peer_id: peer, connection_id: conn, - endpoint: &connected, - handler: NotifsHandlerProto::new(vec![]).into_handler(&peer, &connected), + endpoint: &connected.clone(), + handler: NotifsHandler::new(peer, connected, vec![]), remaining_established: 0usize, }, )); @@ -2738,8 +2763,8 @@ mod tests { fn close_connection_for_incoming_peer_two_connections() { let (mut notif, _peerset) = development_notifs(); let peer = PeerId::random(); - let conn = ConnectionId::new(0usize); - let conn1 = ConnectionId::new(1usize); + let conn = ConnectionId::new_unchecked(0); + let conn1 = ConnectionId::new_unchecked(1); let set_id = sc_peerset::SetId::from(0); let connected = ConnectedPoint::Listener { local_addr: Multiaddr::empty(), @@ -2789,8 +2814,8 @@ mod tests { libp2p::swarm::behaviour::ConnectionClosed { peer_id: peer, connection_id: conn, - endpoint: &connected, - handler: NotifsHandlerProto::new(vec![]).into_handler(&peer, &connected), + endpoint: &connected.clone(), + handler: NotifsHandler::new(peer, connected, vec![]), remaining_established: 0usize, }, )); @@ -2807,7 +2832,7 @@ mod tests { fn connection_and_substream_open() { let (mut notif, _peerset) = development_notifs(); let peer = PeerId::random(); - let conn = ConnectionId::new(0usize); + let conn = ConnectionId::new_unchecked(0); let set_id = sc_peerset::SetId::from(0); let connected = ConnectedPoint::Listener { local_addr: Multiaddr::empty(), @@ -2851,7 +2876,7 @@ mod tests { assert!(std::matches!( notif.events[notif.events.len() - 1], - NetworkBehaviourAction::GenerateEvent(NotificationsOut::CustomProtocolOpen { .. }) + ToSwarm::GenerateEvent(NotificationsOut::CustomProtocolOpen { .. }) )); } @@ -2859,8 +2884,8 @@ mod tests { fn connection_closed_sink_replaced() { let (mut notif, _peerset) = development_notifs(); let peer = PeerId::random(); - let conn1 = ConnectionId::new(0usize); - let conn2 = ConnectionId::new(1usize); + let conn1 = ConnectionId::new_unchecked(0); + let conn2 = ConnectionId::new_unchecked(1); let set_id = sc_peerset::SetId::from(0); let connected = ConnectedPoint::Listener { local_addr: Multiaddr::empty(), @@ -2931,8 +2956,8 @@ mod tests { libp2p::swarm::behaviour::ConnectionClosed { peer_id: peer, connection_id: conn1, - endpoint: &connected, - handler: NotifsHandlerProto::new(vec![]).into_handler(&peer, &connected), + endpoint: &connected.clone(), + handler: NotifsHandler::new(peer, connected, vec![]), remaining_established: 0usize, }, )); @@ -2947,7 +2972,7 @@ mod tests { assert!(std::matches!( notif.events[notif.events.len() - 1], - NetworkBehaviourAction::GenerateEvent(NotificationsOut::CustomProtocolReplaced { .. }) + ToSwarm::GenerateEvent(NotificationsOut::CustomProtocolReplaced { .. }) )); } @@ -2963,8 +2988,8 @@ mod tests { notif.on_swarm_event(FromSwarm::DialFailure(libp2p::swarm::behaviour::DialFailure { peer_id: Some(peer), - handler: NotifsHandlerProto::new(vec![]), error: &libp2p::swarm::DialError::Banned, + connection_id: ConnectionId::new_unchecked(1337), })); if let Some(PeerState::Backoff { timer_deadline, .. }) = notif.peers.get(&(peer, set_id)) { @@ -2978,7 +3003,7 @@ mod tests { async fn write_notification() { let (mut notif, _peerset) = development_notifs(); let peer = PeerId::random(); - let conn = ConnectionId::new(0usize); + let conn = ConnectionId::new_unchecked(0); let set_id = sc_peerset::SetId::from(0); let connected = ConnectedPoint::Listener { local_addr: Multiaddr::empty(), @@ -3028,7 +3053,7 @@ mod tests { let (mut notif, _peerset) = development_notifs(); let set_id = sc_peerset::SetId::from(0); let peer = PeerId::random(); - let conn = ConnectionId::new(0usize); + let conn = ConnectionId::new_unchecked(0); let connected = ConnectedPoint::Listener { local_addr: Multiaddr::empty(), send_back_addr: Multiaddr::empty(), @@ -3057,8 +3082,8 @@ mod tests { libp2p::swarm::behaviour::ConnectionClosed { peer_id: peer, connection_id: conn, - endpoint: &connected, - handler: NotifsHandlerProto::new(vec![]).into_handler(&peer, &connected), + endpoint: &connected.clone(), + handler: NotifsHandler::new(peer, connected, vec![]), remaining_established: 0usize, }, )); @@ -3076,7 +3101,7 @@ mod tests { let (mut notif, _peerset) = development_notifs(); let peer = PeerId::random(); let set_id = sc_peerset::SetId::from(0); - let conn = ConnectionId::new(0usize); + let conn = ConnectionId::new_unchecked(0); let connected = ConnectedPoint::Listener { local_addr: Multiaddr::empty(), send_back_addr: Multiaddr::empty(), @@ -3102,7 +3127,7 @@ mod tests { let (mut notif, _peerset) = development_notifs(); let set_id = sc_peerset::SetId::from(0); let peer = PeerId::random(); - let conn = ConnectionId::new(0usize); + let conn = ConnectionId::new_unchecked(0); let connected = ConnectedPoint::Listener { local_addr: Multiaddr::empty(), send_back_addr: Multiaddr::empty(), @@ -3131,8 +3156,8 @@ mod tests { libp2p::swarm::behaviour::ConnectionClosed { peer_id: peer, connection_id: conn, - endpoint: &connected, - handler: NotifsHandlerProto::new(vec![]).into_handler(&peer, &connected), + endpoint: &connected.clone(), + handler: NotifsHandler::new(peer, connected, vec![]), remaining_established: 0usize, }, )); @@ -3148,8 +3173,8 @@ mod tests { let (mut notif, _peerset) = development_notifs(); let set_id = sc_peerset::SetId::from(0); let peer = PeerId::random(); - let conn1 = ConnectionId::new(0usize); - let conn2 = ConnectionId::new(0usize); + let conn1 = ConnectionId::new_unchecked(0); + let conn2 = ConnectionId::new_unchecked(1); let connected = ConnectedPoint::Listener { local_addr: Multiaddr::empty(), send_back_addr: Multiaddr::empty(), @@ -3194,8 +3219,8 @@ mod tests { libp2p::swarm::behaviour::ConnectionClosed { peer_id: peer, connection_id: conn1, - endpoint: &connected, - handler: NotifsHandlerProto::new(vec![]).into_handler(&peer, &connected), + endpoint: &connected.clone(), + handler: NotifsHandler::new(peer, connected.clone(), vec![]), remaining_established: 0usize, }, )); @@ -3208,8 +3233,8 @@ mod tests { libp2p::swarm::behaviour::ConnectionClosed { peer_id: peer, connection_id: conn2, - endpoint: &connected, - handler: NotifsHandlerProto::new(vec![]).into_handler(&peer, &connected), + endpoint: &connected.clone(), + handler: NotifsHandler::new(peer, connected, vec![]), remaining_established: 0usize, }, )); @@ -3221,7 +3246,7 @@ mod tests { let (mut notif, _peerset) = development_notifs(); let peer = PeerId::random(); let set_id = sc_peerset::SetId::from(0); - let conn = ConnectionId::new(0usize); + let conn = ConnectionId::new_unchecked(0); let connected = ConnectedPoint::Listener { local_addr: Multiaddr::empty(), send_back_addr: Multiaddr::empty(), @@ -3259,8 +3284,8 @@ mod tests { libp2p::swarm::behaviour::ConnectionClosed { peer_id: peer, connection_id: conn, - endpoint: &connected, - handler: NotifsHandlerProto::new(vec![]).into_handler(&peer, &connected), + endpoint: &connected.clone(), + handler: NotifsHandler::new(peer, connected, vec![]), remaining_established: 0usize, }, )); @@ -3271,8 +3296,8 @@ mod tests { fn two_connections_inactive_connection_gets_closed_peer_state_is_still_incoming() { let (mut notif, _peerset) = development_notifs(); let peer = PeerId::random(); - let conn1 = ConnectionId::new(0usize); - let conn2 = ConnectionId::new(1usize); + let conn1 = ConnectionId::new_unchecked(0); + let conn2 = ConnectionId::new_unchecked(1); let set_id = sc_peerset::SetId::from(0); let connected = ConnectedPoint::Listener { local_addr: Multiaddr::empty(), @@ -3314,8 +3339,8 @@ mod tests { libp2p::swarm::behaviour::ConnectionClosed { peer_id: peer, connection_id: conn2, - endpoint: &connected, - handler: NotifsHandlerProto::new(vec![]).into_handler(&peer, &connected), + endpoint: &connected.clone(), + handler: NotifsHandler::new(peer, connected, vec![]), remaining_established: 0usize, }, )); @@ -3326,8 +3351,8 @@ mod tests { fn two_connections_active_connection_gets_closed_peer_state_is_disabled() { let (mut notif, _peerset) = development_notifs(); let peer = PeerId::random(); - let conn1 = ConnectionId::new(0usize); - let conn2 = ConnectionId::new(1usize); + let conn1 = ConnectionId::new_unchecked(0); + let conn2 = ConnectionId::new_unchecked(1); let set_id = sc_peerset::SetId::from(0); let connected = ConnectedPoint::Listener { local_addr: Multiaddr::empty(), @@ -3372,8 +3397,8 @@ mod tests { libp2p::swarm::behaviour::ConnectionClosed { peer_id: peer, connection_id: conn1, - endpoint: &connected, - handler: NotifsHandlerProto::new(vec![]).into_handler(&peer, &connected), + endpoint: &connected.clone(), + handler: NotifsHandler::new(peer, connected, vec![]), remaining_established: 0usize, }, )); @@ -3384,8 +3409,8 @@ mod tests { fn inject_connection_closed_for_active_connection() { let (mut notif, _peerset) = development_notifs(); let peer = PeerId::random(); - let conn1 = ConnectionId::new(0usize); - let conn2 = ConnectionId::new(1usize); + let conn1 = ConnectionId::new_unchecked(0); + let conn2 = ConnectionId::new_unchecked(1); let set_id = sc_peerset::SetId::from(0); let connected = ConnectedPoint::Listener { local_addr: Multiaddr::empty(), @@ -3441,8 +3466,8 @@ mod tests { libp2p::swarm::behaviour::ConnectionClosed { peer_id: peer, connection_id: conn1, - endpoint: &connected, - handler: NotifsHandlerProto::new(vec![]).into_handler(&peer, &connected), + endpoint: &connected.clone(), + handler: NotifsHandler::new(peer, connected, vec![]), remaining_established: 0usize, }, )); @@ -3453,7 +3478,7 @@ mod tests { let (mut notif, _peerset) = development_notifs(); let set_id = sc_peerset::SetId::from(0); let peer = PeerId::random(); - let conn = ConnectionId::new(0usize); + let conn = ConnectionId::new_unchecked(0); let connected = ConnectedPoint::Listener { local_addr: Multiaddr::empty(), send_back_addr: Multiaddr::empty(), @@ -3482,8 +3507,8 @@ mod tests { libp2p::swarm::behaviour::ConnectionClosed { peer_id: peer, connection_id: conn, - endpoint: &connected, - handler: NotifsHandlerProto::new(vec![]).into_handler(&peer, &connected), + endpoint: &connected.clone(), + handler: NotifsHandler::new(peer, connected, vec![]), remaining_established: 0usize, }, )); @@ -3500,8 +3525,8 @@ mod tests { let now = Instant::now(); notif.on_swarm_event(FromSwarm::DialFailure(libp2p::swarm::behaviour::DialFailure { peer_id: Some(peer), - handler: NotifsHandlerProto::new(vec![]), error: &libp2p::swarm::DialError::Banned, + connection_id: ConnectionId::new_unchecked(0), })); if let Some(PeerState::PendingRequest { ref timer_deadline, .. }) = @@ -3516,8 +3541,8 @@ mod tests { let (mut notif, _peerset) = development_notifs(); let peer = PeerId::random(); let set_id = sc_peerset::SetId::from(0); - let conn1 = ConnectionId::new(0usize); - let conn2 = ConnectionId::new(1usize); + let conn1 = ConnectionId::new_unchecked(0); + let conn2 = ConnectionId::new_unchecked(1); let connected = ConnectedPoint::Listener { local_addr: Multiaddr::empty(), send_back_addr: Multiaddr::empty(), @@ -3570,7 +3595,7 @@ mod tests { let (mut notif, _peerset) = development_notifs(); let peer = PeerId::random(); let set_id = sc_peerset::SetId::from(0); - let conn = ConnectionId::new(0usize); + let conn = ConnectionId::new_unchecked(0); let connected = ConnectedPoint::Listener { local_addr: Multiaddr::empty(), send_back_addr: Multiaddr::empty(), @@ -3599,8 +3624,8 @@ mod tests { libp2p::swarm::behaviour::ConnectionClosed { peer_id: peer, connection_id: conn, - endpoint: &connected, - handler: NotifsHandlerProto::new(vec![]).into_handler(&peer, &connected), + endpoint: &connected.clone(), + handler: NotifsHandler::new(peer, connected, vec![]), remaining_established: 0usize, }, )); @@ -3647,7 +3672,7 @@ mod tests { async fn reschedule_disabled_pending_enable_when_connection_not_closed() { let (mut notif, _peerset) = development_notifs(); let peer = PeerId::random(); - let conn = ConnectionId::new(0usize); + let conn = ConnectionId::new_unchecked(0); let set_id = sc_peerset::SetId::from(0); let connected = ConnectedPoint::Listener { local_addr: Multiaddr::empty(), @@ -3763,7 +3788,7 @@ mod tests { fn peerset_report_connect_with_enabled_peer() { let (mut notif, _peerset) = development_notifs(); let peer = PeerId::random(); - let conn = ConnectionId::new(0usize); + let conn = ConnectionId::new_unchecked(0); let set_id = sc_peerset::SetId::from(0); let connected = ConnectedPoint::Listener { local_addr: Multiaddr::empty(), @@ -3815,7 +3840,7 @@ mod tests { let (mut notif, _peerset) = development_notifs(); let set_id = sc_peerset::SetId::from(0); let peer = PeerId::random(); - let conn = ConnectionId::new(0usize); + let conn = ConnectionId::new_unchecked(0); let connected = ConnectedPoint::Listener { local_addr: Multiaddr::empty(), send_back_addr: Multiaddr::empty(), @@ -3872,7 +3897,7 @@ mod tests { let (mut notif, _peerset) = development_notifs(); let set_id = sc_peerset::SetId::from(0); let peer = PeerId::random(); - let conn = ConnectionId::new(0usize); + let conn = ConnectionId::new_unchecked(0); let connected = ConnectedPoint::Listener { local_addr: Multiaddr::empty(), send_back_addr: Multiaddr::empty(), @@ -3901,8 +3926,8 @@ mod tests { libp2p::swarm::behaviour::ConnectionClosed { peer_id: peer, connection_id: conn, - endpoint: &connected, - handler: NotifsHandlerProto::new(vec![]).into_handler(&peer, &connected), + endpoint: &connected.clone(), + handler: NotifsHandler::new(peer, connected, vec![]), remaining_established: 0usize, }, )); @@ -3925,7 +3950,7 @@ mod tests { let (mut notif, _peerset) = development_notifs(); let peer = PeerId::random(); let set_id = sc_peerset::SetId::from(0); - let conn = ConnectionId::new(0usize); + let conn = ConnectionId::new_unchecked(0); let connected = ConnectedPoint::Listener { local_addr: Multiaddr::empty(), send_back_addr: Multiaddr::empty(), @@ -3960,7 +3985,7 @@ mod tests { fn peerset_report_accept_incoming_peer() { let (mut notif, _peerset) = development_notifs(); let peer = PeerId::random(); - let conn = ConnectionId::new(0usize); + let conn = ConnectionId::new_unchecked(0); let set_id = sc_peerset::SetId::from(0); let connected = ConnectedPoint::Listener { local_addr: Multiaddr::empty(), @@ -4001,7 +4026,7 @@ mod tests { fn peerset_report_accept_not_incoming_peer() { let (mut notif, _peerset) = development_notifs(); let peer = PeerId::random(); - let conn = ConnectionId::new(0usize); + let conn = ConnectionId::new_unchecked(0); let set_id = sc_peerset::SetId::from(0); let connected = ConnectedPoint::Listener { local_addr: Multiaddr::empty(), @@ -4058,9 +4083,9 @@ mod tests { notif.on_swarm_event(FromSwarm::ConnectionClosed( libp2p::swarm::behaviour::ConnectionClosed { peer_id: peer, - connection_id: ConnectionId::new(0usize), - endpoint: &endpoint, - handler: NotifsHandlerProto::new(vec![]).into_handler(&peer, &endpoint), + connection_id: ConnectionId::new_unchecked(0), + endpoint: &endpoint.clone(), + handler: NotifsHandler::new(peer, endpoint, vec![]), remaining_established: 0usize, }, )); @@ -4102,7 +4127,7 @@ mod tests { fn reject_non_active_connection() { let (mut notif, _peerset) = development_notifs(); let peer = PeerId::random(); - let conn = ConnectionId::new(0usize); + let conn = ConnectionId::new_unchecked(0); let set_id = sc_peerset::SetId::from(0); let connected = ConnectedPoint::Listener { local_addr: Multiaddr::empty(), @@ -4140,7 +4165,7 @@ mod tests { fn reject_non_existent_peer_but_alive_connection() { let (mut notif, _peerset) = development_notifs(); let peer = PeerId::random(); - let conn = ConnectionId::new(0usize); + let conn = ConnectionId::new_unchecked(0); let set_id = sc_peerset::SetId::from(0); let connected = ConnectedPoint::Listener { local_addr: Multiaddr::empty(), @@ -4180,7 +4205,7 @@ mod tests { fn inject_non_existent_connection_closed_for_incoming_peer() { let (mut notif, _peerset) = development_notifs(); let peer = PeerId::random(); - let conn = ConnectionId::new(0usize); + let conn = ConnectionId::new_unchecked(0); let set_id = sc_peerset::SetId::from(0); let connected = ConnectedPoint::Listener { local_addr: Multiaddr::empty(), @@ -4209,9 +4234,9 @@ mod tests { notif.on_swarm_event(FromSwarm::ConnectionClosed( libp2p::swarm::behaviour::ConnectionClosed { peer_id: peer, - connection_id: ConnectionId::new(1337usize), - endpoint: &connected, - handler: NotifsHandlerProto::new(vec![]).into_handler(&peer, &connected), + connection_id: ConnectionId::new_unchecked(1337), + endpoint: &connected.clone(), + handler: NotifsHandler::new(peer, connected, vec![]), remaining_established: 0usize, }, )); @@ -4224,7 +4249,7 @@ mod tests { let (mut notif, _peerset) = development_notifs(); let set_id = sc_peerset::SetId::from(0); let peer = PeerId::random(); - let conn = ConnectionId::new(0usize); + let conn = ConnectionId::new_unchecked(0); let connected = ConnectedPoint::Listener { local_addr: Multiaddr::empty(), send_back_addr: Multiaddr::empty(), @@ -4244,9 +4269,9 @@ mod tests { notif.on_swarm_event(FromSwarm::ConnectionClosed( libp2p::swarm::behaviour::ConnectionClosed { peer_id: peer, - connection_id: ConnectionId::new(1337usize), - endpoint: &connected, - handler: NotifsHandlerProto::new(vec![]).into_handler(&peer, &connected), + connection_id: ConnectionId::new_unchecked(1337), + endpoint: &connected.clone(), + handler: NotifsHandler::new(peer, connected, vec![]), remaining_established: 0usize, }, )); @@ -4259,7 +4284,7 @@ mod tests { let (mut notif, _peerset) = development_notifs(); let set_id = sc_peerset::SetId::from(0); let peer = PeerId::random(); - let conn = ConnectionId::new(0usize); + let conn = ConnectionId::new_unchecked(0); let connected = ConnectedPoint::Listener { local_addr: Multiaddr::empty(), send_back_addr: Multiaddr::empty(), @@ -4295,9 +4320,9 @@ mod tests { notif.on_swarm_event(FromSwarm::ConnectionClosed( libp2p::swarm::behaviour::ConnectionClosed { peer_id: peer, - connection_id: ConnectionId::new(1337usize), - endpoint: &connected, - handler: NotifsHandlerProto::new(vec![]).into_handler(&peer, &connected), + connection_id: ConnectionId::new_unchecked(1337), + endpoint: &connected.clone(), + handler: NotifsHandler::new(peer, connected, vec![]), remaining_established: 0usize, }, )); @@ -4309,7 +4334,7 @@ mod tests { fn inject_connection_closed_for_incoming_peer_state_mismatch() { let (mut notif, _peerset) = development_notifs(); let peer = PeerId::random(); - let conn = ConnectionId::new(0usize); + let conn = ConnectionId::new_unchecked(0); let set_id = sc_peerset::SetId::from(0); let connected = ConnectedPoint::Listener { local_addr: Multiaddr::empty(), @@ -4340,8 +4365,8 @@ mod tests { libp2p::swarm::behaviour::ConnectionClosed { peer_id: peer, connection_id: conn, - endpoint: &connected, - handler: NotifsHandlerProto::new(vec![]).into_handler(&peer, &connected), + endpoint: &connected.clone(), + handler: NotifsHandler::new(peer, connected, vec![]), remaining_established: 0usize, }, )); @@ -4353,7 +4378,7 @@ mod tests { fn inject_connection_closed_for_enabled_state_mismatch() { let (mut notif, _peerset) = development_notifs(); let peer = PeerId::random(); - let conn = ConnectionId::new(0usize); + let conn = ConnectionId::new_unchecked(0); let set_id = sc_peerset::SetId::from(0); let connected = ConnectedPoint::Listener { local_addr: Multiaddr::empty(), @@ -4386,9 +4411,9 @@ mod tests { notif.on_swarm_event(FromSwarm::ConnectionClosed( libp2p::swarm::behaviour::ConnectionClosed { peer_id: peer, - connection_id: ConnectionId::new(1337usize), - endpoint: &connected, - handler: NotifsHandlerProto::new(vec![]).into_handler(&peer, &connected), + connection_id: ConnectionId::new_unchecked(1337), + endpoint: &connected.clone(), + handler: NotifsHandler::new(peer, connected, vec![]), remaining_established: 0usize, }, )); @@ -4401,7 +4426,7 @@ mod tests { let (mut notif, _peerset) = development_notifs(); let set_id = sc_peerset::SetId::from(0); let peer = PeerId::random(); - let conn = ConnectionId::new(0usize); + let conn = ConnectionId::new_unchecked(0); let connected = ConnectedPoint::Listener { local_addr: Multiaddr::empty(), send_back_addr: Multiaddr::empty(), @@ -4430,8 +4455,8 @@ mod tests { libp2p::swarm::behaviour::ConnectionClosed { peer_id: peer, connection_id: conn, - endpoint: &connected, - handler: NotifsHandlerProto::new(vec![]).into_handler(&peer, &connected), + endpoint: &connected.clone(), + handler: NotifsHandler::new(peer, connected.clone(), vec![]), remaining_established: 0usize, }, )); @@ -4441,8 +4466,8 @@ mod tests { libp2p::swarm::behaviour::ConnectionClosed { peer_id: peer, connection_id: conn, - endpoint: &connected, - handler: NotifsHandlerProto::new(vec![]).into_handler(&peer, &connected), + endpoint: &connected.clone(), + handler: NotifsHandler::new(peer, connected, vec![]), remaining_established: 0usize, }, )); @@ -4453,7 +4478,7 @@ mod tests { #[cfg(debug_assertions)] fn open_result_ok_non_existent_peer() { let (mut notif, _peerset) = development_notifs(); - let conn = ConnectionId::new(0usize); + let conn = ConnectionId::new_unchecked(0); let connected = ConnectedPoint::Listener { local_addr: Multiaddr::empty(), send_back_addr: Multiaddr::empty(), diff --git a/client/network/src/protocol/notifications/handler.rs b/client/network/src/protocol/notifications/handler.rs index 9d8d98fd8..665b646ec 100644 --- a/client/network/src/protocol/notifications/handler.rs +++ b/client/network/src/protocol/notifications/handler.rs @@ -72,11 +72,12 @@ use futures::{ prelude::*, }; use libp2p::{ - core::{ConnectedPoint, PeerId}, + core::ConnectedPoint, swarm::{ - handler::ConnectionEvent, ConnectionHandler, ConnectionHandlerEvent, IntoConnectionHandler, - KeepAlive, NegotiatedSubstream, SubstreamProtocol, + handler::ConnectionEvent, ConnectionHandler, ConnectionHandlerEvent, KeepAlive, + NegotiatedSubstream, SubstreamProtocol, }, + PeerId, }; use log::error; use parking_lot::{Mutex, RwLock}; @@ -105,19 +106,6 @@ const OPEN_TIMEOUT: Duration = Duration::from_secs(10); /// open substreams. const INITIAL_KEEPALIVE_TIME: Duration = Duration::from_secs(5); -/// Implements the `IntoConnectionHandler` trait of libp2p. -/// -/// Every time a connection with a remote starts, an instance of this struct is created and -/// sent to a background task dedicated to this connection. Once the connection is established, -/// it is turned into a [`NotifsHandler`]. -/// -/// See the documentation at the module level for more information. -pub struct NotifsHandlerProto { - /// Name of protocols, prototypes for upgrades for inbound substreams, and the message we - /// send or respond with in the handshake. - protocols: Vec, -} - /// The actual handler once the connection has been established. /// /// See the documentation at the module level for more information. @@ -140,6 +128,30 @@ pub struct NotifsHandler { >, } +impl NotifsHandler { + /// Creates new [`NotifsHandler`]. + pub fn new(peer_id: PeerId, endpoint: ConnectedPoint, protocols: Vec) -> Self { + Self { + protocols: protocols + .into_iter() + .map(|config| { + let in_upgrade = NotificationsIn::new( + config.name.clone(), + config.fallback_names.clone(), + config.max_notification_size, + ); + + Protocol { config, in_upgrade, state: State::Closed { pending_opening: false } } + }) + .collect(), + peer_id, + endpoint, + when_connection_open: Instant::now(), + events_queue: VecDeque::with_capacity(16), + } + } +} + /// Configuration for a notifications protocol. #[derive(Debug, Clone)] pub struct ProtocolConfig { @@ -223,45 +235,6 @@ enum State { }, } -impl IntoConnectionHandler for NotifsHandlerProto { - type Handler = NotifsHandler; - - fn inbound_protocol(&self) -> UpgradeCollec { - self.protocols - .iter() - .map(|cfg| { - NotificationsIn::new( - cfg.name.clone(), - cfg.fallback_names.clone(), - cfg.max_notification_size, - ) - }) - .collect::>() - } - - fn into_handler(self, peer_id: &PeerId, connected_point: &ConnectedPoint) -> Self::Handler { - NotifsHandler { - protocols: self - .protocols - .into_iter() - .map(|config| { - let in_upgrade = NotificationsIn::new( - config.name.clone(), - config.fallback_names.clone(), - config.max_notification_size, - ); - - Protocol { config, in_upgrade, state: State::Closed { pending_opening: false } } - }) - .collect(), - peer_id: *peer_id, - endpoint: connected_point.clone(), - when_connection_open: Instant::now(), - events_queue: VecDeque::with_capacity(16), - } - } -} - /// Event that can be received by a `NotifsHandler`. #[derive(Debug, Clone)] pub enum NotifsHandlerIn { @@ -461,18 +434,6 @@ pub enum NotifsHandlerError { SyncNotificationsClogged, } -impl NotifsHandlerProto { - /// Builds a new handler. - /// - /// `list` is a list of notification protocols names, the message to send as part of the - /// handshake, and the maximum allowed size of a notification. At the moment, the message - /// is always the same whether we open a substream ourselves or respond to handshake from - /// the remote. - pub fn new(list: impl Into>) -> Self { - Self { protocols: list.into() } - } -} - impl ConnectionHandler for NotifsHandler { type InEvent = NotifsHandlerIn; type OutEvent = NotifsHandlerOut; @@ -954,7 +915,6 @@ pub mod tests { .await } } - struct MockSubstream { pub rx: mpsc::Receiver>, pub tx: mpsc::Sender>, diff --git a/client/network/src/protocol/notifications/tests.rs b/client/network/src/protocol/notifications/tests.rs index 9ca6974e4..d13a4fcfa 100644 --- a/client/network/src/protocol/notifications/tests.rs +++ b/client/network/src/protocol/notifications/tests.rs @@ -22,11 +22,12 @@ use crate::protocol::notifications::{Notifications, NotificationsOut, ProtocolCo use futures::prelude::*; use libp2p::{ - core::{connection::ConnectionId, transport::MemoryTransport, upgrade}, + core::{transport::MemoryTransport, upgrade, Endpoint}, identity, noise, swarm::{ - behaviour::FromSwarm, ConnectionHandler, Executor, IntoConnectionHandler, NetworkBehaviour, - NetworkBehaviourAction, PollParameters, Swarm, SwarmEvent, + behaviour::FromSwarm, ConnectionDenied, ConnectionId, Executor, NetworkBehaviour, + PollParameters, Swarm, SwarmBuilder, SwarmEvent, THandler, THandlerInEvent, + THandlerOutEvent, ToSwarm, }, yamux, Multiaddr, PeerId, Transport, }; @@ -57,13 +58,10 @@ fn build_nodes() -> (Swarm, Swarm) { for index in 0..2 { let keypair = keypairs[index].clone(); - let noise_keys = - noise::Keypair::::new().into_authentic(&keypair).unwrap(); - let transport = MemoryTransport::new() .upgrade(upgrade::Version::V1) - .authenticate(noise::NoiseConfig::xx(noise_keys).into_authenticated()) - .multiplex(yamux::YamuxConfig::default()) + .authenticate(noise::Config::new(&keypair).unwrap()) + .multiplex(yamux::Config::default()) .timeout(Duration::from_secs(20)) .boxed(); @@ -105,12 +103,13 @@ fn build_nodes() -> (Swarm, Swarm) { }; let runtime = tokio::runtime::Runtime::new().unwrap(); - let mut swarm = Swarm::with_executor( + let mut swarm = SwarmBuilder::with_executor( transport, behaviour, keypairs[index].public().to_peer_id(), TokioExecutor(runtime), - ); + ) + .build(); swarm.listen_on(addrs[index].clone()).unwrap(); out.push(swarm); } @@ -146,18 +145,63 @@ impl NetworkBehaviour for CustomProtoWithAddr { type ConnectionHandler = ::ConnectionHandler; type OutEvent = ::OutEvent; - fn new_handler(&mut self) -> Self::ConnectionHandler { - self.inner.new_handler() + fn handle_pending_inbound_connection( + &mut self, + connection_id: ConnectionId, + local_addr: &Multiaddr, + remote_addr: &Multiaddr, + ) -> Result<(), ConnectionDenied> { + self.inner + .handle_pending_inbound_connection(connection_id, local_addr, remote_addr) } - fn addresses_of_peer(&mut self, peer_id: &PeerId) -> Vec { - let mut list = self.inner.addresses_of_peer(peer_id); - for (p, a) in self.addrs.iter() { - if p == peer_id { - list.push(a.clone()); + fn handle_pending_outbound_connection( + &mut self, + connection_id: ConnectionId, + maybe_peer: Option, + addresses: &[Multiaddr], + effective_role: Endpoint, + ) -> Result, ConnectionDenied> { + let mut list = self.inner.handle_pending_outbound_connection( + connection_id, + maybe_peer, + addresses, + effective_role, + )?; + if let Some(peer_id) = maybe_peer { + for (p, a) in self.addrs.iter() { + if *p == peer_id { + list.push(a.clone()); + } } } - list + Ok(list) + } + + fn handle_established_inbound_connection( + &mut self, + connection_id: ConnectionId, + peer: PeerId, + local_addr: &Multiaddr, + remote_addr: &Multiaddr, + ) -> Result, ConnectionDenied> { + self.inner.handle_established_inbound_connection( + connection_id, + peer, + local_addr, + remote_addr, + ) + } + + fn handle_established_outbound_connection( + &mut self, + connection_id: ConnectionId, + peer: PeerId, + addr: &Multiaddr, + role_override: Endpoint, + ) -> Result, ConnectionDenied> { + self.inner + .handle_established_outbound_connection(connection_id, peer, addr, role_override) } fn on_swarm_event(&mut self, event: FromSwarm) { @@ -168,8 +212,7 @@ impl NetworkBehaviour for CustomProtoWithAddr { &mut self, peer_id: PeerId, connection_id: ConnectionId, - event: <::Handler as - ConnectionHandler>::OutEvent, + event: THandlerOutEvent, ) { self.inner.on_connection_handler_event(peer_id, connection_id, event); } @@ -178,7 +221,7 @@ impl NetworkBehaviour for CustomProtoWithAddr { &mut self, cx: &mut Context, params: &mut impl PollParameters, - ) -> Poll> { + ) -> Poll>> { self.inner.poll(cx, params) } } diff --git a/client/network/src/request_responses.rs b/client/network/src/request_responses.rs index 4628b0191..e0f4074e0 100644 --- a/client/network/src/request_responses.rs +++ b/client/network/src/request_responses.rs @@ -18,8 +18,8 @@ //! Collection of request-response protocols. //! -//! The [`RequestResponse`] struct defined in this module provides support for zero or more -//! so-called "request-response" protocols. +//! The [`RequestResponsesBehaviour`] struct defined in this module provides support for zero or +//! more so-called "request-response" protocols. //! //! A request-response protocol works in the following way: //! @@ -41,17 +41,15 @@ use futures::{ prelude::*, }; use libp2p::{ - core::{connection::ConnectionId, Multiaddr, PeerId}, - request_response::{ - handler::RequestResponseHandler, ProtocolSupport, RequestResponse, RequestResponseCodec, - RequestResponseEvent, RequestResponseMessage, ResponseChannel, - }, + core::{Endpoint, Multiaddr}, + request_response::{self, Behaviour, Codec, Message, ProtocolSupport, ResponseChannel}, swarm::{ - behaviour::{ConnectionClosed, DialFailure, FromSwarm, ListenFailure}, + behaviour::{ConnectionClosed, FromSwarm}, handler::multi::MultiHandler, - ConnectionHandler, IntoConnectionHandler, NetworkBehaviour, NetworkBehaviourAction, - PollParameters, + ConnectionDenied, ConnectionId, NetworkBehaviour, PollParameters, THandler, + THandlerInEvent, THandlerOutEvent, ToSwarm, }, + PeerId, }; use sc_peerset::{PeersetHandle, BANNED_THRESHOLD}; @@ -64,9 +62,7 @@ use std::{ time::{Duration, Instant}, }; -pub use libp2p::request_response::{ - InboundFailure, OutboundFailure, RequestId, RequestResponseConfig, -}; +pub use libp2p::request_response::{Config, InboundFailure, OutboundFailure, RequestId}; /// Error in a request. #[derive(Debug, thiserror::Error)] @@ -260,14 +256,13 @@ impl From<(ProtocolName, RequestId)> for ProtocolRequestId { /// Implementation of `NetworkBehaviour` that provides support for request-response protocols. pub struct RequestResponsesBehaviour { /// The multiple sub-protocols, by name. - /// Contains the underlying libp2p `RequestResponse` behaviour, plus an optional + /// + /// Contains the underlying libp2p request-response [`Behaviour`], plus an optional /// "response builder" used to build responses for incoming requests. - protocols: HashMap< - ProtocolName, - (RequestResponse, Option>), - >, + protocols: + HashMap, Option>)>, - /// Pending requests, passed down to a [`RequestResponse`] behaviour, awaiting a reply. + /// Pending requests, passed down to a request-response [`Behaviour`], awaiting a reply. pending_requests: HashMap, RequestFailure>>)>, @@ -324,7 +319,7 @@ impl RequestResponsesBehaviour { ) -> Result { let mut protocols = HashMap::new(); for protocol in list { - let mut cfg = RequestResponseConfig::default(); + let mut cfg = Config::default(); cfg.set_connection_keep_alive(Duration::from_secs(10)); cfg.set_request_timeout(protocol.request_timeout); @@ -334,7 +329,7 @@ impl RequestResponsesBehaviour { ProtocolSupport::Outbound }; - let rq_rp = RequestResponse::new( + let rq_rp = Behaviour::new( GenericCodec { max_request_size: protocol.max_request_size, max_response_size: protocol.max_response_size, @@ -401,50 +396,79 @@ impl RequestResponsesBehaviour { ); } } - - fn new_handler_with_replacement( - &mut self, - protocol: String, - handler: RequestResponseHandler, - ) -> ::ConnectionHandler { - let mut handlers: HashMap<_, _> = self - .protocols - .iter_mut() - .map(|(p, (r, _))| (p.to_string(), NetworkBehaviour::new_handler(r))) - .collect(); - - if let Some(h) = handlers.get_mut(&protocol) { - *h = handler - } - - MultiHandler::try_from_iter(handlers).expect( - "Protocols are in a HashMap and there can be at most one handler per protocol name, \ - which is the only possible error; qed", - ) - } } impl NetworkBehaviour for RequestResponsesBehaviour { - type ConnectionHandler = MultiHandler< - String, - as NetworkBehaviour>::ConnectionHandler, - >; + type ConnectionHandler = + MultiHandler as NetworkBehaviour>::ConnectionHandler>; type OutEvent = Event; - fn new_handler(&mut self) -> Self::ConnectionHandler { - let iter = self - .protocols - .iter_mut() - .map(|(p, (r, _))| (p.to_string(), NetworkBehaviour::new_handler(r))); + fn handle_pending_inbound_connection( + &mut self, + _connection_id: ConnectionId, + _local_addr: &Multiaddr, + _remote_addr: &Multiaddr, + ) -> Result<(), ConnectionDenied> { + Ok(()) + } + + fn handle_pending_outbound_connection( + &mut self, + _connection_id: ConnectionId, + _maybe_peer: Option, + _addresses: &[Multiaddr], + _effective_role: Endpoint, + ) -> Result, ConnectionDenied> { + Ok(Vec::new()) + } + + fn handle_established_inbound_connection( + &mut self, + connection_id: ConnectionId, + peer: PeerId, + local_addr: &Multiaddr, + remote_addr: &Multiaddr, + ) -> Result, ConnectionDenied> { + let iter = self.protocols.iter_mut().filter_map(|(p, (r, _))| { + if let Ok(handler) = r.handle_established_inbound_connection( + connection_id, + peer, + local_addr, + remote_addr, + ) { + Some((p.to_string(), handler)) + } else { + None + } + }); - MultiHandler::try_from_iter(iter).expect( + Ok(MultiHandler::try_from_iter(iter).expect( "Protocols are in a HashMap and there can be at most one handler per protocol name, \ which is the only possible error; qed", - ) + )) } - fn addresses_of_peer(&mut self, _: &PeerId) -> Vec { - Vec::new() + fn handle_established_outbound_connection( + &mut self, + connection_id: ConnectionId, + peer: PeerId, + addr: &Multiaddr, + role_override: Endpoint, + ) -> Result, ConnectionDenied> { + let iter = self.protocols.iter_mut().filter_map(|(p, (r, _))| { + if let Ok(handler) = + r.handle_established_outbound_connection(connection_id, peer, addr, role_override) + { + Some((p.to_string(), handler)) + } else { + None + } + }); + + Ok(MultiHandler::try_from_iter(iter).expect( + "Protocols are in a HashMap and there can be at most one handler per protocol name, \ + which is the only possible error; qed", + )) } fn on_swarm_event(&mut self, event: FromSwarm) { @@ -477,42 +501,17 @@ impl NetworkBehaviour for RequestResponsesBehaviour { ) } }, - FromSwarm::DialFailure(DialFailure { peer_id, error, handler }) => { - for (p_name, p_handler) in handler.into_iter() { - if let Some((proto, _)) = self.protocols.get_mut(p_name.as_str()) { - proto.on_swarm_event(FromSwarm::DialFailure(DialFailure { - peer_id, - handler: p_handler, - error, - })); - } else { - log::error!( - target: "sub-libp2p", - "on_swarm_event/dial_failure: no request-response instance registered for protocol {:?}", - p_name, - ) - } - } - }, + FromSwarm::DialFailure(e) => + for (p, _) in self.protocols.values_mut() { + NetworkBehaviour::on_swarm_event(p, FromSwarm::DialFailure(e)); + }, FromSwarm::ListenerClosed(e) => for (p, _) in self.protocols.values_mut() { NetworkBehaviour::on_swarm_event(p, FromSwarm::ListenerClosed(e)); }, - FromSwarm::ListenFailure(ListenFailure { local_addr, send_back_addr, handler }) => - for (p_name, p_handler) in handler.into_iter() { - if let Some((proto, _)) = self.protocols.get_mut(p_name.as_str()) { - proto.on_swarm_event(FromSwarm::ListenFailure(ListenFailure { - local_addr, - send_back_addr, - handler: p_handler, - })); - } else { - log::error!( - target: "sub-libp2p", - "on_swarm_event/listen_failure: no request-response instance registered for protocol {:?}", - p_name, - ) - } + FromSwarm::ListenFailure(e) => + for (p, _) in self.protocols.values_mut() { + NetworkBehaviour::on_swarm_event(p, FromSwarm::ListenFailure(e)); }, FromSwarm::ListenerError(e) => for (p, _) in self.protocols.values_mut() { @@ -549,25 +548,25 @@ impl NetworkBehaviour for RequestResponsesBehaviour { &mut self, peer_id: PeerId, connection_id: ConnectionId, - (p_name, event): <::Handler as - ConnectionHandler>::OutEvent, + event: THandlerOutEvent, ) { - if let Some((proto, _)) = self.protocols.get_mut(&*p_name) { - return proto.on_connection_handler_event(peer_id, connection_id, event) + let p_name = event.0; + if let Some((proto, _)) = self.protocols.get_mut(p_name.as_str()) { + return proto.on_connection_handler_event(peer_id, connection_id, event.1) + } else { + log::warn!( + target: "sub-libp2p", + "on_connection_handler_event: no request-response instance registered for protocol {:?}", + p_name + ); } - - log::warn!( - target: "sub-libp2p", - "on_connection_handler_event: no request-response instance registered for protocol {:?}", - p_name - ); } fn poll( &mut self, cx: &mut Context, params: &mut impl PollParameters, - ) -> Poll> { + ) -> Poll>> { 'poll_all: loop { if let Some(message_request) = self.message_request.take() { // Now we can can poll `MessageRequest` until we get the reputation @@ -621,8 +620,8 @@ impl NetworkBehaviour for RequestResponsesBehaviour { // initialization. if let Some(mut resp_builder) = resp_builder { // If the response builder is too busy, silently drop `tx`. This - // will be reported by the corresponding `RequestResponse` through - // an `InboundFailure::Omission` event. + // will be reported by the corresponding request-response [`Behaviour`] + // through an `InboundFailure::Omission` event. let _ = resp_builder.try_send(IncomingRequest { peer, payload: request, @@ -674,7 +673,7 @@ impl NetworkBehaviour for RequestResponsesBehaviour { if let Some((protocol, _)) = self.protocols.get_mut(&*protocol_name) { if protocol.send_response(inner_channel, Ok(payload)).is_err() { // Note: Failure is handled further below when receiving - // `InboundFailure` event from `RequestResponse` behaviour. + // `InboundFailure` event from request-response [`Behaviour`]. log::debug!( target: "sub-libp2p", "Failed to send response for {:?} on protocol {:?} due to a \ @@ -690,9 +689,10 @@ impl NetworkBehaviour for RequestResponsesBehaviour { } if !reputation_changes.is_empty() { - return Poll::Ready(NetworkBehaviourAction::GenerateEvent( - Event::ReputationChanges { peer, changes: reputation_changes }, - )) + return Poll::Ready(ToSwarm::GenerateEvent(Event::ReputationChanges { + peer, + changes: reputation_changes, + })) } } @@ -701,44 +701,35 @@ impl NetworkBehaviour for RequestResponsesBehaviour { while let Poll::Ready(ev) = behaviour.poll(cx, params) { let ev = match ev { // Main events we are interested in. - NetworkBehaviourAction::GenerateEvent(ev) => ev, + ToSwarm::GenerateEvent(ev) => ev, // Other events generated by the underlying behaviour are transparently // passed through. - NetworkBehaviourAction::Dial { opts, handler } => { + ToSwarm::Dial { opts } => { if opts.get_peer_id().is_none() { log::error!( "The request-response isn't supposed to start dialing addresses" ); } - let protocol = protocol.to_string(); - let handler = self.new_handler_with_replacement(protocol, handler); - return Poll::Ready(NetworkBehaviourAction::Dial { opts, handler }) + return Poll::Ready(ToSwarm::Dial { opts }) }, - NetworkBehaviourAction::NotifyHandler { peer_id, handler, event } => - return Poll::Ready(NetworkBehaviourAction::NotifyHandler { + ToSwarm::NotifyHandler { peer_id, handler, event } => + return Poll::Ready(ToSwarm::NotifyHandler { peer_id, handler, event: ((*protocol).to_string(), event), }), - NetworkBehaviourAction::ReportObservedAddr { address, score } => - return Poll::Ready(NetworkBehaviourAction::ReportObservedAddr { - address, - score, - }), - NetworkBehaviourAction::CloseConnection { peer_id, connection } => - return Poll::Ready(NetworkBehaviourAction::CloseConnection { - peer_id, - connection, - }), + ToSwarm::ReportObservedAddr { address, score } => + return Poll::Ready(ToSwarm::ReportObservedAddr { address, score }), + ToSwarm::CloseConnection { peer_id, connection } => + return Poll::Ready(ToSwarm::CloseConnection { peer_id, connection }), }; match ev { // Received a request from a remote. - RequestResponseEvent::Message { + request_response::Event::Message { peer, - message: - RequestResponseMessage::Request { request_id, request, channel, .. }, + message: Message::Request { request_id, request, channel, .. }, } => { self.pending_responses_arrival_time .insert((protocol.clone(), request_id).into(), Instant::now()); @@ -765,9 +756,9 @@ impl NetworkBehaviour for RequestResponsesBehaviour { }, // Received a response from a remote to one of our requests. - RequestResponseEvent::Message { + request_response::Event::Message { peer, - message: RequestResponseMessage::Response { request_id, response }, + message: Message::Response { request_id, response }, .. } => { let (started, delivered) = match self @@ -798,12 +789,15 @@ impl NetworkBehaviour for RequestResponsesBehaviour { result: delivered, }; - return Poll::Ready(NetworkBehaviourAction::GenerateEvent(out)) + return Poll::Ready(ToSwarm::GenerateEvent(out)) }, // One of our requests has failed. - RequestResponseEvent::OutboundFailure { - peer, request_id, error, .. + request_response::Event::OutboundFailure { + peer, + request_id, + error, + .. } => { let started = match self .pending_requests @@ -841,12 +835,12 @@ impl NetworkBehaviour for RequestResponsesBehaviour { result: Err(RequestFailure::Network(error)), }; - return Poll::Ready(NetworkBehaviourAction::GenerateEvent(out)) + return Poll::Ready(ToSwarm::GenerateEvent(out)) }, // An inbound request failed, either while reading the request or due to // failing to send a response. - RequestResponseEvent::InboundFailure { + request_response::Event::InboundFailure { request_id, peer, error, .. } => { self.pending_responses_arrival_time @@ -857,11 +851,11 @@ impl NetworkBehaviour for RequestResponsesBehaviour { protocol: protocol.clone(), result: Err(ResponseFailure::Network(error)), }; - return Poll::Ready(NetworkBehaviourAction::GenerateEvent(out)) + return Poll::Ready(ToSwarm::GenerateEvent(out)) }, // A response to an inbound request has been sent. - RequestResponseEvent::ResponseSent { request_id, peer } => { + request_response::Event::ResponseSent { request_id, peer } => { let arrival_time = self .pending_responses_arrival_time .remove(&(protocol.clone(), request_id).into()) @@ -886,7 +880,7 @@ impl NetworkBehaviour for RequestResponsesBehaviour { result: Ok(arrival_time), }; - return Poll::Ready(NetworkBehaviourAction::GenerateEvent(out)) + return Poll::Ready(ToSwarm::GenerateEvent(out)) }, }; } @@ -913,7 +907,7 @@ pub enum ResponseFailure { Network(InboundFailure), } -/// Implements the libp2p [`RequestResponseCodec`] trait. Defines how streams of bytes are turned +/// Implements the libp2p [`Codec`] trait. Defines how streams of bytes are turned /// into requests and responses and vice-versa. #[derive(Debug, Clone)] #[doc(hidden)] // Needs to be public in order to satisfy the Rust compiler. @@ -923,7 +917,7 @@ pub struct GenericCodec { } #[async_trait::async_trait] -impl RequestResponseCodec for GenericCodec { +impl Codec for GenericCodec { type Protocol = Vec; type Request = Vec; type Response = Result, ()>; @@ -1054,7 +1048,7 @@ mod tests { }, identity::Keypair, noise, - swarm::{Executor, Swarm, SwarmEvent}, + swarm::{Executor, Swarm, SwarmBuilder, SwarmEvent}, Multiaddr, }; use sc_peerset::{Peerset, PeersetConfig, SetConfig}; @@ -1072,13 +1066,10 @@ mod tests { ) -> (Swarm, Multiaddr, Peerset) { let keypair = Keypair::generate_ed25519(); - let noise_keys = - noise::Keypair::::new().into_authentic(&keypair).unwrap(); - let transport = MemoryTransport::new() .upgrade(upgrade::Version::V1) - .authenticate(noise::NoiseConfig::xx(noise_keys).into_authenticated()) - .multiplex(libp2p::yamux::YamuxConfig::default()) + .authenticate(noise::Config::new(&keypair).unwrap()) + .multiplex(libp2p::yamux::Config::default()) .boxed(); let config = PeersetConfig { @@ -1096,12 +1087,13 @@ mod tests { let behaviour = RequestResponsesBehaviour::new(list, handle).unwrap(); let runtime = tokio::runtime::Runtime::new().unwrap(); - let mut swarm = Swarm::with_executor( + let mut swarm = SwarmBuilder::with_executor( transport, behaviour, keypair.public().to_peer_id(), TokioExecutor(runtime), - ); + ) + .build(); let listen_addr: Multiaddr = format!("/memory/{}", rand::random::()).parse().unwrap(); swarm.listen_on(listen_addr.clone()).unwrap(); @@ -1117,7 +1109,7 @@ mod tests { let protocol_name = "/test/req-resp/1"; let mut pool = LocalPool::new(); - // Build swarms whose behaviour is `RequestResponsesBehaviour`. + // Build swarms whose behaviour is [`RequestResponsesBehaviour`]. let mut swarms = (0..2) .map(|_| { let (tx, mut rx) = mpsc::channel::(64); @@ -1220,7 +1212,7 @@ mod tests { let protocol_name = "/test/req-resp/1"; let mut pool = LocalPool::new(); - // Build swarms whose behaviour is `RequestResponsesBehaviour`. + // Build swarms whose behaviour is [`RequestResponsesBehaviour`]. let mut swarms = (0..2) .map(|_| { let (tx, mut rx) = mpsc::channel::(64); @@ -1322,10 +1314,10 @@ mod tests { } /// A [`RequestId`] is a unique identifier among either all inbound or all outbound requests for - /// a single [`RequestResponse`] behaviour. It is not guaranteed to be unique across multiple - /// [`RequestResponse`] behaviours. Thus when handling [`RequestId`] in the context of multiple - /// [`RequestResponse`] behaviours, one needs to couple the protocol name with the [`RequestId`] - /// to get a unique request identifier. + /// a single [`RequestResponsesBehaviour`] behaviour. It is not guaranteed to be unique across + /// multiple [`RequestResponsesBehaviour`] behaviours. Thus when handling [`RequestId`] in the + /// context of multiple [`RequestResponsesBehaviour`] behaviours, one needs to couple the + /// protocol name with the [`RequestId`] to get a unique request identifier. /// /// This test ensures that two requests on different protocols can be handled concurrently /// without a [`RequestId`] collision. diff --git a/client/network/src/service.rs b/client/network/src/service.rs index 75c9d6692..cd8e18a2e 100644 --- a/client/network/src/service.rs +++ b/client/network/src/service.rs @@ -52,17 +52,19 @@ use crate::{ ReputationChange, }; +use either::Either; use futures::{channel::oneshot, prelude::*}; +#[allow(deprecated)] use libp2p::{ - core::{either::EitherError, upgrade, ConnectedPoint}, + connection_limits::Exceeded, + core::{upgrade, ConnectedPoint, Endpoint}, identify::Info as IdentifyInfo, kad::record::Key as KademliaKey, multiaddr, ping::Failure as PingFailure, swarm::{ - AddressScore, ConnectionError, ConnectionHandler, ConnectionLimits, DialError, Executor, - IntoConnectionHandler, NetworkBehaviour, PendingConnectionError, Swarm, SwarmBuilder, - SwarmEvent, + AddressScore, ConnectionError, ConnectionId, ConnectionLimits, DialError, Executor, + ListenError, NetworkBehaviour, Swarm, SwarmBuilder, SwarmEvent, THandlerErr, }, Multiaddr, PeerId, }; @@ -90,7 +92,7 @@ use std::{ }; pub use behaviour::{InboundFailure, OutboundFailure, ResponseFailure}; -pub use libp2p::identity::{error::DecodingError, Keypair, PublicKey}; +pub use libp2p::identity::{DecodingError, Keypair, PublicKey}; pub use protocol::NotificationsSink; mod metrics; @@ -99,12 +101,6 @@ mod out_events; pub mod signature; pub mod traits; -/// Custom error that can be produced by the [`ConnectionHandler`] of the [`NetworkBehaviour`]. -/// Used as a template parameter of [`SwarmEvent`] below. -type ConnectionHandlerErr = - <<::ConnectionHandler as IntoConnectionHandler> - ::Handler as ConnectionHandler>::Error; - /// Substrate network service. Handles network IO and manages connectivity. pub struct NetworkService { /// Number of peers we're connected to. @@ -311,7 +307,7 @@ where format!("{} ({})", network_config.client_version, network_config.node_name); let discovery_config = { - let mut config = DiscoveryConfig::new(local_public.clone()); + let mut config = DiscoveryConfig::new(local_public.to_peer_id()); config.with_permanent_addresses(known_addresses); config.discovery_limit(u64::from(network_config.default_peers_set.out_peers) + 15); config.with_kademlia( @@ -374,6 +370,7 @@ where SpawnImpl(params.executor), ) }; + #[allow(deprecated)] let builder = builder .connection_limits( ConnectionLimits::default() @@ -384,7 +381,9 @@ where ) .substream_upgrade_protocol_override(upgrade::Version::V1Lazy) .notify_handler_buffer_size(NonZeroUsize::new(32).expect("32 != 0; qed")) - .connection_event_buffer_size(1024) + // NOTE: 24 is somewhat arbitrary and should be tuned in the future if necessary. + // See + .per_connection_event_buffer_size(24) .max_negotiating_inbound_streams(2048); (builder.build(), bandwidth) @@ -509,15 +508,23 @@ where pub fn network_state(&mut self) -> NetworkState { let swarm = &mut self.network_service; let open = swarm.behaviour_mut().user_protocol().open_peers().cloned().collect::>(); - let connected_peers = { let swarm = &mut *swarm; open.iter() .filter_map(move |peer_id| { - let known_addresses = - NetworkBehaviour::addresses_of_peer(swarm.behaviour_mut(), peer_id) - .into_iter() - .collect(); + let known_addresses = if let Ok(addrs) = + NetworkBehaviour::handle_pending_outbound_connection( + swarm.behaviour_mut(), + ConnectionId::new_unchecked(0), // dummy value + Some(*peer_id), + &vec![], + Endpoint::Listener, + ) { + addrs.into_iter().collect() + } else { + error!(target: "sub-libp2p", "Was not able to get known addresses for {:?}", peer_id); + return None + }; let endpoint = if let Some(e) = swarm.behaviour_mut().node(peer_id).and_then(|i| i.endpoint()) @@ -556,6 +563,20 @@ where .into_iter() .filter(|p| open.iter().all(|n| n != p)) .map(move |peer_id| { + let known_addresses = if let Ok(addrs) = + NetworkBehaviour::handle_pending_outbound_connection( + swarm.behaviour_mut(), + ConnectionId::new_unchecked(0), // dummy value + Some(peer_id), + &vec![], + Endpoint::Listener, + ) { + addrs.into_iter().collect() + } else { + error!(target: "sub-libp2p", "Was not able to get known addresses for {:?}", peer_id); + Default::default() + }; + ( peer_id.to_base58(), NetworkStateNotConnectedPeer { @@ -567,12 +588,7 @@ where .behaviour_mut() .node(&peer_id) .and_then(|i| i.latest_ping()), - known_addresses: NetworkBehaviour::addresses_of_peer( - swarm.behaviour_mut(), - &peer_id, - ) - .into_iter() - .collect(), + known_addresses, }, ) }) @@ -1340,10 +1356,7 @@ where } /// Process the next event coming from `Swarm`. - fn handle_swarm_event( - &mut self, - event: SwarmEvent>>, - ) { + fn handle_swarm_event(&mut self, event: SwarmEvent>>) { match event { SwarmEvent::Behaviour(BehaviourOut::InboundRequest { protocol, result, .. }) => { if let Some(metrics) = self.metrics.as_ref() { @@ -1572,6 +1585,7 @@ where endpoint, num_established, concurrent_dial_errors, + .. } => { if let Some(errors) = concurrent_dial_errors { debug!(target: "sub-libp2p", "Libp2p => Connected({:?}) with errors: {:?}", peer_id, errors); @@ -1600,11 +1614,11 @@ where }; let reason = match cause { Some(ConnectionError::IO(_)) => "transport-error", - Some(ConnectionError::Handler(EitherError::A(EitherError::A( - EitherError::B(EitherError::A(PingFailure::Timeout)), + Some(ConnectionError::Handler(Either::Left(Either::Left( + Either::Right(Either::Left(PingFailure::Timeout)), )))) => "ping-timeout", - Some(ConnectionError::Handler(EitherError::A(EitherError::A( - EitherError::A(NotifsHandlerError::SyncNotificationsClogged), + Some(ConnectionError::Handler(Either::Left(Either::Left( + Either::Left(NotifsHandlerError::SyncNotificationsClogged), )))) => "sync-notifications-clogged", Some(ConnectionError::Handler(_)) => "protocol-error", Some(ConnectionError::KeepAliveTimeout) => "keep-alive-timeout", @@ -1653,16 +1667,22 @@ where } if let Some(metrics) = self.metrics.as_ref() { + #[allow(deprecated)] let reason = match error { + DialError::Denied { cause } => + if cause.downcast::().is_ok() { + Some("limit-reached") + } else { + None + }, DialError::ConnectionLimit(_) => Some("limit-reached"), - DialError::InvalidPeerId(_) => Some("invalid-peer-id"), - DialError::Transport(_) | DialError::ConnectionIo(_) => - Some("transport-error"), + DialError::InvalidPeerId(_) | + DialError::WrongPeerId { .. } | + DialError::LocalPeerId { .. } => Some("invalid-peer-id"), + DialError::Transport(_) => Some("transport-error"), DialError::Banned | - DialError::LocalPeerId | DialError::NoAddresses | DialError::DialPeerConditionFalse(_) | - DialError::WrongPeerId { .. } | DialError::Aborted => None, // ignore them }; if let Some(reason) = reason { @@ -1687,12 +1707,19 @@ where local_addr, send_back_addr, error, ); if let Some(metrics) = self.metrics.as_ref() { + #[allow(deprecated)] let reason = match error { - PendingConnectionError::ConnectionLimit(_) => Some("limit-reached"), - PendingConnectionError::WrongPeerId { .. } => Some("invalid-peer-id"), - PendingConnectionError::Transport(_) | PendingConnectionError::IO(_) => - Some("transport-error"), - PendingConnectionError::Aborted => None, // ignore it + ListenError::Denied { cause } => + if cause.downcast::().is_ok() { + Some("limit-reached") + } else { + None + }, + ListenError::ConnectionLimit(_) => Some("limit-reached"), + ListenError::WrongPeerId { .. } | ListenError::LocalPeerId { .. } => + Some("invalid-peer-id"), + ListenError::Transport(_) => Some("transport-error"), + ListenError::Aborted => None, // ignore it }; if let Some(reason) = reason { @@ -1703,6 +1730,7 @@ where } } }, + #[allow(deprecated)] SwarmEvent::BannedPeer { peer_id, endpoint } => { debug!( target: "sub-libp2p", diff --git a/client/network/src/service/signature.rs b/client/network/src/service/signature.rs index e52dd6b1d..024f60e4c 100644 --- a/client/network/src/service/signature.rs +++ b/client/network/src/service/signature.rs @@ -23,7 +23,7 @@ use libp2p::{ PeerId, }; -pub use libp2p::identity::error::SigningError; +pub use libp2p::identity::SigningError; /// A result of signing a message with a network identity. Since `PeerId` is potentially a hash of a /// `PublicKey`, you need to reveal the `PublicKey` next to the signature, so the verifier can check diff --git a/client/network/src/service/traits.rs b/client/network/src/service/traits.rs index 3f9b7e552..787ef4b5a 100644 --- a/client/network/src/service/traits.rs +++ b/client/network/src/service/traits.rs @@ -33,7 +33,7 @@ use sc_peerset::ReputationChange; use std::{collections::HashSet, future::Future, pin::Pin, sync::Arc}; -pub use libp2p::{identity::error::SigningError, kad::record::Key as KademliaKey}; +pub use libp2p::{identity::SigningError, kad::record::Key as KademliaKey}; /// Signer with network identity pub trait NetworkSigner { diff --git a/client/network/src/transport.rs b/client/network/src/transport.rs index 3aa0bb48d..9e63ce988 100644 --- a/client/network/src/transport.rs +++ b/client/network/src/transport.rs @@ -16,20 +16,18 @@ // You should have received a copy of the GNU General Public License // along with this program. If not, see . +use either::Either; use libp2p::{ - bandwidth, core::{ - self, - either::EitherTransport, muxing::StreamMuxerBox, transport::{Boxed, OptionalTransport}, upgrade, }, - dns, identity, mplex, noise, tcp, websocket, PeerId, Transport, + dns, identity, noise, tcp, websocket, PeerId, Transport, TransportExt, }; use std::{sync::Arc, time::Duration}; -pub use self::bandwidth::BandwidthSinks; +pub use libp2p::bandwidth::BandwidthSinks; /// Builds the transport that serves as a common ground for all connections. /// @@ -59,7 +57,7 @@ pub fn build_transport( let tcp_trans = tcp::tokio::Transport::new(tcp_config.clone()); let dns_init = dns::TokioDnsConfig::system(tcp_trans); - EitherTransport::Left(if let Ok(dns) = dns_init { + Either::Left(if let Ok(dns) = dns_init { // WS + WSS transport // // Main transport can't be used for `/wss` addresses because WSS transport needs @@ -68,47 +66,21 @@ pub fn build_transport( let tcp_trans = tcp::tokio::Transport::new(tcp_config); let dns_for_wss = dns::TokioDnsConfig::system(tcp_trans) .expect("same system_conf & resolver to work"); - EitherTransport::Left(websocket::WsConfig::new(dns_for_wss).or_transport(dns)) + Either::Left(websocket::WsConfig::new(dns_for_wss).or_transport(dns)) } else { // In case DNS can't be constructed, fallback to TCP + WS (WSS won't work) let tcp_trans = tcp::tokio::Transport::new(tcp_config.clone()); let desktop_trans = websocket::WsConfig::new(tcp_trans) .or_transport(tcp::tokio::Transport::new(tcp_config)); - EitherTransport::Right(desktop_trans) + Either::Right(desktop_trans) }) } else { - EitherTransport::Right(OptionalTransport::some( - libp2p::core::transport::MemoryTransport::default(), - )) + Either::Right(OptionalTransport::some(libp2p::core::transport::MemoryTransport::default())) }; - let (transport, bandwidth) = bandwidth::BandwidthLogging::new(transport); - - let authentication_config = - { - // For more information about these two panics, see in "On the Importance of - // Checking Cryptographic Protocols for Faults" by Dan Boneh, Richard A. DeMillo, - // and Richard J. Lipton. - let noise_keypair = noise::Keypair::::new().into_authentic(&keypair) - .expect("can only fail in case of a hardware bug; since this signing is performed only \ - once and at initialization, we're taking the bet that the inconvenience of a very \ - rare panic here is basically zero"); - - // Legacy noise configurations for backward compatibility. - let noise_legacy = - noise::LegacyConfig { recv_legacy_handshake: true, ..Default::default() }; - - let mut xx_config = noise::NoiseConfig::xx(noise_keypair); - xx_config.set_legacy_config(noise_legacy); - xx_config.into_authenticated() - }; - + let authentication_config = noise::Config::new(&keypair).expect("Can create noise config. qed"); let multiplexing_config = { - let mut mplex_config = mplex::MplexConfig::new(); - mplex_config.set_max_buffer_behaviour(mplex::MaxBufferBehaviour::Block); - mplex_config.set_max_buffer_size(usize::MAX); - - let mut yamux_config = libp2p::yamux::YamuxConfig::default(); + let mut yamux_config = libp2p::yamux::Config::default(); // Enable proper flow-control: window updates are only sent when // buffered data has been consumed. yamux_config.set_window_update_mode(libp2p::yamux::WindowUpdateMode::on_read()); @@ -118,7 +90,7 @@ pub fn build_transport( yamux_config.set_receive_window_size(yamux_window_size); } - core::upgrade::SelectUpgrade::new(yamux_config, mplex_config) + yamux_config }; let transport = transport @@ -128,5 +100,5 @@ pub fn build_transport( .timeout(Duration::from_secs(20)) .boxed(); - (transport, bandwidth) + transport.with_bandwidth_logging() } diff --git a/client/network/statement/Cargo.toml b/client/network/statement/Cargo.toml index 36d8cb077..a81e6a916 100644 --- a/client/network/statement/Cargo.toml +++ b/client/network/statement/Cargo.toml @@ -17,7 +17,7 @@ array-bytes = "4.1" async-channel = "1.8.0" codec = { package = "parity-scale-codec", version = "3.2.2", features = ["derive"] } futures = "0.3.21" -libp2p = "0.50.0" +libp2p = "0.51.3" log = "0.4.17" pin-project = "1.0.12" prometheus-endpoint = { package = "substrate-prometheus-endpoint", version = "0.10.0-dev", path = "../../../utils/prometheus" } diff --git a/client/network/sync/Cargo.toml b/client/network/sync/Cargo.toml index 298cbf180..ce7135960 100644 --- a/client/network/sync/Cargo.toml +++ b/client/network/sync/Cargo.toml @@ -21,7 +21,7 @@ async-trait = "0.1.58" codec = { package = "parity-scale-codec", version = "3.2.2", features = ["derive"] } futures = "0.3.21" futures-timer = "3.0.2" -libp2p = "0.50.0" +libp2p = "0.51.3" log = "0.4.17" lru = "0.8.1" mockall = "0.11.3" diff --git a/client/network/test/Cargo.toml b/client/network/test/Cargo.toml index 9763feed5..af519008d 100644 --- a/client/network/test/Cargo.toml +++ b/client/network/test/Cargo.toml @@ -17,7 +17,7 @@ tokio = "1.22.0" async-trait = "0.1.57" futures = "0.3.21" futures-timer = "3.0.1" -libp2p = "0.50.0" +libp2p = "0.51.3" log = "0.4.17" parking_lot = "0.12.1" rand = "0.8.5" diff --git a/client/network/transactions/Cargo.toml b/client/network/transactions/Cargo.toml index 3616473d3..3ae1dc590 100644 --- a/client/network/transactions/Cargo.toml +++ b/client/network/transactions/Cargo.toml @@ -16,7 +16,7 @@ targets = ["x86_64-unknown-linux-gnu"] array-bytes = "4.1" codec = { package = "parity-scale-codec", version = "3.2.2", features = ["derive"] } futures = "0.3.21" -libp2p = "0.50.0" +libp2p = "0.51.3" log = "0.4.17" pin-project = "1.0.12" prometheus-endpoint = { package = "substrate-prometheus-endpoint", version = "0.10.0-dev", path = "../../../utils/prometheus" } diff --git a/client/offchain/Cargo.toml b/client/offchain/Cargo.toml index 0307e3125..a2ab54ba5 100644 --- a/client/offchain/Cargo.toml +++ b/client/offchain/Cargo.toml @@ -21,7 +21,7 @@ futures = "0.3.21" futures-timer = "3.0.2" hyper = { version = "0.14.16", features = ["stream", "http2"] } hyper-rustls = { version = "0.23.0", features = ["http2"] } -libp2p = "0.50.0" +libp2p = "0.51.3" num_cpus = "1.13" once_cell = "1.8" parking_lot = "0.12.1" diff --git a/client/peerset/Cargo.toml b/client/peerset/Cargo.toml index a09508c83..043f8a835 100644 --- a/client/peerset/Cargo.toml +++ b/client/peerset/Cargo.toml @@ -15,7 +15,7 @@ targets = ["x86_64-unknown-linux-gnu"] [dependencies] futures = "0.3.21" -libp2p = "0.50.0" +libp2p-identity = { version = "0.1.2", features = ["peerid", "ed25519"] } log = "0.4.17" serde_json = "1.0.85" wasm-timer = "0.2" diff --git a/client/peerset/src/lib.rs b/client/peerset/src/lib.rs index e5393acba..e169be8e8 100644 --- a/client/peerset/src/lib.rs +++ b/client/peerset/src/lib.rs @@ -46,7 +46,7 @@ use std::{ }; use wasm_timer::Delay; -pub use libp2p::PeerId; +pub use libp2p_identity::PeerId; /// We don't accept nodes whose reputation is under this value. pub const BANNED_THRESHOLD: i32 = 82 * (i32::MIN / 100); @@ -781,7 +781,7 @@ mod tests { BANNED_THRESHOLD, }; use futures::prelude::*; - use libp2p::PeerId; + use libp2p_identity::PeerId; use std::{pin::Pin, task::Poll, thread, time::Duration}; fn assert_messages(mut peerset: Peerset, messages: Vec) -> Peerset { diff --git a/client/peerset/src/peersstate.rs b/client/peerset/src/peersstate.rs index 84907ac91..2d4a9295c 100644 --- a/client/peerset/src/peersstate.rs +++ b/client/peerset/src/peersstate.rs @@ -28,7 +28,7 @@ //! > for example connecting to some nodes in priority should be added outside of this //! > module, rather than inside. -use libp2p::PeerId; +use libp2p_identity::PeerId; use log::error; use std::{ borrow::Cow, @@ -626,7 +626,7 @@ impl<'a> Drop for Reputation<'a> { #[cfg(test)] mod tests { use super::{Peer, PeersState, SetConfig}; - use libp2p::PeerId; + use libp2p_identity::PeerId; use std::iter; #[test] diff --git a/client/peerset/tests/fuzz.rs b/client/peerset/tests/fuzz.rs index 1f4bd053b..122f17062 100644 --- a/client/peerset/tests/fuzz.rs +++ b/client/peerset/tests/fuzz.rs @@ -17,7 +17,7 @@ // along with this program. If not, see . use futures::prelude::*; -use libp2p::PeerId; +use libp2p_identity::PeerId; use rand::{ distributions::{Distribution, Uniform, WeightedIndex}, seq::IteratorRandom, diff --git a/client/telemetry/Cargo.toml b/client/telemetry/Cargo.toml index 4d09a2837..1d4758c7d 100644 --- a/client/telemetry/Cargo.toml +++ b/client/telemetry/Cargo.toml @@ -16,7 +16,7 @@ targets = ["x86_64-unknown-linux-gnu"] [dependencies] chrono = "0.4.19" futures = "0.3.21" -libp2p = { version = "0.50.0", features = ["dns", "tcp", "tokio", "wasm-ext", "websocket"] } +libp2p = { version = "0.51.3", features = ["dns", "tcp", "tokio", "wasm-ext", "websocket"] } log = "0.4.17" parking_lot = "0.12.1" pin-project = "1.0.12" From fd4c7b38c952cbac826e2da0a61ea40965f4296a Mon Sep 17 00:00:00 2001 From: Arkadiy Paronyan Date: Fri, 12 May 2023 10:44:55 +0200 Subject: [PATCH 492/558] Bump parity-db (#14114) --- bin/node/bench/Cargo.toml | 2 +- client/db/Cargo.toml | 2 +- client/statement-store/Cargo.toml | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/bin/node/bench/Cargo.toml b/bin/node/bench/Cargo.toml index 2cbe7577f..ea15fce4c 100644 --- a/bin/node/bench/Cargo.toml +++ b/bin/node/bench/Cargo.toml @@ -38,7 +38,7 @@ tempfile = "3.1.0" fs_extra = "1" rand = { version = "0.8.5", features = ["small_rng"] } lazy_static = "1.4.0" -parity-db = "0.4.6" +parity-db = "0.4.7" sc-transaction-pool = { version = "4.0.0-dev", path = "../../../client/transaction-pool" } sc-transaction-pool-api = { version = "4.0.0-dev", path = "../../../client/transaction-pool/api" } futures = { version = "0.3.21", features = ["thread-pool"] } diff --git a/client/db/Cargo.toml b/client/db/Cargo.toml index fe164793d..7d522b404 100644 --- a/client/db/Cargo.toml +++ b/client/db/Cargo.toml @@ -22,7 +22,7 @@ kvdb-memorydb = "0.13.0" kvdb-rocksdb = { version = "0.19.0", optional = true } linked-hash-map = "0.5.4" log = "0.4.17" -parity-db = "0.4.6" +parity-db = "0.4.7" parking_lot = "0.12.1" sc-client-api = { version = "4.0.0-dev", path = "../api" } sc-state-db = { version = "0.10.0-dev", path = "../state-db" } diff --git a/client/statement-store/Cargo.toml b/client/statement-store/Cargo.toml index d9c9f238d..5a4948cee 100644 --- a/client/statement-store/Cargo.toml +++ b/client/statement-store/Cargo.toml @@ -19,7 +19,7 @@ futures = "0.3.21" futures-timer = "3.0.2" log = "0.4.17" parking_lot = "0.12.1" -parity-db = "0.4.6" +parity-db = "0.4.7" tokio = { version = "1.22.0", features = ["time"] } sp-statement-store = { version = "4.0.0-dev", path = "../../primitives/statement-store" } prometheus-endpoint = { package = "substrate-prometheus-endpoint", version = "0.10.0-dev", path = "../../utils/prometheus" } From 2fc9c39a9fb9f38b6256f82cda4c955eb0de2706 Mon Sep 17 00:00:00 2001 From: Sam Johnson Date: Fri, 12 May 2023 08:55:28 -0400 Subject: [PATCH 493/558] fix genesis broken gensis config doc link (#14125) --- frame/support/procedural/src/pallet/expand/genesis_config.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frame/support/procedural/src/pallet/expand/genesis_config.rs b/frame/support/procedural/src/pallet/expand/genesis_config.rs index 85ad20dd4..7c66ef7d3 100644 --- a/frame/support/procedural/src/pallet/expand/genesis_config.rs +++ b/frame/support/procedural/src/pallet/expand/genesis_config.rs @@ -89,7 +89,7 @@ pub fn expand_genesis_config(def: &mut Def) -> proc_macro2::TokenStream { attrs.push(syn::parse_quote!( #[doc = r" Can be used to configure the - [genesis state](https://docs.substrate.io/v3/runtime/chain-specs#the-genesis-state) + [genesis state](https://docs.substrate.io/build/genesis-configuration/) of this pallet. "] )); From d3bb3e853bfeeb445ab6a9d86303525c7aa21f8d Mon Sep 17 00:00:00 2001 From: yjh Date: Sat, 13 May 2023 04:36:56 +0800 Subject: [PATCH 494/558] chore(storage-monitor): improve `free_space` calculation and cli default value (#14133) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * chore(storage-monitor): fix free_space calculation * add `Result` type * add docs * update `polling_period` default value to `6` * Update client/storage-monitor/src/lib.rs Co-authored-by: Michal Kucharczyk <1728078+michalkucharczyk@users.noreply.github.com> * Apply suggestions from code review --------- Co-authored-by: Michal Kucharczyk <1728078+michalkucharczyk@users.noreply.github.com> Co-authored-by: Bastian Köcher --- client/storage-monitor/src/lib.rs | 36 ++++++++++++++++--------------- 1 file changed, 19 insertions(+), 17 deletions(-) diff --git a/client/storage-monitor/src/lib.rs b/client/storage-monitor/src/lib.rs index 277a74de0..655b940e8 100644 --- a/client/storage-monitor/src/lib.rs +++ b/client/storage-monitor/src/lib.rs @@ -27,12 +27,15 @@ use std::{ const LOG_TARGET: &str = "storage-monitor"; +/// Result type used in this crate. +pub type Result = std::result::Result; + /// Error type used in this crate. #[derive(Debug, thiserror::Error)] pub enum Error { #[error("IO Error")] IOError(#[from] io::Error), - #[error("Out of storage space: available {0}MB, required {1}MB")] + #[error("Out of storage space: available {0}MiB, required {1}MiB")] StorageOutOfSpace(u64, u64), } @@ -42,7 +45,7 @@ pub struct StorageMonitorParams { /// Required available space on database storage. If available space for DB storage drops below /// the given threshold, node will be gracefully terminated. If `0` is given monitoring will be /// disabled. - #[arg(long = "db-storage-threshold", value_name = "MB", default_value_t = 1000)] + #[arg(long = "db-storage-threshold", value_name = "MiB", default_value_t = 1024)] pub threshold: u64, /// How often available space is polled. @@ -50,14 +53,14 @@ pub struct StorageMonitorParams { pub polling_period: u32, } -/// Storage monitor service: checks the available space for the filesystem for fiven path. +/// Storage monitor service: checks the available space for the filesystem for given path. pub struct StorageMonitorService { /// watched path path: PathBuf, /// number of megabytes that shall be free on the filesystem for watched path threshold: u64, - /// storage space polling period (seconds) - polling_period: u32, + /// storage space polling period + polling_period: Duration, } impl StorageMonitorService { @@ -66,7 +69,7 @@ impl StorageMonitorService { parameters: StorageMonitorParams, database: DatabaseSource, spawner: &impl SpawnEssentialNamed, - ) -> Result<(), Error> { + ) -> Result<()> { Ok(match (parameters.threshold, database.path()) { (0, _) => { log::info!( @@ -83,8 +86,7 @@ impl StorageMonitorService { (threshold, Some(path)) => { log::debug!( target: LOG_TARGET, - "Initializing StorageMonitorService for db path: {:?}", - path, + "Initializing StorageMonitorService for db path: {path:?}", ); Self::check_free_space(&path, threshold)?; @@ -92,7 +94,7 @@ impl StorageMonitorService { let storage_monitor_service = StorageMonitorService { path: path.to_path_buf(), threshold, - polling_period: parameters.polling_period, + polling_period: Duration::from_secs(parameters.polling_period.into()), }; spawner.spawn_essential( @@ -108,22 +110,22 @@ impl StorageMonitorService { /// below threshold. async fn run(self) { loop { - tokio::time::sleep(Duration::from_secs(self.polling_period.into())).await; + tokio::time::sleep(self.polling_period).await; if Self::check_free_space(&self.path, self.threshold).is_err() { break }; } } - /// Returns free space in MB, or error if statvfs failed. - fn free_space(path: &Path) -> Result { - Ok(fs4::available_space(path).map(|s| s / 1_000_000)?) + /// Returns free space in MiB, or error if statvfs failed. + fn free_space(path: &Path) -> Result { + Ok(fs4::available_space(path).map(|s| s / 1024 / 1024)?) } - /// Checks if the amount of free space for given `path` is above given `threshold`. + /// Checks if the amount of free space for given `path` is above given `threshold` in MiB. /// If it dropped below, error is returned. /// System errors are silently ignored. - fn check_free_space(path: &Path, threshold: u64) -> Result<(), Error> { + fn check_free_space(path: &Path, threshold: u64) -> Result<()> { match StorageMonitorService::free_space(path) { Ok(available_space) => { log::trace!( @@ -132,14 +134,14 @@ impl StorageMonitorService { ); if available_space < threshold { - log::error!(target: LOG_TARGET, "Available space {available_space}MB for path `{}` dropped below threshold: {threshold}MB , terminating...", path.display()); + log::error!(target: LOG_TARGET, "Available space {available_space}MiB for path `{}` dropped below threshold: {threshold}MiB , terminating...", path.display()); Err(Error::StorageOutOfSpace(available_space, threshold)) } else { Ok(()) } }, Err(e) => { - log::error!(target: LOG_TARGET, "Could not read available space: {:?}.", e); + log::error!(target: LOG_TARGET, "Could not read available space: {e:?}."); Err(e) }, } From 1f9e3aad2fcd41ec8dd5d022d8cbd78cf904fc2f Mon Sep 17 00:00:00 2001 From: yjh Date: Sun, 14 May 2023 22:32:27 +0800 Subject: [PATCH 495/558] remove `sp-serializer` which is not useful (#14134) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * remove `sp-serializer` which is not useful * Update primitives/core/Cargo.toml * fix cargo.toml * ".git/.scripts/commands/fmt/fmt.sh" --------- Co-authored-by: Bastian Köcher Co-authored-by: command-bot <> --- Cargo.lock | 9 ------- Cargo.toml | 1 - primitives/core/Cargo.toml | 1 - primitives/core/src/hash.rs | 27 +++++++++++-------- primitives/core/src/uint.rs | 18 +++++++------ primitives/serializer/Cargo.toml | 18 ------------- primitives/serializer/README.md | 6 ----- primitives/serializer/src/lib.rs | 45 -------------------------------- 8 files changed, 26 insertions(+), 99 deletions(-) delete mode 100644 primitives/serializer/Cargo.toml delete mode 100644 primitives/serializer/README.md delete mode 100644 primitives/serializer/src/lib.rs diff --git a/Cargo.lock b/Cargo.lock index d9cb87af4..547721957 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -10900,7 +10900,6 @@ dependencies = [ "sp-debug-derive", "sp-externalities", "sp-runtime-interface", - "sp-serializer", "sp-std", "sp-storage", "ss58-registry", @@ -11218,14 +11217,6 @@ dependencies = [ "substrate-wasm-builder", ] -[[package]] -name = "sp-serializer" -version = "4.0.0-dev" -dependencies = [ - "serde", - "serde_json", -] - [[package]] name = "sp-session" version = "4.0.0-dev" diff --git a/Cargo.toml b/Cargo.toml index 7563a4a64..2b1659d4f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -219,7 +219,6 @@ members = [ "primitives/runtime-interface/test", "primitives/runtime-interface/test-wasm", "primitives/runtime-interface/test-wasm-deprecated", - "primitives/serializer", "primitives/session", "primitives/staking", "primitives/state-machine", diff --git a/primitives/core/Cargo.toml b/primitives/core/Cargo.toml index 0c203a03d..f99ed6c53 100644 --- a/primitives/core/Cargo.toml +++ b/primitives/core/Cargo.toml @@ -56,7 +56,6 @@ sp-runtime-interface = { version = "7.0.0", default-features = false, path = ".. w3f-bls = { version = "0.1.3", default-features = false, optional = true} [dev-dependencies] -sp-serializer = { version = "4.0.0-dev", path = "../serializer" } rand = "0.8.5" criterion = "0.4.0" serde_json = "1.0" diff --git a/primitives/core/src/hash.rs b/primitives/core/src/hash.rs index ec7da9829..ece9b1af7 100644 --- a/primitives/core/src/hash.rs +++ b/primitives/core/src/hash.rs @@ -32,7 +32,6 @@ pub fn convert_hash, H2: AsRef<[u8]>>(src: &H2) -> H1 #[cfg(test)] mod tests { use super::*; - use sp_serializer as ser; #[test] fn test_h160() { @@ -47,8 +46,11 @@ mod tests { ]; for (number, expected) in tests { - assert_eq!(format!("{:?}", expected), ser::to_string_pretty(&number)); - assert_eq!(number, ser::from_str(&format!("{:?}", expected)).unwrap()); + assert_eq!( + format!("{:?}", expected), + serde_json::to_string_pretty(&number).expect("Json pretty print failed") + ); + assert_eq!(number, serde_json::from_str(&format!("{:?}", expected)).unwrap()); } } @@ -86,30 +88,33 @@ mod tests { ]; for (number, expected) in tests { - assert_eq!(format!("{:?}", expected), ser::to_string_pretty(&number)); - assert_eq!(number, ser::from_str(&format!("{:?}", expected)).unwrap()); + assert_eq!( + format!("{:?}", expected), + serde_json::to_string_pretty(&number).expect("Json pretty print failed") + ); + assert_eq!(number, serde_json::from_str(&format!("{:?}", expected)).unwrap()); } } #[test] fn test_invalid() { - assert!(ser::from_str::( + assert!(serde_json::from_str::( "\"0x000000000000000000000000000000000000000000000000000000000000000\"" ) .unwrap_err() .is_data()); - assert!(ser::from_str::( + assert!(serde_json::from_str::( "\"0x000000000000000000000000000000000000000000000000000000000000000g\"" ) .unwrap_err() .is_data()); - assert!(ser::from_str::( + assert!(serde_json::from_str::( "\"0x00000000000000000000000000000000000000000000000000000000000000000\"" ) .unwrap_err() .is_data()); - assert!(ser::from_str::("\"\"").unwrap_err().is_data()); - assert!(ser::from_str::("\"0\"").unwrap_err().is_data()); - assert!(ser::from_str::("\"10\"").unwrap_err().is_data()); + assert!(serde_json::from_str::("\"\"").unwrap_err().is_data()); + assert!(serde_json::from_str::("\"0\"").unwrap_err().is_data()); + assert!(serde_json::from_str::("\"10\"").unwrap_err().is_data()); } } diff --git a/primitives/core/src/uint.rs b/primitives/core/src/uint.rs index df49511e8..b251671db 100644 --- a/primitives/core/src/uint.rs +++ b/primitives/core/src/uint.rs @@ -23,7 +23,6 @@ pub use primitive_types::{U256, U512}; mod tests { use super::*; use codec::{Decode, Encode}; - use sp_serializer as ser; macro_rules! test { ($name: ident, $test_name: ident) => { @@ -43,14 +42,17 @@ mod tests { ]; for (number, expected) in tests { - assert_eq!(format!("{:?}", expected), ser::to_string_pretty(&number)); - assert_eq!(number, ser::from_str(&format!("{:?}", expected)).unwrap()); + assert_eq!( + format!("{:?}", expected), + serde_json::to_string_pretty(&number).expect("Json pretty print failed") + ); + assert_eq!(number, serde_json::from_str(&format!("{:?}", expected)).unwrap()); } // Invalid examples - assert!(ser::from_str::<$name>("\"0x\"").unwrap_err().is_data()); - assert!(ser::from_str::<$name>("\"0xg\"").unwrap_err().is_data()); - assert!(ser::from_str::<$name>("\"\"").unwrap_err().is_data()); + assert!(serde_json::from_str::<$name>("\"0x\"").unwrap_err().is_data()); + assert!(serde_json::from_str::<$name>("\"0xg\"").unwrap_err().is_data()); + assert!(serde_json::from_str::<$name>("\"\"").unwrap_err().is_data()); } }; } @@ -78,10 +80,10 @@ mod tests { #[test] fn test_large_values() { assert_eq!( - ser::to_string_pretty(&!U256::zero()), + serde_json::to_string_pretty(&!U256::zero()).expect("Json pretty print failed"), "\"0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff\"" ); - assert!(ser::from_str::( + assert!(serde_json::from_str::( "\"0x1ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff\"" ) .unwrap_err() diff --git a/primitives/serializer/Cargo.toml b/primitives/serializer/Cargo.toml deleted file mode 100644 index 585e4b4e0..000000000 --- a/primitives/serializer/Cargo.toml +++ /dev/null @@ -1,18 +0,0 @@ -[package] -name = "sp-serializer" -version = "4.0.0-dev" -authors = ["Parity Technologies "] -edition = "2021" -license = "Apache-2.0" -homepage = "https://substrate.io" -repository = "https://github.com/paritytech/substrate/" -description = "Substrate customizable serde serializer." -documentation = "https://docs.rs/sp-serializer" -readme = "README.md" - -[package.metadata.docs.rs] -targets = ["x86_64-unknown-linux-gnu"] - -[dependencies] -serde = "1.0.136" -serde_json = "1.0.85" diff --git a/primitives/serializer/README.md b/primitives/serializer/README.md deleted file mode 100644 index 083a0857c..000000000 --- a/primitives/serializer/README.md +++ /dev/null @@ -1,6 +0,0 @@ -Substrate customizable serde serializer. - -The idea is that we can later change the implementation -to something more compact, but for now we're using JSON. - -License: Apache-2.0 \ No newline at end of file diff --git a/primitives/serializer/src/lib.rs b/primitives/serializer/src/lib.rs deleted file mode 100644 index 3d4270714..000000000 --- a/primitives/serializer/src/lib.rs +++ /dev/null @@ -1,45 +0,0 @@ -// This file is part of Substrate. - -// Copyright (C) Parity Technologies (UK) Ltd. -// SPDX-License-Identifier: Apache-2.0 - -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -//! Substrate customizable serde serializer. -//! -//! The idea is that we can later change the implementation -//! to something more compact, but for now we're using JSON. - -#![warn(missing_docs)] - -pub use serde_json::{from_reader, from_slice, from_str, Error, Result}; - -const PROOF: &str = "Serializers are infallible; qed"; - -/// Serialize the given data structure as a pretty-printed String of JSON. -pub fn to_string_pretty(value: &T) -> String { - serde_json::to_string_pretty(value).expect(PROOF) -} - -/// Serialize the given data structure as a JSON byte vector. -pub fn encode(value: &T) -> Vec { - serde_json::to_vec(value).expect(PROOF) -} - -/// Serialize the given data structure as JSON into the IO stream. -pub fn to_writer( - writer: W, - value: &T, -) -> Result<()> { - serde_json::to_writer(writer, value) -} From 3919ca88d1dd0b06f26bd80fd0066a1ad7187081 Mon Sep 17 00:00:00 2001 From: Oliver Tale-Yazdi Date: Mon, 15 May 2023 12:31:55 +0200 Subject: [PATCH 496/558] Fix staking genesis build (#14140) Cumulus CI is stuck in https://github.com/paritytech/cumulus/pull/2574, so companion check will be red. Changes_ - Controller not needed Signed-off-by: Oliver Tale-Yazdi --- frame/staking/src/pallet/mod.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/frame/staking/src/pallet/mod.rs b/frame/staking/src/pallet/mod.rs index a725d4351..c5c9c669d 100644 --- a/frame/staking/src/pallet/mod.rs +++ b/frame/staking/src/pallet/mod.rs @@ -631,7 +631,7 @@ pub mod pallet { MaxNominatorsCount::::put(x); } - for &(ref stash, ref controller, balance, ref status) in &self.stakers { + for &(ref stash, _, balance, ref status) in &self.stakers { crate::log!( trace, "inserting genesis staker: {:?} => {:?} => {:?}", @@ -650,11 +650,11 @@ pub mod pallet { )); frame_support::assert_ok!(match status { crate::StakerStatus::Validator => >::validate( - T::RuntimeOrigin::from(Some(controller.clone()).into()), + T::RuntimeOrigin::from(Some(stash.clone()).into()), Default::default(), ), crate::StakerStatus::Nominator(votes) => >::nominate( - T::RuntimeOrigin::from(Some(controller.clone()).into()), + T::RuntimeOrigin::from(Some(stash.clone()).into()), votes.iter().map(|l| T::Lookup::unlookup(l.clone())).collect(), ), _ => Ok(()), From 204f39ab4c2f617442977d7d65dfc5e2e95ab0f8 Mon Sep 17 00:00:00 2001 From: Davide Galassi Date: Mon, 15 May 2023 13:56:52 +0200 Subject: [PATCH 497/558] Postpone public key creation in the test GenesisStorageBuilder (#14142) * Postpone public key creation from account id in the test genesis storage builder * Store raw sr25519 in substrate test pallet * Nitpick --- client/service/test/src/client/mod.rs | 4 +- test-utils/runtime/client/src/lib.rs | 2 +- test-utils/runtime/src/genesismap.rs | 39 +++++++++++++------ test-utils/runtime/src/lib.rs | 20 ++++------ .../runtime/src/substrate_test_pallet.rs | 6 +-- 5 files changed, 40 insertions(+), 31 deletions(-) diff --git a/client/service/test/src/client/mod.rs b/client/service/test/src/client/mod.rs index a33bce50a..e71e21176 100644 --- a/client/service/test/src/client/mod.rs +++ b/client/service/test/src/client/mod.rs @@ -173,7 +173,7 @@ fn construct_genesis_should_work_with_native() { vec![AccountKeyring::One.into(), AccountKeyring::Two.into()], 1000 * DOLLARS, ) - .build_storage(); + .build(); let genesis_hash = insert_genesis_block(&mut storage); let backend = InMemoryBackend::from((storage, StateVersion::default())); @@ -204,7 +204,7 @@ fn construct_genesis_should_work_with_wasm() { vec![AccountKeyring::One.into(), AccountKeyring::Two.into()], 1000 * DOLLARS, ) - .build_storage(); + .build(); let genesis_hash = insert_genesis_block(&mut storage); let backend = InMemoryBackend::from((storage, StateVersion::default())); diff --git a/test-utils/runtime/client/src/lib.rs b/test-utils/runtime/client/src/lib.rs index b2c1e8f47..39ee6667f 100644 --- a/test-utils/runtime/client/src/lib.rs +++ b/test-utils/runtime/client/src/lib.rs @@ -102,7 +102,7 @@ impl GenesisInit for GenesisParameters { .with_heap_pages(self.heap_pages_override) .with_wasm_code(&self.wasm_code) .with_extra_storage(self.extra_storage.clone()) - .build_storage() + .build() } } diff --git a/test-utils/runtime/src/genesismap.rs b/test-utils/runtime/src/genesismap.rs index 62ce26c55..aa57eb192 100644 --- a/test-utils/runtime/src/genesismap.rs +++ b/test-utils/runtime/src/genesismap.rs @@ -18,8 +18,7 @@ //! Tool for creating the genesis block. use super::{ - currency, substrate_test_pallet, wasm_binary_unwrap, AccountId, AuthorityId, Balance, - GenesisConfig, + currency, substrate_test_pallet, wasm_binary_unwrap, AccountId, Balance, GenesisConfig, }; use codec::Encode; use sc_service::construct_genesis_block; @@ -34,14 +33,19 @@ use sp_runtime::{ BuildStorage, }; -/// Builder for generating storage from substrate-test-runtime genesis config. Default storage can -/// be extended with additional key-value pairs. +/// Builder for generating storage from substrate-test-runtime genesis config. +/// +/// Default storage can be extended with additional key-value pairs. pub struct GenesisStorageBuilder { - authorities: Vec, + /// Authorities accounts used by any component requiring an authority set (e.g. babe). + authorities: Vec, + /// Accounts to be endowed with some funds. balances: Vec<(AccountId, u64)>, + /// Override default number of heap pages. heap_pages_override: Option, /// Additional storage key pairs that will be added to the genesis map. extra_storage: Storage, + /// Optional wasm code override. wasm_code: Option>, } @@ -50,9 +54,9 @@ impl Default for GenesisStorageBuilder { fn default() -> Self { Self::new( vec![ - sr25519::Public::from(Sr25519Keyring::Alice).into(), - sr25519::Public::from(Sr25519Keyring::Bob).into(), - sr25519::Public::from(Sr25519Keyring::Charlie).into(), + Sr25519Keyring::Alice.into(), + Sr25519Keyring::Bob.into(), + Sr25519Keyring::Charlie.into(), ], (0..16_usize) .into_iter() @@ -74,7 +78,7 @@ impl GenesisStorageBuilder { /// from `extra_storage` will be injected into built storage. `HEAP_PAGES` key and value will /// also be placed into storage. pub fn new( - authorities: Vec, + authorities: Vec, endowed_accounts: Vec, balance: Balance, ) -> Self { @@ -104,17 +108,28 @@ impl GenesisStorageBuilder { } /// Builds the `GenesisConfig` and returns its storage. - pub fn build_storage(&mut self) -> Storage { + pub fn build(self) -> Storage { + let authorities_sr25519: Vec<_> = self + .authorities + .clone() + .into_iter() + .map(|id| sr25519::Public::from(id)) + .collect(); + let genesis_config = GenesisConfig { system: frame_system::GenesisConfig { code: self.wasm_code.clone().unwrap_or(wasm_binary_unwrap().to_vec()), }, babe: pallet_babe::GenesisConfig { - authorities: self.authorities.clone().into_iter().map(|x| (x, 1)).collect(), + authorities: authorities_sr25519 + .clone() + .into_iter() + .map(|x| (x.into(), 1)) + .collect(), epoch_config: Some(crate::TEST_RUNTIME_BABE_EPOCH_CONFIGURATION), }, substrate_test: substrate_test_pallet::GenesisConfig { - authorities: self.authorities.clone(), + authorities: authorities_sr25519.clone(), }, balances: pallet_balances::GenesisConfig { balances: self.balances.clone() }, }; diff --git a/test-utils/runtime/src/lib.rs b/test-utils/runtime/src/lib.rs index 00db6a745..e1d66e860 100644 --- a/test-utils/runtime/src/lib.rs +++ b/test-utils/runtime/src/lib.rs @@ -64,13 +64,11 @@ use sp_runtime::{ use sp_version::NativeVersion; use sp_version::RuntimeVersion; -// Ensure Babe and Aura use the same crypto to simplify things a bit. -pub use sp_consensus_babe::{AllowedSlots, AuthorityId, BabeEpochConfiguration, Slot}; +pub use sp_consensus_babe::{AllowedSlots, BabeEpochConfiguration, Slot}; pub use pallet_balances::Call as BalancesCall; pub type AuraId = sp_consensus_aura::sr25519::AuthorityId; - #[cfg(feature = "std")] pub use extrinsic::{ExtrinsicBuilder, Transfer}; @@ -616,10 +614,7 @@ impl_runtime_apis! { } fn authorities() -> Vec { - SubstrateTest::authorities().into_iter().map(|a| { - let authority: sr25519::Public = a.into(); - AuraId::from(authority) - }).collect() + SubstrateTest::authorities().into_iter().map(|auth| AuraId::from(auth)).collect() } } @@ -630,10 +625,9 @@ impl_runtime_apis! { slot_duration: Babe::slot_duration(), epoch_length: EpochDuration::get(), c: epoch_config.c, - authorities: SubstrateTest::authorities() - .into_iter().map(|x|(x, 1)).collect(), - randomness: Babe::randomness(), - allowed_slots: epoch_config.allowed_slots, + authorities: Babe::authorities().to_vec(), + randomness: Babe::randomness(), + allowed_slots: epoch_config.allowed_slots, } } @@ -1087,7 +1081,7 @@ mod tests { vec![AccountKeyring::One.into(), AccountKeyring::Two.into()], 1000 * currency::DOLLARS, ) - .build_storage() + .build() .into() } @@ -1095,7 +1089,7 @@ mod tests { fn validate_storage_keys() { assert_eq!( genesismap::GenesisStorageBuilder::default() - .build_storage() + .build() .top .keys() .cloned() diff --git a/test-utils/runtime/src/substrate_test_pallet.rs b/test-utils/runtime/src/substrate_test_pallet.rs index 98ebd550b..40e7af0b4 100644 --- a/test-utils/runtime/src/substrate_test_pallet.rs +++ b/test-utils/runtime/src/substrate_test_pallet.rs @@ -21,8 +21,8 @@ //! functioning runtime. Some calls are allowed to be submitted as unsigned extrinsics, however most //! of them requires signing. Refer to `pallet::Call` for further details. -use crate::AuthorityId; use frame_support::{pallet_prelude::*, storage}; +use sp_core::sr25519::Public; use sp_runtime::transaction_validity::{ InvalidTransaction, TransactionSource, TransactionValidity, ValidTransaction, }; @@ -49,12 +49,12 @@ pub mod pallet { #[pallet::storage] #[pallet::getter(fn authorities)] - pub type Authorities = StorageValue<_, Vec, ValueQuery>; + pub type Authorities = StorageValue<_, Vec, ValueQuery>; #[pallet::genesis_config] #[cfg_attr(feature = "std", derive(Default))] pub struct GenesisConfig { - pub authorities: Vec, + pub authorities: Vec, } #[pallet::genesis_build] From 215336763d36401ea7a0b49564cbb6ad18b2a3c1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gon=C3=A7alo=20Pestana?= Date: Mon, 15 May 2023 14:04:29 +0200 Subject: [PATCH 498/558] Adds integration test for slashed/chilled validator with subsequent validation intention (#13717) * Adds integration test for slashed/chilled validator with subsequent validation intention * Removes unecessary comment * Update frame/election-provider-multi-phase/test-staking-e2e/src/lib.rs Co-authored-by: Kian Paimani <5588131+kianenigma@users.noreply.github.com> * Update frame/election-provider-multi-phase/test-staking-e2e/src/lib.rs Co-authored-by: Kian Paimani <5588131+kianenigma@users.noreply.github.com> * Addresses PR review comments * Fixes after conflict resolved --------- Co-authored-by: parity-processbot <> Co-authored-by: Kian Paimani <5588131+kianenigma@users.noreply.github.com> --- .../test-staking-e2e/src/lib.rs | 70 +++++++++++++++++++ .../test-staking-e2e/src/mock.rs | 14 +++- 2 files changed, 81 insertions(+), 3 deletions(-) diff --git a/frame/election-provider-multi-phase/test-staking-e2e/src/lib.rs b/frame/election-provider-multi-phase/test-staking-e2e/src/lib.rs index c396b008f..2b4a255dc 100644 --- a/frame/election-provider-multi-phase/test-staking-e2e/src/lib.rs +++ b/frame/election-provider-multi-phase/test-staking-e2e/src/lib.rs @@ -20,6 +20,7 @@ mod mock; pub(crate) const LOG_TARGET: &str = "tests::e2e-epm"; +use frame_support::assert_ok; use mock::*; use sp_core::Get; use sp_npos_elections::{to_supports, StakedAssignment}; @@ -204,3 +205,72 @@ fn continous_slashes_below_offending_threshold() { } }); } + +#[test] +/// When validators are slashed, they are chilled and removed from the current `VoterList`. Thus, +/// the slashed validator should not be considered in the next validator set. However, if the +/// slashed validator sets its intention to validate again in the same era when it was slashed and +/// chilled, the validator may not be removed from the active validator set across eras, provided +/// it would selected in the subsequent era if there was no slash. Nominators of the slashed +/// validator will also be slashed and chilled, as expected, but the nomination intentions will +/// remain after the validator re-set the intention to be validating again. +/// +/// This behaviour is due to removing implicit chill upon slash +/// . +/// +/// Related to . +fn set_validation_intention_after_chilled() { + use frame_election_provider_support::SortedListProvider; + use pallet_staking::{Event, Forcing, Nominators}; + + let staking_builder = StakingExtBuilder::default(); + let epm_builder = EpmExtBuilder::default(); + + ExtBuilder::default() + .staking(staking_builder) + .epm(epm_builder) + .build_and_execute(|| { + assert_eq!(active_era(), 0); + // validator is part of the validator set. + assert!(Session::validators().contains(&81)); + assert!(::VoterList::contains(&81)); + + // nominate validator 81. + assert_ok!(Staking::nominate(RuntimeOrigin::signed(21), vec![81])); + assert_eq!(Nominators::::get(21).unwrap().targets, vec![81]); + + // validator is slashed. it is removed from the `VoterList` through chilling but in the + // current era, the validator is still part of the active validator set. + add_slash(&81); + assert!(Session::validators().contains(&81)); + assert!(!::VoterList::contains(&81)); + assert_eq!( + staking_events(), + [ + Event::Chilled { stash: 81 }, + Event::ForceEra { mode: Forcing::ForceNew }, + Event::SlashReported { + validator: 81, + slash_era: 0, + fraction: Perbill::from_percent(10) + } + ], + ); + + // after the nominator is slashed and chilled, the nominations remain. + assert_eq!(Nominators::::get(21).unwrap().targets, vec![81]); + + // validator sets intention to stake again in the same era it was chilled. + assert_ok!(Staking::validate(RuntimeOrigin::signed(81), Default::default())); + + // progress era and check that the slashed validator is still part of the validator + // set. + assert!(start_next_active_era().is_ok()); + assert_eq!(active_era(), 1); + assert!(Session::validators().contains(&81)); + assert!(::VoterList::contains(&81)); + + // nominations are still active as before the slash. + assert_eq!(Nominators::::get(21).unwrap().targets, vec![81]); + }) +} diff --git a/frame/election-provider-multi-phase/test-staking-e2e/src/mock.rs b/frame/election-provider-multi-phase/test-staking-e2e/src/mock.rs index e1abbb909..490179e91 100644 --- a/frame/election-provider-multi-phase/test-staking-e2e/src/mock.rs +++ b/frame/election-provider-multi-phase/test-staking-e2e/src/mock.rs @@ -124,11 +124,11 @@ impl pallet_balances::Config for Runtime { type DustRemoval = (); type ExistentialDeposit = ExistentialDeposit; type AccountStore = System; - type WeightInfo = (); + type MaxHolds = ConstU32<1>; + type MaxFreezes = traits::ConstU32<1>; type HoldIdentifier = (); type FreezeIdentifier = (); - type MaxHolds = traits::ConstU32<1>; - type MaxFreezes = traits::ConstU32<1>; + type WeightInfo = (); } impl pallet_timestamp::Config for Runtime { @@ -781,3 +781,11 @@ pub(crate) fn set_minimum_election_score( .map(|_| ()) .map_err(|_| ()) } + +pub(crate) fn staking_events() -> Vec> { + System::events() + .into_iter() + .map(|r| r.event) + .filter_map(|e| if let RuntimeEvent::Staking(inner) = e { Some(inner) } else { None }) + .collect::>() +} From 93e2cf113a7ff1ee5300df8ef90dcd24a23af452 Mon Sep 17 00:00:00 2001 From: Tsvetomir Dimitrov Date: Mon, 15 May 2023 18:00:13 +0300 Subject: [PATCH 499/558] Bump parity-db to 0.4.8 (#14146) --- Cargo.lock | 6 +++--- bin/node/bench/Cargo.toml | 2 +- client/db/Cargo.toml | 2 +- client/statement-store/Cargo.toml | 2 +- 4 files changed, 6 insertions(+), 6 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 547721957..c16f149de 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -7431,9 +7431,9 @@ dependencies = [ [[package]] name = "parity-db" -version = "0.4.7" +version = "0.4.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bd4572a52711e2ccff02b4973ec7e4a5b5c23387ebbfbd6cd42b34755714cefc" +checksum = "4890dcb9556136a4ec2b0c51fa4a08c8b733b829506af8fff2e853f3a065985b" dependencies = [ "blake2", "crc32fast", @@ -12614,7 +12614,7 @@ checksum = "97fee6b57c6a41524a810daee9286c02d7752c4253064d0b05472833a438f675" dependencies = [ "cfg-if", "digest 0.10.6", - "rand 0.8.5", + "rand 0.7.3", "static_assertions", ] diff --git a/bin/node/bench/Cargo.toml b/bin/node/bench/Cargo.toml index ea15fce4c..b3f1434a9 100644 --- a/bin/node/bench/Cargo.toml +++ b/bin/node/bench/Cargo.toml @@ -38,7 +38,7 @@ tempfile = "3.1.0" fs_extra = "1" rand = { version = "0.8.5", features = ["small_rng"] } lazy_static = "1.4.0" -parity-db = "0.4.7" +parity-db = "0.4.8" sc-transaction-pool = { version = "4.0.0-dev", path = "../../../client/transaction-pool" } sc-transaction-pool-api = { version = "4.0.0-dev", path = "../../../client/transaction-pool/api" } futures = { version = "0.3.21", features = ["thread-pool"] } diff --git a/client/db/Cargo.toml b/client/db/Cargo.toml index 7d522b404..aeb31f944 100644 --- a/client/db/Cargo.toml +++ b/client/db/Cargo.toml @@ -22,7 +22,7 @@ kvdb-memorydb = "0.13.0" kvdb-rocksdb = { version = "0.19.0", optional = true } linked-hash-map = "0.5.4" log = "0.4.17" -parity-db = "0.4.7" +parity-db = "0.4.8" parking_lot = "0.12.1" sc-client-api = { version = "4.0.0-dev", path = "../api" } sc-state-db = { version = "0.10.0-dev", path = "../state-db" } diff --git a/client/statement-store/Cargo.toml b/client/statement-store/Cargo.toml index 5a4948cee..936aeb6e3 100644 --- a/client/statement-store/Cargo.toml +++ b/client/statement-store/Cargo.toml @@ -19,7 +19,7 @@ futures = "0.3.21" futures-timer = "3.0.2" log = "0.4.17" parking_lot = "0.12.1" -parity-db = "0.4.7" +parity-db = "0.4.8" tokio = { version = "1.22.0", features = ["time"] } sp-statement-store = { version = "4.0.0-dev", path = "../../primitives/statement-store" } prometheus-endpoint = { package = "substrate-prometheus-endpoint", version = "0.10.0-dev", path = "../../utils/prometheus" } From e4b08db824c42f45e14e1b0a29f133b9c9903267 Mon Sep 17 00:00:00 2001 From: Muharem Ismailov Date: Mon, 15 May 2023 18:08:07 +0200 Subject: [PATCH 500/558] AccountTouch: deposit_required requires asset id (#14147) --- frame/assets/src/lib.rs | 2 +- frame/support/src/traits/misc.rs | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/frame/assets/src/lib.rs b/frame/assets/src/lib.rs index c25f33ae3..33aacd932 100644 --- a/frame/assets/src/lib.rs +++ b/frame/assets/src/lib.rs @@ -1654,7 +1654,7 @@ pub mod pallet { impl, I: 'static> AccountTouch for Pallet { type Balance = DepositBalanceOf; - fn deposit_required() -> Self::Balance { + fn deposit_required(_: T::AssetId) -> Self::Balance { T::AssetAccountDeposit::get() } diff --git a/frame/support/src/traits/misc.rs b/frame/support/src/traits/misc.rs index e21fcfbb2..a6f8c46d6 100644 --- a/frame/support/src/traits/misc.rs +++ b/frame/support/src/traits/misc.rs @@ -1160,8 +1160,8 @@ pub trait AccountTouch { /// The type for currency units of the deposit. type Balance; - /// The deposit amount of a native currency required for creating an asset account. - fn deposit_required() -> Self::Balance; + /// The deposit amount of a native currency required for creating an account of the `asset`. + fn deposit_required(asset: AssetId) -> Self::Balance; /// Create an account for `who` of the `asset` with a deposit taken from the `depositor`. fn touch(asset: AssetId, who: AccountId, depositor: AccountId) -> DispatchResult; From 9dbf60c3f28f9213531b9b3485cb9db3715883f6 Mon Sep 17 00:00:00 2001 From: Gavin Wood Date: Mon, 15 May 2023 18:11:33 +0100 Subject: [PATCH 501/558] Introduce function into frame System (#14149) --- frame/system/src/lib.rs | 14 ++++++++++++++ frame/system/src/tests.rs | 21 +++++++++++++++++++++ primitives/storage/src/lib.rs | 3 +++ 3 files changed, 38 insertions(+) diff --git a/frame/system/src/lib.rs b/frame/system/src/lib.rs index 1b830105b..4c7520452 100644 --- a/frame/system/src/lib.rs +++ b/frame/system/src/lib.rs @@ -66,6 +66,7 @@ #[cfg(feature = "std")] use serde::Serialize; +use sp_io::hashing::blake2_256; #[cfg(feature = "runtime-benchmarks")] use sp_runtime::traits::TrailingZeroInput; use sp_runtime::{ @@ -1316,6 +1317,8 @@ impl Pallet { // populate environment ExecutionPhase::::put(Phase::Initialization); storage::unhashed::put(well_known_keys::EXTRINSIC_INDEX, &0u32); + let entropy = (b"frame_system::initialize", parent_hash).using_encoded(blake2_256); + storage::unhashed::put(well_known_keys::INTRABLOCK_ENTROPY, &entropy[..]); >::put(number); >::put(digest); >::put(parent_hash); @@ -1365,6 +1368,7 @@ impl Pallet { ); ExecutionPhase::::kill(); AllExtrinsicsLen::::kill(); + storage::unhashed::kill(well_known_keys::INTRABLOCK_ENTROPY); // The following fields // @@ -1633,6 +1637,16 @@ impl Pallet { } } +/// Returns a 32 byte datum which is guaranteed to be universally unique. `entropy` is provided +/// as a facility to reduce the potential for precalculating results. +pub fn unique(entropy: impl Encode) -> [u8; 32] { + let mut last = [0u8; 32]; + sp_io::storage::read(well_known_keys::INTRABLOCK_ENTROPY, &mut last[..], 0); + let next = (b"frame_system::unique", entropy, last).using_encoded(blake2_256); + sp_io::storage::set(well_known_keys::INTRABLOCK_ENTROPY, &next.encode()); + next +} + /// Event handler which registers a provider when created. pub struct Provider(PhantomData); impl HandleLifetime for Provider { diff --git a/frame/system/src/tests.rs b/frame/system/src/tests.rs index 128231ccf..d210bb609 100644 --- a/frame/system/src/tests.rs +++ b/frame/system/src/tests.rs @@ -34,6 +34,27 @@ fn origin_works() { assert_eq!(x.unwrap(), RawOrigin::::Signed(1u64)); } +#[test] +fn unique_datum_works() { + new_test_ext().execute_with(|| { + System::initialize(&1, &[0u8; 32].into(), &Default::default()); + assert!(sp_io::storage::exists(well_known_keys::INTRABLOCK_ENTROPY)); + + let h1 = unique(b""); + let h2 = unique(b""); + assert_ne!(h1, h2); + + let h3 = unique(b"Hello"); + assert_ne!(h2, h3); + + let h4 = unique(b"Hello"); + assert_ne!(h3, h4); + + System::finalize(); + assert!(!sp_io::storage::exists(well_known_keys::INTRABLOCK_ENTROPY)); + }); +} + #[test] fn stored_map_works() { new_test_ext().execute_with(|| { diff --git a/primitives/storage/src/lib.rs b/primitives/storage/src/lib.rs index 06995005d..032a236a0 100644 --- a/primitives/storage/src/lib.rs +++ b/primitives/storage/src/lib.rs @@ -204,6 +204,9 @@ pub mod well_known_keys { /// Encodes to `0x3a65787472696e7369635f696e646578`. pub const EXTRINSIC_INDEX: &[u8] = b":extrinsic_index"; + /// Current intra-block entropy (a universally unique `[u8; 32]` value) is stored here. + pub const INTRABLOCK_ENTROPY: &[u8] = b":intrablock_entropy"; + /// Prefix of child storage keys. pub const CHILD_STORAGE_KEY_PREFIX: &[u8] = b":child_storage:"; From 1eb0611893d30e4381eb01d24b55f0a04cdca3f2 Mon Sep 17 00:00:00 2001 From: Kian Paimani <5588131+kianenigma@users.noreply.github.com> Date: Mon, 15 May 2023 21:07:48 +0200 Subject: [PATCH 502/558] fully enable `RuntimeDebug` in `feature = try-runtime` (#14136) * fully enable `RuntimeDebug` in `feature = try-runtime` * Delete settings.json * fix * fix * fix * update lock file with sp-debug-derive dep * update doc --------- Co-authored-by: muharem --- Cargo.lock | 1 + frame/support/Cargo.toml | 5 ++++- frame/support/procedural/src/lib.rs | 14 +++++--------- 3 files changed, 10 insertions(+), 10 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index c16f149de..cb0593ccc 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2700,6 +2700,7 @@ dependencies = [ "sp-arithmetic", "sp-core", "sp-core-hashing-proc-macro", + "sp-debug-derive", "sp-inherents", "sp-io", "sp-runtime", diff --git a/frame/support/Cargo.toml b/frame/support/Cargo.toml index d8b1b9c24..6ed67c808 100644 --- a/frame/support/Cargo.toml +++ b/frame/support/Cargo.toml @@ -27,6 +27,7 @@ sp-arithmetic = { version = "6.0.0", default-features = false, path = "../../pri sp-inherents = { version = "4.0.0-dev", default-features = false, path = "../../primitives/inherents" } sp-staking = { version = "4.0.0-dev", default-features = false, path = "../../primitives/staking" } sp-weights = { version = "4.0.0", default-features = false, path = "../../primitives/weights" } +sp-debug-derive = { default-features = false, path = "../../primitives/debug-derive" } tt-call = "1.0.8" frame-support-procedural = { version = "4.0.0-dev", default-features = false, path = "./procedural" } paste = "1.0" @@ -75,7 +76,9 @@ runtime-benchmarks = [ "sp-runtime/runtime-benchmarks", "sp-staking/runtime-benchmarks" ] -try-runtime = [] +try-runtime = [ + "sp-debug-derive/force-debug" +] # By default some types have documentation, `no-metadata-docs` allows to reduce the documentation # in the metadata. no-metadata-docs = ["frame-support-procedural/no-metadata-docs", "sp-api/no-metadata-docs"] diff --git a/frame/support/procedural/src/lib.rs b/frame/support/procedural/src/lib.rs index 895b09a17..b8aa5674d 100644 --- a/frame/support/procedural/src/lib.rs +++ b/frame/support/procedural/src/lib.rs @@ -648,12 +648,13 @@ pub fn derive_debug_no_bound(input: TokenStream) -> TokenStream { } /// Derive [`Debug`], if `std` is enabled it uses `frame_support::DebugNoBound`, if `std` is not -/// enabled it just returns `""`. +/// enabled it just returns `""`. /// This behaviour is useful to prevent bloating the runtime WASM blob from unneeded code. #[proc_macro_derive(RuntimeDebugNoBound)] pub fn derive_runtime_debug_no_bound(input: TokenStream) -> TokenStream { - #[cfg(not(feature = "std"))] - { + if cfg!(any(feature = "std", feature = "try-runtime")) { + debug_no_bound::derive_debug_no_bound(input) + } else { let input: syn::DeriveInput = match syn::parse(input) { Ok(input) => input, Err(e) => return e.to_compile_error().into(), @@ -666,18 +667,13 @@ pub fn derive_runtime_debug_no_bound(input: TokenStream) -> TokenStream { const _: () = { impl #impl_generics core::fmt::Debug for #name #ty_generics #where_clause { fn fmt(&self, fmt: &mut core::fmt::Formatter) -> core::fmt::Result { - fmt.write_str("") + fmt.write_str("") } } }; ) .into() } - - #[cfg(feature = "std")] - { - debug_no_bound::derive_debug_no_bound(input) - } } /// Derive [`PartialEq`] but do not bound any generic. Docs are at From be4c7cb48f83b293a5d5f3daadf1ae7a2a9fb372 Mon Sep 17 00:00:00 2001 From: Gavin Wood Date: Mon, 15 May 2023 20:19:23 +0100 Subject: [PATCH 503/558] Tweak to avoid minor entropy loss (#14152) --- frame/system/src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frame/system/src/lib.rs b/frame/system/src/lib.rs index 4c7520452..71e27cd4e 100644 --- a/frame/system/src/lib.rs +++ b/frame/system/src/lib.rs @@ -1318,7 +1318,7 @@ impl Pallet { ExecutionPhase::::put(Phase::Initialization); storage::unhashed::put(well_known_keys::EXTRINSIC_INDEX, &0u32); let entropy = (b"frame_system::initialize", parent_hash).using_encoded(blake2_256); - storage::unhashed::put(well_known_keys::INTRABLOCK_ENTROPY, &entropy[..]); + storage::unhashed::put_raw(well_known_keys::INTRABLOCK_ENTROPY, &entropy[..]); >::put(number); >::put(digest); >::put(parent_hash); From 2af3ba87a7a545d534b489e4133cf49de4af52d1 Mon Sep 17 00:00:00 2001 From: klbrvik <124571753+klbrvik@users.noreply.github.com> Date: Mon, 15 May 2023 23:45:19 +0200 Subject: [PATCH 504/558] expose transport, peer_info and discovery in sc-network (#14132) * expose transport, peer_info and discovery in sc-network * fix fmt * add missing module docs --- client/network/src/lib.rs | 6 +++--- client/network/src/peer_info.rs | 3 +++ client/network/src/transport.rs | 2 ++ 3 files changed, 8 insertions(+), 3 deletions(-) diff --git a/client/network/src/lib.rs b/client/network/src/lib.rs index 79023923e..a66c187ca 100644 --- a/client/network/src/lib.rs +++ b/client/network/src/lib.rs @@ -243,17 +243,17 @@ //! More precise usage details are still being worked on and will likely change in the future. mod behaviour; -mod discovery; -mod peer_info; mod protocol; mod service; -mod transport; pub mod config; +pub mod discovery; pub mod error; pub mod event; pub mod network_state; +pub mod peer_info; pub mod request_responses; +pub mod transport; pub mod types; pub mod utils; diff --git a/client/network/src/peer_info.rs b/client/network/src/peer_info.rs index e4a5c5753..aab3fc948 100644 --- a/client/network/src/peer_info.rs +++ b/client/network/src/peer_info.rs @@ -16,6 +16,9 @@ // You should have received a copy of the GNU General Public License // along with this program. If not, see . +//! [`PeerInfoBehaviour`] is implementation of `NetworkBehaviour` that holds information about peers +//! in cache. + use crate::utils::interval; use either::Either; diff --git a/client/network/src/transport.rs b/client/network/src/transport.rs index 9e63ce988..4136b34fc 100644 --- a/client/network/src/transport.rs +++ b/client/network/src/transport.rs @@ -16,6 +16,8 @@ // You should have received a copy of the GNU General Public License // along with this program. If not, see . +//! Transport that serves as a common ground for all connections. + use either::Either; use libp2p::{ core::{ From dd478bc60eb83a5f8f25aa65fdac2e519e9545ab Mon Sep 17 00:00:00 2001 From: Alexander Kalankhodzhaev Date: Tue, 16 May 2023 12:46:18 +0700 Subject: [PATCH 505/558] Move node-template Cargo.toml dependencies to workspace (#14058) * Move node-template Cargo.toml dependencies to workspace * review * Update Cargo.lock Signed-off-by: Oliver Tale-Yazdi --------- Signed-off-by: Oliver Tale-Yazdi Co-authored-by: Oliver Tale-Yazdi --- Cargo.lock | 3 +- docs/node-template-release.md | 25 +- scripts/ci/node-template-release/Cargo.toml | 3 +- scripts/ci/node-template-release/src/main.rs | 381 +++++++++++++------ 4 files changed, 275 insertions(+), 137 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index cb0593ccc..23a70da4d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -5485,9 +5485,10 @@ dependencies = [ "fs_extra", "git2", "glob", + "itertools", "tar", "tempfile", - "toml 0.7.3", + "toml_edit", ] [[package]] diff --git a/docs/node-template-release.md b/docs/node-template-release.md index 4f4977a9d..911e6a2bb 100644 --- a/docs/node-template-release.md +++ b/docs/node-template-release.md @@ -27,33 +27,26 @@ by running the following command. delete files/directories that are removed from the source. So you need to manually check and remove them in the destination. -3. There are actually three packages in the Node Template, `node-template` (the node), -`node-template-runtime` (the runtime), and `pallet-template`, and each has its own `Cargo.toml`. -Inside these three files, dependencies are listed in expanded form and linked to a certain git -commit in Substrate remote repository, such as: +3. There is a `Cargo.toml` file in the root directory. Inside, dependencies are listed form and +linked to a certain git commit in Substrate remote repository, such as: ```toml - [dev-dependencies.sp-core] - default-features = false - git = 'https://github.com/paritytech/substrate.git' - rev = 'c1fe59d060600a10eebb4ace277af1fee20bad17' - version = '3.0.0' + sp-core = { version = "7.0.0", git = "https://github.com/paritytech/substrate.git", rev = "de80d0107336a9c7a2efdc0199015e4d67fcbdb5", default-features = false } ``` - We will update each of them to the shortened form and link them to the Rust - [crate registry](https://crates.io/). After confirming the versioned package is published in - the crate, the above will become: + We will update each of them to link to the Rust [crate registry](https://crates.io/). +After confirming the versioned package is published in the crate, the above will become: ```toml - [dev-dependencies] - sp-core = { version = '3.0.0', default-features = false } + [workspace.dependencies] + sp-core = { version = "7.0.0", default-features = false } ``` P.S: This step can be automated if we update `node-template-release` package in `scripts/ci/node-template-release`. -4. Once the three `Cargo.toml`s are updated, compile and confirm that the Node Template builds. Then -commit the changes to a new branch in [Substrate Node Template](https://github.com/substrate-developer-hub/substrate-node-template), and make a PR. +4. Once the `Cargo.toml` is updated, compile and confirm that the Node Template builds. Then commit +the changes to a new branch in [Substrate Node Template](https://github.com/substrate-developer-hub/substrate-node-template), and make a PR. > Note that there is a chance the code in Substrate Node Template works with the linked Substrate git commit but not with published packages due to the latest (as yet) unpublished features. In this case, diff --git a/scripts/ci/node-template-release/Cargo.toml b/scripts/ci/node-template-release/Cargo.toml index aca8d6e1f..e93810876 100644 --- a/scripts/ci/node-template-release/Cargo.toml +++ b/scripts/ci/node-template-release/Cargo.toml @@ -18,4 +18,5 @@ git2 = "0.16" glob = "0.3" tar = "0.4" tempfile = "3" -toml = "0.7" +toml_edit = "0.19" +itertools = "0.10" diff --git a/scripts/ci/node-template-release/src/main.rs b/scripts/ci/node-template-release/src/main.rs index 91a7e8654..c75252299 100644 --- a/scripts/ci/node-template-release/src/main.rs +++ b/scripts/ci/node-template-release/src/main.rs @@ -1,30 +1,32 @@ -use clap::Parser; - use std::{ collections::HashMap, fs::{self, File, OpenOptions}, - io::{Read, Write}, path::{Path, PathBuf}, process::Command, }; -use glob; - +use clap::Parser; +use flate2::{write::GzEncoder, Compression}; use fs_extra::dir::{self, CopyOptions}; - -use tempfile; - use git2; - -use toml; - +use glob; +use itertools::Itertools; use tar; - -use flate2::{write::GzEncoder, Compression}; +use tempfile; +use toml_edit::{self, value, Array, Item, Table}; const SUBSTRATE_GIT_URL: &str = "https://github.com/paritytech/substrate.git"; -type CargoToml = HashMap; +type CargoToml = toml_edit::Document; + +#[derive(Debug, PartialEq)] +struct Dependency { + name: String, + version: Option, + default_features: Option, +} + +type Dependencies = HashMap>; #[derive(Parser)] struct Options { @@ -36,8 +38,14 @@ struct Options { output: PathBuf, } +/// Copy the `node-template` to the given path. +fn copy_node_template(node_template: &Path, dest_path: &Path) { + let options = CopyOptions::new(); + dir::copy(node_template, dest_path, &options).expect("Copies node-template to tmp dir"); +} + /// Find all `Cargo.toml` files in the given path. -fn find_cargo_tomls(path: PathBuf) -> Vec { +fn find_cargo_tomls(path: &PathBuf) -> Vec { let path = format!("{}/**/*.toml", path.display()); let glob = glob::glob(&path).expect("Generates globbing pattern"); @@ -55,10 +63,18 @@ fn find_cargo_tomls(path: PathBuf) -> Vec { result } -/// Copy the `node-template` to the given path. -fn copy_node_template(node_template: &Path, dest_path: &Path) { - let options = CopyOptions::new(); - dir::copy(node_template, dest_path, &options).expect("Copies node-template to tmp dir"); +/// Parse the given `Cargo.toml`. +fn parse_cargo_toml(file: &Path) -> CargoToml { + fs::read_to_string(file) + .unwrap_or_else(|e| panic!("Failed to read `{}`: {}", file.display(), e)) + .parse() + .unwrap_or_else(|e| panic!("Failed to parse `{}`: {}", file.display(), e)) +} + +/// Write the given `Cargo.toml` to the given path. +fn write_cargo_toml(path: &Path, cargo_toml: CargoToml) { + fs::write(path, cargo_toml.to_string()) + .unwrap_or_else(|e| panic!("Failed to write `{}`: {}", path.display(), e)); } /// Gets the latest commit id of the repository given by `path`. @@ -76,82 +92,125 @@ fn get_git_commit_id(path: &Path) -> String { format!("{}", commit_id) } -/// Parse the given `Cargo.toml` into a `HashMap` -fn parse_cargo_toml(file: &Path) -> CargoToml { - let mut content = String::new(); - File::open(file) - .expect("Cargo.toml exists") - .read_to_string(&mut content) - .expect("Reads file"); - toml::from_str(&content).expect("Cargo.toml is a valid toml file") -} - -/// Replaces all substrate path dependencies with a git dependency. -fn replace_path_dependencies_with_git( - cargo_toml_path: &Path, - commit_id: &str, +/// Rewrites git dependencies: +/// - inserts `workspace = true`; +/// - removes `path`; +/// - removes `version`; +/// - removes `default-features` +/// - and returns the dependencies that were rewritten. +fn update_git_dependencies bool>( cargo_toml: &mut CargoToml, -) { - let mut cargo_toml_path = cargo_toml_path.to_path_buf(); - // remove `Cargo.toml` - cargo_toml_path.pop(); - - for &table in &["dependencies", "build-dependencies", "dev-dependencies"] { - let mut dependencies: toml::value::Table = - match cargo_toml.remove(table).and_then(|v| v.try_into().ok()) { - Some(deps) => deps, - None => continue, - }; - - let deps_rewritten = dependencies - .iter() - .filter_map(|(k, v)| { - v.clone().try_into::().ok().map(move |v| (k, v)) + path_filter: F, +) -> Dependencies { + let process_dep = |dep: (toml_edit::KeyMut, &mut Item)| -> Option { + let (key, value) = dep; + value + .as_table_like_mut() + .filter(|dep| { + dep.get("path").and_then(|path| path.as_str()).map(path_filter).unwrap_or(false) }) - .filter(|t| { - t.1.contains_key("path") && { - // if the path does not exists, we need to add this as git dependency - t.1.get("path") - .unwrap() - .as_str() - .map(|path| !cargo_toml_path.join(path).exists()) - .unwrap_or(false) + .map(|dep| { + dep.insert("workspace", toml_edit::value(true)); + dep.remove("path"); + + Dependency { + name: key.get().to_string(), + version: dep + .remove("version") + .and_then(|version| version.as_str().map(|s| s.to_string())), + default_features: dep.remove("default-features").and_then(|b| b.as_bool()), } }) - .map(|(k, mut v)| { - // remove `path` and add `git` and `rev` - v.remove("path"); - v.insert("git".into(), SUBSTRATE_GIT_URL.into()); - v.insert("rev".into(), commit_id.into()); + }; + + ["dependencies", "build-dependencies", "dev-dependencies"] + .into_iter() + .map(|table| -> (String, HashMap) { + ( + table.to_string(), + cargo_toml[table] + .as_table_mut() + .into_iter() + .flat_map(|deps| deps.iter_mut().filter_map(process_dep)) + .map(|dep| (dep.name.clone(), dep)) + .collect(), + ) + }) + .collect() +} - (k.clone(), v.into()) - }) - .collect::>(); +/// Processes all `Cargo.toml` files, aggregates dependencies and saves the changes. +fn process_cargo_tomls(cargo_tomls: &Vec) -> Dependencies { + /// Merge dependencies from one collection in another. + fn merge_deps(into: &mut Dependencies, from: Dependencies) { + from.into_iter().for_each(|(table, deps)| { + into.entry(table).or_insert_with(HashMap::new).extend(deps); + }); + } - dependencies.extend(deps_rewritten.into_iter()); + cargo_tomls.iter().fold(Dependencies::new(), |mut acc, path| { + let mut cargo_toml = parse_cargo_toml(&path); - cargo_toml.insert(table.into(), dependencies.into()); - } + let mut cargo_toml_path = path.clone(); + cargo_toml_path.pop(); // remove `Cargo.toml` from the path + let deps = update_git_dependencies(&mut cargo_toml, |dep_path| { + !cargo_toml_path.join(dep_path).exists() + }); + + write_cargo_toml(&path, cargo_toml); + merge_deps(&mut acc, deps); + acc + }) } /// Update the top level (workspace) `Cargo.toml` file. /// -/// - Adds `profile.release` = `panic = unwind` /// - Adds `workspace` definition -fn update_top_level_cargo_toml( +/// - Adds dependencies +/// - Adds `profile.release` = `panic = unwind` +fn update_root_cargo_toml( cargo_toml: &mut CargoToml, - workspace_members: Vec<&PathBuf>, - node_template_path: &Path, + members: &[String], + deps: Dependencies, + commit_id: &str, ) { - let mut panic_unwind = toml::value::Table::new(); - panic_unwind.insert("panic".into(), "unwind".into()); - - let mut profile = toml::value::Table::new(); - profile.insert("release".into(), panic_unwind.into()); - - cargo_toml.insert("profile".into(), profile.into()); + let mut workspace = Table::new(); + workspace.insert("members", value(Array::from_iter(members.iter()))); + + let mut workspace_dependencies = Table::new(); + deps.values() + .flatten() + .sorted_by_key(|(name, _)| *name) + .for_each(|(name, dep)| { + if let Some(version) = &dep.version { + workspace_dependencies[name]["version"] = value(version); + } + if let Some(default_features) = dep.default_features { + workspace_dependencies[name]["default-features"] = value(default_features); + } + workspace_dependencies[name]["git"] = value(SUBSTRATE_GIT_URL); + workspace_dependencies[name]["rev"] = value(commit_id); + }); + + workspace.insert("dependencies", Item::Table(workspace_dependencies)); + cargo_toml.insert("workspace", Item::Table(workspace)); + + let mut panic_unwind = Table::new(); + panic_unwind.insert("panic", value("unwind")); + let mut profile = Table::new(); + profile.insert("release", Item::Table(panic_unwind)); + cargo_toml.insert("profile", Item::Table(profile.into())); +} - let members = workspace_members +fn process_root_cargo_toml( + root_cargo_toml_path: &Path, + root_deps: Dependencies, + cargo_tomls: &[PathBuf], + node_template_path: &PathBuf, + commit_id: &str, +) { + let mut root_cargo_toml = parse_cargo_toml(root_cargo_toml_path); + let workspace_members = cargo_tomls .iter() .map(|p| { p.strip_prefix(node_template_path) @@ -165,16 +224,8 @@ fn update_top_level_cargo_toml( }) .collect::>(); - let mut members_section = toml::value::Table::new(); - members_section.insert("members".into(), members.into()); - - cargo_toml.insert("workspace".into(), members_section.into()); -} - -fn write_cargo_toml(path: &Path, cargo_toml: CargoToml) { - let content = toml::to_string_pretty(&cargo_toml).expect("Creates `Cargo.toml`"); - let mut file = File::create(path).expect(&format!("Creates `{}`.", path.display())); - write!(file, "{}", content).expect("Writes `Cargo.toml`"); + update_root_cargo_toml(&mut root_cargo_toml, &workspace_members, root_deps, commit_id); + write_cargo_toml(&root_cargo_toml_path, root_cargo_toml); } /// Build and test the generated node-template @@ -211,8 +262,8 @@ fn build_and_test(path: &Path, cargo_tomls: &[PathBuf]) { fn main() { let options = Options::parse(); + // Copy node-template to a temp build dir. let build_dir = tempfile::tempdir().expect("Creates temp build dir"); - let node_template_folder = options .node_template .canonicalize() @@ -220,46 +271,38 @@ fn main() { .file_name() .expect("Node template folder is last element of path") .to_owned(); + copy_node_template(&options.node_template, build_dir.path()); // The path to the node-template in the build dir. let node_template_path = build_dir.path().join(node_template_folder); + let root_cargo_toml_path = node_template_path.join("Cargo.toml"); - copy_node_template(&options.node_template, build_dir.path()); - let mut cargo_tomls = find_cargo_tomls(build_dir.path().to_owned()); - - let commit_id = get_git_commit_id(&options.node_template); - let top_level_cargo_toml_path = node_template_path.join("Cargo.toml"); + // Get all `Cargo.toml` files in the node-template + let mut cargo_tomls = find_cargo_tomls(&node_template_path); - // Check if top level Cargo.toml exists. If not, create one in the destination - if !cargo_tomls.contains(&top_level_cargo_toml_path) { - // create the top_level_cargo_toml + // Check if top level Cargo.toml exists. If not, create one in the destination, + // else remove it from the list, as this requires some special treatments. + if let Some(index) = cargo_tomls.iter().position(|x| *x == root_cargo_toml_path) { + cargo_tomls.remove(index); + } else { OpenOptions::new() .create(true) .write(true) - .open(top_level_cargo_toml_path.clone()) + .open(root_cargo_toml_path.clone()) .expect("Create root level `Cargo.toml` failed."); - - // push into our data structure - cargo_tomls.push(PathBuf::from(top_level_cargo_toml_path.clone())); } - cargo_tomls.iter().for_each(|t| { - let mut cargo_toml = parse_cargo_toml(&t); - replace_path_dependencies_with_git(&t, &commit_id, &mut cargo_toml); - - // Check if this is the top level `Cargo.toml`, as this requires some special treatments. - if top_level_cargo_toml_path == *t { - // All workspace member `Cargo.toml` file paths. - let workspace_members = - cargo_tomls.iter().filter(|p| **p != top_level_cargo_toml_path).collect(); - - update_top_level_cargo_toml(&mut cargo_toml, workspace_members, &node_template_path); - } - - write_cargo_toml(&t, cargo_toml); - }); + // Process all `Cargo.toml` files. + let root_deps = process_cargo_tomls(&cargo_tomls); + process_root_cargo_toml( + &root_cargo_toml_path, + root_deps, + &cargo_tomls, + &node_template_path, + &get_git_commit_id(&options.node_template), + ); - // adding root rustfmt to node template build path + // Add root rustfmt to node template build path. let node_template_rustfmt_toml_path = node_template_path.join("rustfmt.toml"); let root_rustfmt_toml = &options.node_template.join("../../rustfmt.toml"); if root_rustfmt_toml.exists() { @@ -277,3 +320,103 @@ fn main() { tar.append_dir_all("substrate-node-template", node_template_path) .expect("Writes substrate-node-template archive"); } + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_update_git_dependencies() { + let toml = r#" +[dev-dependencies] +scale-info = { version = "2.5.0", default-features = false, features = ["derive"] } + +[dependencies] +scale-info = { version = "2.5.0", default-features = false, features = ["derive"] } +sp-io = { version = "7.0.0", path = "../../../../primitives/io" } +frame-system = { version = "4.0.0-dev", default-features = false, path = "../../../../frame/system" } +"#; + let mut cargo_toml = toml.parse::().expect("invalid doc"); + let actual_deps = update_git_dependencies(&mut cargo_toml, |_| true); + + assert_eq!(actual_deps.len(), 3); + assert_eq!(actual_deps.get("dependencies").unwrap().len(), 2); + assert_eq!(actual_deps.get("dev-dependencies").unwrap().len(), 0); + assert_eq!( + actual_deps.get("dependencies").unwrap().get("sp-io").unwrap(), + &Dependency { + name: "sp-io".into(), + version: Some("7.0.0".into()), + default_features: None + } + ); + assert_eq!( + actual_deps.get("dependencies").unwrap().get("frame-system").unwrap(), + &Dependency { + name: "frame-system".into(), + version: Some("4.0.0-dev".into()), + default_features: Some(false), + } + ); + + let expected_toml = r#" +[dev-dependencies] +scale-info = { version = "2.5.0", default-features = false, features = ["derive"] } + +[dependencies] +scale-info = { version = "2.5.0", default-features = false, features = ["derive"] } +sp-io = { workspace = true } +frame-system = { workspace = true } +"#; + assert_eq!(cargo_toml.to_string(), expected_toml); + } + + #[test] + fn test_update_root_cargo_toml() { + let mut cargo_toml = CargoToml::new(); + update_root_cargo_toml( + &mut cargo_toml, + &vec!["node".into(), "pallets/template".into(), "runtime".into()], + Dependencies::from([ + ( + "dependencies".into(), + HashMap::from([ + ( + "sp-io".into(), + Dependency { + name: "sp-io".into(), + version: Some("7.0.0".into()), + default_features: None, + }, + ), + ( + "frame-system".into(), + Dependency { + name: "frame-system".into(), + version: Some("4.0.0-dev".into()), + default_features: Some(true), + }, + ), + ]), + ), + ("dev-dependencies".into(), HashMap::new()), + ("build-dependencies".into(), HashMap::new()), + ]), + "commit_id", + ); + + let expected_toml = r#"[workspace] +members = ["node", "pallets/template", "runtime"] + +[workspace.dependencies] +frame-system = { version = "4.0.0-dev", default-features = true, git = "https://github.com/paritytech/substrate.git", rev = "commit_id" } +sp-io = { version = "7.0.0", git = "https://github.com/paritytech/substrate.git", rev = "commit_id" } + +[profile] + +[profile.release] +panic = "unwind" +"#; + assert_eq!(cargo_toml.to_string(), expected_toml); + } +} From 1b8ad0303b039b6f958ca3e52e84befb2bb44ad2 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 16 May 2023 08:18:13 +0000 Subject: [PATCH 506/558] Bump tower-http from 0.3.5 to 0.4.0 (#14128) Bumps [tower-http](https://github.com/tower-rs/tower-http) from 0.3.5 to 0.4.0. - [Release notes](https://github.com/tower-rs/tower-http/releases) - [Commits](https://github.com/tower-rs/tower-http/compare/tower-http-0.3.5...tower-http-0.4.0) --- updated-dependencies: - dependency-name: tower-http dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- Cargo.lock | 4 ++-- client/rpc-servers/Cargo.toml | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 23a70da4d..25548916b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -12293,9 +12293,9 @@ dependencies = [ [[package]] name = "tower-http" -version = "0.3.5" +version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f873044bf02dd1e8239e9c1293ea39dad76dc594ec16185d0a1bf31d8dc8d858" +checksum = "5d1d42a9b3f3ec46ba828e8d376aec14592ea199f70a06a548587ecd1c4ab658" dependencies = [ "bitflags", "bytes", diff --git a/client/rpc-servers/Cargo.toml b/client/rpc-servers/Cargo.toml index c6228d739..704799112 100644 --- a/client/rpc-servers/Cargo.toml +++ b/client/rpc-servers/Cargo.toml @@ -18,6 +18,6 @@ log = "0.4.17" serde_json = "1.0.85" tokio = { version = "1.22.0", features = ["parking_lot"] } prometheus-endpoint = { package = "substrate-prometheus-endpoint", version = "0.10.0-dev", path = "../../utils/prometheus" } -tower-http = { version = "0.3.4", features = ["cors"] } +tower-http = { version = "0.4.0", features = ["cors"] } tower = "0.4.13" http = "0.2.8" From 3c10b1228ff1590399234e56261ab2a0836ca0e9 Mon Sep 17 00:00:00 2001 From: Juan Date: Tue, 16 May 2023 12:24:28 +0200 Subject: [PATCH 507/558] Add review rules regarding the owned files (#14122) * Add FRAME Coders PR rule * change regexp rule from `beef` to `beefy` * exclude pallet-aura from frame coders rule --------- Co-authored-by: parity-processbot <> --- .github/pr-custom-review.yml | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/.github/pr-custom-review.yml b/.github/pr-custom-review.yml index 3620f7e77..c10fb6df2 100644 --- a/.github/pr-custom-review.yml +++ b/.github/pr-custom-review.yml @@ -8,12 +8,24 @@ rules: check_type: changed_files condition: include: .* - # excluding files from 'CI team' rules - exclude: ^\.gitlab-ci\.yml|^scripts/ci/.*|^\.github/.*|^\.config/nextest.toml + # excluding files from 'CI team' and 'FRAME coders' rules + exclude: ^\.gitlab-ci\.yml|^scripts/ci/.*|^\.github/.*|^\.config/nextest.toml|^frame/(?!.*(nfts/.*|uniques/.*|babe/.*|grandpa/.*|beefy|merkle-mountain-range/.*|contracts/.*|election|nomination-pools/.*|staking/.*|aura/.*)) min_approvals: 2 teams: - core-devs + - name: FRAME coders + check_type: changed_files + condition: + include: ^frame/(?!.*(nfts/.*|uniques/.*|babe/.*|grandpa/.*|beefy|merkle-mountain-range/.*|contracts/.*|election|nomination-pools/.*|staking/.*|aura/.*)) + all: + - min_approvals: 2 + teams: + - core-devs + - min_approvals: 1 + teams: + - frame-coders + - name: CI team check_type: changed_files condition: From f741f2e9fb082c1590f2212749dc0adefda20cea Mon Sep 17 00:00:00 2001 From: Oleg Plakida <112385193+oleg-plakida@users.noreply.github.com> Date: Tue, 16 May 2023 11:24:54 +0100 Subject: [PATCH 508/558] Change unit test upload step (#14124) Co-authored-by: parity-processbot <> --- scripts/ci/gitlab/pipeline/test.yml | 24 ++++++++---------------- 1 file changed, 8 insertions(+), 16 deletions(-) diff --git a/scripts/ci/gitlab/pipeline/test.yml b/scripts/ci/gitlab/pipeline/test.yml index 6e86a01b4..f49e36c6a 100644 --- a/scripts/ci/gitlab/pipeline/test.yml +++ b/scripts/ci/gitlab/pipeline/test.yml @@ -226,28 +226,20 @@ test-linux-stable: --partition count:${CI_NODE_INDEX}/${CI_NODE_TOTAL} # we need to update cache only from one job - if [ ${CI_NODE_INDEX} == 1 ]; then rusty-cachier cache upload; fi - artifacts: - when: always - paths: - - target/nextest/default/junit.xml - reports: - junit: target/nextest/default/junit.xml - -test-linux-stable-upload-test-results: - stage: test - needs: - - job: test-linux-stable - artifacts: true - extends: - - .docker-env - - .test-refs - script: + # Upload tests results to Elasticsearch + - echo "Upload test results to Elasticsearch" - cat target/nextest/default/junit.xml | xq . > target/nextest/default/junit.json - "curl -v -XPOST --http1.1 -u ${ELASTIC_USERNAME}:${ELASTIC_PASSWORD} https://elasticsearch.parity-build.parity.io/unit-tests/_doc/${CI_JOB_ID} -H 'Content-Type: application/json' -d @target/nextest/default/junit.json" + artifacts: + when: always + paths: + - target/nextest/default/junit.xml + reports: + junit: target/nextest/default/junit.xml test-frame-support: stage: test From 1cb8fb032197e77301940a4f809b18d1269b95c2 Mon Sep 17 00:00:00 2001 From: Alexander Samusev <41779041+alvicsam@users.noreply.github.com> Date: Tue, 16 May 2023 15:54:08 +0200 Subject: [PATCH 509/558] Fix indent for pr-custom-review config (#14161) --- .github/pr-custom-review.yml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/pr-custom-review.yml b/.github/pr-custom-review.yml index c10fb6df2..059f4a283 100644 --- a/.github/pr-custom-review.yml +++ b/.github/pr-custom-review.yml @@ -20,11 +20,11 @@ rules: include: ^frame/(?!.*(nfts/.*|uniques/.*|babe/.*|grandpa/.*|beefy|merkle-mountain-range/.*|contracts/.*|election|nomination-pools/.*|staking/.*|aura/.*)) all: - min_approvals: 2 - teams: - - core-devs + teams: + - core-devs - min_approvals: 1 - teams: - - frame-coders + teams: + - frame-coders - name: CI team check_type: changed_files From 8423d2f59e53bf178d3462b28ddd7152d83de17e Mon Sep 17 00:00:00 2001 From: Suraj Kumar Date: Tue, 16 May 2023 22:09:21 +0530 Subject: [PATCH 510/558] remove deprecated remove_prefix to clear era_info (#13460) * remove deprecated remove_prefix to clear era_info * add debug assertions to check that the returned cursor is None * add a variable to hold the cursor * ".git/.scripts/commands/fmt/fmt.sh" --------- Co-authored-by: command-bot <> --- frame/staking/src/pallet/impls.rs | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/frame/staking/src/pallet/impls.rs b/frame/staking/src/pallet/impls.rs index 1d9838572..82a0956da 100644 --- a/frame/staking/src/pallet/impls.rs +++ b/frame/staking/src/pallet/impls.rs @@ -669,12 +669,12 @@ impl Pallet { /// Clear all era information for given era. pub(crate) fn clear_era_information(era_index: EraIndex) { - #[allow(deprecated)] - >::remove_prefix(era_index, None); - #[allow(deprecated)] - >::remove_prefix(era_index, None); - #[allow(deprecated)] - >::remove_prefix(era_index, None); + let mut cursor = >::clear_prefix(era_index, u32::MAX, None); + debug_assert!(cursor.maybe_cursor.is_none()); + cursor = >::clear_prefix(era_index, u32::MAX, None); + debug_assert!(cursor.maybe_cursor.is_none()); + cursor = >::clear_prefix(era_index, u32::MAX, None); + debug_assert!(cursor.maybe_cursor.is_none()); >::remove(era_index); >::remove(era_index); >::remove(era_index); From bbe132e2ebf9ee919c6045271d5bde27bace5659 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bastian=20K=C3=B6cher?= Date: Tue, 16 May 2023 21:56:42 +0200 Subject: [PATCH 511/558] frame-system: `uniques` remove one `encode` call (#14154) * frame-system: `uniques` remove one `encode` call * ".git/.scripts/commands/fmt/fmt.sh" --------- Co-authored-by: command-bot <> --- frame/system/src/lib.rs | 22 +++++++++++----------- frame/system/src/tests.rs | 16 ++++++++++++++++ 2 files changed, 27 insertions(+), 11 deletions(-) diff --git a/frame/system/src/lib.rs b/frame/system/src/lib.rs index 71e27cd4e..7a97ace73 100644 --- a/frame/system/src/lib.rs +++ b/frame/system/src/lib.rs @@ -1618,21 +1618,21 @@ impl Pallet { .ok_or(Error::::FailedToExtractRuntimeVersion)?; cfg_if::cfg_if! { - if #[cfg(all(feature = "runtime-benchmarks", not(test)))] { + if #[cfg(all(feature = "runtime-benchmarks", not(test)))] { // Let's ensure the compiler doesn't optimize our fetching of the runtime version away. core::hint::black_box((new_version, current_version)); Ok(()) - } else { - if new_version.spec_name != current_version.spec_name { - return Err(Error::::InvalidSpecName.into()) - } + } else { + if new_version.spec_name != current_version.spec_name { + return Err(Error::::InvalidSpecName.into()) + } - if new_version.spec_version <= current_version.spec_version { - return Err(Error::::SpecVersionNeedsToIncrease.into()) - } + if new_version.spec_version <= current_version.spec_version { + return Err(Error::::SpecVersionNeedsToIncrease.into()) + } - Ok(()) - } + Ok(()) + } } } } @@ -1643,7 +1643,7 @@ pub fn unique(entropy: impl Encode) -> [u8; 32] { let mut last = [0u8; 32]; sp_io::storage::read(well_known_keys::INTRABLOCK_ENTROPY, &mut last[..], 0); let next = (b"frame_system::unique", entropy, last).using_encoded(blake2_256); - sp_io::storage::set(well_known_keys::INTRABLOCK_ENTROPY, &next.encode()); + sp_io::storage::set(well_known_keys::INTRABLOCK_ENTROPY, &next); next } diff --git a/frame/system/src/tests.rs b/frame/system/src/tests.rs index d210bb609..04c178246 100644 --- a/frame/system/src/tests.rs +++ b/frame/system/src/tests.rs @@ -41,13 +41,29 @@ fn unique_datum_works() { assert!(sp_io::storage::exists(well_known_keys::INTRABLOCK_ENTROPY)); let h1 = unique(b""); + assert_eq!( + 32, + sp_io::storage::read(well_known_keys::INTRABLOCK_ENTROPY, &mut [], 0).unwrap() + ); let h2 = unique(b""); + assert_eq!( + 32, + sp_io::storage::read(well_known_keys::INTRABLOCK_ENTROPY, &mut [], 0).unwrap() + ); assert_ne!(h1, h2); let h3 = unique(b"Hello"); + assert_eq!( + 32, + sp_io::storage::read(well_known_keys::INTRABLOCK_ENTROPY, &mut [], 0).unwrap() + ); assert_ne!(h2, h3); let h4 = unique(b"Hello"); + assert_eq!( + 32, + sp_io::storage::read(well_known_keys::INTRABLOCK_ENTROPY, &mut [], 0).unwrap() + ); assert_ne!(h3, h4); System::finalize(); From 43a130c963a44fed05de77cb6be2bfa9ac8e0cc1 Mon Sep 17 00:00:00 2001 From: Muharem Ismailov Date: Wed, 17 May 2023 12:58:44 +0200 Subject: [PATCH 512/558] Max class voters for ranked collective vote tally (#13313) * max class voters for vote tally * fix move * tests * rename to GetMaxVoters * saturating sub --------- Co-authored-by: parity-processbot <> --- frame/ranked-collective/src/lib.rs | 47 ++++++++++++--------- frame/ranked-collective/src/tests.rs | 62 ++++++++++++++++++++++++++-- 2 files changed, 86 insertions(+), 23 deletions(-) diff --git a/frame/ranked-collective/src/lib.rs b/frame/ranked-collective/src/lib.rs index 288fd78d6..6296403d2 100644 --- a/frame/ranked-collective/src/lib.rs +++ b/frame/ranked-collective/src/lib.rs @@ -112,36 +112,39 @@ impl, I: 'static, M: GetMaxVoters> Tally { pub type TallyOf = Tally>; pub type PollIndexOf = <>::Polls as Polling>>::Index; +pub type ClassOf = <>::Polls as Polling>>::Class; type AccountIdLookupOf = <::Lookup as StaticLookup>::Source; -impl, I: 'static, M: GetMaxVoters> VoteTally for Tally { - fn new(_: Rank) -> Self { +impl, I: 'static, M: GetMaxVoters>> + VoteTally> for Tally +{ + fn new(_: ClassOf) -> Self { Self { bare_ayes: 0, ayes: 0, nays: 0, dummy: PhantomData } } - fn ayes(&self, _: Rank) -> Votes { + fn ayes(&self, _: ClassOf) -> Votes { self.bare_ayes } - fn support(&self, class: Rank) -> Perbill { + fn support(&self, class: ClassOf) -> Perbill { Perbill::from_rational(self.bare_ayes, M::get_max_voters(class)) } - fn approval(&self, _: Rank) -> Perbill { + fn approval(&self, _: ClassOf) -> Perbill { Perbill::from_rational(self.ayes, 1.max(self.ayes + self.nays)) } #[cfg(feature = "runtime-benchmarks")] - fn unanimity(class: Rank) -> Self { + fn unanimity(class: ClassOf) -> Self { Self { - bare_ayes: M::get_max_voters(class), + bare_ayes: M::get_max_voters(class.clone()), ayes: M::get_max_voters(class), nays: 0, dummy: PhantomData, } } #[cfg(feature = "runtime-benchmarks")] - fn rejection(class: Rank) -> Self { + fn rejection(class: ClassOf) -> Self { Self { bare_ayes: 0, ayes: 0, nays: M::get_max_voters(class), dummy: PhantomData } } #[cfg(feature = "runtime-benchmarks")] - fn from_requirements(support: Perbill, approval: Perbill, class: Rank) -> Self { + fn from_requirements(support: Perbill, approval: Perbill, class: ClassOf) -> Self { let c = M::get_max_voters(class); let ayes = support * c; let nays = ((ayes as u64) * 1_000_000_000u64 / approval.deconstruct() as u64) as u32 - ayes; @@ -149,14 +152,17 @@ impl, I: 'static, M: GetMaxVoters> VoteTally for Tally } #[cfg(feature = "runtime-benchmarks")] - fn setup(class: Rank, granularity: Perbill) { - if M::get_max_voters(class) == 0 { + fn setup(class: ClassOf, granularity: Perbill) { + if M::get_max_voters(class.clone()) == 0 { let max_voters = granularity.saturating_reciprocal_mul(1u32); for i in 0..max_voters { let who: T::AccountId = frame_benchmarking::account("ranked_collective_benchmarking", i, 0); - crate::Pallet::::do_add_member_to_rank(who, class) - .expect("could not add members for benchmarks"); + crate::Pallet::::do_add_member_to_rank( + who, + T::MinRankOfClass::convert(class.clone()), + ) + .expect("could not add members for benchmarks"); } assert_eq!(M::get_max_voters(class), max_voters); } @@ -234,14 +240,17 @@ impl Convert for Geometric { } } -/// Trait for getting the maximum number of voters for a given rank. +/// Trait for getting the maximum number of voters for a given poll class. pub trait GetMaxVoters { - /// Return the maximum number of voters for the rank `r`. - fn get_max_voters(r: Rank) -> MemberIndex; + /// Poll class type. + type Class; + /// Return the maximum number of voters for the poll class `c`. + fn get_max_voters(c: Self::Class) -> MemberIndex; } impl, I: 'static> GetMaxVoters for Pallet { - fn get_max_voters(r: Rank) -> MemberIndex { - MemberCount::::get(r) + type Class = ClassOf; + fn get_max_voters(c: Self::Class) -> MemberIndex { + MemberCount::::get(T::MinRankOfClass::convert(c)) } } @@ -346,7 +355,7 @@ pub mod pallet { /// Convert the tally class into the minimum rank required to vote on the poll. If /// `Polls::Class` is the same type as `Rank`, then `Identity` can be used here to mean /// "a rank of at least the poll class". - type MinRankOfClass: Convert<>>::Class, Rank>; + type MinRankOfClass: Convert, Rank>; /// Convert a rank_delta into a number of votes the rank gets. /// diff --git a/frame/ranked-collective/src/tests.rs b/frame/ranked-collective/src/tests.rs index 91244a90b..04519bc0f 100644 --- a/frame/ranked-collective/src/tests.rs +++ b/frame/ranked-collective/src/tests.rs @@ -25,10 +25,10 @@ use frame_support::{ parameter_types, traits::{ConstU16, ConstU32, ConstU64, EitherOf, Everything, MapSuccess, Polling}, }; -use sp_core::H256; +use sp_core::{Get, H256}; use sp_runtime::{ testing::Header, - traits::{BlakeTwo256, Identity, IdentityLookup, ReduceBy}, + traits::{BlakeTwo256, IdentityLookup, ReduceBy}, }; use super::*; @@ -36,6 +36,7 @@ use crate as pallet_ranked_collective; type UncheckedExtrinsic = frame_system::mocking::MockUncheckedExtrinsic; type Block = frame_system::mocking::MockBlock; +type Class = Rank; frame_support::construct_runtime!( pub enum Test where @@ -95,7 +96,7 @@ impl Polling> for TestPolls { type Index = u8; type Votes = Votes; type Moment = u64; - type Class = Rank; + type Class = Class; fn classes() -> Vec { vec![0, 1, 2] } @@ -164,6 +165,19 @@ impl Polling> for TestPolls { } } +/// Convert the tally class into the minimum rank required to vote on the poll. +/// MinRank(Class) = Class - Delta +pub struct MinRankOfClass(PhantomData); +impl> Convert for MinRankOfClass { + fn convert(a: Class) -> Rank { + a.saturating_sub(Delta::get()) + } +} + +parameter_types! { + pub static MinRankOfClassDelta: Rank = 0; +} + impl Config for Test { type WeightInfo = (); type RuntimeEvent = RuntimeEvent; @@ -180,7 +194,7 @@ impl Config for Test { MapSuccess, ReduceBy>>, >; type Polls = TestPolls; - type MinRankOfClass = Identity; + type MinRankOfClass = MinRankOfClass; type VoteWeight = Geometric; } @@ -499,3 +513,43 @@ fn do_add_member_to_rank_works() { assert_eq!(member_count(max_rank + 1), 0); }) } + +#[test] +fn tally_support_correct() { + new_test_ext().execute_with(|| { + // add members, + // rank 1: accounts 1, 2, 3 + // rank 2: accounts 2, 3 + // rank 3: accounts 3. + assert_ok!(Club::add_member(RuntimeOrigin::root(), 1)); + assert_ok!(Club::promote_member(RuntimeOrigin::root(), 1)); + assert_ok!(Club::add_member(RuntimeOrigin::root(), 2)); + assert_ok!(Club::promote_member(RuntimeOrigin::root(), 2)); + assert_ok!(Club::promote_member(RuntimeOrigin::root(), 2)); + assert_ok!(Club::add_member(RuntimeOrigin::root(), 3)); + assert_ok!(Club::promote_member(RuntimeOrigin::root(), 3)); + assert_ok!(Club::promote_member(RuntimeOrigin::root(), 3)); + assert_ok!(Club::promote_member(RuntimeOrigin::root(), 3)); + + // init tally with 1 aye vote. + let tally: TallyOf = Tally::from_parts(1, 1, 0); + + // with minRank(Class) = Class + // for class 3, 100% support. + MinRankOfClassDelta::set(0); + assert_eq!(tally.support(3), Perbill::from_rational(1u32, 1)); + + // with minRank(Class) = (Class - 1) + // for class 3, ~50% support. + MinRankOfClassDelta::set(1); + assert_eq!(tally.support(3), Perbill::from_rational(1u32, 2)); + + // with minRank(Class) = (Class - 2) + // for class 3, ~33% support. + MinRankOfClassDelta::set(2); + assert_eq!(tally.support(3), Perbill::from_rational(1u32, 3)); + + // reset back. + MinRankOfClassDelta::set(0); + }); +} From 6507a79b2baf7e4cc6f5868aa490dfe275144f9a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bigna=20H=C3=A4rdi?= Date: Wed, 17 May 2023 13:31:12 +0200 Subject: [PATCH 513/558] Add `serde` feature flag to primitives (#13027) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * add serde_full feature flag add serde_full to sp_runtime add space to toml add serde_full to application-crypto add serde_full to arithmetic fix arithmetic add serde full to beefy add serde full to consensus add serde_full to core add serdefull to finality grandpa add serde_full to several primitives crates make rpc no_std compatible add scale info to runtime make serializer no_std compatible add serde full to storage add full serde to version add serde full to weights add all serde_full features add . to comment add missing impl-serde fix no-std build fix build add full_crypto to serde_full serde_full also implements crypto full_serde does not work with full_crytpo. needs std no no_std serde impl possible also for crypto std is necessary no serde full for application crypto fix arithmetic fix tomls fix some things impl fmt for Signature add serialize to Public add impl_maybe_marker_serde_full fix sp-application-crypto toml add serde feature flag fix clippy fix toml grandpa fix grandpa rename if_std to if_serde keystore is not no_std compatible make keystore vrf no_std compatible fix nopos-elections fix rpc fix serializer fix test-primitives fix version add comment add serde full only import for format string remove all(serde_full and full_crypot) as serde_full enforces full_crypto make comment better readable even better comment clean up rpc toml clean up toml clean up serializer toml clean up storage toml fix std build update .lock fix sp-version move sp_std import test extern crate alloc replace sp_std with core add missing core sp_core: serde feature do not enforce full crypto application-crypto: serde feature do not enforce full crypto rename serde_full to serde add dep:serde and alloc to default feature add full_crypto and remove unnecessary debu/fmt impls for serde update comment remove obolsete change in display AccountId32 remove extra changes minimize diff revert keystore changes remove std from keystore remove full-crypto feature fix serde import fix comment fix feature = serde * rename serde_full to serde * move #[doc(hidden)] back * remove feature = full crypto require frm MultiSigner * reorder serde and scale_info import * fix bs58 missing alloc import in serde feature * add `from_string` to serde feature and add unimplemented * remove serde feature from fixed_point display * Remove serde/alloc Co-authored-by: Davide Galassi * Update primitives/consensus/babe/Cargo.toml Co-authored-by: Bastian Köcher * Update primitives/arithmetic/src/fixed_point.rs Co-authored-by: Bastian Köcher * revert `from_string`fixed impl back to std only * remove duplicate runtime string impl * use sp_std::alloc * remove no_std compatible rpc * remove no_std compatibility from serializer * rename mpl_maybe_marker_serde to std_or_serde * update .lock * add sp-std to executor * fix sp-std import * fix sp_std::format import * use crate import * add serde feature * Update primitives/core/src/lib.rs --------- Co-authored-by: Davide Galassi Co-authored-by: Bastian Köcher --- Cargo.lock | 1 + primitives/application-crypto/Cargo.toml | 11 +++- primitives/application-crypto/src/lib.rs | 32 ++++++---- primitives/arithmetic/Cargo.toml | 9 ++- primitives/arithmetic/src/fixed_point.rs | 11 ++-- primitives/arithmetic/src/lib.rs | 4 +- primitives/arithmetic/src/per_things.rs | 4 +- primitives/consensus/babe/Cargo.toml | 14 ++++- primitives/consensus/babe/src/lib.rs | 6 +- primitives/consensus/beefy/Cargo.toml | 13 +++- primitives/consensus/beefy/src/mmr.rs | 2 +- primitives/consensus/grandpa/Cargo.toml | 13 +++- primitives/consensus/grandpa/src/lib.rs | 6 +- primitives/consensus/slots/Cargo.toml | 10 ++- primitives/consensus/slots/src/lib.rs | 2 +- primitives/core/Cargo.toml | 24 +++++-- primitives/core/src/crypto.rs | 62 ++++++++++--------- primitives/core/src/ecdsa.rs | 14 +++-- primitives/core/src/ed25519.rs | 14 +++-- primitives/core/src/lib.rs | 53 +++++++++++++--- primitives/core/src/offchain/mod.rs | 6 +- primitives/core/src/sr25519.rs | 29 +++++---- primitives/merkle-mountain-range/Cargo.toml | 12 +++- primitives/merkle-mountain-range/src/lib.rs | 4 +- primitives/npos-elections/Cargo.toml | 13 +++- primitives/npos-elections/src/assignments.rs | 6 +- primitives/npos-elections/src/lib.rs | 6 +- primitives/runtime/Cargo.toml | 14 ++++- primitives/runtime/src/generic/block.rs | 14 ++--- primitives/runtime/src/generic/digest.rs | 10 +-- primitives/runtime/src/generic/era.rs | 4 +- primitives/runtime/src/generic/header.rs | 14 ++--- .../src/generic/unchecked_extrinsic.rs | 6 +- .../runtime/src/legacy/byte_sized_error.rs | 10 +-- primitives/runtime/src/lib.rs | 30 ++++----- primitives/runtime/src/runtime_string.rs | 6 +- primitives/runtime/src/traits.rs | 12 ++-- .../runtime/src/transaction_validity.rs | 6 +- primitives/storage/Cargo.toml | 18 +++++- primitives/storage/src/lib.rs | 22 +++---- primitives/test-primitives/Cargo.toml | 14 ++++- primitives/test-primitives/src/lib.rs | 3 +- primitives/version/Cargo.toml | 15 +++-- primitives/version/src/lib.rs | 13 ++-- primitives/weights/Cargo.toml | 12 +++- primitives/weights/src/lib.rs | 6 +- primitives/weights/src/weight_v2.rs | 2 +- 47 files changed, 406 insertions(+), 206 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 25548916b..62e99d437 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -11311,6 +11311,7 @@ dependencies = [ "sp-application-crypto", "sp-core", "sp-runtime", + "sp-std", ] [[package]] diff --git a/primitives/application-crypto/Cargo.toml b/primitives/application-crypto/Cargo.toml index 37b58bc2b..dc97948fd 100644 --- a/primitives/application-crypto/Cargo.toml +++ b/primitives/application-crypto/Cargo.toml @@ -18,7 +18,7 @@ targets = ["x86_64-unknown-linux-gnu"] sp-core = { version = "7.0.0", default-features = false, path = "../core" } codec = { package = "parity-scale-codec", version = "3.2.2", default-features = false, features = ["derive"] } scale-info = { version = "2.5.0", default-features = false, features = ["derive"] } -serde = { version = "1.0.136", optional = true, features = ["derive"] } +serde = { version = "1.0.136", default-features = false, optional = true, features = ["derive", "alloc"] } sp-std = { version = "5.0.0", default-features = false, path = "../std" } sp-io = { version = "7.0.0", default-features = false, path = "../io" } @@ -29,11 +29,18 @@ std = [ "sp-core/std", "codec/std", "scale-info/std", - "serde", + "serde/std", "sp-std/std", "sp-io/std", ] +# Serde support without relying on std features. +serde = [ + "dep:serde", + "sp-core/serde", + "scale-info/serde", +] + # This feature enables all crypto primitives for `no_std` builds like microcontrollers # or Intel SGX. # For the regular wasm runtime builds this should not be used. diff --git a/primitives/application-crypto/src/lib.rs b/primitives/application-crypto/src/lib.rs index 3e8f2f5a7..11be54e29 100644 --- a/primitives/application-crypto/src/lib.rs +++ b/primitives/application-crypto/src/lib.rs @@ -23,20 +23,25 @@ pub use sp_core::crypto::{key_types, CryptoTypeId, KeyTypeId}; #[doc(hidden)] #[cfg(feature = "full_crypto")] -pub use sp_core::crypto::{DeriveError, DeriveJunction, Pair, SecretStringError, Ss58Codec}; +pub use sp_core::crypto::{DeriveError, Pair, SecretStringError}; +#[cfg(any(feature = "full_crypto", feature = "serde"))] +pub use sp_core::crypto::{DeriveJunction, Ss58Codec}; #[doc(hidden)] pub use sp_core::{ self, crypto::{ByteArray, CryptoType, Derive, IsWrappedBy, Public, UncheckedFrom, Wraps}, RuntimeDebug, }; +#[doc(hidden)] +#[cfg(all(not(feature = "std"), feature = "serde"))] +pub use sp_std::alloc::{format, string::String}; #[doc(hidden)] pub use codec; #[doc(hidden)] pub use scale_info; #[doc(hidden)] -#[cfg(feature = "std")] +#[cfg(feature = "serde")] pub use serde; #[doc(hidden)] pub use sp_std::{ops::Deref, vec::Vec}; @@ -282,7 +287,7 @@ macro_rules! app_crypto_public_not_full_crypto { #[macro_export] macro_rules! app_crypto_public_common { ($public:ty, $sig:ty, $key_type:expr, $crypto_type:expr) => { - $crate::app_crypto_public_common_if_std!(); + $crate::app_crypto_public_common_if_serde!(); impl AsRef<[u8]> for Public { fn as_ref(&self) -> &[u8] { @@ -323,11 +328,11 @@ macro_rules! app_crypto_public_common { }; } -/// Implements traits for the public key type if `feature = "std"` is enabled. -#[cfg(feature = "std")] +/// Implements traits for the public key type if `feature = "serde"` is enabled. +#[cfg(feature = "serde")] #[doc(hidden)] #[macro_export] -macro_rules! app_crypto_public_common_if_std { +macro_rules! app_crypto_public_common_if_serde { () => { impl $crate::Derive for Public { fn derive>( @@ -338,15 +343,15 @@ macro_rules! app_crypto_public_common_if_std { } } - impl std::fmt::Display for Public { - fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { + impl core::fmt::Display for Public { + fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result { use $crate::Ss58Codec; write!(f, "{}", self.0.to_ss58check()) } } impl $crate::serde::Serialize for Public { - fn serialize(&self, serializer: S) -> std::result::Result + fn serialize(&self, serializer: S) -> core::result::Result where S: $crate::serde::Serializer, { @@ -356,11 +361,14 @@ macro_rules! app_crypto_public_common_if_std { } impl<'de> $crate::serde::Deserialize<'de> for Public { - fn deserialize(deserializer: D) -> std::result::Result + fn deserialize(deserializer: D) -> core::result::Result where D: $crate::serde::Deserializer<'de>, { use $crate::Ss58Codec; + #[cfg(all(not(feature = "std"), feature = "serde"))] + use $crate::{format, String}; + Public::from_ss58check(&String::deserialize(deserializer)?) .map_err(|e| $crate::serde::de::Error::custom(format!("{:?}", e))) } @@ -368,10 +376,10 @@ macro_rules! app_crypto_public_common_if_std { }; } -#[cfg(not(feature = "std"))] +#[cfg(not(feature = "serde"))] #[doc(hidden)] #[macro_export] -macro_rules! app_crypto_public_common_if_std { +macro_rules! app_crypto_public_common_if_serde { () => { impl $crate::Derive for Public {} }; diff --git a/primitives/arithmetic/Cargo.toml b/primitives/arithmetic/Cargo.toml index 92eebd4a6..122691623 100644 --- a/primitives/arithmetic/Cargo.toml +++ b/primitives/arithmetic/Cargo.toml @@ -21,7 +21,7 @@ codec = { package = "parity-scale-codec", version = "3.2.2", default-features = integer-sqrt = "0.1.2" num-traits = { version = "0.2.8", default-features = false } scale-info = { version = "2.5.0", default-features = false, features = ["derive"] } -serde = { version = "1.0.136", features = ["derive"], optional = true } +serde = { version = "1.0.136", default-features = false, features = ["derive", "alloc"], optional = true } static_assertions = "1.1.0" sp-std = { version = "5.0.0", default-features = false, path = "../std" } @@ -37,9 +37,14 @@ std = [ "codec/std", "num-traits/std", "scale-info/std", - "serde", + "serde/std", "sp-std/std", ] +# Serde support without relying on std features. +serde = [ + "dep:serde", + "scale-info/serde", +] [[bench]] name = "bench" diff --git a/primitives/arithmetic/src/fixed_point.rs b/primitives/arithmetic/src/fixed_point.rs index 67fbd5bc7..08b788e9a 100644 --- a/primitives/arithmetic/src/fixed_point.rs +++ b/primitives/arithmetic/src/fixed_point.rs @@ -32,9 +32,12 @@ use sp_std::{ prelude::*, }; -#[cfg(feature = "std")] +#[cfg(feature = "serde")] use serde::{de, Deserialize, Deserializer, Serialize, Serializer}; +#[cfg(all(not(feature = "std"), feature = "serde"))] +use sp_std::alloc::string::{String, ToString}; + /// Integer types that can be used to interact with `FixedPointNumber` implementations. pub trait FixedPointOperand: Copy @@ -928,14 +931,12 @@ macro_rules! implement_fixed { } } - #[cfg(feature = "std")] impl sp_std::fmt::Display for $name { fn fmt(&self, f: &mut sp_std::fmt::Formatter) -> sp_std::fmt::Result { write!(f, "{}", self.0) } } - #[cfg(feature = "std")] impl sp_std::str::FromStr for $name { type Err = &'static str; @@ -948,7 +949,7 @@ macro_rules! implement_fixed { // Manual impl `Serialize` as serde_json does not support i128. // TODO: remove impl if issue https://github.com/serde-rs/json/issues/548 fixed. - #[cfg(feature = "std")] + #[cfg(feature = "serde")] impl Serialize for $name { fn serialize(&self, serializer: S) -> Result where @@ -960,7 +961,7 @@ macro_rules! implement_fixed { // Manual impl `Deserialize` as serde_json does not support i128. // TODO: remove impl if issue https://github.com/serde-rs/json/issues/548 fixed. - #[cfg(feature = "std")] + #[cfg(feature = "serde")] impl<'de> Deserialize<'de> for $name { fn deserialize(deserializer: D) -> Result where diff --git a/primitives/arithmetic/src/lib.rs b/primitives/arithmetic/src/lib.rs index e7ba08a53..d2eceafab 100644 --- a/primitives/arithmetic/src/lib.rs +++ b/primitives/arithmetic/src/lib.rs @@ -53,12 +53,12 @@ use traits::{BaseArithmetic, One, SaturatedConversion, Unsigned, Zero}; use codec::{Decode, Encode, MaxEncodedLen}; use scale_info::TypeInfo; -#[cfg(feature = "std")] +#[cfg(feature = "serde")] use serde::{Deserialize, Serialize}; /// Arithmetic errors. #[derive(Eq, PartialEq, Clone, Copy, Encode, Decode, Debug, TypeInfo, MaxEncodedLen)] -#[cfg_attr(feature = "std", derive(Serialize, Deserialize))] +#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] pub enum ArithmeticError { /// Underflow. Underflow, diff --git a/primitives/arithmetic/src/per_things.rs b/primitives/arithmetic/src/per_things.rs index 611a4e05e..fe88b72e2 100644 --- a/primitives/arithmetic/src/per_things.rs +++ b/primitives/arithmetic/src/per_things.rs @@ -15,7 +15,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -#[cfg(feature = "std")] +#[cfg(feature = "serde")] use serde::{Deserialize, Serialize}; use crate::traits::{ @@ -556,7 +556,7 @@ macro_rules! implement_per_thing { /// A fixed point representation of a number in the range [0, 1]. /// #[doc = $title] - #[cfg_attr(feature = "std", derive(Serialize, Deserialize))] + #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] #[derive(Encode, Copy, Clone, PartialEq, Eq, codec::MaxEncodedLen, PartialOrd, Ord, scale_info::TypeInfo)] pub struct $name($type); diff --git a/primitives/consensus/babe/Cargo.toml b/primitives/consensus/babe/Cargo.toml index 7e171811a..5e57f276a 100644 --- a/primitives/consensus/babe/Cargo.toml +++ b/primitives/consensus/babe/Cargo.toml @@ -16,7 +16,7 @@ targets = ["x86_64-unknown-linux-gnu"] async-trait = { version = "0.1.57", optional = true } codec = { package = "parity-scale-codec", version = "3.2.2", default-features = false } scale-info = { version = "2.5.0", default-features = false, features = ["derive"] } -serde = { version = "1.0.136", features = ["derive"], optional = true } +serde = { version = "1.0.136", default-features = false, features = ["derive", "alloc"], optional = true } sp-api = { version = "4.0.0-dev", default-features = false, path = "../../api" } sp-application-crypto = { version = "7.0.0", default-features = false, path = "../../application-crypto" } sp-consensus = { version = "0.10.0-dev", optional = true, path = "../common" } @@ -34,7 +34,7 @@ std = [ "async-trait", "codec/std", "scale-info/std", - "serde", + "serde/std", "sp-api/std", "sp-application-crypto/std", "sp-consensus", @@ -46,3 +46,13 @@ std = [ "sp-std/std", "sp-timestamp", ] + +# Serde support without relying on std features. +serde = [ + "dep:serde", + "scale-info/serde", + "sp-application-crypto/serde", + "sp-consensus-slots/serde", + "sp-core/serde", + "sp-runtime/serde", +] diff --git a/primitives/consensus/babe/src/lib.rs b/primitives/consensus/babe/src/lib.rs index dc161525a..c083bfd9a 100644 --- a/primitives/consensus/babe/src/lib.rs +++ b/primitives/consensus/babe/src/lib.rs @@ -25,7 +25,7 @@ pub mod inherents; use codec::{Decode, Encode, MaxEncodedLen}; use scale_info::TypeInfo; -#[cfg(feature = "std")] +#[cfg(feature = "serde")] use serde::{Deserialize, Serialize}; use sp_runtime::{traits::Header, ConsensusEngineId, RuntimeDebug}; use sp_std::vec::Vec; @@ -217,7 +217,7 @@ impl BabeConfiguration { /// Types of allowed slots. #[derive(Clone, Copy, PartialEq, Eq, Encode, Decode, RuntimeDebug, MaxEncodedLen, TypeInfo)] -#[cfg_attr(feature = "std", derive(Serialize, Deserialize))] +#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] pub enum AllowedSlots { /// Only allow primary slots. PrimarySlots, @@ -241,7 +241,7 @@ impl AllowedSlots { /// Configuration data used by the BABE consensus engine that may change with epochs. #[derive(Clone, PartialEq, Eq, Encode, Decode, RuntimeDebug, MaxEncodedLen, TypeInfo)] -#[cfg_attr(feature = "std", derive(Serialize, Deserialize))] +#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] pub struct BabeEpochConfiguration { /// A constant value that is used in the threshold calculation formula. /// Expressed as a rational where the first member of the tuple is the diff --git a/primitives/consensus/beefy/Cargo.toml b/primitives/consensus/beefy/Cargo.toml index 144c8452d..cf5f660b2 100644 --- a/primitives/consensus/beefy/Cargo.toml +++ b/primitives/consensus/beefy/Cargo.toml @@ -13,8 +13,8 @@ targets = ["x86_64-unknown-linux-gnu"] [dependencies] codec = { package = "parity-scale-codec", version = "3.2.2", default-features = false, features = ["derive"] } -serde = { version = "1.0.136", optional = true, features = ["derive"] } scale-info = { version = "2.5.0", default-features = false, features = ["derive"] } +serde = { version = "1.0.136", default-features = false, optional = true, features = ["derive", "alloc"] } sp-api = { version = "4.0.0-dev", default-features = false, path = "../../api" } sp-application-crypto = { version = "7.0.0", default-features = false, path = "../../application-crypto" } sp-core = { version = "7.0.0", default-features = false, path = "../../core" } @@ -34,7 +34,7 @@ default = ["std"] std = [ "codec/std", "scale-info/std", - "serde", + "serde/std", "sp-api/std", "sp-application-crypto/std", "sp-core/std", @@ -43,3 +43,12 @@ std = [ "sp-runtime/std", "sp-std/std", ] + +# Serde support without relying on std features. +serde = [ + "dep:serde", + "scale-info/serde", + "sp-application-crypto/serde", + "sp-core/serde", + "sp-runtime/serde", +] diff --git a/primitives/consensus/beefy/src/mmr.rs b/primitives/consensus/beefy/src/mmr.rs index 465008dc2..c303cae2f 100644 --- a/primitives/consensus/beefy/src/mmr.rs +++ b/primitives/consensus/beefy/src/mmr.rs @@ -101,7 +101,7 @@ impl MmrLeafVersion { /// Details of a BEEFY authority set. #[derive(Debug, Default, PartialEq, Eq, Clone, Encode, Decode, TypeInfo, MaxEncodedLen)] -#[cfg_attr(feature = "std", derive(serde::Serialize, serde::Deserialize))] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] pub struct BeefyAuthoritySet { /// Id of the set. /// diff --git a/primitives/consensus/grandpa/Cargo.toml b/primitives/consensus/grandpa/Cargo.toml index 446471da8..a1846aa76 100644 --- a/primitives/consensus/grandpa/Cargo.toml +++ b/primitives/consensus/grandpa/Cargo.toml @@ -18,7 +18,7 @@ codec = { package = "parity-scale-codec", version = "3.2.2", default-features = grandpa = { package = "finality-grandpa", version = "0.16.2", default-features = false, features = ["derive-codec"] } log = { version = "0.4.17", default-features = false } scale-info = { version = "2.5.0", default-features = false, features = ["derive"] } -serde = { version = "1.0.136", features = ["derive"], optional = true } +serde = { version = "1.0.136", features = ["derive", "alloc"], default-features = false, optional = true } sp-api = { version = "4.0.0-dev", default-features = false, path = "../../api" } sp-application-crypto = { version = "7.0.0", default-features = false, path = "../../application-crypto" } sp-core = { version = "7.0.0", default-features = false, path = "../../core" } @@ -33,7 +33,7 @@ std = [ "grandpa/std", "log/std", "scale-info/std", - "serde", + "serde/std", "sp-api/std", "sp-application-crypto/std", "sp-core/std", @@ -41,3 +41,12 @@ std = [ "sp-runtime/std", "sp-std/std", ] + +# Serde support without relying on std features. +serde = [ + "dep:serde", + "scale-info/serde", + "sp-application-crypto/serde", + "sp-core/serde", + "sp-runtime/serde", +] diff --git a/primitives/consensus/grandpa/src/lib.rs b/primitives/consensus/grandpa/src/lib.rs index 728ce3092..baeaee473 100644 --- a/primitives/consensus/grandpa/src/lib.rs +++ b/primitives/consensus/grandpa/src/lib.rs @@ -22,7 +22,7 @@ #[cfg(not(feature = "std"))] extern crate alloc; -#[cfg(feature = "std")] +#[cfg(feature = "serde")] use serde::Serialize; use codec::{Codec, Decode, Encode, Input}; @@ -140,8 +140,8 @@ pub struct GrandpaJustification { } /// A scheduled change of authority set. -#[cfg_attr(feature = "std", derive(Serialize))] #[derive(Clone, Eq, PartialEq, Encode, Decode, RuntimeDebug, TypeInfo)] +#[cfg_attr(feature = "serde", derive(Serialize))] pub struct ScheduledChange { /// The new authorities after the change, along with their respective weights. pub next_authorities: AuthorityList, @@ -150,8 +150,8 @@ pub struct ScheduledChange { } /// An consensus log item for GRANDPA. -#[cfg_attr(feature = "std", derive(Serialize))] #[derive(Decode, Encode, PartialEq, Eq, Clone, RuntimeDebug)] +#[cfg_attr(feature = "serde", derive(Serialize))] pub enum ConsensusLog { /// Schedule an authority set change. /// diff --git a/primitives/consensus/slots/Cargo.toml b/primitives/consensus/slots/Cargo.toml index a4d85dff6..29aa3c10c 100644 --- a/primitives/consensus/slots/Cargo.toml +++ b/primitives/consensus/slots/Cargo.toml @@ -15,7 +15,7 @@ targets = ["x86_64-unknown-linux-gnu"] [dependencies] codec = { package = "parity-scale-codec", version = "3.2.2", default-features = false, features = ["derive", "max-encoded-len"] } scale-info = { version = "2.0.0", default-features = false, features = ["derive"] } -serde = { version = "1.0", features = ["derive"], optional = true } +serde = { version = "1.0", default-features = false, features = ["derive", "alloc"], optional = true } sp-std = { version = "5.0.0", default-features = false, path = "../../std" } sp-timestamp = { version = "4.0.0-dev", default-features = false, path = "../../timestamp" } @@ -24,7 +24,13 @@ default = ["std"] std = [ "codec/std", "scale-info/std", - "serde", + "serde/std", "sp-std/std", "sp-timestamp/std", ] + +# Serde support without relying on std features. +serde = [ + "dep:serde", + "scale-info/serde", +] diff --git a/primitives/consensus/slots/src/lib.rs b/primitives/consensus/slots/src/lib.rs index 1f2fa585d..30bb42e2c 100644 --- a/primitives/consensus/slots/src/lib.rs +++ b/primitives/consensus/slots/src/lib.rs @@ -25,7 +25,7 @@ use sp_timestamp::Timestamp; /// Unit type wrapper that represents a slot. #[derive(Debug, Encode, MaxEncodedLen, Decode, Eq, Clone, Copy, Default, Ord, TypeInfo)] -#[cfg_attr(feature = "std", derive(serde::Serialize, serde::Deserialize))] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] pub struct Slot(u64); impl core::ops::Deref for Slot { diff --git a/primitives/core/Cargo.toml b/primitives/core/Cargo.toml index f99ed6c53..d0ec98b1c 100644 --- a/primitives/core/Cargo.toml +++ b/primitives/core/Cargo.toml @@ -16,10 +16,10 @@ targets = ["x86_64-unknown-linux-gnu"] codec = { package = "parity-scale-codec", version = "3.2.2", default-features = false, features = ["derive","max-encoded-len"] } scale-info = { version = "2.5.0", default-features = false, features = ["derive"] } log = { version = "0.4.17", default-features = false } -serde = { version = "1.0.136", optional = true, features = ["derive"] } +serde = { version = "1.0.136", optional = true, default-features = false, features = ["derive", "alloc"] } bounded-collections = { version = "0.1.4", default-features = false } primitive-types = { version = "0.12.0", default-features = false, features = ["codec", "scale-info"] } -impl-serde = { version = "0.4.0", optional = true } +impl-serde = { version = "0.4.0", default-features = false, optional = true } hash-db = { version = "0.16.0", default-features = false } hash256-std-hasher = { version = "0.15.2", default-features = false } bs58 = { version = "0.4.0", default-features = false, optional = true } @@ -31,6 +31,7 @@ zeroize = { version = "1.4.3", default-features = false } secrecy = { version = "0.8.0", default-features = false } lazy_static = { version = "1.4.0", default-features = false, optional = true } parking_lot = { version = "0.12.1", optional = true } +ss58-registry = { version = "1.34.0", default-features = false } sp-std = { version = "5.0.0", default-features = false, path = "../std" } sp-debug-derive = { version = "5.0.0", default-features = false, path = "../debug-derive" } sp-storage = { version = "7.0.0", default-features = false, path = "../storage" } @@ -49,7 +50,6 @@ libsecp256k1 = { version = "0.7", default-features = false, features = ["static- schnorrkel = { version = "0.9.1", features = ["preaudit_deprecated", "u64_backend"], default-features = false } merlin = { version = "2.0", default-features = false } secp256k1 = { version = "0.24.0", default-features = false, features = ["recovery", "alloc"], optional = true } -ss58-registry = { version = "1.34.0", default-features = false } sp-core-hashing = { version = "5.0.0", path = "./hashing", default-features = false, optional = true } sp-runtime-interface = { version = "7.0.0", default-features = false, path = "../runtime-interface" } # bls crypto @@ -83,13 +83,13 @@ std = [ "primitive-types/serde", "primitive-types/byteorder", "primitive-types/rustc-hex", - "impl-serde", + "impl-serde/std", "codec/std", "scale-info/std", "hash256-std-hasher/std", "hash-db/std", "sp-std/std", - "serde", + "serde/std", "blake2/std", "array-bytes", "ed25519-zebra/std", @@ -115,6 +115,20 @@ std = [ "dyn-clonable", ] +# Serde support without relying on std features. +serde = [ + "dep:serde", + "array-bytes", + "blake2", + "bs58/alloc", + "scale-info/serde", + "secrecy/alloc", + "impl-serde", + "primitive-types/serde_no_std", + "sp-storage/serde", + "sp-core-hashing", +] + # This feature enables all crypto primitives for `no_std` builds like microcontrollers # or Intel SGX. # For the regular wasm runtime builds this should not be used. diff --git a/primitives/core/src/crypto.rs b/primitives/core/src/crypto.rs index 27b24057b..5947603cd 100644 --- a/primitives/core/src/crypto.rs +++ b/primitives/core/src/crypto.rs @@ -33,13 +33,16 @@ pub use secrecy::{ExposeSecret, SecretString}; use sp_runtime_interface::pass_by::PassByInner; #[doc(hidden)] pub use sp_std::ops::Deref; +#[cfg(all(not(feature = "std"), feature = "serde"))] +use sp_std::{ + alloc::{format, string::String}, + vec, +}; use sp_std::{hash::Hash, str, vec::Vec}; +pub use ss58_registry::{from_known_address_format, Ss58AddressFormat, Ss58AddressFormatRegistry}; /// Trait to zeroize a memory buffer. pub use zeroize::Zeroize; -#[cfg(feature = "full_crypto")] -pub use ss58_registry::{from_known_address_format, Ss58AddressFormat, Ss58AddressFormatRegistry}; - /// The root phrase for our publicly known keys. pub const DEV_PHRASE: &str = "bottom drive obey lake curtain smoke basket hold race lonely fit walk"; @@ -49,7 +52,6 @@ pub const DEV_ADDRESS: &str = "5DfhGyQdFobKM8NsWvEeAKk5EQQgYe9AydgJ7rMB6E1EqRzV" /// The length of the junction identifier. Note that this is also referred to as the /// `CHAIN_CODE_LENGTH` in the context of Schnorrkel. -#[cfg(feature = "full_crypto")] pub const JUNCTION_ID_LEN: usize = 32; /// Similar to `From`, except that the onus is on the part of the caller to ensure @@ -113,7 +115,7 @@ pub enum DeriveError { /// a new secret key from an existing secret key and, in the case of `SoftRaw` and `SoftIndex` /// a new public key from an existing public key. #[derive(Copy, Clone, Eq, PartialEq, Hash, Debug, Encode, Decode)] -#[cfg(feature = "full_crypto")] +#[cfg(any(feature = "full_crypto", feature = "serde"))] pub enum DeriveJunction { /// Soft (vanilla) derivation. Public keys have a correspondent derivation. Soft([u8; JUNCTION_ID_LEN]), @@ -121,7 +123,7 @@ pub enum DeriveJunction { Hard([u8; JUNCTION_ID_LEN]), } -#[cfg(feature = "full_crypto")] +#[cfg(any(feature = "full_crypto", feature = "serde"))] impl DeriveJunction { /// Consume self to return a soft derive junction with the same chain code. pub fn soften(self) -> Self { @@ -180,7 +182,7 @@ impl DeriveJunction { } } -#[cfg(feature = "full_crypto")] +#[cfg(any(feature = "full_crypto", feature = "serde"))] impl> From for DeriveJunction { fn from(j: T) -> DeriveJunction { let j = j.as_ref(); @@ -208,7 +210,7 @@ impl> From for DeriveJunction { #[cfg_attr(not(feature = "std"), derive(Debug))] #[derive(Clone, Copy, Eq, PartialEq)] #[allow(missing_docs)] -#[cfg(feature = "full_crypto")] +#[cfg(any(feature = "full_crypto", feature = "serde"))] pub enum PublicError { #[cfg_attr(feature = "std", error("Base 58 requirement is violated"))] BadBase58, @@ -247,7 +249,6 @@ impl sp_std::fmt::Debug for PublicError { /// /// See /// for information on the codec. -#[cfg(feature = "full_crypto")] pub trait Ss58Codec: Sized + AsMut<[u8]> + AsRef<[u8]> + ByteArray { /// A format filterer, can be used to ensure that `from_ss58check` family only decode for /// allowed identifiers. By default just refuses the two reserved identifiers. @@ -256,7 +257,7 @@ pub trait Ss58Codec: Sized + AsMut<[u8]> + AsRef<[u8]> + ByteArray { } /// Some if the string is a properly encoded SS58Check address. - #[cfg(feature = "std")] + #[cfg(feature = "serde")] fn from_ss58check(s: &str) -> Result { Self::from_ss58check_with_version(s).and_then(|(r, v)| match v { v if !v.is_custom() => Ok(r), @@ -266,7 +267,7 @@ pub trait Ss58Codec: Sized + AsMut<[u8]> + AsRef<[u8]> + ByteArray { } /// Some if the string is a properly encoded SS58Check address. - #[cfg(feature = "std")] + #[cfg(feature = "serde")] fn from_ss58check_with_version(s: &str) -> Result<(Self, Ss58AddressFormat), PublicError> { const CHECKSUM_LEN: usize = 2; let body_len = Self::LEN; @@ -321,7 +322,7 @@ pub trait Ss58Codec: Sized + AsMut<[u8]> + AsRef<[u8]> + ByteArray { } /// Return the ss58-check string for this key. - #[cfg(feature = "std")] + #[cfg(feature = "serde")] fn to_ss58check_with_version(&self, version: Ss58AddressFormat) -> String { // We mask out the upper two bits of the ident - SS58 Prefix currently only supports 14-bits let ident: u16 = u16::from(version) & 0b0011_1111_1111_1111; @@ -344,7 +345,7 @@ pub trait Ss58Codec: Sized + AsMut<[u8]> + AsRef<[u8]> + ByteArray { } /// Return the ss58-check string for this key. - #[cfg(feature = "std")] + #[cfg(feature = "serde")] fn to_ss58check(&self) -> String { self.to_ss58check_with_version(default_ss58_version()) } @@ -362,16 +363,16 @@ pub trait Derive: Sized { /// Derive a child key from a series of given junctions. /// /// Will be `None` for public keys if there are any hard junctions in there. - #[cfg(feature = "std")] + #[cfg(feature = "serde")] fn derive>(&self, _path: Iter) -> Option { None } } -#[cfg(feature = "std")] +#[cfg(feature = "serde")] const PREFIX: &[u8] = b"SS58PRE"; -#[cfg(feature = "std")] +#[cfg(feature = "serde")] fn ss58hash(data: &[u8]) -> Vec { use blake2::{Blake2b512, Digest}; @@ -382,19 +383,19 @@ fn ss58hash(data: &[u8]) -> Vec { } /// Default prefix number -#[cfg(feature = "std")] -static DEFAULT_VERSION: core::sync::atomic::AtomicU16 = std::sync::atomic::AtomicU16::new( +#[cfg(feature = "serde")] +static DEFAULT_VERSION: core::sync::atomic::AtomicU16 = core::sync::atomic::AtomicU16::new( from_known_address_format(Ss58AddressFormatRegistry::SubstrateAccount), ); /// Returns default SS58 format used by the current active process. -#[cfg(feature = "std")] +#[cfg(feature = "serde")] pub fn default_ss58_version() -> Ss58AddressFormat { - DEFAULT_VERSION.load(std::sync::atomic::Ordering::Relaxed).into() + DEFAULT_VERSION.load(core::sync::atomic::Ordering::Relaxed).into() } /// Returns either the input address format or the default. -#[cfg(feature = "std")] +#[cfg(feature = "serde")] pub fn unwrap_or_default_ss58_version(network: Option) -> Ss58AddressFormat { network.unwrap_or_else(default_ss58_version) } @@ -408,9 +409,9 @@ pub fn unwrap_or_default_ss58_version(network: Option) -> Ss5 /// This will enable the node to decode ss58 addresses with this prefix. /// /// This SS58 version/format is also only used by the node and not by the runtime. -#[cfg(feature = "std")] +#[cfg(feature = "serde")] pub fn set_default_ss58_version(new_default: Ss58AddressFormat) { - DEFAULT_VERSION.store(new_default.into(), std::sync::atomic::Ordering::Relaxed); + DEFAULT_VERSION.store(new_default.into(), core::sync::atomic::Ordering::Relaxed); } #[cfg(feature = "std")] @@ -458,6 +459,11 @@ impl + AsRef<[u8]> + Public + Derive> Ss58Codec for T { } } +// Use the default implementations of the trait in serde feature. +// The std implementation is not available because of std only crate Regex. +#[cfg(all(not(feature = "std"), feature = "serde"))] +impl + AsRef<[u8]> + Public + Derive> Ss58Codec for T {} + /// Trait used for types that are really just a fixed-length array. pub trait ByteArray: AsRef<[u8]> + AsMut<[u8]> + for<'a> TryFrom<&'a [u8], Error = ()> { /// The "length" of the values of this type, which is always the same. @@ -507,7 +513,7 @@ impl ByteArray for AccountId32 { const LEN: usize = 32; } -#[cfg(feature = "std")] +#[cfg(feature = "serde")] impl Ss58Codec for AccountId32 {} impl AsRef<[u8]> for AccountId32 { @@ -591,7 +597,7 @@ impl sp_std::fmt::Debug for AccountId32 { } } -#[cfg(feature = "std")] +#[cfg(feature = "serde")] impl serde::Serialize for AccountId32 { fn serialize(&self, serializer: S) -> Result where @@ -601,7 +607,7 @@ impl serde::Serialize for AccountId32 { } } -#[cfg(feature = "std")] +#[cfg(feature = "serde")] impl<'de> serde::Deserialize<'de> for AccountId32 { fn deserialize(deserializer: D) -> Result where @@ -1063,7 +1069,7 @@ pub trait CryptoType { crate::RuntimeDebug, TypeInfo, )] -#[cfg_attr(feature = "std", derive(serde::Serialize, serde::Deserialize))] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] pub struct KeyTypeId(pub [u8; 4]); impl From for KeyTypeId { @@ -1121,7 +1127,7 @@ pub trait VrfPublic: VrfCrypto { /// An identifier for a specific cryptographic algorithm used by a key pair #[derive(Debug, Copy, Clone, Default, PartialEq, Eq, PartialOrd, Ord, Hash, Encode, Decode)] -#[cfg_attr(feature = "std", derive(serde::Serialize, serde::Deserialize))] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] pub struct CryptoTypeId(pub [u8; 4]); /// Known key types; this also functions as a global registry of key types for projects wishing to diff --git a/primitives/core/src/ecdsa.rs b/primitives/core/src/ecdsa.rs index 2474fa773..0c997bc29 100644 --- a/primitives/core/src/ecdsa.rs +++ b/primitives/core/src/ecdsa.rs @@ -21,7 +21,7 @@ use codec::{Decode, Encode, MaxEncodedLen}; use scale_info::TypeInfo; use sp_runtime_interface::pass_by::PassByInner; -#[cfg(feature = "std")] +#[cfg(feature = "serde")] use crate::crypto::Ss58Codec; use crate::crypto::{ ByteArray, CryptoType, CryptoTypeId, Derive, Public as TraitPublic, UncheckedFrom, @@ -40,8 +40,10 @@ use secp256k1::{ ecdsa::{RecoverableSignature, RecoveryId}, Message, PublicKey, SecretKey, }; -#[cfg(feature = "std")] +#[cfg(feature = "serde")] use serde::{de, Deserialize, Deserializer, Serialize, Serializer}; +#[cfg(all(not(feature = "std"), feature = "serde"))] +use sp_std::alloc::{format, string::String}; #[cfg(feature = "full_crypto")] use sp_std::vec::Vec; @@ -164,7 +166,7 @@ impl sp_std::fmt::Debug for Public { } } -#[cfg(feature = "std")] +#[cfg(feature = "serde")] impl Serialize for Public { fn serialize(&self, serializer: S) -> Result where @@ -174,7 +176,7 @@ impl Serialize for Public { } } -#[cfg(feature = "std")] +#[cfg(feature = "serde")] impl<'de> Deserialize<'de> for Public { fn deserialize(deserializer: D) -> Result where @@ -204,7 +206,7 @@ impl TryFrom<&[u8]> for Signature { } } -#[cfg(feature = "std")] +#[cfg(feature = "serde")] impl Serialize for Signature { fn serialize(&self, serializer: S) -> Result where @@ -214,7 +216,7 @@ impl Serialize for Signature { } } -#[cfg(feature = "std")] +#[cfg(feature = "serde")] impl<'de> Deserialize<'de> for Signature { fn deserialize(deserializer: D) -> Result where diff --git a/primitives/core/src/ed25519.rs b/primitives/core/src/ed25519.rs index d0e6bef97..29da78626 100644 --- a/primitives/core/src/ed25519.rs +++ b/primitives/core/src/ed25519.rs @@ -29,7 +29,7 @@ use crate::{ use codec::{Decode, Encode, MaxEncodedLen}; use scale_info::TypeInfo; -#[cfg(feature = "std")] +#[cfg(feature = "serde")] use crate::crypto::Ss58Codec; use crate::crypto::{CryptoType, CryptoTypeId, Derive, Public as TraitPublic, UncheckedFrom}; #[cfg(feature = "full_crypto")] @@ -38,9 +38,11 @@ use crate::crypto::{DeriveError, DeriveJunction, Pair as TraitPair, SecretString use core::convert::TryFrom; #[cfg(feature = "full_crypto")] use ed25519_zebra::{SigningKey, VerificationKey}; -#[cfg(feature = "std")] +#[cfg(feature = "serde")] use serde::{de, Deserialize, Deserializer, Serialize, Serializer}; use sp_runtime_interface::pass_by::PassByInner; +#[cfg(all(not(feature = "std"), feature = "serde"))] +use sp_std::alloc::{format, string::String}; use sp_std::ops::Deref; /// An identifier used to match public keys against ed25519 keys @@ -176,7 +178,7 @@ impl sp_std::fmt::Debug for Public { } } -#[cfg(feature = "std")] +#[cfg(feature = "serde")] impl Serialize for Public { fn serialize(&self, serializer: S) -> Result where @@ -186,7 +188,7 @@ impl Serialize for Public { } } -#[cfg(feature = "std")] +#[cfg(feature = "serde")] impl<'de> Deserialize<'de> for Public { fn deserialize(deserializer: D) -> Result where @@ -216,7 +218,7 @@ impl TryFrom<&[u8]> for Signature { } } -#[cfg(feature = "std")] +#[cfg(feature = "serde")] impl Serialize for Signature { fn serialize(&self, serializer: S) -> Result where @@ -226,7 +228,7 @@ impl Serialize for Signature { } } -#[cfg(feature = "std")] +#[cfg(feature = "serde")] impl<'de> Deserialize<'de> for Signature { fn deserialize(deserializer: D) -> Result where diff --git a/primitives/core/src/lib.rs b/primitives/core/src/lib.rs index f9541b02e..b61009bc6 100644 --- a/primitives/core/src/lib.rs +++ b/primitives/core/src/lib.rs @@ -34,16 +34,16 @@ macro_rules! map { #[doc(hidden)] pub use codec::{Decode, Encode, MaxEncodedLen}; use scale_info::TypeInfo; -#[cfg(feature = "std")] +#[cfg(feature = "serde")] pub use serde; -#[cfg(feature = "std")] +#[cfg(feature = "serde")] use serde::{Deserialize, Serialize}; use sp_runtime_interface::pass_by::{PassByEnum, PassByInner}; use sp_std::{ops::Deref, prelude::*}; pub use sp_debug_derive::RuntimeDebug; -#[cfg(feature = "std")] +#[cfg(feature = "serde")] pub use impl_serde::serialize as bytes; #[cfg(feature = "full_crypto")] @@ -139,8 +139,8 @@ impl ExecutionContext { /// Hex-serialized shim for `Vec`. #[derive(PartialEq, Eq, Clone, RuntimeDebug)] -#[cfg_attr(feature = "std", derive(Serialize, Deserialize, Hash, PartialOrd, Ord))] -pub struct Bytes(#[cfg_attr(feature = "std", serde(with = "bytes"))] pub Vec); +#[cfg_attr(feature = "serde", derive(Serialize, Deserialize, Hash, PartialOrd, Ord))] +pub struct Bytes(#[cfg_attr(feature = "serde", serde(with = "bytes"))] pub Vec); impl From> for Bytes { fn from(s: Vec) -> Self { @@ -209,7 +209,7 @@ impl sp_std::ops::Deref for OpaqueMetadata { PassByInner, TypeInfo, )] -#[cfg_attr(feature = "std", derive(Serialize, Deserialize))] +#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] pub struct OpaquePeerId(pub Vec); impl OpaquePeerId { @@ -394,6 +394,45 @@ macro_rules! impl_maybe_marker { } } +/// Macro for creating `Maybe*` marker traits. +/// +/// Such a maybe-marker trait requires the given bound when either `feature = std` or `feature = +/// serde` is activated. +/// +/// # Example +/// +/// ``` +/// sp_core::impl_maybe_marker_std_or_serde! { +/// /// A marker for a type that implements `Debug` when `feature = serde` or `feature = std`. +/// trait MaybeDebug: std::fmt::Debug; +/// /// A marker for a type that implements `Debug + Display` when `feature = serde` or `feature = std`. +/// trait MaybeDebugDisplay: std::fmt::Debug, std::fmt::Display; +/// } +/// ``` +#[macro_export] +macro_rules! impl_maybe_marker_std_or_serde { + ( + $( + $(#[$doc:meta] )+ + trait $trait_name:ident: $( $trait_bound:path ),+; + )+ + ) => { + $( + $(#[$doc])+ + #[cfg(any(feature = "serde", feature = "std"))] + pub trait $trait_name: $( $trait_bound + )+ {} + #[cfg(any(feature = "serde", feature = "std"))] + impl $trait_name for T {} + + $(#[$doc])+ + #[cfg(not(any(feature = "serde", feature = "std")))] + pub trait $trait_name {} + #[cfg(not(any(feature = "serde", feature = "std")))] + impl $trait_name for T {} + )+ + } +} + /// The maximum number of bytes that can be allocated at one time. // The maximum possible allocation size was chosen rather arbitrary, 32 MiB should be enough for // everybody. @@ -445,7 +484,7 @@ macro_rules! generate_feature_enabled_macro { } // Work around for: - #[doc(hidden)] + #[doc(hidden)] pub use [<_ $macro_name>] as $macro_name; } }; diff --git a/primitives/core/src/offchain/mod.rs b/primitives/core/src/offchain/mod.rs index a9e663980..a6cef85e6 100644 --- a/primitives/core/src/offchain/mod.rs +++ b/primitives/core/src/offchain/mod.rs @@ -58,7 +58,7 @@ pub trait OffchainStorage: Clone + Send + Sync { /// A type of supported crypto. #[derive(Clone, Copy, PartialEq, Eq, Encode, Decode, RuntimeDebug, PassByEnum)] -#[cfg_attr(feature = "std", derive(serde::Serialize, serde::Deserialize))] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] #[repr(C)] pub enum StorageKind { /// Persistent storage is non-revertible and not fork-aware. It means that any value @@ -208,14 +208,14 @@ impl OpaqueMultiaddr { #[derive( Clone, Copy, PartialEq, Eq, Ord, PartialOrd, Default, RuntimeDebug, PassByInner, Encode, Decode, )] -#[cfg_attr(feature = "std", derive(serde::Serialize, serde::Deserialize))] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] pub struct Timestamp(u64); /// Duration type #[derive( Clone, Copy, PartialEq, Eq, Ord, PartialOrd, Default, RuntimeDebug, PassByInner, Encode, Decode, )] -#[cfg_attr(feature = "std", derive(serde::Serialize, serde::Deserialize))] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] pub struct Duration(u64); impl Duration { diff --git a/primitives/core/src/sr25519.rs b/primitives/core/src/sr25519.rs index fead9e2a0..cfcdd6a9f 100644 --- a/primitives/core/src/sr25519.rs +++ b/primitives/core/src/sr25519.rs @@ -19,15 +19,20 @@ //! //! Note: `CHAIN_CODE_LENGTH` must be equal to `crate::crypto::JUNCTION_ID_LEN` //! for this to work. - -#[cfg(feature = "std")] +#[cfg(any(feature = "full_crypto", feature = "serde"))] +use crate::crypto::DeriveJunction; +#[cfg(feature = "serde")] use crate::crypto::Ss58Codec; #[cfg(feature = "full_crypto")] -use crate::crypto::{DeriveError, DeriveJunction, Pair as TraitPair, SecretStringError}; +use crate::crypto::{DeriveError, Pair as TraitPair, SecretStringError}; #[cfg(feature = "full_crypto")] use schnorrkel::{ - derive::{ChainCode, Derivation, CHAIN_CODE_LENGTH}, - signing_context, ExpansionMode, Keypair, MiniSecretKey, PublicKey, SecretKey, + derive::CHAIN_CODE_LENGTH, signing_context, ExpansionMode, Keypair, MiniSecretKey, SecretKey, +}; +#[cfg(any(feature = "full_crypto", feature = "serde"))] +use schnorrkel::{ + derive::{ChainCode, Derivation}, + PublicKey, }; use sp_std::vec::Vec; @@ -41,9 +46,11 @@ use sp_std::ops::Deref; #[cfg(feature = "full_crypto")] use schnorrkel::keys::{MINI_SECRET_KEY_LENGTH, SECRET_KEY_LENGTH}; -#[cfg(feature = "std")] +#[cfg(feature = "serde")] use serde::{de, Deserialize, Deserializer, Serialize, Serializer}; use sp_runtime_interface::pass_by::PassByInner; +#[cfg(all(not(feature = "std"), feature = "serde"))] +use sp_std::alloc::{format, string::String}; // signing context #[cfg(feature = "full_crypto")] @@ -176,7 +183,7 @@ impl sp_std::fmt::Debug for Public { } } -#[cfg(feature = "std")] +#[cfg(feature = "serde")] impl Serialize for Public { fn serialize(&self, serializer: S) -> Result where @@ -186,7 +193,7 @@ impl Serialize for Public { } } -#[cfg(feature = "std")] +#[cfg(feature = "serde")] impl<'de> Deserialize<'de> for Public { fn deserialize(deserializer: D) -> Result where @@ -216,7 +223,7 @@ impl TryFrom<&[u8]> for Signature { } } -#[cfg(feature = "std")] +#[cfg(feature = "serde")] impl Serialize for Signature { fn serialize(&self, serializer: S) -> Result where @@ -226,7 +233,7 @@ impl Serialize for Signature { } } -#[cfg(feature = "std")] +#[cfg(feature = "serde")] impl<'de> Deserialize<'de> for Signature { fn deserialize(deserializer: D) -> Result where @@ -339,7 +346,7 @@ impl Derive for Public { /// Derive a child key from a series of given junctions. /// /// `None` if there are any hard junctions in there. - #[cfg(feature = "std")] + #[cfg(feature = "serde")] fn derive>(&self, path: Iter) -> Option { let mut acc = PublicKey::from_bytes(self.as_ref()).ok()?; for j in path { diff --git a/primitives/merkle-mountain-range/Cargo.toml b/primitives/merkle-mountain-range/Cargo.toml index cb3d6ddd5..4d2c05089 100644 --- a/primitives/merkle-mountain-range/Cargo.toml +++ b/primitives/merkle-mountain-range/Cargo.toml @@ -16,7 +16,7 @@ codec = { package = "parity-scale-codec", version = "3.2.2", default-features = scale-info = { version = "2.5.0", default-features = false, features = ["derive"] } log = { version = "0.4.17", default-features = false } mmr-lib = { package = "ckb-merkle-mountain-range", version = "0.5.2", default-features = false } -serde = { version = "1.0.136", features = ["derive"], optional = true } +serde = { version = "1.0.136", features = ["derive", "alloc"], default-features = false, optional = true } sp-api = { version = "4.0.0-dev", default-features = false, path = "../api" } sp-core = { version = "7.0.0", default-features = false, path = "../core" } sp-debug-derive = { version = "5.0.0", default-features = false, path = "../debug-derive" } @@ -33,10 +33,18 @@ std = [ "codec/std", "log/std", "mmr-lib/std", - "serde", + "serde/std", "sp-api/std", "sp-core/std", "sp-debug-derive/std", "sp-runtime/std", "sp-std/std", ] + +# Serde support without relying on std features. +serde = [ + "dep:serde", + "scale-info/serde", + "sp-core/serde", + "sp-runtime/serde", +] diff --git a/primitives/merkle-mountain-range/src/lib.rs b/primitives/merkle-mountain-range/src/lib.rs index 436755c03..6c0e75005 100644 --- a/primitives/merkle-mountain-range/src/lib.rs +++ b/primitives/merkle-mountain-range/src/lib.rs @@ -101,13 +101,13 @@ impl FullLeaf /// This type does not implement SCALE encoding/decoding on purpose to avoid confusion, /// it would have to be SCALE-compatible with the concrete leaf type, but due to SCALE limitations /// it's not possible to know how many bytes the encoding of concrete leaf type uses. -#[cfg_attr(feature = "std", derive(serde::Serialize, serde::Deserialize))] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] #[derive(RuntimeDebug, Clone, PartialEq)] pub struct OpaqueLeaf( /// Raw bytes of the leaf type encoded in its compact form. /// /// NOTE it DOES NOT include length prefix (like `Vec` encoding would). - #[cfg_attr(feature = "std", serde(with = "sp_core::bytes"))] + #[cfg_attr(feature = "serde", serde(with = "sp_core::bytes"))] pub Vec, ); diff --git a/primitives/npos-elections/Cargo.toml b/primitives/npos-elections/Cargo.toml index 50a7a5bcd..9006a4d19 100644 --- a/primitives/npos-elections/Cargo.toml +++ b/primitives/npos-elections/Cargo.toml @@ -15,7 +15,7 @@ targets = ["x86_64-unknown-linux-gnu"] [dependencies] codec = { package = "parity-scale-codec", version = "3.2.2", default-features = false, features = ["derive"] } scale-info = { version = "2.5.0", default-features = false, features = ["derive"] } -serde = { version = "1.0.136", features = ["derive"], optional = true } +serde = { version = "1.0.136", default-features = false, features = ["derive", "alloc"], optional = true } sp-arithmetic = { version = "6.0.0", default-features = false, path = "../arithmetic" } sp-core = { version = "7.0.0", default-features = false, path = "../core" } sp-runtime = { version = "7.0.0", default-features = false, path = "../runtime" } @@ -31,9 +31,18 @@ bench = [] std = [ "codec/std", "scale-info/std", - "serde", + "serde/std", "sp-arithmetic/std", "sp-core/std", "sp-runtime/std", "sp-std/std", ] + +# Serde support without relying on std features. +serde = [ + "dep:serde", + "scale-info/serde", + "sp-arithmetic/serde", + "sp-core/serde", + "sp-runtime/serde", +] diff --git a/primitives/npos-elections/src/assignments.rs b/primitives/npos-elections/src/assignments.rs index 9390cd1f4..2ac2b9beb 100644 --- a/primitives/npos-elections/src/assignments.rs +++ b/primitives/npos-elections/src/assignments.rs @@ -18,7 +18,7 @@ //! Structs and helpers for distributing a voter's stake among various winners. use crate::{ExtendedBalance, IdentifierT, PerThing128}; -#[cfg(feature = "std")] +#[cfg(feature = "serde")] use codec::{Decode, Encode}; use sp_arithmetic::{ traits::{Bounded, Zero}, @@ -29,7 +29,7 @@ use sp_std::vec::Vec; /// A voter's stake assignment among a set of targets, represented as ratios. #[derive(RuntimeDebug, Clone, Default)] -#[cfg_attr(feature = "std", derive(PartialEq, Eq, Encode, Decode))] +#[cfg_attr(feature = "serde", derive(PartialEq, Eq, Encode, Decode))] pub struct Assignment { /// Voter's identifier. pub who: AccountId, @@ -97,7 +97,7 @@ impl Assignment { /// A voter's stake assignment among a set of targets, represented as absolute values in the scale /// of [`ExtendedBalance`]. #[derive(RuntimeDebug, Clone, Default)] -#[cfg_attr(feature = "std", derive(PartialEq, Eq, Encode, Decode))] +#[cfg_attr(feature = "serde", derive(PartialEq, Eq, Encode, Decode))] pub struct StakedAssignment { /// Voter's identifier pub who: AccountId, diff --git a/primitives/npos-elections/src/lib.rs b/primitives/npos-elections/src/lib.rs index 716c4b283..253a23160 100644 --- a/primitives/npos-elections/src/lib.rs +++ b/primitives/npos-elections/src/lib.rs @@ -77,7 +77,7 @@ use codec::{Decode, Encode, MaxEncodedLen}; use scale_info::TypeInfo; -#[cfg(feature = "std")] +#[cfg(feature = "serde")] use serde::{Deserialize, Serialize}; use sp_arithmetic::{traits::Zero, Normalizable, PerThing, Rational128, ThresholdOrd}; use sp_core::{bounded::BoundedVec, RuntimeDebug}; @@ -144,7 +144,7 @@ pub type ExtendedBalance = u128; /// 2. `sum_stake`. /// 3. `sum_stake_squared`. #[derive(Clone, Copy, PartialEq, Eq, Encode, Decode, MaxEncodedLen, TypeInfo, Debug, Default)] -#[cfg_attr(feature = "std", derive(Serialize, Deserialize))] +#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] pub struct ElectionScore { /// The minimal winner, in terms of total backing stake. /// @@ -430,7 +430,7 @@ pub struct ElectionResult { /// This, at the current version, resembles the `Exposure` defined in the Staking pallet, yet they /// do not necessarily have to be the same. #[derive(RuntimeDebug, Encode, Decode, Clone, Eq, PartialEq, TypeInfo)] -#[cfg_attr(feature = "std", derive(Serialize, Deserialize))] +#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] pub struct Support { /// Total support. pub total: ExtendedBalance, diff --git a/primitives/runtime/Cargo.toml b/primitives/runtime/Cargo.toml index 4ec55162e..0d67a89c3 100644 --- a/primitives/runtime/Cargo.toml +++ b/primitives/runtime/Cargo.toml @@ -22,7 +22,7 @@ log = { version = "0.4.17", default-features = false } paste = "1.0" rand = { version = "0.8.5", optional = true } scale-info = { version = "2.5.0", default-features = false, features = ["derive"] } -serde = { version = "1.0.136", features = ["derive"], optional = true } +serde = { version = "1.0.136", default-features = false, features = ["derive", "alloc"], optional = true } sp-application-crypto = { version = "7.0.0", default-features = false, path = "../application-crypto" } sp-arithmetic = { version = "6.0.0", default-features = false, path = "../arithmetic" } sp-core = { version = "7.0.0", default-features = false, path = "../core" } @@ -50,7 +50,7 @@ std = [ "log/std", "rand", "scale-info/std", - "serde", + "serde/std", "sp-application-crypto/std", "sp-arithmetic/std", "sp-core/std", @@ -58,3 +58,13 @@ std = [ "sp-std/std", "sp-weights/std", ] + +# Serde support without relying on std features. +serde = [ + "dep:serde", + "scale-info/serde", + "sp-application-crypto/serde", + "sp-arithmetic/serde", + "sp-core/serde", + "sp-weights/serde", +] diff --git a/primitives/runtime/src/generic/block.rs b/primitives/runtime/src/generic/block.rs index 1df747a16..6261e412e 100644 --- a/primitives/runtime/src/generic/block.rs +++ b/primitives/runtime/src/generic/block.rs @@ -20,7 +20,7 @@ #[cfg(feature = "std")] use std::fmt; -#[cfg(feature = "std")] +#[cfg(feature = "serde")] use serde::{Deserialize, Serialize}; use crate::{ @@ -76,9 +76,9 @@ impl fmt::Display for BlockId { /// Abstraction over a substrate block. #[derive(PartialEq, Eq, Clone, Encode, Decode, RuntimeDebug, scale_info::TypeInfo)] -#[cfg_attr(feature = "std", derive(Serialize, Deserialize))] -#[cfg_attr(feature = "std", serde(rename_all = "camelCase"))] -#[cfg_attr(feature = "std", serde(deny_unknown_fields))] +#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] +#[cfg_attr(feature = "serde", serde(rename_all = "camelCase"))] +#[cfg_attr(feature = "serde", serde(deny_unknown_fields))] pub struct Block { /// The block header. pub header: Header, @@ -114,9 +114,9 @@ where /// Abstraction over a substrate block and justification. #[derive(PartialEq, Eq, Clone, Encode, Decode, RuntimeDebug)] -#[cfg_attr(feature = "std", derive(Serialize, Deserialize))] -#[cfg_attr(feature = "std", serde(rename_all = "camelCase"))] -#[cfg_attr(feature = "std", serde(deny_unknown_fields))] +#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] +#[cfg_attr(feature = "serde", serde(rename_all = "camelCase"))] +#[cfg_attr(feature = "serde", serde(deny_unknown_fields))] pub struct SignedBlock { /// Full block. pub block: Block, diff --git a/primitives/runtime/src/generic/digest.rs b/primitives/runtime/src/generic/digest.rs index 143fc2ce5..d7db0f91a 100644 --- a/primitives/runtime/src/generic/digest.rs +++ b/primitives/runtime/src/generic/digest.rs @@ -17,8 +17,10 @@ //! Generic implementation of a digest. -#[cfg(feature = "std")] +#[cfg(feature = "serde")] use serde::{Deserialize, Serialize}; +#[cfg(all(not(feature = "std"), feature = "serde"))] +use sp_std::alloc::format; use sp_std::prelude::*; @@ -34,7 +36,7 @@ use sp_core::RuntimeDebug; /// Generic header digest. #[derive(PartialEq, Eq, Clone, Encode, Decode, RuntimeDebug, TypeInfo, Default)] -#[cfg_attr(feature = "std", derive(Serialize, Deserialize))] +#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] pub struct Digest { /// A list of logs in the digest. pub logs: Vec, @@ -106,7 +108,7 @@ pub enum DigestItem { RuntimeEnvironmentUpdated, } -#[cfg(feature = "std")] +#[cfg(feature = "serde")] impl serde::Serialize for DigestItem { fn serialize(&self, seq: S) -> Result where @@ -116,7 +118,7 @@ impl serde::Serialize for DigestItem { } } -#[cfg(feature = "std")] +#[cfg(feature = "serde")] impl<'a> serde::Deserialize<'a> for DigestItem { fn deserialize(de: D) -> Result where diff --git a/primitives/runtime/src/generic/era.rs b/primitives/runtime/src/generic/era.rs index 79dea7258..bdc7e9930 100644 --- a/primitives/runtime/src/generic/era.rs +++ b/primitives/runtime/src/generic/era.rs @@ -17,7 +17,7 @@ //! Generic implementation of an unchecked (pre-verification) extrinsic. -#[cfg(feature = "std")] +#[cfg(feature = "serde")] use serde::{Deserialize, Serialize}; use crate::codec::{Decode, Encode, Error, Input, Output}; @@ -30,7 +30,7 @@ pub type Phase = u64; /// An era to describe the longevity of a transaction. #[derive(PartialEq, Eq, Clone, Copy, sp_core::RuntimeDebug)] -#[cfg_attr(feature = "std", derive(Serialize, Deserialize))] +#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] pub enum Era { /// The transaction is valid forever. The genesis hash must be present in the signed content. Immortal, diff --git a/primitives/runtime/src/generic/header.rs b/primitives/runtime/src/generic/header.rs index e8b99efd4..7c1faaefb 100644 --- a/primitives/runtime/src/generic/header.rs +++ b/primitives/runtime/src/generic/header.rs @@ -26,22 +26,22 @@ use crate::{ MaybeSerializeDeserialize, Member, SimpleBitOps, }, }; -#[cfg(feature = "std")] +#[cfg(feature = "serde")] use serde::{Deserialize, Serialize}; use sp_core::U256; use sp_std::fmt::Debug; /// Abstraction over a block header for a substrate chain. #[derive(Encode, Decode, PartialEq, Eq, Clone, sp_core::RuntimeDebug, TypeInfo)] -#[cfg_attr(feature = "std", derive(Serialize, Deserialize))] -#[cfg_attr(feature = "std", serde(rename_all = "camelCase"))] -#[cfg_attr(feature = "std", serde(deny_unknown_fields))] +#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] +#[cfg_attr(feature = "serde", serde(rename_all = "camelCase"))] +#[cfg_attr(feature = "serde", serde(deny_unknown_fields))] pub struct Header + TryFrom, Hash: HashT> { /// The parent hash. pub parent_hash: Hash::Output, /// The block number. #[cfg_attr( - feature = "std", + feature = "serde", serde(serialize_with = "serialize_number", deserialize_with = "deserialize_number") )] #[codec(compact)] @@ -54,7 +54,7 @@ pub struct Header + TryFrom, Hash: HashT> { pub digest: Digest, } -#[cfg(feature = "std")] +#[cfg(feature = "serde")] pub fn serialize_number + TryFrom>( val: &T, s: S, @@ -66,7 +66,7 @@ where serde::Serialize::serialize(&u256, s) } -#[cfg(feature = "std")] +#[cfg(feature = "serde")] pub fn deserialize_number<'a, D, T: Copy + Into + TryFrom>(d: D) -> Result where D: serde::Deserializer<'a>, diff --git a/primitives/runtime/src/generic/unchecked_extrinsic.rs b/primitives/runtime/src/generic/unchecked_extrinsic.rs index d147e7b6c..b9d7b9eb1 100644 --- a/primitives/runtime/src/generic/unchecked_extrinsic.rs +++ b/primitives/runtime/src/generic/unchecked_extrinsic.rs @@ -29,6 +29,8 @@ use crate::{ use codec::{Compact, Decode, Encode, EncodeLike, Error, Input}; use scale_info::{build::Fields, meta_type, Path, StaticTypeInfo, Type, TypeInfo, TypeParameter}; use sp_io::hashing::blake2_256; +#[cfg(all(not(feature = "std"), feature = "serde"))] +use sp_std::alloc::format; use sp_std::{fmt, prelude::*}; /// Current version of the [`UncheckedExtrinsic`] encoded format. @@ -317,7 +319,7 @@ where { } -#[cfg(feature = "std")] +#[cfg(feature = "serde")] impl serde::Serialize for UncheckedExtrinsic { @@ -329,7 +331,7 @@ impl s } } -#[cfg(feature = "std")] +#[cfg(feature = "serde")] impl<'a, Address: Decode, Signature: Decode, Call: Decode, Extra: SignedExtension> serde::Deserialize<'a> for UncheckedExtrinsic { diff --git a/primitives/runtime/src/legacy/byte_sized_error.rs b/primitives/runtime/src/legacy/byte_sized_error.rs index b552d6af3..a592c7512 100644 --- a/primitives/runtime/src/legacy/byte_sized_error.rs +++ b/primitives/runtime/src/legacy/byte_sized_error.rs @@ -20,12 +20,12 @@ use crate::{ArithmeticError, TokenError}; use codec::{Decode, Encode}; use scale_info::TypeInfo; -#[cfg(feature = "std")] +#[cfg(feature = "serde")] use serde::{Deserialize, Serialize}; /// [`ModuleError`] type definition before BlockBuilder API version 6. #[derive(Eq, Clone, Copy, Encode, Decode, Debug, TypeInfo)] -#[cfg_attr(feature = "std", derive(Serialize, Deserialize))] +#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] pub struct ModuleError { /// Module index, matching the metadata module index. pub index: u8, @@ -33,7 +33,7 @@ pub struct ModuleError { pub error: u8, /// Optional error message. #[codec(skip)] - #[cfg_attr(feature = "std", serde(skip_deserializing))] + #[cfg_attr(feature = "serde", serde(skip_deserializing))] pub message: Option<&'static str>, } @@ -45,12 +45,12 @@ impl PartialEq for ModuleError { /// [`DispatchError`] type definition before BlockBuilder API version 6. #[derive(Eq, Clone, Copy, Encode, Decode, Debug, TypeInfo, PartialEq)] -#[cfg_attr(feature = "std", derive(Serialize, Deserialize))] +#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] pub enum DispatchError { /// Some error occurred. Other( #[codec(skip)] - #[cfg_attr(feature = "std", serde(skip_deserializing))] + #[cfg_attr(feature = "serde", serde(skip_deserializing))] &'static str, ), /// Failed to lookup some data. diff --git a/primitives/runtime/src/lib.rs b/primitives/runtime/src/lib.rs index 13ef157df..7be5bebf5 100644 --- a/primitives/runtime/src/lib.rs +++ b/primitives/runtime/src/lib.rs @@ -24,7 +24,7 @@ pub use codec; #[doc(hidden)] pub use scale_info; -#[cfg(feature = "std")] +#[cfg(feature = "serde")] #[doc(hidden)] pub use serde; #[doc(hidden)] @@ -52,6 +52,8 @@ use sp_std::prelude::*; use codec::{Decode, Encode, MaxEncodedLen}; use scale_info::TypeInfo; +#[cfg(all(not(feature = "std"), feature = "serde"))] +use sp_std::alloc::format; pub mod curve; pub mod generic; @@ -122,7 +124,7 @@ pub type EncodedJustification = Vec; /// Collection of justifications for a given block, multiple justifications may /// be provided by different consensus engines for the same block. -#[cfg_attr(feature = "std", derive(Serialize, Deserialize))] +#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] #[derive(Debug, Clone, PartialEq, Eq, Encode, Decode)] pub struct Justifications(Vec); @@ -179,7 +181,7 @@ impl From for Justifications { use traits::{Lazy, Verify}; use crate::traits::IdentifyAccount; -#[cfg(feature = "std")] +#[cfg(feature = "serde")] pub use serde::{de::DeserializeOwned, Deserialize, Serialize}; /// Complex storage builder stuff. @@ -235,7 +237,7 @@ impl BuildStorage for () { pub type ConsensusEngineId = [u8; 4]; /// Signature verify that can work with any known signature types. -#[cfg_attr(feature = "std", derive(Serialize, Deserialize))] +#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] #[derive(Eq, PartialEq, Clone, Encode, Decode, MaxEncodedLen, RuntimeDebug, TypeInfo)] pub enum MultiSignature { /// An Ed25519 signature. @@ -299,7 +301,7 @@ impl TryFrom for ecdsa::Signature { /// Public key for any known crypto algorithm. #[derive(Eq, PartialEq, Ord, PartialOrd, Clone, Encode, Decode, RuntimeDebug, TypeInfo)] -#[cfg_attr(feature = "std", derive(Serialize, Deserialize))] +#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] pub enum MultiSigner { /// An Ed25519 identity. Ed25519(ed25519::Public), @@ -427,7 +429,7 @@ impl Verify for MultiSignature { /// Signature verify that can work with any known signature types.. #[derive(Eq, PartialEq, Clone, Default, Encode, Decode, RuntimeDebug)] -#[cfg_attr(feature = "std", derive(Serialize, Deserialize))] +#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] pub struct AnySignature(H512); impl Verify for AnySignature { @@ -475,7 +477,7 @@ pub type DispatchResultWithInfo = sp_std::result::Result, } @@ -495,7 +497,7 @@ impl PartialEq for ModuleError { /// Errors related to transactional storage layers. #[derive(Eq, PartialEq, Clone, Copy, Encode, Decode, Debug, TypeInfo, MaxEncodedLen)] -#[cfg_attr(feature = "std", derive(Serialize, Deserialize))] +#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] pub enum TransactionalError { /// Too many transactional layers have been spawned. LimitReached, @@ -520,12 +522,12 @@ impl From for DispatchError { /// Reason why a dispatch call failed. #[derive(Eq, Clone, Copy, Encode, Decode, Debug, TypeInfo, PartialEq, MaxEncodedLen)] -#[cfg_attr(feature = "std", derive(Serialize, Deserialize))] +#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] pub enum DispatchError { /// Some error occurred. Other( #[codec(skip)] - #[cfg_attr(feature = "std", serde(skip_deserializing))] + #[cfg_attr(feature = "serde", serde(skip_deserializing))] &'static str, ), /// Failed to lookup some data. @@ -605,7 +607,7 @@ impl From for DispatchError { /// Description of what went wrong when trying to complete an operation on a token. #[derive(Eq, PartialEq, Clone, Copy, Encode, Decode, Debug, TypeInfo, MaxEncodedLen)] -#[cfg_attr(feature = "std", derive(Serialize, Deserialize))] +#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] pub enum TokenError { /// Funds are unavailable. FundsUnavailable, @@ -879,7 +881,7 @@ impl sp_std::fmt::Debug for OpaqueExtrinsic { } } -#[cfg(feature = "std")] +#[cfg(feature = "serde")] impl ::serde::Serialize for OpaqueExtrinsic { fn serialize(&self, seq: S) -> Result where @@ -889,7 +891,7 @@ impl ::serde::Serialize for OpaqueExtrinsic { } } -#[cfg(feature = "std")] +#[cfg(feature = "serde")] impl<'a> ::serde::Deserialize<'a> for OpaqueExtrinsic { fn deserialize(de: D) -> Result where diff --git a/primitives/runtime/src/runtime_string.rs b/primitives/runtime/src/runtime_string.rs index f8f183ec7..aa0bd52e5 100644 --- a/primitives/runtime/src/runtime_string.rs +++ b/primitives/runtime/src/runtime_string.rs @@ -129,7 +129,7 @@ impl std::fmt::Display for RuntimeString { } } -#[cfg(feature = "std")] +#[cfg(feature = "serde")] impl serde::Serialize for RuntimeString { fn serialize(&self, serializer: S) -> Result { match self { @@ -139,10 +139,10 @@ impl serde::Serialize for RuntimeString { } } -#[cfg(feature = "std")] +#[cfg(feature = "serde")] impl<'de> serde::Deserialize<'de> for RuntimeString { fn deserialize>(de: D) -> Result { - String::deserialize(de).map(Self::Owned) + Ok(Self::Owned(serde::Deserialize::deserialize(de)?)) } } diff --git a/primitives/runtime/src/traits.rs b/primitives/runtime/src/traits.rs index 95f977077..d37db4802 100644 --- a/primitives/runtime/src/traits.rs +++ b/primitives/runtime/src/traits.rs @@ -28,7 +28,7 @@ use crate::{ DispatchResult, }; use impl_trait_for_tuples::impl_for_tuples; -#[cfg(feature = "std")] +#[cfg(feature = "serde")] use serde::{de::DeserializeOwned, Deserialize, Serialize}; use sp_application_crypto::AppCrypto; pub use sp_arithmetic::traits::{ @@ -716,7 +716,7 @@ pub trait Hash: /// Blake2-256 Hash implementation. #[derive(PartialEq, Eq, Clone, RuntimeDebug, TypeInfo)] -#[cfg_attr(feature = "std", derive(Serialize, Deserialize))] +#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] pub struct BlakeTwo256; impl Hasher for BlakeTwo256 { @@ -743,7 +743,7 @@ impl Hash for BlakeTwo256 { /// Keccak-256 Hash implementation. #[derive(PartialEq, Eq, Clone, RuntimeDebug, TypeInfo)] -#[cfg_attr(feature = "std", derive(Serialize, Deserialize))] +#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] pub struct Keccak256; impl Hasher for Keccak256 { @@ -824,11 +824,13 @@ sp_core::impl_maybe_marker!( /// A type that implements Hash when in std environment. trait MaybeHash: sp_std::hash::Hash; +); - /// A type that implements Serialize when in std environment. +sp_core::impl_maybe_marker_std_or_serde!( + /// A type that implements Serialize when in std environment or serde feature is activated. trait MaybeSerialize: Serialize; - /// A type that implements Serialize, DeserializeOwned and Debug when in std environment. + /// A type that implements Serialize, DeserializeOwned and Debug when in std environment or serde feature is activated. trait MaybeSerializeDeserialize: DeserializeOwned, Serialize; ); diff --git a/primitives/runtime/src/transaction_validity.rs b/primitives/runtime/src/transaction_validity.rs index 072609c66..836948493 100644 --- a/primitives/runtime/src/transaction_validity.rs +++ b/primitives/runtime/src/transaction_validity.rs @@ -36,7 +36,7 @@ pub type TransactionTag = Vec; /// An invalid transaction validity. #[derive(Clone, PartialEq, Eq, Encode, Decode, Copy, RuntimeDebug, TypeInfo)] -#[cfg_attr(feature = "std", derive(serde::Serialize, serde::Deserialize))] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] pub enum InvalidTransaction { /// The call of the transaction is not expected. Call, @@ -119,7 +119,7 @@ impl From for &'static str { /// An unknown transaction validity. #[derive(Clone, PartialEq, Eq, Encode, Decode, Copy, RuntimeDebug, TypeInfo)] -#[cfg_attr(feature = "std", derive(serde::Serialize, serde::Deserialize))] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] pub enum UnknownTransaction { /// Could not lookup some information that is required to validate the transaction. CannotLookup, @@ -143,7 +143,7 @@ impl From for &'static str { /// Errors that can occur while checking the validity of a transaction. #[derive(Clone, PartialEq, Eq, Encode, Decode, Copy, RuntimeDebug, TypeInfo)] -#[cfg_attr(feature = "std", derive(serde::Serialize, serde::Deserialize))] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] pub enum TransactionValidityError { /// The transaction is invalid. Invalid(InvalidTransaction), diff --git a/primitives/storage/Cargo.toml b/primitives/storage/Cargo.toml index 72ebc40fc..c8271669f 100644 --- a/primitives/storage/Cargo.toml +++ b/primitives/storage/Cargo.toml @@ -15,12 +15,24 @@ targets = ["x86_64-unknown-linux-gnu"] [dependencies] codec = { package = "parity-scale-codec", version = "3.2.2", default-features = false, features = ["derive"] } -impl-serde = { version = "0.4.0", optional = true } +impl-serde = { version = "0.4.0", optional = true, default-features = false } ref-cast = "1.0.0" -serde = { version = "1.0.136", features = ["derive"], optional = true } +serde = { version = "1.0.136", default-features = false, features = ["derive", "alloc"], optional = true } sp-debug-derive = { version = "5.0.0", default-features = false, path = "../debug-derive" } sp-std = { version = "5.0.0", default-features = false, path = "../std" } [features] default = [ "std" ] -std = [ "codec/std", "impl-serde", "serde", "sp-debug-derive/std", "sp-std/std" ] +std = [ + "codec/std", + "impl-serde/std", + "serde/std", + "sp-debug-derive/std", + "sp-std/std", +] + +# Serde support without relying on std features. +serde = [ + "dep:serde", + "impl-serde", +] diff --git a/primitives/storage/src/lib.rs b/primitives/storage/src/lib.rs index 032a236a0..aa1bc8e30 100644 --- a/primitives/storage/src/lib.rs +++ b/primitives/storage/src/lib.rs @@ -21,7 +21,7 @@ use core::fmt::Display; -#[cfg(feature = "std")] +#[cfg(feature = "serde")] use serde::{Deserialize, Serialize}; use sp_debug_derive::RuntimeDebug; @@ -35,11 +35,11 @@ use sp_std::{ /// Storage key. #[derive(PartialEq, Eq, RuntimeDebug)] #[cfg_attr( - feature = "std", + feature = "serde", derive(Serialize, Deserialize, Hash, PartialOrd, Ord, Clone, Encode, Decode) )] pub struct StorageKey( - #[cfg_attr(feature = "std", serde(with = "impl_serde::serialize"))] pub Vec, + #[cfg_attr(feature = "serde", serde(with = "impl_serde::serialize"))] pub Vec, ); impl AsRef<[u8]> for StorageKey { @@ -100,11 +100,11 @@ impl From> for TrackedStorageKey { /// Storage key of a child trie, it contains the prefix to the key. #[derive(PartialEq, Eq, RuntimeDebug)] -#[cfg_attr(feature = "std", derive(Serialize, Deserialize, Hash, PartialOrd, Ord, Clone))] +#[cfg_attr(feature = "serde", derive(Serialize, Deserialize, Hash, PartialOrd, Ord, Clone))] #[repr(transparent)] #[derive(RefCast)] pub struct PrefixedStorageKey( - #[cfg_attr(feature = "std", serde(with = "impl_serde::serialize"))] Vec, + #[cfg_attr(feature = "serde", serde(with = "impl_serde::serialize"))] Vec, ); impl Deref for PrefixedStorageKey { @@ -142,11 +142,11 @@ impl PrefixedStorageKey { /// Storage data associated to a [`StorageKey`]. #[derive(PartialEq, Eq, RuntimeDebug)] #[cfg_attr( - feature = "std", + feature = "serde", derive(Serialize, Deserialize, Hash, PartialOrd, Ord, Clone, Encode, Decode, Default) )] pub struct StorageData( - #[cfg_attr(feature = "std", serde(with = "impl_serde::serialize"))] pub Vec, + #[cfg_attr(feature = "serde", serde(with = "impl_serde::serialize"))] pub Vec, ); /// Map of data to use in a storage, it is a collection of @@ -178,8 +178,8 @@ pub struct Storage { /// Storage change set #[derive(RuntimeDebug)] -#[cfg_attr(feature = "std", derive(Serialize, Deserialize, PartialEq, Eq))] -#[cfg_attr(feature = "std", serde(rename_all = "camelCase"))] +#[cfg_attr(feature = "serde", derive(Serialize, Deserialize, PartialEq, Eq))] +#[cfg_attr(feature = "serde", serde(rename_all = "camelCase"))] pub struct StorageChangeSet { /// Block hash pub block: Hash, @@ -245,7 +245,7 @@ pub const TRIE_VALUE_NODE_THRESHOLD: u32 = 33; /// Information related to a child state. #[derive(Debug, Clone)] -#[cfg_attr(feature = "std", derive(PartialEq, Eq, Hash, PartialOrd, Ord, Encode, Decode))] +#[cfg_attr(feature = "serde", derive(PartialEq, Eq, Hash, PartialOrd, Ord, Encode, Decode))] pub enum ChildInfo { /// This is the one used by default. ParentKeyId(ChildTrieParentKeyId), @@ -392,7 +392,7 @@ impl ChildType { /// to be a unique id that will be use only once. Those unique id also required to be long enough to /// avoid any unique id to be prefixed by an other unique id. #[derive(Debug, Clone)] -#[cfg_attr(feature = "std", derive(PartialEq, Eq, Hash, PartialOrd, Ord, Encode, Decode))] +#[cfg_attr(feature = "serde", derive(PartialEq, Eq, Hash, PartialOrd, Ord, Encode, Decode))] pub struct ChildTrieParentKeyId { /// Data is the storage key without prefix. data: Vec, diff --git a/primitives/test-primitives/Cargo.toml b/primitives/test-primitives/Cargo.toml index f6d57ce70..77df69b9d 100644 --- a/primitives/test-primitives/Cargo.toml +++ b/primitives/test-primitives/Cargo.toml @@ -14,10 +14,11 @@ targets = ["x86_64-unknown-linux-gnu"] [dependencies] codec = { package = "parity-scale-codec", version = "3.2.2", default-features = false, features = ["derive"] } scale-info = { version = "2.1.1", default-features = false, features = ["derive"] } -serde = { version = "1.0.136", features = ["derive"], optional = true } +serde = { version = "1.0.136", default-features = false, features = ["derive"], optional = true } sp-application-crypto = { version = "7.0.0", default-features = false, path = "../application-crypto" } sp-core = { version = "7.0.0", default-features = false, path = "../core" } sp-runtime = { version = "7.0.0", default-features = false, path = "../runtime" } +sp-std = { version = "5.0.0", default-features = false, path = "../std" } [features] default = [ @@ -25,9 +26,18 @@ default = [ ] std = [ "codec/std", - "serde", + "serde/std", "sp-application-crypto/std", "sp-core/std", "sp-runtime/std", + "sp-std/std", "scale-info/std", ] + +# Serde support without relying on std features. +serde = [ + "dep:serde", + "sp-application-crypto/serde", + "sp-core/serde", + "sp-runtime/serde", +] diff --git a/primitives/test-primitives/src/lib.rs b/primitives/test-primitives/src/lib.rs index 035acc7a3..913cb762d 100644 --- a/primitives/test-primitives/src/lib.rs +++ b/primitives/test-primitives/src/lib.rs @@ -26,6 +26,7 @@ use sp_application_crypto::sr25519; pub use sp_core::{hash::H256, RuntimeDebug}; use sp_runtime::traits::{BlakeTwo256, Extrinsic as ExtrinsicT, Verify}; +use sp_std::vec::Vec; /// Extrinsic for test-runtime. #[derive(Clone, PartialEq, Eq, Encode, Decode, RuntimeDebug, scale_info::TypeInfo)] @@ -34,7 +35,7 @@ pub enum Extrinsic { StorageChange(Vec, Option>), } -#[cfg(feature = "std")] +#[cfg(feature = "serde")] impl serde::Serialize for Extrinsic { fn serialize(&self, seq: S) -> Result where diff --git a/primitives/version/Cargo.toml b/primitives/version/Cargo.toml index 6b425aa7b..9cebb8f01 100644 --- a/primitives/version/Cargo.toml +++ b/primitives/version/Cargo.toml @@ -15,10 +15,10 @@ targets = ["x86_64-unknown-linux-gnu"] [dependencies] codec = { package = "parity-scale-codec", version = "3.2.2", default-features = false, features = ["derive"] } -impl-serde = { version = "0.4.0", optional = true } +impl-serde = { version = "0.4.0", default-features = false, optional = true } parity-wasm = { version = "0.45", optional = true } scale-info = { version = "2.5.0", default-features = false, features = ["derive"] } -serde = { version = "1.0.136", features = ["derive"], optional = true } +serde = { version = "1.0.136", default-features = false, features = ["derive", "alloc"], optional = true } thiserror = { version = "1.0.30", optional = true } sp-core-hashing-proc-macro = { version = "5.0.0", path = "../core/hashing/proc-macro" } sp-runtime = { version = "7.0.0", default-features = false, path = "../runtime" } @@ -29,11 +29,18 @@ sp-version-proc-macro = { version = "4.0.0-dev", default-features = false, path default = ["std"] std = [ "codec/std", - "impl-serde", + "impl-serde/std", "parity-wasm", "scale-info/std", - "serde", + "serde/std", "sp-runtime/std", "sp-std/std", "thiserror", ] + +# Serde support without relying on std features. +serde = [ + "dep:serde", + "impl-serde", + "sp-runtime/serde", +] diff --git a/primitives/version/src/lib.rs b/primitives/version/src/lib.rs index 214606acc..bd8408bb4 100644 --- a/primitives/version/src/lib.rs +++ b/primitives/version/src/lib.rs @@ -33,7 +33,7 @@ #![cfg_attr(not(feature = "std"), no_std)] -#[cfg(feature = "std")] +#[cfg(feature = "serde")] use serde::{Deserialize, Serialize}; #[cfg(feature = "std")] use std::collections::HashSet; @@ -156,8 +156,8 @@ macro_rules! create_apis_vec { /// `authoring_version`, absolutely not `impl_version` since they change the semantics of the /// runtime. #[derive(Clone, PartialEq, Eq, Encode, Default, sp_runtime::RuntimeDebug, TypeInfo)] -#[cfg_attr(feature = "std", derive(Serialize, Deserialize))] -#[cfg_attr(feature = "std", serde(rename_all = "camelCase"))] +#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] +#[cfg_attr(feature = "serde", serde(rename_all = "camelCase"))] pub struct RuntimeVersion { /// Identifies the different Substrate runtimes. There'll be at least polkadot and node. /// A different on-chain spec_name to that of the native runtime would normally result @@ -190,7 +190,7 @@ pub struct RuntimeVersion { /// List of supported API "features" along with their versions. #[cfg_attr( - feature = "std", + feature = "serde", serde( serialize_with = "apis_serialize::serialize", deserialize_with = "apis_serialize::deserialize", @@ -389,11 +389,12 @@ impl GetNativeVersion for std::sync::Arc { } } -#[cfg(feature = "std")] +#[cfg(feature = "serde")] mod apis_serialize { use super::*; use impl_serde::serialize as bytes; use serde::{de, ser::SerializeTuple, Serializer}; + use sp_std::vec::Vec; #[derive(Serialize)] struct ApiId<'a>(#[serde(serialize_with = "serialize_bytesref")] &'a super::ApiId, &'a u32); @@ -428,7 +429,7 @@ mod apis_serialize { impl<'de> de::Visitor<'de> for Visitor { type Value = ApisVec; - fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { + fn expecting(&self, formatter: &mut sp_std::fmt::Formatter) -> sp_std::fmt::Result { formatter.write_str("a sequence of api id and version tuples") } diff --git a/primitives/weights/Cargo.toml b/primitives/weights/Cargo.toml index af1b5647e..534d79088 100644 --- a/primitives/weights/Cargo.toml +++ b/primitives/weights/Cargo.toml @@ -15,7 +15,7 @@ targets = ["x86_64-unknown-linux-gnu"] [dependencies] codec = { package = "parity-scale-codec", version = "3.2.2", default-features = false, features = ["derive"] } scale-info = { version = "2.5.0", default-features = false, features = ["derive"] } -serde = { version = "1.0.136", optional = true, features = ["derive"] } +serde = { version = "1.0.136", default-features = false, optional = true, features = ["derive", "alloc"] } smallvec = "1.8.0" sp-arithmetic = { version = "6.0.0", default-features = false, path = "../arithmetic" } sp-core = { version = "7.0.0", default-features = false, path = "../core" } @@ -27,7 +27,7 @@ default = [ "std" ] std = [ "codec/std", "scale-info/std", - "serde", + "serde/std", "sp-arithmetic/std", "sp-core/std", "sp-debug-derive/std", @@ -36,3 +36,11 @@ std = [ # By default some types have documentation, `full-metadata-docs` allows to add documentation to # more types in the metadata. full-metadata-docs = ["scale-info/docs"] + +# Serde support without relying on std features. +serde = [ + "dep:serde", + "scale-info/serde", + "sp-arithmetic/serde", + "sp-core/serde", +] diff --git a/primitives/weights/src/lib.rs b/primitives/weights/src/lib.rs index 55d9104b0..36cf864dd 100644 --- a/primitives/weights/src/lib.rs +++ b/primitives/weights/src/lib.rs @@ -29,7 +29,7 @@ mod weight_v2; use codec::{CompactAs, Decode, Encode, MaxEncodedLen}; use scale_info::TypeInfo; -#[cfg(feature = "std")] +#[cfg(feature = "serde")] use serde::{Deserialize, Serialize}; use smallvec::SmallVec; use sp_arithmetic::{ @@ -69,8 +69,8 @@ pub mod constants { MaxEncodedLen, TypeInfo, )] -#[cfg_attr(feature = "std", derive(Serialize, Deserialize))] -#[cfg_attr(feature = "std", serde(transparent))] +#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] +#[cfg_attr(feature = "serde", serde(transparent))] #[deprecated(note = "Will be removed soon; use `Weight` instead.")] pub struct OldWeight(pub u64); diff --git a/primitives/weights/src/weight_v2.rs b/primitives/weights/src/weight_v2.rs index cb1cedf18..2aede666d 100644 --- a/primitives/weights/src/weight_v2.rs +++ b/primitives/weights/src/weight_v2.rs @@ -25,7 +25,7 @@ use super::*; #[derive( Encode, Decode, MaxEncodedLen, TypeInfo, Eq, PartialEq, Copy, Clone, RuntimeDebug, Default, )] -#[cfg_attr(feature = "std", derive(Serialize, Deserialize))] +#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] pub struct Weight { #[codec(compact)] /// The weight of computational time used based on some reference hardware. From f4a2e84ee5974b219f2a03cd195105060c41e3cd Mon Sep 17 00:00:00 2001 From: yjh Date: Thu, 18 May 2023 06:26:33 +0800 Subject: [PATCH 514/558] chore: move whitelist test to better place and add missing `Inactive Issuance` (#14130) * chore: move whitelist test to better palace and add missing `Inactive Issuance` * remove duplicated --------- Co-authored-by: parity-processbot <> --- bin/node-template/runtime/src/lib.rs | 37 ---------------------------- bin/node/runtime/src/lib.rs | 36 --------------------------- frame/balances/src/tests/mod.rs | 17 +++++++++++-- frame/system/src/tests.rs | 24 +++++++++++++++++- 4 files changed, 38 insertions(+), 76 deletions(-) diff --git a/bin/node-template/runtime/src/lib.rs b/bin/node-template/runtime/src/lib.rs index c138b390b..49346fbdd 100644 --- a/bin/node-template/runtime/src/lib.rs +++ b/bin/node-template/runtime/src/lib.rs @@ -573,40 +573,3 @@ impl_runtime_apis! { } } } - -#[cfg(test)] -mod tests { - use super::*; - use frame_support::traits::WhitelistedStorageKeys; - use sp_core::hexdisplay::HexDisplay; - use std::collections::HashSet; - - #[test] - fn check_whitelist() { - let whitelist: HashSet = AllPalletsWithSystem::whitelisted_storage_keys() - .iter() - .map(|e| HexDisplay::from(&e.key).to_string()) - .collect(); - - // Block Number - assert!( - whitelist.contains("26aa394eea5630e07c48ae0c9558cef702a5c1b19ab7a04f536c519aca4983ac") - ); - // Total Issuance - assert!( - whitelist.contains("c2261276cc9d1f8598ea4b6a74b15c2f57c875e4cff74148e4628f264b974c80") - ); - // Execution Phase - assert!( - whitelist.contains("26aa394eea5630e07c48ae0c9558cef7ff553b5a9862a516939d82b3d3d8661a") - ); - // Event Count - assert!( - whitelist.contains("26aa394eea5630e07c48ae0c9558cef70a98fdbe9ce6c55837576c60c7af3850") - ); - // System Events - assert!( - whitelist.contains("26aa394eea5630e07c48ae0c9558cef780d41e5e16056765bc8461851072c9d7") - ); - } -} diff --git a/bin/node/runtime/src/lib.rs b/bin/node/runtime/src/lib.rs index 180e4e247..a2cb64cad 100644 --- a/bin/node/runtime/src/lib.rs +++ b/bin/node/runtime/src/lib.rs @@ -2486,44 +2486,8 @@ impl_runtime_apis! { mod tests { use super::*; use frame_election_provider_support::NposSolution; - use frame_support::traits::WhitelistedStorageKeys; use frame_system::offchain::CreateSignedTransaction; - use sp_core::hexdisplay::HexDisplay; use sp_runtime::UpperOf; - use std::collections::HashSet; - - #[test] - fn check_whitelist() { - let whitelist: HashSet = AllPalletsWithSystem::whitelisted_storage_keys() - .iter() - .map(|e| HexDisplay::from(&e.key).to_string()) - .collect(); - - // Block Number - assert!( - whitelist.contains("26aa394eea5630e07c48ae0c9558cef702a5c1b19ab7a04f536c519aca4983ac") - ); - // Total Issuance - assert!( - whitelist.contains("c2261276cc9d1f8598ea4b6a74b15c2f57c875e4cff74148e4628f264b974c80") - ); - // Execution Phase - assert!( - whitelist.contains("26aa394eea5630e07c48ae0c9558cef7ff553b5a9862a516939d82b3d3d8661a") - ); - // Event Count - assert!( - whitelist.contains("26aa394eea5630e07c48ae0c9558cef70a98fdbe9ce6c55837576c60c7af3850") - ); - // System Events - assert!( - whitelist.contains("26aa394eea5630e07c48ae0c9558cef780d41e5e16056765bc8461851072c9d7") - ); - // System BlockWeight - assert!( - whitelist.contains("26aa394eea5630e07c48ae0c9558cef734abf5cb34d6244378cddbf18e849d96") - ); - } #[test] fn validate_transaction_submitter_bounds() { diff --git a/frame/balances/src/tests/mod.rs b/frame/balances/src/tests/mod.rs index 259051ed4..4731dbf8e 100644 --- a/frame/balances/src/tests/mod.rs +++ b/frame/balances/src/tests/mod.rs @@ -27,7 +27,7 @@ use frame_support::{ parameter_types, traits::{ tokens::fungible, ConstU32, ConstU64, ConstU8, Imbalance as ImbalanceT, OnUnbalanced, - StorageMapShim, StoredMap, + StorageMapShim, StoredMap, WhitelistedStorageKeys, }, weights::{IdentityFee, Weight}, RuntimeDebug, @@ -35,13 +35,14 @@ use frame_support::{ use frame_system::{self as system, RawOrigin}; use pallet_transaction_payment::{ChargeTransactionPayment, CurrencyAdapter, Multiplier}; use scale_info::TypeInfo; -use sp_core::H256; +use sp_core::{hexdisplay::HexDisplay, H256}; use sp_io; use sp_runtime::{ testing::Header, traits::{BadOrigin, IdentityLookup, SignedExtension, Zero}, ArithmeticError, DispatchError, DispatchResult, FixedPointNumber, TokenError, }; +use std::collections::BTreeSet; mod currency_tests; mod dispatchable_tests; @@ -304,3 +305,15 @@ fn weights_sane() { let info = crate::Call::::force_unreserve { who: 10, amount: 4 }.get_dispatch_info(); assert_eq!(<() as crate::WeightInfo>::force_unreserve(), info.weight); } + +#[test] +fn check_whitelist() { + let whitelist: BTreeSet = AllPalletsWithSystem::whitelisted_storage_keys() + .iter() + .map(|s| HexDisplay::from(&s.key).to_string()) + .collect(); + // Inactive Issuance + assert!(whitelist.contains("c2261276cc9d1f8598ea4b6a74b15c2f1ccde6872881f893a21de93dfe970cd5")); + // Total Issuance + assert!(whitelist.contains("c2261276cc9d1f8598ea4b6a74b15c2f57c875e4cff74148e4628f264b974c80")); +} diff --git a/frame/system/src/tests.rs b/frame/system/src/tests.rs index 04c178246..05a7e96fd 100644 --- a/frame/system/src/tests.rs +++ b/frame/system/src/tests.rs @@ -19,14 +19,36 @@ use crate::*; use frame_support::{ assert_noop, assert_ok, dispatch::{Pays, PostDispatchInfo, WithPostDispatchInfo}, + traits::WhitelistedStorageKeys, }; +use std::collections::BTreeSet; + use mock::{RuntimeOrigin, *}; -use sp_core::H256; +use sp_core::{hexdisplay::HexDisplay, H256}; use sp_runtime::{ traits::{BlakeTwo256, Header}, DispatchError, DispatchErrorWithPostInfo, }; +#[test] +fn check_whitelist() { + let whitelist: BTreeSet = AllPalletsWithSystem::whitelisted_storage_keys() + .iter() + .map(|s| HexDisplay::from(&s.key).to_string()) + .collect(); + + // Block Number + assert!(whitelist.contains("26aa394eea5630e07c48ae0c9558cef702a5c1b19ab7a04f536c519aca4983ac")); + // Execution Phase + assert!(whitelist.contains("26aa394eea5630e07c48ae0c9558cef7ff553b5a9862a516939d82b3d3d8661a")); + // Event Count + assert!(whitelist.contains("26aa394eea5630e07c48ae0c9558cef70a98fdbe9ce6c55837576c60c7af3850")); + // System Events + assert!(whitelist.contains("26aa394eea5630e07c48ae0c9558cef780d41e5e16056765bc8461851072c9d7")); + // System BlockWeight + assert!(whitelist.contains("26aa394eea5630e07c48ae0c9558cef734abf5cb34d6244378cddbf18e849d96")); +} + #[test] fn origin_works() { let o = RuntimeOrigin::from(RawOrigin::::Signed(1u64)); From bc89da26a8c4dd0793615d5ce86a26b0cd1db5c3 Mon Sep 17 00:00:00 2001 From: Michal Kucharczyk <1728078+michalkucharczyk@users.noreply.github.com> Date: Thu, 18 May 2023 13:36:48 +0200 Subject: [PATCH 515/558] Added serde feature to sp-consensus-aura (#14166) This is followup of #13027. `Aura` need to enable `serde` feature in dependent crates, otherwise test-substrate-runtime compilation fails with the following error if `serde` is enabled: ``` error: cannot find macro `format` in this scope --> /home/miszka/parity/10-genesis-config/substrate-master/primitives/consensus/aura/src/lib.rs:50:3 | 50 | app_crypto!(ed25519, AURA); | ^^^^^^^^^^^^^^^^^^^^^^^^^^ | = help: consider importing one of these items: scale_info::prelude::format sp_application_crypto::format = note: this error originates in the macro `$crate::app_crypto_public_common_if_serde` which comes from the expansion of the macro `app_crypto` (in Nightly builds, run with -Z macro-backtrace for more info) ``` --- primitives/consensus/aura/Cargo.toml | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/primitives/consensus/aura/Cargo.toml b/primitives/consensus/aura/Cargo.toml index 94a031a54..57689aaa1 100644 --- a/primitives/consensus/aura/Cargo.toml +++ b/primitives/consensus/aura/Cargo.toml @@ -40,3 +40,11 @@ std = [ "sp-std/std", "sp-timestamp/std", ] + +# Serde support without relying on std features. +serde = [ + "scale-info/serde", + "sp-application-crypto/serde", + "sp-consensus-slots/serde", + "sp-runtime/serde", +] From 7c06185f13a4b91aa9c5dd2db594b6650f510ca8 Mon Sep 17 00:00:00 2001 From: gupnik <17176722+gupnik@users.noreply.github.com> Date: Fri, 19 May 2023 18:54:32 +0530 Subject: [PATCH 516/558] Adds ability to use default hasher in `dev_mode` for explicit key binding (#14164) * Initial setup * Minor update * Minor update * Addresses review comments * Addresses review comments * Updates doc * ".git/.scripts/commands/fmt/fmt.sh" * Renames file * Updates path in test --------- Co-authored-by: command-bot <> --- frame/support/procedural/src/lib.rs | 3 +- .../procedural/src/pallet/parse/storage.rs | 38 +++++++++++++------ ...storage_map_explicit_key_default_hasher.rs | 33 ++++++++++++++++ ...age_map_explicit_key_default_hasher.stderr | 11 ++++++ .../tests/pallet_ui/pass/dev_mode_valid.rs | 9 +++++ 5 files changed, 82 insertions(+), 12 deletions(-) create mode 100644 frame/support/test/tests/pallet_ui/non_dev_mode_storage_map_explicit_key_default_hasher.rs create mode 100644 frame/support/test/tests/pallet_ui/non_dev_mode_storage_map_explicit_key_default_hasher.stderr diff --git a/frame/support/procedural/src/lib.rs b/frame/support/procedural/src/lib.rs index b8aa5674d..25df8410b 100644 --- a/frame/support/procedural/src/lib.rs +++ b/frame/support/procedural/src/lib.rs @@ -468,7 +468,8 @@ pub fn construct_runtime(input: TokenStream) -> TokenStream { /// storage types. This is equivalent to specifying `#[pallet::unbounded]` on all storage type /// definitions. /// * Storage hashers no longer need to be specified and can be replaced by `_`. In dev mode, these -/// will be replaced by `Blake2_128Concat`. +/// will be replaced by `Blake2_128Concat`. In case of explicit key-binding, `Hasher` can simply +/// be ignored when in `dev_mode`. /// /// Note that the `dev_mode` argument can only be supplied to the `#[pallet]` or /// `#[frame_support::pallet]` attribute macro that encloses your pallet module. This argument diff --git a/frame/support/procedural/src/pallet/parse/storage.rs b/frame/support/procedural/src/pallet/parse/storage.rs index 16c0851ce..12e06b214 100644 --- a/frame/support/procedural/src/pallet/parse/storage.rs +++ b/frame/support/procedural/src/pallet/parse/storage.rs @@ -332,6 +332,7 @@ fn process_named_generics( storage: &StorageKind, args_span: proc_macro2::Span, args: &[syn::AssocType], + dev_mode: bool, ) -> syn::Result<(Option, Metadata, Option, bool)> { let mut parsed = HashMap::::new(); @@ -346,6 +347,14 @@ fn process_named_generics( parsed.insert(arg.ident.to_string(), arg.clone()); } + let mut map_mandatory_generics = vec!["Key", "Value"]; + let mut map_optional_generics = vec!["QueryKind", "OnEmpty", "MaxValues"]; + if dev_mode { + map_optional_generics.push("Hasher"); + } else { + map_mandatory_generics.push("Hasher"); + } + let generics = match storage { StorageKind::Value => { check_generics( @@ -368,8 +377,8 @@ fn process_named_generics( StorageKind::Map => { check_generics( &parsed, - &["Hasher", "Key", "Value"], - &["QueryKind", "OnEmpty", "MaxValues"], + &map_mandatory_generics, + &map_optional_generics, "StorageMap", args_span, )?; @@ -378,7 +387,7 @@ fn process_named_generics( hasher: parsed .remove("Hasher") .map(|binding| binding.ty) - .expect("checked above as mandatory generic"), + .unwrap_or(syn::parse_quote!(Blake2_128Concat)), key: parsed .remove("Key") .map(|binding| binding.ty) @@ -395,8 +404,8 @@ fn process_named_generics( StorageKind::CountedMap => { check_generics( &parsed, - &["Hasher", "Key", "Value"], - &["QueryKind", "OnEmpty", "MaxValues"], + &map_mandatory_generics, + &map_optional_generics, "CountedStorageMap", args_span, )?; @@ -405,7 +414,7 @@ fn process_named_generics( hasher: parsed .remove("Hasher") .map(|binding| binding.ty) - .expect("checked above as mandatory generic"), + .unwrap_or(syn::Type::Verbatim(quote::quote! { Blake2_128Concat })), key: parsed .remove("Key") .map(|binding| binding.ty) @@ -420,10 +429,17 @@ fn process_named_generics( } }, StorageKind::DoubleMap => { + let mut double_map_mandatory_generics = vec!["Key1", "Key2", "Value"]; + if dev_mode { + map_optional_generics.extend(["Hasher1", "Hasher2"]); + } else { + double_map_mandatory_generics.extend(["Hasher1", "Hasher2"]); + } + check_generics( &parsed, - &["Hasher1", "Key1", "Hasher2", "Key2", "Value"], - &["QueryKind", "OnEmpty", "MaxValues"], + &double_map_mandatory_generics, + &map_optional_generics, "StorageDoubleMap", args_span, )?; @@ -432,7 +448,7 @@ fn process_named_generics( hasher1: parsed .remove("Hasher1") .map(|binding| binding.ty) - .expect("checked above as mandatory generic"), + .unwrap_or(syn::parse_quote!(Blake2_128Concat)), key1: parsed .remove("Key1") .map(|binding| binding.ty) @@ -440,7 +456,7 @@ fn process_named_generics( hasher2: parsed .remove("Hasher2") .map(|binding| binding.ty) - .expect("checked above as mandatory generic"), + .unwrap_or(syn::parse_quote!(Blake2_128Concat)), key2: parsed .remove("Key2") .map(|binding| binding.ty) @@ -619,7 +635,7 @@ fn process_generics( _ => unreachable!("It is asserted above that all generics are bindings"), }) .collect::>(); - process_named_generics(&storage_kind, args_span, &args) + process_named_generics(&storage_kind, args_span, &args, dev_mode) } else { let msg = "Invalid pallet::storage, invalid generic declaration for storage. Expect only \ type generics or binding generics, e.g. `` or \ diff --git a/frame/support/test/tests/pallet_ui/non_dev_mode_storage_map_explicit_key_default_hasher.rs b/frame/support/test/tests/pallet_ui/non_dev_mode_storage_map_explicit_key_default_hasher.rs new file mode 100644 index 000000000..7d8be8ec0 --- /dev/null +++ b/frame/support/test/tests/pallet_ui/non_dev_mode_storage_map_explicit_key_default_hasher.rs @@ -0,0 +1,33 @@ +#![cfg_attr(not(feature = "std"), no_std)] + +pub use pallet::*; + +#[frame_support::pallet] +pub mod pallet { + use frame_support::pallet_prelude::*; + + // The struct on which we build all of our Pallet logic. + #[pallet::pallet] + pub struct Pallet(_); + + // Your Pallet's configuration trait, representing custom external types and interfaces. + #[pallet::config] + pub trait Config: frame_system::Config {} + + #[pallet::storage] + type MyStorage = StorageValue<_, Vec>; + + #[pallet::storage] + type MyStorageMap = StorageMap; + + #[pallet::storage] + type MyStorageDoubleMap = StorageDoubleMap; + + #[pallet::storage] + type MyCountedStorageMap = CountedStorageMap; + + // Your Pallet's internal functions. + impl Pallet {} +} + +fn main() {} diff --git a/frame/support/test/tests/pallet_ui/non_dev_mode_storage_map_explicit_key_default_hasher.stderr b/frame/support/test/tests/pallet_ui/non_dev_mode_storage_map_explicit_key_default_hasher.stderr new file mode 100644 index 000000000..68751470a --- /dev/null +++ b/frame/support/test/tests/pallet_ui/non_dev_mode_storage_map_explicit_key_default_hasher.stderr @@ -0,0 +1,11 @@ +error: Invalid pallet::storage, cannot find `Hasher` generic, required for `StorageMap`. + --> tests/pallet_ui/non_dev_mode_storage_map_explicit_key_default_hasher.rs:21:43 + | +21 | type MyStorageMap = StorageMap; + | ^ + +error[E0432]: unresolved import `pallet` + --> tests/pallet_ui/non_dev_mode_storage_map_explicit_key_default_hasher.rs:3:9 + | +3 | pub use pallet::*; + | ^^^^^^ help: a similar path exists: `test_pallet::pallet` diff --git a/frame/support/test/tests/pallet_ui/pass/dev_mode_valid.rs b/frame/support/test/tests/pallet_ui/pass/dev_mode_valid.rs index 483ed9579..28b901213 100644 --- a/frame/support/test/tests/pallet_ui/pass/dev_mode_valid.rs +++ b/frame/support/test/tests/pallet_ui/pass/dev_mode_valid.rs @@ -35,6 +35,15 @@ pub mod pallet { #[pallet::storage] type MyCountedStorageMap = CountedStorageMap<_, _, u32, u64>; + #[pallet::storage] + pub type MyStorageMap2 = StorageMap; + + #[pallet::storage] + type MyStorageDoubleMap2 = StorageDoubleMap; + + #[pallet::storage] + type MyCountedStorageMap2 = CountedStorageMap; + // Your Pallet's callable functions. #[pallet::call] impl Pallet { From e736a97e34d64aeba3eb69e01ad84d3ede98c858 Mon Sep 17 00:00:00 2001 From: Chris Kerr <120631257+piffle-rack@users.noreply.github.com> Date: Fri, 19 May 2023 16:45:11 +0300 Subject: [PATCH 517/558] Fix typos (#14177) --- bin/utils/subkey/README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/bin/utils/subkey/README.md b/bin/utils/subkey/README.md index 768f1335c..ddef20f61 100644 --- a/bin/utils/subkey/README.md +++ b/bin/utils/subkey/README.md @@ -6,11 +6,11 @@ Subkey is a commandline utility included with Substrate. It allows generating an You can see the full list of commands with `subkey --help`. Most commands have additional help available with for instance `subkey generate --help` for the `generate` command. -## Satefy first +## Safety first `subkey` does not need an internet connection to work. Indeed, for the best security, you should be using `subkey` on a machine that is **not connected** to the internet. -`subkey` deals with **seeds** and **private keys**. Make sure to use `subkey` in a safe environment (ie. no one looking over your shoulder) and on a safe computer (ie. no one able to check you commands history). +`subkey` deals with **seeds** and **private keys**. Make sure to use `subkey` in a safe environment (ie. no one looking over your shoulder) and on a safe computer (ie. no one able to check your command history). If you save any output of `subkey` into a file, make sure to apply proper permissions and/or delete the file as soon as possible. From b938a722f94b8ca7cbcef045379e69fa6962771c Mon Sep 17 00:00:00 2001 From: Muharem Ismailov Date: Fri, 19 May 2023 18:16:58 +0200 Subject: [PATCH 518/558] Core-Fellowship: fix origin typo (#14179) --- frame/core-fellowship/src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frame/core-fellowship/src/lib.rs b/frame/core-fellowship/src/lib.rs index 93d230244..97603fcc6 100644 --- a/frame/core-fellowship/src/lib.rs +++ b/frame/core-fellowship/src/lib.rs @@ -368,7 +368,7 @@ pub mod pallet { who: T::AccountId, at_rank: RankOf, ) -> DispatchResult { - match T::PromoteOrigin::try_origin(origin) { + match T::ApproveOrigin::try_origin(origin) { Ok(allow_rank) => ensure!(allow_rank >= at_rank, Error::::NoPermission), Err(origin) => ensure_root(origin)?, } From 2c3b923423fac829b02842fbb9a0016b55c417df Mon Sep 17 00:00:00 2001 From: Liam Aharon Date: Sat, 20 May 2023 05:26:52 +1000 Subject: [PATCH 519/558] remote-externalities: retry for get_keys (#14167) * retry get keys request * remove redundant clone * remove redundant at --- Cargo.lock | 14 ++++++- utils/frame/remote-externalities/Cargo.toml | 1 + utils/frame/remote-externalities/src/lib.rs | 44 +++++++++++++-------- 3 files changed, 42 insertions(+), 17 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 62e99d437..b29ed95b8 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2672,6 +2672,7 @@ dependencies = [ "spinners", "substrate-rpc-client", "tokio", + "tokio-retry", "tracing-subscriber 0.3.17", ] @@ -12187,6 +12188,17 @@ dependencies = [ "syn 2.0.15", ] +[[package]] +name = "tokio-retry" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f57eb36ecbe0fc510036adff84824dd3c24bb781e21bfa67b69d556aa85214f" +dependencies = [ + "pin-project", + "rand 0.8.5", + "tokio", +] + [[package]] name = "tokio-rustls" version = "0.23.4" @@ -12617,7 +12629,7 @@ checksum = "97fee6b57c6a41524a810daee9286c02d7752c4253064d0b05472833a438f675" dependencies = [ "cfg-if", "digest 0.10.6", - "rand 0.7.3", + "rand 0.8.5", "static_assertions", ] diff --git a/utils/frame/remote-externalities/Cargo.toml b/utils/frame/remote-externalities/Cargo.toml index 21b652014..b723c9bab 100644 --- a/utils/frame/remote-externalities/Cargo.toml +++ b/utils/frame/remote-externalities/Cargo.toml @@ -26,6 +26,7 @@ futures = "0.3" async-recursion = "1.0.4" indicatif = "0.17.3" spinners = "4.1.0" +tokio-retry = "0.3.0" [dev-dependencies] frame-support = { version = "4.0.0-dev", path = "../../../frame/support" } diff --git a/utils/frame/remote-externalities/src/lib.rs b/utils/frame/remote-externalities/src/lib.rs index d4ddacbcb..9a64f4ffd 100644 --- a/utils/frame/remote-externalities/src/lib.rs +++ b/utils/frame/remote-externalities/src/lib.rs @@ -49,6 +49,7 @@ use std::{ time::{Duration, Instant}, }; use substrate_rpc_client::{rpc_params, BatchRequestBuilder, ChainApi, ClientT, StateApi}; +use tokio_retry::{strategy::FixedInterval, Retry}; type KeyValue = (StorageKey, StorageData); type TopKeyValues = Vec; @@ -317,8 +318,10 @@ where const BATCH_SIZE_INCREASE_FACTOR: f32 = 1.10; const BATCH_SIZE_DECREASE_FACTOR: f32 = 0.50; const INITIAL_BATCH_SIZE: usize = 5000; - // NOTE: increasing this value does not seem to impact speed all that much. + // nodes by default will not return more than 1000 keys per request const DEFAULT_KEY_DOWNLOAD_PAGE: u32 = 1000; + const KEYS_PAGE_MAX_RETRIES: usize = 12; + const KEYS_PAGE_RETRY_INTERVAL: Duration = Duration::from_secs(5); async fn rpc_get_storage( &self, @@ -345,6 +348,22 @@ where }) } + async fn get_keys_single_page( + &self, + prefix: Option, + start_key: Option, + at: B::Hash, + ) -> Result, &'static str> { + self.as_online() + .rpc_client() + .storage_keys_paged(prefix, Self::DEFAULT_KEY_DOWNLOAD_PAGE, start_key, Some(at)) + .await + .map_err(|e| { + error!(target: LOG_TARGET, "Error = {:?}", e); + "rpc get_keys failed" + }) + } + /// Get all the keys at `prefix` at `hash` using the paged, safe RPC methods. async fn rpc_get_keys_paged( &self, @@ -354,20 +373,13 @@ where let mut last_key: Option = None; let mut all_keys: Vec = vec![]; let keys = loop { - let page = self - .as_online() - .rpc_client() - .storage_keys_paged( - Some(prefix.clone()), - Self::DEFAULT_KEY_DOWNLOAD_PAGE, - last_key.clone(), - Some(at), - ) - .await - .map_err(|e| { - error!(target: LOG_TARGET, "Error = {:?}", e); - "rpc get_keys failed" - })?; + // This loop can hit the node with very rapid requests, occasionally causing it to + // error out in CI (https://github.com/paritytech/substrate/issues/14129), so we retry. + let retry_strategy = FixedInterval::new(Self::KEYS_PAGE_RETRY_INTERVAL) + .take(Self::KEYS_PAGE_MAX_RETRIES); + let get_page_closure = + || self.get_keys_single_page(Some(prefix.clone()), last_key.clone(), at); + let page = Retry::spawn(retry_strategy, get_page_closure).await?; let page_len = page.len(); all_keys.extend(page); @@ -385,7 +397,7 @@ where HexDisplay::from(new_last_key) ); last_key = Some(new_last_key.clone()); - } + }; }; Ok(keys) From 0046337664b221ff1072fb8f872f13a170babca9 Mon Sep 17 00:00:00 2001 From: Michal Kucharczyk <1728078+michalkucharczyk@users.noreply.github.com> Date: Sat, 20 May 2023 09:34:23 +0200 Subject: [PATCH 520/558] frame: Enable GenesisConfig in no_std (#14108) * frame: Default for GenesisConfig in no_std `Default` for `GenesisConfig` will be required for no_std in no native runtime world. It must be possible to instantiate default GenesisConfig for pallets and runtime. * ".git/.scripts/commands/fmt/fmt.sh" * hash69 in no_std reverted * derive(DefaultNoBound) for GenesisConfig used when possible * treasury: derive(Default) * Cargo.lock update * genesis_config: compiler error improved When std feature is not enabled for pallet, the GenesisConfig will be defined, but serde::{Serialize,Deserialize} traits will not be implemented. The compiler error indicates the reason of latter errors. This is temporary and serde traits will be enabled with together with `serde` support in frame. --------- Co-authored-by: command-bot <> --- frame/alliance/src/lib.rs | 8 +- frame/assets/src/lib.rs | 12 +- frame/aura/src/lib.rs | 8 +- frame/authority-discovery/src/lib.rs | 2 +- frame/babe/src/lib.rs | 2 +- frame/balances/src/lib.rs | 3 +- frame/beefy/src/lib.rs | 1 - frame/collective/src/lib.rs | 8 +- frame/democracy/src/lib.rs | 8 +- frame/elections-phragmen/src/lib.rs | 8 +- frame/examples/basic/src/lib.rs | 9 +- frame/grandpa/src/lib.rs | 2 +- frame/im-online/src/lib.rs | 8 +- frame/indices/src/lib.rs | 8 +- frame/membership/src/lib.rs | 8 +- frame/node-authorization/src/lib.rs | 8 +- frame/nomination-pools/src/lib.rs | 1 - frame/scored-pool/src/lib.rs | 8 +- frame/session/src/lib.rs | 8 +- frame/society/src/lib.rs | 12 +- frame/staking/src/pallet/mod.rs | 20 +--- frame/sudo/src/lib.rs | 8 +- .../src/pallet/expand/genesis_config.rs | 25 ++-- frame/support/test/pallet/src/lib.rs | 2 +- .../no_std_genesis_config.stderr | 109 +++++++++++++++++- .../undefined_genesis_config_part.stderr | 4 +- frame/system/src/lib.rs | 4 +- frame/transaction-payment/src/lib.rs | 1 - frame/transaction-storage/src/lib.rs | 1 - frame/treasury/src/lib.rs | 8 +- frame/vesting/src/lib.rs | 8 +- 31 files changed, 149 insertions(+), 173 deletions(-) diff --git a/frame/alliance/src/lib.rs b/frame/alliance/src/lib.rs index 86a64caaf..6c034454c 100644 --- a/frame/alliance/src/lib.rs +++ b/frame/alliance/src/lib.rs @@ -401,19 +401,13 @@ pub mod pallet { } #[pallet::genesis_config] + #[derive(frame_support::DefaultNoBound)] pub struct GenesisConfig, I: 'static = ()> { pub fellows: Vec, pub allies: Vec, pub phantom: PhantomData<(T, I)>, } - #[cfg(feature = "std")] - impl, I: 'static> Default for GenesisConfig { - fn default() -> Self { - Self { fellows: Vec::new(), allies: Vec::new(), phantom: Default::default() } - } - } - #[pallet::genesis_build] impl, I: 'static> GenesisBuild for GenesisConfig { fn build(&self) { diff --git a/frame/assets/src/lib.rs b/frame/assets/src/lib.rs index 33aacd932..e9259f4b6 100644 --- a/frame/assets/src/lib.rs +++ b/frame/assets/src/lib.rs @@ -369,6 +369,7 @@ pub mod pallet { >; #[pallet::genesis_config] + #[derive(frame_support::DefaultNoBound)] pub struct GenesisConfig, I: 'static = ()> { /// Genesis assets: id, owner, is_sufficient, min_balance pub assets: Vec<(T::AssetId, T::AccountId, bool, T::Balance)>, @@ -378,17 +379,6 @@ pub mod pallet { pub accounts: Vec<(T::AssetId, T::AccountId, T::Balance)>, } - #[cfg(feature = "std")] - impl, I: 'static> Default for GenesisConfig { - fn default() -> Self { - Self { - assets: Default::default(), - metadata: Default::default(), - accounts: Default::default(), - } - } - } - #[pallet::genesis_build] impl, I: 'static> GenesisBuild for GenesisConfig { fn build(&self) { diff --git a/frame/aura/src/lib.rs b/frame/aura/src/lib.rs index 108b9303b..12b7ee3f5 100644 --- a/frame/aura/src/lib.rs +++ b/frame/aura/src/lib.rs @@ -129,17 +129,11 @@ pub mod pallet { pub(super) type CurrentSlot = StorageValue<_, Slot, ValueQuery>; #[pallet::genesis_config] + #[derive(frame_support::DefaultNoBound)] pub struct GenesisConfig { pub authorities: Vec, } - #[cfg(feature = "std")] - impl Default for GenesisConfig { - fn default() -> Self { - Self { authorities: Vec::new() } - } - } - #[pallet::genesis_build] impl GenesisBuild for GenesisConfig { fn build(&self) { diff --git a/frame/authority-discovery/src/lib.rs b/frame/authority-discovery/src/lib.rs index 341646b67..6365c9535 100644 --- a/frame/authority-discovery/src/lib.rs +++ b/frame/authority-discovery/src/lib.rs @@ -59,7 +59,7 @@ pub mod pallet { pub(super) type NextKeys = StorageValue<_, WeakBoundedVec, ValueQuery>; - #[cfg_attr(feature = "std", derive(Default))] + #[derive(Default)] #[pallet::genesis_config] pub struct GenesisConfig { pub keys: Vec, diff --git a/frame/babe/src/lib.rs b/frame/babe/src/lib.rs index 15580457c..8001450b4 100644 --- a/frame/babe/src/lib.rs +++ b/frame/babe/src/lib.rs @@ -314,7 +314,7 @@ pub mod pallet { pub(super) type SkippedEpochs = StorageValue<_, BoundedVec<(u64, SessionIndex), ConstU32<100>>, ValueQuery>; - #[cfg_attr(feature = "std", derive(Default))] + #[derive(Default)] #[pallet::genesis_config] pub struct GenesisConfig { pub authorities: Vec<(AuthorityId, BabeAuthorityWeight)>, diff --git a/frame/balances/src/lib.rs b/frame/balances/src/lib.rs index b3dc77a9b..7ed725a77 100644 --- a/frame/balances/src/lib.rs +++ b/frame/balances/src/lib.rs @@ -456,7 +456,6 @@ pub mod pallet { pub balances: Vec<(T::AccountId, T::Balance)>, } - #[cfg(feature = "std")] impl, I: 'static> Default for GenesisConfig { fn default() -> Self { Self { balances: Default::default() } @@ -483,7 +482,7 @@ pub mod pallet { .iter() .map(|(x, _)| x) .cloned() - .collect::>(); + .collect::>(); assert!( endowed_accounts.len() == self.balances.len(), diff --git a/frame/beefy/src/lib.rs b/frame/beefy/src/lib.rs index 945b32c12..6a248da2b 100644 --- a/frame/beefy/src/lib.rs +++ b/frame/beefy/src/lib.rs @@ -166,7 +166,6 @@ pub mod pallet { pub genesis_block: Option>, } - #[cfg(feature = "std")] impl Default for GenesisConfig { fn default() -> Self { // BEEFY genesis will be first BEEFY-MANDATORY block, diff --git a/frame/collective/src/lib.rs b/frame/collective/src/lib.rs index fd89a998a..12917f5bd 100644 --- a/frame/collective/src/lib.rs +++ b/frame/collective/src/lib.rs @@ -224,18 +224,12 @@ pub mod pallet { } #[pallet::genesis_config] + #[derive(frame_support::DefaultNoBound)] pub struct GenesisConfig, I: 'static = ()> { pub phantom: PhantomData, pub members: Vec, } - #[cfg(feature = "std")] - impl, I: 'static> Default for GenesisConfig { - fn default() -> Self { - Self { phantom: Default::default(), members: Default::default() } - } - } - #[pallet::genesis_build] impl, I: 'static> GenesisBuild for GenesisConfig { fn build(&self) { diff --git a/frame/democracy/src/lib.rs b/frame/democracy/src/lib.rs index 69438eba9..370559b10 100644 --- a/frame/democracy/src/lib.rs +++ b/frame/democracy/src/lib.rs @@ -439,17 +439,11 @@ pub mod pallet { pub type MetadataOf = StorageMap<_, Blake2_128Concat, MetadataOwner, PreimageHash>; #[pallet::genesis_config] + #[derive(frame_support::DefaultNoBound)] pub struct GenesisConfig { _phantom: sp_std::marker::PhantomData, } - #[cfg(feature = "std")] - impl Default for GenesisConfig { - fn default() -> Self { - GenesisConfig { _phantom: Default::default() } - } - } - #[pallet::genesis_build] impl GenesisBuild for GenesisConfig { fn build(&self) { diff --git a/frame/elections-phragmen/src/lib.rs b/frame/elections-phragmen/src/lib.rs index 9c40e542e..1d7c79fe3 100644 --- a/frame/elections-phragmen/src/lib.rs +++ b/frame/elections-phragmen/src/lib.rs @@ -711,17 +711,11 @@ pub mod pallet { StorageMap<_, Twox64Concat, T::AccountId, Voter>, ValueQuery>; #[pallet::genesis_config] + #[derive(frame_support::DefaultNoBound)] pub struct GenesisConfig { pub members: Vec<(T::AccountId, BalanceOf)>, } - #[cfg(feature = "std")] - impl Default for GenesisConfig { - fn default() -> Self { - Self { members: Default::default() } - } - } - #[pallet::genesis_build] impl GenesisBuild for GenesisConfig { fn build(&self) { diff --git a/frame/examples/basic/src/lib.rs b/frame/examples/basic/src/lib.rs index bbeaac19f..af159c0f4 100644 --- a/frame/examples/basic/src/lib.rs +++ b/frame/examples/basic/src/lib.rs @@ -618,20 +618,13 @@ pub mod pallet { // The genesis config type. #[pallet::genesis_config] + #[derive(frame_support::DefaultNoBound)] pub struct GenesisConfig { pub dummy: T::Balance, pub bar: Vec<(T::AccountId, T::Balance)>, pub foo: T::Balance, } - // The default value for the genesis config type. - #[cfg(feature = "std")] - impl Default for GenesisConfig { - fn default() -> Self { - Self { dummy: Default::default(), bar: Default::default(), foo: Default::default() } - } - } - // The build of genesis for the pallet. #[pallet::genesis_build] impl GenesisBuild for GenesisConfig { diff --git a/frame/grandpa/src/lib.rs b/frame/grandpa/src/lib.rs index f01c25e49..8311131e9 100644 --- a/frame/grandpa/src/lib.rs +++ b/frame/grandpa/src/lib.rs @@ -333,7 +333,7 @@ pub mod pallet { #[pallet::getter(fn session_for_set)] pub(super) type SetIdSession = StorageMap<_, Twox64Concat, SetId, SessionIndex>; - #[cfg_attr(feature = "std", derive(Default))] + #[derive(Default)] #[pallet::genesis_config] pub struct GenesisConfig { pub authorities: AuthorityList, diff --git a/frame/im-online/src/lib.rs b/frame/im-online/src/lib.rs index bc1e541ce..dd3809f8e 100644 --- a/frame/im-online/src/lib.rs +++ b/frame/im-online/src/lib.rs @@ -442,17 +442,11 @@ pub mod pallet { >; #[pallet::genesis_config] + #[derive(frame_support::DefaultNoBound)] pub struct GenesisConfig { pub keys: Vec, } - #[cfg(feature = "std")] - impl Default for GenesisConfig { - fn default() -> Self { - GenesisConfig { keys: Default::default() } - } - } - #[pallet::genesis_build] impl GenesisBuild for GenesisConfig { fn build(&self) { diff --git a/frame/indices/src/lib.rs b/frame/indices/src/lib.rs index 74f86abb5..56e177da3 100644 --- a/frame/indices/src/lib.rs +++ b/frame/indices/src/lib.rs @@ -263,17 +263,11 @@ pub mod pallet { StorageMap<_, Blake2_128Concat, T::AccountIndex, (T::AccountId, BalanceOf, bool)>; #[pallet::genesis_config] + #[derive(frame_support::DefaultNoBound)] pub struct GenesisConfig { pub indices: Vec<(T::AccountIndex, T::AccountId)>, } - #[cfg(feature = "std")] - impl Default for GenesisConfig { - fn default() -> Self { - Self { indices: Default::default() } - } - } - #[pallet::genesis_build] impl GenesisBuild for GenesisConfig { fn build(&self) { diff --git a/frame/membership/src/lib.rs b/frame/membership/src/lib.rs index e703b88f3..74891186a 100644 --- a/frame/membership/src/lib.rs +++ b/frame/membership/src/lib.rs @@ -105,18 +105,12 @@ pub mod pallet { pub type Prime, I: 'static = ()> = StorageValue<_, T::AccountId, OptionQuery>; #[pallet::genesis_config] + #[derive(frame_support::DefaultNoBound)] pub struct GenesisConfig, I: 'static = ()> { pub members: BoundedVec, pub phantom: PhantomData, } - #[cfg(feature = "std")] - impl, I: 'static> Default for GenesisConfig { - fn default() -> Self { - Self { members: Default::default(), phantom: Default::default() } - } - } - #[pallet::genesis_build] impl, I: 'static> GenesisBuild for GenesisConfig { fn build(&self) { diff --git a/frame/node-authorization/src/lib.rs b/frame/node-authorization/src/lib.rs index eaeda3cad..6ccc14243 100644 --- a/frame/node-authorization/src/lib.rs +++ b/frame/node-authorization/src/lib.rs @@ -109,17 +109,11 @@ pub mod pallet { StorageMap<_, Blake2_128Concat, PeerId, BTreeSet, ValueQuery>; #[pallet::genesis_config] + #[derive(frame_support::DefaultNoBound)] pub struct GenesisConfig { pub nodes: Vec<(PeerId, T::AccountId)>, } - #[cfg(feature = "std")] - impl Default for GenesisConfig { - fn default() -> Self { - Self { nodes: Vec::new() } - } - } - #[pallet::genesis_build] impl GenesisBuild for GenesisConfig { fn build(&self) { diff --git a/frame/nomination-pools/src/lib.rs b/frame/nomination-pools/src/lib.rs index 78f0c730c..cc68f54bd 100644 --- a/frame/nomination-pools/src/lib.rs +++ b/frame/nomination-pools/src/lib.rs @@ -1659,7 +1659,6 @@ pub mod pallet { pub global_max_commission: Option, } - #[cfg(feature = "std")] impl Default for GenesisConfig { fn default() -> Self { Self { diff --git a/frame/scored-pool/src/lib.rs b/frame/scored-pool/src/lib.rs index 8bd44e2ff..f8fc0fecd 100644 --- a/frame/scored-pool/src/lib.rs +++ b/frame/scored-pool/src/lib.rs @@ -249,18 +249,12 @@ pub mod pallet { pub(crate) type MemberCount = StorageValue<_, u32, ValueQuery>; #[pallet::genesis_config] + #[derive(frame_support::DefaultNoBound)] pub struct GenesisConfig, I: 'static = ()> { pub pool: PoolT, pub member_count: u32, } - #[cfg(feature = "std")] - impl, I: 'static> Default for GenesisConfig { - fn default() -> Self { - Self { pool: Default::default(), member_count: Default::default() } - } - } - #[pallet::genesis_build] impl, I: 'static> GenesisBuild for GenesisConfig { fn build(&self) { diff --git a/frame/session/src/lib.rs b/frame/session/src/lib.rs index a9f89412a..1219aaaf1 100644 --- a/frame/session/src/lib.rs +++ b/frame/session/src/lib.rs @@ -414,17 +414,11 @@ pub mod pallet { } #[pallet::genesis_config] + #[derive(frame_support::DefaultNoBound)] pub struct GenesisConfig { pub keys: Vec<(T::AccountId, T::ValidatorId, T::Keys)>, } - #[cfg(feature = "std")] - impl Default for GenesisConfig { - fn default() -> Self { - Self { keys: Default::default() } - } - } - #[pallet::genesis_build] impl GenesisBuild for GenesisConfig { fn build(&self) { diff --git a/frame/society/src/lib.rs b/frame/society/src/lib.rs index 899b6005a..d92bee48d 100644 --- a/frame/society/src/lib.rs +++ b/frame/society/src/lib.rs @@ -643,23 +643,13 @@ pub mod pallet { } #[pallet::genesis_config] + #[derive(frame_support::DefaultNoBound)] pub struct GenesisConfig, I: 'static = ()> { pub pot: BalanceOf, pub members: Vec, pub max_members: u32, } - #[cfg(feature = "std")] - impl, I: 'static> Default for GenesisConfig { - fn default() -> Self { - Self { - pot: Default::default(), - members: Default::default(), - max_members: Default::default(), - } - } - } - #[pallet::genesis_build] impl, I: 'static> GenesisBuild for GenesisConfig { fn build(&self) { diff --git a/frame/staking/src/pallet/mod.rs b/frame/staking/src/pallet/mod.rs index c5c9c669d..2b33573ac 100644 --- a/frame/staking/src/pallet/mod.rs +++ b/frame/staking/src/pallet/mod.rs @@ -579,6 +579,7 @@ pub mod pallet { pub(crate) type ChillThreshold = StorageValue<_, Percent, OptionQuery>; #[pallet::genesis_config] + #[derive(frame_support::DefaultNoBound)] pub struct GenesisConfig { pub validator_count: u32, pub minimum_validator_count: u32, @@ -594,25 +595,6 @@ pub mod pallet { pub max_nominator_count: Option, } - #[cfg(feature = "std")] - impl Default for GenesisConfig { - fn default() -> Self { - GenesisConfig { - validator_count: Default::default(), - minimum_validator_count: Default::default(), - invulnerables: Default::default(), - force_era: Default::default(), - slash_reward_fraction: Default::default(), - canceled_payout: Default::default(), - stakers: Default::default(), - min_nominator_bond: Default::default(), - min_validator_bond: Default::default(), - max_validator_count: None, - max_nominator_count: None, - } - } - } - #[pallet::genesis_build] impl GenesisBuild for GenesisConfig { fn build(&self) { diff --git a/frame/sudo/src/lib.rs b/frame/sudo/src/lib.rs index a35b870ed..50eb83810 100644 --- a/frame/sudo/src/lib.rs +++ b/frame/sudo/src/lib.rs @@ -282,18 +282,12 @@ pub mod pallet { pub(super) type Key = StorageValue<_, T::AccountId, OptionQuery>; #[pallet::genesis_config] + #[derive(frame_support::DefaultNoBound)] pub struct GenesisConfig { /// The `AccountId` of the sudo key. pub key: Option, } - #[cfg(feature = "std")] - impl Default for GenesisConfig { - fn default() -> Self { - Self { key: None } - } - } - #[pallet::genesis_build] impl GenesisBuild for GenesisConfig { fn build(&self) { diff --git a/frame/support/procedural/src/pallet/expand/genesis_config.rs b/frame/support/procedural/src/pallet/expand/genesis_config.rs index 7c66ef7d3..de46afecf 100644 --- a/frame/support/procedural/src/pallet/expand/genesis_config.rs +++ b/frame/support/procedural/src/pallet/expand/genesis_config.rs @@ -94,15 +94,24 @@ pub fn expand_genesis_config(def: &mut Def) -> proc_macro2::TokenStream { "] )); } - attrs.push(syn::parse_quote!( #[cfg(feature = "std")] )); attrs.push(syn::parse_quote!( - #[derive(#frame_support::Serialize, #frame_support::Deserialize)] + #[cfg_attr(feature = "std", derive(#frame_support::Serialize, #frame_support::Deserialize))] )); - attrs.push(syn::parse_quote!( #[serde(rename_all = "camelCase")] )); - attrs.push(syn::parse_quote!( #[serde(deny_unknown_fields)] )); - attrs.push(syn::parse_quote!( #[serde(bound(serialize = ""))] )); - attrs.push(syn::parse_quote!( #[serde(bound(deserialize = ""))] )); - attrs.push(syn::parse_quote!( #[serde(crate = #serde_crate)] )); + attrs.push( + syn::parse_quote!( #[cfg_attr(feature = "std", serde(rename_all = "camelCase"))] ), + ); + attrs.push( + syn::parse_quote!( #[cfg_attr(feature = "std", serde(deny_unknown_fields))] ), + ); + attrs.push( + syn::parse_quote!( #[cfg_attr(feature = "std", serde(bound(serialize = "")))] ), + ); + attrs.push( + syn::parse_quote!( #[cfg_attr(feature = "std", serde(bound(deserialize = "")))] ), + ); + attrs.push( + syn::parse_quote!( #[cfg_attr(feature = "std", serde(crate = #serde_crate))] ), + ); }, _ => unreachable!("Checked by genesis_config parser"), } @@ -126,7 +135,7 @@ pub fn expand_genesis_config(def: &mut Def) -> proc_macro2::TokenStream { stringify!($pallet_name), "` does not have the std feature enabled, this will cause the `", $pallet_path, - "::GenesisConfig` type to be undefined." + "::GenesisConfig` type to not implement serde traits." )); }; } diff --git a/frame/support/test/pallet/src/lib.rs b/frame/support/test/pallet/src/lib.rs index 2dfc94d83..f2d0fd7b8 100644 --- a/frame/support/test/pallet/src/lib.rs +++ b/frame/support/test/pallet/src/lib.rs @@ -37,7 +37,7 @@ pub mod pallet { pub type Value = StorageValue<_, u32>; #[pallet::genesis_config] - #[cfg_attr(feature = "std", derive(Default))] + #[derive(Default)] pub struct GenesisConfig {} #[pallet::genesis_build] diff --git a/frame/support/test/tests/construct_runtime_ui/no_std_genesis_config.stderr b/frame/support/test/tests/construct_runtime_ui/no_std_genesis_config.stderr index cfc8c9264..afa210c1a 100644 --- a/frame/support/test/tests/construct_runtime_ui/no_std_genesis_config.stderr +++ b/frame/support/test/tests/construct_runtime_ui/no_std_genesis_config.stderr @@ -1,4 +1,4 @@ -error: `Pallet` does not have the std feature enabled, this will cause the `test_pallet::GenesisConfig` type to be undefined. +error: `Pallet` does not have the std feature enabled, this will cause the `test_pallet::GenesisConfig` type to not implement serde traits. --> tests/construct_runtime_ui/no_std_genesis_config.rs:40:1 | 40 | / construct_runtime! { @@ -12,7 +12,7 @@ error: `Pallet` does not have the std feature enabled, this will cause the `test | = note: this error originates in the macro `test_pallet::__substrate_genesis_config_check::is_std_enabled_for_genesis` which comes from the expansion of the macro `construct_runtime` (in Nightly builds, run with -Z macro-backtrace for more info) -error[E0412]: cannot find type `GenesisConfig` in crate `test_pallet` +error[E0277]: the trait bound `frame_support_test_pallet::GenesisConfig: Serialize` is not satisfied --> tests/construct_runtime_ui/no_std_genesis_config.rs:40:1 | 40 | / construct_runtime! { @@ -22,10 +22,109 @@ error[E0412]: cannot find type `GenesisConfig` in crate `test_pallet` ... | 48 | | } 49 | | } - | |_^ not found in `test_pallet` + | |_^ the trait `Serialize` is not implemented for `frame_support_test_pallet::GenesisConfig` | + = help: the following other types implement trait `Serialize`: + &'a T + &'a mut T + () + (T0, T1) + (T0, T1, T2) + (T0, T1, T2, T3) + (T0, T1, T2, T3, T4) + (T0, T1, T2, T3, T4, T5) + and $N others +note: required by a bound in `hidden_include::serde::ser::SerializeStruct::serialize_field` + --> $CARGO/serde-1.0.162/src/ser/mod.rs + | + | T: Serialize; + | ^^^^^^^^^ required by this bound in `SerializeStruct::serialize_field` + = note: this error originates in the macro `construct_runtime` (in Nightly builds, run with -Z macro-backtrace for more info) + +error[E0277]: the trait bound `frame_support_test_pallet::GenesisConfig: Deserialize<'_>` is not satisfied + --> tests/construct_runtime_ui/no_std_genesis_config.rs:47:3 + | +47 | Pallet: test_pallet::{Pallet, Config}, + | ^^^^^^ the trait `Deserialize<'_>` is not implemented for `frame_support_test_pallet::GenesisConfig` + | + = help: the following other types implement trait `Deserialize<'de>`: + <&'a [u8] as Deserialize<'de>> + <&'a std::path::Path as Deserialize<'de>> + <&'a str as Deserialize<'de>> + <() as Deserialize<'de>> + <(T0, T1) as Deserialize<'de>> + <(T0, T1, T2) as Deserialize<'de>> + <(T0, T1, T2, T3) as Deserialize<'de>> + <(T0, T1, T2, T3, T4) as Deserialize<'de>> + and $N others +note: required by a bound in `next_element` + --> $CARGO/serde-1.0.162/src/de/mod.rs + | + | T: Deserialize<'de>, + | ^^^^^^^^^^^^^^^^ required by this bound in `SeqAccess::next_element` + +error[E0277]: the trait bound `frame_support_test_pallet::GenesisConfig: Deserialize<'_>` is not satisfied + --> tests/construct_runtime_ui/no_std_genesis_config.rs:47:3 + | +47 | Pallet: test_pallet::{Pallet, Config}, + | ^^^^^^ the trait `Deserialize<'_>` is not implemented for `frame_support_test_pallet::GenesisConfig` + | + = help: the following other types implement trait `Deserialize<'de>`: + <&'a [u8] as Deserialize<'de>> + <&'a std::path::Path as Deserialize<'de>> + <&'a str as Deserialize<'de>> + <() as Deserialize<'de>> + <(T0, T1) as Deserialize<'de>> + <(T0, T1, T2) as Deserialize<'de>> + <(T0, T1, T2, T3) as Deserialize<'de>> + <(T0, T1, T2, T3, T4) as Deserialize<'de>> + and $N others +note: required by a bound in `next_value` + --> $CARGO/serde-1.0.162/src/de/mod.rs + | + | V: Deserialize<'de>, + | ^^^^^^^^^^^^^^^^ required by this bound in `MapAccess::next_value` + +error[E0277]: the trait bound `frame_support_test_pallet::GenesisConfig: Deserialize<'_>` is not satisfied + --> tests/construct_runtime_ui/no_std_genesis_config.rs:40:1 + | +40 | / construct_runtime! { +41 | | pub struct Runtime where +42 | | Block = Block, +43 | | NodeBlock = Block, +... | +48 | | } +49 | | } + | |_^ the trait `Deserialize<'_>` is not implemented for `frame_support_test_pallet::GenesisConfig` + | + = help: the following other types implement trait `Deserialize<'de>`: + <&'a [u8] as Deserialize<'de>> + <&'a std::path::Path as Deserialize<'de>> + <&'a str as Deserialize<'de>> + <() as Deserialize<'de>> + <(T0, T1) as Deserialize<'de>> + <(T0, T1, T2) as Deserialize<'de>> + <(T0, T1, T2, T3) as Deserialize<'de>> + <(T0, T1, T2, T3, T4) as Deserialize<'de>> + and $N others +note: required by a bound in `hidden_include::serde::__private::de::missing_field` + --> $CARGO/serde-1.0.162/src/private/de.rs + | + | V: Deserialize<'de>, + | ^^^^^^^^^^^^^^^^ required by this bound in `missing_field` = note: this error originates in the macro `construct_runtime` (in Nightly builds, run with -Z macro-backtrace for more info) -help: consider importing this struct + +error[E0277]: the trait bound `frame_support_test_pallet::GenesisConfig: BuildModuleGenesisStorage` is not satisfied + --> tests/construct_runtime_ui/no_std_genesis_config.rs:40:1 | -1 | use frame_system::GenesisConfig; +40 | / construct_runtime! { +41 | | pub struct Runtime where +42 | | Block = Block, +43 | | NodeBlock = Block, +... | +48 | | } +49 | | } + | |_^ the trait `BuildModuleGenesisStorage` is not implemented for `frame_support_test_pallet::GenesisConfig` | + = help: the trait `BuildModuleGenesisStorage` is implemented for `frame_system::GenesisConfig` + = note: this error originates in the macro `construct_runtime` (in Nightly builds, run with -Z macro-backtrace for more info) diff --git a/frame/support/test/tests/construct_runtime_ui/undefined_genesis_config_part.stderr b/frame/support/test/tests/construct_runtime_ui/undefined_genesis_config_part.stderr index f56780c0b..a5f6b3a10 100644 --- a/frame/support/test/tests/construct_runtime_ui/undefined_genesis_config_part.stderr +++ b/frame/support/test/tests/construct_runtime_ui/undefined_genesis_config_part.stderr @@ -28,7 +28,9 @@ error[E0412]: cannot find type `GenesisConfig` in module `pallet` | |_^ not found in `pallet` | = note: this error originates in the macro `construct_runtime` (in Nightly builds, run with -Z macro-backtrace for more info) -help: consider importing this struct +help: consider importing one of these items | 1 | use frame_system::GenesisConfig; | +1 | use test_pallet::GenesisConfig; + | diff --git a/frame/system/src/lib.rs b/frame/system/src/lib.rs index 7a97ace73..f3d75f719 100644 --- a/frame/system/src/lib.rs +++ b/frame/system/src/lib.rs @@ -630,10 +630,10 @@ pub mod pallet { #[pallet::whitelist_storage] pub(super) type ExecutionPhase = StorageValue<_, Phase>; - #[cfg_attr(feature = "std", derive(Default))] + #[derive(Default)] #[pallet::genesis_config] pub struct GenesisConfig { - #[serde(with = "sp_core::bytes")] + #[cfg_attr(feature = "std", serde(with = "sp_core::bytes"))] pub code: Vec, } diff --git a/frame/transaction-payment/src/lib.rs b/frame/transaction-payment/src/lib.rs index 83ff69428..f7bdc23a0 100644 --- a/frame/transaction-payment/src/lib.rs +++ b/frame/transaction-payment/src/lib.rs @@ -362,7 +362,6 @@ pub mod pallet { pub multiplier: Multiplier, } - #[cfg(feature = "std")] impl Default for GenesisConfig { fn default() -> Self { Self { multiplier: MULTIPLIER_DEFAULT_VALUE } diff --git a/frame/transaction-storage/src/lib.rs b/frame/transaction-storage/src/lib.rs index 59662ee86..b99bc49fc 100644 --- a/frame/transaction-storage/src/lib.rs +++ b/frame/transaction-storage/src/lib.rs @@ -383,7 +383,6 @@ pub mod pallet { pub storage_period: T::BlockNumber, } - #[cfg(feature = "std")] impl Default for GenesisConfig { fn default() -> Self { Self { diff --git a/frame/treasury/src/lib.rs b/frame/treasury/src/lib.rs index 450aee51f..847ff96a7 100644 --- a/frame/treasury/src/lib.rs +++ b/frame/treasury/src/lib.rs @@ -234,15 +234,9 @@ pub mod pallet { StorageValue<_, BoundedVec, ValueQuery>; #[pallet::genesis_config] + #[derive(Default)] pub struct GenesisConfig; - #[cfg(feature = "std")] - impl Default for GenesisConfig { - fn default() -> Self { - Self - } - } - #[cfg(feature = "std")] impl GenesisConfig { /// Direct implementation of `GenesisBuild::assimilate_storage`. diff --git a/frame/vesting/src/lib.rs b/frame/vesting/src/lib.rs index af3654924..8f98295dd 100644 --- a/frame/vesting/src/lib.rs +++ b/frame/vesting/src/lib.rs @@ -214,17 +214,11 @@ pub mod pallet { pub struct Pallet(_); #[pallet::genesis_config] + #[derive(frame_support::DefaultNoBound)] pub struct GenesisConfig { pub vesting: Vec<(T::AccountId, T::BlockNumber, T::BlockNumber, BalanceOf)>, } - #[cfg(feature = "std")] - impl Default for GenesisConfig { - fn default() -> Self { - GenesisConfig { vesting: Default::default() } - } - } - #[pallet::genesis_build] impl GenesisBuild for GenesisConfig { fn build(&self) { From 2000147ad910f594a998fd6444f259a234013f27 Mon Sep 17 00:00:00 2001 From: Vladimir Pouzanov Date: Sat, 20 May 2023 13:31:51 +0100 Subject: [PATCH 521/558] Update the nix build configuration. (#13706) * Update the nix build configuration. Remove the old shell.nix with some legacy versions pinned and replace it with a flake-based shell. It installs rust via rustup instead of fenix to be more generally compatible with the guidelines. This also adds the rust-toolchain.toml spec with all the components required for wasm, and everything else to make rust-analyzer & clippy happy. * Also add the top level flake for hacking on the substrate as whole * Remove the envrc and ignore it instead. * Remove the top-level configuration --------- Co-authored-by: parity-processbot <> --- .gitignore | 2 ++ bin/node-template/.envrc | 1 + bin/node-template/README.md | 6 ++-- bin/node-template/flake.lock | 43 +++++++++++++++++++++++++++ bin/node-template/flake.nix | 22 ++++++++++++++ bin/node-template/rust-toolchain.toml | 14 +++++++++ bin/node-template/shell.nix | 35 ---------------------- 7 files changed, 86 insertions(+), 37 deletions(-) create mode 100644 bin/node-template/.envrc create mode 100644 bin/node-template/flake.lock create mode 100644 bin/node-template/flake.nix create mode 100644 bin/node-template/rust-toolchain.toml delete mode 100644 bin/node-template/shell.nix diff --git a/.gitignore b/.gitignore index 2961fd68f..65059279f 100644 --- a/.gitignore +++ b/.gitignore @@ -26,3 +26,5 @@ rls*.log *.iml bin/node-template/Cargo.lock substrate.code-workspace +.direnv/ +/.envrc diff --git a/bin/node-template/.envrc b/bin/node-template/.envrc new file mode 100644 index 000000000..3550a30f2 --- /dev/null +++ b/bin/node-template/.envrc @@ -0,0 +1 @@ +use flake diff --git a/bin/node-template/README.md b/bin/node-template/README.md index 7562b6247..0dd4ee507 100644 --- a/bin/node-template/README.md +++ b/bin/node-template/README.md @@ -153,8 +153,10 @@ Instead of installing dependencies and building this source directly, consider t ### Nix -Install [nix](https://nixos.org/), and optionally [direnv](https://github.com/direnv/direnv) and [lorri](https://github.com/nix-community/lorri) for a fully plug-and-play experience for setting up the development environment. -To get all the correct dependencies, activate direnv `direnv allow` and lorri `lorri shell`. +Install [nix](https://nixos.org/) and +[nix-direnv](https://github.com/nix-community/nix-direnv) for a fully plug-and-play +experience for setting up the development environment. +To get all the correct dependencies, activate direnv `direnv allow`. ### Docker diff --git a/bin/node-template/flake.lock b/bin/node-template/flake.lock new file mode 100644 index 000000000..60819f675 --- /dev/null +++ b/bin/node-template/flake.lock @@ -0,0 +1,43 @@ +{ + "nodes": { + "flake-utils": { + "locked": { + "lastModified": 1678901627, + "narHash": "sha256-U02riOqrKKzwjsxc/400XnElV+UtPUQWpANPlyazjH0=", + "owner": "numtide", + "repo": "flake-utils", + "rev": "93a2b84fc4b70d9e089d029deacc3583435c2ed6", + "type": "github" + }, + "original": { + "owner": "numtide", + "repo": "flake-utils", + "type": "github" + } + }, + "nixpkgs": { + "locked": { + "lastModified": 1679262748, + "narHash": "sha256-DQCrrAFrkxijC6haUzOC5ZoFqpcv/tg2WxnyW3np1Cc=", + "owner": "NixOS", + "repo": "nixpkgs", + "rev": "60c1d71f2ba4c80178ec84523c2ca0801522e0a6", + "type": "github" + }, + "original": { + "owner": "NixOS", + "ref": "nixos-unstable", + "repo": "nixpkgs", + "type": "github" + } + }, + "root": { + "inputs": { + "flake-utils": "flake-utils", + "nixpkgs": "nixpkgs" + } + } + }, + "root": "root", + "version": 7 +} diff --git a/bin/node-template/flake.nix b/bin/node-template/flake.nix new file mode 100644 index 000000000..428efd094 --- /dev/null +++ b/bin/node-template/flake.nix @@ -0,0 +1,22 @@ +{ + inputs = { + nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable"; + flake-utils.url = "github:numtide/flake-utils"; + }; + + outputs = { self, nixpkgs, flake-utils, ... }: flake-utils.lib.eachDefaultSystem (system: + let + pkgs = import nixpkgs { inherit system; }; + in + { + devShells.default = pkgs.mkShell { + packages = with pkgs; [ + rustup + clang + protobuf + ]; + + LIBCLANG_PATH = "${pkgs.libclang.lib}/lib"; + }; + }); +} diff --git a/bin/node-template/rust-toolchain.toml b/bin/node-template/rust-toolchain.toml new file mode 100644 index 000000000..64daeff68 --- /dev/null +++ b/bin/node-template/rust-toolchain.toml @@ -0,0 +1,14 @@ +[toolchain] +channel = "nightly" +components = [ + "cargo", + "clippy", + "rust-analyzer", + "rust-src", + "rust-std", + "rustc-dev", + "rustc", + "rustfmt", +] +targets = [ "wasm32-unknown-unknown" ] +profile = "minimal" diff --git a/bin/node-template/shell.nix b/bin/node-template/shell.nix deleted file mode 100644 index c08005c16..000000000 --- a/bin/node-template/shell.nix +++ /dev/null @@ -1,35 +0,0 @@ -let - mozillaOverlay = - import (builtins.fetchGit { - url = "https://github.com/mozilla/nixpkgs-mozilla.git"; - rev = "57c8084c7ef41366993909c20491e359bbb90f54"; - }); - pinned = builtins.fetchGit { - # Descriptive name to make the store path easier to identify - url = "https://github.com/nixos/nixpkgs/"; - # Commit hash for nixos-unstable as of 2020-04-26 - # `git ls-remote https://github.com/nixos/nixpkgs nixos-unstable` - ref = "refs/heads/nixos-unstable"; - rev = "1fe6ed37fd9beb92afe90671c0c2a662a03463dd"; - }; - nixpkgs = import pinned { overlays = [ mozillaOverlay ]; }; - toolchain = with nixpkgs; (rustChannelOf { date = "2021-09-14"; channel = "nightly"; }); - rust-wasm = toolchain.rust.override { - targets = [ "wasm32-unknown-unknown" ]; - }; -in -with nixpkgs; pkgs.mkShell { - buildInputs = [ - clang - pkg-config - rust-wasm - ] ++ stdenv.lib.optionals stdenv.isDarwin [ - darwin.apple_sdk.frameworks.Security - ]; - - LIBCLANG_PATH = "${llvmPackages.libclang}/lib"; - PROTOC = "${protobuf}/bin/protoc"; - RUST_SRC_PATH = "${toolchain.rust-src}/lib/rustlib/src/rust/library/"; - ROCKSDB_LIB_DIR = "${rocksdb}/lib"; - -} From 8fca6cf78642ebaaa3dfcb306574d2d503a36bd9 Mon Sep 17 00:00:00 2001 From: Gavin Wood Date: Sun, 21 May 2023 20:20:14 +0100 Subject: [PATCH 522/558] FRAME: Allow message ID to be mutated in ProcessMessage (#14183) --- frame/message-queue/src/benchmarking.rs | 6 +++--- frame/message-queue/src/lib.rs | 22 ++++++++++------------ frame/message-queue/src/mock.rs | 2 ++ frame/message-queue/src/mock_helpers.rs | 1 + frame/message-queue/src/tests.rs | 13 +++++++------ frame/support/src/traits/messages.rs | 1 + 6 files changed, 24 insertions(+), 21 deletions(-) diff --git a/frame/message-queue/src/benchmarking.rs b/frame/message-queue/src/benchmarking.rs index b53527048..53c84c3da 100644 --- a/frame/message-queue/src/benchmarking.rs +++ b/frame/message-queue/src/benchmarking.rs @@ -142,7 +142,7 @@ mod benchmarks { // Check that it was processed. assert_last_event::( Event::Processed { - hash: T::Hashing::hash(&msg), + id: sp_io::hashing::blake2_256(&msg), origin: 0.into(), weight_used: 1.into_weight(), success: true, @@ -227,7 +227,7 @@ mod benchmarks { assert_last_event::( Event::Processed { - hash: T::Hashing::hash(&((msgs - 1) as u32).encode()), + id: sp_io::hashing::blake2_256(&((msgs - 1) as u32).encode()), origin: 0.into(), weight_used: Weight::from_parts(1, 1), success: true, @@ -264,7 +264,7 @@ mod benchmarks { assert_last_event::( Event::Processed { - hash: T::Hashing::hash(&((msgs - 1) as u32).encode()), + id: sp_io::hashing::blake2_256(&((msgs - 1) as u32).encode()), origin: 0.into(), weight_used: Weight::from_parts(1, 1), success: true, diff --git a/frame/message-queue/src/lib.rs b/frame/message-queue/src/lib.rs index c8e197610..37fbe85fd 100644 --- a/frame/message-queue/src/lib.rs +++ b/frame/message-queue/src/lib.rs @@ -204,7 +204,7 @@ pub use pallet::*; use scale_info::TypeInfo; use sp_arithmetic::traits::{BaseArithmetic, Unsigned}; use sp_runtime::{ - traits::{Hash, One, Zero}, + traits::{One, Zero}, SaturatedConversion, Saturating, }; use sp_std::{fmt::Debug, ops::Deref, prelude::*, vec}; @@ -499,16 +499,13 @@ pub mod pallet { #[pallet::event] #[pallet::generate_deposit(pub(super) fn deposit_event)] pub enum Event { - /// Message discarded due to an inability to decode the item. Usually caused by state - /// corruption. - Discarded { hash: T::Hash }, /// Message discarded due to an error in the `MessageProcessor` (usually a format error). - ProcessingFailed { hash: T::Hash, origin: MessageOriginOf, error: ProcessMessageError }, + ProcessingFailed { id: [u8; 32], origin: MessageOriginOf, error: ProcessMessageError }, /// Message is processed. - Processed { hash: T::Hash, origin: MessageOriginOf, weight_used: Weight, success: bool }, + Processed { id: [u8; 32], origin: MessageOriginOf, weight_used: Weight, success: bool }, /// Message placed in overweight queue. OverweightEnqueued { - hash: T::Hash, + id: [u8; 32], origin: MessageOriginOf, page_index: PageIndex, message_index: T::Size, @@ -1147,15 +1144,16 @@ impl Pallet { meter: &mut WeightMeter, overweight_limit: Weight, ) -> MessageExecutionStatus { - let hash = T::Hashing::hash(message); + let hash = sp_io::hashing::blake2_256(message); use ProcessMessageError::*; let prev_consumed = meter.consumed; + let mut id = hash; - match T::MessageProcessor::process_message(message, origin.clone(), meter) { + match T::MessageProcessor::process_message(message, origin.clone(), meter, &mut id) { Err(Overweight(w)) if w.any_gt(overweight_limit) => { // Permanently overweight. Self::deposit_event(Event::::OverweightEnqueued { - hash, + id, origin, page_index, message_index, @@ -1173,13 +1171,13 @@ impl Pallet { }, Err(error @ BadFormat | error @ Corrupt | error @ Unsupported) => { // Permanent error - drop - Self::deposit_event(Event::::ProcessingFailed { hash, origin, error }); + Self::deposit_event(Event::::ProcessingFailed { id, origin, error }); MessageExecutionStatus::Unprocessable { permanent: true } }, Ok(success) => { // Success let weight_used = meter.consumed.saturating_sub(prev_consumed); - Self::deposit_event(Event::::Processed { hash, origin, weight_used, success }); + Self::deposit_event(Event::::Processed { id, origin, weight_used, success }); MessageExecutionStatus::Processed }, } diff --git a/frame/message-queue/src/mock.rs b/frame/message-queue/src/mock.rs index a0fe01056..71f0b0fa2 100644 --- a/frame/message-queue/src/mock.rs +++ b/frame/message-queue/src/mock.rs @@ -172,6 +172,7 @@ impl ProcessMessage for RecordingMessageProcessor { message: &[u8], origin: Self::Origin, meter: &mut WeightMeter, + _id: &mut [u8; 32], ) -> Result { processing_message(message, &origin)?; @@ -239,6 +240,7 @@ impl ProcessMessage for CountingMessageProcessor { message: &[u8], origin: Self::Origin, meter: &mut WeightMeter, + _id: &mut [u8; 32], ) -> Result { if let Err(e) = processing_message(message, &origin) { NumMessagesErrored::set(NumMessagesErrored::get() + 1); diff --git a/frame/message-queue/src/mock_helpers.rs b/frame/message-queue/src/mock_helpers.rs index 257691cae..5a39eb0a5 100644 --- a/frame/message-queue/src/mock_helpers.rs +++ b/frame/message-queue/src/mock_helpers.rs @@ -62,6 +62,7 @@ where _message: &[u8], _origin: Self::Origin, meter: &mut WeightMeter, + _id: &mut [u8; 32], ) -> Result { let required = Weight::from_parts(REQUIRED_WEIGHT, REQUIRED_WEIGHT); diff --git a/frame/message-queue/src/tests.rs b/frame/message-queue/src/tests.rs index 15bb90573..10eff69c9 100644 --- a/frame/message-queue/src/tests.rs +++ b/frame/message-queue/src/tests.rs @@ -23,6 +23,7 @@ use crate::{mock::*, *}; use frame_support::{assert_noop, assert_ok, assert_storage_noop, StorageNoopGuard}; use rand::{rngs::StdRng, Rng, SeedableRng}; +use sp_core::blake2_256; #[test] fn mocked_weight_works() { @@ -178,7 +179,7 @@ fn service_queues_failing_messages_works() { assert_eq!(MessageQueue::service_queues(1.into_weight()), 1.into_weight()); assert_last_event::( Event::ProcessingFailed { - hash: ::Hashing::hash(b"badformat"), + id: blake2_256(b"badformat"), origin: MessageOrigin::Here, error: ProcessMessageError::BadFormat, } @@ -187,7 +188,7 @@ fn service_queues_failing_messages_works() { assert_eq!(MessageQueue::service_queues(1.into_weight()), 1.into_weight()); assert_last_event::( Event::ProcessingFailed { - hash: ::Hashing::hash(b"corrupt"), + id: blake2_256(b"corrupt"), origin: MessageOrigin::Here, error: ProcessMessageError::Corrupt, } @@ -196,7 +197,7 @@ fn service_queues_failing_messages_works() { assert_eq!(MessageQueue::service_queues(1.into_weight()), 1.into_weight()); assert_last_event::( Event::ProcessingFailed { - hash: ::Hashing::hash(b"unsupported"), + id: blake2_256(b"unsupported"), origin: MessageOrigin::Here, error: ProcessMessageError::Unsupported, } @@ -677,7 +678,7 @@ fn service_page_item_skips_perm_overweight_message() { assert_eq!(weight.consumed, 2.into_weight()); assert_last_event::( Event::OverweightEnqueued { - hash: ::Hashing::hash(b"TooMuch"), + id: blake2_256(b"TooMuch"), origin: MessageOrigin::Here, message_index: 0, page_index: 0, @@ -1050,7 +1051,7 @@ fn execute_overweight_works() { assert_eq!(QueueChanges::take(), vec![(origin, 1, 8)]); assert_last_event::( Event::OverweightEnqueued { - hash: ::Hashing::hash(b"weight=6"), + id: blake2_256(b"weight=6"), origin: MessageOrigin::Here, message_index: 0, page_index: 0, @@ -1105,7 +1106,7 @@ fn permanently_overweight_book_unknits() { assert_eq!(MessageQueue::service_queues(8.into_weight()), 4.into_weight()); assert_last_event::( Event::OverweightEnqueued { - hash: ::Hashing::hash(b"weight=9"), + id: blake2_256(b"weight=9"), origin: Here, message_index: 0, page_index: 0, diff --git a/frame/support/src/traits/messages.rs b/frame/support/src/traits/messages.rs index 781da3ed6..fe907b0c6 100644 --- a/frame/support/src/traits/messages.rs +++ b/frame/support/src/traits/messages.rs @@ -59,6 +59,7 @@ pub trait ProcessMessage { message: &[u8], origin: Self::Origin, meter: &mut WeightMeter, + id: &mut [u8; 32], ) -> Result; } From 691894bb372b525c2261987643077ac841da3f96 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bastian=20K=C3=B6cher?= Date: Sun, 21 May 2023 22:24:04 +0200 Subject: [PATCH 523/558] RevertCmd: Expose database params via CLI (#14182) * RevertCmd: Expose database params via CLI This exposes the database params for the `RevertCmd` via CLI. So, users can use `revert` with ParityDb. * ".git/.scripts/commands/fmt/fmt.sh" --------- Co-authored-by: command-bot <> --- client/cli/src/commands/revert_cmd.rs | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/client/cli/src/commands/revert_cmd.rs b/client/cli/src/commands/revert_cmd.rs index df5d93a7e..34e8c1036 100644 --- a/client/cli/src/commands/revert_cmd.rs +++ b/client/cli/src/commands/revert_cmd.rs @@ -18,7 +18,7 @@ use crate::{ error, - params::{GenericNumber, PruningParams, SharedParams}, + params::{DatabaseParams, GenericNumber, PruningParams, SharedParams}, CliConfiguration, }; use clap::Parser; @@ -41,6 +41,10 @@ pub struct RevertCmd { #[allow(missing_docs)] #[clap(flatten)] pub pruning_params: PruningParams, + + #[allow(missing_docs)] + #[clap(flatten)] + pub database_params: DatabaseParams, } /// Revert handler for auxiliary data (e.g. consensus). @@ -79,4 +83,8 @@ impl CliConfiguration for RevertCmd { fn pruning_params(&self) -> Option<&PruningParams> { Some(&self.pruning_params) } + + fn database_params(&self) -> Option<&DatabaseParams> { + Some(&self.database_params) + } } From e85188b800c63ca3be545c1a43a0927fe0b6fd11 Mon Sep 17 00:00:00 2001 From: Sergej Sakac <73715684+Szegoo@users.noreply.github.com> Date: Sun, 21 May 2023 23:30:49 +0200 Subject: [PATCH 524/558] Improve try-state docs (#13967) * Improve Try-State docs * fmt * fixes --- utils/frame/try-runtime/cli/src/lib.rs | 29 ++++++++++++++++---------- 1 file changed, 18 insertions(+), 11 deletions(-) diff --git a/utils/frame/try-runtime/cli/src/lib.rs b/utils/frame/try-runtime/cli/src/lib.rs index 9268ef2ed..4893c464b 100644 --- a/utils/frame/try-runtime/cli/src/lib.rs +++ b/utils/frame/try-runtime/cli/src/lib.rs @@ -248,7 +248,7 @@ //! # assuming there's `./substrate --dev --tmp --ws-port 9999` or similar running. //! ./substrate-try-runtime \ //! try-runtime \ -//! --runtime kitchensink_runtime.wasm \ +//! --runtime runtime-try-runtime.wasm \ //! -lruntime=debug \ //! on-runtime-upgrade \ //! live --uri ws://localhost:9999 @@ -260,7 +260,7 @@ //! ```bash //! ./substrate-try-runtime \ //! try-runtime \ -//! --runtime kitchensink_runtime.wasm \ +//! --runtime runtime-try-runtime.wasm \ //! -lruntime=debug \ //! on-runtime-upgrade \ //! live --uri ws://localhost:9999 \ @@ -295,7 +295,7 @@ //! Then, we can use it to have the same command as before, `on-runtime-upgrade` //! //! ```bash -//! try-runtime \ +//! ./substrate-try-runtime try-runtime \ //! --runtime runtime-try-runtime.wasm \ //! -lruntime=debug \ //! on-runtime-upgrade \ @@ -309,7 +309,7 @@ //! --runtime runtime-try-runtime.wasm \ //! -lruntime=debug \ //! execute-block live \ -//! --uri ws://localhost:999 +//! --uri ws://localhost:9999 //! ``` //! //! This can still be customized at a given block with `--at`. If you want to use a snapshot, you @@ -320,15 +320,22 @@ //! //! ```bash //! ./substrate-try-runtime try-runtime \ -//! --runtime runtime-try-runtime.wasm \ -//! -lruntime=debug \ -//! execute-block live \ -//! --try-state System,Staking \ -//! --uri ws://localhost:999 +//! --runtime runtime-try-runtime.wasm \ +//! -lruntime=debug \ +//! execute-block \ +//! --try-state System,Staking \ +//! live \ +//! --uri ws://localhost:9999 \ +//! --pallet System Staking //! ``` //! -//! Will only run the `try-state` of the two given pallets. See -//! [`frame_try_runtime::TryStateSelect`] for more information. +//! Will only run the `try-state` of the two given pallets. When running `try-state` against +//! some real chain data it can take a long time for the command to execute since it has to +//! query all the key-value pairs. In scenarios like above where we only want to run the +//! `try-state` for some specific pallets, we can use the `--pallet` option to specify from +//! which pallets we want to query the state. This will greatly decrease the execution time. +//! +//! See [`frame_try_runtime::TryStateSelect`] for more information. //! //! * Follow our live chain's blocks using `follow-chain`, whilst running the try-state of 3 pallets //! in a round robin fashion From 17a99d11d2a7466dd43193cb9ecfdc9b441acd1d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bastian=20K=C3=B6cher?= Date: Mon, 22 May 2023 10:50:58 +0200 Subject: [PATCH 525/558] test-runtime: Return hashed call as provides in unsigned validation (#14180) This is required to make different unsigned extrinsics resolve to different transactions in the tx pool by having `provides` set to theh hash of the call. --- test-utils/runtime/src/lib.rs | 96 ++++++++----------- .../runtime/src/substrate_test_pallet.rs | 12 ++- 2 files changed, 48 insertions(+), 60 deletions(-) diff --git a/test-utils/runtime/src/lib.rs b/test-utils/runtime/src/lib.rs index e1d66e860..c93d5c4e5 100644 --- a/test-utils/runtime/src/lib.rs +++ b/test-utils/runtime/src/lib.rs @@ -995,7 +995,10 @@ mod tests { use sp_consensus::BlockOrigin; use sp_core::{storage::well_known_keys::HEAP_PAGES, ExecutionContext}; use sp_keyring::AccountKeyring; - use sp_runtime::{traits::SignedExtension, transaction_validity::InvalidTransaction}; + use sp_runtime::{ + traits::{Hash as _, SignedExtension}, + transaction_validity::{InvalidTransaction, ValidTransaction}, + }; use sp_state_machine::ExecutionStrategy; use substrate_test_runtime_client::{ prelude::*, runtime::TestAPI, DefaultTestClientBuilderExt, TestClientBuilder, @@ -1103,63 +1106,42 @@ mod tests { fn validate_unsigned_works() { sp_tracing::try_init_simple(); new_test_ext().execute_with(|| { - assert_eq!( - ::validate_unsigned( - TransactionSource::External, - &substrate_test_pallet::Call::bench_call { transfer: Default::default() }, - ), - InvalidTransaction::Call.into(), - ); - - assert_eq!( - ::validate_unsigned( - TransactionSource::External, - &substrate_test_pallet::Call::include_data { data: vec![] }, - ), - InvalidTransaction::Call.into(), - ); - - assert_eq!( - ::validate_unsigned( - TransactionSource::External, - &substrate_test_pallet::Call::fill_block { ratio: Perbill::from_percent(50) }, - ), - InvalidTransaction::Call.into(), - ); - - assert_eq!( - ::validate_unsigned( - TransactionSource::External, - &substrate_test_pallet::Call::deposit_log_digest_item { - log: DigestItem::Other(vec![]) - }, - ), - Ok(Default::default()), - ); - - assert_eq!( - ::validate_unsigned( - TransactionSource::External, - &substrate_test_pallet::Call::storage_change { key: vec![], value: None }, - ), - Ok(Default::default()), - ); - - assert_eq!( - ::validate_unsigned( - TransactionSource::External, - &substrate_test_pallet::Call::read { count: 0 }, - ), - Ok(Default::default()), - ); + let failing_calls = vec![ + substrate_test_pallet::Call::bench_call { transfer: Default::default() }, + substrate_test_pallet::Call::include_data { data: vec![] }, + substrate_test_pallet::Call::fill_block { ratio: Perbill::from_percent(50) }, + ]; + let succeeding_calls = vec![ + substrate_test_pallet::Call::deposit_log_digest_item { + log: DigestItem::Other(vec![]), + }, + substrate_test_pallet::Call::storage_change { key: vec![], value: None }, + substrate_test_pallet::Call::read { count: 0 }, + substrate_test_pallet::Call::read_and_panic { count: 0 }, + ]; + + for call in failing_calls { + assert_eq!( + ::validate_unsigned( + TransactionSource::External, + &call, + ), + InvalidTransaction::Call.into(), + ); + } - assert_eq!( - ::validate_unsigned( - TransactionSource::External, - &substrate_test_pallet::Call::read_and_panic { count: 0 }, - ), - Ok(Default::default()), - ); + for call in succeeding_calls { + assert_eq!( + ::validate_unsigned( + TransactionSource::External, + &call, + ), + Ok(ValidTransaction { + provides: vec![BlakeTwo256::hash_of(&call).encode()], + ..Default::default() + }) + ); + } }); } diff --git a/test-utils/runtime/src/substrate_test_pallet.rs b/test-utils/runtime/src/substrate_test_pallet.rs index 40e7af0b4..93a1a5efc 100644 --- a/test-utils/runtime/src/substrate_test_pallet.rs +++ b/test-utils/runtime/src/substrate_test_pallet.rs @@ -23,8 +23,11 @@ use frame_support::{pallet_prelude::*, storage}; use sp_core::sr25519::Public; -use sp_runtime::transaction_validity::{ - InvalidTransaction, TransactionSource, TransactionValidity, ValidTransaction, +use sp_runtime::{ + traits::{BlakeTwo256, Hash}, + transaction_validity::{ + InvalidTransaction, TransactionSource, TransactionValidity, ValidTransaction, + }, }; use sp_std::prelude::*; @@ -225,7 +228,10 @@ pub mod pallet { Call::deposit_log_digest_item { .. } | Call::storage_change { .. } | Call::read { .. } | - Call::read_and_panic { .. } => Ok(Default::default()), + Call::read_and_panic { .. } => Ok(ValidTransaction { + provides: vec![BlakeTwo256::hash_of(&call).encode()], + ..Default::default() + }), _ => Err(TransactionValidityError::Invalid(InvalidTransaction::Call)), } } From 20bd05cac4112a8455ec542e83313e22fcc77ddb Mon Sep 17 00:00:00 2001 From: Robert Hambrock Date: Mon, 22 May 2023 13:55:40 +0200 Subject: [PATCH 526/558] Revert "Optimize merkle proofs for efficient verification in Solidity (#12857)" (#14176) * Revert "Optimize merkle proofs for efficient verification in Solidity (#12857)" This reverts commit f9d1dcdfa1f0a2edc2b933a0b11ffd499c22c73a since we still require commitment to the leaves - see #12820. * remove PartialOrd trait from mmr hash type --- frame/beefy-mmr/src/tests.rs | 10 +++--- frame/merkle-mountain-range/src/lib.rs | 3 +- utils/binary-merkle-tree/src/lib.rs | 46 ++++++++++++-------------- 3 files changed, 28 insertions(+), 31 deletions(-) diff --git a/frame/beefy-mmr/src/tests.rs b/frame/beefy-mmr/src/tests.rs index dc2e46f31..d7e0ddd98 100644 --- a/frame/beefy-mmr/src/tests.rs +++ b/frame/beefy-mmr/src/tests.rs @@ -70,7 +70,7 @@ fn should_contain_mmr_digest() { ValidatorSet::new(vec![mock_beefy_id(1), mock_beefy_id(2)], 1).unwrap() )), beefy_log(ConsensusLog::MmrRoot(array_bytes::hex_n_into_unchecked( - "200e73880940ac0b66735ffb560fa0a3989292463d262deac6ad61e78a3e46a4" + "95803defe6ea9f41e7ec6afa497064f21bfded027d8812efacbdf984e630cbdc" ))) ] ); @@ -85,13 +85,13 @@ fn should_contain_mmr_digest() { ValidatorSet::new(vec![mock_beefy_id(1), mock_beefy_id(2)], 1).unwrap() )), beefy_log(ConsensusLog::MmrRoot(array_bytes::hex_n_into_unchecked( - "200e73880940ac0b66735ffb560fa0a3989292463d262deac6ad61e78a3e46a4" + "95803defe6ea9f41e7ec6afa497064f21bfded027d8812efacbdf984e630cbdc" ))), beefy_log(ConsensusLog::AuthoritiesChange( ValidatorSet::new(vec![mock_beefy_id(3), mock_beefy_id(4)], 2).unwrap() )), beefy_log(ConsensusLog::MmrRoot(array_bytes::hex_n_into_unchecked( - "ba37d8d5d195ac8caec391da35472f9ecf1116ff1642409148b62e08896d3884" + "a73271a0974f1e67d6e9b8dd58e506177a2e556519a330796721e98279a753e2" ))), ] ); @@ -124,7 +124,7 @@ fn should_contain_valid_leaf_data() { ) }, leaf_extra: array_bytes::hex2bytes_unchecked( - "5572d58c82bddf323f4fc7aecab8a8f0ad6ed2f06ab2bfb8ade36a77a45fcc68" + "55b8e9e1cc9f0db7776fac0ca66318ef8acfb8ec26db11e373120583e07ee648" ) } ); @@ -149,7 +149,7 @@ fn should_contain_valid_leaf_data() { ) }, leaf_extra: array_bytes::hex2bytes_unchecked( - "5572d58c82bddf323f4fc7aecab8a8f0ad6ed2f06ab2bfb8ade36a77a45fcc68" + "55b8e9e1cc9f0db7776fac0ca66318ef8acfb8ec26db11e373120583e07ee648" ) } ); diff --git a/frame/merkle-mountain-range/src/lib.rs b/frame/merkle-mountain-range/src/lib.rs index 4ef833e6c..21d020ed5 100644 --- a/frame/merkle-mountain-range/src/lib.rs +++ b/frame/merkle-mountain-range/src/lib.rs @@ -163,8 +163,7 @@ pub mod pallet { + codec::Codec + codec::EncodeLike + scale_info::TypeInfo - + MaxEncodedLen - + PartialOrd; + + MaxEncodedLen; /// Data stored in the leaf nodes. /// diff --git a/utils/binary-merkle-tree/src/lib.rs b/utils/binary-merkle-tree/src/lib.rs index 43c07cb60..ab532fd0b 100644 --- a/utils/binary-merkle-tree/src/lib.rs +++ b/utils/binary-merkle-tree/src/lib.rs @@ -27,8 +27,7 @@ //! Merkle Tree is constructed from arbitrary-length leaves, that are initially hashed using the //! same hasher as the inner nodes. //! Inner nodes are created by concatenating child hashes and hashing again. The implementation -//! sorts each pair of hashes before every hash operation. This makes proof verification more -//! efficient by removing the need to track which side each intermediate hash is concatenated on. +//! does not perform any sorting of the input data (leaves) nor when inner nodes are created. //! //! If the number of leaves is not even, last leaf (hash of) is promoted to the upper layer. #[cfg(not(feature = "std"))] @@ -48,7 +47,7 @@ use hash_db::Hasher; pub fn merkle_root(leaves: I) -> H::Out where H: Hasher, - H::Out: Default + AsRef<[u8]> + PartialOrd, + H::Out: Default + AsRef<[u8]>, I: IntoIterator, I::Item: AsRef<[u8]>, { @@ -59,7 +58,7 @@ where fn merkelize(leaves: I, visitor: &mut V) -> H::Out where H: Hasher, - H::Out: Default + AsRef<[u8]> + PartialOrd, + H::Out: Default + AsRef<[u8]>, V: Visitor, I: Iterator, { @@ -144,7 +143,7 @@ impl Visitor for () { pub fn merkle_proof(leaves: I, leaf_index: usize) -> MerkleProof where H: Hasher, - H::Out: Default + Copy + AsRef<[u8]> + PartialOrd, + H::Out: Default + Copy + AsRef<[u8]>, I: IntoIterator, I::IntoIter: ExactSizeIterator, T: AsRef<[u8]>, @@ -244,7 +243,7 @@ pub fn verify_proof<'a, H, P, L>( ) -> bool where H: Hasher, - H::Out: PartialEq + AsRef<[u8]> + PartialOrd, + H::Out: PartialEq + AsRef<[u8]>, P: IntoIterator, L: Into>, { @@ -259,13 +258,15 @@ where let hash_len = ::LENGTH; let mut combined = vec![0_u8; hash_len * 2]; + let mut position = leaf_index; + let mut width = number_of_leaves; let computed = proof.into_iter().fold(leaf_hash, |a, b| { - if a < b { - combined[..hash_len].copy_from_slice(&a.as_ref()); - combined[hash_len..].copy_from_slice(&b.as_ref()); - } else { + if position % 2 == 1 || position + 1 == width { combined[..hash_len].copy_from_slice(&b.as_ref()); combined[hash_len..].copy_from_slice(&a.as_ref()); + } else { + combined[..hash_len].copy_from_slice(&a.as_ref()); + combined[hash_len..].copy_from_slice(&b.as_ref()); } let hash = ::hash(&combined); #[cfg(feature = "debug")] @@ -276,6 +277,8 @@ where array_bytes::bytes2hex("", &hash.as_ref()), array_bytes::bytes2hex("", &combined.as_ref()) ); + position /= 2; + width = ((width - 1) / 2) + 1; hash }); @@ -294,7 +297,7 @@ fn merkelize_row( ) -> Result> where H: Hasher, - H::Out: AsRef<[u8]> + PartialOrd, + H::Out: AsRef<[u8]>, V: Visitor, I: Iterator, { @@ -320,13 +323,8 @@ where index += 2; match (a, b) { (Some(a), Some(b)) => { - if a < b { - combined[..hash_len].copy_from_slice(a.as_ref()); - combined[hash_len..].copy_from_slice(b.as_ref()); - } else { - combined[..hash_len].copy_from_slice(b.as_ref()); - combined[hash_len..].copy_from_slice(a.as_ref()); - } + combined[..hash_len].copy_from_slice(a.as_ref()); + combined[hash_len..].copy_from_slice(b.as_ref()); next.push(::hash(&combined)); }, @@ -419,12 +417,12 @@ mod tests { }; test( - "5842148bc6ebeb52af882a317c765fccd3ae80589b21a9b8cbf21abb630e46a7", + "aff1208e69c9e8be9b584b07ebac4e48a1ee9d15ce3afe20b77a4d29e4175aa3", vec!["a", "b", "c"], ); test( - "7b84bec68b13c39798c6c50e9e40a0b268e3c1634db8f4cb97314eb243d4c514", + "b8912f7269068901f231a965adfefbc10f0eedcfa61852b103efd54dac7db3d7", vec!["a", "b", "a"], ); @@ -434,7 +432,7 @@ mod tests { ); test( - "cc50382cfd3c9a617741e9a85efee8752b8feb95a2cbecd6365fb21366ce0c8c", + "fb3b3be94be9e983ba5e094c9c51a7d96a4fa2e5d8e891df00ca89ba05bb1239", vec!["a", "b", "c", "d", "e", "f", "g", "h", "i", "j"], ); } @@ -752,7 +750,7 @@ mod tests { "0xc26B34D375533fFc4c5276282Fa5D660F3d8cbcB", ]; let root: H256 = array_bytes::hex2array_unchecked( - "7b2c6eebec6e85b2e272325a11c31af71df52bc0534d2d4f903e0ced191f022e", + "72b0acd7c302a84f1f6b6cefe0ba7194b7398afb440e1b44a9dbbe270394ca53", ) .into(); @@ -797,11 +795,11 @@ mod tests { ) .into(), array_bytes::hex2array_unchecked( - "1fad92ed8d0504ef6c0231bbbeeda960a40693f297c64e87b582beb92ecfb00f" + "d02609d2bbdb28aa25f58b85afec937d5a4c85d37925bce6d0cf802f9d76ba79" ) .into(), array_bytes::hex2array_unchecked( - "0b84c852cbcf839d562d826fd935e1b37975ccaa419e1def8d219df4b83dcbf4" + "ae3f8991955ed884613b0a5f40295902eea0e0abe5858fc520b72959bc016d4e" ) .into(), ], From e7229f1f18ce672022a7698994593085bca5d5af Mon Sep 17 00:00:00 2001 From: Marcin S Date: Mon, 22 May 2023 11:43:51 -0400 Subject: [PATCH 527/558] Executor: Add `create_runtime_from_artifact_bytes` (#14184) --- client/executor/benches/bench.rs | 9 ++++- client/executor/wasmtime/src/lib.rs | 5 ++- client/executor/wasmtime/src/runtime.rs | 51 ++++++++++++++++++++++++- 3 files changed, 60 insertions(+), 5 deletions(-) diff --git a/client/executor/benches/bench.rs b/client/executor/benches/bench.rs index 772898b8c..2844b510e 100644 --- a/client/executor/benches/bench.rs +++ b/client/executor/benches/bench.rs @@ -188,12 +188,19 @@ fn bench_call_instance(c: &mut Criterion) { }, ), ( - "pooling_vanilla", + "pooling_vanilla_fresh", Method::Compiled { instantiation_strategy: InstantiationStrategy::Pooling, precompile: false, }, ), + ( + "pooling_vanilla_precompiled", + Method::Compiled { + instantiation_strategy: InstantiationStrategy::Pooling, + precompile: true, + }, + ), ( "pooling_cow_fresh", Method::Compiled { diff --git a/client/executor/wasmtime/src/lib.rs b/client/executor/wasmtime/src/lib.rs index c45478ec4..82e62b4a5 100644 --- a/client/executor/wasmtime/src/lib.rs +++ b/client/executor/wasmtime/src/lib.rs @@ -37,6 +37,7 @@ mod util; mod tests; pub use runtime::{ - create_runtime, create_runtime_from_artifact, prepare_runtime_artifact, Config, - DeterministicStackLimit, InstantiationStrategy, Semantics, + create_runtime, create_runtime_from_artifact, create_runtime_from_artifact_bytes, + prepare_runtime_artifact, Config, DeterministicStackLimit, InstantiationStrategy, Semantics, + WasmtimeRuntime, }; diff --git a/client/executor/wasmtime/src/runtime.rs b/client/executor/wasmtime/src/runtime.rs index c9a2c83e0..23b069870 100644 --- a/client/executor/wasmtime/src/runtime.rs +++ b/client/executor/wasmtime/src/runtime.rs @@ -435,7 +435,7 @@ pub struct DeterministicStackLimit { /// All of the CoW strategies (with `CopyOnWrite` suffix) are only supported when either: /// a) we're running on Linux, /// b) we're running on an Unix-like system and we're precompiling -/// our module beforehand. +/// our module beforehand and instantiating from a file. /// /// If the CoW variant of a strategy is unsupported the executor will /// fall back to the non-CoW equivalent. @@ -537,7 +537,7 @@ enum CodeSupplyMode<'a> { /// The runtime is instantiated using the given runtime blob. Fresh(RuntimeBlob), - /// The runtime is instantiated using a precompiled module. + /// The runtime is instantiated using a precompiled module at the given path. /// /// This assumes that the code is already prepared for execution and the same `Config` was /// used. @@ -545,6 +545,12 @@ enum CodeSupplyMode<'a> { /// We use a `Path` here instead of simply passing a byte slice to allow `wasmtime` to /// map the runtime's linear memory on supported platforms in a copy-on-write fashion. Precompiled(&'a Path), + + /// The runtime is instantiated using a precompiled module with the given bytes. + /// + /// This assumes that the code is already prepared for execution and the same `Config` was + /// used. + PrecompiledBytes(&'a [u8]), } /// Create a new `WasmtimeRuntime` given the code. This function performs translation from Wasm to @@ -589,6 +595,31 @@ where do_create_runtime::(CodeSupplyMode::Precompiled(compiled_artifact_path), config) } +/// The same as [`create_runtime`] but takes the bytes of a precompiled artifact, +/// which makes this function considerably faster than [`create_runtime`], +/// but slower than the more optimized [`create_runtime_from_artifact`]. +/// This is especially slow on non-Linux Unix systems. Useful in very niche cases. +/// +/// # Safety +/// +/// The caller must ensure that the compiled artifact passed here was: +/// 1) produced by [`prepare_runtime_artifact`], +/// 2) was not modified, +/// +/// Failure to adhere to these requirements might lead to crashes and arbitrary code execution. +/// +/// It is ok though if the compiled artifact was created by code of another version or with +/// different configuration flags. In such case the caller will receive an `Err` deterministically. +pub unsafe fn create_runtime_from_artifact_bytes( + compiled_artifact_bytes: &[u8], + config: Config, +) -> std::result::Result +where + H: HostFunctions, +{ + do_create_runtime::(CodeSupplyMode::PrecompiledBytes(compiled_artifact_bytes), config) +} + /// # Safety /// /// This is only unsafe if called with [`CodeSupplyMode::Artifact`]. See @@ -663,6 +694,22 @@ where (module, InternalInstantiationStrategy::Builtin) }, + CodeSupplyMode::PrecompiledBytes(compiled_artifact_bytes) => { + if let InstantiationStrategy::LegacyInstanceReuse = + config.semantics.instantiation_strategy + { + return Err(WasmError::Other("the legacy instance reuse instantiation strategy is incompatible with precompiled modules".into())); + } + + // SAFETY: The unsafety of `deserialize` is covered by this function. The + // responsibilities to maintain the invariants are passed to the caller. + // + // See [`create_runtime_from_artifact_bytes`] for more details. + let module = wasmtime::Module::deserialize(&engine, compiled_artifact_bytes) + .map_err(|e| WasmError::Other(format!("cannot deserialize module: {:#}", e)))?; + + (module, InternalInstantiationStrategy::Builtin) + }, }; let mut linker = wasmtime::Linker::new(&engine); From e0de1010b5664f11ac0fcab41d167107649d4d00 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bastian=20K=C3=B6cher?= Date: Mon, 22 May 2023 22:23:10 +0200 Subject: [PATCH 528/558] WarpSync: Show number of required peers in informant (#14190) This makes it for the user more obvious on what we are waiting and not just "waiting for peers". --- client/network/common/src/sync/warp.rs | 5 +++-- client/network/sync/src/lib.rs | 8 ++++++-- 2 files changed, 9 insertions(+), 4 deletions(-) diff --git a/client/network/common/src/sync/warp.rs b/client/network/common/src/sync/warp.rs index aef257af4..37a6e62c5 100644 --- a/client/network/common/src/sync/warp.rs +++ b/client/network/common/src/sync/warp.rs @@ -72,7 +72,7 @@ pub trait WarpSyncProvider: Send + Sync { #[derive(Clone, Eq, PartialEq, Debug)] pub enum WarpSyncPhase { /// Waiting for peers to connect. - AwaitingPeers, + AwaitingPeers { required_peers: usize }, /// Waiting for target block to be received. AwaitingTargetBlock, /// Downloading and verifying grandpa warp proofs. @@ -90,7 +90,8 @@ pub enum WarpSyncPhase { impl fmt::Display for WarpSyncPhase { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { match self { - Self::AwaitingPeers => write!(f, "Waiting for peers"), + Self::AwaitingPeers { required_peers } => + write!(f, "Waiting for {required_peers} peers to be connected"), Self::AwaitingTargetBlock => write!(f, "Waiting for target block to be received"), Self::DownloadingWarpProofs => write!(f, "Downloading finality proofs"), Self::DownloadingTargetBlock => write!(f, "Downloading target block"), diff --git a/client/network/sync/src/lib.rs b/client/network/sync/src/lib.rs index 3f1cbebd5..fbdb275a4 100644 --- a/client/network/sync/src/lib.rs +++ b/client/network/sync/src/lib.rs @@ -515,8 +515,12 @@ where phase: WarpSyncPhase::DownloadingBlocks(gap_sync.best_queued_number), total_bytes: 0, }), - (None, SyncMode::Warp, _) => - Some(WarpSyncProgress { phase: WarpSyncPhase::AwaitingPeers, total_bytes: 0 }), + (None, SyncMode::Warp, _) => Some(WarpSyncProgress { + phase: WarpSyncPhase::AwaitingPeers { + required_peers: MIN_PEERS_TO_START_WARP_SYNC, + }, + total_bytes: 0, + }), (Some(sync), _, _) => Some(sync.progress()), _ => None, }; From 5032b8dbb322ac426f0a0b3a5618bdf01b1d7ff0 Mon Sep 17 00:00:00 2001 From: Sergej Sakac <73715684+Szegoo@users.noreply.github.com> Date: Tue, 23 May 2023 08:56:10 +0200 Subject: [PATCH 529/558] BREAKING - Try-runtime: Use proper error types (#13993) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Try-state: DispatchResult as return type * try_state for the rest of the pallets * pre_upgrade * post_upgrade * try_runtime_upgrade * fixes * bags-list fix * fix * update test * warning fix * ... * final fixes 🤞 * warning.. * frame-support * warnings * Update frame/staking/src/migrations.rs Co-authored-by: Liam Aharon * fix * fix warning * nit fix * merge fixes * small fix * should be good now * missed these ones * introduce TryRuntimeError and TryRuntimeResult * fixes * fix * removed TryRuntimeResult & made some fixes * fix testsg * tests passing * unnecessary imports * Update frame/assets/src/migration.rs Co-authored-by: Keith Yeung --------- Co-authored-by: Liam Aharon Co-authored-by: Keith Yeung --- frame/assets/src/migration.rs | 26 +++-- frame/bags-list/src/lib.rs | 9 +- frame/bags-list/src/list/mod.rs | 16 +-- frame/bags-list/src/list/tests.rs | 11 +- frame/bags-list/src/migrations.rs | 9 +- frame/collective/src/lib.rs | 105 +++++++++--------- frame/contracts/src/migration.rs | 19 ++-- frame/democracy/src/migrations.rs | 14 +-- .../election-provider-multi-phase/src/lib.rs | 32 ++++-- frame/election-provider-support/src/lib.rs | 5 +- frame/elections-phragmen/src/lib.rs | 33 +++--- frame/executive/src/lib.rs | 5 +- frame/fast-unstake/src/lib.rs | 7 +- frame/fast-unstake/src/migrations.rs | 19 +++- frame/multisig/src/migrations.rs | 4 +- frame/nfts/src/migration.rs | 11 +- frame/nomination-pools/src/lib.rs | 73 ++++++++---- frame/nomination-pools/src/migration.rs | 67 ++++++----- frame/nomination-pools/src/tests.rs | 5 +- frame/offences/src/migration.rs | 6 +- frame/preimage/src/migration.rs | 13 ++- frame/referenda/src/migration.rs | 16 +-- frame/scheduler/src/migration.rs | 27 +++-- frame/staking/src/migrations.rs | 29 +++-- frame/staking/src/pallet/impls.rs | 44 +++++--- frame/staking/src/pallet/mod.rs | 2 +- .../procedural/src/pallet/expand/hooks.rs | 10 +- frame/support/src/dispatch.rs | 10 +- frame/support/src/migrations.rs | 6 +- frame/support/src/traits/hooks.rs | 25 +++-- frame/support/src/traits/try_runtime.rs | 9 +- frame/support/test/tests/pallet.rs | 17 ++- primitives/runtime/src/lib.rs | 3 + utils/frame/try-runtime/cli/src/lib.rs | 6 +- 34 files changed, 418 insertions(+), 275 deletions(-) diff --git a/frame/assets/src/migration.rs b/frame/assets/src/migration.rs index 90400ef5b..d854a64af 100644 --- a/frame/assets/src/migration.rs +++ b/frame/assets/src/migration.rs @@ -18,6 +18,9 @@ use super::*; use frame_support::{log, traits::OnRuntimeUpgrade}; +#[cfg(feature = "try-runtime")] +use sp_runtime::TryRuntimeError; + pub mod v1 { use frame_support::{pallet_prelude::*, weights::Weight}; @@ -92,7 +95,7 @@ pub mod v1 { } #[cfg(feature = "try-runtime")] - fn pre_upgrade() -> Result, &'static str> { + fn pre_upgrade() -> Result, TryRuntimeError> { frame_support::ensure!( Pallet::::on_chain_storage_version() == 0, "must upgrade linearly" @@ -102,13 +105,13 @@ pub mod v1 { } #[cfg(feature = "try-runtime")] - fn post_upgrade(prev_count: Vec) -> Result<(), &'static str> { + fn post_upgrade(prev_count: Vec) -> Result<(), TryRuntimeError> { let prev_count: u32 = Decode::decode(&mut prev_count.as_slice()).expect( "the state parameter should be something that was generated by pre_upgrade", ); let post_count = Asset::::iter().count() as u32; - assert_eq!( - prev_count, post_count, + ensure!( + prev_count == post_count, "the asset count before and after the migration should be the same" ); @@ -116,17 +119,18 @@ pub mod v1 { let onchain_version = Pallet::::on_chain_storage_version(); frame_support::ensure!(current_version == 1, "must_upgrade"); - assert_eq!( - current_version, onchain_version, + ensure!( + current_version == onchain_version, "after migration, the current_version and onchain_version should be the same" ); - Asset::::iter().for_each(|(_id, asset)| { - assert!( + Asset::::iter().try_for_each(|(_id, asset)| -> Result<(), TryRuntimeError> { + ensure!( asset.status == AssetStatus::Live || asset.status == AssetStatus::Frozen, - "assets should only be live or frozen. None should be in destroying status, or undefined state" - ) - }); + "assets should only be live or frozen. None should be in destroying status, or undefined state" + ); + Ok(()) + })?; Ok(()) } } diff --git a/frame/bags-list/src/lib.rs b/frame/bags-list/src/lib.rs index d4d54b9a1..156c52cc8 100644 --- a/frame/bags-list/src/lib.rs +++ b/frame/bags-list/src/lib.rs @@ -59,6 +59,9 @@ use frame_system::ensure_signed; use sp_runtime::traits::{AtLeast32BitUnsigned, Bounded, StaticLookup}; use sp_std::prelude::*; +#[cfg(any(test, feature = "try-runtime", feature = "fuzz"))] +use sp_runtime::TryRuntimeError; + #[cfg(any(feature = "runtime-benchmarks", test))] mod benchmarks; @@ -267,7 +270,7 @@ pub mod pallet { } #[cfg(feature = "try-runtime")] - fn try_state(_: BlockNumberFor) -> Result<(), &'static str> { + fn try_state(_: BlockNumberFor) -> Result<(), TryRuntimeError> { >::try_state() } } @@ -275,7 +278,7 @@ pub mod pallet { #[cfg(any(test, feature = "try-runtime", feature = "fuzz"))] impl, I: 'static> Pallet { - pub fn do_try_state() -> Result<(), &'static str> { + pub fn do_try_state() -> Result<(), TryRuntimeError> { List::::do_try_state() } } @@ -355,7 +358,7 @@ impl, I: 'static> SortedListProvider for Pallet } #[cfg(feature = "try-runtime")] - fn try_state() -> Result<(), &'static str> { + fn try_state() -> Result<(), TryRuntimeError> { Self::do_try_state() } diff --git a/frame/bags-list/src/list/mod.rs b/frame/bags-list/src/list/mod.rs index f667f4c10..d8626080e 100644 --- a/frame/bags-list/src/list/mod.rs +++ b/frame/bags-list/src/list/mod.rs @@ -42,6 +42,9 @@ use sp_std::{ prelude::*, }; +#[cfg(any(test, feature = "try-runtime", feature = "fuzz"))] +use sp_runtime::TryRuntimeError; + #[derive(Debug, PartialEq, Eq, Encode, Decode, MaxEncodedLen, TypeInfo, PalletError)] pub enum ListError { /// A duplicate id has been detected. @@ -512,11 +515,11 @@ impl, I: 'static> List { /// * and sanity-checks all bags and nodes. This will cascade down all the checks and makes sure /// all bags and nodes are checked per *any* update to `List`. #[cfg(any(test, feature = "try-runtime", feature = "fuzz"))] - pub(crate) fn do_try_state() -> Result<(), &'static str> { + pub(crate) fn do_try_state() -> Result<(), TryRuntimeError> { let mut seen_in_list = BTreeSet::new(); ensure!( Self::iter().map(|node| node.id).all(|id| seen_in_list.insert(id)), - "duplicate identified", + "duplicate identified" ); let iter_count = Self::iter().count() as u32; @@ -750,7 +753,7 @@ impl, I: 'static> Bag { /// * Ensures tail has no next. /// * Ensures there are no loops, traversal from head to tail is correct. #[cfg(any(test, feature = "try-runtime", feature = "fuzz"))] - fn do_try_state(&self) -> Result<(), &'static str> { + fn do_try_state(&self) -> Result<(), TryRuntimeError> { frame_support::ensure!( self.head() .map(|head| head.prev().is_none()) @@ -895,15 +898,12 @@ impl, I: 'static> Node { } #[cfg(any(test, feature = "try-runtime", feature = "fuzz"))] - fn do_try_state(&self) -> Result<(), &'static str> { + fn do_try_state(&self) -> Result<(), TryRuntimeError> { let expected_bag = Bag::::get(self.bag_upper).ok_or("bag not found for node")?; let id = self.id(); - frame_support::ensure!( - expected_bag.contains(id), - "node does not exist in the expected bag" - ); + frame_support::ensure!(expected_bag.contains(id), "node does not exist in the bag"); let non_terminal_check = !self.is_terminal() && expected_bag.head.as_ref() != Some(id) && diff --git a/frame/bags-list/src/list/tests.rs b/frame/bags-list/src/list/tests.rs index f5afdc24f..fd4ad8f89 100644 --- a/frame/bags-list/src/list/tests.rs +++ b/frame/bags-list/src/list/tests.rs @@ -22,6 +22,7 @@ use crate::{ }; use frame_election_provider_support::{SortedListProvider, VoteWeight}; use frame_support::{assert_ok, assert_storage_noop}; +use sp_runtime::TryRuntimeError; fn node( id: AccountId, @@ -359,7 +360,10 @@ mod list { // make sure there are no duplicates. ExtBuilder::default().build_and_execute_no_post_check(|| { Bag::::get(10).unwrap().insert_unchecked(2, 10); - assert_eq!(List::::do_try_state(), Err("duplicate identified")); + assert_eq!( + List::::do_try_state(), + TryRuntimeError::Other("duplicate identified").into() + ); }); // ensure count is in sync with `ListNodes::count()`. @@ -373,7 +377,10 @@ mod list { CounterForListNodes::::mutate(|counter| *counter += 1); assert_eq!(crate::ListNodes::::count(), 5); - assert_eq!(List::::do_try_state(), Err("iter_count != stored_count")); + assert_eq!( + List::::do_try_state(), + TryRuntimeError::Other("iter_count != stored_count").into() + ); }); } diff --git a/frame/bags-list/src/migrations.rs b/frame/bags-list/src/migrations.rs index 5f9bb8f73..7df63a6a4 100644 --- a/frame/bags-list/src/migrations.rs +++ b/frame/bags-list/src/migrations.rs @@ -24,6 +24,9 @@ use frame_support::traits::OnRuntimeUpgrade; #[cfg(feature = "try-runtime")] use frame_support::ensure; +#[cfg(feature = "try-runtime")] +use sp_runtime::TryRuntimeError; + #[cfg(feature = "try-runtime")] use sp_std::vec::Vec; @@ -35,7 +38,7 @@ impl, I: 'static> OnRuntimeUpgrade for CheckCounterPrefix Result, &'static str> { + fn pre_upgrade() -> Result, TryRuntimeError> { // The old explicit storage item. #[frame_support::storage_alias] type CounterForListNodes, I: 'static> = @@ -88,7 +91,7 @@ mod old { pub struct AddScore, I: 'static = ()>(sp_std::marker::PhantomData<(T, I)>); impl, I: 'static> OnRuntimeUpgrade for AddScore { #[cfg(feature = "try-runtime")] - fn pre_upgrade() -> Result, &'static str> { + fn pre_upgrade() -> Result, TryRuntimeError> { // The list node data should be corrupt at this point, so this is zero. ensure!(crate::ListNodes::::iter().count() == 0, "list node data is not corrupt"); // We can use the helper `old::ListNode` to get the existing data. @@ -119,7 +122,7 @@ impl, I: 'static> OnRuntimeUpgrade for AddScore { } #[cfg(feature = "try-runtime")] - fn post_upgrade(node_count_before: Vec) -> Result<(), &'static str> { + fn post_upgrade(node_count_before: Vec) -> Result<(), TryRuntimeError> { let node_count_before: u32 = Decode::decode(&mut node_count_before.as_slice()) .expect("the state parameter should be something that was generated by pre_upgrade"); // Now the list node data is not corrupt anymore. diff --git a/frame/collective/src/lib.rs b/frame/collective/src/lib.rs index 12917f5bd..f904b42af 100644 --- a/frame/collective/src/lib.rs +++ b/frame/collective/src/lib.rs @@ -60,6 +60,9 @@ use frame_support::{ weights::Weight, }; +#[cfg(any(feature = "try-runtime", test))] +use sp_runtime::TryRuntimeError; + #[cfg(test)] mod tests; @@ -346,9 +349,8 @@ pub mod pallet { #[pallet::hooks] impl, I: 'static> Hooks> for Pallet { #[cfg(feature = "try-runtime")] - fn try_state(_n: BlockNumberFor) -> Result<(), &'static str> { - Self::do_try_state()?; - Ok(()) + fn try_state(_n: BlockNumberFor) -> Result<(), TryRuntimeError> { + Self::do_try_state() } } @@ -967,77 +969,78 @@ impl, I: 'static> Pallet { /// Looking at prime account: /// * The prime account must be a member of the collective. #[cfg(any(feature = "try-runtime", test))] - fn do_try_state() -> DispatchResult { - Self::proposals().into_iter().try_for_each(|proposal| -> DispatchResult { - ensure!( - Self::proposal_of(proposal).is_some(), - DispatchError::Other( + fn do_try_state() -> Result<(), TryRuntimeError> { + Self::proposals() + .into_iter() + .try_for_each(|proposal| -> Result<(), TryRuntimeError> { + ensure!( + Self::proposal_of(proposal).is_some(), "Proposal hash from `Proposals` is not found inside the `ProposalOf` mapping." - ) - ); - Ok(()) - })?; + ); + Ok(()) + })?; ensure!( Self::proposals().into_iter().count() <= Self::proposal_count() as usize, - DispatchError::Other("The actual number of proposals is greater than `ProposalCount`") + "The actual number of proposals is greater than `ProposalCount`" ); ensure!( Self::proposals().into_iter().count() == >::iter_keys().count(), - DispatchError::Other("Proposal count inside `Proposals` is not equal to the proposal count in `ProposalOf`") + "Proposal count inside `Proposals` is not equal to the proposal count in `ProposalOf`" ); - Self::proposals().into_iter().try_for_each(|proposal| -> DispatchResult { - if let Some(votes) = Self::voting(proposal) { - let ayes = votes.ayes.len(); - let nays = votes.nays.len(); - - ensure!( - ayes.saturating_add(nays) <= T::MaxMembers::get() as usize, - DispatchError::Other("The sum of ayes and nays is greater than `MaxMembers`") - ); - } - Ok(()) - })?; + Self::proposals() + .into_iter() + .try_for_each(|proposal| -> Result<(), TryRuntimeError> { + if let Some(votes) = Self::voting(proposal) { + let ayes = votes.ayes.len(); + let nays = votes.nays.len(); + + ensure!( + ayes.saturating_add(nays) <= T::MaxMembers::get() as usize, + "The sum of ayes and nays is greater than `MaxMembers`" + ); + } + Ok(()) + })?; let mut proposal_indices = vec![]; - Self::proposals().into_iter().try_for_each(|proposal| -> DispatchResult { - if let Some(votes) = Self::voting(proposal) { - let proposal_index = votes.index; - ensure!( - !proposal_indices.contains(&proposal_index), - DispatchError::Other("The proposal index is not unique.") - ); - proposal_indices.push(proposal_index); - } - Ok(()) - })?; + Self::proposals() + .into_iter() + .try_for_each(|proposal| -> Result<(), TryRuntimeError> { + if let Some(votes) = Self::voting(proposal) { + let proposal_index = votes.index; + ensure!( + !proposal_indices.contains(&proposal_index), + "The proposal index is not unique." + ); + proposal_indices.push(proposal_index); + } + Ok(()) + })?; - >::iter_keys().try_for_each(|proposal_hash| -> DispatchResult { - ensure!( - Self::proposals().contains(&proposal_hash), - DispatchError::Other( + >::iter_keys().try_for_each( + |proposal_hash| -> Result<(), TryRuntimeError> { + ensure!( + Self::proposals().contains(&proposal_hash), "`Proposals` doesn't contain the proposal hash from the `Voting` storage map." - ) - ); - Ok(()) - })?; + ); + Ok(()) + }, + )?; ensure!( Self::members().len() <= T::MaxMembers::get() as usize, - DispatchError::Other("The member count is greater than `MaxMembers`.") + "The member count is greater than `MaxMembers`." ); ensure!( Self::members().windows(2).all(|members| members[0] <= members[1]), - DispatchError::Other("The members are not sorted by value.") + "The members are not sorted by value." ); if let Some(prime) = Self::prime() { - ensure!( - Self::members().contains(&prime), - DispatchError::Other("Prime account is not a member.") - ); + ensure!(Self::members().contains(&prime), "Prime account is not a member."); } Ok(()) diff --git a/frame/contracts/src/migration.rs b/frame/contracts/src/migration.rs index 96a4c3203..0b7c094dd 100644 --- a/frame/contracts/src/migration.rs +++ b/frame/contracts/src/migration.rs @@ -28,6 +28,9 @@ use frame_support::{ use sp_runtime::traits::Saturating; use sp_std::{marker::PhantomData, prelude::*}; +#[cfg(feature = "try-runtime")] +use sp_runtime::TryRuntimeError; + /// Performs all necessary migrations based on `StorageVersion`. pub struct Migration(PhantomData); impl OnRuntimeUpgrade for Migration { @@ -66,7 +69,7 @@ impl OnRuntimeUpgrade for Migration { } #[cfg(feature = "try-runtime")] - fn pre_upgrade() -> Result, &'static str> { + fn pre_upgrade() -> Result, TryRuntimeError> { let version = >::on_chain_storage_version(); if version == 7 { @@ -77,7 +80,7 @@ impl OnRuntimeUpgrade for Migration { } #[cfg(feature = "try-runtime")] - fn post_upgrade(state: Vec) -> Result<(), &'static str> { + fn post_upgrade(state: Vec) -> Result<(), TryRuntimeError> { let version = Decode::decode(&mut state.as_ref()).map_err(|_| "Cannot decode version")?; post_checks::post_upgrade::(version) } @@ -355,7 +358,7 @@ mod v8 { } #[cfg(feature = "try-runtime")] - pub fn pre_upgrade() -> Result<(), &'static str> { + pub fn pre_upgrade() -> Result<(), TryRuntimeError> { use frame_support::traits::ReservableCurrency; for (key, value) in ContractInfoOf::>::iter() { let reserved = T::Currency::reserved_balance(&key); @@ -418,7 +421,7 @@ mod post_checks { type ContractInfoOf = StorageMap, Twox64Concat, ::AccountId, V>; - pub fn post_upgrade(old_version: StorageVersion) -> Result<(), &'static str> { + pub fn post_upgrade(old_version: StorageVersion) -> Result<(), TryRuntimeError> { if old_version < 7 { return Ok(()) } @@ -434,7 +437,7 @@ mod post_checks { Ok(()) } - fn v8() -> Result<(), &'static str> { + fn v8() -> Result<(), TryRuntimeError> { use frame_support::traits::ReservableCurrency; for (key, value) in ContractInfoOf::>::iter() { let reserved = T::Currency::reserved_balance(&key); @@ -455,13 +458,13 @@ mod post_checks { storage_bytes.saturating_accrue(len); storage_items.saturating_accrue(1); } - ensure!(storage_bytes == value.storage_bytes, "Storage bytes do not match.",); - ensure!(storage_items == value.storage_items, "Storage items do not match.",); + ensure!(storage_bytes == value.storage_bytes, "Storage bytes do not match."); + ensure!(storage_items == value.storage_items, "Storage items do not match."); } Ok(()) } - fn v9() -> Result<(), &'static str> { + fn v9() -> Result<(), TryRuntimeError> { for value in CodeStorage::::iter_values() { ensure!( value.determinism == Determinism::Enforced, diff --git a/frame/democracy/src/migrations.rs b/frame/democracy/src/migrations.rs index fe2e445bd..397f42dce 100644 --- a/frame/democracy/src/migrations.rs +++ b/frame/democracy/src/migrations.rs @@ -61,12 +61,12 @@ pub mod v1 { impl> OnRuntimeUpgrade for Migration { #[cfg(feature = "try-runtime")] - fn pre_upgrade() -> Result, &'static str> { - assert_eq!(StorageVersion::get::>(), 0, "can only upgrade from version 0"); + fn pre_upgrade() -> Result, sp_runtime::TryRuntimeError> { + ensure!(StorageVersion::get::>() == 0, "can only upgrade from version 0"); let props_count = v0::PublicProps::::get().len(); log::info!(target: TARGET, "{} public proposals will be migrated.", props_count,); - ensure!(props_count <= T::MaxProposals::get() as usize, "too many proposals"); + ensure!(props_count <= T::MaxProposals::get() as usize, Error::::TooMany); let referenda_count = v0::ReferendumInfoOf::::iter().count(); log::info!(target: TARGET, "{} referenda will be migrated.", referenda_count); @@ -133,15 +133,15 @@ pub mod v1 { } #[cfg(feature = "try-runtime")] - fn post_upgrade(state: Vec) -> Result<(), &'static str> { - assert_eq!(StorageVersion::get::>(), 1, "must upgrade"); + fn post_upgrade(state: Vec) -> Result<(), sp_runtime::TryRuntimeError> { + ensure!(StorageVersion::get::>() == 1, "must upgrade"); let (old_props_count, old_ref_count): (u32, u32) = Decode::decode(&mut &state[..]).expect("pre_upgrade provides a valid state; qed"); let new_props_count = crate::PublicProps::::get().len() as u32; - assert_eq!(new_props_count, old_props_count, "must migrate all public proposals"); + ensure!(new_props_count == old_props_count, "must migrate all public proposals"); let new_ref_count = crate::ReferendumInfoOf::::iter().count() as u32; - assert_eq!(new_ref_count, old_ref_count, "must migrate all referenda"); + ensure!(new_ref_count == old_ref_count, "must migrate all referenda"); log::info!( target: TARGET, diff --git a/frame/election-provider-multi-phase/src/lib.rs b/frame/election-provider-multi-phase/src/lib.rs index c5247e3c2..98fa88034 100644 --- a/frame/election-provider-multi-phase/src/lib.rs +++ b/frame/election-provider-multi-phase/src/lib.rs @@ -257,6 +257,9 @@ use sp_runtime::{ }; use sp_std::prelude::*; +#[cfg(feature = "try-runtime")] +use sp_runtime::TryRuntimeError; + #[cfg(feature = "runtime-benchmarks")] mod benchmarking; #[cfg(test)] @@ -883,7 +886,7 @@ pub mod pallet { } #[cfg(feature = "try-runtime")] - fn try_state(_n: T::BlockNumber) -> Result<(), &'static str> { + fn try_state(_n: T::BlockNumber) -> Result<(), TryRuntimeError> { Self::do_try_state() } } @@ -1579,7 +1582,7 @@ impl Pallet { #[cfg(feature = "try-runtime")] impl Pallet { - fn do_try_state() -> Result<(), &'static str> { + fn do_try_state() -> Result<(), TryRuntimeError> { Self::try_state_snapshot()?; Self::try_state_signed_submissions_map()?; Self::try_state_phase_off() @@ -1588,7 +1591,7 @@ impl Pallet { // [`Snapshot`] state check. Invariants: // - [`DesiredTargets`] exists if and only if [`Snapshot`] is present. // - [`SnapshotMetadata`] exist if and only if [`Snapshot`] is present. - fn try_state_snapshot() -> Result<(), &'static str> { + fn try_state_snapshot() -> Result<(), TryRuntimeError> { if >::exists() && >::exists() && >::exists() @@ -1600,7 +1603,7 @@ impl Pallet { { Ok(()) } else { - Err("If snapshot exists, metadata and desired targets should be set too. Otherwise, none should be set.") + Err("If snapshot exists, metadata and desired targets should be set too. Otherwise, none should be set.".into()) } } @@ -1608,28 +1611,34 @@ impl Pallet { // - All [`SignedSubmissionIndices`] are present in [`SignedSubmissionsMap`], and no more; // - [`SignedSubmissionNextIndex`] is not present in [`SignedSubmissionsMap`]; // - [`SignedSubmissionIndices`] is sorted by election score. - fn try_state_signed_submissions_map() -> Result<(), &'static str> { + fn try_state_signed_submissions_map() -> Result<(), TryRuntimeError> { let mut last_score: ElectionScore = Default::default(); let indices = >::get(); for (i, indice) in indices.iter().enumerate() { let submission = >::get(indice.2); if submission.is_none() { - return Err("All signed submissions indices must be part of the submissions map") + return Err( + "All signed submissions indices must be part of the submissions map".into() + ) } if i == 0 { last_score = indice.0 } else { if last_score.strict_threshold_better(indice.0, Perbill::zero()) { - return Err("Signed submission indices vector must be ordered by election score") + return Err( + "Signed submission indices vector must be ordered by election score".into() + ) } last_score = indice.0; } } if >::iter().nth(indices.len()).is_some() { - return Err("Signed submissions map length should be the same as the indices vec length") + return Err( + "Signed submissions map length should be the same as the indices vec length".into() + ) } match >::get() { @@ -1637,7 +1646,8 @@ impl Pallet { next => if >::get(next).is_some() { return Err( - "The next submissions index should not be in the submissions maps already", + "The next submissions index should not be in the submissions maps already" + .into(), ) } else { Ok(()) @@ -1647,12 +1657,12 @@ impl Pallet { // [`Phase::Off`] state check. Invariants: // - If phase is `Phase::Off`, [`Snapshot`] must be none. - fn try_state_phase_off() -> Result<(), &'static str> { + fn try_state_phase_off() -> Result<(), TryRuntimeError> { match Self::current_phase().is_off() { false => Ok(()), true => if >::get().is_some() { - Err("Snapshot must be none when in Phase::Off") + Err("Snapshot must be none when in Phase::Off".into()) } else { Ok(()) }, diff --git a/frame/election-provider-support/src/lib.rs b/frame/election-provider-support/src/lib.rs index ee0e41a90..6e6d3341b 100644 --- a/frame/election-provider-support/src/lib.rs +++ b/frame/election-provider-support/src/lib.rs @@ -189,6 +189,9 @@ pub use sp_npos_elections::{ }; pub use traits::NposSolution; +#[cfg(feature = "try-runtime")] +use sp_runtime::TryRuntimeError; + // re-export for the solution macro, with the dependencies of the macro. #[doc(hidden)] pub use codec; @@ -564,7 +567,7 @@ pub trait SortedListProvider { /// Check internal state of the list. Only meant for debugging. #[cfg(feature = "try-runtime")] - fn try_state() -> Result<(), &'static str>; + fn try_state() -> Result<(), TryRuntimeError>; /// If `who` changes by the returned amount they are guaranteed to have a worst case change /// in their list position. diff --git a/frame/elections-phragmen/src/lib.rs b/frame/elections-phragmen/src/lib.rs index 1d7c79fe3..33a8634cb 100644 --- a/frame/elections-phragmen/src/lib.rs +++ b/frame/elections-phragmen/src/lib.rs @@ -115,6 +115,9 @@ use sp_runtime::{ }; use sp_std::{cmp::Ordering, prelude::*}; +#[cfg(any(feature = "try-runtime", test))] +use sp_runtime::TryRuntimeError; + mod benchmarking; pub mod weights; pub use weights::WeightInfo; @@ -327,7 +330,7 @@ pub mod pallet { } #[cfg(feature = "try-runtime")] - fn try_state(_n: T::BlockNumber) -> Result<(), &'static str> { + fn try_state(_n: T::BlockNumber) -> Result<(), TryRuntimeError> { Self::do_try_state() } } @@ -1193,7 +1196,7 @@ impl ContainsLengthBound for Pallet { #[cfg(any(feature = "try-runtime", test))] impl Pallet { - fn do_try_state() -> Result<(), &'static str> { + fn do_try_state() -> Result<(), TryRuntimeError> { Self::try_state_members()?; Self::try_state_runners_up()?; Self::try_state_candidates()?; @@ -1204,20 +1207,20 @@ impl Pallet { /// [`Members`] state checks. Invariants: /// - Members are always sorted based on account ID. - fn try_state_members() -> Result<(), &'static str> { + fn try_state_members() -> Result<(), TryRuntimeError> { let mut members = Members::::get().clone(); members.sort_by_key(|m| m.who.clone()); if Members::::get() == members { Ok(()) } else { - Err("try_state checks: Members must be always sorted by account ID") + Err("try_state checks: Members must be always sorted by account ID".into()) } } // [`RunnersUp`] state checks. Invariants: // - Elements are sorted based on weight (worst to best). - fn try_state_runners_up() -> Result<(), &'static str> { + fn try_state_runners_up() -> Result<(), TryRuntimeError> { let mut sorted = RunnersUp::::get(); // worst stake first sorted.sort_by(|a, b| a.stake.cmp(&b.stake)); @@ -1225,27 +1228,28 @@ impl Pallet { if RunnersUp::::get() == sorted { Ok(()) } else { - Err("try_state checks: Runners Up must always be sorted by stake (worst to best)") + Err("try_state checks: Runners Up must always be sorted by stake (worst to best)" + .into()) } } // [`Candidates`] state checks. Invariants: // - Always sorted based on account ID. - fn try_state_candidates() -> Result<(), &'static str> { + fn try_state_candidates() -> Result<(), TryRuntimeError> { let mut candidates = Candidates::::get().clone(); candidates.sort_by_key(|(c, _)| c.clone()); if Candidates::::get() == candidates { Ok(()) } else { - Err("try_state checks: Candidates must be always sorted by account ID") + Err("try_state checks: Candidates must be always sorted by account ID".into()) } } // [`Candidates`] and [`RunnersUp`] state checks. Invariants: // - Candidates and runners-ups sets are disjoint. - fn try_state_candidates_runners_up_disjoint() -> Result<(), &'static str> { + fn try_state_candidates_runners_up_disjoint() -> Result<(), TryRuntimeError> { match Self::intersects(&Self::candidates_ids(), &Self::runners_up_ids()) { - true => Err("Candidates and runners up sets should always be disjoint"), + true => Err("Candidates and runners up sets should always be disjoint".into()), false => Ok(()), } } @@ -1253,11 +1257,12 @@ impl Pallet { // [`Members`], [`Candidates`] and [`RunnersUp`] state checks. Invariants: // - Members and candidates sets are disjoint; // - Members and runners-ups sets are disjoint. - fn try_state_members_disjoint() -> Result<(), &'static str> { + fn try_state_members_disjoint() -> Result<(), TryRuntimeError> { match Self::intersects(&Pallet::::members_ids(), &Self::candidates_ids()) && Self::intersects(&Pallet::::members_ids(), &Self::runners_up_ids()) { - true => Err("Members set should be disjoint from candidates and runners-up sets"), + true => + Err("Members set should be disjoint from candidates and runners-up sets".into()), false => Ok(()), } } @@ -1265,14 +1270,14 @@ impl Pallet { // [`Members`], [`RunnersUp`] and approval stake state checks. Invariants: // - Selected members should have approval stake; // - Selected RunnersUp should have approval stake. - fn try_state_members_approval_stake() -> Result<(), &'static str> { + fn try_state_members_approval_stake() -> Result<(), TryRuntimeError> { match Members::::get() .iter() .chain(RunnersUp::::get().iter()) .all(|s| s.stake != BalanceOf::::zero()) { true => Ok(()), - false => Err("Members and RunnersUp must have approval stake"), + false => Err("Members and RunnersUp must have approval stake".into()), } } diff --git a/frame/executive/src/lib.rs b/frame/executive/src/lib.rs index fd76fefad..9ec78f254 100644 --- a/frame/executive/src/lib.rs +++ b/frame/executive/src/lib.rs @@ -137,6 +137,9 @@ use sp_runtime::{ }; use sp_std::{marker::PhantomData, prelude::*}; +#[cfg(feature = "try-runtime")] +use sp_runtime::TryRuntimeError; + #[allow(dead_code)] const LOG_TARGET: &str = "runtime::executive"; @@ -338,7 +341,7 @@ where /// `true`. Also, if set to `true`, it runs the `pre_upgrade` and `post_upgrade` hooks. pub fn try_runtime_upgrade( checks: frame_try_runtime::UpgradeCheckSelect, - ) -> Result { + ) -> Result { if checks.try_state() { let _guard = frame_support::StorageNoopGuard::default(); >::try_state( diff --git a/frame/fast-unstake/src/lib.rs b/frame/fast-unstake/src/lib.rs index 3e79bf407..f8a8e0034 100644 --- a/frame/fast-unstake/src/lib.rs +++ b/frame/fast-unstake/src/lib.rs @@ -91,6 +91,9 @@ pub mod pallet { use sp_std::{prelude::*, vec::Vec}; pub use weights::WeightInfo; + #[cfg(feature = "try-runtime")] + use sp_runtime::TryRuntimeError; + #[derive(scale_info::TypeInfo, codec::Encode, codec::Decode, codec::MaxEncodedLen)] #[codec(mel_bound(T: Config))] #[scale_info(skip_type_params(T))] @@ -228,10 +231,10 @@ pub mod pallet { } #[cfg(feature = "try-runtime")] - fn try_state(_n: T::BlockNumber) -> Result<(), &'static str> { + fn try_state(_n: T::BlockNumber) -> Result<(), TryRuntimeError> { // ensure that the value of `ErasToCheckPerBlock` is less than // `T::MaxErasToCheckPerBlock`. - assert!( + ensure!( ErasToCheckPerBlock::::get() <= T::MaxErasToCheckPerBlock::get(), "the value of `ErasToCheckPerBlock` is greater than `T::MaxErasToCheckPerBlock`", ); diff --git a/frame/fast-unstake/src/migrations.rs b/frame/fast-unstake/src/migrations.rs index e5ef91929..564388407 100644 --- a/frame/fast-unstake/src/migrations.rs +++ b/frame/fast-unstake/src/migrations.rs @@ -25,6 +25,11 @@ pub mod v1 { use sp_staking::EraIndex; use sp_std::prelude::*; + #[cfg(feature = "try-runtime")] + use frame_support::ensure; + #[cfg(feature = "try-runtime")] + use sp_runtime::TryRuntimeError; + pub struct MigrateToV1(sp_std::marker::PhantomData); impl OnRuntimeUpgrade for MigrateToV1 { fn on_runtime_upgrade() -> Weight { @@ -65,14 +70,20 @@ pub mod v1 { } #[cfg(feature = "try-runtime")] - fn pre_upgrade() -> Result, &'static str> { - assert_eq!(Pallet::::on_chain_storage_version(), 0); + fn pre_upgrade() -> Result, TryRuntimeError> { + ensure!( + Pallet::::on_chain_storage_version() == 0, + "The onchain storage version must be zero for the migration to execute." + ); Ok(Default::default()) } #[cfg(feature = "try-runtime")] - fn post_upgrade(_: Vec) -> Result<(), &'static str> { - assert_eq!(Pallet::::on_chain_storage_version(), 1); + fn post_upgrade(_: Vec) -> Result<(), TryRuntimeError> { + ensure!( + Pallet::::on_chain_storage_version() == 1, + "The onchain version must be updated after the migration." + ); Ok(()) } } diff --git a/frame/multisig/src/migrations.rs b/frame/multisig/src/migrations.rs index 2a9c858a5..298e73c5d 100644 --- a/frame/multisig/src/migrations.rs +++ b/frame/multisig/src/migrations.rs @@ -43,7 +43,7 @@ pub mod v1 { pub struct MigrateToV1(sp_std::marker::PhantomData); impl OnRuntimeUpgrade for MigrateToV1 { #[cfg(feature = "try-runtime")] - fn pre_upgrade() -> Result, &'static str> { + fn pre_upgrade() -> Result, sp_runtime::TryRuntimeError> { let onchain = Pallet::::on_chain_storage_version(); ensure!(onchain < 1, "this migration can be deleted"); @@ -72,7 +72,7 @@ pub mod v1 { } #[cfg(feature = "try-runtime")] - fn post_upgrade(_state: Vec) -> Result<(), &'static str> { + fn post_upgrade(_state: Vec) -> Result<(), sp_runtime::TryRuntimeError> { let onchain = Pallet::::on_chain_storage_version(); ensure!(onchain < 2, "this migration needs to be removed"); ensure!(onchain == 1, "this migration needs to be run"); diff --git a/frame/nfts/src/migration.rs b/frame/nfts/src/migration.rs index 33ee87e4b..ba14492be 100644 --- a/frame/nfts/src/migration.rs +++ b/frame/nfts/src/migration.rs @@ -18,6 +18,9 @@ use super::*; use frame_support::{log, traits::OnRuntimeUpgrade}; +#[cfg(feature = "try-runtime")] +use sp_runtime::TryRuntimeError; + pub mod v1 { use frame_support::{pallet_prelude::*, weights::Weight}; @@ -90,7 +93,7 @@ pub mod v1 { } #[cfg(feature = "try-runtime")] - fn pre_upgrade() -> Result, &'static str> { + fn pre_upgrade() -> Result, TryRuntimeError> { let current_version = Pallet::::current_storage_version(); let onchain_version = Pallet::::on_chain_storage_version(); ensure!(onchain_version == 0 && current_version == 1, "migration from version 0 to 1."); @@ -99,13 +102,13 @@ pub mod v1 { } #[cfg(feature = "try-runtime")] - fn post_upgrade(prev_count: Vec) -> Result<(), &'static str> { + fn post_upgrade(prev_count: Vec) -> Result<(), TryRuntimeError> { let prev_count: u32 = Decode::decode(&mut prev_count.as_slice()).expect( "the state parameter should be something that was generated by pre_upgrade", ); let post_count = Collection::::iter().count() as u32; - assert_eq!( - prev_count, post_count, + ensure!( + prev_count == post_count, "the records count before and after the migration should be the same" ); diff --git a/frame/nomination-pools/src/lib.rs b/frame/nomination-pools/src/lib.rs index cc68f54bd..66c5f786f 100644 --- a/frame/nomination-pools/src/lib.rs +++ b/frame/nomination-pools/src/lib.rs @@ -378,6 +378,9 @@ use sp_runtime::{ use sp_staking::{EraIndex, OnStakerSlash, StakingInterface}; use sp_std::{collections::btree_map::BTreeMap, fmt::Debug, ops::Div, vec::Vec}; +#[cfg(any(feature = "try-runtime", feature = "fuzzing", test, debug_assertions))] +use sp_runtime::TryRuntimeError; + /// The log target of this pallet. pub const LOG_TARGET: &str = "runtime::nomination-pools"; @@ -2626,7 +2629,7 @@ pub mod pallet { #[pallet::hooks] impl Hooks> for Pallet { #[cfg(feature = "try-runtime")] - fn try_state(_n: BlockNumberFor) -> Result<(), &'static str> { + fn try_state(_n: BlockNumberFor) -> Result<(), TryRuntimeError> { Self::do_try_state(u8::MAX) } @@ -3055,7 +3058,7 @@ impl Pallet { /// multiple `level`s, where the higher the level, the more checks we performs. So, /// `try_state(255)` is the strongest sanity check, and `0` performs no checks. #[cfg(any(feature = "try-runtime", feature = "fuzzing", test, debug_assertions))] - pub fn do_try_state(level: u8) -> Result<(), &'static str> { + pub fn do_try_state(level: u8) -> Result<(), TryRuntimeError> { if level.is_zero() { return Ok(()) } @@ -3063,12 +3066,24 @@ impl Pallet { // result in the same set of keys, in the same order. let bonded_pools = BondedPools::::iter_keys().collect::>(); let reward_pools = RewardPools::::iter_keys().collect::>(); - assert_eq!(bonded_pools, reward_pools); + ensure!( + bonded_pools == reward_pools, + "`BondedPools` and `RewardPools` must all have the EXACT SAME key-set." + ); - assert!(Metadata::::iter_keys().all(|k| bonded_pools.contains(&k))); - assert!(SubPoolsStorage::::iter_keys().all(|k| bonded_pools.contains(&k))); + ensure!( + SubPoolsStorage::::iter_keys().all(|k| bonded_pools.contains(&k)), + "`SubPoolsStorage` must be a subset of the above superset." + ); + ensure!( + Metadata::::iter_keys().all(|k| bonded_pools.contains(&k)), + "`Metadata` keys must be a subset of the above superset." + ); - assert!(MaxPools::::get().map_or(true, |max| bonded_pools.len() <= (max as usize))); + ensure!( + MaxPools::::get().map_or(true, |max| bonded_pools.len() <= (max as usize)), + Error::::MaxPools + ); for id in reward_pools { let account = Self::create_reward_account(id); @@ -3088,9 +3103,9 @@ impl Pallet { let mut pools_members = BTreeMap::::new(); let mut pools_members_pending_rewards = BTreeMap::>::new(); let mut all_members = 0u32; - PoolMembers::::iter().for_each(|(_, d)| { + PoolMembers::::iter().try_for_each(|(_, d)| -> Result<(), TryRuntimeError> { let bonded_pool = BondedPools::::get(d.pool_id).unwrap(); - assert!(!d.total_points().is_zero(), "no member should have zero points: {d:?}"); + ensure!(!d.total_points().is_zero(), "No member should have zero points"); *pools_members.entry(d.pool_id).or_default() += 1; all_members += 1; @@ -3103,9 +3118,11 @@ impl Pallet { let pending_rewards = d.pending_rewards(current_rc).unwrap(); *pools_members_pending_rewards.entry(d.pool_id).or_default() += pending_rewards; } // else this pool has been heavily slashed and cannot have any rewards anymore. - }); - RewardPools::::iter_keys().for_each(|id| { + Ok(()) + })?; + + RewardPools::::iter_keys().try_for_each(|id| -> Result<(), TryRuntimeError> { // the sum of the pending rewards must be less than the leftover balance. Since the // reward math rounds down, we might accumulate some dust here. log!( @@ -3115,30 +3132,40 @@ impl Pallet { pools_members_pending_rewards.get(&id), RewardPool::::current_balance(id) ); - assert!( + ensure!( RewardPool::::current_balance(id) >= - pools_members_pending_rewards.get(&id).copied().unwrap_or_default() - ) - }); + pools_members_pending_rewards.get(&id).copied().unwrap_or_default(), + "The sum of the pending rewards must be less than the leftover balance." + ); + Ok(()) + })?; - BondedPools::::iter().for_each(|(id, inner)| { + BondedPools::::iter().try_for_each(|(id, inner)| -> Result<(), TryRuntimeError> { let bonded_pool = BondedPool { id, inner }; - assert_eq!( - pools_members.get(&id).copied().unwrap_or_default(), - bonded_pool.member_counter + ensure!( + pools_members.get(&id).copied().unwrap_or_default() == + bonded_pool.member_counter, + "Each `BondedPool.member_counter` must be equal to the actual count of members of this pool" + ); + ensure!( + MaxPoolMembersPerPool::::get() + .map_or(true, |max| bonded_pool.member_counter <= max), + Error::::MaxPoolMembers ); - assert!(MaxPoolMembersPerPool::::get() - .map_or(true, |max| bonded_pool.member_counter <= max)); let depositor = PoolMembers::::get(&bonded_pool.roles.depositor).unwrap(); - assert!( + ensure!( bonded_pool.is_destroying_and_only_depositor(depositor.active_points()) || depositor.active_points() >= MinCreateBond::::get(), "depositor must always have MinCreateBond stake in the pool, except for when the \ pool is being destroyed and the depositor is the last member", ); - }); - assert!(MaxPoolMembers::::get().map_or(true, |max| all_members <= max)); + Ok(()) + })?; + ensure!( + MaxPoolMembers::::get().map_or(true, |max| all_members <= max), + Error::::MaxPoolMembers + ); if level <= 1 { return Ok(()) diff --git a/frame/nomination-pools/src/migration.rs b/frame/nomination-pools/src/migration.rs index 45d642411..2c84e4e6d 100644 --- a/frame/nomination-pools/src/migration.rs +++ b/frame/nomination-pools/src/migration.rs @@ -20,6 +20,9 @@ use crate::log; use frame_support::traits::OnRuntimeUpgrade; use sp_std::{collections::btree_map::BTreeMap, vec::Vec}; +#[cfg(feature = "try-runtime")] +use sp_runtime::TryRuntimeError; + pub mod v1 { use super::*; @@ -100,9 +103,12 @@ pub mod v1 { } #[cfg(feature = "try-runtime")] - fn post_upgrade(_: Vec) -> Result<(), &'static str> { + fn post_upgrade(_: Vec) -> Result<(), TryRuntimeError> { // new version must be set. - assert_eq!(Pallet::::on_chain_storage_version(), 1); + ensure!( + Pallet::::on_chain_storage_version() == 1, + "The onchain version must be updated after the migration." + ); Pallet::::try_state(frame_system::Pallet::::block_number())?; Ok(()) } @@ -352,38 +358,47 @@ pub mod v2 { } #[cfg(feature = "try-runtime")] - fn pre_upgrade() -> Result, &'static str> { + fn pre_upgrade() -> Result, TryRuntimeError> { // all reward accounts must have more than ED. - RewardPools::::iter().for_each(|(id, _)| { - assert!( + RewardPools::::iter().try_for_each(|(id, _)| -> Result<(), TryRuntimeError> { + ensure!( T::Currency::free_balance(&Pallet::::create_reward_account(id)) >= - T::Currency::minimum_balance() - ) - }); + T::Currency::minimum_balance(), + "Reward accounts must have greater balance than ED." + ); + Ok(()) + })?; Ok(Vec::new()) } #[cfg(feature = "try-runtime")] - fn post_upgrade(_: Vec) -> Result<(), &'static str> { + fn post_upgrade(_: Vec) -> Result<(), TryRuntimeError> { // new version must be set. - assert_eq!(Pallet::::on_chain_storage_version(), 2); + ensure!( + Pallet::::on_chain_storage_version() == 2, + "The onchain version must be updated after the migration." + ); // no reward or bonded pool has been skipped. - assert_eq!(RewardPools::::iter().count() as u32, RewardPools::::count()); - assert_eq!(BondedPools::::iter().count() as u32, BondedPools::::count()); + ensure!( + RewardPools::::iter().count() as u32 == RewardPools::::count(), + "The count of reward pools must remain the same after the migration." + ); + ensure!( + BondedPools::::iter().count() as u32 == BondedPools::::count(), + "The count of reward pools must remain the same after the migration." + ); // all reward pools must have exactly ED in them. This means no reward can be claimed, // and that setting reward counters all over the board to zero will work henceforth. - RewardPools::::iter().for_each(|(id, _)| { - assert_eq!( - RewardPool::::current_balance(id), - Zero::zero(), - "reward pool({}) balance is {:?}", - id, - RewardPool::::current_balance(id) + RewardPools::::iter().try_for_each(|(id, _)| -> Result<(), TryRuntimeError> { + ensure!( + RewardPool::::current_balance(id) == Zero::zero(), + "Reward pool balance must be zero.", ); - }); + Ok(()) + })?; log!(info, "post upgrade hook for MigrateToV2 executed."); Ok(()) @@ -435,7 +450,7 @@ pub mod v3 { } #[cfg(feature = "try-runtime")] - fn pre_upgrade() -> Result, &'static str> { + fn pre_upgrade() -> Result, TryRuntimeError> { ensure!( Pallet::::current_storage_version() > Pallet::::on_chain_storage_version(), "the on_chain version is equal or more than the current one" @@ -444,7 +459,7 @@ pub mod v3 { } #[cfg(feature = "try-runtime")] - fn post_upgrade(_: Vec) -> Result<(), &'static str> { + fn post_upgrade(_: Vec) -> Result<(), TryRuntimeError> { ensure!( Metadata::::iter_keys().all(|id| BondedPools::::contains_key(&id)), "not all of the stale metadata has been removed" @@ -535,7 +550,7 @@ pub mod v4 { } #[cfg(feature = "try-runtime")] - fn pre_upgrade() -> Result, &'static str> { + fn pre_upgrade() -> Result, TryRuntimeError> { ensure!( Pallet::::current_storage_version() > Pallet::::on_chain_storage_version(), "the on_chain version is equal or more than the current one" @@ -544,7 +559,7 @@ pub mod v4 { } #[cfg(feature = "try-runtime")] - fn post_upgrade(_: Vec) -> Result<(), &'static str> { + fn post_upgrade(_: Vec) -> Result<(), TryRuntimeError> { // ensure all BondedPools items now contain an `inner.commission: Commission` field. ensure!( BondedPools::::iter().all(|(_, inner)| inner.commission.current.is_none() && @@ -620,7 +635,7 @@ pub mod v5 { } #[cfg(feature = "try-runtime")] - fn pre_upgrade() -> Result, &'static str> { + fn pre_upgrade() -> Result, TryRuntimeError> { ensure!( Pallet::::current_storage_version() > Pallet::::on_chain_storage_version(), "the on_chain version is equal or more than the current one" @@ -654,7 +669,7 @@ pub mod v5 { } #[cfg(feature = "try-runtime")] - fn post_upgrade(data: Vec) -> Result<(), &'static str> { + fn post_upgrade(data: Vec) -> Result<(), TryRuntimeError> { let old_rpool_values: u64 = Decode::decode(&mut &data[..]).unwrap(); let rpool_keys = RewardPools::::iter_keys().count() as u64; let rpool_values = RewardPools::::iter_values().count() as u64; diff --git a/frame/nomination-pools/src/tests.rs b/frame/nomination-pools/src/tests.rs index 4cb255e23..49a059658 100644 --- a/frame/nomination-pools/src/tests.rs +++ b/frame/nomination-pools/src/tests.rs @@ -4269,10 +4269,7 @@ mod create { assert!(!BondedPools::::contains_key(2)); assert!(!RewardPools::::contains_key(2)); assert!(!PoolMembers::::contains_key(11)); - assert_err!( - StakingMock::active_stake(&next_pool_stash), - DispatchError::Other("balance not found") - ); + assert_err!(StakingMock::active_stake(&next_pool_stash), "balance not found"); Balances::make_free_balance_be(&11, StakingMock::minimum_nominator_bond() + ed); assert_ok!(Pools::create( diff --git a/frame/offences/src/migration.rs b/frame/offences/src/migration.rs index 07bd68407..14dbd606d 100644 --- a/frame/offences/src/migration.rs +++ b/frame/offences/src/migration.rs @@ -29,6 +29,8 @@ use sp_std::vec::Vec; #[cfg(feature = "try-runtime")] use frame_support::ensure; +#[cfg(feature = "try-runtime")] +use sp_runtime::TryRuntimeError; mod v0 { use super::*; @@ -51,7 +53,7 @@ pub mod v1 { pub struct MigrateToV1(sp_std::marker::PhantomData); impl OnRuntimeUpgrade for MigrateToV1 { #[cfg(feature = "try-runtime")] - fn pre_upgrade() -> Result, &'static str> { + fn pre_upgrade() -> Result, TryRuntimeError> { let onchain = Pallet::::on_chain_storage_version(); ensure!(onchain < 1, "pallet_offences::MigrateToV1 migration can be deleted"); @@ -81,7 +83,7 @@ pub mod v1 { } #[cfg(feature = "try-runtime")] - fn post_upgrade(_state: Vec) -> Result<(), &'static str> { + fn post_upgrade(_state: Vec) -> Result<(), TryRuntimeError> { let onchain = Pallet::::on_chain_storage_version(); ensure!(onchain == 1, "pallet_offences::MigrateToV1 needs to be run"); ensure!( diff --git a/frame/preimage/src/migration.rs b/frame/preimage/src/migration.rs index be352201d..46e555498 100644 --- a/frame/preimage/src/migration.rs +++ b/frame/preimage/src/migration.rs @@ -24,6 +24,11 @@ use frame_support::{ }; use sp_std::collections::btree_map::BTreeMap; +#[cfg(feature = "try-runtime")] +use frame_support::ensure; +#[cfg(feature = "try-runtime")] +use sp_runtime::TryRuntimeError; + /// The log target. const TARGET: &'static str = "runtime::preimage::migration::v1"; @@ -78,8 +83,8 @@ pub mod v1 { impl OnRuntimeUpgrade for Migration { #[cfg(feature = "try-runtime")] - fn pre_upgrade() -> Result, &'static str> { - assert_eq!(StorageVersion::get::>(), 0, "can only upgrade from version 0"); + fn pre_upgrade() -> Result, TryRuntimeError> { + ensure!(StorageVersion::get::>() == 0, "can only upgrade from version 0"); let images = v0::image_count::().expect("v0 storage corrupted"); log::info!(target: TARGET, "Migrating {} images", &images); @@ -148,7 +153,7 @@ pub mod v1 { } #[cfg(feature = "try-runtime")] - fn post_upgrade(state: Vec) -> Result<(), &'static str> { + fn post_upgrade(state: Vec) -> DispatchResult { let old_images: u32 = Decode::decode(&mut &state[..]).expect("pre_upgrade provides a valid state; qed"); let new_images = image_count::().expect("V1 storage corrupted"); @@ -161,7 +166,7 @@ pub mod v1 { old_images ); } - assert_eq!(StorageVersion::get::>(), 1, "must upgrade"); + ensure!(StorageVersion::get::>() == 1, "must upgrade"); Ok(()) } } diff --git a/frame/referenda/src/migration.rs b/frame/referenda/src/migration.rs index c27ab452a..6f796ca40 100644 --- a/frame/referenda/src/migration.rs +++ b/frame/referenda/src/migration.rs @@ -22,6 +22,9 @@ use codec::{Decode, Encode, EncodeLike, MaxEncodedLen}; use frame_support::{pallet_prelude::*, storage_alias, traits::OnRuntimeUpgrade}; use log; +#[cfg(feature = "try-runtime")] +use sp_runtime::TryRuntimeError; + /// Initial version of storage types. pub mod v0 { use super::*; @@ -95,9 +98,9 @@ pub mod v1 { pub struct MigrateV0ToV1(PhantomData<(T, I)>); impl, I: 'static> OnRuntimeUpgrade for MigrateV0ToV1 { #[cfg(feature = "try-runtime")] - fn pre_upgrade() -> Result, &'static str> { + fn pre_upgrade() -> Result, TryRuntimeError> { let onchain_version = Pallet::::on_chain_storage_version(); - assert_eq!(onchain_version, 0, "migration from version 0 to 1."); + ensure!(onchain_version == 0, "migration from version 0 to 1."); let referendum_count = v0::ReferendumInfoFor::::iter().count(); log::info!( target: TARGET, @@ -147,16 +150,13 @@ pub mod v1 { } #[cfg(feature = "try-runtime")] - fn post_upgrade(state: Vec) -> Result<(), &'static str> { + fn post_upgrade(state: Vec) -> Result<(), TryRuntimeError> { let onchain_version = Pallet::::on_chain_storage_version(); - assert_eq!(onchain_version, 1, "must upgrade from version 0 to 1."); + ensure!(onchain_version == 1, "must upgrade from version 0 to 1."); let pre_referendum_count: u32 = Decode::decode(&mut &state[..]) .expect("failed to decode the state from pre-upgrade."); let post_referendum_count = ReferendumInfoFor::::iter().count() as u32; - assert_eq!( - post_referendum_count, pre_referendum_count, - "must migrate all referendums." - ); + ensure!(post_referendum_count == pre_referendum_count, "must migrate all referendums."); log::info!(target: TARGET, "migrated all referendums."); Ok(()) } diff --git a/frame/scheduler/src/migration.rs b/frame/scheduler/src/migration.rs index 5b3a7631e..7f23d9620 100644 --- a/frame/scheduler/src/migration.rs +++ b/frame/scheduler/src/migration.rs @@ -20,6 +20,9 @@ use super::*; use frame_support::traits::OnRuntimeUpgrade; +#[cfg(feature = "try-runtime")] +use sp_runtime::TryRuntimeError; + /// The log target. const TARGET: &'static str = "runtime::scheduler::migration"; @@ -97,8 +100,8 @@ pub mod v3 { impl> OnRuntimeUpgrade for MigrateToV4 { #[cfg(feature = "try-runtime")] - fn pre_upgrade() -> Result, &'static str> { - assert_eq!(StorageVersion::get::>(), 3, "Can only upgrade from version 3"); + fn pre_upgrade() -> Result, TryRuntimeError> { + ensure!(StorageVersion::get::>() == 3, "Can only upgrade from version 3"); let agendas = Agenda::::iter_keys().count() as u32; let decodable_agendas = Agenda::::iter_values().count() as u32; @@ -125,7 +128,7 @@ pub mod v3 { agenda.len(), max_scheduled_per_block, ); - return Err("Agenda would overflow `MaxScheduledPerBlock`.") + return Err("Agenda would overflow `MaxScheduledPerBlock`.".into()) } } // Check that bounding the calls will not overflow `MAX_LENGTH`. @@ -142,7 +145,7 @@ pub mod v3 { block_number, l, ); - return Err("Call is too large.") + return Err("Call is too large.".into()) } }, _ => (), @@ -169,12 +172,12 @@ pub mod v3 { } #[cfg(feature = "try-runtime")] - fn post_upgrade(state: Vec) -> Result<(), &'static str> { - assert_eq!(StorageVersion::get::>(), 4, "Must upgrade"); + fn post_upgrade(state: Vec) -> Result<(), TryRuntimeError> { + ensure!(StorageVersion::get::>() == 4, "Must upgrade"); // Check that everything decoded fine. for k in crate::Agenda::::iter_keys() { - assert!(crate::Agenda::::try_get(k).is_ok(), "Cannot decode V4 Agenda"); + ensure!(crate::Agenda::::try_get(k).is_ok(), "Cannot decode V4 Agenda"); } let old_agendas: u32 = @@ -210,7 +213,7 @@ pub mod v4 { impl OnRuntimeUpgrade for CleanupAgendas { #[cfg(feature = "try-runtime")] - fn pre_upgrade() -> Result, &'static str> { + fn pre_upgrade() -> Result, TryRuntimeError> { assert_eq!( StorageVersion::get::>(), 4, @@ -285,8 +288,8 @@ pub mod v4 { } #[cfg(feature = "try-runtime")] - fn post_upgrade(state: Vec) -> Result<(), &'static str> { - assert_eq!(StorageVersion::get::>(), 4, "Version must not change"); + fn post_upgrade(state: Vec) -> Result<(), TryRuntimeError> { + ensure!(StorageVersion::get::>() == 4, "Version must not change"); let (old_agendas, non_empty_agendas): (u32, u32) = Decode::decode(&mut state.as_ref()).expect("Must decode pre_upgrade state"); @@ -305,7 +308,7 @@ pub mod v4 { old_agendas, new_agendas ), } - assert_eq!(new_agendas, non_empty_agendas, "Expected to keep all non-empty agendas"); + ensure!(new_agendas == non_empty_agendas, "Expected to keep all non-empty agendas"); Ok(()) } @@ -496,7 +499,7 @@ mod test { // The pre_upgrade hook fails: let err = v3::MigrateToV4::::pre_upgrade().unwrap_err(); - assert!(err.contains("Call is too large")); + assert!(err == "Call is too large".into()); // But the migration itself works: let _w = v3::MigrateToV4::::on_runtime_upgrade(); diff --git a/frame/staking/src/migrations.rs b/frame/staking/src/migrations.rs index 23bcfa439..0a27290da 100644 --- a/frame/staking/src/migrations.rs +++ b/frame/staking/src/migrations.rs @@ -23,6 +23,11 @@ use frame_support::{ traits::OnRuntimeUpgrade, }; +#[cfg(feature = "try-runtime")] +use frame_support::ensure; +#[cfg(feature = "try-runtime")] +use sp_runtime::TryRuntimeError; + /// Used for release versioning upto v12. /// /// Obsolete from v13. Keeping around to make encoding/decoding of old migration code easier. @@ -58,7 +63,7 @@ pub mod v13 { pub struct MigrateToV13(sp_std::marker::PhantomData); impl OnRuntimeUpgrade for MigrateToV13 { #[cfg(feature = "try-runtime")] - fn pre_upgrade() -> Result, &'static str> { + fn pre_upgrade() -> Result, TryRuntimeError> { frame_support::ensure!( StorageVersion::::get() == ObsoleteReleases::V12_0_0, "Required v12 before upgrading to v13" @@ -84,7 +89,7 @@ pub mod v13 { } #[cfg(feature = "try-runtime")] - fn post_upgrade(_state: Vec) -> Result<(), &'static str> { + fn post_upgrade(_state: Vec) -> Result<(), TryRuntimeError> { frame_support::ensure!( Pallet::::on_chain_storage_version() == 13, "v13 not applied" @@ -114,7 +119,7 @@ pub mod v12 { pub struct MigrateToV12(sp_std::marker::PhantomData); impl OnRuntimeUpgrade for MigrateToV12 { #[cfg(feature = "try-runtime")] - fn pre_upgrade() -> Result, &'static str> { + fn pre_upgrade() -> Result, TryRuntimeError> { frame_support::ensure!( StorageVersion::::get() == ObsoleteReleases::V11_0_0, "Expected v11 before upgrading to v12" @@ -146,7 +151,7 @@ pub mod v12 { } #[cfg(feature = "try-runtime")] - fn post_upgrade(_state: Vec) -> Result<(), &'static str> { + fn post_upgrade(_state: Vec) -> Result<(), TryRuntimeError> { frame_support::ensure!( StorageVersion::::get() == ObsoleteReleases::V12_0_0, "v12 not applied" @@ -170,7 +175,7 @@ pub mod v11 { for MigrateToV11 { #[cfg(feature = "try-runtime")] - fn pre_upgrade() -> Result, &'static str> { + fn pre_upgrade() -> Result, TryRuntimeError> { frame_support::ensure!( StorageVersion::::get() == ObsoleteReleases::V10_0_0, "must upgrade linearly" @@ -217,7 +222,7 @@ pub mod v11 { } #[cfg(feature = "try-runtime")] - fn post_upgrade(_state: Vec) -> Result<(), &'static str> { + fn post_upgrade(_state: Vec) -> Result<(), TryRuntimeError> { frame_support::ensure!( StorageVersion::::get() == ObsoleteReleases::V11_0_0, "wrong version after the upgrade" @@ -332,7 +337,7 @@ pub mod v9 { } #[cfg(feature = "try-runtime")] - fn pre_upgrade() -> Result, &'static str> { + fn pre_upgrade() -> Result, TryRuntimeError> { frame_support::ensure!( StorageVersion::::get() == ObsoleteReleases::V8_0_0, "must upgrade linearly" @@ -343,17 +348,21 @@ pub mod v9 { } #[cfg(feature = "try-runtime")] - fn post_upgrade(prev_count: Vec) -> Result<(), &'static str> { + fn post_upgrade(prev_count: Vec) -> Result<(), TryRuntimeError> { let prev_count: u32 = Decode::decode(&mut prev_count.as_slice()).expect( "the state parameter should be something that was generated by pre_upgrade", ); let post_count = T::VoterList::count(); let validators = Validators::::count(); - assert!(post_count == prev_count + validators); + ensure!( + post_count == prev_count + validators, + "`VoterList` count after the migration must equal to the sum of \ + previous count and the current number of validators" + ); frame_support::ensure!( StorageVersion::::get() == ObsoleteReleases::V9_0_0, - "must upgrade " + "must upgrade" ); Ok(()) } diff --git a/frame/staking/src/pallet/impls.rs b/frame/staking/src/pallet/impls.rs index 82a0956da..44d5674f2 100644 --- a/frame/staking/src/pallet/impls.rs +++ b/frame/staking/src/pallet/impls.rs @@ -51,6 +51,11 @@ use crate::{ use super::{pallet::*, STAKING_ID}; +#[cfg(feature = "try-runtime")] +use frame_support::ensure; +#[cfg(any(test, feature = "try-runtime"))] +use sp_runtime::TryRuntimeError; + /// The maximum number of iterations that we do whilst iterating over `T::VoterList` in /// `get_npos_voters`. /// @@ -1467,7 +1472,7 @@ impl SortedListProvider for UseValidatorsMap { 0 } #[cfg(feature = "try-runtime")] - fn try_state() -> Result<(), &'static str> { + fn try_state() -> Result<(), TryRuntimeError> { Ok(()) } @@ -1544,7 +1549,7 @@ impl SortedListProvider for UseNominatorsAndValidatorsM } #[cfg(feature = "try-runtime")] - fn try_state() -> Result<(), &'static str> { + fn try_state() -> Result<(), TryRuntimeError> { Ok(()) } @@ -1713,7 +1718,7 @@ impl StakingInterface for Pallet { #[cfg(any(test, feature = "try-runtime"))] impl Pallet { - pub(crate) fn do_try_state(_: BlockNumberFor) -> Result<(), &'static str> { + pub(crate) fn do_try_state(_: BlockNumberFor) -> Result<(), TryRuntimeError> { ensure!( T::VoterList::iter() .all(|x| >::contains_key(&x) || >::contains_key(&x)), @@ -1726,7 +1731,7 @@ impl Pallet { Self::check_count() } - fn check_count() -> Result<(), &'static str> { + fn check_count() -> Result<(), TryRuntimeError> { ensure!( ::VoterList::count() == Nominators::::count() + Validators::::count(), @@ -1739,18 +1744,19 @@ impl Pallet { ensure!( ValidatorCount::::get() <= ::MaxWinners::get(), - "validator count exceeded election max winners" + Error::::TooManyValidators ); Ok(()) } - fn check_ledgers() -> Result<(), &'static str> { + fn check_ledgers() -> Result<(), TryRuntimeError> { Bonded::::iter() .map(|(_, ctrl)| Self::ensure_ledger_consistent(ctrl)) - .collect::>() + .collect::, _>>()?; + Ok(()) } - fn check_exposures() -> Result<(), &'static str> { + fn check_exposures() -> Result<(), TryRuntimeError> { // a check per validator to ensure the exposure struct is always sane. let era = Self::active_era().unwrap().index; ErasStakers::::iter_prefix_values(era) @@ -1766,10 +1772,10 @@ impl Pallet { ); Ok(()) }) - .collect::>() + .collect::>() } - fn check_nominators() -> Result<(), &'static str> { + fn check_nominators() -> Result<(), TryRuntimeError> { // a check per nominator to ensure their entire stake is correctly distributed. Will only // kick-in if the nomination was submitted before the current era. let era = Self::active_era().unwrap().index; @@ -1783,27 +1789,33 @@ impl Pallet { } }, ) - .map(|nominator| { + .map(|nominator| -> Result<(), TryRuntimeError> { // must be bonded. Self::ensure_is_stash(&nominator)?; let mut sum = BalanceOf::::zero(); T::SessionInterface::validators() .iter() .map(|v| Self::eras_stakers(era, v)) - .map(|e| { + .map(|e| -> Result<(), TryRuntimeError> { let individual = e.others.iter().filter(|e| e.who == nominator).collect::>(); let len = individual.len(); match len { 0 => { /* not supporting this validator at all. */ }, 1 => sum += individual[0].value, - _ => return Err("nominator cannot back a validator more than once."), + _ => + return Err( + "nominator cannot back a validator more than once.".into() + ), }; Ok(()) }) - .collect::>() + .collect::, _>>()?; + Ok(()) }) - .collect::>() + .collect::, _>>()?; + + Ok(()) } fn ensure_is_stash(who: &T::AccountId) -> Result<(), &'static str> { @@ -1811,7 +1823,7 @@ impl Pallet { Ok(()) } - fn ensure_ledger_consistent(ctrl: T::AccountId) -> Result<(), &'static str> { + fn ensure_ledger_consistent(ctrl: T::AccountId) -> Result<(), TryRuntimeError> { // ensures ledger.total == ledger.active + sum(ledger.unlocking). let ledger = Self::ledger(ctrl.clone()).ok_or("Not a controller.")?; let real_total: BalanceOf = diff --git a/frame/staking/src/pallet/mod.rs b/frame/staking/src/pallet/mod.rs index 2b33573ac..35aa2626e 100644 --- a/frame/staking/src/pallet/mod.rs +++ b/frame/staking/src/pallet/mod.rs @@ -805,7 +805,7 @@ pub mod pallet { } #[cfg(feature = "try-runtime")] - fn try_state(n: BlockNumberFor) -> Result<(), &'static str> { + fn try_state(n: BlockNumberFor) -> Result<(), sp_runtime::TryRuntimeError> { Self::do_try_state(n) } } diff --git a/frame/support/procedural/src/pallet/expand/hooks.rs b/frame/support/procedural/src/pallet/expand/hooks.rs index d7d8bbded..ef22f5a3a 100644 --- a/frame/support/procedural/src/pallet/expand/hooks.rs +++ b/frame/support/procedural/src/pallet/expand/hooks.rs @@ -105,7 +105,7 @@ pub fn expand_hooks(def: &mut Def) -> proc_macro2::TokenStream { current_version, ); - return Err("On chain and current storage version do not match. Missing runtime upgrade?"); + return Err("On chain and current storage version do not match. Missing runtime upgrade?".into()); } } } else { @@ -128,7 +128,7 @@ pub fn expand_hooks(def: &mut Def) -> proc_macro2::TokenStream { ); return Err("On chain storage version set, while the pallet doesn't \ - have the `#[pallet::storage_version(VERSION)]` attribute."); + have the `#[pallet::storage_version(VERSION)]` attribute.".into()); } } }; @@ -211,7 +211,7 @@ pub fn expand_hooks(def: &mut Def) -> proc_macro2::TokenStream { } #[cfg(feature = "try-runtime")] - fn pre_upgrade() -> Result<#frame_support::sp_std::vec::Vec, &'static str> { + fn pre_upgrade() -> Result<#frame_support::sp_std::vec::Vec, #frame_support::sp_runtime::TryRuntimeError> { < Self as @@ -220,7 +220,7 @@ pub fn expand_hooks(def: &mut Def) -> proc_macro2::TokenStream { } #[cfg(feature = "try-runtime")] - fn post_upgrade(state: #frame_support::sp_std::vec::Vec) -> Result<(), &'static str> { + fn post_upgrade(state: #frame_support::sp_std::vec::Vec) -> Result<(), #frame_support::sp_runtime::TryRuntimeError> { #post_storage_version_check < @@ -268,7 +268,7 @@ pub fn expand_hooks(def: &mut Def) -> proc_macro2::TokenStream { fn try_state( n: ::BlockNumber, _s: #frame_support::traits::TryStateSelect - ) -> Result<(), &'static str> { + ) -> Result<(), #frame_support::sp_runtime::TryRuntimeError> { #log_try_state < Self as #frame_support::traits::Hooks< diff --git a/frame/support/src/dispatch.rs b/frame/support/src/dispatch.rs index 071fb7c9d..47c7b22f5 100644 --- a/frame/support/src/dispatch.rs +++ b/frame/support/src/dispatch.rs @@ -2097,7 +2097,7 @@ macro_rules! decl_module { fn try_state( _: <$trait_instance as $system::Config>::BlockNumber, _: $crate::traits::TryStateSelect, - ) -> Result<(), &'static str> { + ) -> Result<(), $crate::sp_runtime::TryRuntimeError> { let pallet_name = << $trait_instance as @@ -2144,12 +2144,12 @@ macro_rules! decl_module { } #[cfg(feature = "try-runtime")] - fn pre_upgrade() -> Result<$crate::sp_std::vec::Vec, &'static str> { + fn pre_upgrade() -> Result<$crate::sp_std::vec::Vec, $crate::sp_runtime::TryRuntimeError> { Ok($crate::sp_std::vec::Vec::new()) } #[cfg(feature = "try-runtime")] - fn post_upgrade(_: $crate::sp_std::vec::Vec) -> Result<(), &'static str> { + fn post_upgrade(_: $crate::sp_std::vec::Vec) -> Result<(), $crate::sp_runtime::TryRuntimeError> { Ok(()) } } @@ -2182,12 +2182,12 @@ macro_rules! decl_module { } #[cfg(feature = "try-runtime")] - fn pre_upgrade() -> Result<$crate::sp_std::vec::Vec, &'static str> { + fn pre_upgrade() -> Result<$crate::sp_std::vec::Vec, $crate::sp_runtime::TryRuntimeError> { Ok($crate::sp_std::vec::Vec::new()) } #[cfg(feature = "try-runtime")] - fn post_upgrade(_: $crate::sp_std::vec::Vec) -> Result<(), &'static str> { + fn post_upgrade(_: $crate::sp_std::vec::Vec) -> Result<(), $crate::sp_runtime::TryRuntimeError> { Ok(()) } } diff --git a/frame/support/src/migrations.rs b/frame/support/src/migrations.rs index 381f1feda..8bda2662a 100644 --- a/frame/support/src/migrations.rs +++ b/frame/support/src/migrations.rs @@ -184,7 +184,7 @@ impl, DbWeight: Get> frame_support::traits } #[cfg(feature = "try-runtime")] - fn pre_upgrade() -> Result, &'static str> { + fn pre_upgrade() -> Result, sp_runtime::TryRuntimeError> { let hashed_prefix = twox_128(P::get().as_bytes()); match contains_prefixed_key(&hashed_prefix) { true => log::info!("Found {} keys pre-removal 👀", P::get()), @@ -197,12 +197,12 @@ impl, DbWeight: Get> frame_support::traits } #[cfg(feature = "try-runtime")] - fn post_upgrade(_state: Vec) -> Result<(), &'static str> { + fn post_upgrade(_state: Vec) -> Result<(), sp_runtime::TryRuntimeError> { let hashed_prefix = twox_128(P::get().as_bytes()); match contains_prefixed_key(&hashed_prefix) { true => { log::error!("{} has keys remaining post-removal ❗", P::get()); - return Err("Keys remaining post-removal, this should never happen 🚨") + return Err("Keys remaining post-removal, this should never happen 🚨".into()) }, false => log::info!("No {} keys found post-removal 🎉", P::get()), }; diff --git a/frame/support/src/traits/hooks.rs b/frame/support/src/traits/hooks.rs index a4c677657..12dcd6af0 100644 --- a/frame/support/src/traits/hooks.rs +++ b/frame/support/src/traits/hooks.rs @@ -22,6 +22,9 @@ use impl_trait_for_tuples::impl_for_tuples; use sp_runtime::traits::AtLeast32BitUnsigned; use sp_std::prelude::*; +#[cfg(feature = "try-runtime")] +use sp_runtime::TryRuntimeError; + /// The block initialization trait. /// /// Implementing this lets you express what should happen for your pallet when the block is @@ -136,7 +139,7 @@ pub trait OnRuntimeUpgrade { /// Same as `on_runtime_upgrade`, but perform the optional `pre_upgrade` and `post_upgrade` as /// well. #[cfg(feature = "try-runtime")] - fn try_on_runtime_upgrade(checks: bool) -> Result { + fn try_on_runtime_upgrade(checks: bool) -> Result { let maybe_state = if checks { let _guard = frame_support::StorageNoopGuard::default(); let state = Self::pre_upgrade()?; @@ -167,7 +170,7 @@ pub trait OnRuntimeUpgrade { /// This hook must not write to any state, as it would make the main `on_runtime_upgrade` path /// inaccurate. #[cfg(feature = "try-runtime")] - fn pre_upgrade() -> Result, &'static str> { + fn pre_upgrade() -> Result, TryRuntimeError> { Ok(Vec::new()) } @@ -182,7 +185,7 @@ pub trait OnRuntimeUpgrade { /// This hook must not write to any state, as it would make the main `on_runtime_upgrade` path /// inaccurate. #[cfg(feature = "try-runtime")] - fn post_upgrade(_state: Vec) -> Result<(), &'static str> { + fn post_upgrade(_state: Vec) -> Result<(), TryRuntimeError> { Ok(()) } } @@ -201,7 +204,7 @@ impl OnRuntimeUpgrade for Tuple { /// consecutive migrations for the same pallet without errors. Therefore pre and post upgrade /// hooks for tuples are a noop. #[cfg(feature = "try-runtime")] - fn try_on_runtime_upgrade(checks: bool) -> Result { + fn try_on_runtime_upgrade(checks: bool) -> Result { let mut weight = Weight::zero(); let mut errors = Vec::new(); @@ -224,12 +227,12 @@ impl OnRuntimeUpgrade for Tuple { errors.iter().for_each(|err| { log::error!( target: "try-runtime", - "{}", + "{:?}", err ); }); - return Err("Detected multiple errors while executing `try_on_runtime_upgrade`, check the logs!") + return Err("Detected multiple errors while executing `try_on_runtime_upgrade`, check the logs!".into()) } Ok(weight) @@ -305,7 +308,7 @@ pub trait Hooks { /// /// This hook should not alter any storage. #[cfg(feature = "try-runtime")] - fn try_state(_n: BlockNumber) -> Result<(), &'static str> { + fn try_state(_n: BlockNumber) -> Result<(), TryRuntimeError> { Ok(()) } @@ -317,7 +320,7 @@ pub trait Hooks { /// /// This hook is never meant to be executed on-chain but is meant to be used by testing tools. #[cfg(feature = "try-runtime")] - fn pre_upgrade() -> Result, &'static str> { + fn pre_upgrade() -> Result, TryRuntimeError> { Ok(Vec::new()) } @@ -329,7 +332,7 @@ pub trait Hooks { /// /// This hook is never meant to be executed on-chain but is meant to be used by testing tools. #[cfg(feature = "try-runtime")] - fn post_upgrade(_state: Vec) -> Result<(), &'static str> { + fn post_upgrade(_state: Vec) -> Result<(), TryRuntimeError> { Ok(()) } @@ -411,13 +414,13 @@ mod tests { } #[cfg(feature = "try-runtime")] - fn pre_upgrade() -> Result, &'static str> { + fn pre_upgrade() -> Result, TryRuntimeError> { Pre::mutate(|s| s.push(stringify!($name))); Ok(Vec::new()) } #[cfg(feature = "try-runtime")] - fn post_upgrade(_: Vec) -> Result<(), &'static str> { + fn post_upgrade(_: Vec) -> Result<(), TryRuntimeError> { Post::mutate(|s| s.push(stringify!($name))); Ok(()) } diff --git a/frame/support/src/traits/try_runtime.rs b/frame/support/src/traits/try_runtime.rs index bebc24872..cb18f9d5b 100644 --- a/frame/support/src/traits/try_runtime.rs +++ b/frame/support/src/traits/try_runtime.rs @@ -19,6 +19,7 @@ use impl_trait_for_tuples::impl_for_tuples; use sp_arithmetic::traits::AtLeast32BitUnsigned; +use sp_runtime::TryRuntimeError; use sp_std::prelude::*; /// Which state tests to execute. @@ -129,7 +130,7 @@ impl core::str::FromStr for UpgradeCheckSelect { /// This hook should not alter any storage. pub trait TryState { /// Execute the state checks. - fn try_state(_: BlockNumber, _: Select) -> Result<(), &'static str>; + fn try_state(_: BlockNumber, _: Select) -> Result<(), TryRuntimeError>; } #[cfg_attr(all(not(feature = "tuples-96"), not(feature = "tuples-128")), impl_for_tuples(64))] @@ -139,7 +140,7 @@ impl TryState Result<(), &'static str> { + fn try_state(n: BlockNumber, targets: Select) -> Result<(), TryRuntimeError> { match targets { Select::None => Ok(()), Select::All => { @@ -148,7 +149,7 @@ impl TryState { - let functions: &[fn(BlockNumber, Select) -> Result<(), &'static str>] = + let functions: &[fn(BlockNumber, Select) -> Result<(), TryRuntimeError>] = &[for_tuples!(#( Tuple::try_state ),*)]; let skip = n.clone() % (functions.len() as u32).into(); let skip: u32 = @@ -163,7 +164,7 @@ impl TryState { let try_state_fns: &[( &'static str, - fn(BlockNumber, Select) -> Result<(), &'static str>, + fn(BlockNumber, Select) -> Result<(), TryRuntimeError>, )] = &[for_tuples!( #( (::name(), Tuple::try_state) ),* )]; diff --git a/frame/support/test/tests/pallet.rs b/frame/support/test/tests/pallet.rs index 7f15ad1f9..f6b5858f1 100644 --- a/frame/support/test/tests/pallet.rs +++ b/frame/support/test/tests/pallet.rs @@ -2110,9 +2110,11 @@ fn post_runtime_upgrade_detects_storage_version_issues() { // Call `on_genesis` to put the storage version of `Example` into the storage. Example::on_genesis(); // The version isn't changed, we should detect it. - assert!(Executive::try_runtime_upgrade(UpgradeCheckSelect::PreAndPost) - .unwrap_err() - .contains("On chain and current storage version do not match")); + assert!( + Executive::try_runtime_upgrade(UpgradeCheckSelect::PreAndPost).unwrap_err() == + "On chain and current storage version do not match. Missing runtime upgrade?" + .into() + ); }); TestExternalities::default().execute_with(|| { @@ -2138,9 +2140,12 @@ fn post_runtime_upgrade_detects_storage_version_issues() { // `CustomUpgradePallet4` will set a storage version for `Example4` while this doesn't has // any storage version "enabled". - assert!(ExecutiveWithUpgradePallet4::try_runtime_upgrade(UpgradeCheckSelect::PreAndPost) - .unwrap_err() - .contains("On chain storage version set, while the pallet doesn't")); + assert!( + ExecutiveWithUpgradePallet4::try_runtime_upgrade(UpgradeCheckSelect::PreAndPost) + .unwrap_err() == "On chain storage version set, while the pallet \ + doesn't have the `#[pallet::storage_version(VERSION)]` attribute." + .into() + ); }); } diff --git a/primitives/runtime/src/lib.rs b/primitives/runtime/src/lib.rs index 7be5bebf5..363881e43 100644 --- a/primitives/runtime/src/lib.rs +++ b/primitives/runtime/src/lib.rs @@ -784,6 +784,9 @@ pub type ApplyExtrinsicResult = pub type ApplyExtrinsicResultWithInfo = Result, transaction_validity::TransactionValidityError>; +/// The error type used as return type in try runtime hooks. +pub type TryRuntimeError = DispatchError; + /// Verify a signature on an encoded value in a lazy manner. This can be /// an optimization if the signature scheme has an "unsigned" escape hash. pub fn verify_encoded_lazy( diff --git a/utils/frame/try-runtime/cli/src/lib.rs b/utils/frame/try-runtime/cli/src/lib.rs index 4893c464b..3c60184ad 100644 --- a/utils/frame/try-runtime/cli/src/lib.rs +++ b/utils/frame/try-runtime/cli/src/lib.rs @@ -122,10 +122,10 @@ //! //! ```ignore //! #[cfg(feature = "try-runtime")] -//! fn pre_upgrade() -> Result, &'static str> {} +//! fn pre_upgrade() -> Result, TryRuntimeError> {} //! //! #[cfg(feature = "try-runtime")] -//! fn post_upgrade(state: Vec) -> Result<(), &'static str> {} +//! fn post_upgrade(state: Vec) -> Result<(), TryRuntimeError> {} //! ``` //! //! (The pallet macro syntax will support this simply as a part of `#[pallet::hooks]`). @@ -141,7 +141,7 @@ //! //! ```ignore //! #[cfg(feature = "try-runtime")] -//! fn try_state(_: BlockNumber) -> Result<(), &'static str> {} +//! fn try_state(_: BlockNumber) -> Result<(), TryRuntimeError> {} //! ``` //! //! which is called on numerous code paths in the try-runtime tool. These checks should ensure that From 784ac7e9cf42a88cf5ad105281633f705688bae9 Mon Sep 17 00:00:00 2001 From: lanaivina <31368580+lana-shanghai@users.noreply.github.com> Date: Tue, 23 May 2023 09:17:48 +0200 Subject: [PATCH 530/558] NFTs fractionalization (#12565) * Copy Uniques into Nfts * Connect new pallet * Update weights * Nfts: Multiple approvals (#12178) * multiple approvals * clear * tests & clean up * fix in logic & fmt * fix benchmarks * deadline * test deadline * current_block + deadline * update ApprovedTransfer event * benchmark * docs * Update frame/nfts/src/lib.rs Co-authored-by: Jegor Sidorenko <5252494+jsidorenko@users.noreply.github.com> * fmt fix * Update frame/nfts/src/lib.rs Co-authored-by: Jegor Sidorenko <5252494+jsidorenko@users.noreply.github.com> * update tests * anyone can cancel * Update frame/nfts/src/tests.rs Co-authored-by: Jegor Sidorenko <5252494+jsidorenko@users.noreply.github.com> * fmt * fix logic * unnecessary line * ".git/.scripts/bench-bot.sh" pallet dev pallet_nfts * Update frame/nfts/src/lib.rs * Update lib.rs * fmt * Update frame/nfts/src/lib.rs Co-authored-by: Squirrel * Update frame/nfts/src/lib.rs Co-authored-by: Squirrel * fmt * Update frame/nfts/src/lib.rs Co-authored-by: Squirrel * suggestion * new line * ".git/.scripts/bench-bot.sh" pallet dev pallet_nfts Co-authored-by: Jegor Sidorenko <5252494+jsidorenko@users.noreply.github.com> Co-authored-by: command-bot <> Co-authored-by: Squirrel * Fixes * cargo fmt * Fixes * Fixes * Fix CI * Nfts: Fix Auto-Increment (#12223) * commit * passing benchmarks * clean up * sync * runtime implementation * fix * fmt * fix benchmark * cfg * remove try-increment-id * remove unused error * impl Incrementable for unsigned types * clean up * fix in tests * not needed anymore * Use OptionQuery Co-authored-by: Keith Yeung * Rename Origin to RuntimeOrigin * [Uniques V2] Tips (#12168) * Allow to add tips when buying an NFT * Chore * Rework tips feature * Add weights + benchmarks * Convert tuple to struct * Fix benchmark * ".git/.scripts/bench-bot.sh" pallet dev pallet_nfts * Update frame/nfts/src/benchmarking.rs Co-authored-by: Oliver Tale-Yazdi * Fix benchmarks * Revert the bounded_vec![] approach * ".git/.scripts/bench-bot.sh" pallet dev pallet_nfts Co-authored-by: command-bot <> Co-authored-by: Oliver Tale-Yazdi * [Uniques V2] Atomic NFTs swap (#12285) * Atomic NFTs swap * Fmt * Fix benchmark * Rename swap -> atomic_swap * Update target balance * Rollback * Fix * ".git/.scripts/bench-bot.sh" pallet dev pallet_nfts * Make desired item optional * Apply suggestions * Update frame/nfts/src/features/atomic_swap.rs Co-authored-by: Squirrel * Rename fields * Optimisation * Add a comment * deadline -> maybe_deadline * Add docs * Change comments * Add price direction field * ".git/.scripts/bench-bot.sh" pallet dev pallet_nfts * Wrap price and direction * Fix benchmarks * Use ensure! instead of if {} * Make duration param mandatory and limit it to MaxDeadlineDuration * Make the code safer * Fix clippy * Chore * Remove unused vars * try * try 2 * try 3 Co-authored-by: command-bot <> Co-authored-by: Squirrel * [Uniques V2] Feature flags (#12367) * Basics * WIP: change the data format * Refactor * Remove redundant new() method * Rename settings * Enable tests * Chore * Change params order * Delete the config on collection removal * Chore * Remove redundant system features * Rename force_item_status to force_collection_status * Update node runtime * Chore * Remove thaw_collection * Chore * Connect collection.is_frozen to config * Allow to lock the collection in a new way * Move free_holding into settings * Connect collection's metadata locker to feature flags * DRY * Chore * Connect pallet level feature flags * Prepare tests for the new changes * Implement Item settings * Allow to lock the metadata or attributes of an item * Common -> Settings * Extract settings related code to a separate file * Move feature flag checks inside the do_* methods * Split settings.rs into parts * Extract repeated code into macro * Extract macros into their own file * Chore * Fix traits * Fix traits * Test SystemFeatures * Fix benchmarks * Add missing benchmark * Fix node/runtime/lib.rs * ".git/.scripts/bench-bot.sh" pallet dev pallet_nfts * Keep item's config on burn if it's not empty * Fix the merge artifacts * Fmt * Add SystemFeature::NoSwaps check * Rename SystemFeatures to PalletFeatures * Rename errors * Add docs * Change error message * Rework pallet features * Move macros * Change comments * Fmt * Refactor Incrementable * Use pub(crate) for do_* functions * Update comments * Refactor freeze and lock functions * Rework Collection config and Item confg api * Chore * Make clippy happy * Chore * Update comment * RequiredDeposit => DepositRequired * Address comments Co-authored-by: command-bot <> * [Uniques V2] Refactor roles (#12437) * Basics * WIP: change the data format * Refactor * Remove redundant new() method * Rename settings * Enable tests * Chore * Change params order * Delete the config on collection removal * Chore * Remove redundant system features * Rename force_item_status to force_collection_status * Update node runtime * Chore * Remove thaw_collection * Chore * Connect collection.is_frozen to config * Allow to lock the collection in a new way * Move free_holding into settings * Connect collection's metadata locker to feature flags * DRY * Chore * Connect pallet level feature flags * Prepare tests for the new changes * Implement Item settings * Allow to lock the metadata or attributes of an item * Common -> Settings * Extract settings related code to a separate file * Move feature flag checks inside the do_* methods * Split settings.rs into parts * Extract repeated code into macro * Extract macros into their own file * Chore * Fix traits * Fix traits * Test SystemFeatures * Fix benchmarks * Add missing benchmark * Fix node/runtime/lib.rs * ".git/.scripts/bench-bot.sh" pallet dev pallet_nfts * Keep item's config on burn if it's not empty * Fix the merge artifacts * Fmt * Add SystemFeature::NoSwaps check * Refactor roles structure * Rename SystemFeatures to PalletFeatures * Rename errors * Add docs * Change error message * Rework pallet features * Move macros * Change comments * Fmt * Refactor Incrementable * Use pub(crate) for do_* functions * Update comments * Refactor freeze and lock functions * Rework Collection config and Item confg api * Chore * Make clippy happy * Chore * Fix artifacts * Address comments * Further refactoring * Add comments * Add tests for group_roles_by_account() * Update frame/nfts/src/impl_nonfungibles.rs * Add test * Replace Itertools group_by with a custom implementation * ItemsNotTransferable => ItemsNonTransferable * Update frame/nfts/src/features/roles.rs Co-authored-by: Muharem Ismailov * Address PR comments * Add missed comment Co-authored-by: command-bot <> Co-authored-by: Muharem Ismailov * Fix copy * Remove storage_prefix * Remove transactional * Initial commit SFT pallet. * Update comment * [Uniques V2] Minting options (#12483) * Basics * WIP: change the data format * Refactor * Remove redundant new() method * Rename settings * Enable tests * Chore * Change params order * Delete the config on collection removal * Chore * Remove redundant system features * Rename force_item_status to force_collection_status * Update node runtime * Chore * Remove thaw_collection * Chore * Connect collection.is_frozen to config * Allow to lock the collection in a new way * Move free_holding into settings * Connect collection's metadata locker to feature flags * DRY * Chore * Connect pallet level feature flags * Prepare tests for the new changes * Implement Item settings * Allow to lock the metadata or attributes of an item * Common -> Settings * Extract settings related code to a separate file * Move feature flag checks inside the do_* methods * Split settings.rs into parts * Extract repeated code into macro * Extract macros into their own file * Chore * Fix traits * Fix traits * Test SystemFeatures * Fix benchmarks * Add missing benchmark * Fix node/runtime/lib.rs * ".git/.scripts/bench-bot.sh" pallet dev pallet_nfts * Keep item's config on burn if it's not empty * Fix the merge artifacts * Fmt * Add SystemFeature::NoSwaps check * Rename SystemFeatures to PalletFeatures * Rename errors * Add docs * Change error message * Change the format of CollectionConfig to store more data * Move max supply to the CollectionConfig and allow to change it * Remove ItemConfig from the mint() function and use the one set in mint settings * Add different mint options * Allow to change the mint settings * Add a force_mint() method * Check mint params * Some optimisations * Cover with tests * Remove merge artifacts * Chore * Use the new has_role() method * Rework item deposits * More tests * Refactoring * Address comments * Refactor lock_collection() * Update frame/nfts/src/types.rs Co-authored-by: Squirrel * Update frame/nfts/src/types.rs Co-authored-by: Squirrel * Update frame/nfts/src/lib.rs Co-authored-by: Squirrel * Update frame/nfts/src/lib.rs Co-authored-by: Squirrel * Private => Issuer * Add more tests * Fix benchmarks * Add benchmarks for new methods * [Uniques v2] Refactoring (#12570) * Move do_set_price() and do_buy_item() to buy_sell.rs * Move approvals to feature file * Move metadata to feature files * Move the rest of methods to feature files * Remove artifacts * Split force_collection_status into 2 methods * Fix benchmarks * Fix benchmarks * Update deps Co-authored-by: command-bot <> Co-authored-by: Squirrel * Rename module to NFT fractionalisation * Loose coupling for pallet-assets * cargo fmt * [Uniques V2] Smart attributes (#12702) * Basics * WIP: change the data format * Refactor * Remove redundant new() method * Rename settings * Enable tests * Chore * Change params order * Delete the config on collection removal * Chore * Remove redundant system features * Rename force_item_status to force_collection_status * Update node runtime * Chore * Remove thaw_collection * Chore * Connect collection.is_frozen to config * Allow to lock the collection in a new way * Move free_holding into settings * Connect collection's metadata locker to feature flags * DRY * Chore * Connect pallet level feature flags * Prepare tests for the new changes * Implement Item settings * Allow to lock the metadata or attributes of an item * Common -> Settings * Extract settings related code to a separate file * Move feature flag checks inside the do_* methods * Split settings.rs into parts * Extract repeated code into macro * Extract macros into their own file * Chore * Fix traits * Fix traits * Test SystemFeatures * Fix benchmarks * Add missing benchmark * Fix node/runtime/lib.rs * ".git/.scripts/bench-bot.sh" pallet dev pallet_nfts * Keep item's config on burn if it's not empty * Fix the merge artifacts * Fmt * Add SystemFeature::NoSwaps check * Rename SystemFeatures to PalletFeatures * Rename errors * Add docs * Change error message * Change the format of CollectionConfig to store more data * Move max supply to the CollectionConfig and allow to change it * Remove ItemConfig from the mint() function and use the one set in mint settings * Add different mint options * Allow to change the mint settings * Add a force_mint() method * Check mint params * Some optimisations * Cover with tests * Remove merge artifacts * Chore * Use the new has_role() method * Rework item deposits * More tests * Refactoring * Address comments * Refactor lock_collection() * Update frame/nfts/src/types.rs Co-authored-by: Squirrel * Update frame/nfts/src/types.rs Co-authored-by: Squirrel * Update frame/nfts/src/lib.rs Co-authored-by: Squirrel * Update frame/nfts/src/lib.rs Co-authored-by: Squirrel * Private => Issuer * Add more tests * Fix benchmarks * Add benchmarks for new methods * [Uniques v2] Refactoring (#12570) * Move do_set_price() and do_buy_item() to buy_sell.rs * Move approvals to feature file * Move metadata to feature files * Move the rest of methods to feature files * Remove artifacts * Smart attributes * Split force_collection_status into 2 methods * Fix benchmarks * Fix benchmarks * Update deps * Fix merge artifact * Weights + benchmarks + docs * Change params order * Chore * Update frame/nfts/src/lib.rs Co-authored-by: Squirrel * Update frame/nfts/src/lib.rs Co-authored-by: Squirrel * Update docs * Update frame/nfts/src/lib.rs Co-authored-by: Squirrel * Add PalletId * Chore * Add tests * More tests * Add doc * Update errors snapshots * Ensure we track the owner_deposit field correctly Co-authored-by: command-bot <> Co-authored-by: Squirrel * [Uniques V2] Final improvements (#12736) * Use KeyPrefixIterator instead of Box * Change create_collection() * Restrict from claiming NFTs twice * Update Readme * Remove dead code * Refactoring * Update readme * Fix clippy * Update frame/nfts/src/lib.rs Co-authored-by: Squirrel * ".git/.scripts/bench-bot.sh" pallet dev pallet_nfts * Update docs * Typo * Fix benchmarks * Add more docs * Replace uniques with nfts, add minted volume storage * DepositRequired setting should affect only the attributes within the CollectionOwner namespace * Add unlock functionality * [NFTs] Implement missed methods to set the attributes from other pallets (#12919) * Implement missed methods to set the attributes from other pallets * Revert snapshots * Update snapshot * Update snapshot * Revert snapshot changes * Update snapshots * Yet another snapshot update.. * Asset to NFT id storage mutations * Minor fixes * Minor comments * cargo fmt * Remove benchmarking, unused clone() * Update frame/support/src/traits/tokens/nonfungible_v2.rs Co-authored-by: joe petrowski <25483142+joepetrowski@users.noreply.github.com> * Update frame/support/src/traits/tokens/nonfungible_v2.rs Co-authored-by: joe petrowski <25483142+joepetrowski@users.noreply.github.com> * Update frame/support/src/traits/tokens/nonfungible_v2.rs Co-authored-by: joe petrowski <25483142+joepetrowski@users.noreply.github.com> * Update frame/support/src/traits/tokens/nonfungibles_v2.rs Co-authored-by: joe petrowski <25483142+joepetrowski@users.noreply.github.com> * Update frame/nfts/src/lib.rs Co-authored-by: joe petrowski <25483142+joepetrowski@users.noreply.github.com> * Update frame/nfts/src/lib.rs Co-authored-by: joe petrowski <25483142+joepetrowski@users.noreply.github.com> * Update frame/nfts/src/lib.rs Co-authored-by: joe petrowski <25483142+joepetrowski@users.noreply.github.com> * Update frame/nfts/src/lib.rs Co-authored-by: joe petrowski <25483142+joepetrowski@users.noreply.github.com> * Update frame/nfts/src/lib.rs Co-authored-by: joe petrowski <25483142+joepetrowski@users.noreply.github.com> * Update frame/support/src/traits/tokens/nonfungible_v2.rs * Update frame/nfts/src/lib.rs Co-authored-by: joe petrowski <25483142+joepetrowski@users.noreply.github.com> * Update frame/support/src/traits/tokens/nonfungibles_v2.rs Co-authored-by: joe petrowski <25483142+joepetrowski@users.noreply.github.com> * Update frame/nfts/src/lib.rs Co-authored-by: joe petrowski <25483142+joepetrowski@users.noreply.github.com> * Update frame/nfts/src/lib.rs Co-authored-by: joe petrowski <25483142+joepetrowski@users.noreply.github.com> * Update frame/nfts/src/lib.rs Co-authored-by: joe petrowski <25483142+joepetrowski@users.noreply.github.com> * Update frame/nfts/src/lib.rs Co-authored-by: joe petrowski <25483142+joepetrowski@users.noreply.github.com> * Address comments * [NFTs] Add the new `owner` param to mint() method (#12997) * Add the new `owner` param to mint() method * Fmt * Address comments * ".git/.scripts/bench-bot.sh" pallet dev pallet_nfts * Fmt * Update frame/nfts/src/common_functions.rs Co-authored-by: joe petrowski <25483142+joepetrowski@users.noreply.github.com> * Update frame/nfts/src/types.rs Co-authored-by: joe petrowski <25483142+joepetrowski@users.noreply.github.com> * Update frame/nfts/src/types.rs Co-authored-by: joe petrowski <25483142+joepetrowski@users.noreply.github.com> * Update frame/nfts/src/types.rs Co-authored-by: joe petrowski <25483142+joepetrowski@users.noreply.github.com> * Update frame/nfts/src/types.rs Co-authored-by: joe petrowski <25483142+joepetrowski@users.noreply.github.com> * Update frame/nfts/src/types.rs Co-authored-by: joe petrowski <25483142+joepetrowski@users.noreply.github.com> * Add call indexes * Update snapshots * Refactor nft fractionalisation pallet (#13008) * Refactoring * Make it compile * Add tests * Rename * Rework nfts locking * Update cargo.lock * Connect the latest changes to the runtime-kitchensink * Add benchmarks, fix other issues * Chore * Chore 2 * Chore 3 * Add runtime-benchmarks * Rename * Set metadata * Make fields public * Chore * Created asset shouldn't be sufficient * Add documentation * minor edit to docs * Minor corrections Co-authored-by: lana-shanghai * fmt * Add fee reserved before creating an asset * Use ReservableCurrency for fee deposit * Improvements * Revert fmt changes * A bit more cleanup * Consistent naming * Make it more generic * Leftover * Use Vec instead of String * Update to the latest + improve the Locker trait * Refactor NFTs locker * Replace Vec with BoundedVec, add clearer errors * cargo fmt * Add README about unlocking NFTs * add constant definition * add fortitude & precision to asset related functions * fix mock and tests * transfer ExistentialDeposit to pallet if it's balance is below * Refactoring * Simplify the locking mechanism * Use PalletAttributes enum instead of the LOCKED_NFT_KEY * Fix benchmark * Add missing licence details * Update Cargo.toml * ".git/.scripts/commands/bench/bench.sh" pallet dev pallet_nft_fractionalization * Apply suggestions from code review Co-authored-by: joe petrowski <25483142+joepetrowski@users.noreply.github.com> * Update frame/nft-fractionalization/README.md Co-authored-by: joe petrowski <25483142+joepetrowski@users.noreply.github.com> --------- Co-authored-by: Jegor Sidorenko Co-authored-by: Sergej Sakac <73715684+Szegoo@users.noreply.github.com> Co-authored-by: Jegor Sidorenko <5252494+jsidorenko@users.noreply.github.com> Co-authored-by: Squirrel Co-authored-by: Keith Yeung Co-authored-by: Oliver Tale-Yazdi Co-authored-by: Muharem Ismailov Co-authored-by: command-bot <> Co-authored-by: joe petrowski <25483142+joepetrowski@users.noreply.github.com> --- Cargo.lock | 20 + Cargo.toml | 1 + bin/node/runtime/Cargo.toml | 4 + bin/node/runtime/src/lib.rs | 35 +- frame/assets/src/functions.rs | 11 +- frame/assets/src/impl_fungibles.rs | 13 + frame/nft-fractionalization/Cargo.toml | 52 +++ frame/nft-fractionalization/README.md | 6 + .../nft-fractionalization/src/benchmarking.rs | 135 ++++++ frame/nft-fractionalization/src/lib.rs | 389 ++++++++++++++++++ frame/nft-fractionalization/src/mock.rs | 202 +++++++++ frame/nft-fractionalization/src/tests.rs | 278 +++++++++++++ frame/nft-fractionalization/src/types.rs | 76 ++++ frame/nft-fractionalization/src/weights.rs | 184 +++++++++ frame/nfts/src/features/attributes.rs | 15 + frame/nfts/src/features/transfer.rs | 5 + frame/nfts/src/impl_nonfungibles.rs | 22 + frame/nfts/src/types.rs | 2 + .../src/traits/tokens/fungible/regular.rs | 3 +- .../src/traits/tokens/fungibles/metadata.rs | 5 + frame/support/src/traits/tokens/misc.rs | 4 +- .../src/traits/tokens/nonfungible_v2.rs | 16 +- .../src/traits/tokens/nonfungibles_v2.rs | 14 + 23 files changed, 1483 insertions(+), 9 deletions(-) create mode 100644 frame/nft-fractionalization/Cargo.toml create mode 100644 frame/nft-fractionalization/README.md create mode 100644 frame/nft-fractionalization/src/benchmarking.rs create mode 100644 frame/nft-fractionalization/src/lib.rs create mode 100644 frame/nft-fractionalization/src/mock.rs create mode 100644 frame/nft-fractionalization/src/tests.rs create mode 100644 frame/nft-fractionalization/src/types.rs create mode 100644 frame/nft-fractionalization/src/weights.rs diff --git a/Cargo.lock b/Cargo.lock index b29ed95b8..f387b5ef9 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3940,6 +3940,7 @@ dependencies = [ "pallet-message-queue", "pallet-mmr", "pallet-multisig", + "pallet-nft-fractionalization", "pallet-nfts", "pallet-nfts-runtime-api", "pallet-nis", @@ -6648,6 +6649,25 @@ dependencies = [ "sp-std", ] +[[package]] +name = "pallet-nft-fractionalization" +version = "4.0.0-dev" +dependencies = [ + "frame-benchmarking", + "frame-support", + "frame-system", + "log", + "pallet-assets", + "pallet-balances", + "pallet-nfts", + "parity-scale-codec", + "scale-info", + "sp-core", + "sp-io", + "sp-runtime", + "sp-std", +] + [[package]] name = "pallet-nfts" version = "4.0.0-dev" diff --git a/Cargo.toml b/Cargo.toml index 2b1659d4f..2996fb385 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -129,6 +129,7 @@ members = [ "frame/message-queue", "frame/nfts", "frame/nfts/runtime-api", + "frame/nft-fractionalization", "frame/nomination-pools", "frame/nomination-pools/fuzzer", "frame/nomination-pools/benchmarking", diff --git a/bin/node/runtime/Cargo.toml b/bin/node/runtime/Cargo.toml index 3909eff2a..9708e1ed9 100644 --- a/bin/node/runtime/Cargo.toml +++ b/bin/node/runtime/Cargo.toml @@ -85,6 +85,7 @@ pallet-mmr = { version = "4.0.0-dev", default-features = false, path = "../../.. pallet-multisig = { version = "4.0.0-dev", default-features = false, path = "../../../frame/multisig" } pallet-nfts = { version = "4.0.0-dev", default-features = false, path = "../../../frame/nfts" } pallet-nfts-runtime-api = { version = "4.0.0-dev", default-features = false, path = "../../../frame/nfts/runtime-api" } +pallet-nft-fractionalization = { version = "4.0.0-dev", default-features = false, path = "../../../frame/nft-fractionalization" } pallet-nomination-pools = { version = "1.0.0", default-features = false, path = "../../../frame/nomination-pools"} pallet-nomination-pools-benchmarking = { version = "1.0.0", default-features = false, optional = true, path = "../../../frame/nomination-pools/benchmarking" } pallet-nomination-pools-runtime-api = { version = "1.0.0-dev", default-features = false, path = "../../../frame/nomination-pools/runtime-api" } @@ -219,6 +220,7 @@ std = [ "pallet-uniques/std", "pallet-nfts/std", "pallet-nfts-runtime-api/std", + "pallet-nft-fractionalization/std", "pallet-vesting/std", "log/std", "frame-try-runtime?/std", @@ -283,6 +285,7 @@ runtime-benchmarks = [ "pallet-utility/runtime-benchmarks", "pallet-uniques/runtime-benchmarks", "pallet-nfts/runtime-benchmarks", + "pallet-nft-fractionalization/runtime-benchmarks", "pallet-vesting/runtime-benchmarks", "pallet-whitelist/runtime-benchmarks", "frame-system-benchmarking/runtime-benchmarks", @@ -349,6 +352,7 @@ try-runtime = [ "pallet-transaction-storage/try-runtime", "pallet-uniques/try-runtime", "pallet-nfts/try-runtime", + "pallet-nft-fractionalization/try-runtime", "pallet-vesting/try-runtime", "pallet-whitelist/try-runtime", ] diff --git a/bin/node/runtime/src/lib.rs b/bin/node/runtime/src/lib.rs index a2cb64cad..76143aa1b 100644 --- a/bin/node/runtime/src/lib.rs +++ b/bin/node/runtime/src/lib.rs @@ -44,7 +44,7 @@ use frame_support::{ }, ConstantMultiplier, IdentityFee, Weight, }, - PalletId, RuntimeDebug, + BoundedVec, PalletId, RuntimeDebug, }; use frame_system::{ limits::{BlockLength, BlockWeights}, @@ -445,6 +445,8 @@ parameter_types! { pub enum HoldReason { /// The NIS Pallet has reserved it for a non-fungible receipt. Nis, + /// Used by the NFT Fractionalization Pallet. + NftFractionalization, } impl pallet_balances::Config for Runtime { @@ -460,7 +462,7 @@ impl pallet_balances::Config for Runtime { type FreezeIdentifier = (); type MaxFreezes = (); type HoldIdentifier = HoldReason; - type MaxHolds = ConstU32<1>; + type MaxHolds = ConstU32<2>; } parameter_types! { @@ -1612,6 +1614,33 @@ impl pallet_core_fellowship::Config for Runtime { type EvidenceSize = ConstU32<16_384>; } +parameter_types! { + pub const NftFractionalizationPalletId: PalletId = PalletId(*b"fraction"); + pub NewAssetSymbol: BoundedVec = (*b"FRAC").to_vec().try_into().unwrap(); + pub NewAssetName: BoundedVec = (*b"Frac").to_vec().try_into().unwrap(); + pub const NftFractionalizationHoldReason: HoldReason = HoldReason::NftFractionalization; +} + +impl pallet_nft_fractionalization::Config for Runtime { + type RuntimeEvent = RuntimeEvent; + type Deposit = AssetDeposit; + type Currency = Balances; + type NewAssetSymbol = NewAssetSymbol; + type NewAssetName = NewAssetName; + type StringLimit = StringLimit; + type NftCollectionId = ::CollectionId; + type NftId = ::ItemId; + type AssetBalance = ::Balance; + type AssetId = ::AssetId; + type Assets = Assets; + type Nfts = Nfts; + type PalletId = NftFractionalizationPalletId; + type WeightInfo = pallet_nft_fractionalization::weights::SubstrateWeight; + type HoldReason = NftFractionalizationHoldReason; + #[cfg(feature = "runtime-benchmarks")] + type BenchmarkHelper = (); +} + parameter_types! { pub Features: PalletFeatures = PalletFeatures::all_enabled(); pub const MaxAttributesPerCall: u32 = 10; @@ -1829,6 +1858,7 @@ construct_runtime!( Nis: pallet_nis, Uniques: pallet_uniques, Nfts: pallet_nfts, + NftFractionalization: pallet_nft_fractionalization, Salary: pallet_salary, CoreFellowship: pallet_core_fellowship, TransactionStorage: pallet_transaction_storage, @@ -1972,6 +2002,7 @@ mod benches { [pallet_asset_rate, AssetRate] [pallet_uniques, Uniques] [pallet_nfts, Nfts] + [pallet_nft_fractionalization, NftFractionalization] [pallet_utility, Utility] [pallet_vesting, Vesting] [pallet_whitelist, Whitelist] diff --git a/frame/assets/src/functions.rs b/frame/assets/src/functions.rs index d7c5bbe95..3f32f7b94 100644 --- a/frame/assets/src/functions.rs +++ b/frame/assets/src/functions.rs @@ -962,9 +962,7 @@ impl, I: 'static> Pallet { ensure!(metadata.as_ref().map_or(true, |m| !m.is_frozen), Error::::NoPermission); let old_deposit = metadata.take().map_or(Zero::zero(), |m| m.deposit); - let new_deposit = T::MetadataDepositPerByte::get() - .saturating_mul(((name.len() + symbol.len()) as u32).into()) - .saturating_add(T::MetadataDepositBase::get()); + let new_deposit = Self::calc_metadata_deposit(&name, &symbol); if new_deposit > old_deposit { T::Currency::reserve(from, new_deposit - old_deposit)?; @@ -991,6 +989,13 @@ impl, I: 'static> Pallet { }) } + /// Calculate the metadata deposit for the provided data. + pub(super) fn calc_metadata_deposit(name: &[u8], symbol: &[u8]) -> DepositBalanceOf { + T::MetadataDepositPerByte::get() + .saturating_mul(((name.len() + symbol.len()) as u32).into()) + .saturating_add(T::MetadataDepositBase::get()) + } + /// Returns all the non-zero balances for all assets of the given `account`. pub fn account_balances(account: T::AccountId) -> Vec<(T::AssetId, T::Balance)> { Asset::::iter_keys() diff --git a/frame/assets/src/impl_fungibles.rs b/frame/assets/src/impl_fungibles.rs index 893d74b6a..a7df3d154 100644 --- a/frame/assets/src/impl_fungibles.rs +++ b/frame/assets/src/impl_fungibles.rs @@ -226,6 +226,19 @@ impl, I: 'static> fungibles::metadata::Mutate<:: } } +impl, I: 'static> + fungibles::metadata::MetadataDeposit< + ::AccountId>>::Balance, + > for Pallet +{ + fn calc_metadata_deposit( + name: &[u8], + symbol: &[u8], + ) -> ::AccountId>>::Balance { + Self::calc_metadata_deposit(&name, &symbol) + } +} + impl, I: 'static> fungibles::approvals::Inspect<::AccountId> for Pallet { diff --git a/frame/nft-fractionalization/Cargo.toml b/frame/nft-fractionalization/Cargo.toml new file mode 100644 index 000000000..917d9c5d3 --- /dev/null +++ b/frame/nft-fractionalization/Cargo.toml @@ -0,0 +1,52 @@ +[package] +name = "pallet-nft-fractionalization" +version = "4.0.0-dev" +authors = ["Parity Technologies "] +edition = "2021" +license = "Apache-2.0" +homepage = "https://substrate.io" +repository = "https://github.com/paritytech/substrate/" +description = "FRAME pallet to convert non-fungible to fungible tokens." +readme = "README.md" + +[package.metadata.docs.rs] +targets = ["x86_64-unknown-linux-gnu"] + +[dependencies] +codec = { package = "parity-scale-codec", version = "3.2.2", default-features = false } +log = { version = "0.4.17", default-features = false } +scale-info = { version = "2.5.0", default-features = false, features = ["derive"] } +frame-benchmarking = { version = "4.0.0-dev", default-features = false, optional = true, path = "../benchmarking" } +frame-support = { version = "4.0.0-dev", default-features = false, path = "../support" } +frame-system = { version = "4.0.0-dev", default-features = false, path = "../system" } +pallet-assets = { version = "4.0.0-dev", default-features = false, path = "../assets" } +pallet-nfts = { version = "4.0.0-dev", default-features = false, path = "../nfts" } +sp-runtime = { version = "7.0.0", default-features = false, path = "../../primitives/runtime" } +sp-std = { version = "5.0.0", default-features = false, path = "../../primitives/std" } + +[dev-dependencies] +pallet-balances = { version = "4.0.0-dev", path = "../balances" } +sp-core = { version = "7.0.0", path = "../../primitives/core" } +sp-io = { version = "7.0.0", path = "../../primitives/io" } +sp-std = { version = "5.0.0", path = "../../primitives/std" } + +[features] +default = ["std"] +std = [ + "codec/std", + "frame-benchmarking/std", + "frame-support/std", + "frame-system/std", + "log/std", + "pallet-assets/std", + "pallet-nfts/std", + "scale-info/std", + "sp-runtime/std", + "sp-std/std", +] +runtime-benchmarks = [ + "frame-benchmarking/runtime-benchmarks", + "frame-system/runtime-benchmarks", + "sp-runtime/runtime-benchmarks", +] +try-runtime = ["frame-support/try-runtime"] diff --git a/frame/nft-fractionalization/README.md b/frame/nft-fractionalization/README.md new file mode 100644 index 000000000..180eef22c --- /dev/null +++ b/frame/nft-fractionalization/README.md @@ -0,0 +1,6 @@ +### Lock NFT + +Lock an NFT from `pallet-nfts` and mint fungible assets from `pallet-assets`. + +The NFT gets locked by putting a system-level attribute named `Locked`. This prevents the NFT from being transferred further. +The NFT becomes unlocked when the `Locked` attribute is removed. In order to unify the fungible asset and unlock the NFT, an account must hold the full issuance of the asset the NFT was fractionalised into. Holding less of the fungible asset will not allow the unlocking of the NFT. diff --git a/frame/nft-fractionalization/src/benchmarking.rs b/frame/nft-fractionalization/src/benchmarking.rs new file mode 100644 index 000000000..a04e8de12 --- /dev/null +++ b/frame/nft-fractionalization/src/benchmarking.rs @@ -0,0 +1,135 @@ +// This file is part of Substrate. + +// Copyright (C) 2020-2022 Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +//! Nft fractionalization pallet benchmarking. + +#![cfg(feature = "runtime-benchmarks")] + +use super::*; +use frame_benchmarking::{benchmarks, whitelisted_caller}; +use frame_support::{ + assert_ok, + traits::{ + fungible::{Inspect as InspectFungible, Mutate as MutateFungible}, + tokens::nonfungibles_v2::{Create, Mutate}, + Get, + }, +}; +use frame_system::RawOrigin as SystemOrigin; +use pallet_nfts::{CollectionConfig, CollectionSettings, ItemConfig, MintSettings}; +use sp_runtime::traits::StaticLookup; +use sp_std::prelude::*; + +use crate::Pallet as NftFractionalization; + +type BalanceOf = + <::Currency as InspectFungible<::AccountId>>::Balance; + +type CollectionConfigOf = CollectionConfig< + BalanceOf, + ::BlockNumber, + ::NftCollectionId, +>; + +fn default_collection_config() -> CollectionConfigOf +where + T::Currency: InspectFungible, +{ + CollectionConfig { + settings: CollectionSettings::all_enabled(), + max_supply: None, + mint_settings: MintSettings::default(), + } +} + +fn mint_nft(nft_id: T::NftId) -> (T::AccountId, AccountIdLookupOf) +where + T::Nfts: Create, T::BlockNumber, T::NftCollectionId>> + + Mutate, +{ + let caller: T::AccountId = whitelisted_caller(); + let caller_lookup = T::Lookup::unlookup(caller.clone()); + let ed = T::Currency::minimum_balance(); + let multiplier = BalanceOf::::from(100u8); + T::Currency::set_balance(&caller, ed * multiplier + T::Deposit::get() * multiplier); + + assert_ok!(T::Nfts::create_collection(&caller, &caller, &default_collection_config::())); + let collection = T::BenchmarkHelper::collection(0); + assert_ok!(T::Nfts::mint_into(&collection, &nft_id, &caller, &ItemConfig::default(), true)); + (caller, caller_lookup) +} + +fn assert_last_event(generic_event: ::RuntimeEvent) { + let events = frame_system::Pallet::::events(); + let system_event: ::RuntimeEvent = generic_event.into(); + // compare to the last event record + let frame_system::EventRecord { event, .. } = &events[events.len() - 1]; + assert_eq!(event, &system_event); +} + +benchmarks! { + where_clause { + where + T::Nfts: Create, T::BlockNumber, T::NftCollectionId>> + + Mutate, + } + + fractionalize { + let asset = T::BenchmarkHelper::asset(0); + let collection = T::BenchmarkHelper::collection(0); + let nft = T::BenchmarkHelper::nft(0); + let (caller, caller_lookup) = mint_nft::(nft); + }: _(SystemOrigin::Signed(caller.clone()), collection, nft, asset, caller_lookup, 1000u32.into()) + verify { + assert_last_event::( + Event::NftFractionalized { + nft_collection: collection, + nft, + fractions: 1000u32.into(), + asset, + beneficiary: caller, + }.into() + ); + } + + unify { + let asset = T::BenchmarkHelper::asset(0); + let collection = T::BenchmarkHelper::collection(0); + let nft = T::BenchmarkHelper::nft(0); + let (caller, caller_lookup) = mint_nft::(nft); + NftFractionalization::::fractionalize( + SystemOrigin::Signed(caller.clone()).into(), + collection, + nft, + asset, + caller_lookup.clone(), + 1000u32.into(), + )?; + }: _(SystemOrigin::Signed(caller.clone()), collection, nft, asset, caller_lookup) + verify { + assert_last_event::( + Event::NftUnified { + nft_collection: collection, + nft, + asset, + beneficiary: caller, + }.into() + ); + } + + impl_benchmark_test_suite!(NftFractionalization, crate::mock::new_test_ext(), crate::mock::Test); +} diff --git a/frame/nft-fractionalization/src/lib.rs b/frame/nft-fractionalization/src/lib.rs new file mode 100644 index 000000000..c61719c5c --- /dev/null +++ b/frame/nft-fractionalization/src/lib.rs @@ -0,0 +1,389 @@ +// This file is part of Substrate. + +// Copyright (C) 2022 Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +//! # NFT Fractionalization Pallet +//! +//! This pallet provides the basic functionality that should allow users +//! to leverage partial ownership, transfers, and sales, of illiquid assets, +//! whether real-world assets represented by their digital twins, or NFTs, +//! or original NFTs. +//! +//! The functionality allows a user to lock an NFT they own, create a new +//! fungible asset, and mint a set amount of tokens (`fractions`). +//! +//! It also allows the user to burn 100% of the asset and to unlock the NFT +//! into their account. +//! +//! ### Functions +//! +//! * `fractionalize`: Lock the NFT and create and mint a new fungible asset. +//! * `unify`: Return 100% of the asset and unlock the NFT. + +// Ensure we're `no_std` when compiling for Wasm. +#![cfg_attr(not(feature = "std"), no_std)] + +mod types; + +#[cfg(feature = "runtime-benchmarks")] +mod benchmarking; +#[cfg(test)] +pub mod mock; +#[cfg(test)] +mod tests; + +pub mod weights; + +use frame_system::Config as SystemConfig; +pub use pallet::*; +pub use scale_info::Type; +pub use types::*; +pub use weights::WeightInfo; + +#[frame_support::pallet] +pub mod pallet { + use super::*; + use frame_support::{ + dispatch::DispatchResult, + ensure, + pallet_prelude::*, + sp_runtime::traits::{AccountIdConversion, StaticLookup}, + traits::{ + fungible::{ + hold::{Inspect as HoldInspectFungible, Mutate as HoldMutateFungible}, + Inspect as InspectFungible, Mutate as MutateFungible, + }, + fungibles::{ + metadata::{MetadataDeposit, Mutate as MutateMetadata}, + Create, Destroy, Inspect, Mutate, + }, + tokens::{ + nonfungibles_v2::{Inspect as NonFungiblesInspect, Transfer}, + AssetId, Balance as AssetBalance, + Fortitude::Polite, + Precision::{BestEffort, Exact}, + Preservation::Preserve, + }, + }, + BoundedVec, PalletId, + }; + use frame_system::pallet_prelude::*; + use scale_info::prelude::{format, string::String}; + use sp_runtime::traits::{One, Zero}; + use sp_std::{fmt::Display, prelude::*}; + + #[pallet::pallet] + pub struct Pallet(PhantomData); + + #[pallet::config] + pub trait Config: frame_system::Config { + /// The overarching event type. + type RuntimeEvent: From> + IsType<::RuntimeEvent>; + + /// The currency mechanism, used for paying for deposits. + type Currency: InspectFungible + + MutateFungible + + HoldInspectFungible + + HoldMutateFungible; + + #[pallet::constant] + type HoldReason: Get<>::Reason>; + + /// The deposit paid by the user locking an NFT. The deposit is returned to the original NFT + /// owner when the asset is unified and the NFT is unlocked. + #[pallet::constant] + type Deposit: Get>; + + /// Identifier for the collection of NFT. + type NftCollectionId: Member + Parameter + MaxEncodedLen + Copy + Display; + + /// The type used to identify an NFT within a collection. + type NftId: Member + Parameter + MaxEncodedLen + Copy + Display; + + /// The type used to describe the amount of fractions converted into assets. + type AssetBalance: AssetBalance; + + /// The type used to identify the assets created during fractionalization. + type AssetId: AssetId; + + /// Registry for the minted assets. + type Assets: Inspect + + Create + + Destroy + + Mutate + + MutateMetadata + + MetadataDeposit>; + + /// Registry for minted NFTs. + type Nfts: NonFungiblesInspect< + Self::AccountId, + ItemId = Self::NftId, + CollectionId = Self::NftCollectionId, + > + Transfer; + + /// The pallet's id, used for deriving its sovereign account ID. + #[pallet::constant] + type PalletId: Get; + + /// The newly created asset's symbol. + #[pallet::constant] + type NewAssetSymbol: Get>; + + /// The newly created asset's name. + #[pallet::constant] + type NewAssetName: Get>; + + /// The maximum length of a name or symbol stored on-chain. + #[pallet::constant] + type StringLimit: Get; + + /// A set of helper functions for benchmarking. + #[cfg(feature = "runtime-benchmarks")] + type BenchmarkHelper: BenchmarkHelper; + + /// Weight information for extrinsics in this pallet. + type WeightInfo: WeightInfo; + } + + /// Keeps track of the corresponding NFT ID, asset ID and amount minted. + #[pallet::storage] + #[pallet::getter(fn nft_to_asset)] + pub type NftToAsset = StorageMap< + _, + Blake2_128Concat, + (T::NftCollectionId, T::NftId), + Details, AssetBalanceOf, DepositOf, T::AccountId>, + OptionQuery, + >; + + #[pallet::event] + #[pallet::generate_deposit(pub(super) fn deposit_event)] + pub enum Event { + /// An NFT was successfully fractionalized. + NftFractionalized { + nft_collection: T::NftCollectionId, + nft: T::NftId, + fractions: AssetBalanceOf, + asset: AssetIdOf, + beneficiary: T::AccountId, + }, + /// An NFT was successfully returned back. + NftUnified { + nft_collection: T::NftCollectionId, + nft: T::NftId, + asset: AssetIdOf, + beneficiary: T::AccountId, + }, + } + + #[pallet::error] + pub enum Error { + /// Asset ID does not correspond to locked NFT. + IncorrectAssetId, + /// The signing account has no permission to do the operation. + NoPermission, + /// NFT doesn't exist. + NftNotFound, + /// NFT has not yet been fractionalised. + NftNotFractionalized, + } + + #[pallet::call] + impl Pallet { + /// Lock the NFT and mint a new fungible asset. + /// + /// The dispatch origin for this call must be Signed. + /// The origin must be the owner of the NFT they are trying to lock. + /// + /// `Deposit` funds of sender are reserved. + /// + /// - `nft_collection_id`: The ID used to identify the collection of the NFT. + /// Is used within the context of `pallet_nfts`. + /// - `nft_id`: The ID used to identify the NFT within the given collection. + /// Is used within the context of `pallet_nfts`. + /// - `asset_id`: The ID of the new asset. It must not exist. + /// Is used within the context of `pallet_assets`. + /// - `beneficiary`: The account that will receive the newly created asset. + /// - `fractions`: The total issuance of the newly created asset class. + /// + /// Emits `NftFractionalized` event when successful. + #[pallet::call_index(0)] + #[pallet::weight(T::WeightInfo::fractionalize())] + pub fn fractionalize( + origin: OriginFor, + nft_collection_id: T::NftCollectionId, + nft_id: T::NftId, + asset_id: AssetIdOf, + beneficiary: AccountIdLookupOf, + fractions: AssetBalanceOf, + ) -> DispatchResult { + let who = ensure_signed(origin)?; + let beneficiary = T::Lookup::lookup(beneficiary)?; + + let nft_owner = + T::Nfts::owner(&nft_collection_id, &nft_id).ok_or(Error::::NftNotFound)?; + ensure!(nft_owner == who, Error::::NoPermission); + + let pallet_account = Self::get_pallet_account(); + let deposit = T::Deposit::get(); + T::Currency::hold(&T::HoldReason::get(), &nft_owner, deposit)?; + Self::do_lock_nft(nft_collection_id, nft_id)?; + Self::do_create_asset(asset_id, pallet_account.clone())?; + Self::do_mint_asset(asset_id, &beneficiary, fractions)?; + Self::do_set_metadata(asset_id, &who, &pallet_account, &nft_collection_id, &nft_id)?; + + NftToAsset::::insert( + (nft_collection_id, nft_id), + Details { asset: asset_id, fractions, asset_creator: nft_owner, deposit }, + ); + + Self::deposit_event(Event::NftFractionalized { + nft_collection: nft_collection_id, + nft: nft_id, + fractions, + asset: asset_id, + beneficiary, + }); + + Ok(()) + } + + /// Burn the total issuance of the fungible asset and return (unlock) the locked NFT. + /// + /// The dispatch origin for this call must be Signed. + /// + /// `Deposit` funds will be returned to `asset_creator`. + /// + /// - `nft_collection_id`: The ID used to identify the collection of the NFT. + /// Is used within the context of `pallet_nfts`. + /// - `nft_id`: The ID used to identify the NFT within the given collection. + /// Is used within the context of `pallet_nfts`. + /// - `asset_id`: The ID of the asset being returned and destroyed. Must match + /// the original ID of the created asset, corresponding to the NFT. + /// Is used within the context of `pallet_assets`. + /// - `beneficiary`: The account that will receive the unified NFT. + /// + /// Emits `NftUnified` event when successful. + #[pallet::call_index(1)] + #[pallet::weight(T::WeightInfo::unify())] + pub fn unify( + origin: OriginFor, + nft_collection_id: T::NftCollectionId, + nft_id: T::NftId, + asset_id: AssetIdOf, + beneficiary: AccountIdLookupOf, + ) -> DispatchResult { + let who = ensure_signed(origin)?; + let beneficiary = T::Lookup::lookup(beneficiary)?; + + NftToAsset::::try_mutate_exists((nft_collection_id, nft_id), |maybe_details| { + let details = maybe_details.take().ok_or(Error::::NftNotFractionalized)?; + ensure!(details.asset == asset_id, Error::::IncorrectAssetId); + + let deposit = details.deposit; + let asset_creator = details.asset_creator; + Self::do_burn_asset(asset_id, &who, details.fractions)?; + Self::do_unlock_nft(nft_collection_id, nft_id, &beneficiary)?; + T::Currency::release(&T::HoldReason::get(), &asset_creator, deposit, BestEffort)?; + + Self::deposit_event(Event::NftUnified { + nft_collection: nft_collection_id, + nft: nft_id, + asset: asset_id, + beneficiary, + }); + + Ok(()) + }) + } + } + + impl Pallet { + /// The account ID of the pallet. + /// + /// This actually does computation. If you need to keep using it, then make sure you cache + /// the value and only call this once. + fn get_pallet_account() -> T::AccountId { + T::PalletId::get().into_account_truncating() + } + + /// Transfer the NFT from the account holding that NFT to the pallet's account. + fn do_lock_nft(nft_collection_id: T::NftCollectionId, nft_id: T::NftId) -> DispatchResult { + T::Nfts::disable_transfer(&nft_collection_id, &nft_id) + } + + /// Transfer the NFT to the account returning the tokens. + fn do_unlock_nft( + nft_collection_id: T::NftCollectionId, + nft_id: T::NftId, + account: &T::AccountId, + ) -> DispatchResult { + T::Nfts::enable_transfer(&nft_collection_id, &nft_id)?; + T::Nfts::transfer(&nft_collection_id, &nft_id, account) + } + + /// Create the new asset. + fn do_create_asset(asset_id: AssetIdOf, admin: T::AccountId) -> DispatchResult { + T::Assets::create(asset_id, admin, false, One::one()) + } + + /// Mint the `amount` of tokens with `asset_id` into the beneficiary's account. + fn do_mint_asset( + asset_id: AssetIdOf, + beneficiary: &T::AccountId, + amount: AssetBalanceOf, + ) -> DispatchResult { + T::Assets::mint_into(asset_id, beneficiary, amount)?; + Ok(()) + } + + /// Burn tokens from the account. + fn do_burn_asset( + asset_id: AssetIdOf, + account: &T::AccountId, + amount: AssetBalanceOf, + ) -> DispatchResult { + T::Assets::burn_from(asset_id, account, amount, Exact, Polite)?; + T::Assets::start_destroy(asset_id, None) + } + + /// Set the metadata for the newly created asset. + fn do_set_metadata( + asset_id: AssetIdOf, + depositor: &T::AccountId, + pallet_account: &T::AccountId, + nft_collection_id: &T::NftCollectionId, + nft_id: &T::NftId, + ) -> DispatchResult { + let name = format!( + "{} {nft_collection_id}-{nft_id}", + String::from_utf8_lossy(&T::NewAssetName::get()) + ); + let symbol: &[u8] = &T::NewAssetSymbol::get(); + let existential_deposit = T::Currency::minimum_balance(); + let pallet_account_balance = T::Currency::balance(&pallet_account); + + if pallet_account_balance < existential_deposit { + T::Currency::transfer(&depositor, &pallet_account, existential_deposit, Preserve)?; + } + let metadata_deposit = T::Assets::calc_metadata_deposit(name.as_bytes(), symbol); + if !metadata_deposit.is_zero() { + T::Currency::transfer(&depositor, &pallet_account, metadata_deposit, Preserve)?; + } + T::Assets::set(asset_id, &pallet_account, name.into(), symbol.into(), 0) + } + } +} diff --git a/frame/nft-fractionalization/src/mock.rs b/frame/nft-fractionalization/src/mock.rs new file mode 100644 index 000000000..05fbadb03 --- /dev/null +++ b/frame/nft-fractionalization/src/mock.rs @@ -0,0 +1,202 @@ +// This file is part of Substrate. + +// Copyright (C) 2019-2022 Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +//! Test environment for Nft fractionalization pallet. + +use super::*; +use crate as pallet_nft_fractionalization; + +use codec::{Decode, Encode, MaxEncodedLen}; +use frame_support::{ + construct_runtime, parameter_types, + traits::{AsEnsureOriginWithArg, ConstU32, ConstU64}, + BoundedVec, PalletId, +}; +use frame_system::EnsureSigned; +use pallet_nfts::PalletFeatures; +use scale_info::TypeInfo; +use sp_core::H256; +use sp_runtime::{ + testing::Header, + traits::{BlakeTwo256, IdentifyAccount, IdentityLookup, Verify}, + MultiSignature, +}; + +type UncheckedExtrinsic = frame_system::mocking::MockUncheckedExtrinsic; +type Block = frame_system::mocking::MockBlock; +type Signature = MultiSignature; +type AccountPublic = ::Signer; +type AccountId = ::AccountId; + +// Configure a mock runtime to test the pallet. +construct_runtime!( + pub enum Test where + Block = Block, + NodeBlock = Block, + UncheckedExtrinsic = UncheckedExtrinsic, + { + System: frame_system, + NftFractionalization: pallet_nft_fractionalization, + Assets: pallet_assets, + Balances: pallet_balances, + Nfts: pallet_nfts, + } +); +impl frame_system::Config for Test { + type BaseCallFilter = frame_support::traits::Everything; + type BlockWeights = (); + type BlockLength = (); + type RuntimeOrigin = RuntimeOrigin; + type RuntimeCall = RuntimeCall; + type Index = u64; + type BlockNumber = u64; + type Hash = H256; + type Hashing = BlakeTwo256; + type AccountId = AccountId; + type Lookup = IdentityLookup; + type Header = Header; + type RuntimeEvent = RuntimeEvent; + type BlockHashCount = ConstU64<250>; + type DbWeight = (); + type Version = (); + type PalletInfo = PalletInfo; + type AccountData = pallet_balances::AccountData; + type OnNewAccount = (); + type OnKilledAccount = (); + type SystemWeightInfo = (); + type SS58Prefix = (); + type OnSetCode = (); + type MaxConsumers = ConstU32<16>; +} + +#[derive( + Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Encode, Decode, MaxEncodedLen, Debug, TypeInfo, +)] +pub enum HoldIdentifier { + NftFractionalization, +} + +impl pallet_balances::Config for Test { + type Balance = u64; + type DustRemoval = (); + type RuntimeEvent = RuntimeEvent; + type ExistentialDeposit = ConstU64<1>; + type AccountStore = System; + type WeightInfo = (); + type MaxLocks = (); + type MaxReserves = ConstU32<50>; + type ReserveIdentifier = [u8; 8]; + type HoldIdentifier = HoldIdentifier; + type MaxHolds = ConstU32<1>; + type FreezeIdentifier = (); + type MaxFreezes = (); +} + +impl pallet_assets::Config for Test { + type RuntimeEvent = RuntimeEvent; + type Balance = u64; + type RemoveItemsLimit = ConstU32<1000>; + type AssetId = u32; + type AssetIdParameter = u32; + type Currency = Balances; + type CreateOrigin = AsEnsureOriginWithArg>; + type ForceOrigin = frame_system::EnsureRoot; + type AssetDeposit = ConstU64<1>; + type AssetAccountDeposit = ConstU64<10>; + type MetadataDepositBase = ConstU64<1>; + type MetadataDepositPerByte = ConstU64<1>; + type ApprovalDeposit = ConstU64<1>; + type StringLimit = ConstU32<50>; + type Freezer = (); + type Extra = (); + type CallbackHandle = (); + type WeightInfo = (); + pallet_assets::runtime_benchmarks_enabled! { + type BenchmarkHelper = (); + } +} + +parameter_types! { + pub storage Features: PalletFeatures = PalletFeatures::all_enabled(); +} + +impl pallet_nfts::Config for Test { + type RuntimeEvent = RuntimeEvent; + type CollectionId = u32; + type ItemId = u32; + type Currency = Balances; + type CreateOrigin = AsEnsureOriginWithArg>; + type ForceOrigin = frame_system::EnsureRoot; + type Locker = (); + type CollectionDeposit = ConstU64<2>; + type ItemDeposit = ConstU64<1>; + type MetadataDepositBase = ConstU64<1>; + type AttributeDepositBase = ConstU64<1>; + type DepositPerByte = ConstU64<1>; + type StringLimit = ConstU32<50>; + type KeyLimit = ConstU32<50>; + type ValueLimit = ConstU32<50>; + type ApprovalsLimit = ConstU32<10>; + type ItemAttributesApprovalsLimit = ConstU32<2>; + type MaxTips = ConstU32<10>; + type MaxDeadlineDuration = ConstU64<10000>; + type MaxAttributesPerCall = ConstU32<2>; + type Features = Features; + type OffchainSignature = Signature; + type OffchainPublic = AccountPublic; + type WeightInfo = (); + pallet_nfts::runtime_benchmarks_enabled! { + type Helper = (); + } +} + +parameter_types! { + pub const StringLimit: u32 = 50; + pub const NftFractionalizationPalletId: PalletId = PalletId(*b"fraction"); + pub NewAssetSymbol: BoundedVec = (*b"FRAC").to_vec().try_into().unwrap(); + pub NewAssetName: BoundedVec = (*b"Frac").to_vec().try_into().unwrap(); + pub const HoldReason: HoldIdentifier = HoldIdentifier::NftFractionalization; +} + +impl Config for Test { + type RuntimeEvent = RuntimeEvent; + type Deposit = ConstU64<1>; + type Currency = Balances; + type NewAssetSymbol = NewAssetSymbol; + type NewAssetName = NewAssetName; + type NftCollectionId = ::CollectionId; + type NftId = ::ItemId; + type AssetBalance = ::Balance; + type AssetId = ::AssetId; + type Assets = Assets; + type Nfts = Nfts; + type PalletId = NftFractionalizationPalletId; + type WeightInfo = (); + type StringLimit = StringLimit; + #[cfg(feature = "runtime-benchmarks")] + type BenchmarkHelper = (); + type HoldReason = HoldReason; +} + +// Build genesis storage according to the mock runtime. +pub(crate) fn new_test_ext() -> sp_io::TestExternalities { + let t = frame_system::GenesisConfig::default().build_storage::().unwrap(); + + let mut ext = sp_io::TestExternalities::new(t); + ext.execute_with(|| System::set_block_number(1)); + ext +} diff --git a/frame/nft-fractionalization/src/tests.rs b/frame/nft-fractionalization/src/tests.rs new file mode 100644 index 000000000..8564b8053 --- /dev/null +++ b/frame/nft-fractionalization/src/tests.rs @@ -0,0 +1,278 @@ +// This file is part of Substrate. + +// Copyright (C) 2019-2022 Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +//! Tests for Nft fractionalization pallet. + +use crate::{mock::*, *}; +use frame_support::{ + assert_noop, assert_ok, + traits::{ + fungible::{hold::Inspect as InspectHold, Mutate as MutateFungible}, + fungibles::{metadata::Inspect, InspectEnumerable}, + }, +}; +use pallet_nfts::CollectionConfig; +use sp_runtime::{DispatchError, ModuleError, TokenError::FundsUnavailable}; + +fn assets() -> Vec { + let mut s: Vec<_> = <::Assets>::asset_ids().collect(); + s.sort(); + s +} + +fn events() -> Vec> { + let result = System::events() + .into_iter() + .map(|r| r.event) + .filter_map(|e| { + if let mock::RuntimeEvent::NftFractionalization(inner) = e { + Some(inner) + } else { + None + } + }) + .collect(); + + System::reset_events(); + + result +} + +type AccountIdOf = ::AccountId; + +fn account(id: u8) -> AccountIdOf { + [id; 32].into() +} + +#[test] +fn fractionalize_should_work() { + new_test_ext().execute_with(|| { + let nft_collection_id = 0; + let nft_id = 0; + let asset_id = 0; + let fractions = 1000; + + Balances::set_balance(&account(1), 100); + Balances::set_balance(&account(2), 100); + + assert_ok!(Nfts::force_create( + RuntimeOrigin::root(), + account(1), + CollectionConfig::default(), + )); + assert_ok!(Nfts::mint( + RuntimeOrigin::signed(account(1)), + nft_collection_id, + nft_id, + account(1), + None, + )); + + assert_ok!(NftFractionalization::fractionalize( + RuntimeOrigin::signed(account(1)), + nft_collection_id, + nft_id, + asset_id, + account(2), + fractions, + )); + assert_eq!(assets(), vec![asset_id]); + assert_eq!(Assets::balance(asset_id, account(2)), fractions); + assert_eq!(Balances::total_balance_on_hold(&account(1)), 2); + assert_eq!(String::from_utf8(Assets::name(0)).unwrap(), "Frac 0-0"); + assert_eq!(String::from_utf8(Assets::symbol(0)).unwrap(), "FRAC"); + assert_eq!(Nfts::owner(nft_collection_id, nft_id), Some(account(1))); + assert_noop!( + Nfts::transfer( + RuntimeOrigin::signed(account(1)), + nft_collection_id, + nft_id, + account(2), + ), + DispatchError::Module(ModuleError { + index: 4, + error: [12, 0, 0, 0], + message: Some("ItemLocked") + }) + ); + + let details = NftToAsset::::get((&nft_collection_id, &nft_id)).unwrap(); + assert_eq!(details.asset, asset_id); + assert_eq!(details.fractions, fractions); + + assert!(events().contains(&Event::::NftFractionalized { + nft_collection: nft_collection_id, + nft: nft_id, + fractions, + asset: asset_id, + beneficiary: account(2), + })); + + let nft_id = nft_id + 1; + assert_noop!( + NftFractionalization::fractionalize( + RuntimeOrigin::signed(account(1)), + nft_collection_id, + nft_id, + asset_id, + account(2), + fractions, + ), + Error::::NftNotFound + ); + + assert_ok!(Nfts::mint( + RuntimeOrigin::signed(account(1)), + nft_collection_id, + nft_id, + account(2), + None + )); + assert_noop!( + NftFractionalization::fractionalize( + RuntimeOrigin::signed(account(1)), + nft_collection_id, + nft_id, + asset_id, + account(2), + fractions, + ), + Error::::NoPermission + ); + }); +} + +#[test] +fn unify_should_work() { + new_test_ext().execute_with(|| { + let nft_collection_id = 0; + let nft_id = 0; + let asset_id = 0; + let fractions = 1000; + + Balances::set_balance(&account(1), 100); + Balances::set_balance(&account(2), 100); + + assert_ok!(Nfts::force_create( + RuntimeOrigin::root(), + account(1), + CollectionConfig::default(), + )); + assert_ok!(Nfts::mint( + RuntimeOrigin::signed(account(1)), + nft_collection_id, + nft_id, + account(1), + None, + )); + assert_ok!(NftFractionalization::fractionalize( + RuntimeOrigin::signed(account(1)), + nft_collection_id, + nft_id, + asset_id, + account(2), + fractions, + )); + + assert_noop!( + NftFractionalization::unify( + RuntimeOrigin::signed(account(2)), + nft_collection_id + 1, + nft_id, + asset_id, + account(1), + ), + Error::::NftNotFractionalized + ); + assert_noop!( + NftFractionalization::unify( + RuntimeOrigin::signed(account(2)), + nft_collection_id, + nft_id, + asset_id + 1, + account(1), + ), + Error::::IncorrectAssetId + ); + + // can't unify the asset a user doesn't hold + assert_noop!( + NftFractionalization::unify( + RuntimeOrigin::signed(account(1)), + nft_collection_id, + nft_id, + asset_id, + account(1), + ), + DispatchError::Token(FundsUnavailable) + ); + + assert_ok!(NftFractionalization::unify( + RuntimeOrigin::signed(account(2)), + nft_collection_id, + nft_id, + asset_id, + account(1), + )); + + assert_eq!(Assets::balance(asset_id, account(2)), 0); + assert_eq!(Balances::reserved_balance(&account(1)), 1); + assert_eq!(Nfts::owner(nft_collection_id, nft_id), Some(account(1))); + assert!(!NftToAsset::::contains_key((&nft_collection_id, &nft_id))); + + assert!(events().contains(&Event::::NftUnified { + nft_collection: nft_collection_id, + nft: nft_id, + asset: asset_id, + beneficiary: account(1), + })); + + // validate we need to hold the full balance to un-fractionalize the NFT + let asset_id = asset_id + 1; + assert_ok!(NftFractionalization::fractionalize( + RuntimeOrigin::signed(account(1)), + nft_collection_id, + nft_id, + asset_id, + account(1), + fractions, + )); + assert_ok!(Assets::transfer(RuntimeOrigin::signed(account(1)), asset_id, account(2), 1)); + assert_eq!(Assets::balance(asset_id, account(1)), fractions - 1); + assert_eq!(Assets::balance(asset_id, account(2)), 1); + assert_noop!( + NftFractionalization::unify( + RuntimeOrigin::signed(account(1)), + nft_collection_id, + nft_id, + asset_id, + account(1), + ), + DispatchError::Token(FundsUnavailable) + ); + + assert_ok!(Assets::transfer(RuntimeOrigin::signed(account(2)), asset_id, account(1), 1)); + assert_ok!(NftFractionalization::unify( + RuntimeOrigin::signed(account(1)), + nft_collection_id, + nft_id, + asset_id, + account(2), + )); + assert_eq!(Nfts::owner(nft_collection_id, nft_id), Some(account(2))); + }); +} diff --git a/frame/nft-fractionalization/src/types.rs b/frame/nft-fractionalization/src/types.rs new file mode 100644 index 000000000..cbaaf5f51 --- /dev/null +++ b/frame/nft-fractionalization/src/types.rs @@ -0,0 +1,76 @@ +// This file is part of Substrate. + +// Copyright (C) 2017-2022 Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +//! Various basic types for use in the Nft fractionalization pallet. + +use super::*; +use codec::{Decode, Encode, MaxEncodedLen}; +use frame_support::traits::{fungible::Inspect as FunInspect, fungibles::Inspect}; +use scale_info::TypeInfo; +use sp_runtime::traits::StaticLookup; + +pub type AssetIdOf = <::Assets as Inspect<::AccountId>>::AssetId; +pub type AssetBalanceOf = + <::Assets as Inspect<::AccountId>>::Balance; +pub type DepositOf = + <::Currency as FunInspect<::AccountId>>::Balance; +pub type AccountIdLookupOf = <::Lookup as StaticLookup>::Source; + +/// Stores the details of a fractionalized item. +#[derive(Decode, Encode, Default, PartialEq, Eq, MaxEncodedLen, TypeInfo)] +pub struct Details { + /// Minted asset. + pub asset: AssetId, + + /// Number of fractions minted. + pub fractions: Fractions, + + /// Reserved deposit for creating a new asset. + pub deposit: Deposit, + + /// Account that fractionalized an item. + pub asset_creator: AccountId, +} + +/// Benchmark Helper +#[cfg(feature = "runtime-benchmarks")] +pub trait BenchmarkHelper { + /// Returns an asset id from a given integer. + fn asset(id: u32) -> AssetId; + /// Returns a collection id from a given integer. + fn collection(id: u32) -> CollectionId; + /// Returns an nft id from a given integer. + fn nft(id: u32) -> ItemId; +} + +#[cfg(feature = "runtime-benchmarks")] +impl BenchmarkHelper for () +where + AssetId: From, + CollectionId: From, + ItemId: From, +{ + fn asset(id: u32) -> AssetId { + id.into() + } + fn collection(id: u32) -> CollectionId { + id.into() + } + fn nft(id: u32) -> ItemId { + id.into() + } +} diff --git a/frame/nft-fractionalization/src/weights.rs b/frame/nft-fractionalization/src/weights.rs new file mode 100644 index 000000000..735b648b8 --- /dev/null +++ b/frame/nft-fractionalization/src/weights.rs @@ -0,0 +1,184 @@ +// This file is part of Substrate. + +// Copyright (C) Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +//! Autogenerated weights for pallet_nft_fractionalization +//! +//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev +//! DATE: 2023-05-19, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! WORST CASE MAP SIZE: `1000000` +//! HOSTNAME: `bm3`, CPU: `Intel(R) Core(TM) i7-7700K CPU @ 4.20GHz` +//! EXECUTION: Some(Wasm), WASM-EXECUTION: Compiled, CHAIN: Some("dev"), DB CACHE: 1024 + +// Executed Command: +// target/production/substrate +// benchmark +// pallet +// --steps=50 +// --repeat=20 +// --extrinsic=* +// --execution=wasm +// --wasm-execution=compiled +// --heap-pages=4096 +// --json-file=/var/lib/gitlab-runner/builds/zyw4fam_/0/parity/mirrors/substrate/.git/.artifacts/bench.json +// --pallet=pallet_nft_fractionalization +// --chain=dev +// --header=./HEADER-APACHE2 +// --output=./frame/nft-fractionalization/src/weights.rs +// --template=./.maintain/frame-weight-template.hbs + +#![cfg_attr(rustfmt, rustfmt_skip)] +#![allow(unused_parens)] +#![allow(unused_imports)] +#![allow(missing_docs)] + +use frame_support::{traits::Get, weights::{Weight, constants::RocksDbWeight}}; +use core::marker::PhantomData; + +/// Weight functions needed for pallet_nft_fractionalization. +pub trait WeightInfo { + fn fractionalize() -> Weight; + fn unify() -> Weight; +} + +/// Weights for pallet_nft_fractionalization using the Substrate node and recommended hardware. +pub struct SubstrateWeight(PhantomData); +impl WeightInfo for SubstrateWeight { + /// Storage: Nfts Item (r:1 w:0) + /// Proof: Nfts Item (max_values: None, max_size: Some(861), added: 3336, mode: MaxEncodedLen) + /// Storage: Balances Holds (r:1 w:1) + /// Proof: Balances Holds (max_values: None, max_size: Some(83), added: 2558, mode: MaxEncodedLen) + /// Storage: Nfts Collection (r:1 w:1) + /// Proof: Nfts Collection (max_values: None, max_size: Some(84), added: 2559, mode: MaxEncodedLen) + /// Storage: Nfts Attribute (r:1 w:1) + /// Proof: Nfts Attribute (max_values: None, max_size: Some(446), added: 2921, mode: MaxEncodedLen) + /// Storage: Assets Asset (r:1 w:1) + /// Proof: Assets Asset (max_values: None, max_size: Some(210), added: 2685, mode: MaxEncodedLen) + /// Storage: Assets Account (r:1 w:1) + /// Proof: Assets Account (max_values: None, max_size: Some(134), added: 2609, mode: MaxEncodedLen) + /// Storage: System Account (r:1 w:1) + /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) + /// Storage: Assets Metadata (r:1 w:1) + /// Proof: Assets Metadata (max_values: None, max_size: Some(140), added: 2615, mode: MaxEncodedLen) + /// Storage: NftFractionalization NftToAsset (r:0 w:1) + /// Proof: NftFractionalization NftToAsset (max_values: None, max_size: Some(92), added: 2567, mode: MaxEncodedLen) + fn fractionalize() -> Weight { + // Proof Size summary in bytes: + // Measured: `609` + // Estimated: `4326` + // Minimum execution time: 177_498_000 picoseconds. + Weight::from_parts(178_803_000, 4326) + .saturating_add(T::DbWeight::get().reads(8_u64)) + .saturating_add(T::DbWeight::get().writes(8_u64)) + } + /// Storage: NftFractionalization NftToAsset (r:1 w:1) + /// Proof: NftFractionalization NftToAsset (max_values: None, max_size: Some(92), added: 2567, mode: MaxEncodedLen) + /// Storage: Assets Asset (r:1 w:1) + /// Proof: Assets Asset (max_values: None, max_size: Some(210), added: 2685, mode: MaxEncodedLen) + /// Storage: Assets Account (r:1 w:1) + /// Proof: Assets Account (max_values: None, max_size: Some(134), added: 2609, mode: MaxEncodedLen) + /// Storage: Nfts Attribute (r:1 w:1) + /// Proof: Nfts Attribute (max_values: None, max_size: Some(446), added: 2921, mode: MaxEncodedLen) + /// Storage: Nfts Collection (r:1 w:1) + /// Proof: Nfts Collection (max_values: None, max_size: Some(84), added: 2559, mode: MaxEncodedLen) + /// Storage: Nfts CollectionConfigOf (r:1 w:0) + /// Proof: Nfts CollectionConfigOf (max_values: None, max_size: Some(73), added: 2548, mode: MaxEncodedLen) + /// Storage: Nfts ItemConfigOf (r:1 w:0) + /// Proof: Nfts ItemConfigOf (max_values: None, max_size: Some(48), added: 2523, mode: MaxEncodedLen) + /// Storage: Nfts Item (r:1 w:1) + /// Proof: Nfts Item (max_values: None, max_size: Some(861), added: 3336, mode: MaxEncodedLen) + /// Storage: Balances Holds (r:1 w:1) + /// Proof: Balances Holds (max_values: None, max_size: Some(83), added: 2558, mode: MaxEncodedLen) + /// Storage: Nfts Account (r:0 w:1) + /// Proof: Nfts Account (max_values: None, max_size: Some(88), added: 2563, mode: MaxEncodedLen) + /// Storage: Nfts ItemPriceOf (r:0 w:1) + /// Proof: Nfts ItemPriceOf (max_values: None, max_size: Some(89), added: 2564, mode: MaxEncodedLen) + /// Storage: Nfts PendingSwapOf (r:0 w:1) + /// Proof: Nfts PendingSwapOf (max_values: None, max_size: Some(71), added: 2546, mode: MaxEncodedLen) + fn unify() -> Weight { + // Proof Size summary in bytes: + // Measured: `1421` + // Estimated: `4326` + // Minimum execution time: 130_284_000 picoseconds. + Weight::from_parts(131_122_000, 4326) + .saturating_add(T::DbWeight::get().reads(9_u64)) + .saturating_add(T::DbWeight::get().writes(10_u64)) + } +} + +// For backwards compatibility and tests +impl WeightInfo for () { + /// Storage: Nfts Item (r:1 w:0) + /// Proof: Nfts Item (max_values: None, max_size: Some(861), added: 3336, mode: MaxEncodedLen) + /// Storage: Balances Holds (r:1 w:1) + /// Proof: Balances Holds (max_values: None, max_size: Some(83), added: 2558, mode: MaxEncodedLen) + /// Storage: Nfts Collection (r:1 w:1) + /// Proof: Nfts Collection (max_values: None, max_size: Some(84), added: 2559, mode: MaxEncodedLen) + /// Storage: Nfts Attribute (r:1 w:1) + /// Proof: Nfts Attribute (max_values: None, max_size: Some(446), added: 2921, mode: MaxEncodedLen) + /// Storage: Assets Asset (r:1 w:1) + /// Proof: Assets Asset (max_values: None, max_size: Some(210), added: 2685, mode: MaxEncodedLen) + /// Storage: Assets Account (r:1 w:1) + /// Proof: Assets Account (max_values: None, max_size: Some(134), added: 2609, mode: MaxEncodedLen) + /// Storage: System Account (r:1 w:1) + /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) + /// Storage: Assets Metadata (r:1 w:1) + /// Proof: Assets Metadata (max_values: None, max_size: Some(140), added: 2615, mode: MaxEncodedLen) + /// Storage: NftFractionalization NftToAsset (r:0 w:1) + /// Proof: NftFractionalization NftToAsset (max_values: None, max_size: Some(92), added: 2567, mode: MaxEncodedLen) + fn fractionalize() -> Weight { + // Proof Size summary in bytes: + // Measured: `609` + // Estimated: `4326` + // Minimum execution time: 177_498_000 picoseconds. + Weight::from_parts(178_803_000, 4326) + .saturating_add(RocksDbWeight::get().reads(8_u64)) + .saturating_add(RocksDbWeight::get().writes(8_u64)) + } + /// Storage: NftFractionalization NftToAsset (r:1 w:1) + /// Proof: NftFractionalization NftToAsset (max_values: None, max_size: Some(92), added: 2567, mode: MaxEncodedLen) + /// Storage: Assets Asset (r:1 w:1) + /// Proof: Assets Asset (max_values: None, max_size: Some(210), added: 2685, mode: MaxEncodedLen) + /// Storage: Assets Account (r:1 w:1) + /// Proof: Assets Account (max_values: None, max_size: Some(134), added: 2609, mode: MaxEncodedLen) + /// Storage: Nfts Attribute (r:1 w:1) + /// Proof: Nfts Attribute (max_values: None, max_size: Some(446), added: 2921, mode: MaxEncodedLen) + /// Storage: Nfts Collection (r:1 w:1) + /// Proof: Nfts Collection (max_values: None, max_size: Some(84), added: 2559, mode: MaxEncodedLen) + /// Storage: Nfts CollectionConfigOf (r:1 w:0) + /// Proof: Nfts CollectionConfigOf (max_values: None, max_size: Some(73), added: 2548, mode: MaxEncodedLen) + /// Storage: Nfts ItemConfigOf (r:1 w:0) + /// Proof: Nfts ItemConfigOf (max_values: None, max_size: Some(48), added: 2523, mode: MaxEncodedLen) + /// Storage: Nfts Item (r:1 w:1) + /// Proof: Nfts Item (max_values: None, max_size: Some(861), added: 3336, mode: MaxEncodedLen) + /// Storage: Balances Holds (r:1 w:1) + /// Proof: Balances Holds (max_values: None, max_size: Some(83), added: 2558, mode: MaxEncodedLen) + /// Storage: Nfts Account (r:0 w:1) + /// Proof: Nfts Account (max_values: None, max_size: Some(88), added: 2563, mode: MaxEncodedLen) + /// Storage: Nfts ItemPriceOf (r:0 w:1) + /// Proof: Nfts ItemPriceOf (max_values: None, max_size: Some(89), added: 2564, mode: MaxEncodedLen) + /// Storage: Nfts PendingSwapOf (r:0 w:1) + /// Proof: Nfts PendingSwapOf (max_values: None, max_size: Some(71), added: 2546, mode: MaxEncodedLen) + fn unify() -> Weight { + // Proof Size summary in bytes: + // Measured: `1421` + // Estimated: `4326` + // Minimum execution time: 130_284_000 picoseconds. + Weight::from_parts(131_122_000, 4326) + .saturating_add(RocksDbWeight::get().reads(9_u64)) + .saturating_add(RocksDbWeight::get().writes(10_u64)) + } +} diff --git a/frame/nfts/src/features/attributes.rs b/frame/nfts/src/features/attributes.rs index 9098679fa..8a9bbe8a6 100644 --- a/frame/nfts/src/features/attributes.rs +++ b/frame/nfts/src/features/attributes.rs @@ -394,4 +394,19 @@ impl, I: 'static> Pallet { ) -> Result, DispatchError> { Ok(BoundedVec::try_from(value).map_err(|_| Error::::IncorrectData)?) } + + /// A helper method to check whether a system attribute is set for a given item. + pub fn has_system_attribute( + collection: &T::CollectionId, + item: &T::ItemId, + attribute_key: PalletAttributes, + ) -> Result { + let attribute = ( + &collection, + Some(item), + AttributeNamespace::Pallet, + &Self::construct_attribute_key(attribute_key.encode())?, + ); + Ok(Attribute::::contains_key(attribute)) + } } diff --git a/frame/nfts/src/features/transfer.rs b/frame/nfts/src/features/transfer.rs index 00b5d4e76..69209e1bb 100644 --- a/frame/nfts/src/features/transfer.rs +++ b/frame/nfts/src/features/transfer.rs @@ -30,7 +30,12 @@ impl, I: 'static> Pallet { ) -> DispatchResult { let collection_details = Collection::::get(&collection).ok_or(Error::::UnknownCollection)?; + ensure!(!T::Locker::is_locked(collection, item), Error::::ItemLocked); + ensure!( + !Self::has_system_attribute(&collection, &item, PalletAttributes::TransferDisabled)?, + Error::::ItemLocked + ); let collection_config = Self::get_collection_config(&collection)?; ensure!( diff --git a/frame/nfts/src/impl_nonfungibles.rs b/frame/nfts/src/impl_nonfungibles.rs index ef6bbe765..a2bb49a94 100644 --- a/frame/nfts/src/impl_nonfungibles.rs +++ b/frame/nfts/src/impl_nonfungibles.rs @@ -117,6 +117,11 @@ impl, I: 'static> Inspect<::AccountId> for Palle /// /// Default implementation is that all items are transferable. fn can_transfer(collection: &Self::CollectionId, item: &Self::ItemId) -> bool { + use PalletAttributes::TransferDisabled; + match Self::has_system_attribute(&collection, &item, TransferDisabled) { + Ok(transfer_disabled) if transfer_disabled => return false, + _ => (), + } match ( CollectionConfigOf::::get(collection), ItemConfigOf::::get(collection, item), @@ -322,6 +327,23 @@ impl, I: 'static> Transfer for Pallet { ) -> DispatchResult { Self::do_transfer(*collection, *item, destination.clone(), |_, _| Ok(())) } + + fn disable_transfer(collection: &Self::CollectionId, item: &Self::ItemId) -> DispatchResult { + >::set_attribute( + collection, + item, + &PalletAttributes::::TransferDisabled.encode(), + &[], + ) + } + + fn enable_transfer(collection: &Self::CollectionId, item: &Self::ItemId) -> DispatchResult { + >::clear_attribute( + collection, + item, + &PalletAttributes::::TransferDisabled.encode(), + ) + } } impl, I: 'static> InspectEnumerable for Pallet { diff --git a/frame/nfts/src/types.rs b/frame/nfts/src/types.rs index fe6d31c12..8f36acd28 100644 --- a/frame/nfts/src/types.rs +++ b/frame/nfts/src/types.rs @@ -346,6 +346,8 @@ pub struct CancelAttributesApprovalWitness { pub enum PalletAttributes { /// Marks an item as being used in order to claim another item. UsedToClaim(CollectionId), + /// Marks an item as being restricted from transferring. + TransferDisabled, } /// Collection's configuration. diff --git a/frame/support/src/traits/tokens/fungible/regular.rs b/frame/support/src/traits/tokens/fungible/regular.rs index 347654946..c0658ad71 100644 --- a/frame/support/src/traits/tokens/fungible/regular.rs +++ b/frame/support/src/traits/tokens/fungible/regular.rs @@ -58,7 +58,8 @@ pub trait Inspect: Sized { /// The minimum balance any single account may have. fn minimum_balance() -> Self::Balance; - /// Get the total amount of funds whose ultimate bneficial ownership can be determined as `who`. + /// Get the total amount of funds whose ultimate beneficial ownership can be determined as + /// `who`. /// /// This may include funds which are wholly inaccessible to `who`, either temporarily or even /// indefinitely. diff --git a/frame/support/src/traits/tokens/fungibles/metadata.rs b/frame/support/src/traits/tokens/fungibles/metadata.rs index 64f8bf094..ab310119e 100644 --- a/frame/support/src/traits/tokens/fungibles/metadata.rs +++ b/frame/support/src/traits/tokens/fungibles/metadata.rs @@ -39,3 +39,8 @@ pub trait Mutate: Inspect { decimals: u8, ) -> DispatchResult; } + +pub trait MetadataDeposit { + // Returns the required deposit amount for a given metadata. + fn calc_metadata_deposit(name: &[u8], symbol: &[u8]) -> DepositBalance; +} diff --git a/frame/support/src/traits/tokens/misc.rs b/frame/support/src/traits/tokens/misc.rs index 0ba900e95..d1b17b548 100644 --- a/frame/support/src/traits/tokens/misc.rs +++ b/frame/support/src/traits/tokens/misc.rs @@ -262,8 +262,8 @@ pub trait ConversionFromAssetBalance { ) -> Result; } -/// Trait to handle asset locking mechanism to ensure interactions with the asset can be implemented -/// downstream to extend logic of Uniques current functionality. +/// Trait to handle NFT locking mechanism to ensure interactions with the asset can be implemented +/// downstream to extend logic of Uniques/Nfts current functionality. pub trait Locker { /// Check if the asset should be locked and prevent interactions with the asset from executing. fn is_locked(collection: CollectionId, item: ItemId) -> bool; diff --git a/frame/support/src/traits/tokens/nonfungible_v2.rs b/frame/support/src/traits/tokens/nonfungible_v2.rs index c23bf3e40..c4463e007 100644 --- a/frame/support/src/traits/tokens/nonfungible_v2.rs +++ b/frame/support/src/traits/tokens/nonfungible_v2.rs @@ -173,10 +173,18 @@ pub trait Mutate: Inspect { } } -/// Trait for transferring a non-fungible item. +/// Trait for transferring and controlling the transfer of non-fungible sets of items. pub trait Transfer: Inspect { /// Transfer `item` into `destination` account. fn transfer(item: &Self::ItemId, destination: &AccountId) -> DispatchResult; + /// Disable the `item` of `collection` transfer. + /// + /// By default, this is not a supported operation. + fn disable_transfer(item: &Self::ItemId) -> DispatchResult; + /// Re-enable the `item` of `collection` transfer. + /// + /// By default, this is not a supported operation. + fn enable_transfer(item: &Self::ItemId) -> DispatchResult; } /// Convert a `nonfungibles` trait implementation into a `nonfungible` trait implementation by @@ -312,4 +320,10 @@ impl< fn transfer(item: &Self::ItemId, destination: &AccountId) -> DispatchResult { >::transfer(&A::get(), item, destination) } + fn disable_transfer(item: &Self::ItemId) -> DispatchResult { + >::disable_transfer(&A::get(), item) + } + fn enable_transfer(item: &Self::ItemId) -> DispatchResult { + >::enable_transfer(&A::get(), item) + } } diff --git a/frame/support/src/traits/tokens/nonfungibles_v2.rs b/frame/support/src/traits/tokens/nonfungibles_v2.rs index 9d32f29be..f4c3c22ea 100644 --- a/frame/support/src/traits/tokens/nonfungibles_v2.rs +++ b/frame/support/src/traits/tokens/nonfungibles_v2.rs @@ -338,4 +338,18 @@ pub trait Transfer: Inspect { item: &Self::ItemId, destination: &AccountId, ) -> DispatchResult; + + /// Disable the `item` of `collection` transfer. + /// + /// By default, this is not a supported operation. + fn disable_transfer(_collection: &Self::CollectionId, _item: &Self::ItemId) -> DispatchResult { + Err(TokenError::Unsupported.into()) + } + + /// Re-enable the `item` of `collection` transfer. + /// + /// By default, this is not a supported operation. + fn enable_transfer(_collection: &Self::CollectionId, _item: &Self::ItemId) -> DispatchResult { + Err(TokenError::Unsupported.into()) + } } From bc3ce835964347166414203d68dccc26b9e9044f Mon Sep 17 00:00:00 2001 From: Gavin Wood Date: Tue, 23 May 2023 09:47:08 +0100 Subject: [PATCH 531/558] Actually respect locks of zero (#14144) * Actually thaw when locking zero. * Fixes * Just remove the lock if semantically viable --------- Co-authored-by: parity-processbot <> --- frame/balances/src/impl_currency.rs | 7 ++++--- frame/offences/benchmarking/src/lib.rs | 8 ++++---- 2 files changed, 8 insertions(+), 7 deletions(-) diff --git a/frame/balances/src/impl_currency.rs b/frame/balances/src/impl_currency.rs index 9f764a37b..baa153c11 100644 --- a/frame/balances/src/impl_currency.rs +++ b/frame/balances/src/impl_currency.rs @@ -846,17 +846,18 @@ where type MaxLocks = T::MaxLocks; - // Set a lock on the balance of `who`. - // Is a no-op if lock amount is zero or `reasons` `is_none()`. + // Set or alter a lock on the balance of `who`. fn set_lock( id: LockIdentifier, who: &T::AccountId, amount: T::Balance, reasons: WithdrawReasons, ) { - if amount.is_zero() || reasons.is_empty() { + if reasons.is_empty() || amount.is_zero() { + Self::remove_lock(id, who); return } + let mut new_lock = Some(BalanceLock { id, amount, reasons: reasons.into() }); let mut locks = Self::locks(who) .into_iter() diff --git a/frame/offences/benchmarking/src/lib.rs b/frame/offences/benchmarking/src/lib.rs index e7fc39657..92a87fb58 100644 --- a/frame/offences/benchmarking/src/lib.rs +++ b/frame/offences/benchmarking/src/lib.rs @@ -429,9 +429,9 @@ benchmarks! { + 1 // offence + 3 // reporter (reward + endowment) + 1 // offenders reported - + 2 // offenders slashed + + 3 // offenders slashed + 1 // offenders chilled - + 2 * n // nominators slashed + + 3 * n // nominators slashed ); } @@ -466,9 +466,9 @@ benchmarks! { + 1 // offence + 3 // reporter (reward + endowment) + 1 // offenders reported - + 2 // offenders slashed + + 3 // offenders slashed + 1 // offenders chilled - + 2 * n // nominators slashed + + 3 * n // nominators slashed ); } From aacba0fda8f86cf96bbe8c271463923943586748 Mon Sep 17 00:00:00 2001 From: Koute Date: Tue, 23 May 2023 20:34:04 +0900 Subject: [PATCH 532/558] Remove the `Copy` bound on `AssetId` (#14158) * Remove the `Copy` bound on `AssetId` * Also relax the `Copy` bound in the assets pallet * Fix warnings on the newest nightly Rust * Remove some unnecessary `clone()`s * Try to satisfy clippy * Remove some more unnecessary `clone()`s * Add more `.clone()`s for newly merged code * Also add `clone()`s in the benchmarks --------- Co-authored-by: parity-processbot <> --- frame/asset-rate/src/benchmarking.rs | 8 +- frame/asset-rate/src/lib.rs | 10 +- frame/assets/src/benchmarking.rs | 2 +- frame/assets/src/extra_mutator.rs | 6 +- frame/assets/src/functions.rs | 120 +++++++++--------- frame/assets/src/impl_stored_map.rs | 4 +- frame/assets/src/lib.rs | 64 +++++----- .../nft-fractionalization/src/benchmarking.rs | 6 +- frame/nft-fractionalization/src/lib.rs | 18 ++- .../src/traits/tokens/fungibles/hold.rs | 61 +++++---- .../src/traits/tokens/fungibles/imbalance.rs | 10 +- .../src/traits/tokens/fungibles/regular.rs | 97 ++++++++------ frame/support/src/traits/tokens/misc.rs | 4 +- 13 files changed, 223 insertions(+), 187 deletions(-) diff --git a/frame/asset-rate/src/benchmarking.rs b/frame/asset-rate/src/benchmarking.rs index dde0d764a..1209f8db6 100644 --- a/frame/asset-rate/src/benchmarking.rs +++ b/frame/asset-rate/src/benchmarking.rs @@ -38,7 +38,7 @@ mod benchmarks { fn create() -> Result<(), BenchmarkError> { let asset_id: T::AssetId = ASSET_ID.into(); #[extrinsic_call] - _(RawOrigin::Root, asset_id, default_conversion_rate()); + _(RawOrigin::Root, asset_id.clone(), default_conversion_rate()); assert_eq!( pallet_asset_rate::ConversionRateToNative::::get(asset_id), @@ -52,12 +52,12 @@ mod benchmarks { let asset_id: T::AssetId = ASSET_ID.into(); assert_ok!(AssetRate::::create( RawOrigin::Root.into(), - asset_id, + asset_id.clone(), default_conversion_rate() )); #[extrinsic_call] - _(RawOrigin::Root, asset_id, FixedU128::from_u32(2)); + _(RawOrigin::Root, asset_id.clone(), FixedU128::from_u32(2)); assert_eq!( pallet_asset_rate::ConversionRateToNative::::get(asset_id), @@ -76,7 +76,7 @@ mod benchmarks { )); #[extrinsic_call] - _(RawOrigin::Root, asset_id); + _(RawOrigin::Root, asset_id.clone()); assert!(pallet_asset_rate::ConversionRateToNative::::get(asset_id).is_none()); Ok(()) diff --git a/frame/asset-rate/src/lib.rs b/frame/asset-rate/src/lib.rs index 8c6597a38..ecc793184 100644 --- a/frame/asset-rate/src/lib.rs +++ b/frame/asset-rate/src/lib.rs @@ -161,10 +161,10 @@ pub mod pallet { T::CreateOrigin::ensure_origin(origin)?; ensure!( - !ConversionRateToNative::::contains_key(asset_id), + !ConversionRateToNative::::contains_key(asset_id.clone()), Error::::AlreadyExists ); - ConversionRateToNative::::set(asset_id, Some(rate)); + ConversionRateToNative::::set(asset_id.clone(), Some(rate)); Self::deposit_event(Event::AssetRateCreated { asset_id, rate }); Ok(()) @@ -184,7 +184,7 @@ pub mod pallet { T::UpdateOrigin::ensure_origin(origin)?; let mut old = FixedU128::zero(); - ConversionRateToNative::::mutate(asset_id, |maybe_rate| { + ConversionRateToNative::::mutate(asset_id.clone(), |maybe_rate| { if let Some(r) = maybe_rate { old = *r; *r = rate; @@ -209,10 +209,10 @@ pub mod pallet { T::RemoveOrigin::ensure_origin(origin)?; ensure!( - ConversionRateToNative::::contains_key(asset_id), + ConversionRateToNative::::contains_key(asset_id.clone()), Error::::UnknownAssetId ); - ConversionRateToNative::::remove(asset_id); + ConversionRateToNative::::remove(asset_id.clone()); Self::deposit_event(Event::AssetRateRemoved { asset_id }); Ok(()) diff --git a/frame/assets/src/benchmarking.rs b/frame/assets/src/benchmarking.rs index a24836507..747e072dd 100644 --- a/frame/assets/src/benchmarking.rs +++ b/frame/assets/src/benchmarking.rs @@ -144,7 +144,7 @@ benchmarks_instance_pallet! { let caller = T::CreateOrigin::ensure_origin(origin, &asset_id.into()).unwrap(); let caller_lookup = T::Lookup::unlookup(caller.clone()); T::Currency::make_free_balance_be(&caller, DepositBalanceOf::::max_value()); - }: _(SystemOrigin::Signed(caller.clone()), asset_id, caller_lookup, 1u32.into()) + }: _(SystemOrigin::Signed(caller.clone()), asset_id.clone(), caller_lookup, 1u32.into()) verify { assert_last_event::(Event::Created { asset_id: asset_id.into(), creator: caller.clone(), owner: caller }.into()); } diff --git a/frame/assets/src/extra_mutator.rs b/frame/assets/src/extra_mutator.rs index 96fb765cb..2a44df5f0 100644 --- a/frame/assets/src/extra_mutator.rs +++ b/frame/assets/src/extra_mutator.rs @@ -62,7 +62,7 @@ impl, I: 'static> ExtraMutator { id: T::AssetId, who: impl sp_std::borrow::Borrow, ) -> Option> { - if let Some(a) = Account::::get(id, who.borrow()) { + if let Some(a) = Account::::get(&id, who.borrow()) { Some(ExtraMutator:: { id, who: who.borrow().clone(), @@ -77,7 +77,7 @@ impl, I: 'static> ExtraMutator { /// Commit any changes to storage. pub fn commit(&mut self) -> Result<(), ()> { if let Some(extra) = self.pending.take() { - Account::::try_mutate(self.id, self.who.borrow(), |maybe_account| { + Account::::try_mutate(&self.id, &self.who, |maybe_account| { maybe_account.as_mut().ok_or(()).map(|account| account.extra = extra) }) } else { @@ -88,7 +88,7 @@ impl, I: 'static> ExtraMutator { /// Revert any changes, even those already committed by `self` and drop self. pub fn revert(mut self) -> Result<(), ()> { self.pending = None; - Account::::try_mutate(self.id, self.who.borrow(), |maybe_account| { + Account::::try_mutate(&self.id, &self.who, |maybe_account| { maybe_account .as_mut() .ok_or(()) diff --git a/frame/assets/src/functions.rs b/frame/assets/src/functions.rs index 3f32f7b94..7372dcd41 100644 --- a/frame/assets/src/functions.rs +++ b/frame/assets/src/functions.rs @@ -128,7 +128,7 @@ impl, I: 'static> Pallet { amount: T::Balance, increase_supply: bool, ) -> DepositConsequence { - let details = match Asset::::get(id) { + let details = match Asset::::get(&id) { Some(details) => details, None => return DepositConsequence::UnknownAsset, }; @@ -165,7 +165,7 @@ impl, I: 'static> Pallet { keep_alive: bool, ) -> WithdrawConsequence { use WithdrawConsequence::*; - let details = match Asset::::get(id) { + let details = match Asset::::get(&id) { Some(details) => details, None => return UnknownAsset, }; @@ -178,7 +178,7 @@ impl, I: 'static> Pallet { if amount.is_zero() { return Success } - let account = match Account::::get(id, who) { + let account = match Account::::get(&id, who) { Some(a) => a, None => return BalanceLow, }; @@ -186,7 +186,7 @@ impl, I: 'static> Pallet { return Frozen } if let Some(rest) = account.balance.checked_sub(&amount) { - if let Some(frozen) = T::Freezer::frozen_balance(id, who) { + if let Some(frozen) = T::Freezer::frozen_balance(id.clone(), who) { match frozen.checked_add(&details.min_balance) { Some(required) if rest < required => return Frozen, None => return Overflow, @@ -219,10 +219,10 @@ impl, I: 'static> Pallet { who: &T::AccountId, keep_alive: bool, ) -> Result { - let details = Asset::::get(id).ok_or(Error::::Unknown)?; + let details = Asset::::get(&id).ok_or(Error::::Unknown)?; ensure!(details.status == AssetStatus::Live, Error::::AssetNotLive); - let account = Account::::get(id, who).ok_or(Error::::NoAccount)?; + let account = Account::::get(&id, who).ok_or(Error::::NoAccount)?; ensure!(!account.status.is_frozen(), Error::::Frozen); let amount = if let Some(frozen) = T::Freezer::frozen_balance(id, who) { @@ -265,7 +265,7 @@ impl, I: 'static> Pallet { amount: T::Balance, f: DebitFlags, ) -> Result { - let actual = Self::reducible_balance(id, target, f.keep_alive)?.min(amount); + let actual = Self::reducible_balance(id.clone(), target, f.keep_alive)?.min(amount); ensure!(f.best_effort || actual >= amount, Error::::BalanceLow); let conseq = Self::can_decrease(id, target, actual, f.keep_alive); @@ -320,7 +320,7 @@ impl, I: 'static> Pallet { depositor: T::AccountId, check_depositor: bool, ) -> DispatchResult { - ensure!(!Account::::contains_key(id, &who), Error::::AlreadyExists); + ensure!(!Account::::contains_key(&id, &who), Error::::AlreadyExists); let deposit = T::AssetAccountDeposit::get(); let mut details = Asset::::get(&id).ok_or(Error::::Unknown)?; ensure!(details.status == AssetStatus::Live, Error::::AssetNotLive); @@ -332,7 +332,7 @@ impl, I: 'static> Pallet { T::Currency::reserve(&depositor, deposit)?; Asset::::insert(&id, details); Account::::insert( - id, + &id, &who, AssetAccountOf:: { balance: Zero::zero(), @@ -350,7 +350,7 @@ impl, I: 'static> Pallet { pub(super) fn do_refund(id: T::AssetId, who: T::AccountId, allow_burn: bool) -> DispatchResult { use AssetStatus::*; use ExistenceReason::*; - let mut account = Account::::get(id, &who).ok_or(Error::::NoDeposit)?; + let mut account = Account::::get(&id, &who).ok_or(Error::::NoDeposit)?; ensure!(matches!(account.reason, Consumer | DepositHeld(..)), Error::::NoDeposit); let mut details = Asset::::get(&id).ok_or(Error::::Unknown)?; ensure!(matches!(details.status, Live | Frozen), Error::::IncorrectStatus); @@ -361,7 +361,7 @@ impl, I: 'static> Pallet { } if let Remove = Self::dead_account(&who, &mut details, &account.reason, false) { - Account::::remove(id, &who); + Account::::remove(&id, &who); } else { debug_assert!(false, "refund did not result in dead account?!"); // deposit may have been refunded, need to update `Account` @@ -380,7 +380,7 @@ impl, I: 'static> Pallet { who: &T::AccountId, caller: &T::AccountId, ) -> DispatchResult { - let mut account = Account::::get(id, &who).ok_or(Error::::NoDeposit)?; + let mut account = Account::::get(&id, &who).ok_or(Error::::NoDeposit)?; let (depositor, deposit) = account.reason.take_deposit_from().ok_or(Error::::NoDeposit)?; let mut details = Asset::::get(&id).ok_or(Error::::Unknown)?; @@ -392,11 +392,11 @@ impl, I: 'static> Pallet { T::Currency::unreserve(&depositor, deposit); if let Remove = Self::dead_account(&who, &mut details, &account.reason, false) { - Account::::remove(id, &who); + Account::::remove(&id, &who); } else { debug_assert!(false, "refund did not result in dead account?!"); // deposit may have been refunded, need to update `Account` - Account::::insert(id, &who, account); + Account::::insert(&id, &who, account); return Ok(()) } Asset::::insert(&id, details); @@ -416,7 +416,7 @@ impl, I: 'static> Pallet { amount: T::Balance, maybe_check_issuer: Option, ) -> DispatchResult { - Self::increase_balance(id, beneficiary, amount, |details| -> DispatchResult { + Self::increase_balance(id.clone(), beneficiary, amount, |details| -> DispatchResult { if let Some(check_issuer) = maybe_check_issuer { ensure!(check_issuer == details.issuer, Error::::NoPermission); } @@ -450,19 +450,20 @@ impl, I: 'static> Pallet { return Ok(()) } - Self::can_increase(id, beneficiary, amount, true).into_result()?; - Asset::::try_mutate(id, |maybe_details| -> DispatchResult { + Self::can_increase(id.clone(), beneficiary, amount, true).into_result()?; + Asset::::try_mutate(&id, |maybe_details| -> DispatchResult { let details = maybe_details.as_mut().ok_or(Error::::Unknown)?; ensure!(details.status == AssetStatus::Live, Error::::AssetNotLive); check(details)?; - Account::::try_mutate(id, beneficiary, |maybe_account| -> DispatchResult { + Account::::try_mutate(&id, beneficiary, |maybe_account| -> DispatchResult { match maybe_account { Some(ref mut account) => { account.balance.saturating_accrue(amount); }, maybe_account @ None => { - // Note this should never fail as it's already checked by `can_increase`. + // Note this should never fail as it's already checked by + // `can_increase`. ensure!(amount >= details.min_balance, TokenError::BelowMinimum); *maybe_account = Some(AssetAccountOf:: { balance: amount, @@ -493,13 +494,13 @@ impl, I: 'static> Pallet { maybe_check_admin: Option, f: DebitFlags, ) -> Result { - let d = Asset::::get(id).ok_or(Error::::Unknown)?; + let d = Asset::::get(&id).ok_or(Error::::Unknown)?; ensure!( d.status == AssetStatus::Live || d.status == AssetStatus::Frozen, Error::::AssetNotLive ); - let actual = Self::decrease_balance(id, target, amount, f, |actual, details| { + let actual = Self::decrease_balance(id.clone(), target, amount, f, |actual, details| { // Check admin rights. if let Some(check_admin) = maybe_check_admin { ensure!(check_admin == details.admin, Error::::NoPermission); @@ -536,17 +537,17 @@ impl, I: 'static> Pallet { return Ok(amount) } - let details = Asset::::get(id).ok_or(Error::::Unknown)?; + let details = Asset::::get(&id).ok_or(Error::::Unknown)?; ensure!(details.status == AssetStatus::Live, Error::::AssetNotLive); - let actual = Self::prep_debit(id, target, amount, f)?; + let actual = Self::prep_debit(id.clone(), target, amount, f)?; let mut target_died: Option = None; - Asset::::try_mutate(id, |maybe_details| -> DispatchResult { + Asset::::try_mutate(&id, |maybe_details| -> DispatchResult { let details = maybe_details.as_mut().ok_or(Error::::Unknown)?; check(actual, details)?; - Account::::try_mutate(id, target, |maybe_account| -> DispatchResult { + Account::::try_mutate(&id, target, |maybe_account| -> DispatchResult { let mut account = maybe_account.take().ok_or(Error::::NoAccount)?; debug_assert!(account.balance >= actual, "checked in prep; qed"); @@ -590,7 +591,7 @@ impl, I: 'static> Pallet { f: TransferFlags, ) -> Result { let (balance, died) = - Self::transfer_and_die(id, source, dest, amount, maybe_need_admin, f)?; + Self::transfer_and_die(id.clone(), source, dest, amount, maybe_need_admin, f)?; if let Some(Remove) = died { T::Freezer::died(id, source); } @@ -611,18 +612,18 @@ impl, I: 'static> Pallet { if amount.is_zero() { return Ok((amount, None)) } - let details = Asset::::get(id).ok_or(Error::::Unknown)?; + let details = Asset::::get(&id).ok_or(Error::::Unknown)?; ensure!(details.status == AssetStatus::Live, Error::::AssetNotLive); // Figure out the debit and credit, together with side-effects. - let debit = Self::prep_debit(id, source, amount, f.into())?; - let (credit, maybe_burn) = Self::prep_credit(id, dest, amount, debit, f.burn_dust)?; + let debit = Self::prep_debit(id.clone(), source, amount, f.into())?; + let (credit, maybe_burn) = Self::prep_credit(id.clone(), dest, amount, debit, f.burn_dust)?; let mut source_account = - Account::::get(id, &source).ok_or(Error::::NoAccount)?; + Account::::get(&id, &source).ok_or(Error::::NoAccount)?; let mut source_died: Option = None; - Asset::::try_mutate(id, |maybe_details| -> DispatchResult { + Asset::::try_mutate(&id, |maybe_details| -> DispatchResult { let details = maybe_details.as_mut().ok_or(Error::::Unknown)?; // Check admin rights. @@ -647,7 +648,7 @@ impl, I: 'static> Pallet { debug_assert!(source_account.balance >= debit, "checked in prep; qed"); source_account.balance = source_account.balance.saturating_sub(debit); - Account::::try_mutate(id, &dest, |maybe_account| -> DispatchResult { + Account::::try_mutate(&id, &dest, |maybe_account| -> DispatchResult { match maybe_account { Some(ref mut account) => { // Calculate new balance; this will not saturate since it's already checked @@ -676,11 +677,11 @@ impl, I: 'static> Pallet { source_died = Some(Self::dead_account(source, details, &source_account.reason, false)); if let Some(Remove) = source_died { - Account::::remove(id, &source); + Account::::remove(&id, &source); return Ok(()) } } - Account::::insert(id, &source, &source_account); + Account::::insert(&id, &source, &source_account); Ok(()) })?; @@ -707,11 +708,11 @@ impl, I: 'static> Pallet { is_sufficient: bool, min_balance: T::Balance, ) -> DispatchResult { - ensure!(!Asset::::contains_key(id), Error::::InUse); + ensure!(!Asset::::contains_key(&id), Error::::InUse); ensure!(!min_balance.is_zero(), Error::::MinBalanceZero); Asset::::insert( - id, + &id, AssetDetails { owner: owner.clone(), issuer: owner.clone(), @@ -738,8 +739,8 @@ impl, I: 'static> Pallet { id: T::AssetId, maybe_check_owner: Option, ) -> DispatchResult { - Asset::::try_mutate_exists(id, |maybe_details| -> Result<(), DispatchError> { - let mut details = maybe_details.as_mut().ok_or(Error::::Unknown)?; + Asset::::try_mutate_exists(id.clone(), |maybe_details| -> Result<(), DispatchError> { + let details = maybe_details.as_mut().ok_or(Error::::Unknown)?; if let Some(check_owner) = maybe_check_owner { ensure!(details.owner == check_owner, Error::::NoPermission); } @@ -761,12 +762,12 @@ impl, I: 'static> Pallet { let mut dead_accounts: Vec = vec![]; let mut remaining_accounts = 0; let _ = - Asset::::try_mutate_exists(id, |maybe_details| -> Result<(), DispatchError> { + Asset::::try_mutate_exists(&id, |maybe_details| -> Result<(), DispatchError> { let mut details = maybe_details.as_mut().ok_or(Error::::Unknown)?; // Should only destroy accounts while the asset is in a destroying state ensure!(details.status == AssetStatus::Destroying, Error::::IncorrectStatus); - for (who, v) in Account::::drain_prefix(id) { + for (who, v) in Account::::drain_prefix(&id) { let _ = Self::dead_account(&who, &mut details, &v.reason, true); dead_accounts.push(who); if dead_accounts.len() >= (max_items as usize) { @@ -778,7 +779,7 @@ impl, I: 'static> Pallet { })?; for who in &dead_accounts { - T::Freezer::died(id, &who); + T::Freezer::died(id.clone(), &who); } Self::deposit_event(Event::AccountsDestroyed { @@ -798,14 +799,15 @@ impl, I: 'static> Pallet { max_items: u32, ) -> Result { let mut removed_approvals = 0; - let _ = - Asset::::try_mutate_exists(id, |maybe_details| -> Result<(), DispatchError> { - let mut details = maybe_details.as_mut().ok_or(Error::::Unknown)?; + let _ = Asset::::try_mutate_exists( + id.clone(), + |maybe_details| -> Result<(), DispatchError> { + let details = maybe_details.as_mut().ok_or(Error::::Unknown)?; // Should only destroy accounts while the asset is in a destroying state. ensure!(details.status == AssetStatus::Destroying, Error::::IncorrectStatus); - for ((owner, _), approval) in Approvals::::drain_prefix((id,)) { + for ((owner, _), approval) in Approvals::::drain_prefix((id.clone(),)) { T::Currency::unreserve(&owner, approval.deposit); removed_approvals = removed_approvals.saturating_add(1); details.approvals = details.approvals.saturating_sub(1); @@ -819,7 +821,8 @@ impl, I: 'static> Pallet { approvals_remaining: details.approvals as u32, }); Ok(()) - })?; + }, + )?; Ok(removed_approvals) } @@ -827,7 +830,7 @@ impl, I: 'static> Pallet { /// /// On success, the `Event::Destroyed` event is emitted. pub(super) fn do_finish_destroy(id: T::AssetId) -> DispatchResult { - Asset::::try_mutate_exists(id, |maybe_details| -> Result<(), DispatchError> { + Asset::::try_mutate_exists(id.clone(), |maybe_details| -> Result<(), DispatchError> { let details = maybe_details.take().ok_or(Error::::Unknown)?; ensure!(details.status == AssetStatus::Destroying, Error::::IncorrectStatus); ensure!(details.accounts == 0, Error::::InUse); @@ -855,10 +858,10 @@ impl, I: 'static> Pallet { delegate: &T::AccountId, amount: T::Balance, ) -> DispatchResult { - let mut d = Asset::::get(id).ok_or(Error::::Unknown)?; + let mut d = Asset::::get(&id).ok_or(Error::::Unknown)?; ensure!(d.status == AssetStatus::Live, Error::::AssetNotLive); Approvals::::try_mutate( - (id, &owner, &delegate), + (id.clone(), &owner, &delegate), |maybe_approved| -> DispatchResult { let mut approved = match maybe_approved.take() { // an approval already exists and is being updated @@ -879,7 +882,7 @@ impl, I: 'static> Pallet { Ok(()) }, )?; - Asset::::insert(id, d); + Asset::::insert(&id, d); Self::deposit_event(Event::ApprovedTransfer { asset_id: id, source: owner.clone(), @@ -906,22 +909,23 @@ impl, I: 'static> Pallet { ) -> DispatchResult { let mut owner_died: Option = None; - let d = Asset::::get(id).ok_or(Error::::Unknown)?; + let d = Asset::::get(&id).ok_or(Error::::Unknown)?; ensure!(d.status == AssetStatus::Live, Error::::AssetNotLive); Approvals::::try_mutate_exists( - (id, &owner, delegate), + (id.clone(), &owner, delegate), |maybe_approved| -> DispatchResult { let mut approved = maybe_approved.take().ok_or(Error::::Unapproved)?; let remaining = approved.amount.checked_sub(&amount).ok_or(Error::::Unapproved)?; let f = TransferFlags { keep_alive: false, best_effort: false, burn_dust: false }; - owner_died = Self::transfer_and_die(id, owner, destination, amount, None, f)?.1; + owner_died = + Self::transfer_and_die(id.clone(), owner, destination, amount, None, f)?.1; if remaining.is_zero() { T::Currency::unreserve(owner, approved.deposit); - Asset::::mutate(id, |maybe_details| { + Asset::::mutate(id.clone(), |maybe_details| { if let Some(details) = maybe_details { details.approvals.saturating_dec(); } @@ -954,11 +958,11 @@ impl, I: 'static> Pallet { let bounded_symbol: BoundedVec = symbol.clone().try_into().map_err(|_| Error::::BadMetadata)?; - let d = Asset::::get(id).ok_or(Error::::Unknown)?; + let d = Asset::::get(&id).ok_or(Error::::Unknown)?; ensure!(d.status == AssetStatus::Live, Error::::AssetNotLive); ensure!(from == &d.owner, Error::::NoPermission); - Metadata::::try_mutate_exists(id, |metadata| { + Metadata::::try_mutate_exists(id.clone(), |metadata| { ensure!(metadata.as_ref().map_or(true, |m| !m.is_frozen), Error::::NoPermission); let old_deposit = metadata.take().map_or(Zero::zero(), |m| m.deposit); @@ -999,7 +1003,9 @@ impl, I: 'static> Pallet { /// Returns all the non-zero balances for all assets of the given `account`. pub fn account_balances(account: T::AccountId) -> Vec<(T::AssetId, T::Balance)> { Asset::::iter_keys() - .filter_map(|id| Self::maybe_balance(id, account.clone()).map(|balance| (id, balance))) + .filter_map(|id| { + Self::maybe_balance(id.clone(), account.clone()).map(|balance| (id, balance)) + }) .collect::>() } } diff --git a/frame/assets/src/impl_stored_map.rs b/frame/assets/src/impl_stored_map.rs index 5ead70846..a7a5a0859 100644 --- a/frame/assets/src/impl_stored_map.rs +++ b/frame/assets/src/impl_stored_map.rs @@ -21,7 +21,7 @@ use super::*; impl, I: 'static> StoredMap<(T::AssetId, T::AccountId), T::Extra> for Pallet { fn get(id_who: &(T::AssetId, T::AccountId)) -> T::Extra { - let &(id, ref who) = id_who; + let (id, who) = id_who; Account::::get(id, who).map(|a| a.extra).unwrap_or_default() } @@ -29,7 +29,7 @@ impl, I: 'static> StoredMap<(T::AssetId, T::AccountId), T::Extra> f id_who: &(T::AssetId, T::AccountId), f: impl FnOnce(&mut Option) -> Result, ) -> Result { - let &(id, ref who) = id_who; + let (id, who) = id_who; let mut maybe_extra = Account::::get(id, who).map(|a| a.extra); let r = f(&mut maybe_extra)?; // They want to write some value or delete it. diff --git a/frame/assets/src/lib.rs b/frame/assets/src/lib.rs index e9259f4b6..4b91f5184 100644 --- a/frame/assets/src/lib.rs +++ b/frame/assets/src/lib.rs @@ -162,7 +162,7 @@ use sp_runtime::{ traits::{AtLeast32BitUnsigned, CheckedAdd, CheckedSub, Saturating, StaticLookup, Zero}, ArithmeticError, TokenError, }; -use sp_std::{borrow::Borrow, prelude::*}; +use sp_std::prelude::*; use frame_support::{ dispatch::{DispatchError, DispatchResult}, @@ -250,7 +250,7 @@ pub mod pallet { type RemoveItemsLimit: Get; /// Identifier for the class of asset. - type AssetId: Member + Parameter + Copy + MaybeSerializeDeserialize + MaxEncodedLen; + type AssetId: Member + Parameter + Clone + MaybeSerializeDeserialize + MaxEncodedLen; /// Wrapper around `Self::AssetId` to use in dispatchable call signatures. Allows the use /// of compact encoding in instances of the pallet, which will prevent breaking changes @@ -424,7 +424,7 @@ pub mod pallet { for (id, account_id, amount) in &self.accounts { let result = >::increase_balance( - *id, + id.clone(), account_id, *amount, |details| -> DispatchResult { @@ -605,14 +605,14 @@ pub mod pallet { let owner = T::CreateOrigin::ensure_origin(origin, &id)?; let admin = T::Lookup::lookup(admin)?; - ensure!(!Asset::::contains_key(id), Error::::InUse); + ensure!(!Asset::::contains_key(&id), Error::::InUse); ensure!(!min_balance.is_zero(), Error::::MinBalanceZero); let deposit = T::AssetDeposit::get(); T::Currency::reserve(&owner, deposit)?; Asset::::insert( - id, + id.clone(), AssetDetails { owner: owner.clone(), issuer: admin.clone(), @@ -937,7 +937,7 @@ pub mod pallet { let origin = ensure_signed(origin)?; let id: T::AssetId = id.into(); - let d = Asset::::get(id).ok_or(Error::::Unknown)?; + let d = Asset::::get(&id).ok_or(Error::::Unknown)?; ensure!( d.status == AssetStatus::Live || d.status == AssetStatus::Frozen, Error::::AssetNotLive @@ -945,7 +945,7 @@ pub mod pallet { ensure!(origin == d.freezer, Error::::NoPermission); let who = T::Lookup::lookup(who)?; - Account::::try_mutate(id, &who, |maybe_account| -> DispatchResult { + Account::::try_mutate(&id, &who, |maybe_account| -> DispatchResult { maybe_account.as_mut().ok_or(Error::::NoAccount)?.status = AccountStatus::Frozen; Ok(()) @@ -974,7 +974,7 @@ pub mod pallet { let origin = ensure_signed(origin)?; let id: T::AssetId = id.into(); - let details = Asset::::get(id).ok_or(Error::::Unknown)?; + let details = Asset::::get(&id).ok_or(Error::::Unknown)?; ensure!( details.status == AssetStatus::Live || details.status == AssetStatus::Frozen, Error::::AssetNotLive @@ -982,7 +982,7 @@ pub mod pallet { ensure!(origin == details.admin, Error::::NoPermission); let who = T::Lookup::lookup(who)?; - Account::::try_mutate(id, &who, |maybe_account| -> DispatchResult { + Account::::try_mutate(&id, &who, |maybe_account| -> DispatchResult { maybe_account.as_mut().ok_or(Error::::NoAccount)?.status = AccountStatus::Liquid; Ok(()) @@ -1006,7 +1006,7 @@ pub mod pallet { let origin = ensure_signed(origin)?; let id: T::AssetId = id.into(); - Asset::::try_mutate(id, |maybe_details| { + Asset::::try_mutate(id.clone(), |maybe_details| { let d = maybe_details.as_mut().ok_or(Error::::Unknown)?; ensure!(d.status == AssetStatus::Live, Error::::AssetNotLive); ensure!(origin == d.freezer, Error::::NoPermission); @@ -1032,7 +1032,7 @@ pub mod pallet { let origin = ensure_signed(origin)?; let id: T::AssetId = id.into(); - Asset::::try_mutate(id, |maybe_details| { + Asset::::try_mutate(id.clone(), |maybe_details| { let d = maybe_details.as_mut().ok_or(Error::::Unknown)?; ensure!(origin == d.admin, Error::::NoPermission); ensure!(d.status == AssetStatus::Frozen, Error::::NotFrozen); @@ -1064,7 +1064,7 @@ pub mod pallet { let owner = T::Lookup::lookup(owner)?; let id: T::AssetId = id.into(); - Asset::::try_mutate(id, |maybe_details| { + Asset::::try_mutate(id.clone(), |maybe_details| { let details = maybe_details.as_mut().ok_or(Error::::Unknown)?; ensure!(details.status == AssetStatus::Live, Error::::LiveAsset); ensure!(origin == details.owner, Error::::NoPermission); @@ -1072,7 +1072,7 @@ pub mod pallet { return Ok(()) } - let metadata_deposit = Metadata::::get(id).deposit; + let metadata_deposit = Metadata::::get(&id).deposit; let deposit = details.deposit + metadata_deposit; // Move the deposit to the new owner. @@ -1111,7 +1111,7 @@ pub mod pallet { let freezer = T::Lookup::lookup(freezer)?; let id: T::AssetId = id.into(); - Asset::::try_mutate(id, |maybe_details| { + Asset::::try_mutate(id.clone(), |maybe_details| { let details = maybe_details.as_mut().ok_or(Error::::Unknown)?; ensure!(details.status == AssetStatus::Live, Error::::AssetNotLive); ensure!(origin == details.owner, Error::::NoPermission); @@ -1171,11 +1171,11 @@ pub mod pallet { let origin = ensure_signed(origin)?; let id: T::AssetId = id.into(); - let d = Asset::::get(id).ok_or(Error::::Unknown)?; + let d = Asset::::get(&id).ok_or(Error::::Unknown)?; ensure!(d.status == AssetStatus::Live, Error::::AssetNotLive); ensure!(origin == d.owner, Error::::NoPermission); - Metadata::::try_mutate_exists(id, |metadata| { + Metadata::::try_mutate_exists(id.clone(), |metadata| { let deposit = metadata.take().ok_or(Error::::Unknown)?.deposit; T::Currency::unreserve(&d.owner, deposit); Self::deposit_event(Event::MetadataCleared { asset_id: id }); @@ -1216,8 +1216,8 @@ pub mod pallet { let bounded_symbol: BoundedVec = symbol.clone().try_into().map_err(|_| Error::::BadMetadata)?; - ensure!(Asset::::contains_key(id), Error::::Unknown); - Metadata::::try_mutate_exists(id, |metadata| { + ensure!(Asset::::contains_key(&id), Error::::Unknown); + Metadata::::try_mutate_exists(id.clone(), |metadata| { let deposit = metadata.take().map_or(Zero::zero(), |m| m.deposit); *metadata = Some(AssetMetadata { deposit, @@ -1257,8 +1257,8 @@ pub mod pallet { T::ForceOrigin::ensure_origin(origin)?; let id: T::AssetId = id.into(); - let d = Asset::::get(id).ok_or(Error::::Unknown)?; - Metadata::::try_mutate_exists(id, |metadata| { + let d = Asset::::get(&id).ok_or(Error::::Unknown)?; + Metadata::::try_mutate_exists(id.clone(), |metadata| { let deposit = metadata.take().ok_or(Error::::Unknown)?.deposit; T::Currency::unreserve(&d.owner, deposit); Self::deposit_event(Event::MetadataCleared { asset_id: id }); @@ -1303,7 +1303,7 @@ pub mod pallet { T::ForceOrigin::ensure_origin(origin)?; let id: T::AssetId = id.into(); - Asset::::try_mutate(id, |maybe_asset| { + Asset::::try_mutate(id.clone(), |maybe_asset| { let mut asset = maybe_asset.take().ok_or(Error::::Unknown)?; ensure!(asset.status != AssetStatus::Destroying, Error::::AssetNotLive); asset.owner = T::Lookup::lookup(owner)?; @@ -1379,15 +1379,15 @@ pub mod pallet { let owner = ensure_signed(origin)?; let delegate = T::Lookup::lookup(delegate)?; let id: T::AssetId = id.into(); - let mut d = Asset::::get(id).ok_or(Error::::Unknown)?; + let mut d = Asset::::get(&id).ok_or(Error::::Unknown)?; ensure!(d.status == AssetStatus::Live, Error::::AssetNotLive); - let approval = - Approvals::::take((id, &owner, &delegate)).ok_or(Error::::Unknown)?; + let approval = Approvals::::take((id.clone(), &owner, &delegate)) + .ok_or(Error::::Unknown)?; T::Currency::unreserve(&owner, approval.deposit); d.approvals.saturating_dec(); - Asset::::insert(id, d); + Asset::::insert(id.clone(), d); Self::deposit_event(Event::ApprovalCancelled { asset_id: id, owner, delegate }); Ok(()) @@ -1414,7 +1414,7 @@ pub mod pallet { delegate: AccountIdLookupOf, ) -> DispatchResult { let id: T::AssetId = id.into(); - let mut d = Asset::::get(id).ok_or(Error::::Unknown)?; + let mut d = Asset::::get(&id).ok_or(Error::::Unknown)?; ensure!(d.status == AssetStatus::Live, Error::::AssetNotLive); T::ForceOrigin::try_origin(origin) .map(|_| ()) @@ -1427,11 +1427,11 @@ pub mod pallet { let owner = T::Lookup::lookup(owner)?; let delegate = T::Lookup::lookup(delegate)?; - let approval = - Approvals::::take((id, &owner, &delegate)).ok_or(Error::::Unknown)?; + let approval = Approvals::::take((id.clone(), &owner, &delegate)) + .ok_or(Error::::Unknown)?; T::Currency::unreserve(&owner, approval.deposit); d.approvals.saturating_dec(); - Asset::::insert(id, d); + Asset::::insert(id.clone(), d); Self::deposit_event(Event::ApprovalCancelled { asset_id: id, owner, delegate }); Ok(()) @@ -1529,7 +1529,7 @@ pub mod pallet { let origin = ensure_signed(origin)?; let id: T::AssetId = id.into(); - let mut details = Asset::::get(id).ok_or(Error::::Unknown)?; + let mut details = Asset::::get(&id).ok_or(Error::::Unknown)?; ensure!(origin == details.owner, Error::::NoPermission); let old_min_balance = details.min_balance; @@ -1619,7 +1619,7 @@ pub mod pallet { let origin = ensure_signed(origin)?; let id: T::AssetId = id.into(); - let d = Asset::::get(id).ok_or(Error::::Unknown)?; + let d = Asset::::get(&id).ok_or(Error::::Unknown)?; ensure!( d.status == AssetStatus::Live || d.status == AssetStatus::Frozen, Error::::AssetNotLive @@ -1627,7 +1627,7 @@ pub mod pallet { ensure!(origin == d.freezer, Error::::NoPermission); let who = T::Lookup::lookup(who)?; - Account::::try_mutate(id, &who, |maybe_account| -> DispatchResult { + Account::::try_mutate(&id, &who, |maybe_account| -> DispatchResult { maybe_account.as_mut().ok_or(Error::::NoAccount)?.status = AccountStatus::Blocked; Ok(()) diff --git a/frame/nft-fractionalization/src/benchmarking.rs b/frame/nft-fractionalization/src/benchmarking.rs index a04e8de12..1600ae78c 100644 --- a/frame/nft-fractionalization/src/benchmarking.rs +++ b/frame/nft-fractionalization/src/benchmarking.rs @@ -93,7 +93,7 @@ benchmarks! { let collection = T::BenchmarkHelper::collection(0); let nft = T::BenchmarkHelper::nft(0); let (caller, caller_lookup) = mint_nft::(nft); - }: _(SystemOrigin::Signed(caller.clone()), collection, nft, asset, caller_lookup, 1000u32.into()) + }: _(SystemOrigin::Signed(caller.clone()), collection, nft, asset.clone(), caller_lookup, 1000u32.into()) verify { assert_last_event::( Event::NftFractionalized { @@ -115,11 +115,11 @@ benchmarks! { SystemOrigin::Signed(caller.clone()).into(), collection, nft, - asset, + asset.clone(), caller_lookup.clone(), 1000u32.into(), )?; - }: _(SystemOrigin::Signed(caller.clone()), collection, nft, asset, caller_lookup) + }: _(SystemOrigin::Signed(caller.clone()), collection, nft, asset.clone(), caller_lookup) verify { assert_last_event::( Event::NftUnified { diff --git a/frame/nft-fractionalization/src/lib.rs b/frame/nft-fractionalization/src/lib.rs index c61719c5c..e1c8b8fea 100644 --- a/frame/nft-fractionalization/src/lib.rs +++ b/frame/nft-fractionalization/src/lib.rs @@ -241,13 +241,19 @@ pub mod pallet { let deposit = T::Deposit::get(); T::Currency::hold(&T::HoldReason::get(), &nft_owner, deposit)?; Self::do_lock_nft(nft_collection_id, nft_id)?; - Self::do_create_asset(asset_id, pallet_account.clone())?; - Self::do_mint_asset(asset_id, &beneficiary, fractions)?; - Self::do_set_metadata(asset_id, &who, &pallet_account, &nft_collection_id, &nft_id)?; + Self::do_create_asset(asset_id.clone(), pallet_account.clone())?; + Self::do_mint_asset(asset_id.clone(), &beneficiary, fractions)?; + Self::do_set_metadata( + asset_id.clone(), + &who, + &pallet_account, + &nft_collection_id, + &nft_id, + )?; NftToAsset::::insert( (nft_collection_id, nft_id), - Details { asset: asset_id, fractions, asset_creator: nft_owner, deposit }, + Details { asset: asset_id.clone(), fractions, asset_creator: nft_owner, deposit }, ); Self::deposit_event(Event::NftFractionalized { @@ -295,7 +301,7 @@ pub mod pallet { let deposit = details.deposit; let asset_creator = details.asset_creator; - Self::do_burn_asset(asset_id, &who, details.fractions)?; + Self::do_burn_asset(asset_id.clone(), &who, details.fractions)?; Self::do_unlock_nft(nft_collection_id, nft_id, &beneficiary)?; T::Currency::release(&T::HoldReason::get(), &asset_creator, deposit, BestEffort)?; @@ -356,7 +362,7 @@ pub mod pallet { account: &T::AccountId, amount: AssetBalanceOf, ) -> DispatchResult { - T::Assets::burn_from(asset_id, account, amount, Exact, Polite)?; + T::Assets::burn_from(asset_id.clone(), account, amount, Exact, Polite)?; T::Assets::start_destroy(asset_id, None) } diff --git a/frame/support/src/traits/tokens/fungibles/hold.rs b/frame/support/src/traits/tokens/fungibles/hold.rs index 68580ebff..8fc038c57 100644 --- a/frame/support/src/traits/tokens/fungibles/hold.rs +++ b/frame/support/src/traits/tokens/fungibles/hold.rs @@ -98,7 +98,7 @@ pub trait Inspect: super::Inspect { who: &AccountId, amount: Self::Balance, ) -> DispatchResult { - ensure!(Self::hold_available(asset, reason, who), TokenError::CannotCreateHold); + ensure!(Self::hold_available(asset.clone(), reason, who), TokenError::CannotCreateHold); ensure!( amount <= Self::reducible_balance(asset, who, Protect, Force), TokenError::FundsUnavailable @@ -173,7 +173,7 @@ pub trait Unbalanced: Inspect { mut amount: Self::Balance, precision: Precision, ) -> Result { - let old_balance = Self::balance_on_hold(asset, reason, who); + let old_balance = Self::balance_on_hold(asset.clone(), reason, who); if let BestEffort = precision { amount = amount.min(old_balance); } @@ -193,7 +193,7 @@ pub trait Unbalanced: Inspect { amount: Self::Balance, precision: Precision, ) -> Result { - let old_balance = Self::balance_on_hold(asset, reason, who); + let old_balance = Self::balance_on_hold(asset.clone(), reason, who); let new_balance = if let BestEffort = precision { old_balance.saturating_add(amount) } else { @@ -221,11 +221,13 @@ pub trait Balanced: super::Balanced + Unbalanced (Credit, Self::Balance) { - let decrease = Self::decrease_balance_on_hold(asset, reason, who, amount, BestEffort) - .unwrap_or(Default::default()); + let decrease = + Self::decrease_balance_on_hold(asset.clone(), reason, who, amount, BestEffort) + .unwrap_or(Default::default()); let credit = Imbalance::::new( - asset, decrease, + asset.clone(), + decrease, ); Self::done_slash(asset, reason, who, decrease); (credit, amount.saturating_sub(decrease)) @@ -255,10 +257,10 @@ pub trait Mutate: // NOTE: This doesn't change the total balance of the account so there's no need to // check liquidity. - Self::ensure_can_hold(asset, reason, who, amount)?; + Self::ensure_can_hold(asset.clone(), reason, who, amount)?; // Should be infallible now, but we proceed softly anyway. - Self::decrease_balance(asset, who, amount, Exact, Protect, Force)?; - Self::increase_balance_on_hold(asset, reason, who, amount, BestEffort)?; + Self::decrease_balance(asset.clone(), who, amount, Exact, Protect, Force)?; + Self::increase_balance_on_hold(asset.clone(), reason, who, amount, BestEffort)?; Self::done_hold(asset, reason, who, amount); Ok(()) } @@ -281,13 +283,16 @@ pub trait Mutate: // We want to make sure we can deposit the amount in advance. If we can't then something is // very wrong. - ensure!(Self::can_deposit(asset, who, amount, Extant) == Success, TokenError::CannotCreate); + ensure!( + Self::can_deposit(asset.clone(), who, amount, Extant) == Success, + TokenError::CannotCreate + ); // Get the amount we can actually take from the hold. This might be less than what we want // if we're only doing a best-effort. - let amount = Self::decrease_balance_on_hold(asset, reason, who, amount, precision)?; + let amount = Self::decrease_balance_on_hold(asset.clone(), reason, who, amount, precision)?; // Increase the main balance by what we took. We always do a best-effort here because we // already checked that we can deposit before. - let actual = Self::increase_balance(asset, who, amount, BestEffort)?; + let actual = Self::increase_balance(asset.clone(), who, amount, BestEffort)?; Self::done_release(asset, reason, who, actual); Ok(actual) } @@ -310,14 +315,17 @@ pub trait Mutate: force: Fortitude, ) -> Result { // We must check total-balance requirements if `!force`. - let liquid = Self::reducible_total_balance_on_hold(asset, who, force); + let liquid = Self::reducible_total_balance_on_hold(asset.clone(), who, force); if let BestEffort = precision { amount = amount.min(liquid); } else { ensure!(amount <= liquid, TokenError::Frozen); } - let amount = Self::decrease_balance_on_hold(asset, reason, who, amount, precision)?; - Self::set_total_issuance(asset, Self::total_issuance(asset).saturating_sub(amount)); + let amount = Self::decrease_balance_on_hold(asset.clone(), reason, who, amount, precision)?; + Self::set_total_issuance( + asset.clone(), + Self::total_issuance(asset.clone()).saturating_sub(amount), + ); Self::done_burn_held(asset, reason, who, amount); Ok(amount) } @@ -348,8 +356,8 @@ pub trait Mutate: force: Fortitude, ) -> Result { // We must check total-balance requirements if `!force`. - let have = Self::balance_on_hold(asset, reason, source); - let liquid = Self::reducible_total_balance_on_hold(asset, source, force); + let have = Self::balance_on_hold(asset.clone(), reason, source); + let liquid = Self::reducible_total_balance_on_hold(asset.clone(), source, force); if let BestEffort = precision { amount = amount.min(liquid).min(have); } else { @@ -360,19 +368,20 @@ pub trait Mutate: // We want to make sure we can deposit the amount in advance. If we can't then something is // very wrong. ensure!( - Self::can_deposit(asset, dest, amount, Extant) == Success, + Self::can_deposit(asset.clone(), dest, amount, Extant) == Success, TokenError::CannotCreate ); ensure!( - mode == Free || Self::hold_available(asset, reason, dest), + mode == Free || Self::hold_available(asset.clone(), reason, dest), TokenError::CannotCreateHold ); - let amount = Self::decrease_balance_on_hold(asset, reason, source, amount, precision)?; + let amount = + Self::decrease_balance_on_hold(asset.clone(), reason, source, amount, precision)?; let actual = if mode == OnHold { - Self::increase_balance_on_hold(asset, reason, dest, amount, precision)? + Self::increase_balance_on_hold(asset.clone(), reason, dest, amount, precision)? } else { - Self::increase_balance(asset, dest, amount, precision)? + Self::increase_balance(asset.clone(), dest, amount, precision)? }; Self::done_transfer_on_hold(asset, reason, source, dest, actual); Ok(actual) @@ -405,14 +414,14 @@ pub trait Mutate: expendability: Preservation, force: Fortitude, ) -> Result { - ensure!(Self::hold_available(asset, reason, dest), TokenError::CannotCreateHold); + ensure!(Self::hold_available(asset.clone(), reason, dest), TokenError::CannotCreateHold); ensure!( - Self::can_deposit(asset, dest, amount, Extant) == Success, + Self::can_deposit(asset.clone(), dest, amount, Extant) == Success, TokenError::CannotCreate ); let actual = - Self::decrease_balance(asset, source, amount, precision, expendability, force)?; - Self::increase_balance_on_hold(asset, reason, dest, actual, precision)?; + Self::decrease_balance(asset.clone(), source, amount, precision, expendability, force)?; + Self::increase_balance_on_hold(asset.clone(), reason, dest, actual, precision)?; Self::done_transfer_on_hold(asset, reason, source, dest, actual); Ok(actual) } diff --git a/frame/support/src/traits/tokens/fungibles/imbalance.rs b/frame/support/src/traits/tokens/fungibles/imbalance.rs index ab18eec38..1668268ea 100644 --- a/frame/support/src/traits/tokens/fungibles/imbalance.rs +++ b/frame/support/src/traits/tokens/fungibles/imbalance.rs @@ -59,7 +59,7 @@ impl< { fn drop(&mut self) { if !self.amount.is_zero() { - OnDrop::handle(self.asset, self.amount) + OnDrop::handle(self.asset.clone(), self.amount) } } } @@ -104,9 +104,9 @@ impl< pub fn split(self, amount: B) -> (Self, Self) { let first = self.amount.min(amount); let second = self.amount - first; - let asset = self.asset; + let asset = self.asset.clone(); sp_std::mem::forget(self); - (Imbalance::new(asset, first), Imbalance::new(asset, second)) + (Imbalance::new(asset.clone(), first), Imbalance::new(asset, second)) } pub fn merge(mut self, other: Self) -> Result { if self.asset == other.asset { @@ -135,7 +135,7 @@ impl< > { if self.asset == other.asset { let (a, b) = (self.amount, other.amount); - let asset = self.asset; + let asset = self.asset.clone(); sp_std::mem::forget((self, other)); if a == b { @@ -154,7 +154,7 @@ impl< } pub fn asset(&self) -> A { - self.asset + self.asset.clone() } } diff --git a/frame/support/src/traits/tokens/fungibles/regular.rs b/frame/support/src/traits/tokens/fungibles/regular.rs index 27d1a50b3..5a9d3e6e4 100644 --- a/frame/support/src/traits/tokens/fungibles/regular.rs +++ b/frame/support/src/traits/tokens/fungibles/regular.rs @@ -146,7 +146,7 @@ pub trait Unbalanced: Inspect { /// This should not be reimplemented. fn handle_raw_dust(asset: Self::AssetId, amount: Self::Balance) { Self::handle_dust(Dust( - asset, + asset.clone(), amount.min(Self::minimum_balance(asset).saturating_sub(One::one())), )) } @@ -193,13 +193,13 @@ pub trait Unbalanced: Inspect { preservation: Preservation, force: Fortitude, ) -> Result { - let old_balance = Self::balance(asset, who); - let free = Self::reducible_balance(asset, who, preservation, force); + let old_balance = Self::balance(asset.clone(), who); + let free = Self::reducible_balance(asset.clone(), who, preservation, force); if let BestEffort = precision { amount = amount.min(free); } let new_balance = old_balance.checked_sub(&amount).ok_or(TokenError::FundsUnavailable)?; - if let Some(dust) = Self::write_balance(asset, who, new_balance)? { + if let Some(dust) = Self::write_balance(asset.clone(), who, new_balance)? { Self::handle_dust(Dust(asset, dust)); } Ok(old_balance.saturating_sub(new_balance)) @@ -217,13 +217,13 @@ pub trait Unbalanced: Inspect { amount: Self::Balance, precision: Precision, ) -> Result { - let old_balance = Self::balance(asset, who); + let old_balance = Self::balance(asset.clone(), who); let new_balance = if let BestEffort = precision { old_balance.saturating_add(amount) } else { old_balance.checked_add(&amount).ok_or(ArithmeticError::Overflow)? }; - if new_balance < Self::minimum_balance(asset) { + if new_balance < Self::minimum_balance(asset.clone()) { // Attempt to increase from 0 to below minimum -> stays at zero. if let BestEffort = precision { Ok(Self::Balance::default()) @@ -234,7 +234,7 @@ pub trait Unbalanced: Inspect { if new_balance == old_balance { Ok(Self::Balance::default()) } else { - if let Some(dust) = Self::write_balance(asset, who, new_balance)? { + if let Some(dust) = Self::write_balance(asset.clone(), who, new_balance)? { Self::handle_dust(Dust(asset, dust)); } Ok(new_balance.saturating_sub(old_balance)) @@ -258,11 +258,14 @@ pub trait Mutate: Inspect + Unbalanced { who: &AccountId, amount: Self::Balance, ) -> Result { - Self::total_issuance(asset) + Self::total_issuance(asset.clone()) .checked_add(&amount) .ok_or(ArithmeticError::Overflow)?; - let actual = Self::increase_balance(asset, who, amount, Exact)?; - Self::set_total_issuance(asset, Self::total_issuance(asset).saturating_add(actual)); + let actual = Self::increase_balance(asset.clone(), who, amount, Exact)?; + Self::set_total_issuance( + asset.clone(), + Self::total_issuance(asset.clone()).saturating_add(actual), + ); Self::done_mint_into(asset, who, amount); Ok(actual) } @@ -277,13 +280,17 @@ pub trait Mutate: Inspect + Unbalanced { precision: Precision, force: Fortitude, ) -> Result { - let actual = Self::reducible_balance(asset, who, Expendable, force).min(amount); + let actual = Self::reducible_balance(asset.clone(), who, Expendable, force).min(amount); ensure!(actual == amount || precision == BestEffort, TokenError::FundsUnavailable); - Self::total_issuance(asset) + Self::total_issuance(asset.clone()) .checked_sub(&actual) .ok_or(ArithmeticError::Overflow)?; - let actual = Self::decrease_balance(asset, who, actual, BestEffort, Expendable, force)?; - Self::set_total_issuance(asset, Self::total_issuance(asset).saturating_sub(actual)); + let actual = + Self::decrease_balance(asset.clone(), who, actual, BestEffort, Expendable, force)?; + Self::set_total_issuance( + asset.clone(), + Self::total_issuance(asset.clone()).saturating_sub(actual), + ); Self::done_burn_from(asset, who, actual); Ok(actual) } @@ -303,13 +310,17 @@ pub trait Mutate: Inspect + Unbalanced { who: &AccountId, amount: Self::Balance, ) -> Result { - let actual = Self::reducible_balance(asset, who, Expendable, Polite).min(amount); + let actual = Self::reducible_balance(asset.clone(), who, Expendable, Polite).min(amount); ensure!(actual == amount, TokenError::FundsUnavailable); - Self::total_issuance(asset) + Self::total_issuance(asset.clone()) .checked_sub(&actual) .ok_or(ArithmeticError::Overflow)?; - let actual = Self::decrease_balance(asset, who, actual, BestEffort, Expendable, Polite)?; - Self::set_total_issuance(asset, Self::total_issuance(asset).saturating_sub(actual)); + let actual = + Self::decrease_balance(asset.clone(), who, actual, BestEffort, Expendable, Polite)?; + Self::set_total_issuance( + asset.clone(), + Self::total_issuance(asset.clone()).saturating_sub(actual), + ); Self::done_shelve(asset, who, actual); Ok(actual) } @@ -329,11 +340,14 @@ pub trait Mutate: Inspect + Unbalanced { who: &AccountId, amount: Self::Balance, ) -> Result { - Self::total_issuance(asset) + Self::total_issuance(asset.clone()) .checked_add(&amount) .ok_or(ArithmeticError::Overflow)?; - let actual = Self::increase_balance(asset, who, amount, Exact)?; - Self::set_total_issuance(asset, Self::total_issuance(asset).saturating_add(actual)); + let actual = Self::increase_balance(asset.clone(), who, amount, Exact)?; + Self::set_total_issuance( + asset.clone(), + Self::total_issuance(asset.clone()).saturating_add(actual), + ); Self::done_restore(asset, who, amount); Ok(actual) } @@ -346,13 +360,13 @@ pub trait Mutate: Inspect + Unbalanced { amount: Self::Balance, preservation: Preservation, ) -> Result { - let _extra = - Self::can_withdraw(asset, source, amount).into_result(preservation != Expendable)?; - Self::can_deposit(asset, dest, amount, Extant).into_result()?; - Self::decrease_balance(asset, source, amount, BestEffort, preservation, Polite)?; + let _extra = Self::can_withdraw(asset.clone(), source, amount) + .into_result(preservation != Expendable)?; + Self::can_deposit(asset.clone(), dest, amount, Extant).into_result()?; + Self::decrease_balance(asset.clone(), source, amount, BestEffort, preservation, Polite)?; // This should never fail as we checked `can_deposit` earlier. But we do a best-effort // anyway. - let _ = Self::increase_balance(asset, dest, amount, BestEffort); + let _ = Self::increase_balance(asset.clone(), dest, amount, BestEffort); Self::done_transfer(asset, source, dest, amount); Ok(amount) } @@ -363,7 +377,7 @@ pub trait Mutate: Inspect + Unbalanced { /// /// Returns the new balance. fn set_balance(asset: Self::AssetId, who: &AccountId, amount: Self::Balance) -> Self::Balance { - let b = Self::balance(asset, who); + let b = Self::balance(asset.clone(), who); if b > amount { Self::burn_from(asset, who, b - amount, BestEffort, Force).map(|d| b.saturating_sub(d)) } else { @@ -391,7 +405,7 @@ impl> HandleImbalanceDrop { fn handle(asset: U::AssetId, amount: U::Balance) { - U::set_total_issuance(asset, U::total_issuance(asset).saturating_add(amount)) + U::set_total_issuance(asset.clone(), U::total_issuance(asset).saturating_add(amount)) } } @@ -402,7 +416,7 @@ impl> HandleImbalanceDrop { fn handle(asset: U::AssetId, amount: U::Balance) { - U::set_total_issuance(asset, U::total_issuance(asset).saturating_sub(amount)) + U::set_total_issuance(asset.clone(), U::total_issuance(asset).saturating_sub(amount)) } } @@ -423,11 +437,11 @@ pub trait Balanced: Inspect + Unbalanced { /// This is infallible, but doesn't guarantee that the entire `amount` is burnt, for example /// in the case of underflow. fn rescind(asset: Self::AssetId, amount: Self::Balance) -> Debt { - let old = Self::total_issuance(asset); + let old = Self::total_issuance(asset.clone()); let new = old.saturating_sub(amount); - Self::set_total_issuance(asset, new); + Self::set_total_issuance(asset.clone(), new); let delta = old - new; - Self::done_rescind(asset, delta); + Self::done_rescind(asset.clone(), delta); Imbalance::::new( asset, delta, ) @@ -440,11 +454,11 @@ pub trait Balanced: Inspect + Unbalanced { /// This is infallible, but doesn't guarantee that the entire `amount` is issued, for example /// in the case of overflow. fn issue(asset: Self::AssetId, amount: Self::Balance) -> Credit { - let old = Self::total_issuance(asset); + let old = Self::total_issuance(asset.clone()); let new = old.saturating_add(amount); - Self::set_total_issuance(asset, new); + Self::set_total_issuance(asset.clone(), new); let delta = new - old; - Self::done_issue(asset, delta); + Self::done_issue(asset.clone(), delta); Imbalance::::new( asset, delta, ) @@ -458,7 +472,7 @@ pub trait Balanced: Inspect + Unbalanced { asset: Self::AssetId, amount: Self::Balance, ) -> (Debt, Credit) { - (Self::rescind(asset, amount), Self::issue(asset, amount)) + (Self::rescind(asset.clone(), amount), Self::issue(asset, amount)) } /// Mints `value` into the account of `who`, creating it as needed. @@ -476,8 +490,8 @@ pub trait Balanced: Inspect + Unbalanced { value: Self::Balance, precision: Precision, ) -> Result, DispatchError> { - let increase = Self::increase_balance(asset, who, value, precision)?; - Self::done_deposit(asset, who, increase); + let increase = Self::increase_balance(asset.clone(), who, value, precision)?; + Self::done_deposit(asset.clone(), who, increase); Ok(Imbalance::::new( asset, increase, )) @@ -504,8 +518,9 @@ pub trait Balanced: Inspect + Unbalanced { preservation: Preservation, force: Fortitude, ) -> Result, DispatchError> { - let decrease = Self::decrease_balance(asset, who, value, precision, preservation, force)?; - Self::done_withdraw(asset, who, decrease); + let decrease = + Self::decrease_balance(asset.clone(), who, value, precision, preservation, force)?; + Self::done_withdraw(asset.clone(), who, decrease); Ok(Imbalance::::new( asset, decrease, )) @@ -545,7 +560,7 @@ pub trait Balanced: Inspect + Unbalanced { ) -> Result, Debt> { let amount = debt.peek(); let asset = debt.asset(); - let credit = match Self::withdraw(asset, who, amount, Exact, preservation, Polite) { + let credit = match Self::withdraw(asset.clone(), who, amount, Exact, preservation, Polite) { Err(_) => return Err(debt), Ok(d) => d, }; diff --git a/frame/support/src/traits/tokens/misc.rs b/frame/support/src/traits/tokens/misc.rs index d1b17b548..baf3fd5f3 100644 --- a/frame/support/src/traits/tokens/misc.rs +++ b/frame/support/src/traits/tokens/misc.rs @@ -221,10 +221,10 @@ impl WithdrawReasons { /// Simple amalgamation trait to collect together properties for an AssetId under one roof. pub trait AssetId: - FullCodec + Copy + Eq + PartialEq + Debug + scale_info::TypeInfo + MaxEncodedLen + FullCodec + Clone + Eq + PartialEq + Debug + scale_info::TypeInfo + MaxEncodedLen { } -impl AssetId +impl AssetId for T { } From b9c9522eb14f972fc85e8cdc0a8a34a61ad113c2 Mon Sep 17 00:00:00 2001 From: Dmitry Markin Date: Tue, 23 May 2023 14:49:02 +0300 Subject: [PATCH 533/558] Split `Peerset` into `PeerStore` & `ProtocolController`s (#13611) --- Cargo.lock | 11 + .../grandpa/src/communication/tests.rs | 12 - client/network-gossip/src/bridge.rs | 12 - client/network-gossip/src/state_machine.rs | 12 - client/network/src/protocol.rs | 42 +- .../src/protocol/notifications/behaviour.rs | 212 +- .../src/protocol/notifications/tests.rs | 14 +- client/network/src/service.rs | 66 +- client/network/src/service/traits.rs | 40 +- client/network/sync/src/service/mock.rs | 6 - client/offchain/src/api.rs | 12 - client/offchain/src/lib.rs | 12 - client/peerset/Cargo.toml | 5 + client/peerset/src/lib.rs | 837 ++----- client/peerset/src/peer_store.rs | 403 ++++ client/peerset/src/peersstate.rs | 737 ------- client/peerset/src/protocol_controller.rs | 1939 +++++++++++++++++ client/peerset/tests/fuzz.rs | 266 ++- client/utils/src/mpsc.rs | 4 +- 19 files changed, 2914 insertions(+), 1728 deletions(-) create mode 100644 client/peerset/src/peer_store.rs delete mode 100644 client/peerset/src/peersstate.rs create mode 100644 client/peerset/src/protocol_controller.rs diff --git a/Cargo.lock b/Cargo.lock index f387b5ef9..59ccf359f 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -7565,6 +7565,12 @@ dependencies = [ "windows-sys 0.45.0", ] +[[package]] +name = "partial_sort" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7924d1d0ad836f665c9065e26d016c673ece3993f30d340068b16f282afc1156" + [[package]] name = "paste" version = "1.0.12" @@ -9719,9 +9725,14 @@ dependencies = [ "futures", "libp2p-identity", "log", + "mockall", + "parking_lot 0.12.1", + "partial_sort", "rand 0.8.5", "sc-utils", "serde_json", + "sp-arithmetic", + "sp-tracing", "wasm-timer", ] diff --git a/client/consensus/grandpa/src/communication/tests.rs b/client/consensus/grandpa/src/communication/tests.rs index f97b1f1e8..eb8838298 100644 --- a/client/consensus/grandpa/src/communication/tests.rs +++ b/client/consensus/grandpa/src/communication/tests.rs @@ -116,18 +116,6 @@ impl NetworkPeers for TestNetwork { fn remove_peers_from_reserved_set(&self, _protocol: ProtocolName, _peers: Vec) {} - fn add_to_peers_set( - &self, - _protocol: ProtocolName, - _peers: HashSet, - ) -> Result<(), String> { - unimplemented!(); - } - - fn remove_from_peers_set(&self, _protocol: ProtocolName, _peers: Vec) { - unimplemented!(); - } - fn sync_num_connected(&self) -> usize { unimplemented!(); } diff --git a/client/network-gossip/src/bridge.rs b/client/network-gossip/src/bridge.rs index 4793d7822..baf11c9e8 100644 --- a/client/network-gossip/src/bridge.rs +++ b/client/network-gossip/src/bridge.rs @@ -416,18 +416,6 @@ mod tests { fn remove_peers_from_reserved_set(&self, _protocol: ProtocolName, _peers: Vec) {} - fn add_to_peers_set( - &self, - _protocol: ProtocolName, - _peers: HashSet, - ) -> Result<(), String> { - unimplemented!(); - } - - fn remove_from_peers_set(&self, _protocol: ProtocolName, _peers: Vec) { - unimplemented!(); - } - fn sync_num_connected(&self) -> usize { unimplemented!(); } diff --git a/client/network-gossip/src/state_machine.rs b/client/network-gossip/src/state_machine.rs index e6d2b0e2a..ee65bd890 100644 --- a/client/network-gossip/src/state_machine.rs +++ b/client/network-gossip/src/state_machine.rs @@ -646,18 +646,6 @@ mod tests { fn remove_peers_from_reserved_set(&self, _protocol: ProtocolName, _peers: Vec) {} - fn add_to_peers_set( - &self, - _protocol: ProtocolName, - _peers: HashSet, - ) -> Result<(), String> { - unimplemented!(); - } - - fn remove_from_peers_set(&self, _protocol: ProtocolName, _peers: Vec) { - unimplemented!(); - } - fn sync_num_connected(&self) -> usize { unimplemented!(); } diff --git a/client/network/src/protocol.rs b/client/network/src/protocol.rs index e7214d814..5fbb518ce 100644 --- a/client/network/src/protocol.rs +++ b/client/network/src/protocol.rs @@ -257,8 +257,8 @@ impl Protocol { } /// Returns the list of reserved peers. - pub fn reserved_peers(&self) -> impl Iterator { - self.behaviour.reserved_peers(HARDCODED_PEERSETS_SYNC) + pub fn reserved_peers(&self, pending_response: oneshot::Sender>) { + self.behaviour.reserved_peers(HARDCODED_PEERSETS_SYNC, pending_response); } /// Adds a `PeerId` to the list of reserved peers for syncing purposes. @@ -310,39 +310,13 @@ impl Protocol { } } - /// Notify the protocol that we have learned about the existence of nodes on the default set. + /// Notify the protocol that we have learned about the existence of some peer. /// - /// Can be called multiple times with the same `PeerId`s. - pub fn add_default_set_discovered_nodes(&mut self, peer_ids: impl Iterator) { - for peer_id in peer_ids { - self.peerset_handle.add_to_peers_set(HARDCODED_PEERSETS_SYNC, peer_id); - } - } - - /// Add a peer to a peers set. - pub fn add_to_peers_set(&self, protocol: ProtocolName, peer: PeerId) { - if let Some(index) = self.notification_protocols.iter().position(|p| *p == protocol) { - self.peerset_handle.add_to_peers_set(sc_peerset::SetId::from(index), peer); - } else { - error!( - target: "sub-libp2p", - "add_to_peers_set with unknown protocol: {}", - protocol - ); - } - } - - /// Remove a peer from a peers set. - pub fn remove_from_peers_set(&self, protocol: ProtocolName, peer: PeerId) { - if let Some(index) = self.notification_protocols.iter().position(|p| *p == protocol) { - self.peerset_handle.remove_from_peers_set(sc_peerset::SetId::from(index), peer); - } else { - error!( - target: "sub-libp2p", - "remove_from_peers_set with unknown protocol: {}", - protocol - ); - } + /// Can be called multiple times with the same `PeerId`. + pub fn add_known_peer(&mut self, peer_id: PeerId) { + // TODO: get rid of this function and call `Peerset`/`PeerStore` directly + // from `NetworkWorker`. + self.peerset_handle.add_known_peer(peer_id); } } diff --git a/client/network/src/protocol/notifications/behaviour.rs b/client/network/src/protocol/notifications/behaviour.rs index 7e5679393..8f1324dfa 100644 --- a/client/network/src/protocol/notifications/behaviour.rs +++ b/client/network/src/protocol/notifications/behaviour.rs @@ -25,7 +25,7 @@ use crate::{ use bytes::BytesMut; use fnv::FnvHashMap; -use futures::prelude::*; +use futures::{channel::oneshot, prelude::*}; use libp2p::{ core::{ConnectedPoint, Endpoint, Multiaddr}, swarm::{ @@ -35,7 +35,7 @@ use libp2p::{ }, PeerId, }; -use log::{error, trace, warn}; +use log::{debug, error, info, trace, warn}; use parking_lot::RwLock; use rand::distributions::{Distribution as _, Uniform}; use sc_peerset::DropReason; @@ -231,6 +231,9 @@ enum PeerState { /// If `Some`, any dial attempts to this peer are delayed until the given `Instant`. backoff_until: Option, + /// Incoming index tracking this connection. + incoming_index: sc_peerset::IncomingIndex, + /// List of connections with this peer, and their state. connections: SmallVec<[(ConnectionId, ConnectionState); crate::MAX_CONNECTIONS_PER_PEER]>, }, @@ -493,7 +496,7 @@ impl Notifications { // Incoming => Disabled. // Ongoing opening requests from the remote are rejected. - PeerState::Incoming { mut connections, backoff_until } => { + PeerState::Incoming { mut connections, backoff_until, .. } => { let inc = if let Some(inc) = self .incoming .iter_mut() @@ -536,8 +539,12 @@ impl Notifications { } /// Returns the list of reserved peers. - pub fn reserved_peers(&self, set_id: sc_peerset::SetId) -> impl Iterator { - self.peerset.reserved_peers(set_id) + pub fn reserved_peers( + &self, + set_id: sc_peerset::SetId, + pending_response: oneshot::Sender>, + ) { + self.peerset.reserved_peers(set_id, pending_response); } /// Returns the state of the peerset manager, for debugging purposes. @@ -686,65 +693,34 @@ impl Notifications { }; } }, - - // Incoming => Enabled - PeerState::Incoming { mut connections, .. } => { - trace!(target: "sub-libp2p", "PSM => Connect({}, {:?}): Enabling connections.", - occ_entry.key().0, set_id); - if let Some(inc) = self - .incoming - .iter_mut() - .find(|i| i.peer_id == occ_entry.key().0 && i.set_id == set_id && i.alive) - { - inc.alive = false; - } else { - error!( - target: "sub-libp2p", - "State mismatch in libp2p: no entry in incoming for incoming peer", - ) - } - - debug_assert!(connections - .iter() - .any(|(_, s)| matches!(s, ConnectionState::OpenDesiredByRemote))); - for (connec_id, connec_state) in connections - .iter_mut() - .filter(|(_, s)| matches!(s, ConnectionState::OpenDesiredByRemote)) - { - trace!(target: "sub-libp2p", "Handler({:?}, {:?}) <= Open({:?})", - occ_entry.key(), *connec_id, set_id); - self.events.push_back(ToSwarm::NotifyHandler { - peer_id: occ_entry.key().0, - handler: NotifyHandler::One(*connec_id), - event: NotifsHandlerIn::Open { protocol_index: set_id.into() }, - }); - *connec_state = ConnectionState::Opening; - } - - *occ_entry.into_mut() = PeerState::Enabled { connections }; + // Incoming => Incoming + st @ PeerState::Incoming { .. } => { + debug!( + target: "sub-libp2p", + "PSM => Connect({}, {:?}): Ignoring obsolete connect, we are awaiting accept/reject.", + occ_entry.key().0, set_id + ); + *occ_entry.into_mut() = st; }, // Other states are kept as-is. st @ PeerState::Enabled { .. } => { - warn!(target: "sub-libp2p", + debug!(target: "sub-libp2p", "PSM => Connect({}, {:?}): Already connected.", occ_entry.key().0, set_id); *occ_entry.into_mut() = st; - debug_assert!(false); }, st @ PeerState::DisabledPendingEnable { .. } => { - warn!(target: "sub-libp2p", + debug!(target: "sub-libp2p", "PSM => Connect({}, {:?}): Already pending enabling.", occ_entry.key().0, set_id); *occ_entry.into_mut() = st; - debug_assert!(false); }, st @ PeerState::Requested { .. } | st @ PeerState::PendingRequest { .. } => { - warn!(target: "sub-libp2p", + debug!(target: "sub-libp2p", "PSM => Connect({}, {:?}): Duplicate request.", occ_entry.key().0, set_id); *occ_entry.into_mut() = st; - debug_assert!(false); }, PeerState::Poisoned => { @@ -847,10 +823,12 @@ impl Notifications { // Invalid state transitions. st @ PeerState::Incoming { .. } => { - error!(target: "sub-libp2p", "PSM => Drop({}, {:?}): Not enabled (Incoming).", - entry.key().0, set_id); + info!( + target: "sub-libp2p", + "PSM => Drop({}, {:?}): Ignoring obsolete disconnect, we are awaiting accept/reject.", + entry.key().0, set_id, + ); *entry.into_mut() = st; - debug_assert!(false); }, PeerState::Poisoned => { error!(target: "sub-libp2p", "State of {:?} is poisoned", entry.key()); @@ -895,7 +873,24 @@ impl Notifications { match mem::replace(state, PeerState::Poisoned) { // Incoming => Enabled - PeerState::Incoming { mut connections, .. } => { + PeerState::Incoming { mut connections, incoming_index, .. } => { + if index < incoming_index { + warn!( + target: "sub-libp2p", + "PSM => Accept({:?}, {}, {:?}): Ignoring obsolete incoming index, we are already awaiting {:?}.", + index, incoming.peer_id, incoming.set_id, incoming_index + ); + return + } else if index > incoming_index { + error!( + target: "sub-libp2p", + "PSM => Accept({:?}, {}, {:?}): Ignoring incoming index from the future, we are awaiting {:?}.", + index, incoming.peer_id, incoming.set_id, incoming_index + ); + debug_assert!(false); + return + } + trace!(target: "sub-libp2p", "PSM => Accept({:?}, {}, {:?}): Enabling connections.", index, incoming.peer_id, incoming.set_id); @@ -955,7 +950,24 @@ impl Notifications { match mem::replace(state, PeerState::Poisoned) { // Incoming => Disabled - PeerState::Incoming { mut connections, backoff_until } => { + PeerState::Incoming { mut connections, backoff_until, incoming_index } => { + if index < incoming_index { + warn!( + target: "sub-libp2p", + "PSM => Reject({:?}, {}, {:?}): Ignoring obsolete incoming index, we are already awaiting {:?}.", + index, incoming.peer_id, incoming.set_id, incoming_index + ); + return + } else if index > incoming_index { + error!( + target: "sub-libp2p", + "PSM => Reject({:?}, {}, {:?}): Ignoring incoming index from the future, we are awaiting {:?}.", + index, incoming.peer_id, incoming.set_id, incoming_index + ); + debug_assert!(false); + return + } + trace!(target: "sub-libp2p", "PSM => Reject({:?}, {}, {:?}): Rejecting connections.", index, incoming.peer_id, incoming.set_id); @@ -1195,7 +1207,7 @@ impl NetworkBehaviour for Notifications { }, // Incoming => Incoming | Disabled | Backoff | Ø - PeerState::Incoming { mut connections, backoff_until } => { + PeerState::Incoming { mut connections, backoff_until, incoming_index } => { trace!( target: "sub-libp2p", "Libp2p => Disconnected({}, {:?}, {:?}): OpenDesiredByRemote.", @@ -1269,8 +1281,11 @@ impl NetworkBehaviour for Notifications { *entry.get_mut() = PeerState::Disabled { connections, backoff_until }; } else { - *entry.get_mut() = - PeerState::Incoming { connections, backoff_until }; + *entry.get_mut() = PeerState::Incoming { + connections, + backoff_until, + incoming_index, + }; } }, @@ -1489,7 +1504,7 @@ impl NetworkBehaviour for Notifications { match mem::replace(entry.get_mut(), PeerState::Poisoned) { // Incoming => Incoming - PeerState::Incoming { mut connections, backoff_until } => { + PeerState::Incoming { mut connections, backoff_until, incoming_index } => { debug_assert!(connections .iter() .any(|(_, s)| matches!(s, ConnectionState::OpenDesiredByRemote))); @@ -1517,7 +1532,8 @@ impl NetworkBehaviour for Notifications { debug_assert!(false); } - *entry.into_mut() = PeerState::Incoming { connections, backoff_until }; + *entry.into_mut() = + PeerState::Incoming { connections, backoff_until, incoming_index }; }, PeerState::Enabled { mut connections } => { @@ -1582,8 +1598,11 @@ impl NetworkBehaviour for Notifications { incoming_id, }); - *entry.into_mut() = - PeerState::Incoming { connections, backoff_until }; + *entry.into_mut() = PeerState::Incoming { + connections, + backoff_until, + incoming_index: incoming_id, + }; } else { // Connections in `OpeningThenClosing` and `Closing` state can be // in a Closed phase, and as such can emit `OpenDesiredByRemote` @@ -2087,6 +2106,7 @@ mod tests { use super::*; use crate::protocol::notifications::handler::tests::*; use libp2p::swarm::AddressRecord; + use sc_peerset::IncomingIndex; use std::{collections::HashSet, iter}; impl PartialEq for ConnectionState { @@ -2279,7 +2299,7 @@ mod tests { NotifsHandlerOut::OpenDesiredByRemote { protocol_index: 0 }, ); - if let Some(&PeerState::Incoming { ref connections, backoff_until: None }) = + if let Some(&PeerState::Incoming { ref connections, backoff_until: None, .. }) = notif.peers.get(&(peer, 0.into())) { assert_eq!(connections.len(), 1); @@ -2424,8 +2444,10 @@ mod tests { NotifsHandlerOut::OpenDesiredByRemote { protocol_index: 0 }, ); - // attempt to connect to the peer and verify that the peer state is `Enabled` - notif.peerset_report_connect(peer, set_id); + // attempt to connect to the peer and verify that the peer state is `Enabled`; + // we rely on implementation detail that incoming indices are counted from 0 + // to not mock the `Peerset` + notif.peerset_report_accept(IncomingIndex(0)); assert!(std::matches!(notif.peers.get(&(peer, set_id)), Some(&PeerState::Enabled { .. }))); } @@ -2502,7 +2524,9 @@ mod tests { conn, NotifsHandlerOut::OpenDesiredByRemote { protocol_index: 0 }, ); - notif.peerset_report_connect(peer, set_id); + // we rely on the implementation detail that incoming indices are counted from 0 + // to not mock the `Peerset` + notif.peerset_report_accept(IncomingIndex(0)); assert!(std::matches!(notif.peers.get(&(peer, set_id)), Some(&PeerState::Enabled { .. }))); // disconnect peer and verify that the state is `Disabled` @@ -2859,7 +2883,9 @@ mod tests { ); assert!(std::matches!(notif.peers.get(&(peer, set_id)), Some(&PeerState::Incoming { .. }))); - notif.peerset_report_connect(peer, set_id); + // We rely on the implementation detail that incoming indices are counted + // from 0 to not mock the `Peerset`. + notif.peerset_report_accept(sc_peerset::IncomingIndex(0)); assert!(std::matches!(notif.peers.get(&(peer, set_id)), Some(&PeerState::Enabled { .. }))); // open new substream @@ -2948,7 +2974,6 @@ mod tests { // check peer information assert_eq!(notif.open_peers().collect::>(), vec![&peer],); - assert_eq!(notif.reserved_peers(set_id).collect::>(), Vec::<&PeerId>::new(),); assert_eq!(notif.num_discovered_peers(), 0usize); // close the other connection and verify that notification replacement event is emitted @@ -3703,7 +3728,9 @@ mod tests { ); assert!(std::matches!(notif.peers.get(&(peer, set_id)), Some(&PeerState::Incoming { .. }))); - notif.peerset_report_connect(peer, set_id); + // we rely on the implementation detail that incoming indices are counted from 0 + // to not mock the `Peerset` + notif.peerset_report_accept(IncomingIndex(0)); assert!(std::matches!(notif.peers.get(&(peer, set_id)), Some(&PeerState::Enabled { .. }))); let event = conn_yielder.open_substream(peer, 0, connected, vec![1, 2, 3, 4]); @@ -3834,7 +3861,6 @@ mod tests { } #[test] - #[should_panic] #[cfg(debug_assertions)] fn peerset_report_connect_with_disabled_pending_enable_peer() { let (mut notif, _peerset) = development_notifs(); @@ -3872,11 +3898,15 @@ mod tests { Some(&PeerState::DisabledPendingEnable { .. }) )); + // duplicate "connect" must not change the state notif.peerset_report_connect(peer, set_id); + assert!(std::matches!( + notif.peers.get(&(peer, set_id)), + Some(&PeerState::DisabledPendingEnable { .. }) + )); } #[test] - #[should_panic] #[cfg(debug_assertions)] fn peerset_report_connect_with_requested_peer() { let (mut notif, _peerset) = development_notifs(); @@ -3887,11 +3917,12 @@ mod tests { notif.peerset_report_connect(peer, set_id); assert!(std::matches!(notif.peers.get(&(peer, set_id)), Some(&PeerState::Requested))); + // Duplicate "connect" must not change the state. notif.peerset_report_connect(peer, set_id); + assert!(std::matches!(notif.peers.get(&(peer, set_id)), Some(&PeerState::Requested))); } #[test] - #[should_panic] #[cfg(debug_assertions)] fn peerset_report_connect_with_pending_requested() { let (mut notif, _peerset) = development_notifs(); @@ -3940,13 +3971,17 @@ mod tests { Some(&PeerState::PendingRequest { .. }) )); + // duplicate "connect" must not change the state notif.peerset_report_connect(peer, set_id); + assert!(std::matches!( + notif.peers.get(&(peer, set_id)), + Some(&PeerState::PendingRequest { .. }) + )); } #[test] - #[should_panic] #[cfg(debug_assertions)] - fn peerset_report_disconnect_with_incoming_peer() { + fn peerset_report_connect_with_incoming_peer() { let (mut notif, _peerset) = development_notifs(); let peer = PeerId::random(); let set_id = sc_peerset::SetId::from(0); @@ -3973,10 +4008,45 @@ mod tests { conn, NotifsHandlerOut::OpenDesiredByRemote { protocol_index: 0 }, ); + assert!(std::matches!(notif.peers.get(&(peer, set_id)), Some(&PeerState::Incoming { .. }))); + + notif.peerset_report_connect(peer, set_id); + assert!(std::matches!(notif.peers.get(&(peer, set_id)), Some(&PeerState::Incoming { .. }))); + } + #[test] + #[cfg(debug_assertions)] + fn peerset_report_disconnect_with_incoming_peer() { + let (mut notif, _peerset) = development_notifs(); + let peer = PeerId::random(); + let set_id = sc_peerset::SetId::from(0); + let conn = ConnectionId::new_unchecked(0); + let connected = ConnectedPoint::Listener { + local_addr: Multiaddr::empty(), + send_back_addr: Multiaddr::empty(), + }; + + notif.on_swarm_event(FromSwarm::ConnectionEstablished( + libp2p::swarm::behaviour::ConnectionEstablished { + peer_id: peer, + connection_id: conn, + endpoint: &connected, + failed_addresses: &[], + other_established: 0usize, + }, + )); + assert!(std::matches!(notif.peers.get(&(peer, set_id)), Some(&PeerState::Disabled { .. }))); + + // remote opens a substream, verify that peer state is updated to `Incoming` + notif.on_connection_handler_event( + peer, + conn, + NotifsHandlerOut::OpenDesiredByRemote { protocol_index: 0 }, + ); assert!(std::matches!(notif.peers.get(&(peer, set_id)), Some(&PeerState::Incoming { .. }))); notif.peerset_report_disconnect(peer, set_id); + assert!(std::matches!(notif.peers.get(&(peer, set_id)), Some(&PeerState::Incoming { .. }))); } #[test] diff --git a/client/network/src/protocol/notifications/tests.rs b/client/network/src/protocol/notifications/tests.rs index d13a4fcfa..998ec79f8 100644 --- a/client/network/src/protocol/notifications/tests.rs +++ b/client/network/src/protocol/notifications/tests.rs @@ -307,8 +307,20 @@ fn reconnect_after_disconnect() { _ => {}, } + // Due to the bug in `Notifications`, the disconnected node does not always detect that + // it was disconnected. The closed inbound substream is tolerated by design, and the + // closed outbound substream is not detected until something is sent into it. + // See [PR #13396](https://github.com/paritytech/substrate/pull/13396). + // This happens if the disconnecting node reconnects to it fast enough. + // In this case the disconnected node does not transit via `ServiceState::NotConnected` + // and stays in `ServiceState::FirstConnec`. + // TODO: update this once the fix is finally merged. if service1_state == ServiceState::ConnectedAgain && - service2_state == ServiceState::ConnectedAgain + service2_state == ServiceState::ConnectedAgain || + service1_state == ServiceState::ConnectedAgain && + service2_state == ServiceState::FirstConnec || + service1_state == ServiceState::FirstConnec && + service2_state == ServiceState::ConnectedAgain { break } diff --git a/client/network/src/service.rs b/client/network/src/service.rs index cd8e18a2e..34d97ca07 100644 --- a/client/network/src/service.rs +++ b/client/network/src/service.rs @@ -620,8 +620,11 @@ where } /// Returns the list of reserved peers. - pub fn reserved_peers(&self) -> impl Iterator { - self.network_service.behaviour().user_protocol().reserved_peers() + fn reserved_peers(&self, pending_response: oneshot::Sender>) { + self.network_service + .behaviour() + .user_protocol() + .reserved_peers(pending_response); } } @@ -882,40 +885,6 @@ where } } - fn add_to_peers_set( - &self, - protocol: ProtocolName, - peers: HashSet, - ) -> Result<(), String> { - let peers = self.split_multiaddr_and_peer_id(peers)?; - - for (peer_id, addr) in peers.into_iter() { - // Make sure the local peer ID is never added to the PSM. - if peer_id == self.local_peer_id { - return Err("Local peer ID cannot be added as a reserved peer.".to_string()) - } - - if !addr.is_empty() { - let _ = self - .to_worker - .unbounded_send(ServiceToWorkerMsg::AddKnownAddress(peer_id, addr)); - } - let _ = self - .to_worker - .unbounded_send(ServiceToWorkerMsg::AddToPeersSet(protocol.clone(), peer_id)); - } - - Ok(()) - } - - fn remove_from_peers_set(&self, protocol: ProtocolName, peers: Vec) { - for peer_id in peers.into_iter() { - let _ = self - .to_worker - .unbounded_send(ServiceToWorkerMsg::RemoveFromPeersSet(protocol.clone(), peer_id)); - } - } - fn sync_num_connected(&self) -> usize { self.num_connected.load(Ordering::Relaxed) } @@ -1128,8 +1097,6 @@ enum ServiceToWorkerMsg { SetPeersetReserved(ProtocolName, HashSet), AddSetReserved(ProtocolName, PeerId), RemoveSetReserved(ProtocolName, PeerId), - AddToPeersSet(ProtocolName, PeerId), - RemoveFromPeersSet(ProtocolName, PeerId), EventStream(out_events::Sender), Request { target: PeerId, @@ -1306,16 +1273,6 @@ where .remove_set_reserved_peer(protocol, peer_id), ServiceToWorkerMsg::AddKnownAddress(peer_id, addr) => self.network_service.behaviour_mut().add_known_address(peer_id, addr), - ServiceToWorkerMsg::AddToPeersSet(protocol, peer_id) => self - .network_service - .behaviour_mut() - .user_protocol_mut() - .add_to_peers_set(protocol, peer_id), - ServiceToWorkerMsg::RemoveFromPeersSet(protocol, peer_id) => self - .network_service - .behaviour_mut() - .user_protocol_mut() - .remove_from_peers_set(protocol, peer_id), ServiceToWorkerMsg::EventStream(sender) => self.event_streams.push(sender), ServiceToWorkerMsg::Request { target, @@ -1349,8 +1306,7 @@ where .user_protocol_mut() .set_notification_handshake(protocol, handshake), ServiceToWorkerMsg::ReservedPeers { pending_response } => { - let _ = - pending_response.send(self.reserved_peers().map(ToOwned::to_owned).collect()); + self.reserved_peers(pending_response); }, } } @@ -1454,16 +1410,10 @@ where .behaviour_mut() .add_self_reported_address_to_dht(&peer_id, &protocols, addr); } - self.network_service - .behaviour_mut() - .user_protocol_mut() - .add_default_set_discovered_nodes(iter::once(peer_id)); + self.network_service.behaviour_mut().user_protocol_mut().add_known_peer(peer_id); }, SwarmEvent::Behaviour(BehaviourOut::Discovered(peer_id)) => { - self.network_service - .behaviour_mut() - .user_protocol_mut() - .add_default_set_discovered_nodes(iter::once(peer_id)); + self.network_service.behaviour_mut().user_protocol_mut().add_known_peer(peer_id); }, SwarmEvent::Behaviour(BehaviourOut::RandomKademliaStarted) => { if let Some(metrics) = self.metrics.as_ref() { diff --git a/client/network/src/service/traits.rs b/client/network/src/service/traits.rs index 787ef4b5a..22af3816b 100644 --- a/client/network/src/service/traits.rs +++ b/client/network/src/service/traits.rs @@ -156,10 +156,6 @@ pub trait NetworkPeers { /// Disconnect from a node as soon as possible. /// /// This triggers the same effects as if the connection had closed itself spontaneously. - /// - /// See also [`NetworkPeers::remove_from_peers_set`], which has the same effect but also - /// prevents the local node from re-establishing an outgoing substream to this peer until it - /// is added again. fn disconnect_peer(&self, who: PeerId, protocol: ProtocolName); /// Connect to unreserved peers and allow unreserved peers to connect for syncing purposes. @@ -216,26 +212,6 @@ pub trait NetworkPeers { /// Remove peers from a peer set. fn remove_peers_from_reserved_set(&self, protocol: ProtocolName, peers: Vec); - /// Add a peer to a set of peers. - /// - /// If the set has slots available, it will try to open a substream with this peer. - /// - /// Each `Multiaddr` must end with a `/p2p/` component containing the `PeerId`. It can also - /// consist of only `/p2p/`. - /// - /// Returns an `Err` if one of the given addresses is invalid or contains an - /// invalid peer ID (which includes the local peer ID). - fn add_to_peers_set( - &self, - protocol: ProtocolName, - peers: HashSet, - ) -> Result<(), String>; - - /// Remove peers from a peer set. - /// - /// If we currently have an open substream with this peer, it will soon be closed. - fn remove_from_peers_set(&self, protocol: ProtocolName, peers: Vec); - /// Returns the number of peers in the sync peer set we're connected to. fn sync_num_connected(&self) -> usize; } @@ -259,6 +235,10 @@ where } fn report_peer(&self, who: PeerId, cost_benefit: ReputationChange) { + // TODO: when we get rid of `Peerset`, we'll likely need to add some kind of async + // interface to `PeerStore`, otherwise we'll have trouble calling functions accepting + // `&mut self` via `Arc`. + // See https://github.com/paritytech/substrate/issues/14170. T::report_peer(self, who, cost_benefit) } @@ -302,18 +282,6 @@ where T::remove_peers_from_reserved_set(self, protocol, peers) } - fn add_to_peers_set( - &self, - protocol: ProtocolName, - peers: HashSet, - ) -> Result<(), String> { - T::add_to_peers_set(self, protocol, peers) - } - - fn remove_from_peers_set(&self, protocol: ProtocolName, peers: Vec) { - T::remove_from_peers_set(self, protocol, peers) - } - fn sync_num_connected(&self) -> usize { T::sync_num_connected(self) } diff --git a/client/network/sync/src/service/mock.rs b/client/network/sync/src/service/mock.rs index c88263399..9bce9f91b 100644 --- a/client/network/sync/src/service/mock.rs +++ b/client/network/sync/src/service/mock.rs @@ -101,12 +101,6 @@ mockall::mock! { peers: HashSet, ) -> Result<(), String>; fn remove_peers_from_reserved_set(&self, protocol: ProtocolName, peers: Vec); - fn add_to_peers_set( - &self, - protocol: ProtocolName, - peers: HashSet, - ) -> Result<(), String>; - fn remove_from_peers_set(&self, protocol: ProtocolName, peers: Vec); fn sync_num_connected(&self) -> usize; } diff --git a/client/offchain/src/api.rs b/client/offchain/src/api.rs index a15f03bab..f6cfdcd53 100644 --- a/client/offchain/src/api.rs +++ b/client/offchain/src/api.rs @@ -392,18 +392,6 @@ mod tests { unimplemented!(); } - fn add_to_peers_set( - &self, - _protocol: ProtocolName, - _peers: HashSet, - ) -> Result<(), String> { - unimplemented!(); - } - - fn remove_from_peers_set(&self, _protocol: ProtocolName, _peers: Vec) { - unimplemented!(); - } - fn sync_num_connected(&self) -> usize { unimplemented!(); } diff --git a/client/offchain/src/lib.rs b/client/offchain/src/lib.rs index f46fb637a..013bb2798 100644 --- a/client/offchain/src/lib.rs +++ b/client/offchain/src/lib.rs @@ -333,18 +333,6 @@ mod tests { unimplemented!(); } - fn add_to_peers_set( - &self, - _protocol: ProtocolName, - _peers: HashSet, - ) -> Result<(), String> { - unimplemented!(); - } - - fn remove_from_peers_set(&self, _protocol: ProtocolName, _peers: Vec) { - unimplemented!(); - } - fn sync_num_connected(&self) -> usize { unimplemented!(); } diff --git a/client/peerset/Cargo.toml b/client/peerset/Cargo.toml index 043f8a835..b07a6ffa0 100644 --- a/client/peerset/Cargo.toml +++ b/client/peerset/Cargo.toml @@ -17,9 +17,14 @@ targets = ["x86_64-unknown-linux-gnu"] futures = "0.3.21" libp2p-identity = { version = "0.1.2", features = ["peerid", "ed25519"] } log = "0.4.17" +parking_lot = "0.12.1" +partial_sort = "0.2.0" serde_json = "1.0.85" wasm-timer = "0.2" sc-utils = { version = "4.0.0-dev", path = "../utils" } +sp-arithmetic = { version = "6.0.0", path = "../../primitives/arithmetic" } [dev-dependencies] +sp-tracing = { version = "6.0.0", path = "../../primitives/tracing" } +mockall = "0.11.3" rand = "0.8.5" diff --git a/client/peerset/src/lib.rs b/client/peerset/src/lib.rs index e169be8e8..8a5ed7ddc 100644 --- a/client/peerset/src/lib.rs +++ b/client/peerset/src/lib.rs @@ -32,29 +32,32 @@ //! In addition, for each, set, the peerset also holds a list of reserved nodes towards which it //! will at all time try to maintain a connection with. -mod peersstate; +mod peer_store; +mod protocol_controller; -use futures::{channel::oneshot, prelude::*}; -use log::{debug, error, trace}; +use peer_store::{PeerStore, PeerStoreHandle, PeerStoreProvider}; +use protocol_controller::{ProtocolController, ProtocolHandle}; + +use futures::{ + channel::oneshot, + future::{join_all, BoxFuture, JoinAll}, + prelude::*, + stream::Stream, +}; +use log::debug; use sc_utils::mpsc::{tracing_unbounded, TracingUnboundedReceiver, TracingUnboundedSender}; use serde_json::json; use std::{ - collections::{HashMap, HashSet, VecDeque}, + collections::HashSet, pin::Pin, task::{Context, Poll}, - time::{Duration, Instant}, }; -use wasm_timer::Delay; pub use libp2p_identity::PeerId; -/// We don't accept nodes whose reputation is under this value. -pub const BANNED_THRESHOLD: i32 = 82 * (i32::MIN / 100); -/// Reputation change for a node when we get disconnected from it. -const DISCONNECT_REPUTATION_CHANGE: i32 = -256; -/// Amount of time between the moment we disconnect from a node and the moment we remove it from -/// the list. -const FORGET_AFTER: Duration = Duration::from_secs(3600); +pub use peer_store::BANNED_THRESHOLD; + +pub const LOG_TARGET: &str = "peerset"; #[derive(Debug)] enum Action { @@ -63,8 +66,7 @@ enum Action { SetReservedPeers(SetId, HashSet), SetReservedOnly(SetId, bool), ReportPeer(PeerId, ReputationChange), - AddToPeersSet(SetId, PeerId), - RemoveFromPeersSet(SetId, PeerId), + AddKnownPeer(PeerId), PeerReputation(PeerId, oneshot::Sender), } @@ -157,14 +159,9 @@ impl PeersetHandle { let _ = self.tx.unbounded_send(Action::ReportPeer(peer_id, score_diff)); } - /// Add a peer to a set. - pub fn add_to_peers_set(&self, set_id: SetId, peer_id: PeerId) { - let _ = self.tx.unbounded_send(Action::AddToPeersSet(set_id, peer_id)); - } - - /// Remove a peer from a set. - pub fn remove_from_peers_set(&self, set_id: SetId, peer_id: PeerId) { - let _ = self.tx.unbounded_send(Action::RemoveFromPeersSet(set_id, peer_id)); + /// Add a peer to the list of known peers. + pub fn add_known_peer(&self, peer_id: PeerId) { + let _ = self.tx.unbounded_send(Action::AddKnownPeer(peer_id)); } /// Returns the reputation value of the peer. @@ -184,6 +181,7 @@ pub enum Message { /// Request to open a connection to the given peer. From the point of view of the PSM, we are /// immediately connected. Connect { + /// Set id to connect on. set_id: SetId, /// Peer to connect to. peer_id: PeerId, @@ -191,6 +189,7 @@ pub enum Message { /// Drop the connection to the given peer, or cancel the connection attempt after a `Connect`. Drop { + /// Set id to disconnect on. set_id: SetId, /// Peer to disconnect from. peer_id: PeerId, @@ -204,7 +203,7 @@ pub enum Message { } /// Opaque identifier for an incoming connection. Allocated by the network. -#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)] +#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] pub struct IncomingIndex(pub u64); impl From for IncomingIndex { @@ -249,360 +248,73 @@ pub struct SetConfig { /// /// Implements the `Stream` trait and can be polled for messages. The `Stream` never ends and never /// errors. -#[derive(Debug)] pub struct Peerset { - /// Underlying data structure for the nodes's states. - data: peersstate::PeersState, - /// For each set, lists of nodes that don't occupy slots and that we should try to always be - /// connected to, and whether only reserved nodes are accepted. Is kept in sync with the list - /// of non-slot-occupying nodes in [`Peerset::data`]. - reserved_nodes: Vec<(HashSet, bool)>, - /// Receiver for messages from the `PeersetHandle` and from `tx`. - rx: TracingUnboundedReceiver, - /// Sending side of `rx`. - tx: TracingUnboundedSender, - /// Queue of messages to be emitted when the `Peerset` is polled. - message_queue: VecDeque, - /// When the `Peerset` was created. - created: Instant, - /// Last time when we updated the reputations of connected nodes. - latest_time_update: Instant, - /// Next time to do a periodic call to `alloc_slots` with all sets. This is done once per - /// second, to match the period of the reputation updates. - next_periodic_alloc_slots: Delay, + /// Peer reputation store handle. + peer_store_handle: PeerStoreHandle, + /// Peer reputation store. + peer_store_future: BoxFuture<'static, ()>, + /// Protocol handles. + protocol_handles: Vec, + /// Protocol controllers responsible for connections, per `SetId`. + protocol_controller_futures: JoinAll>, + /// Commands sent from protocol controllers to `Notifications`. The size of this vector never + /// changes. + from_controllers: TracingUnboundedReceiver, + /// Receiver for messages from the `PeersetHandle` and from `to_self`. + from_handle: TracingUnboundedReceiver, + /// Sending side of `from_handle`. + to_self: TracingUnboundedSender, } impl Peerset { /// Builds a new peerset from the given configuration. - pub fn from_config(config: PeersetConfig) -> (Self, PeersetHandle) { - let (tx, rx) = tracing_unbounded("mpsc_peerset_messages", 10_000); - - let handle = PeersetHandle { tx: tx.clone() }; - - let mut peerset = { - let now = Instant::now(); - - Self { - data: peersstate::PeersState::new(config.sets.iter().map(|set| { - peersstate::SetConfig { in_peers: set.in_peers, out_peers: set.out_peers } - })), - tx, - rx, - reserved_nodes: config - .sets - .iter() - .map(|set| (set.reserved_nodes.clone(), set.reserved_only)) - .collect(), - message_queue: VecDeque::new(), - created: now, - latest_time_update: now, - next_periodic_alloc_slots: Delay::new(Duration::new(0, 0)), - } + pub fn from_config(config: PeersetConfig) -> (Peerset, PeersetHandle) { + let default_set_config = &config.sets[0]; + let peer_store = PeerStore::new(default_set_config.bootnodes.clone()); + + let (to_notifications, from_controllers) = + tracing_unbounded("mpsc_protocol_controllers_to_notifications", 10_000); + + let controllers = config + .sets + .into_iter() + .enumerate() + .map(|(set, set_config)| { + ProtocolController::new( + SetId::from(set), + set_config, + to_notifications.clone(), + Box::new(peer_store.handle()), + ) + }) + .collect::>(); + + let (protocol_handles, protocol_controllers): (Vec, Vec<_>) = + controllers.into_iter().unzip(); + + let (to_self, from_handle) = tracing_unbounded("mpsc_peerset_messages", 10_000); + + let handle = PeersetHandle { tx: to_self.clone() }; + + let protocol_controller_futures = + join_all(protocol_controllers.into_iter().map(|c| c.run().boxed())); + + let peerset = Peerset { + peer_store_handle: peer_store.handle(), + peer_store_future: peer_store.run().boxed(), + protocol_handles, + protocol_controller_futures, + from_controllers, + from_handle, + to_self, }; - for (set, set_config) in config.sets.into_iter().enumerate() { - for node in set_config.reserved_nodes { - peerset.data.add_no_slot_node(set, node); - } - - for peer_id in set_config.bootnodes { - if let peersstate::Peer::Unknown(entry) = peerset.data.peer(set, &peer_id) { - entry.discover(); - } else { - debug!(target: "peerset", "Duplicate bootnode in config: {:?}", peer_id); - } - } - } - - for set_index in 0..peerset.data.num_sets() { - peerset.alloc_slots(SetId(set_index)); - } - (peerset, handle) } - fn on_add_reserved_peer(&mut self, set_id: SetId, peer_id: PeerId) { - let newly_inserted = self.reserved_nodes[set_id.0].0.insert(peer_id); - if !newly_inserted { - return - } - - self.data.add_no_slot_node(set_id.0, peer_id); - self.alloc_slots(set_id); - } - - fn on_remove_reserved_peer(&mut self, set_id: SetId, peer_id: PeerId) { - if !self.reserved_nodes[set_id.0].0.remove(&peer_id) { - return - } - - self.data.remove_no_slot_node(set_id.0, &peer_id); - - // Nothing more to do if not in reserved-only mode. - if !self.reserved_nodes[set_id.0].1 { - return - } - - // If, however, the peerset is in reserved-only mode, then the removed node needs to be - // disconnected. - if let peersstate::Peer::Connected(peer) = self.data.peer(set_id.0, &peer_id) { - peer.disconnect(); - self.message_queue.push_back(Message::Drop { set_id, peer_id }); - } - } - - fn on_set_reserved_peers(&mut self, set_id: SetId, peer_ids: HashSet) { - // Determine the difference between the current group and the new list. - let (to_insert, to_remove) = { - let to_insert = peer_ids - .difference(&self.reserved_nodes[set_id.0].0) - .cloned() - .collect::>(); - let to_remove = self.reserved_nodes[set_id.0] - .0 - .difference(&peer_ids) - .cloned() - .collect::>(); - (to_insert, to_remove) - }; - - for node in to_insert { - self.on_add_reserved_peer(set_id, node); - } - - for node in to_remove { - self.on_remove_reserved_peer(set_id, node); - } - } - - fn on_set_reserved_only(&mut self, set_id: SetId, reserved_only: bool) { - self.reserved_nodes[set_id.0].1 = reserved_only; - - if reserved_only { - // Disconnect all the nodes that aren't reserved. - for peer_id in - self.data.connected_peers(set_id.0).cloned().collect::>().into_iter() - { - if self.reserved_nodes[set_id.0].0.contains(&peer_id) { - continue - } - - let peer = self.data.peer(set_id.0, &peer_id).into_connected().expect( - "We are enumerating connected peers, therefore the peer is connected; qed", - ); - peer.disconnect(); - self.message_queue.push_back(Message::Drop { set_id, peer_id }); - } - } else { - self.alloc_slots(set_id); - } - } - /// Returns the list of reserved peers. - pub fn reserved_peers(&self, set_id: SetId) -> impl Iterator { - self.reserved_nodes[set_id.0].0.iter() - } - - /// Adds a node to the given set. The peerset will, if possible and not already the case, - /// try to connect to it. - /// - /// > **Note**: This has the same effect as [`PeersetHandle::add_to_peers_set`]. - pub fn add_to_peers_set(&mut self, set_id: SetId, peer_id: PeerId) { - if let peersstate::Peer::Unknown(entry) = self.data.peer(set_id.0, &peer_id) { - entry.discover(); - self.alloc_slots(set_id); - } - } - - fn on_remove_from_peers_set(&mut self, set_id: SetId, peer_id: PeerId) { - // Don't do anything if node is reserved. - if self.reserved_nodes[set_id.0].0.contains(&peer_id) { - return - } - - match self.data.peer(set_id.0, &peer_id) { - peersstate::Peer::Connected(peer) => { - self.message_queue.push_back(Message::Drop { set_id, peer_id: *peer.peer_id() }); - peer.disconnect().forget_peer(); - }, - peersstate::Peer::NotConnected(peer) => { - peer.forget_peer(); - }, - peersstate::Peer::Unknown(_) => {}, - } - } - - fn on_report_peer(&mut self, peer_id: PeerId, change: ReputationChange) { - // We want reputations to be up-to-date before adjusting them. - self.update_time(); - - let mut reputation = self.data.peer_reputation(peer_id); - reputation.add_reputation(change.value); - if reputation.reputation() >= BANNED_THRESHOLD { - trace!(target: "peerset", "Report {}: {:+} to {}. Reason: {}", - peer_id, change.value, reputation.reputation(), change.reason - ); - return - } - - debug!(target: "peerset", "Report {}: {:+} to {}. Reason: {}, Disconnecting", - peer_id, change.value, reputation.reputation(), change.reason - ); - - drop(reputation); - - for set_index in 0..self.data.num_sets() { - if let peersstate::Peer::Connected(peer) = self.data.peer(set_index, &peer_id) { - let peer = peer.disconnect(); - self.message_queue.push_back(Message::Drop { - set_id: SetId(set_index), - peer_id: peer.into_peer_id(), - }); - - self.alloc_slots(SetId(set_index)); - } - } - } - - fn on_peer_reputation(&mut self, peer_id: PeerId, pending_response: oneshot::Sender) { - let reputation = self.data.peer_reputation(peer_id); - let _ = pending_response.send(reputation.reputation()); - } - - /// Updates the value of `self.latest_time_update` and performs all the updates that happen - /// over time, such as reputation increases for staying connected. - fn update_time(&mut self) { - let now = Instant::now(); - - // We basically do `(now - self.latest_update).as_secs()`, except that by the way we do it - // we know that we're not going to miss seconds because of rounding to integers. - let secs_diff = { - let elapsed_latest = self.latest_time_update - self.created; - let elapsed_now = now - self.created; - self.latest_time_update = now; - elapsed_now.as_secs() - elapsed_latest.as_secs() - }; - - // For each elapsed second, move the node reputation towards zero. - // If we multiply each second the reputation by `k` (where `k` is between 0 and 1), it - // takes `ln(0.5) / ln(k)` seconds to reduce the reputation by half. Use this formula to - // empirically determine a value of `k` that looks correct. - for _ in 0..secs_diff { - for peer_id in self.data.peers().cloned().collect::>() { - // We use `k = 0.98`, so we divide by `50`. With that value, it takes 34.3 seconds - // to reduce the reputation by half. - fn reput_tick(reput: i32) -> i32 { - let mut diff = reput / 50; - if diff == 0 && reput < 0 { - diff = -1; - } else if diff == 0 && reput > 0 { - diff = 1; - } - reput.saturating_sub(diff) - } - - let mut peer_reputation = self.data.peer_reputation(peer_id); - - let before = peer_reputation.reputation(); - let after = reput_tick(before); - trace!(target: "peerset", "Fleeting {}: {} -> {}", peer_id, before, after); - peer_reputation.set_reputation(after); - - if after != 0 { - continue - } - - drop(peer_reputation); - - // If the peer reaches a reputation of 0, and there is no connection to it, - // forget it. - for set_index in 0..self.data.num_sets() { - match self.data.peer(set_index, &peer_id) { - peersstate::Peer::Connected(_) => {}, - peersstate::Peer::NotConnected(peer) => { - if peer.last_connected_or_discovered() + FORGET_AFTER < now { - peer.forget_peer(); - } - }, - peersstate::Peer::Unknown(_) => { - // Happens if this peer does not belong to this set. - }, - } - } - } - } - } - - /// Try to fill available out slots with nodes for the given set. - fn alloc_slots(&mut self, set_id: SetId) { - self.update_time(); - - // Try to connect to all the reserved nodes that we are not connected to. - for reserved_node in &self.reserved_nodes[set_id.0].0 { - let entry = match self.data.peer(set_id.0, reserved_node) { - peersstate::Peer::Unknown(n) => n.discover(), - peersstate::Peer::NotConnected(n) => n, - peersstate::Peer::Connected(_) => continue, - }; - - // Don't connect to nodes with an abysmal reputation, even if they're reserved. - // This is a rather opinionated behaviour, and it wouldn't be fundamentally wrong to - // remove that check. If necessary, the peerset should be refactored to give more - // control over what happens in that situation. - if entry.reputation() < BANNED_THRESHOLD { - break - } - - match entry.try_outgoing() { - Ok(conn) => self - .message_queue - .push_back(Message::Connect { set_id, peer_id: conn.into_peer_id() }), - Err(_) => { - // An error is returned only if no slot is available. Reserved nodes are - // marked in the state machine with a flag saying "doesn't occupy a slot", - // and as such this should never happen. - debug_assert!(false); - log::error!( - target: "peerset", - "Not enough slots to connect to reserved node" - ); - }, - } - } - - // Now, we try to connect to other nodes. - - // Nothing more to do if we're in reserved mode. - if self.reserved_nodes[set_id.0].1 { - return - } - - // Try to grab the next node to attempt to connect to. - // Since `highest_not_connected_peer` is rather expensive to call, check beforehand - // whether we have an available slot. - while self.data.has_free_outgoing_slot(set_id.0) { - let next = match self.data.highest_not_connected_peer(set_id.0) { - Some(n) => n, - None => break, - }; - - // Don't connect to nodes with an abysmal reputation. - if next.reputation() < BANNED_THRESHOLD { - break - } - - match next.try_outgoing() { - Ok(conn) => self - .message_queue - .push_back(Message::Connect { set_id, peer_id: conn.into_peer_id() }), - Err(_) => { - // This branch can only be entered if there is no free slot, which is - // checked above. - debug_assert!(false); - break - }, - } - } + pub fn reserved_peers(&self, set_id: SetId, pending_response: oneshot::Sender>) { + self.protocol_handles[set_id.0].reserved_peers(pending_response); } /// Indicate that we received an incoming connection. Must be answered either with @@ -615,62 +327,15 @@ impl Peerset { // message to the output channel with a `PeerId`, and that `incoming` gets called with the same // `PeerId` before that message has been read by the user. In this situation we must not answer. pub fn incoming(&mut self, set_id: SetId, peer_id: PeerId, index: IncomingIndex) { - trace!(target: "peerset", "Incoming {:?}", peer_id); - - self.update_time(); - - if self.reserved_nodes[set_id.0].1 && !self.reserved_nodes[set_id.0].0.contains(&peer_id) { - self.message_queue.push_back(Message::Reject(index)); - return - } - - let not_connected = match self.data.peer(set_id.0, &peer_id) { - // If we're already connected, don't answer, as the docs mention. - peersstate::Peer::Connected(_) => return, - peersstate::Peer::NotConnected(mut entry) => { - entry.bump_last_connected_or_discovered(); - entry - }, - peersstate::Peer::Unknown(entry) => entry.discover(), - }; - - if not_connected.reputation() < BANNED_THRESHOLD { - self.message_queue.push_back(Message::Reject(index)); - return - } - - match not_connected.try_accept_incoming() { - Ok(_) => self.message_queue.push_back(Message::Accept(index)), - Err(_) => self.message_queue.push_back(Message::Reject(index)), - } + self.protocol_handles[set_id.0].incoming_connection(peer_id, index); } /// Indicate that we dropped an active connection with a peer, or that we failed to connect. /// /// Must only be called after the PSM has either generated a `Connect` message with this /// `PeerId`, or accepted an incoming connection with this `PeerId`. - pub fn dropped(&mut self, set_id: SetId, peer_id: PeerId, reason: DropReason) { - // We want reputations to be up-to-date before adjusting them. - self.update_time(); - - match self.data.peer(set_id.0, &peer_id) { - peersstate::Peer::Connected(mut entry) => { - // Decrease the node's reputation so that we don't try it again and again and again. - entry.add_reputation(DISCONNECT_REPUTATION_CHANGE); - trace!(target: "peerset", "Dropping {}: {:+} to {}", - peer_id, DISCONNECT_REPUTATION_CHANGE, entry.reputation()); - entry.disconnect(); - }, - peersstate::Peer::NotConnected(_) | peersstate::Peer::Unknown(_) => { - error!(target: "peerset", "Received dropped() for non-connected node") - }, - } - - if let DropReason::Refused = reason { - self.on_remove_from_peers_set(set_id, peer_id); - } - - self.alloc_slots(set_id); + pub fn dropped(&mut self, set_id: SetId, peer_id: PeerId, _reason: DropReason) { + self.protocol_handles[set_id.0].dropped(peer_id); } /// Reports an adjustment to the reputation of the given peer. @@ -678,44 +343,19 @@ impl Peerset { // We don't immediately perform the adjustments in order to have state consistency. We // don't want the reporting here to take priority over messages sent using the // `PeersetHandle`. - let _ = self.tx.unbounded_send(Action::ReportPeer(peer_id, score_diff)); + let _ = self.to_self.unbounded_send(Action::ReportPeer(peer_id, score_diff)); } /// Produces a JSON object containing the state of the peerset manager, for debugging purposes. pub fn debug_info(&mut self) -> serde_json::Value { - self.update_time(); - - json!({ - "sets": (0..self.data.num_sets()).map(|set_index| { - json!({ - "nodes": self.data.peers().cloned().collect::>().into_iter().filter_map(|peer_id| { - let state = match self.data.peer(set_index, &peer_id) { - peersstate::Peer::Connected(entry) => json!({ - "connected": true, - "reputation": entry.reputation() - }), - peersstate::Peer::NotConnected(entry) => json!({ - "connected": false, - "reputation": entry.reputation() - }), - peersstate::Peer::Unknown(_) => return None, - }; - - Some((peer_id.to_base58(), state)) - }).collect::>(), - "reserved_nodes": self.reserved_nodes[set_index].0.iter().map(|peer_id| { - peer_id.to_base58() - }).collect::>(), - "reserved_only": self.reserved_nodes[set_index].1, - }) - }).collect::>(), - "message_queue": self.message_queue.len(), - }) + // TODO: Check what info we can include here. + // Issue reference: https://github.com/paritytech/substrate/issues/14160. + json!("unimplemented") } /// Returns the number of peers that we have discovered. pub fn num_discovered_peers(&self) -> usize { - self.data.peers().len() + self.peer_store_handle.num_known_peers() } } @@ -723,277 +363,66 @@ impl Stream for Peerset { type Item = Message; fn poll_next(mut self: Pin<&mut Self>, cx: &mut Context) -> Poll> { - loop { - if let Some(message) = self.message_queue.pop_front() { - return Poll::Ready(Some(message)) + if let Poll::Ready(msg) = self.from_controllers.poll_next_unpin(cx) { + if let Some(msg) = msg { + return Poll::Ready(Some(msg)) + } else { + debug!( + target: LOG_TARGET, + "All `ProtocolController`s have terminated, terminating `Peerset`." + ); + return Poll::Ready(None) } + } - if Future::poll(Pin::new(&mut self.next_periodic_alloc_slots), cx).is_ready() { - self.next_periodic_alloc_slots = Delay::new(Duration::new(1, 0)); - - for set_index in 0..self.data.num_sets() { - self.alloc_slots(SetId(set_index)); + while let Poll::Ready(action) = self.from_handle.poll_next_unpin(cx) { + if let Some(action) = action { + match action { + Action::AddReservedPeer(set_id, peer_id) => + self.protocol_handles[set_id.0].add_reserved_peer(peer_id), + Action::RemoveReservedPeer(set_id, peer_id) => + self.protocol_handles[set_id.0].remove_reserved_peer(peer_id), + Action::SetReservedPeers(set_id, peer_ids) => + self.protocol_handles[set_id.0].set_reserved_peers(peer_ids), + Action::SetReservedOnly(set_id, reserved_only) => + self.protocol_handles[set_id.0].set_reserved_only(reserved_only), + Action::ReportPeer(peer_id, score_diff) => + self.peer_store_handle.report_peer(peer_id, score_diff), + Action::AddKnownPeer(peer_id) => self.peer_store_handle.add_known_peer(peer_id), + Action::PeerReputation(peer_id, pending_response) => { + let _ = + pending_response.send(self.peer_store_handle.peer_reputation(&peer_id)); + }, } + } else { + debug!(target: LOG_TARGET, "`PeersetHandle` was dropped, terminating `Peerset`."); + return Poll::Ready(None) } + } - let action = match Stream::poll_next(Pin::new(&mut self.rx), cx) { - Poll::Pending => return Poll::Pending, - Poll::Ready(Some(event)) => event, - Poll::Ready(None) => return Poll::Pending, - }; - - match action { - Action::AddReservedPeer(set_id, peer_id) => - self.on_add_reserved_peer(set_id, peer_id), - Action::RemoveReservedPeer(set_id, peer_id) => - self.on_remove_reserved_peer(set_id, peer_id), - Action::SetReservedPeers(set_id, peer_ids) => - self.on_set_reserved_peers(set_id, peer_ids), - Action::SetReservedOnly(set_id, reserved) => - self.on_set_reserved_only(set_id, reserved), - Action::ReportPeer(peer_id, score_diff) => self.on_report_peer(peer_id, score_diff), - Action::AddToPeersSet(sets_name, peer_id) => - self.add_to_peers_set(sets_name, peer_id), - Action::RemoveFromPeersSet(sets_name, peer_id) => - self.on_remove_from_peers_set(sets_name, peer_id), - Action::PeerReputation(peer_id, pending_response) => - self.on_peer_reputation(peer_id, pending_response), - } + if let Poll::Ready(()) = self.peer_store_future.poll_unpin(cx) { + debug!(target: LOG_TARGET, "`PeerStore` has terminated, terminating `PeerSet`."); + return Poll::Ready(None) + } + + if let Poll::Ready(_) = self.protocol_controller_futures.poll_unpin(cx) { + debug!( + target: LOG_TARGET, + "All `ProtocolHandle`s have terminated, terminating `PeerSet`." + ); + return Poll::Ready(None) } + + Poll::Pending } } /// Reason for calling [`Peerset::dropped`]. +#[derive(Debug)] pub enum DropReason { /// Substream or connection has been closed for an unknown reason. Unknown, /// Substream or connection has been explicitly refused by the target. In other words, the /// peer doesn't actually belong to this set. - /// - /// This has the side effect of calling [`PeersetHandle::remove_from_peers_set`]. Refused, } - -#[cfg(test)] -mod tests { - use super::{ - IncomingIndex, Message, Peerset, PeersetConfig, ReputationChange, SetConfig, SetId, - BANNED_THRESHOLD, - }; - use futures::prelude::*; - use libp2p_identity::PeerId; - use std::{pin::Pin, task::Poll, thread, time::Duration}; - - fn assert_messages(mut peerset: Peerset, messages: Vec) -> Peerset { - for expected_message in messages { - let (message, p) = next_message(peerset).expect("expected message"); - assert_eq!(message, expected_message); - peerset = p; - } - peerset - } - - fn next_message(mut peerset: Peerset) -> Result<(Message, Peerset), ()> { - let next = futures::executor::block_on_stream(&mut peerset).next(); - let message = next.ok_or(())?; - Ok((message, peerset)) - } - - #[test] - fn test_peerset_add_reserved_peer() { - let bootnode = PeerId::random(); - let reserved_peer = PeerId::random(); - let reserved_peer2 = PeerId::random(); - let config = PeersetConfig { - sets: vec![SetConfig { - in_peers: 0, - out_peers: 2, - bootnodes: vec![bootnode], - reserved_nodes: Default::default(), - reserved_only: true, - }], - }; - - let (peerset, handle) = Peerset::from_config(config); - handle.add_reserved_peer(SetId::from(0), reserved_peer); - handle.add_reserved_peer(SetId::from(0), reserved_peer2); - - assert_messages( - peerset, - vec![ - Message::Connect { set_id: SetId::from(0), peer_id: reserved_peer }, - Message::Connect { set_id: SetId::from(0), peer_id: reserved_peer2 }, - ], - ); - } - - #[test] - fn test_peerset_incoming() { - let bootnode = PeerId::random(); - let incoming = PeerId::random(); - let incoming2 = PeerId::random(); - let incoming3 = PeerId::random(); - let ii = IncomingIndex(1); - let ii2 = IncomingIndex(2); - let ii3 = IncomingIndex(3); - let ii4 = IncomingIndex(3); - let config = PeersetConfig { - sets: vec![SetConfig { - in_peers: 2, - out_peers: 1, - bootnodes: vec![bootnode], - reserved_nodes: Default::default(), - reserved_only: false, - }], - }; - - let (mut peerset, _handle) = Peerset::from_config(config); - peerset.incoming(SetId::from(0), incoming, ii); - peerset.incoming(SetId::from(0), incoming, ii4); - peerset.incoming(SetId::from(0), incoming2, ii2); - peerset.incoming(SetId::from(0), incoming3, ii3); - - assert_messages( - peerset, - vec![ - Message::Connect { set_id: SetId::from(0), peer_id: bootnode }, - Message::Accept(ii), - Message::Accept(ii2), - Message::Reject(ii3), - ], - ); - } - - #[test] - fn test_peerset_reject_incoming_in_reserved_only() { - let incoming = PeerId::random(); - let ii = IncomingIndex(1); - let config = PeersetConfig { - sets: vec![SetConfig { - in_peers: 50, - out_peers: 50, - bootnodes: vec![], - reserved_nodes: Default::default(), - reserved_only: true, - }], - }; - - let (mut peerset, _) = Peerset::from_config(config); - peerset.incoming(SetId::from(0), incoming, ii); - - assert_messages(peerset, vec![Message::Reject(ii)]); - } - - #[test] - fn test_peerset_discovered() { - let bootnode = PeerId::random(); - let discovered = PeerId::random(); - let discovered2 = PeerId::random(); - let config = PeersetConfig { - sets: vec![SetConfig { - in_peers: 0, - out_peers: 2, - bootnodes: vec![bootnode], - reserved_nodes: Default::default(), - reserved_only: false, - }], - }; - - let (mut peerset, _handle) = Peerset::from_config(config); - peerset.add_to_peers_set(SetId::from(0), discovered); - peerset.add_to_peers_set(SetId::from(0), discovered); - peerset.add_to_peers_set(SetId::from(0), discovered2); - - assert_messages( - peerset, - vec![ - Message::Connect { set_id: SetId::from(0), peer_id: bootnode }, - Message::Connect { set_id: SetId::from(0), peer_id: discovered }, - ], - ); - } - - #[test] - fn test_peerset_banned() { - let (mut peerset, handle) = Peerset::from_config(PeersetConfig { - sets: vec![SetConfig { - in_peers: 25, - out_peers: 25, - bootnodes: vec![], - reserved_nodes: Default::default(), - reserved_only: false, - }], - }); - - // We ban a node by setting its reputation under the threshold. - let peer_id = PeerId::random(); - handle.report_peer(peer_id, ReputationChange::new(BANNED_THRESHOLD - 1, "")); - - let fut = futures::future::poll_fn(move |cx| { - // We need one polling for the message to be processed. - assert_eq!(Stream::poll_next(Pin::new(&mut peerset), cx), Poll::Pending); - - // Check that an incoming connection from that node gets refused. - peerset.incoming(SetId::from(0), peer_id, IncomingIndex(1)); - if let Poll::Ready(msg) = Stream::poll_next(Pin::new(&mut peerset), cx) { - assert_eq!(msg.unwrap(), Message::Reject(IncomingIndex(1))); - } else { - panic!() - } - - // Wait a bit for the node's reputation to go above the threshold. - thread::sleep(Duration::from_millis(1500)); - - // Try again. This time the node should be accepted. - peerset.incoming(SetId::from(0), peer_id, IncomingIndex(2)); - while let Poll::Ready(msg) = Stream::poll_next(Pin::new(&mut peerset), cx) { - assert_eq!(msg.unwrap(), Message::Accept(IncomingIndex(2))); - } - - Poll::Ready(()) - }); - - futures::executor::block_on(fut); - } - - #[test] - fn test_relloc_after_banned() { - let (mut peerset, handle) = Peerset::from_config(PeersetConfig { - sets: vec![SetConfig { - in_peers: 25, - out_peers: 25, - bootnodes: vec![], - reserved_nodes: Default::default(), - reserved_only: false, - }], - }); - - // We ban a node by setting its reputation under the threshold. - let peer_id = PeerId::random(); - handle.report_peer(peer_id, ReputationChange::new(BANNED_THRESHOLD - 1, "")); - - let fut = futures::future::poll_fn(move |cx| { - // We need one polling for the message to be processed. - assert_eq!(Stream::poll_next(Pin::new(&mut peerset), cx), Poll::Pending); - - // Check that an incoming connection from that node gets refused. - // This is already tested in other tests, but it is done again here because it doesn't - // hurt. - peerset.incoming(SetId::from(0), peer_id, IncomingIndex(1)); - if let Poll::Ready(msg) = Stream::poll_next(Pin::new(&mut peerset), cx) { - assert_eq!(msg.unwrap(), Message::Reject(IncomingIndex(1))); - } else { - panic!() - } - - // Wait for the peerset to change its mind and actually connect to it. - while let Poll::Ready(msg) = Stream::poll_next(Pin::new(&mut peerset), cx) { - assert_eq!(msg.unwrap(), Message::Connect { set_id: SetId::from(0), peer_id }); - } - - Poll::Ready(()) - }); - - futures::executor::block_on(fut); - } -} diff --git a/client/peerset/src/peer_store.rs b/client/peerset/src/peer_store.rs new file mode 100644 index 000000000..425511821 --- /dev/null +++ b/client/peerset/src/peer_store.rs @@ -0,0 +1,403 @@ +// This file is part of Substrate. + +// Copyright (C) Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +use libp2p_identity::PeerId; +use log::trace; +use parking_lot::Mutex; +use partial_sort::PartialSort; +use std::{ + cmp::{Ord, Ordering, PartialOrd}, + collections::{hash_map::Entry, HashMap, HashSet}, + fmt::Debug, + sync::Arc, + time::{Duration, Instant}, +}; +use wasm_timer::Delay; + +use crate::{protocol_controller::ProtocolHandle, ReputationChange, LOG_TARGET}; + +/// We don't accept nodes whose reputation is under this value. +pub const BANNED_THRESHOLD: i32 = 82 * (i32::MIN / 100); +/// Reputation change for a node when we get disconnected from it. +const DISCONNECT_REPUTATION_CHANGE: i32 = -256; +/// Relative decrement of a reputation value that is applied every second. I.e., for inverse +/// decrement of 50 we decrease absolute value of the reputation by 1/50. This corresponds to a +/// factor of `k = 0.98`. It takes ~ `ln(0.5) / ln(k)` seconds to reduce the reputation by half, +/// or 34.3 seconds for the values above. In this setup the maximum allowed absolute value of +/// `i32::MAX` becomes 0 in ~1100 seconds (actually less due to integer arithmetic). +const INVERSE_DECREMENT: i32 = 50; +/// Amount of time between the moment we last updated the [`PeerStore`] entry and the moment we +/// remove it, once the reputation value reaches 0. +const FORGET_AFTER: Duration = Duration::from_secs(3600); + +pub trait PeerStoreProvider: Debug + Send { + /// Check whether the peer is banned. + fn is_banned(&self, peer_id: &PeerId) -> bool; + + /// Register a protocol handle to disconnect peers whose reputation drops below the threshold. + fn register_protocol(&self, protocol_handle: ProtocolHandle); + + /// Report peer disconnection for reputation adjustment. + fn report_disconnect(&mut self, peer_id: PeerId); + + /// Adjust peer reputation. + fn report_peer(&mut self, peer_id: PeerId, change: ReputationChange); + + /// Get peer reputation. + fn peer_reputation(&self, peer_id: &PeerId) -> i32; + + /// Get candidates with highest reputations for initiating outgoing connections. + fn outgoing_candidates(&self, count: usize, ignored: HashSet<&PeerId>) -> Vec; +} + +#[derive(Debug, Clone)] +pub struct PeerStoreHandle { + inner: Arc>, +} + +impl PeerStoreProvider for PeerStoreHandle { + fn is_banned(&self, peer_id: &PeerId) -> bool { + self.inner.lock().is_banned(peer_id) + } + + fn register_protocol(&self, protocol_handle: ProtocolHandle) { + self.inner.lock().register_protocol(protocol_handle); + } + + fn report_disconnect(&mut self, peer_id: PeerId) { + self.inner.lock().report_disconnect(peer_id) + } + + fn report_peer(&mut self, peer_id: PeerId, change: ReputationChange) { + self.inner.lock().report_peer(peer_id, change) + } + + fn peer_reputation(&self, peer_id: &PeerId) -> i32 { + self.inner.lock().peer_reputation(peer_id) + } + + fn outgoing_candidates(&self, count: usize, ignored: HashSet<&PeerId>) -> Vec { + self.inner.lock().outgoing_candidates(count, ignored) + } +} + +impl PeerStoreHandle { + /// Get the number of known peers. + /// + /// This number might not include some connected peers in rare cases when their reputation + /// was not updated for one hour, because their entries in [`PeerStore`] were dropped. + pub fn num_known_peers(&self) -> usize { + self.inner.lock().peers.len() + } + + /// Add known peer. + pub fn add_known_peer(&mut self, peer_id: PeerId) { + self.inner.lock().add_known_peer(peer_id); + } +} + +#[derive(Debug, Clone, Copy)] +struct PeerInfo { + reputation: i32, + last_updated: Instant, +} + +impl Default for PeerInfo { + fn default() -> Self { + Self { reputation: 0, last_updated: Instant::now() } + } +} + +impl PartialEq for PeerInfo { + fn eq(&self, other: &Self) -> bool { + self.reputation == other.reputation + } +} + +impl Eq for PeerInfo {} + +impl Ord for PeerInfo { + // We define reverse order by reputation values. + fn cmp(&self, other: &Self) -> Ordering { + self.reputation.cmp(&other.reputation).reverse() + } +} + +impl PartialOrd for PeerInfo { + fn partial_cmp(&self, other: &Self) -> Option { + Some(self.cmp(other)) + } +} + +impl PeerInfo { + fn is_banned(&self) -> bool { + self.reputation < BANNED_THRESHOLD + } + + fn add_reputation(&mut self, increment: i32) { + self.reputation = self.reputation.saturating_add(increment); + self.bump_last_updated(); + } + + fn decay_reputation(&mut self, seconds_passed: u64) { + // Note that decaying the reputation value happens "on its own", + // so we don't do `bump_last_updated()`. + for _ in 0..seconds_passed { + let mut diff = self.reputation / INVERSE_DECREMENT; + if diff == 0 && self.reputation < 0 { + diff = -1; + } else if diff == 0 && self.reputation > 0 { + diff = 1; + } + + self.reputation = self.reputation.saturating_sub(diff); + + if self.reputation == 0 { + break + } + } + } + + fn bump_last_updated(&mut self) { + self.last_updated = Instant::now(); + } +} + +#[derive(Debug)] +struct PeerStoreInner { + peers: HashMap, + protocols: Vec, +} + +impl PeerStoreInner { + fn is_banned(&self, peer_id: &PeerId) -> bool { + self.peers.get(peer_id).map_or(false, |info| info.is_banned()) + } + + fn register_protocol(&mut self, protocol_handle: ProtocolHandle) { + self.protocols.push(protocol_handle); + } + + fn report_disconnect(&mut self, peer_id: PeerId) { + let peer_info = self.peers.entry(peer_id).or_default(); + peer_info.add_reputation(DISCONNECT_REPUTATION_CHANGE); + + log::trace!( + target: LOG_TARGET, + "Peer {} disconnected, reputation: {:+} to {}", + peer_id, + DISCONNECT_REPUTATION_CHANGE, + peer_info.reputation, + ); + } + + fn report_peer(&mut self, peer_id: PeerId, change: ReputationChange) { + let peer_info = self.peers.entry(peer_id).or_default(); + peer_info.add_reputation(change.value); + + if peer_info.reputation < BANNED_THRESHOLD { + self.protocols.iter().for_each(|handle| handle.disconnect_peer(peer_id)); + + log::trace!( + target: LOG_TARGET, + "Report {}: {:+} to {}. Reason: {}. Banned, disconnecting.", + peer_id, + change.value, + peer_info.reputation, + change.reason, + ); + } else { + log::trace!( + target: LOG_TARGET, + "Report {}: {:+} to {}. Reason: {}.", + peer_id, + change.value, + peer_info.reputation, + change.reason, + ); + } + } + + fn peer_reputation(&self, peer_id: &PeerId) -> i32 { + self.peers.get(peer_id).map_or(0, |info| info.reputation) + } + + fn outgoing_candidates(&self, count: usize, ignored: HashSet<&PeerId>) -> Vec { + let mut candidates = self + .peers + .iter() + .filter_map(|(peer_id, info)| { + (!info.is_banned() && !ignored.contains(peer_id)).then_some((*peer_id, *info)) + }) + .collect::>(); + let count = std::cmp::min(count, candidates.len()); + candidates.partial_sort(count, |(_, info1), (_, info2)| info1.cmp(info2)); + candidates.iter().take(count).map(|(peer_id, _)| *peer_id).collect() + + // TODO: keep the peers sorted (in a "bi-multi-map"?) to not repeat sorting every time. + } + + fn progress_time(&mut self, seconds_passed: u64) { + if seconds_passed == 0 { + return + } + + // Drive reputation values towards 0. + self.peers + .iter_mut() + .for_each(|(_, info)| info.decay_reputation(seconds_passed)); + + // Retain only entries with non-zero reputation values or not expired ones. + let now = Instant::now(); + self.peers + .retain(|_, info| info.reputation != 0 || info.last_updated + FORGET_AFTER > now); + } + + fn add_known_peer(&mut self, peer_id: PeerId) { + match self.peers.entry(peer_id) { + Entry::Occupied(mut e) => { + trace!( + target: LOG_TARGET, + "Trying to add an already known peer {peer_id}, bumping `last_updated`.", + ); + e.get_mut().bump_last_updated(); + }, + Entry::Vacant(e) => { + trace!(target: LOG_TARGET, "Adding a new known peer {peer_id}."); + e.insert(PeerInfo::default()); + }, + } + } +} + +#[derive(Debug)] +pub struct PeerStore { + inner: Arc>, +} + +impl PeerStore { + /// Create a new peer store from the list of bootnodes. + pub fn new(bootnodes: Vec) -> Self { + PeerStore { + inner: Arc::new(Mutex::new(PeerStoreInner { + peers: bootnodes + .into_iter() + .map(|peer_id| (peer_id, PeerInfo::default())) + .collect(), + protocols: Vec::new(), + })), + } + } + + /// Get `PeerStoreHandle`. + pub fn handle(&self) -> PeerStoreHandle { + PeerStoreHandle { inner: self.inner.clone() } + } + + /// Drive the `PeerStore`, decaying reputation values over time and removing expired entries. + pub async fn run(self) { + let started = Instant::now(); + let mut latest_time_update = started; + + loop { + let now = Instant::now(); + // We basically do `(now - self.latest_update).as_secs()`, except that by the way we do + // it we know that we're not going to miss seconds because of rounding to integers. + let seconds_passed = { + let elapsed_latest = latest_time_update - started; + let elapsed_now = now - started; + latest_time_update = now; + elapsed_now.as_secs() - elapsed_latest.as_secs() + }; + + self.inner.lock().progress_time(seconds_passed); + let _ = Delay::new(Duration::from_secs(1)).await; + } + } +} + +#[cfg(test)] +mod tests { + use super::PeerInfo; + + #[test] + fn decaying_zero_reputation_yields_zero() { + let mut peer_info = PeerInfo::default(); + assert_eq!(peer_info.reputation, 0); + + peer_info.decay_reputation(1); + assert_eq!(peer_info.reputation, 0); + + peer_info.decay_reputation(100_000); + assert_eq!(peer_info.reputation, 0); + } + + #[test] + fn decaying_positive_reputation_decreases_it() { + const INITIAL_REPUTATION: i32 = 100; + + let mut peer_info = PeerInfo::default(); + peer_info.reputation = INITIAL_REPUTATION; + + peer_info.decay_reputation(1); + assert!(peer_info.reputation >= 0); + assert!(peer_info.reputation < INITIAL_REPUTATION); + } + + #[test] + fn decaying_negative_reputation_increases_it() { + const INITIAL_REPUTATION: i32 = -100; + + let mut peer_info = PeerInfo::default(); + peer_info.reputation = INITIAL_REPUTATION; + + peer_info.decay_reputation(1); + assert!(peer_info.reputation <= 0); + assert!(peer_info.reputation > INITIAL_REPUTATION); + } + + #[test] + fn decaying_max_reputation_finally_yields_zero() { + const INITIAL_REPUTATION: i32 = i32::MAX; + const SECONDS: u64 = 1000; + + let mut peer_info = PeerInfo::default(); + peer_info.reputation = INITIAL_REPUTATION; + + peer_info.decay_reputation(SECONDS / 2); + assert!(peer_info.reputation > 0); + + peer_info.decay_reputation(SECONDS / 2); + assert_eq!(peer_info.reputation, 0); + } + + #[test] + fn decaying_min_reputation_finally_yields_zero() { + const INITIAL_REPUTATION: i32 = i32::MIN; + const SECONDS: u64 = 1000; + + let mut peer_info = PeerInfo::default(); + peer_info.reputation = INITIAL_REPUTATION; + + peer_info.decay_reputation(SECONDS / 2); + assert!(peer_info.reputation < 0); + + peer_info.decay_reputation(SECONDS / 2); + assert_eq!(peer_info.reputation, 0); + } +} diff --git a/client/peerset/src/peersstate.rs b/client/peerset/src/peersstate.rs deleted file mode 100644 index 2d4a9295c..000000000 --- a/client/peerset/src/peersstate.rs +++ /dev/null @@ -1,737 +0,0 @@ -// This file is part of Substrate. - -// Copyright (C) Parity Technologies (UK) Ltd. -// SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 - -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with this program. If not, see . - -//! Reputation and slots allocation system behind the peerset. -//! -//! The [`PeersState`] state machine is responsible for managing the reputation and allocating -//! slots. It holds a list of nodes, each associated with a reputation value, a list of sets the -//! node belongs to, and for each set whether we are connected or not to this node. Thanks to this -//! list, it knows how many slots are occupied. It also holds a list of nodes which don't occupy -//! slots. -//! -//! > Note: This module is purely dedicated to managing slots and reputations. Features such as -//! > for example connecting to some nodes in priority should be added outside of this -//! > module, rather than inside. - -use libp2p_identity::PeerId; -use log::error; -use std::{ - borrow::Cow, - collections::{ - hash_map::{Entry, OccupiedEntry}, - HashMap, HashSet, - }, - time::Instant, -}; - -/// State storage behind the peerset. -/// -/// # Usage -/// -/// This struct is nothing more but a data structure containing a list of nodes, where each node -/// has a reputation and is either connected to us or not. -#[derive(Debug, Clone)] -pub struct PeersState { - /// List of nodes that we know about. - /// - /// > **Note**: This list should really be ordered by decreasing reputation, so that we can - /// > easily select the best node to connect to. As a first draft, however, we don't sort, to - /// > make the logic easier. - nodes: HashMap, - - /// Configuration of each set. The size of this `Vec` is never modified. - sets: Vec, -} - -/// Configuration of a single set. -#[derive(Debug, Copy, Clone, PartialEq, Eq)] -pub struct SetConfig { - /// Maximum allowed number of slot-occupying nodes for ingoing connections. - pub in_peers: u32, - - /// Maximum allowed number of slot-occupying nodes for outgoing connections. - pub out_peers: u32, -} - -/// State of a single set. -#[derive(Debug, Clone, PartialEq, Eq)] -struct SetInfo { - /// Number of slot-occupying nodes for which the `MembershipState` is `In`. - num_in: u32, - - /// Number of slot-occupying nodes for which the `MembershipState` is `In`. - num_out: u32, - - /// Maximum allowed number of slot-occupying nodes for which the `MembershipState` is `In`. - max_in: u32, - - /// Maximum allowed number of slot-occupying nodes for which the `MembershipState` is `Out`. - max_out: u32, - - /// List of node identities (discovered or not) that don't occupy slots. - /// - /// Note for future readers: this module is purely dedicated to managing slots. If you are - /// considering adding more features, please consider doing so outside of this module rather - /// than inside. - no_slot_nodes: HashSet, -} - -/// State of a single node that we know about. -#[derive(Debug, Clone, PartialEq, Eq)] -struct Node { - /// List of sets the node belongs to. - /// Always has a fixed size equal to the one of [`PeersState::set`]. The various possible sets - /// are indices into this `Vec`. - sets: Vec, - - /// Reputation value of the node, between `i32::MIN` (we hate that node) and - /// `i32::MAX` (we love that node). - reputation: i32, -} - -impl Node { - fn new(num_sets: usize) -> Self { - Self { sets: (0..num_sets).map(|_| MembershipState::NotMember).collect(), reputation: 0 } - } -} - -/// Whether we are connected to a node in the context of a specific set. -#[derive(Debug, Copy, Clone, PartialEq, Eq)] -enum MembershipState { - /// Node isn't part of that set. - NotMember, - /// We are connected through an ingoing connection. - In, - /// We are connected through an outgoing connection. - Out, - /// Node is part of that set, but we are not connected to it. - NotConnected { - /// When we were last connected to the node, or if we were never connected when we - /// discovered it. - last_connected: Instant, - }, -} - -impl MembershipState { - /// Returns `true` for [`MembershipState::In`] and [`MembershipState::Out`]. - fn is_connected(self) -> bool { - match self { - Self::In | Self::Out => true, - Self::NotMember | Self::NotConnected { .. } => false, - } - } - - /// Returns `true` for [`MembershipState::NotConnected`]. - fn is_not_connected(self) -> bool { - matches!(self, Self::NotConnected { .. }) - } -} - -impl PeersState { - /// Builds a new empty [`PeersState`]. - pub fn new(sets: impl IntoIterator) -> Self { - Self { - nodes: HashMap::new(), - sets: sets - .into_iter() - .map(|config| SetInfo { - num_in: 0, - num_out: 0, - max_in: config.in_peers, - max_out: config.out_peers, - no_slot_nodes: HashSet::new(), - }) - .collect(), - } - } - - /// Returns the number of sets. - /// - /// Corresponds to the number of elements passed to [`PeersState::new`]. - pub fn num_sets(&self) -> usize { - self.sets.len() - } - - /// Returns an object that grants access to the reputation value of a peer. - pub fn peer_reputation(&mut self, peer_id: PeerId) -> Reputation { - self.nodes.entry(peer_id).or_insert_with(|| Node::new(self.sets.len())); - - let entry = match self.nodes.entry(peer_id) { - Entry::Vacant(_) => unreachable!("guaranteed to be inserted above; qed"), - Entry::Occupied(e) => e, - }; - - Reputation { node: Some(entry) } - } - - /// Returns an object that grants access to the state of a peer in the context of a specific - /// set. - /// - /// # Panic - /// - /// `set` must be within range of the sets passed to [`PeersState::new`]. - pub fn peer<'a>(&'a mut self, set: usize, peer_id: &'a PeerId) -> Peer<'a> { - // The code below will panic anyway if this happens to be false, but this earlier assert - // makes it explicit what is wrong. - assert!(set < self.sets.len()); - - match self.nodes.get_mut(peer_id).map(|p| &p.sets[set]) { - None | Some(MembershipState::NotMember) => - Peer::Unknown(UnknownPeer { parent: self, set, peer_id: Cow::Borrowed(peer_id) }), - Some(MembershipState::In) | Some(MembershipState::Out) => - Peer::Connected(ConnectedPeer { state: self, set, peer_id: Cow::Borrowed(peer_id) }), - Some(MembershipState::NotConnected { .. }) => Peer::NotConnected(NotConnectedPeer { - state: self, - set, - peer_id: Cow::Borrowed(peer_id), - }), - } - } - - /// Returns the list of all the peers we know of. - // Note: this method could theoretically return a `Peer`, but implementing that - // isn't simple. - pub fn peers(&self) -> impl ExactSizeIterator { - self.nodes.keys() - } - - /// Returns the list of peers we are connected to in the context of a specific set. - /// - /// # Panic - /// - /// `set` must be within range of the sets passed to [`PeersState::new`]. - // Note: this method could theoretically return a `ConnectedPeer`, but implementing that - // isn't simple. - pub fn connected_peers(&self, set: usize) -> impl Iterator { - // The code below will panic anyway if this happens to be false, but this earlier assert - // makes it explicit what is wrong. - assert!(set < self.sets.len()); - - self.nodes - .iter() - .filter(move |(_, p)| p.sets[set].is_connected()) - .map(|(p, _)| p) - } - - /// Returns the peer with the highest reputation and that we are not connected to. - /// - /// If multiple nodes have the same reputation, which one is returned is unspecified. - /// - /// # Panic - /// - /// `set` must be within range of the sets passed to [`PeersState::new`]. - pub fn highest_not_connected_peer(&mut self, set: usize) -> Option { - // The code below will panic anyway if this happens to be false, but this earlier assert - // makes it explicit what is wrong. - assert!(set < self.sets.len()); - - let outcome = self - .nodes - .iter_mut() - .filter(|(_, Node { sets, .. })| sets[set].is_not_connected()) - .fold(None::<(&PeerId, &mut Node)>, |mut cur_node, to_try| { - if let Some(cur_node) = cur_node.take() { - if cur_node.1.reputation >= to_try.1.reputation { - return Some(cur_node) - } - } - Some(to_try) - }) - .map(|(peer_id, _)| *peer_id); - - outcome.map(move |peer_id| NotConnectedPeer { - state: self, - set, - peer_id: Cow::Owned(peer_id), - }) - } - - /// Returns `true` if there is a free outgoing slot available related to this set. - pub fn has_free_outgoing_slot(&self, set: usize) -> bool { - self.sets[set].num_out < self.sets[set].max_out - } - - /// Add a node to the list of nodes that don't occupy slots. - /// - /// Has no effect if the node was already in the group. - pub fn add_no_slot_node(&mut self, set: usize, peer_id: PeerId) { - // Reminder: `HashSet::insert` returns false if the node was already in the set - if !self.sets[set].no_slot_nodes.insert(peer_id) { - return - } - - if let Some(peer) = self.nodes.get_mut(&peer_id) { - match peer.sets[set] { - MembershipState::In => self.sets[set].num_in -= 1, - MembershipState::Out => self.sets[set].num_out -= 1, - MembershipState::NotConnected { .. } | MembershipState::NotMember => {}, - } - } - } - - /// Removes a node from the list of nodes that don't occupy slots. - /// - /// Has no effect if the node was not in the group. - pub fn remove_no_slot_node(&mut self, set: usize, peer_id: &PeerId) { - // Reminder: `HashSet::remove` returns false if the node was already not in the set - if !self.sets[set].no_slot_nodes.remove(peer_id) { - return - } - - if let Some(peer) = self.nodes.get_mut(peer_id) { - match peer.sets[set] { - MembershipState::In => self.sets[set].num_in += 1, - MembershipState::Out => self.sets[set].num_out += 1, - MembershipState::NotConnected { .. } | MembershipState::NotMember => {}, - } - } - } -} - -/// Grants access to the state of a peer in the [`PeersState`] in the context of a specific set. -pub enum Peer<'a> { - /// We are connected to this node. - Connected(ConnectedPeer<'a>), - /// We are not connected to this node. - NotConnected(NotConnectedPeer<'a>), - /// We have never heard of this node, or it is not part of the set. - Unknown(UnknownPeer<'a>), -} - -impl<'a> Peer<'a> { - /// If we are the `Connected` variant, returns the inner [`ConnectedPeer`]. Returns `None` - /// otherwise. - pub fn into_connected(self) -> Option> { - match self { - Self::Connected(peer) => Some(peer), - Self::NotConnected(..) | Self::Unknown(..) => None, - } - } - - /// If we are the `NotConnected` variant, returns the inner [`NotConnectedPeer`]. Returns `None` - /// otherwise. - #[cfg(test)] // Feel free to remove this if this function is needed outside of tests - pub fn into_not_connected(self) -> Option> { - match self { - Self::NotConnected(peer) => Some(peer), - Self::Connected(..) | Self::Unknown(..) => None, - } - } - - /// If we are the `Unknown` variant, returns the inner [`UnknownPeer`]. Returns `None` - /// otherwise. - #[cfg(test)] // Feel free to remove this if this function is needed outside of tests - pub fn into_unknown(self) -> Option> { - match self { - Self::Unknown(peer) => Some(peer), - Self::Connected(..) | Self::NotConnected(..) => None, - } - } -} - -/// A peer that is connected to us. -pub struct ConnectedPeer<'a> { - state: &'a mut PeersState, - set: usize, - peer_id: Cow<'a, PeerId>, -} - -impl<'a> ConnectedPeer<'a> { - /// Get the `PeerId` associated to this `ConnectedPeer`. - pub fn peer_id(&self) -> &PeerId { - &self.peer_id - } - - /// Destroys this `ConnectedPeer` and returns the `PeerId` inside of it. - pub fn into_peer_id(self) -> PeerId { - self.peer_id.into_owned() - } - - /// Switches the peer to "not connected". - pub fn disconnect(self) -> NotConnectedPeer<'a> { - let is_no_slot_occupy = self.state.sets[self.set].no_slot_nodes.contains(&*self.peer_id); - if let Some(node) = self.state.nodes.get_mut(&*self.peer_id) { - if !is_no_slot_occupy { - match node.sets[self.set] { - MembershipState::In => self.state.sets[self.set].num_in -= 1, - MembershipState::Out => self.state.sets[self.set].num_out -= 1, - MembershipState::NotMember | MembershipState::NotConnected { .. } => { - debug_assert!( - false, - "State inconsistency: disconnecting a disconnected node" - ) - }, - } - } - node.sets[self.set] = MembershipState::NotConnected { last_connected: Instant::now() }; - } else { - debug_assert!(false, "State inconsistency: disconnecting a disconnected node"); - } - - NotConnectedPeer { state: self.state, set: self.set, peer_id: self.peer_id } - } - - /// Performs an arithmetic addition on the reputation score of that peer. - /// - /// In case of overflow, the value will be capped. - /// - /// > **Note**: Reputation values aren't specific to a set but are global per peer. - pub fn add_reputation(&mut self, modifier: i32) { - if let Some(node) = self.state.nodes.get_mut(&*self.peer_id) { - node.reputation = node.reputation.saturating_add(modifier); - } else { - debug_assert!(false, "State inconsistency: add_reputation on an unknown node"); - } - } - - /// Returns the reputation value of the node. - /// - /// > **Note**: Reputation values aren't specific to a set but are global per peer. - pub fn reputation(&self) -> i32 { - self.state.nodes.get(&*self.peer_id).map_or(0, |p| p.reputation) - } -} - -/// A peer that is not connected to us. -#[derive(Debug)] -pub struct NotConnectedPeer<'a> { - state: &'a mut PeersState, - set: usize, - peer_id: Cow<'a, PeerId>, -} - -impl<'a> NotConnectedPeer<'a> { - /// Destroys this `NotConnectedPeer` and returns the `PeerId` inside of it. - pub fn into_peer_id(self) -> PeerId { - self.peer_id.into_owned() - } - - /// Bumps the value that `last_connected_or_discovered` would return to now, even if we - /// didn't connect or disconnect. - pub fn bump_last_connected_or_discovered(&mut self) { - let state = match self.state.nodes.get_mut(&*self.peer_id) { - Some(s) => s, - None => return, - }; - - if let MembershipState::NotConnected { last_connected } = &mut state.sets[self.set] { - *last_connected = Instant::now(); - } - } - - /// Returns when we were last connected to this peer, or when we discovered it if we were - /// never connected. - /// - /// Guaranteed to be earlier than calling `Instant::now()` after the function returns. - pub fn last_connected_or_discovered(&self) -> Instant { - let state = match self.state.nodes.get(&*self.peer_id) { - Some(s) => s, - None => { - error!( - target: "peerset", - "State inconsistency with {}; not connected after borrow", - self.peer_id - ); - return Instant::now() - }, - }; - - match state.sets[self.set] { - MembershipState::NotConnected { last_connected } => last_connected, - _ => { - error!(target: "peerset", "State inconsistency with {}", self.peer_id); - Instant::now() - }, - } - } - - /// Tries to set the peer as connected as an outgoing connection. - /// - /// If there are enough slots available, switches the node to "connected" and returns `Ok`. If - /// the slots are full, the node stays "not connected" and we return `Err`. - /// - /// Non-slot-occupying nodes don't count towards the number of slots. - pub fn try_outgoing(self) -> Result, Self> { - let is_no_slot_occupy = self.state.sets[self.set].no_slot_nodes.contains(&*self.peer_id); - - // Note that it is possible for num_out to be strictly superior to the max, in case we were - // connected to reserved node then marked them as not reserved. - if !self.state.has_free_outgoing_slot(self.set) && !is_no_slot_occupy { - return Err(self) - } - - if let Some(peer) = self.state.nodes.get_mut(&*self.peer_id) { - peer.sets[self.set] = MembershipState::Out; - if !is_no_slot_occupy { - self.state.sets[self.set].num_out += 1; - } - } else { - debug_assert!(false, "State inconsistency: try_outgoing on an unknown node"); - } - - Ok(ConnectedPeer { state: self.state, set: self.set, peer_id: self.peer_id }) - } - - /// Tries to accept the peer as an incoming connection. - /// - /// If there are enough slots available, switches the node to "connected" and returns `Ok`. If - /// the slots are full, the node stays "not connected" and we return `Err`. - /// - /// Non-slot-occupying nodes don't count towards the number of slots. - pub fn try_accept_incoming(self) -> Result, Self> { - let is_no_slot_occupy = self.state.sets[self.set].no_slot_nodes.contains(&*self.peer_id); - - // Note that it is possible for num_in to be strictly superior to the max, in case we were - // connected to reserved node then marked them as not reserved. - if self.state.sets[self.set].num_in >= self.state.sets[self.set].max_in && - !is_no_slot_occupy - { - return Err(self) - } - - if let Some(peer) = self.state.nodes.get_mut(&*self.peer_id) { - peer.sets[self.set] = MembershipState::In; - if !is_no_slot_occupy { - self.state.sets[self.set].num_in += 1; - } - } else { - debug_assert!(false, "State inconsistency: try_accept_incoming on an unknown node"); - } - - Ok(ConnectedPeer { state: self.state, set: self.set, peer_id: self.peer_id }) - } - - /// Returns the reputation value of the node. - /// - /// > **Note**: Reputation values aren't specific to a set but are global per peer. - pub fn reputation(&self) -> i32 { - self.state.nodes.get(&*self.peer_id).map_or(0, |p| p.reputation) - } - - /// Sets the reputation of the peer. - /// - /// > **Note**: Reputation values aren't specific to a set but are global per peer. - #[cfg(test)] // Feel free to remove this if this function is needed outside of tests - pub fn set_reputation(&mut self, value: i32) { - if let Some(node) = self.state.nodes.get_mut(&*self.peer_id) { - node.reputation = value; - } else { - debug_assert!(false, "State inconsistency: set_reputation on an unknown node"); - } - } - - /// Removes the peer from the list of members of the set. - pub fn forget_peer(self) -> UnknownPeer<'a> { - if let Some(peer) = self.state.nodes.get_mut(&*self.peer_id) { - debug_assert!(!matches!(peer.sets[self.set], MembershipState::NotMember)); - peer.sets[self.set] = MembershipState::NotMember; - - // Remove the peer from `self.state.nodes` entirely if it isn't a member of any set. - if peer.reputation == 0 && - peer.sets.iter().all(|set| matches!(set, MembershipState::NotMember)) - { - self.state.nodes.remove(&*self.peer_id); - } - } else { - debug_assert!(false, "State inconsistency: forget_peer on an unknown node"); - error!( - target: "peerset", - "State inconsistency with {} when forgetting peer", - self.peer_id - ); - }; - - UnknownPeer { parent: self.state, set: self.set, peer_id: self.peer_id } - } -} - -/// A peer that we have never heard of or that isn't part of the set. -pub struct UnknownPeer<'a> { - parent: &'a mut PeersState, - set: usize, - peer_id: Cow<'a, PeerId>, -} - -impl<'a> UnknownPeer<'a> { - /// Inserts the peer identity in our list. - /// - /// The node starts with a reputation of 0. You can adjust these default - /// values using the `NotConnectedPeer` that this method returns. - pub fn discover(self) -> NotConnectedPeer<'a> { - let num_sets = self.parent.sets.len(); - - self.parent - .nodes - .entry(self.peer_id.clone().into_owned()) - .or_insert_with(|| Node::new(num_sets)) - .sets[self.set] = MembershipState::NotConnected { last_connected: Instant::now() }; - - NotConnectedPeer { state: self.parent, set: self.set, peer_id: self.peer_id } - } -} - -/// Access to the reputation of a peer. -pub struct Reputation<'a> { - /// Node entry in [`PeersState::nodes`]. Always `Some` except right before dropping. - node: Option>, -} - -impl<'a> Reputation<'a> { - /// Returns the reputation value of the node. - pub fn reputation(&self) -> i32 { - self.node.as_ref().unwrap().get().reputation - } - - /// Sets the reputation of the peer. - pub fn set_reputation(&mut self, value: i32) { - self.node.as_mut().unwrap().get_mut().reputation = value; - } - - /// Performs an arithmetic addition on the reputation score of that peer. - /// - /// In case of overflow, the value will be capped. - pub fn add_reputation(&mut self, modifier: i32) { - let reputation = &mut self.node.as_mut().unwrap().get_mut().reputation; - *reputation = reputation.saturating_add(modifier); - } -} - -impl<'a> Drop for Reputation<'a> { - fn drop(&mut self) { - if let Some(node) = self.node.take() { - if node.get().reputation == 0 && - node.get().sets.iter().all(|set| matches!(set, MembershipState::NotMember)) - { - node.remove(); - } - } - } -} - -#[cfg(test)] -mod tests { - use super::{Peer, PeersState, SetConfig}; - use libp2p_identity::PeerId; - use std::iter; - - #[test] - fn full_slots_in() { - let mut peers_state = PeersState::new(iter::once(SetConfig { in_peers: 1, out_peers: 1 })); - let id1 = PeerId::random(); - let id2 = PeerId::random(); - - if let Peer::Unknown(e) = peers_state.peer(0, &id1) { - assert!(e.discover().try_accept_incoming().is_ok()); - } - - if let Peer::Unknown(e) = peers_state.peer(0, &id2) { - assert!(e.discover().try_accept_incoming().is_err()); - } - } - - #[test] - fn no_slot_node_doesnt_use_slot() { - let mut peers_state = PeersState::new(iter::once(SetConfig { in_peers: 1, out_peers: 1 })); - let id1 = PeerId::random(); - let id2 = PeerId::random(); - - peers_state.add_no_slot_node(0, id1); - if let Peer::Unknown(p) = peers_state.peer(0, &id1) { - assert!(p.discover().try_accept_incoming().is_ok()); - } else { - panic!() - } - - if let Peer::Unknown(e) = peers_state.peer(0, &id2) { - assert!(e.discover().try_accept_incoming().is_ok()); - } else { - panic!() - } - } - - #[test] - fn disconnecting_frees_slot() { - let mut peers_state = PeersState::new(iter::once(SetConfig { in_peers: 1, out_peers: 1 })); - let id1 = PeerId::random(); - let id2 = PeerId::random(); - - assert!(peers_state - .peer(0, &id1) - .into_unknown() - .unwrap() - .discover() - .try_accept_incoming() - .is_ok()); - assert!(peers_state - .peer(0, &id2) - .into_unknown() - .unwrap() - .discover() - .try_accept_incoming() - .is_err()); - peers_state.peer(0, &id1).into_connected().unwrap().disconnect(); - assert!(peers_state - .peer(0, &id2) - .into_not_connected() - .unwrap() - .try_accept_incoming() - .is_ok()); - } - - #[test] - fn highest_not_connected_peer() { - let mut peers_state = - PeersState::new(iter::once(SetConfig { in_peers: 25, out_peers: 25 })); - let id1 = PeerId::random(); - let id2 = PeerId::random(); - - assert!(peers_state.highest_not_connected_peer(0).is_none()); - peers_state.peer(0, &id1).into_unknown().unwrap().discover().set_reputation(50); - peers_state.peer(0, &id2).into_unknown().unwrap().discover().set_reputation(25); - assert_eq!(peers_state.highest_not_connected_peer(0).map(|p| p.into_peer_id()), Some(id1)); - peers_state.peer(0, &id2).into_not_connected().unwrap().set_reputation(75); - assert_eq!(peers_state.highest_not_connected_peer(0).map(|p| p.into_peer_id()), Some(id2)); - peers_state - .peer(0, &id2) - .into_not_connected() - .unwrap() - .try_accept_incoming() - .unwrap(); - assert_eq!(peers_state.highest_not_connected_peer(0).map(|p| p.into_peer_id()), Some(id1)); - peers_state.peer(0, &id1).into_not_connected().unwrap().set_reputation(100); - peers_state.peer(0, &id2).into_connected().unwrap().disconnect(); - assert_eq!(peers_state.highest_not_connected_peer(0).map(|p| p.into_peer_id()), Some(id1)); - peers_state.peer(0, &id1).into_not_connected().unwrap().set_reputation(-100); - assert_eq!(peers_state.highest_not_connected_peer(0).map(|p| p.into_peer_id()), Some(id2)); - } - - #[test] - fn disconnect_no_slot_doesnt_panic() { - let mut peers_state = PeersState::new(iter::once(SetConfig { in_peers: 1, out_peers: 1 })); - let id = PeerId::random(); - peers_state.add_no_slot_node(0, id); - let peer = peers_state - .peer(0, &id) - .into_unknown() - .unwrap() - .discover() - .try_outgoing() - .unwrap(); - peer.disconnect(); - } -} diff --git a/client/peerset/src/protocol_controller.rs b/client/peerset/src/protocol_controller.rs new file mode 100644 index 000000000..ce3961f13 --- /dev/null +++ b/client/peerset/src/protocol_controller.rs @@ -0,0 +1,1939 @@ +// This file is part of Substrate. + +// Copyright (C) Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +//! Protocol Controller. Generic implementation of peer management for protocols. +//! Responsible for accepting/rejecting incoming connections and initiating outgoing connections, +//! respecting the inbound and outbound peer slot counts. Communicates with `PeerStore` to get and +//! update peer reputation values and sends commands to `Notifications`. +//! +//! Due to asynchronous nature of communication between `ProtocolController` and `Notifications`, +//! `ProtocolController` has an imperfect view of the states of the peers. To reduce this +//! desynchronization, the following measures are taken: +//! +//! 1. Network peer events from `Notifications` are prioritized over actions from external API and +//! internal actions by `ProtocolController` (like slot allocation). +//! 2. `Notifications` ignores all commands from `ProtocolController` after sending "incoming" +//! request until receiving the answer to this "incoming" request. +//! 3. After sending a "connect" message, `ProtocolController` switches the state of the peer from +//! `Outbound` to `Inbound` if it receives an "incoming" request from `Notifications` for this +//! peer. +//! +//! These measures do not eliminate confusing commands from `ProtocolController` completely, +//! so `Notifications` must correctly handle seemingly inconsistent commands, like a "connect" +//! command for the peer it thinks is already connected, and a "drop" command for a peer that +//! was previously dropped. +//! +//! Even though this does not guarantee that `ProtocolController` and `Notifications` have the same +//! view of the peers' states at any given moment, the eventual consistency is maintained. + +use futures::{channel::oneshot, future::Either, FutureExt, StreamExt}; +use libp2p_identity::PeerId; +use log::{error, trace, warn}; +use sc_utils::mpsc::{tracing_unbounded, TracingUnboundedReceiver, TracingUnboundedSender}; +use sp_arithmetic::traits::SaturatedConversion; +use std::{ + collections::{HashMap, HashSet}, + time::{Duration, Instant}, +}; +use wasm_timer::Delay; + +use crate::{peer_store::PeerStoreProvider, IncomingIndex, Message, SetConfig, SetId, LOG_TARGET}; + +/// External API actions. +#[derive(Debug)] +enum Action { + /// Add a reserved peer or mark already connected peer as reserved. + AddReservedPeer(PeerId), + /// Remove a reserved peer. + RemoveReservedPeer(PeerId), + /// Update reserved peers to match the provided set. + SetReservedPeers(HashSet), + /// Set/unset reserved-only mode. + SetReservedOnly(bool), + /// Disconnect a peer. + DisconnectPeer(PeerId), + /// Get the list of reserved peers. + GetReservedPeers(oneshot::Sender>), +} + +/// Network events from `Notifications`. +#[derive(Debug)] +enum Event { + /// Incoming connection from the peer. + IncomingConnection(PeerId, IncomingIndex), + /// Connection with the peer dropped. + Dropped(PeerId), +} + +/// Shared handle to [`ProtocolController`]. Distributed around the code outside of the +/// protocol implementation. +#[derive(Debug, Clone)] +pub struct ProtocolHandle { + /// Actions from outer API. + actions_tx: TracingUnboundedSender, + /// Connection events from `Notifications`. We prioritize them over actions. + events_tx: TracingUnboundedSender, +} + +impl ProtocolHandle { + /// Adds a new reserved peer. [`ProtocolController`] will make an effort + /// to always remain connected to this peer. + /// + /// Has no effect if the node was already a reserved peer. + /// + /// > **Note**: Keep in mind that the networking has to know an address for this node, + /// > otherwise it will not be able to connect to it. + pub fn add_reserved_peer(&self, peer_id: PeerId) { + let _ = self.actions_tx.unbounded_send(Action::AddReservedPeer(peer_id)); + } + + /// Demotes reserved peer to non-reserved. Does not disconnect the peer. + /// + /// Has no effect if the node was not a reserved peer. + pub fn remove_reserved_peer(&self, peer_id: PeerId) { + let _ = self.actions_tx.unbounded_send(Action::RemoveReservedPeer(peer_id)); + } + + /// Set reserved peers to the new set. + pub fn set_reserved_peers(&self, peer_ids: HashSet) { + let _ = self.actions_tx.unbounded_send(Action::SetReservedPeers(peer_ids)); + } + + /// Sets whether or not [`ProtocolController`] only has connections with nodes marked + /// as reserved for the given set. + pub fn set_reserved_only(&self, reserved: bool) { + let _ = self.actions_tx.unbounded_send(Action::SetReservedOnly(reserved)); + } + + /// Disconnect peer. You should remove the `PeerId` from the `PeerStore` first + /// to not connect to the peer again during the next slot allocation. + pub fn disconnect_peer(&self, peer_id: PeerId) { + let _ = self.actions_tx.unbounded_send(Action::DisconnectPeer(peer_id)); + } + + /// Get the list of reserved peers. + pub fn reserved_peers(&self, pending_response: oneshot::Sender>) { + let _ = self.actions_tx.unbounded_send(Action::GetReservedPeers(pending_response)); + } + + /// Notify about incoming connection. [`ProtocolController`] will either accept or reject it. + pub fn incoming_connection(&self, peer_id: PeerId, incoming_index: IncomingIndex) { + let _ = self + .events_tx + .unbounded_send(Event::IncomingConnection(peer_id, incoming_index)); + } + + /// Notify that connection was dropped (either refused or disconnected). + pub fn dropped(&self, peer_id: PeerId) { + let _ = self.events_tx.unbounded_send(Event::Dropped(peer_id)); + } +} + +/// Direction of a connection +#[derive(Clone, Copy, Debug)] +enum Direction { + Inbound, + Outbound, +} + +/// Status of a connection with a peer. +#[derive(Clone, Debug)] +enum PeerState { + /// We are connected to the peer. + Connected(Direction), + /// We are not connected. + NotConnected, +} + +impl PeerState { + /// Returns true if we are connected with the node. + fn is_connected(&self) -> bool { + matches!(self, PeerState::Connected(_)) + } +} + +impl Default for PeerState { + fn default() -> PeerState { + PeerState::NotConnected + } +} + +/// Side of [`ProtocolHandle`] responsible for all the logic. Currently all instances are +/// owned by [`crate::Peerset`], but they should eventually be moved to corresponding protocols. +#[derive(Debug)] +pub struct ProtocolController { + /// Set id to use when sending connect/drop requests to `Notifications`. + // Will likely be replaced by `ProtocolName` in the future. + set_id: SetId, + /// Receiver for outer API messages from [`ProtocolHandle`]. + actions_rx: TracingUnboundedReceiver, + /// Receiver for connection events from `Notifications` sent via [`ProtocolHandle`]. + events_rx: TracingUnboundedReceiver, + /// Number of occupied slots for incoming connections (not counting reserved nodes). + num_in: u32, + /// Number of occupied slots for outgoing connections (not counting reserved nodes). + num_out: u32, + /// Maximum number of slots for incoming connections (not counting reserved nodes). + max_in: u32, + /// Maximum number of slots for outgoing connections (not counting reserved nodes). + max_out: u32, + /// Connected regular nodes. + nodes: HashMap, + /// Reserved nodes. Should be always connected and do not occupy peer slots. + reserved_nodes: HashMap, + /// Connect only to reserved nodes. + reserved_only: bool, + /// Next time to allocate slots. This is done once per second. + next_periodic_alloc_slots: Instant, + /// Outgoing channel for messages to `Notifications`. + to_notifications: TracingUnboundedSender, + /// `PeerStore` handle for checking peer reputation values and getting connection candidates + /// with highest reputation. + peer_store: Box, +} + +impl ProtocolController { + /// Construct new [`ProtocolController`]. + pub fn new( + set_id: SetId, + config: SetConfig, + to_notifications: TracingUnboundedSender, + peer_store: Box, + ) -> (ProtocolHandle, ProtocolController) { + let (actions_tx, actions_rx) = tracing_unbounded("mpsc_api_protocol", 10_000); + let (events_tx, events_rx) = tracing_unbounded("mpsc_notifications_protocol", 10_000); + let handle = ProtocolHandle { actions_tx, events_tx }; + peer_store.register_protocol(handle.clone()); + let reserved_nodes = + config.reserved_nodes.iter().map(|p| (*p, PeerState::NotConnected)).collect(); + let controller = ProtocolController { + set_id, + actions_rx, + events_rx, + num_in: 0, + num_out: 0, + max_in: config.in_peers, + max_out: config.out_peers, + nodes: HashMap::new(), + reserved_nodes, + reserved_only: config.reserved_only, + next_periodic_alloc_slots: Instant::now(), + to_notifications, + peer_store, + }; + (handle, controller) + } + + /// Drive [`ProtocolController`]. This function returns when all instances of + /// [`ProtocolHandle`] are dropped. + pub async fn run(mut self) { + while self.next_action().await {} + } + + /// Perform one action. Returns `true` if it should be called again. + /// + /// Intended for tests only. Use `run` for driving [`ProtocolController`]. + pub async fn next_action(&mut self) -> bool { + let either = loop { + let mut next_alloc_slots = Delay::new_at(self.next_periodic_alloc_slots).fuse(); + + // See the module doc for why we use `select_biased!`. + futures::select_biased! { + event = self.events_rx.next() => match event { + Some(event) => break Either::Left(event), + None => return false, + }, + action = self.actions_rx.next() => match action { + Some(action) => break Either::Right(action), + None => return false, + }, + _ = next_alloc_slots => { + self.alloc_slots(); + self.next_periodic_alloc_slots = Instant::now() + Duration::new(1, 0); + }, + } + }; + + match either { + Either::Left(event) => self.process_event(event), + Either::Right(action) => self.process_action(action), + } + + true + } + + /// Process connection event. + fn process_event(&mut self, event: Event) { + match event { + Event::IncomingConnection(peer_id, index) => + self.on_incoming_connection(peer_id, index), + Event::Dropped(peer_id) => self.on_peer_dropped(peer_id), + } + } + + /// Process action command. + fn process_action(&mut self, action: Action) { + match action { + Action::AddReservedPeer(peer_id) => self.on_add_reserved_peer(peer_id), + Action::RemoveReservedPeer(peer_id) => self.on_remove_reserved_peer(peer_id), + Action::SetReservedPeers(peer_ids) => self.on_set_reserved_peers(peer_ids), + Action::SetReservedOnly(reserved_only) => self.on_set_reserved_only(reserved_only), + Action::DisconnectPeer(peer_id) => self.on_disconnect_peer(peer_id), + Action::GetReservedPeers(pending_response) => + self.on_get_reserved_peers(pending_response), + } + } + + /// Send "accept" message to `Notifications`. + fn accept_connection(&mut self, incoming_index: IncomingIndex) { + trace!( + target: LOG_TARGET, + "Accepting {:?} on {:?} ({}/{} num_in/max_in).", + incoming_index, + self.set_id, + self.num_in, + self.max_in, + ); + + let _ = self.to_notifications.unbounded_send(Message::Accept(incoming_index)); + } + + /// Send "reject" message to `Notifications`. + fn reject_connection(&mut self, incoming_index: IncomingIndex) { + trace!( + target: LOG_TARGET, + "Rejecting {:?} on {:?} ({}/{} num_in/max_in).", + incoming_index, + self.set_id, + self.num_in, + self.max_in, + ); + + let _ = self.to_notifications.unbounded_send(Message::Reject(incoming_index)); + } + + /// Send "connect" message to `Notifications`. + fn start_connection(&mut self, peer_id: PeerId) { + trace!( + target: LOG_TARGET, + "Connecting to {} on {:?} ({}/{} num_out/max_out).", + peer_id, + self.set_id, + self.num_out, + self.max_out, + ); + + let _ = self + .to_notifications + .unbounded_send(Message::Connect { set_id: self.set_id, peer_id }); + } + + /// Send "drop" message to `Notifications`. + fn drop_connection(&mut self, peer_id: PeerId) { + trace!( + target: LOG_TARGET, + "Dropping {} on {:?} ({}/{} num_in/max_in, {}/{} num_out/max_out).", + peer_id, + self.set_id, + self.num_in, + self.max_in, + self.num_out, + self.max_out, + ); + + let _ = self + .to_notifications + .unbounded_send(Message::Drop { set_id: self.set_id, peer_id }); + } + + /// Report peer disconnect event to `PeerStore` for it to update peer's reputation accordingly. + /// Should only be called if the remote node disconnected us, not the other way around. + fn report_disconnect(&mut self, peer_id: PeerId) { + self.peer_store.report_disconnect(peer_id); + } + + /// Ask `Peerset` if the peer has a reputation value not sufficent for connection with it. + fn is_banned(&self, peer_id: &PeerId) -> bool { + self.peer_store.is_banned(peer_id) + } + + /// Add the peer to the set of reserved peers. [`ProtocolController`] will try to always + /// maintain connections with such peers. + fn on_add_reserved_peer(&mut self, peer_id: PeerId) { + if self.reserved_nodes.contains_key(&peer_id) { + warn!( + target: LOG_TARGET, + "Trying to add an already reserved node as reserved: {peer_id}.", + ); + return + } + + // Get the peer out of non-reserved peers if it's there. + let state = match self.nodes.remove(&peer_id) { + Some(direction) => { + trace!( + target: LOG_TARGET, + "Marking previously connected node {peer_id} ({direction:?}) as reserved.", + ); + PeerState::Connected(direction) + }, + None => { + trace!(target: LOG_TARGET, "Adding reserved node {peer_id}."); + PeerState::NotConnected + }, + }; + + self.reserved_nodes.insert(peer_id, state.clone()); + + // Discount occupied slots or connect to the node. + match state { + PeerState::Connected(Direction::Inbound) => self.num_in -= 1, + PeerState::Connected(Direction::Outbound) => self.num_out -= 1, + PeerState::NotConnected => self.alloc_slots(), + } + } + + /// Remove the peer from the set of reserved peers. The peer is moved to the set of regular + /// nodes. + fn on_remove_reserved_peer(&mut self, peer_id: PeerId) { + let state = match self.reserved_nodes.remove(&peer_id) { + Some(state) => state, + None => { + warn!(target: LOG_TARGET, "Trying to remove unknown reserved node: {peer_id}."); + return + }, + }; + + if let PeerState::Connected(direction) = state { + if self.reserved_only { + // Disconnect the node. + trace!( + target: LOG_TARGET, + "Disconnecting previously reserved node {} ({:?}) on {:?}.", + peer_id, + direction, + self.set_id, + ); + self.drop_connection(peer_id); + } else { + // Count connections as of regular node. + trace!( + target: LOG_TARGET, + "Making a connected reserved node {} ({:?}) on {:?} a regular one.", + peer_id, + direction, + self.set_id, + ); + + match direction { + Direction::Inbound => self.num_in += 1, + Direction::Outbound => self.num_out += 1, + } + + // Put the node into the list of regular nodes. + let prev = self.nodes.insert(peer_id, direction); + assert!(prev.is_none(), "Corrupted state: reserved node was also non-reserved."); + } + } else { + trace!(target: LOG_TARGET, "Removed disconnected reserved node {peer_id}."); + } + } + + /// Replace the set of reserved peers. + fn on_set_reserved_peers(&mut self, peer_ids: HashSet) { + // Determine the difference between the current group and the new list. + let current = self.reserved_nodes.keys().cloned().collect(); + let to_insert = peer_ids.difference(¤t).cloned().collect::>(); + let to_remove = current.difference(&peer_ids).cloned().collect::>(); + + for node in to_insert { + self.on_add_reserved_peer(node); + } + + for node in to_remove { + self.on_remove_reserved_peer(node); + } + } + + /// Change "reserved only" flag. In "reserved only" mode we connect and accept connections to + /// reserved nodes only. + fn on_set_reserved_only(&mut self, reserved_only: bool) { + trace!(target: LOG_TARGET, "Set reserved only: {reserved_only}"); + + self.reserved_only = reserved_only; + + if !reserved_only { + return self.alloc_slots() + } + + // Disconnect all non-reserved peers. + self.nodes + .iter() + .map(|(k, v)| (*k, *v)) + .collect::>() + .iter() + .for_each(|(peer_id, direction)| { + // Update counters in the loop for `drop_connection` to report the correct number. + match direction { + Direction::Inbound => self.num_in -= 1, + Direction::Outbound => self.num_out -= 1, + } + self.drop_connection(*peer_id) + }); + self.nodes.clear(); + } + + /// Get the list of reserved peers. + fn on_get_reserved_peers(&self, pending_response: oneshot::Sender>) { + let _ = pending_response.send(self.reserved_nodes.keys().cloned().collect()); + } + + /// Disconnect the peer. + fn on_disconnect_peer(&mut self, peer_id: PeerId) { + // Don't do anything if the node is reserved. + if self.reserved_nodes.contains_key(&peer_id) { + warn!( + target: LOG_TARGET, + "Ignoring request to disconnect reserved peer {} from {:?}.", peer_id, self.set_id, + ); + return + } + + match self.nodes.remove(&peer_id) { + Some(direction) => { + trace!(target: LOG_TARGET, "Disconnecting peer {peer_id} ({direction:?})."); + match direction { + Direction::Inbound => self.num_in -= 1, + Direction::Outbound => self.num_out -= 1, + } + self.drop_connection(peer_id); + }, + None => { + warn!( + target: LOG_TARGET, + "Trying to disconnect unknown peer {} from {:?}.", peer_id, self.set_id, + ); + }, + } + } + + /// Indicate that we received an incoming connection. Must be answered either with + /// a corresponding `Accept` or `Reject`, except if we were already connected to this peer. + /// + /// Note that this mechanism is orthogonal to `Connect`/`Drop`. Accepting an incoming + /// connection implicitly means `Connect`, but incoming connections aren't cancelled by + /// `dropped`. + // Implementation note: because of concurrency issues, `ProtocolController` has an imperfect + // view of the peers' states, and may issue commands for a peer after `Notifications` received + // an incoming request for that peer. In this case, `Notifications` ignores all the commands + // until it receives a response for the incoming request to `ProtocolController`, so we must + // ensure we handle this incoming request correctly. + fn on_incoming_connection(&mut self, peer_id: PeerId, incoming_index: IncomingIndex) { + trace!(target: LOG_TARGET, "Incoming connection from peer {peer_id} ({incoming_index:?}).",); + + if self.reserved_only && !self.reserved_nodes.contains_key(&peer_id) { + self.reject_connection(incoming_index); + return + } + + // Check if the node is reserved first. + if let Some(state) = self.reserved_nodes.get_mut(&peer_id) { + match state { + PeerState::Connected(ref mut direction) => { + // We are accepting an incoming connection, so ensure the direction is inbound. + // (See the implementation note above.) + *direction = Direction::Inbound; + self.accept_connection(incoming_index); + }, + PeerState::NotConnected => + if self.peer_store.is_banned(&peer_id) { + self.reject_connection(incoming_index); + } else { + *state = PeerState::Connected(Direction::Inbound); + self.accept_connection(incoming_index); + }, + } + return + } + + // If we're already connected, pretend we are not connected and decide on the node again. + // (See the note above.) + if let Some(direction) = self.nodes.remove(&peer_id) { + trace!( + target: LOG_TARGET, + "Handling incoming connection from peer {} we think we already connected as {:?}.", + peer_id, + direction, + ); + match direction { + Direction::Inbound => self.num_in -= 1, + Direction::Outbound => self.num_out -= 1, + } + } + + if self.num_in >= self.max_in { + self.reject_connection(incoming_index); + return + } + + if self.is_banned(&peer_id) { + self.reject_connection(incoming_index); + return + } + + self.num_in += 1; + self.nodes.insert(peer_id, Direction::Inbound); + self.accept_connection(incoming_index); + } + + /// Indicate that a connection with the peer was dropped. + fn on_peer_dropped(&mut self, peer_id: PeerId) { + self.on_peer_dropped_inner(peer_id).unwrap_or_else(|peer_id| { + // We do not assert here, because due to asynchronous nature of communication + // between `ProtocolController` and `Notifications` we can receive `Action::Dropped` + // for a peer we already disconnected ourself. + trace!( + target: LOG_TARGET, + "Received `Action::Dropped` for not connected peer {} on {:?}.", + peer_id, + self.set_id, + ) + }); + } + + /// Indicate that a connection with the peer was dropped. + /// Returns `Err(PeerId)` if the peer wasn't connected or is not known to us. + fn on_peer_dropped_inner(&mut self, peer_id: PeerId) -> Result<(), PeerId> { + if self.drop_reserved_peer(&peer_id)? || self.drop_regular_peer(&peer_id) { + // The peer found and disconnected. + self.report_disconnect(peer_id); + Ok(()) + } else { + // The peer was not found in neither regular or reserved lists. + Err(peer_id) + } + } + + /// Try dropping the peer as a reserved peer. Return `Ok(true)` if the peer was found and + /// disconnected, `Ok(false)` if it wasn't found, `Err(PeerId)`, if the peer found, but not in + /// connected state. + fn drop_reserved_peer(&mut self, peer_id: &PeerId) -> Result { + let Some(state) = self.reserved_nodes.get_mut(peer_id) else { + return Ok(false) + }; + + if let PeerState::Connected(direction) = state { + trace!(target: LOG_TARGET, "Reserved peer {peer_id} ({direction:?}) dropped."); + *state = PeerState::NotConnected; + Ok(true) + } else { + Err(*peer_id) + } + } + + /// Try dropping the peer as a regular peer. Return `true` if the peer was found and + /// disconnected, `false` if it wasn't found. + fn drop_regular_peer(&mut self, peer_id: &PeerId) -> bool { + let Some(direction) = self.nodes.remove(peer_id) else { + return false + }; + + trace!(target: LOG_TARGET, "Peer {peer_id} ({direction:?}) dropped."); + + match direction { + Direction::Inbound => self.num_in -= 1, + Direction::Outbound => self.num_out -= 1, + } + + true + } + + /// Initiate outgoing connections trying to connect all reserved nodes and fill in all outgoing + /// slots. + fn alloc_slots(&mut self) { + // Try connecting to reserved nodes first, ignoring nodes with outstanding events/actions. + self.reserved_nodes + .iter_mut() + .filter_map(|(peer_id, state)| { + (!state.is_connected() && !self.peer_store.is_banned(peer_id)).then(|| { + *state = PeerState::Connected(Direction::Outbound); + peer_id + }) + }) + .cloned() + .collect::>() + .into_iter() + .for_each(|peer_id| { + self.start_connection(peer_id); + }); + + // Nothing more to do if we're in reserved-only mode or don't have slots available. + if self.reserved_only || self.num_out >= self.max_out { + return + } + + // Fill available slots. + let available_slots = (self.max_out - self.num_out).saturated_into(); + + // Ignore reserved nodes (connected above), already connected nodes, and nodes with + // outstanding events/actions. + let ignored = self + .reserved_nodes + .keys() + .collect::>() + .union(&self.nodes.keys().collect::>()) + .cloned() + .collect(); + + let candidates = self + .peer_store + .outgoing_candidates(available_slots, ignored) + .into_iter() + .filter_map(|peer_id| { + (!self.reserved_nodes.contains_key(&peer_id) && !self.nodes.contains_key(&peer_id)) + .then_some(peer_id) + .or_else(|| { + error!( + target: LOG_TARGET, + "`PeerStore` returned a node we asked to ignore: {peer_id}.", + ); + debug_assert!(false, "`PeerStore` returned a node we asked to ignore."); + None + }) + }) + .collect::>(); + + if candidates.len() > available_slots { + error!( + target: LOG_TARGET, + "`PeerStore` returned more nodes than there are slots available.", + ); + debug_assert!(false, "`PeerStore` returned more nodes than there are slots available."); + } + + candidates.into_iter().take(available_slots).for_each(|peer_id| { + self.num_out += 1; + self.nodes.insert(peer_id, Direction::Outbound); + self.start_connection(peer_id); + }) + } +} + +#[cfg(test)] +mod tests { + use super::{Direction, PeerState, ProtocolController, ProtocolHandle}; + use crate::{ + peer_store::PeerStoreProvider, IncomingIndex, Message, ReputationChange, SetConfig, SetId, + }; + use libp2p_identity::PeerId; + use sc_utils::mpsc::{tracing_unbounded, TryRecvError}; + use std::collections::HashSet; + + mockall::mock! { + #[derive(Debug)] + pub PeerStoreHandle {} + + impl PeerStoreProvider for PeerStoreHandle { + fn is_banned(&self, peer_id: &PeerId) -> bool; + fn register_protocol(&self, protocol_handle: ProtocolHandle); + fn report_disconnect(&mut self, peer_id: PeerId); + fn report_peer(&mut self, peer_id: PeerId, change: ReputationChange); + fn peer_reputation(&self, peer_id: &PeerId) -> i32; + fn outgoing_candidates<'a>(&self, count: usize, ignored: HashSet<&'a PeerId>) -> Vec; + } + } + + #[test] + fn reserved_nodes_are_connected_dropped_and_accepted() { + let reserved1 = PeerId::random(); + let reserved2 = PeerId::random(); + + // Add first reserved node via config. + let config = SetConfig { + in_peers: 0, + out_peers: 0, + bootnodes: Vec::new(), + reserved_nodes: std::iter::once(reserved1).collect(), + reserved_only: true, + }; + let (tx, mut rx) = tracing_unbounded("mpsc_test_to_notifications", 100); + + let mut peer_store = MockPeerStoreHandle::new(); + peer_store.expect_register_protocol().once().return_const(()); + peer_store.expect_is_banned().times(4).return_const(false); + peer_store.expect_report_disconnect().times(2).return_const(()); + + let (_handle, mut controller) = + ProtocolController::new(SetId(0), config, tx, Box::new(peer_store)); + + // Add second reserved node at runtime (this currently calls `alloc_slots` internally). + controller.on_add_reserved_peer(reserved2); + + // Initiate connections (currently, `alloc_slots` is also called internally in + // `on_add_reserved_peer` above). + controller.alloc_slots(); + + let mut messages = Vec::new(); + while let Some(message) = rx.try_recv().ok() { + messages.push(message); + } + assert_eq!(messages.len(), 2); + assert!(messages.contains(&Message::Connect { set_id: SetId(0), peer_id: reserved1 })); + assert!(messages.contains(&Message::Connect { set_id: SetId(0), peer_id: reserved2 })); + + // Reserved peers do not occupy slots. + assert_eq!(controller.num_out, 0); + assert_eq!(controller.num_in, 0); + + // Drop connections to be able to accept reserved nodes. + controller.on_peer_dropped(reserved1); + controller.on_peer_dropped(reserved2); + + // Incoming connection from `reserved1`. + let incoming1 = IncomingIndex(1); + controller.on_incoming_connection(reserved1, incoming1); + assert_eq!(rx.try_recv().unwrap(), Message::Accept(incoming1)); + assert_eq!(rx.try_recv().unwrap_err(), TryRecvError::Empty); + + // Incoming connection from `reserved2`. + let incoming2 = IncomingIndex(2); + controller.on_incoming_connection(reserved2, incoming2); + assert_eq!(rx.try_recv().unwrap(), Message::Accept(incoming2)); + assert_eq!(rx.try_recv().unwrap_err(), TryRecvError::Empty); + + // Reserved peers do not occupy slots. + assert_eq!(controller.num_out, 0); + assert_eq!(controller.num_in, 0); + } + + #[test] + fn banned_reserved_nodes_are_not_connected_and_not_accepted() { + let reserved1 = PeerId::random(); + let reserved2 = PeerId::random(); + + // Add first reserved node via config. + let config = SetConfig { + in_peers: 0, + out_peers: 0, + bootnodes: Vec::new(), + reserved_nodes: std::iter::once(reserved1).collect(), + reserved_only: true, + }; + let (tx, mut rx) = tracing_unbounded("mpsc_test_to_notifications", 100); + + let mut peer_store = MockPeerStoreHandle::new(); + peer_store.expect_register_protocol().once().return_const(()); + peer_store.expect_is_banned().times(6).return_const(true); + + let (_handle, mut controller) = + ProtocolController::new(SetId(0), config, tx, Box::new(peer_store)); + + // Add second reserved node at runtime (this currently calls `alloc_slots` internally). + controller.on_add_reserved_peer(reserved2); + + // Initiate connections. + controller.alloc_slots(); + + // No slots occupied. + assert_eq!(controller.num_out, 0); + assert_eq!(controller.num_in, 0); + + // No commands are generated. + assert_eq!(rx.try_recv().unwrap_err(), TryRecvError::Empty); + + // Incoming connection from `reserved1`. + let incoming1 = IncomingIndex(1); + controller.on_incoming_connection(reserved1, incoming1); + assert_eq!(rx.try_recv().unwrap(), Message::Reject(incoming1)); + assert_eq!(rx.try_recv().unwrap_err(), TryRecvError::Empty); + + // Incoming connection from `reserved2`. + let incoming2 = IncomingIndex(2); + controller.on_incoming_connection(reserved2, incoming2); + assert_eq!(rx.try_recv().unwrap(), Message::Reject(incoming2)); + assert_eq!(rx.try_recv().unwrap_err(), TryRecvError::Empty); + + // No slots occupied. + assert_eq!(controller.num_out, 0); + assert_eq!(controller.num_in, 0); + } + + #[test] + fn we_try_to_reconnect_to_dropped_reserved_nodes() { + let reserved1 = PeerId::random(); + let reserved2 = PeerId::random(); + + // Add first reserved node via config. + let config = SetConfig { + in_peers: 0, + out_peers: 0, + bootnodes: Vec::new(), + reserved_nodes: std::iter::once(reserved1).collect(), + reserved_only: true, + }; + let (tx, mut rx) = tracing_unbounded("mpsc_test_to_notifications", 100); + + let mut peer_store = MockPeerStoreHandle::new(); + peer_store.expect_register_protocol().once().return_const(()); + peer_store.expect_is_banned().times(4).return_const(false); + peer_store.expect_report_disconnect().times(2).return_const(()); + + let (_handle, mut controller) = + ProtocolController::new(SetId(0), config, tx, Box::new(peer_store)); + + // Add second reserved node at runtime (this calls `alloc_slots` internally). + controller.on_add_reserved_peer(reserved2); + + // Initiate connections (actually redundant, see previous comment). + controller.alloc_slots(); + + let mut messages = Vec::new(); + while let Some(message) = rx.try_recv().ok() { + messages.push(message); + } + + assert_eq!(messages.len(), 2); + assert!(messages.contains(&Message::Connect { set_id: SetId(0), peer_id: reserved1 })); + assert!(messages.contains(&Message::Connect { set_id: SetId(0), peer_id: reserved2 })); + + // Drop both reserved nodes. + controller.on_peer_dropped(reserved1); + controller.on_peer_dropped(reserved2); + + // Initiate connections. + controller.alloc_slots(); + + let mut messages = Vec::new(); + while let Some(message) = rx.try_recv().ok() { + messages.push(message); + } + + assert_eq!(messages.len(), 2); + assert!(messages.contains(&Message::Connect { set_id: SetId(0), peer_id: reserved1 })); + assert!(messages.contains(&Message::Connect { set_id: SetId(0), peer_id: reserved2 })); + + // No slots occupied. + assert_eq!(controller.num_out, 0); + assert_eq!(controller.num_in, 0); + } + + #[test] + fn nodes_supplied_by_peer_store_are_connected() { + let peer1 = PeerId::random(); + let peer2 = PeerId::random(); + let candidates = vec![peer1, peer2]; + + let config = SetConfig { + in_peers: 0, + // Less slots than candidates. + out_peers: 2, + bootnodes: Vec::new(), + reserved_nodes: HashSet::new(), + reserved_only: false, + }; + let (tx, mut rx) = tracing_unbounded("mpsc_test_to_notifications", 100); + + let mut peer_store = MockPeerStoreHandle::new(); + peer_store.expect_register_protocol().once().return_const(()); + peer_store.expect_outgoing_candidates().once().return_const(candidates); + + let (_handle, mut controller) = + ProtocolController::new(SetId(0), config, tx, Box::new(peer_store)); + + // Initiate connections. + controller.alloc_slots(); + + let mut messages = Vec::new(); + while let Some(message) = rx.try_recv().ok() { + messages.push(message); + } + + // Only first two peers are connected (we only have 2 slots). + assert_eq!(messages.len(), 2); + assert!(messages.contains(&Message::Connect { set_id: SetId(0), peer_id: peer1 })); + assert!(messages.contains(&Message::Connect { set_id: SetId(0), peer_id: peer2 })); + + // Outgoing slots occupied. + assert_eq!(controller.num_out, 2); + assert_eq!(controller.num_in, 0); + + // No more nodes are connected. + controller.alloc_slots(); + assert_eq!(rx.try_recv().unwrap_err(), TryRecvError::Empty); + + // No more slots occupied. + assert_eq!(controller.num_out, 2); + assert_eq!(controller.num_in, 0); + } + + #[test] + fn both_reserved_nodes_and_nodes_supplied_by_peer_store_are_connected() { + let reserved1 = PeerId::random(); + let reserved2 = PeerId::random(); + let regular1 = PeerId::random(); + let regular2 = PeerId::random(); + let outgoing_candidates = vec![regular1, regular2]; + let reserved_nodes = [reserved1, reserved2].iter().cloned().collect(); + + let config = SetConfig { + in_peers: 10, + out_peers: 10, + bootnodes: Vec::new(), + reserved_nodes, + reserved_only: false, + }; + let (tx, mut rx) = tracing_unbounded("mpsc_test_to_notifications", 100); + + let mut peer_store = MockPeerStoreHandle::new(); + peer_store.expect_register_protocol().once().return_const(()); + peer_store.expect_is_banned().times(2).return_const(false); + peer_store.expect_outgoing_candidates().once().return_const(outgoing_candidates); + + let (_handle, mut controller) = + ProtocolController::new(SetId(0), config, tx, Box::new(peer_store)); + + // Initiate connections. + controller.alloc_slots(); + + let mut messages = Vec::new(); + while let Some(message) = rx.try_recv().ok() { + messages.push(message); + } + assert_eq!(messages.len(), 4); + assert!(messages.contains(&Message::Connect { set_id: SetId(0), peer_id: reserved1 })); + assert!(messages.contains(&Message::Connect { set_id: SetId(0), peer_id: reserved2 })); + assert!(messages.contains(&Message::Connect { set_id: SetId(0), peer_id: regular1 })); + assert!(messages.contains(&Message::Connect { set_id: SetId(0), peer_id: regular2 })); + assert_eq!(controller.num_out, 2); + assert_eq!(controller.num_in, 0); + } + + #[test] + fn if_slots_are_freed_we_try_to_allocate_them_again() { + let peer1 = PeerId::random(); + let peer2 = PeerId::random(); + let peer3 = PeerId::random(); + let candidates1 = vec![peer1, peer2]; + let candidates2 = vec![peer3]; + + let config = SetConfig { + in_peers: 0, + // Less slots than candidates. + out_peers: 2, + bootnodes: Vec::new(), + reserved_nodes: HashSet::new(), + reserved_only: false, + }; + let (tx, mut rx) = tracing_unbounded("mpsc_test_to_notifications", 100); + + let mut peer_store = MockPeerStoreHandle::new(); + peer_store.expect_register_protocol().once().return_const(()); + peer_store.expect_outgoing_candidates().once().return_const(candidates1); + peer_store.expect_outgoing_candidates().once().return_const(candidates2); + peer_store.expect_report_disconnect().times(2).return_const(()); + + let (_handle, mut controller) = + ProtocolController::new(SetId(0), config, tx, Box::new(peer_store)); + + // Initiate connections. + controller.alloc_slots(); + + let mut messages = Vec::new(); + while let Some(message) = rx.try_recv().ok() { + messages.push(message); + } + + // Only first two peers are connected (we only have 2 slots). + assert_eq!(messages.len(), 2); + assert!(messages.contains(&Message::Connect { set_id: SetId(0), peer_id: peer1 })); + assert!(messages.contains(&Message::Connect { set_id: SetId(0), peer_id: peer2 })); + + // Outgoing slots occupied. + assert_eq!(controller.num_out, 2); + assert_eq!(controller.num_in, 0); + + // No more nodes are connected. + controller.alloc_slots(); + assert_eq!(rx.try_recv().unwrap_err(), TryRecvError::Empty); + + // No more slots occupied. + assert_eq!(controller.num_out, 2); + assert_eq!(controller.num_in, 0); + + // Drop peers. + controller.on_peer_dropped(peer1); + controller.on_peer_dropped(peer2); + + // Slots are freed. + assert_eq!(controller.num_out, 0); + assert_eq!(controller.num_in, 0); + + // Initiate connections. + controller.alloc_slots(); + + let mut messages = Vec::new(); + while let Some(message) = rx.try_recv().ok() { + messages.push(message); + } + + // Peers are connected. + assert_eq!(messages.len(), 1); + assert!(messages.contains(&Message::Connect { set_id: SetId(0), peer_id: peer3 })); + + // Outgoing slots occupied. + assert_eq!(controller.num_out, 1); + assert_eq!(controller.num_in, 0); + } + + #[test] + fn in_reserved_only_mode_no_peers_are_requested_from_peer_store_and_connected() { + let config = SetConfig { + in_peers: 0, + // Make sure we have slots available. + out_peers: 2, + bootnodes: Vec::new(), + reserved_nodes: HashSet::new(), + reserved_only: true, + }; + let (tx, mut rx) = tracing_unbounded("mpsc_test_to_notifications", 100); + + let mut peer_store = MockPeerStoreHandle::new(); + peer_store.expect_register_protocol().once().return_const(()); + + let (_handle, mut controller) = + ProtocolController::new(SetId(0), config, tx, Box::new(peer_store)); + + // Initiate connections. + controller.alloc_slots(); + + // No nodes are connected. + assert_eq!(controller.num_out, 0); + assert_eq!(controller.num_in, 0); + assert_eq!(rx.try_recv().unwrap_err(), TryRecvError::Empty); + } + + #[test] + fn in_reserved_only_mode_no_regular_peers_are_accepted() { + let config = SetConfig { + // Make sure we have slots available. + in_peers: 2, + out_peers: 0, + bootnodes: Vec::new(), + reserved_nodes: HashSet::new(), + reserved_only: true, + }; + let (tx, mut rx) = tracing_unbounded("mpsc_test_to_notifications", 100); + + let mut peer_store = MockPeerStoreHandle::new(); + peer_store.expect_register_protocol().once().return_const(()); + + let (_handle, mut controller) = + ProtocolController::new(SetId(0), config, tx, Box::new(peer_store)); + + let peer = PeerId::random(); + let incoming_index = IncomingIndex(1); + controller.on_incoming_connection(peer, incoming_index); + + let mut messages = Vec::new(); + while let Some(message) = rx.try_recv().ok() { + messages.push(message); + } + + // Peer is rejected. + assert_eq!(messages.len(), 1); + assert!(messages.contains(&Message::Reject(incoming_index))); + assert_eq!(controller.num_out, 0); + assert_eq!(controller.num_in, 0); + } + + #[test] + fn disabling_reserved_only_mode_allows_to_connect_to_peers() { + let peer1 = PeerId::random(); + let peer2 = PeerId::random(); + let candidates = vec![peer1, peer2]; + + let config = SetConfig { + in_peers: 0, + // Make sure we have slots available. + out_peers: 10, + bootnodes: Vec::new(), + reserved_nodes: HashSet::new(), + reserved_only: true, + }; + let (tx, mut rx) = tracing_unbounded("mpsc_test_to_notifications", 100); + + let mut peer_store = MockPeerStoreHandle::new(); + peer_store.expect_register_protocol().once().return_const(()); + peer_store.expect_outgoing_candidates().once().return_const(candidates); + + let (_handle, mut controller) = + ProtocolController::new(SetId(0), config, tx, Box::new(peer_store)); + + // Initiate connections. + controller.alloc_slots(); + + // No nodes are connected. + assert_eq!(controller.num_out, 0); + assert_eq!(controller.num_in, 0); + assert_eq!(rx.try_recv().unwrap_err(), TryRecvError::Empty); + + // Disable reserved-only mode (this also connects to peers). + controller.on_set_reserved_only(false); + + let mut messages = Vec::new(); + while let Some(message) = rx.try_recv().ok() { + messages.push(message); + } + + assert_eq!(messages.len(), 2); + assert!(messages.contains(&Message::Connect { set_id: SetId(0), peer_id: peer1 })); + assert!(messages.contains(&Message::Connect { set_id: SetId(0), peer_id: peer2 })); + assert_eq!(controller.num_out, 2); + assert_eq!(controller.num_in, 0); + } + + #[test] + fn enabling_reserved_only_mode_disconnects_regular_peers() { + let reserved1 = PeerId::random(); + let reserved2 = PeerId::random(); + let regular1 = PeerId::random(); + let regular2 = PeerId::random(); + let outgoing_candidates = vec![regular1]; + + let config = SetConfig { + in_peers: 10, + out_peers: 10, + bootnodes: Vec::new(), + reserved_nodes: [reserved1, reserved2].iter().cloned().collect(), + reserved_only: false, + }; + let (tx, mut rx) = tracing_unbounded("mpsc_test_to_notifications", 100); + + let mut peer_store = MockPeerStoreHandle::new(); + peer_store.expect_register_protocol().once().return_const(()); + peer_store.expect_is_banned().times(3).return_const(false); + peer_store.expect_outgoing_candidates().once().return_const(outgoing_candidates); + + let (_handle, mut controller) = + ProtocolController::new(SetId(0), config, tx, Box::new(peer_store)); + assert_eq!(controller.num_out, 0); + assert_eq!(controller.num_in, 0); + + // Connect `regular1` as outbound. + controller.alloc_slots(); + + let mut messages = Vec::new(); + while let Some(message) = rx.try_recv().ok() { + messages.push(message); + } + assert_eq!(messages.len(), 3); + assert!(messages.contains(&Message::Connect { set_id: SetId(0), peer_id: reserved1 })); + assert!(messages.contains(&Message::Connect { set_id: SetId(0), peer_id: reserved2 })); + assert!(messages.contains(&Message::Connect { set_id: SetId(0), peer_id: regular1 })); + assert_eq!(controller.num_out, 1); + assert_eq!(controller.num_in, 0); + + // Connect `regular2` as inbound. + let incoming_index = IncomingIndex(1); + controller.on_incoming_connection(regular2, incoming_index); + assert_eq!(rx.try_recv().unwrap(), Message::Accept(incoming_index)); + assert_eq!(rx.try_recv().unwrap_err(), TryRecvError::Empty); + assert_eq!(controller.num_out, 1); + assert_eq!(controller.num_in, 1); + + // Switch to reserved-only mode. + controller.on_set_reserved_only(true); + + let mut messages = Vec::new(); + while let Some(message) = rx.try_recv().ok() { + messages.push(message); + } + assert_eq!(messages.len(), 2); + assert!(messages.contains(&Message::Drop { set_id: SetId(0), peer_id: regular1 })); + assert!(messages.contains(&Message::Drop { set_id: SetId(0), peer_id: regular2 })); + assert_eq!(controller.nodes.len(), 0); + assert_eq!(controller.num_out, 0); + assert_eq!(controller.num_in, 0); + } + + #[test] + fn removed_disconnected_reserved_node_is_forgotten() { + let reserved1 = PeerId::random(); + let reserved2 = PeerId::random(); + + let config = SetConfig { + in_peers: 10, + out_peers: 10, + bootnodes: Vec::new(), + reserved_nodes: [reserved1, reserved2].iter().cloned().collect(), + reserved_only: false, + }; + let (tx, mut rx) = tracing_unbounded("mpsc_test_to_notifications", 100); + + let mut peer_store = MockPeerStoreHandle::new(); + peer_store.expect_register_protocol().once().return_const(()); + + let (_handle, mut controller) = + ProtocolController::new(SetId(0), config, tx, Box::new(peer_store)); + assert_eq!(controller.reserved_nodes.len(), 2); + assert_eq!(controller.nodes.len(), 0); + assert_eq!(controller.num_out, 0); + assert_eq!(controller.num_in, 0); + + controller.on_remove_reserved_peer(reserved1); + assert_eq!(rx.try_recv().unwrap_err(), TryRecvError::Empty); + assert_eq!(controller.reserved_nodes.len(), 1); + assert!(!controller.reserved_nodes.contains_key(&reserved1)); + assert_eq!(controller.nodes.len(), 0); + assert_eq!(controller.num_out, 0); + assert_eq!(controller.num_in, 0); + } + + #[test] + fn removed_connected_reserved_node_is_disconnected_in_reserved_only_mode() { + let reserved1 = PeerId::random(); + let reserved2 = PeerId::random(); + + let config = SetConfig { + in_peers: 10, + out_peers: 10, + bootnodes: Vec::new(), + reserved_nodes: [reserved1, reserved2].iter().cloned().collect(), + reserved_only: true, + }; + let (tx, mut rx) = tracing_unbounded("mpsc_test_to_notifications", 100); + + let mut peer_store = MockPeerStoreHandle::new(); + peer_store.expect_register_protocol().once().return_const(()); + peer_store.expect_is_banned().times(2).return_const(false); + + let (_handle, mut controller) = + ProtocolController::new(SetId(0), config, tx, Box::new(peer_store)); + + // Initiate connections. + controller.alloc_slots(); + let mut messages = Vec::new(); + while let Some(message) = rx.try_recv().ok() { + messages.push(message); + } + assert_eq!(messages.len(), 2); + assert!(messages.contains(&Message::Connect { set_id: SetId(0), peer_id: reserved1 })); + assert!(messages.contains(&Message::Connect { set_id: SetId(0), peer_id: reserved2 })); + assert_eq!(controller.reserved_nodes.len(), 2); + assert!(controller.reserved_nodes.contains_key(&reserved1)); + assert!(controller.reserved_nodes.contains_key(&reserved2)); + assert!(controller.nodes.is_empty()); + + // Remove reserved node + controller.on_remove_reserved_peer(reserved1); + assert_eq!(rx.try_recv().unwrap(), Message::Drop { set_id: SetId(0), peer_id: reserved1 }); + assert_eq!(rx.try_recv().unwrap_err(), TryRecvError::Empty); + assert_eq!(controller.reserved_nodes.len(), 1); + assert!(controller.reserved_nodes.contains_key(&reserved2)); + assert!(controller.nodes.is_empty()); + } + + #[test] + fn removed_connected_reserved_nodes_become_regular_in_non_reserved_mode() { + let peer1 = PeerId::random(); + let peer2 = PeerId::random(); + + let config = SetConfig { + in_peers: 10, + out_peers: 10, + bootnodes: Vec::new(), + reserved_nodes: [peer1, peer2].iter().cloned().collect(), + reserved_only: false, + }; + let (tx, mut rx) = tracing_unbounded("mpsc_test_to_notifications", 100); + + let mut peer_store = MockPeerStoreHandle::new(); + peer_store.expect_register_protocol().once().return_const(()); + peer_store.expect_is_banned().times(2).return_const(false); + peer_store.expect_outgoing_candidates().once().return_const(Vec::new()); + + let (_handle, mut controller) = + ProtocolController::new(SetId(0), config, tx, Box::new(peer_store)); + + // Connect `peer1` as inbound, `peer2` as outbound. + controller.on_incoming_connection(peer1, IncomingIndex(1)); + controller.alloc_slots(); + let mut messages = Vec::new(); + while let Some(message) = rx.try_recv().ok() { + messages.push(message); + } + assert_eq!(messages.len(), 2); + assert!(messages.contains(&Message::Accept(IncomingIndex(1)))); + assert!(messages.contains(&Message::Connect { set_id: SetId(0), peer_id: peer2 })); + assert_eq!(controller.num_out, 0); + assert_eq!(controller.num_in, 0); + + // Remove reserved nodes (and make them regular) + controller.on_remove_reserved_peer(peer1); + controller.on_remove_reserved_peer(peer2); + assert_eq!(rx.try_recv().unwrap_err(), TryRecvError::Empty); + assert_eq!(controller.nodes.len(), 2); + assert!(matches!(controller.nodes.get(&peer1), Some(Direction::Inbound))); + assert!(matches!(controller.nodes.get(&peer2), Some(Direction::Outbound))); + assert_eq!(controller.num_out, 1); + assert_eq!(controller.num_in, 1); + } + + #[test] + fn regular_nodes_stop_occupying_slots_when_become_reserved() { + let peer1 = PeerId::random(); + let peer2 = PeerId::random(); + let outgoing_candidates = vec![peer1]; + + let config = SetConfig { + in_peers: 10, + out_peers: 10, + bootnodes: Vec::new(), + reserved_nodes: HashSet::new(), + reserved_only: false, + }; + let (tx, mut rx) = tracing_unbounded("mpsc_test_to_notifications", 100); + + let mut peer_store = MockPeerStoreHandle::new(); + peer_store.expect_register_protocol().once().return_const(()); + peer_store.expect_is_banned().once().return_const(false); + peer_store.expect_outgoing_candidates().once().return_const(outgoing_candidates); + + let (_handle, mut controller) = + ProtocolController::new(SetId(0), config, tx, Box::new(peer_store)); + + // Connect `peer1` as outbound & `peer2` as inbound. + controller.alloc_slots(); + controller.on_incoming_connection(peer2, IncomingIndex(1)); + let mut messages = Vec::new(); + while let Some(message) = rx.try_recv().ok() { + messages.push(message); + } + assert_eq!(messages.len(), 2); + assert!(messages.contains(&Message::Connect { set_id: SetId(0), peer_id: peer1 })); + assert!(messages.contains(&Message::Accept(IncomingIndex(1)))); + assert_eq!(controller.num_in, 1); + assert_eq!(controller.num_out, 1); + + controller.on_add_reserved_peer(peer1); + controller.on_add_reserved_peer(peer2); + assert_eq!(rx.try_recv().unwrap_err(), TryRecvError::Empty); + assert_eq!(controller.num_in, 0); + assert_eq!(controller.num_out, 0); + } + + #[test] + fn disconnecting_regular_peers_work() { + let peer1 = PeerId::random(); + let peer2 = PeerId::random(); + let outgoing_candidates = vec![peer1]; + + let config = SetConfig { + in_peers: 10, + out_peers: 10, + bootnodes: Vec::new(), + reserved_nodes: HashSet::new(), + reserved_only: false, + }; + let (tx, mut rx) = tracing_unbounded("mpsc_test_to_notifications", 100); + + let mut peer_store = MockPeerStoreHandle::new(); + peer_store.expect_register_protocol().once().return_const(()); + peer_store.expect_is_banned().once().return_const(false); + peer_store.expect_outgoing_candidates().once().return_const(outgoing_candidates); + + let (_handle, mut controller) = + ProtocolController::new(SetId(0), config, tx, Box::new(peer_store)); + + // Connect `peer1` as outbound & `peer2` as inbound. + controller.alloc_slots(); + controller.on_incoming_connection(peer2, IncomingIndex(1)); + let mut messages = Vec::new(); + while let Some(message) = rx.try_recv().ok() { + messages.push(message); + } + assert_eq!(messages.len(), 2); + assert!(messages.contains(&Message::Connect { set_id: SetId(0), peer_id: peer1 })); + assert!(messages.contains(&Message::Accept(IncomingIndex(1)))); + assert_eq!(controller.nodes.len(), 2); + assert!(matches!(controller.nodes.get(&peer1), Some(Direction::Outbound))); + assert!(matches!(controller.nodes.get(&peer2), Some(Direction::Inbound))); + assert_eq!(controller.num_in, 1); + assert_eq!(controller.num_out, 1); + + controller.on_disconnect_peer(peer1); + assert_eq!(rx.try_recv().unwrap(), Message::Drop { set_id: SetId(0), peer_id: peer1 }); + assert_eq!(rx.try_recv().unwrap_err(), TryRecvError::Empty); + assert_eq!(controller.nodes.len(), 1); + assert!(!controller.nodes.contains_key(&peer1)); + assert_eq!(controller.num_in, 1); + assert_eq!(controller.num_out, 0); + + controller.on_disconnect_peer(peer2); + assert_eq!(rx.try_recv().unwrap(), Message::Drop { set_id: SetId(0), peer_id: peer2 }); + assert_eq!(rx.try_recv().unwrap_err(), TryRecvError::Empty); + assert_eq!(controller.nodes.len(), 0); + assert_eq!(controller.num_in, 0); + assert_eq!(controller.num_out, 0); + } + + #[test] + fn disconnecting_reserved_peers_is_a_noop() { + let reserved1 = PeerId::random(); + let reserved2 = PeerId::random(); + + let config = SetConfig { + in_peers: 10, + out_peers: 10, + bootnodes: Vec::new(), + reserved_nodes: [reserved1, reserved2].iter().cloned().collect(), + reserved_only: false, + }; + let (tx, mut rx) = tracing_unbounded("mpsc_test_to_notifications", 100); + + let mut peer_store = MockPeerStoreHandle::new(); + peer_store.expect_register_protocol().once().return_const(()); + peer_store.expect_is_banned().times(2).return_const(false); + peer_store.expect_outgoing_candidates().once().return_const(Vec::new()); + + let (_handle, mut controller) = + ProtocolController::new(SetId(0), config, tx, Box::new(peer_store)); + + // Connect `reserved1` as inbound & `reserved2` as outbound. + controller.on_incoming_connection(reserved1, IncomingIndex(1)); + controller.alloc_slots(); + let mut messages = Vec::new(); + while let Some(message) = rx.try_recv().ok() { + messages.push(message); + } + assert_eq!(messages.len(), 2); + assert!(messages.contains(&Message::Accept(IncomingIndex(1)))); + assert!(messages.contains(&Message::Connect { set_id: SetId(0), peer_id: reserved2 })); + assert!(matches!( + controller.reserved_nodes.get(&reserved1), + Some(PeerState::Connected(Direction::Inbound)) + )); + assert!(matches!( + controller.reserved_nodes.get(&reserved2), + Some(PeerState::Connected(Direction::Outbound)) + )); + + controller.on_disconnect_peer(reserved1); + assert_eq!(rx.try_recv().unwrap_err(), TryRecvError::Empty); + assert!(matches!( + controller.reserved_nodes.get(&reserved1), + Some(PeerState::Connected(Direction::Inbound)) + )); + + controller.on_disconnect_peer(reserved2); + assert_eq!(rx.try_recv().unwrap_err(), TryRecvError::Empty); + assert!(matches!( + controller.reserved_nodes.get(&reserved2), + Some(PeerState::Connected(Direction::Outbound)) + )); + } + + #[test] + fn dropping_regular_peers_work() { + let peer1 = PeerId::random(); + let peer2 = PeerId::random(); + let outgoing_candidates = vec![peer1]; + + let config = SetConfig { + in_peers: 10, + out_peers: 10, + bootnodes: Vec::new(), + reserved_nodes: HashSet::new(), + reserved_only: false, + }; + let (tx, mut rx) = tracing_unbounded("mpsc_test_to_notifications", 100); + + let mut peer_store = MockPeerStoreHandle::new(); + peer_store.expect_register_protocol().once().return_const(()); + peer_store.expect_is_banned().once().return_const(false); + peer_store.expect_outgoing_candidates().once().return_const(outgoing_candidates); + peer_store.expect_report_disconnect().times(2).return_const(()); + + let (_handle, mut controller) = + ProtocolController::new(SetId(0), config, tx, Box::new(peer_store)); + + // Connect `peer1` as outbound & `peer2` as inbound. + controller.alloc_slots(); + controller.on_incoming_connection(peer2, IncomingIndex(1)); + let mut messages = Vec::new(); + while let Some(message) = rx.try_recv().ok() { + messages.push(message); + } + assert_eq!(messages.len(), 2); + assert!(messages.contains(&Message::Connect { set_id: SetId(0), peer_id: peer1 })); + assert!(messages.contains(&Message::Accept(IncomingIndex(1)))); + assert_eq!(controller.nodes.len(), 2); + assert!(matches!(controller.nodes.get(&peer1), Some(Direction::Outbound))); + assert!(matches!(controller.nodes.get(&peer2), Some(Direction::Inbound))); + assert_eq!(controller.num_in, 1); + assert_eq!(controller.num_out, 1); + + controller.on_peer_dropped(peer1); + assert_eq!(rx.try_recv().unwrap_err(), TryRecvError::Empty); + assert_eq!(controller.nodes.len(), 1); + assert!(!controller.nodes.contains_key(&peer1)); + assert_eq!(controller.num_in, 1); + assert_eq!(controller.num_out, 0); + + controller.on_peer_dropped(peer2); + assert_eq!(rx.try_recv().unwrap_err(), TryRecvError::Empty); + assert_eq!(controller.nodes.len(), 0); + assert_eq!(controller.num_in, 0); + assert_eq!(controller.num_out, 0); + } + + #[test] + fn incoming_request_for_connected_reserved_node_switches_it_to_inbound() { + let reserved1 = PeerId::random(); + let reserved2 = PeerId::random(); + + let config = SetConfig { + in_peers: 10, + out_peers: 10, + bootnodes: Vec::new(), + reserved_nodes: [reserved1, reserved2].iter().cloned().collect(), + reserved_only: false, + }; + let (tx, mut rx) = tracing_unbounded("mpsc_test_to_notifications", 100); + + let mut peer_store = MockPeerStoreHandle::new(); + peer_store.expect_register_protocol().once().return_const(()); + peer_store.expect_is_banned().times(2).return_const(false); + peer_store.expect_outgoing_candidates().once().return_const(Vec::new()); + + let (_handle, mut controller) = + ProtocolController::new(SetId(0), config, tx, Box::new(peer_store)); + + // Connect `reserved1` as inbound & `reserved2` as outbound. + controller.on_incoming_connection(reserved1, IncomingIndex(1)); + controller.alloc_slots(); + let mut messages = Vec::new(); + while let Some(message) = rx.try_recv().ok() { + messages.push(message); + } + assert_eq!(messages.len(), 2); + assert!(messages.contains(&Message::Accept(IncomingIndex(1)))); + assert!(messages.contains(&Message::Connect { set_id: SetId(0), peer_id: reserved2 })); + assert!(matches!( + controller.reserved_nodes.get(&reserved1), + Some(PeerState::Connected(Direction::Inbound)) + )); + assert!(matches!( + controller.reserved_nodes.get(&reserved2), + Some(PeerState::Connected(Direction::Outbound)) + )); + + // Incoming request for `reserved1`. + controller.on_incoming_connection(reserved1, IncomingIndex(2)); + assert_eq!(rx.try_recv().ok().unwrap(), Message::Accept(IncomingIndex(2))); + assert_eq!(rx.try_recv().unwrap_err(), TryRecvError::Empty); + assert!(matches!( + controller.reserved_nodes.get(&reserved1), + Some(PeerState::Connected(Direction::Inbound)) + )); + + // Incoming request for `reserved2`. + controller.on_incoming_connection(reserved2, IncomingIndex(3)); + assert_eq!(rx.try_recv().ok().unwrap(), Message::Accept(IncomingIndex(3))); + assert_eq!(rx.try_recv().unwrap_err(), TryRecvError::Empty); + assert!(matches!( + controller.reserved_nodes.get(&reserved2), + Some(PeerState::Connected(Direction::Inbound)) + )); + } + + #[test] + fn incoming_request_for_connected_regular_node_switches_it_to_inbound() { + let regular1 = PeerId::random(); + let regular2 = PeerId::random(); + let outgoing_candidates = vec![regular1]; + + let config = SetConfig { + in_peers: 10, + out_peers: 10, + bootnodes: Vec::new(), + reserved_nodes: HashSet::new(), + reserved_only: false, + }; + let (tx, mut rx) = tracing_unbounded("mpsc_test_to_notifications", 100); + + let mut peer_store = MockPeerStoreHandle::new(); + peer_store.expect_register_protocol().once().return_const(()); + peer_store.expect_is_banned().times(3).return_const(false); + peer_store.expect_outgoing_candidates().once().return_const(outgoing_candidates); + + let (_handle, mut controller) = + ProtocolController::new(SetId(0), config, tx, Box::new(peer_store)); + assert_eq!(controller.num_out, 0); + assert_eq!(controller.num_in, 0); + + // Connect `regular1` as outbound. + controller.alloc_slots(); + assert_eq!( + rx.try_recv().ok().unwrap(), + Message::Connect { set_id: SetId(0), peer_id: regular1 } + ); + assert_eq!(rx.try_recv().unwrap_err(), TryRecvError::Empty); + assert!(matches!(controller.nodes.get(®ular1).unwrap(), Direction::Outbound,)); + + // Connect `regular2` as inbound. + controller.on_incoming_connection(regular2, IncomingIndex(0)); + assert_eq!(rx.try_recv().ok().unwrap(), Message::Accept(IncomingIndex(0))); + assert_eq!(rx.try_recv().unwrap_err(), TryRecvError::Empty); + assert!(matches!(controller.nodes.get(®ular2).unwrap(), Direction::Inbound,)); + + // Incoming request for `regular1`. + controller.on_incoming_connection(regular1, IncomingIndex(1)); + assert_eq!(rx.try_recv().ok().unwrap(), Message::Accept(IncomingIndex(1))); + assert_eq!(rx.try_recv().unwrap_err(), TryRecvError::Empty); + assert!(matches!(controller.nodes.get(®ular1).unwrap(), Direction::Inbound,)); + + // Incoming request for `regular2`. + controller.on_incoming_connection(regular2, IncomingIndex(2)); + assert_eq!(rx.try_recv().ok().unwrap(), Message::Accept(IncomingIndex(2))); + assert_eq!(rx.try_recv().unwrap_err(), TryRecvError::Empty); + assert!(matches!(controller.nodes.get(®ular2).unwrap(), Direction::Inbound,)); + } + + #[test] + fn incoming_request_for_connected_node_is_rejected_if_its_banned() { + let regular1 = PeerId::random(); + let regular2 = PeerId::random(); + let outgoing_candidates = vec![regular1]; + + let config = SetConfig { + in_peers: 10, + out_peers: 10, + bootnodes: Vec::new(), + reserved_nodes: HashSet::new(), + reserved_only: false, + }; + let (tx, mut rx) = tracing_unbounded("mpsc_test_to_notifications", 100); + + let mut peer_store = MockPeerStoreHandle::new(); + peer_store.expect_register_protocol().once().return_const(()); + peer_store.expect_is_banned().once().return_const(false); + peer_store.expect_is_banned().times(2).return_const(true); + peer_store.expect_outgoing_candidates().once().return_const(outgoing_candidates); + + let (_handle, mut controller) = + ProtocolController::new(SetId(0), config, tx, Box::new(peer_store)); + assert_eq!(controller.num_out, 0); + assert_eq!(controller.num_in, 0); + + // Connect `regular1` as outbound. + controller.alloc_slots(); + assert_eq!( + rx.try_recv().ok().unwrap(), + Message::Connect { set_id: SetId(0), peer_id: regular1 } + ); + assert_eq!(rx.try_recv().unwrap_err(), TryRecvError::Empty); + assert!(matches!(controller.nodes.get(®ular1).unwrap(), Direction::Outbound,)); + + // Connect `regular2` as inbound. + controller.on_incoming_connection(regular2, IncomingIndex(0)); + assert_eq!(rx.try_recv().ok().unwrap(), Message::Accept(IncomingIndex(0))); + assert_eq!(rx.try_recv().unwrap_err(), TryRecvError::Empty); + assert!(matches!(controller.nodes.get(®ular2).unwrap(), Direction::Inbound,)); + + // Incoming request for `regular1`. + controller.on_incoming_connection(regular1, IncomingIndex(1)); + assert_eq!(rx.try_recv().ok().unwrap(), Message::Reject(IncomingIndex(1))); + assert_eq!(rx.try_recv().unwrap_err(), TryRecvError::Empty); + assert!(!controller.nodes.contains_key(®ular1)); + + // Incoming request for `regular2`. + controller.on_incoming_connection(regular2, IncomingIndex(2)); + assert_eq!(rx.try_recv().ok().unwrap(), Message::Reject(IncomingIndex(2))); + assert_eq!(rx.try_recv().unwrap_err(), TryRecvError::Empty); + assert!(!controller.nodes.contains_key(®ular2)); + } + + #[test] + fn incoming_request_for_connected_node_is_rejected_if_no_slots_available() { + let regular1 = PeerId::random(); + let regular2 = PeerId::random(); + let outgoing_candidates = vec![regular1]; + + let config = SetConfig { + in_peers: 1, + out_peers: 1, + bootnodes: Vec::new(), + reserved_nodes: HashSet::new(), + reserved_only: false, + }; + let (tx, mut rx) = tracing_unbounded("mpsc_test_to_notifications", 100); + + let mut peer_store = MockPeerStoreHandle::new(); + peer_store.expect_register_protocol().once().return_const(()); + peer_store.expect_is_banned().once().return_const(false); + peer_store.expect_outgoing_candidates().once().return_const(outgoing_candidates); + + let (_handle, mut controller) = + ProtocolController::new(SetId(0), config, tx, Box::new(peer_store)); + assert_eq!(controller.num_out, 0); + assert_eq!(controller.num_in, 0); + + // Connect `regular1` as outbound. + controller.alloc_slots(); + assert_eq!( + rx.try_recv().ok().unwrap(), + Message::Connect { set_id: SetId(0), peer_id: regular1 } + ); + assert_eq!(rx.try_recv().unwrap_err(), TryRecvError::Empty); + assert!(matches!(controller.nodes.get(®ular1).unwrap(), Direction::Outbound,)); + + // Connect `regular2` as inbound. + controller.on_incoming_connection(regular2, IncomingIndex(0)); + assert_eq!(rx.try_recv().ok().unwrap(), Message::Accept(IncomingIndex(0))); + assert_eq!(rx.try_recv().unwrap_err(), TryRecvError::Empty); + assert!(matches!(controller.nodes.get(®ular2).unwrap(), Direction::Inbound,)); + + controller.max_in = 0; + + // Incoming request for `regular1`. + controller.on_incoming_connection(regular1, IncomingIndex(1)); + assert_eq!(rx.try_recv().ok().unwrap(), Message::Reject(IncomingIndex(1))); + assert_eq!(rx.try_recv().unwrap_err(), TryRecvError::Empty); + assert!(!controller.nodes.contains_key(®ular1)); + + // Incoming request for `regular2`. + controller.on_incoming_connection(regular2, IncomingIndex(2)); + assert_eq!(rx.try_recv().ok().unwrap(), Message::Reject(IncomingIndex(2))); + assert_eq!(rx.try_recv().unwrap_err(), TryRecvError::Empty); + assert!(!controller.nodes.contains_key(®ular2)); + } + + #[test] + fn incoming_peers_that_exceed_slots_are_rejected() { + let peer1 = PeerId::random(); + let peer2 = PeerId::random(); + + let config = SetConfig { + in_peers: 1, + out_peers: 10, + bootnodes: Vec::new(), + reserved_nodes: HashSet::new(), + reserved_only: false, + }; + let (tx, mut rx) = tracing_unbounded("mpsc_test_to_notifications", 100); + + let mut peer_store = MockPeerStoreHandle::new(); + peer_store.expect_register_protocol().once().return_const(()); + peer_store.expect_is_banned().once().return_const(false); + + let (_handle, mut controller) = + ProtocolController::new(SetId(0), config, tx, Box::new(peer_store)); + + // Connect `peer1` as inbound. + controller.on_incoming_connection(peer1, IncomingIndex(1)); + assert_eq!(rx.try_recv().unwrap(), Message::Accept(IncomingIndex(1))); + assert_eq!(rx.try_recv().unwrap_err(), TryRecvError::Empty); + + // Incoming requests for `peer2`. + controller.on_incoming_connection(peer2, IncomingIndex(2)); + assert_eq!(rx.try_recv().unwrap(), Message::Reject(IncomingIndex(2))); + assert_eq!(rx.try_recv().unwrap_err(), TryRecvError::Empty); + } + + #[test] + fn banned_regular_incoming_node_is_rejected() { + let peer1 = PeerId::random(); + + let config = SetConfig { + in_peers: 10, + out_peers: 10, + bootnodes: Vec::new(), + reserved_nodes: HashSet::new(), + reserved_only: false, + }; + let (tx, mut rx) = tracing_unbounded("mpsc_test_to_notifications", 100); + + let mut peer_store = MockPeerStoreHandle::new(); + peer_store.expect_register_protocol().once().return_const(()); + peer_store.expect_is_banned().once().return_const(true); + + let (_handle, mut controller) = + ProtocolController::new(SetId(0), config, tx, Box::new(peer_store)); + + // Incoming request. + controller.on_incoming_connection(peer1, IncomingIndex(1)); + assert_eq!(rx.try_recv().unwrap(), Message::Reject(IncomingIndex(1))); + assert_eq!(rx.try_recv().unwrap_err(), TryRecvError::Empty); + } + + #[test] + fn banned_reserved_incoming_node_is_rejected() { + let reserved1 = PeerId::random(); + + let config = SetConfig { + in_peers: 10, + out_peers: 10, + bootnodes: Vec::new(), + reserved_nodes: std::iter::once(reserved1).collect(), + reserved_only: false, + }; + let (tx, mut rx) = tracing_unbounded("mpsc_test_to_notifications", 100); + + let mut peer_store = MockPeerStoreHandle::new(); + peer_store.expect_register_protocol().once().return_const(()); + peer_store.expect_is_banned().once().return_const(true); + + let (_handle, mut controller) = + ProtocolController::new(SetId(0), config, tx, Box::new(peer_store)); + assert!(controller.reserved_nodes.contains_key(&reserved1)); + + // Incoming request. + controller.on_incoming_connection(reserved1, IncomingIndex(1)); + assert_eq!(rx.try_recv().unwrap(), Message::Reject(IncomingIndex(1))); + assert_eq!(rx.try_recv().unwrap_err(), TryRecvError::Empty); + } + + #[test] + fn we_dont_connect_to_banned_reserved_node() { + let reserved1 = PeerId::random(); + + let config = SetConfig { + in_peers: 10, + out_peers: 10, + bootnodes: Vec::new(), + reserved_nodes: std::iter::once(reserved1).collect(), + reserved_only: false, + }; + let (tx, mut rx) = tracing_unbounded("mpsc_test_to_notifications", 100); + + let mut peer_store = MockPeerStoreHandle::new(); + peer_store.expect_register_protocol().once().return_const(()); + peer_store.expect_is_banned().once().return_const(true); + peer_store.expect_outgoing_candidates().once().return_const(Vec::new()); + + let (_handle, mut controller) = + ProtocolController::new(SetId(0), config, tx, Box::new(peer_store)); + assert!(matches!(controller.reserved_nodes.get(&reserved1), Some(PeerState::NotConnected))); + + // Initiate connectios + controller.alloc_slots(); + assert!(matches!(controller.reserved_nodes.get(&reserved1), Some(PeerState::NotConnected))); + assert_eq!(rx.try_recv().unwrap_err(), TryRecvError::Empty); + } +} diff --git a/client/peerset/tests/fuzz.rs b/client/peerset/tests/fuzz.rs index 122f17062..855d2339e 100644 --- a/client/peerset/tests/fuzz.rs +++ b/client/peerset/tests/fuzz.rs @@ -31,19 +31,101 @@ use std::{ task::Poll, }; +/// Peer events as observed by `Notifications` / fuzz test. +#[derive(Debug, PartialEq, Eq, Hash, Clone, Copy)] +enum Event { + /// Either API requested to disconnect from the peer, or the peer dropped. + Disconnected, + /// Incoming request. + Incoming, + /// Answer from PSM: accept. + PsmAccept, + /// Answer from PSM: reject. + PsmReject, + /// Command from PSM: connect. + PsmConnect, + /// Command from PSM: drop connection. + PsmDrop, +} + +/// Simplified peer state as thought by `Notifications` / fuzz test. +#[derive(Debug, PartialEq, Eq, Hash, Clone, Copy)] +enum State { + /// Peer is not connected. + Disconnected, + /// We have an inbound connection, but have not decided yet whether to accept it. + Incoming(IncomingIndex), + /// Peer is connected via an inbound connection. + Inbound, + /// Peer is connected via an outbound connection. + Outbound, +} + +/// Bare simplified state without incoming index. +#[derive(Debug, PartialEq, Eq, Hash, Clone, Copy)] +enum BareState { + /// Peer is not connected. + Disconnected, + /// We have an inbound connection, but have not decided yet whether to accept it. + Incoming, + /// Peer is connected via an inbound connection. + Inbound, + /// Peer is connected via an outbound connection. + Outbound, +} + +fn discard_incoming_index(state: State) -> BareState { + match state { + State::Disconnected => BareState::Disconnected, + State::Incoming(_) => BareState::Incoming, + State::Inbound => BareState::Inbound, + State::Outbound => BareState::Outbound, + } +} + #[test] fn run() { + sp_tracing::try_init_simple(); + for _ in 0..50 { test_once(); } } fn test_once() { + // Allowed events that can be received in a specific state. + let allowed_events: HashMap> = [ + ( + BareState::Disconnected, + [Event::Incoming, Event::PsmConnect, Event::PsmDrop /* must be ignored */] + .into_iter() + .collect::>(), + ), + ( + BareState::Incoming, + [Event::PsmAccept, Event::PsmReject].into_iter().collect::>(), + ), + ( + BareState::Inbound, + [Event::Disconnected, Event::PsmDrop, Event::PsmConnect /* must be ignored */] + .into_iter() + .collect::>(), + ), + ( + BareState::Outbound, + [Event::Disconnected, Event::PsmDrop, Event::PsmConnect /* must be ignored */] + .into_iter() + .collect::>(), + ), + ] + .into_iter() + .collect(); + // PRNG to use. let mut rng = rand::thread_rng(); // Nodes that the peerset knows about. - let mut known_nodes = HashSet::::new(); + let mut known_nodes = HashMap::::new(); // Nodes that we have reserved. Always a subset of `known_nodes`. let mut reserved_nodes = HashSet::::new(); @@ -52,7 +134,7 @@ fn test_once() { bootnodes: (0..Uniform::new_inclusive(0, 4).sample(&mut rng)) .map(|_| { let id = PeerId::random(); - known_nodes.insert(id); + known_nodes.insert(id, State::Disconnected); id }) .collect(), @@ -60,7 +142,7 @@ fn test_once() { (0..Uniform::new_inclusive(0, 2).sample(&mut rng)) .map(|_| { let id = PeerId::random(); - known_nodes.insert(id); + known_nodes.insert(id, State::Disconnected); reserved_nodes.insert(id); id }) @@ -72,6 +154,10 @@ fn test_once() { }], }); + let new_id = PeerId::random(); + known_nodes.insert(new_id, State::Disconnected); + peerset_handle.add_known_peer(new_id); + futures::executor::block_on(futures::future::poll_fn(move |cx| { // List of nodes the user of `peerset` assumes it's connected to. Always a subset of // `known_nodes`. @@ -84,28 +170,129 @@ fn test_once() { // Perform a certain number of actions while checking that the state is consistent. If we // reach the end of the loop, the run has succeeded. + // Note that with the ACKing and event postponing mechanism in `ProtocolController` + // the test time grows quadratically with the number of iterations below. for _ in 0..2500 { + // Peer we are working with. + let mut current_peer = None; + // Current event for event bigrams validation. + let mut current_event = None; + // Last peer state for allowed event validation. + let mut last_state = None; + // Each of these weights corresponds to an action that we may perform. let action_weights = [150, 90, 90, 30, 30, 1, 1, 4, 4]; match WeightedIndex::new(&action_weights).unwrap().sample(&mut rng) { // If we generate 0, poll the peerset. 0 => match Stream::poll_next(Pin::new(&mut peerset), cx) { Poll::Ready(Some(Message::Connect { peer_id, .. })) => { - if let Some(id) = - incoming_nodes.iter().find(|(_, v)| **v == peer_id).map(|(&id, _)| id) - { - incoming_nodes.remove(&id); + log::info!("PSM: connecting to peer {}", peer_id); + + let state = known_nodes.get_mut(&peer_id).unwrap(); + if matches!(*state, State::Incoming(_)) { + log::info!( + "Awaiting incoming response, ignoring obsolete Connect from PSM for peer {}", + peer_id, + ); + continue } - assert!(connected_nodes.insert(peer_id)); + + last_state = Some(*state); + + if *state != State::Inbound { + *state = State::Outbound; + } + + if !connected_nodes.insert(peer_id) { + log::info!("Request to connect to an already connected node {peer_id}"); + } + + current_peer = Some(peer_id); + current_event = Some(Event::PsmConnect); }, Poll::Ready(Some(Message::Drop { peer_id, .. })) => { - connected_nodes.remove(&peer_id); + log::info!("PSM: dropping peer {}", peer_id); + + let state = known_nodes.get_mut(&peer_id).unwrap(); + if matches!(*state, State::Incoming(_)) { + log::info!( + "Awaiting incoming response, ignoring obsolete Drop from PSM for peer {}", + peer_id, + ); + continue + } + + last_state = Some(*state); + *state = State::Disconnected; + + if !connected_nodes.remove(&peer_id) { + log::info!("Ignoring attempt to drop a disconnected peer {}", peer_id); + } + + current_peer = Some(peer_id); + current_event = Some(Event::PsmDrop); }, Poll::Ready(Some(Message::Accept(n))) => { - assert!(connected_nodes.insert(incoming_nodes.remove(&n).unwrap())) + log::info!("PSM: accepting index {}", n.0); + + let peer_id = incoming_nodes.remove(&n).unwrap(); + + let state = known_nodes.get_mut(&peer_id).unwrap(); + match *state { + State::Incoming(incoming_index) => + if n.0 < incoming_index.0 { + log::info!( + "Ignoring obsolete Accept for {:?} while awaiting {:?} for peer {}", + n, incoming_index, peer_id, + ); + continue + } else if n.0 > incoming_index.0 { + panic!( + "Received {:?} while awaiting {:?} for peer {}", + n, incoming_index, peer_id, + ); + }, + _ => {}, + } + + last_state = Some(*state); + *state = State::Inbound; + + assert!(connected_nodes.insert(peer_id)); + + current_peer = Some(peer_id); + current_event = Some(Event::PsmAccept); }, Poll::Ready(Some(Message::Reject(n))) => { - assert!(!connected_nodes.contains(&incoming_nodes.remove(&n).unwrap())) + log::info!("PSM: rejecting index {}", n.0); + + let peer_id = incoming_nodes.remove(&n).unwrap(); + + let state = known_nodes.get_mut(&peer_id).unwrap(); + match *state { + State::Incoming(incoming_index) => + if n.0 < incoming_index.0 { + log::info!( + "Ignoring obsolete Reject for {:?} while awaiting {:?} for peer {}", + n, incoming_index, peer_id, + ); + continue + } else if n.0 > incoming_index.0 { + panic!( + "Received {:?} while awaiting {:?} for peer {}", + n, incoming_index, peer_id, + ); + }, + _ => {}, + } + + last_state = Some(*state); + *state = State::Disconnected; + + assert!(!connected_nodes.contains(&peer_id)); + + current_peer = Some(peer_id); + current_event = Some(Event::PsmReject); }, Poll::Ready(None) => panic!(), Poll::Pending => {}, @@ -114,13 +301,13 @@ fn test_once() { // If we generate 1, discover a new node. 1 => { let new_id = PeerId::random(); - known_nodes.insert(new_id); - peerset.add_to_peers_set(SetId::from(0), new_id); + known_nodes.insert(new_id, State::Disconnected); + peerset_handle.add_known_peer(new_id); }, // If we generate 2, adjust a random reputation. 2 => - if let Some(id) = known_nodes.iter().choose(&mut rng) { + if let Some(id) = known_nodes.keys().choose(&mut rng) { let val = Uniform::new_inclusive(i32::MIN, i32::MAX).sample(&mut rng); peerset_handle.report_peer(*id, ReputationChange::new(val, "")); }, @@ -128,47 +315,86 @@ fn test_once() { // If we generate 3, disconnect from a random node. 3 => if let Some(id) = connected_nodes.iter().choose(&mut rng).cloned() { + log::info!("Disconnected from {}", id); connected_nodes.remove(&id); + + let state = known_nodes.get_mut(&id).unwrap(); + last_state = Some(*state); + *state = State::Disconnected; + peerset.dropped(SetId::from(0), id, DropReason::Unknown); + + current_peer = Some(id); + current_event = Some(Event::Disconnected); }, // If we generate 4, connect to a random node. 4 => { if let Some(id) = known_nodes - .iter() + .keys() .filter(|n| { incoming_nodes.values().all(|m| m != *n) && !connected_nodes.contains(*n) }) .choose(&mut rng) + .cloned() { - peerset.incoming(SetId::from(0), *id, next_incoming_id); - incoming_nodes.insert(next_incoming_id, *id); + log::info!("Incoming connection from {}, index {}", id, next_incoming_id.0); + peerset.incoming(SetId::from(0), id, next_incoming_id); + incoming_nodes.insert(next_incoming_id, id); + + let state = known_nodes.get_mut(&id).unwrap(); + last_state = Some(*state); + *state = State::Incoming(next_incoming_id); + next_incoming_id.0 += 1; + + current_peer = Some(id); + current_event = Some(Event::Incoming); } }, // 5 and 6 are the reserved-only mode. - 5 => peerset_handle.set_reserved_only(SetId::from(0), true), - 6 => peerset_handle.set_reserved_only(SetId::from(0), false), + 5 => { + log::info!("Set reserved only"); + peerset_handle.set_reserved_only(SetId::from(0), true); + }, + 6 => { + log::info!("Unset reserved only"); + peerset_handle.set_reserved_only(SetId::from(0), false); + }, // 7 and 8 are about switching a random node in or out of reserved mode. 7 => { if let Some(id) = - known_nodes.iter().filter(|n| !reserved_nodes.contains(*n)).choose(&mut rng) + known_nodes.keys().filter(|n| !reserved_nodes.contains(*n)).choose(&mut rng) { + log::info!("Add reserved: {}", id); peerset_handle.add_reserved_peer(SetId::from(0), *id); reserved_nodes.insert(*id); } }, 8 => if let Some(id) = reserved_nodes.iter().choose(&mut rng).cloned() { + log::info!("Remove reserved: {}", id); reserved_nodes.remove(&id); peerset_handle.remove_reserved_peer(SetId::from(0), id); }, _ => unreachable!(), } + + // Validate event bigrams and state transitions. + if let Some(peer_id) = current_peer { + let event = current_event.unwrap(); + let last_state = discard_incoming_index(last_state.unwrap()); + if !allowed_events.get(&last_state).unwrap().contains(&event) { + panic!( + "Invalid state transition: {:?} x {:?} for peer {}", + last_state, event, peer_id, + ); + } + } } Poll::Ready(()) diff --git a/client/utils/src/mpsc.rs b/client/utils/src/mpsc.rs index 7e06bd203..36e44be5e 100644 --- a/client/utils/src/mpsc.rs +++ b/client/utils/src/mpsc.rs @@ -18,8 +18,10 @@ //! Code to meter unbounded channels. +pub use async_channel::{TryRecvError, TrySendError}; + use crate::metrics::UNBOUNDED_CHANNELS_COUNTER; -use async_channel::{Receiver, Sender, TryRecvError, TrySendError}; +use async_channel::{Receiver, Sender}; use futures::{ stream::{FusedStream, Stream}, task::{Context, Poll}, From 5b0831525c8a9066c2538bae95d574f9b228d473 Mon Sep 17 00:00:00 2001 From: Ignacio Palacios Date: Tue, 23 May 2023 15:15:20 +0200 Subject: [PATCH 534/558] Add genesis config to Glutton pallet (#14188) * glutton gensis config added * Glutton pallet updates (#14192) * Add admin origin and other fixes Signed-off-by: Oliver Tale-Yazdi * Remove magic number Signed-off-by: Oliver Tale-Yazdi * Typo Signed-off-by: Oliver Tale-Yazdi --------- Signed-off-by: Oliver Tale-Yazdi * fix genesis_build * Fix docs Signed-off-by: Oliver Tale-Yazdi * Fix kitchensink runtime Signed-off-by: Oliver Tale-Yazdi * fix twox_256 * fmt * twox_256 clean * nitpick Co-authored-by: joe petrowski <25483142+joepetrowski@users.noreply.github.com> --------- Signed-off-by: Oliver Tale-Yazdi Co-authored-by: Oliver Tale-Yazdi Co-authored-by: joe petrowski <25483142+joepetrowski@users.noreply.github.com> --- bin/node/cli/src/chain_spec.rs | 13 ++-- bin/node/runtime/src/lib.rs | 1 + bin/node/testing/src/genesis.rs | 9 ++- frame/glutton/src/lib.rs | 101 ++++++++++++++++++++++++++------ frame/glutton/src/mock.rs | 1 + frame/glutton/src/tests.rs | 15 ++++- 6 files changed, 115 insertions(+), 25 deletions(-) diff --git a/bin/node/cli/src/chain_spec.rs b/bin/node/cli/src/chain_spec.rs index 85a08e71c..5a91d9c0f 100644 --- a/bin/node/cli/src/chain_spec.rs +++ b/bin/node/cli/src/chain_spec.rs @@ -21,10 +21,10 @@ use grandpa_primitives::AuthorityId as GrandpaId; use kitchensink_runtime::{ constants::currency::*, wasm_binary_unwrap, AuthorityDiscoveryConfig, BabeConfig, - BalancesConfig, Block, CouncilConfig, DemocracyConfig, ElectionsConfig, GrandpaConfig, - ImOnlineConfig, IndicesConfig, MaxNominations, NominationPoolsConfig, SessionConfig, - SessionKeys, SocietyConfig, StakerStatus, StakingConfig, SudoConfig, SystemConfig, - TechnicalCommitteeConfig, + BalancesConfig, Block, CouncilConfig, DemocracyConfig, ElectionsConfig, GluttonConfig, + GrandpaConfig, ImOnlineConfig, IndicesConfig, MaxNominations, NominationPoolsConfig, + SessionConfig, SessionKeys, SocietyConfig, StakerStatus, StakingConfig, SudoConfig, + SystemConfig, TechnicalCommitteeConfig, }; use pallet_im_online::sr25519::AuthorityId as ImOnlineId; use sc_chain_spec::ChainSpecExtension; @@ -372,6 +372,11 @@ pub fn testnet_genesis( min_join_bond: 1 * DOLLARS, ..Default::default() }, + glutton: GluttonConfig { + compute: Default::default(), + storage: Default::default(), + trash_data_count: Default::default(), + }, } } diff --git a/bin/node/runtime/src/lib.rs b/bin/node/runtime/src/lib.rs index 76143aa1b..47134f41f 100644 --- a/bin/node/runtime/src/lib.rs +++ b/bin/node/runtime/src/lib.rs @@ -377,6 +377,7 @@ impl pallet_scheduler::Config for Runtime { impl pallet_glutton::Config for Runtime { type RuntimeEvent = RuntimeEvent; + type AdminOrigin = EnsureRoot; type WeightInfo = pallet_glutton::weights::SubstrateWeight; } diff --git a/bin/node/testing/src/genesis.rs b/bin/node/testing/src/genesis.rs index d542bb29c..f91070017 100644 --- a/bin/node/testing/src/genesis.rs +++ b/bin/node/testing/src/genesis.rs @@ -21,8 +21,8 @@ use crate::keyring::*; use kitchensink_runtime::{ constants::currency::*, wasm_binary_unwrap, AccountId, AssetsConfig, BabeConfig, - BalancesConfig, GenesisConfig, GrandpaConfig, IndicesConfig, SessionConfig, SocietyConfig, - StakerStatus, StakingConfig, SystemConfig, BABE_GENESIS_EPOCH_CONFIG, + BalancesConfig, GenesisConfig, GluttonConfig, GrandpaConfig, IndicesConfig, SessionConfig, + SocietyConfig, StakerStatus, StakingConfig, SystemConfig, BABE_GENESIS_EPOCH_CONFIG, }; use sp_keyring::{Ed25519Keyring, Sr25519Keyring}; use sp_runtime::Perbill; @@ -94,5 +94,10 @@ pub fn config_endowed(code: Option<&[u8]>, extra_endowed: Vec) -> Gen alliance: Default::default(), alliance_motion: Default::default(), nomination_pools: Default::default(), + glutton: GluttonConfig { + compute: Default::default(), + storage: Default::default(), + trash_data_count: Default::default(), + }, } } diff --git a/frame/glutton/src/lib.rs b/frame/glutton/src/lib.rs index e9a46374a..50a26a495 100644 --- a/frame/glutton/src/lib.rs +++ b/frame/glutton/src/lib.rs @@ -34,12 +34,18 @@ pub mod weights; use blake2::{Blake2b512, Digest}; use frame_support::{pallet_prelude::*, weights::WeightMeter}; use frame_system::pallet_prelude::*; +use sp_io::hashing::twox_256; use sp_runtime::{traits::Zero, Perbill}; use sp_std::{vec, vec::Vec}; pub use pallet::*; pub use weights::WeightInfo; +/// The size of each value in the `TrashData` storage in bytes. +pub const VALUE_SIZE: usize = 1024; +/// Max number of entries for `TrashData` storage item +pub const MAX_TRASH_DATA_ENTRIES: u32 = 65_000; + #[frame_support::pallet] pub mod pallet { use super::*; @@ -48,6 +54,9 @@ pub mod pallet { pub trait Config: frame_system::Config { type RuntimeEvent: From + IsType<::RuntimeEvent>; + /// The admin origin that can set computational limits and initialize the pallet. + type AdminOrigin: EnsureOrigin; + /// Weight information for this pallet. type WeightInfo: WeightInfo; } @@ -58,11 +67,11 @@ pub mod pallet { #[pallet::event] #[pallet::generate_deposit(pub(super) fn deposit_event)] pub enum Event { - /// The pallet has been (re)initialized by root. + /// The pallet has been (re)initialized. PalletInitialized { reinit: bool }, - /// The computation limit has been updated by root. + /// The computation limit has been updated. ComputationLimitSet { compute: Perbill }, - /// The storage limit has been updated by root. + /// The storage limit has been updated. StorageLimitSet { storage: Perbill }, } @@ -96,15 +105,51 @@ pub mod pallet { pub(super) type TrashData = StorageMap< Hasher = Twox64Concat, Key = u32, - Value = [u8; 1024], + Value = [u8; VALUE_SIZE], QueryKind = OptionQuery, - MaxValues = ConstU32<65_000>, + MaxValues = ConstU32, >; /// The current number of entries in `TrashData`. #[pallet::storage] pub(crate) type TrashDataCount = StorageValue<_, u32, ValueQuery>; + #[pallet::genesis_config] + pub struct GenesisConfig { + pub compute: Perbill, + pub storage: Perbill, + pub trash_data_count: u32, + } + + impl Default for GenesisConfig { + fn default() -> Self { + Self { + compute: Default::default(), + storage: Default::default(), + trash_data_count: Default::default(), + } + } + } + + #[pallet::genesis_build] + impl GenesisBuild for GenesisConfig { + fn build(&self) { + assert!( + self.trash_data_count <= MAX_TRASH_DATA_ENTRIES, + "number of TrashData entries cannot be bigger than {:?}", + MAX_TRASH_DATA_ENTRIES + ); + + (0..self.trash_data_count) + .for_each(|i| TrashData::::insert(i, Pallet::::gen_value(i))); + + TrashDataCount::::set(self.trash_data_count); + + >::put(self.compute); + >::put(self.storage); + } + } + #[pallet::hooks] impl Hooks> for Pallet { fn integrity_test() { @@ -143,7 +188,11 @@ pub mod pallet { impl Pallet { /// Initializes the pallet by writing into `TrashData`. /// - /// Only callable by Root. A good default for `trash_count` is `5_000`. + /// `current_count` is the current number of elements in `TrashData`. This can be set to + /// `None` when the pallet is first initialized. + /// + /// Only callable by Root or `AdminOrigin`. A good default for `new_count` is + /// `5_000`. #[pallet::call_index(0)] #[pallet::weight( T::WeightInfo::initialize_pallet_grow(witness_count.unwrap_or_default()) @@ -154,7 +203,7 @@ pub mod pallet { new_count: u32, witness_count: Option, ) -> DispatchResult { - ensure_root(origin)?; + T::AdminOrigin::try_origin(origin).map(|_| ()).or_else(|o| ensure_root(o))?; let current_count = TrashDataCount::::get(); ensure!( @@ -163,7 +212,8 @@ pub mod pallet { ); if new_count > current_count { - (current_count..new_count).for_each(|i| TrashData::::insert(i, [i as u8; 1024])); + (current_count..new_count) + .for_each(|i| TrashData::::insert(i, Self::gen_value(i))); } else { (new_count..current_count).for_each(TrashData::::remove); } @@ -173,28 +223,33 @@ pub mod pallet { Ok(()) } - /// Set the `Compute` storage value that determines how much of the - /// block's weight `ref_time` to use during `on_idle`. + /// Set how much of the remaining `ref_time` weight should be consumed by `on_idle`. /// - /// Only callable by Root. + /// Only callable by Root or `AdminOrigin`. #[pallet::call_index(1)] #[pallet::weight(T::WeightInfo::set_compute())] pub fn set_compute(origin: OriginFor, compute: Perbill) -> DispatchResult { - ensure_root(origin)?; + T::AdminOrigin::try_origin(origin).map(|_| ()).or_else(|o| ensure_root(o))?; + Compute::::set(compute); Self::deposit_event(Event::ComputationLimitSet { compute }); Ok(()) } - /// Set the `Storage` storage value that determines the PoV size usage - /// for each block. + /// Set how much of the remaining `proof_size` weight should be consumed by `on_idle`. + // + /// 100% means that all remaining `proof_size` will be consumed. The PoV benchmarking + /// results that are used here are likely an over-estimation. 100% intended consumption will + /// therefore translate to less than 100% actual consumption. In the future, this could be + /// counter-acted by allowing the glutton to specify over-unity consumption ratios. /// - /// Only callable by Root. + /// Only callable by Root or `AdminOrigin`. #[pallet::call_index(2)] #[pallet::weight(T::WeightInfo::set_storage())] pub fn set_storage(origin: OriginFor, storage: Perbill) -> DispatchResult { - ensure_root(origin)?; + T::AdminOrigin::try_origin(origin).map(|_| ()).or_else(|o| ensure_root(o))?; + Storage::::set(storage); Self::deposit_event(Event::StorageLimitSet { storage }); @@ -251,7 +306,7 @@ pub mod pallet { // compiler does not know that (hopefully). debug_assert!(clobber.len() == 64); if clobber.len() == 65 { - TrashData::::insert(0, [clobber[0] as u8; 1024]); + TrashData::::insert(0, [clobber[0] as u8; VALUE_SIZE]); } } @@ -288,5 +343,17 @@ pub mod pallet { Some(i) => Ok(i as u32), } } + + /// Generate a pseudo-random deterministic value from a `seed`. + pub(crate) fn gen_value(seed: u32) -> [u8; VALUE_SIZE] { + let mut ret = [0u8; VALUE_SIZE]; + + for i in 0u32..(VALUE_SIZE as u32 / 32) { + let hash = (seed, i).using_encoded(twox_256); + ret[i as usize * 32..(i + 1) as usize * 32].copy_from_slice(&hash); + } + + ret + } } } diff --git a/frame/glutton/src/mock.rs b/frame/glutton/src/mock.rs index c8be354f4..8c331dc97 100644 --- a/frame/glutton/src/mock.rs +++ b/frame/glutton/src/mock.rs @@ -68,6 +68,7 @@ impl frame_system::Config for Test { impl Config for Test { type RuntimeEvent = RuntimeEvent; + type AdminOrigin = frame_system::EnsureRoot; type WeightInfo = (); } diff --git a/frame/glutton/src/tests.rs b/frame/glutton/src/tests.rs index d75f2da5c..ba215e1ee 100644 --- a/frame/glutton/src/tests.rs +++ b/frame/glutton/src/tests.rs @@ -43,8 +43,8 @@ fn initialize_pallet_works() { Error::::AlreadyInitialized ); - assert_eq!(TrashData::::get(0), Some([0; 1024])); - assert_eq!(TrashData::::get(1), Some([1; 1024])); + assert_eq!(TrashData::::get(0), Some(Pallet::::gen_value(0))); + assert_eq!(TrashData::::get(1), Some(Pallet::::gen_value(1))); assert_eq!(TrashData::::get(2), None); assert_eq!(TrashDataCount::::get(), 2); @@ -224,3 +224,14 @@ fn waste_at_most_proof_size_weight_close_enough() { ); }); } + +#[test] +fn gen_value_works() { + let g0 = Pallet::::gen_value(0); + let g1 = Pallet::::gen_value(1); + + assert_eq!(g0.len(), VALUE_SIZE); + assert_ne!(g0, g1, "Is distinct"); + assert_ne!(g0, [0; VALUE_SIZE], "Is not zero"); + assert_eq!(g0, Pallet::::gen_value(0), "Is deterministic"); +} From 820d1961aee940b94deb23d38de076a1dfa66ff2 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 23 May 2023 15:55:41 +0200 Subject: [PATCH 535/558] Bump syn from 2.0.15 to 2.0.16 (#14196) Bumps [syn](https://github.com/dtolnay/syn) from 2.0.15 to 2.0.16. - [Release notes](https://github.com/dtolnay/syn/releases) - [Commits](https://github.com/dtolnay/syn/compare/2.0.15...2.0.16) --- updated-dependencies: - dependency-name: syn dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- Cargo.lock | 74 +++++++++---------- client/chain-spec/derive/Cargo.toml | 2 +- client/tracing/proc-macro/Cargo.toml | 2 +- frame/contracts/proc-macro/Cargo.toml | 2 +- .../solution-type/Cargo.toml | 2 +- frame/staking/reward-curve/Cargo.toml | 2 +- frame/support/procedural/Cargo.toml | 2 +- frame/support/procedural/tools/Cargo.toml | 2 +- .../procedural/tools/derive/Cargo.toml | 2 +- primitives/api/proc-macro/Cargo.toml | 2 +- primitives/core/hashing/proc-macro/Cargo.toml | 2 +- primitives/debug-derive/Cargo.toml | 2 +- .../runtime-interface/proc-macro/Cargo.toml | 2 +- primitives/version/proc-macro/Cargo.toml | 2 +- test-utils/derive/Cargo.toml | 2 +- 15 files changed, 51 insertions(+), 51 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 59ccf359f..641ea2a2b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -583,7 +583,7 @@ checksum = "0e97ce7de6cf12de5d7226c73f5ba9811622f4db3a5b91b55c53e987e5f91cba" dependencies = [ "proc-macro2", "quote", - "syn 2.0.15", + "syn 2.0.16", ] [[package]] @@ -605,7 +605,7 @@ checksum = "16e62a023e7c117e27523144c5d2459f4397fcc3cab0085af8e2224f643a0193" dependencies = [ "proc-macro2", "quote", - "syn 2.0.15", + "syn 2.0.16", ] [[package]] @@ -616,7 +616,7 @@ checksum = "b9ccdd8f2a161be9bd5c023df56f1b2a0bd1d83872ae53b71a84a12c9bf6e842" dependencies = [ "proc-macro2", "quote", - "syn 2.0.15", + "syn 2.0.16", ] [[package]] @@ -763,7 +763,7 @@ dependencies = [ "regex", "rustc-hash", "shlex", - "syn 2.0.15", + "syn 2.0.16", ] [[package]] @@ -1253,7 +1253,7 @@ dependencies = [ "heck", "proc-macro2", "quote", - "syn 2.0.15", + "syn 2.0.16", ] [[package]] @@ -1738,7 +1738,7 @@ dependencies = [ "proc-macro2", "quote", "scratch", - "syn 2.0.15", + "syn 2.0.16", ] [[package]] @@ -1755,7 +1755,7 @@ checksum = "2345488264226bf682893e25de0769f3360aac9957980ec49361b083ddaa5bc5" dependencies = [ "proc-macro2", "quote", - "syn 2.0.15", + "syn 2.0.16", ] [[package]] @@ -2023,7 +2023,7 @@ checksum = "487585f4d0c6655fe74905e2504d8ad6908e4db67f744eb140876906c2f3175d" dependencies = [ "proc-macro2", "quote", - "syn 2.0.15", + "syn 2.0.16", ] [[package]] @@ -2227,7 +2227,7 @@ checksum = "5e9a1f9f7d83e59740248a6e14ecf93929ade55027844dfcea78beafccc15745" dependencies = [ "proc-macro2", "quote", - "syn 2.0.15", + "syn 2.0.16", ] [[package]] @@ -2581,7 +2581,7 @@ dependencies = [ "quote", "scale-info", "sp-arithmetic", - "syn 2.0.15", + "syn 2.0.16", "trybuild", ] @@ -2725,7 +2725,7 @@ dependencies = [ "proc-macro-warning", "proc-macro2", "quote", - "syn 2.0.15", + "syn 2.0.16", ] [[package]] @@ -2736,7 +2736,7 @@ dependencies = [ "proc-macro-crate", "proc-macro2", "quote", - "syn 2.0.15", + "syn 2.0.16", ] [[package]] @@ -2745,7 +2745,7 @@ version = "3.0.0" dependencies = [ "proc-macro2", "quote", - "syn 2.0.15", + "syn 2.0.16", ] [[package]] @@ -2963,7 +2963,7 @@ checksum = "89ca545a94061b6365f2c7355b4b32bd20df3ff95f02da9329b34ccc3bd6ee72" dependencies = [ "proc-macro2", "quote", - "syn 2.0.15", + "syn 2.0.16", ] [[package]] @@ -6226,7 +6226,7 @@ version = "4.0.0-dev" dependencies = [ "proc-macro2", "quote", - "syn 2.0.15", + "syn 2.0.16", ] [[package]] @@ -7169,7 +7169,7 @@ dependencies = [ "proc-macro2", "quote", "sp-runtime", - "syn 2.0.15", + "syn 2.0.16", ] [[package]] @@ -7655,7 +7655,7 @@ dependencies = [ "pest_meta", "proc-macro2", "quote", - "syn 2.0.15", + "syn 2.0.16", ] [[package]] @@ -7926,7 +7926,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1ceca8aaf45b5c46ec7ed39fff75f57290368c1846d33d24a122ca81416ab058" dependencies = [ "proc-macro2", - "syn 2.0.15", + "syn 2.0.16", ] [[package]] @@ -7984,7 +7984,7 @@ checksum = "0e99670bafb56b9a106419397343bdbc8b8742c3cc449fec6345f86173f47cd4" dependencies = [ "proc-macro2", "quote", - "syn 2.0.15", + "syn 2.0.16", ] [[package]] @@ -8366,7 +8366,7 @@ checksum = "8d2275aab483050ab2a7364c1a46604865ee7d6906684e08db0f090acf74f9e7" dependencies = [ "proc-macro2", "quote", - "syn 2.0.15", + "syn 2.0.16", ] [[package]] @@ -8829,7 +8829,7 @@ dependencies = [ "proc-macro-crate", "proc-macro2", "quote", - "syn 2.0.15", + "syn 2.0.16", ] [[package]] @@ -10107,7 +10107,7 @@ dependencies = [ "proc-macro-crate", "proc-macro2", "quote", - "syn 2.0.15", + "syn 2.0.16", ] [[package]] @@ -10409,7 +10409,7 @@ checksum = "a2a0814352fd64b58489904a44ea8d90cb1a91dcb6b4f5ebabc32c8318e93cb6" dependencies = [ "proc-macro2", "quote", - "syn 2.0.15", + "syn 2.0.16", ] [[package]] @@ -10667,7 +10667,7 @@ dependencies = [ "proc-macro-crate", "proc-macro2", "quote", - "syn 2.0.15", + "syn 2.0.16", ] [[package]] @@ -10964,7 +10964,7 @@ dependencies = [ "proc-macro2", "quote", "sp-core-hashing", - "syn 2.0.15", + "syn 2.0.16", ] [[package]] @@ -10981,7 +10981,7 @@ version = "5.0.0" dependencies = [ "proc-macro2", "quote", - "syn 2.0.15", + "syn 2.0.16", ] [[package]] @@ -11209,7 +11209,7 @@ dependencies = [ "proc-macro-crate", "proc-macro2", "quote", - "syn 2.0.15", + "syn 2.0.16", ] [[package]] @@ -11445,7 +11445,7 @@ dependencies = [ "proc-macro2", "quote", "sp-version", - "syn 2.0.15", + "syn 2.0.16", ] [[package]] @@ -11888,7 +11888,7 @@ dependencies = [ "proc-macro-crate", "proc-macro2", "quote", - "syn 2.0.15", + "syn 2.0.16", ] [[package]] @@ -11944,9 +11944,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.15" +version = "2.0.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a34fcf3e8b60f57e6a14301a2e916d323af98b0ea63c599441eec8558660c822" +checksum = "a6f671d4b5ffdb8eadec19c0ae67fe2639df8684bd7bc4b83d986b8db549cf01" dependencies = [ "proc-macro2", "quote", @@ -12060,7 +12060,7 @@ checksum = "f9456a42c5b0d803c8cd86e73dd7cc9edd429499f37a3550d286d5e86720569f" dependencies = [ "proc-macro2", "quote", - "syn 2.0.15", + "syn 2.0.16", ] [[package]] @@ -12216,7 +12216,7 @@ checksum = "630bdcf245f78637c13ec01ffae6187cca34625e8c63150d424b59e55af2675e" dependencies = [ "proc-macro2", "quote", - "syn 2.0.15", + "syn 2.0.16", ] [[package]] @@ -12386,7 +12386,7 @@ checksum = "0f57e3ca2a01450b1a921183a9c9cbfda207fd822cef4ccb00a65402cbba7a74" dependencies = [ "proc-macro2", "quote", - "syn 2.0.15", + "syn 2.0.16", ] [[package]] @@ -12916,7 +12916,7 @@ dependencies = [ "once_cell", "proc-macro2", "quote", - "syn 2.0.15", + "syn 2.0.16", "wasm-bindgen-shared", ] @@ -12950,7 +12950,7 @@ checksum = "4783ce29f09b9d93134d41297aded3a712b7b979e9c6f28c32cb88c973a94869" dependencies = [ "proc-macro2", "quote", - "syn 2.0.15", + "syn 2.0.16", "wasm-bindgen-backend", "wasm-bindgen-shared", ] @@ -13972,7 +13972,7 @@ checksum = "ce36e65b0d2999d2aafac989fb249189a141aee1f53c612c1f37d72631959f69" dependencies = [ "proc-macro2", "quote", - "syn 2.0.15", + "syn 2.0.16", ] [[package]] diff --git a/client/chain-spec/derive/Cargo.toml b/client/chain-spec/derive/Cargo.toml index c16cd45a9..ff686857e 100644 --- a/client/chain-spec/derive/Cargo.toml +++ b/client/chain-spec/derive/Cargo.toml @@ -18,4 +18,4 @@ proc-macro = true proc-macro-crate = "1.1.3" proc-macro2 = "1.0.56" quote = "1.0.26" -syn = "2.0.14" +syn = "2.0.16" diff --git a/client/tracing/proc-macro/Cargo.toml b/client/tracing/proc-macro/Cargo.toml index 54078fe1f..c602df73f 100644 --- a/client/tracing/proc-macro/Cargo.toml +++ b/client/tracing/proc-macro/Cargo.toml @@ -18,4 +18,4 @@ proc-macro = true proc-macro-crate = "1.1.3" proc-macro2 = "1.0.56" quote = { version = "1.0.26", features = ["proc-macro"] } -syn = { version = "2.0.14", features = ["proc-macro", "full", "extra-traits", "parsing"] } +syn = { version = "2.0.16", features = ["proc-macro", "full", "extra-traits", "parsing"] } diff --git a/frame/contracts/proc-macro/Cargo.toml b/frame/contracts/proc-macro/Cargo.toml index c700d1e33..b8d80420b 100644 --- a/frame/contracts/proc-macro/Cargo.toml +++ b/frame/contracts/proc-macro/Cargo.toml @@ -17,7 +17,7 @@ proc-macro = true [dependencies] proc-macro2 = "1.0.56" quote = "1.0.26" -syn = { version = "2.0.14", features = ["full"] } +syn = { version = "2.0.16", features = ["full"] } [dev-dependencies] diff --git a/frame/election-provider-support/solution-type/Cargo.toml b/frame/election-provider-support/solution-type/Cargo.toml index 95ad6f226..ebe63ab46 100644 --- a/frame/election-provider-support/solution-type/Cargo.toml +++ b/frame/election-provider-support/solution-type/Cargo.toml @@ -15,7 +15,7 @@ targets = ["x86_64-unknown-linux-gnu"] proc-macro = true [dependencies] -syn = { version = "2.0.14", features = ["full", "visit"] } +syn = { version = "2.0.16", features = ["full", "visit"] } quote = "1.0.26" proc-macro2 = "1.0.56" proc-macro-crate = "1.1.3" diff --git a/frame/staking/reward-curve/Cargo.toml b/frame/staking/reward-curve/Cargo.toml index 4a97d20a5..6cd6323ac 100644 --- a/frame/staking/reward-curve/Cargo.toml +++ b/frame/staking/reward-curve/Cargo.toml @@ -18,7 +18,7 @@ proc-macro = true proc-macro-crate = "1.1.3" proc-macro2 = "1.0.56" quote = "1.0.26" -syn = { version = "2.0.14", features = ["full", "visit"] } +syn = { version = "2.0.16", features = ["full", "visit"] } [dev-dependencies] sp-runtime = { version = "7.0.0", path = "../../../primitives/runtime" } diff --git a/frame/support/procedural/Cargo.toml b/frame/support/procedural/Cargo.toml index 8e18376ba..e6fe140b5 100644 --- a/frame/support/procedural/Cargo.toml +++ b/frame/support/procedural/Cargo.toml @@ -21,7 +21,7 @@ cfg-expr = "0.15.1" itertools = "0.10.3" proc-macro2 = "1.0.56" quote = "1.0.26" -syn = { version = "2.0.14", features = ["full"] } +syn = { version = "2.0.16", features = ["full"] } frame-support-procedural-tools = { version = "4.0.0-dev", path = "./tools" } proc-macro-warning = { version = "0.3.0", default-features = false } diff --git a/frame/support/procedural/tools/Cargo.toml b/frame/support/procedural/tools/Cargo.toml index bf828b01b..4eeedbdd5 100644 --- a/frame/support/procedural/tools/Cargo.toml +++ b/frame/support/procedural/tools/Cargo.toml @@ -15,5 +15,5 @@ targets = ["x86_64-unknown-linux-gnu"] proc-macro-crate = "1.1.3" proc-macro2 = "1.0.56" quote = "1.0.26" -syn = { version = "2.0.14", features = ["full", "visit", "extra-traits"] } +syn = { version = "2.0.16", features = ["full", "visit", "extra-traits"] } frame-support-procedural-tools-derive = { version = "3.0.0", path = "./derive" } diff --git a/frame/support/procedural/tools/derive/Cargo.toml b/frame/support/procedural/tools/derive/Cargo.toml index 81b5a8487..425b4bf2d 100644 --- a/frame/support/procedural/tools/derive/Cargo.toml +++ b/frame/support/procedural/tools/derive/Cargo.toml @@ -17,4 +17,4 @@ proc-macro = true [dependencies] proc-macro2 = "1.0.56" quote = { version = "1.0.26", features = ["proc-macro"] } -syn = { version = "2.0.14", features = ["proc-macro", "full", "extra-traits", "parsing"] } +syn = { version = "2.0.16", features = ["proc-macro", "full", "extra-traits", "parsing"] } diff --git a/primitives/api/proc-macro/Cargo.toml b/primitives/api/proc-macro/Cargo.toml index 9d721950f..d23007626 100644 --- a/primitives/api/proc-macro/Cargo.toml +++ b/primitives/api/proc-macro/Cargo.toml @@ -17,7 +17,7 @@ proc-macro = true [dependencies] quote = "1.0.26" -syn = { version = "2.0.14", features = ["full", "fold", "extra-traits", "visit"] } +syn = { version = "2.0.16", features = ["full", "fold", "extra-traits", "visit"] } proc-macro2 = "1.0.56" blake2 = { version = "0.10.4", default-features = false } proc-macro-crate = "1.1.3" diff --git a/primitives/core/hashing/proc-macro/Cargo.toml b/primitives/core/hashing/proc-macro/Cargo.toml index 66e9667c8..5a8033204 100644 --- a/primitives/core/hashing/proc-macro/Cargo.toml +++ b/primitives/core/hashing/proc-macro/Cargo.toml @@ -18,5 +18,5 @@ proc-macro = true [dependencies] proc-macro2 = "1.0.56" quote = "1.0.26" -syn = { version = "2.0.14", features = ["full", "parsing"] } +syn = { version = "2.0.16", features = ["full", "parsing"] } sp-core-hashing = { version = "5.0.0", default-features = false, path = "../" } diff --git a/primitives/debug-derive/Cargo.toml b/primitives/debug-derive/Cargo.toml index 183d7bd5a..f7ca95b19 100644 --- a/primitives/debug-derive/Cargo.toml +++ b/primitives/debug-derive/Cargo.toml @@ -18,7 +18,7 @@ proc-macro = true [dependencies] quote = "1.0.26" -syn = "2.0.14" +syn = "2.0.16" proc-macro2 = "1.0.56" [features] diff --git a/primitives/runtime-interface/proc-macro/Cargo.toml b/primitives/runtime-interface/proc-macro/Cargo.toml index 3a63c1ef5..3f1a18ee8 100644 --- a/primitives/runtime-interface/proc-macro/Cargo.toml +++ b/primitives/runtime-interface/proc-macro/Cargo.toml @@ -20,4 +20,4 @@ Inflector = "0.11.4" proc-macro-crate = "1.1.3" proc-macro2 = "1.0.56" quote = "1.0.26" -syn = { version = "2.0.14", features = ["full", "visit", "fold", "extra-traits"] } +syn = { version = "2.0.16", features = ["full", "visit", "fold", "extra-traits"] } diff --git a/primitives/version/proc-macro/Cargo.toml b/primitives/version/proc-macro/Cargo.toml index aac41dd43..50095bd71 100644 --- a/primitives/version/proc-macro/Cargo.toml +++ b/primitives/version/proc-macro/Cargo.toml @@ -19,7 +19,7 @@ proc-macro = true codec = { package = "parity-scale-codec", version = "3.2.2", features = [ "derive" ] } proc-macro2 = "1.0.56" quote = "1.0.26" -syn = { version = "2.0.14", features = ["full", "fold", "extra-traits", "visit"] } +syn = { version = "2.0.16", features = ["full", "fold", "extra-traits", "visit"] } [dev-dependencies] sp-version = { version = "5.0.0", path = ".." } diff --git a/test-utils/derive/Cargo.toml b/test-utils/derive/Cargo.toml index f3e38ada2..da357e2cb 100644 --- a/test-utils/derive/Cargo.toml +++ b/test-utils/derive/Cargo.toml @@ -13,7 +13,7 @@ publish = false proc-macro-crate = "1.1.3" proc-macro2 = "1.0.56" quote = "1.0.26" -syn = { version = "2.0.14", features = ["full"] } +syn = { version = "2.0.16", features = ["full"] } [lib] proc-macro = true From 2e494e80fbd3f02078dfb370883e446ee27a7850 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bastian=20K=C3=B6cher?= Date: Tue, 23 May 2023 16:05:52 +0200 Subject: [PATCH 536/558] TrieCache: Fine tune the size of the local and node cache (#14191) First, we increase the size to 10MiB for the local cache. Second, we give the node cache a bigger max size than the value cache, see the changed comment on why. In general this should ensure that we are able to cache the `:code` and not directly throw it out of the cache because it is too big (which currently happens when the size of the runtime > 2MiB). In the future this should be improved to ensure that certain values are not removed from the cache at all, like `:code`. --- primitives/trie/src/cache/mod.rs | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/primitives/trie/src/cache/mod.rs b/primitives/trie/src/cache/mod.rs index 0100e2876..380ce4b8a 100644 --- a/primitives/trie/src/cache/mod.rs +++ b/primitives/trie/src/cache/mod.rs @@ -91,9 +91,13 @@ const LOCAL_NODE_CACHE_MAX_INLINE_SIZE: usize = 512 * 1024; const LOCAL_VALUE_CACHE_MAX_INLINE_SIZE: usize = 512 * 1024; /// The maximum size of the memory allocated on the heap by the local cache, in bytes. -const LOCAL_NODE_CACHE_MAX_HEAP_SIZE: usize = 2 * 1024 * 1024; +/// +/// The size of the node cache should always be bigger than the value cache. The value +/// cache is only holding weak references to the actual values found in the nodes and +/// we account for the size of the node as part of the node cache. +const LOCAL_NODE_CACHE_MAX_HEAP_SIZE: usize = 8 * 1024 * 1024; /// Same as [`LOCAL_NODE_CACHE_MAX_HEAP_SIZE`]. -const LOCAL_VALUE_CACHE_MAX_HEAP_SIZE: usize = 4 * 1024 * 1024; +const LOCAL_VALUE_CACHE_MAX_HEAP_SIZE: usize = 2 * 1024 * 1024; /// The size of the shared cache. #[derive(Debug, Clone, Copy)] From 32ab5cbeb29f68a1977e4803fe8c0731fe5c99be Mon Sep 17 00:00:00 2001 From: Squirrel Date: Tue, 23 May 2023 16:06:48 +0100 Subject: [PATCH 537/558] Easy PR: Fix warnings from latest nightly (#14195) * unneeded mut * remove needless borrows --- bin/node/bench/src/tempdb.rs | 2 +- client/db/src/parity_db.rs | 6 +++--- client/db/src/pinned_blocks_cache.rs | 4 ++-- .../network/src/protocol/notifications/handler.rs | 2 +- client/network/sync/src/state.rs | 2 +- client/statement-store/src/lib.rs | 4 ++-- frame/bounties/src/lib.rs | 10 +++++----- frame/child-bounties/src/lib.rs | 8 ++++---- frame/nomination-pools/benchmarking/src/lib.rs | 2 +- frame/staking/src/pallet/mod.rs | 4 +--- frame/support/src/storage/generator/double_map.rs | 14 +++++++------- frame/support/src/storage/generator/map.rs | 4 ++-- primitives/npos-elections/src/lib.rs | 2 +- 13 files changed, 31 insertions(+), 33 deletions(-) diff --git a/bin/node/bench/src/tempdb.rs b/bin/node/bench/src/tempdb.rs index 2aafd013a..f3fd693d2 100644 --- a/bin/node/bench/src/tempdb.rs +++ b/bin/node/bench/src/tempdb.rs @@ -90,7 +90,7 @@ impl TempDatabase { }, DatabaseType::ParityDb => Arc::new(ParityDbWrapper({ let mut options = parity_db::Options::with_columns(self.0.path(), 1); - let mut column_options = &mut options.columns[0]; + let column_options = &mut options.columns[0]; column_options.ref_counted = true; column_options.preimage = true; column_options.uniform = true; diff --git a/client/db/src/parity_db.rs b/client/db/src/parity_db.rs index 01562081a..b7068f243 100644 --- a/client/db/src/parity_db.rs +++ b/client/db/src/parity_db.rs @@ -54,16 +54,16 @@ pub fn open>( ]; for i in compressed { - let mut column = &mut config.columns[i as usize]; + let column = &mut config.columns[i as usize]; column.compression = parity_db::CompressionType::Lz4; } - let mut state_col = &mut config.columns[columns::STATE as usize]; + let state_col = &mut config.columns[columns::STATE as usize]; state_col.ref_counted = true; state_col.preimage = true; state_col.uniform = true; - let mut tx_col = &mut config.columns[columns::TRANSACTION as usize]; + let tx_col = &mut config.columns[columns::TRANSACTION as usize]; tx_col.ref_counted = true; tx_col.preimage = true; tx_col.uniform = true; diff --git a/client/db/src/pinned_blocks_cache.rs b/client/db/src/pinned_blocks_cache.rs index 7b346b463..46c9287fb 100644 --- a/client/db/src/pinned_blocks_cache.rs +++ b/client/db/src/pinned_blocks_cache.rs @@ -168,7 +168,7 @@ impl PinnedBlocksCache { /// Attach body to an existing cache item pub fn insert_body(&mut self, hash: Block::Hash, extrinsics: Option>) { match self.cache.peek_mut(&hash) { - Some(mut entry) => { + Some(entry) => { entry.body = Some(extrinsics); log::trace!( target: LOG_TARGET, @@ -192,7 +192,7 @@ impl PinnedBlocksCache { justifications: Option, ) { match self.cache.peek_mut(&hash) { - Some(mut entry) => { + Some(entry) => { entry.justifications = Some(justifications); log::trace!( target: LOG_TARGET, diff --git a/client/network/src/protocol/notifications/handler.rs b/client/network/src/protocol/notifications/handler.rs index 665b646ec..0ac2e250a 100644 --- a/client/network/src/protocol/notifications/handler.rs +++ b/client/network/src/protocol/notifications/handler.rs @@ -467,7 +467,7 @@ impl ConnectionHandler for NotifsHandler { match event { ConnectionEvent::FullyNegotiatedInbound(inbound) => { let (mut in_substream_open, protocol_index) = inbound.protocol; - let mut protocol_info = &mut self.protocols[protocol_index]; + let protocol_info = &mut self.protocols[protocol_index]; match protocol_info.state { State::Closed { pending_opening } => { diff --git a/client/network/sync/src/state.rs b/client/network/sync/src/state.rs index 0fcf17158..305f0ee68 100644 --- a/client/network/sync/src/state.rs +++ b/client/network/sync/src/state.rs @@ -150,7 +150,7 @@ where } else { values.key_values }; - let mut entry = self.state.entry(values.state_root).or_default(); + let entry = self.state.entry(values.state_root).or_default(); if entry.0.len() > 0 && entry.1.len() > 1 { // Already imported child_trie with same root. // Warning this will not work with parallel download. diff --git a/client/statement-store/src/lib.rs b/client/statement-store/src/lib.rs index 2e2bb3bd3..4acb89a05 100644 --- a/client/statement-store/src/lib.rs +++ b/client/statement-store/src/lib.rs @@ -236,7 +236,7 @@ impl Index { let priority = Priority(statement.priority().unwrap_or(0)); self.entries.insert(hash, (account, priority, statement.data_len())); self.total_size += statement.data_len(); - let mut account_info = self.accounts.entry(account).or_default(); + let account_info = self.accounts.entry(account).or_default(); account_info.data_size += statement.data_len(); if let Some(channel) = statement.channel() { account_info.channels.insert(channel, ChannelEntry { hash, priority }); @@ -530,7 +530,7 @@ impl Store { let mut config = parity_db::Options::with_columns(&path, col::COUNT); - let mut statement_col = &mut config.columns[col::STATEMENTS as usize]; + let statement_col = &mut config.columns[col::STATEMENTS as usize]; statement_col.ref_counted = false; statement_col.preimage = true; statement_col.uniform = true; diff --git a/frame/bounties/src/lib.rs b/frame/bounties/src/lib.rs index 14f7b45cb..07ac23a9d 100644 --- a/frame/bounties/src/lib.rs +++ b/frame/bounties/src/lib.rs @@ -361,7 +361,7 @@ pub mod pallet { ) -> DispatchResult { let max_amount = T::SpendOrigin::ensure_origin(origin)?; Bounties::::try_mutate_exists(bounty_id, |maybe_bounty| -> DispatchResult { - let mut bounty = maybe_bounty.as_mut().ok_or(Error::::InvalidIndex)?; + let bounty = maybe_bounty.as_mut().ok_or(Error::::InvalidIndex)?; ensure!( bounty.value <= max_amount, pallet_treasury::Error::::InsufficientPermission @@ -396,7 +396,7 @@ pub mod pallet { let curator = T::Lookup::lookup(curator)?; Bounties::::try_mutate_exists(bounty_id, |maybe_bounty| -> DispatchResult { - let mut bounty = maybe_bounty.as_mut().ok_or(Error::::InvalidIndex)?; + let bounty = maybe_bounty.as_mut().ok_or(Error::::InvalidIndex)?; ensure!( bounty.value <= max_amount, pallet_treasury::Error::::InsufficientPermission @@ -444,7 +444,7 @@ pub mod pallet { .or_else(|_| T::RejectOrigin::ensure_origin(origin).map(|_| None))?; Bounties::::try_mutate_exists(bounty_id, |maybe_bounty| -> DispatchResult { - let mut bounty = maybe_bounty.as_mut().ok_or(Error::::InvalidIndex)?; + let bounty = maybe_bounty.as_mut().ok_or(Error::::InvalidIndex)?; let slash_curator = |curator: &T::AccountId, curator_deposit: &mut BalanceOf| { @@ -527,7 +527,7 @@ pub mod pallet { let signer = ensure_signed(origin)?; Bounties::::try_mutate_exists(bounty_id, |maybe_bounty| -> DispatchResult { - let mut bounty = maybe_bounty.as_mut().ok_or(Error::::InvalidIndex)?; + let bounty = maybe_bounty.as_mut().ok_or(Error::::InvalidIndex)?; match bounty.status { BountyStatus::CuratorProposed { ref curator } => { @@ -571,7 +571,7 @@ pub mod pallet { let beneficiary = T::Lookup::lookup(beneficiary)?; Bounties::::try_mutate_exists(bounty_id, |maybe_bounty| -> DispatchResult { - let mut bounty = maybe_bounty.as_mut().ok_or(Error::::InvalidIndex)?; + let bounty = maybe_bounty.as_mut().ok_or(Error::::InvalidIndex)?; // Ensure no active child bounties before processing the call. ensure!( diff --git a/frame/child-bounties/src/lib.rs b/frame/child-bounties/src/lib.rs index 094b41822..14c1907af 100644 --- a/frame/child-bounties/src/lib.rs +++ b/frame/child-bounties/src/lib.rs @@ -331,7 +331,7 @@ pub mod pallet { parent_bounty_id, child_bounty_id, |maybe_child_bounty| -> DispatchResult { - let mut child_bounty = + let child_bounty = maybe_child_bounty.as_mut().ok_or(BountiesError::::InvalidIndex)?; // Ensure child-bounty is in expected state. @@ -396,7 +396,7 @@ pub mod pallet { parent_bounty_id, child_bounty_id, |maybe_child_bounty| -> DispatchResult { - let mut child_bounty = + let child_bounty = maybe_child_bounty.as_mut().ok_or(BountiesError::::InvalidIndex)?; // Ensure child-bounty is in expected state. @@ -473,7 +473,7 @@ pub mod pallet { parent_bounty_id, child_bounty_id, |maybe_child_bounty| -> DispatchResult { - let mut child_bounty = + let child_bounty = maybe_child_bounty.as_mut().ok_or(BountiesError::::InvalidIndex)?; let slash_curator = |curator: &T::AccountId, @@ -591,7 +591,7 @@ pub mod pallet { parent_bounty_id, child_bounty_id, |maybe_child_bounty| -> DispatchResult { - let mut child_bounty = + let child_bounty = maybe_child_bounty.as_mut().ok_or(BountiesError::::InvalidIndex)?; // Ensure child-bounty is in active state. diff --git a/frame/nomination-pools/benchmarking/src/lib.rs b/frame/nomination-pools/benchmarking/src/lib.rs index 137b9e9af..30bef6221 100644 --- a/frame/nomination-pools/benchmarking/src/lib.rs +++ b/frame/nomination-pools/benchmarking/src/lib.rs @@ -609,7 +609,7 @@ frame_benchmarking::benchmarks! { let (depositor, pool_account) = create_pool_account::(0, min_create_bond, None); BondedPools::::mutate(&1, |maybe_pool| { // Force the pool into an invalid state - maybe_pool.as_mut().map(|mut pool| pool.points = min_create_bond * 10u32.into()); + maybe_pool.as_mut().map(|pool| pool.points = min_create_bond * 10u32.into()); }); let caller = account("caller", 0, USER_SEED); diff --git a/frame/staking/src/pallet/mod.rs b/frame/staking/src/pallet/mod.rs index 35aa2626e..d8a7de219 100644 --- a/frame/staking/src/pallet/mod.rs +++ b/frame/staking/src/pallet/mod.rs @@ -1006,9 +1006,7 @@ pub mod pallet { // Note: in case there is no current era it is fine to bond one era more. let era = Self::current_era().unwrap_or(0) + T::BondingDuration::get(); - if let Some(mut chunk) = - ledger.unlocking.last_mut().filter(|chunk| chunk.era == era) - { + if let Some(chunk) = ledger.unlocking.last_mut().filter(|chunk| chunk.era == era) { // To keep the chunk count down, we only keep one chunk per era. Since // `unlocking` is a FiFo queue, if a chunk exists for `era` we know that it will // be the last one. diff --git a/frame/support/src/storage/generator/double_map.rs b/frame/support/src/storage/generator/double_map.rs index 5da68873b..00a3f1bc7 100644 --- a/frame/support/src/storage/generator/double_map.rs +++ b/frame/support/src/storage/generator/double_map.rs @@ -21,7 +21,7 @@ use crate::{ Never, }; use codec::{Decode, Encode, EncodeLike, FullCodec, FullEncode}; -use sp_std::{borrow::Borrow, prelude::*}; +use sp_std::prelude::*; /// Generator for `StorageDoubleMap` used by `decl_storage`. /// @@ -78,7 +78,7 @@ pub trait StorageDoubleMap { KArg1: EncodeLike, { let storage_prefix = storage_prefix(Self::module_prefix(), Self::storage_prefix()); - let key_hashed = k1.borrow().using_encoded(Self::Hasher1::hash); + let key_hashed = k1.using_encoded(Self::Hasher1::hash); let mut final_key = Vec::with_capacity(storage_prefix.len() + key_hashed.as_ref().len()); @@ -95,8 +95,8 @@ pub trait StorageDoubleMap { KArg2: EncodeLike, { let storage_prefix = storage_prefix(Self::module_prefix(), Self::storage_prefix()); - let key1_hashed = k1.borrow().using_encoded(Self::Hasher1::hash); - let key2_hashed = k2.borrow().using_encoded(Self::Hasher2::hash); + let key1_hashed = k1.using_encoded(Self::Hasher1::hash); + let key2_hashed = k2.using_encoded(Self::Hasher2::hash); let mut final_key = Vec::with_capacity( storage_prefix.len() + key1_hashed.as_ref().len() + key2_hashed.as_ref().len(), @@ -198,7 +198,7 @@ where KArg2: EncodeLike, VArg: EncodeLike, { - unhashed::put(&Self::storage_double_map_final_key(k1, k2), &val.borrow()) + unhashed::put(&Self::storage_double_map_final_key(k1, k2), &val) } fn remove(k1: KArg1, k2: KArg2) @@ -336,8 +336,8 @@ where let old_key = { let storage_prefix = storage_prefix(Self::module_prefix(), Self::storage_prefix()); - let key1_hashed = key1.borrow().using_encoded(OldHasher1::hash); - let key2_hashed = key2.borrow().using_encoded(OldHasher2::hash); + let key1_hashed = key1.using_encoded(OldHasher1::hash); + let key2_hashed = key2.using_encoded(OldHasher2::hash); let mut final_key = Vec::with_capacity( storage_prefix.len() + key1_hashed.as_ref().len() + key2_hashed.as_ref().len(), diff --git a/frame/support/src/storage/generator/map.rs b/frame/support/src/storage/generator/map.rs index 3b36b9bdd..3cd7bea2f 100644 --- a/frame/support/src/storage/generator/map.rs +++ b/frame/support/src/storage/generator/map.rs @@ -68,7 +68,7 @@ pub trait StorageMap { KeyArg: EncodeLike, { let storage_prefix = storage_prefix(Self::module_prefix(), Self::storage_prefix()); - let key_hashed = key.borrow().using_encoded(Self::Hasher::hash); + let key_hashed = key.using_encoded(Self::Hasher::hash); let mut final_key = Vec::with_capacity(storage_prefix.len() + key_hashed.as_ref().len()); @@ -327,7 +327,7 @@ impl> storage::StorageMap fn migrate_key>(key: KeyArg) -> Option { let old_key = { let storage_prefix = storage_prefix(Self::module_prefix(), Self::storage_prefix()); - let key_hashed = key.borrow().using_encoded(OldHasher::hash); + let key_hashed = key.using_encoded(OldHasher::hash); let mut final_key = Vec::with_capacity(storage_prefix.len() + key_hashed.as_ref().len()); diff --git a/primitives/npos-elections/src/lib.rs b/primitives/npos-elections/src/lib.rs index 253a23160..9239a2d90 100644 --- a/primitives/npos-elections/src/lib.rs +++ b/primitives/npos-elections/src/lib.rs @@ -470,7 +470,7 @@ pub fn to_support_map( // build support struct. for StakedAssignment { who, distribution } in assignments.iter() { for (c, weight_extended) in distribution.iter() { - let mut support = supports.entry(c.clone()).or_default(); + let support = supports.entry(c.clone()).or_default(); support.total = support.total.saturating_add(*weight_extended); support.voters.push((who.clone(), *weight_extended)); } From 4dd06e43c49620585912fb2e7355a226501fdfec Mon Sep 17 00:00:00 2001 From: yjh Date: Tue, 23 May 2023 23:31:45 +0800 Subject: [PATCH 538/558] Remove wasmi backend from sc-executor (#13800) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * refactor: use builder api for all executors * improve a lot * remove unused args * cleanup deps * fix inconsistency about heap alloc * add `heap_pages` back to try-runtime * fix * chore: reduce duplicated code for sc-service-test * cleanup code * fmt * improve test executor * improve * use #[deprecated] * set runtime_cache_size: 4 * wip * fix and improve * remove sc-executor-wasmi deps * clean up bench and tests * delete "client/executor/wasmi" * cleanup * refactor builder * fix * fix bench * fix tests * fix warnings * fix warnings * fix * fix * remove wasmi and fix tests * unused imports * improve by suggestions * Update client/cli/src/arg_enums.rs --------- Co-authored-by: Bastian Köcher Co-authored-by: parity-processbot <> --- Cargo.lock | 34 +- Cargo.toml | 1 - bin/node-template/node/src/service.rs | 1 - bin/node/executor/benches/bench.rs | 1 - client/cli/src/arg_enums.rs | 35 +- client/executor/Cargo.toml | 2 - client/executor/benches/bench.rs | 11 +- client/executor/common/Cargo.toml | 1 - client/executor/common/src/error.rs | 10 - .../executor/common/src/runtime_blob/mod.rs | 3 - .../executor/src/integration_tests/linux.rs | 23 +- client/executor/src/integration_tests/mod.rs | 57 +- client/executor/src/lib.rs | 1 - client/executor/src/wasm_runtime.rs | 23 +- client/executor/wasmi/Cargo.toml | 22 - client/executor/wasmi/README.md | 3 - client/executor/wasmi/src/lib.rs | 599 ------------------ primitives/runtime-interface/test/src/lib.rs | 4 +- 18 files changed, 36 insertions(+), 795 deletions(-) delete mode 100644 client/executor/wasmi/Cargo.toml delete mode 100644 client/executor/wasmi/README.md delete mode 100644 client/executor/wasmi/src/lib.rs diff --git a/Cargo.lock b/Cargo.lock index 641ea2a2b..ae6557c1b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -7840,14 +7840,14 @@ version = "0.3.20" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e30165d31df606f5726b090ec7592c308a0eaf61721ff64c9a3018e344a8753e" dependencies = [ - "portable-atomic 1.3.1", + "portable-atomic 1.3.2", ] [[package]] name = "portable-atomic" -version = "1.3.1" +version = "1.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1bbda379e6e462c97ea6afe9f6233619b202bbc4968d7caa6917788d2070a044" +checksum = "dc59d1bcc64fc5d021d67521f818db868368028108d37f0e98d74e33f68297b5" [[package]] name = "ppv-lite86" @@ -8413,18 +8413,6 @@ version = "0.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a5996294f19bd3aae0453a862ad728f60e6600695733dd5df01da90c54363a3c" -[[package]] -name = "region" -version = "3.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "76e189c2369884dce920945e2ddf79b3dff49e071a167dd1817fa9c4c00d512e" -dependencies = [ - "bitflags", - "libc", - "mach", - "winapi", -] - [[package]] name = "resolv-conf" version = "0.7.0" @@ -9316,7 +9304,6 @@ dependencies = [ "paste", "regex", "sc-executor-common", - "sc-executor-wasmi", "sc-executor-wasmtime", "sc-runtime-test", "sc-tracing", @@ -9337,7 +9324,6 @@ dependencies = [ "tempfile", "tracing", "tracing-subscriber 0.2.25", - "wasmi 0.13.2", "wat", ] @@ -9350,19 +9336,6 @@ dependencies = [ "sp-wasm-interface", "thiserror", "wasm-instrument 0.3.0", - "wasmi 0.13.2", -] - -[[package]] -name = "sc-executor-wasmi" -version = "0.10.0-dev" -dependencies = [ - "log", - "sc-allocator", - "sc-executor-common", - "sp-runtime-interface", - "sp-wasm-interface", - "wasmi 0.13.2", ] [[package]] @@ -13092,7 +13065,6 @@ dependencies = [ "memory_units", "num-rational", "num-traits", - "region", ] [[package]] diff --git a/Cargo.toml b/Cargo.toml index 2996fb385..0ac6a9dfb 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -38,7 +38,6 @@ members = [ "client/executor", "client/executor/common", "client/executor/runtime-test", - "client/executor/wasmi", "client/executor/wasmtime", "client/informant", "client/keystore", diff --git a/bin/node-template/node/src/service.rs b/bin/node-template/node/src/service.rs index ca827001b..745b14073 100644 --- a/bin/node-template/node/src/service.rs +++ b/bin/node-template/node/src/service.rs @@ -69,7 +69,6 @@ pub fn new_partial( .transpose()?; let executor = sc_service::new_native_or_wasm_executor(&config); - let (client, backend, keystore_container, task_manager) = sc_service::new_full_parts::( config, diff --git a/bin/node/executor/benches/bench.rs b/bin/node/executor/benches/bench.rs index aa7d9eb0f..6b082744e 100644 --- a/bin/node/executor/benches/bench.rs +++ b/bin/node/executor/benches/bench.rs @@ -182,7 +182,6 @@ fn bench_execute_block(c: &mut Criterion) { let mut group = c.benchmark_group("execute blocks"); let execution_methods = vec![ ExecutionMethod::Native, - ExecutionMethod::Wasm(WasmExecutionMethod::Interpreted), ExecutionMethod::Wasm(WasmExecutionMethod::Compiled { instantiation_strategy: WasmtimeInstantiationStrategy::PoolingCopyOnWrite, }), diff --git a/client/cli/src/arg_enums.rs b/client/cli/src/arg_enums.rs index 7e2498cec..89cb45008 100644 --- a/client/cli/src/arg_enums.rs +++ b/client/cli/src/arg_enums.rs @@ -54,7 +54,7 @@ pub const DEFAULT_WASMTIME_INSTANTIATION_STRATEGY: WasmtimeInstantiationStrategy #[derive(Debug, Clone, Copy, ValueEnum)] #[value(rename_all = "kebab-case")] pub enum WasmExecutionMethod { - /// Uses an interpreter. + /// Uses an interpreter which now is deprecated. #[clap(name = "interpreted-i-know-what-i-do")] Interpreted, /// Uses a compiled runtime. @@ -76,21 +76,24 @@ pub fn execution_method_from_cli( execution_method: WasmExecutionMethod, instantiation_strategy: WasmtimeInstantiationStrategy, ) -> sc_service::config::WasmExecutionMethod { - match execution_method { - WasmExecutionMethod::Interpreted => sc_service::config::WasmExecutionMethod::Interpreted, - WasmExecutionMethod::Compiled => sc_service::config::WasmExecutionMethod::Compiled { - instantiation_strategy: match instantiation_strategy { - WasmtimeInstantiationStrategy::PoolingCopyOnWrite => - sc_service::config::WasmtimeInstantiationStrategy::PoolingCopyOnWrite, - WasmtimeInstantiationStrategy::RecreateInstanceCopyOnWrite => - sc_service::config::WasmtimeInstantiationStrategy::RecreateInstanceCopyOnWrite, - WasmtimeInstantiationStrategy::Pooling => - sc_service::config::WasmtimeInstantiationStrategy::Pooling, - WasmtimeInstantiationStrategy::RecreateInstance => - sc_service::config::WasmtimeInstantiationStrategy::RecreateInstance, - WasmtimeInstantiationStrategy::LegacyInstanceReuse => - sc_service::config::WasmtimeInstantiationStrategy::LegacyInstanceReuse, - }, + if let WasmExecutionMethod::Interpreted = execution_method { + log::warn!( + "`interpreted-i-know-what-i-do` is deprecated and will be removed in the future. Defaults to `compiled` execution mode." + ); + } + + sc_service::config::WasmExecutionMethod::Compiled { + instantiation_strategy: match instantiation_strategy { + WasmtimeInstantiationStrategy::PoolingCopyOnWrite => + sc_service::config::WasmtimeInstantiationStrategy::PoolingCopyOnWrite, + WasmtimeInstantiationStrategy::RecreateInstanceCopyOnWrite => + sc_service::config::WasmtimeInstantiationStrategy::RecreateInstanceCopyOnWrite, + WasmtimeInstantiationStrategy::Pooling => + sc_service::config::WasmtimeInstantiationStrategy::Pooling, + WasmtimeInstantiationStrategy::RecreateInstance => + sc_service::config::WasmtimeInstantiationStrategy::RecreateInstance, + WasmtimeInstantiationStrategy::LegacyInstanceReuse => + sc_service::config::WasmtimeInstantiationStrategy::LegacyInstanceReuse, }, } } diff --git a/client/executor/Cargo.toml b/client/executor/Cargo.toml index 21a9bd70d..31ed3bfed 100644 --- a/client/executor/Cargo.toml +++ b/client/executor/Cargo.toml @@ -17,11 +17,9 @@ targets = ["x86_64-unknown-linux-gnu"] lru = "0.8.1" parking_lot = "0.12.1" tracing = "0.1.29" -wasmi = "0.13.2" codec = { package = "parity-scale-codec", version = "3.2.2" } sc-executor-common = { version = "0.10.0-dev", path = "common" } -sc-executor-wasmi = { version = "0.10.0-dev", path = "wasmi" } sc-executor-wasmtime = { version = "0.10.0-dev", path = "wasmtime" } sp-api = { version = "4.0.0-dev", path = "../../primitives/api" } sp-core = { version = "7.0.0", path = "../../primitives/core" } diff --git a/client/executor/benches/bench.rs b/client/executor/benches/bench.rs index 2844b510e..38ae58adb 100644 --- a/client/executor/benches/bench.rs +++ b/client/executor/benches/bench.rs @@ -33,7 +33,6 @@ use std::sync::{ #[derive(Clone)] enum Method { - Interpreted, Compiled { instantiation_strategy: InstantiationStrategy, precompile: bool }, } @@ -50,17 +49,10 @@ fn initialize( method: Method, ) -> Box { let blob = RuntimeBlob::uncompress_if_needed(runtime).unwrap(); - let host_functions = sp_io::SubstrateHostFunctions::host_functions(); + let allow_missing_func_imports = true; match method { - Method::Interpreted => sc_executor_wasmi::create_runtime( - blob, - DEFAULT_HEAP_ALLOC_STRATEGY, - host_functions, - allow_missing_func_imports, - ) - .map(|runtime| -> Box { Box::new(runtime) }), Method::Compiled { instantiation_strategy, precompile } => { let config = sc_executor_wasmtime::Config { allow_missing_func_imports, @@ -215,7 +207,6 @@ fn bench_call_instance(c: &mut Criterion) { precompile: true, }, ), - ("interpreted", Method::Interpreted), ]; let runtimes = [("kusama_runtime", kusama_runtime()), ("test_runtime", test_runtime())]; diff --git a/client/executor/common/Cargo.toml b/client/executor/common/Cargo.toml index dd74ea2cf..19aebc70f 100644 --- a/client/executor/common/Cargo.toml +++ b/client/executor/common/Cargo.toml @@ -16,7 +16,6 @@ targets = ["x86_64-unknown-linux-gnu"] [dependencies] thiserror = "1.0.30" wasm-instrument = "0.3" -wasmi = "0.13.2" sc-allocator = { version = "4.1.0-dev", path = "../../allocator" } sp-maybe-compressed-blob = { version = "4.1.0-dev", path = "../../../primitives/maybe-compressed-blob" } sp-wasm-interface = { version = "7.0.0", path = "../../../primitives/wasm-interface" } diff --git a/client/executor/common/src/error.rs b/client/executor/common/src/error.rs index 2dfe0bf02..2a0dc364b 100644 --- a/client/executor/common/src/error.rs +++ b/client/executor/common/src/error.rs @@ -18,8 +18,6 @@ //! Rust executor possible errors. -use wasmi; - /// Result type alias. pub type Result = std::result::Result; @@ -27,9 +25,6 @@ pub type Result = std::result::Result; #[derive(Debug, thiserror::Error)] #[allow(missing_docs)] pub enum Error { - #[error(transparent)] - Wasmi(#[from] wasmi::Error), - #[error("Error calling api function: {0}")] ApiError(Box), @@ -48,9 +43,6 @@ pub enum Error { #[error("Invalid type returned (should be u64)")] InvalidReturn, - #[error("Runtime error")] - Runtime, - #[error("Runtime panicked: {0}")] RuntimePanicked(String), @@ -109,8 +101,6 @@ pub enum Error { OutputExceedsBounds, } -impl wasmi::HostError for Error {} - impl From<&'static str> for Error { fn from(err: &'static str) -> Error { Error::Other(err.into()) diff --git a/client/executor/common/src/runtime_blob/mod.rs b/client/executor/common/src/runtime_blob/mod.rs index 58278493a..07a0945cc 100644 --- a/client/executor/common/src/runtime_blob/mod.rs +++ b/client/executor/common/src/runtime_blob/mod.rs @@ -26,9 +26,6 @@ //! //! To give you some examples: //! -//! - wasmi allows reaching to non-exported mutable globals so that we could reset them. Wasmtime -//! doesn’t support that. -//! //! We need to reset the globals because when we //! execute the Substrate Runtime, we do not drop and create the instance anew, instead //! we restore some selected parts of the state. diff --git a/client/executor/src/integration_tests/linux.rs b/client/executor/src/integration_tests/linux.rs index 434cb69bf..68ac37e90 100644 --- a/client/executor/src/integration_tests/linux.rs +++ b/client/executor/src/integration_tests/linux.rs @@ -18,34 +18,15 @@ //! Tests that are only relevant for Linux. +mod smaps; + use super::mk_test_runtime; use crate::WasmExecutionMethod; use codec::Encode as _; use sc_executor_common::wasm_runtime::DEFAULT_HEAP_ALLOC_STRATEGY; -mod smaps; - use self::smaps::Smaps; -#[test] -fn memory_consumption_interpreted() { - let _ = sp_tracing::try_init_simple(); - - if std::env::var("RUN_TEST").is_ok() { - memory_consumption(WasmExecutionMethod::Interpreted); - } else { - // We need to run the test in isolation, to not getting interfered by the other tests. - let executable = std::env::current_exe().unwrap(); - let output = std::process::Command::new(executable) - .env("RUN_TEST", "1") - .args(&["--nocapture", "memory_consumption_interpreted"]) - .output() - .unwrap(); - - assert!(output.status.success()); - } -} - #[test] fn memory_consumption_compiled() { let _ = sp_tracing::try_init_simple(); diff --git a/client/executor/src/integration_tests/mod.rs b/client/executor/src/integration_tests/mod.rs index b65aeb8d0..37aed8eef 100644 --- a/client/executor/src/integration_tests/mod.rs +++ b/client/executor/src/integration_tests/mod.rs @@ -24,7 +24,7 @@ use codec::{Decode, Encode}; use sc_executor_common::{ error::Error, runtime_blob::RuntimeBlob, - wasm_runtime::{HeapAllocStrategy, WasmModule, DEFAULT_HEAP_ALLOC_STRATEGY}, + wasm_runtime::{HeapAllocStrategy, WasmModule}, }; use sc_runtime_test::wasm_binary_unwrap; use sp_core::{ @@ -50,12 +50,6 @@ type HostFunctions = sp_io::SubstrateHostFunctions; macro_rules! test_wasm_execution { ($method_name:ident) => { paste::item! { - #[test] - fn [<$method_name _interpreted>]() { - let _ = sp_tracing::try_init_simple(); - $method_name(WasmExecutionMethod::Interpreted); - } - #[test] fn [<$method_name _compiled_recreate_instance_cow>]() { let _ = sp_tracing::try_init_simple(); @@ -97,15 +91,6 @@ macro_rules! test_wasm_execution { } } }; - - (interpreted_only $method_name:ident) => { - paste::item! { - #[test] - fn [<$method_name _interpreted>]() { - $method_name(WasmExecutionMethod::Interpreted); - } - } - }; } fn call_in_wasm( @@ -144,8 +129,8 @@ fn call_not_existing_function(wasm_method: WasmExecutionMethod) { match call_in_wasm("test_calling_missing_external", &[], wasm_method, &mut ext).unwrap_err() { Error::AbortedDueToTrap(error) => { let expected = match wasm_method { - WasmExecutionMethod::Interpreted => "Other: Function `missing_external` is only a stub. Calling a stub is not allowed.", - WasmExecutionMethod::Compiled { .. } => "call to a missing function env:missing_external" + WasmExecutionMethod::Compiled { .. } => + "call to a missing function env:missing_external", }; assert_eq!(error.message, expected); }, @@ -163,8 +148,8 @@ fn call_yet_another_not_existing_function(wasm_method: WasmExecutionMethod) { { Error::AbortedDueToTrap(error) => { let expected = match wasm_method { - WasmExecutionMethod::Interpreted => "Other: Function `yet_another_missing_external` is only a stub. Calling a stub is not allowed.", - WasmExecutionMethod::Compiled { .. } => "call to a missing function env:yet_another_missing_external" + WasmExecutionMethod::Compiled { .. } => + "call to a missing function env:yet_another_missing_external", }; assert_eq!(error.message, expected); }, @@ -473,9 +458,6 @@ fn should_trap_when_heap_exhausted(wasm_method: WasmExecutionMethod) { r#"host code panicked while being called by the runtime: Failed to allocate memory: "Allocator ran out of space""# ); }, - Error::RuntimePanicked(error) if wasm_method == WasmExecutionMethod::Interpreted => { - assert_eq!(error, r#"Failed to allocate memory: "Allocator ran out of space""#); - }, error => panic!("unexpected error: {:?}", error), } } @@ -558,25 +540,6 @@ fn restoration_of_globals(wasm_method: WasmExecutionMethod) { assert!(res.is_ok()); } -test_wasm_execution!(interpreted_only heap_is_reset_between_calls); -fn heap_is_reset_between_calls(wasm_method: WasmExecutionMethod) { - let runtime = mk_test_runtime(wasm_method, DEFAULT_HEAP_ALLOC_STRATEGY); - let mut instance = runtime.new_instance().unwrap(); - - let heap_base = instance - .get_global_const("__heap_base") - .expect("`__heap_base` is valid") - .expect("`__heap_base` exists") - .as_i32() - .expect("`__heap_base` is an `i32`"); - - let params = (heap_base as u32, 512u32 * 64 * 1024).encode(); - instance.call_export("check_and_set_in_heap", ¶ms).unwrap(); - - // Cal it a second time to check that the heap was freed. - instance.call_export("check_and_set_in_heap", ¶ms).unwrap(); -} - test_wasm_execution!(parallel_execution); fn parallel_execution(wasm_method: WasmExecutionMethod) { let executor = Arc::new( @@ -787,7 +750,6 @@ fn unreachable_intrinsic(wasm_method: WasmExecutionMethod) { match call_in_wasm("test_unreachable_intrinsic", &[], wasm_method, &mut ext).unwrap_err() { Error::AbortedDueToTrap(error) => { let expected = match wasm_method { - WasmExecutionMethod::Interpreted => "unreachable", WasmExecutionMethod::Compiled { .. } => "wasm trap: wasm `unreachable` instruction executed", }; @@ -814,9 +776,6 @@ fn return_huge_len(wasm_method: WasmExecutionMethod) { let mut ext = ext.ext(); match call_in_wasm("test_return_huge_len", &[], wasm_method, &mut ext).unwrap_err() { - Error::Runtime => { - assert_matches!(wasm_method, WasmExecutionMethod::Interpreted); - }, Error::OutputExceedsBounds => { assert_matches!(wasm_method, WasmExecutionMethod::Compiled { .. }); }, @@ -843,9 +802,6 @@ fn return_max_memory_offset_plus_one(wasm_method: WasmExecutionMethod) { match call_in_wasm("test_return_max_memory_offset_plus_one", &[], wasm_method, &mut ext) .unwrap_err() { - Error::Runtime => { - assert_matches!(wasm_method, WasmExecutionMethod::Interpreted); - }, Error::OutputExceedsBounds => { assert_matches!(wasm_method, WasmExecutionMethod::Compiled { .. }); }, @@ -859,9 +815,6 @@ fn return_overflow(wasm_method: WasmExecutionMethod) { let mut ext = ext.ext(); match call_in_wasm("test_return_overflow", &[], wasm_method, &mut ext).unwrap_err() { - Error::Runtime => { - assert_matches!(wasm_method, WasmExecutionMethod::Interpreted); - }, Error::OutputExceedsBounds => { assert_matches!(wasm_method, WasmExecutionMethod::Compiled { .. }); }, diff --git a/client/executor/src/lib.rs b/client/executor/src/lib.rs index 42e7dc7d1..6ee0ab351 100644 --- a/client/executor/src/lib.rs +++ b/client/executor/src/lib.rs @@ -49,7 +49,6 @@ pub use sp_core::traits::Externalities; pub use sp_version::{NativeVersion, RuntimeVersion}; #[doc(hidden)] pub use sp_wasm_interface; -pub use wasmi; pub use sc_executor_common::{ error, diff --git a/client/executor/src/wasm_runtime.rs b/client/executor/src/wasm_runtime.rs index 41a095081..dea844679 100644 --- a/client/executor/src/wasm_runtime.rs +++ b/client/executor/src/wasm_runtime.rs @@ -43,8 +43,6 @@ use sp_wasm_interface::HostFunctions; /// Specification of different methods of executing the runtime Wasm code. #[derive(Debug, PartialEq, Eq, Hash, Copy, Clone)] pub enum WasmExecutionMethod { - /// Uses the Wasmi interpreter. - Interpreted, /// Uses the Wasmtime compiled runtime. Compiled { /// The instantiation strategy to use. @@ -53,8 +51,10 @@ pub enum WasmExecutionMethod { } impl Default for WasmExecutionMethod { - fn default() -> WasmExecutionMethod { - WasmExecutionMethod::Interpreted + fn default() -> Self { + Self::Compiled { + instantiation_strategy: sc_executor_wasmtime::InstantiationStrategy::PoolingCopyOnWrite, + } } } @@ -299,21 +299,6 @@ where H: HostFunctions, { match wasm_method { - WasmExecutionMethod::Interpreted => { - // Wasmi doesn't have any need in a cache directory. - // - // We drop the cache_path here to silence warnings that cache_path is not used if - // compiling without the `wasmtime` flag. - let _ = cache_path; - - sc_executor_wasmi::create_runtime( - blob, - heap_alloc_strategy, - H::host_functions(), - allow_missing_func_imports, - ) - .map(|runtime| -> Box { Box::new(runtime) }) - }, WasmExecutionMethod::Compiled { instantiation_strategy } => sc_executor_wasmtime::create_runtime::( blob, diff --git a/client/executor/wasmi/Cargo.toml b/client/executor/wasmi/Cargo.toml deleted file mode 100644 index ded44f4ca..000000000 --- a/client/executor/wasmi/Cargo.toml +++ /dev/null @@ -1,22 +0,0 @@ -[package] -name = "sc-executor-wasmi" -version = "0.10.0-dev" -authors = ["Parity Technologies "] -edition = "2021" -license = "GPL-3.0-or-later WITH Classpath-exception-2.0" -homepage = "https://substrate.io" -repository = "https://github.com/paritytech/substrate/" -description = "This crate provides an implementation of `WasmRuntime` that is baked by wasmi." -documentation = "https://docs.rs/sc-executor-wasmi" -readme = "README.md" - -[package.metadata.docs.rs] -targets = ["x86_64-unknown-linux-gnu"] - -[dependencies] -log = "0.4.17" -wasmi = { version = "0.13.2", features = [ "virtual_memory" ] } -sc-allocator = { version = "4.1.0-dev", path = "../../allocator" } -sc-executor-common = { version = "0.10.0-dev", path = "../common" } -sp-runtime-interface = { version = "7.0.0", path = "../../../primitives/runtime-interface" } -sp-wasm-interface = { version = "7.0.0", path = "../../../primitives/wasm-interface" } diff --git a/client/executor/wasmi/README.md b/client/executor/wasmi/README.md deleted file mode 100644 index ad613aa12..000000000 --- a/client/executor/wasmi/README.md +++ /dev/null @@ -1,3 +0,0 @@ -This crate provides an implementation of `WasmModule` that is baked by wasmi. - -License: GPL-3.0-or-later WITH Classpath-exception-2.0 \ No newline at end of file diff --git a/client/executor/wasmi/src/lib.rs b/client/executor/wasmi/src/lib.rs deleted file mode 100644 index c757ff8af..000000000 --- a/client/executor/wasmi/src/lib.rs +++ /dev/null @@ -1,599 +0,0 @@ -// This file is part of Substrate. - -// Copyright (C) Parity Technologies (UK) Ltd. -// SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 - -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with this program. If not, see . - -//! This crate provides an implementation of `WasmModule` that is baked by wasmi. - -use std::{cell::RefCell, str, sync::Arc}; - -use log::{error, trace}; -use wasmi::{ - memory_units::Pages, - FuncInstance, ImportsBuilder, MemoryRef, Module, ModuleInstance, ModuleRef, - RuntimeValue::{self, I32, I64}, - TableRef, -}; - -use sc_allocator::{AllocationStats, FreeingBumpHeapAllocator}; -use sc_executor_common::{ - error::{Error, MessageWithBacktrace, WasmError}, - runtime_blob::{DataSegmentsSnapshot, RuntimeBlob}, - wasm_runtime::{HeapAllocStrategy, InvokeMethod, WasmInstance, WasmModule}, -}; -use sp_runtime_interface::unpack_ptr_and_len; -use sp_wasm_interface::{Function, FunctionContext, Pointer, Result as WResult, WordSize}; - -/// Wrapper around [`MemorRef`] that implements [`sc_allocator::Memory`]. -struct MemoryWrapper<'a>(&'a MemoryRef); - -impl sc_allocator::Memory for MemoryWrapper<'_> { - fn with_access_mut(&mut self, run: impl FnOnce(&mut [u8]) -> R) -> R { - self.0.with_direct_access_mut(run) - } - - fn with_access(&self, run: impl FnOnce(&[u8]) -> R) -> R { - self.0.with_direct_access(run) - } - - fn pages(&self) -> u32 { - self.0.current_size().0 as _ - } - - fn max_pages(&self) -> Option { - self.0.maximum().map(|p| p.0 as _) - } - - fn grow(&mut self, additional: u32) -> Result<(), ()> { - self.0 - .grow(Pages(additional as _)) - .map_err(|e| { - log::error!( - target: "wasm-executor", - "Failed to grow memory by {} pages: {}", - additional, - e, - ) - }) - .map(drop) - } -} - -struct FunctionExecutor { - heap: RefCell, - memory: MemoryRef, - host_functions: Arc>, - allow_missing_func_imports: bool, - missing_functions: Arc>, - panic_message: Option, -} - -impl FunctionExecutor { - fn new( - m: MemoryRef, - heap_base: u32, - host_functions: Arc>, - allow_missing_func_imports: bool, - missing_functions: Arc>, - ) -> Result { - Ok(FunctionExecutor { - heap: RefCell::new(FreeingBumpHeapAllocator::new(heap_base)), - memory: m, - host_functions, - allow_missing_func_imports, - missing_functions, - panic_message: None, - }) - } -} - -impl FunctionContext for FunctionExecutor { - fn read_memory_into(&self, address: Pointer, dest: &mut [u8]) -> WResult<()> { - self.memory.get_into(address.into(), dest).map_err(|e| e.to_string()) - } - - fn write_memory(&mut self, address: Pointer, data: &[u8]) -> WResult<()> { - self.memory.set(address.into(), data).map_err(|e| e.to_string()) - } - - fn allocate_memory(&mut self, size: WordSize) -> WResult> { - self.heap - .borrow_mut() - .allocate(&mut MemoryWrapper(&self.memory), size) - .map_err(|e| e.to_string()) - } - - fn deallocate_memory(&mut self, ptr: Pointer) -> WResult<()> { - self.heap - .borrow_mut() - .deallocate(&mut MemoryWrapper(&self.memory), ptr) - .map_err(|e| e.to_string()) - } - - fn register_panic_error_message(&mut self, message: &str) { - self.panic_message = Some(message.to_owned()); - } -} - -/// Will be used on initialization of a module to resolve function and memory imports. -struct Resolver<'a> { - /// All the hot functions that we export for the WASM blob. - host_functions: &'a [&'static dyn Function], - /// Should we allow missing function imports? - /// - /// If `true`, we return a stub that will return an error when being called. - allow_missing_func_imports: bool, - /// All the names of functions for that we did not provide a host function. - missing_functions: RefCell>, -} - -impl<'a> Resolver<'a> { - fn new( - host_functions: &'a [&'static dyn Function], - allow_missing_func_imports: bool, - ) -> Resolver<'a> { - Resolver { - host_functions, - allow_missing_func_imports, - missing_functions: RefCell::new(Vec::new()), - } - } -} - -impl<'a> wasmi::ModuleImportResolver for Resolver<'a> { - fn resolve_func( - &self, - name: &str, - signature: &wasmi::Signature, - ) -> std::result::Result { - let signature = sp_wasm_interface::Signature::from(signature); - for (function_index, function) in self.host_functions.iter().enumerate() { - if name == function.name() { - if signature == function.signature() { - return Ok(wasmi::FuncInstance::alloc_host(signature.into(), function_index)) - } else { - return Err(wasmi::Error::Instantiation(format!( - "Invalid signature for function `{}` expected `{:?}`, got `{:?}`", - function.name(), - signature, - function.signature(), - ))) - } - } - } - - if self.allow_missing_func_imports { - trace!( - target: "wasm-executor", - "Could not find function `{}`, a stub will be provided instead.", - name, - ); - let id = self.missing_functions.borrow().len() + self.host_functions.len(); - self.missing_functions.borrow_mut().push(name.to_string()); - - Ok(wasmi::FuncInstance::alloc_host(signature.into(), id)) - } else { - Err(wasmi::Error::Instantiation(format!("Export {} not found", name))) - } - } - - fn resolve_memory( - &self, - _: &str, - _: &wasmi::MemoryDescriptor, - ) -> Result { - Err(wasmi::Error::Instantiation( - "Internal error, wasmi expects that the wasm blob exports memory.".into(), - )) - } -} - -impl wasmi::Externals for FunctionExecutor { - fn invoke_index( - &mut self, - index: usize, - args: wasmi::RuntimeArgs, - ) -> Result, wasmi::Trap> { - let mut args = args.as_ref().iter().copied().map(Into::into); - - if let Some(function) = self.host_functions.clone().get(index) { - function - .execute(self, &mut args) - .map_err(|msg| Error::FunctionExecution(function.name().to_string(), msg)) - .map_err(wasmi::Trap::from) - .map(|v| v.map(Into::into)) - } else if self.allow_missing_func_imports && - index >= self.host_functions.len() && - index < self.host_functions.len() + self.missing_functions.len() - { - Err(Error::from(format!( - "Function `{}` is only a stub. Calling a stub is not allowed.", - self.missing_functions[index - self.host_functions.len()], - )) - .into()) - } else { - Err(Error::from(format!("Could not find host function with index: {}", index)).into()) - } - } -} - -fn get_mem_instance(module: &ModuleRef) -> Result { - Ok(module - .export_by_name("memory") - .ok_or(Error::InvalidMemoryReference)? - .as_memory() - .ok_or(Error::InvalidMemoryReference)? - .clone()) -} - -/// Find the global named `__heap_base` in the given wasm module instance and -/// tries to get its value. -fn get_heap_base(module: &ModuleRef) -> Result { - let heap_base_val = module - .export_by_name("__heap_base") - .ok_or(Error::HeapBaseNotFoundOrInvalid)? - .as_global() - .ok_or(Error::HeapBaseNotFoundOrInvalid)? - .get(); - - match heap_base_val { - wasmi::RuntimeValue::I32(v) => Ok(v as u32), - _ => Err(Error::HeapBaseNotFoundOrInvalid), - } -} - -/// Call a given method in the given wasm-module runtime. -fn call_in_wasm_module( - module_instance: &ModuleRef, - memory: &MemoryRef, - method: InvokeMethod, - data: &[u8], - host_functions: Arc>, - allow_missing_func_imports: bool, - missing_functions: Arc>, - allocation_stats: &mut Option, -) -> Result, Error> { - // Initialize FunctionExecutor. - let table: Option = module_instance - .export_by_name("__indirect_function_table") - .and_then(|e| e.as_table().cloned()); - let heap_base = get_heap_base(module_instance)?; - - let mut function_executor = FunctionExecutor::new( - memory.clone(), - heap_base, - host_functions, - allow_missing_func_imports, - missing_functions, - )?; - - // Write the call data - let offset = function_executor.allocate_memory(data.len() as u32)?; - function_executor.write_memory(offset, data)?; - - fn convert_trap(executor: &mut FunctionExecutor, trap: wasmi::Trap) -> Error { - if let Some(message) = executor.panic_message.take() { - Error::AbortedDueToPanic(MessageWithBacktrace { message, backtrace: None }) - } else { - Error::AbortedDueToTrap(MessageWithBacktrace { - message: trap.to_string(), - backtrace: None, - }) - } - } - - let result = match method { - InvokeMethod::Export(method) => module_instance - .invoke_export( - method, - &[I32(u32::from(offset) as i32), I32(data.len() as i32)], - &mut function_executor, - ) - .map_err(|error| { - if let wasmi::Error::Trap(trap) = error { - convert_trap(&mut function_executor, trap) - } else { - error.into() - } - }), - InvokeMethod::Table(func_ref) => { - let func = table - .ok_or(Error::NoTable)? - .get(func_ref)? - .ok_or(Error::NoTableEntryWithIndex(func_ref))?; - FuncInstance::invoke( - &func, - &[I32(u32::from(offset) as i32), I32(data.len() as i32)], - &mut function_executor, - ) - .map_err(|trap| convert_trap(&mut function_executor, trap)) - }, - InvokeMethod::TableWithWrapper { dispatcher_ref, func } => { - let dispatcher = table - .ok_or(Error::NoTable)? - .get(dispatcher_ref)? - .ok_or(Error::NoTableEntryWithIndex(dispatcher_ref))?; - - FuncInstance::invoke( - &dispatcher, - &[I32(func as _), I32(u32::from(offset) as i32), I32(data.len() as i32)], - &mut function_executor, - ) - .map_err(|trap| convert_trap(&mut function_executor, trap)) - }, - }; - - *allocation_stats = Some(function_executor.heap.borrow().stats()); - - match result { - Ok(Some(I64(r))) => { - let (ptr, length) = unpack_ptr_and_len(r as u64); - #[allow(deprecated)] - memory.get(ptr, length as usize).map_err(|_| Error::Runtime) - }, - Err(e) => { - trace!( - target: "wasm-executor", - "Failed to execute code with {} pages", - memory.current_size().0, - ); - Err(e) - }, - _ => Err(Error::InvalidReturn), - } -} - -/// Prepare module instance -fn instantiate_module( - module: &Module, - host_functions: &[&'static dyn Function], - allow_missing_func_imports: bool, -) -> Result<(ModuleRef, Vec, MemoryRef), Error> { - let resolver = Resolver::new(host_functions, allow_missing_func_imports); - // start module instantiation. Don't run 'start' function yet. - let intermediate_instance = - ModuleInstance::new(module, &ImportsBuilder::new().with_resolver("env", &resolver))?; - - // Verify that the module has the heap base global variable. - let _ = get_heap_base(intermediate_instance.not_started_instance())?; - - // The `module` should export the memory with the correct properties (min, max). - // - // This is ensured by modifying the `RuntimeBlob` before initializing the `Module`. - let memory = get_mem_instance(intermediate_instance.not_started_instance())?; - - if intermediate_instance.has_start() { - // Runtime is not allowed to have the `start` function. - Err(Error::RuntimeHasStartFn) - } else { - Ok(( - intermediate_instance.assert_no_start(), - resolver.missing_functions.into_inner(), - memory, - )) - } -} - -/// A state snapshot of an instance taken just after instantiation. -/// -/// It is used for restoring the state of the module after execution. -#[derive(Clone)] -struct GlobalValsSnapshot { - /// The list of all global mutable variables of the module in their sequential order. - global_mut_values: Vec, -} - -impl GlobalValsSnapshot { - // Returns `None` if instance is not valid. - fn take(module_instance: &ModuleRef) -> Self { - // Collect all values of mutable globals. - let global_mut_values = module_instance - .globals() - .iter() - .filter(|g| g.is_mutable()) - .map(|g| g.get()) - .collect(); - Self { global_mut_values } - } - - /// Reset the runtime instance to the initial version by restoring - /// the preserved memory and globals. - /// - /// Returns `Err` if applying the snapshot is failed. - fn apply(&self, instance: &ModuleRef) -> Result<(), WasmError> { - for (global_ref, global_val) in instance - .globals() - .iter() - .filter(|g| g.is_mutable()) - .zip(self.global_mut_values.iter()) - { - // the instance should be the same as used for preserving and - // we iterate the same way it as we do it for preserving values that means that the - // types should be the same and all the values are mutable. So no error is expected/ - global_ref.set(*global_val).map_err(|_| WasmError::ApplySnapshotFailed)?; - } - Ok(()) - } -} - -/// A runtime along with initial copy of data segments. -pub struct WasmiRuntime { - /// A wasm module. - module: Module, - /// The host functions registered for this instance. - host_functions: Arc>, - /// Enable stub generation for functions that are not available in `host_functions`. - /// These stubs will error when the wasm blob tries to call them. - allow_missing_func_imports: bool, - - global_vals_snapshot: GlobalValsSnapshot, - data_segments_snapshot: DataSegmentsSnapshot, -} - -impl WasmModule for WasmiRuntime { - fn new_instance(&self) -> Result, Error> { - // Instantiate this module. - let (instance, missing_functions, memory) = - instantiate_module(&self.module, &self.host_functions, self.allow_missing_func_imports) - .map_err(|e| WasmError::Instantiation(e.to_string()))?; - - Ok(Box::new(WasmiInstance { - instance, - memory, - global_vals_snapshot: self.global_vals_snapshot.clone(), - data_segments_snapshot: self.data_segments_snapshot.clone(), - host_functions: self.host_functions.clone(), - allow_missing_func_imports: self.allow_missing_func_imports, - missing_functions: Arc::new(missing_functions), - memory_zeroed: true, - })) - } -} - -/// Create a new `WasmiRuntime` given the code. This function loads the module and -/// stores it in the instance. -pub fn create_runtime( - mut blob: RuntimeBlob, - heap_alloc_strategy: HeapAllocStrategy, - host_functions: Vec<&'static dyn Function>, - allow_missing_func_imports: bool, -) -> Result { - let data_segments_snapshot = - DataSegmentsSnapshot::take(&blob).map_err(|e| WasmError::Other(e.to_string()))?; - - // Make sure we only have exported memory to simplify the code of the wasmi executor. - blob.convert_memory_import_into_export()?; - // Ensure that the memory uses the correct heap pages. - blob.setup_memory_according_to_heap_alloc_strategy(heap_alloc_strategy)?; - - let module = - Module::from_parity_wasm_module(blob.into_inner()).map_err(|_| WasmError::InvalidModule)?; - - let global_vals_snapshot = { - let (instance, _, _) = - instantiate_module(&module, &host_functions, allow_missing_func_imports) - .map_err(|e| WasmError::Instantiation(e.to_string()))?; - GlobalValsSnapshot::take(&instance) - }; - - Ok(WasmiRuntime { - module, - data_segments_snapshot, - global_vals_snapshot, - host_functions: Arc::new(host_functions), - allow_missing_func_imports, - }) -} - -/// Wasmi instance wrapper along with the state snapshot. -pub struct WasmiInstance { - /// A wasm module instance. - instance: ModuleRef, - /// The memory instance of used by the wasm module. - memory: MemoryRef, - /// Is the memory zeroed? - memory_zeroed: bool, - /// The snapshot of global variable values just after instantiation. - global_vals_snapshot: GlobalValsSnapshot, - /// The snapshot of data segments. - data_segments_snapshot: DataSegmentsSnapshot, - /// The host functions registered for this instance. - host_functions: Arc>, - /// Enable stub generation for functions that are not available in `host_functions`. - /// These stubs will error when the wasm blob trie to call them. - allow_missing_func_imports: bool, - /// List of missing functions detected during function resolution - missing_functions: Arc>, -} - -// This is safe because `WasmiInstance` does not leak any references to `self.memory` and -// `self.instance` -unsafe impl Send for WasmiInstance {} - -impl WasmiInstance { - fn call_impl( - &mut self, - method: InvokeMethod, - data: &[u8], - allocation_stats: &mut Option, - ) -> Result, Error> { - // We reuse a single wasm instance for multiple calls and a previous call (if any) - // altered the state. Therefore, we need to restore the instance to original state. - - if !self.memory_zeroed { - // First, zero initialize the linear memory. - self.memory.erase().map_err(|e| { - // Snapshot restoration failed. This is pretty unexpected since this can happen - // if some invariant is broken or if the system is under extreme memory pressure - // (so erasing fails). - error!(target: "wasm-executor", "snapshot restoration failed: {}", e); - WasmError::ErasingFailed(e.to_string()) - })?; - } - - // Second, reapply data segments into the linear memory. - self.data_segments_snapshot - .apply(|offset, contents| self.memory.set(offset, contents))?; - - // Third, restore the global variables to their initial values. - self.global_vals_snapshot.apply(&self.instance)?; - - let res = call_in_wasm_module( - &self.instance, - &self.memory, - method, - data, - self.host_functions.clone(), - self.allow_missing_func_imports, - self.missing_functions.clone(), - allocation_stats, - ); - - // If we couldn't unmap it, erase the memory. - self.memory_zeroed = self.memory.erase().is_ok(); - - res - } -} - -impl WasmInstance for WasmiInstance { - fn call_with_allocation_stats( - &mut self, - method: InvokeMethod, - data: &[u8], - ) -> (Result, Error>, Option) { - let mut allocation_stats = None; - let result = self.call_impl(method, data, &mut allocation_stats); - (result, allocation_stats) - } - - fn get_global_const(&mut self, name: &str) -> Result, Error> { - match self.instance.export_by_name(name) { - Some(global) => Ok(Some( - global - .as_global() - .ok_or_else(|| format!("`{}` is not a global", name))? - .get() - .into(), - )), - None => Ok(None), - } - } - - fn linear_memory_base_ptr(&self) -> Option<*const u8> { - Some(self.memory.direct_access().as_ref().as_ptr()) - } -} diff --git a/primitives/runtime-interface/test/src/lib.rs b/primitives/runtime-interface/test/src/lib.rs index e1be3b5d9..215704a11 100644 --- a/primitives/runtime-interface/test/src/lib.rs +++ b/primitives/runtime-interface/test/src/lib.rs @@ -109,8 +109,8 @@ fn host_function_not_found() { .0 .unwrap_err(); - assert!(err.contains("Instantiation: Export ")); - assert!(err.contains(" not found")); + assert!(err.contains("test_return_data")); + assert!(err.contains(" Failed to create module")); } #[test] From dca6ebeb4dac3aac301a475cd52d5566dbc7a9ef Mon Sep 17 00:00:00 2001 From: Liam Aharon Date: Wed, 24 May 2023 02:01:51 +1000 Subject: [PATCH 539/558] Migration hook fixes (#14174) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * fix offences pre_upgrade hook * identify source of ensure! failures * stop migration hooks breaking post migration * add childbounties storage version * init child bounties version to zero * Update frame/child-bounties/src/lib.rs Co-authored-by: Bastian Köcher * remove redundant preupgrade version checks * update test * fix nom pools v3 migration * kick ci * kick ci --------- Co-authored-by: Bastian Köcher --- frame/nomination-pools/src/migration.rs | 70 +++++++++++++------------ frame/offences/src/migration.rs | 14 ++--- 2 files changed, 41 insertions(+), 43 deletions(-) diff --git a/frame/nomination-pools/src/migration.rs b/frame/nomination-pools/src/migration.rs index 2c84e4e6d..2ae4cd1b8 100644 --- a/frame/nomination-pools/src/migration.rs +++ b/frame/nomination-pools/src/migration.rs @@ -416,14 +416,14 @@ pub mod v3 { let current = Pallet::::current_storage_version(); let onchain = Pallet::::on_chain_storage_version(); - log!( - info, - "Running migration with current storage version {:?} / onchain {:?}", - current, - onchain - ); + if onchain == 2 { + log!( + info, + "Running migration with current storage version {:?} / onchain {:?}", + current, + onchain + ); - if current > onchain { let mut metadata_iterated = 0u64; let mut metadata_removed = 0u64; Metadata::::iter_keys() @@ -437,7 +437,7 @@ pub mod v3 { metadata_removed += 1; Metadata::::remove(&id); }); - current.put::>(); + StorageVersion::new(3).put::>(); // metadata iterated + bonded pools read + a storage version read let total_reads = metadata_iterated * 2 + 1; // metadata removed + a storage version write @@ -451,10 +451,6 @@ pub mod v3 { #[cfg(feature = "try-runtime")] fn pre_upgrade() -> Result, TryRuntimeError> { - ensure!( - Pallet::::current_storage_version() > Pallet::::on_chain_storage_version(), - "the on_chain version is equal or more than the current one" - ); Ok(Vec::new()) } @@ -464,7 +460,10 @@ pub mod v3 { Metadata::::iter_keys().all(|id| BondedPools::::contains_key(&id)), "not all of the stale metadata has been removed" ); - ensure!(Pallet::::on_chain_storage_version() == 3, "wrong storage version"); + ensure!( + Pallet::::on_chain_storage_version() >= 3, + "nomination-pools::migration::v3: wrong storage version" + ); Ok(()) } } @@ -551,10 +550,6 @@ pub mod v4 { #[cfg(feature = "try-runtime")] fn pre_upgrade() -> Result, TryRuntimeError> { - ensure!( - Pallet::::current_storage_version() > Pallet::::on_chain_storage_version(), - "the on_chain version is equal or more than the current one" - ); Ok(Vec::new()) } @@ -562,17 +557,28 @@ pub mod v4 { fn post_upgrade(_: Vec) -> Result<(), TryRuntimeError> { // ensure all BondedPools items now contain an `inner.commission: Commission` field. ensure!( - BondedPools::::iter().all(|(_, inner)| inner.commission.current.is_none() && - inner.commission.max.is_none() && - inner.commission.change_rate.is_none() && - inner.commission.throttle_from.is_none()), - "a commission value has been incorrectly set" + BondedPools::::iter().all(|(_, inner)| + // Check current + (inner.commission.current.is_none() || + inner.commission.current.is_some()) && + // Check max + (inner.commission.max.is_none() || inner.commission.max.is_some()) && + // Check change_rate + (inner.commission.change_rate.is_none() || + inner.commission.change_rate.is_some()) && + // Check throttle_from + (inner.commission.throttle_from.is_none() || + inner.commission.throttle_from.is_some())), + "a commission value has not been set correctly" ); ensure!( GlobalMaxCommission::::get() == Some(U::get()), "global maximum commission error" ); - ensure!(Pallet::::on_chain_storage_version() == 4, "wrong storage version"); + ensure!( + Pallet::::on_chain_storage_version() >= 4, + "nomination-pools::migration::v4: wrong storage version" + ); Ok(()) } } @@ -636,11 +642,6 @@ pub mod v5 { #[cfg(feature = "try-runtime")] fn pre_upgrade() -> Result, TryRuntimeError> { - ensure!( - Pallet::::current_storage_version() > Pallet::::on_chain_storage_version(), - "the on_chain version is equal or more than the current one" - ); - let rpool_keys = RewardPools::::iter_keys().count(); let rpool_values = RewardPools::::iter_values().count(); if rpool_keys != rpool_values { @@ -690,13 +691,16 @@ pub mod v5 { // `total_commission_claimed` field. ensure!( RewardPools::::iter().all(|(_, reward_pool)| reward_pool - .total_commission_pending - .is_zero() && reward_pool - .total_commission_claimed - .is_zero()), + .total_commission_pending >= + Zero::zero() && reward_pool + .total_commission_claimed >= + Zero::zero()), "a commission value has been incorrectly set" ); - ensure!(Pallet::::on_chain_storage_version() == 5, "wrong storage version"); + ensure!( + Pallet::::on_chain_storage_version() >= 5, + "nomination-pools::migration::v5: wrong storage version" + ); // These should not have been touched - just in case. ensure!( diff --git a/frame/offences/src/migration.rs b/frame/offences/src/migration.rs index 14dbd606d..3c0d243a5 100644 --- a/frame/offences/src/migration.rs +++ b/frame/offences/src/migration.rs @@ -54,9 +54,6 @@ pub mod v1 { impl OnRuntimeUpgrade for MigrateToV1 { #[cfg(feature = "try-runtime")] fn pre_upgrade() -> Result, TryRuntimeError> { - let onchain = Pallet::::on_chain_storage_version(); - ensure!(onchain < 1, "pallet_offences::MigrateToV1 migration can be deleted"); - log::info!( target: LOG_TARGET, "Number of reports to refund and delete: {}", @@ -67,19 +64,16 @@ pub mod v1 { } fn on_runtime_upgrade() -> Weight { - let onchain = Pallet::::on_chain_storage_version(); - - if onchain > 0 { + if Pallet::::on_chain_storage_version() > 0 { log::info!(target: LOG_TARGET, "pallet_offences::MigrateToV1 should be removed"); return T::DbWeight::get().reads(1) } let keys_removed = v0::ReportsByKindIndex::::clear(u32::MAX, None).unique as u64; - let weight = T::DbWeight::get().reads_writes(keys_removed, keys_removed); - StorageVersion::new(1).put::>(); - weight + // + 1 for reading/writing the new storage version + T::DbWeight::get().reads_writes(keys_removed + 1, keys_removed + 1) } #[cfg(feature = "try-runtime")] @@ -147,7 +141,7 @@ mod test { ext.execute_with(|| { assert_eq!( v1::MigrateToV1::::on_runtime_upgrade(), - ::DbWeight::get().reads_writes(1, 1), + ::DbWeight::get().reads_writes(2, 2), ); assert!(>::iter_values().count() == 0); From e1345e78586fff0cc4dd3a5d6e71dfd5e74669f6 Mon Sep 17 00:00:00 2001 From: Alexandru Vasile <60601340+lexnv@users.noreply.github.com> Date: Wed, 24 May 2023 12:08:21 +0300 Subject: [PATCH 540/558] wasm: Update from v6.0.2 to v8.0.1 (#14178) * wasm: Update from v6.0.2 to v8.0.1 Signed-off-by: Alexandru Vasile * Fix rustix version Signed-off-by: Alexandru Vasile --------- Signed-off-by: Alexandru Vasile --- Cargo.lock | 177 +++++++++++++-------------- client/executor/wasmtime/Cargo.toml | 2 +- primitives/wasm-interface/Cargo.toml | 2 +- 3 files changed, 86 insertions(+), 95 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index ae6557c1b..eefa2f873 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -12,22 +12,13 @@ dependencies = [ "regex", ] -[[package]] -name = "addr2line" -version = "0.17.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b9ecd88a8c8378ca913a680cd98f0f13ac67383d35993f86c90a70e3f137816b" -dependencies = [ - "gimli 0.26.2", -] - [[package]] name = "addr2line" version = "0.19.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a76fd60b23679b7d19bd066031410fb7e458ccc5e958eb5c325888ce4baedc97" dependencies = [ - "gimli 0.27.2", + "gimli", ] [[package]] @@ -661,12 +652,12 @@ version = "0.3.67" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "233d376d6d185f2a3093e58f283f60f880315b6c60075b01f36b3b85154564ca" dependencies = [ - "addr2line 0.19.0", + "addr2line", "cc", "cfg-if", "libc", "miniz_oxide 0.6.2", - "object 0.30.3", + "object", "rustc-demangle", ] @@ -1383,28 +1374,27 @@ dependencies = [ [[package]] name = "cranelift-bforest" -version = "0.93.2" +version = "0.95.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2bc42ba2e232e5b20ff7dc299a812d53337dadce9a7e39a238e6a5cb82d2e57b" +checksum = "1277fbfa94bc82c8ec4af2ded3e639d49ca5f7f3c7eeab2c66accd135ece4e70" dependencies = [ "cranelift-entity", ] [[package]] name = "cranelift-codegen" -version = "0.93.2" +version = "0.95.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "253531aca9b6f56103c9420369db3263e784df39aa1c90685a1f69cfbba0623e" +checksum = "c6e8c31ad3b2270e9aeec38723888fe1b0ace3bea2b06b3f749ccf46661d3220" dependencies = [ - "arrayvec 0.7.2", "bumpalo", "cranelift-bforest", "cranelift-codegen-meta", "cranelift-codegen-shared", "cranelift-entity", "cranelift-isle", - "gimli 0.26.2", - "hashbrown 0.12.3", + "gimli", + "hashbrown 0.13.2", "log", "regalloc2", "smallvec", @@ -1413,33 +1403,33 @@ dependencies = [ [[package]] name = "cranelift-codegen-meta" -version = "0.93.2" +version = "0.95.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "72f2154365e2bff1b1b8537a7181591fdff50d8e27fa6e40d5c69c3bad0ca7c8" +checksum = "c8ac5ac30d62b2d66f12651f6b606dbdfd9c2cfd0908de6b387560a277c5c9da" dependencies = [ "cranelift-codegen-shared", ] [[package]] name = "cranelift-codegen-shared" -version = "0.93.2" +version = "0.95.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "687e14e3f5775248930e0d5a84195abef8b829958e9794bf8d525104993612b4" +checksum = "dd82b8b376247834b59ed9bdc0ddeb50f517452827d4a11bccf5937b213748b8" [[package]] name = "cranelift-entity" -version = "0.93.2" +version = "0.95.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f42ea692c7b450ad18b8c9889661505d51c09ec4380cf1c2d278dbb2da22cae1" +checksum = "40099d38061b37e505e63f89bab52199037a72b931ad4868d9089ff7268660b0" dependencies = [ "serde", ] [[package]] name = "cranelift-frontend" -version = "0.93.2" +version = "0.95.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8483c2db6f45fe9ace984e5adc5d058102227e4c62e5aa2054e16b0275fd3a6e" +checksum = "64a25d9d0a0ae3079c463c34115ec59507b4707175454f0eee0891e83e30e82d" dependencies = [ "cranelift-codegen", "log", @@ -1449,15 +1439,15 @@ dependencies = [ [[package]] name = "cranelift-isle" -version = "0.93.2" +version = "0.95.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e9793158837678902446c411741d87b43f57dadfb944f2440db4287cda8cbd59" +checksum = "80de6a7d0486e4acbd5f9f87ec49912bf4c8fb6aea00087b989685460d4469ba" [[package]] name = "cranelift-native" -version = "0.93.2" +version = "0.95.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "72668c7755f2b880665cb422c8ad2d56db58a88b9bebfef0b73edc2277c13c49" +checksum = "bb6b03e0e03801c4b3fd8ce0758a94750c07a44e7944cc0ffbf0d3f2e7c79b00" dependencies = [ "cranelift-codegen", "libc", @@ -1466,9 +1456,9 @@ dependencies = [ [[package]] name = "cranelift-wasm" -version = "0.93.2" +version = "0.95.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3852ce4b088b44ac4e29459573943009a70d1b192c8d77ef949b4e814f656fc1" +checksum = "ff3220489a3d928ad91e59dd7aeaa8b3de18afb554a6211213673a71c90737ac" dependencies = [ "cranelift-codegen", "cranelift-entity", @@ -3109,21 +3099,15 @@ dependencies = [ [[package]] name = "gimli" -version = "0.26.2" +version = "0.27.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "22030e2c5a68ec659fde1e949a745124b48e6fa8b045b7ed5bd1fe4ccc5c4e5d" +checksum = "ad0a93d233ebf96623465aad4046a8d3aa4da22d4f4beba5388838c8a434bbb4" dependencies = [ "fallible-iterator", "indexmap", "stable_deref_trait", ] -[[package]] -name = "gimli" -version = "0.27.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ad0a93d233ebf96623465aad4046a8d3aa4da22d4f4beba5388838c8a434bbb4" - [[package]] name = "git2" version = "0.16.1" @@ -5699,24 +5683,15 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "830b246a0e5f20af87141b25c173cd1b609bd7779a4617d6ec582abaf90870f3" -[[package]] -name = "object" -version = "0.29.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "21158b2c33aa6d4561f1c0a6ea283ca92bc54802a93b263e910746d679a7eb53" -dependencies = [ - "crc32fast", - "hashbrown 0.12.3", - "indexmap", - "memchr", -] - [[package]] name = "object" version = "0.30.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ea86265d3d3dcb6a27fc51bd29a4bf387fae9d2986b823079d4986af253eb439" dependencies = [ + "crc32fast", + "hashbrown 0.13.2", + "indexmap", "memchr", ] @@ -8371,9 +8346,9 @@ dependencies = [ [[package]] name = "regalloc2" -version = "0.5.1" +version = "0.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "300d4fbfb40c1c66a78ba3ddd41c1110247cf52f97b87d0f2fc9209bd49b030c" +checksum = "80535183cae11b149d618fbd3c37e38d7cda589d82d7769e196ca9a9042d7621" dependencies = [ "fxhash", "log", @@ -13081,9 +13056,9 @@ dependencies = [ [[package]] name = "wasmparser" -version = "0.100.0" +version = "0.102.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "64b20236ab624147dfbb62cf12a19aaf66af0e41b8398838b66e997d07d269d4" +checksum = "48134de3d7598219ab9eaf6b91b15d8e50d31da76b8519fe4ecfcec2cf35104b" dependencies = [ "indexmap", "url", @@ -13100,9 +13075,9 @@ dependencies = [ [[package]] name = "wasmtime" -version = "6.0.2" +version = "8.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "76a222f5fa1e14b2cefc286f1b68494d7a965f4bf57ec04c59bb62673d639af6" +checksum = "f907fdead3153cb9bfb7a93bbd5b62629472dc06dee83605358c64c52ed3dda9" dependencies = [ "anyhow", "bincode", @@ -13110,7 +13085,7 @@ dependencies = [ "indexmap", "libc", "log", - "object 0.29.0", + "object", "once_cell", "paste", "psm", @@ -13123,26 +13098,26 @@ dependencies = [ "wasmtime-environ", "wasmtime-jit", "wasmtime-runtime", - "windows-sys 0.42.0", + "windows-sys 0.45.0", ] [[package]] name = "wasmtime-asm-macros" -version = "6.0.2" +version = "8.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4407a7246e7d2f3d8fb1cf0c72fda8dbafdb6dd34d555ae8bea0e5ae031089cc" +checksum = "d3b9daa7c14cd4fa3edbf69de994408d5f4b7b0959ac13fa69d465f6597f810d" dependencies = [ "cfg-if", ] [[package]] name = "wasmtime-cache" -version = "6.0.2" +version = "8.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5ceb3adf61d654be0be67fffdce42447b0880481348785be5fe40b5dd7663a4c" +checksum = "c86437fa68626fe896e5afc69234bb2b5894949083586535f200385adfd71213" dependencies = [ "anyhow", - "base64 0.13.1", + "base64 0.21.0", "bincode", "directories-next", "file-per-thread-logger", @@ -13151,15 +13126,15 @@ dependencies = [ "serde", "sha2 0.10.6", "toml 0.5.11", - "windows-sys 0.42.0", + "windows-sys 0.45.0", "zstd 0.11.2+zstd.1.5.2", ] [[package]] name = "wasmtime-cranelift" -version = "6.0.2" +version = "8.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3c366bb8647e01fd08cb5589976284b00abfded5529b33d7e7f3f086c68304a4" +checksum = "b1cefde0cce8cb700b1b21b6298a3837dba46521affd7b8c38a9ee2c869eee04" dependencies = [ "anyhow", "cranelift-codegen", @@ -13167,27 +13142,43 @@ dependencies = [ "cranelift-frontend", "cranelift-native", "cranelift-wasm", - "gimli 0.26.2", + "gimli", "log", - "object 0.29.0", + "object", "target-lexicon", "thiserror", "wasmparser", + "wasmtime-cranelift-shared", + "wasmtime-environ", +] + +[[package]] +name = "wasmtime-cranelift-shared" +version = "8.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cd041e382ef5aea1b9fc78442394f1a4f6d676ce457e7076ca4cb3f397882f8b" +dependencies = [ + "anyhow", + "cranelift-codegen", + "cranelift-native", + "gimli", + "object", + "target-lexicon", "wasmtime-environ", ] [[package]] name = "wasmtime-environ" -version = "6.0.2" +version = "8.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "47b8b50962eae38ee319f7b24900b7cf371f03eebdc17400c1dc8575fc10c9a7" +checksum = "a990198cee4197423045235bf89d3359e69bd2ea031005f4c2d901125955c949" dependencies = [ "anyhow", "cranelift-entity", - "gimli 0.26.2", + "gimli", "indexmap", "log", - "object 0.29.0", + "object", "serde", "target-lexicon", "thiserror", @@ -13197,18 +13188,18 @@ dependencies = [ [[package]] name = "wasmtime-jit" -version = "6.0.2" +version = "8.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ffaed4f9a234ba5225d8e64eac7b4a5d13b994aeb37353cde2cbeb3febda9eaa" +checksum = "0de48df552cfca1c9b750002d3e07b45772dd033b0b206d5c0968496abf31244" dependencies = [ - "addr2line 0.17.0", + "addr2line", "anyhow", "bincode", "cfg-if", "cpp_demangle", - "gimli 0.26.2", + "gimli", "log", - "object 0.29.0", + "object", "rustc-demangle", "serde", "target-lexicon", @@ -13216,36 +13207,36 @@ dependencies = [ "wasmtime-jit-debug", "wasmtime-jit-icache-coherence", "wasmtime-runtime", - "windows-sys 0.42.0", + "windows-sys 0.45.0", ] [[package]] name = "wasmtime-jit-debug" -version = "6.0.2" +version = "8.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eed41cbcbf74ce3ff6f1d07d1b707888166dc408d1a880f651268f4f7c9194b2" +checksum = "6e0554b84c15a27d76281d06838aed94e13a77d7bf604bbbaf548aa20eb93846" dependencies = [ - "object 0.29.0", + "object", "once_cell", "rustix 0.36.13", ] [[package]] name = "wasmtime-jit-icache-coherence" -version = "6.0.2" +version = "8.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "43a28ae1e648461bfdbb79db3efdaee1bca5b940872e4175390f465593a2e54c" +checksum = "aecae978b13f7f67efb23bd827373ace4578f2137ec110bbf6a4a7cde4121bbd" dependencies = [ "cfg-if", "libc", - "windows-sys 0.42.0", + "windows-sys 0.45.0", ] [[package]] name = "wasmtime-runtime" -version = "6.0.2" +version = "8.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e704b126e4252788ccfc3526d4d4511d4b23c521bf123e447ac726c14545217b" +checksum = "658cf6f325232b6760e202e5255d823da5e348fdea827eff0a2a22319000b441" dependencies = [ "anyhow", "cc", @@ -13255,21 +13246,21 @@ dependencies = [ "log", "mach", "memfd", - "memoffset 0.6.5", + "memoffset 0.8.0", "paste", "rand 0.8.5", "rustix 0.36.13", "wasmtime-asm-macros", "wasmtime-environ", "wasmtime-jit-debug", - "windows-sys 0.42.0", + "windows-sys 0.45.0", ] [[package]] name = "wasmtime-types" -version = "6.0.2" +version = "8.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "83e5572c5727c1ee7e8f28717aaa8400e4d22dcbd714ea5457d85b5005206568" +checksum = "a4f6fffd2a1011887d57f07654dd112791e872e3ff4a2e626aee8059ee17f06f" dependencies = [ "cranelift-entity", "serde", diff --git a/client/executor/wasmtime/Cargo.toml b/client/executor/wasmtime/Cargo.toml index 58a1ffde0..f3e5dcd06 100644 --- a/client/executor/wasmtime/Cargo.toml +++ b/client/executor/wasmtime/Cargo.toml @@ -19,7 +19,7 @@ libc = "0.2.121" # When bumping wasmtime do not forget to also bump rustix # to exactly the same version as used by wasmtime! -wasmtime = { version = "6.0.2", default-features = false, features = [ +wasmtime = { version = "8.0.1", default-features = false, features = [ "cache", "cranelift", "jitdump", diff --git a/primitives/wasm-interface/Cargo.toml b/primitives/wasm-interface/Cargo.toml index c45ea7583..11804d705 100644 --- a/primitives/wasm-interface/Cargo.toml +++ b/primitives/wasm-interface/Cargo.toml @@ -18,7 +18,7 @@ codec = { package = "parity-scale-codec", version = "3.2.2", default-features = impl-trait-for-tuples = "0.2.2" log = { version = "0.4.17", optional = true } wasmi = { version = "0.13.2", optional = true } -wasmtime = { version = "6.0.2", default-features = false, optional = true } +wasmtime = { version = "8.0.1", default-features = false, optional = true } anyhow = { version = "1.0.68", optional = true } sp-std = { version = "5.0.0", default-features = false, path = "../std" } From bf92638c4994de4a520ff278ab95e7e955c1067a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Silva?= <123550+andresilva@users.noreply.github.com> Date: Wed, 24 May 2023 10:09:03 +0100 Subject: [PATCH 541/558] sc-client-db: add test for reverting finalized blocks (#14205) --- client/db/src/lib.rs | 32 ++++++++++++++++++++++++++++++++ 1 file changed, 32 insertions(+) diff --git a/client/db/src/lib.rs b/client/db/src/lib.rs index c62b9ce0e..43cef1f6e 100644 --- a/client/db/src/lib.rs +++ b/client/db/src/lib.rs @@ -3913,6 +3913,38 @@ pub(crate) mod tests { assert_eq!(1, backend.blockchain.leaves.read().highest_leaf().unwrap().0); } + #[test] + fn revert_finalized_blocks() { + let pruning_modes = [BlocksPruning::Some(10), BlocksPruning::KeepAll]; + + // we will create a chain with 11 blocks, finalize block #8 and then + // attempt to revert 5 blocks. + for pruning_mode in pruning_modes { + let backend = Backend::::new_test_with_tx_storage(pruning_mode, 1); + + let mut parent = Default::default(); + for i in 0..=10 { + parent = insert_block(&backend, i, parent, None, Default::default(), vec![], None) + .unwrap(); + } + + assert_eq!(backend.blockchain().info().best_number, 10); + + let block8 = backend.blockchain().hash(8).unwrap().unwrap(); + backend.finalize_block(block8, None).unwrap(); + backend.revert(5, true).unwrap(); + + match pruning_mode { + // we can only revert to blocks for which we have state, if pruning is enabled + // then the last state available will be that of the latest finalized block + BlocksPruning::Some(_) => + assert_eq!(backend.blockchain().info().finalized_number, 8), + // otherwise if we're not doing state pruning we can revert past finalized blocks + _ => assert_eq!(backend.blockchain().info().finalized_number, 5), + } + } + } + #[test] fn test_no_duplicated_leaves_allowed() { let backend: Backend = Backend::new_test(10, 10); From 3c8666b1906680ad9461a6c46fe17439629ab082 Mon Sep 17 00:00:00 2001 From: Dmitry Markin Date: Wed, 24 May 2023 12:24:09 +0300 Subject: [PATCH 542/558] Replace request-response incoming requests queue with `async-channel` (#14199) --- Cargo.lock | 4 +++ client/consensus/beefy/Cargo.toml | 1 + .../incoming_requests_handler.rs | 9 ++--- .../src/communication/request_response/mod.rs | 3 +- client/network/bitswap/Cargo.toml | 1 + client/network/bitswap/src/lib.rs | 8 ++--- client/network/light/Cargo.toml | 1 + .../src/light_client_requests/handler.rs | 12 ++++--- client/network/src/request_responses.rs | 36 +++++++++---------- client/network/sync/Cargo.toml | 1 + .../network/sync/src/block_request_handler.rs | 9 ++--- .../network/sync/src/state_request_handler.rs | 9 ++--- .../network/sync/src/warp_request_handler.rs | 12 +++---- 13 files changed, 53 insertions(+), 53 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index eefa2f873..7bffd9e16 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -9038,6 +9038,7 @@ name = "sc-consensus-beefy" version = "4.0.0-dev" dependencies = [ "array-bytes 4.2.0", + "async-channel", "async-trait", "fnv", "futures", @@ -9426,6 +9427,7 @@ dependencies = [ name = "sc-network-bitswap" version = "0.10.0-dev" dependencies = [ + "async-channel", "cid", "futures", "libp2p-identity", @@ -9502,6 +9504,7 @@ name = "sc-network-light" version = "0.10.0-dev" dependencies = [ "array-bytes 4.2.0", + "async-channel", "futures", "libp2p-identity", "log", @@ -9543,6 +9546,7 @@ name = "sc-network-sync" version = "0.10.0-dev" dependencies = [ "array-bytes 4.2.0", + "async-channel", "async-trait", "fork-tree", "futures", diff --git a/client/consensus/beefy/Cargo.toml b/client/consensus/beefy/Cargo.toml index 161d53777..7e3ddf688 100644 --- a/client/consensus/beefy/Cargo.toml +++ b/client/consensus/beefy/Cargo.toml @@ -10,6 +10,7 @@ homepage = "https://substrate.io" [dependencies] array-bytes = "4.1" +async-channel = "1.8.0" async-trait = "0.1.57" codec = { package = "parity-scale-codec", version = "3.2.2", features = ["derive"] } fnv = "1.0.6" diff --git a/client/consensus/beefy/src/communication/request_response/incoming_requests_handler.rs b/client/consensus/beefy/src/communication/request_response/incoming_requests_handler.rs index d4f4b59f0..6ed954da5 100644 --- a/client/consensus/beefy/src/communication/request_response/incoming_requests_handler.rs +++ b/client/consensus/beefy/src/communication/request_response/incoming_requests_handler.rs @@ -17,10 +17,7 @@ //! Helper for handling (i.e. answering) BEEFY justifications requests from a remote peer. use codec::Decode; -use futures::{ - channel::{mpsc, oneshot}, - StreamExt, -}; +use futures::{channel::oneshot, StreamExt}; use log::{debug, trace}; use sc_client_api::BlockBackend; use sc_network::{ @@ -102,11 +99,11 @@ impl IncomingRequest { /// /// Takes care of decoding and handling of invalid encoded requests. pub(crate) struct IncomingRequestReceiver { - raw: mpsc::Receiver, + raw: async_channel::Receiver, } impl IncomingRequestReceiver { - pub fn new(inner: mpsc::Receiver) -> Self { + pub fn new(inner: async_channel::Receiver) -> Self { Self { raw: inner } } diff --git a/client/consensus/beefy/src/communication/request_response/mod.rs b/client/consensus/beefy/src/communication/request_response/mod.rs index 545ab18cf..1801512fa 100644 --- a/client/consensus/beefy/src/communication/request_response/mod.rs +++ b/client/consensus/beefy/src/communication/request_response/mod.rs @@ -23,7 +23,6 @@ pub(crate) mod outgoing_requests_engine; pub use incoming_requests_handler::BeefyJustifsRequestHandler; -use futures::channel::mpsc; use std::time::Duration; use codec::{Decode, Encode, Error as CodecError}; @@ -54,7 +53,7 @@ pub(crate) fn on_demand_justifications_protocol_config>( ) -> (IncomingRequestReceiver, RequestResponseConfig) { let name = justifications_protocol_name(genesis_hash, fork_id); let fallback_names = vec![]; - let (tx, rx) = mpsc::channel(JUSTIF_CHANNEL_SIZE); + let (tx, rx) = async_channel::bounded(JUSTIF_CHANNEL_SIZE); let rx = IncomingRequestReceiver::new(rx); let cfg = RequestResponseConfig { name, diff --git a/client/network/bitswap/Cargo.toml b/client/network/bitswap/Cargo.toml index a953676ec..0a3c56970 100644 --- a/client/network/bitswap/Cargo.toml +++ b/client/network/bitswap/Cargo.toml @@ -16,6 +16,7 @@ targets = ["x86_64-unknown-linux-gnu"] prost-build = "0.11" [dependencies] +async-channel = "1.8.0" cid = "0.8.6" futures = "0.3.21" libp2p-identity = { version = "0.1.2", features = ["peerid"] } diff --git a/client/network/bitswap/src/lib.rs b/client/network/bitswap/src/lib.rs index a7857f6ee..beaaa8fd0 100644 --- a/client/network/bitswap/src/lib.rs +++ b/client/network/bitswap/src/lib.rs @@ -21,7 +21,7 @@ //! CID is expected to reference 256-bit Blake2b transaction hash. use cid::{self, Version}; -use futures::{channel::mpsc, StreamExt}; +use futures::StreamExt; use libp2p_identity::PeerId; use log::{debug, error, trace}; use prost::Message; @@ -93,13 +93,13 @@ impl Prefix { /// Bitswap request handler pub struct BitswapRequestHandler { client: Arc + Send + Sync>, - request_receiver: mpsc::Receiver, + request_receiver: async_channel::Receiver, } impl BitswapRequestHandler { /// Create a new [`BitswapRequestHandler`]. pub fn new(client: Arc + Send + Sync>) -> (Self, ProtocolConfig) { - let (tx, request_receiver) = mpsc::channel(MAX_REQUEST_QUEUE); + let (tx, request_receiver) = async_channel::bounded(MAX_REQUEST_QUEUE); let config = ProtocolConfig { name: ProtocolName::from(PROTOCOL_NAME), @@ -289,7 +289,7 @@ pub enum BitswapError { #[cfg(test)] mod tests { use super::*; - use futures::{channel::oneshot, SinkExt}; + use futures::channel::oneshot; use sc_block_builder::BlockBuilderProvider; use schema::bitswap::{ message::{wantlist::Entry, Wantlist}, diff --git a/client/network/light/Cargo.toml b/client/network/light/Cargo.toml index cd0dfbca5..ec2c2c077 100644 --- a/client/network/light/Cargo.toml +++ b/client/network/light/Cargo.toml @@ -16,6 +16,7 @@ targets = ["x86_64-unknown-linux-gnu"] prost-build = "0.11" [dependencies] +async-channel = "1.8.0" array-bytes = "4.1" codec = { package = "parity-scale-codec", version = "3.2.2", features = [ "derive", diff --git a/client/network/light/src/light_client_requests/handler.rs b/client/network/light/src/light_client_requests/handler.rs index 2a68ebe9c..2a0047b40 100644 --- a/client/network/light/src/light_client_requests/handler.rs +++ b/client/network/light/src/light_client_requests/handler.rs @@ -24,7 +24,7 @@ use crate::schema; use codec::{self, Decode, Encode}; -use futures::{channel::mpsc, prelude::*}; +use futures::prelude::*; use libp2p_identity::PeerId; use log::{debug, trace}; use prost::Message; @@ -43,9 +43,13 @@ use std::{marker::PhantomData, sync::Arc}; const LOG_TARGET: &str = "light-client-request-handler"; +/// Incoming requests bounded queue size. For now due to lack of data on light client request +/// handling in production systems, this value is chosen to match the block request limit. +const MAX_LIGHT_REQUEST_QUEUE: usize = 20; + /// Handler for incoming light client requests from a remote peer. pub struct LightClientRequestHandler { - request_receiver: mpsc::Receiver, + request_receiver: async_channel::Receiver, /// Blockchain client. client: Arc, _block: PhantomData, @@ -62,9 +66,7 @@ where fork_id: Option<&str>, client: Arc, ) -> (Self, ProtocolConfig) { - // For now due to lack of data on light client request handling in production systems, this - // value is chosen to match the block request limit. - let (tx, request_receiver) = mpsc::channel(20); + let (tx, request_receiver) = async_channel::bounded(MAX_LIGHT_REQUEST_QUEUE); let mut protocol_config = super::generate_protocol_config( protocol_id, diff --git a/client/network/src/request_responses.rs b/client/network/src/request_responses.rs index e0f4074e0..e21ff3a34 100644 --- a/client/network/src/request_responses.rs +++ b/client/network/src/request_responses.rs @@ -36,10 +36,7 @@ use crate::{types::ProtocolName, ReputationChange}; -use futures::{ - channel::{mpsc, oneshot}, - prelude::*, -}; +use futures::{channel::oneshot, prelude::*}; use libp2p::{ core::{Endpoint, Multiaddr}, request_response::{self, Behaviour, Codec, Message, ProtocolSupport, ResponseChannel}, @@ -126,7 +123,7 @@ pub struct ProtocolConfig { /// other peers. If this is `Some` but the channel is closed, then the local node will /// advertise support for this protocol, but any incoming request will lead to an error being /// sent back. - pub inbound_queue: Option>, + pub inbound_queue: Option>, } /// A single request received by a peer on a request-response protocol. @@ -259,8 +256,10 @@ pub struct RequestResponsesBehaviour { /// /// Contains the underlying libp2p request-response [`Behaviour`], plus an optional /// "response builder" used to build responses for incoming requests. - protocols: - HashMap, Option>)>, + protocols: HashMap< + ProtocolName, + (Behaviour, Option>), + >, /// Pending requests, passed down to a request-response [`Behaviour`], awaiting a reply. pending_requests: @@ -295,7 +294,10 @@ struct MessageRequest { request: Vec, channel: ResponseChannel, ()>>, protocol: ProtocolName, - resp_builder: Option>, + // A builder used for building responses for incoming requests. Note that we use + // `async_channel` and not `mpsc` on purpose, because `mpsc::channel` allocates an extra + // message slot for every cloned `Sender` and this breaks a back-pressure mechanism. + resp_builder: Option>, // Once we get incoming request we save all params, create an async call to Peerset // to get the reputation of the peer. get_peer_reputation: Pin> + Send>>, @@ -618,10 +620,12 @@ impl NetworkBehaviour for RequestResponsesBehaviour { // Submit the request to the "response builder" passed by the user at // initialization. - if let Some(mut resp_builder) = resp_builder { + if let Some(resp_builder) = resp_builder { // If the response builder is too busy, silently drop `tx`. This // will be reported by the corresponding request-response [`Behaviour`] // through an `InboundFailure::Omission` event. + // Note that we use `async_channel::bounded` and not `mpsc::channel` + // because the latter allocates an extra slot for every cloned sender. let _ = resp_builder.try_send(IncomingRequest { peer, payload: request, @@ -1036,11 +1040,7 @@ impl Codec for GenericCodec { mod tests { use super::*; - use futures::{ - channel::{mpsc, oneshot}, - executor::LocalPool, - task::Spawn, - }; + use futures::{channel::oneshot, executor::LocalPool, task::Spawn}; use libp2p::{ core::{ transport::{MemoryTransport, Transport}, @@ -1112,7 +1112,7 @@ mod tests { // Build swarms whose behaviour is [`RequestResponsesBehaviour`]. let mut swarms = (0..2) .map(|_| { - let (tx, mut rx) = mpsc::channel::(64); + let (tx, mut rx) = async_channel::bounded::(64); pool.spawner() .spawn_obj( @@ -1215,7 +1215,7 @@ mod tests { // Build swarms whose behaviour is [`RequestResponsesBehaviour`]. let mut swarms = (0..2) .map(|_| { - let (tx, mut rx) = mpsc::channel::(64); + let (tx, mut rx) = async_channel::bounded::(64); pool.spawner() .spawn_obj( @@ -1353,8 +1353,8 @@ mod tests { }; let (mut swarm_2, mut swarm_2_handler_1, mut swarm_2_handler_2, listen_add_2, peerset) = { - let (tx_1, rx_1) = mpsc::channel(64); - let (tx_2, rx_2) = mpsc::channel(64); + let (tx_1, rx_1) = async_channel::bounded(64); + let (tx_2, rx_2) = async_channel::bounded(64); let protocol_configs = vec![ ProtocolConfig { diff --git a/client/network/sync/Cargo.toml b/client/network/sync/Cargo.toml index ce7135960..81d39d87f 100644 --- a/client/network/sync/Cargo.toml +++ b/client/network/sync/Cargo.toml @@ -17,6 +17,7 @@ prost-build = "0.11" [dependencies] array-bytes = "4.1" +async-channel = "1.8.0" async-trait = "0.1.58" codec = { package = "parity-scale-codec", version = "3.2.2", features = ["derive"] } futures = "0.3.21" diff --git a/client/network/sync/src/block_request_handler.rs b/client/network/sync/src/block_request_handler.rs index 256c0ad38..df17d56ba 100644 --- a/client/network/sync/src/block_request_handler.rs +++ b/client/network/sync/src/block_request_handler.rs @@ -23,10 +23,7 @@ use crate::{ }; use codec::{Decode, Encode}; -use futures::{ - channel::{mpsc, oneshot}, - stream::StreamExt, -}; +use futures::{channel::oneshot, stream::StreamExt}; use libp2p::PeerId; use log::debug; use lru::LruCache; @@ -136,7 +133,7 @@ enum SeenRequestsValue { /// Handler for incoming block requests from a remote peer. pub struct BlockRequestHandler { client: Arc, - request_receiver: mpsc::Receiver, + request_receiver: async_channel::Receiver, /// Maps from request to number of times we have seen this request. /// /// This is used to check if a peer is spamming us with the same request. @@ -157,7 +154,7 @@ where ) -> (Self, ProtocolConfig) { // Reserve enough request slots for one request per peer when we are at the maximum // number of peers. - let (tx, request_receiver) = mpsc::channel(num_peer_hint); + let (tx, request_receiver) = async_channel::bounded(num_peer_hint); let mut protocol_config = generate_protocol_config( protocol_id, diff --git a/client/network/sync/src/state_request_handler.rs b/client/network/sync/src/state_request_handler.rs index 93597453a..e5b0da6ce 100644 --- a/client/network/sync/src/state_request_handler.rs +++ b/client/network/sync/src/state_request_handler.rs @@ -20,10 +20,7 @@ use crate::schema::v1::{KeyValueStateEntry, StateEntry, StateRequest, StateResponse}; use codec::{Decode, Encode}; -use futures::{ - channel::{mpsc, oneshot}, - stream::StreamExt, -}; +use futures::{channel::oneshot, stream::StreamExt}; use libp2p::PeerId; use log::{debug, trace}; use lru::LruCache; @@ -114,7 +111,7 @@ enum SeenRequestsValue { /// Handler for incoming block requests from a remote peer. pub struct StateRequestHandler { client: Arc, - request_receiver: mpsc::Receiver, + request_receiver: async_channel::Receiver, /// Maps from request to number of times we have seen this request. /// /// This is used to check if a peer is spamming us with the same request. @@ -135,7 +132,7 @@ where ) -> (Self, ProtocolConfig) { // Reserve enough request slots for one request per peer when we are at the maximum // number of peers. - let (tx, request_receiver) = mpsc::channel(num_peer_hint); + let (tx, request_receiver) = async_channel::bounded(num_peer_hint); let mut protocol_config = generate_protocol_config( protocol_id, diff --git a/client/network/sync/src/warp_request_handler.rs b/client/network/sync/src/warp_request_handler.rs index 7061d6485..a49a65af5 100644 --- a/client/network/sync/src/warp_request_handler.rs +++ b/client/network/sync/src/warp_request_handler.rs @@ -17,10 +17,7 @@ //! Helper for handling (i.e. answering) grandpa warp sync requests from a remote peer. use codec::Decode; -use futures::{ - channel::{mpsc, oneshot}, - stream::StreamExt, -}; +use futures::{channel::oneshot, stream::StreamExt}; use log::debug; use sc_network::{ @@ -36,6 +33,9 @@ use std::{sync::Arc, time::Duration}; const MAX_RESPONSE_SIZE: u64 = 16 * 1024 * 1024; +/// Incoming warp requests bounded queue size. +const MAX_WARP_REQUEST_QUEUE: usize = 20; + /// Generates a [`RequestResponseConfig`] for the grandpa warp sync request protocol, refusing /// incoming requests. pub fn generate_request_response_config>( @@ -72,7 +72,7 @@ fn generate_legacy_protocol_name(protocol_id: ProtocolId) -> String { /// Handler for incoming grandpa warp sync requests from a remote peer. pub struct RequestHandler { backend: Arc>, - request_receiver: mpsc::Receiver, + request_receiver: async_channel::Receiver, } impl RequestHandler { @@ -83,7 +83,7 @@ impl RequestHandler { fork_id: Option<&str>, backend: Arc>, ) -> (Self, RequestResponseConfig) { - let (tx, request_receiver) = mpsc::channel(20); + let (tx, request_receiver) = async_channel::bounded(MAX_WARP_REQUEST_QUEUE); let mut request_response_config = generate_request_response_config(protocol_id, genesis_hash, fork_id); From 7c195bfa7d90c91144bc128aabdac0b108a8a441 Mon Sep 17 00:00:00 2001 From: Adrian Catangiu Date: Thu, 25 May 2023 15:16:37 +0300 Subject: [PATCH 543/558] sc-consensus-beefy: graceful support for pallet-beefy reset (#14217) BEEFY consensus can be restarted by resetting "genesisBlock" in pallet-beefy, but we don't want to also reset authority set IDs so that they are uniquely identified across the entire chain history regardless of how many times BEEFY consensus has been reset/restarted. This is why the client now also accepts initial authority_set_id != 0. BEEFY client now detects pallet-beefy reset/reinit and errors-out and asks for a restart. BEEFY client persisted state should be discarded on client restarts following pallet-beefy reset/reinit. End result is BEEFY client/voter can now completely reinitialize using "new" on-chain info following pallet-beefy reset/reinit, discarding old state. Fixes #14203 Fixes #14204 Signed-off-by: acatangiu --- client/consensus/beefy/src/aux_schema.rs | 6 +- .../incoming_requests_handler.rs | 6 +- client/consensus/beefy/src/error.rs | 5 +- client/consensus/beefy/src/lib.rs | 86 +++++++++++-------- client/consensus/beefy/src/tests.rs | 44 ++++++++-- client/consensus/beefy/src/worker.rs | 49 +++++++++-- frame/beefy/src/lib.rs | 4 +- 7 files changed, 141 insertions(+), 59 deletions(-) diff --git a/client/consensus/beefy/src/aux_schema.rs b/client/consensus/beefy/src/aux_schema.rs index 84186140b..409eb30d0 100644 --- a/client/consensus/beefy/src/aux_schema.rs +++ b/client/consensus/beefy/src/aux_schema.rs @@ -28,7 +28,7 @@ use sp_runtime::traits::Block as BlockT; const VERSION_KEY: &[u8] = b"beefy_auxschema_version"; const WORKER_STATE_KEY: &[u8] = b"beefy_voter_state"; -const CURRENT_VERSION: u32 = 3; +const CURRENT_VERSION: u32 = 4; pub(crate) fn write_current_version(backend: &BE) -> ClientResult<()> { info!(target: LOG_TARGET, "🥩 write aux schema version {:?}", CURRENT_VERSION); @@ -63,8 +63,8 @@ where match version { None => (), - Some(1) | Some(2) => (), // versions 1 & 2 are obsolete and should be simply ignored - Some(3) => return load_decode::<_, PersistedState>(backend, WORKER_STATE_KEY), + Some(1) | Some(2) | Some(3) => (), // versions 1, 2 & 3 are obsolete and should be ignored + Some(4) => return load_decode::<_, PersistedState>(backend, WORKER_STATE_KEY), other => return Err(ClientError::Backend(format!("Unsupported BEEFY DB version: {:?}", other))), } diff --git a/client/consensus/beefy/src/communication/request_response/incoming_requests_handler.rs b/client/consensus/beefy/src/communication/request_response/incoming_requests_handler.rs index 6ed954da5..46056ac30 100644 --- a/client/consensus/beefy/src/communication/request_response/incoming_requests_handler.rs +++ b/client/consensus/beefy/src/communication/request_response/incoming_requests_handler.rs @@ -18,7 +18,7 @@ use codec::Decode; use futures::{channel::oneshot, StreamExt}; -use log::{debug, trace}; +use log::{debug, error, trace}; use sc_client_api::BlockBackend; use sc_network::{ config as netconfig, config::RequestResponseConfig, types::ProtocolName, PeerId, @@ -215,5 +215,9 @@ where }, } } + error!( + target: crate::LOG_TARGET, + "🥩 On-demand requests receiver stream terminated, closing worker." + ); } } diff --git a/client/consensus/beefy/src/error.rs b/client/consensus/beefy/src/error.rs index 16afbf218..08b9960f4 100644 --- a/client/consensus/beefy/src/error.rs +++ b/client/consensus/beefy/src/error.rs @@ -18,7 +18,7 @@ //! BEEFY gadget specific errors //! -//! Used for BEEFY gadget interal error handling only +//! Used for BEEFY gadget internal error handling only use std::fmt::Debug; @@ -34,6 +34,8 @@ pub enum Error { Signature(String), #[error("Session uninitialized")] UninitSession, + #[error("pallet-beefy was reset, please restart voter")] + ConsensusReset, } #[cfg(test)] @@ -45,6 +47,7 @@ impl PartialEq for Error { (Error::RuntimeApi(_), Error::RuntimeApi(_)) => true, (Error::Signature(s1), Error::Signature(s2)) => s1 == s2, (Error::UninitSession, Error::UninitSession) => true, + (Error::ConsensusReset, Error::ConsensusReset) => true, _ => false, } } diff --git a/client/consensus/beefy/src/lib.rs b/client/consensus/beefy/src/lib.rs index d3e5e4bc6..c55849ff7 100644 --- a/client/consensus/beefy/src/lib.rs +++ b/client/consensus/beefy/src/lib.rs @@ -47,7 +47,6 @@ use sp_blockchain::{ use sp_consensus::{Error as ConsensusError, SyncOracle}; use sp_consensus_beefy::{ crypto::AuthorityId, BeefyApi, MmrRootHash, PayloadProvider, ValidatorSet, BEEFY_ENGINE_ID, - GENESIS_AUTHORITY_SET_ID, }; use sp_keystore::KeystorePtr; use sp_mmr_primitives::MmrApi; @@ -282,8 +281,14 @@ pub async fn start_beefy_gadget( let persisted_state = match wait_for_runtime_pallet(&*runtime, &mut gossip_engine, &mut finality_notifications) .await - .and_then(|best_grandpa| { - load_or_init_voter_state(&*backend, &*runtime, best_grandpa, min_block_delta) + .and_then(|(beefy_genesis, best_grandpa)| { + load_or_init_voter_state( + &*backend, + &*runtime, + beefy_genesis, + best_grandpa, + min_block_delta, + ) }) { Ok(state) => state, Err(e) => { @@ -316,9 +321,9 @@ pub async fn start_beefy_gadget( persisted_state, }; - futures::future::join( - worker.run(block_import_justif, finality_notifications), - on_demand_justifications_handler.run(), + futures::future::select( + Box::pin(worker.run(block_import_justif, finality_notifications)), + Box::pin(on_demand_justifications_handler.run()), ) .await; } @@ -326,6 +331,7 @@ pub async fn start_beefy_gadget( fn load_or_init_voter_state( backend: &BE, runtime: &R, + beefy_genesis: NumberFor, best_grandpa: ::Header, min_block_delta: u32, ) -> ClientResult> @@ -335,17 +341,22 @@ where R: ProvideRuntimeApi, R::Api: BeefyApi, { - // Initialize voter state from AUX DB or from pallet genesis. - if let Some(mut state) = crate::aux_schema::load_persistent(backend)? { - // Overwrite persisted state with current best GRANDPA block. - state.set_best_grandpa(best_grandpa); - // Overwrite persisted data with newly provided `min_block_delta`. - state.set_min_block_delta(min_block_delta); - info!(target: LOG_TARGET, "🥩 Loading BEEFY voter state from db: {:?}.", state); - Ok(state) - } else { - initialize_voter_state(backend, runtime, best_grandpa, min_block_delta) - } + // Initialize voter state from AUX DB if compatible. + crate::aux_schema::load_persistent(backend)? + // Verify state pallet genesis matches runtime. + .filter(|state| state.pallet_genesis() == beefy_genesis) + .and_then(|mut state| { + // Overwrite persisted state with current best GRANDPA block. + state.set_best_grandpa(best_grandpa.clone()); + // Overwrite persisted data with newly provided `min_block_delta`. + state.set_min_block_delta(min_block_delta); + info!(target: LOG_TARGET, "🥩 Loading BEEFY voter state from db: {:?}.", state); + Some(Ok(state)) + }) + // No valid voter-state persisted, re-initialize from pallet genesis. + .unwrap_or_else(|| { + initialize_voter_state(backend, runtime, beefy_genesis, best_grandpa, min_block_delta) + }) } // If no persisted state present, walk back the chain from first GRANDPA notification to either: @@ -355,6 +366,7 @@ where fn initialize_voter_state( backend: &BE, runtime: &R, + beefy_genesis: NumberFor, best_grandpa: ::Header, min_block_delta: u32, ) -> ClientResult> @@ -369,6 +381,7 @@ where .beefy_genesis(best_grandpa.hash()) .ok() .flatten() + .filter(|genesis| *genesis == beefy_genesis) .ok_or_else(|| ClientError::Backend("BEEFY pallet expected to be active.".into()))?; // Walk back the imported blocks and initialize voter either, at the last block with // a BEEFY justification, or at pallet genesis block; voter will resume from there. @@ -396,16 +409,20 @@ where rounds.conclude(best_beefy); sessions.push_front(rounds); } - let state = - PersistedState::checked_new(best_grandpa, best_beefy, sessions, min_block_delta) - .ok_or_else(|| ClientError::Backend("Invalid BEEFY chain".into()))?; + let state = PersistedState::checked_new( + best_grandpa, + best_beefy, + sessions, + min_block_delta, + beefy_genesis, + ) + .ok_or_else(|| ClientError::Backend("Invalid BEEFY chain".into()))?; break state } if *header.number() == beefy_genesis { // We've reached BEEFY genesis, initialize voter here. - let genesis_set = - expect_validator_set(runtime, header.hash()).and_then(genesis_set_sanity_check)?; + let genesis_set = expect_validator_set(runtime, header.hash())?; info!( target: LOG_TARGET, "🥩 Loading BEEFY voter state from genesis on what appears to be first startup. \ @@ -415,8 +432,14 @@ where ); sessions.push_front(Rounds::new(beefy_genesis, genesis_set)); - break PersistedState::checked_new(best_grandpa, Zero::zero(), sessions, min_block_delta) - .ok_or_else(|| ClientError::Backend("Invalid BEEFY chain".into()))? + break PersistedState::checked_new( + best_grandpa, + Zero::zero(), + sessions, + min_block_delta, + beefy_genesis, + ) + .ok_or_else(|| ClientError::Backend("Invalid BEEFY chain".into()))? } if let Some(active) = worker::find_authorities_change::(&header) { @@ -451,7 +474,7 @@ async fn wait_for_runtime_pallet( runtime: &R, mut gossip_engine: &mut GossipEngine, finality: &mut Fuse>, -) -> ClientResult<::Header> +) -> ClientResult<(NumberFor, ::Header)> where B: Block, R: ProvideRuntimeApi, @@ -474,7 +497,7 @@ where "🥩 BEEFY pallet available: block {:?} beefy genesis {:?}", notif.header.number(), start ); - return Ok(notif.header) + return Ok((start, notif.header)) } } }, @@ -488,17 +511,6 @@ where Err(ClientError::Backend(err_msg)) } -fn genesis_set_sanity_check( - active: ValidatorSet, -) -> ClientResult> { - if active.id() == GENESIS_AUTHORITY_SET_ID { - Ok(active) - } else { - error!(target: LOG_TARGET, "🥩 Unexpected ID for genesis validator set {:?}.", active); - Err(ClientError::Backend("BEEFY Genesis sanity check failed.".into())) - } -} - fn expect_validator_set( runtime: &R, at_hash: B::Hash, diff --git a/client/consensus/beefy/src/tests.rs b/client/consensus/beefy/src/tests.rs index 288a9fde5..8bb6930ca 100644 --- a/client/consensus/beefy/src/tests.rs +++ b/client/consensus/beefy/src/tests.rs @@ -373,8 +373,9 @@ async fn voter_init_setup( gossip_validator, None, ); - let best_grandpa = wait_for_runtime_pallet(api, &mut gossip_engine, finality).await.unwrap(); - load_or_init_voter_state(&*backend, api, best_grandpa, 1) + let (beefy_genesis, best_grandpa) = + wait_for_runtime_pallet(api, &mut gossip_engine, finality).await.unwrap(); + load_or_init_voter_state(&*backend, api, beefy_genesis, best_grandpa, 1) } // Spawns beefy voters. Returns a future to spawn on the runtime. @@ -981,9 +982,7 @@ async fn should_initialize_voter_at_genesis() { // push 15 blocks with `AuthorityChange` digests every 10 blocks let hashes = net.generate_blocks_and_sync(15, 10, &validator_set, false).await; - let mut finality = net.peer(0).client().as_client().finality_notification_stream().fuse(); - // finalize 13 without justifications net.peer(0).client().as_client().finalize_block(hashes[13], None).unwrap(); @@ -1022,11 +1021,9 @@ async fn should_initialize_voter_at_custom_genesis() { let custom_pallet_genesis = 7; let api = TestApi::new(custom_pallet_genesis, &validator_set, GOOD_MMR_ROOT); - // push 15 blocks with `AuthorityChange` digests every 10 blocks - let hashes = net.generate_blocks_and_sync(15, 10, &validator_set, false).await; - + // push 15 blocks with `AuthorityChange` digests every 15 blocks + let hashes = net.generate_blocks_and_sync(15, 15, &validator_set, false).await; let mut finality = net.peer(0).client().as_client().finality_notification_stream().fuse(); - // finalize 3, 5, 8 without justifications net.peer(0).client().as_client().finalize_block(hashes[3], None).unwrap(); net.peer(0).client().as_client().finalize_block(hashes[5], None).unwrap(); @@ -1053,6 +1050,35 @@ async fn should_initialize_voter_at_custom_genesis() { assert!(verify_persisted_version(&*backend)); let state = load_persistent(&*backend).unwrap().unwrap(); assert_eq!(state, persisted_state); + + // now re-init after genesis changes + + // should ignore existing aux db state and reinit at new genesis + let new_validator_set = ValidatorSet::new(make_beefy_ids(keys), 42).unwrap(); + let new_pallet_genesis = 10; + let api = TestApi::new(new_pallet_genesis, &new_validator_set, GOOD_MMR_ROOT); + + net.peer(0).client().as_client().finalize_block(hashes[10], None).unwrap(); + // load persistent state - state preset in DB, but with different pallet genesis + let new_persisted_state = voter_init_setup(&mut net, &mut finality, &api).await.unwrap(); + + // verify voter initialized with single session starting at block `new_pallet_genesis` (10) + let sessions = new_persisted_state.voting_oracle().sessions(); + assert_eq!(sessions.len(), 1); + assert_eq!(sessions[0].session_start(), new_pallet_genesis); + let rounds = new_persisted_state.active_round().unwrap(); + assert_eq!(rounds.session_start(), new_pallet_genesis); + assert_eq!(rounds.validator_set_id(), new_validator_set.id()); + + // verify next vote target is mandatory block 10 + assert_eq!(new_persisted_state.best_beefy_block(), 0); + assert_eq!(new_persisted_state.best_grandpa_number(), 10); + assert_eq!(new_persisted_state.voting_oracle().voting_target(), Some(new_pallet_genesis)); + + // verify state also saved to db + assert!(verify_persisted_version(&*backend)); + let state = load_persistent(&*backend).unwrap().unwrap(); + assert_eq!(state, new_persisted_state); } #[tokio::test] @@ -1166,7 +1192,7 @@ async fn beefy_finalizing_after_pallet_genesis() { sp_tracing::try_init_simple(); let peers = [BeefyKeyring::Alice, BeefyKeyring::Bob]; - let validator_set = ValidatorSet::new(make_beefy_ids(&peers), 0).unwrap(); + let validator_set = ValidatorSet::new(make_beefy_ids(&peers), 14).unwrap(); let session_len = 10; let min_block_delta = 1; let pallet_genesis = 15; diff --git a/client/consensus/beefy/src/worker.rs b/client/consensus/beefy/src/worker.rs index c05de197d..cbf58e56b 100644 --- a/client/consensus/beefy/src/worker.rs +++ b/client/consensus/beefy/src/worker.rs @@ -69,6 +69,9 @@ pub(crate) enum RoundAction { /// Responsible for the voting strategy. /// It chooses which incoming votes to accept and which votes to generate. /// Keeps track of voting seen for current and future rounds. +/// +/// Note: this is part of `PersistedState` so any changes here should also bump +/// aux-db schema version. #[derive(Debug, Decode, Encode, PartialEq)] pub(crate) struct VoterOracle { /// Queue of known sessions. Keeps track of voting rounds (block numbers) within each session. @@ -256,6 +259,9 @@ impl VoterOracle { } } +/// BEEFY voter state persisted in aux DB. +/// +/// Note: Any changes here should also bump aux-db schema version. #[derive(Debug, Decode, Encode, PartialEq)] pub(crate) struct PersistedState { /// Best block we voted on. @@ -263,6 +269,8 @@ pub(crate) struct PersistedState { /// Chooses which incoming votes to accept and which votes to generate. /// Keeps track of voting seen for current and future rounds. voting_oracle: VoterOracle, + /// Pallet-beefy genesis block - block number when BEEFY consensus started for this chain. + pallet_genesis: NumberFor, } impl PersistedState { @@ -271,9 +279,19 @@ impl PersistedState { best_beefy: NumberFor, sessions: VecDeque>, min_block_delta: u32, + pallet_genesis: NumberFor, ) -> Option { - VoterOracle::checked_new(sessions, min_block_delta, grandpa_header, best_beefy) - .map(|voting_oracle| PersistedState { best_voted: Zero::zero(), voting_oracle }) + VoterOracle::checked_new(sessions, min_block_delta, grandpa_header, best_beefy).map( + |voting_oracle| PersistedState { + best_voted: Zero::zero(), + voting_oracle, + pallet_genesis, + }, + ) + } + + pub fn pallet_genesis(&self) -> NumberFor { + self.pallet_genesis } pub(crate) fn set_min_block_delta(&mut self, min_block_delta: u32) { @@ -411,7 +429,10 @@ where ); } - fn handle_finality_notification(&mut self, notification: &FinalityNotification) { + fn handle_finality_notification( + &mut self, + notification: &FinalityNotification, + ) -> Result<(), Error> { debug!( target: LOG_TARGET, "🥩 Finality notification: header {:?} tree_route {:?}", @@ -420,6 +441,18 @@ where ); let header = ¬ification.header; + self.runtime + .runtime_api() + .beefy_genesis(header.hash()) + .ok() + .flatten() + .filter(|genesis| *genesis == self.persisted_state.pallet_genesis) + .ok_or_else(|| { + let err = Error::ConsensusReset; + error!(target: LOG_TARGET, "🥩 Error: {}", err); + err + })?; + if *header.number() > self.best_grandpa_block() { // update best GRANDPA finalized block we have seen self.persisted_state.set_best_grandpa(header.clone()); @@ -451,6 +484,8 @@ where error!(target: LOG_TARGET, "🥩 Voter error: {:?}", e); } } + + Ok(()) } /// Based on [VoterOracle] this vote is either processed here or discarded. @@ -813,9 +848,9 @@ where // Use `select_biased!` to prioritize order below. // Process finality notifications first since these drive the voter. notification = finality_notifications.next() => { - if let Some(notification) = notification { - self.handle_finality_notification(¬ification); - } else { + if notification.and_then(|notif| { + self.handle_finality_notification(¬if).ok() + }).is_none() { error!(target: LOG_TARGET, "🥩 Finality stream terminated, closing worker."); return; } @@ -1086,6 +1121,7 @@ pub(crate) mod tests { }; let backend = peer.client().as_backend(); + let beefy_genesis = 1; let api = Arc::new(TestApi::with_validator_set(&genesis_validator_set)); let network = peer.network_service().clone(); let sync = peer.sync_service().clone(); @@ -1118,6 +1154,7 @@ pub(crate) mod tests { Zero::zero(), vec![Rounds::new(One::one(), genesis_validator_set)].into(), min_block_delta, + beefy_genesis, ) .unwrap(); let payload_provider = MmrRootProvider::new(api.clone()); diff --git a/frame/beefy/src/lib.rs b/frame/beefy/src/lib.rs index 6a248da2b..d188226c3 100644 --- a/frame/beefy/src/lib.rs +++ b/frame/beefy/src/lib.rs @@ -148,8 +148,8 @@ pub mod pallet { StorageMap<_, Twox64Concat, sp_consensus_beefy::ValidatorSetId, SessionIndex>; /// Block number where BEEFY consensus is enabled/started. - /// If changing this, make sure `Self::ValidatorSetId` is also reset to - /// `GENESIS_AUTHORITY_SET_ID` in the state of the new block number configured here. + /// By changing this (through governance or sudo), BEEFY consensus is effectively + /// restarted from the new block number. #[pallet::storage] #[pallet::getter(fn genesis_block)] pub(super) type GenesisBlock = From a2037bdedef0aa812017efee1d0fa63d4f5fcbae Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bastian=20K=C3=B6cher?= Date: Mon, 29 May 2023 11:26:38 +0100 Subject: [PATCH 544/558] sp-api: Set correct where bound in the generated code (#14252) The where bound for the `create_metadata` function wasn't correct. This pr fixes this by using the where bound declared at the type declaration augmented with the manual where bound. --- Cargo.lock | 1 + .../api/proc-macro/src/runtime_metadata.rs | 13 ++++-- primitives/api/test/Cargo.toml | 1 + .../ui/positive_cases/custom_where_bound.rs | 46 +++++++++++++++++++ 4 files changed, 56 insertions(+), 5 deletions(-) create mode 100644 primitives/api/test/tests/ui/positive_cases/custom_where_bound.rs diff --git a/Cargo.lock b/Cargo.lock index 7bffd9e16..0f39783f6 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -10632,6 +10632,7 @@ dependencies = [ "parity-scale-codec", "rustversion", "sc-block-builder", + "scale-info", "sp-api", "sp-consensus", "sp-core", diff --git a/primitives/api/proc-macro/src/runtime_metadata.rs b/primitives/api/proc-macro/src/runtime_metadata.rs index ae78fb52d..458248cbd 100644 --- a/primitives/api/proc-macro/src/runtime_metadata.rs +++ b/primitives/api/proc-macro/src/runtime_metadata.rs @@ -162,15 +162,18 @@ pub fn generate_decl_runtime_metadata(decl: &ItemTrait) -> TokenStream2 { ty.default = None; } - let where_clause = where_clause - .iter() - .map(|ty| quote!(#ty: #crate_::scale_info::TypeInfo + 'static)); + where_clause + .into_iter() + .map(|ty| parse_quote!(#ty: #crate_::scale_info::TypeInfo + 'static)) + .for_each(|w| generics.make_where_clause().predicates.push(w)); + + let (impl_generics, _, where_clause) = generics.split_for_impl(); quote!( #( #attrs )* #[inline(always)] - pub fn runtime_metadata #generics () -> #crate_::metadata_ir::RuntimeApiMetadataIR - where #( #where_clause, )* + pub fn runtime_metadata #impl_generics () -> #crate_::metadata_ir::RuntimeApiMetadataIR + #where_clause { #crate_::metadata_ir::RuntimeApiMetadataIR { name: #trait_name, diff --git a/primitives/api/test/Cargo.toml b/primitives/api/test/Cargo.toml index 5b6c144ef..3ac4ad094 100644 --- a/primitives/api/test/Cargo.toml +++ b/primitives/api/test/Cargo.toml @@ -23,6 +23,7 @@ codec = { package = "parity-scale-codec", version = "3.2.2" } sp-state-machine = { version = "0.13.0", path = "../../state-machine" } trybuild = "1.0.74" rustversion = "1.0.6" +scale-info = { version = "2.5.0", default-features = false, features = ["derive"] } [dev-dependencies] criterion = "0.4.0" diff --git a/primitives/api/test/tests/ui/positive_cases/custom_where_bound.rs b/primitives/api/test/tests/ui/positive_cases/custom_where_bound.rs new file mode 100644 index 000000000..22a176256 --- /dev/null +++ b/primitives/api/test/tests/ui/positive_cases/custom_where_bound.rs @@ -0,0 +1,46 @@ +use codec::{Decode, Encode}; +use scale_info::TypeInfo; +use sp_runtime::traits::{Block as BlockT, GetNodeBlockType}; +use substrate_test_runtime_client::runtime::Block; + +struct Runtime {} +impl GetNodeBlockType for Runtime { + type NodeBlock = Block; +} + +pub trait CustomTrait: Encode + Decode + TypeInfo {} + +#[derive(Encode, Decode, TypeInfo)] +pub struct SomeImpl; +impl CustomTrait for SomeImpl {} + +#[derive(Encode, Decode, TypeInfo)] +pub struct SomeOtherType(C); + +sp_api::decl_runtime_apis! { + pub trait Api where A: CustomTrait { + fn test() -> A; + fn test2() -> SomeOtherType; + } +} + +sp_api::impl_runtime_apis! { + impl self::Api for Runtime { + fn test() -> SomeImpl { SomeImpl } + fn test2() -> SomeOtherType { SomeOtherType(SomeImpl) } + } + + impl sp_api::Core for Runtime { + fn version() -> sp_version::RuntimeVersion { + unimplemented!() + } + fn execute_block(_: Block) { + unimplemented!() + } + fn initialize_block(_: &::Header) { + unimplemented!() + } + } +} + +fn main() {} From 5e49f6e44820affccaf517fd22af564f4b495d40 Mon Sep 17 00:00:00 2001 From: wangjj9219 <183318287@qq.com> Date: Sun, 18 Jun 2023 17:14:08 +0800 Subject: [PATCH 545/558] expose the fields of Dust type to public (#14388) --- frame/support/src/traits/tokens/fungible/regular.rs | 2 +- frame/support/src/traits/tokens/fungibles/regular.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/frame/support/src/traits/tokens/fungible/regular.rs b/frame/support/src/traits/tokens/fungible/regular.rs index c0658ad71..2838bed54 100644 --- a/frame/support/src/traits/tokens/fungible/regular.rs +++ b/frame/support/src/traits/tokens/fungible/regular.rs @@ -109,7 +109,7 @@ pub trait Inspect: Sized { /// Special dust type which can be type-safely converted into a `Credit`. #[must_use] -pub struct Dust>(pub(crate) T::Balance); +pub struct Dust>(pub T::Balance); impl> Dust { /// Convert `Dust` into an instance of `Credit`. diff --git a/frame/support/src/traits/tokens/fungibles/regular.rs b/frame/support/src/traits/tokens/fungibles/regular.rs index 5a9d3e6e4..5570659e8 100644 --- a/frame/support/src/traits/tokens/fungibles/regular.rs +++ b/frame/support/src/traits/tokens/fungibles/regular.rs @@ -121,7 +121,7 @@ pub trait Inspect: Sized { /// Special dust type which can be type-safely converted into a `Credit`. #[must_use] -pub struct Dust>(pub(crate) T::AssetId, pub(crate) T::Balance); +pub struct Dust>(pub T::AssetId, pub T::Balance); impl> Dust { /// Convert `Dust` into an instance of `Credit`. From 3b58388f5458bf529ddc29b1b2a5bd4fdc90a463 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kacper=20=C5=BBuk?= Date: Fri, 23 Jun 2023 15:03:00 +0200 Subject: [PATCH 546/558] Fix runtime-benchmarks feature --- bin/node/runtime/src/lib.rs | 2 -- frame/democracy/src/lib.rs | 13 +++++++++++++ frame/federated-bridge/src/lib.rs | 1 - frame/liberland-legislation/src/lib.rs | 1 - frame/llm/src/lib.rs | 1 - frame/office/src/lib.rs | 1 - frame/registry/src/lib.rs | 1 - frame/staking/src/benchmarking.rs | 2 +- 8 files changed, 14 insertions(+), 8 deletions(-) diff --git a/bin/node/runtime/src/lib.rs b/bin/node/runtime/src/lib.rs index 78156d449..be92e2d72 100644 --- a/bin/node/runtime/src/lib.rs +++ b/bin/node/runtime/src/lib.rs @@ -1830,7 +1830,6 @@ mod benches { [pallet_child_bounties, ChildBounties] [pallet_collective, Council] [pallet_contracts, Contracts] - [pallet_core_fellowship, CoreFellowship] [pallet_democracy, Democracy] [pallet_election_provider_multi_phase, ElectionProviderMultiPhase] [pallet_election_provider_support_benchmarking, EPSBench::] @@ -1855,7 +1854,6 @@ mod benches { [pallet_transaction_storage, TransactionStorage] [pallet_treasury, Treasury] [pallet_nfts, Nfts] - [pallet_nft_fractionalization, NftFractionalization] [pallet_utility, Utility] [pallet_whitelist, Whitelist] [pallet_registry, CompanyRegistry] diff --git a/frame/democracy/src/lib.rs b/frame/democracy/src/lib.rs index d674fe35c..ff262f561 100644 --- a/frame/democracy/src/lib.rs +++ b/frame/democracy/src/lib.rs @@ -1807,6 +1807,19 @@ impl< > EnsureOrigin for EnsureReferendumProportionAtLeast { type Success = (); + + #[cfg(feature = "runtime-benchmarks")] + fn try_successful_origin() -> Result { + let tally = Tally::> { + ayes: 1u8.into(), + nays: 0u8.into(), + aye_voters: 1, + nay_voters: 0, + turnout: 1u8.into(), + }; + Ok(O::from(RawOrigin::Referendum(tally, 1u8.into()))) + } + fn try_origin(o: O) -> Result { let n: BalanceOf = N.into(); let d: BalanceOf = D.into(); diff --git a/frame/federated-bridge/src/lib.rs b/frame/federated-bridge/src/lib.rs index 97ce190c7..1ddf29d08 100644 --- a/frame/federated-bridge/src/lib.rs +++ b/frame/federated-bridge/src/lib.rs @@ -204,7 +204,6 @@ pub mod pallet { const STORAGE_VERSION: StorageVersion = StorageVersion::new(1); #[pallet::pallet] - #[pallet::generate_store(pub(super) trait Store)] #[pallet::storage_version(STORAGE_VERSION)] pub struct Pallet(_); diff --git a/frame/liberland-legislation/src/lib.rs b/frame/liberland-legislation/src/lib.rs index 9cc6b1344..754c50b4c 100644 --- a/frame/liberland-legislation/src/lib.rs +++ b/frame/liberland-legislation/src/lib.rs @@ -55,7 +55,6 @@ pub mod pallet { type Citizenship = ::Citizenship; #[pallet::pallet] - #[pallet::generate_store(pub(super) trait Store)] #[pallet::without_storage_info] pub struct Pallet(_); diff --git a/frame/llm/src/lib.rs b/frame/llm/src/lib.rs index 323809dce..f56e4a4f3 100644 --- a/frame/llm/src/lib.rs +++ b/frame/llm/src/lib.rs @@ -323,7 +323,6 @@ pub mod pallet { const STORAGE_VERSION: StorageVersion = StorageVersion::new(1); #[pallet::pallet] - #[pallet::generate_store(pub(super) trait Store)] #[pallet::storage_version(STORAGE_VERSION)] pub struct Pallet(_); diff --git a/frame/office/src/lib.rs b/frame/office/src/lib.rs index 97eaa69d7..62764edf2 100644 --- a/frame/office/src/lib.rs +++ b/frame/office/src/lib.rs @@ -73,7 +73,6 @@ pub mod pallet { const STORAGE_VERSION: StorageVersion = StorageVersion::new(1); #[pallet::pallet] - #[pallet::generate_store(pub(super) trait Store)] #[pallet::storage_version(STORAGE_VERSION)] pub struct Pallet(_); diff --git a/frame/registry/src/lib.rs b/frame/registry/src/lib.rs index a12584649..d5a3f951e 100644 --- a/frame/registry/src/lib.rs +++ b/frame/registry/src/lib.rs @@ -165,7 +165,6 @@ pub mod pallet { const STORAGE_VERSION: StorageVersion = StorageVersion::new(1); #[pallet::pallet] - #[pallet::generate_store(pub(super) trait Store)] #[pallet::storage_version(STORAGE_VERSION)] pub struct Pallet(_); diff --git a/frame/staking/src/benchmarking.rs b/frame/staking/src/benchmarking.rs index 70cd36b6f..f05d98edf 100644 --- a/frame/staking/src/benchmarking.rs +++ b/frame/staking/src/benchmarking.rs @@ -230,7 +230,7 @@ benchmarks! { let stash = create_funded_user::("stash", USER_SEED, 100); let reward_destination = RewardDestination::Staked; let amount = T::Currency::minimum_balance() * 10u32.into(); - T::LLInitializer::make_test_citizen(&controller); + T::LLInitializer::make_test_citizen(&stash); whitelist_account!(stash); }: _(RawOrigin::Signed(stash.clone()), amount, reward_destination) verify { From f4e1f9bbc7ce08585e40116931e863db616c43f4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kacper=20=C5=BBuk?= Date: Fri, 23 Jun 2023 15:44:09 +0200 Subject: [PATCH 547/558] Make tests compile --- Cargo.lock | 1 + bin/node/runtime/src/lib.rs | 30 ++-------------- frame/democracy/src/tests.rs | 6 +++- frame/democracy/src/tests/metadata.rs | 4 +++ frame/elections-phragmen/src/lib.rs | 6 +++- frame/federated-bridge/src/mock.rs | 4 +++ frame/liberland-legislation/src/mock.rs | 12 ++++++- frame/llm/src/mock.rs | 4 +++ frame/nfts/Cargo.toml | 1 + frame/nfts/src/mock.rs | 7 +++- frame/nfts/src/tests.rs | 48 ++++++++++++------------- frame/registry/src/mock.rs | 12 +++++++ frame/staking/src/mock.rs | 8 +++-- frame/staking/src/tests.rs | 3 +- 14 files changed, 87 insertions(+), 59 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 98509ee36..feffcc4f6 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -5643,6 +5643,7 @@ dependencies = [ "scale-info", "sp-core", "sp-io", + "sp-keystore", "sp-runtime", "sp-std", ] diff --git a/bin/node/runtime/src/lib.rs b/bin/node/runtime/src/lib.rs index be92e2d72..df11ce8ae 100644 --- a/bin/node/runtime/src/lib.rs +++ b/bin/node/runtime/src/lib.rs @@ -457,17 +457,6 @@ parameter_types! { pub const MaxReserves: u32 = 50; } -/// A reason for placing a hold on funds. -#[derive( - Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Encode, Decode, MaxEncodedLen, Debug, TypeInfo, -)] -pub enum HoldReason { - /// The NIS Pallet has reserved it for a non-fungible receipt. - Nis, - /// Used by the NFT Fractionalization Pallet. - NftFractionalization, -} - impl pallet_balances::Config for Runtime { type MaxLocks = MaxLocks; type MaxReserves = MaxReserves; @@ -480,8 +469,8 @@ impl pallet_balances::Config for Runtime { type WeightInfo = pallet_balances::weights::SubstrateWeight; type FreezeIdentifier = (); type MaxFreezes = (); - type HoldIdentifier = HoldReason; - type MaxHolds = ConstU32<2>; + type HoldIdentifier = (); + type MaxHolds = (); } parameter_types! { @@ -1340,21 +1329,6 @@ impl pallet_assets::Config for Runtime { type BenchmarkHelper = (); } -parameter_types! { - pub const QueueCount: u32 = 300; - pub const MaxQueueLen: u32 = 1000; - pub const FifoQueueLen: u32 = 500; - pub const NisBasePeriod: BlockNumber = 3 * DAYS; - pub const MinBid: Balance = 100 * DOLLARS; - pub const MinReceipt: Perquintill = Perquintill::from_percent(1); - pub const IntakePeriod: BlockNumber = 10; - pub MaxIntakeWeight: Weight = MAXIMUM_BLOCK_WEIGHT / 10; - pub const ThawThrottle: (Perquintill, BlockNumber) = (Perquintill::from_percent(25), 5); - pub Target: Perquintill = Perquintill::zero(); - pub const NisPalletId: PalletId = PalletId(*b"py/nis "); - pub const NisHoldReason: HoldReason = HoldReason::Nis; -} - parameter_types! { pub const TOTALLLM: Balance = 70_000_000u128 * GRAINS_IN_LLM; pub const PRERELEASELLM: Balance = 13_300_000u128 * GRAINS_IN_LLM; diff --git a/frame/democracy/src/tests.rs b/frame/democracy/src/tests.rs index 19b265f17..1cc9e7519 100644 --- a/frame/democracy/src/tests.rs +++ b/frame/democracy/src/tests.rs @@ -35,7 +35,7 @@ use frame_support::{ use frame_system::{EnsureRoot, EnsureSigned, EnsureSignedBy}; use sp_core::H256; use sp_runtime::{ - testing::Header, + testing::{TestSignature, Header}, traits::{BadOrigin, BlakeTwo256, Hash, IdentityLookup}, Perbill, Permill, @@ -186,6 +186,7 @@ impl pallet_balances::Config for Test { use pallet_nfts::PalletFeatures; parameter_types! { pub storage Features: PalletFeatures = PalletFeatures::all_enabled(); + pub const MaxAttributesPerCall: u32 = 10; } impl pallet_nfts::Config for Test { type RuntimeEvent = RuntimeEvent; @@ -213,6 +214,9 @@ impl pallet_nfts::Config for Test { type Helper = (); type Citizenship = (); type MetadataValidator = (); + type MaxAttributesPerCall = MaxAttributesPerCall; + type OffchainSignature = TestSignature; + type OffchainPublic = ::Signer; } parameter_types! { pub static PreimageByteDeposit: u64 = 0; diff --git a/frame/democracy/src/tests/metadata.rs b/frame/democracy/src/tests/metadata.rs index 5a36d80b7..f617e0a2f 100644 --- a/frame/democracy/src/tests/metadata.rs +++ b/frame/democracy/src/tests/metadata.rs @@ -19,6 +19,8 @@ use super::*; +use super::types::DispatchOrigin; + #[test] fn set_external_metadata_works() { new_test_ext().execute_with(|| { @@ -141,6 +143,7 @@ fn set_referendum_metadata_by_root() { let index = Democracy::inject_referendum( 2, set_balance_proposal(2), + DispatchOrigin::Root, VoteThreshold::SuperMajorityApprove, 0, ); @@ -180,6 +183,7 @@ fn clear_referendum_metadata_works() { let index = Democracy::inject_referendum( 2, set_balance_proposal(2), + DispatchOrigin::Root, VoteThreshold::SuperMajorityApprove, 0, ); diff --git a/frame/elections-phragmen/src/lib.rs b/frame/elections-phragmen/src/lib.rs index b590f7508..009de4388 100644 --- a/frame/elections-phragmen/src/lib.rs +++ b/frame/elections-phragmen/src/lib.rs @@ -1266,7 +1266,7 @@ mod tests { use frame_system::ensure_signed; use sp_runtime::{ Permill, - testing::{Header, H256}, + testing::{TestSignature, Header, H256}, traits::{BlakeTwo256, IdentityLookup}, BuildStorage, }; @@ -1346,6 +1346,7 @@ mod tests { use pallet_nfts::PalletFeatures; parameter_types! { pub storage Features: PalletFeatures = PalletFeatures::all_enabled(); + pub const MaxAttributesPerCall: u32 = 10; } impl pallet_nfts::Config for Test { type RuntimeEvent = RuntimeEvent; @@ -1373,6 +1374,9 @@ mod tests { type Helper = (); type Citizenship = (); type MetadataValidator = (); + type MaxAttributesPerCall = MaxAttributesPerCall; + type OffchainSignature = TestSignature; + type OffchainPublic = ::Signer; } impl pallet_assets::Config for Test { diff --git a/frame/federated-bridge/src/mock.rs b/frame/federated-bridge/src/mock.rs index 69eabba7c..ba2911c81 100644 --- a/frame/federated-bridge/src/mock.rs +++ b/frame/federated-bridge/src/mock.rs @@ -72,6 +72,10 @@ impl pallet_balances::Config for Test { type ExistentialDeposit = ConstU64<1>; type AccountStore = System; type WeightInfo = (); + type FreezeIdentifier = (); + type MaxFreezes = (); + type HoldIdentifier = (); + type MaxHolds = (); } parameter_types! { diff --git a/frame/liberland-legislation/src/mock.rs b/frame/liberland-legislation/src/mock.rs index d44149245..1c32913ae 100644 --- a/frame/liberland-legislation/src/mock.rs +++ b/frame/liberland-legislation/src/mock.rs @@ -14,7 +14,7 @@ use frame_system::{EnsureRoot, EnsureSigned, EnsureSignedBy}; use pallet_balances::AccountData; use sp_core::H256; use sp_runtime::{ - testing::Header, + testing::{TestSignature, Header}, traits::{BlakeTwo256, IdentityLookup}, Perbill, Permill, }; @@ -125,6 +125,7 @@ impl pallet_democracy::Config for Test { type LLM = LLM; type LLInitializer = LiberlandInitializer; type DelegateeFilter = Everything; + type SubmitOrigin = EnsureSigned; } impl pallet_balances::Config for Test { @@ -137,11 +138,16 @@ impl pallet_balances::Config for Test { type ExistentialDeposit = ConstU64<1>; type AccountStore = System; type WeightInfo = (); + type FreezeIdentifier = (); + type MaxFreezes = (); + type HoldIdentifier = (); + type MaxHolds = (); } use pallet_nfts::PalletFeatures; parameter_types! { pub storage Features: PalletFeatures = PalletFeatures::all_enabled(); + pub const MaxAttributesPerCall: u32 = 10; } impl pallet_nfts::Config for Test { type RuntimeEvent = RuntimeEvent; @@ -169,6 +175,10 @@ impl pallet_nfts::Config for Test { type Helper = (); type Citizenship = (); type MetadataValidator = (); + + type MaxAttributesPerCall = MaxAttributesPerCall; + type OffchainSignature = TestSignature; + type OffchainPublic = ::Signer; } impl pallet_assets::Config for Test { diff --git a/frame/llm/src/mock.rs b/frame/llm/src/mock.rs index 25be1cbea..7ffb88cbe 100644 --- a/frame/llm/src/mock.rs +++ b/frame/llm/src/mock.rs @@ -95,6 +95,10 @@ impl pallet_balances::Config for Test { type ExistentialDeposit = ConstU64<1>; type AccountStore = System; type WeightInfo = (); + type FreezeIdentifier = (); + type MaxFreezes = (); + type HoldIdentifier = (); + type MaxHolds = (); } parameter_types! { diff --git a/frame/nfts/Cargo.toml b/frame/nfts/Cargo.toml index 706d2fa6a..cee0c6f35 100644 --- a/frame/nfts/Cargo.toml +++ b/frame/nfts/Cargo.toml @@ -31,6 +31,7 @@ pallet-balances = { version = "4.0.0-dev", branch = "polkadot-v0.9.43", git = "h sp-core = { version = "7.0.0", branch = "polkadot-v0.9.43", git = "https://github.com/paritytech/substrate" } sp-io = { version = "7.0.0", branch = "polkadot-v0.9.43", git = "https://github.com/paritytech/substrate" } sp-std = { version = "5.0.0", branch = "polkadot-v0.9.43", git = "https://github.com/paritytech/substrate" } +sp-keystore = { version = "0.13.0", branch = "polkadot-v0.9.43", git = "https://github.com/paritytech/substrate" } [features] default = ["std"] diff --git a/frame/nfts/src/mock.rs b/frame/nfts/src/mock.rs index cd981e472..911ea0cd9 100644 --- a/frame/nfts/src/mock.rs +++ b/frame/nfts/src/mock.rs @@ -112,6 +112,11 @@ parameter_types! { pub storage Features: PalletFeatures = PalletFeatures::all_enabled(); } +parameter_types! { + pub MockCitizenOne: AccountId = [1u8; 32].into(); + pub MockCitizenTwo: AccountId = [2u8; 32].into(); +} + impl Config for Test { type RuntimeEvent = RuntimeEvent; type CollectionId = u32; @@ -142,7 +147,7 @@ impl Config for Test { type WeightInfo = (); #[cfg(feature = "runtime-benchmarks")] type Helper = (); - type Citizenship = MockCitizenshipChecker, ConstU64<101>>; + type Citizenship = MockCitizenshipChecker; type MetadataValidator = DummyMetadataValidator; } diff --git a/frame/nfts/src/tests.rs b/frame/nfts/src/tests.rs index b3ca4a85e..549479699 100644 --- a/frame/nfts/src/tests.rs +++ b/frame/nfts/src/tests.rs @@ -3652,15 +3652,15 @@ fn pre_signed_attributes_should_work() { #[test] fn set_citizenship_required_should_work() { new_test_ext().execute_with(|| { - assert_ok!(Nfts::force_create(RuntimeOrigin::root(), 1, default_collection_config())); + assert_ok!(Nfts::force_create(RuntimeOrigin::root(), account(1), default_collection_config())); assert_eq!(CitizenshipRequired::::get(0), false); - assert_ok!(Nfts::set_citizenship_required(RuntimeOrigin::signed(1), 0, false)); + assert_ok!(Nfts::set_citizenship_required(RuntimeOrigin::signed(account(1)), 0, false)); assert_eq!(CitizenshipRequired::::get(0), false); - assert_ok!(Nfts::set_citizenship_required(RuntimeOrigin::signed(1), 0, true)); + assert_ok!(Nfts::set_citizenship_required(RuntimeOrigin::signed(account(1)), 0, true)); assert_eq!(CitizenshipRequired::::get(0), true); - assert_ok!(Nfts::set_citizenship_required(RuntimeOrigin::signed(1), 0, true)); + assert_ok!(Nfts::set_citizenship_required(RuntimeOrigin::signed(account(1)), 0, true)); assert_eq!(CitizenshipRequired::::get(0), true); - assert_ok!(Nfts::set_citizenship_required(RuntimeOrigin::signed(1), 0, false)); + assert_ok!(Nfts::set_citizenship_required(RuntimeOrigin::signed(account(1)), 0, false)); assert_eq!(CitizenshipRequired::::get(0), false); }) } @@ -3668,10 +3668,10 @@ fn set_citizenship_required_should_work() { #[test] fn set_citizenship_required_verifies_origin() { new_test_ext().execute_with(|| { - assert_ok!(Nfts::force_create(RuntimeOrigin::root(), 1, default_collection_config())); - assert_ok!(Nfts::set_citizenship_required(RuntimeOrigin::signed(1), 0, false)); + assert_ok!(Nfts::force_create(RuntimeOrigin::root(), account(1), default_collection_config())); + assert_ok!(Nfts::set_citizenship_required(RuntimeOrigin::signed(account(1)), 0, false)); assert_ok!(Nfts::set_citizenship_required(RuntimeOrigin::root(), 0, false)); - assert_noop!(Nfts::set_citizenship_required(RuntimeOrigin::signed(2), 0, false), Error::::NoPermission); + assert_noop!(Nfts::set_citizenship_required(RuntimeOrigin::signed(account(2)), 0, false), Error::::NoPermission); }) } @@ -3679,35 +3679,35 @@ fn set_citizenship_required_verifies_origin() { fn citizenship_is_checked_when_set() { new_test_ext().execute_with(|| { // Only 100 and 101 are considered citizens - assert_ok!(Nfts::force_create(RuntimeOrigin::root(), 1, default_collection_config())); - assert_ok!(Nfts::set_citizenship_required(RuntimeOrigin::signed(1), 0, true)); - assert_noop!(Nfts::mint(RuntimeOrigin::signed(1), 0, 42, 1, None), DispatchError::Other("NotCitizen")); - assert_ok!(Nfts::mint(RuntimeOrigin::signed(1), 0, 42, 100, None)); - assert_noop!(Nfts::transfer(RuntimeOrigin::signed(100), 0, 42, 1), DispatchError::Other("NotCitizen")); - assert_ok!(Nfts::transfer(RuntimeOrigin::signed(100), 0, 42, 101)); - - assert_ok!(Nfts::set_citizenship_required(RuntimeOrigin::signed(1), 0, false)); - assert_ok!(Nfts::mint(RuntimeOrigin::signed(1), 0, 43, 2, None)); - assert_ok!(Nfts::transfer(RuntimeOrigin::signed(2), 0, 43, 3)); + assert_ok!(Nfts::force_create(RuntimeOrigin::root(), account(1), default_collection_config())); + assert_ok!(Nfts::set_citizenship_required(RuntimeOrigin::signed(account(1)), 0, true)); + assert_noop!(Nfts::mint(RuntimeOrigin::signed(account(1)), 0, 42, account(1), None), DispatchError::Other("NotCitizen")); + assert_ok!(Nfts::mint(RuntimeOrigin::signed(account(1)), 0, 42, account(100), None)); + assert_noop!(Nfts::transfer(RuntimeOrigin::signed(account(100)), 0, 42, account(1)), DispatchError::Other("NotCitizen")); + assert_ok!(Nfts::transfer(RuntimeOrigin::signed(account(100)), 0, 42, account(101))); + + assert_ok!(Nfts::set_citizenship_required(RuntimeOrigin::signed(account(1)), 0, false)); + assert_ok!(Nfts::mint(RuntimeOrigin::signed(account(1)), 0, 43, account(2), None)); + assert_ok!(Nfts::transfer(RuntimeOrigin::signed(account(2)), 0, 43, account(3))); }) } #[test] fn metadata_validator_works() { new_test_ext().execute_with(|| { - Balances::make_free_balance_be(&1, 30); + Balances::make_free_balance_be(&account(1), 30); assert_ok!(Nfts::force_create( RuntimeOrigin::root(), - 1, + account(1), collection_config_with_all_settings_enabled() )); - assert_ok!(Nfts::mint(RuntimeOrigin::signed(1), 0, 42, 1, None)); - assert_ok!(Nfts::mint(RuntimeOrigin::signed(1), 0, 9991999, 1, None)); + assert_ok!(Nfts::mint(RuntimeOrigin::signed(account(1)), 0, 42, account(1), None)); + assert_ok!(Nfts::mint(RuntimeOrigin::signed(account(1)), 0, 9991999, account(1), None)); assert_noop!( - Nfts::set_metadata(RuntimeOrigin::signed(1), 0, 9991999, bvec![0u8; 20]), + Nfts::set_metadata(RuntimeOrigin::signed(account(1)), 0, 9991999, bvec![0u8; 20]), Error::::IncorrectData, ); - assert_ok!(Nfts::set_metadata(RuntimeOrigin::signed(1), 0, 42, bvec![0u8; 20])); + assert_ok!(Nfts::set_metadata(RuntimeOrigin::signed(account(1)), 0, 42, bvec![0u8; 20])); }) } \ No newline at end of file diff --git a/frame/registry/src/mock.rs b/frame/registry/src/mock.rs index c6efb4280..ab53ee201 100644 --- a/frame/registry/src/mock.rs +++ b/frame/registry/src/mock.rs @@ -15,6 +15,7 @@ use sp_runtime::{ testing::Header, traits::{AccountIdConversion, BlakeTwo256, IdentityLookup, Morph}, BoundedVec, + Perbill, }; type UncheckedExtrinsic = frame_system::mocking::MockUncheckedExtrinsic; @@ -79,6 +80,10 @@ impl pallet_balances::Config for Test { type ExistentialDeposit = ConstU64<1>; type AccountStore = System; type WeightInfo = (); + type FreezeIdentifier = (); + type MaxFreezes = (); + type HoldIdentifier = (); + type MaxHolds = (); } parameter_types! { @@ -115,6 +120,10 @@ impl pallet_registry::Config for Test { type WeightInfo = (); } +parameter_types! { + pub MaxCollectivesProposalWeight: Weight = Perbill::from_percent(80) * BlockWeights::get().max_block; +} + impl pallet_collective::Config for Test { type RuntimeOrigin = RuntimeOrigin; type Proposal = RuntimeCall; @@ -124,7 +133,10 @@ impl pallet_collective::Config for Test { type MaxMembers = ConstU32<1>; type DefaultVote = pallet_collective::PrimeDefaultVote; type WeightInfo = pallet_collective::weights::SubstrateWeight; + type SetMembersOrigin = EnsureRoot; + type MaxProposalWeight = MaxCollectivesProposalWeight; } + pub struct MapCollective { _phantom: PhantomData, _phantom2: PhantomData, diff --git a/frame/staking/src/mock.rs b/frame/staking/src/mock.rs index 0655d54e4..c858e5036 100644 --- a/frame/staking/src/mock.rs +++ b/frame/staking/src/mock.rs @@ -36,7 +36,7 @@ use sp_core::H256; use sp_io; use sp_runtime::{ curve::PiecewiseLinear, - testing::{Header, UintAuthorityId}, + testing::{TestSignature, Header, UintAuthorityId}, traits::{IdentityLookup, Zero}, Permill, }; @@ -238,6 +238,7 @@ impl pallet_llm::Config for Test { use pallet_nfts::PalletFeatures; parameter_types! { pub storage Features: PalletFeatures = PalletFeatures::all_enabled(); + pub const MaxAttributesPerCall: u32 = 10; } impl pallet_nfts::Config for Test { type RuntimeEvent = RuntimeEvent; @@ -265,6 +266,9 @@ impl pallet_nfts::Config for Test { type Helper = (); type Citizenship = (); type MetadataValidator = (); + type MaxAttributesPerCall = MaxAttributesPerCall; + type OffchainSignature = TestSignature; + type OffchainPublic = ::Signer; } parameter_types! { @@ -715,7 +719,7 @@ pub(crate) fn bond(who: AccountId, val: Balance) { } pub(crate) fn bond_validator(who: AccountId, val: Balance) { - LiberlandInitializer::make_test_citizen(&ctrl); + LiberlandInitializer::make_test_citizen(&who); bond(who, val); assert_ok!(Staking::validate(RuntimeOrigin::signed(who), ValidatorPrefs::default())); assert_ok!(Session::set_keys( diff --git a/frame/staking/src/tests.rs b/frame/staking/src/tests.rs index 90db896e9..c602adb99 100644 --- a/frame/staking/src/tests.rs +++ b/frame/staking/src/tests.rs @@ -4775,7 +4775,8 @@ fn chill_other_works() { Balances::make_free_balance_be(&a, 100_000); Balances::make_free_balance_be(&b, 100_000); Balances::make_free_balance_be(&c, 100_000); - LiberlandInitializer::make_test_citizen(&d); + LiberlandInitializer::make_test_citizen(&a); + LiberlandInitializer::make_test_citizen(&b); // Nominator assert_ok!(Staking::bond( From 266c059122f808e5a1e61d22bef62cc6360788ba Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kacper=20=C5=BBuk?= Date: Fri, 23 Jun 2023 15:52:43 +0200 Subject: [PATCH 548/558] Fix try-runtime feature --- bin/node/runtime/src/lib.rs | 4 ++-- frame/democracy/src/migrations.rs | 8 ++++---- frame/llm/src/migrations.rs | 15 +++++++++------ 3 files changed, 15 insertions(+), 12 deletions(-) diff --git a/bin/node/runtime/src/lib.rs b/bin/node/runtime/src/lib.rs index df11ce8ae..a7d67edd2 100644 --- a/bin/node/runtime/src/lib.rs +++ b/bin/node/runtime/src/lib.rs @@ -1745,7 +1745,7 @@ mod staking_v12 { pub struct Migration(sp_std::marker::PhantomData); impl OnRuntimeUpgrade for Migration { #[cfg(feature = "try-runtime")] - fn pre_upgrade() -> Result, &'static str> { + fn pre_upgrade() -> Result, sp_runtime::TryRuntimeError> { frame_support::ensure!( StorageVersion::::get() == ObsoleteReleases::V7_0_0, "Expected v7 before upgrading to v12" @@ -1761,7 +1761,7 @@ mod staking_v12 { } #[cfg(feature = "try-runtime")] - fn post_upgrade(_: Vec) -> Result<(), &'static str> { + fn post_upgrade(_: Vec) -> Result<(), sp_runtime::TryRuntimeError> { frame_support::ensure!( StorageVersion::::get() == ObsoleteReleases::V12_0_0, "Failed to upgrade to v12" diff --git a/frame/democracy/src/migrations.rs b/frame/democracy/src/migrations.rs index be08c7803..7085ce87a 100644 --- a/frame/democracy/src/migrations.rs +++ b/frame/democracy/src/migrations.rs @@ -240,7 +240,7 @@ pub mod v2 { impl OnRuntimeUpgrade for Migration { #[cfg(feature = "try-runtime")] - fn pre_upgrade() -> Result, &'static str> { + fn pre_upgrade() -> Result, sp_runtime::TryRuntimeError> { assert_eq!(StorageVersion::get::>(), 1, "can only upgrade from version 1"); let props_count = v0::PublicProps::::get().len(); @@ -312,7 +312,7 @@ pub mod v2 { } #[cfg(feature = "try-runtime")] - fn post_upgrade(state: Vec) -> Result<(), &'static str> { + fn post_upgrade(state: Vec) -> Result<(), sp_runtime::TryRuntimeError> { assert_eq!(StorageVersion::get::>(), 2, "must upgrade"); let (old_props_count, old_ref_count): (u32, u32) = @@ -339,7 +339,7 @@ pub mod v3 { impl OnRuntimeUpgrade for Migration { #[cfg(feature = "try-runtime")] - fn pre_upgrade() -> Result, &'static str> { + fn pre_upgrade() -> Result, sp_runtime::TryRuntimeError> { assert_eq!(StorageVersion::get::>(), 2, "can only upgrade from version 2"); let voting_of_count = v2::VotingOf::::iter().count(); @@ -397,7 +397,7 @@ pub mod v3 { } #[cfg(feature = "try-runtime")] - fn post_upgrade(state: Vec) -> Result<(), &'static str> { + fn post_upgrade(state: Vec) -> Result<(), sp_runtime::TryRuntimeError> { assert_eq!(StorageVersion::get::>(), 3, "must upgrade"); let old_votings: u32 = diff --git a/frame/llm/src/migrations.rs b/frame/llm/src/migrations.rs index c6f2c2800..206f80296 100644 --- a/frame/llm/src/migrations.rs +++ b/frame/llm/src/migrations.rs @@ -3,6 +3,9 @@ use frame_support::{pallet_prelude::*, storage_alias, traits::OnRuntimeUpgrade}; use liberland_traits::CitizenshipChecker; use pallet_identity::Registration; +#[cfg(feature = "try-runtime")] +use sp_runtime::TryRuntimeError; + /// The log target. const TARGET: &'static str = "runtime::llm::migration::v1"; @@ -26,7 +29,7 @@ pub mod v1 { impl OnRuntimeUpgrade for Migration { #[cfg(feature = "try-runtime")] - fn pre_upgrade() -> Result, &'static str> { + fn pre_upgrade() -> Result, TryRuntimeError> { assert_eq!(StorageVersion::get::>(), 0, "can only upgrade from version 0"); Ok(().encode()) @@ -78,7 +81,7 @@ pub mod v1 { } #[cfg(feature = "try-runtime")] - fn post_upgrade(_state: Vec) -> Result<(), &'static str> { + fn post_upgrade(_state: Vec) -> Result<(), TryRuntimeError> { assert_eq!(StorageVersion::get::>(), 1, "must upgrade"); log::info!(target: TARGET, "Counted {} citizens", Citizens::::get(),); Ok(()) @@ -95,7 +98,7 @@ pub mod ltm_to_lkn { impl OnRuntimeUpgrade for Migration { #[cfg(feature = "try-runtime")] - fn pre_upgrade() -> Result, &'static str> { + fn pre_upgrade() -> Result, TryRuntimeError> { Ok(().encode()) } @@ -123,7 +126,7 @@ pub mod ltm_to_lkn { } #[cfg(feature = "try-runtime")] - fn post_upgrade(_state: Vec) -> Result<(), &'static str> { + fn post_upgrade(_state: Vec) -> Result<(), TryRuntimeError> { Ok(()) } } @@ -152,7 +155,7 @@ pub mod v2 { impl OnRuntimeUpgrade for Migration { #[cfg(feature = "try-runtime")] - fn pre_upgrade() -> Result, &'static str> { + fn pre_upgrade() -> Result, TryRuntimeError> { assert_eq!(StorageVersion::get::>(), 1, "can only upgrade from version 1"); Ok(().encode()) } @@ -222,7 +225,7 @@ pub mod v2 { } #[cfg(feature = "try-runtime")] - fn post_upgrade(_state: Vec) -> Result<(), &'static str> { + fn post_upgrade(_state: Vec) -> Result<(), TryRuntimeError> { assert_eq!(StorageVersion::get::>(), 2, "must upgrade"); Ok(()) } From 69cf3a5d9d7e28a28a71157ee98da32ce95f67d5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kacper=20=C5=BBuk?= Date: Fri, 23 Jun 2023 16:17:42 +0200 Subject: [PATCH 549/558] Fix tests --- frame/federated-bridge/src/tests.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/frame/federated-bridge/src/tests.rs b/frame/federated-bridge/src/tests.rs index f41487a62..4e142021c 100644 --- a/frame/federated-bridge/src/tests.rs +++ b/frame/federated-bridge/src/tests.rs @@ -6,7 +6,7 @@ use crate::{ VotesRequired, Voting, Watchers, }; use frame_support::{assert_noop, assert_ok}; -use sp_runtime::traits::{AccountIdConversion, BadOrigin}; +use sp_runtime::{TokenError, traits::{AccountIdConversion, BadOrigin}}; fn eth_recipient(n: u8) -> EthAddress { let mut addr: EthAddress = Default::default(); @@ -72,7 +72,7 @@ fn deposit_fails_on_insufficient_funds() { new_test_ext().execute_with(|| { assert_noop!( Bridge::deposit(RuntimeOrigin::signed(0), 101, eth_recipient(0)), - pallet_balances::Error::::InsufficientBalance + TokenError::FundsUnavailable, ); }); } @@ -347,7 +347,7 @@ fn withdraw_fails_on_broke_caller() { System::set_block_number(11); assert_noop!( Bridge::withdraw(RuntimeOrigin::signed(50), receipt_id), - pallet_balances::Error::::InsufficientBalance + TokenError::FundsUnavailable, ); }); } From eb50257e90224322b8d347496b1c7c90b26ebc72 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kacper=20=C5=BBuk?= Date: Fri, 23 Jun 2023 16:18:02 +0200 Subject: [PATCH 550/558] Cargo fmt --- frame/liberland-legislation/src/mock.rs | 2 +- frame/llm/src/impl_fungible.rs | 34 +++++++++++++++---------- frame/registry/src/mock.rs | 5 ++-- 3 files changed, 24 insertions(+), 17 deletions(-) diff --git a/frame/liberland-legislation/src/mock.rs b/frame/liberland-legislation/src/mock.rs index 1c32913ae..0b2473cfd 100644 --- a/frame/liberland-legislation/src/mock.rs +++ b/frame/liberland-legislation/src/mock.rs @@ -14,7 +14,7 @@ use frame_system::{EnsureRoot, EnsureSigned, EnsureSignedBy}; use pallet_balances::AccountData; use sp_core::H256; use sp_runtime::{ - testing::{TestSignature, Header}, + testing::{Header, TestSignature}, traits::{BlakeTwo256, IdentityLookup}, Perbill, Permill, }; diff --git a/frame/llm/src/impl_fungible.rs b/frame/llm/src/impl_fungible.rs index d04778c3d..122f01208 100644 --- a/frame/llm/src/impl_fungible.rs +++ b/frame/llm/src/impl_fungible.rs @@ -2,10 +2,11 @@ use super::*; use frame_support::{ pallet_prelude::*, traits::tokens::{ - Preservation, Fortitude, Provenance, - fungible::{Inspect, Mutate, Unbalanced, Dust}, - fungibles::{Inspect as FungiblesInspect, Unbalanced as FungiblesUnbalanced, Dust as FungiblesDust}, - DepositConsequence, WithdrawConsequence, + fungible::{Dust, Inspect, Mutate, Unbalanced}, + fungibles::{ + Dust as FungiblesDust, Inspect as FungiblesInspect, Unbalanced as FungiblesUnbalanced, + }, + DepositConsequence, Fortitude, Preservation, Provenance, WithdrawConsequence, }, }; @@ -32,12 +33,20 @@ impl Inspect for Pallet { Assets::::balance(id, who) } - fn reducible_balance(who: &T::AccountId, preservation: Preservation, force: Fortitude) -> Self::Balance { + fn reducible_balance( + who: &T::AccountId, + preservation: Preservation, + force: Fortitude, + ) -> Self::Balance { let id = Self::llm_id(); Assets::::reducible_balance(id, who, preservation, force) } - fn can_deposit(who: &T::AccountId, amount: Self::Balance, provenance: Provenance) -> DepositConsequence { + fn can_deposit( + who: &T::AccountId, + amount: Self::Balance, + provenance: Provenance, + ) -> DepositConsequence { let id = Self::llm_id(); Assets::::can_deposit(id, who, amount, provenance) } @@ -58,19 +67,18 @@ impl Unbalanced for Pallet { Assets::::handle_dust(dust); } - fn write_balance( - who: &T::AccountId, - amount: Self::Balance - ) -> Result, DispatchError> { + fn write_balance( + who: &T::AccountId, + amount: Self::Balance, + ) -> Result, DispatchError> { let asset_id = Self::llm_id().into(); Assets::::write_balance(asset_id, who, amount) } - fn set_total_issuance(amount: Self::Balance) { + fn set_total_issuance(amount: Self::Balance) { let asset_id = Self::llm_id().into(); Assets::::set_total_issuance(asset_id, amount) - } } -impl Mutate for Pallet {} \ No newline at end of file +impl Mutate for Pallet {} diff --git a/frame/registry/src/mock.rs b/frame/registry/src/mock.rs index ab53ee201..ea21b2c7a 100644 --- a/frame/registry/src/mock.rs +++ b/frame/registry/src/mock.rs @@ -14,8 +14,7 @@ use sp_core::{ConstU16, Get, H256}; use sp_runtime::{ testing::Header, traits::{AccountIdConversion, BlakeTwo256, IdentityLookup, Morph}, - BoundedVec, - Perbill, + BoundedVec, Perbill, }; type UncheckedExtrinsic = frame_system::mocking::MockUncheckedExtrinsic; @@ -123,7 +122,7 @@ impl pallet_registry::Config for Test { parameter_types! { pub MaxCollectivesProposalWeight: Weight = Perbill::from_percent(80) * BlockWeights::get().max_block; } - + impl pallet_collective::Config for Test { type RuntimeOrigin = RuntimeOrigin; type Proposal = RuntimeCall; From f27d53ed91f01a5426229ecf237727b4b46e2b53 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kacper=20=C5=BBuk?= Date: Fri, 23 Jun 2023 17:36:49 +0200 Subject: [PATCH 551/558] Fix benchmarks + regenerate weights.rs --- frame/liberland-legislation/Cargo.toml | 1 + .../liberland-legislation/src/benchmarking.rs | 2 +- frame/liberland-legislation/src/weights.rs | 206 ++++++---- frame/office/src/weights.rs | 128 ++++-- frame/registry/Cargo.toml | 1 + frame/registry/src/benchmarking.rs | 17 +- frame/registry/src/weights.rs | 388 +++++++++++------- 7 files changed, 469 insertions(+), 274 deletions(-) diff --git a/frame/liberland-legislation/Cargo.toml b/frame/liberland-legislation/Cargo.toml index ca7b7a7ae..4915a466e 100644 --- a/frame/liberland-legislation/Cargo.toml +++ b/frame/liberland-legislation/Cargo.toml @@ -62,6 +62,7 @@ runtime-benchmarks = [ "pallet-liberland-initializer/runtime-benchmarks", "liberland-traits/runtime-benchmarks", "pallet-nfts/runtime-benchmarks", + "pallet-democracy/runtime-benchmarks", ] try-runtime = [ "frame-support/try-runtime", diff --git a/frame/liberland-legislation/src/benchmarking.rs b/frame/liberland-legislation/src/benchmarking.rs index 75cbe1f51..5022123c3 100644 --- a/frame/liberland-legislation/src/benchmarking.rs +++ b/frame/liberland-legislation/src/benchmarking.rs @@ -68,7 +68,7 @@ benchmarks! { } trigger_headcount_veto { - let c in 16 .. 5000 => add_vetos::(c); + let c in 16 .. 1000 => add_vetos::(c); let acc: T::AccountId = account("a", 0, SEED); let origin: T::RuntimeOrigin = RawOrigin::Signed(acc.clone()).into(); }: _(origin, 2, 0) diff --git a/frame/liberland-legislation/src/weights.rs b/frame/liberland-legislation/src/weights.rs index 23cc13f51..0d86484f9 100644 --- a/frame/liberland-legislation/src/weights.rs +++ b/frame/liberland-legislation/src/weights.rs @@ -2,7 +2,8 @@ //! Autogenerated weights for pallet_liberland_legislation //! //! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev -//! DATE: 2023-03-08, STEPS: `20`, REPEAT: 10, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! DATE: 2023-06-23, STEPS: `20`, REPEAT: `10`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! WORST CASE MAP SIZE: `1000000` //! HOSTNAME: `kacper-HP-ProBook-445-G7`, CPU: `AMD Ryzen 7 4700U with Radeon Graphics` //! EXECUTION: Some(Native), WASM-EXECUTION: Compiled, CHAIN: None, DB CACHE: 1024 @@ -21,9 +22,10 @@ #![cfg_attr(rustfmt, rustfmt_skip)] #![allow(unused_parens)] #![allow(unused_imports)] +#![allow(missing_docs)] use frame_support::{traits::Get, weights::{Weight, constants::RocksDbWeight}}; -use sp_std::marker::PhantomData; +use core::marker::PhantomData; /// Weight functions needed for pallet_liberland_legislation. pub trait WeightInfo { @@ -37,106 +39,162 @@ pub trait WeightInfo { /// Weights for pallet_liberland_legislation using the Substrate node and recommended hardware. pub struct SubstrateWeight(PhantomData); impl WeightInfo for SubstrateWeight { - // Storage: LiberlandLegislation Laws (r:1 w:1) + /// Storage: LiberlandLegislation Laws (r:1 w:1) + /// Proof Skipped: LiberlandLegislation Laws (max_values: None, max_size: None, mode: Measured) /// The range of component `s` is `[1, 65536]`. fn add_law(s: u32, ) -> Weight { - // Minimum execution time: 17_403 nanoseconds. - Weight::from_ref_time(18_113_966) - // Standard Error: 4 - .saturating_add(Weight::from_ref_time(84).saturating_mul(s.into())) - .saturating_add(T::DbWeight::get().reads(1)) - .saturating_add(T::DbWeight::get().writes(1)) + // Proof Size summary in bytes: + // Measured: `76` + // Estimated: `3541` + // Minimum execution time: 7_925_000 picoseconds. + Weight::from_parts(8_439_585, 3541) + // Standard Error: 2 + .saturating_add(Weight::from_parts(75, 0).saturating_mul(s.into())) + .saturating_add(T::DbWeight::get().reads(1_u64)) + .saturating_add(T::DbWeight::get().writes(1_u64)) } - // Storage: LiberlandLegislation Laws (r:0 w:1) + /// Storage: LiberlandLegislation Laws (r:0 w:1) + /// Proof Skipped: LiberlandLegislation Laws (max_values: None, max_size: None, mode: Measured) fn repeal_law() -> Weight { - // Minimum execution time: 14_637 nanoseconds. - Weight::from_ref_time(14_988_000) - .saturating_add(T::DbWeight::get().writes(1)) + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 4_920_000 picoseconds. + Weight::from_parts(4_969_000, 0) + .saturating_add(T::DbWeight::get().writes(1_u64)) } - // Storage: Identity IdentityOf (r:1 w:0) - // Storage: LiberlandLegislation Vetos (r:1 w:1) - // Storage: LiberlandLegislation VetosCount (r:1 w:1) + /// Storage: Identity IdentityOf (r:1 w:0) + /// Proof: Identity IdentityOf (max_values: None, max_size: Some(7538), added: 10013, mode: MaxEncodedLen) + /// Storage: LiberlandLegislation Vetos (r:1 w:1) + /// Proof Skipped: LiberlandLegislation Vetos (max_values: None, max_size: None, mode: Measured) + /// Storage: LiberlandLegislation VetosCount (r:1 w:1) + /// Proof Skipped: LiberlandLegislation VetosCount (max_values: None, max_size: None, mode: Measured) fn submit_veto() -> Weight { - // Minimum execution time: 31_379 nanoseconds. - Weight::from_ref_time(31_730_000) - .saturating_add(T::DbWeight::get().reads(3)) - .saturating_add(T::DbWeight::get().writes(2)) + // Proof Size summary in bytes: + // Measured: `446` + // Estimated: `11003` + // Minimum execution time: 18_696_000 picoseconds. + Weight::from_parts(19_086_000, 11003) + .saturating_add(T::DbWeight::get().reads(3_u64)) + .saturating_add(T::DbWeight::get().writes(2_u64)) } - // Storage: LiberlandLegislation Vetos (r:1 w:1) - // Storage: LiberlandLegislation VetosCount (r:1 w:1) + /// Storage: LiberlandLegislation Vetos (r:1 w:1) + /// Proof Skipped: LiberlandLegislation Vetos (max_values: None, max_size: None, mode: Measured) + /// Storage: LiberlandLegislation VetosCount (r:1 w:1) + /// Proof Skipped: LiberlandLegislation VetosCount (max_values: None, max_size: None, mode: Measured) fn revert_veto() -> Weight { - // Minimum execution time: 27_131 nanoseconds. - Weight::from_ref_time(27_602_000) - .saturating_add(T::DbWeight::get().reads(2)) - .saturating_add(T::DbWeight::get().writes(2)) + // Proof Size summary in bytes: + // Measured: `261` + // Estimated: `3726` + // Minimum execution time: 16_231_000 picoseconds. + Weight::from_parts(17_604_000, 3726) + .saturating_add(T::DbWeight::get().reads(2_u64)) + .saturating_add(T::DbWeight::get().writes(2_u64)) } - // Storage: LLM Citizens (r:1 w:0) - // Storage: LiberlandLegislation Vetos (r:17 w:16) - // Storage: Identity IdentityOf (r:16 w:0) - // Storage: LiberlandLegislation VetosCount (r:0 w:1) - // Storage: LiberlandLegislation Laws (r:0 w:1) - /// The range of component `c` is `[16, 5000]`. + /// Storage: LLM Citizens (r:1 w:0) + /// Proof: LLM Citizens (max_values: Some(1), max_size: Some(8), added: 503, mode: MaxEncodedLen) + /// Storage: LiberlandLegislation Vetos (r:1001 w:1000) + /// Proof Skipped: LiberlandLegislation Vetos (max_values: None, max_size: None, mode: Measured) + /// Storage: Identity IdentityOf (r:1000 w:0) + /// Proof: Identity IdentityOf (max_values: None, max_size: Some(7538), added: 10013, mode: MaxEncodedLen) + /// Storage: LiberlandLegislation VetosCount (r:0 w:1) + /// Proof Skipped: LiberlandLegislation VetosCount (max_values: None, max_size: None, mode: Measured) + /// Storage: LiberlandLegislation Laws (r:0 w:1) + /// Proof Skipped: LiberlandLegislation Laws (max_values: None, max_size: None, mode: Measured) + /// The range of component `c` is `[16, 1000]`. fn trigger_headcount_veto(c: u32, ) -> Weight { - // Minimum execution time: 167_418 nanoseconds. - Weight::from_ref_time(168_640_000) - // Standard Error: 58_796 - .saturating_add(Weight::from_ref_time(12_988_794).saturating_mul(c.into())) - .saturating_add(T::DbWeight::get().reads(2)) + // Proof Size summary in bytes: + // Measured: `795 + c * (158 ±0)` + // Estimated: `4284 + c * (10013 ±0)` + // Minimum execution time: 154_543_000 picoseconds. + Weight::from_parts(164_062_000, 4284) + // Standard Error: 74_769 + .saturating_add(Weight::from_parts(6_940_677, 0).saturating_mul(c.into())) + .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().reads((2_u64).saturating_mul(c.into()))) - .saturating_add(T::DbWeight::get().writes(2)) + .saturating_add(T::DbWeight::get().writes(2_u64)) .saturating_add(T::DbWeight::get().writes((1_u64).saturating_mul(c.into()))) + .saturating_add(Weight::from_parts(0, 10013).saturating_mul(c.into())) } } // For backwards compatibility and tests impl WeightInfo for () { - // Storage: LiberlandLegislation Laws (r:1 w:1) + /// Storage: LiberlandLegislation Laws (r:1 w:1) + /// Proof Skipped: LiberlandLegislation Laws (max_values: None, max_size: None, mode: Measured) /// The range of component `s` is `[1, 65536]`. fn add_law(s: u32, ) -> Weight { - // Minimum execution time: 17_403 nanoseconds. - Weight::from_ref_time(18_113_966) - // Standard Error: 4 - .saturating_add(Weight::from_ref_time(84).saturating_mul(s.into())) - .saturating_add(RocksDbWeight::get().reads(1)) - .saturating_add(RocksDbWeight::get().writes(1)) + // Proof Size summary in bytes: + // Measured: `76` + // Estimated: `3541` + // Minimum execution time: 7_925_000 picoseconds. + Weight::from_parts(8_439_585, 3541) + // Standard Error: 2 + .saturating_add(Weight::from_parts(75, 0).saturating_mul(s.into())) + .saturating_add(RocksDbWeight::get().reads(1_u64)) + .saturating_add(RocksDbWeight::get().writes(1_u64)) } - // Storage: LiberlandLegislation Laws (r:0 w:1) + /// Storage: LiberlandLegislation Laws (r:0 w:1) + /// Proof Skipped: LiberlandLegislation Laws (max_values: None, max_size: None, mode: Measured) fn repeal_law() -> Weight { - // Minimum execution time: 14_637 nanoseconds. - Weight::from_ref_time(14_988_000) - .saturating_add(RocksDbWeight::get().writes(1)) + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 4_920_000 picoseconds. + Weight::from_parts(4_969_000, 0) + .saturating_add(RocksDbWeight::get().writes(1_u64)) } - // Storage: Identity IdentityOf (r:1 w:0) - // Storage: LiberlandLegislation Vetos (r:1 w:1) - // Storage: LiberlandLegislation VetosCount (r:1 w:1) + /// Storage: Identity IdentityOf (r:1 w:0) + /// Proof: Identity IdentityOf (max_values: None, max_size: Some(7538), added: 10013, mode: MaxEncodedLen) + /// Storage: LiberlandLegislation Vetos (r:1 w:1) + /// Proof Skipped: LiberlandLegislation Vetos (max_values: None, max_size: None, mode: Measured) + /// Storage: LiberlandLegislation VetosCount (r:1 w:1) + /// Proof Skipped: LiberlandLegislation VetosCount (max_values: None, max_size: None, mode: Measured) fn submit_veto() -> Weight { - // Minimum execution time: 31_379 nanoseconds. - Weight::from_ref_time(31_730_000) - .saturating_add(RocksDbWeight::get().reads(3)) - .saturating_add(RocksDbWeight::get().writes(2)) + // Proof Size summary in bytes: + // Measured: `446` + // Estimated: `11003` + // Minimum execution time: 18_696_000 picoseconds. + Weight::from_parts(19_086_000, 11003) + .saturating_add(RocksDbWeight::get().reads(3_u64)) + .saturating_add(RocksDbWeight::get().writes(2_u64)) } - // Storage: LiberlandLegislation Vetos (r:1 w:1) - // Storage: LiberlandLegislation VetosCount (r:1 w:1) + /// Storage: LiberlandLegislation Vetos (r:1 w:1) + /// Proof Skipped: LiberlandLegislation Vetos (max_values: None, max_size: None, mode: Measured) + /// Storage: LiberlandLegislation VetosCount (r:1 w:1) + /// Proof Skipped: LiberlandLegislation VetosCount (max_values: None, max_size: None, mode: Measured) fn revert_veto() -> Weight { - // Minimum execution time: 27_131 nanoseconds. - Weight::from_ref_time(27_602_000) - .saturating_add(RocksDbWeight::get().reads(2)) - .saturating_add(RocksDbWeight::get().writes(2)) + // Proof Size summary in bytes: + // Measured: `261` + // Estimated: `3726` + // Minimum execution time: 16_231_000 picoseconds. + Weight::from_parts(17_604_000, 3726) + .saturating_add(RocksDbWeight::get().reads(2_u64)) + .saturating_add(RocksDbWeight::get().writes(2_u64)) } - // Storage: LLM Citizens (r:1 w:0) - // Storage: LiberlandLegislation Vetos (r:17 w:16) - // Storage: Identity IdentityOf (r:16 w:0) - // Storage: LiberlandLegislation VetosCount (r:0 w:1) - // Storage: LiberlandLegislation Laws (r:0 w:1) - /// The range of component `c` is `[16, 5000]`. + /// Storage: LLM Citizens (r:1 w:0) + /// Proof: LLM Citizens (max_values: Some(1), max_size: Some(8), added: 503, mode: MaxEncodedLen) + /// Storage: LiberlandLegislation Vetos (r:1001 w:1000) + /// Proof Skipped: LiberlandLegislation Vetos (max_values: None, max_size: None, mode: Measured) + /// Storage: Identity IdentityOf (r:1000 w:0) + /// Proof: Identity IdentityOf (max_values: None, max_size: Some(7538), added: 10013, mode: MaxEncodedLen) + /// Storage: LiberlandLegislation VetosCount (r:0 w:1) + /// Proof Skipped: LiberlandLegislation VetosCount (max_values: None, max_size: None, mode: Measured) + /// Storage: LiberlandLegislation Laws (r:0 w:1) + /// Proof Skipped: LiberlandLegislation Laws (max_values: None, max_size: None, mode: Measured) + /// The range of component `c` is `[16, 1000]`. fn trigger_headcount_veto(c: u32, ) -> Weight { - // Minimum execution time: 167_418 nanoseconds. - Weight::from_ref_time(168_640_000) - // Standard Error: 58_796 - .saturating_add(Weight::from_ref_time(12_988_794).saturating_mul(c.into())) - .saturating_add(RocksDbWeight::get().reads(2)) + // Proof Size summary in bytes: + // Measured: `795 + c * (158 ±0)` + // Estimated: `4284 + c * (10013 ±0)` + // Minimum execution time: 154_543_000 picoseconds. + Weight::from_parts(164_062_000, 4284) + // Standard Error: 74_769 + .saturating_add(Weight::from_parts(6_940_677, 0).saturating_mul(c.into())) + .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().reads((2_u64).saturating_mul(c.into()))) - .saturating_add(RocksDbWeight::get().writes(2)) + .saturating_add(RocksDbWeight::get().writes(2_u64)) .saturating_add(RocksDbWeight::get().writes((1_u64).saturating_mul(c.into()))) + .saturating_add(Weight::from_parts(0, 10013).saturating_mul(c.into())) } } diff --git a/frame/office/src/weights.rs b/frame/office/src/weights.rs index 5e91f8d0d..3082fd311 100644 --- a/frame/office/src/weights.rs +++ b/frame/office/src/weights.rs @@ -2,12 +2,13 @@ //! Autogenerated weights for pallet_office //! //! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev -//! DATE: 2023-03-06, STEPS: `20`, REPEAT: 10, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! DATE: 2023-06-23, STEPS: `20`, REPEAT: `10`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! WORST CASE MAP SIZE: `1000000` //! HOSTNAME: `kacper-HP-ProBook-445-G7`, CPU: `AMD Ryzen 7 4700U with Radeon Graphics` //! EXECUTION: Some(Wasm), WASM-EXECUTION: Compiled, CHAIN: None, DB CACHE: 1024 // Executed Command: -// target/release/substrate +// ./target/release/substrate // benchmark // pallet // --pallet=pallet_office @@ -22,9 +23,10 @@ #![cfg_attr(rustfmt, rustfmt_skip)] #![allow(unused_parens)] #![allow(unused_imports)] +#![allow(missing_docs)] use frame_support::{traits::Get, weights::{Weight, constants::RocksDbWeight}}; -use sp_std::marker::PhantomData; +use core::marker::PhantomData; /// Weight functions needed for pallet_office. pub trait WeightInfo { @@ -37,66 +39,102 @@ pub trait WeightInfo { /// Weights for pallet_office using the Substrate node and recommended hardware. pub struct SubstrateWeight(PhantomData); impl WeightInfo for SubstrateWeight { - // Storage: Office Admin (r:1 w:1) + /// Storage: IdentityOffice Admin (r:1 w:1) + /// Proof: IdentityOffice Admin (max_values: Some(1), max_size: Some(32), added: 527, mode: MaxEncodedLen) fn set_admin() -> Weight { - // Minimum execution time: 25_628 nanoseconds. - Weight::from_ref_time(26_210_000) - .saturating_add(T::DbWeight::get().reads(1)) - .saturating_add(T::DbWeight::get().writes(1)) + // Proof Size summary in bytes: + // Measured: `166` + // Estimated: `1517` + // Minimum execution time: 17_763_000 picoseconds. + Weight::from_parts(18_045_000, 1517) + .saturating_add(T::DbWeight::get().reads(1_u64)) + .saturating_add(T::DbWeight::get().writes(1_u64)) } - // Storage: Office Admin (r:1 w:0) - // Storage: Office Clerks (r:0 w:1) + /// Storage: IdentityOffice Admin (r:1 w:0) + /// Proof: IdentityOffice Admin (max_values: Some(1), max_size: Some(32), added: 527, mode: MaxEncodedLen) + /// Storage: IdentityOffice Clerks (r:0 w:1) + /// Proof: IdentityOffice Clerks (max_values: None, max_size: Some(49), added: 2524, mode: MaxEncodedLen) fn set_clerk() -> Weight { - // Minimum execution time: 27_241 nanoseconds. - Weight::from_ref_time(27_863_000) - .saturating_add(T::DbWeight::get().reads(1)) - .saturating_add(T::DbWeight::get().writes(1)) + // Proof Size summary in bytes: + // Measured: `166` + // Estimated: `1517` + // Minimum execution time: 19_407_000 picoseconds. + Weight::from_parts(19_497_000, 1517) + .saturating_add(T::DbWeight::get().reads(1_u64)) + .saturating_add(T::DbWeight::get().writes(1_u64)) } - // Storage: Office Admin (r:1 w:0) - // Storage: Office Clerks (r:1 w:1) + /// Storage: IdentityOffice Admin (r:1 w:0) + /// Proof: IdentityOffice Admin (max_values: Some(1), max_size: Some(32), added: 527, mode: MaxEncodedLen) + /// Storage: IdentityOffice Clerks (r:1 w:1) + /// Proof: IdentityOffice Clerks (max_values: None, max_size: Some(49), added: 2524, mode: MaxEncodedLen) fn remove_clerk() -> Weight { - // Minimum execution time: 30_267 nanoseconds. - Weight::from_ref_time(30_627_000) - .saturating_add(T::DbWeight::get().reads(2)) - .saturating_add(T::DbWeight::get().writes(1)) + // Proof Size summary in bytes: + // Measured: `240` + // Estimated: `3514` + // Minimum execution time: 23_955_000 picoseconds. + Weight::from_parts(24_246_000, 3514) + .saturating_add(T::DbWeight::get().reads(2_u64)) + .saturating_add(T::DbWeight::get().writes(1_u64)) } - // Storage: Office Clerks (r:1 w:0) + /// Storage: IdentityOffice Clerks (r:1 w:0) + /// Proof: IdentityOffice Clerks (max_values: None, max_size: Some(49), added: 2524, mode: MaxEncodedLen) fn execute() -> Weight { - // Minimum execution time: 29_666 nanoseconds. - Weight::from_ref_time(30_006_000) - .saturating_add(T::DbWeight::get().reads(1)) + // Proof Size summary in bytes: + // Measured: `222` + // Estimated: `3514` + // Minimum execution time: 22_543_000 picoseconds. + Weight::from_parts(22_843_000, 3514) + .saturating_add(T::DbWeight::get().reads(1_u64)) } } // For backwards compatibility and tests impl WeightInfo for () { - // Storage: Office Admin (r:1 w:1) + /// Storage: IdentityOffice Admin (r:1 w:1) + /// Proof: IdentityOffice Admin (max_values: Some(1), max_size: Some(32), added: 527, mode: MaxEncodedLen) fn set_admin() -> Weight { - // Minimum execution time: 25_628 nanoseconds. - Weight::from_ref_time(26_210_000) - .saturating_add(RocksDbWeight::get().reads(1)) - .saturating_add(RocksDbWeight::get().writes(1)) + // Proof Size summary in bytes: + // Measured: `166` + // Estimated: `1517` + // Minimum execution time: 17_763_000 picoseconds. + Weight::from_parts(18_045_000, 1517) + .saturating_add(RocksDbWeight::get().reads(1_u64)) + .saturating_add(RocksDbWeight::get().writes(1_u64)) } - // Storage: Office Admin (r:1 w:0) - // Storage: Office Clerks (r:0 w:1) + /// Storage: IdentityOffice Admin (r:1 w:0) + /// Proof: IdentityOffice Admin (max_values: Some(1), max_size: Some(32), added: 527, mode: MaxEncodedLen) + /// Storage: IdentityOffice Clerks (r:0 w:1) + /// Proof: IdentityOffice Clerks (max_values: None, max_size: Some(49), added: 2524, mode: MaxEncodedLen) fn set_clerk() -> Weight { - // Minimum execution time: 27_241 nanoseconds. - Weight::from_ref_time(27_863_000) - .saturating_add(RocksDbWeight::get().reads(1)) - .saturating_add(RocksDbWeight::get().writes(1)) + // Proof Size summary in bytes: + // Measured: `166` + // Estimated: `1517` + // Minimum execution time: 19_407_000 picoseconds. + Weight::from_parts(19_497_000, 1517) + .saturating_add(RocksDbWeight::get().reads(1_u64)) + .saturating_add(RocksDbWeight::get().writes(1_u64)) } - // Storage: Office Admin (r:1 w:0) - // Storage: Office Clerks (r:1 w:1) + /// Storage: IdentityOffice Admin (r:1 w:0) + /// Proof: IdentityOffice Admin (max_values: Some(1), max_size: Some(32), added: 527, mode: MaxEncodedLen) + /// Storage: IdentityOffice Clerks (r:1 w:1) + /// Proof: IdentityOffice Clerks (max_values: None, max_size: Some(49), added: 2524, mode: MaxEncodedLen) fn remove_clerk() -> Weight { - // Minimum execution time: 30_267 nanoseconds. - Weight::from_ref_time(30_627_000) - .saturating_add(RocksDbWeight::get().reads(2)) - .saturating_add(RocksDbWeight::get().writes(1)) + // Proof Size summary in bytes: + // Measured: `240` + // Estimated: `3514` + // Minimum execution time: 23_955_000 picoseconds. + Weight::from_parts(24_246_000, 3514) + .saturating_add(RocksDbWeight::get().reads(2_u64)) + .saturating_add(RocksDbWeight::get().writes(1_u64)) } - // Storage: Office Clerks (r:1 w:0) + /// Storage: IdentityOffice Clerks (r:1 w:0) + /// Proof: IdentityOffice Clerks (max_values: None, max_size: Some(49), added: 2524, mode: MaxEncodedLen) fn execute() -> Weight { - // Minimum execution time: 29_666 nanoseconds. - Weight::from_ref_time(30_006_000) - .saturating_add(RocksDbWeight::get().reads(1)) + // Proof Size summary in bytes: + // Measured: `222` + // Estimated: `3514` + // Minimum execution time: 22_543_000 picoseconds. + Weight::from_parts(22_843_000, 3514) + .saturating_add(RocksDbWeight::get().reads(1_u64)) } } diff --git a/frame/registry/Cargo.toml b/frame/registry/Cargo.toml index 9d3de825d..d03f42cbe 100644 --- a/frame/registry/Cargo.toml +++ b/frame/registry/Cargo.toml @@ -45,5 +45,6 @@ std = [ ] runtime-benchmarks = [ "frame-benchmarking/runtime-benchmarks", + "pallet-collective/runtime-benchmarks", ] try-runtime = ["frame-support/try-runtime"] diff --git a/frame/registry/src/benchmarking.rs b/frame/registry/src/benchmarking.rs index 480ac17a4..a314bc78d 100644 --- a/frame/registry/src/benchmarking.rs +++ b/frame/registry/src/benchmarking.rs @@ -28,11 +28,12 @@ fn get_data, I: 'static>(b: u8, s: usize) -> T::EntityData { benchmarks_instance_pallet! { add_registry { - let r in 1 .. T::MaxRegistrars::get() - 1 => add_registries::(r)?; + let r in 1 .. T::MaxRegistrars::get() - 2 => add_registries::(r)?; let acc = account("registrar", r, SEED); + let registrars_before = Registry::::registrars().len(); }: _(RawOrigin::Root.into(), acc) verify { - assert_eq!(Registry::::registrars().len(), (r + 1) as usize); + assert_eq!(Registry::::registrars().len(), (registrars_before + 1) as usize); } request_entity { @@ -97,10 +98,10 @@ benchmarks_instance_pallet! { */ unregister { - let r in 1 .. T::MaxRegistrars::get() => add_registries::(r)?; + let r in 1 .. T::MaxRegistrars::get() - 2 => add_registries::(r)?; let registrar: T::RuntimeOrigin = RawOrigin::Signed(account("registrar", r, SEED)).into(); - let reg_idx = r - 1; + let reg_idx: u32 = Registry::::registrars().len() as u32 - 1; let acc: T::AccountId = account("owner", 0, SEED); let origin: T::RuntimeOrigin = RawOrigin::Signed(acc.clone()).into(); let _ = T::Currency::make_free_balance_be(&acc, BalanceOf::::max_value() / 2u32.into()); @@ -121,11 +122,11 @@ benchmarks_instance_pallet! { } register_entity { - let r in 1 .. T::MaxRegistrars::get() => add_registries::(r)?; + let r in 1 .. T::MaxRegistrars::get() - 2 => add_registries::(r)?; let s in 2 .. T::EntityData::max_encoded_len() as u32; let registrar: T::RuntimeOrigin = RawOrigin::Signed(account("registrar", r, SEED)).into(); - let reg_idx = r - 1; + let reg_idx: u32 = Registry::::registrars().len() as u32 - 1; let acc: T::AccountId = account("owner", 0, SEED); let origin: T::RuntimeOrigin = RawOrigin::Signed(acc.clone()).into(); let _ = T::Currency::make_free_balance_be(&acc, BalanceOf::::max_value() / 2u32.into()); @@ -157,11 +158,11 @@ benchmarks_instance_pallet! { } set_registered_entity { - let r in 1 .. T::MaxRegistrars::get() => add_registries::(r)?; + let r in 1 .. T::MaxRegistrars::get() - 2 => add_registries::(r)?; let s in 2 .. T::EntityData::max_encoded_len() as u32; let registrar: T::RuntimeOrigin = RawOrigin::Signed(account("registrar", r, SEED)).into(); - let reg_idx = r - 1; + let reg_idx: u32 = Registry::::registrars().len() as u32 - 1; let acc: T::AccountId = account("owner", 0, SEED); let origin: T::RuntimeOrigin = RawOrigin::Signed(acc.clone()).into(); let _ = T::Currency::make_free_balance_be(&acc, BalanceOf::::max_value() / 2u32.into()); diff --git a/frame/registry/src/weights.rs b/frame/registry/src/weights.rs index 586cf9ab2..8e8bb6add 100644 --- a/frame/registry/src/weights.rs +++ b/frame/registry/src/weights.rs @@ -2,7 +2,8 @@ //! Autogenerated weights for pallet_registry //! //! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev -//! DATE: 2023-03-02, STEPS: `20`, REPEAT: 10, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! DATE: 2023-06-23, STEPS: `20`, REPEAT: `10`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! WORST CASE MAP SIZE: `1000000` //! HOSTNAME: `kacper-HP-ProBook-445-G7`, CPU: `AMD Ryzen 7 4700U with Radeon Graphics` //! EXECUTION: Some(Wasm), WASM-EXECUTION: Compiled, CHAIN: None, DB CACHE: 1024 @@ -22,9 +23,10 @@ #![cfg_attr(rustfmt, rustfmt_skip)] #![allow(unused_parens)] #![allow(unused_imports)] +#![allow(missing_docs)] use frame_support::{traits::Get, weights::{Weight, constants::RocksDbWeight}}; -use sp_std::marker::PhantomData; +use core::marker::PhantomData; /// Weight functions needed for pallet_registry. pub trait WeightInfo { @@ -40,188 +42,282 @@ pub trait WeightInfo { /// Weights for pallet_registry using the Substrate node and recommended hardware. pub struct SubstrateWeight(PhantomData); impl WeightInfo for SubstrateWeight { - // Storage: CompanyRegistry Registrars (r:1 w:1) - /// The range of component `r` is `[1, 9]`. + /// Storage: CompanyRegistry Registrars (r:1 w:1) + /// Proof: CompanyRegistry Registrars (max_values: Some(1), max_size: Some(321), added: 816, mode: MaxEncodedLen) + /// The range of component `r` is `[1, 8]`. fn add_registry(r: u32, ) -> Weight { - // Minimum execution time: 25_949 nanoseconds. - Weight::from_ref_time(26_630_676) - // Standard Error: 15_513 - .saturating_add(Weight::from_ref_time(109_488).saturating_mul(r.into())) - .saturating_add(T::DbWeight::get().reads(1)) - .saturating_add(T::DbWeight::get().writes(1)) + // Proof Size summary in bytes: + // Measured: `235 + r * (32 ±0)` + // Estimated: `1806` + // Minimum execution time: 18_876_000 picoseconds. + Weight::from_parts(19_483_330, 1806) + // Standard Error: 69_679 + .saturating_add(Weight::from_parts(384_797, 0).saturating_mul(r.into())) + .saturating_add(T::DbWeight::get().reads(1_u64)) + .saturating_add(T::DbWeight::get().writes(1_u64)) } - // Storage: CompanyRegistry NextEntityId (r:1 w:1) - // Storage: CompanyRegistry Requests (r:1 w:1) - // Storage: Balances Reserves (r:1 w:1) - // Storage: System Account (r:1 w:1) - // Storage: CompanyRegistry EntityOwner (r:0 w:1) - // Storage: CompanyRegistry OwnerEntities (r:0 w:1) + /// Storage: CompanyRegistry NextEntityId (r:1 w:1) + /// Proof: CompanyRegistry NextEntityId (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) + /// Storage: CompanyRegistry Requests (r:1 w:1) + /// Proof: CompanyRegistry Requests (max_values: None, max_size: Some(8251), added: 10726, mode: MaxEncodedLen) + /// Storage: Balances Reserves (r:1 w:1) + /// Proof: Balances Reserves (max_values: None, max_size: Some(1249), added: 3724, mode: MaxEncodedLen) + /// Storage: System Account (r:1 w:1) + /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) + /// Storage: CompanyRegistry EntityOwner (r:0 w:1) + /// Proof: CompanyRegistry EntityOwner (max_values: None, max_size: Some(52), added: 2527, mode: MaxEncodedLen) + /// Storage: CompanyRegistry OwnerEntities (r:0 w:1) + /// Proof: CompanyRegistry OwnerEntities (max_values: None, max_size: Some(69), added: 2544, mode: MaxEncodedLen) /// The range of component `s` is `[2, 8194]`. fn request_entity(s: u32, ) -> Weight { - // Minimum execution time: 60_154 nanoseconds. - Weight::from_ref_time(62_525_821) - // Standard Error: 60 - .saturating_add(Weight::from_ref_time(569).saturating_mul(s.into())) - .saturating_add(T::DbWeight::get().reads(4)) - .saturating_add(T::DbWeight::get().writes(6)) + // Proof Size summary in bytes: + // Measured: `316` + // Estimated: `11716` + // Minimum execution time: 61_807_000 picoseconds. + Weight::from_parts(64_763_447, 11716) + // Standard Error: 115 + .saturating_add(Weight::from_parts(412, 0).saturating_mul(s.into())) + .saturating_add(T::DbWeight::get().reads(4_u64)) + .saturating_add(T::DbWeight::get().writes(6_u64)) } - // Storage: CompanyRegistry EntityOwner (r:1 w:0) - // Storage: CompanyRegistry Requests (r:1 w:1) - // Storage: Balances Reserves (r:1 w:1) - // Storage: System Account (r:1 w:1) + /// Storage: CompanyRegistry EntityOwner (r:1 w:0) + /// Proof: CompanyRegistry EntityOwner (max_values: None, max_size: Some(52), added: 2527, mode: MaxEncodedLen) + /// Storage: CompanyRegistry Requests (r:1 w:1) + /// Proof: CompanyRegistry Requests (max_values: None, max_size: Some(8251), added: 10726, mode: MaxEncodedLen) + /// Storage: Balances Reserves (r:1 w:1) + /// Proof: Balances Reserves (max_values: None, max_size: Some(1249), added: 3724, mode: MaxEncodedLen) + /// Storage: System Account (r:1 w:1) + /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) /// The range of component `s` is `[2, 8194]`. fn request_registration(s: u32, ) -> Weight { - // Minimum execution time: 73_700 nanoseconds. - Weight::from_ref_time(76_333_235) - // Standard Error: 152 - .saturating_add(Weight::from_ref_time(1_338).saturating_mul(s.into())) - .saturating_add(T::DbWeight::get().reads(4)) - .saturating_add(T::DbWeight::get().writes(3)) + // Proof Size summary in bytes: + // Measured: `593 + s * (1 ±0)` + // Estimated: `11716` + // Minimum execution time: 84_531_000 picoseconds. + Weight::from_parts(88_087_918, 11716) + // Standard Error: 63 + .saturating_add(Weight::from_parts(1_008, 0).saturating_mul(s.into())) + .saturating_add(T::DbWeight::get().reads(4_u64)) + .saturating_add(T::DbWeight::get().writes(3_u64)) } - // Storage: CompanyRegistry EntityOwner (r:1 w:0) - // Storage: CompanyRegistry Requests (r:1 w:1) - // Storage: Balances Reserves (r:1 w:1) - // Storage: System Account (r:1 w:1) + /// Storage: CompanyRegistry EntityOwner (r:1 w:0) + /// Proof: CompanyRegistry EntityOwner (max_values: None, max_size: Some(52), added: 2527, mode: MaxEncodedLen) + /// Storage: CompanyRegistry Requests (r:1 w:1) + /// Proof: CompanyRegistry Requests (max_values: None, max_size: Some(8251), added: 10726, mode: MaxEncodedLen) + /// Storage: Balances Reserves (r:1 w:1) + /// Proof: Balances Reserves (max_values: None, max_size: Some(1249), added: 3724, mode: MaxEncodedLen) + /// Storage: System Account (r:1 w:1) + /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) fn cancel_request() -> Weight { - // Minimum execution time: 60_965 nanoseconds. - Weight::from_ref_time(61_636_000) - .saturating_add(T::DbWeight::get().reads(4)) - .saturating_add(T::DbWeight::get().writes(3)) + // Proof Size summary in bytes: + // Measured: `693` + // Estimated: `11716` + // Minimum execution time: 61_646_000 picoseconds. + Weight::from_parts(62_619_000, 11716) + .saturating_add(T::DbWeight::get().reads(4_u64)) + .saturating_add(T::DbWeight::get().writes(3_u64)) } - // Storage: CompanyRegistry Registrars (r:1 w:0) - // Storage: CompanyRegistry Registries (r:1 w:1) - // Storage: CompanyRegistry EntityOwner (r:1 w:0) - // Storage: Balances Reserves (r:1 w:1) - // Storage: System Account (r:1 w:1) - /// The range of component `r` is `[1, 10]`. + /// Storage: CompanyRegistry Registrars (r:1 w:0) + /// Proof: CompanyRegistry Registrars (max_values: Some(1), max_size: Some(321), added: 816, mode: MaxEncodedLen) + /// Storage: CompanyRegistry Registries (r:1 w:1) + /// Proof: CompanyRegistry Registries (max_values: None, max_size: Some(8251), added: 10726, mode: MaxEncodedLen) + /// Storage: CompanyRegistry EntityOwner (r:1 w:0) + /// Proof: CompanyRegistry EntityOwner (max_values: None, max_size: Some(52), added: 2527, mode: MaxEncodedLen) + /// Storage: Balances Reserves (r:1 w:1) + /// Proof: Balances Reserves (max_values: None, max_size: Some(1249), added: 3724, mode: MaxEncodedLen) + /// Storage: System Account (r:1 w:1) + /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) + /// The range of component `r` is `[1, 8]`. fn unregister(_r: u32, ) -> Weight { - // Minimum execution time: 59_964 nanoseconds. - Weight::from_ref_time(62_568_430) - .saturating_add(T::DbWeight::get().reads(5)) - .saturating_add(T::DbWeight::get().writes(3)) + // Proof Size summary in bytes: + // Measured: `712 + r * (32 ±0)` + // Estimated: `11716` + // Minimum execution time: 60_505_000 picoseconds. + Weight::from_parts(63_352_987, 11716) + .saturating_add(T::DbWeight::get().reads(5_u64)) + .saturating_add(T::DbWeight::get().writes(3_u64)) } - // Storage: CompanyRegistry Registrars (r:1 w:0) - // Storage: CompanyRegistry Requests (r:1 w:1) - // Storage: CompanyRegistry EntityOwner (r:1 w:0) - // Storage: CompanyRegistry Registries (r:1 w:1) - /// The range of component `r` is `[1, 10]`. + /// Storage: CompanyRegistry Registrars (r:1 w:0) + /// Proof: CompanyRegistry Registrars (max_values: Some(1), max_size: Some(321), added: 816, mode: MaxEncodedLen) + /// Storage: CompanyRegistry Requests (r:1 w:1) + /// Proof: CompanyRegistry Requests (max_values: None, max_size: Some(8251), added: 10726, mode: MaxEncodedLen) + /// Storage: CompanyRegistry EntityOwner (r:1 w:0) + /// Proof: CompanyRegistry EntityOwner (max_values: None, max_size: Some(52), added: 2527, mode: MaxEncodedLen) + /// Storage: CompanyRegistry Registries (r:1 w:1) + /// Proof: CompanyRegistry Registries (max_values: None, max_size: Some(8251), added: 10726, mode: MaxEncodedLen) + /// The range of component `r` is `[1, 8]`. /// The range of component `s` is `[2, 8194]`. fn register_entity(r: u32, s: u32, ) -> Weight { - // Minimum execution time: 45_225 nanoseconds. - Weight::from_ref_time(46_933_520) - // Standard Error: 33_003 - .saturating_add(Weight::from_ref_time(19_716).saturating_mul(r.into())) - // Standard Error: 37 - .saturating_add(Weight::from_ref_time(2_024).saturating_mul(s.into())) - .saturating_add(T::DbWeight::get().reads(4)) - .saturating_add(T::DbWeight::get().writes(2)) + // Proof Size summary in bytes: + // Measured: `451 + r * (32 ±0) + s * (1 ±0)` + // Estimated: `11716` + // Minimum execution time: 37_541_000 picoseconds. + Weight::from_parts(39_476_882, 11716) + // Standard Error: 19_642 + .saturating_add(Weight::from_parts(11_047, 0).saturating_mul(r.into())) + // Standard Error: 17 + .saturating_add(Weight::from_parts(1_753, 0).saturating_mul(s.into())) + .saturating_add(T::DbWeight::get().reads(4_u64)) + .saturating_add(T::DbWeight::get().writes(2_u64)) } - // Storage: CompanyRegistry Registrars (r:1 w:0) - // Storage: CompanyRegistry Registries (r:1 w:1) - /// The range of component `r` is `[1, 10]`. + /// Storage: CompanyRegistry Registrars (r:1 w:0) + /// Proof: CompanyRegistry Registrars (max_values: Some(1), max_size: Some(321), added: 816, mode: MaxEncodedLen) + /// Storage: CompanyRegistry Registries (r:1 w:1) + /// Proof: CompanyRegistry Registries (max_values: None, max_size: Some(8251), added: 10726, mode: MaxEncodedLen) + /// The range of component `r` is `[1, 8]`. /// The range of component `s` is `[2, 8194]`. fn set_registered_entity(r: u32, s: u32, ) -> Weight { - // Minimum execution time: 34_325 nanoseconds. - Weight::from_ref_time(34_274_327) - // Standard Error: 139_638 - .saturating_add(Weight::from_ref_time(192_402).saturating_mul(r.into())) - // Standard Error: 160 - .saturating_add(Weight::from_ref_time(1_251).saturating_mul(s.into())) - .saturating_add(T::DbWeight::get().reads(2)) - .saturating_add(T::DbWeight::get().writes(1)) + // Proof Size summary in bytes: + // Measured: `406 + r * (32 ±0) + s * (1 ±0)` + // Estimated: `11716` + // Minimum execution time: 26_109_000 picoseconds. + Weight::from_parts(27_675_938, 11716) + // Standard Error: 13_707 + .saturating_add(Weight::from_parts(6_976, 0).saturating_mul(r.into())) + // Standard Error: 12 + .saturating_add(Weight::from_parts(1_074, 0).saturating_mul(s.into())) + .saturating_add(T::DbWeight::get().reads(2_u64)) + .saturating_add(T::DbWeight::get().writes(1_u64)) } } // For backwards compatibility and tests impl WeightInfo for () { - // Storage: CompanyRegistry Registrars (r:1 w:1) - /// The range of component `r` is `[1, 9]`. + /// Storage: CompanyRegistry Registrars (r:1 w:1) + /// Proof: CompanyRegistry Registrars (max_values: Some(1), max_size: Some(321), added: 816, mode: MaxEncodedLen) + /// The range of component `r` is `[1, 8]`. fn add_registry(r: u32, ) -> Weight { - // Minimum execution time: 25_949 nanoseconds. - Weight::from_ref_time(26_630_676) - // Standard Error: 15_513 - .saturating_add(Weight::from_ref_time(109_488).saturating_mul(r.into())) - .saturating_add(RocksDbWeight::get().reads(1)) - .saturating_add(RocksDbWeight::get().writes(1)) + // Proof Size summary in bytes: + // Measured: `235 + r * (32 ±0)` + // Estimated: `1806` + // Minimum execution time: 18_876_000 picoseconds. + Weight::from_parts(19_483_330, 1806) + // Standard Error: 69_679 + .saturating_add(Weight::from_parts(384_797, 0).saturating_mul(r.into())) + .saturating_add(RocksDbWeight::get().reads(1_u64)) + .saturating_add(RocksDbWeight::get().writes(1_u64)) } - // Storage: CompanyRegistry NextEntityId (r:1 w:1) - // Storage: CompanyRegistry Requests (r:1 w:1) - // Storage: Balances Reserves (r:1 w:1) - // Storage: System Account (r:1 w:1) - // Storage: CompanyRegistry EntityOwner (r:0 w:1) - // Storage: CompanyRegistry OwnerEntities (r:0 w:1) + /// Storage: CompanyRegistry NextEntityId (r:1 w:1) + /// Proof: CompanyRegistry NextEntityId (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) + /// Storage: CompanyRegistry Requests (r:1 w:1) + /// Proof: CompanyRegistry Requests (max_values: None, max_size: Some(8251), added: 10726, mode: MaxEncodedLen) + /// Storage: Balances Reserves (r:1 w:1) + /// Proof: Balances Reserves (max_values: None, max_size: Some(1249), added: 3724, mode: MaxEncodedLen) + /// Storage: System Account (r:1 w:1) + /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) + /// Storage: CompanyRegistry EntityOwner (r:0 w:1) + /// Proof: CompanyRegistry EntityOwner (max_values: None, max_size: Some(52), added: 2527, mode: MaxEncodedLen) + /// Storage: CompanyRegistry OwnerEntities (r:0 w:1) + /// Proof: CompanyRegistry OwnerEntities (max_values: None, max_size: Some(69), added: 2544, mode: MaxEncodedLen) /// The range of component `s` is `[2, 8194]`. fn request_entity(s: u32, ) -> Weight { - // Minimum execution time: 60_154 nanoseconds. - Weight::from_ref_time(62_525_821) - // Standard Error: 60 - .saturating_add(Weight::from_ref_time(569).saturating_mul(s.into())) - .saturating_add(RocksDbWeight::get().reads(4)) - .saturating_add(RocksDbWeight::get().writes(6)) + // Proof Size summary in bytes: + // Measured: `316` + // Estimated: `11716` + // Minimum execution time: 61_807_000 picoseconds. + Weight::from_parts(64_763_447, 11716) + // Standard Error: 115 + .saturating_add(Weight::from_parts(412, 0).saturating_mul(s.into())) + .saturating_add(RocksDbWeight::get().reads(4_u64)) + .saturating_add(RocksDbWeight::get().writes(6_u64)) } - // Storage: CompanyRegistry EntityOwner (r:1 w:0) - // Storage: CompanyRegistry Requests (r:1 w:1) - // Storage: Balances Reserves (r:1 w:1) - // Storage: System Account (r:1 w:1) + /// Storage: CompanyRegistry EntityOwner (r:1 w:0) + /// Proof: CompanyRegistry EntityOwner (max_values: None, max_size: Some(52), added: 2527, mode: MaxEncodedLen) + /// Storage: CompanyRegistry Requests (r:1 w:1) + /// Proof: CompanyRegistry Requests (max_values: None, max_size: Some(8251), added: 10726, mode: MaxEncodedLen) + /// Storage: Balances Reserves (r:1 w:1) + /// Proof: Balances Reserves (max_values: None, max_size: Some(1249), added: 3724, mode: MaxEncodedLen) + /// Storage: System Account (r:1 w:1) + /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) /// The range of component `s` is `[2, 8194]`. fn request_registration(s: u32, ) -> Weight { - // Minimum execution time: 73_700 nanoseconds. - Weight::from_ref_time(76_333_235) - // Standard Error: 152 - .saturating_add(Weight::from_ref_time(1_338).saturating_mul(s.into())) - .saturating_add(RocksDbWeight::get().reads(4)) - .saturating_add(RocksDbWeight::get().writes(3)) + // Proof Size summary in bytes: + // Measured: `593 + s * (1 ±0)` + // Estimated: `11716` + // Minimum execution time: 84_531_000 picoseconds. + Weight::from_parts(88_087_918, 11716) + // Standard Error: 63 + .saturating_add(Weight::from_parts(1_008, 0).saturating_mul(s.into())) + .saturating_add(RocksDbWeight::get().reads(4_u64)) + .saturating_add(RocksDbWeight::get().writes(3_u64)) } - // Storage: CompanyRegistry EntityOwner (r:1 w:0) - // Storage: CompanyRegistry Requests (r:1 w:1) - // Storage: Balances Reserves (r:1 w:1) - // Storage: System Account (r:1 w:1) + /// Storage: CompanyRegistry EntityOwner (r:1 w:0) + /// Proof: CompanyRegistry EntityOwner (max_values: None, max_size: Some(52), added: 2527, mode: MaxEncodedLen) + /// Storage: CompanyRegistry Requests (r:1 w:1) + /// Proof: CompanyRegistry Requests (max_values: None, max_size: Some(8251), added: 10726, mode: MaxEncodedLen) + /// Storage: Balances Reserves (r:1 w:1) + /// Proof: Balances Reserves (max_values: None, max_size: Some(1249), added: 3724, mode: MaxEncodedLen) + /// Storage: System Account (r:1 w:1) + /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) fn cancel_request() -> Weight { - // Minimum execution time: 60_965 nanoseconds. - Weight::from_ref_time(61_636_000) - .saturating_add(RocksDbWeight::get().reads(4)) - .saturating_add(RocksDbWeight::get().writes(3)) + // Proof Size summary in bytes: + // Measured: `693` + // Estimated: `11716` + // Minimum execution time: 61_646_000 picoseconds. + Weight::from_parts(62_619_000, 11716) + .saturating_add(RocksDbWeight::get().reads(4_u64)) + .saturating_add(RocksDbWeight::get().writes(3_u64)) } - // Storage: CompanyRegistry Registrars (r:1 w:0) - // Storage: CompanyRegistry Registries (r:1 w:1) - // Storage: CompanyRegistry EntityOwner (r:1 w:0) - // Storage: Balances Reserves (r:1 w:1) - // Storage: System Account (r:1 w:1) - /// The range of component `r` is `[1, 10]`. + /// Storage: CompanyRegistry Registrars (r:1 w:0) + /// Proof: CompanyRegistry Registrars (max_values: Some(1), max_size: Some(321), added: 816, mode: MaxEncodedLen) + /// Storage: CompanyRegistry Registries (r:1 w:1) + /// Proof: CompanyRegistry Registries (max_values: None, max_size: Some(8251), added: 10726, mode: MaxEncodedLen) + /// Storage: CompanyRegistry EntityOwner (r:1 w:0) + /// Proof: CompanyRegistry EntityOwner (max_values: None, max_size: Some(52), added: 2527, mode: MaxEncodedLen) + /// Storage: Balances Reserves (r:1 w:1) + /// Proof: Balances Reserves (max_values: None, max_size: Some(1249), added: 3724, mode: MaxEncodedLen) + /// Storage: System Account (r:1 w:1) + /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) + /// The range of component `r` is `[1, 8]`. fn unregister(_r: u32, ) -> Weight { - // Minimum execution time: 59_964 nanoseconds. - Weight::from_ref_time(62_568_430) - .saturating_add(RocksDbWeight::get().reads(5)) - .saturating_add(RocksDbWeight::get().writes(3)) + // Proof Size summary in bytes: + // Measured: `712 + r * (32 ±0)` + // Estimated: `11716` + // Minimum execution time: 60_505_000 picoseconds. + Weight::from_parts(63_352_987, 11716) + .saturating_add(RocksDbWeight::get().reads(5_u64)) + .saturating_add(RocksDbWeight::get().writes(3_u64)) } - // Storage: CompanyRegistry Registrars (r:1 w:0) - // Storage: CompanyRegistry Requests (r:1 w:1) - // Storage: CompanyRegistry EntityOwner (r:1 w:0) - // Storage: CompanyRegistry Registries (r:1 w:1) - /// The range of component `r` is `[1, 10]`. + /// Storage: CompanyRegistry Registrars (r:1 w:0) + /// Proof: CompanyRegistry Registrars (max_values: Some(1), max_size: Some(321), added: 816, mode: MaxEncodedLen) + /// Storage: CompanyRegistry Requests (r:1 w:1) + /// Proof: CompanyRegistry Requests (max_values: None, max_size: Some(8251), added: 10726, mode: MaxEncodedLen) + /// Storage: CompanyRegistry EntityOwner (r:1 w:0) + /// Proof: CompanyRegistry EntityOwner (max_values: None, max_size: Some(52), added: 2527, mode: MaxEncodedLen) + /// Storage: CompanyRegistry Registries (r:1 w:1) + /// Proof: CompanyRegistry Registries (max_values: None, max_size: Some(8251), added: 10726, mode: MaxEncodedLen) + /// The range of component `r` is `[1, 8]`. /// The range of component `s` is `[2, 8194]`. fn register_entity(r: u32, s: u32, ) -> Weight { - // Minimum execution time: 45_225 nanoseconds. - Weight::from_ref_time(46_933_520) - // Standard Error: 33_003 - .saturating_add(Weight::from_ref_time(19_716).saturating_mul(r.into())) - // Standard Error: 37 - .saturating_add(Weight::from_ref_time(2_024).saturating_mul(s.into())) - .saturating_add(RocksDbWeight::get().reads(4)) - .saturating_add(RocksDbWeight::get().writes(2)) + // Proof Size summary in bytes: + // Measured: `451 + r * (32 ±0) + s * (1 ±0)` + // Estimated: `11716` + // Minimum execution time: 37_541_000 picoseconds. + Weight::from_parts(39_476_882, 11716) + // Standard Error: 19_642 + .saturating_add(Weight::from_parts(11_047, 0).saturating_mul(r.into())) + // Standard Error: 17 + .saturating_add(Weight::from_parts(1_753, 0).saturating_mul(s.into())) + .saturating_add(RocksDbWeight::get().reads(4_u64)) + .saturating_add(RocksDbWeight::get().writes(2_u64)) } - // Storage: CompanyRegistry Registrars (r:1 w:0) - // Storage: CompanyRegistry Registries (r:1 w:1) - /// The range of component `r` is `[1, 10]`. + /// Storage: CompanyRegistry Registrars (r:1 w:0) + /// Proof: CompanyRegistry Registrars (max_values: Some(1), max_size: Some(321), added: 816, mode: MaxEncodedLen) + /// Storage: CompanyRegistry Registries (r:1 w:1) + /// Proof: CompanyRegistry Registries (max_values: None, max_size: Some(8251), added: 10726, mode: MaxEncodedLen) + /// The range of component `r` is `[1, 8]`. /// The range of component `s` is `[2, 8194]`. fn set_registered_entity(r: u32, s: u32, ) -> Weight { - // Minimum execution time: 34_325 nanoseconds. - Weight::from_ref_time(34_274_327) - // Standard Error: 139_638 - .saturating_add(Weight::from_ref_time(192_402).saturating_mul(r.into())) - // Standard Error: 160 - .saturating_add(Weight::from_ref_time(1_251).saturating_mul(s.into())) - .saturating_add(RocksDbWeight::get().reads(2)) - .saturating_add(RocksDbWeight::get().writes(1)) + // Proof Size summary in bytes: + // Measured: `406 + r * (32 ±0) + s * (1 ±0)` + // Estimated: `11716` + // Minimum execution time: 26_109_000 picoseconds. + Weight::from_parts(27_675_938, 11716) + // Standard Error: 13_707 + .saturating_add(Weight::from_parts(6_976, 0).saturating_mul(r.into())) + // Standard Error: 12 + .saturating_add(Weight::from_parts(1_074, 0).saturating_mul(s.into())) + .saturating_add(RocksDbWeight::get().reads(2_u64)) + .saturating_add(RocksDbWeight::get().writes(1_u64)) } } From 7058c052434bc031644cb3d6313f5765fa75da36 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kacper=20=C5=BBuk?= Date: Mon, 26 Jun 2023 11:26:41 +0200 Subject: [PATCH 552/558] Fix tests --- frame/nfts/src/mock.rs | 4 ++-- frame/registry/src/tests.rs | 6 ------ frame/staking/Cargo.toml | 1 + frame/staking/src/benchmarking.rs | 2 -- frame/staking/src/mock.rs | 4 +--- frame/staking/src/testing_utils.rs | 3 --- frame/staking/src/tests.rs | 17 ++++++++++++++--- 7 files changed, 18 insertions(+), 19 deletions(-) diff --git a/frame/nfts/src/mock.rs b/frame/nfts/src/mock.rs index 911ea0cd9..d27af539f 100644 --- a/frame/nfts/src/mock.rs +++ b/frame/nfts/src/mock.rs @@ -113,8 +113,8 @@ parameter_types! { } parameter_types! { - pub MockCitizenOne: AccountId = [1u8; 32].into(); - pub MockCitizenTwo: AccountId = [2u8; 32].into(); + pub MockCitizenOne: AccountId = [100u8; 32].into(); + pub MockCitizenTwo: AccountId = [101u8; 32].into(); } impl Config for Test { diff --git a/frame/registry/src/tests.rs b/frame/registry/src/tests.rs index c5a62d9ac..1be9078b9 100644 --- a/frame/registry/src/tests.rs +++ b/frame/registry/src/tests.rs @@ -155,16 +155,10 @@ fn request_entity_works() { #[test] fn request_entity_fails_on_broke() { new_test_ext().execute_with(|| { - let almost_broke = RuntimeOrigin::signed(3); let broke = RuntimeOrigin::signed(4); let empty: DataOf = vec![].try_into().unwrap(); let non_empty: DataOf = vec![1].try_into().unwrap(); - assert_ok!(Registry::request_entity(almost_broke.clone(), 0, empty.clone(), false)); - assert_noop!( - Registry::request_entity(almost_broke, 0, non_empty.clone(), false), - pallet_balances::Error::::InsufficientBalance - ); assert_noop!( Registry::request_entity(broke.clone(), 0, empty, false), pallet_balances::Error::::InsufficientBalance diff --git a/frame/staking/Cargo.toml b/frame/staking/Cargo.toml index 603b5d652..31b129359 100644 --- a/frame/staking/Cargo.toml +++ b/frame/staking/Cargo.toml @@ -87,5 +87,6 @@ runtime-benchmarks = [ "sp-staking/runtime-benchmarks", "pallet-liberland-initializer/runtime-benchmarks", "liberland-traits/runtime-benchmarks", + "pallet-nfts/runtime-benchmarks", ] try-runtime = ["frame-support/try-runtime", "frame-election-provider-support/try-runtime"] diff --git a/frame/staking/src/benchmarking.rs b/frame/staking/src/benchmarking.rs index f05d98edf..32dd6178e 100644 --- a/frame/staking/src/benchmarking.rs +++ b/frame/staking/src/benchmarking.rs @@ -43,7 +43,6 @@ pub use frame_benchmarking::v1::{ account, benchmarks, impl_benchmark_test_suite, whitelist_account, whitelisted_caller, }; use frame_system::RawOrigin; -use liberland_traits::LLInitializer; const SEED: u32 = 0; const MAX_SPANS: u32 = 100; @@ -230,7 +229,6 @@ benchmarks! { let stash = create_funded_user::("stash", USER_SEED, 100); let reward_destination = RewardDestination::Staked; let amount = T::Currency::minimum_balance() * 10u32.into(); - T::LLInitializer::make_test_citizen(&stash); whitelist_account!(stash); }: _(RawOrigin::Signed(stash.clone()), amount, reward_destination) verify { diff --git a/frame/staking/src/mock.rs b/frame/staking/src/mock.rs index c858e5036..0e68ff522 100644 --- a/frame/staking/src/mock.rs +++ b/frame/staking/src/mock.rs @@ -41,7 +41,6 @@ use sp_runtime::{ Permill, }; use sp_staking::offence::{DisableStrategy, OffenceDetails, OnOffenceHandler}; -use liberland_traits::LLInitializer; pub const INIT_TIMESTAMP: u64 = 30_000; pub const BLOCK_TIME: u64 = 1000; @@ -658,7 +657,7 @@ impl ExtBuilder { slash_reward_fraction: Perbill::from_percent(10), min_nominator_bond: self.min_nominator_bond, min_validator_bond: self.min_validator_bond, - citizenship_required: true, + citizenship_required: false, ..Default::default() } .assimilate_storage(&mut storage); @@ -719,7 +718,6 @@ pub(crate) fn bond(who: AccountId, val: Balance) { } pub(crate) fn bond_validator(who: AccountId, val: Balance) { - LiberlandInitializer::make_test_citizen(&who); bond(who, val); assert_ok!(Staking::validate(RuntimeOrigin::signed(who), ValidatorPrefs::default())); assert_ok!(Session::set_keys( diff --git a/frame/staking/src/testing_utils.rs b/frame/staking/src/testing_utils.rs index 93c9e00f3..1c1ad97b5 100644 --- a/frame/staking/src/testing_utils.rs +++ b/frame/staking/src/testing_utils.rs @@ -36,8 +36,6 @@ use frame_support::{pallet_prelude::*, traits::Currency}; use sp_runtime::{traits::StaticLookup, Perbill}; use sp_std::prelude::*; -use liberland_traits::LLInitializer; - const SEED: u32 = 0; /// This function removes all validators and nominators from storage. @@ -111,7 +109,6 @@ pub fn create_unique_stash_controller( } // update bonded account to be unique controller >::insert(&stash, &controller); - T::LLInitializer::make_test_citizen(&controller); Ok((stash, controller)) } diff --git a/frame/staking/src/tests.rs b/frame/staking/src/tests.rs index c602adb99..ab7f99829 100644 --- a/frame/staking/src/tests.rs +++ b/frame/staking/src/tests.rs @@ -42,7 +42,6 @@ use sp_staking::{ }; use sp_std::prelude::*; use substrate_test_utils::assert_eq_uvec; -use liberland_traits::LLInitializer; #[test] fn set_staking_configs_works() { @@ -4775,8 +4774,6 @@ fn chill_other_works() { Balances::make_free_balance_be(&a, 100_000); Balances::make_free_balance_be(&b, 100_000); Balances::make_free_balance_be(&c, 100_000); - LiberlandInitializer::make_test_citizen(&a); - LiberlandInitializer::make_test_citizen(&b); // Nominator assert_ok!(Staking::bond( @@ -5849,3 +5846,17 @@ mod staking_interface { }) } } + +#[test] +fn citizenship_checks_works() { + ExtBuilder::default().build_and_execute(|| { + assert_ok!(Staking::set_citizenship_required(RuntimeOrigin::root(), true)); + bond(49, 100); + bond(50, 100); + assert_noop!( + Staking::validate(RuntimeOrigin::signed(49), ValidatorPrefs::default()), + pallet_llm::Error::::NonCitizen, + ); + assert_ok!(Staking::validate(RuntimeOrigin::signed(50), ValidatorPrefs::default())); + }) +} \ No newline at end of file From 9c9c75d25b31d178f771e682953a33f1293bece6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kacper=20=C5=BBuk?= Date: Mon, 26 Jun 2023 11:56:04 +0200 Subject: [PATCH 553/558] Add upstream migrations --- bin/node/runtime/src/lib.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/bin/node/runtime/src/lib.rs b/bin/node/runtime/src/lib.rs index a7d67edd2..20342c5a2 100644 --- a/bin/node/runtime/src/lib.rs +++ b/bin/node/runtime/src/lib.rs @@ -1775,6 +1775,8 @@ mod staking_v12 { // `OnRuntimeUpgrade`. type Migrations = ( pallet_contracts::Migration, + pallet_nfts::migration::v1::MigrateToV1, + pallet_offences::migration::v1::MigrateToV1, ); type EventRecord = frame_system::EventRecord< From 577d056af2ed75b8bfde9fc8bcedd414ea4b49eb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kacper=20=C5=BBuk?= Date: Mon, 26 Jun 2023 14:55:03 +0200 Subject: [PATCH 554/558] Benchmarking for federated bridge --- bin/node/runtime/src/lib.rs | 3 + frame/federated-bridge/src/benchmarking.rs | 260 ++++++++++++ frame/federated-bridge/src/lib.rs | 32 +- frame/federated-bridge/src/mock.rs | 1 + frame/federated-bridge/src/weights.rs | 459 +++++++++++++++++++++ frame/llm/src/impl_fungible.rs | 22 +- frame/llm/src/tests.rs | 25 ++ 7 files changed, 788 insertions(+), 14 deletions(-) create mode 100644 frame/federated-bridge/src/benchmarking.rs create mode 100644 frame/federated-bridge/src/weights.rs diff --git a/bin/node/runtime/src/lib.rs b/bin/node/runtime/src/lib.rs index 20342c5a2..0d7105e03 100644 --- a/bin/node/runtime/src/lib.rs +++ b/bin/node/runtime/src/lib.rs @@ -1586,6 +1586,7 @@ impl pallet_federated_bridge::Config for Runtime { type WithdrawalDelay = WithdrawalDelay; type WithdrawalRateLimit = LLDRateLimit; type MaxTotalLocked = LLDMaxTotalLocked; + type WeightInfo = (); } type EthLLMBridgeInstance = pallet_federated_bridge::Instance2; @@ -1600,6 +1601,7 @@ impl pallet_federated_bridge::Config for Runtime { type WithdrawalDelay = WithdrawalDelay; type WithdrawalRateLimit = LLMRateLimit; type MaxTotalLocked = LLMMaxTotalLocked; + type WeightInfo = (); } construct_runtime!( @@ -1835,6 +1837,7 @@ mod benches { [pallet_registry, CompanyRegistry] [pallet_office, IdentityOffice] [pallet_liberland_legislation, LiberlandLegislation] + [pallet_federated_bridge, EthLLDBridge] ); } diff --git a/frame/federated-bridge/src/benchmarking.rs b/frame/federated-bridge/src/benchmarking.rs new file mode 100644 index 000000000..b90affc5e --- /dev/null +++ b/frame/federated-bridge/src/benchmarking.rs @@ -0,0 +1,260 @@ +/* +Copyright © 2023 Liberland + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the “Software”), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. +THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +*/ + +#![cfg(feature = "runtime-benchmarks")] + +use super::*; +use crate::{ + Pallet as Bridge, + StatusOf, + IncomingReceiptStatus, + Fee, + VotesRequired, + BridgeState, + Admin, SuperAdmin, +}; +use sp_runtime::traits::Bounded; +use frame_benchmarking::{account, benchmarks_instance_pallet, impl_benchmark_test_suite}; +use frame_system::RawOrigin; +use sp_std::prelude::*; +use frame_support::traits::{Currency, fungible::{Mutate, Inspect}}; + +const SEED: u32 = 0; + +type BalanceOf = + <>::Currency as Currency<::AccountId>>::Balance; +type BalanceOfToken = + <>::Token as Inspect<::AccountId>>::Balance; + +fn activate, I: 'static>() { + let admin: T::AccountId = account("admin", 0, SEED); + Bridge::::set_admin(RawOrigin::Root.into(), admin.clone()).unwrap(); + Bridge::::set_state(RawOrigin::Signed(admin).into(), BridgeState::Active).unwrap(); +} + +fn receipt, I: 'static>(i: u8) -> (ReceiptId, IncomingReceipt>) { + ( + [i; 32], + IncomingReceipt { + eth_block_number: Default::default(), + substrate_recipient: account("recipient", 0, SEED), + amount: T::Token::minimum_balance(), + }, + ) +} + +fn add_watchers, I: 'static>(r: u32) -> Result<(), &'static str> { + let admin: T::AccountId = account("admin", 0, SEED); + Bridge::::set_super_admin(RawOrigin::Root.into(), admin.clone()).unwrap(); + let watchers: BoundedVec = vec![].try_into().unwrap(); + Watchers::::put(watchers); + + for i in 1..=r { + let watcher: T::AccountId = account("watcher", i, SEED); + Bridge::::add_watcher(RawOrigin::Signed(admin.clone()).into(), watcher).unwrap(); + } + + assert_eq!(Watchers::::get().len(), r as usize); + Ok(()) +} + +fn add_relays, I: 'static>(r: u32) -> Result<(), &'static str> { + let admin: T::AccountId = account("admin", 0, SEED); + Bridge::::set_super_admin(RawOrigin::Root.into(), admin.clone()).unwrap(); + let relays: BoundedVec = vec![].try_into().unwrap(); + Relays::::put(relays); + + for i in 1..=r { + let relay: T::AccountId = account("relay", i, SEED); + Bridge::::add_relay(RawOrigin::Signed(admin.clone()).into(), relay).unwrap(); + } + + assert_eq!(Relays::::get().len(), r as usize); + Ok(()) +} + +fn add_votes, I: 'static>(r: u32) -> Result<(), &'static str> { + activate::(); + let (receipt_id, receipt_data) = receipt::(0); + for i in 1..=r { + let relay: T::AccountId = account("relay", i, SEED); + Bridge::::vote_withdraw(RawOrigin::Signed(relay).into(), + receipt_id.clone(), + receipt_data.clone(), + ).unwrap(); + } + + assert_eq!(Voting::::get(receipt_id).len(), r as usize); + Ok(()) +} + + +benchmarks_instance_pallet! { + deposit { + activate::(); + let acc: T::AccountId = account("a", 1, SEED); + let amount = T::Token::minimum_balance(); + let eth_recipient: EthAddress = Default::default(); + T::Token::set_balance(&acc, BalanceOfToken::::max_value()/2u8.into()); + T::Currency::make_free_balance_be(&acc, BalanceOf::::max_value()/2u8.into()); + + let origin = RawOrigin::Signed(acc.clone()); + }: _(origin, amount.clone(), eth_recipient.clone()) + verify { + let ev: >::RuntimeEvent = + Event::::OutgoingReceipt { amount, eth_recipient }.into(); + frame_system::Pallet::::assert_last_event(ev.into()); + } + + vote_withdraw { + let r in 1 .. T::MaxRelays::get() => add_relays::(r)?; + activate::(); + + let admin: T::AccountId = account("admin", 0, SEED); + let origin = RawOrigin::Signed(admin.clone()); + Bridge::::set_votes_required(origin.into(), r).unwrap(); + + add_votes::(r-1).unwrap(); + + let relay: T::AccountId = account("relay", r, SEED); + let origin = RawOrigin::Signed(relay.clone()); + let (receipt_id, receipt_data) = receipt::(0); + }: _(origin, receipt_id.clone(), receipt_data) + verify { + assert!( + matches!( + StatusOf::::get(receipt_id), + IncomingReceiptStatus::Approved(_), + ), + ); + } + + withdraw { + let r in 1 .. T::MaxRelays::get() => add_relays::(r)?; + + let admin: T::AccountId = account("admin", 0, SEED); + let origin = RawOrigin::Signed(admin.clone()); + Bridge::::set_votes_required(origin.into(), r).unwrap(); + add_votes::(r).unwrap(); + + let user: T::AccountId = account("user", r, SEED); + let origin = RawOrigin::Signed(user.clone()); + let (receipt_id, _) = receipt::(0); + + frame_system::Pallet::::set_block_number( + T::WithdrawalDelay::get() + 10u8.into() + ); + + T::Token::set_balance(&Bridge::::account_id(), BalanceOfToken::::max_value()/2u8.into()); + T::Currency::make_free_balance_be(&user, BalanceOf::::max_value()/2u8.into()); + + }: _(origin, receipt_id.clone()) + verify { + assert!( + matches!( + StatusOf::::get(receipt_id), + IncomingReceiptStatus::Processed(_), + ), + ); + } + + set_fee { + let admin: T::AccountId = account("admin", 0, SEED); + Bridge::::set_admin(RawOrigin::Root.into(), admin.clone()).unwrap(); + let origin = RawOrigin::Signed(admin.clone()); + }: _(origin, 10u32.into()) + verify { + assert_eq!(Fee::::get(), 10u32.into()); + } + + set_votes_required { + let admin: T::AccountId = account("admin", 0, SEED); + Bridge::::set_super_admin(RawOrigin::Root.into(), admin.clone()).unwrap(); + let origin = RawOrigin::Signed(admin.clone()); + }: _(origin, 10u32) + verify { + assert_eq!(VotesRequired::::get(), 10u32); + } + + add_relay { + let r in 1 .. T::MaxRelays::get() - 1 => add_relays::(r)?; + let admin: T::AccountId = account("admin", 0, SEED); + let relay: T::AccountId = account("relay", r+1, SEED); + let origin = RawOrigin::Signed(admin.clone()); + }: _(origin, relay.clone()) + verify { + assert!(Relays::::get().contains(&relay)); + } + + remove_watcher { + let r in 1 .. T::MaxWatchers::get() => add_watchers::(r)?; + let admin: T::AccountId = account("admin", 0, SEED); + let watcher: T::AccountId = account("watcher", 1, SEED); + let origin = RawOrigin::Signed(admin.clone()); + assert!(Watchers::::get().contains(&watcher)); + }: _(origin, watcher.clone()) + verify { + assert!(!Watchers::::get().contains(&watcher)); + } + + remove_relay { + let r in 1 .. T::MaxRelays::get() => add_relays::(r)?; + let admin: T::AccountId = account("admin", 0, SEED); + let relay: T::AccountId = account("relay", 1, SEED); + let origin = RawOrigin::Signed(admin.clone()); + assert!(Relays::::get().contains(&relay)); + }: _(origin, relay.clone()) + verify { + assert!(!Relays::::get().contains(&relay)); + } + + add_watcher { + let r in 1 .. T::MaxWatchers::get() - 1 => add_watchers::(r)?; + let admin: T::AccountId = account("admin", 0, SEED); + let watcher: T::AccountId = account("watcher", r+1, SEED); + let origin = RawOrigin::Signed(admin.clone()); + }: _(origin, watcher.clone()) + verify { + assert!(Watchers::::get().contains(&watcher)); + } + + set_state { + let admin: T::AccountId = account("admin", 0, SEED); + Bridge::::set_admin(RawOrigin::Root.into(), admin.clone()).unwrap(); + let origin = RawOrigin::Signed(admin.clone()); + }: _(origin, BridgeState::Stopped) + verify { + assert_eq!(State::::get(), BridgeState::Stopped); + } + + emergency_stop { + let r in 1 .. T::MaxWatchers::get() => add_watchers::(r)?; + let watcher: T::AccountId = account("watcher", 1, SEED); + let origin = RawOrigin::Signed(watcher.clone()); + }: _(origin) + verify { + assert_eq!(State::::get(), BridgeState::Stopped); + } + + set_admin { + let admin: T::AccountId = account("admin", 0, SEED); + }: _(RawOrigin::Root, admin.clone()) + verify { + assert_eq!(Admin::::get(), Some(admin)); + } + + set_super_admin { + let admin: T::AccountId = account("admin", 0, SEED); + }: _(RawOrigin::Root, admin.clone()) + verify { + assert_eq!(SuperAdmin::::get(), Some(admin)); + } +} + +impl_benchmark_test_suite!(Bridge, crate::mock::new_test_ext(), crate::mock::Test,); diff --git a/frame/federated-bridge/src/lib.rs b/frame/federated-bridge/src/lib.rs index 1ddf29d08..1f2720cda 100644 --- a/frame/federated-bridge/src/lib.rs +++ b/frame/federated-bridge/src/lib.rs @@ -137,6 +137,9 @@ use serde::{Deserialize, Serialize}; mod mock; mod tests; +mod benchmarking; +pub mod weights; +pub use weights::WeightInfo; pub type EthAddress = [u8; 20]; pub type ReceiptId = [u8; 32]; @@ -253,6 +256,9 @@ pub mod pallet { /// Origin that's authorized to set Admin and SuperAdmin type ForceOrigin: EnsureOrigin; + + /// Weight information for extrinsics in this pallet. + type WeightInfo: WeightInfo; } #[pallet::error] @@ -438,7 +444,7 @@ pub mod pallet { /// /// Fails if bridge is stopped or caller has insufficient funds. #[pallet::call_index(0)] - #[pallet::weight(10_000)] + #[pallet::weight(T::WeightInfo::deposit())] pub fn deposit( origin: OriginFor, amount: BalanceOfToken, @@ -484,7 +490,7 @@ pub mod pallet { /// /// Deposits `Vote` event on successful vote. #[pallet::call_index(1)] - #[pallet::weight(10_000)] + #[pallet::weight(T::WeightInfo::vote_withdraw(T::MaxRelays::get()))] pub fn vote_withdraw( origin: OriginFor, receipt_id: ReceiptId, @@ -556,7 +562,7 @@ pub mod pallet { /// * receipt is unknown on substrate yet /// * caller has insufficient funds to cover the fee #[pallet::call_index(2)] - #[pallet::weight(10_000)] + #[pallet::weight(T::WeightInfo::withdraw(T::MaxRelays::get()))] pub fn withdraw(origin: OriginFor, receipt_id: ReceiptId) -> DispatchResult { // FIXME do we want rate-limiting on substrate side? let caller = ensure_signed(origin)?; @@ -605,7 +611,7 @@ pub mod pallet { /// /// Should be set high enough to cover running costs for all relays. #[pallet::call_index(3)] - #[pallet::weight(10_000)] + #[pallet::weight(T::WeightInfo::set_fee())] pub fn set_fee(origin: OriginFor, amount: BalanceOf) -> DispatchResult { Self::ensure_admin(origin)?; Fee::::set(amount); @@ -617,7 +623,7 @@ pub mod pallet { /// /// Can be called by SuperAdmin. #[pallet::call_index(4)] - #[pallet::weight(10_000)] + #[pallet::weight(T::WeightInfo::set_votes_required())] pub fn set_votes_required(origin: OriginFor, votes_required: u32) -> DispatchResult { Self::ensure_super_admin(origin)?; VotesRequired::::set(votes_required); @@ -632,7 +638,7 @@ pub mod pallet { /// * relay already exists /// * there's already `T::MaxRelays` relays #[pallet::call_index(5)] - #[pallet::weight(10_000)] + #[pallet::weight(T::WeightInfo::add_relay(T::MaxRelays::get()))] pub fn add_relay(origin: OriginFor, relay: T::AccountId) -> DispatchResult { Self::ensure_super_admin(origin)?; Relays::::try_mutate(|relays| -> Result<(), DispatchError> { @@ -649,7 +655,7 @@ pub mod pallet { /// /// Will fail if watcher doesn't exists. #[pallet::call_index(6)] - #[pallet::weight(10_000)] + #[pallet::weight(T::WeightInfo::remove_watcher(T::MaxWatchers::get()))] pub fn remove_watcher(origin: OriginFor, watcher: T::AccountId) -> DispatchResult { Self::ensure_super_admin(origin)?; Watchers::::try_mutate(|watchers| -> Result<(), DispatchError> { @@ -669,7 +675,7 @@ pub mod pallet { /// /// Will fail if relay doesn't exists. #[pallet::call_index(7)] - #[pallet::weight(10_000)] + #[pallet::weight(T::WeightInfo::remove_relay(T::MaxRelays::get()))] pub fn remove_relay(origin: OriginFor, relay: T::AccountId) -> DispatchResult { Self::ensure_admin(origin)?; Relays::::try_mutate(|relays| -> Result<(), DispatchError> { @@ -689,7 +695,7 @@ pub mod pallet { /// * watcher already exists /// * there's already `T::MaxWatchers` relays #[pallet::call_index(8)] - #[pallet::weight(10_000)] + #[pallet::weight(T::WeightInfo::add_watcher(T::MaxWatchers::get()))] pub fn add_watcher(origin: OriginFor, watcher: T::AccountId) -> DispatchResult { Self::ensure_admin(origin)?; Watchers::::try_mutate(|watchers| -> Result<(), DispatchError> { @@ -706,7 +712,7 @@ pub mod pallet { /// /// Deposits `StateChanged` event. #[pallet::call_index(9)] - #[pallet::weight(10_000)] + #[pallet::weight(T::WeightInfo::set_state())] pub fn set_state(origin: OriginFor, state: BridgeState) -> DispatchResult { Self::ensure_admin(origin)?; Self::do_set_state(state); @@ -720,7 +726,7 @@ pub mod pallet { /// /// Deposits `EmergencyStop` and `StateChanged` events. #[pallet::call_index(10)] - #[pallet::weight(10_000)] + #[pallet::weight(T::WeightInfo::emergency_stop(T::MaxWatchers::get()))] pub fn emergency_stop(origin: OriginFor) -> DispatchResult { let caller = ensure_signed(origin)?; let watchers = Watchers::::get(); @@ -742,7 +748,7 @@ pub mod pallet { /// /// Can be called by ForceOrigin, SuperAdmin and Admin #[pallet::call_index(11)] - #[pallet::weight(10_000)] + #[pallet::weight(T::WeightInfo::set_admin())] pub fn set_admin(origin: OriginFor, admin: T::AccountId) -> DispatchResult { if let Err(_) = T::ForceOrigin::ensure_origin(origin.clone()) { Self::ensure_admin(origin)?; @@ -762,7 +768,7 @@ pub mod pallet { /// /// Can be called by ForceOrigin and SuperAdmin #[pallet::call_index(12)] - #[pallet::weight(10_000)] + #[pallet::weight(T::WeightInfo::set_super_admin())] pub fn set_super_admin(origin: OriginFor, super_admin: T::AccountId) -> DispatchResult { if let Err(_) = T::ForceOrigin::ensure_origin(origin.clone()) { Self::ensure_super_admin(origin)?; diff --git a/frame/federated-bridge/src/mock.rs b/frame/federated-bridge/src/mock.rs index ba2911c81..5f66f8159 100644 --- a/frame/federated-bridge/src/mock.rs +++ b/frame/federated-bridge/src/mock.rs @@ -94,6 +94,7 @@ impl pallet_federated_bridge::Config for Test { type WithdrawalRateLimit = RateLimit; type ForceOrigin = EnsureRoot; type MaxTotalLocked = ConstU64<10000>; + type WeightInfo = (); } pub fn new_test_ext() -> sp_io::TestExternalities { diff --git a/frame/federated-bridge/src/weights.rs b/frame/federated-bridge/src/weights.rs new file mode 100644 index 000000000..d1ac37946 --- /dev/null +++ b/frame/federated-bridge/src/weights.rs @@ -0,0 +1,459 @@ + +//! Autogenerated weights for pallet_federated_bridge +//! +//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev +//! DATE: 2023-06-26, STEPS: `20`, REPEAT: `10`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! WORST CASE MAP SIZE: `1000000` +//! HOSTNAME: `kacper-HP-ProBook-445-G7`, CPU: `AMD Ryzen 7 4700U with Radeon Graphics` +//! EXECUTION: Some(Wasm), WASM-EXECUTION: Compiled, CHAIN: None, DB CACHE: 1024 + +// Executed Command: +// target/release/substrate +// benchmark +// pallet +// --pallet=pallet_federated_bridge +// --execution=wasm +// --wasm-execution=compiled +// --steps=20 +// --repeat=10 +// --output=frame/federated-bridge/src/weights.rs +// --extrinsic=* +// --template=./.maintain/frame-weight-template.hbs + +#![cfg_attr(rustfmt, rustfmt_skip)] +#![allow(unused_parens)] +#![allow(unused_imports)] +#![allow(missing_docs)] + +use frame_support::{traits::Get, weights::{Weight, constants::RocksDbWeight}}; +use core::marker::PhantomData; + +/// Weight functions needed for pallet_federated_bridge. +pub trait WeightInfo { + fn deposit() -> Weight; + fn vote_withdraw(r: u32, ) -> Weight; + fn withdraw(r: u32, ) -> Weight; + fn set_fee() -> Weight; + fn set_votes_required() -> Weight; + fn add_relay(r: u32, ) -> Weight; + fn remove_watcher(r: u32, ) -> Weight; + fn remove_relay(r: u32, ) -> Weight; + fn add_watcher(r: u32, ) -> Weight; + fn set_state() -> Weight; + fn emergency_stop(r: u32, ) -> Weight; + fn set_admin() -> Weight; + fn set_super_admin() -> Weight; +} + +/// Weights for pallet_federated_bridge using the Substrate node and recommended hardware. +pub struct SubstrateWeight(PhantomData); +impl WeightInfo for SubstrateWeight { + /// Storage: EthLLDBridge State (r:1 w:0) + /// Proof: EthLLDBridge State (max_values: Some(1), max_size: Some(1), added: 496, mode: MaxEncodedLen) + /// Storage: System Account (r:2 w:2) + /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) + fn deposit() -> Weight { + // Proof Size summary in bytes: + // Measured: `407` + // Estimated: `6196` + // Minimum execution time: 106_483_000 picoseconds. + Weight::from_parts(110_711_000, 6196) + .saturating_add(T::DbWeight::get().reads(3_u64)) + .saturating_add(T::DbWeight::get().writes(2_u64)) + } + /// Storage: EthLLDBridge Relays (r:1 w:0) + /// Proof: EthLLDBridge Relays (max_values: Some(1), max_size: Some(641), added: 1136, mode: MaxEncodedLen) + /// Storage: EthLLDBridge State (r:1 w:0) + /// Proof: EthLLDBridge State (max_values: Some(1), max_size: Some(1), added: 496, mode: MaxEncodedLen) + /// Storage: EthLLDBridge StatusOf (r:1 w:1) + /// Proof: EthLLDBridge StatusOf (max_values: None, max_size: Some(53), added: 2528, mode: MaxEncodedLen) + /// Storage: EthLLDBridge IncomingReceipts (r:1 w:0) + /// Proof: EthLLDBridge IncomingReceipts (max_values: None, max_size: Some(104), added: 2579, mode: MaxEncodedLen) + /// Storage: EthLLDBridge Voting (r:1 w:1) + /// Proof: EthLLDBridge Voting (max_values: None, max_size: Some(689), added: 3164, mode: MaxEncodedLen) + /// Storage: EthLLDBridge VotesRequired (r:1 w:0) + /// Proof: EthLLDBridge VotesRequired (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) + /// The range of component `r` is `[1, 20]`. + fn vote_withdraw(r: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `410 + r * (64 ±0)` + // Estimated: `4154` + // Minimum execution time: 54_715_000 picoseconds. + Weight::from_parts(60_668_013, 4154) + // Standard Error: 76_388 + .saturating_add(Weight::from_parts(165_777, 0).saturating_mul(r.into())) + .saturating_add(T::DbWeight::get().reads(6_u64)) + .saturating_add(T::DbWeight::get().writes(2_u64)) + } + /// Storage: EthLLDBridge Voting (r:1 w:0) + /// Proof: EthLLDBridge Voting (max_values: None, max_size: Some(689), added: 3164, mode: MaxEncodedLen) + /// Storage: EthLLDBridge State (r:1 w:0) + /// Proof: EthLLDBridge State (max_values: Some(1), max_size: Some(1), added: 496, mode: MaxEncodedLen) + /// Storage: EthLLDBridge StatusOf (r:1 w:1) + /// Proof: EthLLDBridge StatusOf (max_values: None, max_size: Some(53), added: 2528, mode: MaxEncodedLen) + /// Storage: EthLLDBridge IncomingReceipts (r:1 w:0) + /// Proof: EthLLDBridge IncomingReceipts (max_values: None, max_size: Some(104), added: 2579, mode: MaxEncodedLen) + /// Storage: EthLLDBridge WithdrawalCounter (r:1 w:1) + /// Proof: EthLLDBridge WithdrawalCounter (max_values: Some(1), max_size: Some(20), added: 515, mode: MaxEncodedLen) + /// Storage: EthLLDBridge Fee (r:1 w:0) + /// Proof: EthLLDBridge Fee (max_values: Some(1), max_size: Some(16), added: 511, mode: MaxEncodedLen) + /// Storage: System Account (r:2 w:2) + /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) + /// The range of component `r` is `[1, 20]`. + fn withdraw(_r: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `636 + r * (32 ±0)` + // Estimated: `6196` + // Minimum execution time: 117_755_000 picoseconds. + Weight::from_parts(140_113_979, 6196) + .saturating_add(T::DbWeight::get().reads(8_u64)) + .saturating_add(T::DbWeight::get().writes(4_u64)) + } + /// Storage: EthLLDBridge Admin (r:1 w:0) + /// Proof: EthLLDBridge Admin (max_values: Some(1), max_size: Some(32), added: 527, mode: MaxEncodedLen) + /// Storage: EthLLDBridge Fee (r:0 w:1) + /// Proof: EthLLDBridge Fee (max_values: Some(1), max_size: Some(16), added: 511, mode: MaxEncodedLen) + fn set_fee() -> Weight { + // Proof Size summary in bytes: + // Measured: `228` + // Estimated: `1517` + // Minimum execution time: 11_371_000 picoseconds. + Weight::from_parts(11_783_000, 1517) + .saturating_add(T::DbWeight::get().reads(1_u64)) + .saturating_add(T::DbWeight::get().writes(1_u64)) + } + /// Storage: EthLLDBridge SuperAdmin (r:1 w:0) + /// Proof: EthLLDBridge SuperAdmin (max_values: Some(1), max_size: Some(32), added: 527, mode: MaxEncodedLen) + /// Storage: EthLLDBridge VotesRequired (r:0 w:1) + /// Proof: EthLLDBridge VotesRequired (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) + fn set_votes_required() -> Weight { + // Proof Size summary in bytes: + // Measured: `251` + // Estimated: `1517` + // Minimum execution time: 12_624_000 picoseconds. + Weight::from_parts(12_935_000, 1517) + .saturating_add(T::DbWeight::get().reads(1_u64)) + .saturating_add(T::DbWeight::get().writes(1_u64)) + } + /// Storage: EthLLDBridge SuperAdmin (r:1 w:0) + /// Proof: EthLLDBridge SuperAdmin (max_values: Some(1), max_size: Some(32), added: 527, mode: MaxEncodedLen) + /// Storage: EthLLDBridge Relays (r:1 w:1) + /// Proof: EthLLDBridge Relays (max_values: Some(1), max_size: Some(641), added: 1136, mode: MaxEncodedLen) + /// The range of component `r` is `[1, 19]`. + fn add_relay(r: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `255 + r * (32 ±0)` + // Estimated: `2126` + // Minimum execution time: 16_151_000 picoseconds. + Weight::from_parts(17_046_853, 2126) + // Standard Error: 9_588 + .saturating_add(Weight::from_parts(73_148, 0).saturating_mul(r.into())) + .saturating_add(T::DbWeight::get().reads(2_u64)) + .saturating_add(T::DbWeight::get().writes(1_u64)) + } + /// Storage: EthLLDBridge SuperAdmin (r:1 w:0) + /// Proof: EthLLDBridge SuperAdmin (max_values: Some(1), max_size: Some(32), added: 527, mode: MaxEncodedLen) + /// Storage: EthLLDBridge Watchers (r:1 w:1) + /// Proof: EthLLDBridge Watchers (max_values: Some(1), max_size: Some(641), added: 1136, mode: MaxEncodedLen) + /// The range of component `r` is `[1, 20]`. + fn remove_watcher(r: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `255 + r * (32 ±0)` + // Estimated: `2126` + // Minimum execution time: 15_970_000 picoseconds. + Weight::from_parts(16_896_792, 2126) + // Standard Error: 10_227 + .saturating_add(Weight::from_parts(91_991, 0).saturating_mul(r.into())) + .saturating_add(T::DbWeight::get().reads(2_u64)) + .saturating_add(T::DbWeight::get().writes(1_u64)) + } + /// Storage: EthLLDBridge Admin (r:1 w:0) + /// Proof: EthLLDBridge Admin (max_values: Some(1), max_size: Some(32), added: 527, mode: MaxEncodedLen) + /// Storage: EthLLDBridge SuperAdmin (r:1 w:0) + /// Proof: EthLLDBridge SuperAdmin (max_values: Some(1), max_size: Some(32), added: 527, mode: MaxEncodedLen) + /// Storage: EthLLDBridge Relays (r:1 w:1) + /// Proof: EthLLDBridge Relays (max_values: Some(1), max_size: Some(641), added: 1136, mode: MaxEncodedLen) + /// The range of component `r` is `[1, 20]`. + fn remove_relay(r: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `274 + r * (32 ±0)` + // Estimated: `2126` + // Minimum execution time: 18_555_000 picoseconds. + Weight::from_parts(19_899_157, 2126) + // Standard Error: 11_477 + .saturating_add(Weight::from_parts(72_957, 0).saturating_mul(r.into())) + .saturating_add(T::DbWeight::get().reads(3_u64)) + .saturating_add(T::DbWeight::get().writes(1_u64)) + } + /// Storage: EthLLDBridge Admin (r:1 w:0) + /// Proof: EthLLDBridge Admin (max_values: Some(1), max_size: Some(32), added: 527, mode: MaxEncodedLen) + /// Storage: EthLLDBridge SuperAdmin (r:1 w:0) + /// Proof: EthLLDBridge SuperAdmin (max_values: Some(1), max_size: Some(32), added: 527, mode: MaxEncodedLen) + /// Storage: EthLLDBridge Watchers (r:1 w:1) + /// Proof: EthLLDBridge Watchers (max_values: Some(1), max_size: Some(641), added: 1136, mode: MaxEncodedLen) + /// The range of component `r` is `[1, 19]`. + fn add_watcher(_r: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `274 + r * (32 ±0)` + // Estimated: `2126` + // Minimum execution time: 18_715_000 picoseconds. + Weight::from_parts(21_015_204, 2126) + .saturating_add(T::DbWeight::get().reads(3_u64)) + .saturating_add(T::DbWeight::get().writes(1_u64)) + } + /// Storage: EthLLDBridge Admin (r:1 w:0) + /// Proof: EthLLDBridge Admin (max_values: Some(1), max_size: Some(32), added: 527, mode: MaxEncodedLen) + /// Storage: EthLLDBridge State (r:0 w:1) + /// Proof: EthLLDBridge State (max_values: Some(1), max_size: Some(1), added: 496, mode: MaxEncodedLen) + fn set_state() -> Weight { + // Proof Size summary in bytes: + // Measured: `228` + // Estimated: `1517` + // Minimum execution time: 18_866_000 picoseconds. + Weight::from_parts(19_257_000, 1517) + .saturating_add(T::DbWeight::get().reads(1_u64)) + .saturating_add(T::DbWeight::get().writes(1_u64)) + } + /// Storage: EthLLDBridge Watchers (r:1 w:0) + /// Proof: EthLLDBridge Watchers (max_values: Some(1), max_size: Some(641), added: 1136, mode: MaxEncodedLen) + /// Storage: EthLLDBridge State (r:0 w:1) + /// Proof: EthLLDBridge State (max_values: Some(1), max_size: Some(1), added: 496, mode: MaxEncodedLen) + /// The range of component `r` is `[1, 20]`. + fn emergency_stop(r: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `213 + r * (32 ±0)` + // Estimated: `2126` + // Minimum execution time: 25_068_000 picoseconds. + Weight::from_parts(25_774_438, 2126) + // Standard Error: 10_069 + .saturating_add(Weight::from_parts(9_392, 0).saturating_mul(r.into())) + .saturating_add(T::DbWeight::get().reads(1_u64)) + .saturating_add(T::DbWeight::get().writes(1_u64)) + } + /// Storage: EthLLDBridge Admin (r:0 w:1) + /// Proof: EthLLDBridge Admin (max_values: Some(1), max_size: Some(32), added: 527, mode: MaxEncodedLen) + fn set_admin() -> Weight { + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 5_541_000 picoseconds. + Weight::from_parts(5_832_000, 0) + .saturating_add(T::DbWeight::get().writes(1_u64)) + } + /// Storage: EthLLDBridge SuperAdmin (r:0 w:1) + /// Proof: EthLLDBridge SuperAdmin (max_values: Some(1), max_size: Some(32), added: 527, mode: MaxEncodedLen) + fn set_super_admin() -> Weight { + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 5_560_000 picoseconds. + Weight::from_parts(5_841_000, 0) + .saturating_add(T::DbWeight::get().writes(1_u64)) + } +} + +// For backwards compatibility and tests +impl WeightInfo for () { + /// Storage: EthLLDBridge State (r:1 w:0) + /// Proof: EthLLDBridge State (max_values: Some(1), max_size: Some(1), added: 496, mode: MaxEncodedLen) + /// Storage: System Account (r:2 w:2) + /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) + fn deposit() -> Weight { + // Proof Size summary in bytes: + // Measured: `407` + // Estimated: `6196` + // Minimum execution time: 106_483_000 picoseconds. + Weight::from_parts(110_711_000, 6196) + .saturating_add(RocksDbWeight::get().reads(3_u64)) + .saturating_add(RocksDbWeight::get().writes(2_u64)) + } + /// Storage: EthLLDBridge Relays (r:1 w:0) + /// Proof: EthLLDBridge Relays (max_values: Some(1), max_size: Some(641), added: 1136, mode: MaxEncodedLen) + /// Storage: EthLLDBridge State (r:1 w:0) + /// Proof: EthLLDBridge State (max_values: Some(1), max_size: Some(1), added: 496, mode: MaxEncodedLen) + /// Storage: EthLLDBridge StatusOf (r:1 w:1) + /// Proof: EthLLDBridge StatusOf (max_values: None, max_size: Some(53), added: 2528, mode: MaxEncodedLen) + /// Storage: EthLLDBridge IncomingReceipts (r:1 w:0) + /// Proof: EthLLDBridge IncomingReceipts (max_values: None, max_size: Some(104), added: 2579, mode: MaxEncodedLen) + /// Storage: EthLLDBridge Voting (r:1 w:1) + /// Proof: EthLLDBridge Voting (max_values: None, max_size: Some(689), added: 3164, mode: MaxEncodedLen) + /// Storage: EthLLDBridge VotesRequired (r:1 w:0) + /// Proof: EthLLDBridge VotesRequired (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) + /// The range of component `r` is `[1, 20]`. + fn vote_withdraw(r: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `410 + r * (64 ±0)` + // Estimated: `4154` + // Minimum execution time: 54_715_000 picoseconds. + Weight::from_parts(60_668_013, 4154) + // Standard Error: 76_388 + .saturating_add(Weight::from_parts(165_777, 0).saturating_mul(r.into())) + .saturating_add(RocksDbWeight::get().reads(6_u64)) + .saturating_add(RocksDbWeight::get().writes(2_u64)) + } + /// Storage: EthLLDBridge Voting (r:1 w:0) + /// Proof: EthLLDBridge Voting (max_values: None, max_size: Some(689), added: 3164, mode: MaxEncodedLen) + /// Storage: EthLLDBridge State (r:1 w:0) + /// Proof: EthLLDBridge State (max_values: Some(1), max_size: Some(1), added: 496, mode: MaxEncodedLen) + /// Storage: EthLLDBridge StatusOf (r:1 w:1) + /// Proof: EthLLDBridge StatusOf (max_values: None, max_size: Some(53), added: 2528, mode: MaxEncodedLen) + /// Storage: EthLLDBridge IncomingReceipts (r:1 w:0) + /// Proof: EthLLDBridge IncomingReceipts (max_values: None, max_size: Some(104), added: 2579, mode: MaxEncodedLen) + /// Storage: EthLLDBridge WithdrawalCounter (r:1 w:1) + /// Proof: EthLLDBridge WithdrawalCounter (max_values: Some(1), max_size: Some(20), added: 515, mode: MaxEncodedLen) + /// Storage: EthLLDBridge Fee (r:1 w:0) + /// Proof: EthLLDBridge Fee (max_values: Some(1), max_size: Some(16), added: 511, mode: MaxEncodedLen) + /// Storage: System Account (r:2 w:2) + /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) + /// The range of component `r` is `[1, 20]`. + fn withdraw(_r: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `636 + r * (32 ±0)` + // Estimated: `6196` + // Minimum execution time: 117_755_000 picoseconds. + Weight::from_parts(140_113_979, 6196) + .saturating_add(RocksDbWeight::get().reads(8_u64)) + .saturating_add(RocksDbWeight::get().writes(4_u64)) + } + /// Storage: EthLLDBridge Admin (r:1 w:0) + /// Proof: EthLLDBridge Admin (max_values: Some(1), max_size: Some(32), added: 527, mode: MaxEncodedLen) + /// Storage: EthLLDBridge Fee (r:0 w:1) + /// Proof: EthLLDBridge Fee (max_values: Some(1), max_size: Some(16), added: 511, mode: MaxEncodedLen) + fn set_fee() -> Weight { + // Proof Size summary in bytes: + // Measured: `228` + // Estimated: `1517` + // Minimum execution time: 11_371_000 picoseconds. + Weight::from_parts(11_783_000, 1517) + .saturating_add(RocksDbWeight::get().reads(1_u64)) + .saturating_add(RocksDbWeight::get().writes(1_u64)) + } + /// Storage: EthLLDBridge SuperAdmin (r:1 w:0) + /// Proof: EthLLDBridge SuperAdmin (max_values: Some(1), max_size: Some(32), added: 527, mode: MaxEncodedLen) + /// Storage: EthLLDBridge VotesRequired (r:0 w:1) + /// Proof: EthLLDBridge VotesRequired (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) + fn set_votes_required() -> Weight { + // Proof Size summary in bytes: + // Measured: `251` + // Estimated: `1517` + // Minimum execution time: 12_624_000 picoseconds. + Weight::from_parts(12_935_000, 1517) + .saturating_add(RocksDbWeight::get().reads(1_u64)) + .saturating_add(RocksDbWeight::get().writes(1_u64)) + } + /// Storage: EthLLDBridge SuperAdmin (r:1 w:0) + /// Proof: EthLLDBridge SuperAdmin (max_values: Some(1), max_size: Some(32), added: 527, mode: MaxEncodedLen) + /// Storage: EthLLDBridge Relays (r:1 w:1) + /// Proof: EthLLDBridge Relays (max_values: Some(1), max_size: Some(641), added: 1136, mode: MaxEncodedLen) + /// The range of component `r` is `[1, 19]`. + fn add_relay(r: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `255 + r * (32 ±0)` + // Estimated: `2126` + // Minimum execution time: 16_151_000 picoseconds. + Weight::from_parts(17_046_853, 2126) + // Standard Error: 9_588 + .saturating_add(Weight::from_parts(73_148, 0).saturating_mul(r.into())) + .saturating_add(RocksDbWeight::get().reads(2_u64)) + .saturating_add(RocksDbWeight::get().writes(1_u64)) + } + /// Storage: EthLLDBridge SuperAdmin (r:1 w:0) + /// Proof: EthLLDBridge SuperAdmin (max_values: Some(1), max_size: Some(32), added: 527, mode: MaxEncodedLen) + /// Storage: EthLLDBridge Watchers (r:1 w:1) + /// Proof: EthLLDBridge Watchers (max_values: Some(1), max_size: Some(641), added: 1136, mode: MaxEncodedLen) + /// The range of component `r` is `[1, 20]`. + fn remove_watcher(r: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `255 + r * (32 ±0)` + // Estimated: `2126` + // Minimum execution time: 15_970_000 picoseconds. + Weight::from_parts(16_896_792, 2126) + // Standard Error: 10_227 + .saturating_add(Weight::from_parts(91_991, 0).saturating_mul(r.into())) + .saturating_add(RocksDbWeight::get().reads(2_u64)) + .saturating_add(RocksDbWeight::get().writes(1_u64)) + } + /// Storage: EthLLDBridge Admin (r:1 w:0) + /// Proof: EthLLDBridge Admin (max_values: Some(1), max_size: Some(32), added: 527, mode: MaxEncodedLen) + /// Storage: EthLLDBridge SuperAdmin (r:1 w:0) + /// Proof: EthLLDBridge SuperAdmin (max_values: Some(1), max_size: Some(32), added: 527, mode: MaxEncodedLen) + /// Storage: EthLLDBridge Relays (r:1 w:1) + /// Proof: EthLLDBridge Relays (max_values: Some(1), max_size: Some(641), added: 1136, mode: MaxEncodedLen) + /// The range of component `r` is `[1, 20]`. + fn remove_relay(r: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `274 + r * (32 ±0)` + // Estimated: `2126` + // Minimum execution time: 18_555_000 picoseconds. + Weight::from_parts(19_899_157, 2126) + // Standard Error: 11_477 + .saturating_add(Weight::from_parts(72_957, 0).saturating_mul(r.into())) + .saturating_add(RocksDbWeight::get().reads(3_u64)) + .saturating_add(RocksDbWeight::get().writes(1_u64)) + } + /// Storage: EthLLDBridge Admin (r:1 w:0) + /// Proof: EthLLDBridge Admin (max_values: Some(1), max_size: Some(32), added: 527, mode: MaxEncodedLen) + /// Storage: EthLLDBridge SuperAdmin (r:1 w:0) + /// Proof: EthLLDBridge SuperAdmin (max_values: Some(1), max_size: Some(32), added: 527, mode: MaxEncodedLen) + /// Storage: EthLLDBridge Watchers (r:1 w:1) + /// Proof: EthLLDBridge Watchers (max_values: Some(1), max_size: Some(641), added: 1136, mode: MaxEncodedLen) + /// The range of component `r` is `[1, 19]`. + fn add_watcher(_r: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `274 + r * (32 ±0)` + // Estimated: `2126` + // Minimum execution time: 18_715_000 picoseconds. + Weight::from_parts(21_015_204, 2126) + .saturating_add(RocksDbWeight::get().reads(3_u64)) + .saturating_add(RocksDbWeight::get().writes(1_u64)) + } + /// Storage: EthLLDBridge Admin (r:1 w:0) + /// Proof: EthLLDBridge Admin (max_values: Some(1), max_size: Some(32), added: 527, mode: MaxEncodedLen) + /// Storage: EthLLDBridge State (r:0 w:1) + /// Proof: EthLLDBridge State (max_values: Some(1), max_size: Some(1), added: 496, mode: MaxEncodedLen) + fn set_state() -> Weight { + // Proof Size summary in bytes: + // Measured: `228` + // Estimated: `1517` + // Minimum execution time: 18_866_000 picoseconds. + Weight::from_parts(19_257_000, 1517) + .saturating_add(RocksDbWeight::get().reads(1_u64)) + .saturating_add(RocksDbWeight::get().writes(1_u64)) + } + /// Storage: EthLLDBridge Watchers (r:1 w:0) + /// Proof: EthLLDBridge Watchers (max_values: Some(1), max_size: Some(641), added: 1136, mode: MaxEncodedLen) + /// Storage: EthLLDBridge State (r:0 w:1) + /// Proof: EthLLDBridge State (max_values: Some(1), max_size: Some(1), added: 496, mode: MaxEncodedLen) + /// The range of component `r` is `[1, 20]`. + fn emergency_stop(r: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `213 + r * (32 ±0)` + // Estimated: `2126` + // Minimum execution time: 25_068_000 picoseconds. + Weight::from_parts(25_774_438, 2126) + // Standard Error: 10_069 + .saturating_add(Weight::from_parts(9_392, 0).saturating_mul(r.into())) + .saturating_add(RocksDbWeight::get().reads(1_u64)) + .saturating_add(RocksDbWeight::get().writes(1_u64)) + } + /// Storage: EthLLDBridge Admin (r:0 w:1) + /// Proof: EthLLDBridge Admin (max_values: Some(1), max_size: Some(32), added: 527, mode: MaxEncodedLen) + fn set_admin() -> Weight { + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 5_541_000 picoseconds. + Weight::from_parts(5_832_000, 0) + .saturating_add(RocksDbWeight::get().writes(1_u64)) + } + /// Storage: EthLLDBridge SuperAdmin (r:0 w:1) + /// Proof: EthLLDBridge SuperAdmin (max_values: Some(1), max_size: Some(32), added: 527, mode: MaxEncodedLen) + fn set_super_admin() -> Weight { + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 5_560_000 picoseconds. + Weight::from_parts(5_841_000, 0) + .saturating_add(RocksDbWeight::get().writes(1_u64)) + } +} diff --git a/frame/llm/src/impl_fungible.rs b/frame/llm/src/impl_fungible.rs index 122f01208..139e5aab0 100644 --- a/frame/llm/src/impl_fungible.rs +++ b/frame/llm/src/impl_fungible.rs @@ -6,7 +6,7 @@ use frame_support::{ fungibles::{ Dust as FungiblesDust, Inspect as FungiblesInspect, Unbalanced as FungiblesUnbalanced, }, - DepositConsequence, Fortitude, Preservation, Provenance, WithdrawConsequence, + DepositConsequence, Fortitude, Preservation, Provenance, WithdrawConsequence, Precision, }, }; @@ -79,6 +79,26 @@ impl Unbalanced for Pallet { let asset_id = Self::llm_id().into(); Assets::::set_total_issuance(asset_id, amount) } + + fn decrease_balance( + who: &T::AccountId, + amount: Self::Balance, + precision: Precision, + preservation: Preservation, + fortitude: Fortitude, + ) -> Result { + let asset_id = Self::llm_id().into(); + Assets::::decrease_balance(asset_id, who, amount, precision, preservation, fortitude) + } + + fn increase_balance( + who: &T::AccountId, + amount: Self::Balance, + precision: Precision, + ) -> Result { + let asset_id = Self::llm_id().into(); + Assets::::increase_balance(asset_id, who, amount, precision) + } } impl Mutate for Pallet {} diff --git a/frame/llm/src/tests.rs b/frame/llm/src/tests.rs index 14787c7de..fbbcf76d4 100644 --- a/frame/llm/src/tests.rs +++ b/frame/llm/src/tests.rs @@ -7,6 +7,7 @@ use frame_support::{assert_noop, assert_ok, error::BadOrigin, traits::OnInitiali use liberland_traits::{CitizenshipChecker, LLM as LLMTrait}; use pallet_identity::{Data, IdentityInfo}; use sp_runtime::traits::{BlakeTwo256, Hash}; +use frame_support::traits::tokens::{Preservation, fungible::{Inspect, Mutate}}; type AssetsError = pallet_assets::Error; @@ -533,3 +534,27 @@ fn transfer_trait_works() { assert_eq!(LLM::balance(2), 12000); }); } + +#[test] +fn fungible_traits_work() { + new_test_ext().execute_with(|| { + assert_eq!( + ::AccountId>>::balance(&99), + ::Balance>>::into(0u8) + ); + ::AccountId>>::set_balance(&99, 100u8.into()); + assert_eq!( + ::AccountId>>::balance(&99), + ::Balance>>::into(100u8) + ); + assert_ok!(::AccountId>>::transfer(&99, &999, 100u8.into(), Preservation::Expendable)); + assert_eq!( + ::AccountId>>::balance(&99), + ::Balance>>::into(0u8) + ); + assert_eq!( + ::AccountId>>::balance(&999), + ::Balance>>::into(100u8) + ); + }); +} \ No newline at end of file From 81eb32fe9a778b23d0c0d4a2a97a21f8dd8969f2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kacper=20=C5=BBuk?= Date: Mon, 26 Jun 2023 15:27:13 +0200 Subject: [PATCH 555/558] Add missing benchmark in staking pallet --- frame/staking/src/benchmarking.rs | 6 ++++++ frame/staking/src/pallet/mod.rs | 2 +- frame/staking/src/weights.rs | 25 +++++++++++++++++++++++++ 3 files changed, 32 insertions(+), 1 deletion(-) diff --git a/frame/staking/src/benchmarking.rs b/frame/staking/src/benchmarking.rs index 32dd6178e..9c890f7d4 100644 --- a/frame/staking/src/benchmarking.rs +++ b/frame/staking/src/benchmarking.rs @@ -942,6 +942,12 @@ benchmarks! { assert_eq!(MinCommission::::get(), Perbill::from_percent(100)); } + set_citizenship_required { + }: _(RawOrigin::Root, true) + verify { + assert_eq!(CitizenshipRequired::::get(), true); + } + impl_benchmark_test_suite!( Staking, crate::mock::ExtBuilder::default().has_stakers(true), diff --git a/frame/staking/src/pallet/mod.rs b/frame/staking/src/pallet/mod.rs index 531869666..9a1c61598 100644 --- a/frame/staking/src/pallet/mod.rs +++ b/frame/staking/src/pallet/mod.rs @@ -1778,7 +1778,7 @@ pub mod pallet { /// Enable or disable citizenship requirement for validators. Root only #[pallet::call_index(100)] - #[pallet::weight(10_000)] // FIXME weight + #[pallet::weight(T::WeightInfo::set_citizenship_required())] pub fn set_citizenship_required( origin: OriginFor, citizenship_required: bool, diff --git a/frame/staking/src/weights.rs b/frame/staking/src/weights.rs index 34b01445d..a0d453ff5 100644 --- a/frame/staking/src/weights.rs +++ b/frame/staking/src/weights.rs @@ -15,6 +15,10 @@ // See the License for the specific language governing permissions and // limitations under the License. +// File has been modified by Liberland in 2023. All modifications by Liberland are distributed under the MIT license. + +// You should have received a copy of the MIT license along with this program. If not, see https://opensource.org/licenses/MIT + //! Autogenerated weights for pallet_staking //! //! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev @@ -78,6 +82,7 @@ pub trait WeightInfo { fn chill_other() -> Weight; fn force_apply_min_commission() -> Weight; fn set_min_commission() -> Weight; + fn set_citizenship_required() -> Weight; } /// Weights for pallet_staking using the Substrate node and recommended hardware. @@ -799,6 +804,16 @@ impl WeightInfo for SubstrateWeight { Weight::from_parts(5_224_000, 0) .saturating_add(T::DbWeight::get().writes(1_u64)) } + /// Storage: Staking CitizenshipRequired (r:0 w:1) + /// Proof: Staking CitizenshipRequired (max_values: Some(1), max_size: Some(1), added: 496, mode: MaxEncodedLen) + fn set_citizenship_required() -> Weight { + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 6_352_000 picoseconds. + Weight::from_parts(6_532_000, 0) + .saturating_add(T::DbWeight::get().writes(1_u64)) + } } // For backwards compatibility and tests @@ -1519,4 +1534,14 @@ impl WeightInfo for () { Weight::from_parts(5_224_000, 0) .saturating_add(RocksDbWeight::get().writes(1_u64)) } + /// Storage: Staking CitizenshipRequired (r:0 w:1) + /// Proof: Staking CitizenshipRequired (max_values: Some(1), max_size: Some(1), added: 496, mode: MaxEncodedLen) + fn set_citizenship_required() -> Weight { + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 6_352_000 picoseconds. + Weight::from_parts(6_532_000, 0) + .saturating_add(RocksDbWeight::get().writes(1_u64)) + } } From c5eb5f7585c94582e74b2c42c5902001fd8b0846 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kacper=20=C5=BBuk?= Date: Mon, 26 Jun 2023 16:52:20 +0200 Subject: [PATCH 556/558] Benchmark LLM pallet --- bin/node/runtime/Cargo.toml | 1 + bin/node/runtime/src/lib.rs | 2 + frame/democracy/src/tests.rs | 1 + frame/elections-phragmen/src/lib.rs | 1 + frame/liberland-legislation/src/mock.rs | 1 + frame/llm/src/benchmarking.rs | 108 +++++++++ frame/llm/src/lib.rs | 21 +- frame/llm/src/mock.rs | 3 + frame/llm/src/tests.rs | 2 + frame/llm/src/weights.rs | 287 ++++++++++++++++++++++++ frame/staking/src/mock.rs | 1 + 11 files changed, 418 insertions(+), 10 deletions(-) create mode 100644 frame/llm/src/benchmarking.rs diff --git a/bin/node/runtime/Cargo.toml b/bin/node/runtime/Cargo.toml index 65034e0f2..2158249c4 100644 --- a/bin/node/runtime/Cargo.toml +++ b/bin/node/runtime/Cargo.toml @@ -241,6 +241,7 @@ runtime-benchmarks = [ "pallet-office/runtime-benchmarks", "pallet-federated-bridge/runtime-benchmarks", "pallet-liberland-legislation/runtime-benchmarks", + "pallet-llm/runtime-benchmarks", "frame-system-benchmarking/runtime-benchmarks", ] try-runtime = [ diff --git a/bin/node/runtime/src/lib.rs b/bin/node/runtime/src/lib.rs index 0d7105e03..fed8e4e3c 100644 --- a/bin/node/runtime/src/lib.rs +++ b/bin/node/runtime/src/lib.rs @@ -1356,6 +1356,7 @@ impl pallet_llm::Config for Runtime { HalfSenateOrigin >; type OnLLMPoliticsUnlock = OnLLMPoliticsUnlock; + type WeightInfo = (); } parameter_types! { @@ -1838,6 +1839,7 @@ mod benches { [pallet_office, IdentityOffice] [pallet_liberland_legislation, LiberlandLegislation] [pallet_federated_bridge, EthLLDBridge] + [pallet_llm, LLM] ); } diff --git a/frame/democracy/src/tests.rs b/frame/democracy/src/tests.rs index 1cc9e7519..0e901162a 100644 --- a/frame/democracy/src/tests.rs +++ b/frame/democracy/src/tests.rs @@ -265,6 +265,7 @@ impl pallet_llm::Config for Test { type InflationEventInterval = InflationEventInterval; type OnLLMPoliticsUnlock = (); type SenateOrigin = EnsureRoot; + type WeightInfo = (); } parameter_types! { diff --git a/frame/elections-phragmen/src/lib.rs b/frame/elections-phragmen/src/lib.rs index 009de4388..f55d48aad 100644 --- a/frame/elections-phragmen/src/lib.rs +++ b/frame/elections-phragmen/src/lib.rs @@ -1455,6 +1455,7 @@ mod tests { type InflationEventInterval = InflationEventInterval; type OnLLMPoliticsUnlock = (); type SenateOrigin = EnsureRoot; + type WeightInfo = (); } pub struct TestChangeMembers; diff --git a/frame/liberland-legislation/src/mock.rs b/frame/liberland-legislation/src/mock.rs index 0b2473cfd..2221b7758 100644 --- a/frame/liberland-legislation/src/mock.rs +++ b/frame/liberland-legislation/src/mock.rs @@ -257,6 +257,7 @@ impl pallet_llm::Config for Test { type InflationEventInterval = InflationEventInterval; type OnLLMPoliticsUnlock = (); type SenateOrigin = EnsureRoot; + type WeightInfo = (); } impl pallet_liberland_legislation::Config for Test { diff --git a/frame/llm/src/benchmarking.rs b/frame/llm/src/benchmarking.rs new file mode 100644 index 000000000..4a35e0bed --- /dev/null +++ b/frame/llm/src/benchmarking.rs @@ -0,0 +1,108 @@ +/* +Copyright © 2023 Liberland + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the “Software”), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. +THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +*/ + +#![cfg(feature = "runtime-benchmarks")] + +use super::*; +use crate::{ + Pallet as LLM, + LLMPolitics, +}; +use frame_benchmarking::{account, benchmarks, impl_benchmark_test_suite}; +use frame_system::RawOrigin; +use sp_runtime::Saturating; +use sp_std::prelude::*; + +const SEED: u32 = 0; + +benchmarks! { + politics_lock { + let user: T::AccountId = account("user", 0, SEED); + let amount: T::Balance = 100u8.into(); + LLM::::transfer_from_treasury(user.clone(), amount.clone()).unwrap(); + let origin = RawOrigin::Signed(user.clone()); + assert_eq!(LLMPolitics::::get(&user), 0u8.into()); + }: _(origin, amount.clone()) + verify { + assert_eq!(LLMPolitics::::get(&user), amount); + } + + politics_unlock { + let user: T::AccountId = account("user", 0, SEED); + let amount: T::Balance = 10000u32.into(); + LLM::::transfer_from_treasury(user.clone(), amount.clone()).unwrap(); + let origin = RawOrigin::Signed(user.clone()); + LLM::::politics_lock(origin.clone().into(), amount.clone()).unwrap(); + assert_eq!(LLMPolitics::::get(&user), amount.clone()); + }: _(origin) + verify { + assert!(LLMPolitics::::get(&user) < amount); + } + + treasury_llm_transfer { + let user: T::AccountId = account("user", 0, SEED); + let amount: T::Balance = 100u32.into(); + assert_eq!(LLM::::balance(user.clone()), 0u8.into()); + }: _(RawOrigin::Root, user.clone(), amount.clone()) + verify { + assert_eq!(LLM::::balance(user), amount); + } + + treasury_llm_transfer_to_politipool { + let user: T::AccountId = account("user", 0, SEED); + let amount: T::Balance = 100u32.into(); + assert_eq!(LLMPolitics::::get(&user), 0u8.into()); + }: _(RawOrigin::Root, user.clone(), amount.clone()) + verify { + assert_eq!(LLMPolitics::::get(&user), amount); + } + + send_llm_to_politipool { + let user: T::AccountId = account("user", 0, SEED); + let amount: T::Balance = 100u8.into(); + LLM::::transfer_from_treasury(user.clone(), amount.clone()).unwrap(); + let origin = RawOrigin::Signed(user.clone()); + + let user2: T::AccountId = account("user", 1, SEED); + assert_eq!(LLMPolitics::::get(&user2), 0u8.into()); + }: _(origin, user2.clone(), amount.clone()) + verify { + assert_eq!(LLMPolitics::::get(&user2), amount); + } + + send_llm { + let user: T::AccountId = account("user", 0, SEED); + let amount: T::Balance = 100u8.into(); + LLM::::transfer_from_treasury(user.clone(), amount.clone()).unwrap(); + let origin = RawOrigin::Signed(user.clone()); + + let user2: T::AccountId = account("user", 1, SEED); + assert_eq!(LLM::::balance(user2.clone()), 0u8.into()); + assert_eq!(LLM::::balance(user.clone()), amount.clone()); + }: _(origin, user2.clone(), amount.clone()) + verify { + assert_eq!(LLM::::balance(user), 0u8.into()); + assert_eq!(LLM::::balance(user2), amount); + } + + treasury_lld_transfer { + let user: T::AccountId = account("user", 0, SEED); + let amount = <::Currency as Currency>::minimum_balance(); + <::Currency as Currency>::make_free_balance_be( + &LLM::::get_llm_treasury_account(), + amount.saturating_mul(2u8.into()), + ); + assert_eq!(<::Currency as Currency>::total_balance(&user), 0u8.into()); + }: _(RawOrigin::Root, user.clone(), amount.clone()) + verify { + assert_eq!(<::Currency as Currency>::total_balance(&user), amount); + } +} + +impl_benchmark_test_suite!(LLM, crate::mock::new_test_ext(), crate::mock::Test,); diff --git a/frame/llm/src/lib.rs b/frame/llm/src/lib.rs index f56e4a4f3..76fe50604 100644 --- a/frame/llm/src/lib.rs +++ b/frame/llm/src/lib.rs @@ -138,11 +138,11 @@ #![cfg_attr(not(feature = "std"), no_std)] pub use pallet::*; -#[cfg(test)] mod mock; - -#[cfg(test)] mod tests; +mod benchmarking; +pub mod weights; +pub use weights::WeightInfo; mod impl_fungible; pub mod migrations; @@ -296,6 +296,7 @@ pub mod pallet { type AssetSymbol: Get>; type InflationEventInterval: Get<::BlockNumber>; type OnLLMPoliticsUnlock: OnLLMPoliticsUnlock; + type WeightInfo: WeightInfo; } pub type AssetId = ::AssetId; @@ -345,7 +346,7 @@ pub mod pallet { /// * `LLMPoliticsLocked` /// * `Transferred` from `pallet-assets` #[pallet::call_index(0)] - #[pallet::weight(10_000)] + #[pallet::weight(::WeightInfo::politics_lock())] pub fn politics_lock(origin: OriginFor, amount: T::Balance) -> DispatchResult { let sender = ensure_signed(origin.clone())?; Self::do_politics_lock(sender, amount)?; @@ -362,7 +363,7 @@ pub mod pallet { /// * `LLMPoliticsLocked` /// * `Transferred` from `pallet-assets` #[pallet::call_index(1)] - #[pallet::weight(10_000)] + #[pallet::weight(::WeightInfo::politics_unlock())] pub fn politics_unlock(origin: OriginFor) -> DispatchResult { let sender: T::AccountId = ensure_signed(origin.clone())?; // check if we have political locked LLM @@ -396,7 +397,7 @@ pub mod pallet { /// /// Emits: `Transferred` from `pallet-assets` #[pallet::call_index(2)] - #[pallet::weight(10_000)] + #[pallet::weight(::WeightInfo::treasury_llm_transfer())] pub fn treasury_llm_transfer( origin: OriginFor, to_account: T::AccountId, @@ -414,7 +415,7 @@ pub mod pallet { /// /// Emits: `Transferred` from `pallet-assets` #[pallet::call_index(3)] - #[pallet::weight(10_000)] + #[pallet::weight(::WeightInfo::treasury_llm_transfer_to_politipool())] pub fn treasury_llm_transfer_to_politipool( origin: OriginFor, to_account: T::AccountId, @@ -433,7 +434,7 @@ pub mod pallet { /// /// Emits: `Transferred` from `pallet-assets` #[pallet::call_index(4)] - #[pallet::weight(10_000)] + #[pallet::weight(::WeightInfo::send_llm_to_politipool())] pub fn send_llm_to_politipool( origin: OriginFor, to_account: T::AccountId, @@ -453,7 +454,7 @@ pub mod pallet { /// /// Emits: `Transferred` from `pallet-assets` #[pallet::call_index(5)] - #[pallet::weight(10_000)] + #[pallet::weight(::WeightInfo::send_llm())] pub fn send_llm( origin: OriginFor, to_account: T::AccountId, @@ -471,7 +472,7 @@ pub mod pallet { /// /// Emits: `Transfer` from `pallet-balances` #[pallet::call_index(6)] - #[pallet::weight(10_000)] + #[pallet::weight(::WeightInfo::treasury_lld_transfer())] pub fn treasury_lld_transfer( origin: OriginFor, to_account: T::AccountId, diff --git a/frame/llm/src/mock.rs b/frame/llm/src/mock.rs index 7ffb88cbe..005b3d8aa 100644 --- a/frame/llm/src/mock.rs +++ b/frame/llm/src/mock.rs @@ -1,3 +1,5 @@ +#![cfg(test)] + use crate as pallet_llm; use frame_support::{ ord_parameter_types, parameter_types, @@ -125,6 +127,7 @@ impl pallet_llm::Config for Test { type InflationEventInterval = InflationEventInterval; type OnLLMPoliticsUnlock = (); type SenateOrigin = EnsureRoot; + type WeightInfo = (); } parameter_types! { diff --git a/frame/llm/src/tests.rs b/frame/llm/src/tests.rs index fbbcf76d4..98a9dbcfe 100644 --- a/frame/llm/src/tests.rs +++ b/frame/llm/src/tests.rs @@ -1,3 +1,5 @@ +#![cfg(test)] + use crate::{ mock::*, Electionlock, ElectionlockDuration, Error, Event, LLMPolitics, NextRelease, Withdrawlock, WithdrawlockDuration, diff --git a/frame/llm/src/weights.rs b/frame/llm/src/weights.rs index e69de29bb..c5b0ca0fa 100644 --- a/frame/llm/src/weights.rs +++ b/frame/llm/src/weights.rs @@ -0,0 +1,287 @@ + +//! Autogenerated weights for pallet_llm +//! +//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev +//! DATE: 2023-06-26, STEPS: `20`, REPEAT: `10`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! WORST CASE MAP SIZE: `1000000` +//! HOSTNAME: `kacper-HP-ProBook-445-G7`, CPU: `AMD Ryzen 7 4700U with Radeon Graphics` +//! EXECUTION: Some(Wasm), WASM-EXECUTION: Compiled, CHAIN: None, DB CACHE: 1024 + +// Executed Command: +// target/release/substrate +// benchmark +// pallet +// --pallet=pallet_llm +// --execution=wasm +// --wasm-execution=compiled +// --steps=20 +// --repeat=10 +// --output=frame/llm/src/weights.rs +// --extrinsic=* +// --template=./.maintain/frame-weight-template.hbs + +#![cfg_attr(rustfmt, rustfmt_skip)] +#![allow(unused_parens)] +#![allow(unused_imports)] +#![allow(missing_docs)] + +use frame_support::{traits::Get, weights::{Weight, constants::RocksDbWeight}}; +use core::marker::PhantomData; + +/// Weight functions needed for pallet_llm. +pub trait WeightInfo { + fn politics_lock() -> Weight; + fn politics_unlock() -> Weight; + fn treasury_llm_transfer() -> Weight; + fn treasury_llm_transfer_to_politipool() -> Weight; + fn send_llm_to_politipool() -> Weight; + fn send_llm() -> Weight; + fn treasury_lld_transfer() -> Weight; +} + +/// Weights for pallet_llm using the Substrate node and recommended hardware. +pub struct SubstrateWeight(PhantomData); +impl WeightInfo for SubstrateWeight { + /// Storage: Assets Asset (r:1 w:1) + /// Proof: Assets Asset (max_values: None, max_size: Some(210), added: 2685, mode: MaxEncodedLen) + /// Storage: Assets Account (r:2 w:2) + /// Proof: Assets Account (max_values: None, max_size: Some(134), added: 2609, mode: MaxEncodedLen) + /// Storage: System Account (r:1 w:1) + /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) + /// Storage: LLM LLMPolitics (r:1 w:1) + /// Proof: LLM LLMPolitics (max_values: None, max_size: Some(64), added: 2539, mode: MaxEncodedLen) + fn politics_lock() -> Weight { + // Proof Size summary in bytes: + // Measured: `1584` + // Estimated: `6208` + // Minimum execution time: 89_971_000 picoseconds. + Weight::from_parts(91_223_000, 6208) + .saturating_add(T::DbWeight::get().reads(5_u64)) + .saturating_add(T::DbWeight::get().writes(5_u64)) + } + /// Storage: LLM LLMPolitics (r:1 w:1) + /// Proof: LLM LLMPolitics (max_values: None, max_size: Some(64), added: 2539, mode: MaxEncodedLen) + /// Storage: LLM Withdrawlock (r:1 w:1) + /// Proof: LLM Withdrawlock (max_values: None, max_size: Some(52), added: 2527, mode: MaxEncodedLen) + /// Storage: Assets Asset (r:1 w:1) + /// Proof: Assets Asset (max_values: None, max_size: Some(210), added: 2685, mode: MaxEncodedLen) + /// Storage: Assets Account (r:2 w:2) + /// Proof: Assets Account (max_values: None, max_size: Some(134), added: 2609, mode: MaxEncodedLen) + /// Storage: System Account (r:1 w:1) + /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) + /// Storage: LLM WithdrawlockDuration (r:1 w:0) + /// Proof: LLM WithdrawlockDuration (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) + /// Storage: LLM ElectionlockDuration (r:1 w:0) + /// Proof: LLM ElectionlockDuration (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) + /// Storage: Democracy VotingOf (r:1 w:0) + /// Proof: Democracy VotingOf (max_values: None, max_size: Some(3803), added: 6278, mode: MaxEncodedLen) + /// Storage: LLM Electionlock (r:0 w:1) + /// Proof: LLM Electionlock (max_values: None, max_size: Some(52), added: 2527, mode: MaxEncodedLen) + fn politics_unlock() -> Weight { + // Proof Size summary in bytes: + // Measured: `1565` + // Estimated: `7268` + // Minimum execution time: 110_189_000 picoseconds. + Weight::from_parts(110_890_000, 7268) + .saturating_add(T::DbWeight::get().reads(9_u64)) + .saturating_add(T::DbWeight::get().writes(7_u64)) + } + /// Storage: Assets Asset (r:1 w:1) + /// Proof: Assets Asset (max_values: None, max_size: Some(210), added: 2685, mode: MaxEncodedLen) + /// Storage: Assets Account (r:2 w:2) + /// Proof: Assets Account (max_values: None, max_size: Some(134), added: 2609, mode: MaxEncodedLen) + /// Storage: System Account (r:1 w:1) + /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) + fn treasury_llm_transfer() -> Weight { + // Proof Size summary in bytes: + // Measured: `878` + // Estimated: `6208` + // Minimum execution time: 70_083_000 picoseconds. + Weight::from_parts(71_415_000, 6208) + .saturating_add(T::DbWeight::get().reads(4_u64)) + .saturating_add(T::DbWeight::get().writes(4_u64)) + } + /// Storage: Assets Asset (r:1 w:1) + /// Proof: Assets Asset (max_values: None, max_size: Some(210), added: 2685, mode: MaxEncodedLen) + /// Storage: Assets Account (r:3 w:3) + /// Proof: Assets Account (max_values: None, max_size: Some(134), added: 2609, mode: MaxEncodedLen) + /// Storage: System Account (r:1 w:1) + /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) + /// Storage: LLM LLMPolitics (r:1 w:1) + /// Proof: LLM LLMPolitics (max_values: None, max_size: Some(64), added: 2539, mode: MaxEncodedLen) + fn treasury_llm_transfer_to_politipool() -> Weight { + // Proof Size summary in bytes: + // Measured: `1415` + // Estimated: `8817` + // Minimum execution time: 137_100_000 picoseconds. + Weight::from_parts(139_083_000, 8817) + .saturating_add(T::DbWeight::get().reads(6_u64)) + .saturating_add(T::DbWeight::get().writes(6_u64)) + } + /// Storage: Assets Asset (r:1 w:1) + /// Proof: Assets Asset (max_values: None, max_size: Some(210), added: 2685, mode: MaxEncodedLen) + /// Storage: Assets Account (r:3 w:3) + /// Proof: Assets Account (max_values: None, max_size: Some(134), added: 2609, mode: MaxEncodedLen) + /// Storage: System Account (r:2 w:2) + /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) + /// Storage: LLM LLMPolitics (r:1 w:1) + /// Proof: LLM LLMPolitics (max_values: None, max_size: Some(64), added: 2539, mode: MaxEncodedLen) + fn send_llm_to_politipool() -> Weight { + // Proof Size summary in bytes: + // Measured: `1689` + // Estimated: `8817` + // Minimum execution time: 149_904_000 picoseconds. + Weight::from_parts(151_277_000, 8817) + .saturating_add(T::DbWeight::get().reads(7_u64)) + .saturating_add(T::DbWeight::get().writes(7_u64)) + } + /// Storage: Assets Asset (r:1 w:1) + /// Proof: Assets Asset (max_values: None, max_size: Some(210), added: 2685, mode: MaxEncodedLen) + /// Storage: Assets Account (r:2 w:2) + /// Proof: Assets Account (max_values: None, max_size: Some(134), added: 2609, mode: MaxEncodedLen) + /// Storage: System Account (r:2 w:2) + /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) + fn send_llm() -> Weight { + // Proof Size summary in bytes: + // Measured: `1151` + // Estimated: `6208` + // Minimum execution time: 82_737_000 picoseconds. + Weight::from_parts(83_479_000, 6208) + .saturating_add(T::DbWeight::get().reads(5_u64)) + .saturating_add(T::DbWeight::get().writes(5_u64)) + } + /// Storage: System Account (r:2 w:2) + /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) + fn treasury_lld_transfer() -> Weight { + // Proof Size summary in bytes: + // Measured: `312` + // Estimated: `6196` + // Minimum execution time: 76_044_000 picoseconds. + Weight::from_parts(76_595_000, 6196) + .saturating_add(T::DbWeight::get().reads(2_u64)) + .saturating_add(T::DbWeight::get().writes(2_u64)) + } +} + +// For backwards compatibility and tests +impl WeightInfo for () { + /// Storage: Assets Asset (r:1 w:1) + /// Proof: Assets Asset (max_values: None, max_size: Some(210), added: 2685, mode: MaxEncodedLen) + /// Storage: Assets Account (r:2 w:2) + /// Proof: Assets Account (max_values: None, max_size: Some(134), added: 2609, mode: MaxEncodedLen) + /// Storage: System Account (r:1 w:1) + /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) + /// Storage: LLM LLMPolitics (r:1 w:1) + /// Proof: LLM LLMPolitics (max_values: None, max_size: Some(64), added: 2539, mode: MaxEncodedLen) + fn politics_lock() -> Weight { + // Proof Size summary in bytes: + // Measured: `1584` + // Estimated: `6208` + // Minimum execution time: 89_971_000 picoseconds. + Weight::from_parts(91_223_000, 6208) + .saturating_add(RocksDbWeight::get().reads(5_u64)) + .saturating_add(RocksDbWeight::get().writes(5_u64)) + } + /// Storage: LLM LLMPolitics (r:1 w:1) + /// Proof: LLM LLMPolitics (max_values: None, max_size: Some(64), added: 2539, mode: MaxEncodedLen) + /// Storage: LLM Withdrawlock (r:1 w:1) + /// Proof: LLM Withdrawlock (max_values: None, max_size: Some(52), added: 2527, mode: MaxEncodedLen) + /// Storage: Assets Asset (r:1 w:1) + /// Proof: Assets Asset (max_values: None, max_size: Some(210), added: 2685, mode: MaxEncodedLen) + /// Storage: Assets Account (r:2 w:2) + /// Proof: Assets Account (max_values: None, max_size: Some(134), added: 2609, mode: MaxEncodedLen) + /// Storage: System Account (r:1 w:1) + /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) + /// Storage: LLM WithdrawlockDuration (r:1 w:0) + /// Proof: LLM WithdrawlockDuration (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) + /// Storage: LLM ElectionlockDuration (r:1 w:0) + /// Proof: LLM ElectionlockDuration (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) + /// Storage: Democracy VotingOf (r:1 w:0) + /// Proof: Democracy VotingOf (max_values: None, max_size: Some(3803), added: 6278, mode: MaxEncodedLen) + /// Storage: LLM Electionlock (r:0 w:1) + /// Proof: LLM Electionlock (max_values: None, max_size: Some(52), added: 2527, mode: MaxEncodedLen) + fn politics_unlock() -> Weight { + // Proof Size summary in bytes: + // Measured: `1565` + // Estimated: `7268` + // Minimum execution time: 110_189_000 picoseconds. + Weight::from_parts(110_890_000, 7268) + .saturating_add(RocksDbWeight::get().reads(9_u64)) + .saturating_add(RocksDbWeight::get().writes(7_u64)) + } + /// Storage: Assets Asset (r:1 w:1) + /// Proof: Assets Asset (max_values: None, max_size: Some(210), added: 2685, mode: MaxEncodedLen) + /// Storage: Assets Account (r:2 w:2) + /// Proof: Assets Account (max_values: None, max_size: Some(134), added: 2609, mode: MaxEncodedLen) + /// Storage: System Account (r:1 w:1) + /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) + fn treasury_llm_transfer() -> Weight { + // Proof Size summary in bytes: + // Measured: `878` + // Estimated: `6208` + // Minimum execution time: 70_083_000 picoseconds. + Weight::from_parts(71_415_000, 6208) + .saturating_add(RocksDbWeight::get().reads(4_u64)) + .saturating_add(RocksDbWeight::get().writes(4_u64)) + } + /// Storage: Assets Asset (r:1 w:1) + /// Proof: Assets Asset (max_values: None, max_size: Some(210), added: 2685, mode: MaxEncodedLen) + /// Storage: Assets Account (r:3 w:3) + /// Proof: Assets Account (max_values: None, max_size: Some(134), added: 2609, mode: MaxEncodedLen) + /// Storage: System Account (r:1 w:1) + /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) + /// Storage: LLM LLMPolitics (r:1 w:1) + /// Proof: LLM LLMPolitics (max_values: None, max_size: Some(64), added: 2539, mode: MaxEncodedLen) + fn treasury_llm_transfer_to_politipool() -> Weight { + // Proof Size summary in bytes: + // Measured: `1415` + // Estimated: `8817` + // Minimum execution time: 137_100_000 picoseconds. + Weight::from_parts(139_083_000, 8817) + .saturating_add(RocksDbWeight::get().reads(6_u64)) + .saturating_add(RocksDbWeight::get().writes(6_u64)) + } + /// Storage: Assets Asset (r:1 w:1) + /// Proof: Assets Asset (max_values: None, max_size: Some(210), added: 2685, mode: MaxEncodedLen) + /// Storage: Assets Account (r:3 w:3) + /// Proof: Assets Account (max_values: None, max_size: Some(134), added: 2609, mode: MaxEncodedLen) + /// Storage: System Account (r:2 w:2) + /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) + /// Storage: LLM LLMPolitics (r:1 w:1) + /// Proof: LLM LLMPolitics (max_values: None, max_size: Some(64), added: 2539, mode: MaxEncodedLen) + fn send_llm_to_politipool() -> Weight { + // Proof Size summary in bytes: + // Measured: `1689` + // Estimated: `8817` + // Minimum execution time: 149_904_000 picoseconds. + Weight::from_parts(151_277_000, 8817) + .saturating_add(RocksDbWeight::get().reads(7_u64)) + .saturating_add(RocksDbWeight::get().writes(7_u64)) + } + /// Storage: Assets Asset (r:1 w:1) + /// Proof: Assets Asset (max_values: None, max_size: Some(210), added: 2685, mode: MaxEncodedLen) + /// Storage: Assets Account (r:2 w:2) + /// Proof: Assets Account (max_values: None, max_size: Some(134), added: 2609, mode: MaxEncodedLen) + /// Storage: System Account (r:2 w:2) + /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) + fn send_llm() -> Weight { + // Proof Size summary in bytes: + // Measured: `1151` + // Estimated: `6208` + // Minimum execution time: 82_737_000 picoseconds. + Weight::from_parts(83_479_000, 6208) + .saturating_add(RocksDbWeight::get().reads(5_u64)) + .saturating_add(RocksDbWeight::get().writes(5_u64)) + } + /// Storage: System Account (r:2 w:2) + /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) + fn treasury_lld_transfer() -> Weight { + // Proof Size summary in bytes: + // Measured: `312` + // Estimated: `6196` + // Minimum execution time: 76_044_000 picoseconds. + Weight::from_parts(76_595_000, 6196) + .saturating_add(RocksDbWeight::get().reads(2_u64)) + .saturating_add(RocksDbWeight::get().writes(2_u64)) + } +} diff --git a/frame/staking/src/mock.rs b/frame/staking/src/mock.rs index 0e68ff522..4792ae3ce 100644 --- a/frame/staking/src/mock.rs +++ b/frame/staking/src/mock.rs @@ -232,6 +232,7 @@ impl pallet_llm::Config for Test { type InflationEventInterval = InflationEventInterval; type OnLLMPoliticsUnlock = (); type SenateOrigin = EnsureRoot; + type WeightInfo = (); } use pallet_nfts::PalletFeatures; From c8748ccfec5bcb391ca2c52216f318dba7aa18a1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kacper=20=C5=BBuk?= Date: Mon, 26 Jun 2023 16:52:29 +0200 Subject: [PATCH 557/558] Cargo fmt our pallets --- frame/llm/src/benchmarking.rs | 5 +---- frame/llm/src/impl_fungible.rs | 28 ++++++++++++++-------------- frame/llm/src/lib.rs | 2 +- frame/llm/src/tests.rs | 22 ++++++++++++++++++---- 4 files changed, 34 insertions(+), 23 deletions(-) diff --git a/frame/llm/src/benchmarking.rs b/frame/llm/src/benchmarking.rs index 4a35e0bed..dab1da3dd 100644 --- a/frame/llm/src/benchmarking.rs +++ b/frame/llm/src/benchmarking.rs @@ -10,10 +10,7 @@ THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR I #![cfg(feature = "runtime-benchmarks")] use super::*; -use crate::{ - Pallet as LLM, - LLMPolitics, -}; +use crate::{LLMPolitics, Pallet as LLM}; use frame_benchmarking::{account, benchmarks, impl_benchmark_test_suite}; use frame_system::RawOrigin; use sp_runtime::Saturating; diff --git a/frame/llm/src/impl_fungible.rs b/frame/llm/src/impl_fungible.rs index 139e5aab0..0b197feda 100644 --- a/frame/llm/src/impl_fungible.rs +++ b/frame/llm/src/impl_fungible.rs @@ -6,7 +6,7 @@ use frame_support::{ fungibles::{ Dust as FungiblesDust, Inspect as FungiblesInspect, Unbalanced as FungiblesUnbalanced, }, - DepositConsequence, Fortitude, Preservation, Provenance, WithdrawConsequence, Precision, + DepositConsequence, Fortitude, Precision, Preservation, Provenance, WithdrawConsequence, }, }; @@ -81,24 +81,24 @@ impl Unbalanced for Pallet { } fn decrease_balance( - who: &T::AccountId, - amount: Self::Balance, - precision: Precision, - preservation: Preservation, - fortitude: Fortitude, - ) -> Result { + who: &T::AccountId, + amount: Self::Balance, + precision: Precision, + preservation: Preservation, + fortitude: Fortitude, + ) -> Result { let asset_id = Self::llm_id().into(); Assets::::decrease_balance(asset_id, who, amount, precision, preservation, fortitude) - } + } - fn increase_balance( - who: &T::AccountId, - amount: Self::Balance, - precision: Precision, - ) -> Result { + fn increase_balance( + who: &T::AccountId, + amount: Self::Balance, + precision: Precision, + ) -> Result { let asset_id = Self::llm_id().into(); Assets::::increase_balance(asset_id, who, amount, precision) - } + } } impl Mutate for Pallet {} diff --git a/frame/llm/src/lib.rs b/frame/llm/src/lib.rs index 76fe50604..84e14c681 100644 --- a/frame/llm/src/lib.rs +++ b/frame/llm/src/lib.rs @@ -138,9 +138,9 @@ #![cfg_attr(not(feature = "std"), no_std)] pub use pallet::*; +mod benchmarking; mod mock; mod tests; -mod benchmarking; pub mod weights; pub use weights::WeightInfo; diff --git a/frame/llm/src/tests.rs b/frame/llm/src/tests.rs index 98a9dbcfe..b1630e2a9 100644 --- a/frame/llm/src/tests.rs +++ b/frame/llm/src/tests.rs @@ -5,11 +5,20 @@ use crate::{ Withdrawlock, WithdrawlockDuration, }; use codec::Compact; -use frame_support::{assert_noop, assert_ok, error::BadOrigin, traits::OnInitialize}; +use frame_support::{ + assert_noop, assert_ok, + error::BadOrigin, + traits::{ + tokens::{ + fungible::{Inspect, Mutate}, + Preservation, + }, + OnInitialize, + }, +}; use liberland_traits::{CitizenshipChecker, LLM as LLMTrait}; use pallet_identity::{Data, IdentityInfo}; use sp_runtime::traits::{BlakeTwo256, Hash}; -use frame_support::traits::tokens::{Preservation, fungible::{Inspect, Mutate}}; type AssetsError = pallet_assets::Error; @@ -549,7 +558,12 @@ fn fungible_traits_work() { ::AccountId>>::balance(&99), ::Balance>>::into(100u8) ); - assert_ok!(::AccountId>>::transfer(&99, &999, 100u8.into(), Preservation::Expendable)); + assert_ok!(::AccountId>>::transfer( + &99, + &999, + 100u8.into(), + Preservation::Expendable + )); assert_eq!( ::AccountId>>::balance(&99), ::Balance>>::into(0u8) @@ -559,4 +573,4 @@ fn fungible_traits_work() { ::Balance>>::into(100u8) ); }); -} \ No newline at end of file +} From 3e0e4344b71da8aa1e3e248ca2085d12b5d7a38b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kacper=20=C5=BBuk?= Date: Mon, 26 Jun 2023 17:06:55 +0200 Subject: [PATCH 558/558] Bump spec version to 11 --- bin/node/runtime/src/lib.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/bin/node/runtime/src/lib.rs b/bin/node/runtime/src/lib.rs index fed8e4e3c..f2c4c7469 100644 --- a/bin/node/runtime/src/lib.rs +++ b/bin/node/runtime/src/lib.rs @@ -143,7 +143,7 @@ pub const VERSION: RuntimeVersion = RuntimeVersion { // and set impl_version to 0. If only runtime // implementation changes and behavior does not, then leave spec_version as // is and increment impl_version. - spec_version: 10, + spec_version: 11, impl_version: 0, apis: RUNTIME_API_VERSIONS, transaction_version: 1, @@ -160,7 +160,7 @@ pub const VERSION: RuntimeVersion = RuntimeVersion { // and set impl_version to 0. If only runtime // implementation changes and behavior does not, then leave spec_version as // is and increment impl_version. - spec_version: 10, + spec_version: 11, impl_version: 0, apis: RUNTIME_API_VERSIONS, transaction_version: 1,

    ::Public; @@ -273,23 +273,6 @@ where format!("0x{}", HexDisplay::from(&public_key.into().into_account().as_ref())) } -/// checks if message is Some, otherwise reads message from stdin and optionally decodes hex -pub fn read_message(msg: Option<&String>, should_decode: bool) -> Result, Error> { - let mut message = vec![]; - match msg { - Some(m) => { - message = array_bytes::hex2bytes(m.as_str())?; - }, - None => { - std::io::stdin().lock().read_to_end(&mut message)?; - if should_decode { - message = array_bytes::hex2bytes(array_bytes::hex_bytes2hex_str(&message)?)?; - } - }, - } - Ok(message) -} - /// Allows for calling $method with appropriate crypto impl. #[macro_export] macro_rules! with_crypto_scheme { diff --git a/client/cli/src/commands/verify.rs b/client/cli/src/commands/verify.rs index 82554fbf2..183bf507b 100644 --- a/client/cli/src/commands/verify.rs +++ b/client/cli/src/commands/verify.rs @@ -18,9 +18,10 @@ //! implementation of the `verify` subcommand -use crate::{error, utils, with_crypto_scheme, CryptoSchemeFlag}; +use crate::{error, params::MessageParams, utils, with_crypto_scheme, CryptoSchemeFlag}; use clap::Parser; use sp_core::crypto::{ByteArray, Ss58Codec}; +use std::io::BufRead; /// The `verify` command #[derive(Debug, Clone, Parser)] @@ -37,14 +38,9 @@ pub struct VerifyCmd { /// If not given, you will be prompted for the URI. uri: Option, - /// Message to verify, if not provided you will be prompted to - /// pass the message via STDIN - #[arg(long)] - message: Option, - - /// The message on STDIN is hex-encoded data - #[arg(long)] - hex: bool, + #[allow(missing_docs)] + #[clap(flatten)] + pub message_params: MessageParams, #[allow(missing_docs)] #[clap(flatten)] @@ -54,7 +50,20 @@ pub struct VerifyCmd { impl VerifyCmd { /// Run the command pub fn run(&self) -> error::Result<()> { - let message = utils::read_message(self.message.as_ref(), self.hex)?; + self.verify(|| std::io::stdin().lock()) + } + + /// Verify a signature for a message. + /// + /// The message can either be provided as immediate argument via CLI or otherwise read from the + /// reader created by `create_reader`. The reader will only be created in case that the message + /// is not passed as immediate. + pub(crate) fn verify(&self, create_reader: F) -> error::Result<()> + where + R: BufRead, + F: FnOnce() -> R, + { + let message = self.message_params.message_from(create_reader)?; let sig_data = array_bytes::hex2bytes(&self.sig)?; let uri = utils::read_uri(self.uri.as_ref())?; let uri = if let Some(uri) = uri.strip_prefix("0x") { uri } else { &uri }; @@ -86,3 +95,43 @@ where Ok(()) } + +#[cfg(test)] +mod test { + use super::*; + + const ALICE: &str = "5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY"; + const SIG1: &str = "0x4eb25a2285a82374888880af0024eb30c3a21ce086eae3862888d345af607f0ad6fb081312f11730932564f24a9f8ebcee2d46861413ae61307eca58db2c3e81"; + const SIG2: &str = "0x026342225155056ea797118c1c8c8b3cc002aa2020c36f4217fa3c302783a572ad3dcd38c231cbaf86cadb93984d329c963ceac0685cc1ee4c1ed50fa443a68f"; + + // Verify work with `--message` argument. + #[test] + fn verify_immediate() { + let cmd = VerifyCmd::parse_from(&["verify", SIG1, ALICE, "--message", "test message"]); + assert!(cmd.run().is_ok(), "Alice' signature should verify"); + } + + // Verify work without `--message` argument. + #[test] + fn verify_stdin() { + let cmd = VerifyCmd::parse_from(&["verify", SIG1, ALICE]); + let message = "test message"; + assert!(cmd.verify(|| message.as_bytes()).is_ok(), "Alice' signature should verify"); + } + + // Verify work with `--message` argument for hex message. + #[test] + fn verify_immediate_hex() { + let cmd = VerifyCmd::parse_from(&["verify", SIG2, ALICE, "--message", "0xaabbcc", "--hex"]); + assert!(cmd.run().is_ok(), "Alice' signature should verify"); + } + + // Verify work without `--message` argument for hex message. + #[test] + fn verify_stdin_hex() { + let cmd = VerifyCmd::parse_from(&["verify", SIG2, ALICE, "--hex"]); + assert!(cmd.verify(|| "0xaabbcc".as_bytes()).is_ok()); + assert!(cmd.verify(|| "aabbcc".as_bytes()).is_ok()); + assert!(cmd.verify(|| "0xaABBcC".as_bytes()).is_ok()); + } +} diff --git a/client/cli/src/params/message_params.rs b/client/cli/src/params/message_params.rs new file mode 100644 index 000000000..9f88c24dc --- /dev/null +++ b/client/cli/src/params/message_params.rs @@ -0,0 +1,121 @@ +// This file is part of Substrate. + +// Copyright (C) 2023 Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +//! Params to configure how a message should be passed into a command. + +use crate::error::Error; +use array_bytes::{hex2bytes, hex_bytes2hex_str}; +use clap::Parser; +use std::io::BufRead; + +/// Params to configure how a message should be passed into a command. +#[derive(Parser, Debug, Clone)] +pub struct MessageParams { + /// Message to process. Will be read from STDIN otherwise. + /// + /// The message is assumed to be raw bytes per default. Use `--hex` for hex input. Can + /// optionally be prefixed with `0x` in the hex case. + #[arg(long)] + message: Option, + + /// The message is hex-encoded data. + #[arg(long)] + hex: bool, +} + +impl MessageParams { + /// Produces the message by either using its immediate value or reading from stdin. + /// + /// This function should only be called once and the result cached. + pub(crate) fn message_from(&self, create_reader: F) -> Result, Error> + where + R: BufRead, + F: FnOnce() -> R, + { + let raw = match &self.message { + Some(raw) => raw.as_bytes().to_vec(), + None => { + let mut raw = vec![]; + create_reader().read_to_end(&mut raw)?; + raw + }, + }; + if self.hex { + hex2bytes(hex_bytes2hex_str(&raw)?).map_err(Into::into) + } else { + Ok(raw) + } + } +} + +#[cfg(test)] +mod tests { + use super::*; + + /// Test that decoding an immediate message works. + #[test] + fn message_decode_immediate() { + for (name, input, hex, output) in test_closures() { + println!("Testing: immediate_{}", name); + let params = MessageParams { message: Some(input.into()), hex }; + let message = params.message_from(|| std::io::stdin().lock()); + + match output { + Some(output) => { + let message = message.expect(&format!("{}: should decode but did not", name)); + assert_eq!(message, output, "{}: decoded a wrong message", name); + }, + None => { + message.err().expect(&format!("{}: should not decode but did", name)); + }, + } + } + } + + /// Test that decoding a message from a stream works. + #[test] + fn message_decode_stream() { + for (name, input, hex, output) in test_closures() { + println!("Testing: stream_{}", name); + let params = MessageParams { message: None, hex }; + let message = params.message_from(|| input.as_bytes()); + + match output { + Some(output) => { + let message = message.expect(&format!("{}: should decode but did not", name)); + assert_eq!(message, output, "{}: decoded a wrong message", name); + }, + None => { + message.err().expect(&format!("{}: should not decode but did", name)); + }, + } + } + } + + /// Returns (test_name, input, hex, output). + fn test_closures() -> Vec<(&'static str, &'static str, bool, Option<&'static [u8]>)> { + vec![ + ("decode_no_hex_works", "Hello this is not hex", false, Some(b"Hello this is not hex")), + ("decode_no_hex_with_hex_string_works", "0xffffffff", false, Some(b"0xffffffff")), + ("decode_hex_works", "0x00112233", true, Some(&[0, 17, 34, 51])), + ("decode_hex_without_prefix_works", "00112233", true, Some(&[0, 17, 34, 51])), + ("decode_hex_uppercase_works", "0xaAbbCCDd", true, Some(&[170, 187, 204, 221])), + ("decode_hex_wrong_len_errors", "0x0011223", true, None), + ] + } +} diff --git a/client/cli/src/params/mod.rs b/client/cli/src/params/mod.rs index 3197deb10..4ec7d1d95 100644 --- a/client/cli/src/params/mod.rs +++ b/client/cli/src/params/mod.rs @@ -18,6 +18,7 @@ mod database_params; mod import_params; mod keystore_params; +mod message_params; mod network_params; mod node_key_params; mod offchain_worker_params; @@ -35,7 +36,7 @@ use sp_runtime::{ use std::{fmt::Debug, str::FromStr}; pub use crate::params::{ - database_params::*, import_params::*, keystore_params::*, network_params::*, + database_params::*, import_params::*, keystore_params::*, message_params::*, network_params::*, node_key_params::*, offchain_worker_params::*, pruning_params::*, shared_params::*, transaction_pool_params::*, }; From a1d42aaa9f52531b27f882832cca9efe370ffcb6 Mon Sep 17 00:00:00 2001 From: Roman Useinov Date: Mon, 13 Feb 2023 23:22:51 +0100 Subject: [PATCH 113/558] [Feature] Introduce storage_alias for CountedStorageMap (#13366) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * [Feature] Introduce storagage_alias for CountedStorageMap * bit more dry * bit more dry * address review comments * some tests and fixes * fix ui tests * Apply suggestions from code review Co-authored-by: Bastian Köcher * compare metadata --------- Co-authored-by: parity-processbot <> Co-authored-by: Bastian Köcher --- frame/support/procedural/src/lib.rs | 6 ++ .../procedural/src/pallet/expand/storage.rs | 15 ++-- frame/support/procedural/src/storage_alias.rs | 81 ++++++++++++++++++- .../support/src/storage/types/counted_map.rs | 10 +++ frame/support/test/tests/pallet.rs | 18 ++++- .../forbid_underscore_as_prefix.stderr | 2 +- 6 files changed, 119 insertions(+), 13 deletions(-) diff --git a/frame/support/procedural/src/lib.rs b/frame/support/procedural/src/lib.rs index 8fa46b48d..67e592f4c 100644 --- a/frame/support/procedural/src/lib.rs +++ b/frame/support/procedural/src/lib.rs @@ -69,6 +69,12 @@ fn get_cargo_env_var(version_env: &str) -> std::result::Result String { + format!("CounterFor{}", prefix) +} + /// Declares strongly-typed wrappers around codec-compatible types in storage. /// /// ## Example diff --git a/frame/support/procedural/src/pallet/expand/storage.rs b/frame/support/procedural/src/pallet/expand/storage.rs index 195a62431..0bb19ad6c 100644 --- a/frame/support/procedural/src/pallet/expand/storage.rs +++ b/frame/support/procedural/src/pallet/expand/storage.rs @@ -15,9 +15,12 @@ // See the License for the specific language governing permissions and // limitations under the License. -use crate::pallet::{ - parse::storage::{Metadata, QueryKind, StorageDef, StorageGenerics}, - Def, +use crate::{ + counter_prefix, + pallet::{ + parse::storage::{Metadata, QueryKind, StorageDef, StorageGenerics}, + Def, + }, }; use quote::ToTokens; use std::{collections::HashMap, ops::IndexMut}; @@ -39,12 +42,6 @@ fn counter_prefix_ident(storage_ident: &syn::Ident) -> syn::Ident { ) } -/// Generate the counter_prefix related to the storage. -/// counter_prefix is used by counted storage map. -fn counter_prefix(prefix: &str) -> String { - format!("CounterFor{}", prefix) -} - /// Check for duplicated storage prefixes. This step is necessary since users can specify an /// alternative storage prefix using the #[pallet::storage_prefix] syntax, and we need to ensure /// that the prefix specified by the user is not a duplicate of an existing one. diff --git a/frame/support/procedural/src/storage_alias.rs b/frame/support/procedural/src/storage_alias.rs index e0df01235..eaf8591a1 100644 --- a/frame/support/procedural/src/storage_alias.rs +++ b/frame/support/procedural/src/storage_alias.rs @@ -17,6 +17,7 @@ //! Implementation of the `storage_alias` attribute macro. +use crate::counter_prefix; use frame_support_procedural_tools::generate_crate_access_2018; use proc_macro2::{Span, TokenStream}; use quote::{quote, ToTokens}; @@ -136,6 +137,7 @@ impl ToTokens for SimpleGenerics { mod storage_types { syn::custom_keyword!(StorageValue); syn::custom_keyword!(StorageMap); + syn::custom_keyword!(CountedStorageMap); syn::custom_keyword!(StorageDoubleMap); syn::custom_keyword!(StorageNMap); } @@ -168,6 +170,21 @@ enum StorageType { _trailing_comma: Option, _gt_token: Token![>], }, + CountedMap { + _kw: storage_types::CountedStorageMap, + _lt_token: Token![<], + prefix: SimplePath, + prefix_generics: Option, + _hasher_comma: Token![,], + hasher_ty: Type, + _key_comma: Token![,], + key_ty: Type, + _value_comma: Token![,], + value_ty: Type, + query_type: Option<(Token![,], Type)>, + _trailing_comma: Option, + _gt_token: Token![>], + }, DoubleMap { _kw: storage_types::StorageDoubleMap, _lt_token: Token![<], @@ -235,12 +252,22 @@ impl StorageType { >; } }, + Self::CountedMap { + value_ty, query_type, hasher_ty, key_ty, prefix_generics, .. + } | Self::Map { value_ty, query_type, hasher_ty, key_ty, prefix_generics, .. } => { let query_type = query_type.as_ref().map(|(c, t)| quote!(#c #t)); + let map_type = Ident::new( + match self { + Self::Map { .. } => "StorageMap", + _ => "CountedStorageMap", + }, + Span::call_site(), + ); quote! { #( #attributes )* - #visibility type #storage_name #storage_generics = #crate_::storage::types::StorageMap< + #visibility type #storage_name #storage_generics = #crate_::storage::types::#map_type< #storage_instance #prefix_generics, #hasher_ty, #key_ty, @@ -296,6 +323,7 @@ impl StorageType { match self { Self::Value { prefix, .. } | Self::Map { prefix, .. } | + Self::CountedMap { prefix, .. } | Self::NMap { prefix, .. } | Self::DoubleMap { prefix, .. } => prefix, } @@ -306,6 +334,7 @@ impl StorageType { match self { Self::Value { prefix_generics, .. } | Self::Map { prefix_generics, .. } | + Self::CountedMap { prefix_generics, .. } | Self::NMap { prefix_generics, .. } | Self::DoubleMap { prefix_generics, .. } => prefix_generics.as_ref(), } @@ -363,6 +392,22 @@ impl Parse for StorageType { _trailing_comma: input.peek(Token![,]).then(|| input.parse()).transpose()?, _gt_token: input.parse()?, }) + } else if lookahead.peek(storage_types::CountedStorageMap) { + Ok(Self::CountedMap { + _kw: input.parse()?, + _lt_token: input.parse()?, + prefix: input.parse()?, + prefix_generics: parse_pallet_generics(input)?, + _hasher_comma: input.parse()?, + hasher_ty: input.parse()?, + _key_comma: input.parse()?, + key_ty: input.parse()?, + _value_comma: input.parse()?, + value_ty: input.parse()?, + query_type: parse_query_type(input)?, + _trailing_comma: input.peek(Token![,]).then(|| input.parse()).transpose()?, + _gt_token: input.parse()?, + }) } else if lookahead.peek(storage_types::StorageDoubleMap) { Ok(Self::DoubleMap { _kw: input.parse()?, @@ -476,6 +521,7 @@ pub fn storage_alias(input: TokenStream) -> Result { input.storage_type.prefix(), input.storage_type.prefix_generics(), &input.visibility, + matches!(input.storage_type, StorageType::CountedMap { .. }), )?; let definition = input.storage_type.generate_type_declaration( @@ -511,6 +557,7 @@ fn generate_storage_instance( prefix: &SimplePath, prefix_generics: Option<&TypeGenerics>, visibility: &Visibility, + is_counted_map: bool, ) -> Result { if let Some(ident) = prefix.get_ident().filter(|i| *i == "_") { return Err(Error::new(ident.span(), "`_` is not allowed as prefix by `storage_alias`.")) @@ -546,9 +593,37 @@ fn generate_storage_instance( let where_clause = storage_where_clause.map(|w| quote!(#w)).unwrap_or_default(); - let name = Ident::new(&format!("{}_Storage_Instance", storage_name), Span::call_site()); + let name_str = format!("{}_Storage_Instance", storage_name); + let name = Ident::new(&name_str, Span::call_site()); let storage_name_str = storage_name.to_string(); + let counter_code = is_counted_map.then(|| { + let counter_name = Ident::new(&counter_prefix(&name_str), Span::call_site()); + let counter_storage_name_str = counter_prefix(&storage_name_str); + + quote! { + #visibility struct #counter_name< #impl_generics >( + #crate_::sp_std::marker::PhantomData<(#type_generics)> + ) #where_clause; + + impl<#impl_generics> #crate_::traits::StorageInstance + for #counter_name< #type_generics > #where_clause + { + fn pallet_prefix() -> &'static str { + #pallet_prefix + } + + const STORAGE_PREFIX: &'static str = #counter_storage_name_str; + } + + impl<#impl_generics> #crate_::storage::types::CountedStorageMapInstance + for #name< #type_generics > #where_clause + { + type CounterPrefix = #counter_name < #type_generics >; + } + } + }); + // Implement `StorageInstance` trait. let code = quote! { #visibility struct #name< #impl_generics >( @@ -564,6 +639,8 @@ fn generate_storage_instance( const STORAGE_PREFIX: &'static str = #storage_name_str; } + + #counter_code }; Ok(StorageInstance { name, code }) diff --git a/frame/support/src/storage/types/counted_map.rs b/frame/support/src/storage/types/counted_map.rs index 3361c4093..7d7d9ebcf 100644 --- a/frame/support/src/storage/types/counted_map.rs +++ b/frame/support/src/storage/types/counted_map.rs @@ -543,6 +543,16 @@ mod test { 97 } } + #[crate::storage_alias] + type ExampleCountedMap = CountedStorageMap; + + #[test] + fn storage_alias_works() { + TestExternalities::default().execute_with(|| { + assert_eq!(ExampleCountedMap::count(), 0); + ExampleCountedMap::insert(3, 10); + }) + } #[test] fn test_value_query() { diff --git a/frame/support/test/tests/pallet.rs b/frame/support/test/tests/pallet.rs index c0376d5aa..36150b280 100644 --- a/frame/support/test/tests/pallet.rs +++ b/frame/support/test/tests/pallet.rs @@ -19,7 +19,7 @@ use frame_support::{ dispatch::{ DispatchClass, DispatchInfo, GetDispatchInfo, Parameter, Pays, UnfilteredDispatchable, }, - pallet_prelude::ValueQuery, + pallet_prelude::{StorageInfoTrait, ValueQuery}, storage::unhashed, traits::{ ConstU32, GetCallName, GetStorageVersion, OnFinalize, OnGenesis, OnInitialize, @@ -1906,14 +1906,30 @@ fn assert_type_all_pallets_without_system_reversed_is_correct() { #[test] fn test_storage_alias() { + use frame_support::Twox64Concat; + #[frame_support::storage_alias] type Value where ::AccountId: From + SomeAssociation1, = StorageValue, u32, ValueQuery>; + #[frame_support::storage_alias] + type SomeCountedStorageMap + where + ::AccountId: From + SomeAssociation1, + = CountedStorageMap, Twox64Concat, u8, u32>; + TestExternalities::default().execute_with(|| { pallet::Value::::put(10); assert_eq!(10, Value::::get()); + + pallet2::SomeCountedStorageMap::::insert(10, 100); + assert_eq!(Some(100), SomeCountedStorageMap::::get(10)); + assert_eq!(1, SomeCountedStorageMap::::count()); + assert_eq!( + SomeCountedStorageMap::::storage_info(), + pallet2::SomeCountedStorageMap::::storage_info() + ); }) } diff --git a/frame/support/test/tests/storage_alias_ui/forbid_underscore_as_prefix.stderr b/frame/support/test/tests/storage_alias_ui/forbid_underscore_as_prefix.stderr index 3aa517ecf..3b5e3e9c2 100644 --- a/frame/support/test/tests/storage_alias_ui/forbid_underscore_as_prefix.stderr +++ b/frame/support/test/tests/storage_alias_ui/forbid_underscore_as_prefix.stderr @@ -1,4 +1,4 @@ -error: expected one of: `StorageValue`, `StorageMap`, `StorageDoubleMap`, `StorageNMap` +error: expected one of: `StorageValue`, `StorageMap`, `CountedStorageMap`, `StorageDoubleMap`, `StorageNMap` --> tests/storage_alias_ui/forbid_underscore_as_prefix.rs:2:14 | 2 | type Ident = CustomStorage; From 0ce39208841e519920b57d3ba5a3962188c4c66c Mon Sep 17 00:00:00 2001 From: Jegor Sidorenko <5252494+jsidorenko@users.noreply.github.com> Date: Tue, 14 Feb 2023 10:19:50 +0200 Subject: [PATCH 114/558] [NFTs] Offchain mint (#13158) * Allow to mint with the pre-signed signatures * Another try * WIP: test encoder * Fix the deposits * Refactoring + tests + benchmarks * Add sp-core/runtime-benchmarks * Remove sp-core from dev deps * Enable full_crypto for benchmarks * Typo * Fix * Update frame/nfts/src/mock.rs Co-authored-by: Squirrel * ".git/.scripts/commands/bench/bench.sh" pallet dev pallet_nfts * Add docs * Add attributes into the pre-signed object & track the deposit owner for attributes * Update docs * ".git/.scripts/commands/bench/bench.sh" pallet dev pallet_nfts * Add the number of attributes provided to weights * Apply suggestions * Remove dead code * Remove Copy * Fix docs * Update frame/nfts/src/lib.rs Co-authored-by: Oliver Tale-Yazdi * Update frame/nfts/src/lib.rs Co-authored-by: Oliver Tale-Yazdi --------- Co-authored-by: Squirrel Co-authored-by: command-bot <> Co-authored-by: Oliver Tale-Yazdi --- Cargo.lock | 1 + bin/node/runtime/src/lib.rs | 4 + frame/nfts/Cargo.toml | 6 +- frame/nfts/src/benchmarking.rs | 68 +- frame/nfts/src/common_functions.rs | 2 +- frame/nfts/src/features/attributes.rs | 81 +- frame/nfts/src/features/create_delete_item.rs | 56 + frame/nfts/src/features/metadata.rs | 21 +- frame/nfts/src/features/settings.rs | 7 + frame/nfts/src/lib.rs | 66 +- frame/nfts/src/mock.rs | 23 +- frame/nfts/src/tests.rs | 1734 ++++++++++++----- frame/nfts/src/types.rs | 24 +- frame/nfts/src/weights.rs | 424 ++-- 14 files changed, 1756 insertions(+), 761 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index ad3267984..0c2f85184 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -6017,6 +6017,7 @@ dependencies = [ "scale-info", "sp-core", "sp-io", + "sp-keystore", "sp-runtime", "sp-std", ] diff --git a/bin/node/runtime/src/lib.rs b/bin/node/runtime/src/lib.rs index 8f8a7ceef..2c7969ebc 100644 --- a/bin/node/runtime/src/lib.rs +++ b/bin/node/runtime/src/lib.rs @@ -1566,6 +1566,7 @@ impl pallet_uniques::Config for Runtime { parameter_types! { pub Features: PalletFeatures = PalletFeatures::all_enabled(); + pub const MaxAttributesPerCall: u32 = 10; } impl pallet_nfts::Config for Runtime { @@ -1586,7 +1587,10 @@ impl pallet_nfts::Config for Runtime { type ItemAttributesApprovalsLimit = ItemAttributesApprovalsLimit; type MaxTips = MaxTips; type MaxDeadlineDuration = MaxDeadlineDuration; + type MaxAttributesPerCall = MaxAttributesPerCall; type Features = Features; + type OffchainSignature = Signature; + type OffchainPublic = ::Signer; type WeightInfo = pallet_nfts::weights::SubstrateWeight; #[cfg(feature = "runtime-benchmarks")] type Helper = (); diff --git a/frame/nfts/Cargo.toml b/frame/nfts/Cargo.toml index 9e010cb55..59aa4e091 100644 --- a/frame/nfts/Cargo.toml +++ b/frame/nfts/Cargo.toml @@ -21,14 +21,13 @@ frame-benchmarking = { version = "4.0.0-dev", default-features = false, optional frame-support = { version = "4.0.0-dev", default-features = false, path = "../support" } frame-system = { version = "4.0.0-dev", default-features = false, path = "../system" } sp-core = { version = "7.0.0", default-features = false, path = "../../primitives/core" } +sp-io = { version = "7.0.0", default-features = false, path = "../../primitives/io" } sp-runtime = { version = "7.0.0", default-features = false, path = "../../primitives/runtime" } sp-std = { version = "5.0.0", default-features = false, path = "../../primitives/std" } [dev-dependencies] pallet-balances = { version = "4.0.0-dev", path = "../balances" } -sp-core = { version = "7.0.0", path = "../../primitives/core" } -sp-io = { version = "7.0.0", path = "../../primitives/io" } -sp-std = { version = "5.0.0", path = "../../primitives/std" } +sp-keystore = { version = "0.13.0", path = "../../primitives/keystore" } [features] default = ["std"] @@ -40,6 +39,7 @@ std = [ "log/std", "scale-info/std", "sp-core/std", + "sp-io/std", "sp-runtime/std", "sp-std/std", ] diff --git a/frame/nfts/src/benchmarking.rs b/frame/nfts/src/benchmarking.rs index a4007f6b2..9e724a2f6 100644 --- a/frame/nfts/src/benchmarking.rs +++ b/frame/nfts/src/benchmarking.rs @@ -31,7 +31,11 @@ use frame_support::{ BoundedVec, }; use frame_system::RawOrigin as SystemOrigin; -use sp_runtime::traits::{Bounded, One}; +use sp_io::crypto::{sr25519_generate, sr25519_sign}; +use sp_runtime::{ + traits::{Bounded, IdentifyAccount, One}, + AccountId32, MultiSignature, MultiSigner, +}; use sp_std::prelude::*; use crate::Pallet as Nfts; @@ -148,7 +152,21 @@ fn default_item_config() -> ItemConfig { ItemConfig { settings: ItemSettings::all_enabled() } } +fn make_filled_vec(value: u16, length: usize) -> Vec { + let mut vec = vec![0u8; length]; + let mut s = Vec::from(value.to_be_bytes()); + vec.truncate(length - s.len()); + vec.append(&mut s); + vec +} + benchmarks_instance_pallet! { + where_clause { + where + T::OffchainSignature: From, + T::AccountId: From, + } + create { let collection = T::Helper::collection(0); let origin = T::CreateOrigin::try_successful_origin(&collection) @@ -439,11 +457,7 @@ benchmarks_instance_pallet! { T::Currency::make_free_balance_be(&target, DepositBalanceOf::::max_value()); let value: BoundedVec<_, _> = vec![0u8; T::ValueLimit::get() as usize].try_into().unwrap(); for i in 0..n { - let mut key = vec![0u8; T::KeyLimit::get() as usize]; - let mut s = Vec::from((i as u16).to_be_bytes()); - key.truncate(s.len()); - key.append(&mut s); - + let key = make_filled_vec(i as u16, T::KeyLimit::get() as usize); Nfts::::set_attribute( SystemOrigin::Signed(target.clone()).into(), T::Helper::collection(0), @@ -717,5 +731,47 @@ benchmarks_instance_pallet! { }.into()); } + mint_pre_signed { + let n in 0 .. T::MaxAttributesPerCall::get() as u32; + let caller_public = sr25519_generate(0.into(), None); + let caller = MultiSigner::Sr25519(caller_public).into_account().into(); + T::Currency::make_free_balance_be(&caller, DepositBalanceOf::::max_value()); + let caller_lookup = T::Lookup::unlookup(caller.clone()); + + let collection = T::Helper::collection(0); + let item = T::Helper::item(0); + assert_ok!(Nfts::::force_create( + SystemOrigin::Root.into(), + caller_lookup.clone(), + default_collection_config::() + )); + + let metadata = vec![0u8; T::StringLimit::get() as usize]; + let mut attributes = vec![]; + let attribute_value = vec![0u8; T::ValueLimit::get() as usize]; + for i in 0..n { + let attribute_key = make_filled_vec(i as u16, T::KeyLimit::get() as usize); + attributes.push((attribute_key, attribute_value.clone())); + } + let mint_data = PreSignedMint { + collection, + item, + attributes, + metadata: metadata.clone(), + only_account: None, + deadline: One::one(), + }; + let message = Encode::encode(&mint_data); + let signature = MultiSignature::Sr25519(sr25519_sign(0.into(), &caller_public, &message).unwrap()); + + let target: T::AccountId = account("target", 0, SEED); + T::Currency::make_free_balance_be(&target, DepositBalanceOf::::max_value()); + frame_system::Pallet::::set_block_number(One::one()); + }: _(SystemOrigin::Signed(target.clone()), mint_data, signature.into(), caller) + verify { + let metadata: BoundedVec<_, _> = metadata.try_into().unwrap(); + assert_last_event::(Event::ItemMetadataSet { collection, item, data: metadata }.into()); + } + impl_benchmark_test_suite!(Nfts, crate::mock::new_test_ext(), crate::mock::Test); } diff --git a/frame/nfts/src/common_functions.rs b/frame/nfts/src/common_functions.rs index 9c0faeb6b..31f8a9661 100644 --- a/frame/nfts/src/common_functions.rs +++ b/frame/nfts/src/common_functions.rs @@ -17,7 +17,7 @@ //! Various pieces of common functionality. -use super::*; +use crate::*; impl, I: 'static> Pallet { /// Get the owner of the item, if the item exists. diff --git a/frame/nfts/src/features/attributes.rs b/frame/nfts/src/features/attributes.rs index b25f2a60c..51c75233c 100644 --- a/frame/nfts/src/features/attributes.rs +++ b/frame/nfts/src/features/attributes.rs @@ -26,6 +26,7 @@ impl, I: 'static> Pallet { namespace: AttributeNamespace, key: BoundedVec, value: BoundedVec, + depositor: T::AccountId, ) -> DispatchResult { ensure!( Self::is_pallet_feature_enabled(PalletFeature::Attributes), @@ -66,7 +67,8 @@ impl, I: 'static> Pallet { } let attribute = Attribute::::get((collection, maybe_item, &namespace, &key)); - if attribute.is_none() { + let attribute_exists = attribute.is_some(); + if !attribute_exists { collection_details.attributes.saturating_inc(); } @@ -74,6 +76,7 @@ impl, I: 'static> Pallet { attribute.map_or(AttributeDeposit { account: None, amount: Zero::zero() }, |m| m.1); let mut deposit = Zero::zero(); + // disabled DepositRequired setting only affects the CollectionOwner namespace if collection_config.is_setting_enabled(CollectionSetting::DepositRequired) || namespace != AttributeNamespace::CollectionOwner { @@ -82,33 +85,50 @@ impl, I: 'static> Pallet { .saturating_add(T::AttributeDepositBase::get()); } + let is_collection_owner_namespace = namespace == AttributeNamespace::CollectionOwner; + let is_depositor_collection_owner = + is_collection_owner_namespace && collection_details.owner == depositor; + + // NOTE: in the CollectionOwner namespace if the depositor is `None` that means the deposit + // was paid by the collection's owner. + let old_depositor = + if is_collection_owner_namespace && old_deposit.account.is_none() && attribute_exists { + Some(collection_details.owner.clone()) + } else { + old_deposit.account + }; + let depositor_has_changed = old_depositor != Some(depositor.clone()); + // NOTE: when we transfer an item, we don't move attributes in the ItemOwner namespace. // When the new owner updates the same attribute, we will update the depositor record // and return the deposit to the previous owner. - if old_deposit.account.is_some() && old_deposit.account != Some(origin.clone()) { - T::Currency::unreserve(&old_deposit.account.unwrap(), old_deposit.amount); - T::Currency::reserve(&origin, deposit)?; + if depositor_has_changed { + if let Some(old_depositor) = old_depositor { + T::Currency::unreserve(&old_depositor, old_deposit.amount); + } + T::Currency::reserve(&depositor, deposit)?; } else if deposit > old_deposit.amount { - T::Currency::reserve(&origin, deposit - old_deposit.amount)?; + T::Currency::reserve(&depositor, deposit - old_deposit.amount)?; } else if deposit < old_deposit.amount { - T::Currency::unreserve(&origin, old_deposit.amount - deposit); + T::Currency::unreserve(&depositor, old_deposit.amount - deposit); } - // NOTE: we don't track the depositor in the CollectionOwner namespace as it's always a - // collection's owner. This simplifies the collection's transfer to another owner. - let deposit_owner = match namespace { - AttributeNamespace::CollectionOwner => { - collection_details.owner_deposit.saturating_accrue(deposit); + if is_depositor_collection_owner { + if !depositor_has_changed { collection_details.owner_deposit.saturating_reduce(old_deposit.amount); - None - }, - _ => Some(origin), - }; + } + collection_details.owner_deposit.saturating_accrue(deposit); + } + let new_deposit_owner = match is_depositor_collection_owner { + true => None, + false => Some(depositor), + }; Attribute::::insert( (&collection, maybe_item, &namespace, &key), - (&value, AttributeDeposit { account: deposit_owner, amount: deposit }), + (&value, AttributeDeposit { account: new_deposit_owner, amount: deposit }), ); + Collection::::insert(collection, &collection_details); Self::deposit_event(Event::AttributeSet { collection, maybe_item, key, value, namespace }); Ok(()) @@ -188,10 +208,21 @@ impl, I: 'static> Pallet { // NOTE: if the item was previously burned, the ItemConfigOf record // might not exist. In that case, we allow to clear the attribute. let maybe_is_locked = Self::get_item_config(&collection, &item) - .map_or(false, |c| { - c.has_disabled_setting(ItemSetting::UnlockedAttributes) + .map_or(None, |c| { + Some(c.has_disabled_setting(ItemSetting::UnlockedAttributes)) }); - ensure!(!maybe_is_locked, Error::::LockedItemAttributes); + match maybe_is_locked { + Some(is_locked) => { + // when item exists, then only the collection's owner can clear that + // attribute + ensure!( + check_owner == &collection_details.owner, + Error::::NoPermission + ); + ensure!(!is_locked, Error::::LockedItemAttributes); + }, + None => (), + } }, }, _ => (), @@ -199,16 +230,16 @@ impl, I: 'static> Pallet { } collection_details.attributes.saturating_dec(); - match namespace { - AttributeNamespace::CollectionOwner => { + + match deposit.account { + Some(deposit_account) => { + T::Currency::unreserve(&deposit_account, deposit.amount); + }, + None if namespace == AttributeNamespace::CollectionOwner => { collection_details.owner_deposit.saturating_reduce(deposit.amount); T::Currency::unreserve(&collection_details.owner, deposit.amount); }, _ => (), - }; - - if let Some(deposit_account) = deposit.account { - T::Currency::unreserve(&deposit_account, deposit.amount); } Collection::::insert(collection, &collection_details); diff --git a/frame/nfts/src/features/create_delete_item.rs b/frame/nfts/src/features/create_delete_item.rs index f724fe5c6..63d7a540c 100644 --- a/frame/nfts/src/features/create_delete_item.rs +++ b/frame/nfts/src/features/create_delete_item.rs @@ -85,6 +85,62 @@ impl, I: 'static> Pallet { Ok(()) } + pub(crate) fn do_mint_pre_signed( + mint_to: T::AccountId, + mint_data: PreSignedMintOf, + signer: T::AccountId, + ) -> DispatchResult { + let PreSignedMint { collection, item, attributes, metadata, deadline, only_account } = + mint_data; + let metadata = Self::construct_metadata(metadata)?; + + ensure!( + attributes.len() <= T::MaxAttributesPerCall::get() as usize, + Error::::MaxAttributesLimitReached + ); + if let Some(account) = only_account { + ensure!(account == mint_to, Error::::WrongOrigin); + } + + let now = frame_system::Pallet::::block_number(); + ensure!(deadline >= now, Error::::DeadlineExpired); + + let collection_details = + Collection::::get(&collection).ok_or(Error::::UnknownCollection)?; + ensure!(collection_details.owner == signer, Error::::NoPermission); + + let item_config = ItemConfig { settings: Self::get_default_item_settings(&collection)? }; + Self::do_mint( + collection, + item, + Some(mint_to.clone()), + mint_to.clone(), + item_config, + |_, _| Ok(()), + )?; + for (key, value) in attributes { + Self::do_set_attribute( + collection_details.owner.clone(), + collection, + Some(item), + AttributeNamespace::CollectionOwner, + Self::construct_attribute_key(key)?, + Self::construct_attribute_value(value)?, + mint_to.clone(), + )?; + } + if !metadata.len().is_zero() { + Self::do_set_item_metadata( + Some(collection_details.owner.clone()), + collection, + item, + metadata, + Some(mint_to.clone()), + )?; + } + Ok(()) + } + pub fn do_burn( collection: T::CollectionId, item: T::ItemId, diff --git a/frame/nfts/src/features/metadata.rs b/frame/nfts/src/features/metadata.rs index 272b22474..c4d355f19 100644 --- a/frame/nfts/src/features/metadata.rs +++ b/frame/nfts/src/features/metadata.rs @@ -60,14 +60,16 @@ impl, I: 'static> Pallet { .saturating_add(T::MetadataDepositBase::get()); } - // the previous deposit was taken from the item's owner - if old_deposit.account.is_some() && maybe_depositor.is_none() { - T::Currency::unreserve(&old_deposit.account.unwrap(), old_deposit.amount); - T::Currency::reserve(&collection_details.owner, deposit)?; + let depositor = maybe_depositor.clone().unwrap_or(collection_details.owner.clone()); + let old_depositor = old_deposit.account.unwrap_or(collection_details.owner.clone()); + + if depositor != old_depositor { + T::Currency::unreserve(&old_depositor, old_deposit.amount); + T::Currency::reserve(&depositor, deposit)?; } else if deposit > old_deposit.amount { - T::Currency::reserve(&collection_details.owner, deposit - old_deposit.amount)?; + T::Currency::reserve(&depositor, deposit - old_deposit.amount)?; } else if deposit < old_deposit.amount { - T::Currency::unreserve(&collection_details.owner, old_deposit.amount - deposit); + T::Currency::unreserve(&depositor, old_deposit.amount - deposit); } if maybe_depositor.is_none() { @@ -191,4 +193,11 @@ impl, I: 'static> Pallet { Ok(()) }) } + + /// A helper method to construct metadata. + pub fn construct_metadata( + metadata: Vec, + ) -> Result, DispatchError> { + Ok(BoundedVec::try_from(metadata).map_err(|_| Error::::IncorrectMetadata)?) + } } diff --git a/frame/nfts/src/features/settings.rs b/frame/nfts/src/features/settings.rs index 5f408ed18..7c2971109 100644 --- a/frame/nfts/src/features/settings.rs +++ b/frame/nfts/src/features/settings.rs @@ -96,6 +96,13 @@ impl, I: 'static> Pallet { Ok(config) } + pub(crate) fn get_default_item_settings( + collection_id: &T::CollectionId, + ) -> Result { + let collection_config = Self::get_collection_config(collection_id)?; + Ok(collection_config.mint_settings.default_item_settings) + } + pub(crate) fn is_pallet_feature_enabled(feature: PalletFeature) -> bool { let features = T::Features::get(); return features.is_enabled(feature) diff --git a/frame/nfts/src/lib.rs b/frame/nfts/src/lib.rs index 8f24c8dcd..cec5ea7ff 100644 --- a/frame/nfts/src/lib.rs +++ b/frame/nfts/src/lib.rs @@ -67,6 +67,7 @@ pub mod pallet { use super::*; use frame_support::{pallet_prelude::*, traits::ExistenceRequirement}; use frame_system::pallet_prelude::*; + use sp_runtime::traits::{IdentifyAccount, Verify}; #[pallet::pallet] #[pallet::generate_store(pub(super) trait Store)] @@ -167,10 +168,24 @@ pub mod pallet { #[pallet::constant] type MaxDeadlineDuration: Get<::BlockNumber>; + /// The max number of attributes a user could set per call. + #[pallet::constant] + type MaxAttributesPerCall: Get; + /// Disables some of pallet's features. #[pallet::constant] type Features: Get; + /// Off-Chain signature type. + /// + /// Can verify whether an `Self::OffchainPublic` created a signature. + type OffchainSignature: Verify + Parameter; + + /// Off-Chain public key. + /// + /// Must identify as an on-chain `Self::AccountId`. + type OffchainPublic: IdentifyAccount; + #[cfg(feature = "runtime-benchmarks")] /// A set of helper functions for benchmarking. type Helper: BenchmarkHelper; @@ -591,6 +606,14 @@ pub mod pallet { AlreadyClaimed, /// The provided data is incorrect. IncorrectData, + /// The extrinsic was sent by the wrong origin. + WrongOrigin, + /// The provided signature is incorrect. + WrongSignature, + /// The provided metadata might be too long. + IncorrectMetadata, + /// Can't set more attributes per one call. + MaxAttributesLimitReached, } #[pallet::call] @@ -742,10 +765,8 @@ pub mod pallet { ) -> DispatchResult { let caller = ensure_signed(origin)?; let mint_to = T::Lookup::lookup(mint_to)?; - - let collection_config = Self::get_collection_config(&collection)?; - let item_settings = collection_config.mint_settings.default_item_settings; - let item_config = ItemConfig { settings: item_settings }; + let item_config = + ItemConfig { settings: Self::get_default_item_settings(&collection)? }; Self::do_mint( collection, @@ -1325,7 +1346,15 @@ pub mod pallet { value: BoundedVec, ) -> DispatchResult { let origin = ensure_signed(origin)?; - Self::do_set_attribute(origin, collection, maybe_item, namespace, key, value) + Self::do_set_attribute( + origin.clone(), + collection, + maybe_item, + namespace, + key, + value, + origin, + ) } /// Force-set an attribute for a collection or item. @@ -1768,6 +1797,33 @@ pub mod pallet { witness_price, ) } + + /// Mint an item by providing the pre-signed approval. + /// + /// Origin must be Signed. + /// + /// - `mint_data`: The pre-signed approval that consists of the information about the item, + /// its metadata, attributes, who can mint it (`None` for anyone) and until what block + /// number. + /// - `signature`: The signature of the `data` object. + /// - `signer`: The `data` object's signer. Should be an owner of the collection. + /// + /// Emits `Issued` on success. + /// Emits `AttributeSet` if the attributes were provided. + /// Emits `ItemMetadataSet` if the metadata was not empty. + #[pallet::call_index(37)] + #[pallet::weight(T::WeightInfo::mint_pre_signed(mint_data.attributes.len() as u32))] + pub fn mint_pre_signed( + origin: OriginFor, + mint_data: PreSignedMintOf, + signature: T::OffchainSignature, + signer: T::AccountId, + ) -> DispatchResult { + let origin = ensure_signed(origin)?; + let msg = Encode::encode(&mint_data); + ensure!(signature.verify(&*msg, &signer), Error::::WrongSignature); + Self::do_mint_pre_signed(origin, mint_data, signer) + } } } diff --git a/frame/nfts/src/mock.rs b/frame/nfts/src/mock.rs index f814b209d..d0ef3cf0b 100644 --- a/frame/nfts/src/mock.rs +++ b/frame/nfts/src/mock.rs @@ -25,10 +25,13 @@ use frame_support::{ traits::{AsEnsureOriginWithArg, ConstU32, ConstU64}, }; use sp_core::H256; +use sp_keystore::{testing::KeyStore, KeystoreExt}; use sp_runtime::{ testing::Header, - traits::{BlakeTwo256, IdentityLookup}, + traits::{BlakeTwo256, IdentifyAccount, IdentityLookup, Verify}, + MultiSignature, }; +use std::sync::Arc; type UncheckedExtrinsic = frame_system::mocking::MockUncheckedExtrinsic; type Block = frame_system::mocking::MockBlock; @@ -45,6 +48,10 @@ construct_runtime!( } ); +pub type Signature = MultiSignature; +pub type AccountPublic = ::Signer; +pub type AccountId = ::AccountId; + impl frame_system::Config for Test { type BaseCallFilter = frame_support::traits::Everything; type BlockWeights = (); @@ -55,7 +62,7 @@ impl frame_system::Config for Test { type BlockNumber = u64; type Hash = H256; type Hashing = BlakeTwo256; - type AccountId = u64; + type AccountId = AccountId; type Lookup = IdentityLookup; type Header = Header; type RuntimeEvent = RuntimeEvent; @@ -93,8 +100,8 @@ impl Config for Test { type CollectionId = u32; type ItemId = u32; type Currency = Balances; - type CreateOrigin = AsEnsureOriginWithArg>; - type ForceOrigin = frame_system::EnsureRoot; + type CreateOrigin = AsEnsureOriginWithArg>; + type ForceOrigin = frame_system::EnsureRoot; type Locker = (); type CollectionDeposit = ConstU64<2>; type ItemDeposit = ConstU64<1>; @@ -108,7 +115,13 @@ impl Config for Test { type ItemAttributesApprovalsLimit = ConstU32<2>; type MaxTips = ConstU32<10>; type MaxDeadlineDuration = ConstU64<10000>; + type MaxAttributesPerCall = ConstU32<2>; type Features = Features; + /// Off-chain = signature On-chain - therefore no conversion needed. + /// It needs to be From for benchmarking. + type OffchainSignature = Signature; + /// Using `AccountPublic` here makes it trivial to convert to `AccountId` via `into_account()`. + type OffchainPublic = AccountPublic; type WeightInfo = (); #[cfg(feature = "runtime-benchmarks")] type Helper = (); @@ -117,7 +130,9 @@ impl Config for Test { pub(crate) fn new_test_ext() -> sp_io::TestExternalities { let t = frame_system::GenesisConfig::default().build_storage::().unwrap(); + let keystore = KeyStore::new(); let mut ext = sp_io::TestExternalities::new(t); + ext.register_extension(KeystoreExt(Arc::new(keystore))); ext.execute_with(|| System::set_block_number(1)); ext } diff --git a/frame/nfts/src/tests.rs b/frame/nfts/src/tests.rs index ebbba33b0..fce9073af 100644 --- a/frame/nfts/src/tests.rs +++ b/frame/nfts/src/tests.rs @@ -28,10 +28,17 @@ use frame_support::{ }, }; use pallet_balances::Error as BalancesError; -use sp_core::bounded::BoundedVec; +use sp_core::{bounded::BoundedVec, Pair}; +use sp_runtime::{traits::IdentifyAccount, MultiSignature, MultiSigner}; use sp_std::prelude::*; -fn items() -> Vec<(u64, u32, u32)> { +type AccountIdOf = ::AccountId; + +fn account(id: u8) -> AccountIdOf { + [id; 32].into() +} + +fn items() -> Vec<(AccountIdOf, u32, u32)> { let mut r: Vec<_> = Account::::iter().map(|x| x.0).collect(); r.sort(); let mut s: Vec<_> = Item::::iter().map(|x| (x.2.owner, x.0, x.1)).collect(); @@ -56,7 +63,7 @@ fn items() -> Vec<(u64, u32, u32)> { r } -fn collections() -> Vec<(u64, u32)> { +fn collections() -> Vec<(AccountIdOf, u32)> { let mut r: Vec<_> = CollectionAccount::::iter().map(|x| (x.0, x.1)).collect(); r.sort(); let mut s: Vec<_> = Collection::::iter().map(|x| (x.1.owner, x.0)).collect(); @@ -71,22 +78,26 @@ macro_rules! bvec { } } -fn attributes(collection: u32) -> Vec<(Option, AttributeNamespace, Vec, Vec)> { +fn attributes( + collection: u32, +) -> Vec<(Option, AttributeNamespace>, Vec, Vec)> { let mut s: Vec<_> = Attribute::::iter_prefix((collection,)) .map(|(k, v)| (k.0, k.1, k.2.into(), v.0.into())) .collect(); - s.sort_by_key(|k: &(Option, AttributeNamespace, Vec, Vec)| k.0); - s.sort_by_key(|k: &(Option, AttributeNamespace, Vec, Vec)| k.2.clone()); + s.sort_by_key(|k: &(Option, AttributeNamespace>, Vec, Vec)| k.0); + s.sort_by_key(|k: &(Option, AttributeNamespace>, Vec, Vec)| { + k.2.clone() + }); s } -fn approvals(collection_id: u32, item_id: u32) -> Vec<(u64, Option)> { +fn approvals(collection_id: u32, item_id: u32) -> Vec<(AccountIdOf, Option)> { let item = Item::::get(collection_id, item_id).unwrap(); let s: Vec<_> = item.approvals.into_iter().collect(); s } -fn item_attributes_approvals(collection_id: u32, item_id: u32) -> Vec { +fn item_attributes_approvals(collection_id: u32, item_id: u32) -> Vec> { let approvals = ItemAttributesApprovalsOf::::get(collection_id, item_id); let s: Vec<_> = approvals.into_iter().collect(); s @@ -144,60 +155,83 @@ fn basic_setup_works() { #[test] fn basic_minting_should_work() { new_test_ext().execute_with(|| { - assert_ok!(Nfts::force_create(RuntimeOrigin::root(), 1, default_collection_config())); - assert_eq!(collections(), vec![(1, 0)]); - assert_ok!(Nfts::mint(RuntimeOrigin::signed(1), 0, 42, 1, None)); - assert_eq!(items(), vec![(1, 0, 42)]); - - assert_ok!(Nfts::force_create(RuntimeOrigin::root(), 2, default_collection_config())); - assert_eq!(collections(), vec![(1, 0), (2, 1)]); - assert_ok!(Nfts::mint(RuntimeOrigin::signed(2), 1, 69, 1, None)); - // assert_ok!(Nfts::force_mint(RuntimeOrigin::signed(2), 1, 69, 1, default_item_config())); - assert_eq!(items(), vec![(1, 0, 42), (1, 1, 69)]); + assert_ok!(Nfts::force_create( + RuntimeOrigin::root(), + account(1), + default_collection_config() + )); + assert_eq!(collections(), vec![(account(1), 0)]); + assert_ok!(Nfts::mint(RuntimeOrigin::signed(account(1)), 0, 42, account(1), None)); + assert_eq!(items(), vec![(account(1), 0, 42)]); + + assert_ok!(Nfts::force_create( + RuntimeOrigin::root(), + account(2), + default_collection_config() + )); + assert_eq!(collections(), vec![(account(1), 0), (account(2), 1)]); + assert_ok!(Nfts::mint(RuntimeOrigin::signed(account(2)), 1, 69, account(1), None)); + assert_eq!(items(), vec![(account(1), 0, 42), (account(1), 1, 69)]); }); } #[test] fn lifecycle_should_work() { new_test_ext().execute_with(|| { - Balances::make_free_balance_be(&1, 100); - Balances::make_free_balance_be(&2, 100); + Balances::make_free_balance_be(&account(1), 100); + Balances::make_free_balance_be(&account(2), 100); assert_ok!(Nfts::create( - RuntimeOrigin::signed(1), - 1, + RuntimeOrigin::signed(account(1)), + account(1), collection_config_with_all_settings_enabled() )); - assert_eq!(Balances::reserved_balance(&1), 2); - assert_eq!(collections(), vec![(1, 0)]); - assert_ok!(Nfts::set_collection_metadata(RuntimeOrigin::signed(1), 0, bvec![0, 0])); - assert_eq!(Balances::reserved_balance(&1), 5); + assert_eq!(Balances::reserved_balance(&account(1)), 2); + assert_eq!(collections(), vec![(account(1), 0)]); + assert_ok!(Nfts::set_collection_metadata( + RuntimeOrigin::signed(account(1)), + 0, + bvec![0, 0] + )); + assert_eq!(Balances::reserved_balance(&account(1)), 5); assert!(CollectionMetadataOf::::contains_key(0)); - assert_ok!(Nfts::force_mint(RuntimeOrigin::signed(1), 0, 42, 10, default_item_config())); - assert_eq!(Balances::reserved_balance(&1), 6); - assert_ok!(Nfts::force_mint(RuntimeOrigin::signed(1), 0, 69, 20, default_item_config())); - assert_eq!(Balances::reserved_balance(&1), 7); - assert_ok!(Nfts::mint(RuntimeOrigin::signed(1), 0, 70, 1, None)); - assert_eq!(items(), vec![(1, 0, 70), (10, 0, 42), (20, 0, 69)]); + assert_ok!(Nfts::force_mint( + RuntimeOrigin::signed(account(1)), + 0, + 42, + account(10), + default_item_config() + )); + assert_eq!(Balances::reserved_balance(&account(1)), 6); + assert_ok!(Nfts::force_mint( + RuntimeOrigin::signed(account(1)), + 0, + 69, + account(20), + default_item_config() + )); + assert_eq!(Balances::reserved_balance(&account(1)), 7); + assert_ok!(Nfts::mint(RuntimeOrigin::signed(account(1)), 0, 70, account(1), None)); + assert_eq!(items(), vec![(account(1), 0, 70), (account(10), 0, 42), (account(20), 0, 69)]); assert_eq!(Collection::::get(0).unwrap().items, 3); assert_eq!(Collection::::get(0).unwrap().item_metadatas, 0); - assert_eq!(Balances::reserved_balance(&2), 0); - assert_ok!(Nfts::transfer(RuntimeOrigin::signed(1), 0, 70, 2)); - assert_eq!(Balances::reserved_balance(&2), 1); + assert_eq!(Balances::reserved_balance(&account(2)), 0); + assert_ok!(Nfts::transfer(RuntimeOrigin::signed(account(1)), 0, 70, account(2))); + assert_eq!(Balances::reserved_balance(&account(2)), 1); - assert_ok!(Nfts::set_metadata(RuntimeOrigin::signed(1), 0, 42, bvec![42, 42])); - assert_eq!(Balances::reserved_balance(&1), 10); + assert_ok!(Nfts::set_metadata(RuntimeOrigin::signed(account(1)), 0, 42, bvec![42, 42])); + assert_eq!(Balances::reserved_balance(&account(1)), 10); assert!(ItemMetadataOf::::contains_key(0, 42)); - assert_ok!(Nfts::set_metadata(RuntimeOrigin::signed(1), 0, 69, bvec![69, 69])); - assert_eq!(Balances::reserved_balance(&1), 13); + assert_ok!(Nfts::set_metadata(RuntimeOrigin::signed(account(1)), 0, 69, bvec![69, 69])); + assert_eq!(Balances::reserved_balance(&account(1)), 13); assert!(ItemMetadataOf::::contains_key(0, 69)); let w = Nfts::get_destroy_witness(&0).unwrap(); assert_eq!(w.items, 3); assert_eq!(w.item_metadatas, 2); - assert_ok!(Nfts::destroy(RuntimeOrigin::signed(1), 0, w)); - assert_eq!(Balances::reserved_balance(&1), 0); + assert_ok!(Nfts::destroy(RuntimeOrigin::signed(account(1)), 0, w)); + assert_eq!(Balances::reserved_balance(&account(1)), 0); assert!(!Collection::::contains_key(0)); assert!(!CollectionConfigOf::::contains_key(0)); @@ -214,31 +248,38 @@ fn lifecycle_should_work() { #[test] fn destroy_with_bad_witness_should_not_work() { new_test_ext().execute_with(|| { - Balances::make_free_balance_be(&1, 100); + Balances::make_free_balance_be(&account(1), 100); assert_ok!(Nfts::create( - RuntimeOrigin::signed(1), - 1, + RuntimeOrigin::signed(account(1)), + account(1), collection_config_with_all_settings_enabled() )); let w = Collection::::get(0).unwrap().destroy_witness(); - assert_ok!(Nfts::mint(RuntimeOrigin::signed(1), 0, 42, 1, None)); - assert_noop!(Nfts::destroy(RuntimeOrigin::signed(1), 0, w), Error::::BadWitness); + assert_ok!(Nfts::mint(RuntimeOrigin::signed(account(1)), 0, 42, account(1), None)); + assert_noop!( + Nfts::destroy(RuntimeOrigin::signed(account(1)), 0, w), + Error::::BadWitness + ); }); } #[test] fn mint_should_work() { new_test_ext().execute_with(|| { - assert_ok!(Nfts::force_create(RuntimeOrigin::root(), 1, default_collection_config())); - assert_ok!(Nfts::mint(RuntimeOrigin::signed(1), 0, 42, 1, None)); - assert_eq!(Nfts::owner(0, 42).unwrap(), 1); - assert_eq!(collections(), vec![(1, 0)]); - assert_eq!(items(), vec![(1, 0, 42)]); + assert_ok!(Nfts::force_create( + RuntimeOrigin::root(), + account(1), + default_collection_config() + )); + assert_ok!(Nfts::mint(RuntimeOrigin::signed(account(1)), 0, 42, account(1), None)); + assert_eq!(Nfts::owner(0, 42).unwrap(), account(1)); + assert_eq!(collections(), vec![(account(1), 0)]); + assert_eq!(items(), vec![(account(1), 0, 42)]); // validate minting start and end settings assert_ok!(Nfts::update_mint_settings( - RuntimeOrigin::signed(1), + RuntimeOrigin::signed(account(1)), 0, MintSettings { start_block: Some(2), @@ -250,55 +291,71 @@ fn mint_should_work() { System::set_block_number(1); assert_noop!( - Nfts::mint(RuntimeOrigin::signed(2), 0, 43, 1, None), + Nfts::mint(RuntimeOrigin::signed(account(2)), 0, 43, account(1), None), Error::::MintNotStarted ); System::set_block_number(4); assert_noop!( - Nfts::mint(RuntimeOrigin::signed(2), 0, 43, 1, None), + Nfts::mint(RuntimeOrigin::signed(account(2)), 0, 43, account(1), None), Error::::MintEnded ); // validate price assert_ok!(Nfts::update_mint_settings( - RuntimeOrigin::signed(1), + RuntimeOrigin::signed(account(1)), 0, MintSettings { mint_type: MintType::Public, price: Some(1), ..Default::default() } )); - Balances::make_free_balance_be(&2, 100); - assert_ok!(Nfts::mint(RuntimeOrigin::signed(2), 0, 43, 2, None)); - assert_eq!(Balances::total_balance(&2), 99); + Balances::make_free_balance_be(&account(2), 100); + assert_ok!(Nfts::mint(RuntimeOrigin::signed(account(2)), 0, 43, account(2), None)); + assert_eq!(Balances::total_balance(&account(2)), 99); // validate types - assert_ok!(Nfts::force_create(RuntimeOrigin::root(), 1, default_collection_config())); + assert_ok!(Nfts::force_create( + RuntimeOrigin::root(), + account(1), + default_collection_config() + )); assert_ok!(Nfts::update_mint_settings( - RuntimeOrigin::signed(1), + RuntimeOrigin::signed(account(1)), 1, MintSettings { mint_type: MintType::HolderOf(0), ..Default::default() } )); assert_noop!( - Nfts::mint(RuntimeOrigin::signed(3), 1, 42, 3, None), + Nfts::mint(RuntimeOrigin::signed(account(3)), 1, 42, account(3), None), Error::::BadWitness ); assert_noop!( - Nfts::mint(RuntimeOrigin::signed(2), 1, 42, 2, None), + Nfts::mint(RuntimeOrigin::signed(account(2)), 1, 42, account(2), None), Error::::BadWitness ); assert_noop!( - Nfts::mint(RuntimeOrigin::signed(2), 1, 42, 2, Some(MintWitness { owner_of_item: 42 })), + Nfts::mint( + RuntimeOrigin::signed(account(2)), + 1, + 42, + account(2), + Some(MintWitness { owner_of_item: 42 }) + ), Error::::BadWitness ); assert_ok!(Nfts::mint( - RuntimeOrigin::signed(2), + RuntimeOrigin::signed(account(2)), 1, 42, - 2, + account(2), Some(MintWitness { owner_of_item: 43 }) )); // can't mint twice assert_noop!( - Nfts::mint(RuntimeOrigin::signed(2), 1, 46, 2, Some(MintWitness { owner_of_item: 43 })), + Nfts::mint( + RuntimeOrigin::signed(account(2)), + 1, + 46, + account(2), + Some(MintWitness { owner_of_item: 43 }) + ), Error::::AlreadyClaimed ); }); @@ -307,33 +364,55 @@ fn mint_should_work() { #[test] fn transfer_should_work() { new_test_ext().execute_with(|| { - assert_ok!(Nfts::force_create(RuntimeOrigin::root(), 1, default_collection_config())); - assert_ok!(Nfts::force_mint(RuntimeOrigin::signed(1), 0, 42, 2, default_item_config())); + assert_ok!(Nfts::force_create( + RuntimeOrigin::root(), + account(1), + default_collection_config() + )); + assert_ok!(Nfts::force_mint( + RuntimeOrigin::signed(account(1)), + 0, + 42, + account(2), + default_item_config() + )); - assert_ok!(Nfts::transfer(RuntimeOrigin::signed(2), 0, 42, 3)); - assert_eq!(items(), vec![(3, 0, 42)]); + assert_ok!(Nfts::transfer(RuntimeOrigin::signed(account(2)), 0, 42, account(3))); + assert_eq!(items(), vec![(account(3), 0, 42)]); assert_noop!( - Nfts::transfer(RuntimeOrigin::signed(2), 0, 42, 4), + Nfts::transfer(RuntimeOrigin::signed(account(2)), 0, 42, account(4)), Error::::NoPermission ); - assert_ok!(Nfts::approve_transfer(RuntimeOrigin::signed(3), 0, 42, 2, None)); - assert_ok!(Nfts::transfer(RuntimeOrigin::signed(2), 0, 42, 4)); + assert_ok!(Nfts::approve_transfer( + RuntimeOrigin::signed(account(3)), + 0, + 42, + account(2), + None + )); + assert_ok!(Nfts::transfer(RuntimeOrigin::signed(account(2)), 0, 42, account(4))); // validate we can't transfer non-transferable items let collection_id = 1; assert_ok!(Nfts::force_create( RuntimeOrigin::root(), - 1, + account(1), collection_config_from_disabled_settings( CollectionSetting::TransferableItems | CollectionSetting::DepositRequired ) )); - assert_ok!(Nfts::force_mint(RuntimeOrigin::signed(1), 1, 1, 42, default_item_config())); + assert_ok!(Nfts::force_mint( + RuntimeOrigin::signed(account(1)), + 1, + 1, + account(42), + default_item_config() + )); assert_noop!( - Nfts::transfer(RuntimeOrigin::signed(1), collection_id, 42, 3,), + Nfts::transfer(RuntimeOrigin::signed(account(1)), collection_id, 42, account(3)), Error::::ItemsNonTransferable ); }); @@ -342,19 +421,26 @@ fn transfer_should_work() { #[test] fn locking_transfer_should_work() { new_test_ext().execute_with(|| { - assert_ok!(Nfts::force_create(RuntimeOrigin::root(), 1, default_collection_config())); - assert_ok!(Nfts::mint(RuntimeOrigin::signed(1), 0, 42, 1, None)); - assert_ok!(Nfts::lock_item_transfer(RuntimeOrigin::signed(1), 0, 42)); - assert_noop!(Nfts::transfer(RuntimeOrigin::signed(1), 0, 42, 2), Error::::ItemLocked); + assert_ok!(Nfts::force_create( + RuntimeOrigin::root(), + account(1), + default_collection_config() + )); + assert_ok!(Nfts::mint(RuntimeOrigin::signed(account(1)), 0, 42, account(1), None)); + assert_ok!(Nfts::lock_item_transfer(RuntimeOrigin::signed(account(1)), 0, 42)); + assert_noop!( + Nfts::transfer(RuntimeOrigin::signed(account(1)), 0, 42, account(2)), + Error::::ItemLocked + ); - assert_ok!(Nfts::unlock_item_transfer(RuntimeOrigin::signed(1), 0, 42)); + assert_ok!(Nfts::unlock_item_transfer(RuntimeOrigin::signed(account(1)), 0, 42)); assert_ok!(Nfts::lock_collection( - RuntimeOrigin::signed(1), + RuntimeOrigin::signed(account(1)), 0, CollectionSettings::from_disabled(CollectionSetting::TransferableItems.into()) )); assert_noop!( - Nfts::transfer(RuntimeOrigin::signed(1), 0, 42, 2), + Nfts::transfer(RuntimeOrigin::signed(account(1)), 0, 42, account(2)), Error::::ItemsNonTransferable ); @@ -363,99 +449,116 @@ fn locking_transfer_should_work() { 0, collection_config_with_all_settings_enabled(), )); - assert_ok!(Nfts::transfer(RuntimeOrigin::signed(1), 0, 42, 2)); + assert_ok!(Nfts::transfer(RuntimeOrigin::signed(account(1)), 0, 42, account(2))); }); } #[test] fn origin_guards_should_work() { new_test_ext().execute_with(|| { - assert_ok!(Nfts::force_create(RuntimeOrigin::root(), 1, default_collection_config())); - assert_ok!(Nfts::mint(RuntimeOrigin::signed(1), 0, 42, 1, None)); + assert_ok!(Nfts::force_create( + RuntimeOrigin::root(), + account(1), + default_collection_config() + )); + assert_ok!(Nfts::mint(RuntimeOrigin::signed(account(1)), 0, 42, account(1), None)); - Balances::make_free_balance_be(&2, 100); - assert_ok!(Nfts::set_accept_ownership(RuntimeOrigin::signed(2), Some(0))); + Balances::make_free_balance_be(&account(2), 100); + assert_ok!(Nfts::set_accept_ownership(RuntimeOrigin::signed(account(2)), Some(0))); assert_noop!( - Nfts::transfer_ownership(RuntimeOrigin::signed(2), 0, 2), + Nfts::transfer_ownership(RuntimeOrigin::signed(account(2)), 0, account(2)), Error::::NoPermission ); assert_noop!( - Nfts::set_team(RuntimeOrigin::signed(2), 0, 2, 2, 2), + Nfts::set_team( + RuntimeOrigin::signed(account(2)), + 0, + account(2), + account(2), + account(2), + ), Error::::NoPermission ); assert_noop!( - Nfts::lock_item_transfer(RuntimeOrigin::signed(2), 0, 42), + Nfts::lock_item_transfer(RuntimeOrigin::signed(account(2)), 0, 42), Error::::NoPermission ); assert_noop!( - Nfts::unlock_item_transfer(RuntimeOrigin::signed(2), 0, 42), + Nfts::unlock_item_transfer(RuntimeOrigin::signed(account(2)), 0, 42), Error::::NoPermission ); assert_noop!( - Nfts::mint(RuntimeOrigin::signed(2), 0, 69, 2, None), + Nfts::mint(RuntimeOrigin::signed(account(2)), 0, 69, account(2), None), Error::::NoPermission ); assert_noop!( - Nfts::burn(RuntimeOrigin::signed(2), 0, 42, None), + Nfts::burn(RuntimeOrigin::signed(account(2)), 0, 42, None), Error::::NoPermission ); let w = Nfts::get_destroy_witness(&0).unwrap(); - assert_noop!(Nfts::destroy(RuntimeOrigin::signed(2), 0, w), Error::::NoPermission); + assert_noop!( + Nfts::destroy(RuntimeOrigin::signed(account(2)), 0, w), + Error::::NoPermission + ); }); } #[test] fn transfer_owner_should_work() { new_test_ext().execute_with(|| { - Balances::make_free_balance_be(&1, 100); - Balances::make_free_balance_be(&2, 100); - Balances::make_free_balance_be(&3, 100); + Balances::make_free_balance_be(&account(1), 100); + Balances::make_free_balance_be(&account(2), 100); + Balances::make_free_balance_be(&account(3), 100); assert_ok!(Nfts::create( - RuntimeOrigin::signed(1), - 1, + RuntimeOrigin::signed(account(1)), + account(1), collection_config_with_all_settings_enabled() )); - assert_eq!(collections(), vec![(1, 0)]); + assert_eq!(collections(), vec![(account(1), 0)]); assert_noop!( - Nfts::transfer_ownership(RuntimeOrigin::signed(1), 0, 2), + Nfts::transfer_ownership(RuntimeOrigin::signed(account(1)), 0, account(2)), Error::::Unaccepted ); - assert_ok!(Nfts::set_accept_ownership(RuntimeOrigin::signed(2), Some(0))); - assert_ok!(Nfts::transfer_ownership(RuntimeOrigin::signed(1), 0, 2)); + assert_ok!(Nfts::set_accept_ownership(RuntimeOrigin::signed(account(2)), Some(0))); + assert_ok!(Nfts::transfer_ownership(RuntimeOrigin::signed(account(1)), 0, account(2))); - assert_eq!(collections(), vec![(2, 0)]); - assert_eq!(Balances::total_balance(&1), 98); - assert_eq!(Balances::total_balance(&2), 102); - assert_eq!(Balances::reserved_balance(&1), 0); - assert_eq!(Balances::reserved_balance(&2), 2); + assert_eq!(collections(), vec![(account(2), 0)]); + assert_eq!(Balances::total_balance(&account(1)), 98); + assert_eq!(Balances::total_balance(&account(2)), 102); + assert_eq!(Balances::reserved_balance(&account(1)), 0); + assert_eq!(Balances::reserved_balance(&account(2)), 2); - assert_ok!(Nfts::set_accept_ownership(RuntimeOrigin::signed(1), Some(0))); + assert_ok!(Nfts::set_accept_ownership(RuntimeOrigin::signed(account(1)), Some(0))); assert_noop!( - Nfts::transfer_ownership(RuntimeOrigin::signed(1), 0, 1), + Nfts::transfer_ownership(RuntimeOrigin::signed(account(1)), 0, account(1)), Error::::NoPermission ); // Mint and set metadata now and make sure that deposit gets transferred back. - assert_ok!(Nfts::set_collection_metadata(RuntimeOrigin::signed(2), 0, bvec![0u8; 20])); - assert_ok!(Nfts::mint(RuntimeOrigin::signed(1), 0, 42, 1, None)); - assert_eq!(Balances::reserved_balance(&1), 1); - assert_ok!(Nfts::set_metadata(RuntimeOrigin::signed(2), 0, 42, bvec![0u8; 20])); - assert_ok!(Nfts::set_accept_ownership(RuntimeOrigin::signed(3), Some(0))); - assert_ok!(Nfts::transfer_ownership(RuntimeOrigin::signed(2), 0, 3)); - assert_eq!(collections(), vec![(3, 0)]); - assert_eq!(Balances::total_balance(&2), 58); - assert_eq!(Balances::total_balance(&3), 144); - assert_eq!(Balances::reserved_balance(&2), 0); - assert_eq!(Balances::reserved_balance(&3), 44); - - assert_ok!(Nfts::transfer(RuntimeOrigin::signed(1), 0, 42, 2)); - assert_eq!(Balances::reserved_balance(&1), 0); - assert_eq!(Balances::reserved_balance(&2), 1); + assert_ok!(Nfts::set_collection_metadata( + RuntimeOrigin::signed(account(2)), + 0, + bvec![0u8; 20], + )); + assert_ok!(Nfts::mint(RuntimeOrigin::signed(account(1)), 0, 42, account(1), None)); + assert_eq!(Balances::reserved_balance(&account(1)), 1); + assert_ok!(Nfts::set_metadata(RuntimeOrigin::signed(account(2)), 0, 42, bvec![0u8; 20])); + assert_ok!(Nfts::set_accept_ownership(RuntimeOrigin::signed(account(3)), Some(0))); + assert_ok!(Nfts::transfer_ownership(RuntimeOrigin::signed(account(2)), 0, account(3))); + assert_eq!(collections(), vec![(account(3), 0)]); + assert_eq!(Balances::total_balance(&account(2)), 58); + assert_eq!(Balances::total_balance(&account(3)), 144); + assert_eq!(Balances::reserved_balance(&account(2)), 0); + assert_eq!(Balances::reserved_balance(&account(3)), 44); + + assert_ok!(Nfts::transfer(RuntimeOrigin::signed(account(1)), 0, 42, account(2))); + assert_eq!(Balances::reserved_balance(&account(1)), 0); + assert_eq!(Balances::reserved_balance(&account(2)), 1); // 2's acceptance from before is reset when it became an owner, so it cannot be transferred // without a fresh acceptance. assert_noop!( - Nfts::transfer_ownership(RuntimeOrigin::signed(3), 0, 2), + Nfts::transfer_ownership(RuntimeOrigin::signed(account(3)), 0, account(2)), Error::::Unaccepted ); }); @@ -464,14 +567,24 @@ fn transfer_owner_should_work() { #[test] fn set_team_should_work() { new_test_ext().execute_with(|| { - assert_ok!(Nfts::force_create(RuntimeOrigin::root(), 1, default_collection_config())); - assert_ok!(Nfts::set_team(RuntimeOrigin::signed(1), 0, 2, 3, 4)); - - assert_ok!(Nfts::mint(RuntimeOrigin::signed(2), 0, 42, 2, None)); - assert_ok!(Nfts::lock_item_transfer(RuntimeOrigin::signed(4), 0, 42)); - assert_ok!(Nfts::unlock_item_transfer(RuntimeOrigin::signed(4), 0, 42)); - assert_ok!(Nfts::transfer(RuntimeOrigin::signed(3), 0, 42, 3)); - assert_ok!(Nfts::burn(RuntimeOrigin::signed(3), 0, 42, None)); + assert_ok!(Nfts::force_create( + RuntimeOrigin::root(), + account(1), + default_collection_config(), + )); + assert_ok!(Nfts::set_team( + RuntimeOrigin::signed(account(1)), + 0, + account(2), + account(3), + account(4), + )); + + assert_ok!(Nfts::mint(RuntimeOrigin::signed(account(2)), 0, 42, account(2), None)); + assert_ok!(Nfts::lock_item_transfer(RuntimeOrigin::signed(account(4)), 0, 42)); + assert_ok!(Nfts::unlock_item_transfer(RuntimeOrigin::signed(account(4)), 0, 42)); + assert_ok!(Nfts::transfer(RuntimeOrigin::signed(account(3)), 0, 42, account(3))); + assert_ok!(Nfts::burn(RuntimeOrigin::signed(account(3)), 0, 42, None)); }); } @@ -480,69 +593,85 @@ fn set_collection_metadata_should_work() { new_test_ext().execute_with(|| { // Cannot add metadata to unknown item assert_noop!( - Nfts::set_collection_metadata(RuntimeOrigin::signed(1), 0, bvec![0u8; 20]), + Nfts::set_collection_metadata(RuntimeOrigin::signed(account(1)), 0, bvec![0u8; 20]), Error::::NoConfig, ); assert_ok!(Nfts::force_create( RuntimeOrigin::root(), - 1, + account(1), collection_config_with_all_settings_enabled() )); // Cannot add metadata to unowned item assert_noop!( - Nfts::set_collection_metadata(RuntimeOrigin::signed(2), 0, bvec![0u8; 20]), + Nfts::set_collection_metadata(RuntimeOrigin::signed(account(2)), 0, bvec![0u8; 20]), Error::::NoPermission, ); // Successfully add metadata and take deposit - Balances::make_free_balance_be(&1, 30); - assert_ok!(Nfts::set_collection_metadata(RuntimeOrigin::signed(1), 0, bvec![0u8; 20])); - assert_eq!(Balances::free_balance(&1), 9); + Balances::make_free_balance_be(&account(1), 30); + assert_ok!(Nfts::set_collection_metadata( + RuntimeOrigin::signed(account(1)), + 0, + bvec![0u8; 20] + )); + assert_eq!(Balances::free_balance(&account(1)), 9); assert!(CollectionMetadataOf::::contains_key(0)); // Force origin works, too. assert_ok!(Nfts::set_collection_metadata(RuntimeOrigin::root(), 0, bvec![0u8; 18])); // Update deposit - assert_ok!(Nfts::set_collection_metadata(RuntimeOrigin::signed(1), 0, bvec![0u8; 15])); - assert_eq!(Balances::free_balance(&1), 14); - assert_ok!(Nfts::set_collection_metadata(RuntimeOrigin::signed(1), 0, bvec![0u8; 25])); - assert_eq!(Balances::free_balance(&1), 4); + assert_ok!(Nfts::set_collection_metadata( + RuntimeOrigin::signed(account(1)), + 0, + bvec![0u8; 15] + )); + assert_eq!(Balances::free_balance(&account(1)), 14); + assert_ok!(Nfts::set_collection_metadata( + RuntimeOrigin::signed(account(1)), + 0, + bvec![0u8; 25] + )); + assert_eq!(Balances::free_balance(&account(1)), 4); // Cannot over-reserve assert_noop!( - Nfts::set_collection_metadata(RuntimeOrigin::signed(1), 0, bvec![0u8; 40]), + Nfts::set_collection_metadata(RuntimeOrigin::signed(account(1)), 0, bvec![0u8; 40]), BalancesError::::InsufficientBalance, ); // Can't set or clear metadata once frozen - assert_ok!(Nfts::set_collection_metadata(RuntimeOrigin::signed(1), 0, bvec![0u8; 15])); + assert_ok!(Nfts::set_collection_metadata( + RuntimeOrigin::signed(account(1)), + 0, + bvec![0u8; 15] + )); assert_ok!(Nfts::lock_collection( - RuntimeOrigin::signed(1), + RuntimeOrigin::signed(account(1)), 0, CollectionSettings::from_disabled(CollectionSetting::UnlockedMetadata.into()) )); assert_noop!( - Nfts::set_collection_metadata(RuntimeOrigin::signed(1), 0, bvec![0u8; 15]), + Nfts::set_collection_metadata(RuntimeOrigin::signed(account(1)), 0, bvec![0u8; 15]), Error::::LockedCollectionMetadata, ); assert_noop!( - Nfts::clear_collection_metadata(RuntimeOrigin::signed(1), 0), + Nfts::clear_collection_metadata(RuntimeOrigin::signed(account(1)), 0), Error::::LockedCollectionMetadata ); // Clear Metadata assert_ok!(Nfts::set_collection_metadata(RuntimeOrigin::root(), 0, bvec![0u8; 15])); assert_noop!( - Nfts::clear_collection_metadata(RuntimeOrigin::signed(2), 0), + Nfts::clear_collection_metadata(RuntimeOrigin::signed(account(2)), 0), Error::::NoPermission ); assert_noop!( - Nfts::clear_collection_metadata(RuntimeOrigin::signed(1), 1), + Nfts::clear_collection_metadata(RuntimeOrigin::signed(account(1)), 1), Error::::UnknownCollection ); assert_noop!( - Nfts::clear_collection_metadata(RuntimeOrigin::signed(1), 0), + Nfts::clear_collection_metadata(RuntimeOrigin::signed(account(1)), 0), Error::::LockedCollectionMetadata ); assert_ok!(Nfts::clear_collection_metadata(RuntimeOrigin::root(), 0)); @@ -553,61 +682,67 @@ fn set_collection_metadata_should_work() { #[test] fn set_item_metadata_should_work() { new_test_ext().execute_with(|| { - Balances::make_free_balance_be(&1, 30); + Balances::make_free_balance_be(&account(1), 30); // Cannot add metadata to unknown item assert_ok!(Nfts::force_create( RuntimeOrigin::root(), - 1, + account(1), collection_config_with_all_settings_enabled() )); - assert_ok!(Nfts::mint(RuntimeOrigin::signed(1), 0, 42, 1, None)); + assert_ok!(Nfts::mint(RuntimeOrigin::signed(account(1)), 0, 42, account(1), None)); // Cannot add metadata to unowned item assert_noop!( - Nfts::set_metadata(RuntimeOrigin::signed(2), 0, 42, bvec![0u8; 20]), + Nfts::set_metadata(RuntimeOrigin::signed(account(2)), 0, 42, bvec![0u8; 20]), Error::::NoPermission, ); // Successfully add metadata and take deposit - assert_ok!(Nfts::set_metadata(RuntimeOrigin::signed(1), 0, 42, bvec![0u8; 20])); - assert_eq!(Balances::free_balance(&1), 8); + assert_ok!(Nfts::set_metadata(RuntimeOrigin::signed(account(1)), 0, 42, bvec![0u8; 20])); + assert_eq!(Balances::free_balance(&account(1)), 8); assert!(ItemMetadataOf::::contains_key(0, 42)); // Force origin works, too. assert_ok!(Nfts::set_metadata(RuntimeOrigin::root(), 0, 42, bvec![0u8; 18])); // Update deposit - assert_ok!(Nfts::set_metadata(RuntimeOrigin::signed(1), 0, 42, bvec![0u8; 15])); - assert_eq!(Balances::free_balance(&1), 13); - assert_ok!(Nfts::set_metadata(RuntimeOrigin::signed(1), 0, 42, bvec![0u8; 25])); - assert_eq!(Balances::free_balance(&1), 3); + assert_ok!(Nfts::set_metadata(RuntimeOrigin::signed(account(1)), 0, 42, bvec![0u8; 15])); + assert_eq!(Balances::free_balance(&account(1)), 13); + assert_ok!(Nfts::set_metadata(RuntimeOrigin::signed(account(1)), 0, 42, bvec![0u8; 25])); + assert_eq!(Balances::free_balance(&account(1)), 3); // Cannot over-reserve assert_noop!( - Nfts::set_metadata(RuntimeOrigin::signed(1), 0, 42, bvec![0u8; 40]), + Nfts::set_metadata(RuntimeOrigin::signed(account(1)), 0, 42, bvec![0u8; 40]), BalancesError::::InsufficientBalance, ); // Can't set or clear metadata once frozen - assert_ok!(Nfts::set_metadata(RuntimeOrigin::signed(1), 0, 42, bvec![0u8; 15])); - assert_ok!(Nfts::lock_item_properties(RuntimeOrigin::signed(1), 0, 42, true, false)); + assert_ok!(Nfts::set_metadata(RuntimeOrigin::signed(account(1)), 0, 42, bvec![0u8; 15])); + assert_ok!(Nfts::lock_item_properties( + RuntimeOrigin::signed(account(1)), + 0, + 42, + true, + false + )); assert_noop!( - Nfts::set_metadata(RuntimeOrigin::signed(1), 0, 42, bvec![0u8; 15]), + Nfts::set_metadata(RuntimeOrigin::signed(account(1)), 0, 42, bvec![0u8; 15]), Error::::LockedItemMetadata, ); assert_noop!( - Nfts::clear_metadata(RuntimeOrigin::signed(1), 0, 42), + Nfts::clear_metadata(RuntimeOrigin::signed(account(1)), 0, 42), Error::::LockedItemMetadata, ); // Clear Metadata assert_ok!(Nfts::set_metadata(RuntimeOrigin::root(), 0, 42, bvec![0u8; 15])); assert_noop!( - Nfts::clear_metadata(RuntimeOrigin::signed(2), 0, 42), + Nfts::clear_metadata(RuntimeOrigin::signed(account(2)), 0, 42), Error::::NoPermission, ); assert_noop!( - Nfts::clear_metadata(RuntimeOrigin::signed(1), 1, 42), + Nfts::clear_metadata(RuntimeOrigin::signed(account(1)), 1, 42), Error::::MetadataNotFound, ); assert_ok!(Nfts::clear_metadata(RuntimeOrigin::root(), 0, 42)); @@ -618,17 +753,17 @@ fn set_item_metadata_should_work() { #[test] fn set_collection_owner_attributes_should_work() { new_test_ext().execute_with(|| { - Balances::make_free_balance_be(&1, 100); + Balances::make_free_balance_be(&account(1), 100); assert_ok!(Nfts::force_create( RuntimeOrigin::root(), - 1, + account(1), collection_config_with_all_settings_enabled() )); - assert_ok!(Nfts::mint(RuntimeOrigin::signed(1), 0, 0, 1, None)); + assert_ok!(Nfts::mint(RuntimeOrigin::signed(account(1)), 0, 0, account(1), None)); assert_ok!(Nfts::set_attribute( - RuntimeOrigin::signed(1), + RuntimeOrigin::signed(account(1)), 0, None, AttributeNamespace::CollectionOwner, @@ -636,7 +771,7 @@ fn set_collection_owner_attributes_should_work() { bvec![0], )); assert_ok!(Nfts::set_attribute( - RuntimeOrigin::signed(1), + RuntimeOrigin::signed(account(1)), 0, Some(0), AttributeNamespace::CollectionOwner, @@ -644,7 +779,7 @@ fn set_collection_owner_attributes_should_work() { bvec![0], )); assert_ok!(Nfts::set_attribute( - RuntimeOrigin::signed(1), + RuntimeOrigin::signed(account(1)), 0, Some(0), AttributeNamespace::CollectionOwner, @@ -659,11 +794,11 @@ fn set_collection_owner_attributes_should_work() { (Some(0), AttributeNamespace::CollectionOwner, bvec![1], bvec![0]), ] ); - assert_eq!(Balances::reserved_balance(1), 10); + assert_eq!(Balances::reserved_balance(account(1)), 10); assert_eq!(Collection::::get(0).unwrap().owner_deposit, 9); assert_ok!(Nfts::set_attribute( - RuntimeOrigin::signed(1), + RuntimeOrigin::signed(account(1)), 0, None, AttributeNamespace::CollectionOwner, @@ -678,11 +813,11 @@ fn set_collection_owner_attributes_should_work() { (Some(0), AttributeNamespace::CollectionOwner, bvec![1], bvec![0]), ] ); - assert_eq!(Balances::reserved_balance(1), 19); + assert_eq!(Balances::reserved_balance(account(1)), 19); assert_eq!(Collection::::get(0).unwrap().owner_deposit, 18); assert_ok!(Nfts::clear_attribute( - RuntimeOrigin::signed(1), + RuntimeOrigin::signed(account(1)), 0, Some(0), AttributeNamespace::CollectionOwner, @@ -695,33 +830,39 @@ fn set_collection_owner_attributes_should_work() { (Some(0), AttributeNamespace::CollectionOwner, bvec![0], bvec![0]), ] ); - assert_eq!(Balances::reserved_balance(1), 16); + assert_eq!(Balances::reserved_balance(account(1)), 16); let w = Nfts::get_destroy_witness(&0).unwrap(); - assert_ok!(Nfts::destroy(RuntimeOrigin::signed(1), 0, w)); + assert_ok!(Nfts::destroy(RuntimeOrigin::signed(account(1)), 0, w)); assert_eq!(attributes(0), vec![]); - assert_eq!(Balances::reserved_balance(1), 0); + assert_eq!(Balances::reserved_balance(account(1)), 0); }); } #[test] fn set_item_owner_attributes_should_work() { new_test_ext().execute_with(|| { - Balances::make_free_balance_be(&1, 100); - Balances::make_free_balance_be(&2, 100); - Balances::make_free_balance_be(&3, 100); + Balances::make_free_balance_be(&account(1), 100); + Balances::make_free_balance_be(&account(2), 100); + Balances::make_free_balance_be(&account(3), 100); assert_ok!(Nfts::force_create( RuntimeOrigin::root(), - 1, + account(1), collection_config_with_all_settings_enabled() )); - assert_ok!(Nfts::force_mint(RuntimeOrigin::signed(1), 0, 0, 2, default_item_config())); + assert_ok!(Nfts::force_mint( + RuntimeOrigin::signed(account(1)), + 0, + 0, + account(2), + default_item_config() + )); // can't set for the collection assert_noop!( Nfts::set_attribute( - RuntimeOrigin::signed(2), + RuntimeOrigin::signed(account(2)), 0, None, AttributeNamespace::ItemOwner, @@ -733,7 +874,7 @@ fn set_item_owner_attributes_should_work() { // can't set for the non-owned item assert_noop!( Nfts::set_attribute( - RuntimeOrigin::signed(1), + RuntimeOrigin::signed(account(1)), 0, Some(0), AttributeNamespace::ItemOwner, @@ -743,7 +884,7 @@ fn set_item_owner_attributes_should_work() { Error::::NoPermission, ); assert_ok!(Nfts::set_attribute( - RuntimeOrigin::signed(2), + RuntimeOrigin::signed(account(2)), 0, Some(0), AttributeNamespace::ItemOwner, @@ -751,7 +892,7 @@ fn set_item_owner_attributes_should_work() { bvec![0], )); assert_ok!(Nfts::set_attribute( - RuntimeOrigin::signed(2), + RuntimeOrigin::signed(account(2)), 0, Some(0), AttributeNamespace::ItemOwner, @@ -759,7 +900,7 @@ fn set_item_owner_attributes_should_work() { bvec![0], )); assert_ok!(Nfts::set_attribute( - RuntimeOrigin::signed(2), + RuntimeOrigin::signed(account(2)), 0, Some(0), AttributeNamespace::ItemOwner, @@ -774,11 +915,11 @@ fn set_item_owner_attributes_should_work() { (Some(0), AttributeNamespace::ItemOwner, bvec![2], bvec![0]), ] ); - assert_eq!(Balances::reserved_balance(2), 9); + assert_eq!(Balances::reserved_balance(account(2)), 9); // validate an attribute can be updated assert_ok!(Nfts::set_attribute( - RuntimeOrigin::signed(2), + RuntimeOrigin::signed(account(2)), 0, Some(0), AttributeNamespace::ItemOwner, @@ -793,12 +934,12 @@ fn set_item_owner_attributes_should_work() { (Some(0), AttributeNamespace::ItemOwner, bvec![2], bvec![0]), ] ); - assert_eq!(Balances::reserved_balance(2), 18); + assert_eq!(Balances::reserved_balance(account(2)), 18); // validate only item's owner (or the root) can remove an attribute assert_noop!( Nfts::clear_attribute( - RuntimeOrigin::signed(1), + RuntimeOrigin::signed(account(1)), 0, Some(0), AttributeNamespace::ItemOwner, @@ -807,7 +948,7 @@ fn set_item_owner_attributes_should_work() { Error::::NoPermission, ); assert_ok!(Nfts::clear_attribute( - RuntimeOrigin::signed(2), + RuntimeOrigin::signed(account(2)), 0, Some(0), AttributeNamespace::ItemOwner, @@ -820,10 +961,10 @@ fn set_item_owner_attributes_should_work() { (Some(0), AttributeNamespace::ItemOwner, bvec![2], bvec![0]) ] ); - assert_eq!(Balances::reserved_balance(2), 15); + assert_eq!(Balances::reserved_balance(account(2)), 15); // transfer item - assert_ok!(Nfts::transfer(RuntimeOrigin::signed(2), 0, 0, 3)); + assert_ok!(Nfts::transfer(RuntimeOrigin::signed(account(2)), 0, 0, account(3))); // validate the attribute are still here & the deposit belongs to the previous owner assert_eq!( @@ -836,12 +977,12 @@ fn set_item_owner_attributes_should_work() { let key: BoundedVec<_, _> = bvec![0]; let (_, deposit) = Attribute::::get((0, Some(0), AttributeNamespace::ItemOwner, &key)).unwrap(); - assert_eq!(deposit.account, Some(2)); + assert_eq!(deposit.account, Some(account(2))); assert_eq!(deposit.amount, 12); // on attribute update the deposit should be returned to the previous owner assert_ok!(Nfts::set_attribute( - RuntimeOrigin::signed(3), + RuntimeOrigin::signed(account(3)), 0, Some(0), AttributeNamespace::ItemOwner, @@ -850,13 +991,13 @@ fn set_item_owner_attributes_should_work() { )); let (_, deposit) = Attribute::::get((0, Some(0), AttributeNamespace::ItemOwner, &key)).unwrap(); - assert_eq!(deposit.account, Some(3)); + assert_eq!(deposit.account, Some(account(3))); assert_eq!(deposit.amount, 13); - assert_eq!(Balances::reserved_balance(2), 3); - assert_eq!(Balances::reserved_balance(3), 13); + assert_eq!(Balances::reserved_balance(account(2)), 3); + assert_eq!(Balances::reserved_balance(account(3)), 13); // validate attributes on item deletion - assert_ok!(Nfts::burn(RuntimeOrigin::signed(3), 0, 0, None)); + assert_ok!(Nfts::burn(RuntimeOrigin::signed(account(3)), 0, 0, None)); assert_eq!( attributes(0), vec![ @@ -865,90 +1006,101 @@ fn set_item_owner_attributes_should_work() { ] ); assert_ok!(Nfts::clear_attribute( - RuntimeOrigin::signed(3), + RuntimeOrigin::signed(account(3)), 0, Some(0), AttributeNamespace::ItemOwner, bvec![0], )); assert_ok!(Nfts::clear_attribute( - RuntimeOrigin::signed(2), + RuntimeOrigin::signed(account(2)), 0, Some(0), AttributeNamespace::ItemOwner, bvec![2], )); - assert_eq!(Balances::reserved_balance(2), 0); - assert_eq!(Balances::reserved_balance(3), 0); + assert_eq!(Balances::reserved_balance(account(2)), 0); + assert_eq!(Balances::reserved_balance(account(3)), 0); }); } #[test] fn set_external_account_attributes_should_work() { new_test_ext().execute_with(|| { - Balances::make_free_balance_be(&1, 100); - Balances::make_free_balance_be(&2, 100); + Balances::make_free_balance_be(&account(1), 100); + Balances::make_free_balance_be(&account(2), 100); assert_ok!(Nfts::force_create( RuntimeOrigin::root(), - 1, + account(1), collection_config_with_all_settings_enabled() )); - assert_ok!(Nfts::force_mint(RuntimeOrigin::signed(1), 0, 0, 1, default_item_config())); - assert_ok!(Nfts::approve_item_attributes(RuntimeOrigin::signed(1), 0, 0, 2)); + assert_ok!(Nfts::force_mint( + RuntimeOrigin::signed(account(1)), + 0, + 0, + account(1), + default_item_config() + )); + assert_ok!(Nfts::approve_item_attributes( + RuntimeOrigin::signed(account(1)), + 0, + 0, + account(2) + )); assert_noop!( Nfts::set_attribute( - RuntimeOrigin::signed(2), + RuntimeOrigin::signed(account(2)), 0, Some(0), - AttributeNamespace::Account(1), + AttributeNamespace::Account(account(1)), bvec![0], bvec![0], ), Error::::NoPermission, ); assert_ok!(Nfts::set_attribute( - RuntimeOrigin::signed(2), + RuntimeOrigin::signed(account(2)), 0, Some(0), - AttributeNamespace::Account(2), + AttributeNamespace::Account(account(2)), bvec![0], bvec![0], )); assert_ok!(Nfts::set_attribute( - RuntimeOrigin::signed(2), + RuntimeOrigin::signed(account(2)), 0, Some(0), - AttributeNamespace::Account(2), + AttributeNamespace::Account(account(2)), bvec![1], bvec![0], )); assert_eq!( attributes(0), vec![ - (Some(0), AttributeNamespace::Account(2), bvec![0], bvec![0]), - (Some(0), AttributeNamespace::Account(2), bvec![1], bvec![0]), + (Some(0), AttributeNamespace::Account(account(2)), bvec![0], bvec![0]), + (Some(0), AttributeNamespace::Account(account(2)), bvec![1], bvec![0]), ] ); - assert_eq!(Balances::reserved_balance(2), 6); + assert_eq!(Balances::reserved_balance(account(2)), 6); // remove permission to set attributes assert_ok!(Nfts::cancel_item_attributes_approval( - RuntimeOrigin::signed(1), + RuntimeOrigin::signed(account(1)), 0, 0, - 2, + account(2), CancelAttributesApprovalWitness { account_attributes: 2 }, )); assert_eq!(attributes(0), vec![]); - assert_eq!(Balances::reserved_balance(2), 0); + assert_eq!(Balances::reserved_balance(account(2)), 0); assert_noop!( Nfts::set_attribute( - RuntimeOrigin::signed(2), + RuntimeOrigin::signed(account(2)), 0, Some(0), - AttributeNamespace::Account(2), + AttributeNamespace::Account(account(2)), bvec![0], bvec![0], ), @@ -960,18 +1112,33 @@ fn set_external_account_attributes_should_work() { #[test] fn validate_deposit_required_setting() { new_test_ext().execute_with(|| { - Balances::make_free_balance_be(&1, 100); - Balances::make_free_balance_be(&2, 100); - Balances::make_free_balance_be(&3, 100); + Balances::make_free_balance_be(&account(1), 100); + Balances::make_free_balance_be(&account(2), 100); + Balances::make_free_balance_be(&account(3), 100); // with the disabled DepositRequired setting, only the collection's owner can set the // attributes for free. - assert_ok!(Nfts::force_create(RuntimeOrigin::root(), 1, default_collection_config())); - assert_ok!(Nfts::force_mint(RuntimeOrigin::signed(1), 0, 0, 2, default_item_config())); - assert_ok!(Nfts::approve_item_attributes(RuntimeOrigin::signed(2), 0, 0, 3)); + assert_ok!(Nfts::force_create( + RuntimeOrigin::root(), + account(1), + default_collection_config() + )); + assert_ok!(Nfts::force_mint( + RuntimeOrigin::signed(account(1)), + 0, + 0, + account(2), + default_item_config() + )); + assert_ok!(Nfts::approve_item_attributes( + RuntimeOrigin::signed(account(2)), + 0, + 0, + account(3) + )); assert_ok!(Nfts::set_attribute( - RuntimeOrigin::signed(1), + RuntimeOrigin::signed(account(1)), 0, Some(0), AttributeNamespace::CollectionOwner, @@ -979,7 +1146,7 @@ fn validate_deposit_required_setting() { bvec![0], )); assert_ok!(Nfts::set_attribute( - RuntimeOrigin::signed(2), + RuntimeOrigin::signed(account(2)), 0, Some(0), AttributeNamespace::ItemOwner, @@ -987,10 +1154,10 @@ fn validate_deposit_required_setting() { bvec![0], )); assert_ok!(Nfts::set_attribute( - RuntimeOrigin::signed(3), + RuntimeOrigin::signed(account(3)), 0, Some(0), - AttributeNamespace::Account(3), + AttributeNamespace::Account(account(3)), bvec![2], bvec![0], )); @@ -1005,13 +1172,13 @@ fn validate_deposit_required_setting() { vec![ (Some(0), AttributeNamespace::CollectionOwner, bvec![0], bvec![0]), (Some(0), AttributeNamespace::ItemOwner, bvec![1], bvec![0]), - (Some(0), AttributeNamespace::Account(3), bvec![2], bvec![0]), + (Some(0), AttributeNamespace::Account(account(3)), bvec![2], bvec![0]), (Some(0), AttributeNamespace::Pallet, bvec![3], bvec![0]), ] ); - assert_eq!(Balances::reserved_balance(1), 0); - assert_eq!(Balances::reserved_balance(2), 3); - assert_eq!(Balances::reserved_balance(3), 3); + assert_eq!(Balances::reserved_balance(account(1)), 0); + assert_eq!(Balances::reserved_balance(account(2)), 3); + assert_eq!(Balances::reserved_balance(account(3)), 3); assert_ok!( ::AccountId, ItemConfig>>::clear_attribute( @@ -1025,7 +1192,7 @@ fn validate_deposit_required_setting() { vec![ (Some(0), AttributeNamespace::CollectionOwner, bvec![0], bvec![0]), (Some(0), AttributeNamespace::ItemOwner, bvec![1], bvec![0]), - (Some(0), AttributeNamespace::Account(3), bvec![2], bvec![0]), + (Some(0), AttributeNamespace::Account(account(3)), bvec![2], bvec![0]), ] ); }); @@ -1034,18 +1201,18 @@ fn validate_deposit_required_setting() { #[test] fn set_attribute_should_respect_lock() { new_test_ext().execute_with(|| { - Balances::make_free_balance_be(&1, 100); + Balances::make_free_balance_be(&account(1), 100); assert_ok!(Nfts::force_create( RuntimeOrigin::root(), - 1, + account(1), collection_config_with_all_settings_enabled(), )); - assert_ok!(Nfts::mint(RuntimeOrigin::signed(1), 0, 0, 1, None)); - assert_ok!(Nfts::mint(RuntimeOrigin::signed(1), 0, 1, 1, None)); + assert_ok!(Nfts::mint(RuntimeOrigin::signed(account(1)), 0, 0, account(1), None)); + assert_ok!(Nfts::mint(RuntimeOrigin::signed(account(1)), 0, 1, account(1), None)); assert_ok!(Nfts::set_attribute( - RuntimeOrigin::signed(1), + RuntimeOrigin::signed(account(1)), 0, None, AttributeNamespace::CollectionOwner, @@ -1053,7 +1220,7 @@ fn set_attribute_should_respect_lock() { bvec![0], )); assert_ok!(Nfts::set_attribute( - RuntimeOrigin::signed(1), + RuntimeOrigin::signed(account(1)), 0, Some(0), AttributeNamespace::CollectionOwner, @@ -1061,7 +1228,7 @@ fn set_attribute_should_respect_lock() { bvec![0], )); assert_ok!(Nfts::set_attribute( - RuntimeOrigin::signed(1), + RuntimeOrigin::signed(account(1)), 0, Some(1), AttributeNamespace::CollectionOwner, @@ -1076,11 +1243,11 @@ fn set_attribute_should_respect_lock() { (Some(1), AttributeNamespace::CollectionOwner, bvec![0], bvec![0]), ] ); - assert_eq!(Balances::reserved_balance(1), 11); + assert_eq!(Balances::reserved_balance(account(1)), 11); - assert_ok!(Nfts::set_collection_metadata(RuntimeOrigin::signed(1), 0, bvec![])); + assert_ok!(Nfts::set_collection_metadata(RuntimeOrigin::signed(account(1)), 0, bvec![])); assert_ok!(Nfts::lock_collection( - RuntimeOrigin::signed(1), + RuntimeOrigin::signed(account(1)), 0, CollectionSettings::from_disabled(CollectionSetting::UnlockedAttributes.into()) )); @@ -1088,7 +1255,7 @@ fn set_attribute_should_respect_lock() { let e = Error::::LockedCollectionAttributes; assert_noop!( Nfts::set_attribute( - RuntimeOrigin::signed(1), + RuntimeOrigin::signed(account(1)), 0, None, AttributeNamespace::CollectionOwner, @@ -1098,7 +1265,7 @@ fn set_attribute_should_respect_lock() { e ); assert_ok!(Nfts::set_attribute( - RuntimeOrigin::signed(1), + RuntimeOrigin::signed(account(1)), 0, Some(0), AttributeNamespace::CollectionOwner, @@ -1106,11 +1273,17 @@ fn set_attribute_should_respect_lock() { bvec![1], )); - assert_ok!(Nfts::lock_item_properties(RuntimeOrigin::signed(1), 0, 0, false, true)); + assert_ok!(Nfts::lock_item_properties( + RuntimeOrigin::signed(account(1)), + 0, + 0, + false, + true + )); let e = Error::::LockedItemAttributes; assert_noop!( Nfts::set_attribute( - RuntimeOrigin::signed(1), + RuntimeOrigin::signed(account(1)), 0, Some(0), AttributeNamespace::CollectionOwner, @@ -1120,7 +1293,7 @@ fn set_attribute_should_respect_lock() { e ); assert_ok!(Nfts::set_attribute( - RuntimeOrigin::signed(1), + RuntimeOrigin::signed(account(1)), 0, Some(1), AttributeNamespace::CollectionOwner, @@ -1133,22 +1306,22 @@ fn set_attribute_should_respect_lock() { #[test] fn preserve_config_for_frozen_items() { new_test_ext().execute_with(|| { - Balances::make_free_balance_be(&1, 100); + Balances::make_free_balance_be(&account(1), 100); assert_ok!(Nfts::force_create( RuntimeOrigin::root(), - 1, + account(1), collection_config_with_all_settings_enabled() )); - assert_ok!(Nfts::mint(RuntimeOrigin::signed(1), 0, 0, 1, None)); - assert_ok!(Nfts::mint(RuntimeOrigin::signed(1), 0, 1, 1, None)); + assert_ok!(Nfts::mint(RuntimeOrigin::signed(account(1)), 0, 0, account(1), None)); + assert_ok!(Nfts::mint(RuntimeOrigin::signed(account(1)), 0, 1, account(1), None)); // if the item is not locked/frozen then the config gets deleted on item burn - assert_ok!(Nfts::burn(RuntimeOrigin::signed(1), 0, 1, Some(1))); + assert_ok!(Nfts::burn(RuntimeOrigin::signed(account(1)), 0, 1, Some(account(1)))); assert!(!ItemConfigOf::::contains_key(0, 1)); // lock the item and ensure the config stays unchanged - assert_ok!(Nfts::lock_item_properties(RuntimeOrigin::signed(1), 0, 0, true, true)); + assert_ok!(Nfts::lock_item_properties(RuntimeOrigin::signed(account(1)), 0, 0, true, true)); let expect_config = item_config_from_disabled_settings( ItemSetting::UnlockedAttributes | ItemSetting::UnlockedMetadata, @@ -1156,18 +1329,24 @@ fn preserve_config_for_frozen_items() { let config = ItemConfigOf::::get(0, 0).unwrap(); assert_eq!(config, expect_config); - assert_ok!(Nfts::burn(RuntimeOrigin::signed(1), 0, 0, Some(1))); + assert_ok!(Nfts::burn(RuntimeOrigin::signed(account(1)), 0, 0, Some(account(1)))); let config = ItemConfigOf::::get(0, 0).unwrap(); assert_eq!(config, expect_config); // can't mint with the different config assert_noop!( - Nfts::force_mint(RuntimeOrigin::signed(1), 0, 0, 1, default_item_config()), + Nfts::force_mint( + RuntimeOrigin::signed(account(1)), + 0, + 0, + account(2), + default_item_config() + ), Error::::InconsistentItemConfig ); assert_ok!(Nfts::update_mint_settings( - RuntimeOrigin::signed(1), + RuntimeOrigin::signed(account(1)), 0, MintSettings { default_item_settings: ItemSettings::from_disabled( @@ -1176,26 +1355,36 @@ fn preserve_config_for_frozen_items() { ..Default::default() } )); - assert_ok!(Nfts::mint(RuntimeOrigin::signed(1), 0, 0, 1, None)); + assert_ok!(Nfts::mint(RuntimeOrigin::signed(account(1)), 0, 0, account(1), None)); }); } #[test] fn force_update_collection_should_work() { new_test_ext().execute_with(|| { - Balances::make_free_balance_be(&1, 100); + Balances::make_free_balance_be(&account(1), 100); assert_ok!(Nfts::force_create( RuntimeOrigin::root(), - 1, + account(1), collection_config_with_all_settings_enabled() )); - assert_ok!(Nfts::mint(RuntimeOrigin::signed(1), 0, 42, 1, None)); - assert_ok!(Nfts::force_mint(RuntimeOrigin::signed(1), 0, 69, 2, default_item_config())); - assert_ok!(Nfts::set_collection_metadata(RuntimeOrigin::signed(1), 0, bvec![0; 20])); - assert_ok!(Nfts::set_metadata(RuntimeOrigin::signed(1), 0, 42, bvec![0; 20])); - assert_ok!(Nfts::set_metadata(RuntimeOrigin::signed(1), 0, 69, bvec![0; 20])); - assert_eq!(Balances::reserved_balance(1), 65); + assert_ok!(Nfts::mint(RuntimeOrigin::signed(account(1)), 0, 42, account(1), None)); + assert_ok!(Nfts::force_mint( + RuntimeOrigin::signed(account(1)), + 0, + 69, + account(2), + default_item_config(), + )); + assert_ok!(Nfts::set_collection_metadata( + RuntimeOrigin::signed(account(1)), + 0, + bvec![0; 20] + )); + assert_ok!(Nfts::set_metadata(RuntimeOrigin::signed(account(1)), 0, 42, bvec![0; 20])); + assert_ok!(Nfts::set_metadata(RuntimeOrigin::signed(account(1)), 0, 69, bvec![0; 20])); + assert_eq!(Balances::reserved_balance(account(1)), 65); // force item status to be free holding assert_ok!(Nfts::force_collection_config( @@ -1203,52 +1392,66 @@ fn force_update_collection_should_work() { 0, collection_config_from_disabled_settings(CollectionSetting::DepositRequired.into()), )); - assert_ok!(Nfts::mint(RuntimeOrigin::signed(1), 0, 142, 1, None)); - assert_ok!(Nfts::force_mint(RuntimeOrigin::signed(1), 0, 169, 2, default_item_config())); - assert_ok!(Nfts::set_metadata(RuntimeOrigin::signed(1), 0, 142, bvec![0; 20])); - assert_ok!(Nfts::set_metadata(RuntimeOrigin::signed(1), 0, 169, bvec![0; 20])); + assert_ok!(Nfts::mint(RuntimeOrigin::signed(account(1)), 0, 142, account(1), None)); + assert_ok!(Nfts::force_mint( + RuntimeOrigin::signed(account(1)), + 0, + 169, + account(2), + default_item_config(), + )); + assert_ok!(Nfts::set_metadata(RuntimeOrigin::signed(account(1)), 0, 142, bvec![0; 20])); + assert_ok!(Nfts::set_metadata(RuntimeOrigin::signed(account(1)), 0, 169, bvec![0; 20])); - Balances::make_free_balance_be(&5, 100); - assert_ok!(Nfts::force_collection_owner(RuntimeOrigin::root(), 0, 5)); - assert_eq!(collections(), vec![(5, 0)]); - assert_eq!(Balances::reserved_balance(1), 2); - assert_eq!(Balances::reserved_balance(5), 63); + Balances::make_free_balance_be(&account(5), 100); + assert_ok!(Nfts::force_collection_owner(RuntimeOrigin::root(), 0, account(5))); + assert_eq!(collections(), vec![(account(5), 0)]); + assert_eq!(Balances::reserved_balance(account(1)), 2); + assert_eq!(Balances::reserved_balance(account(5)), 63); - assert_ok!(Nfts::redeposit(RuntimeOrigin::signed(5), 0, bvec![0, 42, 50, 69, 100])); - assert_eq!(Balances::reserved_balance(1), 0); + assert_ok!(Nfts::redeposit( + RuntimeOrigin::signed(account(5)), + 0, + bvec![0, 42, 50, 69, 100] + )); + assert_eq!(Balances::reserved_balance(account(1)), 0); - assert_ok!(Nfts::set_metadata(RuntimeOrigin::signed(5), 0, 42, bvec![0; 20])); - assert_eq!(Balances::reserved_balance(5), 42); + assert_ok!(Nfts::set_metadata(RuntimeOrigin::signed(account(5)), 0, 42, bvec![0; 20])); + assert_eq!(Balances::reserved_balance(account(5)), 42); - assert_ok!(Nfts::set_metadata(RuntimeOrigin::signed(5), 0, 69, bvec![0; 20])); - assert_eq!(Balances::reserved_balance(5), 21); + assert_ok!(Nfts::set_metadata(RuntimeOrigin::signed(account(5)), 0, 69, bvec![0; 20])); + assert_eq!(Balances::reserved_balance(account(5)), 21); - assert_ok!(Nfts::set_collection_metadata(RuntimeOrigin::signed(5), 0, bvec![0; 20])); - assert_eq!(Balances::reserved_balance(5), 0); + assert_ok!(Nfts::set_collection_metadata( + RuntimeOrigin::signed(account(5)), + 0, + bvec![0; 20] + )); + assert_eq!(Balances::reserved_balance(account(5)), 0); // validate new roles - assert_ok!(Nfts::set_team(RuntimeOrigin::root(), 0, 2, 3, 4)); + assert_ok!(Nfts::set_team(RuntimeOrigin::root(), 0, account(2), account(3), account(4))); assert_eq!( - CollectionRoleOf::::get(0, 2).unwrap(), + CollectionRoleOf::::get(0, account(2)).unwrap(), CollectionRoles(CollectionRole::Issuer.into()) ); assert_eq!( - CollectionRoleOf::::get(0, 3).unwrap(), + CollectionRoleOf::::get(0, account(3)).unwrap(), CollectionRoles(CollectionRole::Admin.into()) ); assert_eq!( - CollectionRoleOf::::get(0, 4).unwrap(), + CollectionRoleOf::::get(0, account(4)).unwrap(), CollectionRoles(CollectionRole::Freezer.into()) ); - assert_ok!(Nfts::set_team(RuntimeOrigin::root(), 0, 3, 2, 3)); + assert_ok!(Nfts::set_team(RuntimeOrigin::root(), 0, account(3), account(2), account(3))); assert_eq!( - CollectionRoleOf::::get(0, 2).unwrap(), + CollectionRoleOf::::get(0, account(2)).unwrap(), CollectionRoles(CollectionRole::Admin.into()) ); assert_eq!( - CollectionRoleOf::::get(0, 3).unwrap(), + CollectionRoleOf::::get(0, account(3)).unwrap(), CollectionRoles(CollectionRole::Issuer | CollectionRole::Freezer) ); }); @@ -1257,68 +1460,120 @@ fn force_update_collection_should_work() { #[test] fn burn_works() { new_test_ext().execute_with(|| { - Balances::make_free_balance_be(&1, 100); + Balances::make_free_balance_be(&account(1), 100); assert_ok!(Nfts::force_create( RuntimeOrigin::root(), - 1, + account(1), collection_config_with_all_settings_enabled() )); - assert_ok!(Nfts::set_team(RuntimeOrigin::signed(1), 0, 2, 3, 4)); + assert_ok!(Nfts::set_team( + RuntimeOrigin::signed(account(1)), + 0, + account(2), + account(3), + account(4), + )); assert_noop!( - Nfts::burn(RuntimeOrigin::signed(5), 0, 42, Some(5)), + Nfts::burn(RuntimeOrigin::signed(account(5)), 0, 42, Some(account(5))), Error::::UnknownItem ); - assert_ok!(Nfts::force_mint(RuntimeOrigin::signed(2), 0, 42, 5, default_item_config())); - assert_ok!(Nfts::force_mint(RuntimeOrigin::signed(2), 0, 69, 5, default_item_config())); - assert_eq!(Balances::reserved_balance(1), 2); + assert_ok!(Nfts::force_mint( + RuntimeOrigin::signed(account(2)), + 0, + 42, + account(5), + default_item_config() + )); + assert_ok!(Nfts::force_mint( + RuntimeOrigin::signed(account(2)), + 0, + 69, + account(5), + default_item_config() + )); + assert_eq!(Balances::reserved_balance(account(1)), 2); assert_noop!( - Nfts::burn(RuntimeOrigin::signed(0), 0, 42, None), + Nfts::burn(RuntimeOrigin::signed(account(0)), 0, 42, None), Error::::NoPermission ); assert_noop!( - Nfts::burn(RuntimeOrigin::signed(5), 0, 42, Some(6)), + Nfts::burn(RuntimeOrigin::signed(account(5)), 0, 42, Some(account(6))), Error::::WrongOwner ); - assert_ok!(Nfts::burn(RuntimeOrigin::signed(5), 0, 42, Some(5))); - assert_ok!(Nfts::burn(RuntimeOrigin::signed(3), 0, 69, Some(5))); - assert_eq!(Balances::reserved_balance(1), 0); + assert_ok!(Nfts::burn(RuntimeOrigin::signed(account(5)), 0, 42, Some(account(5)))); + assert_ok!(Nfts::burn(RuntimeOrigin::signed(account(3)), 0, 69, Some(account(5)))); + assert_eq!(Balances::reserved_balance(account(1)), 0); }); } #[test] fn approval_lifecycle_works() { new_test_ext().execute_with(|| { - assert_ok!(Nfts::force_create(RuntimeOrigin::root(), 1, default_collection_config())); - assert_ok!(Nfts::force_mint(RuntimeOrigin::signed(1), 0, 42, 2, default_item_config())); - assert_ok!(Nfts::approve_transfer(RuntimeOrigin::signed(2), 0, 42, 3, None)); - assert_ok!(Nfts::transfer(RuntimeOrigin::signed(3), 0, 42, 4)); + assert_ok!(Nfts::force_create( + RuntimeOrigin::root(), + account(1), + default_collection_config() + )); + assert_ok!(Nfts::force_mint( + RuntimeOrigin::signed(account(1)), + 0, + 42, + account(2), + default_item_config() + )); + assert_ok!(Nfts::approve_transfer( + RuntimeOrigin::signed(account(2)), + 0, + 42, + account(3), + None + )); + assert_ok!(Nfts::transfer(RuntimeOrigin::signed(account(3)), 0, 42, account(4))); assert_noop!( - Nfts::transfer(RuntimeOrigin::signed(3), 0, 42, 3), + Nfts::transfer(RuntimeOrigin::signed(account(3)), 0, 42, account(3)), Error::::NoPermission ); assert!(Item::::get(0, 42).unwrap().approvals.is_empty()); - assert_ok!(Nfts::approve_transfer(RuntimeOrigin::signed(4), 0, 42, 2, None)); - assert_ok!(Nfts::transfer(RuntimeOrigin::signed(2), 0, 42, 2)); + assert_ok!(Nfts::approve_transfer( + RuntimeOrigin::signed(account(4)), + 0, + 42, + account(2), + None + )); + assert_ok!(Nfts::transfer(RuntimeOrigin::signed(account(2)), 0, 42, account(2))); // ensure we can't buy an item when the collection has a NonTransferableItems flag let collection_id = 1; assert_ok!(Nfts::force_create( RuntimeOrigin::root(), - 1, + account(1), collection_config_from_disabled_settings( CollectionSetting::TransferableItems | CollectionSetting::DepositRequired ) )); - assert_ok!(Nfts::mint(RuntimeOrigin::signed(1), 1, collection_id, 1, None)); + assert_ok!(Nfts::mint( + RuntimeOrigin::signed(account(1)), + 1, + collection_id, + account(1), + None, + )); assert_noop!( - Nfts::approve_transfer(RuntimeOrigin::signed(1), collection_id, 1, 2, None), + Nfts::approve_transfer( + RuntimeOrigin::signed(account(1)), + collection_id, + 1, + account(2), + None + ), Error::::ItemsNonTransferable ); }); @@ -1327,46 +1582,74 @@ fn approval_lifecycle_works() { #[test] fn cancel_approval_works() { new_test_ext().execute_with(|| { - assert_ok!(Nfts::force_create(RuntimeOrigin::root(), 1, default_collection_config())); - assert_ok!(Nfts::force_mint(RuntimeOrigin::signed(1), 0, 42, 2, default_item_config())); + assert_ok!(Nfts::force_create( + RuntimeOrigin::root(), + account(1), + default_collection_config() + )); + assert_ok!(Nfts::force_mint( + RuntimeOrigin::signed(account(1)), + 0, + 42, + account(2), + default_item_config() + )); - assert_ok!(Nfts::approve_transfer(RuntimeOrigin::signed(2), 0, 42, 3, None)); + assert_ok!(Nfts::approve_transfer( + RuntimeOrigin::signed(account(2)), + 0, + 42, + account(3), + None + )); assert_noop!( - Nfts::cancel_approval(RuntimeOrigin::signed(2), 1, 42, 3), + Nfts::cancel_approval(RuntimeOrigin::signed(account(2)), 1, 42, account(3)), Error::::UnknownItem ); assert_noop!( - Nfts::cancel_approval(RuntimeOrigin::signed(2), 0, 43, 3), + Nfts::cancel_approval(RuntimeOrigin::signed(account(2)), 0, 43, account(3)), Error::::UnknownItem ); assert_noop!( - Nfts::cancel_approval(RuntimeOrigin::signed(3), 0, 42, 3), + Nfts::cancel_approval(RuntimeOrigin::signed(account(3)), 0, 42, account(3)), Error::::NoPermission ); assert_noop!( - Nfts::cancel_approval(RuntimeOrigin::signed(2), 0, 42, 4), + Nfts::cancel_approval(RuntimeOrigin::signed(account(2)), 0, 42, account(4)), Error::::NotDelegate ); - assert_ok!(Nfts::cancel_approval(RuntimeOrigin::signed(2), 0, 42, 3)); + assert_ok!(Nfts::cancel_approval(RuntimeOrigin::signed(account(2)), 0, 42, account(3))); assert_noop!( - Nfts::cancel_approval(RuntimeOrigin::signed(2), 0, 42, 3), + Nfts::cancel_approval(RuntimeOrigin::signed(account(2)), 0, 42, account(3)), Error::::NotDelegate ); let current_block = 1; System::set_block_number(current_block); - assert_ok!(Nfts::force_mint(RuntimeOrigin::signed(1), 0, 69, 2, default_item_config())); + assert_ok!(Nfts::force_mint( + RuntimeOrigin::signed(account(1)), + 0, + 69, + account(2), + default_item_config() + )); // approval expires after 2 blocks. - assert_ok!(Nfts::approve_transfer(RuntimeOrigin::signed(2), 0, 42, 3, Some(2))); + assert_ok!(Nfts::approve_transfer( + RuntimeOrigin::signed(account(2)), + 0, + 42, + account(3), + Some(2) + )); assert_noop!( - Nfts::cancel_approval(RuntimeOrigin::signed(5), 0, 42, 3), + Nfts::cancel_approval(RuntimeOrigin::signed(account(5)), 0, 42, account(3)), Error::::NoPermission ); System::set_block_number(current_block + 3); // 5 can cancel the approval since the deadline has passed. - assert_ok!(Nfts::cancel_approval(RuntimeOrigin::signed(5), 0, 42, 3)); + assert_ok!(Nfts::cancel_approval(RuntimeOrigin::signed(account(5)), 0, 42, account(3))); assert_eq!(approvals(0, 69), vec![]); }); } @@ -1374,23 +1657,54 @@ fn cancel_approval_works() { #[test] fn approving_multiple_accounts_works() { new_test_ext().execute_with(|| { - assert_ok!(Nfts::force_create(RuntimeOrigin::root(), 1, default_collection_config())); - assert_ok!(Nfts::force_mint(RuntimeOrigin::signed(1), 0, 42, 2, default_item_config())); + assert_ok!(Nfts::force_create( + RuntimeOrigin::root(), + account(1), + default_collection_config() + )); + assert_ok!(Nfts::force_mint( + RuntimeOrigin::signed(account(1)), + 0, + 42, + account(2), + default_item_config() + )); let current_block = 1; System::set_block_number(current_block); - assert_ok!(Nfts::approve_transfer(RuntimeOrigin::signed(2), 0, 42, 3, None)); - assert_ok!(Nfts::approve_transfer(RuntimeOrigin::signed(2), 0, 42, 4, None)); - assert_ok!(Nfts::approve_transfer(RuntimeOrigin::signed(2), 0, 42, 5, Some(2))); - assert_eq!(approvals(0, 42), vec![(3, None), (4, None), (5, Some(current_block + 2))]); + assert_ok!(Nfts::approve_transfer( + RuntimeOrigin::signed(account(2)), + 0, + 42, + account(3), + None + )); + assert_ok!(Nfts::approve_transfer( + RuntimeOrigin::signed(account(2)), + 0, + 42, + account(4), + None + )); + assert_ok!(Nfts::approve_transfer( + RuntimeOrigin::signed(account(2)), + 0, + 42, + account(5), + Some(2) + )); + assert_eq!( + approvals(0, 42), + vec![(account(3), None), (account(4), None), (account(5), Some(current_block + 2))] + ); - assert_ok!(Nfts::transfer(RuntimeOrigin::signed(4), 0, 42, 6)); + assert_ok!(Nfts::transfer(RuntimeOrigin::signed(account(4)), 0, 42, account(6))); assert_noop!( - Nfts::transfer(RuntimeOrigin::signed(3), 0, 42, 7), + Nfts::transfer(RuntimeOrigin::signed(account(3)), 0, 42, account(7)), Error::::NoPermission ); assert_noop!( - Nfts::transfer(RuntimeOrigin::signed(5), 0, 42, 8), + Nfts::transfer(RuntimeOrigin::signed(account(5)), 0, 42, account(8)), Error::::NoPermission ); }); @@ -1399,15 +1713,31 @@ fn approving_multiple_accounts_works() { #[test] fn approvals_limit_works() { new_test_ext().execute_with(|| { - assert_ok!(Nfts::force_create(RuntimeOrigin::root(), 1, default_collection_config())); - assert_ok!(Nfts::force_mint(RuntimeOrigin::signed(1), 0, 42, 2, default_item_config())); + assert_ok!(Nfts::force_create( + RuntimeOrigin::root(), + account(1), + default_collection_config() + )); + assert_ok!(Nfts::force_mint( + RuntimeOrigin::signed(account(1)), + 0, + 42, + account(2), + default_item_config() + )); for i in 3..13 { - assert_ok!(Nfts::approve_transfer(RuntimeOrigin::signed(2), 0, 42, i, None)); + assert_ok!(Nfts::approve_transfer( + RuntimeOrigin::signed(account(2)), + 0, + 42, + account(i), + None + )); } // the limit is 10 assert_noop!( - Nfts::approve_transfer(RuntimeOrigin::signed(2), 0, 42, 14, None), + Nfts::approve_transfer(RuntimeOrigin::signed(account(2)), 0, 42, account(14), None), Error::::ReachedApprovalLimit ); }); @@ -1421,55 +1751,89 @@ fn approval_deadline_works() { assert_ok!(Nfts::force_create( RuntimeOrigin::root(), - 1, + account(1), collection_config_from_disabled_settings(CollectionSetting::DepositRequired.into()) )); - assert_ok!(Nfts::force_mint(RuntimeOrigin::signed(1), 0, 42, 2, default_item_config())); + assert_ok!(Nfts::force_mint( + RuntimeOrigin::signed(account(1)), + 0, + 42, + account(2), + default_item_config() + )); // the approval expires after the 2nd block. - assert_ok!(Nfts::approve_transfer(RuntimeOrigin::signed(2), 0, 42, 3, Some(2))); + assert_ok!(Nfts::approve_transfer( + RuntimeOrigin::signed(account(2)), + 0, + 42, + account(3), + Some(2) + )); System::set_block_number(3); assert_noop!( - Nfts::transfer(RuntimeOrigin::signed(3), 0, 42, 4), + Nfts::transfer(RuntimeOrigin::signed(account(3)), 0, 42, account(4)), Error::::ApprovalExpired ); System::set_block_number(1); - assert_ok!(Nfts::transfer(RuntimeOrigin::signed(3), 0, 42, 4)); + assert_ok!(Nfts::transfer(RuntimeOrigin::signed(account(3)), 0, 42, account(4))); assert_eq!(System::block_number(), 1); // make a new approval with a deadline after 4 blocks, so it will expire after the 5th // block. - assert_ok!(Nfts::approve_transfer(RuntimeOrigin::signed(4), 0, 42, 6, Some(4))); + assert_ok!(Nfts::approve_transfer( + RuntimeOrigin::signed(account(4)), + 0, + 42, + account(6), + Some(4) + )); // this should still work. System::set_block_number(5); - assert_ok!(Nfts::transfer(RuntimeOrigin::signed(6), 0, 42, 5)); + assert_ok!(Nfts::transfer(RuntimeOrigin::signed(account(6)), 0, 42, account(5))); }); } #[test] fn cancel_approval_works_with_admin() { new_test_ext().execute_with(|| { - assert_ok!(Nfts::force_create(RuntimeOrigin::root(), 1, default_collection_config())); - assert_ok!(Nfts::force_mint(RuntimeOrigin::signed(1), 0, 42, 2, default_item_config())); + assert_ok!(Nfts::force_create( + RuntimeOrigin::root(), + account(1), + default_collection_config() + )); + assert_ok!(Nfts::force_mint( + RuntimeOrigin::signed(account(1)), + 0, + 42, + account(2), + default_item_config() + )); - assert_ok!(Nfts::approve_transfer(RuntimeOrigin::signed(2), 0, 42, 3, None)); + assert_ok!(Nfts::approve_transfer( + RuntimeOrigin::signed(account(2)), + 0, + 42, + account(3), + None + )); assert_noop!( - Nfts::cancel_approval(RuntimeOrigin::signed(1), 1, 42, 1), + Nfts::cancel_approval(RuntimeOrigin::signed(account(1)), 1, 42, account(1)), Error::::UnknownItem ); assert_noop!( - Nfts::cancel_approval(RuntimeOrigin::signed(1), 0, 43, 1), + Nfts::cancel_approval(RuntimeOrigin::signed(account(1)), 0, 43, account(1)), Error::::UnknownItem ); assert_noop!( - Nfts::cancel_approval(RuntimeOrigin::signed(1), 0, 42, 4), + Nfts::cancel_approval(RuntimeOrigin::signed(account(1)), 0, 42, account(4)), Error::::NotDelegate ); - assert_ok!(Nfts::cancel_approval(RuntimeOrigin::signed(1), 0, 42, 3)); + assert_ok!(Nfts::cancel_approval(RuntimeOrigin::signed(account(1)), 0, 42, account(3))); assert_noop!( - Nfts::cancel_approval(RuntimeOrigin::signed(1), 0, 42, 1), + Nfts::cancel_approval(RuntimeOrigin::signed(account(1)), 0, 42, account(1)), Error::::NotDelegate ); }); @@ -1478,26 +1842,42 @@ fn cancel_approval_works_with_admin() { #[test] fn cancel_approval_works_with_force() { new_test_ext().execute_with(|| { - assert_ok!(Nfts::force_create(RuntimeOrigin::root(), 1, default_collection_config())); - assert_ok!(Nfts::force_mint(RuntimeOrigin::signed(1), 0, 42, 2, default_item_config())); + assert_ok!(Nfts::force_create( + RuntimeOrigin::root(), + account(1), + default_collection_config() + )); + assert_ok!(Nfts::force_mint( + RuntimeOrigin::signed(account(1)), + 0, + 42, + account(2), + default_item_config() + )); - assert_ok!(Nfts::approve_transfer(RuntimeOrigin::signed(2), 0, 42, 3, None)); + assert_ok!(Nfts::approve_transfer( + RuntimeOrigin::signed(account(2)), + 0, + 42, + account(3), + None + )); assert_noop!( - Nfts::cancel_approval(RuntimeOrigin::root(), 1, 42, 1), + Nfts::cancel_approval(RuntimeOrigin::root(), 1, 42, account(1)), Error::::UnknownItem ); assert_noop!( - Nfts::cancel_approval(RuntimeOrigin::root(), 0, 43, 1), + Nfts::cancel_approval(RuntimeOrigin::root(), 0, 43, account(1)), Error::::UnknownItem ); assert_noop!( - Nfts::cancel_approval(RuntimeOrigin::root(), 0, 42, 4), + Nfts::cancel_approval(RuntimeOrigin::root(), 0, 42, account(4)), Error::::NotDelegate ); - assert_ok!(Nfts::cancel_approval(RuntimeOrigin::root(), 0, 42, 3)); + assert_ok!(Nfts::cancel_approval(RuntimeOrigin::root(), 0, 42, account(3))); assert_noop!( - Nfts::cancel_approval(RuntimeOrigin::root(), 0, 42, 1), + Nfts::cancel_approval(RuntimeOrigin::root(), 0, 42, account(1)), Error::::NotDelegate ); }); @@ -1506,32 +1886,54 @@ fn cancel_approval_works_with_force() { #[test] fn clear_all_transfer_approvals_works() { new_test_ext().execute_with(|| { - assert_ok!(Nfts::force_create(RuntimeOrigin::root(), 1, default_collection_config())); - assert_ok!(Nfts::force_mint(RuntimeOrigin::signed(1), 0, 42, 2, default_item_config())); + assert_ok!(Nfts::force_create( + RuntimeOrigin::root(), + account(1), + default_collection_config() + )); + assert_ok!(Nfts::force_mint( + RuntimeOrigin::signed(account(1)), + 0, + 42, + account(2), + default_item_config() + )); - assert_ok!(Nfts::approve_transfer(RuntimeOrigin::signed(2), 0, 42, 3, None)); - assert_ok!(Nfts::approve_transfer(RuntimeOrigin::signed(2), 0, 42, 4, None)); + assert_ok!(Nfts::approve_transfer( + RuntimeOrigin::signed(account(2)), + 0, + 42, + account(3), + None + )); + assert_ok!(Nfts::approve_transfer( + RuntimeOrigin::signed(account(2)), + 0, + 42, + account(4), + None + )); assert_noop!( - Nfts::clear_all_transfer_approvals(RuntimeOrigin::signed(3), 0, 42), + Nfts::clear_all_transfer_approvals(RuntimeOrigin::signed(account(3)), 0, 42), Error::::NoPermission ); - assert_ok!(Nfts::clear_all_transfer_approvals(RuntimeOrigin::signed(2), 0, 42)); + assert_ok!(Nfts::clear_all_transfer_approvals(RuntimeOrigin::signed(account(2)), 0, 42)); assert!(events().contains(&Event::::AllApprovalsCancelled { collection: 0, item: 42, - owner: 2, + owner: account(2), })); assert_eq!(approvals(0, 42), vec![]); assert_noop!( - Nfts::transfer(RuntimeOrigin::signed(3), 0, 42, 5), + Nfts::transfer(RuntimeOrigin::signed(account(3)), 0, 42, account(5)), Error::::NoPermission ); assert_noop!( - Nfts::transfer(RuntimeOrigin::signed(4), 0, 42, 5), + Nfts::transfer(RuntimeOrigin::signed(account(4)), 0, 42, account(5)), Error::::NoPermission ); }); @@ -1541,15 +1943,19 @@ fn clear_all_transfer_approvals_works() { fn max_supply_should_work() { new_test_ext().execute_with(|| { let collection_id = 0; - let user_id = 1; + let user_id = account(1); let max_supply = 1; // validate set_collection_max_supply - assert_ok!(Nfts::force_create(RuntimeOrigin::root(), user_id, default_collection_config())); + assert_ok!(Nfts::force_create( + RuntimeOrigin::root(), + user_id.clone(), + default_collection_config() + )); assert_eq!(CollectionConfigOf::::get(collection_id).unwrap().max_supply, None); assert_ok!(Nfts::set_collection_max_supply( - RuntimeOrigin::signed(user_id), + RuntimeOrigin::signed(user_id.clone()), collection_id, max_supply )); @@ -1564,18 +1970,18 @@ fn max_supply_should_work() { })); assert_ok!(Nfts::set_collection_max_supply( - RuntimeOrigin::signed(user_id), + RuntimeOrigin::signed(user_id.clone()), collection_id, max_supply + 1 )); assert_ok!(Nfts::lock_collection( - RuntimeOrigin::signed(user_id), + RuntimeOrigin::signed(user_id.clone()), collection_id, CollectionSettings::from_disabled(CollectionSetting::UnlockedMaxSupply.into()) )); assert_noop!( Nfts::set_collection_max_supply( - RuntimeOrigin::signed(user_id), + RuntimeOrigin::signed(user_id.clone()), collection_id, max_supply + 2 ), @@ -1583,10 +1989,22 @@ fn max_supply_should_work() { ); // validate we can't mint more to max supply - assert_ok!(Nfts::mint(RuntimeOrigin::signed(user_id), collection_id, 0, user_id, None)); - assert_ok!(Nfts::mint(RuntimeOrigin::signed(user_id), collection_id, 1, user_id, None)); + assert_ok!(Nfts::mint( + RuntimeOrigin::signed(user_id.clone()), + collection_id, + 0, + user_id.clone(), + None + )); + assert_ok!(Nfts::mint( + RuntimeOrigin::signed(user_id.clone()), + collection_id, + 1, + user_id.clone(), + None + )); assert_noop!( - Nfts::mint(RuntimeOrigin::signed(user_id), collection_id, 2, user_id, None), + Nfts::mint(RuntimeOrigin::signed(user_id.clone()), collection_id, 2, user_id, None), Error::::MaxSupplyReached ); }); @@ -1596,15 +2014,19 @@ fn max_supply_should_work() { fn mint_settings_should_work() { new_test_ext().execute_with(|| { let collection_id = 0; - let user_id = 1; + let user_id = account(1); let item_id = 0; - assert_ok!(Nfts::force_create(RuntimeOrigin::root(), user_id, default_collection_config())); + assert_ok!(Nfts::force_create( + RuntimeOrigin::root(), + user_id.clone(), + default_collection_config() + )); assert_ok!(Nfts::mint( - RuntimeOrigin::signed(user_id), + RuntimeOrigin::signed(user_id.clone()), collection_id, item_id, - user_id, + user_id.clone(), None, )); assert_eq!( @@ -1618,7 +2040,7 @@ fn mint_settings_should_work() { let collection_id = 1; assert_ok!(Nfts::force_create( RuntimeOrigin::root(), - user_id, + user_id.clone(), CollectionConfig { mint_settings: MintSettings { default_item_settings: ItemSettings::from_disabled( @@ -1630,10 +2052,10 @@ fn mint_settings_should_work() { } )); assert_ok!(Nfts::mint( - RuntimeOrigin::signed(user_id), + RuntimeOrigin::signed(user_id.clone()), collection_id, item_id, - user_id, + user_id.clone(), None, )); assert_eq!( @@ -1650,30 +2072,34 @@ fn mint_settings_should_work() { #[test] fn set_price_should_work() { new_test_ext().execute_with(|| { - let user_id = 1; + let user_id = account(1); let collection_id = 0; let item_1 = 1; let item_2 = 2; - assert_ok!(Nfts::force_create(RuntimeOrigin::root(), user_id, default_collection_config())); + assert_ok!(Nfts::force_create( + RuntimeOrigin::root(), + user_id.clone(), + default_collection_config() + )); assert_ok!(Nfts::mint( - RuntimeOrigin::signed(user_id), + RuntimeOrigin::signed(user_id.clone()), collection_id, item_1, - user_id, + user_id.clone(), None, )); assert_ok!(Nfts::mint( - RuntimeOrigin::signed(user_id), + RuntimeOrigin::signed(user_id.clone()), collection_id, item_2, - user_id, + user_id.clone(), None, )); assert_ok!(Nfts::set_price( - RuntimeOrigin::signed(user_id), + RuntimeOrigin::signed(user_id.clone()), collection_id, item_1, Some(1), @@ -1681,11 +2107,11 @@ fn set_price_should_work() { )); assert_ok!(Nfts::set_price( - RuntimeOrigin::signed(user_id), + RuntimeOrigin::signed(user_id.clone()), collection_id, item_2, Some(2), - Some(3) + Some(account(3)), )); let item = ItemPriceOf::::get(collection_id, item_1).unwrap(); @@ -1694,7 +2120,7 @@ fn set_price_should_work() { let item = ItemPriceOf::::get(collection_id, item_2).unwrap(); assert_eq!(item.0, 2); - assert_eq!(item.1, Some(3)); + assert_eq!(item.1, Some(account(3))); assert!(events().contains(&Event::::ItemPriceSet { collection: collection_id, @@ -1705,7 +2131,7 @@ fn set_price_should_work() { // validate we can unset the price assert_ok!(Nfts::set_price( - RuntimeOrigin::signed(user_id), + RuntimeOrigin::signed(user_id.clone()), collection_id, item_2, None, @@ -1721,22 +2147,28 @@ fn set_price_should_work() { let collection_id = 1; assert_ok!(Nfts::force_create( RuntimeOrigin::root(), - user_id, + user_id.clone(), collection_config_from_disabled_settings( CollectionSetting::TransferableItems | CollectionSetting::DepositRequired ) )); assert_ok!(Nfts::mint( - RuntimeOrigin::signed(user_id), + RuntimeOrigin::signed(user_id.clone()), collection_id, item_1, - user_id, + user_id.clone(), None, )); assert_noop!( - Nfts::set_price(RuntimeOrigin::signed(user_id), collection_id, item_1, Some(2), None), + Nfts::set_price( + RuntimeOrigin::signed(user_id.clone()), + collection_id, + item_1, + Some(2), + None + ), Error::::ItemsNonTransferable ); }); @@ -1745,9 +2177,9 @@ fn set_price_should_work() { #[test] fn buy_item_should_work() { new_test_ext().execute_with(|| { - let user_1 = 1; - let user_2 = 2; - let user_3 = 3; + let user_1 = account(1); + let user_2 = account(2); + let user_3 = account(3); let collection_id = 0; let item_1 = 1; let item_2 = 2; @@ -1760,14 +2192,36 @@ fn buy_item_should_work() { Balances::make_free_balance_be(&user_2, initial_balance); Balances::make_free_balance_be(&user_3, initial_balance); - assert_ok!(Nfts::force_create(RuntimeOrigin::root(), user_1, default_collection_config())); + assert_ok!(Nfts::force_create( + RuntimeOrigin::root(), + user_1.clone(), + default_collection_config() + )); - assert_ok!(Nfts::mint(RuntimeOrigin::signed(user_1), collection_id, item_1, user_1, None)); - assert_ok!(Nfts::mint(RuntimeOrigin::signed(user_1), collection_id, item_2, user_1, None)); - assert_ok!(Nfts::mint(RuntimeOrigin::signed(user_1), collection_id, item_3, user_1, None)); + assert_ok!(Nfts::mint( + RuntimeOrigin::signed(user_1.clone()), + collection_id, + item_1, + user_1.clone(), + None + )); + assert_ok!(Nfts::mint( + RuntimeOrigin::signed(user_1.clone()), + collection_id, + item_2, + user_1.clone(), + None + )); + assert_ok!(Nfts::mint( + RuntimeOrigin::signed(user_1.clone()), + collection_id, + item_3, + user_1.clone(), + None + )); assert_ok!(Nfts::set_price( - RuntimeOrigin::signed(user_1), + RuntimeOrigin::signed(user_1.clone()), collection_id, item_1, Some(price_1), @@ -1775,22 +2229,22 @@ fn buy_item_should_work() { )); assert_ok!(Nfts::set_price( - RuntimeOrigin::signed(user_1), + RuntimeOrigin::signed(user_1.clone()), collection_id, item_2, Some(price_2), - Some(user_3), + Some(user_3.clone()), )); // can't buy for less assert_noop!( - Nfts::buy_item(RuntimeOrigin::signed(user_2), collection_id, item_1, 1), + Nfts::buy_item(RuntimeOrigin::signed(user_2.clone()), collection_id, item_1, 1), Error::::BidTooLow ); // pass the higher price to validate it will still deduct correctly assert_ok!(Nfts::buy_item( - RuntimeOrigin::signed(user_2), + RuntimeOrigin::signed(user_2.clone()), collection_id, item_1, price_1 + 1, @@ -1798,31 +2252,36 @@ fn buy_item_should_work() { // validate the new owner & balances let item = Item::::get(collection_id, item_1).unwrap(); - assert_eq!(item.owner, user_2); - assert_eq!(Balances::total_balance(&user_1), initial_balance + price_1); - assert_eq!(Balances::total_balance(&user_2), initial_balance - price_1); + assert_eq!(item.owner, user_2.clone()); + assert_eq!(Balances::total_balance(&user_1.clone()), initial_balance + price_1); + assert_eq!(Balances::total_balance(&user_2.clone()), initial_balance - price_1); // can't buy from yourself assert_noop!( - Nfts::buy_item(RuntimeOrigin::signed(user_1), collection_id, item_2, price_2), + Nfts::buy_item(RuntimeOrigin::signed(user_1.clone()), collection_id, item_2, price_2), Error::::NoPermission ); // can't buy when the item is listed for a specific buyer assert_noop!( - Nfts::buy_item(RuntimeOrigin::signed(user_2), collection_id, item_2, price_2), + Nfts::buy_item(RuntimeOrigin::signed(user_2.clone()), collection_id, item_2, price_2), Error::::NoPermission ); // can buy when I'm a whitelisted buyer - assert_ok!(Nfts::buy_item(RuntimeOrigin::signed(user_3), collection_id, item_2, price_2)); + assert_ok!(Nfts::buy_item( + RuntimeOrigin::signed(user_3.clone()), + collection_id, + item_2, + price_2 + )); assert!(events().contains(&Event::::ItemBought { collection: collection_id, item: item_2, price: price_2, - seller: user_1, - buyer: user_3, + seller: user_1.clone(), + buyer: user_3.clone(), })); // ensure we reset the buyer field @@ -1830,14 +2289,14 @@ fn buy_item_should_work() { // can't buy when item is not for sale assert_noop!( - Nfts::buy_item(RuntimeOrigin::signed(user_2), collection_id, item_3, price_2), + Nfts::buy_item(RuntimeOrigin::signed(user_2.clone()), collection_id, item_3, price_2), Error::::NotForSale ); // ensure we can't buy an item when the collection or an item are frozen { assert_ok!(Nfts::set_price( - RuntimeOrigin::signed(user_1), + RuntimeOrigin::signed(user_1.clone()), collection_id, item_3, Some(price_1), @@ -1846,7 +2305,7 @@ fn buy_item_should_work() { // lock the collection assert_ok!(Nfts::lock_collection( - RuntimeOrigin::signed(user_1), + RuntimeOrigin::signed(user_1.clone()), collection_id, CollectionSettings::from_disabled(CollectionSetting::TransferableItems.into()) )); @@ -1857,7 +2316,7 @@ fn buy_item_should_work() { bid_price: price_1, }); assert_noop!( - buy_item_call.dispatch(RuntimeOrigin::signed(user_2)), + buy_item_call.dispatch(RuntimeOrigin::signed(user_2.clone())), Error::::ItemsNonTransferable ); @@ -1870,7 +2329,7 @@ fn buy_item_should_work() { // lock the transfer assert_ok!(Nfts::lock_item_transfer( - RuntimeOrigin::signed(user_1), + RuntimeOrigin::signed(user_1.clone()), collection_id, item_3, )); @@ -1891,9 +2350,9 @@ fn buy_item_should_work() { #[test] fn pay_tips_should_work() { new_test_ext().execute_with(|| { - let user_1 = 1; - let user_2 = 2; - let user_3 = 3; + let user_1 = account(1); + let user_2 = account(2); + let user_3 = account(3); let collection_id = 0; let item_id = 1; let tip = 2; @@ -1904,10 +2363,20 @@ fn pay_tips_should_work() { Balances::make_free_balance_be(&user_3, initial_balance); assert_ok!(Nfts::pay_tips( - RuntimeOrigin::signed(user_1), + RuntimeOrigin::signed(user_1.clone()), bvec![ - ItemTip { collection: collection_id, item: item_id, receiver: user_2, amount: tip }, - ItemTip { collection: collection_id, item: item_id, receiver: user_3, amount: tip }, + ItemTip { + collection: collection_id, + item: item_id, + receiver: user_2.clone(), + amount: tip + }, + ItemTip { + collection: collection_id, + item: item_id, + receiver: user_3.clone(), + amount: tip + }, ] )); @@ -1919,15 +2388,15 @@ fn pay_tips_should_work() { assert!(events.contains(&Event::::TipSent { collection: collection_id, item: item_id, - sender: user_1, - receiver: user_2, + sender: user_1.clone(), + receiver: user_2.clone(), amount: tip, })); assert!(events.contains(&Event::::TipSent { collection: collection_id, item: item_id, - sender: user_1, - receiver: user_3, + sender: user_1.clone(), + receiver: user_3.clone(), amount: tip, })); }); @@ -1937,7 +2406,7 @@ fn pay_tips_should_work() { fn create_cancel_swap_should_work() { new_test_ext().execute_with(|| { System::set_block_number(1); - let user_id = 1; + let user_id = account(1); let collection_id = 0; let item_1 = 1; let item_2 = 2; @@ -1947,27 +2416,31 @@ fn create_cancel_swap_should_work() { let duration = 2; let expect_deadline = 3; - assert_ok!(Nfts::force_create(RuntimeOrigin::root(), user_id, default_collection_config())); + assert_ok!(Nfts::force_create( + RuntimeOrigin::root(), + user_id.clone(), + default_collection_config() + )); assert_ok!(Nfts::mint( - RuntimeOrigin::signed(user_id), + RuntimeOrigin::signed(user_id.clone()), collection_id, item_1, - user_id, + user_id.clone(), None, )); assert_ok!(Nfts::mint( - RuntimeOrigin::signed(user_id), + RuntimeOrigin::signed(user_id.clone()), collection_id, item_2, - user_id, + user_id.clone(), None, )); // validate desired item and the collection exists assert_noop!( Nfts::create_swap( - RuntimeOrigin::signed(user_id), + RuntimeOrigin::signed(user_id.clone()), collection_id, item_1, collection_id, @@ -1979,7 +2452,7 @@ fn create_cancel_swap_should_work() { ); assert_noop!( Nfts::create_swap( - RuntimeOrigin::signed(user_id), + RuntimeOrigin::signed(user_id.clone()), collection_id, item_1, collection_id + 1, @@ -1993,7 +2466,7 @@ fn create_cancel_swap_should_work() { let max_duration: u64 = ::MaxDeadlineDuration::get(); assert_noop!( Nfts::create_swap( - RuntimeOrigin::signed(user_id), + RuntimeOrigin::signed(user_id.clone()), collection_id, item_1, collection_id, @@ -2005,7 +2478,7 @@ fn create_cancel_swap_should_work() { ); assert_ok!(Nfts::create_swap( - RuntimeOrigin::signed(user_id), + RuntimeOrigin::signed(user_id.clone()), collection_id, item_1, collection_id, @@ -2030,7 +2503,11 @@ fn create_cancel_swap_should_work() { })); // validate we can cancel the swap - assert_ok!(Nfts::cancel_swap(RuntimeOrigin::signed(user_id), collection_id, item_1)); + assert_ok!(Nfts::cancel_swap( + RuntimeOrigin::signed(user_id.clone()), + collection_id, + item_1 + )); assert!(events().contains(&Event::::SwapCancelled { offered_collection: collection_id, offered_item: item_1, @@ -2043,7 +2520,7 @@ fn create_cancel_swap_should_work() { // validate anyone can cancel the expired swap assert_ok!(Nfts::create_swap( - RuntimeOrigin::signed(user_id), + RuntimeOrigin::signed(user_id.clone()), collection_id, item_1, collection_id, @@ -2052,11 +2529,11 @@ fn create_cancel_swap_should_work() { duration, )); assert_noop!( - Nfts::cancel_swap(RuntimeOrigin::signed(user_id + 1), collection_id, item_1), + Nfts::cancel_swap(RuntimeOrigin::signed(account(2)), collection_id, item_1), Error::::NoPermission ); System::set_block_number(expect_deadline + 1); - assert_ok!(Nfts::cancel_swap(RuntimeOrigin::signed(user_id + 1), collection_id, item_1)); + assert_ok!(Nfts::cancel_swap(RuntimeOrigin::signed(account(2)), collection_id, item_1)); // validate optional desired_item param assert_ok!(Nfts::create_swap( @@ -2078,8 +2555,8 @@ fn create_cancel_swap_should_work() { fn claim_swap_should_work() { new_test_ext().execute_with(|| { System::set_block_number(1); - let user_1 = 1; - let user_2 = 2; + let user_1 = account(1); + let user_2 = account(2); let collection_id = 0; let item_1 = 1; let item_2 = 2; @@ -2097,45 +2574,46 @@ fn claim_swap_should_work() { Balances::make_free_balance_be(&user_1, initial_balance); Balances::make_free_balance_be(&user_2, initial_balance); - assert_ok!(Nfts::force_create(RuntimeOrigin::root(), user_1, default_collection_config())); + assert_ok!(Nfts::force_create(RuntimeOrigin::root(), user_1.clone(), default_collection_config())); assert_ok!(Nfts::mint( - RuntimeOrigin::signed(user_1), + RuntimeOrigin::signed(user_1.clone()), collection_id, - item_1,user_1, + item_1, + user_1.clone(), None, )); assert_ok!(Nfts::force_mint( - RuntimeOrigin::signed(user_1), + RuntimeOrigin::signed(user_1.clone()), collection_id, item_2, - user_2, + user_2.clone(), default_item_config(), )); assert_ok!(Nfts::force_mint( - RuntimeOrigin::signed(user_1), + RuntimeOrigin::signed(user_1.clone()), collection_id, item_3, - user_2, + user_2.clone(), default_item_config(), )); assert_ok!(Nfts::mint( - RuntimeOrigin::signed(user_1), + RuntimeOrigin::signed(user_1.clone()), collection_id, item_4, - user_1, + user_1.clone(), None, )); assert_ok!(Nfts::force_mint( - RuntimeOrigin::signed(user_1), + RuntimeOrigin::signed(user_1.clone()), collection_id, item_5, - user_2, + user_2.clone(), default_item_config(), )); assert_ok!(Nfts::create_swap( - RuntimeOrigin::signed(user_1), + RuntimeOrigin::signed(user_1.clone()), collection_id, item_1, collection_id, @@ -2148,7 +2626,7 @@ fn claim_swap_should_work() { System::set_block_number(5); assert_noop!( Nfts::claim_swap( - RuntimeOrigin::signed(user_2), + RuntimeOrigin::signed(user_2.clone()), collection_id, item_2, collection_id, @@ -2162,7 +2640,7 @@ fn claim_swap_should_work() { // validate edge cases assert_noop!( Nfts::claim_swap( - RuntimeOrigin::signed(user_2), + RuntimeOrigin::signed(user_2.clone()), collection_id, item_2, collection_id, @@ -2173,7 +2651,7 @@ fn claim_swap_should_work() { ); assert_noop!( Nfts::claim_swap( - RuntimeOrigin::signed(user_2), + RuntimeOrigin::signed(user_2.clone()), collection_id, item_4, // not my item collection_id, @@ -2184,7 +2662,7 @@ fn claim_swap_should_work() { ); assert_noop!( Nfts::claim_swap( - RuntimeOrigin::signed(user_2), + RuntimeOrigin::signed(user_2.clone()), collection_id, item_5, // my item, but not the one another part wants collection_id, @@ -2195,7 +2673,7 @@ fn claim_swap_should_work() { ); assert_noop!( Nfts::claim_swap( - RuntimeOrigin::signed(user_2), + RuntimeOrigin::signed(user_2.clone()), collection_id, item_2, collection_id, @@ -2206,7 +2684,7 @@ fn claim_swap_should_work() { ); assert_noop!( Nfts::claim_swap( - RuntimeOrigin::signed(user_2), + RuntimeOrigin::signed(user_2.clone()), collection_id, item_2, collection_id, @@ -2217,7 +2695,7 @@ fn claim_swap_should_work() { ); assert_ok!(Nfts::claim_swap( - RuntimeOrigin::signed(user_2), + RuntimeOrigin::signed(user_2.clone()), collection_id, item_2, collection_id, @@ -2227,9 +2705,9 @@ fn claim_swap_should_work() { // validate the new owner let item = Item::::get(collection_id, item_1).unwrap(); - assert_eq!(item.owner, user_2); + assert_eq!(item.owner, user_2.clone()); let item = Item::::get(collection_id, item_2).unwrap(); - assert_eq!(item.owner, user_1); + assert_eq!(item.owner, user_1.clone()); // validate the balances assert_eq!(Balances::total_balance(&user_1), initial_balance + price); @@ -2242,10 +2720,10 @@ fn claim_swap_should_work() { assert!(events().contains(&Event::::SwapClaimed { sent_collection: collection_id, sent_item: item_2, - sent_item_owner: user_2, + sent_item_owner: user_2.clone(), received_collection: collection_id, received_item: item_1, - received_item_owner: user_1, + received_item_owner: user_1.clone(), price: Some(price_with_direction.clone()), deadline, })); @@ -2257,7 +2735,7 @@ fn claim_swap_should_work() { Balances::make_free_balance_be(&user_2, initial_balance); assert_ok!(Nfts::create_swap( - RuntimeOrigin::signed(user_1), + RuntimeOrigin::signed(user_1.clone()), collection_id, item_4, collection_id, @@ -2266,7 +2744,7 @@ fn claim_swap_should_work() { duration, )); assert_ok!(Nfts::claim_swap( - RuntimeOrigin::signed(user_2), + RuntimeOrigin::signed(user_2.clone()), collection_id, item_1, collection_id, @@ -2289,7 +2767,7 @@ fn various_collection_settings() { // when we set only one value it's required to call .into() on it let config = collection_config_from_disabled_settings(CollectionSetting::TransferableItems.into()); - assert_ok!(Nfts::force_create(RuntimeOrigin::root(), 1, config)); + assert_ok!(Nfts::force_create(RuntimeOrigin::root(), account(1), config)); let config = CollectionConfigOf::::get(0).unwrap(); assert!(!config.is_setting_enabled(CollectionSetting::TransferableItems)); @@ -2299,25 +2777,29 @@ fn various_collection_settings() { let config = collection_config_from_disabled_settings( CollectionSetting::UnlockedMetadata | CollectionSetting::TransferableItems, ); - assert_ok!(Nfts::force_create(RuntimeOrigin::root(), 1, config)); + assert_ok!(Nfts::force_create(RuntimeOrigin::root(), account(1), config)); let config = CollectionConfigOf::::get(1).unwrap(); assert!(!config.is_setting_enabled(CollectionSetting::TransferableItems)); assert!(!config.is_setting_enabled(CollectionSetting::UnlockedMetadata)); - assert_ok!(Nfts::force_create(RuntimeOrigin::root(), 1, default_collection_config())); + assert_ok!(Nfts::force_create( + RuntimeOrigin::root(), + account(1), + default_collection_config() + )); }); } #[test] fn collection_locking_should_work() { new_test_ext().execute_with(|| { - let user_id = 1; + let user_id = account(1); let collection_id = 0; assert_ok!(Nfts::force_create( RuntimeOrigin::root(), - user_id, + user_id.clone(), collection_config_with_all_settings_enabled() )); @@ -2325,7 +2807,7 @@ fn collection_locking_should_work() { collection_config_from_disabled_settings(CollectionSetting::DepositRequired.into()); assert_noop!( Nfts::lock_collection( - RuntimeOrigin::signed(user_id), + RuntimeOrigin::signed(user_id.clone()), collection_id, lock_config.settings, ), @@ -2337,7 +2819,7 @@ fn collection_locking_should_work() { CollectionSetting::TransferableItems | CollectionSetting::UnlockedAttributes, ); assert_ok!(Nfts::lock_collection( - RuntimeOrigin::signed(user_id), + RuntimeOrigin::signed(user_id.clone()), collection_id, lock_config.settings, )); @@ -2369,33 +2851,49 @@ fn pallet_level_feature_flags_should_work() { PalletFeature::Trading | PalletFeature::Approvals | PalletFeature::Attributes, )); - let user_id = 1; + let user_id = account(1); let collection_id = 0; let item_id = 1; - assert_ok!(Nfts::force_create(RuntimeOrigin::root(), user_id, default_collection_config())); + assert_ok!(Nfts::force_create( + RuntimeOrigin::root(), + user_id.clone(), + default_collection_config() + )); assert_ok!(Nfts::mint( - RuntimeOrigin::signed(user_id), + RuntimeOrigin::signed(user_id.clone()), collection_id, item_id, - user_id, + user_id.clone(), None, )); // PalletFeature::Trading assert_noop!( - Nfts::set_price(RuntimeOrigin::signed(user_id), collection_id, item_id, Some(1), None), + Nfts::set_price( + RuntimeOrigin::signed(user_id.clone()), + collection_id, + item_id, + Some(1), + None + ), Error::::MethodDisabled ); assert_noop!( - Nfts::buy_item(RuntimeOrigin::signed(user_id), collection_id, item_id, 1), + Nfts::buy_item(RuntimeOrigin::signed(user_id.clone()), collection_id, item_id, 1), Error::::MethodDisabled ); // PalletFeature::Approvals assert_noop!( - Nfts::approve_transfer(RuntimeOrigin::signed(user_id), collection_id, item_id, 2, None), + Nfts::approve_transfer( + RuntimeOrigin::signed(user_id.clone()), + collection_id, + item_id, + account(2), + None + ), Error::::MethodDisabled ); @@ -2420,25 +2918,25 @@ fn group_roles_by_account_should_work() { assert_eq!(Nfts::group_roles_by_account(vec![]), vec![]); let account_to_role = Nfts::group_roles_by_account(vec![ - (3, CollectionRole::Freezer), - (1, CollectionRole::Issuer), - (2, CollectionRole::Admin), + (account(3), CollectionRole::Freezer), + (account(1), CollectionRole::Issuer), + (account(2), CollectionRole::Admin), ]); let expect = vec![ - (1, CollectionRoles(CollectionRole::Issuer.into())), - (2, CollectionRoles(CollectionRole::Admin.into())), - (3, CollectionRoles(CollectionRole::Freezer.into())), + (account(1), CollectionRoles(CollectionRole::Issuer.into())), + (account(2), CollectionRoles(CollectionRole::Admin.into())), + (account(3), CollectionRoles(CollectionRole::Freezer.into())), ]; assert_eq!(account_to_role, expect); let account_to_role = Nfts::group_roles_by_account(vec![ - (3, CollectionRole::Freezer), - (2, CollectionRole::Issuer), - (2, CollectionRole::Admin), + (account(3), CollectionRole::Freezer), + (account(2), CollectionRole::Issuer), + (account(2), CollectionRole::Admin), ]); let expect = vec![ - (2, CollectionRoles(CollectionRole::Issuer | CollectionRole::Admin)), - (3, CollectionRoles(CollectionRole::Freezer.into())), + (account(2), CollectionRoles(CollectionRole::Issuer | CollectionRole::Admin)), + (account(3), CollectionRoles(CollectionRole::Freezer.into())), ]; assert_eq!(account_to_role, expect); }) @@ -2447,40 +2945,53 @@ fn group_roles_by_account_should_work() { #[test] fn add_remove_item_attributes_approval_should_work() { new_test_ext().execute_with(|| { - let user_1 = 1; - let user_2 = 2; - let user_3 = 3; - let user_4 = 4; + let user_1 = account(1); + let user_2 = account(2); + let user_3 = account(3); + let user_4 = account(4); let collection_id = 0; let item_id = 0; - assert_ok!(Nfts::force_create(RuntimeOrigin::root(), user_1, default_collection_config())); - assert_ok!(Nfts::mint(RuntimeOrigin::signed(user_1), collection_id, item_id, user_1, None)); + assert_ok!(Nfts::force_create( + RuntimeOrigin::root(), + user_1.clone(), + default_collection_config() + )); + assert_ok!(Nfts::mint( + RuntimeOrigin::signed(user_1.clone()), + collection_id, + item_id, + user_1.clone(), + None + )); assert_ok!(Nfts::approve_item_attributes( - RuntimeOrigin::signed(user_1), + RuntimeOrigin::signed(user_1.clone()), collection_id, item_id, - user_2, + user_2.clone(), )); - assert_eq!(item_attributes_approvals(collection_id, item_id), vec![user_2]); + assert_eq!(item_attributes_approvals(collection_id, item_id), vec![user_2.clone()]); assert_ok!(Nfts::approve_item_attributes( - RuntimeOrigin::signed(user_1), + RuntimeOrigin::signed(user_1.clone()), collection_id, item_id, - user_3, + user_3.clone(), )); assert_ok!(Nfts::approve_item_attributes( - RuntimeOrigin::signed(user_1), + RuntimeOrigin::signed(user_1.clone()), collection_id, item_id, - user_2, + user_2.clone(), )); - assert_eq!(item_attributes_approvals(collection_id, item_id), vec![user_2, user_3]); + assert_eq!( + item_attributes_approvals(collection_id, item_id), + vec![user_2.clone(), user_3.clone()] + ); assert_noop!( Nfts::approve_item_attributes( - RuntimeOrigin::signed(user_1), + RuntimeOrigin::signed(user_1.clone()), collection_id, item_id, user_4, @@ -2498,3 +3009,170 @@ fn add_remove_item_attributes_approval_should_work() { assert_eq!(item_attributes_approvals(collection_id, item_id), vec![user_3]); }) } + +#[test] +fn pre_signed_mints_should_work() { + new_test_ext().execute_with(|| { + let user_1_pair = sp_core::sr25519::Pair::from_string("//Alice", None).unwrap(); + let user_1_signer = MultiSigner::Sr25519(user_1_pair.public()); + let user_1 = user_1_signer.clone().into_account(); + let mint_data = PreSignedMint { + collection: 0, + item: 0, + attributes: vec![(vec![0], vec![1]), (vec![2], vec![3])], + metadata: vec![0, 1], + only_account: None, + deadline: 10000000, + }; + let message = Encode::encode(&mint_data); + let signature = MultiSignature::Sr25519(user_1_pair.sign(&message)); + let user_2 = account(2); + let user_3 = account(3); + + Balances::make_free_balance_be(&user_1, 100); + Balances::make_free_balance_be(&user_2, 100); + assert_ok!(Nfts::create( + RuntimeOrigin::signed(user_1.clone()), + user_1.clone(), + collection_config_with_all_settings_enabled(), + )); + + assert_ok!(Nfts::mint_pre_signed( + RuntimeOrigin::signed(user_2.clone()), + mint_data.clone(), + signature.clone(), + user_1.clone(), + )); + assert_eq!(items(), vec![(user_2.clone(), 0, 0)]); + let metadata = ItemMetadataOf::::get(0, 0).unwrap(); + assert_eq!( + metadata.deposit, + ItemMetadataDeposit { account: Some(user_2.clone()), amount: 3 } + ); + assert_eq!(metadata.data, vec![0, 1]); + + assert_eq!( + attributes(0), + vec![ + (Some(0), AttributeNamespace::CollectionOwner, bvec![0], bvec![1]), + (Some(0), AttributeNamespace::CollectionOwner, bvec![2], bvec![3]), + ] + ); + let attribute_key: BoundedVec<_, _> = bvec![0]; + let (_, deposit) = Attribute::::get(( + 0, + Some(0), + AttributeNamespace::CollectionOwner, + &attribute_key, + )) + .unwrap(); + assert_eq!(deposit.account, Some(user_2.clone())); + assert_eq!(deposit.amount, 3); + + assert_eq!(Balances::free_balance(&user_1), 100 - 2); // 2 - collection deposit + assert_eq!(Balances::free_balance(&user_2), 100 - 1 - 3 - 6); // 1 - item deposit, 3 - metadata, 6 - attributes + + assert_noop!( + Nfts::mint_pre_signed( + RuntimeOrigin::signed(user_2.clone()), + mint_data, + signature.clone(), + user_1.clone(), + ), + Error::::AlreadyExists + ); + + assert_ok!(Nfts::burn(RuntimeOrigin::signed(user_2.clone()), 0, 0, Some(user_2.clone()))); + assert_eq!(Balances::free_balance(&user_2), 100 - 6); + + // validate the `only_account` field + let mint_data = PreSignedMint { + collection: 0, + item: 0, + attributes: vec![], + metadata: vec![], + only_account: Some(account(2)), + deadline: 10000000, + }; + + // can't mint with the wrong signature + assert_noop!( + Nfts::mint_pre_signed( + RuntimeOrigin::signed(user_2.clone()), + mint_data.clone(), + signature.clone(), + user_1.clone(), + ), + Error::::WrongSignature + ); + + let message = Encode::encode(&mint_data); + let signature = MultiSignature::Sr25519(user_1_pair.sign(&message)); + + assert_noop!( + Nfts::mint_pre_signed( + RuntimeOrigin::signed(user_3), + mint_data.clone(), + signature.clone(), + user_1.clone(), + ), + Error::::WrongOrigin + ); + + // validate signature's expiration + System::set_block_number(10000001); + assert_noop!( + Nfts::mint_pre_signed( + RuntimeOrigin::signed(user_2.clone()), + mint_data, + signature, + user_1.clone(), + ), + Error::::DeadlineExpired + ); + System::set_block_number(1); + + // validate the collection + let mint_data = PreSignedMint { + collection: 1, + item: 0, + attributes: vec![], + metadata: vec![], + only_account: Some(account(2)), + deadline: 10000000, + }; + let message = Encode::encode(&mint_data); + let signature = MultiSignature::Sr25519(user_1_pair.sign(&message)); + + assert_noop!( + Nfts::mint_pre_signed( + RuntimeOrigin::signed(user_2.clone()), + mint_data, + signature, + user_1.clone(), + ), + Error::::UnknownCollection + ); + + // validate max attributes limit + let mint_data = PreSignedMint { + collection: 0, + item: 0, + attributes: vec![(vec![0], vec![1]), (vec![2], vec![3]), (vec![2], vec![3])], + metadata: vec![0, 1], + only_account: None, + deadline: 10000000, + }; + let message = Encode::encode(&mint_data); + let signature = MultiSignature::Sr25519(user_1_pair.sign(&message)); + assert_noop!( + Nfts::mint_pre_signed( + RuntimeOrigin::signed(user_2), + mint_data, + signature, + user_1.clone(), + ), + Error::::MaxAttributesLimitReached + ); + }) +} diff --git a/frame/nfts/src/types.rs b/frame/nfts/src/types.rs index d8938aab4..c2ecd61c2 100644 --- a/frame/nfts/src/types.rs +++ b/frame/nfts/src/types.rs @@ -61,6 +61,12 @@ pub(super) type CollectionConfigFor = CollectionConfig< ::BlockNumber, >::CollectionId, >; +pub(super) type PreSignedMintOf = PreSignedMint< + >::CollectionId, + >::ItemId, + ::AccountId, + ::BlockNumber, +>; pub trait Incrementable { fn increment(&self) -> Self; @@ -187,7 +193,7 @@ pub struct PendingSwap { pub(super) desired_item: Option, /// A price for the desired `item` with the direction. pub(super) price: Option, - /// An optional deadline for the swap. + /// A deadline for the swap. pub(super) deadline: Deadline, } @@ -473,3 +479,19 @@ impl CollectionRoles { } } impl_codec_bitflags!(CollectionRoles, u8, CollectionRole); + +#[derive(Clone, Eq, PartialEq, Encode, Decode, RuntimeDebug, TypeInfo)] +pub struct PreSignedMint { + /// A collection of the item to be minted. + pub(super) collection: CollectionId, + /// Item's id. + pub(super) item: ItemId, + /// Additional item's key-value attributes. + pub(super) attributes: Vec<(Vec, Vec)>, + /// Additional item's metadata. + pub(super) metadata: Vec, + /// Restrict the claim to a particular account. + pub(super) only_account: Option, + /// A deadline for the signature. + pub(super) deadline: Deadline, +} diff --git a/frame/nfts/src/weights.rs b/frame/nfts/src/weights.rs index 81be145b6..40592c299 100644 --- a/frame/nfts/src/weights.rs +++ b/frame/nfts/src/weights.rs @@ -18,25 +18,26 @@ //! Autogenerated weights for pallet_nfts //! //! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev -//! DATE: 2023-01-24, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! DATE: 2023-01-31, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` //! WORST CASE MAP SIZE: `1000000` -//! HOSTNAME: `bm2`, CPU: `Intel(R) Core(TM) i7-7700K CPU @ 4.20GHz` +//! HOSTNAME: `runner-b3zmxxc-project-145-concurrent-0`, CPU: `Intel(R) Xeon(R) CPU @ 2.60GHz` //! EXECUTION: Some(Wasm), WASM-EXECUTION: Compiled, CHAIN: Some("dev"), DB CACHE: 1024 // Executed Command: -// ./target/production/substrate +// target/production/substrate // benchmark // pallet -// --chain=dev // --steps=50 // --repeat=20 -// --pallet=pallet_nfts // --extrinsic=* // --execution=wasm // --wasm-execution=compiled // --heap-pages=4096 -// --output=./frame/nfts/src/weights.rs +// --json-file=/builds/parity/mirrors/substrate/.git/.artifacts/bench.json +// --pallet=pallet_nfts +// --chain=dev // --header=./HEADER-APACHE2 +// --output=./frame/nfts/src/weights.rs // --template=./.maintain/frame-weight-template.hbs #![cfg_attr(rustfmt, rustfmt_skip)] @@ -85,6 +86,7 @@ pub trait WeightInfo { fn create_swap() -> Weight; fn cancel_swap() -> Weight; fn claim_swap() -> Weight; + fn mint_pre_signed(n: u32, ) -> Weight; } /// Weights for pallet_nfts using the Substrate node and recommended hardware. @@ -104,8 +106,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `214` // Estimated: `3054` - // Minimum execution time: 32_467 nanoseconds. - Weight::from_parts(33_236_000, 3054) + // Minimum execution time: 33_666 nanoseconds. + Weight::from_parts(34_405_000, 3054) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().writes(5_u64)) } @@ -123,8 +125,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `42` // Estimated: `3054` - // Minimum execution time: 22_198 nanoseconds. - Weight::from_parts(22_776_000, 3054) + // Minimum execution time: 22_028 nanoseconds. + Weight::from_parts(23_030_000, 3054) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().writes(5_u64)) } @@ -151,18 +153,16 @@ impl WeightInfo for SubstrateWeight { /// The range of component `n` is `[0, 1000]`. /// The range of component `m` is `[0, 1000]`. /// The range of component `a` is `[0, 1000]`. - fn destroy(n: u32, m: u32, a: u32, ) -> Weight { + fn destroy(_n: u32, m: u32, a: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `172781 + m * (127 ±0) + a * (402 ±0)` // Estimated: `3347427 + a * (2921 ±0) + m * (2615 ±0)` - // Minimum execution time: 24_021_657 nanoseconds. - Weight::from_parts(16_029_391_606, 3347427) - // Standard Error: 20_364 - .saturating_add(Weight::from_ref_time(300_580).saturating_mul(n.into())) - // Standard Error: 20_364 - .saturating_add(Weight::from_ref_time(7_748_502).saturating_mul(m.into())) - // Standard Error: 20_364 - .saturating_add(Weight::from_ref_time(9_183_566).saturating_mul(a.into())) + // Minimum execution time: 27_944_985 nanoseconds. + Weight::from_parts(19_865_318_850, 3347427) + // Standard Error: 32_345 + .saturating_add(Weight::from_ref_time(8_729_316).saturating_mul(m.into())) + // Standard Error: 32_345 + .saturating_add(Weight::from_ref_time(10_264_491).saturating_mul(a.into())) .saturating_add(T::DbWeight::get().reads(1004_u64)) .saturating_add(T::DbWeight::get().reads((1_u64).saturating_mul(m.into()))) .saturating_add(T::DbWeight::get().reads((1_u64).saturating_mul(a.into()))) @@ -188,8 +188,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `448` // Estimated: `13506` - // Minimum execution time: 42_634 nanoseconds. - Weight::from_parts(43_231_000, 13506) + // Minimum execution time: 43_925 nanoseconds. + Weight::from_parts(45_885_000, 13506) .saturating_add(T::DbWeight::get().reads(5_u64)) .saturating_add(T::DbWeight::get().writes(4_u64)) } @@ -209,8 +209,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `448` // Estimated: `13506` - // Minimum execution time: 41_686 nanoseconds. - Weight::from_parts(41_991_000, 13506) + // Minimum execution time: 42_832 nanoseconds. + Weight::from_parts(44_621_000, 13506) .saturating_add(T::DbWeight::get().reads(5_u64)) .saturating_add(T::DbWeight::get().writes(4_u64)) } @@ -236,8 +236,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `647` // Estimated: `13573` - // Minimum execution time: 45_192 nanoseconds. - Weight::from_parts(45_792_000, 13573) + // Minimum execution time: 47_787 nanoseconds. + Weight::from_parts(49_204_000, 13573) .saturating_add(T::DbWeight::get().reads(5_u64)) .saturating_add(T::DbWeight::get().writes(7_u64)) } @@ -263,8 +263,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `882` // Estimated: `16109` - // Minimum execution time: 51_962 nanoseconds. - Weight::from_parts(52_367_000, 16109) + // Minimum execution time: 55_524 nanoseconds. + Weight::from_parts(56_962_000, 16109) .saturating_add(T::DbWeight::get().reads(6_u64)) .saturating_add(T::DbWeight::get().writes(6_u64)) } @@ -279,10 +279,10 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `756 + i * (140 ±0)` // Estimated: `5103 + i * (3336 ±0)` - // Minimum execution time: 15_512 nanoseconds. - Weight::from_parts(15_731_000, 5103) - // Standard Error: 9_495 - .saturating_add(Weight::from_ref_time(11_462_413).saturating_mul(i.into())) + // Minimum execution time: 15_246 nanoseconds. + Weight::from_parts(15_671_000, 5103) + // Standard Error: 20_348 + .saturating_add(Weight::from_ref_time(14_692_422).saturating_mul(i.into())) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().reads((1_u64).saturating_mul(i.into()))) .saturating_add(T::DbWeight::get().writes((1_u64).saturating_mul(i.into()))) @@ -296,8 +296,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `401` // Estimated: `5067` - // Minimum execution time: 19_273 nanoseconds. - Weight::from_parts(19_508_000, 5067) + // Minimum execution time: 19_270 nanoseconds. + Weight::from_parts(19_775_000, 5067) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -309,8 +309,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `401` // Estimated: `5067` - // Minimum execution time: 19_022 nanoseconds. - Weight::from_parts(19_430_000, 5067) + // Minimum execution time: 19_364 nanoseconds. + Weight::from_parts(20_274_000, 5067) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -322,8 +322,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `289` // Estimated: `5092` - // Minimum execution time: 17_593 nanoseconds. - Weight::from_parts(17_950_000, 5092) + // Minimum execution time: 17_036 nanoseconds. + Weight::from_parts(17_750_000, 5092) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -337,8 +337,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `381` // Estimated: `5082` - // Minimum execution time: 22_068 nanoseconds. - Weight::from_parts(22_235_000, 5082) + // Minimum execution time: 22_104 nanoseconds. + Weight::from_parts(23_022_000, 5082) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().writes(4_u64)) } @@ -350,8 +350,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `362` // Estimated: `2555` - // Minimum execution time: 25_056 nanoseconds. - Weight::from_parts(25_767_000, 2555) + // Minimum execution time: 24_516 nanoseconds. + Weight::from_parts(25_300_000, 2555) .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(T::DbWeight::get().writes(5_u64)) } @@ -363,8 +363,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `304` // Estimated: `2555` - // Minimum execution time: 17_398 nanoseconds. - Weight::from_parts(17_684_000, 2555) + // Minimum execution time: 16_974 nanoseconds. + Weight::from_parts(17_654_000, 2555) .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) } @@ -376,8 +376,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `242` // Estimated: `2555` - // Minimum execution time: 14_054 nanoseconds. - Weight::from_parts(14_243_000, 2555) + // Minimum execution time: 13_190 nanoseconds. + Weight::from_parts(13_826_000, 2555) .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -389,8 +389,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `445` // Estimated: `5078` - // Minimum execution time: 17_662 nanoseconds. - Weight::from_parts(18_073_000, 5078) + // Minimum execution time: 17_336 nanoseconds. + Weight::from_parts(18_242_000, 5078) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -406,8 +406,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `474` // Estimated: `10547` - // Minimum execution time: 40_098 nanoseconds. - Weight::from_parts(40_649_000, 10547) + // Minimum execution time: 40_791 nanoseconds. + Weight::from_parts(42_489_000, 10547) .saturating_add(T::DbWeight::get().reads(4_u64)) .saturating_add(T::DbWeight::get().writes(2_u64)) } @@ -419,8 +419,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `337` // Estimated: `5476` - // Minimum execution time: 25_178 nanoseconds. - Weight::from_parts(25_473_000, 5476) + // Minimum execution time: 24_620 nanoseconds. + Weight::from_parts(25_370_000, 5476) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().writes(2_u64)) } @@ -434,8 +434,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `916` // Estimated: `7999` - // Minimum execution time: 35_202 nanoseconds. - Weight::from_parts(35_518_000, 7999) + // Minimum execution time: 36_411 nanoseconds. + Weight::from_parts(37_439_000, 7999) .saturating_add(T::DbWeight::get().reads(3_u64)) .saturating_add(T::DbWeight::get().writes(2_u64)) } @@ -447,8 +447,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `379` // Estimated: `6492` - // Minimum execution time: 17_260 nanoseconds. - Weight::from_parts(17_498_000, 6492) + // Minimum execution time: 16_696 nanoseconds. + Weight::from_parts(17_411_000, 6492) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -463,12 +463,12 @@ impl WeightInfo for SubstrateWeight { /// The range of component `n` is `[0, 1000]`. fn cancel_item_attributes_approval(n: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `865 + n * (367 ±0)` + // Measured: `899 + n * (396 ±0)` // Estimated: `12016 + n * (2921 ±0)` - // Minimum execution time: 25_579 nanoseconds. - Weight::from_parts(25_846_000, 12016) - // Standard Error: 7_759 - .saturating_add(Weight::from_ref_time(7_159_200).saturating_mul(n.into())) + // Minimum execution time: 25_928 nanoseconds. + Weight::from_parts(26_440_000, 12016) + // Standard Error: 9_158 + .saturating_add(Weight::from_ref_time(9_271_441).saturating_mul(n.into())) .saturating_add(T::DbWeight::get().reads(4_u64)) .saturating_add(T::DbWeight::get().reads((1_u64).saturating_mul(n.into()))) .saturating_add(T::DbWeight::get().writes(2_u64)) @@ -487,8 +487,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `474` // Estimated: `10241` - // Minimum execution time: 33_285 nanoseconds. - Weight::from_parts(33_692_000, 10241) + // Minimum execution time: 34_150 nanoseconds. + Weight::from_parts(35_398_000, 10241) .saturating_add(T::DbWeight::get().reads(4_u64)) .saturating_add(T::DbWeight::get().writes(2_u64)) } @@ -502,8 +502,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `609` // Estimated: `7693` - // Minimum execution time: 30_670 nanoseconds. - Weight::from_parts(31_282_000, 7693) + // Minimum execution time: 31_871 nanoseconds. + Weight::from_parts(33_057_000, 7693) .saturating_add(T::DbWeight::get().reads(3_u64)) .saturating_add(T::DbWeight::get().writes(2_u64)) } @@ -517,8 +517,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `333` // Estimated: `7665` - // Minimum execution time: 28_313 nanoseconds. - Weight::from_parts(28_724_000, 7665) + // Minimum execution time: 28_843 nanoseconds. + Weight::from_parts(30_057_000, 7665) .saturating_add(T::DbWeight::get().reads(3_u64)) .saturating_add(T::DbWeight::get().writes(2_u64)) } @@ -532,8 +532,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `476` // Estimated: `7665` - // Minimum execution time: 27_034 nanoseconds. - Weight::from_parts(27_655_000, 7665) + // Minimum execution time: 27_777 nanoseconds. + Weight::from_parts(28_471_000, 7665) .saturating_add(T::DbWeight::get().reads(3_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -547,8 +547,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `466` // Estimated: `8428` - // Minimum execution time: 23_408 nanoseconds. - Weight::from_parts(23_916_000, 8428) + // Minimum execution time: 23_726 nanoseconds. + Weight::from_parts(24_455_000, 8428) .saturating_add(T::DbWeight::get().reads(3_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -560,8 +560,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `474` // Estimated: `5880` - // Minimum execution time: 21_177 nanoseconds. - Weight::from_parts(21_492_000, 5880) + // Minimum execution time: 21_051 nanoseconds. + Weight::from_parts(21_722_000, 5880) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -573,8 +573,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `474` // Estimated: `5880` - // Minimum execution time: 20_279 nanoseconds. - Weight::from_parts(20_919_000, 5880) + // Minimum execution time: 20_095 nanoseconds. + Weight::from_parts(20_770_000, 5880) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -584,8 +584,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `42` // Estimated: `2527` - // Minimum execution time: 14_921 nanoseconds. - Weight::from_parts(15_382_000, 2527) + // Minimum execution time: 14_078 nanoseconds. + Weight::from_parts(14_582_000, 2527) .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -597,8 +597,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `333` // Estimated: `5103` - // Minimum execution time: 18_201 nanoseconds. - Weight::from_parts(18_628_000, 5103) + // Minimum execution time: 17_677 nanoseconds. + Weight::from_parts(18_381_000, 5103) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -610,8 +610,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `333` // Estimated: `5103` - // Minimum execution time: 16_870 nanoseconds. - Weight::from_parts(17_318_000, 5103) + // Minimum execution time: 16_295 nanoseconds. + Weight::from_parts(17_036_000, 5103) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -627,8 +627,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `516` // Estimated: `8407` - // Minimum execution time: 22_604 nanoseconds. - Weight::from_parts(22_867_000, 8407) + // Minimum execution time: 22_847 nanoseconds. + Weight::from_parts(23_536_000, 8407) .saturating_add(T::DbWeight::get().reads(3_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -652,8 +652,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `934` // Estimated: `16129` - // Minimum execution time: 56_849 nanoseconds. - Weight::from_parts(57_336_000, 16129) + // Minimum execution time: 60_517 nanoseconds. + Weight::from_parts(62_528_000, 16129) .saturating_add(T::DbWeight::get().reads(6_u64)) .saturating_add(T::DbWeight::get().writes(6_u64)) } @@ -662,10 +662,10 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 2_308 nanoseconds. - Weight::from_ref_time(4_805_401) - // Standard Error: 13_875 - .saturating_add(Weight::from_ref_time(3_167_190).saturating_mul(n.into())) + // Minimum execution time: 1_866 nanoseconds. + Weight::from_ref_time(3_949_301) + // Standard Error: 11_044 + .saturating_add(Weight::from_ref_time(3_424_466).saturating_mul(n.into())) } /// Storage: Nfts Item (r:2 w:0) /// Proof: Nfts Item (max_values: None, max_size: Some(861), added: 3336, mode: MaxEncodedLen) @@ -675,8 +675,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `524` // Estimated: `6672` - // Minimum execution time: 20_395 nanoseconds. - Weight::from_parts(20_716_000, 6672) + // Minimum execution time: 21_174 nanoseconds. + Weight::from_parts(21_619_000, 6672) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -688,8 +688,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `511` // Estimated: `5882` - // Minimum execution time: 19_936 nanoseconds. - Weight::from_parts(20_344_000, 5882) + // Minimum execution time: 20_606 nanoseconds. + Weight::from_parts(21_150_000, 5882) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -713,11 +713,42 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `1097` // Estimated: `21970` - // Minimum execution time: 80_884 nanoseconds. - Weight::from_parts(81_643_000, 21970) + // Minimum execution time: 88_414 nanoseconds. + Weight::from_parts(91_830_000, 21970) .saturating_add(T::DbWeight::get().reads(8_u64)) .saturating_add(T::DbWeight::get().writes(11_u64)) } + /// Storage: Nfts Collection (r:1 w:1) + /// Proof: Nfts Collection (max_values: None, max_size: Some(80), added: 2555, mode: MaxEncodedLen) + /// Storage: Nfts CollectionConfigOf (r:1 w:0) + /// Proof: Nfts CollectionConfigOf (max_values: None, max_size: Some(73), added: 2548, mode: MaxEncodedLen) + /// Storage: Nfts Item (r:1 w:1) + /// Proof: Nfts Item (max_values: None, max_size: Some(861), added: 3336, mode: MaxEncodedLen) + /// Storage: Nfts ItemConfigOf (r:1 w:1) + /// Proof: Nfts ItemConfigOf (max_values: None, max_size: Some(48), added: 2523, mode: MaxEncodedLen) + /// Storage: System Account (r:1 w:1) + /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) + /// Storage: Nfts Attribute (r:10 w:10) + /// Proof: Nfts Attribute (max_values: None, max_size: Some(446), added: 2921, mode: MaxEncodedLen) + /// Storage: Nfts ItemMetadataOf (r:1 w:1) + /// Proof: Nfts ItemMetadataOf (max_values: None, max_size: Some(140), added: 2615, mode: MaxEncodedLen) + /// Storage: Nfts Account (r:0 w:1) + /// Proof: Nfts Account (max_values: None, max_size: Some(88), added: 2563, mode: MaxEncodedLen) + /// The range of component `n` is `[0, 10]`. + fn mint_pre_signed(n: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `596` + // Estimated: `16180 + n * (2921 ±0)` + // Minimum execution time: 124_354 nanoseconds. + Weight::from_parts(133_779_491, 16180) + // Standard Error: 38_452 + .saturating_add(Weight::from_ref_time(25_110_697).saturating_mul(n.into())) + .saturating_add(T::DbWeight::get().reads(6_u64)) + .saturating_add(T::DbWeight::get().reads((1_u64).saturating_mul(n.into()))) + .saturating_add(T::DbWeight::get().writes(6_u64)) + .saturating_add(T::DbWeight::get().writes((1_u64).saturating_mul(n.into()))) + .saturating_add(Weight::from_proof_size(2921).saturating_mul(n.into())) + } } // For backwards compatibility and tests @@ -736,8 +767,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `214` // Estimated: `3054` - // Minimum execution time: 32_467 nanoseconds. - Weight::from_parts(33_236_000, 3054) + // Minimum execution time: 33_666 nanoseconds. + Weight::from_parts(34_405_000, 3054) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().writes(5_u64)) } @@ -755,8 +786,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `42` // Estimated: `3054` - // Minimum execution time: 22_198 nanoseconds. - Weight::from_parts(22_776_000, 3054) + // Minimum execution time: 22_028 nanoseconds. + Weight::from_parts(23_030_000, 3054) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().writes(5_u64)) } @@ -783,18 +814,16 @@ impl WeightInfo for () { /// The range of component `n` is `[0, 1000]`. /// The range of component `m` is `[0, 1000]`. /// The range of component `a` is `[0, 1000]`. - fn destroy(n: u32, m: u32, a: u32, ) -> Weight { + fn destroy(_n: u32, m: u32, a: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `172781 + m * (127 ±0) + a * (402 ±0)` // Estimated: `3347427 + a * (2921 ±0) + m * (2615 ±0)` - // Minimum execution time: 24_021_657 nanoseconds. - Weight::from_parts(16_029_391_606, 3347427) - // Standard Error: 20_364 - .saturating_add(Weight::from_ref_time(300_580).saturating_mul(n.into())) - // Standard Error: 20_364 - .saturating_add(Weight::from_ref_time(7_748_502).saturating_mul(m.into())) - // Standard Error: 20_364 - .saturating_add(Weight::from_ref_time(9_183_566).saturating_mul(a.into())) + // Minimum execution time: 27_944_985 nanoseconds. + Weight::from_parts(19_865_318_850, 3347427) + // Standard Error: 32_345 + .saturating_add(Weight::from_ref_time(8_729_316).saturating_mul(m.into())) + // Standard Error: 32_345 + .saturating_add(Weight::from_ref_time(10_264_491).saturating_mul(a.into())) .saturating_add(RocksDbWeight::get().reads(1004_u64)) .saturating_add(RocksDbWeight::get().reads((1_u64).saturating_mul(m.into()))) .saturating_add(RocksDbWeight::get().reads((1_u64).saturating_mul(a.into()))) @@ -820,8 +849,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `448` // Estimated: `13506` - // Minimum execution time: 42_634 nanoseconds. - Weight::from_parts(43_231_000, 13506) + // Minimum execution time: 43_925 nanoseconds. + Weight::from_parts(45_885_000, 13506) .saturating_add(RocksDbWeight::get().reads(5_u64)) .saturating_add(RocksDbWeight::get().writes(4_u64)) } @@ -841,8 +870,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `448` // Estimated: `13506` - // Minimum execution time: 41_686 nanoseconds. - Weight::from_parts(41_991_000, 13506) + // Minimum execution time: 42_832 nanoseconds. + Weight::from_parts(44_621_000, 13506) .saturating_add(RocksDbWeight::get().reads(5_u64)) .saturating_add(RocksDbWeight::get().writes(4_u64)) } @@ -868,8 +897,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `647` // Estimated: `13573` - // Minimum execution time: 45_192 nanoseconds. - Weight::from_parts(45_792_000, 13573) + // Minimum execution time: 47_787 nanoseconds. + Weight::from_parts(49_204_000, 13573) .saturating_add(RocksDbWeight::get().reads(5_u64)) .saturating_add(RocksDbWeight::get().writes(7_u64)) } @@ -895,8 +924,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `882` // Estimated: `16109` - // Minimum execution time: 51_962 nanoseconds. - Weight::from_parts(52_367_000, 16109) + // Minimum execution time: 55_524 nanoseconds. + Weight::from_parts(56_962_000, 16109) .saturating_add(RocksDbWeight::get().reads(6_u64)) .saturating_add(RocksDbWeight::get().writes(6_u64)) } @@ -911,10 +940,10 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `756 + i * (140 ±0)` // Estimated: `5103 + i * (3336 ±0)` - // Minimum execution time: 15_512 nanoseconds. - Weight::from_parts(15_731_000, 5103) - // Standard Error: 9_495 - .saturating_add(Weight::from_ref_time(11_462_413).saturating_mul(i.into())) + // Minimum execution time: 15_246 nanoseconds. + Weight::from_parts(15_671_000, 5103) + // Standard Error: 20_348 + .saturating_add(Weight::from_ref_time(14_692_422).saturating_mul(i.into())) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().reads((1_u64).saturating_mul(i.into()))) .saturating_add(RocksDbWeight::get().writes((1_u64).saturating_mul(i.into()))) @@ -928,8 +957,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `401` // Estimated: `5067` - // Minimum execution time: 19_273 nanoseconds. - Weight::from_parts(19_508_000, 5067) + // Minimum execution time: 19_270 nanoseconds. + Weight::from_parts(19_775_000, 5067) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } @@ -941,8 +970,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `401` // Estimated: `5067` - // Minimum execution time: 19_022 nanoseconds. - Weight::from_parts(19_430_000, 5067) + // Minimum execution time: 19_364 nanoseconds. + Weight::from_parts(20_274_000, 5067) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } @@ -954,8 +983,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `289` // Estimated: `5092` - // Minimum execution time: 17_593 nanoseconds. - Weight::from_parts(17_950_000, 5092) + // Minimum execution time: 17_036 nanoseconds. + Weight::from_parts(17_750_000, 5092) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } @@ -969,8 +998,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `381` // Estimated: `5082` - // Minimum execution time: 22_068 nanoseconds. - Weight::from_parts(22_235_000, 5082) + // Minimum execution time: 22_104 nanoseconds. + Weight::from_parts(23_022_000, 5082) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().writes(4_u64)) } @@ -982,8 +1011,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `362` // Estimated: `2555` - // Minimum execution time: 25_056 nanoseconds. - Weight::from_parts(25_767_000, 2555) + // Minimum execution time: 24_516 nanoseconds. + Weight::from_parts(25_300_000, 2555) .saturating_add(RocksDbWeight::get().reads(1_u64)) .saturating_add(RocksDbWeight::get().writes(5_u64)) } @@ -995,8 +1024,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `304` // Estimated: `2555` - // Minimum execution time: 17_398 nanoseconds. - Weight::from_parts(17_684_000, 2555) + // Minimum execution time: 16_974 nanoseconds. + Weight::from_parts(17_654_000, 2555) .saturating_add(RocksDbWeight::get().reads(1_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) } @@ -1008,8 +1037,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `242` // Estimated: `2555` - // Minimum execution time: 14_054 nanoseconds. - Weight::from_parts(14_243_000, 2555) + // Minimum execution time: 13_190 nanoseconds. + Weight::from_parts(13_826_000, 2555) .saturating_add(RocksDbWeight::get().reads(1_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } @@ -1021,8 +1050,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `445` // Estimated: `5078` - // Minimum execution time: 17_662 nanoseconds. - Weight::from_parts(18_073_000, 5078) + // Minimum execution time: 17_336 nanoseconds. + Weight::from_parts(18_242_000, 5078) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } @@ -1038,8 +1067,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `474` // Estimated: `10547` - // Minimum execution time: 40_098 nanoseconds. - Weight::from_parts(40_649_000, 10547) + // Minimum execution time: 40_791 nanoseconds. + Weight::from_parts(42_489_000, 10547) .saturating_add(RocksDbWeight::get().reads(4_u64)) .saturating_add(RocksDbWeight::get().writes(2_u64)) } @@ -1051,8 +1080,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `337` // Estimated: `5476` - // Minimum execution time: 25_178 nanoseconds. - Weight::from_parts(25_473_000, 5476) + // Minimum execution time: 24_620 nanoseconds. + Weight::from_parts(25_370_000, 5476) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().writes(2_u64)) } @@ -1066,8 +1095,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `916` // Estimated: `7999` - // Minimum execution time: 35_202 nanoseconds. - Weight::from_parts(35_518_000, 7999) + // Minimum execution time: 36_411 nanoseconds. + Weight::from_parts(37_439_000, 7999) .saturating_add(RocksDbWeight::get().reads(3_u64)) .saturating_add(RocksDbWeight::get().writes(2_u64)) } @@ -1079,8 +1108,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `379` // Estimated: `6492` - // Minimum execution time: 17_260 nanoseconds. - Weight::from_parts(17_498_000, 6492) + // Minimum execution time: 16_696 nanoseconds. + Weight::from_parts(17_411_000, 6492) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } @@ -1095,12 +1124,12 @@ impl WeightInfo for () { /// The range of component `n` is `[0, 1000]`. fn cancel_item_attributes_approval(n: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `865 + n * (367 ±0)` + // Measured: `899 + n * (396 ±0)` // Estimated: `12016 + n * (2921 ±0)` - // Minimum execution time: 25_579 nanoseconds. - Weight::from_parts(25_846_000, 12016) - // Standard Error: 7_759 - .saturating_add(Weight::from_ref_time(7_159_200).saturating_mul(n.into())) + // Minimum execution time: 25_928 nanoseconds. + Weight::from_parts(26_440_000, 12016) + // Standard Error: 9_158 + .saturating_add(Weight::from_ref_time(9_271_441).saturating_mul(n.into())) .saturating_add(RocksDbWeight::get().reads(4_u64)) .saturating_add(RocksDbWeight::get().reads((1_u64).saturating_mul(n.into()))) .saturating_add(RocksDbWeight::get().writes(2_u64)) @@ -1119,8 +1148,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `474` // Estimated: `10241` - // Minimum execution time: 33_285 nanoseconds. - Weight::from_parts(33_692_000, 10241) + // Minimum execution time: 34_150 nanoseconds. + Weight::from_parts(35_398_000, 10241) .saturating_add(RocksDbWeight::get().reads(4_u64)) .saturating_add(RocksDbWeight::get().writes(2_u64)) } @@ -1134,8 +1163,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `609` // Estimated: `7693` - // Minimum execution time: 30_670 nanoseconds. - Weight::from_parts(31_282_000, 7693) + // Minimum execution time: 31_871 nanoseconds. + Weight::from_parts(33_057_000, 7693) .saturating_add(RocksDbWeight::get().reads(3_u64)) .saturating_add(RocksDbWeight::get().writes(2_u64)) } @@ -1149,8 +1178,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `333` // Estimated: `7665` - // Minimum execution time: 28_313 nanoseconds. - Weight::from_parts(28_724_000, 7665) + // Minimum execution time: 28_843 nanoseconds. + Weight::from_parts(30_057_000, 7665) .saturating_add(RocksDbWeight::get().reads(3_u64)) .saturating_add(RocksDbWeight::get().writes(2_u64)) } @@ -1164,8 +1193,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `476` // Estimated: `7665` - // Minimum execution time: 27_034 nanoseconds. - Weight::from_parts(27_655_000, 7665) + // Minimum execution time: 27_777 nanoseconds. + Weight::from_parts(28_471_000, 7665) .saturating_add(RocksDbWeight::get().reads(3_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } @@ -1179,8 +1208,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `466` // Estimated: `8428` - // Minimum execution time: 23_408 nanoseconds. - Weight::from_parts(23_916_000, 8428) + // Minimum execution time: 23_726 nanoseconds. + Weight::from_parts(24_455_000, 8428) .saturating_add(RocksDbWeight::get().reads(3_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } @@ -1192,8 +1221,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `474` // Estimated: `5880` - // Minimum execution time: 21_177 nanoseconds. - Weight::from_parts(21_492_000, 5880) + // Minimum execution time: 21_051 nanoseconds. + Weight::from_parts(21_722_000, 5880) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } @@ -1205,8 +1234,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `474` // Estimated: `5880` - // Minimum execution time: 20_279 nanoseconds. - Weight::from_parts(20_919_000, 5880) + // Minimum execution time: 20_095 nanoseconds. + Weight::from_parts(20_770_000, 5880) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } @@ -1216,8 +1245,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `42` // Estimated: `2527` - // Minimum execution time: 14_921 nanoseconds. - Weight::from_parts(15_382_000, 2527) + // Minimum execution time: 14_078 nanoseconds. + Weight::from_parts(14_582_000, 2527) .saturating_add(RocksDbWeight::get().reads(1_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } @@ -1229,8 +1258,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `333` // Estimated: `5103` - // Minimum execution time: 18_201 nanoseconds. - Weight::from_parts(18_628_000, 5103) + // Minimum execution time: 17_677 nanoseconds. + Weight::from_parts(18_381_000, 5103) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } @@ -1242,8 +1271,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `333` // Estimated: `5103` - // Minimum execution time: 16_870 nanoseconds. - Weight::from_parts(17_318_000, 5103) + // Minimum execution time: 16_295 nanoseconds. + Weight::from_parts(17_036_000, 5103) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } @@ -1259,8 +1288,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `516` // Estimated: `8407` - // Minimum execution time: 22_604 nanoseconds. - Weight::from_parts(22_867_000, 8407) + // Minimum execution time: 22_847 nanoseconds. + Weight::from_parts(23_536_000, 8407) .saturating_add(RocksDbWeight::get().reads(3_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } @@ -1284,8 +1313,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `934` // Estimated: `16129` - // Minimum execution time: 56_849 nanoseconds. - Weight::from_parts(57_336_000, 16129) + // Minimum execution time: 60_517 nanoseconds. + Weight::from_parts(62_528_000, 16129) .saturating_add(RocksDbWeight::get().reads(6_u64)) .saturating_add(RocksDbWeight::get().writes(6_u64)) } @@ -1294,10 +1323,10 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 2_308 nanoseconds. - Weight::from_ref_time(4_805_401) - // Standard Error: 13_875 - .saturating_add(Weight::from_ref_time(3_167_190).saturating_mul(n.into())) + // Minimum execution time: 1_866 nanoseconds. + Weight::from_ref_time(3_949_301) + // Standard Error: 11_044 + .saturating_add(Weight::from_ref_time(3_424_466).saturating_mul(n.into())) } /// Storage: Nfts Item (r:2 w:0) /// Proof: Nfts Item (max_values: None, max_size: Some(861), added: 3336, mode: MaxEncodedLen) @@ -1307,8 +1336,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `524` // Estimated: `6672` - // Minimum execution time: 20_395 nanoseconds. - Weight::from_parts(20_716_000, 6672) + // Minimum execution time: 21_174 nanoseconds. + Weight::from_parts(21_619_000, 6672) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } @@ -1320,8 +1349,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `511` // Estimated: `5882` - // Minimum execution time: 19_936 nanoseconds. - Weight::from_parts(20_344_000, 5882) + // Minimum execution time: 20_606 nanoseconds. + Weight::from_parts(21_150_000, 5882) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } @@ -1345,9 +1374,40 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `1097` // Estimated: `21970` - // Minimum execution time: 80_884 nanoseconds. - Weight::from_parts(81_643_000, 21970) + // Minimum execution time: 88_414 nanoseconds. + Weight::from_parts(91_830_000, 21970) .saturating_add(RocksDbWeight::get().reads(8_u64)) .saturating_add(RocksDbWeight::get().writes(11_u64)) } + /// Storage: Nfts Collection (r:1 w:1) + /// Proof: Nfts Collection (max_values: None, max_size: Some(80), added: 2555, mode: MaxEncodedLen) + /// Storage: Nfts CollectionConfigOf (r:1 w:0) + /// Proof: Nfts CollectionConfigOf (max_values: None, max_size: Some(73), added: 2548, mode: MaxEncodedLen) + /// Storage: Nfts Item (r:1 w:1) + /// Proof: Nfts Item (max_values: None, max_size: Some(861), added: 3336, mode: MaxEncodedLen) + /// Storage: Nfts ItemConfigOf (r:1 w:1) + /// Proof: Nfts ItemConfigOf (max_values: None, max_size: Some(48), added: 2523, mode: MaxEncodedLen) + /// Storage: System Account (r:1 w:1) + /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) + /// Storage: Nfts Attribute (r:10 w:10) + /// Proof: Nfts Attribute (max_values: None, max_size: Some(446), added: 2921, mode: MaxEncodedLen) + /// Storage: Nfts ItemMetadataOf (r:1 w:1) + /// Proof: Nfts ItemMetadataOf (max_values: None, max_size: Some(140), added: 2615, mode: MaxEncodedLen) + /// Storage: Nfts Account (r:0 w:1) + /// Proof: Nfts Account (max_values: None, max_size: Some(88), added: 2563, mode: MaxEncodedLen) + /// The range of component `n` is `[0, 10]`. + fn mint_pre_signed(n: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `596` + // Estimated: `16180 + n * (2921 ±0)` + // Minimum execution time: 124_354 nanoseconds. + Weight::from_parts(133_779_491, 16180) + // Standard Error: 38_452 + .saturating_add(Weight::from_ref_time(25_110_697).saturating_mul(n.into())) + .saturating_add(RocksDbWeight::get().reads(6_u64)) + .saturating_add(RocksDbWeight::get().reads((1_u64).saturating_mul(n.into()))) + .saturating_add(RocksDbWeight::get().writes(6_u64)) + .saturating_add(RocksDbWeight::get().writes((1_u64).saturating_mul(n.into()))) + .saturating_add(Weight::from_proof_size(2921).saturating_mul(n.into())) + } } From ef1c36114d7f0be2d9ae9b3af7e36a2014ee6e97 Mon Sep 17 00:00:00 2001 From: yjh Date: Tue, 14 Feb 2023 21:17:14 +0800 Subject: [PATCH 115/558] feat: improve FinalityProofProvider api (#13374) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * feat: improve prove_finality api and export it * fmt * fix * improve prove_finality and kept private * Update client/finality-grandpa/src/finality_proof.rs Co-authored-by: André Silva <123550+andresilva@users.noreply.github.com> * add `prove_finality_proof` to `FinalityProofProvider` * fix some and impl Clone for FinalityProofProvider * improve by suggestions --------- Co-authored-by: André Silva <123550+andresilva@users.noreply.github.com> --- client/finality-grandpa/src/finality_proof.rs | 91 +++++++++++++------ 1 file changed, 61 insertions(+), 30 deletions(-) diff --git a/client/finality-grandpa/src/finality_proof.rs b/client/finality-grandpa/src/finality_proof.rs index 416e71478..af3ed6e14 100644 --- a/client/finality-grandpa/src/finality_proof.rs +++ b/client/finality-grandpa/src/finality_proof.rs @@ -58,6 +58,7 @@ use crate::{ const MAX_UNKNOWN_HEADERS: usize = 100_000; /// Finality proof provider for serving network requests. +#[derive(Clone)] pub struct FinalityProofProvider { backend: Arc, shared_authority_set: Option>>, @@ -99,11 +100,24 @@ where B: Backend, { /// Prove finality for the given block number by returning a Justification for the last block of - /// the authority set. + /// the authority set in bytes. pub fn prove_finality( &self, block: NumberFor, ) -> Result>, FinalityProofError> { + Ok(self.prove_finality_proof(block, true)?.map(|proof| proof.encode())) + } + + /// Prove finality for the given block number by returning a Justification for the last block of + /// the authority set. + /// + /// If `collect_unknown_headers` is true, the finality proof will include all headers from the + /// requested block until the block the justification refers to. + pub fn prove_finality_proof( + &self, + block: NumberFor, + collect_unknown_headers: bool, + ) -> Result>, FinalityProofError> { let authority_set_changes = if let Some(changes) = self .shared_authority_set .as_ref() @@ -114,7 +128,7 @@ where return Ok(None) }; - prove_finality(&*self.backend, authority_set_changes, block) + prove_finality(&*self.backend, authority_set_changes, block, collect_unknown_headers) } } @@ -146,22 +160,29 @@ pub enum FinalityProofError { Client(#[from] sp_blockchain::Error), } +/// Prove finality for the given block number by returning a justification for the last block of +/// the authority set of which the given block is part of, or a justification for the latest +/// finalized block if the given block is part of the current authority set. +/// +/// If `collect_unknown_headers` is true, the finality proof will include all headers from the +/// requested block until the block the justification refers to. fn prove_finality( backend: &B, authority_set_changes: AuthoritySetChanges>, block: NumberFor, -) -> Result>, FinalityProofError> + collect_unknown_headers: bool, +) -> Result>, FinalityProofError> where Block: BlockT, B: Backend, { // Early-return if we are sure that there are no blocks finalized that cover the requested // block. - let info = backend.blockchain().info(); - if info.finalized_number < block { + let finalized_number = backend.blockchain().info().finalized_number; + if finalized_number < block { let err = format!( "Requested finality proof for descendant of #{} while we only have finalized #{}.", - block, info.finalized_number, + block, finalized_number, ); trace!(target: LOG_TARGET, "{}", &err); return Err(FinalityProofError::BlockNotYetFinalized) @@ -214,9 +235,9 @@ where }, }; - // Collect all headers from the requested block until the last block of the set - let unknown_headers = { - let mut headers = Vec::new(); + let mut headers = Vec::new(); + if collect_unknown_headers { + // Collect all headers from the requested block until the last block of the set let mut current = block + One::one(); loop { if current > just_block || headers.len() >= MAX_UNKNOWN_HEADERS { @@ -226,17 +247,13 @@ where headers.push(backend.blockchain().expect_header(hash)?); current += One::one(); } - headers }; - Ok(Some( - FinalityProof { - block: backend.blockchain().expect_block_hash_from_id(&BlockId::Number(just_block))?, - justification, - unknown_headers, - } - .encode(), - )) + Ok(Some(FinalityProof { + block: backend.blockchain().expect_block_hash_from_id(&BlockId::Number(just_block))?, + justification, + unknown_headers: headers, + })) } #[cfg(test)] @@ -334,7 +351,7 @@ mod tests { let authority_set_changes = AuthoritySetChanges::empty(); // The last finalized block is 4, so we cannot provide further justifications. - let proof_of_5 = prove_finality(&*backend, authority_set_changes, 5); + let proof_of_5 = prove_finality(&*backend, authority_set_changes, 5, true); assert!(matches!(proof_of_5, Err(FinalityProofError::BlockNotYetFinalized))); } @@ -347,7 +364,7 @@ mod tests { // Block 4 is finalized without justification // => we can't prove finality of 3 - let proof_of_3 = prove_finality(&*backend, authority_set_changes, 3).unwrap(); + let proof_of_3 = prove_finality(&*backend, authority_set_changes, 3, true).unwrap(); assert_eq!(proof_of_3, None); } @@ -478,7 +495,7 @@ mod tests { let mut authority_set_changes = AuthoritySetChanges::empty(); authority_set_changes.append(1, 8); - let proof_of_6 = prove_finality(&*backend, authority_set_changes, 6); + let proof_of_6 = prove_finality(&*backend, authority_set_changes, 6, true); assert!(matches!(proof_of_6, Err(FinalityProofError::BlockNotInAuthoritySetChanges))); } @@ -502,10 +519,11 @@ mod tests { authority_set_changes.append(0, 5); authority_set_changes.append(1, 8); - let proof_of_6: FinalityProof = Decode::decode( - &mut &prove_finality(&*backend, authority_set_changes.clone(), 6).unwrap().unwrap()[..], - ) - .unwrap(); + let proof_of_6: FinalityProof = + prove_finality(&*backend, authority_set_changes.clone(), 6, true) + .unwrap() + .unwrap(); + assert_eq!( proof_of_6, FinalityProof { @@ -514,6 +532,20 @@ mod tests { unknown_headers: vec![block7.header().clone(), block8.header().clone()], }, ); + + let proof_of_6_without_unknown: FinalityProof = + prove_finality(&*backend, authority_set_changes.clone(), 6, false) + .unwrap() + .unwrap(); + + assert_eq!( + proof_of_6_without_unknown, + FinalityProof { + block: block8.hash(), + justification: grandpa_just8.encode(), + unknown_headers: vec![], + }, + ); } #[test] @@ -525,7 +557,7 @@ mod tests { let mut authority_set_changes = AuthoritySetChanges::empty(); authority_set_changes.append(0, 5); - assert!(matches!(prove_finality(&*backend, authority_set_changes, 6), Ok(None))); + assert!(matches!(prove_finality(&*backend, authority_set_changes, 6, true), Ok(None))); } #[test] @@ -544,10 +576,9 @@ mod tests { let mut authority_set_changes = AuthoritySetChanges::empty(); authority_set_changes.append(0, 5); - let proof_of_6: FinalityProof = Decode::decode( - &mut &prove_finality(&*backend, authority_set_changes, 6).unwrap().unwrap()[..], - ) - .unwrap(); + let proof_of_6: FinalityProof = + prove_finality(&*backend, authority_set_changes, 6, true).unwrap().unwrap(); + assert_eq!( proof_of_6, FinalityProof { From e6f6c8c0e90e512f19bc4da43234a76224a65610 Mon Sep 17 00:00:00 2001 From: Vivek Pandya Date: Tue, 14 Feb 2023 19:39:36 +0530 Subject: [PATCH 116/558] cleanup `` from docs comments (#13350) * cleanup from docs comments * Changes to address review commnets * Fix CI cargo test --docs --------- Co-authored-by: parity-processbot <> --- frame/balances/src/lib.rs | 14 +- frame/bounties/src/lib.rs | 24 ++-- frame/collective/src/lib.rs | 82 ++--------- frame/democracy/src/lib.rs | 10 +- frame/elections-phragmen/src/lib.rs | 23 ++-- frame/identity/src/lib.rs | 80 ++++------- frame/im-online/src/lib.rs | 10 +- frame/indices/src/lib.rs | 44 +----- frame/multisig/src/lib.rs | 28 +--- frame/nicks/src/lib.rs | 24 +--- frame/scheduler/src/lib.rs | 8 -- frame/session/src/lib.rs | 21 +-- frame/society/src/lib.rs | 198 +++++++-------------------- frame/staking/src/pallet/mod.rs | 73 +++------- frame/sudo/src/lib.rs | 21 +-- frame/system/src/lib.rs | 24 +--- frame/timestamp/src/lib.rs | 7 +- frame/tips/src/lib.rs | 49 +++---- frame/transaction-storage/src/lib.rs | 16 +-- frame/treasury/src/lib.rs | 36 ++--- frame/utility/src/lib.rs | 21 +-- frame/vesting/src/lib.rs | 24 +--- 22 files changed, 202 insertions(+), 635 deletions(-) diff --git a/frame/balances/src/lib.rs b/frame/balances/src/lib.rs index 99d77a3e7..f62ea6be7 100644 --- a/frame/balances/src/lib.rs +++ b/frame/balances/src/lib.rs @@ -267,7 +267,7 @@ pub mod pallet { /// /// The dispatch origin for this call must be `Signed` by the transactor. /// - /// # + /// ## Complexity /// - Dependent on arguments but not critical, given proper implementations for input config /// types. See related functions below. /// - It contains a limited number of reads and writes internally and no complex @@ -281,9 +281,6 @@ pub mod pallet { /// - Removing enough funds from an account will trigger `T::DustRemoval::on_unbalanced`. /// - `transfer_keep_alive` works the same way as `transfer`, but has an additional check /// that the transfer will not kill the origin account. - /// --------------------------------- - /// - Origin account is already in memory, so no DB operations for them. - /// # #[pallet::call_index(0)] #[pallet::weight(T::WeightInfo::transfer())] pub fn transfer( @@ -360,10 +357,9 @@ pub mod pallet { /// Exactly as `transfer`, except the origin must be root and the source account may be /// specified. - /// # + /// ## Complexity /// - Same as transfer, but additional read and write because the source account is not /// assumed to be in the overlay. - /// # #[pallet::call_index(2)] #[pallet::weight(T::WeightInfo::force_transfer())] pub fn force_transfer( @@ -417,9 +413,8 @@ pub mod pallet { /// - `keep_alive`: A boolean to determine if the `transfer_all` operation should send all /// of the funds the account has, causing the sender account to be killed (false), or /// transfer everything except at least the existential deposit, which will guarantee to - /// keep the sender account alive (true). # + /// keep the sender account alive (true). ## Complexity /// - O(1). Just like transfer, but reading the user's transferable balance first. - /// # #[pallet::call_index(4)] #[pallet::weight(T::WeightInfo::transfer_all())] pub fn transfer_all( @@ -1484,10 +1479,9 @@ where // restrictions like locks and vesting balance. // Is a no-op if amount to be withdrawn is zero. // - // # + // ## Complexity // Despite iterating over a list of locks, they are limited by the number of // lock IDs, which means the number of runtime pallets that intend to use and create locks. - // # fn ensure_can_withdraw( who: &T::AccountId, amount: T::Balance, diff --git a/frame/bounties/src/lib.rs b/frame/bounties/src/lib.rs index c3c2c08d2..afdcfef82 100644 --- a/frame/bounties/src/lib.rs +++ b/frame/bounties/src/lib.rs @@ -349,9 +349,8 @@ pub mod pallet { /// /// May only be called from `T::SpendOrigin`. /// - /// # + /// ## Complexity /// - O(1). - /// # #[pallet::call_index(1)] #[pallet::weight(>::WeightInfo::approve_bounty())] pub fn approve_bounty( @@ -381,9 +380,8 @@ pub mod pallet { /// /// May only be called from `T::SpendOrigin`. /// - /// # + /// ## Complexity /// - O(1). - /// # #[pallet::call_index(2)] #[pallet::weight(>::WeightInfo::propose_curator())] pub fn propose_curator( @@ -431,9 +429,8 @@ pub mod pallet { /// anyone in the community to call out that a curator is not doing their due diligence, and /// we should pick a new curator. In this case the curator should also be slashed. /// - /// # + /// ## Complexity /// - O(1). - /// # #[pallet::call_index(3)] #[pallet::weight(>::WeightInfo::unassign_curator())] pub fn unassign_curator( @@ -517,9 +514,8 @@ pub mod pallet { /// /// May only be called from the curator. /// - /// # + /// ## Complexity /// - O(1). - /// # #[pallet::call_index(4)] #[pallet::weight(>::WeightInfo::accept_curator())] pub fn accept_curator( @@ -560,9 +556,8 @@ pub mod pallet { /// - `bounty_id`: Bounty ID to award. /// - `beneficiary`: The beneficiary account whom will receive the payout. /// - /// # + /// ## Complexity /// - O(1). - /// # #[pallet::call_index(5)] #[pallet::weight(>::WeightInfo::award_bounty())] pub fn award_bounty( @@ -608,9 +603,8 @@ pub mod pallet { /// /// - `bounty_id`: Bounty ID to claim. /// - /// # + /// ## Complexity /// - O(1). - /// # #[pallet::call_index(6)] #[pallet::weight(>::WeightInfo::claim_bounty())] pub fn claim_bounty( @@ -672,9 +666,8 @@ pub mod pallet { /// /// - `bounty_id`: Bounty ID to cancel. /// - /// # + /// ## Complexity /// - O(1). - /// # #[pallet::call_index(7)] #[pallet::weight(>::WeightInfo::close_bounty_proposed() .max(>::WeightInfo::close_bounty_active()))] @@ -764,9 +757,8 @@ pub mod pallet { /// - `bounty_id`: Bounty ID to extend. /// - `remark`: additional information. /// - /// # + /// ## Complexity /// - O(1). - /// # #[pallet::call_index(8)] #[pallet::weight(>::WeightInfo::extend_bounty_expiry())] pub fn extend_bounty_expiry( diff --git a/frame/collective/src/lib.rs b/frame/collective/src/lib.rs index 7d625a69a..8424527b1 100644 --- a/frame/collective/src/lib.rs +++ b/frame/collective/src/lib.rs @@ -364,19 +364,11 @@ pub mod pallet { /// Any call to `set_members` must be careful that the member set doesn't get out of sync /// with other logic managing the member set. /// - /// # - /// ## Weight + /// ## Complexity: /// - `O(MP + N)` where: /// - `M` old-members-count (code- and governance-bounded) /// - `N` new-members-count (code- and governance-bounded) /// - `P` proposals-count (code-bounded) - /// - DB: - /// - 1 storage mutation (codec `O(M)` read, `O(N)` write) for reading and writing the - /// members - /// - 1 storage read (codec `O(P)`) for reading the proposals - /// - `P` storage mutations (codec `O(M)`) for updating the votes for each proposal - /// - 1 storage write (codec `O(1)`) for deleting the old `prime` and setting the new one - /// # #[pallet::call_index(0)] #[pallet::weight(( T::WeightInfo::set_members( @@ -428,13 +420,11 @@ pub mod pallet { /// /// Origin must be a member of the collective. /// - /// # - /// ## Weight - /// - `O(M + P)` where `M` members-count (code-bounded) and `P` complexity of dispatching - /// `proposal` - /// - DB: 1 read (codec `O(M)`) + DB access of `proposal` - /// - 1 event - /// # + /// ## Complexity: + /// - `O(B + M + P)` where: + /// - `B` is `proposal` size in bytes (length-fee-bounded) + /// - `M` members-count (code-bounded) + /// - `P` complexity of dispatching `proposal` #[pallet::call_index(1)] #[pallet::weight(( T::WeightInfo::execute( @@ -479,26 +469,13 @@ pub mod pallet { /// `threshold` determines whether `proposal` is executed directly (`threshold < 2`) /// or put up for voting. /// - /// # - /// ## Weight + /// ## Complexity /// - `O(B + M + P1)` or `O(B + M + P2)` where: /// - `B` is `proposal` size in bytes (length-fee-bounded) /// - `M` is members-count (code- and governance-bounded) /// - branching is influenced by `threshold` where: /// - `P1` is proposal execution complexity (`threshold < 2`) /// - `P2` is proposals-count (code-bounded) (`threshold >= 2`) - /// - DB: - /// - 1 storage read `is_member` (codec `O(M)`) - /// - 1 storage read `ProposalOf::contains_key` (codec `O(1)`) - /// - DB accesses influenced by `threshold`: - /// - EITHER storage accesses done by `proposal` (`threshold < 2`) - /// - OR proposal insertion (`threshold <= 2`) - /// - 1 storage mutation `Proposals` (codec `O(P2)`) - /// - 1 storage mutation `ProposalCount` (codec `O(1)`) - /// - 1 storage write `ProposalOf` (codec `O(B)`) - /// - 1 storage write `Voting` (codec `O(M)`) - /// - 1 event - /// # #[pallet::call_index(2)] #[pallet::weight(( if *threshold < 2 { @@ -557,14 +534,8 @@ pub mod pallet { /// Transaction fees will be waived if the member is voting on any particular proposal /// for the first time and the call is successful. Subsequent vote changes will charge a /// fee. - /// # - /// ## Weight + /// ## Complexity /// - `O(M)` where `M` is members-count (code- and governance-bounded) - /// - DB: - /// - 1 storage read `Members` (codec `O(M)`) - /// - 1 storage mutation `Voting` (codec `O(M)`) - /// - 1 event - /// # #[pallet::call_index(3)] #[pallet::weight((T::WeightInfo::vote(T::MaxMembers::get()), DispatchClass::Operational))] pub fn vote( @@ -605,20 +576,12 @@ pub mod pallet { /// + `length_bound`: The upper bound for the length of the proposal in storage. Checked via /// `storage::read` so it is `size_of::() == 4` larger than the pure length. /// - /// # - /// ## Weight + /// ## Complexity /// - `O(B + M + P1 + P2)` where: /// - `B` is `proposal` size in bytes (length-fee-bounded) /// - `M` is members-count (code- and governance-bounded) /// - `P1` is the complexity of `proposal` preimage. /// - `P2` is proposal-count (code-bounded) - /// - DB: - /// - 2 storage reads (`Members`: codec `O(M)`, `Prime`: codec `O(1)`) - /// - 3 mutations (`Voting`: codec `O(M)`, `ProposalOf`: codec `O(B)`, `Proposals`: codec - /// `O(P2)`) - /// - any mutations done while executing `proposal` (`P1`) - /// - up to 3 events - /// # #[pallet::call_index(4)] #[pallet::weight(( { @@ -657,12 +620,8 @@ pub mod pallet { /// Parameters: /// * `proposal_hash`: The hash of the proposal that should be disapproved. /// - /// # - /// Complexity: O(P) where P is the number of max proposals - /// DB Weight: - /// * Reads: Proposals - /// * Writes: Voting, Proposals, ProposalOf - /// # + /// ## Complexity + /// O(P) where P is the number of max proposals #[pallet::call_index(5)] #[pallet::weight(T::WeightInfo::disapprove_proposal(T::MaxProposals::get()))] pub fn disapprove_proposal( @@ -692,20 +651,12 @@ pub mod pallet { /// + `length_bound`: The upper bound for the length of the proposal in storage. Checked via /// `storage::read` so it is `size_of::() == 4` larger than the pure length. /// - /// # - /// ## Weight + /// ## Complexity /// - `O(B + M + P1 + P2)` where: /// - `B` is `proposal` size in bytes (length-fee-bounded) /// - `M` is members-count (code- and governance-bounded) /// - `P1` is the complexity of `proposal` preimage. /// - `P2` is proposal-count (code-bounded) - /// - DB: - /// - 2 storage reads (`Members`: codec `O(M)`, `Prime`: codec `O(1)`) - /// - 3 mutations (`Voting`: codec `O(M)`, `ProposalOf`: codec `O(B)`, `Proposals`: codec - /// `O(P2)`) - /// - any mutations done while executing `proposal` (`P1`) - /// - up to 3 events - /// # #[pallet::call_index(6)] #[pallet::weight(( { @@ -1026,18 +977,11 @@ impl, I: 'static> ChangeMembers for Pallet { /// NOTE: Does not enforce the expected `MaxMembers` limit on the amount of members, but /// the weight estimations rely on it to estimate dispatchable weight. /// - /// # - /// ## Weight + /// ## Complexity /// - `O(MP + N)` /// - where `M` old-members-count (governance-bounded) /// - where `N` new-members-count (governance-bounded) /// - where `P` proposals-count - /// - DB: - /// - 1 storage read (codec `O(P)`) for reading the proposals - /// - `P` storage mutations for updating the votes (codec `O(M)`) - /// - 1 storage write (codec `O(N)`) for storing the new members - /// - 1 storage write (codec `O(1)`) for deleting the old prime - /// # fn change_members_sorted( _incoming: &[T::AccountId], outgoing: &[T::AccountId], diff --git a/frame/democracy/src/lib.rs b/frame/democracy/src/lib.rs index 3aa8bc001..d9aae63ce 100644 --- a/frame/democracy/src/lib.rs +++ b/frame/democracy/src/lib.rs @@ -1617,15 +1617,9 @@ impl Pallet { /// Current era is ending; we should finish up any proposals. /// /// - /// # + /// ## Complexity: /// If a referendum is launched or maturing, this will take full block weight if queue is not - /// empty. Otherwise: - /// - Complexity: `O(R)` where `R` is the number of unbaked referenda. - /// - Db reads: `LastTabledWasExternal`, `NextExternal`, `PublicProps`, `account`, - /// `ReferendumCount`, `LowestUnbaked` - /// - Db writes: `PublicProps`, `account`, `ReferendumCount`, `DepositOf`, `ReferendumInfoOf` - /// - Db reads per R: `DepositOf`, `ReferendumInfoOf` - /// # + /// empty. Otherwise, `O(R)` where `R` is the number of unbaked referenda. fn begin_block(now: T::BlockNumber) -> Weight { let max_block_weight = T::BlockWeights::get().max_block; let mut weight = Weight::zero(); diff --git a/frame/elections-phragmen/src/lib.rs b/frame/elections-phragmen/src/lib.rs index c655d4a16..37ee44f34 100644 --- a/frame/elections-phragmen/src/lib.rs +++ b/frame/elections-phragmen/src/lib.rs @@ -435,9 +435,9 @@ pub mod pallet { /// Even if a candidate ends up being a member, they must call [`Call::renounce_candidacy`] /// to get their deposit back. Losing the spot in an election will always lead to a slash. /// - /// # /// The number of current candidates must be provided as witness data. - /// # + /// ## Complexity + /// O(C + log(C)) where C is candidate_count. #[pallet::call_index(2)] #[pallet::weight(T::WeightInfo::submit_candidacy(*candidate_count))] pub fn submit_candidacy( @@ -479,10 +479,12 @@ pub mod pallet { /// next round. /// /// The dispatch origin of this call must be signed, and have one of the above roles. - /// - /// # /// The type of renouncing must be provided as witness data. - /// # + /// + /// ## Complexity + /// - Renouncing::Candidate(count): O(count + log(count)) + /// - Renouncing::Member: O(1) + /// - Renouncing::RunnerUp: O(1) #[pallet::call_index(3)] #[pallet::weight(match *renouncing { Renouncing::Candidate(count) => T::WeightInfo::renounce_candidacy_candidate(count), @@ -542,10 +544,8 @@ pub mod pallet { /// /// Note that this does not affect the designated block number of the next election. /// - /// # - /// If we have a replacement, we use a small weight. Else, since this is a root call and - /// will go into phragmen, we assume full block for now. - /// # + /// ## Complexity + /// - Check details of remove_and_replace_member() and do_phragmen(). #[pallet::call_index(4)] #[pallet::weight(if *rerun_election { T::WeightInfo::remove_member_without_replacement() @@ -579,9 +579,8 @@ pub mod pallet { /// /// The dispatch origin of this call must be root. /// - /// # - /// The total number of voters and those that are defunct must be provided as witness data. - /// # + /// ## Complexity + /// - Check is_defunct_voter() details. #[pallet::call_index(5)] #[pallet::weight(T::WeightInfo::clean_defunct_voters(*_num_voters, *_num_defunct))] pub fn clean_defunct_voters( diff --git a/frame/identity/src/lib.rs b/frame/identity/src/lib.rs index 8eab2c674..39d828ef2 100644 --- a/frame/identity/src/lib.rs +++ b/frame/identity/src/lib.rs @@ -279,11 +279,8 @@ pub mod pallet { /// /// Emits `RegistrarAdded` if successful. /// - /// # + /// ## Complexity /// - `O(R)` where `R` registrar-count (governance-bounded and code-bounded). - /// - One storage mutation (codec `O(R)`). - /// - One event. - /// # #[pallet::call_index(0)] #[pallet::weight(T::WeightInfo::add_registrar(T::MaxRegistrars::get()))] pub fn add_registrar( @@ -322,14 +319,10 @@ pub mod pallet { /// /// Emits `IdentitySet` if successful. /// - /// # + /// ## Complexity /// - `O(X + X' + R)` /// - where `X` additional-field-count (deposit-bounded and code-bounded) /// - where `R` judgements-count (registrar-count-bounded) - /// - One balance reserve operation. - /// - One storage mutation (codec-read `O(X' + R)`, codec-write `O(X + R)`). - /// - One event. - /// # #[pallet::call_index(1)] #[pallet::weight( T::WeightInfo::set_identity( T::MaxRegistrars::get(), // R @@ -389,17 +382,10 @@ pub mod pallet { /// /// - `subs`: The identity's (new) sub-accounts. /// - /// # + /// ## Complexity /// - `O(P + S)` /// - where `P` old-subs-count (hard- and deposit-bounded). /// - where `S` subs-count (hard- and deposit-bounded). - /// - At most one balance operations. - /// - DB: - /// - `P + S` storage mutations (codec complexity `O(1)`) - /// - One storage read (codec complexity `O(P)`). - /// - One storage write (codec complexity `O(S)`). - /// - One storage-exists (`IdentityOf::contains_key`). - /// # // TODO: This whole extrinsic screams "not optimized". For example we could // filter any overlap between new and old subs, and avoid reading/writing // to those values... We could also ideally avoid needing to write to @@ -469,15 +455,11 @@ pub mod pallet { /// /// Emits `IdentityCleared` if successful. /// - /// # + /// ## Complexity /// - `O(R + S + X)` /// - where `R` registrar-count (governance-bounded). /// - where `S` subs-count (hard- and deposit-bounded). /// - where `X` additional-field-count (deposit-bounded and code-bounded). - /// - One balance-unreserve operation. - /// - `2` storage reads and `S + 2` storage deletions. - /// - One event. - /// # #[pallet::call_index(3)] #[pallet::weight(T::WeightInfo::clear_identity( T::MaxRegistrars::get(), // R @@ -524,12 +506,10 @@ pub mod pallet { /// /// Emits `JudgementRequested` if successful. /// - /// # + /// ## Complexity /// - `O(R + X)`. - /// - One balance-reserve operation. - /// - Storage: 1 read `O(R)`, 1 mutate `O(X + R)`. - /// - One event. - /// # + /// - where `R` registrar-count (governance-bounded). + /// - where `X` additional-field-count (deposit-bounded and code-bounded). #[pallet::call_index(4)] #[pallet::weight(T::WeightInfo::request_judgement( T::MaxRegistrars::get(), // R @@ -587,12 +567,10 @@ pub mod pallet { /// /// Emits `JudgementUnrequested` if successful. /// - /// # + /// ## Complexity /// - `O(R + X)`. - /// - One balance-reserve operation. - /// - One storage mutation `O(R + X)`. - /// - One event - /// # + /// - where `R` registrar-count (governance-bounded). + /// - where `X` additional-field-count (deposit-bounded and code-bounded). #[pallet::call_index(5)] #[pallet::weight(T::WeightInfo::cancel_request( T::MaxRegistrars::get(), // R @@ -637,11 +615,9 @@ pub mod pallet { /// - `index`: the index of the registrar whose fee is to be set. /// - `fee`: the new fee. /// - /// # + /// ## Complexity /// - `O(R)`. - /// - One storage mutation `O(R)`. - /// - Benchmark: 7.315 + R * 0.329 µs (min squares analysis) - /// # + /// - where `R` registrar-count (governance-bounded). #[pallet::call_index(6)] #[pallet::weight(T::WeightInfo::set_fee(T::MaxRegistrars::get()))] // R pub fn set_fee( @@ -676,11 +652,9 @@ pub mod pallet { /// - `index`: the index of the registrar whose fee is to be set. /// - `new`: the new account ID. /// - /// # + /// ## Complexity /// - `O(R)`. - /// - One storage mutation `O(R)`. - /// - Benchmark: 8.823 + R * 0.32 µs (min squares analysis) - /// # + /// - where `R` registrar-count (governance-bounded). #[pallet::call_index(7)] #[pallet::weight(T::WeightInfo::set_account_id(T::MaxRegistrars::get()))] // R pub fn set_account_id( @@ -716,11 +690,9 @@ pub mod pallet { /// - `index`: the index of the registrar whose fee is to be set. /// - `fields`: the fields that the registrar concerns themselves with. /// - /// # + /// ## Complexity /// - `O(R)`. - /// - One storage mutation `O(R)`. - /// - Benchmark: 7.464 + R * 0.325 µs (min squares analysis) - /// # + /// - where `R` registrar-count (governance-bounded). #[pallet::call_index(8)] #[pallet::weight(T::WeightInfo::set_fields(T::MaxRegistrars::get()))] // R pub fn set_fields( @@ -763,13 +735,10 @@ pub mod pallet { /// /// Emits `JudgementGiven` if successful. /// - /// # + /// ## Complexity /// - `O(R + X)`. - /// - One balance-transfer operation. - /// - Up to one account-lookup operation. - /// - Storage: 1 read `O(R)`, 1 mutate `O(R + X)`. - /// - One event. - /// # + /// - where `R` registrar-count (governance-bounded). + /// - where `X` additional-field-count (deposit-bounded and code-bounded). #[pallet::call_index(9)] #[pallet::weight(T::WeightInfo::provide_judgement( T::MaxRegistrars::get(), // R @@ -838,12 +807,11 @@ pub mod pallet { /// /// Emits `IdentityKilled` if successful. /// - /// # - /// - `O(R + S + X)`. - /// - One balance-reserve operation. - /// - `S + 2` storage mutations. - /// - One event. - /// # + /// ## Complexity + /// - `O(R + S + X)` + /// - where `R` registrar-count (governance-bounded). + /// - where `S` subs-count (hard- and deposit-bounded). + /// - where `X` additional-field-count (deposit-bounded and code-bounded). #[pallet::call_index(10)] #[pallet::weight(T::WeightInfo::kill_identity( T::MaxRegistrars::get(), // R diff --git a/frame/im-online/src/lib.rs b/frame/im-online/src/lib.rs index 8c1f46978..3be1f53fc 100644 --- a/frame/im-online/src/lib.rs +++ b/frame/im-online/src/lib.rs @@ -463,15 +463,11 @@ pub mod pallet { #[pallet::call] impl Pallet { - /// # - /// - Complexity: `O(K + E)` where K is length of `Keys` (heartbeat.validators_len) and E is - /// length of `heartbeat.network_state.external_address` + /// ## Complexity: + /// - `O(K + E)` where K is length of `Keys` (heartbeat.validators_len) and E is length of + /// `heartbeat.network_state.external_address` /// - `O(K)`: decoding of length `K` /// - `O(E)`: decoding/encoding of length `E` - /// - DbReads: pallet_session `Validators`, pallet_session `CurrentIndex`, `Keys`, - /// `ReceivedHeartbeats` - /// - DbWrites: `ReceivedHeartbeats` - /// # // NOTE: the weight includes the cost of validate_unsigned as it is part of the cost to // import block with such an extrinsic. #[pallet::call_index(0)] diff --git a/frame/indices/src/lib.rs b/frame/indices/src/lib.rs index 95d3cf4b2..0eaee0f4b 100644 --- a/frame/indices/src/lib.rs +++ b/frame/indices/src/lib.rs @@ -90,14 +90,8 @@ pub mod pallet { /// /// Emits `IndexAssigned` if successful. /// - /// # + /// ## Complexity /// - `O(1)`. - /// - One storage mutation (codec `O(1)`). - /// - One reserve operation. - /// - One event. - /// ------------------- - /// - DB Weight: 1 Read/Write (Accounts) - /// # #[pallet::call_index(0)] #[pallet::weight(T::WeightInfo::claim())] pub fn claim(origin: OriginFor, index: T::AccountIndex) -> DispatchResult { @@ -122,16 +116,8 @@ pub mod pallet { /// /// Emits `IndexAssigned` if successful. /// - /// # + /// ## Complexity /// - `O(1)`. - /// - One storage mutation (codec `O(1)`). - /// - One transfer operation. - /// - One event. - /// ------------------- - /// - DB Weight: - /// - Reads: Indices Accounts, System Account (recipient) - /// - Writes: Indices Accounts, System Account (recipient) - /// # #[pallet::call_index(1)] #[pallet::weight(T::WeightInfo::transfer())] pub fn transfer( @@ -165,14 +151,8 @@ pub mod pallet { /// /// Emits `IndexFreed` if successful. /// - /// # + /// ## Complexity /// - `O(1)`. - /// - One storage mutation (codec `O(1)`). - /// - One reserve operation. - /// - One event. - /// ------------------- - /// - DB Weight: 1 Read/Write (Accounts) - /// # #[pallet::call_index(2)] #[pallet::weight(T::WeightInfo::free())] pub fn free(origin: OriginFor, index: T::AccountIndex) -> DispatchResult { @@ -200,16 +180,8 @@ pub mod pallet { /// /// Emits `IndexAssigned` if successful. /// - /// # + /// ## Complexity /// - `O(1)`. - /// - One storage mutation (codec `O(1)`). - /// - Up to one reserve operation. - /// - One event. - /// ------------------- - /// - DB Weight: - /// - Reads: Indices Accounts, System Account (original owner) - /// - Writes: Indices Accounts, System Account (original owner) - /// # #[pallet::call_index(3)] #[pallet::weight(T::WeightInfo::force_transfer())] pub fn force_transfer( @@ -241,14 +213,8 @@ pub mod pallet { /// /// Emits `IndexFrozen` if successful. /// - /// # + /// ## Complexity /// - `O(1)`. - /// - One storage mutation (codec `O(1)`). - /// - Up to one slash operation. - /// - One event. - /// ------------------- - /// - DB Weight: 1 Read/Write (Accounts) - /// # #[pallet::call_index(4)] #[pallet::weight(T::WeightInfo::freeze())] pub fn freeze(origin: OriginFor, index: T::AccountIndex) -> DispatchResult { diff --git a/frame/multisig/src/lib.rs b/frame/multisig/src/lib.rs index 03d7ddec6..de20a9e51 100644 --- a/frame/multisig/src/lib.rs +++ b/frame/multisig/src/lib.rs @@ -263,12 +263,8 @@ pub mod pallet { /// /// Result is equivalent to the dispatched result. /// - /// # + /// ## Complexity /// O(Z + C) where Z is the length of the call and C its execution weight. - /// ------------------------------- - /// - DB Weight: None - /// - Plus Call Weight - /// # #[pallet::call_index(0)] #[pallet::weight({ let dispatch_info = call.get_dispatch_info(); @@ -344,7 +340,7 @@ pub mod pallet { /// on success, result is `Ok` and the result from the interior call, if it was executed, /// may be found in the deposited `MultisigExecuted` event. /// - /// # + /// ## Complexity /// - `O(S + Z + Call)`. /// - Up to one balance-reserve or unreserve operation. /// - One passthrough operation, one insert, both `O(S)` where `S` is the number of @@ -357,12 +353,6 @@ pub mod pallet { /// - The weight of the `call`. /// - Storage: inserts one item, value size bounded by `MaxSignatories`, with a deposit /// taken for its lifetime of `DepositBase + threshold * DepositFactor`. - /// ------------------------------- - /// - DB Weight: - /// - Reads: Multisig Storage, [Caller Account] - /// - Writes: Multisig Storage, [Caller Account] - /// - Plus Call Weight - /// # #[pallet::call_index(1)] #[pallet::weight({ let s = other_signatories.len() as u32; @@ -411,7 +401,7 @@ pub mod pallet { /// /// NOTE: If this is the final approval, you will want to use `as_multi` instead. /// - /// # + /// ## Complexity /// - `O(S)`. /// - Up to one balance-reserve or unreserve operation. /// - One passthrough operation, one insert, both `O(S)` where `S` is the number of @@ -422,11 +412,6 @@ pub mod pallet { /// - One event. /// - Storage: inserts one item, value size bounded by `MaxSignatories`, with a deposit /// taken for its lifetime of `DepositBase + threshold * DepositFactor`. - /// ---------------------------------- - /// - DB Weight: - /// - Read: Multisig Storage, [Caller Account] - /// - Write: Multisig Storage, [Caller Account] - /// # #[pallet::call_index(2)] #[pallet::weight({ let s = other_signatories.len() as u32; @@ -466,7 +451,7 @@ pub mod pallet { /// transaction for this dispatch. /// - `call_hash`: The hash of the call to be executed. /// - /// # + /// ## Complexity /// - `O(S)`. /// - Up to one balance-reserve or unreserve operation. /// - One passthrough operation, one insert, both `O(S)` where `S` is the number of @@ -475,11 +460,6 @@ pub mod pallet { /// - One event. /// - I/O: 1 read `O(S)`, one remove. /// - Storage: removes one item. - /// ---------------------------------- - /// - DB Weight: - /// - Read: Multisig Storage, [Caller Account], Refund Account - /// - Write: Multisig Storage, [Caller Account], Refund Account - /// # #[pallet::call_index(3)] #[pallet::weight(T::WeightInfo::cancel_as_multi(other_signatories.len() as u32))] pub fn cancel_as_multi( diff --git a/frame/nicks/src/lib.rs b/frame/nicks/src/lib.rs index 3d8307c43..05d1a3639 100644 --- a/frame/nicks/src/lib.rs +++ b/frame/nicks/src/lib.rs @@ -129,12 +129,8 @@ pub mod pallet { /// /// The dispatch origin for this call must be _Signed_. /// - /// # + /// ## Complexity /// - O(1). - /// - At most one balance operation. - /// - One storage read/write. - /// - One event. - /// # #[pallet::call_index(0)] #[pallet::weight(50_000_000)] pub fn set_name(origin: OriginFor, name: Vec) -> DispatchResult { @@ -162,12 +158,8 @@ pub mod pallet { /// /// The dispatch origin for this call must be _Signed_. /// - /// # + /// ## Complexity /// - O(1). - /// - One balance operation. - /// - One storage read/write. - /// - One event. - /// # #[pallet::call_index(1)] #[pallet::weight(70_000_000)] pub fn clear_name(origin: OriginFor) -> DispatchResult { @@ -189,12 +181,8 @@ pub mod pallet { /// /// The dispatch origin for this call must match `T::ForceOrigin`. /// - /// # + /// ## Complexity /// - O(1). - /// - One unbalanced handler (probably a balance transfer) - /// - One storage read/write. - /// - One event. - /// # #[pallet::call_index(2)] #[pallet::weight(70_000_000)] pub fn kill_name(origin: OriginFor, target: AccountIdLookupOf) -> DispatchResult { @@ -217,12 +205,8 @@ pub mod pallet { /// /// The dispatch origin for this call must match `T::ForceOrigin`. /// - /// # + /// ## Complexity /// - O(1). - /// - At most one balance operation. - /// - One storage read/write. - /// - One event. - /// # #[pallet::call_index(3)] #[pallet::weight(70_000_000)] pub fn force_name( diff --git a/frame/scheduler/src/lib.rs b/frame/scheduler/src/lib.rs index dfec98d4e..97aaaaf0d 100644 --- a/frame/scheduler/src/lib.rs +++ b/frame/scheduler/src/lib.rs @@ -363,10 +363,6 @@ pub mod pallet { } /// Anonymously schedule a task after a delay. - /// - /// # - /// Same as [`schedule`]. - /// # #[pallet::call_index(4)] #[pallet::weight(::WeightInfo::schedule(T::MaxScheduledPerBlock::get()))] pub fn schedule_after( @@ -389,10 +385,6 @@ pub mod pallet { } /// Schedule a named task after a delay. - /// - /// # - /// Same as [`schedule_named`](Self::schedule_named). - /// # #[pallet::call_index(5)] #[pallet::weight(::WeightInfo::schedule_named(T::MaxScheduledPerBlock::get()))] pub fn schedule_named_after( diff --git a/frame/session/src/lib.rs b/frame/session/src/lib.rs index 4e2caf5e0..38a5c0f57 100644 --- a/frame/session/src/lib.rs +++ b/frame/session/src/lib.rs @@ -587,14 +587,9 @@ pub mod pallet { /// /// The dispatch origin of this function must be signed. /// - /// # - /// - Complexity: `O(1)`. Actual cost depends on the number of length of - /// `T::Keys::key_ids()` which is fixed. - /// - DbReads: `origin account`, `T::ValidatorIdOf`, `NextKeys` - /// - DbWrites: `origin account`, `NextKeys` - /// - DbReads per key id: `KeyOwner` - /// - DbWrites per key id: `KeyOwner` - /// # + /// ## Complexity + /// - `O(1)`. Actual cost depends on the number of length of `T::Keys::key_ids()` which is + /// fixed. #[pallet::call_index(0)] #[pallet::weight(T::WeightInfo::set_keys())] pub fn set_keys(origin: OriginFor, keys: T::Keys, proof: Vec) -> DispatchResult { @@ -614,13 +609,9 @@ pub mod pallet { /// means being a controller account) or directly convertible into a validator ID (which /// usually means being a stash account). /// - /// # - /// - Complexity: `O(1)` in number of key types. Actual cost depends on the number of length - /// of `T::Keys::key_ids()` which is fixed. - /// - DbReads: `T::ValidatorIdOf`, `NextKeys`, `origin account` - /// - DbWrites: `NextKeys`, `origin account` - /// - DbWrites per key id: `KeyOwner` - /// # + /// ## Complexity + /// - `O(1)` in number of key types. Actual cost depends on the number of length of + /// `T::Keys::key_ids()` which is fixed. #[pallet::call_index(1)] #[pallet::weight(T::WeightInfo::purge_keys())] pub fn purge_keys(origin: OriginFor) -> DispatchResult { diff --git a/frame/society/src/lib.rs b/frame/society/src/lib.rs index 18152b9e2..4e83d938b 100644 --- a/frame/society/src/lib.rs +++ b/frame/society/src/lib.rs @@ -689,30 +689,12 @@ pub mod pallet { /// Parameters: /// - `value`: A one time payment the bid would like to receive when joining the society. /// - /// # - /// Key: B (len of bids), C (len of candidates), M (len of members), X (balance reserve) - /// - Storage Reads: - /// - One storage read to check for suspended candidate. O(1) - /// - One storage read to check for suspended member. O(1) - /// - One storage read to retrieve all current bids. O(B) - /// - One storage read to retrieve all current candidates. O(C) - /// - One storage read to retrieve all members. O(M) - /// - Storage Writes: - /// - One storage mutate to add a new bid to the vector O(B) (TODO: possible optimization - /// w/ read) - /// - Up to one storage removal if bid.len() > MAX_BID_COUNT. O(1) - /// - Notable Computation: - /// - O(B + C + log M) search to check user is not already a part of society. - /// - O(log B) search to insert the new bid sorted. - /// - External Pallet Operations: - /// - One balance reserve operation. O(X) - /// - Up to one balance unreserve operation if bids.len() > MAX_BID_COUNT. - /// - Events: - /// - One event for new bid. - /// - Up to one event for AutoUnbid if bid.len() > MAX_BID_COUNT. - /// - /// Total Complexity: O(M + B + C + logM + logB + X) - /// # + /// ## Complexity + /// - O(M + B + C + logM + logB + X) + /// - B (len of bids) + /// - C (len of candidates) + /// - M (len of members) + /// - X (balance reserve) #[pallet::call_index(0)] #[pallet::weight(T::BlockWeights::get().max_block / 10)] pub fn bid(origin: OriginFor, value: BalanceOf) -> DispatchResult { @@ -745,14 +727,10 @@ pub mod pallet { /// Parameters: /// - `pos`: Position in the `Bids` vector of the bid who wants to unbid. /// - /// # - /// Key: B (len of bids), X (balance unreserve) - /// - One storage read and write to retrieve and update the bids. O(B) - /// - Either one unreserve balance action O(X) or one vouching storage removal. O(1) - /// - One event. - /// - /// Total Complexity: O(B + X) - /// # + /// ## Complexity + /// - O(B + X) + /// - B (len of bids) + /// - X (balance unreserve) #[pallet::call_index(1)] #[pallet::weight(T::BlockWeights::get().max_block / 10)] pub fn unbid(origin: OriginFor, pos: u32) -> DispatchResult { @@ -799,33 +777,12 @@ pub mod pallet { /// - `tip`: Your cut of the total `value` payout when the candidate is inducted into /// the society. Tips larger than `value` will be saturated upon payout. /// - /// # - /// Key: B (len of bids), C (len of candidates), M (len of members) - /// - Storage Reads: - /// - One storage read to retrieve all members. O(M) - /// - One storage read to check member is not already vouching. O(1) - /// - One storage read to check for suspended candidate. O(1) - /// - One storage read to check for suspended member. O(1) - /// - One storage read to retrieve all current bids. O(B) - /// - One storage read to retrieve all current candidates. O(C) - /// - Storage Writes: - /// - One storage write to insert vouching status to the member. O(1) - /// - One storage mutate to add a new bid to the vector O(B) (TODO: possible optimization - /// w/ read) - /// - Up to one storage removal if bid.len() > MAX_BID_COUNT. O(1) - /// - Notable Computation: - /// - O(log M) search to check sender is a member. - /// - O(B + C + log M) search to check user is not already a part of society. - /// - O(log B) search to insert the new bid sorted. - /// - External Pallet Operations: - /// - One balance reserve operation. O(X) - /// - Up to one balance unreserve operation if bids.len() > MAX_BID_COUNT. - /// - Events: - /// - One event for vouch. - /// - Up to one event for AutoUnbid if bid.len() > MAX_BID_COUNT. - /// - /// Total Complexity: O(M + B + C + logM + logB + X) - /// # + /// ## Complexity + /// - O(M + B + C + logM + logB + X) + /// - B (len of bids) + /// - C (len of candidates) + /// - M (len of members) + /// - X (balance reserve) #[pallet::call_index(2)] #[pallet::weight(T::BlockWeights::get().max_block / 10)] pub fn vouch( @@ -869,15 +826,9 @@ pub mod pallet { /// Parameters: /// - `pos`: Position in the `Bids` vector of the bid who should be unvouched. /// - /// # - /// Key: B (len of bids) - /// - One storage read O(1) to check the signer is a vouching member. - /// - One storage mutate to retrieve and update the bids. O(B) - /// - One vouching storage removal. O(1) - /// - One event. - /// - /// Total Complexity: O(B) - /// # + /// ## Complexity + /// - O(B) + /// - B (len of bids) #[pallet::call_index(3)] #[pallet::weight(T::BlockWeights::get().max_block / 10)] pub fn unvouch(origin: OriginFor, pos: u32) -> DispatchResult { @@ -910,16 +861,10 @@ pub mod pallet { /// - `approve`: A boolean which says if the candidate should be approved (`true`) or /// rejected (`false`). /// - /// # - /// Key: C (len of candidates), M (len of members) - /// - One storage read O(M) and O(log M) search to check user is a member. - /// - One account lookup. - /// - One storage read O(C) and O(C) search to check that user is a candidate. - /// - One storage write to add vote to votes. O(1) - /// - One event. - /// - /// Total Complexity: O(M + logM + C) - /// # + /// ## Complexity + /// - O(M + logM + C) + /// - C (len of candidates) + /// - M (len of members) #[pallet::call_index(4)] #[pallet::weight(T::BlockWeights::get().max_block / 10)] pub fn vote( @@ -949,14 +894,9 @@ pub mod pallet { /// - `approve`: A boolean which says if the candidate should be /// approved (`true`) or rejected (`false`). /// - /// # - /// - Key: M (len of members) - /// - One storage read O(M) and O(log M) search to check user is a member. - /// - One storage write to add vote to votes. O(1) - /// - One event. - /// - /// Total Complexity: O(M + logM) - /// # + /// ## Complexity + /// - O(M + logM) + /// - M (len of members) #[pallet::call_index(5)] #[pallet::weight(T::BlockWeights::get().max_block / 10)] pub fn defender_vote(origin: OriginFor, approve: bool) -> DispatchResult { @@ -982,16 +922,11 @@ pub mod pallet { /// The dispatch origin for this call must be _Signed_ and a member with /// payouts remaining. /// - /// # - /// Key: M (len of members), P (number of payouts for a particular member) - /// - One storage read O(M) and O(log M) search to check signer is a member. - /// - One storage read O(P) to get all payouts for a member. - /// - One storage read O(1) to get the current block number. - /// - One currency transfer call. O(X) - /// - One storage write or removal to update the member's payouts. O(P) - /// - /// Total Complexity: O(M + logM + P + X) - /// # + /// ## Complexity + /// - O(M + logM + P + X) + /// - M (len of members) + /// - P (number of payouts for a particular member) + /// - X (currency transfer call) #[pallet::call_index(6)] #[pallet::weight(T::BlockWeights::get().max_block / 10)] pub fn payout(origin: OriginFor) -> DispatchResult { @@ -1028,13 +963,8 @@ pub mod pallet { /// - `max_members` - The initial max number of members for the society. /// - `rules` - The rules of this society concerning membership. /// - /// # - /// - Two storage mutates to set `Head` and `Founder`. O(1) - /// - One storage write to add the first member to society. O(1) - /// - One event. - /// - /// Total Complexity: O(1) - /// # + /// ## Complexity + /// - O(1) #[pallet::call_index(7)] #[pallet::weight(T::BlockWeights::get().max_block / 10)] pub fn found( @@ -1063,13 +993,8 @@ pub mod pallet { /// the `Founder` and the `Head`. This implies that it may only be done when there is one /// member. /// - /// # - /// - Two storage reads O(1). - /// - Four storage removals O(1). - /// - One event. - /// - /// Total Complexity: O(1) - /// # + /// ## Complexity + /// - O(1) #[pallet::call_index(8)] #[pallet::weight(T::BlockWeights::get().max_block / 10)] pub fn unfound(origin: OriginFor) -> DispatchResult { @@ -1103,19 +1028,10 @@ pub mod pallet { /// - `forgive` - A boolean representing whether the suspension judgement origin forgives /// (`true`) or rejects (`false`) a suspended member. /// - /// # - /// Key: B (len of bids), M (len of members) - /// - One storage read to check `who` is a suspended member. O(1) - /// - Up to one storage write O(M) with O(log M) binary search to add a member back to - /// society. - /// - Up to 3 storage removals O(1) to clean up a removed member. - /// - Up to one storage write O(B) with O(B) search to remove vouched bid from bids. - /// - Up to one additional event if unvouch takes place. - /// - One storage removal. O(1) - /// - One event for the judgement. - /// - /// Total Complexity: O(M + logM + B) - /// # + /// ## Complexity + /// - O(M + logM + B) + /// - B (len of bids) + /// - M (len of members) #[pallet::call_index(9)] #[pallet::weight(T::BlockWeights::get().max_block / 10)] pub fn judge_suspended_member( @@ -1171,29 +1087,11 @@ pub mod pallet { /// - `who` - The suspended candidate to be judged. /// - `judgement` - `Approve`, `Reject`, or `Rebid`. /// - /// # - /// Key: B (len of bids), M (len of members), X (balance action) - /// - One storage read to check `who` is a suspended candidate. - /// - One storage removal of the suspended candidate. - /// - Approve Logic - /// - One storage read to get the available pot to pay users with. O(1) - /// - One storage write to update the available pot. O(1) - /// - One storage read to get the current block number. O(1) - /// - One storage read to get all members. O(M) - /// - Up to one unreserve currency action. - /// - Up to two new storage writes to payouts. - /// - Up to one storage write with O(log M) binary search to add a member to society. - /// - Reject Logic - /// - Up to one repatriate reserved currency action. O(X) - /// - Up to one storage write to ban the vouching member from vouching again. - /// - Rebid Logic - /// - Storage mutate with O(log B) binary search to place the user back into bids. - /// - Up to one additional event if unvouch takes place. - /// - One storage removal. - /// - One event for the judgement. - /// - /// Total Complexity: O(M + logM + B + X) - /// # + /// ## Complexity + /// - O(M + logM + B + X) + /// - B (len of bids) + /// - M (len of members) + /// - X (balance action) #[pallet::call_index(10)] #[pallet::weight(T::BlockWeights::get().max_block / 10)] pub fn judge_suspended_candidate( @@ -1262,12 +1160,8 @@ pub mod pallet { /// Parameters: /// - `max` - The maximum number of members for the society. /// - /// # - /// - One storage write to update the max. O(1) - /// - One event. - /// - /// Total Complexity: O(1) - /// # + /// ## Complexity + /// - O(1) #[pallet::call_index(11)] #[pallet::weight(T::BlockWeights::get().max_block / 10)] pub fn set_max_members(origin: OriginFor, max: u32) -> DispatchResult { diff --git a/frame/staking/src/pallet/mod.rs b/frame/staking/src/pallet/mod.rs index c0c18b40c..4b0e8133b 100644 --- a/frame/staking/src/pallet/mod.rs +++ b/frame/staking/src/pallet/mod.rs @@ -840,15 +840,13 @@ pub mod pallet { /// The dispatch origin for this call must be _Signed_ by the stash account. /// /// Emits `Bonded`. - /// # + /// ## Complexity /// - Independent of the arguments. Moderate complexity. /// - O(1). /// - Three extra DB entries. /// /// NOTE: Two of the storage writes (`Self::bonded`, `Self::payee`) are _never_ cleaned /// unless the `origin` falls below _existential deposit_ and gets removed as dust. - /// ------------------ - /// # #[pallet::call_index(0)] #[pallet::weight(T::WeightInfo::bond())] pub fn bond( @@ -915,10 +913,9 @@ pub mod pallet { /// /// Emits `Bonded`. /// - /// # + /// ## Complexity /// - Independent of the arguments. Insignificant complexity. /// - O(1). - /// # #[pallet::call_index(1)] #[pallet::weight(T::WeightInfo::bond_extra())] pub fn bond_extra( @@ -1076,10 +1073,9 @@ pub mod pallet { /// /// See also [`Call::unbond`]. /// - /// # - /// Complexity O(S) where S is the number of slashing spans to remove + /// ## Complexity + /// O(S) where S is the number of slashing spans to remove /// NOTE: Weight annotation is the kill scenario, we refund otherwise. - /// # #[pallet::call_index(3)] #[pallet::weight(T::WeightInfo::withdraw_unbonded_kill(*num_slashing_spans))] pub fn withdraw_unbonded( @@ -1136,11 +1132,10 @@ pub mod pallet { /// /// The dispatch origin for this call must be _Signed_ by the controller, not the stash. /// - /// # + /// ## Complexity /// - The transaction's complexity is proportional to the size of `targets` (N) /// which is capped at CompactAssignments::LIMIT (T::MaxNominations). /// - Both the reads and writes follow a similar pattern. - /// # #[pallet::call_index(5)] #[pallet::weight(T::WeightInfo::nominate(targets.len() as u32))] pub fn nominate( @@ -1205,11 +1200,10 @@ pub mod pallet { /// /// The dispatch origin for this call must be _Signed_ by the controller, not the stash. /// - /// # + /// ## Complexity /// - Independent of the arguments. Insignificant complexity. /// - Contains one read. /// - Writes are limited to the `origin` account key. - /// # #[pallet::call_index(6)] #[pallet::weight(T::WeightInfo::chill())] pub fn chill(origin: OriginFor) -> DispatchResult { @@ -1225,16 +1219,12 @@ pub mod pallet { /// /// The dispatch origin for this call must be _Signed_ by the controller, not the stash. /// - /// # + /// ## Complexity + /// - O(1) /// - Independent of the arguments. Insignificant complexity. /// - Contains a limited number of reads. /// - Writes are limited to the `origin` account key. /// --------- - /// - Weight: O(1) - /// - DB Weight: - /// - Read: Ledger - /// - Write: Payee - /// # #[pallet::call_index(7)] #[pallet::weight(T::WeightInfo::set_payee())] pub fn set_payee( @@ -1254,16 +1244,11 @@ pub mod pallet { /// /// The dispatch origin for this call must be _Signed_ by the stash, not the controller. /// - /// # + /// ## Complexity + /// O(1) /// - Independent of the arguments. Insignificant complexity. /// - Contains a limited number of reads. /// - Writes are limited to the `origin` account key. - /// ---------- - /// Weight: O(1) - /// DB Weight: - /// - Read: Bonded, Ledger New Controller, Ledger Old Controller - /// - Write: Bonded, Ledger New Controller, Ledger Old Controller - /// # #[pallet::call_index(8)] #[pallet::weight(T::WeightInfo::set_controller())] pub fn set_controller( @@ -1289,10 +1274,8 @@ pub mod pallet { /// /// The dispatch origin must be Root. /// - /// # - /// Weight: O(1) - /// Write: Validator Count - /// # + /// ## Complexity + /// O(1) #[pallet::call_index(9)] #[pallet::weight(T::WeightInfo::set_validator_count())] pub fn set_validator_count( @@ -1315,9 +1298,8 @@ pub mod pallet { /// /// The dispatch origin must be Root. /// - /// # + /// ## Complexity /// Same as [`Self::set_validator_count`]. - /// # #[pallet::call_index(10)] #[pallet::weight(T::WeightInfo::set_validator_count())] pub fn increase_validator_count( @@ -1341,9 +1323,8 @@ pub mod pallet { /// /// The dispatch origin must be Root. /// - /// # + /// ## Complexity /// Same as [`Self::set_validator_count`]. - /// # #[pallet::call_index(11)] #[pallet::weight(T::WeightInfo::set_validator_count())] pub fn scale_validator_count(origin: OriginFor, factor: Percent) -> DispatchResult { @@ -1370,11 +1351,9 @@ pub mod pallet { /// Thus the election process may be ongoing when this is called. In this case the /// election will continue until the next era is triggered. /// - /// # + /// ## Complexity /// - No arguments. /// - Weight: O(1) - /// - Write: ForceEra - /// # #[pallet::call_index(12)] #[pallet::weight(T::WeightInfo::force_no_eras())] pub fn force_no_eras(origin: OriginFor) -> DispatchResult { @@ -1394,11 +1373,9 @@ pub mod pallet { /// If this is called just before a new era is triggered, the election process may not /// have enough blocks to get a result. /// - /// # + /// ## Complexity /// - No arguments. /// - Weight: O(1) - /// - Write ForceEra - /// # #[pallet::call_index(13)] #[pallet::weight(T::WeightInfo::force_new_era())] pub fn force_new_era(origin: OriginFor) -> DispatchResult { @@ -1497,18 +1474,8 @@ pub mod pallet { /// The origin of this call must be _Signed_. Any account can call this function, even if /// it is not one of the stakers. /// - /// # - /// - Time complexity: at most O(MaxNominatorRewardedPerValidator). - /// - Contains a limited number of reads and writes. - /// ----------- - /// N is the Number of payouts for the validator (including the validator) - /// Weight: - /// - Reward Destination Staked: O(N) - /// - Reward Destination Controller (Creating): O(N) - /// - /// NOTE: weights are assuming that payouts are made to alive stash account (Staked). - /// Paying even a dead controller is cheaper weight-wise. We don't do any refunds here. - /// # + /// ## Complexity + /// - At most O(MaxNominatorRewardedPerValidator). #[pallet::call_index(18)] #[pallet::weight(T::WeightInfo::payout_stakers_alive_staked( T::MaxNominatorRewardedPerValidator::get() @@ -1526,11 +1493,9 @@ pub mod pallet { /// /// The dispatch origin must be signed by the controller. /// - /// # + /// ## Complexity /// - Time complexity: O(L), where L is unlocking chunks /// - Bounded by `MaxUnlockingChunks`. - /// - Storage changes: Can't increase storage, only decrease it. - /// # #[pallet::call_index(19)] #[pallet::weight(T::WeightInfo::rebond(T::MaxUnlockingChunks::get() as u32))] pub fn rebond( diff --git a/frame/sudo/src/lib.rs b/frame/sudo/src/lib.rs index 0867f24b1..3a708b6d5 100644 --- a/frame/sudo/src/lib.rs +++ b/frame/sudo/src/lib.rs @@ -142,12 +142,8 @@ pub mod pallet { /// /// The dispatch origin for this call must be _Signed_. /// - /// # + /// ## Complexity /// - O(1). - /// - Limited storage reads. - /// - One DB write (event). - /// - Weight of derivative `call` execution + 10,000. - /// # #[pallet::call_index(0)] #[pallet::weight({ let dispatch_info = call.get_dispatch_info(); @@ -173,10 +169,8 @@ pub mod pallet { /// /// The dispatch origin for this call must be _Signed_. /// - /// # + /// ## Complexity /// - O(1). - /// - The weight of this call is defined by the caller. - /// # #[pallet::call_index(1)] #[pallet::weight((*_weight, call.get_dispatch_info().class))] pub fn sudo_unchecked_weight( @@ -199,11 +193,8 @@ pub mod pallet { /// /// The dispatch origin for this call must be _Signed_. /// - /// # + /// ## Complexity /// - O(1). - /// - Limited storage reads. - /// - One DB change. - /// # #[pallet::call_index(2)] #[pallet::weight(0)] pub fn set_key( @@ -226,12 +217,8 @@ pub mod pallet { /// /// The dispatch origin for this call must be _Signed_. /// - /// # + /// ## Complexity /// - O(1). - /// - Limited storage reads. - /// - One DB write (event). - /// - Weight of derivative `call` execution + 10,000. - /// # #[pallet::call_index(3)] #[pallet::weight({ let dispatch_info = call.get_dispatch_info(); diff --git a/frame/system/src/lib.rs b/frame/system/src/lib.rs index c5d170600..c22a68296 100644 --- a/frame/system/src/lib.rs +++ b/frame/system/src/lib.rs @@ -374,9 +374,8 @@ pub mod pallet { impl Pallet { /// Make some on-chain remark. /// - /// # + /// ## Complexity /// - `O(1)` - /// # #[pallet::call_index(0)] #[pallet::weight(T::SystemWeightInfo::remark(_remark.len() as u32))] pub fn remark(origin: OriginFor, _remark: Vec) -> DispatchResultWithPostInfo { @@ -396,16 +395,8 @@ pub mod pallet { /// Set the new runtime code. /// - /// # + /// ## Complexity /// - `O(C + S)` where `C` length of `code` and `S` complexity of `can_set_code` - /// - 1 call to `can_set_code`: `O(S)` (calls `sp_io::misc::runtime_version` which is - /// expensive). - /// - 1 storage write (codec `O(C)`). - /// - 1 digest item. - /// - 1 event. - /// The weight of this function is dependent on the runtime, but generally this is very - /// expensive. We will treat this as a full block. - /// # #[pallet::call_index(2)] #[pallet::weight((T::BlockWeights::get().max_block, DispatchClass::Operational))] pub fn set_code(origin: OriginFor, code: Vec) -> DispatchResultWithPostInfo { @@ -417,13 +408,8 @@ pub mod pallet { /// Set the new runtime code without doing any checks of the given `code`. /// - /// # + /// ## Complexity /// - `O(C)` where `C` length of `code` - /// - 1 storage write (codec `O(C)`). - /// - 1 digest item. - /// - 1 event. - /// The weight of this function is dependent on the runtime. We will treat this as a full - /// block. # #[pallet::call_index(3)] #[pallet::weight((T::BlockWeights::get().max_block, DispatchClass::Operational))] pub fn set_code_without_checks( @@ -1423,10 +1409,8 @@ impl Pallet { /// Deposits a log and ensures it matches the block's log data. /// - /// # + /// ## Complexity /// - `O(1)` - /// - 1 storage write (codec `O(1)`) - /// # pub fn deposit_log(item: generic::DigestItem) { >::append(item); } diff --git a/frame/timestamp/src/lib.rs b/frame/timestamp/src/lib.rs index e859474c2..6e9c88782 100644 --- a/frame/timestamp/src/lib.rs +++ b/frame/timestamp/src/lib.rs @@ -171,10 +171,8 @@ pub mod pallet { T::WeightInfo::on_finalize() } - /// # + /// ## Complexity /// - `O(1)` - /// - 1 storage deletion (codec `O(1)`). - /// # fn on_finalize(_n: BlockNumberFor) { assert!(DidUpdate::::take(), "Timestamp must be updated once in the block"); } @@ -192,12 +190,11 @@ pub mod pallet { /// /// The dispatch origin for this call must be `Inherent`. /// - /// # + /// ## Complexity /// - `O(1)` (Note that implementations of `OnTimestampSet` must also be `O(1)`) /// - 1 storage read and 1 storage mutation (codec `O(1)`). (because of `DidUpdate::take` in /// `on_finalize`) /// - 1 event handler `on_timestamp_set`. Must be `O(1)`. - /// # #[pallet::call_index(0)] #[pallet::weight(( T::WeightInfo::set(), diff --git a/frame/tips/src/lib.rs b/frame/tips/src/lib.rs index 43002c7b5..175fc3ac3 100644 --- a/frame/tips/src/lib.rs +++ b/frame/tips/src/lib.rs @@ -231,12 +231,9 @@ pub mod pallet { /// /// Emits `NewTip` if successful. /// - /// # - /// - Complexity: `O(R)` where `R` length of `reason`. + /// ## Complexity + /// - `O(R)` where `R` length of `reason`. /// - encoding and hashing of 'reason' - /// - DbReads: `Reasons`, `Tips` - /// - DbWrites: `Reasons`, `Tips` - /// # #[pallet::call_index(0)] #[pallet::weight(>::WeightInfo::report_awesome(reason.len() as u32))] pub fn report_awesome( @@ -289,12 +286,9 @@ pub mod pallet { /// /// Emits `TipRetracted` if successful. /// - /// # - /// - Complexity: `O(1)` + /// ## Complexity + /// - `O(1)` /// - Depends on the length of `T::Hash` which is fixed. - /// - DbReads: `Tips`, `origin account` - /// - DbWrites: `Reasons`, `Tips`, `origin account` - /// # #[pallet::call_index(1)] #[pallet::weight(>::WeightInfo::retract_tip())] pub fn retract_tip(origin: OriginFor, hash: T::Hash) -> DispatchResult { @@ -325,15 +319,12 @@ pub mod pallet { /// /// Emits `NewTip` if successful. /// - /// # - /// - Complexity: `O(R + T)` where `R` length of `reason`, `T` is the number of tippers. + /// ## Complexity + /// - `O(R + T)` where `R` length of `reason`, `T` is the number of tippers. /// - `O(T)`: decoding `Tipper` vec of length `T`. `T` is charged as upper bound given by /// `ContainsLengthBound`. The actual cost depends on the implementation of /// `T::Tippers`. /// - `O(R)`: hashing and encoding of reason of length `R` - /// - DbReads: `Tippers`, `Reasons` - /// - DbWrites: `Reasons`, `Tips` - /// # #[pallet::call_index(2)] #[pallet::weight(>::WeightInfo::tip_new(reason.len() as u32, T::Tippers::max_len() as u32))] pub fn tip_new( @@ -379,16 +370,13 @@ pub mod pallet { /// Emits `TipClosing` if the threshold of tippers has been reached and the countdown period /// has started. /// - /// # - /// - Complexity: `O(T)` where `T` is the number of tippers. decoding `Tipper` vec of length - /// `T`, insert tip and check closing, `T` is charged as upper bound given by - /// `ContainsLengthBound`. The actual cost depends on the implementation of `T::Tippers`. + /// ## Complexity + /// - `O(T)` where `T` is the number of tippers. decoding `Tipper` vec of length `T`, insert + /// tip and check closing, `T` is charged as upper bound given by `ContainsLengthBound`. + /// The actual cost depends on the implementation of `T::Tippers`. /// /// Actually weight could be lower as it depends on how many tips are in `OpenTip` but it /// is weighted as if almost full i.e of length `T-1`. - /// - DbReads: `Tippers`, `Tips` - /// - DbWrites: `Tips` - /// # #[pallet::call_index(3)] #[pallet::weight(>::WeightInfo::tip(T::Tippers::max_len() as u32))] pub fn tip( @@ -416,13 +404,10 @@ pub mod pallet { /// - `hash`: The identity of the open tip for which a tip value is declared. This is formed /// as the hash of the tuple of the original tip `reason` and the beneficiary account ID. /// - /// # - /// - Complexity: `O(T)` where `T` is the number of tippers. decoding `Tipper` vec of length - /// `T`. `T` is charged as upper bound given by `ContainsLengthBound`. The actual cost - /// depends on the implementation of `T::Tippers`. - /// - DbReads: `Tips`, `Tippers`, `tip finder` - /// - DbWrites: `Reasons`, `Tips`, `Tippers`, `tip finder` - /// # + /// ## Complexity + /// - : `O(T)` where `T` is the number of tippers. decoding `Tipper` vec of length `T`. `T` + /// is charged as upper bound given by `ContainsLengthBound`. The actual cost depends on + /// the implementation of `T::Tippers`. #[pallet::call_index(4)] #[pallet::weight(>::WeightInfo::close_tip(T::Tippers::max_len() as u32))] pub fn close_tip(origin: OriginFor, hash: T::Hash) -> DispatchResult { @@ -446,10 +431,8 @@ pub mod pallet { /// /// Emits `TipSlashed` if successful. /// - /// # - /// `T` is charged as upper bound given by `ContainsLengthBound`. - /// The actual cost depends on the implementation of `T::Tippers`. - /// # + /// ## Complexity + /// - O(1). #[pallet::call_index(5)] #[pallet::weight(>::WeightInfo::slash_tip(T::Tippers::max_len() as u32))] pub fn slash_tip(origin: OriginFor, hash: T::Hash) -> DispatchResult { diff --git a/frame/transaction-storage/src/lib.rs b/frame/transaction-storage/src/lib.rs index cda7610ef..aaa484d58 100644 --- a/frame/transaction-storage/src/lib.rs +++ b/frame/transaction-storage/src/lib.rs @@ -184,10 +184,9 @@ pub mod pallet { impl Pallet { /// Index and store data off chain. Minimum data size is 1 bytes, maximum is /// `MaxTransactionSize`. Data will be removed after `STORAGE_PERIOD` blocks, unless `renew` - /// is called. # - /// - n*log(n) of data size, as all data is pushed to an in-memory trie. - /// Additionally contains a DB write. - /// # + /// is called. + /// ## Complexity + /// - O(n*log(n)) of data size, as all data is pushed to an in-memory trie. #[pallet::call_index(0)] #[pallet::weight(T::WeightInfo::store(data.len() as u32))] pub fn store(origin: OriginFor, data: Vec) -> DispatchResult { @@ -234,9 +233,8 @@ pub mod pallet { /// previous `store` or `renew` call and transaction index within that block. /// Transaction index is emitted in the `Stored` or `Renewed` event. /// Applies same fees as `store`. - /// # - /// - Constant. - /// # + /// ## Complexity + /// - O(1). #[pallet::call_index(1)] #[pallet::weight(T::WeightInfo::renew())] pub fn renew( @@ -277,12 +275,10 @@ pub mod pallet { /// Check storage proof for block number `block_number() - StoragePeriod`. /// If such block does not exist the proof is expected to be `None`. - /// # + /// ## Complexity /// - Linear w.r.t the number of indexed transactions in the proved block for random /// probing. /// There's a DB read for each transaction. - /// Here we assume a maximum of 100 probed transactions. - /// # #[pallet::call_index(2)] #[pallet::weight((T::WeightInfo::check_proof_max(), DispatchClass::Mandatory))] pub fn check_proof( diff --git a/frame/treasury/src/lib.rs b/frame/treasury/src/lib.rs index 1bde12381..f5ca0a984 100644 --- a/frame/treasury/src/lib.rs +++ b/frame/treasury/src/lib.rs @@ -315,13 +315,8 @@ pub mod pallet { #[pallet::hooks] impl, I: 'static> Hooks> for Pallet { - /// # - /// - Complexity: `O(A)` where `A` is the number of approvals - /// - Db reads and writes: `Approvals`, `pot account data` - /// - Db reads and writes per approval: `Proposals`, `proposer account data`, `beneficiary - /// account data` - /// - The weight is overestimated if some approvals got missed. - /// # + /// ## Complexity + /// - `O(A)` where `A` is the number of approvals fn on_initialize(n: T::BlockNumber) -> Weight { let pot = Self::pot(); let deactivated = Deactivated::::get(); @@ -350,11 +345,8 @@ pub mod pallet { /// is reserved and slashed if the proposal is rejected. It is returned once the /// proposal is awarded. /// - /// # - /// - Complexity: O(1) - /// - DbReads: `ProposalCount`, `origin account` - /// - DbWrites: `ProposalCount`, `Proposals`, `origin account` - /// # + /// ## Complexity + /// - O(1) #[pallet::call_index(0)] #[pallet::weight(T::WeightInfo::propose_spend())] pub fn propose_spend( @@ -381,11 +373,8 @@ pub mod pallet { /// /// May only be called from `T::RejectOrigin`. /// - /// # - /// - Complexity: O(1) - /// - DbReads: `Proposals`, `rejected proposer account` - /// - DbWrites: `Proposals`, `rejected proposer account` - /// # + /// ## Complexity + /// - O(1) #[pallet::call_index(1)] #[pallet::weight((T::WeightInfo::reject_proposal(), DispatchClass::Operational))] pub fn reject_proposal( @@ -412,11 +401,8 @@ pub mod pallet { /// /// May only be called from `T::ApproveOrigin`. /// - /// # - /// - Complexity: O(1). - /// - DbReads: `Proposals`, `Approvals` - /// - DbWrite: `Approvals` - /// # + /// ## Complexity + /// - O(1). #[pallet::call_index(2)] #[pallet::weight((T::WeightInfo::approve_proposal(T::MaxApprovals::get()), DispatchClass::Operational))] pub fn approve_proposal( @@ -472,10 +458,8 @@ pub mod pallet { /// May only be called from `T::RejectOrigin`. /// - `proposal_id`: The index of a proposal /// - /// # - /// - Complexity: O(A) where `A` is the number of approvals - /// - Db reads and writes: `Approvals` - /// # + /// ## Complexity + /// - O(A) where `A` is the number of approvals /// /// Errors: /// - `ProposalNotApproved`: The `proposal_id` supplied was not found in the approval queue, diff --git a/frame/utility/src/lib.rs b/frame/utility/src/lib.rs index 2d60ae156..a28dd4994 100644 --- a/frame/utility/src/lib.rs +++ b/frame/utility/src/lib.rs @@ -172,9 +172,8 @@ pub mod pallet { /// If origin is root then the calls are dispatched without checking origin filter. (This /// includes bypassing `frame_system::Config::BaseCallFilter`). /// - /// # - /// - Complexity: O(C) where C is the number of calls to be batched. - /// # + /// ## Complexity + /// - O(C) where C is the number of calls to be batched. /// /// This will return `Ok` in all circumstances. To determine the success of the batch, an /// event is deposited. If a call failed and the batch was interrupted, then the @@ -301,9 +300,8 @@ pub mod pallet { /// If origin is root then the calls are dispatched without checking origin filter. (This /// includes bypassing `frame_system::Config::BaseCallFilter`). /// - /// # - /// - Complexity: O(C) where C is the number of calls to be batched. - /// # + /// ## Complexity + /// - O(C) where C is the number of calls to be batched. #[pallet::call_index(2)] #[pallet::weight({ let dispatch_infos = calls.iter().map(|call| call.get_dispatch_info()).collect::>(); @@ -374,12 +372,8 @@ pub mod pallet { /// /// The dispatch origin for this call must be _Root_. /// - /// # + /// ## Complexity /// - O(1). - /// - Limited storage reads. - /// - One DB write (event). - /// - Weight of derivative `call` execution + T::WeightInfo::dispatch_as(). - /// # #[pallet::call_index(3)] #[pallet::weight({ let dispatch_info = call.get_dispatch_info(); @@ -415,9 +409,8 @@ pub mod pallet { /// If origin is root then the calls are dispatch without checking origin filter. (This /// includes bypassing `frame_system::Config::BaseCallFilter`). /// - /// # - /// - Complexity: O(C) where C is the number of calls to be batched. - /// # + /// ## Complexity + /// - O(C) where C is the number of calls to be batched. #[pallet::call_index(4)] #[pallet::weight({ let dispatch_infos = calls.iter().map(|call| call.get_dispatch_info()).collect::>(); diff --git a/frame/vesting/src/lib.rs b/frame/vesting/src/lib.rs index 3439608af..6cde546f3 100644 --- a/frame/vesting/src/lib.rs +++ b/frame/vesting/src/lib.rs @@ -297,12 +297,8 @@ pub mod pallet { /// /// Emits either `VestingCompleted` or `VestingUpdated`. /// - /// # + /// ## Complexity /// - `O(1)`. - /// - DbWeight: 2 Reads, 2 Writes - /// - Reads: Vesting Storage, Balances Locks, [Sender Account] - /// - Writes: Vesting Storage, Balances Locks, [Sender Account] - /// # #[pallet::call_index(0)] #[pallet::weight(T::WeightInfo::vest_locked(MaxLocksOf::::get(), T::MAX_VESTING_SCHEDULES) .max(T::WeightInfo::vest_unlocked(MaxLocksOf::::get(), T::MAX_VESTING_SCHEDULES)) @@ -321,12 +317,8 @@ pub mod pallet { /// /// Emits either `VestingCompleted` or `VestingUpdated`. /// - /// # + /// ## Complexity /// - `O(1)`. - /// - DbWeight: 3 Reads, 3 Writes - /// - Reads: Vesting Storage, Balances Locks, Target Account - /// - Writes: Vesting Storage, Balances Locks, Target Account - /// # #[pallet::call_index(1)] #[pallet::weight(T::WeightInfo::vest_other_locked(MaxLocksOf::::get(), T::MAX_VESTING_SCHEDULES) .max(T::WeightInfo::vest_other_unlocked(MaxLocksOf::::get(), T::MAX_VESTING_SCHEDULES)) @@ -348,12 +340,8 @@ pub mod pallet { /// /// NOTE: This will unlock all schedules through the current block. /// - /// # + /// ## Complexity /// - `O(1)`. - /// - DbWeight: 3 Reads, 3 Writes - /// - Reads: Vesting Storage, Balances Locks, Target Account, [Sender Account] - /// - Writes: Vesting Storage, Balances Locks, Target Account, [Sender Account] - /// # #[pallet::call_index(2)] #[pallet::weight( T::WeightInfo::vested_transfer(MaxLocksOf::::get(), T::MAX_VESTING_SCHEDULES) @@ -380,12 +368,8 @@ pub mod pallet { /// /// NOTE: This will unlock all schedules through the current block. /// - /// # + /// ## Complexity /// - `O(1)`. - /// - DbWeight: 4 Reads, 4 Writes - /// - Reads: Vesting Storage, Balances Locks, Target Account, Source Account - /// - Writes: Vesting Storage, Balances Locks, Target Account, Source Account - /// # #[pallet::call_index(3)] #[pallet::weight( T::WeightInfo::force_vested_transfer(MaxLocksOf::::get(), T::MAX_VESTING_SCHEDULES) From 3ca3f776e1d566c7f1ab46fad1252ad3cef74e98 Mon Sep 17 00:00:00 2001 From: Michal Kucharczyk <1728078+michalkucharczyk@users.noreply.github.com> Date: Tue, 14 Feb 2023 15:17:58 +0100 Subject: [PATCH 117/558] zombienet: is up timtout increased to 30s (#13386) --- .../test-block-building-warp-sync.zndsl | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/zombienet/0003-block-building-warp-sync/test-block-building-warp-sync.zndsl b/zombienet/0003-block-building-warp-sync/test-block-building-warp-sync.zndsl index 2041366a6..441fa45a8 100644 --- a/zombienet/0003-block-building-warp-sync/test-block-building-warp-sync.zndsl +++ b/zombienet/0003-block-building-warp-sync/test-block-building-warp-sync.zndsl @@ -2,10 +2,10 @@ Description: Warp sync Network: ./test-block-building-warp-sync.toml Creds: config -alice: is up -bob: is up -charlie: is up -dave: is up +alice: is up within 30 seconds +bob: is up within 30 seconds +charlie: is up within 30 seconds +dave: is up within 30 seconds alice: reports node_roles is 4 bob: reports node_roles is 4 From c3ffbcdfaa88420e8aaa421a404c102cfc5c6c6b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bastian=20K=C3=B6cher?= Date: Tue, 14 Feb 2023 16:17:37 +0100 Subject: [PATCH 118/558] pallet-timestamp: Remove `ValidAtTimestamp` error variant (#13346) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * pallet-timestamp: Remove `ValidAtTimestamp` error variant The error variant wasn't that useful and it was also used wrongly in the code. In the code we returned this variant when the `timestamp < minimum`. The problem of this is that we waited on the node side some time, but then `set` function rejects the timestamp because of the same check (the timestamp in the block stays the same). We ensure that the timestamp isn't drifting too much in the future, but waiting for the timestamp to be "valid" would open some attack vector. The consensus protocols also compare the slots in the blocks to ensure that there isn't a block from the future and in the runtime we then ensure that `slot = timestamp / slot_duration`. So, we can just remove this variant and replace it with a new variant `TimeBetweenBlocksTooShort` to not even try importing a block which uses a too short delay since the last block. * Update primitives/timestamp/src/lib.rs Co-authored-by: André Silva <123550+andresilva@users.noreply.github.com> * Rename to `TooEarly` * FMT --------- Co-authored-by: André Silva <123550+andresilva@users.noreply.github.com> --- frame/timestamp/src/lib.rs | 2 +- primitives/timestamp/src/lib.rs | 44 ++++++++------------------------- 2 files changed, 11 insertions(+), 35 deletions(-) diff --git a/frame/timestamp/src/lib.rs b/frame/timestamp/src/lib.rs index 6e9c88782..61a46125a 100644 --- a/frame/timestamp/src/lib.rs +++ b/frame/timestamp/src/lib.rs @@ -255,7 +255,7 @@ pub mod pallet { if t > *(data + MAX_TIMESTAMP_DRIFT_MILLIS) { Err(InherentError::TooFarInFuture) } else if t < minimum { - Err(InherentError::ValidAtTimestamp(minimum.into())) + Err(InherentError::TooEarly) } else { Ok(()) } diff --git a/primitives/timestamp/src/lib.rs b/primitives/timestamp/src/lib.rs index 14b067793..0ec079816 100644 --- a/primitives/timestamp/src/lib.rs +++ b/primitives/timestamp/src/lib.rs @@ -134,10 +134,12 @@ impl From for Timestamp { #[derive(Encode, sp_runtime::RuntimeDebug)] #[cfg_attr(feature = "std", derive(Decode, thiserror::Error))] pub enum InherentError { - /// The timestamp is valid in the future. - /// This is a non-fatal-error and will not stop checking the inherents. - #[cfg_attr(feature = "std", error("Block will be valid at {0}."))] - ValidAtTimestamp(InherentType), + /// The time between the blocks is too short. + #[cfg_attr( + feature = "std", + error("The time since the last timestamp is lower than the minimum period.") + )] + TooEarly, /// The block timestamp is too far in the future #[cfg_attr(feature = "std", error("The timestamp of the block is too far in the future."))] TooFarInFuture, @@ -146,7 +148,7 @@ pub enum InherentError { impl IsFatalError for InherentError { fn is_fatal_error(&self) -> bool { match self { - InherentError::ValidAtTimestamp(_) => false, + InherentError::TooEarly => true, InherentError::TooFarInFuture => true, } } @@ -240,34 +242,8 @@ impl sp_inherents::InherentDataProvider for InherentDataProvider { identifier: &InherentIdentifier, error: &[u8], ) -> Option> { - if *identifier != INHERENT_IDENTIFIER { - return None - } - - match InherentError::try_from(&INHERENT_IDENTIFIER, error)? { - InherentError::ValidAtTimestamp(valid) => { - let max_drift = self.max_drift; - let timestamp = self.timestamp; - // halt import until timestamp is valid. - // reject when too far ahead. - if valid > timestamp + max_drift { - return Some(Err(sp_inherents::Error::Application(Box::from( - InherentError::TooFarInFuture, - )))) - } - - let diff = valid.checked_sub(timestamp).unwrap_or_default(); - log::info!( - target: "timestamp", - "halting for block {} milliseconds in the future", - diff.0, - ); - - futures_timer::Delay::new(diff.as_duration()).await; - - Some(Ok(())) - }, - o => Some(Err(sp_inherents::Error::Application(Box::from(o)))), - } + Some(Err(sp_inherents::Error::Application(Box::from(InherentError::try_from( + identifier, error, + )?)))) } } From 74ee5212765a77177ad90e542894b837be091952 Mon Sep 17 00:00:00 2001 From: Sam Elamin Date: Tue, 14 Feb 2023 17:46:51 +0000 Subject: [PATCH 119/558] add warp to target block for parachains (#12761) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * add warp to target block for parachains * fix for failing tests * format using `Cargo +nightly fmt` * Remove blocking based on PR comments and create new `WarpSync` on poll * remove method from trait * add tests for wait for target * Update client/network/common/src/sync/warp.rs Co-authored-by: Bastian Köcher * Update client/network/common/src/sync/warp.rs Co-authored-by: Bastian Köcher * Update client/network/test/src/sync.rs Co-authored-by: Bastian Köcher * Update client/network/test/src/sync.rs Co-authored-by: Bastian Köcher * Update client/network/test/src/lib.rs Co-authored-by: Bastian Köcher * Update client/network/test/src/sync.rs Co-authored-by: Bastian Köcher * Update client/network/test/src/sync.rs Co-authored-by: Bastian Köcher * code refactor based on pr comments * Second round of PR comments * Third round of pr comments * add comments to explain logic * Update client/network/sync/src/lib.rs Co-authored-by: Bastian Köcher * Update client/network/sync/src/lib.rs Co-authored-by: Bastian Köcher * Update client/network/sync/src/warp.rs Co-authored-by: Bastian Köcher * Update client/network/sync/src/warp.rs Co-authored-by: Bastian Köcher * Update client/network/sync/src/warp.rs Co-authored-by: Bastian Köcher * Update client/network/sync/src/lib.rs Co-authored-by: Bastian Köcher * code refactor based on last PR comments * move warp sync polling before `process_outbound_requests` Add error message if target block fails to be retreived * Update client/network/sync/src/warp.rs Co-authored-by: Arkadiy Paronyan * Update client/network/sync/src/lib.rs Co-authored-by: Bastian Köcher * Update client/network/sync/src/warp.rs Co-authored-by: Bastian Köcher * fmt after code suggestions * rebase changes * Bring down the node if the target block fails to return * Revert "Bring down the node if the target block fails to return" This reverts commit c0ecb220d66dd8e7b1a5ee29831b776f4f18d024. * Update client/network/common/src/sync/warp.rs Co-authored-by: Michal Kucharczyk <1728078+michalkucharczyk@users.noreply.github.com> * Update client/network/common/src/sync/warp.rs Co-authored-by: Michal Kucharczyk <1728078+michalkucharczyk@users.noreply.github.com> * use matching on polling to avoid calling poll more than once * Update client/network/sync/src/warp.rs Co-authored-by: Bastian Köcher * Update client/network/sync/src/warp.rs Co-authored-by: Bastian Köcher * Update client/network/sync/src/warp.rs Co-authored-by: Bastian Köcher * fix typo on comment * update snapshot with new folder structure * Upload snapshot * Bump zombienet * bump zombienet again * Improve test * Update client/network/test/src/sync.rs Co-authored-by: Bastian Köcher * Update client/network/test/src/sync.rs Co-authored-by: Bastian Köcher * fix tests * dummy commit to restart builds * Converted the target block to an optional value that is set to `None` when an error occurs * dummy commit to restart builds --------- Co-authored-by: Bastian Köcher Co-authored-by: Arkadiy Paronyan Co-authored-by: Michal Kucharczyk <1728078+michalkucharczyk@users.noreply.github.com> Co-authored-by: Sebastian Kunert --- bin/node-template/node/src/service.rs | 4 +- bin/node/cli/src/service.rs | 6 +- client/informant/src/display.rs | 5 ++ client/network/common/src/sync/warp.rs | 16 +++- client/network/sync/src/lib.rs | 28 ++++--- client/network/sync/src/warp.rs | 106 ++++++++++++++++++------- client/network/test/src/lib.rs | 20 ++++- client/network/test/src/sync.rs | 38 +++++++++ client/service/src/builder.rs | 25 +++--- client/service/src/lib.rs | 1 + 10 files changed, 189 insertions(+), 60 deletions(-) diff --git a/bin/node-template/node/src/service.rs b/bin/node-template/node/src/service.rs index ee8464688..d7d075efd 100644 --- a/bin/node-template/node/src/service.rs +++ b/bin/node-template/node/src/service.rs @@ -6,7 +6,7 @@ use sc_consensus_aura::{ImportQueueParams, SlotProportion, StartAuraParams}; pub use sc_executor::NativeElseWasmExecutor; use sc_finality_grandpa::SharedVoterState; use sc_keystore::LocalKeystore; -use sc_service::{error::Error as ServiceError, Configuration, TaskManager}; +use sc_service::{error::Error as ServiceError, Configuration, TaskManager, WarpSyncParams}; use sc_telemetry::{Telemetry, TelemetryWorker}; use sp_consensus_aura::sr25519::AuthorityPair as AuraPair; use std::{sync::Arc, time::Duration}; @@ -200,7 +200,7 @@ pub fn new_full(mut config: Configuration) -> Result spawn_handle: task_manager.spawn_handle(), import_queue, block_announce_validator_builder: None, - warp_sync: Some(warp_sync), + warp_sync_params: Some(WarpSyncParams::WithProvider(warp_sync)), })?; if config.offchain_worker.enabled { diff --git a/bin/node/cli/src/service.rs b/bin/node/cli/src/service.rs index b1e9360b4..7ad1a9486 100644 --- a/bin/node/cli/src/service.rs +++ b/bin/node/cli/src/service.rs @@ -32,7 +32,9 @@ use sc_client_api::BlockBackend; use sc_consensus_babe::{self, SlotProportion}; use sc_executor::NativeElseWasmExecutor; use sc_network::NetworkService; -use sc_network_common::{protocol::event::Event, service::NetworkEventStream}; +use sc_network_common::{ + protocol::event::Event, service::NetworkEventStream, sync::warp::WarpSyncParams, +}; use sc_service::{config::Configuration, error::Error as ServiceError, RpcHandlers, TaskManager}; use sc_telemetry::{Telemetry, TelemetryWorker}; use sp_api::ProvideRuntimeApi; @@ -359,7 +361,7 @@ pub fn new_full_base( spawn_handle: task_manager.spawn_handle(), import_queue, block_announce_validator_builder: None, - warp_sync: Some(warp_sync), + warp_sync_params: Some(WarpSyncParams::WithProvider(warp_sync)), })?; if config.offchain_worker.enabled { diff --git a/client/informant/src/display.rs b/client/informant/src/display.rs index 3d585a998..4da46ebb6 100644 --- a/client/informant/src/display.rs +++ b/client/informant/src/display.rs @@ -100,6 +100,11 @@ impl InformantDisplay { _, Some(WarpSyncProgress { phase: WarpSyncPhase::DownloadingBlocks(n), .. }), ) => ("⏩", "Block history".into(), format!(", #{}", n)), + ( + _, + _, + Some(WarpSyncProgress { phase: WarpSyncPhase::AwaitingTargetBlock, .. }), + ) => ("⏩", "Waiting for pending target block".into(), "".into()), (_, _, Some(warp)) => ( "⏩", "Warping".into(), diff --git a/client/network/common/src/sync/warp.rs b/client/network/common/src/sync/warp.rs index c9b903754..020642fb4 100644 --- a/client/network/common/src/sync/warp.rs +++ b/client/network/common/src/sync/warp.rs @@ -15,9 +15,10 @@ // along with Substrate. If not, see . use codec::{Decode, Encode}; +use futures::channel::oneshot; pub use sp_finality_grandpa::{AuthorityList, SetId}; use sp_runtime::traits::{Block as BlockT, NumberFor}; -use std::fmt; +use std::{fmt, sync::Arc}; /// Scale-encoded warp sync proof response. pub struct EncodedProof(pub Vec); @@ -29,6 +30,16 @@ pub struct WarpProofRequest { pub begin: B::Hash, } +/// The different types of warp syncing. +pub enum WarpSyncParams { + /// Standard warp sync for the relay chain + WithProvider(Arc>), + /// Skip downloading proofs and wait for a header of the state that should be downloaded. + /// + /// It is expected that the header provider ensures that the header is trusted. + WaitForTarget(oneshot::Receiver<::Header>), +} + /// Proof verification result. pub enum VerificationResult { /// Proof is valid, but the target was not reached. @@ -62,6 +73,8 @@ pub trait WarpSyncProvider: Send + Sync { pub enum WarpSyncPhase { /// Waiting for peers to connect. AwaitingPeers, + /// Waiting for target block to be received. + AwaitingTargetBlock, /// Downloading and verifying grandpa warp proofs. DownloadingWarpProofs, /// Downloading target block. @@ -78,6 +91,7 @@ impl fmt::Display for WarpSyncPhase { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { match self { Self::AwaitingPeers => write!(f, "Waiting for peers"), + Self::AwaitingTargetBlock => write!(f, "Waiting for target block to be received"), Self::DownloadingWarpProofs => write!(f, "Downloading finality proofs"), Self::DownloadingTargetBlock => write!(f, "Downloading target block"), Self::DownloadingState => write!(f, "Downloading state"), diff --git a/client/network/sync/src/lib.rs b/client/network/sync/src/lib.rs index 312fc6f5b..ffc0edaf3 100644 --- a/client/network/sync/src/lib.rs +++ b/client/network/sync/src/lib.rs @@ -71,7 +71,7 @@ use sc_network_common::{ BlockAnnounce, BlockAnnouncesHandshake, BlockAttributes, BlockData, BlockRequest, BlockResponse, Direction, FromBlock, }, - warp::{EncodedProof, WarpProofRequest, WarpSyncPhase, WarpSyncProgress, WarpSyncProvider}, + warp::{EncodedProof, WarpProofRequest, WarpSyncParams, WarpSyncPhase, WarpSyncProgress}, BadPeer, ChainSync as ChainSyncT, ImportResult, Metrics, OnBlockData, OnBlockJustification, OnStateData, OpaqueBlockRequest, OpaqueBlockResponse, OpaqueStateRequest, OpaqueStateResponse, PeerInfo, PeerRequest, PollBlockAnnounceValidation, SyncMode, @@ -318,8 +318,10 @@ pub struct ChainSync { state_sync: Option>, /// Warp sync in progress, if any. warp_sync: Option>, - /// Warp sync provider. - warp_sync_provider: Option>>, + /// Warp sync params. + /// + /// Will be `None` after `self.warp_sync` is `Some(_)`. + warp_sync_params: Option>, /// Enable importing existing blocks. This is used used after the state download to /// catch up to the latest state while re-importing blocks. import_existing: bool, @@ -565,6 +567,7 @@ where info!("💔 New peer with unknown genesis hash {} ({}).", best_hash, best_number); return Err(BadPeer(who, rep::GENESIS_MISMATCH)) } + // If there are more than `MAJOR_SYNC_BLOCKS` in the import queue then we have // enough to do in the import queue that it's not worth kicking off // an ancestor search, which is what we do in the next match case below. @@ -630,17 +633,15 @@ where }, ); - if let SyncMode::Warp = &self.mode { + if let SyncMode::Warp = self.mode { if self.peers.len() >= MIN_PEERS_TO_START_WARP_SYNC && self.warp_sync.is_none() { log::debug!(target: "sync", "Starting warp state sync."); - if let Some(provider) = &self.warp_sync_provider { - self.warp_sync = - Some(WarpSync::new(self.client.clone(), provider.clone())); + if let Some(params) = self.warp_sync_params.take() { + self.warp_sync = Some(WarpSync::new(self.client.clone(), params)); } } } - Ok(req) }, Ok(BlockStatus::Queued) | @@ -1359,6 +1360,13 @@ where }, } } + + // Should be called before `process_outbound_requests` to ensure + // that a potential target block is directly leading to requests. + if let Some(warp_sync) = &mut self.warp_sync { + let _ = warp_sync.poll(cx); + } + self.process_outbound_requests(); while let Poll::Ready(result) = self.poll_pending_responses(cx) { @@ -1427,7 +1435,7 @@ where roles: Roles, block_announce_validator: Box + Send>, max_parallel_downloads: u32, - warp_sync_provider: Option>>, + warp_sync_params: Option>, metrics_registry: Option<&Registry>, network_service: service::network::NetworkServiceHandle, import_queue: Box>, @@ -1467,13 +1475,13 @@ where block_announce_validation_per_peer_stats: Default::default(), state_sync: None, warp_sync: None, - warp_sync_provider, import_existing: false, gap_sync: None, service_rx, network_service, block_request_protocol_name, state_request_protocol_name, + warp_sync_params, warp_sync_protocol_name, block_announce_protocol_name: block_announce_config .notifications_protocol diff --git a/client/network/sync/src/warp.rs b/client/network/sync/src/warp.rs index ab8a7c66b..bb64ff6a1 100644 --- a/client/network/sync/src/warp.rs +++ b/client/network/sync/src/warp.rs @@ -19,24 +19,35 @@ //! Warp sync support. use crate::{ + oneshot, schema::v1::{StateRequest, StateResponse}, state::{ImportResult, StateSync}, }; +use futures::FutureExt; +use log::error; use sc_client_api::ProofProvider; use sc_network_common::sync::{ message::{BlockAttributes, BlockData, BlockRequest, Direction, FromBlock}, warp::{ - EncodedProof, VerificationResult, WarpProofRequest, WarpSyncPhase, WarpSyncProgress, - WarpSyncProvider, + EncodedProof, VerificationResult, WarpProofRequest, WarpSyncParams, WarpSyncPhase, + WarpSyncProgress, WarpSyncProvider, }, }; use sp_blockchain::HeaderBackend; use sp_finality_grandpa::{AuthorityList, SetId}; use sp_runtime::traits::{Block as BlockT, Header, NumberFor, Zero}; -use std::sync::Arc; +use std::{sync::Arc, task::Poll}; enum Phase { - WarpProof { set_id: SetId, authorities: AuthorityList, last_hash: B::Hash }, + WarpProof { + set_id: SetId, + authorities: AuthorityList, + last_hash: B::Hash, + warp_sync_provider: Arc>, + }, + PendingTargetBlock { + target_block: Option>, + }, TargetBlock(B::Header), State(StateSync), } @@ -61,7 +72,6 @@ pub enum TargetBlockImportResult { pub struct WarpSync { phase: Phase, client: Arc, - warp_sync_provider: Arc>, total_proof_bytes: u64, } @@ -70,21 +80,56 @@ where B: BlockT, Client: HeaderBackend + ProofProvider + 'static, { - /// Create a new instance. - pub fn new(client: Arc, warp_sync_provider: Arc>) -> Self { + /// Create a new instance. When passing a warp sync provider we will be checking for proof and + /// authorities. Alternatively we can pass a target block when we want to skip downloading + /// proofs, in this case we will continue polling until the target block is known. + pub fn new(client: Arc, warp_sync_params: WarpSyncParams) -> Self { let last_hash = client.hash(Zero::zero()).unwrap().expect("Genesis header always exists"); - let phase = Phase::WarpProof { - set_id: 0, - authorities: warp_sync_provider.current_authorities(), - last_hash, + match warp_sync_params { + WarpSyncParams::WithProvider(warp_sync_provider) => { + let phase = Phase::WarpProof { + set_id: 0, + authorities: warp_sync_provider.current_authorities(), + last_hash, + warp_sync_provider: warp_sync_provider.clone(), + }; + Self { client, phase, total_proof_bytes: 0 } + }, + WarpSyncParams::WaitForTarget(block) => Self { + client, + phase: Phase::PendingTargetBlock { target_block: Some(block) }, + total_proof_bytes: 0, + }, + } + } + + /// Poll to make progress. + /// + /// This only makes progress when `phase = Phase::PendingTargetBlock` and the pending block was + /// sent. + pub fn poll(&mut self, cx: &mut std::task::Context) { + let new_phase = if let Phase::PendingTargetBlock { target_block: Some(target_block) } = + &mut self.phase + { + match target_block.poll_unpin(cx) { + Poll::Ready(Ok(target)) => Phase::TargetBlock(target), + Poll::Ready(Err(e)) => { + error!(target: "sync", "Failed to get target block. Error: {:?}",e); + Phase::PendingTargetBlock { target_block: None } + }, + _ => return, + } + } else { + return }; - Self { client, warp_sync_provider, phase, total_proof_bytes: 0 } + + self.phase = new_phase; } /// Validate and import a state response. pub fn import_state(&mut self, response: StateResponse) -> ImportResult { match &mut self.phase { - Phase::WarpProof { .. } | Phase::TargetBlock(_) => { + Phase::WarpProof { .. } | Phase::TargetBlock(_) | Phase::PendingTargetBlock { .. } => { log::debug!(target: "sync", "Unexpected state response"); ImportResult::BadResponse }, @@ -95,12 +140,12 @@ where /// Validate and import a warp proof response. pub fn import_warp_proof(&mut self, response: EncodedProof) -> WarpProofImportResult { match &mut self.phase { - Phase::State(_) | Phase::TargetBlock(_) => { + Phase::State(_) | Phase::TargetBlock(_) | Phase::PendingTargetBlock { .. } => { log::debug!(target: "sync", "Unexpected warp proof response"); WarpProofImportResult::BadResponse }, - Phase::WarpProof { set_id, authorities, last_hash } => { - match self.warp_sync_provider.verify(&response, *set_id, authorities.clone()) { + Phase::WarpProof { set_id, authorities, last_hash, warp_sync_provider } => + match warp_sync_provider.verify(&response, *set_id, authorities.clone()) { Err(e) => { log::debug!(target: "sync", "Bad warp proof response: {}", e); WarpProofImportResult::BadResponse @@ -119,15 +164,14 @@ where self.phase = Phase::TargetBlock(header); WarpProofImportResult::Success }, - } - }, + }, } } /// Import the target block body. pub fn import_target_block(&mut self, block: BlockData) -> TargetBlockImportResult { match &mut self.phase { - Phase::WarpProof { .. } | Phase::State(_) => { + Phase::WarpProof { .. } | Phase::State(_) | Phase::PendingTargetBlock { .. } => { log::debug!(target: "sync", "Unexpected target block response"); TargetBlockImportResult::BadResponse }, @@ -168,8 +212,8 @@ where /// Produce next state request. pub fn next_state_request(&self) -> Option { match &self.phase { - Phase::WarpProof { .. } => None, - Phase::TargetBlock(_) => None, + Phase::WarpProof { .. } | Phase::TargetBlock(_) | Phase::PendingTargetBlock { .. } => + None, Phase::State(sync) => Some(sync.next_request()), } } @@ -178,15 +222,14 @@ where pub fn next_warp_proof_request(&self) -> Option> { match &self.phase { Phase::WarpProof { last_hash, .. } => Some(WarpProofRequest { begin: *last_hash }), - Phase::TargetBlock(_) => None, - Phase::State(_) => None, + Phase::TargetBlock(_) | Phase::State(_) | Phase::PendingTargetBlock { .. } => None, } } /// Produce next target block request. pub fn next_target_block_request(&self) -> Option<(NumberFor, BlockRequest)> { match &self.phase { - Phase::WarpProof { .. } => None, + Phase::WarpProof { .. } | Phase::State(_) | Phase::PendingTargetBlock { .. } => None, Phase::TargetBlock(header) => { let request = BlockRequest:: { id: 0, @@ -198,15 +241,14 @@ where }; Some((*header.number(), request)) }, - Phase::State(_) => None, } } /// Return target block hash if it is known. pub fn target_block_hash(&self) -> Option { match &self.phase { - Phase::WarpProof { .. } => None, - Phase::TargetBlock(_) => None, + Phase::WarpProof { .. } | Phase::TargetBlock(_) | Phase::PendingTargetBlock { .. } => + None, Phase::State(s) => Some(s.target()), } } @@ -214,7 +256,7 @@ where /// Return target block number if it is known. pub fn target_block_number(&self) -> Option> { match &self.phase { - Phase::WarpProof { .. } => None, + Phase::WarpProof { .. } | Phase::PendingTargetBlock { .. } => None, Phase::TargetBlock(header) => Some(*header.number()), Phase::State(s) => Some(s.target_block_num()), } @@ -223,8 +265,8 @@ where /// Check if the state is complete. pub fn is_complete(&self) -> bool { match &self.phase { - Phase::WarpProof { .. } => false, - Phase::TargetBlock(_) => false, + Phase::WarpProof { .. } | Phase::TargetBlock(_) | Phase::PendingTargetBlock { .. } => + false, Phase::State(sync) => sync.is_complete(), } } @@ -240,6 +282,10 @@ where phase: WarpSyncPhase::DownloadingTargetBlock, total_bytes: self.total_proof_bytes, }, + Phase::PendingTargetBlock { .. } => WarpSyncProgress { + phase: WarpSyncPhase::AwaitingTargetBlock, + total_bytes: self.total_proof_bytes, + }, Phase::State(sync) => WarpSyncProgress { phase: if self.is_complete() { WarpSyncPhase::ImportingState diff --git a/client/network/test/src/lib.rs b/client/network/test/src/lib.rs index b3653ac7c..ccaebc976 100644 --- a/client/network/test/src/lib.rs +++ b/client/network/test/src/lib.rs @@ -31,7 +31,7 @@ use std::{ time::Duration, }; -use futures::{future::BoxFuture, prelude::*}; +use futures::{channel::oneshot, future::BoxFuture, prelude::*}; use libp2p::{build_multiaddr, PeerId}; use log::trace; use parking_lot::Mutex; @@ -56,7 +56,9 @@ use sc_network_common::{ }, protocol::{role::Roles, ProtocolName}, service::{NetworkBlock, NetworkStateInfo, NetworkSyncForkRequest}, - sync::warp::{AuthorityList, EncodedProof, SetId, VerificationResult, WarpSyncProvider}, + sync::warp::{ + AuthorityList, EncodedProof, SetId, VerificationResult, WarpSyncParams, WarpSyncProvider, + }, }; use sc_network_light::light_client_requests::handler::LightClientRequestHandler; use sc_network_sync::{ @@ -722,6 +724,8 @@ pub struct FullPeerConfig { pub extra_storage: Option, /// Enable transaction indexing. pub storage_chain: bool, + /// Optional target block header to sync to + pub target_block: Option<::Header>, } #[async_trait::async_trait] @@ -867,6 +871,15 @@ where let warp_sync = Arc::new(TestWarpSyncProvider(client.clone())); + let warp_sync_params = match config.target_block { + Some(target_block) => { + let (sender, receiver) = oneshot::channel::<::Header>(); + let _ = sender.send(target_block); + WarpSyncParams::WaitForTarget(receiver) + }, + _ => WarpSyncParams::WithProvider(warp_sync.clone()), + }; + let warp_protocol_config = { let (handler, protocol_config) = warp_request_handler::RequestHandler::new( protocol_id.clone(), @@ -887,6 +900,7 @@ where .unwrap_or_else(|| Box::new(DefaultBlockAnnounceValidator)); let (chain_sync_network_provider, chain_sync_network_handle) = NetworkServiceProvider::new(); + let (chain_sync, chain_sync_service, block_announce_config) = ChainSync::new( match network_config.sync_mode { SyncMode::Full => sc_network_common::sync::SyncMode::Full, @@ -903,7 +917,7 @@ where Roles::from(if config.is_authority { &Role::Authority } else { &Role::Full }), block_announce_validator, network_config.max_parallel_downloads, - Some(warp_sync), + Some(warp_sync_params), None, chain_sync_network_handle, import_queue.service(), diff --git a/client/network/test/src/sync.rs b/client/network/test/src/sync.rs index b629574fe..8c2306403 100644 --- a/client/network/test/src/sync.rs +++ b/client/network/test/src/sync.rs @@ -1249,6 +1249,44 @@ async fn warp_sync() { .await; } +#[tokio::test(flavor = "multi_thread", worker_threads = 2)] +async fn warp_sync_to_target_block() { + sp_tracing::try_init_simple(); + let mut net = TestNet::new(0); + // Create 3 synced peers and 1 peer trying to warp sync. + net.add_full_peer_with_config(Default::default()); + net.add_full_peer_with_config(Default::default()); + net.add_full_peer_with_config(Default::default()); + + let blocks = net.peer(0).push_blocks(64, false); + let target = blocks[63]; + net.peer(1).push_blocks(64, false); + net.peer(2).push_blocks(64, false); + + let target_block = net.peer(0).client.header(target).unwrap().unwrap(); + + net.add_full_peer_with_config(FullPeerConfig { + sync_mode: SyncMode::Warp, + target_block: Some(target_block), + ..Default::default() + }); + + net.run_until_sync().await; + assert!(net.peer(3).client().has_state_at(&BlockId::Number(64))); + + // Wait for peer 1 download block history + futures::future::poll_fn::<(), _>(|cx| { + net.poll(cx); + let peer = net.peer(3); + if blocks.iter().all(|b| peer.has_body(*b)) { + Poll::Ready(()) + } else { + Poll::Pending + } + }) + .await; +} + #[tokio::test(flavor = "multi_thread", worker_threads = 2)] async fn syncs_huge_blocks() { use sp_core::storage::well_known_keys::HEAP_PAGES; diff --git a/client/service/src/builder.rs b/client/service/src/builder.rs index 0b09f550c..a737601f7 100644 --- a/client/service/src/builder.rs +++ b/client/service/src/builder.rs @@ -43,7 +43,7 @@ use sc_network_bitswap::BitswapRequestHandler; use sc_network_common::{ protocol::role::Roles, service::{NetworkStateInfo, NetworkStatusProvider}, - sync::warp::WarpSyncProvider, + sync::warp::WarpSyncParams, }; use sc_network_light::light_client_requests::handler::LightClientRequestHandler; use sc_network_sync::{ @@ -759,8 +759,8 @@ pub struct BuildNetworkParams<'a, TBl: BlockT, TExPool, TImpQu, TCl> { /// A block announce validator builder. pub block_announce_validator_builder: Option) -> Box + Send> + Send>>, - /// An optional warp sync provider. - pub warp_sync: Option>>, + /// Optional warp sync params. + pub warp_sync_params: Option>, } /// Build the network service, the network status sinks and an RPC sender. pub fn build_network( @@ -795,12 +795,12 @@ where spawn_handle, import_queue, block_announce_validator_builder, - warp_sync, + warp_sync_params, } = params; let mut request_response_protocol_configs = Vec::new(); - if warp_sync.is_none() && config.network.sync_mode.is_warp() { + if warp_sync_params.is_none() && config.network.sync_mode.is_warp() { return Err("Warp sync enabled, but no warp sync provider configured.".into()) } @@ -845,8 +845,8 @@ where protocol_config }; - let (warp_sync_provider, warp_sync_protocol_config) = warp_sync - .map(|provider| { + let warp_sync_protocol_config = match warp_sync_params.as_ref() { + Some(WarpSyncParams::WithProvider(warp_with_provider)) => { // Allow both outgoing and incoming requests. let (handler, protocol_config) = WarpSyncRequestHandler::new( protocol_id.clone(), @@ -856,12 +856,13 @@ where .flatten() .expect("Genesis block exists; qed"), config.chain_spec.fork_id(), - provider.clone(), + warp_with_provider.clone(), ); spawn_handle.spawn("warp-sync-request-handler", Some("networking"), handler.run()); - (Some(provider), Some(protocol_config)) - }) - .unwrap_or_default(); + Some(protocol_config) + }, + _ => None, + }; let light_client_request_protocol_config = { // Allow both outgoing and incoming requests. @@ -888,7 +889,7 @@ where Roles::from(&config.role), block_announce_validator, config.network.max_parallel_downloads, - warp_sync_provider, + warp_sync_params, config.prometheus_config.as_ref().map(|config| config.registry.clone()).as_ref(), chain_sync_network_handle, import_queue.service(), diff --git a/client/service/src/lib.rs b/client/service/src/lib.rs index 1529b822a..e3dcf0128 100644 --- a/client/service/src/lib.rs +++ b/client/service/src/lib.rs @@ -73,6 +73,7 @@ pub use sc_chain_spec::{ pub use sc_consensus::ImportQueue; pub use sc_executor::NativeExecutionDispatch; +pub use sc_network_common::sync::warp::WarpSyncParams; #[doc(hidden)] pub use sc_network_transactions::config::{TransactionImport, TransactionImportFuture}; pub use sc_rpc::{ From 169c3f02449566638225ab73bdf75a95cb8b1da0 Mon Sep 17 00:00:00 2001 From: Sasha Gryaznov Date: Tue, 14 Feb 2023 20:28:34 +0200 Subject: [PATCH 120/558] [contracts] make `debug_message` execution outcome invariant to node debug logging setting (#13197) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * update benchmark for seal_debug_message * add seal_debug_message_per_kb benchmark * un-fallable debug buffer: silently drops excessive and wrong utf-8 encoded messages * charge debug_message per byte of the message * improved benchmark * cap debug_message * ".git/.scripts/commands/bench/bench.sh" pallet dev pallet_contracts * Apply suggestions from code review Co-authored-by: Alexander Theißen * fix applied buggy suggestion * make sure i*1024 < MaxDebugBufferLen * fix schedule for our non-batched benchmark * Switch to a `wasmtime` fork with LTO linking failure workaround * ".git/.scripts/commands/bench/bench.sh" pallet dev pallet_contracts --------- Co-authored-by: command-bot <> Co-authored-by: Alexander Theißen Co-authored-by: Jan Bujak --- Cargo.lock | 57 +- client/executor/wasmtime/Cargo.toml | 2 +- .../fixtures/debug_message_invalid_utf8.wat | 24 +- .../debug_message_logging_disabled.wat | 2 +- frame/contracts/src/benchmarking/mod.rs | 73 +- frame/contracts/src/exec.rs | 51 +- frame/contracts/src/lib.rs | 2 - frame/contracts/src/schedule.rs | 4 + frame/contracts/src/tests.rs | 3 +- frame/contracts/src/wasm/mod.rs | 9 +- frame/contracts/src/wasm/runtime.rs | 35 +- frame/contracts/src/weights.rs | 2174 ++++++++++------- primitives/wasm-interface/Cargo.toml | 2 +- 13 files changed, 1373 insertions(+), 1065 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 0c2f85184..ef320a85f 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1186,8 +1186,7 @@ checksum = "dcb25d077389e53838a8158c8e99174c5a9d902dee4904320db714f3c653ffba" [[package]] name = "cranelift-bforest" version = "0.92.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2f3d54eab028f5805ae3b26fd60eca3f3a9cfb76b989d9bab173be3f61356cc3" +source = "git+https://github.com/paritytech/wasmtime.git?branch=v5.0.0_lto_fix#8a02705ad378108e43abe23c538688adf73f3b71" dependencies = [ "cranelift-entity", ] @@ -1195,8 +1194,7 @@ dependencies = [ [[package]] name = "cranelift-codegen" version = "0.92.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2be1d5f2c3cca1efb691844bc1988b89c77291f13f778499a3f3c0cf49c0ed61" +source = "git+https://github.com/paritytech/wasmtime.git?branch=v5.0.0_lto_fix#8a02705ad378108e43abe23c538688adf73f3b71" dependencies = [ "arrayvec 0.7.2", "bumpalo", @@ -1216,8 +1214,7 @@ dependencies = [ [[package]] name = "cranelift-codegen-meta" version = "0.92.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3f9b1b1089750ce4005893af7ee00bb08a2cf1c9779999c0f7164cbc8ad2e0d2" +source = "git+https://github.com/paritytech/wasmtime.git?branch=v5.0.0_lto_fix#8a02705ad378108e43abe23c538688adf73f3b71" dependencies = [ "cranelift-codegen-shared", ] @@ -1225,14 +1222,12 @@ dependencies = [ [[package]] name = "cranelift-codegen-shared" version = "0.92.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cc5fbaec51de47297fd7304986fd53c8c0030abbe69728a60d72e1c63559318d" +source = "git+https://github.com/paritytech/wasmtime.git?branch=v5.0.0_lto_fix#8a02705ad378108e43abe23c538688adf73f3b71" [[package]] name = "cranelift-entity" version = "0.92.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dab984c94593f876090fae92e984bdcc74d9b1acf740ab5f79036001c65cba13" +source = "git+https://github.com/paritytech/wasmtime.git?branch=v5.0.0_lto_fix#8a02705ad378108e43abe23c538688adf73f3b71" dependencies = [ "serde", ] @@ -1240,8 +1235,7 @@ dependencies = [ [[package]] name = "cranelift-frontend" version = "0.92.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6e0cb3102d21a2fe5f3210af608748ddd0cd09825ac12d42dc56ed5ed8725fe0" +source = "git+https://github.com/paritytech/wasmtime.git?branch=v5.0.0_lto_fix#8a02705ad378108e43abe23c538688adf73f3b71" dependencies = [ "cranelift-codegen", "log", @@ -1252,14 +1246,12 @@ dependencies = [ [[package]] name = "cranelift-isle" version = "0.92.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "72101dd1f441d629735143c41e00b3428f9267738176983ef588ff43382af0a0" +source = "git+https://github.com/paritytech/wasmtime.git?branch=v5.0.0_lto_fix#8a02705ad378108e43abe23c538688adf73f3b71" [[package]] name = "cranelift-native" version = "0.92.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c22b0d9fcbe3fc5a1af9e7021b44ce42b930bcefac446ce22e02e8f9a0d67120" +source = "git+https://github.com/paritytech/wasmtime.git?branch=v5.0.0_lto_fix#8a02705ad378108e43abe23c538688adf73f3b71" dependencies = [ "cranelift-codegen", "libc", @@ -1269,8 +1261,7 @@ dependencies = [ [[package]] name = "cranelift-wasm" version = "0.92.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bddebe32fb14fbfd9efa5f130ffb8f4665795de019928dcd7247b136c46f9249" +source = "git+https://github.com/paritytech/wasmtime.git?branch=v5.0.0_lto_fix#8a02705ad378108e43abe23c538688adf73f3b71" dependencies = [ "cranelift-codegen", "cranelift-entity", @@ -11932,8 +11923,7 @@ dependencies = [ [[package]] name = "wasmtime" version = "5.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4e5b183a159484980138cc05231419c536d395a7b25c1802091310ea2f74276a" +source = "git+https://github.com/paritytech/wasmtime.git?branch=v5.0.0_lto_fix#8a02705ad378108e43abe23c538688adf73f3b71" dependencies = [ "anyhow", "bincode", @@ -11960,8 +11950,7 @@ dependencies = [ [[package]] name = "wasmtime-asm-macros" version = "5.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c0aeb1cb256d76cf07b20264c808351c8b525ece56de1ef4d93f87a0aaf342db" +source = "git+https://github.com/paritytech/wasmtime.git?branch=v5.0.0_lto_fix#8a02705ad378108e43abe23c538688adf73f3b71" dependencies = [ "cfg-if", ] @@ -11969,8 +11958,7 @@ dependencies = [ [[package]] name = "wasmtime-cache" version = "5.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "830570847f905b8f6d2ca635c33cf42ce701dd8e4abd7d1806c631f8f06e9e4b" +source = "git+https://github.com/paritytech/wasmtime.git?branch=v5.0.0_lto_fix#8a02705ad378108e43abe23c538688adf73f3b71" dependencies = [ "anyhow", "base64 0.13.1", @@ -11989,8 +11977,7 @@ dependencies = [ [[package]] name = "wasmtime-cranelift" version = "5.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2f7695d3814dcb508bf4d1c181a86ea6b97a209f6444478e95d86e2ffab8d1a3" +source = "git+https://github.com/paritytech/wasmtime.git?branch=v5.0.0_lto_fix#8a02705ad378108e43abe23c538688adf73f3b71" dependencies = [ "anyhow", "cranelift-codegen", @@ -12010,8 +11997,7 @@ dependencies = [ [[package]] name = "wasmtime-environ" version = "5.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e5a2a5f0fb93aa837a727a48dd1076e8a9f882cc2fee20b433c04a18740ff63b" +source = "git+https://github.com/paritytech/wasmtime.git?branch=v5.0.0_lto_fix#8a02705ad378108e43abe23c538688adf73f3b71" dependencies = [ "anyhow", "cranelift-entity", @@ -12029,8 +12015,7 @@ dependencies = [ [[package]] name = "wasmtime-jit" version = "5.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "01c78f9fb2922dbb5a95f009539d4badb44866caeeb53d156bf2cf4d683c3afd" +source = "git+https://github.com/paritytech/wasmtime.git?branch=v5.0.0_lto_fix#8a02705ad378108e43abe23c538688adf73f3b71" dependencies = [ "addr2line 0.17.0", "anyhow", @@ -12053,8 +12038,7 @@ dependencies = [ [[package]] name = "wasmtime-jit-debug" version = "5.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "67cacdb52a77b8c8e744e510beeabf0bd698b1c94c59eed33c52b3fbd19639b0" +source = "git+https://github.com/paritytech/wasmtime.git?branch=v5.0.0_lto_fix#8a02705ad378108e43abe23c538688adf73f3b71" dependencies = [ "object 0.29.0", "once_cell", @@ -12064,8 +12048,7 @@ dependencies = [ [[package]] name = "wasmtime-jit-icache-coherence" version = "5.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "08fcba5ebd96da2a9f0747ab6337fe9788adfb3f63fa2c180520d665562d257e" +source = "git+https://github.com/paritytech/wasmtime.git?branch=v5.0.0_lto_fix#8a02705ad378108e43abe23c538688adf73f3b71" dependencies = [ "cfg-if", "libc", @@ -12075,8 +12058,7 @@ dependencies = [ [[package]] name = "wasmtime-runtime" version = "5.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0793210acf50d4c69182c916abaee1d423dc5d172cdfde6acfea2f9446725940" +source = "git+https://github.com/paritytech/wasmtime.git?branch=v5.0.0_lto_fix#8a02705ad378108e43abe23c538688adf73f3b71" dependencies = [ "anyhow", "cc", @@ -12099,8 +12081,7 @@ dependencies = [ [[package]] name = "wasmtime-types" version = "5.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "50d015ba8b231248a811e323cf7a525cd3f982d4be0b9e62d27685102e5f12b1" +source = "git+https://github.com/paritytech/wasmtime.git?branch=v5.0.0_lto_fix#8a02705ad378108e43abe23c538688adf73f3b71" dependencies = [ "cranelift-entity", "serde", diff --git a/client/executor/wasmtime/Cargo.toml b/client/executor/wasmtime/Cargo.toml index acd1a5aef..e29a19527 100644 --- a/client/executor/wasmtime/Cargo.toml +++ b/client/executor/wasmtime/Cargo.toml @@ -25,7 +25,7 @@ wasmtime = { version = "5.0.0", default-features = false, features = [ "jitdump", "parallel-compilation", "pooling-allocator" -] } +], git = "https://github.com/paritytech/wasmtime.git", branch = "v5.0.0_lto_fix" } anyhow = "1.0.68" sc-allocator = { version = "4.1.0-dev", path = "../../allocator" } sc-executor-common = { version = "0.10.0-dev", path = "../common" } diff --git a/frame/contracts/fixtures/debug_message_invalid_utf8.wat b/frame/contracts/fixtures/debug_message_invalid_utf8.wat index c60371076..e8c447b42 100644 --- a/frame/contracts/fixtures/debug_message_invalid_utf8.wat +++ b/frame/contracts/fixtures/debug_message_invalid_utf8.wat @@ -1,18 +1,28 @@ -;; Emit a "Hello World!" debug message +;; Emit a debug message with an invalid utf-8 code (module (import "seal0" "seal_debug_message" (func $seal_debug_message (param i32 i32) (result i32))) (import "env" "memory" (memory 1 1)) (data (i32.const 0) "\fc") - (func (export "call") - (call $seal_debug_message - (i32.const 0) ;; Pointer to the text buffer - (i32.const 12) ;; The size of the buffer + (func $assert_eq (param i32 i32) + (block $ok + (br_if $ok + (i32.eq (get_local 0) (get_local 1)) + ) + (unreachable) ) - ;; the above call traps because we supplied invalid utf8 - unreachable ) + (func (export "call") + (call $assert_eq + (call $seal_debug_message + (i32.const 0) ;; Pointer to the text buffer + (i32.const 12) ;; The size of the buffer + ) + (i32.const 0) ;; Success return code + ) + ) + (func (export "deploy")) ) diff --git a/frame/contracts/fixtures/debug_message_logging_disabled.wat b/frame/contracts/fixtures/debug_message_logging_disabled.wat index cfe238943..fc6ee72df 100644 --- a/frame/contracts/fixtures/debug_message_logging_disabled.wat +++ b/frame/contracts/fixtures/debug_message_logging_disabled.wat @@ -20,7 +20,7 @@ (i32.const 0) ;; Pointer to the text buffer (i32.const 12) ;; The size of the buffer ) - (i32.const 9) ;; LoggingDisabled return code + (i32.const 0) ;; Success return code ) ) diff --git a/frame/contracts/src/benchmarking/mod.rs b/frame/contracts/src/benchmarking/mod.rs index 708d92766..e834508a0 100644 --- a/frame/contracts/src/benchmarking/mod.rs +++ b/frame/contracts/src/benchmarking/mod.rs @@ -910,13 +910,12 @@ benchmarks! { let origin = RawOrigin::Signed(instance.caller.clone()); }: call(origin, instance.addr, 0u32.into(), Weight::MAX, None, vec![]) - // The size of the supplied message does not influence the weight because as it is never - // processed during on-chain execution: It is only ever read during debugging which happens - // when the contract is called as RPC where weights do not matter. + // Benchmark debug_message call with zero input data. + // Whereas this function is used in RPC mode only, it still should be secured + // against an excessive use. #[pov_mode = Ignored] seal_debug_message { let r in 0 .. API_BENCHMARK_BATCHES; - let max_bytes = code::max_pages::() * 64 * 1024; let code = WasmModule::::from(ModuleDefinition { memory: Some(ImportedMemory { min_pages: 1, max_pages: 1 }), imported_functions: vec![ImportedFunction { @@ -927,15 +926,75 @@ benchmarks! { }], call_body: Some(body::repeated(r * API_BENCHMARK_BATCH_SIZE, &[ Instruction::I32Const(0), // value_ptr - Instruction::I32Const(max_bytes as i32), // value_len + Instruction::I32Const(0), // value_len Instruction::Call(0), Instruction::Drop, ])), .. Default::default() }); let instance = Contract::::new(code, vec![])?; - let origin = RawOrigin::Signed(instance.caller.clone()); - }: call(origin, instance.addr, 0u32.into(), Weight::MAX, None, vec![]) + }: { + >::bare_call( + instance.caller, + instance.account_id, + 0u32.into(), + Weight::MAX, + None, + vec![], + true, + Determinism::Deterministic, + ) + .result?; + } + + seal_debug_message_per_kb { + // Vary size of input in kilobytes up to maximum allowed contract memory + // or maximum allowed debug buffer size, whichever is less. + let i in 0 .. (T::Schedule::get().limits.memory_pages * 64).min(T::MaxDebugBufferLen::get() / 1024); + // We benchmark versus messages containing printable ASCII codes. + // About 1Kb goes to the instrumented contract code instructions, + // whereas all the space left we use for the initialization of the debug messages data. + let message = (0 .. T::MaxCodeLen::get() - 1024).zip((32..127).cycle()).map(|i| i.1).collect::>(); + let code = WasmModule::::from(ModuleDefinition { + memory: Some(ImportedMemory { + min_pages: T::Schedule::get().limits.memory_pages, + max_pages: T::Schedule::get().limits.memory_pages, + }), + imported_functions: vec![ImportedFunction { + module: "seal0", + name: "seal_debug_message", + params: vec![ValueType::I32, ValueType::I32], + return_type: Some(ValueType::I32), + }], + data_segments: vec![ + DataSegment { + offset: 0, + value: message, + }, + ], + call_body: Some(body::plain(vec![ + Instruction::I32Const(0), // value_ptr + Instruction::I32Const((i * 1024) as i32), // value_len increments by i Kb + Instruction::Call(0), + Instruction::Drop, + Instruction::End, + ])), + ..Default::default() + }); + let instance = Contract::::new(code, vec![])?; + }: { + >::bare_call( + instance.caller, + instance.account_id, + 0u32.into(), + Weight::MAX, + None, + vec![], + true, + Determinism::Deterministic, + ) + .result?; + } // Only the overhead of calling the function itself with minimal arguments. // The contract is a bit more complex because it needs to use different keys in order diff --git a/frame/contracts/src/exec.rs b/frame/contracts/src/exec.rs index 3eb59354d..9b033a76c 100644 --- a/frame/contracts/src/exec.rs +++ b/frame/contracts/src/exec.rs @@ -1336,31 +1336,16 @@ where fn append_debug_buffer(&mut self, msg: &str) -> bool { if let Some(buffer) = &mut self.debug_message { - let err_msg = scale_info::prelude::format!( - "Debug message too big (size={}) for debug buffer (bound={})", - msg.len(), - DebugBufferVec::::bound(), - ); - - let mut msg = if msg.len() > DebugBufferVec::::bound() { - err_msg.bytes() - } else { - msg.bytes() - }; - - let num_drain = { - let capacity = DebugBufferVec::::bound().checked_sub(buffer.len()).expect( - " - `buffer` is of type `DebugBufferVec`, - `DebugBufferVec` is a `BoundedVec`, - `BoundedVec::len()` <= `BoundedVec::bound()`; - qed - ", - ); - msg.len().saturating_sub(capacity).min(buffer.len()) - }; - buffer.drain(0..num_drain); - buffer.try_extend(&mut msg).ok(); + buffer + .try_extend(&mut msg.bytes()) + .map_err(|_| { + log::debug!( + target: "runtime::contracts", + "Debug buffer (of {} bytes) exhausted!", + DebugBufferVec::::bound(), + ) + }) + .ok(); true } else { false @@ -2603,9 +2588,11 @@ mod tests { exec_success() }); - // Pre-fill the buffer up to its limit - let mut debug_buffer = - DebugBufferVec::::try_from(vec![0u8; DebugBufferVec::::bound()]).unwrap(); + // Pre-fill the buffer almost up to its limit, leaving not enough space to the message + let debug_buf_before = + DebugBufferVec::::try_from(vec![0u8; DebugBufferVec::::bound() - 5]) + .unwrap(); + let mut debug_buf_after = debug_buf_before.clone(); ExtBuilder::default().build().execute_with(|| { let schedule: Schedule = ::Schedule::get(); @@ -2622,15 +2609,11 @@ mod tests { &schedule, 0, vec![], - Some(&mut debug_buffer), + Some(&mut debug_buf_after), Determinism::Deterministic, ) .unwrap(); - assert_eq!( - &String::from_utf8(debug_buffer[DebugBufferVec::::bound() - 17..].to_vec()) - .unwrap(), - "overflowing bytes" - ); + assert_eq!(debug_buf_before, debug_buf_after); }); } diff --git a/frame/contracts/src/lib.rs b/frame/contracts/src/lib.rs index c067b2644..e131a2685 100644 --- a/frame/contracts/src/lib.rs +++ b/frame/contracts/src/lib.rs @@ -912,8 +912,6 @@ pub mod pallet { /// /// This can be triggered by a call to `seal_terminate`. TerminatedInConstructor, - /// The debug message specified to `seal_debug_message` does contain invalid UTF-8. - DebugMessageInvalidUTF8, /// A call tried to invoke a contract that is flagged as non-reentrant. ReentranceDenied, /// Origin doesn't have enough balance to pay the required storage deposits. diff --git a/frame/contracts/src/schedule.rs b/frame/contracts/src/schedule.rs index 7e09d55b4..5e4798df1 100644 --- a/frame/contracts/src/schedule.rs +++ b/frame/contracts/src/schedule.rs @@ -323,6 +323,9 @@ pub struct HostFnWeights { /// Weight of calling `seal_debug_message`. pub debug_message: Weight, + /// Weight of calling `seal_debug_message` per byte of the message. + pub debug_message_per_byte: Weight, + /// Weight of calling `seal_set_storage`. pub set_storage: Weight, @@ -644,6 +647,7 @@ impl Default for HostFnWeights { 1 )), debug_message: to_weight!(cost_batched!(seal_debug_message)), + debug_message_per_byte: to_weight!(cost_byte!(seal_debug_message_per_kb)), set_storage: to_weight!(cost_batched!(seal_set_storage), 1024u64), set_code_hash: to_weight!(cost_batched!(seal_set_code_hash)), set_storage_per_new_byte: to_weight!(cost_byte_batched!(seal_set_storage_per_new_kb)), diff --git a/frame/contracts/src/tests.rs b/frame/contracts/src/tests.rs index 973fdefe1..958cbf772 100644 --- a/frame/contracts/src/tests.rs +++ b/frame/contracts/src/tests.rs @@ -2714,7 +2714,8 @@ fn debug_message_invalid_utf8() { true, Determinism::Deterministic, ); - assert_err!(result.result, >::DebugMessageInvalidUTF8); + assert_ok!(result.result); + assert!(result.debug_message.is_empty()); }); } diff --git a/frame/contracts/src/wasm/mod.rs b/frame/contracts/src/wasm/mod.rs index 9bf36b47f..3ca732618 100644 --- a/frame/contracts/src/wasm/mod.rs +++ b/frame/contracts/src/wasm/mod.rs @@ -2363,13 +2363,8 @@ mod tests { "#; let mut ext = MockExt::default(); let result = execute(CODE_DEBUG_MESSAGE_FAIL, vec![], &mut ext); - assert_eq!( - result, - Err(ExecError { - error: Error::::DebugMessageInvalidUTF8.into(), - origin: ErrorOrigin::Caller, - }) - ); + assert_ok!(result); + assert!(ext.debug_buffer.is_empty()); } const CODE_CALL_RUNTIME: &str = r#" diff --git a/frame/contracts/src/wasm/runtime.rs b/frame/contracts/src/wasm/runtime.rs index 9cdf0bf09..ef0c35df7 100644 --- a/frame/contracts/src/wasm/runtime.rs +++ b/frame/contracts/src/wasm/runtime.rs @@ -21,7 +21,7 @@ use crate::{ exec::{ExecError, ExecResult, Ext, FixSizedKey, TopicOf, VarSizedKey}, gas::{ChargedAmount, Token}, schedule::HostFnWeights, - BalanceOf, CodeHash, Config, Error, SENTINEL, + BalanceOf, CodeHash, Config, DebugBufferVec, Error, SENTINEL, }; use bitflags::bitflags; @@ -114,9 +114,6 @@ pub enum ReturnCode { CodeNotFound = 7, /// The contract that was called is no contract (a plain account). NotCallable = 8, - /// The call to `seal_debug_message` had no effect because debug message - /// recording was disabled. - LoggingDisabled = 9, /// The call dispatched by `seal_call_runtime` was executed but returned an error. CallRuntimeReturnedError = 10, /// ECDSA pubkey recovery failed (most probably wrong recovery id or signature), or @@ -229,8 +226,8 @@ pub enum RuntimeCosts { Random, /// Weight of calling `seal_deposit_event` with the given number of topics and event size. DepositEvent { num_topic: u32, len: u32 }, - /// Weight of calling `seal_debug_message`. - DebugMessage, + /// Weight of calling `seal_debug_message` per byte of passed message. + DebugMessage(u32), /// Weight of calling `seal_set_storage` for the given storage item sizes. SetStorage { old_bytes: u32, new_bytes: u32 }, /// Weight of calling `seal_clear_storage` per cleared byte. @@ -309,7 +306,9 @@ impl RuntimeCosts { .deposit_event .saturating_add(s.deposit_event_per_topic.saturating_mul(num_topic.into())) .saturating_add(s.deposit_event_per_byte.saturating_mul(len.into())), - DebugMessage => s.debug_message, + DebugMessage(len) => s + .debug_message + .saturating_add(s.deposit_event_per_byte.saturating_mul(len.into())), SetStorage { new_bytes, old_bytes } => s .set_storage .saturating_add(s.set_storage_per_new_byte.saturating_mul(new_bytes.into())) @@ -2054,7 +2053,7 @@ pub mod env { _delta_ptr: u32, _delta_count: u32, ) -> Result<(), TrapReason> { - ctx.charge_gas(RuntimeCosts::DebugMessage)?; + ctx.charge_gas(RuntimeCosts::DebugMessage(0))?; Ok(()) } @@ -2076,7 +2075,7 @@ pub mod env { _delta_ptr: u32, _delta_count: u32, ) -> Result<(), TrapReason> { - ctx.charge_gas(RuntimeCosts::DebugMessage)?; + ctx.charge_gas(RuntimeCosts::DebugMessage(0))?; Ok(()) } @@ -2094,7 +2093,7 @@ pub mod env { _value_ptr: u32, _value_len: u32, ) -> Result<(), TrapReason> { - ctx.charge_gas(RuntimeCosts::DebugMessage)?; + ctx.charge_gas(RuntimeCosts::DebugMessage(0))?; Ok(()) } @@ -2108,7 +2107,7 @@ pub mod env { #[prefixed_alias] #[deprecated] fn set_rent_allowance(ctx: _, _memory: _, _value_ptr: u32) -> Result<(), TrapReason> { - ctx.charge_gas(RuntimeCosts::DebugMessage)?; + ctx.charge_gas(RuntimeCosts::DebugMessage(0))?; Ok(()) } @@ -2362,7 +2361,7 @@ pub mod env { /// Emit a custom debug message. /// /// No newlines are added to the supplied message. - /// Specifying invalid UTF-8 triggers a trap. + /// Specifying invalid UTF-8 just drops the message with no trap. /// /// This is a no-op if debug message recording is disabled which is always the case /// when the code is executing on-chain. The message is interpreted as UTF-8 and @@ -2383,15 +2382,15 @@ pub mod env { str_ptr: u32, str_len: u32, ) -> Result { - ctx.charge_gas(RuntimeCosts::DebugMessage)?; + let str_len = str_len.min(DebugBufferVec::::bound() as u32); + ctx.charge_gas(RuntimeCosts::DebugMessage(str_len))?; if ctx.ext.append_debug_buffer("") { let data = ctx.read_sandbox_memory(memory, str_ptr, str_len)?; - let msg = - core::str::from_utf8(&data).map_err(|_| >::DebugMessageInvalidUTF8)?; - ctx.ext.append_debug_buffer(msg); - return Ok(ReturnCode::Success) + if let Some(msg) = core::str::from_utf8(&data).ok() { + ctx.ext.append_debug_buffer(msg); + } } - Ok(ReturnCode::LoggingDisabled) + Ok(ReturnCode::Success) } /// Call some dispatchable of the runtime. diff --git a/frame/contracts/src/weights.rs b/frame/contracts/src/weights.rs index d9ec1b10b..50aa30e89 100644 --- a/frame/contracts/src/weights.rs +++ b/frame/contracts/src/weights.rs @@ -18,25 +18,26 @@ //! Autogenerated weights for pallet_contracts //! //! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev -//! DATE: 2023-01-24, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! DATE: 2023-02-14, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` //! WORST CASE MAP SIZE: `1000000` -//! HOSTNAME: `bm2`, CPU: `Intel(R) Core(TM) i7-7700K CPU @ 4.20GHz` +//! HOSTNAME: `bm3`, CPU: `Intel(R) Core(TM) i7-7700K CPU @ 4.20GHz` //! EXECUTION: Some(Wasm), WASM-EXECUTION: Compiled, CHAIN: Some("dev"), DB CACHE: 1024 // Executed Command: -// ./target/production/substrate +// /home/benchbot/cargo_target_dir/production/substrate // benchmark // pallet -// --chain=dev // --steps=50 // --repeat=20 -// --pallet=pallet_contracts // --extrinsic=* // --execution=wasm // --wasm-execution=compiled // --heap-pages=4096 -// --output=./frame/contracts/src/weights.rs +// --json-file=/var/lib/gitlab-runner/builds/zyw4fam_/0/parity/mirrors/substrate/.git/.artifacts/bench.json +// --pallet=pallet_contracts +// --chain=dev // --header=./HEADER-APACHE2 +// --output=./frame/contracts/src/weights.rs // --template=./.maintain/frame-weight-template.hbs #![cfg_attr(rustfmt, rustfmt_skip)] @@ -82,6 +83,7 @@ pub trait WeightInfo { fn seal_deposit_event(r: u32, ) -> Weight; fn seal_deposit_event_per_topic_and_kb(t: u32, n: u32, ) -> Weight; fn seal_debug_message(r: u32, ) -> Weight; + fn seal_debug_message_per_kb(i: u32, ) -> Weight; fn seal_set_storage(r: u32, ) -> Weight; fn seal_set_storage_per_new_kb(n: u32, ) -> Weight; fn seal_set_storage_per_old_kb(n: u32, ) -> Weight; @@ -176,8 +178,9 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `109` // Estimated: `0` - // Minimum execution time: 2_305 nanoseconds. - Weight::from_ref_time(2_560_000) + // Minimum execution time: 2_564 nanoseconds. + Weight::from_ref_time(2_722_000) + .saturating_add(Weight::from_proof_size(0)) .saturating_add(T::DbWeight::get().reads(1_u64)) } /// Storage: Skipped Metadata (r:0 w:0) @@ -187,10 +190,11 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `481 + k * (69 ±0)` // Estimated: `0` - // Minimum execution time: 9_311 nanoseconds. - Weight::from_ref_time(5_419_288) - // Standard Error: 562 - .saturating_add(Weight::from_ref_time(911_962).saturating_mul(k.into())) + // Minimum execution time: 10_292 nanoseconds. + Weight::from_ref_time(7_474_496) + .saturating_add(Weight::from_proof_size(0)) + // Standard Error: 901 + .saturating_add(Weight::from_ref_time(956_864).saturating_mul(k.into())) .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) .saturating_add(T::DbWeight::get().writes((1_u64).saturating_mul(k.into()))) @@ -202,10 +206,11 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `281 + q * (33 ±0)` // Estimated: `0` - // Minimum execution time: 2_288 nanoseconds. - Weight::from_ref_time(9_442_437) - // Standard Error: 2_720 - .saturating_add(Weight::from_ref_time(1_076_950).saturating_mul(q.into())) + // Minimum execution time: 2_620 nanoseconds. + Weight::from_ref_time(10_288_873) + .saturating_add(Weight::from_proof_size(0)) + // Standard Error: 3_073 + .saturating_add(Weight::from_ref_time(1_148_167).saturating_mul(q.into())) .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -218,10 +223,11 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `270 + c * (1 ±0)` // Estimated: `0` - // Minimum execution time: 27_539 nanoseconds. - Weight::from_ref_time(23_554_889) - // Standard Error: 56 - .saturating_add(Weight::from_ref_time(46_766).saturating_mul(c.into())) + // Minimum execution time: 28_004 nanoseconds. + Weight::from_ref_time(26_706_943) + .saturating_add(Weight::from_proof_size(0)) + // Standard Error: 67 + .saturating_add(Weight::from_ref_time(51_603).saturating_mul(c.into())) .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -240,10 +246,11 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `771` // Estimated: `0` - // Minimum execution time: 297_710 nanoseconds. - Weight::from_ref_time(307_327_529) - // Standard Error: 18 - .saturating_add(Weight::from_ref_time(29_849).saturating_mul(c.into())) + // Minimum execution time: 295_799 nanoseconds. + Weight::from_ref_time(308_660_753) + .saturating_add(Weight::from_proof_size(0)) + // Standard Error: 20 + .saturating_add(Weight::from_ref_time(32_458).saturating_mul(c.into())) .saturating_add(T::DbWeight::get().reads(6_u64)) .saturating_add(T::DbWeight::get().writes(4_u64)) } @@ -270,14 +277,15 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `257` // Estimated: `0` - // Minimum execution time: 3_586_223 nanoseconds. - Weight::from_ref_time(561_614_281) - // Standard Error: 274 - .saturating_add(Weight::from_ref_time(87_557).saturating_mul(c.into())) + // Minimum execution time: 3_754_887 nanoseconds. + Weight::from_ref_time(657_695_827) + .saturating_add(Weight::from_proof_size(0)) + // Standard Error: 283 + .saturating_add(Weight::from_ref_time(94_808).saturating_mul(c.into())) // Standard Error: 16 - .saturating_add(Weight::from_ref_time(1_307).saturating_mul(i.into())) + .saturating_add(Weight::from_ref_time(1_357).saturating_mul(i.into())) // Standard Error: 16 - .saturating_add(Weight::from_ref_time(1_721).saturating_mul(s.into())) + .saturating_add(Weight::from_ref_time(1_756).saturating_mul(s.into())) .saturating_add(T::DbWeight::get().reads(8_u64)) .saturating_add(T::DbWeight::get().writes(9_u64)) } @@ -301,12 +309,13 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `533` // Estimated: `0` - // Minimum execution time: 1_885_437 nanoseconds. - Weight::from_ref_time(199_943_867) + // Minimum execution time: 1_961_131 nanoseconds. + Weight::from_ref_time(208_539_564) + .saturating_add(Weight::from_proof_size(0)) // Standard Error: 8 - .saturating_add(Weight::from_ref_time(1_611).saturating_mul(i.into())) + .saturating_add(Weight::from_ref_time(1_688).saturating_mul(i.into())) // Standard Error: 8 - .saturating_add(Weight::from_ref_time(1_737).saturating_mul(s.into())) + .saturating_add(Weight::from_ref_time(1_802).saturating_mul(s.into())) .saturating_add(T::DbWeight::get().reads(8_u64)) .saturating_add(T::DbWeight::get().writes(7_u64)) } @@ -324,8 +333,9 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `823` // Estimated: `0` - // Minimum execution time: 150_604 nanoseconds. - Weight::from_ref_time(151_777_000) + // Minimum execution time: 148_138 nanoseconds. + Weight::from_ref_time(148_862_000) + .saturating_add(Weight::from_proof_size(0)) .saturating_add(T::DbWeight::get().reads(6_u64)) .saturating_add(T::DbWeight::get().writes(4_u64)) } @@ -342,10 +352,11 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `109` // Estimated: `0` - // Minimum execution time: 295_505 nanoseconds. - Weight::from_ref_time(305_609_098) - // Standard Error: 58 - .saturating_add(Weight::from_ref_time(88_676).saturating_mul(c.into())) + // Minimum execution time: 293_444 nanoseconds. + Weight::from_ref_time(293_242_988) + .saturating_add(Weight::from_proof_size(0)) + // Standard Error: 156 + .saturating_add(Weight::from_ref_time(97_476).saturating_mul(c.into())) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().writes(4_u64)) } @@ -361,8 +372,9 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `287` // Estimated: `0` - // Minimum execution time: 25_949 nanoseconds. - Weight::from_ref_time(26_316_000) + // Minimum execution time: 26_802 nanoseconds. + Weight::from_ref_time(27_121_000) + .saturating_add(Weight::from_proof_size(0)) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().writes(4_u64)) } @@ -376,8 +388,9 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `634` // Estimated: `0` - // Minimum execution time: 29_005 nanoseconds. - Weight::from_ref_time(29_370_000) + // Minimum execution time: 31_079 nanoseconds. + Weight::from_ref_time(31_541_000) + .saturating_add(Weight::from_proof_size(0)) .saturating_add(T::DbWeight::get().reads(6_u64)) .saturating_add(T::DbWeight::get().writes(6_u64)) } @@ -396,10 +409,11 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `845 + r * (480 ±0)` // Estimated: `0` - // Minimum execution time: 281_880 nanoseconds. - Weight::from_ref_time(289_637_700) - // Standard Error: 22_662 - .saturating_add(Weight::from_ref_time(16_866_274).saturating_mul(r.into())) + // Minimum execution time: 284_877 nanoseconds. + Weight::from_ref_time(290_043_606) + .saturating_add(Weight::from_proof_size(0)) + // Standard Error: 22_094 + .saturating_add(Weight::from_ref_time(18_094_219).saturating_mul(r.into())) .saturating_add(T::DbWeight::get().reads(6_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) } @@ -418,10 +432,11 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `882 + r * (19218 ±0)` // Estimated: `0` - // Minimum execution time: 285_590 nanoseconds. - Weight::from_ref_time(231_277_523) - // Standard Error: 425_084 - .saturating_add(Weight::from_ref_time(192_985_377).saturating_mul(r.into())) + // Minimum execution time: 283_956 nanoseconds. + Weight::from_ref_time(129_690_397) + .saturating_add(Weight::from_proof_size(0)) + // Standard Error: 531_270 + .saturating_add(Weight::from_ref_time(264_082_418).saturating_mul(r.into())) .saturating_add(T::DbWeight::get().reads(6_u64)) .saturating_add(T::DbWeight::get().reads((80_u64).saturating_mul(r.into()))) .saturating_add(T::DbWeight::get().writes(3_u64)) @@ -441,10 +456,11 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `889 + r * (19539 ±0)` // Estimated: `0` - // Minimum execution time: 282_966 nanoseconds. - Weight::from_ref_time(236_127_328) - // Standard Error: 405_193 - .saturating_add(Weight::from_ref_time(235_541_377).saturating_mul(r.into())) + // Minimum execution time: 287_077 nanoseconds. + Weight::from_ref_time(148_155_166) + .saturating_add(Weight::from_proof_size(0)) + // Standard Error: 466_496 + .saturating_add(Weight::from_ref_time(306_625_930).saturating_mul(r.into())) .saturating_add(T::DbWeight::get().reads(6_u64)) .saturating_add(T::DbWeight::get().reads((80_u64).saturating_mul(r.into()))) .saturating_add(T::DbWeight::get().writes(3_u64)) @@ -464,10 +480,11 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `852 + r * (480 ±0)` // Estimated: `0` - // Minimum execution time: 286_021 nanoseconds. - Weight::from_ref_time(290_200_599) - // Standard Error: 19_224 - .saturating_add(Weight::from_ref_time(20_692_099).saturating_mul(r.into())) + // Minimum execution time: 286_459 nanoseconds. + Weight::from_ref_time(292_826_594) + .saturating_add(Weight::from_proof_size(0)) + // Standard Error: 27_808 + .saturating_add(Weight::from_ref_time(22_401_931).saturating_mul(r.into())) .saturating_add(T::DbWeight::get().reads(6_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) } @@ -486,10 +503,11 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `842 + r * (240 ±0)` // Estimated: `0` - // Minimum execution time: 284_175 nanoseconds. - Weight::from_ref_time(286_665_694) - // Standard Error: 14_104 - .saturating_add(Weight::from_ref_time(11_196_944).saturating_mul(r.into())) + // Minimum execution time: 284_330 nanoseconds. + Weight::from_ref_time(289_069_701) + .saturating_add(Weight::from_proof_size(0)) + // Standard Error: 13_666 + .saturating_add(Weight::from_ref_time(11_157_859).saturating_mul(r.into())) .saturating_add(T::DbWeight::get().reads(6_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) } @@ -508,10 +526,11 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `846 + r * (480 ±0)` // Estimated: `0` - // Minimum execution time: 285_315 nanoseconds. - Weight::from_ref_time(289_734_189) - // Standard Error: 15_980 - .saturating_add(Weight::from_ref_time(16_940_657).saturating_mul(r.into())) + // Minimum execution time: 284_506 nanoseconds. + Weight::from_ref_time(290_155_141) + .saturating_add(Weight::from_proof_size(0)) + // Standard Error: 22_944 + .saturating_add(Weight::from_ref_time(17_992_627).saturating_mul(r.into())) .saturating_add(T::DbWeight::get().reads(6_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) } @@ -530,10 +549,11 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `847 + r * (480 ±0)` // Estimated: `0` - // Minimum execution time: 285_708 nanoseconds. - Weight::from_ref_time(289_872_393) - // Standard Error: 16_551 - .saturating_add(Weight::from_ref_time(16_672_944).saturating_mul(r.into())) + // Minimum execution time: 284_177 nanoseconds. + Weight::from_ref_time(289_314_787) + .saturating_add(Weight::from_proof_size(0)) + // Standard Error: 24_552 + .saturating_add(Weight::from_ref_time(17_754_919).saturating_mul(r.into())) .saturating_add(T::DbWeight::get().reads(6_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) } @@ -552,10 +572,11 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `1017 + r * (480 ±0)` // Estimated: `0` - // Minimum execution time: 285_698 nanoseconds. - Weight::from_ref_time(295_636_093) - // Standard Error: 97_582 - .saturating_add(Weight::from_ref_time(92_891_252).saturating_mul(r.into())) + // Minimum execution time: 284_096 nanoseconds. + Weight::from_ref_time(293_655_130) + .saturating_add(Weight::from_proof_size(0)) + // Standard Error: 56_379 + .saturating_add(Weight::from_ref_time(95_688_575).saturating_mul(r.into())) .saturating_add(T::DbWeight::get().reads(7_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) } @@ -574,10 +595,11 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `856 + r * (480 ±0)` // Estimated: `0` - // Minimum execution time: 282_057 nanoseconds. - Weight::from_ref_time(289_304_621) - // Standard Error: 17_818 - .saturating_add(Weight::from_ref_time(16_725_632).saturating_mul(r.into())) + // Minimum execution time: 285_967 nanoseconds. + Weight::from_ref_time(288_043_137) + .saturating_add(Weight::from_proof_size(0)) + // Standard Error: 83_838 + .saturating_add(Weight::from_ref_time(18_256_709).saturating_mul(r.into())) .saturating_add(T::DbWeight::get().reads(6_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) } @@ -596,10 +618,11 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `854 + r * (480 ±0)` // Estimated: `0` - // Minimum execution time: 282_478 nanoseconds. - Weight::from_ref_time(289_682_366) - // Standard Error: 20_379 - .saturating_add(Weight::from_ref_time(16_517_079).saturating_mul(r.into())) + // Minimum execution time: 284_060 nanoseconds. + Weight::from_ref_time(290_577_678) + .saturating_add(Weight::from_proof_size(0)) + // Standard Error: 26_131 + .saturating_add(Weight::from_ref_time(17_560_562).saturating_mul(r.into())) .saturating_add(T::DbWeight::get().reads(6_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) } @@ -618,10 +641,11 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `851 + r * (480 ±0)` // Estimated: `0` - // Minimum execution time: 283_826 nanoseconds. - Weight::from_ref_time(289_935_300) - // Standard Error: 15_180 - .saturating_add(Weight::from_ref_time(16_268_515).saturating_mul(r.into())) + // Minimum execution time: 286_068 nanoseconds. + Weight::from_ref_time(290_026_137) + .saturating_add(Weight::from_proof_size(0)) + // Standard Error: 34_787 + .saturating_add(Weight::from_ref_time(17_597_404).saturating_mul(r.into())) .saturating_add(T::DbWeight::get().reads(6_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) } @@ -640,10 +664,11 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `842 + r * (480 ±0)` // Estimated: `0` - // Minimum execution time: 285_455 nanoseconds. - Weight::from_ref_time(289_682_526) - // Standard Error: 18_667 - .saturating_add(Weight::from_ref_time(16_502_025).saturating_mul(r.into())) + // Minimum execution time: 285_082 nanoseconds. + Weight::from_ref_time(290_487_103) + .saturating_add(Weight::from_proof_size(0)) + // Standard Error: 22_743 + .saturating_add(Weight::from_ref_time(17_506_771).saturating_mul(r.into())) .saturating_add(T::DbWeight::get().reads(6_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) } @@ -664,10 +689,11 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `919 + r * (800 ±0)` // Estimated: `0` - // Minimum execution time: 286_106 nanoseconds. - Weight::from_ref_time(294_493_680) - // Standard Error: 76_469 - .saturating_add(Weight::from_ref_time(87_055_837).saturating_mul(r.into())) + // Minimum execution time: 285_309 nanoseconds. + Weight::from_ref_time(296_735_365) + .saturating_add(Weight::from_proof_size(0)) + // Standard Error: 49_300 + .saturating_add(Weight::from_ref_time(87_678_979).saturating_mul(r.into())) .saturating_add(T::DbWeight::get().reads(7_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) } @@ -686,10 +712,11 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `809 + r * (320 ±0)` // Estimated: `0` - // Minimum execution time: 137_877 nanoseconds. - Weight::from_ref_time(141_863_027) - // Standard Error: 10_200 - .saturating_add(Weight::from_ref_time(7_925_232).saturating_mul(r.into())) + // Minimum execution time: 136_110 nanoseconds. + Weight::from_ref_time(139_800_659) + .saturating_add(Weight::from_proof_size(0)) + // Standard Error: 9_674 + .saturating_add(Weight::from_ref_time(8_302_834).saturating_mul(r.into())) .saturating_add(T::DbWeight::get().reads(6_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) } @@ -708,10 +735,11 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `844 + r * (480 ±0)` // Estimated: `0` - // Minimum execution time: 282_034 nanoseconds. - Weight::from_ref_time(289_388_799) - // Standard Error: 21_999 - .saturating_add(Weight::from_ref_time(15_039_420).saturating_mul(r.into())) + // Minimum execution time: 285_238 nanoseconds. + Weight::from_ref_time(289_723_839) + .saturating_add(Weight::from_proof_size(0)) + // Standard Error: 19_531 + .saturating_add(Weight::from_ref_time(15_590_085).saturating_mul(r.into())) .saturating_add(T::DbWeight::get().reads(6_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) } @@ -730,10 +758,11 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `1324` // Estimated: `0` - // Minimum execution time: 303_229 nanoseconds. - Weight::from_ref_time(321_863_704) - // Standard Error: 2_754 - .saturating_add(Weight::from_ref_time(9_545_103).saturating_mul(n.into())) + // Minimum execution time: 303_189 nanoseconds. + Weight::from_ref_time(323_374_503) + .saturating_add(Weight::from_proof_size(0)) + // Standard Error: 2_060 + .saturating_add(Weight::from_ref_time(9_799_357).saturating_mul(n.into())) .saturating_add(T::DbWeight::get().reads(6_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) } @@ -752,10 +781,11 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `832 + r * (45 ±0)` // Estimated: `0` - // Minimum execution time: 278_824 nanoseconds. - Weight::from_ref_time(285_019_861) - // Standard Error: 101_646 - .saturating_add(Weight::from_ref_time(1_757_938).saturating_mul(r.into())) + // Minimum execution time: 280_736 nanoseconds. + Weight::from_ref_time(285_027_920) + .saturating_add(Weight::from_proof_size(0)) + // Standard Error: 222_526 + .saturating_add(Weight::from_ref_time(1_814_579).saturating_mul(r.into())) .saturating_add(T::DbWeight::get().reads(6_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) } @@ -774,10 +804,11 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `842` // Estimated: `0` - // Minimum execution time: 286_316 nanoseconds. - Weight::from_ref_time(287_206_936) - // Standard Error: 589 - .saturating_add(Weight::from_ref_time(186_684).saturating_mul(n.into())) + // Minimum execution time: 285_559 nanoseconds. + Weight::from_ref_time(290_522_234) + .saturating_add(Weight::from_proof_size(0)) + // Standard Error: 2_578 + .saturating_add(Weight::from_ref_time(195_134).saturating_mul(n.into())) .saturating_add(T::DbWeight::get().reads(6_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) } @@ -800,10 +831,11 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `874 + r * (280 ±0)` // Estimated: `0` - // Minimum execution time: 282_428 nanoseconds. - Weight::from_ref_time(287_101_148) - // Standard Error: 145_605 - .saturating_add(Weight::from_ref_time(58_079_551).saturating_mul(r.into())) + // Minimum execution time: 282_070 nanoseconds. + Weight::from_ref_time(286_466_489) + .saturating_add(Weight::from_proof_size(0)) + // Standard Error: 238_285 + .saturating_add(Weight::from_ref_time(58_542_610).saturating_mul(r.into())) .saturating_add(T::DbWeight::get().reads(6_u64)) .saturating_add(T::DbWeight::get().reads((5_u64).saturating_mul(r.into()))) .saturating_add(T::DbWeight::get().writes(3_u64)) @@ -826,10 +858,11 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `889 + r * (800 ±0)` // Estimated: `0` - // Minimum execution time: 281_516 nanoseconds. - Weight::from_ref_time(292_759_183) - // Standard Error: 152_698 - .saturating_add(Weight::from_ref_time(112_729_555).saturating_mul(r.into())) + // Minimum execution time: 284_147 nanoseconds. + Weight::from_ref_time(295_373_881) + .saturating_add(Weight::from_proof_size(0)) + // Standard Error: 64_682 + .saturating_add(Weight::from_ref_time(114_198_680).saturating_mul(r.into())) .saturating_add(T::DbWeight::get().reads(7_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) } @@ -848,10 +881,11 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `842 + r * (800 ±0)` // Estimated: `0` - // Minimum execution time: 280_100 nanoseconds. - Weight::from_ref_time(299_862_082) - // Standard Error: 95_658 - .saturating_add(Weight::from_ref_time(234_211_246).saturating_mul(r.into())) + // Minimum execution time: 282_681 nanoseconds. + Weight::from_ref_time(290_974_649) + .saturating_add(Weight::from_proof_size(0)) + // Standard Error: 77_850 + .saturating_add(Weight::from_ref_time(233_273_622).saturating_mul(r.into())) .saturating_add(T::DbWeight::get().reads(6_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) } @@ -871,12 +905,13 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `1778 + t * (2608 ±0) + n * (8 ±0)` // Estimated: `0` - // Minimum execution time: 1_197_721 nanoseconds. - Weight::from_ref_time(508_692_255) - // Standard Error: 538_596 - .saturating_add(Weight::from_ref_time(174_792_656).saturating_mul(t.into())) - // Standard Error: 147_924 - .saturating_add(Weight::from_ref_time(67_443_118).saturating_mul(n.into())) + // Minimum execution time: 1_226_868 nanoseconds. + Weight::from_ref_time(517_031_747) + .saturating_add(Weight::from_proof_size(0)) + // Standard Error: 342_786 + .saturating_add(Weight::from_ref_time(183_829_556).saturating_mul(t.into())) + // Standard Error: 94_145 + .saturating_add(Weight::from_ref_time(70_675_099).saturating_mul(n.into())) .saturating_add(T::DbWeight::get().reads(6_u64)) .saturating_add(T::DbWeight::get().reads((80_u64).saturating_mul(t.into()))) .saturating_add(T::DbWeight::get().writes(3_u64)) @@ -895,12 +930,36 @@ impl WeightInfo for SubstrateWeight { /// The range of component `r` is `[0, 20]`. fn seal_debug_message(r: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `841 + r * (800 ±0)` + // Measured: `841 + r * (560 ±0)` // Estimated: `0` - // Minimum execution time: 149_687 nanoseconds. - Weight::from_ref_time(153_589_818) - // Standard Error: 13_361 - .saturating_add(Weight::from_ref_time(13_379_131).saturating_mul(r.into())) + // Minimum execution time: 143_026 nanoseconds. + Weight::from_ref_time(147_158_038) + .saturating_add(Weight::from_proof_size(0)) + // Standard Error: 16_662 + .saturating_add(Weight::from_ref_time(14_990_989).saturating_mul(r.into())) + .saturating_add(T::DbWeight::get().reads(6_u64)) + .saturating_add(T::DbWeight::get().writes(3_u64)) + } + /// Storage: System Account (r:1 w:0) + /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) + /// Storage: Contracts ContractInfoOf (r:1 w:1) + /// Proof: Contracts ContractInfoOf (max_values: None, max_size: Some(258), added: 2733, mode: MaxEncodedLen) + /// Storage: Contracts CodeStorage (r:1 w:0) + /// Proof: Contracts CodeStorage (max_values: None, max_size: Some(126001), added: 128476, mode: MaxEncodedLen) + /// Storage: Timestamp Now (r:1 w:0) + /// Proof: Timestamp Now (max_values: Some(1), max_size: Some(8), added: 503, mode: MaxEncodedLen) + /// Storage: System EventTopics (r:2 w:2) + /// Proof Skipped: System EventTopics (max_values: None, max_size: None, mode: Measured) + /// The range of component `i` is `[0, 1024]`. + fn seal_debug_message_per_kb(i: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `125792` + // Estimated: `265059` + // Minimum execution time: 412_112 nanoseconds. + Weight::from_ref_time(415_624_011) + .saturating_add(Weight::from_proof_size(265059)) + // Standard Error: 1_190 + .saturating_add(Weight::from_ref_time(797_964).saturating_mul(i.into())) .saturating_add(T::DbWeight::get().reads(6_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) } @@ -911,10 +970,11 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `883 + r * (23417 ±0)` // Estimated: `0` - // Minimum execution time: 281_920 nanoseconds. - Weight::from_ref_time(242_057_723) - // Standard Error: 464_911 - .saturating_add(Weight::from_ref_time(404_673_309).saturating_mul(r.into())) + // Minimum execution time: 286_118 nanoseconds. + Weight::from_ref_time(195_812_951) + .saturating_add(Weight::from_proof_size(0)) + // Standard Error: 858_501 + .saturating_add(Weight::from_ref_time(485_357_331).saturating_mul(r.into())) .saturating_add(T::DbWeight::get().reads(6_u64)) .saturating_add(T::DbWeight::get().reads((80_u64).saturating_mul(r.into()))) .saturating_add(T::DbWeight::get().writes(3_u64)) @@ -927,10 +987,11 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `12583 + n * (11969 ±0)` // Estimated: `0` - // Minimum execution time: 423_923 nanoseconds. - Weight::from_ref_time(573_806_626) - // Standard Error: 1_371_107 - .saturating_add(Weight::from_ref_time(85_963_445).saturating_mul(n.into())) + // Minimum execution time: 433_181 nanoseconds. + Weight::from_ref_time(618_575_096) + .saturating_add(Weight::from_proof_size(0)) + // Standard Error: 1_696_126 + .saturating_add(Weight::from_ref_time(96_213_633).saturating_mul(n.into())) .saturating_add(T::DbWeight::get().reads(52_u64)) .saturating_add(T::DbWeight::get().reads((7_u64).saturating_mul(n.into()))) .saturating_add(T::DbWeight::get().writes(50_u64)) @@ -943,10 +1004,11 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `15138 + n * (175775 ±0)` // Estimated: `0` - // Minimum execution time: 424_048 nanoseconds. - Weight::from_ref_time(542_298_050) - // Standard Error: 1_092_010 - .saturating_add(Weight::from_ref_time(60_111_206).saturating_mul(n.into())) + // Minimum execution time: 433_354 nanoseconds. + Weight::from_ref_time(597_131_349) + .saturating_add(Weight::from_proof_size(0)) + // Standard Error: 1_509_003 + .saturating_add(Weight::from_ref_time(64_872_907).saturating_mul(n.into())) .saturating_add(T::DbWeight::get().reads(51_u64)) .saturating_add(T::DbWeight::get().reads((7_u64).saturating_mul(n.into()))) .saturating_add(T::DbWeight::get().writes(49_u64)) @@ -959,10 +1021,11 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `876 + r * (23098 ±0)` // Estimated: `0` - // Minimum execution time: 285_714 nanoseconds. - Weight::from_ref_time(245_068_941) - // Standard Error: 417_796 - .saturating_add(Weight::from_ref_time(394_288_572).saturating_mul(r.into())) + // Minimum execution time: 286_788 nanoseconds. + Weight::from_ref_time(207_787_332) + .saturating_add(Weight::from_proof_size(0)) + // Standard Error: 778_284 + .saturating_add(Weight::from_ref_time(474_879_317).saturating_mul(r.into())) .saturating_add(T::DbWeight::get().reads(6_u64)) .saturating_add(T::DbWeight::get().reads((80_u64).saturating_mul(r.into()))) .saturating_add(T::DbWeight::get().writes(3_u64)) @@ -975,10 +1038,11 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `14863 + n * (175768 ±0)` // Estimated: `0` - // Minimum execution time: 385_278 nanoseconds. - Weight::from_ref_time(522_656_525) - // Standard Error: 1_259_587 - .saturating_add(Weight::from_ref_time(62_799_142).saturating_mul(n.into())) + // Minimum execution time: 397_414 nanoseconds. + Weight::from_ref_time(573_575_029) + .saturating_add(Weight::from_proof_size(0)) + // Standard Error: 1_630_691 + .saturating_add(Weight::from_ref_time(67_888_226).saturating_mul(n.into())) .saturating_add(T::DbWeight::get().reads(51_u64)) .saturating_add(T::DbWeight::get().reads((7_u64).saturating_mul(n.into()))) .saturating_add(T::DbWeight::get().writes(48_u64)) @@ -991,10 +1055,11 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `878 + r * (23740 ±0)` // Estimated: `0` - // Minimum execution time: 282_513 nanoseconds. - Weight::from_ref_time(256_242_753) - // Standard Error: 362_571 - .saturating_add(Weight::from_ref_time(317_951_687).saturating_mul(r.into())) + // Minimum execution time: 286_098 nanoseconds. + Weight::from_ref_time(223_637_903) + .saturating_add(Weight::from_proof_size(0)) + // Standard Error: 669_294 + .saturating_add(Weight::from_ref_time(387_577_627).saturating_mul(r.into())) .saturating_add(T::DbWeight::get().reads(6_u64)) .saturating_add(T::DbWeight::get().reads((80_u64).saturating_mul(r.into()))) .saturating_add(T::DbWeight::get().writes(3_u64)) @@ -1006,10 +1071,11 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `15469 + n * (175775 ±0)` // Estimated: `0` - // Minimum execution time: 370_576 nanoseconds. - Weight::from_ref_time(487_764_999) - // Standard Error: 1_073_165 - .saturating_add(Weight::from_ref_time(147_588_190).saturating_mul(n.into())) + // Minimum execution time: 373_335 nanoseconds. + Weight::from_ref_time(531_442_564) + .saturating_add(Weight::from_proof_size(0)) + // Standard Error: 1_474_336 + .saturating_add(Weight::from_ref_time(154_469_598).saturating_mul(n.into())) .saturating_add(T::DbWeight::get().reads(51_u64)) .saturating_add(T::DbWeight::get().reads((7_u64).saturating_mul(n.into()))) .saturating_add(T::DbWeight::get().writes(3_u64)) @@ -1021,10 +1087,11 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `871 + r * (23100 ±0)` // Estimated: `0` - // Minimum execution time: 285_917 nanoseconds. - Weight::from_ref_time(259_066_807) - // Standard Error: 340_183 - .saturating_add(Weight::from_ref_time(306_291_698).saturating_mul(r.into())) + // Minimum execution time: 284_183 nanoseconds. + Weight::from_ref_time(223_325_689) + .saturating_add(Weight::from_proof_size(0)) + // Standard Error: 644_587 + .saturating_add(Weight::from_ref_time(373_628_582).saturating_mul(r.into())) .saturating_add(T::DbWeight::get().reads(6_u64)) .saturating_add(T::DbWeight::get().reads((80_u64).saturating_mul(r.into()))) .saturating_add(T::DbWeight::get().writes(3_u64)) @@ -1036,10 +1103,11 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `14814 + n * (175782 ±0)` // Estimated: `0` - // Minimum execution time: 366_225 nanoseconds. - Weight::from_ref_time(470_470_223) - // Standard Error: 953_976 - .saturating_add(Weight::from_ref_time(57_748_742).saturating_mul(n.into())) + // Minimum execution time: 368_310 nanoseconds. + Weight::from_ref_time(512_814_023) + .saturating_add(Weight::from_proof_size(0)) + // Standard Error: 1_337_352 + .saturating_add(Weight::from_ref_time(62_871_539).saturating_mul(n.into())) .saturating_add(T::DbWeight::get().reads(51_u64)) .saturating_add(T::DbWeight::get().reads((7_u64).saturating_mul(n.into()))) .saturating_add(T::DbWeight::get().writes(3_u64)) @@ -1051,10 +1119,11 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `879 + r * (23740 ±0)` // Estimated: `0` - // Minimum execution time: 286_867 nanoseconds. - Weight::from_ref_time(244_403_664) - // Standard Error: 435_431 - .saturating_add(Weight::from_ref_time(409_282_991).saturating_mul(r.into())) + // Minimum execution time: 287_096 nanoseconds. + Weight::from_ref_time(204_878_281) + .saturating_add(Weight::from_proof_size(0)) + // Standard Error: 833_763 + .saturating_add(Weight::from_ref_time(483_927_706).saturating_mul(r.into())) .saturating_add(T::DbWeight::get().reads(6_u64)) .saturating_add(T::DbWeight::get().reads((80_u64).saturating_mul(r.into()))) .saturating_add(T::DbWeight::get().writes(3_u64)) @@ -1067,10 +1136,11 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `15470 + n * (175775 ±0)` // Estimated: `0` - // Minimum execution time: 393_392 nanoseconds. - Weight::from_ref_time(540_938_487) - // Standard Error: 1_361_411 - .saturating_add(Weight::from_ref_time(153_456_560).saturating_mul(n.into())) + // Minimum execution time: 399_318 nanoseconds. + Weight::from_ref_time(586_658_466) + .saturating_add(Weight::from_proof_size(0)) + // Standard Error: 1_734_497 + .saturating_add(Weight::from_ref_time(161_047_970).saturating_mul(n.into())) .saturating_add(T::DbWeight::get().reads(51_u64)) .saturating_add(T::DbWeight::get().reads((7_u64).saturating_mul(n.into()))) .saturating_add(T::DbWeight::get().writes(48_u64)) @@ -1091,10 +1161,11 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `1393 + r * (3602 ±0)` // Estimated: `0` - // Minimum execution time: 286_766 nanoseconds. - Weight::from_ref_time(221_458_774) - // Standard Error: 714_182 - .saturating_add(Weight::from_ref_time(1_402_610_222).saturating_mul(r.into())) + // Minimum execution time: 285_460 nanoseconds. + Weight::from_ref_time(227_848_079) + .saturating_add(Weight::from_proof_size(0)) + // Standard Error: 717_899 + .saturating_add(Weight::from_ref_time(1_434_112_130).saturating_mul(r.into())) .saturating_add(T::DbWeight::get().reads(7_u64)) .saturating_add(T::DbWeight::get().reads((80_u64).saturating_mul(r.into()))) .saturating_add(T::DbWeight::get().writes(4_u64)) @@ -1115,10 +1186,11 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `1551 + r * (20511 ±0)` // Estimated: `0` - // Minimum execution time: 287_158 nanoseconds. - Weight::from_ref_time(288_377_000) - // Standard Error: 6_108_706 - .saturating_add(Weight::from_ref_time(21_691_098_517).saturating_mul(r.into())) + // Minimum execution time: 286_947 nanoseconds. + Weight::from_ref_time(288_518_000) + .saturating_add(Weight::from_proof_size(0)) + // Standard Error: 8_492_544 + .saturating_add(Weight::from_ref_time(21_606_377_662).saturating_mul(r.into())) .saturating_add(T::DbWeight::get().reads(7_u64)) .saturating_add(T::DbWeight::get().reads((160_u64).saturating_mul(r.into()))) .saturating_add(T::DbWeight::get().writes(3_u64)) @@ -1139,10 +1211,11 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `0 + r * (71670 ±0)` // Estimated: `0` - // Minimum execution time: 287_380 nanoseconds. - Weight::from_ref_time(288_241_000) - // Standard Error: 7_007_658 - .saturating_add(Weight::from_ref_time(21_428_850_764).saturating_mul(r.into())) + // Minimum execution time: 285_196 nanoseconds. + Weight::from_ref_time(287_958_000) + .saturating_add(Weight::from_proof_size(0)) + // Standard Error: 10_345_109 + .saturating_add(Weight::from_ref_time(21_395_365_662).saturating_mul(r.into())) .saturating_add(T::DbWeight::get().reads(6_u64)) .saturating_add(T::DbWeight::get().reads((150_u64).saturating_mul(r.into()))) .saturating_add(T::DbWeight::get().writes(3_u64)) @@ -1164,12 +1237,13 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `21611 + t * (15369 ±0)` // Estimated: `0` - // Minimum execution time: 10_644_986 nanoseconds. - Weight::from_ref_time(9_596_635_640) - // Standard Error: 6_393_384 - .saturating_add(Weight::from_ref_time(1_304_764_528).saturating_mul(t.into())) - // Standard Error: 9_586 - .saturating_add(Weight::from_ref_time(9_663_819).saturating_mul(c.into())) + // Minimum execution time: 10_540_614 nanoseconds. + Weight::from_ref_time(9_281_766_912) + .saturating_add(Weight::from_proof_size(0)) + // Standard Error: 5_409_378 + .saturating_add(Weight::from_ref_time(1_449_975_070).saturating_mul(t.into())) + // Standard Error: 8_111 + .saturating_add(Weight::from_ref_time(10_275_108).saturating_mul(c.into())) .saturating_add(T::DbWeight::get().reads(167_u64)) .saturating_add(T::DbWeight::get().reads((81_u64).saturating_mul(t.into()))) .saturating_add(T::DbWeight::get().writes(163_u64)) @@ -1194,10 +1268,11 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `1613 + r * (25576 ±0)` // Estimated: `0` - // Minimum execution time: 284_948 nanoseconds. - Weight::from_ref_time(289_276_000) - // Standard Error: 18_674_951 - .saturating_add(Weight::from_ref_time(27_090_355_673).saturating_mul(r.into())) + // Minimum execution time: 285_643 nanoseconds. + Weight::from_ref_time(287_472_000) + .saturating_add(Weight::from_proof_size(0)) + // Standard Error: 25_921_947 + .saturating_add(Weight::from_ref_time(27_745_815_511).saturating_mul(r.into())) .saturating_add(T::DbWeight::get().reads(8_u64)) .saturating_add(T::DbWeight::get().reads((400_u64).saturating_mul(r.into()))) .saturating_add(T::DbWeight::get().writes(5_u64)) @@ -1224,14 +1299,13 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `5666 + t * (17 ±0)` // Estimated: `0` - // Minimum execution time: 127_857_642 nanoseconds. - Weight::from_ref_time(11_399_054_049) - // Standard Error: 95_033_651 - .saturating_add(Weight::from_ref_time(434_246_236).saturating_mul(t.into())) - // Standard Error: 154_973 - .saturating_add(Weight::from_ref_time(121_130_672).saturating_mul(i.into())) - // Standard Error: 154_973 - .saturating_add(Weight::from_ref_time(121_554_853).saturating_mul(s.into())) + // Minimum execution time: 129_710_453 nanoseconds. + Weight::from_ref_time(14_347_603_160) + .saturating_add(Weight::from_proof_size(0)) + // Standard Error: 194_692 + .saturating_add(Weight::from_ref_time(128_837_066).saturating_mul(i.into())) + // Standard Error: 194_692 + .saturating_add(Weight::from_ref_time(127_568_555).saturating_mul(s.into())) .saturating_add(T::DbWeight::get().reads(249_u64)) .saturating_add(T::DbWeight::get().reads((1_u64).saturating_mul(t.into()))) .saturating_add(T::DbWeight::get().writes(247_u64)) @@ -1252,10 +1326,11 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `839 + r * (642 ±0)` // Estimated: `0` - // Minimum execution time: 280_949 nanoseconds. - Weight::from_ref_time(286_538_475) - // Standard Error: 124_866 - .saturating_add(Weight::from_ref_time(42_531_824).saturating_mul(r.into())) + // Minimum execution time: 282_059 nanoseconds. + Weight::from_ref_time(287_215_177) + .saturating_add(Weight::from_proof_size(0)) + // Standard Error: 273_484 + .saturating_add(Weight::from_ref_time(44_941_522).saturating_mul(r.into())) .saturating_add(T::DbWeight::get().reads(6_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) } @@ -1274,10 +1349,11 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `1641` // Estimated: `0` - // Minimum execution time: 328_414 nanoseconds. - Weight::from_ref_time(329_293_000) - // Standard Error: 50_816 - .saturating_add(Weight::from_ref_time(318_312_506).saturating_mul(n.into())) + // Minimum execution time: 330_822 nanoseconds. + Weight::from_ref_time(333_012_000) + .saturating_add(Weight::from_proof_size(0)) + // Standard Error: 53_200 + .saturating_add(Weight::from_ref_time(327_144_474).saturating_mul(n.into())) .saturating_add(T::DbWeight::get().reads(6_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) } @@ -1296,10 +1372,11 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `841 + r * (642 ±0)` // Estimated: `0` - // Minimum execution time: 282_208 nanoseconds. - Weight::from_ref_time(286_848_187) - // Standard Error: 106_214 - .saturating_add(Weight::from_ref_time(56_342_512).saturating_mul(r.into())) + // Minimum execution time: 282_859 nanoseconds. + Weight::from_ref_time(286_293_402) + .saturating_add(Weight::from_proof_size(0)) + // Standard Error: 187_930 + .saturating_add(Weight::from_ref_time(57_415_297).saturating_mul(r.into())) .saturating_add(T::DbWeight::get().reads(6_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) } @@ -1318,10 +1395,11 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `1643` // Estimated: `0` - // Minimum execution time: 342_478 nanoseconds. - Weight::from_ref_time(342_947_000) - // Standard Error: 60_809 - .saturating_add(Weight::from_ref_time(255_492_149).saturating_mul(n.into())) + // Minimum execution time: 342_221 nanoseconds. + Weight::from_ref_time(343_250_000) + .saturating_add(Weight::from_proof_size(0)) + // Standard Error: 67_989 + .saturating_add(Weight::from_ref_time(261_498_077).saturating_mul(n.into())) .saturating_add(T::DbWeight::get().reads(6_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) } @@ -1340,10 +1418,11 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `841 + r * (642 ±0)` // Estimated: `0` - // Minimum execution time: 279_059 nanoseconds. - Weight::from_ref_time(285_413_659) - // Standard Error: 123_081 - .saturating_add(Weight::from_ref_time(33_154_840).saturating_mul(r.into())) + // Minimum execution time: 282_158 nanoseconds. + Weight::from_ref_time(285_783_279) + .saturating_add(Weight::from_proof_size(0)) + // Standard Error: 200_413 + .saturating_add(Weight::from_ref_time(35_099_520).saturating_mul(r.into())) .saturating_add(T::DbWeight::get().reads(6_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) } @@ -1362,10 +1441,11 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `1643` // Estimated: `0` - // Minimum execution time: 317_518 nanoseconds. - Weight::from_ref_time(318_178_000) - // Standard Error: 60_074 - .saturating_add(Weight::from_ref_time(99_403_819).saturating_mul(n.into())) + // Minimum execution time: 317_376 nanoseconds. + Weight::from_ref_time(319_441_000) + .saturating_add(Weight::from_proof_size(0)) + // Standard Error: 61_325 + .saturating_add(Weight::from_ref_time(101_080_739).saturating_mul(n.into())) .saturating_add(T::DbWeight::get().reads(6_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) } @@ -1384,10 +1464,11 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `841 + r * (679 ±0)` // Estimated: `0` - // Minimum execution time: 280_145 nanoseconds. - Weight::from_ref_time(285_483_032) - // Standard Error: 106_113 - .saturating_add(Weight::from_ref_time(33_475_067).saturating_mul(r.into())) + // Minimum execution time: 280_403 nanoseconds. + Weight::from_ref_time(282_592_267) + .saturating_add(Weight::from_proof_size(0)) + // Standard Error: 237_832 + .saturating_add(Weight::from_ref_time(37_056_632).saturating_mul(r.into())) .saturating_add(T::DbWeight::get().reads(6_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) } @@ -1406,10 +1487,11 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `1643` // Estimated: `0` - // Minimum execution time: 318_141 nanoseconds. - Weight::from_ref_time(318_699_000) - // Standard Error: 55_136 - .saturating_add(Weight::from_ref_time(99_275_434).saturating_mul(n.into())) + // Minimum execution time: 315_482 nanoseconds. + Weight::from_ref_time(316_160_000) + .saturating_add(Weight::from_proof_size(0)) + // Standard Error: 40_926 + .saturating_add(Weight::from_ref_time(100_317_235).saturating_mul(n.into())) .saturating_add(T::DbWeight::get().reads(6_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) } @@ -1428,10 +1510,11 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `885 + r * (6083 ±0)` // Estimated: `0` - // Minimum execution time: 282_474 nanoseconds. - Weight::from_ref_time(288_078_802) - // Standard Error: 302_968 - .saturating_add(Weight::from_ref_time(2_944_967_597).saturating_mul(r.into())) + // Minimum execution time: 276_155 nanoseconds. + Weight::from_ref_time(278_531_728) + .saturating_add(Weight::from_proof_size(0)) + // Standard Error: 510_501 + .saturating_add(Weight::from_ref_time(3_056_236_671).saturating_mul(r.into())) .saturating_add(T::DbWeight::get().reads(6_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) } @@ -1450,10 +1533,11 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `854 + r * (3362 ±0)` // Estimated: `0` - // Minimum execution time: 281_514 nanoseconds. - Weight::from_ref_time(287_458_651) - // Standard Error: 146_715 - .saturating_add(Weight::from_ref_time(731_367_948).saturating_mul(r.into())) + // Minimum execution time: 276_204 nanoseconds. + Weight::from_ref_time(278_114_283) + .saturating_add(Weight::from_proof_size(0)) + // Standard Error: 217_508 + .saturating_add(Weight::from_ref_time(737_577_816).saturating_mul(r.into())) .saturating_add(T::DbWeight::get().reads(6_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) } @@ -1474,10 +1558,11 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `0 + r * (79300 ±0)` // Estimated: `0` - // Minimum execution time: 282_591 nanoseconds. - Weight::from_ref_time(286_842_000) - // Standard Error: 2_645_254 - .saturating_add(Weight::from_ref_time(1_394_535_676).saturating_mul(r.into())) + // Minimum execution time: 276_330 nanoseconds. + Weight::from_ref_time(277_573_000) + .saturating_add(Weight::from_proof_size(0)) + // Standard Error: 3_652_635 + .saturating_add(Weight::from_ref_time(1_547_361_882).saturating_mul(r.into())) .saturating_add(T::DbWeight::get().reads(6_u64)) .saturating_add(T::DbWeight::get().reads((225_u64).saturating_mul(r.into()))) .saturating_add(T::DbWeight::get().writes(3_u64)) @@ -1498,10 +1583,11 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `837 + r * (240 ±0)` // Estimated: `0` - // Minimum execution time: 286_631 nanoseconds. - Weight::from_ref_time(288_787_650) - // Standard Error: 29_802 - .saturating_add(Weight::from_ref_time(11_115_811).saturating_mul(r.into())) + // Minimum execution time: 276_228 nanoseconds. + Weight::from_ref_time(281_135_551) + .saturating_add(Weight::from_proof_size(0)) + // Standard Error: 18_737 + .saturating_add(Weight::from_ref_time(10_998_953).saturating_mul(r.into())) .saturating_add(T::DbWeight::get().reads(6_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) } @@ -1520,10 +1606,11 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `2056 + r * (3153 ±0)` // Estimated: `0` - // Minimum execution time: 287_775 nanoseconds. - Weight::from_ref_time(319_806_123) - // Standard Error: 111_808 - .saturating_add(Weight::from_ref_time(17_641_181).saturating_mul(r.into())) + // Minimum execution time: 278_157 nanoseconds. + Weight::from_ref_time(300_739_078) + .saturating_add(Weight::from_proof_size(0)) + // Standard Error: 72_695 + .saturating_add(Weight::from_ref_time(18_499_645).saturating_mul(r.into())) .saturating_add(T::DbWeight::get().reads(6_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) } @@ -1544,10 +1631,11 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `840 + r * (240 ±0)` // Estimated: `0` - // Minimum execution time: 285_077 nanoseconds. - Weight::from_ref_time(289_980_475) - // Standard Error: 14_535 - .saturating_add(Weight::from_ref_time(9_295_346).saturating_mul(r.into())) + // Minimum execution time: 275_532 nanoseconds. + Weight::from_ref_time(281_299_677) + .saturating_add(Weight::from_proof_size(0)) + // Standard Error: 21_719 + .saturating_add(Weight::from_ref_time(9_513_444).saturating_mul(r.into())) .saturating_add(T::DbWeight::get().reads(7_u64)) .saturating_add(T::DbWeight::get().writes(4_u64)) } @@ -1556,520 +1644,572 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 777 nanoseconds. - Weight::from_ref_time(1_014_498) - // Standard Error: 154 - .saturating_add(Weight::from_ref_time(405_551).saturating_mul(r.into())) + // Minimum execution time: 856 nanoseconds. + Weight::from_ref_time(1_054_710) + .saturating_add(Weight::from_proof_size(0)) + // Standard Error: 273 + .saturating_add(Weight::from_ref_time(411_548).saturating_mul(r.into())) } /// The range of component `r` is `[0, 50]`. fn instr_i64load(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 862 nanoseconds. - Weight::from_ref_time(1_345_826) - // Standard Error: 457 - .saturating_add(Weight::from_ref_time(1_033_909).saturating_mul(r.into())) + // Minimum execution time: 980 nanoseconds. + Weight::from_ref_time(1_463_182) + .saturating_add(Weight::from_proof_size(0)) + // Standard Error: 415 + .saturating_add(Weight::from_ref_time(1_063_436).saturating_mul(r.into())) } /// The range of component `r` is `[0, 50]`. fn instr_i64store(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 892 nanoseconds. - Weight::from_ref_time(1_233_601) - // Standard Error: 341 - .saturating_add(Weight::from_ref_time(885_275).saturating_mul(r.into())) + // Minimum execution time: 991 nanoseconds. + Weight::from_ref_time(1_521_750) + .saturating_add(Weight::from_proof_size(0)) + // Standard Error: 8_499 + .saturating_add(Weight::from_ref_time(997_972).saturating_mul(r.into())) } /// The range of component `r` is `[0, 50]`. fn instr_select(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 771 nanoseconds. - Weight::from_ref_time(1_099_906) - // Standard Error: 261 - .saturating_add(Weight::from_ref_time(1_092_031).saturating_mul(r.into())) + // Minimum execution time: 833 nanoseconds. + Weight::from_ref_time(1_152_048) + .saturating_add(Weight::from_proof_size(0)) + // Standard Error: 392 + .saturating_add(Weight::from_ref_time(1_147_506).saturating_mul(r.into())) } /// The range of component `r` is `[0, 50]`. fn instr_if(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 785 nanoseconds. - Weight::from_ref_time(929_328) - // Standard Error: 333 - .saturating_add(Weight::from_ref_time(1_374_749).saturating_mul(r.into())) + // Minimum execution time: 866 nanoseconds. + Weight::from_ref_time(1_154_637) + .saturating_add(Weight::from_proof_size(0)) + // Standard Error: 5_131 + .saturating_add(Weight::from_ref_time(1_320_580).saturating_mul(r.into())) } /// The range of component `r` is `[0, 50]`. fn instr_br(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 772 nanoseconds. - Weight::from_ref_time(979_702) - // Standard Error: 351 - .saturating_add(Weight::from_ref_time(621_385).saturating_mul(r.into())) + // Minimum execution time: 849 nanoseconds. + Weight::from_ref_time(1_073_940) + .saturating_add(Weight::from_proof_size(0)) + // Standard Error: 639 + .saturating_add(Weight::from_ref_time(642_885).saturating_mul(r.into())) } /// The range of component `r` is `[0, 50]`. fn instr_br_if(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 766 nanoseconds. - Weight::from_ref_time(1_303_783) - // Standard Error: 1_556 - .saturating_add(Weight::from_ref_time(841_842).saturating_mul(r.into())) + // Minimum execution time: 855 nanoseconds. + Weight::from_ref_time(728_631) + .saturating_add(Weight::from_proof_size(0)) + // Standard Error: 1_730 + .saturating_add(Weight::from_ref_time(979_839).saturating_mul(r.into())) } /// The range of component `r` is `[0, 50]`. fn instr_br_table(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 792 nanoseconds. - Weight::from_ref_time(1_093_901) - // Standard Error: 1_383 - .saturating_add(Weight::from_ref_time(1_145_435).saturating_mul(r.into())) + // Minimum execution time: 852 nanoseconds. + Weight::from_ref_time(658_935) + .saturating_add(Weight::from_proof_size(0)) + // Standard Error: 2_373 + .saturating_add(Weight::from_ref_time(1_172_585).saturating_mul(r.into())) } /// The range of component `e` is `[1, 256]`. fn instr_br_table_per_entry(e: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 2_526 nanoseconds. - Weight::from_ref_time(2_872_561) - // Standard Error: 60 - .saturating_add(Weight::from_ref_time(4_365).saturating_mul(e.into())) + // Minimum execution time: 2_577 nanoseconds. + Weight::from_ref_time(2_872_555) + .saturating_add(Weight::from_proof_size(0)) + // Standard Error: 70 + .saturating_add(Weight::from_ref_time(4_237).saturating_mul(e.into())) } /// The range of component `r` is `[0, 50]`. fn instr_call(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 834 nanoseconds. - Weight::from_ref_time(1_431_876) - // Standard Error: 1_448 - .saturating_add(Weight::from_ref_time(2_268_715).saturating_mul(r.into())) + // Minimum execution time: 833 nanoseconds. + Weight::from_ref_time(874_000) + .saturating_add(Weight::from_proof_size(0)) + // Standard Error: 30_863 + .saturating_add(Weight::from_ref_time(2_591_963).saturating_mul(r.into())) } /// The range of component `r` is `[0, 50]`. fn instr_call_indirect(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 920 nanoseconds. - Weight::from_ref_time(2_167_004) - // Standard Error: 2_060 - .saturating_add(Weight::from_ref_time(2_921_443).saturating_mul(r.into())) + // Minimum execution time: 992 nanoseconds. + Weight::from_ref_time(2_772_709) + .saturating_add(Weight::from_proof_size(0)) + // Standard Error: 3_822 + .saturating_add(Weight::from_ref_time(3_070_825).saturating_mul(r.into())) } /// The range of component `p` is `[0, 128]`. fn instr_call_indirect_per_param(p: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 4_624 nanoseconds. - Weight::from_ref_time(5_534_325) - // Standard Error: 326 - .saturating_add(Weight::from_ref_time(184_307).saturating_mul(p.into())) + // Minimum execution time: 4_766 nanoseconds. + Weight::from_ref_time(5_559_951) + .saturating_add(Weight::from_proof_size(0)) + // Standard Error: 315 + .saturating_add(Weight::from_ref_time(227_249).saturating_mul(p.into())) } /// The range of component `l` is `[0, 1024]`. fn instr_call_per_local(l: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 3_062 nanoseconds. - Weight::from_ref_time(4_432_879) - // Standard Error: 64 - .saturating_add(Weight::from_ref_time(46_196).saturating_mul(l.into())) + // Minimum execution time: 3_215 nanoseconds. + Weight::from_ref_time(4_697_732) + .saturating_add(Weight::from_proof_size(0)) + // Standard Error: 99 + .saturating_add(Weight::from_ref_time(46_431).saturating_mul(l.into())) } /// The range of component `r` is `[0, 50]`. fn instr_local_get(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 2_036 nanoseconds. - Weight::from_ref_time(2_318_877) - // Standard Error: 172 - .saturating_add(Weight::from_ref_time(500_498).saturating_mul(r.into())) + // Minimum execution time: 2_193 nanoseconds. + Weight::from_ref_time(2_472_277) + .saturating_add(Weight::from_proof_size(0)) + // Standard Error: 374 + .saturating_add(Weight::from_ref_time(459_651).saturating_mul(r.into())) } /// The range of component `r` is `[0, 50]`. fn instr_local_set(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 2_027 nanoseconds. - Weight::from_ref_time(2_355_900) - // Standard Error: 220 - .saturating_add(Weight::from_ref_time(461_393).saturating_mul(r.into())) + // Minimum execution time: 2_112 nanoseconds. + Weight::from_ref_time(2_388_132) + .saturating_add(Weight::from_proof_size(0)) + // Standard Error: 380 + .saturating_add(Weight::from_ref_time(485_216).saturating_mul(r.into())) } /// The range of component `r` is `[0, 50]`. fn instr_local_tee(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 2_038 nanoseconds. - Weight::from_ref_time(2_350_330) - // Standard Error: 224 - .saturating_add(Weight::from_ref_time(586_808).saturating_mul(r.into())) + // Minimum execution time: 2_219 nanoseconds. + Weight::from_ref_time(2_419_568) + .saturating_add(Weight::from_proof_size(0)) + // Standard Error: 402 + .saturating_add(Weight::from_ref_time(658_484).saturating_mul(r.into())) } /// The range of component `r` is `[0, 50]`. fn instr_global_get(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 897 nanoseconds. - Weight::from_ref_time(1_267_115) - // Standard Error: 189 - .saturating_add(Weight::from_ref_time(884_926).saturating_mul(r.into())) + // Minimum execution time: 959 nanoseconds. + Weight::from_ref_time(1_321_004) + .saturating_add(Weight::from_proof_size(0)) + // Standard Error: 480 + .saturating_add(Weight::from_ref_time(898_447).saturating_mul(r.into())) } /// The range of component `r` is `[0, 50]`. fn instr_global_set(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 892 nanoseconds. - Weight::from_ref_time(1_202_122) - // Standard Error: 286 - .saturating_add(Weight::from_ref_time(885_157).saturating_mul(r.into())) + // Minimum execution time: 934 nanoseconds. + Weight::from_ref_time(1_264_350) + .saturating_add(Weight::from_proof_size(0)) + // Standard Error: 1_031 + .saturating_add(Weight::from_ref_time(922_243).saturating_mul(r.into())) } /// The range of component `r` is `[0, 50]`. fn instr_memory_current(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 852 nanoseconds. - Weight::from_ref_time(1_132_479) - // Standard Error: 224 - .saturating_add(Weight::from_ref_time(719_603).saturating_mul(r.into())) + // Minimum execution time: 941 nanoseconds. + Weight::from_ref_time(1_229_407) + .saturating_add(Weight::from_proof_size(0)) + // Standard Error: 507 + .saturating_add(Weight::from_ref_time(824_206).saturating_mul(r.into())) } /// The range of component `r` is `[0, 1]`. fn instr_memory_grow(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 786 nanoseconds. - Weight::from_ref_time(874_044) - // Standard Error: 91_309 - .saturating_add(Weight::from_ref_time(181_849_955).saturating_mul(r.into())) + // Minimum execution time: 870 nanoseconds. + Weight::from_ref_time(946_681) + .saturating_add(Weight::from_proof_size(0)) + // Standard Error: 7_248 + .saturating_add(Weight::from_ref_time(179_759_218).saturating_mul(r.into())) } /// The range of component `r` is `[0, 50]`. fn instr_i64clz(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 763 nanoseconds. - Weight::from_ref_time(1_055_236) - // Standard Error: 207 - .saturating_add(Weight::from_ref_time(554_985).saturating_mul(r.into())) + // Minimum execution time: 843 nanoseconds. + Weight::from_ref_time(1_105_279) + .saturating_add(Weight::from_proof_size(0)) + // Standard Error: 251 + .saturating_add(Weight::from_ref_time(633_218).saturating_mul(r.into())) } /// The range of component `r` is `[0, 50]`. fn instr_i64ctz(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 756 nanoseconds. - Weight::from_ref_time(1_053_050) - // Standard Error: 165 - .saturating_add(Weight::from_ref_time(555_401).saturating_mul(r.into())) + // Minimum execution time: 843 nanoseconds. + Weight::from_ref_time(1_119_719) + .saturating_add(Weight::from_proof_size(0)) + // Standard Error: 248 + .saturating_add(Weight::from_ref_time(633_064).saturating_mul(r.into())) } /// The range of component `r` is `[0, 50]`. fn instr_i64popcnt(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 791 nanoseconds. - Weight::from_ref_time(1_080_240) - // Standard Error: 164 - .saturating_add(Weight::from_ref_time(554_698).saturating_mul(r.into())) + // Minimum execution time: 865 nanoseconds. + Weight::from_ref_time(1_394_651) + .saturating_add(Weight::from_proof_size(0)) + // Standard Error: 9_898 + .saturating_add(Weight::from_ref_time(630_054).saturating_mul(r.into())) } /// The range of component `r` is `[0, 50]`. fn instr_i64eqz(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 766 nanoseconds. - Weight::from_ref_time(1_074_739) - // Standard Error: 178 - .saturating_add(Weight::from_ref_time(565_891).saturating_mul(r.into())) + // Minimum execution time: 831 nanoseconds. + Weight::from_ref_time(1_095_288) + .saturating_add(Weight::from_proof_size(0)) + // Standard Error: 271 + .saturating_add(Weight::from_ref_time(650_578).saturating_mul(r.into())) } /// The range of component `r` is `[0, 50]`. fn instr_i64extendsi32(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 781 nanoseconds. - Weight::from_ref_time(1_077_122) - // Standard Error: 177 - .saturating_add(Weight::from_ref_time(548_846).saturating_mul(r.into())) + // Minimum execution time: 835 nanoseconds. + Weight::from_ref_time(1_128_234) + .saturating_add(Weight::from_proof_size(0)) + // Standard Error: 239 + .saturating_add(Weight::from_ref_time(615_970).saturating_mul(r.into())) } /// The range of component `r` is `[0, 50]`. fn instr_i64extendui32(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 793 nanoseconds. - Weight::from_ref_time(1_086_278) - // Standard Error: 163 - .saturating_add(Weight::from_ref_time(548_765).saturating_mul(r.into())) + // Minimum execution time: 839 nanoseconds. + Weight::from_ref_time(1_111_296) + .saturating_add(Weight::from_proof_size(0)) + // Standard Error: 303 + .saturating_add(Weight::from_ref_time(616_831).saturating_mul(r.into())) } /// The range of component `r` is `[0, 50]`. fn instr_i32wrapi64(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 769 nanoseconds. - Weight::from_ref_time(1_096_044) - // Standard Error: 134 - .saturating_add(Weight::from_ref_time(547_353).saturating_mul(r.into())) + // Minimum execution time: 839 nanoseconds. + Weight::from_ref_time(1_082_700) + .saturating_add(Weight::from_proof_size(0)) + // Standard Error: 236 + .saturating_add(Weight::from_ref_time(617_371).saturating_mul(r.into())) } /// The range of component `r` is `[0, 50]`. fn instr_i64eq(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 766 nanoseconds. - Weight::from_ref_time(1_072_610) - // Standard Error: 161 - .saturating_add(Weight::from_ref_time(774_895).saturating_mul(r.into())) + // Minimum execution time: 837 nanoseconds. + Weight::from_ref_time(1_249_666) + .saturating_add(Weight::from_proof_size(0)) + // Standard Error: 836 + .saturating_add(Weight::from_ref_time(904_457).saturating_mul(r.into())) } /// The range of component `r` is `[0, 50]`. fn instr_i64ne(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 751 nanoseconds. - Weight::from_ref_time(1_038_676) - // Standard Error: 158 - .saturating_add(Weight::from_ref_time(775_194).saturating_mul(r.into())) + // Minimum execution time: 818 nanoseconds. + Weight::from_ref_time(1_624_057) + .saturating_add(Weight::from_proof_size(0)) + // Standard Error: 5_906 + .saturating_add(Weight::from_ref_time(893_488).saturating_mul(r.into())) } /// The range of component `r` is `[0, 50]`. fn instr_i64lts(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 798 nanoseconds. - Weight::from_ref_time(1_089_712) - // Standard Error: 144 - .saturating_add(Weight::from_ref_time(774_377).saturating_mul(r.into())) + // Minimum execution time: 836 nanoseconds. + Weight::from_ref_time(1_973_044) + .saturating_add(Weight::from_proof_size(0)) + // Standard Error: 7_556 + .saturating_add(Weight::from_ref_time(885_369).saturating_mul(r.into())) } /// The range of component `r` is `[0, 50]`. fn instr_i64ltu(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 758 nanoseconds. - Weight::from_ref_time(1_078_460) - // Standard Error: 180 - .saturating_add(Weight::from_ref_time(779_861).saturating_mul(r.into())) + // Minimum execution time: 846 nanoseconds. + Weight::from_ref_time(1_088_223) + .saturating_add(Weight::from_proof_size(0)) + // Standard Error: 531 + .saturating_add(Weight::from_ref_time(909_313).saturating_mul(r.into())) } /// The range of component `r` is `[0, 50]`. fn instr_i64gts(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 758 nanoseconds. - Weight::from_ref_time(1_089_007) - // Standard Error: 164 - .saturating_add(Weight::from_ref_time(779_372).saturating_mul(r.into())) + // Minimum execution time: 842 nanoseconds. + Weight::from_ref_time(1_435_966) + .saturating_add(Weight::from_proof_size(0)) + // Standard Error: 3_824 + .saturating_add(Weight::from_ref_time(898_198).saturating_mul(r.into())) } /// The range of component `r` is `[0, 50]`. fn instr_i64gtu(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 772 nanoseconds. - Weight::from_ref_time(1_077_512) - // Standard Error: 165 - .saturating_add(Weight::from_ref_time(779_513).saturating_mul(r.into())) + // Minimum execution time: 846 nanoseconds. + Weight::from_ref_time(1_154_015) + .saturating_add(Weight::from_proof_size(0)) + // Standard Error: 319 + .saturating_add(Weight::from_ref_time(918_083).saturating_mul(r.into())) } /// The range of component `r` is `[0, 50]`. fn instr_i64les(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 760 nanoseconds. - Weight::from_ref_time(1_078_546) - // Standard Error: 168 - .saturating_add(Weight::from_ref_time(779_138).saturating_mul(r.into())) + // Minimum execution time: 848 nanoseconds. + Weight::from_ref_time(1_155_323) + .saturating_add(Weight::from_proof_size(0)) + // Standard Error: 307 + .saturating_add(Weight::from_ref_time(906_516).saturating_mul(r.into())) } /// The range of component `r` is `[0, 50]`. fn instr_i64leu(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 757 nanoseconds. - Weight::from_ref_time(1_080_251) - // Standard Error: 168 - .saturating_add(Weight::from_ref_time(779_391).saturating_mul(r.into())) + // Minimum execution time: 877 nanoseconds. + Weight::from_ref_time(1_629_210) + .saturating_add(Weight::from_proof_size(0)) + // Standard Error: 8_175 + .saturating_add(Weight::from_ref_time(906_657).saturating_mul(r.into())) } /// The range of component `r` is `[0, 50]`. fn instr_i64ges(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 786 nanoseconds. - Weight::from_ref_time(1_072_690) - // Standard Error: 219 - .saturating_add(Weight::from_ref_time(780_381).saturating_mul(r.into())) + // Minimum execution time: 843 nanoseconds. + Weight::from_ref_time(1_126_252) + .saturating_add(Weight::from_proof_size(0)) + // Standard Error: 251 + .saturating_add(Weight::from_ref_time(907_052).saturating_mul(r.into())) } /// The range of component `r` is `[0, 50]`. fn instr_i64geu(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 771 nanoseconds. - Weight::from_ref_time(1_063_735) - // Standard Error: 162 - .saturating_add(Weight::from_ref_time(779_906).saturating_mul(r.into())) + // Minimum execution time: 852 nanoseconds. + Weight::from_ref_time(1_497_667) + .saturating_add(Weight::from_proof_size(0)) + // Standard Error: 7_593 + .saturating_add(Weight::from_ref_time(897_474).saturating_mul(r.into())) } /// The range of component `r` is `[0, 50]`. fn instr_i64add(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 785 nanoseconds. - Weight::from_ref_time(1_059_585) - // Standard Error: 155 - .saturating_add(Weight::from_ref_time(756_828).saturating_mul(r.into())) + // Minimum execution time: 856 nanoseconds. + Weight::from_ref_time(1_350_390) + .saturating_add(Weight::from_proof_size(0)) + // Standard Error: 2_625 + .saturating_add(Weight::from_ref_time(889_609).saturating_mul(r.into())) } /// The range of component `r` is `[0, 50]`. fn instr_i64sub(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 800 nanoseconds. - Weight::from_ref_time(1_066_659) - // Standard Error: 154 - .saturating_add(Weight::from_ref_time(754_318).saturating_mul(r.into())) + // Minimum execution time: 841 nanoseconds. + Weight::from_ref_time(2_677_235) + .saturating_add(Weight::from_proof_size(0)) + // Standard Error: 11_507 + .saturating_add(Weight::from_ref_time(846_704).saturating_mul(r.into())) } /// The range of component `r` is `[0, 50]`. fn instr_i64mul(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 769 nanoseconds. - Weight::from_ref_time(1_078_854) - // Standard Error: 172 - .saturating_add(Weight::from_ref_time(754_183).saturating_mul(r.into())) + // Minimum execution time: 846 nanoseconds. + Weight::from_ref_time(1_148_206) + .saturating_add(Weight::from_proof_size(0)) + // Standard Error: 393 + .saturating_add(Weight::from_ref_time(884_886).saturating_mul(r.into())) } /// The range of component `r` is `[0, 50]`. fn instr_i64divs(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 770 nanoseconds. - Weight::from_ref_time(1_057_476) - // Standard Error: 191 - .saturating_add(Weight::from_ref_time(1_443_902).saturating_mul(r.into())) + // Minimum execution time: 865 nanoseconds. + Weight::from_ref_time(2_862_483) + .saturating_add(Weight::from_proof_size(0)) + // Standard Error: 16_413 + .saturating_add(Weight::from_ref_time(1_481_623).saturating_mul(r.into())) } /// The range of component `r` is `[0, 50]`. fn instr_i64divu(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 764 nanoseconds. - Weight::from_ref_time(1_063_821) - // Standard Error: 193 - .saturating_add(Weight::from_ref_time(1_324_496).saturating_mul(r.into())) + // Minimum execution time: 1_443 nanoseconds. + Weight::from_ref_time(1_086_902) + .saturating_add(Weight::from_proof_size(0)) + // Standard Error: 2_245 + .saturating_add(Weight::from_ref_time(1_463_145).saturating_mul(r.into())) } /// The range of component `r` is `[0, 50]`. fn instr_i64rems(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 747 nanoseconds. - Weight::from_ref_time(1_093_209) - // Standard Error: 270 - .saturating_add(Weight::from_ref_time(1_447_180).saturating_mul(r.into())) + // Minimum execution time: 878 nanoseconds. + Weight::from_ref_time(1_130_306) + .saturating_add(Weight::from_proof_size(0)) + // Standard Error: 475 + .saturating_add(Weight::from_ref_time(1_522_011).saturating_mul(r.into())) } /// The range of component `r` is `[0, 50]`. fn instr_i64remu(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 805 nanoseconds. - Weight::from_ref_time(1_033_953) - // Standard Error: 152 - .saturating_add(Weight::from_ref_time(1_336_911).saturating_mul(r.into())) + // Minimum execution time: 827 nanoseconds. + Weight::from_ref_time(1_197_053) + .saturating_add(Weight::from_proof_size(0)) + // Standard Error: 474 + .saturating_add(Weight::from_ref_time(1_449_062).saturating_mul(r.into())) } /// The range of component `r` is `[0, 50]`. fn instr_i64and(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 766 nanoseconds. - Weight::from_ref_time(1_059_430) - // Standard Error: 160 - .saturating_add(Weight::from_ref_time(757_265).saturating_mul(r.into())) + // Minimum execution time: 847 nanoseconds. + Weight::from_ref_time(1_152_423) + .saturating_add(Weight::from_proof_size(0)) + // Standard Error: 296 + .saturating_add(Weight::from_ref_time(895_541).saturating_mul(r.into())) } /// The range of component `r` is `[0, 50]`. fn instr_i64or(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 760 nanoseconds. - Weight::from_ref_time(1_077_376) - // Standard Error: 160 - .saturating_add(Weight::from_ref_time(755_800).saturating_mul(r.into())) + // Minimum execution time: 876 nanoseconds. + Weight::from_ref_time(1_169_485) + .saturating_add(Weight::from_proof_size(0)) + // Standard Error: 571 + .saturating_add(Weight::from_ref_time(932_659).saturating_mul(r.into())) } /// The range of component `r` is `[0, 50]`. fn instr_i64xor(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 798 nanoseconds. - Weight::from_ref_time(1_070_570) - // Standard Error: 157 - .saturating_add(Weight::from_ref_time(756_839).saturating_mul(r.into())) + // Minimum execution time: 856 nanoseconds. + Weight::from_ref_time(1_155_127) + .saturating_add(Weight::from_proof_size(0)) + // Standard Error: 256 + .saturating_add(Weight::from_ref_time(895_663).saturating_mul(r.into())) } /// The range of component `r` is `[0, 50]`. fn instr_i64shl(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 761 nanoseconds. - Weight::from_ref_time(1_074_645) - // Standard Error: 169 - .saturating_add(Weight::from_ref_time(771_486).saturating_mul(r.into())) + // Minimum execution time: 871 nanoseconds. + Weight::from_ref_time(1_139_722) + .saturating_add(Weight::from_proof_size(0)) + // Standard Error: 367 + .saturating_add(Weight::from_ref_time(903_115).saturating_mul(r.into())) } /// The range of component `r` is `[0, 50]`. fn instr_i64shrs(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 779 nanoseconds. - Weight::from_ref_time(1_107_671) - // Standard Error: 185 - .saturating_add(Weight::from_ref_time(769_168).saturating_mul(r.into())) + // Minimum execution time: 853 nanoseconds. + Weight::from_ref_time(1_155_542) + .saturating_add(Weight::from_proof_size(0)) + // Standard Error: 541 + .saturating_add(Weight::from_ref_time(901_635).saturating_mul(r.into())) } /// The range of component `r` is `[0, 50]`. fn instr_i64shru(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 755 nanoseconds. - Weight::from_ref_time(1_075_769) - // Standard Error: 164 - .saturating_add(Weight::from_ref_time(770_334).saturating_mul(r.into())) + // Minimum execution time: 866 nanoseconds. + Weight::from_ref_time(2_260_588) + .saturating_add(Weight::from_proof_size(0)) + // Standard Error: 12_944 + .saturating_add(Weight::from_ref_time(877_790).saturating_mul(r.into())) } /// The range of component `r` is `[0, 50]`. fn instr_i64rotl(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 767 nanoseconds. - Weight::from_ref_time(608_749) - // Standard Error: 2_059 - .saturating_add(Weight::from_ref_time(804_228).saturating_mul(r.into())) + // Minimum execution time: 851 nanoseconds. + Weight::from_ref_time(1_627_816) + .saturating_add(Weight::from_proof_size(0)) + // Standard Error: 4_004 + .saturating_add(Weight::from_ref_time(887_929).saturating_mul(r.into())) } /// The range of component `r` is `[0, 50]`. fn instr_i64rotr(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 779 nanoseconds. - Weight::from_ref_time(1_054_998) - // Standard Error: 191 - .saturating_add(Weight::from_ref_time(770_225).saturating_mul(r.into())) + // Minimum execution time: 874 nanoseconds. + Weight::from_ref_time(2_611_817) + .saturating_add(Weight::from_proof_size(0)) + // Standard Error: 10_026 + .saturating_add(Weight::from_ref_time(862_138).saturating_mul(r.into())) } } @@ -2081,8 +2221,9 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `109` // Estimated: `0` - // Minimum execution time: 2_305 nanoseconds. - Weight::from_ref_time(2_560_000) + // Minimum execution time: 2_564 nanoseconds. + Weight::from_ref_time(2_722_000) + .saturating_add(Weight::from_proof_size(0)) .saturating_add(RocksDbWeight::get().reads(1_u64)) } /// Storage: Skipped Metadata (r:0 w:0) @@ -2092,10 +2233,11 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `481 + k * (69 ±0)` // Estimated: `0` - // Minimum execution time: 9_311 nanoseconds. - Weight::from_ref_time(5_419_288) - // Standard Error: 562 - .saturating_add(Weight::from_ref_time(911_962).saturating_mul(k.into())) + // Minimum execution time: 10_292 nanoseconds. + Weight::from_ref_time(7_474_496) + .saturating_add(Weight::from_proof_size(0)) + // Standard Error: 901 + .saturating_add(Weight::from_ref_time(956_864).saturating_mul(k.into())) .saturating_add(RocksDbWeight::get().reads(1_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) .saturating_add(RocksDbWeight::get().writes((1_u64).saturating_mul(k.into()))) @@ -2107,10 +2249,11 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `281 + q * (33 ±0)` // Estimated: `0` - // Minimum execution time: 2_288 nanoseconds. - Weight::from_ref_time(9_442_437) - // Standard Error: 2_720 - .saturating_add(Weight::from_ref_time(1_076_950).saturating_mul(q.into())) + // Minimum execution time: 2_620 nanoseconds. + Weight::from_ref_time(10_288_873) + .saturating_add(Weight::from_proof_size(0)) + // Standard Error: 3_073 + .saturating_add(Weight::from_ref_time(1_148_167).saturating_mul(q.into())) .saturating_add(RocksDbWeight::get().reads(1_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } @@ -2123,10 +2266,11 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `270 + c * (1 ±0)` // Estimated: `0` - // Minimum execution time: 27_539 nanoseconds. - Weight::from_ref_time(23_554_889) - // Standard Error: 56 - .saturating_add(Weight::from_ref_time(46_766).saturating_mul(c.into())) + // Minimum execution time: 28_004 nanoseconds. + Weight::from_ref_time(26_706_943) + .saturating_add(Weight::from_proof_size(0)) + // Standard Error: 67 + .saturating_add(Weight::from_ref_time(51_603).saturating_mul(c.into())) .saturating_add(RocksDbWeight::get().reads(1_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } @@ -2145,10 +2289,11 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `771` // Estimated: `0` - // Minimum execution time: 297_710 nanoseconds. - Weight::from_ref_time(307_327_529) - // Standard Error: 18 - .saturating_add(Weight::from_ref_time(29_849).saturating_mul(c.into())) + // Minimum execution time: 295_799 nanoseconds. + Weight::from_ref_time(308_660_753) + .saturating_add(Weight::from_proof_size(0)) + // Standard Error: 20 + .saturating_add(Weight::from_ref_time(32_458).saturating_mul(c.into())) .saturating_add(RocksDbWeight::get().reads(6_u64)) .saturating_add(RocksDbWeight::get().writes(4_u64)) } @@ -2175,14 +2320,15 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `257` // Estimated: `0` - // Minimum execution time: 3_586_223 nanoseconds. - Weight::from_ref_time(561_614_281) - // Standard Error: 274 - .saturating_add(Weight::from_ref_time(87_557).saturating_mul(c.into())) + // Minimum execution time: 3_754_887 nanoseconds. + Weight::from_ref_time(657_695_827) + .saturating_add(Weight::from_proof_size(0)) + // Standard Error: 283 + .saturating_add(Weight::from_ref_time(94_808).saturating_mul(c.into())) // Standard Error: 16 - .saturating_add(Weight::from_ref_time(1_307).saturating_mul(i.into())) + .saturating_add(Weight::from_ref_time(1_357).saturating_mul(i.into())) // Standard Error: 16 - .saturating_add(Weight::from_ref_time(1_721).saturating_mul(s.into())) + .saturating_add(Weight::from_ref_time(1_756).saturating_mul(s.into())) .saturating_add(RocksDbWeight::get().reads(8_u64)) .saturating_add(RocksDbWeight::get().writes(9_u64)) } @@ -2206,12 +2352,13 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `533` // Estimated: `0` - // Minimum execution time: 1_885_437 nanoseconds. - Weight::from_ref_time(199_943_867) + // Minimum execution time: 1_961_131 nanoseconds. + Weight::from_ref_time(208_539_564) + .saturating_add(Weight::from_proof_size(0)) // Standard Error: 8 - .saturating_add(Weight::from_ref_time(1_611).saturating_mul(i.into())) + .saturating_add(Weight::from_ref_time(1_688).saturating_mul(i.into())) // Standard Error: 8 - .saturating_add(Weight::from_ref_time(1_737).saturating_mul(s.into())) + .saturating_add(Weight::from_ref_time(1_802).saturating_mul(s.into())) .saturating_add(RocksDbWeight::get().reads(8_u64)) .saturating_add(RocksDbWeight::get().writes(7_u64)) } @@ -2229,8 +2376,9 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `823` // Estimated: `0` - // Minimum execution time: 150_604 nanoseconds. - Weight::from_ref_time(151_777_000) + // Minimum execution time: 148_138 nanoseconds. + Weight::from_ref_time(148_862_000) + .saturating_add(Weight::from_proof_size(0)) .saturating_add(RocksDbWeight::get().reads(6_u64)) .saturating_add(RocksDbWeight::get().writes(4_u64)) } @@ -2247,10 +2395,11 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `109` // Estimated: `0` - // Minimum execution time: 295_505 nanoseconds. - Weight::from_ref_time(305_609_098) - // Standard Error: 58 - .saturating_add(Weight::from_ref_time(88_676).saturating_mul(c.into())) + // Minimum execution time: 293_444 nanoseconds. + Weight::from_ref_time(293_242_988) + .saturating_add(Weight::from_proof_size(0)) + // Standard Error: 156 + .saturating_add(Weight::from_ref_time(97_476).saturating_mul(c.into())) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().writes(4_u64)) } @@ -2266,8 +2415,9 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `287` // Estimated: `0` - // Minimum execution time: 25_949 nanoseconds. - Weight::from_ref_time(26_316_000) + // Minimum execution time: 26_802 nanoseconds. + Weight::from_ref_time(27_121_000) + .saturating_add(Weight::from_proof_size(0)) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().writes(4_u64)) } @@ -2281,8 +2431,9 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `634` // Estimated: `0` - // Minimum execution time: 29_005 nanoseconds. - Weight::from_ref_time(29_370_000) + // Minimum execution time: 31_079 nanoseconds. + Weight::from_ref_time(31_541_000) + .saturating_add(Weight::from_proof_size(0)) .saturating_add(RocksDbWeight::get().reads(6_u64)) .saturating_add(RocksDbWeight::get().writes(6_u64)) } @@ -2301,10 +2452,11 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `845 + r * (480 ±0)` // Estimated: `0` - // Minimum execution time: 281_880 nanoseconds. - Weight::from_ref_time(289_637_700) - // Standard Error: 22_662 - .saturating_add(Weight::from_ref_time(16_866_274).saturating_mul(r.into())) + // Minimum execution time: 284_877 nanoseconds. + Weight::from_ref_time(290_043_606) + .saturating_add(Weight::from_proof_size(0)) + // Standard Error: 22_094 + .saturating_add(Weight::from_ref_time(18_094_219).saturating_mul(r.into())) .saturating_add(RocksDbWeight::get().reads(6_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) } @@ -2323,10 +2475,11 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `882 + r * (19218 ±0)` // Estimated: `0` - // Minimum execution time: 285_590 nanoseconds. - Weight::from_ref_time(231_277_523) - // Standard Error: 425_084 - .saturating_add(Weight::from_ref_time(192_985_377).saturating_mul(r.into())) + // Minimum execution time: 283_956 nanoseconds. + Weight::from_ref_time(129_690_397) + .saturating_add(Weight::from_proof_size(0)) + // Standard Error: 531_270 + .saturating_add(Weight::from_ref_time(264_082_418).saturating_mul(r.into())) .saturating_add(RocksDbWeight::get().reads(6_u64)) .saturating_add(RocksDbWeight::get().reads((80_u64).saturating_mul(r.into()))) .saturating_add(RocksDbWeight::get().writes(3_u64)) @@ -2346,10 +2499,11 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `889 + r * (19539 ±0)` // Estimated: `0` - // Minimum execution time: 282_966 nanoseconds. - Weight::from_ref_time(236_127_328) - // Standard Error: 405_193 - .saturating_add(Weight::from_ref_time(235_541_377).saturating_mul(r.into())) + // Minimum execution time: 287_077 nanoseconds. + Weight::from_ref_time(148_155_166) + .saturating_add(Weight::from_proof_size(0)) + // Standard Error: 466_496 + .saturating_add(Weight::from_ref_time(306_625_930).saturating_mul(r.into())) .saturating_add(RocksDbWeight::get().reads(6_u64)) .saturating_add(RocksDbWeight::get().reads((80_u64).saturating_mul(r.into()))) .saturating_add(RocksDbWeight::get().writes(3_u64)) @@ -2369,10 +2523,11 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `852 + r * (480 ±0)` // Estimated: `0` - // Minimum execution time: 286_021 nanoseconds. - Weight::from_ref_time(290_200_599) - // Standard Error: 19_224 - .saturating_add(Weight::from_ref_time(20_692_099).saturating_mul(r.into())) + // Minimum execution time: 286_459 nanoseconds. + Weight::from_ref_time(292_826_594) + .saturating_add(Weight::from_proof_size(0)) + // Standard Error: 27_808 + .saturating_add(Weight::from_ref_time(22_401_931).saturating_mul(r.into())) .saturating_add(RocksDbWeight::get().reads(6_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) } @@ -2391,10 +2546,11 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `842 + r * (240 ±0)` // Estimated: `0` - // Minimum execution time: 284_175 nanoseconds. - Weight::from_ref_time(286_665_694) - // Standard Error: 14_104 - .saturating_add(Weight::from_ref_time(11_196_944).saturating_mul(r.into())) + // Minimum execution time: 284_330 nanoseconds. + Weight::from_ref_time(289_069_701) + .saturating_add(Weight::from_proof_size(0)) + // Standard Error: 13_666 + .saturating_add(Weight::from_ref_time(11_157_859).saturating_mul(r.into())) .saturating_add(RocksDbWeight::get().reads(6_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) } @@ -2413,10 +2569,11 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `846 + r * (480 ±0)` // Estimated: `0` - // Minimum execution time: 285_315 nanoseconds. - Weight::from_ref_time(289_734_189) - // Standard Error: 15_980 - .saturating_add(Weight::from_ref_time(16_940_657).saturating_mul(r.into())) + // Minimum execution time: 284_506 nanoseconds. + Weight::from_ref_time(290_155_141) + .saturating_add(Weight::from_proof_size(0)) + // Standard Error: 22_944 + .saturating_add(Weight::from_ref_time(17_992_627).saturating_mul(r.into())) .saturating_add(RocksDbWeight::get().reads(6_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) } @@ -2435,10 +2592,11 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `847 + r * (480 ±0)` // Estimated: `0` - // Minimum execution time: 285_708 nanoseconds. - Weight::from_ref_time(289_872_393) - // Standard Error: 16_551 - .saturating_add(Weight::from_ref_time(16_672_944).saturating_mul(r.into())) + // Minimum execution time: 284_177 nanoseconds. + Weight::from_ref_time(289_314_787) + .saturating_add(Weight::from_proof_size(0)) + // Standard Error: 24_552 + .saturating_add(Weight::from_ref_time(17_754_919).saturating_mul(r.into())) .saturating_add(RocksDbWeight::get().reads(6_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) } @@ -2457,10 +2615,11 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `1017 + r * (480 ±0)` // Estimated: `0` - // Minimum execution time: 285_698 nanoseconds. - Weight::from_ref_time(295_636_093) - // Standard Error: 97_582 - .saturating_add(Weight::from_ref_time(92_891_252).saturating_mul(r.into())) + // Minimum execution time: 284_096 nanoseconds. + Weight::from_ref_time(293_655_130) + .saturating_add(Weight::from_proof_size(0)) + // Standard Error: 56_379 + .saturating_add(Weight::from_ref_time(95_688_575).saturating_mul(r.into())) .saturating_add(RocksDbWeight::get().reads(7_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) } @@ -2479,10 +2638,11 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `856 + r * (480 ±0)` // Estimated: `0` - // Minimum execution time: 282_057 nanoseconds. - Weight::from_ref_time(289_304_621) - // Standard Error: 17_818 - .saturating_add(Weight::from_ref_time(16_725_632).saturating_mul(r.into())) + // Minimum execution time: 285_967 nanoseconds. + Weight::from_ref_time(288_043_137) + .saturating_add(Weight::from_proof_size(0)) + // Standard Error: 83_838 + .saturating_add(Weight::from_ref_time(18_256_709).saturating_mul(r.into())) .saturating_add(RocksDbWeight::get().reads(6_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) } @@ -2501,10 +2661,11 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `854 + r * (480 ±0)` // Estimated: `0` - // Minimum execution time: 282_478 nanoseconds. - Weight::from_ref_time(289_682_366) - // Standard Error: 20_379 - .saturating_add(Weight::from_ref_time(16_517_079).saturating_mul(r.into())) + // Minimum execution time: 284_060 nanoseconds. + Weight::from_ref_time(290_577_678) + .saturating_add(Weight::from_proof_size(0)) + // Standard Error: 26_131 + .saturating_add(Weight::from_ref_time(17_560_562).saturating_mul(r.into())) .saturating_add(RocksDbWeight::get().reads(6_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) } @@ -2523,10 +2684,11 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `851 + r * (480 ±0)` // Estimated: `0` - // Minimum execution time: 283_826 nanoseconds. - Weight::from_ref_time(289_935_300) - // Standard Error: 15_180 - .saturating_add(Weight::from_ref_time(16_268_515).saturating_mul(r.into())) + // Minimum execution time: 286_068 nanoseconds. + Weight::from_ref_time(290_026_137) + .saturating_add(Weight::from_proof_size(0)) + // Standard Error: 34_787 + .saturating_add(Weight::from_ref_time(17_597_404).saturating_mul(r.into())) .saturating_add(RocksDbWeight::get().reads(6_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) } @@ -2545,10 +2707,11 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `842 + r * (480 ±0)` // Estimated: `0` - // Minimum execution time: 285_455 nanoseconds. - Weight::from_ref_time(289_682_526) - // Standard Error: 18_667 - .saturating_add(Weight::from_ref_time(16_502_025).saturating_mul(r.into())) + // Minimum execution time: 285_082 nanoseconds. + Weight::from_ref_time(290_487_103) + .saturating_add(Weight::from_proof_size(0)) + // Standard Error: 22_743 + .saturating_add(Weight::from_ref_time(17_506_771).saturating_mul(r.into())) .saturating_add(RocksDbWeight::get().reads(6_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) } @@ -2569,10 +2732,11 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `919 + r * (800 ±0)` // Estimated: `0` - // Minimum execution time: 286_106 nanoseconds. - Weight::from_ref_time(294_493_680) - // Standard Error: 76_469 - .saturating_add(Weight::from_ref_time(87_055_837).saturating_mul(r.into())) + // Minimum execution time: 285_309 nanoseconds. + Weight::from_ref_time(296_735_365) + .saturating_add(Weight::from_proof_size(0)) + // Standard Error: 49_300 + .saturating_add(Weight::from_ref_time(87_678_979).saturating_mul(r.into())) .saturating_add(RocksDbWeight::get().reads(7_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) } @@ -2591,10 +2755,11 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `809 + r * (320 ±0)` // Estimated: `0` - // Minimum execution time: 137_877 nanoseconds. - Weight::from_ref_time(141_863_027) - // Standard Error: 10_200 - .saturating_add(Weight::from_ref_time(7_925_232).saturating_mul(r.into())) + // Minimum execution time: 136_110 nanoseconds. + Weight::from_ref_time(139_800_659) + .saturating_add(Weight::from_proof_size(0)) + // Standard Error: 9_674 + .saturating_add(Weight::from_ref_time(8_302_834).saturating_mul(r.into())) .saturating_add(RocksDbWeight::get().reads(6_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) } @@ -2613,10 +2778,11 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `844 + r * (480 ±0)` // Estimated: `0` - // Minimum execution time: 282_034 nanoseconds. - Weight::from_ref_time(289_388_799) - // Standard Error: 21_999 - .saturating_add(Weight::from_ref_time(15_039_420).saturating_mul(r.into())) + // Minimum execution time: 285_238 nanoseconds. + Weight::from_ref_time(289_723_839) + .saturating_add(Weight::from_proof_size(0)) + // Standard Error: 19_531 + .saturating_add(Weight::from_ref_time(15_590_085).saturating_mul(r.into())) .saturating_add(RocksDbWeight::get().reads(6_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) } @@ -2635,10 +2801,11 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `1324` // Estimated: `0` - // Minimum execution time: 303_229 nanoseconds. - Weight::from_ref_time(321_863_704) - // Standard Error: 2_754 - .saturating_add(Weight::from_ref_time(9_545_103).saturating_mul(n.into())) + // Minimum execution time: 303_189 nanoseconds. + Weight::from_ref_time(323_374_503) + .saturating_add(Weight::from_proof_size(0)) + // Standard Error: 2_060 + .saturating_add(Weight::from_ref_time(9_799_357).saturating_mul(n.into())) .saturating_add(RocksDbWeight::get().reads(6_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) } @@ -2657,10 +2824,11 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `832 + r * (45 ±0)` // Estimated: `0` - // Minimum execution time: 278_824 nanoseconds. - Weight::from_ref_time(285_019_861) - // Standard Error: 101_646 - .saturating_add(Weight::from_ref_time(1_757_938).saturating_mul(r.into())) + // Minimum execution time: 280_736 nanoseconds. + Weight::from_ref_time(285_027_920) + .saturating_add(Weight::from_proof_size(0)) + // Standard Error: 222_526 + .saturating_add(Weight::from_ref_time(1_814_579).saturating_mul(r.into())) .saturating_add(RocksDbWeight::get().reads(6_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) } @@ -2679,10 +2847,11 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `842` // Estimated: `0` - // Minimum execution time: 286_316 nanoseconds. - Weight::from_ref_time(287_206_936) - // Standard Error: 589 - .saturating_add(Weight::from_ref_time(186_684).saturating_mul(n.into())) + // Minimum execution time: 285_559 nanoseconds. + Weight::from_ref_time(290_522_234) + .saturating_add(Weight::from_proof_size(0)) + // Standard Error: 2_578 + .saturating_add(Weight::from_ref_time(195_134).saturating_mul(n.into())) .saturating_add(RocksDbWeight::get().reads(6_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) } @@ -2705,10 +2874,11 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `874 + r * (280 ±0)` // Estimated: `0` - // Minimum execution time: 282_428 nanoseconds. - Weight::from_ref_time(287_101_148) - // Standard Error: 145_605 - .saturating_add(Weight::from_ref_time(58_079_551).saturating_mul(r.into())) + // Minimum execution time: 282_070 nanoseconds. + Weight::from_ref_time(286_466_489) + .saturating_add(Weight::from_proof_size(0)) + // Standard Error: 238_285 + .saturating_add(Weight::from_ref_time(58_542_610).saturating_mul(r.into())) .saturating_add(RocksDbWeight::get().reads(6_u64)) .saturating_add(RocksDbWeight::get().reads((5_u64).saturating_mul(r.into()))) .saturating_add(RocksDbWeight::get().writes(3_u64)) @@ -2731,10 +2901,11 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `889 + r * (800 ±0)` // Estimated: `0` - // Minimum execution time: 281_516 nanoseconds. - Weight::from_ref_time(292_759_183) - // Standard Error: 152_698 - .saturating_add(Weight::from_ref_time(112_729_555).saturating_mul(r.into())) + // Minimum execution time: 284_147 nanoseconds. + Weight::from_ref_time(295_373_881) + .saturating_add(Weight::from_proof_size(0)) + // Standard Error: 64_682 + .saturating_add(Weight::from_ref_time(114_198_680).saturating_mul(r.into())) .saturating_add(RocksDbWeight::get().reads(7_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) } @@ -2753,10 +2924,11 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `842 + r * (800 ±0)` // Estimated: `0` - // Minimum execution time: 280_100 nanoseconds. - Weight::from_ref_time(299_862_082) - // Standard Error: 95_658 - .saturating_add(Weight::from_ref_time(234_211_246).saturating_mul(r.into())) + // Minimum execution time: 282_681 nanoseconds. + Weight::from_ref_time(290_974_649) + .saturating_add(Weight::from_proof_size(0)) + // Standard Error: 77_850 + .saturating_add(Weight::from_ref_time(233_273_622).saturating_mul(r.into())) .saturating_add(RocksDbWeight::get().reads(6_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) } @@ -2776,12 +2948,13 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `1778 + t * (2608 ±0) + n * (8 ±0)` // Estimated: `0` - // Minimum execution time: 1_197_721 nanoseconds. - Weight::from_ref_time(508_692_255) - // Standard Error: 538_596 - .saturating_add(Weight::from_ref_time(174_792_656).saturating_mul(t.into())) - // Standard Error: 147_924 - .saturating_add(Weight::from_ref_time(67_443_118).saturating_mul(n.into())) + // Minimum execution time: 1_226_868 nanoseconds. + Weight::from_ref_time(517_031_747) + .saturating_add(Weight::from_proof_size(0)) + // Standard Error: 342_786 + .saturating_add(Weight::from_ref_time(183_829_556).saturating_mul(t.into())) + // Standard Error: 94_145 + .saturating_add(Weight::from_ref_time(70_675_099).saturating_mul(n.into())) .saturating_add(RocksDbWeight::get().reads(6_u64)) .saturating_add(RocksDbWeight::get().reads((80_u64).saturating_mul(t.into()))) .saturating_add(RocksDbWeight::get().writes(3_u64)) @@ -2800,12 +2973,36 @@ impl WeightInfo for () { /// The range of component `r` is `[0, 20]`. fn seal_debug_message(r: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `841 + r * (800 ±0)` + // Measured: `841 + r * (560 ±0)` // Estimated: `0` - // Minimum execution time: 149_687 nanoseconds. - Weight::from_ref_time(153_589_818) - // Standard Error: 13_361 - .saturating_add(Weight::from_ref_time(13_379_131).saturating_mul(r.into())) + // Minimum execution time: 143_026 nanoseconds. + Weight::from_ref_time(147_158_038) + .saturating_add(Weight::from_proof_size(0)) + // Standard Error: 16_662 + .saturating_add(Weight::from_ref_time(14_990_989).saturating_mul(r.into())) + .saturating_add(RocksDbWeight::get().reads(6_u64)) + .saturating_add(RocksDbWeight::get().writes(3_u64)) + } + /// Storage: System Account (r:1 w:0) + /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) + /// Storage: Contracts ContractInfoOf (r:1 w:1) + /// Proof: Contracts ContractInfoOf (max_values: None, max_size: Some(258), added: 2733, mode: MaxEncodedLen) + /// Storage: Contracts CodeStorage (r:1 w:0) + /// Proof: Contracts CodeStorage (max_values: None, max_size: Some(126001), added: 128476, mode: MaxEncodedLen) + /// Storage: Timestamp Now (r:1 w:0) + /// Proof: Timestamp Now (max_values: Some(1), max_size: Some(8), added: 503, mode: MaxEncodedLen) + /// Storage: System EventTopics (r:2 w:2) + /// Proof Skipped: System EventTopics (max_values: None, max_size: None, mode: Measured) + /// The range of component `i` is `[0, 1024]`. + fn seal_debug_message_per_kb(i: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `125792` + // Estimated: `265059` + // Minimum execution time: 412_112 nanoseconds. + Weight::from_ref_time(415_624_011) + .saturating_add(Weight::from_proof_size(265059)) + // Standard Error: 1_190 + .saturating_add(Weight::from_ref_time(797_964).saturating_mul(i.into())) .saturating_add(RocksDbWeight::get().reads(6_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) } @@ -2816,10 +3013,11 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `883 + r * (23417 ±0)` // Estimated: `0` - // Minimum execution time: 281_920 nanoseconds. - Weight::from_ref_time(242_057_723) - // Standard Error: 464_911 - .saturating_add(Weight::from_ref_time(404_673_309).saturating_mul(r.into())) + // Minimum execution time: 286_118 nanoseconds. + Weight::from_ref_time(195_812_951) + .saturating_add(Weight::from_proof_size(0)) + // Standard Error: 858_501 + .saturating_add(Weight::from_ref_time(485_357_331).saturating_mul(r.into())) .saturating_add(RocksDbWeight::get().reads(6_u64)) .saturating_add(RocksDbWeight::get().reads((80_u64).saturating_mul(r.into()))) .saturating_add(RocksDbWeight::get().writes(3_u64)) @@ -2832,10 +3030,11 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `12583 + n * (11969 ±0)` // Estimated: `0` - // Minimum execution time: 423_923 nanoseconds. - Weight::from_ref_time(573_806_626) - // Standard Error: 1_371_107 - .saturating_add(Weight::from_ref_time(85_963_445).saturating_mul(n.into())) + // Minimum execution time: 433_181 nanoseconds. + Weight::from_ref_time(618_575_096) + .saturating_add(Weight::from_proof_size(0)) + // Standard Error: 1_696_126 + .saturating_add(Weight::from_ref_time(96_213_633).saturating_mul(n.into())) .saturating_add(RocksDbWeight::get().reads(52_u64)) .saturating_add(RocksDbWeight::get().reads((7_u64).saturating_mul(n.into()))) .saturating_add(RocksDbWeight::get().writes(50_u64)) @@ -2848,10 +3047,11 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `15138 + n * (175775 ±0)` // Estimated: `0` - // Minimum execution time: 424_048 nanoseconds. - Weight::from_ref_time(542_298_050) - // Standard Error: 1_092_010 - .saturating_add(Weight::from_ref_time(60_111_206).saturating_mul(n.into())) + // Minimum execution time: 433_354 nanoseconds. + Weight::from_ref_time(597_131_349) + .saturating_add(Weight::from_proof_size(0)) + // Standard Error: 1_509_003 + .saturating_add(Weight::from_ref_time(64_872_907).saturating_mul(n.into())) .saturating_add(RocksDbWeight::get().reads(51_u64)) .saturating_add(RocksDbWeight::get().reads((7_u64).saturating_mul(n.into()))) .saturating_add(RocksDbWeight::get().writes(49_u64)) @@ -2864,10 +3064,11 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `876 + r * (23098 ±0)` // Estimated: `0` - // Minimum execution time: 285_714 nanoseconds. - Weight::from_ref_time(245_068_941) - // Standard Error: 417_796 - .saturating_add(Weight::from_ref_time(394_288_572).saturating_mul(r.into())) + // Minimum execution time: 286_788 nanoseconds. + Weight::from_ref_time(207_787_332) + .saturating_add(Weight::from_proof_size(0)) + // Standard Error: 778_284 + .saturating_add(Weight::from_ref_time(474_879_317).saturating_mul(r.into())) .saturating_add(RocksDbWeight::get().reads(6_u64)) .saturating_add(RocksDbWeight::get().reads((80_u64).saturating_mul(r.into()))) .saturating_add(RocksDbWeight::get().writes(3_u64)) @@ -2880,10 +3081,11 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `14863 + n * (175768 ±0)` // Estimated: `0` - // Minimum execution time: 385_278 nanoseconds. - Weight::from_ref_time(522_656_525) - // Standard Error: 1_259_587 - .saturating_add(Weight::from_ref_time(62_799_142).saturating_mul(n.into())) + // Minimum execution time: 397_414 nanoseconds. + Weight::from_ref_time(573_575_029) + .saturating_add(Weight::from_proof_size(0)) + // Standard Error: 1_630_691 + .saturating_add(Weight::from_ref_time(67_888_226).saturating_mul(n.into())) .saturating_add(RocksDbWeight::get().reads(51_u64)) .saturating_add(RocksDbWeight::get().reads((7_u64).saturating_mul(n.into()))) .saturating_add(RocksDbWeight::get().writes(48_u64)) @@ -2896,10 +3098,11 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `878 + r * (23740 ±0)` // Estimated: `0` - // Minimum execution time: 282_513 nanoseconds. - Weight::from_ref_time(256_242_753) - // Standard Error: 362_571 - .saturating_add(Weight::from_ref_time(317_951_687).saturating_mul(r.into())) + // Minimum execution time: 286_098 nanoseconds. + Weight::from_ref_time(223_637_903) + .saturating_add(Weight::from_proof_size(0)) + // Standard Error: 669_294 + .saturating_add(Weight::from_ref_time(387_577_627).saturating_mul(r.into())) .saturating_add(RocksDbWeight::get().reads(6_u64)) .saturating_add(RocksDbWeight::get().reads((80_u64).saturating_mul(r.into()))) .saturating_add(RocksDbWeight::get().writes(3_u64)) @@ -2911,10 +3114,11 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `15469 + n * (175775 ±0)` // Estimated: `0` - // Minimum execution time: 370_576 nanoseconds. - Weight::from_ref_time(487_764_999) - // Standard Error: 1_073_165 - .saturating_add(Weight::from_ref_time(147_588_190).saturating_mul(n.into())) + // Minimum execution time: 373_335 nanoseconds. + Weight::from_ref_time(531_442_564) + .saturating_add(Weight::from_proof_size(0)) + // Standard Error: 1_474_336 + .saturating_add(Weight::from_ref_time(154_469_598).saturating_mul(n.into())) .saturating_add(RocksDbWeight::get().reads(51_u64)) .saturating_add(RocksDbWeight::get().reads((7_u64).saturating_mul(n.into()))) .saturating_add(RocksDbWeight::get().writes(3_u64)) @@ -2926,10 +3130,11 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `871 + r * (23100 ±0)` // Estimated: `0` - // Minimum execution time: 285_917 nanoseconds. - Weight::from_ref_time(259_066_807) - // Standard Error: 340_183 - .saturating_add(Weight::from_ref_time(306_291_698).saturating_mul(r.into())) + // Minimum execution time: 284_183 nanoseconds. + Weight::from_ref_time(223_325_689) + .saturating_add(Weight::from_proof_size(0)) + // Standard Error: 644_587 + .saturating_add(Weight::from_ref_time(373_628_582).saturating_mul(r.into())) .saturating_add(RocksDbWeight::get().reads(6_u64)) .saturating_add(RocksDbWeight::get().reads((80_u64).saturating_mul(r.into()))) .saturating_add(RocksDbWeight::get().writes(3_u64)) @@ -2941,10 +3146,11 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `14814 + n * (175782 ±0)` // Estimated: `0` - // Minimum execution time: 366_225 nanoseconds. - Weight::from_ref_time(470_470_223) - // Standard Error: 953_976 - .saturating_add(Weight::from_ref_time(57_748_742).saturating_mul(n.into())) + // Minimum execution time: 368_310 nanoseconds. + Weight::from_ref_time(512_814_023) + .saturating_add(Weight::from_proof_size(0)) + // Standard Error: 1_337_352 + .saturating_add(Weight::from_ref_time(62_871_539).saturating_mul(n.into())) .saturating_add(RocksDbWeight::get().reads(51_u64)) .saturating_add(RocksDbWeight::get().reads((7_u64).saturating_mul(n.into()))) .saturating_add(RocksDbWeight::get().writes(3_u64)) @@ -2956,10 +3162,11 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `879 + r * (23740 ±0)` // Estimated: `0` - // Minimum execution time: 286_867 nanoseconds. - Weight::from_ref_time(244_403_664) - // Standard Error: 435_431 - .saturating_add(Weight::from_ref_time(409_282_991).saturating_mul(r.into())) + // Minimum execution time: 287_096 nanoseconds. + Weight::from_ref_time(204_878_281) + .saturating_add(Weight::from_proof_size(0)) + // Standard Error: 833_763 + .saturating_add(Weight::from_ref_time(483_927_706).saturating_mul(r.into())) .saturating_add(RocksDbWeight::get().reads(6_u64)) .saturating_add(RocksDbWeight::get().reads((80_u64).saturating_mul(r.into()))) .saturating_add(RocksDbWeight::get().writes(3_u64)) @@ -2972,10 +3179,11 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `15470 + n * (175775 ±0)` // Estimated: `0` - // Minimum execution time: 393_392 nanoseconds. - Weight::from_ref_time(540_938_487) - // Standard Error: 1_361_411 - .saturating_add(Weight::from_ref_time(153_456_560).saturating_mul(n.into())) + // Minimum execution time: 399_318 nanoseconds. + Weight::from_ref_time(586_658_466) + .saturating_add(Weight::from_proof_size(0)) + // Standard Error: 1_734_497 + .saturating_add(Weight::from_ref_time(161_047_970).saturating_mul(n.into())) .saturating_add(RocksDbWeight::get().reads(51_u64)) .saturating_add(RocksDbWeight::get().reads((7_u64).saturating_mul(n.into()))) .saturating_add(RocksDbWeight::get().writes(48_u64)) @@ -2996,10 +3204,11 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `1393 + r * (3602 ±0)` // Estimated: `0` - // Minimum execution time: 286_766 nanoseconds. - Weight::from_ref_time(221_458_774) - // Standard Error: 714_182 - .saturating_add(Weight::from_ref_time(1_402_610_222).saturating_mul(r.into())) + // Minimum execution time: 285_460 nanoseconds. + Weight::from_ref_time(227_848_079) + .saturating_add(Weight::from_proof_size(0)) + // Standard Error: 717_899 + .saturating_add(Weight::from_ref_time(1_434_112_130).saturating_mul(r.into())) .saturating_add(RocksDbWeight::get().reads(7_u64)) .saturating_add(RocksDbWeight::get().reads((80_u64).saturating_mul(r.into()))) .saturating_add(RocksDbWeight::get().writes(4_u64)) @@ -3020,10 +3229,11 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `1551 + r * (20511 ±0)` // Estimated: `0` - // Minimum execution time: 287_158 nanoseconds. - Weight::from_ref_time(288_377_000) - // Standard Error: 6_108_706 - .saturating_add(Weight::from_ref_time(21_691_098_517).saturating_mul(r.into())) + // Minimum execution time: 286_947 nanoseconds. + Weight::from_ref_time(288_518_000) + .saturating_add(Weight::from_proof_size(0)) + // Standard Error: 8_492_544 + .saturating_add(Weight::from_ref_time(21_606_377_662).saturating_mul(r.into())) .saturating_add(RocksDbWeight::get().reads(7_u64)) .saturating_add(RocksDbWeight::get().reads((160_u64).saturating_mul(r.into()))) .saturating_add(RocksDbWeight::get().writes(3_u64)) @@ -3044,10 +3254,11 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `0 + r * (71670 ±0)` // Estimated: `0` - // Minimum execution time: 287_380 nanoseconds. - Weight::from_ref_time(288_241_000) - // Standard Error: 7_007_658 - .saturating_add(Weight::from_ref_time(21_428_850_764).saturating_mul(r.into())) + // Minimum execution time: 285_196 nanoseconds. + Weight::from_ref_time(287_958_000) + .saturating_add(Weight::from_proof_size(0)) + // Standard Error: 10_345_109 + .saturating_add(Weight::from_ref_time(21_395_365_662).saturating_mul(r.into())) .saturating_add(RocksDbWeight::get().reads(6_u64)) .saturating_add(RocksDbWeight::get().reads((150_u64).saturating_mul(r.into()))) .saturating_add(RocksDbWeight::get().writes(3_u64)) @@ -3069,12 +3280,13 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `21611 + t * (15369 ±0)` // Estimated: `0` - // Minimum execution time: 10_644_986 nanoseconds. - Weight::from_ref_time(9_596_635_640) - // Standard Error: 6_393_384 - .saturating_add(Weight::from_ref_time(1_304_764_528).saturating_mul(t.into())) - // Standard Error: 9_586 - .saturating_add(Weight::from_ref_time(9_663_819).saturating_mul(c.into())) + // Minimum execution time: 10_540_614 nanoseconds. + Weight::from_ref_time(9_281_766_912) + .saturating_add(Weight::from_proof_size(0)) + // Standard Error: 5_409_378 + .saturating_add(Weight::from_ref_time(1_449_975_070).saturating_mul(t.into())) + // Standard Error: 8_111 + .saturating_add(Weight::from_ref_time(10_275_108).saturating_mul(c.into())) .saturating_add(RocksDbWeight::get().reads(167_u64)) .saturating_add(RocksDbWeight::get().reads((81_u64).saturating_mul(t.into()))) .saturating_add(RocksDbWeight::get().writes(163_u64)) @@ -3099,10 +3311,11 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `1613 + r * (25576 ±0)` // Estimated: `0` - // Minimum execution time: 284_948 nanoseconds. - Weight::from_ref_time(289_276_000) - // Standard Error: 18_674_951 - .saturating_add(Weight::from_ref_time(27_090_355_673).saturating_mul(r.into())) + // Minimum execution time: 285_643 nanoseconds. + Weight::from_ref_time(287_472_000) + .saturating_add(Weight::from_proof_size(0)) + // Standard Error: 25_921_947 + .saturating_add(Weight::from_ref_time(27_745_815_511).saturating_mul(r.into())) .saturating_add(RocksDbWeight::get().reads(8_u64)) .saturating_add(RocksDbWeight::get().reads((400_u64).saturating_mul(r.into()))) .saturating_add(RocksDbWeight::get().writes(5_u64)) @@ -3129,14 +3342,13 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `5666 + t * (17 ±0)` // Estimated: `0` - // Minimum execution time: 127_857_642 nanoseconds. - Weight::from_ref_time(11_399_054_049) - // Standard Error: 95_033_651 - .saturating_add(Weight::from_ref_time(434_246_236).saturating_mul(t.into())) - // Standard Error: 154_973 - .saturating_add(Weight::from_ref_time(121_130_672).saturating_mul(i.into())) - // Standard Error: 154_973 - .saturating_add(Weight::from_ref_time(121_554_853).saturating_mul(s.into())) + // Minimum execution time: 129_710_453 nanoseconds. + Weight::from_ref_time(14_347_603_160) + .saturating_add(Weight::from_proof_size(0)) + // Standard Error: 194_692 + .saturating_add(Weight::from_ref_time(128_837_066).saturating_mul(i.into())) + // Standard Error: 194_692 + .saturating_add(Weight::from_ref_time(127_568_555).saturating_mul(s.into())) .saturating_add(RocksDbWeight::get().reads(249_u64)) .saturating_add(RocksDbWeight::get().reads((1_u64).saturating_mul(t.into()))) .saturating_add(RocksDbWeight::get().writes(247_u64)) @@ -3157,10 +3369,11 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `839 + r * (642 ±0)` // Estimated: `0` - // Minimum execution time: 280_949 nanoseconds. - Weight::from_ref_time(286_538_475) - // Standard Error: 124_866 - .saturating_add(Weight::from_ref_time(42_531_824).saturating_mul(r.into())) + // Minimum execution time: 282_059 nanoseconds. + Weight::from_ref_time(287_215_177) + .saturating_add(Weight::from_proof_size(0)) + // Standard Error: 273_484 + .saturating_add(Weight::from_ref_time(44_941_522).saturating_mul(r.into())) .saturating_add(RocksDbWeight::get().reads(6_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) } @@ -3179,10 +3392,11 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `1641` // Estimated: `0` - // Minimum execution time: 328_414 nanoseconds. - Weight::from_ref_time(329_293_000) - // Standard Error: 50_816 - .saturating_add(Weight::from_ref_time(318_312_506).saturating_mul(n.into())) + // Minimum execution time: 330_822 nanoseconds. + Weight::from_ref_time(333_012_000) + .saturating_add(Weight::from_proof_size(0)) + // Standard Error: 53_200 + .saturating_add(Weight::from_ref_time(327_144_474).saturating_mul(n.into())) .saturating_add(RocksDbWeight::get().reads(6_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) } @@ -3201,10 +3415,11 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `841 + r * (642 ±0)` // Estimated: `0` - // Minimum execution time: 282_208 nanoseconds. - Weight::from_ref_time(286_848_187) - // Standard Error: 106_214 - .saturating_add(Weight::from_ref_time(56_342_512).saturating_mul(r.into())) + // Minimum execution time: 282_859 nanoseconds. + Weight::from_ref_time(286_293_402) + .saturating_add(Weight::from_proof_size(0)) + // Standard Error: 187_930 + .saturating_add(Weight::from_ref_time(57_415_297).saturating_mul(r.into())) .saturating_add(RocksDbWeight::get().reads(6_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) } @@ -3223,10 +3438,11 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `1643` // Estimated: `0` - // Minimum execution time: 342_478 nanoseconds. - Weight::from_ref_time(342_947_000) - // Standard Error: 60_809 - .saturating_add(Weight::from_ref_time(255_492_149).saturating_mul(n.into())) + // Minimum execution time: 342_221 nanoseconds. + Weight::from_ref_time(343_250_000) + .saturating_add(Weight::from_proof_size(0)) + // Standard Error: 67_989 + .saturating_add(Weight::from_ref_time(261_498_077).saturating_mul(n.into())) .saturating_add(RocksDbWeight::get().reads(6_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) } @@ -3245,10 +3461,11 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `841 + r * (642 ±0)` // Estimated: `0` - // Minimum execution time: 279_059 nanoseconds. - Weight::from_ref_time(285_413_659) - // Standard Error: 123_081 - .saturating_add(Weight::from_ref_time(33_154_840).saturating_mul(r.into())) + // Minimum execution time: 282_158 nanoseconds. + Weight::from_ref_time(285_783_279) + .saturating_add(Weight::from_proof_size(0)) + // Standard Error: 200_413 + .saturating_add(Weight::from_ref_time(35_099_520).saturating_mul(r.into())) .saturating_add(RocksDbWeight::get().reads(6_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) } @@ -3267,10 +3484,11 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `1643` // Estimated: `0` - // Minimum execution time: 317_518 nanoseconds. - Weight::from_ref_time(318_178_000) - // Standard Error: 60_074 - .saturating_add(Weight::from_ref_time(99_403_819).saturating_mul(n.into())) + // Minimum execution time: 317_376 nanoseconds. + Weight::from_ref_time(319_441_000) + .saturating_add(Weight::from_proof_size(0)) + // Standard Error: 61_325 + .saturating_add(Weight::from_ref_time(101_080_739).saturating_mul(n.into())) .saturating_add(RocksDbWeight::get().reads(6_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) } @@ -3289,10 +3507,11 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `841 + r * (679 ±0)` // Estimated: `0` - // Minimum execution time: 280_145 nanoseconds. - Weight::from_ref_time(285_483_032) - // Standard Error: 106_113 - .saturating_add(Weight::from_ref_time(33_475_067).saturating_mul(r.into())) + // Minimum execution time: 280_403 nanoseconds. + Weight::from_ref_time(282_592_267) + .saturating_add(Weight::from_proof_size(0)) + // Standard Error: 237_832 + .saturating_add(Weight::from_ref_time(37_056_632).saturating_mul(r.into())) .saturating_add(RocksDbWeight::get().reads(6_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) } @@ -3311,10 +3530,11 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `1643` // Estimated: `0` - // Minimum execution time: 318_141 nanoseconds. - Weight::from_ref_time(318_699_000) - // Standard Error: 55_136 - .saturating_add(Weight::from_ref_time(99_275_434).saturating_mul(n.into())) + // Minimum execution time: 315_482 nanoseconds. + Weight::from_ref_time(316_160_000) + .saturating_add(Weight::from_proof_size(0)) + // Standard Error: 40_926 + .saturating_add(Weight::from_ref_time(100_317_235).saturating_mul(n.into())) .saturating_add(RocksDbWeight::get().reads(6_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) } @@ -3333,10 +3553,11 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `885 + r * (6083 ±0)` // Estimated: `0` - // Minimum execution time: 282_474 nanoseconds. - Weight::from_ref_time(288_078_802) - // Standard Error: 302_968 - .saturating_add(Weight::from_ref_time(2_944_967_597).saturating_mul(r.into())) + // Minimum execution time: 276_155 nanoseconds. + Weight::from_ref_time(278_531_728) + .saturating_add(Weight::from_proof_size(0)) + // Standard Error: 510_501 + .saturating_add(Weight::from_ref_time(3_056_236_671).saturating_mul(r.into())) .saturating_add(RocksDbWeight::get().reads(6_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) } @@ -3355,10 +3576,11 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `854 + r * (3362 ±0)` // Estimated: `0` - // Minimum execution time: 281_514 nanoseconds. - Weight::from_ref_time(287_458_651) - // Standard Error: 146_715 - .saturating_add(Weight::from_ref_time(731_367_948).saturating_mul(r.into())) + // Minimum execution time: 276_204 nanoseconds. + Weight::from_ref_time(278_114_283) + .saturating_add(Weight::from_proof_size(0)) + // Standard Error: 217_508 + .saturating_add(Weight::from_ref_time(737_577_816).saturating_mul(r.into())) .saturating_add(RocksDbWeight::get().reads(6_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) } @@ -3379,10 +3601,11 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `0 + r * (79300 ±0)` // Estimated: `0` - // Minimum execution time: 282_591 nanoseconds. - Weight::from_ref_time(286_842_000) - // Standard Error: 2_645_254 - .saturating_add(Weight::from_ref_time(1_394_535_676).saturating_mul(r.into())) + // Minimum execution time: 276_330 nanoseconds. + Weight::from_ref_time(277_573_000) + .saturating_add(Weight::from_proof_size(0)) + // Standard Error: 3_652_635 + .saturating_add(Weight::from_ref_time(1_547_361_882).saturating_mul(r.into())) .saturating_add(RocksDbWeight::get().reads(6_u64)) .saturating_add(RocksDbWeight::get().reads((225_u64).saturating_mul(r.into()))) .saturating_add(RocksDbWeight::get().writes(3_u64)) @@ -3403,10 +3626,11 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `837 + r * (240 ±0)` // Estimated: `0` - // Minimum execution time: 286_631 nanoseconds. - Weight::from_ref_time(288_787_650) - // Standard Error: 29_802 - .saturating_add(Weight::from_ref_time(11_115_811).saturating_mul(r.into())) + // Minimum execution time: 276_228 nanoseconds. + Weight::from_ref_time(281_135_551) + .saturating_add(Weight::from_proof_size(0)) + // Standard Error: 18_737 + .saturating_add(Weight::from_ref_time(10_998_953).saturating_mul(r.into())) .saturating_add(RocksDbWeight::get().reads(6_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) } @@ -3425,10 +3649,11 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `2056 + r * (3153 ±0)` // Estimated: `0` - // Minimum execution time: 287_775 nanoseconds. - Weight::from_ref_time(319_806_123) - // Standard Error: 111_808 - .saturating_add(Weight::from_ref_time(17_641_181).saturating_mul(r.into())) + // Minimum execution time: 278_157 nanoseconds. + Weight::from_ref_time(300_739_078) + .saturating_add(Weight::from_proof_size(0)) + // Standard Error: 72_695 + .saturating_add(Weight::from_ref_time(18_499_645).saturating_mul(r.into())) .saturating_add(RocksDbWeight::get().reads(6_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) } @@ -3449,10 +3674,11 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `840 + r * (240 ±0)` // Estimated: `0` - // Minimum execution time: 285_077 nanoseconds. - Weight::from_ref_time(289_980_475) - // Standard Error: 14_535 - .saturating_add(Weight::from_ref_time(9_295_346).saturating_mul(r.into())) + // Minimum execution time: 275_532 nanoseconds. + Weight::from_ref_time(281_299_677) + .saturating_add(Weight::from_proof_size(0)) + // Standard Error: 21_719 + .saturating_add(Weight::from_ref_time(9_513_444).saturating_mul(r.into())) .saturating_add(RocksDbWeight::get().reads(7_u64)) .saturating_add(RocksDbWeight::get().writes(4_u64)) } @@ -3461,519 +3687,571 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 777 nanoseconds. - Weight::from_ref_time(1_014_498) - // Standard Error: 154 - .saturating_add(Weight::from_ref_time(405_551).saturating_mul(r.into())) + // Minimum execution time: 856 nanoseconds. + Weight::from_ref_time(1_054_710) + .saturating_add(Weight::from_proof_size(0)) + // Standard Error: 273 + .saturating_add(Weight::from_ref_time(411_548).saturating_mul(r.into())) } /// The range of component `r` is `[0, 50]`. fn instr_i64load(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 862 nanoseconds. - Weight::from_ref_time(1_345_826) - // Standard Error: 457 - .saturating_add(Weight::from_ref_time(1_033_909).saturating_mul(r.into())) + // Minimum execution time: 980 nanoseconds. + Weight::from_ref_time(1_463_182) + .saturating_add(Weight::from_proof_size(0)) + // Standard Error: 415 + .saturating_add(Weight::from_ref_time(1_063_436).saturating_mul(r.into())) } /// The range of component `r` is `[0, 50]`. fn instr_i64store(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 892 nanoseconds. - Weight::from_ref_time(1_233_601) - // Standard Error: 341 - .saturating_add(Weight::from_ref_time(885_275).saturating_mul(r.into())) + // Minimum execution time: 991 nanoseconds. + Weight::from_ref_time(1_521_750) + .saturating_add(Weight::from_proof_size(0)) + // Standard Error: 8_499 + .saturating_add(Weight::from_ref_time(997_972).saturating_mul(r.into())) } /// The range of component `r` is `[0, 50]`. fn instr_select(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 771 nanoseconds. - Weight::from_ref_time(1_099_906) - // Standard Error: 261 - .saturating_add(Weight::from_ref_time(1_092_031).saturating_mul(r.into())) + // Minimum execution time: 833 nanoseconds. + Weight::from_ref_time(1_152_048) + .saturating_add(Weight::from_proof_size(0)) + // Standard Error: 392 + .saturating_add(Weight::from_ref_time(1_147_506).saturating_mul(r.into())) } /// The range of component `r` is `[0, 50]`. fn instr_if(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 785 nanoseconds. - Weight::from_ref_time(929_328) - // Standard Error: 333 - .saturating_add(Weight::from_ref_time(1_374_749).saturating_mul(r.into())) + // Minimum execution time: 866 nanoseconds. + Weight::from_ref_time(1_154_637) + .saturating_add(Weight::from_proof_size(0)) + // Standard Error: 5_131 + .saturating_add(Weight::from_ref_time(1_320_580).saturating_mul(r.into())) } /// The range of component `r` is `[0, 50]`. fn instr_br(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 772 nanoseconds. - Weight::from_ref_time(979_702) - // Standard Error: 351 - .saturating_add(Weight::from_ref_time(621_385).saturating_mul(r.into())) + // Minimum execution time: 849 nanoseconds. + Weight::from_ref_time(1_073_940) + .saturating_add(Weight::from_proof_size(0)) + // Standard Error: 639 + .saturating_add(Weight::from_ref_time(642_885).saturating_mul(r.into())) } /// The range of component `r` is `[0, 50]`. fn instr_br_if(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 766 nanoseconds. - Weight::from_ref_time(1_303_783) - // Standard Error: 1_556 - .saturating_add(Weight::from_ref_time(841_842).saturating_mul(r.into())) + // Minimum execution time: 855 nanoseconds. + Weight::from_ref_time(728_631) + .saturating_add(Weight::from_proof_size(0)) + // Standard Error: 1_730 + .saturating_add(Weight::from_ref_time(979_839).saturating_mul(r.into())) } /// The range of component `r` is `[0, 50]`. fn instr_br_table(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 792 nanoseconds. - Weight::from_ref_time(1_093_901) - // Standard Error: 1_383 - .saturating_add(Weight::from_ref_time(1_145_435).saturating_mul(r.into())) + // Minimum execution time: 852 nanoseconds. + Weight::from_ref_time(658_935) + .saturating_add(Weight::from_proof_size(0)) + // Standard Error: 2_373 + .saturating_add(Weight::from_ref_time(1_172_585).saturating_mul(r.into())) } /// The range of component `e` is `[1, 256]`. fn instr_br_table_per_entry(e: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 2_526 nanoseconds. - Weight::from_ref_time(2_872_561) - // Standard Error: 60 - .saturating_add(Weight::from_ref_time(4_365).saturating_mul(e.into())) + // Minimum execution time: 2_577 nanoseconds. + Weight::from_ref_time(2_872_555) + .saturating_add(Weight::from_proof_size(0)) + // Standard Error: 70 + .saturating_add(Weight::from_ref_time(4_237).saturating_mul(e.into())) } /// The range of component `r` is `[0, 50]`. fn instr_call(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 834 nanoseconds. - Weight::from_ref_time(1_431_876) - // Standard Error: 1_448 - .saturating_add(Weight::from_ref_time(2_268_715).saturating_mul(r.into())) + // Minimum execution time: 833 nanoseconds. + Weight::from_ref_time(874_000) + .saturating_add(Weight::from_proof_size(0)) + // Standard Error: 30_863 + .saturating_add(Weight::from_ref_time(2_591_963).saturating_mul(r.into())) } /// The range of component `r` is `[0, 50]`. fn instr_call_indirect(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 920 nanoseconds. - Weight::from_ref_time(2_167_004) - // Standard Error: 2_060 - .saturating_add(Weight::from_ref_time(2_921_443).saturating_mul(r.into())) + // Minimum execution time: 992 nanoseconds. + Weight::from_ref_time(2_772_709) + .saturating_add(Weight::from_proof_size(0)) + // Standard Error: 3_822 + .saturating_add(Weight::from_ref_time(3_070_825).saturating_mul(r.into())) } /// The range of component `p` is `[0, 128]`. fn instr_call_indirect_per_param(p: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 4_624 nanoseconds. - Weight::from_ref_time(5_534_325) - // Standard Error: 326 - .saturating_add(Weight::from_ref_time(184_307).saturating_mul(p.into())) + // Minimum execution time: 4_766 nanoseconds. + Weight::from_ref_time(5_559_951) + .saturating_add(Weight::from_proof_size(0)) + // Standard Error: 315 + .saturating_add(Weight::from_ref_time(227_249).saturating_mul(p.into())) } /// The range of component `l` is `[0, 1024]`. fn instr_call_per_local(l: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 3_062 nanoseconds. - Weight::from_ref_time(4_432_879) - // Standard Error: 64 - .saturating_add(Weight::from_ref_time(46_196).saturating_mul(l.into())) + // Minimum execution time: 3_215 nanoseconds. + Weight::from_ref_time(4_697_732) + .saturating_add(Weight::from_proof_size(0)) + // Standard Error: 99 + .saturating_add(Weight::from_ref_time(46_431).saturating_mul(l.into())) } /// The range of component `r` is `[0, 50]`. fn instr_local_get(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 2_036 nanoseconds. - Weight::from_ref_time(2_318_877) - // Standard Error: 172 - .saturating_add(Weight::from_ref_time(500_498).saturating_mul(r.into())) + // Minimum execution time: 2_193 nanoseconds. + Weight::from_ref_time(2_472_277) + .saturating_add(Weight::from_proof_size(0)) + // Standard Error: 374 + .saturating_add(Weight::from_ref_time(459_651).saturating_mul(r.into())) } /// The range of component `r` is `[0, 50]`. fn instr_local_set(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 2_027 nanoseconds. - Weight::from_ref_time(2_355_900) - // Standard Error: 220 - .saturating_add(Weight::from_ref_time(461_393).saturating_mul(r.into())) + // Minimum execution time: 2_112 nanoseconds. + Weight::from_ref_time(2_388_132) + .saturating_add(Weight::from_proof_size(0)) + // Standard Error: 380 + .saturating_add(Weight::from_ref_time(485_216).saturating_mul(r.into())) } /// The range of component `r` is `[0, 50]`. fn instr_local_tee(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 2_038 nanoseconds. - Weight::from_ref_time(2_350_330) - // Standard Error: 224 - .saturating_add(Weight::from_ref_time(586_808).saturating_mul(r.into())) + // Minimum execution time: 2_219 nanoseconds. + Weight::from_ref_time(2_419_568) + .saturating_add(Weight::from_proof_size(0)) + // Standard Error: 402 + .saturating_add(Weight::from_ref_time(658_484).saturating_mul(r.into())) } /// The range of component `r` is `[0, 50]`. fn instr_global_get(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 897 nanoseconds. - Weight::from_ref_time(1_267_115) - // Standard Error: 189 - .saturating_add(Weight::from_ref_time(884_926).saturating_mul(r.into())) + // Minimum execution time: 959 nanoseconds. + Weight::from_ref_time(1_321_004) + .saturating_add(Weight::from_proof_size(0)) + // Standard Error: 480 + .saturating_add(Weight::from_ref_time(898_447).saturating_mul(r.into())) } /// The range of component `r` is `[0, 50]`. fn instr_global_set(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 892 nanoseconds. - Weight::from_ref_time(1_202_122) - // Standard Error: 286 - .saturating_add(Weight::from_ref_time(885_157).saturating_mul(r.into())) + // Minimum execution time: 934 nanoseconds. + Weight::from_ref_time(1_264_350) + .saturating_add(Weight::from_proof_size(0)) + // Standard Error: 1_031 + .saturating_add(Weight::from_ref_time(922_243).saturating_mul(r.into())) } /// The range of component `r` is `[0, 50]`. fn instr_memory_current(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 852 nanoseconds. - Weight::from_ref_time(1_132_479) - // Standard Error: 224 - .saturating_add(Weight::from_ref_time(719_603).saturating_mul(r.into())) + // Minimum execution time: 941 nanoseconds. + Weight::from_ref_time(1_229_407) + .saturating_add(Weight::from_proof_size(0)) + // Standard Error: 507 + .saturating_add(Weight::from_ref_time(824_206).saturating_mul(r.into())) } /// The range of component `r` is `[0, 1]`. fn instr_memory_grow(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 786 nanoseconds. - Weight::from_ref_time(874_044) - // Standard Error: 91_309 - .saturating_add(Weight::from_ref_time(181_849_955).saturating_mul(r.into())) + // Minimum execution time: 870 nanoseconds. + Weight::from_ref_time(946_681) + .saturating_add(Weight::from_proof_size(0)) + // Standard Error: 7_248 + .saturating_add(Weight::from_ref_time(179_759_218).saturating_mul(r.into())) } /// The range of component `r` is `[0, 50]`. fn instr_i64clz(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 763 nanoseconds. - Weight::from_ref_time(1_055_236) - // Standard Error: 207 - .saturating_add(Weight::from_ref_time(554_985).saturating_mul(r.into())) + // Minimum execution time: 843 nanoseconds. + Weight::from_ref_time(1_105_279) + .saturating_add(Weight::from_proof_size(0)) + // Standard Error: 251 + .saturating_add(Weight::from_ref_time(633_218).saturating_mul(r.into())) } /// The range of component `r` is `[0, 50]`. fn instr_i64ctz(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 756 nanoseconds. - Weight::from_ref_time(1_053_050) - // Standard Error: 165 - .saturating_add(Weight::from_ref_time(555_401).saturating_mul(r.into())) + // Minimum execution time: 843 nanoseconds. + Weight::from_ref_time(1_119_719) + .saturating_add(Weight::from_proof_size(0)) + // Standard Error: 248 + .saturating_add(Weight::from_ref_time(633_064).saturating_mul(r.into())) } /// The range of component `r` is `[0, 50]`. fn instr_i64popcnt(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 791 nanoseconds. - Weight::from_ref_time(1_080_240) - // Standard Error: 164 - .saturating_add(Weight::from_ref_time(554_698).saturating_mul(r.into())) + // Minimum execution time: 865 nanoseconds. + Weight::from_ref_time(1_394_651) + .saturating_add(Weight::from_proof_size(0)) + // Standard Error: 9_898 + .saturating_add(Weight::from_ref_time(630_054).saturating_mul(r.into())) } /// The range of component `r` is `[0, 50]`. fn instr_i64eqz(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 766 nanoseconds. - Weight::from_ref_time(1_074_739) - // Standard Error: 178 - .saturating_add(Weight::from_ref_time(565_891).saturating_mul(r.into())) + // Minimum execution time: 831 nanoseconds. + Weight::from_ref_time(1_095_288) + .saturating_add(Weight::from_proof_size(0)) + // Standard Error: 271 + .saturating_add(Weight::from_ref_time(650_578).saturating_mul(r.into())) } /// The range of component `r` is `[0, 50]`. fn instr_i64extendsi32(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 781 nanoseconds. - Weight::from_ref_time(1_077_122) - // Standard Error: 177 - .saturating_add(Weight::from_ref_time(548_846).saturating_mul(r.into())) + // Minimum execution time: 835 nanoseconds. + Weight::from_ref_time(1_128_234) + .saturating_add(Weight::from_proof_size(0)) + // Standard Error: 239 + .saturating_add(Weight::from_ref_time(615_970).saturating_mul(r.into())) } /// The range of component `r` is `[0, 50]`. fn instr_i64extendui32(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 793 nanoseconds. - Weight::from_ref_time(1_086_278) - // Standard Error: 163 - .saturating_add(Weight::from_ref_time(548_765).saturating_mul(r.into())) + // Minimum execution time: 839 nanoseconds. + Weight::from_ref_time(1_111_296) + .saturating_add(Weight::from_proof_size(0)) + // Standard Error: 303 + .saturating_add(Weight::from_ref_time(616_831).saturating_mul(r.into())) } /// The range of component `r` is `[0, 50]`. fn instr_i32wrapi64(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 769 nanoseconds. - Weight::from_ref_time(1_096_044) - // Standard Error: 134 - .saturating_add(Weight::from_ref_time(547_353).saturating_mul(r.into())) + // Minimum execution time: 839 nanoseconds. + Weight::from_ref_time(1_082_700) + .saturating_add(Weight::from_proof_size(0)) + // Standard Error: 236 + .saturating_add(Weight::from_ref_time(617_371).saturating_mul(r.into())) } /// The range of component `r` is `[0, 50]`. fn instr_i64eq(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 766 nanoseconds. - Weight::from_ref_time(1_072_610) - // Standard Error: 161 - .saturating_add(Weight::from_ref_time(774_895).saturating_mul(r.into())) + // Minimum execution time: 837 nanoseconds. + Weight::from_ref_time(1_249_666) + .saturating_add(Weight::from_proof_size(0)) + // Standard Error: 836 + .saturating_add(Weight::from_ref_time(904_457).saturating_mul(r.into())) } /// The range of component `r` is `[0, 50]`. fn instr_i64ne(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 751 nanoseconds. - Weight::from_ref_time(1_038_676) - // Standard Error: 158 - .saturating_add(Weight::from_ref_time(775_194).saturating_mul(r.into())) + // Minimum execution time: 818 nanoseconds. + Weight::from_ref_time(1_624_057) + .saturating_add(Weight::from_proof_size(0)) + // Standard Error: 5_906 + .saturating_add(Weight::from_ref_time(893_488).saturating_mul(r.into())) } /// The range of component `r` is `[0, 50]`. fn instr_i64lts(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 798 nanoseconds. - Weight::from_ref_time(1_089_712) - // Standard Error: 144 - .saturating_add(Weight::from_ref_time(774_377).saturating_mul(r.into())) + // Minimum execution time: 836 nanoseconds. + Weight::from_ref_time(1_973_044) + .saturating_add(Weight::from_proof_size(0)) + // Standard Error: 7_556 + .saturating_add(Weight::from_ref_time(885_369).saturating_mul(r.into())) } /// The range of component `r` is `[0, 50]`. fn instr_i64ltu(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 758 nanoseconds. - Weight::from_ref_time(1_078_460) - // Standard Error: 180 - .saturating_add(Weight::from_ref_time(779_861).saturating_mul(r.into())) + // Minimum execution time: 846 nanoseconds. + Weight::from_ref_time(1_088_223) + .saturating_add(Weight::from_proof_size(0)) + // Standard Error: 531 + .saturating_add(Weight::from_ref_time(909_313).saturating_mul(r.into())) } /// The range of component `r` is `[0, 50]`. fn instr_i64gts(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 758 nanoseconds. - Weight::from_ref_time(1_089_007) - // Standard Error: 164 - .saturating_add(Weight::from_ref_time(779_372).saturating_mul(r.into())) + // Minimum execution time: 842 nanoseconds. + Weight::from_ref_time(1_435_966) + .saturating_add(Weight::from_proof_size(0)) + // Standard Error: 3_824 + .saturating_add(Weight::from_ref_time(898_198).saturating_mul(r.into())) } /// The range of component `r` is `[0, 50]`. fn instr_i64gtu(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 772 nanoseconds. - Weight::from_ref_time(1_077_512) - // Standard Error: 165 - .saturating_add(Weight::from_ref_time(779_513).saturating_mul(r.into())) + // Minimum execution time: 846 nanoseconds. + Weight::from_ref_time(1_154_015) + .saturating_add(Weight::from_proof_size(0)) + // Standard Error: 319 + .saturating_add(Weight::from_ref_time(918_083).saturating_mul(r.into())) } /// The range of component `r` is `[0, 50]`. fn instr_i64les(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 760 nanoseconds. - Weight::from_ref_time(1_078_546) - // Standard Error: 168 - .saturating_add(Weight::from_ref_time(779_138).saturating_mul(r.into())) + // Minimum execution time: 848 nanoseconds. + Weight::from_ref_time(1_155_323) + .saturating_add(Weight::from_proof_size(0)) + // Standard Error: 307 + .saturating_add(Weight::from_ref_time(906_516).saturating_mul(r.into())) } /// The range of component `r` is `[0, 50]`. fn instr_i64leu(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 757 nanoseconds. - Weight::from_ref_time(1_080_251) - // Standard Error: 168 - .saturating_add(Weight::from_ref_time(779_391).saturating_mul(r.into())) + // Minimum execution time: 877 nanoseconds. + Weight::from_ref_time(1_629_210) + .saturating_add(Weight::from_proof_size(0)) + // Standard Error: 8_175 + .saturating_add(Weight::from_ref_time(906_657).saturating_mul(r.into())) } /// The range of component `r` is `[0, 50]`. fn instr_i64ges(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 786 nanoseconds. - Weight::from_ref_time(1_072_690) - // Standard Error: 219 - .saturating_add(Weight::from_ref_time(780_381).saturating_mul(r.into())) + // Minimum execution time: 843 nanoseconds. + Weight::from_ref_time(1_126_252) + .saturating_add(Weight::from_proof_size(0)) + // Standard Error: 251 + .saturating_add(Weight::from_ref_time(907_052).saturating_mul(r.into())) } /// The range of component `r` is `[0, 50]`. fn instr_i64geu(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 771 nanoseconds. - Weight::from_ref_time(1_063_735) - // Standard Error: 162 - .saturating_add(Weight::from_ref_time(779_906).saturating_mul(r.into())) + // Minimum execution time: 852 nanoseconds. + Weight::from_ref_time(1_497_667) + .saturating_add(Weight::from_proof_size(0)) + // Standard Error: 7_593 + .saturating_add(Weight::from_ref_time(897_474).saturating_mul(r.into())) } /// The range of component `r` is `[0, 50]`. fn instr_i64add(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 785 nanoseconds. - Weight::from_ref_time(1_059_585) - // Standard Error: 155 - .saturating_add(Weight::from_ref_time(756_828).saturating_mul(r.into())) + // Minimum execution time: 856 nanoseconds. + Weight::from_ref_time(1_350_390) + .saturating_add(Weight::from_proof_size(0)) + // Standard Error: 2_625 + .saturating_add(Weight::from_ref_time(889_609).saturating_mul(r.into())) } /// The range of component `r` is `[0, 50]`. fn instr_i64sub(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 800 nanoseconds. - Weight::from_ref_time(1_066_659) - // Standard Error: 154 - .saturating_add(Weight::from_ref_time(754_318).saturating_mul(r.into())) + // Minimum execution time: 841 nanoseconds. + Weight::from_ref_time(2_677_235) + .saturating_add(Weight::from_proof_size(0)) + // Standard Error: 11_507 + .saturating_add(Weight::from_ref_time(846_704).saturating_mul(r.into())) } /// The range of component `r` is `[0, 50]`. fn instr_i64mul(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 769 nanoseconds. - Weight::from_ref_time(1_078_854) - // Standard Error: 172 - .saturating_add(Weight::from_ref_time(754_183).saturating_mul(r.into())) + // Minimum execution time: 846 nanoseconds. + Weight::from_ref_time(1_148_206) + .saturating_add(Weight::from_proof_size(0)) + // Standard Error: 393 + .saturating_add(Weight::from_ref_time(884_886).saturating_mul(r.into())) } /// The range of component `r` is `[0, 50]`. fn instr_i64divs(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 770 nanoseconds. - Weight::from_ref_time(1_057_476) - // Standard Error: 191 - .saturating_add(Weight::from_ref_time(1_443_902).saturating_mul(r.into())) + // Minimum execution time: 865 nanoseconds. + Weight::from_ref_time(2_862_483) + .saturating_add(Weight::from_proof_size(0)) + // Standard Error: 16_413 + .saturating_add(Weight::from_ref_time(1_481_623).saturating_mul(r.into())) } /// The range of component `r` is `[0, 50]`. fn instr_i64divu(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 764 nanoseconds. - Weight::from_ref_time(1_063_821) - // Standard Error: 193 - .saturating_add(Weight::from_ref_time(1_324_496).saturating_mul(r.into())) + // Minimum execution time: 1_443 nanoseconds. + Weight::from_ref_time(1_086_902) + .saturating_add(Weight::from_proof_size(0)) + // Standard Error: 2_245 + .saturating_add(Weight::from_ref_time(1_463_145).saturating_mul(r.into())) } /// The range of component `r` is `[0, 50]`. fn instr_i64rems(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 747 nanoseconds. - Weight::from_ref_time(1_093_209) - // Standard Error: 270 - .saturating_add(Weight::from_ref_time(1_447_180).saturating_mul(r.into())) + // Minimum execution time: 878 nanoseconds. + Weight::from_ref_time(1_130_306) + .saturating_add(Weight::from_proof_size(0)) + // Standard Error: 475 + .saturating_add(Weight::from_ref_time(1_522_011).saturating_mul(r.into())) } /// The range of component `r` is `[0, 50]`. fn instr_i64remu(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 805 nanoseconds. - Weight::from_ref_time(1_033_953) - // Standard Error: 152 - .saturating_add(Weight::from_ref_time(1_336_911).saturating_mul(r.into())) + // Minimum execution time: 827 nanoseconds. + Weight::from_ref_time(1_197_053) + .saturating_add(Weight::from_proof_size(0)) + // Standard Error: 474 + .saturating_add(Weight::from_ref_time(1_449_062).saturating_mul(r.into())) } /// The range of component `r` is `[0, 50]`. fn instr_i64and(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 766 nanoseconds. - Weight::from_ref_time(1_059_430) - // Standard Error: 160 - .saturating_add(Weight::from_ref_time(757_265).saturating_mul(r.into())) + // Minimum execution time: 847 nanoseconds. + Weight::from_ref_time(1_152_423) + .saturating_add(Weight::from_proof_size(0)) + // Standard Error: 296 + .saturating_add(Weight::from_ref_time(895_541).saturating_mul(r.into())) } /// The range of component `r` is `[0, 50]`. fn instr_i64or(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 760 nanoseconds. - Weight::from_ref_time(1_077_376) - // Standard Error: 160 - .saturating_add(Weight::from_ref_time(755_800).saturating_mul(r.into())) + // Minimum execution time: 876 nanoseconds. + Weight::from_ref_time(1_169_485) + .saturating_add(Weight::from_proof_size(0)) + // Standard Error: 571 + .saturating_add(Weight::from_ref_time(932_659).saturating_mul(r.into())) } /// The range of component `r` is `[0, 50]`. fn instr_i64xor(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 798 nanoseconds. - Weight::from_ref_time(1_070_570) - // Standard Error: 157 - .saturating_add(Weight::from_ref_time(756_839).saturating_mul(r.into())) + // Minimum execution time: 856 nanoseconds. + Weight::from_ref_time(1_155_127) + .saturating_add(Weight::from_proof_size(0)) + // Standard Error: 256 + .saturating_add(Weight::from_ref_time(895_663).saturating_mul(r.into())) } /// The range of component `r` is `[0, 50]`. fn instr_i64shl(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 761 nanoseconds. - Weight::from_ref_time(1_074_645) - // Standard Error: 169 - .saturating_add(Weight::from_ref_time(771_486).saturating_mul(r.into())) + // Minimum execution time: 871 nanoseconds. + Weight::from_ref_time(1_139_722) + .saturating_add(Weight::from_proof_size(0)) + // Standard Error: 367 + .saturating_add(Weight::from_ref_time(903_115).saturating_mul(r.into())) } /// The range of component `r` is `[0, 50]`. fn instr_i64shrs(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 779 nanoseconds. - Weight::from_ref_time(1_107_671) - // Standard Error: 185 - .saturating_add(Weight::from_ref_time(769_168).saturating_mul(r.into())) + // Minimum execution time: 853 nanoseconds. + Weight::from_ref_time(1_155_542) + .saturating_add(Weight::from_proof_size(0)) + // Standard Error: 541 + .saturating_add(Weight::from_ref_time(901_635).saturating_mul(r.into())) } /// The range of component `r` is `[0, 50]`. fn instr_i64shru(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 755 nanoseconds. - Weight::from_ref_time(1_075_769) - // Standard Error: 164 - .saturating_add(Weight::from_ref_time(770_334).saturating_mul(r.into())) + // Minimum execution time: 866 nanoseconds. + Weight::from_ref_time(2_260_588) + .saturating_add(Weight::from_proof_size(0)) + // Standard Error: 12_944 + .saturating_add(Weight::from_ref_time(877_790).saturating_mul(r.into())) } /// The range of component `r` is `[0, 50]`. fn instr_i64rotl(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 767 nanoseconds. - Weight::from_ref_time(608_749) - // Standard Error: 2_059 - .saturating_add(Weight::from_ref_time(804_228).saturating_mul(r.into())) + // Minimum execution time: 851 nanoseconds. + Weight::from_ref_time(1_627_816) + .saturating_add(Weight::from_proof_size(0)) + // Standard Error: 4_004 + .saturating_add(Weight::from_ref_time(887_929).saturating_mul(r.into())) } /// The range of component `r` is `[0, 50]`. fn instr_i64rotr(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 779 nanoseconds. - Weight::from_ref_time(1_054_998) - // Standard Error: 191 - .saturating_add(Weight::from_ref_time(770_225).saturating_mul(r.into())) + // Minimum execution time: 874 nanoseconds. + Weight::from_ref_time(2_611_817) + .saturating_add(Weight::from_proof_size(0)) + // Standard Error: 10_026 + .saturating_add(Weight::from_ref_time(862_138).saturating_mul(r.into())) } } diff --git a/primitives/wasm-interface/Cargo.toml b/primitives/wasm-interface/Cargo.toml index 5f914ae3a..21b0bb432 100644 --- a/primitives/wasm-interface/Cargo.toml +++ b/primitives/wasm-interface/Cargo.toml @@ -18,7 +18,7 @@ codec = { package = "parity-scale-codec", version = "3.2.2", default-features = impl-trait-for-tuples = "0.2.2" log = { version = "0.4.17", optional = true } wasmi = { version = "0.13", optional = true } -wasmtime = { version = "5.0.0", default-features = false, optional = true } +wasmtime = { version = "5.0.0", default-features = false, optional = true, git = "https://github.com/paritytech/wasmtime.git", branch = "v5.0.0_lto_fix" } anyhow = { version = "1.0.68", optional = true } sp-std = { version = "5.0.0", default-features = false, path = "../std" } From a10ccb562fa2f01832283d4d854a2938037f487a Mon Sep 17 00:00:00 2001 From: Davide Galassi Date: Wed, 15 Feb 2023 11:25:26 +0100 Subject: [PATCH 121/558] Fork tree prune assumptions removal v2 (#13327) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Removed assumptions about ancestry from fork tree prune method * Tests improvement * Fork tree prune refactory * Code refactory * Correctly handle borderline, but legit, case * Apply suggestions from code review Co-authored-by: André Silva <123550+andresilva@users.noreply.github.com> * Removed duplicated test --------- Co-authored-by: André Silva <123550+andresilva@users.noreply.github.com> --- client/consensus/epochs/src/lib.rs | 4 +- utils/fork-tree/src/lib.rs | 369 +++++++++++++++++------------ 2 files changed, 225 insertions(+), 148 deletions(-) diff --git a/client/consensus/epochs/src/lib.rs b/client/consensus/epochs/src/lib.rs index 7784a9943..0e0b9fc17 100644 --- a/client/consensus/epochs/src/lib.rs +++ b/client/consensus/epochs/src/lib.rs @@ -1039,7 +1039,7 @@ mod tests { let mut nodes: Vec<_> = epoch_changes.tree().iter().map(|(h, _, _)| h).collect(); nodes.sort(); - assert_eq!(nodes, vec![b"A", b"B", b"C", b"E", b"F", b"G"]); + assert_eq!(nodes, vec![b"A", b"B", b"C", b"F", b"G"]); // Finalize block y @ number 35, slot 330 // This should prune all nodes imported by blocks with a number < 35 that are not @@ -1050,7 +1050,7 @@ mod tests { let mut nodes: Vec<_> = epoch_changes.tree().iter().map(|(h, _, _)| h).collect(); nodes.sort(); - assert_eq!(nodes, vec![b"B", b"C", b"F", b"G"]); + assert_eq!(nodes, vec![b"B", b"C", b"G"]); } #[test] diff --git a/utils/fork-tree/src/lib.rs b/utils/fork-tree/src/lib.rs index 76c28d910..bbd0daca4 100644 --- a/utils/fork-tree/src/lib.rs +++ b/utils/fork-tree/src/lib.rs @@ -32,7 +32,7 @@ pub enum Error { UnfinalizedAncestor, /// Imported or finalized node that is an ancestor of previously finalized node. Revert, - /// Error throw by client when checking for node ancestry. + /// Error thrown by user when checking for node ancestry. Client(E), } @@ -48,11 +48,7 @@ impl fmt::Display for Error { } } -impl std::error::Error for Error { - fn cause(&self) -> Option<&dyn std::error::Error> { - None - } -} +impl std::error::Error for Error {} impl From for Error { fn from(err: E) -> Error { @@ -83,7 +79,7 @@ pub enum FilterAction { /// A tree data structure that stores several nodes across multiple branches. /// /// Top-level branches are called roots. The tree has functionality for -/// finalizing nodes, which means that that node is traversed, and all competing +/// finalizing nodes, which means that node is traversed, and all competing /// branches are pruned. It also guarantees that nodes in the tree are finalized /// in order. Each node is uniquely identified by its hash but can be ordered by /// its number. In order to build the tree an external function must be provided @@ -99,12 +95,14 @@ where H: PartialEq, N: Ord, { - /// Create a new empty tree. + /// Create a new empty tree instance. pub fn new() -> ForkTree { ForkTree { roots: Vec::new(), best_finalized_number: None } } - /// Rebalance the tree, i.e. sort child nodes by max branch depth (decreasing). + /// Rebalance the tree. + /// + /// For each tree level sort child nodes by max branch depth (decreasing). /// /// Most operations in the tree are performed with depth-first search /// starting from the leftmost node at every level, since this tree is meant @@ -120,10 +118,12 @@ where } } - /// Import a new node into the tree. The given function `is_descendent_of` - /// should return `true` if the second hash (target) is a descendent of the - /// first hash (base). This method assumes that nodes in the same branch are - /// imported in order. + /// Import a new node into the tree. + /// + /// The given function `is_descendent_of` should return `true` if the second + /// hash (target) is a descendent of the first hash (base). + /// + /// This method assumes that nodes in the same branch are imported in order. /// /// Returns `true` if the imported node is a root. // WARNING: some users of this method (i.e. consensus epoch changes tree) currently silently @@ -232,9 +232,10 @@ where } /// Find a node in the tree that is the deepest ancestor of the given - /// block hash and which passes the given predicate. The given function - /// `is_descendent_of` should return `true` if the second hash (target) - /// is a descendent of the first hash (base). + /// block hash and which passes the given predicate. + /// + /// The given function `is_descendent_of` should return `true` if the + /// second hash (target) is a descendent of the first hash (base). pub fn find_node_where( &self, hash: &H, @@ -281,10 +282,12 @@ where /// Same as [`find_node_where`](ForkTree::find_node_where), but returns indices. /// /// The returned indices represent the full path to reach the matching node starting - /// from first to last, i.e. the earliest index in the traverse path goes first, and the final - /// index in the traverse path goes last. If a node is found that matches the predicate - /// the returned path should always contain at least one index, otherwise `None` is - /// returned. + /// from one of the roots, i.e. the earliest index in the traverse path goes first, + /// and the final index in the traverse path goes last. + /// + /// If a node is found that matches the predicate the returned path should always + /// contain at least one index, otherwise `None` is returned. + // // WARNING: some users of this method (i.e. consensus epoch changes tree) currently silently // rely on a **post-order DFS** traversal. If we are using instead a top-down traversal method // then the `is_descendent_of` closure, when used after a warp-sync, will end up querying the @@ -351,14 +354,16 @@ where }) } - /// Prune the tree, removing all non-canonical nodes. We find the node in the - /// tree that is the deepest ancestor of the given hash and that passes the - /// given predicate. If such a node exists, we re-root the tree to this - /// node. Otherwise the tree remains unchanged. The given function - /// `is_descendent_of` should return `true` if the second hash (target) is a - /// descendent of the first hash (base). + /// Prune the tree, removing all non-canonical nodes. /// - /// Returns all pruned node data. + /// We find the node in the tree that is the deepest ancestor of the given hash + /// and that passes the given predicate. If such a node exists, we re-root the + /// tree to this node. Otherwise the tree remains unchanged. + /// + /// The given function `is_descendent_of` should return `true` if the second + /// hash (target) is a descendent of the first hash (base). + /// + /// Returns all pruned nodes data. pub fn prune( &mut self, hash: &H, @@ -371,42 +376,59 @@ where F: Fn(&H, &H) -> Result, P: Fn(&V) -> bool, { - let root_index = + let new_root_path = match self.find_node_index_where(hash, number, is_descendent_of, predicate)? { - Some(idx) => idx, + Some(path) => path, None => return Ok(RemovedIterator { stack: Vec::new() }), }; - let mut old_roots = std::mem::take(&mut self.roots); + let mut removed = std::mem::take(&mut self.roots); - let curr_children = root_index + // Find and detach the new root from the removed nodes + let root_siblings = new_root_path .iter() - .take(root_index.len() - 1) - .fold(&mut old_roots, |curr, idx| &mut curr[*idx].children); - let mut root = curr_children.remove(root_index[root_index.len() - 1]); - - let mut removed = old_roots; + .take(new_root_path.len() - 1) + .fold(&mut removed, |curr, idx| &mut curr[*idx].children); + let root = root_siblings.remove(new_root_path[new_root_path.len() - 1]); + self.roots = vec![root]; - // we found the deepest ancestor of the finalized block, so we prune - // out any children that don't include the finalized block. - let root_children = std::mem::take(&mut root.children); - let mut is_first = true; + // If, because of the `predicate`, the new root is not the deepest ancestor + // of `hash` then we can remove all the nodes that are descendants of the new + // `root` but not ancestors of `hash`. + let mut curr = &mut self.roots[0]; + loop { + let mut maybe_ancestor_idx = None; + for (idx, child) in curr.children.iter().enumerate() { + if child.number < *number && is_descendent_of(&child.hash, hash)? { + maybe_ancestor_idx = Some(idx); + break + } + } + let Some(ancestor_idx) = maybe_ancestor_idx else { + // Now we are positioned just above block identified by `hash` + break + }; + // Preserve only the ancestor node, the siblings are removed + let mut next_siblings = std::mem::take(&mut curr.children); + let next = next_siblings.remove(ancestor_idx); + curr.children = vec![next]; + removed.append(&mut next_siblings); + curr = &mut curr.children[0]; + } - for child in root_children { - if is_first && - (child.number == *number && child.hash == *hash || - child.number < *number && is_descendent_of(&child.hash, hash)?) + // Curr now points to our direct ancestor, if necessary remove any node that is + // not a descendant of `hash`. + let children = std::mem::take(&mut curr.children); + for child in children { + if child.number == *number && child.hash == *hash || + *number < child.number && is_descendent_of(hash, &child.hash)? { - root.children.push(child); - // assuming that the tree is well formed only one child should pass this - // requirement due to ancestry restrictions (i.e. they must be different forks). - is_first = false; + curr.children.push(child); } else { removed.push(child); } } - self.roots = vec![root]; self.rebalance(); Ok(RemovedIterator { stack: removed }) @@ -836,21 +858,21 @@ mod test { impl std::error::Error for TestError {} fn test_fork_tree<'a>( - ) -> (ForkTree<&'a str, u64, ()>, impl Fn(&&str, &&str) -> Result) { + ) -> (ForkTree<&'a str, u64, u32>, impl Fn(&&str, &&str) -> Result) { let mut tree = ForkTree::new(); #[rustfmt::skip] // - // - B - C - D - E - // / - // / - G - // / / - // A - F - H - I - // \ \ - // \ - L - M - N - // \ \ - // \ - O - // - J - K + // +---B-c-C---D---E + // | + // | +---G + // | | + // 0---A---F---H---I + // | | + // | +---L-m-M---N + // | | + // | +---O + // +---J---K // // (where N is not a part of fork tree) // @@ -859,9 +881,12 @@ mod test { // will be on the leftmost side of the tree. let is_descendent_of = |base: &&str, block: &&str| -> Result { let letters = vec!["B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M", "N", "O"]; - match (*base, *block) { + // This is a trick to have lowercase blocks be direct parents of their + // uppercase correspondent (A excluded) + let block = block.to_uppercase(); + match (*base, block) { ("A", b) => Ok(letters.into_iter().any(|n| n == b)), - ("B", b) => Ok(b == "C" || b == "D" || b == "E"), + ("B" | "c", b) => Ok(b == "C" || b == "D" || b == "E"), ("C", b) => Ok(b == "D" || b == "E"), ("D", b) => Ok(b == "E"), ("E", _) => Ok(false), @@ -872,7 +897,8 @@ mod test { ("I", _) => Ok(false), ("J", b) => Ok(b == "K"), ("K", _) => Ok(false), - ("L", b) => Ok(b == "M" || b == "O" || b == "N"), + ("L", b) => Ok(b == "M" || b == "N" || b == "O"), + ("m", b) => Ok(b == "M" || b == "N"), ("M", b) => Ok(b == "N"), ("O", _) => Ok(false), ("0", _) => Ok(true), @@ -880,24 +906,20 @@ mod test { } }; - tree.import("A", 1, (), &is_descendent_of).unwrap(); - - tree.import("B", 2, (), &is_descendent_of).unwrap(); - tree.import("C", 3, (), &is_descendent_of).unwrap(); - tree.import("D", 4, (), &is_descendent_of).unwrap(); - tree.import("E", 5, (), &is_descendent_of).unwrap(); - - tree.import("F", 2, (), &is_descendent_of).unwrap(); - tree.import("G", 3, (), &is_descendent_of).unwrap(); - - tree.import("H", 3, (), &is_descendent_of).unwrap(); - tree.import("I", 4, (), &is_descendent_of).unwrap(); - tree.import("L", 4, (), &is_descendent_of).unwrap(); - tree.import("M", 5, (), &is_descendent_of).unwrap(); - tree.import("O", 5, (), &is_descendent_of).unwrap(); - - tree.import("J", 2, (), &is_descendent_of).unwrap(); - tree.import("K", 3, (), &is_descendent_of).unwrap(); + tree.import("A", 10, 1, &is_descendent_of).unwrap(); + tree.import("B", 20, 2, &is_descendent_of).unwrap(); + tree.import("C", 30, 3, &is_descendent_of).unwrap(); + tree.import("D", 40, 4, &is_descendent_of).unwrap(); + tree.import("E", 50, 5, &is_descendent_of).unwrap(); + tree.import("F", 20, 2, &is_descendent_of).unwrap(); + tree.import("G", 30, 3, &is_descendent_of).unwrap(); + tree.import("H", 30, 3, &is_descendent_of).unwrap(); + tree.import("I", 40, 4, &is_descendent_of).unwrap(); + tree.import("L", 40, 4, &is_descendent_of).unwrap(); + tree.import("M", 50, 5, &is_descendent_of).unwrap(); + tree.import("O", 50, 5, &is_descendent_of).unwrap(); + tree.import("J", 20, 2, &is_descendent_of).unwrap(); + tree.import("K", 30, 3, &is_descendent_of).unwrap(); (tree, is_descendent_of) } @@ -908,22 +930,22 @@ mod test { tree.finalize_root(&"A"); - assert_eq!(tree.best_finalized_number, Some(1)); + assert_eq!(tree.best_finalized_number, Some(10)); - assert_eq!(tree.import("A", 1, (), &is_descendent_of), Err(Error::Revert)); + assert_eq!(tree.import("A", 10, 1, &is_descendent_of), Err(Error::Revert)); } #[test] fn import_doesnt_add_duplicates() { let (mut tree, is_descendent_of) = test_fork_tree(); - assert_eq!(tree.import("A", 1, (), &is_descendent_of), Err(Error::Duplicate)); + assert_eq!(tree.import("A", 10, 1, &is_descendent_of), Err(Error::Duplicate)); - assert_eq!(tree.import("I", 4, (), &is_descendent_of), Err(Error::Duplicate)); + assert_eq!(tree.import("I", 40, 4, &is_descendent_of), Err(Error::Duplicate)); - assert_eq!(tree.import("G", 3, (), &is_descendent_of), Err(Error::Duplicate)); + assert_eq!(tree.import("G", 30, 3, &is_descendent_of), Err(Error::Duplicate)); - assert_eq!(tree.import("K", 3, (), &is_descendent_of), Err(Error::Duplicate)); + assert_eq!(tree.import("K", 30, 3, &is_descendent_of), Err(Error::Duplicate)); } #[test] @@ -931,14 +953,14 @@ mod test { let finalize_a = || { let (mut tree, ..) = test_fork_tree(); - assert_eq!(tree.roots().map(|(h, n, _)| (*h, *n)).collect::>(), vec![("A", 1)]); + assert_eq!(tree.roots().map(|(h, n, _)| (*h, *n)).collect::>(), vec![("A", 10)]); // finalizing "A" opens up three possible forks tree.finalize_root(&"A"); assert_eq!( tree.roots().map(|(h, n, _)| (*h, *n)).collect::>(), - vec![("B", 2), ("F", 2), ("J", 2)], + vec![("B", 20), ("F", 20), ("J", 20)], ); tree @@ -950,7 +972,7 @@ mod test { // finalizing "B" will progress on its fork and remove any other competing forks tree.finalize_root(&"B"); - assert_eq!(tree.roots().map(|(h, n, _)| (*h, *n)).collect::>(), vec![("C", 3)],); + assert_eq!(tree.roots().map(|(h, n, _)| (*h, *n)).collect::>(), vec![("C", 30)],); // all the other forks have been pruned assert!(tree.roots.len() == 1); @@ -962,7 +984,7 @@ mod test { // finalizing "J" will progress on its fork and remove any other competing forks tree.finalize_root(&"J"); - assert_eq!(tree.roots().map(|(h, n, _)| (*h, *n)).collect::>(), vec![("K", 3)],); + assert_eq!(tree.roots().map(|(h, n, _)| (*h, *n)).collect::>(), vec![("K", 30)],); // all the other forks have been pruned assert!(tree.roots.len() == 1); @@ -982,42 +1004,42 @@ mod test { // finalizing "A" opens up three possible forks assert_eq!( - tree.finalize(&"A", 1, &is_descendent_of), - Ok(FinalizationResult::Changed(Some(()))), + tree.finalize(&"A", 10, &is_descendent_of), + Ok(FinalizationResult::Changed(Some(1))), ); assert_eq!( tree.roots().map(|(h, n, _)| (*h, *n)).collect::>(), - vec![("B", 2), ("F", 2), ("J", 2)], + vec![("B", 20), ("F", 20), ("J", 20)], ); // finalizing anything lower than what we observed will fail - assert_eq!(tree.best_finalized_number, Some(1)); + assert_eq!(tree.best_finalized_number, Some(10)); - assert_eq!(tree.finalize(&"Z", 1, &is_descendent_of), Err(Error::Revert)); + assert_eq!(tree.finalize(&"Z", 10, &is_descendent_of), Err(Error::Revert)); // trying to finalize a node without finalizing its ancestors first will fail - assert_eq!(tree.finalize(&"H", 3, &is_descendent_of), Err(Error::UnfinalizedAncestor)); + assert_eq!(tree.finalize(&"H", 30, &is_descendent_of), Err(Error::UnfinalizedAncestor)); // after finalizing "F" we can finalize "H" assert_eq!( - tree.finalize(&"F", 2, &is_descendent_of), - Ok(FinalizationResult::Changed(Some(()))), + tree.finalize(&"F", 20, &is_descendent_of), + Ok(FinalizationResult::Changed(Some(2))), ); assert_eq!( - tree.finalize(&"H", 3, &is_descendent_of), - Ok(FinalizationResult::Changed(Some(()))), + tree.finalize(&"H", 30, &is_descendent_of), + Ok(FinalizationResult::Changed(Some(3))), ); assert_eq!( tree.roots().map(|(h, n, _)| (*h, *n)).collect::>(), - vec![("L", 4), ("I", 4)], + vec![("L", 40), ("I", 40)], ); // finalizing a node from another fork that isn't part of the tree clears the tree assert_eq!( - tree.finalize(&"Z", 5, &is_descendent_of), + tree.finalize(&"Z", 50, &is_descendent_of), Ok(FinalizationResult::Changed(None)), ); @@ -1040,13 +1062,13 @@ mod test { // finalizing "A" opens up three possible forks assert_eq!( - tree.finalize_with_ancestors(&"A", 1, &is_descendent_of), - Ok(FinalizationResult::Changed(Some(()))), + tree.finalize_with_ancestors(&"A", 10, &is_descendent_of), + Ok(FinalizationResult::Changed(Some(1))), ); assert_eq!( tree.roots().map(|(h, n, _)| (*h, *n)).collect::>(), - vec![("B", 2), ("F", 2), ("J", 2)], + vec![("B", 20), ("F", 20), ("J", 20)], ); // finalizing H: @@ -1054,16 +1076,16 @@ mod test { // 2) opens root that is ancestor of H (F -> G+H) // 3) finalizes the just opened root H (H -> I + L) assert_eq!( - tree.finalize_with_ancestors(&"H", 3, &is_descendent_of), - Ok(FinalizationResult::Changed(Some(()))), + tree.finalize_with_ancestors(&"H", 30, &is_descendent_of), + Ok(FinalizationResult::Changed(Some(3))), ); assert_eq!( tree.roots().map(|(h, n, _)| (*h, *n)).collect::>(), - vec![("L", 4), ("I", 4)], + vec![("L", 40), ("I", 40)], ); - assert_eq!(tree.best_finalized_number, Some(3)); + assert_eq!(tree.best_finalized_number, Some(30)); // finalizing N (which is not a part of the tree): // 1) removes roots that are not ancestors/descendants of N (I) @@ -1071,13 +1093,13 @@ mod test { // 3) removes roots that are not ancestors/descendants of N (O) // 4) opens root that is ancestor of N (M -> {}) assert_eq!( - tree.finalize_with_ancestors(&"N", 6, &is_descendent_of), + tree.finalize_with_ancestors(&"N", 60, &is_descendent_of), Ok(FinalizationResult::Changed(None)), ); assert_eq!(tree.roots().map(|(h, n, _)| (*h, *n)).collect::>(), vec![],); - assert_eq!(tree.best_finalized_number, Some(6)); + assert_eq!(tree.best_finalized_number, Some(60)); } #[test] @@ -1209,20 +1231,20 @@ mod test { assert_eq!( tree.iter().map(|(h, n, _)| (*h, *n)).collect::>(), vec![ - ("A", 1), - ("B", 2), - ("C", 3), - ("D", 4), - ("E", 5), - ("F", 2), - ("H", 3), - ("L", 4), - ("M", 5), - ("O", 5), - ("I", 4), - ("G", 3), - ("J", 2), - ("K", 3), + ("A", 10), + ("B", 20), + ("C", 30), + ("D", 40), + ("E", 50), + ("F", 20), + ("H", 30), + ("L", 40), + ("M", 50), + ("O", 50), + ("I", 40), + ("G", 30), + ("J", 20), + ("K", 30), ], ); } @@ -1289,9 +1311,9 @@ mod test { // Extend the single root fork-tree to also excercise the roots order during map. let is_descendent_of = |_: &&str, _: &&str| -> Result { Ok(false) }; - let is_root = tree.import("A1", 1, (), &is_descendent_of).unwrap(); + let is_root = tree.import("A1", 10, 1, &is_descendent_of).unwrap(); assert!(is_root); - let is_root = tree.import("A2", 1, (), &is_descendent_of).unwrap(); + let is_root = tree.import("A2", 10, 1, &is_descendent_of).unwrap(); assert!(is_root); let old_tree = tree.clone(); @@ -1306,10 +1328,10 @@ mod test { } #[test] - fn prune_works() { + fn prune_works_for_in_tree_hashes() { let (mut tree, is_descendent_of) = test_fork_tree(); - let removed = tree.prune(&"C", &3, &is_descendent_of, &|_| true).unwrap(); + let removed = tree.prune(&"C", &30, &is_descendent_of, &|_| true).unwrap(); assert_eq!(tree.roots.iter().map(|node| node.hash).collect::>(), vec!["B"]); @@ -1323,7 +1345,7 @@ mod test { vec!["A", "F", "H", "L", "M", "O", "I", "G", "J", "K"] ); - let removed = tree.prune(&"E", &5, &is_descendent_of, &|_| true).unwrap(); + let removed = tree.prune(&"E", &50, &is_descendent_of, &|_| true).unwrap(); assert_eq!(tree.roots.iter().map(|node| node.hash).collect::>(), vec!["D"]); @@ -1332,6 +1354,61 @@ mod test { assert_eq!(removed.map(|(hash, _, _)| hash).collect::>(), vec!["B", "C"]); } + #[test] + fn prune_works_for_out_of_tree_hashes() { + let (mut tree, is_descendent_of) = test_fork_tree(); + + let removed = tree.prune(&"c", &25, &is_descendent_of, &|_| true).unwrap(); + + assert_eq!(tree.roots.iter().map(|node| node.hash).collect::>(), vec!["B"]); + + assert_eq!( + tree.iter().map(|(hash, _, _)| *hash).collect::>(), + vec!["B", "C", "D", "E"], + ); + + assert_eq!( + removed.map(|(hash, _, _)| hash).collect::>(), + vec!["A", "F", "H", "L", "M", "O", "I", "G", "J", "K"] + ); + } + + #[test] + fn prune_works_for_not_direct_ancestor() { + let (mut tree, is_descendent_of) = test_fork_tree(); + + // This is to re-root the tree not at the immediate ancestor, but the one just before. + let removed = tree.prune(&"m", &45, &is_descendent_of, &|height| *height == 3).unwrap(); + + assert_eq!(tree.roots.iter().map(|node| node.hash).collect::>(), vec!["H"]); + + assert_eq!(tree.iter().map(|(hash, _, _)| *hash).collect::>(), vec!["H", "L", "M"],); + + assert_eq!( + removed.map(|(hash, _, _)| hash).collect::>(), + vec!["O", "I", "A", "B", "C", "D", "E", "F", "G", "J", "K"] + ); + } + + #[test] + fn prune_works_for_far_away_ancestor() { + let (mut tree, is_descendent_of) = test_fork_tree(); + + let removed = tree.prune(&"m", &45, &is_descendent_of, &|height| *height == 2).unwrap(); + + assert_eq!(tree.roots.iter().map(|node| node.hash).collect::>(), vec!["F"]); + + assert_eq!( + tree.iter().map(|(hash, _, _)| *hash).collect::>(), + vec!["F", "H", "L", "M"], + ); + + assert_eq!( + removed.map(|(hash, _, _)| hash).collect::>(), + vec!["O", "I", "G", "A", "B", "C", "D", "E", "J", "K"] + ); + } + #[test] fn find_node_backtracks_after_finding_highest_descending_node() { let mut tree = ForkTree::new(); @@ -1381,7 +1458,7 @@ mod test { } }; - tree.import("P", 6, (), &is_descendent_of).unwrap(); + tree.import("P", 60, 6, &is_descendent_of).unwrap(); // this should re-order the tree, since the branch "A -> B -> C -> D -> E" is no longer tied // with 5 blocks depth. additionally "O" should be visited before "M" now, since it has one @@ -1396,7 +1473,7 @@ mod test { fn drain_filter_works() { let (mut tree, _) = test_fork_tree(); - let filter = |h: &&str, _: &u64, _: &()| match *h { + let filter = |h: &&str, _: &u64, _: &u32| match *h { "A" | "B" | "F" | "G" => FilterAction::KeepNode, "C" => FilterAction::KeepTree, "H" | "J" => FilterAction::Remove, @@ -1421,19 +1498,19 @@ mod test { let (tree, is_descendent_of) = test_fork_tree(); let path = tree - .find_node_index_where(&"D", &4, &is_descendent_of, &|_| true) + .find_node_index_where(&"D", &40, &is_descendent_of, &|_| true) .unwrap() .unwrap(); assert_eq!(path, [0, 0, 0]); let path = tree - .find_node_index_where(&"O", &5, &is_descendent_of, &|_| true) + .find_node_index_where(&"O", &50, &is_descendent_of, &|_| true) .unwrap() .unwrap(); assert_eq!(path, [0, 1, 0, 0]); let path = tree - .find_node_index_where(&"N", &6, &is_descendent_of, &|_| true) + .find_node_index_where(&"N", &60, &is_descendent_of, &|_| true) .unwrap() .unwrap(); assert_eq!(path, [0, 1, 0, 0, 0]); @@ -1489,17 +1566,17 @@ mod test { fn find_node_works() { let (tree, is_descendent_of) = test_fork_tree(); - let node = tree.find_node_where(&"B", &2, &is_descendent_of, &|_| true).unwrap().unwrap(); - assert_eq!((node.hash, node.number), ("A", 1)); + let node = tree.find_node_where(&"B", &20, &is_descendent_of, &|_| true).unwrap().unwrap(); + assert_eq!((node.hash, node.number), ("A", 10)); - let node = tree.find_node_where(&"D", &4, &is_descendent_of, &|_| true).unwrap().unwrap(); - assert_eq!((node.hash, node.number), ("C", 3)); + let node = tree.find_node_where(&"D", &40, &is_descendent_of, &|_| true).unwrap().unwrap(); + assert_eq!((node.hash, node.number), ("C", 30)); - let node = tree.find_node_where(&"O", &5, &is_descendent_of, &|_| true).unwrap().unwrap(); - assert_eq!((node.hash, node.number), ("L", 4)); + let node = tree.find_node_where(&"O", &50, &is_descendent_of, &|_| true).unwrap().unwrap(); + assert_eq!((node.hash, node.number), ("L", 40)); - let node = tree.find_node_where(&"N", &6, &is_descendent_of, &|_| true).unwrap().unwrap(); - assert_eq!((node.hash, node.number), ("M", 5)); + let node = tree.find_node_where(&"N", &60, &is_descendent_of, &|_| true).unwrap().unwrap(); + assert_eq!((node.hash, node.number), ("M", 50)); } #[test] @@ -1516,13 +1593,13 @@ mod test { // Post order traversal requirement for `find_node_index_where` let path = tree - .find_node_index_where(&"N", &6, &is_descendent_of_for_post_order, &|_| true) + .find_node_index_where(&"N", &60, &is_descendent_of_for_post_order, &|_| true) .unwrap() .unwrap(); assert_eq!(path, [0, 1, 0, 0, 0]); // Post order traversal requirement for `import` - let res = tree.import(&"Z", 100, (), &is_descendent_of_for_post_order); + let res = tree.import(&"Z", 100, 10, &is_descendent_of_for_post_order); assert_eq!(res, Ok(false)); assert_eq!( tree.iter().map(|node| *node.0).collect::>(), From 725e47a4252392dd1b045e32861978c3c18e7419 Mon Sep 17 00:00:00 2001 From: Sergejs Kostjucenko <85877331+sergejparity@users.noreply.github.com> Date: Wed, 15 Feb 2023 22:35:16 +0200 Subject: [PATCH 122/558] CI add subkey docker image description and CI job to publish it to the hub.docker.com (#13392) * Add subkey docker image description and ci job to publish it * Add link to docs * fix formatting * fix ci --- scripts/ci/docker/subkey.Dockerfile.README.md | 8 +++++++ scripts/ci/gitlab/pipeline/publish.yml | 21 +++++++++++++++++++ 2 files changed, 29 insertions(+) create mode 100644 scripts/ci/docker/subkey.Dockerfile.README.md diff --git a/scripts/ci/docker/subkey.Dockerfile.README.md b/scripts/ci/docker/subkey.Dockerfile.README.md new file mode 100644 index 000000000..30a5e8212 --- /dev/null +++ b/scripts/ci/docker/subkey.Dockerfile.README.md @@ -0,0 +1,8 @@ +# The `subkey` program is a key management utility for Substrate-based blockchains. You can use the `subkey` program to perform the following tasks: + +* Generate and inspect cryptographically-secure public and private key pairs. +* Restore keys from secret phrases and raw seeds. +* Sign and verify signatures on messages. +* Sign and verify signatures for encoded transactions. +* Derive hierarchical deterministic child key pairs. +* [Documentation](https://docs.substrate.io/reference/command-line-tools/subkey/) \ No newline at end of file diff --git a/scripts/ci/gitlab/pipeline/publish.yml b/scripts/ci/gitlab/pipeline/publish.yml index 4eafb0a05..9f0759036 100644 --- a/scripts/ci/gitlab/pipeline/publish.yml +++ b/scripts/ci/gitlab/pipeline/publish.yml @@ -49,6 +49,19 @@ DOCKER_USER: $Docker_Hub_User_Parity DOCKER_PASS: $Docker_Hub_Pass_Parity +.push-docker-image-description: + stage: publish + extends: + - .kubernetes-env + - .publish-refs + variables: + CI_IMAGE: paritytech/docker-description + DOCKERHUB_REPOSITORY: parity/$PRODUCT + DOCKER_USERNAME: $Docker_Hub_User_Parity + DOCKER_PASSWORD: $Docker_Hub_Pass_Parity + README_FILEPATH: $CI_PROJECT_DIR/scripts/ci/docker/$PRODUCT.Dockerfile.README.md + script: + - echo # Dummy command to satisfy GitLab CI linter. # publish image to docker.io/paritypr, (e.g. for later use in zombienet testing) .build-push-image-temporary: @@ -90,6 +103,14 @@ publish-docker-subkey: variables: PRODUCT: subkey +publish-docker-description-subkey: + extends: .push-docker-image-description + needs: + - job: build-subkey-linux + variables: + PRODUCT: subkey + SHORT_DESCRIPTION: "The subkey program is a key management utility for Substrate-based blockchains." + publish-s3-release: stage: publish extends: From 7732f88c117ceb41b57a51402abd64f888acd013 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alexander=20Thei=C3=9Fen?= Date: Wed, 15 Feb 2023 22:56:50 +0100 Subject: [PATCH 123/558] contracts: Use `proof_size` from benchmarks (#13268) * Avoid reading contract code when it is supplied in the extrinsic * Remove custom proof size injection from schedule * Set benchmarks pov_mode to Measure * Reduce overestimation of code size on re-instrument * ".git/.scripts/commands/bench/bench.sh" pallet dev pallet_contracts * Do not override proof size from benchmark * Do not charge proof size for basic block * Incrase gas limit for tests * Fix deletion queue to also use `proof_size` * Fix tests * Update frame/contracts/src/schedule.rs Co-authored-by: Cyrill Leutwiler * Fix wrong schedule macro invocations * Remove stale docs * ".git/.scripts/commands/bench/bench.sh" pallet dev pallet_contracts * Handle zero components * ".git/.scripts/commands/bench/bench.sh" pallet dev pallet_contracts * Fix instruction weight --------- Co-authored-by: command-bot <> Co-authored-by: Cyrill Leutwiler --- frame/contracts/src/benchmarking/mod.rs | 148 +- frame/contracts/src/schedule.rs | 173 +- frame/contracts/src/storage.rs | 17 +- frame/contracts/src/tests.rs | 8 +- frame/contracts/src/wasm/code_cache.rs | 104 +- frame/contracts/src/wasm/mod.rs | 4 +- frame/contracts/src/weights.rs | 3664 ++++++++++++----------- primitives/weights/src/weight_v2.rs | 76 + 8 files changed, 2173 insertions(+), 2021 deletions(-) diff --git a/frame/contracts/src/benchmarking/mod.rs b/frame/contracts/src/benchmarking/mod.rs index e834508a0..55d315d5c 100644 --- a/frame/contracts/src/benchmarking/mod.rs +++ b/frame/contracts/src/benchmarking/mod.rs @@ -205,13 +205,13 @@ benchmarks! { } // The base weight consumed on processing contracts deletion queue. - #[pov_mode = Ignored] + #[pov_mode = Measured] on_process_deletion_queue_batch {}: { Storage::::process_deletion_queue_batch(Weight::MAX) } #[skip_meta] - #[pov_mode = Ignored] + #[pov_mode = Measured] on_initialize_per_trie_key { let k in 0..1024; let instance = Contract::::with_storage(WasmModule::dummy(), k, T::Schedule::get().limits.payload_len)?; @@ -220,7 +220,7 @@ benchmarks! { Storage::::process_deletion_queue_batch(Weight::MAX) } - #[pov_mode = Ignored] + #[pov_mode = Measured] on_initialize_per_queue_item { let q in 0..1024.min(T::DeletionQueueDepth::get()); for i in 0 .. q { @@ -235,7 +235,7 @@ benchmarks! { // This benchmarks the additional weight that is charged when a contract is executed the // first time after a new schedule was deployed: For every new schedule a contract needs // to re-run the instrumentation once. - #[pov_mode = Ignored] + #[pov_mode = Measured] reinstrument { let c in 0 .. Perbill::from_percent(49).mul_ceil(T::MaxCodeLen::get()); let WasmModule { code, hash, .. } = WasmModule::::sized(c, Location::Call); @@ -252,7 +252,7 @@ benchmarks! { // is responsible. This is achieved by generating all code to the `deploy` function // which is in the wasm module but not executed on `call`. // The results are supposed to be used as `call_with_code_kb(c) - call_with_code_kb(0)`. - #[pov_mode = Ignored] + #[pov_mode = Measured] call_with_code_per_byte { let c in 0 .. T::MaxCodeLen::get(); let instance = Contract::::with_caller( @@ -278,7 +278,7 @@ benchmarks! { // // We cannot let `c` grow to the maximum code size because the code is not allowed // to be larger than the maximum size **after instrumentation**. - #[pov_mode = Ignored] + #[pov_mode = Measured] instantiate_with_code { let c in 0 .. Perbill::from_percent(49).mul_ceil(T::MaxCodeLen::get()); let i in 0 .. code::max_pages::() * 64 * 1024; @@ -310,7 +310,7 @@ benchmarks! { // Instantiate uses a dummy contract constructor to measure the overhead of the instantiate. // `i`: Size of the input in kilobytes. // `s`: Size of the salt in kilobytes. - #[pov_mode = Ignored] + #[pov_mode = Measured] instantiate { let i in 0 .. code::max_pages::() * 64 * 1024; let s in 0 .. code::max_pages::() * 64 * 1024; @@ -342,7 +342,7 @@ benchmarks! { // part of `seal_input`. The costs for invoking a contract of a specific size are not part // of this benchmark because we cannot know the size of the contract when issuing a call // transaction. See `invoke_per_code_kb` for this. - #[pov_mode = Ignored] + #[pov_mode = Measured] call { let data = vec![42u8; 1024]; let instance = Contract::::with_caller( @@ -375,7 +375,7 @@ benchmarks! { // // We cannot let `c` grow to the maximum code size because the code is not allowed // to be larger than the maximum size **after instrumentation**. - #[pov_mode = Ignored] + #[pov_mode = Measured] upload_code { let c in 0 .. Perbill::from_percent(49).mul_ceil(T::MaxCodeLen::get()); let caller = whitelisted_caller(); @@ -392,7 +392,7 @@ benchmarks! { // Removing code does not depend on the size of the contract because all the information // needed to verify the removal claim (refcount, owner) is stored in a separate storage // item (`OwnerInfoOf`). - #[pov_mode = Ignored] + #[pov_mode = Measured] remove_code { let caller = whitelisted_caller(); T::Currency::make_free_balance_be(&caller, caller_funding::()); @@ -409,7 +409,7 @@ benchmarks! { assert!(>::code_removed(&hash)); } - #[pov_mode = Ignored] + #[pov_mode = Measured] set_code { let instance = >::with_caller( whitelisted_caller(), WasmModule::dummy(), vec![], @@ -424,7 +424,7 @@ benchmarks! { assert_eq!(instance.info()?.code_hash, hash); } - #[pov_mode = Ignored] + #[pov_mode = Measured] seal_caller { let r in 0 .. API_BENCHMARK_BATCHES; let instance = Contract::::new(WasmModule::getter( @@ -433,7 +433,7 @@ benchmarks! { let origin = RawOrigin::Signed(instance.caller.clone()); }: call(origin, instance.addr, 0u32.into(), Weight::MAX, None, vec![]) - #[pov_mode = Ignored] + #[pov_mode = Measured] seal_is_contract { let r in 0 .. API_BENCHMARK_BATCHES; let accounts = (0 .. r * API_BENCHMARK_BATCH_SIZE) @@ -471,7 +471,7 @@ benchmarks! { let origin = RawOrigin::Signed(instance.caller.clone()); }: call(origin, instance.addr, 0u32.into(), Weight::MAX, None, vec![]) - #[pov_mode = Ignored] + #[pov_mode = Measured] seal_code_hash { let r in 0 .. API_BENCHMARK_BATCHES; let accounts = (0 .. r * API_BENCHMARK_BATCH_SIZE) @@ -517,7 +517,7 @@ benchmarks! { let origin = RawOrigin::Signed(instance.caller.clone()); }: call(origin, instance.addr, 0u32.into(), Weight::MAX, None, vec![]) - #[pov_mode = Ignored] + #[pov_mode = Measured] seal_own_code_hash { let r in 0 .. API_BENCHMARK_BATCHES; let instance = Contract::::new(WasmModule::getter( @@ -526,7 +526,7 @@ benchmarks! { let origin = RawOrigin::Signed(instance.caller.clone()); }: call(origin, instance.addr, 0u32.into(), Weight::MAX, None, vec![]) - #[pov_mode = Ignored] + #[pov_mode = Measured] seal_caller_is_origin { let r in 0 .. API_BENCHMARK_BATCHES; let code = WasmModule::::from(ModuleDefinition { @@ -547,7 +547,7 @@ benchmarks! { let origin = RawOrigin::Signed(instance.caller.clone()); }: call(origin, instance.addr, 0u32.into(), Weight::MAX, None, vec![]) - #[pov_mode = Ignored] + #[pov_mode = Measured] seal_address { let r in 0 .. API_BENCHMARK_BATCHES; let instance = Contract::::new(WasmModule::getter( @@ -556,7 +556,7 @@ benchmarks! { let origin = RawOrigin::Signed(instance.caller.clone()); }: call(origin, instance.addr, 0u32.into(), Weight::MAX, None, vec![]) - #[pov_mode = Ignored] + #[pov_mode = Measured] seal_gas_left { let r in 0 .. API_BENCHMARK_BATCHES; let instance = Contract::::new(WasmModule::getter( @@ -565,7 +565,7 @@ benchmarks! { let origin = RawOrigin::Signed(instance.caller.clone()); }: call(origin, instance.addr, 0u32.into(), Weight::MAX, None, vec![]) - #[pov_mode = Ignored] + #[pov_mode = Measured] seal_balance { let r in 0 .. API_BENCHMARK_BATCHES; let instance = Contract::::new(WasmModule::getter( @@ -574,7 +574,7 @@ benchmarks! { let origin = RawOrigin::Signed(instance.caller.clone()); }: call(origin, instance.addr, 0u32.into(), Weight::MAX, None, vec![]) - #[pov_mode = Ignored] + #[pov_mode = Measured] seal_value_transferred { let r in 0 .. API_BENCHMARK_BATCHES; let instance = Contract::::new(WasmModule::getter( @@ -583,7 +583,7 @@ benchmarks! { let origin = RawOrigin::Signed(instance.caller.clone()); }: call(origin, instance.addr, 0u32.into(), Weight::MAX, None, vec![]) - #[pov_mode = Ignored] + #[pov_mode = Measured] seal_minimum_balance { let r in 0 .. API_BENCHMARK_BATCHES; let instance = Contract::::new(WasmModule::getter( @@ -592,7 +592,7 @@ benchmarks! { let origin = RawOrigin::Signed(instance.caller.clone()); }: call(origin, instance.addr, 0u32.into(), Weight::MAX, None, vec![]) - #[pov_mode = Ignored] + #[pov_mode = Measured] seal_block_number { let r in 0 .. API_BENCHMARK_BATCHES; let instance = Contract::::new(WasmModule::getter( @@ -601,7 +601,7 @@ benchmarks! { let origin = RawOrigin::Signed(instance.caller.clone()); }: call(origin, instance.addr, 0u32.into(), Weight::MAX, None, vec![]) - #[pov_mode = Ignored] + #[pov_mode = Measured] seal_now { let r in 0 .. API_BENCHMARK_BATCHES; let instance = Contract::::new(WasmModule::getter( @@ -610,7 +610,7 @@ benchmarks! { let origin = RawOrigin::Signed(instance.caller.clone()); }: call(origin, instance.addr, 0u32.into(), Weight::MAX, None, vec![]) - #[pov_mode = Ignored] + #[pov_mode = Measured] seal_weight_to_fee { let r in 0 .. API_BENCHMARK_BATCHES; let pages = code::max_pages::(); @@ -638,7 +638,7 @@ benchmarks! { let origin = RawOrigin::Signed(instance.caller.clone()); }: call(origin, instance.addr, 0u32.into(), Weight::MAX, None, vec![]) - #[pov_mode = Ignored] + #[pov_mode = Measured] seal_gas { let r in 0 .. API_BENCHMARK_BATCHES; let code = WasmModule::::from(ModuleDefinition { @@ -659,7 +659,7 @@ benchmarks! { }: call(origin, instance.addr, 0u32.into(), Weight::MAX, None, vec![]) - #[pov_mode = Ignored] + #[pov_mode = Measured] seal_input { let r in 0 .. API_BENCHMARK_BATCHES; let code = WasmModule::::from(ModuleDefinition { @@ -687,7 +687,7 @@ benchmarks! { let origin = RawOrigin::Signed(instance.caller.clone()); }: call(origin, instance.addr, 0u32.into(), Weight::MAX, None, vec![]) - #[pov_mode = Ignored] + #[pov_mode = Measured] seal_input_per_kb { let n in 0 .. code::max_pages::() * 64; let pages = code::max_pages::(); @@ -721,7 +721,7 @@ benchmarks! { // We cannot call `seal_return` multiple times. Therefore our weight determination is not // as precise as with other APIs. Because this function can only be called once per // contract it cannot be used as an attack vector. - #[pov_mode = Ignored] + #[pov_mode = Measured] seal_return { let r in 0 .. 1; let code = WasmModule::::from(ModuleDefinition { @@ -744,7 +744,7 @@ benchmarks! { let origin = RawOrigin::Signed(instance.caller.clone()); }: call(origin, instance.addr, 0u32.into(), Weight::MAX, None, vec![]) - #[pov_mode = Ignored] + #[pov_mode = Measured] seal_return_per_kb { let n in 0 .. code::max_pages::() * 64; let code = WasmModule::::from(ModuleDefinition { @@ -769,7 +769,7 @@ benchmarks! { }: call(origin, instance.addr, 0u32.into(), Weight::MAX, None, vec![]) // The same argument as for `seal_return` is true here. - #[pov_mode = Ignored] + #[pov_mode = Measured] seal_terminate { let r in 0 .. 1; let beneficiary = account::("beneficiary", 0, 0); @@ -812,7 +812,7 @@ benchmarks! { // We benchmark only for the maximum subject length. We assume that this is some lowish // number (< 1 KB). Therefore we are not overcharging too much in case a smaller subject is // used. - #[pov_mode = Ignored] + #[pov_mode = Measured] seal_random { let r in 0 .. API_BENCHMARK_BATCHES; let pages = code::max_pages::(); @@ -847,7 +847,7 @@ benchmarks! { // Overhead of calling the function without any topic. // We benchmark for the worst case (largest event). - #[pov_mode = Ignored] + #[pov_mode = Measured] seal_deposit_event { let r in 0 .. API_BENCHMARK_BATCHES; let code = WasmModule::::from(ModuleDefinition { @@ -874,7 +874,7 @@ benchmarks! { // Benchmark the overhead that topics generate. // `t`: Number of topics // `n`: Size of event payload in kb - #[pov_mode = Ignored] + #[pov_mode = Measured] seal_deposit_event_per_topic_and_kb { let t in 0 .. T::Schedule::get().limits.event_topics; let n in 0 .. T::Schedule::get().limits.payload_len / 1024; @@ -913,7 +913,7 @@ benchmarks! { // Benchmark debug_message call with zero input data. // Whereas this function is used in RPC mode only, it still should be secured // against an excessive use. - #[pov_mode = Ignored] + #[pov_mode = Measured] seal_debug_message { let r in 0 .. API_BENCHMARK_BATCHES; let code = WasmModule::::from(ModuleDefinition { @@ -1003,7 +1003,7 @@ benchmarks! { // because re-writing at an existing key is always more expensive than writing // it at a virgin key. #[skip_meta] - #[pov_mode = Ignored] + #[pov_mode = Measured] seal_set_storage { let r in 0 .. API_BENCHMARK_BATCHES/2; let max_key_len = T::MaxStorageKeyLen::get(); @@ -1052,7 +1052,7 @@ benchmarks! { }: call(origin, instance.addr, 0u32.into(), Weight::MAX, None, vec![]) #[skip_meta] - #[pov_mode = Ignored] + #[pov_mode = Measured] seal_set_storage_per_new_kb { let n in 0 .. T::Schedule::get().limits.payload_len / 2048; // half of the max payload_len in kb let max_key_len = T::MaxStorageKeyLen::get(); @@ -1101,7 +1101,7 @@ benchmarks! { }: call(origin, instance.addr, 0u32.into(), Weight::MAX, None, vec![]) #[skip_meta] - #[pov_mode = Ignored] + #[pov_mode = Measured] seal_set_storage_per_old_kb { let n in 0 .. T::Schedule::get().limits.payload_len / 2048; // half of the max payload_len in kb let max_key_len = T::MaxStorageKeyLen::get(); @@ -1154,7 +1154,7 @@ benchmarks! { // deleting a non existing key. We generate keys of a maximum length, and have to // reduce batch size in order to make resulting contract code size less than MaxCodeLen. #[skip_meta] - #[pov_mode = Ignored] + #[pov_mode = Measured] seal_clear_storage { let r in 0 .. API_BENCHMARK_BATCHES/2; let max_key_len = T::MaxStorageKeyLen::get(); @@ -1202,7 +1202,7 @@ benchmarks! { }: call(origin, instance.addr, 0u32.into(), Weight::MAX, None, vec![]) #[skip_meta] - #[pov_mode = Ignored] + #[pov_mode = Measured] seal_clear_storage_per_kb { let n in 0 .. T::Schedule::get().limits.payload_len / 2048; // half of the max payload_len in kb let max_key_len = T::MaxStorageKeyLen::get(); @@ -1250,7 +1250,7 @@ benchmarks! { // We make sure that all storage accesses are to unique keys. #[skip_meta] - #[pov_mode = Ignored] + #[pov_mode = Measured] seal_get_storage { let r in 0 .. API_BENCHMARK_BATCHES/2; let max_key_len = T::MaxStorageKeyLen::get(); @@ -1305,7 +1305,7 @@ benchmarks! { }: call(origin, instance.addr, 0u32.into(), Weight::MAX, None, vec![]) #[skip_meta] - #[pov_mode = Ignored] + #[pov_mode = Measured] seal_get_storage_per_kb { let n in 0 .. T::Schedule::get().limits.payload_len / 2048; // half of the max payload_len in kb let max_key_len = T::MaxStorageKeyLen::get(); @@ -1361,7 +1361,7 @@ benchmarks! { // We make sure that all storage accesses are to unique keys. #[skip_meta] - #[pov_mode = Ignored] + #[pov_mode = Measured] seal_contains_storage { let r in 0 .. API_BENCHMARK_BATCHES/2; let max_key_len = T::MaxStorageKeyLen::get(); @@ -1410,7 +1410,7 @@ benchmarks! { }: call(origin, instance.addr, 0u32.into(), Weight::MAX, None, vec![]) #[skip_meta] - #[pov_mode = Ignored] + #[pov_mode = Measured] seal_contains_storage_per_kb { let n in 0 .. T::Schedule::get().limits.payload_len / 2048; // half of the max payload_len in kb let max_key_len = T::MaxStorageKeyLen::get(); @@ -1458,7 +1458,7 @@ benchmarks! { }: call(origin, instance.addr, 0u32.into(), Weight::MAX, None, vec![]) #[skip_meta] - #[pov_mode = Ignored] + #[pov_mode = Measured] seal_take_storage { let r in 0 .. API_BENCHMARK_BATCHES/2; let max_key_len = T::MaxStorageKeyLen::get(); @@ -1513,7 +1513,7 @@ benchmarks! { }: call(origin, instance.addr, 0u32.into(), Weight::MAX, None, vec![]) #[skip_meta] - #[pov_mode = Ignored] + #[pov_mode = Measured] seal_take_storage_per_kb { let n in 0 .. T::Schedule::get().limits.payload_len / 2048; // half of the max payload_len in kb let max_key_len = T::MaxStorageKeyLen::get(); @@ -1568,7 +1568,7 @@ benchmarks! { }: call(origin, instance.addr, 0u32.into(), Weight::MAX, None, vec![]) // We transfer to unique accounts. - #[pov_mode = Ignored] + #[pov_mode = Measured] seal_transfer { let r in 0 .. API_BENCHMARK_BATCHES; let accounts = (0..r * API_BENCHMARK_BATCH_SIZE) @@ -1622,7 +1622,7 @@ benchmarks! { } // We call unique accounts. - #[pov_mode = Ignored] + #[pov_mode = Measured] seal_call { let r in 0 .. API_BENCHMARK_BATCHES; let dummy_code = WasmModule::::dummy_with_bytes(0); @@ -1681,7 +1681,7 @@ benchmarks! { let origin = RawOrigin::Signed(instance.caller.clone()); }: call(origin, instance.addr, 0u32.into(), Weight::MAX, None, vec![]) - #[pov_mode = Ignored] + #[pov_mode = Measured] seal_delegate_call { let r in 0 .. API_BENCHMARK_BATCHES; let hashes = (0..r * API_BENCHMARK_BATCH_SIZE) @@ -1734,7 +1734,7 @@ benchmarks! { let origin = RawOrigin::Signed(instance.caller); }: call(origin, callee, 0u32.into(), Weight::MAX, None, vec![]) - #[pov_mode = Ignored] + #[pov_mode = Measured] seal_call_per_transfer_clone_kb { let t in 0 .. 1; let c in 0 .. code::max_pages::() * 64; @@ -1793,7 +1793,7 @@ benchmarks! { }: call(origin, instance.addr, 0u32.into(), Weight::MAX, None, bytes) // We assume that every instantiate sends at least the minimum balance. - #[pov_mode = Ignored] + #[pov_mode = Measured] seal_instantiate { let r in 0 .. API_BENCHMARK_BATCHES; let hashes = (0..r * API_BENCHMARK_BATCH_SIZE) @@ -1907,7 +1907,7 @@ benchmarks! { } } - #[pov_mode = Ignored] + #[pov_mode = Measured] seal_instantiate_per_transfer_input_salt_kb { let t in 0 .. 1; let i in 0 .. (code::max_pages::() - 1) * 64; @@ -2002,7 +2002,7 @@ benchmarks! { }: call(origin, instance.addr, 0u32.into(), Weight::MAX, None, vec![]) // Only the overhead of calling the function itself with minimal arguments. - #[pov_mode = Ignored] + #[pov_mode = Measured] seal_hash_sha2_256 { let r in 0 .. 1; let instance = Contract::::new(WasmModule::hasher( @@ -2012,7 +2012,7 @@ benchmarks! { }: call(origin, instance.addr, 0u32.into(), Weight::MAX, None, vec![]) // `n`: Input to hash in kilobytes - #[pov_mode = Ignored] + #[pov_mode = Measured] seal_hash_sha2_256_per_kb { let n in 0 .. code::max_pages::() * 64; let instance = Contract::::new(WasmModule::hasher( @@ -2022,7 +2022,7 @@ benchmarks! { }: call(origin, instance.addr, 0u32.into(), Weight::MAX, None, vec![]) // Only the overhead of calling the function itself with minimal arguments. - #[pov_mode = Ignored] + #[pov_mode = Measured] seal_hash_keccak_256 { let r in 0 .. 1; let instance = Contract::::new(WasmModule::hasher( @@ -2032,7 +2032,7 @@ benchmarks! { }: call(origin, instance.addr, 0u32.into(), Weight::MAX, None, vec![]) // `n`: Input to hash in kilobytes - #[pov_mode = Ignored] + #[pov_mode = Measured] seal_hash_keccak_256_per_kb { let n in 0 .. code::max_pages::() * 64; let instance = Contract::::new(WasmModule::hasher( @@ -2042,7 +2042,7 @@ benchmarks! { }: call(origin, instance.addr, 0u32.into(), Weight::MAX, None, vec![]) // Only the overhead of calling the function itself with minimal arguments. - #[pov_mode = Ignored] + #[pov_mode = Measured] seal_hash_blake2_256 { let r in 0 .. 1; let instance = Contract::::new(WasmModule::hasher( @@ -2052,7 +2052,7 @@ benchmarks! { }: call(origin, instance.addr, 0u32.into(), Weight::MAX, None, vec![]) // `n`: Input to hash in kilobytes - #[pov_mode = Ignored] + #[pov_mode = Measured] seal_hash_blake2_256_per_kb { let n in 0 .. code::max_pages::() * 64; let instance = Contract::::new(WasmModule::hasher( @@ -2062,7 +2062,7 @@ benchmarks! { }: call(origin, instance.addr, 0u32.into(), Weight::MAX, None, vec![]) // Only the overhead of calling the function itself with minimal arguments. - #[pov_mode = Ignored] + #[pov_mode = Measured] seal_hash_blake2_128 { let r in 0 .. 1; let instance = Contract::::new(WasmModule::hasher( @@ -2072,7 +2072,7 @@ benchmarks! { }: call(origin, instance.addr, 0u32.into(), Weight::MAX, None, vec![]) // `n`: Input to hash in kilobytes - #[pov_mode = Ignored] + #[pov_mode = Measured] seal_hash_blake2_128_per_kb { let n in 0 .. code::max_pages::() * 64; let instance = Contract::::new(WasmModule::hasher( @@ -2083,7 +2083,7 @@ benchmarks! { // Only calling the function itself with valid arguments. // It generates different private keys and signatures for the message "Hello world". - #[pov_mode = Ignored] + #[pov_mode = Measured] seal_ecdsa_recover { let r in 0 .. 1; @@ -2132,7 +2132,7 @@ benchmarks! { // Only calling the function itself for the list of // generated different ECDSA keys. - #[pov_mode = Ignored] + #[pov_mode = Measured] seal_ecdsa_to_eth_address { let r in 0 .. 1; let key_type = sp_core::crypto::KeyTypeId(*b"code"); @@ -2168,7 +2168,7 @@ benchmarks! { let origin = RawOrigin::Signed(instance.caller.clone()); }: call(origin, instance.addr, 0u32.into(), Weight::MAX, None, vec![]) - #[pov_mode = Ignored] + #[pov_mode = Measured] seal_set_code_hash { let r in 0 .. API_BENCHMARK_BATCHES; let code_hashes = (0..r * API_BENCHMARK_BATCH_SIZE) @@ -2209,7 +2209,7 @@ benchmarks! { let origin = RawOrigin::Signed(instance.caller.clone()); }: call(origin, instance.addr, 0u32.into(), Weight::MAX, None, vec![]) - #[pov_mode = Ignored] + #[pov_mode = Measured] seal_reentrance_count { let r in 0 .. API_BENCHMARK_BATCHES; let code = WasmModule::::from(ModuleDefinition { @@ -2230,7 +2230,7 @@ benchmarks! { let origin = RawOrigin::Signed(instance.caller.clone()); }: call(origin, instance.addr, 0u32.into(), Weight::MAX, None, vec![]) - #[pov_mode = Ignored] + #[pov_mode = Measured] seal_account_reentrance_count { let r in 0 .. API_BENCHMARK_BATCHES; let dummy_code = WasmModule::::dummy_with_bytes(0); @@ -2264,7 +2264,7 @@ benchmarks! { let origin = RawOrigin::Signed(instance.caller.clone()); }: call(origin, instance.addr, 0u32.into(), Weight::MAX, None, vec![]) - #[pov_mode = Ignored] + #[pov_mode = Measured] seal_instantiation_nonce { let r in 0 .. API_BENCHMARK_BATCHES; let code = WasmModule::::from(ModuleDefinition { @@ -3103,7 +3103,7 @@ benchmarks! { // This is no benchmark. It merely exist to have an easy way to pretty print the curently // configured `Schedule` during benchmark development. // It can be outputed using the following command: - // cargo run --manifest-path=bin/node/cli/Cargo.toml --release \ + // cargo run --manifest-path=bin/node/cli/Cargo.toml \ // --features runtime-benchmarks -- benchmark pallet --extra --dev --execution=native \ // -p pallet_contracts -e print_schedule --no-median-slopes --no-min-squares #[extra] @@ -3111,17 +3111,15 @@ benchmarks! { print_schedule { #[cfg(feature = "std")] { - let weight_per_key = T::WeightInfo::on_initialize_per_trie_key(1) - - T::WeightInfo::on_initialize_per_trie_key(0); - let weight_per_queue_item = T::WeightInfo::on_initialize_per_queue_item(1) - - T::WeightInfo::on_initialize_per_queue_item(0); let weight_limit = T::DeletionWeightLimit::get(); - let queue_depth: u64 = T::DeletionQueueDepth::get().into(); + let max_queue_depth = T::DeletionQueueDepth::get() as usize; + let empty_queue_throughput = Storage::::deletion_budget(0, weight_limit); + let full_queue_throughput = Storage::::deletion_budget(max_queue_depth, weight_limit); println!("{:#?}", Schedule::::default()); println!("###############################################"); + println!("Lazy deletion weight per key: {}", empty_queue_throughput.0); println!("Lazy deletion throughput per block (empty queue, full queue): {}, {}", - weight_limit / weight_per_key.ref_time(), - (weight_limit - weight_per_queue_item * queue_depth) / weight_per_key.ref_time(), + empty_queue_throughput.1, full_queue_throughput.1, ); } #[cfg(not(feature = "std"))] @@ -3133,7 +3131,7 @@ benchmarks! { // `g` is used to enable gas instrumentation to compare the performance impact of // that instrumentation at runtime. #[extra] - #[pov_mode = Ignored] + #[pov_mode = Measured] ink_erc20_transfer { let g in 0 .. 1; let gas_metering = g != 0; @@ -3172,7 +3170,7 @@ benchmarks! { // `g` is used to enable gas instrumentation to compare the performance impact of // that instrumentation at runtime. #[extra] - #[pov_mode = Ignored] + #[pov_mode = Measured] solang_erc20_transfer { let g in 0 .. 1; let gas_metering = g != 0; diff --git a/frame/contracts/src/schedule.rs b/frame/contracts/src/schedule.rs index 5e4798df1..ccfba941e 100644 --- a/frame/contracts/src/schedule.rs +++ b/frame/contracts/src/schedule.rs @@ -447,7 +447,7 @@ macro_rules! call_zero { macro_rules! cost_args { ($name:ident, $( $arg: expr ),+) => { - (T::WeightInfo::$name($( $arg ),+).saturating_sub(call_zero!($name, $( $arg ),+))).ref_time() + (T::WeightInfo::$name($( $arg ),+).saturating_sub(call_zero!($name, $( $arg ),+))) } } @@ -459,7 +459,7 @@ macro_rules! cost_batched_args { macro_rules! cost_instr_no_params_with_batch_size { ($name:ident, $batch_size:expr) => { - (cost_args!($name, 1) / u64::from($batch_size)) as u32 + (cost_args!($name, 1).ref_time() / u64::from($batch_size)) as u32 }; } @@ -514,12 +514,6 @@ macro_rules! cost_byte_batched { }; } -macro_rules! to_weight { - ($ref_time:expr $(, $proof_size:expr )?) => { - Weight::from_ref_time($ref_time)$(.set_proof_size($proof_size))? - }; -} - impl Default for Limits { fn default() -> Self { Self { @@ -554,8 +548,8 @@ impl Default for InstructionWeights { br_table_per_entry: cost_instr!(instr_br_table_per_entry, 0), call: cost_instr!(instr_call, 2), call_indirect: cost_instr!(instr_call_indirect, 3), - call_indirect_per_param: cost_instr!(instr_call_indirect_per_param, 1), - call_per_local: cost_instr!(instr_call_per_local, 1), + call_indirect_per_param: cost_instr!(instr_call_indirect_per_param, 0), + call_per_local: cost_instr!(instr_call_per_local, 0), local_get: cost_instr!(instr_local_get, 1), local_set: cost_instr!(instr_local_set, 1), local_tee: cost_instr!(instr_local_tee, 2), @@ -601,116 +595,91 @@ impl Default for InstructionWeights { } impl Default for HostFnWeights { - /// PoV should contain all trie nodes that are read during state transition (i.e. block - /// production). Hence we need to charge the `proof_size` weight for every host function which - /// reads storage, namely: - /// - get_storage, - /// - take_storage, - /// - contains_storage, - /// - clear_storage, - /// - set_storage. - /// - /// The last two functions write to storage, but they also do read storage in order to return - /// the size of the pre-existed value. Till we have PoV benchmarks implemented, we approximate - /// `proof_size` as being equal to the size of storage read. fn default() -> Self { Self { - caller: to_weight!(cost_batched!(seal_caller)), - is_contract: to_weight!(cost_batched!(seal_is_contract)), - code_hash: to_weight!(cost_batched!(seal_code_hash)), - own_code_hash: to_weight!(cost_batched!(seal_own_code_hash)), - caller_is_origin: to_weight!(cost_batched!(seal_caller_is_origin)), - address: to_weight!(cost_batched!(seal_address)), - gas_left: to_weight!(cost_batched!(seal_gas_left)), - balance: to_weight!(cost_batched!(seal_balance)), - value_transferred: to_weight!(cost_batched!(seal_value_transferred)), - minimum_balance: to_weight!(cost_batched!(seal_minimum_balance)), - block_number: to_weight!(cost_batched!(seal_block_number)), - now: to_weight!(cost_batched!(seal_now)), - weight_to_fee: to_weight!(cost_batched!(seal_weight_to_fee)), - gas: to_weight!(cost_batched!(seal_gas)), - input: to_weight!(cost_batched!(seal_input)), - input_per_byte: to_weight!(cost_byte_batched!(seal_input_per_kb)), - r#return: to_weight!(cost!(seal_return)), - return_per_byte: to_weight!(cost_byte!(seal_return_per_kb)), - terminate: to_weight!(cost!(seal_terminate)), - random: to_weight!(cost_batched!(seal_random)), - deposit_event: to_weight!(cost_batched!(seal_deposit_event)), - deposit_event_per_topic: to_weight!(cost_batched_args!( - seal_deposit_event_per_topic_and_kb, - 1, - 0 - )), - deposit_event_per_byte: to_weight!(cost_byte_batched_args!( + caller: cost_batched!(seal_caller), + is_contract: cost_batched!(seal_is_contract), + code_hash: cost_batched!(seal_code_hash), + own_code_hash: cost_batched!(seal_own_code_hash), + caller_is_origin: cost_batched!(seal_caller_is_origin), + address: cost_batched!(seal_address), + gas_left: cost_batched!(seal_gas_left), + balance: cost_batched!(seal_balance), + value_transferred: cost_batched!(seal_value_transferred), + minimum_balance: cost_batched!(seal_minimum_balance), + block_number: cost_batched!(seal_block_number), + now: cost_batched!(seal_now), + weight_to_fee: cost_batched!(seal_weight_to_fee), + // Manually remove proof size from basic block cost. + // + // Due to imperfect benchmarking some host functions incur a small + // amount of proof size. Usually this is ok. However, charging a basic block is such + // a frequent operation that this would be a vast overestimation. + gas: cost_batched!(seal_gas).set_proof_size(0), + input: cost_batched!(seal_input), + input_per_byte: cost_byte_batched!(seal_input_per_kb), + r#return: cost!(seal_return), + return_per_byte: cost_byte!(seal_return_per_kb), + terminate: cost!(seal_terminate), + random: cost_batched!(seal_random), + deposit_event: cost_batched!(seal_deposit_event), + deposit_event_per_topic: cost_batched_args!(seal_deposit_event_per_topic_and_kb, 1, 0), + deposit_event_per_byte: cost_byte_batched_args!( seal_deposit_event_per_topic_and_kb, 0, 1 - )), - debug_message: to_weight!(cost_batched!(seal_debug_message)), - debug_message_per_byte: to_weight!(cost_byte!(seal_debug_message_per_kb)), - set_storage: to_weight!(cost_batched!(seal_set_storage), 1024u64), - set_code_hash: to_weight!(cost_batched!(seal_set_code_hash)), - set_storage_per_new_byte: to_weight!(cost_byte_batched!(seal_set_storage_per_new_kb)), - set_storage_per_old_byte: to_weight!( - cost_byte_batched!(seal_set_storage_per_old_kb), - 1u64 - ), - clear_storage: to_weight!(cost_batched!(seal_clear_storage), 1024u64), - clear_storage_per_byte: to_weight!(cost_byte_batched!(seal_clear_storage_per_kb), 1u64), - contains_storage: to_weight!(cost_batched!(seal_contains_storage), 1024u64), - contains_storage_per_byte: to_weight!( - cost_byte_batched!(seal_contains_storage_per_kb), - 1u64 ), - get_storage: to_weight!(cost_batched!(seal_get_storage), 1024u64), - get_storage_per_byte: to_weight!(cost_byte_batched!(seal_get_storage_per_kb), 1u64), - take_storage: to_weight!(cost_batched!(seal_take_storage), 1024u64), - take_storage_per_byte: to_weight!(cost_byte_batched!(seal_take_storage_per_kb), 1u64), - transfer: to_weight!(cost_batched!(seal_transfer)), - call: to_weight!(cost_batched!(seal_call)), - delegate_call: to_weight!(cost_batched!(seal_delegate_call)), - call_transfer_surcharge: to_weight!(cost_batched_args!( - seal_call_per_transfer_clone_kb, - 1, - 0 - )), - call_per_cloned_byte: to_weight!(cost_batched_args!( - seal_call_per_transfer_clone_kb, - 0, - 1 - )), - instantiate: to_weight!(cost_batched!(seal_instantiate)), - instantiate_transfer_surcharge: to_weight!(cost_byte_batched_args!( + debug_message: cost_batched!(seal_debug_message), + debug_message_per_byte: cost_byte!(seal_debug_message_per_kb), + set_storage: cost_batched!(seal_set_storage), + set_code_hash: cost_batched!(seal_set_code_hash), + set_storage_per_new_byte: cost_byte_batched!(seal_set_storage_per_new_kb), + set_storage_per_old_byte: cost_byte_batched!(seal_set_storage_per_old_kb), + clear_storage: cost_batched!(seal_clear_storage), + clear_storage_per_byte: cost_byte_batched!(seal_clear_storage_per_kb), + contains_storage: cost_batched!(seal_contains_storage), + contains_storage_per_byte: cost_byte_batched!(seal_contains_storage_per_kb), + get_storage: cost_batched!(seal_get_storage), + get_storage_per_byte: cost_byte_batched!(seal_get_storage_per_kb), + take_storage: cost_batched!(seal_take_storage), + take_storage_per_byte: cost_byte_batched!(seal_take_storage_per_kb), + transfer: cost_batched!(seal_transfer), + call: cost_batched!(seal_call), + delegate_call: cost_batched!(seal_delegate_call), + call_transfer_surcharge: cost_batched_args!(seal_call_per_transfer_clone_kb, 1, 0), + call_per_cloned_byte: cost_byte_batched_args!(seal_call_per_transfer_clone_kb, 0, 1), + instantiate: cost_batched!(seal_instantiate), + instantiate_transfer_surcharge: cost_batched_args!( seal_instantiate_per_transfer_input_salt_kb, 1, 0, 0 - )), - instantiate_per_input_byte: to_weight!(cost_byte_batched_args!( + ), + instantiate_per_input_byte: cost_byte_batched_args!( seal_instantiate_per_transfer_input_salt_kb, 0, 1, 0 - )), - instantiate_per_salt_byte: to_weight!(cost_byte_batched_args!( + ), + instantiate_per_salt_byte: cost_byte_batched_args!( seal_instantiate_per_transfer_input_salt_kb, 0, 0, 1 - )), - hash_sha2_256: to_weight!(cost_batched!(seal_hash_sha2_256)), - hash_sha2_256_per_byte: to_weight!(cost_byte_batched!(seal_hash_sha2_256_per_kb)), - hash_keccak_256: to_weight!(cost_batched!(seal_hash_keccak_256)), - hash_keccak_256_per_byte: to_weight!(cost_byte_batched!(seal_hash_keccak_256_per_kb)), - hash_blake2_256: to_weight!(cost_batched!(seal_hash_blake2_256)), - hash_blake2_256_per_byte: to_weight!(cost_byte_batched!(seal_hash_blake2_256_per_kb)), - hash_blake2_128: to_weight!(cost_batched!(seal_hash_blake2_128)), - hash_blake2_128_per_byte: to_weight!(cost_byte_batched!(seal_hash_blake2_128_per_kb)), - ecdsa_recover: to_weight!(cost_batched!(seal_ecdsa_recover)), - ecdsa_to_eth_address: to_weight!(cost_batched!(seal_ecdsa_to_eth_address)), - reentrance_count: to_weight!(cost_batched!(seal_reentrance_count)), - account_reentrance_count: to_weight!(cost_batched!(seal_account_reentrance_count)), - instantiation_nonce: to_weight!(cost_batched!(seal_instantiation_nonce)), + ), + hash_sha2_256: cost_batched!(seal_hash_sha2_256), + hash_sha2_256_per_byte: cost_byte_batched!(seal_hash_sha2_256_per_kb), + hash_keccak_256: cost_batched!(seal_hash_keccak_256), + hash_keccak_256_per_byte: cost_byte_batched!(seal_hash_keccak_256_per_kb), + hash_blake2_256: cost_batched!(seal_hash_blake2_256), + hash_blake2_256_per_byte: cost_byte_batched!(seal_hash_blake2_256_per_kb), + hash_blake2_128: cost_batched!(seal_hash_blake2_128), + hash_blake2_128_per_byte: cost_byte_batched!(seal_hash_blake2_128_per_kb), + ecdsa_recover: cost_batched!(seal_ecdsa_recover), + ecdsa_to_eth_address: cost_batched!(seal_ecdsa_to_eth_address), + reentrance_count: cost_batched!(seal_reentrance_count), + account_reentrance_count: cost_batched!(seal_account_reentrance_count), + instantiation_nonce: cost_batched!(seal_instantiation_nonce), _phantom: PhantomData, } } diff --git a/frame/contracts/src/storage.rs b/frame/contracts/src/storage.rs index d40cc755c..bf030a2bd 100644 --- a/frame/contracts/src/storage.rs +++ b/frame/contracts/src/storage.rs @@ -245,13 +245,12 @@ impl Storage { /// Calculates the weight that is necessary to remove one key from the trie and how many /// of those keys can be deleted from the deletion queue given the supplied queue length /// and weight limit. - pub fn deletion_budget(queue_len: usize, weight_limit: Weight) -> (u64, u32) { + pub fn deletion_budget(queue_len: usize, weight_limit: Weight) -> (Weight, u32) { let base_weight = T::WeightInfo::on_process_deletion_queue_batch(); let weight_per_queue_item = T::WeightInfo::on_initialize_per_queue_item(1) - T::WeightInfo::on_initialize_per_queue_item(0); - let weight_per_key = (T::WeightInfo::on_initialize_per_trie_key(1) - - T::WeightInfo::on_initialize_per_trie_key(0)) - .ref_time(); + let weight_per_key = T::WeightInfo::on_initialize_per_trie_key(1) - + T::WeightInfo::on_initialize_per_trie_key(0); let decoding_weight = weight_per_queue_item.saturating_mul(queue_len as u64); // `weight_per_key` being zero makes no sense and would constitute a failure to @@ -259,9 +258,8 @@ impl Storage { let key_budget = weight_limit .saturating_sub(base_weight) .saturating_sub(decoding_weight) - .checked_div(weight_per_key) - .unwrap_or(Weight::zero()) - .ref_time() as u32; + .checked_div_per_component(&weight_per_key) + .unwrap_or(0) as u32; (weight_per_key, key_budget) } @@ -306,10 +304,7 @@ impl Storage { } >::put(queue); - let ref_time_weight = weight_limit - .ref_time() - .saturating_sub(weight_per_key.saturating_mul(u64::from(remaining_key_budget))); - Weight::from_ref_time(ref_time_weight) + weight_limit.saturating_sub(weight_per_key.saturating_mul(u64::from(remaining_key_budget))) } /// Generates a unique trie id by returning `hash(account_id ++ nonce)`. diff --git a/frame/contracts/src/tests.rs b/frame/contracts/src/tests.rs index 958cbf772..cab426ad1 100644 --- a/frame/contracts/src/tests.rs +++ b/frame/contracts/src/tests.rs @@ -383,7 +383,7 @@ impl Contains for TestFilter { } parameter_types! { - pub const DeletionWeightLimit: Weight = Weight::from_ref_time(500_000_000_000); + pub const DeletionWeightLimit: Weight = GAS_LIMIT; pub static UnstableInterface: bool = true; } @@ -416,7 +416,7 @@ pub const BOB: AccountId32 = AccountId32::new([2u8; 32]); pub const CHARLIE: AccountId32 = AccountId32::new([3u8; 32]); pub const DJANGO: AccountId32 = AccountId32::new([4u8; 32]); -pub const GAS_LIMIT: Weight = Weight::from_parts(100_000_000_000, 512 * 1024); +pub const GAS_LIMIT: Weight = Weight::from_parts(100_000_000_000, 3 * 1024 * 1024); pub struct ExtBuilder { existential_deposit: u64, @@ -2322,7 +2322,7 @@ fn lazy_removal_does_no_run_on_low_remaining_weight() { fn lazy_removal_does_not_use_all_weight() { let (code, _hash) = compile_module::("self_destruct").unwrap(); - let weight_limit = Weight::from_ref_time(5_000_000_000); + let weight_limit = Weight::from_parts(5_000_000_000, 100 * 1024); let mut ext = ExtBuilder::default().existential_deposit(50).build(); let (trie, vals, weight_per_key) = ext.execute_with(|| { @@ -2396,7 +2396,7 @@ fn lazy_removal_does_not_use_all_weight() { let weight_used = Storage::::process_deletion_queue_batch(weight_limit); // We have one less key in our trie than our weight limit suffices for - assert_eq!(weight_used, weight_limit - Weight::from_ref_time(weight_per_key)); + assert_eq!(weight_used, weight_limit - weight_per_key); // All the keys are removed for val in vals { diff --git a/frame/contracts/src/wasm/code_cache.rs b/frame/contracts/src/wasm/code_cache.rs index 410d26921..385ed54cc 100644 --- a/frame/contracts/src/wasm/code_cache.rs +++ b/frame/contracts/src/wasm/code_cache.rs @@ -50,53 +50,61 @@ use sp_std::vec; /// under the specified `code_hash`. pub fn store(mut module: PrefabWasmModule, instantiated: bool) -> DispatchResult { let code_hash = sp_std::mem::take(&mut module.code_hash); - >::mutate(&code_hash, |existing| match existing { - Some(existing) => { - // We instrument any uploaded contract anyways. We might as well store it to save - // a potential re-instrumentation later. - existing.code = module.code; - existing.instruction_weights_version = module.instruction_weights_version; - // When the code was merely uploaded but not instantiated we can skip this. - if instantiated { - >::mutate(&code_hash, |owner_info| { - if let Some(owner_info) = owner_info { - owner_info.refcount = owner_info.refcount.checked_add(1).expect( - " - refcount is 64bit. Generating this overflow would require to store - _at least_ 18 exabyte of data assuming that a contract consumes only - one byte of data. Any node would run out of storage space before hitting - this overflow. - qed - ", - ); - } - }) - } - Ok(()) - }, - None => { - let orig_code = module.original_code.take().expect( - " + >::mutate(&code_hash, |owner_info| { + match owner_info { + // Instantiate existing contract. + // + // No need to update the `CodeStorage` as any re-instrumentation eagerly saves + // the re-instrumented code. + Some(owner_info) if instantiated => { + owner_info.refcount = owner_info.refcount.checked_add(1).expect( + " + refcount is 64bit. Generating this overflow would require to store + _at least_ 18 exabyte of data assuming that a contract consumes only + one byte of data. Any node would run out of storage space before hitting + this overflow. + qed + ", + ); + Ok(()) + }, + // Re-upload existing contract without executing it. + // + // We are careful here to just overwrite the code to not include it into the PoV. + // We do this because the uploaded code was instrumented with the latest schedule + // and hence we persist those changes. Otherwise the next execution will pay again + // for the instrumentation. + Some(_) => { + >::insert(&code_hash, module); + Ok(()) + }, + // Upload a new contract. + // + // We need to write all data structures and collect the deposit. + None => { + let orig_code = module.original_code.take().expect( + " If an executable isn't in storage it was uploaded. If it was uploaded the original code must exist. qed ", - ); - let mut owner_info = module.owner_info.take().expect( - "If an executable isn't in storage it was uploaded. + ); + let mut new_owner_info = module.owner_info.take().expect( + "If an executable isn't in storage it was uploaded. If it was uploaded the owner info was generated and attached. qed ", - ); - // This `None` case happens only in freshly uploaded modules. This means that - // the `owner` is always the origin of the current transaction. - T::Currency::reserve(&owner_info.owner, owner_info.deposit) - .map_err(|_| >::StorageDepositNotEnoughFunds)?; - owner_info.refcount = if instantiated { 1 } else { 0 }; - >::insert(&code_hash, orig_code); - >::insert(&code_hash, owner_info); - *existing = Some(module); - >::deposit_event(vec![code_hash], Event::CodeStored { code_hash }); - Ok(()) - }, + ); + // This `None` case happens only in freshly uploaded modules. This means that + // the `owner` is always the origin of the current transaction. + T::Currency::reserve(&new_owner_info.owner, new_owner_info.deposit) + .map_err(|_| >::StorageDepositNotEnoughFunds)?; + new_owner_info.refcount = if instantiated { 1 } else { 0 }; + >::insert(&code_hash, orig_code); + >::insert(&code_hash, module); + *owner_info = Some(new_owner_info); + >::deposit_event(vec![code_hash], Event::CodeStored { code_hash }); + Ok(()) + }, + } }) } @@ -162,15 +170,16 @@ pub fn load( let charged = gas_meter.charge(CodeToken::Load(max_code_len))?; let mut prefab_module = >::get(code_hash).ok_or(Error::::CodeNotFound)?; - gas_meter.adjust_gas(charged, CodeToken::Load(prefab_module.code.len() as u32)); + let instrumented_code_len = prefab_module.code.len() as u32; + gas_meter.adjust_gas(charged, CodeToken::Load(instrumented_code_len)); prefab_module.code_hash = code_hash; if prefab_module.instruction_weights_version < schedule.instruction_weights.version { // The instruction weights have changed. // We need to re-instrument the code with the new instruction weights. - let charged = gas_meter.charge(CodeToken::Reinstrument(max_code_len))?; - let code_size = reinstrument(&mut prefab_module, schedule)?; - gas_meter.adjust_gas(charged, CodeToken::Reinstrument(code_size)); + let charged = gas_meter.charge(CodeToken::Reinstrument(instrumented_code_len))?; + let orig_code_len = reinstrument(&mut prefab_module, schedule)?; + gas_meter.adjust_gas(charged, CodeToken::Reinstrument(orig_code_len)); } Ok(prefab_module) @@ -224,8 +233,7 @@ impl Token for CodeToken { match *self { Reinstrument(len) => T::WeightInfo::reinstrument(len), Load(len) => T::WeightInfo::call_with_code_per_byte(len) - .saturating_sub(T::WeightInfo::call_with_code_per_byte(0)) - .set_proof_size(len.into()), + .saturating_sub(T::WeightInfo::call_with_code_per_byte(0)), } } } diff --git a/frame/contracts/src/wasm/mod.rs b/frame/contracts/src/wasm/mod.rs index 3ca732618..d25499641 100644 --- a/frame/contracts/src/wasm/mod.rs +++ b/frame/contracts/src/wasm/mod.rs @@ -42,7 +42,7 @@ pub use crate::wasm::{ use crate::{ exec::{ExecResult, Executable, ExportedFunction, Ext}, gas::GasMeter, - AccountIdOf, BalanceOf, CodeHash, CodeStorage, CodeVec, Config, Error, RelaxedCodeVec, + AccountIdOf, BalanceOf, CodeHash, CodeVec, Config, Error, OwnerInfoOf, RelaxedCodeVec, Schedule, }; use codec::{Decode, Encode, MaxEncodedLen}; @@ -191,7 +191,7 @@ impl PrefabWasmModule { /// Returns `0` if the module is already in storage and hence no deposit will /// be charged when storing it. pub fn open_deposit(&self) -> BalanceOf { - if >::contains_key(&self.code_hash) { + if >::contains_key(&self.code_hash) { 0u32.into() } else { // Only already in-storage contracts have their `owner_info` set to `None`. diff --git a/frame/contracts/src/weights.rs b/frame/contracts/src/weights.rs index 50aa30e89..057a9257d 100644 --- a/frame/contracts/src/weights.rs +++ b/frame/contracts/src/weights.rs @@ -18,7 +18,7 @@ //! Autogenerated weights for pallet_contracts //! //! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev -//! DATE: 2023-02-14, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! DATE: 2023-02-15, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` //! WORST CASE MAP SIZE: `1000000` //! HOSTNAME: `bm3`, CPU: `Intel(R) Core(TM) i7-7700K CPU @ 4.20GHz` //! EXECUTION: Some(Wasm), WASM-EXECUTION: Compiled, CHAIN: Some("dev"), DB CACHE: 1024 @@ -173,772 +173,796 @@ pub trait WeightInfo { pub struct SubstrateWeight(PhantomData); impl WeightInfo for SubstrateWeight { /// Storage: Contracts DeletionQueue (r:1 w:0) - /// Proof: Contracts DeletionQueue (max_values: Some(1), max_size: Some(16642), added: 17137, mode: Ignored) + /// Proof: Contracts DeletionQueue (max_values: Some(1), max_size: Some(16642), added: 17137, mode: Measured) fn on_process_deletion_queue_batch() -> Weight { // Proof Size summary in bytes: // Measured: `109` - // Estimated: `0` - // Minimum execution time: 2_564 nanoseconds. - Weight::from_ref_time(2_722_000) - .saturating_add(Weight::from_proof_size(0)) + // Estimated: `604` + // Minimum execution time: 2_507 nanoseconds. + Weight::from_ref_time(2_650_000) + .saturating_add(Weight::from_proof_size(604)) .saturating_add(T::DbWeight::get().reads(1_u64)) } /// Storage: Skipped Metadata (r:0 w:0) - /// Proof Skipped: Skipped Metadata (max_values: None, max_size: None, mode: Ignored) + /// Proof Skipped: Skipped Metadata (max_values: None, max_size: None, mode: Measured) /// The range of component `k` is `[0, 1024]`. fn on_initialize_per_trie_key(k: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `481 + k * (69 ±0)` - // Estimated: `0` - // Minimum execution time: 10_292 nanoseconds. - Weight::from_ref_time(7_474_496) - .saturating_add(Weight::from_proof_size(0)) - // Standard Error: 901 - .saturating_add(Weight::from_ref_time(956_864).saturating_mul(k.into())) + // Estimated: `471 + k * (70 ±0)` + // Minimum execution time: 9_953 nanoseconds. + Weight::from_ref_time(6_467_352) + .saturating_add(Weight::from_proof_size(471)) + // Standard Error: 1_074 + .saturating_add(Weight::from_ref_time(943_946).saturating_mul(k.into())) .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) .saturating_add(T::DbWeight::get().writes((1_u64).saturating_mul(k.into()))) + .saturating_add(Weight::from_proof_size(70).saturating_mul(k.into())) } /// Storage: Contracts DeletionQueue (r:1 w:1) - /// Proof: Contracts DeletionQueue (max_values: Some(1), max_size: Some(16642), added: 17137, mode: Ignored) + /// Proof: Contracts DeletionQueue (max_values: Some(1), max_size: Some(16642), added: 17137, mode: Measured) /// The range of component `q` is `[0, 128]`. fn on_initialize_per_queue_item(q: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `281 + q * (33 ±0)` - // Estimated: `0` - // Minimum execution time: 2_620 nanoseconds. - Weight::from_ref_time(10_288_873) - .saturating_add(Weight::from_proof_size(0)) - // Standard Error: 3_073 - .saturating_add(Weight::from_ref_time(1_148_167).saturating_mul(q.into())) + // Estimated: `763 + q * (33 ±0)` + // Minimum execution time: 2_545 nanoseconds. + Weight::from_ref_time(10_080_106) + .saturating_add(Weight::from_proof_size(763)) + // Standard Error: 2_929 + .saturating_add(Weight::from_ref_time(1_078_265).saturating_mul(q.into())) .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) + .saturating_add(Weight::from_proof_size(33).saturating_mul(q.into())) } /// Storage: Contracts PristineCode (r:1 w:0) - /// Proof: Contracts PristineCode (max_values: None, max_size: Some(125988), added: 128463, mode: Ignored) + /// Proof: Contracts PristineCode (max_values: None, max_size: Some(125988), added: 128463, mode: Measured) /// Storage: Contracts CodeStorage (r:0 w:1) - /// Proof: Contracts CodeStorage (max_values: None, max_size: Some(126001), added: 128476, mode: Ignored) + /// Proof: Contracts CodeStorage (max_values: None, max_size: Some(126001), added: 128476, mode: Measured) /// The range of component `c` is `[0, 61717]`. fn reinstrument(c: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `270 + c * (1 ±0)` - // Estimated: `0` - // Minimum execution time: 28_004 nanoseconds. - Weight::from_ref_time(26_706_943) - .saturating_add(Weight::from_proof_size(0)) - // Standard Error: 67 - .saturating_add(Weight::from_ref_time(51_603).saturating_mul(c.into())) + // Estimated: `3025 + c * (2 ±0)` + // Minimum execution time: 34_613 nanoseconds. + Weight::from_ref_time(27_355_774) + .saturating_add(Weight::from_proof_size(3025)) + // Standard Error: 81 + .saturating_add(Weight::from_ref_time(51_954).saturating_mul(c.into())) .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) + .saturating_add(Weight::from_proof_size(2).saturating_mul(c.into())) } /// Storage: Contracts ContractInfoOf (r:1 w:1) - /// Proof: Contracts ContractInfoOf (max_values: None, max_size: Some(258), added: 2733, mode: Ignored) + /// Proof: Contracts ContractInfoOf (max_values: None, max_size: Some(258), added: 2733, mode: Measured) /// Storage: Contracts CodeStorage (r:1 w:0) - /// Proof: Contracts CodeStorage (max_values: None, max_size: Some(126001), added: 128476, mode: Ignored) + /// Proof: Contracts CodeStorage (max_values: None, max_size: Some(126001), added: 128476, mode: Measured) /// Storage: Timestamp Now (r:1 w:0) - /// Proof: Timestamp Now (max_values: Some(1), max_size: Some(8), added: 503, mode: Ignored) + /// Proof: Timestamp Now (max_values: Some(1), max_size: Some(8), added: 503, mode: Measured) /// Storage: System Account (r:1 w:1) - /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: Ignored) + /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: Measured) /// Storage: System EventTopics (r:2 w:2) - /// Proof Skipped: System EventTopics (max_values: None, max_size: None, mode: Ignored) + /// Proof Skipped: System EventTopics (max_values: None, max_size: None, mode: Measured) /// The range of component `c` is `[0, 125952]`. fn call_with_code_per_byte(c: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `771` - // Estimated: `0` - // Minimum execution time: 295_799 nanoseconds. - Weight::from_ref_time(308_660_753) - .saturating_add(Weight::from_proof_size(0)) - // Standard Error: 20 - .saturating_add(Weight::from_ref_time(32_458).saturating_mul(c.into())) + // Estimated: `16780 + c * (5 ±0)` + // Minimum execution time: 386_104 nanoseconds. + Weight::from_ref_time(396_718_823) + .saturating_add(Weight::from_proof_size(16780)) + // Standard Error: 27 + .saturating_add(Weight::from_ref_time(31_370).saturating_mul(c.into())) .saturating_add(T::DbWeight::get().reads(6_u64)) .saturating_add(T::DbWeight::get().writes(4_u64)) + .saturating_add(Weight::from_proof_size(5).saturating_mul(c.into())) } - /// Storage: Contracts CodeStorage (r:1 w:1) - /// Proof: Contracts CodeStorage (max_values: None, max_size: Some(126001), added: 128476, mode: Ignored) + /// Storage: Contracts OwnerInfoOf (r:1 w:1) + /// Proof: Contracts OwnerInfoOf (max_values: None, max_size: Some(88), added: 2563, mode: Measured) /// Storage: Contracts Nonce (r:1 w:1) - /// Proof: Contracts Nonce (max_values: Some(1), max_size: Some(8), added: 503, mode: Ignored) + /// Proof: Contracts Nonce (max_values: Some(1), max_size: Some(8), added: 503, mode: Measured) /// Storage: Contracts ContractInfoOf (r:1 w:1) - /// Proof: Contracts ContractInfoOf (max_values: None, max_size: Some(258), added: 2733, mode: Ignored) + /// Proof: Contracts ContractInfoOf (max_values: None, max_size: Some(258), added: 2733, mode: Measured) /// Storage: Timestamp Now (r:1 w:0) - /// Proof: Timestamp Now (max_values: Some(1), max_size: Some(8), added: 503, mode: Ignored) + /// Proof: Timestamp Now (max_values: Some(1), max_size: Some(8), added: 503, mode: Measured) /// Storage: System Account (r:1 w:1) - /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: Ignored) + /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: Measured) /// Storage: System EventTopics (r:3 w:3) - /// Proof Skipped: System EventTopics (max_values: None, max_size: None, mode: Ignored) + /// Proof Skipped: System EventTopics (max_values: None, max_size: None, mode: Measured) + /// Storage: Contracts CodeStorage (r:0 w:1) + /// Proof: Contracts CodeStorage (max_values: None, max_size: Some(126001), added: 128476, mode: Measured) /// Storage: Contracts PristineCode (r:0 w:1) - /// Proof: Contracts PristineCode (max_values: None, max_size: Some(125988), added: 128463, mode: Ignored) - /// Storage: Contracts OwnerInfoOf (r:0 w:1) - /// Proof: Contracts OwnerInfoOf (max_values: None, max_size: Some(88), added: 2563, mode: Ignored) + /// Proof: Contracts PristineCode (max_values: None, max_size: Some(125988), added: 128463, mode: Measured) /// The range of component `c` is `[0, 61717]`. /// The range of component `i` is `[0, 1048576]`. /// The range of component `s` is `[0, 1048576]`. fn instantiate_with_code(c: u32, i: u32, s: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `257` - // Estimated: `0` - // Minimum execution time: 3_754_887 nanoseconds. - Weight::from_ref_time(657_695_827) - .saturating_add(Weight::from_proof_size(0)) - // Standard Error: 283 - .saturating_add(Weight::from_ref_time(94_808).saturating_mul(c.into())) + // Estimated: `17752` + // Minimum execution time: 3_786_500 nanoseconds. + Weight::from_ref_time(672_511_565) + .saturating_add(Weight::from_proof_size(17752)) + // Standard Error: 281 + .saturating_add(Weight::from_ref_time(94_538).saturating_mul(c.into())) // Standard Error: 16 - .saturating_add(Weight::from_ref_time(1_357).saturating_mul(i.into())) + .saturating_add(Weight::from_ref_time(1_364).saturating_mul(i.into())) // Standard Error: 16 - .saturating_add(Weight::from_ref_time(1_756).saturating_mul(s.into())) + .saturating_add(Weight::from_ref_time(1_768).saturating_mul(s.into())) .saturating_add(T::DbWeight::get().reads(8_u64)) .saturating_add(T::DbWeight::get().writes(9_u64)) } - /// Storage: Contracts CodeStorage (r:1 w:1) - /// Proof: Contracts CodeStorage (max_values: None, max_size: Some(126001), added: 128476, mode: Ignored) + /// Storage: Contracts CodeStorage (r:1 w:0) + /// Proof: Contracts CodeStorage (max_values: None, max_size: Some(126001), added: 128476, mode: Measured) /// Storage: Contracts Nonce (r:1 w:1) - /// Proof: Contracts Nonce (max_values: Some(1), max_size: Some(8), added: 503, mode: Ignored) + /// Proof: Contracts Nonce (max_values: Some(1), max_size: Some(8), added: 503, mode: Measured) /// Storage: Contracts ContractInfoOf (r:1 w:1) - /// Proof: Contracts ContractInfoOf (max_values: None, max_size: Some(258), added: 2733, mode: Ignored) + /// Proof: Contracts ContractInfoOf (max_values: None, max_size: Some(258), added: 2733, mode: Measured) /// Storage: Timestamp Now (r:1 w:0) - /// Proof: Timestamp Now (max_values: Some(1), max_size: Some(8), added: 503, mode: Ignored) + /// Proof: Timestamp Now (max_values: Some(1), max_size: Some(8), added: 503, mode: Measured) /// Storage: System Account (r:1 w:1) - /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: Ignored) + /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: Measured) /// Storage: Contracts OwnerInfoOf (r:1 w:1) - /// Proof: Contracts OwnerInfoOf (max_values: None, max_size: Some(88), added: 2563, mode: Ignored) + /// Proof: Contracts OwnerInfoOf (max_values: None, max_size: Some(88), added: 2563, mode: Measured) /// Storage: System EventTopics (r:2 w:2) - /// Proof Skipped: System EventTopics (max_values: None, max_size: None, mode: Ignored) + /// Proof Skipped: System EventTopics (max_values: None, max_size: None, mode: Measured) /// The range of component `i` is `[0, 1048576]`. /// The range of component `s` is `[0, 1048576]`. fn instantiate(i: u32, s: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `533` - // Estimated: `0` - // Minimum execution time: 1_961_131 nanoseconds. - Weight::from_ref_time(208_539_564) - .saturating_add(Weight::from_proof_size(0)) + // Estimated: `19543` + // Minimum execution time: 1_927_365 nanoseconds. + Weight::from_ref_time(189_843_928) + .saturating_add(Weight::from_proof_size(19543)) // Standard Error: 8 - .saturating_add(Weight::from_ref_time(1_688).saturating_mul(i.into())) + .saturating_add(Weight::from_ref_time(1_677).saturating_mul(i.into())) // Standard Error: 8 - .saturating_add(Weight::from_ref_time(1_802).saturating_mul(s.into())) + .saturating_add(Weight::from_ref_time(1_803).saturating_mul(s.into())) .saturating_add(T::DbWeight::get().reads(8_u64)) - .saturating_add(T::DbWeight::get().writes(7_u64)) + .saturating_add(T::DbWeight::get().writes(6_u64)) } /// Storage: Contracts ContractInfoOf (r:1 w:1) - /// Proof: Contracts ContractInfoOf (max_values: None, max_size: Some(258), added: 2733, mode: Ignored) + /// Proof: Contracts ContractInfoOf (max_values: None, max_size: Some(258), added: 2733, mode: Measured) /// Storage: Contracts CodeStorage (r:1 w:0) - /// Proof: Contracts CodeStorage (max_values: None, max_size: Some(126001), added: 128476, mode: Ignored) + /// Proof: Contracts CodeStorage (max_values: None, max_size: Some(126001), added: 128476, mode: Measured) /// Storage: Timestamp Now (r:1 w:0) - /// Proof: Timestamp Now (max_values: Some(1), max_size: Some(8), added: 503, mode: Ignored) + /// Proof: Timestamp Now (max_values: Some(1), max_size: Some(8), added: 503, mode: Measured) /// Storage: System Account (r:1 w:1) - /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: Ignored) + /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: Measured) /// Storage: System EventTopics (r:2 w:2) - /// Proof Skipped: System EventTopics (max_values: None, max_size: None, mode: Ignored) + /// Proof Skipped: System EventTopics (max_values: None, max_size: None, mode: Measured) fn call() -> Weight { // Proof Size summary in bytes: // Measured: `823` - // Estimated: `0` - // Minimum execution time: 148_138 nanoseconds. - Weight::from_ref_time(148_862_000) - .saturating_add(Weight::from_proof_size(0)) + // Estimated: `16985` + // Minimum execution time: 146_354 nanoseconds. + Weight::from_ref_time(147_693_000) + .saturating_add(Weight::from_proof_size(16985)) .saturating_add(T::DbWeight::get().reads(6_u64)) .saturating_add(T::DbWeight::get().writes(4_u64)) } - /// Storage: Contracts CodeStorage (r:1 w:1) - /// Proof: Contracts CodeStorage (max_values: None, max_size: Some(126001), added: 128476, mode: Ignored) + /// Storage: Contracts OwnerInfoOf (r:1 w:1) + /// Proof: Contracts OwnerInfoOf (max_values: None, max_size: Some(88), added: 2563, mode: Measured) /// Storage: System EventTopics (r:1 w:1) - /// Proof Skipped: System EventTopics (max_values: None, max_size: None, mode: Ignored) + /// Proof Skipped: System EventTopics (max_values: None, max_size: None, mode: Measured) + /// Storage: Contracts CodeStorage (r:0 w:1) + /// Proof: Contracts CodeStorage (max_values: None, max_size: Some(126001), added: 128476, mode: Measured) /// Storage: Contracts PristineCode (r:0 w:1) - /// Proof: Contracts PristineCode (max_values: None, max_size: Some(125988), added: 128463, mode: Ignored) - /// Storage: Contracts OwnerInfoOf (r:0 w:1) - /// Proof: Contracts OwnerInfoOf (max_values: None, max_size: Some(88), added: 2563, mode: Ignored) + /// Proof: Contracts PristineCode (max_values: None, max_size: Some(125988), added: 128463, mode: Measured) /// The range of component `c` is `[0, 61717]`. fn upload_code(c: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `109` - // Estimated: `0` - // Minimum execution time: 293_444 nanoseconds. - Weight::from_ref_time(293_242_988) - .saturating_add(Weight::from_proof_size(0)) - // Standard Error: 156 - .saturating_add(Weight::from_ref_time(97_476).saturating_mul(c.into())) + // Estimated: `5386` + // Minimum execution time: 388_107 nanoseconds. + Weight::from_ref_time(383_172_702) + .saturating_add(Weight::from_proof_size(5386)) + // Standard Error: 73 + .saturating_add(Weight::from_ref_time(95_037).saturating_mul(c.into())) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().writes(4_u64)) } /// Storage: Contracts OwnerInfoOf (r:1 w:1) - /// Proof: Contracts OwnerInfoOf (max_values: None, max_size: Some(88), added: 2563, mode: Ignored) + /// Proof: Contracts OwnerInfoOf (max_values: None, max_size: Some(88), added: 2563, mode: Measured) /// Storage: System EventTopics (r:1 w:1) - /// Proof Skipped: System EventTopics (max_values: None, max_size: None, mode: Ignored) + /// Proof Skipped: System EventTopics (max_values: None, max_size: None, mode: Measured) /// Storage: Contracts CodeStorage (r:0 w:1) - /// Proof: Contracts CodeStorage (max_values: None, max_size: Some(126001), added: 128476, mode: Ignored) + /// Proof: Contracts CodeStorage (max_values: None, max_size: Some(126001), added: 128476, mode: Measured) /// Storage: Contracts PristineCode (r:0 w:1) - /// Proof: Contracts PristineCode (max_values: None, max_size: Some(125988), added: 128463, mode: Ignored) + /// Proof: Contracts PristineCode (max_values: None, max_size: Some(125988), added: 128463, mode: Measured) fn remove_code() -> Weight { // Proof Size summary in bytes: // Measured: `287` - // Estimated: `0` - // Minimum execution time: 26_802 nanoseconds. - Weight::from_ref_time(27_121_000) - .saturating_add(Weight::from_proof_size(0)) + // Estimated: `6098` + // Minimum execution time: 26_278 nanoseconds. + Weight::from_ref_time(26_682_000) + .saturating_add(Weight::from_proof_size(6098)) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().writes(4_u64)) } /// Storage: Contracts ContractInfoOf (r:1 w:1) - /// Proof: Contracts ContractInfoOf (max_values: None, max_size: Some(258), added: 2733, mode: Ignored) + /// Proof: Contracts ContractInfoOf (max_values: None, max_size: Some(258), added: 2733, mode: Measured) /// Storage: Contracts OwnerInfoOf (r:2 w:2) - /// Proof: Contracts OwnerInfoOf (max_values: None, max_size: Some(88), added: 2563, mode: Ignored) + /// Proof: Contracts OwnerInfoOf (max_values: None, max_size: Some(88), added: 2563, mode: Measured) /// Storage: System EventTopics (r:3 w:3) - /// Proof Skipped: System EventTopics (max_values: None, max_size: None, mode: Ignored) + /// Proof Skipped: System EventTopics (max_values: None, max_size: None, mode: Measured) fn set_code() -> Weight { // Proof Size summary in bytes: // Measured: `634` - // Estimated: `0` - // Minimum execution time: 31_079 nanoseconds. - Weight::from_ref_time(31_541_000) - .saturating_add(Weight::from_proof_size(0)) + // Estimated: `16752` + // Minimum execution time: 30_223 nanoseconds. + Weight::from_ref_time(30_737_000) + .saturating_add(Weight::from_proof_size(16752)) .saturating_add(T::DbWeight::get().reads(6_u64)) .saturating_add(T::DbWeight::get().writes(6_u64)) } /// Storage: System Account (r:1 w:0) - /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: Ignored) + /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: Measured) /// Storage: Contracts ContractInfoOf (r:1 w:1) - /// Proof: Contracts ContractInfoOf (max_values: None, max_size: Some(258), added: 2733, mode: Ignored) + /// Proof: Contracts ContractInfoOf (max_values: None, max_size: Some(258), added: 2733, mode: Measured) /// Storage: Contracts CodeStorage (r:1 w:0) - /// Proof: Contracts CodeStorage (max_values: None, max_size: Some(126001), added: 128476, mode: Ignored) + /// Proof: Contracts CodeStorage (max_values: None, max_size: Some(126001), added: 128476, mode: Measured) /// Storage: Timestamp Now (r:1 w:0) - /// Proof: Timestamp Now (max_values: Some(1), max_size: Some(8), added: 503, mode: Ignored) + /// Proof: Timestamp Now (max_values: Some(1), max_size: Some(8), added: 503, mode: Measured) /// Storage: System EventTopics (r:2 w:2) - /// Proof Skipped: System EventTopics (max_values: None, max_size: None, mode: Ignored) + /// Proof Skipped: System EventTopics (max_values: None, max_size: None, mode: Measured) /// The range of component `r` is `[0, 20]`. fn seal_caller(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `845 + r * (480 ±0)` - // Estimated: `0` - // Minimum execution time: 284_877 nanoseconds. - Weight::from_ref_time(290_043_606) - .saturating_add(Weight::from_proof_size(0)) - // Standard Error: 22_094 - .saturating_add(Weight::from_ref_time(18_094_219).saturating_mul(r.into())) + // Estimated: `17120 + r * (2400 ±0)` + // Minimum execution time: 374_780 nanoseconds. + Weight::from_ref_time(379_468_453) + .saturating_add(Weight::from_proof_size(17120)) + // Standard Error: 45_809 + .saturating_add(Weight::from_ref_time(17_553_577).saturating_mul(r.into())) .saturating_add(T::DbWeight::get().reads(6_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) + .saturating_add(Weight::from_proof_size(2400).saturating_mul(r.into())) } /// Storage: System Account (r:1 w:0) - /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: Ignored) + /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: Measured) /// Storage: Contracts ContractInfoOf (r:1601 w:1) - /// Proof: Contracts ContractInfoOf (max_values: None, max_size: Some(258), added: 2733, mode: Ignored) + /// Proof: Contracts ContractInfoOf (max_values: None, max_size: Some(258), added: 2733, mode: Measured) /// Storage: Contracts CodeStorage (r:1 w:0) - /// Proof: Contracts CodeStorage (max_values: None, max_size: Some(126001), added: 128476, mode: Ignored) + /// Proof: Contracts CodeStorage (max_values: None, max_size: Some(126001), added: 128476, mode: Measured) /// Storage: Timestamp Now (r:1 w:0) - /// Proof: Timestamp Now (max_values: Some(1), max_size: Some(8), added: 503, mode: Ignored) + /// Proof: Timestamp Now (max_values: Some(1), max_size: Some(8), added: 503, mode: Measured) /// Storage: System EventTopics (r:2 w:2) - /// Proof Skipped: System EventTopics (max_values: None, max_size: None, mode: Ignored) + /// Proof Skipped: System EventTopics (max_values: None, max_size: None, mode: Measured) /// The range of component `r` is `[0, 20]`. fn seal_is_contract(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `882 + r * (19218 ±0)` - // Estimated: `0` - // Minimum execution time: 283_956 nanoseconds. - Weight::from_ref_time(129_690_397) - .saturating_add(Weight::from_proof_size(0)) - // Standard Error: 531_270 - .saturating_add(Weight::from_ref_time(264_082_418).saturating_mul(r.into())) + // Estimated: `17110 + r * (294100 ±0)` + // Minimum execution time: 374_399 nanoseconds. + Weight::from_ref_time(228_569_211) + .saturating_add(Weight::from_proof_size(17110)) + // Standard Error: 492_562 + .saturating_add(Weight::from_ref_time(251_682_897).saturating_mul(r.into())) .saturating_add(T::DbWeight::get().reads(6_u64)) .saturating_add(T::DbWeight::get().reads((80_u64).saturating_mul(r.into()))) .saturating_add(T::DbWeight::get().writes(3_u64)) + .saturating_add(Weight::from_proof_size(294100).saturating_mul(r.into())) } /// Storage: System Account (r:1 w:0) - /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: Ignored) + /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: Measured) /// Storage: Contracts ContractInfoOf (r:1601 w:1) - /// Proof: Contracts ContractInfoOf (max_values: None, max_size: Some(258), added: 2733, mode: Ignored) + /// Proof: Contracts ContractInfoOf (max_values: None, max_size: Some(258), added: 2733, mode: Measured) /// Storage: Contracts CodeStorage (r:1 w:0) - /// Proof: Contracts CodeStorage (max_values: None, max_size: Some(126001), added: 128476, mode: Ignored) + /// Proof: Contracts CodeStorage (max_values: None, max_size: Some(126001), added: 128476, mode: Measured) /// Storage: Timestamp Now (r:1 w:0) - /// Proof: Timestamp Now (max_values: Some(1), max_size: Some(8), added: 503, mode: Ignored) + /// Proof: Timestamp Now (max_values: Some(1), max_size: Some(8), added: 503, mode: Measured) /// Storage: System EventTopics (r:2 w:2) - /// Proof Skipped: System EventTopics (max_values: None, max_size: None, mode: Ignored) + /// Proof Skipped: System EventTopics (max_values: None, max_size: None, mode: Measured) /// The range of component `r` is `[0, 20]`. fn seal_code_hash(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `889 + r * (19539 ±0)` - // Estimated: `0` - // Minimum execution time: 287_077 nanoseconds. - Weight::from_ref_time(148_155_166) - .saturating_add(Weight::from_proof_size(0)) - // Standard Error: 466_496 - .saturating_add(Weight::from_ref_time(306_625_930).saturating_mul(r.into())) + // Estimated: `17170 + r * (295700 ±0)` + // Minimum execution time: 375_980 nanoseconds. + Weight::from_ref_time(232_604_331) + .saturating_add(Weight::from_proof_size(17170)) + // Standard Error: 491_912 + .saturating_add(Weight::from_ref_time(299_479_335).saturating_mul(r.into())) .saturating_add(T::DbWeight::get().reads(6_u64)) .saturating_add(T::DbWeight::get().reads((80_u64).saturating_mul(r.into()))) .saturating_add(T::DbWeight::get().writes(3_u64)) + .saturating_add(Weight::from_proof_size(295700).saturating_mul(r.into())) } /// Storage: System Account (r:1 w:0) - /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: Ignored) + /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: Measured) /// Storage: Contracts ContractInfoOf (r:1 w:1) - /// Proof: Contracts ContractInfoOf (max_values: None, max_size: Some(258), added: 2733, mode: Ignored) + /// Proof: Contracts ContractInfoOf (max_values: None, max_size: Some(258), added: 2733, mode: Measured) /// Storage: Contracts CodeStorage (r:1 w:0) - /// Proof: Contracts CodeStorage (max_values: None, max_size: Some(126001), added: 128476, mode: Ignored) + /// Proof: Contracts CodeStorage (max_values: None, max_size: Some(126001), added: 128476, mode: Measured) /// Storage: Timestamp Now (r:1 w:0) - /// Proof: Timestamp Now (max_values: Some(1), max_size: Some(8), added: 503, mode: Ignored) + /// Proof: Timestamp Now (max_values: Some(1), max_size: Some(8), added: 503, mode: Measured) /// Storage: System EventTopics (r:2 w:2) - /// Proof Skipped: System EventTopics (max_values: None, max_size: None, mode: Ignored) + /// Proof Skipped: System EventTopics (max_values: None, max_size: None, mode: Measured) /// The range of component `r` is `[0, 20]`. fn seal_own_code_hash(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `852 + r * (480 ±0)` - // Estimated: `0` - // Minimum execution time: 286_459 nanoseconds. - Weight::from_ref_time(292_826_594) - .saturating_add(Weight::from_proof_size(0)) - // Standard Error: 27_808 - .saturating_add(Weight::from_ref_time(22_401_931).saturating_mul(r.into())) + // Estimated: `17155 + r * (2400 ±0)` + // Minimum execution time: 375_075 nanoseconds. + Weight::from_ref_time(381_217_372) + .saturating_add(Weight::from_proof_size(17155)) + // Standard Error: 63_950 + .saturating_add(Weight::from_ref_time(21_872_316).saturating_mul(r.into())) .saturating_add(T::DbWeight::get().reads(6_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) + .saturating_add(Weight::from_proof_size(2400).saturating_mul(r.into())) } /// Storage: System Account (r:1 w:0) - /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: Ignored) + /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: Measured) /// Storage: Contracts ContractInfoOf (r:1 w:1) - /// Proof: Contracts ContractInfoOf (max_values: None, max_size: Some(258), added: 2733, mode: Ignored) + /// Proof: Contracts ContractInfoOf (max_values: None, max_size: Some(258), added: 2733, mode: Measured) /// Storage: Contracts CodeStorage (r:1 w:0) - /// Proof: Contracts CodeStorage (max_values: None, max_size: Some(126001), added: 128476, mode: Ignored) + /// Proof: Contracts CodeStorage (max_values: None, max_size: Some(126001), added: 128476, mode: Measured) /// Storage: Timestamp Now (r:1 w:0) - /// Proof: Timestamp Now (max_values: Some(1), max_size: Some(8), added: 503, mode: Ignored) + /// Proof: Timestamp Now (max_values: Some(1), max_size: Some(8), added: 503, mode: Measured) /// Storage: System EventTopics (r:2 w:2) - /// Proof Skipped: System EventTopics (max_values: None, max_size: None, mode: Ignored) + /// Proof Skipped: System EventTopics (max_values: None, max_size: None, mode: Measured) /// The range of component `r` is `[0, 20]`. fn seal_caller_is_origin(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `842 + r * (240 ±0)` - // Estimated: `0` - // Minimum execution time: 284_330 nanoseconds. - Weight::from_ref_time(289_069_701) - .saturating_add(Weight::from_proof_size(0)) - // Standard Error: 13_666 - .saturating_add(Weight::from_ref_time(11_157_859).saturating_mul(r.into())) + // Estimated: `17080 + r * (1200 ±0)` + // Minimum execution time: 373_260 nanoseconds. + Weight::from_ref_time(377_987_666) + .saturating_add(Weight::from_proof_size(17080)) + // Standard Error: 35_603 + .saturating_add(Weight::from_ref_time(11_274_165).saturating_mul(r.into())) .saturating_add(T::DbWeight::get().reads(6_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) + .saturating_add(Weight::from_proof_size(1200).saturating_mul(r.into())) } /// Storage: System Account (r:1 w:0) - /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: Ignored) + /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: Measured) /// Storage: Contracts ContractInfoOf (r:1 w:1) - /// Proof: Contracts ContractInfoOf (max_values: None, max_size: Some(258), added: 2733, mode: Ignored) + /// Proof: Contracts ContractInfoOf (max_values: None, max_size: Some(258), added: 2733, mode: Measured) /// Storage: Contracts CodeStorage (r:1 w:0) - /// Proof: Contracts CodeStorage (max_values: None, max_size: Some(126001), added: 128476, mode: Ignored) + /// Proof: Contracts CodeStorage (max_values: None, max_size: Some(126001), added: 128476, mode: Measured) /// Storage: Timestamp Now (r:1 w:0) - /// Proof: Timestamp Now (max_values: Some(1), max_size: Some(8), added: 503, mode: Ignored) + /// Proof: Timestamp Now (max_values: Some(1), max_size: Some(8), added: 503, mode: Measured) /// Storage: System EventTopics (r:2 w:2) - /// Proof Skipped: System EventTopics (max_values: None, max_size: None, mode: Ignored) + /// Proof Skipped: System EventTopics (max_values: None, max_size: None, mode: Measured) /// The range of component `r` is `[0, 20]`. fn seal_address(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `846 + r * (480 ±0)` - // Estimated: `0` - // Minimum execution time: 284_506 nanoseconds. - Weight::from_ref_time(290_155_141) - .saturating_add(Weight::from_proof_size(0)) - // Standard Error: 22_944 - .saturating_add(Weight::from_ref_time(17_992_627).saturating_mul(r.into())) + // Estimated: `17100 + r * (2400 ±0)` + // Minimum execution time: 374_605 nanoseconds. + Weight::from_ref_time(379_395_443) + .saturating_add(Weight::from_proof_size(17100)) + // Standard Error: 49_646 + .saturating_add(Weight::from_ref_time(17_487_585).saturating_mul(r.into())) .saturating_add(T::DbWeight::get().reads(6_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) + .saturating_add(Weight::from_proof_size(2400).saturating_mul(r.into())) } /// Storage: System Account (r:1 w:0) - /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: Ignored) + /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: Measured) /// Storage: Contracts ContractInfoOf (r:1 w:1) - /// Proof: Contracts ContractInfoOf (max_values: None, max_size: Some(258), added: 2733, mode: Ignored) + /// Proof: Contracts ContractInfoOf (max_values: None, max_size: Some(258), added: 2733, mode: Measured) /// Storage: Contracts CodeStorage (r:1 w:0) - /// Proof: Contracts CodeStorage (max_values: None, max_size: Some(126001), added: 128476, mode: Ignored) + /// Proof: Contracts CodeStorage (max_values: None, max_size: Some(126001), added: 128476, mode: Measured) /// Storage: Timestamp Now (r:1 w:0) - /// Proof: Timestamp Now (max_values: Some(1), max_size: Some(8), added: 503, mode: Ignored) + /// Proof: Timestamp Now (max_values: Some(1), max_size: Some(8), added: 503, mode: Measured) /// Storage: System EventTopics (r:2 w:2) - /// Proof Skipped: System EventTopics (max_values: None, max_size: None, mode: Ignored) + /// Proof Skipped: System EventTopics (max_values: None, max_size: None, mode: Measured) /// The range of component `r` is `[0, 20]`. fn seal_gas_left(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `847 + r * (480 ±0)` - // Estimated: `0` - // Minimum execution time: 284_177 nanoseconds. - Weight::from_ref_time(289_314_787) - .saturating_add(Weight::from_proof_size(0)) - // Standard Error: 24_552 - .saturating_add(Weight::from_ref_time(17_754_919).saturating_mul(r.into())) + // Estimated: `17105 + r * (2400 ±0)` + // Minimum execution time: 374_620 nanoseconds. + Weight::from_ref_time(379_623_792) + .saturating_add(Weight::from_proof_size(17105)) + // Standard Error: 49_990 + .saturating_add(Weight::from_ref_time(17_226_706).saturating_mul(r.into())) .saturating_add(T::DbWeight::get().reads(6_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) + .saturating_add(Weight::from_proof_size(2400).saturating_mul(r.into())) } /// Storage: System Account (r:2 w:0) - /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: Ignored) + /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: Measured) /// Storage: Contracts ContractInfoOf (r:1 w:1) - /// Proof: Contracts ContractInfoOf (max_values: None, max_size: Some(258), added: 2733, mode: Ignored) + /// Proof: Contracts ContractInfoOf (max_values: None, max_size: Some(258), added: 2733, mode: Measured) /// Storage: Contracts CodeStorage (r:1 w:0) - /// Proof: Contracts CodeStorage (max_values: None, max_size: Some(126001), added: 128476, mode: Ignored) + /// Proof: Contracts CodeStorage (max_values: None, max_size: Some(126001), added: 128476, mode: Measured) /// Storage: Timestamp Now (r:1 w:0) - /// Proof: Timestamp Now (max_values: Some(1), max_size: Some(8), added: 503, mode: Ignored) + /// Proof: Timestamp Now (max_values: Some(1), max_size: Some(8), added: 503, mode: Measured) /// Storage: System EventTopics (r:2 w:2) - /// Proof Skipped: System EventTopics (max_values: None, max_size: None, mode: Ignored) + /// Proof Skipped: System EventTopics (max_values: None, max_size: None, mode: Measured) /// The range of component `r` is `[0, 20]`. fn seal_balance(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `1017 + r * (480 ±0)` - // Estimated: `0` - // Minimum execution time: 284_096 nanoseconds. - Weight::from_ref_time(293_655_130) - .saturating_add(Weight::from_proof_size(0)) - // Standard Error: 56_379 - .saturating_add(Weight::from_ref_time(95_688_575).saturating_mul(r.into())) + // Estimated: `19673 + r * (2456 ±4)` + // Minimum execution time: 374_002 nanoseconds. + Weight::from_ref_time(384_615_649) + .saturating_add(Weight::from_proof_size(19673)) + // Standard Error: 85_633 + .saturating_add(Weight::from_ref_time(95_227_118).saturating_mul(r.into())) .saturating_add(T::DbWeight::get().reads(7_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) + .saturating_add(Weight::from_proof_size(2456).saturating_mul(r.into())) } /// Storage: System Account (r:1 w:0) - /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: Ignored) + /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: Measured) /// Storage: Contracts ContractInfoOf (r:1 w:1) - /// Proof: Contracts ContractInfoOf (max_values: None, max_size: Some(258), added: 2733, mode: Ignored) + /// Proof: Contracts ContractInfoOf (max_values: None, max_size: Some(258), added: 2733, mode: Measured) /// Storage: Contracts CodeStorage (r:1 w:0) - /// Proof: Contracts CodeStorage (max_values: None, max_size: Some(126001), added: 128476, mode: Ignored) + /// Proof: Contracts CodeStorage (max_values: None, max_size: Some(126001), added: 128476, mode: Measured) /// Storage: Timestamp Now (r:1 w:0) - /// Proof: Timestamp Now (max_values: Some(1), max_size: Some(8), added: 503, mode: Ignored) + /// Proof: Timestamp Now (max_values: Some(1), max_size: Some(8), added: 503, mode: Measured) /// Storage: System EventTopics (r:2 w:2) - /// Proof Skipped: System EventTopics (max_values: None, max_size: None, mode: Ignored) + /// Proof Skipped: System EventTopics (max_values: None, max_size: None, mode: Measured) /// The range of component `r` is `[0, 20]`. fn seal_value_transferred(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `856 + r * (480 ±0)` - // Estimated: `0` - // Minimum execution time: 285_967 nanoseconds. - Weight::from_ref_time(288_043_137) - .saturating_add(Weight::from_proof_size(0)) - // Standard Error: 83_838 - .saturating_add(Weight::from_ref_time(18_256_709).saturating_mul(r.into())) + // Estimated: `17200 + r * (2400 ±0)` + // Minimum execution time: 375_307 nanoseconds. + Weight::from_ref_time(378_389_705) + .saturating_add(Weight::from_proof_size(17200)) + // Standard Error: 42_265 + .saturating_add(Weight::from_ref_time(17_316_680).saturating_mul(r.into())) .saturating_add(T::DbWeight::get().reads(6_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) + .saturating_add(Weight::from_proof_size(2400).saturating_mul(r.into())) } /// Storage: System Account (r:1 w:0) - /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: Ignored) + /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: Measured) /// Storage: Contracts ContractInfoOf (r:1 w:1) - /// Proof: Contracts ContractInfoOf (max_values: None, max_size: Some(258), added: 2733, mode: Ignored) + /// Proof: Contracts ContractInfoOf (max_values: None, max_size: Some(258), added: 2733, mode: Measured) /// Storage: Contracts CodeStorage (r:1 w:0) - /// Proof: Contracts CodeStorage (max_values: None, max_size: Some(126001), added: 128476, mode: Ignored) + /// Proof: Contracts CodeStorage (max_values: None, max_size: Some(126001), added: 128476, mode: Measured) /// Storage: Timestamp Now (r:1 w:0) - /// Proof: Timestamp Now (max_values: Some(1), max_size: Some(8), added: 503, mode: Ignored) + /// Proof: Timestamp Now (max_values: Some(1), max_size: Some(8), added: 503, mode: Measured) /// Storage: System EventTopics (r:2 w:2) - /// Proof Skipped: System EventTopics (max_values: None, max_size: None, mode: Ignored) + /// Proof Skipped: System EventTopics (max_values: None, max_size: None, mode: Measured) /// The range of component `r` is `[0, 20]`. fn seal_minimum_balance(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `854 + r * (480 ±0)` - // Estimated: `0` - // Minimum execution time: 284_060 nanoseconds. - Weight::from_ref_time(290_577_678) - .saturating_add(Weight::from_proof_size(0)) - // Standard Error: 26_131 - .saturating_add(Weight::from_ref_time(17_560_562).saturating_mul(r.into())) + // Estimated: `17140 + r * (2400 ±0)` + // Minimum execution time: 374_878 nanoseconds. + Weight::from_ref_time(379_364_066) + .saturating_add(Weight::from_proof_size(17140)) + // Standard Error: 49_158 + .saturating_add(Weight::from_ref_time(17_111_145).saturating_mul(r.into())) .saturating_add(T::DbWeight::get().reads(6_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) + .saturating_add(Weight::from_proof_size(2400).saturating_mul(r.into())) } /// Storage: System Account (r:1 w:0) - /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: Ignored) + /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: Measured) /// Storage: Contracts ContractInfoOf (r:1 w:1) - /// Proof: Contracts ContractInfoOf (max_values: None, max_size: Some(258), added: 2733, mode: Ignored) + /// Proof: Contracts ContractInfoOf (max_values: None, max_size: Some(258), added: 2733, mode: Measured) /// Storage: Contracts CodeStorage (r:1 w:0) - /// Proof: Contracts CodeStorage (max_values: None, max_size: Some(126001), added: 128476, mode: Ignored) + /// Proof: Contracts CodeStorage (max_values: None, max_size: Some(126001), added: 128476, mode: Measured) /// Storage: Timestamp Now (r:1 w:0) - /// Proof: Timestamp Now (max_values: Some(1), max_size: Some(8), added: 503, mode: Ignored) + /// Proof: Timestamp Now (max_values: Some(1), max_size: Some(8), added: 503, mode: Measured) /// Storage: System EventTopics (r:2 w:2) - /// Proof Skipped: System EventTopics (max_values: None, max_size: None, mode: Ignored) + /// Proof Skipped: System EventTopics (max_values: None, max_size: None, mode: Measured) /// The range of component `r` is `[0, 20]`. fn seal_block_number(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `851 + r * (480 ±0)` - // Estimated: `0` - // Minimum execution time: 286_068 nanoseconds. - Weight::from_ref_time(290_026_137) - .saturating_add(Weight::from_proof_size(0)) - // Standard Error: 34_787 - .saturating_add(Weight::from_ref_time(17_597_404).saturating_mul(r.into())) + // Estimated: `17125 + r * (2400 ±0)` + // Minimum execution time: 374_981 nanoseconds. + Weight::from_ref_time(381_539_022) + .saturating_add(Weight::from_proof_size(17125)) + // Standard Error: 61_419 + .saturating_add(Weight::from_ref_time(17_062_381).saturating_mul(r.into())) .saturating_add(T::DbWeight::get().reads(6_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) + .saturating_add(Weight::from_proof_size(2400).saturating_mul(r.into())) } /// Storage: System Account (r:1 w:0) - /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: Ignored) + /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: Measured) /// Storage: Contracts ContractInfoOf (r:1 w:1) - /// Proof: Contracts ContractInfoOf (max_values: None, max_size: Some(258), added: 2733, mode: Ignored) + /// Proof: Contracts ContractInfoOf (max_values: None, max_size: Some(258), added: 2733, mode: Measured) /// Storage: Contracts CodeStorage (r:1 w:0) - /// Proof: Contracts CodeStorage (max_values: None, max_size: Some(126001), added: 128476, mode: Ignored) + /// Proof: Contracts CodeStorage (max_values: None, max_size: Some(126001), added: 128476, mode: Measured) /// Storage: Timestamp Now (r:1 w:0) - /// Proof: Timestamp Now (max_values: Some(1), max_size: Some(8), added: 503, mode: Ignored) + /// Proof: Timestamp Now (max_values: Some(1), max_size: Some(8), added: 503, mode: Measured) /// Storage: System EventTopics (r:2 w:2) - /// Proof Skipped: System EventTopics (max_values: None, max_size: None, mode: Ignored) + /// Proof Skipped: System EventTopics (max_values: None, max_size: None, mode: Measured) /// The range of component `r` is `[0, 20]`. fn seal_now(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `842 + r * (480 ±0)` - // Estimated: `0` - // Minimum execution time: 285_082 nanoseconds. - Weight::from_ref_time(290_487_103) - .saturating_add(Weight::from_proof_size(0)) - // Standard Error: 22_743 - .saturating_add(Weight::from_ref_time(17_506_771).saturating_mul(r.into())) + // Estimated: `17100 + r * (2400 ±0)` + // Minimum execution time: 374_798 nanoseconds. + Weight::from_ref_time(372_659_915) + .saturating_add(Weight::from_proof_size(17100)) + // Standard Error: 151_499 + .saturating_add(Weight::from_ref_time(18_192_683).saturating_mul(r.into())) .saturating_add(T::DbWeight::get().reads(6_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) + .saturating_add(Weight::from_proof_size(2400).saturating_mul(r.into())) } /// Storage: System Account (r:1 w:0) - /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: Ignored) + /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: Measured) /// Storage: Contracts ContractInfoOf (r:1 w:1) - /// Proof: Contracts ContractInfoOf (max_values: None, max_size: Some(258), added: 2733, mode: Ignored) + /// Proof: Contracts ContractInfoOf (max_values: None, max_size: Some(258), added: 2733, mode: Measured) /// Storage: Contracts CodeStorage (r:1 w:0) - /// Proof: Contracts CodeStorage (max_values: None, max_size: Some(126001), added: 128476, mode: Ignored) + /// Proof: Contracts CodeStorage (max_values: None, max_size: Some(126001), added: 128476, mode: Measured) /// Storage: Timestamp Now (r:1 w:0) - /// Proof: Timestamp Now (max_values: Some(1), max_size: Some(8), added: 503, mode: Ignored) + /// Proof: Timestamp Now (max_values: Some(1), max_size: Some(8), added: 503, mode: Measured) /// Storage: TransactionPayment NextFeeMultiplier (r:1 w:0) - /// Proof: TransactionPayment NextFeeMultiplier (max_values: Some(1), max_size: Some(16), added: 511, mode: Ignored) + /// Proof: TransactionPayment NextFeeMultiplier (max_values: Some(1), max_size: Some(16), added: 511, mode: Measured) /// Storage: System EventTopics (r:2 w:2) - /// Proof Skipped: System EventTopics (max_values: None, max_size: None, mode: Ignored) + /// Proof Skipped: System EventTopics (max_values: None, max_size: None, mode: Measured) /// The range of component `r` is `[0, 20]`. fn seal_weight_to_fee(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `919 + r * (800 ±0)` - // Estimated: `0` - // Minimum execution time: 285_309 nanoseconds. - Weight::from_ref_time(296_735_365) - .saturating_add(Weight::from_proof_size(0)) - // Standard Error: 49_300 - .saturating_add(Weight::from_ref_time(87_678_979).saturating_mul(r.into())) + // Estimated: `18835 + r * (4805 ±0)` + // Minimum execution time: 374_841 nanoseconds. + Weight::from_ref_time(385_475_889) + .saturating_add(Weight::from_proof_size(18835)) + // Standard Error: 106_207 + .saturating_add(Weight::from_ref_time(88_099_987).saturating_mul(r.into())) .saturating_add(T::DbWeight::get().reads(7_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) + .saturating_add(Weight::from_proof_size(4805).saturating_mul(r.into())) } /// Storage: System Account (r:1 w:0) - /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: Ignored) + /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: Measured) /// Storage: Contracts ContractInfoOf (r:1 w:1) - /// Proof: Contracts ContractInfoOf (max_values: None, max_size: Some(258), added: 2733, mode: Ignored) + /// Proof: Contracts ContractInfoOf (max_values: None, max_size: Some(258), added: 2733, mode: Measured) /// Storage: Contracts CodeStorage (r:1 w:0) - /// Proof: Contracts CodeStorage (max_values: None, max_size: Some(126001), added: 128476, mode: Ignored) + /// Proof: Contracts CodeStorage (max_values: None, max_size: Some(126001), added: 128476, mode: Measured) /// Storage: Timestamp Now (r:1 w:0) - /// Proof: Timestamp Now (max_values: Some(1), max_size: Some(8), added: 503, mode: Ignored) + /// Proof: Timestamp Now (max_values: Some(1), max_size: Some(8), added: 503, mode: Measured) /// Storage: System EventTopics (r:2 w:2) - /// Proof Skipped: System EventTopics (max_values: None, max_size: None, mode: Ignored) + /// Proof Skipped: System EventTopics (max_values: None, max_size: None, mode: Measured) /// The range of component `r` is `[0, 20]`. fn seal_gas(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `809 + r * (320 ±0)` - // Estimated: `0` - // Minimum execution time: 136_110 nanoseconds. - Weight::from_ref_time(139_800_659) - .saturating_add(Weight::from_proof_size(0)) - // Standard Error: 9_674 - .saturating_add(Weight::from_ref_time(8_302_834).saturating_mul(r.into())) + // Estimated: `16955 + r * (1600 ±0)` + // Minimum execution time: 134_436 nanoseconds. + Weight::from_ref_time(137_789_498) + .saturating_add(Weight::from_proof_size(16955)) + // Standard Error: 10_622 + .saturating_add(Weight::from_ref_time(8_144_024).saturating_mul(r.into())) .saturating_add(T::DbWeight::get().reads(6_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) + .saturating_add(Weight::from_proof_size(1600).saturating_mul(r.into())) } /// Storage: System Account (r:1 w:0) - /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: Ignored) + /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: Measured) /// Storage: Contracts ContractInfoOf (r:1 w:1) - /// Proof: Contracts ContractInfoOf (max_values: None, max_size: Some(258), added: 2733, mode: Ignored) + /// Proof: Contracts ContractInfoOf (max_values: None, max_size: Some(258), added: 2733, mode: Measured) /// Storage: Contracts CodeStorage (r:1 w:0) - /// Proof: Contracts CodeStorage (max_values: None, max_size: Some(126001), added: 128476, mode: Ignored) + /// Proof: Contracts CodeStorage (max_values: None, max_size: Some(126001), added: 128476, mode: Measured) /// Storage: Timestamp Now (r:1 w:0) - /// Proof: Timestamp Now (max_values: Some(1), max_size: Some(8), added: 503, mode: Ignored) + /// Proof: Timestamp Now (max_values: Some(1), max_size: Some(8), added: 503, mode: Measured) /// Storage: System EventTopics (r:2 w:2) - /// Proof Skipped: System EventTopics (max_values: None, max_size: None, mode: Ignored) + /// Proof Skipped: System EventTopics (max_values: None, max_size: None, mode: Measured) /// The range of component `r` is `[0, 20]`. fn seal_input(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `844 + r * (480 ±0)` - // Estimated: `0` - // Minimum execution time: 285_238 nanoseconds. - Weight::from_ref_time(289_723_839) - .saturating_add(Weight::from_proof_size(0)) - // Standard Error: 19_531 - .saturating_add(Weight::from_ref_time(15_590_085).saturating_mul(r.into())) + // Estimated: `17085 + r * (2400 ±0)` + // Minimum execution time: 374_480 nanoseconds. + Weight::from_ref_time(379_723_392) + .saturating_add(Weight::from_proof_size(17085)) + // Standard Error: 50_240 + .saturating_add(Weight::from_ref_time(15_358_041).saturating_mul(r.into())) .saturating_add(T::DbWeight::get().reads(6_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) + .saturating_add(Weight::from_proof_size(2400).saturating_mul(r.into())) } /// Storage: System Account (r:1 w:0) - /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: Ignored) + /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: Measured) /// Storage: Contracts ContractInfoOf (r:1 w:1) - /// Proof: Contracts ContractInfoOf (max_values: None, max_size: Some(258), added: 2733, mode: Ignored) + /// Proof: Contracts ContractInfoOf (max_values: None, max_size: Some(258), added: 2733, mode: Measured) /// Storage: Contracts CodeStorage (r:1 w:0) - /// Proof: Contracts CodeStorage (max_values: None, max_size: Some(126001), added: 128476, mode: Ignored) + /// Proof: Contracts CodeStorage (max_values: None, max_size: Some(126001), added: 128476, mode: Measured) /// Storage: Timestamp Now (r:1 w:0) - /// Proof: Timestamp Now (max_values: Some(1), max_size: Some(8), added: 503, mode: Ignored) + /// Proof: Timestamp Now (max_values: Some(1), max_size: Some(8), added: 503, mode: Measured) /// Storage: System EventTopics (r:2 w:2) - /// Proof Skipped: System EventTopics (max_values: None, max_size: None, mode: Ignored) + /// Proof Skipped: System EventTopics (max_values: None, max_size: None, mode: Measured) /// The range of component `n` is `[0, 1024]`. fn seal_input_per_kb(n: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `1324` - // Estimated: `0` - // Minimum execution time: 303_189 nanoseconds. - Weight::from_ref_time(323_374_503) - .saturating_add(Weight::from_proof_size(0)) - // Standard Error: 2_060 - .saturating_add(Weight::from_ref_time(9_799_357).saturating_mul(n.into())) + // Estimated: `19490` + // Minimum execution time: 392_282 nanoseconds. + Weight::from_ref_time(418_943_323) + .saturating_add(Weight::from_proof_size(19490)) + // Standard Error: 4_673 + .saturating_add(Weight::from_ref_time(9_664_301).saturating_mul(n.into())) .saturating_add(T::DbWeight::get().reads(6_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) } /// Storage: System Account (r:1 w:0) - /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: Ignored) + /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: Measured) /// Storage: Contracts ContractInfoOf (r:1 w:1) - /// Proof: Contracts ContractInfoOf (max_values: None, max_size: Some(258), added: 2733, mode: Ignored) + /// Proof: Contracts ContractInfoOf (max_values: None, max_size: Some(258), added: 2733, mode: Measured) /// Storage: Contracts CodeStorage (r:1 w:0) - /// Proof: Contracts CodeStorage (max_values: None, max_size: Some(126001), added: 128476, mode: Ignored) + /// Proof: Contracts CodeStorage (max_values: None, max_size: Some(126001), added: 128476, mode: Measured) /// Storage: Timestamp Now (r:1 w:0) - /// Proof: Timestamp Now (max_values: Some(1), max_size: Some(8), added: 503, mode: Ignored) + /// Proof: Timestamp Now (max_values: Some(1), max_size: Some(8), added: 503, mode: Measured) /// Storage: System EventTopics (r:2 w:2) - /// Proof Skipped: System EventTopics (max_values: None, max_size: None, mode: Ignored) + /// Proof Skipped: System EventTopics (max_values: None, max_size: None, mode: Measured) /// The range of component `r` is `[0, 1]`. fn seal_return(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `832 + r * (45 ±0)` - // Estimated: `0` - // Minimum execution time: 280_736 nanoseconds. - Weight::from_ref_time(285_027_920) - .saturating_add(Weight::from_proof_size(0)) - // Standard Error: 222_526 - .saturating_add(Weight::from_ref_time(1_814_579).saturating_mul(r.into())) + // Estimated: `17030 + r * (225 ±0)` + // Minimum execution time: 372_207 nanoseconds. + Weight::from_ref_time(376_232_444) + .saturating_add(Weight::from_proof_size(17030)) .saturating_add(T::DbWeight::get().reads(6_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) + .saturating_add(Weight::from_proof_size(225).saturating_mul(r.into())) } /// Storage: System Account (r:1 w:0) - /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: Ignored) + /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: Measured) /// Storage: Contracts ContractInfoOf (r:1 w:1) - /// Proof: Contracts ContractInfoOf (max_values: None, max_size: Some(258), added: 2733, mode: Ignored) + /// Proof: Contracts ContractInfoOf (max_values: None, max_size: Some(258), added: 2733, mode: Measured) /// Storage: Contracts CodeStorage (r:1 w:0) - /// Proof: Contracts CodeStorage (max_values: None, max_size: Some(126001), added: 128476, mode: Ignored) + /// Proof: Contracts CodeStorage (max_values: None, max_size: Some(126001), added: 128476, mode: Measured) /// Storage: Timestamp Now (r:1 w:0) - /// Proof: Timestamp Now (max_values: Some(1), max_size: Some(8), added: 503, mode: Ignored) + /// Proof: Timestamp Now (max_values: Some(1), max_size: Some(8), added: 503, mode: Measured) /// Storage: System EventTopics (r:2 w:2) - /// Proof Skipped: System EventTopics (max_values: None, max_size: None, mode: Ignored) + /// Proof Skipped: System EventTopics (max_values: None, max_size: None, mode: Measured) /// The range of component `n` is `[0, 1024]`. fn seal_return_per_kb(n: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `842` - // Estimated: `0` - // Minimum execution time: 285_559 nanoseconds. - Weight::from_ref_time(290_522_234) - .saturating_add(Weight::from_proof_size(0)) - // Standard Error: 2_578 - .saturating_add(Weight::from_ref_time(195_134).saturating_mul(n.into())) + // Estimated: `17125` + // Minimum execution time: 374_743 nanoseconds. + Weight::from_ref_time(377_365_053) + .saturating_add(Weight::from_proof_size(17125)) + // Standard Error: 1_073 + .saturating_add(Weight::from_ref_time(231_251).saturating_mul(n.into())) .saturating_add(T::DbWeight::get().reads(6_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) } /// Storage: System Account (r:3 w:3) - /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: Ignored) + /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: Measured) /// Storage: Contracts ContractInfoOf (r:1 w:1) - /// Proof: Contracts ContractInfoOf (max_values: None, max_size: Some(258), added: 2733, mode: Ignored) + /// Proof: Contracts ContractInfoOf (max_values: None, max_size: Some(258), added: 2733, mode: Measured) /// Storage: Contracts CodeStorage (r:1 w:0) - /// Proof: Contracts CodeStorage (max_values: None, max_size: Some(126001), added: 128476, mode: Ignored) + /// Proof: Contracts CodeStorage (max_values: None, max_size: Some(126001), added: 128476, mode: Measured) /// Storage: Timestamp Now (r:1 w:0) - /// Proof: Timestamp Now (max_values: Some(1), max_size: Some(8), added: 503, mode: Ignored) + /// Proof: Timestamp Now (max_values: Some(1), max_size: Some(8), added: 503, mode: Measured) /// Storage: Contracts DeletionQueue (r:1 w:1) - /// Proof: Contracts DeletionQueue (max_values: Some(1), max_size: Some(16642), added: 17137, mode: Ignored) + /// Proof: Contracts DeletionQueue (max_values: Some(1), max_size: Some(16642), added: 17137, mode: Measured) /// Storage: Contracts OwnerInfoOf (r:1 w:1) - /// Proof: Contracts OwnerInfoOf (max_values: None, max_size: Some(88), added: 2563, mode: Ignored) + /// Proof: Contracts OwnerInfoOf (max_values: None, max_size: Some(88), added: 2563, mode: Measured) /// Storage: System EventTopics (r:3 w:3) - /// Proof Skipped: System EventTopics (max_values: None, max_size: None, mode: Ignored) + /// Proof Skipped: System EventTopics (max_values: None, max_size: None, mode: Measured) /// The range of component `r` is `[0, 1]`. fn seal_terminate(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `874 + r * (280 ±0)` - // Estimated: `0` - // Minimum execution time: 282_070 nanoseconds. - Weight::from_ref_time(286_466_489) - .saturating_add(Weight::from_proof_size(0)) - // Standard Error: 238_285 - .saturating_add(Weight::from_ref_time(58_542_610).saturating_mul(r.into())) + // Estimated: `19880 + r * (11465 ±0)` + // Minimum execution time: 374_873 nanoseconds. + Weight::from_ref_time(378_422_289) + .saturating_add(Weight::from_proof_size(19880)) + // Standard Error: 979_734 + .saturating_add(Weight::from_ref_time(57_035_310).saturating_mul(r.into())) .saturating_add(T::DbWeight::get().reads(6_u64)) .saturating_add(T::DbWeight::get().reads((5_u64).saturating_mul(r.into()))) .saturating_add(T::DbWeight::get().writes(3_u64)) .saturating_add(T::DbWeight::get().writes((6_u64).saturating_mul(r.into()))) + .saturating_add(Weight::from_proof_size(11465).saturating_mul(r.into())) } /// Storage: System Account (r:1 w:0) - /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: Ignored) + /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: Measured) /// Storage: Contracts ContractInfoOf (r:1 w:1) - /// Proof: Contracts ContractInfoOf (max_values: None, max_size: Some(258), added: 2733, mode: Ignored) + /// Proof: Contracts ContractInfoOf (max_values: None, max_size: Some(258), added: 2733, mode: Measured) /// Storage: Contracts CodeStorage (r:1 w:0) - /// Proof: Contracts CodeStorage (max_values: None, max_size: Some(126001), added: 128476, mode: Ignored) + /// Proof: Contracts CodeStorage (max_values: None, max_size: Some(126001), added: 128476, mode: Measured) /// Storage: Timestamp Now (r:1 w:0) - /// Proof: Timestamp Now (max_values: Some(1), max_size: Some(8), added: 503, mode: Ignored) + /// Proof: Timestamp Now (max_values: Some(1), max_size: Some(8), added: 503, mode: Measured) /// Storage: RandomnessCollectiveFlip RandomMaterial (r:1 w:0) - /// Proof: RandomnessCollectiveFlip RandomMaterial (max_values: Some(1), max_size: Some(2594), added: 3089, mode: Ignored) + /// Proof: RandomnessCollectiveFlip RandomMaterial (max_values: Some(1), max_size: Some(2594), added: 3089, mode: Measured) /// Storage: System EventTopics (r:2 w:2) - /// Proof Skipped: System EventTopics (max_values: None, max_size: None, mode: Ignored) + /// Proof Skipped: System EventTopics (max_values: None, max_size: None, mode: Measured) /// The range of component `r` is `[0, 20]`. fn seal_random(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `889 + r * (800 ±0)` - // Estimated: `0` - // Minimum execution time: 284_147 nanoseconds. - Weight::from_ref_time(295_373_881) - .saturating_add(Weight::from_proof_size(0)) - // Standard Error: 64_682 - .saturating_add(Weight::from_ref_time(114_198_680).saturating_mul(r.into())) + // Estimated: `18643 + r * (4805 ±0)` + // Minimum execution time: 375_001 nanoseconds. + Weight::from_ref_time(382_558_599) + .saturating_add(Weight::from_proof_size(18643)) + // Standard Error: 94_918 + .saturating_add(Weight::from_ref_time(112_973_277).saturating_mul(r.into())) .saturating_add(T::DbWeight::get().reads(7_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) + .saturating_add(Weight::from_proof_size(4805).saturating_mul(r.into())) } /// Storage: System Account (r:1 w:0) - /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: Ignored) + /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: Measured) /// Storage: Contracts ContractInfoOf (r:1 w:1) - /// Proof: Contracts ContractInfoOf (max_values: None, max_size: Some(258), added: 2733, mode: Ignored) + /// Proof: Contracts ContractInfoOf (max_values: None, max_size: Some(258), added: 2733, mode: Measured) /// Storage: Contracts CodeStorage (r:1 w:0) - /// Proof: Contracts CodeStorage (max_values: None, max_size: Some(126001), added: 128476, mode: Ignored) + /// Proof: Contracts CodeStorage (max_values: None, max_size: Some(126001), added: 128476, mode: Measured) /// Storage: Timestamp Now (r:1 w:0) - /// Proof: Timestamp Now (max_values: Some(1), max_size: Some(8), added: 503, mode: Ignored) + /// Proof: Timestamp Now (max_values: Some(1), max_size: Some(8), added: 503, mode: Measured) /// Storage: System EventTopics (r:2 w:2) - /// Proof Skipped: System EventTopics (max_values: None, max_size: None, mode: Ignored) + /// Proof Skipped: System EventTopics (max_values: None, max_size: None, mode: Measured) /// The range of component `r` is `[0, 20]`. fn seal_deposit_event(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `842 + r * (800 ±0)` - // Estimated: `0` - // Minimum execution time: 282_681 nanoseconds. - Weight::from_ref_time(290_974_649) - .saturating_add(Weight::from_proof_size(0)) - // Standard Error: 77_850 - .saturating_add(Weight::from_ref_time(233_273_622).saturating_mul(r.into())) + // Estimated: `17075 + r * (4000 ±0)` + // Minimum execution time: 372_547 nanoseconds. + Weight::from_ref_time(383_278_916) + .saturating_add(Weight::from_proof_size(17075)) + // Standard Error: 128_339 + .saturating_add(Weight::from_ref_time(229_356_088).saturating_mul(r.into())) .saturating_add(T::DbWeight::get().reads(6_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) + .saturating_add(Weight::from_proof_size(4000).saturating_mul(r.into())) } /// Storage: System Account (r:1 w:0) - /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: Ignored) + /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: Measured) /// Storage: Contracts ContractInfoOf (r:1 w:1) - /// Proof: Contracts ContractInfoOf (max_values: None, max_size: Some(258), added: 2733, mode: Ignored) + /// Proof: Contracts ContractInfoOf (max_values: None, max_size: Some(258), added: 2733, mode: Measured) /// Storage: Contracts CodeStorage (r:1 w:0) - /// Proof: Contracts CodeStorage (max_values: None, max_size: Some(126001), added: 128476, mode: Ignored) + /// Proof: Contracts CodeStorage (max_values: None, max_size: Some(126001), added: 128476, mode: Measured) /// Storage: Timestamp Now (r:1 w:0) - /// Proof: Timestamp Now (max_values: Some(1), max_size: Some(8), added: 503, mode: Ignored) + /// Proof: Timestamp Now (max_values: Some(1), max_size: Some(8), added: 503, mode: Measured) /// Storage: System EventTopics (r:322 w:322) - /// Proof Skipped: System EventTopics (max_values: None, max_size: None, mode: Ignored) + /// Proof Skipped: System EventTopics (max_values: None, max_size: None, mode: Measured) /// The range of component `t` is `[0, 4]`. /// The range of component `n` is `[0, 16]`. fn seal_deposit_event_per_topic_and_kb(t: u32, n: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `1778 + t * (2608 ±0) + n * (8 ±0)` - // Estimated: `0` - // Minimum execution time: 1_226_868 nanoseconds. - Weight::from_ref_time(517_031_747) - .saturating_add(Weight::from_proof_size(0)) - // Standard Error: 342_786 - .saturating_add(Weight::from_ref_time(183_829_556).saturating_mul(t.into())) - // Standard Error: 94_145 - .saturating_add(Weight::from_ref_time(70_675_099).saturating_mul(n.into())) + // Estimated: `21675 + t * (211030 ±0) + n * (50 ±0)` + // Minimum execution time: 1_290_432 nanoseconds. + Weight::from_ref_time(595_859_216) + .saturating_add(Weight::from_proof_size(21675)) + // Standard Error: 602_943 + .saturating_add(Weight::from_ref_time(178_128_149).saturating_mul(t.into())) + // Standard Error: 165_597 + .saturating_add(Weight::from_ref_time(71_475_468).saturating_mul(n.into())) .saturating_add(T::DbWeight::get().reads(6_u64)) .saturating_add(T::DbWeight::get().reads((80_u64).saturating_mul(t.into()))) .saturating_add(T::DbWeight::get().writes(3_u64)) .saturating_add(T::DbWeight::get().writes((80_u64).saturating_mul(t.into()))) + .saturating_add(Weight::from_proof_size(211030).saturating_mul(t.into())) + .saturating_add(Weight::from_proof_size(50).saturating_mul(n.into())) } /// Storage: System Account (r:1 w:0) - /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: Ignored) + /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: Measured) /// Storage: Contracts ContractInfoOf (r:1 w:1) - /// Proof: Contracts ContractInfoOf (max_values: None, max_size: Some(258), added: 2733, mode: Ignored) + /// Proof: Contracts ContractInfoOf (max_values: None, max_size: Some(258), added: 2733, mode: Measured) /// Storage: Contracts CodeStorage (r:1 w:0) - /// Proof: Contracts CodeStorage (max_values: None, max_size: Some(126001), added: 128476, mode: Ignored) + /// Proof: Contracts CodeStorage (max_values: None, max_size: Some(126001), added: 128476, mode: Measured) /// Storage: Timestamp Now (r:1 w:0) - /// Proof: Timestamp Now (max_values: Some(1), max_size: Some(8), added: 503, mode: Ignored) + /// Proof: Timestamp Now (max_values: Some(1), max_size: Some(8), added: 503, mode: Measured) /// Storage: System EventTopics (r:2 w:2) - /// Proof Skipped: System EventTopics (max_values: None, max_size: None, mode: Ignored) + /// Proof Skipped: System EventTopics (max_values: None, max_size: None, mode: Measured) /// The range of component `r` is `[0, 20]`. fn seal_debug_message(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `841 + r * (560 ±0)` - // Estimated: `0` - // Minimum execution time: 143_026 nanoseconds. - Weight::from_ref_time(147_158_038) - .saturating_add(Weight::from_proof_size(0)) - // Standard Error: 16_662 - .saturating_add(Weight::from_ref_time(14_990_989).saturating_mul(r.into())) + // Estimated: `17065 + r * (2800 ±0)` + // Minimum execution time: 149_020 nanoseconds. + Weight::from_ref_time(152_893_012) + .saturating_add(Weight::from_proof_size(17065)) + // Standard Error: 31_804 + .saturating_add(Weight::from_ref_time(14_497_512).saturating_mul(r.into())) .saturating_add(T::DbWeight::get().reads(6_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) + .saturating_add(Weight::from_proof_size(2800).saturating_mul(r.into())) } /// Storage: System Account (r:1 w:0) /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) @@ -955,2033 +979,2086 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `125792` // Estimated: `265059` - // Minimum execution time: 412_112 nanoseconds. - Weight::from_ref_time(415_624_011) + // Minimum execution time: 502_589 nanoseconds. + Weight::from_ref_time(506_695_307) .saturating_add(Weight::from_proof_size(265059)) - // Standard Error: 1_190 - .saturating_add(Weight::from_ref_time(797_964).saturating_mul(i.into())) + // Standard Error: 2_148 + .saturating_add(Weight::from_ref_time(814_647).saturating_mul(i.into())) .saturating_add(T::DbWeight::get().reads(6_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) } /// Storage: Skipped Metadata (r:0 w:0) - /// Proof Skipped: Skipped Metadata (max_values: None, max_size: None, mode: Ignored) + /// Proof Skipped: Skipped Metadata (max_values: None, max_size: None, mode: Measured) /// The range of component `r` is `[0, 10]`. fn seal_set_storage(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `883 + r * (23417 ±0)` - // Estimated: `0` - // Minimum execution time: 286_118 nanoseconds. - Weight::from_ref_time(195_812_951) - .saturating_add(Weight::from_proof_size(0)) - // Standard Error: 858_501 - .saturating_add(Weight::from_ref_time(485_357_331).saturating_mul(r.into())) + // Estimated: `883 + r * (23417 ±0)` + // Minimum execution time: 375_473 nanoseconds. + Weight::from_ref_time(291_125_810) + .saturating_add(Weight::from_proof_size(883)) + // Standard Error: 824_971 + .saturating_add(Weight::from_ref_time(477_999_695).saturating_mul(r.into())) .saturating_add(T::DbWeight::get().reads(6_u64)) .saturating_add(T::DbWeight::get().reads((80_u64).saturating_mul(r.into()))) .saturating_add(T::DbWeight::get().writes(3_u64)) .saturating_add(T::DbWeight::get().writes((80_u64).saturating_mul(r.into()))) + .saturating_add(Weight::from_proof_size(23417).saturating_mul(r.into())) } /// Storage: Skipped Metadata (r:0 w:0) - /// Proof Skipped: Skipped Metadata (max_values: None, max_size: None, mode: Ignored) + /// Proof Skipped: Skipped Metadata (max_values: None, max_size: None, mode: Measured) /// The range of component `n` is `[0, 8]`. fn seal_set_storage_per_new_kb(n: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `12583 + n * (11969 ±0)` - // Estimated: `0` - // Minimum execution time: 433_181 nanoseconds. - Weight::from_ref_time(618_575_096) - .saturating_add(Weight::from_proof_size(0)) - // Standard Error: 1_696_126 - .saturating_add(Weight::from_ref_time(96_213_633).saturating_mul(n.into())) + // Estimated: `8500 + n * (12813 ±61)` + // Minimum execution time: 515_962 nanoseconds. + Weight::from_ref_time(697_904_030) + .saturating_add(Weight::from_proof_size(8500)) + // Standard Error: 1_684_000 + .saturating_add(Weight::from_ref_time(98_411_710).saturating_mul(n.into())) .saturating_add(T::DbWeight::get().reads(52_u64)) .saturating_add(T::DbWeight::get().reads((7_u64).saturating_mul(n.into()))) .saturating_add(T::DbWeight::get().writes(50_u64)) .saturating_add(T::DbWeight::get().writes((7_u64).saturating_mul(n.into()))) + .saturating_add(Weight::from_proof_size(12813).saturating_mul(n.into())) } /// Storage: Skipped Metadata (r:0 w:0) - /// Proof Skipped: Skipped Metadata (max_values: None, max_size: None, mode: Ignored) + /// Proof Skipped: Skipped Metadata (max_values: None, max_size: None, mode: Measured) /// The range of component `n` is `[0, 8]`. fn seal_set_storage_per_old_kb(n: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `15138 + n * (175775 ±0)` - // Estimated: `0` - // Minimum execution time: 433_354 nanoseconds. - Weight::from_ref_time(597_131_349) - .saturating_add(Weight::from_proof_size(0)) - // Standard Error: 1_509_003 - .saturating_add(Weight::from_ref_time(64_872_907).saturating_mul(n.into())) + // Estimated: `9898 + n * (176855 ±74)` + // Minimum execution time: 515_828 nanoseconds. + Weight::from_ref_time(661_240_495) + .saturating_add(Weight::from_proof_size(9898)) + // Standard Error: 1_338_661 + .saturating_add(Weight::from_ref_time(65_767_819).saturating_mul(n.into())) .saturating_add(T::DbWeight::get().reads(51_u64)) .saturating_add(T::DbWeight::get().reads((7_u64).saturating_mul(n.into()))) .saturating_add(T::DbWeight::get().writes(49_u64)) .saturating_add(T::DbWeight::get().writes((7_u64).saturating_mul(n.into()))) + .saturating_add(Weight::from_proof_size(176855).saturating_mul(n.into())) } /// Storage: Skipped Metadata (r:0 w:0) - /// Proof Skipped: Skipped Metadata (max_values: None, max_size: None, mode: Ignored) + /// Proof Skipped: Skipped Metadata (max_values: None, max_size: None, mode: Measured) /// The range of component `r` is `[0, 10]`. fn seal_clear_storage(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `876 + r * (23098 ±0)` - // Estimated: `0` - // Minimum execution time: 286_788 nanoseconds. - Weight::from_ref_time(207_787_332) - .saturating_add(Weight::from_proof_size(0)) - // Standard Error: 778_284 - .saturating_add(Weight::from_ref_time(474_879_317).saturating_mul(r.into())) + // Estimated: `881 + r * (23097 ±0)` + // Minimum execution time: 375_528 nanoseconds. + Weight::from_ref_time(296_453_612) + .saturating_add(Weight::from_proof_size(881)) + // Standard Error: 809_232 + .saturating_add(Weight::from_ref_time(465_365_815).saturating_mul(r.into())) .saturating_add(T::DbWeight::get().reads(6_u64)) .saturating_add(T::DbWeight::get().reads((80_u64).saturating_mul(r.into()))) .saturating_add(T::DbWeight::get().writes(3_u64)) .saturating_add(T::DbWeight::get().writes((80_u64).saturating_mul(r.into()))) + .saturating_add(Weight::from_proof_size(23097).saturating_mul(r.into())) } /// Storage: Skipped Metadata (r:0 w:0) - /// Proof Skipped: Skipped Metadata (max_values: None, max_size: None, mode: Ignored) + /// Proof Skipped: Skipped Metadata (max_values: None, max_size: None, mode: Measured) /// The range of component `n` is `[0, 8]`. fn seal_clear_storage_per_kb(n: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `14863 + n * (175768 ±0)` - // Estimated: `0` - // Minimum execution time: 397_414 nanoseconds. - Weight::from_ref_time(573_575_029) - .saturating_add(Weight::from_proof_size(0)) - // Standard Error: 1_630_691 - .saturating_add(Weight::from_ref_time(67_888_226).saturating_mul(n.into())) + // Estimated: `9519 + n * (176867 ±75)` + // Minimum execution time: 480_427 nanoseconds. + Weight::from_ref_time(640_337_570) + .saturating_add(Weight::from_proof_size(9519)) + // Standard Error: 1_497_141 + .saturating_add(Weight::from_ref_time(67_963_696).saturating_mul(n.into())) .saturating_add(T::DbWeight::get().reads(51_u64)) .saturating_add(T::DbWeight::get().reads((7_u64).saturating_mul(n.into()))) .saturating_add(T::DbWeight::get().writes(48_u64)) .saturating_add(T::DbWeight::get().writes((7_u64).saturating_mul(n.into()))) + .saturating_add(Weight::from_proof_size(176867).saturating_mul(n.into())) } /// Storage: Skipped Metadata (r:0 w:0) - /// Proof Skipped: Skipped Metadata (max_values: None, max_size: None, mode: Ignored) + /// Proof Skipped: Skipped Metadata (max_values: None, max_size: None, mode: Measured) /// The range of component `r` is `[0, 10]`. fn seal_get_storage(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `878 + r * (23740 ±0)` - // Estimated: `0` - // Minimum execution time: 286_098 nanoseconds. - Weight::from_ref_time(223_637_903) - .saturating_add(Weight::from_proof_size(0)) - // Standard Error: 669_294 - .saturating_add(Weight::from_ref_time(387_577_627).saturating_mul(r.into())) + // Estimated: `880 + r * (23739 ±0)` + // Minimum execution time: 375_725 nanoseconds. + Weight::from_ref_time(307_839_394) + .saturating_add(Weight::from_proof_size(880)) + // Standard Error: 710_694 + .saturating_add(Weight::from_ref_time(381_738_407).saturating_mul(r.into())) .saturating_add(T::DbWeight::get().reads(6_u64)) .saturating_add(T::DbWeight::get().reads((80_u64).saturating_mul(r.into()))) .saturating_add(T::DbWeight::get().writes(3_u64)) + .saturating_add(Weight::from_proof_size(23739).saturating_mul(r.into())) } /// Storage: Skipped Metadata (r:0 w:0) - /// Proof Skipped: Skipped Metadata (max_values: None, max_size: None, mode: Ignored) + /// Proof Skipped: Skipped Metadata (max_values: None, max_size: None, mode: Measured) /// The range of component `n` is `[0, 8]`. fn seal_get_storage_per_kb(n: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `15469 + n * (175775 ±0)` - // Estimated: `0` - // Minimum execution time: 373_335 nanoseconds. - Weight::from_ref_time(531_442_564) - .saturating_add(Weight::from_proof_size(0)) - // Standard Error: 1_474_336 - .saturating_add(Weight::from_ref_time(154_469_598).saturating_mul(n.into())) + // Estimated: `10010 + n * (176900 ±76)` + // Minimum execution time: 461_871 nanoseconds. + Weight::from_ref_time(605_755_493) + .saturating_add(Weight::from_proof_size(10010)) + // Standard Error: 1_375_044 + .saturating_add(Weight::from_ref_time(161_332_330).saturating_mul(n.into())) .saturating_add(T::DbWeight::get().reads(51_u64)) .saturating_add(T::DbWeight::get().reads((7_u64).saturating_mul(n.into()))) .saturating_add(T::DbWeight::get().writes(3_u64)) + .saturating_add(Weight::from_proof_size(176900).saturating_mul(n.into())) } /// Storage: Skipped Metadata (r:0 w:0) - /// Proof Skipped: Skipped Metadata (max_values: None, max_size: None, mode: Ignored) + /// Proof Skipped: Skipped Metadata (max_values: None, max_size: None, mode: Measured) /// The range of component `r` is `[0, 10]`. fn seal_contains_storage(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `871 + r * (23100 ±0)` - // Estimated: `0` - // Minimum execution time: 284_183 nanoseconds. - Weight::from_ref_time(223_325_689) - .saturating_add(Weight::from_proof_size(0)) - // Standard Error: 644_587 - .saturating_add(Weight::from_ref_time(373_628_582).saturating_mul(r.into())) + // Estimated: `873 + r * (23099 ±0)` + // Minimum execution time: 375_325 nanoseconds. + Weight::from_ref_time(305_508_307) + .saturating_add(Weight::from_proof_size(873)) + // Standard Error: 715_627 + .saturating_add(Weight::from_ref_time(369_985_438).saturating_mul(r.into())) .saturating_add(T::DbWeight::get().reads(6_u64)) .saturating_add(T::DbWeight::get().reads((80_u64).saturating_mul(r.into()))) .saturating_add(T::DbWeight::get().writes(3_u64)) + .saturating_add(Weight::from_proof_size(23099).saturating_mul(r.into())) } /// Storage: Skipped Metadata (r:0 w:0) - /// Proof Skipped: Skipped Metadata (max_values: None, max_size: None, mode: Ignored) + /// Proof Skipped: Skipped Metadata (max_values: None, max_size: None, mode: Measured) /// The range of component `n` is `[0, 8]`. fn seal_contains_storage_per_kb(n: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `14814 + n * (175782 ±0)` - // Estimated: `0` - // Minimum execution time: 368_310 nanoseconds. - Weight::from_ref_time(512_814_023) - .saturating_add(Weight::from_proof_size(0)) - // Standard Error: 1_337_352 - .saturating_add(Weight::from_ref_time(62_871_539).saturating_mul(n.into())) + // Estimated: `9502 + n * (176872 ±75)` + // Minimum execution time: 457_128 nanoseconds. + Weight::from_ref_time(582_799_799) + .saturating_add(Weight::from_proof_size(9502)) + // Standard Error: 1_151_126 + .saturating_add(Weight::from_ref_time(63_425_277).saturating_mul(n.into())) .saturating_add(T::DbWeight::get().reads(51_u64)) .saturating_add(T::DbWeight::get().reads((7_u64).saturating_mul(n.into()))) .saturating_add(T::DbWeight::get().writes(3_u64)) + .saturating_add(Weight::from_proof_size(176872).saturating_mul(n.into())) } /// Storage: Skipped Metadata (r:0 w:0) - /// Proof Skipped: Skipped Metadata (max_values: None, max_size: None, mode: Ignored) + /// Proof Skipped: Skipped Metadata (max_values: None, max_size: None, mode: Measured) /// The range of component `r` is `[0, 10]`. fn seal_take_storage(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `879 + r * (23740 ±0)` - // Estimated: `0` - // Minimum execution time: 287_096 nanoseconds. - Weight::from_ref_time(204_878_281) - .saturating_add(Weight::from_proof_size(0)) - // Standard Error: 833_763 - .saturating_add(Weight::from_ref_time(483_927_706).saturating_mul(r.into())) + // Estimated: `881 + r * (23739 ±0)` + // Minimum execution time: 375_918 nanoseconds. + Weight::from_ref_time(293_217_646) + .saturating_add(Weight::from_proof_size(881)) + // Standard Error: 840_266 + .saturating_add(Weight::from_ref_time(478_374_701).saturating_mul(r.into())) .saturating_add(T::DbWeight::get().reads(6_u64)) .saturating_add(T::DbWeight::get().reads((80_u64).saturating_mul(r.into()))) .saturating_add(T::DbWeight::get().writes(3_u64)) .saturating_add(T::DbWeight::get().writes((80_u64).saturating_mul(r.into()))) + .saturating_add(Weight::from_proof_size(23739).saturating_mul(r.into())) } /// Storage: Skipped Metadata (r:0 w:0) - /// Proof Skipped: Skipped Metadata (max_values: None, max_size: None, mode: Ignored) + /// Proof Skipped: Skipped Metadata (max_values: None, max_size: None, mode: Measured) /// The range of component `n` is `[0, 8]`. fn seal_take_storage_per_kb(n: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `15470 + n * (175775 ±0)` - // Estimated: `0` - // Minimum execution time: 399_318 nanoseconds. - Weight::from_ref_time(586_658_466) - .saturating_add(Weight::from_proof_size(0)) - // Standard Error: 1_734_497 - .saturating_add(Weight::from_ref_time(161_047_970).saturating_mul(n.into())) + // Estimated: `10010 + n * (176898 ±76)` + // Minimum execution time: 484_207 nanoseconds. + Weight::from_ref_time(664_065_436) + .saturating_add(Weight::from_proof_size(10010)) + // Standard Error: 1_655_442 + .saturating_add(Weight::from_ref_time(166_258_757).saturating_mul(n.into())) .saturating_add(T::DbWeight::get().reads(51_u64)) .saturating_add(T::DbWeight::get().reads((7_u64).saturating_mul(n.into()))) .saturating_add(T::DbWeight::get().writes(48_u64)) .saturating_add(T::DbWeight::get().writes((7_u64).saturating_mul(n.into()))) + .saturating_add(Weight::from_proof_size(176898).saturating_mul(n.into())) } /// Storage: System Account (r:1602 w:1601) - /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: Ignored) + /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: Measured) /// Storage: Contracts ContractInfoOf (r:1 w:1) - /// Proof: Contracts ContractInfoOf (max_values: None, max_size: Some(258), added: 2733, mode: Ignored) + /// Proof: Contracts ContractInfoOf (max_values: None, max_size: Some(258), added: 2733, mode: Measured) /// Storage: Contracts CodeStorage (r:1 w:0) - /// Proof: Contracts CodeStorage (max_values: None, max_size: Some(126001), added: 128476, mode: Ignored) + /// Proof: Contracts CodeStorage (max_values: None, max_size: Some(126001), added: 128476, mode: Measured) /// Storage: Timestamp Now (r:1 w:0) - /// Proof: Timestamp Now (max_values: Some(1), max_size: Some(8), added: 503, mode: Ignored) + /// Proof: Timestamp Now (max_values: Some(1), max_size: Some(8), added: 503, mode: Measured) /// Storage: System EventTopics (r:2 w:2) - /// Proof Skipped: System EventTopics (max_values: None, max_size: None, mode: Ignored) + /// Proof Skipped: System EventTopics (max_values: None, max_size: None, mode: Measured) /// The range of component `r` is `[0, 20]`. fn seal_transfer(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `1393 + r * (3602 ±0)` - // Estimated: `0` - // Minimum execution time: 285_460 nanoseconds. - Weight::from_ref_time(227_848_079) - .saturating_add(Weight::from_proof_size(0)) - // Standard Error: 717_899 - .saturating_add(Weight::from_ref_time(1_434_112_130).saturating_mul(r.into())) + // Estimated: `21258 + r * (216091 ±0)` + // Minimum execution time: 377_152 nanoseconds. + Weight::from_ref_time(317_470_910) + .saturating_add(Weight::from_proof_size(21258)) + // Standard Error: 994_076 + .saturating_add(Weight::from_ref_time(1_409_416_087).saturating_mul(r.into())) .saturating_add(T::DbWeight::get().reads(7_u64)) .saturating_add(T::DbWeight::get().reads((80_u64).saturating_mul(r.into()))) .saturating_add(T::DbWeight::get().writes(4_u64)) .saturating_add(T::DbWeight::get().writes((80_u64).saturating_mul(r.into()))) + .saturating_add(Weight::from_proof_size(216091).saturating_mul(r.into())) } /// Storage: System Account (r:1 w:0) - /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: Ignored) + /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: Measured) /// Storage: Contracts ContractInfoOf (r:1601 w:1601) - /// Proof: Contracts ContractInfoOf (max_values: None, max_size: Some(258), added: 2733, mode: Ignored) + /// Proof: Contracts ContractInfoOf (max_values: None, max_size: Some(258), added: 2733, mode: Measured) /// Storage: Contracts CodeStorage (r:2 w:0) - /// Proof: Contracts CodeStorage (max_values: None, max_size: Some(126001), added: 128476, mode: Ignored) + /// Proof: Contracts CodeStorage (max_values: None, max_size: Some(126001), added: 128476, mode: Measured) /// Storage: Timestamp Now (r:1 w:0) - /// Proof: Timestamp Now (max_values: Some(1), max_size: Some(8), added: 503, mode: Ignored) + /// Proof: Timestamp Now (max_values: Some(1), max_size: Some(8), added: 503, mode: Measured) /// Storage: System EventTopics (r:1602 w:1602) - /// Proof Skipped: System EventTopics (max_values: None, max_size: None, mode: Ignored) + /// Proof Skipped: System EventTopics (max_values: None, max_size: None, mode: Measured) /// The range of component `r` is `[0, 20]`. fn seal_call(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `1551 + r * (20511 ±0)` - // Estimated: `0` - // Minimum execution time: 286_947 nanoseconds. - Weight::from_ref_time(288_518_000) - .saturating_add(Weight::from_proof_size(0)) - // Standard Error: 8_492_544 - .saturating_add(Weight::from_ref_time(21_606_377_662).saturating_mul(r.into())) + // Estimated: `21848 + r * (498651 ±1)` + // Minimum execution time: 376_257 nanoseconds. + Weight::from_ref_time(377_035_000) + .saturating_add(Weight::from_proof_size(21848)) + // Standard Error: 7_966_778 + .saturating_add(Weight::from_ref_time(28_873_495_129).saturating_mul(r.into())) .saturating_add(T::DbWeight::get().reads(7_u64)) .saturating_add(T::DbWeight::get().reads((160_u64).saturating_mul(r.into()))) .saturating_add(T::DbWeight::get().writes(3_u64)) .saturating_add(T::DbWeight::get().writes((160_u64).saturating_mul(r.into()))) + .saturating_add(Weight::from_proof_size(498651).saturating_mul(r.into())) } /// Storage: System Account (r:1 w:0) - /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: Ignored) + /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: Measured) /// Storage: Contracts ContractInfoOf (r:1 w:1) - /// Proof: Contracts ContractInfoOf (max_values: None, max_size: Some(258), added: 2733, mode: Ignored) + /// Proof: Contracts ContractInfoOf (max_values: None, max_size: Some(258), added: 2733, mode: Measured) /// Storage: Contracts CodeStorage (r:1536 w:0) - /// Proof: Contracts CodeStorage (max_values: None, max_size: Some(126001), added: 128476, mode: Ignored) + /// Proof: Contracts CodeStorage (max_values: None, max_size: Some(126001), added: 128476, mode: Measured) /// Storage: Timestamp Now (r:1 w:0) - /// Proof: Timestamp Now (max_values: Some(1), max_size: Some(8), added: 503, mode: Ignored) + /// Proof: Timestamp Now (max_values: Some(1), max_size: Some(8), added: 503, mode: Measured) /// Storage: System EventTopics (r:1537 w:1537) - /// Proof Skipped: System EventTopics (max_values: None, max_size: None, mode: Ignored) + /// Proof Skipped: System EventTopics (max_values: None, max_size: None, mode: Measured) /// The range of component `r` is `[0, 20]`. fn seal_delegate_call(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0 + r * (71670 ±0)` - // Estimated: `0` - // Minimum execution time: 285_196 nanoseconds. - Weight::from_ref_time(287_958_000) - .saturating_add(Weight::from_proof_size(0)) - // Standard Error: 10_345_109 - .saturating_add(Weight::from_ref_time(21_395_365_662).saturating_mul(r.into())) + // Estimated: `17125 + r * (659927 ±822)` + // Minimum execution time: 376_544 nanoseconds. + Weight::from_ref_time(377_490_000) + .saturating_add(Weight::from_proof_size(17125)) + // Standard Error: 8_608_050 + .saturating_add(Weight::from_ref_time(28_568_714_410).saturating_mul(r.into())) .saturating_add(T::DbWeight::get().reads(6_u64)) .saturating_add(T::DbWeight::get().reads((150_u64).saturating_mul(r.into()))) .saturating_add(T::DbWeight::get().writes(3_u64)) .saturating_add(T::DbWeight::get().writes((75_u64).saturating_mul(r.into()))) + .saturating_add(Weight::from_proof_size(659927).saturating_mul(r.into())) } /// Storage: System Account (r:82 w:81) - /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: Ignored) + /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: Measured) /// Storage: Contracts ContractInfoOf (r:81 w:81) - /// Proof: Contracts ContractInfoOf (max_values: None, max_size: Some(258), added: 2733, mode: Ignored) + /// Proof: Contracts ContractInfoOf (max_values: None, max_size: Some(258), added: 2733, mode: Measured) /// Storage: Contracts CodeStorage (r:2 w:0) - /// Proof: Contracts CodeStorage (max_values: None, max_size: Some(126001), added: 128476, mode: Ignored) + /// Proof: Contracts CodeStorage (max_values: None, max_size: Some(126001), added: 128476, mode: Measured) /// Storage: Timestamp Now (r:1 w:0) - /// Proof: Timestamp Now (max_values: Some(1), max_size: Some(8), added: 503, mode: Ignored) + /// Proof: Timestamp Now (max_values: Some(1), max_size: Some(8), added: 503, mode: Measured) /// Storage: System EventTopics (r:82 w:82) - /// Proof Skipped: System EventTopics (max_values: None, max_size: None, mode: Ignored) + /// Proof Skipped: System EventTopics (max_values: None, max_size: None, mode: Measured) /// The range of component `t` is `[0, 1]`. /// The range of component `c` is `[0, 1024]`. fn seal_call_per_transfer_clone_kb(t: u32, c: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `21611 + t * (15369 ±0)` - // Estimated: `0` - // Minimum execution time: 10_540_614 nanoseconds. - Weight::from_ref_time(9_281_766_912) - .saturating_add(Weight::from_proof_size(0)) - // Standard Error: 5_409_378 - .saturating_add(Weight::from_ref_time(1_449_975_070).saturating_mul(t.into())) - // Standard Error: 8_111 - .saturating_add(Weight::from_ref_time(10_275_108).saturating_mul(c.into())) + // Estimated: `519400 + t * (277320 ±0)` + // Minimum execution time: 10_322_052 nanoseconds. + Weight::from_ref_time(9_248_652_894) + .saturating_add(Weight::from_proof_size(519400)) + // Standard Error: 9_039_638 + .saturating_add(Weight::from_ref_time(1_393_054_441).saturating_mul(t.into())) + // Standard Error: 13_554 + .saturating_add(Weight::from_ref_time(9_819_606).saturating_mul(c.into())) .saturating_add(T::DbWeight::get().reads(167_u64)) .saturating_add(T::DbWeight::get().reads((81_u64).saturating_mul(t.into()))) .saturating_add(T::DbWeight::get().writes(163_u64)) .saturating_add(T::DbWeight::get().writes((81_u64).saturating_mul(t.into()))) + .saturating_add(Weight::from_proof_size(277320).saturating_mul(t.into())) } /// Storage: System Account (r:1602 w:1602) - /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: Ignored) + /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: Measured) /// Storage: Contracts ContractInfoOf (r:1601 w:1601) - /// Proof: Contracts ContractInfoOf (max_values: None, max_size: Some(258), added: 2733, mode: Ignored) - /// Storage: Contracts CodeStorage (r:1601 w:1600) - /// Proof: Contracts CodeStorage (max_values: None, max_size: Some(126001), added: 128476, mode: Ignored) + /// Proof: Contracts ContractInfoOf (max_values: None, max_size: Some(258), added: 2733, mode: Measured) + /// Storage: Contracts CodeStorage (r:1601 w:0) + /// Proof: Contracts CodeStorage (max_values: None, max_size: Some(126001), added: 128476, mode: Measured) /// Storage: Timestamp Now (r:1 w:0) - /// Proof: Timestamp Now (max_values: Some(1), max_size: Some(8), added: 503, mode: Ignored) + /// Proof: Timestamp Now (max_values: Some(1), max_size: Some(8), added: 503, mode: Measured) /// Storage: Contracts Nonce (r:1 w:1) - /// Proof: Contracts Nonce (max_values: Some(1), max_size: Some(8), added: 503, mode: Ignored) + /// Proof: Contracts Nonce (max_values: Some(1), max_size: Some(8), added: 503, mode: Measured) /// Storage: Contracts OwnerInfoOf (r:1600 w:1600) - /// Proof: Contracts OwnerInfoOf (max_values: None, max_size: Some(88), added: 2563, mode: Ignored) + /// Proof: Contracts OwnerInfoOf (max_values: None, max_size: Some(88), added: 2563, mode: Measured) /// Storage: System EventTopics (r:1602 w:1602) - /// Proof Skipped: System EventTopics (max_values: None, max_size: None, mode: Ignored) + /// Proof Skipped: System EventTopics (max_values: None, max_size: None, mode: Measured) /// The range of component `r` is `[0, 20]`. fn seal_instantiate(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `1613 + r * (25576 ±0)` - // Estimated: `0` - // Minimum execution time: 285_643 nanoseconds. - Weight::from_ref_time(287_472_000) - .saturating_add(Weight::from_proof_size(0)) - // Standard Error: 25_921_947 - .saturating_add(Weight::from_ref_time(27_745_815_511).saturating_mul(r.into())) + // Estimated: `25698 + r * (1169112 ±1)` + // Minimum execution time: 376_639 nanoseconds. + Weight::from_ref_time(377_892_000) + .saturating_add(Weight::from_proof_size(25698)) + // Standard Error: 21_259_255 + .saturating_add(Weight::from_ref_time(34_131_174_956).saturating_mul(r.into())) .saturating_add(T::DbWeight::get().reads(8_u64)) .saturating_add(T::DbWeight::get().reads((400_u64).saturating_mul(r.into()))) .saturating_add(T::DbWeight::get().writes(5_u64)) - .saturating_add(T::DbWeight::get().writes((400_u64).saturating_mul(r.into()))) + .saturating_add(T::DbWeight::get().writes((320_u64).saturating_mul(r.into()))) + .saturating_add(Weight::from_proof_size(1169112).saturating_mul(r.into())) } /// Storage: System Account (r:82 w:82) - /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: Ignored) + /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: Measured) /// Storage: Contracts ContractInfoOf (r:81 w:81) - /// Proof: Contracts ContractInfoOf (max_values: None, max_size: Some(258), added: 2733, mode: Ignored) - /// Storage: Contracts CodeStorage (r:2 w:1) - /// Proof: Contracts CodeStorage (max_values: None, max_size: Some(126001), added: 128476, mode: Ignored) + /// Proof: Contracts ContractInfoOf (max_values: None, max_size: Some(258), added: 2733, mode: Measured) + /// Storage: Contracts CodeStorage (r:2 w:0) + /// Proof: Contracts CodeStorage (max_values: None, max_size: Some(126001), added: 128476, mode: Measured) /// Storage: Timestamp Now (r:1 w:0) - /// Proof: Timestamp Now (max_values: Some(1), max_size: Some(8), added: 503, mode: Ignored) + /// Proof: Timestamp Now (max_values: Some(1), max_size: Some(8), added: 503, mode: Measured) /// Storage: Contracts Nonce (r:1 w:1) - /// Proof: Contracts Nonce (max_values: Some(1), max_size: Some(8), added: 503, mode: Ignored) + /// Proof: Contracts Nonce (max_values: Some(1), max_size: Some(8), added: 503, mode: Measured) /// Storage: Contracts OwnerInfoOf (r:1 w:1) - /// Proof: Contracts OwnerInfoOf (max_values: None, max_size: Some(88), added: 2563, mode: Ignored) + /// Proof: Contracts OwnerInfoOf (max_values: None, max_size: Some(88), added: 2563, mode: Measured) /// Storage: System EventTopics (r:82 w:82) - /// Proof Skipped: System EventTopics (max_values: None, max_size: None, mode: Ignored) + /// Proof Skipped: System EventTopics (max_values: None, max_size: None, mode: Measured) /// The range of component `t` is `[0, 1]`. /// The range of component `i` is `[0, 960]`. /// The range of component `s` is `[0, 960]`. fn seal_instantiate_per_transfer_input_salt_kb(t: u32, i: u32, s: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `5666 + t * (17 ±0)` - // Estimated: `0` - // Minimum execution time: 129_710_453 nanoseconds. - Weight::from_ref_time(14_347_603_160) - .saturating_add(Weight::from_proof_size(0)) - // Standard Error: 194_692 - .saturating_add(Weight::from_ref_time(128_837_066).saturating_mul(i.into())) - // Standard Error: 194_692 - .saturating_add(Weight::from_ref_time(127_568_555).saturating_mul(s.into())) + // Estimated: `651914 + t * (2762 ±3)` + // Minimum execution time: 130_366_376 nanoseconds. + Weight::from_ref_time(9_844_607_874) + .saturating_add(Weight::from_proof_size(651914)) + // Standard Error: 100_211_149 + .saturating_add(Weight::from_ref_time(390_481_449).saturating_mul(t.into())) + // Standard Error: 163_416 + .saturating_add(Weight::from_ref_time(126_154_200).saturating_mul(i.into())) + // Standard Error: 163_416 + .saturating_add(Weight::from_ref_time(126_430_874).saturating_mul(s.into())) .saturating_add(T::DbWeight::get().reads(249_u64)) .saturating_add(T::DbWeight::get().reads((1_u64).saturating_mul(t.into()))) - .saturating_add(T::DbWeight::get().writes(247_u64)) + .saturating_add(T::DbWeight::get().writes(246_u64)) .saturating_add(T::DbWeight::get().writes((1_u64).saturating_mul(t.into()))) + .saturating_add(Weight::from_proof_size(2762).saturating_mul(t.into())) } /// Storage: System Account (r:1 w:0) - /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: Ignored) + /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: Measured) /// Storage: Contracts ContractInfoOf (r:1 w:1) - /// Proof: Contracts ContractInfoOf (max_values: None, max_size: Some(258), added: 2733, mode: Ignored) + /// Proof: Contracts ContractInfoOf (max_values: None, max_size: Some(258), added: 2733, mode: Measured) /// Storage: Contracts CodeStorage (r:1 w:0) - /// Proof: Contracts CodeStorage (max_values: None, max_size: Some(126001), added: 128476, mode: Ignored) + /// Proof: Contracts CodeStorage (max_values: None, max_size: Some(126001), added: 128476, mode: Measured) /// Storage: Timestamp Now (r:1 w:0) - /// Proof: Timestamp Now (max_values: Some(1), max_size: Some(8), added: 503, mode: Ignored) + /// Proof: Timestamp Now (max_values: Some(1), max_size: Some(8), added: 503, mode: Measured) /// Storage: System EventTopics (r:2 w:2) - /// Proof Skipped: System EventTopics (max_values: None, max_size: None, mode: Ignored) + /// Proof Skipped: System EventTopics (max_values: None, max_size: None, mode: Measured) /// The range of component `r` is `[0, 1]`. fn seal_hash_sha2_256(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `839 + r * (642 ±0)` - // Estimated: `0` - // Minimum execution time: 282_059 nanoseconds. - Weight::from_ref_time(287_215_177) - .saturating_add(Weight::from_proof_size(0)) - // Standard Error: 273_484 - .saturating_add(Weight::from_ref_time(44_941_522).saturating_mul(r.into())) + // Estimated: `17065 + r * (3210 ±0)` + // Minimum execution time: 373_848 nanoseconds. + Weight::from_ref_time(375_974_597) + .saturating_add(Weight::from_proof_size(17065)) + // Standard Error: 558_923 + .saturating_add(Weight::from_ref_time(42_648_202).saturating_mul(r.into())) .saturating_add(T::DbWeight::get().reads(6_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) + .saturating_add(Weight::from_proof_size(3210).saturating_mul(r.into())) } /// Storage: System Account (r:1 w:0) - /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: Ignored) + /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: Measured) /// Storage: Contracts ContractInfoOf (r:1 w:1) - /// Proof: Contracts ContractInfoOf (max_values: None, max_size: Some(258), added: 2733, mode: Ignored) + /// Proof: Contracts ContractInfoOf (max_values: None, max_size: Some(258), added: 2733, mode: Measured) /// Storage: Contracts CodeStorage (r:1 w:0) - /// Proof: Contracts CodeStorage (max_values: None, max_size: Some(126001), added: 128476, mode: Ignored) + /// Proof: Contracts CodeStorage (max_values: None, max_size: Some(126001), added: 128476, mode: Measured) /// Storage: Timestamp Now (r:1 w:0) - /// Proof: Timestamp Now (max_values: Some(1), max_size: Some(8), added: 503, mode: Ignored) + /// Proof: Timestamp Now (max_values: Some(1), max_size: Some(8), added: 503, mode: Measured) /// Storage: System EventTopics (r:2 w:2) - /// Proof Skipped: System EventTopics (max_values: None, max_size: None, mode: Ignored) + /// Proof Skipped: System EventTopics (max_values: None, max_size: None, mode: Measured) /// The range of component `n` is `[0, 1024]`. fn seal_hash_sha2_256_per_kb(n: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `1641` - // Estimated: `0` - // Minimum execution time: 330_822 nanoseconds. - Weight::from_ref_time(333_012_000) - .saturating_add(Weight::from_proof_size(0)) - // Standard Error: 53_200 - .saturating_add(Weight::from_ref_time(327_144_474).saturating_mul(n.into())) + // Estimated: `21000` + // Minimum execution time: 417_281 nanoseconds. + Weight::from_ref_time(418_086_000) + .saturating_add(Weight::from_proof_size(21000)) + // Standard Error: 43_883 + .saturating_add(Weight::from_ref_time(324_497_460).saturating_mul(n.into())) .saturating_add(T::DbWeight::get().reads(6_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) } /// Storage: System Account (r:1 w:0) - /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: Ignored) + /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: Measured) /// Storage: Contracts ContractInfoOf (r:1 w:1) - /// Proof: Contracts ContractInfoOf (max_values: None, max_size: Some(258), added: 2733, mode: Ignored) + /// Proof: Contracts ContractInfoOf (max_values: None, max_size: Some(258), added: 2733, mode: Measured) /// Storage: Contracts CodeStorage (r:1 w:0) - /// Proof: Contracts CodeStorage (max_values: None, max_size: Some(126001), added: 128476, mode: Ignored) + /// Proof: Contracts CodeStorage (max_values: None, max_size: Some(126001), added: 128476, mode: Measured) /// Storage: Timestamp Now (r:1 w:0) - /// Proof: Timestamp Now (max_values: Some(1), max_size: Some(8), added: 503, mode: Ignored) + /// Proof: Timestamp Now (max_values: Some(1), max_size: Some(8), added: 503, mode: Measured) /// Storage: System EventTopics (r:2 w:2) - /// Proof Skipped: System EventTopics (max_values: None, max_size: None, mode: Ignored) + /// Proof Skipped: System EventTopics (max_values: None, max_size: None, mode: Measured) /// The range of component `r` is `[0, 1]`. fn seal_hash_keccak_256(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `841 + r * (642 ±0)` - // Estimated: `0` - // Minimum execution time: 282_859 nanoseconds. - Weight::from_ref_time(286_293_402) - .saturating_add(Weight::from_proof_size(0)) - // Standard Error: 187_930 - .saturating_add(Weight::from_ref_time(57_415_297).saturating_mul(r.into())) + // Estimated: `17075 + r * (3210 ±0)` + // Minimum execution time: 372_362 nanoseconds. + Weight::from_ref_time(374_975_677) + .saturating_add(Weight::from_proof_size(17075)) + // Standard Error: 307_932 + .saturating_add(Weight::from_ref_time(57_607_522).saturating_mul(r.into())) .saturating_add(T::DbWeight::get().reads(6_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) + .saturating_add(Weight::from_proof_size(3210).saturating_mul(r.into())) } /// Storage: System Account (r:1 w:0) - /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: Ignored) + /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: Measured) /// Storage: Contracts ContractInfoOf (r:1 w:1) - /// Proof: Contracts ContractInfoOf (max_values: None, max_size: Some(258), added: 2733, mode: Ignored) + /// Proof: Contracts ContractInfoOf (max_values: None, max_size: Some(258), added: 2733, mode: Measured) /// Storage: Contracts CodeStorage (r:1 w:0) - /// Proof: Contracts CodeStorage (max_values: None, max_size: Some(126001), added: 128476, mode: Ignored) + /// Proof: Contracts CodeStorage (max_values: None, max_size: Some(126001), added: 128476, mode: Measured) /// Storage: Timestamp Now (r:1 w:0) - /// Proof: Timestamp Now (max_values: Some(1), max_size: Some(8), added: 503, mode: Ignored) + /// Proof: Timestamp Now (max_values: Some(1), max_size: Some(8), added: 503, mode: Measured) /// Storage: System EventTopics (r:2 w:2) - /// Proof Skipped: System EventTopics (max_values: None, max_size: None, mode: Ignored) + /// Proof Skipped: System EventTopics (max_values: None, max_size: None, mode: Measured) /// The range of component `n` is `[0, 1024]`. fn seal_hash_keccak_256_per_kb(n: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `1643` - // Estimated: `0` - // Minimum execution time: 342_221 nanoseconds. - Weight::from_ref_time(343_250_000) - .saturating_add(Weight::from_proof_size(0)) - // Standard Error: 67_989 - .saturating_add(Weight::from_ref_time(261_498_077).saturating_mul(n.into())) + // Estimated: `21040` + // Minimum execution time: 431_034 nanoseconds. + Weight::from_ref_time(431_571_000) + .saturating_add(Weight::from_proof_size(21040)) + // Standard Error: 80_071 + .saturating_add(Weight::from_ref_time(261_645_325).saturating_mul(n.into())) .saturating_add(T::DbWeight::get().reads(6_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) } /// Storage: System Account (r:1 w:0) - /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: Ignored) + /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: Measured) /// Storage: Contracts ContractInfoOf (r:1 w:1) - /// Proof: Contracts ContractInfoOf (max_values: None, max_size: Some(258), added: 2733, mode: Ignored) + /// Proof: Contracts ContractInfoOf (max_values: None, max_size: Some(258), added: 2733, mode: Measured) /// Storage: Contracts CodeStorage (r:1 w:0) - /// Proof: Contracts CodeStorage (max_values: None, max_size: Some(126001), added: 128476, mode: Ignored) + /// Proof: Contracts CodeStorage (max_values: None, max_size: Some(126001), added: 128476, mode: Measured) /// Storage: Timestamp Now (r:1 w:0) - /// Proof: Timestamp Now (max_values: Some(1), max_size: Some(8), added: 503, mode: Ignored) + /// Proof: Timestamp Now (max_values: Some(1), max_size: Some(8), added: 503, mode: Measured) /// Storage: System EventTopics (r:2 w:2) - /// Proof Skipped: System EventTopics (max_values: None, max_size: None, mode: Ignored) + /// Proof Skipped: System EventTopics (max_values: None, max_size: None, mode: Measured) /// The range of component `r` is `[0, 1]`. fn seal_hash_blake2_256(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `841 + r * (642 ±0)` - // Estimated: `0` - // Minimum execution time: 282_158 nanoseconds. - Weight::from_ref_time(285_783_279) - .saturating_add(Weight::from_proof_size(0)) - // Standard Error: 200_413 - .saturating_add(Weight::from_ref_time(35_099_520).saturating_mul(r.into())) + // Estimated: `17075 + r * (3210 ±0)` + // Minimum execution time: 372_069 nanoseconds. + Weight::from_ref_time(374_200_608) + .saturating_add(Weight::from_proof_size(17075)) + // Standard Error: 171_799 + .saturating_add(Weight::from_ref_time(32_843_391).saturating_mul(r.into())) .saturating_add(T::DbWeight::get().reads(6_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) + .saturating_add(Weight::from_proof_size(3210).saturating_mul(r.into())) } /// Storage: System Account (r:1 w:0) - /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: Ignored) + /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: Measured) /// Storage: Contracts ContractInfoOf (r:1 w:1) - /// Proof: Contracts ContractInfoOf (max_values: None, max_size: Some(258), added: 2733, mode: Ignored) + /// Proof: Contracts ContractInfoOf (max_values: None, max_size: Some(258), added: 2733, mode: Measured) /// Storage: Contracts CodeStorage (r:1 w:0) - /// Proof: Contracts CodeStorage (max_values: None, max_size: Some(126001), added: 128476, mode: Ignored) + /// Proof: Contracts CodeStorage (max_values: None, max_size: Some(126001), added: 128476, mode: Measured) /// Storage: Timestamp Now (r:1 w:0) - /// Proof: Timestamp Now (max_values: Some(1), max_size: Some(8), added: 503, mode: Ignored) + /// Proof: Timestamp Now (max_values: Some(1), max_size: Some(8), added: 503, mode: Measured) /// Storage: System EventTopics (r:2 w:2) - /// Proof Skipped: System EventTopics (max_values: None, max_size: None, mode: Ignored) + /// Proof Skipped: System EventTopics (max_values: None, max_size: None, mode: Measured) /// The range of component `n` is `[0, 1024]`. fn seal_hash_blake2_256_per_kb(n: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `1643` - // Estimated: `0` - // Minimum execution time: 317_376 nanoseconds. - Weight::from_ref_time(319_441_000) - .saturating_add(Weight::from_proof_size(0)) - // Standard Error: 61_325 - .saturating_add(Weight::from_ref_time(101_080_739).saturating_mul(n.into())) + // Estimated: `21010` + // Minimum execution time: 406_143 nanoseconds. + Weight::from_ref_time(406_744_000) + .saturating_add(Weight::from_proof_size(21010)) + // Standard Error: 48_038 + .saturating_add(Weight::from_ref_time(103_286_295).saturating_mul(n.into())) .saturating_add(T::DbWeight::get().reads(6_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) } /// Storage: System Account (r:1 w:0) - /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: Ignored) + /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: Measured) /// Storage: Contracts ContractInfoOf (r:1 w:1) - /// Proof: Contracts ContractInfoOf (max_values: None, max_size: Some(258), added: 2733, mode: Ignored) + /// Proof: Contracts ContractInfoOf (max_values: None, max_size: Some(258), added: 2733, mode: Measured) /// Storage: Contracts CodeStorage (r:1 w:0) - /// Proof: Contracts CodeStorage (max_values: None, max_size: Some(126001), added: 128476, mode: Ignored) + /// Proof: Contracts CodeStorage (max_values: None, max_size: Some(126001), added: 128476, mode: Measured) /// Storage: Timestamp Now (r:1 w:0) - /// Proof: Timestamp Now (max_values: Some(1), max_size: Some(8), added: 503, mode: Ignored) + /// Proof: Timestamp Now (max_values: Some(1), max_size: Some(8), added: 503, mode: Measured) /// Storage: System EventTopics (r:2 w:2) - /// Proof Skipped: System EventTopics (max_values: None, max_size: None, mode: Ignored) + /// Proof Skipped: System EventTopics (max_values: None, max_size: None, mode: Measured) /// The range of component `r` is `[0, 1]`. fn seal_hash_blake2_128(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `841 + r * (679 ±0)` - // Estimated: `0` - // Minimum execution time: 280_403 nanoseconds. - Weight::from_ref_time(282_592_267) - .saturating_add(Weight::from_proof_size(0)) - // Standard Error: 237_832 - .saturating_add(Weight::from_ref_time(37_056_632).saturating_mul(r.into())) + // Estimated: `17075 + r * (3395 ±0)` + // Minimum execution time: 372_201 nanoseconds. + Weight::from_ref_time(374_049_708) + .saturating_add(Weight::from_proof_size(17075)) + // Standard Error: 387_179 + .saturating_add(Weight::from_ref_time(38_857_191).saturating_mul(r.into())) .saturating_add(T::DbWeight::get().reads(6_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) + .saturating_add(Weight::from_proof_size(3395).saturating_mul(r.into())) } /// Storage: System Account (r:1 w:0) - /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: Ignored) + /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: Measured) /// Storage: Contracts ContractInfoOf (r:1 w:1) - /// Proof: Contracts ContractInfoOf (max_values: None, max_size: Some(258), added: 2733, mode: Ignored) + /// Proof: Contracts ContractInfoOf (max_values: None, max_size: Some(258), added: 2733, mode: Measured) /// Storage: Contracts CodeStorage (r:1 w:0) - /// Proof: Contracts CodeStorage (max_values: None, max_size: Some(126001), added: 128476, mode: Ignored) + /// Proof: Contracts CodeStorage (max_values: None, max_size: Some(126001), added: 128476, mode: Measured) /// Storage: Timestamp Now (r:1 w:0) - /// Proof: Timestamp Now (max_values: Some(1), max_size: Some(8), added: 503, mode: Ignored) + /// Proof: Timestamp Now (max_values: Some(1), max_size: Some(8), added: 503, mode: Measured) /// Storage: System EventTopics (r:2 w:2) - /// Proof Skipped: System EventTopics (max_values: None, max_size: None, mode: Ignored) + /// Proof Skipped: System EventTopics (max_values: None, max_size: None, mode: Measured) /// The range of component `n` is `[0, 1024]`. fn seal_hash_blake2_128_per_kb(n: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `1643` - // Estimated: `0` - // Minimum execution time: 315_482 nanoseconds. - Weight::from_ref_time(316_160_000) - .saturating_add(Weight::from_proof_size(0)) - // Standard Error: 40_926 - .saturating_add(Weight::from_ref_time(100_317_235).saturating_mul(n.into())) + // Estimated: `21050` + // Minimum execution time: 405_819 nanoseconds. + Weight::from_ref_time(406_364_000) + .saturating_add(Weight::from_proof_size(21050)) + // Standard Error: 46_248 + .saturating_add(Weight::from_ref_time(103_189_157).saturating_mul(n.into())) .saturating_add(T::DbWeight::get().reads(6_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) } /// Storage: System Account (r:1 w:0) - /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: Ignored) + /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: Measured) /// Storage: Contracts ContractInfoOf (r:1 w:1) - /// Proof: Contracts ContractInfoOf (max_values: None, max_size: Some(258), added: 2733, mode: Ignored) + /// Proof: Contracts ContractInfoOf (max_values: None, max_size: Some(258), added: 2733, mode: Measured) /// Storage: Contracts CodeStorage (r:1 w:0) - /// Proof: Contracts CodeStorage (max_values: None, max_size: Some(126001), added: 128476, mode: Ignored) + /// Proof: Contracts CodeStorage (max_values: None, max_size: Some(126001), added: 128476, mode: Measured) /// Storage: Timestamp Now (r:1 w:0) - /// Proof: Timestamp Now (max_values: Some(1), max_size: Some(8), added: 503, mode: Ignored) + /// Proof: Timestamp Now (max_values: Some(1), max_size: Some(8), added: 503, mode: Measured) /// Storage: System EventTopics (r:2 w:2) - /// Proof Skipped: System EventTopics (max_values: None, max_size: None, mode: Ignored) + /// Proof Skipped: System EventTopics (max_values: None, max_size: None, mode: Measured) /// The range of component `r` is `[0, 1]`. fn seal_ecdsa_recover(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `885 + r * (6083 ±0)` - // Estimated: `0` - // Minimum execution time: 276_155 nanoseconds. - Weight::from_ref_time(278_531_728) - .saturating_add(Weight::from_proof_size(0)) - // Standard Error: 510_501 - .saturating_add(Weight::from_ref_time(3_056_236_671).saturating_mul(r.into())) + // Estimated: `17295 + r * (30415 ±0)` + // Minimum execution time: 374_836 nanoseconds. + Weight::from_ref_time(379_385_500) + .saturating_add(Weight::from_proof_size(17295)) + // Standard Error: 1_694_427 + .saturating_add(Weight::from_ref_time(3_021_801_000).saturating_mul(r.into())) .saturating_add(T::DbWeight::get().reads(6_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) + .saturating_add(Weight::from_proof_size(30415).saturating_mul(r.into())) } /// Storage: System Account (r:1 w:0) - /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: Ignored) + /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: Measured) /// Storage: Contracts ContractInfoOf (r:1 w:1) - /// Proof: Contracts ContractInfoOf (max_values: None, max_size: Some(258), added: 2733, mode: Ignored) + /// Proof: Contracts ContractInfoOf (max_values: None, max_size: Some(258), added: 2733, mode: Measured) /// Storage: Contracts CodeStorage (r:1 w:0) - /// Proof: Contracts CodeStorage (max_values: None, max_size: Some(126001), added: 128476, mode: Ignored) + /// Proof: Contracts CodeStorage (max_values: None, max_size: Some(126001), added: 128476, mode: Measured) /// Storage: Timestamp Now (r:1 w:0) - /// Proof: Timestamp Now (max_values: Some(1), max_size: Some(8), added: 503, mode: Ignored) + /// Proof: Timestamp Now (max_values: Some(1), max_size: Some(8), added: 503, mode: Measured) /// Storage: System EventTopics (r:2 w:2) - /// Proof Skipped: System EventTopics (max_values: None, max_size: None, mode: Ignored) + /// Proof Skipped: System EventTopics (max_values: None, max_size: None, mode: Measured) /// The range of component `r` is `[0, 1]`. fn seal_ecdsa_to_eth_address(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `854 + r * (3362 ±0)` - // Estimated: `0` - // Minimum execution time: 276_204 nanoseconds. - Weight::from_ref_time(278_114_283) - .saturating_add(Weight::from_proof_size(0)) - // Standard Error: 217_508 - .saturating_add(Weight::from_ref_time(737_577_816).saturating_mul(r.into())) + // Estimated: `17140 + r * (16810 ±0)` + // Minimum execution time: 374_223 nanoseconds. + Weight::from_ref_time(376_120_230) + .saturating_add(Weight::from_proof_size(17140)) + // Standard Error: 619_576 + .saturating_add(Weight::from_ref_time(754_257_969).saturating_mul(r.into())) .saturating_add(T::DbWeight::get().reads(6_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) + .saturating_add(Weight::from_proof_size(16810).saturating_mul(r.into())) } /// Storage: System Account (r:1 w:0) - /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: Ignored) + /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: Measured) /// Storage: Contracts ContractInfoOf (r:1 w:1) - /// Proof: Contracts ContractInfoOf (max_values: None, max_size: Some(258), added: 2733, mode: Ignored) + /// Proof: Contracts ContractInfoOf (max_values: None, max_size: Some(258), added: 2733, mode: Measured) /// Storage: Contracts CodeStorage (r:1536 w:0) - /// Proof: Contracts CodeStorage (max_values: None, max_size: Some(126001), added: 128476, mode: Ignored) + /// Proof: Contracts CodeStorage (max_values: None, max_size: Some(126001), added: 128476, mode: Measured) /// Storage: Timestamp Now (r:1 w:0) - /// Proof: Timestamp Now (max_values: Some(1), max_size: Some(8), added: 503, mode: Ignored) + /// Proof: Timestamp Now (max_values: Some(1), max_size: Some(8), added: 503, mode: Measured) /// Storage: Contracts OwnerInfoOf (r:1536 w:1536) - /// Proof: Contracts OwnerInfoOf (max_values: None, max_size: Some(88), added: 2563, mode: Ignored) + /// Proof: Contracts OwnerInfoOf (max_values: None, max_size: Some(88), added: 2563, mode: Measured) /// Storage: System EventTopics (r:1538 w:1538) - /// Proof Skipped: System EventTopics (max_values: None, max_size: None, mode: Ignored) + /// Proof Skipped: System EventTopics (max_values: None, max_size: None, mode: Measured) /// The range of component `r` is `[0, 20]`. fn seal_set_code_hash(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0 + r * (79300 ±0)` - // Estimated: `0` - // Minimum execution time: 276_330 nanoseconds. - Weight::from_ref_time(277_573_000) - .saturating_add(Weight::from_proof_size(0)) - // Standard Error: 3_652_635 - .saturating_add(Weight::from_ref_time(1_547_361_882).saturating_mul(r.into())) + // Estimated: `64652 + r * (942952 ±829)` + // Minimum execution time: 374_442 nanoseconds. + Weight::from_ref_time(375_591_000) + .saturating_add(Weight::from_proof_size(64652)) + // Standard Error: 3_764_193 + .saturating_add(Weight::from_ref_time(1_552_885_601).saturating_mul(r.into())) .saturating_add(T::DbWeight::get().reads(6_u64)) .saturating_add(T::DbWeight::get().reads((225_u64).saturating_mul(r.into()))) .saturating_add(T::DbWeight::get().writes(3_u64)) .saturating_add(T::DbWeight::get().writes((150_u64).saturating_mul(r.into()))) + .saturating_add(Weight::from_proof_size(942952).saturating_mul(r.into())) } /// Storage: System Account (r:1 w:0) - /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: Ignored) + /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: Measured) /// Storage: Contracts ContractInfoOf (r:1 w:1) - /// Proof: Contracts ContractInfoOf (max_values: None, max_size: Some(258), added: 2733, mode: Ignored) + /// Proof: Contracts ContractInfoOf (max_values: None, max_size: Some(258), added: 2733, mode: Measured) /// Storage: Contracts CodeStorage (r:1 w:0) - /// Proof: Contracts CodeStorage (max_values: None, max_size: Some(126001), added: 128476, mode: Ignored) + /// Proof: Contracts CodeStorage (max_values: None, max_size: Some(126001), added: 128476, mode: Measured) /// Storage: Timestamp Now (r:1 w:0) - /// Proof: Timestamp Now (max_values: Some(1), max_size: Some(8), added: 503, mode: Ignored) + /// Proof: Timestamp Now (max_values: Some(1), max_size: Some(8), added: 503, mode: Measured) /// Storage: System EventTopics (r:2 w:2) - /// Proof Skipped: System EventTopics (max_values: None, max_size: None, mode: Ignored) + /// Proof Skipped: System EventTopics (max_values: None, max_size: None, mode: Measured) /// The range of component `r` is `[0, 20]`. fn seal_reentrance_count(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `837 + r * (240 ±0)` - // Estimated: `0` - // Minimum execution time: 276_228 nanoseconds. - Weight::from_ref_time(281_135_551) - .saturating_add(Weight::from_proof_size(0)) - // Standard Error: 18_737 - .saturating_add(Weight::from_ref_time(10_998_953).saturating_mul(r.into())) + // Estimated: `17055 + r * (1200 ±0)` + // Minimum execution time: 374_922 nanoseconds. + Weight::from_ref_time(376_234_094) + .saturating_add(Weight::from_proof_size(17055)) + // Standard Error: 45_995 + .saturating_add(Weight::from_ref_time(11_606_505).saturating_mul(r.into())) .saturating_add(T::DbWeight::get().reads(6_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) + .saturating_add(Weight::from_proof_size(1200).saturating_mul(r.into())) } /// Storage: System Account (r:1 w:0) - /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: Ignored) + /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: Measured) /// Storage: Contracts ContractInfoOf (r:1 w:1) - /// Proof: Contracts ContractInfoOf (max_values: None, max_size: Some(258), added: 2733, mode: Ignored) + /// Proof: Contracts ContractInfoOf (max_values: None, max_size: Some(258), added: 2733, mode: Measured) /// Storage: Contracts CodeStorage (r:1 w:0) - /// Proof: Contracts CodeStorage (max_values: None, max_size: Some(126001), added: 128476, mode: Ignored) + /// Proof: Contracts CodeStorage (max_values: None, max_size: Some(126001), added: 128476, mode: Measured) /// Storage: Timestamp Now (r:1 w:0) - /// Proof: Timestamp Now (max_values: Some(1), max_size: Some(8), added: 503, mode: Ignored) + /// Proof: Timestamp Now (max_values: Some(1), max_size: Some(8), added: 503, mode: Measured) /// Storage: System EventTopics (r:2 w:2) - /// Proof Skipped: System EventTopics (max_values: None, max_size: None, mode: Ignored) + /// Proof Skipped: System EventTopics (max_values: None, max_size: None, mode: Measured) /// The range of component `r` is `[0, 20]`. fn seal_account_reentrance_count(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `2056 + r * (3153 ±0)` - // Estimated: `0` - // Minimum execution time: 278_157 nanoseconds. - Weight::from_ref_time(300_739_078) - .saturating_add(Weight::from_proof_size(0)) - // Standard Error: 72_695 - .saturating_add(Weight::from_ref_time(18_499_645).saturating_mul(r.into())) + // Estimated: `21730 + r * (15870 ±2)` + // Minimum execution time: 376_762 nanoseconds. + Weight::from_ref_time(396_934_359) + .saturating_add(Weight::from_proof_size(21730)) + // Standard Error: 68_263 + .saturating_add(Weight::from_ref_time(18_367_270).saturating_mul(r.into())) .saturating_add(T::DbWeight::get().reads(6_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) + .saturating_add(Weight::from_proof_size(15870).saturating_mul(r.into())) } /// Storage: System Account (r:1 w:0) - /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: Ignored) + /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: Measured) /// Storage: Contracts ContractInfoOf (r:1 w:1) - /// Proof: Contracts ContractInfoOf (max_values: None, max_size: Some(258), added: 2733, mode: Ignored) + /// Proof: Contracts ContractInfoOf (max_values: None, max_size: Some(258), added: 2733, mode: Measured) /// Storage: Contracts CodeStorage (r:1 w:0) - /// Proof: Contracts CodeStorage (max_values: None, max_size: Some(126001), added: 128476, mode: Ignored) + /// Proof: Contracts CodeStorage (max_values: None, max_size: Some(126001), added: 128476, mode: Measured) /// Storage: Timestamp Now (r:1 w:0) - /// Proof: Timestamp Now (max_values: Some(1), max_size: Some(8), added: 503, mode: Ignored) + /// Proof: Timestamp Now (max_values: Some(1), max_size: Some(8), added: 503, mode: Measured) /// Storage: Contracts Nonce (r:1 w:1) - /// Proof: Contracts Nonce (max_values: Some(1), max_size: Some(8), added: 503, mode: Ignored) + /// Proof: Contracts Nonce (max_values: Some(1), max_size: Some(8), added: 503, mode: Measured) /// Storage: System EventTopics (r:2 w:2) - /// Proof Skipped: System EventTopics (max_values: None, max_size: None, mode: Ignored) + /// Proof Skipped: System EventTopics (max_values: None, max_size: None, mode: Measured) /// The range of component `r` is `[0, 20]`. fn seal_instantiation_nonce(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `840 + r * (240 ±0)` - // Estimated: `0` - // Minimum execution time: 275_532 nanoseconds. - Weight::from_ref_time(281_299_677) - .saturating_add(Weight::from_proof_size(0)) - // Standard Error: 21_719 - .saturating_add(Weight::from_ref_time(9_513_444).saturating_mul(r.into())) + // Estimated: `18405 + r * (1440 ±0)` + // Minimum execution time: 374_257 nanoseconds. + Weight::from_ref_time(380_453_380) + .saturating_add(Weight::from_proof_size(18405)) + // Standard Error: 42_718 + .saturating_add(Weight::from_ref_time(9_355_253).saturating_mul(r.into())) .saturating_add(T::DbWeight::get().reads(7_u64)) .saturating_add(T::DbWeight::get().writes(4_u64)) + .saturating_add(Weight::from_proof_size(1440).saturating_mul(r.into())) } /// The range of component `r` is `[0, 50]`. fn instr_i64const(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 856 nanoseconds. - Weight::from_ref_time(1_054_710) + // Minimum execution time: 788 nanoseconds. + Weight::from_ref_time(1_209_829) .saturating_add(Weight::from_proof_size(0)) - // Standard Error: 273 - .saturating_add(Weight::from_ref_time(411_548).saturating_mul(r.into())) + // Standard Error: 3_436 + .saturating_add(Weight::from_ref_time(409_858).saturating_mul(r.into())) } /// The range of component `r` is `[0, 50]`. fn instr_i64load(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 980 nanoseconds. - Weight::from_ref_time(1_463_182) + // Minimum execution time: 955 nanoseconds. + Weight::from_ref_time(1_526_327) .saturating_add(Weight::from_proof_size(0)) - // Standard Error: 415 - .saturating_add(Weight::from_ref_time(1_063_436).saturating_mul(r.into())) + // Standard Error: 2_399 + .saturating_add(Weight::from_ref_time(1_084_504).saturating_mul(r.into())) } /// The range of component `r` is `[0, 50]`. fn instr_i64store(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 991 nanoseconds. - Weight::from_ref_time(1_521_750) + // Minimum execution time: 967 nanoseconds. + Weight::from_ref_time(1_576_183) .saturating_add(Weight::from_proof_size(0)) - // Standard Error: 8_499 - .saturating_add(Weight::from_ref_time(997_972).saturating_mul(r.into())) + // Standard Error: 5_719 + .saturating_add(Weight::from_ref_time(1_006_742).saturating_mul(r.into())) } /// The range of component `r` is `[0, 50]`. fn instr_select(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 833 nanoseconds. - Weight::from_ref_time(1_152_048) + // Minimum execution time: 849 nanoseconds. + Weight::from_ref_time(1_106_539) .saturating_add(Weight::from_proof_size(0)) - // Standard Error: 392 - .saturating_add(Weight::from_ref_time(1_147_506).saturating_mul(r.into())) + // Standard Error: 445 + .saturating_add(Weight::from_ref_time(1_149_752).saturating_mul(r.into())) } /// The range of component `r` is `[0, 50]`. fn instr_if(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 866 nanoseconds. - Weight::from_ref_time(1_154_637) + // Minimum execution time: 829 nanoseconds. + Weight::from_ref_time(1_171_360) .saturating_add(Weight::from_proof_size(0)) - // Standard Error: 5_131 - .saturating_add(Weight::from_ref_time(1_320_580).saturating_mul(r.into())) + // Standard Error: 552 + .saturating_add(Weight::from_ref_time(1_309_914).saturating_mul(r.into())) } /// The range of component `r` is `[0, 50]`. fn instr_br(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 849 nanoseconds. - Weight::from_ref_time(1_073_940) + // Minimum execution time: 835 nanoseconds. + Weight::from_ref_time(1_125_578) .saturating_add(Weight::from_proof_size(0)) - // Standard Error: 639 - .saturating_add(Weight::from_ref_time(642_885).saturating_mul(r.into())) + // Standard Error: 374 + .saturating_add(Weight::from_ref_time(641_683).saturating_mul(r.into())) } /// The range of component `r` is `[0, 50]`. fn instr_br_if(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 855 nanoseconds. - Weight::from_ref_time(728_631) + // Minimum execution time: 816 nanoseconds. + Weight::from_ref_time(1_032_093) .saturating_add(Weight::from_proof_size(0)) - // Standard Error: 1_730 - .saturating_add(Weight::from_ref_time(979_839).saturating_mul(r.into())) + // Standard Error: 811 + .saturating_add(Weight::from_ref_time(956_228).saturating_mul(r.into())) } /// The range of component `r` is `[0, 50]`. fn instr_br_table(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 852 nanoseconds. - Weight::from_ref_time(658_935) + // Minimum execution time: 801 nanoseconds. + Weight::from_ref_time(816_764) .saturating_add(Weight::from_proof_size(0)) - // Standard Error: 2_373 - .saturating_add(Weight::from_ref_time(1_172_585).saturating_mul(r.into())) + // Standard Error: 2_669 + .saturating_add(Weight::from_ref_time(1_166_556).saturating_mul(r.into())) } /// The range of component `e` is `[1, 256]`. fn instr_br_table_per_entry(e: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 2_577 nanoseconds. - Weight::from_ref_time(2_872_555) + // Minimum execution time: 2_639 nanoseconds. + Weight::from_ref_time(2_905_554) .saturating_add(Weight::from_proof_size(0)) - // Standard Error: 70 - .saturating_add(Weight::from_ref_time(4_237).saturating_mul(e.into())) + // Standard Error: 62 + .saturating_add(Weight::from_ref_time(4_438).saturating_mul(e.into())) } /// The range of component `r` is `[0, 50]`. fn instr_call(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 833 nanoseconds. - Weight::from_ref_time(874_000) + // Minimum execution time: 831 nanoseconds. + Weight::from_ref_time(1_729_584) .saturating_add(Weight::from_proof_size(0)) - // Standard Error: 30_863 - .saturating_add(Weight::from_ref_time(2_591_963).saturating_mul(r.into())) + // Standard Error: 33_753 + .saturating_add(Weight::from_ref_time(2_380_315).saturating_mul(r.into())) } /// The range of component `r` is `[0, 50]`. fn instr_call_indirect(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 992 nanoseconds. - Weight::from_ref_time(2_772_709) + // Minimum execution time: 964 nanoseconds. + Weight::from_ref_time(2_445_291) .saturating_add(Weight::from_proof_size(0)) - // Standard Error: 3_822 - .saturating_add(Weight::from_ref_time(3_070_825).saturating_mul(r.into())) + // Standard Error: 3_285 + .saturating_add(Weight::from_ref_time(2_938_681).saturating_mul(r.into())) } /// The range of component `p` is `[0, 128]`. fn instr_call_indirect_per_param(p: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 4_766 nanoseconds. - Weight::from_ref_time(5_559_951) + // Minimum execution time: 4_809 nanoseconds. + Weight::from_ref_time(6_763_286) .saturating_add(Weight::from_proof_size(0)) - // Standard Error: 315 - .saturating_add(Weight::from_ref_time(227_249).saturating_mul(p.into())) + // Standard Error: 3_994 + .saturating_add(Weight::from_ref_time(217_632).saturating_mul(p.into())) } /// The range of component `l` is `[0, 1024]`. fn instr_call_per_local(l: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 3_215 nanoseconds. - Weight::from_ref_time(4_697_732) + // Minimum execution time: 3_138 nanoseconds. + Weight::from_ref_time(3_894_816) .saturating_add(Weight::from_proof_size(0)) - // Standard Error: 99 - .saturating_add(Weight::from_ref_time(46_431).saturating_mul(l.into())) + // Standard Error: 131 + .saturating_add(Weight::from_ref_time(91_699).saturating_mul(l.into())) } /// The range of component `r` is `[0, 50]`. fn instr_local_get(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 2_193 nanoseconds. - Weight::from_ref_time(2_472_277) + // Minimum execution time: 2_959 nanoseconds. + Weight::from_ref_time(3_271_550) .saturating_add(Weight::from_proof_size(0)) - // Standard Error: 374 - .saturating_add(Weight::from_ref_time(459_651).saturating_mul(r.into())) + // Standard Error: 223 + .saturating_add(Weight::from_ref_time(460_056).saturating_mul(r.into())) } /// The range of component `r` is `[0, 50]`. fn instr_local_set(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 2_112 nanoseconds. - Weight::from_ref_time(2_388_132) + // Minimum execution time: 2_970 nanoseconds. + Weight::from_ref_time(3_216_157) .saturating_add(Weight::from_proof_size(0)) - // Standard Error: 380 - .saturating_add(Weight::from_ref_time(485_216).saturating_mul(r.into())) + // Standard Error: 413 + .saturating_add(Weight::from_ref_time(485_842).saturating_mul(r.into())) } /// The range of component `r` is `[0, 50]`. fn instr_local_tee(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 2_219 nanoseconds. - Weight::from_ref_time(2_419_568) + // Minimum execution time: 2_980 nanoseconds. + Weight::from_ref_time(3_323_878) .saturating_add(Weight::from_proof_size(0)) - // Standard Error: 402 - .saturating_add(Weight::from_ref_time(658_484).saturating_mul(r.into())) + // Standard Error: 2_652 + .saturating_add(Weight::from_ref_time(660_257).saturating_mul(r.into())) } /// The range of component `r` is `[0, 50]`. fn instr_global_get(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 959 nanoseconds. - Weight::from_ref_time(1_321_004) + // Minimum execution time: 917 nanoseconds. + Weight::from_ref_time(1_445_816) .saturating_add(Weight::from_proof_size(0)) - // Standard Error: 480 - .saturating_add(Weight::from_ref_time(898_447).saturating_mul(r.into())) + // Standard Error: 5_642 + .saturating_add(Weight::from_ref_time(894_521).saturating_mul(r.into())) } /// The range of component `r` is `[0, 50]`. fn instr_global_set(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 934 nanoseconds. - Weight::from_ref_time(1_264_350) + // Minimum execution time: 965 nanoseconds. + Weight::from_ref_time(1_373_722) .saturating_add(Weight::from_proof_size(0)) - // Standard Error: 1_031 - .saturating_add(Weight::from_ref_time(922_243).saturating_mul(r.into())) + // Standard Error: 1_157 + .saturating_add(Weight::from_ref_time(917_643).saturating_mul(r.into())) } /// The range of component `r` is `[0, 50]`. fn instr_memory_current(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 941 nanoseconds. - Weight::from_ref_time(1_229_407) + // Minimum execution time: 982 nanoseconds. + Weight::from_ref_time(1_240_280) .saturating_add(Weight::from_proof_size(0)) - // Standard Error: 507 - .saturating_add(Weight::from_ref_time(824_206).saturating_mul(r.into())) + // Standard Error: 308 + .saturating_add(Weight::from_ref_time(817_972).saturating_mul(r.into())) } /// The range of component `r` is `[0, 1]`. fn instr_memory_grow(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 870 nanoseconds. - Weight::from_ref_time(946_681) + // Minimum execution time: 858 nanoseconds. + Weight::from_ref_time(962_183) .saturating_add(Weight::from_proof_size(0)) - // Standard Error: 7_248 - .saturating_add(Weight::from_ref_time(179_759_218).saturating_mul(r.into())) + // Standard Error: 6_701 + .saturating_add(Weight::from_ref_time(239_704_216).saturating_mul(r.into())) } /// The range of component `r` is `[0, 50]`. fn instr_i64clz(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 843 nanoseconds. - Weight::from_ref_time(1_105_279) + // Minimum execution time: 807 nanoseconds. + Weight::from_ref_time(1_132_872) .saturating_add(Weight::from_proof_size(0)) - // Standard Error: 251 - .saturating_add(Weight::from_ref_time(633_218).saturating_mul(r.into())) + // Standard Error: 303 + .saturating_add(Weight::from_ref_time(633_832).saturating_mul(r.into())) } /// The range of component `r` is `[0, 50]`. fn instr_i64ctz(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 843 nanoseconds. - Weight::from_ref_time(1_119_719) + // Minimum execution time: 823 nanoseconds. + Weight::from_ref_time(1_267_518) .saturating_add(Weight::from_proof_size(0)) - // Standard Error: 248 - .saturating_add(Weight::from_ref_time(633_064).saturating_mul(r.into())) + // Standard Error: 483 + .saturating_add(Weight::from_ref_time(632_620).saturating_mul(r.into())) } /// The range of component `r` is `[0, 50]`. fn instr_i64popcnt(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 865 nanoseconds. - Weight::from_ref_time(1_394_651) + // Minimum execution time: 857 nanoseconds. + Weight::from_ref_time(1_105_214) .saturating_add(Weight::from_proof_size(0)) - // Standard Error: 9_898 - .saturating_add(Weight::from_ref_time(630_054).saturating_mul(r.into())) + // Standard Error: 285 + .saturating_add(Weight::from_ref_time(635_039).saturating_mul(r.into())) } /// The range of component `r` is `[0, 50]`. fn instr_i64eqz(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 831 nanoseconds. - Weight::from_ref_time(1_095_288) + // Minimum execution time: 821 nanoseconds. + Weight::from_ref_time(750_223) .saturating_add(Weight::from_proof_size(0)) - // Standard Error: 271 - .saturating_add(Weight::from_ref_time(650_578).saturating_mul(r.into())) + // Standard Error: 15_923 + .saturating_add(Weight::from_ref_time(686_322).saturating_mul(r.into())) } /// The range of component `r` is `[0, 50]`. fn instr_i64extendsi32(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 835 nanoseconds. - Weight::from_ref_time(1_128_234) + // Minimum execution time: 797 nanoseconds. + Weight::from_ref_time(1_145_072) .saturating_add(Weight::from_proof_size(0)) - // Standard Error: 239 - .saturating_add(Weight::from_ref_time(615_970).saturating_mul(r.into())) + // Standard Error: 311 + .saturating_add(Weight::from_ref_time(618_147).saturating_mul(r.into())) } /// The range of component `r` is `[0, 50]`. fn instr_i64extendui32(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 839 nanoseconds. - Weight::from_ref_time(1_111_296) + // Minimum execution time: 803 nanoseconds. + Weight::from_ref_time(1_139_498) .saturating_add(Weight::from_proof_size(0)) - // Standard Error: 303 - .saturating_add(Weight::from_ref_time(616_831).saturating_mul(r.into())) + // Standard Error: 284 + .saturating_add(Weight::from_ref_time(617_393).saturating_mul(r.into())) } /// The range of component `r` is `[0, 50]`. fn instr_i32wrapi64(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 839 nanoseconds. - Weight::from_ref_time(1_082_700) + // Minimum execution time: 814 nanoseconds. + Weight::from_ref_time(1_099_405) .saturating_add(Weight::from_proof_size(0)) - // Standard Error: 236 - .saturating_add(Weight::from_ref_time(617_371).saturating_mul(r.into())) + // Standard Error: 663 + .saturating_add(Weight::from_ref_time(618_565).saturating_mul(r.into())) } /// The range of component `r` is `[0, 50]`. fn instr_i64eq(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 837 nanoseconds. - Weight::from_ref_time(1_249_666) + // Minimum execution time: 819 nanoseconds. + Weight::from_ref_time(1_199_220) .saturating_add(Weight::from_proof_size(0)) - // Standard Error: 836 - .saturating_add(Weight::from_ref_time(904_457).saturating_mul(r.into())) + // Standard Error: 485 + .saturating_add(Weight::from_ref_time(906_878).saturating_mul(r.into())) } /// The range of component `r` is `[0, 50]`. fn instr_i64ne(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 818 nanoseconds. - Weight::from_ref_time(1_624_057) + // Minimum execution time: 822 nanoseconds. + Weight::from_ref_time(274_212) .saturating_add(Weight::from_proof_size(0)) - // Standard Error: 5_906 - .saturating_add(Weight::from_ref_time(893_488).saturating_mul(r.into())) + // Standard Error: 29_294 + .saturating_add(Weight::from_ref_time(971_608).saturating_mul(r.into())) } /// The range of component `r` is `[0, 50]`. fn instr_i64lts(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 836 nanoseconds. - Weight::from_ref_time(1_973_044) + // Minimum execution time: 796 nanoseconds. + Weight::from_ref_time(1_396_586) .saturating_add(Weight::from_proof_size(0)) - // Standard Error: 7_556 - .saturating_add(Weight::from_ref_time(885_369).saturating_mul(r.into())) + // Standard Error: 7_205 + .saturating_add(Weight::from_ref_time(903_202).saturating_mul(r.into())) } /// The range of component `r` is `[0, 50]`. fn instr_i64ltu(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 846 nanoseconds. - Weight::from_ref_time(1_088_223) + // Minimum execution time: 833 nanoseconds. + Weight::from_ref_time(1_115_115) .saturating_add(Weight::from_proof_size(0)) - // Standard Error: 531 - .saturating_add(Weight::from_ref_time(909_313).saturating_mul(r.into())) + // Standard Error: 310 + .saturating_add(Weight::from_ref_time(908_195).saturating_mul(r.into())) } /// The range of component `r` is `[0, 50]`. fn instr_i64gts(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 842 nanoseconds. - Weight::from_ref_time(1_435_966) + // Minimum execution time: 854 nanoseconds. + Weight::from_ref_time(1_170_419) .saturating_add(Weight::from_proof_size(0)) - // Standard Error: 3_824 - .saturating_add(Weight::from_ref_time(898_198).saturating_mul(r.into())) + // Standard Error: 298 + .saturating_add(Weight::from_ref_time(907_171).saturating_mul(r.into())) } /// The range of component `r` is `[0, 50]`. fn instr_i64gtu(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 846 nanoseconds. - Weight::from_ref_time(1_154_015) + // Minimum execution time: 822 nanoseconds. + Weight::from_ref_time(1_186_349) .saturating_add(Weight::from_proof_size(0)) - // Standard Error: 319 - .saturating_add(Weight::from_ref_time(918_083).saturating_mul(r.into())) + // Standard Error: 302 + .saturating_add(Weight::from_ref_time(917_857).saturating_mul(r.into())) } /// The range of component `r` is `[0, 50]`. fn instr_i64les(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 848 nanoseconds. - Weight::from_ref_time(1_155_323) + // Minimum execution time: 807 nanoseconds. + Weight::from_ref_time(1_127_093) .saturating_add(Weight::from_proof_size(0)) - // Standard Error: 307 - .saturating_add(Weight::from_ref_time(906_516).saturating_mul(r.into())) + // Standard Error: 1_891 + .saturating_add(Weight::from_ref_time(910_738).saturating_mul(r.into())) } /// The range of component `r` is `[0, 50]`. fn instr_i64leu(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 877 nanoseconds. - Weight::from_ref_time(1_629_210) + // Minimum execution time: 819 nanoseconds. + Weight::from_ref_time(1_143_022) .saturating_add(Weight::from_proof_size(0)) - // Standard Error: 8_175 - .saturating_add(Weight::from_ref_time(906_657).saturating_mul(r.into())) + // Standard Error: 309 + .saturating_add(Weight::from_ref_time(919_047).saturating_mul(r.into())) } /// The range of component `r` is `[0, 50]`. fn instr_i64ges(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 843 nanoseconds. - Weight::from_ref_time(1_126_252) + // Minimum execution time: 788 nanoseconds. + Weight::from_ref_time(1_116_914) .saturating_add(Weight::from_proof_size(0)) - // Standard Error: 251 - .saturating_add(Weight::from_ref_time(907_052).saturating_mul(r.into())) + // Standard Error: 643 + .saturating_add(Weight::from_ref_time(911_159).saturating_mul(r.into())) } /// The range of component `r` is `[0, 50]`. fn instr_i64geu(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 852 nanoseconds. - Weight::from_ref_time(1_497_667) + // Minimum execution time: 791 nanoseconds. + Weight::from_ref_time(1_123_747) .saturating_add(Weight::from_proof_size(0)) - // Standard Error: 7_593 - .saturating_add(Weight::from_ref_time(897_474).saturating_mul(r.into())) + // Standard Error: 583 + .saturating_add(Weight::from_ref_time(910_242).saturating_mul(r.into())) } /// The range of component `r` is `[0, 50]`. fn instr_i64add(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 856 nanoseconds. - Weight::from_ref_time(1_350_390) + // Minimum execution time: 787 nanoseconds. + Weight::from_ref_time(1_109_471) .saturating_add(Weight::from_proof_size(0)) - // Standard Error: 2_625 - .saturating_add(Weight::from_ref_time(889_609).saturating_mul(r.into())) + // Standard Error: 305 + .saturating_add(Weight::from_ref_time(897_608).saturating_mul(r.into())) } /// The range of component `r` is `[0, 50]`. fn instr_i64sub(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 841 nanoseconds. - Weight::from_ref_time(2_677_235) + // Minimum execution time: 821 nanoseconds. + Weight::from_ref_time(1_098_631) .saturating_add(Weight::from_proof_size(0)) - // Standard Error: 11_507 - .saturating_add(Weight::from_ref_time(846_704).saturating_mul(r.into())) + // Standard Error: 523 + .saturating_add(Weight::from_ref_time(887_814).saturating_mul(r.into())) } /// The range of component `r` is `[0, 50]`. fn instr_i64mul(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 846 nanoseconds. - Weight::from_ref_time(1_148_206) + // Minimum execution time: 794 nanoseconds. + Weight::from_ref_time(1_166_332) .saturating_add(Weight::from_proof_size(0)) - // Standard Error: 393 - .saturating_add(Weight::from_ref_time(884_886).saturating_mul(r.into())) + // Standard Error: 384 + .saturating_add(Weight::from_ref_time(885_584).saturating_mul(r.into())) } /// The range of component `r` is `[0, 50]`. fn instr_i64divs(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 865 nanoseconds. - Weight::from_ref_time(2_862_483) + // Minimum execution time: 822 nanoseconds. + Weight::from_ref_time(1_155_826) .saturating_add(Weight::from_proof_size(0)) - // Standard Error: 16_413 - .saturating_add(Weight::from_ref_time(1_481_623).saturating_mul(r.into())) + // Standard Error: 521 + .saturating_add(Weight::from_ref_time(1_520_958).saturating_mul(r.into())) } /// The range of component `r` is `[0, 50]`. fn instr_i64divu(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_443 nanoseconds. - Weight::from_ref_time(1_086_902) + // Minimum execution time: 881 nanoseconds. + Weight::from_ref_time(1_158_125) .saturating_add(Weight::from_proof_size(0)) - // Standard Error: 2_245 - .saturating_add(Weight::from_ref_time(1_463_145).saturating_mul(r.into())) + // Standard Error: 624 + .saturating_add(Weight::from_ref_time(1_458_378).saturating_mul(r.into())) } /// The range of component `r` is `[0, 50]`. fn instr_i64rems(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 878 nanoseconds. - Weight::from_ref_time(1_130_306) + // Minimum execution time: 827 nanoseconds. + Weight::from_ref_time(1_209_535) .saturating_add(Weight::from_proof_size(0)) - // Standard Error: 475 - .saturating_add(Weight::from_ref_time(1_522_011).saturating_mul(r.into())) + // Standard Error: 399 + .saturating_add(Weight::from_ref_time(1_547_640).saturating_mul(r.into())) } /// The range of component `r` is `[0, 50]`. fn instr_i64remu(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 827 nanoseconds. - Weight::from_ref_time(1_197_053) + // Minimum execution time: 801 nanoseconds. + Weight::from_ref_time(1_313_872) .saturating_add(Weight::from_proof_size(0)) - // Standard Error: 474 - .saturating_add(Weight::from_ref_time(1_449_062).saturating_mul(r.into())) + // Standard Error: 2_175 + .saturating_add(Weight::from_ref_time(1_449_416).saturating_mul(r.into())) } /// The range of component `r` is `[0, 50]`. fn instr_i64and(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 847 nanoseconds. - Weight::from_ref_time(1_152_423) + // Minimum execution time: 827 nanoseconds. + Weight::from_ref_time(1_093_874) .saturating_add(Weight::from_proof_size(0)) - // Standard Error: 296 - .saturating_add(Weight::from_ref_time(895_541).saturating_mul(r.into())) + // Standard Error: 708 + .saturating_add(Weight::from_ref_time(901_450).saturating_mul(r.into())) } /// The range of component `r` is `[0, 50]`. fn instr_i64or(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 876 nanoseconds. - Weight::from_ref_time(1_169_485) + // Minimum execution time: 824 nanoseconds. + Weight::from_ref_time(1_164_076) .saturating_add(Weight::from_proof_size(0)) - // Standard Error: 571 - .saturating_add(Weight::from_ref_time(932_659).saturating_mul(r.into())) + // Standard Error: 623 + .saturating_add(Weight::from_ref_time(897_579).saturating_mul(r.into())) } /// The range of component `r` is `[0, 50]`. fn instr_i64xor(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 856 nanoseconds. - Weight::from_ref_time(1_155_127) + // Minimum execution time: 789 nanoseconds. + Weight::from_ref_time(1_113_915) .saturating_add(Weight::from_proof_size(0)) - // Standard Error: 256 - .saturating_add(Weight::from_ref_time(895_663).saturating_mul(r.into())) + // Standard Error: 390 + .saturating_add(Weight::from_ref_time(897_354).saturating_mul(r.into())) } /// The range of component `r` is `[0, 50]`. fn instr_i64shl(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 871 nanoseconds. - Weight::from_ref_time(1_139_722) + // Minimum execution time: 811 nanoseconds. + Weight::from_ref_time(1_117_366) .saturating_add(Weight::from_proof_size(0)) - // Standard Error: 367 - .saturating_add(Weight::from_ref_time(903_115).saturating_mul(r.into())) + // Standard Error: 437 + .saturating_add(Weight::from_ref_time(903_759).saturating_mul(r.into())) } /// The range of component `r` is `[0, 50]`. fn instr_i64shrs(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 853 nanoseconds. - Weight::from_ref_time(1_155_542) + // Minimum execution time: 846 nanoseconds. + Weight::from_ref_time(1_103_954) .saturating_add(Weight::from_proof_size(0)) - // Standard Error: 541 - .saturating_add(Weight::from_ref_time(901_635).saturating_mul(r.into())) + // Standard Error: 414 + .saturating_add(Weight::from_ref_time(903_429).saturating_mul(r.into())) } /// The range of component `r` is `[0, 50]`. fn instr_i64shru(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 866 nanoseconds. - Weight::from_ref_time(2_260_588) + // Minimum execution time: 803 nanoseconds. + Weight::from_ref_time(1_124_328) .saturating_add(Weight::from_proof_size(0)) - // Standard Error: 12_944 - .saturating_add(Weight::from_ref_time(877_790).saturating_mul(r.into())) + // Standard Error: 410 + .saturating_add(Weight::from_ref_time(903_216).saturating_mul(r.into())) } /// The range of component `r` is `[0, 50]`. fn instr_i64rotl(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 851 nanoseconds. - Weight::from_ref_time(1_627_816) + // Minimum execution time: 810 nanoseconds. + Weight::from_ref_time(1_131_433) .saturating_add(Weight::from_proof_size(0)) - // Standard Error: 4_004 - .saturating_add(Weight::from_ref_time(887_929).saturating_mul(r.into())) + // Standard Error: 402 + .saturating_add(Weight::from_ref_time(903_381).saturating_mul(r.into())) } /// The range of component `r` is `[0, 50]`. fn instr_i64rotr(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 874 nanoseconds. - Weight::from_ref_time(2_611_817) + // Minimum execution time: 807 nanoseconds. + Weight::from_ref_time(1_144_933) .saturating_add(Weight::from_proof_size(0)) - // Standard Error: 10_026 - .saturating_add(Weight::from_ref_time(862_138).saturating_mul(r.into())) + // Standard Error: 373 + .saturating_add(Weight::from_ref_time(902_918).saturating_mul(r.into())) } } // For backwards compatibility and tests impl WeightInfo for () { /// Storage: Contracts DeletionQueue (r:1 w:0) - /// Proof: Contracts DeletionQueue (max_values: Some(1), max_size: Some(16642), added: 17137, mode: Ignored) + /// Proof: Contracts DeletionQueue (max_values: Some(1), max_size: Some(16642), added: 17137, mode: Measured) fn on_process_deletion_queue_batch() -> Weight { // Proof Size summary in bytes: // Measured: `109` - // Estimated: `0` - // Minimum execution time: 2_564 nanoseconds. - Weight::from_ref_time(2_722_000) - .saturating_add(Weight::from_proof_size(0)) + // Estimated: `604` + // Minimum execution time: 2_507 nanoseconds. + Weight::from_ref_time(2_650_000) + .saturating_add(Weight::from_proof_size(604)) .saturating_add(RocksDbWeight::get().reads(1_u64)) } /// Storage: Skipped Metadata (r:0 w:0) - /// Proof Skipped: Skipped Metadata (max_values: None, max_size: None, mode: Ignored) + /// Proof Skipped: Skipped Metadata (max_values: None, max_size: None, mode: Measured) /// The range of component `k` is `[0, 1024]`. fn on_initialize_per_trie_key(k: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `481 + k * (69 ±0)` - // Estimated: `0` - // Minimum execution time: 10_292 nanoseconds. - Weight::from_ref_time(7_474_496) - .saturating_add(Weight::from_proof_size(0)) - // Standard Error: 901 - .saturating_add(Weight::from_ref_time(956_864).saturating_mul(k.into())) + // Estimated: `471 + k * (70 ±0)` + // Minimum execution time: 9_953 nanoseconds. + Weight::from_ref_time(6_467_352) + .saturating_add(Weight::from_proof_size(471)) + // Standard Error: 1_074 + .saturating_add(Weight::from_ref_time(943_946).saturating_mul(k.into())) .saturating_add(RocksDbWeight::get().reads(1_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) .saturating_add(RocksDbWeight::get().writes((1_u64).saturating_mul(k.into()))) + .saturating_add(Weight::from_proof_size(70).saturating_mul(k.into())) } /// Storage: Contracts DeletionQueue (r:1 w:1) - /// Proof: Contracts DeletionQueue (max_values: Some(1), max_size: Some(16642), added: 17137, mode: Ignored) + /// Proof: Contracts DeletionQueue (max_values: Some(1), max_size: Some(16642), added: 17137, mode: Measured) /// The range of component `q` is `[0, 128]`. fn on_initialize_per_queue_item(q: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `281 + q * (33 ±0)` - // Estimated: `0` - // Minimum execution time: 2_620 nanoseconds. - Weight::from_ref_time(10_288_873) - .saturating_add(Weight::from_proof_size(0)) - // Standard Error: 3_073 - .saturating_add(Weight::from_ref_time(1_148_167).saturating_mul(q.into())) + // Estimated: `763 + q * (33 ±0)` + // Minimum execution time: 2_545 nanoseconds. + Weight::from_ref_time(10_080_106) + .saturating_add(Weight::from_proof_size(763)) + // Standard Error: 2_929 + .saturating_add(Weight::from_ref_time(1_078_265).saturating_mul(q.into())) .saturating_add(RocksDbWeight::get().reads(1_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) + .saturating_add(Weight::from_proof_size(33).saturating_mul(q.into())) } /// Storage: Contracts PristineCode (r:1 w:0) - /// Proof: Contracts PristineCode (max_values: None, max_size: Some(125988), added: 128463, mode: Ignored) + /// Proof: Contracts PristineCode (max_values: None, max_size: Some(125988), added: 128463, mode: Measured) /// Storage: Contracts CodeStorage (r:0 w:1) - /// Proof: Contracts CodeStorage (max_values: None, max_size: Some(126001), added: 128476, mode: Ignored) + /// Proof: Contracts CodeStorage (max_values: None, max_size: Some(126001), added: 128476, mode: Measured) /// The range of component `c` is `[0, 61717]`. fn reinstrument(c: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `270 + c * (1 ±0)` - // Estimated: `0` - // Minimum execution time: 28_004 nanoseconds. - Weight::from_ref_time(26_706_943) - .saturating_add(Weight::from_proof_size(0)) - // Standard Error: 67 - .saturating_add(Weight::from_ref_time(51_603).saturating_mul(c.into())) + // Estimated: `3025 + c * (2 ±0)` + // Minimum execution time: 34_613 nanoseconds. + Weight::from_ref_time(27_355_774) + .saturating_add(Weight::from_proof_size(3025)) + // Standard Error: 81 + .saturating_add(Weight::from_ref_time(51_954).saturating_mul(c.into())) .saturating_add(RocksDbWeight::get().reads(1_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) + .saturating_add(Weight::from_proof_size(2).saturating_mul(c.into())) } /// Storage: Contracts ContractInfoOf (r:1 w:1) - /// Proof: Contracts ContractInfoOf (max_values: None, max_size: Some(258), added: 2733, mode: Ignored) + /// Proof: Contracts ContractInfoOf (max_values: None, max_size: Some(258), added: 2733, mode: Measured) /// Storage: Contracts CodeStorage (r:1 w:0) - /// Proof: Contracts CodeStorage (max_values: None, max_size: Some(126001), added: 128476, mode: Ignored) + /// Proof: Contracts CodeStorage (max_values: None, max_size: Some(126001), added: 128476, mode: Measured) /// Storage: Timestamp Now (r:1 w:0) - /// Proof: Timestamp Now (max_values: Some(1), max_size: Some(8), added: 503, mode: Ignored) + /// Proof: Timestamp Now (max_values: Some(1), max_size: Some(8), added: 503, mode: Measured) /// Storage: System Account (r:1 w:1) - /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: Ignored) + /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: Measured) /// Storage: System EventTopics (r:2 w:2) - /// Proof Skipped: System EventTopics (max_values: None, max_size: None, mode: Ignored) + /// Proof Skipped: System EventTopics (max_values: None, max_size: None, mode: Measured) /// The range of component `c` is `[0, 125952]`. fn call_with_code_per_byte(c: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `771` - // Estimated: `0` - // Minimum execution time: 295_799 nanoseconds. - Weight::from_ref_time(308_660_753) - .saturating_add(Weight::from_proof_size(0)) - // Standard Error: 20 - .saturating_add(Weight::from_ref_time(32_458).saturating_mul(c.into())) + // Estimated: `16780 + c * (5 ±0)` + // Minimum execution time: 386_104 nanoseconds. + Weight::from_ref_time(396_718_823) + .saturating_add(Weight::from_proof_size(16780)) + // Standard Error: 27 + .saturating_add(Weight::from_ref_time(31_370).saturating_mul(c.into())) .saturating_add(RocksDbWeight::get().reads(6_u64)) .saturating_add(RocksDbWeight::get().writes(4_u64)) + .saturating_add(Weight::from_proof_size(5).saturating_mul(c.into())) } - /// Storage: Contracts CodeStorage (r:1 w:1) - /// Proof: Contracts CodeStorage (max_values: None, max_size: Some(126001), added: 128476, mode: Ignored) + /// Storage: Contracts OwnerInfoOf (r:1 w:1) + /// Proof: Contracts OwnerInfoOf (max_values: None, max_size: Some(88), added: 2563, mode: Measured) /// Storage: Contracts Nonce (r:1 w:1) - /// Proof: Contracts Nonce (max_values: Some(1), max_size: Some(8), added: 503, mode: Ignored) + /// Proof: Contracts Nonce (max_values: Some(1), max_size: Some(8), added: 503, mode: Measured) /// Storage: Contracts ContractInfoOf (r:1 w:1) - /// Proof: Contracts ContractInfoOf (max_values: None, max_size: Some(258), added: 2733, mode: Ignored) + /// Proof: Contracts ContractInfoOf (max_values: None, max_size: Some(258), added: 2733, mode: Measured) /// Storage: Timestamp Now (r:1 w:0) - /// Proof: Timestamp Now (max_values: Some(1), max_size: Some(8), added: 503, mode: Ignored) + /// Proof: Timestamp Now (max_values: Some(1), max_size: Some(8), added: 503, mode: Measured) /// Storage: System Account (r:1 w:1) - /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: Ignored) + /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: Measured) /// Storage: System EventTopics (r:3 w:3) - /// Proof Skipped: System EventTopics (max_values: None, max_size: None, mode: Ignored) + /// Proof Skipped: System EventTopics (max_values: None, max_size: None, mode: Measured) + /// Storage: Contracts CodeStorage (r:0 w:1) + /// Proof: Contracts CodeStorage (max_values: None, max_size: Some(126001), added: 128476, mode: Measured) /// Storage: Contracts PristineCode (r:0 w:1) - /// Proof: Contracts PristineCode (max_values: None, max_size: Some(125988), added: 128463, mode: Ignored) - /// Storage: Contracts OwnerInfoOf (r:0 w:1) - /// Proof: Contracts OwnerInfoOf (max_values: None, max_size: Some(88), added: 2563, mode: Ignored) + /// Proof: Contracts PristineCode (max_values: None, max_size: Some(125988), added: 128463, mode: Measured) /// The range of component `c` is `[0, 61717]`. /// The range of component `i` is `[0, 1048576]`. /// The range of component `s` is `[0, 1048576]`. fn instantiate_with_code(c: u32, i: u32, s: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `257` - // Estimated: `0` - // Minimum execution time: 3_754_887 nanoseconds. - Weight::from_ref_time(657_695_827) - .saturating_add(Weight::from_proof_size(0)) - // Standard Error: 283 - .saturating_add(Weight::from_ref_time(94_808).saturating_mul(c.into())) + // Estimated: `17752` + // Minimum execution time: 3_786_500 nanoseconds. + Weight::from_ref_time(672_511_565) + .saturating_add(Weight::from_proof_size(17752)) + // Standard Error: 281 + .saturating_add(Weight::from_ref_time(94_538).saturating_mul(c.into())) // Standard Error: 16 - .saturating_add(Weight::from_ref_time(1_357).saturating_mul(i.into())) + .saturating_add(Weight::from_ref_time(1_364).saturating_mul(i.into())) // Standard Error: 16 - .saturating_add(Weight::from_ref_time(1_756).saturating_mul(s.into())) + .saturating_add(Weight::from_ref_time(1_768).saturating_mul(s.into())) .saturating_add(RocksDbWeight::get().reads(8_u64)) .saturating_add(RocksDbWeight::get().writes(9_u64)) } - /// Storage: Contracts CodeStorage (r:1 w:1) - /// Proof: Contracts CodeStorage (max_values: None, max_size: Some(126001), added: 128476, mode: Ignored) + /// Storage: Contracts CodeStorage (r:1 w:0) + /// Proof: Contracts CodeStorage (max_values: None, max_size: Some(126001), added: 128476, mode: Measured) /// Storage: Contracts Nonce (r:1 w:1) - /// Proof: Contracts Nonce (max_values: Some(1), max_size: Some(8), added: 503, mode: Ignored) + /// Proof: Contracts Nonce (max_values: Some(1), max_size: Some(8), added: 503, mode: Measured) /// Storage: Contracts ContractInfoOf (r:1 w:1) - /// Proof: Contracts ContractInfoOf (max_values: None, max_size: Some(258), added: 2733, mode: Ignored) + /// Proof: Contracts ContractInfoOf (max_values: None, max_size: Some(258), added: 2733, mode: Measured) /// Storage: Timestamp Now (r:1 w:0) - /// Proof: Timestamp Now (max_values: Some(1), max_size: Some(8), added: 503, mode: Ignored) + /// Proof: Timestamp Now (max_values: Some(1), max_size: Some(8), added: 503, mode: Measured) /// Storage: System Account (r:1 w:1) - /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: Ignored) + /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: Measured) /// Storage: Contracts OwnerInfoOf (r:1 w:1) - /// Proof: Contracts OwnerInfoOf (max_values: None, max_size: Some(88), added: 2563, mode: Ignored) + /// Proof: Contracts OwnerInfoOf (max_values: None, max_size: Some(88), added: 2563, mode: Measured) /// Storage: System EventTopics (r:2 w:2) - /// Proof Skipped: System EventTopics (max_values: None, max_size: None, mode: Ignored) + /// Proof Skipped: System EventTopics (max_values: None, max_size: None, mode: Measured) /// The range of component `i` is `[0, 1048576]`. /// The range of component `s` is `[0, 1048576]`. fn instantiate(i: u32, s: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `533` - // Estimated: `0` - // Minimum execution time: 1_961_131 nanoseconds. - Weight::from_ref_time(208_539_564) - .saturating_add(Weight::from_proof_size(0)) + // Estimated: `19543` + // Minimum execution time: 1_927_365 nanoseconds. + Weight::from_ref_time(189_843_928) + .saturating_add(Weight::from_proof_size(19543)) // Standard Error: 8 - .saturating_add(Weight::from_ref_time(1_688).saturating_mul(i.into())) + .saturating_add(Weight::from_ref_time(1_677).saturating_mul(i.into())) // Standard Error: 8 - .saturating_add(Weight::from_ref_time(1_802).saturating_mul(s.into())) + .saturating_add(Weight::from_ref_time(1_803).saturating_mul(s.into())) .saturating_add(RocksDbWeight::get().reads(8_u64)) - .saturating_add(RocksDbWeight::get().writes(7_u64)) + .saturating_add(RocksDbWeight::get().writes(6_u64)) } /// Storage: Contracts ContractInfoOf (r:1 w:1) - /// Proof: Contracts ContractInfoOf (max_values: None, max_size: Some(258), added: 2733, mode: Ignored) + /// Proof: Contracts ContractInfoOf (max_values: None, max_size: Some(258), added: 2733, mode: Measured) /// Storage: Contracts CodeStorage (r:1 w:0) - /// Proof: Contracts CodeStorage (max_values: None, max_size: Some(126001), added: 128476, mode: Ignored) + /// Proof: Contracts CodeStorage (max_values: None, max_size: Some(126001), added: 128476, mode: Measured) /// Storage: Timestamp Now (r:1 w:0) - /// Proof: Timestamp Now (max_values: Some(1), max_size: Some(8), added: 503, mode: Ignored) + /// Proof: Timestamp Now (max_values: Some(1), max_size: Some(8), added: 503, mode: Measured) /// Storage: System Account (r:1 w:1) - /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: Ignored) + /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: Measured) /// Storage: System EventTopics (r:2 w:2) - /// Proof Skipped: System EventTopics (max_values: None, max_size: None, mode: Ignored) + /// Proof Skipped: System EventTopics (max_values: None, max_size: None, mode: Measured) fn call() -> Weight { // Proof Size summary in bytes: // Measured: `823` - // Estimated: `0` - // Minimum execution time: 148_138 nanoseconds. - Weight::from_ref_time(148_862_000) - .saturating_add(Weight::from_proof_size(0)) + // Estimated: `16985` + // Minimum execution time: 146_354 nanoseconds. + Weight::from_ref_time(147_693_000) + .saturating_add(Weight::from_proof_size(16985)) .saturating_add(RocksDbWeight::get().reads(6_u64)) .saturating_add(RocksDbWeight::get().writes(4_u64)) } - /// Storage: Contracts CodeStorage (r:1 w:1) - /// Proof: Contracts CodeStorage (max_values: None, max_size: Some(126001), added: 128476, mode: Ignored) + /// Storage: Contracts OwnerInfoOf (r:1 w:1) + /// Proof: Contracts OwnerInfoOf (max_values: None, max_size: Some(88), added: 2563, mode: Measured) /// Storage: System EventTopics (r:1 w:1) - /// Proof Skipped: System EventTopics (max_values: None, max_size: None, mode: Ignored) + /// Proof Skipped: System EventTopics (max_values: None, max_size: None, mode: Measured) + /// Storage: Contracts CodeStorage (r:0 w:1) + /// Proof: Contracts CodeStorage (max_values: None, max_size: Some(126001), added: 128476, mode: Measured) /// Storage: Contracts PristineCode (r:0 w:1) - /// Proof: Contracts PristineCode (max_values: None, max_size: Some(125988), added: 128463, mode: Ignored) - /// Storage: Contracts OwnerInfoOf (r:0 w:1) - /// Proof: Contracts OwnerInfoOf (max_values: None, max_size: Some(88), added: 2563, mode: Ignored) + /// Proof: Contracts PristineCode (max_values: None, max_size: Some(125988), added: 128463, mode: Measured) /// The range of component `c` is `[0, 61717]`. fn upload_code(c: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `109` - // Estimated: `0` - // Minimum execution time: 293_444 nanoseconds. - Weight::from_ref_time(293_242_988) - .saturating_add(Weight::from_proof_size(0)) - // Standard Error: 156 - .saturating_add(Weight::from_ref_time(97_476).saturating_mul(c.into())) + // Estimated: `5386` + // Minimum execution time: 388_107 nanoseconds. + Weight::from_ref_time(383_172_702) + .saturating_add(Weight::from_proof_size(5386)) + // Standard Error: 73 + .saturating_add(Weight::from_ref_time(95_037).saturating_mul(c.into())) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().writes(4_u64)) } /// Storage: Contracts OwnerInfoOf (r:1 w:1) - /// Proof: Contracts OwnerInfoOf (max_values: None, max_size: Some(88), added: 2563, mode: Ignored) + /// Proof: Contracts OwnerInfoOf (max_values: None, max_size: Some(88), added: 2563, mode: Measured) /// Storage: System EventTopics (r:1 w:1) - /// Proof Skipped: System EventTopics (max_values: None, max_size: None, mode: Ignored) + /// Proof Skipped: System EventTopics (max_values: None, max_size: None, mode: Measured) /// Storage: Contracts CodeStorage (r:0 w:1) - /// Proof: Contracts CodeStorage (max_values: None, max_size: Some(126001), added: 128476, mode: Ignored) + /// Proof: Contracts CodeStorage (max_values: None, max_size: Some(126001), added: 128476, mode: Measured) /// Storage: Contracts PristineCode (r:0 w:1) - /// Proof: Contracts PristineCode (max_values: None, max_size: Some(125988), added: 128463, mode: Ignored) + /// Proof: Contracts PristineCode (max_values: None, max_size: Some(125988), added: 128463, mode: Measured) fn remove_code() -> Weight { // Proof Size summary in bytes: // Measured: `287` - // Estimated: `0` - // Minimum execution time: 26_802 nanoseconds. - Weight::from_ref_time(27_121_000) - .saturating_add(Weight::from_proof_size(0)) + // Estimated: `6098` + // Minimum execution time: 26_278 nanoseconds. + Weight::from_ref_time(26_682_000) + .saturating_add(Weight::from_proof_size(6098)) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().writes(4_u64)) } /// Storage: Contracts ContractInfoOf (r:1 w:1) - /// Proof: Contracts ContractInfoOf (max_values: None, max_size: Some(258), added: 2733, mode: Ignored) + /// Proof: Contracts ContractInfoOf (max_values: None, max_size: Some(258), added: 2733, mode: Measured) /// Storage: Contracts OwnerInfoOf (r:2 w:2) - /// Proof: Contracts OwnerInfoOf (max_values: None, max_size: Some(88), added: 2563, mode: Ignored) + /// Proof: Contracts OwnerInfoOf (max_values: None, max_size: Some(88), added: 2563, mode: Measured) /// Storage: System EventTopics (r:3 w:3) - /// Proof Skipped: System EventTopics (max_values: None, max_size: None, mode: Ignored) + /// Proof Skipped: System EventTopics (max_values: None, max_size: None, mode: Measured) fn set_code() -> Weight { // Proof Size summary in bytes: // Measured: `634` - // Estimated: `0` - // Minimum execution time: 31_079 nanoseconds. - Weight::from_ref_time(31_541_000) - .saturating_add(Weight::from_proof_size(0)) + // Estimated: `16752` + // Minimum execution time: 30_223 nanoseconds. + Weight::from_ref_time(30_737_000) + .saturating_add(Weight::from_proof_size(16752)) .saturating_add(RocksDbWeight::get().reads(6_u64)) .saturating_add(RocksDbWeight::get().writes(6_u64)) } /// Storage: System Account (r:1 w:0) - /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: Ignored) + /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: Measured) /// Storage: Contracts ContractInfoOf (r:1 w:1) - /// Proof: Contracts ContractInfoOf (max_values: None, max_size: Some(258), added: 2733, mode: Ignored) + /// Proof: Contracts ContractInfoOf (max_values: None, max_size: Some(258), added: 2733, mode: Measured) /// Storage: Contracts CodeStorage (r:1 w:0) - /// Proof: Contracts CodeStorage (max_values: None, max_size: Some(126001), added: 128476, mode: Ignored) + /// Proof: Contracts CodeStorage (max_values: None, max_size: Some(126001), added: 128476, mode: Measured) /// Storage: Timestamp Now (r:1 w:0) - /// Proof: Timestamp Now (max_values: Some(1), max_size: Some(8), added: 503, mode: Ignored) + /// Proof: Timestamp Now (max_values: Some(1), max_size: Some(8), added: 503, mode: Measured) /// Storage: System EventTopics (r:2 w:2) - /// Proof Skipped: System EventTopics (max_values: None, max_size: None, mode: Ignored) + /// Proof Skipped: System EventTopics (max_values: None, max_size: None, mode: Measured) /// The range of component `r` is `[0, 20]`. fn seal_caller(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `845 + r * (480 ±0)` - // Estimated: `0` - // Minimum execution time: 284_877 nanoseconds. - Weight::from_ref_time(290_043_606) - .saturating_add(Weight::from_proof_size(0)) - // Standard Error: 22_094 - .saturating_add(Weight::from_ref_time(18_094_219).saturating_mul(r.into())) + // Estimated: `17120 + r * (2400 ±0)` + // Minimum execution time: 374_780 nanoseconds. + Weight::from_ref_time(379_468_453) + .saturating_add(Weight::from_proof_size(17120)) + // Standard Error: 45_809 + .saturating_add(Weight::from_ref_time(17_553_577).saturating_mul(r.into())) .saturating_add(RocksDbWeight::get().reads(6_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) + .saturating_add(Weight::from_proof_size(2400).saturating_mul(r.into())) } /// Storage: System Account (r:1 w:0) - /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: Ignored) + /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: Measured) /// Storage: Contracts ContractInfoOf (r:1601 w:1) - /// Proof: Contracts ContractInfoOf (max_values: None, max_size: Some(258), added: 2733, mode: Ignored) + /// Proof: Contracts ContractInfoOf (max_values: None, max_size: Some(258), added: 2733, mode: Measured) /// Storage: Contracts CodeStorage (r:1 w:0) - /// Proof: Contracts CodeStorage (max_values: None, max_size: Some(126001), added: 128476, mode: Ignored) + /// Proof: Contracts CodeStorage (max_values: None, max_size: Some(126001), added: 128476, mode: Measured) /// Storage: Timestamp Now (r:1 w:0) - /// Proof: Timestamp Now (max_values: Some(1), max_size: Some(8), added: 503, mode: Ignored) + /// Proof: Timestamp Now (max_values: Some(1), max_size: Some(8), added: 503, mode: Measured) /// Storage: System EventTopics (r:2 w:2) - /// Proof Skipped: System EventTopics (max_values: None, max_size: None, mode: Ignored) + /// Proof Skipped: System EventTopics (max_values: None, max_size: None, mode: Measured) /// The range of component `r` is `[0, 20]`. fn seal_is_contract(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `882 + r * (19218 ±0)` - // Estimated: `0` - // Minimum execution time: 283_956 nanoseconds. - Weight::from_ref_time(129_690_397) - .saturating_add(Weight::from_proof_size(0)) - // Standard Error: 531_270 - .saturating_add(Weight::from_ref_time(264_082_418).saturating_mul(r.into())) + // Estimated: `17110 + r * (294100 ±0)` + // Minimum execution time: 374_399 nanoseconds. + Weight::from_ref_time(228_569_211) + .saturating_add(Weight::from_proof_size(17110)) + // Standard Error: 492_562 + .saturating_add(Weight::from_ref_time(251_682_897).saturating_mul(r.into())) .saturating_add(RocksDbWeight::get().reads(6_u64)) .saturating_add(RocksDbWeight::get().reads((80_u64).saturating_mul(r.into()))) .saturating_add(RocksDbWeight::get().writes(3_u64)) + .saturating_add(Weight::from_proof_size(294100).saturating_mul(r.into())) } /// Storage: System Account (r:1 w:0) - /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: Ignored) + /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: Measured) /// Storage: Contracts ContractInfoOf (r:1601 w:1) - /// Proof: Contracts ContractInfoOf (max_values: None, max_size: Some(258), added: 2733, mode: Ignored) + /// Proof: Contracts ContractInfoOf (max_values: None, max_size: Some(258), added: 2733, mode: Measured) /// Storage: Contracts CodeStorage (r:1 w:0) - /// Proof: Contracts CodeStorage (max_values: None, max_size: Some(126001), added: 128476, mode: Ignored) + /// Proof: Contracts CodeStorage (max_values: None, max_size: Some(126001), added: 128476, mode: Measured) /// Storage: Timestamp Now (r:1 w:0) - /// Proof: Timestamp Now (max_values: Some(1), max_size: Some(8), added: 503, mode: Ignored) + /// Proof: Timestamp Now (max_values: Some(1), max_size: Some(8), added: 503, mode: Measured) /// Storage: System EventTopics (r:2 w:2) - /// Proof Skipped: System EventTopics (max_values: None, max_size: None, mode: Ignored) + /// Proof Skipped: System EventTopics (max_values: None, max_size: None, mode: Measured) /// The range of component `r` is `[0, 20]`. fn seal_code_hash(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `889 + r * (19539 ±0)` - // Estimated: `0` - // Minimum execution time: 287_077 nanoseconds. - Weight::from_ref_time(148_155_166) - .saturating_add(Weight::from_proof_size(0)) - // Standard Error: 466_496 - .saturating_add(Weight::from_ref_time(306_625_930).saturating_mul(r.into())) + // Estimated: `17170 + r * (295700 ±0)` + // Minimum execution time: 375_980 nanoseconds. + Weight::from_ref_time(232_604_331) + .saturating_add(Weight::from_proof_size(17170)) + // Standard Error: 491_912 + .saturating_add(Weight::from_ref_time(299_479_335).saturating_mul(r.into())) .saturating_add(RocksDbWeight::get().reads(6_u64)) .saturating_add(RocksDbWeight::get().reads((80_u64).saturating_mul(r.into()))) .saturating_add(RocksDbWeight::get().writes(3_u64)) + .saturating_add(Weight::from_proof_size(295700).saturating_mul(r.into())) } /// Storage: System Account (r:1 w:0) - /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: Ignored) + /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: Measured) /// Storage: Contracts ContractInfoOf (r:1 w:1) - /// Proof: Contracts ContractInfoOf (max_values: None, max_size: Some(258), added: 2733, mode: Ignored) + /// Proof: Contracts ContractInfoOf (max_values: None, max_size: Some(258), added: 2733, mode: Measured) /// Storage: Contracts CodeStorage (r:1 w:0) - /// Proof: Contracts CodeStorage (max_values: None, max_size: Some(126001), added: 128476, mode: Ignored) + /// Proof: Contracts CodeStorage (max_values: None, max_size: Some(126001), added: 128476, mode: Measured) /// Storage: Timestamp Now (r:1 w:0) - /// Proof: Timestamp Now (max_values: Some(1), max_size: Some(8), added: 503, mode: Ignored) + /// Proof: Timestamp Now (max_values: Some(1), max_size: Some(8), added: 503, mode: Measured) /// Storage: System EventTopics (r:2 w:2) - /// Proof Skipped: System EventTopics (max_values: None, max_size: None, mode: Ignored) + /// Proof Skipped: System EventTopics (max_values: None, max_size: None, mode: Measured) /// The range of component `r` is `[0, 20]`. fn seal_own_code_hash(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `852 + r * (480 ±0)` - // Estimated: `0` - // Minimum execution time: 286_459 nanoseconds. - Weight::from_ref_time(292_826_594) - .saturating_add(Weight::from_proof_size(0)) - // Standard Error: 27_808 - .saturating_add(Weight::from_ref_time(22_401_931).saturating_mul(r.into())) + // Estimated: `17155 + r * (2400 ±0)` + // Minimum execution time: 375_075 nanoseconds. + Weight::from_ref_time(381_217_372) + .saturating_add(Weight::from_proof_size(17155)) + // Standard Error: 63_950 + .saturating_add(Weight::from_ref_time(21_872_316).saturating_mul(r.into())) .saturating_add(RocksDbWeight::get().reads(6_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) + .saturating_add(Weight::from_proof_size(2400).saturating_mul(r.into())) } /// Storage: System Account (r:1 w:0) - /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: Ignored) + /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: Measured) /// Storage: Contracts ContractInfoOf (r:1 w:1) - /// Proof: Contracts ContractInfoOf (max_values: None, max_size: Some(258), added: 2733, mode: Ignored) + /// Proof: Contracts ContractInfoOf (max_values: None, max_size: Some(258), added: 2733, mode: Measured) /// Storage: Contracts CodeStorage (r:1 w:0) - /// Proof: Contracts CodeStorage (max_values: None, max_size: Some(126001), added: 128476, mode: Ignored) + /// Proof: Contracts CodeStorage (max_values: None, max_size: Some(126001), added: 128476, mode: Measured) /// Storage: Timestamp Now (r:1 w:0) - /// Proof: Timestamp Now (max_values: Some(1), max_size: Some(8), added: 503, mode: Ignored) + /// Proof: Timestamp Now (max_values: Some(1), max_size: Some(8), added: 503, mode: Measured) /// Storage: System EventTopics (r:2 w:2) - /// Proof Skipped: System EventTopics (max_values: None, max_size: None, mode: Ignored) + /// Proof Skipped: System EventTopics (max_values: None, max_size: None, mode: Measured) /// The range of component `r` is `[0, 20]`. fn seal_caller_is_origin(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `842 + r * (240 ±0)` - // Estimated: `0` - // Minimum execution time: 284_330 nanoseconds. - Weight::from_ref_time(289_069_701) - .saturating_add(Weight::from_proof_size(0)) - // Standard Error: 13_666 - .saturating_add(Weight::from_ref_time(11_157_859).saturating_mul(r.into())) + // Estimated: `17080 + r * (1200 ±0)` + // Minimum execution time: 373_260 nanoseconds. + Weight::from_ref_time(377_987_666) + .saturating_add(Weight::from_proof_size(17080)) + // Standard Error: 35_603 + .saturating_add(Weight::from_ref_time(11_274_165).saturating_mul(r.into())) .saturating_add(RocksDbWeight::get().reads(6_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) + .saturating_add(Weight::from_proof_size(1200).saturating_mul(r.into())) } /// Storage: System Account (r:1 w:0) - /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: Ignored) + /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: Measured) /// Storage: Contracts ContractInfoOf (r:1 w:1) - /// Proof: Contracts ContractInfoOf (max_values: None, max_size: Some(258), added: 2733, mode: Ignored) + /// Proof: Contracts ContractInfoOf (max_values: None, max_size: Some(258), added: 2733, mode: Measured) /// Storage: Contracts CodeStorage (r:1 w:0) - /// Proof: Contracts CodeStorage (max_values: None, max_size: Some(126001), added: 128476, mode: Ignored) + /// Proof: Contracts CodeStorage (max_values: None, max_size: Some(126001), added: 128476, mode: Measured) /// Storage: Timestamp Now (r:1 w:0) - /// Proof: Timestamp Now (max_values: Some(1), max_size: Some(8), added: 503, mode: Ignored) + /// Proof: Timestamp Now (max_values: Some(1), max_size: Some(8), added: 503, mode: Measured) /// Storage: System EventTopics (r:2 w:2) - /// Proof Skipped: System EventTopics (max_values: None, max_size: None, mode: Ignored) + /// Proof Skipped: System EventTopics (max_values: None, max_size: None, mode: Measured) /// The range of component `r` is `[0, 20]`. fn seal_address(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `846 + r * (480 ±0)` - // Estimated: `0` - // Minimum execution time: 284_506 nanoseconds. - Weight::from_ref_time(290_155_141) - .saturating_add(Weight::from_proof_size(0)) - // Standard Error: 22_944 - .saturating_add(Weight::from_ref_time(17_992_627).saturating_mul(r.into())) + // Estimated: `17100 + r * (2400 ±0)` + // Minimum execution time: 374_605 nanoseconds. + Weight::from_ref_time(379_395_443) + .saturating_add(Weight::from_proof_size(17100)) + // Standard Error: 49_646 + .saturating_add(Weight::from_ref_time(17_487_585).saturating_mul(r.into())) .saturating_add(RocksDbWeight::get().reads(6_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) + .saturating_add(Weight::from_proof_size(2400).saturating_mul(r.into())) } /// Storage: System Account (r:1 w:0) - /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: Ignored) + /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: Measured) /// Storage: Contracts ContractInfoOf (r:1 w:1) - /// Proof: Contracts ContractInfoOf (max_values: None, max_size: Some(258), added: 2733, mode: Ignored) + /// Proof: Contracts ContractInfoOf (max_values: None, max_size: Some(258), added: 2733, mode: Measured) /// Storage: Contracts CodeStorage (r:1 w:0) - /// Proof: Contracts CodeStorage (max_values: None, max_size: Some(126001), added: 128476, mode: Ignored) + /// Proof: Contracts CodeStorage (max_values: None, max_size: Some(126001), added: 128476, mode: Measured) /// Storage: Timestamp Now (r:1 w:0) - /// Proof: Timestamp Now (max_values: Some(1), max_size: Some(8), added: 503, mode: Ignored) + /// Proof: Timestamp Now (max_values: Some(1), max_size: Some(8), added: 503, mode: Measured) /// Storage: System EventTopics (r:2 w:2) - /// Proof Skipped: System EventTopics (max_values: None, max_size: None, mode: Ignored) + /// Proof Skipped: System EventTopics (max_values: None, max_size: None, mode: Measured) /// The range of component `r` is `[0, 20]`. fn seal_gas_left(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `847 + r * (480 ±0)` - // Estimated: `0` - // Minimum execution time: 284_177 nanoseconds. - Weight::from_ref_time(289_314_787) - .saturating_add(Weight::from_proof_size(0)) - // Standard Error: 24_552 - .saturating_add(Weight::from_ref_time(17_754_919).saturating_mul(r.into())) + // Estimated: `17105 + r * (2400 ±0)` + // Minimum execution time: 374_620 nanoseconds. + Weight::from_ref_time(379_623_792) + .saturating_add(Weight::from_proof_size(17105)) + // Standard Error: 49_990 + .saturating_add(Weight::from_ref_time(17_226_706).saturating_mul(r.into())) .saturating_add(RocksDbWeight::get().reads(6_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) + .saturating_add(Weight::from_proof_size(2400).saturating_mul(r.into())) } /// Storage: System Account (r:2 w:0) - /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: Ignored) + /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: Measured) /// Storage: Contracts ContractInfoOf (r:1 w:1) - /// Proof: Contracts ContractInfoOf (max_values: None, max_size: Some(258), added: 2733, mode: Ignored) + /// Proof: Contracts ContractInfoOf (max_values: None, max_size: Some(258), added: 2733, mode: Measured) /// Storage: Contracts CodeStorage (r:1 w:0) - /// Proof: Contracts CodeStorage (max_values: None, max_size: Some(126001), added: 128476, mode: Ignored) + /// Proof: Contracts CodeStorage (max_values: None, max_size: Some(126001), added: 128476, mode: Measured) /// Storage: Timestamp Now (r:1 w:0) - /// Proof: Timestamp Now (max_values: Some(1), max_size: Some(8), added: 503, mode: Ignored) + /// Proof: Timestamp Now (max_values: Some(1), max_size: Some(8), added: 503, mode: Measured) /// Storage: System EventTopics (r:2 w:2) - /// Proof Skipped: System EventTopics (max_values: None, max_size: None, mode: Ignored) + /// Proof Skipped: System EventTopics (max_values: None, max_size: None, mode: Measured) /// The range of component `r` is `[0, 20]`. fn seal_balance(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `1017 + r * (480 ±0)` - // Estimated: `0` - // Minimum execution time: 284_096 nanoseconds. - Weight::from_ref_time(293_655_130) - .saturating_add(Weight::from_proof_size(0)) - // Standard Error: 56_379 - .saturating_add(Weight::from_ref_time(95_688_575).saturating_mul(r.into())) + // Estimated: `19673 + r * (2456 ±4)` + // Minimum execution time: 374_002 nanoseconds. + Weight::from_ref_time(384_615_649) + .saturating_add(Weight::from_proof_size(19673)) + // Standard Error: 85_633 + .saturating_add(Weight::from_ref_time(95_227_118).saturating_mul(r.into())) .saturating_add(RocksDbWeight::get().reads(7_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) + .saturating_add(Weight::from_proof_size(2456).saturating_mul(r.into())) } /// Storage: System Account (r:1 w:0) - /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: Ignored) + /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: Measured) /// Storage: Contracts ContractInfoOf (r:1 w:1) - /// Proof: Contracts ContractInfoOf (max_values: None, max_size: Some(258), added: 2733, mode: Ignored) + /// Proof: Contracts ContractInfoOf (max_values: None, max_size: Some(258), added: 2733, mode: Measured) /// Storage: Contracts CodeStorage (r:1 w:0) - /// Proof: Contracts CodeStorage (max_values: None, max_size: Some(126001), added: 128476, mode: Ignored) + /// Proof: Contracts CodeStorage (max_values: None, max_size: Some(126001), added: 128476, mode: Measured) /// Storage: Timestamp Now (r:1 w:0) - /// Proof: Timestamp Now (max_values: Some(1), max_size: Some(8), added: 503, mode: Ignored) + /// Proof: Timestamp Now (max_values: Some(1), max_size: Some(8), added: 503, mode: Measured) /// Storage: System EventTopics (r:2 w:2) - /// Proof Skipped: System EventTopics (max_values: None, max_size: None, mode: Ignored) + /// Proof Skipped: System EventTopics (max_values: None, max_size: None, mode: Measured) /// The range of component `r` is `[0, 20]`. fn seal_value_transferred(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `856 + r * (480 ±0)` - // Estimated: `0` - // Minimum execution time: 285_967 nanoseconds. - Weight::from_ref_time(288_043_137) - .saturating_add(Weight::from_proof_size(0)) - // Standard Error: 83_838 - .saturating_add(Weight::from_ref_time(18_256_709).saturating_mul(r.into())) + // Estimated: `17200 + r * (2400 ±0)` + // Minimum execution time: 375_307 nanoseconds. + Weight::from_ref_time(378_389_705) + .saturating_add(Weight::from_proof_size(17200)) + // Standard Error: 42_265 + .saturating_add(Weight::from_ref_time(17_316_680).saturating_mul(r.into())) .saturating_add(RocksDbWeight::get().reads(6_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) + .saturating_add(Weight::from_proof_size(2400).saturating_mul(r.into())) } /// Storage: System Account (r:1 w:0) - /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: Ignored) + /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: Measured) /// Storage: Contracts ContractInfoOf (r:1 w:1) - /// Proof: Contracts ContractInfoOf (max_values: None, max_size: Some(258), added: 2733, mode: Ignored) + /// Proof: Contracts ContractInfoOf (max_values: None, max_size: Some(258), added: 2733, mode: Measured) /// Storage: Contracts CodeStorage (r:1 w:0) - /// Proof: Contracts CodeStorage (max_values: None, max_size: Some(126001), added: 128476, mode: Ignored) + /// Proof: Contracts CodeStorage (max_values: None, max_size: Some(126001), added: 128476, mode: Measured) /// Storage: Timestamp Now (r:1 w:0) - /// Proof: Timestamp Now (max_values: Some(1), max_size: Some(8), added: 503, mode: Ignored) + /// Proof: Timestamp Now (max_values: Some(1), max_size: Some(8), added: 503, mode: Measured) /// Storage: System EventTopics (r:2 w:2) - /// Proof Skipped: System EventTopics (max_values: None, max_size: None, mode: Ignored) + /// Proof Skipped: System EventTopics (max_values: None, max_size: None, mode: Measured) /// The range of component `r` is `[0, 20]`. fn seal_minimum_balance(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `854 + r * (480 ±0)` - // Estimated: `0` - // Minimum execution time: 284_060 nanoseconds. - Weight::from_ref_time(290_577_678) - .saturating_add(Weight::from_proof_size(0)) - // Standard Error: 26_131 - .saturating_add(Weight::from_ref_time(17_560_562).saturating_mul(r.into())) + // Estimated: `17140 + r * (2400 ±0)` + // Minimum execution time: 374_878 nanoseconds. + Weight::from_ref_time(379_364_066) + .saturating_add(Weight::from_proof_size(17140)) + // Standard Error: 49_158 + .saturating_add(Weight::from_ref_time(17_111_145).saturating_mul(r.into())) .saturating_add(RocksDbWeight::get().reads(6_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) + .saturating_add(Weight::from_proof_size(2400).saturating_mul(r.into())) } /// Storage: System Account (r:1 w:0) - /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: Ignored) + /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: Measured) /// Storage: Contracts ContractInfoOf (r:1 w:1) - /// Proof: Contracts ContractInfoOf (max_values: None, max_size: Some(258), added: 2733, mode: Ignored) + /// Proof: Contracts ContractInfoOf (max_values: None, max_size: Some(258), added: 2733, mode: Measured) /// Storage: Contracts CodeStorage (r:1 w:0) - /// Proof: Contracts CodeStorage (max_values: None, max_size: Some(126001), added: 128476, mode: Ignored) + /// Proof: Contracts CodeStorage (max_values: None, max_size: Some(126001), added: 128476, mode: Measured) /// Storage: Timestamp Now (r:1 w:0) - /// Proof: Timestamp Now (max_values: Some(1), max_size: Some(8), added: 503, mode: Ignored) + /// Proof: Timestamp Now (max_values: Some(1), max_size: Some(8), added: 503, mode: Measured) /// Storage: System EventTopics (r:2 w:2) - /// Proof Skipped: System EventTopics (max_values: None, max_size: None, mode: Ignored) + /// Proof Skipped: System EventTopics (max_values: None, max_size: None, mode: Measured) /// The range of component `r` is `[0, 20]`. fn seal_block_number(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `851 + r * (480 ±0)` - // Estimated: `0` - // Minimum execution time: 286_068 nanoseconds. - Weight::from_ref_time(290_026_137) - .saturating_add(Weight::from_proof_size(0)) - // Standard Error: 34_787 - .saturating_add(Weight::from_ref_time(17_597_404).saturating_mul(r.into())) + // Estimated: `17125 + r * (2400 ±0)` + // Minimum execution time: 374_981 nanoseconds. + Weight::from_ref_time(381_539_022) + .saturating_add(Weight::from_proof_size(17125)) + // Standard Error: 61_419 + .saturating_add(Weight::from_ref_time(17_062_381).saturating_mul(r.into())) .saturating_add(RocksDbWeight::get().reads(6_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) + .saturating_add(Weight::from_proof_size(2400).saturating_mul(r.into())) } /// Storage: System Account (r:1 w:0) - /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: Ignored) + /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: Measured) /// Storage: Contracts ContractInfoOf (r:1 w:1) - /// Proof: Contracts ContractInfoOf (max_values: None, max_size: Some(258), added: 2733, mode: Ignored) + /// Proof: Contracts ContractInfoOf (max_values: None, max_size: Some(258), added: 2733, mode: Measured) /// Storage: Contracts CodeStorage (r:1 w:0) - /// Proof: Contracts CodeStorage (max_values: None, max_size: Some(126001), added: 128476, mode: Ignored) + /// Proof: Contracts CodeStorage (max_values: None, max_size: Some(126001), added: 128476, mode: Measured) /// Storage: Timestamp Now (r:1 w:0) - /// Proof: Timestamp Now (max_values: Some(1), max_size: Some(8), added: 503, mode: Ignored) + /// Proof: Timestamp Now (max_values: Some(1), max_size: Some(8), added: 503, mode: Measured) /// Storage: System EventTopics (r:2 w:2) - /// Proof Skipped: System EventTopics (max_values: None, max_size: None, mode: Ignored) + /// Proof Skipped: System EventTopics (max_values: None, max_size: None, mode: Measured) /// The range of component `r` is `[0, 20]`. fn seal_now(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `842 + r * (480 ±0)` - // Estimated: `0` - // Minimum execution time: 285_082 nanoseconds. - Weight::from_ref_time(290_487_103) - .saturating_add(Weight::from_proof_size(0)) - // Standard Error: 22_743 - .saturating_add(Weight::from_ref_time(17_506_771).saturating_mul(r.into())) + // Estimated: `17100 + r * (2400 ±0)` + // Minimum execution time: 374_798 nanoseconds. + Weight::from_ref_time(372_659_915) + .saturating_add(Weight::from_proof_size(17100)) + // Standard Error: 151_499 + .saturating_add(Weight::from_ref_time(18_192_683).saturating_mul(r.into())) .saturating_add(RocksDbWeight::get().reads(6_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) + .saturating_add(Weight::from_proof_size(2400).saturating_mul(r.into())) } /// Storage: System Account (r:1 w:0) - /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: Ignored) + /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: Measured) /// Storage: Contracts ContractInfoOf (r:1 w:1) - /// Proof: Contracts ContractInfoOf (max_values: None, max_size: Some(258), added: 2733, mode: Ignored) + /// Proof: Contracts ContractInfoOf (max_values: None, max_size: Some(258), added: 2733, mode: Measured) /// Storage: Contracts CodeStorage (r:1 w:0) - /// Proof: Contracts CodeStorage (max_values: None, max_size: Some(126001), added: 128476, mode: Ignored) + /// Proof: Contracts CodeStorage (max_values: None, max_size: Some(126001), added: 128476, mode: Measured) /// Storage: Timestamp Now (r:1 w:0) - /// Proof: Timestamp Now (max_values: Some(1), max_size: Some(8), added: 503, mode: Ignored) + /// Proof: Timestamp Now (max_values: Some(1), max_size: Some(8), added: 503, mode: Measured) /// Storage: TransactionPayment NextFeeMultiplier (r:1 w:0) - /// Proof: TransactionPayment NextFeeMultiplier (max_values: Some(1), max_size: Some(16), added: 511, mode: Ignored) + /// Proof: TransactionPayment NextFeeMultiplier (max_values: Some(1), max_size: Some(16), added: 511, mode: Measured) /// Storage: System EventTopics (r:2 w:2) - /// Proof Skipped: System EventTopics (max_values: None, max_size: None, mode: Ignored) + /// Proof Skipped: System EventTopics (max_values: None, max_size: None, mode: Measured) /// The range of component `r` is `[0, 20]`. fn seal_weight_to_fee(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `919 + r * (800 ±0)` - // Estimated: `0` - // Minimum execution time: 285_309 nanoseconds. - Weight::from_ref_time(296_735_365) - .saturating_add(Weight::from_proof_size(0)) - // Standard Error: 49_300 - .saturating_add(Weight::from_ref_time(87_678_979).saturating_mul(r.into())) + // Estimated: `18835 + r * (4805 ±0)` + // Minimum execution time: 374_841 nanoseconds. + Weight::from_ref_time(385_475_889) + .saturating_add(Weight::from_proof_size(18835)) + // Standard Error: 106_207 + .saturating_add(Weight::from_ref_time(88_099_987).saturating_mul(r.into())) .saturating_add(RocksDbWeight::get().reads(7_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) + .saturating_add(Weight::from_proof_size(4805).saturating_mul(r.into())) } /// Storage: System Account (r:1 w:0) - /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: Ignored) + /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: Measured) /// Storage: Contracts ContractInfoOf (r:1 w:1) - /// Proof: Contracts ContractInfoOf (max_values: None, max_size: Some(258), added: 2733, mode: Ignored) + /// Proof: Contracts ContractInfoOf (max_values: None, max_size: Some(258), added: 2733, mode: Measured) /// Storage: Contracts CodeStorage (r:1 w:0) - /// Proof: Contracts CodeStorage (max_values: None, max_size: Some(126001), added: 128476, mode: Ignored) + /// Proof: Contracts CodeStorage (max_values: None, max_size: Some(126001), added: 128476, mode: Measured) /// Storage: Timestamp Now (r:1 w:0) - /// Proof: Timestamp Now (max_values: Some(1), max_size: Some(8), added: 503, mode: Ignored) + /// Proof: Timestamp Now (max_values: Some(1), max_size: Some(8), added: 503, mode: Measured) /// Storage: System EventTopics (r:2 w:2) - /// Proof Skipped: System EventTopics (max_values: None, max_size: None, mode: Ignored) + /// Proof Skipped: System EventTopics (max_values: None, max_size: None, mode: Measured) /// The range of component `r` is `[0, 20]`. fn seal_gas(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `809 + r * (320 ±0)` - // Estimated: `0` - // Minimum execution time: 136_110 nanoseconds. - Weight::from_ref_time(139_800_659) - .saturating_add(Weight::from_proof_size(0)) - // Standard Error: 9_674 - .saturating_add(Weight::from_ref_time(8_302_834).saturating_mul(r.into())) + // Estimated: `16955 + r * (1600 ±0)` + // Minimum execution time: 134_436 nanoseconds. + Weight::from_ref_time(137_789_498) + .saturating_add(Weight::from_proof_size(16955)) + // Standard Error: 10_622 + .saturating_add(Weight::from_ref_time(8_144_024).saturating_mul(r.into())) .saturating_add(RocksDbWeight::get().reads(6_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) + .saturating_add(Weight::from_proof_size(1600).saturating_mul(r.into())) } /// Storage: System Account (r:1 w:0) - /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: Ignored) + /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: Measured) /// Storage: Contracts ContractInfoOf (r:1 w:1) - /// Proof: Contracts ContractInfoOf (max_values: None, max_size: Some(258), added: 2733, mode: Ignored) + /// Proof: Contracts ContractInfoOf (max_values: None, max_size: Some(258), added: 2733, mode: Measured) /// Storage: Contracts CodeStorage (r:1 w:0) - /// Proof: Contracts CodeStorage (max_values: None, max_size: Some(126001), added: 128476, mode: Ignored) + /// Proof: Contracts CodeStorage (max_values: None, max_size: Some(126001), added: 128476, mode: Measured) /// Storage: Timestamp Now (r:1 w:0) - /// Proof: Timestamp Now (max_values: Some(1), max_size: Some(8), added: 503, mode: Ignored) + /// Proof: Timestamp Now (max_values: Some(1), max_size: Some(8), added: 503, mode: Measured) /// Storage: System EventTopics (r:2 w:2) - /// Proof Skipped: System EventTopics (max_values: None, max_size: None, mode: Ignored) + /// Proof Skipped: System EventTopics (max_values: None, max_size: None, mode: Measured) /// The range of component `r` is `[0, 20]`. fn seal_input(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `844 + r * (480 ±0)` - // Estimated: `0` - // Minimum execution time: 285_238 nanoseconds. - Weight::from_ref_time(289_723_839) - .saturating_add(Weight::from_proof_size(0)) - // Standard Error: 19_531 - .saturating_add(Weight::from_ref_time(15_590_085).saturating_mul(r.into())) + // Estimated: `17085 + r * (2400 ±0)` + // Minimum execution time: 374_480 nanoseconds. + Weight::from_ref_time(379_723_392) + .saturating_add(Weight::from_proof_size(17085)) + // Standard Error: 50_240 + .saturating_add(Weight::from_ref_time(15_358_041).saturating_mul(r.into())) .saturating_add(RocksDbWeight::get().reads(6_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) + .saturating_add(Weight::from_proof_size(2400).saturating_mul(r.into())) } /// Storage: System Account (r:1 w:0) - /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: Ignored) + /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: Measured) /// Storage: Contracts ContractInfoOf (r:1 w:1) - /// Proof: Contracts ContractInfoOf (max_values: None, max_size: Some(258), added: 2733, mode: Ignored) + /// Proof: Contracts ContractInfoOf (max_values: None, max_size: Some(258), added: 2733, mode: Measured) /// Storage: Contracts CodeStorage (r:1 w:0) - /// Proof: Contracts CodeStorage (max_values: None, max_size: Some(126001), added: 128476, mode: Ignored) + /// Proof: Contracts CodeStorage (max_values: None, max_size: Some(126001), added: 128476, mode: Measured) /// Storage: Timestamp Now (r:1 w:0) - /// Proof: Timestamp Now (max_values: Some(1), max_size: Some(8), added: 503, mode: Ignored) + /// Proof: Timestamp Now (max_values: Some(1), max_size: Some(8), added: 503, mode: Measured) /// Storage: System EventTopics (r:2 w:2) - /// Proof Skipped: System EventTopics (max_values: None, max_size: None, mode: Ignored) + /// Proof Skipped: System EventTopics (max_values: None, max_size: None, mode: Measured) /// The range of component `n` is `[0, 1024]`. fn seal_input_per_kb(n: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `1324` - // Estimated: `0` - // Minimum execution time: 303_189 nanoseconds. - Weight::from_ref_time(323_374_503) - .saturating_add(Weight::from_proof_size(0)) - // Standard Error: 2_060 - .saturating_add(Weight::from_ref_time(9_799_357).saturating_mul(n.into())) + // Estimated: `19490` + // Minimum execution time: 392_282 nanoseconds. + Weight::from_ref_time(418_943_323) + .saturating_add(Weight::from_proof_size(19490)) + // Standard Error: 4_673 + .saturating_add(Weight::from_ref_time(9_664_301).saturating_mul(n.into())) .saturating_add(RocksDbWeight::get().reads(6_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) } /// Storage: System Account (r:1 w:0) - /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: Ignored) + /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: Measured) /// Storage: Contracts ContractInfoOf (r:1 w:1) - /// Proof: Contracts ContractInfoOf (max_values: None, max_size: Some(258), added: 2733, mode: Ignored) + /// Proof: Contracts ContractInfoOf (max_values: None, max_size: Some(258), added: 2733, mode: Measured) /// Storage: Contracts CodeStorage (r:1 w:0) - /// Proof: Contracts CodeStorage (max_values: None, max_size: Some(126001), added: 128476, mode: Ignored) + /// Proof: Contracts CodeStorage (max_values: None, max_size: Some(126001), added: 128476, mode: Measured) /// Storage: Timestamp Now (r:1 w:0) - /// Proof: Timestamp Now (max_values: Some(1), max_size: Some(8), added: 503, mode: Ignored) + /// Proof: Timestamp Now (max_values: Some(1), max_size: Some(8), added: 503, mode: Measured) /// Storage: System EventTopics (r:2 w:2) - /// Proof Skipped: System EventTopics (max_values: None, max_size: None, mode: Ignored) + /// Proof Skipped: System EventTopics (max_values: None, max_size: None, mode: Measured) /// The range of component `r` is `[0, 1]`. fn seal_return(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `832 + r * (45 ±0)` - // Estimated: `0` - // Minimum execution time: 280_736 nanoseconds. - Weight::from_ref_time(285_027_920) - .saturating_add(Weight::from_proof_size(0)) - // Standard Error: 222_526 - .saturating_add(Weight::from_ref_time(1_814_579).saturating_mul(r.into())) + // Estimated: `17030 + r * (225 ±0)` + // Minimum execution time: 372_207 nanoseconds. + Weight::from_ref_time(376_232_444) + .saturating_add(Weight::from_proof_size(17030)) .saturating_add(RocksDbWeight::get().reads(6_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) + .saturating_add(Weight::from_proof_size(225).saturating_mul(r.into())) } /// Storage: System Account (r:1 w:0) - /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: Ignored) + /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: Measured) /// Storage: Contracts ContractInfoOf (r:1 w:1) - /// Proof: Contracts ContractInfoOf (max_values: None, max_size: Some(258), added: 2733, mode: Ignored) + /// Proof: Contracts ContractInfoOf (max_values: None, max_size: Some(258), added: 2733, mode: Measured) /// Storage: Contracts CodeStorage (r:1 w:0) - /// Proof: Contracts CodeStorage (max_values: None, max_size: Some(126001), added: 128476, mode: Ignored) + /// Proof: Contracts CodeStorage (max_values: None, max_size: Some(126001), added: 128476, mode: Measured) /// Storage: Timestamp Now (r:1 w:0) - /// Proof: Timestamp Now (max_values: Some(1), max_size: Some(8), added: 503, mode: Ignored) + /// Proof: Timestamp Now (max_values: Some(1), max_size: Some(8), added: 503, mode: Measured) /// Storage: System EventTopics (r:2 w:2) - /// Proof Skipped: System EventTopics (max_values: None, max_size: None, mode: Ignored) + /// Proof Skipped: System EventTopics (max_values: None, max_size: None, mode: Measured) /// The range of component `n` is `[0, 1024]`. fn seal_return_per_kb(n: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `842` - // Estimated: `0` - // Minimum execution time: 285_559 nanoseconds. - Weight::from_ref_time(290_522_234) - .saturating_add(Weight::from_proof_size(0)) - // Standard Error: 2_578 - .saturating_add(Weight::from_ref_time(195_134).saturating_mul(n.into())) + // Estimated: `17125` + // Minimum execution time: 374_743 nanoseconds. + Weight::from_ref_time(377_365_053) + .saturating_add(Weight::from_proof_size(17125)) + // Standard Error: 1_073 + .saturating_add(Weight::from_ref_time(231_251).saturating_mul(n.into())) .saturating_add(RocksDbWeight::get().reads(6_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) } /// Storage: System Account (r:3 w:3) - /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: Ignored) + /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: Measured) /// Storage: Contracts ContractInfoOf (r:1 w:1) - /// Proof: Contracts ContractInfoOf (max_values: None, max_size: Some(258), added: 2733, mode: Ignored) + /// Proof: Contracts ContractInfoOf (max_values: None, max_size: Some(258), added: 2733, mode: Measured) /// Storage: Contracts CodeStorage (r:1 w:0) - /// Proof: Contracts CodeStorage (max_values: None, max_size: Some(126001), added: 128476, mode: Ignored) + /// Proof: Contracts CodeStorage (max_values: None, max_size: Some(126001), added: 128476, mode: Measured) /// Storage: Timestamp Now (r:1 w:0) - /// Proof: Timestamp Now (max_values: Some(1), max_size: Some(8), added: 503, mode: Ignored) + /// Proof: Timestamp Now (max_values: Some(1), max_size: Some(8), added: 503, mode: Measured) /// Storage: Contracts DeletionQueue (r:1 w:1) - /// Proof: Contracts DeletionQueue (max_values: Some(1), max_size: Some(16642), added: 17137, mode: Ignored) + /// Proof: Contracts DeletionQueue (max_values: Some(1), max_size: Some(16642), added: 17137, mode: Measured) /// Storage: Contracts OwnerInfoOf (r:1 w:1) - /// Proof: Contracts OwnerInfoOf (max_values: None, max_size: Some(88), added: 2563, mode: Ignored) + /// Proof: Contracts OwnerInfoOf (max_values: None, max_size: Some(88), added: 2563, mode: Measured) /// Storage: System EventTopics (r:3 w:3) - /// Proof Skipped: System EventTopics (max_values: None, max_size: None, mode: Ignored) + /// Proof Skipped: System EventTopics (max_values: None, max_size: None, mode: Measured) /// The range of component `r` is `[0, 1]`. fn seal_terminate(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `874 + r * (280 ±0)` - // Estimated: `0` - // Minimum execution time: 282_070 nanoseconds. - Weight::from_ref_time(286_466_489) - .saturating_add(Weight::from_proof_size(0)) - // Standard Error: 238_285 - .saturating_add(Weight::from_ref_time(58_542_610).saturating_mul(r.into())) + // Estimated: `19880 + r * (11465 ±0)` + // Minimum execution time: 374_873 nanoseconds. + Weight::from_ref_time(378_422_289) + .saturating_add(Weight::from_proof_size(19880)) + // Standard Error: 979_734 + .saturating_add(Weight::from_ref_time(57_035_310).saturating_mul(r.into())) .saturating_add(RocksDbWeight::get().reads(6_u64)) .saturating_add(RocksDbWeight::get().reads((5_u64).saturating_mul(r.into()))) .saturating_add(RocksDbWeight::get().writes(3_u64)) .saturating_add(RocksDbWeight::get().writes((6_u64).saturating_mul(r.into()))) + .saturating_add(Weight::from_proof_size(11465).saturating_mul(r.into())) } /// Storage: System Account (r:1 w:0) - /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: Ignored) + /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: Measured) /// Storage: Contracts ContractInfoOf (r:1 w:1) - /// Proof: Contracts ContractInfoOf (max_values: None, max_size: Some(258), added: 2733, mode: Ignored) + /// Proof: Contracts ContractInfoOf (max_values: None, max_size: Some(258), added: 2733, mode: Measured) /// Storage: Contracts CodeStorage (r:1 w:0) - /// Proof: Contracts CodeStorage (max_values: None, max_size: Some(126001), added: 128476, mode: Ignored) + /// Proof: Contracts CodeStorage (max_values: None, max_size: Some(126001), added: 128476, mode: Measured) /// Storage: Timestamp Now (r:1 w:0) - /// Proof: Timestamp Now (max_values: Some(1), max_size: Some(8), added: 503, mode: Ignored) + /// Proof: Timestamp Now (max_values: Some(1), max_size: Some(8), added: 503, mode: Measured) /// Storage: RandomnessCollectiveFlip RandomMaterial (r:1 w:0) - /// Proof: RandomnessCollectiveFlip RandomMaterial (max_values: Some(1), max_size: Some(2594), added: 3089, mode: Ignored) + /// Proof: RandomnessCollectiveFlip RandomMaterial (max_values: Some(1), max_size: Some(2594), added: 3089, mode: Measured) /// Storage: System EventTopics (r:2 w:2) - /// Proof Skipped: System EventTopics (max_values: None, max_size: None, mode: Ignored) + /// Proof Skipped: System EventTopics (max_values: None, max_size: None, mode: Measured) /// The range of component `r` is `[0, 20]`. fn seal_random(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `889 + r * (800 ±0)` - // Estimated: `0` - // Minimum execution time: 284_147 nanoseconds. - Weight::from_ref_time(295_373_881) - .saturating_add(Weight::from_proof_size(0)) - // Standard Error: 64_682 - .saturating_add(Weight::from_ref_time(114_198_680).saturating_mul(r.into())) + // Estimated: `18643 + r * (4805 ±0)` + // Minimum execution time: 375_001 nanoseconds. + Weight::from_ref_time(382_558_599) + .saturating_add(Weight::from_proof_size(18643)) + // Standard Error: 94_918 + .saturating_add(Weight::from_ref_time(112_973_277).saturating_mul(r.into())) .saturating_add(RocksDbWeight::get().reads(7_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) + .saturating_add(Weight::from_proof_size(4805).saturating_mul(r.into())) } /// Storage: System Account (r:1 w:0) - /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: Ignored) + /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: Measured) /// Storage: Contracts ContractInfoOf (r:1 w:1) - /// Proof: Contracts ContractInfoOf (max_values: None, max_size: Some(258), added: 2733, mode: Ignored) + /// Proof: Contracts ContractInfoOf (max_values: None, max_size: Some(258), added: 2733, mode: Measured) /// Storage: Contracts CodeStorage (r:1 w:0) - /// Proof: Contracts CodeStorage (max_values: None, max_size: Some(126001), added: 128476, mode: Ignored) + /// Proof: Contracts CodeStorage (max_values: None, max_size: Some(126001), added: 128476, mode: Measured) /// Storage: Timestamp Now (r:1 w:0) - /// Proof: Timestamp Now (max_values: Some(1), max_size: Some(8), added: 503, mode: Ignored) + /// Proof: Timestamp Now (max_values: Some(1), max_size: Some(8), added: 503, mode: Measured) /// Storage: System EventTopics (r:2 w:2) - /// Proof Skipped: System EventTopics (max_values: None, max_size: None, mode: Ignored) + /// Proof Skipped: System EventTopics (max_values: None, max_size: None, mode: Measured) /// The range of component `r` is `[0, 20]`. fn seal_deposit_event(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `842 + r * (800 ±0)` - // Estimated: `0` - // Minimum execution time: 282_681 nanoseconds. - Weight::from_ref_time(290_974_649) - .saturating_add(Weight::from_proof_size(0)) - // Standard Error: 77_850 - .saturating_add(Weight::from_ref_time(233_273_622).saturating_mul(r.into())) + // Estimated: `17075 + r * (4000 ±0)` + // Minimum execution time: 372_547 nanoseconds. + Weight::from_ref_time(383_278_916) + .saturating_add(Weight::from_proof_size(17075)) + // Standard Error: 128_339 + .saturating_add(Weight::from_ref_time(229_356_088).saturating_mul(r.into())) .saturating_add(RocksDbWeight::get().reads(6_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) + .saturating_add(Weight::from_proof_size(4000).saturating_mul(r.into())) } /// Storage: System Account (r:1 w:0) - /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: Ignored) + /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: Measured) /// Storage: Contracts ContractInfoOf (r:1 w:1) - /// Proof: Contracts ContractInfoOf (max_values: None, max_size: Some(258), added: 2733, mode: Ignored) + /// Proof: Contracts ContractInfoOf (max_values: None, max_size: Some(258), added: 2733, mode: Measured) /// Storage: Contracts CodeStorage (r:1 w:0) - /// Proof: Contracts CodeStorage (max_values: None, max_size: Some(126001), added: 128476, mode: Ignored) + /// Proof: Contracts CodeStorage (max_values: None, max_size: Some(126001), added: 128476, mode: Measured) /// Storage: Timestamp Now (r:1 w:0) - /// Proof: Timestamp Now (max_values: Some(1), max_size: Some(8), added: 503, mode: Ignored) + /// Proof: Timestamp Now (max_values: Some(1), max_size: Some(8), added: 503, mode: Measured) /// Storage: System EventTopics (r:322 w:322) - /// Proof Skipped: System EventTopics (max_values: None, max_size: None, mode: Ignored) + /// Proof Skipped: System EventTopics (max_values: None, max_size: None, mode: Measured) /// The range of component `t` is `[0, 4]`. /// The range of component `n` is `[0, 16]`. fn seal_deposit_event_per_topic_and_kb(t: u32, n: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `1778 + t * (2608 ±0) + n * (8 ±0)` - // Estimated: `0` - // Minimum execution time: 1_226_868 nanoseconds. - Weight::from_ref_time(517_031_747) - .saturating_add(Weight::from_proof_size(0)) - // Standard Error: 342_786 - .saturating_add(Weight::from_ref_time(183_829_556).saturating_mul(t.into())) - // Standard Error: 94_145 - .saturating_add(Weight::from_ref_time(70_675_099).saturating_mul(n.into())) + // Estimated: `21675 + t * (211030 ±0) + n * (50 ±0)` + // Minimum execution time: 1_290_432 nanoseconds. + Weight::from_ref_time(595_859_216) + .saturating_add(Weight::from_proof_size(21675)) + // Standard Error: 602_943 + .saturating_add(Weight::from_ref_time(178_128_149).saturating_mul(t.into())) + // Standard Error: 165_597 + .saturating_add(Weight::from_ref_time(71_475_468).saturating_mul(n.into())) .saturating_add(RocksDbWeight::get().reads(6_u64)) .saturating_add(RocksDbWeight::get().reads((80_u64).saturating_mul(t.into()))) .saturating_add(RocksDbWeight::get().writes(3_u64)) .saturating_add(RocksDbWeight::get().writes((80_u64).saturating_mul(t.into()))) + .saturating_add(Weight::from_proof_size(211030).saturating_mul(t.into())) + .saturating_add(Weight::from_proof_size(50).saturating_mul(n.into())) } /// Storage: System Account (r:1 w:0) - /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: Ignored) + /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: Measured) /// Storage: Contracts ContractInfoOf (r:1 w:1) - /// Proof: Contracts ContractInfoOf (max_values: None, max_size: Some(258), added: 2733, mode: Ignored) + /// Proof: Contracts ContractInfoOf (max_values: None, max_size: Some(258), added: 2733, mode: Measured) /// Storage: Contracts CodeStorage (r:1 w:0) - /// Proof: Contracts CodeStorage (max_values: None, max_size: Some(126001), added: 128476, mode: Ignored) + /// Proof: Contracts CodeStorage (max_values: None, max_size: Some(126001), added: 128476, mode: Measured) /// Storage: Timestamp Now (r:1 w:0) - /// Proof: Timestamp Now (max_values: Some(1), max_size: Some(8), added: 503, mode: Ignored) + /// Proof: Timestamp Now (max_values: Some(1), max_size: Some(8), added: 503, mode: Measured) /// Storage: System EventTopics (r:2 w:2) - /// Proof Skipped: System EventTopics (max_values: None, max_size: None, mode: Ignored) + /// Proof Skipped: System EventTopics (max_values: None, max_size: None, mode: Measured) /// The range of component `r` is `[0, 20]`. fn seal_debug_message(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `841 + r * (560 ±0)` - // Estimated: `0` - // Minimum execution time: 143_026 nanoseconds. - Weight::from_ref_time(147_158_038) - .saturating_add(Weight::from_proof_size(0)) - // Standard Error: 16_662 - .saturating_add(Weight::from_ref_time(14_990_989).saturating_mul(r.into())) + // Estimated: `17065 + r * (2800 ±0)` + // Minimum execution time: 149_020 nanoseconds. + Weight::from_ref_time(152_893_012) + .saturating_add(Weight::from_proof_size(17065)) + // Standard Error: 31_804 + .saturating_add(Weight::from_ref_time(14_497_512).saturating_mul(r.into())) .saturating_add(RocksDbWeight::get().reads(6_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) + .saturating_add(Weight::from_proof_size(2800).saturating_mul(r.into())) } /// Storage: System Account (r:1 w:0) /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) @@ -2998,1260 +3075,1289 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `125792` // Estimated: `265059` - // Minimum execution time: 412_112 nanoseconds. - Weight::from_ref_time(415_624_011) + // Minimum execution time: 502_589 nanoseconds. + Weight::from_ref_time(506_695_307) .saturating_add(Weight::from_proof_size(265059)) - // Standard Error: 1_190 - .saturating_add(Weight::from_ref_time(797_964).saturating_mul(i.into())) + // Standard Error: 2_148 + .saturating_add(Weight::from_ref_time(814_647).saturating_mul(i.into())) .saturating_add(RocksDbWeight::get().reads(6_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) } /// Storage: Skipped Metadata (r:0 w:0) - /// Proof Skipped: Skipped Metadata (max_values: None, max_size: None, mode: Ignored) + /// Proof Skipped: Skipped Metadata (max_values: None, max_size: None, mode: Measured) /// The range of component `r` is `[0, 10]`. fn seal_set_storage(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `883 + r * (23417 ±0)` - // Estimated: `0` - // Minimum execution time: 286_118 nanoseconds. - Weight::from_ref_time(195_812_951) - .saturating_add(Weight::from_proof_size(0)) - // Standard Error: 858_501 - .saturating_add(Weight::from_ref_time(485_357_331).saturating_mul(r.into())) + // Estimated: `883 + r * (23417 ±0)` + // Minimum execution time: 375_473 nanoseconds. + Weight::from_ref_time(291_125_810) + .saturating_add(Weight::from_proof_size(883)) + // Standard Error: 824_971 + .saturating_add(Weight::from_ref_time(477_999_695).saturating_mul(r.into())) .saturating_add(RocksDbWeight::get().reads(6_u64)) .saturating_add(RocksDbWeight::get().reads((80_u64).saturating_mul(r.into()))) .saturating_add(RocksDbWeight::get().writes(3_u64)) .saturating_add(RocksDbWeight::get().writes((80_u64).saturating_mul(r.into()))) + .saturating_add(Weight::from_proof_size(23417).saturating_mul(r.into())) } /// Storage: Skipped Metadata (r:0 w:0) - /// Proof Skipped: Skipped Metadata (max_values: None, max_size: None, mode: Ignored) + /// Proof Skipped: Skipped Metadata (max_values: None, max_size: None, mode: Measured) /// The range of component `n` is `[0, 8]`. fn seal_set_storage_per_new_kb(n: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `12583 + n * (11969 ±0)` - // Estimated: `0` - // Minimum execution time: 433_181 nanoseconds. - Weight::from_ref_time(618_575_096) - .saturating_add(Weight::from_proof_size(0)) - // Standard Error: 1_696_126 - .saturating_add(Weight::from_ref_time(96_213_633).saturating_mul(n.into())) + // Estimated: `8500 + n * (12813 ±61)` + // Minimum execution time: 515_962 nanoseconds. + Weight::from_ref_time(697_904_030) + .saturating_add(Weight::from_proof_size(8500)) + // Standard Error: 1_684_000 + .saturating_add(Weight::from_ref_time(98_411_710).saturating_mul(n.into())) .saturating_add(RocksDbWeight::get().reads(52_u64)) .saturating_add(RocksDbWeight::get().reads((7_u64).saturating_mul(n.into()))) .saturating_add(RocksDbWeight::get().writes(50_u64)) .saturating_add(RocksDbWeight::get().writes((7_u64).saturating_mul(n.into()))) + .saturating_add(Weight::from_proof_size(12813).saturating_mul(n.into())) } /// Storage: Skipped Metadata (r:0 w:0) - /// Proof Skipped: Skipped Metadata (max_values: None, max_size: None, mode: Ignored) + /// Proof Skipped: Skipped Metadata (max_values: None, max_size: None, mode: Measured) /// The range of component `n` is `[0, 8]`. fn seal_set_storage_per_old_kb(n: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `15138 + n * (175775 ±0)` - // Estimated: `0` - // Minimum execution time: 433_354 nanoseconds. - Weight::from_ref_time(597_131_349) - .saturating_add(Weight::from_proof_size(0)) - // Standard Error: 1_509_003 - .saturating_add(Weight::from_ref_time(64_872_907).saturating_mul(n.into())) + // Estimated: `9898 + n * (176855 ±74)` + // Minimum execution time: 515_828 nanoseconds. + Weight::from_ref_time(661_240_495) + .saturating_add(Weight::from_proof_size(9898)) + // Standard Error: 1_338_661 + .saturating_add(Weight::from_ref_time(65_767_819).saturating_mul(n.into())) .saturating_add(RocksDbWeight::get().reads(51_u64)) .saturating_add(RocksDbWeight::get().reads((7_u64).saturating_mul(n.into()))) .saturating_add(RocksDbWeight::get().writes(49_u64)) .saturating_add(RocksDbWeight::get().writes((7_u64).saturating_mul(n.into()))) + .saturating_add(Weight::from_proof_size(176855).saturating_mul(n.into())) } /// Storage: Skipped Metadata (r:0 w:0) - /// Proof Skipped: Skipped Metadata (max_values: None, max_size: None, mode: Ignored) + /// Proof Skipped: Skipped Metadata (max_values: None, max_size: None, mode: Measured) /// The range of component `r` is `[0, 10]`. fn seal_clear_storage(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `876 + r * (23098 ±0)` - // Estimated: `0` - // Minimum execution time: 286_788 nanoseconds. - Weight::from_ref_time(207_787_332) - .saturating_add(Weight::from_proof_size(0)) - // Standard Error: 778_284 - .saturating_add(Weight::from_ref_time(474_879_317).saturating_mul(r.into())) + // Estimated: `881 + r * (23097 ±0)` + // Minimum execution time: 375_528 nanoseconds. + Weight::from_ref_time(296_453_612) + .saturating_add(Weight::from_proof_size(881)) + // Standard Error: 809_232 + .saturating_add(Weight::from_ref_time(465_365_815).saturating_mul(r.into())) .saturating_add(RocksDbWeight::get().reads(6_u64)) .saturating_add(RocksDbWeight::get().reads((80_u64).saturating_mul(r.into()))) .saturating_add(RocksDbWeight::get().writes(3_u64)) .saturating_add(RocksDbWeight::get().writes((80_u64).saturating_mul(r.into()))) + .saturating_add(Weight::from_proof_size(23097).saturating_mul(r.into())) } /// Storage: Skipped Metadata (r:0 w:0) - /// Proof Skipped: Skipped Metadata (max_values: None, max_size: None, mode: Ignored) + /// Proof Skipped: Skipped Metadata (max_values: None, max_size: None, mode: Measured) /// The range of component `n` is `[0, 8]`. fn seal_clear_storage_per_kb(n: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `14863 + n * (175768 ±0)` - // Estimated: `0` - // Minimum execution time: 397_414 nanoseconds. - Weight::from_ref_time(573_575_029) - .saturating_add(Weight::from_proof_size(0)) - // Standard Error: 1_630_691 - .saturating_add(Weight::from_ref_time(67_888_226).saturating_mul(n.into())) + // Estimated: `9519 + n * (176867 ±75)` + // Minimum execution time: 480_427 nanoseconds. + Weight::from_ref_time(640_337_570) + .saturating_add(Weight::from_proof_size(9519)) + // Standard Error: 1_497_141 + .saturating_add(Weight::from_ref_time(67_963_696).saturating_mul(n.into())) .saturating_add(RocksDbWeight::get().reads(51_u64)) .saturating_add(RocksDbWeight::get().reads((7_u64).saturating_mul(n.into()))) .saturating_add(RocksDbWeight::get().writes(48_u64)) .saturating_add(RocksDbWeight::get().writes((7_u64).saturating_mul(n.into()))) + .saturating_add(Weight::from_proof_size(176867).saturating_mul(n.into())) } /// Storage: Skipped Metadata (r:0 w:0) - /// Proof Skipped: Skipped Metadata (max_values: None, max_size: None, mode: Ignored) + /// Proof Skipped: Skipped Metadata (max_values: None, max_size: None, mode: Measured) /// The range of component `r` is `[0, 10]`. fn seal_get_storage(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `878 + r * (23740 ±0)` - // Estimated: `0` - // Minimum execution time: 286_098 nanoseconds. - Weight::from_ref_time(223_637_903) - .saturating_add(Weight::from_proof_size(0)) - // Standard Error: 669_294 - .saturating_add(Weight::from_ref_time(387_577_627).saturating_mul(r.into())) + // Estimated: `880 + r * (23739 ±0)` + // Minimum execution time: 375_725 nanoseconds. + Weight::from_ref_time(307_839_394) + .saturating_add(Weight::from_proof_size(880)) + // Standard Error: 710_694 + .saturating_add(Weight::from_ref_time(381_738_407).saturating_mul(r.into())) .saturating_add(RocksDbWeight::get().reads(6_u64)) .saturating_add(RocksDbWeight::get().reads((80_u64).saturating_mul(r.into()))) .saturating_add(RocksDbWeight::get().writes(3_u64)) + .saturating_add(Weight::from_proof_size(23739).saturating_mul(r.into())) } /// Storage: Skipped Metadata (r:0 w:0) - /// Proof Skipped: Skipped Metadata (max_values: None, max_size: None, mode: Ignored) + /// Proof Skipped: Skipped Metadata (max_values: None, max_size: None, mode: Measured) /// The range of component `n` is `[0, 8]`. fn seal_get_storage_per_kb(n: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `15469 + n * (175775 ±0)` - // Estimated: `0` - // Minimum execution time: 373_335 nanoseconds. - Weight::from_ref_time(531_442_564) - .saturating_add(Weight::from_proof_size(0)) - // Standard Error: 1_474_336 - .saturating_add(Weight::from_ref_time(154_469_598).saturating_mul(n.into())) + // Estimated: `10010 + n * (176900 ±76)` + // Minimum execution time: 461_871 nanoseconds. + Weight::from_ref_time(605_755_493) + .saturating_add(Weight::from_proof_size(10010)) + // Standard Error: 1_375_044 + .saturating_add(Weight::from_ref_time(161_332_330).saturating_mul(n.into())) .saturating_add(RocksDbWeight::get().reads(51_u64)) .saturating_add(RocksDbWeight::get().reads((7_u64).saturating_mul(n.into()))) .saturating_add(RocksDbWeight::get().writes(3_u64)) + .saturating_add(Weight::from_proof_size(176900).saturating_mul(n.into())) } /// Storage: Skipped Metadata (r:0 w:0) - /// Proof Skipped: Skipped Metadata (max_values: None, max_size: None, mode: Ignored) + /// Proof Skipped: Skipped Metadata (max_values: None, max_size: None, mode: Measured) /// The range of component `r` is `[0, 10]`. fn seal_contains_storage(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `871 + r * (23100 ±0)` - // Estimated: `0` - // Minimum execution time: 284_183 nanoseconds. - Weight::from_ref_time(223_325_689) - .saturating_add(Weight::from_proof_size(0)) - // Standard Error: 644_587 - .saturating_add(Weight::from_ref_time(373_628_582).saturating_mul(r.into())) + // Estimated: `873 + r * (23099 ±0)` + // Minimum execution time: 375_325 nanoseconds. + Weight::from_ref_time(305_508_307) + .saturating_add(Weight::from_proof_size(873)) + // Standard Error: 715_627 + .saturating_add(Weight::from_ref_time(369_985_438).saturating_mul(r.into())) .saturating_add(RocksDbWeight::get().reads(6_u64)) .saturating_add(RocksDbWeight::get().reads((80_u64).saturating_mul(r.into()))) .saturating_add(RocksDbWeight::get().writes(3_u64)) + .saturating_add(Weight::from_proof_size(23099).saturating_mul(r.into())) } /// Storage: Skipped Metadata (r:0 w:0) - /// Proof Skipped: Skipped Metadata (max_values: None, max_size: None, mode: Ignored) + /// Proof Skipped: Skipped Metadata (max_values: None, max_size: None, mode: Measured) /// The range of component `n` is `[0, 8]`. fn seal_contains_storage_per_kb(n: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `14814 + n * (175782 ±0)` - // Estimated: `0` - // Minimum execution time: 368_310 nanoseconds. - Weight::from_ref_time(512_814_023) - .saturating_add(Weight::from_proof_size(0)) - // Standard Error: 1_337_352 - .saturating_add(Weight::from_ref_time(62_871_539).saturating_mul(n.into())) + // Estimated: `9502 + n * (176872 ±75)` + // Minimum execution time: 457_128 nanoseconds. + Weight::from_ref_time(582_799_799) + .saturating_add(Weight::from_proof_size(9502)) + // Standard Error: 1_151_126 + .saturating_add(Weight::from_ref_time(63_425_277).saturating_mul(n.into())) .saturating_add(RocksDbWeight::get().reads(51_u64)) .saturating_add(RocksDbWeight::get().reads((7_u64).saturating_mul(n.into()))) .saturating_add(RocksDbWeight::get().writes(3_u64)) + .saturating_add(Weight::from_proof_size(176872).saturating_mul(n.into())) } /// Storage: Skipped Metadata (r:0 w:0) - /// Proof Skipped: Skipped Metadata (max_values: None, max_size: None, mode: Ignored) + /// Proof Skipped: Skipped Metadata (max_values: None, max_size: None, mode: Measured) /// The range of component `r` is `[0, 10]`. fn seal_take_storage(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `879 + r * (23740 ±0)` - // Estimated: `0` - // Minimum execution time: 287_096 nanoseconds. - Weight::from_ref_time(204_878_281) - .saturating_add(Weight::from_proof_size(0)) - // Standard Error: 833_763 - .saturating_add(Weight::from_ref_time(483_927_706).saturating_mul(r.into())) + // Estimated: `881 + r * (23739 ±0)` + // Minimum execution time: 375_918 nanoseconds. + Weight::from_ref_time(293_217_646) + .saturating_add(Weight::from_proof_size(881)) + // Standard Error: 840_266 + .saturating_add(Weight::from_ref_time(478_374_701).saturating_mul(r.into())) .saturating_add(RocksDbWeight::get().reads(6_u64)) .saturating_add(RocksDbWeight::get().reads((80_u64).saturating_mul(r.into()))) .saturating_add(RocksDbWeight::get().writes(3_u64)) .saturating_add(RocksDbWeight::get().writes((80_u64).saturating_mul(r.into()))) + .saturating_add(Weight::from_proof_size(23739).saturating_mul(r.into())) } /// Storage: Skipped Metadata (r:0 w:0) - /// Proof Skipped: Skipped Metadata (max_values: None, max_size: None, mode: Ignored) + /// Proof Skipped: Skipped Metadata (max_values: None, max_size: None, mode: Measured) /// The range of component `n` is `[0, 8]`. fn seal_take_storage_per_kb(n: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `15470 + n * (175775 ±0)` - // Estimated: `0` - // Minimum execution time: 399_318 nanoseconds. - Weight::from_ref_time(586_658_466) - .saturating_add(Weight::from_proof_size(0)) - // Standard Error: 1_734_497 - .saturating_add(Weight::from_ref_time(161_047_970).saturating_mul(n.into())) + // Estimated: `10010 + n * (176898 ±76)` + // Minimum execution time: 484_207 nanoseconds. + Weight::from_ref_time(664_065_436) + .saturating_add(Weight::from_proof_size(10010)) + // Standard Error: 1_655_442 + .saturating_add(Weight::from_ref_time(166_258_757).saturating_mul(n.into())) .saturating_add(RocksDbWeight::get().reads(51_u64)) .saturating_add(RocksDbWeight::get().reads((7_u64).saturating_mul(n.into()))) .saturating_add(RocksDbWeight::get().writes(48_u64)) .saturating_add(RocksDbWeight::get().writes((7_u64).saturating_mul(n.into()))) + .saturating_add(Weight::from_proof_size(176898).saturating_mul(n.into())) } /// Storage: System Account (r:1602 w:1601) - /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: Ignored) + /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: Measured) /// Storage: Contracts ContractInfoOf (r:1 w:1) - /// Proof: Contracts ContractInfoOf (max_values: None, max_size: Some(258), added: 2733, mode: Ignored) + /// Proof: Contracts ContractInfoOf (max_values: None, max_size: Some(258), added: 2733, mode: Measured) /// Storage: Contracts CodeStorage (r:1 w:0) - /// Proof: Contracts CodeStorage (max_values: None, max_size: Some(126001), added: 128476, mode: Ignored) + /// Proof: Contracts CodeStorage (max_values: None, max_size: Some(126001), added: 128476, mode: Measured) /// Storage: Timestamp Now (r:1 w:0) - /// Proof: Timestamp Now (max_values: Some(1), max_size: Some(8), added: 503, mode: Ignored) + /// Proof: Timestamp Now (max_values: Some(1), max_size: Some(8), added: 503, mode: Measured) /// Storage: System EventTopics (r:2 w:2) - /// Proof Skipped: System EventTopics (max_values: None, max_size: None, mode: Ignored) + /// Proof Skipped: System EventTopics (max_values: None, max_size: None, mode: Measured) /// The range of component `r` is `[0, 20]`. fn seal_transfer(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `1393 + r * (3602 ±0)` - // Estimated: `0` - // Minimum execution time: 285_460 nanoseconds. - Weight::from_ref_time(227_848_079) - .saturating_add(Weight::from_proof_size(0)) - // Standard Error: 717_899 - .saturating_add(Weight::from_ref_time(1_434_112_130).saturating_mul(r.into())) + // Estimated: `21258 + r * (216091 ±0)` + // Minimum execution time: 377_152 nanoseconds. + Weight::from_ref_time(317_470_910) + .saturating_add(Weight::from_proof_size(21258)) + // Standard Error: 994_076 + .saturating_add(Weight::from_ref_time(1_409_416_087).saturating_mul(r.into())) .saturating_add(RocksDbWeight::get().reads(7_u64)) .saturating_add(RocksDbWeight::get().reads((80_u64).saturating_mul(r.into()))) .saturating_add(RocksDbWeight::get().writes(4_u64)) .saturating_add(RocksDbWeight::get().writes((80_u64).saturating_mul(r.into()))) + .saturating_add(Weight::from_proof_size(216091).saturating_mul(r.into())) } /// Storage: System Account (r:1 w:0) - /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: Ignored) + /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: Measured) /// Storage: Contracts ContractInfoOf (r:1601 w:1601) - /// Proof: Contracts ContractInfoOf (max_values: None, max_size: Some(258), added: 2733, mode: Ignored) + /// Proof: Contracts ContractInfoOf (max_values: None, max_size: Some(258), added: 2733, mode: Measured) /// Storage: Contracts CodeStorage (r:2 w:0) - /// Proof: Contracts CodeStorage (max_values: None, max_size: Some(126001), added: 128476, mode: Ignored) + /// Proof: Contracts CodeStorage (max_values: None, max_size: Some(126001), added: 128476, mode: Measured) /// Storage: Timestamp Now (r:1 w:0) - /// Proof: Timestamp Now (max_values: Some(1), max_size: Some(8), added: 503, mode: Ignored) + /// Proof: Timestamp Now (max_values: Some(1), max_size: Some(8), added: 503, mode: Measured) /// Storage: System EventTopics (r:1602 w:1602) - /// Proof Skipped: System EventTopics (max_values: None, max_size: None, mode: Ignored) + /// Proof Skipped: System EventTopics (max_values: None, max_size: None, mode: Measured) /// The range of component `r` is `[0, 20]`. fn seal_call(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `1551 + r * (20511 ±0)` - // Estimated: `0` - // Minimum execution time: 286_947 nanoseconds. - Weight::from_ref_time(288_518_000) - .saturating_add(Weight::from_proof_size(0)) - // Standard Error: 8_492_544 - .saturating_add(Weight::from_ref_time(21_606_377_662).saturating_mul(r.into())) + // Estimated: `21848 + r * (498651 ±1)` + // Minimum execution time: 376_257 nanoseconds. + Weight::from_ref_time(377_035_000) + .saturating_add(Weight::from_proof_size(21848)) + // Standard Error: 7_966_778 + .saturating_add(Weight::from_ref_time(28_873_495_129).saturating_mul(r.into())) .saturating_add(RocksDbWeight::get().reads(7_u64)) .saturating_add(RocksDbWeight::get().reads((160_u64).saturating_mul(r.into()))) .saturating_add(RocksDbWeight::get().writes(3_u64)) .saturating_add(RocksDbWeight::get().writes((160_u64).saturating_mul(r.into()))) + .saturating_add(Weight::from_proof_size(498651).saturating_mul(r.into())) } /// Storage: System Account (r:1 w:0) - /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: Ignored) + /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: Measured) /// Storage: Contracts ContractInfoOf (r:1 w:1) - /// Proof: Contracts ContractInfoOf (max_values: None, max_size: Some(258), added: 2733, mode: Ignored) + /// Proof: Contracts ContractInfoOf (max_values: None, max_size: Some(258), added: 2733, mode: Measured) /// Storage: Contracts CodeStorage (r:1536 w:0) - /// Proof: Contracts CodeStorage (max_values: None, max_size: Some(126001), added: 128476, mode: Ignored) + /// Proof: Contracts CodeStorage (max_values: None, max_size: Some(126001), added: 128476, mode: Measured) /// Storage: Timestamp Now (r:1 w:0) - /// Proof: Timestamp Now (max_values: Some(1), max_size: Some(8), added: 503, mode: Ignored) + /// Proof: Timestamp Now (max_values: Some(1), max_size: Some(8), added: 503, mode: Measured) /// Storage: System EventTopics (r:1537 w:1537) - /// Proof Skipped: System EventTopics (max_values: None, max_size: None, mode: Ignored) + /// Proof Skipped: System EventTopics (max_values: None, max_size: None, mode: Measured) /// The range of component `r` is `[0, 20]`. fn seal_delegate_call(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0 + r * (71670 ±0)` - // Estimated: `0` - // Minimum execution time: 285_196 nanoseconds. - Weight::from_ref_time(287_958_000) - .saturating_add(Weight::from_proof_size(0)) - // Standard Error: 10_345_109 - .saturating_add(Weight::from_ref_time(21_395_365_662).saturating_mul(r.into())) + // Estimated: `17125 + r * (659927 ±822)` + // Minimum execution time: 376_544 nanoseconds. + Weight::from_ref_time(377_490_000) + .saturating_add(Weight::from_proof_size(17125)) + // Standard Error: 8_608_050 + .saturating_add(Weight::from_ref_time(28_568_714_410).saturating_mul(r.into())) .saturating_add(RocksDbWeight::get().reads(6_u64)) .saturating_add(RocksDbWeight::get().reads((150_u64).saturating_mul(r.into()))) .saturating_add(RocksDbWeight::get().writes(3_u64)) .saturating_add(RocksDbWeight::get().writes((75_u64).saturating_mul(r.into()))) + .saturating_add(Weight::from_proof_size(659927).saturating_mul(r.into())) } /// Storage: System Account (r:82 w:81) - /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: Ignored) + /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: Measured) /// Storage: Contracts ContractInfoOf (r:81 w:81) - /// Proof: Contracts ContractInfoOf (max_values: None, max_size: Some(258), added: 2733, mode: Ignored) + /// Proof: Contracts ContractInfoOf (max_values: None, max_size: Some(258), added: 2733, mode: Measured) /// Storage: Contracts CodeStorage (r:2 w:0) - /// Proof: Contracts CodeStorage (max_values: None, max_size: Some(126001), added: 128476, mode: Ignored) + /// Proof: Contracts CodeStorage (max_values: None, max_size: Some(126001), added: 128476, mode: Measured) /// Storage: Timestamp Now (r:1 w:0) - /// Proof: Timestamp Now (max_values: Some(1), max_size: Some(8), added: 503, mode: Ignored) + /// Proof: Timestamp Now (max_values: Some(1), max_size: Some(8), added: 503, mode: Measured) /// Storage: System EventTopics (r:82 w:82) - /// Proof Skipped: System EventTopics (max_values: None, max_size: None, mode: Ignored) + /// Proof Skipped: System EventTopics (max_values: None, max_size: None, mode: Measured) /// The range of component `t` is `[0, 1]`. /// The range of component `c` is `[0, 1024]`. fn seal_call_per_transfer_clone_kb(t: u32, c: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `21611 + t * (15369 ±0)` - // Estimated: `0` - // Minimum execution time: 10_540_614 nanoseconds. - Weight::from_ref_time(9_281_766_912) - .saturating_add(Weight::from_proof_size(0)) - // Standard Error: 5_409_378 - .saturating_add(Weight::from_ref_time(1_449_975_070).saturating_mul(t.into())) - // Standard Error: 8_111 - .saturating_add(Weight::from_ref_time(10_275_108).saturating_mul(c.into())) + // Estimated: `519400 + t * (277320 ±0)` + // Minimum execution time: 10_322_052 nanoseconds. + Weight::from_ref_time(9_248_652_894) + .saturating_add(Weight::from_proof_size(519400)) + // Standard Error: 9_039_638 + .saturating_add(Weight::from_ref_time(1_393_054_441).saturating_mul(t.into())) + // Standard Error: 13_554 + .saturating_add(Weight::from_ref_time(9_819_606).saturating_mul(c.into())) .saturating_add(RocksDbWeight::get().reads(167_u64)) .saturating_add(RocksDbWeight::get().reads((81_u64).saturating_mul(t.into()))) .saturating_add(RocksDbWeight::get().writes(163_u64)) .saturating_add(RocksDbWeight::get().writes((81_u64).saturating_mul(t.into()))) + .saturating_add(Weight::from_proof_size(277320).saturating_mul(t.into())) } /// Storage: System Account (r:1602 w:1602) - /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: Ignored) + /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: Measured) /// Storage: Contracts ContractInfoOf (r:1601 w:1601) - /// Proof: Contracts ContractInfoOf (max_values: None, max_size: Some(258), added: 2733, mode: Ignored) - /// Storage: Contracts CodeStorage (r:1601 w:1600) - /// Proof: Contracts CodeStorage (max_values: None, max_size: Some(126001), added: 128476, mode: Ignored) + /// Proof: Contracts ContractInfoOf (max_values: None, max_size: Some(258), added: 2733, mode: Measured) + /// Storage: Contracts CodeStorage (r:1601 w:0) + /// Proof: Contracts CodeStorage (max_values: None, max_size: Some(126001), added: 128476, mode: Measured) /// Storage: Timestamp Now (r:1 w:0) - /// Proof: Timestamp Now (max_values: Some(1), max_size: Some(8), added: 503, mode: Ignored) + /// Proof: Timestamp Now (max_values: Some(1), max_size: Some(8), added: 503, mode: Measured) /// Storage: Contracts Nonce (r:1 w:1) - /// Proof: Contracts Nonce (max_values: Some(1), max_size: Some(8), added: 503, mode: Ignored) + /// Proof: Contracts Nonce (max_values: Some(1), max_size: Some(8), added: 503, mode: Measured) /// Storage: Contracts OwnerInfoOf (r:1600 w:1600) - /// Proof: Contracts OwnerInfoOf (max_values: None, max_size: Some(88), added: 2563, mode: Ignored) + /// Proof: Contracts OwnerInfoOf (max_values: None, max_size: Some(88), added: 2563, mode: Measured) /// Storage: System EventTopics (r:1602 w:1602) - /// Proof Skipped: System EventTopics (max_values: None, max_size: None, mode: Ignored) + /// Proof Skipped: System EventTopics (max_values: None, max_size: None, mode: Measured) /// The range of component `r` is `[0, 20]`. fn seal_instantiate(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `1613 + r * (25576 ±0)` - // Estimated: `0` - // Minimum execution time: 285_643 nanoseconds. - Weight::from_ref_time(287_472_000) - .saturating_add(Weight::from_proof_size(0)) - // Standard Error: 25_921_947 - .saturating_add(Weight::from_ref_time(27_745_815_511).saturating_mul(r.into())) + // Estimated: `25698 + r * (1169112 ±1)` + // Minimum execution time: 376_639 nanoseconds. + Weight::from_ref_time(377_892_000) + .saturating_add(Weight::from_proof_size(25698)) + // Standard Error: 21_259_255 + .saturating_add(Weight::from_ref_time(34_131_174_956).saturating_mul(r.into())) .saturating_add(RocksDbWeight::get().reads(8_u64)) .saturating_add(RocksDbWeight::get().reads((400_u64).saturating_mul(r.into()))) .saturating_add(RocksDbWeight::get().writes(5_u64)) - .saturating_add(RocksDbWeight::get().writes((400_u64).saturating_mul(r.into()))) + .saturating_add(RocksDbWeight::get().writes((320_u64).saturating_mul(r.into()))) + .saturating_add(Weight::from_proof_size(1169112).saturating_mul(r.into())) } /// Storage: System Account (r:82 w:82) - /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: Ignored) + /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: Measured) /// Storage: Contracts ContractInfoOf (r:81 w:81) - /// Proof: Contracts ContractInfoOf (max_values: None, max_size: Some(258), added: 2733, mode: Ignored) - /// Storage: Contracts CodeStorage (r:2 w:1) - /// Proof: Contracts CodeStorage (max_values: None, max_size: Some(126001), added: 128476, mode: Ignored) + /// Proof: Contracts ContractInfoOf (max_values: None, max_size: Some(258), added: 2733, mode: Measured) + /// Storage: Contracts CodeStorage (r:2 w:0) + /// Proof: Contracts CodeStorage (max_values: None, max_size: Some(126001), added: 128476, mode: Measured) /// Storage: Timestamp Now (r:1 w:0) - /// Proof: Timestamp Now (max_values: Some(1), max_size: Some(8), added: 503, mode: Ignored) + /// Proof: Timestamp Now (max_values: Some(1), max_size: Some(8), added: 503, mode: Measured) /// Storage: Contracts Nonce (r:1 w:1) - /// Proof: Contracts Nonce (max_values: Some(1), max_size: Some(8), added: 503, mode: Ignored) + /// Proof: Contracts Nonce (max_values: Some(1), max_size: Some(8), added: 503, mode: Measured) /// Storage: Contracts OwnerInfoOf (r:1 w:1) - /// Proof: Contracts OwnerInfoOf (max_values: None, max_size: Some(88), added: 2563, mode: Ignored) + /// Proof: Contracts OwnerInfoOf (max_values: None, max_size: Some(88), added: 2563, mode: Measured) /// Storage: System EventTopics (r:82 w:82) - /// Proof Skipped: System EventTopics (max_values: None, max_size: None, mode: Ignored) + /// Proof Skipped: System EventTopics (max_values: None, max_size: None, mode: Measured) /// The range of component `t` is `[0, 1]`. /// The range of component `i` is `[0, 960]`. /// The range of component `s` is `[0, 960]`. fn seal_instantiate_per_transfer_input_salt_kb(t: u32, i: u32, s: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `5666 + t * (17 ±0)` - // Estimated: `0` - // Minimum execution time: 129_710_453 nanoseconds. - Weight::from_ref_time(14_347_603_160) - .saturating_add(Weight::from_proof_size(0)) - // Standard Error: 194_692 - .saturating_add(Weight::from_ref_time(128_837_066).saturating_mul(i.into())) - // Standard Error: 194_692 - .saturating_add(Weight::from_ref_time(127_568_555).saturating_mul(s.into())) + // Estimated: `651914 + t * (2762 ±3)` + // Minimum execution time: 130_366_376 nanoseconds. + Weight::from_ref_time(9_844_607_874) + .saturating_add(Weight::from_proof_size(651914)) + // Standard Error: 100_211_149 + .saturating_add(Weight::from_ref_time(390_481_449).saturating_mul(t.into())) + // Standard Error: 163_416 + .saturating_add(Weight::from_ref_time(126_154_200).saturating_mul(i.into())) + // Standard Error: 163_416 + .saturating_add(Weight::from_ref_time(126_430_874).saturating_mul(s.into())) .saturating_add(RocksDbWeight::get().reads(249_u64)) .saturating_add(RocksDbWeight::get().reads((1_u64).saturating_mul(t.into()))) - .saturating_add(RocksDbWeight::get().writes(247_u64)) + .saturating_add(RocksDbWeight::get().writes(246_u64)) .saturating_add(RocksDbWeight::get().writes((1_u64).saturating_mul(t.into()))) + .saturating_add(Weight::from_proof_size(2762).saturating_mul(t.into())) } /// Storage: System Account (r:1 w:0) - /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: Ignored) + /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: Measured) /// Storage: Contracts ContractInfoOf (r:1 w:1) - /// Proof: Contracts ContractInfoOf (max_values: None, max_size: Some(258), added: 2733, mode: Ignored) + /// Proof: Contracts ContractInfoOf (max_values: None, max_size: Some(258), added: 2733, mode: Measured) /// Storage: Contracts CodeStorage (r:1 w:0) - /// Proof: Contracts CodeStorage (max_values: None, max_size: Some(126001), added: 128476, mode: Ignored) + /// Proof: Contracts CodeStorage (max_values: None, max_size: Some(126001), added: 128476, mode: Measured) /// Storage: Timestamp Now (r:1 w:0) - /// Proof: Timestamp Now (max_values: Some(1), max_size: Some(8), added: 503, mode: Ignored) + /// Proof: Timestamp Now (max_values: Some(1), max_size: Some(8), added: 503, mode: Measured) /// Storage: System EventTopics (r:2 w:2) - /// Proof Skipped: System EventTopics (max_values: None, max_size: None, mode: Ignored) + /// Proof Skipped: System EventTopics (max_values: None, max_size: None, mode: Measured) /// The range of component `r` is `[0, 1]`. fn seal_hash_sha2_256(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `839 + r * (642 ±0)` - // Estimated: `0` - // Minimum execution time: 282_059 nanoseconds. - Weight::from_ref_time(287_215_177) - .saturating_add(Weight::from_proof_size(0)) - // Standard Error: 273_484 - .saturating_add(Weight::from_ref_time(44_941_522).saturating_mul(r.into())) + // Estimated: `17065 + r * (3210 ±0)` + // Minimum execution time: 373_848 nanoseconds. + Weight::from_ref_time(375_974_597) + .saturating_add(Weight::from_proof_size(17065)) + // Standard Error: 558_923 + .saturating_add(Weight::from_ref_time(42_648_202).saturating_mul(r.into())) .saturating_add(RocksDbWeight::get().reads(6_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) + .saturating_add(Weight::from_proof_size(3210).saturating_mul(r.into())) } /// Storage: System Account (r:1 w:0) - /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: Ignored) + /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: Measured) /// Storage: Contracts ContractInfoOf (r:1 w:1) - /// Proof: Contracts ContractInfoOf (max_values: None, max_size: Some(258), added: 2733, mode: Ignored) + /// Proof: Contracts ContractInfoOf (max_values: None, max_size: Some(258), added: 2733, mode: Measured) /// Storage: Contracts CodeStorage (r:1 w:0) - /// Proof: Contracts CodeStorage (max_values: None, max_size: Some(126001), added: 128476, mode: Ignored) + /// Proof: Contracts CodeStorage (max_values: None, max_size: Some(126001), added: 128476, mode: Measured) /// Storage: Timestamp Now (r:1 w:0) - /// Proof: Timestamp Now (max_values: Some(1), max_size: Some(8), added: 503, mode: Ignored) + /// Proof: Timestamp Now (max_values: Some(1), max_size: Some(8), added: 503, mode: Measured) /// Storage: System EventTopics (r:2 w:2) - /// Proof Skipped: System EventTopics (max_values: None, max_size: None, mode: Ignored) + /// Proof Skipped: System EventTopics (max_values: None, max_size: None, mode: Measured) /// The range of component `n` is `[0, 1024]`. fn seal_hash_sha2_256_per_kb(n: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `1641` - // Estimated: `0` - // Minimum execution time: 330_822 nanoseconds. - Weight::from_ref_time(333_012_000) - .saturating_add(Weight::from_proof_size(0)) - // Standard Error: 53_200 - .saturating_add(Weight::from_ref_time(327_144_474).saturating_mul(n.into())) + // Estimated: `21000` + // Minimum execution time: 417_281 nanoseconds. + Weight::from_ref_time(418_086_000) + .saturating_add(Weight::from_proof_size(21000)) + // Standard Error: 43_883 + .saturating_add(Weight::from_ref_time(324_497_460).saturating_mul(n.into())) .saturating_add(RocksDbWeight::get().reads(6_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) } /// Storage: System Account (r:1 w:0) - /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: Ignored) + /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: Measured) /// Storage: Contracts ContractInfoOf (r:1 w:1) - /// Proof: Contracts ContractInfoOf (max_values: None, max_size: Some(258), added: 2733, mode: Ignored) + /// Proof: Contracts ContractInfoOf (max_values: None, max_size: Some(258), added: 2733, mode: Measured) /// Storage: Contracts CodeStorage (r:1 w:0) - /// Proof: Contracts CodeStorage (max_values: None, max_size: Some(126001), added: 128476, mode: Ignored) + /// Proof: Contracts CodeStorage (max_values: None, max_size: Some(126001), added: 128476, mode: Measured) /// Storage: Timestamp Now (r:1 w:0) - /// Proof: Timestamp Now (max_values: Some(1), max_size: Some(8), added: 503, mode: Ignored) + /// Proof: Timestamp Now (max_values: Some(1), max_size: Some(8), added: 503, mode: Measured) /// Storage: System EventTopics (r:2 w:2) - /// Proof Skipped: System EventTopics (max_values: None, max_size: None, mode: Ignored) + /// Proof Skipped: System EventTopics (max_values: None, max_size: None, mode: Measured) /// The range of component `r` is `[0, 1]`. fn seal_hash_keccak_256(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `841 + r * (642 ±0)` - // Estimated: `0` - // Minimum execution time: 282_859 nanoseconds. - Weight::from_ref_time(286_293_402) - .saturating_add(Weight::from_proof_size(0)) - // Standard Error: 187_930 - .saturating_add(Weight::from_ref_time(57_415_297).saturating_mul(r.into())) + // Estimated: `17075 + r * (3210 ±0)` + // Minimum execution time: 372_362 nanoseconds. + Weight::from_ref_time(374_975_677) + .saturating_add(Weight::from_proof_size(17075)) + // Standard Error: 307_932 + .saturating_add(Weight::from_ref_time(57_607_522).saturating_mul(r.into())) .saturating_add(RocksDbWeight::get().reads(6_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) + .saturating_add(Weight::from_proof_size(3210).saturating_mul(r.into())) } /// Storage: System Account (r:1 w:0) - /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: Ignored) + /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: Measured) /// Storage: Contracts ContractInfoOf (r:1 w:1) - /// Proof: Contracts ContractInfoOf (max_values: None, max_size: Some(258), added: 2733, mode: Ignored) + /// Proof: Contracts ContractInfoOf (max_values: None, max_size: Some(258), added: 2733, mode: Measured) /// Storage: Contracts CodeStorage (r:1 w:0) - /// Proof: Contracts CodeStorage (max_values: None, max_size: Some(126001), added: 128476, mode: Ignored) + /// Proof: Contracts CodeStorage (max_values: None, max_size: Some(126001), added: 128476, mode: Measured) /// Storage: Timestamp Now (r:1 w:0) - /// Proof: Timestamp Now (max_values: Some(1), max_size: Some(8), added: 503, mode: Ignored) + /// Proof: Timestamp Now (max_values: Some(1), max_size: Some(8), added: 503, mode: Measured) /// Storage: System EventTopics (r:2 w:2) - /// Proof Skipped: System EventTopics (max_values: None, max_size: None, mode: Ignored) + /// Proof Skipped: System EventTopics (max_values: None, max_size: None, mode: Measured) /// The range of component `n` is `[0, 1024]`. fn seal_hash_keccak_256_per_kb(n: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `1643` - // Estimated: `0` - // Minimum execution time: 342_221 nanoseconds. - Weight::from_ref_time(343_250_000) - .saturating_add(Weight::from_proof_size(0)) - // Standard Error: 67_989 - .saturating_add(Weight::from_ref_time(261_498_077).saturating_mul(n.into())) + // Estimated: `21040` + // Minimum execution time: 431_034 nanoseconds. + Weight::from_ref_time(431_571_000) + .saturating_add(Weight::from_proof_size(21040)) + // Standard Error: 80_071 + .saturating_add(Weight::from_ref_time(261_645_325).saturating_mul(n.into())) .saturating_add(RocksDbWeight::get().reads(6_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) } /// Storage: System Account (r:1 w:0) - /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: Ignored) + /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: Measured) /// Storage: Contracts ContractInfoOf (r:1 w:1) - /// Proof: Contracts ContractInfoOf (max_values: None, max_size: Some(258), added: 2733, mode: Ignored) + /// Proof: Contracts ContractInfoOf (max_values: None, max_size: Some(258), added: 2733, mode: Measured) /// Storage: Contracts CodeStorage (r:1 w:0) - /// Proof: Contracts CodeStorage (max_values: None, max_size: Some(126001), added: 128476, mode: Ignored) + /// Proof: Contracts CodeStorage (max_values: None, max_size: Some(126001), added: 128476, mode: Measured) /// Storage: Timestamp Now (r:1 w:0) - /// Proof: Timestamp Now (max_values: Some(1), max_size: Some(8), added: 503, mode: Ignored) + /// Proof: Timestamp Now (max_values: Some(1), max_size: Some(8), added: 503, mode: Measured) /// Storage: System EventTopics (r:2 w:2) - /// Proof Skipped: System EventTopics (max_values: None, max_size: None, mode: Ignored) + /// Proof Skipped: System EventTopics (max_values: None, max_size: None, mode: Measured) /// The range of component `r` is `[0, 1]`. fn seal_hash_blake2_256(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `841 + r * (642 ±0)` - // Estimated: `0` - // Minimum execution time: 282_158 nanoseconds. - Weight::from_ref_time(285_783_279) - .saturating_add(Weight::from_proof_size(0)) - // Standard Error: 200_413 - .saturating_add(Weight::from_ref_time(35_099_520).saturating_mul(r.into())) + // Estimated: `17075 + r * (3210 ±0)` + // Minimum execution time: 372_069 nanoseconds. + Weight::from_ref_time(374_200_608) + .saturating_add(Weight::from_proof_size(17075)) + // Standard Error: 171_799 + .saturating_add(Weight::from_ref_time(32_843_391).saturating_mul(r.into())) .saturating_add(RocksDbWeight::get().reads(6_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) + .saturating_add(Weight::from_proof_size(3210).saturating_mul(r.into())) } /// Storage: System Account (r:1 w:0) - /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: Ignored) + /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: Measured) /// Storage: Contracts ContractInfoOf (r:1 w:1) - /// Proof: Contracts ContractInfoOf (max_values: None, max_size: Some(258), added: 2733, mode: Ignored) + /// Proof: Contracts ContractInfoOf (max_values: None, max_size: Some(258), added: 2733, mode: Measured) /// Storage: Contracts CodeStorage (r:1 w:0) - /// Proof: Contracts CodeStorage (max_values: None, max_size: Some(126001), added: 128476, mode: Ignored) + /// Proof: Contracts CodeStorage (max_values: None, max_size: Some(126001), added: 128476, mode: Measured) /// Storage: Timestamp Now (r:1 w:0) - /// Proof: Timestamp Now (max_values: Some(1), max_size: Some(8), added: 503, mode: Ignored) + /// Proof: Timestamp Now (max_values: Some(1), max_size: Some(8), added: 503, mode: Measured) /// Storage: System EventTopics (r:2 w:2) - /// Proof Skipped: System EventTopics (max_values: None, max_size: None, mode: Ignored) + /// Proof Skipped: System EventTopics (max_values: None, max_size: None, mode: Measured) /// The range of component `n` is `[0, 1024]`. fn seal_hash_blake2_256_per_kb(n: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `1643` - // Estimated: `0` - // Minimum execution time: 317_376 nanoseconds. - Weight::from_ref_time(319_441_000) - .saturating_add(Weight::from_proof_size(0)) - // Standard Error: 61_325 - .saturating_add(Weight::from_ref_time(101_080_739).saturating_mul(n.into())) + // Estimated: `21010` + // Minimum execution time: 406_143 nanoseconds. + Weight::from_ref_time(406_744_000) + .saturating_add(Weight::from_proof_size(21010)) + // Standard Error: 48_038 + .saturating_add(Weight::from_ref_time(103_286_295).saturating_mul(n.into())) .saturating_add(RocksDbWeight::get().reads(6_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) } /// Storage: System Account (r:1 w:0) - /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: Ignored) + /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: Measured) /// Storage: Contracts ContractInfoOf (r:1 w:1) - /// Proof: Contracts ContractInfoOf (max_values: None, max_size: Some(258), added: 2733, mode: Ignored) + /// Proof: Contracts ContractInfoOf (max_values: None, max_size: Some(258), added: 2733, mode: Measured) /// Storage: Contracts CodeStorage (r:1 w:0) - /// Proof: Contracts CodeStorage (max_values: None, max_size: Some(126001), added: 128476, mode: Ignored) + /// Proof: Contracts CodeStorage (max_values: None, max_size: Some(126001), added: 128476, mode: Measured) /// Storage: Timestamp Now (r:1 w:0) - /// Proof: Timestamp Now (max_values: Some(1), max_size: Some(8), added: 503, mode: Ignored) + /// Proof: Timestamp Now (max_values: Some(1), max_size: Some(8), added: 503, mode: Measured) /// Storage: System EventTopics (r:2 w:2) - /// Proof Skipped: System EventTopics (max_values: None, max_size: None, mode: Ignored) + /// Proof Skipped: System EventTopics (max_values: None, max_size: None, mode: Measured) /// The range of component `r` is `[0, 1]`. fn seal_hash_blake2_128(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `841 + r * (679 ±0)` - // Estimated: `0` - // Minimum execution time: 280_403 nanoseconds. - Weight::from_ref_time(282_592_267) - .saturating_add(Weight::from_proof_size(0)) - // Standard Error: 237_832 - .saturating_add(Weight::from_ref_time(37_056_632).saturating_mul(r.into())) + // Estimated: `17075 + r * (3395 ±0)` + // Minimum execution time: 372_201 nanoseconds. + Weight::from_ref_time(374_049_708) + .saturating_add(Weight::from_proof_size(17075)) + // Standard Error: 387_179 + .saturating_add(Weight::from_ref_time(38_857_191).saturating_mul(r.into())) .saturating_add(RocksDbWeight::get().reads(6_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) + .saturating_add(Weight::from_proof_size(3395).saturating_mul(r.into())) } /// Storage: System Account (r:1 w:0) - /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: Ignored) + /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: Measured) /// Storage: Contracts ContractInfoOf (r:1 w:1) - /// Proof: Contracts ContractInfoOf (max_values: None, max_size: Some(258), added: 2733, mode: Ignored) + /// Proof: Contracts ContractInfoOf (max_values: None, max_size: Some(258), added: 2733, mode: Measured) /// Storage: Contracts CodeStorage (r:1 w:0) - /// Proof: Contracts CodeStorage (max_values: None, max_size: Some(126001), added: 128476, mode: Ignored) + /// Proof: Contracts CodeStorage (max_values: None, max_size: Some(126001), added: 128476, mode: Measured) /// Storage: Timestamp Now (r:1 w:0) - /// Proof: Timestamp Now (max_values: Some(1), max_size: Some(8), added: 503, mode: Ignored) + /// Proof: Timestamp Now (max_values: Some(1), max_size: Some(8), added: 503, mode: Measured) /// Storage: System EventTopics (r:2 w:2) - /// Proof Skipped: System EventTopics (max_values: None, max_size: None, mode: Ignored) + /// Proof Skipped: System EventTopics (max_values: None, max_size: None, mode: Measured) /// The range of component `n` is `[0, 1024]`. fn seal_hash_blake2_128_per_kb(n: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `1643` - // Estimated: `0` - // Minimum execution time: 315_482 nanoseconds. - Weight::from_ref_time(316_160_000) - .saturating_add(Weight::from_proof_size(0)) - // Standard Error: 40_926 - .saturating_add(Weight::from_ref_time(100_317_235).saturating_mul(n.into())) + // Estimated: `21050` + // Minimum execution time: 405_819 nanoseconds. + Weight::from_ref_time(406_364_000) + .saturating_add(Weight::from_proof_size(21050)) + // Standard Error: 46_248 + .saturating_add(Weight::from_ref_time(103_189_157).saturating_mul(n.into())) .saturating_add(RocksDbWeight::get().reads(6_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) } /// Storage: System Account (r:1 w:0) - /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: Ignored) + /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: Measured) /// Storage: Contracts ContractInfoOf (r:1 w:1) - /// Proof: Contracts ContractInfoOf (max_values: None, max_size: Some(258), added: 2733, mode: Ignored) + /// Proof: Contracts ContractInfoOf (max_values: None, max_size: Some(258), added: 2733, mode: Measured) /// Storage: Contracts CodeStorage (r:1 w:0) - /// Proof: Contracts CodeStorage (max_values: None, max_size: Some(126001), added: 128476, mode: Ignored) + /// Proof: Contracts CodeStorage (max_values: None, max_size: Some(126001), added: 128476, mode: Measured) /// Storage: Timestamp Now (r:1 w:0) - /// Proof: Timestamp Now (max_values: Some(1), max_size: Some(8), added: 503, mode: Ignored) + /// Proof: Timestamp Now (max_values: Some(1), max_size: Some(8), added: 503, mode: Measured) /// Storage: System EventTopics (r:2 w:2) - /// Proof Skipped: System EventTopics (max_values: None, max_size: None, mode: Ignored) + /// Proof Skipped: System EventTopics (max_values: None, max_size: None, mode: Measured) /// The range of component `r` is `[0, 1]`. fn seal_ecdsa_recover(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `885 + r * (6083 ±0)` - // Estimated: `0` - // Minimum execution time: 276_155 nanoseconds. - Weight::from_ref_time(278_531_728) - .saturating_add(Weight::from_proof_size(0)) - // Standard Error: 510_501 - .saturating_add(Weight::from_ref_time(3_056_236_671).saturating_mul(r.into())) + // Estimated: `17295 + r * (30415 ±0)` + // Minimum execution time: 374_836 nanoseconds. + Weight::from_ref_time(379_385_500) + .saturating_add(Weight::from_proof_size(17295)) + // Standard Error: 1_694_427 + .saturating_add(Weight::from_ref_time(3_021_801_000).saturating_mul(r.into())) .saturating_add(RocksDbWeight::get().reads(6_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) + .saturating_add(Weight::from_proof_size(30415).saturating_mul(r.into())) } /// Storage: System Account (r:1 w:0) - /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: Ignored) + /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: Measured) /// Storage: Contracts ContractInfoOf (r:1 w:1) - /// Proof: Contracts ContractInfoOf (max_values: None, max_size: Some(258), added: 2733, mode: Ignored) + /// Proof: Contracts ContractInfoOf (max_values: None, max_size: Some(258), added: 2733, mode: Measured) /// Storage: Contracts CodeStorage (r:1 w:0) - /// Proof: Contracts CodeStorage (max_values: None, max_size: Some(126001), added: 128476, mode: Ignored) + /// Proof: Contracts CodeStorage (max_values: None, max_size: Some(126001), added: 128476, mode: Measured) /// Storage: Timestamp Now (r:1 w:0) - /// Proof: Timestamp Now (max_values: Some(1), max_size: Some(8), added: 503, mode: Ignored) + /// Proof: Timestamp Now (max_values: Some(1), max_size: Some(8), added: 503, mode: Measured) /// Storage: System EventTopics (r:2 w:2) - /// Proof Skipped: System EventTopics (max_values: None, max_size: None, mode: Ignored) + /// Proof Skipped: System EventTopics (max_values: None, max_size: None, mode: Measured) /// The range of component `r` is `[0, 1]`. fn seal_ecdsa_to_eth_address(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `854 + r * (3362 ±0)` - // Estimated: `0` - // Minimum execution time: 276_204 nanoseconds. - Weight::from_ref_time(278_114_283) - .saturating_add(Weight::from_proof_size(0)) - // Standard Error: 217_508 - .saturating_add(Weight::from_ref_time(737_577_816).saturating_mul(r.into())) + // Estimated: `17140 + r * (16810 ±0)` + // Minimum execution time: 374_223 nanoseconds. + Weight::from_ref_time(376_120_230) + .saturating_add(Weight::from_proof_size(17140)) + // Standard Error: 619_576 + .saturating_add(Weight::from_ref_time(754_257_969).saturating_mul(r.into())) .saturating_add(RocksDbWeight::get().reads(6_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) + .saturating_add(Weight::from_proof_size(16810).saturating_mul(r.into())) } /// Storage: System Account (r:1 w:0) - /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: Ignored) + /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: Measured) /// Storage: Contracts ContractInfoOf (r:1 w:1) - /// Proof: Contracts ContractInfoOf (max_values: None, max_size: Some(258), added: 2733, mode: Ignored) + /// Proof: Contracts ContractInfoOf (max_values: None, max_size: Some(258), added: 2733, mode: Measured) /// Storage: Contracts CodeStorage (r:1536 w:0) - /// Proof: Contracts CodeStorage (max_values: None, max_size: Some(126001), added: 128476, mode: Ignored) + /// Proof: Contracts CodeStorage (max_values: None, max_size: Some(126001), added: 128476, mode: Measured) /// Storage: Timestamp Now (r:1 w:0) - /// Proof: Timestamp Now (max_values: Some(1), max_size: Some(8), added: 503, mode: Ignored) + /// Proof: Timestamp Now (max_values: Some(1), max_size: Some(8), added: 503, mode: Measured) /// Storage: Contracts OwnerInfoOf (r:1536 w:1536) - /// Proof: Contracts OwnerInfoOf (max_values: None, max_size: Some(88), added: 2563, mode: Ignored) + /// Proof: Contracts OwnerInfoOf (max_values: None, max_size: Some(88), added: 2563, mode: Measured) /// Storage: System EventTopics (r:1538 w:1538) - /// Proof Skipped: System EventTopics (max_values: None, max_size: None, mode: Ignored) + /// Proof Skipped: System EventTopics (max_values: None, max_size: None, mode: Measured) /// The range of component `r` is `[0, 20]`. fn seal_set_code_hash(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0 + r * (79300 ±0)` - // Estimated: `0` - // Minimum execution time: 276_330 nanoseconds. - Weight::from_ref_time(277_573_000) - .saturating_add(Weight::from_proof_size(0)) - // Standard Error: 3_652_635 - .saturating_add(Weight::from_ref_time(1_547_361_882).saturating_mul(r.into())) + // Estimated: `64652 + r * (942952 ±829)` + // Minimum execution time: 374_442 nanoseconds. + Weight::from_ref_time(375_591_000) + .saturating_add(Weight::from_proof_size(64652)) + // Standard Error: 3_764_193 + .saturating_add(Weight::from_ref_time(1_552_885_601).saturating_mul(r.into())) .saturating_add(RocksDbWeight::get().reads(6_u64)) .saturating_add(RocksDbWeight::get().reads((225_u64).saturating_mul(r.into()))) .saturating_add(RocksDbWeight::get().writes(3_u64)) .saturating_add(RocksDbWeight::get().writes((150_u64).saturating_mul(r.into()))) + .saturating_add(Weight::from_proof_size(942952).saturating_mul(r.into())) } /// Storage: System Account (r:1 w:0) - /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: Ignored) + /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: Measured) /// Storage: Contracts ContractInfoOf (r:1 w:1) - /// Proof: Contracts ContractInfoOf (max_values: None, max_size: Some(258), added: 2733, mode: Ignored) + /// Proof: Contracts ContractInfoOf (max_values: None, max_size: Some(258), added: 2733, mode: Measured) /// Storage: Contracts CodeStorage (r:1 w:0) - /// Proof: Contracts CodeStorage (max_values: None, max_size: Some(126001), added: 128476, mode: Ignored) + /// Proof: Contracts CodeStorage (max_values: None, max_size: Some(126001), added: 128476, mode: Measured) /// Storage: Timestamp Now (r:1 w:0) - /// Proof: Timestamp Now (max_values: Some(1), max_size: Some(8), added: 503, mode: Ignored) + /// Proof: Timestamp Now (max_values: Some(1), max_size: Some(8), added: 503, mode: Measured) /// Storage: System EventTopics (r:2 w:2) - /// Proof Skipped: System EventTopics (max_values: None, max_size: None, mode: Ignored) + /// Proof Skipped: System EventTopics (max_values: None, max_size: None, mode: Measured) /// The range of component `r` is `[0, 20]`. fn seal_reentrance_count(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `837 + r * (240 ±0)` - // Estimated: `0` - // Minimum execution time: 276_228 nanoseconds. - Weight::from_ref_time(281_135_551) - .saturating_add(Weight::from_proof_size(0)) - // Standard Error: 18_737 - .saturating_add(Weight::from_ref_time(10_998_953).saturating_mul(r.into())) + // Estimated: `17055 + r * (1200 ±0)` + // Minimum execution time: 374_922 nanoseconds. + Weight::from_ref_time(376_234_094) + .saturating_add(Weight::from_proof_size(17055)) + // Standard Error: 45_995 + .saturating_add(Weight::from_ref_time(11_606_505).saturating_mul(r.into())) .saturating_add(RocksDbWeight::get().reads(6_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) + .saturating_add(Weight::from_proof_size(1200).saturating_mul(r.into())) } /// Storage: System Account (r:1 w:0) - /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: Ignored) + /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: Measured) /// Storage: Contracts ContractInfoOf (r:1 w:1) - /// Proof: Contracts ContractInfoOf (max_values: None, max_size: Some(258), added: 2733, mode: Ignored) + /// Proof: Contracts ContractInfoOf (max_values: None, max_size: Some(258), added: 2733, mode: Measured) /// Storage: Contracts CodeStorage (r:1 w:0) - /// Proof: Contracts CodeStorage (max_values: None, max_size: Some(126001), added: 128476, mode: Ignored) + /// Proof: Contracts CodeStorage (max_values: None, max_size: Some(126001), added: 128476, mode: Measured) /// Storage: Timestamp Now (r:1 w:0) - /// Proof: Timestamp Now (max_values: Some(1), max_size: Some(8), added: 503, mode: Ignored) + /// Proof: Timestamp Now (max_values: Some(1), max_size: Some(8), added: 503, mode: Measured) /// Storage: System EventTopics (r:2 w:2) - /// Proof Skipped: System EventTopics (max_values: None, max_size: None, mode: Ignored) + /// Proof Skipped: System EventTopics (max_values: None, max_size: None, mode: Measured) /// The range of component `r` is `[0, 20]`. fn seal_account_reentrance_count(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `2056 + r * (3153 ±0)` - // Estimated: `0` - // Minimum execution time: 278_157 nanoseconds. - Weight::from_ref_time(300_739_078) - .saturating_add(Weight::from_proof_size(0)) - // Standard Error: 72_695 - .saturating_add(Weight::from_ref_time(18_499_645).saturating_mul(r.into())) + // Estimated: `21730 + r * (15870 ±2)` + // Minimum execution time: 376_762 nanoseconds. + Weight::from_ref_time(396_934_359) + .saturating_add(Weight::from_proof_size(21730)) + // Standard Error: 68_263 + .saturating_add(Weight::from_ref_time(18_367_270).saturating_mul(r.into())) .saturating_add(RocksDbWeight::get().reads(6_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) + .saturating_add(Weight::from_proof_size(15870).saturating_mul(r.into())) } /// Storage: System Account (r:1 w:0) - /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: Ignored) + /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: Measured) /// Storage: Contracts ContractInfoOf (r:1 w:1) - /// Proof: Contracts ContractInfoOf (max_values: None, max_size: Some(258), added: 2733, mode: Ignored) + /// Proof: Contracts ContractInfoOf (max_values: None, max_size: Some(258), added: 2733, mode: Measured) /// Storage: Contracts CodeStorage (r:1 w:0) - /// Proof: Contracts CodeStorage (max_values: None, max_size: Some(126001), added: 128476, mode: Ignored) + /// Proof: Contracts CodeStorage (max_values: None, max_size: Some(126001), added: 128476, mode: Measured) /// Storage: Timestamp Now (r:1 w:0) - /// Proof: Timestamp Now (max_values: Some(1), max_size: Some(8), added: 503, mode: Ignored) + /// Proof: Timestamp Now (max_values: Some(1), max_size: Some(8), added: 503, mode: Measured) /// Storage: Contracts Nonce (r:1 w:1) - /// Proof: Contracts Nonce (max_values: Some(1), max_size: Some(8), added: 503, mode: Ignored) + /// Proof: Contracts Nonce (max_values: Some(1), max_size: Some(8), added: 503, mode: Measured) /// Storage: System EventTopics (r:2 w:2) - /// Proof Skipped: System EventTopics (max_values: None, max_size: None, mode: Ignored) + /// Proof Skipped: System EventTopics (max_values: None, max_size: None, mode: Measured) /// The range of component `r` is `[0, 20]`. fn seal_instantiation_nonce(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `840 + r * (240 ±0)` - // Estimated: `0` - // Minimum execution time: 275_532 nanoseconds. - Weight::from_ref_time(281_299_677) - .saturating_add(Weight::from_proof_size(0)) - // Standard Error: 21_719 - .saturating_add(Weight::from_ref_time(9_513_444).saturating_mul(r.into())) + // Estimated: `18405 + r * (1440 ±0)` + // Minimum execution time: 374_257 nanoseconds. + Weight::from_ref_time(380_453_380) + .saturating_add(Weight::from_proof_size(18405)) + // Standard Error: 42_718 + .saturating_add(Weight::from_ref_time(9_355_253).saturating_mul(r.into())) .saturating_add(RocksDbWeight::get().reads(7_u64)) .saturating_add(RocksDbWeight::get().writes(4_u64)) + .saturating_add(Weight::from_proof_size(1440).saturating_mul(r.into())) } /// The range of component `r` is `[0, 50]`. fn instr_i64const(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 856 nanoseconds. - Weight::from_ref_time(1_054_710) + // Minimum execution time: 788 nanoseconds. + Weight::from_ref_time(1_209_829) .saturating_add(Weight::from_proof_size(0)) - // Standard Error: 273 - .saturating_add(Weight::from_ref_time(411_548).saturating_mul(r.into())) + // Standard Error: 3_436 + .saturating_add(Weight::from_ref_time(409_858).saturating_mul(r.into())) } /// The range of component `r` is `[0, 50]`. fn instr_i64load(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 980 nanoseconds. - Weight::from_ref_time(1_463_182) + // Minimum execution time: 955 nanoseconds. + Weight::from_ref_time(1_526_327) .saturating_add(Weight::from_proof_size(0)) - // Standard Error: 415 - .saturating_add(Weight::from_ref_time(1_063_436).saturating_mul(r.into())) + // Standard Error: 2_399 + .saturating_add(Weight::from_ref_time(1_084_504).saturating_mul(r.into())) } /// The range of component `r` is `[0, 50]`. fn instr_i64store(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 991 nanoseconds. - Weight::from_ref_time(1_521_750) + // Minimum execution time: 967 nanoseconds. + Weight::from_ref_time(1_576_183) .saturating_add(Weight::from_proof_size(0)) - // Standard Error: 8_499 - .saturating_add(Weight::from_ref_time(997_972).saturating_mul(r.into())) + // Standard Error: 5_719 + .saturating_add(Weight::from_ref_time(1_006_742).saturating_mul(r.into())) } /// The range of component `r` is `[0, 50]`. fn instr_select(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 833 nanoseconds. - Weight::from_ref_time(1_152_048) + // Minimum execution time: 849 nanoseconds. + Weight::from_ref_time(1_106_539) .saturating_add(Weight::from_proof_size(0)) - // Standard Error: 392 - .saturating_add(Weight::from_ref_time(1_147_506).saturating_mul(r.into())) + // Standard Error: 445 + .saturating_add(Weight::from_ref_time(1_149_752).saturating_mul(r.into())) } /// The range of component `r` is `[0, 50]`. fn instr_if(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 866 nanoseconds. - Weight::from_ref_time(1_154_637) + // Minimum execution time: 829 nanoseconds. + Weight::from_ref_time(1_171_360) .saturating_add(Weight::from_proof_size(0)) - // Standard Error: 5_131 - .saturating_add(Weight::from_ref_time(1_320_580).saturating_mul(r.into())) + // Standard Error: 552 + .saturating_add(Weight::from_ref_time(1_309_914).saturating_mul(r.into())) } /// The range of component `r` is `[0, 50]`. fn instr_br(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 849 nanoseconds. - Weight::from_ref_time(1_073_940) + // Minimum execution time: 835 nanoseconds. + Weight::from_ref_time(1_125_578) .saturating_add(Weight::from_proof_size(0)) - // Standard Error: 639 - .saturating_add(Weight::from_ref_time(642_885).saturating_mul(r.into())) + // Standard Error: 374 + .saturating_add(Weight::from_ref_time(641_683).saturating_mul(r.into())) } /// The range of component `r` is `[0, 50]`. fn instr_br_if(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 855 nanoseconds. - Weight::from_ref_time(728_631) + // Minimum execution time: 816 nanoseconds. + Weight::from_ref_time(1_032_093) .saturating_add(Weight::from_proof_size(0)) - // Standard Error: 1_730 - .saturating_add(Weight::from_ref_time(979_839).saturating_mul(r.into())) + // Standard Error: 811 + .saturating_add(Weight::from_ref_time(956_228).saturating_mul(r.into())) } /// The range of component `r` is `[0, 50]`. fn instr_br_table(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 852 nanoseconds. - Weight::from_ref_time(658_935) + // Minimum execution time: 801 nanoseconds. + Weight::from_ref_time(816_764) .saturating_add(Weight::from_proof_size(0)) - // Standard Error: 2_373 - .saturating_add(Weight::from_ref_time(1_172_585).saturating_mul(r.into())) + // Standard Error: 2_669 + .saturating_add(Weight::from_ref_time(1_166_556).saturating_mul(r.into())) } /// The range of component `e` is `[1, 256]`. fn instr_br_table_per_entry(e: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 2_577 nanoseconds. - Weight::from_ref_time(2_872_555) + // Minimum execution time: 2_639 nanoseconds. + Weight::from_ref_time(2_905_554) .saturating_add(Weight::from_proof_size(0)) - // Standard Error: 70 - .saturating_add(Weight::from_ref_time(4_237).saturating_mul(e.into())) + // Standard Error: 62 + .saturating_add(Weight::from_ref_time(4_438).saturating_mul(e.into())) } /// The range of component `r` is `[0, 50]`. fn instr_call(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 833 nanoseconds. - Weight::from_ref_time(874_000) + // Minimum execution time: 831 nanoseconds. + Weight::from_ref_time(1_729_584) .saturating_add(Weight::from_proof_size(0)) - // Standard Error: 30_863 - .saturating_add(Weight::from_ref_time(2_591_963).saturating_mul(r.into())) + // Standard Error: 33_753 + .saturating_add(Weight::from_ref_time(2_380_315).saturating_mul(r.into())) } /// The range of component `r` is `[0, 50]`. fn instr_call_indirect(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 992 nanoseconds. - Weight::from_ref_time(2_772_709) + // Minimum execution time: 964 nanoseconds. + Weight::from_ref_time(2_445_291) .saturating_add(Weight::from_proof_size(0)) - // Standard Error: 3_822 - .saturating_add(Weight::from_ref_time(3_070_825).saturating_mul(r.into())) + // Standard Error: 3_285 + .saturating_add(Weight::from_ref_time(2_938_681).saturating_mul(r.into())) } /// The range of component `p` is `[0, 128]`. fn instr_call_indirect_per_param(p: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 4_766 nanoseconds. - Weight::from_ref_time(5_559_951) + // Minimum execution time: 4_809 nanoseconds. + Weight::from_ref_time(6_763_286) .saturating_add(Weight::from_proof_size(0)) - // Standard Error: 315 - .saturating_add(Weight::from_ref_time(227_249).saturating_mul(p.into())) + // Standard Error: 3_994 + .saturating_add(Weight::from_ref_time(217_632).saturating_mul(p.into())) } /// The range of component `l` is `[0, 1024]`. fn instr_call_per_local(l: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 3_215 nanoseconds. - Weight::from_ref_time(4_697_732) + // Minimum execution time: 3_138 nanoseconds. + Weight::from_ref_time(3_894_816) .saturating_add(Weight::from_proof_size(0)) - // Standard Error: 99 - .saturating_add(Weight::from_ref_time(46_431).saturating_mul(l.into())) + // Standard Error: 131 + .saturating_add(Weight::from_ref_time(91_699).saturating_mul(l.into())) } /// The range of component `r` is `[0, 50]`. fn instr_local_get(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 2_193 nanoseconds. - Weight::from_ref_time(2_472_277) + // Minimum execution time: 2_959 nanoseconds. + Weight::from_ref_time(3_271_550) .saturating_add(Weight::from_proof_size(0)) - // Standard Error: 374 - .saturating_add(Weight::from_ref_time(459_651).saturating_mul(r.into())) + // Standard Error: 223 + .saturating_add(Weight::from_ref_time(460_056).saturating_mul(r.into())) } /// The range of component `r` is `[0, 50]`. fn instr_local_set(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 2_112 nanoseconds. - Weight::from_ref_time(2_388_132) + // Minimum execution time: 2_970 nanoseconds. + Weight::from_ref_time(3_216_157) .saturating_add(Weight::from_proof_size(0)) - // Standard Error: 380 - .saturating_add(Weight::from_ref_time(485_216).saturating_mul(r.into())) + // Standard Error: 413 + .saturating_add(Weight::from_ref_time(485_842).saturating_mul(r.into())) } /// The range of component `r` is `[0, 50]`. fn instr_local_tee(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 2_219 nanoseconds. - Weight::from_ref_time(2_419_568) + // Minimum execution time: 2_980 nanoseconds. + Weight::from_ref_time(3_323_878) .saturating_add(Weight::from_proof_size(0)) - // Standard Error: 402 - .saturating_add(Weight::from_ref_time(658_484).saturating_mul(r.into())) + // Standard Error: 2_652 + .saturating_add(Weight::from_ref_time(660_257).saturating_mul(r.into())) } /// The range of component `r` is `[0, 50]`. fn instr_global_get(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 959 nanoseconds. - Weight::from_ref_time(1_321_004) + // Minimum execution time: 917 nanoseconds. + Weight::from_ref_time(1_445_816) .saturating_add(Weight::from_proof_size(0)) - // Standard Error: 480 - .saturating_add(Weight::from_ref_time(898_447).saturating_mul(r.into())) + // Standard Error: 5_642 + .saturating_add(Weight::from_ref_time(894_521).saturating_mul(r.into())) } /// The range of component `r` is `[0, 50]`. fn instr_global_set(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 934 nanoseconds. - Weight::from_ref_time(1_264_350) + // Minimum execution time: 965 nanoseconds. + Weight::from_ref_time(1_373_722) .saturating_add(Weight::from_proof_size(0)) - // Standard Error: 1_031 - .saturating_add(Weight::from_ref_time(922_243).saturating_mul(r.into())) + // Standard Error: 1_157 + .saturating_add(Weight::from_ref_time(917_643).saturating_mul(r.into())) } /// The range of component `r` is `[0, 50]`. fn instr_memory_current(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 941 nanoseconds. - Weight::from_ref_time(1_229_407) + // Minimum execution time: 982 nanoseconds. + Weight::from_ref_time(1_240_280) .saturating_add(Weight::from_proof_size(0)) - // Standard Error: 507 - .saturating_add(Weight::from_ref_time(824_206).saturating_mul(r.into())) + // Standard Error: 308 + .saturating_add(Weight::from_ref_time(817_972).saturating_mul(r.into())) } /// The range of component `r` is `[0, 1]`. fn instr_memory_grow(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 870 nanoseconds. - Weight::from_ref_time(946_681) + // Minimum execution time: 858 nanoseconds. + Weight::from_ref_time(962_183) .saturating_add(Weight::from_proof_size(0)) - // Standard Error: 7_248 - .saturating_add(Weight::from_ref_time(179_759_218).saturating_mul(r.into())) + // Standard Error: 6_701 + .saturating_add(Weight::from_ref_time(239_704_216).saturating_mul(r.into())) } /// The range of component `r` is `[0, 50]`. fn instr_i64clz(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 843 nanoseconds. - Weight::from_ref_time(1_105_279) + // Minimum execution time: 807 nanoseconds. + Weight::from_ref_time(1_132_872) .saturating_add(Weight::from_proof_size(0)) - // Standard Error: 251 - .saturating_add(Weight::from_ref_time(633_218).saturating_mul(r.into())) + // Standard Error: 303 + .saturating_add(Weight::from_ref_time(633_832).saturating_mul(r.into())) } /// The range of component `r` is `[0, 50]`. fn instr_i64ctz(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 843 nanoseconds. - Weight::from_ref_time(1_119_719) + // Minimum execution time: 823 nanoseconds. + Weight::from_ref_time(1_267_518) .saturating_add(Weight::from_proof_size(0)) - // Standard Error: 248 - .saturating_add(Weight::from_ref_time(633_064).saturating_mul(r.into())) + // Standard Error: 483 + .saturating_add(Weight::from_ref_time(632_620).saturating_mul(r.into())) } /// The range of component `r` is `[0, 50]`. fn instr_i64popcnt(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 865 nanoseconds. - Weight::from_ref_time(1_394_651) + // Minimum execution time: 857 nanoseconds. + Weight::from_ref_time(1_105_214) .saturating_add(Weight::from_proof_size(0)) - // Standard Error: 9_898 - .saturating_add(Weight::from_ref_time(630_054).saturating_mul(r.into())) + // Standard Error: 285 + .saturating_add(Weight::from_ref_time(635_039).saturating_mul(r.into())) } /// The range of component `r` is `[0, 50]`. fn instr_i64eqz(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 831 nanoseconds. - Weight::from_ref_time(1_095_288) + // Minimum execution time: 821 nanoseconds. + Weight::from_ref_time(750_223) .saturating_add(Weight::from_proof_size(0)) - // Standard Error: 271 - .saturating_add(Weight::from_ref_time(650_578).saturating_mul(r.into())) + // Standard Error: 15_923 + .saturating_add(Weight::from_ref_time(686_322).saturating_mul(r.into())) } /// The range of component `r` is `[0, 50]`. fn instr_i64extendsi32(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 835 nanoseconds. - Weight::from_ref_time(1_128_234) + // Minimum execution time: 797 nanoseconds. + Weight::from_ref_time(1_145_072) .saturating_add(Weight::from_proof_size(0)) - // Standard Error: 239 - .saturating_add(Weight::from_ref_time(615_970).saturating_mul(r.into())) + // Standard Error: 311 + .saturating_add(Weight::from_ref_time(618_147).saturating_mul(r.into())) } /// The range of component `r` is `[0, 50]`. fn instr_i64extendui32(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 839 nanoseconds. - Weight::from_ref_time(1_111_296) + // Minimum execution time: 803 nanoseconds. + Weight::from_ref_time(1_139_498) .saturating_add(Weight::from_proof_size(0)) - // Standard Error: 303 - .saturating_add(Weight::from_ref_time(616_831).saturating_mul(r.into())) + // Standard Error: 284 + .saturating_add(Weight::from_ref_time(617_393).saturating_mul(r.into())) } /// The range of component `r` is `[0, 50]`. fn instr_i32wrapi64(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 839 nanoseconds. - Weight::from_ref_time(1_082_700) + // Minimum execution time: 814 nanoseconds. + Weight::from_ref_time(1_099_405) .saturating_add(Weight::from_proof_size(0)) - // Standard Error: 236 - .saturating_add(Weight::from_ref_time(617_371).saturating_mul(r.into())) + // Standard Error: 663 + .saturating_add(Weight::from_ref_time(618_565).saturating_mul(r.into())) } /// The range of component `r` is `[0, 50]`. fn instr_i64eq(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 837 nanoseconds. - Weight::from_ref_time(1_249_666) + // Minimum execution time: 819 nanoseconds. + Weight::from_ref_time(1_199_220) .saturating_add(Weight::from_proof_size(0)) - // Standard Error: 836 - .saturating_add(Weight::from_ref_time(904_457).saturating_mul(r.into())) + // Standard Error: 485 + .saturating_add(Weight::from_ref_time(906_878).saturating_mul(r.into())) } /// The range of component `r` is `[0, 50]`. fn instr_i64ne(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 818 nanoseconds. - Weight::from_ref_time(1_624_057) + // Minimum execution time: 822 nanoseconds. + Weight::from_ref_time(274_212) .saturating_add(Weight::from_proof_size(0)) - // Standard Error: 5_906 - .saturating_add(Weight::from_ref_time(893_488).saturating_mul(r.into())) + // Standard Error: 29_294 + .saturating_add(Weight::from_ref_time(971_608).saturating_mul(r.into())) } /// The range of component `r` is `[0, 50]`. fn instr_i64lts(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 836 nanoseconds. - Weight::from_ref_time(1_973_044) + // Minimum execution time: 796 nanoseconds. + Weight::from_ref_time(1_396_586) .saturating_add(Weight::from_proof_size(0)) - // Standard Error: 7_556 - .saturating_add(Weight::from_ref_time(885_369).saturating_mul(r.into())) + // Standard Error: 7_205 + .saturating_add(Weight::from_ref_time(903_202).saturating_mul(r.into())) } /// The range of component `r` is `[0, 50]`. fn instr_i64ltu(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 846 nanoseconds. - Weight::from_ref_time(1_088_223) + // Minimum execution time: 833 nanoseconds. + Weight::from_ref_time(1_115_115) .saturating_add(Weight::from_proof_size(0)) - // Standard Error: 531 - .saturating_add(Weight::from_ref_time(909_313).saturating_mul(r.into())) + // Standard Error: 310 + .saturating_add(Weight::from_ref_time(908_195).saturating_mul(r.into())) } /// The range of component `r` is `[0, 50]`. fn instr_i64gts(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 842 nanoseconds. - Weight::from_ref_time(1_435_966) + // Minimum execution time: 854 nanoseconds. + Weight::from_ref_time(1_170_419) .saturating_add(Weight::from_proof_size(0)) - // Standard Error: 3_824 - .saturating_add(Weight::from_ref_time(898_198).saturating_mul(r.into())) + // Standard Error: 298 + .saturating_add(Weight::from_ref_time(907_171).saturating_mul(r.into())) } /// The range of component `r` is `[0, 50]`. fn instr_i64gtu(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 846 nanoseconds. - Weight::from_ref_time(1_154_015) + // Minimum execution time: 822 nanoseconds. + Weight::from_ref_time(1_186_349) .saturating_add(Weight::from_proof_size(0)) - // Standard Error: 319 - .saturating_add(Weight::from_ref_time(918_083).saturating_mul(r.into())) + // Standard Error: 302 + .saturating_add(Weight::from_ref_time(917_857).saturating_mul(r.into())) } /// The range of component `r` is `[0, 50]`. fn instr_i64les(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 848 nanoseconds. - Weight::from_ref_time(1_155_323) + // Minimum execution time: 807 nanoseconds. + Weight::from_ref_time(1_127_093) .saturating_add(Weight::from_proof_size(0)) - // Standard Error: 307 - .saturating_add(Weight::from_ref_time(906_516).saturating_mul(r.into())) + // Standard Error: 1_891 + .saturating_add(Weight::from_ref_time(910_738).saturating_mul(r.into())) } /// The range of component `r` is `[0, 50]`. fn instr_i64leu(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 877 nanoseconds. - Weight::from_ref_time(1_629_210) + // Minimum execution time: 819 nanoseconds. + Weight::from_ref_time(1_143_022) .saturating_add(Weight::from_proof_size(0)) - // Standard Error: 8_175 - .saturating_add(Weight::from_ref_time(906_657).saturating_mul(r.into())) + // Standard Error: 309 + .saturating_add(Weight::from_ref_time(919_047).saturating_mul(r.into())) } /// The range of component `r` is `[0, 50]`. fn instr_i64ges(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 843 nanoseconds. - Weight::from_ref_time(1_126_252) + // Minimum execution time: 788 nanoseconds. + Weight::from_ref_time(1_116_914) .saturating_add(Weight::from_proof_size(0)) - // Standard Error: 251 - .saturating_add(Weight::from_ref_time(907_052).saturating_mul(r.into())) + // Standard Error: 643 + .saturating_add(Weight::from_ref_time(911_159).saturating_mul(r.into())) } /// The range of component `r` is `[0, 50]`. fn instr_i64geu(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 852 nanoseconds. - Weight::from_ref_time(1_497_667) + // Minimum execution time: 791 nanoseconds. + Weight::from_ref_time(1_123_747) .saturating_add(Weight::from_proof_size(0)) - // Standard Error: 7_593 - .saturating_add(Weight::from_ref_time(897_474).saturating_mul(r.into())) + // Standard Error: 583 + .saturating_add(Weight::from_ref_time(910_242).saturating_mul(r.into())) } /// The range of component `r` is `[0, 50]`. fn instr_i64add(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 856 nanoseconds. - Weight::from_ref_time(1_350_390) + // Minimum execution time: 787 nanoseconds. + Weight::from_ref_time(1_109_471) .saturating_add(Weight::from_proof_size(0)) - // Standard Error: 2_625 - .saturating_add(Weight::from_ref_time(889_609).saturating_mul(r.into())) + // Standard Error: 305 + .saturating_add(Weight::from_ref_time(897_608).saturating_mul(r.into())) } /// The range of component `r` is `[0, 50]`. fn instr_i64sub(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 841 nanoseconds. - Weight::from_ref_time(2_677_235) + // Minimum execution time: 821 nanoseconds. + Weight::from_ref_time(1_098_631) .saturating_add(Weight::from_proof_size(0)) - // Standard Error: 11_507 - .saturating_add(Weight::from_ref_time(846_704).saturating_mul(r.into())) + // Standard Error: 523 + .saturating_add(Weight::from_ref_time(887_814).saturating_mul(r.into())) } /// The range of component `r` is `[0, 50]`. fn instr_i64mul(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 846 nanoseconds. - Weight::from_ref_time(1_148_206) + // Minimum execution time: 794 nanoseconds. + Weight::from_ref_time(1_166_332) .saturating_add(Weight::from_proof_size(0)) - // Standard Error: 393 - .saturating_add(Weight::from_ref_time(884_886).saturating_mul(r.into())) + // Standard Error: 384 + .saturating_add(Weight::from_ref_time(885_584).saturating_mul(r.into())) } /// The range of component `r` is `[0, 50]`. fn instr_i64divs(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 865 nanoseconds. - Weight::from_ref_time(2_862_483) + // Minimum execution time: 822 nanoseconds. + Weight::from_ref_time(1_155_826) .saturating_add(Weight::from_proof_size(0)) - // Standard Error: 16_413 - .saturating_add(Weight::from_ref_time(1_481_623).saturating_mul(r.into())) + // Standard Error: 521 + .saturating_add(Weight::from_ref_time(1_520_958).saturating_mul(r.into())) } /// The range of component `r` is `[0, 50]`. fn instr_i64divu(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_443 nanoseconds. - Weight::from_ref_time(1_086_902) + // Minimum execution time: 881 nanoseconds. + Weight::from_ref_time(1_158_125) .saturating_add(Weight::from_proof_size(0)) - // Standard Error: 2_245 - .saturating_add(Weight::from_ref_time(1_463_145).saturating_mul(r.into())) + // Standard Error: 624 + .saturating_add(Weight::from_ref_time(1_458_378).saturating_mul(r.into())) } /// The range of component `r` is `[0, 50]`. fn instr_i64rems(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 878 nanoseconds. - Weight::from_ref_time(1_130_306) + // Minimum execution time: 827 nanoseconds. + Weight::from_ref_time(1_209_535) .saturating_add(Weight::from_proof_size(0)) - // Standard Error: 475 - .saturating_add(Weight::from_ref_time(1_522_011).saturating_mul(r.into())) + // Standard Error: 399 + .saturating_add(Weight::from_ref_time(1_547_640).saturating_mul(r.into())) } /// The range of component `r` is `[0, 50]`. fn instr_i64remu(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 827 nanoseconds. - Weight::from_ref_time(1_197_053) + // Minimum execution time: 801 nanoseconds. + Weight::from_ref_time(1_313_872) .saturating_add(Weight::from_proof_size(0)) - // Standard Error: 474 - .saturating_add(Weight::from_ref_time(1_449_062).saturating_mul(r.into())) + // Standard Error: 2_175 + .saturating_add(Weight::from_ref_time(1_449_416).saturating_mul(r.into())) } /// The range of component `r` is `[0, 50]`. fn instr_i64and(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 847 nanoseconds. - Weight::from_ref_time(1_152_423) + // Minimum execution time: 827 nanoseconds. + Weight::from_ref_time(1_093_874) .saturating_add(Weight::from_proof_size(0)) - // Standard Error: 296 - .saturating_add(Weight::from_ref_time(895_541).saturating_mul(r.into())) + // Standard Error: 708 + .saturating_add(Weight::from_ref_time(901_450).saturating_mul(r.into())) } /// The range of component `r` is `[0, 50]`. fn instr_i64or(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 876 nanoseconds. - Weight::from_ref_time(1_169_485) + // Minimum execution time: 824 nanoseconds. + Weight::from_ref_time(1_164_076) .saturating_add(Weight::from_proof_size(0)) - // Standard Error: 571 - .saturating_add(Weight::from_ref_time(932_659).saturating_mul(r.into())) + // Standard Error: 623 + .saturating_add(Weight::from_ref_time(897_579).saturating_mul(r.into())) } /// The range of component `r` is `[0, 50]`. fn instr_i64xor(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 856 nanoseconds. - Weight::from_ref_time(1_155_127) + // Minimum execution time: 789 nanoseconds. + Weight::from_ref_time(1_113_915) .saturating_add(Weight::from_proof_size(0)) - // Standard Error: 256 - .saturating_add(Weight::from_ref_time(895_663).saturating_mul(r.into())) + // Standard Error: 390 + .saturating_add(Weight::from_ref_time(897_354).saturating_mul(r.into())) } /// The range of component `r` is `[0, 50]`. fn instr_i64shl(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 871 nanoseconds. - Weight::from_ref_time(1_139_722) + // Minimum execution time: 811 nanoseconds. + Weight::from_ref_time(1_117_366) .saturating_add(Weight::from_proof_size(0)) - // Standard Error: 367 - .saturating_add(Weight::from_ref_time(903_115).saturating_mul(r.into())) + // Standard Error: 437 + .saturating_add(Weight::from_ref_time(903_759).saturating_mul(r.into())) } /// The range of component `r` is `[0, 50]`. fn instr_i64shrs(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 853 nanoseconds. - Weight::from_ref_time(1_155_542) + // Minimum execution time: 846 nanoseconds. + Weight::from_ref_time(1_103_954) .saturating_add(Weight::from_proof_size(0)) - // Standard Error: 541 - .saturating_add(Weight::from_ref_time(901_635).saturating_mul(r.into())) + // Standard Error: 414 + .saturating_add(Weight::from_ref_time(903_429).saturating_mul(r.into())) } /// The range of component `r` is `[0, 50]`. fn instr_i64shru(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 866 nanoseconds. - Weight::from_ref_time(2_260_588) + // Minimum execution time: 803 nanoseconds. + Weight::from_ref_time(1_124_328) .saturating_add(Weight::from_proof_size(0)) - // Standard Error: 12_944 - .saturating_add(Weight::from_ref_time(877_790).saturating_mul(r.into())) + // Standard Error: 410 + .saturating_add(Weight::from_ref_time(903_216).saturating_mul(r.into())) } /// The range of component `r` is `[0, 50]`. fn instr_i64rotl(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 851 nanoseconds. - Weight::from_ref_time(1_627_816) + // Minimum execution time: 810 nanoseconds. + Weight::from_ref_time(1_131_433) .saturating_add(Weight::from_proof_size(0)) - // Standard Error: 4_004 - .saturating_add(Weight::from_ref_time(887_929).saturating_mul(r.into())) + // Standard Error: 402 + .saturating_add(Weight::from_ref_time(903_381).saturating_mul(r.into())) } /// The range of component `r` is `[0, 50]`. fn instr_i64rotr(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 874 nanoseconds. - Weight::from_ref_time(2_611_817) + // Minimum execution time: 807 nanoseconds. + Weight::from_ref_time(1_144_933) .saturating_add(Weight::from_proof_size(0)) - // Standard Error: 10_026 - .saturating_add(Weight::from_ref_time(862_138).saturating_mul(r.into())) + // Standard Error: 373 + .saturating_add(Weight::from_ref_time(902_918).saturating_mul(r.into())) } } diff --git a/primitives/weights/src/weight_v2.rs b/primitives/weights/src/weight_v2.rs index 3a15b1179..52e148f30 100644 --- a/primitives/weights/src/weight_v2.rs +++ b/primitives/weights/src/weight_v2.rs @@ -232,6 +232,38 @@ impl Weight { Some(Self { ref_time, proof_size }) } + /// Calculates how many `other` fit into `self`. + /// + /// Divides each component of `self` against the same component of `other`. Returns the minimum + /// of all those divisions. Returns `None` in case **all** components of `other` are zero. + /// + /// This returns `Some` even if some components of `other` are zero as long as there is at least + /// one non-zero component in `other`. The devision for this particular component will then + /// yield the maximum value (e.g u64::MAX). This is because we assume not every operation and + /// hence each `Weight` will necessarily use each resource. + pub const fn checked_div_per_component(self, other: &Self) -> Option { + let mut all_zero = true; + let ref_time = match self.ref_time.checked_div(other.ref_time) { + Some(ref_time) => { + all_zero = false; + ref_time + }, + None => u64::MAX, + }; + let proof_size = match self.proof_size.checked_div(other.proof_size) { + Some(proof_size) => { + all_zero = false; + proof_size + }, + None => u64::MAX, + }; + if all_zero { + None + } else { + Some(if ref_time < proof_size { ref_time } else { proof_size }) + } + } + /// Try to increase `self` by `amount` via checked addition. pub fn checked_accrue(&mut self, amount: Self) -> Option<()> { self.checked_add(&amount).map(|new_self| *self = new_self) @@ -582,4 +614,48 @@ mod tests { assert!(weight.checked_reduce(Weight::from_parts(0, 18)).is_some()); assert!(weight.is_zero()); } + + #[test] + fn checked_div_per_component_works() { + assert_eq!( + Weight::from_parts(10, 20).checked_div_per_component(&Weight::from_parts(2, 10)), + Some(2) + ); + assert_eq!( + Weight::from_parts(10, 200).checked_div_per_component(&Weight::from_parts(2, 10)), + Some(5) + ); + assert_eq!( + Weight::from_parts(10, 200).checked_div_per_component(&Weight::from_parts(1, 10)), + Some(10) + ); + assert_eq!( + Weight::from_parts(10, 200).checked_div_per_component(&Weight::from_parts(2, 1)), + Some(5) + ); + assert_eq!( + Weight::from_parts(10, 200).checked_div_per_component(&Weight::from_parts(0, 10)), + Some(20) + ); + assert_eq!( + Weight::from_parts(10, 200).checked_div_per_component(&Weight::from_parts(1, 0)), + Some(10) + ); + assert_eq!( + Weight::from_parts(0, 200).checked_div_per_component(&Weight::from_parts(2, 3)), + Some(0) + ); + assert_eq!( + Weight::from_parts(10, 0).checked_div_per_component(&Weight::from_parts(2, 3)), + Some(0) + ); + assert_eq!( + Weight::from_parts(10, 200).checked_div_per_component(&Weight::from_parts(0, 0)), + None, + ); + assert_eq!( + Weight::from_parts(0, 0).checked_div_per_component(&Weight::from_parts(0, 0)), + None, + ); + } } From 528e71fabf4cbf329cea6377efd1f24e3e3d4da8 Mon Sep 17 00:00:00 2001 From: Alexander Samusev <41779041+alvicsam@users.noreply.github.com> Date: Thu, 16 Feb 2023 12:09:32 +0100 Subject: [PATCH 124/558] [ci] Move publish-crates-locally to publish stage (#13395) --- scripts/ci/gitlab/pipeline/publish.yml | 18 ++++++++++++++++++ scripts/ci/gitlab/pipeline/test.yml | 18 ------------------ 2 files changed, 18 insertions(+), 18 deletions(-) diff --git a/scripts/ci/gitlab/pipeline/publish.yml b/scripts/ci/gitlab/pipeline/publish.yml index 9f0759036..3052658cd 100644 --- a/scripts/ci/gitlab/pipeline/publish.yml +++ b/scripts/ci/gitlab/pipeline/publish.yml @@ -238,3 +238,21 @@ publish-crates-manual: extends: .publish-crates-template when: manual interruptible: false + +publish-crates-locally: + stage: publish + extends: + - .test-refs + - .crates-publishing-template + # When lots of crates are taken into account (for example on master where all crates are tested) + # the job might take a long time, as evidenced by: + # https://gitlab.parity.io/parity/mirrors/substrate/-/jobs/2269364 + timeout: 4h + script: + - rusty-cachier snapshot create + - git clone + --depth 1 + --branch "$RELENG_SCRIPTS_BRANCH" + https://github.com/paritytech/releng-scripts.git + - CRATESIO_TARGET_INSTANCE=local ./releng-scripts/publish-crates + - rusty-cachier cache upload diff --git a/scripts/ci/gitlab/pipeline/test.yml b/scripts/ci/gitlab/pipeline/test.yml index 02e05752f..732b8c0fd 100644 --- a/scripts/ci/gitlab/pipeline/test.yml +++ b/scripts/ci/gitlab/pipeline/test.yml @@ -416,24 +416,6 @@ cargo-check-each-crate: - if [ "$CI_NODE_INDEX" == 1 ]; then rusty-cachier cache upload; fi parallel: 2 -publish-crates-locally: - stage: test - extends: - - .test-refs - - .crates-publishing-template - # When lots of crates are taken into account (for example on master where all crates are tested) - # the job might take a long time, as evidenced by: - # https://gitlab.parity.io/parity/mirrors/substrate/-/jobs/2269364 - timeout: 4h - script: - - rusty-cachier snapshot create - - git clone - --depth 1 - --branch "$RELENG_SCRIPTS_BRANCH" - https://github.com/paritytech/releng-scripts.git - - CRATESIO_TARGET_INSTANCE=local ./releng-scripts/publish-crates - - rusty-cachier cache upload - cargo-check-each-crate-macos: stage: test extends: From c24969ab6d6dcbf60564fedeeaf035792c57e53a Mon Sep 17 00:00:00 2001 From: Alexander Samusev <41779041+alvicsam@users.noreply.github.com> Date: Thu, 16 Feb 2023 13:10:40 +0100 Subject: [PATCH 125/558] [ci] Add dependencies for publish-crates (#13397) * [ci] Add dependencies for publishsing crates * apply review comments * fix needs --- scripts/ci/gitlab/pipeline/publish.yml | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/scripts/ci/gitlab/pipeline/publish.yml b/scripts/ci/gitlab/pipeline/publish.yml index 3052658cd..5b0671116 100644 --- a/scripts/ci/gitlab/pipeline/publish.yml +++ b/scripts/ci/gitlab/pipeline/publish.yml @@ -233,13 +233,17 @@ publish-draft-release: publish-crates: extends: .publish-crates-template + # publish-crates should only be run if publish-crates-locally passes + needs: + - job: check-crate-publishing + artifacts: false publish-crates-manual: extends: .publish-crates-template when: manual interruptible: false -publish-crates-locally: +check-crate-publishing: stage: publish extends: - .test-refs From 6a36704c40738bc0cdb59761f871aba9918651ab Mon Sep 17 00:00:00 2001 From: Sergejs Kostjucenko <85877331+sergejparity@users.noreply.github.com> Date: Thu, 16 Feb 2023 14:37:59 +0200 Subject: [PATCH 126/558] fix image name (#13398) --- scripts/ci/gitlab/pipeline/publish.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/ci/gitlab/pipeline/publish.yml b/scripts/ci/gitlab/pipeline/publish.yml index 5b0671116..557a7f212 100644 --- a/scripts/ci/gitlab/pipeline/publish.yml +++ b/scripts/ci/gitlab/pipeline/publish.yml @@ -55,7 +55,7 @@ - .kubernetes-env - .publish-refs variables: - CI_IMAGE: paritytech/docker-description + CI_IMAGE: paritytech/dockerhub-description DOCKERHUB_REPOSITORY: parity/$PRODUCT DOCKER_USERNAME: $Docker_Hub_User_Parity DOCKER_PASSWORD: $Docker_Hub_Pass_Parity From 46c1bd5b77f8dc7ed68fa021255985f17194fe7c Mon Sep 17 00:00:00 2001 From: dharjeezy Date: Thu, 16 Feb 2023 13:45:57 +0100 Subject: [PATCH 127/558] client/beefy: add more metrics for production visibility (#12910) * few beefy metrics * more beefy metrics * some beefy metrics * some beefy metrics * more metrics * other metrics * fix tests * merge changes * Apply suggestions from code review * client/beefy: fix metrics * client/beefy: separate metrics per component, avoid double registering * client/beefy: deduplicate metrics registration code * remove unused metric * impl review suggestions --------- Co-authored-by: Adrian Catangiu --- .../incoming_requests_handler.rs | 20 +- .../outgoing_requests_engine.rs | 11 + client/beefy/src/import.rs | 10 +- client/beefy/src/lib.rs | 30 +-- client/beefy/src/metrics.rs | 249 +++++++++++++++++- client/beefy/src/tests.rs | 3 +- client/beefy/src/worker.rs | 42 ++- 7 files changed, 316 insertions(+), 49 deletions(-) diff --git a/client/beefy/src/communication/request_response/incoming_requests_handler.rs b/client/beefy/src/communication/request_response/incoming_requests_handler.rs index d3be73dc0..854ece11c 100644 --- a/client/beefy/src/communication/request_response/incoming_requests_handler.rs +++ b/client/beefy/src/communication/request_response/incoming_requests_handler.rs @@ -29,8 +29,13 @@ use sc_network_common::protocol::ProtocolName; use sp_runtime::traits::Block; use std::{marker::PhantomData, sync::Arc}; -use crate::communication::request_response::{ - on_demand_justifications_protocol_config, Error, JustificationRequest, BEEFY_SYNC_LOG_TARGET, +use crate::{ + communication::request_response::{ + on_demand_justifications_protocol_config, Error, JustificationRequest, + BEEFY_SYNC_LOG_TARGET, + }, + metric_inc, + metrics::{register_metrics, OnDemandIncomingRequestsMetrics}, }; /// A request coming in, including a sender for sending responses. @@ -119,6 +124,7 @@ pub struct BeefyJustifsRequestHandler { pub(crate) request_receiver: IncomingRequestReceiver, pub(crate) justif_protocol_name: ProtocolName, pub(crate) client: Arc, + pub(crate) metrics: Option, pub(crate) _block: PhantomData, } @@ -132,12 +138,16 @@ where genesis_hash: Hash, fork_id: Option<&str>, client: Arc, + prometheus_registry: Option, ) -> (Self, RequestResponseConfig) { let (request_receiver, config) = on_demand_justifications_protocol_config(genesis_hash, fork_id); let justif_protocol_name = config.name.clone(); - - (Self { request_receiver, justif_protocol_name, client, _block: PhantomData }, config) + let metrics = register_metrics(prometheus_registry); + ( + Self { request_receiver, justif_protocol_name, client, metrics, _block: PhantomData }, + config, + ) } /// Network request-response protocol name used by this handler. @@ -180,12 +190,14 @@ where let peer = request.peer; match self.handle_request(request) { Ok(()) => { + metric_inc!(self, beefy_successful_justification_responses); debug!( target: BEEFY_SYNC_LOG_TARGET, "🥩 Handled BEEFY justification request from {:?}.", peer ) }, Err(e) => { + metric_inc!(self, beefy_failed_justification_responses); // TODO (issue #12293): apply reputation changes here based on error type. debug!( target: BEEFY_SYNC_LOG_TARGET, diff --git a/client/beefy/src/communication/request_response/outgoing_requests_engine.rs b/client/beefy/src/communication/request_response/outgoing_requests_engine.rs index 766480f78..3e0d6e47d 100644 --- a/client/beefy/src/communication/request_response/outgoing_requests_engine.rs +++ b/client/beefy/src/communication/request_response/outgoing_requests_engine.rs @@ -34,6 +34,8 @@ use std::{collections::VecDeque, result::Result, sync::Arc}; use crate::{ communication::request_response::{Error, JustificationRequest, BEEFY_SYNC_LOG_TARGET}, justification::{decode_and_verify_finality_proof, BeefyVersionedFinalityProof}, + metric_inc, + metrics::{register_metrics, OnDemandOutgoingRequestsMetrics}, KnownPeers, }; @@ -61,6 +63,7 @@ pub struct OnDemandJustificationsEngine { peers_cache: VecDeque, state: State, + metrics: Option, } impl OnDemandJustificationsEngine { @@ -68,13 +71,16 @@ impl OnDemandJustificationsEngine { network: Arc, protocol_name: ProtocolName, live_peers: Arc>>, + prometheus_registry: Option, ) -> Self { + let metrics = register_metrics(prometheus_registry); Self { network, protocol_name, live_peers, peers_cache: VecDeque::new(), state: State::Idle, + metrics, } } @@ -130,6 +136,7 @@ impl OnDemandJustificationsEngine { if let Some(peer) = self.try_next_peer() { self.request_from_peer(peer, RequestInfo { block, active_set }); } else { + metric_inc!(self, beefy_on_demand_justification_no_peer_to_request_from); debug!( target: BEEFY_SYNC_LOG_TARGET, "🥩 no good peers to request justif #{:?} from", block @@ -159,6 +166,7 @@ impl OnDemandJustificationsEngine { ) -> Result, Error> { response .map_err(|e| { + metric_inc!(self, beefy_on_demand_justification_peer_hang_up); debug!( target: BEEFY_SYNC_LOG_TARGET, "🥩 for on demand justification #{:?}, peer {:?} hung up: {:?}", @@ -169,6 +177,7 @@ impl OnDemandJustificationsEngine { Error::InvalidResponse })? .map_err(|e| { + metric_inc!(self, beefy_on_demand_justification_peer_error); debug!( target: BEEFY_SYNC_LOG_TARGET, "🥩 for on demand justification #{:?}, peer {:?} error: {:?}", @@ -185,6 +194,7 @@ impl OnDemandJustificationsEngine { &req_info.active_set, ) .map_err(|e| { + metric_inc!(self, beefy_on_demand_justification_invalid_proof); debug!( target: BEEFY_SYNC_LOG_TARGET, "🥩 for on demand justification #{:?}, peer {:?} responded with invalid proof: {:?}", @@ -224,6 +234,7 @@ impl OnDemandJustificationsEngine { } }) .map(|proof| { + metric_inc!(self, beefy_on_demand_justification_good_proof); debug!( target: BEEFY_SYNC_LOG_TARGET, "🥩 received valid on-demand justif #{:?} from {:?}", block, peer diff --git a/client/beefy/src/import.rs b/client/beefy/src/import.rs index f0fe3bfa0..1b5dda379 100644 --- a/client/beefy/src/import.rs +++ b/client/beefy/src/import.rs @@ -35,6 +35,8 @@ use sc_consensus::{BlockCheckParams, BlockImport, BlockImportParams, ImportResul use crate::{ communication::notification::BeefyVersionedFinalityProofSender, justification::{decode_and_verify_finality_proof, BeefyVersionedFinalityProof}, + metric_inc, + metrics::BlockImportMetrics, LOG_TARGET, }; @@ -49,6 +51,7 @@ pub struct BeefyBlockImport { runtime: Arc, inner: I, justification_sender: BeefyVersionedFinalityProofSender, + metrics: Option, } impl Clone for BeefyBlockImport { @@ -58,6 +61,7 @@ impl Clone for BeefyBlockImport BeefyBlockImport { runtime: Arc, inner: I, justification_sender: BeefyVersionedFinalityProofSender, + metrics: Option, ) -> BeefyBlockImport { - BeefyBlockImport { backend, runtime, inner, justification_sender } + BeefyBlockImport { backend, runtime, inner, justification_sender, metrics } } } @@ -147,6 +152,8 @@ where self.justification_sender .notify(|| Ok::<_, ()>(proof)) .expect("forwards closure result; the closure always returns Ok; qed."); + + metric_inc!(self, beefy_good_justification_imports); } else { debug!( target: LOG_TARGET, @@ -154,6 +161,7 @@ where encoded, number, ); + metric_inc!(self, beefy_bad_justification_imports); } }, _ => (), diff --git a/client/beefy/src/lib.rs b/client/beefy/src/lib.rs index d7de7295a..5f74b052e 100644 --- a/client/beefy/src/lib.rs +++ b/client/beefy/src/lib.rs @@ -28,6 +28,7 @@ use crate::{ }, }, import::BeefyBlockImport, + metrics::register_metrics, round::Rounds, worker::PersistedState, }; @@ -36,7 +37,7 @@ use beefy_primitives::{ GENESIS_AUTHORITY_SET_ID, }; use futures::{stream::Fuse, StreamExt}; -use log::{debug, error, info}; +use log::{error, info}; use parking_lot::Mutex; use prometheus::Registry; use sc_client_api::{Backend, BlockBackend, BlockchainEvents, FinalityNotifications, Finalizer}; @@ -133,6 +134,7 @@ pub fn beefy_block_import_and_links( wrapped_block_import: I, backend: Arc, runtime: Arc, + prometheus_registry: Option, ) -> (BeefyBlockImport, BeefyVoterLinks, BeefyRPCLinks) where B: Block, @@ -152,10 +154,16 @@ where // BlockImport -> Voter links let (to_voter_justif_sender, from_block_import_justif_stream) = BeefyVersionedFinalityProofStream::::channel(); + let metrics = register_metrics(prometheus_registry); // BlockImport - let import = - BeefyBlockImport::new(backend, runtime, wrapped_block_import, to_voter_justif_sender); + let import = BeefyBlockImport::new( + backend, + runtime, + wrapped_block_import, + to_voter_justif_sender, + metrics, + ); let voter_links = BeefyVoterLinks { from_block_import_justif_stream, to_rpc_justif_sender, @@ -242,28 +250,16 @@ where gossip_validator.clone(), None, ); + let metrics = register_metrics(prometheus_registry.clone()); // The `GossipValidator` adds and removes known peers based on valid votes and network events. let on_demand_justifications = OnDemandJustificationsEngine::new( network.clone(), justifications_protocol_name, known_peers, + prometheus_registry.clone(), ); - let metrics = - prometheus_registry.as_ref().map(metrics::Metrics::register).and_then( - |result| match result { - Ok(metrics) => { - debug!(target: LOG_TARGET, "🥩 Registered metrics"); - Some(metrics) - }, - Err(err) => { - debug!(target: LOG_TARGET, "🥩 Failed to register metrics: {:?}", err); - None - }, - }, - ); - // Subscribe to finality notifications and justifications before waiting for runtime pallet and // reuse the streams, so we don't miss notifications while waiting for pallet to be available. let mut finality_notifications = client.finality_notification_stream().fuse(); diff --git a/client/beefy/src/metrics.rs b/client/beefy/src/metrics.rs index 55fdecc36..e3b48e6ec 100644 --- a/client/beefy/src/metrics.rs +++ b/client/beefy/src/metrics.rs @@ -18,16 +18,22 @@ //! BEEFY Prometheus metrics definition +use log::debug; use prometheus::{register, Counter, Gauge, PrometheusError, Registry, U64}; -/// BEEFY metrics exposed through Prometheus -pub(crate) struct Metrics { +/// Helper trait for registering BEEFY metrics to Prometheus registry. +pub(crate) trait PrometheusRegister: Sized { + const DESCRIPTION: &'static str; + fn register(registry: &Registry) -> Result; +} + +/// BEEFY voting-related metrics exposed through Prometheus +#[derive(Clone, Debug)] +pub struct VoterMetrics { /// Current active validator set id pub beefy_validator_set_id: Gauge, /// Total number of votes sent by this node pub beefy_votes_sent: Counter, - /// Most recent concluded voting round - pub beefy_round_concluded: Gauge, /// Best block finalized by BEEFY pub beefy_best_block: Gauge, /// Best block BEEFY voted on @@ -36,10 +42,31 @@ pub(crate) struct Metrics { pub beefy_should_vote_on: Gauge, /// Number of sessions with lagging signed commitment on mandatory block pub beefy_lagging_sessions: Counter, + /// Number of times no Authority public key found in store + pub beefy_no_authority_found_in_store: Counter, + /// Number of currently buffered votes + pub beefy_buffered_votes: Gauge, + /// Number of valid but stale votes received + pub beefy_stale_votes: Counter, + /// Number of votes dropped due to full buffers + pub beefy_buffered_votes_dropped: Counter, + /// Number of currently buffered justifications + pub beefy_buffered_justifications: Gauge, + /// Number of valid but stale justifications received + pub beefy_stale_justifications: Counter, + /// Number of valid justifications successfully imported + pub beefy_imported_justifications: Counter, + /// Number of justifications dropped due to full buffers + pub beefy_buffered_justifications_dropped: Counter, + /// Trying to set Best Beefy block to old block + pub beefy_best_block_set_last_failure: Gauge, + /// Number of Successful handled votes + pub beefy_successful_handled_votes: Counter, } -impl Metrics { - pub(crate) fn register(registry: &Registry) -> Result { +impl PrometheusRegister for VoterMetrics { + const DESCRIPTION: &'static str = "voter"; + fn register(registry: &Registry) -> Result { Ok(Self { beefy_validator_set_id: register( Gauge::new( @@ -52,13 +79,6 @@ impl Metrics { Counter::new("substrate_beefy_votes_sent", "Number of votes sent by this node")?, registry, )?, - beefy_round_concluded: register( - Gauge::new( - "substrate_beefy_round_concluded", - "Voting round, that has been concluded", - )?, - registry, - )?, beefy_best_block: register( Gauge::new("substrate_beefy_best_block", "Best block finalized by BEEFY")?, registry, @@ -78,10 +98,212 @@ impl Metrics { )?, registry, )?, + beefy_no_authority_found_in_store: register( + Counter::new( + "substrate_beefy_no_authority_found_in_store", + "Number of times no Authority public key found in store", + )?, + registry, + )?, + beefy_buffered_votes: register( + Gauge::new("substrate_beefy_buffered_votes", "Number of currently buffered votes")?, + registry, + )?, + beefy_stale_votes: register( + Counter::new( + "substrate_beefy_stale_votes", + "Number of valid but stale votes received", + )?, + registry, + )?, + beefy_buffered_votes_dropped: register( + Counter::new( + "substrate_beefy_buffered_votes_dropped", + "Number of votes dropped due to full buffers", + )?, + registry, + )?, + beefy_buffered_justifications: register( + Gauge::new( + "substrate_beefy_buffered_justifications", + "Number of currently buffered justifications", + )?, + registry, + )?, + beefy_stale_justifications: register( + Counter::new( + "substrate_beefy_stale_justifications", + "Number of valid but stale justifications received", + )?, + registry, + )?, + beefy_imported_justifications: register( + Counter::new( + "substrate_beefy_imported_justifications", + "Number of valid justifications successfully imported", + )?, + registry, + )?, + beefy_buffered_justifications_dropped: register( + Counter::new( + "substrate_beefy_buffered_justifications_dropped", + "Number of justifications dropped due to full buffers", + )?, + registry, + )?, + beefy_best_block_set_last_failure: register( + Gauge::new( + "substrate_beefy_best_block_to_old_block", + "Trying to set Best Beefy block to old block", + )?, + registry, + )?, + beefy_successful_handled_votes: register( + Counter::new( + "substrate_beefy_successful_handled_votes", + "Number of Successful handled votes", + )?, + registry, + )?, }) } } +/// BEEFY block-import-related metrics exposed through Prometheus +#[derive(Clone, Debug)] +pub struct BlockImportMetrics { + /// Number of Good Justification imports + pub beefy_good_justification_imports: Counter, + /// Number of Bad Justification imports + pub beefy_bad_justification_imports: Counter, +} + +impl PrometheusRegister for BlockImportMetrics { + const DESCRIPTION: &'static str = "block-import"; + fn register(registry: &Registry) -> Result { + Ok(Self { + beefy_good_justification_imports: register( + Counter::new( + "substrate_beefy_good_justification_imports", + "Number of Good Justification imports", + )?, + registry, + )?, + beefy_bad_justification_imports: register( + Counter::new( + "substrate_beefy_bad_justification_imports", + "Number of Bad Justification imports", + )?, + registry, + )?, + }) + } +} + +/// BEEFY on-demand-justifications-related metrics exposed through Prometheus +#[derive(Clone, Debug)] +pub struct OnDemandIncomingRequestsMetrics { + /// Number of Successful Justification responses + pub beefy_successful_justification_responses: Counter, + /// Number of Failed Justification responses + pub beefy_failed_justification_responses: Counter, +} + +impl PrometheusRegister for OnDemandIncomingRequestsMetrics { + const DESCRIPTION: &'static str = "on-demand incoming justification requests"; + fn register(registry: &Registry) -> Result { + Ok(Self { + beefy_successful_justification_responses: register( + Counter::new( + "substrate_beefy_successful_justification_responses", + "Number of Successful Justification responses", + )?, + registry, + )?, + beefy_failed_justification_responses: register( + Counter::new( + "substrate_beefy_failed_justification_responses", + "Number of Failed Justification responses", + )?, + registry, + )?, + }) + } +} + +/// BEEFY on-demand-justifications-related metrics exposed through Prometheus +#[derive(Clone, Debug)] +pub struct OnDemandOutgoingRequestsMetrics { + /// Number of times there was no good peer to request justification from + pub beefy_on_demand_justification_no_peer_to_request_from: Counter, + /// Number of on-demand justification peer hang up + pub beefy_on_demand_justification_peer_hang_up: Counter, + /// Number of on-demand justification peer error + pub beefy_on_demand_justification_peer_error: Counter, + /// Number of on-demand justification invalid proof + pub beefy_on_demand_justification_invalid_proof: Counter, + /// Number of on-demand justification good proof + pub beefy_on_demand_justification_good_proof: Counter, +} + +impl PrometheusRegister for OnDemandOutgoingRequestsMetrics { + const DESCRIPTION: &'static str = "on-demand outgoing justification requests"; + fn register(registry: &Registry) -> Result { + Ok(Self { + beefy_on_demand_justification_no_peer_to_request_from: register( + Counter::new( + "substrate_beefy_on_demand_justification_no_peer_to_request_from", + "Number of times there was no good peer to request justification from", + )?, + registry, + )?, + beefy_on_demand_justification_peer_hang_up: register( + Counter::new( + "substrate_beefy_on_demand_justification_peer_hang_up", + "Number of on-demand justification peer hang up", + )?, + registry, + )?, + beefy_on_demand_justification_peer_error: register( + Counter::new( + "substrate_beefy_on_demand_justification_peer_error", + "Number of on-demand justification peer error", + )?, + registry, + )?, + beefy_on_demand_justification_invalid_proof: register( + Counter::new( + "substrate_beefy_on_demand_justification_invalid_proof", + "Number of on-demand justification invalid proof", + )?, + registry, + )?, + beefy_on_demand_justification_good_proof: register( + Counter::new( + "substrate_beefy_on_demand_justification_good_proof", + "Number of on-demand justification good proof", + )?, + registry, + )?, + }) + } +} + +pub(crate) fn register_metrics( + prometheus_registry: Option, +) -> Option { + prometheus_registry.as_ref().map(T::register).and_then(|result| match result { + Ok(metrics) => { + debug!(target: "beefy", "🥩 Registered {} metrics", T::DESCRIPTION); + Some(metrics) + }, + Err(err) => { + debug!(target: "beefy", "🥩 Failed to register {} metrics: {:?}", T::DESCRIPTION, err); + None + }, + }) +} + // Note: we use the `format` macro to convert an expr into a `u64`. This will fail, // if expr does not derive `Display`. #[macro_export] @@ -104,7 +326,6 @@ macro_rules! metric_inc { }}; } -#[cfg(test)] #[macro_export] macro_rules! metric_get { ($self:ident, $m:ident) => {{ diff --git a/client/beefy/src/tests.rs b/client/beefy/src/tests.rs index 932897c77..007c57e42 100644 --- a/client/beefy/src/tests.rs +++ b/client/beefy/src/tests.rs @@ -124,6 +124,7 @@ impl BeefyTestNet { justif_protocol_name, client, _block: PhantomData, + metrics: None, }; *net.peers[i].data.beefy_justif_req_handler.lock() = Some(justif_handler); } @@ -203,7 +204,7 @@ impl TestNetFactory for BeefyTestNet { let api = Arc::new(TestApi::with_validator_set(&validator_set)); let inner = BlockImportAdapter::new(client.clone()); let (block_import, voter_links, rpc_links) = - beefy_block_import_and_links(inner, client.as_backend(), api); + beefy_block_import_and_links(inner, client.as_backend(), api, None); let peer_data = PeerData { beefy_rpc_links: Mutex::new(Some(rpc_links)), beefy_voter_links: Mutex::new(Some(voter_links)), diff --git a/client/beefy/src/worker.rs b/client/beefy/src/worker.rs index f367c8b46..6528aef12 100644 --- a/client/beefy/src/worker.rs +++ b/client/beefy/src/worker.rs @@ -24,8 +24,8 @@ use crate::{ error::Error, justification::BeefyVersionedFinalityProof, keystore::BeefyKeystore, - metric_inc, metric_set, - metrics::Metrics, + metric_get, metric_inc, metric_set, + metrics::VoterMetrics, round::{Rounds, VoteImportResult}, BeefyVoterLinks, LOG_TARGET, }; @@ -252,7 +252,7 @@ pub(crate) struct WorkerParams { pub gossip_validator: Arc>, pub on_demand_justifications: OnDemandJustificationsEngine, pub links: BeefyVoterLinks, - pub metrics: Option, + pub metrics: Option, pub persisted_state: PersistedState, } @@ -312,7 +312,7 @@ pub(crate) struct BeefyWorker { // voter state /// BEEFY client metrics. - metrics: Option, + metrics: Option, /// Buffer holding votes for future processing. pending_votes: BTreeMap< NumberFor, @@ -407,6 +407,7 @@ where if store.intersection(&active).count() == 0 { let msg = "no authority public key found in store".to_string(); debug!(target: LOG_TARGET, "🥩 for block {:?} {}", block, msg); + metric_inc!(self, beefy_no_authority_found_in_store); Err(Error::Keystore(msg)) } else { Ok(()) @@ -494,17 +495,21 @@ where debug!(target: LOG_TARGET, "🥩 Buffer vote for round: {:?}.", block_num); if self.pending_votes.len() < MAX_BUFFERED_VOTE_ROUNDS { let votes_vec = self.pending_votes.entry(block_num).or_default(); - if votes_vec.try_push(vote).is_err() { + if votes_vec.try_push(vote).is_ok() { + metric_inc!(self, beefy_buffered_votes); + } else { warn!( target: LOG_TARGET, "🥩 Buffer vote dropped for round: {:?}", block_num - ) + ); + metric_inc!(self, beefy_buffered_votes_dropped); } } else { warn!(target: LOG_TARGET, "🥩 Buffer vote dropped for round: {:?}.", block_num); + metric_inc!(self, beefy_buffered_votes_dropped); } }, - RoundAction::Drop => (), + RoundAction::Drop => metric_inc!(self, beefy_stale_votes), }; Ok(()) } @@ -524,20 +529,23 @@ where match self.voting_oracle().triage_round(block_num, best_grandpa)? { RoundAction::Process => { debug!(target: LOG_TARGET, "🥩 Process justification for round: {:?}.", block_num); + metric_inc!(self, beefy_imported_justifications); self.finalize(justification)? }, RoundAction::Enqueue => { debug!(target: LOG_TARGET, "🥩 Buffer justification for round: {:?}.", block_num); if self.pending_justifications.len() < MAX_BUFFERED_JUSTIFICATIONS { self.pending_justifications.entry(block_num).or_insert(justification); + metric_inc!(self, beefy_buffered_justifications); } else { + metric_inc!(self, beefy_buffered_justifications_dropped); warn!( target: LOG_TARGET, "🥩 Buffer justification dropped for round: {:?}.", block_num ); } }, - RoundAction::Drop => (), + RoundAction::Drop => metric_inc!(self, beefy_stale_justifications), }; Ok(()) } @@ -555,8 +563,6 @@ where let block_number = vote.commitment.block_number; match rounds.add_vote(vote) { VoteImportResult::RoundConcluded(signed_commitment) => { - metric_set!(self, beefy_round_concluded, block_number); - let finality_proof = VersionedFinalityProof::V1(signed_commitment); info!( target: LOG_TARGET, @@ -584,6 +590,7 @@ where }, VoteImportResult::Invalid | VoteImportResult::Stale => (), }; + metric_inc!(self, beefy_successful_handled_votes); Ok(()) } @@ -637,7 +644,8 @@ where .notify(|| Ok::<_, ()>(finality_proof)) .expect("forwards closure result; the closure always returns Ok; qed."); } else { - debug!(target: LOG_TARGET, "🥩 Can't set best beefy to older: {}", block_num); + debug!(target: LOG_TARGET, "🥩 Can't set best beefy to old: {}", block_num); + metric_set!(self, beefy_best_block_set_last_failure, block_num); } Ok(()) } @@ -669,25 +677,33 @@ where let justifs_to_handle = to_process_for(&mut self.pending_justifications, interval, _ph); for (num, justification) in justifs_to_handle.into_iter() { debug!(target: LOG_TARGET, "🥩 Handle buffered justification for: {:?}.", num); + metric_inc!(self, beefy_imported_justifications); if let Err(err) = self.finalize(justification) { error!(target: LOG_TARGET, "🥩 Error finalizing block: {}", err); } } + metric_set!(self, beefy_buffered_justifications, self.pending_justifications.len()); // Possibly new interval after processing justifications. interval = self.voting_oracle().accepted_interval(best_grandpa)?; } // Process pending votes. if !self.pending_votes.is_empty() { + let mut processed = 0u64; let votes_to_handle = to_process_for(&mut self.pending_votes, interval, _ph); for (num, votes) in votes_to_handle.into_iter() { debug!(target: LOG_TARGET, "🥩 Handle buffered votes for: {:?}.", num); + processed += votes.len() as u64; for v in votes.into_iter() { if let Err(err) = self.handle_vote(v) { error!(target: LOG_TARGET, "🥩 Error handling buffered vote: {}", err); }; } } + if let Some(previous) = metric_get!(self, beefy_buffered_votes) { + previous.sub(processed); + metric_set!(self, beefy_buffered_votes, previous.get()); + } } Ok(()) } @@ -1053,10 +1069,12 @@ pub(crate) mod tests { let gossip_validator = Arc::new(GossipValidator::new(known_peers.clone())); let gossip_engine = GossipEngine::new(network.clone(), "/beefy/1", gossip_validator.clone(), None); + let metrics = None; let on_demand_justifications = OnDemandJustificationsEngine::new( network.clone(), "/beefy/justifs/1".into(), known_peers, + None, ); let genesis_header = backend .blockchain() @@ -1077,7 +1095,7 @@ pub(crate) mod tests { links, gossip_engine, gossip_validator, - metrics: None, + metrics, network, on_demand_justifications, persisted_state, From e8178c98750f436d1ed9284f213793eae0967761 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alexander=20Thei=C3=9Fen?= Date: Thu, 16 Feb 2023 16:58:28 +0100 Subject: [PATCH 128/558] contracts: Don't rely on reserved balances keeping an account alive (#13369) * Move storage deposits to their own account * Take ed for contract's account from origin * Apply suggestions from code review Co-authored-by: Cyrill Leutwiler Co-authored-by: Sasha Gryaznov * Update stale docs * Use 16 bytes prefix for address derivation * Update frame/contracts/src/address.rs Co-authored-by: Cyrill Leutwiler * Fix merge * ".git/.scripts/commands/bench/bench.sh" pallet dev pallet_contracts * Update frame/contracts/primitives/src/lib.rs Co-authored-by: Sasha Gryaznov --------- Co-authored-by: Cyrill Leutwiler Co-authored-by: Sasha Gryaznov Co-authored-by: command-bot <> --- frame/contracts/fixtures/caller_contract.wat | 55 - frame/contracts/fixtures/drain.wat | 5 +- frame/contracts/primitives/src/lib.rs | 16 +- frame/contracts/src/address.rs | 81 + frame/contracts/src/benchmarking/mod.rs | 100 +- frame/contracts/src/exec.rs | 97 +- frame/contracts/src/lib.rs | 62 +- frame/contracts/src/storage.rs | 236 +- frame/contracts/src/storage/meter.rs | 193 +- frame/contracts/src/tests.rs | 496 ++- frame/contracts/src/weights.rs | 2930 +++++++++--------- 11 files changed, 2098 insertions(+), 2173 deletions(-) create mode 100644 frame/contracts/src/address.rs diff --git a/frame/contracts/fixtures/caller_contract.wat b/frame/contracts/fixtures/caller_contract.wat index 9c7cdf62a..f9caf49f2 100644 --- a/frame/contracts/fixtures/caller_contract.wat +++ b/frame/contracts/fixtures/caller_contract.wat @@ -16,27 +16,11 @@ ) ) - (func $current_balance (param $sp i32) (result i64) - (i32.store - (i32.sub (get_local $sp) (i32.const 16)) - (i32.const 8) - ) - (call $seal_balance - (i32.sub (get_local $sp) (i32.const 8)) - (i32.sub (get_local $sp) (i32.const 16)) - ) - (call $assert - (i32.eq (i32.load (i32.sub (get_local $sp) (i32.const 16))) (i32.const 8)) - ) - (i64.load (i32.sub (get_local $sp) (i32.const 8))) - ) - (func (export "deploy")) (func (export "call") (local $sp i32) (local $exit_code i32) - (local $balance i64) ;; Length of the buffer (i32.store (i32.const 20) (i32.const 32)) @@ -54,9 +38,6 @@ ;; Read current balance into local variable. (set_local $sp (i32.const 1024)) - (set_local $balance - (call $current_balance (get_local $sp)) - ) ;; Fail to deploy the contract since it returns a non-zero exit status. (set_local $exit_code @@ -82,11 +63,6 @@ (i32.eq (get_local $exit_code) (i32.const 2)) ;; ReturnCode::CalleeReverted ) - ;; Check that balance has not changed. - (call $assert - (i64.eq (get_local $balance) (call $current_balance (get_local $sp))) - ) - ;; Fail to deploy the contract due to insufficient gas. (set_local $exit_code (call $seal_instantiate @@ -112,11 +88,6 @@ (i32.eq (get_local $exit_code) (i32.const 1)) ;; ReturnCode::CalleeTrapped ) - ;; Check that balance has not changed. - (call $assert - (i64.eq (get_local $balance) (call $current_balance (get_local $sp))) - ) - ;; Length of the output buffer (i32.store (i32.sub (get_local $sp) (i32.const 4)) @@ -153,14 +124,6 @@ (i32.eq (i32.load (i32.sub (get_local $sp) (i32.const 4))) (i32.const 32)) ) - ;; Check that balance has been deducted. - (set_local $balance - (i64.sub (get_local $balance) (i64.load (i32.const 0))) - ) - (call $assert - (i64.eq (get_local $balance) (call $current_balance (get_local $sp))) - ) - ;; Zero out destination buffer of output (i32.store (i32.sub (get_local $sp) (i32.const 4)) @@ -204,11 +167,6 @@ ) ) - ;; Check that balance has not changed. - (call $assert - (i64.eq (get_local $balance) (call $current_balance (get_local $sp))) - ) - ;; Fail to call the contract due to insufficient gas. (set_local $exit_code (call $seal_call @@ -229,11 +187,6 @@ (i32.eq (get_local $exit_code) (i32.const 1)) ;; ReturnCode::CalleeTrapped ) - ;; Check that balance has not changed. - (call $assert - (i64.eq (get_local $balance) (call $current_balance (get_local $sp))) - ) - ;; Zero out destination buffer of output (i32.store (i32.sub (get_local $sp) (i32.const 4)) @@ -276,14 +229,6 @@ (i32.const 0x77665544) ) ) - - ;; Check that balance has been deducted. - (set_local $balance - (i64.sub (get_local $balance) (i64.load (i32.const 0))) - ) - (call $assert - (i64.eq (get_local $balance) (call $current_balance (get_local $sp))) - ) ) (data (i32.const 0) "\00\80") ;; The value to transfer on instantiation and calls. diff --git a/frame/contracts/fixtures/drain.wat b/frame/contracts/fixtures/drain.wat index 94c651842..9f126898f 100644 --- a/frame/contracts/fixtures/drain.wat +++ b/frame/contracts/fixtures/drain.wat @@ -34,8 +34,7 @@ ) ;; Try to self-destruct by sending full balance to the 0 address. - ;; All the *free* balance will be send away, which is a valid thing to do - ;; because the storage deposits will keep the account alive. + ;; The call will fail because a contract transfer has a keep alive requirement (call $assert (i32.eq (call $seal_transfer @@ -44,7 +43,7 @@ (i32.const 0) ;; Pointer to the buffer with value to transfer (i32.const 8) ;; Length of the buffer with value to transfer ) - (i32.const 0) ;; ReturnCode::Success + (i32.const 5) ;; ReturnCode::TransferFailed ) ) ) diff --git a/frame/contracts/primitives/src/lib.rs b/frame/contracts/primitives/src/lib.rs index 1811ae09c..f5b8da249 100644 --- a/frame/contracts/primitives/src/lib.rs +++ b/frame/contracts/primitives/src/lib.rs @@ -47,10 +47,12 @@ pub struct ContractResult { /// Additionally, any `seal_call` or `seal_instantiate` makes use of pre-charging /// when a non-zero `gas_limit` argument is supplied. pub gas_required: Weight, - /// How much balance was deposited and reserved during execution in order to pay for storage. + /// How much balance was paid by the origin into the contract's deposit account in order to + /// pay for storage. /// - /// The storage deposit is never actually charged from the caller in case of [`Self::result`] - /// is `Err`. This is because on error all storage changes are rolled back. + /// The storage deposit is never actually charged from the origin in case of [`Self::result`] + /// is `Err`. This is because on error all storage changes are rolled back including the + /// payment of the deposit. pub storage_deposit: StorageDeposit, /// An optional debug message. This message is only filled when explicitly requested /// by the code that calls into the contract. Otherwise it is empty. @@ -159,12 +161,12 @@ pub enum StorageDeposit { /// The transaction reduced storage consumption. /// /// This means that the specified amount of balance was transferred from the involved - /// contracts to the call origin. + /// deposit accounts to the origin. Refund(Balance), - /// The transaction increased overall storage usage. + /// The transaction increased storage consumption. /// - /// This means that the specified amount of balance was transferred from the call origin - /// to the contracts involved. + /// This means that the specified amount of balance was transferred from the origin + /// to the involved deposit accounts. Charge(Balance), } diff --git a/frame/contracts/src/address.rs b/frame/contracts/src/address.rs new file mode 100644 index 000000000..4b1bb1f48 --- /dev/null +++ b/frame/contracts/src/address.rs @@ -0,0 +1,81 @@ +// This file is part of Substrate. + +// Copyright (C) 2018-2022 Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +//! Functions that deal with address derivation. + +use crate::{CodeHash, Config}; +use codec::{Decode, Encode}; +use sp_runtime::traits::{Hash, TrailingZeroInput}; + +/// Provides the contract address generation method. +/// +/// See [`DefaultAddressGenerator`] for the default implementation. +/// +/// # Note for implementors +/// +/// 1. Make sure that there are no collisions, different inputs never lead to the same output. +/// 2. Make sure that the same inputs lead to the same output. +pub trait AddressGenerator { + /// The address of a contract based on the given instantiate parameters. + /// + /// Changing the formular for an already deployed chain is fine as long as no collisons + /// with the old formular. Changes only affect existing contracts. + fn contract_address( + deploying_address: &T::AccountId, + code_hash: &CodeHash, + input_data: &[u8], + salt: &[u8], + ) -> T::AccountId; + + /// The address of the deposit account of `contract_addr`. + /// + /// The address is generated once on instantiation and then stored in the contracts + /// metadata. Hence changes do only affect newly created contracts. + fn deposit_address(contract_addr: &T::AccountId) -> T::AccountId; +} + +/// Default address generator. +/// +/// This is the default address generator used by contract instantiation. Its result +/// is only dependent on its inputs. It can therefore be used to reliably predict the +/// address of a contract. This is akin to the formula of eth's CREATE2 opcode. There +/// is no CREATE equivalent because CREATE2 is strictly more powerful. +/// Formula: +/// `hash("contract_addr_v1" ++ deploying_address ++ code_hash ++ input_data ++ salt)` +pub struct DefaultAddressGenerator; + +impl AddressGenerator for DefaultAddressGenerator { + /// Formula: `hash("contract_addr_v1" ++ deploying_address ++ code_hash ++ input_data ++ salt)` + fn contract_address( + deploying_address: &T::AccountId, + code_hash: &CodeHash, + input_data: &[u8], + salt: &[u8], + ) -> T::AccountId { + let entropy = (b"contract_addr_v1", deploying_address, code_hash, input_data, salt) + .using_encoded(T::Hashing::hash); + Decode::decode(&mut TrailingZeroInput::new(entropy.as_ref())) + .expect("infinite length input; no invalid inputs for type; qed") + } + + /// Formula: `hash("contract_depo_v1" ++ contract_addr)` + fn deposit_address(contract_addr: &T::AccountId) -> T::AccountId { + let entropy = (b"contract_depo_v1", contract_addr).using_encoded(T::Hashing::hash); + Decode::decode(&mut TrailingZeroInput::new(entropy.as_ref())) + .expect("infinite length input; no invalid inputs for type; qed") + } +} diff --git a/frame/contracts/src/benchmarking/mod.rs b/frame/contracts/src/benchmarking/mod.rs index 55d315d5c..ed41cece1 100644 --- a/frame/contracts/src/benchmarking/mod.rs +++ b/frame/contracts/src/benchmarking/mod.rs @@ -32,7 +32,6 @@ use self::{ use crate::{ exec::{AccountIdOf, FixSizedKey, VarSizedKey}, schedule::{API_BENCHMARK_BATCH_SIZE, INSTR_BENCHMARK_BATCH_SIZE}, - storage::Storage, wasm::CallFlags, Pallet as Contracts, *, }; @@ -133,14 +132,8 @@ where fn store(&self, items: &Vec<(FixSizedKey, Vec)>) -> Result<(), &'static str> { let info = self.info()?; for item in items { - Storage::::write( - &info.trie_id, - &item.0 as &FixSizedKey, - Some(item.1.clone()), - None, - false, - ) - .map_err(|_| "Failed to write storage to restoration dest")?; + info.write(&item.0 as &FixSizedKey, Some(item.1.clone()), None, false) + .map_err(|_| "Failed to write storage to restoration dest")?; } >::insert(&self.account_id, info); Ok(()) @@ -207,7 +200,7 @@ benchmarks! { // The base weight consumed on processing contracts deletion queue. #[pov_mode = Measured] on_process_deletion_queue_batch {}: { - Storage::::process_deletion_queue_batch(Weight::MAX) + ContractInfo::::process_deletion_queue_batch(Weight::MAX) } #[skip_meta] @@ -215,9 +208,9 @@ benchmarks! { on_initialize_per_trie_key { let k in 0..1024; let instance = Contract::::with_storage(WasmModule::dummy(), k, T::Schedule::get().limits.payload_len)?; - Storage::::queue_trie_for_deletion(&instance.info()?)?; + instance.info()?.queue_trie_for_deletion()?; }: { - Storage::::process_deletion_queue_batch(Weight::MAX) + ContractInfo::::process_deletion_queue_batch(Weight::MAX) } #[pov_mode = Measured] @@ -225,11 +218,11 @@ benchmarks! { let q in 0..1024.min(T::DeletionQueueDepth::get()); for i in 0 .. q { let instance = Contract::::with_index(i, WasmModule::dummy(), vec![])?; - Storage::::queue_trie_for_deletion(&instance.info()?)?; + instance.info()?.queue_trie_for_deletion()?; ContractInfoOf::::remove(instance.account_id); } }: { - Storage::::process_deletion_queue_batch(Weight::MAX) + ContractInfo::::process_deletion_queue_batch(Weight::MAX) } // This benchmarks the additional weight that is charged when a contract is executed the @@ -293,18 +286,16 @@ benchmarks! { let addr = Contracts::::contract_address(&caller, &hash, &input, &salt); }: _(origin, value, Weight::MAX, None, code, input, salt) verify { - // the contract itself does not trigger any reserves - let deposit = T::Currency::reserved_balance(&addr); + let deposit_account = Contract::::address_info(&addr)?.deposit_account().clone(); + let deposit = T::Currency::free_balance(&deposit_account); // uploading the code reserves some balance in the callers account let code_deposit = T::Currency::reserved_balance(&caller); assert_eq!( T::Currency::free_balance(&caller), - caller_funding::() - value - deposit - code_deposit, + caller_funding::() - value - deposit - code_deposit - Pallet::::min_balance(), ); // contract has the full value - assert_eq!(T::Currency::free_balance(&addr), value); - // instantiate should leave a contract - Contract::::address_info(&addr)?; + assert_eq!(T::Currency::free_balance(&addr), value + Pallet::::min_balance()); } // Instantiate uses a dummy contract constructor to measure the overhead of the instantiate. @@ -325,14 +316,15 @@ benchmarks! { Contracts::::store_code_raw(code, caller.clone())?; }: _(origin, value, Weight::MAX, None, hash, input, salt) verify { - // the contract itself does not trigger any reserves - let deposit = T::Currency::reserved_balance(&addr); + let deposit_account = Contract::::address_info(&addr)?.deposit_account().clone(); + let deposit = T::Currency::free_balance(&deposit_account); // value was removed from the caller - assert_eq!(T::Currency::free_balance(&caller), caller_funding::() - value - deposit); + assert_eq!( + T::Currency::free_balance(&caller), + caller_funding::() - value - deposit - Pallet::::min_balance(), + ); // contract has the full value - assert_eq!(T::Currency::free_balance(&addr), value); - // instantiate should leave a contract - Contract::::address_info(&addr)?; + assert_eq!(T::Currency::free_balance(&addr), value + Pallet::::min_balance()); } // We just call a dummy contract to measure the overhead of the call extrinsic. @@ -348,18 +340,19 @@ benchmarks! { let instance = Contract::::with_caller( whitelisted_caller(), WasmModule::dummy(), vec![], )?; + let deposit_account = instance.info()?.deposit_account().clone(); let value = Pallet::::min_balance(); let origin = RawOrigin::Signed(instance.caller.clone()); let callee = instance.addr.clone(); let before = T::Currency::free_balance(&instance.account_id); + let before_deposit = T::Currency::free_balance(&deposit_account); }: _(origin, callee, value, Weight::MAX, None, data) verify { - // the contract itself does not trigger any reserves - let deposit = T::Currency::reserved_balance(&instance.account_id); + let deposit = T::Currency::free_balance(&deposit_account); // value and value transfered via call should be removed from the caller assert_eq!( T::Currency::free_balance(&instance.caller), - caller_funding::() - instance.value - value - deposit, + caller_funding::() - instance.value - value - deposit - Pallet::::min_balance(), ); // contract should have received the value assert_eq!(T::Currency::free_balance(&instance.account_id), before + value); @@ -798,14 +791,16 @@ benchmarks! { }); let instance = Contract::::new(code, vec![])?; let origin = RawOrigin::Signed(instance.caller.clone()); + let deposit_account = instance.info()?.deposit_account().clone(); assert_eq!(T::Currency::total_balance(&beneficiary), 0u32.into()); - assert_eq!(T::Currency::free_balance(&instance.account_id), Pallet::::min_balance()); - assert_ne!(T::Currency::reserved_balance(&instance.account_id), 0u32.into()); + assert_eq!(T::Currency::free_balance(&instance.account_id), Pallet::::min_balance() * 2u32.into()); + assert_ne!(T::Currency::free_balance(&deposit_account), 0u32.into()); }: call(origin, instance.addr.clone(), 0u32.into(), Weight::MAX, None, vec![]) verify { if r > 0 { assert_eq!(T::Currency::total_balance(&instance.account_id), 0u32.into()); - assert_eq!(T::Currency::total_balance(&beneficiary), Pallet::::min_balance()); + assert_eq!(T::Currency::total_balance(&deposit_account), 0u32.into()); + assert_eq!(T::Currency::total_balance(&beneficiary), Pallet::::min_balance() * 2u32.into()); } } @@ -1039,8 +1034,7 @@ benchmarks! { let instance = Contract::::new(code, vec![])?; let info = instance.info()?; for key in keys { - Storage::::write( - &info.trie_id, + info.write( &VarSizedKey::::try_from(key).map_err(|e| "Key has wrong length")?, Some(vec![]), None, @@ -1088,8 +1082,7 @@ benchmarks! { let instance = Contract::::new(code, vec![])?; let info = instance.info()?; for key in keys { - Storage::::write( - &info.trie_id, + info.write( &VarSizedKey::::try_from(key).map_err(|e| "Key has wrong length")?, Some(vec![]), None, @@ -1137,8 +1130,7 @@ benchmarks! { let instance = Contract::::new(code, vec![])?; let info = instance.info()?; for key in keys { - Storage::::write( - &info.trie_id, + info.write( &VarSizedKey::::try_from(key).map_err(|e| "Key has wrong length")?, Some(vec![42u8; (n * 2048) as usize]), // value_len increments by 2kb up to max payload_len None, @@ -1188,8 +1180,7 @@ benchmarks! { let instance = Contract::::new(code, vec![])?; let info = instance.info()?; for key in keys { - Storage::::write( - &info.trie_id, + info.write( &VarSizedKey::::try_from(key).map_err(|e| "Key has wrong length")?, Some(vec![]), None, @@ -1236,8 +1227,7 @@ benchmarks! { let instance = Contract::::new(code, vec![])?; let info = instance.info()?; for key in keys { - Storage::::write( - &info.trie_id, + info.write( &VarSizedKey::::try_from(key).map_err(|e| "Key has wrong length")?, Some(vec![42u8; (n * 2048) as usize]), // value_len increments by 2kb up to max payload_len None, @@ -1291,8 +1281,7 @@ benchmarks! { let instance = Contract::::new(code, vec![])?; let info = instance.info()?; for key in keys { - Storage::::write( - &info.trie_id, + info.write( &VarSizedKey::::try_from(key).map_err(|e| "Key has wrong length")?, Some(vec![]), None, @@ -1346,8 +1335,7 @@ benchmarks! { let instance = Contract::::new(code, vec![])?; let info = instance.info()?; for key in keys { - Storage::::write( - &info.trie_id, + info.write( &VarSizedKey::::try_from(key).map_err(|e| "Key has wrong length")?, Some(vec![42u8; (n * 2048) as usize]), // value_len increments by 2kb up to max payload_len None, @@ -1396,8 +1384,7 @@ benchmarks! { let instance = Contract::::new(code, vec![])?; let info = instance.info()?; for key in keys { - Storage::::write( - &info.trie_id, + info.write( &VarSizedKey::::try_from(key).map_err(|e| "Key has wrong length")?, Some(vec![]), None, @@ -1444,8 +1431,7 @@ benchmarks! { let instance = Contract::::new(code, vec![])?; let info = instance.info()?; for key in keys { - Storage::::write( - &info.trie_id, + info.write( &VarSizedKey::::try_from(key).map_err(|e| "Key has wrong length")?, Some(vec![42u8; (n * 2048) as usize]), // value_len increments by 2kb up to max payload_len None, @@ -1499,8 +1485,7 @@ benchmarks! { let instance = Contract::::new(code, vec![])?; let info = instance.info()?; for key in keys { - Storage::::write( - &info.trie_id, + info.write( &VarSizedKey::::try_from(key).map_err(|e| "Key has wrong length")?, Some(vec![]), None, @@ -1554,8 +1539,7 @@ benchmarks! { let instance = Contract::::new(code, vec![])?; let info = instance.info()?; for key in keys { - Storage::::write( - &info.trie_id, + info.write( &VarSizedKey::::try_from(key).map_err(|e| "Key has wrong length")?, Some(vec![42u8; (n * 2048) as usize]), // value_len increments by 2kb up to max payload_len None, @@ -1884,7 +1868,7 @@ benchmarks! { .. Default::default() }); let instance = Contract::::new(code, vec![])?; - instance.set_balance(value * (r * API_BENCHMARK_BATCH_SIZE + 1).into()); + instance.set_balance((value + Pallet::::min_balance()) * (r * API_BENCHMARK_BATCH_SIZE + 1).into()); let origin = RawOrigin::Signed(instance.caller.clone()); let callee = instance.addr.clone(); let addresses = hashes @@ -1997,7 +1981,7 @@ benchmarks! { .. Default::default() }); let instance = Contract::::new(code, vec![])?; - instance.set_balance(value * (API_BENCHMARK_BATCH_SIZE + 1).into()); + instance.set_balance((value + Pallet::::min_balance()) * (API_BENCHMARK_BATCH_SIZE + 1).into()); let origin = RawOrigin::Signed(instance.caller.clone()); }: call(origin, instance.addr, 0u32.into(), Weight::MAX, None, vec![]) @@ -3113,8 +3097,8 @@ benchmarks! { { let weight_limit = T::DeletionWeightLimit::get(); let max_queue_depth = T::DeletionQueueDepth::get() as usize; - let empty_queue_throughput = Storage::::deletion_budget(0, weight_limit); - let full_queue_throughput = Storage::::deletion_budget(max_queue_depth, weight_limit); + let empty_queue_throughput = ContractInfo::::deletion_budget(0, weight_limit); + let full_queue_throughput = ContractInfo::::deletion_budget(max_queue_depth, weight_limit); println!("{:#?}", Schedule::::default()); println!("###############################################"); println!("Lazy deletion weight per key: {}", empty_queue_throughput.0); diff --git a/frame/contracts/src/exec.rs b/frame/contracts/src/exec.rs index 9b033a76c..92930cc79 100644 --- a/frame/contracts/src/exec.rs +++ b/frame/contracts/src/exec.rs @@ -17,9 +17,9 @@ use crate::{ gas::GasMeter, - storage::{self, Storage, WriteOutcome}, + storage::{self, DepositAccount, WriteOutcome}, BalanceOf, CodeHash, Config, ContractInfo, ContractInfoOf, DebugBufferVec, Determinism, Error, - Event, Nonce, Pallet as Contracts, Schedule, + Event, Nonce, Pallet as Contracts, Schedule, System, }; use frame_support::{ crypto::ecdsa::ECDSAExt, @@ -492,7 +492,7 @@ enum CachedContract { /// /// In this case a reload is neither allowed nor possible. Please note that recursive /// calls cannot remove a contract as this is checked and denied. - Terminated, + Terminated(DepositAccount), } impl CachedContract { @@ -513,6 +513,15 @@ impl CachedContract { None } } + + /// Returns `Some` iff the contract is not `Cached::Invalidated`. + fn deposit_account(&self) -> Option<&DepositAccount> { + match self { + CachedContract::Cached(contract) => Some(contract.deposit_account()), + CachedContract::Terminated(deposit_account) => Some(&deposit_account), + CachedContract::Invalidated => None, + } + } } impl Frame { @@ -591,7 +600,9 @@ impl CachedContract { /// Terminate and return the contract info. fn terminate(&mut self, account_id: &T::AccountId) -> ContractInfo { self.load(account_id); - get_cached_or_panic_after_load!(mem::replace(self, Self::Terminated)) + let contract = get_cached_or_panic_after_load!(self); + let deposit_account = contract.deposit_account().clone(); + get_cached_or_panic_after_load!(mem::replace(self, Self::Terminated(deposit_account))) } } @@ -751,9 +762,7 @@ where input_data, salt, ); - let trie_id = Storage::::generate_trie_id(&account_id, nonce); - let contract = - Storage::::new_contract(&account_id, trie_id, *executable.code_hash())?; + let contract = ContractInfo::new(&account_id, nonce, *executable.code_hash())?; ( account_id, contract, @@ -873,7 +882,7 @@ where match (entry_point, delegated_code_hash) { (ExportedFunction::Constructor, _) => { // It is not allowed to terminate a contract inside its constructor. - if matches!(frame.contract_info, CachedContract::Terminated) { + if matches!(frame.contract_info, CachedContract::Terminated(_)) { return Err(Error::::TerminatedInConstructor.into()) } @@ -962,11 +971,21 @@ where // Record the storage meter changes of the nested call into the parent meter. // If the dropped frame's contract wasn't terminated we update the deposit counter - // in its contract info. The load is necessary to to pull it from storage in case + // in its contract info. The load is necessary to pull it from storage in case // it was invalidated. frame.contract_info.load(account_id); + let deposit_account = frame + .contract_info + .deposit_account() + .expect( + "Is only `None` when the info is invalidated. + We just re-loaded from storage which either makes the state `Cached` or `Terminated`. + qed", + ) + .clone(); let mut contract = frame.contract_info.into_contract(); - prev.nested_storage.absorb(frame.nested_storage, account_id, contract.as_mut()); + prev.nested_storage + .absorb(frame.nested_storage, deposit_account, contract.as_mut()); // In case the contract wasn't terminated we need to persist changes made to it. if let Some(contract) = contract { @@ -1001,10 +1020,14 @@ where if !persist { return } + let deposit_account = self.first_frame.contract_info.deposit_account().expect( + "Is only `None` when the info is invalidated. The first frame can't be invalidated. + qed", + ).clone(); let mut contract = self.first_frame.contract_info.as_contract(); self.storage_meter.absorb( mem::take(&mut self.first_frame.nested_storage), - &self.first_frame.account_id, + deposit_account, contract.as_deref_mut(), ); if let Some(contract) = contract { @@ -1182,19 +1205,21 @@ where } fn terminate(&mut self, beneficiary: &AccountIdOf) -> Result<(), DispatchError> { + use frame_support::traits::fungible::Inspect; if self.is_recursive() { return Err(Error::::TerminatedWhileReentrant.into()) } let frame = self.top_frame_mut(); let info = frame.terminate(); frame.nested_storage.terminate(&info); - Storage::::queue_trie_for_deletion(&info)?; - >::transfer( - ExistenceRequirement::AllowDeath, + System::::dec_consumers(&frame.account_id); + T::Currency::transfer( &frame.account_id, beneficiary, - T::Currency::free_balance(&frame.account_id), + T::Currency::reducible_balance(&frame.account_id, false), + ExistenceRequirement::AllowDeath, )?; + info.queue_trie_for_deletion()?; ContractInfoOf::::remove(&frame.account_id); E::remove_user(info.code_hash); Contracts::::deposit_event( @@ -1212,19 +1237,19 @@ where } fn get_storage(&mut self, key: &FixSizedKey) -> Option> { - Storage::::read(&self.top_frame_mut().contract_info().trie_id, key) + self.top_frame_mut().contract_info().read(key) } fn get_storage_transparent(&mut self, key: &VarSizedKey) -> Option> { - Storage::::read(&self.top_frame_mut().contract_info().trie_id, key) + self.top_frame_mut().contract_info().read(key) } fn get_storage_size(&mut self, key: &FixSizedKey) -> Option { - Storage::::size(&self.top_frame_mut().contract_info().trie_id, key) + self.top_frame_mut().contract_info().size(key) } fn get_storage_size_transparent(&mut self, key: &VarSizedKey) -> Option { - Storage::::size(&self.top_frame_mut().contract_info().trie_id, key) + self.top_frame_mut().contract_info().size(key) } fn set_storage( @@ -1234,8 +1259,7 @@ where take_old: bool, ) -> Result { let frame = self.top_frame_mut(); - Storage::::write( - &frame.contract_info.get(&frame.account_id).trie_id, + frame.contract_info.get(&frame.account_id).write( key, value, Some(&mut frame.nested_storage), @@ -1250,8 +1274,7 @@ where take_old: bool, ) -> Result { let frame = self.top_frame_mut(); - Storage::::write( - &frame.contract_info.get(&frame.account_id).trie_id, + frame.contract_info.get(&frame.account_id).write( key, value, Some(&mut frame.nested_storage), @@ -1438,7 +1461,6 @@ mod tests { use crate::{ exec::ExportedFunction::*, gas::GasMeter, - storage::Storage, tests::{ test_utils::{get_balance, hash, place_contract, set_balance}, ExtBuilder, RuntimeCall, RuntimeEvent as MetaEvent, Test, TestFilter, ALICE, BOB, @@ -1891,9 +1913,8 @@ mod tests { let mut gas_meter = GasMeter::::new(GAS_LIMIT); let executable = MockExecutable::from_storage(input_data_ch, &schedule, &mut gas_meter).unwrap(); - set_balance(&ALICE, min_balance * 1000); - let mut storage_meter = - storage::meter::Meter::new(&ALICE, Some(min_balance * 100), min_balance).unwrap(); + set_balance(&ALICE, min_balance * 10_000); + let mut storage_meter = storage::meter::Meter::new(&ALICE, None, min_balance).unwrap(); let result = MockStack::run_instantiate( ALICE, @@ -2231,7 +2252,7 @@ mod tests { // Check that the newly created account has the expected code hash and // there are instantiation event. assert_eq!( - Storage::::code_hash(&instantiated_contract_address).unwrap(), + ContractInfo::::load_code_hash(&instantiated_contract_address).unwrap(), dummy_ch ); assert_eq!( @@ -2273,7 +2294,7 @@ mod tests { ); // Check that the account has not been created. - assert!(Storage::::code_hash(&instantiated_contract_address).is_none()); + assert!(ContractInfo::::load_code_hash(&instantiated_contract_address).is_none()); assert!(events().is_empty()); }); } @@ -2332,7 +2353,7 @@ mod tests { // Check that the newly created account has the expected code hash and // there are instantiation event. assert_eq!( - Storage::::code_hash(&instantiated_contract_address).unwrap(), + ContractInfo::::load_code_hash(&instantiated_contract_address).unwrap(), dummy_ch ); assert_eq!( @@ -2374,7 +2395,7 @@ mod tests { set_balance(&ALICE, 1000); set_balance(&BOB, 100); place_contract(&BOB, instantiator_ch); - let mut storage_meter = storage::meter::Meter::new(&ALICE, Some(100), 0).unwrap(); + let mut storage_meter = storage::meter::Meter::new(&ALICE, Some(200), 0).unwrap(); assert_matches!( MockStack::run_call( @@ -2409,8 +2430,8 @@ mod tests { let mut gas_meter = GasMeter::::new(GAS_LIMIT); let executable = MockExecutable::from_storage(terminate_ch, &schedule, &mut gas_meter).unwrap(); - set_balance(&ALICE, 1000); - let mut storage_meter = storage::meter::Meter::new(&ALICE, Some(100), 100).unwrap(); + set_balance(&ALICE, 10_000); + let mut storage_meter = storage::meter::Meter::new(&ALICE, None, 100).unwrap(); assert_eq!( MockStack::run_instantiate( @@ -2494,9 +2515,8 @@ mod tests { let min_balance = ::Currency::minimum_balance(); let mut gas_meter = GasMeter::::new(GAS_LIMIT); let executable = MockExecutable::from_storage(code, &schedule, &mut gas_meter).unwrap(); - set_balance(&ALICE, min_balance * 1000); - let mut storage_meter = - storage::meter::Meter::new(&ALICE, Some(min_balance * 100), min_balance).unwrap(); + set_balance(&ALICE, min_balance * 10_000); + let mut storage_meter = storage::meter::Meter::new(&ALICE, None, min_balance).unwrap(); let result = MockStack::run_instantiate( ALICE, @@ -2902,10 +2922,9 @@ mod tests { MockExecutable::from_storage(succ_fail_code, &schedule, &mut gas_meter).unwrap(); let succ_succ_executable = MockExecutable::from_storage(succ_succ_code, &schedule, &mut gas_meter).unwrap(); - set_balance(&ALICE, min_balance * 1000); + set_balance(&ALICE, min_balance * 10_000); let mut storage_meter = - storage::meter::Meter::new(&ALICE, Some(min_balance * 500), min_balance * 100) - .unwrap(); + storage::meter::Meter::new(&ALICE, None, min_balance * 100).unwrap(); MockStack::run_instantiate( ALICE, diff --git a/frame/contracts/src/lib.rs b/frame/contracts/src/lib.rs index e131a2685..a5f35a9cb 100644 --- a/frame/contracts/src/lib.rs +++ b/frame/contracts/src/lib.rs @@ -85,6 +85,7 @@ #[macro_use] mod gas; +mod address; mod benchmarking; mod exec; mod migration; @@ -101,11 +102,11 @@ mod tests; use crate::{ exec::{AccountIdOf, ExecError, Executable, Stack as ExecStack}, gas::GasMeter, - storage::{meter::Meter as StorageMeter, ContractInfo, DeletedContract, Storage}, + storage::{meter::Meter as StorageMeter, ContractInfo, DeletedContract}, wasm::{OwnerInfo, PrefabWasmModule, TryInstantiate}, weights::WeightInfo, }; -use codec::{Codec, Decode, Encode, HasCompact}; +use codec::{Codec, Encode, HasCompact}; use frame_support::{ dispatch::{Dispatchable, GetDispatchInfo, Pays, PostDispatchInfo}, ensure, @@ -124,10 +125,11 @@ use pallet_contracts_primitives::{ }; use scale_info::TypeInfo; use smallvec::Array; -use sp_runtime::traits::{Convert, Hash, Saturating, StaticLookup, TrailingZeroInput}; +use sp_runtime::traits::{Convert, Hash, Saturating, StaticLookup}; use sp_std::{fmt::Debug, marker::PhantomData, prelude::*}; pub use crate::{ + address::{AddressGenerator, DefaultAddressGenerator}, exec::{Frame, VarSizedKey as StorageKey}, migration::Migration, pallet::*, @@ -155,49 +157,6 @@ type DebugBufferVec = BoundedVec::MaxDebugBufferLen>; /// that this value makes sense for a memory location or length. const SENTINEL: u32 = u32::MAX; -/// Provides the contract address generation method. -/// -/// See [`DefaultAddressGenerator`] for the default implementation. -pub trait AddressGenerator { - /// Generate the address of a contract based on the given instantiate parameters. - /// - /// # Note for implementors - /// 1. Make sure that there are no collisions, different inputs never lead to the same output. - /// 2. Make sure that the same inputs lead to the same output. - /// 3. Changing the implementation through a runtime upgrade without a proper storage migration - /// would lead to catastrophic misbehavior. - fn generate_address( - deploying_address: &T::AccountId, - code_hash: &CodeHash, - input_data: &[u8], - salt: &[u8], - ) -> T::AccountId; -} - -/// Default address generator. -/// -/// This is the default address generator used by contract instantiation. Its result -/// is only dependant on its inputs. It can therefore be used to reliably predict the -/// address of a contract. This is akin to the formula of eth's CREATE2 opcode. There -/// is no CREATE equivalent because CREATE2 is strictly more powerful. -/// Formula: -/// `hash("contract_addr_v1" ++ deploying_address ++ code_hash ++ input_data ++ salt)` -pub struct DefaultAddressGenerator; - -impl AddressGenerator for DefaultAddressGenerator { - fn generate_address( - deploying_address: &T::AccountId, - code_hash: &CodeHash, - input_data: &[u8], - salt: &[u8], - ) -> T::AccountId { - let entropy = (b"contract_addr_v1", deploying_address, code_hash, input_data, salt) - .using_encoded(T::Hashing::hash); - Decode::decode(&mut TrailingZeroInput::new(entropy.as_ref())) - .expect("infinite length input; no invalid inputs for type; qed") - } -} - #[frame_support::pallet] pub mod pallet { use super::*; @@ -365,7 +324,7 @@ pub mod pallet { #[pallet::hooks] impl Hooks> for Pallet { fn on_idle(_block: T::BlockNumber, remaining_weight: Weight) -> Weight { - Storage::::process_deletion_queue_batch(remaining_weight) + ContractInfo::::process_deletion_queue_batch(remaining_weight) .saturating_add(T::WeightInfo::on_process_deletion_queue_batch()) } @@ -381,7 +340,7 @@ pub mod pallet { .max_block .saturating_sub(System::::block_weight().total()) .min(T::DeletionWeightLimit::get()); - Storage::::process_deletion_queue_batch(weight_limit) + ContractInfo::::process_deletion_queue_batch(weight_limit) .saturating_add(T::WeightInfo::on_process_deletion_queue_batch()) } else { T::WeightInfo::on_process_deletion_queue_batch() @@ -1129,8 +1088,7 @@ impl Pallet { let contract_info = ContractInfoOf::::get(&address).ok_or(ContractAccessError::DoesntExist)?; - let maybe_value = Storage::::read( - &contract_info.trie_id, + let maybe_value = contract_info.read( &StorageKey::::try_from(key).map_err(|_| ContractAccessError::KeyDecodingFailed)?, ); Ok(maybe_value) @@ -1146,12 +1104,12 @@ impl Pallet { input_data: &[u8], salt: &[u8], ) -> T::AccountId { - T::AddressGenerator::generate_address(deploying_address, code_hash, input_data, salt) + T::AddressGenerator::contract_address(deploying_address, code_hash, input_data, salt) } /// Returns the code hash of the contract specified by `account` ID. pub fn code_hash(account: &AccountIdOf) -> Option> { - Storage::::code_hash(account) + ContractInfo::::load_code_hash(account) } /// Store code for benchmarks which does not check nor instrument the code. diff --git a/frame/contracts/src/storage.rs b/frame/contracts/src/storage.rs index bf030a2bd..35db33c4c 100644 --- a/frame/contracts/src/storage.rs +++ b/frame/contracts/src/storage.rs @@ -22,13 +22,15 @@ pub mod meter; use crate::{ exec::{AccountIdOf, StorageKey}, weights::WeightInfo, - BalanceOf, CodeHash, Config, ContractInfoOf, DeletionQueue, Error, TrieId, SENTINEL, + AddressGenerator, BalanceOf, CodeHash, Config, ContractInfoOf, DeletionQueue, Error, Pallet, + TrieId, SENTINEL, }; use codec::{Decode, Encode, MaxEncodedLen}; use frame_support::{ dispatch::{DispatchError, DispatchResult}, storage::child::{self, ChildInfo}, weights::Weight, + RuntimeDebugNoBound, }; use scale_info::TypeInfo; use sp_io::KillStorageResult; @@ -36,7 +38,7 @@ use sp_runtime::{ traits::{Hash, Saturating, Zero}, RuntimeDebug, }; -use sp_std::{marker::PhantomData, prelude::*}; +use sp_std::{ops::Deref, prelude::*}; /// Information for managing an account and its sub trie abstraction. /// This is the required info to cache for an account. @@ -45,28 +47,68 @@ use sp_std::{marker::PhantomData, prelude::*}; pub struct ContractInfo { /// Unique ID for the subtree encoded as a bytes vector. pub trie_id: TrieId, + /// The account that holds this contracts storage deposit. + /// + /// This is held in a separate account to prevent the contract from spending it. + deposit_account: DepositAccount, /// The code associated with a given account. pub code_hash: CodeHash, /// How many bytes of storage are accumulated in this contract's child trie. - pub storage_bytes: u32, + storage_bytes: u32, /// How many items of storage are accumulated in this contract's child trie. - pub storage_items: u32, + storage_items: u32, /// This records to how much deposit the accumulated `storage_bytes` amount to. pub storage_byte_deposit: BalanceOf, /// This records to how much deposit the accumulated `storage_items` amount to. - pub storage_item_deposit: BalanceOf, + storage_item_deposit: BalanceOf, /// This records how much deposit is put down in order to pay for the contract itself. /// /// We need to store this information separately so it is not used when calculating any refunds /// since the base deposit can only ever be refunded on contract termination. - pub storage_base_deposit: BalanceOf, + storage_base_deposit: BalanceOf, } impl ContractInfo { + /// Constructs a new contract info **without** writing it to storage. + /// + /// This returns an `Err` if an contract with the supplied `account` already exists + /// in storage. + pub fn new( + account: &AccountIdOf, + nonce: u64, + code_hash: CodeHash, + ) -> Result { + if >::contains_key(account) { + return Err(Error::::DuplicateContract.into()) + } + + let trie_id = { + let buf = (account, nonce).using_encoded(T::Hashing::hash); + buf.as_ref() + .to_vec() + .try_into() + .expect("Runtime uses a reasonable hash size. Hence sizeof(T::Hash) <= 128; qed") + }; + + let deposit_account = DepositAccount(T::AddressGenerator::deposit_address(account)); + + let contract = Self { + trie_id, + deposit_account, + code_hash, + storage_bytes: 0, + storage_items: 0, + storage_byte_deposit: Zero::zero(), + storage_item_deposit: Zero::zero(), + storage_base_deposit: Zero::zero(), + }; + + Ok(contract) + } + /// Associated child trie unique id is built from the hash part of the trie id. - #[cfg(test)] pub fn child_trie_info(&self) -> ChildInfo { - child_trie_info(&self.trie_id[..]) + ChildInfo::new_default(self.trie_id.as_ref()) } /// The deposit paying for the accumulated storage generated within the contract's child trie. @@ -76,79 +118,30 @@ impl ContractInfo { /// Same as [`Self::extra_deposit`] but including the base deposit. pub fn total_deposit(&self) -> BalanceOf { - self.extra_deposit().saturating_add(self.storage_base_deposit) + self.extra_deposit() + .saturating_add(self.storage_base_deposit) + .saturating_sub(Pallet::::min_balance()) } -} - -/// Associated child trie unique id is built from the hash part of the trie id. -fn child_trie_info(trie_id: &[u8]) -> ChildInfo { - ChildInfo::new_default(trie_id) -} - -#[derive(Encode, Decode, TypeInfo, MaxEncodedLen)] -pub struct DeletedContract { - pub(crate) trie_id: TrieId, -} -/// Information about what happended to the pre-existing value when calling [`Storage::write`]. -#[cfg_attr(test, derive(Debug, PartialEq))] -pub enum WriteOutcome { - /// No value existed at the specified key. - New, - /// A value of the returned length was overwritten. - Overwritten(u32), - /// The returned value was taken out of storage before being overwritten. - /// - /// This is only returned when specifically requested because it causes additional work - /// depending on the size of the pre-existing value. When not requested [`Self::Overwritten`] - /// is returned instead. - Taken(Vec), -} - -impl WriteOutcome { - /// Extracts the size of the overwritten value or `0` if there - /// was no value in storage. - pub fn old_len(&self) -> u32 { - match self { - Self::New => 0, - Self::Overwritten(len) => *len, - Self::Taken(value) => value.len() as u32, - } + /// Return the account that storage deposits should be deposited into. + pub fn deposit_account(&self) -> &DepositAccount { + &self.deposit_account } - /// Extracts the size of the overwritten value or `SENTINEL` if there - /// was no value in storage. - /// - /// # Note - /// - /// We cannot use `0` as sentinel value because there could be a zero sized - /// storage entry which is different from a non existing one. - pub fn old_len_with_sentinel(&self) -> u32 { - match self { - Self::New => SENTINEL, - Self::Overwritten(len) => *len, - Self::Taken(value) => value.len() as u32, - } - } -} - -pub struct Storage(PhantomData); - -impl Storage { /// Reads a storage kv pair of a contract. /// /// The read is performed from the `trie_id` only. The `address` is not necessary. If the /// contract doesn't store under the given `key` `None` is returned. - pub fn read>(trie_id: &TrieId, key: &K) -> Option> { - child::get_raw(&child_trie_info(trie_id), key.hash().as_slice()) + pub fn read>(&self, key: &K) -> Option> { + child::get_raw(&self.child_trie_info(), key.hash().as_slice()) } /// Returns `Some(len)` (in bytes) if a storage item exists at `key`. /// /// Returns `None` if the `key` wasn't previously set by `set_storage` or /// was deleted. - pub fn size>(trie_id: &TrieId, key: &K) -> Option { - child::len(&child_trie_info(trie_id), key.hash().as_slice()) + pub fn size>(&self, key: &K) -> Option { + child::len(&self.child_trie_info(), key.hash().as_slice()) } /// Update a storage entry into a contract's kv storage. @@ -159,13 +152,13 @@ impl Storage { /// This function also records how much storage was created or removed if a `storage_meter` /// is supplied. It should only be absent for testing or benchmarking code. pub fn write>( - trie_id: &TrieId, + &self, key: &K, new_value: Option>, storage_meter: Option<&mut meter::NestedMeter>, take: bool, ) -> Result { - let child_trie_info = &child_trie_info(trie_id); + let child_trie_info = &self.child_trie_info(); let hashed_key = key.hash(); let (old_len, old_value) = if take { let val = child::get_raw(child_trie_info, &hashed_key); @@ -208,37 +201,11 @@ impl Storage { }) } - /// Creates a new contract descriptor in the storage with the given code hash at the given - /// address. - /// - /// Returns `Err` if there is already a contract at the given address. - pub fn new_contract( - account: &AccountIdOf, - trie_id: TrieId, - code_hash: CodeHash, - ) -> Result, DispatchError> { - if >::contains_key(account) { - return Err(Error::::DuplicateContract.into()) - } - - let contract = ContractInfo:: { - code_hash, - trie_id, - storage_bytes: 0, - storage_items: 0, - storage_byte_deposit: Zero::zero(), - storage_item_deposit: Zero::zero(), - storage_base_deposit: Zero::zero(), - }; - - Ok(contract) - } - /// Push a contract's trie to the deletion queue for lazy removal. /// /// You must make sure that the contract is also removed when queuing the trie for deletion. - pub fn queue_trie_for_deletion(contract: &ContractInfo) -> DispatchResult { - >::try_append(DeletedContract { trie_id: contract.trie_id.clone() }) + pub fn queue_trie_for_deletion(&self) -> DispatchResult { + >::try_append(DeletedContract { trie_id: self.trie_id.clone() }) .map_err(|_| >::DeletionQueueFull.into()) } @@ -289,7 +256,10 @@ impl Storage { // Cannot panic due to loop condition let trie = &mut queue[0]; #[allow(deprecated)] - let outcome = child::kill_storage(&child_trie_info(&trie.trie_id), Some(remaining_key_budget)); + let outcome = child::kill_storage( + &ChildInfo::new_default(&trie.trie_id), + Some(remaining_key_budget), + ); let keys_removed = match outcome { // This happens when our budget wasn't large enough to remove all keys. KillStorageResult::SomeRemaining(c) => c, @@ -307,17 +277,8 @@ impl Storage { weight_limit.saturating_sub(weight_per_key.saturating_mul(u64::from(remaining_key_budget))) } - /// Generates a unique trie id by returning `hash(account_id ++ nonce)`. - pub fn generate_trie_id(account_id: &AccountIdOf, nonce: u64) -> TrieId { - let buf = (account_id, nonce).using_encoded(T::Hashing::hash); - buf.as_ref() - .to_vec() - .try_into() - .expect("Runtime uses a reasonable hash size. Hence sizeof(T::Hash) <= 128; qed") - } - /// Returns the code hash of the contract specified by `account` ID. - pub fn code_hash(account: &AccountIdOf) -> Option> { + pub fn load_code_hash(account: &AccountIdOf) -> Option> { >::get(account).map(|i| i.code_hash) } @@ -332,3 +293,62 @@ impl Storage { >::put(bounded); } } + +#[derive(Encode, Decode, TypeInfo, MaxEncodedLen)] +pub struct DeletedContract { + pub(crate) trie_id: TrieId, +} + +/// Information about what happended to the pre-existing value when calling [`ContractInfo::write`]. +#[cfg_attr(test, derive(Debug, PartialEq))] +pub enum WriteOutcome { + /// No value existed at the specified key. + New, + /// A value of the returned length was overwritten. + Overwritten(u32), + /// The returned value was taken out of storage before being overwritten. + /// + /// This is only returned when specifically requested because it causes additional work + /// depending on the size of the pre-existing value. When not requested [`Self::Overwritten`] + /// is returned instead. + Taken(Vec), +} + +impl WriteOutcome { + /// Extracts the size of the overwritten value or `0` if there + /// was no value in storage. + pub fn old_len(&self) -> u32 { + match self { + Self::New => 0, + Self::Overwritten(len) => *len, + Self::Taken(value) => value.len() as u32, + } + } + + /// Extracts the size of the overwritten value or `SENTINEL` if there + /// was no value in storage. + /// + /// # Note + /// + /// We cannot use `0` as sentinel value because there could be a zero sized + /// storage entry which is different from a non existing one. + pub fn old_len_with_sentinel(&self) -> u32 { + match self { + Self::New => SENTINEL, + Self::Overwritten(len) => *len, + Self::Taken(value) => value.len() as u32, + } + } +} + +#[derive(Encode, Decode, Clone, PartialEq, Eq, RuntimeDebugNoBound, TypeInfo, MaxEncodedLen)] +#[scale_info(skip_type_params(T))] +pub struct DepositAccount(AccountIdOf); + +impl Deref for DepositAccount { + type Target = AccountIdOf; + + fn deref(&self) -> &Self::Target { + &self.0 + } +} diff --git a/frame/contracts/src/storage/meter.rs b/frame/contracts/src/storage/meter.rs index 94bca741a..5102edb78 100644 --- a/frame/contracts/src/storage/meter.rs +++ b/frame/contracts/src/storage/meter.rs @@ -17,22 +17,19 @@ //! This module contains functions to meter the storage deposit. -use crate::{storage::ContractInfo, BalanceOf, Config, Error, Inspect, Pallet}; +use crate::{ + storage::{ContractInfo, DepositAccount}, + BalanceOf, Config, Error, Inspect, Pallet, System, +}; use codec::Encode; use frame_support::{ dispatch::DispatchError, ensure, - traits::{ - tokens::{BalanceStatus, WithdrawConsequence}, - Currency, ExistenceRequirement, Get, ReservableCurrency, - }, + traits::{tokens::WithdrawConsequence, Currency, ExistenceRequirement, Get}, DefaultNoBound, RuntimeDebugNoBound, }; use pallet_contracts_primitives::StorageDeposit as Deposit; -use sp_runtime::{ - traits::{Saturating, Zero}, - FixedPointNumber, FixedU128, -}; +use sp_runtime::{traits::Saturating, FixedPointNumber, FixedU128}; use sp_std::{marker::PhantomData, vec::Vec}; /// Deposit that uses the native currency's balance type. @@ -72,14 +69,14 @@ pub trait Ext { /// This is called to inform the implementer that some balance should be charged due to /// some interaction of the `origin` with a `contract`. /// - /// The balance transfer can either flow from `origin` to `contract` or the other way + /// The balance transfer can either flow from `origin` to `deposit_account` or the other way /// around depending on whether `amount` constitutes a `Charge` or a `Refund`. - /// It is guaranteed that that this succeeds because no more balance than returned by + /// It is guaranteed that this succeeds because no more balance than returned by /// `check_limit` is ever charged. This is why this function is infallible. /// `terminated` designates whether the `contract` was terminated. fn charge( origin: &T::AccountId, - contract: &T::AccountId, + deposit_account: &DepositAccount, amount: &DepositOf, terminated: bool, ); @@ -216,7 +213,7 @@ impl Diff { /// essentially makes the order of storage changes irrelevant with regard to the deposit system. #[derive(RuntimeDebugNoBound, Clone)] struct Charge { - contract: T::AccountId, + deposit_account: DepositAccount, amount: DepositOf, terminated: bool, } @@ -270,8 +267,8 @@ where /// Absorb a child that was spawned to handle a sub call. /// /// This should be called whenever a sub call comes to its end and it is **not** reverted. - /// This does the actual balance transfer from/to `origin` and `contract` based on the overall - /// storage consumption of the call. It also updates the supplied contract info. + /// This does the actual balance transfer from/to `origin` and `deposit_account` based on the + /// overall storage consumption of the call. It also updates the supplied contract info. /// /// In case a contract reverted the child meter should just be dropped in order to revert /// any changes it recorded. @@ -280,12 +277,12 @@ where /// /// - `absorbed`: The child storage meter that should be absorbed. /// - `origin`: The origin that spawned the original root meter. - /// - `contract`: The contract that this sub call belongs to. + /// - `deposit_account`: The contract's deposit account that this sub call belongs to. /// - `info`: The info of the contract in question. `None` if the contract was terminated. pub fn absorb( &mut self, absorbed: RawMeter, - contract: &T::AccountId, + deposit_account: DepositAccount, info: Option<&mut ContractInfo>, ) { let own_deposit = absorbed.own_contribution.update_contract(info); @@ -296,7 +293,7 @@ where if !own_deposit.is_zero() { self.charges.extend_from_slice(&absorbed.charges); self.charges.push(Charge { - contract: contract.clone(), + deposit_account, amount: own_deposit, terminated: absorbed.is_terminated(), }); @@ -345,10 +342,10 @@ where /// execution did finish. pub fn into_deposit(self, origin: &T::AccountId) -> DepositOf { for charge in self.charges.iter().filter(|c| matches!(c.amount, Deposit::Refund(_))) { - E::charge(origin, &charge.contract, &charge.amount, charge.terminated); + E::charge(origin, &charge.deposit_account, &charge.amount, charge.terminated); } for charge in self.charges.iter().filter(|c| matches!(c.amount, Deposit::Charge(_))) { - E::charge(origin, &charge.contract, &charge.amount, charge.terminated); + E::charge(origin, &charge.deposit_account, &charge.amount, charge.terminated); } self.total_deposit } @@ -360,9 +357,8 @@ where T: Config, E: Ext, { - /// Try to charge the `diff` from the meter. Fails if this would exceed the original limit. + /// Charge `diff` from the meter. pub fn charge(&mut self, diff: &Diff) { - debug_assert!(self.is_alive()); match &mut self.own_contribution { Contribution::Alive(own) => *own = own.saturating_add(diff), _ => panic!("Charge is never called after termination; qed"), @@ -379,13 +375,16 @@ where info: &mut ContractInfo, ) -> Result, DispatchError> { debug_assert!(self.is_alive()); + + let ed = Pallet::::min_balance(); let mut deposit = Diff { bytes_added: info.encoded_size() as u32, items_added: 1, ..Default::default() } .update_contract::(None); - // Instantiate needs to transfer the minimum balance at least in order to pull the - // contract's account into existence. - deposit = deposit.max(Deposit::Charge(Pallet::::min_balance())); + // Instantiate needs to transfer at least the minimum balance in order to pull the + // deposit account into existence. + // We also add another `ed` here which goes to the contract's own account into existence. + deposit = deposit.max(Deposit::Charge(ed)).saturating_add(&Deposit::Charge(ed)); if deposit.charge_or_zero() > self.limit { return Err(>::StorageDepositLimitExhausted.into()) } @@ -394,11 +393,22 @@ where // contract execution does conclude and hence would lead to a double charge. self.total_deposit = deposit.clone(); info.storage_base_deposit = deposit.charge_or_zero(); - if !deposit.is_zero() { - // We need to charge immediately so that the account is created before the `value` - // is transferred from the caller to the contract. - E::charge(origin, contract, &deposit, false); - } + + // Usually, deposit charges are deferred to be able to coalesce them with refunds. + // However, we need to charge immediately so that the account is created before + // charges possibly below the ed are collected and fail. + E::charge( + origin, + info.deposit_account(), + &deposit.saturating_sub(&Deposit::Charge(ed)), + false, + ); + System::::inc_consumers(info.deposit_account())?; + + // We also need to make sure that the contract's account itself exists. + T::Currency::transfer(origin, contract, ed, ExistenceRequirement::KeepAlive)?; + System::::inc_consumers(contract)?; + Ok(deposit) } @@ -443,7 +453,12 @@ impl Ext for ReservingExt { limit: Option>, min_leftover: BalanceOf, ) -> Result, DispatchError> { - let max = T::Currency::reducible_balance(origin, true).saturating_sub(min_leftover); + // We are sending the `min_leftover` and the `min_balance` from the origin + // account as part of a contract call. Hence origin needs to have those left over + // as free balance after accounting for all deposits. + let max = T::Currency::reducible_balance(origin, true) + .saturating_sub(min_leftover) + .saturating_sub(Pallet::::min_balance()); let limit = limit.unwrap_or(max); ensure!( limit <= max && @@ -455,67 +470,67 @@ impl Ext for ReservingExt { fn charge( origin: &T::AccountId, - contract: &T::AccountId, + deposit_account: &DepositAccount, amount: &DepositOf, terminated: bool, ) { - // There is nothing we can do when this fails as this constitutes a bug in the runtime: - // Either the runtime does not hold up the invariant of never deleting a contract's account - // or it does not honor reserved balances. We need to settle for emitting an error log - // in this case. + // There is nothing we can do when this fails as this constitutes a bug in the runtime. + // We need to settle for emitting an error log in this case. + // + // # Note + // + // This is infallible because it is called in a part of the execution where we cannot + // simply roll back. It might make sense to do some refactoring to move the deposit + // collection to the fallible part of execution. match amount { Deposit::Charge(amount) => { - // This will never fail because a contract's account is required to exist - // at all times. The pallet enforces this invariant by depositing at least the - // existential deposit when instantiating and never refunds it unless the contract - // is removed. This means the receiver always exists except when instantiating a - // contract. In this case we made sure that at least the existential deposit is - // sent. The sender always has enough balance because we checked that it had enough - // balance when instantiating the storage meter. + // This will never fail because a deposit account is required to exist + // at all times. The pallet enforces this invariant by holding a consumer reference + // on the deposit account as long as the contract exists. + // + // The sender always has enough balance because we checked that it had enough + // balance when instantiating the storage meter. There is no way for the sender + // which is a plain account to send away this balance in the meantime. let result = T::Currency::transfer( origin, - contract, + deposit_account, *amount, ExistenceRequirement::KeepAlive, - ) - .and_then(|_| T::Currency::reserve(contract, *amount)); + ); if let Err(err) = result { log::error!( target: "runtime::contracts", - "Failed to transfer storage deposit {:?} from origin {:?} to contract {:?}: {:?}", - amount, origin, contract, err, + "Failed to transfer storage deposit {:?} from origin {:?} to deposit account {:?}: {:?}", + amount, origin, deposit_account, err, ); if cfg!(debug_assertions) { panic!("Unable to collect storage deposit. This is a bug."); } } }, - // For `Refund(_)` no error happen because the initial value transfer from the - // origin to the contract has a keep alive existence requirement and when reserving we - // make sure to leave at least the ed in the free balance. Therefore the receiver always - // exists because there is no way for it to be removed in between. The sender always has - // enough reserved balance because we track it in the `ContractInfo` and never send more - // back than we have. + // The receiver always exists because the initial value transfer from the + // origin to the contract has a keep alive existence requirement. When taking a deposit + // we make sure to leave at least the ed in the free balance. + // + // The sender always has enough balance because we track it in the `ContractInfo` and + // never send more back than we have. Noone has access to the deposit account. Hence no + // other interaction with this account takes place. Deposit::Refund(amount) => { - let amount = if terminated { - *amount - } else { - // This is necessary when the `storage_deposit` tracked inside the account - // info is out of sync with the actual balance. That can only happen due to - // slashing. We make sure to never dust the contract's account through a - // refund because we consider this unexpected behaviour. - *amount.min( - &T::Currency::reserved_balance(contract) - .saturating_sub(Pallet::::min_balance()), - ) - }; - let result = - T::Currency::repatriate_reserved(contract, origin, amount, BalanceStatus::Free); - if matches!(result, Ok(val) if !val.is_zero()) || matches!(result, Err(_)) { + if terminated { + System::::dec_consumers(&deposit_account); + } + let result = T::Currency::transfer( + deposit_account, + origin, + *amount, + // We can safely use `AllowDeath` because our own consumer prevents an removal. + ExistenceRequirement::AllowDeath, + ); + if matches!(result, Err(_)) { log::error!( target: "runtime::contracts", - "Failed to repatriate storage deposit {:?} from contract {:?} to origin {:?}: {:?}", - amount, contract, origin, result, + "Failed to refund storage deposit {:?} from deposit account {:?} to origin {:?}: {:?}", + amount, deposit_account, origin, result, ); if cfg!(debug_assertions) { panic!("Unable to refund storage deposit. This is a bug."); @@ -558,7 +573,7 @@ mod tests { #[derive(Debug, PartialEq, Eq, Clone)] struct Charge { origin: AccountIdOf, - contract: AccountIdOf, + contract: DepositAccount, amount: DepositOf, terminated: bool, } @@ -592,7 +607,7 @@ mod tests { fn charge( origin: &AccountIdOf, - contract: &AccountIdOf, + contract: &DepositAccount, amount: &DepositOf, terminated: bool, ) { @@ -620,12 +635,10 @@ mod tests { } fn new_info(info: StorageInfo) -> ContractInfo { - use crate::storage::Storage; - use sp_runtime::traits::Hash; - ContractInfo:: { - trie_id: >::generate_trie_id(&ALICE, 42), - code_hash: ::Hashing::hash(b"42"), + trie_id: Default::default(), + deposit_account: DepositAccount([0u8; 32].into()), + code_hash: Default::default(), storage_bytes: info.bytes, storage_items: info.items, storage_byte_deposit: info.bytes_deposit, @@ -659,7 +672,7 @@ mod tests { // an empty charge does not create a `Charge` entry let mut nested0 = meter.nested(); nested0.charge(&Default::default()); - meter.absorb(nested0, &BOB, None); + meter.absorb(nested0, DepositAccount(BOB), None); assert_eq!( TestExtTestValue::get(), @@ -692,16 +705,16 @@ mod tests { new_info(StorageInfo { bytes: 100, items: 10, bytes_deposit: 100, items_deposit: 20 }); let mut nested1 = nested0.nested(); nested1.charge(&Diff { items_removed: 5, ..Default::default() }); - nested0.absorb(nested1, &CHARLIE, Some(&mut nested1_info)); + nested0.absorb(nested1, DepositAccount(CHARLIE), Some(&mut nested1_info)); let mut nested2_info = new_info(StorageInfo { bytes: 100, items: 7, bytes_deposit: 100, items_deposit: 20 }); let mut nested2 = nested0.nested(); nested2.charge(&Diff { items_removed: 7, ..Default::default() }); - nested0.absorb(nested2, &CHARLIE, Some(&mut nested2_info)); + nested0.absorb(nested2, DepositAccount(CHARLIE), Some(&mut nested2_info)); nested0.enforce_limit(Some(&mut nested0_info)).unwrap(); - meter.absorb(nested0, &BOB, Some(&mut nested0_info)); + meter.absorb(nested0, DepositAccount(BOB), Some(&mut nested0_info)); meter.into_deposit(&ALICE); @@ -716,19 +729,19 @@ mod tests { charges: vec![ Charge { origin: ALICE, - contract: CHARLIE, + contract: DepositAccount(CHARLIE), amount: Deposit::Refund(10), terminated: false }, Charge { origin: ALICE, - contract: CHARLIE, + contract: DepositAccount(CHARLIE), amount: Deposit::Refund(20), terminated: false }, Charge { origin: ALICE, - contract: BOB, + contract: DepositAccount(BOB), amount: Deposit::Charge(2), terminated: false } @@ -760,9 +773,9 @@ mod tests { nested1.charge(&Diff { bytes_added: 20, ..Default::default() }); nested1.terminate(&nested1_info); nested0.enforce_limit(Some(&mut nested1_info)).unwrap(); - nested0.absorb(nested1, &CHARLIE, None); + nested0.absorb(nested1, DepositAccount(CHARLIE), None); - meter.absorb(nested0, &BOB, None); + meter.absorb(nested0, DepositAccount(BOB), None); meter.into_deposit(&ALICE); assert_eq!( @@ -772,13 +785,13 @@ mod tests { charges: vec![ Charge { origin: ALICE, - contract: CHARLIE, - amount: Deposit::Refund(120), + contract: DepositAccount(CHARLIE), + amount: Deposit::Refund(119), terminated: true }, Charge { origin: ALICE, - contract: BOB, + contract: DepositAccount(BOB), amount: Deposit::Charge(12), terminated: false } diff --git a/frame/contracts/src/tests.rs b/frame/contracts/src/tests.rs index cab426ad1..879268a68 100644 --- a/frame/contracts/src/tests.rs +++ b/frame/contracts/src/tests.rs @@ -22,12 +22,11 @@ use crate::{ Result as ExtensionResult, RetVal, ReturnFlags, SysConfig, }, exec::{FixSizedKey, Frame}, - storage::Storage, tests::test_utils::{get_contract, get_contract_checked}, wasm::{Determinism, PrefabWasmModule, ReturnCode as RuntimeReturnCode}, weights::WeightInfo, - BalanceOf, Code, CodeStorage, Config, ContractInfoOf, DefaultAddressGenerator, DeletionQueue, - Error, Pallet, Schedule, + BalanceOf, Code, CodeStorage, Config, ContractInfo, ContractInfoOf, DefaultAddressGenerator, + DeletionQueue, Error, Pallet, Schedule, }; use assert_matches::assert_matches; use codec::Encode; @@ -37,8 +36,8 @@ use frame_support::{ parameter_types, storage::child, traits::{ - BalanceStatus, ConstU32, ConstU64, Contains, Currency, Get, LockableCurrency, OnIdle, - OnInitialize, ReservableCurrency, WithdrawReasons, + ConstU32, ConstU64, Contains, Currency, ExistenceRequirement, Get, LockableCurrency, + OnIdle, OnInitialize, ReservableCurrency, WithdrawReasons, }, weights::{constants::WEIGHT_REF_TIME_PER_SECOND, Weight}, }; @@ -51,7 +50,7 @@ use sp_runtime::{ traits::{BlakeTwo256, Convert, Hash, IdentityLookup}, AccountId32, }; -use std::sync::Arc; +use std::{ops::Deref, sync::Arc}; use crate as pallet_contracts; @@ -76,9 +75,7 @@ frame_support::construct_runtime!( #[macro_use] pub mod test_utils { use super::{Balances, Hash, SysConfig, Test}; - use crate::{ - exec::AccountIdOf, storage::Storage, CodeHash, Config, ContractInfo, ContractInfoOf, Nonce, - }; + use crate::{exec::AccountIdOf, CodeHash, Config, ContractInfo, ContractInfoOf, Nonce}; use codec::Encode; use frame_support::traits::Currency; @@ -87,9 +84,8 @@ pub mod test_utils { *counter += 1; *counter }); - let trie_id = Storage::::generate_trie_id(address, nonce); set_balance(address, ::Currency::minimum_balance() * 10); - let contract = Storage::::new_contract(&address, trie_id, code_hash).unwrap(); + let contract = >::new(&address, nonce, code_hash).unwrap(); >::insert(address, contract); } pub fn set_balance(who: &AccountIdOf, amount: u64) { @@ -518,7 +514,7 @@ fn calling_plain_account_fails() { fn instantiate_and_call_and_deposit_event() { let (wasm, code_hash) = compile_module::("event_and_return_on_deploy").unwrap(); - ExtBuilder::default().existential_deposit(500).build().execute_with(|| { + ExtBuilder::default().existential_deposit(1).build().execute_with(|| { let _ = Balances::deposit_creating(&ALICE, 1_000_000); let min_balance = ::Currency::minimum_balance(); let value = 100; @@ -551,21 +547,24 @@ fn instantiate_and_call_and_deposit_event() { .account_id; assert!(ContractInfoOf::::contains_key(&addr)); + let contract = get_contract(&addr); + let deposit_account = contract.deposit_account().deref(); + assert_eq!( System::events(), vec![ EventRecord { phase: Phase::Initialization, event: RuntimeEvent::System(frame_system::Event::NewAccount { - account: addr.clone() + account: deposit_account.clone(), }), topics: vec![], }, EventRecord { phase: Phase::Initialization, event: RuntimeEvent::Balances(pallet_balances::Event::Endowed { - account: addr.clone(), - free_balance: min_balance, + account: deposit_account.clone(), + free_balance: 131, }), topics: vec![], }, @@ -573,15 +572,31 @@ fn instantiate_and_call_and_deposit_event() { phase: Phase::Initialization, event: RuntimeEvent::Balances(pallet_balances::Event::Transfer { from: ALICE, - to: addr.clone(), - amount: min_balance, + to: deposit_account.clone(), + amount: 131, }), topics: vec![], }, EventRecord { phase: Phase::Initialization, - event: RuntimeEvent::Balances(pallet_balances::Event::Reserved { - who: addr.clone(), + event: RuntimeEvent::System(frame_system::Event::NewAccount { + account: addr.clone() + }), + topics: vec![], + }, + EventRecord { + phase: Phase::Initialization, + event: RuntimeEvent::Balances(pallet_balances::Event::Endowed { + account: addr.clone(), + free_balance: min_balance, + }), + topics: vec![], + }, + EventRecord { + phase: Phase::Initialization, + event: RuntimeEvent::Balances(pallet_balances::Event::Transfer { + from: ALICE, + to: addr.clone(), amount: min_balance, }), topics: vec![], @@ -823,7 +838,7 @@ fn deploy_and_call_other_contract() { let (caller_wasm, _caller_code_hash) = compile_module::("caller_contract").unwrap(); let (callee_wasm, callee_code_hash) = compile_module::("return_with_data").unwrap(); - ExtBuilder::default().existential_deposit(500).build().execute_with(|| { + ExtBuilder::default().existential_deposit(1).build().execute_with(|| { let min_balance = ::Currency::minimum_balance(); // Create @@ -841,18 +856,7 @@ fn deploy_and_call_other_contract() { .result .unwrap() .account_id; - Contracts::bare_instantiate( - ALICE, - 100_000, - GAS_LIMIT, - None, - Code::Upload(callee_wasm), - 0u32.to_le_bytes().encode(), - vec![42], - false, - ) - .result - .unwrap(); + Contracts::bare_upload_code(ALICE, callee_wasm, None, Determinism::Deterministic).unwrap(); let callee_addr = Contracts::contract_address( &caller_addr, @@ -875,21 +879,24 @@ fn deploy_and_call_other_contract() { callee_code_hash.as_ref().to_vec(), )); + let callee = get_contract(&callee_addr); + let deposit_account = callee.deposit_account().deref(); + assert_eq!( System::events(), vec![ EventRecord { phase: Phase::Initialization, event: RuntimeEvent::System(frame_system::Event::NewAccount { - account: callee_addr.clone() + account: deposit_account.clone(), }), topics: vec![], }, EventRecord { phase: Phase::Initialization, event: RuntimeEvent::Balances(pallet_balances::Event::Endowed { - account: callee_addr.clone(), - free_balance: min_balance, + account: deposit_account.clone(), + free_balance: 131, }), topics: vec![], }, @@ -897,15 +904,31 @@ fn deploy_and_call_other_contract() { phase: Phase::Initialization, event: RuntimeEvent::Balances(pallet_balances::Event::Transfer { from: ALICE, - to: callee_addr.clone(), - amount: min_balance, + to: deposit_account.clone(), + amount: 131, }), topics: vec![], }, EventRecord { phase: Phase::Initialization, - event: RuntimeEvent::Balances(pallet_balances::Event::Reserved { - who: callee_addr.clone(), + event: RuntimeEvent::System(frame_system::Event::NewAccount { + account: callee_addr.clone() + }), + topics: vec![], + }, + EventRecord { + phase: Phase::Initialization, + event: RuntimeEvent::Balances(pallet_balances::Event::Endowed { + account: callee_addr.clone(), + free_balance: min_balance, + }), + topics: vec![], + }, + EventRecord { + phase: Phase::Initialization, + event: RuntimeEvent::Balances(pallet_balances::Event::Transfer { + from: ALICE, + to: callee_addr.clone(), amount: min_balance, }), topics: vec![], @@ -915,7 +938,7 @@ fn deploy_and_call_other_contract() { event: RuntimeEvent::Balances(pallet_balances::Event::Transfer { from: caller_addr.clone(), to: callee_addr.clone(), - amount: 32768, // hard coded in wasm + amount: 32768 // hardcoded in wasm }), topics: vec![], }, @@ -999,8 +1022,8 @@ fn delegate_call() { } #[test] -fn cannot_self_destruct_through_draning() { - let (wasm, _code_hash) = compile_module::("drain").unwrap(); +fn transfer_allow_death_cannot_kill_account() { + let (wasm, _code_hash) = compile_module::("dummy").unwrap(); ExtBuilder::default().existential_deposit(200).build().execute_with(|| { let _ = Balances::deposit_creating(&ALICE, 1_000_000); @@ -1022,36 +1045,32 @@ fn cannot_self_destruct_through_draning() { // Check that the BOB contract has been instantiated. get_contract(&addr); - // Call BOB which makes it send all funds to the zero address - // The contract code asserts that the transfer was successful - assert_ok!(Contracts::call( - RuntimeOrigin::signed(ALICE), - addr.clone(), - 0, - GAS_LIMIT, - None, - vec![] - )); + let total_balance = ::Currency::total_balance(&addr); - // Make sure the account wasn't remove by sending all free balance away. - assert_eq!( - ::Currency::total_balance(&addr), - ::Currency::minimum_balance(), + assert_err!( + <::Currency as Currency>::transfer( + &addr, + &ALICE, + total_balance, + ExistenceRequirement::AllowDeath, + ), + pallet_balances::Error::::KeepAlive, ); + + assert_eq!(::Currency::total_balance(&addr), total_balance); }); } #[test] -fn cannot_self_destruct_through_storage_refund_after_price_change() { - let (wasm, _code_hash) = compile_module::("store").unwrap(); +fn cannot_self_destruct_through_draning() { + let (wasm, _code_hash) = compile_module::("drain").unwrap(); ExtBuilder::default().existential_deposit(200).build().execute_with(|| { let _ = Balances::deposit_creating(&ALICE, 1_000_000); - let min_balance = ::Currency::minimum_balance(); // Instantiate the BOB contract. let addr = Contracts::bare_instantiate( ALICE, - 0, + 1_000, GAS_LIMIT, None, Code::Upload(wasm), @@ -1063,50 +1082,36 @@ fn cannot_self_destruct_through_storage_refund_after_price_change() { .unwrap() .account_id; - // Check that the BOB contract has been instantiated and has the minimum balance - assert_eq!(get_contract(&addr).total_deposit(), min_balance); - assert_eq!(get_contract(&addr).extra_deposit(), 0); - assert_eq!(::Currency::total_balance(&addr), min_balance); - - // Create 100 bytes of storage with a price of per byte and a single storage item of price 2 - assert_ok!(Contracts::call( - RuntimeOrigin::signed(ALICE), - addr.clone(), - 0, - GAS_LIMIT, - None, - 100u32.to_le_bytes().to_vec() - )); - assert_eq!(get_contract(&addr).total_deposit(), min_balance + 102); + // Check that the BOB contract has been instantiated. + get_contract(&addr); - // Increase the byte price and trigger a refund. This should not have any influence because - // the removal is pro rata and exactly those 100 bytes should have been removed. - DEPOSIT_PER_BYTE.with(|c| *c.borrow_mut() = 500); + // Call BOB which makes it send all funds to the zero address + // The contract code asserts that the transfer fails with the correct error code assert_ok!(Contracts::call( RuntimeOrigin::signed(ALICE), addr.clone(), 0, GAS_LIMIT, None, - 0u32.to_le_bytes().to_vec() + vec![] )); - // Make sure the account wasn't removed by the refund + // Make sure the account wasn't remove by sending all free balance away. assert_eq!( ::Currency::total_balance(&addr), - get_contract(&addr).total_deposit(), + 1_000 + ::Currency::minimum_balance(), ); - assert_eq!(get_contract(&addr).extra_deposit(), 2,); }); } #[test] -fn cannot_self_destruct_by_refund_after_slash() { +fn cannot_self_destruct_through_storage_refund_after_price_change() { let (wasm, _code_hash) = compile_module::("store").unwrap(); - ExtBuilder::default().existential_deposit(500).build().execute_with(|| { + ExtBuilder::default().existential_deposit(200).build().execute_with(|| { let _ = Balances::deposit_creating(&ALICE, 1_000_000); let min_balance = ::Currency::minimum_balance(); + // Instantiate the BOB contract. let addr = Contracts::bare_instantiate( ALICE, 0, @@ -1121,68 +1126,40 @@ fn cannot_self_destruct_by_refund_after_slash() { .unwrap() .account_id; - // create 100 more reserved balance + // Check that the BOB contract has been instantiated and has the minimum balance + assert_eq!(get_contract(&addr).total_deposit(), min_balance); + assert_eq!(get_contract(&addr).extra_deposit(), 0); + assert_eq!(::Currency::total_balance(&addr), min_balance); + + // Create 100 bytes of storage with a price of per byte and a single storage item of price 2 assert_ok!(Contracts::call( RuntimeOrigin::signed(ALICE), addr.clone(), 0, GAS_LIMIT, None, - 98u32.encode(), + 100u32.to_le_bytes().to_vec() )); + assert_eq!(get_contract(&addr).total_deposit(), min_balance + 102); - // Drop previous events - initialize_block(2); - - // slash parts of the 100 so that the next refund ould remove the account - // because it the value it stored for `storage_deposit` becomes out of sync - let _ = ::Currency::slash(&addr, 90); - assert_eq!(::Currency::total_balance(&addr), min_balance + 10); - - // trigger a refund of 50 which would bring the contract below min when actually refunded + // Increase the byte price and trigger a refund. This should not have any influence because + // the removal is pro rata and exactly those 100 bytes should have been removed. + DEPOSIT_PER_BYTE.with(|c| *c.borrow_mut() = 500); assert_ok!(Contracts::call( RuntimeOrigin::signed(ALICE), addr.clone(), 0, GAS_LIMIT, None, - 48u32.encode(), + 0u32.to_le_bytes().to_vec() )); - // Make sure the account kept the minimum balance and was not destroyed - assert_eq!(::Currency::total_balance(&addr), min_balance); - + // Make sure the account wasn't removed by the refund assert_eq!( - System::events(), - vec![ - EventRecord { - phase: Phase::Initialization, - event: RuntimeEvent::Balances(pallet_balances::Event::Slashed { - who: addr.clone(), - amount: 90, - }), - topics: vec![], - }, - EventRecord { - phase: Phase::Initialization, - event: RuntimeEvent::Contracts(crate::Event::Called { - caller: ALICE, - contract: addr.clone(), - }), - topics: vec![hash(&ALICE), hash(&addr)], - }, - EventRecord { - phase: Phase::Initialization, - event: RuntimeEvent::Balances(pallet_balances::Event::ReserveRepatriated { - from: addr.clone(), - to: ALICE, - amount: 10, - destination_status: BalanceStatus::Free, - }), - topics: vec![], - }, - ] + ::Currency::total_balance(get_contract(&addr).deposit_account()), + get_contract(&addr).total_deposit(), ); + assert_eq!(get_contract(&addr).extra_deposit(), 2); }); } @@ -1252,7 +1229,7 @@ fn self_destruct_works() { .account_id; // Check that the BOB contract has been instantiated. - get_contract(&addr); + let contract = get_contract(&addr); // Drop all previous events initialize_block(2); @@ -1271,17 +1248,25 @@ fn self_destruct_works() { assert_eq!(Balances::total_balance(&addr), 0); // check that the beneficiary (django) got remaining balance - assert_eq!(Balances::free_balance(DJANGO), 1_000_000 + 100_000); + let ed = ::Currency::minimum_balance(); + assert_eq!(Balances::free_balance(DJANGO), 1_000_000 + 100_000 + ed); pretty_assertions::assert_eq!( System::events(), vec![ + EventRecord { + phase: Phase::Initialization, + event: RuntimeEvent::System(frame_system::Event::KilledAccount { + account: addr.clone() + }), + topics: vec![], + }, EventRecord { phase: Phase::Initialization, event: RuntimeEvent::Balances(pallet_balances::Event::Transfer { from: addr.clone(), to: DJANGO, - amount: 100_000, + amount: 100_000 + ed, }), topics: vec![], }, @@ -1304,17 +1289,16 @@ fn self_destruct_works() { EventRecord { phase: Phase::Initialization, event: RuntimeEvent::System(frame_system::Event::KilledAccount { - account: addr.clone() + account: contract.deposit_account().deref().clone(), }), topics: vec![], }, EventRecord { phase: Phase::Initialization, - event: RuntimeEvent::Balances(pallet_balances::Event::ReserveRepatriated { - from: addr.clone(), + event: RuntimeEvent::Balances(pallet_balances::Event::Transfer { + from: contract.deposit_account().deref().clone(), to: ALICE, amount: 1_000, - destination_status: BalanceStatus::Free, }), topics: vec![], }, @@ -2055,7 +2039,7 @@ fn lazy_removal_on_full_queue_works_on_initialize() { ExtBuilder::default().existential_deposit(50).build().execute_with(|| { // Fill the deletion queue with dummy values, so that on_initialize attempts // to clear the queue - Storage::::fill_queue_with_dummies(); + ContractInfo::::fill_queue_with_dummies(); let queue_len_initial = >::decode_len().unwrap_or(0); @@ -2132,7 +2116,7 @@ fn lazy_removal_partial_remove_works() { // We create a contract with some extra keys above the weight limit let extra_keys = 7u32; let weight_limit = Weight::from_ref_time(5_000_000_000); - let (_, max_keys) = Storage::::deletion_budget(1, weight_limit); + let (_, max_keys) = ContractInfo::::deletion_budget(1, weight_limit); let vals: Vec<_> = (0..max_keys + extra_keys) .map(|i| (blake2_256(&i.encode()), (i as u32), (i as u32).encode())) .collect(); @@ -2161,14 +2145,7 @@ fn lazy_removal_partial_remove_works() { // Put value into the contracts child trie for val in &vals { - Storage::::write( - &info.trie_id, - &val.0 as &FixSizedKey, - Some(val.2.clone()), - None, - false, - ) - .unwrap(); + info.write(&val.0 as &FixSizedKey, Some(val.2.clone()), None, false).unwrap(); } >::insert(&addr, info.clone()); @@ -2201,7 +2178,7 @@ fn lazy_removal_partial_remove_works() { ext.execute_with(|| { // Run the lazy removal - let weight_used = Storage::::process_deletion_queue_batch(weight_limit); + let weight_used = ContractInfo::::process_deletion_queue_batch(weight_limit); // Weight should be exhausted because we could not even delete all keys assert_eq!(weight_used, weight_limit); @@ -2235,7 +2212,7 @@ fn lazy_removal_does_no_run_on_full_queue_and_full_block() { // Fill the deletion queue with dummy values, so that on_initialize attempts // to clear the queue - Storage::::fill_queue_with_dummies(); + ContractInfo::::fill_queue_with_dummies(); // Check that on_initialize() tries to perform lazy removal but removes nothing // as no more weight is left for that. @@ -2344,7 +2321,7 @@ fn lazy_removal_does_not_use_all_weight() { .account_id; let info = get_contract(&addr); - let (weight_per_key, max_keys) = Storage::::deletion_budget(1, weight_limit); + let (weight_per_key, max_keys) = ContractInfo::::deletion_budget(1, weight_limit); // We create a contract with one less storage item than we can remove within the limit let vals: Vec<_> = (0..max_keys - 1) @@ -2353,14 +2330,7 @@ fn lazy_removal_does_not_use_all_weight() { // Put value into the contracts child trie for val in &vals { - Storage::::write( - &info.trie_id, - &val.0 as &FixSizedKey, - Some(val.2.clone()), - None, - false, - ) - .unwrap(); + info.write(&val.0 as &FixSizedKey, Some(val.2.clone()), None, false).unwrap(); } >::insert(&addr, info.clone()); @@ -2393,7 +2363,7 @@ fn lazy_removal_does_not_use_all_weight() { ext.execute_with(|| { // Run the lazy removal - let weight_used = Storage::::process_deletion_queue_batch(weight_limit); + let weight_used = ContractInfo::::process_deletion_queue_batch(weight_limit); // We have one less key in our trie than our weight limit suffices for assert_eq!(weight_used, weight_limit - weight_per_key); @@ -2427,7 +2397,7 @@ fn deletion_queue_full() { .account_id; // fill the deletion queue up until its limit - Storage::::fill_queue_with_dummies(); + ContractInfo::::fill_queue_with_dummies(); // Terminate the contract should fail assert_err_ignore_postinfo!( @@ -3196,14 +3166,12 @@ fn instantiate_with_zero_balance_works() { .account_id; // Check that the BOB contract has been instantiated. - get_contract(&addr); + let contract = get_contract(&addr); + let deposit_account = contract.deposit_account().deref(); // Make sure the account exists even though no free balance was send - assert_eq!(::Currency::free_balance(&addr), 0,); - assert_eq!( - ::Currency::total_balance(&addr), - ::Currency::minimum_balance(), - ); + assert_eq!(::Currency::free_balance(&addr), min_balance); + assert_eq!(::Currency::total_balance(&addr), min_balance,); assert_eq!( System::events(), @@ -3211,14 +3179,14 @@ fn instantiate_with_zero_balance_works() { EventRecord { phase: Phase::Initialization, event: RuntimeEvent::System(frame_system::Event::NewAccount { - account: addr.clone() + account: deposit_account.clone(), }), topics: vec![], }, EventRecord { phase: Phase::Initialization, event: RuntimeEvent::Balances(pallet_balances::Event::Endowed { - account: addr.clone(), + account: deposit_account.clone(), free_balance: min_balance, }), topics: vec![], @@ -3227,15 +3195,31 @@ fn instantiate_with_zero_balance_works() { phase: Phase::Initialization, event: RuntimeEvent::Balances(pallet_balances::Event::Transfer { from: ALICE, - to: addr.clone(), + to: deposit_account.clone(), amount: min_balance, }), topics: vec![], }, EventRecord { phase: Phase::Initialization, - event: RuntimeEvent::Balances(pallet_balances::Event::Reserved { - who: addr.clone(), + event: RuntimeEvent::System(frame_system::Event::NewAccount { + account: addr.clone(), + }), + topics: vec![], + }, + EventRecord { + phase: Phase::Initialization, + event: RuntimeEvent::Balances(pallet_balances::Event::Endowed { + account: addr.clone(), + free_balance: min_balance, + }), + topics: vec![], + }, + EventRecord { + phase: Phase::Initialization, + event: RuntimeEvent::Balances(pallet_balances::Event::Transfer { + from: ALICE, + to: addr.clone(), amount: min_balance, }), topics: vec![], @@ -3292,14 +3276,12 @@ fn instantiate_with_below_existential_deposit_works() { .account_id; // Check that the BOB contract has been instantiated. - get_contract(&addr); + let contract = get_contract(&addr); + let deposit_account = contract.deposit_account().deref(); - // Make sure the account exists even though no free balance was send - assert_eq!(::Currency::free_balance(&addr), 50,); - assert_eq!( - ::Currency::total_balance(&addr), - ::Currency::minimum_balance() + 50, - ); + // Make sure the account exists even though not enough free balance was send + assert_eq!(::Currency::free_balance(&addr), min_balance + 50); + assert_eq!(::Currency::total_balance(&addr), min_balance + 50); assert_eq!( System::events(), @@ -3307,14 +3289,14 @@ fn instantiate_with_below_existential_deposit_works() { EventRecord { phase: Phase::Initialization, event: RuntimeEvent::System(frame_system::Event::NewAccount { - account: addr.clone() + account: deposit_account.clone(), }), topics: vec![], }, EventRecord { phase: Phase::Initialization, event: RuntimeEvent::Balances(pallet_balances::Event::Endowed { - account: addr.clone(), + account: deposit_account.clone(), free_balance: min_balance, }), topics: vec![], @@ -3323,15 +3305,31 @@ fn instantiate_with_below_existential_deposit_works() { phase: Phase::Initialization, event: RuntimeEvent::Balances(pallet_balances::Event::Transfer { from: ALICE, - to: addr.clone(), + to: deposit_account.clone(), amount: min_balance, }), topics: vec![], }, EventRecord { phase: Phase::Initialization, - event: RuntimeEvent::Balances(pallet_balances::Event::Reserved { - who: addr.clone(), + event: RuntimeEvent::System(frame_system::Event::NewAccount { + account: addr.clone() + }), + topics: vec![], + }, + EventRecord { + phase: Phase::Initialization, + event: RuntimeEvent::Balances(pallet_balances::Event::Endowed { + account: addr.clone(), + free_balance: min_balance, + }), + topics: vec![], + }, + EventRecord { + phase: Phase::Initialization, + event: RuntimeEvent::Balances(pallet_balances::Event::Transfer { + from: ALICE, + to: addr.clone(), amount: min_balance, }), topics: vec![], @@ -3436,6 +3434,9 @@ fn storage_deposit_works() { deposit -= refunded0; assert_eq!(get_contract(&addr).total_deposit(), deposit); + let contract = get_contract(&addr); + let deposit_account = contract.deposit_account().deref(); + assert_eq!( System::events(), vec![ @@ -3460,15 +3461,7 @@ fn storage_deposit_works() { phase: Phase::Initialization, event: RuntimeEvent::Balances(pallet_balances::Event::Transfer { from: ALICE, - to: addr.clone(), - amount: charged0, - }), - topics: vec![], - }, - EventRecord { - phase: Phase::Initialization, - event: RuntimeEvent::Balances(pallet_balances::Event::Reserved { - who: addr.clone(), + to: deposit_account.clone(), amount: charged0, }), topics: vec![], @@ -3485,15 +3478,7 @@ fn storage_deposit_works() { phase: Phase::Initialization, event: RuntimeEvent::Balances(pallet_balances::Event::Transfer { from: ALICE, - to: addr.clone(), - amount: charged1, - }), - topics: vec![], - }, - EventRecord { - phase: Phase::Initialization, - event: RuntimeEvent::Balances(pallet_balances::Event::Reserved { - who: addr.clone(), + to: deposit_account.clone(), amount: charged1, }), topics: vec![], @@ -3508,11 +3493,10 @@ fn storage_deposit_works() { }, EventRecord { phase: Phase::Initialization, - event: RuntimeEvent::Balances(pallet_balances::Event::ReserveRepatriated { - from: addr.clone(), + event: RuntimeEvent::Balances(pallet_balances::Event::Transfer { + from: deposit_account.clone(), to: ALICE, amount: refunded0, - destination_status: BalanceStatus::Free, }), topics: vec![], }, @@ -3610,11 +3594,10 @@ fn set_code_extrinsic() { } #[test] -fn call_after_killed_account_needs_funding() { +fn slash_cannot_kill_account() { let (wasm, _code_hash) = compile_module::("dummy").unwrap(); ExtBuilder::default().existential_deposit(200).build().execute_with(|| { let _ = Balances::deposit_creating(&ALICE, 1_000_000); - let min_balance = ::Currency::minimum_balance(); let addr = Contracts::bare_instantiate( ALICE, @@ -3633,107 +3616,24 @@ fn call_after_killed_account_needs_funding() { // Drop previous events initialize_block(2); - // Destroy the account of the contract by slashing. - // Slashing can actually happen if the contract takes part in staking. - // It is a corner case and we accept the destruction of the account. + // Try to destroy the account of the contract by slashing. + // The account does not get destroyed because of the consumer reference. + // Slashing can for example happen if the contract takes part in staking. let _ = ::Currency::slash( &addr, ::Currency::total_balance(&addr), ); - // Sending below the minimum balance will fail the call because it needs to create the - // account in order to send balance there. - assert_err_ignore_postinfo!( - Contracts::call( - RuntimeOrigin::signed(ALICE), - addr.clone(), - min_balance - 1, - GAS_LIMIT, - None, - vec![], - ), - >::TransferFailed - ); - - // Sending zero should work as it does not do a transfer - assert_ok!(Contracts::call( - RuntimeOrigin::signed(ALICE), - addr.clone(), - 0, - GAS_LIMIT, - None, - vec![], - )); - - // Sending minimum balance should work - assert_ok!(Contracts::call( - RuntimeOrigin::signed(ALICE), - addr.clone(), - min_balance, - GAS_LIMIT, - None, - vec![], - )); - assert_eq!( System::events(), - vec![ - EventRecord { - phase: Phase::Initialization, - event: RuntimeEvent::System(frame_system::Event::KilledAccount { - account: addr.clone() - }), - topics: vec![], - }, - EventRecord { - phase: Phase::Initialization, - event: RuntimeEvent::Balances(pallet_balances::Event::Slashed { - who: addr.clone(), - amount: min_balance + 700 - }), - topics: vec![], - }, - EventRecord { - phase: Phase::Initialization, - event: RuntimeEvent::Contracts(crate::Event::Called { - caller: ALICE, - contract: addr.clone(), - }), - topics: vec![hash(&ALICE), hash(&addr)], - }, - EventRecord { - phase: Phase::Initialization, - event: RuntimeEvent::System(frame_system::Event::NewAccount { - account: addr.clone() - }), - topics: vec![], - }, - EventRecord { - phase: Phase::Initialization, - event: RuntimeEvent::Balances(pallet_balances::Event::Endowed { - account: addr.clone(), - free_balance: min_balance - }), - topics: vec![], - }, - EventRecord { - phase: Phase::Initialization, - event: RuntimeEvent::Balances(pallet_balances::Event::Transfer { - from: ALICE, - to: addr.clone(), - amount: min_balance - }), - topics: vec![], - }, - EventRecord { - phase: Phase::Initialization, - event: RuntimeEvent::Contracts(crate::Event::Called { - caller: ALICE, - contract: addr.clone(), - }), - topics: vec![hash(&ALICE), hash(&addr)], - }, - ] + vec![EventRecord { + phase: Phase::Initialization, + event: RuntimeEvent::Balances(pallet_balances::Event::Slashed { + who: addr.clone(), + amount: 700, // slash didn't remove the minimum balance + }), + topics: vec![], + },] ); }); } diff --git a/frame/contracts/src/weights.rs b/frame/contracts/src/weights.rs index 057a9257d..d03261584 100644 --- a/frame/contracts/src/weights.rs +++ b/frame/contracts/src/weights.rs @@ -18,7 +18,7 @@ //! Autogenerated weights for pallet_contracts //! //! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev -//! DATE: 2023-02-15, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! DATE: 2023-02-16, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` //! WORST CASE MAP SIZE: `1000000` //! HOSTNAME: `bm3`, CPU: `Intel(R) Core(TM) i7-7700K CPU @ 4.20GHz` //! EXECUTION: Some(Wasm), WASM-EXECUTION: Compiled, CHAIN: Some("dev"), DB CACHE: 1024 @@ -178,8 +178,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `109` // Estimated: `604` - // Minimum execution time: 2_507 nanoseconds. - Weight::from_ref_time(2_650_000) + // Minimum execution time: 2_591 nanoseconds. + Weight::from_ref_time(2_817_000) .saturating_add(Weight::from_proof_size(604)) .saturating_add(T::DbWeight::get().reads(1_u64)) } @@ -190,11 +190,11 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `481 + k * (69 ±0)` // Estimated: `471 + k * (70 ±0)` - // Minimum execution time: 9_953 nanoseconds. - Weight::from_ref_time(6_467_352) + // Minimum execution time: 10_190 nanoseconds. + Weight::from_ref_time(6_642_117) .saturating_add(Weight::from_proof_size(471)) - // Standard Error: 1_074 - .saturating_add(Weight::from_ref_time(943_946).saturating_mul(k.into())) + // Standard Error: 992 + .saturating_add(Weight::from_ref_time(919_828).saturating_mul(k.into())) .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) .saturating_add(T::DbWeight::get().writes((1_u64).saturating_mul(k.into()))) @@ -207,11 +207,11 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `281 + q * (33 ±0)` // Estimated: `763 + q * (33 ±0)` - // Minimum execution time: 2_545 nanoseconds. - Weight::from_ref_time(10_080_106) + // Minimum execution time: 2_598 nanoseconds. + Weight::from_ref_time(10_288_252) .saturating_add(Weight::from_proof_size(763)) - // Standard Error: 2_929 - .saturating_add(Weight::from_ref_time(1_078_265).saturating_mul(q.into())) + // Standard Error: 2_886 + .saturating_add(Weight::from_ref_time(1_092_420).saturating_mul(q.into())) .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) .saturating_add(Weight::from_proof_size(33).saturating_mul(q.into())) @@ -225,17 +225,17 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `270 + c * (1 ±0)` // Estimated: `3025 + c * (2 ±0)` - // Minimum execution time: 34_613 nanoseconds. - Weight::from_ref_time(27_355_774) + // Minimum execution time: 34_338 nanoseconds. + Weight::from_ref_time(32_159_677) .saturating_add(Weight::from_proof_size(3025)) - // Standard Error: 81 - .saturating_add(Weight::from_ref_time(51_954).saturating_mul(c.into())) + // Standard Error: 53 + .saturating_add(Weight::from_ref_time(51_034).saturating_mul(c.into())) .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) .saturating_add(Weight::from_proof_size(2).saturating_mul(c.into())) } /// Storage: Contracts ContractInfoOf (r:1 w:1) - /// Proof: Contracts ContractInfoOf (max_values: None, max_size: Some(258), added: 2733, mode: Measured) + /// Proof: Contracts ContractInfoOf (max_values: None, max_size: Some(290), added: 2765, mode: Measured) /// Storage: Contracts CodeStorage (r:1 w:0) /// Proof: Contracts CodeStorage (max_values: None, max_size: Some(126001), added: 128476, mode: Measured) /// Storage: Timestamp Now (r:1 w:0) @@ -247,13 +247,13 @@ impl WeightInfo for SubstrateWeight { /// The range of component `c` is `[0, 125952]`. fn call_with_code_per_byte(c: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `771` - // Estimated: `16780 + c * (5 ±0)` - // Minimum execution time: 386_104 nanoseconds. - Weight::from_ref_time(396_718_823) - .saturating_add(Weight::from_proof_size(16780)) + // Measured: `803` + // Estimated: `16930 + c * (5 ±0)` + // Minimum execution time: 385_587 nanoseconds. + Weight::from_ref_time(395_545_811) + .saturating_add(Weight::from_proof_size(16930)) // Standard Error: 27 - .saturating_add(Weight::from_ref_time(31_370).saturating_mul(c.into())) + .saturating_add(Weight::from_ref_time(31_342).saturating_mul(c.into())) .saturating_add(T::DbWeight::get().reads(6_u64)) .saturating_add(T::DbWeight::get().writes(4_u64)) .saturating_add(Weight::from_proof_size(5).saturating_mul(c.into())) @@ -263,10 +263,10 @@ impl WeightInfo for SubstrateWeight { /// Storage: Contracts Nonce (r:1 w:1) /// Proof: Contracts Nonce (max_values: Some(1), max_size: Some(8), added: 503, mode: Measured) /// Storage: Contracts ContractInfoOf (r:1 w:1) - /// Proof: Contracts ContractInfoOf (max_values: None, max_size: Some(258), added: 2733, mode: Measured) + /// Proof: Contracts ContractInfoOf (max_values: None, max_size: Some(290), added: 2765, mode: Measured) /// Storage: Timestamp Now (r:1 w:0) /// Proof: Timestamp Now (max_values: Some(1), max_size: Some(8), added: 503, mode: Measured) - /// Storage: System Account (r:1 w:1) + /// Storage: System Account (r:2 w:2) /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: Measured) /// Storage: System EventTopics (r:3 w:3) /// Proof Skipped: System EventTopics (max_values: None, max_size: None, mode: Measured) @@ -279,29 +279,29 @@ impl WeightInfo for SubstrateWeight { /// The range of component `s` is `[0, 1048576]`. fn instantiate_with_code(c: u32, i: u32, s: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `257` - // Estimated: `17752` - // Minimum execution time: 3_786_500 nanoseconds. - Weight::from_ref_time(672_511_565) - .saturating_add(Weight::from_proof_size(17752)) - // Standard Error: 281 - .saturating_add(Weight::from_ref_time(94_538).saturating_mul(c.into())) + // Measured: `270` + // Estimated: `20267` + // Minimum execution time: 3_799_742 nanoseconds. + Weight::from_ref_time(670_115_588) + .saturating_add(Weight::from_proof_size(20267)) + // Standard Error: 287 + .saturating_add(Weight::from_ref_time(93_885).saturating_mul(c.into())) // Standard Error: 16 - .saturating_add(Weight::from_ref_time(1_364).saturating_mul(i.into())) + .saturating_add(Weight::from_ref_time(1_367).saturating_mul(i.into())) // Standard Error: 16 - .saturating_add(Weight::from_ref_time(1_768).saturating_mul(s.into())) - .saturating_add(T::DbWeight::get().reads(8_u64)) - .saturating_add(T::DbWeight::get().writes(9_u64)) + .saturating_add(Weight::from_ref_time(1_781).saturating_mul(s.into())) + .saturating_add(T::DbWeight::get().reads(9_u64)) + .saturating_add(T::DbWeight::get().writes(10_u64)) } /// Storage: Contracts CodeStorage (r:1 w:0) /// Proof: Contracts CodeStorage (max_values: None, max_size: Some(126001), added: 128476, mode: Measured) /// Storage: Contracts Nonce (r:1 w:1) /// Proof: Contracts Nonce (max_values: Some(1), max_size: Some(8), added: 503, mode: Measured) /// Storage: Contracts ContractInfoOf (r:1 w:1) - /// Proof: Contracts ContractInfoOf (max_values: None, max_size: Some(258), added: 2733, mode: Measured) + /// Proof: Contracts ContractInfoOf (max_values: None, max_size: Some(290), added: 2765, mode: Measured) /// Storage: Timestamp Now (r:1 w:0) /// Proof: Timestamp Now (max_values: Some(1), max_size: Some(8), added: 503, mode: Measured) - /// Storage: System Account (r:1 w:1) + /// Storage: System Account (r:2 w:2) /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: Measured) /// Storage: Contracts OwnerInfoOf (r:1 w:1) /// Proof: Contracts OwnerInfoOf (max_values: None, max_size: Some(88), added: 2563, mode: Measured) @@ -311,20 +311,20 @@ impl WeightInfo for SubstrateWeight { /// The range of component `s` is `[0, 1048576]`. fn instantiate(i: u32, s: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `533` - // Estimated: `19543` - // Minimum execution time: 1_927_365 nanoseconds. - Weight::from_ref_time(189_843_928) - .saturating_add(Weight::from_proof_size(19543)) + // Measured: `546` + // Estimated: `22039` + // Minimum execution time: 1_949_008 nanoseconds. + Weight::from_ref_time(214_033_418) + .saturating_add(Weight::from_proof_size(22039)) // Standard Error: 8 - .saturating_add(Weight::from_ref_time(1_677).saturating_mul(i.into())) + .saturating_add(Weight::from_ref_time(1_666).saturating_mul(i.into())) // Standard Error: 8 - .saturating_add(Weight::from_ref_time(1_803).saturating_mul(s.into())) - .saturating_add(T::DbWeight::get().reads(8_u64)) - .saturating_add(T::DbWeight::get().writes(6_u64)) + .saturating_add(Weight::from_ref_time(1_801).saturating_mul(s.into())) + .saturating_add(T::DbWeight::get().reads(9_u64)) + .saturating_add(T::DbWeight::get().writes(7_u64)) } /// Storage: Contracts ContractInfoOf (r:1 w:1) - /// Proof: Contracts ContractInfoOf (max_values: None, max_size: Some(258), added: 2733, mode: Measured) + /// Proof: Contracts ContractInfoOf (max_values: None, max_size: Some(290), added: 2765, mode: Measured) /// Storage: Contracts CodeStorage (r:1 w:0) /// Proof: Contracts CodeStorage (max_values: None, max_size: Some(126001), added: 128476, mode: Measured) /// Storage: Timestamp Now (r:1 w:0) @@ -335,11 +335,11 @@ impl WeightInfo for SubstrateWeight { /// Proof Skipped: System EventTopics (max_values: None, max_size: None, mode: Measured) fn call() -> Weight { // Proof Size summary in bytes: - // Measured: `823` - // Estimated: `16985` - // Minimum execution time: 146_354 nanoseconds. - Weight::from_ref_time(147_693_000) - .saturating_add(Weight::from_proof_size(16985)) + // Measured: `855` + // Estimated: `17145` + // Minimum execution time: 146_654 nanoseconds. + Weight::from_ref_time(147_528_000) + .saturating_add(Weight::from_proof_size(17145)) .saturating_add(T::DbWeight::get().reads(6_u64)) .saturating_add(T::DbWeight::get().writes(4_u64)) } @@ -356,11 +356,11 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `109` // Estimated: `5386` - // Minimum execution time: 388_107 nanoseconds. - Weight::from_ref_time(383_172_702) + // Minimum execution time: 387_889 nanoseconds. + Weight::from_ref_time(391_379_335) .saturating_add(Weight::from_proof_size(5386)) - // Standard Error: 73 - .saturating_add(Weight::from_ref_time(95_037).saturating_mul(c.into())) + // Standard Error: 89 + .saturating_add(Weight::from_ref_time(94_810).saturating_mul(c.into())) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().writes(4_u64)) } @@ -376,32 +376,32 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `287` // Estimated: `6098` - // Minimum execution time: 26_278 nanoseconds. - Weight::from_ref_time(26_682_000) + // Minimum execution time: 26_014 nanoseconds. + Weight::from_ref_time(26_510_000) .saturating_add(Weight::from_proof_size(6098)) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().writes(4_u64)) } /// Storage: Contracts ContractInfoOf (r:1 w:1) - /// Proof: Contracts ContractInfoOf (max_values: None, max_size: Some(258), added: 2733, mode: Measured) + /// Proof: Contracts ContractInfoOf (max_values: None, max_size: Some(290), added: 2765, mode: Measured) /// Storage: Contracts OwnerInfoOf (r:2 w:2) /// Proof: Contracts OwnerInfoOf (max_values: None, max_size: Some(88), added: 2563, mode: Measured) /// Storage: System EventTopics (r:3 w:3) /// Proof Skipped: System EventTopics (max_values: None, max_size: None, mode: Measured) fn set_code() -> Weight { // Proof Size summary in bytes: - // Measured: `634` - // Estimated: `16752` - // Minimum execution time: 30_223 nanoseconds. - Weight::from_ref_time(30_737_000) - .saturating_add(Weight::from_proof_size(16752)) + // Measured: `666` + // Estimated: `16848` + // Minimum execution time: 30_177 nanoseconds. + Weight::from_ref_time(30_639_000) + .saturating_add(Weight::from_proof_size(16848)) .saturating_add(T::DbWeight::get().reads(6_u64)) .saturating_add(T::DbWeight::get().writes(6_u64)) } /// Storage: System Account (r:1 w:0) /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: Measured) /// Storage: Contracts ContractInfoOf (r:1 w:1) - /// Proof: Contracts ContractInfoOf (max_values: None, max_size: Some(258), added: 2733, mode: Measured) + /// Proof: Contracts ContractInfoOf (max_values: None, max_size: Some(290), added: 2765, mode: Measured) /// Storage: Contracts CodeStorage (r:1 w:0) /// Proof: Contracts CodeStorage (max_values: None, max_size: Some(126001), added: 128476, mode: Measured) /// Storage: Timestamp Now (r:1 w:0) @@ -411,13 +411,13 @@ impl WeightInfo for SubstrateWeight { /// The range of component `r` is `[0, 20]`. fn seal_caller(r: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `845 + r * (480 ±0)` - // Estimated: `17120 + r * (2400 ±0)` - // Minimum execution time: 374_780 nanoseconds. - Weight::from_ref_time(379_468_453) - .saturating_add(Weight::from_proof_size(17120)) - // Standard Error: 45_809 - .saturating_add(Weight::from_ref_time(17_553_577).saturating_mul(r.into())) + // Measured: `877 + r * (480 ±0)` + // Estimated: `17295 + r * (2400 ±0)` + // Minimum execution time: 373_786 nanoseconds. + Weight::from_ref_time(377_332_691) + .saturating_add(Weight::from_proof_size(17295)) + // Standard Error: 51_211 + .saturating_add(Weight::from_ref_time(17_715_615).saturating_mul(r.into())) .saturating_add(T::DbWeight::get().reads(6_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) .saturating_add(Weight::from_proof_size(2400).saturating_mul(r.into())) @@ -425,7 +425,7 @@ impl WeightInfo for SubstrateWeight { /// Storage: System Account (r:1 w:0) /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: Measured) /// Storage: Contracts ContractInfoOf (r:1601 w:1) - /// Proof: Contracts ContractInfoOf (max_values: None, max_size: Some(258), added: 2733, mode: Measured) + /// Proof: Contracts ContractInfoOf (max_values: None, max_size: Some(290), added: 2765, mode: Measured) /// Storage: Contracts CodeStorage (r:1 w:0) /// Proof: Contracts CodeStorage (max_values: None, max_size: Some(126001), added: 128476, mode: Measured) /// Storage: Timestamp Now (r:1 w:0) @@ -435,22 +435,22 @@ impl WeightInfo for SubstrateWeight { /// The range of component `r` is `[0, 20]`. fn seal_is_contract(r: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `882 + r * (19218 ±0)` - // Estimated: `17110 + r * (294100 ±0)` - // Minimum execution time: 374_399 nanoseconds. - Weight::from_ref_time(228_569_211) - .saturating_add(Weight::from_proof_size(17110)) - // Standard Error: 492_562 - .saturating_add(Weight::from_ref_time(251_682_897).saturating_mul(r.into())) + // Measured: `917 + r * (21778 ±0)` + // Estimated: `17295 + r * (306895 ±0)` + // Minimum execution time: 374_009 nanoseconds. + Weight::from_ref_time(238_991_986) + .saturating_add(Weight::from_proof_size(17295)) + // Standard Error: 464_711 + .saturating_add(Weight::from_ref_time(249_099_538).saturating_mul(r.into())) .saturating_add(T::DbWeight::get().reads(6_u64)) .saturating_add(T::DbWeight::get().reads((80_u64).saturating_mul(r.into()))) .saturating_add(T::DbWeight::get().writes(3_u64)) - .saturating_add(Weight::from_proof_size(294100).saturating_mul(r.into())) + .saturating_add(Weight::from_proof_size(306895).saturating_mul(r.into())) } /// Storage: System Account (r:1 w:0) /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: Measured) /// Storage: Contracts ContractInfoOf (r:1601 w:1) - /// Proof: Contracts ContractInfoOf (max_values: None, max_size: Some(258), added: 2733, mode: Measured) + /// Proof: Contracts ContractInfoOf (max_values: None, max_size: Some(290), added: 2765, mode: Measured) /// Storage: Contracts CodeStorage (r:1 w:0) /// Proof: Contracts CodeStorage (max_values: None, max_size: Some(126001), added: 128476, mode: Measured) /// Storage: Timestamp Now (r:1 w:0) @@ -460,22 +460,22 @@ impl WeightInfo for SubstrateWeight { /// The range of component `r` is `[0, 20]`. fn seal_code_hash(r: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `889 + r * (19539 ±0)` - // Estimated: `17170 + r * (295700 ±0)` - // Minimum execution time: 375_980 nanoseconds. - Weight::from_ref_time(232_604_331) - .saturating_add(Weight::from_proof_size(17170)) - // Standard Error: 491_912 - .saturating_add(Weight::from_ref_time(299_479_335).saturating_mul(r.into())) + // Measured: `921 + r * (22099 ±0)` + // Estimated: `17340 + r * (308500 ±0)` + // Minimum execution time: 375_058 nanoseconds. + Weight::from_ref_time(238_765_068) + .saturating_add(Weight::from_proof_size(17340)) + // Standard Error: 662_617 + .saturating_add(Weight::from_ref_time(302_175_089).saturating_mul(r.into())) .saturating_add(T::DbWeight::get().reads(6_u64)) .saturating_add(T::DbWeight::get().reads((80_u64).saturating_mul(r.into()))) .saturating_add(T::DbWeight::get().writes(3_u64)) - .saturating_add(Weight::from_proof_size(295700).saturating_mul(r.into())) + .saturating_add(Weight::from_proof_size(308500).saturating_mul(r.into())) } /// Storage: System Account (r:1 w:0) /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: Measured) /// Storage: Contracts ContractInfoOf (r:1 w:1) - /// Proof: Contracts ContractInfoOf (max_values: None, max_size: Some(258), added: 2733, mode: Measured) + /// Proof: Contracts ContractInfoOf (max_values: None, max_size: Some(290), added: 2765, mode: Measured) /// Storage: Contracts CodeStorage (r:1 w:0) /// Proof: Contracts CodeStorage (max_values: None, max_size: Some(126001), added: 128476, mode: Measured) /// Storage: Timestamp Now (r:1 w:0) @@ -485,13 +485,13 @@ impl WeightInfo for SubstrateWeight { /// The range of component `r` is `[0, 20]`. fn seal_own_code_hash(r: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `852 + r * (480 ±0)` - // Estimated: `17155 + r * (2400 ±0)` - // Minimum execution time: 375_075 nanoseconds. - Weight::from_ref_time(381_217_372) - .saturating_add(Weight::from_proof_size(17155)) - // Standard Error: 63_950 - .saturating_add(Weight::from_ref_time(21_872_316).saturating_mul(r.into())) + // Measured: `884 + r * (480 ±0)` + // Estimated: `17330 + r * (2400 ±0)` + // Minimum execution time: 374_747 nanoseconds. + Weight::from_ref_time(376_482_380) + .saturating_add(Weight::from_proof_size(17330)) + // Standard Error: 61_919 + .saturating_add(Weight::from_ref_time(22_376_795).saturating_mul(r.into())) .saturating_add(T::DbWeight::get().reads(6_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) .saturating_add(Weight::from_proof_size(2400).saturating_mul(r.into())) @@ -499,7 +499,7 @@ impl WeightInfo for SubstrateWeight { /// Storage: System Account (r:1 w:0) /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: Measured) /// Storage: Contracts ContractInfoOf (r:1 w:1) - /// Proof: Contracts ContractInfoOf (max_values: None, max_size: Some(258), added: 2733, mode: Measured) + /// Proof: Contracts ContractInfoOf (max_values: None, max_size: Some(290), added: 2765, mode: Measured) /// Storage: Contracts CodeStorage (r:1 w:0) /// Proof: Contracts CodeStorage (max_values: None, max_size: Some(126001), added: 128476, mode: Measured) /// Storage: Timestamp Now (r:1 w:0) @@ -509,13 +509,13 @@ impl WeightInfo for SubstrateWeight { /// The range of component `r` is `[0, 20]`. fn seal_caller_is_origin(r: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `842 + r * (240 ±0)` - // Estimated: `17080 + r * (1200 ±0)` - // Minimum execution time: 373_260 nanoseconds. - Weight::from_ref_time(377_987_666) - .saturating_add(Weight::from_proof_size(17080)) - // Standard Error: 35_603 - .saturating_add(Weight::from_ref_time(11_274_165).saturating_mul(r.into())) + // Measured: `874 + r * (240 ±0)` + // Estimated: `17265 + r * (1200 ±0)` + // Minimum execution time: 372_287 nanoseconds. + Weight::from_ref_time(376_250_858) + .saturating_add(Weight::from_proof_size(17265)) + // Standard Error: 40_119 + .saturating_add(Weight::from_ref_time(11_359_647).saturating_mul(r.into())) .saturating_add(T::DbWeight::get().reads(6_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) .saturating_add(Weight::from_proof_size(1200).saturating_mul(r.into())) @@ -523,7 +523,7 @@ impl WeightInfo for SubstrateWeight { /// Storage: System Account (r:1 w:0) /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: Measured) /// Storage: Contracts ContractInfoOf (r:1 w:1) - /// Proof: Contracts ContractInfoOf (max_values: None, max_size: Some(258), added: 2733, mode: Measured) + /// Proof: Contracts ContractInfoOf (max_values: None, max_size: Some(290), added: 2765, mode: Measured) /// Storage: Contracts CodeStorage (r:1 w:0) /// Proof: Contracts CodeStorage (max_values: None, max_size: Some(126001), added: 128476, mode: Measured) /// Storage: Timestamp Now (r:1 w:0) @@ -533,13 +533,13 @@ impl WeightInfo for SubstrateWeight { /// The range of component `r` is `[0, 20]`. fn seal_address(r: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `846 + r * (480 ±0)` - // Estimated: `17100 + r * (2400 ±0)` - // Minimum execution time: 374_605 nanoseconds. - Weight::from_ref_time(379_395_443) - .saturating_add(Weight::from_proof_size(17100)) - // Standard Error: 49_646 - .saturating_add(Weight::from_ref_time(17_487_585).saturating_mul(r.into())) + // Measured: `878 + r * (480 ±0)` + // Estimated: `17260 + r * (2400 ±0)` + // Minimum execution time: 374_445 nanoseconds. + Weight::from_ref_time(377_243_521) + .saturating_add(Weight::from_proof_size(17260)) + // Standard Error: 53_032 + .saturating_add(Weight::from_ref_time(17_684_246).saturating_mul(r.into())) .saturating_add(T::DbWeight::get().reads(6_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) .saturating_add(Weight::from_proof_size(2400).saturating_mul(r.into())) @@ -547,7 +547,7 @@ impl WeightInfo for SubstrateWeight { /// Storage: System Account (r:1 w:0) /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: Measured) /// Storage: Contracts ContractInfoOf (r:1 w:1) - /// Proof: Contracts ContractInfoOf (max_values: None, max_size: Some(258), added: 2733, mode: Measured) + /// Proof: Contracts ContractInfoOf (max_values: None, max_size: Some(290), added: 2765, mode: Measured) /// Storage: Contracts CodeStorage (r:1 w:0) /// Proof: Contracts CodeStorage (max_values: None, max_size: Some(126001), added: 128476, mode: Measured) /// Storage: Timestamp Now (r:1 w:0) @@ -557,21 +557,21 @@ impl WeightInfo for SubstrateWeight { /// The range of component `r` is `[0, 20]`. fn seal_gas_left(r: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `847 + r * (480 ±0)` - // Estimated: `17105 + r * (2400 ±0)` - // Minimum execution time: 374_620 nanoseconds. - Weight::from_ref_time(379_623_792) - .saturating_add(Weight::from_proof_size(17105)) - // Standard Error: 49_990 - .saturating_add(Weight::from_ref_time(17_226_706).saturating_mul(r.into())) + // Measured: `879 + r * (480 ±0)` + // Estimated: `17250 + r * (2405 ±0)` + // Minimum execution time: 374_029 nanoseconds. + Weight::from_ref_time(380_415_186) + .saturating_add(Weight::from_proof_size(17250)) + // Standard Error: 60_562 + .saturating_add(Weight::from_ref_time(17_152_599).saturating_mul(r.into())) .saturating_add(T::DbWeight::get().reads(6_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) - .saturating_add(Weight::from_proof_size(2400).saturating_mul(r.into())) + .saturating_add(Weight::from_proof_size(2405).saturating_mul(r.into())) } /// Storage: System Account (r:2 w:0) /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: Measured) /// Storage: Contracts ContractInfoOf (r:1 w:1) - /// Proof: Contracts ContractInfoOf (max_values: None, max_size: Some(258), added: 2733, mode: Measured) + /// Proof: Contracts ContractInfoOf (max_values: None, max_size: Some(290), added: 2765, mode: Measured) /// Storage: Contracts CodeStorage (r:1 w:0) /// Proof: Contracts CodeStorage (max_values: None, max_size: Some(126001), added: 128476, mode: Measured) /// Storage: Timestamp Now (r:1 w:0) @@ -581,13 +581,13 @@ impl WeightInfo for SubstrateWeight { /// The range of component `r` is `[0, 20]`. fn seal_balance(r: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `1017 + r * (480 ±0)` - // Estimated: `19673 + r * (2456 ±4)` - // Minimum execution time: 374_002 nanoseconds. - Weight::from_ref_time(384_615_649) - .saturating_add(Weight::from_proof_size(19673)) - // Standard Error: 85_633 - .saturating_add(Weight::from_ref_time(95_227_118).saturating_mul(r.into())) + // Measured: `1049 + r * (480 ±0)` + // Estimated: `19849 + r * (2456 ±0)` + // Minimum execution time: 373_999 nanoseconds. + Weight::from_ref_time(381_757_033) + .saturating_add(Weight::from_proof_size(19849)) + // Standard Error: 97_983 + .saturating_add(Weight::from_ref_time(98_290_984).saturating_mul(r.into())) .saturating_add(T::DbWeight::get().reads(7_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) .saturating_add(Weight::from_proof_size(2456).saturating_mul(r.into())) @@ -595,7 +595,7 @@ impl WeightInfo for SubstrateWeight { /// Storage: System Account (r:1 w:0) /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: Measured) /// Storage: Contracts ContractInfoOf (r:1 w:1) - /// Proof: Contracts ContractInfoOf (max_values: None, max_size: Some(258), added: 2733, mode: Measured) + /// Proof: Contracts ContractInfoOf (max_values: None, max_size: Some(290), added: 2765, mode: Measured) /// Storage: Contracts CodeStorage (r:1 w:0) /// Proof: Contracts CodeStorage (max_values: None, max_size: Some(126001), added: 128476, mode: Measured) /// Storage: Timestamp Now (r:1 w:0) @@ -605,13 +605,13 @@ impl WeightInfo for SubstrateWeight { /// The range of component `r` is `[0, 20]`. fn seal_value_transferred(r: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `856 + r * (480 ±0)` - // Estimated: `17200 + r * (2400 ±0)` - // Minimum execution time: 375_307 nanoseconds. - Weight::from_ref_time(378_389_705) - .saturating_add(Weight::from_proof_size(17200)) - // Standard Error: 42_265 - .saturating_add(Weight::from_ref_time(17_316_680).saturating_mul(r.into())) + // Measured: `888 + r * (480 ±0)` + // Estimated: `17360 + r * (2400 ±0)` + // Minimum execution time: 374_197 nanoseconds. + Weight::from_ref_time(377_755_896) + .saturating_add(Weight::from_proof_size(17360)) + // Standard Error: 60_542 + .saturating_add(Weight::from_ref_time(17_442_065).saturating_mul(r.into())) .saturating_add(T::DbWeight::get().reads(6_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) .saturating_add(Weight::from_proof_size(2400).saturating_mul(r.into())) @@ -619,7 +619,7 @@ impl WeightInfo for SubstrateWeight { /// Storage: System Account (r:1 w:0) /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: Measured) /// Storage: Contracts ContractInfoOf (r:1 w:1) - /// Proof: Contracts ContractInfoOf (max_values: None, max_size: Some(258), added: 2733, mode: Measured) + /// Proof: Contracts ContractInfoOf (max_values: None, max_size: Some(290), added: 2765, mode: Measured) /// Storage: Contracts CodeStorage (r:1 w:0) /// Proof: Contracts CodeStorage (max_values: None, max_size: Some(126001), added: 128476, mode: Measured) /// Storage: Timestamp Now (r:1 w:0) @@ -629,13 +629,13 @@ impl WeightInfo for SubstrateWeight { /// The range of component `r` is `[0, 20]`. fn seal_minimum_balance(r: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `854 + r * (480 ±0)` - // Estimated: `17140 + r * (2400 ±0)` - // Minimum execution time: 374_878 nanoseconds. - Weight::from_ref_time(379_364_066) - .saturating_add(Weight::from_proof_size(17140)) - // Standard Error: 49_158 - .saturating_add(Weight::from_ref_time(17_111_145).saturating_mul(r.into())) + // Measured: `886 + r * (480 ±0)` + // Estimated: `17290 + r * (2400 ±0)` + // Minimum execution time: 373_888 nanoseconds. + Weight::from_ref_time(377_825_771) + .saturating_add(Weight::from_proof_size(17290)) + // Standard Error: 38_026 + .saturating_add(Weight::from_ref_time(17_147_903).saturating_mul(r.into())) .saturating_add(T::DbWeight::get().reads(6_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) .saturating_add(Weight::from_proof_size(2400).saturating_mul(r.into())) @@ -643,7 +643,7 @@ impl WeightInfo for SubstrateWeight { /// Storage: System Account (r:1 w:0) /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: Measured) /// Storage: Contracts ContractInfoOf (r:1 w:1) - /// Proof: Contracts ContractInfoOf (max_values: None, max_size: Some(258), added: 2733, mode: Measured) + /// Proof: Contracts ContractInfoOf (max_values: None, max_size: Some(290), added: 2765, mode: Measured) /// Storage: Contracts CodeStorage (r:1 w:0) /// Proof: Contracts CodeStorage (max_values: None, max_size: Some(126001), added: 128476, mode: Measured) /// Storage: Timestamp Now (r:1 w:0) @@ -653,13 +653,13 @@ impl WeightInfo for SubstrateWeight { /// The range of component `r` is `[0, 20]`. fn seal_block_number(r: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `851 + r * (480 ±0)` - // Estimated: `17125 + r * (2400 ±0)` - // Minimum execution time: 374_981 nanoseconds. - Weight::from_ref_time(381_539_022) - .saturating_add(Weight::from_proof_size(17125)) - // Standard Error: 61_419 - .saturating_add(Weight::from_ref_time(17_062_381).saturating_mul(r.into())) + // Measured: `883 + r * (480 ±0)` + // Estimated: `17315 + r * (2400 ±0)` + // Minimum execution time: 373_904 nanoseconds. + Weight::from_ref_time(378_652_372) + .saturating_add(Weight::from_proof_size(17315)) + // Standard Error: 43_833 + .saturating_add(Weight::from_ref_time(16_936_781).saturating_mul(r.into())) .saturating_add(T::DbWeight::get().reads(6_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) .saturating_add(Weight::from_proof_size(2400).saturating_mul(r.into())) @@ -667,7 +667,7 @@ impl WeightInfo for SubstrateWeight { /// Storage: System Account (r:1 w:0) /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: Measured) /// Storage: Contracts ContractInfoOf (r:1 w:1) - /// Proof: Contracts ContractInfoOf (max_values: None, max_size: Some(258), added: 2733, mode: Measured) + /// Proof: Contracts ContractInfoOf (max_values: None, max_size: Some(290), added: 2765, mode: Measured) /// Storage: Contracts CodeStorage (r:1 w:0) /// Proof: Contracts CodeStorage (max_values: None, max_size: Some(126001), added: 128476, mode: Measured) /// Storage: Timestamp Now (r:1 w:0) @@ -677,13 +677,13 @@ impl WeightInfo for SubstrateWeight { /// The range of component `r` is `[0, 20]`. fn seal_now(r: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `842 + r * (480 ±0)` - // Estimated: `17100 + r * (2400 ±0)` - // Minimum execution time: 374_798 nanoseconds. - Weight::from_ref_time(372_659_915) - .saturating_add(Weight::from_proof_size(17100)) - // Standard Error: 151_499 - .saturating_add(Weight::from_ref_time(18_192_683).saturating_mul(r.into())) + // Measured: `874 + r * (480 ±0)` + // Estimated: `17245 + r * (2400 ±0)` + // Minimum execution time: 373_473 nanoseconds. + Weight::from_ref_time(376_386_312) + .saturating_add(Weight::from_proof_size(17245)) + // Standard Error: 46_945 + .saturating_add(Weight::from_ref_time(17_336_462).saturating_mul(r.into())) .saturating_add(T::DbWeight::get().reads(6_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) .saturating_add(Weight::from_proof_size(2400).saturating_mul(r.into())) @@ -691,7 +691,7 @@ impl WeightInfo for SubstrateWeight { /// Storage: System Account (r:1 w:0) /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: Measured) /// Storage: Contracts ContractInfoOf (r:1 w:1) - /// Proof: Contracts ContractInfoOf (max_values: None, max_size: Some(258), added: 2733, mode: Measured) + /// Proof: Contracts ContractInfoOf (max_values: None, max_size: Some(290), added: 2765, mode: Measured) /// Storage: Contracts CodeStorage (r:1 w:0) /// Proof: Contracts CodeStorage (max_values: None, max_size: Some(126001), added: 128476, mode: Measured) /// Storage: Timestamp Now (r:1 w:0) @@ -703,13 +703,13 @@ impl WeightInfo for SubstrateWeight { /// The range of component `r` is `[0, 20]`. fn seal_weight_to_fee(r: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `919 + r * (800 ±0)` - // Estimated: `18835 + r * (4805 ±0)` - // Minimum execution time: 374_841 nanoseconds. - Weight::from_ref_time(385_475_889) - .saturating_add(Weight::from_proof_size(18835)) - // Standard Error: 106_207 - .saturating_add(Weight::from_ref_time(88_099_987).saturating_mul(r.into())) + // Measured: `951 + r * (800 ±0)` + // Estimated: `19046 + r * (4805 ±0)` + // Minimum execution time: 373_661 nanoseconds. + Weight::from_ref_time(385_824_015) + .saturating_add(Weight::from_proof_size(19046)) + // Standard Error: 75_964 + .saturating_add(Weight::from_ref_time(88_530_074).saturating_mul(r.into())) .saturating_add(T::DbWeight::get().reads(7_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) .saturating_add(Weight::from_proof_size(4805).saturating_mul(r.into())) @@ -717,7 +717,7 @@ impl WeightInfo for SubstrateWeight { /// Storage: System Account (r:1 w:0) /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: Measured) /// Storage: Contracts ContractInfoOf (r:1 w:1) - /// Proof: Contracts ContractInfoOf (max_values: None, max_size: Some(258), added: 2733, mode: Measured) + /// Proof: Contracts ContractInfoOf (max_values: None, max_size: Some(290), added: 2765, mode: Measured) /// Storage: Contracts CodeStorage (r:1 w:0) /// Proof: Contracts CodeStorage (max_values: None, max_size: Some(126001), added: 128476, mode: Measured) /// Storage: Timestamp Now (r:1 w:0) @@ -727,13 +727,13 @@ impl WeightInfo for SubstrateWeight { /// The range of component `r` is `[0, 20]`. fn seal_gas(r: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `809 + r * (320 ±0)` - // Estimated: `16955 + r * (1600 ±0)` - // Minimum execution time: 134_436 nanoseconds. - Weight::from_ref_time(137_789_498) - .saturating_add(Weight::from_proof_size(16955)) - // Standard Error: 10_622 - .saturating_add(Weight::from_ref_time(8_144_024).saturating_mul(r.into())) + // Measured: `841 + r * (320 ±0)` + // Estimated: `17120 + r * (1600 ±0)` + // Minimum execution time: 133_849 nanoseconds. + Weight::from_ref_time(137_283_391) + .saturating_add(Weight::from_proof_size(17120)) + // Standard Error: 13_312 + .saturating_add(Weight::from_ref_time(8_055_328).saturating_mul(r.into())) .saturating_add(T::DbWeight::get().reads(6_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) .saturating_add(Weight::from_proof_size(1600).saturating_mul(r.into())) @@ -741,7 +741,7 @@ impl WeightInfo for SubstrateWeight { /// Storage: System Account (r:1 w:0) /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: Measured) /// Storage: Contracts ContractInfoOf (r:1 w:1) - /// Proof: Contracts ContractInfoOf (max_values: None, max_size: Some(258), added: 2733, mode: Measured) + /// Proof: Contracts ContractInfoOf (max_values: None, max_size: Some(290), added: 2765, mode: Measured) /// Storage: Contracts CodeStorage (r:1 w:0) /// Proof: Contracts CodeStorage (max_values: None, max_size: Some(126001), added: 128476, mode: Measured) /// Storage: Timestamp Now (r:1 w:0) @@ -751,13 +751,13 @@ impl WeightInfo for SubstrateWeight { /// The range of component `r` is `[0, 20]`. fn seal_input(r: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `844 + r * (480 ±0)` - // Estimated: `17085 + r * (2400 ±0)` - // Minimum execution time: 374_480 nanoseconds. - Weight::from_ref_time(379_723_392) - .saturating_add(Weight::from_proof_size(17085)) - // Standard Error: 50_240 - .saturating_add(Weight::from_ref_time(15_358_041).saturating_mul(r.into())) + // Measured: `876 + r * (480 ±0)` + // Estimated: `17245 + r * (2400 ±0)` + // Minimum execution time: 373_468 nanoseconds. + Weight::from_ref_time(376_121_093) + .saturating_add(Weight::from_proof_size(17245)) + // Standard Error: 61_857 + .saturating_add(Weight::from_ref_time(15_868_414).saturating_mul(r.into())) .saturating_add(T::DbWeight::get().reads(6_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) .saturating_add(Weight::from_proof_size(2400).saturating_mul(r.into())) @@ -765,7 +765,7 @@ impl WeightInfo for SubstrateWeight { /// Storage: System Account (r:1 w:0) /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: Measured) /// Storage: Contracts ContractInfoOf (r:1 w:1) - /// Proof: Contracts ContractInfoOf (max_values: None, max_size: Some(258), added: 2733, mode: Measured) + /// Proof: Contracts ContractInfoOf (max_values: None, max_size: Some(290), added: 2765, mode: Measured) /// Storage: Contracts CodeStorage (r:1 w:0) /// Proof: Contracts CodeStorage (max_values: None, max_size: Some(126001), added: 128476, mode: Measured) /// Storage: Timestamp Now (r:1 w:0) @@ -775,20 +775,20 @@ impl WeightInfo for SubstrateWeight { /// The range of component `n` is `[0, 1024]`. fn seal_input_per_kb(n: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `1324` - // Estimated: `19490` - // Minimum execution time: 392_282 nanoseconds. - Weight::from_ref_time(418_943_323) - .saturating_add(Weight::from_proof_size(19490)) - // Standard Error: 4_673 - .saturating_add(Weight::from_ref_time(9_664_301).saturating_mul(n.into())) + // Measured: `1356` + // Estimated: `19650` + // Minimum execution time: 390_668 nanoseconds. + Weight::from_ref_time(419_608_449) + .saturating_add(Weight::from_proof_size(19650)) + // Standard Error: 4_890 + .saturating_add(Weight::from_ref_time(9_672_288).saturating_mul(n.into())) .saturating_add(T::DbWeight::get().reads(6_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) } /// Storage: System Account (r:1 w:0) /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: Measured) /// Storage: Contracts ContractInfoOf (r:1 w:1) - /// Proof: Contracts ContractInfoOf (max_values: None, max_size: Some(258), added: 2733, mode: Measured) + /// Proof: Contracts ContractInfoOf (max_values: None, max_size: Some(290), added: 2765, mode: Measured) /// Storage: Contracts CodeStorage (r:1 w:0) /// Proof: Contracts CodeStorage (max_values: None, max_size: Some(126001), added: 128476, mode: Measured) /// Storage: Timestamp Now (r:1 w:0) @@ -798,11 +798,13 @@ impl WeightInfo for SubstrateWeight { /// The range of component `r` is `[0, 1]`. fn seal_return(r: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `832 + r * (45 ±0)` - // Estimated: `17030 + r * (225 ±0)` - // Minimum execution time: 372_207 nanoseconds. - Weight::from_ref_time(376_232_444) - .saturating_add(Weight::from_proof_size(17030)) + // Measured: `864 + r * (45 ±0)` + // Estimated: `17190 + r * (225 ±0)` + // Minimum execution time: 371_309 nanoseconds. + Weight::from_ref_time(373_625_402) + .saturating_add(Weight::from_proof_size(17190)) + // Standard Error: 419_605 + .saturating_add(Weight::from_ref_time(1_737_397).saturating_mul(r.into())) .saturating_add(T::DbWeight::get().reads(6_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) .saturating_add(Weight::from_proof_size(225).saturating_mul(r.into())) @@ -810,7 +812,7 @@ impl WeightInfo for SubstrateWeight { /// Storage: System Account (r:1 w:0) /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: Measured) /// Storage: Contracts ContractInfoOf (r:1 w:1) - /// Proof: Contracts ContractInfoOf (max_values: None, max_size: Some(258), added: 2733, mode: Measured) + /// Proof: Contracts ContractInfoOf (max_values: None, max_size: Some(290), added: 2765, mode: Measured) /// Storage: Contracts CodeStorage (r:1 w:0) /// Proof: Contracts CodeStorage (max_values: None, max_size: Some(126001), added: 128476, mode: Measured) /// Storage: Timestamp Now (r:1 w:0) @@ -820,20 +822,20 @@ impl WeightInfo for SubstrateWeight { /// The range of component `n` is `[0, 1024]`. fn seal_return_per_kb(n: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `842` - // Estimated: `17125` - // Minimum execution time: 374_743 nanoseconds. - Weight::from_ref_time(377_365_053) - .saturating_add(Weight::from_proof_size(17125)) - // Standard Error: 1_073 - .saturating_add(Weight::from_ref_time(231_251).saturating_mul(n.into())) + // Measured: `874` + // Estimated: `17285` + // Minimum execution time: 374_094 nanoseconds. + Weight::from_ref_time(375_965_200) + .saturating_add(Weight::from_proof_size(17285)) + // Standard Error: 1_127 + .saturating_add(Weight::from_ref_time(232_645).saturating_mul(n.into())) .saturating_add(T::DbWeight::get().reads(6_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) } - /// Storage: System Account (r:3 w:3) + /// Storage: System Account (r:4 w:4) /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: Measured) /// Storage: Contracts ContractInfoOf (r:1 w:1) - /// Proof: Contracts ContractInfoOf (max_values: None, max_size: Some(258), added: 2733, mode: Measured) + /// Proof: Contracts ContractInfoOf (max_values: None, max_size: Some(290), added: 2765, mode: Measured) /// Storage: Contracts CodeStorage (r:1 w:0) /// Proof: Contracts CodeStorage (max_values: None, max_size: Some(126001), added: 128476, mode: Measured) /// Storage: Timestamp Now (r:1 w:0) @@ -847,23 +849,23 @@ impl WeightInfo for SubstrateWeight { /// The range of component `r` is `[0, 1]`. fn seal_terminate(r: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `874 + r * (280 ±0)` - // Estimated: `19880 + r * (11465 ±0)` - // Minimum execution time: 374_873 nanoseconds. - Weight::from_ref_time(378_422_289) - .saturating_add(Weight::from_proof_size(19880)) - // Standard Error: 979_734 - .saturating_add(Weight::from_ref_time(57_035_310).saturating_mul(r.into())) + // Measured: `906 + r * (452 ±0)` + // Estimated: `20242 + r * (15004 ±0)` + // Minimum execution time: 373_123 nanoseconds. + Weight::from_ref_time(374_924_634) + .saturating_add(Weight::from_proof_size(20242)) + // Standard Error: 378_010 + .saturating_add(Weight::from_ref_time(70_441_665).saturating_mul(r.into())) .saturating_add(T::DbWeight::get().reads(6_u64)) - .saturating_add(T::DbWeight::get().reads((5_u64).saturating_mul(r.into()))) + .saturating_add(T::DbWeight::get().reads((6_u64).saturating_mul(r.into()))) .saturating_add(T::DbWeight::get().writes(3_u64)) - .saturating_add(T::DbWeight::get().writes((6_u64).saturating_mul(r.into()))) - .saturating_add(Weight::from_proof_size(11465).saturating_mul(r.into())) + .saturating_add(T::DbWeight::get().writes((7_u64).saturating_mul(r.into()))) + .saturating_add(Weight::from_proof_size(15004).saturating_mul(r.into())) } /// Storage: System Account (r:1 w:0) /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: Measured) /// Storage: Contracts ContractInfoOf (r:1 w:1) - /// Proof: Contracts ContractInfoOf (max_values: None, max_size: Some(258), added: 2733, mode: Measured) + /// Proof: Contracts ContractInfoOf (max_values: None, max_size: Some(290), added: 2765, mode: Measured) /// Storage: Contracts CodeStorage (r:1 w:0) /// Proof: Contracts CodeStorage (max_values: None, max_size: Some(126001), added: 128476, mode: Measured) /// Storage: Timestamp Now (r:1 w:0) @@ -875,13 +877,13 @@ impl WeightInfo for SubstrateWeight { /// The range of component `r` is `[0, 20]`. fn seal_random(r: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `889 + r * (800 ±0)` - // Estimated: `18643 + r * (4805 ±0)` - // Minimum execution time: 375_001 nanoseconds. - Weight::from_ref_time(382_558_599) - .saturating_add(Weight::from_proof_size(18643)) - // Standard Error: 94_918 - .saturating_add(Weight::from_ref_time(112_973_277).saturating_mul(r.into())) + // Measured: `921 + r * (800 ±0)` + // Estimated: `18835 + r * (4805 ±0)` + // Minimum execution time: 373_291 nanoseconds. + Weight::from_ref_time(385_684_344) + .saturating_add(Weight::from_proof_size(18835)) + // Standard Error: 99_025 + .saturating_add(Weight::from_ref_time(111_308_793).saturating_mul(r.into())) .saturating_add(T::DbWeight::get().reads(7_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) .saturating_add(Weight::from_proof_size(4805).saturating_mul(r.into())) @@ -889,7 +891,7 @@ impl WeightInfo for SubstrateWeight { /// Storage: System Account (r:1 w:0) /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: Measured) /// Storage: Contracts ContractInfoOf (r:1 w:1) - /// Proof: Contracts ContractInfoOf (max_values: None, max_size: Some(258), added: 2733, mode: Measured) + /// Proof: Contracts ContractInfoOf (max_values: None, max_size: Some(290), added: 2765, mode: Measured) /// Storage: Contracts CodeStorage (r:1 w:0) /// Proof: Contracts CodeStorage (max_values: None, max_size: Some(126001), added: 128476, mode: Measured) /// Storage: Timestamp Now (r:1 w:0) @@ -899,13 +901,13 @@ impl WeightInfo for SubstrateWeight { /// The range of component `r` is `[0, 20]`. fn seal_deposit_event(r: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `842 + r * (800 ±0)` - // Estimated: `17075 + r * (4000 ±0)` - // Minimum execution time: 372_547 nanoseconds. - Weight::from_ref_time(383_278_916) - .saturating_add(Weight::from_proof_size(17075)) - // Standard Error: 128_339 - .saturating_add(Weight::from_ref_time(229_356_088).saturating_mul(r.into())) + // Measured: `874 + r * (800 ±0)` + // Estimated: `17250 + r * (4000 ±0)` + // Minimum execution time: 371_900 nanoseconds. + Weight::from_ref_time(384_166_626) + .saturating_add(Weight::from_proof_size(17250)) + // Standard Error: 205_255 + .saturating_add(Weight::from_ref_time(229_214_157).saturating_mul(r.into())) .saturating_add(T::DbWeight::get().reads(6_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) .saturating_add(Weight::from_proof_size(4000).saturating_mul(r.into())) @@ -913,7 +915,7 @@ impl WeightInfo for SubstrateWeight { /// Storage: System Account (r:1 w:0) /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: Measured) /// Storage: Contracts ContractInfoOf (r:1 w:1) - /// Proof: Contracts ContractInfoOf (max_values: None, max_size: Some(258), added: 2733, mode: Measured) + /// Proof: Contracts ContractInfoOf (max_values: None, max_size: Some(290), added: 2765, mode: Measured) /// Storage: Contracts CodeStorage (r:1 w:0) /// Proof: Contracts CodeStorage (max_values: None, max_size: Some(126001), added: 128476, mode: Measured) /// Storage: Timestamp Now (r:1 w:0) @@ -924,15 +926,15 @@ impl WeightInfo for SubstrateWeight { /// The range of component `n` is `[0, 16]`. fn seal_deposit_event_per_topic_and_kb(t: u32, n: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `1778 + t * (2608 ±0) + n * (8 ±0)` - // Estimated: `21675 + t * (211030 ±0) + n * (50 ±0)` - // Minimum execution time: 1_290_432 nanoseconds. - Weight::from_ref_time(595_859_216) - .saturating_add(Weight::from_proof_size(21675)) - // Standard Error: 602_943 - .saturating_add(Weight::from_ref_time(178_128_149).saturating_mul(t.into())) - // Standard Error: 165_597 - .saturating_add(Weight::from_ref_time(71_475_468).saturating_mul(n.into())) + // Measured: `1821 + t * (2608 ±0) + n * (7 ±0)` + // Estimated: `21870 + t * (211030 ±0) + n * (50 ±0)` + // Minimum execution time: 1_289_873 nanoseconds. + Weight::from_ref_time(581_702_206) + .saturating_add(Weight::from_proof_size(21870)) + // Standard Error: 665_638 + .saturating_add(Weight::from_ref_time(181_470_553).saturating_mul(t.into())) + // Standard Error: 182_816 + .saturating_add(Weight::from_ref_time(71_635_250).saturating_mul(n.into())) .saturating_add(T::DbWeight::get().reads(6_u64)) .saturating_add(T::DbWeight::get().reads((80_u64).saturating_mul(t.into()))) .saturating_add(T::DbWeight::get().writes(3_u64)) @@ -943,7 +945,7 @@ impl WeightInfo for SubstrateWeight { /// Storage: System Account (r:1 w:0) /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: Measured) /// Storage: Contracts ContractInfoOf (r:1 w:1) - /// Proof: Contracts ContractInfoOf (max_values: None, max_size: Some(258), added: 2733, mode: Measured) + /// Proof: Contracts ContractInfoOf (max_values: None, max_size: Some(290), added: 2765, mode: Measured) /// Storage: Contracts CodeStorage (r:1 w:0) /// Proof: Contracts CodeStorage (max_values: None, max_size: Some(126001), added: 128476, mode: Measured) /// Storage: Timestamp Now (r:1 w:0) @@ -953,13 +955,13 @@ impl WeightInfo for SubstrateWeight { /// The range of component `r` is `[0, 20]`. fn seal_debug_message(r: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `841 + r * (560 ±0)` - // Estimated: `17065 + r * (2800 ±0)` - // Minimum execution time: 149_020 nanoseconds. - Weight::from_ref_time(152_893_012) - .saturating_add(Weight::from_proof_size(17065)) - // Standard Error: 31_804 - .saturating_add(Weight::from_ref_time(14_497_512).saturating_mul(r.into())) + // Measured: `873 + r * (560 ±0)` + // Estimated: `17240 + r * (2800 ±0)` + // Minimum execution time: 148_635 nanoseconds. + Weight::from_ref_time(154_095_712) + .saturating_add(Weight::from_proof_size(17240)) + // Standard Error: 77_790 + .saturating_add(Weight::from_ref_time(14_837_085).saturating_mul(r.into())) .saturating_add(T::DbWeight::get().reads(6_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) .saturating_add(Weight::from_proof_size(2800).saturating_mul(r.into())) @@ -967,7 +969,7 @@ impl WeightInfo for SubstrateWeight { /// Storage: System Account (r:1 w:0) /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) /// Storage: Contracts ContractInfoOf (r:1 w:1) - /// Proof: Contracts ContractInfoOf (max_values: None, max_size: Some(258), added: 2733, mode: MaxEncodedLen) + /// Proof: Contracts ContractInfoOf (max_values: None, max_size: Some(290), added: 2765, mode: MaxEncodedLen) /// Storage: Contracts CodeStorage (r:1 w:0) /// Proof: Contracts CodeStorage (max_values: None, max_size: Some(126001), added: 128476, mode: MaxEncodedLen) /// Storage: Timestamp Now (r:1 w:0) @@ -977,13 +979,13 @@ impl WeightInfo for SubstrateWeight { /// The range of component `i` is `[0, 1024]`. fn seal_debug_message_per_kb(i: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `125792` - // Estimated: `265059` - // Minimum execution time: 502_589 nanoseconds. - Weight::from_ref_time(506_695_307) - .saturating_add(Weight::from_proof_size(265059)) - // Standard Error: 2_148 - .saturating_add(Weight::from_ref_time(814_647).saturating_mul(i.into())) + // Measured: `125824` + // Estimated: `265128` + // Minimum execution time: 501_014 nanoseconds. + Weight::from_ref_time(505_634_218) + .saturating_add(Weight::from_proof_size(265128)) + // Standard Error: 2_441 + .saturating_add(Weight::from_ref_time(819_257).saturating_mul(i.into())) .saturating_add(T::DbWeight::get().reads(6_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) } @@ -992,85 +994,85 @@ impl WeightInfo for SubstrateWeight { /// The range of component `r` is `[0, 10]`. fn seal_set_storage(r: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `883 + r * (23417 ±0)` - // Estimated: `883 + r * (23417 ±0)` - // Minimum execution time: 375_473 nanoseconds. - Weight::from_ref_time(291_125_810) - .saturating_add(Weight::from_proof_size(883)) - // Standard Error: 824_971 - .saturating_add(Weight::from_ref_time(477_999_695).saturating_mul(r.into())) + // Measured: `911 + r * (23420 ±0)` + // Estimated: `911 + r * (23418 ±0)` + // Minimum execution time: 375_301 nanoseconds. + Weight::from_ref_time(291_498_841) + .saturating_add(Weight::from_proof_size(911)) + // Standard Error: 809_989 + .saturating_add(Weight::from_ref_time(464_550_291).saturating_mul(r.into())) .saturating_add(T::DbWeight::get().reads(6_u64)) .saturating_add(T::DbWeight::get().reads((80_u64).saturating_mul(r.into()))) .saturating_add(T::DbWeight::get().writes(3_u64)) .saturating_add(T::DbWeight::get().writes((80_u64).saturating_mul(r.into()))) - .saturating_add(Weight::from_proof_size(23417).saturating_mul(r.into())) + .saturating_add(Weight::from_proof_size(23418).saturating_mul(r.into())) } /// Storage: Skipped Metadata (r:0 w:0) /// Proof Skipped: Skipped Metadata (max_values: None, max_size: None, mode: Measured) /// The range of component `n` is `[0, 8]`. fn seal_set_storage_per_new_kb(n: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `12583 + n * (11969 ±0)` - // Estimated: `8500 + n * (12813 ±61)` - // Minimum execution time: 515_962 nanoseconds. - Weight::from_ref_time(697_904_030) - .saturating_add(Weight::from_proof_size(8500)) - // Standard Error: 1_684_000 - .saturating_add(Weight::from_ref_time(98_411_710).saturating_mul(n.into())) + // Measured: `12672 + n * (11945 ±0)` + // Estimated: `8529 + n * (12814 ±61)` + // Minimum execution time: 506_318 nanoseconds. + Weight::from_ref_time(676_935_313) + .saturating_add(Weight::from_proof_size(8529)) + // Standard Error: 1_589_291 + .saturating_add(Weight::from_ref_time(97_839_399).saturating_mul(n.into())) .saturating_add(T::DbWeight::get().reads(52_u64)) .saturating_add(T::DbWeight::get().reads((7_u64).saturating_mul(n.into()))) .saturating_add(T::DbWeight::get().writes(50_u64)) .saturating_add(T::DbWeight::get().writes((7_u64).saturating_mul(n.into()))) - .saturating_add(Weight::from_proof_size(12813).saturating_mul(n.into())) + .saturating_add(Weight::from_proof_size(12814).saturating_mul(n.into())) } /// Storage: Skipped Metadata (r:0 w:0) /// Proof Skipped: Skipped Metadata (max_values: None, max_size: None, mode: Measured) /// The range of component `n` is `[0, 8]`. fn seal_set_storage_per_old_kb(n: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `15138 + n * (175775 ±0)` - // Estimated: `9898 + n * (176855 ±74)` - // Minimum execution time: 515_828 nanoseconds. - Weight::from_ref_time(661_240_495) - .saturating_add(Weight::from_proof_size(9898)) - // Standard Error: 1_338_661 - .saturating_add(Weight::from_ref_time(65_767_819).saturating_mul(n.into())) + // Measured: `15170 + n * (175775 ±0)` + // Estimated: `9914 + n * (176858 ±74)` + // Minimum execution time: 506_148 nanoseconds. + Weight::from_ref_time(648_278_778) + .saturating_add(Weight::from_proof_size(9914)) + // Standard Error: 1_343_586 + .saturating_add(Weight::from_ref_time(65_789_595).saturating_mul(n.into())) .saturating_add(T::DbWeight::get().reads(51_u64)) .saturating_add(T::DbWeight::get().reads((7_u64).saturating_mul(n.into()))) .saturating_add(T::DbWeight::get().writes(49_u64)) .saturating_add(T::DbWeight::get().writes((7_u64).saturating_mul(n.into()))) - .saturating_add(Weight::from_proof_size(176855).saturating_mul(n.into())) + .saturating_add(Weight::from_proof_size(176858).saturating_mul(n.into())) } /// Storage: Skipped Metadata (r:0 w:0) /// Proof Skipped: Skipped Metadata (max_values: None, max_size: None, mode: Measured) /// The range of component `r` is `[0, 10]`. fn seal_clear_storage(r: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `876 + r * (23098 ±0)` - // Estimated: `881 + r * (23097 ±0)` - // Minimum execution time: 375_528 nanoseconds. - Weight::from_ref_time(296_453_612) - .saturating_add(Weight::from_proof_size(881)) - // Standard Error: 809_232 - .saturating_add(Weight::from_ref_time(465_365_815).saturating_mul(r.into())) + // Measured: `903 + r * (23099 ±0)` + // Estimated: `908 + r * (23099 ±0)` + // Minimum execution time: 374_344 nanoseconds. + Weight::from_ref_time(293_272_061) + .saturating_add(Weight::from_proof_size(908)) + // Standard Error: 810_412 + .saturating_add(Weight::from_ref_time(453_315_956).saturating_mul(r.into())) .saturating_add(T::DbWeight::get().reads(6_u64)) .saturating_add(T::DbWeight::get().reads((80_u64).saturating_mul(r.into()))) .saturating_add(T::DbWeight::get().writes(3_u64)) .saturating_add(T::DbWeight::get().writes((80_u64).saturating_mul(r.into()))) - .saturating_add(Weight::from_proof_size(23097).saturating_mul(r.into())) + .saturating_add(Weight::from_proof_size(23099).saturating_mul(r.into())) } /// Storage: Skipped Metadata (r:0 w:0) /// Proof Skipped: Skipped Metadata (max_values: None, max_size: None, mode: Measured) /// The range of component `n` is `[0, 8]`. fn seal_clear_storage_per_kb(n: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `14863 + n * (175768 ±0)` - // Estimated: `9519 + n * (176867 ±75)` - // Minimum execution time: 480_427 nanoseconds. - Weight::from_ref_time(640_337_570) - .saturating_add(Weight::from_proof_size(9519)) - // Standard Error: 1_497_141 - .saturating_add(Weight::from_ref_time(67_963_696).saturating_mul(n.into())) + // Measured: `14895 + n * (175768 ±0)` + // Estimated: `9551 + n * (176867 ±75)` + // Minimum execution time: 478_564 nanoseconds. + Weight::from_ref_time(630_839_142) + .saturating_add(Weight::from_proof_size(9551)) + // Standard Error: 1_427_520 + .saturating_add(Weight::from_ref_time(66_813_592).saturating_mul(n.into())) .saturating_add(T::DbWeight::get().reads(51_u64)) .saturating_add(T::DbWeight::get().reads((7_u64).saturating_mul(n.into()))) .saturating_add(T::DbWeight::get().writes(48_u64)) @@ -1082,30 +1084,30 @@ impl WeightInfo for SubstrateWeight { /// The range of component `r` is `[0, 10]`. fn seal_get_storage(r: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `878 + r * (23740 ±0)` - // Estimated: `880 + r * (23739 ±0)` - // Minimum execution time: 375_725 nanoseconds. - Weight::from_ref_time(307_839_394) - .saturating_add(Weight::from_proof_size(880)) - // Standard Error: 710_694 - .saturating_add(Weight::from_ref_time(381_738_407).saturating_mul(r.into())) + // Measured: `896 + r * (23744 ±0)` + // Estimated: `909 + r * (23740 ±0)` + // Minimum execution time: 374_479 nanoseconds. + Weight::from_ref_time(311_839_315) + .saturating_add(Weight::from_proof_size(909)) + // Standard Error: 666_553 + .saturating_add(Weight::from_ref_time(371_213_042).saturating_mul(r.into())) .saturating_add(T::DbWeight::get().reads(6_u64)) .saturating_add(T::DbWeight::get().reads((80_u64).saturating_mul(r.into()))) .saturating_add(T::DbWeight::get().writes(3_u64)) - .saturating_add(Weight::from_proof_size(23739).saturating_mul(r.into())) + .saturating_add(Weight::from_proof_size(23740).saturating_mul(r.into())) } /// Storage: Skipped Metadata (r:0 w:0) /// Proof Skipped: Skipped Metadata (max_values: None, max_size: None, mode: Measured) /// The range of component `n` is `[0, 8]`. fn seal_get_storage_per_kb(n: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `15469 + n * (175775 ±0)` - // Estimated: `10010 + n * (176900 ±76)` - // Minimum execution time: 461_871 nanoseconds. - Weight::from_ref_time(605_755_493) - .saturating_add(Weight::from_proof_size(10010)) - // Standard Error: 1_375_044 - .saturating_add(Weight::from_ref_time(161_332_330).saturating_mul(n.into())) + // Measured: `15501 + n * (175775 ±0)` + // Estimated: `10042 + n * (176900 ±76)` + // Minimum execution time: 460_639 nanoseconds. + Weight::from_ref_time(591_187_094) + .saturating_add(Weight::from_proof_size(10042)) + // Standard Error: 1_233_792 + .saturating_add(Weight::from_ref_time(160_874_477).saturating_mul(n.into())) .saturating_add(T::DbWeight::get().reads(51_u64)) .saturating_add(T::DbWeight::get().reads((7_u64).saturating_mul(n.into()))) .saturating_add(T::DbWeight::get().writes(3_u64)) @@ -1116,47 +1118,47 @@ impl WeightInfo for SubstrateWeight { /// The range of component `r` is `[0, 10]`. fn seal_contains_storage(r: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `871 + r * (23100 ±0)` - // Estimated: `873 + r * (23099 ±0)` - // Minimum execution time: 375_325 nanoseconds. - Weight::from_ref_time(305_508_307) - .saturating_add(Weight::from_proof_size(873)) - // Standard Error: 715_627 - .saturating_add(Weight::from_ref_time(369_985_438).saturating_mul(r.into())) + // Measured: `914 + r * (23098 ±0)` + // Estimated: `920 + r * (23098 ±0)` + // Minimum execution time: 374_272 nanoseconds. + Weight::from_ref_time(311_446_269) + .saturating_add(Weight::from_proof_size(920)) + // Standard Error: 630_307 + .saturating_add(Weight::from_ref_time(357_134_931).saturating_mul(r.into())) .saturating_add(T::DbWeight::get().reads(6_u64)) .saturating_add(T::DbWeight::get().reads((80_u64).saturating_mul(r.into()))) .saturating_add(T::DbWeight::get().writes(3_u64)) - .saturating_add(Weight::from_proof_size(23099).saturating_mul(r.into())) + .saturating_add(Weight::from_proof_size(23098).saturating_mul(r.into())) } /// Storage: Skipped Metadata (r:0 w:0) /// Proof Skipped: Skipped Metadata (max_values: None, max_size: None, mode: Measured) /// The range of component `n` is `[0, 8]`. fn seal_contains_storage_per_kb(n: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `14814 + n * (175782 ±0)` - // Estimated: `9502 + n * (176872 ±75)` - // Minimum execution time: 457_128 nanoseconds. - Weight::from_ref_time(582_799_799) - .saturating_add(Weight::from_proof_size(9502)) - // Standard Error: 1_151_126 - .saturating_add(Weight::from_ref_time(63_425_277).saturating_mul(n.into())) + // Measured: `14839 + n * (175789 ±0)` + // Estimated: `9532 + n * (176874 ±75)` + // Minimum execution time: 456_013 nanoseconds. + Weight::from_ref_time(575_116_352) + .saturating_add(Weight::from_proof_size(9532)) + // Standard Error: 1_122_298 + .saturating_add(Weight::from_ref_time(61_786_107).saturating_mul(n.into())) .saturating_add(T::DbWeight::get().reads(51_u64)) .saturating_add(T::DbWeight::get().reads((7_u64).saturating_mul(n.into()))) .saturating_add(T::DbWeight::get().writes(3_u64)) - .saturating_add(Weight::from_proof_size(176872).saturating_mul(n.into())) + .saturating_add(Weight::from_proof_size(176874).saturating_mul(n.into())) } /// Storage: Skipped Metadata (r:0 w:0) /// Proof Skipped: Skipped Metadata (max_values: None, max_size: None, mode: Measured) /// The range of component `r` is `[0, 10]`. fn seal_take_storage(r: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `879 + r * (23740 ±0)` - // Estimated: `881 + r * (23739 ±0)` - // Minimum execution time: 375_918 nanoseconds. - Weight::from_ref_time(293_217_646) - .saturating_add(Weight::from_proof_size(881)) - // Standard Error: 840_266 - .saturating_add(Weight::from_ref_time(478_374_701).saturating_mul(r.into())) + // Measured: `911 + r * (23740 ±0)` + // Estimated: `913 + r * (23739 ±0)` + // Minimum execution time: 374_621 nanoseconds. + Weight::from_ref_time(299_689_489) + .saturating_add(Weight::from_proof_size(913)) + // Standard Error: 757_735 + .saturating_add(Weight::from_ref_time(465_213_246).saturating_mul(r.into())) .saturating_add(T::DbWeight::get().reads(6_u64)) .saturating_add(T::DbWeight::get().reads((80_u64).saturating_mul(r.into()))) .saturating_add(T::DbWeight::get().writes(3_u64)) @@ -1168,13 +1170,13 @@ impl WeightInfo for SubstrateWeight { /// The range of component `n` is `[0, 8]`. fn seal_take_storage_per_kb(n: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `15470 + n * (175775 ±0)` - // Estimated: `10010 + n * (176898 ±76)` - // Minimum execution time: 484_207 nanoseconds. - Weight::from_ref_time(664_065_436) - .saturating_add(Weight::from_proof_size(10010)) - // Standard Error: 1_655_442 - .saturating_add(Weight::from_ref_time(166_258_757).saturating_mul(n.into())) + // Measured: `15502 + n * (175775 ±0)` + // Estimated: `10042 + n * (176898 ±76)` + // Minimum execution time: 481_980 nanoseconds. + Weight::from_ref_time(647_289_053) + .saturating_add(Weight::from_proof_size(10042)) + // Standard Error: 1_556_155 + .saturating_add(Weight::from_ref_time(166_592_657).saturating_mul(n.into())) .saturating_add(T::DbWeight::get().reads(51_u64)) .saturating_add(T::DbWeight::get().reads((7_u64).saturating_mul(n.into()))) .saturating_add(T::DbWeight::get().writes(48_u64)) @@ -1184,7 +1186,7 @@ impl WeightInfo for SubstrateWeight { /// Storage: System Account (r:1602 w:1601) /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: Measured) /// Storage: Contracts ContractInfoOf (r:1 w:1) - /// Proof: Contracts ContractInfoOf (max_values: None, max_size: Some(258), added: 2733, mode: Measured) + /// Proof: Contracts ContractInfoOf (max_values: None, max_size: Some(290), added: 2765, mode: Measured) /// Storage: Contracts CodeStorage (r:1 w:0) /// Proof: Contracts CodeStorage (max_values: None, max_size: Some(126001), added: 128476, mode: Measured) /// Storage: Timestamp Now (r:1 w:0) @@ -1194,23 +1196,23 @@ impl WeightInfo for SubstrateWeight { /// The range of component `r` is `[0, 20]`. fn seal_transfer(r: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `1393 + r * (3602 ±0)` - // Estimated: `21258 + r * (216091 ±0)` - // Minimum execution time: 377_152 nanoseconds. - Weight::from_ref_time(317_470_910) - .saturating_add(Weight::from_proof_size(21258)) - // Standard Error: 994_076 - .saturating_add(Weight::from_ref_time(1_409_416_087).saturating_mul(r.into())) + // Measured: `1457 + r * (3604 ±0)` + // Estimated: `21583 + r * (216101 ±0)` + // Minimum execution time: 374_962 nanoseconds. + Weight::from_ref_time(313_416_386) + .saturating_add(Weight::from_proof_size(21583)) + // Standard Error: 710_675 + .saturating_add(Weight::from_ref_time(1_396_551_156).saturating_mul(r.into())) .saturating_add(T::DbWeight::get().reads(7_u64)) .saturating_add(T::DbWeight::get().reads((80_u64).saturating_mul(r.into()))) .saturating_add(T::DbWeight::get().writes(4_u64)) .saturating_add(T::DbWeight::get().writes((80_u64).saturating_mul(r.into()))) - .saturating_add(Weight::from_proof_size(216091).saturating_mul(r.into())) + .saturating_add(Weight::from_proof_size(216101).saturating_mul(r.into())) } /// Storage: System Account (r:1 w:0) /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: Measured) /// Storage: Contracts ContractInfoOf (r:1601 w:1601) - /// Proof: Contracts ContractInfoOf (max_values: None, max_size: Some(258), added: 2733, mode: Measured) + /// Proof: Contracts ContractInfoOf (max_values: None, max_size: Some(290), added: 2765, mode: Measured) /// Storage: Contracts CodeStorage (r:2 w:0) /// Proof: Contracts CodeStorage (max_values: None, max_size: Some(126001), added: 128476, mode: Measured) /// Storage: Timestamp Now (r:1 w:0) @@ -1220,23 +1222,23 @@ impl WeightInfo for SubstrateWeight { /// The range of component `r` is `[0, 20]`. fn seal_call(r: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `1551 + r * (20511 ±0)` - // Estimated: `21848 + r * (498651 ±1)` - // Minimum execution time: 376_257 nanoseconds. - Weight::from_ref_time(377_035_000) - .saturating_add(Weight::from_proof_size(21848)) - // Standard Error: 7_966_778 - .saturating_add(Weight::from_ref_time(28_873_495_129).saturating_mul(r.into())) + // Measured: `1609 + r * (23073 ±0)` + // Estimated: `22098 + r * (511456 ±1)` + // Minimum execution time: 375_916 nanoseconds. + Weight::from_ref_time(376_468_000) + .saturating_add(Weight::from_proof_size(22098)) + // Standard Error: 7_246_855 + .saturating_add(Weight::from_ref_time(28_982_425_139).saturating_mul(r.into())) .saturating_add(T::DbWeight::get().reads(7_u64)) .saturating_add(T::DbWeight::get().reads((160_u64).saturating_mul(r.into()))) .saturating_add(T::DbWeight::get().writes(3_u64)) .saturating_add(T::DbWeight::get().writes((160_u64).saturating_mul(r.into()))) - .saturating_add(Weight::from_proof_size(498651).saturating_mul(r.into())) + .saturating_add(Weight::from_proof_size(511456).saturating_mul(r.into())) } /// Storage: System Account (r:1 w:0) /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: Measured) /// Storage: Contracts ContractInfoOf (r:1 w:1) - /// Proof: Contracts ContractInfoOf (max_values: None, max_size: Some(258), added: 2733, mode: Measured) + /// Proof: Contracts ContractInfoOf (max_values: None, max_size: Some(290), added: 2765, mode: Measured) /// Storage: Contracts CodeStorage (r:1536 w:0) /// Proof: Contracts CodeStorage (max_values: None, max_size: Some(126001), added: 128476, mode: Measured) /// Storage: Timestamp Now (r:1 w:0) @@ -1247,22 +1249,22 @@ impl WeightInfo for SubstrateWeight { fn seal_delegate_call(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0 + r * (71670 ±0)` - // Estimated: `17125 + r * (659927 ±822)` - // Minimum execution time: 376_544 nanoseconds. - Weight::from_ref_time(377_490_000) - .saturating_add(Weight::from_proof_size(17125)) - // Standard Error: 8_608_050 - .saturating_add(Weight::from_ref_time(28_568_714_410).saturating_mul(r.into())) + // Estimated: `17285 + r * (659930 ±563)` + // Minimum execution time: 375_412 nanoseconds. + Weight::from_ref_time(376_493_000) + .saturating_add(Weight::from_proof_size(17285)) + // Standard Error: 8_239_575 + .saturating_add(Weight::from_ref_time(28_716_347_183).saturating_mul(r.into())) .saturating_add(T::DbWeight::get().reads(6_u64)) .saturating_add(T::DbWeight::get().reads((150_u64).saturating_mul(r.into()))) .saturating_add(T::DbWeight::get().writes(3_u64)) .saturating_add(T::DbWeight::get().writes((75_u64).saturating_mul(r.into()))) - .saturating_add(Weight::from_proof_size(659927).saturating_mul(r.into())) + .saturating_add(Weight::from_proof_size(659930).saturating_mul(r.into())) } /// Storage: System Account (r:82 w:81) /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: Measured) /// Storage: Contracts ContractInfoOf (r:81 w:81) - /// Proof: Contracts ContractInfoOf (max_values: None, max_size: Some(258), added: 2733, mode: Measured) + /// Proof: Contracts ContractInfoOf (max_values: None, max_size: Some(290), added: 2765, mode: Measured) /// Storage: Contracts CodeStorage (r:2 w:0) /// Proof: Contracts CodeStorage (max_values: None, max_size: Some(126001), added: 128476, mode: Measured) /// Storage: Timestamp Now (r:1 w:0) @@ -1273,25 +1275,25 @@ impl WeightInfo for SubstrateWeight { /// The range of component `c` is `[0, 1024]`. fn seal_call_per_transfer_clone_kb(t: u32, c: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `21611 + t * (15369 ±0)` - // Estimated: `519400 + t * (277320 ±0)` - // Minimum execution time: 10_322_052 nanoseconds. - Weight::from_ref_time(9_248_652_894) - .saturating_add(Weight::from_proof_size(519400)) - // Standard Error: 9_039_638 - .saturating_add(Weight::from_ref_time(1_393_054_441).saturating_mul(t.into())) - // Standard Error: 13_554 - .saturating_add(Weight::from_ref_time(9_819_606).saturating_mul(c.into())) + // Measured: `24269 + t * (16910 ±0)` + // Estimated: `532690 + t * (285025 ±0)` + // Minimum execution time: 10_443_315 nanoseconds. + Weight::from_ref_time(9_342_574_069) + .saturating_add(Weight::from_proof_size(532690)) + // Standard Error: 7_237_279 + .saturating_add(Weight::from_ref_time(1_390_221_936).saturating_mul(t.into())) + // Standard Error: 10_851 + .saturating_add(Weight::from_ref_time(9_842_151).saturating_mul(c.into())) .saturating_add(T::DbWeight::get().reads(167_u64)) .saturating_add(T::DbWeight::get().reads((81_u64).saturating_mul(t.into()))) .saturating_add(T::DbWeight::get().writes(163_u64)) .saturating_add(T::DbWeight::get().writes((81_u64).saturating_mul(t.into()))) - .saturating_add(Weight::from_proof_size(277320).saturating_mul(t.into())) + .saturating_add(Weight::from_proof_size(285025).saturating_mul(t.into())) } - /// Storage: System Account (r:1602 w:1602) + /// Storage: System Account (r:3202 w:3202) /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: Measured) /// Storage: Contracts ContractInfoOf (r:1601 w:1601) - /// Proof: Contracts ContractInfoOf (max_values: None, max_size: Some(258), added: 2733, mode: Measured) + /// Proof: Contracts ContractInfoOf (max_values: None, max_size: Some(290), added: 2765, mode: Measured) /// Storage: Contracts CodeStorage (r:1601 w:0) /// Proof: Contracts CodeStorage (max_values: None, max_size: Some(126001), added: 128476, mode: Measured) /// Storage: Timestamp Now (r:1 w:0) @@ -1305,23 +1307,23 @@ impl WeightInfo for SubstrateWeight { /// The range of component `r` is `[0, 20]`. fn seal_instantiate(r: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `1613 + r * (25576 ±0)` - // Estimated: `25698 + r * (1169112 ±1)` - // Minimum execution time: 376_639 nanoseconds. - Weight::from_ref_time(377_892_000) - .saturating_add(Weight::from_proof_size(25698)) - // Standard Error: 21_259_255 - .saturating_add(Weight::from_ref_time(34_131_174_956).saturating_mul(r.into())) + // Measured: `1775 + r * (25568 ±0)` + // Estimated: `26563 + r * (1367114 ±2)` + // Minimum execution time: 376_418 nanoseconds. + Weight::from_ref_time(377_292_000) + .saturating_add(Weight::from_proof_size(26563)) + // Standard Error: 32_312_545 + .saturating_add(Weight::from_ref_time(35_904_826_312).saturating_mul(r.into())) .saturating_add(T::DbWeight::get().reads(8_u64)) - .saturating_add(T::DbWeight::get().reads((400_u64).saturating_mul(r.into()))) + .saturating_add(T::DbWeight::get().reads((480_u64).saturating_mul(r.into()))) .saturating_add(T::DbWeight::get().writes(5_u64)) - .saturating_add(T::DbWeight::get().writes((320_u64).saturating_mul(r.into()))) - .saturating_add(Weight::from_proof_size(1169112).saturating_mul(r.into())) + .saturating_add(T::DbWeight::get().writes((400_u64).saturating_mul(r.into()))) + .saturating_add(Weight::from_proof_size(1367114).saturating_mul(r.into())) } - /// Storage: System Account (r:82 w:82) + /// Storage: System Account (r:162 w:162) /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: Measured) /// Storage: Contracts ContractInfoOf (r:81 w:81) - /// Proof: Contracts ContractInfoOf (max_values: None, max_size: Some(258), added: 2733, mode: Measured) + /// Proof: Contracts ContractInfoOf (max_values: None, max_size: Some(290), added: 2765, mode: Measured) /// Storage: Contracts CodeStorage (r:2 w:0) /// Proof: Contracts CodeStorage (max_values: None, max_size: Some(126001), added: 128476, mode: Measured) /// Storage: Timestamp Now (r:1 w:0) @@ -1337,27 +1339,27 @@ impl WeightInfo for SubstrateWeight { /// The range of component `s` is `[0, 960]`. fn seal_instantiate_per_transfer_input_salt_kb(t: u32, i: u32, s: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `5666 + t * (17 ±0)` - // Estimated: `651914 + t * (2762 ±3)` - // Minimum execution time: 130_366_376 nanoseconds. - Weight::from_ref_time(9_844_607_874) - .saturating_add(Weight::from_proof_size(651914)) - // Standard Error: 100_211_149 - .saturating_add(Weight::from_ref_time(390_481_449).saturating_mul(t.into())) - // Standard Error: 163_416 - .saturating_add(Weight::from_ref_time(126_154_200).saturating_mul(i.into())) - // Standard Error: 163_416 - .saturating_add(Weight::from_ref_time(126_430_874).saturating_mul(s.into())) - .saturating_add(T::DbWeight::get().reads(249_u64)) + // Measured: `5785 + t * (33 ±0)` + // Estimated: `850985 + t * (2671 ±3)` + // Minimum execution time: 132_157_340 nanoseconds. + Weight::from_ref_time(11_329_968_948) + .saturating_add(Weight::from_proof_size(850985)) + // Standard Error: 99_102_968 + .saturating_add(Weight::from_ref_time(84_719_458).saturating_mul(t.into())) + // Standard Error: 161_609 + .saturating_add(Weight::from_ref_time(126_156_627).saturating_mul(i.into())) + // Standard Error: 161_609 + .saturating_add(Weight::from_ref_time(126_628_313).saturating_mul(s.into())) + .saturating_add(T::DbWeight::get().reads(329_u64)) .saturating_add(T::DbWeight::get().reads((1_u64).saturating_mul(t.into()))) - .saturating_add(T::DbWeight::get().writes(246_u64)) + .saturating_add(T::DbWeight::get().writes(326_u64)) .saturating_add(T::DbWeight::get().writes((1_u64).saturating_mul(t.into()))) - .saturating_add(Weight::from_proof_size(2762).saturating_mul(t.into())) + .saturating_add(Weight::from_proof_size(2671).saturating_mul(t.into())) } /// Storage: System Account (r:1 w:0) /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: Measured) /// Storage: Contracts ContractInfoOf (r:1 w:1) - /// Proof: Contracts ContractInfoOf (max_values: None, max_size: Some(258), added: 2733, mode: Measured) + /// Proof: Contracts ContractInfoOf (max_values: None, max_size: Some(290), added: 2765, mode: Measured) /// Storage: Contracts CodeStorage (r:1 w:0) /// Proof: Contracts CodeStorage (max_values: None, max_size: Some(126001), added: 128476, mode: Measured) /// Storage: Timestamp Now (r:1 w:0) @@ -1367,13 +1369,13 @@ impl WeightInfo for SubstrateWeight { /// The range of component `r` is `[0, 1]`. fn seal_hash_sha2_256(r: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `839 + r * (642 ±0)` - // Estimated: `17065 + r * (3210 ±0)` - // Minimum execution time: 373_848 nanoseconds. - Weight::from_ref_time(375_974_597) - .saturating_add(Weight::from_proof_size(17065)) - // Standard Error: 558_923 - .saturating_add(Weight::from_ref_time(42_648_202).saturating_mul(r.into())) + // Measured: `871 + r * (642 ±0)` + // Estimated: `17225 + r * (3210 ±0)` + // Minimum execution time: 373_559 nanoseconds. + Weight::from_ref_time(375_166_904) + .saturating_add(Weight::from_proof_size(17225)) + // Standard Error: 125_024 + .saturating_add(Weight::from_ref_time(42_291_595).saturating_mul(r.into())) .saturating_add(T::DbWeight::get().reads(6_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) .saturating_add(Weight::from_proof_size(3210).saturating_mul(r.into())) @@ -1381,7 +1383,7 @@ impl WeightInfo for SubstrateWeight { /// Storage: System Account (r:1 w:0) /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: Measured) /// Storage: Contracts ContractInfoOf (r:1 w:1) - /// Proof: Contracts ContractInfoOf (max_values: None, max_size: Some(258), added: 2733, mode: Measured) + /// Proof: Contracts ContractInfoOf (max_values: None, max_size: Some(290), added: 2765, mode: Measured) /// Storage: Contracts CodeStorage (r:1 w:0) /// Proof: Contracts CodeStorage (max_values: None, max_size: Some(126001), added: 128476, mode: Measured) /// Storage: Timestamp Now (r:1 w:0) @@ -1391,20 +1393,20 @@ impl WeightInfo for SubstrateWeight { /// The range of component `n` is `[0, 1024]`. fn seal_hash_sha2_256_per_kb(n: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `1641` - // Estimated: `21000` - // Minimum execution time: 417_281 nanoseconds. - Weight::from_ref_time(418_086_000) - .saturating_add(Weight::from_proof_size(21000)) - // Standard Error: 43_883 - .saturating_add(Weight::from_ref_time(324_497_460).saturating_mul(n.into())) + // Measured: `1673` + // Estimated: `21160` + // Minimum execution time: 416_233 nanoseconds. + Weight::from_ref_time(416_785_000) + .saturating_add(Weight::from_proof_size(21160)) + // Standard Error: 56_223 + .saturating_add(Weight::from_ref_time(324_513_835).saturating_mul(n.into())) .saturating_add(T::DbWeight::get().reads(6_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) } /// Storage: System Account (r:1 w:0) /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: Measured) /// Storage: Contracts ContractInfoOf (r:1 w:1) - /// Proof: Contracts ContractInfoOf (max_values: None, max_size: Some(258), added: 2733, mode: Measured) + /// Proof: Contracts ContractInfoOf (max_values: None, max_size: Some(290), added: 2765, mode: Measured) /// Storage: Contracts CodeStorage (r:1 w:0) /// Proof: Contracts CodeStorage (max_values: None, max_size: Some(126001), added: 128476, mode: Measured) /// Storage: Timestamp Now (r:1 w:0) @@ -1414,13 +1416,13 @@ impl WeightInfo for SubstrateWeight { /// The range of component `r` is `[0, 1]`. fn seal_hash_keccak_256(r: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `841 + r * (642 ±0)` - // Estimated: `17075 + r * (3210 ±0)` - // Minimum execution time: 372_362 nanoseconds. - Weight::from_ref_time(374_975_677) - .saturating_add(Weight::from_proof_size(17075)) - // Standard Error: 307_932 - .saturating_add(Weight::from_ref_time(57_607_522).saturating_mul(r.into())) + // Measured: `873 + r * (642 ±0)` + // Estimated: `17235 + r * (3210 ±0)` + // Minimum execution time: 371_735 nanoseconds. + Weight::from_ref_time(375_979_430) + .saturating_add(Weight::from_proof_size(17235)) + // Standard Error: 968_037 + .saturating_add(Weight::from_ref_time(57_780_769).saturating_mul(r.into())) .saturating_add(T::DbWeight::get().reads(6_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) .saturating_add(Weight::from_proof_size(3210).saturating_mul(r.into())) @@ -1428,7 +1430,7 @@ impl WeightInfo for SubstrateWeight { /// Storage: System Account (r:1 w:0) /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: Measured) /// Storage: Contracts ContractInfoOf (r:1 w:1) - /// Proof: Contracts ContractInfoOf (max_values: None, max_size: Some(258), added: 2733, mode: Measured) + /// Proof: Contracts ContractInfoOf (max_values: None, max_size: Some(290), added: 2765, mode: Measured) /// Storage: Contracts CodeStorage (r:1 w:0) /// Proof: Contracts CodeStorage (max_values: None, max_size: Some(126001), added: 128476, mode: Measured) /// Storage: Timestamp Now (r:1 w:0) @@ -1438,20 +1440,20 @@ impl WeightInfo for SubstrateWeight { /// The range of component `n` is `[0, 1024]`. fn seal_hash_keccak_256_per_kb(n: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `1643` - // Estimated: `21040` - // Minimum execution time: 431_034 nanoseconds. - Weight::from_ref_time(431_571_000) - .saturating_add(Weight::from_proof_size(21040)) - // Standard Error: 80_071 - .saturating_add(Weight::from_ref_time(261_645_325).saturating_mul(n.into())) + // Measured: `1675` + // Estimated: `21205` + // Minimum execution time: 428_196 nanoseconds. + Weight::from_ref_time(429_438_000) + .saturating_add(Weight::from_proof_size(21205)) + // Standard Error: 57_860 + .saturating_add(Weight::from_ref_time(260_917_896).saturating_mul(n.into())) .saturating_add(T::DbWeight::get().reads(6_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) } /// Storage: System Account (r:1 w:0) /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: Measured) /// Storage: Contracts ContractInfoOf (r:1 w:1) - /// Proof: Contracts ContractInfoOf (max_values: None, max_size: Some(258), added: 2733, mode: Measured) + /// Proof: Contracts ContractInfoOf (max_values: None, max_size: Some(290), added: 2765, mode: Measured) /// Storage: Contracts CodeStorage (r:1 w:0) /// Proof: Contracts CodeStorage (max_values: None, max_size: Some(126001), added: 128476, mode: Measured) /// Storage: Timestamp Now (r:1 w:0) @@ -1461,13 +1463,13 @@ impl WeightInfo for SubstrateWeight { /// The range of component `r` is `[0, 1]`. fn seal_hash_blake2_256(r: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `841 + r * (642 ±0)` - // Estimated: `17075 + r * (3210 ±0)` - // Minimum execution time: 372_069 nanoseconds. - Weight::from_ref_time(374_200_608) - .saturating_add(Weight::from_proof_size(17075)) - // Standard Error: 171_799 - .saturating_add(Weight::from_ref_time(32_843_391).saturating_mul(r.into())) + // Measured: `873 + r * (642 ±0)` + // Estimated: `17235 + r * (3210 ±0)` + // Minimum execution time: 371_412 nanoseconds. + Weight::from_ref_time(373_635_818) + .saturating_add(Weight::from_proof_size(17235)) + // Standard Error: 222_973 + .saturating_add(Weight::from_ref_time(33_347_181).saturating_mul(r.into())) .saturating_add(T::DbWeight::get().reads(6_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) .saturating_add(Weight::from_proof_size(3210).saturating_mul(r.into())) @@ -1475,7 +1477,7 @@ impl WeightInfo for SubstrateWeight { /// Storage: System Account (r:1 w:0) /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: Measured) /// Storage: Contracts ContractInfoOf (r:1 w:1) - /// Proof: Contracts ContractInfoOf (max_values: None, max_size: Some(258), added: 2733, mode: Measured) + /// Proof: Contracts ContractInfoOf (max_values: None, max_size: Some(290), added: 2765, mode: Measured) /// Storage: Contracts CodeStorage (r:1 w:0) /// Proof: Contracts CodeStorage (max_values: None, max_size: Some(126001), added: 128476, mode: Measured) /// Storage: Timestamp Now (r:1 w:0) @@ -1485,20 +1487,20 @@ impl WeightInfo for SubstrateWeight { /// The range of component `n` is `[0, 1024]`. fn seal_hash_blake2_256_per_kb(n: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `1643` - // Estimated: `21010` - // Minimum execution time: 406_143 nanoseconds. - Weight::from_ref_time(406_744_000) - .saturating_add(Weight::from_proof_size(21010)) - // Standard Error: 48_038 - .saturating_add(Weight::from_ref_time(103_286_295).saturating_mul(n.into())) + // Measured: `1675` + // Estimated: `21180` + // Minimum execution time: 405_858 nanoseconds. + Weight::from_ref_time(406_498_000) + .saturating_add(Weight::from_proof_size(21180)) + // Standard Error: 48_388 + .saturating_add(Weight::from_ref_time(103_283_157).saturating_mul(n.into())) .saturating_add(T::DbWeight::get().reads(6_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) } /// Storage: System Account (r:1 w:0) /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: Measured) /// Storage: Contracts ContractInfoOf (r:1 w:1) - /// Proof: Contracts ContractInfoOf (max_values: None, max_size: Some(258), added: 2733, mode: Measured) + /// Proof: Contracts ContractInfoOf (max_values: None, max_size: Some(290), added: 2765, mode: Measured) /// Storage: Contracts CodeStorage (r:1 w:0) /// Proof: Contracts CodeStorage (max_values: None, max_size: Some(126001), added: 128476, mode: Measured) /// Storage: Timestamp Now (r:1 w:0) @@ -1508,13 +1510,13 @@ impl WeightInfo for SubstrateWeight { /// The range of component `r` is `[0, 1]`. fn seal_hash_blake2_128(r: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `841 + r * (679 ±0)` - // Estimated: `17075 + r * (3395 ±0)` - // Minimum execution time: 372_201 nanoseconds. - Weight::from_ref_time(374_049_708) - .saturating_add(Weight::from_proof_size(17075)) - // Standard Error: 387_179 - .saturating_add(Weight::from_ref_time(38_857_191).saturating_mul(r.into())) + // Measured: `873 + r * (679 ±0)` + // Estimated: `17235 + r * (3395 ±0)` + // Minimum execution time: 371_746 nanoseconds. + Weight::from_ref_time(373_538_171) + .saturating_add(Weight::from_proof_size(17235)) + // Standard Error: 387_332 + .saturating_add(Weight::from_ref_time(35_933_528).saturating_mul(r.into())) .saturating_add(T::DbWeight::get().reads(6_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) .saturating_add(Weight::from_proof_size(3395).saturating_mul(r.into())) @@ -1522,7 +1524,7 @@ impl WeightInfo for SubstrateWeight { /// Storage: System Account (r:1 w:0) /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: Measured) /// Storage: Contracts ContractInfoOf (r:1 w:1) - /// Proof: Contracts ContractInfoOf (max_values: None, max_size: Some(258), added: 2733, mode: Measured) + /// Proof: Contracts ContractInfoOf (max_values: None, max_size: Some(290), added: 2765, mode: Measured) /// Storage: Contracts CodeStorage (r:1 w:0) /// Proof: Contracts CodeStorage (max_values: None, max_size: Some(126001), added: 128476, mode: Measured) /// Storage: Timestamp Now (r:1 w:0) @@ -1532,20 +1534,20 @@ impl WeightInfo for SubstrateWeight { /// The range of component `n` is `[0, 1024]`. fn seal_hash_blake2_128_per_kb(n: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `1643` - // Estimated: `21050` - // Minimum execution time: 405_819 nanoseconds. - Weight::from_ref_time(406_364_000) - .saturating_add(Weight::from_proof_size(21050)) - // Standard Error: 46_248 - .saturating_add(Weight::from_ref_time(103_189_157).saturating_mul(n.into())) + // Measured: `1675` + // Estimated: `21225` + // Minimum execution time: 405_752 nanoseconds. + Weight::from_ref_time(406_417_000) + .saturating_add(Weight::from_proof_size(21225)) + // Standard Error: 47_051 + .saturating_add(Weight::from_ref_time(103_325_027).saturating_mul(n.into())) .saturating_add(T::DbWeight::get().reads(6_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) } /// Storage: System Account (r:1 w:0) /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: Measured) /// Storage: Contracts ContractInfoOf (r:1 w:1) - /// Proof: Contracts ContractInfoOf (max_values: None, max_size: Some(258), added: 2733, mode: Measured) + /// Proof: Contracts ContractInfoOf (max_values: None, max_size: Some(290), added: 2765, mode: Measured) /// Storage: Contracts CodeStorage (r:1 w:0) /// Proof: Contracts CodeStorage (max_values: None, max_size: Some(126001), added: 128476, mode: Measured) /// Storage: Timestamp Now (r:1 w:0) @@ -1555,13 +1557,13 @@ impl WeightInfo for SubstrateWeight { /// The range of component `r` is `[0, 1]`. fn seal_ecdsa_recover(r: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `885 + r * (6083 ±0)` - // Estimated: `17295 + r * (30415 ±0)` - // Minimum execution time: 374_836 nanoseconds. - Weight::from_ref_time(379_385_500) - .saturating_add(Weight::from_proof_size(17295)) - // Standard Error: 1_694_427 - .saturating_add(Weight::from_ref_time(3_021_801_000).saturating_mul(r.into())) + // Measured: `917 + r * (6083 ±0)` + // Estimated: `17455 + r * (30415 ±0)` + // Minimum execution time: 373_882 nanoseconds. + Weight::from_ref_time(376_553_787) + .saturating_add(Weight::from_proof_size(17455)) + // Standard Error: 912_833 + .saturating_add(Weight::from_ref_time(3_021_100_412).saturating_mul(r.into())) .saturating_add(T::DbWeight::get().reads(6_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) .saturating_add(Weight::from_proof_size(30415).saturating_mul(r.into())) @@ -1569,7 +1571,7 @@ impl WeightInfo for SubstrateWeight { /// Storage: System Account (r:1 w:0) /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: Measured) /// Storage: Contracts ContractInfoOf (r:1 w:1) - /// Proof: Contracts ContractInfoOf (max_values: None, max_size: Some(258), added: 2733, mode: Measured) + /// Proof: Contracts ContractInfoOf (max_values: None, max_size: Some(290), added: 2765, mode: Measured) /// Storage: Contracts CodeStorage (r:1 w:0) /// Proof: Contracts CodeStorage (max_values: None, max_size: Some(126001), added: 128476, mode: Measured) /// Storage: Timestamp Now (r:1 w:0) @@ -1579,13 +1581,13 @@ impl WeightInfo for SubstrateWeight { /// The range of component `r` is `[0, 1]`. fn seal_ecdsa_to_eth_address(r: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `854 + r * (3362 ±0)` - // Estimated: `17140 + r * (16810 ±0)` - // Minimum execution time: 374_223 nanoseconds. - Weight::from_ref_time(376_120_230) - .saturating_add(Weight::from_proof_size(17140)) - // Standard Error: 619_576 - .saturating_add(Weight::from_ref_time(754_257_969).saturating_mul(r.into())) + // Measured: `886 + r * (3362 ±0)` + // Estimated: `17300 + r * (16810 ±0)` + // Minimum execution time: 373_673 nanoseconds. + Weight::from_ref_time(375_712_961) + .saturating_add(Weight::from_proof_size(17300)) + // Standard Error: 596_297 + .saturating_add(Weight::from_ref_time(738_257_638).saturating_mul(r.into())) .saturating_add(T::DbWeight::get().reads(6_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) .saturating_add(Weight::from_proof_size(16810).saturating_mul(r.into())) @@ -1593,7 +1595,7 @@ impl WeightInfo for SubstrateWeight { /// Storage: System Account (r:1 w:0) /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: Measured) /// Storage: Contracts ContractInfoOf (r:1 w:1) - /// Proof: Contracts ContractInfoOf (max_values: None, max_size: Some(258), added: 2733, mode: Measured) + /// Proof: Contracts ContractInfoOf (max_values: None, max_size: Some(290), added: 2765, mode: Measured) /// Storage: Contracts CodeStorage (r:1536 w:0) /// Proof: Contracts CodeStorage (max_values: None, max_size: Some(126001), added: 128476, mode: Measured) /// Storage: Timestamp Now (r:1 w:0) @@ -1606,12 +1608,12 @@ impl WeightInfo for SubstrateWeight { fn seal_set_code_hash(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0 + r * (79300 ±0)` - // Estimated: `64652 + r * (942952 ±829)` - // Minimum execution time: 374_442 nanoseconds. - Weight::from_ref_time(375_591_000) - .saturating_add(Weight::from_proof_size(64652)) - // Standard Error: 3_764_193 - .saturating_add(Weight::from_ref_time(1_552_885_601).saturating_mul(r.into())) + // Estimated: `64844 + r * (942952 ±833)` + // Minimum execution time: 374_097 nanoseconds. + Weight::from_ref_time(374_985_000) + .saturating_add(Weight::from_proof_size(64844)) + // Standard Error: 3_772_336 + .saturating_add(Weight::from_ref_time(1_546_402_854).saturating_mul(r.into())) .saturating_add(T::DbWeight::get().reads(6_u64)) .saturating_add(T::DbWeight::get().reads((225_u64).saturating_mul(r.into()))) .saturating_add(T::DbWeight::get().writes(3_u64)) @@ -1621,7 +1623,7 @@ impl WeightInfo for SubstrateWeight { /// Storage: System Account (r:1 w:0) /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: Measured) /// Storage: Contracts ContractInfoOf (r:1 w:1) - /// Proof: Contracts ContractInfoOf (max_values: None, max_size: Some(258), added: 2733, mode: Measured) + /// Proof: Contracts ContractInfoOf (max_values: None, max_size: Some(290), added: 2765, mode: Measured) /// Storage: Contracts CodeStorage (r:1 w:0) /// Proof: Contracts CodeStorage (max_values: None, max_size: Some(126001), added: 128476, mode: Measured) /// Storage: Timestamp Now (r:1 w:0) @@ -1631,13 +1633,13 @@ impl WeightInfo for SubstrateWeight { /// The range of component `r` is `[0, 20]`. fn seal_reentrance_count(r: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `837 + r * (240 ±0)` - // Estimated: `17055 + r * (1200 ±0)` - // Minimum execution time: 374_922 nanoseconds. - Weight::from_ref_time(376_234_094) - .saturating_add(Weight::from_proof_size(17055)) - // Standard Error: 45_995 - .saturating_add(Weight::from_ref_time(11_606_505).saturating_mul(r.into())) + // Measured: `869 + r * (240 ±0)` + // Estimated: `17215 + r * (1200 ±0)` + // Minimum execution time: 374_249 nanoseconds. + Weight::from_ref_time(377_990_998) + .saturating_add(Weight::from_proof_size(17215)) + // Standard Error: 38_133 + .saturating_add(Weight::from_ref_time(11_483_273).saturating_mul(r.into())) .saturating_add(T::DbWeight::get().reads(6_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) .saturating_add(Weight::from_proof_size(1200).saturating_mul(r.into())) @@ -1645,7 +1647,7 @@ impl WeightInfo for SubstrateWeight { /// Storage: System Account (r:1 w:0) /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: Measured) /// Storage: Contracts ContractInfoOf (r:1 w:1) - /// Proof: Contracts ContractInfoOf (max_values: None, max_size: Some(258), added: 2733, mode: Measured) + /// Proof: Contracts ContractInfoOf (max_values: None, max_size: Some(290), added: 2765, mode: Measured) /// Storage: Contracts CodeStorage (r:1 w:0) /// Proof: Contracts CodeStorage (max_values: None, max_size: Some(126001), added: 128476, mode: Measured) /// Storage: Timestamp Now (r:1 w:0) @@ -1655,21 +1657,21 @@ impl WeightInfo for SubstrateWeight { /// The range of component `r` is `[0, 20]`. fn seal_account_reentrance_count(r: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `2056 + r * (3153 ±0)` - // Estimated: `21730 + r * (15870 ±2)` - // Minimum execution time: 376_762 nanoseconds. - Weight::from_ref_time(396_934_359) - .saturating_add(Weight::from_proof_size(21730)) - // Standard Error: 68_263 - .saturating_add(Weight::from_ref_time(18_367_270).saturating_mul(r.into())) + // Measured: `2102 + r * (3154 ±0)` + // Estimated: `21980 + r * (15875 ±2)` + // Minimum execution time: 375_552 nanoseconds. + Weight::from_ref_time(400_624_032) + .saturating_add(Weight::from_proof_size(21980)) + // Standard Error: 82_523 + .saturating_add(Weight::from_ref_time(18_057_327).saturating_mul(r.into())) .saturating_add(T::DbWeight::get().reads(6_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) - .saturating_add(Weight::from_proof_size(15870).saturating_mul(r.into())) + .saturating_add(Weight::from_proof_size(15875).saturating_mul(r.into())) } /// Storage: System Account (r:1 w:0) /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: Measured) /// Storage: Contracts ContractInfoOf (r:1 w:1) - /// Proof: Contracts ContractInfoOf (max_values: None, max_size: Some(258), added: 2733, mode: Measured) + /// Proof: Contracts ContractInfoOf (max_values: None, max_size: Some(290), added: 2765, mode: Measured) /// Storage: Contracts CodeStorage (r:1 w:0) /// Proof: Contracts CodeStorage (max_values: None, max_size: Some(126001), added: 128476, mode: Measured) /// Storage: Timestamp Now (r:1 w:0) @@ -1681,13 +1683,13 @@ impl WeightInfo for SubstrateWeight { /// The range of component `r` is `[0, 20]`. fn seal_instantiation_nonce(r: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `840 + r * (240 ±0)` - // Estimated: `18405 + r * (1440 ±0)` - // Minimum execution time: 374_257 nanoseconds. - Weight::from_ref_time(380_453_380) - .saturating_add(Weight::from_proof_size(18405)) - // Standard Error: 42_718 - .saturating_add(Weight::from_ref_time(9_355_253).saturating_mul(r.into())) + // Measured: `872 + r * (240 ±0)` + // Estimated: `18598 + r * (1440 ±0)` + // Minimum execution time: 373_899 nanoseconds. + Weight::from_ref_time(379_733_943) + .saturating_add(Weight::from_proof_size(18598)) + // Standard Error: 32_022 + .saturating_add(Weight::from_ref_time(9_381_180).saturating_mul(r.into())) .saturating_add(T::DbWeight::get().reads(7_u64)) .saturating_add(T::DbWeight::get().writes(4_u64)) .saturating_add(Weight::from_proof_size(1440).saturating_mul(r.into())) @@ -1697,572 +1699,572 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 788 nanoseconds. - Weight::from_ref_time(1_209_829) + // Minimum execution time: 834 nanoseconds. + Weight::from_ref_time(1_009_646) .saturating_add(Weight::from_proof_size(0)) - // Standard Error: 3_436 - .saturating_add(Weight::from_ref_time(409_858).saturating_mul(r.into())) + // Standard Error: 388 + .saturating_add(Weight::from_ref_time(411_979).saturating_mul(r.into())) } /// The range of component `r` is `[0, 50]`. fn instr_i64load(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 955 nanoseconds. - Weight::from_ref_time(1_526_327) + // Minimum execution time: 882 nanoseconds. + Weight::from_ref_time(1_416_377) .saturating_add(Weight::from_proof_size(0)) - // Standard Error: 2_399 - .saturating_add(Weight::from_ref_time(1_084_504).saturating_mul(r.into())) + // Standard Error: 1_133 + .saturating_add(Weight::from_ref_time(1_075_838).saturating_mul(r.into())) } /// The range of component `r` is `[0, 50]`. fn instr_i64store(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 967 nanoseconds. - Weight::from_ref_time(1_576_183) + // Minimum execution time: 878 nanoseconds. + Weight::from_ref_time(1_343_056) .saturating_add(Weight::from_proof_size(0)) - // Standard Error: 5_719 - .saturating_add(Weight::from_ref_time(1_006_742).saturating_mul(r.into())) + // Standard Error: 426 + .saturating_add(Weight::from_ref_time(1_001_214).saturating_mul(r.into())) } /// The range of component `r` is `[0, 50]`. fn instr_select(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 849 nanoseconds. - Weight::from_ref_time(1_106_539) + // Minimum execution time: 796 nanoseconds. + Weight::from_ref_time(1_079_086) .saturating_add(Weight::from_proof_size(0)) - // Standard Error: 445 - .saturating_add(Weight::from_ref_time(1_149_752).saturating_mul(r.into())) + // Standard Error: 409 + .saturating_add(Weight::from_ref_time(1_149_188).saturating_mul(r.into())) } /// The range of component `r` is `[0, 50]`. fn instr_if(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 829 nanoseconds. - Weight::from_ref_time(1_171_360) + // Minimum execution time: 800 nanoseconds. + Weight::from_ref_time(1_044_184) .saturating_add(Weight::from_proof_size(0)) - // Standard Error: 552 - .saturating_add(Weight::from_ref_time(1_309_914).saturating_mul(r.into())) + // Standard Error: 707 + .saturating_add(Weight::from_ref_time(1_315_686).saturating_mul(r.into())) } /// The range of component `r` is `[0, 50]`. fn instr_br(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 835 nanoseconds. - Weight::from_ref_time(1_125_578) + // Minimum execution time: 807 nanoseconds. + Weight::from_ref_time(1_049_633) .saturating_add(Weight::from_proof_size(0)) - // Standard Error: 374 - .saturating_add(Weight::from_ref_time(641_683).saturating_mul(r.into())) + // Standard Error: 361 + .saturating_add(Weight::from_ref_time(640_530).saturating_mul(r.into())) } /// The range of component `r` is `[0, 50]`. fn instr_br_if(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 816 nanoseconds. - Weight::from_ref_time(1_032_093) + // Minimum execution time: 774 nanoseconds. + Weight::from_ref_time(1_124_053) .saturating_add(Weight::from_proof_size(0)) - // Standard Error: 811 - .saturating_add(Weight::from_ref_time(956_228).saturating_mul(r.into())) + // Standard Error: 784 + .saturating_add(Weight::from_ref_time(949_398).saturating_mul(r.into())) } /// The range of component `r` is `[0, 50]`. fn instr_br_table(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 801 nanoseconds. - Weight::from_ref_time(816_764) + // Minimum execution time: 810 nanoseconds. + Weight::from_ref_time(676_581) .saturating_add(Weight::from_proof_size(0)) - // Standard Error: 2_669 - .saturating_add(Weight::from_ref_time(1_166_556).saturating_mul(r.into())) + // Standard Error: 2_356 + .saturating_add(Weight::from_ref_time(1_163_465).saturating_mul(r.into())) } /// The range of component `e` is `[1, 256]`. fn instr_br_table_per_entry(e: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 2_639 nanoseconds. - Weight::from_ref_time(2_905_554) + // Minimum execution time: 2_580 nanoseconds. + Weight::from_ref_time(2_835_656) .saturating_add(Weight::from_proof_size(0)) - // Standard Error: 62 - .saturating_add(Weight::from_ref_time(4_438).saturating_mul(e.into())) + // Standard Error: 71 + .saturating_add(Weight::from_ref_time(4_686).saturating_mul(e.into())) } /// The range of component `r` is `[0, 50]`. fn instr_call(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 831 nanoseconds. - Weight::from_ref_time(1_729_584) + // Minimum execution time: 826 nanoseconds. + Weight::from_ref_time(1_625_698) .saturating_add(Weight::from_proof_size(0)) - // Standard Error: 33_753 - .saturating_add(Weight::from_ref_time(2_380_315).saturating_mul(r.into())) + // Standard Error: 1_740 + .saturating_add(Weight::from_ref_time(2_332_187).saturating_mul(r.into())) } /// The range of component `r` is `[0, 50]`. fn instr_call_indirect(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 964 nanoseconds. - Weight::from_ref_time(2_445_291) + // Minimum execution time: 901 nanoseconds. + Weight::from_ref_time(2_338_620) .saturating_add(Weight::from_proof_size(0)) - // Standard Error: 3_285 - .saturating_add(Weight::from_ref_time(2_938_681).saturating_mul(r.into())) + // Standard Error: 1_642 + .saturating_add(Weight::from_ref_time(2_924_090).saturating_mul(r.into())) } /// The range of component `p` is `[0, 128]`. fn instr_call_indirect_per_param(p: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 4_809 nanoseconds. - Weight::from_ref_time(6_763_286) + // Minimum execution time: 4_670 nanoseconds. + Weight::from_ref_time(5_556_246) .saturating_add(Weight::from_proof_size(0)) - // Standard Error: 3_994 - .saturating_add(Weight::from_ref_time(217_632).saturating_mul(p.into())) + // Standard Error: 1_491 + .saturating_add(Weight::from_ref_time(228_965).saturating_mul(p.into())) } /// The range of component `l` is `[0, 1024]`. fn instr_call_per_local(l: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 3_138 nanoseconds. - Weight::from_ref_time(3_894_816) + // Minimum execution time: 3_099 nanoseconds. + Weight::from_ref_time(3_896_177) .saturating_add(Weight::from_proof_size(0)) - // Standard Error: 131 - .saturating_add(Weight::from_ref_time(91_699).saturating_mul(l.into())) + // Standard Error: 99 + .saturating_add(Weight::from_ref_time(91_304).saturating_mul(l.into())) } /// The range of component `r` is `[0, 50]`. fn instr_local_get(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 2_959 nanoseconds. - Weight::from_ref_time(3_271_550) + // Minimum execution time: 3_042 nanoseconds. + Weight::from_ref_time(3_334_621) .saturating_add(Weight::from_proof_size(0)) - // Standard Error: 223 - .saturating_add(Weight::from_ref_time(460_056).saturating_mul(r.into())) + // Standard Error: 793 + .saturating_add(Weight::from_ref_time(459_346).saturating_mul(r.into())) } /// The range of component `r` is `[0, 50]`. fn instr_local_set(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 2_970 nanoseconds. - Weight::from_ref_time(3_216_157) + // Minimum execution time: 2_968 nanoseconds. + Weight::from_ref_time(3_235_286) .saturating_add(Weight::from_proof_size(0)) - // Standard Error: 413 - .saturating_add(Weight::from_ref_time(485_842).saturating_mul(r.into())) + // Standard Error: 427 + .saturating_add(Weight::from_ref_time(485_454).saturating_mul(r.into())) } /// The range of component `r` is `[0, 50]`. fn instr_local_tee(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 2_980 nanoseconds. - Weight::from_ref_time(3_323_878) + // Minimum execution time: 3_012 nanoseconds. + Weight::from_ref_time(3_303_555) .saturating_add(Weight::from_proof_size(0)) - // Standard Error: 2_652 - .saturating_add(Weight::from_ref_time(660_257).saturating_mul(r.into())) + // Standard Error: 371 + .saturating_add(Weight::from_ref_time(657_811).saturating_mul(r.into())) } /// The range of component `r` is `[0, 50]`. fn instr_global_get(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 917 nanoseconds. - Weight::from_ref_time(1_445_816) + // Minimum execution time: 865 nanoseconds. + Weight::from_ref_time(1_249_987) .saturating_add(Weight::from_proof_size(0)) - // Standard Error: 5_642 - .saturating_add(Weight::from_ref_time(894_521).saturating_mul(r.into())) + // Standard Error: 417 + .saturating_add(Weight::from_ref_time(896_704).saturating_mul(r.into())) } /// The range of component `r` is `[0, 50]`. fn instr_global_set(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 965 nanoseconds. - Weight::from_ref_time(1_373_722) + // Minimum execution time: 866 nanoseconds. + Weight::from_ref_time(1_216_218) .saturating_add(Weight::from_proof_size(0)) - // Standard Error: 1_157 - .saturating_add(Weight::from_ref_time(917_643).saturating_mul(r.into())) + // Standard Error: 503 + .saturating_add(Weight::from_ref_time(919_719).saturating_mul(r.into())) } /// The range of component `r` is `[0, 50]`. fn instr_memory_current(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 982 nanoseconds. - Weight::from_ref_time(1_240_280) + // Minimum execution time: 921 nanoseconds. + Weight::from_ref_time(1_228_408) .saturating_add(Weight::from_proof_size(0)) - // Standard Error: 308 - .saturating_add(Weight::from_ref_time(817_972).saturating_mul(r.into())) + // Standard Error: 309 + .saturating_add(Weight::from_ref_time(813_007).saturating_mul(r.into())) } /// The range of component `r` is `[0, 1]`. fn instr_memory_grow(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 858 nanoseconds. - Weight::from_ref_time(962_183) + // Minimum execution time: 820 nanoseconds. + Weight::from_ref_time(914_830) .saturating_add(Weight::from_proof_size(0)) - // Standard Error: 6_701 - .saturating_add(Weight::from_ref_time(239_704_216).saturating_mul(r.into())) + // Standard Error: 6_018 + .saturating_add(Weight::from_ref_time(237_062_769).saturating_mul(r.into())) } /// The range of component `r` is `[0, 50]`. fn instr_i64clz(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 807 nanoseconds. - Weight::from_ref_time(1_132_872) + // Minimum execution time: 812 nanoseconds. + Weight::from_ref_time(1_554_406) .saturating_add(Weight::from_proof_size(0)) - // Standard Error: 303 - .saturating_add(Weight::from_ref_time(633_832).saturating_mul(r.into())) + // Standard Error: 9_979 + .saturating_add(Weight::from_ref_time(625_434).saturating_mul(r.into())) } /// The range of component `r` is `[0, 50]`. fn instr_i64ctz(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 823 nanoseconds. - Weight::from_ref_time(1_267_518) + // Minimum execution time: 798 nanoseconds. + Weight::from_ref_time(1_095_113) .saturating_add(Weight::from_proof_size(0)) - // Standard Error: 483 - .saturating_add(Weight::from_ref_time(632_620).saturating_mul(r.into())) + // Standard Error: 243 + .saturating_add(Weight::from_ref_time(634_204).saturating_mul(r.into())) } /// The range of component `r` is `[0, 50]`. fn instr_i64popcnt(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 857 nanoseconds. - Weight::from_ref_time(1_105_214) + // Minimum execution time: 792 nanoseconds. + Weight::from_ref_time(1_109_845) .saturating_add(Weight::from_proof_size(0)) - // Standard Error: 285 - .saturating_add(Weight::from_ref_time(635_039).saturating_mul(r.into())) + // Standard Error: 14_944 + .saturating_add(Weight::from_ref_time(658_834).saturating_mul(r.into())) } /// The range of component `r` is `[0, 50]`. fn instr_i64eqz(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 821 nanoseconds. - Weight::from_ref_time(750_223) + // Minimum execution time: 815 nanoseconds. + Weight::from_ref_time(1_068_916) .saturating_add(Weight::from_proof_size(0)) - // Standard Error: 15_923 - .saturating_add(Weight::from_ref_time(686_322).saturating_mul(r.into())) + // Standard Error: 327 + .saturating_add(Weight::from_ref_time(652_897).saturating_mul(r.into())) } /// The range of component `r` is `[0, 50]`. fn instr_i64extendsi32(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 797 nanoseconds. - Weight::from_ref_time(1_145_072) + // Minimum execution time: 798 nanoseconds. + Weight::from_ref_time(1_069_745) .saturating_add(Weight::from_proof_size(0)) - // Standard Error: 311 - .saturating_add(Weight::from_ref_time(618_147).saturating_mul(r.into())) + // Standard Error: 306 + .saturating_add(Weight::from_ref_time(618_481).saturating_mul(r.into())) } /// The range of component `r` is `[0, 50]`. fn instr_i64extendui32(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 803 nanoseconds. - Weight::from_ref_time(1_139_498) + // Minimum execution time: 799 nanoseconds. + Weight::from_ref_time(1_398_001) .saturating_add(Weight::from_proof_size(0)) - // Standard Error: 284 - .saturating_add(Weight::from_ref_time(617_393).saturating_mul(r.into())) + // Standard Error: 6_234 + .saturating_add(Weight::from_ref_time(611_399).saturating_mul(r.into())) } /// The range of component `r` is `[0, 50]`. fn instr_i32wrapi64(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 814 nanoseconds. - Weight::from_ref_time(1_099_405) + // Minimum execution time: 811 nanoseconds. + Weight::from_ref_time(1_098_083) .saturating_add(Weight::from_proof_size(0)) - // Standard Error: 663 - .saturating_add(Weight::from_ref_time(618_565).saturating_mul(r.into())) + // Standard Error: 297 + .saturating_add(Weight::from_ref_time(617_692).saturating_mul(r.into())) } /// The range of component `r` is `[0, 50]`. fn instr_i64eq(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 819 nanoseconds. - Weight::from_ref_time(1_199_220) + // Minimum execution time: 815 nanoseconds. + Weight::from_ref_time(1_046_922) .saturating_add(Weight::from_proof_size(0)) - // Standard Error: 485 - .saturating_add(Weight::from_ref_time(906_878).saturating_mul(r.into())) + // Standard Error: 335 + .saturating_add(Weight::from_ref_time(909_196).saturating_mul(r.into())) } /// The range of component `r` is `[0, 50]`. fn instr_i64ne(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 822 nanoseconds. - Weight::from_ref_time(274_212) + // Minimum execution time: 805 nanoseconds. + Weight::from_ref_time(1_093_667) .saturating_add(Weight::from_proof_size(0)) - // Standard Error: 29_294 - .saturating_add(Weight::from_ref_time(971_608).saturating_mul(r.into())) + // Standard Error: 233 + .saturating_add(Weight::from_ref_time(907_378).saturating_mul(r.into())) } /// The range of component `r` is `[0, 50]`. fn instr_i64lts(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 796 nanoseconds. - Weight::from_ref_time(1_396_586) + // Minimum execution time: 805 nanoseconds. + Weight::from_ref_time(1_290_591) .saturating_add(Weight::from_proof_size(0)) - // Standard Error: 7_205 - .saturating_add(Weight::from_ref_time(903_202).saturating_mul(r.into())) + // Standard Error: 3_201 + .saturating_add(Weight::from_ref_time(902_006).saturating_mul(r.into())) } /// The range of component `r` is `[0, 50]`. fn instr_i64ltu(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 833 nanoseconds. - Weight::from_ref_time(1_115_115) + // Minimum execution time: 783 nanoseconds. + Weight::from_ref_time(1_159_977) .saturating_add(Weight::from_proof_size(0)) - // Standard Error: 310 - .saturating_add(Weight::from_ref_time(908_195).saturating_mul(r.into())) + // Standard Error: 2_310 + .saturating_add(Weight::from_ref_time(906_489).saturating_mul(r.into())) } /// The range of component `r` is `[0, 50]`. fn instr_i64gts(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 854 nanoseconds. - Weight::from_ref_time(1_170_419) + // Minimum execution time: 790 nanoseconds. + Weight::from_ref_time(1_109_719) .saturating_add(Weight::from_proof_size(0)) - // Standard Error: 298 - .saturating_add(Weight::from_ref_time(907_171).saturating_mul(r.into())) + // Standard Error: 261 + .saturating_add(Weight::from_ref_time(906_614).saturating_mul(r.into())) } /// The range of component `r` is `[0, 50]`. fn instr_i64gtu(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 822 nanoseconds. - Weight::from_ref_time(1_186_349) + // Minimum execution time: 776 nanoseconds. + Weight::from_ref_time(1_076_567) .saturating_add(Weight::from_proof_size(0)) - // Standard Error: 302 - .saturating_add(Weight::from_ref_time(917_857).saturating_mul(r.into())) + // Standard Error: 348 + .saturating_add(Weight::from_ref_time(919_374).saturating_mul(r.into())) } /// The range of component `r` is `[0, 50]`. fn instr_i64les(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 807 nanoseconds. - Weight::from_ref_time(1_127_093) + // Minimum execution time: 780 nanoseconds. + Weight::from_ref_time(1_069_663) .saturating_add(Weight::from_proof_size(0)) - // Standard Error: 1_891 - .saturating_add(Weight::from_ref_time(910_738).saturating_mul(r.into())) + // Standard Error: 265 + .saturating_add(Weight::from_ref_time(908_037).saturating_mul(r.into())) } /// The range of component `r` is `[0, 50]`. fn instr_i64leu(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 819 nanoseconds. - Weight::from_ref_time(1_143_022) + // Minimum execution time: 832 nanoseconds. + Weight::from_ref_time(930_920) .saturating_add(Weight::from_proof_size(0)) - // Standard Error: 309 - .saturating_add(Weight::from_ref_time(919_047).saturating_mul(r.into())) + // Standard Error: 2_170 + .saturating_add(Weight::from_ref_time(929_811).saturating_mul(r.into())) } /// The range of component `r` is `[0, 50]`. fn instr_i64ges(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 788 nanoseconds. - Weight::from_ref_time(1_116_914) + // Minimum execution time: 787 nanoseconds. + Weight::from_ref_time(1_087_325) .saturating_add(Weight::from_proof_size(0)) - // Standard Error: 643 - .saturating_add(Weight::from_ref_time(911_159).saturating_mul(r.into())) + // Standard Error: 315 + .saturating_add(Weight::from_ref_time(908_321).saturating_mul(r.into())) } /// The range of component `r` is `[0, 50]`. fn instr_i64geu(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 791 nanoseconds. - Weight::from_ref_time(1_123_747) + // Minimum execution time: 798 nanoseconds. + Weight::from_ref_time(1_029_132) .saturating_add(Weight::from_proof_size(0)) - // Standard Error: 583 - .saturating_add(Weight::from_ref_time(910_242).saturating_mul(r.into())) + // Standard Error: 2_095 + .saturating_add(Weight::from_ref_time(913_553).saturating_mul(r.into())) } /// The range of component `r` is `[0, 50]`. fn instr_i64add(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 787 nanoseconds. - Weight::from_ref_time(1_109_471) + // Minimum execution time: 791 nanoseconds. + Weight::from_ref_time(1_086_314) .saturating_add(Weight::from_proof_size(0)) - // Standard Error: 305 - .saturating_add(Weight::from_ref_time(897_608).saturating_mul(r.into())) + // Standard Error: 197 + .saturating_add(Weight::from_ref_time(896_392).saturating_mul(r.into())) } /// The range of component `r` is `[0, 50]`. fn instr_i64sub(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 821 nanoseconds. - Weight::from_ref_time(1_098_631) + // Minimum execution time: 793 nanoseconds. + Weight::from_ref_time(1_078_172) .saturating_add(Weight::from_proof_size(0)) - // Standard Error: 523 - .saturating_add(Weight::from_ref_time(887_814).saturating_mul(r.into())) + // Standard Error: 404 + .saturating_add(Weight::from_ref_time(886_329).saturating_mul(r.into())) } /// The range of component `r` is `[0, 50]`. fn instr_i64mul(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 794 nanoseconds. - Weight::from_ref_time(1_166_332) + // Minimum execution time: 799 nanoseconds. + Weight::from_ref_time(1_095_010) .saturating_add(Weight::from_proof_size(0)) - // Standard Error: 384 - .saturating_add(Weight::from_ref_time(885_584).saturating_mul(r.into())) + // Standard Error: 431 + .saturating_add(Weight::from_ref_time(886_513).saturating_mul(r.into())) } /// The range of component `r` is `[0, 50]`. fn instr_i64divs(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 822 nanoseconds. - Weight::from_ref_time(1_155_826) + // Minimum execution time: 810 nanoseconds. + Weight::from_ref_time(1_114_325) .saturating_add(Weight::from_proof_size(0)) - // Standard Error: 521 - .saturating_add(Weight::from_ref_time(1_520_958).saturating_mul(r.into())) + // Standard Error: 452 + .saturating_add(Weight::from_ref_time(1_521_849).saturating_mul(r.into())) } /// The range of component `r` is `[0, 50]`. fn instr_i64divu(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 881 nanoseconds. - Weight::from_ref_time(1_158_125) + // Minimum execution time: 784 nanoseconds. + Weight::from_ref_time(1_123_153) .saturating_add(Weight::from_proof_size(0)) - // Standard Error: 624 - .saturating_add(Weight::from_ref_time(1_458_378).saturating_mul(r.into())) + // Standard Error: 475 + .saturating_add(Weight::from_ref_time(1_457_746).saturating_mul(r.into())) } /// The range of component `r` is `[0, 50]`. fn instr_i64rems(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 827 nanoseconds. - Weight::from_ref_time(1_209_535) + // Minimum execution time: 809 nanoseconds. + Weight::from_ref_time(1_145_906) .saturating_add(Weight::from_proof_size(0)) - // Standard Error: 399 - .saturating_add(Weight::from_ref_time(1_547_640).saturating_mul(r.into())) + // Standard Error: 718 + .saturating_add(Weight::from_ref_time(1_549_964).saturating_mul(r.into())) } /// The range of component `r` is `[0, 50]`. fn instr_i64remu(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 801 nanoseconds. - Weight::from_ref_time(1_313_872) + // Minimum execution time: 803 nanoseconds. + Weight::from_ref_time(1_110_328) .saturating_add(Weight::from_proof_size(0)) - // Standard Error: 2_175 - .saturating_add(Weight::from_ref_time(1_449_416).saturating_mul(r.into())) + // Standard Error: 627 + .saturating_add(Weight::from_ref_time(1_453_013).saturating_mul(r.into())) } /// The range of component `r` is `[0, 50]`. fn instr_i64and(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 827 nanoseconds. - Weight::from_ref_time(1_093_874) + // Minimum execution time: 786 nanoseconds. + Weight::from_ref_time(1_108_792) .saturating_add(Weight::from_proof_size(0)) - // Standard Error: 708 - .saturating_add(Weight::from_ref_time(901_450).saturating_mul(r.into())) + // Standard Error: 286 + .saturating_add(Weight::from_ref_time(897_035).saturating_mul(r.into())) } /// The range of component `r` is `[0, 50]`. fn instr_i64or(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 824 nanoseconds. - Weight::from_ref_time(1_164_076) + // Minimum execution time: 787 nanoseconds. + Weight::from_ref_time(830_000) .saturating_add(Weight::from_proof_size(0)) - // Standard Error: 623 - .saturating_add(Weight::from_ref_time(897_579).saturating_mul(r.into())) + // Standard Error: 15_995 + .saturating_add(Weight::from_ref_time(963_344).saturating_mul(r.into())) } /// The range of component `r` is `[0, 50]`. fn instr_i64xor(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 789 nanoseconds. - Weight::from_ref_time(1_113_915) + // Minimum execution time: 773 nanoseconds. + Weight::from_ref_time(1_082_459) .saturating_add(Weight::from_proof_size(0)) - // Standard Error: 390 - .saturating_add(Weight::from_ref_time(897_354).saturating_mul(r.into())) + // Standard Error: 330 + .saturating_add(Weight::from_ref_time(897_077).saturating_mul(r.into())) } /// The range of component `r` is `[0, 50]`. fn instr_i64shl(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 811 nanoseconds. - Weight::from_ref_time(1_117_366) + // Minimum execution time: 810 nanoseconds. + Weight::from_ref_time(1_325_815) .saturating_add(Weight::from_proof_size(0)) - // Standard Error: 437 - .saturating_add(Weight::from_ref_time(903_759).saturating_mul(r.into())) + // Standard Error: 3_352 + .saturating_add(Weight::from_ref_time(899_555).saturating_mul(r.into())) } /// The range of component `r` is `[0, 50]`. fn instr_i64shrs(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 846 nanoseconds. - Weight::from_ref_time(1_103_954) + // Minimum execution time: 808 nanoseconds. + Weight::from_ref_time(1_085_903) .saturating_add(Weight::from_proof_size(0)) - // Standard Error: 414 - .saturating_add(Weight::from_ref_time(903_429).saturating_mul(r.into())) + // Standard Error: 430 + .saturating_add(Weight::from_ref_time(903_249).saturating_mul(r.into())) } /// The range of component `r` is `[0, 50]`. fn instr_i64shru(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 803 nanoseconds. - Weight::from_ref_time(1_124_328) + // Minimum execution time: 792 nanoseconds. + Weight::from_ref_time(1_091_261) .saturating_add(Weight::from_proof_size(0)) - // Standard Error: 410 - .saturating_add(Weight::from_ref_time(903_216).saturating_mul(r.into())) + // Standard Error: 312 + .saturating_add(Weight::from_ref_time(902_245).saturating_mul(r.into())) } /// The range of component `r` is `[0, 50]`. fn instr_i64rotl(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 810 nanoseconds. - Weight::from_ref_time(1_131_433) + // Minimum execution time: 807 nanoseconds. + Weight::from_ref_time(1_121_052) .saturating_add(Weight::from_proof_size(0)) - // Standard Error: 402 - .saturating_add(Weight::from_ref_time(903_381).saturating_mul(r.into())) + // Standard Error: 506 + .saturating_add(Weight::from_ref_time(902_772).saturating_mul(r.into())) } /// The range of component `r` is `[0, 50]`. fn instr_i64rotr(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 807 nanoseconds. - Weight::from_ref_time(1_144_933) + // Minimum execution time: 823 nanoseconds. + Weight::from_ref_time(1_317_597) .saturating_add(Weight::from_proof_size(0)) - // Standard Error: 373 - .saturating_add(Weight::from_ref_time(902_918).saturating_mul(r.into())) + // Standard Error: 6_219 + .saturating_add(Weight::from_ref_time(896_692).saturating_mul(r.into())) } } @@ -2274,8 +2276,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `109` // Estimated: `604` - // Minimum execution time: 2_507 nanoseconds. - Weight::from_ref_time(2_650_000) + // Minimum execution time: 2_591 nanoseconds. + Weight::from_ref_time(2_817_000) .saturating_add(Weight::from_proof_size(604)) .saturating_add(RocksDbWeight::get().reads(1_u64)) } @@ -2286,11 +2288,11 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `481 + k * (69 ±0)` // Estimated: `471 + k * (70 ±0)` - // Minimum execution time: 9_953 nanoseconds. - Weight::from_ref_time(6_467_352) + // Minimum execution time: 10_190 nanoseconds. + Weight::from_ref_time(6_642_117) .saturating_add(Weight::from_proof_size(471)) - // Standard Error: 1_074 - .saturating_add(Weight::from_ref_time(943_946).saturating_mul(k.into())) + // Standard Error: 992 + .saturating_add(Weight::from_ref_time(919_828).saturating_mul(k.into())) .saturating_add(RocksDbWeight::get().reads(1_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) .saturating_add(RocksDbWeight::get().writes((1_u64).saturating_mul(k.into()))) @@ -2303,11 +2305,11 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `281 + q * (33 ±0)` // Estimated: `763 + q * (33 ±0)` - // Minimum execution time: 2_545 nanoseconds. - Weight::from_ref_time(10_080_106) + // Minimum execution time: 2_598 nanoseconds. + Weight::from_ref_time(10_288_252) .saturating_add(Weight::from_proof_size(763)) - // Standard Error: 2_929 - .saturating_add(Weight::from_ref_time(1_078_265).saturating_mul(q.into())) + // Standard Error: 2_886 + .saturating_add(Weight::from_ref_time(1_092_420).saturating_mul(q.into())) .saturating_add(RocksDbWeight::get().reads(1_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) .saturating_add(Weight::from_proof_size(33).saturating_mul(q.into())) @@ -2321,17 +2323,17 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `270 + c * (1 ±0)` // Estimated: `3025 + c * (2 ±0)` - // Minimum execution time: 34_613 nanoseconds. - Weight::from_ref_time(27_355_774) + // Minimum execution time: 34_338 nanoseconds. + Weight::from_ref_time(32_159_677) .saturating_add(Weight::from_proof_size(3025)) - // Standard Error: 81 - .saturating_add(Weight::from_ref_time(51_954).saturating_mul(c.into())) + // Standard Error: 53 + .saturating_add(Weight::from_ref_time(51_034).saturating_mul(c.into())) .saturating_add(RocksDbWeight::get().reads(1_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) .saturating_add(Weight::from_proof_size(2).saturating_mul(c.into())) } /// Storage: Contracts ContractInfoOf (r:1 w:1) - /// Proof: Contracts ContractInfoOf (max_values: None, max_size: Some(258), added: 2733, mode: Measured) + /// Proof: Contracts ContractInfoOf (max_values: None, max_size: Some(290), added: 2765, mode: Measured) /// Storage: Contracts CodeStorage (r:1 w:0) /// Proof: Contracts CodeStorage (max_values: None, max_size: Some(126001), added: 128476, mode: Measured) /// Storage: Timestamp Now (r:1 w:0) @@ -2343,13 +2345,13 @@ impl WeightInfo for () { /// The range of component `c` is `[0, 125952]`. fn call_with_code_per_byte(c: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `771` - // Estimated: `16780 + c * (5 ±0)` - // Minimum execution time: 386_104 nanoseconds. - Weight::from_ref_time(396_718_823) - .saturating_add(Weight::from_proof_size(16780)) + // Measured: `803` + // Estimated: `16930 + c * (5 ±0)` + // Minimum execution time: 385_587 nanoseconds. + Weight::from_ref_time(395_545_811) + .saturating_add(Weight::from_proof_size(16930)) // Standard Error: 27 - .saturating_add(Weight::from_ref_time(31_370).saturating_mul(c.into())) + .saturating_add(Weight::from_ref_time(31_342).saturating_mul(c.into())) .saturating_add(RocksDbWeight::get().reads(6_u64)) .saturating_add(RocksDbWeight::get().writes(4_u64)) .saturating_add(Weight::from_proof_size(5).saturating_mul(c.into())) @@ -2359,10 +2361,10 @@ impl WeightInfo for () { /// Storage: Contracts Nonce (r:1 w:1) /// Proof: Contracts Nonce (max_values: Some(1), max_size: Some(8), added: 503, mode: Measured) /// Storage: Contracts ContractInfoOf (r:1 w:1) - /// Proof: Contracts ContractInfoOf (max_values: None, max_size: Some(258), added: 2733, mode: Measured) + /// Proof: Contracts ContractInfoOf (max_values: None, max_size: Some(290), added: 2765, mode: Measured) /// Storage: Timestamp Now (r:1 w:0) /// Proof: Timestamp Now (max_values: Some(1), max_size: Some(8), added: 503, mode: Measured) - /// Storage: System Account (r:1 w:1) + /// Storage: System Account (r:2 w:2) /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: Measured) /// Storage: System EventTopics (r:3 w:3) /// Proof Skipped: System EventTopics (max_values: None, max_size: None, mode: Measured) @@ -2375,29 +2377,29 @@ impl WeightInfo for () { /// The range of component `s` is `[0, 1048576]`. fn instantiate_with_code(c: u32, i: u32, s: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `257` - // Estimated: `17752` - // Minimum execution time: 3_786_500 nanoseconds. - Weight::from_ref_time(672_511_565) - .saturating_add(Weight::from_proof_size(17752)) - // Standard Error: 281 - .saturating_add(Weight::from_ref_time(94_538).saturating_mul(c.into())) + // Measured: `270` + // Estimated: `20267` + // Minimum execution time: 3_799_742 nanoseconds. + Weight::from_ref_time(670_115_588) + .saturating_add(Weight::from_proof_size(20267)) + // Standard Error: 287 + .saturating_add(Weight::from_ref_time(93_885).saturating_mul(c.into())) // Standard Error: 16 - .saturating_add(Weight::from_ref_time(1_364).saturating_mul(i.into())) + .saturating_add(Weight::from_ref_time(1_367).saturating_mul(i.into())) // Standard Error: 16 - .saturating_add(Weight::from_ref_time(1_768).saturating_mul(s.into())) - .saturating_add(RocksDbWeight::get().reads(8_u64)) - .saturating_add(RocksDbWeight::get().writes(9_u64)) + .saturating_add(Weight::from_ref_time(1_781).saturating_mul(s.into())) + .saturating_add(RocksDbWeight::get().reads(9_u64)) + .saturating_add(RocksDbWeight::get().writes(10_u64)) } /// Storage: Contracts CodeStorage (r:1 w:0) /// Proof: Contracts CodeStorage (max_values: None, max_size: Some(126001), added: 128476, mode: Measured) /// Storage: Contracts Nonce (r:1 w:1) /// Proof: Contracts Nonce (max_values: Some(1), max_size: Some(8), added: 503, mode: Measured) /// Storage: Contracts ContractInfoOf (r:1 w:1) - /// Proof: Contracts ContractInfoOf (max_values: None, max_size: Some(258), added: 2733, mode: Measured) + /// Proof: Contracts ContractInfoOf (max_values: None, max_size: Some(290), added: 2765, mode: Measured) /// Storage: Timestamp Now (r:1 w:0) /// Proof: Timestamp Now (max_values: Some(1), max_size: Some(8), added: 503, mode: Measured) - /// Storage: System Account (r:1 w:1) + /// Storage: System Account (r:2 w:2) /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: Measured) /// Storage: Contracts OwnerInfoOf (r:1 w:1) /// Proof: Contracts OwnerInfoOf (max_values: None, max_size: Some(88), added: 2563, mode: Measured) @@ -2407,20 +2409,20 @@ impl WeightInfo for () { /// The range of component `s` is `[0, 1048576]`. fn instantiate(i: u32, s: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `533` - // Estimated: `19543` - // Minimum execution time: 1_927_365 nanoseconds. - Weight::from_ref_time(189_843_928) - .saturating_add(Weight::from_proof_size(19543)) + // Measured: `546` + // Estimated: `22039` + // Minimum execution time: 1_949_008 nanoseconds. + Weight::from_ref_time(214_033_418) + .saturating_add(Weight::from_proof_size(22039)) // Standard Error: 8 - .saturating_add(Weight::from_ref_time(1_677).saturating_mul(i.into())) + .saturating_add(Weight::from_ref_time(1_666).saturating_mul(i.into())) // Standard Error: 8 - .saturating_add(Weight::from_ref_time(1_803).saturating_mul(s.into())) - .saturating_add(RocksDbWeight::get().reads(8_u64)) - .saturating_add(RocksDbWeight::get().writes(6_u64)) + .saturating_add(Weight::from_ref_time(1_801).saturating_mul(s.into())) + .saturating_add(RocksDbWeight::get().reads(9_u64)) + .saturating_add(RocksDbWeight::get().writes(7_u64)) } /// Storage: Contracts ContractInfoOf (r:1 w:1) - /// Proof: Contracts ContractInfoOf (max_values: None, max_size: Some(258), added: 2733, mode: Measured) + /// Proof: Contracts ContractInfoOf (max_values: None, max_size: Some(290), added: 2765, mode: Measured) /// Storage: Contracts CodeStorage (r:1 w:0) /// Proof: Contracts CodeStorage (max_values: None, max_size: Some(126001), added: 128476, mode: Measured) /// Storage: Timestamp Now (r:1 w:0) @@ -2431,11 +2433,11 @@ impl WeightInfo for () { /// Proof Skipped: System EventTopics (max_values: None, max_size: None, mode: Measured) fn call() -> Weight { // Proof Size summary in bytes: - // Measured: `823` - // Estimated: `16985` - // Minimum execution time: 146_354 nanoseconds. - Weight::from_ref_time(147_693_000) - .saturating_add(Weight::from_proof_size(16985)) + // Measured: `855` + // Estimated: `17145` + // Minimum execution time: 146_654 nanoseconds. + Weight::from_ref_time(147_528_000) + .saturating_add(Weight::from_proof_size(17145)) .saturating_add(RocksDbWeight::get().reads(6_u64)) .saturating_add(RocksDbWeight::get().writes(4_u64)) } @@ -2452,11 +2454,11 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `109` // Estimated: `5386` - // Minimum execution time: 388_107 nanoseconds. - Weight::from_ref_time(383_172_702) + // Minimum execution time: 387_889 nanoseconds. + Weight::from_ref_time(391_379_335) .saturating_add(Weight::from_proof_size(5386)) - // Standard Error: 73 - .saturating_add(Weight::from_ref_time(95_037).saturating_mul(c.into())) + // Standard Error: 89 + .saturating_add(Weight::from_ref_time(94_810).saturating_mul(c.into())) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().writes(4_u64)) } @@ -2472,32 +2474,32 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `287` // Estimated: `6098` - // Minimum execution time: 26_278 nanoseconds. - Weight::from_ref_time(26_682_000) + // Minimum execution time: 26_014 nanoseconds. + Weight::from_ref_time(26_510_000) .saturating_add(Weight::from_proof_size(6098)) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().writes(4_u64)) } /// Storage: Contracts ContractInfoOf (r:1 w:1) - /// Proof: Contracts ContractInfoOf (max_values: None, max_size: Some(258), added: 2733, mode: Measured) + /// Proof: Contracts ContractInfoOf (max_values: None, max_size: Some(290), added: 2765, mode: Measured) /// Storage: Contracts OwnerInfoOf (r:2 w:2) /// Proof: Contracts OwnerInfoOf (max_values: None, max_size: Some(88), added: 2563, mode: Measured) /// Storage: System EventTopics (r:3 w:3) /// Proof Skipped: System EventTopics (max_values: None, max_size: None, mode: Measured) fn set_code() -> Weight { // Proof Size summary in bytes: - // Measured: `634` - // Estimated: `16752` - // Minimum execution time: 30_223 nanoseconds. - Weight::from_ref_time(30_737_000) - .saturating_add(Weight::from_proof_size(16752)) + // Measured: `666` + // Estimated: `16848` + // Minimum execution time: 30_177 nanoseconds. + Weight::from_ref_time(30_639_000) + .saturating_add(Weight::from_proof_size(16848)) .saturating_add(RocksDbWeight::get().reads(6_u64)) .saturating_add(RocksDbWeight::get().writes(6_u64)) } /// Storage: System Account (r:1 w:0) /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: Measured) /// Storage: Contracts ContractInfoOf (r:1 w:1) - /// Proof: Contracts ContractInfoOf (max_values: None, max_size: Some(258), added: 2733, mode: Measured) + /// Proof: Contracts ContractInfoOf (max_values: None, max_size: Some(290), added: 2765, mode: Measured) /// Storage: Contracts CodeStorage (r:1 w:0) /// Proof: Contracts CodeStorage (max_values: None, max_size: Some(126001), added: 128476, mode: Measured) /// Storage: Timestamp Now (r:1 w:0) @@ -2507,13 +2509,13 @@ impl WeightInfo for () { /// The range of component `r` is `[0, 20]`. fn seal_caller(r: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `845 + r * (480 ±0)` - // Estimated: `17120 + r * (2400 ±0)` - // Minimum execution time: 374_780 nanoseconds. - Weight::from_ref_time(379_468_453) - .saturating_add(Weight::from_proof_size(17120)) - // Standard Error: 45_809 - .saturating_add(Weight::from_ref_time(17_553_577).saturating_mul(r.into())) + // Measured: `877 + r * (480 ±0)` + // Estimated: `17295 + r * (2400 ±0)` + // Minimum execution time: 373_786 nanoseconds. + Weight::from_ref_time(377_332_691) + .saturating_add(Weight::from_proof_size(17295)) + // Standard Error: 51_211 + .saturating_add(Weight::from_ref_time(17_715_615).saturating_mul(r.into())) .saturating_add(RocksDbWeight::get().reads(6_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) .saturating_add(Weight::from_proof_size(2400).saturating_mul(r.into())) @@ -2521,7 +2523,7 @@ impl WeightInfo for () { /// Storage: System Account (r:1 w:0) /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: Measured) /// Storage: Contracts ContractInfoOf (r:1601 w:1) - /// Proof: Contracts ContractInfoOf (max_values: None, max_size: Some(258), added: 2733, mode: Measured) + /// Proof: Contracts ContractInfoOf (max_values: None, max_size: Some(290), added: 2765, mode: Measured) /// Storage: Contracts CodeStorage (r:1 w:0) /// Proof: Contracts CodeStorage (max_values: None, max_size: Some(126001), added: 128476, mode: Measured) /// Storage: Timestamp Now (r:1 w:0) @@ -2531,22 +2533,22 @@ impl WeightInfo for () { /// The range of component `r` is `[0, 20]`. fn seal_is_contract(r: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `882 + r * (19218 ±0)` - // Estimated: `17110 + r * (294100 ±0)` - // Minimum execution time: 374_399 nanoseconds. - Weight::from_ref_time(228_569_211) - .saturating_add(Weight::from_proof_size(17110)) - // Standard Error: 492_562 - .saturating_add(Weight::from_ref_time(251_682_897).saturating_mul(r.into())) + // Measured: `917 + r * (21778 ±0)` + // Estimated: `17295 + r * (306895 ±0)` + // Minimum execution time: 374_009 nanoseconds. + Weight::from_ref_time(238_991_986) + .saturating_add(Weight::from_proof_size(17295)) + // Standard Error: 464_711 + .saturating_add(Weight::from_ref_time(249_099_538).saturating_mul(r.into())) .saturating_add(RocksDbWeight::get().reads(6_u64)) .saturating_add(RocksDbWeight::get().reads((80_u64).saturating_mul(r.into()))) .saturating_add(RocksDbWeight::get().writes(3_u64)) - .saturating_add(Weight::from_proof_size(294100).saturating_mul(r.into())) + .saturating_add(Weight::from_proof_size(306895).saturating_mul(r.into())) } /// Storage: System Account (r:1 w:0) /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: Measured) /// Storage: Contracts ContractInfoOf (r:1601 w:1) - /// Proof: Contracts ContractInfoOf (max_values: None, max_size: Some(258), added: 2733, mode: Measured) + /// Proof: Contracts ContractInfoOf (max_values: None, max_size: Some(290), added: 2765, mode: Measured) /// Storage: Contracts CodeStorage (r:1 w:0) /// Proof: Contracts CodeStorage (max_values: None, max_size: Some(126001), added: 128476, mode: Measured) /// Storage: Timestamp Now (r:1 w:0) @@ -2556,22 +2558,22 @@ impl WeightInfo for () { /// The range of component `r` is `[0, 20]`. fn seal_code_hash(r: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `889 + r * (19539 ±0)` - // Estimated: `17170 + r * (295700 ±0)` - // Minimum execution time: 375_980 nanoseconds. - Weight::from_ref_time(232_604_331) - .saturating_add(Weight::from_proof_size(17170)) - // Standard Error: 491_912 - .saturating_add(Weight::from_ref_time(299_479_335).saturating_mul(r.into())) + // Measured: `921 + r * (22099 ±0)` + // Estimated: `17340 + r * (308500 ±0)` + // Minimum execution time: 375_058 nanoseconds. + Weight::from_ref_time(238_765_068) + .saturating_add(Weight::from_proof_size(17340)) + // Standard Error: 662_617 + .saturating_add(Weight::from_ref_time(302_175_089).saturating_mul(r.into())) .saturating_add(RocksDbWeight::get().reads(6_u64)) .saturating_add(RocksDbWeight::get().reads((80_u64).saturating_mul(r.into()))) .saturating_add(RocksDbWeight::get().writes(3_u64)) - .saturating_add(Weight::from_proof_size(295700).saturating_mul(r.into())) + .saturating_add(Weight::from_proof_size(308500).saturating_mul(r.into())) } /// Storage: System Account (r:1 w:0) /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: Measured) /// Storage: Contracts ContractInfoOf (r:1 w:1) - /// Proof: Contracts ContractInfoOf (max_values: None, max_size: Some(258), added: 2733, mode: Measured) + /// Proof: Contracts ContractInfoOf (max_values: None, max_size: Some(290), added: 2765, mode: Measured) /// Storage: Contracts CodeStorage (r:1 w:0) /// Proof: Contracts CodeStorage (max_values: None, max_size: Some(126001), added: 128476, mode: Measured) /// Storage: Timestamp Now (r:1 w:0) @@ -2581,13 +2583,13 @@ impl WeightInfo for () { /// The range of component `r` is `[0, 20]`. fn seal_own_code_hash(r: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `852 + r * (480 ±0)` - // Estimated: `17155 + r * (2400 ±0)` - // Minimum execution time: 375_075 nanoseconds. - Weight::from_ref_time(381_217_372) - .saturating_add(Weight::from_proof_size(17155)) - // Standard Error: 63_950 - .saturating_add(Weight::from_ref_time(21_872_316).saturating_mul(r.into())) + // Measured: `884 + r * (480 ±0)` + // Estimated: `17330 + r * (2400 ±0)` + // Minimum execution time: 374_747 nanoseconds. + Weight::from_ref_time(376_482_380) + .saturating_add(Weight::from_proof_size(17330)) + // Standard Error: 61_919 + .saturating_add(Weight::from_ref_time(22_376_795).saturating_mul(r.into())) .saturating_add(RocksDbWeight::get().reads(6_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) .saturating_add(Weight::from_proof_size(2400).saturating_mul(r.into())) @@ -2595,7 +2597,7 @@ impl WeightInfo for () { /// Storage: System Account (r:1 w:0) /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: Measured) /// Storage: Contracts ContractInfoOf (r:1 w:1) - /// Proof: Contracts ContractInfoOf (max_values: None, max_size: Some(258), added: 2733, mode: Measured) + /// Proof: Contracts ContractInfoOf (max_values: None, max_size: Some(290), added: 2765, mode: Measured) /// Storage: Contracts CodeStorage (r:1 w:0) /// Proof: Contracts CodeStorage (max_values: None, max_size: Some(126001), added: 128476, mode: Measured) /// Storage: Timestamp Now (r:1 w:0) @@ -2605,13 +2607,13 @@ impl WeightInfo for () { /// The range of component `r` is `[0, 20]`. fn seal_caller_is_origin(r: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `842 + r * (240 ±0)` - // Estimated: `17080 + r * (1200 ±0)` - // Minimum execution time: 373_260 nanoseconds. - Weight::from_ref_time(377_987_666) - .saturating_add(Weight::from_proof_size(17080)) - // Standard Error: 35_603 - .saturating_add(Weight::from_ref_time(11_274_165).saturating_mul(r.into())) + // Measured: `874 + r * (240 ±0)` + // Estimated: `17265 + r * (1200 ±0)` + // Minimum execution time: 372_287 nanoseconds. + Weight::from_ref_time(376_250_858) + .saturating_add(Weight::from_proof_size(17265)) + // Standard Error: 40_119 + .saturating_add(Weight::from_ref_time(11_359_647).saturating_mul(r.into())) .saturating_add(RocksDbWeight::get().reads(6_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) .saturating_add(Weight::from_proof_size(1200).saturating_mul(r.into())) @@ -2619,7 +2621,7 @@ impl WeightInfo for () { /// Storage: System Account (r:1 w:0) /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: Measured) /// Storage: Contracts ContractInfoOf (r:1 w:1) - /// Proof: Contracts ContractInfoOf (max_values: None, max_size: Some(258), added: 2733, mode: Measured) + /// Proof: Contracts ContractInfoOf (max_values: None, max_size: Some(290), added: 2765, mode: Measured) /// Storage: Contracts CodeStorage (r:1 w:0) /// Proof: Contracts CodeStorage (max_values: None, max_size: Some(126001), added: 128476, mode: Measured) /// Storage: Timestamp Now (r:1 w:0) @@ -2629,13 +2631,13 @@ impl WeightInfo for () { /// The range of component `r` is `[0, 20]`. fn seal_address(r: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `846 + r * (480 ±0)` - // Estimated: `17100 + r * (2400 ±0)` - // Minimum execution time: 374_605 nanoseconds. - Weight::from_ref_time(379_395_443) - .saturating_add(Weight::from_proof_size(17100)) - // Standard Error: 49_646 - .saturating_add(Weight::from_ref_time(17_487_585).saturating_mul(r.into())) + // Measured: `878 + r * (480 ±0)` + // Estimated: `17260 + r * (2400 ±0)` + // Minimum execution time: 374_445 nanoseconds. + Weight::from_ref_time(377_243_521) + .saturating_add(Weight::from_proof_size(17260)) + // Standard Error: 53_032 + .saturating_add(Weight::from_ref_time(17_684_246).saturating_mul(r.into())) .saturating_add(RocksDbWeight::get().reads(6_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) .saturating_add(Weight::from_proof_size(2400).saturating_mul(r.into())) @@ -2643,7 +2645,7 @@ impl WeightInfo for () { /// Storage: System Account (r:1 w:0) /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: Measured) /// Storage: Contracts ContractInfoOf (r:1 w:1) - /// Proof: Contracts ContractInfoOf (max_values: None, max_size: Some(258), added: 2733, mode: Measured) + /// Proof: Contracts ContractInfoOf (max_values: None, max_size: Some(290), added: 2765, mode: Measured) /// Storage: Contracts CodeStorage (r:1 w:0) /// Proof: Contracts CodeStorage (max_values: None, max_size: Some(126001), added: 128476, mode: Measured) /// Storage: Timestamp Now (r:1 w:0) @@ -2653,21 +2655,21 @@ impl WeightInfo for () { /// The range of component `r` is `[0, 20]`. fn seal_gas_left(r: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `847 + r * (480 ±0)` - // Estimated: `17105 + r * (2400 ±0)` - // Minimum execution time: 374_620 nanoseconds. - Weight::from_ref_time(379_623_792) - .saturating_add(Weight::from_proof_size(17105)) - // Standard Error: 49_990 - .saturating_add(Weight::from_ref_time(17_226_706).saturating_mul(r.into())) + // Measured: `879 + r * (480 ±0)` + // Estimated: `17250 + r * (2405 ±0)` + // Minimum execution time: 374_029 nanoseconds. + Weight::from_ref_time(380_415_186) + .saturating_add(Weight::from_proof_size(17250)) + // Standard Error: 60_562 + .saturating_add(Weight::from_ref_time(17_152_599).saturating_mul(r.into())) .saturating_add(RocksDbWeight::get().reads(6_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) - .saturating_add(Weight::from_proof_size(2400).saturating_mul(r.into())) + .saturating_add(Weight::from_proof_size(2405).saturating_mul(r.into())) } /// Storage: System Account (r:2 w:0) /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: Measured) /// Storage: Contracts ContractInfoOf (r:1 w:1) - /// Proof: Contracts ContractInfoOf (max_values: None, max_size: Some(258), added: 2733, mode: Measured) + /// Proof: Contracts ContractInfoOf (max_values: None, max_size: Some(290), added: 2765, mode: Measured) /// Storage: Contracts CodeStorage (r:1 w:0) /// Proof: Contracts CodeStorage (max_values: None, max_size: Some(126001), added: 128476, mode: Measured) /// Storage: Timestamp Now (r:1 w:0) @@ -2677,13 +2679,13 @@ impl WeightInfo for () { /// The range of component `r` is `[0, 20]`. fn seal_balance(r: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `1017 + r * (480 ±0)` - // Estimated: `19673 + r * (2456 ±4)` - // Minimum execution time: 374_002 nanoseconds. - Weight::from_ref_time(384_615_649) - .saturating_add(Weight::from_proof_size(19673)) - // Standard Error: 85_633 - .saturating_add(Weight::from_ref_time(95_227_118).saturating_mul(r.into())) + // Measured: `1049 + r * (480 ±0)` + // Estimated: `19849 + r * (2456 ±0)` + // Minimum execution time: 373_999 nanoseconds. + Weight::from_ref_time(381_757_033) + .saturating_add(Weight::from_proof_size(19849)) + // Standard Error: 97_983 + .saturating_add(Weight::from_ref_time(98_290_984).saturating_mul(r.into())) .saturating_add(RocksDbWeight::get().reads(7_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) .saturating_add(Weight::from_proof_size(2456).saturating_mul(r.into())) @@ -2691,7 +2693,7 @@ impl WeightInfo for () { /// Storage: System Account (r:1 w:0) /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: Measured) /// Storage: Contracts ContractInfoOf (r:1 w:1) - /// Proof: Contracts ContractInfoOf (max_values: None, max_size: Some(258), added: 2733, mode: Measured) + /// Proof: Contracts ContractInfoOf (max_values: None, max_size: Some(290), added: 2765, mode: Measured) /// Storage: Contracts CodeStorage (r:1 w:0) /// Proof: Contracts CodeStorage (max_values: None, max_size: Some(126001), added: 128476, mode: Measured) /// Storage: Timestamp Now (r:1 w:0) @@ -2701,13 +2703,13 @@ impl WeightInfo for () { /// The range of component `r` is `[0, 20]`. fn seal_value_transferred(r: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `856 + r * (480 ±0)` - // Estimated: `17200 + r * (2400 ±0)` - // Minimum execution time: 375_307 nanoseconds. - Weight::from_ref_time(378_389_705) - .saturating_add(Weight::from_proof_size(17200)) - // Standard Error: 42_265 - .saturating_add(Weight::from_ref_time(17_316_680).saturating_mul(r.into())) + // Measured: `888 + r * (480 ±0)` + // Estimated: `17360 + r * (2400 ±0)` + // Minimum execution time: 374_197 nanoseconds. + Weight::from_ref_time(377_755_896) + .saturating_add(Weight::from_proof_size(17360)) + // Standard Error: 60_542 + .saturating_add(Weight::from_ref_time(17_442_065).saturating_mul(r.into())) .saturating_add(RocksDbWeight::get().reads(6_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) .saturating_add(Weight::from_proof_size(2400).saturating_mul(r.into())) @@ -2715,7 +2717,7 @@ impl WeightInfo for () { /// Storage: System Account (r:1 w:0) /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: Measured) /// Storage: Contracts ContractInfoOf (r:1 w:1) - /// Proof: Contracts ContractInfoOf (max_values: None, max_size: Some(258), added: 2733, mode: Measured) + /// Proof: Contracts ContractInfoOf (max_values: None, max_size: Some(290), added: 2765, mode: Measured) /// Storage: Contracts CodeStorage (r:1 w:0) /// Proof: Contracts CodeStorage (max_values: None, max_size: Some(126001), added: 128476, mode: Measured) /// Storage: Timestamp Now (r:1 w:0) @@ -2725,13 +2727,13 @@ impl WeightInfo for () { /// The range of component `r` is `[0, 20]`. fn seal_minimum_balance(r: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `854 + r * (480 ±0)` - // Estimated: `17140 + r * (2400 ±0)` - // Minimum execution time: 374_878 nanoseconds. - Weight::from_ref_time(379_364_066) - .saturating_add(Weight::from_proof_size(17140)) - // Standard Error: 49_158 - .saturating_add(Weight::from_ref_time(17_111_145).saturating_mul(r.into())) + // Measured: `886 + r * (480 ±0)` + // Estimated: `17290 + r * (2400 ±0)` + // Minimum execution time: 373_888 nanoseconds. + Weight::from_ref_time(377_825_771) + .saturating_add(Weight::from_proof_size(17290)) + // Standard Error: 38_026 + .saturating_add(Weight::from_ref_time(17_147_903).saturating_mul(r.into())) .saturating_add(RocksDbWeight::get().reads(6_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) .saturating_add(Weight::from_proof_size(2400).saturating_mul(r.into())) @@ -2739,7 +2741,7 @@ impl WeightInfo for () { /// Storage: System Account (r:1 w:0) /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: Measured) /// Storage: Contracts ContractInfoOf (r:1 w:1) - /// Proof: Contracts ContractInfoOf (max_values: None, max_size: Some(258), added: 2733, mode: Measured) + /// Proof: Contracts ContractInfoOf (max_values: None, max_size: Some(290), added: 2765, mode: Measured) /// Storage: Contracts CodeStorage (r:1 w:0) /// Proof: Contracts CodeStorage (max_values: None, max_size: Some(126001), added: 128476, mode: Measured) /// Storage: Timestamp Now (r:1 w:0) @@ -2749,13 +2751,13 @@ impl WeightInfo for () { /// The range of component `r` is `[0, 20]`. fn seal_block_number(r: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `851 + r * (480 ±0)` - // Estimated: `17125 + r * (2400 ±0)` - // Minimum execution time: 374_981 nanoseconds. - Weight::from_ref_time(381_539_022) - .saturating_add(Weight::from_proof_size(17125)) - // Standard Error: 61_419 - .saturating_add(Weight::from_ref_time(17_062_381).saturating_mul(r.into())) + // Measured: `883 + r * (480 ±0)` + // Estimated: `17315 + r * (2400 ±0)` + // Minimum execution time: 373_904 nanoseconds. + Weight::from_ref_time(378_652_372) + .saturating_add(Weight::from_proof_size(17315)) + // Standard Error: 43_833 + .saturating_add(Weight::from_ref_time(16_936_781).saturating_mul(r.into())) .saturating_add(RocksDbWeight::get().reads(6_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) .saturating_add(Weight::from_proof_size(2400).saturating_mul(r.into())) @@ -2763,7 +2765,7 @@ impl WeightInfo for () { /// Storage: System Account (r:1 w:0) /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: Measured) /// Storage: Contracts ContractInfoOf (r:1 w:1) - /// Proof: Contracts ContractInfoOf (max_values: None, max_size: Some(258), added: 2733, mode: Measured) + /// Proof: Contracts ContractInfoOf (max_values: None, max_size: Some(290), added: 2765, mode: Measured) /// Storage: Contracts CodeStorage (r:1 w:0) /// Proof: Contracts CodeStorage (max_values: None, max_size: Some(126001), added: 128476, mode: Measured) /// Storage: Timestamp Now (r:1 w:0) @@ -2773,13 +2775,13 @@ impl WeightInfo for () { /// The range of component `r` is `[0, 20]`. fn seal_now(r: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `842 + r * (480 ±0)` - // Estimated: `17100 + r * (2400 ±0)` - // Minimum execution time: 374_798 nanoseconds. - Weight::from_ref_time(372_659_915) - .saturating_add(Weight::from_proof_size(17100)) - // Standard Error: 151_499 - .saturating_add(Weight::from_ref_time(18_192_683).saturating_mul(r.into())) + // Measured: `874 + r * (480 ±0)` + // Estimated: `17245 + r * (2400 ±0)` + // Minimum execution time: 373_473 nanoseconds. + Weight::from_ref_time(376_386_312) + .saturating_add(Weight::from_proof_size(17245)) + // Standard Error: 46_945 + .saturating_add(Weight::from_ref_time(17_336_462).saturating_mul(r.into())) .saturating_add(RocksDbWeight::get().reads(6_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) .saturating_add(Weight::from_proof_size(2400).saturating_mul(r.into())) @@ -2787,7 +2789,7 @@ impl WeightInfo for () { /// Storage: System Account (r:1 w:0) /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: Measured) /// Storage: Contracts ContractInfoOf (r:1 w:1) - /// Proof: Contracts ContractInfoOf (max_values: None, max_size: Some(258), added: 2733, mode: Measured) + /// Proof: Contracts ContractInfoOf (max_values: None, max_size: Some(290), added: 2765, mode: Measured) /// Storage: Contracts CodeStorage (r:1 w:0) /// Proof: Contracts CodeStorage (max_values: None, max_size: Some(126001), added: 128476, mode: Measured) /// Storage: Timestamp Now (r:1 w:0) @@ -2799,13 +2801,13 @@ impl WeightInfo for () { /// The range of component `r` is `[0, 20]`. fn seal_weight_to_fee(r: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `919 + r * (800 ±0)` - // Estimated: `18835 + r * (4805 ±0)` - // Minimum execution time: 374_841 nanoseconds. - Weight::from_ref_time(385_475_889) - .saturating_add(Weight::from_proof_size(18835)) - // Standard Error: 106_207 - .saturating_add(Weight::from_ref_time(88_099_987).saturating_mul(r.into())) + // Measured: `951 + r * (800 ±0)` + // Estimated: `19046 + r * (4805 ±0)` + // Minimum execution time: 373_661 nanoseconds. + Weight::from_ref_time(385_824_015) + .saturating_add(Weight::from_proof_size(19046)) + // Standard Error: 75_964 + .saturating_add(Weight::from_ref_time(88_530_074).saturating_mul(r.into())) .saturating_add(RocksDbWeight::get().reads(7_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) .saturating_add(Weight::from_proof_size(4805).saturating_mul(r.into())) @@ -2813,7 +2815,7 @@ impl WeightInfo for () { /// Storage: System Account (r:1 w:0) /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: Measured) /// Storage: Contracts ContractInfoOf (r:1 w:1) - /// Proof: Contracts ContractInfoOf (max_values: None, max_size: Some(258), added: 2733, mode: Measured) + /// Proof: Contracts ContractInfoOf (max_values: None, max_size: Some(290), added: 2765, mode: Measured) /// Storage: Contracts CodeStorage (r:1 w:0) /// Proof: Contracts CodeStorage (max_values: None, max_size: Some(126001), added: 128476, mode: Measured) /// Storage: Timestamp Now (r:1 w:0) @@ -2823,13 +2825,13 @@ impl WeightInfo for () { /// The range of component `r` is `[0, 20]`. fn seal_gas(r: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `809 + r * (320 ±0)` - // Estimated: `16955 + r * (1600 ±0)` - // Minimum execution time: 134_436 nanoseconds. - Weight::from_ref_time(137_789_498) - .saturating_add(Weight::from_proof_size(16955)) - // Standard Error: 10_622 - .saturating_add(Weight::from_ref_time(8_144_024).saturating_mul(r.into())) + // Measured: `841 + r * (320 ±0)` + // Estimated: `17120 + r * (1600 ±0)` + // Minimum execution time: 133_849 nanoseconds. + Weight::from_ref_time(137_283_391) + .saturating_add(Weight::from_proof_size(17120)) + // Standard Error: 13_312 + .saturating_add(Weight::from_ref_time(8_055_328).saturating_mul(r.into())) .saturating_add(RocksDbWeight::get().reads(6_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) .saturating_add(Weight::from_proof_size(1600).saturating_mul(r.into())) @@ -2837,7 +2839,7 @@ impl WeightInfo for () { /// Storage: System Account (r:1 w:0) /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: Measured) /// Storage: Contracts ContractInfoOf (r:1 w:1) - /// Proof: Contracts ContractInfoOf (max_values: None, max_size: Some(258), added: 2733, mode: Measured) + /// Proof: Contracts ContractInfoOf (max_values: None, max_size: Some(290), added: 2765, mode: Measured) /// Storage: Contracts CodeStorage (r:1 w:0) /// Proof: Contracts CodeStorage (max_values: None, max_size: Some(126001), added: 128476, mode: Measured) /// Storage: Timestamp Now (r:1 w:0) @@ -2847,13 +2849,13 @@ impl WeightInfo for () { /// The range of component `r` is `[0, 20]`. fn seal_input(r: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `844 + r * (480 ±0)` - // Estimated: `17085 + r * (2400 ±0)` - // Minimum execution time: 374_480 nanoseconds. - Weight::from_ref_time(379_723_392) - .saturating_add(Weight::from_proof_size(17085)) - // Standard Error: 50_240 - .saturating_add(Weight::from_ref_time(15_358_041).saturating_mul(r.into())) + // Measured: `876 + r * (480 ±0)` + // Estimated: `17245 + r * (2400 ±0)` + // Minimum execution time: 373_468 nanoseconds. + Weight::from_ref_time(376_121_093) + .saturating_add(Weight::from_proof_size(17245)) + // Standard Error: 61_857 + .saturating_add(Weight::from_ref_time(15_868_414).saturating_mul(r.into())) .saturating_add(RocksDbWeight::get().reads(6_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) .saturating_add(Weight::from_proof_size(2400).saturating_mul(r.into())) @@ -2861,7 +2863,7 @@ impl WeightInfo for () { /// Storage: System Account (r:1 w:0) /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: Measured) /// Storage: Contracts ContractInfoOf (r:1 w:1) - /// Proof: Contracts ContractInfoOf (max_values: None, max_size: Some(258), added: 2733, mode: Measured) + /// Proof: Contracts ContractInfoOf (max_values: None, max_size: Some(290), added: 2765, mode: Measured) /// Storage: Contracts CodeStorage (r:1 w:0) /// Proof: Contracts CodeStorage (max_values: None, max_size: Some(126001), added: 128476, mode: Measured) /// Storage: Timestamp Now (r:1 w:0) @@ -2871,20 +2873,20 @@ impl WeightInfo for () { /// The range of component `n` is `[0, 1024]`. fn seal_input_per_kb(n: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `1324` - // Estimated: `19490` - // Minimum execution time: 392_282 nanoseconds. - Weight::from_ref_time(418_943_323) - .saturating_add(Weight::from_proof_size(19490)) - // Standard Error: 4_673 - .saturating_add(Weight::from_ref_time(9_664_301).saturating_mul(n.into())) + // Measured: `1356` + // Estimated: `19650` + // Minimum execution time: 390_668 nanoseconds. + Weight::from_ref_time(419_608_449) + .saturating_add(Weight::from_proof_size(19650)) + // Standard Error: 4_890 + .saturating_add(Weight::from_ref_time(9_672_288).saturating_mul(n.into())) .saturating_add(RocksDbWeight::get().reads(6_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) } /// Storage: System Account (r:1 w:0) /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: Measured) /// Storage: Contracts ContractInfoOf (r:1 w:1) - /// Proof: Contracts ContractInfoOf (max_values: None, max_size: Some(258), added: 2733, mode: Measured) + /// Proof: Contracts ContractInfoOf (max_values: None, max_size: Some(290), added: 2765, mode: Measured) /// Storage: Contracts CodeStorage (r:1 w:0) /// Proof: Contracts CodeStorage (max_values: None, max_size: Some(126001), added: 128476, mode: Measured) /// Storage: Timestamp Now (r:1 w:0) @@ -2894,11 +2896,13 @@ impl WeightInfo for () { /// The range of component `r` is `[0, 1]`. fn seal_return(r: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `832 + r * (45 ±0)` - // Estimated: `17030 + r * (225 ±0)` - // Minimum execution time: 372_207 nanoseconds. - Weight::from_ref_time(376_232_444) - .saturating_add(Weight::from_proof_size(17030)) + // Measured: `864 + r * (45 ±0)` + // Estimated: `17190 + r * (225 ±0)` + // Minimum execution time: 371_309 nanoseconds. + Weight::from_ref_time(373_625_402) + .saturating_add(Weight::from_proof_size(17190)) + // Standard Error: 419_605 + .saturating_add(Weight::from_ref_time(1_737_397).saturating_mul(r.into())) .saturating_add(RocksDbWeight::get().reads(6_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) .saturating_add(Weight::from_proof_size(225).saturating_mul(r.into())) @@ -2906,7 +2910,7 @@ impl WeightInfo for () { /// Storage: System Account (r:1 w:0) /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: Measured) /// Storage: Contracts ContractInfoOf (r:1 w:1) - /// Proof: Contracts ContractInfoOf (max_values: None, max_size: Some(258), added: 2733, mode: Measured) + /// Proof: Contracts ContractInfoOf (max_values: None, max_size: Some(290), added: 2765, mode: Measured) /// Storage: Contracts CodeStorage (r:1 w:0) /// Proof: Contracts CodeStorage (max_values: None, max_size: Some(126001), added: 128476, mode: Measured) /// Storage: Timestamp Now (r:1 w:0) @@ -2916,20 +2920,20 @@ impl WeightInfo for () { /// The range of component `n` is `[0, 1024]`. fn seal_return_per_kb(n: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `842` - // Estimated: `17125` - // Minimum execution time: 374_743 nanoseconds. - Weight::from_ref_time(377_365_053) - .saturating_add(Weight::from_proof_size(17125)) - // Standard Error: 1_073 - .saturating_add(Weight::from_ref_time(231_251).saturating_mul(n.into())) + // Measured: `874` + // Estimated: `17285` + // Minimum execution time: 374_094 nanoseconds. + Weight::from_ref_time(375_965_200) + .saturating_add(Weight::from_proof_size(17285)) + // Standard Error: 1_127 + .saturating_add(Weight::from_ref_time(232_645).saturating_mul(n.into())) .saturating_add(RocksDbWeight::get().reads(6_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) } - /// Storage: System Account (r:3 w:3) + /// Storage: System Account (r:4 w:4) /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: Measured) /// Storage: Contracts ContractInfoOf (r:1 w:1) - /// Proof: Contracts ContractInfoOf (max_values: None, max_size: Some(258), added: 2733, mode: Measured) + /// Proof: Contracts ContractInfoOf (max_values: None, max_size: Some(290), added: 2765, mode: Measured) /// Storage: Contracts CodeStorage (r:1 w:0) /// Proof: Contracts CodeStorage (max_values: None, max_size: Some(126001), added: 128476, mode: Measured) /// Storage: Timestamp Now (r:1 w:0) @@ -2943,23 +2947,23 @@ impl WeightInfo for () { /// The range of component `r` is `[0, 1]`. fn seal_terminate(r: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `874 + r * (280 ±0)` - // Estimated: `19880 + r * (11465 ±0)` - // Minimum execution time: 374_873 nanoseconds. - Weight::from_ref_time(378_422_289) - .saturating_add(Weight::from_proof_size(19880)) - // Standard Error: 979_734 - .saturating_add(Weight::from_ref_time(57_035_310).saturating_mul(r.into())) + // Measured: `906 + r * (452 ±0)` + // Estimated: `20242 + r * (15004 ±0)` + // Minimum execution time: 373_123 nanoseconds. + Weight::from_ref_time(374_924_634) + .saturating_add(Weight::from_proof_size(20242)) + // Standard Error: 378_010 + .saturating_add(Weight::from_ref_time(70_441_665).saturating_mul(r.into())) .saturating_add(RocksDbWeight::get().reads(6_u64)) - .saturating_add(RocksDbWeight::get().reads((5_u64).saturating_mul(r.into()))) + .saturating_add(RocksDbWeight::get().reads((6_u64).saturating_mul(r.into()))) .saturating_add(RocksDbWeight::get().writes(3_u64)) - .saturating_add(RocksDbWeight::get().writes((6_u64).saturating_mul(r.into()))) - .saturating_add(Weight::from_proof_size(11465).saturating_mul(r.into())) + .saturating_add(RocksDbWeight::get().writes((7_u64).saturating_mul(r.into()))) + .saturating_add(Weight::from_proof_size(15004).saturating_mul(r.into())) } /// Storage: System Account (r:1 w:0) /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: Measured) /// Storage: Contracts ContractInfoOf (r:1 w:1) - /// Proof: Contracts ContractInfoOf (max_values: None, max_size: Some(258), added: 2733, mode: Measured) + /// Proof: Contracts ContractInfoOf (max_values: None, max_size: Some(290), added: 2765, mode: Measured) /// Storage: Contracts CodeStorage (r:1 w:0) /// Proof: Contracts CodeStorage (max_values: None, max_size: Some(126001), added: 128476, mode: Measured) /// Storage: Timestamp Now (r:1 w:0) @@ -2971,13 +2975,13 @@ impl WeightInfo for () { /// The range of component `r` is `[0, 20]`. fn seal_random(r: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `889 + r * (800 ±0)` - // Estimated: `18643 + r * (4805 ±0)` - // Minimum execution time: 375_001 nanoseconds. - Weight::from_ref_time(382_558_599) - .saturating_add(Weight::from_proof_size(18643)) - // Standard Error: 94_918 - .saturating_add(Weight::from_ref_time(112_973_277).saturating_mul(r.into())) + // Measured: `921 + r * (800 ±0)` + // Estimated: `18835 + r * (4805 ±0)` + // Minimum execution time: 373_291 nanoseconds. + Weight::from_ref_time(385_684_344) + .saturating_add(Weight::from_proof_size(18835)) + // Standard Error: 99_025 + .saturating_add(Weight::from_ref_time(111_308_793).saturating_mul(r.into())) .saturating_add(RocksDbWeight::get().reads(7_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) .saturating_add(Weight::from_proof_size(4805).saturating_mul(r.into())) @@ -2985,7 +2989,7 @@ impl WeightInfo for () { /// Storage: System Account (r:1 w:0) /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: Measured) /// Storage: Contracts ContractInfoOf (r:1 w:1) - /// Proof: Contracts ContractInfoOf (max_values: None, max_size: Some(258), added: 2733, mode: Measured) + /// Proof: Contracts ContractInfoOf (max_values: None, max_size: Some(290), added: 2765, mode: Measured) /// Storage: Contracts CodeStorage (r:1 w:0) /// Proof: Contracts CodeStorage (max_values: None, max_size: Some(126001), added: 128476, mode: Measured) /// Storage: Timestamp Now (r:1 w:0) @@ -2995,13 +2999,13 @@ impl WeightInfo for () { /// The range of component `r` is `[0, 20]`. fn seal_deposit_event(r: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `842 + r * (800 ±0)` - // Estimated: `17075 + r * (4000 ±0)` - // Minimum execution time: 372_547 nanoseconds. - Weight::from_ref_time(383_278_916) - .saturating_add(Weight::from_proof_size(17075)) - // Standard Error: 128_339 - .saturating_add(Weight::from_ref_time(229_356_088).saturating_mul(r.into())) + // Measured: `874 + r * (800 ±0)` + // Estimated: `17250 + r * (4000 ±0)` + // Minimum execution time: 371_900 nanoseconds. + Weight::from_ref_time(384_166_626) + .saturating_add(Weight::from_proof_size(17250)) + // Standard Error: 205_255 + .saturating_add(Weight::from_ref_time(229_214_157).saturating_mul(r.into())) .saturating_add(RocksDbWeight::get().reads(6_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) .saturating_add(Weight::from_proof_size(4000).saturating_mul(r.into())) @@ -3009,7 +3013,7 @@ impl WeightInfo for () { /// Storage: System Account (r:1 w:0) /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: Measured) /// Storage: Contracts ContractInfoOf (r:1 w:1) - /// Proof: Contracts ContractInfoOf (max_values: None, max_size: Some(258), added: 2733, mode: Measured) + /// Proof: Contracts ContractInfoOf (max_values: None, max_size: Some(290), added: 2765, mode: Measured) /// Storage: Contracts CodeStorage (r:1 w:0) /// Proof: Contracts CodeStorage (max_values: None, max_size: Some(126001), added: 128476, mode: Measured) /// Storage: Timestamp Now (r:1 w:0) @@ -3020,15 +3024,15 @@ impl WeightInfo for () { /// The range of component `n` is `[0, 16]`. fn seal_deposit_event_per_topic_and_kb(t: u32, n: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `1778 + t * (2608 ±0) + n * (8 ±0)` - // Estimated: `21675 + t * (211030 ±0) + n * (50 ±0)` - // Minimum execution time: 1_290_432 nanoseconds. - Weight::from_ref_time(595_859_216) - .saturating_add(Weight::from_proof_size(21675)) - // Standard Error: 602_943 - .saturating_add(Weight::from_ref_time(178_128_149).saturating_mul(t.into())) - // Standard Error: 165_597 - .saturating_add(Weight::from_ref_time(71_475_468).saturating_mul(n.into())) + // Measured: `1821 + t * (2608 ±0) + n * (7 ±0)` + // Estimated: `21870 + t * (211030 ±0) + n * (50 ±0)` + // Minimum execution time: 1_289_873 nanoseconds. + Weight::from_ref_time(581_702_206) + .saturating_add(Weight::from_proof_size(21870)) + // Standard Error: 665_638 + .saturating_add(Weight::from_ref_time(181_470_553).saturating_mul(t.into())) + // Standard Error: 182_816 + .saturating_add(Weight::from_ref_time(71_635_250).saturating_mul(n.into())) .saturating_add(RocksDbWeight::get().reads(6_u64)) .saturating_add(RocksDbWeight::get().reads((80_u64).saturating_mul(t.into()))) .saturating_add(RocksDbWeight::get().writes(3_u64)) @@ -3039,7 +3043,7 @@ impl WeightInfo for () { /// Storage: System Account (r:1 w:0) /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: Measured) /// Storage: Contracts ContractInfoOf (r:1 w:1) - /// Proof: Contracts ContractInfoOf (max_values: None, max_size: Some(258), added: 2733, mode: Measured) + /// Proof: Contracts ContractInfoOf (max_values: None, max_size: Some(290), added: 2765, mode: Measured) /// Storage: Contracts CodeStorage (r:1 w:0) /// Proof: Contracts CodeStorage (max_values: None, max_size: Some(126001), added: 128476, mode: Measured) /// Storage: Timestamp Now (r:1 w:0) @@ -3049,13 +3053,13 @@ impl WeightInfo for () { /// The range of component `r` is `[0, 20]`. fn seal_debug_message(r: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `841 + r * (560 ±0)` - // Estimated: `17065 + r * (2800 ±0)` - // Minimum execution time: 149_020 nanoseconds. - Weight::from_ref_time(152_893_012) - .saturating_add(Weight::from_proof_size(17065)) - // Standard Error: 31_804 - .saturating_add(Weight::from_ref_time(14_497_512).saturating_mul(r.into())) + // Measured: `873 + r * (560 ±0)` + // Estimated: `17240 + r * (2800 ±0)` + // Minimum execution time: 148_635 nanoseconds. + Weight::from_ref_time(154_095_712) + .saturating_add(Weight::from_proof_size(17240)) + // Standard Error: 77_790 + .saturating_add(Weight::from_ref_time(14_837_085).saturating_mul(r.into())) .saturating_add(RocksDbWeight::get().reads(6_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) .saturating_add(Weight::from_proof_size(2800).saturating_mul(r.into())) @@ -3063,7 +3067,7 @@ impl WeightInfo for () { /// Storage: System Account (r:1 w:0) /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) /// Storage: Contracts ContractInfoOf (r:1 w:1) - /// Proof: Contracts ContractInfoOf (max_values: None, max_size: Some(258), added: 2733, mode: MaxEncodedLen) + /// Proof: Contracts ContractInfoOf (max_values: None, max_size: Some(290), added: 2765, mode: MaxEncodedLen) /// Storage: Contracts CodeStorage (r:1 w:0) /// Proof: Contracts CodeStorage (max_values: None, max_size: Some(126001), added: 128476, mode: MaxEncodedLen) /// Storage: Timestamp Now (r:1 w:0) @@ -3073,13 +3077,13 @@ impl WeightInfo for () { /// The range of component `i` is `[0, 1024]`. fn seal_debug_message_per_kb(i: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `125792` - // Estimated: `265059` - // Minimum execution time: 502_589 nanoseconds. - Weight::from_ref_time(506_695_307) - .saturating_add(Weight::from_proof_size(265059)) - // Standard Error: 2_148 - .saturating_add(Weight::from_ref_time(814_647).saturating_mul(i.into())) + // Measured: `125824` + // Estimated: `265128` + // Minimum execution time: 501_014 nanoseconds. + Weight::from_ref_time(505_634_218) + .saturating_add(Weight::from_proof_size(265128)) + // Standard Error: 2_441 + .saturating_add(Weight::from_ref_time(819_257).saturating_mul(i.into())) .saturating_add(RocksDbWeight::get().reads(6_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) } @@ -3088,85 +3092,85 @@ impl WeightInfo for () { /// The range of component `r` is `[0, 10]`. fn seal_set_storage(r: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `883 + r * (23417 ±0)` - // Estimated: `883 + r * (23417 ±0)` - // Minimum execution time: 375_473 nanoseconds. - Weight::from_ref_time(291_125_810) - .saturating_add(Weight::from_proof_size(883)) - // Standard Error: 824_971 - .saturating_add(Weight::from_ref_time(477_999_695).saturating_mul(r.into())) + // Measured: `911 + r * (23420 ±0)` + // Estimated: `911 + r * (23418 ±0)` + // Minimum execution time: 375_301 nanoseconds. + Weight::from_ref_time(291_498_841) + .saturating_add(Weight::from_proof_size(911)) + // Standard Error: 809_989 + .saturating_add(Weight::from_ref_time(464_550_291).saturating_mul(r.into())) .saturating_add(RocksDbWeight::get().reads(6_u64)) .saturating_add(RocksDbWeight::get().reads((80_u64).saturating_mul(r.into()))) .saturating_add(RocksDbWeight::get().writes(3_u64)) .saturating_add(RocksDbWeight::get().writes((80_u64).saturating_mul(r.into()))) - .saturating_add(Weight::from_proof_size(23417).saturating_mul(r.into())) + .saturating_add(Weight::from_proof_size(23418).saturating_mul(r.into())) } /// Storage: Skipped Metadata (r:0 w:0) /// Proof Skipped: Skipped Metadata (max_values: None, max_size: None, mode: Measured) /// The range of component `n` is `[0, 8]`. fn seal_set_storage_per_new_kb(n: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `12583 + n * (11969 ±0)` - // Estimated: `8500 + n * (12813 ±61)` - // Minimum execution time: 515_962 nanoseconds. - Weight::from_ref_time(697_904_030) - .saturating_add(Weight::from_proof_size(8500)) - // Standard Error: 1_684_000 - .saturating_add(Weight::from_ref_time(98_411_710).saturating_mul(n.into())) + // Measured: `12672 + n * (11945 ±0)` + // Estimated: `8529 + n * (12814 ±61)` + // Minimum execution time: 506_318 nanoseconds. + Weight::from_ref_time(676_935_313) + .saturating_add(Weight::from_proof_size(8529)) + // Standard Error: 1_589_291 + .saturating_add(Weight::from_ref_time(97_839_399).saturating_mul(n.into())) .saturating_add(RocksDbWeight::get().reads(52_u64)) .saturating_add(RocksDbWeight::get().reads((7_u64).saturating_mul(n.into()))) .saturating_add(RocksDbWeight::get().writes(50_u64)) .saturating_add(RocksDbWeight::get().writes((7_u64).saturating_mul(n.into()))) - .saturating_add(Weight::from_proof_size(12813).saturating_mul(n.into())) + .saturating_add(Weight::from_proof_size(12814).saturating_mul(n.into())) } /// Storage: Skipped Metadata (r:0 w:0) /// Proof Skipped: Skipped Metadata (max_values: None, max_size: None, mode: Measured) /// The range of component `n` is `[0, 8]`. fn seal_set_storage_per_old_kb(n: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `15138 + n * (175775 ±0)` - // Estimated: `9898 + n * (176855 ±74)` - // Minimum execution time: 515_828 nanoseconds. - Weight::from_ref_time(661_240_495) - .saturating_add(Weight::from_proof_size(9898)) - // Standard Error: 1_338_661 - .saturating_add(Weight::from_ref_time(65_767_819).saturating_mul(n.into())) + // Measured: `15170 + n * (175775 ±0)` + // Estimated: `9914 + n * (176858 ±74)` + // Minimum execution time: 506_148 nanoseconds. + Weight::from_ref_time(648_278_778) + .saturating_add(Weight::from_proof_size(9914)) + // Standard Error: 1_343_586 + .saturating_add(Weight::from_ref_time(65_789_595).saturating_mul(n.into())) .saturating_add(RocksDbWeight::get().reads(51_u64)) .saturating_add(RocksDbWeight::get().reads((7_u64).saturating_mul(n.into()))) .saturating_add(RocksDbWeight::get().writes(49_u64)) .saturating_add(RocksDbWeight::get().writes((7_u64).saturating_mul(n.into()))) - .saturating_add(Weight::from_proof_size(176855).saturating_mul(n.into())) + .saturating_add(Weight::from_proof_size(176858).saturating_mul(n.into())) } /// Storage: Skipped Metadata (r:0 w:0) /// Proof Skipped: Skipped Metadata (max_values: None, max_size: None, mode: Measured) /// The range of component `r` is `[0, 10]`. fn seal_clear_storage(r: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `876 + r * (23098 ±0)` - // Estimated: `881 + r * (23097 ±0)` - // Minimum execution time: 375_528 nanoseconds. - Weight::from_ref_time(296_453_612) - .saturating_add(Weight::from_proof_size(881)) - // Standard Error: 809_232 - .saturating_add(Weight::from_ref_time(465_365_815).saturating_mul(r.into())) + // Measured: `903 + r * (23099 ±0)` + // Estimated: `908 + r * (23099 ±0)` + // Minimum execution time: 374_344 nanoseconds. + Weight::from_ref_time(293_272_061) + .saturating_add(Weight::from_proof_size(908)) + // Standard Error: 810_412 + .saturating_add(Weight::from_ref_time(453_315_956).saturating_mul(r.into())) .saturating_add(RocksDbWeight::get().reads(6_u64)) .saturating_add(RocksDbWeight::get().reads((80_u64).saturating_mul(r.into()))) .saturating_add(RocksDbWeight::get().writes(3_u64)) .saturating_add(RocksDbWeight::get().writes((80_u64).saturating_mul(r.into()))) - .saturating_add(Weight::from_proof_size(23097).saturating_mul(r.into())) + .saturating_add(Weight::from_proof_size(23099).saturating_mul(r.into())) } /// Storage: Skipped Metadata (r:0 w:0) /// Proof Skipped: Skipped Metadata (max_values: None, max_size: None, mode: Measured) /// The range of component `n` is `[0, 8]`. fn seal_clear_storage_per_kb(n: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `14863 + n * (175768 ±0)` - // Estimated: `9519 + n * (176867 ±75)` - // Minimum execution time: 480_427 nanoseconds. - Weight::from_ref_time(640_337_570) - .saturating_add(Weight::from_proof_size(9519)) - // Standard Error: 1_497_141 - .saturating_add(Weight::from_ref_time(67_963_696).saturating_mul(n.into())) + // Measured: `14895 + n * (175768 ±0)` + // Estimated: `9551 + n * (176867 ±75)` + // Minimum execution time: 478_564 nanoseconds. + Weight::from_ref_time(630_839_142) + .saturating_add(Weight::from_proof_size(9551)) + // Standard Error: 1_427_520 + .saturating_add(Weight::from_ref_time(66_813_592).saturating_mul(n.into())) .saturating_add(RocksDbWeight::get().reads(51_u64)) .saturating_add(RocksDbWeight::get().reads((7_u64).saturating_mul(n.into()))) .saturating_add(RocksDbWeight::get().writes(48_u64)) @@ -3178,30 +3182,30 @@ impl WeightInfo for () { /// The range of component `r` is `[0, 10]`. fn seal_get_storage(r: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `878 + r * (23740 ±0)` - // Estimated: `880 + r * (23739 ±0)` - // Minimum execution time: 375_725 nanoseconds. - Weight::from_ref_time(307_839_394) - .saturating_add(Weight::from_proof_size(880)) - // Standard Error: 710_694 - .saturating_add(Weight::from_ref_time(381_738_407).saturating_mul(r.into())) + // Measured: `896 + r * (23744 ±0)` + // Estimated: `909 + r * (23740 ±0)` + // Minimum execution time: 374_479 nanoseconds. + Weight::from_ref_time(311_839_315) + .saturating_add(Weight::from_proof_size(909)) + // Standard Error: 666_553 + .saturating_add(Weight::from_ref_time(371_213_042).saturating_mul(r.into())) .saturating_add(RocksDbWeight::get().reads(6_u64)) .saturating_add(RocksDbWeight::get().reads((80_u64).saturating_mul(r.into()))) .saturating_add(RocksDbWeight::get().writes(3_u64)) - .saturating_add(Weight::from_proof_size(23739).saturating_mul(r.into())) + .saturating_add(Weight::from_proof_size(23740).saturating_mul(r.into())) } /// Storage: Skipped Metadata (r:0 w:0) /// Proof Skipped: Skipped Metadata (max_values: None, max_size: None, mode: Measured) /// The range of component `n` is `[0, 8]`. fn seal_get_storage_per_kb(n: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `15469 + n * (175775 ±0)` - // Estimated: `10010 + n * (176900 ±76)` - // Minimum execution time: 461_871 nanoseconds. - Weight::from_ref_time(605_755_493) - .saturating_add(Weight::from_proof_size(10010)) - // Standard Error: 1_375_044 - .saturating_add(Weight::from_ref_time(161_332_330).saturating_mul(n.into())) + // Measured: `15501 + n * (175775 ±0)` + // Estimated: `10042 + n * (176900 ±76)` + // Minimum execution time: 460_639 nanoseconds. + Weight::from_ref_time(591_187_094) + .saturating_add(Weight::from_proof_size(10042)) + // Standard Error: 1_233_792 + .saturating_add(Weight::from_ref_time(160_874_477).saturating_mul(n.into())) .saturating_add(RocksDbWeight::get().reads(51_u64)) .saturating_add(RocksDbWeight::get().reads((7_u64).saturating_mul(n.into()))) .saturating_add(RocksDbWeight::get().writes(3_u64)) @@ -3212,47 +3216,47 @@ impl WeightInfo for () { /// The range of component `r` is `[0, 10]`. fn seal_contains_storage(r: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `871 + r * (23100 ±0)` - // Estimated: `873 + r * (23099 ±0)` - // Minimum execution time: 375_325 nanoseconds. - Weight::from_ref_time(305_508_307) - .saturating_add(Weight::from_proof_size(873)) - // Standard Error: 715_627 - .saturating_add(Weight::from_ref_time(369_985_438).saturating_mul(r.into())) + // Measured: `914 + r * (23098 ±0)` + // Estimated: `920 + r * (23098 ±0)` + // Minimum execution time: 374_272 nanoseconds. + Weight::from_ref_time(311_446_269) + .saturating_add(Weight::from_proof_size(920)) + // Standard Error: 630_307 + .saturating_add(Weight::from_ref_time(357_134_931).saturating_mul(r.into())) .saturating_add(RocksDbWeight::get().reads(6_u64)) .saturating_add(RocksDbWeight::get().reads((80_u64).saturating_mul(r.into()))) .saturating_add(RocksDbWeight::get().writes(3_u64)) - .saturating_add(Weight::from_proof_size(23099).saturating_mul(r.into())) + .saturating_add(Weight::from_proof_size(23098).saturating_mul(r.into())) } /// Storage: Skipped Metadata (r:0 w:0) /// Proof Skipped: Skipped Metadata (max_values: None, max_size: None, mode: Measured) /// The range of component `n` is `[0, 8]`. fn seal_contains_storage_per_kb(n: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `14814 + n * (175782 ±0)` - // Estimated: `9502 + n * (176872 ±75)` - // Minimum execution time: 457_128 nanoseconds. - Weight::from_ref_time(582_799_799) - .saturating_add(Weight::from_proof_size(9502)) - // Standard Error: 1_151_126 - .saturating_add(Weight::from_ref_time(63_425_277).saturating_mul(n.into())) + // Measured: `14839 + n * (175789 ±0)` + // Estimated: `9532 + n * (176874 ±75)` + // Minimum execution time: 456_013 nanoseconds. + Weight::from_ref_time(575_116_352) + .saturating_add(Weight::from_proof_size(9532)) + // Standard Error: 1_122_298 + .saturating_add(Weight::from_ref_time(61_786_107).saturating_mul(n.into())) .saturating_add(RocksDbWeight::get().reads(51_u64)) .saturating_add(RocksDbWeight::get().reads((7_u64).saturating_mul(n.into()))) .saturating_add(RocksDbWeight::get().writes(3_u64)) - .saturating_add(Weight::from_proof_size(176872).saturating_mul(n.into())) + .saturating_add(Weight::from_proof_size(176874).saturating_mul(n.into())) } /// Storage: Skipped Metadata (r:0 w:0) /// Proof Skipped: Skipped Metadata (max_values: None, max_size: None, mode: Measured) /// The range of component `r` is `[0, 10]`. fn seal_take_storage(r: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `879 + r * (23740 ±0)` - // Estimated: `881 + r * (23739 ±0)` - // Minimum execution time: 375_918 nanoseconds. - Weight::from_ref_time(293_217_646) - .saturating_add(Weight::from_proof_size(881)) - // Standard Error: 840_266 - .saturating_add(Weight::from_ref_time(478_374_701).saturating_mul(r.into())) + // Measured: `911 + r * (23740 ±0)` + // Estimated: `913 + r * (23739 ±0)` + // Minimum execution time: 374_621 nanoseconds. + Weight::from_ref_time(299_689_489) + .saturating_add(Weight::from_proof_size(913)) + // Standard Error: 757_735 + .saturating_add(Weight::from_ref_time(465_213_246).saturating_mul(r.into())) .saturating_add(RocksDbWeight::get().reads(6_u64)) .saturating_add(RocksDbWeight::get().reads((80_u64).saturating_mul(r.into()))) .saturating_add(RocksDbWeight::get().writes(3_u64)) @@ -3264,13 +3268,13 @@ impl WeightInfo for () { /// The range of component `n` is `[0, 8]`. fn seal_take_storage_per_kb(n: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `15470 + n * (175775 ±0)` - // Estimated: `10010 + n * (176898 ±76)` - // Minimum execution time: 484_207 nanoseconds. - Weight::from_ref_time(664_065_436) - .saturating_add(Weight::from_proof_size(10010)) - // Standard Error: 1_655_442 - .saturating_add(Weight::from_ref_time(166_258_757).saturating_mul(n.into())) + // Measured: `15502 + n * (175775 ±0)` + // Estimated: `10042 + n * (176898 ±76)` + // Minimum execution time: 481_980 nanoseconds. + Weight::from_ref_time(647_289_053) + .saturating_add(Weight::from_proof_size(10042)) + // Standard Error: 1_556_155 + .saturating_add(Weight::from_ref_time(166_592_657).saturating_mul(n.into())) .saturating_add(RocksDbWeight::get().reads(51_u64)) .saturating_add(RocksDbWeight::get().reads((7_u64).saturating_mul(n.into()))) .saturating_add(RocksDbWeight::get().writes(48_u64)) @@ -3280,7 +3284,7 @@ impl WeightInfo for () { /// Storage: System Account (r:1602 w:1601) /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: Measured) /// Storage: Contracts ContractInfoOf (r:1 w:1) - /// Proof: Contracts ContractInfoOf (max_values: None, max_size: Some(258), added: 2733, mode: Measured) + /// Proof: Contracts ContractInfoOf (max_values: None, max_size: Some(290), added: 2765, mode: Measured) /// Storage: Contracts CodeStorage (r:1 w:0) /// Proof: Contracts CodeStorage (max_values: None, max_size: Some(126001), added: 128476, mode: Measured) /// Storage: Timestamp Now (r:1 w:0) @@ -3290,23 +3294,23 @@ impl WeightInfo for () { /// The range of component `r` is `[0, 20]`. fn seal_transfer(r: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `1393 + r * (3602 ±0)` - // Estimated: `21258 + r * (216091 ±0)` - // Minimum execution time: 377_152 nanoseconds. - Weight::from_ref_time(317_470_910) - .saturating_add(Weight::from_proof_size(21258)) - // Standard Error: 994_076 - .saturating_add(Weight::from_ref_time(1_409_416_087).saturating_mul(r.into())) + // Measured: `1457 + r * (3604 ±0)` + // Estimated: `21583 + r * (216101 ±0)` + // Minimum execution time: 374_962 nanoseconds. + Weight::from_ref_time(313_416_386) + .saturating_add(Weight::from_proof_size(21583)) + // Standard Error: 710_675 + .saturating_add(Weight::from_ref_time(1_396_551_156).saturating_mul(r.into())) .saturating_add(RocksDbWeight::get().reads(7_u64)) .saturating_add(RocksDbWeight::get().reads((80_u64).saturating_mul(r.into()))) .saturating_add(RocksDbWeight::get().writes(4_u64)) .saturating_add(RocksDbWeight::get().writes((80_u64).saturating_mul(r.into()))) - .saturating_add(Weight::from_proof_size(216091).saturating_mul(r.into())) + .saturating_add(Weight::from_proof_size(216101).saturating_mul(r.into())) } /// Storage: System Account (r:1 w:0) /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: Measured) /// Storage: Contracts ContractInfoOf (r:1601 w:1601) - /// Proof: Contracts ContractInfoOf (max_values: None, max_size: Some(258), added: 2733, mode: Measured) + /// Proof: Contracts ContractInfoOf (max_values: None, max_size: Some(290), added: 2765, mode: Measured) /// Storage: Contracts CodeStorage (r:2 w:0) /// Proof: Contracts CodeStorage (max_values: None, max_size: Some(126001), added: 128476, mode: Measured) /// Storage: Timestamp Now (r:1 w:0) @@ -3316,23 +3320,23 @@ impl WeightInfo for () { /// The range of component `r` is `[0, 20]`. fn seal_call(r: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `1551 + r * (20511 ±0)` - // Estimated: `21848 + r * (498651 ±1)` - // Minimum execution time: 376_257 nanoseconds. - Weight::from_ref_time(377_035_000) - .saturating_add(Weight::from_proof_size(21848)) - // Standard Error: 7_966_778 - .saturating_add(Weight::from_ref_time(28_873_495_129).saturating_mul(r.into())) + // Measured: `1609 + r * (23073 ±0)` + // Estimated: `22098 + r * (511456 ±1)` + // Minimum execution time: 375_916 nanoseconds. + Weight::from_ref_time(376_468_000) + .saturating_add(Weight::from_proof_size(22098)) + // Standard Error: 7_246_855 + .saturating_add(Weight::from_ref_time(28_982_425_139).saturating_mul(r.into())) .saturating_add(RocksDbWeight::get().reads(7_u64)) .saturating_add(RocksDbWeight::get().reads((160_u64).saturating_mul(r.into()))) .saturating_add(RocksDbWeight::get().writes(3_u64)) .saturating_add(RocksDbWeight::get().writes((160_u64).saturating_mul(r.into()))) - .saturating_add(Weight::from_proof_size(498651).saturating_mul(r.into())) + .saturating_add(Weight::from_proof_size(511456).saturating_mul(r.into())) } /// Storage: System Account (r:1 w:0) /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: Measured) /// Storage: Contracts ContractInfoOf (r:1 w:1) - /// Proof: Contracts ContractInfoOf (max_values: None, max_size: Some(258), added: 2733, mode: Measured) + /// Proof: Contracts ContractInfoOf (max_values: None, max_size: Some(290), added: 2765, mode: Measured) /// Storage: Contracts CodeStorage (r:1536 w:0) /// Proof: Contracts CodeStorage (max_values: None, max_size: Some(126001), added: 128476, mode: Measured) /// Storage: Timestamp Now (r:1 w:0) @@ -3343,22 +3347,22 @@ impl WeightInfo for () { fn seal_delegate_call(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0 + r * (71670 ±0)` - // Estimated: `17125 + r * (659927 ±822)` - // Minimum execution time: 376_544 nanoseconds. - Weight::from_ref_time(377_490_000) - .saturating_add(Weight::from_proof_size(17125)) - // Standard Error: 8_608_050 - .saturating_add(Weight::from_ref_time(28_568_714_410).saturating_mul(r.into())) + // Estimated: `17285 + r * (659930 ±563)` + // Minimum execution time: 375_412 nanoseconds. + Weight::from_ref_time(376_493_000) + .saturating_add(Weight::from_proof_size(17285)) + // Standard Error: 8_239_575 + .saturating_add(Weight::from_ref_time(28_716_347_183).saturating_mul(r.into())) .saturating_add(RocksDbWeight::get().reads(6_u64)) .saturating_add(RocksDbWeight::get().reads((150_u64).saturating_mul(r.into()))) .saturating_add(RocksDbWeight::get().writes(3_u64)) .saturating_add(RocksDbWeight::get().writes((75_u64).saturating_mul(r.into()))) - .saturating_add(Weight::from_proof_size(659927).saturating_mul(r.into())) + .saturating_add(Weight::from_proof_size(659930).saturating_mul(r.into())) } /// Storage: System Account (r:82 w:81) /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: Measured) /// Storage: Contracts ContractInfoOf (r:81 w:81) - /// Proof: Contracts ContractInfoOf (max_values: None, max_size: Some(258), added: 2733, mode: Measured) + /// Proof: Contracts ContractInfoOf (max_values: None, max_size: Some(290), added: 2765, mode: Measured) /// Storage: Contracts CodeStorage (r:2 w:0) /// Proof: Contracts CodeStorage (max_values: None, max_size: Some(126001), added: 128476, mode: Measured) /// Storage: Timestamp Now (r:1 w:0) @@ -3369,25 +3373,25 @@ impl WeightInfo for () { /// The range of component `c` is `[0, 1024]`. fn seal_call_per_transfer_clone_kb(t: u32, c: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `21611 + t * (15369 ±0)` - // Estimated: `519400 + t * (277320 ±0)` - // Minimum execution time: 10_322_052 nanoseconds. - Weight::from_ref_time(9_248_652_894) - .saturating_add(Weight::from_proof_size(519400)) - // Standard Error: 9_039_638 - .saturating_add(Weight::from_ref_time(1_393_054_441).saturating_mul(t.into())) - // Standard Error: 13_554 - .saturating_add(Weight::from_ref_time(9_819_606).saturating_mul(c.into())) + // Measured: `24269 + t * (16910 ±0)` + // Estimated: `532690 + t * (285025 ±0)` + // Minimum execution time: 10_443_315 nanoseconds. + Weight::from_ref_time(9_342_574_069) + .saturating_add(Weight::from_proof_size(532690)) + // Standard Error: 7_237_279 + .saturating_add(Weight::from_ref_time(1_390_221_936).saturating_mul(t.into())) + // Standard Error: 10_851 + .saturating_add(Weight::from_ref_time(9_842_151).saturating_mul(c.into())) .saturating_add(RocksDbWeight::get().reads(167_u64)) .saturating_add(RocksDbWeight::get().reads((81_u64).saturating_mul(t.into()))) .saturating_add(RocksDbWeight::get().writes(163_u64)) .saturating_add(RocksDbWeight::get().writes((81_u64).saturating_mul(t.into()))) - .saturating_add(Weight::from_proof_size(277320).saturating_mul(t.into())) + .saturating_add(Weight::from_proof_size(285025).saturating_mul(t.into())) } - /// Storage: System Account (r:1602 w:1602) + /// Storage: System Account (r:3202 w:3202) /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: Measured) /// Storage: Contracts ContractInfoOf (r:1601 w:1601) - /// Proof: Contracts ContractInfoOf (max_values: None, max_size: Some(258), added: 2733, mode: Measured) + /// Proof: Contracts ContractInfoOf (max_values: None, max_size: Some(290), added: 2765, mode: Measured) /// Storage: Contracts CodeStorage (r:1601 w:0) /// Proof: Contracts CodeStorage (max_values: None, max_size: Some(126001), added: 128476, mode: Measured) /// Storage: Timestamp Now (r:1 w:0) @@ -3401,23 +3405,23 @@ impl WeightInfo for () { /// The range of component `r` is `[0, 20]`. fn seal_instantiate(r: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `1613 + r * (25576 ±0)` - // Estimated: `25698 + r * (1169112 ±1)` - // Minimum execution time: 376_639 nanoseconds. - Weight::from_ref_time(377_892_000) - .saturating_add(Weight::from_proof_size(25698)) - // Standard Error: 21_259_255 - .saturating_add(Weight::from_ref_time(34_131_174_956).saturating_mul(r.into())) + // Measured: `1775 + r * (25568 ±0)` + // Estimated: `26563 + r * (1367114 ±2)` + // Minimum execution time: 376_418 nanoseconds. + Weight::from_ref_time(377_292_000) + .saturating_add(Weight::from_proof_size(26563)) + // Standard Error: 32_312_545 + .saturating_add(Weight::from_ref_time(35_904_826_312).saturating_mul(r.into())) .saturating_add(RocksDbWeight::get().reads(8_u64)) - .saturating_add(RocksDbWeight::get().reads((400_u64).saturating_mul(r.into()))) + .saturating_add(RocksDbWeight::get().reads((480_u64).saturating_mul(r.into()))) .saturating_add(RocksDbWeight::get().writes(5_u64)) - .saturating_add(RocksDbWeight::get().writes((320_u64).saturating_mul(r.into()))) - .saturating_add(Weight::from_proof_size(1169112).saturating_mul(r.into())) + .saturating_add(RocksDbWeight::get().writes((400_u64).saturating_mul(r.into()))) + .saturating_add(Weight::from_proof_size(1367114).saturating_mul(r.into())) } - /// Storage: System Account (r:82 w:82) + /// Storage: System Account (r:162 w:162) /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: Measured) /// Storage: Contracts ContractInfoOf (r:81 w:81) - /// Proof: Contracts ContractInfoOf (max_values: None, max_size: Some(258), added: 2733, mode: Measured) + /// Proof: Contracts ContractInfoOf (max_values: None, max_size: Some(290), added: 2765, mode: Measured) /// Storage: Contracts CodeStorage (r:2 w:0) /// Proof: Contracts CodeStorage (max_values: None, max_size: Some(126001), added: 128476, mode: Measured) /// Storage: Timestamp Now (r:1 w:0) @@ -3433,27 +3437,27 @@ impl WeightInfo for () { /// The range of component `s` is `[0, 960]`. fn seal_instantiate_per_transfer_input_salt_kb(t: u32, i: u32, s: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `5666 + t * (17 ±0)` - // Estimated: `651914 + t * (2762 ±3)` - // Minimum execution time: 130_366_376 nanoseconds. - Weight::from_ref_time(9_844_607_874) - .saturating_add(Weight::from_proof_size(651914)) - // Standard Error: 100_211_149 - .saturating_add(Weight::from_ref_time(390_481_449).saturating_mul(t.into())) - // Standard Error: 163_416 - .saturating_add(Weight::from_ref_time(126_154_200).saturating_mul(i.into())) - // Standard Error: 163_416 - .saturating_add(Weight::from_ref_time(126_430_874).saturating_mul(s.into())) - .saturating_add(RocksDbWeight::get().reads(249_u64)) + // Measured: `5785 + t * (33 ±0)` + // Estimated: `850985 + t * (2671 ±3)` + // Minimum execution time: 132_157_340 nanoseconds. + Weight::from_ref_time(11_329_968_948) + .saturating_add(Weight::from_proof_size(850985)) + // Standard Error: 99_102_968 + .saturating_add(Weight::from_ref_time(84_719_458).saturating_mul(t.into())) + // Standard Error: 161_609 + .saturating_add(Weight::from_ref_time(126_156_627).saturating_mul(i.into())) + // Standard Error: 161_609 + .saturating_add(Weight::from_ref_time(126_628_313).saturating_mul(s.into())) + .saturating_add(RocksDbWeight::get().reads(329_u64)) .saturating_add(RocksDbWeight::get().reads((1_u64).saturating_mul(t.into()))) - .saturating_add(RocksDbWeight::get().writes(246_u64)) + .saturating_add(RocksDbWeight::get().writes(326_u64)) .saturating_add(RocksDbWeight::get().writes((1_u64).saturating_mul(t.into()))) - .saturating_add(Weight::from_proof_size(2762).saturating_mul(t.into())) + .saturating_add(Weight::from_proof_size(2671).saturating_mul(t.into())) } /// Storage: System Account (r:1 w:0) /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: Measured) /// Storage: Contracts ContractInfoOf (r:1 w:1) - /// Proof: Contracts ContractInfoOf (max_values: None, max_size: Some(258), added: 2733, mode: Measured) + /// Proof: Contracts ContractInfoOf (max_values: None, max_size: Some(290), added: 2765, mode: Measured) /// Storage: Contracts CodeStorage (r:1 w:0) /// Proof: Contracts CodeStorage (max_values: None, max_size: Some(126001), added: 128476, mode: Measured) /// Storage: Timestamp Now (r:1 w:0) @@ -3463,13 +3467,13 @@ impl WeightInfo for () { /// The range of component `r` is `[0, 1]`. fn seal_hash_sha2_256(r: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `839 + r * (642 ±0)` - // Estimated: `17065 + r * (3210 ±0)` - // Minimum execution time: 373_848 nanoseconds. - Weight::from_ref_time(375_974_597) - .saturating_add(Weight::from_proof_size(17065)) - // Standard Error: 558_923 - .saturating_add(Weight::from_ref_time(42_648_202).saturating_mul(r.into())) + // Measured: `871 + r * (642 ±0)` + // Estimated: `17225 + r * (3210 ±0)` + // Minimum execution time: 373_559 nanoseconds. + Weight::from_ref_time(375_166_904) + .saturating_add(Weight::from_proof_size(17225)) + // Standard Error: 125_024 + .saturating_add(Weight::from_ref_time(42_291_595).saturating_mul(r.into())) .saturating_add(RocksDbWeight::get().reads(6_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) .saturating_add(Weight::from_proof_size(3210).saturating_mul(r.into())) @@ -3477,7 +3481,7 @@ impl WeightInfo for () { /// Storage: System Account (r:1 w:0) /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: Measured) /// Storage: Contracts ContractInfoOf (r:1 w:1) - /// Proof: Contracts ContractInfoOf (max_values: None, max_size: Some(258), added: 2733, mode: Measured) + /// Proof: Contracts ContractInfoOf (max_values: None, max_size: Some(290), added: 2765, mode: Measured) /// Storage: Contracts CodeStorage (r:1 w:0) /// Proof: Contracts CodeStorage (max_values: None, max_size: Some(126001), added: 128476, mode: Measured) /// Storage: Timestamp Now (r:1 w:0) @@ -3487,20 +3491,20 @@ impl WeightInfo for () { /// The range of component `n` is `[0, 1024]`. fn seal_hash_sha2_256_per_kb(n: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `1641` - // Estimated: `21000` - // Minimum execution time: 417_281 nanoseconds. - Weight::from_ref_time(418_086_000) - .saturating_add(Weight::from_proof_size(21000)) - // Standard Error: 43_883 - .saturating_add(Weight::from_ref_time(324_497_460).saturating_mul(n.into())) + // Measured: `1673` + // Estimated: `21160` + // Minimum execution time: 416_233 nanoseconds. + Weight::from_ref_time(416_785_000) + .saturating_add(Weight::from_proof_size(21160)) + // Standard Error: 56_223 + .saturating_add(Weight::from_ref_time(324_513_835).saturating_mul(n.into())) .saturating_add(RocksDbWeight::get().reads(6_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) } /// Storage: System Account (r:1 w:0) /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: Measured) /// Storage: Contracts ContractInfoOf (r:1 w:1) - /// Proof: Contracts ContractInfoOf (max_values: None, max_size: Some(258), added: 2733, mode: Measured) + /// Proof: Contracts ContractInfoOf (max_values: None, max_size: Some(290), added: 2765, mode: Measured) /// Storage: Contracts CodeStorage (r:1 w:0) /// Proof: Contracts CodeStorage (max_values: None, max_size: Some(126001), added: 128476, mode: Measured) /// Storage: Timestamp Now (r:1 w:0) @@ -3510,13 +3514,13 @@ impl WeightInfo for () { /// The range of component `r` is `[0, 1]`. fn seal_hash_keccak_256(r: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `841 + r * (642 ±0)` - // Estimated: `17075 + r * (3210 ±0)` - // Minimum execution time: 372_362 nanoseconds. - Weight::from_ref_time(374_975_677) - .saturating_add(Weight::from_proof_size(17075)) - // Standard Error: 307_932 - .saturating_add(Weight::from_ref_time(57_607_522).saturating_mul(r.into())) + // Measured: `873 + r * (642 ±0)` + // Estimated: `17235 + r * (3210 ±0)` + // Minimum execution time: 371_735 nanoseconds. + Weight::from_ref_time(375_979_430) + .saturating_add(Weight::from_proof_size(17235)) + // Standard Error: 968_037 + .saturating_add(Weight::from_ref_time(57_780_769).saturating_mul(r.into())) .saturating_add(RocksDbWeight::get().reads(6_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) .saturating_add(Weight::from_proof_size(3210).saturating_mul(r.into())) @@ -3524,7 +3528,7 @@ impl WeightInfo for () { /// Storage: System Account (r:1 w:0) /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: Measured) /// Storage: Contracts ContractInfoOf (r:1 w:1) - /// Proof: Contracts ContractInfoOf (max_values: None, max_size: Some(258), added: 2733, mode: Measured) + /// Proof: Contracts ContractInfoOf (max_values: None, max_size: Some(290), added: 2765, mode: Measured) /// Storage: Contracts CodeStorage (r:1 w:0) /// Proof: Contracts CodeStorage (max_values: None, max_size: Some(126001), added: 128476, mode: Measured) /// Storage: Timestamp Now (r:1 w:0) @@ -3534,20 +3538,20 @@ impl WeightInfo for () { /// The range of component `n` is `[0, 1024]`. fn seal_hash_keccak_256_per_kb(n: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `1643` - // Estimated: `21040` - // Minimum execution time: 431_034 nanoseconds. - Weight::from_ref_time(431_571_000) - .saturating_add(Weight::from_proof_size(21040)) - // Standard Error: 80_071 - .saturating_add(Weight::from_ref_time(261_645_325).saturating_mul(n.into())) + // Measured: `1675` + // Estimated: `21205` + // Minimum execution time: 428_196 nanoseconds. + Weight::from_ref_time(429_438_000) + .saturating_add(Weight::from_proof_size(21205)) + // Standard Error: 57_860 + .saturating_add(Weight::from_ref_time(260_917_896).saturating_mul(n.into())) .saturating_add(RocksDbWeight::get().reads(6_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) } /// Storage: System Account (r:1 w:0) /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: Measured) /// Storage: Contracts ContractInfoOf (r:1 w:1) - /// Proof: Contracts ContractInfoOf (max_values: None, max_size: Some(258), added: 2733, mode: Measured) + /// Proof: Contracts ContractInfoOf (max_values: None, max_size: Some(290), added: 2765, mode: Measured) /// Storage: Contracts CodeStorage (r:1 w:0) /// Proof: Contracts CodeStorage (max_values: None, max_size: Some(126001), added: 128476, mode: Measured) /// Storage: Timestamp Now (r:1 w:0) @@ -3557,13 +3561,13 @@ impl WeightInfo for () { /// The range of component `r` is `[0, 1]`. fn seal_hash_blake2_256(r: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `841 + r * (642 ±0)` - // Estimated: `17075 + r * (3210 ±0)` - // Minimum execution time: 372_069 nanoseconds. - Weight::from_ref_time(374_200_608) - .saturating_add(Weight::from_proof_size(17075)) - // Standard Error: 171_799 - .saturating_add(Weight::from_ref_time(32_843_391).saturating_mul(r.into())) + // Measured: `873 + r * (642 ±0)` + // Estimated: `17235 + r * (3210 ±0)` + // Minimum execution time: 371_412 nanoseconds. + Weight::from_ref_time(373_635_818) + .saturating_add(Weight::from_proof_size(17235)) + // Standard Error: 222_973 + .saturating_add(Weight::from_ref_time(33_347_181).saturating_mul(r.into())) .saturating_add(RocksDbWeight::get().reads(6_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) .saturating_add(Weight::from_proof_size(3210).saturating_mul(r.into())) @@ -3571,7 +3575,7 @@ impl WeightInfo for () { /// Storage: System Account (r:1 w:0) /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: Measured) /// Storage: Contracts ContractInfoOf (r:1 w:1) - /// Proof: Contracts ContractInfoOf (max_values: None, max_size: Some(258), added: 2733, mode: Measured) + /// Proof: Contracts ContractInfoOf (max_values: None, max_size: Some(290), added: 2765, mode: Measured) /// Storage: Contracts CodeStorage (r:1 w:0) /// Proof: Contracts CodeStorage (max_values: None, max_size: Some(126001), added: 128476, mode: Measured) /// Storage: Timestamp Now (r:1 w:0) @@ -3581,20 +3585,20 @@ impl WeightInfo for () { /// The range of component `n` is `[0, 1024]`. fn seal_hash_blake2_256_per_kb(n: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `1643` - // Estimated: `21010` - // Minimum execution time: 406_143 nanoseconds. - Weight::from_ref_time(406_744_000) - .saturating_add(Weight::from_proof_size(21010)) - // Standard Error: 48_038 - .saturating_add(Weight::from_ref_time(103_286_295).saturating_mul(n.into())) + // Measured: `1675` + // Estimated: `21180` + // Minimum execution time: 405_858 nanoseconds. + Weight::from_ref_time(406_498_000) + .saturating_add(Weight::from_proof_size(21180)) + // Standard Error: 48_388 + .saturating_add(Weight::from_ref_time(103_283_157).saturating_mul(n.into())) .saturating_add(RocksDbWeight::get().reads(6_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) } /// Storage: System Account (r:1 w:0) /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: Measured) /// Storage: Contracts ContractInfoOf (r:1 w:1) - /// Proof: Contracts ContractInfoOf (max_values: None, max_size: Some(258), added: 2733, mode: Measured) + /// Proof: Contracts ContractInfoOf (max_values: None, max_size: Some(290), added: 2765, mode: Measured) /// Storage: Contracts CodeStorage (r:1 w:0) /// Proof: Contracts CodeStorage (max_values: None, max_size: Some(126001), added: 128476, mode: Measured) /// Storage: Timestamp Now (r:1 w:0) @@ -3604,13 +3608,13 @@ impl WeightInfo for () { /// The range of component `r` is `[0, 1]`. fn seal_hash_blake2_128(r: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `841 + r * (679 ±0)` - // Estimated: `17075 + r * (3395 ±0)` - // Minimum execution time: 372_201 nanoseconds. - Weight::from_ref_time(374_049_708) - .saturating_add(Weight::from_proof_size(17075)) - // Standard Error: 387_179 - .saturating_add(Weight::from_ref_time(38_857_191).saturating_mul(r.into())) + // Measured: `873 + r * (679 ±0)` + // Estimated: `17235 + r * (3395 ±0)` + // Minimum execution time: 371_746 nanoseconds. + Weight::from_ref_time(373_538_171) + .saturating_add(Weight::from_proof_size(17235)) + // Standard Error: 387_332 + .saturating_add(Weight::from_ref_time(35_933_528).saturating_mul(r.into())) .saturating_add(RocksDbWeight::get().reads(6_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) .saturating_add(Weight::from_proof_size(3395).saturating_mul(r.into())) @@ -3618,7 +3622,7 @@ impl WeightInfo for () { /// Storage: System Account (r:1 w:0) /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: Measured) /// Storage: Contracts ContractInfoOf (r:1 w:1) - /// Proof: Contracts ContractInfoOf (max_values: None, max_size: Some(258), added: 2733, mode: Measured) + /// Proof: Contracts ContractInfoOf (max_values: None, max_size: Some(290), added: 2765, mode: Measured) /// Storage: Contracts CodeStorage (r:1 w:0) /// Proof: Contracts CodeStorage (max_values: None, max_size: Some(126001), added: 128476, mode: Measured) /// Storage: Timestamp Now (r:1 w:0) @@ -3628,20 +3632,20 @@ impl WeightInfo for () { /// The range of component `n` is `[0, 1024]`. fn seal_hash_blake2_128_per_kb(n: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `1643` - // Estimated: `21050` - // Minimum execution time: 405_819 nanoseconds. - Weight::from_ref_time(406_364_000) - .saturating_add(Weight::from_proof_size(21050)) - // Standard Error: 46_248 - .saturating_add(Weight::from_ref_time(103_189_157).saturating_mul(n.into())) + // Measured: `1675` + // Estimated: `21225` + // Minimum execution time: 405_752 nanoseconds. + Weight::from_ref_time(406_417_000) + .saturating_add(Weight::from_proof_size(21225)) + // Standard Error: 47_051 + .saturating_add(Weight::from_ref_time(103_325_027).saturating_mul(n.into())) .saturating_add(RocksDbWeight::get().reads(6_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) } /// Storage: System Account (r:1 w:0) /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: Measured) /// Storage: Contracts ContractInfoOf (r:1 w:1) - /// Proof: Contracts ContractInfoOf (max_values: None, max_size: Some(258), added: 2733, mode: Measured) + /// Proof: Contracts ContractInfoOf (max_values: None, max_size: Some(290), added: 2765, mode: Measured) /// Storage: Contracts CodeStorage (r:1 w:0) /// Proof: Contracts CodeStorage (max_values: None, max_size: Some(126001), added: 128476, mode: Measured) /// Storage: Timestamp Now (r:1 w:0) @@ -3651,13 +3655,13 @@ impl WeightInfo for () { /// The range of component `r` is `[0, 1]`. fn seal_ecdsa_recover(r: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `885 + r * (6083 ±0)` - // Estimated: `17295 + r * (30415 ±0)` - // Minimum execution time: 374_836 nanoseconds. - Weight::from_ref_time(379_385_500) - .saturating_add(Weight::from_proof_size(17295)) - // Standard Error: 1_694_427 - .saturating_add(Weight::from_ref_time(3_021_801_000).saturating_mul(r.into())) + // Measured: `917 + r * (6083 ±0)` + // Estimated: `17455 + r * (30415 ±0)` + // Minimum execution time: 373_882 nanoseconds. + Weight::from_ref_time(376_553_787) + .saturating_add(Weight::from_proof_size(17455)) + // Standard Error: 912_833 + .saturating_add(Weight::from_ref_time(3_021_100_412).saturating_mul(r.into())) .saturating_add(RocksDbWeight::get().reads(6_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) .saturating_add(Weight::from_proof_size(30415).saturating_mul(r.into())) @@ -3665,7 +3669,7 @@ impl WeightInfo for () { /// Storage: System Account (r:1 w:0) /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: Measured) /// Storage: Contracts ContractInfoOf (r:1 w:1) - /// Proof: Contracts ContractInfoOf (max_values: None, max_size: Some(258), added: 2733, mode: Measured) + /// Proof: Contracts ContractInfoOf (max_values: None, max_size: Some(290), added: 2765, mode: Measured) /// Storage: Contracts CodeStorage (r:1 w:0) /// Proof: Contracts CodeStorage (max_values: None, max_size: Some(126001), added: 128476, mode: Measured) /// Storage: Timestamp Now (r:1 w:0) @@ -3675,13 +3679,13 @@ impl WeightInfo for () { /// The range of component `r` is `[0, 1]`. fn seal_ecdsa_to_eth_address(r: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `854 + r * (3362 ±0)` - // Estimated: `17140 + r * (16810 ±0)` - // Minimum execution time: 374_223 nanoseconds. - Weight::from_ref_time(376_120_230) - .saturating_add(Weight::from_proof_size(17140)) - // Standard Error: 619_576 - .saturating_add(Weight::from_ref_time(754_257_969).saturating_mul(r.into())) + // Measured: `886 + r * (3362 ±0)` + // Estimated: `17300 + r * (16810 ±0)` + // Minimum execution time: 373_673 nanoseconds. + Weight::from_ref_time(375_712_961) + .saturating_add(Weight::from_proof_size(17300)) + // Standard Error: 596_297 + .saturating_add(Weight::from_ref_time(738_257_638).saturating_mul(r.into())) .saturating_add(RocksDbWeight::get().reads(6_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) .saturating_add(Weight::from_proof_size(16810).saturating_mul(r.into())) @@ -3689,7 +3693,7 @@ impl WeightInfo for () { /// Storage: System Account (r:1 w:0) /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: Measured) /// Storage: Contracts ContractInfoOf (r:1 w:1) - /// Proof: Contracts ContractInfoOf (max_values: None, max_size: Some(258), added: 2733, mode: Measured) + /// Proof: Contracts ContractInfoOf (max_values: None, max_size: Some(290), added: 2765, mode: Measured) /// Storage: Contracts CodeStorage (r:1536 w:0) /// Proof: Contracts CodeStorage (max_values: None, max_size: Some(126001), added: 128476, mode: Measured) /// Storage: Timestamp Now (r:1 w:0) @@ -3702,12 +3706,12 @@ impl WeightInfo for () { fn seal_set_code_hash(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0 + r * (79300 ±0)` - // Estimated: `64652 + r * (942952 ±829)` - // Minimum execution time: 374_442 nanoseconds. - Weight::from_ref_time(375_591_000) - .saturating_add(Weight::from_proof_size(64652)) - // Standard Error: 3_764_193 - .saturating_add(Weight::from_ref_time(1_552_885_601).saturating_mul(r.into())) + // Estimated: `64844 + r * (942952 ±833)` + // Minimum execution time: 374_097 nanoseconds. + Weight::from_ref_time(374_985_000) + .saturating_add(Weight::from_proof_size(64844)) + // Standard Error: 3_772_336 + .saturating_add(Weight::from_ref_time(1_546_402_854).saturating_mul(r.into())) .saturating_add(RocksDbWeight::get().reads(6_u64)) .saturating_add(RocksDbWeight::get().reads((225_u64).saturating_mul(r.into()))) .saturating_add(RocksDbWeight::get().writes(3_u64)) @@ -3717,7 +3721,7 @@ impl WeightInfo for () { /// Storage: System Account (r:1 w:0) /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: Measured) /// Storage: Contracts ContractInfoOf (r:1 w:1) - /// Proof: Contracts ContractInfoOf (max_values: None, max_size: Some(258), added: 2733, mode: Measured) + /// Proof: Contracts ContractInfoOf (max_values: None, max_size: Some(290), added: 2765, mode: Measured) /// Storage: Contracts CodeStorage (r:1 w:0) /// Proof: Contracts CodeStorage (max_values: None, max_size: Some(126001), added: 128476, mode: Measured) /// Storage: Timestamp Now (r:1 w:0) @@ -3727,13 +3731,13 @@ impl WeightInfo for () { /// The range of component `r` is `[0, 20]`. fn seal_reentrance_count(r: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `837 + r * (240 ±0)` - // Estimated: `17055 + r * (1200 ±0)` - // Minimum execution time: 374_922 nanoseconds. - Weight::from_ref_time(376_234_094) - .saturating_add(Weight::from_proof_size(17055)) - // Standard Error: 45_995 - .saturating_add(Weight::from_ref_time(11_606_505).saturating_mul(r.into())) + // Measured: `869 + r * (240 ±0)` + // Estimated: `17215 + r * (1200 ±0)` + // Minimum execution time: 374_249 nanoseconds. + Weight::from_ref_time(377_990_998) + .saturating_add(Weight::from_proof_size(17215)) + // Standard Error: 38_133 + .saturating_add(Weight::from_ref_time(11_483_273).saturating_mul(r.into())) .saturating_add(RocksDbWeight::get().reads(6_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) .saturating_add(Weight::from_proof_size(1200).saturating_mul(r.into())) @@ -3741,7 +3745,7 @@ impl WeightInfo for () { /// Storage: System Account (r:1 w:0) /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: Measured) /// Storage: Contracts ContractInfoOf (r:1 w:1) - /// Proof: Contracts ContractInfoOf (max_values: None, max_size: Some(258), added: 2733, mode: Measured) + /// Proof: Contracts ContractInfoOf (max_values: None, max_size: Some(290), added: 2765, mode: Measured) /// Storage: Contracts CodeStorage (r:1 w:0) /// Proof: Contracts CodeStorage (max_values: None, max_size: Some(126001), added: 128476, mode: Measured) /// Storage: Timestamp Now (r:1 w:0) @@ -3751,21 +3755,21 @@ impl WeightInfo for () { /// The range of component `r` is `[0, 20]`. fn seal_account_reentrance_count(r: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `2056 + r * (3153 ±0)` - // Estimated: `21730 + r * (15870 ±2)` - // Minimum execution time: 376_762 nanoseconds. - Weight::from_ref_time(396_934_359) - .saturating_add(Weight::from_proof_size(21730)) - // Standard Error: 68_263 - .saturating_add(Weight::from_ref_time(18_367_270).saturating_mul(r.into())) + // Measured: `2102 + r * (3154 ±0)` + // Estimated: `21980 + r * (15875 ±2)` + // Minimum execution time: 375_552 nanoseconds. + Weight::from_ref_time(400_624_032) + .saturating_add(Weight::from_proof_size(21980)) + // Standard Error: 82_523 + .saturating_add(Weight::from_ref_time(18_057_327).saturating_mul(r.into())) .saturating_add(RocksDbWeight::get().reads(6_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) - .saturating_add(Weight::from_proof_size(15870).saturating_mul(r.into())) + .saturating_add(Weight::from_proof_size(15875).saturating_mul(r.into())) } /// Storage: System Account (r:1 w:0) /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: Measured) /// Storage: Contracts ContractInfoOf (r:1 w:1) - /// Proof: Contracts ContractInfoOf (max_values: None, max_size: Some(258), added: 2733, mode: Measured) + /// Proof: Contracts ContractInfoOf (max_values: None, max_size: Some(290), added: 2765, mode: Measured) /// Storage: Contracts CodeStorage (r:1 w:0) /// Proof: Contracts CodeStorage (max_values: None, max_size: Some(126001), added: 128476, mode: Measured) /// Storage: Timestamp Now (r:1 w:0) @@ -3777,13 +3781,13 @@ impl WeightInfo for () { /// The range of component `r` is `[0, 20]`. fn seal_instantiation_nonce(r: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `840 + r * (240 ±0)` - // Estimated: `18405 + r * (1440 ±0)` - // Minimum execution time: 374_257 nanoseconds. - Weight::from_ref_time(380_453_380) - .saturating_add(Weight::from_proof_size(18405)) - // Standard Error: 42_718 - .saturating_add(Weight::from_ref_time(9_355_253).saturating_mul(r.into())) + // Measured: `872 + r * (240 ±0)` + // Estimated: `18598 + r * (1440 ±0)` + // Minimum execution time: 373_899 nanoseconds. + Weight::from_ref_time(379_733_943) + .saturating_add(Weight::from_proof_size(18598)) + // Standard Error: 32_022 + .saturating_add(Weight::from_ref_time(9_381_180).saturating_mul(r.into())) .saturating_add(RocksDbWeight::get().reads(7_u64)) .saturating_add(RocksDbWeight::get().writes(4_u64)) .saturating_add(Weight::from_proof_size(1440).saturating_mul(r.into())) @@ -3793,571 +3797,571 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 788 nanoseconds. - Weight::from_ref_time(1_209_829) + // Minimum execution time: 834 nanoseconds. + Weight::from_ref_time(1_009_646) .saturating_add(Weight::from_proof_size(0)) - // Standard Error: 3_436 - .saturating_add(Weight::from_ref_time(409_858).saturating_mul(r.into())) + // Standard Error: 388 + .saturating_add(Weight::from_ref_time(411_979).saturating_mul(r.into())) } /// The range of component `r` is `[0, 50]`. fn instr_i64load(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 955 nanoseconds. - Weight::from_ref_time(1_526_327) + // Minimum execution time: 882 nanoseconds. + Weight::from_ref_time(1_416_377) .saturating_add(Weight::from_proof_size(0)) - // Standard Error: 2_399 - .saturating_add(Weight::from_ref_time(1_084_504).saturating_mul(r.into())) + // Standard Error: 1_133 + .saturating_add(Weight::from_ref_time(1_075_838).saturating_mul(r.into())) } /// The range of component `r` is `[0, 50]`. fn instr_i64store(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 967 nanoseconds. - Weight::from_ref_time(1_576_183) + // Minimum execution time: 878 nanoseconds. + Weight::from_ref_time(1_343_056) .saturating_add(Weight::from_proof_size(0)) - // Standard Error: 5_719 - .saturating_add(Weight::from_ref_time(1_006_742).saturating_mul(r.into())) + // Standard Error: 426 + .saturating_add(Weight::from_ref_time(1_001_214).saturating_mul(r.into())) } /// The range of component `r` is `[0, 50]`. fn instr_select(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 849 nanoseconds. - Weight::from_ref_time(1_106_539) + // Minimum execution time: 796 nanoseconds. + Weight::from_ref_time(1_079_086) .saturating_add(Weight::from_proof_size(0)) - // Standard Error: 445 - .saturating_add(Weight::from_ref_time(1_149_752).saturating_mul(r.into())) + // Standard Error: 409 + .saturating_add(Weight::from_ref_time(1_149_188).saturating_mul(r.into())) } /// The range of component `r` is `[0, 50]`. fn instr_if(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 829 nanoseconds. - Weight::from_ref_time(1_171_360) + // Minimum execution time: 800 nanoseconds. + Weight::from_ref_time(1_044_184) .saturating_add(Weight::from_proof_size(0)) - // Standard Error: 552 - .saturating_add(Weight::from_ref_time(1_309_914).saturating_mul(r.into())) + // Standard Error: 707 + .saturating_add(Weight::from_ref_time(1_315_686).saturating_mul(r.into())) } /// The range of component `r` is `[0, 50]`. fn instr_br(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 835 nanoseconds. - Weight::from_ref_time(1_125_578) + // Minimum execution time: 807 nanoseconds. + Weight::from_ref_time(1_049_633) .saturating_add(Weight::from_proof_size(0)) - // Standard Error: 374 - .saturating_add(Weight::from_ref_time(641_683).saturating_mul(r.into())) + // Standard Error: 361 + .saturating_add(Weight::from_ref_time(640_530).saturating_mul(r.into())) } /// The range of component `r` is `[0, 50]`. fn instr_br_if(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 816 nanoseconds. - Weight::from_ref_time(1_032_093) + // Minimum execution time: 774 nanoseconds. + Weight::from_ref_time(1_124_053) .saturating_add(Weight::from_proof_size(0)) - // Standard Error: 811 - .saturating_add(Weight::from_ref_time(956_228).saturating_mul(r.into())) + // Standard Error: 784 + .saturating_add(Weight::from_ref_time(949_398).saturating_mul(r.into())) } /// The range of component `r` is `[0, 50]`. fn instr_br_table(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 801 nanoseconds. - Weight::from_ref_time(816_764) + // Minimum execution time: 810 nanoseconds. + Weight::from_ref_time(676_581) .saturating_add(Weight::from_proof_size(0)) - // Standard Error: 2_669 - .saturating_add(Weight::from_ref_time(1_166_556).saturating_mul(r.into())) + // Standard Error: 2_356 + .saturating_add(Weight::from_ref_time(1_163_465).saturating_mul(r.into())) } /// The range of component `e` is `[1, 256]`. fn instr_br_table_per_entry(e: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 2_639 nanoseconds. - Weight::from_ref_time(2_905_554) + // Minimum execution time: 2_580 nanoseconds. + Weight::from_ref_time(2_835_656) .saturating_add(Weight::from_proof_size(0)) - // Standard Error: 62 - .saturating_add(Weight::from_ref_time(4_438).saturating_mul(e.into())) + // Standard Error: 71 + .saturating_add(Weight::from_ref_time(4_686).saturating_mul(e.into())) } /// The range of component `r` is `[0, 50]`. fn instr_call(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 831 nanoseconds. - Weight::from_ref_time(1_729_584) + // Minimum execution time: 826 nanoseconds. + Weight::from_ref_time(1_625_698) .saturating_add(Weight::from_proof_size(0)) - // Standard Error: 33_753 - .saturating_add(Weight::from_ref_time(2_380_315).saturating_mul(r.into())) + // Standard Error: 1_740 + .saturating_add(Weight::from_ref_time(2_332_187).saturating_mul(r.into())) } /// The range of component `r` is `[0, 50]`. fn instr_call_indirect(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 964 nanoseconds. - Weight::from_ref_time(2_445_291) + // Minimum execution time: 901 nanoseconds. + Weight::from_ref_time(2_338_620) .saturating_add(Weight::from_proof_size(0)) - // Standard Error: 3_285 - .saturating_add(Weight::from_ref_time(2_938_681).saturating_mul(r.into())) + // Standard Error: 1_642 + .saturating_add(Weight::from_ref_time(2_924_090).saturating_mul(r.into())) } /// The range of component `p` is `[0, 128]`. fn instr_call_indirect_per_param(p: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 4_809 nanoseconds. - Weight::from_ref_time(6_763_286) + // Minimum execution time: 4_670 nanoseconds. + Weight::from_ref_time(5_556_246) .saturating_add(Weight::from_proof_size(0)) - // Standard Error: 3_994 - .saturating_add(Weight::from_ref_time(217_632).saturating_mul(p.into())) + // Standard Error: 1_491 + .saturating_add(Weight::from_ref_time(228_965).saturating_mul(p.into())) } /// The range of component `l` is `[0, 1024]`. fn instr_call_per_local(l: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 3_138 nanoseconds. - Weight::from_ref_time(3_894_816) + // Minimum execution time: 3_099 nanoseconds. + Weight::from_ref_time(3_896_177) .saturating_add(Weight::from_proof_size(0)) - // Standard Error: 131 - .saturating_add(Weight::from_ref_time(91_699).saturating_mul(l.into())) + // Standard Error: 99 + .saturating_add(Weight::from_ref_time(91_304).saturating_mul(l.into())) } /// The range of component `r` is `[0, 50]`. fn instr_local_get(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 2_959 nanoseconds. - Weight::from_ref_time(3_271_550) + // Minimum execution time: 3_042 nanoseconds. + Weight::from_ref_time(3_334_621) .saturating_add(Weight::from_proof_size(0)) - // Standard Error: 223 - .saturating_add(Weight::from_ref_time(460_056).saturating_mul(r.into())) + // Standard Error: 793 + .saturating_add(Weight::from_ref_time(459_346).saturating_mul(r.into())) } /// The range of component `r` is `[0, 50]`. fn instr_local_set(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 2_970 nanoseconds. - Weight::from_ref_time(3_216_157) + // Minimum execution time: 2_968 nanoseconds. + Weight::from_ref_time(3_235_286) .saturating_add(Weight::from_proof_size(0)) - // Standard Error: 413 - .saturating_add(Weight::from_ref_time(485_842).saturating_mul(r.into())) + // Standard Error: 427 + .saturating_add(Weight::from_ref_time(485_454).saturating_mul(r.into())) } /// The range of component `r` is `[0, 50]`. fn instr_local_tee(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 2_980 nanoseconds. - Weight::from_ref_time(3_323_878) + // Minimum execution time: 3_012 nanoseconds. + Weight::from_ref_time(3_303_555) .saturating_add(Weight::from_proof_size(0)) - // Standard Error: 2_652 - .saturating_add(Weight::from_ref_time(660_257).saturating_mul(r.into())) + // Standard Error: 371 + .saturating_add(Weight::from_ref_time(657_811).saturating_mul(r.into())) } /// The range of component `r` is `[0, 50]`. fn instr_global_get(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 917 nanoseconds. - Weight::from_ref_time(1_445_816) + // Minimum execution time: 865 nanoseconds. + Weight::from_ref_time(1_249_987) .saturating_add(Weight::from_proof_size(0)) - // Standard Error: 5_642 - .saturating_add(Weight::from_ref_time(894_521).saturating_mul(r.into())) + // Standard Error: 417 + .saturating_add(Weight::from_ref_time(896_704).saturating_mul(r.into())) } /// The range of component `r` is `[0, 50]`. fn instr_global_set(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 965 nanoseconds. - Weight::from_ref_time(1_373_722) + // Minimum execution time: 866 nanoseconds. + Weight::from_ref_time(1_216_218) .saturating_add(Weight::from_proof_size(0)) - // Standard Error: 1_157 - .saturating_add(Weight::from_ref_time(917_643).saturating_mul(r.into())) + // Standard Error: 503 + .saturating_add(Weight::from_ref_time(919_719).saturating_mul(r.into())) } /// The range of component `r` is `[0, 50]`. fn instr_memory_current(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 982 nanoseconds. - Weight::from_ref_time(1_240_280) + // Minimum execution time: 921 nanoseconds. + Weight::from_ref_time(1_228_408) .saturating_add(Weight::from_proof_size(0)) - // Standard Error: 308 - .saturating_add(Weight::from_ref_time(817_972).saturating_mul(r.into())) + // Standard Error: 309 + .saturating_add(Weight::from_ref_time(813_007).saturating_mul(r.into())) } /// The range of component `r` is `[0, 1]`. fn instr_memory_grow(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 858 nanoseconds. - Weight::from_ref_time(962_183) + // Minimum execution time: 820 nanoseconds. + Weight::from_ref_time(914_830) .saturating_add(Weight::from_proof_size(0)) - // Standard Error: 6_701 - .saturating_add(Weight::from_ref_time(239_704_216).saturating_mul(r.into())) + // Standard Error: 6_018 + .saturating_add(Weight::from_ref_time(237_062_769).saturating_mul(r.into())) } /// The range of component `r` is `[0, 50]`. fn instr_i64clz(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 807 nanoseconds. - Weight::from_ref_time(1_132_872) + // Minimum execution time: 812 nanoseconds. + Weight::from_ref_time(1_554_406) .saturating_add(Weight::from_proof_size(0)) - // Standard Error: 303 - .saturating_add(Weight::from_ref_time(633_832).saturating_mul(r.into())) + // Standard Error: 9_979 + .saturating_add(Weight::from_ref_time(625_434).saturating_mul(r.into())) } /// The range of component `r` is `[0, 50]`. fn instr_i64ctz(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 823 nanoseconds. - Weight::from_ref_time(1_267_518) + // Minimum execution time: 798 nanoseconds. + Weight::from_ref_time(1_095_113) .saturating_add(Weight::from_proof_size(0)) - // Standard Error: 483 - .saturating_add(Weight::from_ref_time(632_620).saturating_mul(r.into())) + // Standard Error: 243 + .saturating_add(Weight::from_ref_time(634_204).saturating_mul(r.into())) } /// The range of component `r` is `[0, 50]`. fn instr_i64popcnt(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 857 nanoseconds. - Weight::from_ref_time(1_105_214) + // Minimum execution time: 792 nanoseconds. + Weight::from_ref_time(1_109_845) .saturating_add(Weight::from_proof_size(0)) - // Standard Error: 285 - .saturating_add(Weight::from_ref_time(635_039).saturating_mul(r.into())) + // Standard Error: 14_944 + .saturating_add(Weight::from_ref_time(658_834).saturating_mul(r.into())) } /// The range of component `r` is `[0, 50]`. fn instr_i64eqz(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 821 nanoseconds. - Weight::from_ref_time(750_223) + // Minimum execution time: 815 nanoseconds. + Weight::from_ref_time(1_068_916) .saturating_add(Weight::from_proof_size(0)) - // Standard Error: 15_923 - .saturating_add(Weight::from_ref_time(686_322).saturating_mul(r.into())) + // Standard Error: 327 + .saturating_add(Weight::from_ref_time(652_897).saturating_mul(r.into())) } /// The range of component `r` is `[0, 50]`. fn instr_i64extendsi32(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 797 nanoseconds. - Weight::from_ref_time(1_145_072) + // Minimum execution time: 798 nanoseconds. + Weight::from_ref_time(1_069_745) .saturating_add(Weight::from_proof_size(0)) - // Standard Error: 311 - .saturating_add(Weight::from_ref_time(618_147).saturating_mul(r.into())) + // Standard Error: 306 + .saturating_add(Weight::from_ref_time(618_481).saturating_mul(r.into())) } /// The range of component `r` is `[0, 50]`. fn instr_i64extendui32(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 803 nanoseconds. - Weight::from_ref_time(1_139_498) + // Minimum execution time: 799 nanoseconds. + Weight::from_ref_time(1_398_001) .saturating_add(Weight::from_proof_size(0)) - // Standard Error: 284 - .saturating_add(Weight::from_ref_time(617_393).saturating_mul(r.into())) + // Standard Error: 6_234 + .saturating_add(Weight::from_ref_time(611_399).saturating_mul(r.into())) } /// The range of component `r` is `[0, 50]`. fn instr_i32wrapi64(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 814 nanoseconds. - Weight::from_ref_time(1_099_405) + // Minimum execution time: 811 nanoseconds. + Weight::from_ref_time(1_098_083) .saturating_add(Weight::from_proof_size(0)) - // Standard Error: 663 - .saturating_add(Weight::from_ref_time(618_565).saturating_mul(r.into())) + // Standard Error: 297 + .saturating_add(Weight::from_ref_time(617_692).saturating_mul(r.into())) } /// The range of component `r` is `[0, 50]`. fn instr_i64eq(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 819 nanoseconds. - Weight::from_ref_time(1_199_220) + // Minimum execution time: 815 nanoseconds. + Weight::from_ref_time(1_046_922) .saturating_add(Weight::from_proof_size(0)) - // Standard Error: 485 - .saturating_add(Weight::from_ref_time(906_878).saturating_mul(r.into())) + // Standard Error: 335 + .saturating_add(Weight::from_ref_time(909_196).saturating_mul(r.into())) } /// The range of component `r` is `[0, 50]`. fn instr_i64ne(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 822 nanoseconds. - Weight::from_ref_time(274_212) + // Minimum execution time: 805 nanoseconds. + Weight::from_ref_time(1_093_667) .saturating_add(Weight::from_proof_size(0)) - // Standard Error: 29_294 - .saturating_add(Weight::from_ref_time(971_608).saturating_mul(r.into())) + // Standard Error: 233 + .saturating_add(Weight::from_ref_time(907_378).saturating_mul(r.into())) } /// The range of component `r` is `[0, 50]`. fn instr_i64lts(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 796 nanoseconds. - Weight::from_ref_time(1_396_586) + // Minimum execution time: 805 nanoseconds. + Weight::from_ref_time(1_290_591) .saturating_add(Weight::from_proof_size(0)) - // Standard Error: 7_205 - .saturating_add(Weight::from_ref_time(903_202).saturating_mul(r.into())) + // Standard Error: 3_201 + .saturating_add(Weight::from_ref_time(902_006).saturating_mul(r.into())) } /// The range of component `r` is `[0, 50]`. fn instr_i64ltu(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 833 nanoseconds. - Weight::from_ref_time(1_115_115) + // Minimum execution time: 783 nanoseconds. + Weight::from_ref_time(1_159_977) .saturating_add(Weight::from_proof_size(0)) - // Standard Error: 310 - .saturating_add(Weight::from_ref_time(908_195).saturating_mul(r.into())) + // Standard Error: 2_310 + .saturating_add(Weight::from_ref_time(906_489).saturating_mul(r.into())) } /// The range of component `r` is `[0, 50]`. fn instr_i64gts(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 854 nanoseconds. - Weight::from_ref_time(1_170_419) + // Minimum execution time: 790 nanoseconds. + Weight::from_ref_time(1_109_719) .saturating_add(Weight::from_proof_size(0)) - // Standard Error: 298 - .saturating_add(Weight::from_ref_time(907_171).saturating_mul(r.into())) + // Standard Error: 261 + .saturating_add(Weight::from_ref_time(906_614).saturating_mul(r.into())) } /// The range of component `r` is `[0, 50]`. fn instr_i64gtu(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 822 nanoseconds. - Weight::from_ref_time(1_186_349) + // Minimum execution time: 776 nanoseconds. + Weight::from_ref_time(1_076_567) .saturating_add(Weight::from_proof_size(0)) - // Standard Error: 302 - .saturating_add(Weight::from_ref_time(917_857).saturating_mul(r.into())) + // Standard Error: 348 + .saturating_add(Weight::from_ref_time(919_374).saturating_mul(r.into())) } /// The range of component `r` is `[0, 50]`. fn instr_i64les(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 807 nanoseconds. - Weight::from_ref_time(1_127_093) + // Minimum execution time: 780 nanoseconds. + Weight::from_ref_time(1_069_663) .saturating_add(Weight::from_proof_size(0)) - // Standard Error: 1_891 - .saturating_add(Weight::from_ref_time(910_738).saturating_mul(r.into())) + // Standard Error: 265 + .saturating_add(Weight::from_ref_time(908_037).saturating_mul(r.into())) } /// The range of component `r` is `[0, 50]`. fn instr_i64leu(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 819 nanoseconds. - Weight::from_ref_time(1_143_022) + // Minimum execution time: 832 nanoseconds. + Weight::from_ref_time(930_920) .saturating_add(Weight::from_proof_size(0)) - // Standard Error: 309 - .saturating_add(Weight::from_ref_time(919_047).saturating_mul(r.into())) + // Standard Error: 2_170 + .saturating_add(Weight::from_ref_time(929_811).saturating_mul(r.into())) } /// The range of component `r` is `[0, 50]`. fn instr_i64ges(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 788 nanoseconds. - Weight::from_ref_time(1_116_914) + // Minimum execution time: 787 nanoseconds. + Weight::from_ref_time(1_087_325) .saturating_add(Weight::from_proof_size(0)) - // Standard Error: 643 - .saturating_add(Weight::from_ref_time(911_159).saturating_mul(r.into())) + // Standard Error: 315 + .saturating_add(Weight::from_ref_time(908_321).saturating_mul(r.into())) } /// The range of component `r` is `[0, 50]`. fn instr_i64geu(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 791 nanoseconds. - Weight::from_ref_time(1_123_747) + // Minimum execution time: 798 nanoseconds. + Weight::from_ref_time(1_029_132) .saturating_add(Weight::from_proof_size(0)) - // Standard Error: 583 - .saturating_add(Weight::from_ref_time(910_242).saturating_mul(r.into())) + // Standard Error: 2_095 + .saturating_add(Weight::from_ref_time(913_553).saturating_mul(r.into())) } /// The range of component `r` is `[0, 50]`. fn instr_i64add(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 787 nanoseconds. - Weight::from_ref_time(1_109_471) + // Minimum execution time: 791 nanoseconds. + Weight::from_ref_time(1_086_314) .saturating_add(Weight::from_proof_size(0)) - // Standard Error: 305 - .saturating_add(Weight::from_ref_time(897_608).saturating_mul(r.into())) + // Standard Error: 197 + .saturating_add(Weight::from_ref_time(896_392).saturating_mul(r.into())) } /// The range of component `r` is `[0, 50]`. fn instr_i64sub(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 821 nanoseconds. - Weight::from_ref_time(1_098_631) + // Minimum execution time: 793 nanoseconds. + Weight::from_ref_time(1_078_172) .saturating_add(Weight::from_proof_size(0)) - // Standard Error: 523 - .saturating_add(Weight::from_ref_time(887_814).saturating_mul(r.into())) + // Standard Error: 404 + .saturating_add(Weight::from_ref_time(886_329).saturating_mul(r.into())) } /// The range of component `r` is `[0, 50]`. fn instr_i64mul(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 794 nanoseconds. - Weight::from_ref_time(1_166_332) + // Minimum execution time: 799 nanoseconds. + Weight::from_ref_time(1_095_010) .saturating_add(Weight::from_proof_size(0)) - // Standard Error: 384 - .saturating_add(Weight::from_ref_time(885_584).saturating_mul(r.into())) + // Standard Error: 431 + .saturating_add(Weight::from_ref_time(886_513).saturating_mul(r.into())) } /// The range of component `r` is `[0, 50]`. fn instr_i64divs(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 822 nanoseconds. - Weight::from_ref_time(1_155_826) + // Minimum execution time: 810 nanoseconds. + Weight::from_ref_time(1_114_325) .saturating_add(Weight::from_proof_size(0)) - // Standard Error: 521 - .saturating_add(Weight::from_ref_time(1_520_958).saturating_mul(r.into())) + // Standard Error: 452 + .saturating_add(Weight::from_ref_time(1_521_849).saturating_mul(r.into())) } /// The range of component `r` is `[0, 50]`. fn instr_i64divu(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 881 nanoseconds. - Weight::from_ref_time(1_158_125) + // Minimum execution time: 784 nanoseconds. + Weight::from_ref_time(1_123_153) .saturating_add(Weight::from_proof_size(0)) - // Standard Error: 624 - .saturating_add(Weight::from_ref_time(1_458_378).saturating_mul(r.into())) + // Standard Error: 475 + .saturating_add(Weight::from_ref_time(1_457_746).saturating_mul(r.into())) } /// The range of component `r` is `[0, 50]`. fn instr_i64rems(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 827 nanoseconds. - Weight::from_ref_time(1_209_535) + // Minimum execution time: 809 nanoseconds. + Weight::from_ref_time(1_145_906) .saturating_add(Weight::from_proof_size(0)) - // Standard Error: 399 - .saturating_add(Weight::from_ref_time(1_547_640).saturating_mul(r.into())) + // Standard Error: 718 + .saturating_add(Weight::from_ref_time(1_549_964).saturating_mul(r.into())) } /// The range of component `r` is `[0, 50]`. fn instr_i64remu(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 801 nanoseconds. - Weight::from_ref_time(1_313_872) + // Minimum execution time: 803 nanoseconds. + Weight::from_ref_time(1_110_328) .saturating_add(Weight::from_proof_size(0)) - // Standard Error: 2_175 - .saturating_add(Weight::from_ref_time(1_449_416).saturating_mul(r.into())) + // Standard Error: 627 + .saturating_add(Weight::from_ref_time(1_453_013).saturating_mul(r.into())) } /// The range of component `r` is `[0, 50]`. fn instr_i64and(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 827 nanoseconds. - Weight::from_ref_time(1_093_874) + // Minimum execution time: 786 nanoseconds. + Weight::from_ref_time(1_108_792) .saturating_add(Weight::from_proof_size(0)) - // Standard Error: 708 - .saturating_add(Weight::from_ref_time(901_450).saturating_mul(r.into())) + // Standard Error: 286 + .saturating_add(Weight::from_ref_time(897_035).saturating_mul(r.into())) } /// The range of component `r` is `[0, 50]`. fn instr_i64or(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 824 nanoseconds. - Weight::from_ref_time(1_164_076) + // Minimum execution time: 787 nanoseconds. + Weight::from_ref_time(830_000) .saturating_add(Weight::from_proof_size(0)) - // Standard Error: 623 - .saturating_add(Weight::from_ref_time(897_579).saturating_mul(r.into())) + // Standard Error: 15_995 + .saturating_add(Weight::from_ref_time(963_344).saturating_mul(r.into())) } /// The range of component `r` is `[0, 50]`. fn instr_i64xor(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 789 nanoseconds. - Weight::from_ref_time(1_113_915) + // Minimum execution time: 773 nanoseconds. + Weight::from_ref_time(1_082_459) .saturating_add(Weight::from_proof_size(0)) - // Standard Error: 390 - .saturating_add(Weight::from_ref_time(897_354).saturating_mul(r.into())) + // Standard Error: 330 + .saturating_add(Weight::from_ref_time(897_077).saturating_mul(r.into())) } /// The range of component `r` is `[0, 50]`. fn instr_i64shl(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 811 nanoseconds. - Weight::from_ref_time(1_117_366) + // Minimum execution time: 810 nanoseconds. + Weight::from_ref_time(1_325_815) .saturating_add(Weight::from_proof_size(0)) - // Standard Error: 437 - .saturating_add(Weight::from_ref_time(903_759).saturating_mul(r.into())) + // Standard Error: 3_352 + .saturating_add(Weight::from_ref_time(899_555).saturating_mul(r.into())) } /// The range of component `r` is `[0, 50]`. fn instr_i64shrs(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 846 nanoseconds. - Weight::from_ref_time(1_103_954) + // Minimum execution time: 808 nanoseconds. + Weight::from_ref_time(1_085_903) .saturating_add(Weight::from_proof_size(0)) - // Standard Error: 414 - .saturating_add(Weight::from_ref_time(903_429).saturating_mul(r.into())) + // Standard Error: 430 + .saturating_add(Weight::from_ref_time(903_249).saturating_mul(r.into())) } /// The range of component `r` is `[0, 50]`. fn instr_i64shru(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 803 nanoseconds. - Weight::from_ref_time(1_124_328) + // Minimum execution time: 792 nanoseconds. + Weight::from_ref_time(1_091_261) .saturating_add(Weight::from_proof_size(0)) - // Standard Error: 410 - .saturating_add(Weight::from_ref_time(903_216).saturating_mul(r.into())) + // Standard Error: 312 + .saturating_add(Weight::from_ref_time(902_245).saturating_mul(r.into())) } /// The range of component `r` is `[0, 50]`. fn instr_i64rotl(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 810 nanoseconds. - Weight::from_ref_time(1_131_433) + // Minimum execution time: 807 nanoseconds. + Weight::from_ref_time(1_121_052) .saturating_add(Weight::from_proof_size(0)) - // Standard Error: 402 - .saturating_add(Weight::from_ref_time(903_381).saturating_mul(r.into())) + // Standard Error: 506 + .saturating_add(Weight::from_ref_time(902_772).saturating_mul(r.into())) } /// The range of component `r` is `[0, 50]`. fn instr_i64rotr(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 807 nanoseconds. - Weight::from_ref_time(1_144_933) + // Minimum execution time: 823 nanoseconds. + Weight::from_ref_time(1_317_597) .saturating_add(Weight::from_proof_size(0)) - // Standard Error: 373 - .saturating_add(Weight::from_ref_time(902_918).saturating_mul(r.into())) + // Standard Error: 6_219 + .saturating_add(Weight::from_ref_time(896_692).saturating_mul(r.into())) } } From b6c4ab862cda5dd00c16a5de89f7e23aa55e9c44 Mon Sep 17 00:00:00 2001 From: Sergejs Kostjucenko <85877331+sergejparity@users.noreply.github.com> Date: Thu, 16 Feb 2023 18:41:10 +0200 Subject: [PATCH 129/558] fix docker description publishing (#13399) --- scripts/ci/gitlab/pipeline/publish.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/ci/gitlab/pipeline/publish.yml b/scripts/ci/gitlab/pipeline/publish.yml index 557a7f212..0d2e0cac9 100644 --- a/scripts/ci/gitlab/pipeline/publish.yml +++ b/scripts/ci/gitlab/pipeline/publish.yml @@ -61,7 +61,7 @@ DOCKER_PASSWORD: $Docker_Hub_Pass_Parity README_FILEPATH: $CI_PROJECT_DIR/scripts/ci/docker/$PRODUCT.Dockerfile.README.md script: - - echo # Dummy command to satisfy GitLab CI linter. + - cd / && sh entrypoint.sh # publish image to docker.io/paritypr, (e.g. for later use in zombienet testing) .build-push-image-temporary: From 873fb68eeb544c436afe5edbd1625b252039653d Mon Sep 17 00:00:00 2001 From: Michal Kucharczyk <1728078+michalkucharczyk@users.noreply.github.com> Date: Thu, 16 Feb 2023 23:21:15 +0100 Subject: [PATCH 130/558] `BlockId` removal: `CallApiAt::state_at` (#13394) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * BlockId removal: CallApiAt::state_at It changes the arguments of: - `CallApiAt::state_at`, - `Client::code_at`, - `Client::state_at` from: `BlockId` to: `Block::Hash` * Apply suggestions from code review Co-authored-by: Bastian Köcher --------- Co-authored-by: Bastian Köcher Co-authored-by: parity-processbot <> --- client/service/src/client/client.rs | 8 +++----- primitives/api/src/lib.rs | 2 +- primitives/api/test/tests/runtime_calls.rs | 4 ++-- 3 files changed, 6 insertions(+), 8 deletions(-) diff --git a/client/service/src/client/client.rs b/client/service/src/client/client.rs index 6a75fad62..4b28b3993 100644 --- a/client/service/src/client/client.rs +++ b/client/service/src/client/client.rs @@ -481,8 +481,7 @@ where } /// Get the code at a given block. - pub fn code_at(&self, id: &BlockId) -> sp_blockchain::Result> { - let hash = self.backend.blockchain().expect_block_hash_from_id(id)?; + pub fn code_at(&self, hash: Block::Hash) -> sp_blockchain::Result> { Ok(StorageProvider::storage(self, hash, &StorageKey(well_known_keys::CODE.to_vec()))? .expect( "None is returned if there's no value stored for the given key;\ @@ -1746,9 +1745,8 @@ where CallExecutor::runtime_version(&self.executor, hash).map_err(Into::into) } - fn state_at(&self, at: &BlockId) -> Result { - let hash = self.backend.blockchain().expect_block_hash_from_id(at)?; - self.state_at(hash).map_err(Into::into) + fn state_at(&self, at: Block::Hash) -> Result { + self.state_at(at).map_err(Into::into) } } diff --git a/primitives/api/src/lib.rs b/primitives/api/src/lib.rs index 4ff4becb8..ad9a9b181 100644 --- a/primitives/api/src/lib.rs +++ b/primitives/api/src/lib.rs @@ -622,7 +622,7 @@ pub trait CallApiAt { fn runtime_version_at(&self, at: &BlockId) -> Result; /// Get the state `at` the given block. - fn state_at(&self, at: &BlockId) -> Result; + fn state_at(&self, at: Block::Hash) -> Result; } /// Auxiliary wrapper that holds an api instance and binds it to the given lifetime. diff --git a/primitives/api/test/tests/runtime_calls.rs b/primitives/api/test/tests/runtime_calls.rs index 2ac88c7e6..3d355c474 100644 --- a/primitives/api/test/tests/runtime_calls.rs +++ b/primitives/api/test/tests/runtime_calls.rs @@ -147,13 +147,12 @@ fn record_proof_works() { .set_execution_strategy(ExecutionStrategy::Both) .build_with_longest_chain(); - let block_id = BlockId::Number(client.chain_info().best_number); let storage_root = *futures::executor::block_on(longest_chain.best_chain()).unwrap().state_root(); let runtime_code = sp_core::traits::RuntimeCode { code_fetcher: &sp_core::traits::WrappedRuntimeCode( - client.code_at(&block_id).unwrap().into(), + client.code_at(client.chain_info().best_hash).unwrap().into(), ), hash: vec![1], heap_pages: None, @@ -167,6 +166,7 @@ fn record_proof_works() { } .into_signed_tx(); + let block_id = BlockId::Hash(client.chain_info().best_hash); // Build the block and record proof let mut builder = client .new_block_at(&block_id, Default::default(), true) From d97a18851f9da0b1c299daa8fb18022794065779 Mon Sep 17 00:00:00 2001 From: Michal Kucharczyk <1728078+michalkucharczyk@users.noreply.github.com> Date: Thu, 16 Feb 2023 23:34:00 +0100 Subject: [PATCH 131/558] `BlockId` removal: `Client::runtime_version_at` (#13393) * BlockId removal: Client::runtime_version_at It changes the arguments of `Client::runtime_version_at` from: `BlockId` to: `Block::Hash` * Apply suggestions from code review Co-authored-by: Anton --------- Co-authored-by: Anton Co-authored-by: parity-processbot <> --- bin/node/bench/src/construct.rs | 2 +- bin/node/bench/src/import.rs | 3 +-- bin/node/bench/src/txpool.rs | 2 +- bin/node/cli/src/service.rs | 6 +++--- bin/node/testing/src/bench.rs | 13 +++++-------- client/service/src/client/call_executor.rs | 2 +- client/service/src/client/client.rs | 3 +-- 7 files changed, 13 insertions(+), 18 deletions(-) diff --git a/bin/node/bench/src/construct.rs b/bin/node/bench/src/construct.rs index f994b29b5..529a3ba7a 100644 --- a/bin/node/bench/src/construct.rs +++ b/bin/node/bench/src/construct.rs @@ -120,7 +120,7 @@ impl core::Benchmark for ConstructionBenchmark { let _ = context .client - .runtime_version_at(&BlockId::Number(0)) + .runtime_version_at(context.client.chain_info().genesis_hash) .expect("Failed to get runtime version") .spec_version; diff --git a/bin/node/bench/src/import.rs b/bin/node/bench/src/import.rs index 7d59e94a4..db93833c8 100644 --- a/bin/node/bench/src/import.rs +++ b/bin/node/bench/src/import.rs @@ -35,7 +35,6 @@ use std::borrow::Cow; use node_primitives::Block; use node_testing::bench::{BenchDb, BlockType, DatabaseType, KeyTypes, Profile}; use sc_client_api::backend::Backend; -use sp_runtime::generic::BlockId; use sp_state_machine::InspectState; use crate::{ @@ -115,7 +114,7 @@ impl core::Benchmark for ImportBenchmark { let _ = context .client - .runtime_version_at(&BlockId::Number(0)) + .runtime_version_at(context.client.chain_info().genesis_hash) .expect("Failed to get runtime version") .spec_version; diff --git a/bin/node/bench/src/txpool.rs b/bin/node/bench/src/txpool.rs index 322dc352e..5b4d5513e 100644 --- a/bin/node/bench/src/txpool.rs +++ b/bin/node/bench/src/txpool.rs @@ -61,7 +61,7 @@ impl core::Benchmark for PoolBenchmark { let _ = context .client - .runtime_version_at(&BlockId::Number(0)) + .runtime_version_at(context.client.chain_info().genesis_hash) .expect("Failed to get runtime version") .spec_version; diff --git a/bin/node/cli/src/service.rs b/bin/node/cli/src/service.rs index 7ad1a9486..c98fe17cf 100644 --- a/bin/node/cli/src/service.rs +++ b/bin/node/cli/src/service.rs @@ -587,7 +587,7 @@ mod tests { use sp_keyring::AccountKeyring; use sp_keystore::{SyncCryptoStore, SyncCryptoStorePtr}; use sp_runtime::{ - generic::{BlockId, Digest, Era, SignedPayload}, + generic::{Digest, Era, SignedPayload}, key_types::BABE, traits::{Block as BlockT, Header as HeaderT, IdentifyAccount, Verify}, RuntimeAppPublic, @@ -754,9 +754,9 @@ mod tests { let to: Address = AccountPublic::from(bob.public()).into_account().into(); let from: Address = AccountPublic::from(charlie.public()).into_account().into(); let genesis_hash = service.client().block_hash(0).unwrap().unwrap(); - let best_block_id = BlockId::number(service.client().chain_info().best_number); + let best_hash = service.client().chain_info().best_hash; let (spec_version, transaction_version) = { - let version = service.client().runtime_version_at(&best_block_id).unwrap(); + let version = service.client().runtime_version_at(best_hash).unwrap(); (version.spec_version, version.transaction_version) }; let signer = charlie.clone(); diff --git a/bin/node/testing/src/bench.rs b/bin/node/testing/src/bench.rs index 2151a61a6..cc0a93ca2 100644 --- a/bin/node/testing/src/bench.rs +++ b/bin/node/testing/src/bench.rs @@ -42,7 +42,7 @@ use node_primitives::Block; use sc_block_builder::BlockBuilderProvider; use sc_client_api::{ execution_extensions::{ExecutionExtensions, ExecutionStrategies}, - BlockBackend, ExecutionStrategy, + ExecutionStrategy, }; use sc_client_db::PruningMode; use sc_consensus::{BlockImport, BlockImportParams, ForkChoiceStrategy, ImportResult, ImportedAux}; @@ -54,7 +54,7 @@ use sp_core::{blake2_256, ed25519, sr25519, traits::SpawnNamed, ExecutionContext use sp_inherents::InherentData; use sp_runtime::{ generic::BlockId, - traits::{Block as BlockT, IdentifyAccount, Verify, Zero}, + traits::{Block as BlockT, IdentifyAccount, Verify}, OpaqueExtrinsic, }; @@ -273,15 +273,12 @@ pub struct BlockContentIterator<'a> { impl<'a> BlockContentIterator<'a> { fn new(content: BlockContent, keyring: &'a BenchKeyring, client: &Client) -> Self { + let genesis_hash = client.chain_info().genesis_hash; + let runtime_version = client - .runtime_version_at(&BlockId::number(0)) + .runtime_version_at(genesis_hash) .expect("There should be runtime version at 0"); - let genesis_hash = client - .block_hash(Zero::zero()) - .expect("Database error?") - .expect("Genesis block always exists; qed"); - BlockContentIterator { iteration: 0, content, keyring, runtime_version, genesis_hash } } } diff --git a/client/service/src/client/call_executor.rs b/client/service/src/client/call_executor.rs index 7906d6224..49b969e3e 100644 --- a/client/service/src/client/call_executor.rs +++ b/client/service/src/client/call_executor.rs @@ -485,7 +485,7 @@ mod tests { ) .expect("Creates a client"); - let version = client.runtime_version_at(&BlockId::Number(0)).unwrap(); + let version = client.runtime_version_at(client.chain_info().genesis_hash).unwrap(); assert_eq!(SUBSTITUTE_SPEC_NAME, &*version.spec_name); } diff --git a/client/service/src/client/client.rs b/client/service/src/client/client.rs index 4b28b3993..d452104ce 100644 --- a/client/service/src/client/client.rs +++ b/client/service/src/client/client.rs @@ -491,8 +491,7 @@ where } /// Get the RuntimeVersion at a given block. - pub fn runtime_version_at(&self, id: &BlockId) -> sp_blockchain::Result { - let hash = self.backend.blockchain().expect_block_hash_from_id(id)?; + pub fn runtime_version_at(&self, hash: Block::Hash) -> sp_blockchain::Result { CallExecutor::runtime_version(&self.executor, hash) } From 63a24581f7f4c42642e8eb5812dd222b4f680a61 Mon Sep 17 00:00:00 2001 From: Adrian Catangiu Date: Fri, 17 Feb 2023 11:45:00 +0200 Subject: [PATCH 132/558] BEEFY: implement equivocations detection, reporting and slashing (#13121) * client/beefy: simplify self_vote logic * client/beefy: migrate to new state version * client/beefy: detect equivocated votes * fix typos * sp-beefy: add equivocation primitives * client/beefy: refactor vote processing * fix version migration for new rounds struct * client/beefy: track equivocations and create proofs * client/beefy: adjust tests for new voting logic * sp-beefy: fix commitment ordering and equality * client/beefy: simplify handle_vote() a bit * client/beefy: add simple equivocation test * client/beefy: submit equivocation proof - WIP * frame/beefy: add equivocation report runtime api - part 1 * frame/beefy: report equivocation logic - part 2 * frame/beefy: add pluggable Equivocation handler - part 3 * frame/beefy: impl ValidateUnsigned for equivocations reporting * client/beefy: submit report equivocation unsigned extrinsic * primitives/beefy: fix tests * frame/beefy: add default weights * frame/beefy: fix tests * client/beefy: fix tests * frame/beefy-mmr: fix tests * frame/beefy: cross-check session index with equivocation report * sp-beefy: make test Keyring useable in pallet * frame/beefy: add basic equivocation test * frame/beefy: test verify equivocation results in slashing * frame/beefy: test report_equivocation_old_set * frame/beefy: add more equivocation tests * sp-beefy: fix docs * beefy: simplify equivocations and fix tests * client/beefy: address review comments * frame/beefy: add ValidateUnsigned to test/mock runtime * client/beefy: fixes after merge master * fix missed merge damage * client/beefy: add test for reporting equivocations Also validated there's no unexpected equivocations reported in the other tests. Signed-off-by: acatangiu * sp-beefy: move test utils to their own file * client/beefy: add negative test for equivocation reports * sp-beefy: move back MmrRootProvider - used in polkadot-service * impl review suggestions * client/beefy: add equivocation metrics --------- Signed-off-by: acatangiu Co-authored-by: parity-processbot <> --- Cargo.lock | 8 + client/beefy/src/communication/gossip.rs | 4 +- client/beefy/src/error.rs | 18 +- client/beefy/src/justification.rs | 3 +- client/beefy/src/keystore.rs | 2 +- client/beefy/src/lib.rs | 3 +- client/beefy/src/metrics.rs | 47 +- client/beefy/src/round.rs | 52 +- client/beefy/src/tests.rs | 127 +++-- client/beefy/src/worker.rs | 157 +++++- client/consensus/babe/src/lib.rs | 2 +- frame/beefy-mmr/src/lib.rs | 4 +- frame/beefy-mmr/src/mock.rs | 13 +- frame/beefy/Cargo.toml | 12 + frame/beefy/src/default_weights.rs | 54 ++ frame/beefy/src/equivocation.rs | 385 ++++++++++++++ frame/beefy/src/lib.rs | 252 ++++++++- frame/beefy/src/mock.rs | 231 +++++++- frame/beefy/src/tests.rs | 647 ++++++++++++++++++++++- primitives/beefy/src/lib.rs | 208 +++++--- primitives/beefy/src/test_utils.rs | 110 ++++ test-utils/runtime/src/lib.rs | 14 + 22 files changed, 2140 insertions(+), 213 deletions(-) create mode 100644 frame/beefy/src/default_weights.rs create mode 100644 frame/beefy/src/equivocation.rs create mode 100644 primitives/beefy/src/test_utils.rs diff --git a/Cargo.lock b/Cargo.lock index ef320a85f..b33f7627f 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -5500,9 +5500,16 @@ dependencies = [ name = "pallet-beefy" version = "4.0.0-dev" dependencies = [ + "frame-election-provider-support", "frame-support", "frame-system", + "pallet-authorship", + "pallet-balances", + "pallet-offences", "pallet-session", + "pallet-staking", + "pallet-staking-reward-curve", + "pallet-timestamp", "parity-scale-codec", "scale-info", "serde", @@ -5510,6 +5517,7 @@ dependencies = [ "sp-core", "sp-io", "sp-runtime", + "sp-session", "sp-staking", "sp-std", ] diff --git a/client/beefy/src/communication/gossip.rs b/client/beefy/src/communication/gossip.rs index 5d5161021..7e60eb11e 100644 --- a/client/beefy/src/communication/gossip.rs +++ b/client/beefy/src/communication/gossip.rs @@ -244,8 +244,8 @@ mod tests { use crate::keystore::BeefyKeystore; use beefy_primitives::{ - crypto::Signature, keyring::Keyring, known_payloads, Commitment, MmrRootHash, Payload, - VoteMessage, KEY_TYPE, + crypto::Signature, known_payloads, Commitment, Keyring, MmrRootHash, Payload, VoteMessage, + KEY_TYPE, }; use super::*; diff --git a/client/beefy/src/error.rs b/client/beefy/src/error.rs index dd5fd649d..1b44e3801 100644 --- a/client/beefy/src/error.rs +++ b/client/beefy/src/error.rs @@ -22,14 +22,30 @@ use std::fmt::Debug; -#[derive(Debug, thiserror::Error, PartialEq)] +#[derive(Debug, thiserror::Error)] pub enum Error { #[error("Backend: {0}")] Backend(String), #[error("Keystore error: {0}")] Keystore(String), + #[error("Runtime api error: {0}")] + RuntimeApi(sp_api::ApiError), #[error("Signature error: {0}")] Signature(String), #[error("Session uninitialized")] UninitSession, } + +#[cfg(test)] +impl PartialEq for Error { + fn eq(&self, other: &Self) -> bool { + match (self, other) { + (Error::Backend(s1), Error::Backend(s2)) => s1 == s2, + (Error::Keystore(s1), Error::Keystore(s2)) => s1 == s2, + (Error::RuntimeApi(_), Error::RuntimeApi(_)) => true, + (Error::Signature(s1), Error::Signature(s2)) => s1 == s2, + (Error::UninitSession, Error::UninitSession) => true, + _ => false, + } + } +} diff --git a/client/beefy/src/justification.rs b/client/beefy/src/justification.rs index 6f869015b..9f433aa6a 100644 --- a/client/beefy/src/justification.rs +++ b/client/beefy/src/justification.rs @@ -81,8 +81,7 @@ fn verify_with_validator_set( #[cfg(test)] pub(crate) mod tests { use beefy_primitives::{ - keyring::Keyring, known_payloads, Commitment, Payload, SignedCommitment, - VersionedFinalityProof, + known_payloads, Commitment, Keyring, Payload, SignedCommitment, VersionedFinalityProof, }; use substrate_test_runtime_client::runtime::Block; diff --git a/client/beefy/src/keystore.rs b/client/beefy/src/keystore.rs index 73788a31b..29436e36e 100644 --- a/client/beefy/src/keystore.rs +++ b/client/beefy/src/keystore.rs @@ -123,7 +123,7 @@ pub mod tests { use sc_keystore::LocalKeystore; use sp_core::{ecdsa, Pair}; - use beefy_primitives::{crypto, keyring::Keyring}; + use beefy_primitives::{crypto, Keyring}; use super::*; use crate::error::Error; diff --git a/client/beefy/src/lib.rs b/client/beefy/src/lib.rs index 5f74b052e..4031b5297 100644 --- a/client/beefy/src/lib.rs +++ b/client/beefy/src/lib.rs @@ -282,6 +282,7 @@ where let worker_params = worker::WorkerParams { backend, payload_provider, + runtime, network, key_store: key_store.into(), gossip_engine, @@ -292,7 +293,7 @@ where persisted_state, }; - let worker = worker::BeefyWorker::<_, _, _, _>::new(worker_params); + let worker = worker::BeefyWorker::<_, _, _, _, _>::new(worker_params); futures::future::join( worker.run(block_import_justif, finality_notifications), diff --git a/client/beefy/src/metrics.rs b/client/beefy/src/metrics.rs index e3b48e6ec..c74fb420d 100644 --- a/client/beefy/src/metrics.rs +++ b/client/beefy/src/metrics.rs @@ -46,10 +46,16 @@ pub struct VoterMetrics { pub beefy_no_authority_found_in_store: Counter, /// Number of currently buffered votes pub beefy_buffered_votes: Gauge, - /// Number of valid but stale votes received - pub beefy_stale_votes: Counter, /// Number of votes dropped due to full buffers pub beefy_buffered_votes_dropped: Counter, + /// Number of good votes successfully handled + pub beefy_good_votes_processed: Counter, + /// Number of equivocation votes received + pub beefy_equivocation_votes: Counter, + /// Number of invalid votes received + pub beefy_invalid_votes: Counter, + /// Number of valid but stale votes received + pub beefy_stale_votes: Counter, /// Number of currently buffered justifications pub beefy_buffered_justifications: Gauge, /// Number of valid but stale justifications received @@ -60,8 +66,6 @@ pub struct VoterMetrics { pub beefy_buffered_justifications_dropped: Counter, /// Trying to set Best Beefy block to old block pub beefy_best_block_set_last_failure: Gauge, - /// Number of Successful handled votes - pub beefy_successful_handled_votes: Counter, } impl PrometheusRegister for VoterMetrics { @@ -109,17 +113,35 @@ impl PrometheusRegister for VoterMetrics { Gauge::new("substrate_beefy_buffered_votes", "Number of currently buffered votes")?, registry, )?, - beefy_stale_votes: register( + beefy_buffered_votes_dropped: register( + Counter::new( + "substrate_beefy_buffered_votes_dropped", + "Number of votes dropped due to full buffers", + )?, + registry, + )?, + beefy_good_votes_processed: register( + Counter::new( + "substrate_beefy_successful_handled_votes", + "Number of good votes successfully handled", + )?, + registry, + )?, + beefy_equivocation_votes: register( Counter::new( "substrate_beefy_stale_votes", - "Number of valid but stale votes received", + "Number of equivocation votes received", )?, registry, )?, - beefy_buffered_votes_dropped: register( + beefy_invalid_votes: register( + Counter::new("substrate_beefy_stale_votes", "Number of invalid votes received")?, + registry, + )?, + beefy_stale_votes: register( Counter::new( - "substrate_beefy_buffered_votes_dropped", - "Number of votes dropped due to full buffers", + "substrate_beefy_stale_votes", + "Number of valid but stale votes received", )?, registry, )?, @@ -158,13 +180,6 @@ impl PrometheusRegister for VoterMetrics { )?, registry, )?, - beefy_successful_handled_votes: register( - Counter::new( - "substrate_beefy_successful_handled_votes", - "Number of Successful handled votes", - )?, - registry, - )?, }) } } diff --git a/client/beefy/src/round.rs b/client/beefy/src/round.rs index 9ad4e5d02..b2c4e97a4 100644 --- a/client/beefy/src/round.rs +++ b/client/beefy/src/round.rs @@ -20,7 +20,7 @@ use crate::LOG_TARGET; use beefy_primitives::{ crypto::{AuthorityId, Public, Signature}, - Commitment, SignedCommitment, ValidatorSet, ValidatorSetId, VoteMessage, + Commitment, EquivocationProof, SignedCommitment, ValidatorSet, ValidatorSetId, VoteMessage, }; use codec::{Decode, Encode}; use log::debug; @@ -61,7 +61,7 @@ pub fn threshold(authorities: usize) -> usize { pub enum VoteImportResult { Ok, RoundConcluded(SignedCommitment, Signature>), - Equivocation, /* TODO: (EquivocationProof, Public, Signature>) */ + Equivocation(EquivocationProof, Public, Signature>), Invalid, Stale, } @@ -149,8 +149,10 @@ where target: LOG_TARGET, "🥩 detected equivocated vote: 1st: {:?}, 2nd: {:?}", previous_vote, vote ); - // TODO: build `EquivocationProof` and return it here. - return VoteImportResult::Equivocation + return VoteImportResult::Equivocation(EquivocationProof { + first: previous_vote.clone(), + second: vote, + }) } } else { // this is the first vote sent by `id` for `num`, all good @@ -197,8 +199,8 @@ mod tests { use sc_network_test::Block; use beefy_primitives::{ - crypto::Public, keyring::Keyring, known_payloads::MMR_ROOT_ID, Commitment, Payload, - SignedCommitment, ValidatorSet, VoteMessage, + crypto::Public, known_payloads::MMR_ROOT_ID, Commitment, EquivocationProof, Keyring, + Payload, SignedCommitment, ValidatorSet, VoteMessage, }; use super::{threshold, Block as BlockT, RoundTracker, Rounds}; @@ -452,4 +454,42 @@ mod tests { rounds.conclude(3); assert!(rounds.previous_votes.is_empty()); } + + #[test] + fn should_provide_equivocation_proof() { + sp_tracing::try_init_simple(); + + let validators = ValidatorSet::::new( + vec![Keyring::Alice.public(), Keyring::Bob.public()], + Default::default(), + ) + .unwrap(); + let validator_set_id = validators.id(); + let session_start = 1u64.into(); + let mut rounds = Rounds::::new(session_start, validators); + + let payload1 = Payload::from_single_entry(MMR_ROOT_ID, vec![1, 1, 1, 1]); + let payload2 = Payload::from_single_entry(MMR_ROOT_ID, vec![2, 2, 2, 2]); + let commitment1 = Commitment { block_number: 1, payload: payload1, validator_set_id }; + let commitment2 = Commitment { block_number: 1, payload: payload2, validator_set_id }; + + let alice_vote1 = VoteMessage { + id: Keyring::Alice.public(), + commitment: commitment1, + signature: Keyring::Alice.sign(b"I am committed"), + }; + let mut alice_vote2 = alice_vote1.clone(); + alice_vote2.commitment = commitment2; + + let expected_result = VoteImportResult::Equivocation(EquivocationProof { + first: alice_vote1.clone(), + second: alice_vote2.clone(), + }); + + // vote on one payload - ok + assert_eq!(rounds.add_vote(alice_vote1), VoteImportResult::Ok); + + // vote on _another_ commitment/payload -> expected equivocation proof + assert_eq!(rounds.add_vote(alice_vote2), expected_result); + } } diff --git a/client/beefy/src/tests.rs b/client/beefy/src/tests.rs index 007c57e42..42f56226c 100644 --- a/client/beefy/src/tests.rs +++ b/client/beefy/src/tests.rs @@ -31,10 +31,10 @@ use crate::{ }; use beefy_primitives::{ crypto::{AuthorityId, Signature}, - keyring::Keyring as BeefyKeyring, known_payloads, mmr::MmrRootProvider, - BeefyApi, Commitment, ConsensusLog, MmrRootHash, Payload, SignedCommitment, ValidatorSet, + BeefyApi, Commitment, ConsensusLog, EquivocationProof, Keyring as BeefyKeyring, MmrRootHash, + OpaqueKeyOwnershipProof, Payload, SignedCommitment, ValidatorSet, ValidatorSetId, VersionedFinalityProof, BEEFY_ENGINE_ID, KEY_TYPE as BeefyKeyType, }; use futures::{future, stream::FuturesUnordered, Future, StreamExt}; @@ -55,7 +55,7 @@ use sp_api::{ApiRef, ProvideRuntimeApi}; use sp_consensus::BlockOrigin; use sp_core::H256; use sp_keystore::{testing::KeyStore as TestKeystore, SyncCryptoStore, SyncCryptoStorePtr}; -use sp_mmr_primitives::{EncodableOpaqueLeaf, Error as MmrError, MmrApi, Proof}; +use sp_mmr_primitives::{Error as MmrError, MmrApi}; use sp_runtime::{ codec::Encode, generic::BlockId, @@ -73,6 +73,7 @@ fn beefy_gossip_proto_name() -> ProtocolName { const GOOD_MMR_ROOT: MmrRootHash = MmrRootHash::repeat_byte(0xbf); const BAD_MMR_ROOT: MmrRootHash = MmrRootHash::repeat_byte(0x42); +const ALTERNATE_BAD_MMR_ROOT: MmrRootHash = MmrRootHash::repeat_byte(0x13); type BeefyBlockImport = crate::BeefyBlockImport< Block, @@ -236,6 +237,8 @@ pub(crate) struct TestApi { pub beefy_genesis: u64, pub validator_set: BeefyValidatorSet, pub mmr_root_hash: MmrRootHash, + pub reported_equivocations: + Option, AuthorityId, Signature>>>>>, } impl TestApi { @@ -244,7 +247,12 @@ impl TestApi { validator_set: &BeefyValidatorSet, mmr_root_hash: MmrRootHash, ) -> Self { - TestApi { beefy_genesis, validator_set: validator_set.clone(), mmr_root_hash } + TestApi { + beefy_genesis, + validator_set: validator_set.clone(), + mmr_root_hash, + reported_equivocations: None, + } } pub fn with_validator_set(validator_set: &BeefyValidatorSet) -> Self { @@ -252,8 +260,13 @@ impl TestApi { beefy_genesis: 1, validator_set: validator_set.clone(), mmr_root_hash: GOOD_MMR_ROOT, + reported_equivocations: None, } } + + pub fn allow_equivocations(&mut self) { + self.reported_equivocations = Some(Arc::new(Mutex::new(vec![]))); + } } // compiler gets confused and warns us about unused inner @@ -277,31 +290,29 @@ sp_api::mock_impl_runtime_apis! { fn validator_set() -> Option { Some(self.inner.validator_set.clone()) } + + fn submit_report_equivocation_unsigned_extrinsic( + proof: EquivocationProof, AuthorityId, Signature>, + _dummy: OpaqueKeyOwnershipProof, + ) -> Option<()> { + if let Some(equivocations_buf) = self.inner.reported_equivocations.as_ref() { + equivocations_buf.lock().push(proof); + None + } else { + panic!("Equivocations not expected, but following proof was reported: {:?}", proof); + } + } + + fn generate_key_ownership_proof( + _dummy1: ValidatorSetId, + _dummy2: AuthorityId, + ) -> Option { Some(OpaqueKeyOwnershipProof::new(vec![])) } } impl MmrApi> for RuntimeApi { fn mmr_root() -> Result { Ok(self.inner.mmr_root_hash) } - - fn generate_proof( - _block_numbers: Vec, - _best_known_block_number: Option - ) -> Result<(Vec, Proof), MmrError> { - unimplemented!() - } - - fn verify_proof(_leaves: Vec, _proof: Proof) -> Result<(), MmrError> { - unimplemented!() - } - - fn verify_proof_stateless( - _root: MmrRootHash, - _leaves: Vec, - _proof: Proof - ) -> Result<(), MmrError> { - unimplemented!() - } } } @@ -330,7 +341,7 @@ pub(crate) fn create_beefy_keystore(authority: BeefyKeyring) -> SyncCryptoStoreP keystore } -fn voter_init_setup( +async fn voter_init_setup( net: &mut BeefyTestNet, finality: &mut futures::stream::Fuse>, api: &TestApi, @@ -345,9 +356,7 @@ fn voter_init_setup( gossip_validator, None, ); - let best_grandpa = - futures::executor::block_on(wait_for_runtime_pallet(api, &mut gossip_engine, finality)) - .unwrap(); + let best_grandpa = wait_for_runtime_pallet(api, &mut gossip_engine, finality).await.unwrap(); load_or_init_voter_state(&*backend, api, best_grandpa, 1) } @@ -980,7 +989,7 @@ async fn should_initialize_voter_at_genesis() { let api = TestApi::with_validator_set(&validator_set); // load persistent state - nothing in DB, should init at genesis - let persisted_state = voter_init_setup(&mut net, &mut finality, &api).unwrap(); + let persisted_state = voter_init_setup(&mut net, &mut finality, &api).await.unwrap(); // Test initialization at session boundary. // verify voter initialized with two sessions starting at blocks 1 and 10 @@ -1029,7 +1038,7 @@ async fn should_initialize_voter_at_custom_genesis() { net.peer(0).client().as_client().finalize_block(hashes[8], None).unwrap(); // load persistent state - nothing in DB, should init at genesis - let persisted_state = voter_init_setup(&mut net, &mut finality, &api).unwrap(); + let persisted_state = voter_init_setup(&mut net, &mut finality, &api).await.unwrap(); // Test initialization at session boundary. // verify voter initialized with single session starting at block `custom_pallet_genesis` (7) @@ -1090,7 +1099,7 @@ async fn should_initialize_voter_when_last_final_is_session_boundary() { let api = TestApi::with_validator_set(&validator_set); // load persistent state - nothing in DB, should init at session boundary - let persisted_state = voter_init_setup(&mut net, &mut finality, &api).unwrap(); + let persisted_state = voter_init_setup(&mut net, &mut finality, &api).await.unwrap(); // verify voter initialized with single session starting at block 10 assert_eq!(persisted_state.voting_oracle().sessions().len(), 1); @@ -1148,7 +1157,7 @@ async fn should_initialize_voter_at_latest_finalized() { let api = TestApi::with_validator_set(&validator_set); // load persistent state - nothing in DB, should init at last BEEFY finalized - let persisted_state = voter_init_setup(&mut net, &mut finality, &api).unwrap(); + let persisted_state = voter_init_setup(&mut net, &mut finality, &api).await.unwrap(); // verify voter initialized with single session starting at block 12 assert_eq!(persisted_state.voting_oracle().sessions().len(), 1); @@ -1205,3 +1214,59 @@ async fn beefy_finalizing_after_pallet_genesis() { // GRANDPA finalize #21 -> BEEFY finalize #20 (mandatory) and #21 finalize_block_and_wait_for_beefy(&net, peers.clone(), &[hashes[21]], &[20, 21]).await; } + +#[tokio::test] +async fn beefy_reports_equivocations() { + sp_tracing::try_init_simple(); + + let peers = [BeefyKeyring::Alice, BeefyKeyring::Bob, BeefyKeyring::Charlie]; + let validator_set = ValidatorSet::new(make_beefy_ids(&peers), 0).unwrap(); + let session_len = 10; + let min_block_delta = 4; + + let mut net = BeefyTestNet::new(3); + + // Alice votes on good MMR roots, equivocations are allowed/expected. + let mut api_alice = TestApi::with_validator_set(&validator_set); + api_alice.allow_equivocations(); + let api_alice = Arc::new(api_alice); + let alice = (0, &peers[0], api_alice.clone()); + tokio::spawn(initialize_beefy(&mut net, vec![alice], min_block_delta)); + + // Bob votes on bad MMR roots, equivocations are allowed/expected. + let mut api_bob = TestApi::new(1, &validator_set, BAD_MMR_ROOT); + api_bob.allow_equivocations(); + let api_bob = Arc::new(api_bob); + let bob = (1, &peers[1], api_bob.clone()); + tokio::spawn(initialize_beefy(&mut net, vec![bob], min_block_delta)); + + // We spawn another node voting with Bob key, on alternate bad MMR roots (equivocating). + // Equivocations are allowed/expected. + let mut api_bob_prime = TestApi::new(1, &validator_set, ALTERNATE_BAD_MMR_ROOT); + api_bob_prime.allow_equivocations(); + let api_bob_prime = Arc::new(api_bob_prime); + let bob_prime = (2, &BeefyKeyring::Bob, api_bob_prime.clone()); + tokio::spawn(initialize_beefy(&mut net, vec![bob_prime], min_block_delta)); + + // push 42 blocks including `AuthorityChange` digests every 10 blocks. + let hashes = net.generate_blocks_and_sync(42, session_len, &validator_set, false).await; + + let net = Arc::new(Mutex::new(net)); + + // Minimum BEEFY block delta is 4. + + let peers = peers.into_iter().enumerate(); + // finalize block #1 -> BEEFY should not finalize anything (each node votes on different MMR). + finalize_block_and_wait_for_beefy(&net, peers, &[hashes[1]], &[]).await; + + // Verify neither Bob or Bob_Prime report themselves as equivocating. + assert!(api_bob.reported_equivocations.as_ref().unwrap().lock().is_empty()); + assert!(api_bob_prime.reported_equivocations.as_ref().unwrap().lock().is_empty()); + + // Verify Alice reports Bob/Bob_Prime equivocation. + let alice_reported_equivocations = api_alice.reported_equivocations.as_ref().unwrap().lock(); + assert_eq!(alice_reported_equivocations.len(), 1); + let equivocation_proof = alice_reported_equivocations.get(0).unwrap(); + assert_eq!(equivocation_proof.first.id, BeefyKeyring::Bob.public()); + assert_eq!(equivocation_proof.first.commitment.block_number, 1); +} diff --git a/client/beefy/src/worker.rs b/client/beefy/src/worker.rs index 6528aef12..8783997cc 100644 --- a/client/beefy/src/worker.rs +++ b/client/beefy/src/worker.rs @@ -23,16 +23,17 @@ use crate::{ }, error::Error, justification::BeefyVersionedFinalityProof, - keystore::BeefyKeystore, + keystore::{BeefyKeystore, BeefySignatureHasher}, metric_get, metric_inc, metric_set, metrics::VoterMetrics, round::{Rounds, VoteImportResult}, BeefyVoterLinks, LOG_TARGET, }; use beefy_primitives::{ + check_equivocation_proof, crypto::{AuthorityId, Signature}, - Commitment, ConsensusLog, PayloadProvider, ValidatorSet, VersionedFinalityProof, VoteMessage, - BEEFY_ENGINE_ID, + BeefyApi, Commitment, ConsensusLog, EquivocationProof, PayloadProvider, ValidatorSet, + VersionedFinalityProof, VoteMessage, BEEFY_ENGINE_ID, }; use codec::{Codec, Decode, Encode}; use futures::{stream::Fuse, FutureExt, StreamExt}; @@ -41,7 +42,7 @@ use sc_client_api::{Backend, FinalityNotification, FinalityNotifications, Header use sc_network_common::service::{NetworkEventStream, NetworkRequest}; use sc_network_gossip::GossipEngine; use sc_utils::notification::NotificationReceiver; -use sp_api::BlockId; +use sp_api::{BlockId, ProvideRuntimeApi}; use sp_arithmetic::traits::{AtLeast32Bit, Saturating}; use sp_consensus::SyncOracle; use sp_runtime::{ @@ -243,9 +244,10 @@ impl VoterOracle { } } -pub(crate) struct WorkerParams { +pub(crate) struct WorkerParams { pub backend: Arc, pub payload_provider: P, + pub runtime: Arc, pub network: N, pub key_store: BeefyKeystore, pub gossip_engine: GossipEngine, @@ -294,10 +296,11 @@ impl PersistedState { } /// A BEEFY worker plays the BEEFY protocol -pub(crate) struct BeefyWorker { +pub(crate) struct BeefyWorker { // utilities backend: Arc, payload_provider: P, + runtime: Arc, network: N, key_store: BeefyKeystore, @@ -327,11 +330,13 @@ pub(crate) struct BeefyWorker { persisted_state: PersistedState, } -impl BeefyWorker +impl BeefyWorker where B: Block + Codec, BE: Backend, P: PayloadProvider, + R: ProvideRuntimeApi, + R::Api: BeefyApi, N: NetworkEventStream + NetworkRequest + SyncOracle + Send + Sync + Clone + 'static, { /// Return a new BEEFY worker instance. @@ -340,10 +345,11 @@ where /// BEEFY pallet has been deployed on-chain. /// /// The BEEFY pallet is needed in order to keep track of the BEEFY authority set. - pub(crate) fn new(worker_params: WorkerParams) -> Self { + pub(crate) fn new(worker_params: WorkerParams) -> Self { let WorkerParams { backend, payload_provider, + runtime, key_store, network, gossip_engine, @@ -357,6 +363,7 @@ where BeefyWorker { backend, payload_provider, + runtime, network, key_store, gossip_engine, @@ -571,6 +578,7 @@ where // We created the `finality_proof` and know to be valid. // New state is persisted after finalization. self.finalize(finality_proof)?; + metric_inc!(self, beefy_good_votes_processed); }, VoteImportResult::Ok => { // Persist state after handling mandatory block vote. @@ -583,14 +591,15 @@ where crate::aux_schema::write_voter_state(&*self.backend, &self.persisted_state) .map_err(|e| Error::Backend(e.to_string()))?; } + metric_inc!(self, beefy_good_votes_processed); }, - VoteImportResult::Equivocation => { - // TODO: report returned `EquivocationProof` to chain through `pallet-beefy`. - () + VoteImportResult::Equivocation(proof) => { + metric_inc!(self, beefy_equivocation_votes); + self.report_equivocation(proof)?; }, - VoteImportResult::Invalid | VoteImportResult::Stale => (), + VoteImportResult::Invalid => metric_inc!(self, beefy_invalid_votes), + VoteImportResult::Stale => metric_inc!(self, beefy_stale_votes), }; - metric_inc!(self, beefy_successful_handled_votes); Ok(()) } @@ -928,6 +937,60 @@ where } } } + + /// Report the given equivocation to the BEEFY runtime module. This method + /// generates a session membership proof of the offender and then submits an + /// extrinsic to report the equivocation. In particular, the session membership + /// proof must be generated at the block at which the given set was active which + /// isn't necessarily the best block if there are pending authority set changes. + pub(crate) fn report_equivocation( + &self, + proof: EquivocationProof, AuthorityId, Signature>, + ) -> Result<(), Error> { + let rounds = + self.persisted_state.voting_oracle.active_rounds().ok_or(Error::UninitSession)?; + let (validators, validator_set_id) = (rounds.validators(), rounds.validator_set_id()); + let offender_id = proof.offender_id().clone(); + + if !check_equivocation_proof::<_, _, BeefySignatureHasher>(&proof) { + debug!(target: LOG_TARGET, "🥩 Skip report for bad equivocation {:?}", proof); + return Ok(()) + } else if let Some(local_id) = self.key_store.authority_id(validators) { + if offender_id == local_id { + debug!(target: LOG_TARGET, "🥩 Skip equivocation report for own equivocation"); + return Ok(()) + } + } + + let number = *proof.round_number(); + let runtime_api = self.runtime.runtime_api(); + // generate key ownership proof at that block + let key_owner_proof = match runtime_api + .generate_key_ownership_proof(&BlockId::Number(number), validator_set_id, offender_id) + .map_err(Error::RuntimeApi)? + { + Some(proof) => proof, + None => { + debug!( + target: LOG_TARGET, + "🥩 Equivocation offender not part of the authority set." + ); + return Ok(()) + }, + }; + + // submit equivocation report at **best** block + let best_block_hash = self.backend.blockchain().info().best_hash; + runtime_api + .submit_report_equivocation_unsigned_extrinsic( + &BlockId::Hash(best_block_hash), + proof, + key_owner_proof, + ) + .map_err(Error::RuntimeApi)?; + + Ok(()) + } } /// Scan the `header` digest log for a BEEFY validator set change. Return either the new @@ -993,7 +1056,8 @@ pub(crate) mod tests { BeefyRPCLinks, KnownPeers, }; use beefy_primitives::{ - keyring::Keyring, known_payloads, mmr::MmrRootProvider, Payload, SignedCommitment, + generate_equivocation_proof, known_payloads, known_payloads::MMR_ROOT_ID, + mmr::MmrRootProvider, Keyring, Payload, SignedCommitment, }; use futures::{future::poll_fn, task::Poll}; use parking_lot::Mutex; @@ -1041,6 +1105,7 @@ pub(crate) mod tests { Block, Backend, MmrRootProvider, + TestApi, Arc>, > { let keystore = create_beefy_keystore(*key); @@ -1091,6 +1156,7 @@ pub(crate) mod tests { let worker_params = crate::worker::WorkerParams { backend, payload_provider, + runtime: api, key_store: Some(keystore).into(), links, gossip_engine, @@ -1100,7 +1166,7 @@ pub(crate) mod tests { on_demand_justifications, persisted_state, }; - BeefyWorker::<_, _, _, _>::new(worker_params) + BeefyWorker::<_, _, _, _, _>::new(worker_params) } #[test] @@ -1546,4 +1612,65 @@ pub(crate) mod tests { assert_eq!(votes.next().unwrap().first().unwrap().commitment.block_number, 21); assert_eq!(votes.next().unwrap().first().unwrap().commitment.block_number, 22); } + + #[tokio::test] + async fn should_not_report_bad_old_or_self_equivocations() { + let block_num = 1; + let set_id = 1; + let keys = [Keyring::Alice]; + let validator_set = ValidatorSet::new(make_beefy_ids(&keys), set_id).unwrap(); + // Alice votes on good MMR roots, equivocations are allowed/expected + let mut api_alice = TestApi::with_validator_set(&validator_set); + api_alice.allow_equivocations(); + let api_alice = Arc::new(api_alice); + + let mut net = BeefyTestNet::new(1); + let mut worker = create_beefy_worker(&net.peer(0), &keys[0], 1, validator_set.clone()); + worker.runtime = api_alice.clone(); + + let payload1 = Payload::from_single_entry(MMR_ROOT_ID, vec![42]); + let payload2 = Payload::from_single_entry(MMR_ROOT_ID, vec![128]); + + // generate an equivocation proof, with Bob as perpetrator + let good_proof = generate_equivocation_proof( + (block_num, payload1.clone(), set_id, &Keyring::Bob), + (block_num, payload2.clone(), set_id, &Keyring::Bob), + ); + { + // expect voter (Alice) to successfully report it + assert_eq!(worker.report_equivocation(good_proof.clone()), Ok(())); + // verify Alice reports Bob equivocation to runtime + let reported = api_alice.reported_equivocations.as_ref().unwrap().lock(); + assert_eq!(reported.len(), 1); + assert_eq!(*reported.get(0).unwrap(), good_proof); + } + api_alice.reported_equivocations.as_ref().unwrap().lock().clear(); + + // now let's try with a bad proof + let mut bad_proof = good_proof.clone(); + bad_proof.first.id = Keyring::Charlie.public(); + // bad proofs are simply ignored + assert_eq!(worker.report_equivocation(bad_proof), Ok(())); + // verify nothing reported to runtime + assert!(api_alice.reported_equivocations.as_ref().unwrap().lock().is_empty()); + + // now let's try with old set it + let mut old_proof = good_proof.clone(); + old_proof.first.commitment.validator_set_id = 0; + old_proof.second.commitment.validator_set_id = 0; + // old proofs are simply ignored + assert_eq!(worker.report_equivocation(old_proof), Ok(())); + // verify nothing reported to runtime + assert!(api_alice.reported_equivocations.as_ref().unwrap().lock().is_empty()); + + // now let's try reporting a self-equivocation + let self_proof = generate_equivocation_proof( + (block_num, payload1.clone(), set_id, &Keyring::Alice), + (block_num, payload2.clone(), set_id, &Keyring::Alice), + ); + // equivocations done by 'self' are simply ignored (not reported) + assert_eq!(worker.report_equivocation(self_proof), Ok(())); + // verify nothing reported to runtime + assert!(api_alice.reported_equivocations.as_ref().unwrap().lock().is_empty()); + } } diff --git a/client/consensus/babe/src/lib.rs b/client/consensus/babe/src/lib.rs index 99b29986e..403792249 100644 --- a/client/consensus/babe/src/lib.rs +++ b/client/consensus/babe/src/lib.rs @@ -1086,7 +1086,7 @@ where .map_err(|e| Error::Client(e.into()))?; // generate a key ownership proof. we start by trying to generate the - // key owernship proof at the parent of the equivocating header, this + // key ownership proof at the parent of the equivocating header, this // will make sure that proof generation is successful since it happens // during the on-going session (i.e. session keys are available in the // state to be able to generate the proof). this might fail if the diff --git a/frame/beefy-mmr/src/lib.rs b/frame/beefy-mmr/src/lib.rs index e5506ecd0..eb030b71d 100644 --- a/frame/beefy-mmr/src/lib.rs +++ b/frame/beefy-mmr/src/lib.rs @@ -23,8 +23,8 @@ //! While both BEEFY and Merkle Mountain Range (MMR) can be used separately, //! these tools were designed to work together in unison. //! -//! The pallet provides a standardized MMR Leaf format that is can be used -//! to bridge BEEFY+MMR-based networks (both standalone and polkadot-like). +//! The pallet provides a standardized MMR Leaf format that can be used +//! to bridge BEEFY+MMR-based networks (both standalone and Polkadot-like). //! //! The MMR leaf contains: //! 1. Block number and parent block hash. diff --git a/frame/beefy-mmr/src/mock.rs b/frame/beefy-mmr/src/mock.rs index 2de71cd5b..de6fa1352 100644 --- a/frame/beefy-mmr/src/mock.rs +++ b/frame/beefy-mmr/src/mock.rs @@ -22,10 +22,10 @@ use codec::Encode; use frame_support::{ construct_runtime, parameter_types, sp_io::TestExternalities, - traits::{ConstU16, ConstU32, ConstU64, GenesisBuild}, + traits::{ConstU16, ConstU32, ConstU64, GenesisBuild, KeyOwnerProofSystem}, BasicExternalities, }; -use sp_core::{Hasher, H256}; +use sp_core::{crypto::KeyTypeId, Hasher, H256}; use sp_runtime::{ app_crypto::ecdsa::Public, impl_opaque_keys, @@ -124,8 +124,17 @@ impl pallet_mmr::Config for Test { impl pallet_beefy::Config for Test { type BeefyId = BeefyId; + type KeyOwnerProofSystem = (); + type KeyOwnerProof = + >::Proof; + type KeyOwnerIdentification = >::IdentificationTuple; + type HandleEquivocation = (); type MaxAuthorities = ConstU32<100>; type OnNewValidatorSet = BeefyMmr; + type WeightInfo = (); } parameter_types! { diff --git a/frame/beefy/Cargo.toml b/frame/beefy/Cargo.toml index 4db8c0568..b2d663537 100644 --- a/frame/beefy/Cargo.toml +++ b/frame/beefy/Cargo.toml @@ -15,11 +15,20 @@ serde = { version = "1.0.136", optional = true } beefy-primitives = { version = "4.0.0-dev", default-features = false, path = "../../primitives/beefy", package = "sp-beefy" } frame-support = { version = "4.0.0-dev", default-features = false, path = "../support" } frame-system = { version = "4.0.0-dev", default-features = false, path = "../system" } +pallet-authorship = { version = "4.0.0-dev", default-features = false, path = "../authorship" } pallet-session = { version = "4.0.0-dev", default-features = false, path = "../session" } sp-runtime = { version = "7.0.0", default-features = false, path = "../../primitives/runtime" } +sp-session = { version = "4.0.0-dev", default-features = false, path = "../../primitives/session" } +sp-staking = { version = "4.0.0-dev", default-features = false, path = "../../primitives/staking" } sp-std = { version = "5.0.0", default-features = false, path = "../../primitives/std" } [dev-dependencies] +frame-election-provider-support = { version = "4.0.0-dev", path = "../election-provider-support" } +pallet-balances = { version = "4.0.0-dev", path = "../balances" } +pallet-offences = { version = "4.0.0-dev", path = "../offences" } +pallet-staking = { version = "4.0.0-dev", path = "../staking" } +pallet-staking-reward-curve = { version = "4.0.0-dev", path = "../staking/reward-curve" } +pallet-timestamp = { version = "4.0.0-dev", path = "../timestamp" } sp-core = { version = "7.0.0", path = "../../primitives/core" } sp-io = { version = "7.0.0", path = "../../primitives/io" } sp-staking = { version = "4.0.0-dev", path = "../../primitives/staking" } @@ -31,10 +40,13 @@ std = [ "codec/std", "frame-support/std", "frame-system/std", + "pallet-authorship/std", "pallet-session/std", "scale-info/std", "serde", "sp-runtime/std", + "sp-session/std", + "sp-staking/std", "sp-std/std", ] try-runtime = ["frame-support/try-runtime"] diff --git a/frame/beefy/src/default_weights.rs b/frame/beefy/src/default_weights.rs new file mode 100644 index 000000000..0b642cb45 --- /dev/null +++ b/frame/beefy/src/default_weights.rs @@ -0,0 +1,54 @@ +// This file is part of Substrate. + +// Copyright (C) 2020-2023 Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +//! Default weights for the BEEFY Pallet +//! This file was not auto-generated. + +use frame_support::weights::{ + constants::{RocksDbWeight as DbWeight, WEIGHT_REF_TIME_PER_MICROS, WEIGHT_REF_TIME_PER_NANOS}, + Weight, +}; + +impl crate::WeightInfo for () { + fn report_equivocation(validator_count: u32) -> Weight { + // we take the validator set count from the membership proof to + // calculate the weight but we set a floor of 100 validators. + let validator_count = validator_count.max(100) as u64; + + // worst case we are considering is that the given offender is backed by 200 nominators + const MAX_NOMINATORS: u64 = 200; + + // checking membership proof + Weight::from_ref_time(35u64 * WEIGHT_REF_TIME_PER_MICROS) + .saturating_add( + Weight::from_ref_time(175u64 * WEIGHT_REF_TIME_PER_NANOS) + .saturating_mul(validator_count), + ) + .saturating_add(DbWeight::get().reads(5)) + // check equivocation proof + .saturating_add(Weight::from_ref_time(95u64 * WEIGHT_REF_TIME_PER_MICROS)) + // report offence + .saturating_add(Weight::from_ref_time(110u64 * WEIGHT_REF_TIME_PER_MICROS)) + .saturating_add(Weight::from_ref_time( + 25u64 * WEIGHT_REF_TIME_PER_MICROS * MAX_NOMINATORS, + )) + .saturating_add(DbWeight::get().reads(14 + 3 * MAX_NOMINATORS)) + .saturating_add(DbWeight::get().writes(10 + 3 * MAX_NOMINATORS)) + // fetching set id -> session index mappings + .saturating_add(DbWeight::get().reads(2)) + } +} diff --git a/frame/beefy/src/equivocation.rs b/frame/beefy/src/equivocation.rs new file mode 100644 index 000000000..ea6a9f912 --- /dev/null +++ b/frame/beefy/src/equivocation.rs @@ -0,0 +1,385 @@ +// This file is part of Substrate. + +// Copyright (C) 2017-2023 Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +//! An opt-in utility module for reporting equivocations. +//! +//! This module defines an offence type for BEEFY equivocations +//! and some utility traits to wire together: +//! - a key ownership proof system (e.g. to prove that a given authority was part of a session); +//! - a system for reporting offences; +//! - a system for signing and submitting transactions; +//! - a way to get the current block author; +//! +//! These can be used in an offchain context in order to submit equivocation +//! reporting extrinsics (from the client that's running the BEEFY protocol). +//! And in a runtime context, so that the BEEFY pallet can validate the +//! equivocation proofs in the extrinsic and report the offences. +//! +//! IMPORTANT: +//! When using this module for enabling equivocation reporting it is required +//! that the `ValidateUnsigned` for the BEEFY pallet is used in the runtime +//! definition. + +use sp_std::prelude::*; + +use beefy_primitives::{EquivocationProof, ValidatorSetId}; +use codec::{self as codec, Decode, Encode}; +use frame_support::{ + log, + traits::{Get, KeyOwnerProofSystem}, +}; +use frame_system::pallet_prelude::BlockNumberFor; +use sp_runtime::{ + transaction_validity::{ + InvalidTransaction, TransactionPriority, TransactionSource, TransactionValidity, + TransactionValidityError, ValidTransaction, + }, + DispatchResult, Perbill, RuntimeAppPublic, +}; +use sp_staking::{ + offence::{Kind, Offence, OffenceError, ReportOffence}, + SessionIndex, +}; + +use super::{Call, Config, Pallet, LOG_TARGET}; + +/// A trait with utility methods for handling equivocation reports in BEEFY. +/// The offence type is generic, and the trait provides, reporting an offence +/// triggered by a valid equivocation report, and also for creating and +/// submitting equivocation report extrinsics (useful only in offchain context). +pub trait HandleEquivocation { + /// The offence type used for reporting offences on valid equivocation reports. + type Offence: BeefyOffence, T::KeyOwnerIdentification>; + + /// The longevity, in blocks, that the equivocation report is valid for. When using the staking + /// pallet this should be equal to the bonding duration (in blocks, not eras). + type ReportLongevity: Get; + + /// Report an offence proved by the given reporters. + fn report_offence( + reporters: Vec, + offence: Self::Offence, + ) -> Result<(), OffenceError>; + + /// Returns true if all of the offenders at the given time slot have already been reported. + fn is_known_offence( + offenders: &[T::KeyOwnerIdentification], + time_slot: &>::TimeSlot, + ) -> bool; + + /// Create and dispatch an equivocation report extrinsic. + fn submit_unsigned_equivocation_report( + equivocation_proof: EquivocationProof< + BlockNumberFor, + T::BeefyId, + ::Signature, + >, + key_owner_proof: T::KeyOwnerProof, + ) -> DispatchResult; + + /// Fetch the current block author id, if defined. + fn block_author() -> Option; +} + +impl HandleEquivocation for () { + type Offence = BeefyEquivocationOffence, T::KeyOwnerIdentification>; + type ReportLongevity = (); + + fn report_offence( + _reporters: Vec, + _offence: BeefyEquivocationOffence, T::KeyOwnerIdentification>, + ) -> Result<(), OffenceError> { + Ok(()) + } + + fn is_known_offence( + _offenders: &[T::KeyOwnerIdentification], + _time_slot: &BeefyTimeSlot>, + ) -> bool { + true + } + + fn submit_unsigned_equivocation_report( + _equivocation_proof: EquivocationProof< + BlockNumberFor, + T::BeefyId, + ::Signature, + >, + _key_owner_proof: T::KeyOwnerProof, + ) -> DispatchResult { + Ok(()) + } + + fn block_author() -> Option { + None + } +} + +/// Generic equivocation handler. This type implements `HandleEquivocation` +/// using existing subsystems that are part of frame (type bounds described +/// below) and will dispatch to them directly, it's only purpose is to wire all +/// subsystems together. +pub struct EquivocationHandler> { + _phantom: sp_std::marker::PhantomData<(N, I, R, L, O)>, +} + +impl Default for EquivocationHandler { + fn default() -> Self { + Self { _phantom: Default::default() } + } +} + +impl HandleEquivocation + for EquivocationHandler, T::KeyOwnerIdentification, R, L, O> +where + // We use the authorship pallet to fetch the current block author and use + // `offchain::SendTransactionTypes` for unsigned extrinsic creation and + // submission. + T: Config + pallet_authorship::Config + frame_system::offchain::SendTransactionTypes>, + // A system for reporting offences after valid equivocation reports are + // processed. + R: ReportOffence, + // The longevity (in blocks) that the equivocation report is valid for. When using the staking + // pallet this should be the bonding duration. + L: Get, + // The offence type that should be used when reporting. + O: BeefyOffence, T::KeyOwnerIdentification>, +{ + type Offence = O; + type ReportLongevity = L; + + fn report_offence(reporters: Vec, offence: O) -> Result<(), OffenceError> { + R::report_offence(reporters, offence) + } + + fn is_known_offence(offenders: &[T::KeyOwnerIdentification], time_slot: &O::TimeSlot) -> bool { + R::is_known_offence(offenders, time_slot) + } + + fn submit_unsigned_equivocation_report( + equivocation_proof: EquivocationProof< + BlockNumberFor, + T::BeefyId, + ::Signature, + >, + key_owner_proof: T::KeyOwnerProof, + ) -> DispatchResult { + use frame_system::offchain::SubmitTransaction; + + let call = Call::report_equivocation_unsigned { + equivocation_proof: Box::new(equivocation_proof), + key_owner_proof, + }; + + match SubmitTransaction::>::submit_unsigned_transaction(call.into()) { + Ok(()) => log::info!(target: LOG_TARGET, "Submitted BEEFY equivocation report.",), + Err(e) => + log::error!(target: LOG_TARGET, "Error submitting equivocation report: {:?}", e,), + } + + Ok(()) + } + + fn block_author() -> Option { + >::author() + } +} + +/// A round number and set id which point on the time of an offence. +#[derive(Copy, Clone, PartialOrd, Ord, Eq, PartialEq, Encode, Decode)] +pub struct BeefyTimeSlot { + // The order of these matters for `derive(Ord)`. + /// BEEFY Set ID. + pub set_id: ValidatorSetId, + /// Round number. + pub round: N, +} + +/// Methods for the `ValidateUnsigned` implementation: +/// It restricts calls to `report_equivocation_unsigned` to local calls (i.e. extrinsics generated +/// on this node) or that already in a block. This guarantees that only block authors can include +/// unsigned equivocation reports. +impl Pallet { + pub fn validate_unsigned(source: TransactionSource, call: &Call) -> TransactionValidity { + if let Call::report_equivocation_unsigned { equivocation_proof, key_owner_proof } = call { + // discard equivocation report not coming from the local node + match source { + TransactionSource::Local | TransactionSource::InBlock => { /* allowed */ }, + _ => { + log::warn!( + target: LOG_TARGET, + "rejecting unsigned report equivocation transaction because it is not local/in-block." + ); + return InvalidTransaction::Call.into() + }, + } + + // check report staleness + is_known_offence::(equivocation_proof, key_owner_proof)?; + + let longevity = + >::ReportLongevity::get(); + + ValidTransaction::with_tag_prefix("BeefyEquivocation") + // We assign the maximum priority for any equivocation report. + .priority(TransactionPriority::MAX) + // Only one equivocation report for the same offender at the same slot. + .and_provides(( + equivocation_proof.offender_id().clone(), + equivocation_proof.set_id(), + *equivocation_proof.round_number(), + )) + .longevity(longevity) + // We don't propagate this. This can never be included on a remote node. + .propagate(false) + .build() + } else { + InvalidTransaction::Call.into() + } + } + + pub fn pre_dispatch(call: &Call) -> Result<(), TransactionValidityError> { + if let Call::report_equivocation_unsigned { equivocation_proof, key_owner_proof } = call { + is_known_offence::(equivocation_proof, key_owner_proof) + } else { + Err(InvalidTransaction::Call.into()) + } + } +} + +fn is_known_offence( + equivocation_proof: &EquivocationProof< + BlockNumberFor, + T::BeefyId, + ::Signature, + >, + key_owner_proof: &T::KeyOwnerProof, +) -> Result<(), TransactionValidityError> { + // check the membership proof to extract the offender's id, + // equivocation validity will be fully checked during the call. + let key = (beefy_primitives::KEY_TYPE, equivocation_proof.offender_id().clone()); + + let offender = T::KeyOwnerProofSystem::check_proof(key, key_owner_proof.clone()) + .ok_or(InvalidTransaction::BadProof)?; + + // check if the offence has already been reported, + // and if so then we can discard the report. + let time_slot = >::Offence::new_time_slot( + equivocation_proof.set_id(), + *equivocation_proof.round_number(), + ); + + let is_known_offence = T::HandleEquivocation::is_known_offence(&[offender], &time_slot); + + if is_known_offence { + Err(InvalidTransaction::Stale.into()) + } else { + Ok(()) + } +} + +/// A BEEFY equivocation offence report. +pub struct BeefyEquivocationOffence +where + N: Copy + Clone + PartialOrd + Ord + Eq + PartialEq + Encode + Decode, +{ + /// Time slot at which this incident happened. + pub time_slot: BeefyTimeSlot, + /// The session index in which the incident happened. + pub session_index: SessionIndex, + /// The size of the validator set at the time of the offence. + pub validator_set_count: u32, + /// The authority which produced this equivocation. + pub offender: FullIdentification, +} + +/// An interface for types that will be used as BEEFY offences and must also +/// implement the `Offence` trait. This trait provides a constructor that is +/// provided all available data during processing of BEEFY equivocations. +pub trait BeefyOffence: Offence +where + N: Copy + Clone + PartialOrd + Ord + Eq + PartialEq + Encode + Decode, +{ + /// Create a new BEEFY offence using the given equivocation details. + fn new( + session_index: SessionIndex, + validator_set_count: u32, + offender: FullIdentification, + set_id: ValidatorSetId, + round: N, + ) -> Self; + + /// Create a new BEEFY offence time slot. + fn new_time_slot(set_id: ValidatorSetId, round: N) -> Self::TimeSlot; +} + +impl BeefyOffence + for BeefyEquivocationOffence +where + N: Copy + Clone + PartialOrd + Ord + Eq + PartialEq + Encode + Decode, +{ + fn new( + session_index: SessionIndex, + validator_set_count: u32, + offender: FullIdentification, + set_id: ValidatorSetId, + round: N, + ) -> Self { + BeefyEquivocationOffence { + session_index, + validator_set_count, + offender, + time_slot: BeefyTimeSlot { set_id, round }, + } + } + + fn new_time_slot(set_id: ValidatorSetId, round: N) -> Self::TimeSlot { + BeefyTimeSlot { set_id, round } + } +} + +impl Offence + for BeefyEquivocationOffence +where + N: Copy + Clone + PartialOrd + Ord + Eq + PartialEq + Encode + Decode, +{ + const ID: Kind = *b"beefy:equivocati"; + type TimeSlot = BeefyTimeSlot; + + fn offenders(&self) -> Vec { + vec![self.offender.clone()] + } + + fn session_index(&self) -> SessionIndex { + self.session_index + } + + fn validator_set_count(&self) -> u32 { + self.validator_set_count + } + + fn time_slot(&self) -> Self::TimeSlot { + self.time_slot + } + + fn slash_fraction(&self, offenders_count: u32) -> Perbill { + // the formula is min((3k / n)^2, 1) + let x = Perbill::from_rational(3 * offenders_count, self.validator_set_count); + // _ ^ 2 + x.square() + } +} diff --git a/frame/beefy/src/lib.rs b/frame/beefy/src/lib.rs index 86c8763a5..d29440457 100644 --- a/frame/beefy/src/lib.rs +++ b/frame/beefy/src/lib.rs @@ -20,35 +20,48 @@ use codec::{Encode, MaxEncodedLen}; use frame_support::{ + dispatch::{DispatchResultWithPostInfo, Pays}, log, - traits::{Get, OneSessionHandler}, + pallet_prelude::*, + traits::{Get, KeyOwnerProofSystem, OneSessionHandler}, + weights::Weight, BoundedSlice, BoundedVec, Parameter, }; - +use frame_system::{ + ensure_none, ensure_signed, + pallet_prelude::{BlockNumberFor, OriginFor}, +}; use sp_runtime::{ generic::DigestItem, traits::{IsMember, Member}, - RuntimeAppPublic, + KeyTypeId, RuntimeAppPublic, }; +use sp_session::{GetSessionNumber, GetValidatorCount}; +use sp_staking::SessionIndex; use sp_std::prelude::*; use beefy_primitives::{ - AuthorityIndex, ConsensusLog, OnNewValidatorSet, ValidatorSet, BEEFY_ENGINE_ID, - GENESIS_AUTHORITY_SET_ID, + AuthorityIndex, BeefyAuthorityId, ConsensusLog, EquivocationProof, OnNewValidatorSet, + ValidatorSet, BEEFY_ENGINE_ID, GENESIS_AUTHORITY_SET_ID, }; +mod default_weights; +mod equivocation; #[cfg(test)] mod mock; - #[cfg(test)] mod tests; +pub use crate::equivocation::{ + BeefyEquivocationOffence, BeefyOffence, BeefyTimeSlot, EquivocationHandler, HandleEquivocation, +}; pub use pallet::*; +const LOG_TARGET: &str = "runtime::beefy"; + #[frame_support::pallet] pub mod pallet { use super::*; - use frame_support::pallet_prelude::*; use frame_system::pallet_prelude::BlockNumberFor; #[pallet::config] @@ -56,10 +69,35 @@ pub mod pallet { /// Authority identifier type type BeefyId: Member + Parameter - + RuntimeAppPublic + // todo: use custom signature hashing type instead of hardcoded `Keccak256` + + BeefyAuthorityId + MaybeSerializeDeserialize + MaxEncodedLen; + /// A system for proving ownership of keys, i.e. that a given key was part + /// of a validator set, needed for validating equivocation reports. + type KeyOwnerProofSystem: KeyOwnerProofSystem< + (KeyTypeId, Self::BeefyId), + Proof = Self::KeyOwnerProof, + IdentificationTuple = Self::KeyOwnerIdentification, + >; + + /// The proof of key ownership, used for validating equivocation reports + /// The proof must include the session index and validator count of the + /// session at which the equivocation occurred. + type KeyOwnerProof: Parameter + GetSessionNumber + GetValidatorCount; + + /// The identification of a key owner, used when reporting equivocations. + type KeyOwnerIdentification: Parameter; + + /// The equivocation handling subsystem, defines methods to report an + /// offence (after the equivocation has been validated) and for submitting a + /// transaction to report an equivocation (from an offchain context). + /// NOTE: when enabling equivocation handling (i.e. this type isn't set to + /// `()`) you must use this pallet's `ValidateUnsigned` in the runtime + /// definition. + type HandleEquivocation: HandleEquivocation; + /// The maximum number of authorities that can be added. type MaxAuthorities: Get; @@ -69,6 +107,9 @@ pub mod pallet { /// externally apart from having it in the storage. For instance you might cache a light /// weight MMR root over validators and make it available for Light Clients. type OnNewValidatorSet: OnNewValidatorSet<::BeefyId>; + + /// Weights for this pallet. + type WeightInfo: WeightInfo; } #[pallet::pallet] @@ -92,6 +133,15 @@ pub mod pallet { pub(super) type NextAuthorities = StorageValue<_, BoundedVec, ValueQuery>; + /// A mapping from BEEFY set ID to the index of the *most recent* session for which its + /// members were responsible. + /// + /// TWOX-NOTE: `ValidatorSetId` is not under user control. + #[pallet::storage] + #[pallet::getter(fn session_for_set)] + pub(super) type SetIdSession = + StorageMap<_, Twox64Concat, beefy_primitives::ValidatorSetId, SessionIndex>; + /// Block number where BEEFY consensus is enabled/started. /// If changing this, make sure `Self::ValidatorSetId` is also reset to /// `GENESIS_AUTHORITY_SET_ID` in the state of the new block number configured here. @@ -124,13 +174,90 @@ pub mod pallet { #[pallet::genesis_build] impl GenesisBuild for GenesisConfig { fn build(&self) { - Pallet::::initialize_authorities(&self.authorities) + Pallet::::initialize(&self.authorities) // we panic here as runtime maintainers can simply reconfigure genesis and restart // the chain easily .expect("Authorities vec too big"); >::put(&self.genesis_block); } } + + #[pallet::error] + pub enum Error { + /// A key ownership proof provided as part of an equivocation report is invalid. + InvalidKeyOwnershipProof, + /// An equivocation proof provided as part of an equivocation report is invalid. + InvalidEquivocationProof, + /// A given equivocation report is valid but already previously reported. + DuplicateOffenceReport, + } + + #[pallet::call] + impl Pallet { + /// Report voter equivocation/misbehavior. This method will verify the + /// equivocation proof and validate the given key ownership proof + /// against the extracted offender. If both are valid, the offence + /// will be reported. + #[pallet::call_index(0)] + #[pallet::weight(T::WeightInfo::report_equivocation(key_owner_proof.validator_count()))] + pub fn report_equivocation( + origin: OriginFor, + equivocation_proof: Box< + EquivocationProof< + BlockNumberFor, + T::BeefyId, + ::Signature, + >, + >, + key_owner_proof: T::KeyOwnerProof, + ) -> DispatchResultWithPostInfo { + let reporter = ensure_signed(origin)?; + + Self::do_report_equivocation(Some(reporter), *equivocation_proof, key_owner_proof) + } + + /// Report voter equivocation/misbehavior. This method will verify the + /// equivocation proof and validate the given key ownership proof + /// against the extracted offender. If both are valid, the offence + /// will be reported. + /// + /// This extrinsic must be called unsigned and it is expected that only + /// block authors will call it (validated in `ValidateUnsigned`), as such + /// if the block author is defined it will be defined as the equivocation + /// reporter. + #[pallet::call_index(1)] + #[pallet::weight(T::WeightInfo::report_equivocation(key_owner_proof.validator_count()))] + pub fn report_equivocation_unsigned( + origin: OriginFor, + equivocation_proof: Box< + EquivocationProof< + BlockNumberFor, + T::BeefyId, + ::Signature, + >, + >, + key_owner_proof: T::KeyOwnerProof, + ) -> DispatchResultWithPostInfo { + ensure_none(origin)?; + + Self::do_report_equivocation( + T::HandleEquivocation::block_author(), + *equivocation_proof, + key_owner_proof, + ) + } + } + + #[pallet::validate_unsigned] + impl ValidateUnsigned for Pallet { + type Call = Call; + fn pre_dispatch(call: &Self::Call) -> Result<(), TransactionValidityError> { + Self::pre_dispatch(call) + } + fn validate_unsigned(source: TransactionSource, call: &Self::Call) -> TransactionValidity { + Self::validate_unsigned(source, call) + } + } } impl Pallet { @@ -141,6 +268,24 @@ impl Pallet { ValidatorSet::::new(validators, id) } + /// Submits an extrinsic to report an equivocation. This method will create + /// an unsigned extrinsic with a call to `report_equivocation_unsigned` and + /// will push the transaction to the pool. Only useful in an offchain context. + pub fn submit_unsigned_equivocation_report( + equivocation_proof: EquivocationProof< + BlockNumberFor, + T::BeefyId, + ::Signature, + >, + key_owner_proof: T::KeyOwnerProof, + ) -> Option<()> { + T::HandleEquivocation::submit_unsigned_equivocation_report( + equivocation_proof, + key_owner_proof, + ) + .ok() + } + fn change_authorities( new: BoundedVec, queued: BoundedVec, @@ -169,7 +314,7 @@ impl Pallet { } } - fn initialize_authorities(authorities: &Vec) -> Result<(), ()> { + fn initialize(authorities: &Vec) -> Result<(), ()> { if authorities.is_empty() { return Ok(()) } @@ -199,15 +344,80 @@ impl Pallet { ); } } + + // NOTE: initialize first session of first set. this is necessary for + // the genesis set and session since we only update the set -> session + // mapping whenever a new session starts, i.e. through `on_new_session`. + SetIdSession::::insert(0, 0); + Ok(()) } + + fn do_report_equivocation( + reporter: Option, + equivocation_proof: EquivocationProof< + BlockNumberFor, + T::BeefyId, + ::Signature, + >, + key_owner_proof: T::KeyOwnerProof, + ) -> DispatchResultWithPostInfo { + // We check the equivocation within the context of its set id (and + // associated session) and round. We also need to know the validator + // set count at the time of the offence since it is required to calculate + // the slash amount. + let set_id = equivocation_proof.set_id(); + let round = *equivocation_proof.round_number(); + let offender_id = equivocation_proof.offender_id().clone(); + let session_index = key_owner_proof.session(); + let validator_count = key_owner_proof.validator_count(); + + // validate the key ownership proof extracting the id of the offender. + let offender = T::KeyOwnerProofSystem::check_proof( + (beefy_primitives::KEY_TYPE, offender_id), + key_owner_proof, + ) + .ok_or(Error::::InvalidKeyOwnershipProof)?; + + // validate equivocation proof (check votes are different and signatures are valid). + if !beefy_primitives::check_equivocation_proof(&equivocation_proof) { + return Err(Error::::InvalidEquivocationProof.into()) + } + + // check that the session id for the membership proof is within the + // bounds of the set id reported in the equivocation. + let set_id_session_index = + Self::session_for_set(set_id).ok_or(Error::::InvalidEquivocationProof)?; + if session_index != set_id_session_index { + return Err(Error::::InvalidEquivocationProof.into()) + } + + // report to the offences module rewarding the sender. + T::HandleEquivocation::report_offence( + reporter.into_iter().collect(), + >::Offence::new( + session_index, + validator_count, + offender, + set_id, + round, + ), + ) + .map_err(|_| Error::::DuplicateOffenceReport)?; + + // waive the fee since the report is valid and beneficial + Ok(Pays::No.into()) + } } impl sp_runtime::BoundToRuntimeAppPublic for Pallet { type Public = T::BeefyId; } -impl OneSessionHandler for Pallet { +impl OneSessionHandler for Pallet +where + T: pallet_session::Config, +{ type Key = T::BeefyId; fn on_genesis_session<'a, I: 'a>(validators: I) @@ -217,7 +427,7 @@ impl OneSessionHandler for Pallet { let authorities = validators.map(|(_, k)| k).collect::>(); // we panic here as runtime maintainers can simply reconfigure genesis and restart the // chain easily - Self::initialize_authorities(&authorities).expect("Authorities vec too big"); + Self::initialize(&authorities).expect("Authorities vec too big"); } fn on_new_session<'a, I: 'a>(_changed: bool, validators: I, queued_validators: I) @@ -227,9 +437,10 @@ impl OneSessionHandler for Pallet { let next_authorities = validators.map(|(_, k)| k).collect::>(); if next_authorities.len() as u32 > T::MaxAuthorities::get() { log::error!( - target: "runtime::beefy", + target: LOG_TARGET, "authorities list {:?} truncated to length {}", - next_authorities, T::MaxAuthorities::get(), + next_authorities, + T::MaxAuthorities::get(), ); } let bounded_next_authorities = @@ -238,9 +449,10 @@ impl OneSessionHandler for Pallet { let next_queued_authorities = queued_validators.map(|(_, k)| k).collect::>(); if next_queued_authorities.len() as u32 > T::MaxAuthorities::get() { log::error!( - target: "runtime::beefy", + target: LOG_TARGET, "queued authorities list {:?} truncated to length {}", - next_queued_authorities, T::MaxAuthorities::get(), + next_queued_authorities, + T::MaxAuthorities::get(), ); } let bounded_next_queued_authorities = @@ -249,6 +461,10 @@ impl OneSessionHandler for Pallet { // Always issue a change on each `session`, even if validator set hasn't changed. // We want to have at least one BEEFY mandatory block per session. Self::change_authorities(bounded_next_authorities, bounded_next_queued_authorities); + + // Update the mapping for the new set id that corresponds to the latest session (i.e. now). + let session_index = >::current_index(); + SetIdSession::::insert(Self::validator_set_id(), &session_index); } fn on_disabled(i: u32) { @@ -266,3 +482,7 @@ impl IsMember for Pallet { Self::authorities().iter().any(|id| id == authority_id) } } + +pub trait WeightInfo { + fn report_equivocation(validator_count: u32) -> Weight; +} diff --git a/frame/beefy/src/mock.rs b/frame/beefy/src/mock.rs index ad3a67233..8d8b83195 100644 --- a/frame/beefy/src/mock.rs +++ b/frame/beefy/src/mock.rs @@ -17,24 +17,33 @@ use std::vec; +use frame_election_provider_support::{onchain, SequentialPhragmen}; use frame_support::{ construct_runtime, parameter_types, sp_io::TestExternalities, - traits::{ConstU16, ConstU32, ConstU64, GenesisBuild}, + traits::{ + ConstU16, ConstU32, ConstU64, GenesisBuild, KeyOwnerProofSystem, OnFinalize, OnInitialize, + }, BasicExternalities, }; -use sp_core::H256; +use pallet_session::historical as pallet_session_historical; +use sp_core::{crypto::KeyTypeId, ConstU128, H256}; use sp_runtime::{ app_crypto::ecdsa::Public, + curve::PiecewiseLinear, impl_opaque_keys, - testing::Header, - traits::{BlakeTwo256, ConvertInto, IdentityLookup, OpaqueKeys}, + testing::{Header, TestXt}, + traits::{BlakeTwo256, IdentityLookup, OpaqueKeys}, Perbill, }; +use sp_staking::{EraIndex, SessionIndex}; use crate as pallet_beefy; -pub use beefy_primitives::{crypto::AuthorityId as BeefyId, ConsensusLog, BEEFY_ENGINE_ID}; +pub use beefy_primitives::{ + crypto::{AuthorityId as BeefyId, AuthoritySignature as BeefySignature}, + ConsensusLog, EquivocationProof, BEEFY_ENGINE_ID, +}; impl_opaque_keys! { pub struct MockSessionKeys { @@ -51,9 +60,15 @@ construct_runtime!( NodeBlock = Block, UncheckedExtrinsic = UncheckedExtrinsic, { - System: frame_system::{Pallet, Call, Config, Storage, Event}, - Beefy: pallet_beefy::{Pallet, Config, Storage}, - Session: pallet_session::{Pallet, Call, Storage, Event, Config}, + System: frame_system, + Authorship: pallet_authorship, + Timestamp: pallet_timestamp, + Balances: pallet_balances, + Beefy: pallet_beefy, + Staking: pallet_staking, + Session: pallet_session, + Offences: pallet_offences, + Historical: pallet_session_historical, } ); @@ -75,7 +90,7 @@ impl frame_system::Config for Test { type BlockHashCount = ConstU64<250>; type Version = (); type PalletInfo = PalletInfo; - type AccountData = (); + type AccountData = pallet_balances::AccountData; type OnNewAccount = (); type OnKilledAccount = (); type SystemWeightInfo = (); @@ -84,10 +99,34 @@ impl frame_system::Config for Test { type MaxConsumers = ConstU32<16>; } +impl frame_system::offchain::SendTransactionTypes for Test +where + RuntimeCall: From, +{ + type OverarchingCall = RuntimeCall; + type Extrinsic = TestXt; +} + +parameter_types! { + pub const Period: u64 = 1; + pub const ReportLongevity: u64 = + BondingDuration::get() as u64 * SessionsPerEra::get() as u64 * Period::get(); +} + impl pallet_beefy::Config for Test { type BeefyId = BeefyId; + type KeyOwnerProofSystem = Historical; + type KeyOwnerProof = + >::Proof; + type KeyOwnerIdentification = >::IdentificationTuple; + type HandleEquivocation = + super::EquivocationHandler; type MaxAuthorities = ConstU32<100>; type OnNewValidatorSet = (); + type WeightInfo = (); } parameter_types! { @@ -97,29 +136,107 @@ parameter_types! { impl pallet_session::Config for Test { type RuntimeEvent = RuntimeEvent; type ValidatorId = u64; - type ValidatorIdOf = ConvertInto; + type ValidatorIdOf = pallet_staking::StashOf; type ShouldEndSession = pallet_session::PeriodicSessions, ConstU64<0>>; type NextSessionRotation = pallet_session::PeriodicSessions, ConstU64<0>>; - type SessionManager = MockSessionManager; + type SessionManager = pallet_session::historical::NoteHistoricalRoot; type SessionHandler = ::KeyTypeIdProviders; type Keys = MockSessionKeys; type WeightInfo = (); } -pub struct MockSessionManager; +impl pallet_session::historical::Config for Test { + type FullIdentification = pallet_staking::Exposure; + type FullIdentificationOf = pallet_staking::ExposureOf; +} -impl pallet_session::SessionManager for MockSessionManager { - fn end_session(_: sp_staking::SessionIndex) {} - fn start_session(_: sp_staking::SessionIndex) {} - fn new_session(idx: sp_staking::SessionIndex) -> Option> { - if idx == 0 || idx == 1 { - Some(vec![1, 2]) - } else if idx == 2 { - Some(vec![3, 4]) - } else { - None - } - } +impl pallet_authorship::Config for Test { + type FindAuthor = (); + type EventHandler = (); +} + +impl pallet_balances::Config for Test { + type MaxLocks = (); + type MaxReserves = (); + type ReserveIdentifier = [u8; 8]; + type Balance = u128; + type DustRemoval = (); + type RuntimeEvent = RuntimeEvent; + type ExistentialDeposit = ConstU128<1>; + type AccountStore = System; + type WeightInfo = (); +} + +impl pallet_timestamp::Config for Test { + type Moment = u64; + type OnTimestampSet = (); + type MinimumPeriod = ConstU64<3>; + type WeightInfo = (); +} + +pallet_staking_reward_curve::build! { + const REWARD_CURVE: PiecewiseLinear<'static> = curve!( + min_inflation: 0_025_000u64, + max_inflation: 0_100_000, + ideal_stake: 0_500_000, + falloff: 0_050_000, + max_piece_count: 40, + test_precision: 0_005_000, + ); +} + +parameter_types! { + pub const SessionsPerEra: SessionIndex = 3; + pub const BondingDuration: EraIndex = 3; + pub const RewardCurve: &'static PiecewiseLinear<'static> = &REWARD_CURVE; + pub const OffendingValidatorsThreshold: Perbill = Perbill::from_percent(17); +} + +pub struct OnChainSeqPhragmen; +impl onchain::Config for OnChainSeqPhragmen { + type System = Test; + type Solver = SequentialPhragmen; + type DataProvider = Staking; + type WeightInfo = (); + type MaxWinners = ConstU32<100>; + type VotersBound = ConstU32<{ u32::MAX }>; + type TargetsBound = ConstU32<{ u32::MAX }>; +} + +impl pallet_staking::Config for Test { + type MaxNominations = ConstU32<16>; + type RewardRemainder = (); + type CurrencyToVote = frame_support::traits::SaturatingCurrencyToVote; + type RuntimeEvent = RuntimeEvent; + type Currency = Balances; + type CurrencyBalance = ::Balance; + type Slash = (); + type Reward = (); + type SessionsPerEra = SessionsPerEra; + type BondingDuration = BondingDuration; + type SlashDeferDuration = (); + type AdminOrigin = frame_system::EnsureRoot; + type SessionInterface = Self; + type UnixTime = pallet_timestamp::Pallet; + type EraPayout = pallet_staking::ConvertCurve; + type MaxNominatorRewardedPerValidator = ConstU32<64>; + type OffendingValidatorsThreshold = OffendingValidatorsThreshold; + type NextNewSession = Session; + type ElectionProvider = onchain::OnChainExecution; + type GenesisElectionProvider = Self::ElectionProvider; + type VoterList = pallet_staking::UseNominatorsAndValidatorsMap; + type TargetList = pallet_staking::UseValidatorsMap; + type MaxUnlockingChunks = ConstU32<32>; + type HistoryDepth = ConstU32<84>; + type OnStakerSlash = (); + type BenchmarkingConfig = pallet_staking::TestBenchmarkingConfig; + type WeightInfo = (); +} + +impl pallet_offences::Config for Test { + type RuntimeEvent = RuntimeEvent; + type IdentificationTuple = pallet_session::historical::IdentificationTuple; + type OnOffenceHandler = Staking; } // Note, that we can't use `UintAuthorityId` here. Reason is that the implementation @@ -134,20 +251,27 @@ pub fn mock_beefy_id(id: u8) -> BeefyId { BeefyId::from(pk) } -pub fn mock_authorities(vec: Vec) -> Vec<(u64, BeefyId)> { - vec.into_iter().map(|id| ((id as u64), mock_beefy_id(id))).collect() +pub fn mock_authorities(vec: Vec) -> Vec { + vec.into_iter().map(|id| mock_beefy_id(id)).collect() } pub fn new_test_ext(ids: Vec) -> TestExternalities { new_test_ext_raw_authorities(mock_authorities(ids)) } -pub fn new_test_ext_raw_authorities(authorities: Vec<(u64, BeefyId)>) -> TestExternalities { +pub fn new_test_ext_raw_authorities(authorities: Vec) -> TestExternalities { let mut t = frame_system::GenesisConfig::default().build_storage::().unwrap(); + let balances: Vec<_> = (0..authorities.len()).map(|i| (i as u64, 10_000_000)).collect(); + + pallet_balances::GenesisConfig:: { balances } + .assimilate_storage(&mut t) + .unwrap(); + let session_keys: Vec<_> = authorities .iter() - .map(|id| (id.0 as u64, id.0 as u64, MockSessionKeys { dummy: id.1.clone() })) + .enumerate() + .map(|(i, k)| (i as u64, i as u64, MockSessionKeys { dummy: k.clone() })) .collect(); BasicExternalities::execute_with_storage(&mut t, || { @@ -160,5 +284,56 @@ pub fn new_test_ext_raw_authorities(authorities: Vec<(u64, BeefyId)>) -> TestExt .assimilate_storage(&mut t) .unwrap(); + // controllers are the index + 1000 + let stakers: Vec<_> = (0..authorities.len()) + .map(|i| { + (i as u64, i as u64 + 1000, 10_000, pallet_staking::StakerStatus::::Validator) + }) + .collect(); + + let staking_config = pallet_staking::GenesisConfig:: { + stakers, + validator_count: 2, + force_era: pallet_staking::Forcing::ForceNew, + minimum_validator_count: 0, + invulnerables: vec![], + ..Default::default() + }; + + staking_config.assimilate_storage(&mut t).unwrap(); + t.into() } + +pub fn start_session(session_index: SessionIndex) { + for i in Session::current_index()..session_index { + System::on_finalize(System::block_number()); + Session::on_finalize(System::block_number()); + Staking::on_finalize(System::block_number()); + Beefy::on_finalize(System::block_number()); + + let parent_hash = if System::block_number() > 1 { + let hdr = System::finalize(); + hdr.hash() + } else { + System::parent_hash() + }; + + System::reset_events(); + System::initialize(&(i as u64 + 1), &parent_hash, &Default::default()); + System::set_block_number((i + 1).into()); + Timestamp::set_timestamp(System::block_number() * 6000); + + System::on_initialize(System::block_number()); + Session::on_initialize(System::block_number()); + Staking::on_initialize(System::block_number()); + Beefy::on_initialize(System::block_number()); + } + + assert_eq!(Session::current_index(), session_index); +} + +pub fn start_era(era_index: EraIndex) { + start_session((era_index * 3).into()); + assert_eq!(Staking::current_era(), Some(era_index)); +} diff --git a/frame/beefy/src/tests.rs b/frame/beefy/src/tests.rs index 4136b0c1f..a92f90b61 100644 --- a/frame/beefy/src/tests.rs +++ b/frame/beefy/src/tests.rs @@ -17,14 +17,21 @@ use std::vec; -use beefy_primitives::ValidatorSet; +use beefy_primitives::{ + check_equivocation_proof, generate_equivocation_proof, known_payloads::MMR_ROOT_ID, + Keyring as BeefyKeyring, Payload, ValidatorSet, +}; use codec::Encode; use sp_runtime::DigestItem; -use frame_support::traits::OnInitialize; +use frame_support::{ + assert_err, assert_ok, + dispatch::{GetDispatchInfo, Pays}, + traits::{Currency, KeyOwnerProofSystem, OnInitialize}, +}; -use crate::mock::*; +use crate::{mock::*, Call, Config, Error, Weight, WeightInfo}; fn init_block(block: u64) { System::set_block_number(block); @@ -37,12 +44,13 @@ pub fn beefy_log(log: ConsensusLog) -> DigestItem { #[test] fn genesis_session_initializes_authorities() { - let want = vec![mock_beefy_id(1), mock_beefy_id(2), mock_beefy_id(3), mock_beefy_id(4)]; + let authorities = mock_authorities(vec![1, 2, 3, 4]); + let want = authorities.clone(); - new_test_ext(vec![1, 2, 3, 4]).execute_with(|| { + new_test_ext_raw_authorities(authorities).execute_with(|| { let authorities = Beefy::authorities(); - assert!(authorities.len() == 2); + assert_eq!(authorities.len(), 4); assert_eq!(want[0], authorities[0]); assert_eq!(want[1], authorities[1]); @@ -50,7 +58,7 @@ fn genesis_session_initializes_authorities() { let next_authorities = Beefy::next_authorities(); - assert!(next_authorities.len() == 2); + assert_eq!(next_authorities.len(), 4); assert_eq!(want[0], next_authorities[0]); assert_eq!(want[1], next_authorities[1]); }); @@ -58,6 +66,9 @@ fn genesis_session_initializes_authorities() { #[test] fn session_change_updates_authorities() { + let authorities = mock_authorities(vec![1, 2, 3, 4]); + let want_validators = authorities.clone(); + new_test_ext(vec![1, 2, 3, 4]).execute_with(|| { assert!(0 == Beefy::validator_set_id()); @@ -66,7 +77,7 @@ fn session_change_updates_authorities() { assert!(1 == Beefy::validator_set_id()); let want = beefy_log(ConsensusLog::AuthoritiesChange( - ValidatorSet::new(vec![mock_beefy_id(1), mock_beefy_id(2)], 1).unwrap(), + ValidatorSet::new(want_validators, 1).unwrap(), )); let log = System::digest().logs[0].clone(); @@ -77,7 +88,7 @@ fn session_change_updates_authorities() { assert!(2 == Beefy::validator_set_id()); let want = beefy_log(ConsensusLog::AuthoritiesChange( - ValidatorSet::new(vec![mock_beefy_id(3), mock_beefy_id(4)], 2).unwrap(), + ValidatorSet::new(vec![mock_beefy_id(2), mock_beefy_id(4)], 2).unwrap(), )); let log = System::digest().logs[1].clone(); @@ -92,16 +103,18 @@ fn session_change_updates_next_authorities() { new_test_ext(vec![1, 2, 3, 4]).execute_with(|| { let next_authorities = Beefy::next_authorities(); - assert!(next_authorities.len() == 2); + assert_eq!(next_authorities.len(), 4); assert_eq!(want[0], next_authorities[0]); assert_eq!(want[1], next_authorities[1]); + assert_eq!(want[2], next_authorities[2]); + assert_eq!(want[3], next_authorities[3]); init_block(1); let next_authorities = Beefy::next_authorities(); - assert!(next_authorities.len() == 2); - assert_eq!(want[2], next_authorities[0]); + assert_eq!(next_authorities.len(), 2); + assert_eq!(want[1], next_authorities[0]); assert_eq!(want[3], next_authorities[1]); }); } @@ -126,6 +139,10 @@ fn validator_set_updates_work() { new_test_ext(vec![1, 2, 3, 4]).execute_with(|| { let vs = Beefy::validator_set().unwrap(); assert_eq!(vs.id(), 0u64); + assert_eq!(want[0], vs.validators()[0]); + assert_eq!(want[1], vs.validators()[1]); + assert_eq!(want[2], vs.validators()[2]); + assert_eq!(want[3], vs.validators()[3]); init_block(1); @@ -140,7 +157,611 @@ fn validator_set_updates_work() { let vs = Beefy::validator_set().unwrap(); assert_eq!(vs.id(), 2u64); - assert_eq!(want[2], vs.validators()[0]); + assert_eq!(want[1], vs.validators()[0]); assert_eq!(want[3], vs.validators()[1]); }); } + +/// Returns a list with 3 authorities with known keys: +/// Alice, Bob and Charlie. +pub fn test_authorities() -> Vec { + let authorities = vec![BeefyKeyring::Alice, BeefyKeyring::Bob, BeefyKeyring::Charlie]; + authorities.into_iter().map(|id| id.public()).collect() +} + +#[test] +fn should_sign_and_verify() { + use sp_runtime::traits::Keccak256; + + let set_id = 3; + let payload1 = Payload::from_single_entry(MMR_ROOT_ID, vec![42]); + let payload2 = Payload::from_single_entry(MMR_ROOT_ID, vec![128]); + + // generate an equivocation proof, with two votes in the same round for + // same payload signed by the same key + let equivocation_proof = generate_equivocation_proof( + (1, payload1.clone(), set_id, &BeefyKeyring::Bob), + (1, payload1.clone(), set_id, &BeefyKeyring::Bob), + ); + // expect invalid equivocation proof + assert!(!check_equivocation_proof::<_, _, Keccak256>(&equivocation_proof)); + + // generate an equivocation proof, with two votes in different rounds for + // different payloads signed by the same key + let equivocation_proof = generate_equivocation_proof( + (1, payload1.clone(), set_id, &BeefyKeyring::Bob), + (2, payload2.clone(), set_id, &BeefyKeyring::Bob), + ); + // expect invalid equivocation proof + assert!(!check_equivocation_proof::<_, _, Keccak256>(&equivocation_proof)); + + // generate an equivocation proof, with two votes by different authorities + let equivocation_proof = generate_equivocation_proof( + (1, payload1.clone(), set_id, &BeefyKeyring::Alice), + (1, payload2.clone(), set_id, &BeefyKeyring::Bob), + ); + // expect invalid equivocation proof + assert!(!check_equivocation_proof::<_, _, Keccak256>(&equivocation_proof)); + + // generate an equivocation proof, with two votes in different set ids + let equivocation_proof = generate_equivocation_proof( + (1, payload1.clone(), set_id, &BeefyKeyring::Bob), + (1, payload2.clone(), set_id + 1, &BeefyKeyring::Bob), + ); + // expect invalid equivocation proof + assert!(!check_equivocation_proof::<_, _, Keccak256>(&equivocation_proof)); + + // generate an equivocation proof, with two votes in the same round for + // different payloads signed by the same key + let payload2 = Payload::from_single_entry(MMR_ROOT_ID, vec![128]); + let equivocation_proof = generate_equivocation_proof( + (1, payload1, set_id, &BeefyKeyring::Bob), + (1, payload2, set_id, &BeefyKeyring::Bob), + ); + // expect valid equivocation proof + assert!(check_equivocation_proof::<_, _, Keccak256>(&equivocation_proof)); +} + +#[test] +fn report_equivocation_current_set_works() { + let authorities = test_authorities(); + + new_test_ext_raw_authorities(authorities).execute_with(|| { + assert_eq!(Staking::current_era(), Some(0)); + assert_eq!(Session::current_index(), 0); + + start_era(1); + + let block_num = System::block_number(); + let validator_set = Beefy::validator_set().unwrap(); + let authorities = validator_set.validators(); + let set_id = validator_set.id(); + let validators = Session::validators(); + + // make sure that all validators have the same balance + for validator in &validators { + assert_eq!(Balances::total_balance(validator), 10_000_000); + assert_eq!(Staking::slashable_balance_of(validator), 10_000); + + assert_eq!( + Staking::eras_stakers(1, validator), + pallet_staking::Exposure { total: 10_000, own: 10_000, others: vec![] }, + ); + } + + assert_eq!(authorities.len(), 2); + let equivocation_authority_index = 1; + let equivocation_key = &authorities[equivocation_authority_index]; + let equivocation_keyring = BeefyKeyring::from_public(equivocation_key).unwrap(); + + let payload1 = Payload::from_single_entry(MMR_ROOT_ID, vec![42]); + let payload2 = Payload::from_single_entry(MMR_ROOT_ID, vec![128]); + // generate an equivocation proof, with two votes in the same round for + // different payloads signed by the same key + let equivocation_proof = generate_equivocation_proof( + (block_num, payload1, set_id, &equivocation_keyring), + (block_num, payload2, set_id, &equivocation_keyring), + ); + + // create the key ownership proof + let key_owner_proof = + Historical::prove((beefy_primitives::KEY_TYPE, &equivocation_key)).unwrap(); + + // report the equivocation and the tx should be dispatched successfully + assert_ok!(Beefy::report_equivocation_unsigned( + RuntimeOrigin::none(), + Box::new(equivocation_proof), + key_owner_proof, + ),); + + start_era(2); + + // check that the balance of 0-th validator is slashed 100%. + let equivocation_validator_id = validators[equivocation_authority_index]; + + assert_eq!(Balances::total_balance(&equivocation_validator_id), 10_000_000 - 10_000); + assert_eq!(Staking::slashable_balance_of(&equivocation_validator_id), 0); + assert_eq!( + Staking::eras_stakers(2, equivocation_validator_id), + pallet_staking::Exposure { total: 0, own: 0, others: vec![] }, + ); + + // check that the balances of all other validators are left intact. + for validator in &validators { + if *validator == equivocation_validator_id { + continue + } + + assert_eq!(Balances::total_balance(validator), 10_000_000); + assert_eq!(Staking::slashable_balance_of(validator), 10_000); + + assert_eq!( + Staking::eras_stakers(2, validator), + pallet_staking::Exposure { total: 10_000, own: 10_000, others: vec![] }, + ); + } + }); +} + +#[test] +fn report_equivocation_old_set_works() { + let authorities = test_authorities(); + + new_test_ext_raw_authorities(authorities).execute_with(|| { + start_era(1); + + let block_num = System::block_number(); + let validator_set = Beefy::validator_set().unwrap(); + let authorities = validator_set.validators(); + let validators = Session::validators(); + let old_set_id = validator_set.id(); + + assert_eq!(authorities.len(), 2); + let equivocation_authority_index = 0; + let equivocation_key = &authorities[equivocation_authority_index]; + + // create the key ownership proof in the "old" set + let key_owner_proof = + Historical::prove((beefy_primitives::KEY_TYPE, &equivocation_key)).unwrap(); + + start_era(2); + + // make sure that all authorities have the same balance + for validator in &validators { + assert_eq!(Balances::total_balance(validator), 10_000_000); + assert_eq!(Staking::slashable_balance_of(validator), 10_000); + + assert_eq!( + Staking::eras_stakers(2, validator), + pallet_staking::Exposure { total: 10_000, own: 10_000, others: vec![] }, + ); + } + + let validator_set = Beefy::validator_set().unwrap(); + let new_set_id = validator_set.id(); + assert_eq!(old_set_id + 3, new_set_id); + + let equivocation_keyring = BeefyKeyring::from_public(equivocation_key).unwrap(); + + let payload1 = Payload::from_single_entry(MMR_ROOT_ID, vec![42]); + let payload2 = Payload::from_single_entry(MMR_ROOT_ID, vec![128]); + // generate an equivocation proof for the old set, + let equivocation_proof = generate_equivocation_proof( + (block_num, payload1, old_set_id, &equivocation_keyring), + (block_num, payload2, old_set_id, &equivocation_keyring), + ); + + // report the equivocation and the tx should be dispatched successfully + assert_ok!(Beefy::report_equivocation_unsigned( + RuntimeOrigin::none(), + Box::new(equivocation_proof), + key_owner_proof, + ),); + + start_era(3); + + // check that the balance of 0-th validator is slashed 100%. + let equivocation_validator_id = validators[equivocation_authority_index]; + + assert_eq!(Balances::total_balance(&equivocation_validator_id), 10_000_000 - 10_000); + assert_eq!(Staking::slashable_balance_of(&equivocation_validator_id), 0); + assert_eq!( + Staking::eras_stakers(3, equivocation_validator_id), + pallet_staking::Exposure { total: 0, own: 0, others: vec![] }, + ); + + // check that the balances of all other validators are left intact. + for validator in &validators { + if *validator == equivocation_validator_id { + continue + } + + assert_eq!(Balances::total_balance(validator), 10_000_000); + assert_eq!(Staking::slashable_balance_of(validator), 10_000); + + assert_eq!( + Staking::eras_stakers(3, validator), + pallet_staking::Exposure { total: 10_000, own: 10_000, others: vec![] }, + ); + } + }); +} + +#[test] +fn report_equivocation_invalid_set_id() { + let authorities = test_authorities(); + + new_test_ext_raw_authorities(authorities).execute_with(|| { + start_era(1); + + let block_num = System::block_number(); + let validator_set = Beefy::validator_set().unwrap(); + let authorities = validator_set.validators(); + let set_id = validator_set.id(); + + let equivocation_authority_index = 0; + let equivocation_key = &authorities[equivocation_authority_index]; + let equivocation_keyring = BeefyKeyring::from_public(equivocation_key).unwrap(); + + let key_owner_proof = + Historical::prove((beefy_primitives::KEY_TYPE, &equivocation_key)).unwrap(); + + let payload1 = Payload::from_single_entry(MMR_ROOT_ID, vec![42]); + let payload2 = Payload::from_single_entry(MMR_ROOT_ID, vec![128]); + // generate an equivocation for a future set + let equivocation_proof = generate_equivocation_proof( + (block_num, payload1, set_id + 1, &equivocation_keyring), + (block_num, payload2, set_id + 1, &equivocation_keyring), + ); + + // the call for reporting the equivocation should error + assert_err!( + Beefy::report_equivocation_unsigned( + RuntimeOrigin::none(), + Box::new(equivocation_proof), + key_owner_proof, + ), + Error::::InvalidEquivocationProof, + ); + }); +} + +#[test] +fn report_equivocation_invalid_session() { + let authorities = test_authorities(); + + new_test_ext_raw_authorities(authorities).execute_with(|| { + start_era(1); + + let block_num = System::block_number(); + let validator_set = Beefy::validator_set().unwrap(); + let authorities = validator_set.validators(); + + let equivocation_authority_index = 0; + let equivocation_key = &authorities[equivocation_authority_index]; + let equivocation_keyring = BeefyKeyring::from_public(equivocation_key).unwrap(); + + // generate a key ownership proof at current era set id + let key_owner_proof = + Historical::prove((beefy_primitives::KEY_TYPE, &equivocation_key)).unwrap(); + + start_era(2); + + let set_id = Beefy::validator_set().unwrap().id(); + + let payload1 = Payload::from_single_entry(MMR_ROOT_ID, vec![42]); + let payload2 = Payload::from_single_entry(MMR_ROOT_ID, vec![128]); + // generate an equivocation proof at following era set id = 2 + let equivocation_proof = generate_equivocation_proof( + (block_num, payload1, set_id, &equivocation_keyring), + (block_num, payload2, set_id, &equivocation_keyring), + ); + + // report an equivocation for the current set using an key ownership + // proof from the previous set, the session should be invalid. + assert_err!( + Beefy::report_equivocation_unsigned( + RuntimeOrigin::none(), + Box::new(equivocation_proof), + key_owner_proof, + ), + Error::::InvalidEquivocationProof, + ); + }); +} + +#[test] +fn report_equivocation_invalid_key_owner_proof() { + let authorities = test_authorities(); + + new_test_ext_raw_authorities(authorities).execute_with(|| { + start_era(1); + + let block_num = System::block_number(); + let validator_set = Beefy::validator_set().unwrap(); + let authorities = validator_set.validators(); + let set_id = validator_set.id(); + + let invalid_owner_authority_index = 1; + let invalid_owner_key = &authorities[invalid_owner_authority_index]; + + // generate a key ownership proof for the authority at index 1 + let invalid_key_owner_proof = + Historical::prove((beefy_primitives::KEY_TYPE, &invalid_owner_key)).unwrap(); + + let equivocation_authority_index = 0; + let equivocation_key = &authorities[equivocation_authority_index]; + let equivocation_keyring = BeefyKeyring::from_public(equivocation_key).unwrap(); + + let payload1 = Payload::from_single_entry(MMR_ROOT_ID, vec![42]); + let payload2 = Payload::from_single_entry(MMR_ROOT_ID, vec![128]); + // generate an equivocation proof for the authority at index 0 + let equivocation_proof = generate_equivocation_proof( + (block_num, payload1, set_id + 1, &equivocation_keyring), + (block_num, payload2, set_id + 1, &equivocation_keyring), + ); + + // we need to start a new era otherwise the key ownership proof won't be + // checked since the authorities are part of the current session + start_era(2); + + // report an equivocation for the current set using a key ownership + // proof for a different key than the one in the equivocation proof. + assert_err!( + Beefy::report_equivocation_unsigned( + RuntimeOrigin::none(), + Box::new(equivocation_proof), + invalid_key_owner_proof, + ), + Error::::InvalidKeyOwnershipProof, + ); + }); +} + +#[test] +fn report_equivocation_invalid_equivocation_proof() { + let authorities = test_authorities(); + + new_test_ext_raw_authorities(authorities).execute_with(|| { + start_era(1); + + let block_num = System::block_number(); + let validator_set = Beefy::validator_set().unwrap(); + let authorities = validator_set.validators(); + let set_id = validator_set.id(); + + let equivocation_authority_index = 0; + let equivocation_key = &authorities[equivocation_authority_index]; + let equivocation_keyring = BeefyKeyring::from_public(equivocation_key).unwrap(); + + // generate a key ownership proof at set id in era 1 + let key_owner_proof = + Historical::prove((beefy_primitives::KEY_TYPE, &equivocation_key)).unwrap(); + + let assert_invalid_equivocation_proof = |equivocation_proof| { + assert_err!( + Beefy::report_equivocation_unsigned( + RuntimeOrigin::none(), + Box::new(equivocation_proof), + key_owner_proof.clone(), + ), + Error::::InvalidEquivocationProof, + ); + }; + + start_era(2); + + let payload1 = Payload::from_single_entry(MMR_ROOT_ID, vec![42]); + let payload2 = Payload::from_single_entry(MMR_ROOT_ID, vec![128]); + + // both votes target the same block number and payload, + // there is no equivocation. + assert_invalid_equivocation_proof(generate_equivocation_proof( + (block_num, payload1.clone(), set_id, &equivocation_keyring), + (block_num, payload1.clone(), set_id, &equivocation_keyring), + )); + + // votes targeting different rounds, there is no equivocation. + assert_invalid_equivocation_proof(generate_equivocation_proof( + (block_num, payload1.clone(), set_id, &equivocation_keyring), + (block_num + 1, payload2.clone(), set_id, &equivocation_keyring), + )); + + // votes signed with different authority keys + assert_invalid_equivocation_proof(generate_equivocation_proof( + (block_num, payload1.clone(), set_id, &equivocation_keyring), + (block_num, payload1.clone(), set_id, &BeefyKeyring::Charlie), + )); + + // votes signed with a key that isn't part of the authority set + assert_invalid_equivocation_proof(generate_equivocation_proof( + (block_num, payload1.clone(), set_id, &equivocation_keyring), + (block_num, payload1.clone(), set_id, &BeefyKeyring::Dave), + )); + + // votes targeting different set ids + assert_invalid_equivocation_proof(generate_equivocation_proof( + (block_num, payload1, set_id, &equivocation_keyring), + (block_num, payload2, set_id + 1, &equivocation_keyring), + )); + }); +} + +#[test] +fn report_equivocation_validate_unsigned_prevents_duplicates() { + use sp_runtime::transaction_validity::{ + InvalidTransaction, TransactionPriority, TransactionSource, TransactionValidity, + ValidTransaction, + }; + + let authorities = test_authorities(); + + new_test_ext_raw_authorities(authorities).execute_with(|| { + start_era(1); + + let block_num = System::block_number(); + let validator_set = Beefy::validator_set().unwrap(); + let authorities = validator_set.validators(); + let set_id = validator_set.id(); + + // generate and report an equivocation for the validator at index 0 + let equivocation_authority_index = 0; + let equivocation_key = &authorities[equivocation_authority_index]; + let equivocation_keyring = BeefyKeyring::from_public(equivocation_key).unwrap(); + + let payload1 = Payload::from_single_entry(MMR_ROOT_ID, vec![42]); + let payload2 = Payload::from_single_entry(MMR_ROOT_ID, vec![128]); + let equivocation_proof = generate_equivocation_proof( + (block_num, payload1, set_id, &equivocation_keyring), + (block_num, payload2, set_id, &equivocation_keyring), + ); + + let key_owner_proof = + Historical::prove((beefy_primitives::KEY_TYPE, &equivocation_key)).unwrap(); + + let call = Call::report_equivocation_unsigned { + equivocation_proof: Box::new(equivocation_proof.clone()), + key_owner_proof: key_owner_proof.clone(), + }; + + // only local/inblock reports are allowed + assert_eq!( + ::validate_unsigned( + TransactionSource::External, + &call, + ), + InvalidTransaction::Call.into(), + ); + + // the transaction is valid when passed as local + let tx_tag = (equivocation_key, set_id, 3u64); + + assert_eq!( + ::validate_unsigned( + TransactionSource::Local, + &call, + ), + TransactionValidity::Ok(ValidTransaction { + priority: TransactionPriority::max_value(), + requires: vec![], + provides: vec![("BeefyEquivocation", tx_tag).encode()], + longevity: ReportLongevity::get(), + propagate: false, + }) + ); + + // the pre dispatch checks should also pass + assert_ok!(::pre_dispatch(&call)); + + // we submit the report + Beefy::report_equivocation_unsigned( + RuntimeOrigin::none(), + Box::new(equivocation_proof), + key_owner_proof, + ) + .unwrap(); + + // the report should now be considered stale and the transaction is invalid + // the check for staleness should be done on both `validate_unsigned` and on `pre_dispatch` + assert_err!( + ::validate_unsigned( + TransactionSource::Local, + &call, + ), + InvalidTransaction::Stale, + ); + + assert_err!( + ::pre_dispatch(&call), + InvalidTransaction::Stale, + ); + }); +} + +#[test] +fn report_equivocation_has_valid_weight() { + // the weight depends on the size of the validator set, + // but there's a lower bound of 100 validators. + assert!((1..=100) + .map(::WeightInfo::report_equivocation) + .collect::>() + .windows(2) + .all(|w| w[0] == w[1])); + + // after 100 validators the weight should keep increasing + // with every extra validator. + assert!((100..=1000) + .map(::WeightInfo::report_equivocation) + .collect::>() + .windows(2) + .all(|w| w[0].ref_time() < w[1].ref_time())); +} + +#[test] +fn valid_equivocation_reports_dont_pay_fees() { + let authorities = test_authorities(); + + new_test_ext_raw_authorities(authorities).execute_with(|| { + start_era(1); + + let block_num = System::block_number(); + let validator_set = Beefy::validator_set().unwrap(); + let authorities = validator_set.validators(); + let set_id = validator_set.id(); + + let equivocation_authority_index = 0; + let equivocation_key = &authorities[equivocation_authority_index]; + let equivocation_keyring = BeefyKeyring::from_public(equivocation_key).unwrap(); + + // generate equivocation proof + let payload1 = Payload::from_single_entry(MMR_ROOT_ID, vec![42]); + let payload2 = Payload::from_single_entry(MMR_ROOT_ID, vec![128]); + let equivocation_proof = generate_equivocation_proof( + (block_num, payload1, set_id, &equivocation_keyring), + (block_num, payload2, set_id, &equivocation_keyring), + ); + + // create the key ownership proof. + let key_owner_proof = + Historical::prove((beefy_primitives::KEY_TYPE, &equivocation_key)).unwrap(); + + // check the dispatch info for the call. + let info = Call::::report_equivocation_unsigned { + equivocation_proof: Box::new(equivocation_proof.clone()), + key_owner_proof: key_owner_proof.clone(), + } + .get_dispatch_info(); + + // it should have non-zero weight and the fee has to be paid. + assert!(info.weight.any_gt(Weight::zero())); + assert_eq!(info.pays_fee, Pays::Yes); + + // report the equivocation. + let post_info = Beefy::report_equivocation_unsigned( + RuntimeOrigin::none(), + Box::new(equivocation_proof.clone()), + key_owner_proof.clone(), + ) + .unwrap(); + + // the original weight should be kept, but given that the report + // is valid the fee is waived. + assert!(post_info.actual_weight.is_none()); + assert_eq!(post_info.pays_fee, Pays::No); + + // report the equivocation again which is invalid now since it is + // duplicate. + let post_info = Beefy::report_equivocation_unsigned( + RuntimeOrigin::none(), + Box::new(equivocation_proof), + key_owner_proof, + ) + .err() + .unwrap() + .post_info; + + // the fee is not waived and the original weight is kept. + assert!(post_info.actual_weight.is_none()); + assert_eq!(post_info.pays_fee, Pays::Yes); + }) +} diff --git a/primitives/beefy/src/lib.rs b/primitives/beefy/src/lib.rs index 08467a723..ad9c1b8d1 100644 --- a/primitives/beefy/src/lib.rs +++ b/primitives/beefy/src/lib.rs @@ -34,10 +34,14 @@ mod commitment; pub mod mmr; mod payload; +#[cfg(feature = "std")] +mod test_utils; pub mod witness; pub use commitment::{Commitment, SignedCommitment, VersionedFinalityProof}; pub use payload::{known_payloads, BeefyPayloadId, Payload, PayloadProvider}; +#[cfg(feature = "std")] +pub use test_utils::*; use codec::{Codec, Decode, Encode}; use scale_info::TypeInfo; @@ -183,6 +187,83 @@ pub struct VoteMessage { pub signature: Signature, } +/// Proof of voter misbehavior on a given set id. Misbehavior/equivocation in +/// BEEFY happens when a voter votes on the same round/block for different payloads. +/// Proving is achieved by collecting the signed commitments of conflicting votes. +#[derive(Clone, Debug, Decode, Encode, PartialEq, TypeInfo)] +pub struct EquivocationProof { + /// The first vote in the equivocation. + pub first: VoteMessage, + /// The second vote in the equivocation. + pub second: VoteMessage, +} + +impl EquivocationProof { + /// Returns the authority id of the equivocator. + pub fn offender_id(&self) -> &Id { + &self.first.id + } + /// Returns the round number at which the equivocation occurred. + pub fn round_number(&self) -> &Number { + &self.first.commitment.block_number + } + /// Returns the set id at which the equivocation occurred. + pub fn set_id(&self) -> ValidatorSetId { + self.first.commitment.validator_set_id + } +} + +/// Check a commitment signature by encoding the commitment and +/// verifying the provided signature using the expected authority id. +pub fn check_commitment_signature( + commitment: &Commitment, + authority_id: &Id, + signature: &::Signature, +) -> bool +where + Id: BeefyAuthorityId, + Number: Clone + Encode + PartialEq, + MsgHash: Hash, +{ + let encoded_commitment = commitment.encode(); + BeefyAuthorityId::::verify(authority_id, signature, &encoded_commitment) +} + +/// Verifies the equivocation proof by making sure that both votes target +/// different blocks and that its signatures are valid. +pub fn check_equivocation_proof( + report: &EquivocationProof::Signature>, +) -> bool +where + Id: BeefyAuthorityId + PartialEq, + Number: Clone + Encode + PartialEq, + MsgHash: Hash, +{ + let first = &report.first; + let second = &report.second; + + // if votes + // come from different authorities, + // are for different rounds, + // have different validator set ids, + // or both votes have the same commitment, + // --> the equivocation is invalid. + if first.id != second.id || + first.commitment.block_number != second.commitment.block_number || + first.commitment.validator_set_id != second.commitment.validator_set_id || + first.commitment.payload == second.commitment.payload + { + return false + } + + // check signatures on both votes are valid + let valid_first = check_commitment_signature(&first.commitment, &first.id, &first.signature); + let valid_second = + check_commitment_signature(&second.commitment, &second.id, &second.signature); + + return valid_first && valid_second +} + /// New BEEFY validator set notification hook. pub trait OnNewValidatorSet { /// Function called by the pallet when BEEFY validator set changes. @@ -197,6 +278,28 @@ impl OnNewValidatorSet for () { fn on_new_validator_set(_: &ValidatorSet, _: &ValidatorSet) {} } +/// An opaque type used to represent the key ownership proof at the runtime API +/// boundary. The inner value is an encoded representation of the actual key +/// ownership proof which will be parameterized when defining the runtime. At +/// the runtime API boundary this type is unknown and as such we keep this +/// opaque representation, implementors of the runtime API will have to make +/// sure that all usages of `OpaqueKeyOwnershipProof` refer to the same type. +#[derive(Decode, Encode, PartialEq)] +pub struct OpaqueKeyOwnershipProof(Vec); +impl OpaqueKeyOwnershipProof { + /// Create a new `OpaqueKeyOwnershipProof` using the given encoded + /// representation. + pub fn new(inner: Vec) -> OpaqueKeyOwnershipProof { + OpaqueKeyOwnershipProof(inner) + } + + /// Try to decode this `OpaqueKeyOwnershipProof` into the given concrete key + /// ownership proof type. + pub fn decode(self) -> Option { + codec::Decode::decode(&mut &self.0[..]).ok() + } +} + sp_api::decl_runtime_apis! { /// API necessary for BEEFY voters. pub trait BeefyApi @@ -206,83 +309,36 @@ sp_api::decl_runtime_apis! { /// Return the current active BEEFY validator set fn validator_set() -> Option>; - } -} -#[cfg(feature = "std")] -/// Test accounts using [`crate::crypto`] types. -pub mod keyring { - use super::*; - use sp_core::{ecdsa, keccak_256, Pair}; - use std::collections::HashMap; - use strum::IntoEnumIterator; - - /// Set of test accounts using [`crate::crypto`] types. - #[allow(missing_docs)] - #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, strum::Display, strum::EnumIter)] - pub enum Keyring { - Alice, - Bob, - Charlie, - Dave, - Eve, - Ferdie, - One, - Two, - } - - impl Keyring { - /// Sign `msg`. - pub fn sign(self, msg: &[u8]) -> crypto::Signature { - // todo: use custom signature hashing type - let msg = keccak_256(msg); - ecdsa::Pair::from(self).sign_prehashed(&msg).into() - } - - /// Return key pair. - pub fn pair(self) -> crypto::Pair { - ecdsa::Pair::from_string(self.to_seed().as_str(), None).unwrap().into() - } - - /// Return public key. - pub fn public(self) -> crypto::Public { - self.pair().public() - } - - /// Return seed string. - pub fn to_seed(self) -> String { - format!("//{}", self) - } - - /// Get Keyring from public key. - pub fn from_public(who: &crypto::Public) -> Option { - Self::iter().find(|&k| &crypto::Public::from(k) == who) - } - } - - lazy_static::lazy_static! { - static ref PRIVATE_KEYS: HashMap = - Keyring::iter().map(|i| (i, i.pair())).collect(); - static ref PUBLIC_KEYS: HashMap = - PRIVATE_KEYS.iter().map(|(&name, pair)| (name, pair.public())).collect(); - } - - impl From for crypto::Pair { - fn from(k: Keyring) -> Self { - k.pair() - } - } - - impl From for ecdsa::Pair { - fn from(k: Keyring) -> Self { - k.pair().into() - } - } - - impl From for crypto::Public { - fn from(k: Keyring) -> Self { - (*PUBLIC_KEYS).get(&k).cloned().unwrap() - } + /// Submits an unsigned extrinsic to report an equivocation. The caller + /// must provide the equivocation proof and a key ownership proof + /// (should be obtained using `generate_key_ownership_proof`). The + /// extrinsic will be unsigned and should only be accepted for local + /// authorship (not to be broadcast to the network). This method returns + /// `None` when creation of the extrinsic fails, e.g. if equivocation + /// reporting is disabled for the given runtime (i.e. this method is + /// hardcoded to return `None`). Only useful in an offchain context. + fn submit_report_equivocation_unsigned_extrinsic( + equivocation_proof: + EquivocationProof, crypto::AuthorityId, crypto::Signature>, + key_owner_proof: OpaqueKeyOwnershipProof, + ) -> Option<()>; + + /// Generates a proof of key ownership for the given authority in the + /// given set. An example usage of this module is coupled with the + /// session historical module to prove that a given authority key is + /// tied to a given staking identity during a specific session. Proofs + /// of key ownership are necessary for submitting equivocation reports. + /// NOTE: even though the API takes a `set_id` as parameter the current + /// implementations ignores this parameter and instead relies on this + /// method being called at the correct block height, i.e. any point at + /// which the given set id is live on-chain. Future implementations will + /// instead use indexed data through an offchain worker, not requiring + /// older states to be available. + fn generate_key_ownership_proof( + set_id: ValidatorSetId, + authority_id: crypto::AuthorityId, + ) -> Option; } } diff --git a/primitives/beefy/src/test_utils.rs b/primitives/beefy/src/test_utils.rs new file mode 100644 index 000000000..1a5e03be3 --- /dev/null +++ b/primitives/beefy/src/test_utils.rs @@ -0,0 +1,110 @@ +// This file is part of Substrate. + +// Copyright (C) 2021-2023 Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#![cfg(feature = "std")] + +use crate::{crypto, Commitment, EquivocationProof, Payload, ValidatorSetId, VoteMessage}; +use codec::Encode; +use sp_core::{ecdsa, keccak_256, Pair}; +use std::collections::HashMap; +use strum::IntoEnumIterator; + +/// Set of test accounts using [`crate::crypto`] types. +#[allow(missing_docs)] +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, strum::Display, strum::EnumIter)] +pub enum Keyring { + Alice, + Bob, + Charlie, + Dave, + Eve, + Ferdie, + One, + Two, +} + +impl Keyring { + /// Sign `msg`. + pub fn sign(self, msg: &[u8]) -> crypto::Signature { + // todo: use custom signature hashing type + let msg = keccak_256(msg); + ecdsa::Pair::from(self).sign_prehashed(&msg).into() + } + + /// Return key pair. + pub fn pair(self) -> crypto::Pair { + ecdsa::Pair::from_string(self.to_seed().as_str(), None).unwrap().into() + } + + /// Return public key. + pub fn public(self) -> crypto::Public { + self.pair().public() + } + + /// Return seed string. + pub fn to_seed(self) -> String { + format!("//{}", self) + } + + /// Get Keyring from public key. + pub fn from_public(who: &crypto::Public) -> Option { + Self::iter().find(|&k| &crypto::Public::from(k) == who) + } +} + +lazy_static::lazy_static! { + static ref PRIVATE_KEYS: HashMap = + Keyring::iter().map(|i| (i, i.pair())).collect(); + static ref PUBLIC_KEYS: HashMap = + PRIVATE_KEYS.iter().map(|(&name, pair)| (name, pair.public())).collect(); +} + +impl From for crypto::Pair { + fn from(k: Keyring) -> Self { + k.pair() + } +} + +impl From for ecdsa::Pair { + fn from(k: Keyring) -> Self { + k.pair().into() + } +} + +impl From for crypto::Public { + fn from(k: Keyring) -> Self { + (*PUBLIC_KEYS).get(&k).cloned().unwrap() + } +} + +/// Create a new `EquivocationProof` based on given arguments. +pub fn generate_equivocation_proof( + vote1: (u64, Payload, ValidatorSetId, &Keyring), + vote2: (u64, Payload, ValidatorSetId, &Keyring), +) -> EquivocationProof { + let signed_vote = |block_number: u64, + payload: Payload, + validator_set_id: ValidatorSetId, + keyring: &Keyring| { + let commitment = Commitment { validator_set_id, block_number, payload }; + let signature = keyring.sign(&commitment.encode()); + VoteMessage { commitment, id: keyring.public(), signature } + }; + let first = signed_vote(vote1.0, vote1.1, vote1.2, vote1.3); + let second = signed_vote(vote2.0, vote2.1, vote2.2, vote2.3); + EquivocationProof { first, second } +} diff --git a/test-utils/runtime/src/lib.rs b/test-utils/runtime/src/lib.rs index c1a66eb6a..359061070 100644 --- a/test-utils/runtime/src/lib.rs +++ b/test-utils/runtime/src/lib.rs @@ -980,6 +980,20 @@ cfg_if! { fn validator_set() -> Option> { None } + + fn submit_report_equivocation_unsigned_extrinsic( + _equivocation_proof: beefy_primitives::EquivocationProof< + NumberFor, + beefy_primitives::crypto::AuthorityId, + beefy_primitives::crypto::Signature + >, + _key_owner_proof: beefy_primitives::OpaqueKeyOwnershipProof, + ) -> Option<()> { None } + + fn generate_key_ownership_proof( + _set_id: beefy_primitives::ValidatorSetId, + _authority_id: beefy_primitives::crypto::AuthorityId, + ) -> Option { None } } impl pallet_beefy_mmr::BeefyMmrApi for Runtime { From dfa654d427d73850ec5095194552d5305a01a9a9 Mon Sep 17 00:00:00 2001 From: Dmitry Markin Date: Fri, 17 Feb 2023 13:08:59 +0300 Subject: [PATCH 133/558] network: Detect early that `NotificationOutSubstream` was closed by the remote (#13396) --- .../src/protocol/notifications/handler.rs | 7 +- .../notifications/upgrade/notifications.rs | 132 ++++++++++++++++-- 2 files changed, 127 insertions(+), 12 deletions(-) diff --git a/client/network/src/protocol/notifications/handler.rs b/client/network/src/protocol/notifications/handler.rs index 57561c7b9..ca87941cb 100644 --- a/client/network/src/protocol/notifications/handler.rs +++ b/client/network/src/protocol/notifications/handler.rs @@ -782,6 +782,9 @@ impl ConnectionHandler for NotifsHandler { // performed before the code paths that can produce `Ready` (with some rare exceptions). // Importantly, however, the flush is performed *after* notifications are queued with // `Sink::start_send`. + // Note that we must call `poll_flush` on all substreams and not only on those we + // have called `Sink::start_send` on, because `NotificationsOutSubstream::poll_flush` + // also reports the substream termination (even if no data was written into it). for protocol_index in 0..self.protocols.len() { match &mut self.protocols[protocol_index].state { State::Open { out_substream: out_substream @ Some(_), .. } => { @@ -824,7 +827,7 @@ impl ConnectionHandler for NotifsHandler { State::OpenDesiredByRemote { in_substream, pending_opening } => match NotificationsInSubstream::poll_process(Pin::new(in_substream), cx) { Poll::Pending => {}, - Poll::Ready(Ok(void)) => match void {}, + Poll::Ready(Ok(())) => {}, Poll::Ready(Err(_)) => { self.protocols[protocol_index].state = State::Closed { pending_opening: *pending_opening }; @@ -840,7 +843,7 @@ impl ConnectionHandler for NotifsHandler { cx, ) { Poll::Pending => {}, - Poll::Ready(Ok(void)) => match void {}, + Poll::Ready(Ok(())) => {}, Poll::Ready(Err(_)) => *in_substream = None, }, } diff --git a/client/network/src/protocol/notifications/upgrade/notifications.rs b/client/network/src/protocol/notifications/upgrade/notifications.rs index 71afc3c90..3621c6349 100644 --- a/client/network/src/protocol/notifications/upgrade/notifications.rs +++ b/client/network/src/protocol/notifications/upgrade/notifications.rs @@ -41,7 +41,6 @@ use libp2p::core::{upgrade, InboundUpgrade, OutboundUpgrade, UpgradeInfo}; use log::{error, warn}; use sc_network_common::protocol::ProtocolName; use std::{ - convert::Infallible, io, mem, pin::Pin, task::{Context, Poll}, @@ -221,10 +220,7 @@ where /// Equivalent to `Stream::poll_next`, except that it only drives the handshake and is /// guaranteed to not generate any notification. - pub fn poll_process( - self: Pin<&mut Self>, - cx: &mut Context, - ) -> Poll> { + pub fn poll_process(self: Pin<&mut Self>, cx: &mut Context) -> Poll> { let mut this = self.project(); loop { @@ -246,8 +242,10 @@ where }, NotificationsInSubstreamHandshake::Flush => { match Sink::poll_flush(this.socket.as_mut(), cx)? { - Poll::Ready(()) => - *this.handshake = NotificationsInSubstreamHandshake::Sent, + Poll::Ready(()) => { + *this.handshake = NotificationsInSubstreamHandshake::Sent; + return Poll::Ready(Ok(())) + }, Poll::Pending => { *this.handshake = NotificationsInSubstreamHandshake::Flush; return Poll::Pending @@ -260,7 +258,7 @@ where st @ NotificationsInSubstreamHandshake::ClosingInResponseToRemote | st @ NotificationsInSubstreamHandshake::BothSidesClosed => { *this.handshake = st; - return Poll::Pending + return Poll::Ready(Ok(())) }, } } @@ -443,6 +441,21 @@ where fn poll_flush(self: Pin<&mut Self>, cx: &mut Context) -> Poll> { let mut this = self.project(); + + // `Sink::poll_flush` does not expose stream closed error until we write something into + // the stream, so the code below makes sure we detect that the substream was closed + // even if we don't write anything into it. + match Stream::poll_next(this.socket.as_mut(), cx) { + Poll::Pending => {}, + Poll::Ready(Some(_)) => { + error!( + target: "sub-libp2p", + "Unexpected incoming data in `NotificationsOutSubstream`", + ); + }, + Poll::Ready(None) => return Poll::Ready(Err(NotificationsOutError::Terminated)), + } + Sink::poll_flush(this.socket.as_mut(), cx).map_err(NotificationsOutError::Io) } @@ -492,13 +505,21 @@ pub enum NotificationsOutError { /// I/O error on the substream. #[error(transparent)] Io(#[from] io::Error), + + /// End of incoming data detected on out substream. + #[error("substream was closed/reset")] + Terminated, } #[cfg(test)] mod tests { - use super::{NotificationsIn, NotificationsInOpen, NotificationsOut, NotificationsOutOpen}; - use futures::{channel::oneshot, prelude::*}; + use super::{ + NotificationsIn, NotificationsInOpen, NotificationsOut, NotificationsOutError, + NotificationsOutOpen, + }; + use futures::{channel::oneshot, future, prelude::*}; use libp2p::core::upgrade; + use std::{pin::Pin, task::Poll}; use tokio::net::{TcpListener, TcpStream}; use tokio_util::compat::TokioAsyncReadCompatExt; @@ -691,4 +712,95 @@ mod tests { client.await.unwrap(); } + + #[tokio::test] + async fn send_handshake_without_polling_for_incoming_data() { + const PROTO_NAME: &str = "/test/proto/1"; + let (listener_addr_tx, listener_addr_rx) = oneshot::channel(); + + let client = tokio::spawn(async move { + let socket = TcpStream::connect(listener_addr_rx.await.unwrap()).await.unwrap(); + let NotificationsOutOpen { handshake, .. } = upgrade::apply_outbound( + socket.compat(), + NotificationsOut::new(PROTO_NAME, Vec::new(), &b"initial message"[..], 1024 * 1024), + upgrade::Version::V1, + ) + .await + .unwrap(); + + assert_eq!(handshake, b"hello world"); + }); + + let listener = TcpListener::bind("127.0.0.1:0").await.unwrap(); + listener_addr_tx.send(listener.local_addr().unwrap()).unwrap(); + + let (socket, _) = listener.accept().await.unwrap(); + let NotificationsInOpen { handshake, mut substream, .. } = upgrade::apply_inbound( + socket.compat(), + NotificationsIn::new(PROTO_NAME, Vec::new(), 1024 * 1024), + ) + .await + .unwrap(); + + assert_eq!(handshake, b"initial message"); + substream.send_handshake(&b"hello world"[..]); + + // Actually send the handshake. + future::poll_fn(|cx| Pin::new(&mut substream).poll_process(cx)).await.unwrap(); + + client.await.unwrap(); + } + + #[tokio::test] + async fn can_detect_dropped_out_substream_without_writing_data() { + const PROTO_NAME: &str = "/test/proto/1"; + let (listener_addr_tx, listener_addr_rx) = oneshot::channel(); + + let client = tokio::spawn(async move { + let socket = TcpStream::connect(listener_addr_rx.await.unwrap()).await.unwrap(); + let NotificationsOutOpen { handshake, mut substream, .. } = upgrade::apply_outbound( + socket.compat(), + NotificationsOut::new(PROTO_NAME, Vec::new(), &b"initial message"[..], 1024 * 1024), + upgrade::Version::V1, + ) + .await + .unwrap(); + + assert_eq!(handshake, b"hello world"); + + future::poll_fn(|cx| match Pin::new(&mut substream).poll_flush(cx) { + Poll::Pending => Poll::Pending, + Poll::Ready(Ok(())) => { + cx.waker().wake_by_ref(); + Poll::Pending + }, + Poll::Ready(Err(e)) => { + assert!(matches!(e, NotificationsOutError::Terminated)); + Poll::Ready(()) + }, + }) + .await; + }); + + let listener = TcpListener::bind("127.0.0.1:0").await.unwrap(); + listener_addr_tx.send(listener.local_addr().unwrap()).unwrap(); + + let (socket, _) = listener.accept().await.unwrap(); + let NotificationsInOpen { handshake, mut substream, .. } = upgrade::apply_inbound( + socket.compat(), + NotificationsIn::new(PROTO_NAME, Vec::new(), 1024 * 1024), + ) + .await + .unwrap(); + + assert_eq!(handshake, b"initial message"); + + // Send the handhsake. + substream.send_handshake(&b"hello world"[..]); + future::poll_fn(|cx| Pin::new(&mut substream).poll_process(cx)).await.unwrap(); + + drop(substream); + + client.await.unwrap(); + } } From ea4fbcb84cf3883123d1341068e1e70310ab2049 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Silva?= <123550+andresilva@users.noreply.github.com> Date: Fri, 17 Feb 2023 11:46:43 +0000 Subject: [PATCH 134/558] babe: account for skipped epochs when handling equivocations (#13335) * babe: account for skipped epochs when handling equivocations * typos * babe: enforce epoch index >= session index --- frame/babe/src/lib.rs | 90 ++++++++++++++++++++++++++++++++++++++--- frame/babe/src/tests.rs | 62 ++++++++++++++++++++++++++++ 2 files changed, 146 insertions(+), 6 deletions(-) diff --git a/frame/babe/src/lib.rs b/frame/babe/src/lib.rs index eb87d65b0..69c89fdce 100644 --- a/frame/babe/src/lib.rs +++ b/frame/babe/src/lib.rs @@ -39,6 +39,7 @@ use sp_runtime::{ ConsensusEngineId, KeyTypeId, Permill, }; use sp_session::{GetSessionNumber, GetValidatorCount}; +use sp_staking::SessionIndex; use sp_std::prelude::*; use sp_consensus_babe::{ @@ -102,7 +103,7 @@ impl EpochChangeTrigger for SameAuthoritiesForever { let authorities = >::authorities(); let next_authorities = authorities.clone(); - >::enact_epoch_change(authorities, next_authorities); + >::enact_epoch_change(authorities, next_authorities, None); } } } @@ -316,6 +317,19 @@ pub mod pallet { #[pallet::storage] pub(super) type NextEpochConfig = StorageValue<_, BabeEpochConfiguration>; + /// A list of the last 100 skipped epochs and the corresponding session index + /// when the epoch was skipped. + /// + /// This is only used for validating equivocation proofs. An equivocation proof + /// must contains a key-ownership proof for a given session, therefore we need a + /// way to tie together sessions and epoch indices, i.e. we need to validate that + /// a validator was the owner of a given key on a given session, and what the + /// active epoch index was during that session. + #[pallet::storage] + #[pallet::getter(fn skipped_epochs)] + pub(super) type SkippedEpochs = + StorageValue<_, BoundedVec<(u64, SessionIndex), ConstU32<100>>, ValueQuery>; + #[cfg_attr(feature = "std", derive(Default))] #[pallet::genesis_config] pub struct GenesisConfig { @@ -577,6 +591,7 @@ impl Pallet { pub fn enact_epoch_change( authorities: WeakBoundedVec<(AuthorityId, BabeAuthorityWeight), T::MaxAuthorities>, next_authorities: WeakBoundedVec<(AuthorityId, BabeAuthorityWeight), T::MaxAuthorities>, + session_index: Option, ) { // PRECONDITION: caller has done initialization and is guaranteed // by the session module to be called before this. @@ -602,6 +617,35 @@ impl Pallet { T::EpochDuration::get(), ); + let current_epoch_index = EpochIndex::::get(); + if current_epoch_index.saturating_add(1) != epoch_index { + // we are skipping epochs therefore we need to update the mapping + // of epochs to session + if let Some(session_index) = session_index { + SkippedEpochs::::mutate(|skipped_epochs| { + if epoch_index < session_index as u64 { + log::warn!( + target: LOG_TARGET, + "Current epoch index {} is lower than session index {}.", + epoch_index, + session_index, + ); + + return + } + + if skipped_epochs.is_full() { + // NOTE: this is O(n) but we currently don't have a bounded `VecDeque`. + // this vector is bounded to a small number of elements so performance + // shouldn't be an issue. + skipped_epochs.remove(0); + } + + skipped_epochs.force_push((epoch_index, session_index)); + }) + } + } + EpochIndex::::put(epoch_index); Authorities::::put(authorities); @@ -816,6 +860,36 @@ impl Pallet { this_randomness } + /// Returns the session index that was live when the given epoch happened, + /// taking into account any skipped epochs. + /// + /// This function is only well defined for epochs that actually existed, + /// e.g. if we skipped from epoch 10 to 20 then a call for epoch 15 (which + /// didn't exist) will return an incorrect session index. + fn session_index_for_epoch(epoch_index: u64) -> SessionIndex { + let skipped_epochs = SkippedEpochs::::get(); + match skipped_epochs.binary_search_by_key(&epoch_index, |(epoch_index, _)| *epoch_index) { + // we have an exact match so we just return the given session index + Ok(index) => skipped_epochs[index].1, + // we haven't found any skipped epoch before the given epoch, + // so the epoch index and session index should match + Err(0) => epoch_index.saturated_into::(), + // we have found a skipped epoch before the given epoch + Err(index) => { + // the element before the given index should give us the skipped epoch + // that's closest to the one we're trying to find the session index for + let closest_skipped_epoch = skipped_epochs[index - 1]; + + // calculate the number of skipped epochs at this point by checking the difference + // between the epoch and session indices. epoch index should always be greater or + // equal to session index, this is because epochs can be skipped whereas sessions + // can't (this is enforced when pushing into `SkippedEpochs`) + let skipped_epochs = closest_skipped_epoch.0 - closest_skipped_epoch.1 as u64; + epoch_index.saturating_sub(skipped_epochs).saturated_into::() + }, + } + } + fn do_report_equivocation( reporter: Option, equivocation_proof: EquivocationProof, @@ -832,12 +906,11 @@ impl Pallet { let validator_set_count = key_owner_proof.validator_count(); let session_index = key_owner_proof.session(); - let epoch_index = (*slot.saturating_sub(GenesisSlot::::get()) / T::EpochDuration::get()) - .saturated_into::(); + let epoch_index = *slot.saturating_sub(GenesisSlot::::get()) / T::EpochDuration::get(); // check that the slot number is consistent with the session index // in the key ownership proof (i.e. slot is for that epoch) - if epoch_index != session_index { + if Self::session_index_for_epoch(epoch_index) != session_index { return Err(Error::::InvalidKeyOwnershipProof.into()) } @@ -926,7 +999,10 @@ impl sp_runtime::BoundToRuntimeAppPublic for Pallet { type Public = AuthorityId; } -impl OneSessionHandler for Pallet { +impl OneSessionHandler for Pallet +where + T: pallet_session::Config, +{ type Key = AuthorityId; fn on_genesis_session<'a, I: 'a>(validators: I) @@ -959,7 +1035,9 @@ impl OneSessionHandler for Pallet { ), ); - Self::enact_epoch_change(bounded_authorities, next_bounded_authorities) + let session_index = >::current_index(); + + Self::enact_epoch_change(bounded_authorities, next_bounded_authorities, Some(session_index)) } fn on_disabled(i: u32) { diff --git a/frame/babe/src/tests.rs b/frame/babe/src/tests.rs index 0b8a02547..938269a6c 100644 --- a/frame/babe/src/tests.rs +++ b/frame/babe/src/tests.rs @@ -826,6 +826,56 @@ fn report_equivocation_has_valid_weight() { .all(|w| w[0].ref_time() < w[1].ref_time())); } +#[test] +fn report_equivocation_after_skipped_epochs_works() { + let (pairs, mut ext) = new_test_ext_with_pairs(3); + + ext.execute_with(|| { + let epoch_duration: u64 = ::EpochDuration::get(); + + // this sets the genesis slot to 100; + let genesis_slot = 100; + go_to_block(1, genesis_slot); + assert_eq!(EpochIndex::::get(), 0); + + // skip from epoch #0 to epoch #10 + go_to_block(System::block_number() + 1, genesis_slot + epoch_duration * 10); + + assert_eq!(EpochIndex::::get(), 10); + assert_eq!(SkippedEpochs::::get(), vec![(10, 1)]); + + // generate an equivocation proof for validator at index 1 + let authorities = Babe::authorities(); + let offending_validator_index = 1; + let offending_authority_pair = pairs + .into_iter() + .find(|p| p.public() == authorities[offending_validator_index].0) + .unwrap(); + + let equivocation_proof = generate_equivocation_proof( + offending_validator_index as u32, + &offending_authority_pair, + CurrentSlot::::get(), + ); + + // create the key ownership proof + let key = (sp_consensus_babe::KEY_TYPE, &offending_authority_pair.public()); + let key_owner_proof = Historical::prove(key).unwrap(); + + // which is for session index 1 (while current epoch index is 10) + assert_eq!(key_owner_proof.session, 1); + + // report the equivocation, in order for the validation to pass the mapping + // between epoch index and session index must be checked. + assert!(Babe::report_equivocation_unsigned( + RuntimeOrigin::none(), + Box::new(equivocation_proof), + key_owner_proof + ) + .is_ok()); + }) +} + #[test] fn valid_equivocation_reports_dont_pay_fees() { let (pairs, mut ext) = new_test_ext_with_pairs(3); @@ -977,5 +1027,17 @@ fn skipping_over_epochs_works() { assert_eq!(EpochIndex::::get(), 4); assert_eq!(Randomness::::get(), randomness_for_epoch_2); + + // after skipping epochs the information is registered on-chain so that + // we can map epochs to sessions + assert_eq!(SkippedEpochs::::get(), vec![(4, 2)]); + + // before epochs are skipped the mapping should be one to one + assert_eq!(Babe::session_index_for_epoch(0), 0); + assert_eq!(Babe::session_index_for_epoch(1), 1); + + // otherwise the session index is offset by the number of skipped epochs + assert_eq!(Babe::session_index_for_epoch(4), 2); + assert_eq!(Babe::session_index_for_epoch(5), 3); }); } From a33091270e54f9e3f3488485c0a7ca3a58f5a26c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Piotr=20Miko=C5=82ajczyk?= Date: Fri, 17 Feb 2023 13:21:58 +0100 Subject: [PATCH 135/558] `try-runtime::fast-forward` (#12896) * try-runtime::fast-forward * Revert un`pub`ing command's fields * Handle storage change failure * Adjust Substrate node * Feature-gated imports * doc link * Feature-gated imports in node-template * Move trait, blanket implementation and auxiliary functions to a new module * Distinguish between plain babe+timestamp and substrate enhanced info * Remove uncles inherents * Missing argument * Add doc comment about `blocktime_millis` * Add licenses --- Cargo.lock | 6 + bin/node-template/node/src/command.rs | 7 +- bin/node/cli/src/command.rs | 10 +- utils/frame/try-runtime/cli/Cargo.toml | 6 + .../cli/src/block_building_info.rs | 152 ++++++++++ .../cli/src/commands/fast_forward.rs | 268 ++++++++++++++++++ .../frame/try-runtime/cli/src/commands/mod.rs | 1 + utils/frame/try-runtime/cli/src/lib.rs | 27 +- 8 files changed, 473 insertions(+), 4 deletions(-) create mode 100644 utils/frame/try-runtime/cli/src/block_building_info.rs create mode 100644 utils/frame/try-runtime/cli/src/commands/fast_forward.rs diff --git a/Cargo.lock b/Cargo.lock index b33f7627f..d7f48c17f 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -11426,6 +11426,7 @@ checksum = "3528ecfd12c466c6f163363caf2d02a71161dd5e1cc6ae7b34207ea2d42d81ed" name = "try-runtime-cli" version = "0.10.0-dev" dependencies = [ + "async-trait", "clap 4.1.4", "frame-remote-externalities", "frame-try-runtime", @@ -11438,14 +11439,19 @@ dependencies = [ "serde", "serde_json", "sp-api", + "sp-consensus-aura", + "sp-consensus-babe", "sp-core", "sp-debug-derive", "sp-externalities", + "sp-inherents", "sp-io", "sp-keystore", "sp-rpc", "sp-runtime", "sp-state-machine", + "sp-timestamp", + "sp-transaction-storage-proof", "sp-version", "sp-weights", "substrate-rpc-client", diff --git a/bin/node-template/node/src/command.rs b/bin/node-template/node/src/command.rs index 15cd69b34..c3dc098cd 100644 --- a/bin/node-template/node/src/command.rs +++ b/bin/node-template/node/src/command.rs @@ -10,6 +10,9 @@ use sc_cli::{ChainSpec, RuntimeVersion, SubstrateCli}; use sc_service::PartialComponents; use sp_keyring::Sr25519Keyring; +#[cfg(feature = "try-runtime")] +use try_runtime_cli::block_building_info::timestamp_with_aura_info; + impl SubstrateCli for Cli { fn impl_name() -> String { "Substrate Node".into() @@ -184,11 +187,13 @@ pub fn run() -> sc_cli::Result<()> { let task_manager = sc_service::TaskManager::new(config.tokio_handle.clone(), registry) .map_err(|e| sc_cli::Error::Service(sc_service::Error::Prometheus(e)))?; + let info_provider = timestamp_with_aura_info(6000); + Ok(( cmd.run::::ExtendHostFunctions, - >>(), + >, _>(Some(info_provider)), task_manager, )) }) diff --git a/bin/node/cli/src/command.rs b/bin/node/cli/src/command.rs index 2ed8a2c75..4256c7d61 100644 --- a/bin/node/cli/src/command.rs +++ b/bin/node/cli/src/command.rs @@ -32,6 +32,12 @@ use sp_keyring::Sr25519Keyring; use std::sync::Arc; +#[cfg(feature = "try-runtime")] +use { + kitchensink_runtime::constants::time::SLOT_DURATION, + try_runtime_cli::block_building_info::substrate_info, +}; + impl SubstrateCli for Cli { fn impl_name() -> String { "Substrate Node".into() @@ -236,11 +242,13 @@ pub fn run() -> Result<()> { sc_service::TaskManager::new(config.tokio_handle.clone(), registry) .map_err(|e| sc_cli::Error::Service(sc_service::Error::Prometheus(e)))?; + let info_provider = substrate_info(SLOT_DURATION); + Ok(( cmd.run::::ExtendHostFunctions, - >>(), + >, _>(Some(info_provider)), task_manager, )) }) diff --git a/utils/frame/try-runtime/cli/Cargo.toml b/utils/frame/try-runtime/cli/Cargo.toml index 8c14ddc29..a22028954 100644 --- a/utils/frame/try-runtime/cli/Cargo.toml +++ b/utils/frame/try-runtime/cli/Cargo.toml @@ -16,13 +16,18 @@ remote-externalities = { version = "0.10.0-dev", path = "../../remote-externalit sc-cli = { version = "0.10.0-dev", path = "../../../../client/cli" } sc-executor = { version = "0.10.0-dev", path = "../../../../client/executor" } sc-service = { version = "0.10.0-dev", default-features = false, path = "../../../../client/service" } +sp-consensus-aura = { path = "../../../../primitives/consensus/aura" } +sp-consensus-babe = { path = "../../../../primitives/consensus/babe" } sp-core = { version = "7.0.0", path = "../../../../primitives/core" } sp-externalities = { version = "0.13.0", path = "../../../../primitives/externalities" } +sp-inherents = { path = "../../../../primitives/inherents" } sp-io = { version = "7.0.0", path = "../../../../primitives/io" } sp-keystore = { version = "0.13.0", path = "../../../../primitives/keystore" } sp-runtime = { version = "7.0.0", path = "../../../../primitives/runtime" } sp-rpc = { version = "6.0.0", path = "../../../../primitives/rpc" } sp-state-machine = { version = "0.13.0", path = "../../../../primitives/state-machine" } +sp-timestamp = { path = "../../../../primitives/timestamp" } +sp-transaction-storage-proof = { path = "../../../../primitives/transaction-storage-proof" } sp-version = { version = "5.0.0", path = "../../../../primitives/version" } sp-debug-derive = { path = "../../../../primitives/debug-derive" } sp-api = { path = "../../../../primitives/api" } @@ -30,6 +35,7 @@ sp-weights = { version = "4.0.0", path = "../../../../primitives/weights" } frame-try-runtime = { optional = true, path = "../../../../frame/try-runtime" } substrate-rpc-client = { path = "../../rpc/client" } +async-trait = "0.1.57" clap = { version = "4.0.9", features = ["derive"] } hex = { version = "0.4.3", default-features = false } log = "0.4.17" diff --git a/utils/frame/try-runtime/cli/src/block_building_info.rs b/utils/frame/try-runtime/cli/src/block_building_info.rs new file mode 100644 index 000000000..b68bf081f --- /dev/null +++ b/utils/frame/try-runtime/cli/src/block_building_info.rs @@ -0,0 +1,152 @@ +// This file is part of Substrate. + +// Copyright (C) 2021-2022 Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +use crate::BlockT; +use parity_scale_codec::Encode; +use sc_cli::Result; +use sp_consensus_aura::{Slot, SlotDuration, AURA_ENGINE_ID}; +use sp_consensus_babe::{ + digests::{PreDigest, SecondaryPlainPreDigest}, + BABE_ENGINE_ID, +}; +use sp_inherents::{InherentData, InherentDataProvider}; +use sp_runtime::{Digest, DigestItem}; +use sp_timestamp::TimestampInherentData; + +/// Something that can create inherent data providers and pre-runtime digest. +/// +/// It is possible for the caller to provide custom arguments to the callee by setting the +/// `ExtraArgs` generic parameter. +/// +/// This module already provides some convenience implementation of this trait for closures. So, it +/// should not be required to implement it directly. +#[async_trait::async_trait] +pub trait BlockBuildingInfoProvider { + type InherentDataProviders: InherentDataProvider; + + async fn get_inherent_providers_and_pre_digest( + &self, + parent_hash: Block::Hash, + extra_args: ExtraArgs, + ) -> Result<(Self::InherentDataProviders, Vec)>; +} + +#[async_trait::async_trait] +impl BlockBuildingInfoProvider for F +where + Block: BlockT, + F: Fn(Block::Hash, ExtraArgs) -> Fut + Sync + Send, + Fut: std::future::Future)>> + Send + 'static, + IDP: InherentDataProvider + 'static, + ExtraArgs: Send + 'static, +{ + type InherentDataProviders = IDP; + + async fn get_inherent_providers_and_pre_digest( + &self, + parent: Block::Hash, + extra_args: ExtraArgs, + ) -> Result<(Self::InherentDataProviders, Vec)> { + (*self)(parent, extra_args).await + } +} + +/// Provides [`BlockBuildingInfoProvider`] implementation for chains that include timestamp inherent +/// and use Aura for a block production. +/// +/// It depends only on the expected block production frequency, i.e. `blocktime_millis`. +pub fn timestamp_with_aura_info( + blocktime_millis: u64, +) -> impl BlockBuildingInfoProvider> { + move |_, maybe_prev_info: Option<(InherentData, Digest)>| async move { + let timestamp_idp = match maybe_prev_info { + Some((inherent_data, _)) => sp_timestamp::InherentDataProvider::new( + inherent_data.timestamp_inherent_data().unwrap().unwrap() + blocktime_millis, + ), + None => sp_timestamp::InherentDataProvider::from_system_time(), + }; + + let slot = + Slot::from_timestamp(*timestamp_idp, SlotDuration::from_millis(blocktime_millis)); + let digest = vec![DigestItem::PreRuntime(AURA_ENGINE_ID, slot.encode())]; + + Ok((timestamp_idp, digest)) + } +} + +/// Provides [`BlockBuildingInfoProvider`] implementation for chains that include timestamp inherent +/// and use Babe for a block production. +/// +/// It depends only on the expected block production frequency, i.e. `blocktime_millis`. +pub fn timestamp_with_babe_info( + blocktime_millis: u64, +) -> impl BlockBuildingInfoProvider> { + move |_, maybe_prev_info: Option<(InherentData, Digest)>| async move { + let timestamp_idp = match maybe_prev_info { + Some((inherent_data, _)) => sp_timestamp::InherentDataProvider::new( + inherent_data.timestamp_inherent_data().unwrap().unwrap() + blocktime_millis, + ), + None => sp_timestamp::InherentDataProvider::from_system_time(), + }; + + let slot = + Slot::from_timestamp(*timestamp_idp, SlotDuration::from_millis(blocktime_millis)); + let slot_idp = sp_consensus_babe::inherents::InherentDataProvider::new(slot); + + let digest = vec![DigestItem::PreRuntime( + BABE_ENGINE_ID, + PreDigest::SecondaryPlain(SecondaryPlainPreDigest { slot, authority_index: 0 }) + .encode(), + )]; + + Ok(((slot_idp, timestamp_idp), digest)) + } +} + +/// Provides [`BlockBuildingInfoProvider`] implementation for chains that use: +/// - timestamp inherent, +/// - Babe for a block production (inherent + digest), +/// - uncles inherent, +/// - storage proof inherent +/// +/// It depends only on the expected block production frequency, i.e. `blocktime_millis`. +pub fn substrate_info( + blocktime_millis: u64, +) -> impl BlockBuildingInfoProvider> { + move |_, maybe_prev_info: Option<(InherentData, Digest)>| async move { + let timestamp_idp = match maybe_prev_info { + Some((inherent_data, _)) => sp_timestamp::InherentDataProvider::new( + inherent_data.timestamp_inherent_data().unwrap().unwrap() + blocktime_millis, + ), + None => sp_timestamp::InherentDataProvider::from_system_time(), + }; + + let slot = + Slot::from_timestamp(*timestamp_idp, SlotDuration::from_millis(blocktime_millis)); + let slot_idp = sp_consensus_babe::inherents::InherentDataProvider::new(slot); + + let storage_proof_idp = sp_transaction_storage_proof::InherentDataProvider::new(None); + + let digest = vec![DigestItem::PreRuntime( + BABE_ENGINE_ID, + PreDigest::SecondaryPlain(SecondaryPlainPreDigest { slot, authority_index: 0 }) + .encode(), + )]; + + Ok(((slot_idp, timestamp_idp, storage_proof_idp), digest)) + } +} diff --git a/utils/frame/try-runtime/cli/src/commands/fast_forward.rs b/utils/frame/try-runtime/cli/src/commands/fast_forward.rs new file mode 100644 index 000000000..ae8f9a5cf --- /dev/null +++ b/utils/frame/try-runtime/cli/src/commands/fast_forward.rs @@ -0,0 +1,268 @@ +// This file is part of Substrate. + +// Copyright (C) 2021-2022 Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +use crate::{ + block_building_info::BlockBuildingInfoProvider, build_executor, full_extensions, + rpc_err_handler, state_machine_call, BlockT, LiveState, SharedParams, State, +}; +use parity_scale_codec::{Decode, Encode}; +use sc_cli::Result; +use sc_executor::{sp_wasm_interface::HostFunctions, WasmExecutor}; +use serde::de::DeserializeOwned; +use sp_core::H256; +use sp_inherents::{InherentData, InherentDataProvider}; +use sp_io::TestExternalities; +use sp_runtime::{ + traits::{Header, NumberFor, One}, + Digest, +}; +use std::{fmt::Debug, str::FromStr}; +use substrate_rpc_client::{ws_client, ChainApi}; + +/// Configurations of the [`crate::Command::FastForward`]. +#[derive(Debug, Clone, clap::Parser)] +pub struct FastForwardCmd { + /// How many blocks should be processed. If `None`, then blocks will be produced and processed + /// in a loop. + #[arg(long)] + n_blocks: Option, + + /// The state type to use. + #[command(subcommand)] + state: State, + + /// The ws uri from which to fetch the block. + /// + /// If `state` is `Live`, this is ignored. Otherwise, it must not be empty. + #[arg(long, value_parser = crate::parse::url)] + block_ws_uri: Option, + + /// Which try-state targets to execute when running this command. + /// + /// Expected values: + /// - `all` + /// - `none` + /// - A comma separated list of pallets, as per pallet names in `construct_runtime!()` (e.g. + /// `Staking, System`). + /// - `rr-[x]` where `[x]` is a number. Then, the given number of pallets are checked in a + /// round-robin fashion. + #[arg(long, default_value = "all")] + try_state: frame_try_runtime::TryStateSelect, +} + +impl FastForwardCmd { + fn block_ws_uri(&self) -> &str { + match self.state { + State::Live(LiveState { ref uri, .. }) => &uri, + _ => self + .block_ws_uri + .as_ref() + .expect("Either `--block-uri` must be provided, or state must be `live`"), + } + } +} + +/// Read the block number corresponding to `hash` with an RPC call to `ws_uri`. +async fn get_block_number( + hash: Block::Hash, + ws_uri: &str, +) -> Result> +where + Block::Header: DeserializeOwned, +{ + let rpc = ws_client(ws_uri).await?; + Ok(ChainApi::<(), Block::Hash, Block::Header, ()>::header(&rpc, Some(hash)) + .await + .map_err(rpc_err_handler) + .and_then(|maybe_header| maybe_header.ok_or("header_not_found").map(|h| *h.number()))?) +} + +/// Call `method` with `data` and return the result. `externalities` will not change. +async fn dry_run( + externalities: &TestExternalities, + executor: &WasmExecutor, + method: &'static str, + data: &[u8], +) -> Result { + let (_, result) = state_machine_call::( + externalities, + executor, + method, + data, + full_extensions(), + )?; + + Ok(::decode(&mut &*result)?) +} + +/// Call `method` with `data` and actually save storage changes to `externalities`. +async fn run( + externalities: &mut TestExternalities, + executor: &WasmExecutor, + method: &'static str, + data: &[u8], +) -> Result<()> { + let (mut changes, _) = state_machine_call::( + externalities, + executor, + method, + data, + full_extensions(), + )?; + + let storage_changes = changes.drain_storage_changes( + &externalities.backend, + &mut Default::default(), + externalities.state_version, + )?; + + externalities + .backend + .apply_transaction(storage_changes.transaction_storage_root, storage_changes.transaction); + + Ok(()) +} + +/// Produce next empty block. +async fn next_empty_block< + Block: BlockT, + HostFns: HostFunctions, + BBIP: BlockBuildingInfoProvider>, +>( + externalities: &mut TestExternalities, + executor: &WasmExecutor, + parent_height: NumberFor, + parent_hash: Block::Hash, + block_building_info_provider: &Option, + previous_block_building_info: Option<(InherentData, Digest)>, +) -> Result<(Block, Option<(InherentData, Digest)>)> { + let (maybe_inherent_data, pre_digest) = match &block_building_info_provider { + None => (None, Default::default()), + Some(bbip) => { + let (inherent_data_provider, pre_digest) = bbip + .get_inherent_providers_and_pre_digest(parent_hash, previous_block_building_info) + .await?; + let inherent_data = inherent_data_provider + .create_inherent_data() + .await + .map_err(|e| sc_cli::Error::Input(format!("I don't know how to convert {e:?}")))?; + + (Some(inherent_data), Digest { logs: pre_digest }) + }, + }; + + let header = Block::Header::new( + parent_height + One::one(), + Default::default(), + Default::default(), + parent_hash, + pre_digest.clone(), + ); + let mut extrinsics = >::new(); + + run::(externalities, executor, "Core_initialize_block", &header.encode()).await?; + + if let Some(ref inherent_data) = maybe_inherent_data { + extrinsics = dry_run::, Block, _>( + externalities, + executor, + "BlockBuilder_inherent_extrinsics", + &inherent_data.encode(), + ) + .await?; + } + + for xt in &extrinsics { + run::(externalities, executor, "BlockBuilder_apply_extrinsic", &xt.encode()) + .await?; + } + + let header = dry_run::( + externalities, + executor, + "BlockBuilder_finalize_block", + &[0u8; 0], + ) + .await?; + + run::(externalities, executor, "BlockBuilder_finalize_block", &[0u8; 0]).await?; + + Ok((Block::new(header, extrinsics), (maybe_inherent_data.map(|id| (id, pre_digest))))) +} + +pub(crate) async fn fast_forward( + shared: SharedParams, + command: FastForwardCmd, + block_building_info_provider: Option, +) -> Result<()> +where + Block: BlockT + DeserializeOwned, + Block::Hash: FromStr, + Block::Header: DeserializeOwned, + ::Err: Debug, + NumberFor: FromStr, + as FromStr>::Err: Debug, + HostFns: HostFunctions, + BBIP: BlockBuildingInfoProvider>, +{ + let executor = build_executor::(&shared); + let ext = command.state.into_ext::(&shared, &executor, None, true).await?; + + let mut last_block_hash = ext.block_hash; + let mut last_block_number = + get_block_number::(last_block_hash, command.block_ws_uri()).await?; + let mut prev_block_building_info = None; + + let mut ext = ext.inner_ext; + + for _ in 1..=command.n_blocks.unwrap_or(u64::MAX) { + // We are saving state before we overwrite it while producing new block. + let backend = ext.as_backend(); + + log::info!("Producing new empty block at height {:?}", last_block_number + One::one()); + + let (next_block, new_block_building_info) = next_empty_block::( + &mut ext, + &executor, + last_block_number, + last_block_hash, + &block_building_info_provider, + prev_block_building_info, + ) + .await?; + + log::info!("Produced a new block: {:?}", next_block.header()); + + // And now we restore previous state. + ext.backend = backend; + + let state_root_check = true; + let signature_check = true; + let payload = + (next_block.clone(), state_root_check, signature_check, command.try_state.clone()) + .encode(); + run::(&mut ext, &executor, "TryRuntime_execute_block", &payload).await?; + + log::info!("Executed the new block"); + + prev_block_building_info = new_block_building_info; + last_block_hash = next_block.hash(); + last_block_number += One::one(); + } + + Ok(()) +} diff --git a/utils/frame/try-runtime/cli/src/commands/mod.rs b/utils/frame/try-runtime/cli/src/commands/mod.rs index ab0a06658..81deb2631 100644 --- a/utils/frame/try-runtime/cli/src/commands/mod.rs +++ b/utils/frame/try-runtime/cli/src/commands/mod.rs @@ -17,6 +17,7 @@ pub mod create_snapshot; pub mod execute_block; +pub mod fast_forward; pub mod follow_chain; pub mod offchain_worker; pub mod on_runtime_upgrade; diff --git a/utils/frame/try-runtime/cli/src/lib.rs b/utils/frame/try-runtime/cli/src/lib.rs index dea20febb..1a2f32de5 100644 --- a/utils/frame/try-runtime/cli/src/lib.rs +++ b/utils/frame/try-runtime/cli/src/lib.rs @@ -358,6 +358,7 @@ #![cfg(feature = "try-runtime")] +use crate::block_building_info::BlockBuildingInfoProvider; use parity_scale_codec::Decode; use remote_externalities::{ Builder, Mode, OfflineConfig, OnlineConfig, RemoteExternalities, SnapshotConfig, @@ -381,15 +382,17 @@ use sp_core::{ twox_128, H256, }; use sp_externalities::Extensions; +use sp_inherents::InherentData; use sp_keystore::{testing::KeyStore, KeystoreExt}; use sp_runtime::{ traits::{BlakeTwo256, Block as BlockT, NumberFor}, - DeserializeOwned, + DeserializeOwned, Digest, }; use sp_state_machine::{CompactProof, OverlayedChanges, StateMachine, TrieBackendBuilder}; use sp_version::StateVersion; use std::{fmt::Debug, path::PathBuf, str::FromStr}; +pub mod block_building_info; pub mod commands; pub(crate) mod parse; pub(crate) const LOG_TARGET: &str = "try-runtime::cli"; @@ -445,6 +448,15 @@ pub enum Command { /// tested has remained the same, otherwise block decoding might fail. FollowChain(commands::follow_chain::FollowChainCmd), + /// Produce a series of empty, consecutive blocks and execute them one-by-one. + /// + /// To compare it with [`Command::FollowChain`]: + /// - we don't have the delay of the original blocktime (for Polkadot 6s), but instead, we + /// execute every block immediately + /// - the only data that will be put into blocks are pre-runtime digest items and inherent + /// extrinsics; both things should be defined in your node CLI handling level + FastForward(commands::fast_forward::FastForwardCmd), + /// Create a new snapshot file. CreateSnapshot(commands::create_snapshot::CreateSnapshotCmd), } @@ -719,7 +731,10 @@ impl State { } impl TryRuntimeCmd { - pub async fn run(&self) -> sc_cli::Result<()> + pub async fn run( + &self, + block_building_info_provider: Option, + ) -> sc_cli::Result<()> where Block: BlockT + DeserializeOwned, Block::Header: DeserializeOwned, @@ -729,6 +744,7 @@ impl TryRuntimeCmd { as TryInto>::Error: Debug, NumberFor: FromStr, HostFns: HostFunctions, + BBIP: BlockBuildingInfoProvider>, { match &self.command { Command::OnRuntimeUpgrade(ref cmd) => @@ -755,6 +771,13 @@ impl TryRuntimeCmd { cmd.clone(), ) .await, + Command::FastForward(cmd) => + commands::fast_forward::fast_forward::( + self.shared.clone(), + cmd.clone(), + block_building_info_provider, + ) + .await, Command::CreateSnapshot(cmd) => commands::create_snapshot::create_snapshot::( self.shared.clone(), From 30cb4d10b3118d1b3aa5b2ae7fa8429b2c4f28de Mon Sep 17 00:00:00 2001 From: Dmitry Markin Date: Fri, 17 Feb 2023 19:58:04 +0300 Subject: [PATCH 136/558] Revert "network: Detect early that `NotificationOutSubstream` was closed by the remote (#13396)" (#13409) This reverts commit dfa654d427d73850ec5095194552d5305a01a9a9. --- .../src/protocol/notifications/handler.rs | 7 +- .../notifications/upgrade/notifications.rs | 132 ++---------------- 2 files changed, 12 insertions(+), 127 deletions(-) diff --git a/client/network/src/protocol/notifications/handler.rs b/client/network/src/protocol/notifications/handler.rs index ca87941cb..57561c7b9 100644 --- a/client/network/src/protocol/notifications/handler.rs +++ b/client/network/src/protocol/notifications/handler.rs @@ -782,9 +782,6 @@ impl ConnectionHandler for NotifsHandler { // performed before the code paths that can produce `Ready` (with some rare exceptions). // Importantly, however, the flush is performed *after* notifications are queued with // `Sink::start_send`. - // Note that we must call `poll_flush` on all substreams and not only on those we - // have called `Sink::start_send` on, because `NotificationsOutSubstream::poll_flush` - // also reports the substream termination (even if no data was written into it). for protocol_index in 0..self.protocols.len() { match &mut self.protocols[protocol_index].state { State::Open { out_substream: out_substream @ Some(_), .. } => { @@ -827,7 +824,7 @@ impl ConnectionHandler for NotifsHandler { State::OpenDesiredByRemote { in_substream, pending_opening } => match NotificationsInSubstream::poll_process(Pin::new(in_substream), cx) { Poll::Pending => {}, - Poll::Ready(Ok(())) => {}, + Poll::Ready(Ok(void)) => match void {}, Poll::Ready(Err(_)) => { self.protocols[protocol_index].state = State::Closed { pending_opening: *pending_opening }; @@ -843,7 +840,7 @@ impl ConnectionHandler for NotifsHandler { cx, ) { Poll::Pending => {}, - Poll::Ready(Ok(())) => {}, + Poll::Ready(Ok(void)) => match void {}, Poll::Ready(Err(_)) => *in_substream = None, }, } diff --git a/client/network/src/protocol/notifications/upgrade/notifications.rs b/client/network/src/protocol/notifications/upgrade/notifications.rs index 3621c6349..71afc3c90 100644 --- a/client/network/src/protocol/notifications/upgrade/notifications.rs +++ b/client/network/src/protocol/notifications/upgrade/notifications.rs @@ -41,6 +41,7 @@ use libp2p::core::{upgrade, InboundUpgrade, OutboundUpgrade, UpgradeInfo}; use log::{error, warn}; use sc_network_common::protocol::ProtocolName; use std::{ + convert::Infallible, io, mem, pin::Pin, task::{Context, Poll}, @@ -220,7 +221,10 @@ where /// Equivalent to `Stream::poll_next`, except that it only drives the handshake and is /// guaranteed to not generate any notification. - pub fn poll_process(self: Pin<&mut Self>, cx: &mut Context) -> Poll> { + pub fn poll_process( + self: Pin<&mut Self>, + cx: &mut Context, + ) -> Poll> { let mut this = self.project(); loop { @@ -242,10 +246,8 @@ where }, NotificationsInSubstreamHandshake::Flush => { match Sink::poll_flush(this.socket.as_mut(), cx)? { - Poll::Ready(()) => { - *this.handshake = NotificationsInSubstreamHandshake::Sent; - return Poll::Ready(Ok(())) - }, + Poll::Ready(()) => + *this.handshake = NotificationsInSubstreamHandshake::Sent, Poll::Pending => { *this.handshake = NotificationsInSubstreamHandshake::Flush; return Poll::Pending @@ -258,7 +260,7 @@ where st @ NotificationsInSubstreamHandshake::ClosingInResponseToRemote | st @ NotificationsInSubstreamHandshake::BothSidesClosed => { *this.handshake = st; - return Poll::Ready(Ok(())) + return Poll::Pending }, } } @@ -441,21 +443,6 @@ where fn poll_flush(self: Pin<&mut Self>, cx: &mut Context) -> Poll> { let mut this = self.project(); - - // `Sink::poll_flush` does not expose stream closed error until we write something into - // the stream, so the code below makes sure we detect that the substream was closed - // even if we don't write anything into it. - match Stream::poll_next(this.socket.as_mut(), cx) { - Poll::Pending => {}, - Poll::Ready(Some(_)) => { - error!( - target: "sub-libp2p", - "Unexpected incoming data in `NotificationsOutSubstream`", - ); - }, - Poll::Ready(None) => return Poll::Ready(Err(NotificationsOutError::Terminated)), - } - Sink::poll_flush(this.socket.as_mut(), cx).map_err(NotificationsOutError::Io) } @@ -505,21 +492,13 @@ pub enum NotificationsOutError { /// I/O error on the substream. #[error(transparent)] Io(#[from] io::Error), - - /// End of incoming data detected on out substream. - #[error("substream was closed/reset")] - Terminated, } #[cfg(test)] mod tests { - use super::{ - NotificationsIn, NotificationsInOpen, NotificationsOut, NotificationsOutError, - NotificationsOutOpen, - }; - use futures::{channel::oneshot, future, prelude::*}; + use super::{NotificationsIn, NotificationsInOpen, NotificationsOut, NotificationsOutOpen}; + use futures::{channel::oneshot, prelude::*}; use libp2p::core::upgrade; - use std::{pin::Pin, task::Poll}; use tokio::net::{TcpListener, TcpStream}; use tokio_util::compat::TokioAsyncReadCompatExt; @@ -712,95 +691,4 @@ mod tests { client.await.unwrap(); } - - #[tokio::test] - async fn send_handshake_without_polling_for_incoming_data() { - const PROTO_NAME: &str = "/test/proto/1"; - let (listener_addr_tx, listener_addr_rx) = oneshot::channel(); - - let client = tokio::spawn(async move { - let socket = TcpStream::connect(listener_addr_rx.await.unwrap()).await.unwrap(); - let NotificationsOutOpen { handshake, .. } = upgrade::apply_outbound( - socket.compat(), - NotificationsOut::new(PROTO_NAME, Vec::new(), &b"initial message"[..], 1024 * 1024), - upgrade::Version::V1, - ) - .await - .unwrap(); - - assert_eq!(handshake, b"hello world"); - }); - - let listener = TcpListener::bind("127.0.0.1:0").await.unwrap(); - listener_addr_tx.send(listener.local_addr().unwrap()).unwrap(); - - let (socket, _) = listener.accept().await.unwrap(); - let NotificationsInOpen { handshake, mut substream, .. } = upgrade::apply_inbound( - socket.compat(), - NotificationsIn::new(PROTO_NAME, Vec::new(), 1024 * 1024), - ) - .await - .unwrap(); - - assert_eq!(handshake, b"initial message"); - substream.send_handshake(&b"hello world"[..]); - - // Actually send the handshake. - future::poll_fn(|cx| Pin::new(&mut substream).poll_process(cx)).await.unwrap(); - - client.await.unwrap(); - } - - #[tokio::test] - async fn can_detect_dropped_out_substream_without_writing_data() { - const PROTO_NAME: &str = "/test/proto/1"; - let (listener_addr_tx, listener_addr_rx) = oneshot::channel(); - - let client = tokio::spawn(async move { - let socket = TcpStream::connect(listener_addr_rx.await.unwrap()).await.unwrap(); - let NotificationsOutOpen { handshake, mut substream, .. } = upgrade::apply_outbound( - socket.compat(), - NotificationsOut::new(PROTO_NAME, Vec::new(), &b"initial message"[..], 1024 * 1024), - upgrade::Version::V1, - ) - .await - .unwrap(); - - assert_eq!(handshake, b"hello world"); - - future::poll_fn(|cx| match Pin::new(&mut substream).poll_flush(cx) { - Poll::Pending => Poll::Pending, - Poll::Ready(Ok(())) => { - cx.waker().wake_by_ref(); - Poll::Pending - }, - Poll::Ready(Err(e)) => { - assert!(matches!(e, NotificationsOutError::Terminated)); - Poll::Ready(()) - }, - }) - .await; - }); - - let listener = TcpListener::bind("127.0.0.1:0").await.unwrap(); - listener_addr_tx.send(listener.local_addr().unwrap()).unwrap(); - - let (socket, _) = listener.accept().await.unwrap(); - let NotificationsInOpen { handshake, mut substream, .. } = upgrade::apply_inbound( - socket.compat(), - NotificationsIn::new(PROTO_NAME, Vec::new(), 1024 * 1024), - ) - .await - .unwrap(); - - assert_eq!(handshake, b"initial message"); - - // Send the handhsake. - substream.send_handshake(&b"hello world"[..]); - future::poll_fn(|cx| Pin::new(&mut substream).poll_process(cx)).await.unwrap(); - - drop(substream); - - client.await.unwrap(); - } } From 7ffae37f5c5eca019ca8c5448c97767aace0c9fa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bastian=20K=C3=B6cher?= Date: Sat, 18 Feb 2023 17:59:08 +0100 Subject: [PATCH 137/558] state-db: Print warning when using large pruning window on RocksDb (#13414) * state-db: Print warning when using large pruning window on RocksDb This pr changes state-db to print a warning when using a large pruning window and running with a database that isn't supporting ref-counting like RocksDb. This makes the user aware of potential out of memory errors because this option together with RocksDb etc puts the entire pruning window into memory. Besides that the pr introduces `LOG_TARGET` for having the target declared central! * Review comments --- client/state-db/src/lib.rs | 4 ++- client/state-db/src/noncanonical.rs | 47 ++++++++++++++++++++++------- client/state-db/src/pruning.rs | 38 +++++++++++++++++++---- 3 files changed, 71 insertions(+), 18 deletions(-) diff --git a/client/state-db/src/lib.rs b/client/state-db/src/lib.rs index 6186a45f3..b6106ee81 100644 --- a/client/state-db/src/lib.rs +++ b/client/state-db/src/lib.rs @@ -56,6 +56,8 @@ use std::{ fmt, }; +const LOG_TARGET: &str = "state-db"; +const LOG_TARGET_PIN: &str = "state-db::pin"; const PRUNING_MODE: &[u8] = b"mode"; const PRUNING_MODE_ARCHIVE: &[u8] = b"archive"; const PRUNING_MODE_ARCHIVE_CANON: &[u8] = b"archive_canonical"; @@ -309,7 +311,7 @@ impl StateDbSync { ref_counting: bool, db: D, ) -> Result, Error> { - trace!(target: "state-db", "StateDb settings: {:?}. Ref-counting: {}", mode, ref_counting); + trace!(target: LOG_TARGET, "StateDb settings: {:?}. Ref-counting: {}", mode, ref_counting); let non_canonical: NonCanonicalOverlay = NonCanonicalOverlay::new(&db)?; let pruning: Option> = match mode { diff --git a/client/state-db/src/noncanonical.rs b/client/state-db/src/noncanonical.rs index e950d245d..d6cc49a3d 100644 --- a/client/state-db/src/noncanonical.rs +++ b/client/state-db/src/noncanonical.rs @@ -20,6 +20,8 @@ //! Maintains trees of block overlays and allows discarding trees/roots //! The overlays are added in `insert` and removed in `canonicalize`. +use crate::{LOG_TARGET, LOG_TARGET_PIN}; + use super::{to_meta_key, ChangeSet, CommitSet, DBValue, Error, Hash, MetaDb, StateDbError}; use codec::{Decode, Encode}; use log::trace; @@ -178,7 +180,12 @@ impl NonCanonicalOverlay { let mut values = HashMap::new(); if let Some((ref hash, mut block)) = last_canonicalized { // read the journal - trace!(target: "state-db", "Reading uncanonicalized journal. Last canonicalized #{} ({:?})", block, hash); + trace!( + target: LOG_TARGET, + "Reading uncanonicalized journal. Last canonicalized #{} ({:?})", + block, + hash + ); let mut total: u64 = 0; block += 1; loop { @@ -198,7 +205,7 @@ impl NonCanonicalOverlay { }; insert_values(&mut values, record.inserted); trace!( - target: "state-db", + target: LOG_TARGET, "Uncanonicalized journal entry {}.{} ({:?}) ({} inserted, {} deleted)", block, index, @@ -217,7 +224,11 @@ impl NonCanonicalOverlay { levels.push_back(level); block += 1; } - trace!(target: "state-db", "Finished reading uncanonicalized journal, {} entries", total); + trace!( + target: LOG_TARGET, + "Finished reading uncanonicalized journal, {} entries", + total + ); } Ok(NonCanonicalOverlay { last_canonicalized, @@ -252,7 +263,9 @@ impl NonCanonicalOverlay { } else if self.last_canonicalized.is_some() { if number < front_block_number || number > front_block_number + self.levels.len() as u64 { - trace!(target: "state-db", "Failed to insert block {}, current is {} .. {})", + trace!( + target: LOG_TARGET, + "Failed to insert block {}, current is {} .. {})", number, front_block_number, front_block_number + self.levels.len() as u64, @@ -284,7 +297,7 @@ impl NonCanonicalOverlay { if level.blocks.len() >= MAX_BLOCKS_PER_LEVEL as usize { trace!( - target: "state-db", + target: LOG_TARGET, "Too many sibling blocks at #{number}: {:?}", level.blocks.iter().map(|b| &b.hash).collect::>() ); @@ -314,7 +327,15 @@ impl NonCanonicalOverlay { deleted: changeset.deleted, }; commit.meta.inserted.push((journal_key, journal_record.encode())); - trace!(target: "state-db", "Inserted uncanonicalized changeset {}.{} {:?} ({} inserted, {} deleted)", number, index, hash, journal_record.inserted.len(), journal_record.deleted.len()); + trace!( + target: LOG_TARGET, + "Inserted uncanonicalized changeset {}.{} {:?} ({} inserted, {} deleted)", + number, + index, + hash, + journal_record.inserted.len(), + journal_record.deleted.len() + ); insert_values(&mut self.values, journal_record.inserted); Ok(commit) } @@ -368,7 +389,7 @@ impl NonCanonicalOverlay { hash: &BlockHash, commit: &mut CommitSet, ) -> Result { - trace!(target: "state-db", "Canonicalizing {:?}", hash); + trace!(target: LOG_TARGET, "Canonicalizing {:?}", hash); let level = match self.levels.pop_front() { Some(level) => level, None => return Err(StateDbError::InvalidBlock), @@ -432,7 +453,7 @@ impl NonCanonicalOverlay { .meta .inserted .push((to_meta_key(LAST_CANONICAL, &()), canonicalized.encode())); - trace!(target: "state-db", "Discarding {} records", commit.meta.deleted.len()); + trace!(target: LOG_TARGET, "Discarding {} records", commit.meta.deleted.len()); let num = canonicalized.1; self.last_canonicalized = Some(canonicalized); @@ -479,7 +500,7 @@ impl NonCanonicalOverlay { }; // Check that it does not have any children if (level_index != level_count - 1) && self.parents.values().any(|h| h == hash) { - log::debug!(target: "state-db", "Trying to remove block {:?} with children", hash); + log::debug!(target: LOG_TARGET, "Trying to remove block {:?} with children", hash); return None } let overlay = level.remove(index); @@ -502,7 +523,7 @@ impl NonCanonicalOverlay { pub fn pin(&mut self, hash: &BlockHash) { let refs = self.pinned.entry(hash.clone()).or_default(); if *refs == 0 { - trace!(target: "state-db-pin", "Pinned non-canon block: {:?}", hash); + trace!(target: LOG_TARGET_PIN, "Pinned non-canon block: {:?}", hash); } *refs += 1; } @@ -531,7 +552,11 @@ impl NonCanonicalOverlay { entry.get_mut().1 -= 1; if entry.get().1 == 0 { let (inserted, _) = entry.remove(); - trace!(target: "state-db-pin", "Discarding unpinned non-canon block: {:?}", hash); + trace!( + target: LOG_TARGET_PIN, + "Discarding unpinned non-canon block: {:?}", + hash + ); discard_values(&mut self.values, inserted); self.parents.remove(&hash); } diff --git a/client/state-db/src/pruning.rs b/client/state-db/src/pruning.rs index d942fb242..16561bbe0 100644 --- a/client/state-db/src/pruning.rs +++ b/client/state-db/src/pruning.rs @@ -26,7 +26,7 @@ use crate::{ noncanonical::LAST_CANONICAL, to_meta_key, CommitSet, Error, Hash, MetaDb, StateDbError, - DEFAULT_MAX_BLOCK_CONSTRAINT, + DEFAULT_MAX_BLOCK_CONSTRAINT, LOG_TARGET, }; use codec::{Decode, Encode}; use log::trace; @@ -79,14 +79,24 @@ impl DeathRowQueue { death_index: HashMap::new(), }; // read the journal - trace!(target: "state-db", "Reading pruning journal for the memory queue. Pending #{}", base); + trace!( + target: LOG_TARGET, + "Reading pruning journal for the memory queue. Pending #{}", + base, + ); loop { let journal_key = to_journal_key(block); match db.get_meta(&journal_key).map_err(Error::Db)? { Some(record) => { let record: JournalRecord = Decode::decode(&mut record.as_slice())?; - trace!(target: "state-db", "Pruning journal entry {} ({} inserted, {} deleted)", block, record.inserted.len(), record.deleted.len()); + trace!( + target: LOG_TARGET, + "Pruning journal entry {} ({} inserted, {} deleted)", + block, + record.inserted.len(), + record.deleted.len(), + ); queue.import(base, block, record); }, None => break, @@ -107,7 +117,11 @@ impl DeathRowQueue { // limit the cache capacity from 1 to `DEFAULT_MAX_BLOCK_CONSTRAINT` let cache_capacity = window_size.clamp(1, DEFAULT_MAX_BLOCK_CONSTRAINT) as usize; let mut cache = VecDeque::with_capacity(cache_capacity); - trace!(target: "state-db", "Reading pruning journal for the database-backed queue. Pending #{}", base); + trace!( + target: LOG_TARGET, + "Reading pruning journal for the database-backed queue. Pending #{}", + base + ); DeathRowQueue::load_batch_from_db(&db, &mut cache, base, cache_capacity)?; Ok(DeathRowQueue::DbBacked { db, cache, cache_capacity, last }) } @@ -115,13 +129,13 @@ impl DeathRowQueue { /// import a new block to the back of the queue fn import(&mut self, base: u64, num: u64, journal_record: JournalRecord) { let JournalRecord { hash, inserted, deleted } = journal_record; - trace!(target: "state-db", "Importing {}, base={}", num, base); + trace!(target: LOG_TARGET, "Importing {}, base={}", num, base); match self { DeathRowQueue::DbBacked { cache, cache_capacity, last, .. } => { // If the new block continues cached range and there is space, load it directly into // cache. if num == base + cache.len() as u64 && cache.len() < *cache_capacity { - trace!(target: "state-db", "Adding to DB backed cache {:?} (#{})", hash, num); + trace!(target: LOG_TARGET, "Adding to DB backed cache {:?} (#{})", hash, num); cache.push_back(DeathRow { hash, deleted: deleted.into_iter().collect() }); } *last = Some(num); @@ -306,6 +320,18 @@ impl RefWindow { }; let queue = if count_insertions { + // Highly scientific crafted number for deciding when to print the warning! + // + // Rocksdb doesn't support refcounting and requires that we load the entire pruning + // window into the memory. + if window_size > 1000 { + log::warn!( + target: LOG_TARGET, + "Large pruning window of {window_size} detected! THIS CAN LEAD TO HIGH MEMORY USAGE AND CRASHES. \ + Reduce the pruning window or switch your database to paritydb." + ); + } + DeathRowQueue::new_mem(&db, base)? } else { let last = match last_canonicalized_number { From 76208c89d77705b608776df069c200c36feba02f Mon Sep 17 00:00:00 2001 From: Dmitry Markin Date: Mon, 20 Feb 2023 15:08:02 +0300 Subject: [PATCH 138/558] Use async/await instead of manual polling of `NetworkWorker` (#13219) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Convert `NetworkWorker::poll()` into async `next_action()` * Use `NetworkWorker::next_action` instead of `poll` in `sc-network-test` * Revert "Use `NetworkWorker::next_action` instead of `poll` in `sc-network-test`" This reverts commit 4b5d851ec864f78f9d083a18a618fbe117c896d2. * Fix `sc-network-test` to poll `NetworkWorker::next_action` * Fix `sc_network::service` tests to poll `NetworkWorker::next_action` * Fix docs * kick CI * Factor out `next_worker_message()` & `next_swarm_event()` * Error handling: replace `futures::pending!()` with `expect()` * Simplify stream polling in `select!` * Replace `NetworkWorker::next_action()` with `run()` * Apply suggestions from code review Co-authored-by: Bastian Köcher * minor: comment * Apply suggestions from code review Co-authored-by: Bastian Köcher * Print debug log when network future is shut down * Evaluate `NetworkWorker::run()` future once before the loop * Fix client code to match new `NetworkService` interfaces * Make clippy happy * Apply suggestions from code review Co-authored-by: Bastian Köcher * Apply suggestions from code review Co-authored-by: Bastian Köcher * Revert "Apply suggestions from code review" This reverts commit 9fa646d0ed613e5f8623d3d37d1d59ec0a535850. * Make `NetworkWorker::run()` consume `self` * Terminate system RPC future if RPC rx stream has terminated. * Rewrite with let-else * Fix comments * Get `best_seen_block` and call `on_block_finalized` via `ChainSync` instead of `NetworkService` * rustfmt * make clippy happy * Tests: schedule wake if `next_action()` returned true * minor: comment * minor: fix `NetworkWorker` rustdoc * minor: amend the rustdoc * Fix bug that caused `on_demand_beefy_justification_sync` test to hang * rustfmt * Apply review suggestions --------- Co-authored-by: Bastian Köcher --- .../authority-discovery/src/worker/tests.rs | 4 + client/network/common/src/service.rs | 11 +- client/network/src/service.rs | 1190 +++++++++-------- .../network/src/service/tests/chain_sync.rs | 36 +- client/network/src/service/tests/mod.rs | 5 +- client/network/sync/src/lib.rs | 6 + client/network/sync/src/service/chain_sync.rs | 22 +- client/network/test/src/lib.rs | 17 +- client/offchain/src/api.rs | 4 + client/offchain/src/lib.rs | 4 + client/service/src/builder.rs | 30 +- client/service/src/lib.rs | 263 ++-- 12 files changed, 853 insertions(+), 739 deletions(-) diff --git a/client/authority-discovery/src/worker/tests.rs b/client/authority-discovery/src/worker/tests.rs index ce55728a1..7f3d113a8 100644 --- a/client/authority-discovery/src/worker/tests.rs +++ b/client/authority-discovery/src/worker/tests.rs @@ -184,6 +184,10 @@ impl NetworkStateInfo for TestNetwork { fn external_addresses(&self) -> Vec { self.external_addresses.clone() } + + fn listen_addresses(&self) -> Vec { + self.external_addresses.clone() + } } struct TestSigner<'a> { diff --git a/client/network/common/src/service.rs b/client/network/common/src/service.rs index 54d254eac..f0f307800 100644 --- a/client/network/common/src/service.rs +++ b/client/network/common/src/service.rs @@ -180,13 +180,13 @@ pub trait NetworkPeers { /// purposes. fn deny_unreserved_peers(&self); - /// Adds a `PeerId` and its `Multiaddr` as reserved. + /// Adds a `PeerId` and its `Multiaddr` as reserved for a sync protocol (default peer set). /// /// Returns an `Err` if the given string is not a valid multiaddress /// or contains an invalid peer ID (which includes the local peer ID). fn add_reserved_peer(&self, peer: MultiaddrWithPeerId) -> Result<(), String>; - /// Removes a `PeerId` from the list of reserved peers. + /// Removes a `PeerId` from the list of reserved peers for a sync protocol (default peer set). fn remove_reserved_peer(&self, peer_id: PeerId); /// Sets the reserved set of a protocol to the given set of peers. @@ -359,6 +359,9 @@ pub trait NetworkStateInfo { /// Returns the local external addresses. fn external_addresses(&self) -> Vec; + /// Returns the listening addresses (without trailing `/p2p/` with our `PeerId`). + fn listen_addresses(&self) -> Vec; + /// Returns the local Peer ID. fn local_peer_id(&self) -> PeerId; } @@ -372,6 +375,10 @@ where T::external_addresses(self) } + fn listen_addresses(&self) -> Vec { + T::listen_addresses(self) + } + fn local_peer_id(&self) -> PeerId { T::local_peer_id(self) } diff --git a/client/network/src/service.rs b/client/network/src/service.rs index dbe148c89..3c8856eaf 100644 --- a/client/network/src/service.rs +++ b/client/network/src/service.rs @@ -19,13 +19,13 @@ //! Main entry point of the sc-network crate. //! //! There are two main structs in this module: [`NetworkWorker`] and [`NetworkService`]. -//! The [`NetworkWorker`] *is* the network and implements the `Future` trait. It must be polled in -//! order for the network to advance. +//! The [`NetworkWorker`] *is* the network. Network is driven by [`NetworkWorker::run`] future that +//! terminates only when all instances of the control handles [`NetworkService`] were dropped. //! The [`NetworkService`] is merely a shared version of the [`NetworkWorker`]. You can obtain an //! `Arc` by calling [`NetworkWorker::service`]. //! //! The methods of the [`NetworkService`] are implemented by sending a message over a channel, -//! which is then processed by [`NetworkWorker::poll`]. +//! which is then processed by [`NetworkWorker::next_action`]. use crate::{ behaviour::{self, Behaviour, BehaviourOut}, @@ -46,8 +46,9 @@ use libp2p::{ multiaddr, ping::Failure as PingFailure, swarm::{ - AddressScore, ConnectionError, ConnectionLimits, DialError, Executor, NetworkBehaviour, - PendingConnectionError, Swarm, SwarmBuilder, SwarmEvent, + AddressScore, ConnectionError, ConnectionHandler, ConnectionLimits, DialError, Executor, + IntoConnectionHandler, NetworkBehaviour, PendingConnectionError, Swarm, SwarmBuilder, + SwarmEvent, }, Multiaddr, PeerId, }; @@ -87,7 +88,6 @@ use std::{ atomic::{AtomicBool, AtomicUsize, Ordering}, Arc, }, - task::Poll, }; pub use behaviour::{InboundFailure, OutboundFailure, ResponseFailure}; @@ -100,12 +100,20 @@ mod tests; pub use libp2p::identity::{error::DecodingError, Keypair, PublicKey}; use sc_network_common::service::{NetworkBlock, NetworkRequest}; +/// Custom error that can be produced by the [`ConnectionHandler`] of the [`NetworkBehaviour`]. +/// Used as a template parameter of [`SwarmEvent`] below. +type ConnectionHandlerErr = + <<::ConnectionHandler as IntoConnectionHandler> + ::Handler as ConnectionHandler>::Error; + /// Substrate network service. Handles network IO and manages connectivity. pub struct NetworkService { /// Number of peers we're connected to. num_connected: Arc, /// The local external addresses. external_addresses: Arc>>, + /// Listen addresses. Do **NOT** include a trailing `/p2p/` with our `PeerId`. + listen_addresses: Arc>>, /// Are we actively catching up with the chain? is_major_syncing: Arc, /// Local copy of the `PeerId` of the local node. @@ -434,11 +442,13 @@ where } let external_addresses = Arc::new(Mutex::new(Vec::new())); + let listen_addresses = Arc::new(Mutex::new(Vec::new())); let peers_notifications_sinks = Arc::new(Mutex::new(HashMap::new())); let service = Arc::new(NetworkService { bandwidth, external_addresses: external_addresses.clone(), + listen_addresses: listen_addresses.clone(), num_connected: num_connected.clone(), is_major_syncing: is_major_syncing.clone(), peerset: peerset_handle, @@ -455,6 +465,7 @@ where Ok(NetworkWorker { external_addresses, + listen_addresses, num_connected, is_major_syncing, network_service: swarm, @@ -711,6 +722,34 @@ impl NetworkService { } } + /// Get connected peers debug information. + /// + /// Returns an error if the `NetworkWorker` is no longer running. + pub async fn peers_debug_info(&self) -> Result)>, ()> { + let (tx, rx) = oneshot::channel(); + + let _ = self + .to_worker + .unbounded_send(ServiceToWorkerMsg::PeersDebugInfo { pending_response: tx }); + + // The channel can only be closed if the network worker no longer exists. + rx.await.map_err(|_| ()) + } + + /// Get the list of reserved peers. + /// + /// Returns an error if the `NetworkWorker` is no longer running. + pub async fn reserved_peers(&self) -> Result, ()> { + let (tx, rx) = oneshot::channel(); + + let _ = self + .to_worker + .unbounded_send(ServiceToWorkerMsg::ReservedPeers { pending_response: tx }); + + // The channel can only be closed if the network worker no longer exists. + rx.await.map_err(|_| ()) + } + /// Utility function to extract `PeerId` from each `Multiaddr` for peer set updates. /// /// Returns an `Err` if one of the given addresses is invalid or contains an @@ -774,6 +813,11 @@ where self.external_addresses.lock().clone() } + /// Returns the listener addresses (without trailing `/p2p/` with our `PeerId`). + fn listen_addresses(&self) -> Vec { + self.listen_addresses.lock().clone() + } + /// Returns the local Peer ID. fn local_peer_id(&self) -> PeerId { self.local_peer_id @@ -1243,6 +1287,12 @@ enum ServiceToWorkerMsg { }, DisconnectPeer(PeerId, ProtocolName), NewBestBlockImported(B::Hash, NumberFor), + PeersDebugInfo { + pending_response: oneshot::Sender)>>, + }, + ReservedPeers { + pending_response: oneshot::Sender>, + }, } /// Main network worker. Must be polled in order for the network to advance. @@ -1258,6 +1308,8 @@ where /// Updated by the `NetworkWorker` and loaded by the `NetworkService`. external_addresses: Arc>>, /// Updated by the `NetworkWorker` and loaded by the `NetworkService`. + listen_addresses: Arc>>, + /// Updated by the `NetworkWorker` and loaded by the `NetworkService`. num_connected: Arc, /// Updated by the `NetworkWorker` and loaded by the `NetworkService`. is_major_syncing: Arc, @@ -1281,637 +1333,595 @@ where _marker: PhantomData, } -impl Future for NetworkWorker +impl NetworkWorker where B: BlockT + 'static, H: ExHashT, Client: HeaderBackend + 'static, { - type Output = (); - - fn poll(mut self: Pin<&mut Self>, cx: &mut std::task::Context) -> Poll { - let this = &mut *self; - - // At the time of writing of this comment, due to a high volume of messages, the network - // worker sometimes takes a long time to process the loop below. When that happens, the - // rest of the polling is frozen. In order to avoid negative side-effects caused by this - // freeze, a limit to the number of iterations is enforced below. If the limit is reached, - // the task is interrupted then scheduled again. - // - // This allows for a more even distribution in the time taken by each sub-part of the - // polling. - let mut num_iterations = 0; - loop { - num_iterations += 1; - if num_iterations >= 100 { - cx.waker().wake_by_ref(); - break - } + /// Run the network. + pub async fn run(mut self) { + while self.next_action().await {} + } - // Process the next message coming from the `NetworkService`. - let msg = match this.from_service.poll_next_unpin(cx) { - Poll::Ready(Some(msg)) => msg, - Poll::Ready(None) => return Poll::Ready(()), - Poll::Pending => break, - }; - match msg { - ServiceToWorkerMsg::AnnounceBlock(hash, data) => this - .network_service - .behaviour_mut() - .user_protocol_mut() - .announce_block(hash, data), - ServiceToWorkerMsg::GetValue(key) => - this.network_service.behaviour_mut().get_value(key), - ServiceToWorkerMsg::PutValue(key, value) => - this.network_service.behaviour_mut().put_value(key, value), - ServiceToWorkerMsg::SetReservedOnly(reserved_only) => this - .network_service - .behaviour_mut() - .user_protocol_mut() - .set_reserved_only(reserved_only), - ServiceToWorkerMsg::SetReserved(peers) => this - .network_service - .behaviour_mut() - .user_protocol_mut() - .set_reserved_peers(peers), - ServiceToWorkerMsg::SetPeersetReserved(protocol, peers) => this - .network_service - .behaviour_mut() - .user_protocol_mut() - .set_reserved_peerset_peers(protocol, peers), - ServiceToWorkerMsg::AddReserved(peer_id) => this - .network_service - .behaviour_mut() - .user_protocol_mut() - .add_reserved_peer(peer_id), - ServiceToWorkerMsg::RemoveReserved(peer_id) => this - .network_service - .behaviour_mut() - .user_protocol_mut() - .remove_reserved_peer(peer_id), - ServiceToWorkerMsg::AddSetReserved(protocol, peer_id) => this - .network_service - .behaviour_mut() - .user_protocol_mut() - .add_set_reserved_peer(protocol, peer_id), - ServiceToWorkerMsg::RemoveSetReserved(protocol, peer_id) => this - .network_service - .behaviour_mut() - .user_protocol_mut() - .remove_set_reserved_peer(protocol, peer_id), - ServiceToWorkerMsg::AddKnownAddress(peer_id, addr) => - this.network_service.behaviour_mut().add_known_address(peer_id, addr), - ServiceToWorkerMsg::AddToPeersSet(protocol, peer_id) => this - .network_service - .behaviour_mut() - .user_protocol_mut() - .add_to_peers_set(protocol, peer_id), - ServiceToWorkerMsg::RemoveFromPeersSet(protocol, peer_id) => this - .network_service - .behaviour_mut() - .user_protocol_mut() - .remove_from_peers_set(protocol, peer_id), - ServiceToWorkerMsg::EventStream(sender) => this.event_streams.push(sender), - ServiceToWorkerMsg::Request { - target, - protocol, - request, - pending_response, - connect, - } => { - this.network_service.behaviour_mut().send_request( - &target, - &protocol, - request, - pending_response, - connect, - ); - }, - ServiceToWorkerMsg::NetworkStatus { pending_response } => { - let _ = pending_response.send(Ok(this.status())); - }, - ServiceToWorkerMsg::NetworkState { pending_response } => { - let _ = pending_response.send(Ok(this.network_state())); - }, - ServiceToWorkerMsg::DisconnectPeer(who, protocol_name) => this - .network_service - .behaviour_mut() - .user_protocol_mut() - .disconnect_peer(&who, protocol_name), - ServiceToWorkerMsg::NewBestBlockImported(hash, number) => this - .network_service - .behaviour_mut() - .user_protocol_mut() - .new_best_block_imported(hash, number), - } + /// Perform one action on the network. + /// + /// Returns `false` when the worker should be shutdown. + /// Use in tests only. + pub async fn next_action(&mut self) -> bool { + futures::select! { + // Next message from the service. + msg = self.from_service.next() => { + if let Some(msg) = msg { + self.handle_worker_message(msg); + } else { + return false + } + }, + // Next event from `Swarm` (the stream guaranteed to never terminate). + event = self.network_service.select_next_some() => { + self.handle_swarm_event(event); + }, + }; + + let num_connected_peers = + self.network_service.behaviour_mut().user_protocol_mut().num_connected_peers(); + + // Update the variables shared with the `NetworkService`. + self.num_connected.store(num_connected_peers, Ordering::Relaxed); + { + let external_addresses = + self.network_service.external_addresses().map(|r| &r.addr).cloned().collect(); + *self.external_addresses.lock() = external_addresses; + + let listen_addresses = + self.network_service.listeners().map(ToOwned::to_owned).collect(); + *self.listen_addresses.lock() = listen_addresses; } - // `num_iterations` serves the same purpose as in the previous loop. - // See the previous loop for explanations. - let mut num_iterations = 0; - loop { - num_iterations += 1; - if num_iterations >= 1000 { - cx.waker().wake_by_ref(); - break + let is_major_syncing = self + .network_service + .behaviour_mut() + .user_protocol_mut() + .sync_state() + .state + .is_major_syncing(); + + self.is_major_syncing.store(is_major_syncing, Ordering::Relaxed); + + if let Some(metrics) = self.metrics.as_ref() { + if let Some(buckets) = self.network_service.behaviour_mut().num_entries_per_kbucket() { + for (lower_ilog2_bucket_bound, num_entries) in buckets { + metrics + .kbuckets_num_nodes + .with_label_values(&[&lower_ilog2_bucket_bound.to_string()]) + .set(num_entries as u64); + } + } + if let Some(num_entries) = self.network_service.behaviour_mut().num_kademlia_records() { + metrics.kademlia_records_count.set(num_entries as u64); + } + if let Some(num_entries) = + self.network_service.behaviour_mut().kademlia_records_total_size() + { + metrics.kademlia_records_sizes_total.set(num_entries as u64); } + metrics + .peerset_num_discovered + .set(self.network_service.behaviour_mut().user_protocol().num_discovered_peers() + as u64); + metrics.pending_connections.set( + Swarm::network_info(&self.network_service).connection_counters().num_pending() + as u64, + ); + } - // Process the next action coming from the network. - let next_event = this.network_service.select_next_some(); - futures::pin_mut!(next_event); - let poll_value = next_event.poll_unpin(cx); + true + } - match poll_value { - Poll::Pending => break, - Poll::Ready(SwarmEvent::Behaviour(BehaviourOut::InboundRequest { - protocol, - result, - .. - })) => { - if let Some(metrics) = this.metrics.as_ref() { - match result { - Ok(serve_time) => { - metrics - .requests_in_success_total - .with_label_values(&[&protocol]) - .observe(serve_time.as_secs_f64()); - }, - Err(err) => { - let reason = match err { - ResponseFailure::Network(InboundFailure::Timeout) => "timeout", - ResponseFailure::Network( - InboundFailure::UnsupportedProtocols, - ) => - // `UnsupportedProtocols` is reported for every single - // inbound request whenever a request with an unsupported - // protocol is received. This is not reported in order to - // avoid confusions. - continue, - ResponseFailure::Network(InboundFailure::ResponseOmission) => - "busy-omitted", - ResponseFailure::Network(InboundFailure::ConnectionClosed) => - "connection-closed", - }; + /// Process the next message coming from the `NetworkService`. + fn handle_worker_message(&mut self, msg: ServiceToWorkerMsg) { + match msg { + ServiceToWorkerMsg::AnnounceBlock(hash, data) => self + .network_service + .behaviour_mut() + .user_protocol_mut() + .announce_block(hash, data), + ServiceToWorkerMsg::GetValue(key) => + self.network_service.behaviour_mut().get_value(key), + ServiceToWorkerMsg::PutValue(key, value) => + self.network_service.behaviour_mut().put_value(key, value), + ServiceToWorkerMsg::SetReservedOnly(reserved_only) => self + .network_service + .behaviour_mut() + .user_protocol_mut() + .set_reserved_only(reserved_only), + ServiceToWorkerMsg::SetReserved(peers) => self + .network_service + .behaviour_mut() + .user_protocol_mut() + .set_reserved_peers(peers), + ServiceToWorkerMsg::SetPeersetReserved(protocol, peers) => self + .network_service + .behaviour_mut() + .user_protocol_mut() + .set_reserved_peerset_peers(protocol, peers), + ServiceToWorkerMsg::AddReserved(peer_id) => self + .network_service + .behaviour_mut() + .user_protocol_mut() + .add_reserved_peer(peer_id), + ServiceToWorkerMsg::RemoveReserved(peer_id) => self + .network_service + .behaviour_mut() + .user_protocol_mut() + .remove_reserved_peer(peer_id), + ServiceToWorkerMsg::AddSetReserved(protocol, peer_id) => self + .network_service + .behaviour_mut() + .user_protocol_mut() + .add_set_reserved_peer(protocol, peer_id), + ServiceToWorkerMsg::RemoveSetReserved(protocol, peer_id) => self + .network_service + .behaviour_mut() + .user_protocol_mut() + .remove_set_reserved_peer(protocol, peer_id), + ServiceToWorkerMsg::AddKnownAddress(peer_id, addr) => + self.network_service.behaviour_mut().add_known_address(peer_id, addr), + ServiceToWorkerMsg::AddToPeersSet(protocol, peer_id) => self + .network_service + .behaviour_mut() + .user_protocol_mut() + .add_to_peers_set(protocol, peer_id), + ServiceToWorkerMsg::RemoveFromPeersSet(protocol, peer_id) => self + .network_service + .behaviour_mut() + .user_protocol_mut() + .remove_from_peers_set(protocol, peer_id), + ServiceToWorkerMsg::EventStream(sender) => self.event_streams.push(sender), + ServiceToWorkerMsg::Request { + target, + protocol, + request, + pending_response, + connect, + } => { + self.network_service.behaviour_mut().send_request( + &target, + &protocol, + request, + pending_response, + connect, + ); + }, + ServiceToWorkerMsg::NetworkStatus { pending_response } => { + let _ = pending_response.send(Ok(self.status())); + }, + ServiceToWorkerMsg::NetworkState { pending_response } => { + let _ = pending_response.send(Ok(self.network_state())); + }, + ServiceToWorkerMsg::DisconnectPeer(who, protocol_name) => self + .network_service + .behaviour_mut() + .user_protocol_mut() + .disconnect_peer(&who, protocol_name), + ServiceToWorkerMsg::NewBestBlockImported(hash, number) => self + .network_service + .behaviour_mut() + .user_protocol_mut() + .new_best_block_imported(hash, number), + ServiceToWorkerMsg::PeersDebugInfo { pending_response } => { + let _ = pending_response.send(self.peers_debug_info()); + }, + ServiceToWorkerMsg::ReservedPeers { pending_response } => { + let _ = + pending_response.send(self.reserved_peers().map(ToOwned::to_owned).collect()); + }, + } + } + /// Process the next event coming from `Swarm`. + fn handle_swarm_event( + &mut self, + event: SwarmEvent>>, + ) { + match event { + SwarmEvent::Behaviour(BehaviourOut::InboundRequest { protocol, result, .. }) => { + if let Some(metrics) = self.metrics.as_ref() { + match result { + Ok(serve_time) => { + metrics + .requests_in_success_total + .with_label_values(&[&protocol]) + .observe(serve_time.as_secs_f64()); + }, + Err(err) => { + let reason = match err { + ResponseFailure::Network(InboundFailure::Timeout) => + Some("timeout"), + ResponseFailure::Network(InboundFailure::UnsupportedProtocols) => + // `UnsupportedProtocols` is reported for every single + // inbound request whenever a request with an unsupported + // protocol is received. This is not reported in order to + // avoid confusions. + None, + ResponseFailure::Network(InboundFailure::ResponseOmission) => + Some("busy-omitted"), + ResponseFailure::Network(InboundFailure::ConnectionClosed) => + Some("connection-closed"), + }; + + if let Some(reason) = reason { metrics .requests_in_failure_total .with_label_values(&[&protocol, reason]) .inc(); - }, - } + } + }, } - }, - Poll::Ready(SwarmEvent::Behaviour(BehaviourOut::RequestFinished { - protocol, - duration, - result, - .. - })) => - if let Some(metrics) = this.metrics.as_ref() { - match result { - Ok(_) => { - metrics - .requests_out_success_total - .with_label_values(&[&protocol]) - .observe(duration.as_secs_f64()); - }, - Err(err) => { - let reason = match err { - RequestFailure::NotConnected => "not-connected", - RequestFailure::UnknownProtocol => "unknown-protocol", - RequestFailure::Refused => "refused", - RequestFailure::Obsolete => "obsolete", - RequestFailure::Network(OutboundFailure::DialFailure) => - "dial-failure", - RequestFailure::Network(OutboundFailure::Timeout) => "timeout", - RequestFailure::Network(OutboundFailure::ConnectionClosed) => - "connection-closed", - RequestFailure::Network( - OutboundFailure::UnsupportedProtocols, - ) => "unsupported", - }; + } + }, + SwarmEvent::Behaviour(BehaviourOut::RequestFinished { + protocol, + duration, + result, + .. + }) => + if let Some(metrics) = self.metrics.as_ref() { + match result { + Ok(_) => { + metrics + .requests_out_success_total + .with_label_values(&[&protocol]) + .observe(duration.as_secs_f64()); + }, + Err(err) => { + let reason = match err { + RequestFailure::NotConnected => "not-connected", + RequestFailure::UnknownProtocol => "unknown-protocol", + RequestFailure::Refused => "refused", + RequestFailure::Obsolete => "obsolete", + RequestFailure::Network(OutboundFailure::DialFailure) => + "dial-failure", + RequestFailure::Network(OutboundFailure::Timeout) => "timeout", + RequestFailure::Network(OutboundFailure::ConnectionClosed) => + "connection-closed", + RequestFailure::Network(OutboundFailure::UnsupportedProtocols) => + "unsupported", + }; - metrics - .requests_out_failure_total - .with_label_values(&[&protocol, reason]) - .inc(); - }, - } - }, - Poll::Ready(SwarmEvent::Behaviour(BehaviourOut::ReputationChanges { - peer, - changes, - })) => - for change in changes { - this.network_service.behaviour().user_protocol().report_peer(peer, change); - }, - Poll::Ready(SwarmEvent::Behaviour(BehaviourOut::PeerIdentify { - peer_id, - info: - IdentifyInfo { - protocol_version, - agent_version, - mut listen_addrs, - protocols, - .. + metrics + .requests_out_failure_total + .with_label_values(&[&protocol, reason]) + .inc(); }, - })) => { - if listen_addrs.len() > 30 { - debug!( - target: "sub-libp2p", - "Node {:?} has reported more than 30 addresses; it is identified by {:?} and {:?}", - peer_id, protocol_version, agent_version - ); - listen_addrs.truncate(30); } - for addr in listen_addrs { - this.network_service - .behaviour_mut() - .add_self_reported_address_to_dht(&peer_id, &protocols, addr); - } - this.network_service - .behaviour_mut() - .user_protocol_mut() - .add_default_set_discovered_nodes(iter::once(peer_id)); }, - Poll::Ready(SwarmEvent::Behaviour(BehaviourOut::Discovered(peer_id))) => { - this.network_service - .behaviour_mut() - .user_protocol_mut() - .add_default_set_discovered_nodes(iter::once(peer_id)); + SwarmEvent::Behaviour(BehaviourOut::ReputationChanges { peer, changes }) => + for change in changes { + self.network_service.behaviour().user_protocol().report_peer(peer, change); }, - Poll::Ready(SwarmEvent::Behaviour(BehaviourOut::RandomKademliaStarted)) => - if let Some(metrics) = this.metrics.as_ref() { - metrics.kademlia_random_queries_total.inc(); + SwarmEvent::Behaviour(BehaviourOut::PeerIdentify { + peer_id, + info: + IdentifyInfo { + protocol_version, agent_version, mut listen_addrs, protocols, .. }, - Poll::Ready(SwarmEvent::Behaviour(BehaviourOut::NotificationStreamOpened { + }) => { + if listen_addrs.len() > 30 { + debug!( + target: "sub-libp2p", + "Node {:?} has reported more than 30 addresses; it is identified by {:?} and {:?}", + peer_id, protocol_version, agent_version + ); + listen_addrs.truncate(30); + } + for addr in listen_addrs { + self.network_service + .behaviour_mut() + .add_self_reported_address_to_dht(&peer_id, &protocols, addr); + } + self.network_service + .behaviour_mut() + .user_protocol_mut() + .add_default_set_discovered_nodes(iter::once(peer_id)); + }, + SwarmEvent::Behaviour(BehaviourOut::Discovered(peer_id)) => { + self.network_service + .behaviour_mut() + .user_protocol_mut() + .add_default_set_discovered_nodes(iter::once(peer_id)); + }, + SwarmEvent::Behaviour(BehaviourOut::RandomKademliaStarted) => + if let Some(metrics) = self.metrics.as_ref() { + metrics.kademlia_random_queries_total.inc(); + }, + SwarmEvent::Behaviour(BehaviourOut::NotificationStreamOpened { + remote, + protocol, + negotiated_fallback, + notifications_sink, + role, + }) => { + if let Some(metrics) = self.metrics.as_ref() { + metrics + .notifications_streams_opened_total + .with_label_values(&[&protocol]) + .inc(); + } + { + let mut peers_notifications_sinks = self.peers_notifications_sinks.lock(); + let _previous_value = peers_notifications_sinks + .insert((remote, protocol.clone()), notifications_sink); + debug_assert!(_previous_value.is_none()); + } + self.event_streams.send(Event::NotificationStreamOpened { remote, protocol, negotiated_fallback, - notifications_sink, role, - })) => { - if let Some(metrics) = this.metrics.as_ref() { - metrics - .notifications_streams_opened_total - .with_label_values(&[&protocol]) - .inc(); - } - { - let mut peers_notifications_sinks = this.peers_notifications_sinks.lock(); - let _previous_value = peers_notifications_sinks - .insert((remote, protocol.clone()), notifications_sink); - debug_assert!(_previous_value.is_none()); - } - this.event_streams.send(Event::NotificationStreamOpened { - remote, - protocol, - negotiated_fallback, - role, - }); - }, - Poll::Ready(SwarmEvent::Behaviour(BehaviourOut::NotificationStreamReplaced { - remote, - protocol, - notifications_sink, - })) => { - let mut peers_notifications_sinks = this.peers_notifications_sinks.lock(); - if let Some(s) = peers_notifications_sinks.get_mut(&(remote, protocol)) { - *s = notifications_sink; - } else { - error!( - target: "sub-libp2p", - "NotificationStreamReplaced for non-existing substream" - ); - debug_assert!(false); - } + }); + }, + SwarmEvent::Behaviour(BehaviourOut::NotificationStreamReplaced { + remote, + protocol, + notifications_sink, + }) => { + let mut peers_notifications_sinks = self.peers_notifications_sinks.lock(); + if let Some(s) = peers_notifications_sinks.get_mut(&(remote, protocol)) { + *s = notifications_sink; + } else { + error!( + target: "sub-libp2p", + "NotificationStreamReplaced for non-existing substream" + ); + debug_assert!(false); + } - // TODO: Notifications might have been lost as a result of the previous - // connection being dropped, and as a result it would be preferable to notify - // the users of this fact by simulating the substream being closed then - // reopened. - // The code below doesn't compile because `role` is unknown. Propagating the - // handshake of the secondary connections is quite an invasive change and - // would conflict with https://github.com/paritytech/substrate/issues/6403. - // Considering that dropping notifications is generally regarded as - // acceptable, this bug is at the moment intentionally left there and is - // intended to be fixed at the same time as - // https://github.com/paritytech/substrate/issues/6403. - // this.event_streams.send(Event::NotificationStreamClosed { - // remote, - // protocol, - // }); - // this.event_streams.send(Event::NotificationStreamOpened { - // remote, - // protocol, - // role, - // }); - }, - Poll::Ready(SwarmEvent::Behaviour(BehaviourOut::NotificationStreamClosed { - remote, - protocol, - })) => { - if let Some(metrics) = this.metrics.as_ref() { - metrics - .notifications_streams_closed_total - .with_label_values(&[&protocol[..]]) - .inc(); - } - this.event_streams.send(Event::NotificationStreamClosed { - remote, - protocol: protocol.clone(), - }); - { - let mut peers_notifications_sinks = this.peers_notifications_sinks.lock(); - let _previous_value = peers_notifications_sinks.remove(&(remote, protocol)); - debug_assert!(_previous_value.is_some()); - } - }, - Poll::Ready(SwarmEvent::Behaviour(BehaviourOut::NotificationsReceived { - remote, - messages, - })) => { - if let Some(metrics) = this.metrics.as_ref() { - for (protocol, message) in &messages { - metrics - .notifications_sizes - .with_label_values(&["in", protocol]) - .observe(message.len() as f64); - } - } - this.event_streams.send(Event::NotificationsReceived { remote, messages }); - }, - Poll::Ready(SwarmEvent::Behaviour(BehaviourOut::SyncConnected(remote))) => { - this.event_streams.send(Event::SyncConnected { remote }); - }, - Poll::Ready(SwarmEvent::Behaviour(BehaviourOut::SyncDisconnected(remote))) => { - this.event_streams.send(Event::SyncDisconnected { remote }); - }, - Poll::Ready(SwarmEvent::Behaviour(BehaviourOut::Dht(event, duration))) => { - if let Some(metrics) = this.metrics.as_ref() { - let query_type = match event { - DhtEvent::ValueFound(_) => "value-found", - DhtEvent::ValueNotFound(_) => "value-not-found", - DhtEvent::ValuePut(_) => "value-put", - DhtEvent::ValuePutFailed(_) => "value-put-failed", - }; + // TODO: Notifications might have been lost as a result of the previous + // connection being dropped, and as a result it would be preferable to notify + // the users of this fact by simulating the substream being closed then + // reopened. + // The code below doesn't compile because `role` is unknown. Propagating the + // handshake of the secondary connections is quite an invasive change and + // would conflict with https://github.com/paritytech/substrate/issues/6403. + // Considering that dropping notifications is generally regarded as + // acceptable, this bug is at the moment intentionally left there and is + // intended to be fixed at the same time as + // https://github.com/paritytech/substrate/issues/6403. + // self.event_streams.send(Event::NotificationStreamClosed { + // remote, + // protocol, + // }); + // self.event_streams.send(Event::NotificationStreamOpened { + // remote, + // protocol, + // role, + // }); + }, + SwarmEvent::Behaviour(BehaviourOut::NotificationStreamClosed { remote, protocol }) => { + if let Some(metrics) = self.metrics.as_ref() { + metrics + .notifications_streams_closed_total + .with_label_values(&[&protocol[..]]) + .inc(); + } + self.event_streams + .send(Event::NotificationStreamClosed { remote, protocol: protocol.clone() }); + { + let mut peers_notifications_sinks = self.peers_notifications_sinks.lock(); + let _previous_value = peers_notifications_sinks.remove(&(remote, protocol)); + debug_assert!(_previous_value.is_some()); + } + }, + SwarmEvent::Behaviour(BehaviourOut::NotificationsReceived { remote, messages }) => { + if let Some(metrics) = self.metrics.as_ref() { + for (protocol, message) in &messages { metrics - .kademlia_query_duration - .with_label_values(&[query_type]) - .observe(duration.as_secs_f64()); + .notifications_sizes + .with_label_values(&["in", protocol]) + .observe(message.len() as f64); } + } + self.event_streams.send(Event::NotificationsReceived { remote, messages }); + }, + SwarmEvent::Behaviour(BehaviourOut::SyncConnected(remote)) => { + self.event_streams.send(Event::SyncConnected { remote }); + }, + SwarmEvent::Behaviour(BehaviourOut::SyncDisconnected(remote)) => { + self.event_streams.send(Event::SyncDisconnected { remote }); + }, + SwarmEvent::Behaviour(BehaviourOut::Dht(event, duration)) => { + if let Some(metrics) = self.metrics.as_ref() { + let query_type = match event { + DhtEvent::ValueFound(_) => "value-found", + DhtEvent::ValueNotFound(_) => "value-not-found", + DhtEvent::ValuePut(_) => "value-put", + DhtEvent::ValuePutFailed(_) => "value-put-failed", + }; + metrics + .kademlia_query_duration + .with_label_values(&[query_type]) + .observe(duration.as_secs_f64()); + } - this.event_streams.send(Event::Dht(event)); - }, - Poll::Ready(SwarmEvent::Behaviour(BehaviourOut::None)) => { - // Ignored event from lower layers. - }, - Poll::Ready(SwarmEvent::ConnectionEstablished { - peer_id, - endpoint, - num_established, - concurrent_dial_errors, - }) => { - if let Some(errors) = concurrent_dial_errors { - debug!(target: "sub-libp2p", "Libp2p => Connected({:?}) with errors: {:?}", peer_id, errors); - } else { - debug!(target: "sub-libp2p", "Libp2p => Connected({:?})", peer_id); - } + self.event_streams.send(Event::Dht(event)); + }, + SwarmEvent::Behaviour(BehaviourOut::None) => { + // Ignored event from lower layers. + }, + SwarmEvent::ConnectionEstablished { + peer_id, + endpoint, + num_established, + concurrent_dial_errors, + } => { + if let Some(errors) = concurrent_dial_errors { + debug!(target: "sub-libp2p", "Libp2p => Connected({:?}) with errors: {:?}", peer_id, errors); + } else { + debug!(target: "sub-libp2p", "Libp2p => Connected({:?})", peer_id); + } - if let Some(metrics) = this.metrics.as_ref() { - let direction = match endpoint { - ConnectedPoint::Dialer { .. } => "out", - ConnectedPoint::Listener { .. } => "in", - }; - metrics.connections_opened_total.with_label_values(&[direction]).inc(); + if let Some(metrics) = self.metrics.as_ref() { + let direction = match endpoint { + ConnectedPoint::Dialer { .. } => "out", + ConnectedPoint::Listener { .. } => "in", + }; + metrics.connections_opened_total.with_label_values(&[direction]).inc(); - if num_established.get() == 1 { - metrics.distinct_peers_connections_opened_total.inc(); - } + if num_established.get() == 1 { + metrics.distinct_peers_connections_opened_total.inc(); } - }, - Poll::Ready(SwarmEvent::ConnectionClosed { - peer_id, - cause, - endpoint, - num_established, - }) => { - debug!(target: "sub-libp2p", "Libp2p => Disconnected({:?}, {:?})", peer_id, cause); - if let Some(metrics) = this.metrics.as_ref() { - let direction = match endpoint { - ConnectedPoint::Dialer { .. } => "out", - ConnectedPoint::Listener { .. } => "in", - }; - let reason = match cause { - Some(ConnectionError::IO(_)) => "transport-error", - Some(ConnectionError::Handler(EitherError::A(EitherError::A( - EitherError::B(EitherError::A(PingFailure::Timeout)), - )))) => "ping-timeout", - Some(ConnectionError::Handler(EitherError::A(EitherError::A( - EitherError::A(NotifsHandlerError::SyncNotificationsClogged), - )))) => "sync-notifications-clogged", - Some(ConnectionError::Handler(_)) => "protocol-error", - Some(ConnectionError::KeepAliveTimeout) => "keep-alive-timeout", - None => "actively-closed", - }; - metrics - .connections_closed_total - .with_label_values(&[direction, reason]) - .inc(); + } + }, + SwarmEvent::ConnectionClosed { peer_id, cause, endpoint, num_established } => { + debug!(target: "sub-libp2p", "Libp2p => Disconnected({:?}, {:?})", peer_id, cause); + if let Some(metrics) = self.metrics.as_ref() { + let direction = match endpoint { + ConnectedPoint::Dialer { .. } => "out", + ConnectedPoint::Listener { .. } => "in", + }; + let reason = match cause { + Some(ConnectionError::IO(_)) => "transport-error", + Some(ConnectionError::Handler(EitherError::A(EitherError::A( + EitherError::B(EitherError::A(PingFailure::Timeout)), + )))) => "ping-timeout", + Some(ConnectionError::Handler(EitherError::A(EitherError::A( + EitherError::A(NotifsHandlerError::SyncNotificationsClogged), + )))) => "sync-notifications-clogged", + Some(ConnectionError::Handler(_)) => "protocol-error", + Some(ConnectionError::KeepAliveTimeout) => "keep-alive-timeout", + None => "actively-closed", + }; + metrics.connections_closed_total.with_label_values(&[direction, reason]).inc(); - // `num_established` represents the number of *remaining* connections. - if num_established == 0 { - metrics.distinct_peers_connections_closed_total.inc(); - } - } - }, - Poll::Ready(SwarmEvent::NewListenAddr { address, .. }) => { - trace!(target: "sub-libp2p", "Libp2p => NewListenAddr({})", address); - if let Some(metrics) = this.metrics.as_ref() { - metrics.listeners_local_addresses.inc(); - } - }, - Poll::Ready(SwarmEvent::ExpiredListenAddr { address, .. }) => { - info!(target: "sub-libp2p", "📪 No longer listening on {}", address); - if let Some(metrics) = this.metrics.as_ref() { - metrics.listeners_local_addresses.dec(); + // `num_established` represents the number of *remaining* connections. + if num_established == 0 { + metrics.distinct_peers_connections_closed_total.inc(); } - }, - Poll::Ready(SwarmEvent::OutgoingConnectionError { peer_id, error }) => { - if let Some(peer_id) = peer_id { - trace!( - target: "sub-libp2p", - "Libp2p => Failed to reach {:?}: {}", - peer_id, error, - ); - - if this.boot_node_ids.contains(&peer_id) { - if let DialError::WrongPeerId { obtained, endpoint } = &error { - if let ConnectedPoint::Dialer { address, role_override: _ } = - endpoint - { - warn!( - "💔 The bootnode you want to connect to at `{}` provided a different peer ID `{}` than the one you expect `{}`.", - address, - obtained, - peer_id, - ); - } + } + }, + SwarmEvent::NewListenAddr { address, .. } => { + trace!(target: "sub-libp2p", "Libp2p => NewListenAddr({})", address); + if let Some(metrics) = self.metrics.as_ref() { + metrics.listeners_local_addresses.inc(); + } + }, + SwarmEvent::ExpiredListenAddr { address, .. } => { + info!(target: "sub-libp2p", "📪 No longer listening on {}", address); + if let Some(metrics) = self.metrics.as_ref() { + metrics.listeners_local_addresses.dec(); + } + }, + SwarmEvent::OutgoingConnectionError { peer_id, error } => { + if let Some(peer_id) = peer_id { + trace!( + target: "sub-libp2p", + "Libp2p => Failed to reach {:?}: {}", + peer_id, error, + ); + + if self.boot_node_ids.contains(&peer_id) { + if let DialError::WrongPeerId { obtained, endpoint } = &error { + if let ConnectedPoint::Dialer { address, role_override: _ } = endpoint { + warn!( + "💔 The bootnode you want to connect to at `{}` provided a different peer ID `{}` than the one you expect `{}`.", + address, + obtained, + peer_id, + ); } } } + } - if let Some(metrics) = this.metrics.as_ref() { - let reason = match error { - DialError::ConnectionLimit(_) => Some("limit-reached"), - DialError::InvalidPeerId(_) => Some("invalid-peer-id"), - DialError::Transport(_) | DialError::ConnectionIo(_) => - Some("transport-error"), - DialError::Banned | - DialError::LocalPeerId | - DialError::NoAddresses | - DialError::DialPeerConditionFalse(_) | - DialError::WrongPeerId { .. } | - DialError::Aborted => None, // ignore them - }; - if let Some(reason) = reason { - metrics - .pending_connections_errors_total - .with_label_values(&[reason]) - .inc(); - } - } - }, - Poll::Ready(SwarmEvent::Dialing(peer_id)) => { - trace!(target: "sub-libp2p", "Libp2p => Dialing({:?})", peer_id) - }, - Poll::Ready(SwarmEvent::IncomingConnection { local_addr, send_back_addr }) => { - trace!(target: "sub-libp2p", "Libp2p => IncomingConnection({},{}))", - local_addr, send_back_addr); - if let Some(metrics) = this.metrics.as_ref() { - metrics.incoming_connections_total.inc(); - } - }, - Poll::Ready(SwarmEvent::IncomingConnectionError { - local_addr, - send_back_addr, - error, - }) => { - debug!( - target: "sub-libp2p", - "Libp2p => IncomingConnectionError({},{}): {}", - local_addr, send_back_addr, error, - ); - if let Some(metrics) = this.metrics.as_ref() { - let reason = match error { - PendingConnectionError::ConnectionLimit(_) => Some("limit-reached"), - PendingConnectionError::WrongPeerId { .. } => Some("invalid-peer-id"), - PendingConnectionError::Transport(_) | - PendingConnectionError::IO(_) => Some("transport-error"), - PendingConnectionError::Aborted => None, // ignore it - }; - - if let Some(reason) = reason { - metrics - .incoming_connections_errors_total - .with_label_values(&[reason]) - .inc(); - } + if let Some(metrics) = self.metrics.as_ref() { + let reason = match error { + DialError::ConnectionLimit(_) => Some("limit-reached"), + DialError::InvalidPeerId(_) => Some("invalid-peer-id"), + DialError::Transport(_) | DialError::ConnectionIo(_) => + Some("transport-error"), + DialError::Banned | + DialError::LocalPeerId | + DialError::NoAddresses | + DialError::DialPeerConditionFalse(_) | + DialError::WrongPeerId { .. } | + DialError::Aborted => None, // ignore them + }; + if let Some(reason) = reason { + metrics.pending_connections_errors_total.with_label_values(&[reason]).inc(); } - }, - Poll::Ready(SwarmEvent::BannedPeer { peer_id, endpoint }) => { - debug!( - target: "sub-libp2p", - "Libp2p => BannedPeer({}). Connected via {:?}.", - peer_id, endpoint, - ); - if let Some(metrics) = this.metrics.as_ref() { + } + }, + SwarmEvent::Dialing(peer_id) => { + trace!(target: "sub-libp2p", "Libp2p => Dialing({:?})", peer_id) + }, + SwarmEvent::IncomingConnection { local_addr, send_back_addr } => { + trace!(target: "sub-libp2p", "Libp2p => IncomingConnection({},{}))", + local_addr, send_back_addr); + if let Some(metrics) = self.metrics.as_ref() { + metrics.incoming_connections_total.inc(); + } + }, + SwarmEvent::IncomingConnectionError { local_addr, send_back_addr, error } => { + debug!( + target: "sub-libp2p", + "Libp2p => IncomingConnectionError({},{}): {}", + local_addr, send_back_addr, error, + ); + if let Some(metrics) = self.metrics.as_ref() { + let reason = match error { + PendingConnectionError::ConnectionLimit(_) => Some("limit-reached"), + PendingConnectionError::WrongPeerId { .. } => Some("invalid-peer-id"), + PendingConnectionError::Transport(_) | PendingConnectionError::IO(_) => + Some("transport-error"), + PendingConnectionError::Aborted => None, // ignore it + }; + + if let Some(reason) = reason { metrics .incoming_connections_errors_total - .with_label_values(&["banned"]) + .with_label_values(&[reason]) .inc(); } - }, - Poll::Ready(SwarmEvent::ListenerClosed { reason, addresses, .. }) => { - if let Some(metrics) = this.metrics.as_ref() { - metrics.listeners_local_addresses.sub(addresses.len() as u64); - } - let addrs = - addresses.into_iter().map(|a| a.to_string()).collect::>().join(", "); - match reason { - Ok(()) => error!( - target: "sub-libp2p", - "📪 Libp2p listener ({}) closed gracefully", - addrs - ), - Err(e) => error!( - target: "sub-libp2p", - "📪 Libp2p listener ({}) closed: {}", - addrs, e - ), - } - }, - Poll::Ready(SwarmEvent::ListenerError { error, .. }) => { - debug!(target: "sub-libp2p", "Libp2p => ListenerError: {}", error); - if let Some(metrics) = this.metrics.as_ref() { - metrics.listeners_errors_total.inc(); - } - }, - }; - } - - let num_connected_peers = - this.network_service.behaviour_mut().user_protocol_mut().num_connected_peers(); - - // Update the variables shared with the `NetworkService`. - this.num_connected.store(num_connected_peers, Ordering::Relaxed); - { - let external_addresses = - Swarm::>::external_addresses(&this.network_service) - .map(|r| &r.addr) - .cloned() - .collect(); - *this.external_addresses.lock() = external_addresses; - } - - let is_major_syncing = this - .network_service - .behaviour_mut() - .user_protocol_mut() - .sync_state() - .state - .is_major_syncing(); - - this.is_major_syncing.store(is_major_syncing, Ordering::Relaxed); - - if let Some(metrics) = this.metrics.as_ref() { - if let Some(buckets) = this.network_service.behaviour_mut().num_entries_per_kbucket() { - for (lower_ilog2_bucket_bound, num_entries) in buckets { - metrics - .kbuckets_num_nodes - .with_label_values(&[&lower_ilog2_bucket_bound.to_string()]) - .set(num_entries as u64); } - } - if let Some(num_entries) = this.network_service.behaviour_mut().num_kademlia_records() { - metrics.kademlia_records_count.set(num_entries as u64); - } - if let Some(num_entries) = - this.network_service.behaviour_mut().kademlia_records_total_size() - { - metrics.kademlia_records_sizes_total.set(num_entries as u64); - } - metrics - .peerset_num_discovered - .set(this.network_service.behaviour_mut().user_protocol().num_discovered_peers() - as u64); - metrics.pending_connections.set( - Swarm::network_info(&this.network_service).connection_counters().num_pending() - as u64, - ); + }, + SwarmEvent::BannedPeer { peer_id, endpoint } => { + debug!( + target: "sub-libp2p", + "Libp2p => BannedPeer({}). Connected via {:?}.", + peer_id, endpoint, + ); + if let Some(metrics) = self.metrics.as_ref() { + metrics.incoming_connections_errors_total.with_label_values(&["banned"]).inc(); + } + }, + SwarmEvent::ListenerClosed { reason, addresses, .. } => { + if let Some(metrics) = self.metrics.as_ref() { + metrics.listeners_local_addresses.sub(addresses.len() as u64); + } + let addrs = + addresses.into_iter().map(|a| a.to_string()).collect::>().join(", "); + match reason { + Ok(()) => error!( + target: "sub-libp2p", + "📪 Libp2p listener ({}) closed gracefully", + addrs + ), + Err(e) => error!( + target: "sub-libp2p", + "📪 Libp2p listener ({}) closed: {}", + addrs, e + ), + } + }, + SwarmEvent::ListenerError { error, .. } => { + debug!(target: "sub-libp2p", "Libp2p => ListenerError: {}", error); + if let Some(metrics) = self.metrics.as_ref() { + metrics.listeners_errors_total.inc(); + } + }, } - - Poll::Pending } } diff --git a/client/network/src/service/tests/chain_sync.rs b/client/network/src/service/tests/chain_sync.rs index 9d8463ff1..1ae432fd4 100644 --- a/client/network/src/service/tests/chain_sync.rs +++ b/client/network/src/service/tests/chain_sync.rs @@ -75,12 +75,8 @@ async fn normal_network_poll_no_peers() { .with_chain_sync((chain_sync, chain_sync_service)) .build(); - // poll the network once - futures::future::poll_fn(|cx| { - let _ = network.network().poll_unpin(cx); - Poll::Ready(()) - }) - .await; + // perform one action on network + let _ = network.network().next_action().await; } #[tokio::test] @@ -110,11 +106,8 @@ async fn request_justification() { // send "request justifiction" message and poll the network network.service().request_justification(&hash, number); - futures::future::poll_fn(|cx| { - let _ = network.network().poll_unpin(cx); - Poll::Ready(()) - }) - .await; + // perform one action on network + let _ = network.network().next_action().await; } #[tokio::test] @@ -141,11 +134,8 @@ async fn clear_justification_requests() { // send "request justifiction" message and poll the network network.service().clear_justification_requests(); - futures::future::poll_fn(|cx| { - let _ = network.network().poll_unpin(cx); - Poll::Ready(()) - }) - .await; + // perform one action on network + let _ = network.network().next_action().await; } #[tokio::test] @@ -180,11 +170,8 @@ async fn set_sync_fork_request() { // send "set sync fork request" message and poll the network network.service().set_sync_fork_request(copy_peers, hash, number); - futures::future::poll_fn(|cx| { - let _ = network.network().poll_unpin(cx); - Poll::Ready(()) - }) - .await; + // perform one action on network + let _ = network.network().next_action().await; } #[tokio::test] @@ -225,11 +212,8 @@ async fn on_block_finalized() { // send "set sync fork request" message and poll the network network.network().on_block_finalized(hash, header); - futures::future::poll_fn(|cx| { - let _ = network.network().poll_unpin(cx); - Poll::Ready(()) - }) - .await; + // perform one action on network + let _ = network.network().next_action().await; } // report from mock import queue that importing a justification was not successful diff --git a/client/network/src/service/tests/mod.rs b/client/network/src/service/tests/mod.rs index 9c97a7f73..3ce139ff0 100644 --- a/client/network/src/service/tests/mod.rs +++ b/client/network/src/service/tests/mod.rs @@ -80,10 +80,7 @@ impl TestNetwork { let service = worker.service().clone(); let event_stream = service.event_stream("test"); - tokio::spawn(async move { - futures::pin_mut!(worker); - let _ = worker.await; - }); + tokio::spawn(worker.run()); (service, event_stream) } diff --git a/client/network/sync/src/lib.rs b/client/network/sync/src/lib.rs index ffc0edaf3..f56ef13fc 100644 --- a/client/network/sync/src/lib.rs +++ b/client/network/sync/src/lib.rs @@ -1358,6 +1358,12 @@ where ); } }, + ToServiceCommand::BlockFinalized(hash, number) => { + self.on_block_finalized(&hash, number); + }, + ToServiceCommand::Status { pending_response } => { + let _ = pending_response.send(self.status()); + }, } } diff --git a/client/network/sync/src/service/chain_sync.rs b/client/network/sync/src/service/chain_sync.rs index 50ded5b64..824303ec0 100644 --- a/client/network/sync/src/service/chain_sync.rs +++ b/client/network/sync/src/service/chain_sync.rs @@ -16,9 +16,10 @@ // You should have received a copy of the GNU General Public License // along with this program. If not, see . +use futures::channel::oneshot; use libp2p::PeerId; use sc_consensus::{BlockImportError, BlockImportStatus, JustificationSyncLink, Link}; -use sc_network_common::service::NetworkSyncForkRequest; +use sc_network_common::{service::NetworkSyncForkRequest, sync::SyncStatus}; use sc_utils::mpsc::TracingUnboundedSender; use sp_runtime::traits::{Block as BlockT, NumberFor}; @@ -34,6 +35,10 @@ pub enum ToServiceCommand { Vec<(Result>, BlockImportError>, B::Hash)>, ), JustificationImported(PeerId, B::Hash, NumberFor, bool), + BlockFinalized(B::Hash, NumberFor), + Status { + pending_response: oneshot::Sender>, + }, } /// Handle for communicating with `ChainSync` asynchronously @@ -47,6 +52,21 @@ impl ChainSyncInterfaceHandle { pub fn new(tx: TracingUnboundedSender>) -> Self { Self { tx } } + + /// Notify ChainSync about finalized block + pub fn on_block_finalized(&self, hash: B::Hash, number: NumberFor) { + let _ = self.tx.unbounded_send(ToServiceCommand::BlockFinalized(hash, number)); + } + + /// Get sync status + /// + /// Returns an error if `ChainSync` has terminated. + pub async fn status(&self) -> Result, ()> { + let (tx, rx) = oneshot::channel(); + let _ = self.tx.unbounded_send(ToServiceCommand::Status { pending_response: tx }); + + rx.await.map_err(|_| ()) + } } impl NetworkSyncForkRequest> diff --git a/client/network/test/src/lib.rs b/client/network/test/src/lib.rs index ccaebc976..c47e3c86f 100644 --- a/client/network/test/src/lib.rs +++ b/client/network/test/src/lib.rs @@ -31,7 +31,7 @@ use std::{ time::Duration, }; -use futures::{channel::oneshot, future::BoxFuture, prelude::*}; +use futures::{channel::oneshot, future::BoxFuture, pin_mut, prelude::*}; use libp2p::{build_multiaddr, PeerId}; use log::trace; use parking_lot::Mutex; @@ -83,7 +83,7 @@ use sp_runtime::{ }; use substrate_test_runtime_client::AccountKeyring; pub use substrate_test_runtime_client::{ - runtime::{Block, Extrinsic, Hash, Transfer}, + runtime::{Block, Extrinsic, Hash, Header, Transfer}, TestClient, TestClientBuilder, TestClientBuilderExt, }; use tokio::time::timeout; @@ -1078,8 +1078,17 @@ where self.mut_peers(|peers| { for (i, peer) in peers.iter_mut().enumerate() { trace!(target: "sync", "-- Polling {}: {}", i, peer.id()); - if let Poll::Ready(()) = peer.network.poll_unpin(cx) { - panic!("NetworkWorker has terminated unexpectedly.") + loop { + // The code below is not quite correct, because we are polling a different + // instance of the future every time. But as long as + // `NetworkWorker::next_action()` contains just streams polling not interleaved + // with other `.await`s, dropping the future and recreating it works the same as + // polling a single instance. + let net_poll_future = peer.network.next_action(); + pin_mut!(net_poll_future); + if let Poll::Pending = net_poll_future.poll(cx) { + break + } } trace!(target: "sync", "-- Polling complete {}: {}", i, peer.id()); diff --git a/client/offchain/src/api.rs b/client/offchain/src/api.rs index 1301ce9fd..cd9f5c8f6 100644 --- a/client/offchain/src/api.rs +++ b/client/offchain/src/api.rs @@ -419,6 +419,10 @@ mod tests { fn local_peer_id(&self) -> PeerId { PeerId::random() } + + fn listen_addresses(&self) -> Vec { + Vec::new() + } } fn offchain_api() -> (Api, AsyncApi) { diff --git a/client/offchain/src/lib.rs b/client/offchain/src/lib.rs index 6b28d3f8a..6cf5838a4 100644 --- a/client/offchain/src/lib.rs +++ b/client/offchain/src/lib.rs @@ -270,6 +270,10 @@ mod tests { fn local_peer_id(&self) -> PeerId { PeerId::random() } + + fn listen_addresses(&self) -> Vec { + Vec::new() + } } impl NetworkPeers for TestNetwork { diff --git a/client/service/src/builder.rs b/client/service/src/builder.rs index a737601f7..fb80753b4 100644 --- a/client/service/src/builder.rs +++ b/client/service/src/builder.rs @@ -17,7 +17,7 @@ // along with this program. If not, see . use crate::{ - build_network_future, + build_network_future, build_system_rpc_future, client::{Client, ClientConfig}, config::{Configuration, KeystoreConfig, PrometheusConfig}, error::Error, @@ -963,19 +963,29 @@ where Some("networking"), chain_sync_network_provider.run(network.clone()), ); - spawn_handle.spawn("import-queue", None, import_queue.run(Box::new(chain_sync_service))); + spawn_handle.spawn( + "import-queue", + None, + import_queue.run(Box::new(chain_sync_service.clone())), + ); let (system_rpc_tx, system_rpc_rx) = tracing_unbounded("mpsc_system_rpc", 10_000); - - let future = build_network_future( - config.role.clone(), - network_mut, - client, - system_rpc_rx, - has_bootnodes, - config.announce_block, + spawn_handle.spawn( + "system-rpc-handler", + Some("networking"), + build_system_rpc_future( + config.role.clone(), + network_mut.service().clone(), + chain_sync_service.clone(), + client.clone(), + system_rpc_rx, + has_bootnodes, + ), ); + let future = + build_network_future(network_mut, client, chain_sync_service, config.announce_block); + // TODO: Normally, one is supposed to pass a list of notifications protocols supported by the // node through the `NetworkConfiguration` struct. But because this function doesn't know in // advance which components, such as GrandPa or Polkadot, will be plugged on top of the diff --git a/client/service/src/lib.rs b/client/service/src/lib.rs index e3dcf0128..253479abc 100644 --- a/client/service/src/lib.rs +++ b/client/service/src/lib.rs @@ -37,12 +37,16 @@ mod task_manager; use std::{collections::HashMap, net::SocketAddr}; use codec::{Decode, Encode}; -use futures::{channel::mpsc, FutureExt, StreamExt}; +use futures::{channel::mpsc, pin_mut, FutureExt, StreamExt}; use jsonrpsee::{core::Error as JsonRpseeError, RpcModule}; use log::{debug, error, warn}; use sc_client_api::{blockchain::HeaderBackend, BlockBackend, BlockchainEvents, ProofProvider}; -use sc_network::PeerId; -use sc_network_common::{config::MultiaddrWithPeerId, service::NetworkBlock}; +use sc_network::{NetworkStateInfo, PeerId}; +use sc_network_common::{ + config::MultiaddrWithPeerId, + service::{NetworkBlock, NetworkPeers}, +}; +use sc_network_sync::service::chain_sync::ChainSyncInterfaceHandle; use sc_utils::mpsc::TracingUnboundedReceiver; use sp_blockchain::HeaderMetadata; use sp_consensus::SyncOracle; @@ -138,9 +142,7 @@ pub struct PartialComponents @@ -153,21 +155,21 @@ async fn build_network_future< + 'static, H: sc_network_common::ExHashT, >( - role: Role, - mut network: sc_network::NetworkWorker, + network: sc_network::NetworkWorker, client: Arc, - mut rpc_rx: TracingUnboundedReceiver>, - should_have_peers: bool, + chain_sync_service: ChainSyncInterfaceHandle, announce_imported_blocks: bool, ) { let mut imported_blocks_stream = client.import_notification_stream().fuse(); - // Current best block at initialization, to report to the RPC layer. - let starting_block = client.info().best_number; - // Stream of finalized blocks reported by the client. let mut finality_notification_stream = client.finality_notification_stream().fuse(); + let network_service = network.service().clone(); + + let network_run = network.run().fuse(); + pin_mut!(network_run); + loop { futures::select! { // List of blocks that the client has imported. @@ -176,15 +178,18 @@ async fn build_network_future< Some(n) => n, // If this stream is shut down, that means the client has shut down, and the // most appropriate thing to do for the network future is to shut down too. - None => return, + None => { + debug!("Block import stream has terminated, shutting down the network future."); + return + }, }; if announce_imported_blocks { - network.service().announce_block(notification.hash, None); + network_service.announce_block(notification.hash, None); } if notification.is_new_best { - network.service().new_best_block_imported( + network_service.new_best_block_imported( notification.hash, *notification.header.number(), ); @@ -193,106 +198,160 @@ async fn build_network_future< // List of blocks that the client has finalized. notification = finality_notification_stream.select_next_some() => { - network.on_block_finalized(notification.hash, notification.header); + chain_sync_service.on_block_finalized(notification.hash, *notification.header.number()); } - // Answer incoming RPC requests. - request = rpc_rx.select_next_some() => { - match request { - sc_rpc::system::Request::Health(sender) => { - let _ = sender.send(sc_rpc::system::Health { - peers: network.peers_debug_info().len(), - is_syncing: network.service().is_major_syncing(), - should_have_peers, - }); - }, - sc_rpc::system::Request::LocalPeerId(sender) => { - let _ = sender.send(network.local_peer_id().to_base58()); - }, - sc_rpc::system::Request::LocalListenAddresses(sender) => { - let peer_id = (*network.local_peer_id()).into(); - let p2p_proto_suffix = sc_network::multiaddr::Protocol::P2p(peer_id); - let addresses = network.listen_addresses() - .map(|addr| addr.clone().with(p2p_proto_suffix.clone()).to_string()) - .collect(); - let _ = sender.send(addresses); - }, - sc_rpc::system::Request::Peers(sender) => { - let _ = sender.send(network.peers_debug_info().into_iter().map(|(peer_id, p)| - sc_rpc::system::PeerInfo { + // Drive the network. Shut down the network future if `NetworkWorker` has terminated. + _ = network_run => { + debug!("`NetworkWorker` has terminated, shutting down the network future."); + return + } + } + } +} + +/// Builds a future that processes system RPC requests. +async fn build_system_rpc_future< + B: BlockT, + C: BlockchainEvents + + HeaderBackend + + BlockBackend + + HeaderMetadata + + ProofProvider + + Send + + Sync + + 'static, + H: sc_network_common::ExHashT, +>( + role: Role, + network_service: Arc>, + chain_sync_service: ChainSyncInterfaceHandle, + client: Arc, + mut rpc_rx: TracingUnboundedReceiver>, + should_have_peers: bool, +) { + // Current best block at initialization, to report to the RPC layer. + let starting_block = client.info().best_number; + + loop { + // Answer incoming RPC requests. + let Some(req) = rpc_rx.next().await else { + debug!("RPC requests stream has terminated, shutting down the system RPC future."); + return; + }; + + match req { + sc_rpc::system::Request::Health(sender) => { + let peers = network_service.peers_debug_info().await; + if let Ok(peers) = peers { + let _ = sender.send(sc_rpc::system::Health { + peers: peers.len(), + is_syncing: network_service.is_major_syncing(), + should_have_peers, + }); + } else { + break + } + }, + sc_rpc::system::Request::LocalPeerId(sender) => { + let _ = sender.send(network_service.local_peer_id().to_base58()); + }, + sc_rpc::system::Request::LocalListenAddresses(sender) => { + let peer_id = network_service.local_peer_id().into(); + let p2p_proto_suffix = sc_network::multiaddr::Protocol::P2p(peer_id); + let addresses = network_service + .listen_addresses() + .iter() + .map(|addr| addr.clone().with(p2p_proto_suffix.clone()).to_string()) + .collect(); + let _ = sender.send(addresses); + }, + sc_rpc::system::Request::Peers(sender) => { + let peers = network_service.peers_debug_info().await; + if let Ok(peers) = peers { + let _ = sender.send( + peers + .into_iter() + .map(|(peer_id, p)| sc_rpc::system::PeerInfo { peer_id: peer_id.to_base58(), roles: format!("{:?}", p.roles), best_hash: p.best_hash, best_number: p.best_number, - } - ).collect()); - } - sc_rpc::system::Request::NetworkState(sender) => { - if let Ok(network_state) = serde_json::to_value(&network.network_state()) { - let _ = sender.send(network_state); - } - } - sc_rpc::system::Request::NetworkAddReservedPeer(peer_addr, sender) => { - let result = match MultiaddrWithPeerId::try_from(peer_addr) { - Ok(peer) => { - network.add_reserved_peer(peer) - }, - Err(err) => { - Err(err.to_string()) - }, - }; - let x = result.map_err(sc_rpc::system::error::Error::MalformattedPeerArg); - let _ = sender.send(x); - } - sc_rpc::system::Request::NetworkRemoveReservedPeer(peer_id, sender) => { - let _ = match peer_id.parse::() { - Ok(peer_id) => { - network.remove_reserved_peer(peer_id); - sender.send(Ok(())) - } - Err(e) => sender.send(Err(sc_rpc::system::error::Error::MalformattedPeerArg( - e.to_string(), - ))), - }; - } - sc_rpc::system::Request::NetworkReservedPeers(sender) => { - let reserved_peers = network.reserved_peers(); - let reserved_peers = reserved_peers - .map(|peer_id| peer_id.to_base58()) - .collect(); - - let _ = sender.send(reserved_peers); + }) + .collect(), + ); + } else { + break + } + }, + sc_rpc::system::Request::NetworkState(sender) => { + let network_state = network_service.network_state().await; + if let Ok(network_state) = network_state { + if let Ok(network_state) = serde_json::to_value(network_state) { + let _ = sender.send(network_state); } - sc_rpc::system::Request::NodeRoles(sender) => { - use sc_rpc::system::NodeRole; + } else { + break + } + }, + sc_rpc::system::Request::NetworkAddReservedPeer(peer_addr, sender) => { + let result = match MultiaddrWithPeerId::try_from(peer_addr) { + Ok(peer) => network_service.add_reserved_peer(peer), + Err(err) => Err(err.to_string()), + }; + let x = result.map_err(sc_rpc::system::error::Error::MalformattedPeerArg); + let _ = sender.send(x); + }, + sc_rpc::system::Request::NetworkRemoveReservedPeer(peer_id, sender) => { + let _ = match peer_id.parse::() { + Ok(peer_id) => { + network_service.remove_reserved_peer(peer_id); + sender.send(Ok(())) + }, + Err(e) => sender.send(Err(sc_rpc::system::error::Error::MalformattedPeerArg( + e.to_string(), + ))), + }; + }, + sc_rpc::system::Request::NetworkReservedPeers(sender) => { + let reserved_peers = network_service.reserved_peers().await; + if let Ok(reserved_peers) = reserved_peers { + let reserved_peers = + reserved_peers.iter().map(|peer_id| peer_id.to_base58()).collect(); + let _ = sender.send(reserved_peers); + } else { + break + } + }, + sc_rpc::system::Request::NodeRoles(sender) => { + use sc_rpc::system::NodeRole; - let node_role = match role { - Role::Authority { .. } => NodeRole::Authority, - Role::Full => NodeRole::Full, - }; + let node_role = match role { + Role::Authority { .. } => NodeRole::Authority, + Role::Full => NodeRole::Full, + }; - let _ = sender.send(vec![node_role]); - } - sc_rpc::system::Request::SyncState(sender) => { - use sc_rpc::system::SyncState; + let _ = sender.send(vec![node_role]); + }, + sc_rpc::system::Request::SyncState(sender) => { + use sc_rpc::system::SyncState; - let best_number = client.info().best_number; + let best_number = client.info().best_number; - let _ = sender.send(SyncState { - starting_block, - current_block: best_number, - highest_block: network.best_seen_block().unwrap_or(best_number), - }); - } - } - } + let Ok(status) = chain_sync_service.status().await else { + debug!("`ChainSync` has terminated, shutting down the system RPC future."); + return + }; - // The network worker has done something. Nothing special to do, but could be - // used in the future to perform actions in response of things that happened on - // the network. - _ = (&mut network).fuse() => {} + let _ = sender.send(SyncState { + starting_block, + current_block: best_number, + highest_block: status.best_seen_block.unwrap_or(best_number), + }); + }, } } + debug!("`NetworkWorker` has terminated, shutting down the system RPC future."); } // Wrapper for HTTP and WS servers that makes sure they are properly shut down. From abfa341754d8f8fbe2a580b2e3a1fde6428db182 Mon Sep 17 00:00:00 2001 From: Adrian Catangiu Date: Mon, 20 Feb 2023 16:58:37 +0200 Subject: [PATCH 139/558] client/beefy: drop justification on import if pallet not enabled (#13422) BEEFY pallet allows setting on-chain BEEFY genesis to some future block. Disregard any BEEFY justifications attached to imported blocks that predate configured BEEFY genesis. Signed-off-by: acatangiu --- client/beefy/src/import.rs | 56 +++++++++++++--------- client/beefy/src/tests.rs | 98 +++++++++++++++++++++----------------- 2 files changed, 89 insertions(+), 65 deletions(-) diff --git a/client/beefy/src/import.rs b/client/beefy/src/import.rs index 1b5dda379..15554dc53 100644 --- a/client/beefy/src/import.rs +++ b/client/beefy/src/import.rs @@ -92,13 +92,23 @@ where number: NumberFor, hash: ::Hash, ) -> Result, ConsensusError> { + use ConsensusError::ClientImport as ImportError; let block_id = BlockId::hash(hash); + let beefy_genesis = self + .runtime + .runtime_api() + .beefy_genesis(&block_id) + .map_err(|e| ImportError(e.to_string()))? + .ok_or_else(|| ImportError("Unknown BEEFY genesis".to_string()))?; + if number < beefy_genesis { + return Err(ImportError("BEEFY genesis is set for future block".to_string())) + } let validator_set = self .runtime .runtime_api() .validator_set(&block_id) - .map_err(|e| ConsensusError::ClientImport(e.to_string()))? - .ok_or_else(|| ConsensusError::ClientImport("Unknown validator set".to_string()))?; + .map_err(|e| ImportError(e.to_string()))? + .ok_or_else(|| ImportError("Unknown validator set".to_string()))?; decode_and_verify_finality_proof::(&encoded[..], number, &validator_set) } @@ -142,26 +152,28 @@ where match (beefy_encoded, &inner_import_result) { (Some(encoded), ImportResult::Imported(_)) => { - if let Ok(proof) = self.decode_and_verify(&encoded, number, hash) { - // The proof is valid and the block is imported and final, we can import. - debug!( - target: LOG_TARGET, - "🥩 import justif {:?} for block number {:?}.", proof, number - ); - // Send the justification to the BEEFY voter for processing. - self.justification_sender - .notify(|| Ok::<_, ()>(proof)) - .expect("forwards closure result; the closure always returns Ok; qed."); - - metric_inc!(self, beefy_good_justification_imports); - } else { - debug!( - target: LOG_TARGET, - "🥩 error decoding justification: {:?} for imported block {:?}", - encoded, - number, - ); - metric_inc!(self, beefy_bad_justification_imports); + match self.decode_and_verify(&encoded, number, hash) { + Ok(proof) => { + // The proof is valid and the block is imported and final, we can import. + debug!( + target: LOG_TARGET, + "🥩 import justif {:?} for block number {:?}.", proof, number + ); + // Send the justification to the BEEFY voter for processing. + self.justification_sender + .notify(|| Ok::<_, ()>(proof)) + .expect("the closure always returns Ok; qed."); + metric_inc!(self, beefy_good_justification_imports); + }, + Err(err) => { + debug!( + target: LOG_TARGET, + "🥩 error importing BEEFY justification for block {:?}: {:?}", + number, + err, + ); + metric_inc!(self, beefy_bad_justification_imports); + }, } }, _ => (), diff --git a/client/beefy/src/tests.rs b/client/beefy/src/tests.rs index 42f56226c..f17a42f20 100644 --- a/client/beefy/src/tests.rs +++ b/client/beefy/src/tests.rs @@ -60,7 +60,7 @@ use sp_runtime::{ codec::Encode, generic::BlockId, traits::{Header as HeaderT, NumberFor}, - BuildStorage, DigestItem, Justifications, Storage, + BuildStorage, DigestItem, EncodedJustification, Justifications, Storage, }; use std::{collections::HashMap, marker::PhantomData, sync::Arc, task::Poll}; use substrate_test_runtime_client::{runtime::Header, ClientExt}; @@ -107,11 +107,13 @@ pub(crate) struct PeerData { #[derive(Default)] pub(crate) struct BeefyTestNet { peers: Vec, + pub beefy_genesis: NumberFor, } impl BeefyTestNet { pub(crate) fn new(n_authority: usize) -> Self { - let mut net = BeefyTestNet { peers: Vec::with_capacity(n_authority) }; + let beefy_genesis = 1; + let mut net = BeefyTestNet { peers: Vec::with_capacity(n_authority), beefy_genesis }; for i in 0..n_authority { let (rx, cfg) = on_demand_justifications_protocol_config(GENESIS_HASH, None); @@ -202,7 +204,7 @@ impl TestNetFactory for BeefyTestNet { ) { let keys = &[BeefyKeyring::Alice, BeefyKeyring::Bob]; let validator_set = ValidatorSet::new(make_beefy_ids(keys), 0).unwrap(); - let api = Arc::new(TestApi::with_validator_set(&validator_set)); + let api = Arc::new(TestApi::new(self.beefy_genesis, &validator_set, GOOD_MMR_ROOT)); let inner = BlockImportAdapter::new(client.clone()); let (block_import, voter_links, rpc_links) = beefy_block_import_and_links(inner, client.as_backend(), api, None); @@ -716,7 +718,7 @@ async fn correct_beefy_payload() { } #[tokio::test] -async fn beefy_importing_blocks() { +async fn beefy_importing_justifications() { use futures::{future::poll_fn, task::Poll}; use sc_block_builder::BlockBuilderProvider; use sc_client_api::BlockBackend; @@ -726,11 +728,15 @@ async fn beefy_importing_blocks() { let mut net = BeefyTestNet::new(2); let keys = &[BeefyKeyring::Alice, BeefyKeyring::Bob]; let good_set = ValidatorSet::new(make_beefy_ids(keys), 0).unwrap(); + // Set BEEFY genesis to block 3. + net.beefy_genesis = 3; let client = net.peer(0).client().clone(); + let full_client = client.as_client(); let (mut block_import, _, peer_data) = net.make_block_import(client.clone()); let PeerData { beefy_voter_links, .. } = peer_data; let justif_stream = beefy_voter_links.lock().take().unwrap().from_block_import_justif_stream; + let mut justif_recv = justif_stream.subscribe(100_000); let params = |block: Block, justifications: Option| { let mut import = BlockImportParams::new(BlockOrigin::File, block.header); @@ -740,15 +746,19 @@ async fn beefy_importing_blocks() { import.fork_choice = Some(ForkChoiceStrategy::LongestChain); import }; + let backend_justif_for = |block_hash: H256| -> Option { + full_client + .justifications(block_hash) + .unwrap() + .and_then(|j| j.get(BEEFY_ENGINE_ID).cloned()) + }; - let full_client = client.as_client(); let parent_id = BlockId::Number(0); let builder = full_client.new_block_at(&parent_id, Default::default(), false).unwrap(); let block = builder.build().unwrap().block; let hashof1 = block.header.hash(); - // Import without justifications. - let mut justif_recv = justif_stream.subscribe(100_000); + // Import block 1 without justifications. assert_eq!( block_import .import_block(params(block.clone(), None), HashMap::new()) @@ -760,16 +770,32 @@ async fn beefy_importing_blocks() { block_import.import_block(params(block, None), HashMap::new()).await.unwrap(), ImportResult::AlreadyInChain, ); - // Verify no BEEFY justifications present: + + // Import block 2 with "valid" justification (beefy pallet genesis block not yet reached). + let parent_id = BlockId::Number(1); + let block_num = 2; + let builder = full_client.new_block_at(&parent_id, Default::default(), false).unwrap(); + let block = builder.build().unwrap().block; + let hashof2 = block.header.hash(); + + let proof = crate::justification::tests::new_finality_proof(block_num, &good_set, keys); + let versioned_proof: VersionedFinalityProof, Signature> = proof.into(); + let encoded = versioned_proof.encode(); + let justif = Some(Justifications::from((BEEFY_ENGINE_ID, encoded))); + assert_eq!( + block_import.import_block(params(block, justif), HashMap::new()).await.unwrap(), + ImportResult::Imported(ImportedAux { + bad_justification: false, + is_new_best: true, + ..Default::default() + }), + ); + + // Verify no BEEFY justifications present (for either block 1 or 2): { // none in backend, - assert_eq!( - full_client - .justifications(hashof1) - .unwrap() - .and_then(|j| j.get(BEEFY_ENGINE_ID).cloned()), - None - ); + assert_eq!(backend_justif_for(hashof1), None); + assert_eq!(backend_justif_for(hashof2), None); // and none sent to BEEFY worker. poll_fn(move |cx| { assert_eq!(justif_recv.poll_next_unpin(cx), Poll::Pending); @@ -778,17 +804,16 @@ async fn beefy_importing_blocks() { .await; } - // Import with valid justification. - let parent_id = BlockId::Number(1); - let block_num = 2; + // Import block 3 with valid justification. + let parent_id = BlockId::Number(2); + let block_num = 3; + let builder = full_client.new_block_at(&parent_id, Default::default(), false).unwrap(); + let block = builder.build().unwrap().block; + let hashof3 = block.header.hash(); let proof = crate::justification::tests::new_finality_proof(block_num, &good_set, keys); let versioned_proof: VersionedFinalityProof, Signature> = proof.into(); let encoded = versioned_proof.encode(); let justif = Some(Justifications::from((BEEFY_ENGINE_ID, encoded))); - - let builder = full_client.new_block_at(&parent_id, Default::default(), false).unwrap(); - let block = builder.build().unwrap().block; - let hashof2 = block.header.hash(); let mut justif_recv = justif_stream.subscribe(100_000); assert_eq!( block_import.import_block(params(block, justif), HashMap::new()).await.unwrap(), @@ -801,13 +826,7 @@ async fn beefy_importing_blocks() { // Verify BEEFY justification successfully imported: { // still not in backend (worker is responsible for appending to backend), - assert_eq!( - full_client - .justifications(hashof2) - .unwrap() - .and_then(|j| j.get(BEEFY_ENGINE_ID).cloned()), - None - ); + assert_eq!(backend_justif_for(hashof3), None); // but sent to BEEFY worker // (worker will append it to backend when all previous mandatory justifs are there as well). poll_fn(move |cx| { @@ -820,19 +839,18 @@ async fn beefy_importing_blocks() { .await; } - // Import with invalid justification (incorrect validator set). - let parent_id = BlockId::Number(2); - let block_num = 3; + // Import block 4 with invalid justification (incorrect validator set). + let parent_id = BlockId::Number(3); + let block_num = 4; + let builder = full_client.new_block_at(&parent_id, Default::default(), false).unwrap(); + let block = builder.build().unwrap().block; + let hashof4 = block.header.hash(); let keys = &[BeefyKeyring::Alice]; let bad_set = ValidatorSet::new(make_beefy_ids(keys), 1).unwrap(); let proof = crate::justification::tests::new_finality_proof(block_num, &bad_set, keys); let versioned_proof: VersionedFinalityProof, Signature> = proof.into(); let encoded = versioned_proof.encode(); let justif = Some(Justifications::from((BEEFY_ENGINE_ID, encoded))); - - let builder = full_client.new_block_at(&parent_id, Default::default(), false).unwrap(); - let block = builder.build().unwrap().block; - let hashof3 = block.header.hash(); let mut justif_recv = justif_stream.subscribe(100_000); assert_eq!( block_import.import_block(params(block, justif), HashMap::new()).await.unwrap(), @@ -846,13 +864,7 @@ async fn beefy_importing_blocks() { // Verify bad BEEFY justifications was not imported: { // none in backend, - assert_eq!( - full_client - .justifications(hashof3) - .unwrap() - .and_then(|j| j.get(BEEFY_ENGINE_ID).cloned()), - None - ); + assert_eq!(backend_justif_for(hashof4), None); // and none sent to BEEFY worker. poll_fn(move |cx| { assert_eq!(justif_recv.poll_next_unpin(cx), Poll::Pending); From 1eff8873407ea0df01a701010086008eb06e1907 Mon Sep 17 00:00:00 2001 From: Sasha Gryaznov Date: Mon, 20 Feb 2023 17:45:54 +0200 Subject: [PATCH 140/558] rename call_runtime() returned error (#13412) --- frame/contracts/src/wasm/runtime.rs | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/frame/contracts/src/wasm/runtime.rs b/frame/contracts/src/wasm/runtime.rs index ef0c35df7..eba20e959 100644 --- a/frame/contracts/src/wasm/runtime.rs +++ b/frame/contracts/src/wasm/runtime.rs @@ -115,7 +115,7 @@ pub enum ReturnCode { /// The contract that was called is no contract (a plain account). NotCallable = 8, /// The call dispatched by `seal_call_runtime` was executed but returned an error. - CallRuntimeReturnedError = 10, + CallRuntimeFailed = 10, /// ECDSA pubkey recovery failed (most probably wrong recovery id or signature), or /// ECDSA compressed pubkey conversion into Ethereum address failed (most probably /// wrong pubkey provided). @@ -2404,21 +2404,21 @@ pub mod env { /// /// # Parameters /// - /// - `input_ptr`: the pointer into the linear memory where the input data is placed. - /// - `input_len`: the length of the input data in bytes. + /// - `call_ptr`: the pointer into the linear memory where the input data is placed. + /// - `call_len`: the length of the input data in bytes. /// /// # Return Value /// /// Returns `ReturnCode::Success` when the dispatchable was succesfully executed and /// returned `Ok`. When the dispatchable was exeuted but returned an error - /// `ReturnCode::CallRuntimeReturnedError` is returned. The full error is not + /// `ReturnCode::CallRuntimeFailed` is returned. The full error is not /// provided because it is not guaranteed to be stable. /// /// # Comparison with `ChainExtension` /// /// Just as a chain extension this API allows the runtime to extend the functionality - /// of contracts. While making use of this function is generelly easier it cannot be - /// used in call cases. Consider writing a chain extension if you need to do perform + /// of contracts. While making use of this function is generally easier it cannot be + /// used in all cases. Consider writing a chain extension if you need to do perform /// one of the following tasks: /// /// - Return data. @@ -2444,7 +2444,7 @@ pub mod env { ctx.adjust_gas(charged, RuntimeCosts::CallRuntime(actual_weight)); match result { Ok(_) => Ok(ReturnCode::Success), - Err(_) => Ok(ReturnCode::CallRuntimeReturnedError), + Err(_) => Ok(ReturnCode::CallRuntimeFailed), } } From 5b6519a7ff4a2d3cc424d78bc4830688f3b184c0 Mon Sep 17 00:00:00 2001 From: Michal Kucharczyk <1728078+michalkucharczyk@users.noreply.github.com> Date: Mon, 20 Feb 2023 23:47:21 +0100 Subject: [PATCH 141/558] `BlockId` removal: `runtime-api` refactor (#13255) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * BlockId removal: refactor of runtime API It changes the arguments of: - `ApiExt` methods: `has_api`, `has_api_with`, `api_version` - `CallApiAt` method: `runtime_version_at` from: `BlockId` to: `Block::Hash` It also changes the first argument of all generated runtime API calls from: `BlockId` to: `Block::Hash` This PR is part of BlockId::Number refactoring analysis (paritytech/substrate#11292) * BlockId removal: refactor of runtime API - tests - tests adjusted to new runtime API, - some tests migrated from block number to block hash * benchmarking-cli: BlockId(0) migrated to info().genesis_hash `runtime_api.call()` now requires the block hash instead of BlockId::Number. To access the genesis hash widely used in benchmarking engine the Client was constrained to satisfy `sp_blockchain::HeaderBackend` trait which provides `info().genesis_hash`. * trivial: api.call(BlockId) -> api.call(Hash) - Migrated all `runtime_api.calls` to use Hash - Noteworthy (?): -- `validate_transaction_blocking` in transaction pool, * CallApiAtParams::at changed to Block::Hash * missed doc updated * Apply suggestions from code review Co-authored-by: Bastian Köcher * ".git/.scripts/commands/fmt/fmt.sh" * BlockId removal: Benchmark::consumed_weight Little refactor around `Benchmark::consumed_weight`: `BlockId` removed. * at_hash renamed * wrong merge fixed * beefy worker: merged with master * beefy: tests: missing block problem fixed * Apply review suggestion * fix --------- Co-authored-by: Bastian Köcher Co-authored-by: command-bot <> --- bin/node/cli/src/service.rs | 2 +- bin/node/testing/src/bench.rs | 4 +- client/authority-discovery/src/worker.rs | 4 +- .../basic-authorship/src/basic_authorship.rs | 2 +- client/beefy/src/import.rs | 6 +-- client/beefy/src/lib.rs | 36 ++++++-------- client/beefy/src/worker.rs | 22 ++++++--- client/block-builder/src/lib.rs | 24 +++++----- client/consensus/aura/src/import_queue.rs | 12 ++--- client/consensus/aura/src/lib.rs | 11 +++-- client/consensus/babe/rpc/src/lib.rs | 4 +- client/consensus/babe/src/lib.rs | 41 ++++++++-------- client/consensus/babe/src/tests.rs | 2 +- client/consensus/pow/src/lib.rs | 6 +-- client/finality-grandpa/src/environment.rs | 9 ++-- client/finality-grandpa/src/import.rs | 9 ++-- client/merkle-mountain-range/rpc/src/lib.rs | 19 +++----- client/merkle-mountain-range/src/lib.rs | 16 +++---- client/offchain/src/lib.rs | 24 +++++----- .../rpc-spec-v2/src/chain_head/chain_head.rs | 28 ++++------- client/rpc/src/author/mod.rs | 4 +- client/rpc/src/dev/mod.rs | 4 +- client/rpc/src/state/state_full.rs | 17 +++---- client/service/src/client/client.rs | 9 ++-- client/service/test/src/client/mod.rs | 29 +++--------- client/tracing/src/block/mod.rs | 5 +- client/transaction-pool/src/api.rs | 18 +++---- frame/transaction-payment/rpc/src/lib.rs | 17 +++---- .../api/proc-macro/src/decl_runtime_apis.rs | 12 ++--- .../api/proc-macro/src/impl_runtime_apis.rs | 11 ++--- .../proc-macro/src/mock_impl_runtime_apis.rs | 47 +++++++++---------- primitives/api/proc-macro/src/utils.rs | 6 +-- primitives/api/src/lib.rs | 36 +++++++------- primitives/api/test/benches/bench.rs | 25 +++++----- primitives/api/test/tests/decl_and_impl.rs | 32 ++++++------- primitives/api/test/tests/runtime_calls.rs | 47 ++++++++++--------- ....rs => mock_advanced_hash_by_reference.rs} | 2 +- ...=> mock_advanced_hash_by_reference.stderr} | 6 +-- ...ockid.rs => mock_advanced_missing_hash.rs} | 0 ...derr => mock_advanced_missing_hash.stderr} | 4 +- .../application-crypto/test/src/ecdsa.rs | 3 +- .../application-crypto/test/src/ed25519.rs | 3 +- .../application-crypto/test/src/sr25519.rs | 3 +- primitives/beefy/src/mmr.rs | 7 +-- primitives/runtime/src/runtime_logger.rs | 7 +-- primitives/session/src/lib.rs | 4 +- test-utils/runtime/src/lib.rs | 19 ++++---- .../frame/benchmarking-cli/src/block/bench.rs | 14 +++--- .../benchmarking-cli/src/extrinsic/bench.rs | 14 +++--- .../benchmarking-cli/src/extrinsic/cmd.rs | 4 +- .../benchmarking-cli/src/overhead/cmd.rs | 4 +- utils/frame/rpc/system/src/lib.rs | 18 +++---- 52 files changed, 321 insertions(+), 391 deletions(-) rename primitives/api/test/tests/ui/{mock_advanced_block_id_by_value.rs => mock_advanced_hash_by_reference.rs} (81%) rename primitives/api/test/tests/ui/{mock_advanced_block_id_by_value.stderr => mock_advanced_hash_by_reference.stderr} (60%) rename primitives/api/test/tests/ui/{mock_advanced_missing_blockid.rs => mock_advanced_missing_hash.rs} (100%) rename primitives/api/test/tests/ui/{mock_advanced_missing_blockid.stderr => mock_advanced_missing_hash.stderr} (56%) diff --git a/bin/node/cli/src/service.rs b/bin/node/cli/src/service.rs index c98fe17cf..e0247079d 100644 --- a/bin/node/cli/src/service.rs +++ b/bin/node/cli/src/service.rs @@ -60,7 +60,7 @@ pub fn fetch_nonce(client: &FullClient, account: sp_core::sr25519::Pair) -> u32 let best_hash = client.chain_info().best_hash; client .runtime_api() - .account_nonce(&generic::BlockId::Hash(best_hash), account.public().into()) + .account_nonce(best_hash, account.public().into()) .expect("Fetching account nonce works; qed") } diff --git a/bin/node/testing/src/bench.rs b/bin/node/testing/src/bench.rs index cc0a93ca2..f9bad2a56 100644 --- a/bin/node/testing/src/bench.rs +++ b/bin/node/testing/src/bench.rs @@ -53,7 +53,6 @@ use sp_consensus::BlockOrigin; use sp_core::{blake2_256, ed25519, sr25519, traits::SpawnNamed, ExecutionContext, Pair, Public}; use sp_inherents::InherentData; use sp_runtime::{ - generic::BlockId, traits::{Block as BlockT, IdentifyAccount, Verify}, OpaqueExtrinsic, }; @@ -274,7 +273,6 @@ pub struct BlockContentIterator<'a> { impl<'a> BlockContentIterator<'a> { fn new(content: BlockContent, keyring: &'a BenchKeyring, client: &Client) -> Self { let genesis_hash = client.chain_info().genesis_hash; - let runtime_version = client .runtime_version_at(genesis_hash) .expect("There should be runtime version at 0"); @@ -442,7 +440,7 @@ impl BenchDb { client .runtime_api() .inherent_extrinsics_with_context( - &BlockId::number(0), + client.chain_info().genesis_hash, ExecutionContext::BlockConstruction, inherent_data, ) diff --git a/client/authority-discovery/src/worker.rs b/client/authority-discovery/src/worker.rs index 4121b64e0..72a371cb6 100644 --- a/client/authority-discovery/src/worker.rs +++ b/client/authority-discovery/src/worker.rs @@ -55,7 +55,7 @@ use sp_blockchain::HeaderBackend; use sp_core::crypto::{key_types, CryptoTypePublicPair, Pair}; use sp_keystore::CryptoStore; -use sp_runtime::{generic::BlockId, traits::Block as BlockT}; +use sp_runtime::traits::Block as BlockT; mod addr_cache; /// Dht payload schemas generated from Protobuf definitions via Prost crate in build.rs. @@ -171,7 +171,7 @@ where &self, at: Block::Hash, ) -> std::result::Result, ApiError> { - self.runtime_api().authorities(&BlockId::Hash(at)) + self.runtime_api().authorities(at) } } diff --git a/client/basic-authorship/src/basic_authorship.rs b/client/basic-authorship/src/basic_authorship.rs index c39d07a14..9ab7ef55f 100644 --- a/client/basic-authorship/src/basic_authorship.rs +++ b/client/basic-authorship/src/basic_authorship.rs @@ -731,7 +731,7 @@ mod tests { assert_eq!(proposal.block.extrinsics().len(), 1); let api = client.runtime_api(); - api.execute_block(&BlockId::Hash(genesis_hash), proposal.block).unwrap(); + api.execute_block(genesis_hash, proposal.block).unwrap(); let state = backend.state_at(genesis_hash).unwrap(); diff --git a/client/beefy/src/import.rs b/client/beefy/src/import.rs index 15554dc53..66421e227 100644 --- a/client/beefy/src/import.rs +++ b/client/beefy/src/import.rs @@ -24,7 +24,6 @@ use sp_api::{ProvideRuntimeApi, TransactionFor}; use sp_blockchain::well_known_cache_keys; use sp_consensus::Error as ConsensusError; use sp_runtime::{ - generic::BlockId, traits::{Block as BlockT, Header as HeaderT, NumberFor}, EncodedJustification, }; @@ -93,11 +92,10 @@ where hash: ::Hash, ) -> Result, ConsensusError> { use ConsensusError::ClientImport as ImportError; - let block_id = BlockId::hash(hash); let beefy_genesis = self .runtime .runtime_api() - .beefy_genesis(&block_id) + .beefy_genesis(hash) .map_err(|e| ImportError(e.to_string()))? .ok_or_else(|| ImportError("Unknown BEEFY genesis".to_string()))?; if number < beefy_genesis { @@ -106,7 +104,7 @@ where let validator_set = self .runtime .runtime_api() - .validator_set(&block_id) + .validator_set(hash) .map_err(|e| ImportError(e.to_string()))? .ok_or_else(|| ImportError("Unknown validator set".to_string()))?; diff --git a/client/beefy/src/lib.rs b/client/beefy/src/lib.rs index 4031b5297..04345912f 100644 --- a/client/beefy/src/lib.rs +++ b/client/beefy/src/lib.rs @@ -52,10 +52,7 @@ use sp_blockchain::{ use sp_consensus::{Error as ConsensusError, SyncOracle}; use sp_keystore::SyncCryptoStorePtr; use sp_mmr_primitives::MmrApi; -use sp_runtime::{ - generic::BlockId, - traits::{Block, Zero}, -}; +use sp_runtime::traits::{Block, Zero}; use std::{collections::VecDeque, marker::PhantomData, sync::Arc}; mod aux_schema; @@ -345,7 +342,7 @@ where { let beefy_genesis = runtime .runtime_api() - .beefy_genesis(&BlockId::hash(best_grandpa.hash())) + .beefy_genesis(best_grandpa.hash()) .ok() .flatten() .ok_or_else(|| ClientError::Backend("BEEFY pallet expected to be active.".into()))?; @@ -369,7 +366,7 @@ where let best_beefy = *header.number(); // If no session boundaries detected so far, just initialize new rounds here. if sessions.is_empty() { - let active_set = expect_validator_set(runtime, BlockId::hash(header.hash()))?; + let active_set = expect_validator_set(runtime, header.hash())?; let mut rounds = Rounds::new(best_beefy, active_set); // Mark the round as already finalized. rounds.conclude(best_beefy); @@ -383,8 +380,8 @@ where if *header.number() == beefy_genesis { // We've reached BEEFY genesis, initialize voter here. - let genesis_set = expect_validator_set(runtime, BlockId::hash(header.hash())) - .and_then(genesis_set_sanity_check)?; + let genesis_set = + expect_validator_set(runtime, header.hash()).and_then(genesis_set_sanity_check)?; info!( target: LOG_TARGET, "🥩 Loading BEEFY voter state from genesis on what appears to be first startup. \ @@ -409,16 +406,11 @@ where // Check if state is still available if we move up the chain. let parent_hash = *header.parent_hash(); - runtime - .runtime_api() - .validator_set(&BlockId::hash(parent_hash)) - .ok() - .flatten() - .ok_or_else(|| { - let msg = format!("{}. Could not initialize BEEFY voter.", parent_hash); - error!(target: LOG_TARGET, "🥩 {}", msg); - ClientError::Consensus(sp_consensus::Error::StateUnavailable(msg)) - })?; + runtime.runtime_api().validator_set(parent_hash).ok().flatten().ok_or_else(|| { + let msg = format!("{}. Could not initialize BEEFY voter.", parent_hash); + error!(target: LOG_TARGET, "🥩 {}", msg); + ClientError::Consensus(sp_consensus::Error::StateUnavailable(msg)) + })?; // Move up the chain. header = blockchain.expect_header(parent_hash)?; @@ -449,8 +441,8 @@ where Some(notif) => notif, None => break }; - let at = BlockId::hash(notif.header.hash()); - if let Some(start) = runtime.runtime_api().beefy_genesis(&at).ok().flatten() { + let at = notif.header.hash(); + if let Some(start) = runtime.runtime_api().beefy_genesis(at).ok().flatten() { if *notif.header.number() >= start { // Beefy pallet available, return header for best grandpa at the time. info!( @@ -485,7 +477,7 @@ fn genesis_set_sanity_check( fn expect_validator_set( runtime: &R, - at: BlockId, + at_hash: B::Hash, ) -> ClientResult> where B: Block, @@ -494,7 +486,7 @@ where { runtime .runtime_api() - .validator_set(&at) + .validator_set(at_hash) .ok() .flatten() .ok_or_else(|| ClientError::Backend("BEEFY pallet expected to be active.".into())) diff --git a/client/beefy/src/worker.rs b/client/beefy/src/worker.rs index 8783997cc..f67ec17fc 100644 --- a/client/beefy/src/worker.rs +++ b/client/beefy/src/worker.rs @@ -963,10 +963,21 @@ where } let number = *proof.round_number(); + let hash = self + .backend + .blockchain() + .expect_block_hash_from_id(&BlockId::Number(number)) + .map_err(|err| { + let err_msg = format!( + "Couldn't get hash for block #{:?} (error: {:?}), skipping report for equivocation", + number, err + ); + Error::Backend(err_msg) + })?; let runtime_api = self.runtime.runtime_api(); // generate key ownership proof at that block let key_owner_proof = match runtime_api - .generate_key_ownership_proof(&BlockId::Number(number), validator_set_id, offender_id) + .generate_key_ownership_proof(hash, validator_set_id, offender_id) .map_err(Error::RuntimeApi)? { Some(proof) => proof, @@ -982,11 +993,7 @@ where // submit equivocation report at **best** block let best_block_hash = self.backend.blockchain().info().best_hash; runtime_api - .submit_report_equivocation_unsigned_extrinsic( - &BlockId::Hash(best_block_hash), - proof, - key_owner_proof, - ) + .submit_report_equivocation_unsigned_extrinsic(best_block_hash, proof, key_owner_proof) .map_err(Error::RuntimeApi)?; Ok(()) @@ -1628,6 +1635,9 @@ pub(crate) mod tests { let mut worker = create_beefy_worker(&net.peer(0), &keys[0], 1, validator_set.clone()); worker.runtime = api_alice.clone(); + // let there be a block with num = 1: + let _ = net.peer(0).push_blocks(1, false); + let payload1 = Payload::from_single_entry(MMR_ROOT_ID, vec![42]); let payload2 = Payload::from_single_entry(MMR_ROOT_ID, vec![128]); diff --git a/client/block-builder/src/lib.rs b/client/block-builder/src/lib.rs index b6c2ac3ba..d302a834f 100644 --- a/client/block-builder/src/lib.rs +++ b/client/block-builder/src/lib.rs @@ -137,7 +137,6 @@ pub struct BlockBuilder<'a, Block: BlockT, A: ProvideRuntimeApi, B> { extrinsics: Vec, api: ApiRef<'a, A::Api>, version: u32, - block_id: BlockId, parent_hash: Block::Hash, backend: &'a B, /// The estimated size of the block header. @@ -181,12 +180,14 @@ where api.record_proof(); } - let block_id = BlockId::Hash(parent_hash); - - api.initialize_block_with_context(&block_id, ExecutionContext::BlockConstruction, &header)?; + api.initialize_block_with_context( + parent_hash, + ExecutionContext::BlockConstruction, + &header, + )?; let version = api - .api_version::>(&block_id)? + .api_version::>(parent_hash)? .ok_or_else(|| Error::VersionInvalid("BlockBuilderApi".to_string()))?; Ok(Self { @@ -194,7 +195,6 @@ where extrinsics: Vec::new(), api, version, - block_id, backend, estimated_header_size, }) @@ -204,7 +204,7 @@ where /// /// This will ensure the extrinsic can be validly executed (by executing it). pub fn push(&mut self, xt: ::Extrinsic) -> Result<(), Error> { - let block_id = &self.block_id; + let parent_hash = self.parent_hash; let extrinsics = &mut self.extrinsics; let version = self.version; @@ -212,14 +212,14 @@ where let res = if version < 6 { #[allow(deprecated)] api.apply_extrinsic_before_version_6_with_context( - block_id, + parent_hash, ExecutionContext::BlockConstruction, xt.clone(), ) .map(legacy::byte_sized_error::convert_to_latest) } else { api.apply_extrinsic_with_context( - block_id, + parent_hash, ExecutionContext::BlockConstruction, xt.clone(), ) @@ -246,7 +246,7 @@ where pub fn build(mut self) -> Result>, Error> { let header = self .api - .finalize_block_with_context(&self.block_id, ExecutionContext::BlockConstruction)?; + .finalize_block_with_context(self.parent_hash, ExecutionContext::BlockConstruction)?; debug_assert_eq!( header.extrinsics_root().clone(), @@ -279,13 +279,13 @@ where &mut self, inherent_data: sp_inherents::InherentData, ) -> Result, Error> { - let block_id = self.block_id; + let parent_hash = self.parent_hash; self.api .execute_in_transaction(move |api| { // `create_inherents` should not change any state, to ensure this we always rollback // the transaction. TransactionOutcome::Rollback(api.inherent_extrinsics_with_context( - &block_id, + parent_hash, ExecutionContext::BlockConstruction, inherent_data, )) diff --git a/client/consensus/aura/src/import_queue.rs b/client/consensus/aura/src/import_queue.rs index 4c8722c31..be52bffe0 100644 --- a/client/consensus/aura/src/import_queue.rs +++ b/client/consensus/aura/src/import_queue.rs @@ -41,7 +41,6 @@ use sp_consensus_slots::Slot; use sp_core::{crypto::Pair, ExecutionContext}; use sp_inherents::{CreateInherentDataProviders, InherentDataProvider as _}; use sp_runtime::{ - generic::BlockId, traits::{Block as BlockT, Header, NumberFor}, DigestItem, }; @@ -142,7 +141,7 @@ where async fn check_inherents( &self, block: B, - block_id: BlockId, + at_hash: B::Hash, inherent_data: sp_inherents::InherentData, create_inherent_data_providers: CIDP::InherentDataProviders, execution_context: ExecutionContext, @@ -155,7 +154,7 @@ where let inherent_res = self .client .runtime_api() - .check_inherents_with_context(&block_id, execution_context, block, inherent_data) + .check_inherents_with_context(at_hash, execution_context, block, inherent_data) .map_err(|e| Error::Client(e.into()))?; if !inherent_res.ok() { @@ -248,15 +247,12 @@ where if self .client .runtime_api() - .has_api_with::, _>( - &BlockId::Hash(parent_hash), - |v| v >= 2, - ) + .has_api_with::, _>(parent_hash, |v| v >= 2) .map_err(|e| e.to_string())? { self.check_inherents( new_block.clone(), - BlockId::Hash(parent_hash), + parent_hash, inherent_data, create_inherent_data_providers, block.origin.into(), diff --git a/client/consensus/aura/src/lib.rs b/client/consensus/aura/src/lib.rs index 6ef6bdc2e..0345eaa39 100644 --- a/client/consensus/aura/src/lib.rs +++ b/client/consensus/aura/src/lib.rs @@ -53,7 +53,6 @@ use sp_core::crypto::{ByteArray, Pair, Public}; use sp_inherents::CreateInherentDataProviders; use sp_keystore::{SyncCryptoStore, SyncCryptoStorePtr}; use sp_runtime::{ - generic::BlockId, traits::{Block as BlockT, Header, Member, NumberFor, Zero}, DigestItem, }; @@ -121,8 +120,10 @@ where C: AuxStore + ProvideRuntimeApi + UsageProvider, C::Api: AuraApi, { - let best_block_id = BlockId::Hash(client.usage_info().chain.best_hash); - client.runtime_api().slot_duration(&best_block_id).map_err(|err| err.into()) + client + .runtime_api() + .slot_duration(client.usage_info().chain.best_hash) + .map_err(|err| err.into()) } /// Get slot author for given block along with authorities. @@ -613,7 +614,7 @@ where if *until > context_block_number { runtime_api .initialize_block( - &BlockId::Hash(parent_hash), + parent_hash, &B::Header::new( context_block_number, Default::default(), @@ -627,7 +628,7 @@ where } runtime_api - .authorities(&BlockId::Hash(parent_hash)) + .authorities(parent_hash) .ok() .ok_or(sp_consensus::Error::InvalidAuthoritiesSet) } diff --git a/client/consensus/babe/rpc/src/lib.rs b/client/consensus/babe/rpc/src/lib.rs index 288f852a5..2d6d95b8d 100644 --- a/client/consensus/babe/rpc/src/lib.rs +++ b/client/consensus/babe/rpc/src/lib.rs @@ -29,7 +29,7 @@ use sc_consensus_babe::{authorship, Epoch}; use sc_consensus_epochs::{descendent_query, Epoch as EpochT, SharedEpochChanges}; use sc_rpc_api::DenyUnsafe; use serde::{Deserialize, Serialize}; -use sp_api::{BlockId, ProvideRuntimeApi}; +use sp_api::ProvideRuntimeApi; use sp_application_crypto::AppKey; use sp_blockchain::{Error as BlockChainError, HeaderBackend, HeaderMetadata}; use sp_consensus::{Error as ConsensusError, SelectChain}; @@ -97,7 +97,7 @@ where let epoch_start = self .client .runtime_api() - .current_epoch_start(&BlockId::Hash(header.hash())) + .current_epoch_start(header.hash()) .map_err(|err| Error::StringError(format!("{:?}", err)))?; let epoch = epoch_data( diff --git a/client/consensus/babe/src/lib.rs b/client/consensus/babe/src/lib.rs index 403792249..a30540340 100644 --- a/client/consensus/babe/src/lib.rs +++ b/client/consensus/babe/src/lib.rs @@ -123,7 +123,7 @@ use sp_core::{crypto::ByteArray, ExecutionContext}; use sp_inherents::{CreateInherentDataProviders, InherentData, InherentDataProvider}; use sp_keystore::{SyncCryptoStore, SyncCryptoStorePtr}; use sp_runtime::{ - generic::{BlockId, OpaqueDigestItemId}, + generic::OpaqueDigestItemId, traits::{Block as BlockT, Header, NumberFor, SaturatedConversion, Zero}, DigestItem, }; @@ -377,24 +377,24 @@ where C: AuxStore + ProvideRuntimeApi + UsageProvider, C::Api: BabeApi, { - let block_id = if client.usage_info().chain.finalized_state.is_some() { - BlockId::Hash(client.usage_info().chain.best_hash) + let at_hash = if client.usage_info().chain.finalized_state.is_some() { + client.usage_info().chain.best_hash } else { debug!(target: LOG_TARGET, "No finalized state is available. Reading config from genesis"); - BlockId::Hash(client.usage_info().chain.genesis_hash) + client.usage_info().chain.genesis_hash }; let runtime_api = client.runtime_api(); - let version = runtime_api.api_version::>(&block_id)?; + let version = runtime_api.api_version::>(at_hash)?; let config = match version { Some(1) => { #[allow(deprecated)] { - runtime_api.configuration_before_version_2(&block_id)?.into() + runtime_api.configuration_before_version_2(at_hash)?.into() } }, - Some(2) => runtime_api.configuration(&block_id)?, + Some(2) => runtime_api.configuration(at_hash)?, _ => return Err(sp_blockchain::Error::VersionInvalid( "Unsupported or invalid BabeApi version".to_string(), @@ -1023,7 +1023,7 @@ where async fn check_inherents( &self, block: Block, - block_id: BlockId, + at_hash: Block::Hash, inherent_data: InherentData, create_inherent_data_providers: CIDP::InherentDataProviders, execution_context: ExecutionContext, @@ -1031,7 +1031,7 @@ where let inherent_res = self .client .runtime_api() - .check_inherents_with_context(&block_id, execution_context, block, inherent_data) + .check_inherents_with_context(at_hash, execution_context, block, inherent_data) .map_err(Error::RuntimeApi)?; if !inherent_res.ok() { @@ -1078,11 +1078,11 @@ where ); // get the best block on which we will build and send the equivocation report. - let best_id = self + let best_hash = self .select_chain .best_chain() .await - .map(|h| BlockId::Hash(h.hash())) + .map(|h| h.hash()) .map_err(|e| Error::Client(e.into()))?; // generate a key ownership proof. we start by trying to generate the @@ -1093,17 +1093,17 @@ where // equivocation happens on the first block of the session, in which case // its parent would be on the previous session. if generation on the // parent header fails we try with best block as well. - let generate_key_owner_proof = |block_id: &BlockId| { + let generate_key_owner_proof = |at_hash: Block::Hash| { self.client .runtime_api() - .generate_key_ownership_proof(block_id, slot, equivocation_proof.offender.clone()) + .generate_key_ownership_proof(at_hash, slot, equivocation_proof.offender.clone()) .map_err(Error::RuntimeApi) }; - let parent_id = BlockId::Hash(*header.parent_hash()); - let key_owner_proof = match generate_key_owner_proof(&parent_id)? { + let parent_hash = *header.parent_hash(); + let key_owner_proof = match generate_key_owner_proof(parent_hash)? { Some(proof) => proof, - None => match generate_key_owner_proof(&best_id)? { + None => match generate_key_owner_proof(best_hash)? { Some(proof) => proof, None => { debug!( @@ -1119,7 +1119,7 @@ where self.client .runtime_api() .submit_report_equivocation_unsigned_extrinsic( - &best_id, + best_hash, equivocation_proof, key_owner_proof, ) @@ -1268,7 +1268,7 @@ where self.check_inherents( new_block.clone(), - BlockId::Hash(parent_hash), + parent_hash, inherent_data, create_inherent_data_providers, block.origin.into(), @@ -1395,11 +1395,10 @@ where }; // Read epoch info from the imported state. - let block_id = BlockId::hash(hash); - let current_epoch = self.client.runtime_api().current_epoch(&block_id).map_err(|e| { + let current_epoch = self.client.runtime_api().current_epoch(hash).map_err(|e| { ConsensusError::ClientImport(babe_err::(Error::RuntimeApi(e)).into()) })?; - let next_epoch = self.client.runtime_api().next_epoch(&block_id).map_err(|e| { + let next_epoch = self.client.runtime_api().next_epoch(hash).map_err(|e| { ConsensusError::ClientImport(babe_err::(Error::RuntimeApi(e)).into()) })?; diff --git a/client/consensus/babe/src/tests.rs b/client/consensus/babe/src/tests.rs index 0dd0b59dd..ad2e95df0 100644 --- a/client/consensus/babe/src/tests.rs +++ b/client/consensus/babe/src/tests.rs @@ -45,7 +45,7 @@ use sp_keystore::{ SyncCryptoStore, }; use sp_runtime::{ - generic::{Digest, DigestItem}, + generic::{BlockId, Digest, DigestItem}, traits::Block as BlockT, }; use sp_timestamp::Timestamp; diff --git a/client/consensus/pow/src/lib.rs b/client/consensus/pow/src/lib.rs index ace00a344..a81ad8ebe 100644 --- a/client/consensus/pow/src/lib.rs +++ b/client/consensus/pow/src/lib.rs @@ -267,7 +267,7 @@ where async fn check_inherents( &self, block: B, - block_id: BlockId, + at_hash: B::Hash, inherent_data_providers: CIDP::InherentDataProviders, execution_context: ExecutionContext, ) -> Result<(), Error> { @@ -283,7 +283,7 @@ where let inherent_res = self .client .runtime_api() - .check_inherents_with_context(&block_id, execution_context, block, inherent_data) + .check_inherents_with_context(at_hash, execution_context, block, inherent_data) .map_err(|e| Error::Client(e.into()))?; if !inherent_res.ok() { @@ -344,7 +344,7 @@ where if !block.state_action.skip_execution_checks() { self.check_inherents( check_block.clone(), - BlockId::Hash(parent_hash), + parent_hash, self.create_inherent_data_providers .create_inherent_data_providers(parent_hash, ()) .await?, diff --git a/client/finality-grandpa/src/environment.rs b/client/finality-grandpa/src/environment.rs index a9aa204f1..9ee7e486c 100644 --- a/client/finality-grandpa/src/environment.rs +++ b/client/finality-grandpa/src/environment.rs @@ -46,10 +46,7 @@ use sp_finality_grandpa::{ AuthorityId, AuthoritySignature, Equivocation, EquivocationProof, GrandpaApi, RoundNumber, SetId, GRANDPA_ENGINE_ID, }; -use sp_runtime::{ - generic::BlockId, - traits::{Block as BlockT, Header as HeaderT, NumberFor, Zero}, -}; +use sp_runtime::traits::{Block as BlockT, Header as HeaderT, NumberFor, Zero}; use crate::{ authorities::{AuthoritySet, SharedAuthoritySet}, @@ -543,7 +540,7 @@ where .client .runtime_api() .generate_key_ownership_proof( - &BlockId::Hash(current_set_latest_hash), + current_set_latest_hash, authority_set.set_id, equivocation.offender().clone(), ) @@ -565,7 +562,7 @@ where self.client .runtime_api() .submit_report_equivocation_unsigned_extrinsic( - &BlockId::Hash(best_block_hash), + best_block_hash, equivocation_proof, key_owner_proof, ) diff --git a/client/finality-grandpa/src/import.rs b/client/finality-grandpa/src/import.rs index ed6e4a0fa..f5e2f9643 100644 --- a/client/finality-grandpa/src/import.rs +++ b/client/finality-grandpa/src/import.rs @@ -34,7 +34,7 @@ use sp_consensus::{BlockOrigin, Error as ConsensusError, SelectChain}; use sp_core::hashing::twox_128; use sp_finality_grandpa::{ConsensusLog, GrandpaApi, ScheduledChange, SetId, GRANDPA_ENGINE_ID}; use sp_runtime::{ - generic::{BlockId, OpaqueDigestItemId}, + generic::OpaqueDigestItemId, traits::{Block as BlockT, Header as HeaderT, NumberFor, Zero}, Justification, }; @@ -424,8 +424,7 @@ where /// Read current set id form a given state. fn current_set_id(&self, hash: Block::Hash) -> Result { - let id = &BlockId::hash(hash); - let runtime_version = self.inner.runtime_api().version(id).map_err(|e| { + let runtime_version = self.inner.runtime_api().version(hash).map_err(|e| { ConsensusError::ClientImport(format!( "Unable to retrieve current runtime version. {}", e @@ -452,7 +451,7 @@ where } else { self.inner .runtime_api() - .current_set_id(id) + .current_set_id(hash) .map_err(|e| ConsensusError::ClientImport(e.to_string())) } } @@ -477,7 +476,7 @@ where let authorities = self .inner .runtime_api() - .grandpa_authorities(&BlockId::hash(hash)) + .grandpa_authorities(hash) .map_err(|e| ConsensusError::ClientImport(e.to_string()))?; let set_id = self.current_set_id(hash)?; let authority_set = AuthoritySet::new( diff --git a/client/merkle-mountain-range/rpc/src/lib.rs b/client/merkle-mountain-range/rpc/src/lib.rs index 6e520b3bf..5c65be57e 100644 --- a/client/merkle-mountain-range/rpc/src/lib.rs +++ b/client/merkle-mountain-range/rpc/src/lib.rs @@ -34,7 +34,7 @@ use sp_api::{NumberFor, ProvideRuntimeApi}; use sp_blockchain::HeaderBackend; use sp_core::Bytes; use sp_mmr_primitives::{Error as MmrError, Proof}; -use sp_runtime::{generic::BlockId, traits::Block as BlockT}; +use sp_runtime::traits::Block as BlockT; pub use sp_mmr_primitives::MmrApi as MmrRuntimeApi; @@ -154,7 +154,7 @@ where self.client.info().best_hash); let api = self.client.runtime_api(); let mmr_root = api - .mmr_root(&BlockId::Hash(block_hash)) + .mmr_root(block_hash) .map_err(runtime_error_into_rpc_error)? .map_err(mmr_error_into_rpc_error)?; Ok(mmr_root) @@ -173,7 +173,7 @@ where let (leaves, proof) = api .generate_proof_with_context( - &BlockId::hash(block_hash), + block_hash, sp_core::ExecutionContext::OffchainCall(None), block_numbers, best_known_block_number, @@ -194,7 +194,7 @@ where .map_err(|e| CallError::InvalidParams(anyhow::Error::new(e)))?; api.verify_proof_with_context( - &BlockId::hash(proof.block_hash), + proof.block_hash, sp_core::ExecutionContext::OffchainCall(None), leaves, decoded_proof, @@ -218,14 +218,9 @@ where let decoded_proof = Decode::decode(&mut &proof.proof.0[..]) .map_err(|e| CallError::InvalidParams(anyhow::Error::new(e)))?; - api.verify_proof_stateless( - &BlockId::hash(proof.block_hash), - mmr_root, - leaves, - decoded_proof, - ) - .map_err(runtime_error_into_rpc_error)? - .map_err(mmr_error_into_rpc_error)?; + api.verify_proof_stateless(proof.block_hash, mmr_root, leaves, decoded_proof) + .map_err(runtime_error_into_rpc_error)? + .map_err(mmr_error_into_rpc_error)?; Ok(true) } diff --git a/client/merkle-mountain-range/src/lib.rs b/client/merkle-mountain-range/src/lib.rs index 568c4b94c..ce7223525 100644 --- a/client/merkle-mountain-range/src/lib.rs +++ b/client/merkle-mountain-range/src/lib.rs @@ -51,10 +51,7 @@ use sc_offchain::OffchainDb; use sp_api::ProvideRuntimeApi; use sp_blockchain::{HeaderBackend, HeaderMetadata}; use sp_mmr_primitives::{utils, LeafIndex, MmrApi}; -use sp_runtime::{ - generic::BlockId, - traits::{Block, Header, NumberFor}, -}; +use sp_runtime::traits::{Block, Header, NumberFor}; use std::{marker::PhantomData, sync::Arc}; /// Logging target for the mmr gadget. @@ -71,15 +68,16 @@ where { /// Get the block number where the mmr pallet was added to the runtime. fn first_mmr_block_num(&self, notification: &FinalityNotification) -> Option> { - let best_block = *notification.header.number(); - match self.runtime_api().mmr_leaf_count(&BlockId::number(best_block)) { + let best_block_hash = notification.header.hash(); + let best_block_number = *notification.header.number(); + match self.runtime_api().mmr_leaf_count(best_block_hash) { Ok(Ok(mmr_leaf_count)) => { - match utils::first_mmr_block_num::(best_block, mmr_leaf_count) { + match utils::first_mmr_block_num::(best_block_number, mmr_leaf_count) { Ok(first_mmr_block) => { debug!( target: LOG_TARGET, "pallet-mmr detected at block {:?} with genesis at block {:?}", - best_block, + best_block_number, first_mmr_block ); Some(first_mmr_block) @@ -97,7 +95,7 @@ where trace!( target: LOG_TARGET, "pallet-mmr not detected at block {:?} ... (best finalized {:?})", - best_block, + best_block_number, notification.header.number() ); None diff --git a/client/offchain/src/lib.rs b/client/offchain/src/lib.rs index 6cf5838a4..904d127e1 100644 --- a/client/offchain/src/lib.rs +++ b/client/offchain/src/lib.rs @@ -45,10 +45,7 @@ use parking_lot::Mutex; use sc_network_common::service::{NetworkPeers, NetworkStateInfo}; use sp_api::{ApiExt, ProvideRuntimeApi}; use sp_core::{offchain, traits::SpawnNamed, ExecutionContext}; -use sp_runtime::{ - generic::BlockId, - traits::{self, Header}, -}; +use sp_runtime::traits::{self, Header}; use threadpool::ThreadPool; mod api; @@ -123,9 +120,9 @@ where is_validator: bool, ) -> impl Future { let runtime = self.client.runtime_api(); - let at = BlockId::hash(header.hash()); - let has_api_v1 = runtime.has_api_with::, _>(&at, |v| v == 1); - let has_api_v2 = runtime.has_api_with::, _>(&at, |v| v == 2); + let hash = header.hash(); + let has_api_v1 = runtime.has_api_with::, _>(hash, |v| v == 1); + let has_api_v2 = runtime.has_api_with::, _>(hash, |v| v == 2); let version = match (has_api_v1, has_api_v2) { (_, Ok(true)) => 2, (Ok(true), _) => 1, @@ -144,13 +141,13 @@ where tracing::debug!( target: LOG_TARGET, "Checking offchain workers at {:?}: version:{}", - at, + hash, version ); let process = (version > 0).then(|| { let (api, runner) = api::AsyncApi::new(network_provider, is_validator, self.shared_http_client.clone()); - tracing::debug!(target: LOG_TARGET, "Spawning offchain workers at {:?}", at); + tracing::debug!(target: LOG_TARGET, "Spawning offchain workers at {:?}", hash); let header = header.clone(); let client = self.client.clone(); @@ -160,15 +157,15 @@ where self.spawn_worker(move || { let runtime = client.runtime_api(); let api = Box::new(api); - tracing::debug!(target: LOG_TARGET, "Running offchain workers at {:?}", at); + tracing::debug!(target: LOG_TARGET, "Running offchain workers at {:?}", hash); let context = ExecutionContext::OffchainCall(Some((api, capabilities))); let run = if version == 2 { - runtime.offchain_worker_with_context(&at, context, &header) + runtime.offchain_worker_with_context(hash, context, &header) } else { #[allow(deprecated)] runtime.offchain_worker_before_version_2_with_context( - &at, + hash, context, *header.number(), ) @@ -177,7 +174,7 @@ where tracing::error!( target: LOG_TARGET, "Error running offchain workers at {:?}: {}", - at, + hash, e ); } @@ -254,6 +251,7 @@ mod tests { use sc_transaction_pool::{BasicPool, FullChainApi}; use sc_transaction_pool_api::{InPoolTransaction, TransactionPool}; use sp_consensus::BlockOrigin; + use sp_runtime::generic::BlockId; use std::{collections::HashSet, sync::Arc}; use substrate_test_runtime_client::{ runtime::Block, ClientBlockImportExt, DefaultTestClientBuilderExt, TestClient, diff --git a/client/rpc-spec-v2/src/chain_head/chain_head.rs b/client/rpc-spec-v2/src/chain_head/chain_head.rs index 2a9cabaf2..76963d81e 100644 --- a/client/rpc-spec-v2/src/chain_head/chain_head.rs +++ b/client/rpc-spec-v2/src/chain_head/chain_head.rs @@ -53,10 +53,7 @@ use sp_blockchain::{ Backend as BlockChainBackend, Error as BlockChainError, HeaderBackend, HeaderMetadata, }; use sp_core::{hexdisplay::HexDisplay, storage::well_known_keys, Bytes}; -use sp_runtime::{ - generic::BlockId, - traits::{Block as BlockT, Header}, -}; +use sp_runtime::traits::{Block as BlockT, Header}; use std::{marker::PhantomData, sync::Arc}; /// An API for chain head RPC calls. @@ -142,12 +139,8 @@ where let finalized_block_hash = client.info().finalized_hash; handle.pin_block(finalized_block_hash)?; - let finalized_block_runtime = generate_runtime_event( - &client, - runtime_updates, - &BlockId::Hash(finalized_block_hash), - None, - ); + let finalized_block_runtime = + generate_runtime_event(&client, runtime_updates, finalized_block_hash, None); let initialized_event = FollowEvent::Initialized(Initialized { finalized_block_hash, @@ -162,12 +155,7 @@ where for (child, parent) in initial_blocks.into_iter() { handle.pin_block(child)?; - let new_runtime = generate_runtime_event( - &client, - runtime_updates, - &BlockId::Hash(child), - Some(&BlockId::Hash(parent)), - ); + let new_runtime = generate_runtime_event(&client, runtime_updates, child, Some(parent)); let event = FollowEvent::NewBlock(NewBlock { block_hash: child, @@ -214,8 +202,8 @@ fn parse_hex_param( fn generate_runtime_event( client: &Arc, runtime_updates: bool, - block: &BlockId, - parent: Option<&BlockId>, + block: Block::Hash, + parent: Option, ) -> Option where Block: BlockT + 'static, @@ -329,8 +317,8 @@ where let new_runtime = generate_runtime_event( &client, runtime_updates, - &BlockId::Hash(notification.hash), - Some(&BlockId::Hash(*notification.header.parent_hash())), + notification.hash, + Some(*notification.header.parent_hash()), ); // Note: `Block::Hash` will serialize to hexadecimal encoded string. diff --git a/client/rpc/src/author/mod.rs b/client/rpc/src/author/mod.rs index 7d0ffdc62..cf47e9faf 100644 --- a/client/rpc/src/author/mod.rs +++ b/client/rpc/src/author/mod.rs @@ -123,7 +123,7 @@ where let best_block_hash = self.client.info().best_hash; self.client .runtime_api() - .generate_session_keys(&generic::BlockId::Hash(best_block_hash), None) + .generate_session_keys(best_block_hash, None) .map(Into::into) .map_err(|api_err| Error::Client(Box::new(api_err)).into()) } @@ -135,7 +135,7 @@ where let keys = self .client .runtime_api() - .decode_session_keys(&generic::BlockId::Hash(best_block_hash), session_keys.to_vec()) + .decode_session_keys(best_block_hash, session_keys.to_vec()) .map_err(|e| Error::Client(Box::new(e)))? .ok_or(Error::InvalidSessionKeys)?; diff --git a/client/rpc/src/dev/mod.rs b/client/rpc/src/dev/mod.rs index e48a8ee4e..fa5d358b0 100644 --- a/client/rpc/src/dev/mod.rs +++ b/client/rpc/src/dev/mod.rs @@ -28,7 +28,7 @@ use sc_rpc_api::{dev::error::Error, DenyUnsafe}; use sp_api::{ApiExt, Core, ProvideRuntimeApi}; use sp_core::Encode; use sp_runtime::{ - generic::{BlockId, DigestItem}, + generic::DigestItem, traits::{Block as BlockT, Header}, }; use std::{ @@ -98,7 +98,7 @@ where let mut runtime_api = self.client.runtime_api(); runtime_api.record_proof(); runtime_api - .execute_block(&BlockId::Hash(parent_header.hash()), block) + .execute_block(parent_header.hash(), block) .map_err(|_| Error::BlockExecutionFailed)?; let witness = runtime_api .extract_proof() diff --git a/client/rpc/src/state/state_full.rs b/client/rpc/src/state/state_full.rs index 58dfd9ea8..b0c0922c6 100644 --- a/client/rpc/src/state/state_full.rs +++ b/client/rpc/src/state/state_full.rs @@ -48,7 +48,7 @@ use sp_core::{ }, Bytes, }; -use sp_runtime::{generic::BlockId, traits::Block as BlockT}; +use sp_runtime::traits::Block as BlockT; use sp_version::RuntimeVersion; /// The maximum time allowed for an RPC call when running without unsafe RPC enabled. @@ -321,7 +321,7 @@ where self.block_or_best(block).map_err(client_err).and_then(|block| { self.client .runtime_api() - .metadata(&BlockId::Hash(block)) + .metadata(block) .map(Into::into) .map_err(|e| Error::Client(Box::new(e))) }) @@ -332,9 +332,7 @@ where block: Option, ) -> std::result::Result { self.block_or_best(block).map_err(client_err).and_then(|block| { - self.client - .runtime_version_at(&BlockId::Hash(block)) - .map_err(|e| Error::Client(Box::new(e))) + self.client.runtime_version_at(block).map_err(|e| Error::Client(Box::new(e))) }) } @@ -383,9 +381,7 @@ where let initial = match self .block_or_best(None) - .and_then(|block| { - self.client.runtime_version_at(&BlockId::Hash(block)).map_err(Into::into) - }) + .and_then(|block| self.client.runtime_version_at(block).map_err(Into::into)) .map_err(|e| Error::Client(Box::new(e))) { Ok(initial) => initial, @@ -402,9 +398,8 @@ where .import_notification_stream() .filter(|n| future::ready(n.is_new_best)) .filter_map(move |n| { - let version = client - .runtime_version_at(&BlockId::hash(n.hash)) - .map_err(|e| Error::Client(Box::new(e))); + let version = + client.runtime_version_at(n.hash).map_err(|e| Error::Client(Box::new(e))); match version { Ok(version) if version != previous_version => { diff --git a/client/service/src/client/client.rs b/client/service/src/client/client.rs index d452104ce..7bd37b516 100644 --- a/client/service/src/client/client.rs +++ b/client/service/src/client/client.rs @@ -838,7 +838,6 @@ where CoreApi + ApiExt, { let parent_hash = import_block.header.parent_hash(); - let at = BlockId::Hash(*parent_hash); let state_action = std::mem::replace(&mut import_block.state_action, StateAction::Skip); let (enact_state, storage_changes) = match (self.block_status(*parent_hash)?, state_action) { @@ -870,7 +869,7 @@ where let execution_context = import_block.origin.into(); runtime_api.execute_block_with_context( - &at, + *parent_hash, execution_context, Block::new(import_block.header.clone(), body.clone()), )?; @@ -1725,10 +1724,9 @@ where &self, params: CallApiAtParams, ) -> Result, sp_api::ApiError> { - let at_hash = self.expect_block_hash_from_id(params.at)?; self.executor .contextual_call( - at_hash, + params.at, params.function, ¶ms.arguments, params.overlayed_changes, @@ -1739,8 +1737,7 @@ where .map_err(Into::into) } - fn runtime_version_at(&self, at: &BlockId) -> Result { - let hash = self.backend.blockchain().expect_block_hash_from_id(at)?; + fn runtime_version_at(&self, hash: Block::Hash) -> Result { CallExecutor::runtime_version(&self.executor, hash).map_err(Into::into) } diff --git a/client/service/test/src/client/mod.rs b/client/service/test/src/client/mod.rs index 12b92afc4..53af18c6b 100644 --- a/client/service/test/src/client/mod.rs +++ b/client/service/test/src/client/mod.rs @@ -291,20 +291,14 @@ fn client_initializes_from_genesis_ok() { assert_eq!( client .runtime_api() - .balance_of( - &BlockId::Number(client.chain_info().best_number), - AccountKeyring::Alice.into(), - ) + .balance_of(client.chain_info().best_hash, AccountKeyring::Alice.into()) .unwrap(), 1000 ); assert_eq!( client .runtime_api() - .balance_of( - &BlockId::Number(client.chain_info().best_number), - AccountKeyring::Ferdie.into(), - ) + .balance_of(client.chain_info().best_hash, AccountKeyring::Ferdie.into()) .unwrap(), 0 ); @@ -351,20 +345,14 @@ fn block_builder_works_with_transactions() { assert_eq!( client .runtime_api() - .balance_of( - &BlockId::Number(client.chain_info().best_number), - AccountKeyring::Alice.into(), - ) + .balance_of(client.chain_info().best_hash, AccountKeyring::Alice.into()) .unwrap(), 958 ); assert_eq!( client .runtime_api() - .balance_of( - &BlockId::Number(client.chain_info().best_number), - AccountKeyring::Ferdie.into(), - ) + .balance_of(client.chain_info().best_hash, AccountKeyring::Ferdie.into()) .unwrap(), 42 ); @@ -1256,10 +1244,7 @@ fn state_reverted_on_reorg() { let current_balance = |client: &substrate_test_runtime_client::TestClient| { client .runtime_api() - .balance_of( - &BlockId::number(client.chain_info().best_number), - AccountKeyring::Alice.into(), - ) + .balance_of(client.chain_info().best_hash, AccountKeyring::Alice.into()) .unwrap() }; @@ -1996,10 +1981,10 @@ fn use_dalek_ext_works() { // On block zero it will use dalek and then on block 1 it will use zebra assert!(!client .runtime_api() - .verify_ed25519(&BlockId::Number(0), zero_ed_sig(), zero_ed_pub(), vec![]) + .verify_ed25519(client.chain_info().genesis_hash, zero_ed_sig(), zero_ed_pub(), vec![]) .unwrap()); assert!(client .runtime_api() - .verify_ed25519(&BlockId::Number(1), zero_ed_sig(), zero_ed_pub(), vec![]) + .verify_ed25519(a1.hash(), zero_ed_sig(), zero_ed_pub(), vec![]) .unwrap()); } diff --git a/client/tracing/src/block/mod.rs b/client/tracing/src/block/mod.rs index 89c0945e0..465767781 100644 --- a/client/tracing/src/block/mod.rs +++ b/client/tracing/src/block/mod.rs @@ -229,7 +229,6 @@ where .ok_or_else(|| Error::MissingBlockComponent("Extrinsics not found".to_string()))?; tracing::debug!(target: "state_tracing", "Found {} extrinsics", extrinsics.len()); let parent_hash = *header.parent_hash(); - let parent_id = BlockId::Hash(parent_hash); // Remove all `Seal`s as they are added by the consensus engines after building the block. // On import they are normally removed by the consensus engine. header.digest_mut().logs.retain(|d| d.as_seal().is_none()); @@ -249,7 +248,7 @@ where if let Err(e) = dispatcher::with_default(&dispatch, || { let span = tracing::info_span!(target: TRACE_TARGET, "trace_block"); let _enter = span.enter(); - self.client.runtime_api().execute_block(&parent_id, block) + self.client.runtime_api().execute_block(parent_hash, block) }) { return Err(Error::Dispatch(format!( "Failed to collect traces and execute block: {}", @@ -298,7 +297,7 @@ where } else { TraceBlockResponse::BlockTrace(BlockTrace { block_hash: block_id_as_string(BlockId::::Hash(self.block)), - parent_hash: block_id_as_string(parent_id), + parent_hash: block_id_as_string(BlockId::::Hash(parent_hash)), tracing_targets: targets.to_string(), storage_keys: self.storage_keys.clone().unwrap_or_default(), methods: self.methods.clone().unwrap_or_default(), diff --git a/client/transaction-pool/src/api.rs b/client/transaction-pool/src/api.rs index 0166dface..9e20915db 100644 --- a/client/transaction-pool/src/api.rs +++ b/client/transaction-pool/src/api.rs @@ -225,27 +225,27 @@ where { sp_tracing::within_span!(sp_tracing::Level::TRACE, "validate_transaction"; { + let block_hash = client.to_hash(at) + .map_err(|e| Error::RuntimeApi(e.to_string()))? + .ok_or_else(|| Error::RuntimeApi(format!("Could not get hash for block `{:?}`.", at)))?; + let runtime_api = client.runtime_api(); let api_version = sp_tracing::within_span! { sp_tracing::Level::TRACE, "check_version"; runtime_api - .api_version::>(at) + .api_version::>(block_hash) .map_err(|e| Error::RuntimeApi(e.to_string()))? .ok_or_else(|| Error::RuntimeApi( format!("Could not find `TaggedTransactionQueue` api for block `{:?}`.", at) )) }?; - let block_hash = client.to_hash(at) - .map_err(|e| Error::RuntimeApi(e.to_string()))? - .ok_or_else(|| Error::RuntimeApi(format!("Could not get hash for block `{:?}`.", at)))?; - use sp_api::Core; sp_tracing::within_span!( sp_tracing::Level::TRACE, "runtime::validate_transaction"; { if api_version >= 3 { - runtime_api.validate_transaction(at, source, uxt, block_hash) + runtime_api.validate_transaction(block_hash, source, uxt, block_hash) .map_err(|e| Error::RuntimeApi(e.to_string())) } else { let block_number = client.to_number(at) @@ -255,7 +255,7 @@ where )?; // The old versions require us to call `initialize_block` before. - runtime_api.initialize_block(at, &sp_runtime::traits::Header::new( + runtime_api.initialize_block(block_hash, &sp_runtime::traits::Header::new( block_number + sp_runtime::traits::One::one(), Default::default(), Default::default(), @@ -265,11 +265,11 @@ where if api_version == 2 { #[allow(deprecated)] // old validate_transaction - runtime_api.validate_transaction_before_version_3(at, source, uxt) + runtime_api.validate_transaction_before_version_3(block_hash, source, uxt) .map_err(|e| Error::RuntimeApi(e.to_string())) } else { #[allow(deprecated)] // old validate_transaction - runtime_api.validate_transaction_before_version_2(at, uxt) + runtime_api.validate_transaction_before_version_2(block_hash, uxt) .map_err(|e| Error::RuntimeApi(e.to_string())) } } diff --git a/frame/transaction-payment/rpc/src/lib.rs b/frame/transaction-payment/rpc/src/lib.rs index 19007d379..46ad5dbc0 100644 --- a/frame/transaction-payment/rpc/src/lib.rs +++ b/frame/transaction-payment/rpc/src/lib.rs @@ -30,10 +30,7 @@ use sp_api::{ApiExt, ProvideRuntimeApi}; use sp_blockchain::HeaderBackend; use sp_core::Bytes; use sp_rpc::number::NumberOrHex; -use sp_runtime::{ - generic::BlockId, - traits::{Block as BlockT, MaybeDisplay}, -}; +use sp_runtime::traits::{Block as BlockT, MaybeDisplay}; pub use pallet_transaction_payment_rpc_runtime_api::TransactionPaymentApi as TransactionPaymentRuntimeApi; @@ -98,7 +95,7 @@ where at: Option, ) -> RpcResult> { let api = self.client.runtime_api(); - let at = BlockId::hash(at.unwrap_or_else(|| self.client.info().best_hash)); + let at_hash = at.unwrap_or_else(|| self.client.info().best_hash); let encoded_len = encoded_xt.len() as u32; @@ -119,7 +116,7 @@ where } let api_version = api - .api_version::>(&at) + .api_version::>(at_hash) .map_err(|e| map_err(e, "Failed to get transaction payment runtime api version"))? .ok_or_else(|| { CallError::Custom(ErrorObject::owned( @@ -131,11 +128,11 @@ where if api_version < 2 { #[allow(deprecated)] - api.query_info_before_version_2(&at, uxt, encoded_len) + api.query_info_before_version_2(at_hash, uxt, encoded_len) .map_err(|e| map_err(e, "Unable to query dispatch info.").into()) } else { let res = api - .query_info(&at, uxt, encoded_len) + .query_info(at_hash, uxt, encoded_len) .map_err(|e| map_err(e, "Unable to query dispatch info."))?; Ok(RuntimeDispatchInfo { @@ -152,7 +149,7 @@ where at: Option, ) -> RpcResult> { let api = self.client.runtime_api(); - let at = BlockId::hash(at.unwrap_or_else(|| self.client.info().best_hash)); + let at_hash = at.unwrap_or_else(|| self.client.info().best_hash); let encoded_len = encoded_xt.len() as u32; @@ -163,7 +160,7 @@ where Some(format!("{:?}", e)), )) })?; - let fee_details = api.query_fee_details(&at, uxt, encoded_len).map_err(|e| { + let fee_details = api.query_fee_details(at_hash, uxt, encoded_len).map_err(|e| { CallError::Custom(ErrorObject::owned( Error::RuntimeError.into(), "Unable to query fee details.", diff --git a/primitives/api/proc-macro/src/decl_runtime_apis.rs b/primitives/api/proc-macro/src/decl_runtime_apis.rs index 8d46047db..c254237cc 100644 --- a/primitives/api/proc-macro/src/decl_runtime_apis.rs +++ b/primitives/api/proc-macro/src/decl_runtime_apis.rs @@ -316,7 +316,7 @@ fn generate_runtime_decls(decls: &[ItemTrait]) -> Result { /// Modify the given runtime api declaration to be usable on the client side. struct ToClientSideDecl<'a> { - block_id: &'a TokenStream, + block_hash: &'a TokenStream, crate_: &'a TokenStream, found_attributes: &'a mut HashMap<&'static str, Attribute>, /// Any error that we found while converting this declaration. @@ -329,7 +329,7 @@ impl<'a> ToClientSideDecl<'a> { fn process(mut self, decl: ItemTrait) -> ItemTrait { let mut decl = self.fold_item_trait(decl); - let block_id = self.block_id; + let block_hash = self.block_hash; let crate_ = self.crate_; // Add the special method that will be implemented by the `impl_runtime_apis!` macro @@ -339,7 +339,7 @@ impl<'a> ToClientSideDecl<'a> { #[doc(hidden)] fn __runtime_api_internal_call_api_at( &self, - at: &#block_id, + at: #block_hash, context: #crate_::ExecutionContext, params: std::vec::Vec, fn_name: &dyn Fn(#crate_::RuntimeVersion) -> &'static str, @@ -420,7 +420,7 @@ impl<'a> ToClientSideDecl<'a> { }; let ret_type = return_type_extract_type(&method.sig.output); - fold_fn_decl_for_client_side(&mut method.sig, self.block_id, self.crate_); + fold_fn_decl_for_client_side(&mut method.sig, self.block_hash, self.crate_); let crate_ = self.crate_; @@ -621,14 +621,14 @@ fn generate_client_side_decls(decls: &[ItemTrait]) -> Result { let decl = decl.clone(); let crate_ = generate_crate_access(HIDDEN_INCLUDES_ID); - let block_id = quote!( #crate_::BlockId ); + let block_hash = quote!( ::Hash ); let mut found_attributes = HashMap::new(); let mut errors = Vec::new(); let trait_ = decl.ident.clone(); let decl = ToClientSideDecl { crate_: &crate_, - block_id: &block_id, + block_hash: &block_hash, found_attributes: &mut found_attributes, errors: &mut errors, trait_: &trait_, diff --git a/primitives/api/proc-macro/src/impl_runtime_apis.rs b/primitives/api/proc-macro/src/impl_runtime_apis.rs index c3f4e3665..76c9e1164 100644 --- a/primitives/api/proc-macro/src/impl_runtime_apis.rs +++ b/primitives/api/proc-macro/src/impl_runtime_apis.rs @@ -233,7 +233,7 @@ fn generate_runtime_api_base_structures() -> Result { fn has_api( &self, - at: &#crate_::BlockId, + at: ::Hash, ) -> std::result::Result where Self: Sized { #crate_::CallApiAt::::runtime_version_at(self.call, at) .map(|v| #crate_::RuntimeVersion::has_api_with(&v, &A::ID, |v| v == A::VERSION)) @@ -241,7 +241,7 @@ fn generate_runtime_api_base_structures() -> Result { fn has_api_with bool>( &self, - at: &#crate_::BlockId, + at: ::Hash, pred: P, ) -> std::result::Result where Self: Sized { #crate_::CallApiAt::::runtime_version_at(self.call, at) @@ -250,7 +250,7 @@ fn generate_runtime_api_base_structures() -> Result { fn api_version( &self, - at: &#crate_::BlockId, + at: ::Hash, ) -> std::result::Result, #crate_::ApiError> where Self: Sized { #crate_::CallApiAt::::runtime_version_at(self.call, at) .map(|v| #crate_::RuntimeVersion::api_version(&v, &A::ID)) @@ -281,8 +281,7 @@ fn generate_runtime_api_base_structures() -> Result { #crate_::StorageChanges, String > where Self: Sized { - let at = #crate_::BlockId::Hash(std::clone::Clone::clone(&parent_hash)); - let state_version = #crate_::CallApiAt::::runtime_version_at(self.call, &at) + let state_version = #crate_::CallApiAt::::runtime_version_at(self.call, std::clone::Clone::clone(&parent_hash)) .map(|v| #crate_::RuntimeVersion::state_version(&v)) .map_err(|e| format!("Failed to get state version: {}", e))?; @@ -424,7 +423,7 @@ impl<'a> ApiRuntimeImplToApiRuntimeApiImpl<'a> { input.items.push(parse_quote! { fn __runtime_api_internal_call_api_at( &self, - at: &#crate_::BlockId<__SR_API_BLOCK__>, + at: <__SR_API_BLOCK__ as #crate_::BlockT>::Hash, context: #crate_::ExecutionContext, params: std::vec::Vec, fn_name: &dyn Fn(#crate_::RuntimeVersion) -> &'static str, diff --git a/primitives/api/proc-macro/src/mock_impl_runtime_apis.rs b/primitives/api/proc-macro/src/mock_impl_runtime_apis.rs index e43a302e1..493111e4f 100644 --- a/primitives/api/proc-macro/src/mock_impl_runtime_apis.rs +++ b/primitives/api/proc-macro/src/mock_impl_runtime_apis.rs @@ -38,7 +38,7 @@ const HIDDEN_INCLUDES_ID: &str = "MOCK_IMPL_RUNTIME_APIS"; /// The `advanced` attribute. /// -/// If this attribute is given to a function, the function gets access to the `BlockId` as first +/// If this attribute is given to a function, the function gets access to the `Hash` as first /// parameter and needs to return a `Result` with the appropriate error type. const ADVANCED_ATTRIBUTE: &str = "advanced"; @@ -80,14 +80,14 @@ fn implement_common_api_traits(block_type: TypePath, self_ty: Type) -> Result( &self, - _: &#crate_::BlockId<#block_type>, + _: ::Hash, ) -> std::result::Result where Self: Sized { Ok(true) } fn has_api_with bool>( &self, - _: &#crate_::BlockId<#block_type>, + _: ::Hash, pred: P, ) -> std::result::Result where Self: Sized { Ok(pred(A::VERSION)) @@ -95,7 +95,7 @@ fn implement_common_api_traits(block_type: TypePath, self_ty: Type) -> Result( &self, - _: &#crate_::BlockId<#block_type>, + _: ::Hash, ) -> std::result::Result, #crate_::ApiError> where Self: Sized { Ok(Some(A::VERSION)) } @@ -129,7 +129,7 @@ fn implement_common_api_traits(block_type: TypePath, self_ty: Type) -> Result for #self_ty { fn __runtime_api_internal_call_api_at( &self, - _: &#crate_::BlockId<#block_type>, + _: <#block_type as #crate_::BlockT>::Hash, _: #crate_::ExecutionContext, _: std::vec::Vec, _: &dyn Fn(#crate_::RuntimeVersion) -> &'static str, @@ -139,14 +139,14 @@ fn implement_common_api_traits(block_type: TypePath, self_ty: Type) -> Result, + _: <#block_type as #crate_::BlockT>::Hash, ) -> std::result::Result<#crate_::RuntimeVersion, #crate_::ApiError> { unimplemented!("`Core::version` not implemented for runtime api mocks") } fn version_with_context( &self, - _: &#crate_::BlockId<#block_type>, + _: <#block_type as #crate_::BlockT>::Hash, _: #crate_::ExecutionContext, ) -> std::result::Result<#crate_::RuntimeVersion, #crate_::ApiError> { unimplemented!("`Core::version` not implemented for runtime api mocks") @@ -154,7 +154,7 @@ fn implement_common_api_traits(block_type: TypePath, self_ty: Type) -> Result, + _: <#block_type as #crate_::BlockT>::Hash, _: #block_type, ) -> std::result::Result<(), #crate_::ApiError> { unimplemented!("`Core::execute_block` not implemented for runtime api mocks") @@ -162,7 +162,7 @@ fn implement_common_api_traits(block_type: TypePath, self_ty: Type) -> Result, + _: <#block_type as #crate_::BlockT>::Hash, _: #crate_::ExecutionContext, _: #block_type, ) -> std::result::Result<(), #crate_::ApiError> { @@ -171,7 +171,7 @@ fn implement_common_api_traits(block_type: TypePath, self_ty: Type) -> Result, + _: <#block_type as #crate_::BlockT>::Hash, _: &<#block_type as #crate_::BlockT>::Header, ) -> std::result::Result<(), #crate_::ApiError> { unimplemented!("`Core::initialize_block` not implemented for runtime api mocks") @@ -179,7 +179,7 @@ fn implement_common_api_traits(block_type: TypePath, self_ty: Type) -> Result, + _: <#block_type as #crate_::BlockT>::Hash, _: #crate_::ExecutionContext, _: &<#block_type as #crate_::BlockT>::Header, ) -> std::result::Result<(), #crate_::ApiError> { @@ -214,7 +214,7 @@ fn get_at_param_name( param_names: &mut Vec, param_types_and_borrows: &mut Vec<(TokenStream, bool)>, function_span: Span, - default_block_id_type: &TokenStream, + default_hash_type: &TokenStream, ) -> Result<(TokenStream, TokenStream)> { if is_advanced { if param_names.is_empty() { @@ -222,7 +222,7 @@ fn get_at_param_name( function_span, format!( "If using the `{}` attribute, it is required that the function \ - takes at least one argument, the `BlockId`.", + takes at least one argument, the `Hash`.", ADVANCED_ATTRIBUTE, ), )) @@ -232,17 +232,14 @@ fn get_at_param_name( // `param_types` can not be empty as well. let ptype_and_borrows = param_types_and_borrows.remove(0); let span = ptype_and_borrows.1.span(); - if !ptype_and_borrows.1 { - return Err(Error::new( - span, - "`BlockId` needs to be taken by reference and not by value!", - )) + if ptype_and_borrows.1 { + return Err(Error::new(span, "`Hash` needs to be taken by value and not by reference!")) } let name = param_names.remove(0); Ok((quote!( #name ), ptype_and_borrows.0)) } else { - Ok((quote!(_), default_block_id_type.clone())) + Ok((quote!(_), default_hash_type.clone())) } } @@ -279,7 +276,7 @@ impl<'a> FoldRuntimeApiImpl<'a> { impl_item.items.push(parse_quote! { fn __runtime_api_internal_call_api_at( &self, - _: &#crate_::BlockId<#block_type>, + _: <#block_type as #crate_::BlockT>::Hash, _: #crate_::ExecutionContext, _: std::vec::Vec, _: &dyn Fn(#crate_::RuntimeVersion) -> &'static str, @@ -325,19 +322,19 @@ impl<'a> Fold for FoldRuntimeApiImpl<'a> { }; let block_type = &self.block_type; - let block_id_type = quote!( &#crate_::BlockId<#block_type> ); + let hash_type = quote!( <#block_type as #crate_::BlockT>::Hash ); - let (at_param_name, block_id_type) = match get_at_param_name( + let (at_param_name, hash_type) = match get_at_param_name( is_advanced, &mut param_names, &mut param_types_and_borrows, input.span(), - &block_id_type, + &hash_type, ) { Ok(res) => res, Err(e) => { errors.push(e.to_compile_error()); - (quote!(_), block_id_type) + (quote!(_), hash_type) }, }; @@ -345,7 +342,7 @@ impl<'a> Fold for FoldRuntimeApiImpl<'a> { // Rewrite the input parameters. input.sig.inputs = parse_quote! { &self, - #at_param_name: #block_id_type, + #at_param_name: #hash_type, #( #param_names: #param_types ),* }; diff --git a/primitives/api/proc-macro/src/utils.rs b/primitives/api/proc-macro/src/utils.rs index 2ccd050cf..6f2a766e9 100644 --- a/primitives/api/proc-macro/src/utils.rs +++ b/primitives/api/proc-macro/src/utils.rs @@ -94,13 +94,13 @@ pub fn replace_wild_card_parameter_names(input: &mut Signature) { /// Fold the given `Signature` to make it usable on the client side. pub fn fold_fn_decl_for_client_side( input: &mut Signature, - block_id: &TokenStream, + block_hash: &TokenStream, crate_: &TokenStream, ) { replace_wild_card_parameter_names(input); - // Add `&self, at:& BlockId` as parameters to each function at the beginning. - input.inputs.insert(0, parse_quote!( __runtime_api_at_param__: &#block_id )); + // Add `&self, at:& Block::Hash` as parameters to each function at the beginning. + input.inputs.insert(0, parse_quote!( __runtime_api_at_param__: #block_hash )); input.inputs.insert(0, parse_quote!(&self)); // Wrap the output in a `Result` diff --git a/primitives/api/src/lib.rs b/primitives/api/src/lib.rs index ad9a9b181..c9605b811 100644 --- a/primitives/api/src/lib.rs +++ b/primitives/api/src/lib.rs @@ -115,7 +115,7 @@ pub const MAX_EXTRINSIC_DEPTH: u32 = 256; /// The macro will create two declarations, one for using on the client side and one for using /// on the runtime side. The declaration for the runtime side is hidden in its own module. /// The client side declaration gets two extra parameters per function, -/// `&self` and `at: &BlockId`. The runtime side declaration will match the given trait +/// `&self` and `at: Block::Hash`. The runtime side declaration will match the given trait /// declaration. Besides one exception, the macro adds an extra generic parameter `Block: /// BlockT` to the client side and the runtime side. This generic parameter is usable by the /// user. @@ -182,7 +182,7 @@ pub const MAX_EXTRINSIC_DEPTH: u32 = 256; /// ``` /// /// To check if a given runtime implements a runtime api trait, the `RuntimeVersion` has the -/// function `has_api()`. Also the `ApiExt` provides a function `has_api(at: &BlockId)` +/// function `has_api()`. Also the `ApiExt` provides a function `has_api(at: Hash)` /// to check if the runtime at the given block id implements the requested runtime api trait. /// /// # Declaring multiple api versions @@ -400,16 +400,16 @@ pub use sp_api_proc_macro::impl_runtime_apis; /// /// This attribute can be placed above individual function in the mock implementation to /// request more control over the function declaration. From the client side each runtime api -/// function is called with the `at` parameter that is a [`BlockId`](sp_api::BlockId). When -/// using the `advanced` attribute, the macro expects that the first parameter of the function -/// is this `at` parameter. Besides that the macro also doesn't do the automatic return value -/// rewrite, which means that full return value must be specified. The full return value is -/// constructed like [`Result`]`<, Error>` while `ReturnValue` being the return -/// value that is specified in the trait declaration. +/// function is called with the `at` parameter that is a [`Hash`](sp_runtime::traits::Hash). +/// When using the `advanced` attribute, the macro expects that the first parameter of the +/// function is this `at` parameter. Besides that the macro also doesn't do the automatic +/// return value rewrite, which means that full return value must be specified. The full return +/// value is constructed like [`Result`]`<, Error>` while `ReturnValue` being the +/// return value that is specified in the trait declaration. /// /// ## Example /// ```rust -/// # use sp_runtime::{traits::Block as BlockT, generic::BlockId}; +/// # use sp_runtime::traits::Block as BlockT; /// # use sp_test_primitives::Block; /// # use codec; /// # @@ -429,16 +429,14 @@ pub use sp_api_proc_macro::impl_runtime_apis; /// sp_api::mock_impl_runtime_apis! { /// impl Balance for MockApi { /// #[advanced] -/// fn get_balance(&self, at: &BlockId) -> Result { +/// fn get_balance(&self, at: ::Hash) -> Result { /// println!("Being called at: {}", at); /// /// Ok(self.balance.into()) /// } /// #[advanced] -/// fn set_balance(at: &BlockId, val: u64) -> Result<(), sp_api::ApiError> { -/// if let BlockId::Number(1) = at { -/// println!("Being called to set balance to: {}", val); -/// } +/// fn set_balance(at: ::Hash, val: u64) -> Result<(), sp_api::ApiError> { +/// println!("Being called at: {}", at); /// /// Ok(().into()) /// } @@ -539,14 +537,14 @@ pub trait ApiExt { Self: Sized; /// Checks if the given api is implemented and versions match. - fn has_api(&self, at: &BlockId) -> Result + fn has_api(&self, at_hash: Block::Hash) -> Result where Self: Sized; /// Check if the given api is implemented and the version passes a predicate. fn has_api_with bool>( &self, - at: &BlockId, + at_hash: Block::Hash, pred: P, ) -> Result where @@ -555,7 +553,7 @@ pub trait ApiExt { /// Returns the version of the given api. fn api_version( &self, - at: &BlockId, + at_hash: Block::Hash, ) -> Result, ApiError> where Self: Sized; @@ -590,7 +588,7 @@ pub trait ApiExt { #[cfg(feature = "std")] pub struct CallApiAtParams<'a, Block: BlockT, Backend: StateBackend>> { /// The block id that determines the state that should be setup when calling the function. - pub at: &'a BlockId, + pub at: Block::Hash, /// The name of the function that should be called. pub function: &'static str, /// The encoded arguments of the function. @@ -619,7 +617,7 @@ pub trait CallApiAt { ) -> Result, ApiError>; /// Returns the runtime version at the given block. - fn runtime_version_at(&self, at: &BlockId) -> Result; + fn runtime_version_at(&self, at_hash: Block::Hash) -> Result; /// Get the state `at` the given block. fn state_at(&self, at: Block::Hash) -> Result; diff --git a/primitives/api/test/benches/bench.rs b/primitives/api/test/benches/bench.rs index 2445a5c07..0d47d3038 100644 --- a/primitives/api/test/benches/bench.rs +++ b/primitives/api/test/benches/bench.rs @@ -17,7 +17,6 @@ use criterion::{criterion_group, criterion_main, Criterion}; use sp_api::ProvideRuntimeApi; -use sp_runtime::generic::BlockId; use sp_state_machine::ExecutionStrategy; use substrate_test_runtime_client::{ runtime::TestAPI, DefaultTestClientBuilderExt, TestClientBuilder, TestClientBuilderExt, @@ -27,49 +26,49 @@ fn sp_api_benchmark(c: &mut Criterion) { c.bench_function("add one with same runtime api", |b| { let client = substrate_test_runtime_client::new(); let runtime_api = client.runtime_api(); - let block_id = BlockId::Hash(client.chain_info().best_hash); + let best_hash = client.chain_info().best_hash; - b.iter(|| runtime_api.benchmark_add_one(&block_id, &1)) + b.iter(|| runtime_api.benchmark_add_one(best_hash, &1)) }); c.bench_function("add one with recreating runtime api", |b| { let client = substrate_test_runtime_client::new(); - let block_id = BlockId::Hash(client.chain_info().best_hash); + let best_hash = client.chain_info().best_hash; - b.iter(|| client.runtime_api().benchmark_add_one(&block_id, &1)) + b.iter(|| client.runtime_api().benchmark_add_one(best_hash, &1)) }); c.bench_function("vector add one with same runtime api", |b| { let client = substrate_test_runtime_client::new(); let runtime_api = client.runtime_api(); - let block_id = BlockId::Hash(client.chain_info().best_hash); + let best_hash = client.chain_info().best_hash; let data = vec![0; 1000]; - b.iter_with_large_drop(|| runtime_api.benchmark_vector_add_one(&block_id, &data)) + b.iter_with_large_drop(|| runtime_api.benchmark_vector_add_one(best_hash, &data)) }); c.bench_function("vector add one with recreating runtime api", |b| { let client = substrate_test_runtime_client::new(); - let block_id = BlockId::Hash(client.chain_info().best_hash); + let best_hash = client.chain_info().best_hash; let data = vec![0; 1000]; - b.iter_with_large_drop(|| client.runtime_api().benchmark_vector_add_one(&block_id, &data)) + b.iter_with_large_drop(|| client.runtime_api().benchmark_vector_add_one(best_hash, &data)) }); c.bench_function("calling function by function pointer in wasm", |b| { let client = TestClientBuilder::new() .set_execution_strategy(ExecutionStrategy::AlwaysWasm) .build(); - let block_id = BlockId::Hash(client.chain_info().best_hash); - b.iter(|| client.runtime_api().benchmark_indirect_call(&block_id).unwrap()) + let best_hash = client.chain_info().best_hash; + b.iter(|| client.runtime_api().benchmark_indirect_call(best_hash).unwrap()) }); c.bench_function("calling function in wasm", |b| { let client = TestClientBuilder::new() .set_execution_strategy(ExecutionStrategy::AlwaysWasm) .build(); - let block_id = BlockId::Hash(client.chain_info().best_hash); - b.iter(|| client.runtime_api().benchmark_direct_call(&block_id).unwrap()) + let best_hash = client.chain_info().best_hash; + b.iter(|| client.runtime_api().benchmark_direct_call(best_hash).unwrap()) }); } diff --git a/primitives/api/test/tests/decl_and_impl.rs b/primitives/api/test/tests/decl_and_impl.rs index 42628830c..47e33d81d 100644 --- a/primitives/api/test/tests/decl_and_impl.rs +++ b/primitives/api/test/tests/decl_and_impl.rs @@ -18,11 +18,9 @@ use sp_api::{ decl_runtime_apis, impl_runtime_apis, mock_impl_runtime_apis, ApiError, ApiExt, RuntimeApiInfo, }; -use sp_runtime::{ - generic::BlockId, - traits::{Block as BlockT, GetNodeBlockType}, -}; -use substrate_test_runtime_client::runtime::Block; +use sp_runtime::traits::{Block as BlockT, GetNodeBlockType}; + +use substrate_test_runtime_client::runtime::{Block, Hash}; /// The declaration of the `Runtime` type and the implementation of the `GetNodeBlockType` /// trait are done by the `construct_runtime!` macro in a real runtime. @@ -119,13 +117,13 @@ mock_impl_runtime_apis! { } #[advanced] - fn same_name(_: &BlockId) -> Result<(), ApiError> { + fn same_name(_: ::Hash) -> Result<(), ApiError> { Ok(().into()) } #[advanced] - fn wild_card(at: &BlockId, _: u32) -> Result<(), ApiError> { - if let BlockId::Number(1337) = at { + fn wild_card(at: ::Hash, _: u32) -> Result<(), ApiError> { + if Hash::repeat_byte(0x0f) == at { // yeah Ok(().into()) } else { @@ -150,19 +148,19 @@ type TestClient = substrate_test_runtime_client::client::Client< fn test_client_side_function_signature() { let _test: fn( &RuntimeApiImpl, - &BlockId, + ::Hash, u64, ) -> Result<(), ApiError> = RuntimeApiImpl::::test; let _something_with_block: fn( &RuntimeApiImpl, - &BlockId, + ::Hash, Block, ) -> Result = RuntimeApiImpl::::something_with_block; #[allow(deprecated)] let _same_name_before_version_2: fn( &RuntimeApiImpl, - &BlockId, + ::Hash, ) -> Result = RuntimeApiImpl::::same_name_before_version_2; } @@ -204,8 +202,8 @@ fn check_runtime_api_versions() { fn mock_runtime_api_has_api() { let mock = MockApi { block: None }; - assert!(mock.has_api::>(&BlockId::Number(0)).unwrap()); - assert!(mock.has_api::>(&BlockId::Number(0)).unwrap()); + assert!(mock.has_api::>(Hash::default()).unwrap()); + assert!(mock.has_api::>(Hash::default()).unwrap()); } #[test] @@ -214,17 +212,17 @@ fn mock_runtime_api_panics_on_calling_old_version() { let mock = MockApi { block: None }; #[allow(deprecated)] - let _ = mock.same_name_before_version_2(&BlockId::Number(0)); + let _ = mock.same_name_before_version_2(Hash::default()); } #[test] fn mock_runtime_api_works_with_advanced() { let mock = MockApi { block: None }; - Api::::same_name(&mock, &BlockId::Number(0)).unwrap(); - mock.wild_card(&BlockId::Number(1337), 1).unwrap(); + Api::::same_name(&mock, Hash::default()).unwrap(); + mock.wild_card(Hash::repeat_byte(0x0f), 1).unwrap(); assert_eq!( "Test error".to_string(), - mock.wild_card(&BlockId::Number(1336), 1).unwrap_err().to_string(), + mock.wild_card(Hash::repeat_byte(0x01), 1).unwrap_err().to_string(), ); } diff --git a/primitives/api/test/tests/runtime_calls.rs b/primitives/api/test/tests/runtime_calls.rs index 3d355c474..4f88d887c 100644 --- a/primitives/api/test/tests/runtime_calls.rs +++ b/primitives/api/test/tests/runtime_calls.rs @@ -36,9 +36,9 @@ use sp_consensus::SelectChain; fn calling_function_with_strat(strat: ExecutionStrategy) { let client = TestClientBuilder::new().set_execution_strategy(strat).build(); let runtime_api = client.runtime_api(); - let block_id = BlockId::Number(client.chain_info().best_number); + let best_hash = client.chain_info().best_hash; - assert_eq!(runtime_api.benchmark_add_one(&block_id, &1).unwrap(), 2); + assert_eq!(runtime_api.benchmark_add_one(best_hash, &1).unwrap(), 2); } #[test] @@ -57,9 +57,9 @@ fn calling_native_runtime_signature_changed_function() { .set_execution_strategy(ExecutionStrategy::NativeWhenPossible) .build(); let runtime_api = client.runtime_api(); - let block_id = BlockId::Number(client.chain_info().best_number); + let best_hash = client.chain_info().best_hash; - assert_eq!(runtime_api.function_signature_changed(&block_id).unwrap(), 1); + assert_eq!(runtime_api.function_signature_changed(best_hash).unwrap(), 1); } #[test] @@ -68,10 +68,10 @@ fn calling_wasm_runtime_signature_changed_old_function() { .set_execution_strategy(ExecutionStrategy::AlwaysWasm) .build(); let runtime_api = client.runtime_api(); - let block_id = BlockId::Number(client.chain_info().best_number); + let best_hash = client.chain_info().best_hash; #[allow(deprecated)] - let res = runtime_api.function_signature_changed_before_version_2(&block_id).unwrap(); + let res = runtime_api.function_signature_changed_before_version_2(best_hash).unwrap(); assert_eq!(&res, &[1, 2]); } @@ -79,16 +79,16 @@ fn calling_wasm_runtime_signature_changed_old_function() { fn calling_with_both_strategy_and_fail_on_wasm_should_return_error() { let client = TestClientBuilder::new().set_execution_strategy(ExecutionStrategy::Both).build(); let runtime_api = client.runtime_api(); - let block_id = BlockId::Number(client.chain_info().best_number); - assert!(runtime_api.fail_on_wasm(&block_id).is_err()); + let best_hash = client.chain_info().best_hash; + assert!(runtime_api.fail_on_wasm(best_hash).is_err()); } #[test] fn calling_with_both_strategy_and_fail_on_native_should_work() { let client = TestClientBuilder::new().set_execution_strategy(ExecutionStrategy::Both).build(); let runtime_api = client.runtime_api(); - let block_id = BlockId::Number(client.chain_info().best_number); - assert_eq!(runtime_api.fail_on_native(&block_id).unwrap(), 1); + let best_hash = client.chain_info().best_hash; + assert_eq!(runtime_api.fail_on_native(best_hash).unwrap(), 1); } #[test] @@ -97,8 +97,8 @@ fn calling_with_native_else_wasm_and_fail_on_wasm_should_work() { .set_execution_strategy(ExecutionStrategy::NativeElseWasm) .build(); let runtime_api = client.runtime_api(); - let block_id = BlockId::Number(client.chain_info().best_number); - assert_eq!(runtime_api.fail_on_wasm(&block_id).unwrap(), 1); + let best_hash = client.chain_info().best_hash; + assert_eq!(runtime_api.fail_on_wasm(best_hash).unwrap(), 1); } #[test] @@ -107,8 +107,8 @@ fn calling_with_native_else_wasm_and_fail_on_native_should_work() { .set_execution_strategy(ExecutionStrategy::NativeElseWasm) .build(); let runtime_api = client.runtime_api(); - let block_id = BlockId::Number(client.chain_info().best_number); - assert_eq!(runtime_api.fail_on_native(&block_id).unwrap(), 1); + let best_hash = client.chain_info().best_hash; + assert_eq!(runtime_api.fail_on_native(best_hash).unwrap(), 1); } #[test] @@ -117,18 +117,18 @@ fn use_trie_function() { .set_execution_strategy(ExecutionStrategy::AlwaysWasm) .build(); let runtime_api = client.runtime_api(); - let block_id = BlockId::Number(client.chain_info().best_number); - assert_eq!(runtime_api.use_trie(&block_id).unwrap(), 2); + let best_hash = client.chain_info().best_hash; + assert_eq!(runtime_api.use_trie(best_hash).unwrap(), 2); } #[test] fn initialize_block_works() { let client = TestClientBuilder::new().set_execution_strategy(ExecutionStrategy::Both).build(); let runtime_api = client.runtime_api(); - let block_id = BlockId::Number(client.chain_info().best_number); + let best_hash = client.chain_info().best_hash; runtime_api .initialize_block( - &block_id, + best_hash, &Header::new( 1, Default::default(), @@ -138,7 +138,7 @@ fn initialize_block_works() { ), ) .unwrap(); - assert_eq!(runtime_api.get_block_number(&block_id).unwrap(), 1); + assert_eq!(runtime_api.get_block_number(best_hash).unwrap(), 1); } #[test] @@ -205,10 +205,10 @@ fn call_runtime_api_with_multiple_arguments() { let client = TestClientBuilder::new().set_execution_strategy(ExecutionStrategy::Both).build(); let data = vec![1, 2, 4, 5, 6, 7, 8, 8, 10, 12]; - let block_id = BlockId::Number(client.chain_info().best_number); + let best_hash = client.chain_info().best_hash; client .runtime_api() - .test_multiple_arguments(&block_id, data.clone(), data.clone(), data.len() as u32) + .test_multiple_arguments(best_hash, data.clone(), data.clone(), data.len() as u32) .unwrap(); } @@ -225,8 +225,9 @@ fn disable_logging_works() { let client = builder.build(); let runtime_api = client.runtime_api(); - let block_id = BlockId::Number(0); - runtime_api.do_trace_log(&block_id).expect("Logging should not fail"); + runtime_api + .do_trace_log(client.chain_info().genesis_hash) + .expect("Logging should not fail"); log::error!("Logging from native works"); } else { let executable = std::env::current_exe().unwrap(); diff --git a/primitives/api/test/tests/ui/mock_advanced_block_id_by_value.rs b/primitives/api/test/tests/ui/mock_advanced_hash_by_reference.rs similarity index 81% rename from primitives/api/test/tests/ui/mock_advanced_block_id_by_value.rs rename to primitives/api/test/tests/ui/mock_advanced_hash_by_reference.rs index aeef40f4c..45e6adb2f 100644 --- a/primitives/api/test/tests/ui/mock_advanced_block_id_by_value.rs +++ b/primitives/api/test/tests/ui/mock_advanced_hash_by_reference.rs @@ -12,7 +12,7 @@ struct MockApi; sp_api::mock_impl_runtime_apis! { impl Api for MockApi { #[advanced] - fn test(&self, _: BlockId) -> Result<(), ApiError> { + fn test(&self, _: &Hash) -> Result<(), ApiError> { Ok(().into()) } } diff --git a/primitives/api/test/tests/ui/mock_advanced_block_id_by_value.stderr b/primitives/api/test/tests/ui/mock_advanced_hash_by_reference.stderr similarity index 60% rename from primitives/api/test/tests/ui/mock_advanced_block_id_by_value.stderr rename to primitives/api/test/tests/ui/mock_advanced_hash_by_reference.stderr index 3b3c4e94c..234331c97 100644 --- a/primitives/api/test/tests/ui/mock_advanced_block_id_by_value.stderr +++ b/primitives/api/test/tests/ui/mock_advanced_hash_by_reference.stderr @@ -1,10 +1,10 @@ -error: `BlockId` needs to be taken by reference and not by value! - --> tests/ui/mock_advanced_block_id_by_value.rs:12:1 +error: `Hash` needs to be taken by value and not by reference! + --> tests/ui/mock_advanced_hash_by_reference.rs:12:1 | 12 | / sp_api::mock_impl_runtime_apis! { 13 | | impl Api for MockApi { 14 | | #[advanced] -15 | | fn test(&self, _: BlockId) -> Result<(), ApiError> { +15 | | fn test(&self, _: &Hash) -> Result<(), ApiError> { ... | 18 | | } 19 | | } diff --git a/primitives/api/test/tests/ui/mock_advanced_missing_blockid.rs b/primitives/api/test/tests/ui/mock_advanced_missing_hash.rs similarity index 100% rename from primitives/api/test/tests/ui/mock_advanced_missing_blockid.rs rename to primitives/api/test/tests/ui/mock_advanced_missing_hash.rs diff --git a/primitives/api/test/tests/ui/mock_advanced_missing_blockid.stderr b/primitives/api/test/tests/ui/mock_advanced_missing_hash.stderr similarity index 56% rename from primitives/api/test/tests/ui/mock_advanced_missing_blockid.stderr rename to primitives/api/test/tests/ui/mock_advanced_missing_hash.stderr index b9ce7324b..48a94a00b 100644 --- a/primitives/api/test/tests/ui/mock_advanced_missing_blockid.stderr +++ b/primitives/api/test/tests/ui/mock_advanced_missing_hash.stderr @@ -1,5 +1,5 @@ -error: If using the `advanced` attribute, it is required that the function takes at least one argument, the `BlockId`. - --> tests/ui/mock_advanced_missing_blockid.rs:15:3 +error: If using the `advanced` attribute, it is required that the function takes at least one argument, the `Hash`. + --> tests/ui/mock_advanced_missing_hash.rs:15:3 | 15 | fn test(&self) -> Result<(), ApiError> { | ^^ diff --git a/primitives/application-crypto/test/src/ecdsa.rs b/primitives/application-crypto/test/src/ecdsa.rs index e45a3d5f8..4fe77db30 100644 --- a/primitives/application-crypto/test/src/ecdsa.rs +++ b/primitives/application-crypto/test/src/ecdsa.rs @@ -20,7 +20,6 @@ use sp_api::ProvideRuntimeApi; use sp_application_crypto::ecdsa::{AppPair, AppPublic}; use sp_core::{crypto::Pair, testing::ECDSA}; use sp_keystore::{testing::KeyStore, SyncCryptoStore}; -use sp_runtime::generic::BlockId; use std::sync::Arc; use substrate_test_runtime_client::{ runtime::TestAPI, DefaultTestClientBuilderExt, TestClientBuilder, TestClientBuilderExt, @@ -32,7 +31,7 @@ fn ecdsa_works_in_runtime() { let test_client = TestClientBuilder::new().set_keystore(keystore.clone()).build(); let (signature, public) = test_client .runtime_api() - .test_ecdsa_crypto(&BlockId::Number(0)) + .test_ecdsa_crypto(test_client.chain_info().genesis_hash) .expect("Tests `ecdsa` crypto."); let supported_keys = SyncCryptoStore::keys(&*keystore, ECDSA).unwrap(); diff --git a/primitives/application-crypto/test/src/ed25519.rs b/primitives/application-crypto/test/src/ed25519.rs index ef2df9fe9..7f37744ec 100644 --- a/primitives/application-crypto/test/src/ed25519.rs +++ b/primitives/application-crypto/test/src/ed25519.rs @@ -21,7 +21,6 @@ use sp_api::ProvideRuntimeApi; use sp_application_crypto::ed25519::{AppPair, AppPublic}; use sp_core::{crypto::Pair, testing::ED25519}; use sp_keystore::{testing::KeyStore, SyncCryptoStore}; -use sp_runtime::generic::BlockId; use std::sync::Arc; use substrate_test_runtime_client::{ runtime::TestAPI, DefaultTestClientBuilderExt, TestClientBuilder, TestClientBuilderExt, @@ -33,7 +32,7 @@ fn ed25519_works_in_runtime() { let test_client = TestClientBuilder::new().set_keystore(keystore.clone()).build(); let (signature, public) = test_client .runtime_api() - .test_ed25519_crypto(&BlockId::Number(0)) + .test_ed25519_crypto(test_client.chain_info().genesis_hash) .expect("Tests `ed25519` crypto."); let supported_keys = SyncCryptoStore::keys(&*keystore, ED25519).unwrap(); diff --git a/primitives/application-crypto/test/src/sr25519.rs b/primitives/application-crypto/test/src/sr25519.rs index e15ffe82a..aa6734bf6 100644 --- a/primitives/application-crypto/test/src/sr25519.rs +++ b/primitives/application-crypto/test/src/sr25519.rs @@ -21,7 +21,6 @@ use sp_api::ProvideRuntimeApi; use sp_application_crypto::sr25519::{AppPair, AppPublic}; use sp_core::{crypto::Pair, testing::SR25519}; use sp_keystore::{testing::KeyStore, SyncCryptoStore}; -use sp_runtime::generic::BlockId; use std::sync::Arc; use substrate_test_runtime_client::{ runtime::TestAPI, DefaultTestClientBuilderExt, TestClientBuilder, TestClientBuilderExt, @@ -33,7 +32,7 @@ fn sr25519_works_in_runtime() { let test_client = TestClientBuilder::new().set_keystore(keystore.clone()).build(); let (signature, public) = test_client .runtime_api() - .test_sr25519_crypto(&BlockId::Number(0)) + .test_sr25519_crypto(test_client.chain_info().genesis_hash) .expect("Tests `sr25519` crypto."); let supported_keys = SyncCryptoStore::keys(&*keystore, SR25519).unwrap(); diff --git a/primitives/beefy/src/mmr.rs b/primitives/beefy/src/mmr.rs index 549d2edbd..ca60f2a91 100644 --- a/primitives/beefy/src/mmr.rs +++ b/primitives/beefy/src/mmr.rs @@ -145,7 +145,6 @@ mod mmr_root_provider { use crate::{known_payloads, payload::PayloadProvider, Payload}; use sp_api::{NumberFor, ProvideRuntimeApi}; use sp_mmr_primitives::MmrApi; - use sp_runtime::generic::BlockId; use sp_std::{marker::PhantomData, sync::Arc}; /// A [`crate::Payload`] provider where payload is Merkle Mountain Range root hash. @@ -170,11 +169,7 @@ mod mmr_root_provider { /// Simple wrapper that gets MMR root from header digests or from client state. fn mmr_root_from_digest_or_runtime(&self, header: &B::Header) -> Option { find_mmr_root_digest::(header).or_else(|| { - self.runtime - .runtime_api() - .mmr_root(&BlockId::hash(header.hash())) - .ok() - .and_then(|r| r.ok()) + self.runtime.runtime_api().mmr_root(header.hash()).ok().and_then(|r| r.ok()) }) } } diff --git a/primitives/runtime/src/runtime_logger.rs b/primitives/runtime/src/runtime_logger.rs index 8b5a15762..fa866f834 100644 --- a/primitives/runtime/src/runtime_logger.rs +++ b/primitives/runtime/src/runtime_logger.rs @@ -65,7 +65,7 @@ impl log::Log for RuntimeLogger { #[cfg(test)] mod tests { - use sp_api::{BlockId, ProvideRuntimeApi}; + use sp_api::ProvideRuntimeApi; use std::{env, str::FromStr}; use substrate_test_runtime_client::{ runtime::TestAPI, DefaultTestClientBuilderExt, ExecutionStrategy, TestClientBuilder, @@ -82,8 +82,9 @@ mod tests { .set_execution_strategy(ExecutionStrategy::AlwaysWasm) .build(); let runtime_api = client.runtime_api(); - let block_id = BlockId::Number(0); - runtime_api.do_trace_log(&block_id).expect("Logging should not fail"); + runtime_api + .do_trace_log(client.chain_info().genesis_hash) + .expect("Logging should not fail"); } else { for (level, should_print) in &[("trace", true), ("info", false)] { let executable = std::env::current_exe().unwrap(); diff --git a/primitives/session/src/lib.rs b/primitives/session/src/lib.rs index 9b403409c..9688547db 100644 --- a/primitives/session/src/lib.rs +++ b/primitives/session/src/lib.rs @@ -24,7 +24,7 @@ use codec::{Decode, Encode}; #[cfg(feature = "std")] use sp_api::ProvideRuntimeApi; #[cfg(feature = "std")] -use sp_runtime::{generic::BlockId, traits::Block as BlockT}; +use sp_runtime::traits::Block as BlockT; use sp_core::{crypto::KeyTypeId, RuntimeDebug}; use sp_staking::SessionIndex; @@ -125,7 +125,7 @@ where let runtime_api = client.runtime_api(); for seed in seeds { - runtime_api.generate_session_keys(&BlockId::Hash(at), Some(seed.as_bytes().to_vec()))?; + runtime_api.generate_session_keys(at, Some(seed.as_bytes().to_vec()))?; } Ok(()) diff --git a/test-utils/runtime/src/lib.rs b/test-utils/runtime/src/lib.rs index 359061070..b979a961a 100644 --- a/test-utils/runtime/src/lib.rs +++ b/test-utils/runtime/src/lib.rs @@ -1353,7 +1353,6 @@ mod tests { use sp_api::ProvideRuntimeApi; use sp_consensus::BlockOrigin; use sp_core::storage::well_known_keys::HEAP_PAGES; - use sp_runtime::generic::BlockId; use sp_state_machine::ExecutionStrategy; use substrate_test_runtime_client::{ prelude::*, runtime::TestAPI, DefaultTestClientBuilderExt, TestClientBuilder, @@ -1368,27 +1367,27 @@ mod tests { .set_execution_strategy(ExecutionStrategy::AlwaysWasm) .set_heap_pages(8) .build(); - let block_id = BlockId::Hash(client.chain_info().best_hash); + let best_hash = client.chain_info().best_hash; // Try to allocate 1024k of memory on heap. This is going to fail since it is twice larger // than the heap. - let ret = client.runtime_api().vec_with_capacity(&block_id, 1048576); + let ret = client.runtime_api().vec_with_capacity(best_hash, 1048576); assert!(ret.is_err()); // Create a block that sets the `:heap_pages` to 32 pages of memory which corresponds to // ~2048k of heap memory. - let (new_block_id, block) = { + let (new_at_hash, block) = { let mut builder = client.new_block(Default::default()).unwrap(); builder.push_storage_change(HEAP_PAGES.to_vec(), Some(32u64.encode())).unwrap(); let block = builder.build().unwrap().block; let hash = block.header.hash(); - (BlockId::Hash(hash), block) + (hash, block) }; futures::executor::block_on(client.import(BlockOrigin::Own, block)).unwrap(); // Allocation of 1024k while having ~2048k should succeed. - let ret = client.runtime_api().vec_with_capacity(&new_block_id, 1048576); + let ret = client.runtime_api().vec_with_capacity(new_at_hash, 1048576); assert!(ret.is_ok()); } @@ -1397,9 +1396,9 @@ mod tests { let client = TestClientBuilder::new().set_execution_strategy(ExecutionStrategy::Both).build(); let runtime_api = client.runtime_api(); - let block_id = BlockId::Hash(client.chain_info().best_hash); + let best_hash = client.chain_info().best_hash; - runtime_api.test_storage(&block_id).unwrap(); + runtime_api.test_storage(best_hash).unwrap(); } fn witness_backend() -> (sp_trie::MemoryDB, crate::Hash) { @@ -1424,8 +1423,8 @@ mod tests { let client = TestClientBuilder::new().set_execution_strategy(ExecutionStrategy::Both).build(); let runtime_api = client.runtime_api(); - let block_id = BlockId::Hash(client.chain_info().best_hash); + let best_hash = client.chain_info().best_hash; - runtime_api.test_witness(&block_id, proof, root).unwrap(); + runtime_api.test_witness(best_hash, proof, root).unwrap(); } } diff --git a/utils/frame/benchmarking-cli/src/block/bench.rs b/utils/frame/benchmarking-cli/src/block/bench.rs index c69d95725..2ceac787f 100644 --- a/utils/frame/benchmarking-cli/src/block/bench.rs +++ b/utils/frame/benchmarking-cli/src/block/bench.rs @@ -92,13 +92,12 @@ where for i in self.params.from..=self.params.to { let block_num = BlockId::Number(i.into()); - let parent_num = BlockId::Number(((i - 1) as u32).into()); - let consumed = self.consumed_weight(&block_num)?; let hash = self.client.expect_block_hash_from_id(&block_num)?; + let consumed = self.consumed_weight(hash)?; let block = self.client.block(hash)?.ok_or(format!("Block {} not found", block_num))?; let block = self.unsealed(block.block); - let took = self.measure_block(&block, &parent_num)?; + let took = self.measure_block(&block, *block.header().parent_hash())?; self.log_weight(i, block.extrinsics().len(), consumed, took); } @@ -107,7 +106,7 @@ where } /// Return the average *execution* aka. *import* time of the block. - fn measure_block(&self, block: &Block, parent_num: &BlockId) -> Result { + fn measure_block(&self, block: &Block, parent_hash: Block::Hash) -> Result { let mut record = Vec::::default(); // Interesting part here: // Execute the block multiple times and collect stats about its execution time. @@ -117,7 +116,7 @@ where let start = Instant::now(); runtime_api - .execute_block(&parent_num, block) + .execute_block(parent_hash, block) .map_err(|e| Error::Client(RuntimeApiError(e)))?; record.push(start.elapsed().as_nanos() as NanoSeconds); @@ -131,7 +130,7 @@ where /// /// This is the post-dispatch corrected weight and is only available /// after executing the block. - fn consumed_weight(&self, block: &BlockId) -> Result { + fn consumed_weight(&self, block_hash: Block::Hash) -> Result { // Hard-coded key for System::BlockWeight. It could also be passed in as argument // for the benchmark, but I think this should work as well. let hash = array_bytes::hex2bytes( @@ -139,11 +138,10 @@ where )?; let key = StorageKey(hash); - let block_hash = self.client.expect_block_hash_from_id(block)?; let mut raw_weight = &self .client .storage(block_hash, &key)? - .ok_or(format!("Could not find System::BlockWeight for block: {}", block))? + .ok_or(format!("Could not find System::BlockWeight for block: {}", block_hash))? .0[..]; let weight = ConsumedWeight::decode_all(&mut raw_weight)?; diff --git a/utils/frame/benchmarking-cli/src/extrinsic/bench.rs b/utils/frame/benchmarking-cli/src/extrinsic/bench.rs index 2a86c10e7..948f64837 100644 --- a/utils/frame/benchmarking-cli/src/extrinsic/bench.rs +++ b/utils/frame/benchmarking-cli/src/extrinsic/bench.rs @@ -20,13 +20,13 @@ use sc_block_builder::{BlockBuilderApi, BlockBuilderProvider}; use sc_cli::{Error, Result}; use sc_client_api::Backend as ClientBackend; -use sp_api::{ApiExt, BlockId, Core, ProvideRuntimeApi}; +use sp_api::{ApiExt, Core, ProvideRuntimeApi}; use sp_blockchain::{ ApplyExtrinsicFailed::Validity, Error::{ApplyExtrinsicFailed, RuntimeApiError}, }; use sp_runtime::{ - traits::{Block as BlockT, Zero}, + traits::Block as BlockT, transaction_validity::{InvalidTransaction, TransactionValidityError}, Digest, DigestItem, OpaqueExtrinsic, }; @@ -73,7 +73,9 @@ impl Benchmark where Block: BlockT, BA: ClientBackend, - C: BlockBuilderProvider + ProvideRuntimeApi, + C: BlockBuilderProvider + + ProvideRuntimeApi + + sp_blockchain::HeaderBackend, C::Api: ApiExt + BlockBuilderApi, { /// Create a new [`Self`] from the arguments. @@ -167,13 +169,13 @@ where /// Measures the time that it take to execute a block or an extrinsic. fn measure_block(&self, block: &Block) -> Result { let mut record = BenchRecord::new(); - let genesis = BlockId::Number(Zero::zero()); + let genesis = self.client.info().genesis_hash; info!("Running {} warmups...", self.params.warmup); for _ in 0..self.params.warmup { self.client .runtime_api() - .execute_block(&genesis, block.clone()) + .execute_block(genesis, block.clone()) .map_err(|e| Error::Client(RuntimeApiError(e)))?; } @@ -186,7 +188,7 @@ where let start = Instant::now(); runtime_api - .execute_block(&genesis, block) + .execute_block(genesis, block) .map_err(|e| Error::Client(RuntimeApiError(e)))?; let elapsed = start.elapsed().as_nanos(); diff --git a/utils/frame/benchmarking-cli/src/extrinsic/cmd.rs b/utils/frame/benchmarking-cli/src/extrinsic/cmd.rs index b95cd6b5c..dc8aa8b7c 100644 --- a/utils/frame/benchmarking-cli/src/extrinsic/cmd.rs +++ b/utils/frame/benchmarking-cli/src/extrinsic/cmd.rs @@ -94,7 +94,9 @@ impl ExtrinsicCmd { where Block: BlockT, BA: ClientBackend, - C: BlockBuilderProvider + ProvideRuntimeApi, + C: BlockBuilderProvider + + ProvideRuntimeApi + + sp_blockchain::HeaderBackend, C::Api: ApiExt + BlockBuilderApi, { // Short circuit if --list was specified. diff --git a/utils/frame/benchmarking-cli/src/overhead/cmd.rs b/utils/frame/benchmarking-cli/src/overhead/cmd.rs index 74c1e7efc..bca63f7ca 100644 --- a/utils/frame/benchmarking-cli/src/overhead/cmd.rs +++ b/utils/frame/benchmarking-cli/src/overhead/cmd.rs @@ -108,7 +108,9 @@ impl OverheadCmd { where Block: BlockT, BA: ClientBackend, - C: BlockBuilderProvider + ProvideRuntimeApi, + C: BlockBuilderProvider + + ProvideRuntimeApi + + sp_blockchain::HeaderBackend, C::Api: ApiExt + BlockBuilderApi, { if ext_builder.pallet() != "system" || ext_builder.extrinsic() != "remark" { diff --git a/utils/frame/rpc/system/src/lib.rs b/utils/frame/rpc/system/src/lib.rs index 72ad99e43..965a7b44d 100644 --- a/utils/frame/rpc/system/src/lib.rs +++ b/utils/frame/rpc/system/src/lib.rs @@ -32,7 +32,7 @@ use sp_api::ApiExt; use sp_block_builder::BlockBuilder; use sp_blockchain::HeaderBackend; use sp_core::{hexdisplay::HexDisplay, Bytes}; -use sp_runtime::{generic::BlockId, legacy, traits}; +use sp_runtime::{legacy, traits}; pub use frame_system_rpc_runtime_api::AccountNonceApi; @@ -101,9 +101,8 @@ where async fn nonce(&self, account: AccountId) -> RpcResult { let api = self.client.runtime_api(); let best = self.client.info().best_hash; - let at = BlockId::hash(best); - let nonce = api.account_nonce(&at, account.clone()).map_err(|e| { + let nonce = api.account_nonce(best, account.clone()).map_err(|e| { CallError::Custom(ErrorObject::owned( Error::RuntimeError.into(), "Unable to query nonce.", @@ -120,9 +119,9 @@ where ) -> RpcResult { self.deny_unsafe.check_if_safe()?; let api = self.client.runtime_api(); - let at = BlockId::::hash(at.unwrap_or_else(|| + let best_hash = at.unwrap_or_else(|| // If the block hash is not supplied assume the best block. - self.client.info().best_hash)); + self.client.info().best_hash); let uxt: ::Extrinsic = Decode::decode(&mut &*extrinsic).map_err(|e| { @@ -134,7 +133,7 @@ where })?; let api_version = api - .api_version::>(&at) + .api_version::>(best_hash) .map_err(|e| { CallError::Custom(ErrorObject::owned( Error::RuntimeError.into(), @@ -146,13 +145,13 @@ where CallError::Custom(ErrorObject::owned( Error::RuntimeError.into(), "Unable to dry run extrinsic.", - Some(format!("Could not find `BlockBuilder` api for block `{:?}`.", at)), + Some(format!("Could not find `BlockBuilder` api for block `{:?}`.", best_hash)), )) })?; let result = if api_version < 6 { #[allow(deprecated)] - api.apply_extrinsic_before_version_6(&at, uxt) + api.apply_extrinsic_before_version_6(best_hash, uxt) .map(legacy::byte_sized_error::convert_to_latest) .map_err(|e| { CallError::Custom(ErrorObject::owned( @@ -162,7 +161,7 @@ where )) })? } else { - api.apply_extrinsic(&at, uxt).map_err(|e| { + api.apply_extrinsic(best_hash, uxt).map_err(|e| { CallError::Custom(ErrorObject::owned( Error::RuntimeError.into(), "Unable to dry run extrinsic.", @@ -220,6 +219,7 @@ mod tests { use jsonrpsee::{core::Error as JsonRpseeError, types::error::CallError}; use sc_transaction_pool::BasicPool; use sp_runtime::{ + generic::BlockId, transaction_validity::{InvalidTransaction, TransactionValidityError}, ApplyExtrinsicResult, }; From 49fa611204650467ebce59c912999e1b28953978 Mon Sep 17 00:00:00 2001 From: Adrian Catangiu Date: Tue, 21 Feb 2023 12:50:41 +0200 Subject: [PATCH 142/558] frame/beefy: prune entries in set id session mapping (#13411) Add limit for the number of entries in the `SetIdSession` mapping. For example, it can be set to the bonding duration (in sessions). Signed-off-by: acatangiu --- frame/beefy-mmr/src/mock.rs | 1 + frame/beefy/src/lib.rs | 24 +++++++++++++++++++++++- frame/beefy/src/mock.rs | 2 ++ frame/beefy/src/tests.rs | 33 +++++++++++++++++++++++++++++++++ 4 files changed, 59 insertions(+), 1 deletion(-) diff --git a/frame/beefy-mmr/src/mock.rs b/frame/beefy-mmr/src/mock.rs index de6fa1352..8f3bdd619 100644 --- a/frame/beefy-mmr/src/mock.rs +++ b/frame/beefy-mmr/src/mock.rs @@ -133,6 +133,7 @@ impl pallet_beefy::Config for Test { )>>::IdentificationTuple; type HandleEquivocation = (); type MaxAuthorities = ConstU32<100>; + type MaxSetIdSessionEntries = ConstU64<100>; type OnNewValidatorSet = BeefyMmr; type WeightInfo = (); } diff --git a/frame/beefy/src/lib.rs b/frame/beefy/src/lib.rs index d29440457..cccfdbb81 100644 --- a/frame/beefy/src/lib.rs +++ b/frame/beefy/src/lib.rs @@ -99,8 +99,18 @@ pub mod pallet { type HandleEquivocation: HandleEquivocation; /// The maximum number of authorities that can be added. + #[pallet::constant] type MaxAuthorities: Get; + /// The maximum number of entries to keep in the set id to session index mapping. + /// + /// Since the `SetIdSession` map is only used for validating equivocations this + /// value should relate to the bonding duration of whatever staking system is + /// being used (if any). If equivocation handling is not enabled then this value + /// can be zero. + #[pallet::constant] + type MaxSetIdSessionEntries: Get; + /// A hook to act on the new BEEFY validator set. /// /// For some applications it might be beneficial to make the BEEFY validator set available @@ -136,6 +146,12 @@ pub mod pallet { /// A mapping from BEEFY set ID to the index of the *most recent* session for which its /// members were responsible. /// + /// This is only used for validating equivocation proofs. An equivocation proof must + /// contains a key-ownership proof for a given session, therefore we need a way to tie + /// together sessions and BEEFY set ids, i.e. we need to validate that a validator + /// was the owner of a given key on a given session, and what the active set ID was + /// during that session. + /// /// TWOX-NOTE: `ValidatorSetId` is not under user control. #[pallet::storage] #[pallet::getter(fn session_for_set)] @@ -462,9 +478,15 @@ where // We want to have at least one BEEFY mandatory block per session. Self::change_authorities(bounded_next_authorities, bounded_next_queued_authorities); + let validator_set_id = Self::validator_set_id(); // Update the mapping for the new set id that corresponds to the latest session (i.e. now). let session_index = >::current_index(); - SetIdSession::::insert(Self::validator_set_id(), &session_index); + SetIdSession::::insert(validator_set_id, &session_index); + // Prune old entry if limit reached. + let max_set_id_session_entries = T::MaxSetIdSessionEntries::get().max(1); + if validator_set_id >= max_set_id_session_entries { + SetIdSession::::remove(validator_set_id - max_set_id_session_entries); + } } fn on_disabled(i: u32) { diff --git a/frame/beefy/src/mock.rs b/frame/beefy/src/mock.rs index 8d8b83195..c6cde5332 100644 --- a/frame/beefy/src/mock.rs +++ b/frame/beefy/src/mock.rs @@ -111,6 +111,7 @@ parameter_types! { pub const Period: u64 = 1; pub const ReportLongevity: u64 = BondingDuration::get() as u64 * SessionsPerEra::get() as u64 * Period::get(); + pub const MaxSetIdSessionEntries: u32 = BondingDuration::get() * SessionsPerEra::get(); } impl pallet_beefy::Config for Test { @@ -125,6 +126,7 @@ impl pallet_beefy::Config for Test { type HandleEquivocation = super::EquivocationHandler; type MaxAuthorities = ConstU32<100>; + type MaxSetIdSessionEntries = MaxSetIdSessionEntries; type OnNewValidatorSet = (); type WeightInfo = (); } diff --git a/frame/beefy/src/tests.rs b/frame/beefy/src/tests.rs index a92f90b61..b2e1b6769 100644 --- a/frame/beefy/src/tests.rs +++ b/frame/beefy/src/tests.rs @@ -162,6 +162,39 @@ fn validator_set_updates_work() { }); } +#[test] +fn cleans_up_old_set_id_session_mappings() { + new_test_ext(vec![1, 2, 3, 4]).execute_with(|| { + let max_set_id_session_entries = MaxSetIdSessionEntries::get(); + + // we have 3 sessions per era + let era_limit = max_set_id_session_entries / 3; + // sanity check against division precision loss + assert_eq!(0, max_set_id_session_entries % 3); + // go through `max_set_id_session_entries` sessions + start_era(era_limit); + + // we should have a session id mapping for all the set ids from + // `max_set_id_session_entries` eras we have observed + for i in 1..=max_set_id_session_entries { + assert!(Beefy::session_for_set(i as u64).is_some()); + } + + // go through another `max_set_id_session_entries` sessions + start_era(era_limit * 2); + + // we should keep tracking the new mappings for new sessions + for i in max_set_id_session_entries + 1..=max_set_id_session_entries * 2 { + assert!(Beefy::session_for_set(i as u64).is_some()); + } + + // but the old ones should have been pruned by now + for i in 1..=max_set_id_session_entries { + assert!(Beefy::session_for_set(i as u64).is_none()); + } + }); +} + /// Returns a list with 3 authorities with known keys: /// Alice, Bob and Charlie. pub fn test_authorities() -> Vec { From 425bf6b440242fa39fd7d64e9987418fba8f29f1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gon=C3=A7alo=20Pestana?= Date: Tue, 21 Feb 2023 11:17:01 +0000 Subject: [PATCH 143/558] Staking and nomination pools runtime API improvements (#13119) * Adds StakingAPI_nominations_quota and NominationPoolsApi_balanceToPoint and NominationPoolsApi_pointsToBalance runtime APIs * Adds balance param to api_nominations_quota * Update frame/nomination-pools/src/lib.rs Co-authored-by: Anton * Update frame/nomination-pools/src/lib.rs Co-authored-by: Anton * Addresses comments - returns zero instead of error in runtime api * Update frame/staking/runtime-api/src/lib.rs Co-authored-by: Kian Paimani <5588131+kianenigma@users.noreply.github.com> * Update frame/staking/runtime-api/src/lib.rs Co-authored-by: Kian Paimani <5588131+kianenigma@users.noreply.github.com> * Addresses PR comments * Update frame/nomination-pools/runtime-api/Cargo.toml Co-authored-by: Kian Paimani <5588131+kianenigma@users.noreply.github.com> * Fixes points_to_balance logic; adds tests * test comment fix * Update frame/nomination-pools/runtime-api/src/lib.rs Co-authored-by: Kian Paimani <5588131+kianenigma@users.noreply.github.com> * Update frame/nomination-pools/runtime-api/src/lib.rs Co-authored-by: Kian Paimani <5588131+kianenigma@users.noreply.github.com> * Fix block pruning (#13323) * Referendum proposal's metadata (#12568) * referenda metadata * todo comment * remove TODO, update rustdocs * referenda clear_metadata origin signed or root * referenda metadata unit tests * drop schema type for referenda metadata * remove metadata type * referenda metadata benches * note different preimages * metadata for democracy pallet * metadata democracy pallet tests and benches * fix cargo clippy * update docs * ".git/.scripts/bench-bot.sh" pallet dev pallet_democracy * ".git/.scripts/bench-bot.sh" pallet dev pallet_referenda * Update the doc frame/democracy/src/lib.rs Co-authored-by: Roman Useinov * Update the doc frame/democracy/src/lib.rs Co-authored-by: Anthony Alaribe * reference instead clone for take Co-authored-by: Anthony Alaribe * error rename BadMetadata to PreimageNotExist * clear metadata within internal_cancel_referendum fn * remove redundant clone * collapse metadata api into one set_metadata method * fmt * review fixes * not request preimage on set_metadata * rename events and update docs * ".git/.scripts/commands/bench/bench.sh" pallet dev pallet_democracy * ".git/.scripts/commands/bench/bench.sh" pallet dev pallet_referenda * rename reset_metadata to transfer_metadata --------- Co-authored-by: command-bot <> Co-authored-by: Roman Useinov Co-authored-by: Anthony Alaribe * Improve test coverage of the `Notifications` protocol (#13033) * Add handler and upgrade tests * Add tests for `behaviour.rs` * Apply review comments * Update dependencies * Apply suggestions from code review Co-authored-by: Dmitry Markin * Apply review comments * Fix clippy * Update mockall * Apply review comment --------- Co-authored-by: Dmitry Markin * refactors runtime API logic to own pallet impl block * removes unrelated changes * Fixes cargo doc comments lint * fixes node cargo * fixes comment * restart ci * restart ci * restart ci --------- Co-authored-by: Anton Co-authored-by: Kian Paimani <5588131+kianenigma@users.noreply.github.com> Co-authored-by: parity-processbot <> Co-authored-by: Arkadiy Paronyan Co-authored-by: Muharem Ismailov Co-authored-by: Roman Useinov Co-authored-by: Anthony Alaribe Co-authored-by: Aaro Altonen <48052676+altonen@users.noreply.github.com> Co-authored-by: Dmitry Markin --- Cargo.lock | 10 +++ Cargo.toml | 1 + bin/node/runtime/Cargo.toml | 2 + bin/node/runtime/src/lib.rs | 18 +++- frame/nomination-pools/runtime-api/Cargo.toml | 2 + frame/nomination-pools/runtime-api/src/lib.rs | 13 ++- frame/nomination-pools/src/lib.rs | 62 +++++++++---- frame/nomination-pools/src/tests.rs | 86 ++++++++++++++----- frame/staking/runtime-api/Cargo.toml | 24 ++++++ frame/staking/runtime-api/README.md | 3 + frame/staking/runtime-api/src/lib.rs | 32 +++++++ frame/staking/src/pallet/impls.rs | 14 +++ 12 files changed, 222 insertions(+), 45 deletions(-) create mode 100644 frame/staking/runtime-api/Cargo.toml create mode 100644 frame/staking/runtime-api/README.md create mode 100644 frame/staking/runtime-api/src/lib.rs diff --git a/Cargo.lock b/Cargo.lock index d7f48c17f..bed09f135 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3568,6 +3568,7 @@ dependencies = [ "pallet-society", "pallet-staking", "pallet-staking-reward-curve", + "pallet-staking-runtime-api", "pallet-state-trie-migration", "pallet-sudo", "pallet-timestamp", @@ -6129,6 +6130,7 @@ dependencies = [ name = "pallet-nomination-pools-runtime-api" version = "1.0.0-dev" dependencies = [ + "pallet-nomination-pools", "parity-scale-codec", "sp-api", "sp-std", @@ -6486,6 +6488,14 @@ dependencies = [ "sp-arithmetic", ] +[[package]] +name = "pallet-staking-runtime-api" +version = "4.0.0-dev" +dependencies = [ + "parity-scale-codec", + "sp-api", +] + [[package]] name = "pallet-state-trie-migration" version = "4.0.0-dev" diff --git a/Cargo.toml b/Cargo.toml index aaa1c2a21..a86ec3146 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -141,6 +141,7 @@ members = [ "frame/staking", "frame/staking/reward-curve", "frame/staking/reward-fn", + "frame/staking/runtime-api", "frame/state-trie-migration", "frame/sudo", "frame/root-offences", diff --git a/bin/node/runtime/Cargo.toml b/bin/node/runtime/Cargo.toml index 1d2e6f057..e27e4f7f8 100644 --- a/bin/node/runtime/Cargo.toml +++ b/bin/node/runtime/Cargo.toml @@ -97,6 +97,7 @@ pallet-session = { version = "4.0.0-dev", features = [ "historical" ], path = ". pallet-session-benchmarking = { version = "4.0.0-dev", path = "../../../frame/session/benchmarking", default-features = false, optional = true } pallet-staking = { version = "4.0.0-dev", default-features = false, path = "../../../frame/staking" } pallet-staking-reward-curve = { version = "4.0.0-dev", default-features = false, path = "../../../frame/staking/reward-curve" } +pallet-staking-runtime-api = { version = "4.0.0-dev", default-features = false, path = "../../../frame/staking/runtime-api" } pallet-state-trie-migration = { version = "4.0.0-dev", default-features = false, path = "../../../frame/state-trie-migration" } pallet-scheduler = { version = "4.0.0-dev", default-features = false, path = "../../../frame/scheduler" } pallet-society = { version = "4.0.0-dev", default-features = false, path = "../../../frame/society" } @@ -175,6 +176,7 @@ std = [ "sp-runtime/std", "sp-staking/std", "pallet-staking/std", + "pallet-staking-runtime-api/std", "pallet-state-trie-migration/std", "sp-session/std", "pallet-sudo/std", diff --git a/bin/node/runtime/src/lib.rs b/bin/node/runtime/src/lib.rs index 2c7969ebc..0b887ff7d 100644 --- a/bin/node/runtime/src/lib.rs +++ b/bin/node/runtime/src/lib.rs @@ -1986,8 +1986,22 @@ impl_runtime_apis! { } impl pallet_nomination_pools_runtime_api::NominationPoolsApi for Runtime { - fn pending_rewards(member_account: AccountId) -> Balance { - NominationPools::pending_rewards(member_account).unwrap_or_default() + fn pending_rewards(who: AccountId) -> Balance { + NominationPools::api_pending_rewards(who).unwrap_or_default() + } + + fn points_to_balance(pool_id: pallet_nomination_pools::PoolId, points: Balance) -> Balance { + NominationPools::api_points_to_balance(pool_id, points) + } + + fn balance_to_points(pool_id: pallet_nomination_pools::PoolId, new_funds: Balance) -> Balance { + NominationPools::api_balance_to_points(pool_id, new_funds) + } + } + + impl pallet_staking_runtime_api::StakingApi for Runtime { + fn nominations_quota(balance: Balance) -> u32 { + Staking::api_nominations_quota(balance) } } diff --git a/frame/nomination-pools/runtime-api/Cargo.toml b/frame/nomination-pools/runtime-api/Cargo.toml index d1e4fbb30..5e290232a 100644 --- a/frame/nomination-pools/runtime-api/Cargo.toml +++ b/frame/nomination-pools/runtime-api/Cargo.toml @@ -16,6 +16,7 @@ targets = ["x86_64-unknown-linux-gnu"] codec = { package = "parity-scale-codec", version = "3.2.2", default-features = false, features = ["derive"] } sp-api = { version = "4.0.0-dev", default-features = false, path = "../../../primitives/api" } sp-std = { version = "5.0.0", default-features = false, path = "../../../primitives/std" } +pallet-nomination-pools = { version = "1.0.0", default-features = false, path = "../" } [features] default = ["std"] @@ -23,4 +24,5 @@ std = [ "codec/std", "sp-api/std", "sp-std/std", + "pallet-nomination-pools/std", ] diff --git a/frame/nomination-pools/runtime-api/src/lib.rs b/frame/nomination-pools/runtime-api/src/lib.rs index aa3ca57ca..94573bfdb 100644 --- a/frame/nomination-pools/runtime-api/src/lib.rs +++ b/frame/nomination-pools/runtime-api/src/lib.rs @@ -21,13 +21,22 @@ #![cfg_attr(not(feature = "std"), no_std)] use codec::Codec; +use pallet_nomination_pools::PoolId; sp_api::decl_runtime_apis! { /// Runtime api for accessing information about nomination pools. pub trait NominationPoolsApi - where AccountId: Codec, Balance: Codec + where + AccountId: Codec, + Balance: Codec, { /// Returns the pending rewards for the member that the AccountId was given for. - fn pending_rewards(member: AccountId) -> Balance; + fn pending_rewards(who: AccountId) -> Balance; + + /// Returns the equivalent balance of `points` for a given pool. + fn points_to_balance(pool_id: PoolId, points: Balance) -> Balance; + + /// Returns the equivalent points of `new_funds` for a given pool. + fn balance_to_points(pool_id: PoolId, new_funds: Balance) -> Balance; } } diff --git a/frame/nomination-pools/src/lib.rs b/frame/nomination-pools/src/lib.rs index 3cb8abedd..9c52cf71c 100644 --- a/frame/nomination-pools/src/lib.rs +++ b/frame/nomination-pools/src/lib.rs @@ -2142,24 +2142,6 @@ pub mod pallet { } impl Pallet { - /// Returns the pending rewards for the specified `member_account`. - /// - /// In the case of error, `None` is returned. - pub fn pending_rewards(member_account: T::AccountId) -> Option> { - if let Some(pool_member) = PoolMembers::::get(member_account) { - if let Some((reward_pool, bonded_pool)) = RewardPools::::get(pool_member.pool_id) - .zip(BondedPools::::get(pool_member.pool_id)) - { - let current_reward_counter = reward_pool - .current_reward_counter(pool_member.pool_id, bonded_pool.points) - .ok()?; - return pool_member.pending_rewards(current_reward_counter).ok() - } - } - - None - } - /// The amount of bond that MUST REMAIN IN BONDED in ALL POOLS. /// /// It is the responsibility of the depositor to put these funds into the pool initially. Upon @@ -2579,6 +2561,50 @@ impl Pallet { } } +impl Pallet { + /// Returns the pending rewards for the specified `who` account. + /// + /// In the case of error, `None` is returned. Used by runtime API. + pub fn api_pending_rewards(who: T::AccountId) -> Option> { + if let Some(pool_member) = PoolMembers::::get(who) { + if let Some((reward_pool, bonded_pool)) = RewardPools::::get(pool_member.pool_id) + .zip(BondedPools::::get(pool_member.pool_id)) + { + let current_reward_counter = reward_pool + .current_reward_counter(pool_member.pool_id, bonded_pool.points) + .ok()?; + return pool_member.pending_rewards(current_reward_counter).ok() + } + } + + None + } + + /// Returns the points to balance conversion for a specified pool. + /// + /// If the pool ID does not exist, it returns 0 ratio points to balance. Used by runtime API. + pub fn api_points_to_balance(pool_id: PoolId, points: BalanceOf) -> BalanceOf { + if let Some(pool) = BondedPool::::get(pool_id) { + pool.points_to_balance(points) + } else { + Zero::zero() + } + } + + /// Returns the equivalent `new_funds` balance to point conversion for a specified pool. + /// + /// If the pool ID does not exist, returns 0 ratio balance to points. Used by runtime API. + pub fn api_balance_to_points(pool_id: PoolId, new_funds: BalanceOf) -> BalanceOf { + if let Some(pool) = BondedPool::::get(pool_id) { + let bonded_balance = + T::Staking::active_stake(&pool.bonded_account()).unwrap_or(Zero::zero()); + Pallet::::balance_to_point(bonded_balance, pool.points, new_funds) + } else { + Zero::zero() + } + } +} + impl OnStakerSlash> for Pallet { fn on_slash( pool_account: &T::AccountId, diff --git a/frame/nomination-pools/src/tests.rs b/frame/nomination-pools/src/tests.rs index 7d5d418bb..be52996a5 100644 --- a/frame/nomination-pools/src/tests.rs +++ b/frame/nomination-pools/src/tests.rs @@ -195,6 +195,46 @@ mod bonded_pool { }) } + #[test] + fn api_points_to_balance_works() { + ExtBuilder::default().build_and_execute(|| { + assert!(BondedPool::::get(1).is_some()); + assert_eq!(Pallet::::api_points_to_balance(1, 10), 10); + + // slash half of the pool's balance. expected result of `fn api_points_to_balance` + // to be 1/2 of the pool's balance. + StakingMock::set_bonded_balance( + default_bonded_account(), + Pools::depositor_min_bond() / 2, + ); + assert_eq!(Pallet::::api_points_to_balance(1, 10), 5); + + // if pool does not exist, points to balance ratio is 0. + assert_eq!(BondedPool::::get(2), None); + assert_eq!(Pallet::::api_points_to_balance(2, 10), 0); + }) + } + + #[test] + fn api_balance_to_points_works() { + ExtBuilder::default().build_and_execute(|| { + assert_eq!(Pallet::::api_balance_to_points(1, 0), 0); + assert_eq!(Pallet::::api_balance_to_points(1, 10), 10); + + // slash half of the pool's balance. expect result of `fn api_balance_to_points` + // to be 2 * of the balance to add to the pool. + StakingMock::set_bonded_balance( + default_bonded_account(), + Pools::depositor_min_bond() / 2, + ); + assert_eq!(Pallet::::api_balance_to_points(1, 10), 20); + + // if pool does not exist, balance to points ratio is 0. + assert_eq!(BondedPool::::get(2), None); + assert_eq!(Pallet::::api_points_to_balance(2, 10), 0); + }) + } + #[test] fn ok_to_join_with_works() { ExtBuilder::default().build_and_execute(|| { @@ -1305,51 +1345,51 @@ mod claim_payout { ExtBuilder::default().build_and_execute(|| { let ed = Balances::minimum_balance(); - assert_eq!(Pools::pending_rewards(10), Some(0)); + assert_eq!(Pools::api_pending_rewards(10), Some(0)); Balances::mutate_account(&default_reward_account(), |f| f.free += 30).unwrap(); - assert_eq!(Pools::pending_rewards(10), Some(30)); - assert_eq!(Pools::pending_rewards(20), None); + assert_eq!(Pools::api_pending_rewards(10), Some(30)); + assert_eq!(Pools::api_pending_rewards(20), None); Balances::make_free_balance_be(&20, ed + 10); assert_ok!(Pools::join(RuntimeOrigin::signed(20), 10, 1)); - assert_eq!(Pools::pending_rewards(10), Some(30)); - assert_eq!(Pools::pending_rewards(20), Some(0)); + assert_eq!(Pools::api_pending_rewards(10), Some(30)); + assert_eq!(Pools::api_pending_rewards(20), Some(0)); Balances::mutate_account(&default_reward_account(), |f| f.free += 100).unwrap(); - assert_eq!(Pools::pending_rewards(10), Some(30 + 50)); - assert_eq!(Pools::pending_rewards(20), Some(50)); - assert_eq!(Pools::pending_rewards(30), None); + assert_eq!(Pools::api_pending_rewards(10), Some(30 + 50)); + assert_eq!(Pools::api_pending_rewards(20), Some(50)); + assert_eq!(Pools::api_pending_rewards(30), None); Balances::make_free_balance_be(&30, ed + 10); assert_ok!(Pools::join(RuntimeOrigin::signed(30), 10, 1)); - assert_eq!(Pools::pending_rewards(10), Some(30 + 50)); - assert_eq!(Pools::pending_rewards(20), Some(50)); - assert_eq!(Pools::pending_rewards(30), Some(0)); + assert_eq!(Pools::api_pending_rewards(10), Some(30 + 50)); + assert_eq!(Pools::api_pending_rewards(20), Some(50)); + assert_eq!(Pools::api_pending_rewards(30), Some(0)); Balances::mutate_account(&default_reward_account(), |f| f.free += 60).unwrap(); - assert_eq!(Pools::pending_rewards(10), Some(30 + 50 + 20)); - assert_eq!(Pools::pending_rewards(20), Some(50 + 20)); - assert_eq!(Pools::pending_rewards(30), Some(20)); + assert_eq!(Pools::api_pending_rewards(10), Some(30 + 50 + 20)); + assert_eq!(Pools::api_pending_rewards(20), Some(50 + 20)); + assert_eq!(Pools::api_pending_rewards(30), Some(20)); // 10 should claim 10, 20 should claim nothing. assert_ok!(Pools::claim_payout(RuntimeOrigin::signed(10))); - assert_eq!(Pools::pending_rewards(10), Some(0)); - assert_eq!(Pools::pending_rewards(20), Some(50 + 20)); - assert_eq!(Pools::pending_rewards(30), Some(20)); + assert_eq!(Pools::api_pending_rewards(10), Some(0)); + assert_eq!(Pools::api_pending_rewards(20), Some(50 + 20)); + assert_eq!(Pools::api_pending_rewards(30), Some(20)); assert_ok!(Pools::claim_payout(RuntimeOrigin::signed(20))); - assert_eq!(Pools::pending_rewards(10), Some(0)); - assert_eq!(Pools::pending_rewards(20), Some(0)); - assert_eq!(Pools::pending_rewards(30), Some(20)); + assert_eq!(Pools::api_pending_rewards(10), Some(0)); + assert_eq!(Pools::api_pending_rewards(20), Some(0)); + assert_eq!(Pools::api_pending_rewards(30), Some(20)); assert_ok!(Pools::claim_payout(RuntimeOrigin::signed(30))); - assert_eq!(Pools::pending_rewards(10), Some(0)); - assert_eq!(Pools::pending_rewards(20), Some(0)); - assert_eq!(Pools::pending_rewards(30), Some(0)); + assert_eq!(Pools::api_pending_rewards(10), Some(0)); + assert_eq!(Pools::api_pending_rewards(20), Some(0)); + assert_eq!(Pools::api_pending_rewards(30), Some(0)); }); } diff --git a/frame/staking/runtime-api/Cargo.toml b/frame/staking/runtime-api/Cargo.toml new file mode 100644 index 000000000..9923b881c --- /dev/null +++ b/frame/staking/runtime-api/Cargo.toml @@ -0,0 +1,24 @@ +[package] +name = "pallet-staking-runtime-api" +version = "4.0.0-dev" +authors = ["Parity Technologies "] +edition = "2021" +license = "Apache-2.0" +homepage = "https://substrate.io" +repository = "https://github.com/paritytech/substrate/" +description = "RPC runtime API for transaction payment FRAME pallet" +readme = "README.md" + +[package.metadata.docs.rs] +targets = ["x86_64-unknown-linux-gnu"] + +[dependencies] +codec = { package = "parity-scale-codec", version = "3.0.0", default-features = false, features = ["derive"] } +sp-api = { version = "4.0.0-dev", default-features = false, path = "../../../primitives/api" } + +[features] +default = ["std"] +std = [ + "codec/std", + "sp-api/std", +] diff --git a/frame/staking/runtime-api/README.md b/frame/staking/runtime-api/README.md new file mode 100644 index 000000000..a999e519f --- /dev/null +++ b/frame/staking/runtime-api/README.md @@ -0,0 +1,3 @@ +Runtime API definition for the staking pallet. + +License: Apache-2.0 diff --git a/frame/staking/runtime-api/src/lib.rs b/frame/staking/runtime-api/src/lib.rs new file mode 100644 index 000000000..378599c66 --- /dev/null +++ b/frame/staking/runtime-api/src/lib.rs @@ -0,0 +1,32 @@ +// This file is part of Substrate. + +// Copyright (C) 2023 Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +//! Runtime API definition for the staking pallet. + +#![cfg_attr(not(feature = "std"), no_std)] + +use codec::Codec; + +sp_api::decl_runtime_apis! { + pub trait StakingApi + where + Balance: Codec, + { + /// Returns the nominations quota for a nominator with a given balance. + fn nominations_quota(balance: Balance) -> u32; + } +} diff --git a/frame/staking/src/pallet/impls.rs b/frame/staking/src/pallet/impls.rs index 6a35e2b86..fc0bf082c 100644 --- a/frame/staking/src/pallet/impls.rs +++ b/frame/staking/src/pallet/impls.rs @@ -972,6 +972,20 @@ impl Pallet { } } +impl Pallet { + /// Returns the current nominations quota for nominators. + /// + /// Used by the runtime API. + /// Note: for now, this api runtime will always return value of `T::MaxNominations` and thus it + /// is redundant. However, with the upcoming changes in + /// , the nominations quota will change + /// depending on the nominators balance. We're introducing this runtime API now to prepare the + /// community to use it before rolling out PR#12970. + pub fn api_nominations_quota(_balance: BalanceOf) -> u32 { + T::MaxNominations::get() + } +} + impl ElectionDataProvider for Pallet { type AccountId = T::AccountId; type BlockNumber = BlockNumberFor; From 088a7fc5e66cc08b1a5ac2fbe53e19baf7349489 Mon Sep 17 00:00:00 2001 From: Ross Bulat Date: Tue, 21 Feb 2023 19:39:52 +0800 Subject: [PATCH 144/558] Nomination Pools: Rename state-toggler to bouncer (#13421) * rename state-toggler to bouncer * add migration * fmt * bump storage version * rm migration * revert version * ".git/.scripts/commands/fmt/fmt.sh" --------- Co-authored-by: command-bot <> --- .../nomination-pools/benchmarking/src/lib.rs | 6 +- frame/nomination-pools/fuzzer/src/call.rs | 4 +- frame/nomination-pools/src/lib.rs | 63 ++++++++-------- frame/nomination-pools/src/migration.rs | 4 +- frame/nomination-pools/src/tests.rs | 73 ++++++------------- 5 files changed, 60 insertions(+), 90 deletions(-) diff --git a/frame/nomination-pools/benchmarking/src/lib.rs b/frame/nomination-pools/benchmarking/src/lib.rs index 32708ff53..675a4a832 100644 --- a/frame/nomination-pools/benchmarking/src/lib.rs +++ b/frame/nomination-pools/benchmarking/src/lib.rs @@ -518,7 +518,7 @@ frame_benchmarking::benchmarks! { depositor: depositor.clone(), root: Some(depositor.clone()), nominator: Some(depositor.clone()), - state_toggler: Some(depositor.clone()), + bouncer: Some(depositor.clone()), }, } ); @@ -557,7 +557,7 @@ frame_benchmarking::benchmarks! { depositor: depositor.clone(), root: Some(depositor.clone()), nominator: Some(depositor.clone()), - state_toggler: Some(depositor.clone()), + bouncer: Some(depositor.clone()), } } ); @@ -630,7 +630,7 @@ frame_benchmarking::benchmarks! { pallet_nomination_pools::PoolRoles { depositor: root, nominator: Some(random.clone()), - state_toggler: Some(random.clone()), + bouncer: Some(random.clone()), root: Some(random), }, ) diff --git a/frame/nomination-pools/fuzzer/src/call.rs b/frame/nomination-pools/fuzzer/src/call.rs index b07903609..805cc265d 100644 --- a/frame/nomination-pools/fuzzer/src/call.rs +++ b/frame/nomination-pools/fuzzer/src/call.rs @@ -143,9 +143,9 @@ fn random_call(mut rng: &mut R) -> (pools::Call, RuntimeOrigin) { let amount = random_ed_multiple(&mut rng); fund_account(&mut rng, &who); let root = who; - let state_toggler = who; + let bouncer = who; let nominator = who; - (PoolsCall::::create { amount, root, state_toggler, nominator }, origin) + (PoolsCall::::create { amount, root, bouncer, nominator }, origin) }, 7 => { // nominate diff --git a/frame/nomination-pools/src/lib.rs b/frame/nomination-pools/src/lib.rs index 9c52cf71c..4a8016846 100644 --- a/frame/nomination-pools/src/lib.rs +++ b/frame/nomination-pools/src/lib.rs @@ -120,9 +120,9 @@ //! * Depositor: creates the pool and is the initial member. They can only leave the pool once all //! other members have left. Once they fully withdraw their funds, the pool is destroyed. //! * Nominator: can select which validators the pool nominates. -//! * State-Toggler: can change the pools state and kick members if the pool is blocked. -//! * Root: can change the nominator, state-toggler, or itself and can perform any of the actions -//! the nominator or state-toggler can. +//! * Bouncer: can change the pools state and kick members if the pool is blocked. +//! * Root: can change the nominator, bouncer, or itself and can perform any of the actions the +//! nominator or bouncer can. //! //! ### Dismantling //! @@ -573,13 +573,13 @@ pub struct PoolRoles { /// Creates the pool and is the initial member. They can only leave the pool once all other /// members have left. Once they fully leave, the pool is destroyed. pub depositor: AccountId, - /// Can change the nominator, state-toggler, or itself and can perform any of the actions the - /// nominator or state-toggler can. + /// Can change the nominator, bouncer, or itself and can perform any of the actions the + /// nominator or bouncer can. pub root: Option, /// Can select which validators the pool nominates. pub nominator: Option, /// Can change the pools state and kick members if the pool is blocked. - pub state_toggler: Option, + pub bouncer: Option, } /// Pool permissions and state @@ -734,11 +734,8 @@ impl BondedPool { self.roles.root.as_ref().map_or(false, |root| root == who) } - fn is_state_toggler(&self, who: &T::AccountId) -> bool { - self.roles - .state_toggler - .as_ref() - .map_or(false, |state_toggler| state_toggler == who) + fn is_bouncer(&self, who: &T::AccountId) -> bool { + self.roles.bouncer.as_ref().map_or(false, |bouncer| bouncer == who) } fn can_update_roles(&self, who: &T::AccountId) -> bool { @@ -751,15 +748,15 @@ impl BondedPool { } fn can_kick(&self, who: &T::AccountId) -> bool { - self.state == PoolState::Blocked && (self.is_root(who) || self.is_state_toggler(who)) + self.state == PoolState::Blocked && (self.is_root(who) || self.is_bouncer(who)) } fn can_toggle_state(&self, who: &T::AccountId) -> bool { - (self.is_root(who) || self.is_state_toggler(who)) && !self.is_destroying() + (self.is_root(who) || self.is_bouncer(who)) && !self.is_destroying() } fn can_set_metadata(&self, who: &T::AccountId) -> bool { - self.is_root(who) || self.is_state_toggler(who) + self.is_root(who) || self.is_bouncer(who) } fn is_destroying(&self) -> bool { @@ -1407,7 +1404,7 @@ pub mod pallet { /// can never change. RolesUpdated { root: Option, - state_toggler: Option, + bouncer: Option, nominator: Option, }, /// The active balance of pool `pool_id` has been slashed to `balance`. @@ -1630,8 +1627,8 @@ pub mod pallet { /// /// # Conditions for a permissionless dispatch. /// - /// * The pool is blocked and the caller is either the root or state-toggler. This is - /// refereed to as a kick. + /// * The pool is blocked and the caller is either the root or bouncer. This is refereed to + /// as a kick. /// * The pool is destroying and the member is not the depositor. /// * The pool is destroying, the member is the depositor and no other members are in the /// pool. @@ -1754,7 +1751,7 @@ pub mod pallet { /// /// * The pool is in destroy mode and the target is not the depositor. /// * The target is the depositor and they are the only member in the sub pools. - /// * The pool is blocked and the caller is either the root or state-toggler. + /// * The pool is blocked and the caller is either the root or bouncer. /// /// # Conditions for permissioned dispatch /// @@ -1879,7 +1876,7 @@ pub mod pallet { /// creating multiple pools in the same extrinsic. /// * `root` - The account to set as [`PoolRoles::root`]. /// * `nominator` - The account to set as the [`PoolRoles::nominator`]. - /// * `state_toggler` - The account to set as the [`PoolRoles::state_toggler`]. + /// * `bouncer` - The account to set as the [`PoolRoles::bouncer`]. /// /// # Note /// @@ -1892,7 +1889,7 @@ pub mod pallet { #[pallet::compact] amount: BalanceOf, root: AccountIdLookupOf, nominator: AccountIdLookupOf, - state_toggler: AccountIdLookupOf, + bouncer: AccountIdLookupOf, ) -> DispatchResult { let depositor = ensure_signed(origin)?; @@ -1901,7 +1898,7 @@ pub mod pallet { Ok(*id) })?; - Self::do_create(depositor, amount, root, nominator, state_toggler, pool_id) + Self::do_create(depositor, amount, root, nominator, bouncer, pool_id) } /// Create a new delegation pool with a previously used pool id @@ -1917,7 +1914,7 @@ pub mod pallet { #[pallet::compact] amount: BalanceOf, root: AccountIdLookupOf, nominator: AccountIdLookupOf, - state_toggler: AccountIdLookupOf, + bouncer: AccountIdLookupOf, pool_id: PoolId, ) -> DispatchResult { let depositor = ensure_signed(origin)?; @@ -1925,7 +1922,7 @@ pub mod pallet { ensure!(!BondedPools::::contains_key(pool_id), Error::::PoolIdInUse); ensure!(pool_id < LastPoolId::::get(), Error::::InvalidPoolId); - Self::do_create(depositor, amount, root, nominator, state_toggler, pool_id) + Self::do_create(depositor, amount, root, nominator, bouncer, pool_id) } /// Nominate on behalf of the pool. @@ -1955,7 +1952,7 @@ pub mod pallet { /// /// The dispatch origin of this call must be either: /// - /// 1. signed by the state toggler, or the root role of the pool, + /// 1. signed by the bouncer, or the root role of the pool, /// 2. if the pool conditions to be open are NOT met (as described by `ok_to_be_open`), and /// then the state of the pool can be permissionlessly changed to `Destroying`. #[pallet::call_index(9)] @@ -1985,7 +1982,7 @@ pub mod pallet { /// Set a new metadata for the pool. /// - /// The dispatch origin of this call must be signed by the state toggler, or the root role + /// The dispatch origin of this call must be signed by the bouncer, or the root role /// of the pool. #[pallet::call_index(10)] #[pallet::weight(T::WeightInfo::set_metadata(metadata.len() as u32))] @@ -2063,7 +2060,7 @@ pub mod pallet { pool_id: PoolId, new_root: ConfigOp, new_nominator: ConfigOp, - new_state_toggler: ConfigOp, + new_bouncer: ConfigOp, ) -> DispatchResult { let mut bonded_pool = match ensure_root(origin.clone()) { Ok(()) => BondedPool::::get(pool_id).ok_or(Error::::PoolNotFound)?, @@ -2086,16 +2083,16 @@ pub mod pallet { ConfigOp::Remove => bonded_pool.roles.nominator = None, ConfigOp::Set(v) => bonded_pool.roles.nominator = Some(v), }; - match new_state_toggler { + match new_bouncer { ConfigOp::Noop => (), - ConfigOp::Remove => bonded_pool.roles.state_toggler = None, - ConfigOp::Set(v) => bonded_pool.roles.state_toggler = Some(v), + ConfigOp::Remove => bonded_pool.roles.bouncer = None, + ConfigOp::Set(v) => bonded_pool.roles.bouncer = Some(v), }; Self::deposit_event(Event::::RolesUpdated { root: bonded_pool.roles.root.clone(), nominator: bonded_pool.roles.nominator.clone(), - state_toggler: bonded_pool.roles.state_toggler.clone(), + bouncer: bonded_pool.roles.bouncer.clone(), }); bonded_pool.put(); @@ -2336,12 +2333,12 @@ impl Pallet { amount: BalanceOf, root: AccountIdLookupOf, nominator: AccountIdLookupOf, - state_toggler: AccountIdLookupOf, + bouncer: AccountIdLookupOf, pool_id: PoolId, ) -> DispatchResult { let root = T::Lookup::lookup(root)?; let nominator = T::Lookup::lookup(nominator)?; - let state_toggler = T::Lookup::lookup(state_toggler)?; + let bouncer = T::Lookup::lookup(bouncer)?; ensure!(amount >= Pallet::::depositor_min_bond(), Error::::MinimumBondNotMet); ensure!( @@ -2354,7 +2351,7 @@ impl Pallet { PoolRoles { root: Some(root), nominator: Some(nominator), - state_toggler: Some(state_toggler), + bouncer: Some(bouncer), depositor: who.clone(), }, ); diff --git a/frame/nomination-pools/src/migration.rs b/frame/nomination-pools/src/migration.rs index 4a9b6ca0f..53087e763 100644 --- a/frame/nomination-pools/src/migration.rs +++ b/frame/nomination-pools/src/migration.rs @@ -28,7 +28,7 @@ pub mod v1 { pub depositor: AccountId, pub root: AccountId, pub nominator: AccountId, - pub state_toggler: AccountId, + pub bouncer: AccountId, } impl OldPoolRoles { @@ -37,7 +37,7 @@ pub mod v1 { depositor: self.depositor, root: Some(self.root), nominator: Some(self.nominator), - state_toggler: Some(self.state_toggler), + bouncer: Some(self.bouncer), } } } diff --git a/frame/nomination-pools/src/tests.rs b/frame/nomination-pools/src/tests.rs index be52996a5..46330051b 100644 --- a/frame/nomination-pools/src/tests.rs +++ b/frame/nomination-pools/src/tests.rs @@ -37,7 +37,7 @@ macro_rules! member_unbonding_eras { } pub const DEFAULT_ROLES: PoolRoles = - PoolRoles { depositor: 10, root: Some(900), nominator: Some(901), state_toggler: Some(902) }; + PoolRoles { depositor: 10, root: Some(900), nominator: Some(901), bouncer: Some(902) }; #[test] fn test_setup_works() { @@ -2148,7 +2148,7 @@ mod unbond { .add_members(vec![(20, 20)]) .build_and_execute(|| { unsafe_set_state(1, PoolState::Blocked); - let kicker = DEFAULT_ROLES.state_toggler.unwrap(); + let kicker = DEFAULT_ROLES.bouncer.unwrap(); // cannot be kicked to above the limit. assert_noop!( @@ -2251,7 +2251,7 @@ mod unbond { // set the stage unsafe_set_state(1, PoolState::Blocked); - let kicker = DEFAULT_ROLES.state_toggler.unwrap(); + let kicker = DEFAULT_ROLES.bouncer.unwrap(); // cannot be kicked to above limit. assert_noop!( @@ -2590,7 +2590,7 @@ mod unbond { #[test] fn unbond_kick_works() { - // Kick: the pool is blocked and the caller is either the root or state-toggler. + // Kick: the pool is blocked and the caller is either the root or bouncer. ExtBuilder::default() .add_members(vec![(100, 100), (200, 200)]) .build_and_execute(|| { @@ -2599,7 +2599,7 @@ mod unbond { let bonded_pool = BondedPool::::get(1).unwrap(); assert_eq!(bonded_pool.roles.root.unwrap(), 900); assert_eq!(bonded_pool.roles.nominator.unwrap(), 901); - assert_eq!(bonded_pool.roles.state_toggler.unwrap(), 902); + assert_eq!(bonded_pool.roles.bouncer.unwrap(), 902); // When the nominator tries to kick, then its a noop assert_noop!( @@ -2627,7 +2627,7 @@ mod unbond { ] ); - // When the state toggler kicks then its ok + // When the bouncer kicks then its ok assert_ok!(Pools::fully_unbond(RuntimeOrigin::signed(902), 200)); assert_eq!( @@ -3558,7 +3558,7 @@ mod withdraw_unbonded { // Can kick as root assert_ok!(Pools::withdraw_unbonded(RuntimeOrigin::signed(900), 100, 0)); - // Can kick as state toggler + // Can kick as bouncer assert_ok!(Pools::withdraw_unbonded(RuntimeOrigin::signed(900), 200, 0)); assert_eq!(Balances::free_balance(100), 100 + 100); @@ -4148,7 +4148,7 @@ mod create { depositor: 11, root: Some(123), nominator: Some(456), - state_toggler: Some(789) + bouncer: Some(789) } } } @@ -4232,7 +4232,7 @@ mod create { amount: 20, root: 11, nominator: 11, - state_toggler: 11, + bouncer: 11, }); assert_noop!( create.dispatch(RuntimeOrigin::signed(11)), @@ -4292,7 +4292,7 @@ mod nominate { Error::::NotNominator ); - // State toggler can't nominate + // bouncer can't nominate assert_noop!( Pools::nominate(RuntimeOrigin::signed(902), 1, vec![21]), Error::::NotNominator @@ -4324,7 +4324,7 @@ mod set_state { // Given assert_ok!(BondedPool::::get(1).unwrap().ok_to_be_open()); - // Only the root and state toggler can change the state when the pool is ok to be open. + // Only the root and bouncer can change the state when the pool is ok to be open. assert_noop!( Pools::set_state(RuntimeOrigin::signed(10), 1, PoolState::Blocked), Error::::CanNotChangeState @@ -4348,7 +4348,7 @@ mod set_state { assert_eq!(BondedPool::::get(1).unwrap().state, PoolState::Blocked); - // State toggler can change state + // bouncer can change state assert_ok!(Pools::set_state(RuntimeOrigin::signed(902), 1, PoolState::Destroying)); assert_eq!(BondedPool::::get(1).unwrap().state, PoolState::Destroying); @@ -4412,7 +4412,7 @@ mod set_metadata { assert_ok!(Pools::set_metadata(RuntimeOrigin::signed(900), 1, vec![1, 1])); assert_eq!(Metadata::::get(1), vec![1, 1]); - // State toggler can set metadata + // bouncer can set metadata assert_ok!(Pools::set_metadata(RuntimeOrigin::signed(902), 1, vec![2, 2])); assert_eq!(Metadata::::get(1), vec![2, 2]); @@ -4637,7 +4637,7 @@ mod update_roles { depositor: 10, root: Some(900), nominator: Some(901), - state_toggler: Some(902) + bouncer: Some(902) }, ); @@ -4676,7 +4676,7 @@ mod update_roles { ), Error::::DoesNotHavePermission, ); - // state-toggler + // bouncer assert_noop!( Pools::update_roles( RuntimeOrigin::signed(902), @@ -4702,21 +4702,12 @@ mod update_roles { vec![ Event::Created { depositor: 10, pool_id: 1 }, Event::Bonded { member: 10, pool_id: 1, bonded: 10, joined: true }, - Event::RolesUpdated { - root: Some(5), - state_toggler: Some(7), - nominator: Some(6) - } + Event::RolesUpdated { root: Some(5), bouncer: Some(7), nominator: Some(6) } ] ); assert_eq!( BondedPools::::get(1).unwrap().roles, - PoolRoles { - depositor: 10, - root: Some(5), - nominator: Some(6), - state_toggler: Some(7) - }, + PoolRoles { depositor: 10, root: Some(5), nominator: Some(6), bouncer: Some(7) }, ); // also root origin can @@ -4730,20 +4721,11 @@ mod update_roles { assert_eq!( pool_events_since_last_call(), - vec![Event::RolesUpdated { - root: Some(1), - state_toggler: Some(3), - nominator: Some(2) - }] + vec![Event::RolesUpdated { root: Some(1), bouncer: Some(3), nominator: Some(2) }] ); assert_eq!( BondedPools::::get(1).unwrap().roles, - PoolRoles { - depositor: 10, - root: Some(1), - nominator: Some(2), - state_toggler: Some(3) - }, + PoolRoles { depositor: 10, root: Some(1), nominator: Some(2), bouncer: Some(3) }, ); // Noop works @@ -4757,21 +4739,12 @@ mod update_roles { assert_eq!( pool_events_since_last_call(), - vec![Event::RolesUpdated { - root: Some(11), - state_toggler: Some(3), - nominator: Some(2) - }] + vec![Event::RolesUpdated { root: Some(11), bouncer: Some(3), nominator: Some(2) }] ); assert_eq!( BondedPools::::get(1).unwrap().roles, - PoolRoles { - depositor: 10, - root: Some(11), - nominator: Some(2), - state_toggler: Some(3) - }, + PoolRoles { depositor: 10, root: Some(11), nominator: Some(2), bouncer: Some(3) }, ); // Remove works @@ -4785,12 +4758,12 @@ mod update_roles { assert_eq!( pool_events_since_last_call(), - vec![Event::RolesUpdated { root: Some(69), state_toggler: None, nominator: None }] + vec![Event::RolesUpdated { root: Some(69), bouncer: None, nominator: None }] ); assert_eq!( BondedPools::::get(1).unwrap().roles, - PoolRoles { depositor: 10, root: Some(69), nominator: None, state_toggler: None }, + PoolRoles { depositor: 10, root: Some(69), nominator: None, bouncer: None }, ); }) } From 0e8daf091fd801c26022d0da1876d63852334ec3 Mon Sep 17 00:00:00 2001 From: Oliver Tale-Yazdi Date: Tue, 21 Feb 2023 18:03:17 +0100 Subject: [PATCH 145/558] Add `defensive_assert!` macro (#13423) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Add defensive_assert macro Signed-off-by: Oliver Tale-Yazdi * Apply review suggestions Co-authored-by: Bastian Köcher Signed-off-by: Oliver Tale-Yazdi --------- Signed-off-by: Oliver Tale-Yazdi --- frame/support/src/traits/misc.rs | 44 ++++++++++++++++++++++++++++++-- 1 file changed, 42 insertions(+), 2 deletions(-) diff --git a/frame/support/src/traits/misc.rs b/frame/support/src/traits/misc.rs index 734b84ebd..5a13fc4ab 100644 --- a/frame/support/src/traits/misc.rs +++ b/frame/support/src/traits/misc.rs @@ -49,7 +49,7 @@ macro_rules! defensive { ); debug_assert!(false, "{}", $crate::traits::DEFENSIVE_OP_INTERNAL_ERROR); }; - ($error:tt) => { + ($error:expr $(,)?) => { frame_support::log::error!( target: "runtime", "{}: {:?}", @@ -58,7 +58,7 @@ macro_rules! defensive { ); debug_assert!(false, "{}: {:?}", $crate::traits::DEFENSIVE_OP_INTERNAL_ERROR, $error); }; - ($error:tt, $proof:tt) => { + ($error:expr, $proof:expr $(,)?) => { frame_support::log::error!( target: "runtime", "{}: {:?}: {:?}", @@ -70,6 +70,25 @@ macro_rules! defensive { } } +/// Trigger a defensive failure if a condition is not met. +/// +/// Similar to [`assert!`] but will print an error without `debug_assertions` instead of silently +/// ignoring it. Only accepts one instead of variable formatting arguments. +/// +/// # Example +/// +/// ```should_panic +/// frame_support::defensive_assert!(1 == 0, "Must fail") +/// ``` +#[macro_export] +macro_rules! defensive_assert { + ($cond:expr $(, $proof:expr )? $(,)?) => { + if !($cond) { + $crate::defensive!(::core::stringify!($cond) $(, $proof )?); + } + }; +} + /// Prelude module for all defensive traits to be imported at once. pub mod defensive_prelude { pub use super::{Defensive, DefensiveOption, DefensiveResult}; @@ -1141,6 +1160,27 @@ mod test { use sp_core::bounded::{BoundedSlice, BoundedVec}; use sp_std::marker::PhantomData; + #[test] + fn defensive_assert_works() { + defensive_assert!(true); + defensive_assert!(true,); + defensive_assert!(true, "must work"); + defensive_assert!(true, "must work",); + } + + #[test] + #[cfg(debug_assertions)] + #[should_panic(expected = "Defensive failure has been triggered!: \"1 == 0\": \"Must fail\"")] + fn defensive_assert_panics() { + defensive_assert!(1 == 0, "Must fail"); + } + + #[test] + #[cfg(not(debug_assertions))] + fn defensive_assert_does_not_panic() { + defensive_assert!(1 == 0, "Must fail"); + } + #[test] #[cfg(not(debug_assertions))] fn defensive_saturating_accrue_works() { From 2d6e5556bbf47581b5f86b1c2f6bd7eb9add4cd9 Mon Sep 17 00:00:00 2001 From: Michal Kucharczyk <1728078+michalkucharczyk@users.noreply.github.com> Date: Tue, 21 Feb 2023 19:36:00 +0100 Subject: [PATCH 146/558] `BlockId` removal: `BlockBuilderProvider::new_block_at` (#13401) * `BlockId` removal: `BlockBuilderProvider::new_block_at` It changes the arguments of `BlockBuilderProvider::new_block_at` from: `BlockId` to: `Block::Hash` * fmt * fix * more fixes --- bin/node/cli/benches/block_production.rs | 7 +- .../basic-authorship/src/basic_authorship.rs | 11 +- client/beefy/src/tests.rs | 15 +- client/block-builder/src/lib.rs | 3 +- client/consensus/babe/src/tests.rs | 9 +- client/finality-grandpa/src/tests.rs | 6 +- .../merkle-mountain-range/src/test_utils.rs | 3 +- .../network/src/service/tests/chain_sync.rs | 7 +- client/network/sync/src/lib.rs | 7 +- client/network/test/src/lib.rs | 3 +- client/rpc-spec-v2/src/chain_head/tests.rs | 10 +- client/service/src/client/client.rs | 6 +- client/service/test/src/client/mod.rs | 202 ++++++++++-------- primitives/api/test/tests/runtime_calls.rs | 8 +- test-utils/runtime/client/src/trait_tests.rs | 74 +++---- 15 files changed, 175 insertions(+), 196 deletions(-) diff --git a/bin/node/cli/benches/block_production.rs b/bin/node/cli/benches/block_production.rs index 4fcebb123..64ea03f58 100644 --- a/bin/node/cli/benches/block_production.rs +++ b/bin/node/cli/benches/block_production.rs @@ -37,7 +37,6 @@ use sp_blockchain::{ApplyExtrinsicFailed::Validity, Error::ApplyExtrinsicFailed} use sp_consensus::BlockOrigin; use sp_keyring::Sr25519Keyring; use sp_runtime::{ - generic::BlockId, transaction_validity::{InvalidTransaction, TransactionValidityError}, AccountId32, MultiAddress, OpaqueExtrinsic, }; @@ -206,14 +205,14 @@ fn block_production(c: &mut Criterion) { group.sample_size(10); group.throughput(Throughput::Elements(max_transfer_count as u64)); - let block_id = BlockId::Hash(client.chain_info().best_hash); + let best_hash = client.chain_info().best_hash; group.bench_function(format!("{} transfers (no proof)", max_transfer_count), |b| { b.iter_batched( || extrinsics.clone(), |extrinsics| { let mut block_builder = - client.new_block_at(&block_id, Default::default(), RecordProof::No).unwrap(); + client.new_block_at(best_hash, Default::default(), RecordProof::No).unwrap(); for extrinsic in extrinsics { block_builder.push(extrinsic).unwrap(); } @@ -228,7 +227,7 @@ fn block_production(c: &mut Criterion) { || extrinsics.clone(), |extrinsics| { let mut block_builder = - client.new_block_at(&block_id, Default::default(), RecordProof::Yes).unwrap(); + client.new_block_at(best_hash, Default::default(), RecordProof::Yes).unwrap(); for extrinsic in extrinsics { block_builder.push(extrinsic).unwrap(); } diff --git a/client/basic-authorship/src/basic_authorship.rs b/client/basic-authorship/src/basic_authorship.rs index 9ab7ef55f..f68a86ef1 100644 --- a/client/basic-authorship/src/basic_authorship.rs +++ b/client/basic-authorship/src/basic_authorship.rs @@ -38,7 +38,6 @@ use sp_consensus::{DisableProofRecording, EnableProofRecording, ProofRecording, use sp_core::traits::SpawnNamed; use sp_inherents::InherentData; use sp_runtime::{ - generic::BlockId, traits::{BlakeTwo256, Block as BlockT, Hash as HashT, Header as HeaderT}, Digest, Percent, SaturatedConversion, }; @@ -196,14 +195,12 @@ where ) -> Proposer { let parent_hash = parent_header.hash(); - let id = BlockId::hash(parent_hash); - info!("🙌 Starting consensus session on top of parent {:?}", parent_hash); let proposer = Proposer::<_, _, _, _, PR> { spawn_handle: self.spawn_handle.clone(), client: self.client.clone(), - parent_id: id, + parent_hash, parent_number: *parent_header.number(), transaction_pool: self.transaction_pool.clone(), now, @@ -247,7 +244,7 @@ where pub struct Proposer { spawn_handle: Box, client: Arc, - parent_id: BlockId, + parent_hash: Block::Hash, parent_number: <::Header as HeaderT>::Number, transaction_pool: Arc, now: Box time::Instant + Send + Sync>, @@ -344,7 +341,7 @@ where { let propose_with_start = time::Instant::now(); let mut block_builder = - self.client.new_block_at(&self.parent_id, inherent_digests, PR::ENABLED)?; + self.client.new_block_at(self.parent_hash, inherent_digests, PR::ENABLED)?; let create_inherents_start = time::Instant::now(); let inherents = block_builder.create_inherents(inherent_data)?; @@ -559,7 +556,7 @@ mod tests { use sp_blockchain::HeaderBackend; use sp_consensus::{BlockOrigin, Environment, Proposer}; use sp_core::Pair; - use sp_runtime::traits::NumberFor; + use sp_runtime::{generic::BlockId, traits::NumberFor}; use substrate_test_runtime_client::{ prelude::*, runtime::{Extrinsic, Transfer}, diff --git a/client/beefy/src/tests.rs b/client/beefy/src/tests.rs index f17a42f20..d8afc5465 100644 --- a/client/beefy/src/tests.rs +++ b/client/beefy/src/tests.rs @@ -58,7 +58,6 @@ use sp_keystore::{testing::KeyStore as TestKeystore, SyncCryptoStore, SyncCrypto use sp_mmr_primitives::{Error as MmrError, MmrApi}; use sp_runtime::{ codec::Encode, - generic::BlockId, traits::{Header as HeaderT, NumberFor}, BuildStorage, DigestItem, EncodedJustification, Justifications, Storage, }; @@ -753,8 +752,9 @@ async fn beefy_importing_justifications() { .and_then(|j| j.get(BEEFY_ENGINE_ID).cloned()) }; - let parent_id = BlockId::Number(0); - let builder = full_client.new_block_at(&parent_id, Default::default(), false).unwrap(); + let builder = full_client + .new_block_at(full_client.chain_info().genesis_hash, Default::default(), false) + .unwrap(); let block = builder.build().unwrap().block; let hashof1 = block.header.hash(); @@ -772,9 +772,8 @@ async fn beefy_importing_justifications() { ); // Import block 2 with "valid" justification (beefy pallet genesis block not yet reached). - let parent_id = BlockId::Number(1); let block_num = 2; - let builder = full_client.new_block_at(&parent_id, Default::default(), false).unwrap(); + let builder = full_client.new_block_at(hashof1, Default::default(), false).unwrap(); let block = builder.build().unwrap().block; let hashof2 = block.header.hash(); @@ -805,9 +804,8 @@ async fn beefy_importing_justifications() { } // Import block 3 with valid justification. - let parent_id = BlockId::Number(2); let block_num = 3; - let builder = full_client.new_block_at(&parent_id, Default::default(), false).unwrap(); + let builder = full_client.new_block_at(hashof2, Default::default(), false).unwrap(); let block = builder.build().unwrap().block; let hashof3 = block.header.hash(); let proof = crate::justification::tests::new_finality_proof(block_num, &good_set, keys); @@ -840,9 +838,8 @@ async fn beefy_importing_justifications() { } // Import block 4 with invalid justification (incorrect validator set). - let parent_id = BlockId::Number(3); let block_num = 4; - let builder = full_client.new_block_at(&parent_id, Default::default(), false).unwrap(); + let builder = full_client.new_block_at(hashof3, Default::default(), false).unwrap(); let block = builder.build().unwrap().block; let hashof4 = block.header.hash(); let keys = &[BeefyKeyring::Alice]; diff --git a/client/block-builder/src/lib.rs b/client/block-builder/src/lib.rs index d302a834f..437e43a71 100644 --- a/client/block-builder/src/lib.rs +++ b/client/block-builder/src/lib.rs @@ -34,7 +34,6 @@ use sp_api::{ use sp_blockchain::{ApplyExtrinsicFailed, Error}; use sp_core::ExecutionContext; use sp_runtime::{ - generic::BlockId, legacy, traits::{Block as BlockT, Hash, HashFor, Header as HeaderT, NumberFor, One}, Digest, @@ -120,7 +119,7 @@ where /// output of this block builder without having access to the full storage. fn new_block_at>( &self, - parent: &BlockId, + parent: Block::Hash, inherent_digests: Digest, record_proof: R, ) -> sp_blockchain::Result>; diff --git a/client/consensus/babe/src/tests.rs b/client/consensus/babe/src/tests.rs index ad2e95df0..d1ad91a1c 100644 --- a/client/consensus/babe/src/tests.rs +++ b/client/consensus/babe/src/tests.rs @@ -45,7 +45,7 @@ use sp_keystore::{ SyncCryptoStore, }; use sp_runtime::{ - generic::{BlockId, Digest, DigestItem}, + generic::{Digest, DigestItem}, traits::Block as BlockT, }; use sp_timestamp::Timestamp; @@ -123,11 +123,8 @@ impl DummyProposer { Error, >, > { - let block_builder = self - .factory - .client - .new_block_at(&BlockId::Hash(self.parent_hash), pre_digests, false) - .unwrap(); + let block_builder = + self.factory.client.new_block_at(self.parent_hash, pre_digests, false).unwrap(); let mut block = match block_builder.build().map_err(|e| e.into()) { Ok(b) => b.block, diff --git a/client/finality-grandpa/src/tests.rs b/client/finality-grandpa/src/tests.rs index d132abd94..fee3b13ba 100644 --- a/client/finality-grandpa/src/tests.rs +++ b/client/finality-grandpa/src/tests.rs @@ -875,7 +875,7 @@ async fn allows_reimporting_change_blocks() { let full_client = client.as_client(); let builder = full_client - .new_block_at(&BlockId::Number(0), Default::default(), false) + .new_block_at(full_client.chain_info().genesis_hash, Default::default(), false) .unwrap(); let mut block = builder.build().unwrap().block; add_scheduled_change( @@ -922,7 +922,7 @@ async fn test_bad_justification() { let full_client = client.as_client(); let builder = full_client - .new_block_at(&BlockId::Number(0), Default::default(), false) + .new_block_at(full_client.chain_info().genesis_hash, Default::default(), false) .unwrap(); let mut block = builder.build().unwrap().block; @@ -1854,7 +1854,7 @@ async fn imports_justification_for_regular_blocks_on_import() { let full_client = client.as_client(); let builder = full_client - .new_block_at(&BlockId::Number(0), Default::default(), false) + .new_block_at(full_client.chain_info().genesis_hash, Default::default(), false) .unwrap(); let block = builder.build().unwrap().block; diff --git a/client/merkle-mountain-range/src/test_utils.rs b/client/merkle-mountain-range/src/test_utils.rs index e5a667348..9decaa3c2 100644 --- a/client/merkle-mountain-range/src/test_utils.rs +++ b/client/merkle-mountain-range/src/test_utils.rs @@ -124,7 +124,8 @@ impl MockClient { ) -> MmrBlock { let mut client = self.client.lock(); - let mut block_builder = client.new_block_at(at, Default::default(), false).unwrap(); + let hash = client.expect_block_hash_from_id(&at).unwrap(); + let mut block_builder = client.new_block_at(hash, Default::default(), false).unwrap(); // Make sure the block has a different hash than its siblings block_builder .push_storage_change(b"name".to_vec(), Some(name.to_vec())) diff --git a/client/network/src/service/tests/chain_sync.rs b/client/network/src/service/tests/chain_sync.rs index 1ae432fd4..98884024a 100644 --- a/client/network/src/service/tests/chain_sync.rs +++ b/client/network/src/service/tests/chain_sync.rs @@ -34,10 +34,7 @@ use sc_network_common::{ }; use sc_network_sync::{mock::MockChainSync, service::mock::MockChainSyncInterface, ChainSync}; use sp_core::H256; -use sp_runtime::{ - generic::BlockId, - traits::{Block as BlockT, Header as _}, -}; +use sp_runtime::traits::{Block as BlockT, Header as _}; use std::{ sync::{Arc, RwLock}, task::Poll, @@ -188,7 +185,7 @@ async fn on_block_finalized() { let at = client.header(client.info().best_hash).unwrap().unwrap().hash(); let block = client - .new_block_at(&BlockId::Hash(at), Default::default(), false) + .new_block_at(at, Default::default(), false) .unwrap() .build() .unwrap() diff --git a/client/network/sync/src/lib.rs b/client/network/sync/src/lib.rs index f56ef13fc..1f4f6a04e 100644 --- a/client/network/sync/src/lib.rs +++ b/client/network/sync/src/lib.rs @@ -3212,7 +3212,6 @@ mod test { }; use sp_blockchain::HeaderBackend; use sp_consensus::block_validation::DefaultBlockAnnounceValidator; - use sp_runtime::generic::BlockId; use substrate_test_runtime_client::{ runtime::{Block, Hash, Header}, BlockBuilderExt, ClientBlockImportExt, ClientExt, DefaultTestClientBuilderExt, TestClient, @@ -3449,8 +3448,7 @@ mod test { fn build_block(client: &mut Arc, at: Option, fork: bool) -> Block { let at = at.unwrap_or_else(|| client.info().best_hash); - let mut block_builder = - client.new_block_at(&BlockId::Hash(at), Default::default(), false).unwrap(); + let mut block_builder = client.new_block_at(at, Default::default(), false).unwrap(); if fork { block_builder.push_storage_change(vec![1, 2, 3], Some(vec![4, 5, 6])).unwrap(); @@ -3503,8 +3501,7 @@ mod test { let mut client2 = client.clone(); let mut build_block_at = |at, import| { - let mut block_builder = - client2.new_block_at(&BlockId::Hash(at), Default::default(), false).unwrap(); + let mut block_builder = client2.new_block_at(at, Default::default(), false).unwrap(); // Make sure we generate a different block as fork block_builder.push_storage_change(vec![1, 2, 3], Some(vec![4, 5, 6])).unwrap(); diff --git a/client/network/test/src/lib.rs b/client/network/test/src/lib.rs index c47e3c86f..3cd508103 100644 --- a/client/network/test/src/lib.rs +++ b/client/network/test/src/lib.rs @@ -377,8 +377,7 @@ where let full_client = self.client.as_client(); let mut at = full_client.block_hash_from_id(&at).unwrap().unwrap(); for _ in 0..count { - let builder = - full_client.new_block_at(&BlockId::Hash(at), Default::default(), false).unwrap(); + let builder = full_client.new_block_at(at, Default::default(), false).unwrap(); let block = edit_block(builder); let hash = block.header.hash(); trace!( diff --git a/client/rpc-spec-v2/src/chain_head/tests.rs b/client/rpc-spec-v2/src/chain_head/tests.rs index 4084075f0..79be39136 100644 --- a/client/rpc-spec-v2/src/chain_head/tests.rs +++ b/client/rpc-spec-v2/src/chain_head/tests.rs @@ -8,7 +8,6 @@ use jsonrpsee::{ }; use sc_block_builder::BlockBuilderProvider; use sc_client_api::ChildInfo; -use sp_api::BlockId; use sp_blockchain::HeaderBackend; use sp_consensus::BlockOrigin; use sp_core::{ @@ -639,9 +638,8 @@ async fn follow_generates_initial_blocks() { let block_2_hash = block_2.header.hash(); client.import(BlockOrigin::Own, block_2.clone()).await.unwrap(); - let mut block_builder = client - .new_block_at(&BlockId::Hash(block_1.header.hash()), Default::default(), false) - .unwrap(); + let mut block_builder = + client.new_block_at(block_1.header.hash(), Default::default(), false).unwrap(); // This push is required as otherwise block 3 has the same hash as block 2 and won't get // imported block_builder @@ -921,9 +919,7 @@ async fn follow_prune_best_block() { client.import(BlockOrigin::Own, block_4.clone()).await.unwrap(); // Import block 2 as best on the fork. - let mut block_builder = client - .new_block_at(&BlockId::Hash(block_1.header.hash()), Default::default(), false) - .unwrap(); + let mut block_builder = client.new_block_at(block_1_hash, Default::default(), false).unwrap(); // This push is required as otherwise block 3 has the same hash as block 2 and won't get // imported block_builder diff --git a/client/service/src/client/client.rs b/client/service/src/client/client.rs index 7bd37b516..615817cc3 100644 --- a/client/service/src/client/client.rs +++ b/client/service/src/client/client.rs @@ -1408,14 +1408,14 @@ where { fn new_block_at>( &self, - parent: &BlockId, + parent: Block::Hash, inherent_digests: Digest, record_proof: R, ) -> sp_blockchain::Result> { sc_block_builder::BlockBuilder::new( self, - self.expect_block_hash_from_id(parent)?, - self.expect_block_number_from_id(parent)?, + parent, + self.expect_block_number_from_id(&BlockId::Hash(parent))?, record_proof.into(), inherent_digests, &self.backend, diff --git a/client/service/test/src/client/mod.rs b/client/service/test/src/client/mod.rs index 53af18c6b..0fe4760fc 100644 --- a/client/service/test/src/client/mod.rs +++ b/client/service/test/src/client/mod.rs @@ -447,7 +447,7 @@ fn uncles_with_multiple_forks() { // A1 -> A2 let a2 = client - .new_block_at(&BlockId::Hash(a1.hash()), Default::default(), false) + .new_block_at(a1.hash(), Default::default(), false) .unwrap() .build() .unwrap() @@ -456,7 +456,7 @@ fn uncles_with_multiple_forks() { // A2 -> A3 let a3 = client - .new_block_at(&BlockId::Hash(a2.hash()), Default::default(), false) + .new_block_at(a2.hash(), Default::default(), false) .unwrap() .build() .unwrap() @@ -465,7 +465,7 @@ fn uncles_with_multiple_forks() { // A3 -> A4 let a4 = client - .new_block_at(&BlockId::Hash(a3.hash()), Default::default(), false) + .new_block_at(a3.hash(), Default::default(), false) .unwrap() .build() .unwrap() @@ -474,7 +474,7 @@ fn uncles_with_multiple_forks() { // A4 -> A5 let a5 = client - .new_block_at(&BlockId::Hash(a4.hash()), Default::default(), false) + .new_block_at(a4.hash(), Default::default(), false) .unwrap() .build() .unwrap() @@ -482,9 +482,7 @@ fn uncles_with_multiple_forks() { block_on(client.import(BlockOrigin::Own, a5.clone())).unwrap(); // A1 -> B2 - let mut builder = client - .new_block_at(&BlockId::Hash(a1.hash()), Default::default(), false) - .unwrap(); + let mut builder = client.new_block_at(a1.hash(), Default::default(), false).unwrap(); // this push is required as otherwise B2 has the same hash as A2 and won't get imported builder .push_transfer(Transfer { @@ -499,7 +497,7 @@ fn uncles_with_multiple_forks() { // B2 -> B3 let b3 = client - .new_block_at(&BlockId::Hash(b2.hash()), Default::default(), false) + .new_block_at(b2.hash(), Default::default(), false) .unwrap() .build() .unwrap() @@ -508,7 +506,7 @@ fn uncles_with_multiple_forks() { // B3 -> B4 let b4 = client - .new_block_at(&BlockId::Hash(b3.hash()), Default::default(), false) + .new_block_at(b3.hash(), Default::default(), false) .unwrap() .build() .unwrap() @@ -516,9 +514,7 @@ fn uncles_with_multiple_forks() { block_on(client.import(BlockOrigin::Own, b4.clone())).unwrap(); // // B2 -> C3 - let mut builder = client - .new_block_at(&BlockId::Hash(b2.hash()), Default::default(), false) - .unwrap(); + let mut builder = client.new_block_at(b2.hash(), Default::default(), false).unwrap(); // this push is required as otherwise C3 has the same hash as B3 and won't get imported builder .push_transfer(Transfer { @@ -532,9 +528,7 @@ fn uncles_with_multiple_forks() { block_on(client.import(BlockOrigin::Own, c3.clone())).unwrap(); // A1 -> D2 - let mut builder = client - .new_block_at(&BlockId::Hash(a1.hash()), Default::default(), false) - .unwrap(); + let mut builder = client.new_block_at(a1.hash(), Default::default(), false).unwrap(); // this push is required as otherwise D2 has the same hash as B2 and won't get imported builder .push_transfer(Transfer { @@ -608,7 +602,7 @@ fn finality_target_on_longest_chain_with_multiple_forks() { // A1 -> A2 let a2 = client - .new_block_at(&BlockId::Hash(a1.hash()), Default::default(), false) + .new_block_at(a1.hash(), Default::default(), false) .unwrap() .build() .unwrap() @@ -617,7 +611,7 @@ fn finality_target_on_longest_chain_with_multiple_forks() { // A2 -> A3 let a3 = client - .new_block_at(&BlockId::Hash(a2.hash()), Default::default(), false) + .new_block_at(a2.hash(), Default::default(), false) .unwrap() .build() .unwrap() @@ -626,7 +620,7 @@ fn finality_target_on_longest_chain_with_multiple_forks() { // A3 -> A4 let a4 = client - .new_block_at(&BlockId::Hash(a3.hash()), Default::default(), false) + .new_block_at(a3.hash(), Default::default(), false) .unwrap() .build() .unwrap() @@ -635,7 +629,7 @@ fn finality_target_on_longest_chain_with_multiple_forks() { // A4 -> A5 let a5 = client - .new_block_at(&BlockId::Hash(a4.hash()), Default::default(), false) + .new_block_at(a4.hash(), Default::default(), false) .unwrap() .build() .unwrap() @@ -643,9 +637,7 @@ fn finality_target_on_longest_chain_with_multiple_forks() { block_on(client.import(BlockOrigin::Own, a5.clone())).unwrap(); // A1 -> B2 - let mut builder = client - .new_block_at(&BlockId::Hash(a1.hash()), Default::default(), false) - .unwrap(); + let mut builder = client.new_block_at(a1.hash(), Default::default(), false).unwrap(); // this push is required as otherwise B2 has the same hash as A2 and won't get imported builder .push_transfer(Transfer { @@ -660,7 +652,7 @@ fn finality_target_on_longest_chain_with_multiple_forks() { // B2 -> B3 let b3 = client - .new_block_at(&BlockId::Hash(b2.hash()), Default::default(), false) + .new_block_at(b2.hash(), Default::default(), false) .unwrap() .build() .unwrap() @@ -669,7 +661,7 @@ fn finality_target_on_longest_chain_with_multiple_forks() { // B3 -> B4 let b4 = client - .new_block_at(&BlockId::Hash(b3.hash()), Default::default(), false) + .new_block_at(b3.hash(), Default::default(), false) .unwrap() .build() .unwrap() @@ -677,9 +669,7 @@ fn finality_target_on_longest_chain_with_multiple_forks() { block_on(client.import(BlockOrigin::Own, b4.clone())).unwrap(); // B2 -> C3 - let mut builder = client - .new_block_at(&BlockId::Hash(b2.hash()), Default::default(), false) - .unwrap(); + let mut builder = client.new_block_at(b2.hash(), Default::default(), false).unwrap(); // this push is required as otherwise C3 has the same hash as B3 and won't get imported builder .push_transfer(Transfer { @@ -693,9 +683,7 @@ fn finality_target_on_longest_chain_with_multiple_forks() { block_on(client.import(BlockOrigin::Own, c3.clone())).unwrap(); // A1 -> D2 - let mut builder = client - .new_block_at(&BlockId::Hash(a1.hash()), Default::default(), false) - .unwrap(); + let mut builder = client.new_block_at(a1.hash(), Default::default(), false).unwrap(); // this push is required as otherwise D2 has the same hash as B2 and won't get imported builder .push_transfer(Transfer { @@ -871,9 +859,7 @@ fn finality_target_with_best_not_on_longest_chain() { block_on(client.import(BlockOrigin::Own, a5.clone())).unwrap(); // A1 -> B2 - let mut builder = client - .new_block_at(&BlockId::Hash(a1.hash()), Default::default(), false) - .unwrap(); + let mut builder = client.new_block_at(a1.hash(), Default::default(), false).unwrap(); // this push is required as otherwise B2 has the same hash as A2 and won't get imported builder .push_transfer(Transfer { @@ -895,7 +881,7 @@ fn finality_target_with_best_not_on_longest_chain() { // B2 -> B3 let b3 = client - .new_block_at(&BlockId::Hash(b2.hash()), Default::default(), false) + .new_block_at(b2.hash(), Default::default(), false) .unwrap() .build() .unwrap() @@ -904,7 +890,7 @@ fn finality_target_with_best_not_on_longest_chain() { // B3 -> B4 let b4 = client - .new_block_at(&BlockId::Hash(b3.hash()), Default::default(), false) + .new_block_at(b3.hash(), Default::default(), false) .unwrap() .build() .unwrap() @@ -940,7 +926,7 @@ fn import_with_justification() { // A1 -> A2 let a2 = client - .new_block_at(&BlockId::Hash(a1.hash()), Default::default(), false) + .new_block_at(a1.hash(), Default::default(), false) .unwrap() .build() .unwrap() @@ -951,7 +937,7 @@ fn import_with_justification() { // A2 -> A3 let justification = Justifications::from((TEST_ENGINE_ID, vec![1, 2, 3])); let a3 = client - .new_block_at(&BlockId::Hash(a2.hash()), Default::default(), false) + .new_block_at(a2.hash(), Default::default(), false) .unwrap() .build() .unwrap() @@ -982,7 +968,7 @@ fn importing_diverged_finalized_block_should_trigger_reorg() { let mut finality_notifications = client.finality_notification_stream(); let a1 = client - .new_block_at(&BlockId::Number(0), Default::default(), false) + .new_block_at(client.chain_info().genesis_hash, Default::default(), false) .unwrap() .build() .unwrap() @@ -990,14 +976,16 @@ fn importing_diverged_finalized_block_should_trigger_reorg() { block_on(client.import(BlockOrigin::Own, a1.clone())).unwrap(); let a2 = client - .new_block_at(&BlockId::Hash(a1.hash()), Default::default(), false) + .new_block_at(a1.hash(), Default::default(), false) .unwrap() .build() .unwrap() .block; block_on(client.import(BlockOrigin::Own, a2.clone())).unwrap(); - let mut b1 = client.new_block_at(&BlockId::Number(0), Default::default(), false).unwrap(); + let mut b1 = client + .new_block_at(client.chain_info().genesis_hash, Default::default(), false) + .unwrap(); // needed to make sure B1 gets a different hash from A1 b1.push_transfer(Transfer { from: AccountKeyring::Alice.into(), @@ -1035,7 +1023,7 @@ fn finalizing_diverged_block_should_trigger_reorg() { let mut finality_notifications = client.finality_notification_stream(); let a1 = client - .new_block_at(&BlockId::Number(0), Default::default(), false) + .new_block_at(client.chain_info().genesis_hash, Default::default(), false) .unwrap() .build() .unwrap() @@ -1043,14 +1031,16 @@ fn finalizing_diverged_block_should_trigger_reorg() { block_on(client.import(BlockOrigin::Own, a1.clone())).unwrap(); let a2 = client - .new_block_at(&BlockId::Hash(a1.hash()), Default::default(), false) + .new_block_at(a1.hash(), Default::default(), false) .unwrap() .build() .unwrap() .block; block_on(client.import(BlockOrigin::Own, a2.clone())).unwrap(); - let mut b1 = client.new_block_at(&BlockId::Number(0), Default::default(), false).unwrap(); + let mut b1 = client + .new_block_at(client.chain_info().genesis_hash, Default::default(), false) + .unwrap(); // needed to make sure B1 gets a different hash from A1 b1.push_transfer(Transfer { from: AccountKeyring::Alice.into(), @@ -1063,7 +1053,7 @@ fn finalizing_diverged_block_should_trigger_reorg() { block_on(client.import(BlockOrigin::Own, b1.clone())).unwrap(); let b2 = client - .new_block_at(&BlockId::Hash(b1.hash()), Default::default(), false) + .new_block_at(b1.hash(), Default::default(), false) .unwrap() .build() .unwrap() @@ -1089,7 +1079,7 @@ fn finalizing_diverged_block_should_trigger_reorg() { // after we build B3 on top of B2 and import it, it should be the new best block let b3 = client - .new_block_at(&BlockId::Hash(b2.hash()), Default::default(), false) + .new_block_at(b2.hash(), Default::default(), false) .unwrap() .build() .unwrap() @@ -1121,7 +1111,7 @@ fn finality_notifications_content() { let mut finality_notifications = client.finality_notification_stream(); let a1 = client - .new_block_at(&BlockId::Number(0), Default::default(), false) + .new_block_at(client.chain_info().genesis_hash, Default::default(), false) .unwrap() .build() .unwrap() @@ -1129,7 +1119,7 @@ fn finality_notifications_content() { block_on(client.import(BlockOrigin::Own, a1.clone())).unwrap(); let a2 = client - .new_block_at(&BlockId::Hash(a1.hash()), Default::default(), false) + .new_block_at(a1.hash(), Default::default(), false) .unwrap() .build() .unwrap() @@ -1137,14 +1127,16 @@ fn finality_notifications_content() { block_on(client.import(BlockOrigin::Own, a2.clone())).unwrap(); let a3 = client - .new_block_at(&BlockId::Hash(a2.hash()), Default::default(), false) + .new_block_at(a2.hash(), Default::default(), false) .unwrap() .build() .unwrap() .block; block_on(client.import(BlockOrigin::Own, a3.clone())).unwrap(); - let mut b1 = client.new_block_at(&BlockId::Number(0), Default::default(), false).unwrap(); + let mut b1 = client + .new_block_at(client.chain_info().genesis_hash, Default::default(), false) + .unwrap(); // needed to make sure B1 gets a different hash from A1 b1.push_transfer(Transfer { from: AccountKeyring::Alice.into(), @@ -1157,14 +1149,16 @@ fn finality_notifications_content() { block_on(client.import(BlockOrigin::Own, b1.clone())).unwrap(); let b2 = client - .new_block_at(&BlockId::Hash(b1.hash()), Default::default(), false) + .new_block_at(b1.hash(), Default::default(), false) .unwrap() .build() .unwrap() .block; block_on(client.import(BlockOrigin::Own, b2.clone())).unwrap(); - let mut c1 = client.new_block_at(&BlockId::Number(0), Default::default(), false).unwrap(); + let mut c1 = client + .new_block_at(client.chain_info().genesis_hash, Default::default(), false) + .unwrap(); // needed to make sure B1 gets a different hash from A1 c1.push_transfer(Transfer { from: AccountKeyring::Alice.into(), @@ -1176,9 +1170,7 @@ fn finality_notifications_content() { let c1 = c1.build().unwrap().block; block_on(client.import(BlockOrigin::Own, c1.clone())).unwrap(); - let mut d3 = client - .new_block_at(&BlockId::Hash(a2.hash()), Default::default(), false) - .unwrap(); + let mut d3 = client.new_block_at(a2.hash(), Default::default(), false).unwrap(); // needed to make sure D3 gets a different hash from A3 d3.push_transfer(Transfer { from: AccountKeyring::Alice.into(), @@ -1191,7 +1183,7 @@ fn finality_notifications_content() { block_on(client.import(BlockOrigin::Own, d3.clone())).unwrap(); let d4 = client - .new_block_at(&BlockId::Hash(d3.hash()), Default::default(), false) + .new_block_at(d3.hash(), Default::default(), false) .unwrap() .build() .unwrap() @@ -1251,7 +1243,9 @@ fn state_reverted_on_reorg() { // G -> A1 -> A2 // \ // -> B1 - let mut a1 = client.new_block_at(&BlockId::Number(0), Default::default(), false).unwrap(); + let mut a1 = client + .new_block_at(client.chain_info().genesis_hash, Default::default(), false) + .unwrap(); a1.push_transfer(Transfer { from: AccountKeyring::Alice.into(), to: AccountKeyring::Bob.into(), @@ -1262,7 +1256,9 @@ fn state_reverted_on_reorg() { let a1 = a1.build().unwrap().block; block_on(client.import(BlockOrigin::Own, a1.clone())).unwrap(); - let mut b1 = client.new_block_at(&BlockId::Number(0), Default::default(), false).unwrap(); + let mut b1 = client + .new_block_at(client.chain_info().genesis_hash, Default::default(), false) + .unwrap(); b1.push_transfer(Transfer { from: AccountKeyring::Alice.into(), to: AccountKeyring::Ferdie.into(), @@ -1275,9 +1271,7 @@ fn state_reverted_on_reorg() { block_on(client.import_as_best(BlockOrigin::Own, b1.clone())).unwrap(); assert_eq!(950, current_balance(&client)); - let mut a2 = client - .new_block_at(&BlockId::Hash(a1.hash()), Default::default(), false) - .unwrap(); + let mut a2 = client.new_block_at(a1.hash(), Default::default(), false).unwrap(); a2.push_transfer(Transfer { from: AccountKeyring::Alice.into(), to: AccountKeyring::Charlie.into(), @@ -1322,7 +1316,7 @@ fn doesnt_import_blocks_that_revert_finality() { // -> B1 -> B2 -> B3 let a1 = client - .new_block_at(&BlockId::Number(0), Default::default(), false) + .new_block_at(client.chain_info().genesis_hash, Default::default(), false) .unwrap() .build() .unwrap() @@ -1330,14 +1324,16 @@ fn doesnt_import_blocks_that_revert_finality() { block_on(client.import(BlockOrigin::Own, a1.clone())).unwrap(); let a2 = client - .new_block_at(&BlockId::Hash(a1.hash()), Default::default(), false) + .new_block_at(a1.hash(), Default::default(), false) .unwrap() .build() .unwrap() .block; block_on(client.import(BlockOrigin::Own, a2.clone())).unwrap(); - let mut b1 = client.new_block_at(&BlockId::Number(0), Default::default(), false).unwrap(); + let mut b1 = client + .new_block_at(client.chain_info().genesis_hash, Default::default(), false) + .unwrap(); // needed to make sure B1 gets a different hash from A1 b1.push_transfer(Transfer { @@ -1351,7 +1347,7 @@ fn doesnt_import_blocks_that_revert_finality() { block_on(client.import(BlockOrigin::Own, b1.clone())).unwrap(); let b2 = client - .new_block_at(&BlockId::Hash(b1.hash()), Default::default(), false) + .new_block_at(b1.hash(), Default::default(), false) .unwrap() .build() .unwrap() @@ -1361,7 +1357,7 @@ fn doesnt_import_blocks_that_revert_finality() { // prepare B3 before we finalize A2, because otherwise we won't be able to // read changes trie configuration after A2 is finalized let b3 = client - .new_block_at(&BlockId::Hash(b2.hash()), Default::default(), false) + .new_block_at(b2.hash(), Default::default(), false) .unwrap() .build() .unwrap() @@ -1379,7 +1375,9 @@ fn doesnt_import_blocks_that_revert_finality() { // adding a C1 block which is lower than the last finalized should also // fail (with a cheaper check that doesn't require checking ancestry). - let mut c1 = client.new_block_at(&BlockId::Number(0), Default::default(), false).unwrap(); + let mut c1 = client + .new_block_at(client.chain_info().genesis_hash, Default::default(), false) + .unwrap(); // needed to make sure C1 gets a different hash from A1 and B1 c1.push_transfer(Transfer { @@ -1398,7 +1396,7 @@ fn doesnt_import_blocks_that_revert_finality() { assert_eq!(import_err.to_string(), expected_err.to_string()); let a3 = client - .new_block_at(&BlockId::Hash(a2.hash()), Default::default(), false) + .new_block_at(a2.hash(), Default::default(), false) .unwrap() .build() .unwrap() @@ -1428,16 +1426,34 @@ fn respects_block_rules() { .build() }; + // test modus operandi: + // + // B[2] + // / + // G[0]--B[1] + // \ \ + // \ B'[2] + // \ + // B'[1] + // + // B[1] - block ok + // B'[1] - block not ok, added to block_rules::bad + // + // B[2] - block ok, correct fork for block height==2, added to block_rules::forks + // B'[2] - block not ok, (incorrect fork) + + // build B[1] let block_ok = client - .new_block_at(&BlockId::Number(0), Default::default(), false) + .new_block_at(client.chain_info().genesis_hash, Default::default(), false) .unwrap() .build() .unwrap() .block; + let block_ok_1_hash = block_ok.hash(); let params = BlockCheckParams { hash: block_ok.hash(), - number: 0, + number: 1, parent_hash: *block_ok.header().parent_hash(), allow_missing_state: false, allow_missing_parent: false, @@ -1445,15 +1461,16 @@ fn respects_block_rules() { }; assert_eq!(block_on(client.check_block(params)).unwrap(), ImportResult::imported(false)); - // this is 0x0d6d6612a10485370d9e085aeea7ec427fb3f34d961c6a816cdbe5cde2278864 - let mut block_not_ok = - client.new_block_at(&BlockId::Number(0), Default::default(), false).unwrap(); + // build B'[1] + let mut block_not_ok = client + .new_block_at(client.chain_info().genesis_hash, Default::default(), false) + .unwrap(); block_not_ok.push_storage_change(vec![0], Some(vec![1])).unwrap(); let block_not_ok = block_not_ok.build().unwrap().block; let params = BlockCheckParams { hash: block_not_ok.hash(), - number: 0, + number: 1, parent_hash: *block_not_ok.header().parent_hash(), allow_missing_state: false, allow_missing_parent: false, @@ -1468,34 +1485,35 @@ fn respects_block_rules() { // Now going to the fork block_on(client.import_as_final(BlockOrigin::Own, block_ok)).unwrap(); - // And check good fork - let mut block_ok = - client.new_block_at(&BlockId::Number(1), Default::default(), false).unwrap(); + // And check good fork (build B[2]) + let mut block_ok = client.new_block_at(block_ok_1_hash, Default::default(), false).unwrap(); block_ok.push_storage_change(vec![0], Some(vec![2])).unwrap(); let block_ok = block_ok.build().unwrap().block; + assert_eq!(*block_ok.header().number(), 2); let params = BlockCheckParams { hash: block_ok.hash(), - number: 1, + number: 2, parent_hash: *block_ok.header().parent_hash(), allow_missing_state: false, allow_missing_parent: false, import_existing: false, }; if record_only { - fork_rules.push((1, block_ok.hash())); + fork_rules.push((2, block_ok.hash())); } assert_eq!(block_on(client.check_block(params)).unwrap(), ImportResult::imported(false)); - // And now try bad fork + // And now try bad fork (build B'[2]) let mut block_not_ok = - client.new_block_at(&BlockId::Number(1), Default::default(), false).unwrap(); + client.new_block_at(block_ok_1_hash, Default::default(), false).unwrap(); block_not_ok.push_storage_change(vec![0], Some(vec![3])).unwrap(); let block_not_ok = block_not_ok.build().unwrap().block; + assert_eq!(*block_not_ok.header().number(), 2); let params = BlockCheckParams { hash: block_not_ok.hash(), - number: 1, + number: 2, parent_hash: *block_not_ok.header().parent_hash(), allow_missing_state: false, allow_missing_parent: false, @@ -1540,13 +1558,15 @@ fn returns_status_for_pruned_blocks() { let mut client = TestClientBuilder::with_backend(backend).build(); let a1 = client - .new_block_at(&BlockId::Number(0), Default::default(), false) + .new_block_at(client.chain_info().genesis_hash, Default::default(), false) .unwrap() .build() .unwrap() .block; - let mut b1 = client.new_block_at(&BlockId::Number(0), Default::default(), false).unwrap(); + let mut b1 = client + .new_block_at(client.chain_info().genesis_hash, Default::default(), false) + .unwrap(); // b1 is created, but not imported b1.push_transfer(Transfer { @@ -1582,7 +1602,7 @@ fn returns_status_for_pruned_blocks() { assert_eq!(client.block_status(check_block_a1.hash).unwrap(), BlockStatus::InChainWithState); let a2 = client - .new_block_at(&BlockId::Hash(a1.hash()), Default::default(), false) + .new_block_at(a1.hash(), Default::default(), false) .unwrap() .build() .unwrap() @@ -1610,7 +1630,7 @@ fn returns_status_for_pruned_blocks() { assert_eq!(client.block_status(check_block_a2.hash).unwrap(), BlockStatus::InChainWithState); let a3 = client - .new_block_at(&BlockId::Hash(a2.hash()), Default::default(), false) + .new_block_at(a2.hash(), Default::default(), false) .unwrap() .build() .unwrap() @@ -1907,7 +1927,7 @@ fn reorg_triggers_a_notification_even_for_sources_that_should_not_trigger_notifi futures::executor::block_on_stream(client.import_notification_stream()); let a1 = client - .new_block_at(&BlockId::Number(0), Default::default(), false) + .new_block_at(client.chain_info().genesis_hash, Default::default(), false) .unwrap() .build() .unwrap() @@ -1915,14 +1935,16 @@ fn reorg_triggers_a_notification_even_for_sources_that_should_not_trigger_notifi block_on(client.import(BlockOrigin::NetworkInitialSync, a1.clone())).unwrap(); let a2 = client - .new_block_at(&BlockId::Hash(a1.hash()), Default::default(), false) + .new_block_at(a1.hash(), Default::default(), false) .unwrap() .build() .unwrap() .block; block_on(client.import(BlockOrigin::NetworkInitialSync, a2.clone())).unwrap(); - let mut b1 = client.new_block_at(&BlockId::Number(0), Default::default(), false).unwrap(); + let mut b1 = client + .new_block_at(client.chain_info().genesis_hash, Default::default(), false) + .unwrap(); // needed to make sure B1 gets a different hash from A1 b1.push_transfer(Transfer { from: AccountKeyring::Alice.into(), @@ -1935,7 +1957,7 @@ fn reorg_triggers_a_notification_even_for_sources_that_should_not_trigger_notifi block_on(client.import(BlockOrigin::NetworkInitialSync, b1.clone())).unwrap(); let b2 = client - .new_block_at(&BlockId::Hash(b1.hash()), Default::default(), false) + .new_block_at(b1.hash(), Default::default(), false) .unwrap() .build() .unwrap() @@ -1971,7 +1993,7 @@ fn use_dalek_ext_works() { ); let a1 = client - .new_block_at(&BlockId::Number(0), Default::default(), false) + .new_block_at(client.chain_info().genesis_hash, Default::default(), false) .unwrap() .build() .unwrap() diff --git a/primitives/api/test/tests/runtime_calls.rs b/primitives/api/test/tests/runtime_calls.rs index 4f88d887c..feb3e1a6e 100644 --- a/primitives/api/test/tests/runtime_calls.rs +++ b/primitives/api/test/tests/runtime_calls.rs @@ -16,10 +16,7 @@ // limitations under the License. use sp_api::{Core, ProvideRuntimeApi}; -use sp_runtime::{ - generic::BlockId, - traits::{HashFor, Header as HeaderT}, -}; +use sp_runtime::traits::{HashFor, Header as HeaderT}; use sp_state_machine::{ create_proof_check_backend, execution_proof_check_on_trie_backend, ExecutionStrategy, }; @@ -166,10 +163,9 @@ fn record_proof_works() { } .into_signed_tx(); - let block_id = BlockId::Hash(client.chain_info().best_hash); // Build the block and record proof let mut builder = client - .new_block_at(&block_id, Default::default(), true) + .new_block_at(client.chain_info().best_hash, Default::default(), true) .expect("Creates block builder"); builder.push(transaction.clone()).unwrap(); let (block, _, proof) = builder.build().expect("Bake block").into_inner(); diff --git a/test-utils/runtime/client/src/trait_tests.rs b/test-utils/runtime/client/src/trait_tests.rs index 19772c8c9..7ec56635e 100644 --- a/test-utils/runtime/client/src/trait_tests.rs +++ b/test-utils/runtime/client/src/trait_tests.rs @@ -32,7 +32,7 @@ use sc_client_api::{ blockchain::{Backend as BlockChainBackendT, HeaderBackend}, }; use sp_consensus::BlockOrigin; -use sp_runtime::{generic::BlockId, traits::Block as BlockT}; +use sp_runtime::traits::Block as BlockT; use substrate_test_runtime::{self, Transfer}; /// helper to test the `leaves` implementation for various backends @@ -60,7 +60,7 @@ where // A1 -> A2 let a2 = client - .new_block_at(&BlockId::Hash(a1.hash()), Default::default(), false) + .new_block_at(a1.hash(), Default::default(), false) .unwrap() .build() .unwrap() @@ -71,7 +71,7 @@ where // A2 -> A3 let a3 = client - .new_block_at(&BlockId::Hash(a2.hash()), Default::default(), false) + .new_block_at(a2.hash(), Default::default(), false) .unwrap() .build() .unwrap() @@ -82,7 +82,7 @@ where // A3 -> A4 let a4 = client - .new_block_at(&BlockId::Hash(a3.hash()), Default::default(), false) + .new_block_at(a3.hash(), Default::default(), false) .unwrap() .build() .unwrap() @@ -92,7 +92,7 @@ where // A4 -> A5 let a5 = client - .new_block_at(&BlockId::Hash(a4.hash()), Default::default(), false) + .new_block_at(a4.hash(), Default::default(), false) .unwrap() .build() .unwrap() @@ -102,9 +102,7 @@ where assert_eq!(blockchain.leaves().unwrap(), vec![a5.hash()]); // A1 -> B2 - let mut builder = client - .new_block_at(&BlockId::Hash(a1.hash()), Default::default(), false) - .unwrap(); + let mut builder = client.new_block_at(a1.hash(), Default::default(), false).unwrap(); // this push is required as otherwise B2 has the same hash as A2 and won't get imported builder @@ -121,7 +119,7 @@ where // B2 -> B3 let b3 = client - .new_block_at(&BlockId::Hash(b2.hash()), Default::default(), false) + .new_block_at(b2.hash(), Default::default(), false) .unwrap() .build() .unwrap() @@ -132,7 +130,7 @@ where // B3 -> B4 let b4 = client - .new_block_at(&BlockId::Hash(b3.hash()), Default::default(), false) + .new_block_at(b3.hash(), Default::default(), false) .unwrap() .build() .unwrap() @@ -141,9 +139,7 @@ where assert_eq!(blockchain.leaves().unwrap(), vec![a5.hash(), b4.hash()]); // // B2 -> C3 - let mut builder = client - .new_block_at(&BlockId::Hash(b2.hash()), Default::default(), false) - .unwrap(); + let mut builder = client.new_block_at(b2.hash(), Default::default(), false).unwrap(); // this push is required as otherwise C3 has the same hash as B3 and won't get imported builder .push_transfer(Transfer { @@ -158,9 +154,7 @@ where assert_eq!(blockchain.leaves().unwrap(), vec![a5.hash(), b4.hash(), c3.hash()]); // A1 -> D2 - let mut builder = client - .new_block_at(&BlockId::Hash(a1.hash()), Default::default(), false) - .unwrap(); + let mut builder = client.new_block_at(a1.hash(), Default::default(), false).unwrap(); // this push is required as otherwise D2 has the same hash as B2 and won't get imported builder .push_transfer(Transfer { @@ -195,7 +189,7 @@ where // A1 -> A2 let a2 = client - .new_block_at(&BlockId::Hash(a1.hash()), Default::default(), false) + .new_block_at(a1.hash(), Default::default(), false) .unwrap() .build() .unwrap() @@ -204,7 +198,7 @@ where // A2 -> A3 let a3 = client - .new_block_at(&BlockId::Hash(a2.hash()), Default::default(), false) + .new_block_at(a2.hash(), Default::default(), false) .unwrap() .build() .unwrap() @@ -213,7 +207,7 @@ where // A3 -> A4 let a4 = client - .new_block_at(&BlockId::Hash(a3.hash()), Default::default(), false) + .new_block_at(a3.hash(), Default::default(), false) .unwrap() .build() .unwrap() @@ -222,7 +216,7 @@ where // A4 -> A5 let a5 = client - .new_block_at(&BlockId::Hash(a4.hash()), Default::default(), false) + .new_block_at(a4.hash(), Default::default(), false) .unwrap() .build() .unwrap() @@ -230,9 +224,7 @@ where block_on(client.import(BlockOrigin::Own, a5.clone())).unwrap(); // A1 -> B2 - let mut builder = client - .new_block_at(&BlockId::Hash(a1.hash()), Default::default(), false) - .unwrap(); + let mut builder = client.new_block_at(a1.hash(), Default::default(), false).unwrap(); // this push is required as otherwise B2 has the same hash as A2 and won't get imported builder .push_transfer(Transfer { @@ -247,7 +239,7 @@ where // B2 -> B3 let b3 = client - .new_block_at(&BlockId::Hash(b2.hash()), Default::default(), false) + .new_block_at(b2.hash(), Default::default(), false) .unwrap() .build() .unwrap() @@ -256,7 +248,7 @@ where // B3 -> B4 let b4 = client - .new_block_at(&BlockId::Hash(b3.hash()), Default::default(), false) + .new_block_at(b3.hash(), Default::default(), false) .unwrap() .build() .unwrap() @@ -264,9 +256,7 @@ where block_on(client.import(BlockOrigin::Own, b4)).unwrap(); // // B2 -> C3 - let mut builder = client - .new_block_at(&BlockId::Hash(b2.hash()), Default::default(), false) - .unwrap(); + let mut builder = client.new_block_at(b2.hash(), Default::default(), false).unwrap(); // this push is required as otherwise C3 has the same hash as B3 and won't get imported builder .push_transfer(Transfer { @@ -280,9 +270,7 @@ where block_on(client.import(BlockOrigin::Own, c3.clone())).unwrap(); // A1 -> D2 - let mut builder = client - .new_block_at(&BlockId::Hash(a1.hash()), Default::default(), false) - .unwrap(); + let mut builder = client.new_block_at(a1.hash(), Default::default(), false).unwrap(); // this push is required as otherwise D2 has the same hash as B2 and won't get imported builder .push_transfer(Transfer { @@ -328,7 +316,7 @@ where // A1 -> A2 let a2 = client - .new_block_at(&BlockId::Hash(a1.hash()), Default::default(), false) + .new_block_at(a1.hash(), Default::default(), false) .unwrap() .build() .unwrap() @@ -337,7 +325,7 @@ where // A2 -> A3 let a3 = client - .new_block_at(&BlockId::Hash(a2.hash()), Default::default(), false) + .new_block_at(a2.hash(), Default::default(), false) .unwrap() .build() .unwrap() @@ -346,7 +334,7 @@ where // A3 -> A4 let a4 = client - .new_block_at(&BlockId::Hash(a3.hash()), Default::default(), false) + .new_block_at(a3.hash(), Default::default(), false) .unwrap() .build() .unwrap() @@ -355,7 +343,7 @@ where // A4 -> A5 let a5 = client - .new_block_at(&BlockId::Hash(a4.hash()), Default::default(), false) + .new_block_at(a4.hash(), Default::default(), false) .unwrap() .build() .unwrap() @@ -363,9 +351,7 @@ where block_on(client.import(BlockOrigin::Own, a5.clone())).unwrap(); // A1 -> B2 - let mut builder = client - .new_block_at(&BlockId::Hash(a1.hash()), Default::default(), false) - .unwrap(); + let mut builder = client.new_block_at(a1.hash(), Default::default(), false).unwrap(); // this push is required as otherwise B2 has the same hash as A2 and won't get imported builder .push_transfer(Transfer { @@ -380,7 +366,7 @@ where // B2 -> B3 let b3 = client - .new_block_at(&BlockId::Hash(b2.hash()), Default::default(), false) + .new_block_at(b2.hash(), Default::default(), false) .unwrap() .build() .unwrap() @@ -389,7 +375,7 @@ where // B3 -> B4 let b4 = client - .new_block_at(&BlockId::Hash(b3.hash()), Default::default(), false) + .new_block_at(b3.hash(), Default::default(), false) .unwrap() .build() .unwrap() @@ -397,9 +383,7 @@ where block_on(client.import(BlockOrigin::Own, b4)).unwrap(); // // B2 -> C3 - let mut builder = client - .new_block_at(&BlockId::Hash(b2.hash()), Default::default(), false) - .unwrap(); + let mut builder = client.new_block_at(b2.hash(), Default::default(), false).unwrap(); // this push is required as otherwise C3 has the same hash as B3 and won't get imported builder .push_transfer(Transfer { @@ -413,9 +397,7 @@ where block_on(client.import(BlockOrigin::Own, c3)).unwrap(); // A1 -> D2 - let mut builder = client - .new_block_at(&BlockId::Hash(a1.hash()), Default::default(), false) - .unwrap(); + let mut builder = client.new_block_at(a1.hash(), Default::default(), false).unwrap(); // this push is required as otherwise D2 has the same hash as B2 and won't get imported builder .push_transfer(Transfer { From e374a33fe1d99d59eb24a08981090bdb4503e81b Mon Sep 17 00:00:00 2001 From: Vivek Pandya Date: Wed, 22 Feb 2023 00:16:41 +0530 Subject: [PATCH 147/558] Remove years from copyright notes. (#13415) * Change copyright year to 2023 from 2022 * Fix incorrect update of copyright year * Remove years from copy right header * Fix remaining files * Fix typo in a header and remove update-copyright.sh --- .maintain/update-copyright.sh | 14 -------------- HEADER-APACHE2 | 2 +- HEADER-GPL3 | 2 +- bin/node/bench/src/common.rs | 2 +- bin/node/bench/src/construct.rs | 2 +- bin/node/bench/src/core.rs | 2 +- bin/node/bench/src/generator.rs | 2 +- bin/node/bench/src/import.rs | 2 +- bin/node/bench/src/main.rs | 2 +- bin/node/bench/src/simple_trie.rs | 2 +- bin/node/bench/src/state_sizes.rs | 2 +- bin/node/bench/src/tempdb.rs | 2 +- bin/node/bench/src/trie.rs | 2 +- bin/node/bench/src/txpool.rs | 2 +- bin/node/cli/benches/block_production.rs | 2 +- bin/node/cli/benches/transaction_pool.rs | 2 +- bin/node/cli/bin/main.rs | 2 +- bin/node/cli/build.rs | 2 +- bin/node/cli/src/benchmarking.rs | 2 +- bin/node/cli/src/chain_spec.rs | 2 +- bin/node/cli/src/cli.rs | 2 +- bin/node/cli/src/command.rs | 2 +- bin/node/cli/src/lib.rs | 2 +- bin/node/cli/src/service.rs | 2 +- bin/node/cli/tests/benchmark_block_works.rs | 2 +- bin/node/cli/tests/benchmark_extrinsic_works.rs | 2 +- bin/node/cli/tests/benchmark_machine_works.rs | 2 +- bin/node/cli/tests/benchmark_overhead_works.rs | 2 +- bin/node/cli/tests/benchmark_pallet_works.rs | 2 +- bin/node/cli/tests/benchmark_storage_works.rs | 2 +- bin/node/cli/tests/build_spec_works.rs | 2 +- bin/node/cli/tests/check_block_works.rs | 2 +- bin/node/cli/tests/common.rs | 2 +- bin/node/cli/tests/export_import_flow.rs | 2 +- bin/node/cli/tests/inspect_works.rs | 2 +- bin/node/cli/tests/purge_chain_works.rs | 2 +- bin/node/cli/tests/remember_state_pruning_works.rs | 2 +- .../cli/tests/running_the_node_and_interrupt.rs | 2 +- bin/node/cli/tests/telemetry.rs | 2 +- bin/node/cli/tests/temp_base_path_works.rs | 2 +- bin/node/cli/tests/version.rs | 2 +- bin/node/cli/tests/websocket_server.rs | 2 +- bin/node/executor/benches/bench.rs | 2 +- bin/node/executor/src/lib.rs | 2 +- bin/node/executor/tests/basic.rs | 2 +- bin/node/executor/tests/common.rs | 2 +- bin/node/executor/tests/fees.rs | 2 +- bin/node/executor/tests/submit_transaction.rs | 2 +- bin/node/inspect/src/cli.rs | 2 +- bin/node/inspect/src/command.rs | 2 +- bin/node/inspect/src/lib.rs | 2 +- bin/node/primitives/src/lib.rs | 2 +- bin/node/rpc/src/lib.rs | 2 +- bin/node/runtime/build.rs | 2 +- bin/node/runtime/src/constants.rs | 2 +- bin/node/runtime/src/impls.rs | 2 +- bin/node/runtime/src/lib.rs | 2 +- bin/node/runtime/src/voter_bags.rs | 2 +- bin/node/testing/src/bench.rs | 2 +- bin/node/testing/src/client.rs | 2 +- bin/node/testing/src/genesis.rs | 2 +- bin/node/testing/src/keyring.rs | 2 +- bin/node/testing/src/lib.rs | 2 +- bin/utils/chain-spec-builder/build.rs | 2 +- bin/utils/chain-spec-builder/src/main.rs | 2 +- bin/utils/subkey/src/lib.rs | 2 +- bin/utils/subkey/src/main.rs | 2 +- client/allocator/src/error.rs | 2 +- client/allocator/src/freeing_bump.rs | 2 +- client/allocator/src/lib.rs | 2 +- client/api/src/backend.rs | 2 +- client/api/src/call_executor.rs | 2 +- client/api/src/client.rs | 2 +- client/api/src/execution_extensions.rs | 2 +- client/api/src/in_mem.rs | 2 +- client/api/src/leaves.rs | 2 +- client/api/src/lib.rs | 2 +- client/api/src/notifications.rs | 2 +- client/api/src/notifications/registry.rs | 2 +- client/api/src/notifications/tests.rs | 2 +- client/api/src/proof_provider.rs | 2 +- client/authority-discovery/src/error.rs | 2 +- client/authority-discovery/src/interval.rs | 2 +- client/authority-discovery/src/lib.rs | 2 +- client/authority-discovery/src/service.rs | 2 +- client/authority-discovery/src/tests.rs | 2 +- client/authority-discovery/src/worker.rs | 2 +- .../authority-discovery/src/worker/addr_cache.rs | 2 +- .../authority-discovery/src/worker/schema/tests.rs | 2 +- client/authority-discovery/src/worker/tests.rs | 2 +- client/basic-authorship/src/basic_authorship.rs | 2 +- client/basic-authorship/src/lib.rs | 2 +- client/beefy/rpc/src/lib.rs | 2 +- client/beefy/rpc/src/notification.rs | 2 +- client/beefy/src/aux_schema.rs | 2 +- client/beefy/src/communication/gossip.rs | 2 +- client/beefy/src/communication/mod.rs | 2 +- client/beefy/src/communication/notification.rs | 2 +- client/beefy/src/communication/peers.rs | 2 +- .../request_response/incoming_requests_handler.rs | 2 +- .../src/communication/request_response/mod.rs | 2 +- .../request_response/outgoing_requests_engine.rs | 2 +- client/beefy/src/error.rs | 2 +- client/beefy/src/import.rs | 2 +- client/beefy/src/justification.rs | 2 +- client/beefy/src/keystore.rs | 2 +- client/beefy/src/lib.rs | 2 +- client/beefy/src/metrics.rs | 2 +- client/beefy/src/round.rs | 2 +- client/beefy/src/tests.rs | 2 +- client/beefy/src/worker.rs | 2 +- client/block-builder/src/lib.rs | 2 +- client/chain-spec/derive/src/impls.rs | 2 +- client/chain-spec/derive/src/lib.rs | 2 +- client/chain-spec/src/chain_spec.rs | 2 +- client/chain-spec/src/extension.rs | 2 +- client/chain-spec/src/lib.rs | 2 +- client/cli/src/arg_enums.rs | 2 +- client/cli/src/commands/build_spec_cmd.rs | 2 +- client/cli/src/commands/chain_info_cmd.rs | 2 +- client/cli/src/commands/check_block_cmd.rs | 2 +- client/cli/src/commands/export_blocks_cmd.rs | 2 +- client/cli/src/commands/export_state_cmd.rs | 2 +- client/cli/src/commands/generate.rs | 2 +- client/cli/src/commands/generate_node_key.rs | 2 +- client/cli/src/commands/import_blocks_cmd.rs | 2 +- client/cli/src/commands/insert_key.rs | 2 +- client/cli/src/commands/inspect_key.rs | 2 +- client/cli/src/commands/inspect_node_key.rs | 2 +- client/cli/src/commands/key.rs | 2 +- client/cli/src/commands/mod.rs | 2 +- client/cli/src/commands/purge_chain_cmd.rs | 2 +- client/cli/src/commands/revert_cmd.rs | 2 +- client/cli/src/commands/run_cmd.rs | 2 +- client/cli/src/commands/sign.rs | 2 +- client/cli/src/commands/test/mod.rs | 2 +- client/cli/src/commands/test/sig_verify.rs | 2 +- client/cli/src/commands/utils.rs | 2 +- client/cli/src/commands/vanity.rs | 2 +- client/cli/src/commands/verify.rs | 2 +- client/cli/src/config.rs | 2 +- client/cli/src/error.rs | 2 +- client/cli/src/lib.rs | 2 +- client/cli/src/params/database_params.rs | 2 +- client/cli/src/params/import_params.rs | 2 +- client/cli/src/params/keystore_params.rs | 2 +- client/cli/src/params/message_params.rs | 2 +- client/cli/src/params/mod.rs | 2 +- client/cli/src/params/network_params.rs | 2 +- client/cli/src/params/node_key_params.rs | 2 +- client/cli/src/params/offchain_worker_params.rs | 2 +- client/cli/src/params/pruning_params.rs | 2 +- client/cli/src/params/shared_params.rs | 2 +- client/cli/src/params/transaction_pool_params.rs | 2 +- client/cli/src/runner.rs | 2 +- client/consensus/aura/src/import_queue.rs | 2 +- client/consensus/aura/src/lib.rs | 2 +- client/consensus/babe/rpc/src/lib.rs | 2 +- client/consensus/babe/src/authorship.rs | 2 +- client/consensus/babe/src/aux_schema.rs | 2 +- client/consensus/babe/src/lib.rs | 2 +- client/consensus/babe/src/migration.rs | 2 +- client/consensus/babe/src/tests.rs | 2 +- client/consensus/babe/src/verification.rs | 2 +- client/consensus/common/src/block_import.rs | 2 +- client/consensus/common/src/import_queue.rs | 2 +- .../common/src/import_queue/basic_queue.rs | 2 +- .../common/src/import_queue/buffered_link.rs | 2 +- client/consensus/common/src/import_queue/mock.rs | 2 +- client/consensus/common/src/lib.rs | 2 +- client/consensus/common/src/longest_chain.rs | 2 +- client/consensus/common/src/metrics.rs | 2 +- client/consensus/common/src/shared_data.rs | 2 +- client/consensus/epochs/src/lib.rs | 2 +- client/consensus/epochs/src/migration.rs | 2 +- client/consensus/manual-seal/src/consensus.rs | 2 +- client/consensus/manual-seal/src/consensus/aura.rs | 2 +- client/consensus/manual-seal/src/consensus/babe.rs | 2 +- .../manual-seal/src/consensus/timestamp.rs | 2 +- client/consensus/manual-seal/src/error.rs | 2 +- client/consensus/manual-seal/src/finalize_block.rs | 2 +- client/consensus/manual-seal/src/lib.rs | 2 +- client/consensus/manual-seal/src/rpc.rs | 2 +- client/consensus/manual-seal/src/seal_block.rs | 2 +- client/consensus/pow/src/lib.rs | 2 +- client/consensus/pow/src/worker.rs | 2 +- client/consensus/slots/build.rs | 2 +- client/consensus/slots/src/aux_schema.rs | 2 +- client/consensus/slots/src/lib.rs | 2 +- client/consensus/slots/src/slots.rs | 2 +- client/db/benches/state_access.rs | 2 +- client/db/src/bench.rs | 2 +- client/db/src/children.rs | 2 +- client/db/src/lib.rs | 2 +- client/db/src/offchain.rs | 2 +- client/db/src/parity_db.rs | 2 +- client/db/src/pinned_blocks_cache.rs | 2 +- client/db/src/record_stats_state.rs | 2 +- client/db/src/stats.rs | 2 +- client/db/src/upgrade.rs | 2 +- client/db/src/utils.rs | 2 +- client/executor/benches/bench.rs | 2 +- client/executor/common/src/error.rs | 2 +- client/executor/common/src/lib.rs | 2 +- .../src/runtime_blob/data_segments_snapshot.rs | 2 +- .../common/src/runtime_blob/globals_snapshot.rs | 2 +- client/executor/common/src/runtime_blob/mod.rs | 2 +- .../common/src/runtime_blob/runtime_blob.rs | 2 +- client/executor/common/src/util.rs | 2 +- client/executor/common/src/wasm_runtime.rs | 2 +- client/executor/runtime-test/build.rs | 2 +- client/executor/src/integration_tests/linux.rs | 2 +- .../executor/src/integration_tests/linux/smaps.rs | 2 +- client/executor/src/integration_tests/mod.rs | 2 +- client/executor/src/lib.rs | 2 +- client/executor/src/native_executor.rs | 2 +- client/executor/src/wasm_runtime.rs | 2 +- client/executor/wasmi/src/lib.rs | 2 +- client/executor/wasmtime/build.rs | 2 +- client/executor/wasmtime/src/host.rs | 2 +- client/executor/wasmtime/src/imports.rs | 2 +- client/executor/wasmtime/src/instance_wrapper.rs | 2 +- client/executor/wasmtime/src/lib.rs | 2 +- client/executor/wasmtime/src/runtime.rs | 2 +- client/executor/wasmtime/src/tests.rs | 2 +- client/executor/wasmtime/src/util.rs | 2 +- client/finality-grandpa/rpc/src/error.rs | 2 +- client/finality-grandpa/rpc/src/finality.rs | 2 +- client/finality-grandpa/rpc/src/lib.rs | 2 +- client/finality-grandpa/rpc/src/notification.rs | 2 +- client/finality-grandpa/rpc/src/report.rs | 2 +- client/finality-grandpa/src/authorities.rs | 2 +- client/finality-grandpa/src/aux_schema.rs | 2 +- .../finality-grandpa/src/communication/gossip.rs | 2 +- client/finality-grandpa/src/communication/mod.rs | 2 +- .../finality-grandpa/src/communication/periodic.rs | 2 +- client/finality-grandpa/src/communication/tests.rs | 2 +- client/finality-grandpa/src/environment.rs | 2 +- client/finality-grandpa/src/finality_proof.rs | 2 +- client/finality-grandpa/src/import.rs | 2 +- client/finality-grandpa/src/justification.rs | 2 +- client/finality-grandpa/src/lib.rs | 2 +- client/finality-grandpa/src/notification.rs | 2 +- client/finality-grandpa/src/observer.rs | 2 +- client/finality-grandpa/src/tests.rs | 2 +- client/finality-grandpa/src/until_imported.rs | 2 +- client/finality-grandpa/src/voting_rule.rs | 2 +- client/finality-grandpa/src/warp_proof.rs | 2 +- client/informant/src/display.rs | 2 +- client/informant/src/lib.rs | 2 +- client/keystore/src/lib.rs | 2 +- client/keystore/src/local.rs | 2 +- client/merkle-mountain-range/rpc/src/lib.rs | 2 +- client/merkle-mountain-range/src/aux_schema.rs | 2 +- client/merkle-mountain-range/src/lib.rs | 2 +- client/merkle-mountain-range/src/offchain_mmr.rs | 2 +- client/merkle-mountain-range/src/test_utils.rs | 2 +- client/network-gossip/src/bridge.rs | 2 +- client/network-gossip/src/lib.rs | 2 +- client/network-gossip/src/state_machine.rs | 2 +- client/network-gossip/src/validator.rs | 2 +- client/network/bitswap/src/lib.rs | 2 +- client/network/bitswap/src/schema.rs | 2 +- client/network/common/src/config.rs | 2 +- client/network/common/src/error.rs | 2 +- client/network/common/src/lib.rs | 2 +- client/network/common/src/message.rs | 2 +- client/network/common/src/protocol.rs | 2 +- client/network/common/src/protocol/event.rs | 2 +- client/network/common/src/protocol/role.rs | 2 +- client/network/common/src/request_responses.rs | 2 +- client/network/common/src/service.rs | 2 +- client/network/common/src/service/signature.rs | 2 +- client/network/common/src/sync.rs | 2 +- client/network/common/src/sync/message.rs | 2 +- client/network/common/src/sync/metrics.rs | 2 +- client/network/common/src/sync/warp.rs | 2 +- client/network/common/src/utils.rs | 2 +- client/network/light/src/lib.rs | 2 +- client/network/light/src/light_client_requests.rs | 2 +- .../light/src/light_client_requests/handler.rs | 2 +- client/network/light/src/schema.rs | 2 +- client/network/src/behaviour.rs | 2 +- client/network/src/config.rs | 2 +- client/network/src/discovery.rs | 2 +- client/network/src/lib.rs | 2 +- client/network/src/network_state.rs | 2 +- client/network/src/peer_info.rs | 2 +- client/network/src/protocol.rs | 2 +- client/network/src/protocol/message.rs | 2 +- client/network/src/protocol/notifications.rs | 2 +- .../src/protocol/notifications/behaviour.rs | 2 +- .../network/src/protocol/notifications/handler.rs | 2 +- client/network/src/protocol/notifications/tests.rs | 2 +- .../network/src/protocol/notifications/upgrade.rs | 2 +- .../src/protocol/notifications/upgrade/collec.rs | 2 +- .../notifications/upgrade/notifications.rs | 2 +- client/network/src/request_responses.rs | 2 +- client/network/src/service.rs | 2 +- client/network/src/service/metrics.rs | 2 +- client/network/src/service/out_events.rs | 2 +- client/network/src/service/tests/chain_sync.rs | 2 +- client/network/src/service/tests/mod.rs | 2 +- client/network/src/service/tests/service.rs | 2 +- client/network/src/transport.rs | 2 +- client/network/sync/src/block_request_handler.rs | 2 +- client/network/sync/src/blocks.rs | 2 +- client/network/sync/src/extra_requests.rs | 2 +- client/network/sync/src/lib.rs | 2 +- client/network/sync/src/mock.rs | 2 +- client/network/sync/src/schema.rs | 2 +- client/network/sync/src/service/chain_sync.rs | 2 +- client/network/sync/src/service/mock.rs | 2 +- client/network/sync/src/service/mod.rs | 2 +- client/network/sync/src/service/network.rs | 2 +- client/network/sync/src/state.rs | 2 +- client/network/sync/src/state_request_handler.rs | 2 +- client/network/sync/src/tests.rs | 2 +- client/network/sync/src/warp.rs | 2 +- client/network/sync/src/warp_request_handler.rs | 2 +- client/network/test/src/block_import.rs | 2 +- client/network/test/src/lib.rs | 2 +- client/network/test/src/sync.rs | 2 +- client/network/transactions/src/config.rs | 2 +- client/network/transactions/src/lib.rs | 2 +- client/offchain/src/api.rs | 2 +- client/offchain/src/api/http.rs | 2 +- client/offchain/src/api/timestamp.rs | 2 +- client/offchain/src/lib.rs | 2 +- client/peerset/src/lib.rs | 2 +- client/peerset/src/peersstate.rs | 2 +- client/peerset/tests/fuzz.rs | 2 +- client/proposer-metrics/src/lib.rs | 2 +- client/rpc-api/src/author/error.rs | 2 +- client/rpc-api/src/author/hash.rs | 2 +- client/rpc-api/src/author/mod.rs | 2 +- client/rpc-api/src/chain/error.rs | 2 +- client/rpc-api/src/chain/mod.rs | 2 +- client/rpc-api/src/child_state/mod.rs | 2 +- client/rpc-api/src/dev/error.rs | 2 +- client/rpc-api/src/dev/mod.rs | 2 +- client/rpc-api/src/lib.rs | 2 +- client/rpc-api/src/offchain/error.rs | 2 +- client/rpc-api/src/offchain/mod.rs | 2 +- client/rpc-api/src/policy.rs | 2 +- client/rpc-api/src/state/error.rs | 2 +- client/rpc-api/src/state/helpers.rs | 2 +- client/rpc-api/src/state/mod.rs | 2 +- client/rpc-api/src/system/error.rs | 2 +- client/rpc-api/src/system/helpers.rs | 2 +- client/rpc-api/src/system/mod.rs | 2 +- client/rpc-servers/src/lib.rs | 2 +- client/rpc-servers/src/middleware.rs | 2 +- client/rpc-spec-v2/src/chain_head/api.rs | 2 +- client/rpc-spec-v2/src/chain_head/chain_head.rs | 2 +- client/rpc-spec-v2/src/chain_head/error.rs | 2 +- client/rpc-spec-v2/src/chain_head/event.rs | 2 +- client/rpc-spec-v2/src/chain_head/mod.rs | 2 +- client/rpc-spec-v2/src/chain_head/subscription.rs | 2 +- client/rpc-spec-v2/src/chain_spec/api.rs | 2 +- client/rpc-spec-v2/src/chain_spec/chain_spec.rs | 2 +- client/rpc-spec-v2/src/chain_spec/mod.rs | 2 +- client/rpc-spec-v2/src/chain_spec/tests.rs | 2 +- client/rpc-spec-v2/src/lib.rs | 2 +- client/rpc-spec-v2/src/transaction/api.rs | 2 +- client/rpc-spec-v2/src/transaction/error.rs | 2 +- client/rpc-spec-v2/src/transaction/event.rs | 2 +- client/rpc-spec-v2/src/transaction/mod.rs | 2 +- client/rpc-spec-v2/src/transaction/transaction.rs | 2 +- client/rpc/src/author/mod.rs | 2 +- client/rpc/src/author/tests.rs | 2 +- client/rpc/src/chain/chain_full.rs | 2 +- client/rpc/src/chain/mod.rs | 2 +- client/rpc/src/chain/tests.rs | 2 +- client/rpc/src/dev/mod.rs | 2 +- client/rpc/src/dev/tests.rs | 2 +- client/rpc/src/lib.rs | 2 +- client/rpc/src/offchain/mod.rs | 2 +- client/rpc/src/offchain/tests.rs | 2 +- client/rpc/src/state/mod.rs | 2 +- client/rpc/src/state/state_full.rs | 2 +- client/rpc/src/state/tests.rs | 2 +- client/rpc/src/state/utils.rs | 2 +- client/rpc/src/system/mod.rs | 2 +- client/rpc/src/system/tests.rs | 2 +- client/rpc/src/testing.rs | 2 +- client/service/src/builder.rs | 2 +- client/service/src/chain_ops/check_block.rs | 2 +- client/service/src/chain_ops/export_blocks.rs | 2 +- client/service/src/chain_ops/export_raw_state.rs | 2 +- client/service/src/chain_ops/import_blocks.rs | 2 +- client/service/src/chain_ops/mod.rs | 2 +- client/service/src/chain_ops/revert_chain.rs | 2 +- client/service/src/client/block_rules.rs | 2 +- client/service/src/client/call_executor.rs | 2 +- client/service/src/client/client.rs | 2 +- client/service/src/client/genesis.rs | 2 +- client/service/src/client/mod.rs | 2 +- client/service/src/client/wasm_override.rs | 2 +- client/service/src/client/wasm_substitutes.rs | 2 +- client/service/src/config.rs | 2 +- client/service/src/error.rs | 2 +- client/service/src/lib.rs | 2 +- client/service/src/metrics.rs | 2 +- client/service/src/task_manager/mod.rs | 2 +- .../service/src/task_manager/prometheus_future.rs | 2 +- client/service/src/task_manager/tests.rs | 2 +- client/service/test/src/client/db.rs | 2 +- client/service/test/src/client/mod.rs | 2 +- client/service/test/src/lib.rs | 2 +- client/state-db/src/lib.rs | 2 +- client/state-db/src/noncanonical.rs | 2 +- client/state-db/src/pruning.rs | 2 +- client/state-db/src/test.rs | 2 +- client/storage-monitor/src/lib.rs | 2 +- client/sync-state-rpc/src/lib.rs | 2 +- client/sysinfo/build.rs | 2 +- client/sysinfo/src/lib.rs | 2 +- client/sysinfo/src/sysinfo.rs | 2 +- client/sysinfo/src/sysinfo_linux.rs | 2 +- client/telemetry/src/endpoints.rs | 2 +- client/telemetry/src/error.rs | 2 +- client/telemetry/src/lib.rs | 2 +- client/telemetry/src/node.rs | 2 +- client/telemetry/src/transport.rs | 2 +- client/tracing/benches/bench.rs | 2 +- client/tracing/proc-macro/src/lib.rs | 2 +- client/tracing/src/block/mod.rs | 2 +- client/tracing/src/lib.rs | 2 +- client/tracing/src/logging/directives.rs | 2 +- client/tracing/src/logging/event_format.rs | 2 +- client/tracing/src/logging/fast_local_time.rs | 2 +- client/tracing/src/logging/layers/mod.rs | 2 +- client/tracing/src/logging/layers/prefix_layer.rs | 2 +- client/tracing/src/logging/mod.rs | 2 +- client/tracing/src/logging/stderr_writer.rs | 2 +- client/transaction-pool/api/src/error.rs | 2 +- client/transaction-pool/api/src/lib.rs | 2 +- client/transaction-pool/benches/basics.rs | 2 +- client/transaction-pool/src/api.rs | 2 +- client/transaction-pool/src/enactment_state.rs | 2 +- client/transaction-pool/src/error.rs | 2 +- client/transaction-pool/src/graph/base_pool.rs | 2 +- client/transaction-pool/src/graph/future.rs | 2 +- client/transaction-pool/src/graph/listener.rs | 2 +- client/transaction-pool/src/graph/mod.rs | 2 +- client/transaction-pool/src/graph/pool.rs | 2 +- client/transaction-pool/src/graph/ready.rs | 2 +- client/transaction-pool/src/graph/rotator.rs | 2 +- client/transaction-pool/src/graph/tracked_map.rs | 2 +- .../transaction-pool/src/graph/validated_pool.rs | 2 +- client/transaction-pool/src/graph/watcher.rs | 2 +- client/transaction-pool/src/lib.rs | 2 +- client/transaction-pool/src/metrics.rs | 2 +- client/transaction-pool/src/revalidation.rs | 2 +- client/transaction-pool/src/tests.rs | 2 +- client/transaction-pool/tests/pool.rs | 2 +- client/utils/src/id_sequence.rs | 2 +- client/utils/src/lib.rs | 2 +- client/utils/src/metrics.rs | 2 +- client/utils/src/mpsc.rs | 2 +- client/utils/src/notification.rs | 2 +- client/utils/src/notification/registry.rs | 2 +- client/utils/src/notification/tests.rs | 2 +- client/utils/src/pubsub.rs | 2 +- client/utils/src/pubsub/tests.rs | 2 +- client/utils/src/pubsub/tests/normal_operation.rs | 2 +- .../utils/src/pubsub/tests/panicking_registry.rs | 2 +- client/utils/src/status_sinks.rs | 2 +- frame/alliance/src/benchmarking.rs | 2 +- frame/alliance/src/lib.rs | 2 +- frame/alliance/src/migration.rs | 2 +- frame/alliance/src/mock.rs | 2 +- frame/alliance/src/tests.rs | 2 +- frame/alliance/src/types.rs | 2 +- frame/alliance/src/weights.rs | 2 +- frame/assets/src/benchmarking.rs | 2 +- frame/assets/src/extra_mutator.rs | 2 +- frame/assets/src/functions.rs | 2 +- frame/assets/src/impl_fungibles.rs | 2 +- frame/assets/src/impl_stored_map.rs | 2 +- frame/assets/src/lib.rs | 2 +- frame/assets/src/migration.rs | 2 +- frame/assets/src/mock.rs | 2 +- frame/assets/src/tests.rs | 2 +- frame/assets/src/types.rs | 2 +- frame/assets/src/weights.rs | 2 +- frame/atomic-swap/src/lib.rs | 2 +- frame/aura/src/lib.rs | 2 +- frame/aura/src/migrations.rs | 2 +- frame/aura/src/mock.rs | 2 +- frame/aura/src/tests.rs | 2 +- frame/authority-discovery/src/lib.rs | 2 +- frame/authorship/src/lib.rs | 2 +- frame/babe/src/benchmarking.rs | 2 +- frame/babe/src/default_weights.rs | 2 +- frame/babe/src/equivocation.rs | 2 +- frame/babe/src/lib.rs | 2 +- frame/babe/src/mock.rs | 2 +- frame/babe/src/randomness.rs | 2 +- frame/babe/src/tests.rs | 2 +- frame/bags-list/fuzzer/src/main.rs | 2 +- frame/bags-list/remote-tests/src/lib.rs | 2 +- frame/bags-list/remote-tests/src/migration.rs | 2 +- frame/bags-list/remote-tests/src/snapshot.rs | 2 +- frame/bags-list/remote-tests/src/try_state.rs | 2 +- frame/bags-list/src/benchmarks.rs | 2 +- frame/bags-list/src/lib.rs | 2 +- frame/bags-list/src/list/mod.rs | 2 +- frame/bags-list/src/list/tests.rs | 2 +- frame/bags-list/src/migrations.rs | 2 +- frame/bags-list/src/mock.rs | 2 +- frame/bags-list/src/tests.rs | 2 +- frame/bags-list/src/weights.rs | 2 +- frame/balances/src/benchmarking.rs | 2 +- frame/balances/src/lib.rs | 2 +- frame/balances/src/migration.rs | 2 +- frame/balances/src/tests.rs | 2 +- frame/balances/src/tests_composite.rs | 2 +- frame/balances/src/tests_local.rs | 2 +- frame/balances/src/tests_reentrancy.rs | 2 +- frame/balances/src/weights.rs | 2 +- frame/beefy-mmr/src/lib.rs | 2 +- frame/beefy-mmr/src/mock.rs | 2 +- frame/beefy-mmr/src/tests.rs | 2 +- frame/beefy/src/default_weights.rs | 2 +- frame/beefy/src/equivocation.rs | 2 +- frame/beefy/src/lib.rs | 2 +- frame/beefy/src/mock.rs | 2 +- frame/beefy/src/tests.rs | 2 +- frame/benchmarking/pov/src/benchmarking.rs | 2 +- frame/benchmarking/pov/src/lib.rs | 2 +- frame/benchmarking/pov/src/tests.rs | 2 +- frame/benchmarking/src/analysis.rs | 2 +- frame/benchmarking/src/baseline.rs | 2 +- frame/benchmarking/src/lib.rs | 2 +- frame/benchmarking/src/tests.rs | 2 +- frame/benchmarking/src/tests_instance.rs | 2 +- frame/benchmarking/src/utils.rs | 2 +- frame/benchmarking/src/v1.rs | 2 +- frame/benchmarking/src/weights.rs | 2 +- frame/bounties/src/benchmarking.rs | 2 +- frame/bounties/src/lib.rs | 2 +- frame/bounties/src/migrations/mod.rs | 2 +- frame/bounties/src/migrations/v4.rs | 2 +- frame/bounties/src/tests.rs | 2 +- frame/bounties/src/weights.rs | 2 +- frame/child-bounties/src/benchmarking.rs | 2 +- frame/child-bounties/src/lib.rs | 2 +- frame/child-bounties/src/tests.rs | 2 +- frame/child-bounties/src/weights.rs | 2 +- frame/collective/src/benchmarking.rs | 2 +- frame/collective/src/lib.rs | 2 +- frame/collective/src/migrations/mod.rs | 2 +- frame/collective/src/migrations/v4.rs | 2 +- frame/collective/src/tests.rs | 2 +- frame/collective/src/weights.rs | 2 +- frame/contracts/primitives/src/lib.rs | 2 +- frame/contracts/proc-macro/src/lib.rs | 2 +- frame/contracts/src/address.rs | 2 +- frame/contracts/src/benchmarking/code.rs | 2 +- frame/contracts/src/benchmarking/mod.rs | 2 +- frame/contracts/src/benchmarking/sandbox.rs | 2 +- frame/contracts/src/chain_extension.rs | 2 +- frame/contracts/src/exec.rs | 2 +- frame/contracts/src/gas.rs | 2 +- frame/contracts/src/lib.rs | 2 +- frame/contracts/src/migration.rs | 2 +- frame/contracts/src/schedule.rs | 2 +- frame/contracts/src/storage.rs | 2 +- frame/contracts/src/storage/meter.rs | 2 +- frame/contracts/src/tests.rs | 2 +- frame/contracts/src/wasm/code_cache.rs | 2 +- frame/contracts/src/wasm/mod.rs | 2 +- frame/contracts/src/wasm/prepare.rs | 2 +- frame/contracts/src/wasm/runtime.rs | 2 +- frame/contracts/src/weights.rs | 2 +- frame/conviction-voting/src/benchmarking.rs | 2 +- frame/conviction-voting/src/conviction.rs | 2 +- frame/conviction-voting/src/lib.rs | 2 +- frame/conviction-voting/src/tests.rs | 2 +- frame/conviction-voting/src/types.rs | 2 +- frame/conviction-voting/src/vote.rs | 2 +- frame/conviction-voting/src/weights.rs | 2 +- frame/democracy/src/benchmarking.rs | 2 +- frame/democracy/src/conviction.rs | 2 +- frame/democracy/src/lib.rs | 2 +- frame/democracy/src/migrations.rs | 2 +- frame/democracy/src/tests.rs | 2 +- frame/democracy/src/tests/cancellation.rs | 2 +- frame/democracy/src/tests/decoders.rs | 2 +- frame/democracy/src/tests/delegation.rs | 2 +- frame/democracy/src/tests/external_proposing.rs | 2 +- frame/democracy/src/tests/fast_tracking.rs | 2 +- frame/democracy/src/tests/lock_voting.rs | 2 +- frame/democracy/src/tests/metadata.rs | 2 +- frame/democracy/src/tests/public_proposals.rs | 2 +- frame/democracy/src/tests/scheduling.rs | 2 +- frame/democracy/src/tests/voting.rs | 2 +- frame/democracy/src/types.rs | 2 +- frame/democracy/src/vote.rs | 2 +- frame/democracy/src/vote_threshold.rs | 2 +- frame/democracy/src/weights.rs | 2 +- .../src/benchmarking.rs | 2 +- frame/election-provider-multi-phase/src/helpers.rs | 2 +- frame/election-provider-multi-phase/src/lib.rs | 2 +- .../src/migrations.rs | 2 +- frame/election-provider-multi-phase/src/mock.rs | 2 +- frame/election-provider-multi-phase/src/signed.rs | 2 +- .../election-provider-multi-phase/src/unsigned.rs | 2 +- frame/election-provider-multi-phase/src/weights.rs | 2 +- .../benchmarking/src/lib.rs | 2 +- .../solution-type/src/codec.rs | 2 +- .../solution-type/src/from_assignment_helpers.rs | 2 +- .../solution-type/src/index_assignment.rs | 2 +- .../solution-type/src/lib.rs | 2 +- .../solution-type/src/single_page.rs | 2 +- frame/election-provider-support/src/lib.rs | 2 +- frame/election-provider-support/src/mock.rs | 2 +- frame/election-provider-support/src/onchain.rs | 2 +- frame/election-provider-support/src/tests.rs | 2 +- frame/election-provider-support/src/traits.rs | 2 +- frame/election-provider-support/src/weights.rs | 2 +- frame/elections-phragmen/src/benchmarking.rs | 2 +- frame/elections-phragmen/src/lib.rs | 2 +- frame/elections-phragmen/src/migrations/mod.rs | 2 +- frame/elections-phragmen/src/migrations/v3.rs | 2 +- frame/elections-phragmen/src/migrations/v4.rs | 2 +- frame/elections-phragmen/src/weights.rs | 2 +- frame/examples/basic/src/benchmarking.rs | 2 +- frame/examples/basic/src/lib.rs | 2 +- frame/examples/basic/src/tests.rs | 2 +- frame/examples/basic/src/weights.rs | 2 +- frame/examples/offchain-worker/src/lib.rs | 2 +- frame/examples/offchain-worker/src/tests.rs | 2 +- frame/executive/src/lib.rs | 2 +- frame/fast-unstake/src/benchmarking.rs | 2 +- frame/fast-unstake/src/lib.rs | 2 +- frame/fast-unstake/src/migrations.rs | 2 +- frame/fast-unstake/src/mock.rs | 2 +- frame/fast-unstake/src/tests.rs | 2 +- frame/fast-unstake/src/types.rs | 2 +- frame/fast-unstake/src/weights.rs | 2 +- frame/grandpa/src/benchmarking.rs | 2 +- frame/grandpa/src/default_weights.rs | 2 +- frame/grandpa/src/equivocation.rs | 2 +- frame/grandpa/src/lib.rs | 2 +- frame/grandpa/src/migrations.rs | 2 +- frame/grandpa/src/migrations/v4.rs | 2 +- frame/grandpa/src/mock.rs | 2 +- frame/grandpa/src/tests.rs | 2 +- frame/identity/src/benchmarking.rs | 2 +- frame/identity/src/lib.rs | 2 +- frame/identity/src/tests.rs | 2 +- frame/identity/src/types.rs | 2 +- frame/identity/src/weights.rs | 2 +- frame/im-online/src/benchmarking.rs | 2 +- frame/im-online/src/lib.rs | 2 +- frame/im-online/src/mock.rs | 2 +- frame/im-online/src/tests.rs | 2 +- frame/im-online/src/weights.rs | 2 +- frame/indices/src/benchmarking.rs | 2 +- frame/indices/src/lib.rs | 2 +- frame/indices/src/mock.rs | 2 +- frame/indices/src/tests.rs | 2 +- frame/indices/src/weights.rs | 2 +- .../insecure-randomness-collective-flip/src/lib.rs | 2 +- frame/lottery/src/benchmarking.rs | 2 +- frame/lottery/src/lib.rs | 2 +- frame/lottery/src/mock.rs | 2 +- frame/lottery/src/tests.rs | 2 +- frame/lottery/src/weights.rs | 2 +- frame/membership/src/lib.rs | 2 +- frame/membership/src/migrations/mod.rs | 2 +- frame/membership/src/migrations/v4.rs | 2 +- frame/membership/src/weights.rs | 2 +- frame/merkle-mountain-range/src/benchmarking.rs | 2 +- frame/merkle-mountain-range/src/default_weights.rs | 2 +- frame/merkle-mountain-range/src/lib.rs | 2 +- frame/merkle-mountain-range/src/mmr/mmr.rs | 2 +- frame/merkle-mountain-range/src/mmr/mod.rs | 2 +- frame/merkle-mountain-range/src/mmr/storage.rs | 2 +- frame/merkle-mountain-range/src/mock.rs | 2 +- frame/merkle-mountain-range/src/tests.rs | 2 +- frame/message-queue/src/benchmarking.rs | 2 +- frame/message-queue/src/integration_test.rs | 2 +- frame/message-queue/src/lib.rs | 2 +- frame/message-queue/src/mock.rs | 2 +- frame/message-queue/src/mock_helpers.rs | 2 +- frame/message-queue/src/tests.rs | 2 +- frame/message-queue/src/weights.rs | 2 +- frame/multisig/src/benchmarking.rs | 2 +- frame/multisig/src/lib.rs | 2 +- frame/multisig/src/migrations.rs | 2 +- frame/multisig/src/tests.rs | 2 +- frame/multisig/src/weights.rs | 2 +- frame/nfts/src/benchmarking.rs | 2 +- frame/nfts/src/common_functions.rs | 2 +- frame/nfts/src/features/approvals.rs | 2 +- frame/nfts/src/features/atomic_swap.rs | 2 +- frame/nfts/src/features/attributes.rs | 2 +- frame/nfts/src/features/buy_sell.rs | 2 +- .../nfts/src/features/create_delete_collection.rs | 2 +- frame/nfts/src/features/create_delete_item.rs | 2 +- frame/nfts/src/features/lock.rs | 2 +- frame/nfts/src/features/metadata.rs | 2 +- frame/nfts/src/features/mod.rs | 2 +- frame/nfts/src/features/roles.rs | 2 +- frame/nfts/src/features/settings.rs | 2 +- frame/nfts/src/features/transfer.rs | 2 +- frame/nfts/src/impl_nonfungibles.rs | 2 +- frame/nfts/src/lib.rs | 2 +- frame/nfts/src/macros.rs | 2 +- frame/nfts/src/mock.rs | 2 +- frame/nfts/src/tests.rs | 2 +- frame/nfts/src/types.rs | 2 +- frame/nfts/src/weights.rs | 2 +- frame/nicks/src/lib.rs | 2 +- frame/nis/src/benchmarking.rs | 2 +- frame/nis/src/lib.rs | 2 +- frame/nis/src/mock.rs | 2 +- frame/nis/src/tests.rs | 2 +- frame/nis/src/weights.rs | 2 +- frame/node-authorization/src/lib.rs | 2 +- frame/node-authorization/src/mock.rs | 2 +- frame/node-authorization/src/tests.rs | 2 +- frame/node-authorization/src/weights.rs | 2 +- frame/nomination-pools/benchmarking/src/lib.rs | 2 +- frame/nomination-pools/benchmarking/src/mock.rs | 2 +- frame/nomination-pools/fuzzer/src/call.rs | 2 +- frame/nomination-pools/runtime-api/src/lib.rs | 2 +- frame/nomination-pools/src/lib.rs | 2 +- frame/nomination-pools/src/migration.rs | 2 +- frame/nomination-pools/src/tests.rs | 2 +- frame/nomination-pools/src/weights.rs | 2 +- frame/nomination-pools/test-staking/src/lib.rs | 2 +- frame/nomination-pools/test-staking/src/mock.rs | 2 +- frame/offences/benchmarking/src/lib.rs | 2 +- frame/offences/benchmarking/src/mock.rs | 2 +- frame/offences/src/lib.rs | 2 +- frame/offences/src/migration.rs | 2 +- frame/offences/src/mock.rs | 2 +- frame/offences/src/tests.rs | 2 +- frame/preimage/src/benchmarking.rs | 2 +- frame/preimage/src/lib.rs | 2 +- frame/preimage/src/migration.rs | 2 +- frame/preimage/src/mock.rs | 2 +- frame/preimage/src/tests.rs | 2 +- frame/preimage/src/weights.rs | 2 +- frame/proxy/src/benchmarking.rs | 2 +- frame/proxy/src/lib.rs | 2 +- frame/proxy/src/tests.rs | 2 +- frame/proxy/src/weights.rs | 2 +- frame/ranked-collective/src/benchmarking.rs | 2 +- frame/ranked-collective/src/lib.rs | 2 +- frame/ranked-collective/src/tests.rs | 2 +- frame/ranked-collective/src/weights.rs | 2 +- frame/recovery/src/benchmarking.rs | 2 +- frame/recovery/src/lib.rs | 2 +- frame/recovery/src/mock.rs | 2 +- frame/recovery/src/tests.rs | 2 +- frame/recovery/src/weights.rs | 2 +- frame/referenda/src/benchmarking.rs | 2 +- frame/referenda/src/branch.rs | 2 +- frame/referenda/src/lib.rs | 2 +- frame/referenda/src/migration.rs | 2 +- frame/referenda/src/mock.rs | 2 +- frame/referenda/src/tests.rs | 2 +- frame/referenda/src/types.rs | 2 +- frame/referenda/src/weights.rs | 2 +- frame/remark/src/benchmarking.rs | 2 +- frame/remark/src/lib.rs | 2 +- frame/remark/src/mock.rs | 2 +- frame/remark/src/tests.rs | 2 +- frame/remark/src/weights.rs | 2 +- frame/root-offences/src/lib.rs | 2 +- frame/root-offences/src/mock.rs | 2 +- frame/root-offences/src/tests.rs | 2 +- frame/root-testing/src/lib.rs | 2 +- frame/scheduler/src/benchmarking.rs | 2 +- frame/scheduler/src/lib.rs | 2 +- frame/scheduler/src/migration.rs | 2 +- frame/scheduler/src/mock.rs | 2 +- frame/scheduler/src/tests.rs | 2 +- frame/scheduler/src/weights.rs | 2 +- frame/scored-pool/src/lib.rs | 2 +- frame/scored-pool/src/mock.rs | 2 +- frame/scored-pool/src/tests.rs | 2 +- frame/session/benchmarking/src/lib.rs | 2 +- frame/session/benchmarking/src/mock.rs | 2 +- frame/session/src/historical/mod.rs | 2 +- frame/session/src/historical/offchain.rs | 2 +- frame/session/src/historical/onchain.rs | 2 +- frame/session/src/historical/shared.rs | 2 +- frame/session/src/lib.rs | 2 +- frame/session/src/migrations/mod.rs | 2 +- frame/session/src/migrations/v1.rs | 2 +- frame/session/src/mock.rs | 2 +- frame/session/src/tests.rs | 2 +- frame/session/src/weights.rs | 2 +- frame/society/src/lib.rs | 2 +- frame/society/src/mock.rs | 2 +- frame/society/src/tests.rs | 2 +- frame/staking/reward-curve/src/lib.rs | 2 +- frame/staking/reward-curve/tests/test.rs | 2 +- frame/staking/reward-fn/src/lib.rs | 2 +- frame/staking/reward-fn/tests/test.rs | 2 +- frame/staking/src/benchmarking.rs | 2 +- frame/staking/src/inflation.rs | 2 +- frame/staking/src/lib.rs | 2 +- frame/staking/src/migrations.rs | 2 +- frame/staking/src/mock.rs | 2 +- frame/staking/src/pallet/impls.rs | 2 +- frame/staking/src/pallet/mod.rs | 2 +- frame/staking/src/slashing.rs | 2 +- frame/staking/src/testing_utils.rs | 2 +- frame/staking/src/tests.rs | 2 +- frame/staking/src/weights.rs | 2 +- frame/state-trie-migration/src/lib.rs | 2 +- frame/state-trie-migration/src/weights.rs | 2 +- frame/sudo/src/extension.rs | 2 +- frame/sudo/src/lib.rs | 2 +- frame/sudo/src/mock.rs | 2 +- frame/sudo/src/tests.rs | 2 +- frame/support/procedural/src/benchmark.rs | 2 +- frame/support/procedural/src/clone_no_bound.rs | 2 +- .../src/construct_runtime/expand/call.rs | 2 +- .../src/construct_runtime/expand/config.rs | 2 +- .../src/construct_runtime/expand/event.rs | 2 +- .../src/construct_runtime/expand/inherent.rs | 2 +- .../src/construct_runtime/expand/metadata.rs | 2 +- .../procedural/src/construct_runtime/expand/mod.rs | 2 +- .../src/construct_runtime/expand/origin.rs | 2 +- .../src/construct_runtime/expand/unsigned.rs | 2 +- .../procedural/src/construct_runtime/mod.rs | 2 +- .../procedural/src/construct_runtime/parse.rs | 2 +- frame/support/procedural/src/crate_version.rs | 2 +- frame/support/procedural/src/debug_no_bound.rs | 2 +- frame/support/procedural/src/default_no_bound.rs | 2 +- frame/support/procedural/src/key_prefix.rs | 2 +- frame/support/procedural/src/lib.rs | 2 +- frame/support/procedural/src/match_and_insert.rs | 2 +- frame/support/procedural/src/pallet/expand/call.rs | 2 +- .../support/procedural/src/pallet/expand/config.rs | 2 +- .../procedural/src/pallet/expand/constants.rs | 2 +- .../support/procedural/src/pallet/expand/error.rs | 2 +- .../support/procedural/src/pallet/expand/event.rs | 2 +- .../procedural/src/pallet/expand/genesis_build.rs | 2 +- .../procedural/src/pallet/expand/genesis_config.rs | 2 +- .../support/procedural/src/pallet/expand/hooks.rs | 2 +- .../procedural/src/pallet/expand/inherent.rs | 2 +- .../procedural/src/pallet/expand/instances.rs | 2 +- frame/support/procedural/src/pallet/expand/mod.rs | 2 +- .../support/procedural/src/pallet/expand/origin.rs | 2 +- .../procedural/src/pallet/expand/pallet_struct.rs | 2 +- .../procedural/src/pallet/expand/storage.rs | 2 +- .../procedural/src/pallet/expand/store_trait.rs | 2 +- .../src/pallet/expand/tt_default_parts.rs | 2 +- .../procedural/src/pallet/expand/type_value.rs | 2 +- .../src/pallet/expand/validate_unsigned.rs | 2 +- frame/support/procedural/src/pallet/mod.rs | 2 +- frame/support/procedural/src/pallet/parse/call.rs | 2 +- .../support/procedural/src/pallet/parse/config.rs | 2 +- frame/support/procedural/src/pallet/parse/error.rs | 2 +- frame/support/procedural/src/pallet/parse/event.rs | 2 +- .../procedural/src/pallet/parse/extra_constants.rs | 2 +- .../procedural/src/pallet/parse/genesis_build.rs | 2 +- .../procedural/src/pallet/parse/genesis_config.rs | 2 +- .../support/procedural/src/pallet/parse/helper.rs | 2 +- frame/support/procedural/src/pallet/parse/hooks.rs | 2 +- .../procedural/src/pallet/parse/inherent.rs | 2 +- frame/support/procedural/src/pallet/parse/mod.rs | 2 +- .../support/procedural/src/pallet/parse/origin.rs | 2 +- .../procedural/src/pallet/parse/pallet_struct.rs | 2 +- .../support/procedural/src/pallet/parse/storage.rs | 2 +- .../procedural/src/pallet/parse/type_value.rs | 2 +- .../src/pallet/parse/validate_unsigned.rs | 2 +- frame/support/procedural/src/pallet_error.rs | 2 +- .../support/procedural/src/partial_eq_no_bound.rs | 2 +- .../src/storage/genesis_config/builder_def.rs | 2 +- .../storage/genesis_config/genesis_config_def.rs | 2 +- .../procedural/src/storage/genesis_config/mod.rs | 2 +- frame/support/procedural/src/storage/getters.rs | 2 +- .../procedural/src/storage/instance_trait.rs | 2 +- frame/support/procedural/src/storage/metadata.rs | 2 +- frame/support/procedural/src/storage/mod.rs | 2 +- frame/support/procedural/src/storage/parse.rs | 2 +- .../support/procedural/src/storage/storage_info.rs | 2 +- .../procedural/src/storage/storage_struct.rs | 2 +- .../support/procedural/src/storage/store_trait.rs | 2 +- frame/support/procedural/src/storage_alias.rs | 2 +- frame/support/procedural/src/transactional.rs | 2 +- frame/support/procedural/src/tt_macro.rs | 2 +- frame/support/procedural/tools/derive/src/lib.rs | 2 +- frame/support/procedural/tools/src/lib.rs | 2 +- frame/support/procedural/tools/src/syn_ext.rs | 2 +- frame/support/src/crypto.rs | 2 +- frame/support/src/crypto/ecdsa.rs | 2 +- frame/support/src/dispatch.rs | 2 +- frame/support/src/error.rs | 2 +- frame/support/src/event.rs | 2 +- frame/support/src/hash.rs | 2 +- frame/support/src/inherent.rs | 2 +- frame/support/src/instances.rs | 2 +- frame/support/src/lib.rs | 2 +- frame/support/src/migrations.rs | 2 +- frame/support/src/storage/bounded_btree_map.rs | 2 +- frame/support/src/storage/bounded_btree_set.rs | 2 +- frame/support/src/storage/bounded_vec.rs | 2 +- frame/support/src/storage/child.rs | 2 +- frame/support/src/storage/generator/double_map.rs | 2 +- frame/support/src/storage/generator/map.rs | 2 +- frame/support/src/storage/generator/mod.rs | 2 +- frame/support/src/storage/generator/nmap.rs | 2 +- frame/support/src/storage/generator/value.rs | 2 +- frame/support/src/storage/hashed.rs | 2 +- frame/support/src/storage/migration.rs | 2 +- frame/support/src/storage/mod.rs | 2 +- frame/support/src/storage/storage_noop_guard.rs | 2 +- frame/support/src/storage/stream_iter.rs | 2 +- frame/support/src/storage/transactional.rs | 2 +- frame/support/src/storage/types/counted_map.rs | 2 +- frame/support/src/storage/types/double_map.rs | 2 +- frame/support/src/storage/types/key.rs | 2 +- frame/support/src/storage/types/map.rs | 2 +- frame/support/src/storage/types/mod.rs | 2 +- frame/support/src/storage/types/nmap.rs | 2 +- frame/support/src/storage/types/value.rs | 2 +- frame/support/src/storage/unhashed.rs | 2 +- frame/support/src/storage/weak_bounded_vec.rs | 2 +- frame/support/src/traits.rs | 2 +- frame/support/src/traits/dispatch.rs | 2 +- frame/support/src/traits/error.rs | 2 +- frame/support/src/traits/filter.rs | 2 +- frame/support/src/traits/hooks.rs | 2 +- frame/support/src/traits/members.rs | 2 +- frame/support/src/traits/messages.rs | 2 +- frame/support/src/traits/metadata.rs | 2 +- frame/support/src/traits/misc.rs | 2 +- frame/support/src/traits/preimages.rs | 2 +- frame/support/src/traits/randomness.rs | 2 +- frame/support/src/traits/schedule.rs | 2 +- frame/support/src/traits/storage.rs | 2 +- frame/support/src/traits/stored_map.rs | 2 +- frame/support/src/traits/tokens.rs | 2 +- frame/support/src/traits/tokens/currency.rs | 2 +- .../support/src/traits/tokens/currency/lockable.rs | 2 +- .../src/traits/tokens/currency/reservable.rs | 2 +- frame/support/src/traits/tokens/fungible.rs | 2 +- .../support/src/traits/tokens/fungible/balanced.rs | 2 +- .../src/traits/tokens/fungible/imbalance.rs | 2 +- frame/support/src/traits/tokens/fungibles.rs | 2 +- .../src/traits/tokens/fungibles/approvals.rs | 2 +- .../src/traits/tokens/fungibles/balanced.rs | 2 +- .../src/traits/tokens/fungibles/enumerable.rs | 2 +- .../src/traits/tokens/fungibles/imbalance.rs | 2 +- .../src/traits/tokens/fungibles/metadata.rs | 2 +- frame/support/src/traits/tokens/fungibles/roles.rs | 2 +- frame/support/src/traits/tokens/imbalance.rs | 2 +- .../src/traits/tokens/imbalance/on_unbalanced.rs | 2 +- .../traits/tokens/imbalance/signed_imbalance.rs | 2 +- .../src/traits/tokens/imbalance/split_two_ways.rs | 2 +- frame/support/src/traits/tokens/misc.rs | 2 +- frame/support/src/traits/tokens/nonfungible.rs | 2 +- frame/support/src/traits/tokens/nonfungible_v2.rs | 2 +- frame/support/src/traits/tokens/nonfungibles.rs | 2 +- frame/support/src/traits/tokens/nonfungibles_v2.rs | 2 +- frame/support/src/traits/try_runtime.rs | 2 +- frame/support/src/traits/validation.rs | 2 +- frame/support/src/traits/voting.rs | 2 +- frame/support/src/weights.rs | 2 +- frame/support/src/weights/block_weights.rs | 2 +- frame/support/src/weights/extrinsic_weights.rs | 2 +- frame/support/src/weights/paritydb_weights.rs | 2 +- frame/support/src/weights/rocksdb_weights.rs | 2 +- frame/support/test/compile_pass/src/lib.rs | 2 +- frame/support/test/pallet/src/lib.rs | 2 +- frame/support/test/src/lib.rs | 2 +- frame/support/test/src/pallet_version.rs | 2 +- frame/support/test/tests/benchmark_ui.rs | 2 +- frame/support/test/tests/construct_runtime.rs | 2 +- frame/support/test/tests/construct_runtime_ui.rs | 2 +- frame/support/test/tests/decl_module_ui.rs | 2 +- frame/support/test/tests/decl_storage.rs | 2 +- frame/support/test/tests/decl_storage_ui.rs | 2 +- .../test/tests/decl_storage_ui/config_duplicate.rs | 2 +- .../tests/decl_storage_ui/config_get_duplicate.rs | 2 +- .../test/tests/decl_storage_ui/get_duplicate.rs | 2 +- frame/support/test/tests/derive_no_bound.rs | 2 +- frame/support/test/tests/derive_no_bound_ui.rs | 2 +- frame/support/test/tests/final_keys.rs | 2 +- frame/support/test/tests/genesisconfig.rs | 2 +- frame/support/test/tests/instance.rs | 2 +- frame/support/test/tests/issue2219.rs | 2 +- frame/support/test/tests/origin.rs | 2 +- frame/support/test/tests/pallet.rs | 2 +- frame/support/test/tests/pallet_compatibility.rs | 2 +- .../test/tests/pallet_compatibility_instance.rs | 2 +- frame/support/test/tests/pallet_instance.rs | 2 +- frame/support/test/tests/pallet_ui.rs | 2 +- .../test/tests/pallet_with_name_trait_is_valid.rs | 2 +- frame/support/test/tests/storage_alias_ui.rs | 2 +- frame/support/test/tests/storage_layers.rs | 2 +- frame/support/test/tests/storage_transaction.rs | 2 +- frame/support/test/tests/system.rs | 2 +- frame/system/benches/bench.rs | 2 +- frame/system/benchmarking/src/lib.rs | 2 +- frame/system/benchmarking/src/mock.rs | 2 +- frame/system/rpc/runtime-api/src/lib.rs | 2 +- frame/system/src/extensions/check_genesis.rs | 2 +- frame/system/src/extensions/check_mortality.rs | 2 +- .../system/src/extensions/check_non_zero_sender.rs | 2 +- frame/system/src/extensions/check_nonce.rs | 2 +- frame/system/src/extensions/check_spec_version.rs | 2 +- frame/system/src/extensions/check_tx_version.rs | 2 +- frame/system/src/extensions/check_weight.rs | 2 +- frame/system/src/extensions/mod.rs | 2 +- frame/system/src/lib.rs | 2 +- frame/system/src/limits.rs | 2 +- frame/system/src/migrations/mod.rs | 2 +- frame/system/src/mock.rs | 2 +- frame/system/src/mocking.rs | 2 +- frame/system/src/offchain.rs | 2 +- frame/system/src/tests.rs | 2 +- frame/system/src/weights.rs | 2 +- frame/timestamp/src/benchmarking.rs | 2 +- frame/timestamp/src/lib.rs | 2 +- frame/timestamp/src/mock.rs | 2 +- frame/timestamp/src/tests.rs | 2 +- frame/timestamp/src/weights.rs | 2 +- frame/tips/src/benchmarking.rs | 2 +- frame/tips/src/lib.rs | 2 +- frame/tips/src/migrations/mod.rs | 2 +- frame/tips/src/migrations/v4.rs | 2 +- frame/tips/src/tests.rs | 2 +- frame/tips/src/weights.rs | 2 +- .../asset-tx-payment/src/lib.rs | 2 +- .../asset-tx-payment/src/mock.rs | 2 +- .../asset-tx-payment/src/payment.rs | 2 +- .../asset-tx-payment/src/tests.rs | 2 +- .../transaction-payment/rpc/runtime-api/src/lib.rs | 2 +- frame/transaction-payment/rpc/src/lib.rs | 2 +- frame/transaction-payment/src/lib.rs | 2 +- frame/transaction-payment/src/mock.rs | 2 +- frame/transaction-payment/src/tests.rs | 2 +- frame/transaction-payment/src/types.rs | 2 +- frame/transaction-storage/src/benchmarking.rs | 2 +- frame/transaction-storage/src/lib.rs | 2 +- frame/transaction-storage/src/mock.rs | 2 +- frame/transaction-storage/src/tests.rs | 2 +- frame/transaction-storage/src/weights.rs | 2 +- frame/treasury/src/benchmarking.rs | 2 +- frame/treasury/src/lib.rs | 2 +- frame/treasury/src/tests.rs | 2 +- frame/treasury/src/weights.rs | 2 +- frame/try-runtime/src/lib.rs | 2 +- frame/uniques/src/benchmarking.rs | 2 +- frame/uniques/src/functions.rs | 2 +- frame/uniques/src/impl_nonfungibles.rs | 2 +- frame/uniques/src/lib.rs | 2 +- frame/uniques/src/migration.rs | 2 +- frame/uniques/src/mock.rs | 2 +- frame/uniques/src/tests.rs | 2 +- frame/uniques/src/types.rs | 2 +- frame/uniques/src/weights.rs | 2 +- frame/utility/src/benchmarking.rs | 2 +- frame/utility/src/lib.rs | 2 +- frame/utility/src/tests.rs | 2 +- frame/utility/src/weights.rs | 2 +- frame/vesting/src/benchmarking.rs | 2 +- frame/vesting/src/lib.rs | 2 +- frame/vesting/src/migrations.rs | 2 +- frame/vesting/src/mock.rs | 2 +- frame/vesting/src/tests.rs | 2 +- frame/vesting/src/vesting_info.rs | 2 +- frame/vesting/src/weights.rs | 2 +- frame/whitelist/src/benchmarking.rs | 2 +- frame/whitelist/src/lib.rs | 2 +- frame/whitelist/src/mock.rs | 2 +- frame/whitelist/src/tests.rs | 2 +- frame/whitelist/src/weights.rs | 2 +- primitives/api/proc-macro/src/common.rs | 2 +- primitives/api/proc-macro/src/decl_runtime_apis.rs | 2 +- primitives/api/proc-macro/src/impl_runtime_apis.rs | 2 +- primitives/api/proc-macro/src/lib.rs | 2 +- .../api/proc-macro/src/mock_impl_runtime_apis.rs | 2 +- primitives/api/proc-macro/src/utils.rs | 2 +- primitives/api/src/lib.rs | 2 +- primitives/api/test/benches/bench.rs | 2 +- primitives/api/test/tests/decl_and_impl.rs | 2 +- primitives/api/test/tests/runtime_calls.rs | 2 +- primitives/api/test/tests/trybuild.rs | 2 +- primitives/application-crypto/src/ecdsa.rs | 2 +- primitives/application-crypto/src/ed25519.rs | 2 +- primitives/application-crypto/src/lib.rs | 2 +- primitives/application-crypto/src/sr25519.rs | 2 +- primitives/application-crypto/src/traits.rs | 2 +- primitives/application-crypto/test/src/ecdsa.rs | 2 +- primitives/application-crypto/test/src/ed25519.rs | 2 +- primitives/application-crypto/test/src/lib.rs | 2 +- primitives/application-crypto/test/src/sr25519.rs | 2 +- primitives/arithmetic/benches/bench.rs | 2 +- primitives/arithmetic/fuzzer/src/biguint.rs | 2 +- primitives/arithmetic/fuzzer/src/fixed_point.rs | 2 +- .../src/multiply_by_rational_with_rounding.rs | 2 +- primitives/arithmetic/fuzzer/src/normalize.rs | 2 +- .../arithmetic/fuzzer/src/per_thing_rational.rs | 2 +- primitives/arithmetic/src/biguint.rs | 2 +- primitives/arithmetic/src/fixed_point.rs | 2 +- primitives/arithmetic/src/helpers_128bit.rs | 2 +- primitives/arithmetic/src/lib.rs | 2 +- primitives/arithmetic/src/per_things.rs | 2 +- primitives/arithmetic/src/rational.rs | 2 +- primitives/arithmetic/src/traits.rs | 2 +- primitives/authority-discovery/src/lib.rs | 2 +- primitives/beefy/src/commitment.rs | 2 +- primitives/beefy/src/lib.rs | 2 +- primitives/beefy/src/mmr.rs | 2 +- primitives/beefy/src/payload.rs | 2 +- primitives/beefy/src/test_utils.rs | 2 +- primitives/beefy/src/witness.rs | 2 +- primitives/block-builder/src/lib.rs | 2 +- primitives/blockchain/src/backend.rs | 2 +- primitives/blockchain/src/error.rs | 2 +- primitives/blockchain/src/header_metadata.rs | 2 +- primitives/blockchain/src/lib.rs | 2 +- primitives/consensus/aura/src/digests.rs | 2 +- primitives/consensus/aura/src/inherents.rs | 2 +- primitives/consensus/aura/src/lib.rs | 2 +- primitives/consensus/babe/src/digests.rs | 2 +- primitives/consensus/babe/src/inherents.rs | 2 +- primitives/consensus/babe/src/lib.rs | 2 +- .../consensus/common/src/block_validation.rs | 2 +- primitives/consensus/common/src/error.rs | 2 +- primitives/consensus/common/src/lib.rs | 2 +- primitives/consensus/common/src/select_chain.rs | 2 +- primitives/consensus/pow/src/lib.rs | 2 +- primitives/consensus/slots/src/lib.rs | 2 +- primitives/consensus/vrf/src/lib.rs | 2 +- primitives/consensus/vrf/src/schnorrkel.rs | 2 +- primitives/core/benches/bench.rs | 2 +- primitives/core/hashing/proc-macro/src/impls.rs | 2 +- primitives/core/hashing/proc-macro/src/lib.rs | 2 +- primitives/core/hashing/src/lib.rs | 2 +- primitives/core/src/bounded.rs | 2 +- primitives/core/src/crypto.rs | 2 +- primitives/core/src/defer.rs | 2 +- primitives/core/src/ecdsa.rs | 2 +- primitives/core/src/ed25519.rs | 2 +- primitives/core/src/hash.rs | 2 +- primitives/core/src/hasher.rs | 2 +- primitives/core/src/hashing.rs | 2 +- primitives/core/src/hexdisplay.rs | 2 +- primitives/core/src/lib.rs | 2 +- primitives/core/src/offchain/mod.rs | 2 +- primitives/core/src/offchain/storage.rs | 2 +- primitives/core/src/offchain/testing.rs | 2 +- primitives/core/src/sr25519.rs | 2 +- primitives/core/src/testing.rs | 2 +- primitives/core/src/traits.rs | 2 +- primitives/core/src/uint.rs | 2 +- primitives/database/src/error.rs | 2 +- primitives/database/src/kvdb.rs | 2 +- primitives/database/src/lib.rs | 2 +- primitives/database/src/mem.rs | 2 +- primitives/debug-derive/src/impls.rs | 2 +- primitives/debug-derive/src/lib.rs | 2 +- primitives/debug-derive/tests/tests.rs | 2 +- primitives/externalities/src/extensions.rs | 2 +- primitives/externalities/src/lib.rs | 2 +- primitives/externalities/src/scope_limited.rs | 2 +- primitives/finality-grandpa/src/lib.rs | 2 +- primitives/inherents/src/client_side.rs | 2 +- primitives/inherents/src/lib.rs | 2 +- primitives/io/src/batch_verifier.rs | 2 +- primitives/io/src/lib.rs | 2 +- primitives/keyring/src/ed25519.rs | 2 +- primitives/keyring/src/lib.rs | 2 +- primitives/keyring/src/sr25519.rs | 2 +- primitives/keystore/src/lib.rs | 2 +- primitives/keystore/src/testing.rs | 2 +- primitives/keystore/src/vrf.rs | 2 +- primitives/maybe-compressed-blob/src/lib.rs | 2 +- primitives/merkle-mountain-range/src/lib.rs | 2 +- primitives/merkle-mountain-range/src/utils.rs | 2 +- primitives/npos-elections/fuzzer/src/common.rs | 2 +- .../fuzzer/src/phragmen_balancing.rs | 2 +- .../npos-elections/fuzzer/src/phragmen_pjr.rs | 2 +- .../fuzzer/src/phragmms_balancing.rs | 2 +- primitives/npos-elections/fuzzer/src/reduce.rs | 2 +- primitives/npos-elections/src/assignments.rs | 2 +- primitives/npos-elections/src/balancing.rs | 2 +- primitives/npos-elections/src/helpers.rs | 2 +- primitives/npos-elections/src/lib.rs | 3 ++- primitives/npos-elections/src/mock.rs | 2 +- primitives/npos-elections/src/node.rs | 2 +- primitives/npos-elections/src/phragmen.rs | 2 +- primitives/npos-elections/src/phragmms.rs | 2 +- primitives/npos-elections/src/pjr.rs | 2 +- primitives/npos-elections/src/reduce.rs | 2 +- primitives/npos-elections/src/tests.rs | 2 +- primitives/npos-elections/src/traits.rs | 2 +- primitives/offchain/src/lib.rs | 2 +- primitives/panic-handler/src/lib.rs | 2 +- primitives/rpc/src/lib.rs | 2 +- primitives/rpc/src/list.rs | 2 +- primitives/rpc/src/number.rs | 2 +- primitives/rpc/src/tracing.rs | 2 +- primitives/runtime-interface/proc-macro/src/lib.rs | 2 +- .../proc-macro/src/pass_by/codec.rs | 2 +- .../proc-macro/src/pass_by/enum_.rs | 2 +- .../proc-macro/src/pass_by/inner.rs | 2 +- .../proc-macro/src/pass_by/mod.rs | 2 +- .../runtime_interface/bare_function_interface.rs | 2 +- .../runtime_interface/host_function_interface.rs | 2 +- .../proc-macro/src/runtime_interface/mod.rs | 2 +- .../src/runtime_interface/trait_decl_impl.rs | 2 +- .../runtime-interface/proc-macro/src/utils.rs | 2 +- primitives/runtime-interface/src/host.rs | 2 +- primitives/runtime-interface/src/impls.rs | 2 +- primitives/runtime-interface/src/lib.rs | 2 +- primitives/runtime-interface/src/pass_by.rs | 2 +- primitives/runtime-interface/src/util.rs | 2 +- primitives/runtime-interface/src/wasm.rs | 2 +- .../test-wasm-deprecated/build.rs | 2 +- .../test-wasm-deprecated/src/lib.rs | 2 +- primitives/runtime-interface/test-wasm/build.rs | 2 +- primitives/runtime-interface/test-wasm/src/lib.rs | 2 +- primitives/runtime-interface/test/src/lib.rs | 2 +- primitives/runtime-interface/tests/ui.rs | 2 +- primitives/runtime/src/curve.rs | 2 +- primitives/runtime/src/generic/block.rs | 2 +- .../runtime/src/generic/checked_extrinsic.rs | 2 +- primitives/runtime/src/generic/digest.rs | 2 +- primitives/runtime/src/generic/era.rs | 2 +- primitives/runtime/src/generic/header.rs | 2 +- primitives/runtime/src/generic/mod.rs | 2 +- primitives/runtime/src/generic/tests.rs | 2 +- .../runtime/src/generic/unchecked_extrinsic.rs | 2 +- primitives/runtime/src/legacy.rs | 2 +- primitives/runtime/src/legacy/byte_sized_error.rs | 2 +- primitives/runtime/src/lib.rs | 2 +- primitives/runtime/src/multiaddress.rs | 2 +- primitives/runtime/src/offchain/http.rs | 2 +- primitives/runtime/src/offchain/mod.rs | 2 +- primitives/runtime/src/offchain/storage.rs | 2 +- primitives/runtime/src/offchain/storage_lock.rs | 2 +- primitives/runtime/src/runtime_logger.rs | 2 +- primitives/runtime/src/runtime_string.rs | 2 +- primitives/runtime/src/testing.rs | 2 +- primitives/runtime/src/traits.rs | 2 +- primitives/runtime/src/transaction_validity.rs | 2 +- primitives/serializer/src/lib.rs | 2 +- primitives/session/src/lib.rs | 2 +- primitives/staking/src/lib.rs | 2 +- primitives/staking/src/offence.rs | 2 +- primitives/state-machine/src/backend.rs | 2 +- primitives/state-machine/src/basic.rs | 2 +- primitives/state-machine/src/error.rs | 2 +- primitives/state-machine/src/ext.rs | 2 +- primitives/state-machine/src/in_memory_backend.rs | 2 +- primitives/state-machine/src/lib.rs | 2 +- .../src/overlayed_changes/changeset.rs | 2 +- .../state-machine/src/overlayed_changes/mod.rs | 2 +- .../src/overlayed_changes/offchain.rs | 2 +- primitives/state-machine/src/read_only.rs | 2 +- primitives/state-machine/src/stats.rs | 2 +- primitives/state-machine/src/testing.rs | 2 +- primitives/state-machine/src/trie_backend.rs | 2 +- .../state-machine/src/trie_backend_essence.rs | 2 +- primitives/std/src/lib.rs | 2 +- primitives/std/with_std.rs | 2 +- primitives/std/without_std.rs | 2 +- primitives/storage/src/lib.rs | 2 +- primitives/test-primitives/src/lib.rs | 2 +- primitives/timestamp/src/lib.rs | 2 +- primitives/tracing/src/lib.rs | 2 +- primitives/tracing/src/types.rs | 2 +- primitives/transaction-pool/src/lib.rs | 2 +- primitives/transaction-pool/src/runtime_api.rs | 2 +- primitives/transaction-storage-proof/src/lib.rs | 2 +- primitives/trie/benches/bench.rs | 2 +- primitives/trie/src/cache/mod.rs | 2 +- primitives/trie/src/cache/shared_cache.rs | 2 +- primitives/trie/src/error.rs | 2 +- primitives/trie/src/lib.rs | 2 +- primitives/trie/src/node_codec.rs | 2 +- primitives/trie/src/node_header.rs | 2 +- primitives/trie/src/recorder.rs | 2 +- primitives/trie/src/storage_proof.rs | 2 +- primitives/trie/src/trie_codec.rs | 2 +- primitives/trie/src/trie_stream.rs | 2 +- .../version/proc-macro/src/decl_runtime_version.rs | 2 +- primitives/version/proc-macro/src/lib.rs | 2 +- primitives/version/src/embed.rs | 2 +- primitives/version/src/lib.rs | 2 +- primitives/wasm-interface/src/lib.rs | 2 +- primitives/wasm-interface/src/wasmi_impl.rs | 2 +- primitives/weights/src/lib.rs | 2 +- primitives/weights/src/weight_meter.rs | 2 +- primitives/weights/src/weight_v2.rs | 2 +- scripts/run_all_benchmarks.sh | 2 +- test-utils/client/src/client_ext.rs | 2 +- test-utils/client/src/lib.rs | 2 +- test-utils/derive/src/lib.rs | 2 +- test-utils/runtime/build.rs | 2 +- test-utils/runtime/client/src/block_builder_ext.rs | 2 +- test-utils/runtime/client/src/lib.rs | 2 +- test-utils/runtime/client/src/trait_tests.rs | 2 +- test-utils/runtime/src/genesismap.rs | 2 +- test-utils/runtime/src/lib.rs | 2 +- test-utils/runtime/src/system.rs | 2 +- test-utils/runtime/transaction-pool/src/lib.rs | 2 +- test-utils/src/lib.rs | 2 +- test-utils/test-crate/src/main.rs | 2 +- test-utils/tests/basic.rs | 2 +- test-utils/tests/ui.rs | 2 +- test-utils/tests/ui/too-many-func-parameters.rs | 2 +- utils/binary-merkle-tree/src/lib.rs | 2 +- utils/build-script-utils/src/git.rs | 2 +- utils/build-script-utils/src/lib.rs | 2 +- utils/build-script-utils/src/version.rs | 2 +- utils/fork-tree/src/lib.rs | 2 +- utils/frame/benchmarking-cli/build.rs | 2 +- utils/frame/benchmarking-cli/src/block/bench.rs | 2 +- utils/frame/benchmarking-cli/src/block/cmd.rs | 2 +- utils/frame/benchmarking-cli/src/block/mod.rs | 2 +- .../frame/benchmarking-cli/src/extrinsic/bench.rs | 2 +- utils/frame/benchmarking-cli/src/extrinsic/cmd.rs | 2 +- .../src/extrinsic/extrinsic_factory.rs | 2 +- utils/frame/benchmarking-cli/src/extrinsic/mod.rs | 2 +- utils/frame/benchmarking-cli/src/lib.rs | 2 +- .../frame/benchmarking-cli/src/machine/hardware.rs | 2 +- utils/frame/benchmarking-cli/src/machine/mod.rs | 2 +- utils/frame/benchmarking-cli/src/overhead/cmd.rs | 2 +- utils/frame/benchmarking-cli/src/overhead/mod.rs | 2 +- .../benchmarking-cli/src/overhead/template.rs | 2 +- utils/frame/benchmarking-cli/src/pallet/command.rs | 2 +- utils/frame/benchmarking-cli/src/pallet/mod.rs | 2 +- utils/frame/benchmarking-cli/src/pallet/writer.rs | 2 +- utils/frame/benchmarking-cli/src/shared/mod.rs | 2 +- utils/frame/benchmarking-cli/src/shared/record.rs | 2 +- utils/frame/benchmarking-cli/src/shared/stats.rs | 2 +- .../benchmarking-cli/src/shared/weight_params.rs | 2 +- utils/frame/benchmarking-cli/src/storage/cmd.rs | 2 +- utils/frame/benchmarking-cli/src/storage/mod.rs | 2 +- utils/frame/benchmarking-cli/src/storage/read.rs | 2 +- .../frame/benchmarking-cli/src/storage/template.rs | 2 +- utils/frame/benchmarking-cli/src/storage/write.rs | 2 +- utils/frame/frame-utilities-cli/src/lib.rs | 2 +- utils/frame/frame-utilities-cli/src/pallet_id.rs | 2 +- utils/frame/generate-bags/node-runtime/src/main.rs | 2 +- utils/frame/generate-bags/src/lib.rs | 2 +- utils/frame/remote-externalities/src/lib.rs | 2 +- utils/frame/rpc/client/src/lib.rs | 2 +- .../frame/rpc/state-trie-migration-rpc/src/lib.rs | 2 +- utils/frame/rpc/support/src/lib.rs | 2 +- utils/frame/rpc/system/src/lib.rs | 2 +- .../try-runtime/cli/src/block_building_info.rs | 2 +- .../cli/src/commands/create_snapshot.rs | 2 +- .../try-runtime/cli/src/commands/execute_block.rs | 2 +- .../try-runtime/cli/src/commands/fast_forward.rs | 2 +- .../try-runtime/cli/src/commands/follow_chain.rs | 2 +- utils/frame/try-runtime/cli/src/commands/mod.rs | 2 +- .../cli/src/commands/offchain_worker.rs | 2 +- .../cli/src/commands/on_runtime_upgrade.rs | 2 +- utils/frame/try-runtime/cli/src/lib.rs | 2 +- utils/frame/try-runtime/cli/src/parse.rs | 2 +- utils/prometheus/src/lib.rs | 2 +- utils/prometheus/src/sourced.rs | 2 +- utils/wasm-builder/src/builder.rs | 2 +- utils/wasm-builder/src/lib.rs | 2 +- utils/wasm-builder/src/prerequisites.rs | 2 +- utils/wasm-builder/src/wasm_project.rs | 2 +- 1374 files changed, 1374 insertions(+), 1387 deletions(-) delete mode 100755 .maintain/update-copyright.sh diff --git a/.maintain/update-copyright.sh b/.maintain/update-copyright.sh deleted file mode 100755 index d67cab7c1..000000000 --- a/.maintain/update-copyright.sh +++ /dev/null @@ -1,14 +0,0 @@ -#!/usr/bin/env bash - -SINGLE_DATES=$(grep -lr "// Copyright (C) [0-9]* Parity Technologies (UK) Ltd.") -YEAR=$(date +%Y) - -for file in $SINGLE_DATES; do - FILE_YEAR=$(cat $file | sed -n "s|// Copyright (C) \([[:digit:]][[:digit:]][[:digit:]][[:digit:]]\) Parity Technologies (UK) Ltd.|\1|p") - if [ $YEAR -ne $FILE_YEAR ]; then - sed -i -e "s|// Copyright (C) \([[:digit:]][[:digit:]][[:digit:]][[:digit:]]\) Parity Technologies (UK) Ltd.|// Copyright (C) \1-$YEAR Parity Technologies (UK) Ltd.|g" $file - fi -done - -grep -lr "// Copyright (C) [0-9]*-[0-9]* Parity Technologies (UK) Ltd." | - xargs sed -i -e "s|// Copyright (C) \([[:digit:]][[:digit:]][[:digit:]][[:digit:]]\)-[[:digit:]][[:digit:]][[:digit:]][[:digit:]] Parity Technologies (UK) Ltd.|// Copyright (C) \1-$YEAR Parity Technologies (UK) Ltd.|g" diff --git a/HEADER-APACHE2 b/HEADER-APACHE2 index 403497a86..ecd364a6d 100644 --- a/HEADER-APACHE2 +++ b/HEADER-APACHE2 @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2023 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/HEADER-GPL3 b/HEADER-GPL3 index ed7daba72..b46a8f752 100644 --- a/HEADER-GPL3 +++ b/HEADER-GPL3 @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2023 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 // This program is free software: you can redistribute it and/or modify diff --git a/bin/node/bench/src/common.rs b/bin/node/bench/src/common.rs index 839e26f9f..46c9d0417 100644 --- a/bin/node/bench/src/common.rs +++ b/bin/node/bench/src/common.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2020-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 // This program is free software: you can redistribute it and/or modify diff --git a/bin/node/bench/src/construct.rs b/bin/node/bench/src/construct.rs index 529a3ba7a..ec2a829f6 100644 --- a/bin/node/bench/src/construct.rs +++ b/bin/node/bench/src/construct.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2020-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 // This program is free software: you can redistribute it and/or modify diff --git a/bin/node/bench/src/core.rs b/bin/node/bench/src/core.rs index b6ad3ecd8..29cda7099 100644 --- a/bin/node/bench/src/core.rs +++ b/bin/node/bench/src/core.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2020-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 // This program is free software: you can redistribute it and/or modify diff --git a/bin/node/bench/src/generator.rs b/bin/node/bench/src/generator.rs index 76bd3a324..0fe082602 100644 --- a/bin/node/bench/src/generator.rs +++ b/bin/node/bench/src/generator.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2020-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 // This program is free software: you can redistribute it and/or modify diff --git a/bin/node/bench/src/import.rs b/bin/node/bench/src/import.rs index db93833c8..167377ea9 100644 --- a/bin/node/bench/src/import.rs +++ b/bin/node/bench/src/import.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2020-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 // This program is free software: you can redistribute it and/or modify diff --git a/bin/node/bench/src/main.rs b/bin/node/bench/src/main.rs index 8a5d99640..051d8ddb9 100644 --- a/bin/node/bench/src/main.rs +++ b/bin/node/bench/src/main.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2020-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 // This program is free software: you can redistribute it and/or modify diff --git a/bin/node/bench/src/simple_trie.rs b/bin/node/bench/src/simple_trie.rs index aa9c96a1c..6d5072358 100644 --- a/bin/node/bench/src/simple_trie.rs +++ b/bin/node/bench/src/simple_trie.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2020-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 // This program is free software: you can redistribute it and/or modify diff --git a/bin/node/bench/src/state_sizes.rs b/bin/node/bench/src/state_sizes.rs index 538785066..12e0f6eae 100644 --- a/bin/node/bench/src/state_sizes.rs +++ b/bin/node/bench/src/state_sizes.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2020-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 // This program is free software: you can redistribute it and/or modify diff --git a/bin/node/bench/src/tempdb.rs b/bin/node/bench/src/tempdb.rs index 82895ddfa..2aafd013a 100644 --- a/bin/node/bench/src/tempdb.rs +++ b/bin/node/bench/src/tempdb.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2020-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 // This program is free software: you can redistribute it and/or modify diff --git a/bin/node/bench/src/trie.rs b/bin/node/bench/src/trie.rs index de49a6fe7..09ab405c0 100644 --- a/bin/node/bench/src/trie.rs +++ b/bin/node/bench/src/trie.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2020-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 // This program is free software: you can redistribute it and/or modify diff --git a/bin/node/bench/src/txpool.rs b/bin/node/bench/src/txpool.rs index 5b4d5513e..4e8e5c0d9 100644 --- a/bin/node/bench/src/txpool.rs +++ b/bin/node/bench/src/txpool.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2020-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 // This program is free software: you can redistribute it and/or modify diff --git a/bin/node/cli/benches/block_production.rs b/bin/node/cli/benches/block_production.rs index 64ea03f58..e4b48497e 100644 --- a/bin/node/cli/benches/block_production.rs +++ b/bin/node/cli/benches/block_production.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2021-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 // This program is free software: you can redistribute it and/or modify diff --git a/bin/node/cli/benches/transaction_pool.rs b/bin/node/cli/benches/transaction_pool.rs index a8839642d..165c67f60 100644 --- a/bin/node/cli/benches/transaction_pool.rs +++ b/bin/node/cli/benches/transaction_pool.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2021-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 // This program is free software: you can redistribute it and/or modify diff --git a/bin/node/cli/bin/main.rs b/bin/node/cli/bin/main.rs index 3ae295754..4b434a3e6 100644 --- a/bin/node/cli/bin/main.rs +++ b/bin/node/cli/bin/main.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2018-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 // This program is free software: you can redistribute it and/or modify diff --git a/bin/node/cli/build.rs b/bin/node/cli/build.rs index e8142b297..18860a1af 100644 --- a/bin/node/cli/build.rs +++ b/bin/node/cli/build.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2017-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 // This program is free software: you can redistribute it and/or modify diff --git a/bin/node/cli/src/benchmarking.rs b/bin/node/cli/src/benchmarking.rs index 16ea9109d..333f855f2 100644 --- a/bin/node/cli/src/benchmarking.rs +++ b/bin/node/cli/src/benchmarking.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 // This program is free software: you can redistribute it and/or modify diff --git a/bin/node/cli/src/chain_spec.rs b/bin/node/cli/src/chain_spec.rs index 1e4e806fd..178b97823 100644 --- a/bin/node/cli/src/chain_spec.rs +++ b/bin/node/cli/src/chain_spec.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2018-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 // This program is free software: you can redistribute it and/or modify diff --git a/bin/node/cli/src/cli.rs b/bin/node/cli/src/cli.rs index 7bea336c8..35b949831 100644 --- a/bin/node/cli/src/cli.rs +++ b/bin/node/cli/src/cli.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2018-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 // This program is free software: you can redistribute it and/or modify diff --git a/bin/node/cli/src/command.rs b/bin/node/cli/src/command.rs index 4256c7d61..b38b25d8f 100644 --- a/bin/node/cli/src/command.rs +++ b/bin/node/cli/src/command.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2017-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 // This program is free software: you can redistribute it and/or modify diff --git a/bin/node/cli/src/lib.rs b/bin/node/cli/src/lib.rs index 13c074268..2fe238ef3 100644 --- a/bin/node/cli/src/lib.rs +++ b/bin/node/cli/src/lib.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2018-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 // This program is free software: you can redistribute it and/or modify diff --git a/bin/node/cli/src/service.rs b/bin/node/cli/src/service.rs index e0247079d..7b1ab2c1e 100644 --- a/bin/node/cli/src/service.rs +++ b/bin/node/cli/src/service.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2018-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 // This program is free software: you can redistribute it and/or modify diff --git a/bin/node/cli/tests/benchmark_block_works.rs b/bin/node/cli/tests/benchmark_block_works.rs index 65cb474ea..369e1b3e5 100644 --- a/bin/node/cli/tests/benchmark_block_works.rs +++ b/bin/node/cli/tests/benchmark_block_works.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 // This program is free software: you can redistribute it and/or modify diff --git a/bin/node/cli/tests/benchmark_extrinsic_works.rs b/bin/node/cli/tests/benchmark_extrinsic_works.rs index 69800ad3c..9cdb971de 100644 --- a/bin/node/cli/tests/benchmark_extrinsic_works.rs +++ b/bin/node/cli/tests/benchmark_extrinsic_works.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 // This program is free software: you can redistribute it and/or modify diff --git a/bin/node/cli/tests/benchmark_machine_works.rs b/bin/node/cli/tests/benchmark_machine_works.rs index 193e6b701..2cdadb646 100644 --- a/bin/node/cli/tests/benchmark_machine_works.rs +++ b/bin/node/cli/tests/benchmark_machine_works.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 // This program is free software: you can redistribute it and/or modify diff --git a/bin/node/cli/tests/benchmark_overhead_works.rs b/bin/node/cli/tests/benchmark_overhead_works.rs index 44dcebfbc..92ab93b7f 100644 --- a/bin/node/cli/tests/benchmark_overhead_works.rs +++ b/bin/node/cli/tests/benchmark_overhead_works.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 // This program is free software: you can redistribute it and/or modify diff --git a/bin/node/cli/tests/benchmark_pallet_works.rs b/bin/node/cli/tests/benchmark_pallet_works.rs index bf29c0e30..1a278f985 100644 --- a/bin/node/cli/tests/benchmark_pallet_works.rs +++ b/bin/node/cli/tests/benchmark_pallet_works.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 // This program is free software: you can redistribute it and/or modify diff --git a/bin/node/cli/tests/benchmark_storage_works.rs b/bin/node/cli/tests/benchmark_storage_works.rs index 82d1c943a..1f1181218 100644 --- a/bin/node/cli/tests/benchmark_storage_works.rs +++ b/bin/node/cli/tests/benchmark_storage_works.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 // This program is free software: you can redistribute it and/or modify diff --git a/bin/node/cli/tests/build_spec_works.rs b/bin/node/cli/tests/build_spec_works.rs index aecabed60..dc5d36184 100644 --- a/bin/node/cli/tests/build_spec_works.rs +++ b/bin/node/cli/tests/build_spec_works.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2020-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 // This program is free software: you can redistribute it and/or modify diff --git a/bin/node/cli/tests/check_block_works.rs b/bin/node/cli/tests/check_block_works.rs index d4afc530b..019d97aa8 100644 --- a/bin/node/cli/tests/check_block_works.rs +++ b/bin/node/cli/tests/check_block_works.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2020-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 // This program is free software: you can redistribute it and/or modify diff --git a/bin/node/cli/tests/common.rs b/bin/node/cli/tests/common.rs index c8f9e2b3d..a0de7300f 100644 --- a/bin/node/cli/tests/common.rs +++ b/bin/node/cli/tests/common.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2020-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 // This program is free software: you can redistribute it and/or modify diff --git a/bin/node/cli/tests/export_import_flow.rs b/bin/node/cli/tests/export_import_flow.rs index 750b4f7ac..afee9ef70 100644 --- a/bin/node/cli/tests/export_import_flow.rs +++ b/bin/node/cli/tests/export_import_flow.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2020-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 // This program is free software: you can redistribute it and/or modify diff --git a/bin/node/cli/tests/inspect_works.rs b/bin/node/cli/tests/inspect_works.rs index 849fb913a..56569248b 100644 --- a/bin/node/cli/tests/inspect_works.rs +++ b/bin/node/cli/tests/inspect_works.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2020-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 // This program is free software: you can redistribute it and/or modify diff --git a/bin/node/cli/tests/purge_chain_works.rs b/bin/node/cli/tests/purge_chain_works.rs index 811762a71..470c0a7e4 100644 --- a/bin/node/cli/tests/purge_chain_works.rs +++ b/bin/node/cli/tests/purge_chain_works.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2020-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 // This program is free software: you can redistribute it and/or modify diff --git a/bin/node/cli/tests/remember_state_pruning_works.rs b/bin/node/cli/tests/remember_state_pruning_works.rs index 5b8e34cc7..b8c5ddf4a 100644 --- a/bin/node/cli/tests/remember_state_pruning_works.rs +++ b/bin/node/cli/tests/remember_state_pruning_works.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 // This program is free software: you can redistribute it and/or modify diff --git a/bin/node/cli/tests/running_the_node_and_interrupt.rs b/bin/node/cli/tests/running_the_node_and_interrupt.rs index 6d4a4b404..fc0bf69a0 100644 --- a/bin/node/cli/tests/running_the_node_and_interrupt.rs +++ b/bin/node/cli/tests/running_the_node_and_interrupt.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2020-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 // This program is free software: you can redistribute it and/or modify diff --git a/bin/node/cli/tests/telemetry.rs b/bin/node/cli/tests/telemetry.rs index 98cf0b3af..633cc996c 100644 --- a/bin/node/cli/tests/telemetry.rs +++ b/bin/node/cli/tests/telemetry.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2021-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 // This program is free software: you can redistribute it and/or modify diff --git a/bin/node/cli/tests/temp_base_path_works.rs b/bin/node/cli/tests/temp_base_path_works.rs index 98422a21f..4e743f2d3 100644 --- a/bin/node/cli/tests/temp_base_path_works.rs +++ b/bin/node/cli/tests/temp_base_path_works.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2020-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 // This program is free software: you can redistribute it and/or modify diff --git a/bin/node/cli/tests/version.rs b/bin/node/cli/tests/version.rs index f5a6f3a53..e239277c9 100644 --- a/bin/node/cli/tests/version.rs +++ b/bin/node/cli/tests/version.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2020-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 // This program is free software: you can redistribute it and/or modify diff --git a/bin/node/cli/tests/websocket_server.rs b/bin/node/cli/tests/websocket_server.rs index 1e7450995..432a4871c 100644 --- a/bin/node/cli/tests/websocket_server.rs +++ b/bin/node/cli/tests/websocket_server.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2021-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 // This program is free software: you can redistribute it and/or modify diff --git a/bin/node/executor/benches/bench.rs b/bin/node/executor/benches/bench.rs index 4cbd95471..4154d1cff 100644 --- a/bin/node/executor/benches/bench.rs +++ b/bin/node/executor/benches/bench.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2018-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/bin/node/executor/src/lib.rs b/bin/node/executor/src/lib.rs index c4edf5ad2..4e3ec9a0b 100644 --- a/bin/node/executor/src/lib.rs +++ b/bin/node/executor/src/lib.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2018-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/bin/node/executor/tests/basic.rs b/bin/node/executor/tests/basic.rs index 02b2a8787..c19db5af5 100644 --- a/bin/node/executor/tests/basic.rs +++ b/bin/node/executor/tests/basic.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2018-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/bin/node/executor/tests/common.rs b/bin/node/executor/tests/common.rs index 803ec7832..b428e32ea 100644 --- a/bin/node/executor/tests/common.rs +++ b/bin/node/executor/tests/common.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2018-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/bin/node/executor/tests/fees.rs b/bin/node/executor/tests/fees.rs index 3c696d595..cbf0fdce9 100644 --- a/bin/node/executor/tests/fees.rs +++ b/bin/node/executor/tests/fees.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2018-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/bin/node/executor/tests/submit_transaction.rs b/bin/node/executor/tests/submit_transaction.rs index be43f3c78..ab5df62a2 100644 --- a/bin/node/executor/tests/submit_transaction.rs +++ b/bin/node/executor/tests/submit_transaction.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2018-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/bin/node/inspect/src/cli.rs b/bin/node/inspect/src/cli.rs index fa6344ac3..c02a9c61d 100644 --- a/bin/node/inspect/src/cli.rs +++ b/bin/node/inspect/src/cli.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2020-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 // This program is free software: you can redistribute it and/or modify diff --git a/bin/node/inspect/src/command.rs b/bin/node/inspect/src/command.rs index ce164e076..3dca979eb 100644 --- a/bin/node/inspect/src/command.rs +++ b/bin/node/inspect/src/command.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2020-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 // This program is free software: you can redistribute it and/or modify diff --git a/bin/node/inspect/src/lib.rs b/bin/node/inspect/src/lib.rs index 3b9d7b2b1..5764e0f05 100644 --- a/bin/node/inspect/src/lib.rs +++ b/bin/node/inspect/src/lib.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. // -// Copyright (C) 2020-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 // // This program is free software: you can redistribute it and/or modify diff --git a/bin/node/primitives/src/lib.rs b/bin/node/primitives/src/lib.rs index feb9ee60d..e2fa5c310 100644 --- a/bin/node/primitives/src/lib.rs +++ b/bin/node/primitives/src/lib.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2018-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/bin/node/rpc/src/lib.rs b/bin/node/rpc/src/lib.rs index 0dc5ba403..40e4de6b9 100644 --- a/bin/node/rpc/src/lib.rs +++ b/bin/node/rpc/src/lib.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2019-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/bin/node/runtime/build.rs b/bin/node/runtime/build.rs index 70f84bc00..b7676a70d 100644 --- a/bin/node/runtime/build.rs +++ b/bin/node/runtime/build.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2019-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/bin/node/runtime/src/constants.rs b/bin/node/runtime/src/constants.rs index 23fb13cfb..e4fafbf0f 100644 --- a/bin/node/runtime/src/constants.rs +++ b/bin/node/runtime/src/constants.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2019-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/bin/node/runtime/src/impls.rs b/bin/node/runtime/src/impls.rs index b3f58ea5d..9c606ff07 100644 --- a/bin/node/runtime/src/impls.rs +++ b/bin/node/runtime/src/impls.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2019-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/bin/node/runtime/src/lib.rs b/bin/node/runtime/src/lib.rs index 0b887ff7d..8e924a9c5 100644 --- a/bin/node/runtime/src/lib.rs +++ b/bin/node/runtime/src/lib.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2018-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 // This program is free software: you can redistribute it and/or modify diff --git a/bin/node/runtime/src/voter_bags.rs b/bin/node/runtime/src/voter_bags.rs index eb540c27a..bf18097dd 100644 --- a/bin/node/runtime/src/voter_bags.rs +++ b/bin/node/runtime/src/voter_bags.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/bin/node/testing/src/bench.rs b/bin/node/testing/src/bench.rs index f9bad2a56..ccce6dc23 100644 --- a/bin/node/testing/src/bench.rs +++ b/bin/node/testing/src/bench.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2019-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 // This program is free software: you can redistribute it and/or modify diff --git a/bin/node/testing/src/client.rs b/bin/node/testing/src/client.rs index 590304bdd..8594a4a2e 100644 --- a/bin/node/testing/src/client.rs +++ b/bin/node/testing/src/client.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2019-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 // This program is free software: you can redistribute it and/or modify diff --git a/bin/node/testing/src/genesis.rs b/bin/node/testing/src/genesis.rs index b207fc7f9..b8c80aeb1 100644 --- a/bin/node/testing/src/genesis.rs +++ b/bin/node/testing/src/genesis.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2019-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 // This program is free software: you can redistribute it and/or modify diff --git a/bin/node/testing/src/keyring.rs b/bin/node/testing/src/keyring.rs index 88d9dc56b..e16502bf1 100644 --- a/bin/node/testing/src/keyring.rs +++ b/bin/node/testing/src/keyring.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2019-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 // This program is free software: you can redistribute it and/or modify diff --git a/bin/node/testing/src/lib.rs b/bin/node/testing/src/lib.rs index ce3471e5b..14e540906 100644 --- a/bin/node/testing/src/lib.rs +++ b/bin/node/testing/src/lib.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2019-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 // This program is free software: you can redistribute it and/or modify diff --git a/bin/utils/chain-spec-builder/build.rs b/bin/utils/chain-spec-builder/build.rs index b700b28e3..a68cb706e 100644 --- a/bin/utils/chain-spec-builder/build.rs +++ b/bin/utils/chain-spec-builder/build.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2019-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 // This program is free software: you can redistribute it and/or modify diff --git a/bin/utils/chain-spec-builder/src/main.rs b/bin/utils/chain-spec-builder/src/main.rs index 7dfcaed77..b3f28b269 100644 --- a/bin/utils/chain-spec-builder/src/main.rs +++ b/bin/utils/chain-spec-builder/src/main.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2019-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 // This program is free software: you can redistribute it and/or modify diff --git a/bin/utils/subkey/src/lib.rs b/bin/utils/subkey/src/lib.rs index 280b90848..201d4d25f 100644 --- a/bin/utils/subkey/src/lib.rs +++ b/bin/utils/subkey/src/lib.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2018-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 // This program is free software: you can redistribute it and/or modify diff --git a/bin/utils/subkey/src/main.rs b/bin/utils/subkey/src/main.rs index 271388549..d836a9b8f 100644 --- a/bin/utils/subkey/src/main.rs +++ b/bin/utils/subkey/src/main.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2018-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 // This program is free software: you can redistribute it and/or modify diff --git a/client/allocator/src/error.rs b/client/allocator/src/error.rs index d9fc48322..a3d91a3e2 100644 --- a/client/allocator/src/error.rs +++ b/client/allocator/src/error.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2020-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/client/allocator/src/freeing_bump.rs b/client/allocator/src/freeing_bump.rs index e81d1b79e..fdd63bd66 100644 --- a/client/allocator/src/freeing_bump.rs +++ b/client/allocator/src/freeing_bump.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2017-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/client/allocator/src/lib.rs b/client/allocator/src/lib.rs index 2fe63a1ec..31179ab5d 100644 --- a/client/allocator/src/lib.rs +++ b/client/allocator/src/lib.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2020-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/client/api/src/backend.rs b/client/api/src/backend.rs index 4ef609bdd..bc799a418 100644 --- a/client/api/src/backend.rs +++ b/client/api/src/backend.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2017-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 // This program is free software: you can redistribute it and/or modify diff --git a/client/api/src/call_executor.rs b/client/api/src/call_executor.rs index 74a2b8b85..42d3c4e34 100644 --- a/client/api/src/call_executor.rs +++ b/client/api/src/call_executor.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2017-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 // This program is free software: you can redistribute it and/or modify diff --git a/client/api/src/client.rs b/client/api/src/client.rs index 8e7ceb687..aeb119e0e 100644 --- a/client/api/src/client.rs +++ b/client/api/src/client.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2019-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 // This program is free software: you can redistribute it and/or modify diff --git a/client/api/src/execution_extensions.rs b/client/api/src/execution_extensions.rs index 58c085a29..b491d7672 100644 --- a/client/api/src/execution_extensions.rs +++ b/client/api/src/execution_extensions.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2019-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 // This program is free software: you can redistribute it and/or modify diff --git a/client/api/src/in_mem.rs b/client/api/src/in_mem.rs index 5e82757e7..7096910e1 100644 --- a/client/api/src/in_mem.rs +++ b/client/api/src/in_mem.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2017-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 // This program is free software: you can redistribute it and/or modify diff --git a/client/api/src/leaves.rs b/client/api/src/leaves.rs index cdcb80a11..a8a988771 100644 --- a/client/api/src/leaves.rs +++ b/client/api/src/leaves.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2018-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 // This program is free software: you can redistribute it and/or modify diff --git a/client/api/src/lib.rs b/client/api/src/lib.rs index 3d21f12f6..0faddc10f 100644 --- a/client/api/src/lib.rs +++ b/client/api/src/lib.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2019-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 // This program is free software: you can redistribute it and/or modify diff --git a/client/api/src/notifications.rs b/client/api/src/notifications.rs index 4dd23581d..c16cefe21 100644 --- a/client/api/src/notifications.rs +++ b/client/api/src/notifications.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2017-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 // This program is free software: you can redistribute it and/or modify diff --git a/client/api/src/notifications/registry.rs b/client/api/src/notifications/registry.rs index 882d6ed40..10c667311 100644 --- a/client/api/src/notifications/registry.rs +++ b/client/api/src/notifications/registry.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2017-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 // This program is free software: you can redistribute it and/or modify diff --git a/client/api/src/notifications/tests.rs b/client/api/src/notifications/tests.rs index 2c728de74..fba829b1c 100644 --- a/client/api/src/notifications/tests.rs +++ b/client/api/src/notifications/tests.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2017-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 // This program is free software: you can redistribute it and/or modify diff --git a/client/api/src/proof_provider.rs b/client/api/src/proof_provider.rs index 01e35df1d..7f60f856a 100644 --- a/client/api/src/proof_provider.rs +++ b/client/api/src/proof_provider.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2017-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 // This program is free software: you can redistribute it and/or modify diff --git a/client/authority-discovery/src/error.rs b/client/authority-discovery/src/error.rs index 285a2714b..89c05b71b 100644 --- a/client/authority-discovery/src/error.rs +++ b/client/authority-discovery/src/error.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2019-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 // This program is free software: you can redistribute it and/or modify diff --git a/client/authority-discovery/src/interval.rs b/client/authority-discovery/src/interval.rs index 5ddf81fbc..23c7ce266 100644 --- a/client/authority-discovery/src/interval.rs +++ b/client/authority-discovery/src/interval.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2020-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 // This program is free software: you can redistribute it and/or modify diff --git a/client/authority-discovery/src/lib.rs b/client/authority-discovery/src/lib.rs index db3802b16..579b9d8b1 100644 --- a/client/authority-discovery/src/lib.rs +++ b/client/authority-discovery/src/lib.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2019-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 // This program is free software: you can redistribute it and/or modify diff --git a/client/authority-discovery/src/service.rs b/client/authority-discovery/src/service.rs index df09b6ea4..89ae058d1 100644 --- a/client/authority-discovery/src/service.rs +++ b/client/authority-discovery/src/service.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2020-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 // This program is free software: you can redistribute it and/or modify diff --git a/client/authority-discovery/src/tests.rs b/client/authority-discovery/src/tests.rs index 208440b7a..982c3fc04 100644 --- a/client/authority-discovery/src/tests.rs +++ b/client/authority-discovery/src/tests.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2017-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 // This program is free software: you can redistribute it and/or modify diff --git a/client/authority-discovery/src/worker.rs b/client/authority-discovery/src/worker.rs index 72a371cb6..bc5fe2848 100644 --- a/client/authority-discovery/src/worker.rs +++ b/client/authority-discovery/src/worker.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2020-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 // This program is free software: you can redistribute it and/or modify diff --git a/client/authority-discovery/src/worker/addr_cache.rs b/client/authority-discovery/src/worker/addr_cache.rs index 4859413a8..8084b7f0a 100644 --- a/client/authority-discovery/src/worker/addr_cache.rs +++ b/client/authority-discovery/src/worker/addr_cache.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2019-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 // This program is free software: you can redistribute it and/or modify diff --git a/client/authority-discovery/src/worker/schema/tests.rs b/client/authority-discovery/src/worker/schema/tests.rs index 60147d676..89c921e0c 100644 --- a/client/authority-discovery/src/worker/schema/tests.rs +++ b/client/authority-discovery/src/worker/schema/tests.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2017-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 // This program is free software: you can redistribute it and/or modify diff --git a/client/authority-discovery/src/worker/tests.rs b/client/authority-discovery/src/worker/tests.rs index 7f3d113a8..22f984d97 100644 --- a/client/authority-discovery/src/worker/tests.rs +++ b/client/authority-discovery/src/worker/tests.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2017-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 // This program is free software: you can redistribute it and/or modify diff --git a/client/basic-authorship/src/basic_authorship.rs b/client/basic-authorship/src/basic_authorship.rs index f68a86ef1..376278fa1 100644 --- a/client/basic-authorship/src/basic_authorship.rs +++ b/client/basic-authorship/src/basic_authorship.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2018-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 // This program is free software: you can redistribute it and/or modify diff --git a/client/basic-authorship/src/lib.rs b/client/basic-authorship/src/lib.rs index e7f0d4496..23a2120ac 100644 --- a/client/basic-authorship/src/lib.rs +++ b/client/basic-authorship/src/lib.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2017-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 // This program is free software: you can redistribute it and/or modify diff --git a/client/beefy/rpc/src/lib.rs b/client/beefy/rpc/src/lib.rs index 4fea98c6e..0a7a4de14 100644 --- a/client/beefy/rpc/src/lib.rs +++ b/client/beefy/rpc/src/lib.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2021-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 // This program is free software: you can redistribute it and/or modify diff --git a/client/beefy/rpc/src/notification.rs b/client/beefy/rpc/src/notification.rs index a81542564..286385187 100644 --- a/client/beefy/rpc/src/notification.rs +++ b/client/beefy/rpc/src/notification.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2021-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 // This program is free software: you can redistribute it and/or modify diff --git a/client/beefy/src/aux_schema.rs b/client/beefy/src/aux_schema.rs index 6e87956da..2e99b4cc4 100644 --- a/client/beefy/src/aux_schema.rs +++ b/client/beefy/src/aux_schema.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2019-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 // This program is free software: you can redistribute it and/or modify diff --git a/client/beefy/src/communication/gossip.rs b/client/beefy/src/communication/gossip.rs index 7e60eb11e..a1fb6fa26 100644 --- a/client/beefy/src/communication/gossip.rs +++ b/client/beefy/src/communication/gossip.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2017-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 // This program is free software: you can redistribute it and/or modify diff --git a/client/beefy/src/communication/mod.rs b/client/beefy/src/communication/mod.rs index 91798d4ae..f91b08bbc 100644 --- a/client/beefy/src/communication/mod.rs +++ b/client/beefy/src/communication/mod.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 // This program is free software: you can redistribute it and/or modify diff --git a/client/beefy/src/communication/notification.rs b/client/beefy/src/communication/notification.rs index c673115e4..a4486e523 100644 --- a/client/beefy/src/communication/notification.rs +++ b/client/beefy/src/communication/notification.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2021-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 // This program is free software: you can redistribute it and/or modify diff --git a/client/beefy/src/communication/peers.rs b/client/beefy/src/communication/peers.rs index 50158126a..c2fb06fad 100644 --- a/client/beefy/src/communication/peers.rs +++ b/client/beefy/src/communication/peers.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 // This program is free software: you can redistribute it and/or modify diff --git a/client/beefy/src/communication/request_response/incoming_requests_handler.rs b/client/beefy/src/communication/request_response/incoming_requests_handler.rs index 854ece11c..3266f6dba 100644 --- a/client/beefy/src/communication/request_response/incoming_requests_handler.rs +++ b/client/beefy/src/communication/request_response/incoming_requests_handler.rs @@ -1,4 +1,4 @@ -// Copyright 2022 Parity Technologies (UK) Ltd. +// Copyright Parity Technologies (UK) Ltd. // This file is part of Substrate. // Substrate is free software: you can redistribute it and/or modify diff --git a/client/beefy/src/communication/request_response/mod.rs b/client/beefy/src/communication/request_response/mod.rs index 22a20c247..c528d06bb 100644 --- a/client/beefy/src/communication/request_response/mod.rs +++ b/client/beefy/src/communication/request_response/mod.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 // This program is free software: you can redistribute it and/or modify diff --git a/client/beefy/src/communication/request_response/outgoing_requests_engine.rs b/client/beefy/src/communication/request_response/outgoing_requests_engine.rs index 3e0d6e47d..0f4f07835 100644 --- a/client/beefy/src/communication/request_response/outgoing_requests_engine.rs +++ b/client/beefy/src/communication/request_response/outgoing_requests_engine.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 // This program is free software: you can redistribute it and/or modify diff --git a/client/beefy/src/error.rs b/client/beefy/src/error.rs index 1b44e3801..16afbf218 100644 --- a/client/beefy/src/error.rs +++ b/client/beefy/src/error.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2021-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 // This program is free software: you can redistribute it and/or modify diff --git a/client/beefy/src/import.rs b/client/beefy/src/import.rs index 66421e227..44d30d5d7 100644 --- a/client/beefy/src/import.rs +++ b/client/beefy/src/import.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2021-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 // This program is free software: you can redistribute it and/or modify diff --git a/client/beefy/src/justification.rs b/client/beefy/src/justification.rs index 9f433aa6a..6ceb3d2f5 100644 --- a/client/beefy/src/justification.rs +++ b/client/beefy/src/justification.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2021-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 // This program is free software: you can redistribute it and/or modify diff --git a/client/beefy/src/keystore.rs b/client/beefy/src/keystore.rs index 29436e36e..3f0d8a5f3 100644 --- a/client/beefy/src/keystore.rs +++ b/client/beefy/src/keystore.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2017-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 // This program is free software: you can redistribute it and/or modify diff --git a/client/beefy/src/lib.rs b/client/beefy/src/lib.rs index 04345912f..4f5f8b443 100644 --- a/client/beefy/src/lib.rs +++ b/client/beefy/src/lib.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2021-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 // This program is free software: you can redistribute it and/or modify diff --git a/client/beefy/src/metrics.rs b/client/beefy/src/metrics.rs index c74fb420d..85438f1ca 100644 --- a/client/beefy/src/metrics.rs +++ b/client/beefy/src/metrics.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2021-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 // This program is free software: you can redistribute it and/or modify diff --git a/client/beefy/src/round.rs b/client/beefy/src/round.rs index b2c4e97a4..142e138f5 100644 --- a/client/beefy/src/round.rs +++ b/client/beefy/src/round.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2021-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 // This program is free software: you can redistribute it and/or modify diff --git a/client/beefy/src/tests.rs b/client/beefy/src/tests.rs index d8afc5465..419033efe 100644 --- a/client/beefy/src/tests.rs +++ b/client/beefy/src/tests.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2018-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 // This program is free software: you can redistribute it and/or modify diff --git a/client/beefy/src/worker.rs b/client/beefy/src/worker.rs index f67ec17fc..439da2138 100644 --- a/client/beefy/src/worker.rs +++ b/client/beefy/src/worker.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2021-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 // This program is free software: you can redistribute it and/or modify diff --git a/client/block-builder/src/lib.rs b/client/block-builder/src/lib.rs index 437e43a71..d7ef3b1da 100644 --- a/client/block-builder/src/lib.rs +++ b/client/block-builder/src/lib.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2017-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 // This program is free software: you can redistribute it and/or modify diff --git a/client/chain-spec/derive/src/impls.rs b/client/chain-spec/derive/src/impls.rs index 7af403d46..e54c1895e 100644 --- a/client/chain-spec/derive/src/impls.rs +++ b/client/chain-spec/derive/src/impls.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2019-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 // This program is free software: you can redistribute it and/or modify diff --git a/client/chain-spec/derive/src/lib.rs b/client/chain-spec/derive/src/lib.rs index 75356a225..7607c6fdc 100644 --- a/client/chain-spec/derive/src/lib.rs +++ b/client/chain-spec/derive/src/lib.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2019-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 // This program is free software: you can redistribute it and/or modify diff --git a/client/chain-spec/src/chain_spec.rs b/client/chain-spec/src/chain_spec.rs index 0b951200b..9eed5bbbc 100644 --- a/client/chain-spec/src/chain_spec.rs +++ b/client/chain-spec/src/chain_spec.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2017-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 // This program is free software: you can redistribute it and/or modify diff --git a/client/chain-spec/src/extension.rs b/client/chain-spec/src/extension.rs index c0b3e15a2..25ab011a0 100644 --- a/client/chain-spec/src/extension.rs +++ b/client/chain-spec/src/extension.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2019-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 // This program is free software: you can redistribute it and/or modify diff --git a/client/chain-spec/src/lib.rs b/client/chain-spec/src/lib.rs index 9d2cc728b..414df1fe1 100644 --- a/client/chain-spec/src/lib.rs +++ b/client/chain-spec/src/lib.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2019-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 // This program is free software: you can redistribute it and/or modify diff --git a/client/cli/src/arg_enums.rs b/client/cli/src/arg_enums.rs index 372e8645e..c3399a896 100644 --- a/client/cli/src/arg_enums.rs +++ b/client/cli/src/arg_enums.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2018-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 // This program is free software: you can redistribute it and/or modify diff --git a/client/cli/src/commands/build_spec_cmd.rs b/client/cli/src/commands/build_spec_cmd.rs index 5ab3ce9e8..a2cbfedb7 100644 --- a/client/cli/src/commands/build_spec_cmd.rs +++ b/client/cli/src/commands/build_spec_cmd.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2018-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 // This program is free software: you can redistribute it and/or modify diff --git a/client/cli/src/commands/chain_info_cmd.rs b/client/cli/src/commands/chain_info_cmd.rs index cbc22cc4d..002d7893d 100644 --- a/client/cli/src/commands/chain_info_cmd.rs +++ b/client/cli/src/commands/chain_info_cmd.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 // This program is free software: you can redistribute it and/or modify diff --git a/client/cli/src/commands/check_block_cmd.rs b/client/cli/src/commands/check_block_cmd.rs index 3e25eab2c..897b61c8e 100644 --- a/client/cli/src/commands/check_block_cmd.rs +++ b/client/cli/src/commands/check_block_cmd.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2018-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 // This program is free software: you can redistribute it and/or modify diff --git a/client/cli/src/commands/export_blocks_cmd.rs b/client/cli/src/commands/export_blocks_cmd.rs index 76fedff4d..7e8a295f9 100644 --- a/client/cli/src/commands/export_blocks_cmd.rs +++ b/client/cli/src/commands/export_blocks_cmd.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2018-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 // This program is free software: you can redistribute it and/or modify diff --git a/client/cli/src/commands/export_state_cmd.rs b/client/cli/src/commands/export_state_cmd.rs index 04bce0c1d..45196c119 100644 --- a/client/cli/src/commands/export_state_cmd.rs +++ b/client/cli/src/commands/export_state_cmd.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2020-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 // This program is free software: you can redistribute it and/or modify diff --git a/client/cli/src/commands/generate.rs b/client/cli/src/commands/generate.rs index 461cb98bc..93b83fcbe 100644 --- a/client/cli/src/commands/generate.rs +++ b/client/cli/src/commands/generate.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2020-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/client/cli/src/commands/generate_node_key.rs b/client/cli/src/commands/generate_node_key.rs index e84b4a71d..2288cd403 100644 --- a/client/cli/src/commands/generate_node_key.rs +++ b/client/cli/src/commands/generate_node_key.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2020-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/client/cli/src/commands/import_blocks_cmd.rs b/client/cli/src/commands/import_blocks_cmd.rs index debc69724..f76c72924 100644 --- a/client/cli/src/commands/import_blocks_cmd.rs +++ b/client/cli/src/commands/import_blocks_cmd.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2018-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 // This program is free software: you can redistribute it and/or modify diff --git a/client/cli/src/commands/insert_key.rs b/client/cli/src/commands/insert_key.rs index 7d66a680d..77d0f5778 100644 --- a/client/cli/src/commands/insert_key.rs +++ b/client/cli/src/commands/insert_key.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2020-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/client/cli/src/commands/inspect_key.rs b/client/cli/src/commands/inspect_key.rs index 369fd1092..de82fe71e 100644 --- a/client/cli/src/commands/inspect_key.rs +++ b/client/cli/src/commands/inspect_key.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2020-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/client/cli/src/commands/inspect_node_key.rs b/client/cli/src/commands/inspect_node_key.rs index 9300007cb..2370f4a09 100644 --- a/client/cli/src/commands/inspect_node_key.rs +++ b/client/cli/src/commands/inspect_node_key.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2020-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/client/cli/src/commands/key.rs b/client/cli/src/commands/key.rs index 59cca554b..d49b7e407 100644 --- a/client/cli/src/commands/key.rs +++ b/client/cli/src/commands/key.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2020-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/client/cli/src/commands/mod.rs b/client/cli/src/commands/mod.rs index eeb9f3be3..d004fc1be 100644 --- a/client/cli/src/commands/mod.rs +++ b/client/cli/src/commands/mod.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2018-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 // This program is free software: you can redistribute it and/or modify diff --git a/client/cli/src/commands/purge_chain_cmd.rs b/client/cli/src/commands/purge_chain_cmd.rs index 9a3aeee50..2ff3d4b9a 100644 --- a/client/cli/src/commands/purge_chain_cmd.rs +++ b/client/cli/src/commands/purge_chain_cmd.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2018-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 // This program is free software: you can redistribute it and/or modify diff --git a/client/cli/src/commands/revert_cmd.rs b/client/cli/src/commands/revert_cmd.rs index 8477630cf..df5d93a7e 100644 --- a/client/cli/src/commands/revert_cmd.rs +++ b/client/cli/src/commands/revert_cmd.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2018-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 // This program is free software: you can redistribute it and/or modify diff --git a/client/cli/src/commands/run_cmd.rs b/client/cli/src/commands/run_cmd.rs index 35181d83f..9441acecc 100644 --- a/client/cli/src/commands/run_cmd.rs +++ b/client/cli/src/commands/run_cmd.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2018-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 // This program is free software: you can redistribute it and/or modify diff --git a/client/cli/src/commands/sign.rs b/client/cli/src/commands/sign.rs index b3da31e56..91b0651f0 100644 --- a/client/cli/src/commands/sign.rs +++ b/client/cli/src/commands/sign.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2018-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 // This program is free software: you can redistribute it and/or modify diff --git a/client/cli/src/commands/test/mod.rs b/client/cli/src/commands/test/mod.rs index 762918f70..9b5d0ee89 100644 --- a/client/cli/src/commands/test/mod.rs +++ b/client/cli/src/commands/test/mod.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2023 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 // This program is free software: you can redistribute it and/or modify diff --git a/client/cli/src/commands/test/sig_verify.rs b/client/cli/src/commands/test/sig_verify.rs index 8d7247480..bffd7dbc9 100644 --- a/client/cli/src/commands/test/sig_verify.rs +++ b/client/cli/src/commands/test/sig_verify.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2023 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 // This program is free software: you can redistribute it and/or modify diff --git a/client/cli/src/commands/utils.rs b/client/cli/src/commands/utils.rs index 8ced185d7..ff159909b 100644 --- a/client/cli/src/commands/utils.rs +++ b/client/cli/src/commands/utils.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2019-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 // This program is free software: you can redistribute it and/or modify diff --git a/client/cli/src/commands/vanity.rs b/client/cli/src/commands/vanity.rs index ae0007ac7..ce7516132 100644 --- a/client/cli/src/commands/vanity.rs +++ b/client/cli/src/commands/vanity.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2018-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 // This program is free software: you can redistribute it and/or modify diff --git a/client/cli/src/commands/verify.rs b/client/cli/src/commands/verify.rs index 183bf507b..c6d280de3 100644 --- a/client/cli/src/commands/verify.rs +++ b/client/cli/src/commands/verify.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2019-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 // This program is free software: you can redistribute it and/or modify diff --git a/client/cli/src/config.rs b/client/cli/src/config.rs index 77689708a..5fedcf99a 100644 --- a/client/cli/src/config.rs +++ b/client/cli/src/config.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2020-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 // This program is free software: you can redistribute it and/or modify diff --git a/client/cli/src/error.rs b/client/cli/src/error.rs index a0f843e73..3b30ccb3e 100644 --- a/client/cli/src/error.rs +++ b/client/cli/src/error.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2017-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 // This program is free software: you can redistribute it and/or modify diff --git a/client/cli/src/lib.rs b/client/cli/src/lib.rs index b6d0d47e5..adbb29931 100644 --- a/client/cli/src/lib.rs +++ b/client/cli/src/lib.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2017-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 // This program is free software: you can redistribute it and/or modify diff --git a/client/cli/src/params/database_params.rs b/client/cli/src/params/database_params.rs index 06a154fd6..cbc602cb8 100644 --- a/client/cli/src/params/database_params.rs +++ b/client/cli/src/params/database_params.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2018-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 // This program is free software: you can redistribute it and/or modify diff --git a/client/cli/src/params/import_params.rs b/client/cli/src/params/import_params.rs index b7ccbf1c8..e36fbb5ff 100644 --- a/client/cli/src/params/import_params.rs +++ b/client/cli/src/params/import_params.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2018-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 // This program is free software: you can redistribute it and/or modify diff --git a/client/cli/src/params/keystore_params.rs b/client/cli/src/params/keystore_params.rs index d6c3c35d8..e6e6fd9eb 100644 --- a/client/cli/src/params/keystore_params.rs +++ b/client/cli/src/params/keystore_params.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2018-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 // This program is free software: you can redistribute it and/or modify diff --git a/client/cli/src/params/message_params.rs b/client/cli/src/params/message_params.rs index 9f88c24dc..a935bbb25 100644 --- a/client/cli/src/params/message_params.rs +++ b/client/cli/src/params/message_params.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2023 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 // This program is free software: you can redistribute it and/or modify diff --git a/client/cli/src/params/mod.rs b/client/cli/src/params/mod.rs index 4ec7d1d95..f9c840d2d 100644 --- a/client/cli/src/params/mod.rs +++ b/client/cli/src/params/mod.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2020-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 // This program is free software: you can redistribute it and/or modify diff --git a/client/cli/src/params/network_params.rs b/client/cli/src/params/network_params.rs index 984789da7..2402b5d68 100644 --- a/client/cli/src/params/network_params.rs +++ b/client/cli/src/params/network_params.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2018-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 // This program is free software: you can redistribute it and/or modify diff --git a/client/cli/src/params/node_key_params.rs b/client/cli/src/params/node_key_params.rs index 2346455c2..074b95bea 100644 --- a/client/cli/src/params/node_key_params.rs +++ b/client/cli/src/params/node_key_params.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2017-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 // This program is free software: you can redistribute it and/or modify diff --git a/client/cli/src/params/offchain_worker_params.rs b/client/cli/src/params/offchain_worker_params.rs index b18e036c0..33fd8d609 100644 --- a/client/cli/src/params/offchain_worker_params.rs +++ b/client/cli/src/params/offchain_worker_params.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2020-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 // This program is free software: you can redistribute it and/or modify diff --git a/client/cli/src/params/pruning_params.rs b/client/cli/src/params/pruning_params.rs index 66282fd08..59eeb13cd 100644 --- a/client/cli/src/params/pruning_params.rs +++ b/client/cli/src/params/pruning_params.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2020-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 // This program is free software: you can redistribute it and/or modify diff --git a/client/cli/src/params/shared_params.rs b/client/cli/src/params/shared_params.rs index 5cbb6dbad..46f539003 100644 --- a/client/cli/src/params/shared_params.rs +++ b/client/cli/src/params/shared_params.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2018-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 // This program is free software: you can redistribute it and/or modify diff --git a/client/cli/src/params/transaction_pool_params.rs b/client/cli/src/params/transaction_pool_params.rs index 6b3a2d8a9..b2bf0b9b3 100644 --- a/client/cli/src/params/transaction_pool_params.rs +++ b/client/cli/src/params/transaction_pool_params.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2018-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 // This program is free software: you can redistribute it and/or modify diff --git a/client/cli/src/runner.rs b/client/cli/src/runner.rs index c7a6f1f3c..8917a37c4 100644 --- a/client/cli/src/runner.rs +++ b/client/cli/src/runner.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2020-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 // This program is free software: you can redistribute it and/or modify diff --git a/client/consensus/aura/src/import_queue.rs b/client/consensus/aura/src/import_queue.rs index be52bffe0..99b5af081 100644 --- a/client/consensus/aura/src/import_queue.rs +++ b/client/consensus/aura/src/import_queue.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2018-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 // This program is free software: you can redistribute it and/or modify diff --git a/client/consensus/aura/src/lib.rs b/client/consensus/aura/src/lib.rs index 0345eaa39..f5f70857c 100644 --- a/client/consensus/aura/src/lib.rs +++ b/client/consensus/aura/src/lib.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2018-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 // This program is free software: you can redistribute it and/or modify diff --git a/client/consensus/babe/rpc/src/lib.rs b/client/consensus/babe/rpc/src/lib.rs index 2d6d95b8d..677a4ad92 100644 --- a/client/consensus/babe/rpc/src/lib.rs +++ b/client/consensus/babe/rpc/src/lib.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2020-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 // This program is free software: you can redistribute it and/or modify diff --git a/client/consensus/babe/src/authorship.rs b/client/consensus/babe/src/authorship.rs index 3a92aa5c6..195a19b3d 100644 --- a/client/consensus/babe/src/authorship.rs +++ b/client/consensus/babe/src/authorship.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2019-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 // This program is free software: you can redistribute it and/or modify diff --git a/client/consensus/babe/src/aux_schema.rs b/client/consensus/babe/src/aux_schema.rs index 2a09aa738..a87b7c9a0 100644 --- a/client/consensus/babe/src/aux_schema.rs +++ b/client/consensus/babe/src/aux_schema.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2019-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 // This program is free software: you can redistribute it and/or modify diff --git a/client/consensus/babe/src/lib.rs b/client/consensus/babe/src/lib.rs index a30540340..271e3f648 100644 --- a/client/consensus/babe/src/lib.rs +++ b/client/consensus/babe/src/lib.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2019-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 // This program is free software: you can redistribute it and/or modify diff --git a/client/consensus/babe/src/migration.rs b/client/consensus/babe/src/migration.rs index 23413aa6a..ec864b8e5 100644 --- a/client/consensus/babe/src/migration.rs +++ b/client/consensus/babe/src/migration.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2019-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 // This program is free software: you can redistribute it and/or modify diff --git a/client/consensus/babe/src/tests.rs b/client/consensus/babe/src/tests.rs index d1ad91a1c..42b8730cd 100644 --- a/client/consensus/babe/src/tests.rs +++ b/client/consensus/babe/src/tests.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2019-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 // This program is free software: you can redistribute it and/or modify diff --git a/client/consensus/babe/src/verification.rs b/client/consensus/babe/src/verification.rs index 8e8f5d998..96d3961d4 100644 --- a/client/consensus/babe/src/verification.rs +++ b/client/consensus/babe/src/verification.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2019-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 // This program is free software: you can redistribute it and/or modify diff --git a/client/consensus/common/src/block_import.rs b/client/consensus/common/src/block_import.rs index f888176ad..98accc1d2 100644 --- a/client/consensus/common/src/block_import.rs +++ b/client/consensus/common/src/block_import.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2017-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 // This program is free software: you can redistribute it and/or modify diff --git a/client/consensus/common/src/import_queue.rs b/client/consensus/common/src/import_queue.rs index 02309cc6a..66ff5e6d5 100644 --- a/client/consensus/common/src/import_queue.rs +++ b/client/consensus/common/src/import_queue.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2017-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 // This program is free software: you can redistribute it and/or modify diff --git a/client/consensus/common/src/import_queue/basic_queue.rs b/client/consensus/common/src/import_queue/basic_queue.rs index a96b0a47e..06122491e 100644 --- a/client/consensus/common/src/import_queue/basic_queue.rs +++ b/client/consensus/common/src/import_queue/basic_queue.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2017-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 // This program is free software: you can redistribute it and/or modify diff --git a/client/consensus/common/src/import_queue/buffered_link.rs b/client/consensus/common/src/import_queue/buffered_link.rs index 71adcf2dc..5c95cbc42 100644 --- a/client/consensus/common/src/import_queue/buffered_link.rs +++ b/client/consensus/common/src/import_queue/buffered_link.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2017-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 // This program is free software: you can redistribute it and/or modify diff --git a/client/consensus/common/src/import_queue/mock.rs b/client/consensus/common/src/import_queue/mock.rs index 67deee951..64ac532de 100644 --- a/client/consensus/common/src/import_queue/mock.rs +++ b/client/consensus/common/src/import_queue/mock.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 // This program is free software: you can redistribute it and/or modify diff --git a/client/consensus/common/src/lib.rs b/client/consensus/common/src/lib.rs index f291cc270..6bf1ed0b4 100644 --- a/client/consensus/common/src/lib.rs +++ b/client/consensus/common/src/lib.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2019-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 // This program is free software: you can redistribute it and/or modify diff --git a/client/consensus/common/src/longest_chain.rs b/client/consensus/common/src/longest_chain.rs index ece248620..f27cde498 100644 --- a/client/consensus/common/src/longest_chain.rs +++ b/client/consensus/common/src/longest_chain.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2019-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 // This program is free software: you can redistribute it and/or modify diff --git a/client/consensus/common/src/metrics.rs b/client/consensus/common/src/metrics.rs index 831404923..e807189d4 100644 --- a/client/consensus/common/src/metrics.rs +++ b/client/consensus/common/src/metrics.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2020-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 // This program is free software: you can redistribute it and/or modify diff --git a/client/consensus/common/src/shared_data.rs b/client/consensus/common/src/shared_data.rs index fb04966b5..efd41d098 100644 --- a/client/consensus/common/src/shared_data.rs +++ b/client/consensus/common/src/shared_data.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2021-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 // This program is free software: you can redistribute it and/or modify diff --git a/client/consensus/epochs/src/lib.rs b/client/consensus/epochs/src/lib.rs index 0e0b9fc17..29bb18e14 100644 --- a/client/consensus/epochs/src/lib.rs +++ b/client/consensus/epochs/src/lib.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2019-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 // This program is free software: you can redistribute it and/or modify diff --git a/client/consensus/epochs/src/migration.rs b/client/consensus/epochs/src/migration.rs index 052d0b7be..8838dbb46 100644 --- a/client/consensus/epochs/src/migration.rs +++ b/client/consensus/epochs/src/migration.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2019-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 // This program is free software: you can redistribute it and/or modify diff --git a/client/consensus/manual-seal/src/consensus.rs b/client/consensus/manual-seal/src/consensus.rs index a812bb028..b54ec5e41 100644 --- a/client/consensus/manual-seal/src/consensus.rs +++ b/client/consensus/manual-seal/src/consensus.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2020-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 // This program is free software: you can redistribute it and/or modify diff --git a/client/consensus/manual-seal/src/consensus/aura.rs b/client/consensus/manual-seal/src/consensus/aura.rs index 065b78732..92203f918 100644 --- a/client/consensus/manual-seal/src/consensus/aura.rs +++ b/client/consensus/manual-seal/src/consensus/aura.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 // This program is free software: you can redistribute it and/or modify diff --git a/client/consensus/manual-seal/src/consensus/babe.rs b/client/consensus/manual-seal/src/consensus/babe.rs index c5bb3b78f..476d2f0f4 100644 --- a/client/consensus/manual-seal/src/consensus/babe.rs +++ b/client/consensus/manual-seal/src/consensus/babe.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2020-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 // This program is free software: you can redistribute it and/or modify diff --git a/client/consensus/manual-seal/src/consensus/timestamp.rs b/client/consensus/manual-seal/src/consensus/timestamp.rs index b3c2d780c..dbffb2fbb 100644 --- a/client/consensus/manual-seal/src/consensus/timestamp.rs +++ b/client/consensus/manual-seal/src/consensus/timestamp.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2020-2021 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 // This program is free software: you can redistribute it and/or modify diff --git a/client/consensus/manual-seal/src/error.rs b/client/consensus/manual-seal/src/error.rs index a056c541c..eeae1d153 100644 --- a/client/consensus/manual-seal/src/error.rs +++ b/client/consensus/manual-seal/src/error.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2020-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 // This program is free software: you can redistribute it and/or modify diff --git a/client/consensus/manual-seal/src/finalize_block.rs b/client/consensus/manual-seal/src/finalize_block.rs index cee4d59b6..06883b229 100644 --- a/client/consensus/manual-seal/src/finalize_block.rs +++ b/client/consensus/manual-seal/src/finalize_block.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2019-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 // This program is free software: you can redistribute it and/or modify diff --git a/client/consensus/manual-seal/src/lib.rs b/client/consensus/manual-seal/src/lib.rs index 5c86382a3..41f1f4433 100644 --- a/client/consensus/manual-seal/src/lib.rs +++ b/client/consensus/manual-seal/src/lib.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2020-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 // This program is free software: you can redistribute it and/or modify diff --git a/client/consensus/manual-seal/src/rpc.rs b/client/consensus/manual-seal/src/rpc.rs index b9bb06551..db92b9fd2 100644 --- a/client/consensus/manual-seal/src/rpc.rs +++ b/client/consensus/manual-seal/src/rpc.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2019-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 // This program is free software: you can redistribute it and/or modify diff --git a/client/consensus/manual-seal/src/seal_block.rs b/client/consensus/manual-seal/src/seal_block.rs index 2cafdae50..eda3d91c9 100644 --- a/client/consensus/manual-seal/src/seal_block.rs +++ b/client/consensus/manual-seal/src/seal_block.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2019-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 // This program is free software: you can redistribute it and/or modify diff --git a/client/consensus/pow/src/lib.rs b/client/consensus/pow/src/lib.rs index a81ad8ebe..44eaccd2a 100644 --- a/client/consensus/pow/src/lib.rs +++ b/client/consensus/pow/src/lib.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2017-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 // This program is free software: you can redistribute it and/or modify diff --git a/client/consensus/pow/src/worker.rs b/client/consensus/pow/src/worker.rs index b53227bb3..1a4238957 100644 --- a/client/consensus/pow/src/worker.rs +++ b/client/consensus/pow/src/worker.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2020-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 // This program is free software: you can redistribute it and/or modify diff --git a/client/consensus/slots/build.rs b/client/consensus/slots/build.rs index b700b28e3..a68cb706e 100644 --- a/client/consensus/slots/build.rs +++ b/client/consensus/slots/build.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2019-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 // This program is free software: you can redistribute it and/or modify diff --git a/client/consensus/slots/src/aux_schema.rs b/client/consensus/slots/src/aux_schema.rs index c1d01500f..9c6bc0ad0 100644 --- a/client/consensus/slots/src/aux_schema.rs +++ b/client/consensus/slots/src/aux_schema.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2019-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 // This program is free software: you can redistribute it and/or modify diff --git a/client/consensus/slots/src/lib.rs b/client/consensus/slots/src/lib.rs index 30d2c1761..ec6481a87 100644 --- a/client/consensus/slots/src/lib.rs +++ b/client/consensus/slots/src/lib.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2019-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 // This program is free software: you can redistribute it and/or modify diff --git a/client/consensus/slots/src/slots.rs b/client/consensus/slots/src/slots.rs index 7bb263b2b..203764310 100644 --- a/client/consensus/slots/src/slots.rs +++ b/client/consensus/slots/src/slots.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2019-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 // This program is free software: you can redistribute it and/or modify diff --git a/client/db/benches/state_access.rs b/client/db/benches/state_access.rs index bab79fe7c..e47559e71 100644 --- a/client/db/benches/state_access.rs +++ b/client/db/benches/state_access.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2021 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 // This program is free software: you can redistribute it and/or modify diff --git a/client/db/src/bench.rs b/client/db/src/bench.rs index a74476287..b8fb3b6ce 100644 --- a/client/db/src/bench.rs +++ b/client/db/src/bench.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2020-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 // This program is free software: you can redistribute it and/or modify diff --git a/client/db/src/children.rs b/client/db/src/children.rs index 538e51851..067137653 100644 --- a/client/db/src/children.rs +++ b/client/db/src/children.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2019-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 // This program is free software: you can redistribute it and/or modify diff --git a/client/db/src/lib.rs b/client/db/src/lib.rs index 03de383f2..24c66fd77 100644 --- a/client/db/src/lib.rs +++ b/client/db/src/lib.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2017-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 // This program is free software: you can redistribute it and/or modify diff --git a/client/db/src/offchain.rs b/client/db/src/offchain.rs index 030a41098..dc7202ff6 100644 --- a/client/db/src/offchain.rs +++ b/client/db/src/offchain.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2017-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 // This program is free software: you can redistribute it and/or modify diff --git a/client/db/src/parity_db.rs b/client/db/src/parity_db.rs index 4adacbf6f..01562081a 100644 --- a/client/db/src/parity_db.rs +++ b/client/db/src/parity_db.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2017-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 // This program is free software: you can redistribute it and/or modify diff --git a/client/db/src/pinned_blocks_cache.rs b/client/db/src/pinned_blocks_cache.rs index 39ff1c527..f50a7081d 100644 --- a/client/db/src/pinned_blocks_cache.rs +++ b/client/db/src/pinned_blocks_cache.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2017-2023 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 // This program is free software: you can redistribute it and/or modify diff --git a/client/db/src/record_stats_state.rs b/client/db/src/record_stats_state.rs index 0b51d3fef..91d6c2fe5 100644 --- a/client/db/src/record_stats_state.rs +++ b/client/db/src/record_stats_state.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2019-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 // This program is free software: you can redistribute it and/or modify diff --git a/client/db/src/stats.rs b/client/db/src/stats.rs index f6c145682..1609ef9d5 100644 --- a/client/db/src/stats.rs +++ b/client/db/src/stats.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2017-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 // This program is free software: you can redistribute it and/or modify diff --git a/client/db/src/upgrade.rs b/client/db/src/upgrade.rs index 98517acad..f1e503867 100644 --- a/client/db/src/upgrade.rs +++ b/client/db/src/upgrade.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2019-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 // This program is free software: you can redistribute it and/or modify diff --git a/client/db/src/utils.rs b/client/db/src/utils.rs index e99851101..abf9c4629 100644 --- a/client/db/src/utils.rs +++ b/client/db/src/utils.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2017-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 // This program is free software: you can redistribute it and/or modify diff --git a/client/executor/benches/bench.rs b/client/executor/benches/bench.rs index a282cdfbd..62e8b261f 100644 --- a/client/executor/benches/bench.rs +++ b/client/executor/benches/bench.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2021 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/client/executor/common/src/error.rs b/client/executor/common/src/error.rs index c35a874b7..c47164a60 100644 --- a/client/executor/common/src/error.rs +++ b/client/executor/common/src/error.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2017-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 // This program is free software: you can redistribute it and/or modify diff --git a/client/executor/common/src/lib.rs b/client/executor/common/src/lib.rs index 79bb74b62..751801fb3 100644 --- a/client/executor/common/src/lib.rs +++ b/client/executor/common/src/lib.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2019-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 // This program is free software: you can redistribute it and/or modify diff --git a/client/executor/common/src/runtime_blob/data_segments_snapshot.rs b/client/executor/common/src/runtime_blob/data_segments_snapshot.rs index e65fc32f6..3fd546ce4 100644 --- a/client/executor/common/src/runtime_blob/data_segments_snapshot.rs +++ b/client/executor/common/src/runtime_blob/data_segments_snapshot.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2021-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 // This program is free software: you can redistribute it and/or modify diff --git a/client/executor/common/src/runtime_blob/globals_snapshot.rs b/client/executor/common/src/runtime_blob/globals_snapshot.rs index 207fa751b..9ba6fc55e 100644 --- a/client/executor/common/src/runtime_blob/globals_snapshot.rs +++ b/client/executor/common/src/runtime_blob/globals_snapshot.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2020-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 // This program is free software: you can redistribute it and/or modify diff --git a/client/executor/common/src/runtime_blob/mod.rs b/client/executor/common/src/runtime_blob/mod.rs index 4b163bbaa..58278493a 100644 --- a/client/executor/common/src/runtime_blob/mod.rs +++ b/client/executor/common/src/runtime_blob/mod.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2021-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 // This program is free software: you can redistribute it and/or modify diff --git a/client/executor/common/src/runtime_blob/runtime_blob.rs b/client/executor/common/src/runtime_blob/runtime_blob.rs index 08df4b32d..dc0cde8d2 100644 --- a/client/executor/common/src/runtime_blob/runtime_blob.rs +++ b/client/executor/common/src/runtime_blob/runtime_blob.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2021-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 // This program is free software: you can redistribute it and/or modify diff --git a/client/executor/common/src/util.rs b/client/executor/common/src/util.rs index fbae01b55..e672861cb 100644 --- a/client/executor/common/src/util.rs +++ b/client/executor/common/src/util.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2019-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 // This program is free software: you can redistribute it and/or modify diff --git a/client/executor/common/src/wasm_runtime.rs b/client/executor/common/src/wasm_runtime.rs index d0cc89261..589925206 100644 --- a/client/executor/common/src/wasm_runtime.rs +++ b/client/executor/common/src/wasm_runtime.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2019-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 // This program is free software: you can redistribute it and/or modify diff --git a/client/executor/runtime-test/build.rs b/client/executor/runtime-test/build.rs index d3aceedf9..745742123 100644 --- a/client/executor/runtime-test/build.rs +++ b/client/executor/runtime-test/build.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2019-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 // This program is free software: you can redistribute it and/or modify diff --git a/client/executor/src/integration_tests/linux.rs b/client/executor/src/integration_tests/linux.rs index efac4e74b..9afe37037 100644 --- a/client/executor/src/integration_tests/linux.rs +++ b/client/executor/src/integration_tests/linux.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2021-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 // This program is free software: you can redistribute it and/or modify diff --git a/client/executor/src/integration_tests/linux/smaps.rs b/client/executor/src/integration_tests/linux/smaps.rs index 665155ff8..1ac570dd8 100644 --- a/client/executor/src/integration_tests/linux/smaps.rs +++ b/client/executor/src/integration_tests/linux/smaps.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2017-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 // This program is free software: you can redistribute it and/or modify diff --git a/client/executor/src/integration_tests/mod.rs b/client/executor/src/integration_tests/mod.rs index bb2930803..324c8d515 100644 --- a/client/executor/src/integration_tests/mod.rs +++ b/client/executor/src/integration_tests/mod.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2017-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 // This program is free software: you can redistribute it and/or modify diff --git a/client/executor/src/lib.rs b/client/executor/src/lib.rs index 0670b8409..e5bae474e 100644 --- a/client/executor/src/lib.rs +++ b/client/executor/src/lib.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2017-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 // This program is free software: you can redistribute it and/or modify diff --git a/client/executor/src/native_executor.rs b/client/executor/src/native_executor.rs index 0eabffb8c..a2af97ae4 100644 --- a/client/executor/src/native_executor.rs +++ b/client/executor/src/native_executor.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2017-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 // This program is free software: you can redistribute it and/or modify diff --git a/client/executor/src/wasm_runtime.rs b/client/executor/src/wasm_runtime.rs index 5576fff18..4869fbae2 100644 --- a/client/executor/src/wasm_runtime.rs +++ b/client/executor/src/wasm_runtime.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2019-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 // This program is free software: you can redistribute it and/or modify diff --git a/client/executor/wasmi/src/lib.rs b/client/executor/wasmi/src/lib.rs index 6eb381469..78b32a027 100644 --- a/client/executor/wasmi/src/lib.rs +++ b/client/executor/wasmi/src/lib.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2019-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 // This program is free software: you can redistribute it and/or modify diff --git a/client/executor/wasmtime/build.rs b/client/executor/wasmtime/build.rs index c514b0041..a68cb706e 100644 --- a/client/executor/wasmtime/build.rs +++ b/client/executor/wasmtime/build.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2021-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 // This program is free software: you can redistribute it and/or modify diff --git a/client/executor/wasmtime/src/host.rs b/client/executor/wasmtime/src/host.rs index 0d9eac875..ead248385 100644 --- a/client/executor/wasmtime/src/host.rs +++ b/client/executor/wasmtime/src/host.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2019-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 // This program is free software: you can redistribute it and/or modify diff --git a/client/executor/wasmtime/src/imports.rs b/client/executor/wasmtime/src/imports.rs index fad3195de..fccc121fa 100644 --- a/client/executor/wasmtime/src/imports.rs +++ b/client/executor/wasmtime/src/imports.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2020-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 // This program is free software: you can redistribute it and/or modify diff --git a/client/executor/wasmtime/src/instance_wrapper.rs b/client/executor/wasmtime/src/instance_wrapper.rs index 2117f1ebf..f80c7b70d 100644 --- a/client/executor/wasmtime/src/instance_wrapper.rs +++ b/client/executor/wasmtime/src/instance_wrapper.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2020-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 // This program is free software: you can redistribute it and/or modify diff --git a/client/executor/wasmtime/src/lib.rs b/client/executor/wasmtime/src/lib.rs index 4d0e4c37c..c45478ec4 100644 --- a/client/executor/wasmtime/src/lib.rs +++ b/client/executor/wasmtime/src/lib.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2019-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 // This program is free software: you can redistribute it and/or modify diff --git a/client/executor/wasmtime/src/runtime.rs b/client/executor/wasmtime/src/runtime.rs index e54070065..1728feeee 100644 --- a/client/executor/wasmtime/src/runtime.rs +++ b/client/executor/wasmtime/src/runtime.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2019-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 // This program is free software: you can redistribute it and/or modify diff --git a/client/executor/wasmtime/src/tests.rs b/client/executor/wasmtime/src/tests.rs index 3c6a25a18..57540b35d 100644 --- a/client/executor/wasmtime/src/tests.rs +++ b/client/executor/wasmtime/src/tests.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2021-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 // This program is free software: you can redistribute it and/or modify diff --git a/client/executor/wasmtime/src/util.rs b/client/executor/wasmtime/src/util.rs index 15f62e475..5c64fc01c 100644 --- a/client/executor/wasmtime/src/util.rs +++ b/client/executor/wasmtime/src/util.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2020-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 // This program is free software: you can redistribute it and/or modify diff --git a/client/finality-grandpa/rpc/src/error.rs b/client/finality-grandpa/rpc/src/error.rs index 197c0b8a7..885408d8f 100644 --- a/client/finality-grandpa/rpc/src/error.rs +++ b/client/finality-grandpa/rpc/src/error.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2020-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 // This program is free software: you can redistribute it and/or modify diff --git a/client/finality-grandpa/rpc/src/finality.rs b/client/finality-grandpa/rpc/src/finality.rs index f2be6d674..dd1f0af86 100644 --- a/client/finality-grandpa/rpc/src/finality.rs +++ b/client/finality-grandpa/rpc/src/finality.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2020-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 // This program is free software: you can redistribute it and/or modify diff --git a/client/finality-grandpa/rpc/src/lib.rs b/client/finality-grandpa/rpc/src/lib.rs index 70ff7ed17..8f06531be 100644 --- a/client/finality-grandpa/rpc/src/lib.rs +++ b/client/finality-grandpa/rpc/src/lib.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2020-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 // This program is free software: you can redistribute it and/or modify diff --git a/client/finality-grandpa/rpc/src/notification.rs b/client/finality-grandpa/rpc/src/notification.rs index 6fb138357..45e7d3488 100644 --- a/client/finality-grandpa/rpc/src/notification.rs +++ b/client/finality-grandpa/rpc/src/notification.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2020-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 // This program is free software: you can redistribute it and/or modify diff --git a/client/finality-grandpa/rpc/src/report.rs b/client/finality-grandpa/rpc/src/report.rs index 8c04ca28e..58e59d8ae 100644 --- a/client/finality-grandpa/rpc/src/report.rs +++ b/client/finality-grandpa/rpc/src/report.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2020-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 // This program is free software: you can redistribute it and/or modify diff --git a/client/finality-grandpa/src/authorities.rs b/client/finality-grandpa/src/authorities.rs index a61c66979..245c6bfe3 100644 --- a/client/finality-grandpa/src/authorities.rs +++ b/client/finality-grandpa/src/authorities.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2018-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 // This program is free software: you can redistribute it and/or modify diff --git a/client/finality-grandpa/src/aux_schema.rs b/client/finality-grandpa/src/aux_schema.rs index a7357a7fa..fbb0ab8a8 100644 --- a/client/finality-grandpa/src/aux_schema.rs +++ b/client/finality-grandpa/src/aux_schema.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2019-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 // This program is free software: you can redistribute it and/or modify diff --git a/client/finality-grandpa/src/communication/gossip.rs b/client/finality-grandpa/src/communication/gossip.rs index cbcafc727..65e765a4c 100644 --- a/client/finality-grandpa/src/communication/gossip.rs +++ b/client/finality-grandpa/src/communication/gossip.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2019-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 // This program is free software: you can redistribute it and/or modify diff --git a/client/finality-grandpa/src/communication/mod.rs b/client/finality-grandpa/src/communication/mod.rs index e7e3c1298..3c1de1f46 100644 --- a/client/finality-grandpa/src/communication/mod.rs +++ b/client/finality-grandpa/src/communication/mod.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2017-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 // This program is free software: you can redistribute it and/or modify diff --git a/client/finality-grandpa/src/communication/periodic.rs b/client/finality-grandpa/src/communication/periodic.rs index c00fed129..f3f757286 100644 --- a/client/finality-grandpa/src/communication/periodic.rs +++ b/client/finality-grandpa/src/communication/periodic.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2019-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 // This program is free software: you can redistribute it and/or modify diff --git a/client/finality-grandpa/src/communication/tests.rs b/client/finality-grandpa/src/communication/tests.rs index 839b2d52b..19c2e4fef 100644 --- a/client/finality-grandpa/src/communication/tests.rs +++ b/client/finality-grandpa/src/communication/tests.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2019-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 // This program is free software: you can redistribute it and/or modify diff --git a/client/finality-grandpa/src/environment.rs b/client/finality-grandpa/src/environment.rs index 9ee7e486c..cdc98fc22 100644 --- a/client/finality-grandpa/src/environment.rs +++ b/client/finality-grandpa/src/environment.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2018-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 // This program is free software: you can redistribute it and/or modify diff --git a/client/finality-grandpa/src/finality_proof.rs b/client/finality-grandpa/src/finality_proof.rs index af3ed6e14..b042d6fce 100644 --- a/client/finality-grandpa/src/finality_proof.rs +++ b/client/finality-grandpa/src/finality_proof.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2018-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 // This program is free software: you can redistribute it and/or modify diff --git a/client/finality-grandpa/src/import.rs b/client/finality-grandpa/src/import.rs index f5e2f9643..19c424637 100644 --- a/client/finality-grandpa/src/import.rs +++ b/client/finality-grandpa/src/import.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2018-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 // This program is free software: you can redistribute it and/or modify diff --git a/client/finality-grandpa/src/justification.rs b/client/finality-grandpa/src/justification.rs index 96c31d4c0..1534ca752 100644 --- a/client/finality-grandpa/src/justification.rs +++ b/client/finality-grandpa/src/justification.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2018-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 // This program is free software: you can redistribute it and/or modify diff --git a/client/finality-grandpa/src/lib.rs b/client/finality-grandpa/src/lib.rs index 15d689e91..fca1a8a83 100644 --- a/client/finality-grandpa/src/lib.rs +++ b/client/finality-grandpa/src/lib.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2018-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 // This program is free software: you can redistribute it and/or modify diff --git a/client/finality-grandpa/src/notification.rs b/client/finality-grandpa/src/notification.rs index 1d6e25e55..de1fba09e 100644 --- a/client/finality-grandpa/src/notification.rs +++ b/client/finality-grandpa/src/notification.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2020-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 // This program is free software: you can redistribute it and/or modify diff --git a/client/finality-grandpa/src/observer.rs b/client/finality-grandpa/src/observer.rs index 96101a8ed..af56f2ac3 100644 --- a/client/finality-grandpa/src/observer.rs +++ b/client/finality-grandpa/src/observer.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2018-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 // This program is free software: you can redistribute it and/or modify diff --git a/client/finality-grandpa/src/tests.rs b/client/finality-grandpa/src/tests.rs index fee3b13ba..1a1f20797 100644 --- a/client/finality-grandpa/src/tests.rs +++ b/client/finality-grandpa/src/tests.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2018-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 // This program is free software: you can redistribute it and/or modify diff --git a/client/finality-grandpa/src/until_imported.rs b/client/finality-grandpa/src/until_imported.rs index 3bca77ae8..f9ecb7df3 100644 --- a/client/finality-grandpa/src/until_imported.rs +++ b/client/finality-grandpa/src/until_imported.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2017-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 // This program is free software: you can redistribute it and/or modify diff --git a/client/finality-grandpa/src/voting_rule.rs b/client/finality-grandpa/src/voting_rule.rs index 6f15a9cdd..27a91d547 100644 --- a/client/finality-grandpa/src/voting_rule.rs +++ b/client/finality-grandpa/src/voting_rule.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2018-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 // This program is free software: you can redistribute it and/or modify diff --git a/client/finality-grandpa/src/warp_proof.rs b/client/finality-grandpa/src/warp_proof.rs index 1169083ea..f73a0a323 100644 --- a/client/finality-grandpa/src/warp_proof.rs +++ b/client/finality-grandpa/src/warp_proof.rs @@ -1,4 +1,4 @@ -// Copyright 2021 Parity Technologies (UK) Ltd. +// Copyright Parity Technologies (UK) Ltd. // This file is part of Substrate. // Substrate is free software: you can redistribute it and/or modify diff --git a/client/informant/src/display.rs b/client/informant/src/display.rs index 4da46ebb6..85be926d9 100644 --- a/client/informant/src/display.rs +++ b/client/informant/src/display.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2019-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 // This program is free software: you can redistribute it and/or modify diff --git a/client/informant/src/lib.rs b/client/informant/src/lib.rs index b03b92686..cb30d08c4 100644 --- a/client/informant/src/lib.rs +++ b/client/informant/src/lib.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2017-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 // This program is free software: you can redistribute it and/or modify diff --git a/client/keystore/src/lib.rs b/client/keystore/src/lib.rs index cf94a16f0..4151f8c4d 100644 --- a/client/keystore/src/lib.rs +++ b/client/keystore/src/lib.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2019-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 // This program is free software: you can redistribute it and/or modify diff --git a/client/keystore/src/local.rs b/client/keystore/src/local.rs index 54ff6a5b1..cd55739a9 100644 --- a/client/keystore/src/local.rs +++ b/client/keystore/src/local.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2019-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/client/merkle-mountain-range/rpc/src/lib.rs b/client/merkle-mountain-range/rpc/src/lib.rs index 5c65be57e..daf2cd1ec 100644 --- a/client/merkle-mountain-range/rpc/src/lib.rs +++ b/client/merkle-mountain-range/rpc/src/lib.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2021-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/client/merkle-mountain-range/src/aux_schema.rs b/client/merkle-mountain-range/src/aux_schema.rs index e6c53d406..436252267 100644 --- a/client/merkle-mountain-range/src/aux_schema.rs +++ b/client/merkle-mountain-range/src/aux_schema.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2019-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 // This program is free software: you can redistribute it and/or modify diff --git a/client/merkle-mountain-range/src/lib.rs b/client/merkle-mountain-range/src/lib.rs index ce7223525..de21b9b49 100644 --- a/client/merkle-mountain-range/src/lib.rs +++ b/client/merkle-mountain-range/src/lib.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2021-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 // This program is free software: you can redistribute it and/or modify diff --git a/client/merkle-mountain-range/src/offchain_mmr.rs b/client/merkle-mountain-range/src/offchain_mmr.rs index bbb53cf3a..33d85f0cb 100644 --- a/client/merkle-mountain-range/src/offchain_mmr.rs +++ b/client/merkle-mountain-range/src/offchain_mmr.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2021-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 // This program is free software: you can redistribute it and/or modify diff --git a/client/merkle-mountain-range/src/test_utils.rs b/client/merkle-mountain-range/src/test_utils.rs index 9decaa3c2..9e00d620a 100644 --- a/client/merkle-mountain-range/src/test_utils.rs +++ b/client/merkle-mountain-range/src/test_utils.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2021-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 // This program is free software: you can redistribute it and/or modify diff --git a/client/network-gossip/src/bridge.rs b/client/network-gossip/src/bridge.rs index 5d7fd96c1..129e18d8a 100644 --- a/client/network-gossip/src/bridge.rs +++ b/client/network-gossip/src/bridge.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2019-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 // This program is free software: you can redistribute it and/or modify diff --git a/client/network-gossip/src/lib.rs b/client/network-gossip/src/lib.rs index 1c8fb8ba0..859ec3d52 100644 --- a/client/network-gossip/src/lib.rs +++ b/client/network-gossip/src/lib.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2019-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 // This program is free software: you can redistribute it and/or modify diff --git a/client/network-gossip/src/state_machine.rs b/client/network-gossip/src/state_machine.rs index 001f2c613..48a1a2e53 100644 --- a/client/network-gossip/src/state_machine.rs +++ b/client/network-gossip/src/state_machine.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2017-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 // This program is free software: you can redistribute it and/or modify diff --git a/client/network-gossip/src/validator.rs b/client/network-gossip/src/validator.rs index 77dcc3bdc..26835a5ae 100644 --- a/client/network-gossip/src/validator.rs +++ b/client/network-gossip/src/validator.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2017-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 // This program is free software: you can redistribute it and/or modify diff --git a/client/network/bitswap/src/lib.rs b/client/network/bitswap/src/lib.rs index 62a18b18c..dd5fc46f6 100644 --- a/client/network/bitswap/src/lib.rs +++ b/client/network/bitswap/src/lib.rs @@ -1,4 +1,4 @@ -// Copyright 2022 Parity Technologies (UK) Ltd. +// Copyright Parity Technologies (UK) Ltd. // This file is part of Substrate. // Substrate is free software: you can redistribute it and/or modify diff --git a/client/network/bitswap/src/schema.rs b/client/network/bitswap/src/schema.rs index 362e59aca..1d62847c2 100644 --- a/client/network/bitswap/src/schema.rs +++ b/client/network/bitswap/src/schema.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2017-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 // This program is free software: you can redistribute it and/or modify diff --git a/client/network/common/src/config.rs b/client/network/common/src/config.rs index c35d8a42a..782b5a965 100644 --- a/client/network/common/src/config.rs +++ b/client/network/common/src/config.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2017-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 // This program is free software: you can redistribute it and/or modify diff --git a/client/network/common/src/error.rs b/client/network/common/src/error.rs index 4326b1af5..712b8c5de 100644 --- a/client/network/common/src/error.rs +++ b/client/network/common/src/error.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2017-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 // This program is free software: you can redistribute it and/or modify diff --git a/client/network/common/src/lib.rs b/client/network/common/src/lib.rs index 36e67f11e..10d244938 100644 --- a/client/network/common/src/lib.rs +++ b/client/network/common/src/lib.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2017-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 // This program is free software: you can redistribute it and/or modify diff --git a/client/network/common/src/message.rs b/client/network/common/src/message.rs index 930fe5ca5..12d2a6800 100644 --- a/client/network/common/src/message.rs +++ b/client/network/common/src/message.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2017-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 // This program is free software: you can redistribute it and/or modify diff --git a/client/network/common/src/protocol.rs b/client/network/common/src/protocol.rs index 04bfaedbc..bfeb1daf5 100644 --- a/client/network/common/src/protocol.rs +++ b/client/network/common/src/protocol.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2017-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 // This program is free software: you can redistribute it and/or modify diff --git a/client/network/common/src/protocol/event.rs b/client/network/common/src/protocol/event.rs index 236913df1..7a14a2801 100644 --- a/client/network/common/src/protocol/event.rs +++ b/client/network/common/src/protocol/event.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2019-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 // This program is free software: you can redistribute it and/or modify diff --git a/client/network/common/src/protocol/role.rs b/client/network/common/src/protocol/role.rs index ed22830fd..cd43f6655 100644 --- a/client/network/common/src/protocol/role.rs +++ b/client/network/common/src/protocol/role.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 // This program is free software: you can redistribute it and/or modify diff --git a/client/network/common/src/request_responses.rs b/client/network/common/src/request_responses.rs index 1a8d48e11..4fa53a736 100644 --- a/client/network/common/src/request_responses.rs +++ b/client/network/common/src/request_responses.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2019-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 // This program is free software: you can redistribute it and/or modify diff --git a/client/network/common/src/service.rs b/client/network/common/src/service.rs index f0f307800..e96a00c40 100644 --- a/client/network/common/src/service.rs +++ b/client/network/common/src/service.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. // -// Copyright (C) 2017-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 // // This program is free software: you can redistribute it and/or modify diff --git a/client/network/common/src/service/signature.rs b/client/network/common/src/service/signature.rs index 602ef3d82..14addafc6 100644 --- a/client/network/common/src/service/signature.rs +++ b/client/network/common/src/service/signature.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. // -// Copyright (C) 2017-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 // // This program is free software: you can redistribute it and/or modify diff --git a/client/network/common/src/sync.rs b/client/network/common/src/sync.rs index 802cf75fc..6a98543d4 100644 --- a/client/network/common/src/sync.rs +++ b/client/network/common/src/sync.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2017-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 // This program is free software: you can redistribute it and/or modify diff --git a/client/network/common/src/sync/message.rs b/client/network/common/src/sync/message.rs index 346f1dbce..7b8be18bd 100644 --- a/client/network/common/src/sync/message.rs +++ b/client/network/common/src/sync/message.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2017-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 // This program is free software: you can redistribute it and/or modify diff --git a/client/network/common/src/sync/metrics.rs b/client/network/common/src/sync/metrics.rs index 15ff090a8..455f57ec3 100644 --- a/client/network/common/src/sync/metrics.rs +++ b/client/network/common/src/sync/metrics.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2017-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 // This program is free software: you can redistribute it and/or modify diff --git a/client/network/common/src/sync/warp.rs b/client/network/common/src/sync/warp.rs index 020642fb4..a906091ff 100644 --- a/client/network/common/src/sync/warp.rs +++ b/client/network/common/src/sync/warp.rs @@ -1,4 +1,4 @@ -// Copyright 2022 Parity Technologies (UK) Ltd. +// Copyright Parity Technologies (UK) Ltd. // This file is part of Substrate. // Substrate is free software: you can redistribute it and/or modify diff --git a/client/network/common/src/utils.rs b/client/network/common/src/utils.rs index d0e61a0d0..c32d264a9 100644 --- a/client/network/common/src/utils.rs +++ b/client/network/common/src/utils.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2019-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 // This program is free software: you can redistribute it and/or modify diff --git a/client/network/light/src/lib.rs b/client/network/light/src/lib.rs index 2b7cf226f..656f658d4 100644 --- a/client/network/light/src/lib.rs +++ b/client/network/light/src/lib.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2017-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 // This program is free software: you can redistribute it and/or modify diff --git a/client/network/light/src/light_client_requests.rs b/client/network/light/src/light_client_requests.rs index 61b549d0f..5b6daf174 100644 --- a/client/network/light/src/light_client_requests.rs +++ b/client/network/light/src/light_client_requests.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2020-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 // This program is free software: you can redistribute it and/or modify diff --git a/client/network/light/src/light_client_requests/handler.rs b/client/network/light/src/light_client_requests/handler.rs index abf012b82..0a1215a1d 100644 --- a/client/network/light/src/light_client_requests/handler.rs +++ b/client/network/light/src/light_client_requests/handler.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2020-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 // This program is free software: you can redistribute it and/or modify diff --git a/client/network/light/src/schema.rs b/client/network/light/src/schema.rs index 1fc91659a..0ef9bac4d 100644 --- a/client/network/light/src/schema.rs +++ b/client/network/light/src/schema.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2017-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 // This program is free software: you can redistribute it and/or modify diff --git a/client/network/src/behaviour.rs b/client/network/src/behaviour.rs index eac74f7e6..1ed8efbcd 100644 --- a/client/network/src/behaviour.rs +++ b/client/network/src/behaviour.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2019-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 // This program is free software: you can redistribute it and/or modify diff --git a/client/network/src/config.rs b/client/network/src/config.rs index 8ca8d75f1..fc5304779 100644 --- a/client/network/src/config.rs +++ b/client/network/src/config.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2017-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 // This program is free software: you can redistribute it and/or modify diff --git a/client/network/src/discovery.rs b/client/network/src/discovery.rs index b8805d1b1..6a8c777ca 100644 --- a/client/network/src/discovery.rs +++ b/client/network/src/discovery.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2019-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 // This program is free software: you can redistribute it and/or modify diff --git a/client/network/src/lib.rs b/client/network/src/lib.rs index f185458e0..8ecb3e549 100644 --- a/client/network/src/lib.rs +++ b/client/network/src/lib.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2017-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 // This program is free software: you can redistribute it and/or modify diff --git a/client/network/src/network_state.rs b/client/network/src/network_state.rs index 57073c57a..cf8b8b55a 100644 --- a/client/network/src/network_state.rs +++ b/client/network/src/network_state.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2017-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 // This program is free software: you can redistribute it and/or modify diff --git a/client/network/src/peer_info.rs b/client/network/src/peer_info.rs index f3402c0af..1cf0c928a 100644 --- a/client/network/src/peer_info.rs +++ b/client/network/src/peer_info.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2019-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 // This program is free software: you can redistribute it and/or modify diff --git a/client/network/src/protocol.rs b/client/network/src/protocol.rs index cd232334e..2443fce81 100644 --- a/client/network/src/protocol.rs +++ b/client/network/src/protocol.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2017-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 // This program is free software: you can redistribute it and/or modify diff --git a/client/network/src/protocol/message.rs b/client/network/src/protocol/message.rs index ef652387d..f7981f171 100644 --- a/client/network/src/protocol/message.rs +++ b/client/network/src/protocol/message.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2017-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 // This program is free software: you can redistribute it and/or modify diff --git a/client/network/src/protocol/notifications.rs b/client/network/src/protocol/notifications.rs index bf183abf1..aa49cfcf9 100644 --- a/client/network/src/protocol/notifications.rs +++ b/client/network/src/protocol/notifications.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2019-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 // This program is free software: you can redistribute it and/or modify diff --git a/client/network/src/protocol/notifications/behaviour.rs b/client/network/src/protocol/notifications/behaviour.rs index c546f6b6f..ebfb95afa 100644 --- a/client/network/src/protocol/notifications/behaviour.rs +++ b/client/network/src/protocol/notifications/behaviour.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2019-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 // This program is free software: you can redistribute it and/or modify diff --git a/client/network/src/protocol/notifications/handler.rs b/client/network/src/protocol/notifications/handler.rs index 57561c7b9..70157386e 100644 --- a/client/network/src/protocol/notifications/handler.rs +++ b/client/network/src/protocol/notifications/handler.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2019-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 // This program is free software: you can redistribute it and/or modify diff --git a/client/network/src/protocol/notifications/tests.rs b/client/network/src/protocol/notifications/tests.rs index 6e8586974..9ca6974e4 100644 --- a/client/network/src/protocol/notifications/tests.rs +++ b/client/network/src/protocol/notifications/tests.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2019-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 // This program is free software: you can redistribute it and/or modify diff --git a/client/network/src/protocol/notifications/upgrade.rs b/client/network/src/protocol/notifications/upgrade.rs index 89fede25f..70c602362 100644 --- a/client/network/src/protocol/notifications/upgrade.rs +++ b/client/network/src/protocol/notifications/upgrade.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2018-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 // This program is free software: you can redistribute it and/or modify diff --git a/client/network/src/protocol/notifications/upgrade/collec.rs b/client/network/src/protocol/notifications/upgrade/collec.rs index 288b8bfd4..89999ff0c 100644 --- a/client/network/src/protocol/notifications/upgrade/collec.rs +++ b/client/network/src/protocol/notifications/upgrade/collec.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2018-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 // This program is free software: you can redistribute it and/or modify diff --git a/client/network/src/protocol/notifications/upgrade/notifications.rs b/client/network/src/protocol/notifications/upgrade/notifications.rs index 71afc3c90..bfdd9ccfa 100644 --- a/client/network/src/protocol/notifications/upgrade/notifications.rs +++ b/client/network/src/protocol/notifications/upgrade/notifications.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2019-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 // This program is free software: you can redistribute it and/or modify diff --git a/client/network/src/request_responses.rs b/client/network/src/request_responses.rs index 45f103040..37c97e828 100644 --- a/client/network/src/request_responses.rs +++ b/client/network/src/request_responses.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2019-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 // This program is free software: you can redistribute it and/or modify diff --git a/client/network/src/service.rs b/client/network/src/service.rs index 3c8856eaf..fd0b965b7 100644 --- a/client/network/src/service.rs +++ b/client/network/src/service.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2017-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 // This program is free software: you can redistribute it and/or modify diff --git a/client/network/src/service/metrics.rs b/client/network/src/service/metrics.rs index a099bba71..ccb440b2c 100644 --- a/client/network/src/service/metrics.rs +++ b/client/network/src/service/metrics.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2017-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 // This program is free software: you can redistribute it and/or modify diff --git a/client/network/src/service/out_events.rs b/client/network/src/service/out_events.rs index 5e0c8ac6a..57a5092ae 100644 --- a/client/network/src/service/out_events.rs +++ b/client/network/src/service/out_events.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2017-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 // This program is free software: you can redistribute it and/or modify diff --git a/client/network/src/service/tests/chain_sync.rs b/client/network/src/service/tests/chain_sync.rs index 98884024a..a369b7174 100644 --- a/client/network/src/service/tests/chain_sync.rs +++ b/client/network/src/service/tests/chain_sync.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 // This program is free software: you can redistribute it and/or modify diff --git a/client/network/src/service/tests/mod.rs b/client/network/src/service/tests/mod.rs index 3ce139ff0..3233b1584 100644 --- a/client/network/src/service/tests/mod.rs +++ b/client/network/src/service/tests/mod.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2017-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 // This program is free software: you can redistribute it and/or modify diff --git a/client/network/src/service/tests/service.rs b/client/network/src/service/tests/service.rs index bdf300963..1c7b32ff0 100644 --- a/client/network/src/service/tests/service.rs +++ b/client/network/src/service/tests/service.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2017-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 // This program is free software: you can redistribute it and/or modify diff --git a/client/network/src/transport.rs b/client/network/src/transport.rs index 51a8483e0..3aa0bb48d 100644 --- a/client/network/src/transport.rs +++ b/client/network/src/transport.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2018-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 // This program is free software: you can redistribute it and/or modify diff --git a/client/network/sync/src/block_request_handler.rs b/client/network/sync/src/block_request_handler.rs index 84755453f..38aca2cde 100644 --- a/client/network/sync/src/block_request_handler.rs +++ b/client/network/sync/src/block_request_handler.rs @@ -1,4 +1,4 @@ -// Copyright 2020 Parity Technologies (UK) Ltd. +// Copyright Parity Technologies (UK) Ltd. // This file is part of Substrate. // Substrate is free software: you can redistribute it and/or modify diff --git a/client/network/sync/src/blocks.rs b/client/network/sync/src/blocks.rs index b8acd61a2..4cc1ca080 100644 --- a/client/network/sync/src/blocks.rs +++ b/client/network/sync/src/blocks.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2017-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 // This program is free software: you can redistribute it and/or modify diff --git a/client/network/sync/src/extra_requests.rs b/client/network/sync/src/extra_requests.rs index 0506bd542..09e6bdb57 100644 --- a/client/network/sync/src/extra_requests.rs +++ b/client/network/sync/src/extra_requests.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2017-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 // This program is free software: you can redistribute it and/or modify diff --git a/client/network/sync/src/lib.rs b/client/network/sync/src/lib.rs index 1f4f6a04e..5c1496e75 100644 --- a/client/network/sync/src/lib.rs +++ b/client/network/sync/src/lib.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2017-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 // This program is free software: you can redistribute it and/or modify diff --git a/client/network/sync/src/mock.rs b/client/network/sync/src/mock.rs index b59ea7e4f..5300638ce 100644 --- a/client/network/sync/src/mock.rs +++ b/client/network/sync/src/mock.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 // This program is free software: you can redistribute it and/or modify diff --git a/client/network/sync/src/schema.rs b/client/network/sync/src/schema.rs index b31005360..22b7ee592 100644 --- a/client/network/sync/src/schema.rs +++ b/client/network/sync/src/schema.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2017-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 // This program is free software: you can redistribute it and/or modify diff --git a/client/network/sync/src/service/chain_sync.rs b/client/network/sync/src/service/chain_sync.rs index 824303ec0..4d47899a3 100644 --- a/client/network/sync/src/service/chain_sync.rs +++ b/client/network/sync/src/service/chain_sync.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 // This program is free software: you can redistribute it and/or modify diff --git a/client/network/sync/src/service/mock.rs b/client/network/sync/src/service/mock.rs index d8aad2fa7..e66a9e561 100644 --- a/client/network/sync/src/service/mock.rs +++ b/client/network/sync/src/service/mock.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 // This program is free software: you can redistribute it and/or modify diff --git a/client/network/sync/src/service/mod.rs b/client/network/sync/src/service/mod.rs index 692aa2698..18331d63e 100644 --- a/client/network/sync/src/service/mod.rs +++ b/client/network/sync/src/service/mod.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 // This program is free software: you can redistribute it and/or modify diff --git a/client/network/sync/src/service/network.rs b/client/network/sync/src/service/network.rs index b81a65ae7..de7e255f5 100644 --- a/client/network/sync/src/service/network.rs +++ b/client/network/sync/src/service/network.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 // This program is free software: you can redistribute it and/or modify diff --git a/client/network/sync/src/state.rs b/client/network/sync/src/state.rs index 9f64b5233..0fcf17158 100644 --- a/client/network/sync/src/state.rs +++ b/client/network/sync/src/state.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2017-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 // This program is free software: you can redistribute it and/or modify diff --git a/client/network/sync/src/state_request_handler.rs b/client/network/sync/src/state_request_handler.rs index 98f1ed34d..2f615f4ea 100644 --- a/client/network/sync/src/state_request_handler.rs +++ b/client/network/sync/src/state_request_handler.rs @@ -1,4 +1,4 @@ -// Copyright 2020 Parity Technologies (UK) Ltd. +// Copyright Parity Technologies (UK) Ltd. // This file is part of Substrate. // Substrate is free software: you can redistribute it and/or modify diff --git a/client/network/sync/src/tests.rs b/client/network/sync/src/tests.rs index 61de08443..d56e84093 100644 --- a/client/network/sync/src/tests.rs +++ b/client/network/sync/src/tests.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2017-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 // This program is free software: you can redistribute it and/or modify diff --git a/client/network/sync/src/warp.rs b/client/network/sync/src/warp.rs index bb64ff6a1..21af50787 100644 --- a/client/network/sync/src/warp.rs +++ b/client/network/sync/src/warp.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2021-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 // This program is free software: you can redistribute it and/or modify diff --git a/client/network/sync/src/warp_request_handler.rs b/client/network/sync/src/warp_request_handler.rs index e675bf45c..fcb0d116b 100644 --- a/client/network/sync/src/warp_request_handler.rs +++ b/client/network/sync/src/warp_request_handler.rs @@ -1,4 +1,4 @@ -// Copyright 2022 Parity Technologies (UK) Ltd. +// Copyright Parity Technologies (UK) Ltd. // This file is part of Substrate. // Substrate is free software: you can redistribute it and/or modify diff --git a/client/network/test/src/block_import.rs b/client/network/test/src/block_import.rs index 250f5f5fd..25e3b9bee 100644 --- a/client/network/test/src/block_import.rs +++ b/client/network/test/src/block_import.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2017-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 // This program is free software: you can redistribute it and/or modify diff --git a/client/network/test/src/lib.rs b/client/network/test/src/lib.rs index 3cd508103..544f4c5f6 100644 --- a/client/network/test/src/lib.rs +++ b/client/network/test/src/lib.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2017-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 // This program is free software: you can redistribute it and/or modify diff --git a/client/network/test/src/sync.rs b/client/network/test/src/sync.rs index 8c2306403..490ec134c 100644 --- a/client/network/test/src/sync.rs +++ b/client/network/test/src/sync.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2017-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 // This program is free software: you can redistribute it and/or modify diff --git a/client/network/transactions/src/config.rs b/client/network/transactions/src/config.rs index abb8cccd3..fdf81fcd9 100644 --- a/client/network/transactions/src/config.rs +++ b/client/network/transactions/src/config.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2017-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 // This program is free software: you can redistribute it and/or modify diff --git a/client/network/transactions/src/lib.rs b/client/network/transactions/src/lib.rs index a5adb274d..d4d08d2ab 100644 --- a/client/network/transactions/src/lib.rs +++ b/client/network/transactions/src/lib.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2017-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 // This program is free software: you can redistribute it and/or modify diff --git a/client/offchain/src/api.rs b/client/offchain/src/api.rs index cd9f5c8f6..6fdd91cda 100644 --- a/client/offchain/src/api.rs +++ b/client/offchain/src/api.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2019-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 // This program is free software: you can redistribute it and/or modify diff --git a/client/offchain/src/api/http.rs b/client/offchain/src/api/http.rs index a47adb3e8..e3872614e 100644 --- a/client/offchain/src/api/http.rs +++ b/client/offchain/src/api/http.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2019-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 // This program is free software: you can redistribute it and/or modify diff --git a/client/offchain/src/api/timestamp.rs b/client/offchain/src/api/timestamp.rs index 4b3f5efdd..0087ff592 100644 --- a/client/offchain/src/api/timestamp.rs +++ b/client/offchain/src/api/timestamp.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2019-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 // This program is free software: you can redistribute it and/or modify diff --git a/client/offchain/src/lib.rs b/client/offchain/src/lib.rs index 904d127e1..c69f01de1 100644 --- a/client/offchain/src/lib.rs +++ b/client/offchain/src/lib.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2019-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 // This program is free software: you can redistribute it and/or modify diff --git a/client/peerset/src/lib.rs b/client/peerset/src/lib.rs index 9b1dc6a2d..e5393acba 100644 --- a/client/peerset/src/lib.rs +++ b/client/peerset/src/lib.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2018-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 // This program is free software: you can redistribute it and/or modify diff --git a/client/peerset/src/peersstate.rs b/client/peerset/src/peersstate.rs index c9af5b8e2..84907ac91 100644 --- a/client/peerset/src/peersstate.rs +++ b/client/peerset/src/peersstate.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2019-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 // This program is free software: you can redistribute it and/or modify diff --git a/client/peerset/tests/fuzz.rs b/client/peerset/tests/fuzz.rs index 48c5cb341..1f4bd053b 100644 --- a/client/peerset/tests/fuzz.rs +++ b/client/peerset/tests/fuzz.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2018-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 // This program is free software: you can redistribute it and/or modify diff --git a/client/proposer-metrics/src/lib.rs b/client/proposer-metrics/src/lib.rs index c27d16ea0..012e8ca76 100644 --- a/client/proposer-metrics/src/lib.rs +++ b/client/proposer-metrics/src/lib.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2020-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 // This program is free software: you can redistribute it and/or modify diff --git a/client/rpc-api/src/author/error.rs b/client/rpc-api/src/author/error.rs index 57a27d48d..7ca96bbab 100644 --- a/client/rpc-api/src/author/error.rs +++ b/client/rpc-api/src/author/error.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2017-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 // This program is free software: you can redistribute it and/or modify diff --git a/client/rpc-api/src/author/hash.rs b/client/rpc-api/src/author/hash.rs index 453947dd2..5c9542ffe 100644 --- a/client/rpc-api/src/author/hash.rs +++ b/client/rpc-api/src/author/hash.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2019-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 // This program is free software: you can redistribute it and/or modify diff --git a/client/rpc-api/src/author/mod.rs b/client/rpc-api/src/author/mod.rs index feba7640e..55881e621 100644 --- a/client/rpc-api/src/author/mod.rs +++ b/client/rpc-api/src/author/mod.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2017-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 // This program is free software: you can redistribute it and/or modify diff --git a/client/rpc-api/src/chain/error.rs b/client/rpc-api/src/chain/error.rs index 670e221cf..cfb429bcf 100644 --- a/client/rpc-api/src/chain/error.rs +++ b/client/rpc-api/src/chain/error.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2017-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 // This program is free software: you can redistribute it and/or modify diff --git a/client/rpc-api/src/chain/mod.rs b/client/rpc-api/src/chain/mod.rs index c7cc97463..2ed767c37 100644 --- a/client/rpc-api/src/chain/mod.rs +++ b/client/rpc-api/src/chain/mod.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2017-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 // This program is free software: you can redistribute it and/or modify diff --git a/client/rpc-api/src/child_state/mod.rs b/client/rpc-api/src/child_state/mod.rs index be279a974..a184677a7 100644 --- a/client/rpc-api/src/child_state/mod.rs +++ b/client/rpc-api/src/child_state/mod.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2017-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 // This program is free software: you can redistribute it and/or modify diff --git a/client/rpc-api/src/dev/error.rs b/client/rpc-api/src/dev/error.rs index 43fd3325f..2896e66bc 100644 --- a/client/rpc-api/src/dev/error.rs +++ b/client/rpc-api/src/dev/error.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 // This program is free software: you can redistribute it and/or modify diff --git a/client/rpc-api/src/dev/mod.rs b/client/rpc-api/src/dev/mod.rs index afd83272a..bc7216199 100644 --- a/client/rpc-api/src/dev/mod.rs +++ b/client/rpc-api/src/dev/mod.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 // This program is free software: you can redistribute it and/or modify diff --git a/client/rpc-api/src/lib.rs b/client/rpc-api/src/lib.rs index a0cbbcee8..bc76d029a 100644 --- a/client/rpc-api/src/lib.rs +++ b/client/rpc-api/src/lib.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2019-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 // This program is free software: you can redistribute it and/or modify diff --git a/client/rpc-api/src/offchain/error.rs b/client/rpc-api/src/offchain/error.rs index be72e05fc..5ca047608 100644 --- a/client/rpc-api/src/offchain/error.rs +++ b/client/rpc-api/src/offchain/error.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2020-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 // This program is free software: you can redistribute it and/or modify diff --git a/client/rpc-api/src/offchain/mod.rs b/client/rpc-api/src/offchain/mod.rs index d9435d9a8..cd42d6db3 100644 --- a/client/rpc-api/src/offchain/mod.rs +++ b/client/rpc-api/src/offchain/mod.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2020-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 // This program is free software: you can redistribute it and/or modify diff --git a/client/rpc-api/src/policy.rs b/client/rpc-api/src/policy.rs index 69ca89585..799898fb7 100644 --- a/client/rpc-api/src/policy.rs +++ b/client/rpc-api/src/policy.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2020-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 // This program is free software: you can redistribute it and/or modify diff --git a/client/rpc-api/src/state/error.rs b/client/rpc-api/src/state/error.rs index b1df64b47..c69b3d919 100644 --- a/client/rpc-api/src/state/error.rs +++ b/client/rpc-api/src/state/error.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2017-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 // This program is free software: you can redistribute it and/or modify diff --git a/client/rpc-api/src/state/helpers.rs b/client/rpc-api/src/state/helpers.rs index 25f07be1c..de20ee6f1 100644 --- a/client/rpc-api/src/state/helpers.rs +++ b/client/rpc-api/src/state/helpers.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2020-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 // This program is free software: you can redistribute it and/or modify diff --git a/client/rpc-api/src/state/mod.rs b/client/rpc-api/src/state/mod.rs index 323e6ad1d..ebc1eb9b4 100644 --- a/client/rpc-api/src/state/mod.rs +++ b/client/rpc-api/src/state/mod.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2017-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 // This program is free software: you can redistribute it and/or modify diff --git a/client/rpc-api/src/system/error.rs b/client/rpc-api/src/system/error.rs index 777f8c6c6..4ad0f1b69 100644 --- a/client/rpc-api/src/system/error.rs +++ b/client/rpc-api/src/system/error.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2017-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 // This program is free software: you can redistribute it and/or modify diff --git a/client/rpc-api/src/system/helpers.rs b/client/rpc-api/src/system/helpers.rs index 7ddb3f813..ad56dc385 100644 --- a/client/rpc-api/src/system/helpers.rs +++ b/client/rpc-api/src/system/helpers.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2017-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 // This program is free software: you can redistribute it and/or modify diff --git a/client/rpc-api/src/system/mod.rs b/client/rpc-api/src/system/mod.rs index 1e12d5be8..bf2e92bc2 100644 --- a/client/rpc-api/src/system/mod.rs +++ b/client/rpc-api/src/system/mod.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2017-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 // This program is free software: you can redistribute it and/or modify diff --git a/client/rpc-servers/src/lib.rs b/client/rpc-servers/src/lib.rs index 1fa2ba81d..62a9b4fc4 100644 --- a/client/rpc-servers/src/lib.rs +++ b/client/rpc-servers/src/lib.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2017-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 // This program is free software: you can redistribute it and/or modify diff --git a/client/rpc-servers/src/middleware.rs b/client/rpc-servers/src/middleware.rs index 1c25ac1df..c3e17c785 100644 --- a/client/rpc-servers/src/middleware.rs +++ b/client/rpc-servers/src/middleware.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2020-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 // This program is free software: you can redistribute it and/or modify diff --git a/client/rpc-spec-v2/src/chain_head/api.rs b/client/rpc-spec-v2/src/chain_head/api.rs index 7e72d6d77..ad1e58500 100644 --- a/client/rpc-spec-v2/src/chain_head/api.rs +++ b/client/rpc-spec-v2/src/chain_head/api.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 // This program is free software: you can redistribute it and/or modify diff --git a/client/rpc-spec-v2/src/chain_head/chain_head.rs b/client/rpc-spec-v2/src/chain_head/chain_head.rs index 76963d81e..4dbdf277e 100644 --- a/client/rpc-spec-v2/src/chain_head/chain_head.rs +++ b/client/rpc-spec-v2/src/chain_head/chain_head.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 // This program is free software: you can redistribute it and/or modify diff --git a/client/rpc-spec-v2/src/chain_head/error.rs b/client/rpc-spec-v2/src/chain_head/error.rs index 92f336ed4..3f31d985d 100644 --- a/client/rpc-spec-v2/src/chain_head/error.rs +++ b/client/rpc-spec-v2/src/chain_head/error.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 // This program is free software: you can redistribute it and/or modify diff --git a/client/rpc-spec-v2/src/chain_head/event.rs b/client/rpc-spec-v2/src/chain_head/event.rs index 25930cb6f..2e070c7ce 100644 --- a/client/rpc-spec-v2/src/chain_head/event.rs +++ b/client/rpc-spec-v2/src/chain_head/event.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 // This program is free software: you can redistribute it and/or modify diff --git a/client/rpc-spec-v2/src/chain_head/mod.rs b/client/rpc-spec-v2/src/chain_head/mod.rs index a25933b40..3d6d5a916 100644 --- a/client/rpc-spec-v2/src/chain_head/mod.rs +++ b/client/rpc-spec-v2/src/chain_head/mod.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 // This program is free software: you can redistribute it and/or modify diff --git a/client/rpc-spec-v2/src/chain_head/subscription.rs b/client/rpc-spec-v2/src/chain_head/subscription.rs index 033db45ca..e52336097 100644 --- a/client/rpc-spec-v2/src/chain_head/subscription.rs +++ b/client/rpc-spec-v2/src/chain_head/subscription.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 // This program is free software: you can redistribute it and/or modify diff --git a/client/rpc-spec-v2/src/chain_spec/api.rs b/client/rpc-spec-v2/src/chain_spec/api.rs index dfe2d76de..66c9f8680 100644 --- a/client/rpc-spec-v2/src/chain_spec/api.rs +++ b/client/rpc-spec-v2/src/chain_spec/api.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 // This program is free software: you can redistribute it and/or modify diff --git a/client/rpc-spec-v2/src/chain_spec/chain_spec.rs b/client/rpc-spec-v2/src/chain_spec/chain_spec.rs index 90d05f1d9..99ea34521 100644 --- a/client/rpc-spec-v2/src/chain_spec/chain_spec.rs +++ b/client/rpc-spec-v2/src/chain_spec/chain_spec.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 // This program is free software: you can redistribute it and/or modify diff --git a/client/rpc-spec-v2/src/chain_spec/mod.rs b/client/rpc-spec-v2/src/chain_spec/mod.rs index cd4fcf246..0dfbdf1b1 100644 --- a/client/rpc-spec-v2/src/chain_spec/mod.rs +++ b/client/rpc-spec-v2/src/chain_spec/mod.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 // This program is free software: you can redistribute it and/or modify diff --git a/client/rpc-spec-v2/src/chain_spec/tests.rs b/client/rpc-spec-v2/src/chain_spec/tests.rs index 6f662ba42..74aec01a2 100644 --- a/client/rpc-spec-v2/src/chain_spec/tests.rs +++ b/client/rpc-spec-v2/src/chain_spec/tests.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 // This program is free software: you can redistribute it and/or modify diff --git a/client/rpc-spec-v2/src/lib.rs b/client/rpc-spec-v2/src/lib.rs index 5af7e3be2..7c22ef5d5 100644 --- a/client/rpc-spec-v2/src/lib.rs +++ b/client/rpc-spec-v2/src/lib.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 // This program is free software: you can redistribute it and/or modify diff --git a/client/rpc-spec-v2/src/transaction/api.rs b/client/rpc-spec-v2/src/transaction/api.rs index 2f0c799f1..c226ab867 100644 --- a/client/rpc-spec-v2/src/transaction/api.rs +++ b/client/rpc-spec-v2/src/transaction/api.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 // This program is free software: you can redistribute it and/or modify diff --git a/client/rpc-spec-v2/src/transaction/error.rs b/client/rpc-spec-v2/src/transaction/error.rs index 901d7c58b..d2de07afd 100644 --- a/client/rpc-spec-v2/src/transaction/error.rs +++ b/client/rpc-spec-v2/src/transaction/error.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 // This program is free software: you can redistribute it and/or modify diff --git a/client/rpc-spec-v2/src/transaction/event.rs b/client/rpc-spec-v2/src/transaction/event.rs index 3c75eaff1..bdc126366 100644 --- a/client/rpc-spec-v2/src/transaction/event.rs +++ b/client/rpc-spec-v2/src/transaction/event.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 // This program is free software: you can redistribute it and/or modify diff --git a/client/rpc-spec-v2/src/transaction/mod.rs b/client/rpc-spec-v2/src/transaction/mod.rs index bb983894a..212912ba1 100644 --- a/client/rpc-spec-v2/src/transaction/mod.rs +++ b/client/rpc-spec-v2/src/transaction/mod.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 // This program is free software: you can redistribute it and/or modify diff --git a/client/rpc-spec-v2/src/transaction/transaction.rs b/client/rpc-spec-v2/src/transaction/transaction.rs index e2cf736df..44f4bd36c 100644 --- a/client/rpc-spec-v2/src/transaction/transaction.rs +++ b/client/rpc-spec-v2/src/transaction/transaction.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 // This program is free software: you can redistribute it and/or modify diff --git a/client/rpc/src/author/mod.rs b/client/rpc/src/author/mod.rs index cf47e9faf..2bb88352e 100644 --- a/client/rpc/src/author/mod.rs +++ b/client/rpc/src/author/mod.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2017-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 // This program is free software: you can redistribute it and/or modify diff --git a/client/rpc/src/author/tests.rs b/client/rpc/src/author/tests.rs index 573d01630..bf880cfbc 100644 --- a/client/rpc/src/author/tests.rs +++ b/client/rpc/src/author/tests.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2017-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 // This program is free software: you can redistribute it and/or modify diff --git a/client/rpc/src/chain/chain_full.rs b/client/rpc/src/chain/chain_full.rs index 6ff544b0d..a88291eb7 100644 --- a/client/rpc/src/chain/chain_full.rs +++ b/client/rpc/src/chain/chain_full.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2019-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 // This program is free software: you can redistribute it and/or modify diff --git a/client/rpc/src/chain/mod.rs b/client/rpc/src/chain/mod.rs index 16e69c0f6..d54811320 100644 --- a/client/rpc/src/chain/mod.rs +++ b/client/rpc/src/chain/mod.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2017-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 // This program is free software: you can redistribute it and/or modify diff --git a/client/rpc/src/chain/tests.rs b/client/rpc/src/chain/tests.rs index 224d021f9..75211a43b 100644 --- a/client/rpc/src/chain/tests.rs +++ b/client/rpc/src/chain/tests.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2017-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 // This program is free software: you can redistribute it and/or modify diff --git a/client/rpc/src/dev/mod.rs b/client/rpc/src/dev/mod.rs index fa5d358b0..4d2e8618d 100644 --- a/client/rpc/src/dev/mod.rs +++ b/client/rpc/src/dev/mod.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2017-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 // This program is free software: you can redistribute it and/or modify diff --git a/client/rpc/src/dev/tests.rs b/client/rpc/src/dev/tests.rs index 816f3cdfe..9beb01182 100644 --- a/client/rpc/src/dev/tests.rs +++ b/client/rpc/src/dev/tests.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2017-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 // This program is free software: you can redistribute it and/or modify diff --git a/client/rpc/src/lib.rs b/client/rpc/src/lib.rs index a0e810eaf..6230ef664 100644 --- a/client/rpc/src/lib.rs +++ b/client/rpc/src/lib.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2017-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 // This program is free software: you can redistribute it and/or modify diff --git a/client/rpc/src/offchain/mod.rs b/client/rpc/src/offchain/mod.rs index 6896b8261..de711accd 100644 --- a/client/rpc/src/offchain/mod.rs +++ b/client/rpc/src/offchain/mod.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2020-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 // This program is free software: you can redistribute it and/or modify diff --git a/client/rpc/src/offchain/tests.rs b/client/rpc/src/offchain/tests.rs index 28a7b6115..62a846d08 100644 --- a/client/rpc/src/offchain/tests.rs +++ b/client/rpc/src/offchain/tests.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2020-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 // This program is free software: you can redistribute it and/or modify diff --git a/client/rpc/src/state/mod.rs b/client/rpc/src/state/mod.rs index 9ba4c8218..12d59ad3b 100644 --- a/client/rpc/src/state/mod.rs +++ b/client/rpc/src/state/mod.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2017-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 // This program is free software: you can redistribute it and/or modify diff --git a/client/rpc/src/state/state_full.rs b/client/rpc/src/state/state_full.rs index b0c0922c6..ffc97487a 100644 --- a/client/rpc/src/state/state_full.rs +++ b/client/rpc/src/state/state_full.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2019-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 // This program is free software: you can redistribute it and/or modify diff --git a/client/rpc/src/state/tests.rs b/client/rpc/src/state/tests.rs index fe8bdf0ac..53f8f1d48 100644 --- a/client/rpc/src/state/tests.rs +++ b/client/rpc/src/state/tests.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2017-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 // This program is free software: you can redistribute it and/or modify diff --git a/client/rpc/src/state/utils.rs b/client/rpc/src/state/utils.rs index 81476cdd3..9fc21b72b 100644 --- a/client/rpc/src/state/utils.rs +++ b/client/rpc/src/state/utils.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2017-2023 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 // This program is free software: you can redistribute it and/or modify diff --git a/client/rpc/src/system/mod.rs b/client/rpc/src/system/mod.rs index ea24524cd..0da4f8d0e 100644 --- a/client/rpc/src/system/mod.rs +++ b/client/rpc/src/system/mod.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2017-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 // This program is free software: you can redistribute it and/or modify diff --git a/client/rpc/src/system/tests.rs b/client/rpc/src/system/tests.rs index 4da49cdd1..c0ee1e8f8 100644 --- a/client/rpc/src/system/tests.rs +++ b/client/rpc/src/system/tests.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2017-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 // This program is free software: you can redistribute it and/or modify diff --git a/client/rpc/src/testing.rs b/client/rpc/src/testing.rs index 584e4a990..6b6e1906d 100644 --- a/client/rpc/src/testing.rs +++ b/client/rpc/src/testing.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2017-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 // This program is free software: you can redistribute it and/or modify diff --git a/client/service/src/builder.rs b/client/service/src/builder.rs index fb80753b4..2c690dcd2 100644 --- a/client/service/src/builder.rs +++ b/client/service/src/builder.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2017-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 // This program is free software: you can redistribute it and/or modify diff --git a/client/service/src/chain_ops/check_block.rs b/client/service/src/chain_ops/check_block.rs index c3380e547..a14f535b9 100644 --- a/client/service/src/chain_ops/check_block.rs +++ b/client/service/src/chain_ops/check_block.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2017-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 // This program is free software: you can redistribute it and/or modify diff --git a/client/service/src/chain_ops/export_blocks.rs b/client/service/src/chain_ops/export_blocks.rs index 9638722ef..8d66f1f96 100644 --- a/client/service/src/chain_ops/export_blocks.rs +++ b/client/service/src/chain_ops/export_blocks.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2017-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 // This program is free software: you can redistribute it and/or modify diff --git a/client/service/src/chain_ops/export_raw_state.rs b/client/service/src/chain_ops/export_raw_state.rs index ca7a07008..f5e154795 100644 --- a/client/service/src/chain_ops/export_raw_state.rs +++ b/client/service/src/chain_ops/export_raw_state.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2020-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 // This program is free software: you can redistribute it and/or modify diff --git a/client/service/src/chain_ops/import_blocks.rs b/client/service/src/chain_ops/import_blocks.rs index ca09c1658..34f7669d0 100644 --- a/client/service/src/chain_ops/import_blocks.rs +++ b/client/service/src/chain_ops/import_blocks.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2017-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 // This program is free software: you can redistribute it and/or modify diff --git a/client/service/src/chain_ops/mod.rs b/client/service/src/chain_ops/mod.rs index 98245eeb9..9ee6848ad 100644 --- a/client/service/src/chain_ops/mod.rs +++ b/client/service/src/chain_ops/mod.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2020-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 // This program is free software: you can redistribute it and/or modify diff --git a/client/service/src/chain_ops/revert_chain.rs b/client/service/src/chain_ops/revert_chain.rs index 3ee4399d0..f3c344a75 100644 --- a/client/service/src/chain_ops/revert_chain.rs +++ b/client/service/src/chain_ops/revert_chain.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2017-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 // This program is free software: you can redistribute it and/or modify diff --git a/client/service/src/client/block_rules.rs b/client/service/src/client/block_rules.rs index 2ed27b8fe..532cde1ae 100644 --- a/client/service/src/client/block_rules.rs +++ b/client/service/src/client/block_rules.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2020-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 // This program is free software: you can redistribute it and/or modify diff --git a/client/service/src/client/call_executor.rs b/client/service/src/client/call_executor.rs index 49b969e3e..8942b20bd 100644 --- a/client/service/src/client/call_executor.rs +++ b/client/service/src/client/call_executor.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2017-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 // This program is free software: you can redistribute it and/or modify diff --git a/client/service/src/client/client.rs b/client/service/src/client/client.rs index 615817cc3..968044ba5 100644 --- a/client/service/src/client/client.rs +++ b/client/service/src/client/client.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2017-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 // This program is free software: you can redistribute it and/or modify diff --git a/client/service/src/client/genesis.rs b/client/service/src/client/genesis.rs index 54ef1182b..b3ed46c15 100644 --- a/client/service/src/client/genesis.rs +++ b/client/service/src/client/genesis.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2017-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 // This program is free software: you can redistribute it and/or modify diff --git a/client/service/src/client/mod.rs b/client/service/src/client/mod.rs index dafeb6883..38d818c49 100644 --- a/client/service/src/client/mod.rs +++ b/client/service/src/client/mod.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2017-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 // This program is free software: you can redistribute it and/or modify diff --git a/client/service/src/client/wasm_override.rs b/client/service/src/client/wasm_override.rs index 03a5704ad..3a56e5c59 100644 --- a/client/service/src/client/wasm_override.rs +++ b/client/service/src/client/wasm_override.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2020-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 // This program is free software: you can redistribute it and/or modify diff --git a/client/service/src/client/wasm_substitutes.rs b/client/service/src/client/wasm_substitutes.rs index ed931d3da..a792ab87e 100644 --- a/client/service/src/client/wasm_substitutes.rs +++ b/client/service/src/client/wasm_substitutes.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2021-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 // This program is free software: you can redistribute it and/or modify diff --git a/client/service/src/config.rs b/client/service/src/config.rs index efadb8433..7f3a2e3c0 100644 --- a/client/service/src/config.rs +++ b/client/service/src/config.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2017-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 // This program is free software: you can redistribute it and/or modify diff --git a/client/service/src/error.rs b/client/service/src/error.rs index ec2951193..c61c51640 100644 --- a/client/service/src/error.rs +++ b/client/service/src/error.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2017-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 // This program is free software: you can redistribute it and/or modify diff --git a/client/service/src/lib.rs b/client/service/src/lib.rs index 253479abc..af0b11f20 100644 --- a/client/service/src/lib.rs +++ b/client/service/src/lib.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2017-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 // This program is free software: you can redistribute it and/or modify diff --git a/client/service/src/metrics.rs b/client/service/src/metrics.rs index c83b3988f..19c64e953 100644 --- a/client/service/src/metrics.rs +++ b/client/service/src/metrics.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2020-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 // This program is free software: you can redistribute it and/or modify diff --git a/client/service/src/task_manager/mod.rs b/client/service/src/task_manager/mod.rs index f2987e684..8dc4748b0 100644 --- a/client/service/src/task_manager/mod.rs +++ b/client/service/src/task_manager/mod.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2020-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 // This program is free software: you can redistribute it and/or modify diff --git a/client/service/src/task_manager/prometheus_future.rs b/client/service/src/task_manager/prometheus_future.rs index 88296a673..51bf7fea4 100644 --- a/client/service/src/task_manager/prometheus_future.rs +++ b/client/service/src/task_manager/prometheus_future.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2020-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 // This program is free software: you can redistribute it and/or modify diff --git a/client/service/src/task_manager/tests.rs b/client/service/src/task_manager/tests.rs index 323110a4e..27be4f9a8 100644 --- a/client/service/src/task_manager/tests.rs +++ b/client/service/src/task_manager/tests.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2020-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 // This program is free software: you can redistribute it and/or modify diff --git a/client/service/test/src/client/db.rs b/client/service/test/src/client/db.rs index 5c1315fc8..def1a41e6 100644 --- a/client/service/test/src/client/db.rs +++ b/client/service/test/src/client/db.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2018-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 // This program is free software: you can redistribute it and/or modify diff --git a/client/service/test/src/client/mod.rs b/client/service/test/src/client/mod.rs index 0fe4760fc..9d021be5b 100644 --- a/client/service/test/src/client/mod.rs +++ b/client/service/test/src/client/mod.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2018-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 // This program is free software: you can redistribute it and/or modify diff --git a/client/service/test/src/lib.rs b/client/service/test/src/lib.rs index 6533fe139..655aff242 100644 --- a/client/service/test/src/lib.rs +++ b/client/service/test/src/lib.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2018-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 // This program is free software: you can redistribute it and/or modify diff --git a/client/state-db/src/lib.rs b/client/state-db/src/lib.rs index b6106ee81..14b4622ac 100644 --- a/client/state-db/src/lib.rs +++ b/client/state-db/src/lib.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2017-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 // This program is free software: you can redistribute it and/or modify diff --git a/client/state-db/src/noncanonical.rs b/client/state-db/src/noncanonical.rs index d6cc49a3d..bdbe83183 100644 --- a/client/state-db/src/noncanonical.rs +++ b/client/state-db/src/noncanonical.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2017-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 // This program is free software: you can redistribute it and/or modify diff --git a/client/state-db/src/pruning.rs b/client/state-db/src/pruning.rs index 16561bbe0..7bee2b1d9 100644 --- a/client/state-db/src/pruning.rs +++ b/client/state-db/src/pruning.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2017-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 // This program is free software: you can redistribute it and/or modify diff --git a/client/state-db/src/test.rs b/client/state-db/src/test.rs index 314ec2902..74571145b 100644 --- a/client/state-db/src/test.rs +++ b/client/state-db/src/test.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2017-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 // This program is free software: you can redistribute it and/or modify diff --git a/client/storage-monitor/src/lib.rs b/client/storage-monitor/src/lib.rs index 162fcfe34..e65b13ff6 100644 --- a/client/storage-monitor/src/lib.rs +++ b/client/storage-monitor/src/lib.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 // This program is free software: you can redistribute it and/or modify diff --git a/client/sync-state-rpc/src/lib.rs b/client/sync-state-rpc/src/lib.rs index 9edc2ceec..94bf53c5a 100644 --- a/client/sync-state-rpc/src/lib.rs +++ b/client/sync-state-rpc/src/lib.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2020-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 // This program is free software: you can redistribute it and/or modify diff --git a/client/sysinfo/build.rs b/client/sysinfo/build.rs index 6d2881074..d267f3670 100644 --- a/client/sysinfo/build.rs +++ b/client/sysinfo/build.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 // This program is free software: you can redistribute it and/or modify diff --git a/client/sysinfo/src/lib.rs b/client/sysinfo/src/lib.rs index f623bdae5..7065c9b99 100644 --- a/client/sysinfo/src/lib.rs +++ b/client/sysinfo/src/lib.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 // This program is free software: you can redistribute it and/or modify diff --git a/client/sysinfo/src/sysinfo.rs b/client/sysinfo/src/sysinfo.rs index 1fbca7d37..41161fc68 100644 --- a/client/sysinfo/src/sysinfo.rs +++ b/client/sysinfo/src/sysinfo.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 // This program is free software: you can redistribute it and/or modify diff --git a/client/sysinfo/src/sysinfo_linux.rs b/client/sysinfo/src/sysinfo_linux.rs index 41ab6014c..ae678e076 100644 --- a/client/sysinfo/src/sysinfo_linux.rs +++ b/client/sysinfo/src/sysinfo_linux.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 // This program is free software: you can redistribute it and/or modify diff --git a/client/telemetry/src/endpoints.rs b/client/telemetry/src/endpoints.rs index fba3822a9..a4f0d0f83 100644 --- a/client/telemetry/src/endpoints.rs +++ b/client/telemetry/src/endpoints.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2021-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 // This program is free software: you can redistribute it and/or modify diff --git a/client/telemetry/src/error.rs b/client/telemetry/src/error.rs index 4d9cdc05c..b8e3d2875 100644 --- a/client/telemetry/src/error.rs +++ b/client/telemetry/src/error.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2021-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 // This program is free software: you can redistribute it and/or modify diff --git a/client/telemetry/src/lib.rs b/client/telemetry/src/lib.rs index aa6b841b7..113d8303a 100644 --- a/client/telemetry/src/lib.rs +++ b/client/telemetry/src/lib.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2017-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 // This program is free software: you can redistribute it and/or modify diff --git a/client/telemetry/src/node.rs b/client/telemetry/src/node.rs index 71eed0412..0bbdbfb62 100644 --- a/client/telemetry/src/node.rs +++ b/client/telemetry/src/node.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2017-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 // This program is free software: you can redistribute it and/or modify diff --git a/client/telemetry/src/transport.rs b/client/telemetry/src/transport.rs index 5e940bbe5..a82626caa 100644 --- a/client/telemetry/src/transport.rs +++ b/client/telemetry/src/transport.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2021-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 // This program is free software: you can redistribute it and/or modify diff --git a/client/tracing/benches/bench.rs b/client/tracing/benches/bench.rs index a939a2679..1379023dd 100644 --- a/client/tracing/benches/bench.rs +++ b/client/tracing/benches/bench.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2021-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/client/tracing/proc-macro/src/lib.rs b/client/tracing/proc-macro/src/lib.rs index ba757619f..8fb01de40 100644 --- a/client/tracing/proc-macro/src/lib.rs +++ b/client/tracing/proc-macro/src/lib.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2020-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 // This program is free software: you can redistribute it and/or modify diff --git a/client/tracing/src/block/mod.rs b/client/tracing/src/block/mod.rs index 465767781..f5efadc34 100644 --- a/client/tracing/src/block/mod.rs +++ b/client/tracing/src/block/mod.rs @@ -1,4 +1,4 @@ -// Copyright 2021 Parity Technologies (UK) Ltd. +// Copyright Parity Technologies (UK) Ltd. // This file is part of Substrate. // Substrate is free software: you can redistribute it and/or modify diff --git a/client/tracing/src/lib.rs b/client/tracing/src/lib.rs index acbde8b75..bd5045fed 100644 --- a/client/tracing/src/lib.rs +++ b/client/tracing/src/lib.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2019-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 // This program is free software: you can redistribute it and/or modify diff --git a/client/tracing/src/logging/directives.rs b/client/tracing/src/logging/directives.rs index b5fb37367..3985bf2d8 100644 --- a/client/tracing/src/logging/directives.rs +++ b/client/tracing/src/logging/directives.rs @@ -1,4 +1,4 @@ -// Copyright 2021 Parity Technologies (UK) Ltd. +// Copyright Parity Technologies (UK) Ltd. // This file is part of Substrate. // Substrate is free software: you can redistribute it and/or modify diff --git a/client/tracing/src/logging/event_format.rs b/client/tracing/src/logging/event_format.rs index aec6b7684..f4579f006 100644 --- a/client/tracing/src/logging/event_format.rs +++ b/client/tracing/src/logging/event_format.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2020-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 // This program is free software: you can redistribute it and/or modify diff --git a/client/tracing/src/logging/fast_local_time.rs b/client/tracing/src/logging/fast_local_time.rs index 47fc23340..7be7bec83 100644 --- a/client/tracing/src/logging/fast_local_time.rs +++ b/client/tracing/src/logging/fast_local_time.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2021-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 // This program is free software: you can redistribute it and/or modify diff --git a/client/tracing/src/logging/layers/mod.rs b/client/tracing/src/logging/layers/mod.rs index 382b79bc8..b39320975 100644 --- a/client/tracing/src/logging/layers/mod.rs +++ b/client/tracing/src/logging/layers/mod.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2021-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 // This program is free software: you can redistribute it and/or modify diff --git a/client/tracing/src/logging/layers/prefix_layer.rs b/client/tracing/src/logging/layers/prefix_layer.rs index 836ffd2ad..fc444257b 100644 --- a/client/tracing/src/logging/layers/prefix_layer.rs +++ b/client/tracing/src/logging/layers/prefix_layer.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2021-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 // This program is free software: you can redistribute it and/or modify diff --git a/client/tracing/src/logging/mod.rs b/client/tracing/src/logging/mod.rs index 77307ad99..a3cf277fb 100644 --- a/client/tracing/src/logging/mod.rs +++ b/client/tracing/src/logging/mod.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2021-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 // This program is free software: you can redistribute it and/or modify diff --git a/client/tracing/src/logging/stderr_writer.rs b/client/tracing/src/logging/stderr_writer.rs index de78a61af..80df2f1fe 100644 --- a/client/tracing/src/logging/stderr_writer.rs +++ b/client/tracing/src/logging/stderr_writer.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2021-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 // This program is free software: you can redistribute it and/or modify diff --git a/client/transaction-pool/api/src/error.rs b/client/transaction-pool/api/src/error.rs index aada44734..e521502f6 100644 --- a/client/transaction-pool/api/src/error.rs +++ b/client/transaction-pool/api/src/error.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2018-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 // This program is free software: you can redistribute it and/or modify diff --git a/client/transaction-pool/api/src/lib.rs b/client/transaction-pool/api/src/lib.rs index 870760962..428d0aed6 100644 --- a/client/transaction-pool/api/src/lib.rs +++ b/client/transaction-pool/api/src/lib.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2019-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 // This program is free software: you can redistribute it and/or modify diff --git a/client/transaction-pool/benches/basics.rs b/client/transaction-pool/benches/basics.rs index 97d8edddf..f7739ef73 100644 --- a/client/transaction-pool/benches/basics.rs +++ b/client/transaction-pool/benches/basics.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2018-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 // This program is free software: you can redistribute it and/or modify diff --git a/client/transaction-pool/src/api.rs b/client/transaction-pool/src/api.rs index 9e20915db..f9d79ee42 100644 --- a/client/transaction-pool/src/api.rs +++ b/client/transaction-pool/src/api.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2018-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 // This program is free software: you can redistribute it and/or modify diff --git a/client/transaction-pool/src/enactment_state.rs b/client/transaction-pool/src/enactment_state.rs index 7252c0dec..ed6b3d186 100644 --- a/client/transaction-pool/src/enactment_state.rs +++ b/client/transaction-pool/src/enactment_state.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 // This program is free software: you can redistribute it and/or modify diff --git a/client/transaction-pool/src/error.rs b/client/transaction-pool/src/error.rs index a4cf75648..d93872d6c 100644 --- a/client/transaction-pool/src/error.rs +++ b/client/transaction-pool/src/error.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2018-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 // This program is free software: you can redistribute it and/or modify diff --git a/client/transaction-pool/src/graph/base_pool.rs b/client/transaction-pool/src/graph/base_pool.rs index 44371be62..a9d2d6c82 100644 --- a/client/transaction-pool/src/graph/base_pool.rs +++ b/client/transaction-pool/src/graph/base_pool.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2018-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 // This program is free software: you can redistribute it and/or modify diff --git a/client/transaction-pool/src/graph/future.rs b/client/transaction-pool/src/graph/future.rs index d23b5f2b6..bad466318 100644 --- a/client/transaction-pool/src/graph/future.rs +++ b/client/transaction-pool/src/graph/future.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2018-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 // This program is free software: you can redistribute it and/or modify diff --git a/client/transaction-pool/src/graph/listener.rs b/client/transaction-pool/src/graph/listener.rs index c603a542c..46b7957e0 100644 --- a/client/transaction-pool/src/graph/listener.rs +++ b/client/transaction-pool/src/graph/listener.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2018-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 // This program is free software: you can redistribute it and/or modify diff --git a/client/transaction-pool/src/graph/mod.rs b/client/transaction-pool/src/graph/mod.rs index 42812897a..5afdddb74 100644 --- a/client/transaction-pool/src/graph/mod.rs +++ b/client/transaction-pool/src/graph/mod.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2018-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 // This program is free software: you can redistribute it and/or modify diff --git a/client/transaction-pool/src/graph/pool.rs b/client/transaction-pool/src/graph/pool.rs index 14217d56a..54534df82 100644 --- a/client/transaction-pool/src/graph/pool.rs +++ b/client/transaction-pool/src/graph/pool.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2018-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 // This program is free software: you can redistribute it and/or modify diff --git a/client/transaction-pool/src/graph/ready.rs b/client/transaction-pool/src/graph/ready.rs index 9040c81b3..b4a5d9e3b 100644 --- a/client/transaction-pool/src/graph/ready.rs +++ b/client/transaction-pool/src/graph/ready.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2018-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 // This program is free software: you can redistribute it and/or modify diff --git a/client/transaction-pool/src/graph/rotator.rs b/client/transaction-pool/src/graph/rotator.rs index 47e00a129..61a26fb41 100644 --- a/client/transaction-pool/src/graph/rotator.rs +++ b/client/transaction-pool/src/graph/rotator.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2018-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 // This program is free software: you can redistribute it and/or modify diff --git a/client/transaction-pool/src/graph/tracked_map.rs b/client/transaction-pool/src/graph/tracked_map.rs index 729229325..47ad22603 100644 --- a/client/transaction-pool/src/graph/tracked_map.rs +++ b/client/transaction-pool/src/graph/tracked_map.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2018-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 // This program is free software: you can redistribute it and/or modify diff --git a/client/transaction-pool/src/graph/validated_pool.rs b/client/transaction-pool/src/graph/validated_pool.rs index bf8a059af..ed76d439a 100644 --- a/client/transaction-pool/src/graph/validated_pool.rs +++ b/client/transaction-pool/src/graph/validated_pool.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2018-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 // This program is free software: you can redistribute it and/or modify diff --git a/client/transaction-pool/src/graph/watcher.rs b/client/transaction-pool/src/graph/watcher.rs index df5bb94ed..fc440771d 100644 --- a/client/transaction-pool/src/graph/watcher.rs +++ b/client/transaction-pool/src/graph/watcher.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2018-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 // This program is free software: you can redistribute it and/or modify diff --git a/client/transaction-pool/src/lib.rs b/client/transaction-pool/src/lib.rs index 44bda82da..c3a85a373 100644 --- a/client/transaction-pool/src/lib.rs +++ b/client/transaction-pool/src/lib.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2018-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 // This program is free software: you can redistribute it and/or modify diff --git a/client/transaction-pool/src/metrics.rs b/client/transaction-pool/src/metrics.rs index 8bcefe246..170bface9 100644 --- a/client/transaction-pool/src/metrics.rs +++ b/client/transaction-pool/src/metrics.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2020-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 // This program is free software: you can redistribute it and/or modify diff --git a/client/transaction-pool/src/revalidation.rs b/client/transaction-pool/src/revalidation.rs index 99602b50a..bd8f3dd64 100644 --- a/client/transaction-pool/src/revalidation.rs +++ b/client/transaction-pool/src/revalidation.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2018-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 // This program is free software: you can redistribute it and/or modify diff --git a/client/transaction-pool/src/tests.rs b/client/transaction-pool/src/tests.rs index d399367ea..3945c88d4 100644 --- a/client/transaction-pool/src/tests.rs +++ b/client/transaction-pool/src/tests.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2018-2021 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 // This program is free software: you can redistribute it and/or modify diff --git a/client/transaction-pool/tests/pool.rs b/client/transaction-pool/tests/pool.rs index 511f6d9e2..284c777a6 100644 --- a/client/transaction-pool/tests/pool.rs +++ b/client/transaction-pool/tests/pool.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2020-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 // This program is free software: you can redistribute it and/or modify diff --git a/client/utils/src/id_sequence.rs b/client/utils/src/id_sequence.rs index 2ccb4fc3f..abb1271c7 100644 --- a/client/utils/src/id_sequence.rs +++ b/client/utils/src/id_sequence.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2020-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 // This program is free software: you can redistribute it and/or modify diff --git a/client/utils/src/lib.rs b/client/utils/src/lib.rs index 0e4733033..f0cc1efe6 100644 --- a/client/utils/src/lib.rs +++ b/client/utils/src/lib.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2020-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 // This program is free software: you can redistribute it and/or modify diff --git a/client/utils/src/metrics.rs b/client/utils/src/metrics.rs index 457ef23e4..c2b10100f 100644 --- a/client/utils/src/metrics.rs +++ b/client/utils/src/metrics.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2020-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 // This program is free software: you can redistribute it and/or modify diff --git a/client/utils/src/mpsc.rs b/client/utils/src/mpsc.rs index d74703c4a..df45e33ff 100644 --- a/client/utils/src/mpsc.rs +++ b/client/utils/src/mpsc.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2020-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 // This program is free software: you can redistribute it and/or modify diff --git a/client/utils/src/notification.rs b/client/utils/src/notification.rs index 4917a4326..7d866ffc8 100644 --- a/client/utils/src/notification.rs +++ b/client/utils/src/notification.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2021-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 // This program is free software: you can redistribute it and/or modify diff --git a/client/utils/src/notification/registry.rs b/client/utils/src/notification/registry.rs index ebe41452a..6f0960ea9 100644 --- a/client/utils/src/notification/registry.rs +++ b/client/utils/src/notification/registry.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2021-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 // This program is free software: you can redistribute it and/or modify diff --git a/client/utils/src/notification/tests.rs b/client/utils/src/notification/tests.rs index f813f37d2..1afc2307e 100644 --- a/client/utils/src/notification/tests.rs +++ b/client/utils/src/notification/tests.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2021-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 // This program is free software: you can redistribute it and/or modify diff --git a/client/utils/src/pubsub.rs b/client/utils/src/pubsub.rs index f85f44b49..8136aa069 100644 --- a/client/utils/src/pubsub.rs +++ b/client/utils/src/pubsub.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2021-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 // This program is free software: you can redistribute it and/or modify diff --git a/client/utils/src/pubsub/tests.rs b/client/utils/src/pubsub/tests.rs index 12945c0d8..0d832c7db 100644 --- a/client/utils/src/pubsub/tests.rs +++ b/client/utils/src/pubsub/tests.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2020-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 // This program is free software: you can redistribute it and/or modify diff --git a/client/utils/src/pubsub/tests/normal_operation.rs b/client/utils/src/pubsub/tests/normal_operation.rs index 830388de3..d4b614d7a 100644 --- a/client/utils/src/pubsub/tests/normal_operation.rs +++ b/client/utils/src/pubsub/tests/normal_operation.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2020-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 // This program is free software: you can redistribute it and/or modify diff --git a/client/utils/src/pubsub/tests/panicking_registry.rs b/client/utils/src/pubsub/tests/panicking_registry.rs index cfe8168d8..9f7579e79 100644 --- a/client/utils/src/pubsub/tests/panicking_registry.rs +++ b/client/utils/src/pubsub/tests/panicking_registry.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2020-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 // This program is free software: you can redistribute it and/or modify diff --git a/client/utils/src/status_sinks.rs b/client/utils/src/status_sinks.rs index c536e2c18..51d78aa49 100644 --- a/client/utils/src/status_sinks.rs +++ b/client/utils/src/status_sinks.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2019-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 // This program is free software: you can redistribute it and/or modify diff --git a/frame/alliance/src/benchmarking.rs b/frame/alliance/src/benchmarking.rs index 01b81ed66..a30b5adce 100644 --- a/frame/alliance/src/benchmarking.rs +++ b/frame/alliance/src/benchmarking.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/frame/alliance/src/lib.rs b/frame/alliance/src/lib.rs index 790c3c384..c9232a1f2 100644 --- a/frame/alliance/src/lib.rs +++ b/frame/alliance/src/lib.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/frame/alliance/src/migration.rs b/frame/alliance/src/migration.rs index ea07f8c12..e3a44a788 100644 --- a/frame/alliance/src/migration.rs +++ b/frame/alliance/src/migration.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/frame/alliance/src/mock.rs b/frame/alliance/src/mock.rs index 0f774dc48..0925a82fd 100644 --- a/frame/alliance/src/mock.rs +++ b/frame/alliance/src/mock.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2019-2021 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/frame/alliance/src/tests.rs b/frame/alliance/src/tests.rs index 363b19429..594259546 100644 --- a/frame/alliance/src/tests.rs +++ b/frame/alliance/src/tests.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2019-2021 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/frame/alliance/src/types.rs b/frame/alliance/src/types.rs index c155b9fa9..784993b2b 100644 --- a/frame/alliance/src/types.rs +++ b/frame/alliance/src/types.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/frame/alliance/src/weights.rs b/frame/alliance/src/weights.rs index 9ff6ce612..6ccf6bed3 100644 --- a/frame/alliance/src/weights.rs +++ b/frame/alliance/src/weights.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2023 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/frame/assets/src/benchmarking.rs b/frame/assets/src/benchmarking.rs index cf0ec353f..4fe951f56 100644 --- a/frame/assets/src/benchmarking.rs +++ b/frame/assets/src/benchmarking.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2020-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/frame/assets/src/extra_mutator.rs b/frame/assets/src/extra_mutator.rs index b72bfa86d..96fb765cb 100644 --- a/frame/assets/src/extra_mutator.rs +++ b/frame/assets/src/extra_mutator.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2017-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/frame/assets/src/functions.rs b/frame/assets/src/functions.rs index b540f02b8..f970ce6cf 100644 --- a/frame/assets/src/functions.rs +++ b/frame/assets/src/functions.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2017-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/frame/assets/src/impl_fungibles.rs b/frame/assets/src/impl_fungibles.rs index b10b8c6b1..f420ea02c 100644 --- a/frame/assets/src/impl_fungibles.rs +++ b/frame/assets/src/impl_fungibles.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2017-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/frame/assets/src/impl_stored_map.rs b/frame/assets/src/impl_stored_map.rs index a4669c776..5ead70846 100644 --- a/frame/assets/src/impl_stored_map.rs +++ b/frame/assets/src/impl_stored_map.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2017-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/frame/assets/src/lib.rs b/frame/assets/src/lib.rs index c2480a206..894f8f3a4 100644 --- a/frame/assets/src/lib.rs +++ b/frame/assets/src/lib.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2017-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/frame/assets/src/migration.rs b/frame/assets/src/migration.rs index 9f8905cef..71da8823e 100644 --- a/frame/assets/src/migration.rs +++ b/frame/assets/src/migration.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2017-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/frame/assets/src/mock.rs b/frame/assets/src/mock.rs index 93a87ee67..14795b10a 100644 --- a/frame/assets/src/mock.rs +++ b/frame/assets/src/mock.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2019-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/frame/assets/src/tests.rs b/frame/assets/src/tests.rs index f7007ff3c..6a3fd672a 100644 --- a/frame/assets/src/tests.rs +++ b/frame/assets/src/tests.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2019-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/frame/assets/src/types.rs b/frame/assets/src/types.rs index 557af6bd3..d50461ba5 100644 --- a/frame/assets/src/types.rs +++ b/frame/assets/src/types.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2017-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/frame/assets/src/weights.rs b/frame/assets/src/weights.rs index c2028105c..9ebba6223 100644 --- a/frame/assets/src/weights.rs +++ b/frame/assets/src/weights.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2023 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/frame/atomic-swap/src/lib.rs b/frame/atomic-swap/src/lib.rs index 66628e8e6..68d254420 100644 --- a/frame/atomic-swap/src/lib.rs +++ b/frame/atomic-swap/src/lib.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2017-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/frame/aura/src/lib.rs b/frame/aura/src/lib.rs index b19fbd57c..108b9303b 100644 --- a/frame/aura/src/lib.rs +++ b/frame/aura/src/lib.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2017-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/frame/aura/src/migrations.rs b/frame/aura/src/migrations.rs index d9b7cb887..b45e4eb7c 100644 --- a/frame/aura/src/migrations.rs +++ b/frame/aura/src/migrations.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2021-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/frame/aura/src/mock.rs b/frame/aura/src/mock.rs index 7a07fe99a..72d11a074 100644 --- a/frame/aura/src/mock.rs +++ b/frame/aura/src/mock.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2018-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/frame/aura/src/tests.rs b/frame/aura/src/tests.rs index ce09f8567..1ed937a77 100644 --- a/frame/aura/src/tests.rs +++ b/frame/aura/src/tests.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2017-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/frame/authority-discovery/src/lib.rs b/frame/authority-discovery/src/lib.rs index 39e01dba4..e93deabeb 100644 --- a/frame/authority-discovery/src/lib.rs +++ b/frame/authority-discovery/src/lib.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2019-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/frame/authorship/src/lib.rs b/frame/authorship/src/lib.rs index b5568a6ac..cb1d777fa 100644 --- a/frame/authorship/src/lib.rs +++ b/frame/authorship/src/lib.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2019-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/frame/babe/src/benchmarking.rs b/frame/babe/src/benchmarking.rs index 70605e4cf..92f556659 100644 --- a/frame/babe/src/benchmarking.rs +++ b/frame/babe/src/benchmarking.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2020-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/frame/babe/src/default_weights.rs b/frame/babe/src/default_weights.rs index f864fd18d..e875bc835 100644 --- a/frame/babe/src/default_weights.rs +++ b/frame/babe/src/default_weights.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2020-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/frame/babe/src/equivocation.rs b/frame/babe/src/equivocation.rs index 70f087fd4..2a2e2b476 100644 --- a/frame/babe/src/equivocation.rs +++ b/frame/babe/src/equivocation.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2020-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/frame/babe/src/lib.rs b/frame/babe/src/lib.rs index 69c89fdce..bb07037ee 100644 --- a/frame/babe/src/lib.rs +++ b/frame/babe/src/lib.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2019-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/frame/babe/src/mock.rs b/frame/babe/src/mock.rs index b09dc29f6..b13067789 100644 --- a/frame/babe/src/mock.rs +++ b/frame/babe/src/mock.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2019-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/frame/babe/src/randomness.rs b/frame/babe/src/randomness.rs index 28b2ea7cd..b223df680 100644 --- a/frame/babe/src/randomness.rs +++ b/frame/babe/src/randomness.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2019-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/frame/babe/src/tests.rs b/frame/babe/src/tests.rs index 938269a6c..68426d0a8 100644 --- a/frame/babe/src/tests.rs +++ b/frame/babe/src/tests.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2019-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/frame/bags-list/fuzzer/src/main.rs b/frame/bags-list/fuzzer/src/main.rs index c78e2a130..18391bbb3 100644 --- a/frame/bags-list/fuzzer/src/main.rs +++ b/frame/bags-list/fuzzer/src/main.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2021-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/frame/bags-list/remote-tests/src/lib.rs b/frame/bags-list/remote-tests/src/lib.rs index fc25e3b65..9f7c22d99 100644 --- a/frame/bags-list/remote-tests/src/lib.rs +++ b/frame/bags-list/remote-tests/src/lib.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2021-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/frame/bags-list/remote-tests/src/migration.rs b/frame/bags-list/remote-tests/src/migration.rs index 759906a4e..7847fdc75 100644 --- a/frame/bags-list/remote-tests/src/migration.rs +++ b/frame/bags-list/remote-tests/src/migration.rs @@ -1,4 +1,4 @@ -// Copyright 2021 Parity Technologies (UK) Ltd. +// Copyright Parity Technologies (UK) Ltd. // This file is part of Polkadot. // Polkadot is free software: you can redistribute it and/or modify diff --git a/frame/bags-list/remote-tests/src/snapshot.rs b/frame/bags-list/remote-tests/src/snapshot.rs index 0163ca200..0c6e194d3 100644 --- a/frame/bags-list/remote-tests/src/snapshot.rs +++ b/frame/bags-list/remote-tests/src/snapshot.rs @@ -1,4 +1,4 @@ -// Copyright 2021 Parity Technologies (UK) Ltd. +// Copyright Parity Technologies (UK) Ltd. // This file is part of Polkadot. // Polkadot is free software: you can redistribute it and/or modify diff --git a/frame/bags-list/remote-tests/src/try_state.rs b/frame/bags-list/remote-tests/src/try_state.rs index 9ed877a43..5bbac00bc 100644 --- a/frame/bags-list/remote-tests/src/try_state.rs +++ b/frame/bags-list/remote-tests/src/try_state.rs @@ -1,4 +1,4 @@ -// Copyright 2021 Parity Technologies (UK) Ltd. +// Copyright Parity Technologies (UK) Ltd. // This file is part of Polkadot. // Polkadot is free software: you can redistribute it and/or modify diff --git a/frame/bags-list/src/benchmarks.rs b/frame/bags-list/src/benchmarks.rs index 74ae5f31a..0c3955c0d 100644 --- a/frame/bags-list/src/benchmarks.rs +++ b/frame/bags-list/src/benchmarks.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2021-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/frame/bags-list/src/lib.rs b/frame/bags-list/src/lib.rs index f04c685ae..b65b65c39 100644 --- a/frame/bags-list/src/lib.rs +++ b/frame/bags-list/src/lib.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2021-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/frame/bags-list/src/list/mod.rs b/frame/bags-list/src/list/mod.rs index 4082a5324..f667f4c10 100644 --- a/frame/bags-list/src/list/mod.rs +++ b/frame/bags-list/src/list/mod.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2021-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/frame/bags-list/src/list/tests.rs b/frame/bags-list/src/list/tests.rs index 3c4aa7c86..f5afdc24f 100644 --- a/frame/bags-list/src/list/tests.rs +++ b/frame/bags-list/src/list/tests.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2021-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/frame/bags-list/src/migrations.rs b/frame/bags-list/src/migrations.rs index e1dc9f777..5f9bb8f73 100644 --- a/frame/bags-list/src/migrations.rs +++ b/frame/bags-list/src/migrations.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2021-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/frame/bags-list/src/mock.rs b/frame/bags-list/src/mock.rs index 32f6bb09e..2ccc3c438 100644 --- a/frame/bags-list/src/mock.rs +++ b/frame/bags-list/src/mock.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2021-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/frame/bags-list/src/tests.rs b/frame/bags-list/src/tests.rs index 63a395ed0..74f149183 100644 --- a/frame/bags-list/src/tests.rs +++ b/frame/bags-list/src/tests.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2021-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/frame/bags-list/src/weights.rs b/frame/bags-list/src/weights.rs index d06f7b0db..65d27c5ae 100644 --- a/frame/bags-list/src/weights.rs +++ b/frame/bags-list/src/weights.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2023 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/frame/balances/src/benchmarking.rs b/frame/balances/src/benchmarking.rs index d80aa3ec8..c2ed0ace5 100644 --- a/frame/balances/src/benchmarking.rs +++ b/frame/balances/src/benchmarking.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2020-2023 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/frame/balances/src/lib.rs b/frame/balances/src/lib.rs index f62ea6be7..f02c7f9c0 100644 --- a/frame/balances/src/lib.rs +++ b/frame/balances/src/lib.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2017-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/frame/balances/src/migration.rs b/frame/balances/src/migration.rs index 80d1bcd35..6a272a611 100644 --- a/frame/balances/src/migration.rs +++ b/frame/balances/src/migration.rs @@ -1,4 +1,4 @@ -// Copyright 2017-2020 Parity Technologies (UK) Ltd. +// Copyright Parity Technologies (UK) Ltd. // This file is part of Polkadot. // Polkadot is free software: you can redistribute it and/or modify diff --git a/frame/balances/src/tests.rs b/frame/balances/src/tests.rs index 83944caf9..82a6ee5f6 100644 --- a/frame/balances/src/tests.rs +++ b/frame/balances/src/tests.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2017-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/frame/balances/src/tests_composite.rs b/frame/balances/src/tests_composite.rs index f8a8fdd18..6f371f1e2 100644 --- a/frame/balances/src/tests_composite.rs +++ b/frame/balances/src/tests_composite.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2018-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/frame/balances/src/tests_local.rs b/frame/balances/src/tests_local.rs index 152a5da37..0d57a3320 100644 --- a/frame/balances/src/tests_local.rs +++ b/frame/balances/src/tests_local.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2018-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/frame/balances/src/tests_reentrancy.rs b/frame/balances/src/tests_reentrancy.rs index 903631400..1699a8796 100644 --- a/frame/balances/src/tests_reentrancy.rs +++ b/frame/balances/src/tests_reentrancy.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2018-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/frame/balances/src/weights.rs b/frame/balances/src/weights.rs index 3266fcbcd..6bd7d7548 100644 --- a/frame/balances/src/weights.rs +++ b/frame/balances/src/weights.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2023 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/frame/beefy-mmr/src/lib.rs b/frame/beefy-mmr/src/lib.rs index eb030b71d..8bb3aaa7e 100644 --- a/frame/beefy-mmr/src/lib.rs +++ b/frame/beefy-mmr/src/lib.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2021-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/frame/beefy-mmr/src/mock.rs b/frame/beefy-mmr/src/mock.rs index 8f3bdd619..922f882e1 100644 --- a/frame/beefy-mmr/src/mock.rs +++ b/frame/beefy-mmr/src/mock.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2021-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/frame/beefy-mmr/src/tests.rs b/frame/beefy-mmr/src/tests.rs index 0f7804e54..af10d1127 100644 --- a/frame/beefy-mmr/src/tests.rs +++ b/frame/beefy-mmr/src/tests.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2021-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/frame/beefy/src/default_weights.rs b/frame/beefy/src/default_weights.rs index 0b642cb45..7743edf25 100644 --- a/frame/beefy/src/default_weights.rs +++ b/frame/beefy/src/default_weights.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2020-2023 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/frame/beefy/src/equivocation.rs b/frame/beefy/src/equivocation.rs index ea6a9f912..19c41cbfa 100644 --- a/frame/beefy/src/equivocation.rs +++ b/frame/beefy/src/equivocation.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2017-2023 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/frame/beefy/src/lib.rs b/frame/beefy/src/lib.rs index cccfdbb81..bcb7fc126 100644 --- a/frame/beefy/src/lib.rs +++ b/frame/beefy/src/lib.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2021-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/frame/beefy/src/mock.rs b/frame/beefy/src/mock.rs index c6cde5332..74dba2a01 100644 --- a/frame/beefy/src/mock.rs +++ b/frame/beefy/src/mock.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2021-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/frame/beefy/src/tests.rs b/frame/beefy/src/tests.rs index b2e1b6769..767b1e565 100644 --- a/frame/beefy/src/tests.rs +++ b/frame/beefy/src/tests.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2021-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/frame/benchmarking/pov/src/benchmarking.rs b/frame/benchmarking/pov/src/benchmarking.rs index 608077e2d..331570ad2 100644 --- a/frame/benchmarking/pov/src/benchmarking.rs +++ b/frame/benchmarking/pov/src/benchmarking.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/frame/benchmarking/pov/src/lib.rs b/frame/benchmarking/pov/src/lib.rs index 6618ce60d..7dee5c37c 100644 --- a/frame/benchmarking/pov/src/lib.rs +++ b/frame/benchmarking/pov/src/lib.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2020-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/frame/benchmarking/pov/src/tests.rs b/frame/benchmarking/pov/src/tests.rs index 1100fdc46..19156b930 100644 --- a/frame/benchmarking/pov/src/tests.rs +++ b/frame/benchmarking/pov/src/tests.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/frame/benchmarking/src/analysis.rs b/frame/benchmarking/src/analysis.rs index e5afe8103..5fc3abb5a 100644 --- a/frame/benchmarking/src/analysis.rs +++ b/frame/benchmarking/src/analysis.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2020-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/frame/benchmarking/src/baseline.rs b/frame/benchmarking/src/baseline.rs index 0d511cbff..11d4ba501 100644 --- a/frame/benchmarking/src/baseline.rs +++ b/frame/benchmarking/src/baseline.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2020-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/frame/benchmarking/src/lib.rs b/frame/benchmarking/src/lib.rs index a3bf809f6..f5dc51365 100644 --- a/frame/benchmarking/src/lib.rs +++ b/frame/benchmarking/src/lib.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2020-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/frame/benchmarking/src/tests.rs b/frame/benchmarking/src/tests.rs index 1499f9c18..a3e19cde3 100644 --- a/frame/benchmarking/src/tests.rs +++ b/frame/benchmarking/src/tests.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2020-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/frame/benchmarking/src/tests_instance.rs b/frame/benchmarking/src/tests_instance.rs index ecc0a78a1..49f9786d7 100644 --- a/frame/benchmarking/src/tests_instance.rs +++ b/frame/benchmarking/src/tests_instance.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2020-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/frame/benchmarking/src/utils.rs b/frame/benchmarking/src/utils.rs index 74e44fbf9..59e5192b4 100644 --- a/frame/benchmarking/src/utils.rs +++ b/frame/benchmarking/src/utils.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2020-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/frame/benchmarking/src/v1.rs b/frame/benchmarking/src/v1.rs index 089083f85..be6dc393c 100644 --- a/frame/benchmarking/src/v1.rs +++ b/frame/benchmarking/src/v1.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2020-2023 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/frame/benchmarking/src/weights.rs b/frame/benchmarking/src/weights.rs index dea959471..85dbb18c7 100644 --- a/frame/benchmarking/src/weights.rs +++ b/frame/benchmarking/src/weights.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2023 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/frame/bounties/src/benchmarking.rs b/frame/bounties/src/benchmarking.rs index 881f1c912..04465a25e 100644 --- a/frame/bounties/src/benchmarking.rs +++ b/frame/bounties/src/benchmarking.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2020-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/frame/bounties/src/lib.rs b/frame/bounties/src/lib.rs index afdcfef82..325347502 100644 --- a/frame/bounties/src/lib.rs +++ b/frame/bounties/src/lib.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2017-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/frame/bounties/src/migrations/mod.rs b/frame/bounties/src/migrations/mod.rs index 235d0f1c7..2487ed1d5 100644 --- a/frame/bounties/src/migrations/mod.rs +++ b/frame/bounties/src/migrations/mod.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2019-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/frame/bounties/src/migrations/v4.rs b/frame/bounties/src/migrations/v4.rs index 2f81c9712..936bac117 100644 --- a/frame/bounties/src/migrations/v4.rs +++ b/frame/bounties/src/migrations/v4.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2019-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/frame/bounties/src/tests.rs b/frame/bounties/src/tests.rs index bc59e3a70..27aa08585 100644 --- a/frame/bounties/src/tests.rs +++ b/frame/bounties/src/tests.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2020-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/frame/bounties/src/weights.rs b/frame/bounties/src/weights.rs index 1ae8ab298..174891f95 100644 --- a/frame/bounties/src/weights.rs +++ b/frame/bounties/src/weights.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2023 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/frame/child-bounties/src/benchmarking.rs b/frame/child-bounties/src/benchmarking.rs index e7f18c164..0a83f7c39 100644 --- a/frame/child-bounties/src/benchmarking.rs +++ b/frame/child-bounties/src/benchmarking.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2021-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/frame/child-bounties/src/lib.rs b/frame/child-bounties/src/lib.rs index 9eb784eac..3046d9700 100644 --- a/frame/child-bounties/src/lib.rs +++ b/frame/child-bounties/src/lib.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2021-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/frame/child-bounties/src/tests.rs b/frame/child-bounties/src/tests.rs index f3415c69d..b4b24eb91 100644 --- a/frame/child-bounties/src/tests.rs +++ b/frame/child-bounties/src/tests.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2021-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/frame/child-bounties/src/weights.rs b/frame/child-bounties/src/weights.rs index 77ab24488..e27393de9 100644 --- a/frame/child-bounties/src/weights.rs +++ b/frame/child-bounties/src/weights.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2023 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/frame/collective/src/benchmarking.rs b/frame/collective/src/benchmarking.rs index 5c9e55862..d4b5af1bc 100644 --- a/frame/collective/src/benchmarking.rs +++ b/frame/collective/src/benchmarking.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2020-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/frame/collective/src/lib.rs b/frame/collective/src/lib.rs index 8424527b1..ae5b22559 100644 --- a/frame/collective/src/lib.rs +++ b/frame/collective/src/lib.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2017-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/frame/collective/src/migrations/mod.rs b/frame/collective/src/migrations/mod.rs index 235d0f1c7..2487ed1d5 100644 --- a/frame/collective/src/migrations/mod.rs +++ b/frame/collective/src/migrations/mod.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2019-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/frame/collective/src/migrations/v4.rs b/frame/collective/src/migrations/v4.rs index 2756b3fd1..b3326b425 100644 --- a/frame/collective/src/migrations/v4.rs +++ b/frame/collective/src/migrations/v4.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2019-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/frame/collective/src/tests.rs b/frame/collective/src/tests.rs index b7cdeb338..bfc6a0249 100644 --- a/frame/collective/src/tests.rs +++ b/frame/collective/src/tests.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2021-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/frame/collective/src/weights.rs b/frame/collective/src/weights.rs index fe6a22201..d8a713118 100644 --- a/frame/collective/src/weights.rs +++ b/frame/collective/src/weights.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2023 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/frame/contracts/primitives/src/lib.rs b/frame/contracts/primitives/src/lib.rs index f5b8da249..ee415cb82 100644 --- a/frame/contracts/primitives/src/lib.rs +++ b/frame/contracts/primitives/src/lib.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2020-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/frame/contracts/proc-macro/src/lib.rs b/frame/contracts/proc-macro/src/lib.rs index c5f52f43a..1b530a409 100644 --- a/frame/contracts/proc-macro/src/lib.rs +++ b/frame/contracts/proc-macro/src/lib.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2020-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/frame/contracts/src/address.rs b/frame/contracts/src/address.rs index 4b1bb1f48..1c25af21e 100644 --- a/frame/contracts/src/address.rs +++ b/frame/contracts/src/address.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2018-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/frame/contracts/src/benchmarking/code.rs b/frame/contracts/src/benchmarking/code.rs index 156b9e2f9..aa44cc397 100644 --- a/frame/contracts/src/benchmarking/code.rs +++ b/frame/contracts/src/benchmarking/code.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2020-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/frame/contracts/src/benchmarking/mod.rs b/frame/contracts/src/benchmarking/mod.rs index ed41cece1..c18d9ffb4 100644 --- a/frame/contracts/src/benchmarking/mod.rs +++ b/frame/contracts/src/benchmarking/mod.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2020-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/frame/contracts/src/benchmarking/sandbox.rs b/frame/contracts/src/benchmarking/sandbox.rs index a6a97d11a..7e2884098 100644 --- a/frame/contracts/src/benchmarking/sandbox.rs +++ b/frame/contracts/src/benchmarking/sandbox.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2020-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/frame/contracts/src/chain_extension.rs b/frame/contracts/src/chain_extension.rs index 3974bdba3..9b9454254 100644 --- a/frame/contracts/src/chain_extension.rs +++ b/frame/contracts/src/chain_extension.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2020-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/frame/contracts/src/exec.rs b/frame/contracts/src/exec.rs index 92930cc79..37be12d50 100644 --- a/frame/contracts/src/exec.rs +++ b/frame/contracts/src/exec.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2018-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/frame/contracts/src/gas.rs b/frame/contracts/src/gas.rs index c8383f307..c6f7d44be 100644 --- a/frame/contracts/src/gas.rs +++ b/frame/contracts/src/gas.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2018-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/frame/contracts/src/lib.rs b/frame/contracts/src/lib.rs index a5f35a9cb..a115cf119 100644 --- a/frame/contracts/src/lib.rs +++ b/frame/contracts/src/lib.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2018-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/frame/contracts/src/migration.rs b/frame/contracts/src/migration.rs index 56d688abc..cd8f1dd1c 100644 --- a/frame/contracts/src/migration.rs +++ b/frame/contracts/src/migration.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2018-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/frame/contracts/src/schedule.rs b/frame/contracts/src/schedule.rs index ccfba941e..71faf08c2 100644 --- a/frame/contracts/src/schedule.rs +++ b/frame/contracts/src/schedule.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2020-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/frame/contracts/src/storage.rs b/frame/contracts/src/storage.rs index 35db33c4c..26162b55c 100644 --- a/frame/contracts/src/storage.rs +++ b/frame/contracts/src/storage.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2019-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/frame/contracts/src/storage/meter.rs b/frame/contracts/src/storage/meter.rs index 5102edb78..ef6fb3277 100644 --- a/frame/contracts/src/storage/meter.rs +++ b/frame/contracts/src/storage/meter.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2019-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/frame/contracts/src/tests.rs b/frame/contracts/src/tests.rs index 879268a68..bf1bd4f65 100644 --- a/frame/contracts/src/tests.rs +++ b/frame/contracts/src/tests.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2018-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/frame/contracts/src/wasm/code_cache.rs b/frame/contracts/src/wasm/code_cache.rs index 385ed54cc..7dbce367c 100644 --- a/frame/contracts/src/wasm/code_cache.rs +++ b/frame/contracts/src/wasm/code_cache.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2018-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/frame/contracts/src/wasm/mod.rs b/frame/contracts/src/wasm/mod.rs index d25499641..f8c5a6ed9 100644 --- a/frame/contracts/src/wasm/mod.rs +++ b/frame/contracts/src/wasm/mod.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2018-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/frame/contracts/src/wasm/prepare.rs b/frame/contracts/src/wasm/prepare.rs index 787b97d31..3c69fc2a7 100644 --- a/frame/contracts/src/wasm/prepare.rs +++ b/frame/contracts/src/wasm/prepare.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2018-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/frame/contracts/src/wasm/runtime.rs b/frame/contracts/src/wasm/runtime.rs index eba20e959..ab873331c 100644 --- a/frame/contracts/src/wasm/runtime.rs +++ b/frame/contracts/src/wasm/runtime.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2018-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/frame/contracts/src/weights.rs b/frame/contracts/src/weights.rs index d03261584..880b7305f 100644 --- a/frame/contracts/src/weights.rs +++ b/frame/contracts/src/weights.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2023 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/frame/conviction-voting/src/benchmarking.rs b/frame/conviction-voting/src/benchmarking.rs index 8c168a34a..27f46507d 100644 --- a/frame/conviction-voting/src/benchmarking.rs +++ b/frame/conviction-voting/src/benchmarking.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2020-2021 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/frame/conviction-voting/src/conviction.rs b/frame/conviction-voting/src/conviction.rs index 1feff35b1..b5c9a3a70 100644 --- a/frame/conviction-voting/src/conviction.rs +++ b/frame/conviction-voting/src/conviction.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2017-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/frame/conviction-voting/src/lib.rs b/frame/conviction-voting/src/lib.rs index 141e9690f..6c15c6cf2 100644 --- a/frame/conviction-voting/src/lib.rs +++ b/frame/conviction-voting/src/lib.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2017-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/frame/conviction-voting/src/tests.rs b/frame/conviction-voting/src/tests.rs index 9caa451e0..e01931d7a 100644 --- a/frame/conviction-voting/src/tests.rs +++ b/frame/conviction-voting/src/tests.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2017-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/frame/conviction-voting/src/types.rs b/frame/conviction-voting/src/types.rs index aa2406eef..2c45b5448 100644 --- a/frame/conviction-voting/src/types.rs +++ b/frame/conviction-voting/src/types.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2017-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/frame/conviction-voting/src/vote.rs b/frame/conviction-voting/src/vote.rs index 1fed70536..5ae08f0de 100644 --- a/frame/conviction-voting/src/vote.rs +++ b/frame/conviction-voting/src/vote.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2017-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/frame/conviction-voting/src/weights.rs b/frame/conviction-voting/src/weights.rs index c5c712383..a2f992b1f 100644 --- a/frame/conviction-voting/src/weights.rs +++ b/frame/conviction-voting/src/weights.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2023 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/frame/democracy/src/benchmarking.rs b/frame/democracy/src/benchmarking.rs index f376b733a..9a67ba698 100644 --- a/frame/democracy/src/benchmarking.rs +++ b/frame/democracy/src/benchmarking.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2020-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/frame/democracy/src/conviction.rs b/frame/democracy/src/conviction.rs index a938d8a4e..d2f685f7d 100644 --- a/frame/democracy/src/conviction.rs +++ b/frame/democracy/src/conviction.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2017-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/frame/democracy/src/lib.rs b/frame/democracy/src/lib.rs index d9aae63ce..b3ac3dfe7 100644 --- a/frame/democracy/src/lib.rs +++ b/frame/democracy/src/lib.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2017-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/frame/democracy/src/migrations.rs b/frame/democracy/src/migrations.rs index 3ec249c1d..fe2e445bd 100644 --- a/frame/democracy/src/migrations.rs +++ b/frame/democracy/src/migrations.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/frame/democracy/src/tests.rs b/frame/democracy/src/tests.rs index 9c7ec2906..eb1d2650a 100644 --- a/frame/democracy/src/tests.rs +++ b/frame/democracy/src/tests.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2017-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/frame/democracy/src/tests/cancellation.rs b/frame/democracy/src/tests/cancellation.rs index ff046d612..4384fe6a1 100644 --- a/frame/democracy/src/tests/cancellation.rs +++ b/frame/democracy/src/tests/cancellation.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2017-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/frame/democracy/src/tests/decoders.rs b/frame/democracy/src/tests/decoders.rs index 1c8b9c3d9..9cd725d97 100644 --- a/frame/democracy/src/tests/decoders.rs +++ b/frame/democracy/src/tests/decoders.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2020-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/frame/democracy/src/tests/delegation.rs b/frame/democracy/src/tests/delegation.rs index bca7cb952..710faf919 100644 --- a/frame/democracy/src/tests/delegation.rs +++ b/frame/democracy/src/tests/delegation.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2017-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/frame/democracy/src/tests/external_proposing.rs b/frame/democracy/src/tests/external_proposing.rs index 4cfdd2aa7..08b497ab4 100644 --- a/frame/democracy/src/tests/external_proposing.rs +++ b/frame/democracy/src/tests/external_proposing.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2017-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/frame/democracy/src/tests/fast_tracking.rs b/frame/democracy/src/tests/fast_tracking.rs index 09af3dbf3..85e7792a4 100644 --- a/frame/democracy/src/tests/fast_tracking.rs +++ b/frame/democracy/src/tests/fast_tracking.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2017-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/frame/democracy/src/tests/lock_voting.rs b/frame/democracy/src/tests/lock_voting.rs index 540198ecf..9b9172ff0 100644 --- a/frame/democracy/src/tests/lock_voting.rs +++ b/frame/democracy/src/tests/lock_voting.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2017-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/frame/democracy/src/tests/metadata.rs b/frame/democracy/src/tests/metadata.rs index 95b617e37..59229abfe 100644 --- a/frame/democracy/src/tests/metadata.rs +++ b/frame/democracy/src/tests/metadata.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2017-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/frame/democracy/src/tests/public_proposals.rs b/frame/democracy/src/tests/public_proposals.rs index f9471442c..69a2d3e25 100644 --- a/frame/democracy/src/tests/public_proposals.rs +++ b/frame/democracy/src/tests/public_proposals.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2017-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/frame/democracy/src/tests/scheduling.rs b/frame/democracy/src/tests/scheduling.rs index 5e133f389..fdbc8fdb3 100644 --- a/frame/democracy/src/tests/scheduling.rs +++ b/frame/democracy/src/tests/scheduling.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2017-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/frame/democracy/src/tests/voting.rs b/frame/democracy/src/tests/voting.rs index 482cd430e..f096b633e 100644 --- a/frame/democracy/src/tests/voting.rs +++ b/frame/democracy/src/tests/voting.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2017-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/frame/democracy/src/types.rs b/frame/democracy/src/types.rs index 25954e054..ee6e2e0aa 100644 --- a/frame/democracy/src/types.rs +++ b/frame/democracy/src/types.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2017-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/frame/democracy/src/vote.rs b/frame/democracy/src/vote.rs index 122f54feb..c1b626fd9 100644 --- a/frame/democracy/src/vote.rs +++ b/frame/democracy/src/vote.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2017-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/frame/democracy/src/vote_threshold.rs b/frame/democracy/src/vote_threshold.rs index e8ef91def..e8efa179e 100644 --- a/frame/democracy/src/vote_threshold.rs +++ b/frame/democracy/src/vote_threshold.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2017-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/frame/democracy/src/weights.rs b/frame/democracy/src/weights.rs index 10d0744ea..a2cdeead6 100644 --- a/frame/democracy/src/weights.rs +++ b/frame/democracy/src/weights.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2023 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/frame/election-provider-multi-phase/src/benchmarking.rs b/frame/election-provider-multi-phase/src/benchmarking.rs index 16263d97d..4d48f1790 100644 --- a/frame/election-provider-multi-phase/src/benchmarking.rs +++ b/frame/election-provider-multi-phase/src/benchmarking.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2021-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/frame/election-provider-multi-phase/src/helpers.rs b/frame/election-provider-multi-phase/src/helpers.rs index 0a7240c5d..57d580e93 100644 --- a/frame/election-provider-multi-phase/src/helpers.rs +++ b/frame/election-provider-multi-phase/src/helpers.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2020-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/frame/election-provider-multi-phase/src/lib.rs b/frame/election-provider-multi-phase/src/lib.rs index 6c1b1d163..3ba90f411 100644 --- a/frame/election-provider-multi-phase/src/lib.rs +++ b/frame/election-provider-multi-phase/src/lib.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2021-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/frame/election-provider-multi-phase/src/migrations.rs b/frame/election-provider-multi-phase/src/migrations.rs index 77efe0d0c..50b821e6d 100644 --- a/frame/election-provider-multi-phase/src/migrations.rs +++ b/frame/election-provider-multi-phase/src/migrations.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/frame/election-provider-multi-phase/src/mock.rs b/frame/election-provider-multi-phase/src/mock.rs index 347a4f191..2ceaa6c23 100644 --- a/frame/election-provider-multi-phase/src/mock.rs +++ b/frame/election-provider-multi-phase/src/mock.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2021-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/frame/election-provider-multi-phase/src/signed.rs b/frame/election-provider-multi-phase/src/signed.rs index 895f3670a..d5d17e26b 100644 --- a/frame/election-provider-multi-phase/src/signed.rs +++ b/frame/election-provider-multi-phase/src/signed.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2020-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/frame/election-provider-multi-phase/src/unsigned.rs b/frame/election-provider-multi-phase/src/unsigned.rs index cdc71f7bf..cff1e01d9 100644 --- a/frame/election-provider-multi-phase/src/unsigned.rs +++ b/frame/election-provider-multi-phase/src/unsigned.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2020-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/frame/election-provider-multi-phase/src/weights.rs b/frame/election-provider-multi-phase/src/weights.rs index 51bb7b0f1..f3ea218e4 100644 --- a/frame/election-provider-multi-phase/src/weights.rs +++ b/frame/election-provider-multi-phase/src/weights.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2023 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/frame/election-provider-support/benchmarking/src/lib.rs b/frame/election-provider-support/benchmarking/src/lib.rs index 5323513da..774b7036f 100644 --- a/frame/election-provider-support/benchmarking/src/lib.rs +++ b/frame/election-provider-support/benchmarking/src/lib.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2021-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/frame/election-provider-support/solution-type/src/codec.rs b/frame/election-provider-support/solution-type/src/codec.rs index 05d753d3d..3e91fc1ea 100644 --- a/frame/election-provider-support/solution-type/src/codec.rs +++ b/frame/election-provider-support/solution-type/src/codec.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2020-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/frame/election-provider-support/solution-type/src/from_assignment_helpers.rs b/frame/election-provider-support/solution-type/src/from_assignment_helpers.rs index e0613afc0..e8429b7f9 100644 --- a/frame/election-provider-support/solution-type/src/from_assignment_helpers.rs +++ b/frame/election-provider-support/solution-type/src/from_assignment_helpers.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2020-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/frame/election-provider-support/solution-type/src/index_assignment.rs b/frame/election-provider-support/solution-type/src/index_assignment.rs index ccdcb4ba2..251839303 100644 --- a/frame/election-provider-support/solution-type/src/index_assignment.rs +++ b/frame/election-provider-support/solution-type/src/index_assignment.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2020-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/frame/election-provider-support/solution-type/src/lib.rs b/frame/election-provider-support/solution-type/src/lib.rs index 0a5c11e76..f79554321 100644 --- a/frame/election-provider-support/solution-type/src/lib.rs +++ b/frame/election-provider-support/solution-type/src/lib.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2020-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/frame/election-provider-support/solution-type/src/single_page.rs b/frame/election-provider-support/solution-type/src/single_page.rs index a7ccf5085..688fee70a 100644 --- a/frame/election-provider-support/solution-type/src/single_page.rs +++ b/frame/election-provider-support/solution-type/src/single_page.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2019-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/frame/election-provider-support/src/lib.rs b/frame/election-provider-support/src/lib.rs index 9e60eb3be..750ccca46 100644 --- a/frame/election-provider-support/src/lib.rs +++ b/frame/election-provider-support/src/lib.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2020-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/frame/election-provider-support/src/mock.rs b/frame/election-provider-support/src/mock.rs index 0789ee90e..5cbe305eb 100644 --- a/frame/election-provider-support/src/mock.rs +++ b/frame/election-provider-support/src/mock.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2019-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/frame/election-provider-support/src/onchain.rs b/frame/election-provider-support/src/onchain.rs index 483c402fe..296ac976d 100644 --- a/frame/election-provider-support/src/onchain.rs +++ b/frame/election-provider-support/src/onchain.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2020-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/frame/election-provider-support/src/tests.rs b/frame/election-provider-support/src/tests.rs index c38444f01..73ce1427c 100644 --- a/frame/election-provider-support/src/tests.rs +++ b/frame/election-provider-support/src/tests.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2019-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/frame/election-provider-support/src/traits.rs b/frame/election-provider-support/src/traits.rs index ed812e2e0..43d183b33 100644 --- a/frame/election-provider-support/src/traits.rs +++ b/frame/election-provider-support/src/traits.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2019-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/frame/election-provider-support/src/weights.rs b/frame/election-provider-support/src/weights.rs index 44075ba87..fd4db1374 100644 --- a/frame/election-provider-support/src/weights.rs +++ b/frame/election-provider-support/src/weights.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/frame/elections-phragmen/src/benchmarking.rs b/frame/elections-phragmen/src/benchmarking.rs index b7c04bad3..56ea19578 100644 --- a/frame/elections-phragmen/src/benchmarking.rs +++ b/frame/elections-phragmen/src/benchmarking.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2020-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/frame/elections-phragmen/src/lib.rs b/frame/elections-phragmen/src/lib.rs index 37ee44f34..fa63762c5 100644 --- a/frame/elections-phragmen/src/lib.rs +++ b/frame/elections-phragmen/src/lib.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2019-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/frame/elections-phragmen/src/migrations/mod.rs b/frame/elections-phragmen/src/migrations/mod.rs index 7c62e8fa9..3c40c51b1 100644 --- a/frame/elections-phragmen/src/migrations/mod.rs +++ b/frame/elections-phragmen/src/migrations/mod.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2021-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/frame/elections-phragmen/src/migrations/v3.rs b/frame/elections-phragmen/src/migrations/v3.rs index e48cd6c1a..204614a5b 100644 --- a/frame/elections-phragmen/src/migrations/v3.rs +++ b/frame/elections-phragmen/src/migrations/v3.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2019-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/frame/elections-phragmen/src/migrations/v4.rs b/frame/elections-phragmen/src/migrations/v4.rs index 535a9fb82..7e946371f 100644 --- a/frame/elections-phragmen/src/migrations/v4.rs +++ b/frame/elections-phragmen/src/migrations/v4.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2021-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/frame/elections-phragmen/src/weights.rs b/frame/elections-phragmen/src/weights.rs index a13dadf21..24ab3bc15 100644 --- a/frame/elections-phragmen/src/weights.rs +++ b/frame/elections-phragmen/src/weights.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2023 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/frame/examples/basic/src/benchmarking.rs b/frame/examples/basic/src/benchmarking.rs index a9e2013b0..0d2e9c5b3 100644 --- a/frame/examples/basic/src/benchmarking.rs +++ b/frame/examples/basic/src/benchmarking.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2019-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/frame/examples/basic/src/lib.rs b/frame/examples/basic/src/lib.rs index d5045157d..a2b55bff1 100644 --- a/frame/examples/basic/src/lib.rs +++ b/frame/examples/basic/src/lib.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2017-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/frame/examples/basic/src/tests.rs b/frame/examples/basic/src/tests.rs index 891c7c786..2e6a03bdd 100644 --- a/frame/examples/basic/src/tests.rs +++ b/frame/examples/basic/src/tests.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2019-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/frame/examples/basic/src/weights.rs b/frame/examples/basic/src/weights.rs index a69f0824e..4ea1e01d7 100644 --- a/frame/examples/basic/src/weights.rs +++ b/frame/examples/basic/src/weights.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/frame/examples/offchain-worker/src/lib.rs b/frame/examples/offchain-worker/src/lib.rs index 203ea2f27..c9c72f8e8 100644 --- a/frame/examples/offchain-worker/src/lib.rs +++ b/frame/examples/offchain-worker/src/lib.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2020-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/frame/examples/offchain-worker/src/tests.rs b/frame/examples/offchain-worker/src/tests.rs index 2050d76a2..75a42e872 100644 --- a/frame/examples/offchain-worker/src/tests.rs +++ b/frame/examples/offchain-worker/src/tests.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2020-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/frame/executive/src/lib.rs b/frame/executive/src/lib.rs index b454d1e27..573332294 100644 --- a/frame/executive/src/lib.rs +++ b/frame/executive/src/lib.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2017-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/frame/fast-unstake/src/benchmarking.rs b/frame/fast-unstake/src/benchmarking.rs index e0e4186d7..5ec997e8e 100644 --- a/frame/fast-unstake/src/benchmarking.rs +++ b/frame/fast-unstake/src/benchmarking.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/frame/fast-unstake/src/lib.rs b/frame/fast-unstake/src/lib.rs index 82454a4b8..81eb3087b 100644 --- a/frame/fast-unstake/src/lib.rs +++ b/frame/fast-unstake/src/lib.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/frame/fast-unstake/src/migrations.rs b/frame/fast-unstake/src/migrations.rs index d2778d48b..e5ef91929 100644 --- a/frame/fast-unstake/src/migrations.rs +++ b/frame/fast-unstake/src/migrations.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/frame/fast-unstake/src/mock.rs b/frame/fast-unstake/src/mock.rs index 38cf41fde..b20ba43ab 100644 --- a/frame/fast-unstake/src/mock.rs +++ b/frame/fast-unstake/src/mock.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/frame/fast-unstake/src/tests.rs b/frame/fast-unstake/src/tests.rs index 3e62146ae..1bb6fc100 100644 --- a/frame/fast-unstake/src/tests.rs +++ b/frame/fast-unstake/src/tests.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/frame/fast-unstake/src/types.rs b/frame/fast-unstake/src/types.rs index 34ca6517f..3ec4b3a9b 100644 --- a/frame/fast-unstake/src/types.rs +++ b/frame/fast-unstake/src/types.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/frame/fast-unstake/src/weights.rs b/frame/fast-unstake/src/weights.rs index f7d0cacba..d1c659596 100644 --- a/frame/fast-unstake/src/weights.rs +++ b/frame/fast-unstake/src/weights.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2023 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/frame/grandpa/src/benchmarking.rs b/frame/grandpa/src/benchmarking.rs index 6eee12ad9..7a3849766 100644 --- a/frame/grandpa/src/benchmarking.rs +++ b/frame/grandpa/src/benchmarking.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2020-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/frame/grandpa/src/default_weights.rs b/frame/grandpa/src/default_weights.rs index ba343caba..6b203783e 100644 --- a/frame/grandpa/src/default_weights.rs +++ b/frame/grandpa/src/default_weights.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2020-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/frame/grandpa/src/equivocation.rs b/frame/grandpa/src/equivocation.rs index 23801a498..8de8d7ae6 100644 --- a/frame/grandpa/src/equivocation.rs +++ b/frame/grandpa/src/equivocation.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2017-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/frame/grandpa/src/lib.rs b/frame/grandpa/src/lib.rs index ea534947d..86751e23a 100644 --- a/frame/grandpa/src/lib.rs +++ b/frame/grandpa/src/lib.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2017-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/frame/grandpa/src/migrations.rs b/frame/grandpa/src/migrations.rs index f4a28fff1..6307cbdd3 100644 --- a/frame/grandpa/src/migrations.rs +++ b/frame/grandpa/src/migrations.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2021-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/frame/grandpa/src/migrations/v4.rs b/frame/grandpa/src/migrations/v4.rs index 33e200b72..8604296b6 100644 --- a/frame/grandpa/src/migrations/v4.rs +++ b/frame/grandpa/src/migrations/v4.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2021-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/frame/grandpa/src/mock.rs b/frame/grandpa/src/mock.rs index 7d54966a4..0948be008 100644 --- a/frame/grandpa/src/mock.rs +++ b/frame/grandpa/src/mock.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2018-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/frame/grandpa/src/tests.rs b/frame/grandpa/src/tests.rs index e090dcebb..4fd5727d5 100644 --- a/frame/grandpa/src/tests.rs +++ b/frame/grandpa/src/tests.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2017-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/frame/identity/src/benchmarking.rs b/frame/identity/src/benchmarking.rs index 2d485af0f..4b51d23f6 100644 --- a/frame/identity/src/benchmarking.rs +++ b/frame/identity/src/benchmarking.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2020-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/frame/identity/src/lib.rs b/frame/identity/src/lib.rs index 39d828ef2..d1b764591 100644 --- a/frame/identity/src/lib.rs +++ b/frame/identity/src/lib.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2019-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/frame/identity/src/tests.rs b/frame/identity/src/tests.rs index baca70e27..0e15f08dd 100644 --- a/frame/identity/src/tests.rs +++ b/frame/identity/src/tests.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2019-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/frame/identity/src/types.rs b/frame/identity/src/types.rs index b1f15da3b..4d59a5280 100644 --- a/frame/identity/src/types.rs +++ b/frame/identity/src/types.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2021-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/frame/identity/src/weights.rs b/frame/identity/src/weights.rs index 7bcbf539d..647588052 100644 --- a/frame/identity/src/weights.rs +++ b/frame/identity/src/weights.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2023 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/frame/im-online/src/benchmarking.rs b/frame/im-online/src/benchmarking.rs index e4d1a6465..f90dcd53b 100644 --- a/frame/im-online/src/benchmarking.rs +++ b/frame/im-online/src/benchmarking.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2020-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/frame/im-online/src/lib.rs b/frame/im-online/src/lib.rs index 3be1f53fc..cdac51c94 100644 --- a/frame/im-online/src/lib.rs +++ b/frame/im-online/src/lib.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2019-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/frame/im-online/src/mock.rs b/frame/im-online/src/mock.rs index 783e68dfe..97b411e87 100644 --- a/frame/im-online/src/mock.rs +++ b/frame/im-online/src/mock.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2019-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/frame/im-online/src/tests.rs b/frame/im-online/src/tests.rs index 2c026f717..8cc7f38eb 100644 --- a/frame/im-online/src/tests.rs +++ b/frame/im-online/src/tests.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2019-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/frame/im-online/src/weights.rs b/frame/im-online/src/weights.rs index 4ea275a33..ee7cabf8a 100644 --- a/frame/im-online/src/weights.rs +++ b/frame/im-online/src/weights.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2023 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/frame/indices/src/benchmarking.rs b/frame/indices/src/benchmarking.rs index ce1ea9f4d..bd173815c 100644 --- a/frame/indices/src/benchmarking.rs +++ b/frame/indices/src/benchmarking.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2019-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/frame/indices/src/lib.rs b/frame/indices/src/lib.rs index 0eaee0f4b..82dc48f7b 100644 --- a/frame/indices/src/lib.rs +++ b/frame/indices/src/lib.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2017-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/frame/indices/src/mock.rs b/frame/indices/src/mock.rs index 6b5373bb4..6cf1f9d97 100644 --- a/frame/indices/src/mock.rs +++ b/frame/indices/src/mock.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2018-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/frame/indices/src/tests.rs b/frame/indices/src/tests.rs index bed6cfffa..56b101900 100644 --- a/frame/indices/src/tests.rs +++ b/frame/indices/src/tests.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2017-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/frame/indices/src/weights.rs b/frame/indices/src/weights.rs index 36dd79ece..fcb71177a 100644 --- a/frame/indices/src/weights.rs +++ b/frame/indices/src/weights.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2023 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/frame/insecure-randomness-collective-flip/src/lib.rs b/frame/insecure-randomness-collective-flip/src/lib.rs index 1d7ddc371..c73419efe 100644 --- a/frame/insecure-randomness-collective-flip/src/lib.rs +++ b/frame/insecure-randomness-collective-flip/src/lib.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2019-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/frame/lottery/src/benchmarking.rs b/frame/lottery/src/benchmarking.rs index 0db7fabb3..9216464b0 100644 --- a/frame/lottery/src/benchmarking.rs +++ b/frame/lottery/src/benchmarking.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2021-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/frame/lottery/src/lib.rs b/frame/lottery/src/lib.rs index 3255062e3..bd7ca40fb 100644 --- a/frame/lottery/src/lib.rs +++ b/frame/lottery/src/lib.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2021-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/frame/lottery/src/mock.rs b/frame/lottery/src/mock.rs index 1977da595..9b1a933d0 100644 --- a/frame/lottery/src/mock.rs +++ b/frame/lottery/src/mock.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2021-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/frame/lottery/src/tests.rs b/frame/lottery/src/tests.rs index 0eaf08056..09386c7e3 100644 --- a/frame/lottery/src/tests.rs +++ b/frame/lottery/src/tests.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2021-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/frame/lottery/src/weights.rs b/frame/lottery/src/weights.rs index e2cf9ea2b..e7857b230 100644 --- a/frame/lottery/src/weights.rs +++ b/frame/lottery/src/weights.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2023 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/frame/membership/src/lib.rs b/frame/membership/src/lib.rs index 8b39a08ce..04263b93e 100644 --- a/frame/membership/src/lib.rs +++ b/frame/membership/src/lib.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2019-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/frame/membership/src/migrations/mod.rs b/frame/membership/src/migrations/mod.rs index 235d0f1c7..2487ed1d5 100644 --- a/frame/membership/src/migrations/mod.rs +++ b/frame/membership/src/migrations/mod.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2019-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/frame/membership/src/migrations/v4.rs b/frame/membership/src/migrations/v4.rs index cbc58cd6a..38e97af51 100644 --- a/frame/membership/src/migrations/v4.rs +++ b/frame/membership/src/migrations/v4.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2019-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/frame/membership/src/weights.rs b/frame/membership/src/weights.rs index 1be50afc5..17f4b2f84 100644 --- a/frame/membership/src/weights.rs +++ b/frame/membership/src/weights.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2023 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/frame/merkle-mountain-range/src/benchmarking.rs b/frame/merkle-mountain-range/src/benchmarking.rs index 7731c0e50..9eb676a4e 100644 --- a/frame/merkle-mountain-range/src/benchmarking.rs +++ b/frame/merkle-mountain-range/src/benchmarking.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2020-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/frame/merkle-mountain-range/src/default_weights.rs b/frame/merkle-mountain-range/src/default_weights.rs index e4f9750fb..5d2e37cdd 100644 --- a/frame/merkle-mountain-range/src/default_weights.rs +++ b/frame/merkle-mountain-range/src/default_weights.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2020-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/frame/merkle-mountain-range/src/lib.rs b/frame/merkle-mountain-range/src/lib.rs index 170c1d724..0b4c2f8d9 100644 --- a/frame/merkle-mountain-range/src/lib.rs +++ b/frame/merkle-mountain-range/src/lib.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2020-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/frame/merkle-mountain-range/src/mmr/mmr.rs b/frame/merkle-mountain-range/src/mmr/mmr.rs index cdb189a14..beb4ac977 100644 --- a/frame/merkle-mountain-range/src/mmr/mmr.rs +++ b/frame/merkle-mountain-range/src/mmr/mmr.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2020-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/frame/merkle-mountain-range/src/mmr/mod.rs b/frame/merkle-mountain-range/src/mmr/mod.rs index 51de29475..536faa68e 100644 --- a/frame/merkle-mountain-range/src/mmr/mod.rs +++ b/frame/merkle-mountain-range/src/mmr/mod.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2020-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/frame/merkle-mountain-range/src/mmr/storage.rs b/frame/merkle-mountain-range/src/mmr/storage.rs index 2e3b174f3..d7466784e 100644 --- a/frame/merkle-mountain-range/src/mmr/storage.rs +++ b/frame/merkle-mountain-range/src/mmr/storage.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2020-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/frame/merkle-mountain-range/src/mock.rs b/frame/merkle-mountain-range/src/mock.rs index 3fd442758..292c80a48 100644 --- a/frame/merkle-mountain-range/src/mock.rs +++ b/frame/merkle-mountain-range/src/mock.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2020-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/frame/merkle-mountain-range/src/tests.rs b/frame/merkle-mountain-range/src/tests.rs index b5f9a78ed..b628b51d2 100644 --- a/frame/merkle-mountain-range/src/tests.rs +++ b/frame/merkle-mountain-range/src/tests.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2020-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/frame/message-queue/src/benchmarking.rs b/frame/message-queue/src/benchmarking.rs index 97ca71ce9..b53527048 100644 --- a/frame/message-queue/src/benchmarking.rs +++ b/frame/message-queue/src/benchmarking.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2023 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/frame/message-queue/src/integration_test.rs b/frame/message-queue/src/integration_test.rs index c9e3b2e87..b89c78086 100644 --- a/frame/message-queue/src/integration_test.rs +++ b/frame/message-queue/src/integration_test.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/frame/message-queue/src/lib.rs b/frame/message-queue/src/lib.rs index 8d9faebe0..2b64e3fc9 100644 --- a/frame/message-queue/src/lib.rs +++ b/frame/message-queue/src/lib.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/frame/message-queue/src/mock.rs b/frame/message-queue/src/mock.rs index 37e504ac5..8817ebc35 100644 --- a/frame/message-queue/src/mock.rs +++ b/frame/message-queue/src/mock.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/frame/message-queue/src/mock_helpers.rs b/frame/message-queue/src/mock_helpers.rs index f12cf4cc4..64fc70057 100644 --- a/frame/message-queue/src/mock_helpers.rs +++ b/frame/message-queue/src/mock_helpers.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/frame/message-queue/src/tests.rs b/frame/message-queue/src/tests.rs index 103fb690d..f4cbd81ac 100644 --- a/frame/message-queue/src/tests.rs +++ b/frame/message-queue/src/tests.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2019-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/frame/message-queue/src/weights.rs b/frame/message-queue/src/weights.rs index 325fc2083..be2f801f1 100644 --- a/frame/message-queue/src/weights.rs +++ b/frame/message-queue/src/weights.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2023 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/frame/multisig/src/benchmarking.rs b/frame/multisig/src/benchmarking.rs index 8993908b2..ebe19df5d 100644 --- a/frame/multisig/src/benchmarking.rs +++ b/frame/multisig/src/benchmarking.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2019-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/frame/multisig/src/lib.rs b/frame/multisig/src/lib.rs index de20a9e51..fb10838ce 100644 --- a/frame/multisig/src/lib.rs +++ b/frame/multisig/src/lib.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2019-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/frame/multisig/src/migrations.rs b/frame/multisig/src/migrations.rs index 5085297cd..2a9c858a5 100644 --- a/frame/multisig/src/migrations.rs +++ b/frame/multisig/src/migrations.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2019-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/frame/multisig/src/tests.rs b/frame/multisig/src/tests.rs index 13493587c..c5517195d 100644 --- a/frame/multisig/src/tests.rs +++ b/frame/multisig/src/tests.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2019-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/frame/multisig/src/weights.rs b/frame/multisig/src/weights.rs index 46bf911f2..eff6a2980 100644 --- a/frame/multisig/src/weights.rs +++ b/frame/multisig/src/weights.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2023 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/frame/nfts/src/benchmarking.rs b/frame/nfts/src/benchmarking.rs index 9e724a2f6..efa0a7412 100644 --- a/frame/nfts/src/benchmarking.rs +++ b/frame/nfts/src/benchmarking.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2020-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/frame/nfts/src/common_functions.rs b/frame/nfts/src/common_functions.rs index 31f8a9661..1ef6591db 100644 --- a/frame/nfts/src/common_functions.rs +++ b/frame/nfts/src/common_functions.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2017-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/frame/nfts/src/features/approvals.rs b/frame/nfts/src/features/approvals.rs index cb5279fd9..408c44129 100644 --- a/frame/nfts/src/features/approvals.rs +++ b/frame/nfts/src/features/approvals.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/frame/nfts/src/features/atomic_swap.rs b/frame/nfts/src/features/atomic_swap.rs index bacaccdae..505056be9 100644 --- a/frame/nfts/src/features/atomic_swap.rs +++ b/frame/nfts/src/features/atomic_swap.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/frame/nfts/src/features/attributes.rs b/frame/nfts/src/features/attributes.rs index 51c75233c..e310a5e2e 100644 --- a/frame/nfts/src/features/attributes.rs +++ b/frame/nfts/src/features/attributes.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/frame/nfts/src/features/buy_sell.rs b/frame/nfts/src/features/buy_sell.rs index 8ba5171f8..ad721e074 100644 --- a/frame/nfts/src/features/buy_sell.rs +++ b/frame/nfts/src/features/buy_sell.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/frame/nfts/src/features/create_delete_collection.rs b/frame/nfts/src/features/create_delete_collection.rs index a2caa04bc..8187fff7f 100644 --- a/frame/nfts/src/features/create_delete_collection.rs +++ b/frame/nfts/src/features/create_delete_collection.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/frame/nfts/src/features/create_delete_item.rs b/frame/nfts/src/features/create_delete_item.rs index 63d7a540c..0319f2c8a 100644 --- a/frame/nfts/src/features/create_delete_item.rs +++ b/frame/nfts/src/features/create_delete_item.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/frame/nfts/src/features/lock.rs b/frame/nfts/src/features/lock.rs index e96a30dfd..c28fdda98 100644 --- a/frame/nfts/src/features/lock.rs +++ b/frame/nfts/src/features/lock.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/frame/nfts/src/features/metadata.rs b/frame/nfts/src/features/metadata.rs index c4d355f19..232262bdb 100644 --- a/frame/nfts/src/features/metadata.rs +++ b/frame/nfts/src/features/metadata.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/frame/nfts/src/features/mod.rs b/frame/nfts/src/features/mod.rs index b77ee9bf2..752feaf5d 100644 --- a/frame/nfts/src/features/mod.rs +++ b/frame/nfts/src/features/mod.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/frame/nfts/src/features/roles.rs b/frame/nfts/src/features/roles.rs index d6be9965a..f3fa277b1 100644 --- a/frame/nfts/src/features/roles.rs +++ b/frame/nfts/src/features/roles.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/frame/nfts/src/features/settings.rs b/frame/nfts/src/features/settings.rs index 7c2971109..91bff9dc6 100644 --- a/frame/nfts/src/features/settings.rs +++ b/frame/nfts/src/features/settings.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/frame/nfts/src/features/transfer.rs b/frame/nfts/src/features/transfer.rs index 3a34f0b8c..998df81cd 100644 --- a/frame/nfts/src/features/transfer.rs +++ b/frame/nfts/src/features/transfer.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/frame/nfts/src/impl_nonfungibles.rs b/frame/nfts/src/impl_nonfungibles.rs index 86d82b402..ac02355ba 100644 --- a/frame/nfts/src/impl_nonfungibles.rs +++ b/frame/nfts/src/impl_nonfungibles.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2017-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/frame/nfts/src/lib.rs b/frame/nfts/src/lib.rs index cec5ea7ff..41313e651 100644 --- a/frame/nfts/src/lib.rs +++ b/frame/nfts/src/lib.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2017-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/frame/nfts/src/macros.rs b/frame/nfts/src/macros.rs index 07a8f3b9f..8b0b8358d 100644 --- a/frame/nfts/src/macros.rs +++ b/frame/nfts/src/macros.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/frame/nfts/src/mock.rs b/frame/nfts/src/mock.rs index d0ef3cf0b..a9db1a62c 100644 --- a/frame/nfts/src/mock.rs +++ b/frame/nfts/src/mock.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2019-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/frame/nfts/src/tests.rs b/frame/nfts/src/tests.rs index fce9073af..fb8265007 100644 --- a/frame/nfts/src/tests.rs +++ b/frame/nfts/src/tests.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2019-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/frame/nfts/src/types.rs b/frame/nfts/src/types.rs index c2ecd61c2..860f937aa 100644 --- a/frame/nfts/src/types.rs +++ b/frame/nfts/src/types.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2017-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/frame/nfts/src/weights.rs b/frame/nfts/src/weights.rs index 40592c299..dc59330f3 100644 --- a/frame/nfts/src/weights.rs +++ b/frame/nfts/src/weights.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2023 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/frame/nicks/src/lib.rs b/frame/nicks/src/lib.rs index 05d1a3639..b457cc299 100644 --- a/frame/nicks/src/lib.rs +++ b/frame/nicks/src/lib.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2019-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/frame/nis/src/benchmarking.rs b/frame/nis/src/benchmarking.rs index 10d3a238a..6fc9fbc2b 100644 --- a/frame/nis/src/benchmarking.rs +++ b/frame/nis/src/benchmarking.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2021-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/frame/nis/src/lib.rs b/frame/nis/src/lib.rs index fa0163fb6..9cae5e2aa 100644 --- a/frame/nis/src/lib.rs +++ b/frame/nis/src/lib.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2019-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/frame/nis/src/mock.rs b/frame/nis/src/mock.rs index 585ed2d8b..8df9f89c1 100644 --- a/frame/nis/src/mock.rs +++ b/frame/nis/src/mock.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2019-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/frame/nis/src/tests.rs b/frame/nis/src/tests.rs index b5feb4df1..d0808367e 100644 --- a/frame/nis/src/tests.rs +++ b/frame/nis/src/tests.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2019-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/frame/nis/src/weights.rs b/frame/nis/src/weights.rs index 07221f358..36a169f16 100644 --- a/frame/nis/src/weights.rs +++ b/frame/nis/src/weights.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2023 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/frame/node-authorization/src/lib.rs b/frame/node-authorization/src/lib.rs index 543ba2450..d91a769b3 100644 --- a/frame/node-authorization/src/lib.rs +++ b/frame/node-authorization/src/lib.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2019-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/frame/node-authorization/src/mock.rs b/frame/node-authorization/src/mock.rs index fcf7ff018..b7c5957e1 100644 --- a/frame/node-authorization/src/mock.rs +++ b/frame/node-authorization/src/mock.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2019-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/frame/node-authorization/src/tests.rs b/frame/node-authorization/src/tests.rs index d9db09db4..4704b5adf 100644 --- a/frame/node-authorization/src/tests.rs +++ b/frame/node-authorization/src/tests.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2019-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/frame/node-authorization/src/weights.rs b/frame/node-authorization/src/weights.rs index 63728c21b..f1ff9580d 100644 --- a/frame/node-authorization/src/weights.rs +++ b/frame/node-authorization/src/weights.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2021-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/frame/nomination-pools/benchmarking/src/lib.rs b/frame/nomination-pools/benchmarking/src/lib.rs index 675a4a832..3b6df669a 100644 --- a/frame/nomination-pools/benchmarking/src/lib.rs +++ b/frame/nomination-pools/benchmarking/src/lib.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2020-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/frame/nomination-pools/benchmarking/src/mock.rs b/frame/nomination-pools/benchmarking/src/mock.rs index 06a668385..4e188ea7e 100644 --- a/frame/nomination-pools/benchmarking/src/mock.rs +++ b/frame/nomination-pools/benchmarking/src/mock.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2020-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/frame/nomination-pools/fuzzer/src/call.rs b/frame/nomination-pools/fuzzer/src/call.rs index 805cc265d..5e8247120 100644 --- a/frame/nomination-pools/fuzzer/src/call.rs +++ b/frame/nomination-pools/fuzzer/src/call.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2019-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/frame/nomination-pools/runtime-api/src/lib.rs b/frame/nomination-pools/runtime-api/src/lib.rs index 94573bfdb..881c3c363 100644 --- a/frame/nomination-pools/runtime-api/src/lib.rs +++ b/frame/nomination-pools/runtime-api/src/lib.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/frame/nomination-pools/src/lib.rs b/frame/nomination-pools/src/lib.rs index 4a8016846..f61a37644 100644 --- a/frame/nomination-pools/src/lib.rs +++ b/frame/nomination-pools/src/lib.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2020-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/frame/nomination-pools/src/migration.rs b/frame/nomination-pools/src/migration.rs index 53087e763..a16963269 100644 --- a/frame/nomination-pools/src/migration.rs +++ b/frame/nomination-pools/src/migration.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/frame/nomination-pools/src/tests.rs b/frame/nomination-pools/src/tests.rs index 46330051b..59fd17be5 100644 --- a/frame/nomination-pools/src/tests.rs +++ b/frame/nomination-pools/src/tests.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2020-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/frame/nomination-pools/src/weights.rs b/frame/nomination-pools/src/weights.rs index ae7301594..c24689558 100644 --- a/frame/nomination-pools/src/weights.rs +++ b/frame/nomination-pools/src/weights.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2023 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/frame/nomination-pools/test-staking/src/lib.rs b/frame/nomination-pools/test-staking/src/lib.rs index 933e42578..158749258 100644 --- a/frame/nomination-pools/test-staking/src/lib.rs +++ b/frame/nomination-pools/test-staking/src/lib.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/frame/nomination-pools/test-staking/src/mock.rs b/frame/nomination-pools/test-staking/src/mock.rs index c67aec013..3d0ab2c6f 100644 --- a/frame/nomination-pools/test-staking/src/mock.rs +++ b/frame/nomination-pools/test-staking/src/mock.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/frame/offences/benchmarking/src/lib.rs b/frame/offences/benchmarking/src/lib.rs index b71321906..16d77335e 100644 --- a/frame/offences/benchmarking/src/lib.rs +++ b/frame/offences/benchmarking/src/lib.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2020-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/frame/offences/benchmarking/src/mock.rs b/frame/offences/benchmarking/src/mock.rs index 592e821a8..233aa449d 100644 --- a/frame/offences/benchmarking/src/mock.rs +++ b/frame/offences/benchmarking/src/mock.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2020-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/frame/offences/src/lib.rs b/frame/offences/src/lib.rs index 7858b0271..6ba16161b 100644 --- a/frame/offences/src/lib.rs +++ b/frame/offences/src/lib.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2019-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/frame/offences/src/migration.rs b/frame/offences/src/migration.rs index e8fab1c0b..95b94ba87 100644 --- a/frame/offences/src/migration.rs +++ b/frame/offences/src/migration.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2021-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/frame/offences/src/mock.rs b/frame/offences/src/mock.rs index 63d2b0de3..5fc987ebd 100644 --- a/frame/offences/src/mock.rs +++ b/frame/offences/src/mock.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2018-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/frame/offences/src/tests.rs b/frame/offences/src/tests.rs index 266e05deb..a5c18020b 100644 --- a/frame/offences/src/tests.rs +++ b/frame/offences/src/tests.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2017-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/frame/preimage/src/benchmarking.rs b/frame/preimage/src/benchmarking.rs index 56361b88c..b719c28be 100644 --- a/frame/preimage/src/benchmarking.rs +++ b/frame/preimage/src/benchmarking.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2020-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/frame/preimage/src/lib.rs b/frame/preimage/src/lib.rs index bf7d60205..74a3176b1 100644 --- a/frame/preimage/src/lib.rs +++ b/frame/preimage/src/lib.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2017-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/frame/preimage/src/migration.rs b/frame/preimage/src/migration.rs index a5d15c23c..be352201d 100644 --- a/frame/preimage/src/migration.rs +++ b/frame/preimage/src/migration.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/frame/preimage/src/mock.rs b/frame/preimage/src/mock.rs index d81f5a9c3..23875ccb0 100644 --- a/frame/preimage/src/mock.rs +++ b/frame/preimage/src/mock.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2017-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/frame/preimage/src/tests.rs b/frame/preimage/src/tests.rs index f480b9c36..63a178c40 100644 --- a/frame/preimage/src/tests.rs +++ b/frame/preimage/src/tests.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2017-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/frame/preimage/src/weights.rs b/frame/preimage/src/weights.rs index 394c76956..7d181be01 100644 --- a/frame/preimage/src/weights.rs +++ b/frame/preimage/src/weights.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2023 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/frame/proxy/src/benchmarking.rs b/frame/proxy/src/benchmarking.rs index ef3df02dc..7244dd5f1 100644 --- a/frame/proxy/src/benchmarking.rs +++ b/frame/proxy/src/benchmarking.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2019-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/frame/proxy/src/lib.rs b/frame/proxy/src/lib.rs index dfe8c86ca..c26a53d1a 100644 --- a/frame/proxy/src/lib.rs +++ b/frame/proxy/src/lib.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2019-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/frame/proxy/src/tests.rs b/frame/proxy/src/tests.rs index 0e7db3512..c49c344ac 100644 --- a/frame/proxy/src/tests.rs +++ b/frame/proxy/src/tests.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2019-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/frame/proxy/src/weights.rs b/frame/proxy/src/weights.rs index 53e368aca..5e85e148a 100644 --- a/frame/proxy/src/weights.rs +++ b/frame/proxy/src/weights.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2023 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/frame/ranked-collective/src/benchmarking.rs b/frame/ranked-collective/src/benchmarking.rs index ad81325be..b610d1000 100644 --- a/frame/ranked-collective/src/benchmarking.rs +++ b/frame/ranked-collective/src/benchmarking.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2020-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/frame/ranked-collective/src/lib.rs b/frame/ranked-collective/src/lib.rs index a65d184d6..30bb280a1 100644 --- a/frame/ranked-collective/src/lib.rs +++ b/frame/ranked-collective/src/lib.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2017-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/frame/ranked-collective/src/tests.rs b/frame/ranked-collective/src/tests.rs index c11d4877d..91244a90b 100644 --- a/frame/ranked-collective/src/tests.rs +++ b/frame/ranked-collective/src/tests.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2017-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/frame/ranked-collective/src/weights.rs b/frame/ranked-collective/src/weights.rs index 2d5ed3afd..1f50441aa 100644 --- a/frame/ranked-collective/src/weights.rs +++ b/frame/ranked-collective/src/weights.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2023 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/frame/recovery/src/benchmarking.rs b/frame/recovery/src/benchmarking.rs index 48ca7dad7..2deb55bb6 100644 --- a/frame/recovery/src/benchmarking.rs +++ b/frame/recovery/src/benchmarking.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/frame/recovery/src/lib.rs b/frame/recovery/src/lib.rs index 9c57ca79d..b32d19d5d 100644 --- a/frame/recovery/src/lib.rs +++ b/frame/recovery/src/lib.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2020-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/frame/recovery/src/mock.rs b/frame/recovery/src/mock.rs index 05ca8e6a3..1d21be801 100644 --- a/frame/recovery/src/mock.rs +++ b/frame/recovery/src/mock.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2020-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/frame/recovery/src/tests.rs b/frame/recovery/src/tests.rs index b037a8110..9381d6a88 100644 --- a/frame/recovery/src/tests.rs +++ b/frame/recovery/src/tests.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2020-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/frame/recovery/src/weights.rs b/frame/recovery/src/weights.rs index 73f61654a..7a67c716f 100644 --- a/frame/recovery/src/weights.rs +++ b/frame/recovery/src/weights.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2023 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/frame/referenda/src/benchmarking.rs b/frame/referenda/src/benchmarking.rs index ea44beeb3..288c65fea 100644 --- a/frame/referenda/src/benchmarking.rs +++ b/frame/referenda/src/benchmarking.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2020-2021 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/frame/referenda/src/branch.rs b/frame/referenda/src/branch.rs index d3744979f..499e62387 100644 --- a/frame/referenda/src/branch.rs +++ b/frame/referenda/src/branch.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2017-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/frame/referenda/src/lib.rs b/frame/referenda/src/lib.rs index 4491c1b40..dccc40112 100644 --- a/frame/referenda/src/lib.rs +++ b/frame/referenda/src/lib.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2017-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/frame/referenda/src/migration.rs b/frame/referenda/src/migration.rs index 39575556e..e15eb499b 100644 --- a/frame/referenda/src/migration.rs +++ b/frame/referenda/src/migration.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2017-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/frame/referenda/src/mock.rs b/frame/referenda/src/mock.rs index f6e19d06e..ade2a09a9 100644 --- a/frame/referenda/src/mock.rs +++ b/frame/referenda/src/mock.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2017-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/frame/referenda/src/tests.rs b/frame/referenda/src/tests.rs index 034454cfc..f728350c3 100644 --- a/frame/referenda/src/tests.rs +++ b/frame/referenda/src/tests.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2017-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/frame/referenda/src/types.rs b/frame/referenda/src/types.rs index 0763a71a9..d61b89554 100644 --- a/frame/referenda/src/types.rs +++ b/frame/referenda/src/types.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2017-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/frame/referenda/src/weights.rs b/frame/referenda/src/weights.rs index ab4975951..817e0d88d 100644 --- a/frame/referenda/src/weights.rs +++ b/frame/referenda/src/weights.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2023 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/frame/remark/src/benchmarking.rs b/frame/remark/src/benchmarking.rs index ee93cb24a..831946834 100644 --- a/frame/remark/src/benchmarking.rs +++ b/frame/remark/src/benchmarking.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2021-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/frame/remark/src/lib.rs b/frame/remark/src/lib.rs index 80fe393c2..8ca3cd395 100644 --- a/frame/remark/src/lib.rs +++ b/frame/remark/src/lib.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2017-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/frame/remark/src/mock.rs b/frame/remark/src/mock.rs index 22467796c..39f5d50ed 100644 --- a/frame/remark/src/mock.rs +++ b/frame/remark/src/mock.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2019-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/frame/remark/src/tests.rs b/frame/remark/src/tests.rs index 2278e3817..51fb32b60 100644 --- a/frame/remark/src/tests.rs +++ b/frame/remark/src/tests.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2019-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/frame/remark/src/weights.rs b/frame/remark/src/weights.rs index 9142ed04c..0e18e10c6 100644 --- a/frame/remark/src/weights.rs +++ b/frame/remark/src/weights.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2023 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/frame/root-offences/src/lib.rs b/frame/root-offences/src/lib.rs index ed039f46b..92f3e17b5 100644 --- a/frame/root-offences/src/lib.rs +++ b/frame/root-offences/src/lib.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/frame/root-offences/src/mock.rs b/frame/root-offences/src/mock.rs index 273fbf614..0937c43d6 100644 --- a/frame/root-offences/src/mock.rs +++ b/frame/root-offences/src/mock.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/frame/root-offences/src/tests.rs b/frame/root-offences/src/tests.rs index b0df4fcee..f96884d75 100644 --- a/frame/root-offences/src/tests.rs +++ b/frame/root-offences/src/tests.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/frame/root-testing/src/lib.rs b/frame/root-testing/src/lib.rs index da6790496..ad0e4cfea 100644 --- a/frame/root-testing/src/lib.rs +++ b/frame/root-testing/src/lib.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/frame/scheduler/src/benchmarking.rs b/frame/scheduler/src/benchmarking.rs index 94a833bd9..d56e007ec 100644 --- a/frame/scheduler/src/benchmarking.rs +++ b/frame/scheduler/src/benchmarking.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2020-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/frame/scheduler/src/lib.rs b/frame/scheduler/src/lib.rs index 97aaaaf0d..95bfe8411 100644 --- a/frame/scheduler/src/lib.rs +++ b/frame/scheduler/src/lib.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2017-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/frame/scheduler/src/migration.rs b/frame/scheduler/src/migration.rs index f71b9457f..e641d2895 100644 --- a/frame/scheduler/src/migration.rs +++ b/frame/scheduler/src/migration.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2017-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/frame/scheduler/src/mock.rs b/frame/scheduler/src/mock.rs index 0aaac5666..5a2328971 100644 --- a/frame/scheduler/src/mock.rs +++ b/frame/scheduler/src/mock.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2017-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/frame/scheduler/src/tests.rs b/frame/scheduler/src/tests.rs index a261c6718..258e6a281 100644 --- a/frame/scheduler/src/tests.rs +++ b/frame/scheduler/src/tests.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2017-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/frame/scheduler/src/weights.rs b/frame/scheduler/src/weights.rs index 9be3bfaef..6b786ef8b 100644 --- a/frame/scheduler/src/weights.rs +++ b/frame/scheduler/src/weights.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2023 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/frame/scored-pool/src/lib.rs b/frame/scored-pool/src/lib.rs index 5db9c6506..16d59b660 100644 --- a/frame/scored-pool/src/lib.rs +++ b/frame/scored-pool/src/lib.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2019-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/frame/scored-pool/src/mock.rs b/frame/scored-pool/src/mock.rs index 951871dd8..a91b1a60f 100644 --- a/frame/scored-pool/src/mock.rs +++ b/frame/scored-pool/src/mock.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2019-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/frame/scored-pool/src/tests.rs b/frame/scored-pool/src/tests.rs index 8f4daff47..96c94a6c1 100644 --- a/frame/scored-pool/src/tests.rs +++ b/frame/scored-pool/src/tests.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2019-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/frame/session/benchmarking/src/lib.rs b/frame/session/benchmarking/src/lib.rs index a6c25f5e1..7f64dc70f 100644 --- a/frame/session/benchmarking/src/lib.rs +++ b/frame/session/benchmarking/src/lib.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2020-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/frame/session/benchmarking/src/mock.rs b/frame/session/benchmarking/src/mock.rs index 0699640bc..4c4accbbf 100644 --- a/frame/session/benchmarking/src/mock.rs +++ b/frame/session/benchmarking/src/mock.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2020-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/frame/session/src/historical/mod.rs b/frame/session/src/historical/mod.rs index 45b4ba3c0..d98276794 100644 --- a/frame/session/src/historical/mod.rs +++ b/frame/session/src/historical/mod.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2019-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/frame/session/src/historical/offchain.rs b/frame/session/src/historical/offchain.rs index ececb8af5..4185788cb 100644 --- a/frame/session/src/historical/offchain.rs +++ b/frame/session/src/historical/offchain.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2019-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/frame/session/src/historical/onchain.rs b/frame/session/src/historical/onchain.rs index c7160e2fc..97a7f02bd 100644 --- a/frame/session/src/historical/onchain.rs +++ b/frame/session/src/historical/onchain.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2019-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/frame/session/src/historical/shared.rs b/frame/session/src/historical/shared.rs index faa91f791..297385dfb 100644 --- a/frame/session/src/historical/shared.rs +++ b/frame/session/src/historical/shared.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2019-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/frame/session/src/lib.rs b/frame/session/src/lib.rs index 38a5c0f57..7d5dd6324 100644 --- a/frame/session/src/lib.rs +++ b/frame/session/src/lib.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2017-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/frame/session/src/migrations/mod.rs b/frame/session/src/migrations/mod.rs index 5981e87b6..3b15d0ac4 100644 --- a/frame/session/src/migrations/mod.rs +++ b/frame/session/src/migrations/mod.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2019-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/frame/session/src/migrations/v1.rs b/frame/session/src/migrations/v1.rs index 6689ca729..394a1f4a5 100644 --- a/frame/session/src/migrations/v1.rs +++ b/frame/session/src/migrations/v1.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2019-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/frame/session/src/mock.rs b/frame/session/src/mock.rs index 1138bd954..d6b8d9e20 100644 --- a/frame/session/src/mock.rs +++ b/frame/session/src/mock.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2019-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/frame/session/src/tests.rs b/frame/session/src/tests.rs index 43809cc3a..69337e016 100644 --- a/frame/session/src/tests.rs +++ b/frame/session/src/tests.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2017-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/frame/session/src/weights.rs b/frame/session/src/weights.rs index aea9d9dbf..c01799ce5 100644 --- a/frame/session/src/weights.rs +++ b/frame/session/src/weights.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2023 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/frame/society/src/lib.rs b/frame/society/src/lib.rs index 4e83d938b..f3207dabd 100644 --- a/frame/society/src/lib.rs +++ b/frame/society/src/lib.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2020-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/frame/society/src/mock.rs b/frame/society/src/mock.rs index 24544bf9e..73565dedc 100644 --- a/frame/society/src/mock.rs +++ b/frame/society/src/mock.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2020-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/frame/society/src/tests.rs b/frame/society/src/tests.rs index 0b42f7703..8a5c626de 100644 --- a/frame/society/src/tests.rs +++ b/frame/society/src/tests.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2020-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/frame/staking/reward-curve/src/lib.rs b/frame/staking/reward-curve/src/lib.rs index e1ea8aa7b..ecf3af553 100644 --- a/frame/staking/reward-curve/src/lib.rs +++ b/frame/staking/reward-curve/src/lib.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2017-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/frame/staking/reward-curve/tests/test.rs b/frame/staking/reward-curve/tests/test.rs index aa19b1782..339e00322 100644 --- a/frame/staking/reward-curve/tests/test.rs +++ b/frame/staking/reward-curve/tests/test.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2019-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/frame/staking/reward-fn/src/lib.rs b/frame/staking/reward-fn/src/lib.rs index cc9919c28..2c7f4613b 100644 --- a/frame/staking/reward-fn/src/lib.rs +++ b/frame/staking/reward-fn/src/lib.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2021-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/frame/staking/reward-fn/tests/test.rs b/frame/staking/reward-fn/tests/test.rs index a79137716..d76d2ce5e 100644 --- a/frame/staking/reward-fn/tests/test.rs +++ b/frame/staking/reward-fn/tests/test.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2021-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/frame/staking/src/benchmarking.rs b/frame/staking/src/benchmarking.rs index a4366bbd8..ad7aab984 100644 --- a/frame/staking/src/benchmarking.rs +++ b/frame/staking/src/benchmarking.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2020-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/frame/staking/src/inflation.rs b/frame/staking/src/inflation.rs index c7519683c..8b4a85b6c 100644 --- a/frame/staking/src/inflation.rs +++ b/frame/staking/src/inflation.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2019-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/frame/staking/src/lib.rs b/frame/staking/src/lib.rs index 367205653..d6345c216 100644 --- a/frame/staking/src/lib.rs +++ b/frame/staking/src/lib.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2017-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/frame/staking/src/migrations.rs b/frame/staking/src/migrations.rs index a8d360c09..3c33ae2a3 100644 --- a/frame/staking/src/migrations.rs +++ b/frame/staking/src/migrations.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2020-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/frame/staking/src/mock.rs b/frame/staking/src/mock.rs index 30db036d0..e876940cb 100644 --- a/frame/staking/src/mock.rs +++ b/frame/staking/src/mock.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2018-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/frame/staking/src/pallet/impls.rs b/frame/staking/src/pallet/impls.rs index fc0bf082c..99c2f6043 100644 --- a/frame/staking/src/pallet/impls.rs +++ b/frame/staking/src/pallet/impls.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2017-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/frame/staking/src/pallet/mod.rs b/frame/staking/src/pallet/mod.rs index 4b0e8133b..a03053887 100644 --- a/frame/staking/src/pallet/mod.rs +++ b/frame/staking/src/pallet/mod.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2017-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/frame/staking/src/slashing.rs b/frame/staking/src/slashing.rs index aeea0a1a5..f73e3a6f8 100644 --- a/frame/staking/src/slashing.rs +++ b/frame/staking/src/slashing.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2019-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/frame/staking/src/testing_utils.rs b/frame/staking/src/testing_utils.rs index 0e0ac7652..9bd231cce 100644 --- a/frame/staking/src/testing_utils.rs +++ b/frame/staking/src/testing_utils.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2020-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/frame/staking/src/tests.rs b/frame/staking/src/tests.rs index e200f621b..16bafae01 100644 --- a/frame/staking/src/tests.rs +++ b/frame/staking/src/tests.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2017-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/frame/staking/src/weights.rs b/frame/staking/src/weights.rs index af2afa2b5..2c9e1b266 100644 --- a/frame/staking/src/weights.rs +++ b/frame/staking/src/weights.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2023 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/frame/state-trie-migration/src/lib.rs b/frame/state-trie-migration/src/lib.rs index 23f73bb56..0ebb8122d 100644 --- a/frame/state-trie-migration/src/lib.rs +++ b/frame/state-trie-migration/src/lib.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/frame/state-trie-migration/src/weights.rs b/frame/state-trie-migration/src/weights.rs index 29b7f7ad4..a833d816c 100644 --- a/frame/state-trie-migration/src/weights.rs +++ b/frame/state-trie-migration/src/weights.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2023 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/frame/sudo/src/extension.rs b/frame/sudo/src/extension.rs index 068fa2ed9..c717ff356 100644 --- a/frame/sudo/src/extension.rs +++ b/frame/sudo/src/extension.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/frame/sudo/src/lib.rs b/frame/sudo/src/lib.rs index 3a708b6d5..4d5149926 100644 --- a/frame/sudo/src/lib.rs +++ b/frame/sudo/src/lib.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2017-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/frame/sudo/src/mock.rs b/frame/sudo/src/mock.rs index f8a03836f..59a5cb6cc 100644 --- a/frame/sudo/src/mock.rs +++ b/frame/sudo/src/mock.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2020-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/frame/sudo/src/tests.rs b/frame/sudo/src/tests.rs index ae8f19873..e924bc77c 100644 --- a/frame/sudo/src/tests.rs +++ b/frame/sudo/src/tests.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2020-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/frame/support/procedural/src/benchmark.rs b/frame/support/procedural/src/benchmark.rs index fa7fd4492..6cf1d7ecc 100644 --- a/frame/support/procedural/src/benchmark.rs +++ b/frame/support/procedural/src/benchmark.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2023 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/frame/support/procedural/src/clone_no_bound.rs b/frame/support/procedural/src/clone_no_bound.rs index bd2741c0d..bbea2feff 100644 --- a/frame/support/procedural/src/clone_no_bound.rs +++ b/frame/support/procedural/src/clone_no_bound.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2020-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/frame/support/procedural/src/construct_runtime/expand/call.rs b/frame/support/procedural/src/construct_runtime/expand/call.rs index 8f07448f5..5ec665682 100644 --- a/frame/support/procedural/src/construct_runtime/expand/call.rs +++ b/frame/support/procedural/src/construct_runtime/expand/call.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2021-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/frame/support/procedural/src/construct_runtime/expand/config.rs b/frame/support/procedural/src/construct_runtime/expand/config.rs index 9b731a582..4d73ebb11 100644 --- a/frame/support/procedural/src/construct_runtime/expand/config.rs +++ b/frame/support/procedural/src/construct_runtime/expand/config.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2021-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/frame/support/procedural/src/construct_runtime/expand/event.rs b/frame/support/procedural/src/construct_runtime/expand/event.rs index b11fcef1b..fcd9b3214 100644 --- a/frame/support/procedural/src/construct_runtime/expand/event.rs +++ b/frame/support/procedural/src/construct_runtime/expand/event.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2021-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/frame/support/procedural/src/construct_runtime/expand/inherent.rs b/frame/support/procedural/src/construct_runtime/expand/inherent.rs index 599b34ba8..52586bd69 100644 --- a/frame/support/procedural/src/construct_runtime/expand/inherent.rs +++ b/frame/support/procedural/src/construct_runtime/expand/inherent.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2021-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/frame/support/procedural/src/construct_runtime/expand/metadata.rs b/frame/support/procedural/src/construct_runtime/expand/metadata.rs index ec90a0d30..e99961217 100644 --- a/frame/support/procedural/src/construct_runtime/expand/metadata.rs +++ b/frame/support/procedural/src/construct_runtime/expand/metadata.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2021-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/frame/support/procedural/src/construct_runtime/expand/mod.rs b/frame/support/procedural/src/construct_runtime/expand/mod.rs index 6c92d2c34..ace0b23bd 100644 --- a/frame/support/procedural/src/construct_runtime/expand/mod.rs +++ b/frame/support/procedural/src/construct_runtime/expand/mod.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2021-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/frame/support/procedural/src/construct_runtime/expand/origin.rs b/frame/support/procedural/src/construct_runtime/expand/origin.rs index 1551d85ea..e48a70a9d 100644 --- a/frame/support/procedural/src/construct_runtime/expand/origin.rs +++ b/frame/support/procedural/src/construct_runtime/expand/origin.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2021-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/frame/support/procedural/src/construct_runtime/expand/unsigned.rs b/frame/support/procedural/src/construct_runtime/expand/unsigned.rs index 1d5287794..33aadba0d 100644 --- a/frame/support/procedural/src/construct_runtime/expand/unsigned.rs +++ b/frame/support/procedural/src/construct_runtime/expand/unsigned.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2021-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/frame/support/procedural/src/construct_runtime/mod.rs b/frame/support/procedural/src/construct_runtime/mod.rs index 18b66ddff..37f23efed 100644 --- a/frame/support/procedural/src/construct_runtime/mod.rs +++ b/frame/support/procedural/src/construct_runtime/mod.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2019-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/frame/support/procedural/src/construct_runtime/parse.rs b/frame/support/procedural/src/construct_runtime/parse.rs index 7a5acf43b..b0bebcd9e 100644 --- a/frame/support/procedural/src/construct_runtime/parse.rs +++ b/frame/support/procedural/src/construct_runtime/parse.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2019-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/frame/support/procedural/src/crate_version.rs b/frame/support/procedural/src/crate_version.rs index e2be6dd88..3f728abdb 100644 --- a/frame/support/procedural/src/crate_version.rs +++ b/frame/support/procedural/src/crate_version.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2020-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/frame/support/procedural/src/debug_no_bound.rs b/frame/support/procedural/src/debug_no_bound.rs index 56168edb8..ae182829a 100644 --- a/frame/support/procedural/src/debug_no_bound.rs +++ b/frame/support/procedural/src/debug_no_bound.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2020-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/frame/support/procedural/src/default_no_bound.rs b/frame/support/procedural/src/default_no_bound.rs index b174a49e9..127b49087 100644 --- a/frame/support/procedural/src/default_no_bound.rs +++ b/frame/support/procedural/src/default_no_bound.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2021-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/frame/support/procedural/src/key_prefix.rs b/frame/support/procedural/src/key_prefix.rs index 05582f129..6f793d0e3 100644 --- a/frame/support/procedural/src/key_prefix.rs +++ b/frame/support/procedural/src/key_prefix.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2021-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/frame/support/procedural/src/lib.rs b/frame/support/procedural/src/lib.rs index 67e592f4c..fec824107 100644 --- a/frame/support/procedural/src/lib.rs +++ b/frame/support/procedural/src/lib.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2017-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/frame/support/procedural/src/match_and_insert.rs b/frame/support/procedural/src/match_and_insert.rs index 79d1da754..aa9cc56d7 100644 --- a/frame/support/procedural/src/match_and_insert.rs +++ b/frame/support/procedural/src/match_and_insert.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2021-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/frame/support/procedural/src/pallet/expand/call.rs b/frame/support/procedural/src/pallet/expand/call.rs index 30ef1a8fc..72367dc39 100644 --- a/frame/support/procedural/src/pallet/expand/call.rs +++ b/frame/support/procedural/src/pallet/expand/call.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2020-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/frame/support/procedural/src/pallet/expand/config.rs b/frame/support/procedural/src/pallet/expand/config.rs index 8ad34361d..c70f6eb80 100644 --- a/frame/support/procedural/src/pallet/expand/config.rs +++ b/frame/support/procedural/src/pallet/expand/config.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2020-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/frame/support/procedural/src/pallet/expand/constants.rs b/frame/support/procedural/src/pallet/expand/constants.rs index 1f9526f9c..21ac1de36 100644 --- a/frame/support/procedural/src/pallet/expand/constants.rs +++ b/frame/support/procedural/src/pallet/expand/constants.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2020-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/frame/support/procedural/src/pallet/expand/error.rs b/frame/support/procedural/src/pallet/expand/error.rs index 5a8487b09..70f9fdfc7 100644 --- a/frame/support/procedural/src/pallet/expand/error.rs +++ b/frame/support/procedural/src/pallet/expand/error.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2020-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/frame/support/procedural/src/pallet/expand/event.rs b/frame/support/procedural/src/pallet/expand/event.rs index abed680eb..2f0cefb8b 100644 --- a/frame/support/procedural/src/pallet/expand/event.rs +++ b/frame/support/procedural/src/pallet/expand/event.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2020-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/frame/support/procedural/src/pallet/expand/genesis_build.rs b/frame/support/procedural/src/pallet/expand/genesis_build.rs index d19476779..9447154f3 100644 --- a/frame/support/procedural/src/pallet/expand/genesis_build.rs +++ b/frame/support/procedural/src/pallet/expand/genesis_build.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2020-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/frame/support/procedural/src/pallet/expand/genesis_config.rs b/frame/support/procedural/src/pallet/expand/genesis_config.rs index 739e85e0d..85ad20dd4 100644 --- a/frame/support/procedural/src/pallet/expand/genesis_config.rs +++ b/frame/support/procedural/src/pallet/expand/genesis_config.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2020-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/frame/support/procedural/src/pallet/expand/hooks.rs b/frame/support/procedural/src/pallet/expand/hooks.rs index 987d0691b..711b78e64 100644 --- a/frame/support/procedural/src/pallet/expand/hooks.rs +++ b/frame/support/procedural/src/pallet/expand/hooks.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2020-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/frame/support/procedural/src/pallet/expand/inherent.rs b/frame/support/procedural/src/pallet/expand/inherent.rs index 71d95f958..182d79f5b 100644 --- a/frame/support/procedural/src/pallet/expand/inherent.rs +++ b/frame/support/procedural/src/pallet/expand/inherent.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2021-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/frame/support/procedural/src/pallet/expand/instances.rs b/frame/support/procedural/src/pallet/expand/instances.rs index 52dc88763..b6dfa7e6d 100644 --- a/frame/support/procedural/src/pallet/expand/instances.rs +++ b/frame/support/procedural/src/pallet/expand/instances.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2020-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/frame/support/procedural/src/pallet/expand/mod.rs b/frame/support/procedural/src/pallet/expand/mod.rs index c33d23867..3ccbf91b4 100644 --- a/frame/support/procedural/src/pallet/expand/mod.rs +++ b/frame/support/procedural/src/pallet/expand/mod.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2020-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/frame/support/procedural/src/pallet/expand/origin.rs b/frame/support/procedural/src/pallet/expand/origin.rs index 721fc781a..55865b424 100644 --- a/frame/support/procedural/src/pallet/expand/origin.rs +++ b/frame/support/procedural/src/pallet/expand/origin.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2020-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/frame/support/procedural/src/pallet/expand/pallet_struct.rs b/frame/support/procedural/src/pallet/expand/pallet_struct.rs index e5941a33f..27fe9d340 100644 --- a/frame/support/procedural/src/pallet/expand/pallet_struct.rs +++ b/frame/support/procedural/src/pallet/expand/pallet_struct.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2020-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/frame/support/procedural/src/pallet/expand/storage.rs b/frame/support/procedural/src/pallet/expand/storage.rs index 0bb19ad6c..05d61bb52 100644 --- a/frame/support/procedural/src/pallet/expand/storage.rs +++ b/frame/support/procedural/src/pallet/expand/storage.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2020-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/frame/support/procedural/src/pallet/expand/store_trait.rs b/frame/support/procedural/src/pallet/expand/store_trait.rs index 4fb4f4614..15a6a223e 100644 --- a/frame/support/procedural/src/pallet/expand/store_trait.rs +++ b/frame/support/procedural/src/pallet/expand/store_trait.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2020-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/frame/support/procedural/src/pallet/expand/tt_default_parts.rs b/frame/support/procedural/src/pallet/expand/tt_default_parts.rs index 173dcc0e2..564b526e9 100644 --- a/frame/support/procedural/src/pallet/expand/tt_default_parts.rs +++ b/frame/support/procedural/src/pallet/expand/tt_default_parts.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2021-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/frame/support/procedural/src/pallet/expand/type_value.rs b/frame/support/procedural/src/pallet/expand/type_value.rs index abe52459a..5dc6309c0 100644 --- a/frame/support/procedural/src/pallet/expand/type_value.rs +++ b/frame/support/procedural/src/pallet/expand/type_value.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2020-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/frame/support/procedural/src/pallet/expand/validate_unsigned.rs b/frame/support/procedural/src/pallet/expand/validate_unsigned.rs index b49ba2b5b..876995585 100644 --- a/frame/support/procedural/src/pallet/expand/validate_unsigned.rs +++ b/frame/support/procedural/src/pallet/expand/validate_unsigned.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2020-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/frame/support/procedural/src/pallet/mod.rs b/frame/support/procedural/src/pallet/mod.rs index 3f85be81c..31048841c 100644 --- a/frame/support/procedural/src/pallet/mod.rs +++ b/frame/support/procedural/src/pallet/mod.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2020-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/frame/support/procedural/src/pallet/parse/call.rs b/frame/support/procedural/src/pallet/parse/call.rs index 3af322548..5b90c2d98 100644 --- a/frame/support/procedural/src/pallet/parse/call.rs +++ b/frame/support/procedural/src/pallet/parse/call.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2020-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/frame/support/procedural/src/pallet/parse/config.rs b/frame/support/procedural/src/pallet/parse/config.rs index 0f3aa69b1..4d22e50ef 100644 --- a/frame/support/procedural/src/pallet/parse/config.rs +++ b/frame/support/procedural/src/pallet/parse/config.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2020-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/frame/support/procedural/src/pallet/parse/error.rs b/frame/support/procedural/src/pallet/parse/error.rs index c6ce9b37c..f344c5d27 100644 --- a/frame/support/procedural/src/pallet/parse/error.rs +++ b/frame/support/procedural/src/pallet/parse/error.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2020-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/frame/support/procedural/src/pallet/parse/event.rs b/frame/support/procedural/src/pallet/parse/event.rs index e046cacac..64c3afd55 100644 --- a/frame/support/procedural/src/pallet/parse/event.rs +++ b/frame/support/procedural/src/pallet/parse/event.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2020-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/frame/support/procedural/src/pallet/parse/extra_constants.rs b/frame/support/procedural/src/pallet/parse/extra_constants.rs index d8622da08..8e85d2c26 100644 --- a/frame/support/procedural/src/pallet/parse/extra_constants.rs +++ b/frame/support/procedural/src/pallet/parse/extra_constants.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2020-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/frame/support/procedural/src/pallet/parse/genesis_build.rs b/frame/support/procedural/src/pallet/parse/genesis_build.rs index 9815b8d22..6d356b2ee 100644 --- a/frame/support/procedural/src/pallet/parse/genesis_build.rs +++ b/frame/support/procedural/src/pallet/parse/genesis_build.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2020-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/frame/support/procedural/src/pallet/parse/genesis_config.rs b/frame/support/procedural/src/pallet/parse/genesis_config.rs index 45e765c01..62da6ba13 100644 --- a/frame/support/procedural/src/pallet/parse/genesis_config.rs +++ b/frame/support/procedural/src/pallet/parse/genesis_config.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2020-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/frame/support/procedural/src/pallet/parse/helper.rs b/frame/support/procedural/src/pallet/parse/helper.rs index 824407917..4814dce5a 100644 --- a/frame/support/procedural/src/pallet/parse/helper.rs +++ b/frame/support/procedural/src/pallet/parse/helper.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2020-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/frame/support/procedural/src/pallet/parse/hooks.rs b/frame/support/procedural/src/pallet/parse/hooks.rs index 2dc8f4da4..f941df5f2 100644 --- a/frame/support/procedural/src/pallet/parse/hooks.rs +++ b/frame/support/procedural/src/pallet/parse/hooks.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2020-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/frame/support/procedural/src/pallet/parse/inherent.rs b/frame/support/procedural/src/pallet/parse/inherent.rs index a485eed4c..d8641691a 100644 --- a/frame/support/procedural/src/pallet/parse/inherent.rs +++ b/frame/support/procedural/src/pallet/parse/inherent.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2020-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/frame/support/procedural/src/pallet/parse/mod.rs b/frame/support/procedural/src/pallet/parse/mod.rs index f91159248..94e6f7a7c 100644 --- a/frame/support/procedural/src/pallet/parse/mod.rs +++ b/frame/support/procedural/src/pallet/parse/mod.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2020-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/frame/support/procedural/src/pallet/parse/origin.rs b/frame/support/procedural/src/pallet/parse/origin.rs index 89929e3e8..76e2a8841 100644 --- a/frame/support/procedural/src/pallet/parse/origin.rs +++ b/frame/support/procedural/src/pallet/parse/origin.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2020-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/frame/support/procedural/src/pallet/parse/pallet_struct.rs b/frame/support/procedural/src/pallet/parse/pallet_struct.rs index a96c310b6..571298d5a 100644 --- a/frame/support/procedural/src/pallet/parse/pallet_struct.rs +++ b/frame/support/procedural/src/pallet/parse/pallet_struct.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2020-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/frame/support/procedural/src/pallet/parse/storage.rs b/frame/support/procedural/src/pallet/parse/storage.rs index 8b551ab31..3ed7ce54e 100644 --- a/frame/support/procedural/src/pallet/parse/storage.rs +++ b/frame/support/procedural/src/pallet/parse/storage.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2020-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/frame/support/procedural/src/pallet/parse/type_value.rs b/frame/support/procedural/src/pallet/parse/type_value.rs index a3d004cd8..4b8050688 100644 --- a/frame/support/procedural/src/pallet/parse/type_value.rs +++ b/frame/support/procedural/src/pallet/parse/type_value.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2020-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/frame/support/procedural/src/pallet/parse/validate_unsigned.rs b/frame/support/procedural/src/pallet/parse/validate_unsigned.rs index 18d5a2dc4..2bf0a1b6c 100644 --- a/frame/support/procedural/src/pallet/parse/validate_unsigned.rs +++ b/frame/support/procedural/src/pallet/parse/validate_unsigned.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2020-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/frame/support/procedural/src/pallet_error.rs b/frame/support/procedural/src/pallet_error.rs index 216168131..fb0bf52f6 100644 --- a/frame/support/procedural/src/pallet_error.rs +++ b/frame/support/procedural/src/pallet_error.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2021 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/frame/support/procedural/src/partial_eq_no_bound.rs b/frame/support/procedural/src/partial_eq_no_bound.rs index 31930c0c3..27f5e9881 100644 --- a/frame/support/procedural/src/partial_eq_no_bound.rs +++ b/frame/support/procedural/src/partial_eq_no_bound.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2020-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/frame/support/procedural/src/storage/genesis_config/builder_def.rs b/frame/support/procedural/src/storage/genesis_config/builder_def.rs index 29e2560bc..5b8e4eb76 100644 --- a/frame/support/procedural/src/storage/genesis_config/builder_def.rs +++ b/frame/support/procedural/src/storage/genesis_config/builder_def.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2017-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/frame/support/procedural/src/storage/genesis_config/genesis_config_def.rs b/frame/support/procedural/src/storage/genesis_config/genesis_config_def.rs index d24e50096..491db5918 100644 --- a/frame/support/procedural/src/storage/genesis_config/genesis_config_def.rs +++ b/frame/support/procedural/src/storage/genesis_config/genesis_config_def.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2017-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/frame/support/procedural/src/storage/genesis_config/mod.rs b/frame/support/procedural/src/storage/genesis_config/mod.rs index d4348ee19..57c938368 100644 --- a/frame/support/procedural/src/storage/genesis_config/mod.rs +++ b/frame/support/procedural/src/storage/genesis_config/mod.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2017-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/frame/support/procedural/src/storage/getters.rs b/frame/support/procedural/src/storage/getters.rs index 9fe3734e6..428b2ec45 100644 --- a/frame/support/procedural/src/storage/getters.rs +++ b/frame/support/procedural/src/storage/getters.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2017-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/frame/support/procedural/src/storage/instance_trait.rs b/frame/support/procedural/src/storage/instance_trait.rs index e9df02e3d..8b5aa5005 100644 --- a/frame/support/procedural/src/storage/instance_trait.rs +++ b/frame/support/procedural/src/storage/instance_trait.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2017-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/frame/support/procedural/src/storage/metadata.rs b/frame/support/procedural/src/storage/metadata.rs index d1879d150..3e3e0576f 100644 --- a/frame/support/procedural/src/storage/metadata.rs +++ b/frame/support/procedural/src/storage/metadata.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2017-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/frame/support/procedural/src/storage/mod.rs b/frame/support/procedural/src/storage/mod.rs index e8e2d7529..1e48a1fbb 100644 --- a/frame/support/procedural/src/storage/mod.rs +++ b/frame/support/procedural/src/storage/mod.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2017-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/frame/support/procedural/src/storage/parse.rs b/frame/support/procedural/src/storage/parse.rs index 54026b7d7..f9f62e0ff 100644 --- a/frame/support/procedural/src/storage/parse.rs +++ b/frame/support/procedural/src/storage/parse.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2017-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/frame/support/procedural/src/storage/storage_info.rs b/frame/support/procedural/src/storage/storage_info.rs index 77515fa73..3e851b042 100644 --- a/frame/support/procedural/src/storage/storage_info.rs +++ b/frame/support/procedural/src/storage/storage_info.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2017-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/frame/support/procedural/src/storage/storage_struct.rs b/frame/support/procedural/src/storage/storage_struct.rs index 649a41bab..373e72212 100644 --- a/frame/support/procedural/src/storage/storage_struct.rs +++ b/frame/support/procedural/src/storage/storage_struct.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2019-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/frame/support/procedural/src/storage/store_trait.rs b/frame/support/procedural/src/storage/store_trait.rs index daf933b3b..5dca50251 100644 --- a/frame/support/procedural/src/storage/store_trait.rs +++ b/frame/support/procedural/src/storage/store_trait.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2017-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/frame/support/procedural/src/storage_alias.rs b/frame/support/procedural/src/storage_alias.rs index eaf8591a1..769956872 100644 --- a/frame/support/procedural/src/storage_alias.rs +++ b/frame/support/procedural/src/storage_alias.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2020-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/frame/support/procedural/src/transactional.rs b/frame/support/procedural/src/transactional.rs index eb77a320a..23117ffa3 100644 --- a/frame/support/procedural/src/transactional.rs +++ b/frame/support/procedural/src/transactional.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2020-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/frame/support/procedural/src/tt_macro.rs b/frame/support/procedural/src/tt_macro.rs index 0a270a717..69b5eb3d5 100644 --- a/frame/support/procedural/src/tt_macro.rs +++ b/frame/support/procedural/src/tt_macro.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/frame/support/procedural/tools/derive/src/lib.rs b/frame/support/procedural/tools/derive/src/lib.rs index 392a43834..f7c57c086 100644 --- a/frame/support/procedural/tools/derive/src/lib.rs +++ b/frame/support/procedural/tools/derive/src/lib.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2017-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/frame/support/procedural/tools/src/lib.rs b/frame/support/procedural/tools/src/lib.rs index 3abde5957..385b20ad0 100644 --- a/frame/support/procedural/tools/src/lib.rs +++ b/frame/support/procedural/tools/src/lib.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2017-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/frame/support/procedural/tools/src/syn_ext.rs b/frame/support/procedural/tools/src/syn_ext.rs index 1a2d7c1d3..833cd17df 100644 --- a/frame/support/procedural/tools/src/syn_ext.rs +++ b/frame/support/procedural/tools/src/syn_ext.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2017-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/frame/support/src/crypto.rs b/frame/support/src/crypto.rs index 182a78464..1957a7bc9 100644 --- a/frame/support/src/crypto.rs +++ b/frame/support/src/crypto.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/frame/support/src/crypto/ecdsa.rs b/frame/support/src/crypto/ecdsa.rs index a4a04acab..afa45e296 100644 --- a/frame/support/src/crypto/ecdsa.rs +++ b/frame/support/src/crypto/ecdsa.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/frame/support/src/dispatch.rs b/frame/support/src/dispatch.rs index 291abdd12..c26a3d8e7 100644 --- a/frame/support/src/dispatch.rs +++ b/frame/support/src/dispatch.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2017-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/frame/support/src/error.rs b/frame/support/src/error.rs index 337bd7589..72d13dfce 100644 --- a/frame/support/src/error.rs +++ b/frame/support/src/error.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2019-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/frame/support/src/event.rs b/frame/support/src/event.rs index dc45f2bf6..ce9e0dbb7 100644 --- a/frame/support/src/event.rs +++ b/frame/support/src/event.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2018-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/frame/support/src/hash.rs b/frame/support/src/hash.rs index 9bdad6d9d..bf9eb2f88 100644 --- a/frame/support/src/hash.rs +++ b/frame/support/src/hash.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2017-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/frame/support/src/inherent.rs b/frame/support/src/inherent.rs index 0aa6b9d3f..dce61378d 100644 --- a/frame/support/src/inherent.rs +++ b/frame/support/src/inherent.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2018-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/frame/support/src/instances.rs b/frame/support/src/instances.rs index 81139f867..482051c1f 100644 --- a/frame/support/src/instances.rs +++ b/frame/support/src/instances.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2017-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/frame/support/src/lib.rs b/frame/support/src/lib.rs index 77ca89dc6..c11c902d6 100644 --- a/frame/support/src/lib.rs +++ b/frame/support/src/lib.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2017-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/frame/support/src/migrations.rs b/frame/support/src/migrations.rs index 63679fd56..645316042 100644 --- a/frame/support/src/migrations.rs +++ b/frame/support/src/migrations.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2021-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/frame/support/src/storage/bounded_btree_map.rs b/frame/support/src/storage/bounded_btree_map.rs index d567faa38..f2f32d890 100644 --- a/frame/support/src/storage/bounded_btree_map.rs +++ b/frame/support/src/storage/bounded_btree_map.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2017-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/frame/support/src/storage/bounded_btree_set.rs b/frame/support/src/storage/bounded_btree_set.rs index 9ed129e67..52be1bb99 100644 --- a/frame/support/src/storage/bounded_btree_set.rs +++ b/frame/support/src/storage/bounded_btree_set.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2017-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/frame/support/src/storage/bounded_vec.rs b/frame/support/src/storage/bounded_vec.rs index 1fa01b44a..59b716b3a 100644 --- a/frame/support/src/storage/bounded_vec.rs +++ b/frame/support/src/storage/bounded_vec.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2017-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/frame/support/src/storage/child.rs b/frame/support/src/storage/child.rs index 397daaa82..76b9c554d 100644 --- a/frame/support/src/storage/child.rs +++ b/frame/support/src/storage/child.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2019-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/frame/support/src/storage/generator/double_map.rs b/frame/support/src/storage/generator/double_map.rs index b60bb7956..5763a472c 100644 --- a/frame/support/src/storage/generator/double_map.rs +++ b/frame/support/src/storage/generator/double_map.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2019-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/frame/support/src/storage/generator/map.rs b/frame/support/src/storage/generator/map.rs index f6c8eaa27..fb499384f 100644 --- a/frame/support/src/storage/generator/map.rs +++ b/frame/support/src/storage/generator/map.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2019-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/frame/support/src/storage/generator/mod.rs b/frame/support/src/storage/generator/mod.rs index 334e9b9e2..2da612b24 100644 --- a/frame/support/src/storage/generator/mod.rs +++ b/frame/support/src/storage/generator/mod.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2019-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/frame/support/src/storage/generator/nmap.rs b/frame/support/src/storage/generator/nmap.rs index 8cf145943..21fe7b41e 100755 --- a/frame/support/src/storage/generator/nmap.rs +++ b/frame/support/src/storage/generator/nmap.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2021-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/frame/support/src/storage/generator/value.rs b/frame/support/src/storage/generator/value.rs index 4a1fd5c55..4ffe40bac 100644 --- a/frame/support/src/storage/generator/value.rs +++ b/frame/support/src/storage/generator/value.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2019-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/frame/support/src/storage/hashed.rs b/frame/support/src/storage/hashed.rs index 19c9d73be..6633adce8 100644 --- a/frame/support/src/storage/hashed.rs +++ b/frame/support/src/storage/hashed.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2019-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/frame/support/src/storage/migration.rs b/frame/support/src/storage/migration.rs index 67001fc4e..8e945afdb 100644 --- a/frame/support/src/storage/migration.rs +++ b/frame/support/src/storage/migration.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2017-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/frame/support/src/storage/mod.rs b/frame/support/src/storage/mod.rs index dd91d8cc2..4c6ea943c 100644 --- a/frame/support/src/storage/mod.rs +++ b/frame/support/src/storage/mod.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2017-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/frame/support/src/storage/storage_noop_guard.rs b/frame/support/src/storage/storage_noop_guard.rs index 7186c3eaf..f2fb60027 100644 --- a/frame/support/src/storage/storage_noop_guard.rs +++ b/frame/support/src/storage/storage_noop_guard.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/frame/support/src/storage/stream_iter.rs b/frame/support/src/storage/stream_iter.rs index 4e25ca6ca..e784ebd14 100644 --- a/frame/support/src/storage/stream_iter.rs +++ b/frame/support/src/storage/stream_iter.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/frame/support/src/storage/transactional.rs b/frame/support/src/storage/transactional.rs index b7ae22d86..d42e1809e 100644 --- a/frame/support/src/storage/transactional.rs +++ b/frame/support/src/storage/transactional.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/frame/support/src/storage/types/counted_map.rs b/frame/support/src/storage/types/counted_map.rs index 7d7d9ebcf..24b00be48 100644 --- a/frame/support/src/storage/types/counted_map.rs +++ b/frame/support/src/storage/types/counted_map.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2021-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/frame/support/src/storage/types/double_map.rs b/frame/support/src/storage/types/double_map.rs index 9ba4cf052..6a4bdc1e6 100644 --- a/frame/support/src/storage/types/double_map.rs +++ b/frame/support/src/storage/types/double_map.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2017-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/frame/support/src/storage/types/key.rs b/frame/support/src/storage/types/key.rs index 182ddbedd..901bdb8b0 100755 --- a/frame/support/src/storage/types/key.rs +++ b/frame/support/src/storage/types/key.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2021-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/frame/support/src/storage/types/map.rs b/frame/support/src/storage/types/map.rs index 3e2b744d7..53cf74d26 100644 --- a/frame/support/src/storage/types/map.rs +++ b/frame/support/src/storage/types/map.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2017-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/frame/support/src/storage/types/mod.rs b/frame/support/src/storage/types/mod.rs index f87da5de1..9a6f15d1e 100644 --- a/frame/support/src/storage/types/mod.rs +++ b/frame/support/src/storage/types/mod.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2017-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/frame/support/src/storage/types/nmap.rs b/frame/support/src/storage/types/nmap.rs index dcbdac761..d97103596 100755 --- a/frame/support/src/storage/types/nmap.rs +++ b/frame/support/src/storage/types/nmap.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2021-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/frame/support/src/storage/types/value.rs b/frame/support/src/storage/types/value.rs index 15290f1b1..64fb432b2 100644 --- a/frame/support/src/storage/types/value.rs +++ b/frame/support/src/storage/types/value.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2017-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/frame/support/src/storage/unhashed.rs b/frame/support/src/storage/unhashed.rs index 8388c5f88..aae83034a 100644 --- a/frame/support/src/storage/unhashed.rs +++ b/frame/support/src/storage/unhashed.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2019-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/frame/support/src/storage/weak_bounded_vec.rs b/frame/support/src/storage/weak_bounded_vec.rs index 72ba8d775..41d27b51a 100644 --- a/frame/support/src/storage/weak_bounded_vec.rs +++ b/frame/support/src/storage/weak_bounded_vec.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2017-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/frame/support/src/traits.rs b/frame/support/src/traits.rs index f7b57f446..a649dad40 100644 --- a/frame/support/src/traits.rs +++ b/frame/support/src/traits.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2019-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/frame/support/src/traits/dispatch.rs b/frame/support/src/traits/dispatch.rs index 50c6c2279..6961e69ba 100644 --- a/frame/support/src/traits/dispatch.rs +++ b/frame/support/src/traits/dispatch.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2019-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/frame/support/src/traits/error.rs b/frame/support/src/traits/error.rs index 8e2689166..0f30e266d 100644 --- a/frame/support/src/traits/error.rs +++ b/frame/support/src/traits/error.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/frame/support/src/traits/filter.rs b/frame/support/src/traits/filter.rs index cdd82a312..36420b46f 100644 --- a/frame/support/src/traits/filter.rs +++ b/frame/support/src/traits/filter.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2019-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/frame/support/src/traits/hooks.rs b/frame/support/src/traits/hooks.rs index 3f7db1fa0..fe67e33d1 100644 --- a/frame/support/src/traits/hooks.rs +++ b/frame/support/src/traits/hooks.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2019-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/frame/support/src/traits/members.rs b/frame/support/src/traits/members.rs index daf2d3aa6..f336b460c 100644 --- a/frame/support/src/traits/members.rs +++ b/frame/support/src/traits/members.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2019-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/frame/support/src/traits/messages.rs b/frame/support/src/traits/messages.rs index 9b86c421a..637aa7fdc 100644 --- a/frame/support/src/traits/messages.rs +++ b/frame/support/src/traits/messages.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2019-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/frame/support/src/traits/metadata.rs b/frame/support/src/traits/metadata.rs index 42f2d759a..f3e4b955d 100644 --- a/frame/support/src/traits/metadata.rs +++ b/frame/support/src/traits/metadata.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2019-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/frame/support/src/traits/misc.rs b/frame/support/src/traits/misc.rs index 5a13fc4ab..a320b85ea 100644 --- a/frame/support/src/traits/misc.rs +++ b/frame/support/src/traits/misc.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2019-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/frame/support/src/traits/preimages.rs b/frame/support/src/traits/preimages.rs index ce3537c79..db8abdbdc 100644 --- a/frame/support/src/traits/preimages.rs +++ b/frame/support/src/traits/preimages.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2019-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/frame/support/src/traits/randomness.rs b/frame/support/src/traits/randomness.rs index d68b95b1d..3666a4864 100644 --- a/frame/support/src/traits/randomness.rs +++ b/frame/support/src/traits/randomness.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2019-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/frame/support/src/traits/schedule.rs b/frame/support/src/traits/schedule.rs index b8e6a7f80..4e17092ae 100644 --- a/frame/support/src/traits/schedule.rs +++ b/frame/support/src/traits/schedule.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2019-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/frame/support/src/traits/storage.rs b/frame/support/src/traits/storage.rs index 8285ab988..c3394185a 100644 --- a/frame/support/src/traits/storage.rs +++ b/frame/support/src/traits/storage.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2019-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/frame/support/src/traits/stored_map.rs b/frame/support/src/traits/stored_map.rs index 2aae88096..a073b2682 100644 --- a/frame/support/src/traits/stored_map.rs +++ b/frame/support/src/traits/stored_map.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2019-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/frame/support/src/traits/tokens.rs b/frame/support/src/traits/tokens.rs index 03a24bd3b..6055fde21 100644 --- a/frame/support/src/traits/tokens.rs +++ b/frame/support/src/traits/tokens.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2019-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/frame/support/src/traits/tokens/currency.rs b/frame/support/src/traits/tokens/currency.rs index 48247b602..3f6f6b8e7 100644 --- a/frame/support/src/traits/tokens/currency.rs +++ b/frame/support/src/traits/tokens/currency.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2019-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/frame/support/src/traits/tokens/currency/lockable.rs b/frame/support/src/traits/tokens/currency/lockable.rs index a10edd6e3..955814f5a 100644 --- a/frame/support/src/traits/tokens/currency/lockable.rs +++ b/frame/support/src/traits/tokens/currency/lockable.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2019-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/frame/support/src/traits/tokens/currency/reservable.rs b/frame/support/src/traits/tokens/currency/reservable.rs index 53f6764c3..aa097a756 100644 --- a/frame/support/src/traits/tokens/currency/reservable.rs +++ b/frame/support/src/traits/tokens/currency/reservable.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2019-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/frame/support/src/traits/tokens/fungible.rs b/frame/support/src/traits/tokens/fungible.rs index a36e63e71..12bac2972 100644 --- a/frame/support/src/traits/tokens/fungible.rs +++ b/frame/support/src/traits/tokens/fungible.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2019-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/frame/support/src/traits/tokens/fungible/balanced.rs b/frame/support/src/traits/tokens/fungible/balanced.rs index 0e75ccc22..437d2c82d 100644 --- a/frame/support/src/traits/tokens/fungible/balanced.rs +++ b/frame/support/src/traits/tokens/fungible/balanced.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2019-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/frame/support/src/traits/tokens/fungible/imbalance.rs b/frame/support/src/traits/tokens/fungible/imbalance.rs index ca911cf12..1b3d16c62 100644 --- a/frame/support/src/traits/tokens/fungible/imbalance.rs +++ b/frame/support/src/traits/tokens/fungible/imbalance.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2019-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/frame/support/src/traits/tokens/fungibles.rs b/frame/support/src/traits/tokens/fungibles.rs index a29cb974f..d146832f3 100644 --- a/frame/support/src/traits/tokens/fungibles.rs +++ b/frame/support/src/traits/tokens/fungibles.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2019-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/frame/support/src/traits/tokens/fungibles/approvals.rs b/frame/support/src/traits/tokens/fungibles/approvals.rs index 48929955d..7a80279b0 100644 --- a/frame/support/src/traits/tokens/fungibles/approvals.rs +++ b/frame/support/src/traits/tokens/fungibles/approvals.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2019-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/frame/support/src/traits/tokens/fungibles/balanced.rs b/frame/support/src/traits/tokens/fungibles/balanced.rs index 9e50ff834..598ba250e 100644 --- a/frame/support/src/traits/tokens/fungibles/balanced.rs +++ b/frame/support/src/traits/tokens/fungibles/balanced.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2019-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/frame/support/src/traits/tokens/fungibles/enumerable.rs b/frame/support/src/traits/tokens/fungibles/enumerable.rs index 151d15b36..5d7266d9f 100644 --- a/frame/support/src/traits/tokens/fungibles/enumerable.rs +++ b/frame/support/src/traits/tokens/fungibles/enumerable.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2019-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/frame/support/src/traits/tokens/fungibles/imbalance.rs b/frame/support/src/traits/tokens/fungibles/imbalance.rs index 61bd4a430..87445859c 100644 --- a/frame/support/src/traits/tokens/fungibles/imbalance.rs +++ b/frame/support/src/traits/tokens/fungibles/imbalance.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2019-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/frame/support/src/traits/tokens/fungibles/metadata.rs b/frame/support/src/traits/tokens/fungibles/metadata.rs index b736ab148..64f8bf094 100644 --- a/frame/support/src/traits/tokens/fungibles/metadata.rs +++ b/frame/support/src/traits/tokens/fungibles/metadata.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2019-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/frame/support/src/traits/tokens/fungibles/roles.rs b/frame/support/src/traits/tokens/fungibles/roles.rs index 18fd1cc80..5cd1228af 100644 --- a/frame/support/src/traits/tokens/fungibles/roles.rs +++ b/frame/support/src/traits/tokens/fungibles/roles.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2019-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/frame/support/src/traits/tokens/imbalance.rs b/frame/support/src/traits/tokens/imbalance.rs index d721beb41..403321725 100644 --- a/frame/support/src/traits/tokens/imbalance.rs +++ b/frame/support/src/traits/tokens/imbalance.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2019-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/frame/support/src/traits/tokens/imbalance/on_unbalanced.rs b/frame/support/src/traits/tokens/imbalance/on_unbalanced.rs index 0125254ce..27bfe46e1 100644 --- a/frame/support/src/traits/tokens/imbalance/on_unbalanced.rs +++ b/frame/support/src/traits/tokens/imbalance/on_unbalanced.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2019-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/frame/support/src/traits/tokens/imbalance/signed_imbalance.rs b/frame/support/src/traits/tokens/imbalance/signed_imbalance.rs index f969a4363..03e821b16 100644 --- a/frame/support/src/traits/tokens/imbalance/signed_imbalance.rs +++ b/frame/support/src/traits/tokens/imbalance/signed_imbalance.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2019-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/frame/support/src/traits/tokens/imbalance/split_two_ways.rs b/frame/support/src/traits/tokens/imbalance/split_two_ways.rs index b963895af..c1afac35f 100644 --- a/frame/support/src/traits/tokens/imbalance/split_two_ways.rs +++ b/frame/support/src/traits/tokens/imbalance/split_two_ways.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2019-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/frame/support/src/traits/tokens/misc.rs b/frame/support/src/traits/tokens/misc.rs index f9876ef47..b4bd46401 100644 --- a/frame/support/src/traits/tokens/misc.rs +++ b/frame/support/src/traits/tokens/misc.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2019-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/frame/support/src/traits/tokens/nonfungible.rs b/frame/support/src/traits/tokens/nonfungible.rs index 46ca57313..e3fc84f1d 100644 --- a/frame/support/src/traits/tokens/nonfungible.rs +++ b/frame/support/src/traits/tokens/nonfungible.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2019-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/frame/support/src/traits/tokens/nonfungible_v2.rs b/frame/support/src/traits/tokens/nonfungible_v2.rs index e1899020b..36ef9ab0a 100644 --- a/frame/support/src/traits/tokens/nonfungible_v2.rs +++ b/frame/support/src/traits/tokens/nonfungible_v2.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2019-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/frame/support/src/traits/tokens/nonfungibles.rs b/frame/support/src/traits/tokens/nonfungibles.rs index ac007b5a6..e9538d14f 100644 --- a/frame/support/src/traits/tokens/nonfungibles.rs +++ b/frame/support/src/traits/tokens/nonfungibles.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2019-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/frame/support/src/traits/tokens/nonfungibles_v2.rs b/frame/support/src/traits/tokens/nonfungibles_v2.rs index 17c5d887d..9331f9668 100644 --- a/frame/support/src/traits/tokens/nonfungibles_v2.rs +++ b/frame/support/src/traits/tokens/nonfungibles_v2.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2019-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/frame/support/src/traits/try_runtime.rs b/frame/support/src/traits/try_runtime.rs index cb4902201..6103f07a7 100644 --- a/frame/support/src/traits/try_runtime.rs +++ b/frame/support/src/traits/try_runtime.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/frame/support/src/traits/validation.rs b/frame/support/src/traits/validation.rs index 135dd927a..84430901b 100644 --- a/frame/support/src/traits/validation.rs +++ b/frame/support/src/traits/validation.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2019-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/frame/support/src/traits/voting.rs b/frame/support/src/traits/voting.rs index 49ae3163d..caec47278 100644 --- a/frame/support/src/traits/voting.rs +++ b/frame/support/src/traits/voting.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2019-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/frame/support/src/weights.rs b/frame/support/src/weights.rs index d52380b83..75eba8fbe 100644 --- a/frame/support/src/weights.rs +++ b/frame/support/src/weights.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2019-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/frame/support/src/weights/block_weights.rs b/frame/support/src/weights/block_weights.rs index dc01107ce..a208c77aa 100644 --- a/frame/support/src/weights/block_weights.rs +++ b/frame/support/src/weights/block_weights.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2023 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/frame/support/src/weights/extrinsic_weights.rs b/frame/support/src/weights/extrinsic_weights.rs index 3d25d5a40..4e3a0fda8 100644 --- a/frame/support/src/weights/extrinsic_weights.rs +++ b/frame/support/src/weights/extrinsic_weights.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2023 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/frame/support/src/weights/paritydb_weights.rs b/frame/support/src/weights/paritydb_weights.rs index 6fd1112ee..f69fc0cd9 100644 --- a/frame/support/src/weights/paritydb_weights.rs +++ b/frame/support/src/weights/paritydb_weights.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/frame/support/src/weights/rocksdb_weights.rs b/frame/support/src/weights/rocksdb_weights.rs index b18b387de..25d2ac1cd 100644 --- a/frame/support/src/weights/rocksdb_weights.rs +++ b/frame/support/src/weights/rocksdb_weights.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/frame/support/test/compile_pass/src/lib.rs b/frame/support/test/compile_pass/src/lib.rs index b46f6c48a..1ff72bea5 100644 --- a/frame/support/test/compile_pass/src/lib.rs +++ b/frame/support/test/compile_pass/src/lib.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2018-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 // This program is free software: you can redistribute it and/or modify diff --git a/frame/support/test/pallet/src/lib.rs b/frame/support/test/pallet/src/lib.rs index 37678e056..82f12ea95 100644 --- a/frame/support/test/pallet/src/lib.rs +++ b/frame/support/test/pallet/src/lib.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2021-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/frame/support/test/src/lib.rs b/frame/support/test/src/lib.rs index 0ceeed42f..c0b025380 100644 --- a/frame/support/test/src/lib.rs +++ b/frame/support/test/src/lib.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2019-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/frame/support/test/src/pallet_version.rs b/frame/support/test/src/pallet_version.rs index 096289116..b01569968 100644 --- a/frame/support/test/src/pallet_version.rs +++ b/frame/support/test/src/pallet_version.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2020-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/frame/support/test/tests/benchmark_ui.rs b/frame/support/test/tests/benchmark_ui.rs index 243030bd0..aa5fadd0e 100644 --- a/frame/support/test/tests/benchmark_ui.rs +++ b/frame/support/test/tests/benchmark_ui.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2020-2023 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/frame/support/test/tests/construct_runtime.rs b/frame/support/test/tests/construct_runtime.rs index 18e4af1ac..096557714 100644 --- a/frame/support/test/tests/construct_runtime.rs +++ b/frame/support/test/tests/construct_runtime.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2019-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/frame/support/test/tests/construct_runtime_ui.rs b/frame/support/test/tests/construct_runtime_ui.rs index 42fd87ca9..ec6758f4b 100644 --- a/frame/support/test/tests/construct_runtime_ui.rs +++ b/frame/support/test/tests/construct_runtime_ui.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2019-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/frame/support/test/tests/decl_module_ui.rs b/frame/support/test/tests/decl_module_ui.rs index 292451335..296ae3e58 100644 --- a/frame/support/test/tests/decl_module_ui.rs +++ b/frame/support/test/tests/decl_module_ui.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2020-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/frame/support/test/tests/decl_storage.rs b/frame/support/test/tests/decl_storage.rs index a4e420eb4..c2ee77424 100644 --- a/frame/support/test/tests/decl_storage.rs +++ b/frame/support/test/tests/decl_storage.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2019-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/frame/support/test/tests/decl_storage_ui.rs b/frame/support/test/tests/decl_storage_ui.rs index 34dfea860..1414c8a99 100644 --- a/frame/support/test/tests/decl_storage_ui.rs +++ b/frame/support/test/tests/decl_storage_ui.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2019-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/frame/support/test/tests/decl_storage_ui/config_duplicate.rs b/frame/support/test/tests/decl_storage_ui/config_duplicate.rs index af74823f6..a6e1bfad5 100644 --- a/frame/support/test/tests/decl_storage_ui/config_duplicate.rs +++ b/frame/support/test/tests/decl_storage_ui/config_duplicate.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2019-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/frame/support/test/tests/decl_storage_ui/config_get_duplicate.rs b/frame/support/test/tests/decl_storage_ui/config_get_duplicate.rs index 8805effd9..c40dd0e44 100644 --- a/frame/support/test/tests/decl_storage_ui/config_get_duplicate.rs +++ b/frame/support/test/tests/decl_storage_ui/config_get_duplicate.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2019-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/frame/support/test/tests/decl_storage_ui/get_duplicate.rs b/frame/support/test/tests/decl_storage_ui/get_duplicate.rs index 5e0588711..23c89d276 100644 --- a/frame/support/test/tests/decl_storage_ui/get_duplicate.rs +++ b/frame/support/test/tests/decl_storage_ui/get_duplicate.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2019-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/frame/support/test/tests/derive_no_bound.rs b/frame/support/test/tests/derive_no_bound.rs index 9162b5013..8b1394fcd 100644 --- a/frame/support/test/tests/derive_no_bound.rs +++ b/frame/support/test/tests/derive_no_bound.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2020-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/frame/support/test/tests/derive_no_bound_ui.rs b/frame/support/test/tests/derive_no_bound_ui.rs index d714e1113..b1c9283c0 100644 --- a/frame/support/test/tests/derive_no_bound_ui.rs +++ b/frame/support/test/tests/derive_no_bound_ui.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2020-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/frame/support/test/tests/final_keys.rs b/frame/support/test/tests/final_keys.rs index a7ad88c80..9e1d6c087 100644 --- a/frame/support/test/tests/final_keys.rs +++ b/frame/support/test/tests/final_keys.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2019-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/frame/support/test/tests/genesisconfig.rs b/frame/support/test/tests/genesisconfig.rs index f0094b318..869641df8 100644 --- a/frame/support/test/tests/genesisconfig.rs +++ b/frame/support/test/tests/genesisconfig.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2019-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/frame/support/test/tests/instance.rs b/frame/support/test/tests/instance.rs index 043959b67..28bd5c8c5 100644 --- a/frame/support/test/tests/instance.rs +++ b/frame/support/test/tests/instance.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2019-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/frame/support/test/tests/issue2219.rs b/frame/support/test/tests/issue2219.rs index 5d24d5416..da3226d62 100644 --- a/frame/support/test/tests/issue2219.rs +++ b/frame/support/test/tests/issue2219.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2019-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/frame/support/test/tests/origin.rs b/frame/support/test/tests/origin.rs index f794cd8be..996b996c8 100644 --- a/frame/support/test/tests/origin.rs +++ b/frame/support/test/tests/origin.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2021 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/frame/support/test/tests/pallet.rs b/frame/support/test/tests/pallet.rs index 36150b280..7dd9aa0dd 100644 --- a/frame/support/test/tests/pallet.rs +++ b/frame/support/test/tests/pallet.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2020-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/frame/support/test/tests/pallet_compatibility.rs b/frame/support/test/tests/pallet_compatibility.rs index 300fb9a40..8b0573398 100644 --- a/frame/support/test/tests/pallet_compatibility.rs +++ b/frame/support/test/tests/pallet_compatibility.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2020-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/frame/support/test/tests/pallet_compatibility_instance.rs b/frame/support/test/tests/pallet_compatibility_instance.rs index 79370d911..5cc1c1f44 100644 --- a/frame/support/test/tests/pallet_compatibility_instance.rs +++ b/frame/support/test/tests/pallet_compatibility_instance.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2020-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/frame/support/test/tests/pallet_instance.rs b/frame/support/test/tests/pallet_instance.rs index 7fec0aa9d..ea1e27fe9 100644 --- a/frame/support/test/tests/pallet_instance.rs +++ b/frame/support/test/tests/pallet_instance.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2020-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/frame/support/test/tests/pallet_ui.rs b/frame/support/test/tests/pallet_ui.rs index 26d016c5a..466957c9f 100644 --- a/frame/support/test/tests/pallet_ui.rs +++ b/frame/support/test/tests/pallet_ui.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2020-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/frame/support/test/tests/pallet_with_name_trait_is_valid.rs b/frame/support/test/tests/pallet_with_name_trait_is_valid.rs index 006642056..43330dbe2 100644 --- a/frame/support/test/tests/pallet_with_name_trait_is_valid.rs +++ b/frame/support/test/tests/pallet_with_name_trait_is_valid.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2017-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/frame/support/test/tests/storage_alias_ui.rs b/frame/support/test/tests/storage_alias_ui.rs index d45d07157..b82acd8f3 100644 --- a/frame/support/test/tests/storage_alias_ui.rs +++ b/frame/support/test/tests/storage_alias_ui.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2020-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/frame/support/test/tests/storage_layers.rs b/frame/support/test/tests/storage_layers.rs index cff81c0be..c5bbbae02 100644 --- a/frame/support/test/tests/storage_layers.rs +++ b/frame/support/test/tests/storage_layers.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/frame/support/test/tests/storage_transaction.rs b/frame/support/test/tests/storage_transaction.rs index 6fedd7501..769ecb29a 100644 --- a/frame/support/test/tests/storage_transaction.rs +++ b/frame/support/test/tests/storage_transaction.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2020-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/frame/support/test/tests/system.rs b/frame/support/test/tests/system.rs index eff412429..1a938ad4e 100644 --- a/frame/support/test/tests/system.rs +++ b/frame/support/test/tests/system.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2019-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/frame/system/benches/bench.rs b/frame/system/benches/bench.rs index f2acc319d..51652727c 100644 --- a/frame/system/benches/bench.rs +++ b/frame/system/benches/bench.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2019-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/frame/system/benchmarking/src/lib.rs b/frame/system/benchmarking/src/lib.rs index 87d3c7fc7..ef0672974 100644 --- a/frame/system/benchmarking/src/lib.rs +++ b/frame/system/benchmarking/src/lib.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2019-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/frame/system/benchmarking/src/mock.rs b/frame/system/benchmarking/src/mock.rs index a7f28ca30..8da623a3a 100644 --- a/frame/system/benchmarking/src/mock.rs +++ b/frame/system/benchmarking/src/mock.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2020-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/frame/system/rpc/runtime-api/src/lib.rs b/frame/system/rpc/runtime-api/src/lib.rs index 6e01bdae2..2ea9f2f62 100644 --- a/frame/system/rpc/runtime-api/src/lib.rs +++ b/frame/system/rpc/runtime-api/src/lib.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2019-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/frame/system/src/extensions/check_genesis.rs b/frame/system/src/extensions/check_genesis.rs index f5811f306..5964ec452 100644 --- a/frame/system/src/extensions/check_genesis.rs +++ b/frame/system/src/extensions/check_genesis.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2017-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/frame/system/src/extensions/check_mortality.rs b/frame/system/src/extensions/check_mortality.rs index 635ab4ef1..739a27a2a 100644 --- a/frame/system/src/extensions/check_mortality.rs +++ b/frame/system/src/extensions/check_mortality.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2017-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/frame/system/src/extensions/check_non_zero_sender.rs b/frame/system/src/extensions/check_non_zero_sender.rs index 036f70c2f..b0b6704fe 100644 --- a/frame/system/src/extensions/check_non_zero_sender.rs +++ b/frame/system/src/extensions/check_non_zero_sender.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2017-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/frame/system/src/extensions/check_nonce.rs b/frame/system/src/extensions/check_nonce.rs index 1616a2d8a..57ebd7701 100644 --- a/frame/system/src/extensions/check_nonce.rs +++ b/frame/system/src/extensions/check_nonce.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2017-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/frame/system/src/extensions/check_spec_version.rs b/frame/system/src/extensions/check_spec_version.rs index ef5f40402..24d5ef9ca 100644 --- a/frame/system/src/extensions/check_spec_version.rs +++ b/frame/system/src/extensions/check_spec_version.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2017-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/frame/system/src/extensions/check_tx_version.rs b/frame/system/src/extensions/check_tx_version.rs index be0b8fe23..3f9d6a190 100644 --- a/frame/system/src/extensions/check_tx_version.rs +++ b/frame/system/src/extensions/check_tx_version.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2017-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/frame/system/src/extensions/check_weight.rs b/frame/system/src/extensions/check_weight.rs index 590d05474..ca5f7a82c 100644 --- a/frame/system/src/extensions/check_weight.rs +++ b/frame/system/src/extensions/check_weight.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2017-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/frame/system/src/extensions/mod.rs b/frame/system/src/extensions/mod.rs index 9ba73c5f4..a88c9fbf9 100644 --- a/frame/system/src/extensions/mod.rs +++ b/frame/system/src/extensions/mod.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2020-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/frame/system/src/lib.rs b/frame/system/src/lib.rs index c22a68296..5e921ab85 100644 --- a/frame/system/src/lib.rs +++ b/frame/system/src/lib.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2017-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/frame/system/src/limits.rs b/frame/system/src/limits.rs index 54d27c5b9..5fd7a5af8 100644 --- a/frame/system/src/limits.rs +++ b/frame/system/src/limits.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2017-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/frame/system/src/migrations/mod.rs b/frame/system/src/migrations/mod.rs index 90b88de1a..f8ebfab33 100644 --- a/frame/system/src/migrations/mod.rs +++ b/frame/system/src/migrations/mod.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2021-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/frame/system/src/mock.rs b/frame/system/src/mock.rs index fb230f66a..a3807512d 100644 --- a/frame/system/src/mock.rs +++ b/frame/system/src/mock.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2017-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/frame/system/src/mocking.rs b/frame/system/src/mocking.rs index d8cfcb9ba..8f76c1b8e 100644 --- a/frame/system/src/mocking.rs +++ b/frame/system/src/mocking.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2021-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/frame/system/src/offchain.rs b/frame/system/src/offchain.rs index 99a4c1541..742146d16 100644 --- a/frame/system/src/offchain.rs +++ b/frame/system/src/offchain.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2019-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/frame/system/src/tests.rs b/frame/system/src/tests.rs index f441e7855..3e24d8f3c 100644 --- a/frame/system/src/tests.rs +++ b/frame/system/src/tests.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2017-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/frame/system/src/weights.rs b/frame/system/src/weights.rs index d189afd4a..fa77db90d 100644 --- a/frame/system/src/weights.rs +++ b/frame/system/src/weights.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2023 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/frame/timestamp/src/benchmarking.rs b/frame/timestamp/src/benchmarking.rs index 3a9297fda..8a7febed4 100644 --- a/frame/timestamp/src/benchmarking.rs +++ b/frame/timestamp/src/benchmarking.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2020-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/frame/timestamp/src/lib.rs b/frame/timestamp/src/lib.rs index 61a46125a..93af4447d 100644 --- a/frame/timestamp/src/lib.rs +++ b/frame/timestamp/src/lib.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2017-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/frame/timestamp/src/mock.rs b/frame/timestamp/src/mock.rs index e1ba5cd11..6f6817882 100644 --- a/frame/timestamp/src/mock.rs +++ b/frame/timestamp/src/mock.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2020-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/frame/timestamp/src/tests.rs b/frame/timestamp/src/tests.rs index 6a76fbc48..317631eeb 100644 --- a/frame/timestamp/src/tests.rs +++ b/frame/timestamp/src/tests.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2020-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/frame/timestamp/src/weights.rs b/frame/timestamp/src/weights.rs index 4c7dfd5ed..8c7ef0d1f 100644 --- a/frame/timestamp/src/weights.rs +++ b/frame/timestamp/src/weights.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2023 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/frame/tips/src/benchmarking.rs b/frame/tips/src/benchmarking.rs index 5b1a102b8..613f684af 100644 --- a/frame/tips/src/benchmarking.rs +++ b/frame/tips/src/benchmarking.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2020-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/frame/tips/src/lib.rs b/frame/tips/src/lib.rs index 175fc3ac3..d82906563 100644 --- a/frame/tips/src/lib.rs +++ b/frame/tips/src/lib.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2017-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/frame/tips/src/migrations/mod.rs b/frame/tips/src/migrations/mod.rs index 719bb2f86..f7f144adc 100644 --- a/frame/tips/src/migrations/mod.rs +++ b/frame/tips/src/migrations/mod.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2019-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/frame/tips/src/migrations/v4.rs b/frame/tips/src/migrations/v4.rs index 0107e96a4..35569633d 100644 --- a/frame/tips/src/migrations/v4.rs +++ b/frame/tips/src/migrations/v4.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2019-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/frame/tips/src/tests.rs b/frame/tips/src/tests.rs index cb0b4458c..fa4ec1501 100644 --- a/frame/tips/src/tests.rs +++ b/frame/tips/src/tests.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2020-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/frame/tips/src/weights.rs b/frame/tips/src/weights.rs index a568aaa87..41fdf4928 100644 --- a/frame/tips/src/weights.rs +++ b/frame/tips/src/weights.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2023 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/frame/transaction-payment/asset-tx-payment/src/lib.rs b/frame/transaction-payment/asset-tx-payment/src/lib.rs index 645ebb1e1..ec0bfcd4f 100644 --- a/frame/transaction-payment/asset-tx-payment/src/lib.rs +++ b/frame/transaction-payment/asset-tx-payment/src/lib.rs @@ -1,4 +1,4 @@ -// Copyright (C) 2021-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/frame/transaction-payment/asset-tx-payment/src/mock.rs b/frame/transaction-payment/asset-tx-payment/src/mock.rs index 60fb2e6c5..ada4777cf 100644 --- a/frame/transaction-payment/asset-tx-payment/src/mock.rs +++ b/frame/transaction-payment/asset-tx-payment/src/mock.rs @@ -1,4 +1,4 @@ -// Copyright (C) 2021-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/frame/transaction-payment/asset-tx-payment/src/payment.rs b/frame/transaction-payment/asset-tx-payment/src/payment.rs index ae8253b6e..e273ffcfc 100644 --- a/frame/transaction-payment/asset-tx-payment/src/payment.rs +++ b/frame/transaction-payment/asset-tx-payment/src/payment.rs @@ -1,4 +1,4 @@ -// Copyright (C) 2021-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/frame/transaction-payment/asset-tx-payment/src/tests.rs b/frame/transaction-payment/asset-tx-payment/src/tests.rs index f76293f45..08699fef0 100644 --- a/frame/transaction-payment/asset-tx-payment/src/tests.rs +++ b/frame/transaction-payment/asset-tx-payment/src/tests.rs @@ -1,4 +1,4 @@ -// Copyright (C) 2021-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/frame/transaction-payment/rpc/runtime-api/src/lib.rs b/frame/transaction-payment/rpc/runtime-api/src/lib.rs index 760f9e369..ae4a25bf1 100644 --- a/frame/transaction-payment/rpc/runtime-api/src/lib.rs +++ b/frame/transaction-payment/rpc/runtime-api/src/lib.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2019-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/frame/transaction-payment/rpc/src/lib.rs b/frame/transaction-payment/rpc/src/lib.rs index 46ad5dbc0..1207a8cbf 100644 --- a/frame/transaction-payment/rpc/src/lib.rs +++ b/frame/transaction-payment/rpc/src/lib.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2019-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/frame/transaction-payment/src/lib.rs b/frame/transaction-payment/src/lib.rs index fb4381c52..6ed3e9789 100644 --- a/frame/transaction-payment/src/lib.rs +++ b/frame/transaction-payment/src/lib.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2019-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/frame/transaction-payment/src/mock.rs b/frame/transaction-payment/src/mock.rs index e214458b3..b4b8a784a 100644 --- a/frame/transaction-payment/src/mock.rs +++ b/frame/transaction-payment/src/mock.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2019-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/frame/transaction-payment/src/tests.rs b/frame/transaction-payment/src/tests.rs index ee54c1130..2748b80b1 100644 --- a/frame/transaction-payment/src/tests.rs +++ b/frame/transaction-payment/src/tests.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2019-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/frame/transaction-payment/src/types.rs b/frame/transaction-payment/src/types.rs index d9677861f..b8bc17918 100644 --- a/frame/transaction-payment/src/types.rs +++ b/frame/transaction-payment/src/types.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2021-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/frame/transaction-storage/src/benchmarking.rs b/frame/transaction-storage/src/benchmarking.rs index 736d2c40d..dfea33315 100644 --- a/frame/transaction-storage/src/benchmarking.rs +++ b/frame/transaction-storage/src/benchmarking.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2021-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/frame/transaction-storage/src/lib.rs b/frame/transaction-storage/src/lib.rs index aaa484d58..c4b1a622f 100644 --- a/frame/transaction-storage/src/lib.rs +++ b/frame/transaction-storage/src/lib.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2017-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/frame/transaction-storage/src/mock.rs b/frame/transaction-storage/src/mock.rs index 8764b16c3..f54c134d7 100644 --- a/frame/transaction-storage/src/mock.rs +++ b/frame/transaction-storage/src/mock.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2019-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/frame/transaction-storage/src/tests.rs b/frame/transaction-storage/src/tests.rs index 01b71a785..43dfed81f 100644 --- a/frame/transaction-storage/src/tests.rs +++ b/frame/transaction-storage/src/tests.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2019-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/frame/transaction-storage/src/weights.rs b/frame/transaction-storage/src/weights.rs index 9ee7b37b3..d9659a925 100644 --- a/frame/transaction-storage/src/weights.rs +++ b/frame/transaction-storage/src/weights.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2023 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/frame/treasury/src/benchmarking.rs b/frame/treasury/src/benchmarking.rs index 0947618a2..a3761083e 100644 --- a/frame/treasury/src/benchmarking.rs +++ b/frame/treasury/src/benchmarking.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2020-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/frame/treasury/src/lib.rs b/frame/treasury/src/lib.rs index f5ca0a984..2e22063eb 100644 --- a/frame/treasury/src/lib.rs +++ b/frame/treasury/src/lib.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2017-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/frame/treasury/src/tests.rs b/frame/treasury/src/tests.rs index e6a1ea0f5..c9305dfd1 100644 --- a/frame/treasury/src/tests.rs +++ b/frame/treasury/src/tests.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2020-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/frame/treasury/src/weights.rs b/frame/treasury/src/weights.rs index aff593d88..10bab0083 100644 --- a/frame/treasury/src/weights.rs +++ b/frame/treasury/src/weights.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2023 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/frame/try-runtime/src/lib.rs b/frame/try-runtime/src/lib.rs index d8b117d1f..43292efe2 100644 --- a/frame/try-runtime/src/lib.rs +++ b/frame/try-runtime/src/lib.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2021-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/frame/uniques/src/benchmarking.rs b/frame/uniques/src/benchmarking.rs index 2656582f0..6d1795142 100644 --- a/frame/uniques/src/benchmarking.rs +++ b/frame/uniques/src/benchmarking.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2020-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/frame/uniques/src/functions.rs b/frame/uniques/src/functions.rs index f73e5c479..1aa79134d 100644 --- a/frame/uniques/src/functions.rs +++ b/frame/uniques/src/functions.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2017-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/frame/uniques/src/impl_nonfungibles.rs b/frame/uniques/src/impl_nonfungibles.rs index 75d193ad1..e8bef4e3f 100644 --- a/frame/uniques/src/impl_nonfungibles.rs +++ b/frame/uniques/src/impl_nonfungibles.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2017-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/frame/uniques/src/lib.rs b/frame/uniques/src/lib.rs index da0f0e056..587f1ae4b 100644 --- a/frame/uniques/src/lib.rs +++ b/frame/uniques/src/lib.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2017-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/frame/uniques/src/migration.rs b/frame/uniques/src/migration.rs index fd98c030d..6c92b753b 100644 --- a/frame/uniques/src/migration.rs +++ b/frame/uniques/src/migration.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2021-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/frame/uniques/src/mock.rs b/frame/uniques/src/mock.rs index d6ed5cc5c..67d994d79 100644 --- a/frame/uniques/src/mock.rs +++ b/frame/uniques/src/mock.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2019-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/frame/uniques/src/tests.rs b/frame/uniques/src/tests.rs index 7af54ddbb..993552c3a 100644 --- a/frame/uniques/src/tests.rs +++ b/frame/uniques/src/tests.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2019-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/frame/uniques/src/types.rs b/frame/uniques/src/types.rs index 98e056163..5b1399815 100644 --- a/frame/uniques/src/types.rs +++ b/frame/uniques/src/types.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2017-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/frame/uniques/src/weights.rs b/frame/uniques/src/weights.rs index 3c1b102ba..144a384f0 100644 --- a/frame/uniques/src/weights.rs +++ b/frame/uniques/src/weights.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2023 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/frame/utility/src/benchmarking.rs b/frame/utility/src/benchmarking.rs index f381b1e5f..78911fd31 100644 --- a/frame/utility/src/benchmarking.rs +++ b/frame/utility/src/benchmarking.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2019-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/frame/utility/src/lib.rs b/frame/utility/src/lib.rs index a28dd4994..6cfa8b7e3 100644 --- a/frame/utility/src/lib.rs +++ b/frame/utility/src/lib.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2019-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/frame/utility/src/tests.rs b/frame/utility/src/tests.rs index c63ed24c6..938a5392b 100644 --- a/frame/utility/src/tests.rs +++ b/frame/utility/src/tests.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2019-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/frame/utility/src/weights.rs b/frame/utility/src/weights.rs index 7b8c261d1..8c7a14715 100644 --- a/frame/utility/src/weights.rs +++ b/frame/utility/src/weights.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2023 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/frame/vesting/src/benchmarking.rs b/frame/vesting/src/benchmarking.rs index eb0d596b8..35a094a9f 100644 --- a/frame/vesting/src/benchmarking.rs +++ b/frame/vesting/src/benchmarking.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2020-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/frame/vesting/src/lib.rs b/frame/vesting/src/lib.rs index 6cde546f3..dbc018796 100644 --- a/frame/vesting/src/lib.rs +++ b/frame/vesting/src/lib.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2019-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/frame/vesting/src/migrations.rs b/frame/vesting/src/migrations.rs index 15668425b..69bbc9729 100644 --- a/frame/vesting/src/migrations.rs +++ b/frame/vesting/src/migrations.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2017-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/frame/vesting/src/mock.rs b/frame/vesting/src/mock.rs index da9490bea..f2ad6a700 100644 --- a/frame/vesting/src/mock.rs +++ b/frame/vesting/src/mock.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2019-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/frame/vesting/src/tests.rs b/frame/vesting/src/tests.rs index cbc2e09c8..efc134cab 100644 --- a/frame/vesting/src/tests.rs +++ b/frame/vesting/src/tests.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2019-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/frame/vesting/src/vesting_info.rs b/frame/vesting/src/vesting_info.rs index 9069b6948..5d5ae31fc 100644 --- a/frame/vesting/src/vesting_info.rs +++ b/frame/vesting/src/vesting_info.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2017-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/frame/vesting/src/weights.rs b/frame/vesting/src/weights.rs index a6a70fb56..350e6a86e 100644 --- a/frame/vesting/src/weights.rs +++ b/frame/vesting/src/weights.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2023 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/frame/whitelist/src/benchmarking.rs b/frame/whitelist/src/benchmarking.rs index 582f4a799..1982f5eb8 100644 --- a/frame/whitelist/src/benchmarking.rs +++ b/frame/whitelist/src/benchmarking.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2021 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/frame/whitelist/src/lib.rs b/frame/whitelist/src/lib.rs index 8a5666331..bfbadd073 100644 --- a/frame/whitelist/src/lib.rs +++ b/frame/whitelist/src/lib.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2021 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/frame/whitelist/src/mock.rs b/frame/whitelist/src/mock.rs index e08c2875a..b16286863 100644 --- a/frame/whitelist/src/mock.rs +++ b/frame/whitelist/src/mock.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2021 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/frame/whitelist/src/tests.rs b/frame/whitelist/src/tests.rs index d04bb48c1..986a09b8e 100644 --- a/frame/whitelist/src/tests.rs +++ b/frame/whitelist/src/tests.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2021 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/frame/whitelist/src/weights.rs b/frame/whitelist/src/weights.rs index d5bc37c57..2b25e8194 100644 --- a/frame/whitelist/src/weights.rs +++ b/frame/whitelist/src/weights.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2023 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/primitives/api/proc-macro/src/common.rs b/primitives/api/proc-macro/src/common.rs index 10887be61..d29057723 100644 --- a/primitives/api/proc-macro/src/common.rs +++ b/primitives/api/proc-macro/src/common.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2018-2024 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/primitives/api/proc-macro/src/decl_runtime_apis.rs b/primitives/api/proc-macro/src/decl_runtime_apis.rs index c254237cc..d9a72adad 100644 --- a/primitives/api/proc-macro/src/decl_runtime_apis.rs +++ b/primitives/api/proc-macro/src/decl_runtime_apis.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2018-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/primitives/api/proc-macro/src/impl_runtime_apis.rs b/primitives/api/proc-macro/src/impl_runtime_apis.rs index 76c9e1164..f32961f89 100644 --- a/primitives/api/proc-macro/src/impl_runtime_apis.rs +++ b/primitives/api/proc-macro/src/impl_runtime_apis.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2018-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/primitives/api/proc-macro/src/lib.rs b/primitives/api/proc-macro/src/lib.rs index 31636b8e2..cea958426 100644 --- a/primitives/api/proc-macro/src/lib.rs +++ b/primitives/api/proc-macro/src/lib.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2018-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/primitives/api/proc-macro/src/mock_impl_runtime_apis.rs b/primitives/api/proc-macro/src/mock_impl_runtime_apis.rs index 493111e4f..4c79787a1 100644 --- a/primitives/api/proc-macro/src/mock_impl_runtime_apis.rs +++ b/primitives/api/proc-macro/src/mock_impl_runtime_apis.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2020-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/primitives/api/proc-macro/src/utils.rs b/primitives/api/proc-macro/src/utils.rs index 6f2a766e9..dee29b1a5 100644 --- a/primitives/api/proc-macro/src/utils.rs +++ b/primitives/api/proc-macro/src/utils.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2018-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/primitives/api/src/lib.rs b/primitives/api/src/lib.rs index c9605b811..7542ca3f2 100644 --- a/primitives/api/src/lib.rs +++ b/primitives/api/src/lib.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2019-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/primitives/api/test/benches/bench.rs b/primitives/api/test/benches/bench.rs index 0d47d3038..88ebdbc61 100644 --- a/primitives/api/test/benches/bench.rs +++ b/primitives/api/test/benches/bench.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2018-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/primitives/api/test/tests/decl_and_impl.rs b/primitives/api/test/tests/decl_and_impl.rs index 47e33d81d..78990d546 100644 --- a/primitives/api/test/tests/decl_and_impl.rs +++ b/primitives/api/test/tests/decl_and_impl.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2019-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/primitives/api/test/tests/runtime_calls.rs b/primitives/api/test/tests/runtime_calls.rs index feb3e1a6e..69f9a88ff 100644 --- a/primitives/api/test/tests/runtime_calls.rs +++ b/primitives/api/test/tests/runtime_calls.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2019-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/primitives/api/test/tests/trybuild.rs b/primitives/api/test/tests/trybuild.rs index 13af1ded7..b0a334eb7 100644 --- a/primitives/api/test/tests/trybuild.rs +++ b/primitives/api/test/tests/trybuild.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2019-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/primitives/application-crypto/src/ecdsa.rs b/primitives/application-crypto/src/ecdsa.rs index 6356f54a0..011456df0 100644 --- a/primitives/application-crypto/src/ecdsa.rs +++ b/primitives/application-crypto/src/ecdsa.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2019-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/primitives/application-crypto/src/ed25519.rs b/primitives/application-crypto/src/ed25519.rs index 199e55383..822dd3fa5 100644 --- a/primitives/application-crypto/src/ed25519.rs +++ b/primitives/application-crypto/src/ed25519.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2019-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/primitives/application-crypto/src/lib.rs b/primitives/application-crypto/src/lib.rs index 05f89c40e..992ecd1d0 100644 --- a/primitives/application-crypto/src/lib.rs +++ b/primitives/application-crypto/src/lib.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2019-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/primitives/application-crypto/src/sr25519.rs b/primitives/application-crypto/src/sr25519.rs index c96e7382e..a961f5cf3 100644 --- a/primitives/application-crypto/src/sr25519.rs +++ b/primitives/application-crypto/src/sr25519.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2019-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/primitives/application-crypto/src/traits.rs b/primitives/application-crypto/src/traits.rs index 853208bc2..e3586ccec 100644 --- a/primitives/application-crypto/src/traits.rs +++ b/primitives/application-crypto/src/traits.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2019-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/primitives/application-crypto/test/src/ecdsa.rs b/primitives/application-crypto/test/src/ecdsa.rs index 4fe77db30..df3be6f93 100644 --- a/primitives/application-crypto/test/src/ecdsa.rs +++ b/primitives/application-crypto/test/src/ecdsa.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2019-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/primitives/application-crypto/test/src/ed25519.rs b/primitives/application-crypto/test/src/ed25519.rs index 7f37744ec..8ce08717a 100644 --- a/primitives/application-crypto/test/src/ed25519.rs +++ b/primitives/application-crypto/test/src/ed25519.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2019-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/primitives/application-crypto/test/src/lib.rs b/primitives/application-crypto/test/src/lib.rs index 7cc3f8b07..90856ee1e 100644 --- a/primitives/application-crypto/test/src/lib.rs +++ b/primitives/application-crypto/test/src/lib.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2019-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/primitives/application-crypto/test/src/sr25519.rs b/primitives/application-crypto/test/src/sr25519.rs index aa6734bf6..4c1d0f1ef 100644 --- a/primitives/application-crypto/test/src/sr25519.rs +++ b/primitives/application-crypto/test/src/sr25519.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2019-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/primitives/arithmetic/benches/bench.rs b/primitives/arithmetic/benches/bench.rs index 5551d2090..6a8abca14 100644 --- a/primitives/arithmetic/benches/bench.rs +++ b/primitives/arithmetic/benches/bench.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2019-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/primitives/arithmetic/fuzzer/src/biguint.rs b/primitives/arithmetic/fuzzer/src/biguint.rs index f49743a4b..2f9f54c81 100644 --- a/primitives/arithmetic/fuzzer/src/biguint.rs +++ b/primitives/arithmetic/fuzzer/src/biguint.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2019-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/primitives/arithmetic/fuzzer/src/fixed_point.rs b/primitives/arithmetic/fuzzer/src/fixed_point.rs index c1b93f8c6..e76dd1503 100644 --- a/primitives/arithmetic/fuzzer/src/fixed_point.rs +++ b/primitives/arithmetic/fuzzer/src/fixed_point.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2020-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/primitives/arithmetic/fuzzer/src/multiply_by_rational_with_rounding.rs b/primitives/arithmetic/fuzzer/src/multiply_by_rational_with_rounding.rs index 474b2d363..13c9d022b 100644 --- a/primitives/arithmetic/fuzzer/src/multiply_by_rational_with_rounding.rs +++ b/primitives/arithmetic/fuzzer/src/multiply_by_rational_with_rounding.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2019-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/primitives/arithmetic/fuzzer/src/normalize.rs b/primitives/arithmetic/fuzzer/src/normalize.rs index 3d2d6eb9a..721181901 100644 --- a/primitives/arithmetic/fuzzer/src/normalize.rs +++ b/primitives/arithmetic/fuzzer/src/normalize.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2020-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/primitives/arithmetic/fuzzer/src/per_thing_rational.rs b/primitives/arithmetic/fuzzer/src/per_thing_rational.rs index 7021c54c0..c9c514656 100644 --- a/primitives/arithmetic/fuzzer/src/per_thing_rational.rs +++ b/primitives/arithmetic/fuzzer/src/per_thing_rational.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2019-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/primitives/arithmetic/src/biguint.rs b/primitives/arithmetic/src/biguint.rs index 33f0960ee..d92b08c8e 100644 --- a/primitives/arithmetic/src/biguint.rs +++ b/primitives/arithmetic/src/biguint.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2019-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/primitives/arithmetic/src/fixed_point.rs b/primitives/arithmetic/src/fixed_point.rs index bf3c93cda..67fbd5bc7 100644 --- a/primitives/arithmetic/src/fixed_point.rs +++ b/primitives/arithmetic/src/fixed_point.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2019-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/primitives/arithmetic/src/helpers_128bit.rs b/primitives/arithmetic/src/helpers_128bit.rs index 7938c31d1..56432d15a 100644 --- a/primitives/arithmetic/src/helpers_128bit.rs +++ b/primitives/arithmetic/src/helpers_128bit.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2019-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // Some code is based upon Derek Dreery's IntegerSquareRoot impl, used under license. // SPDX-License-Identifier: Apache-2.0 diff --git a/primitives/arithmetic/src/lib.rs b/primitives/arithmetic/src/lib.rs index 590aaa316..581206b6d 100644 --- a/primitives/arithmetic/src/lib.rs +++ b/primitives/arithmetic/src/lib.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2019-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/primitives/arithmetic/src/per_things.rs b/primitives/arithmetic/src/per_things.rs index fc3767761..068bdb4e1 100644 --- a/primitives/arithmetic/src/per_things.rs +++ b/primitives/arithmetic/src/per_things.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2019-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/primitives/arithmetic/src/rational.rs b/primitives/arithmetic/src/rational.rs index 447b37551..0fcb7d4d9 100644 --- a/primitives/arithmetic/src/rational.rs +++ b/primitives/arithmetic/src/rational.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2019-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/primitives/arithmetic/src/traits.rs b/primitives/arithmetic/src/traits.rs index dfba04675..91e13a832 100644 --- a/primitives/arithmetic/src/traits.rs +++ b/primitives/arithmetic/src/traits.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2017-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/primitives/authority-discovery/src/lib.rs b/primitives/authority-discovery/src/lib.rs index 95bb458b1..3b25e39d4 100644 --- a/primitives/authority-discovery/src/lib.rs +++ b/primitives/authority-discovery/src/lib.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2019-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/primitives/beefy/src/commitment.rs b/primitives/beefy/src/commitment.rs index 8642a83aa..f824c90e2 100644 --- a/primitives/beefy/src/commitment.rs +++ b/primitives/beefy/src/commitment.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2021-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/primitives/beefy/src/lib.rs b/primitives/beefy/src/lib.rs index ad9c1b8d1..2e3ec2ef8 100644 --- a/primitives/beefy/src/lib.rs +++ b/primitives/beefy/src/lib.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2021-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/primitives/beefy/src/mmr.rs b/primitives/beefy/src/mmr.rs index ca60f2a91..465008dc2 100644 --- a/primitives/beefy/src/mmr.rs +++ b/primitives/beefy/src/mmr.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2021-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/primitives/beefy/src/payload.rs b/primitives/beefy/src/payload.rs index 0f23c3f38..d520de445 100644 --- a/primitives/beefy/src/payload.rs +++ b/primitives/beefy/src/payload.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/primitives/beefy/src/test_utils.rs b/primitives/beefy/src/test_utils.rs index 1a5e03be3..9e0758cde 100644 --- a/primitives/beefy/src/test_utils.rs +++ b/primitives/beefy/src/test_utils.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2021-2023 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/primitives/beefy/src/witness.rs b/primitives/beefy/src/witness.rs index 2c45e0ade..9cc31d54b 100644 --- a/primitives/beefy/src/witness.rs +++ b/primitives/beefy/src/witness.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2021-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/primitives/block-builder/src/lib.rs b/primitives/block-builder/src/lib.rs index cf1bfa256..29e04857f 100644 --- a/primitives/block-builder/src/lib.rs +++ b/primitives/block-builder/src/lib.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2019-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/primitives/blockchain/src/backend.rs b/primitives/blockchain/src/backend.rs index 7339d4c1a..227559239 100644 --- a/primitives/blockchain/src/backend.rs +++ b/primitives/blockchain/src/backend.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2017-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/primitives/blockchain/src/error.rs b/primitives/blockchain/src/error.rs index 6585cc54f..41e5cda9c 100644 --- a/primitives/blockchain/src/error.rs +++ b/primitives/blockchain/src/error.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2017-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/primitives/blockchain/src/header_metadata.rs b/primitives/blockchain/src/header_metadata.rs index 87ac44459..1d406dd0f 100644 --- a/primitives/blockchain/src/header_metadata.rs +++ b/primitives/blockchain/src/header_metadata.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2019-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/primitives/blockchain/src/lib.rs b/primitives/blockchain/src/lib.rs index 2fdef6cba..eabbbcf50 100644 --- a/primitives/blockchain/src/lib.rs +++ b/primitives/blockchain/src/lib.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2019-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/primitives/consensus/aura/src/digests.rs b/primitives/consensus/aura/src/digests.rs index b71930b6c..13484da2a 100644 --- a/primitives/consensus/aura/src/digests.rs +++ b/primitives/consensus/aura/src/digests.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2018-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 // This program is free software: you can redistribute it and/or modify diff --git a/primitives/consensus/aura/src/inherents.rs b/primitives/consensus/aura/src/inherents.rs index 135ae2660..1ef25feb0 100644 --- a/primitives/consensus/aura/src/inherents.rs +++ b/primitives/consensus/aura/src/inherents.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2019-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/primitives/consensus/aura/src/lib.rs b/primitives/consensus/aura/src/lib.rs index 2c6a97b93..78409e84e 100644 --- a/primitives/consensus/aura/src/lib.rs +++ b/primitives/consensus/aura/src/lib.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2017-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/primitives/consensus/babe/src/digests.rs b/primitives/consensus/babe/src/digests.rs index 1e4c82037..4364057a4 100644 --- a/primitives/consensus/babe/src/digests.rs +++ b/primitives/consensus/babe/src/digests.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2019-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/primitives/consensus/babe/src/inherents.rs b/primitives/consensus/babe/src/inherents.rs index 263450796..b01bd1c92 100644 --- a/primitives/consensus/babe/src/inherents.rs +++ b/primitives/consensus/babe/src/inherents.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2019-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/primitives/consensus/babe/src/lib.rs b/primitives/consensus/babe/src/lib.rs index 10d027bd9..e7747ac4c 100644 --- a/primitives/consensus/babe/src/lib.rs +++ b/primitives/consensus/babe/src/lib.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2019-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/primitives/consensus/common/src/block_validation.rs b/primitives/consensus/common/src/block_validation.rs index 1dc421049..91e5330bb 100644 --- a/primitives/consensus/common/src/block_validation.rs +++ b/primitives/consensus/common/src/block_validation.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2019-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/primitives/consensus/common/src/error.rs b/primitives/consensus/common/src/error.rs index 0656b5761..33c1afa63 100644 --- a/primitives/consensus/common/src/error.rs +++ b/primitives/consensus/common/src/error.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2017-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/primitives/consensus/common/src/lib.rs b/primitives/consensus/common/src/lib.rs index 458a5eee2..e02564654 100644 --- a/primitives/consensus/common/src/lib.rs +++ b/primitives/consensus/common/src/lib.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2018-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/primitives/consensus/common/src/select_chain.rs b/primitives/consensus/common/src/select_chain.rs index 5beab6705..d387cc1ad 100644 --- a/primitives/consensus/common/src/select_chain.rs +++ b/primitives/consensus/common/src/select_chain.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2019-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/primitives/consensus/pow/src/lib.rs b/primitives/consensus/pow/src/lib.rs index fe10ee808..f37aae1c5 100644 --- a/primitives/consensus/pow/src/lib.rs +++ b/primitives/consensus/pow/src/lib.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2017-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/primitives/consensus/slots/src/lib.rs b/primitives/consensus/slots/src/lib.rs index 21b3cad1e..d72378527 100644 --- a/primitives/consensus/slots/src/lib.rs +++ b/primitives/consensus/slots/src/lib.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2020-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/primitives/consensus/vrf/src/lib.rs b/primitives/consensus/vrf/src/lib.rs index 07e3f2c31..84040c185 100644 --- a/primitives/consensus/vrf/src/lib.rs +++ b/primitives/consensus/vrf/src/lib.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2019-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/primitives/consensus/vrf/src/schnorrkel.rs b/primitives/consensus/vrf/src/schnorrkel.rs index 8666de6c4..be145c5b1 100644 --- a/primitives/consensus/vrf/src/schnorrkel.rs +++ b/primitives/consensus/vrf/src/schnorrkel.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2019-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/primitives/core/benches/bench.rs b/primitives/core/benches/bench.rs index 77e5bb63f..e91c1758c 100644 --- a/primitives/core/benches/bench.rs +++ b/primitives/core/benches/bench.rs @@ -1,4 +1,4 @@ -// Copyright 2019-2020 Parity Technologies +// Copyright Parity Technologies (UK) Ltd. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/primitives/core/hashing/proc-macro/src/impls.rs b/primitives/core/hashing/proc-macro/src/impls.rs index 3058cf019..714852ae3 100644 --- a/primitives/core/hashing/proc-macro/src/impls.rs +++ b/primitives/core/hashing/proc-macro/src/impls.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2021-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/primitives/core/hashing/proc-macro/src/lib.rs b/primitives/core/hashing/proc-macro/src/lib.rs index 2db292d8d..69668cadb 100644 --- a/primitives/core/hashing/proc-macro/src/lib.rs +++ b/primitives/core/hashing/proc-macro/src/lib.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2021-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/primitives/core/hashing/src/lib.rs b/primitives/core/hashing/src/lib.rs index e6ccd5aaa..5bb68586a 100644 --- a/primitives/core/hashing/src/lib.rs +++ b/primitives/core/hashing/src/lib.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2017-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/primitives/core/src/bounded.rs b/primitives/core/src/bounded.rs index 45b4a9ca6..c78f1f85a 100644 --- a/primitives/core/src/bounded.rs +++ b/primitives/core/src/bounded.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/primitives/core/src/crypto.rs b/primitives/core/src/crypto.rs index 52c805bb6..16af3d069 100644 --- a/primitives/core/src/crypto.rs +++ b/primitives/core/src/crypto.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2017-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/primitives/core/src/defer.rs b/primitives/core/src/defer.rs index d14b26d59..c5ff50259 100644 --- a/primitives/core/src/defer.rs +++ b/primitives/core/src/defer.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/primitives/core/src/ecdsa.rs b/primitives/core/src/ecdsa.rs index ca6b80062..d68ba39a0 100644 --- a/primitives/core/src/ecdsa.rs +++ b/primitives/core/src/ecdsa.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2017-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/primitives/core/src/ed25519.rs b/primitives/core/src/ed25519.rs index e85eb87c9..4eb9401a9 100644 --- a/primitives/core/src/ed25519.rs +++ b/primitives/core/src/ed25519.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2017-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/primitives/core/src/hash.rs b/primitives/core/src/hash.rs index f2974e937..ec7da9829 100644 --- a/primitives/core/src/hash.rs +++ b/primitives/core/src/hash.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2017-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/primitives/core/src/hasher.rs b/primitives/core/src/hasher.rs index 173bd560a..5c4c40cba 100644 --- a/primitives/core/src/hasher.rs +++ b/primitives/core/src/hasher.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2018-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/primitives/core/src/hashing.rs b/primitives/core/src/hashing.rs index 1c439355a..e71d0b543 100644 --- a/primitives/core/src/hashing.rs +++ b/primitives/core/src/hashing.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2017-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/primitives/core/src/hexdisplay.rs b/primitives/core/src/hexdisplay.rs index 26c04c433..9f35e5ec7 100644 --- a/primitives/core/src/hexdisplay.rs +++ b/primitives/core/src/hexdisplay.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2017-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/primitives/core/src/lib.rs b/primitives/core/src/lib.rs index 13dbad66b..efccd0378 100644 --- a/primitives/core/src/lib.rs +++ b/primitives/core/src/lib.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2017-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/primitives/core/src/offchain/mod.rs b/primitives/core/src/offchain/mod.rs index 3f0eed87a..5a77e19a3 100644 --- a/primitives/core/src/offchain/mod.rs +++ b/primitives/core/src/offchain/mod.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2019-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/primitives/core/src/offchain/storage.rs b/primitives/core/src/offchain/storage.rs index cf2c93641..3a114de5b 100644 --- a/primitives/core/src/offchain/storage.rs +++ b/primitives/core/src/offchain/storage.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2019-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/primitives/core/src/offchain/testing.rs b/primitives/core/src/offchain/testing.rs index a2065eb17..ee3620e70 100644 --- a/primitives/core/src/offchain/testing.rs +++ b/primitives/core/src/offchain/testing.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2019-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/primitives/core/src/sr25519.rs b/primitives/core/src/sr25519.rs index 9064fb742..1206cf5f4 100644 --- a/primitives/core/src/sr25519.rs +++ b/primitives/core/src/sr25519.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2017-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/primitives/core/src/testing.rs b/primitives/core/src/testing.rs index d3fa3fc86..756275aed 100644 --- a/primitives/core/src/testing.rs +++ b/primitives/core/src/testing.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2019-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/primitives/core/src/traits.rs b/primitives/core/src/traits.rs index c4b7f20f7..3fe2a2ed3 100644 --- a/primitives/core/src/traits.rs +++ b/primitives/core/src/traits.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2019-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/primitives/core/src/uint.rs b/primitives/core/src/uint.rs index 4bf914bde..df49511e8 100644 --- a/primitives/core/src/uint.rs +++ b/primitives/core/src/uint.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2017-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/primitives/database/src/error.rs b/primitives/database/src/error.rs index 78646427a..0cc1159b2 100644 --- a/primitives/database/src/error.rs +++ b/primitives/database/src/error.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2020-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/primitives/database/src/kvdb.rs b/primitives/database/src/kvdb.rs index 5fe5fda30..735813c36 100644 --- a/primitives/database/src/kvdb.rs +++ b/primitives/database/src/kvdb.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2017-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/primitives/database/src/lib.rs b/primitives/database/src/lib.rs index 3ec62a2b7..012f69955 100644 --- a/primitives/database/src/lib.rs +++ b/primitives/database/src/lib.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2017-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/primitives/database/src/mem.rs b/primitives/database/src/mem.rs index 6ecd44c60..71ba7a992 100644 --- a/primitives/database/src/mem.rs +++ b/primitives/database/src/mem.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2017-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/primitives/debug-derive/src/impls.rs b/primitives/debug-derive/src/impls.rs index 51a4d876c..76ef83672 100644 --- a/primitives/debug-derive/src/impls.rs +++ b/primitives/debug-derive/src/impls.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2019-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/primitives/debug-derive/src/lib.rs b/primitives/debug-derive/src/lib.rs index c98610ce4..639dbb6df 100644 --- a/primitives/debug-derive/src/lib.rs +++ b/primitives/debug-derive/src/lib.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2019-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/primitives/debug-derive/tests/tests.rs b/primitives/debug-derive/tests/tests.rs index 39414da86..da521068e 100644 --- a/primitives/debug-derive/tests/tests.rs +++ b/primitives/debug-derive/tests/tests.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2019-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/primitives/externalities/src/extensions.rs b/primitives/externalities/src/extensions.rs index ecb489e5e..84155227a 100644 --- a/primitives/externalities/src/extensions.rs +++ b/primitives/externalities/src/extensions.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2017-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/primitives/externalities/src/lib.rs b/primitives/externalities/src/lib.rs index b343b8c39..411ec97a6 100644 --- a/primitives/externalities/src/lib.rs +++ b/primitives/externalities/src/lib.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2017-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/primitives/externalities/src/scope_limited.rs b/primitives/externalities/src/scope_limited.rs index 1d2e6863c..2167db7c1 100644 --- a/primitives/externalities/src/scope_limited.rs +++ b/primitives/externalities/src/scope_limited.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2019-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/primitives/finality-grandpa/src/lib.rs b/primitives/finality-grandpa/src/lib.rs index f5307ab06..b466dcf37 100644 --- a/primitives/finality-grandpa/src/lib.rs +++ b/primitives/finality-grandpa/src/lib.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2018-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/primitives/inherents/src/client_side.rs b/primitives/inherents/src/client_side.rs index 1ece7e1e4..27479de13 100644 --- a/primitives/inherents/src/client_side.rs +++ b/primitives/inherents/src/client_side.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2021-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/primitives/inherents/src/lib.rs b/primitives/inherents/src/lib.rs index f05c9a9ad..dd7c294f1 100644 --- a/primitives/inherents/src/lib.rs +++ b/primitives/inherents/src/lib.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2019-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/primitives/io/src/batch_verifier.rs b/primitives/io/src/batch_verifier.rs index 501d98675..e6d8c6131 100644 --- a/primitives/io/src/batch_verifier.rs +++ b/primitives/io/src/batch_verifier.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2020-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/primitives/io/src/lib.rs b/primitives/io/src/lib.rs index bb06c00ee..8c3cdc668 100644 --- a/primitives/io/src/lib.rs +++ b/primitives/io/src/lib.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2017-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/primitives/keyring/src/ed25519.rs b/primitives/keyring/src/ed25519.rs index 404e4121e..c3ad86409 100644 --- a/primitives/keyring/src/ed25519.rs +++ b/primitives/keyring/src/ed25519.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2017-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/primitives/keyring/src/lib.rs b/primitives/keyring/src/lib.rs index 170a4c3d0..7432aff12 100644 --- a/primitives/keyring/src/lib.rs +++ b/primitives/keyring/src/lib.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2017-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/primitives/keyring/src/sr25519.rs b/primitives/keyring/src/sr25519.rs index 115a7fce7..c738cfdc5 100644 --- a/primitives/keyring/src/sr25519.rs +++ b/primitives/keyring/src/sr25519.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2017-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/primitives/keystore/src/lib.rs b/primitives/keystore/src/lib.rs index 6540e71bc..17c435483 100644 --- a/primitives/keystore/src/lib.rs +++ b/primitives/keystore/src/lib.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2020-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/primitives/keystore/src/testing.rs b/primitives/keystore/src/testing.rs index a9ec6709d..5058a0650 100644 --- a/primitives/keystore/src/testing.rs +++ b/primitives/keystore/src/testing.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2019-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/primitives/keystore/src/vrf.rs b/primitives/keystore/src/vrf.rs index 7409353af..e089336c1 100644 --- a/primitives/keystore/src/vrf.rs +++ b/primitives/keystore/src/vrf.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2019-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/primitives/maybe-compressed-blob/src/lib.rs b/primitives/maybe-compressed-blob/src/lib.rs index 99c12ed39..add620d4a 100644 --- a/primitives/maybe-compressed-blob/src/lib.rs +++ b/primitives/maybe-compressed-blob/src/lib.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2017-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/primitives/merkle-mountain-range/src/lib.rs b/primitives/merkle-mountain-range/src/lib.rs index df0a7f75a..1c319c4e3 100644 --- a/primitives/merkle-mountain-range/src/lib.rs +++ b/primitives/merkle-mountain-range/src/lib.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2020-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/primitives/merkle-mountain-range/src/utils.rs b/primitives/merkle-mountain-range/src/utils.rs index 619ca7e98..b9171c96a 100644 --- a/primitives/merkle-mountain-range/src/utils.rs +++ b/primitives/merkle-mountain-range/src/utils.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2020-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/primitives/npos-elections/fuzzer/src/common.rs b/primitives/npos-elections/fuzzer/src/common.rs index ad9bd43f9..ebf103401 100644 --- a/primitives/npos-elections/fuzzer/src/common.rs +++ b/primitives/npos-elections/fuzzer/src/common.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2020-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/primitives/npos-elections/fuzzer/src/phragmen_balancing.rs b/primitives/npos-elections/fuzzer/src/phragmen_balancing.rs index e053f9aa0..c7aaf6cee 100644 --- a/primitives/npos-elections/fuzzer/src/phragmen_balancing.rs +++ b/primitives/npos-elections/fuzzer/src/phragmen_balancing.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2020-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/primitives/npos-elections/fuzzer/src/phragmen_pjr.rs b/primitives/npos-elections/fuzzer/src/phragmen_pjr.rs index 0401249a3..94e697d9c 100644 --- a/primitives/npos-elections/fuzzer/src/phragmen_pjr.rs +++ b/primitives/npos-elections/fuzzer/src/phragmen_pjr.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2020-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/primitives/npos-elections/fuzzer/src/phragmms_balancing.rs b/primitives/npos-elections/fuzzer/src/phragmms_balancing.rs index 3f114674e..067b788f0 100644 --- a/primitives/npos-elections/fuzzer/src/phragmms_balancing.rs +++ b/primitives/npos-elections/fuzzer/src/phragmms_balancing.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2020-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/primitives/npos-elections/fuzzer/src/reduce.rs b/primitives/npos-elections/fuzzer/src/reduce.rs index 602467a34..9ac34189d 100644 --- a/primitives/npos-elections/fuzzer/src/reduce.rs +++ b/primitives/npos-elections/fuzzer/src/reduce.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2020-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/primitives/npos-elections/src/assignments.rs b/primitives/npos-elections/src/assignments.rs index fc88ef400..9390cd1f4 100644 --- a/primitives/npos-elections/src/assignments.rs +++ b/primitives/npos-elections/src/assignments.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2020-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/primitives/npos-elections/src/balancing.rs b/primitives/npos-elections/src/balancing.rs index 4a713658a..234326ee9 100644 --- a/primitives/npos-elections/src/balancing.rs +++ b/primitives/npos-elections/src/balancing.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2020-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/primitives/npos-elections/src/helpers.rs b/primitives/npos-elections/src/helpers.rs index 598eb3b2e..082491ea0 100644 --- a/primitives/npos-elections/src/helpers.rs +++ b/primitives/npos-elections/src/helpers.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2020-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/primitives/npos-elections/src/lib.rs b/primitives/npos-elections/src/lib.rs index d0c9ed18c..716c4b283 100644 --- a/primitives/npos-elections/src/lib.rs +++ b/primitives/npos-elections/src/lib.rs @@ -1,6 +1,7 @@ // This file is part of Substrate. -// Copyright (C) 2019-2022 Parity Technologies (UK) Ltd. SPDX-License-Identifier: Apache-2.0 +// Copyright (C) Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except // in compliance with the License. You may obtain a copy of the License at diff --git a/primitives/npos-elections/src/mock.rs b/primitives/npos-elections/src/mock.rs index 5a06e3f3c..2fc49fd72 100644 --- a/primitives/npos-elections/src/mock.rs +++ b/primitives/npos-elections/src/mock.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2019-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/primitives/npos-elections/src/node.rs b/primitives/npos-elections/src/node.rs index 6642a9ae3..caca9561d 100644 --- a/primitives/npos-elections/src/node.rs +++ b/primitives/npos-elections/src/node.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2020-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/primitives/npos-elections/src/phragmen.rs b/primitives/npos-elections/src/phragmen.rs index ca32780ed..c3578065f 100644 --- a/primitives/npos-elections/src/phragmen.rs +++ b/primitives/npos-elections/src/phragmen.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2020-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/primitives/npos-elections/src/phragmms.rs b/primitives/npos-elections/src/phragmms.rs index 3fbbad75e..df6becf47 100644 --- a/primitives/npos-elections/src/phragmms.rs +++ b/primitives/npos-elections/src/phragmms.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2020-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/primitives/npos-elections/src/pjr.rs b/primitives/npos-elections/src/pjr.rs index fd7c8ef53..f0e59a25d 100644 --- a/primitives/npos-elections/src/pjr.rs +++ b/primitives/npos-elections/src/pjr.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2021-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/primitives/npos-elections/src/reduce.rs b/primitives/npos-elections/src/reduce.rs index c802a2950..6a5a0159e 100644 --- a/primitives/npos-elections/src/reduce.rs +++ b/primitives/npos-elections/src/reduce.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2020-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/primitives/npos-elections/src/tests.rs b/primitives/npos-elections/src/tests.rs index 6f2e4fca7..72ae9a022 100644 --- a/primitives/npos-elections/src/tests.rs +++ b/primitives/npos-elections/src/tests.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2019-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/primitives/npos-elections/src/traits.rs b/primitives/npos-elections/src/traits.rs index 91026f9de..d49970873 100644 --- a/primitives/npos-elections/src/traits.rs +++ b/primitives/npos-elections/src/traits.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2019-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/primitives/offchain/src/lib.rs b/primitives/offchain/src/lib.rs index 3e967f16d..56de4f158 100644 --- a/primitives/offchain/src/lib.rs +++ b/primitives/offchain/src/lib.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2018-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/primitives/panic-handler/src/lib.rs b/primitives/panic-handler/src/lib.rs index e06fe90ad..e2a9bfa19 100644 --- a/primitives/panic-handler/src/lib.rs +++ b/primitives/panic-handler/src/lib.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2017-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/primitives/rpc/src/lib.rs b/primitives/rpc/src/lib.rs index 915482e5f..4dbc629bb 100644 --- a/primitives/rpc/src/lib.rs +++ b/primitives/rpc/src/lib.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2019-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/primitives/rpc/src/list.rs b/primitives/rpc/src/list.rs index 7ec4d3491..860e5161b 100644 --- a/primitives/rpc/src/list.rs +++ b/primitives/rpc/src/list.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2019-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/primitives/rpc/src/number.rs b/primitives/rpc/src/number.rs index 81084a09d..28caa243e 100644 --- a/primitives/rpc/src/number.rs +++ b/primitives/rpc/src/number.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2017-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/primitives/rpc/src/tracing.rs b/primitives/rpc/src/tracing.rs index a923ffcb6..eb3fd4784 100644 --- a/primitives/rpc/src/tracing.rs +++ b/primitives/rpc/src/tracing.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2020-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/primitives/runtime-interface/proc-macro/src/lib.rs b/primitives/runtime-interface/proc-macro/src/lib.rs index afba38993..e6f060c21 100644 --- a/primitives/runtime-interface/proc-macro/src/lib.rs +++ b/primitives/runtime-interface/proc-macro/src/lib.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2019-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/primitives/runtime-interface/proc-macro/src/pass_by/codec.rs b/primitives/runtime-interface/proc-macro/src/pass_by/codec.rs index 4259b137d..a1b7bccd3 100644 --- a/primitives/runtime-interface/proc-macro/src/pass_by/codec.rs +++ b/primitives/runtime-interface/proc-macro/src/pass_by/codec.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2019-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/primitives/runtime-interface/proc-macro/src/pass_by/enum_.rs b/primitives/runtime-interface/proc-macro/src/pass_by/enum_.rs index e25295fdc..0d05dd9aa 100644 --- a/primitives/runtime-interface/proc-macro/src/pass_by/enum_.rs +++ b/primitives/runtime-interface/proc-macro/src/pass_by/enum_.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2019-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/primitives/runtime-interface/proc-macro/src/pass_by/inner.rs b/primitives/runtime-interface/proc-macro/src/pass_by/inner.rs index 7c9bd63ab..cc51fe44f 100644 --- a/primitives/runtime-interface/proc-macro/src/pass_by/inner.rs +++ b/primitives/runtime-interface/proc-macro/src/pass_by/inner.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2019-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/primitives/runtime-interface/proc-macro/src/pass_by/mod.rs b/primitives/runtime-interface/proc-macro/src/pass_by/mod.rs index e32c2beb8..f3d51d362 100644 --- a/primitives/runtime-interface/proc-macro/src/pass_by/mod.rs +++ b/primitives/runtime-interface/proc-macro/src/pass_by/mod.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2019-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/primitives/runtime-interface/proc-macro/src/runtime_interface/bare_function_interface.rs b/primitives/runtime-interface/proc-macro/src/runtime_interface/bare_function_interface.rs index b5745e25d..16c1cffb7 100644 --- a/primitives/runtime-interface/proc-macro/src/runtime_interface/bare_function_interface.rs +++ b/primitives/runtime-interface/proc-macro/src/runtime_interface/bare_function_interface.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2019-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/primitives/runtime-interface/proc-macro/src/runtime_interface/host_function_interface.rs b/primitives/runtime-interface/proc-macro/src/runtime_interface/host_function_interface.rs index 280c13667..fb751c69b 100644 --- a/primitives/runtime-interface/proc-macro/src/runtime_interface/host_function_interface.rs +++ b/primitives/runtime-interface/proc-macro/src/runtime_interface/host_function_interface.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2019-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/primitives/runtime-interface/proc-macro/src/runtime_interface/mod.rs b/primitives/runtime-interface/proc-macro/src/runtime_interface/mod.rs index d14c1f67e..008d69b32 100644 --- a/primitives/runtime-interface/proc-macro/src/runtime_interface/mod.rs +++ b/primitives/runtime-interface/proc-macro/src/runtime_interface/mod.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2019-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/primitives/runtime-interface/proc-macro/src/runtime_interface/trait_decl_impl.rs b/primitives/runtime-interface/proc-macro/src/runtime_interface/trait_decl_impl.rs index 0ae0f5260..4a3a688e5 100644 --- a/primitives/runtime-interface/proc-macro/src/runtime_interface/trait_decl_impl.rs +++ b/primitives/runtime-interface/proc-macro/src/runtime_interface/trait_decl_impl.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2019-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/primitives/runtime-interface/proc-macro/src/utils.rs b/primitives/runtime-interface/proc-macro/src/utils.rs index 386eef153..07f9b5ce0 100644 --- a/primitives/runtime-interface/proc-macro/src/utils.rs +++ b/primitives/runtime-interface/proc-macro/src/utils.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2019-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/primitives/runtime-interface/src/host.rs b/primitives/runtime-interface/src/host.rs index 364924302..914e57553 100644 --- a/primitives/runtime-interface/src/host.rs +++ b/primitives/runtime-interface/src/host.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2019-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/primitives/runtime-interface/src/impls.rs b/primitives/runtime-interface/src/impls.rs index e311b01db..3530b6266 100644 --- a/primitives/runtime-interface/src/impls.rs +++ b/primitives/runtime-interface/src/impls.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2019-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/primitives/runtime-interface/src/lib.rs b/primitives/runtime-interface/src/lib.rs index 975d7158b..058801522 100644 --- a/primitives/runtime-interface/src/lib.rs +++ b/primitives/runtime-interface/src/lib.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2019-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/primitives/runtime-interface/src/pass_by.rs b/primitives/runtime-interface/src/pass_by.rs index ac6f0def9..8d145669a 100644 --- a/primitives/runtime-interface/src/pass_by.rs +++ b/primitives/runtime-interface/src/pass_by.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2019-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/primitives/runtime-interface/src/util.rs b/primitives/runtime-interface/src/util.rs index fe8afe995..8db32271a 100644 --- a/primitives/runtime-interface/src/util.rs +++ b/primitives/runtime-interface/src/util.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2019-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/primitives/runtime-interface/src/wasm.rs b/primitives/runtime-interface/src/wasm.rs index 4ed27687a..91205addf 100644 --- a/primitives/runtime-interface/src/wasm.rs +++ b/primitives/runtime-interface/src/wasm.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2019-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/primitives/runtime-interface/test-wasm-deprecated/build.rs b/primitives/runtime-interface/test-wasm-deprecated/build.rs index 70f84bc00..b7676a70d 100644 --- a/primitives/runtime-interface/test-wasm-deprecated/build.rs +++ b/primitives/runtime-interface/test-wasm-deprecated/build.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2019-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/primitives/runtime-interface/test-wasm-deprecated/src/lib.rs b/primitives/runtime-interface/test-wasm-deprecated/src/lib.rs index 512d52214..2f42e6050 100644 --- a/primitives/runtime-interface/test-wasm-deprecated/src/lib.rs +++ b/primitives/runtime-interface/test-wasm-deprecated/src/lib.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2020-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/primitives/runtime-interface/test-wasm/build.rs b/primitives/runtime-interface/test-wasm/build.rs index 70f84bc00..b7676a70d 100644 --- a/primitives/runtime-interface/test-wasm/build.rs +++ b/primitives/runtime-interface/test-wasm/build.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2019-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/primitives/runtime-interface/test-wasm/src/lib.rs b/primitives/runtime-interface/test-wasm/src/lib.rs index 3720735ac..cf1ff3bca 100644 --- a/primitives/runtime-interface/test-wasm/src/lib.rs +++ b/primitives/runtime-interface/test-wasm/src/lib.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2019-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/primitives/runtime-interface/test/src/lib.rs b/primitives/runtime-interface/test/src/lib.rs index d1db3e064..d691d4846 100644 --- a/primitives/runtime-interface/test/src/lib.rs +++ b/primitives/runtime-interface/test/src/lib.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2019-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/primitives/runtime-interface/tests/ui.rs b/primitives/runtime-interface/tests/ui.rs index f3d6aa59a..821d0b73f 100644 --- a/primitives/runtime-interface/tests/ui.rs +++ b/primitives/runtime-interface/tests/ui.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2019-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/primitives/runtime/src/curve.rs b/primitives/runtime/src/curve.rs index c040b7cf5..7cea92293 100644 --- a/primitives/runtime/src/curve.rs +++ b/primitives/runtime/src/curve.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2019-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/primitives/runtime/src/generic/block.rs b/primitives/runtime/src/generic/block.rs index b03fab290..85d0b7ca1 100644 --- a/primitives/runtime/src/generic/block.rs +++ b/primitives/runtime/src/generic/block.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2017-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/primitives/runtime/src/generic/checked_extrinsic.rs b/primitives/runtime/src/generic/checked_extrinsic.rs index fd7745c60..4b0e017f4 100644 --- a/primitives/runtime/src/generic/checked_extrinsic.rs +++ b/primitives/runtime/src/generic/checked_extrinsic.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2017-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/primitives/runtime/src/generic/digest.rs b/primitives/runtime/src/generic/digest.rs index 1d1173057..73741ba5d 100644 --- a/primitives/runtime/src/generic/digest.rs +++ b/primitives/runtime/src/generic/digest.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2017-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/primitives/runtime/src/generic/era.rs b/primitives/runtime/src/generic/era.rs index b26545fb8..79dea7258 100644 --- a/primitives/runtime/src/generic/era.rs +++ b/primitives/runtime/src/generic/era.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2017-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/primitives/runtime/src/generic/header.rs b/primitives/runtime/src/generic/header.rs index 04d09f6b1..e8b99efd4 100644 --- a/primitives/runtime/src/generic/header.rs +++ b/primitives/runtime/src/generic/header.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2017-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/primitives/runtime/src/generic/mod.rs b/primitives/runtime/src/generic/mod.rs index 049b0e162..d9eee7fee 100644 --- a/primitives/runtime/src/generic/mod.rs +++ b/primitives/runtime/src/generic/mod.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2017-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/primitives/runtime/src/generic/tests.rs b/primitives/runtime/src/generic/tests.rs index d0536a567..b63efeb52 100644 --- a/primitives/runtime/src/generic/tests.rs +++ b/primitives/runtime/src/generic/tests.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2017-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/primitives/runtime/src/generic/unchecked_extrinsic.rs b/primitives/runtime/src/generic/unchecked_extrinsic.rs index bab441116..d147e7b6c 100644 --- a/primitives/runtime/src/generic/unchecked_extrinsic.rs +++ b/primitives/runtime/src/generic/unchecked_extrinsic.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2017-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/primitives/runtime/src/legacy.rs b/primitives/runtime/src/legacy.rs index 7bc7c88a7..b134038a1 100644 --- a/primitives/runtime/src/legacy.rs +++ b/primitives/runtime/src/legacy.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/primitives/runtime/src/legacy/byte_sized_error.rs b/primitives/runtime/src/legacy/byte_sized_error.rs index 049abff69..b552d6af3 100644 --- a/primitives/runtime/src/legacy/byte_sized_error.rs +++ b/primitives/runtime/src/legacy/byte_sized_error.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/primitives/runtime/src/lib.rs b/primitives/runtime/src/lib.rs index 8a32d0b9b..dc03e074f 100644 --- a/primitives/runtime/src/lib.rs +++ b/primitives/runtime/src/lib.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2017-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/primitives/runtime/src/multiaddress.rs b/primitives/runtime/src/multiaddress.rs index b2d46fb10..89b0a3bcf 100644 --- a/primitives/runtime/src/multiaddress.rs +++ b/primitives/runtime/src/multiaddress.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2017-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/primitives/runtime/src/offchain/http.rs b/primitives/runtime/src/offchain/http.rs index dede4db5d..0229203c1 100644 --- a/primitives/runtime/src/offchain/http.rs +++ b/primitives/runtime/src/offchain/http.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2019-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/primitives/runtime/src/offchain/mod.rs b/primitives/runtime/src/offchain/mod.rs index bbff693de..07abef7c4 100644 --- a/primitives/runtime/src/offchain/mod.rs +++ b/primitives/runtime/src/offchain/mod.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2019-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/primitives/runtime/src/offchain/storage.rs b/primitives/runtime/src/offchain/storage.rs index 38ad268e5..23ab7433e 100644 --- a/primitives/runtime/src/offchain/storage.rs +++ b/primitives/runtime/src/offchain/storage.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2020-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/primitives/runtime/src/offchain/storage_lock.rs b/primitives/runtime/src/offchain/storage_lock.rs index 47325743b..1b7959784 100644 --- a/primitives/runtime/src/offchain/storage_lock.rs +++ b/primitives/runtime/src/offchain/storage_lock.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2019-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/primitives/runtime/src/runtime_logger.rs b/primitives/runtime/src/runtime_logger.rs index fa866f834..63e96a52a 100644 --- a/primitives/runtime/src/runtime_logger.rs +++ b/primitives/runtime/src/runtime_logger.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2021-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/primitives/runtime/src/runtime_string.rs b/primitives/runtime/src/runtime_string.rs index 762af0acd..f8f183ec7 100644 --- a/primitives/runtime/src/runtime_string.rs +++ b/primitives/runtime/src/runtime_string.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2020-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/primitives/runtime/src/testing.rs b/primitives/runtime/src/testing.rs index 81762b3fc..7e2ee5403 100644 --- a/primitives/runtime/src/testing.rs +++ b/primitives/runtime/src/testing.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2017-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/primitives/runtime/src/traits.rs b/primitives/runtime/src/traits.rs index 6eb19683b..e853cad61 100644 --- a/primitives/runtime/src/traits.rs +++ b/primitives/runtime/src/traits.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2017-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/primitives/runtime/src/transaction_validity.rs b/primitives/runtime/src/transaction_validity.rs index f47459f3e..072609c66 100644 --- a/primitives/runtime/src/transaction_validity.rs +++ b/primitives/runtime/src/transaction_validity.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2017-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/primitives/serializer/src/lib.rs b/primitives/serializer/src/lib.rs index d1e364a13..3d4270714 100644 --- a/primitives/serializer/src/lib.rs +++ b/primitives/serializer/src/lib.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2017-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/primitives/session/src/lib.rs b/primitives/session/src/lib.rs index 9688547db..642aa2a21 100644 --- a/primitives/session/src/lib.rs +++ b/primitives/session/src/lib.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2019-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/primitives/staking/src/lib.rs b/primitives/staking/src/lib.rs index 9eb4a4890..a8d8e6a60 100644 --- a/primitives/staking/src/lib.rs +++ b/primitives/staking/src/lib.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2019-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/primitives/staking/src/offence.rs b/primitives/staking/src/offence.rs index f6517b9e9..dec975668 100644 --- a/primitives/staking/src/offence.rs +++ b/primitives/staking/src/offence.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2019-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/primitives/state-machine/src/backend.rs b/primitives/state-machine/src/backend.rs index 791183c4d..eeb264aed 100644 --- a/primitives/state-machine/src/backend.rs +++ b/primitives/state-machine/src/backend.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2017-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/primitives/state-machine/src/basic.rs b/primitives/state-machine/src/basic.rs index fdc50e3f8..a7adbc8a0 100644 --- a/primitives/state-machine/src/basic.rs +++ b/primitives/state-machine/src/basic.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2017-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/primitives/state-machine/src/error.rs b/primitives/state-machine/src/error.rs index a12b3eae7..4e8e02a26 100644 --- a/primitives/state-machine/src/error.rs +++ b/primitives/state-machine/src/error.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2019-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/primitives/state-machine/src/ext.rs b/primitives/state-machine/src/ext.rs index 1db0ec517..548bab56a 100644 --- a/primitives/state-machine/src/ext.rs +++ b/primitives/state-machine/src/ext.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2017-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/primitives/state-machine/src/in_memory_backend.rs b/primitives/state-machine/src/in_memory_backend.rs index 06714fb41..2c3ed7441 100644 --- a/primitives/state-machine/src/in_memory_backend.rs +++ b/primitives/state-machine/src/in_memory_backend.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2017-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/primitives/state-machine/src/lib.rs b/primitives/state-machine/src/lib.rs index 188fb2f4b..94fede96f 100644 --- a/primitives/state-machine/src/lib.rs +++ b/primitives/state-machine/src/lib.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2017-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/primitives/state-machine/src/overlayed_changes/changeset.rs b/primitives/state-machine/src/overlayed_changes/changeset.rs index e5dad7157..8f2d02fd6 100644 --- a/primitives/state-machine/src/overlayed_changes/changeset.rs +++ b/primitives/state-machine/src/overlayed_changes/changeset.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2020-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/primitives/state-machine/src/overlayed_changes/mod.rs b/primitives/state-machine/src/overlayed_changes/mod.rs index 9eb26d52f..b32df635b 100644 --- a/primitives/state-machine/src/overlayed_changes/mod.rs +++ b/primitives/state-machine/src/overlayed_changes/mod.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2017-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/primitives/state-machine/src/overlayed_changes/offchain.rs b/primitives/state-machine/src/overlayed_changes/offchain.rs index a9643c265..66e7ab586 100644 --- a/primitives/state-machine/src/overlayed_changes/offchain.rs +++ b/primitives/state-machine/src/overlayed_changes/offchain.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2019-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/primitives/state-machine/src/read_only.rs b/primitives/state-machine/src/read_only.rs index e58fb760f..2056bf986 100644 --- a/primitives/state-machine/src/read_only.rs +++ b/primitives/state-machine/src/read_only.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2020-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/primitives/state-machine/src/stats.rs b/primitives/state-machine/src/stats.rs index 863ea5cfe..7c5510961 100644 --- a/primitives/state-machine/src/stats.rs +++ b/primitives/state-machine/src/stats.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2017-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/primitives/state-machine/src/testing.rs b/primitives/state-machine/src/testing.rs index e94d34b55..23e0ed2b5 100644 --- a/primitives/state-machine/src/testing.rs +++ b/primitives/state-machine/src/testing.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2017-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/primitives/state-machine/src/trie_backend.rs b/primitives/state-machine/src/trie_backend.rs index ff5cce24c..a42976fb5 100644 --- a/primitives/state-machine/src/trie_backend.rs +++ b/primitives/state-machine/src/trie_backend.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2017-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/primitives/state-machine/src/trie_backend_essence.rs b/primitives/state-machine/src/trie_backend_essence.rs index cdd1bb0bb..d92caeb84 100644 --- a/primitives/state-machine/src/trie_backend_essence.rs +++ b/primitives/state-machine/src/trie_backend_essence.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2017-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/primitives/std/src/lib.rs b/primitives/std/src/lib.rs index 6653c3d7e..c5873b369 100644 --- a/primitives/std/src/lib.rs +++ b/primitives/std/src/lib.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2017-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/primitives/std/with_std.rs b/primitives/std/with_std.rs index b5fa3f85e..b838de07a 100644 --- a/primitives/std/with_std.rs +++ b/primitives/std/with_std.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2017-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/primitives/std/without_std.rs b/primitives/std/without_std.rs index 7a6d5851d..80baeae44 100755 --- a/primitives/std/without_std.rs +++ b/primitives/std/without_std.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2017-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/primitives/storage/src/lib.rs b/primitives/storage/src/lib.rs index 68d7b9063..77aa260c9 100644 --- a/primitives/storage/src/lib.rs +++ b/primitives/storage/src/lib.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2017-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/primitives/test-primitives/src/lib.rs b/primitives/test-primitives/src/lib.rs index 9779fe239..3a5f3dac4 100644 --- a/primitives/test-primitives/src/lib.rs +++ b/primitives/test-primitives/src/lib.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2017-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/primitives/timestamp/src/lib.rs b/primitives/timestamp/src/lib.rs index 0ec079816..eeec73efb 100644 --- a/primitives/timestamp/src/lib.rs +++ b/primitives/timestamp/src/lib.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2019-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/primitives/tracing/src/lib.rs b/primitives/tracing/src/lib.rs index 1efae226a..cc6518368 100644 --- a/primitives/tracing/src/lib.rs +++ b/primitives/tracing/src/lib.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2020-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/primitives/tracing/src/types.rs b/primitives/tracing/src/types.rs index e7d5abfb2..003787f31 100644 --- a/primitives/tracing/src/types.rs +++ b/primitives/tracing/src/types.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2020-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/primitives/transaction-pool/src/lib.rs b/primitives/transaction-pool/src/lib.rs index 143958f06..431f429e2 100644 --- a/primitives/transaction-pool/src/lib.rs +++ b/primitives/transaction-pool/src/lib.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2019-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/primitives/transaction-pool/src/runtime_api.rs b/primitives/transaction-pool/src/runtime_api.rs index 87a0c82e9..5d321ede4 100644 --- a/primitives/transaction-pool/src/runtime_api.rs +++ b/primitives/transaction-pool/src/runtime_api.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2019-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/primitives/transaction-storage-proof/src/lib.rs b/primitives/transaction-storage-proof/src/lib.rs index 43928c83f..9d540ae68 100644 --- a/primitives/transaction-storage-proof/src/lib.rs +++ b/primitives/transaction-storage-proof/src/lib.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2019-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/primitives/trie/benches/bench.rs b/primitives/trie/benches/bench.rs index f1670b0c2..35aa0b808 100644 --- a/primitives/trie/benches/bench.rs +++ b/primitives/trie/benches/bench.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2015-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/primitives/trie/src/cache/mod.rs b/primitives/trie/src/cache/mod.rs index 3c1e5b8d0..e55a56971 100644 --- a/primitives/trie/src/cache/mod.rs +++ b/primitives/trie/src/cache/mod.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/primitives/trie/src/cache/shared_cache.rs b/primitives/trie/src/cache/shared_cache.rs index 8c60d5043..28b3274fd 100644 --- a/primitives/trie/src/cache/shared_cache.rs +++ b/primitives/trie/src/cache/shared_cache.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/primitives/trie/src/error.rs b/primitives/trie/src/error.rs index a781d408e..17be556d3 100644 --- a/primitives/trie/src/error.rs +++ b/primitives/trie/src/error.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2015-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/primitives/trie/src/lib.rs b/primitives/trie/src/lib.rs index 01650e9a3..34517d6aa 100644 --- a/primitives/trie/src/lib.rs +++ b/primitives/trie/src/lib.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2015-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/primitives/trie/src/node_codec.rs b/primitives/trie/src/node_codec.rs index f632320dd..46acde77c 100644 --- a/primitives/trie/src/node_codec.rs +++ b/primitives/trie/src/node_codec.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2015-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/primitives/trie/src/node_header.rs b/primitives/trie/src/node_header.rs index f3544be65..c118ee07b 100644 --- a/primitives/trie/src/node_header.rs +++ b/primitives/trie/src/node_header.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2015-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/primitives/trie/src/recorder.rs b/primitives/trie/src/recorder.rs index bc67cfc28..d496408b2 100644 --- a/primitives/trie/src/recorder.rs +++ b/primitives/trie/src/recorder.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2021 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/primitives/trie/src/storage_proof.rs b/primitives/trie/src/storage_proof.rs index 5351e8de6..6c871d73b 100644 --- a/primitives/trie/src/storage_proof.rs +++ b/primitives/trie/src/storage_proof.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2020-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/primitives/trie/src/trie_codec.rs b/primitives/trie/src/trie_codec.rs index d5ae9a43f..f29e009c4 100644 --- a/primitives/trie/src/trie_codec.rs +++ b/primitives/trie/src/trie_codec.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2021-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/primitives/trie/src/trie_stream.rs b/primitives/trie/src/trie_stream.rs index 435e6a986..f57b80f97 100644 --- a/primitives/trie/src/trie_stream.rs +++ b/primitives/trie/src/trie_stream.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2015-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/primitives/version/proc-macro/src/decl_runtime_version.rs b/primitives/version/proc-macro/src/decl_runtime_version.rs index 9a25adfa5..7ca2d9b71 100644 --- a/primitives/version/proc-macro/src/decl_runtime_version.rs +++ b/primitives/version/proc-macro/src/decl_runtime_version.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2021-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/primitives/version/proc-macro/src/lib.rs b/primitives/version/proc-macro/src/lib.rs index 8be18b158..4f9179f31 100644 --- a/primitives/version/proc-macro/src/lib.rs +++ b/primitives/version/proc-macro/src/lib.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2021-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/primitives/version/src/embed.rs b/primitives/version/src/embed.rs index c71849238..096a7009a 100644 --- a/primitives/version/src/embed.rs +++ b/primitives/version/src/embed.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2021-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/primitives/version/src/lib.rs b/primitives/version/src/lib.rs index 37bb15afb..214606acc 100644 --- a/primitives/version/src/lib.rs +++ b/primitives/version/src/lib.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2017-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/primitives/wasm-interface/src/lib.rs b/primitives/wasm-interface/src/lib.rs index c06eb43c7..b096d236c 100644 --- a/primitives/wasm-interface/src/lib.rs +++ b/primitives/wasm-interface/src/lib.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2019-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/primitives/wasm-interface/src/wasmi_impl.rs b/primitives/wasm-interface/src/wasmi_impl.rs index 977b4fc60..7394e3455 100644 --- a/primitives/wasm-interface/src/wasmi_impl.rs +++ b/primitives/wasm-interface/src/wasmi_impl.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2019-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/primitives/weights/src/lib.rs b/primitives/weights/src/lib.rs index 59852815d..acfcae1df 100644 --- a/primitives/weights/src/lib.rs +++ b/primitives/weights/src/lib.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2019-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/primitives/weights/src/weight_meter.rs b/primitives/weights/src/weight_meter.rs index 17c5da150..ab7b6c63e 100644 --- a/primitives/weights/src/weight_meter.rs +++ b/primitives/weights/src/weight_meter.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/primitives/weights/src/weight_v2.rs b/primitives/weights/src/weight_v2.rs index 52e148f30..f6e6164b5 100644 --- a/primitives/weights/src/weight_v2.rs +++ b/primitives/weights/src/weight_v2.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/scripts/run_all_benchmarks.sh b/scripts/run_all_benchmarks.sh index f122be577..b632cb5c1 100755 --- a/scripts/run_all_benchmarks.sh +++ b/scripts/run_all_benchmarks.sh @@ -1,7 +1,7 @@ #!/usr/bin/env bash # This file is part of Substrate. -# Copyright (C) 2022 Parity Technologies (UK) Ltd. +# Copyright (C) Parity Technologies (UK) Ltd. # SPDX-License-Identifier: Apache-2.0 # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/test-utils/client/src/client_ext.rs b/test-utils/client/src/client_ext.rs index 881c50d43..c73ea76a3 100644 --- a/test-utils/client/src/client_ext.rs +++ b/test-utils/client/src/client_ext.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2018-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/test-utils/client/src/lib.rs b/test-utils/client/src/lib.rs index ff744b80c..a27178792 100644 --- a/test-utils/client/src/lib.rs +++ b/test-utils/client/src/lib.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2019-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/test-utils/derive/src/lib.rs b/test-utils/derive/src/lib.rs index 06b7d2463..0291d825e 100644 --- a/test-utils/derive/src/lib.rs +++ b/test-utils/derive/src/lib.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2020-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 // This program is free software: you can redistribute it and/or modify diff --git a/test-utils/runtime/build.rs b/test-utils/runtime/build.rs index 870d8ac43..dd79ce2c5 100644 --- a/test-utils/runtime/build.rs +++ b/test-utils/runtime/build.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2019-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/test-utils/runtime/client/src/block_builder_ext.rs b/test-utils/runtime/client/src/block_builder_ext.rs index 66b0b4c3b..3c5e1122f 100644 --- a/test-utils/runtime/client/src/block_builder_ext.rs +++ b/test-utils/runtime/client/src/block_builder_ext.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2018-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/test-utils/runtime/client/src/lib.rs b/test-utils/runtime/client/src/lib.rs index 99d4e1163..97ee525f6 100644 --- a/test-utils/runtime/client/src/lib.rs +++ b/test-utils/runtime/client/src/lib.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2018-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/test-utils/runtime/client/src/trait_tests.rs b/test-utils/runtime/client/src/trait_tests.rs index 7ec56635e..5fce7a286 100644 --- a/test-utils/runtime/client/src/trait_tests.rs +++ b/test-utils/runtime/client/src/trait_tests.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2018-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/test-utils/runtime/src/genesismap.rs b/test-utils/runtime/src/genesismap.rs index 42706730e..77fdf59ea 100644 --- a/test-utils/runtime/src/genesismap.rs +++ b/test-utils/runtime/src/genesismap.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2017-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/test-utils/runtime/src/lib.rs b/test-utils/runtime/src/lib.rs index b979a961a..1fa3c7a00 100644 --- a/test-utils/runtime/src/lib.rs +++ b/test-utils/runtime/src/lib.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2017-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/test-utils/runtime/src/system.rs b/test-utils/runtime/src/system.rs index 6e33d5c25..76ddf11a8 100644 --- a/test-utils/runtime/src/system.rs +++ b/test-utils/runtime/src/system.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2017-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/test-utils/runtime/transaction-pool/src/lib.rs b/test-utils/runtime/transaction-pool/src/lib.rs index 8a648cd00..8a39b8295 100644 --- a/test-utils/runtime/transaction-pool/src/lib.rs +++ b/test-utils/runtime/transaction-pool/src/lib.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2020-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/test-utils/src/lib.rs b/test-utils/src/lib.rs index 643985940..1ad702893 100644 --- a/test-utils/src/lib.rs +++ b/test-utils/src/lib.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2017-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/test-utils/test-crate/src/main.rs b/test-utils/test-crate/src/main.rs index 4696e7177..cab4cc6e9 100644 --- a/test-utils/test-crate/src/main.rs +++ b/test-utils/test-crate/src/main.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2020-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 // This program is free software: you can redistribute it and/or modify diff --git a/test-utils/tests/basic.rs b/test-utils/tests/basic.rs index 4daca836c..e8c46b1e8 100644 --- a/test-utils/tests/basic.rs +++ b/test-utils/tests/basic.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2020-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 // This program is free software: you can redistribute it and/or modify diff --git a/test-utils/tests/ui.rs b/test-utils/tests/ui.rs index be3b9c111..baf822bb8 100644 --- a/test-utils/tests/ui.rs +++ b/test-utils/tests/ui.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2020-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 // This program is free software: you can redistribute it and/or modify diff --git a/test-utils/tests/ui/too-many-func-parameters.rs b/test-utils/tests/ui/too-many-func-parameters.rs index 51059f37a..0eece5f9e 100644 --- a/test-utils/tests/ui/too-many-func-parameters.rs +++ b/test-utils/tests/ui/too-many-func-parameters.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2020-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 // This program is free software: you can redistribute it and/or modify diff --git a/utils/binary-merkle-tree/src/lib.rs b/utils/binary-merkle-tree/src/lib.rs index 42f4e8a5b..43c07cb60 100644 --- a/utils/binary-merkle-tree/src/lib.rs +++ b/utils/binary-merkle-tree/src/lib.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2021-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/utils/build-script-utils/src/git.rs b/utils/build-script-utils/src/git.rs index db9a4b291..057ee0af1 100644 --- a/utils/build-script-utils/src/git.rs +++ b/utils/build-script-utils/src/git.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2019-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/utils/build-script-utils/src/lib.rs b/utils/build-script-utils/src/lib.rs index 7e69f2ac8..7bab35c98 100644 --- a/utils/build-script-utils/src/lib.rs +++ b/utils/build-script-utils/src/lib.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2019-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/utils/build-script-utils/src/version.rs b/utils/build-script-utils/src/version.rs index 19b507ba2..4ee5376ed 100644 --- a/utils/build-script-utils/src/version.rs +++ b/utils/build-script-utils/src/version.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2019-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/utils/fork-tree/src/lib.rs b/utils/fork-tree/src/lib.rs index bbd0daca4..cd175166b 100644 --- a/utils/fork-tree/src/lib.rs +++ b/utils/fork-tree/src/lib.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2019-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/utils/frame/benchmarking-cli/build.rs b/utils/frame/benchmarking-cli/build.rs index 434780415..1545d1e0c 100644 --- a/utils/frame/benchmarking-cli/build.rs +++ b/utils/frame/benchmarking-cli/build.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/utils/frame/benchmarking-cli/src/block/bench.rs b/utils/frame/benchmarking-cli/src/block/bench.rs index 2ceac787f..960056991 100644 --- a/utils/frame/benchmarking-cli/src/block/bench.rs +++ b/utils/frame/benchmarking-cli/src/block/bench.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/utils/frame/benchmarking-cli/src/block/cmd.rs b/utils/frame/benchmarking-cli/src/block/cmd.rs index 8bac04110..0192372fa 100644 --- a/utils/frame/benchmarking-cli/src/block/cmd.rs +++ b/utils/frame/benchmarking-cli/src/block/cmd.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/utils/frame/benchmarking-cli/src/block/mod.rs b/utils/frame/benchmarking-cli/src/block/mod.rs index 97fdb6ad2..9fbd4ea70 100644 --- a/utils/frame/benchmarking-cli/src/block/mod.rs +++ b/utils/frame/benchmarking-cli/src/block/mod.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/utils/frame/benchmarking-cli/src/extrinsic/bench.rs b/utils/frame/benchmarking-cli/src/extrinsic/bench.rs index 948f64837..facde14ad 100644 --- a/utils/frame/benchmarking-cli/src/extrinsic/bench.rs +++ b/utils/frame/benchmarking-cli/src/extrinsic/bench.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/utils/frame/benchmarking-cli/src/extrinsic/cmd.rs b/utils/frame/benchmarking-cli/src/extrinsic/cmd.rs index dc8aa8b7c..1001958fe 100644 --- a/utils/frame/benchmarking-cli/src/extrinsic/cmd.rs +++ b/utils/frame/benchmarking-cli/src/extrinsic/cmd.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/utils/frame/benchmarking-cli/src/extrinsic/extrinsic_factory.rs b/utils/frame/benchmarking-cli/src/extrinsic/extrinsic_factory.rs index 7e1a22ccd..9a209e9c5 100644 --- a/utils/frame/benchmarking-cli/src/extrinsic/extrinsic_factory.rs +++ b/utils/frame/benchmarking-cli/src/extrinsic/extrinsic_factory.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/utils/frame/benchmarking-cli/src/extrinsic/mod.rs b/utils/frame/benchmarking-cli/src/extrinsic/mod.rs index 12a09c3b1..4e8a66c02 100644 --- a/utils/frame/benchmarking-cli/src/extrinsic/mod.rs +++ b/utils/frame/benchmarking-cli/src/extrinsic/mod.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/utils/frame/benchmarking-cli/src/lib.rs b/utils/frame/benchmarking-cli/src/lib.rs index 5723a8038..0ef2c299d 100644 --- a/utils/frame/benchmarking-cli/src/lib.rs +++ b/utils/frame/benchmarking-cli/src/lib.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2020-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/utils/frame/benchmarking-cli/src/machine/hardware.rs b/utils/frame/benchmarking-cli/src/machine/hardware.rs index 318c193d7..b3cbc0053 100644 --- a/utils/frame/benchmarking-cli/src/machine/hardware.rs +++ b/utils/frame/benchmarking-cli/src/machine/hardware.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/utils/frame/benchmarking-cli/src/machine/mod.rs b/utils/frame/benchmarking-cli/src/machine/mod.rs index bcffef255..fb9f14c9a 100644 --- a/utils/frame/benchmarking-cli/src/machine/mod.rs +++ b/utils/frame/benchmarking-cli/src/machine/mod.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/utils/frame/benchmarking-cli/src/overhead/cmd.rs b/utils/frame/benchmarking-cli/src/overhead/cmd.rs index bca63f7ca..70e64cc2b 100644 --- a/utils/frame/benchmarking-cli/src/overhead/cmd.rs +++ b/utils/frame/benchmarking-cli/src/overhead/cmd.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/utils/frame/benchmarking-cli/src/overhead/mod.rs b/utils/frame/benchmarking-cli/src/overhead/mod.rs index fc3db912f..00cde66fd 100644 --- a/utils/frame/benchmarking-cli/src/overhead/mod.rs +++ b/utils/frame/benchmarking-cli/src/overhead/mod.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/utils/frame/benchmarking-cli/src/overhead/template.rs b/utils/frame/benchmarking-cli/src/overhead/template.rs index ceed34d19..7c8c92b07 100644 --- a/utils/frame/benchmarking-cli/src/overhead/template.rs +++ b/utils/frame/benchmarking-cli/src/overhead/template.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/utils/frame/benchmarking-cli/src/pallet/command.rs b/utils/frame/benchmarking-cli/src/pallet/command.rs index ec1be2761..3f0e9298c 100644 --- a/utils/frame/benchmarking-cli/src/pallet/command.rs +++ b/utils/frame/benchmarking-cli/src/pallet/command.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2020-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/utils/frame/benchmarking-cli/src/pallet/mod.rs b/utils/frame/benchmarking-cli/src/pallet/mod.rs index 1fcf0cc11..9ae984674 100644 --- a/utils/frame/benchmarking-cli/src/pallet/mod.rs +++ b/utils/frame/benchmarking-cli/src/pallet/mod.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2020-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/utils/frame/benchmarking-cli/src/pallet/writer.rs b/utils/frame/benchmarking-cli/src/pallet/writer.rs index 9359b2725..a609f9e38 100644 --- a/utils/frame/benchmarking-cli/src/pallet/writer.rs +++ b/utils/frame/benchmarking-cli/src/pallet/writer.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2020-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/utils/frame/benchmarking-cli/src/shared/mod.rs b/utils/frame/benchmarking-cli/src/shared/mod.rs index ea5415f33..f8aa49b86 100644 --- a/utils/frame/benchmarking-cli/src/shared/mod.rs +++ b/utils/frame/benchmarking-cli/src/shared/mod.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/utils/frame/benchmarking-cli/src/shared/record.rs b/utils/frame/benchmarking-cli/src/shared/record.rs index 79ab37f65..d82caa172 100644 --- a/utils/frame/benchmarking-cli/src/shared/record.rs +++ b/utils/frame/benchmarking-cli/src/shared/record.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/utils/frame/benchmarking-cli/src/shared/stats.rs b/utils/frame/benchmarking-cli/src/shared/stats.rs index ffae4a177..5c724456d 100644 --- a/utils/frame/benchmarking-cli/src/shared/stats.rs +++ b/utils/frame/benchmarking-cli/src/shared/stats.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/utils/frame/benchmarking-cli/src/shared/weight_params.rs b/utils/frame/benchmarking-cli/src/shared/weight_params.rs index 030bbfa00..10230ba30 100644 --- a/utils/frame/benchmarking-cli/src/shared/weight_params.rs +++ b/utils/frame/benchmarking-cli/src/shared/weight_params.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/utils/frame/benchmarking-cli/src/storage/cmd.rs b/utils/frame/benchmarking-cli/src/storage/cmd.rs index ce2d52e57..10ea8caae 100644 --- a/utils/frame/benchmarking-cli/src/storage/cmd.rs +++ b/utils/frame/benchmarking-cli/src/storage/cmd.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/utils/frame/benchmarking-cli/src/storage/mod.rs b/utils/frame/benchmarking-cli/src/storage/mod.rs index 0c722fdd4..188cc5e3d 100644 --- a/utils/frame/benchmarking-cli/src/storage/mod.rs +++ b/utils/frame/benchmarking-cli/src/storage/mod.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/utils/frame/benchmarking-cli/src/storage/read.rs b/utils/frame/benchmarking-cli/src/storage/read.rs index 20c41e4a5..c2aef1c79 100644 --- a/utils/frame/benchmarking-cli/src/storage/read.rs +++ b/utils/frame/benchmarking-cli/src/storage/read.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/utils/frame/benchmarking-cli/src/storage/template.rs b/utils/frame/benchmarking-cli/src/storage/template.rs index ebc415ccb..43aea75b4 100644 --- a/utils/frame/benchmarking-cli/src/storage/template.rs +++ b/utils/frame/benchmarking-cli/src/storage/template.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/utils/frame/benchmarking-cli/src/storage/write.rs b/utils/frame/benchmarking-cli/src/storage/write.rs index 1917bc5f8..66d5a30ea 100644 --- a/utils/frame/benchmarking-cli/src/storage/write.rs +++ b/utils/frame/benchmarking-cli/src/storage/write.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/utils/frame/frame-utilities-cli/src/lib.rs b/utils/frame/frame-utilities-cli/src/lib.rs index 9a18d3926..97129e36f 100644 --- a/utils/frame/frame-utilities-cli/src/lib.rs +++ b/utils/frame/frame-utilities-cli/src/lib.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2020-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/utils/frame/frame-utilities-cli/src/pallet_id.rs b/utils/frame/frame-utilities-cli/src/pallet_id.rs index 2a80e3a3d..abc0cdb3f 100644 --- a/utils/frame/frame-utilities-cli/src/pallet_id.rs +++ b/utils/frame/frame-utilities-cli/src/pallet_id.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2020-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/utils/frame/generate-bags/node-runtime/src/main.rs b/utils/frame/generate-bags/node-runtime/src/main.rs index 27e51b205..b8d233814 100644 --- a/utils/frame/generate-bags/node-runtime/src/main.rs +++ b/utils/frame/generate-bags/node-runtime/src/main.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2021-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/utils/frame/generate-bags/src/lib.rs b/utils/frame/generate-bags/src/lib.rs index 23da131a6..509ae5530 100644 --- a/utils/frame/generate-bags/src/lib.rs +++ b/utils/frame/generate-bags/src/lib.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2021-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/utils/frame/remote-externalities/src/lib.rs b/utils/frame/remote-externalities/src/lib.rs index fb63b4275..ee3424088 100644 --- a/utils/frame/remote-externalities/src/lib.rs +++ b/utils/frame/remote-externalities/src/lib.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2020-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/utils/frame/rpc/client/src/lib.rs b/utils/frame/rpc/client/src/lib.rs index a6f73ba67..a6a667bef 100644 --- a/utils/frame/rpc/client/src/lib.rs +++ b/utils/frame/rpc/client/src/lib.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/utils/frame/rpc/state-trie-migration-rpc/src/lib.rs b/utils/frame/rpc/state-trie-migration-rpc/src/lib.rs index 2140ee884..d1175fdea 100644 --- a/utils/frame/rpc/state-trie-migration-rpc/src/lib.rs +++ b/utils/frame/rpc/state-trie-migration-rpc/src/lib.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2019-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/utils/frame/rpc/support/src/lib.rs b/utils/frame/rpc/support/src/lib.rs index a13d4f707..c0ec8befc 100644 --- a/utils/frame/rpc/support/src/lib.rs +++ b/utils/frame/rpc/support/src/lib.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2019-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/utils/frame/rpc/system/src/lib.rs b/utils/frame/rpc/system/src/lib.rs index 965a7b44d..46d8472b2 100644 --- a/utils/frame/rpc/system/src/lib.rs +++ b/utils/frame/rpc/system/src/lib.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2019-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/utils/frame/try-runtime/cli/src/block_building_info.rs b/utils/frame/try-runtime/cli/src/block_building_info.rs index b68bf081f..db24d06ef 100644 --- a/utils/frame/try-runtime/cli/src/block_building_info.rs +++ b/utils/frame/try-runtime/cli/src/block_building_info.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2021-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/utils/frame/try-runtime/cli/src/commands/create_snapshot.rs b/utils/frame/try-runtime/cli/src/commands/create_snapshot.rs index 8d6144f84..87855c1d6 100644 --- a/utils/frame/try-runtime/cli/src/commands/create_snapshot.rs +++ b/utils/frame/try-runtime/cli/src/commands/create_snapshot.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/utils/frame/try-runtime/cli/src/commands/execute_block.rs b/utils/frame/try-runtime/cli/src/commands/execute_block.rs index e054c8ab8..561bc57c7 100644 --- a/utils/frame/try-runtime/cli/src/commands/execute_block.rs +++ b/utils/frame/try-runtime/cli/src/commands/execute_block.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2021-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/utils/frame/try-runtime/cli/src/commands/fast_forward.rs b/utils/frame/try-runtime/cli/src/commands/fast_forward.rs index ae8f9a5cf..75c48c3c4 100644 --- a/utils/frame/try-runtime/cli/src/commands/fast_forward.rs +++ b/utils/frame/try-runtime/cli/src/commands/fast_forward.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2021-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/utils/frame/try-runtime/cli/src/commands/follow_chain.rs b/utils/frame/try-runtime/cli/src/commands/follow_chain.rs index d887757b1..2a67d269c 100644 --- a/utils/frame/try-runtime/cli/src/commands/follow_chain.rs +++ b/utils/frame/try-runtime/cli/src/commands/follow_chain.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2021-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/utils/frame/try-runtime/cli/src/commands/mod.rs b/utils/frame/try-runtime/cli/src/commands/mod.rs index 81deb2631..37902e676 100644 --- a/utils/frame/try-runtime/cli/src/commands/mod.rs +++ b/utils/frame/try-runtime/cli/src/commands/mod.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2021-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/utils/frame/try-runtime/cli/src/commands/offchain_worker.rs b/utils/frame/try-runtime/cli/src/commands/offchain_worker.rs index 985b04bfb..352d552a3 100644 --- a/utils/frame/try-runtime/cli/src/commands/offchain_worker.rs +++ b/utils/frame/try-runtime/cli/src/commands/offchain_worker.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2021-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/utils/frame/try-runtime/cli/src/commands/on_runtime_upgrade.rs b/utils/frame/try-runtime/cli/src/commands/on_runtime_upgrade.rs index 61b5847ed..c8582ea4d 100644 --- a/utils/frame/try-runtime/cli/src/commands/on_runtime_upgrade.rs +++ b/utils/frame/try-runtime/cli/src/commands/on_runtime_upgrade.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2021-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/utils/frame/try-runtime/cli/src/lib.rs b/utils/frame/try-runtime/cli/src/lib.rs index 1a2f32de5..04459ab59 100644 --- a/utils/frame/try-runtime/cli/src/lib.rs +++ b/utils/frame/try-runtime/cli/src/lib.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2021-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/utils/frame/try-runtime/cli/src/parse.rs b/utils/frame/try-runtime/cli/src/parse.rs index 257a99566..336a36baf 100644 --- a/utils/frame/try-runtime/cli/src/parse.rs +++ b/utils/frame/try-runtime/cli/src/parse.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2021-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/utils/prometheus/src/lib.rs b/utils/prometheus/src/lib.rs index 3ea9d45d4..581666635 100644 --- a/utils/prometheus/src/lib.rs +++ b/utils/prometheus/src/lib.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2019-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/utils/prometheus/src/sourced.rs b/utils/prometheus/src/sourced.rs index 9f52d1eff..8adaefa09 100644 --- a/utils/prometheus/src/sourced.rs +++ b/utils/prometheus/src/sourced.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2020-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/utils/wasm-builder/src/builder.rs b/utils/wasm-builder/src/builder.rs index 81a869396..72d32445e 100644 --- a/utils/wasm-builder/src/builder.rs +++ b/utils/wasm-builder/src/builder.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2019-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/utils/wasm-builder/src/lib.rs b/utils/wasm-builder/src/lib.rs index fc86a0617..659b95525 100644 --- a/utils/wasm-builder/src/lib.rs +++ b/utils/wasm-builder/src/lib.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2019-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/utils/wasm-builder/src/prerequisites.rs b/utils/wasm-builder/src/prerequisites.rs index fb04dc3c9..ca07a0292 100644 --- a/utils/wasm-builder/src/prerequisites.rs +++ b/utils/wasm-builder/src/prerequisites.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2019-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/utils/wasm-builder/src/wasm_project.rs b/utils/wasm-builder/src/wasm_project.rs index d17997360..a3038c4e9 100644 --- a/utils/wasm-builder/src/wasm_project.rs +++ b/utils/wasm-builder/src/wasm_project.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2019-2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); From 59fd2ac6b1e2909aa9d4bddcdf7d445a6bffcbe4 Mon Sep 17 00:00:00 2001 From: Dan Shields <35669742+NukeManDan@users.noreply.github.com> Date: Tue, 21 Feb 2023 15:24:23 -0700 Subject: [PATCH 148/558] Update Template README (#12741) * update README remove matrix links remove playground references (service reported as sunsetting) OSPL rule per Docs style guide * Apply suggestions from code review Co-authored-by: Sacha Lansky * Update README.md * Apply suggestions from code review Co-authored-by: lisa-parity <92225469+lisa-parity@users.noreply.github.com> * (fix) chmod +x script --------- Co-authored-by: Sacha Lansky Co-authored-by: lisa-parity <92225469+lisa-parity@users.noreply.github.com> Co-authored-by: parity-processbot <> --- bin/node-template/README.md | 219 +++++++++--------------- bin/node-template/scripts/docker_run.sh | 0 2 files changed, 85 insertions(+), 134 deletions(-) mode change 100644 => 100755 bin/node-template/scripts/docker_run.sh diff --git a/bin/node-template/README.md b/bin/node-template/README.md index 0f6fd9450..c42b58449 100644 --- a/bin/node-template/README.md +++ b/bin/node-template/README.md @@ -1,38 +1,22 @@ # Substrate Node Template -[![Try on playground](https://img.shields.io/badge/Playground-Node_Template-brightgreen?logo=Parity%20Substrate)](https://docs.substrate.io/playground/) [![Matrix](https://img.shields.io/matrix/substrate-technical:matrix.org)](https://matrix.to/#/#substrate-technical:matrix.org) +A fresh [Substrate](https://substrate.io/) node, ready for hacking :rocket: -A fresh FRAME-based [Substrate](https://www.substrate.io/) node, ready for hacking :rocket: +A standalone version of this template is available for each release of Polkadot in the [Substrate Developer Hub Parachain Template](https://github.com/substrate-developer-hub/substrate-parachain-template/) repository. +The parachain template is generated directly at each Polkadot release branch form the [Node Template in Substreate](https://github.com/paritytech/substrate/tree/master/bin/node-template) upstream -## Getting Started - -Follow the steps below to get started with the Node Template, or get it up and running right from -your browser in just a few clicks using -the [Substrate Playground](https://docs.substrate.io/playground/) :hammer_and_wrench: - -### Using Nix - -Install [nix](https://nixos.org/) and optionally [direnv](https://github.com/direnv/direnv) and -[lorri](https://github.com/nix-community/lorri) for a fully plug and play experience for setting up -the development environment. To get all the correct dependencies activate direnv `direnv allow` and -lorri `lorri shell`. - -### Rust Setup +It is usually best to to use the stand-alone version to start a new project. +All bugs, suggestions, and feature requests should be made upstream in the [Substrate](https://github.com/paritytech/substrate/tree/master/bin/node-template) repository. -First, complete the [basic Rust setup instructions](./docs/rust-setup.md). - -### Run +## Getting Started -Use Rust's native `cargo` command to build and launch the template node: - -```sh -cargo run --release -- --dev -``` +Depending on your operating system and Rust version, there might be additional packages required to compile this template. +Check the [Install](https://docs.substrate.io/install/) instructions for your platform for the most common dependencies. +Alternatively, you can use one of the [alternative installation](#alternatives-installations) options. ### Build -The `cargo run` command will perform an initial build. Use the following command to build the node -without launching it: +Use the following command to build the node without launching it: ```sh cargo build --release @@ -40,54 +24,49 @@ cargo build --release ### Embedded Docs -Once the project has been built, the following command can be used to explore all parameters and -subcommands: +After you build the project, you can use the following command to explore its parameters and subcommands: ```sh ./target/release/node-template -h ``` -## Run +You can generate and view the [Rust Docs](https://doc.rust-lang.org/cargo/commands/cargo-doc.html) for this template with this command: -The provided `cargo run` command will launch a temporary node and its state will be discarded after -you terminate the process. After the project has been built, there are other ways to launch the -node. +```sh +cargo +nightly doc --open +``` ### Single-Node Development Chain -This command will start the single-node development chain with non-persistent state: +The following command starts a single-node development chain that doesn't persist state: -```bash +```sh ./target/release/node-template --dev ``` -Purge the development chain's state: +To purge the development chain's state, run the following command: -```bash +```sh ./target/release/node-template purge-chain --dev ``` -Start the development chain with detailed logging: +To start the development chain with detailed logging, run the following command: -```bash +```sh RUST_BACKTRACE=1 ./target/release/node-template -ldebug --dev ``` -> Development chain means that the state of our chain will be in a tmp folder while the nodes are -> running. Also, **alice** account will be authority and sudo account as declared in the -> [genesis state](https://github.com/substrate-developer-hub/substrate-node-template/blob/main/node/src/chain_spec.rs#L49). -> At the same time the following accounts will be pre-funded: -> - Alice -> - Bob -> - Alice//stash -> - Bob//stash - -In case of being interested in maintaining the chain' state between runs a base path must be added -so the db can be stored in the provided folder instead of a temporal one. We could use this folder -to store different chain databases, as a different folder will be created per different chain that -is ran. The following commands shows how to use a newly created folder as our db base path. - -```bash +Development chains: + +- Maintain state in a `tmp` folder while the node is running. +- Use the **Alice** and **Bob** accounts as default validator authorities. +- Use the **Alice** account as the default `sudo` account. +- Are preconfigured with a genesis state (`/node/src/chain_spec.rs`) that includes several prefunded development accounts. + + +To persist chain state between runs, specify a base path by running a command similar to the following: + +```sh // Create a folder to use as the db base path $ mkdir my-chain-state @@ -103,23 +82,19 @@ $ ls ./my-chain-state/chains/dev db keystore network ``` +### Connect with Polkadot-JS Apps Front-End -### Connect with Polkadot-JS Apps Front-end - -Once the node template is running locally, you can connect it with **Polkadot-JS Apps** front-end -to interact with your chain. [Click -here](https://polkadot.js.org/apps/#/explorer?rpc=ws://localhost:9944) connecting the Apps to your -local node template. +After you start the node template locally, you can interact with it using the hosted version of the [Polkadot/Substrate Portal](https://polkadot.js.org/apps/#/explorer?rpc=ws://localhost:9944) front-end by connecting to the local node endpoint. +A hosted version is also available on [IPFS (redirect) here](https://dotapps.io/) or [IPNS (direct) here](ipns://dotapps.io/?rpc=ws%3A%2F%2F127.0.0.1%3A9944#/explorer). +You can also find the source code and instructions for hosting your own instance on the [polkadot-js/apps](https://github.com/polkadot-js/apps) repository. ### Multi-Node Local Testnet -If you want to see the multi-node consensus algorithm in action, refer to our -[Simulate a network tutorial](https://docs.substrate.io/tutorials/get-started/simulate-network/). +If you want to see the multi-node consensus algorithm in action, see [Simulate a network](https://docs.substrate.io/tutorials/get-started/simulate-network/). ## Template Structure -A Substrate project such as this consists of a number of components that are spread across a few -directories. +A Substrate project such as this consists of a number of components that are spread across a few directories. ### Node @@ -128,98 +103,74 @@ Substrate-based blockchain nodes expose a number of capabilities: - Networking: Substrate nodes use the [`libp2p`](https://libp2p.io/) networking stack to allow the nodes in the network to communicate with one another. -- Consensus: Blockchains must have a way to come to - [consensus](https://docs.substrate.io/main-docs/fundamentals/consensus/) on the state of the - network. Substrate makes it possible to supply custom consensus engines and also ships with - several consensus mechanisms that have been built on top of - [Web3 Foundation research](https://research.web3.foundation/en/latest/polkadot/NPoS/index.html). +- Consensus: Blockchains must have a way to come to [consensus](https://docs.substrate.io/fundamentals/consensus/) on the state of the network. + Substrate makes it possible to supply custom consensus engines and also ships with several consensus mechanisms that have been built on top of [Web3 Foundation research](https://research.web3.foundation/en/latest/polkadot/NPoS/index.html). - RPC Server: A remote procedure call (RPC) server is used to interact with Substrate nodes. -There are several files in the `node` directory - take special note of the following: - -- [`chain_spec.rs`](./node/src/chain_spec.rs): A - [chain specification](https://docs.substrate.io/main-docs/build/chain-spec/) is a - source code file that defines a Substrate chain's initial (genesis) state. Chain specifications - are useful for development and testing, and critical when architecting the launch of a - production chain. Take note of the `development_config` and `testnet_genesis` functions, which - are used to define the genesis state for the local development chain configuration. These - functions identify some - [well-known accounts](https://docs.substrate.io/reference/command-line-tools/subkey/) - and use them to configure the blockchain's initial state. -- [`service.rs`](./node/src/service.rs): This file defines the node implementation. Take note of - the libraries that this file imports and the names of the functions it invokes. In particular, - there are references to consensus-related topics, such as the - [block finalization and forks](https://docs.substrate.io/main-docs/fundamentals/consensus/#finalization-and-forks) - and other [consensus mechanisms](https://docs.substrate.io/main-docs/fundamentals/consensus/#default-consensus-models) - such as Aura for block authoring and GRANDPA for finality. - -After the node has been [built](#build), refer to the embedded documentation to learn more about the -capabilities and configuration parameters that it exposes: - -```shell -./target/release/node-template --help -``` +There are several files in the `node` directory. +Take special note of the following: + +- [`chain_spec.rs`](./node/src/chain_spec.rs): A [chain specification](https://docs.substrate.io/build/chain-spec/) is a source code file that defines a Substrate chain's initial (genesis) state. + Chain specifications are useful for development and testing, and critical when architecting the launch of a production chain. + Take note of the `development_config` and `testnet_genesis` functions,. + These functions are used to define the genesis state for the local development chain configuration. + These functions identify some [well-known accounts](https://docs.substrate.io/reference/command-line-tools/subkey/) and use them to configure the blockchain's initial state. +- [`service.rs`](./node/src/service.rs): This file defines the node implementation. + Take note of the libraries that this file imports and the names of the functions it invokes. + In particular, there are references to consensus-related topics, such as the [block finalization and forks](https://docs.substrate.io/fundamentals/consensus/#finalization-and-forks) and other [consensus mechanisms](https://docs.substrate.io/fundamentals/consensus/#default-consensus-models) such as Aura for block authoring and GRANDPA for finality. + + ### Runtime -In Substrate, the terms -"runtime" and "state transition function" -are analogous - they refer to the core logic of the blockchain that is responsible for validating -blocks and executing the state changes they define. The Substrate project in this repository uses -[FRAME](https://docs.substrate.io/main-docs/fundamentals/runtime-intro/#frame) to construct a -blockchain runtime. FRAME allows runtime developers to declare domain-specific logic in modules -called "pallets". At the heart of FRAME is a helpful -[macro language](https://docs.substrate.io/reference/frame-macros/) that makes it easy to -create pallets and flexibly compose them to create blockchains that can address -[a variety of needs](https://substrate.io/ecosystem/projects/). - -Review the [FRAME runtime implementation](./runtime/src/lib.rs) included in this template and note -the following: - -- This file configures several pallets to include in the runtime. Each pallet configuration is - defined by a code block that begins with `impl $PALLET_NAME::Config for Runtime`. -- The pallets are composed into a single runtime by way of the - [`construct_runtime!`](https://crates.parity.io/frame_support/macro.construct_runtime.html) - macro, which is part of the core - FRAME Support [system](https://docs.substrate.io/reference/frame-pallets/#system-pallets) library. +In Substrate, the terms "runtime" and "state transition function" are analogous. +Both terms refer to the core logic of the blockchain that is responsible for validating blocks and executing the state changes they define. +The Substrate project in this repository uses [FRAME](https://docs.substrate.io/fundamentals/runtime-development/#frame) to construct a blockchain runtime. +FRAME allows runtime developers to declare domain-specific logic in modules called "pallets". +At the heart of FRAME is a helpful [macro language](https://docs.substrate.io/reference/frame-macros/) that makes it easy to create pallets and flexibly compose them to create blockchains that can address [a variety of needs](https://substrate.io/ecosystem/projects/). + +Review the [FRAME runtime implementation](./runtime/src/lib.rs) included in this template and note the following: + +- This file configures several pallets to include in the runtime. + Each pallet configuration is defined by a code block that begins with `impl $PALLET_NAME::Config for Runtime`. +- The pallets are composed into a single runtime by way of the [`construct_runtime!`](https://crates.parity.io/frame_support/macro.construct_runtime.html) macro, which is part of the core FRAME Support [system](https://docs.substrate.io/reference/frame-pallets/#system-pallets) library. ### Pallets -The runtime in this project is constructed using many FRAME pallets that ship with the -[core Substrate repository](https://github.com/paritytech/substrate/tree/master/frame) and a -template pallet that is [defined in the `pallets`](./pallets/template/src/lib.rs) directory. +The runtime in this project is constructed using many FRAME pallets that ship with the [core Substrate repository](https://github.com/paritytech/substrate/tree/master/frame) and a template pallet that is [defined in the `pallets`](./pallets/template/src/lib.rs) directory. A FRAME pallet is compromised of a number of blockchain primitives: -- Storage: FRAME defines a rich set of powerful - [storage abstractions](https://docs.substrate.io/main-docs/build/runtime-storage/) that makes - it easy to use Substrate's efficient key-value database to manage the evolving state of a - blockchain. -- Dispatchables: FRAME pallets define special types of functions that can be invoked (dispatched) - from outside of the runtime in order to update its state. -- Events: Substrate uses [events and errors](https://docs.substrate.io/main-docs/build/events-errors/) - to notify users of important changes in the runtime. +- Storage: FRAME defines a rich set of powerful [storage abstractions](https://docs.substrate.io/build/runtime-storage/) that makes it easy to use Substrate's efficient key-value database to manage the evolving state of a blockchain. +- Dispatchables: FRAME pallets define special types of functions that can be invoked (dispatched) from outside of the runtime in order to update its state. +- Events: Substrate uses [events and errors](https://docs.substrate.io/build/events-and-errors/) to notify users of important changes in the runtime. - Errors: When a dispatchable fails, it returns an error. -- Config: The `Config` configuration interface is used to define the types and parameters upon - which a FRAME pallet depends. +- Config: The `Config` configuration interface is used to define the types and parameters upon which a FRAME pallet depends. + +## Alternatives Installations + +Instead of installing dependencies and building this source directly, consider the following alternatives. -### Run in Docker +### Nix -First, install [Docker](https://docs.docker.com/get-docker/) and -[Docker Compose](https://docs.docker.com/compose/install/). +Install [nix](https://nixos.org/), and optionally [direnv](https://github.com/direnv/direnv) and [lorri](https://github.com/nix-community/lorri) for a fully plug-and-play experience for setting up the development environment. +To get all the correct dependencies, activate direnv `direnv allow` and lorri `lorri shell`. + +### Docker + +First, install [Docker](https://docs.docker.com/get-docker/) and [Docker Compose](https://docs.docker.com/compose/install/). Then run the following command to start a single node development chain. -```bash +```sh ./scripts/docker_run.sh ``` -This command will firstly compile your code, and then start a local development network. You can -also replace the default command -(`cargo build --release && ./target/release/node-template --dev --ws-external`) -by appending your own. A few useful ones are as follow. +This command compiles the code and starts a local development network. +You can also replace the default command (`cargo build --release && ./target/release/node-template --dev --ws-external`) by appending your own. +For example: -```bash +```sh # Run Substrate node without re-compiling ./scripts/docker_run.sh ./target/release/node-template --dev --ws-external diff --git a/bin/node-template/scripts/docker_run.sh b/bin/node-template/scripts/docker_run.sh old mode 100644 new mode 100755 From ea182669e111d819b2f4539749b86edee4944168 Mon Sep 17 00:00:00 2001 From: Sergej Sakac <73715684+Szegoo@users.noreply.github.com> Date: Tue, 21 Feb 2023 23:39:27 +0100 Subject: [PATCH 149/558] Glutton pallet (#12833) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Pov-Limit pallet * use Perbill & fixes * fixes * reads & writes * update docs * tests * calculate weight * fmt * benchmark * logic fix * naming fix * caclulate computation weight limit * make the Hasher generic * make the code compile * generate weight * fix on_idle * fix * fix * make reading generic * fix? * fixes * remove warning * fix * hasher fix :D * change value * test * actual weight and expected weight are the same * update * fix * add events * remove useless line * using actual hashing algorithm * better readability * fix nits * Update Signed-off-by: Oliver Tale-Yazdi * add migration * hardcode proof_size in weights.rs * format * fixes * Fix weight * docs * fix * Update frame/pov-limit/src/lib.rs Co-authored-by: Bastian Köcher * Update frame/pov-limit/src/lib.rs Co-authored-by: Bastian Köcher * empty on_ilde & update weight.rs * remove migration & fix benchmark * remove migration from migrations * initialize_pallet * use blake2 * Update frame/pov-limit/Cargo.toml Co-authored-by: Bastian Köcher * Update frame/pov-limit/src/lib.rs Co-authored-by: Bastian Köcher * Update frame/pov-limit/Cargo.toml Co-authored-by: Bastian Köcher * rename pallet * make the wasters non-generic * rename to glutton * fix * small fixes & run benchmark * increase left over ref-time in tests * Update frame/glutton/src/lib.rs Co-authored-by: Bastian Köcher * or values * fix * generate weight * Update frame/glutton/Cargo.toml Co-authored-by: Oliver Tale-Yazdi * fix nits * Adjustments Signed-off-by: Oliver Tale-Yazdi * Fixes Signed-off-by: Oliver Tale-Yazdi * fix * Update weights Signed-off-by: Oliver Tale-Yazdi * Tweak constants Signed-off-by: Oliver Tale-Yazdi * Update weights Signed-off-by: Oliver Tale-Yazdi * remove genesis config * passing tests * More precise results Signed-off-by: Oliver Tale-Yazdi * update node-executor test * Calculate number of iterations in advance Signed-off-by: Oliver Tale-Yazdi * increase criteria * Final fixes Signed-off-by: Oliver Tale-Yazdi * Update frame/glutton/src/tests.rs Co-authored-by: Kian Paimani <5588131+kianenigma@users.noreply.github.com> * Update frame/glutton/src/lib.rs Co-authored-by: Kian Paimani <5588131+kianenigma@users.noreply.github.com> * Update frame/glutton/src/lib.rs Co-authored-by: Kian Paimani <5588131+kianenigma@users.noreply.github.com> * fix typos * Update frame/glutton/src/lib.rs Co-authored-by: Bastian Köcher * Update frame/glutton/src/lib.rs Co-authored-by: Bastian Köcher * expand/shrink * fmt * Revert "Update frame/glutton/src/lib.rs" This reverts commit 98a237afd27de3deb15ba381871e12f71a9d71b1. Signed-off-by: Oliver Tale-Yazdi * Use CountedStorageMap Signed-off-by: Oliver Tale-Yazdi * Add benchmark Signed-off-by: Oliver Tale-Yazdi * Use manual map counter Something with the R/W count in the benchmarking result did not add up. Need to investigate but for now just using a manual counter. Signed-off-by: Oliver Tale-Yazdi * Use new template Signed-off-by: Oliver Tale-Yazdi * Doc+typos Signed-off-by: Oliver Tale-Yazdi * ".git/.scripts/commands/bench/bench.sh" pallet dev pallet_glutton * Apply suggestions from code review Co-authored-by: Koute * Add minimal README Signed-off-by: Oliver Tale-Yazdi * ".git/.scripts/commands/bench/bench.sh" pallet dev pallet_glutton --------- Signed-off-by: Oliver Tale-Yazdi Co-authored-by: Oliver Tale-Yazdi Co-authored-by: Bastian Köcher Co-authored-by: Kian Paimani <5588131+kianenigma@users.noreply.github.com> Co-authored-by: command-bot <> Co-authored-by: Koute --- Cargo.lock | 20 ++ Cargo.toml | 1 + bin/node/executor/Cargo.toml | 1 + bin/node/runtime/Cargo.toml | 4 + bin/node/runtime/src/lib.rs | 7 + frame/glutton/Cargo.toml | 51 +++++ frame/glutton/README.md | 9 + frame/glutton/src/benchmarking.rs | 98 +++++++++ frame/glutton/src/lib.rs | 292 +++++++++++++++++++++++++++ frame/glutton/src/mock.rs | 80 ++++++++ frame/glutton/src/tests.rs | 226 +++++++++++++++++++++ frame/glutton/src/weights.rs | 324 ++++++++++++++++++++++++++++++ 12 files changed, 1113 insertions(+) create mode 100644 frame/glutton/Cargo.toml create mode 100644 frame/glutton/README.md create mode 100644 frame/glutton/src/benchmarking.rs create mode 100644 frame/glutton/src/lib.rs create mode 100644 frame/glutton/src/mock.rs create mode 100644 frame/glutton/src/tests.rs create mode 100644 frame/glutton/src/weights.rs diff --git a/Cargo.lock b/Cargo.lock index bed09f135..7c0b40cd9 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3538,6 +3538,7 @@ dependencies = [ "pallet-election-provider-support-benchmarking", "pallet-elections-phragmen", "pallet-fast-unstake", + "pallet-glutton", "pallet-grandpa", "pallet-identity", "pallet-im-online", @@ -4850,6 +4851,7 @@ dependencies = [ "node-testing", "pallet-balances", "pallet-contracts", + "pallet-glutton", "pallet-im-online", "pallet-root-testing", "pallet-sudo", @@ -5813,6 +5815,24 @@ dependencies = [ "substrate-test-utils", ] +[[package]] +name = "pallet-glutton" +version = "4.0.0-dev" +dependencies = [ + "blake2", + "frame-benchmarking", + "frame-support", + "frame-system", + "log", + "pallet-balances", + "parity-scale-codec", + "scale-info", + "sp-core", + "sp-io", + "sp-runtime", + "sp-std", +] + [[package]] name = "pallet-grandpa" version = "4.0.0-dev" diff --git a/Cargo.toml b/Cargo.toml index a86ec3146..89a52fd16 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -167,6 +167,7 @@ members = [ "frame/uniques", "frame/utility", "frame/vesting", + "frame/glutton", "frame/whitelist", "primitives/api", "primitives/api/proc-macro", diff --git a/bin/node/executor/Cargo.toml b/bin/node/executor/Cargo.toml index ffa8d38ee..cb661f536 100644 --- a/bin/node/executor/Cargo.toml +++ b/bin/node/executor/Cargo.toml @@ -35,6 +35,7 @@ node-testing = { version = "3.0.0-dev", path = "../testing" } pallet-balances = { version = "4.0.0-dev", path = "../../../frame/balances" } pallet-contracts = { version = "4.0.0-dev", path = "../../../frame/contracts" } pallet-im-online = { version = "4.0.0-dev", path = "../../../frame/im-online" } +pallet-glutton = { version = "4.0.0-dev", path = "../../../frame/glutton" } pallet-sudo = { version = "4.0.0-dev", path = "../../../frame/sudo" } pallet-timestamp = { version = "4.0.0-dev", path = "../../../frame/timestamp" } pallet-treasury = { version = "4.0.0-dev", path = "../../../frame/treasury" } diff --git a/bin/node/runtime/Cargo.toml b/bin/node/runtime/Cargo.toml index e27e4f7f8..37a99414b 100644 --- a/bin/node/runtime/Cargo.toml +++ b/bin/node/runtime/Cargo.toml @@ -85,6 +85,7 @@ pallet-nomination-pools-benchmarking = { version = "1.0.0", default-features = f pallet-nomination-pools-runtime-api = { version = "1.0.0-dev", default-features = false, path = "../../../frame/nomination-pools/runtime-api" } pallet-offences = { version = "4.0.0-dev", default-features = false, path = "../../../frame/offences" } pallet-offences-benchmarking = { version = "4.0.0-dev", path = "../../../frame/offences/benchmarking", default-features = false, optional = true } +pallet-glutton = { version = "4.0.0-dev", default-features = false, path = "../../../frame/glutton" } pallet-preimage = { version = "4.0.0-dev", default-features = false, path = "../../../frame/preimage" } pallet-proxy = { version = "4.0.0-dev", default-features = false, path = "../../../frame/proxy" } pallet-insecure-randomness-collective-flip = { version = "4.0.0-dev", default-features = false, path = "../../../frame/insecure-randomness-collective-flip" } @@ -165,6 +166,7 @@ std = [ "node-primitives/std", "sp-offchain/std", "pallet-offences/std", + "pallet-glutton/std", "pallet-preimage/std", "pallet-proxy/std", "sp-core/std", @@ -244,6 +246,7 @@ runtime-benchmarks = [ "pallet-multisig/runtime-benchmarks", "pallet-nomination-pools-benchmarking/runtime-benchmarks", "pallet-offences-benchmarking/runtime-benchmarks", + "pallet-glutton/runtime-benchmarks", "pallet-preimage/runtime-benchmarks", "pallet-proxy/runtime-benchmarks", "pallet-scheduler/runtime-benchmarks", @@ -300,6 +303,7 @@ try-runtime = [ "pallet-multisig/try-runtime", "pallet-nomination-pools/try-runtime", "pallet-offences/try-runtime", + "pallet-glutton/try-runtime", "pallet-preimage/try-runtime", "pallet-proxy/try-runtime", "pallet-insecure-randomness-collective-flip/try-runtime", diff --git a/bin/node/runtime/src/lib.rs b/bin/node/runtime/src/lib.rs index 8e924a9c5..3e3a0d753 100644 --- a/bin/node/runtime/src/lib.rs +++ b/bin/node/runtime/src/lib.rs @@ -362,6 +362,11 @@ impl pallet_scheduler::Config for Runtime { type Preimages = Preimage; } +impl pallet_glutton::Config for Runtime { + type RuntimeEvent = RuntimeEvent; + type WeightInfo = pallet_glutton::weights::SubstrateWeight; +} + parameter_types! { pub const PreimageMaxSize: u32 = 4096 * 1024; pub const PreimageBaseDeposit: Balance = 1 * DOLLARS; @@ -1747,6 +1752,7 @@ construct_runtime!( Recovery: pallet_recovery, Vesting: pallet_vesting, Scheduler: pallet_scheduler, + Glutton: pallet_glutton, Preimage: pallet_preimage, Proxy: pallet_proxy, Multisig: pallet_multisig, @@ -1878,6 +1884,7 @@ mod benches { [pallet_recovery, Recovery] [pallet_remark, Remark] [pallet_scheduler, Scheduler] + [pallet_glutton, Glutton] [pallet_session, SessionBench::] [pallet_staking, Staking] [pallet_state_trie_migration, StateTrieMigration] diff --git a/frame/glutton/Cargo.toml b/frame/glutton/Cargo.toml new file mode 100644 index 000000000..c0f031246 --- /dev/null +++ b/frame/glutton/Cargo.toml @@ -0,0 +1,51 @@ +[package] +name = "pallet-glutton" +version = "4.0.0-dev" +authors = ["Parity Technologies "] +edition = "2021" +license = "Apache-2.0" +homepage = "https://substrate.io" +repository = "https://github.com/paritytech/substrate/" +description = "FRAME pallet for pushing a chain to its weight limits" +readme = "README.md" + +[package.metadata.docs.rs] +targets = ["x86_64-unknown-linux-gnu"] + +[dependencies] +blake2 = { version = "0.10.4", default-features = false } +codec = { package = "parity-scale-codec", version = "3.0.0", default-features = false, features = ["derive"] } +scale-info = { version = "2.1.1", default-features = false, features = ["derive"] } +log = { version = "0.4.14", default-features = false } +frame-benchmarking = { version = "4.0.0-dev", default-features = false, optional = true, path = "../benchmarking" } +frame-support = { version = "4.0.0-dev", default-features = false, path = "../support" } +frame-system = { version = "4.0.0-dev", default-features = false, path = "../system" } +sp-core = { version = "7.0.0", default-features = false, path = "../../primitives/core" } +sp-io = { version = "7.0.0", default-features = false, path = "../../primitives/io" } +sp-runtime = { version = "7.0.0", default-features = false, path = "../../primitives/runtime" } +sp-std = { version = "5.0.0", default-features = false, path = "../../primitives/std" } + +[dev-dependencies] +pallet-balances = { version = "4.0.0-dev", path = "../balances" } + +[features] +default = ["std"] +std = [ + "blake2/std", + "codec/std", + "frame-benchmarking?/std", + "frame-support/std", + "frame-system/std", + "scale-info/std", + "sp-core/std", + "sp-io/std", + "sp-runtime/std", + "sp-std/std", +] +try-runtime = ["frame-support/try-runtime"] + +runtime-benchmarks = [ + "frame-benchmarking/runtime-benchmarks", + "frame-system/runtime-benchmarks", + "sp-runtime/runtime-benchmarks", +] diff --git a/frame/glutton/README.md b/frame/glutton/README.md new file mode 100644 index 000000000..bcd6c51a6 --- /dev/null +++ b/frame/glutton/README.md @@ -0,0 +1,9 @@ +# WARNING + +Do not use on value-bearing chains. This pallet is **only** intended for usage on test-chains. + +# Glutton Pallet + +The `Glutton` pallet gets the name from its property to consume vast amounts of resources. It can be used to push para-chains and their relay-chains to the limits. This is good for testing out theoretical limits in a practical way. + +The `Glutton` can be set to consume a fraction of the available unused weight of a chain. It accomplishes this by utilizing the `on_idle` hook and consuming a specific ration of the remaining weight. The rations can be set via `set_compute` and `set_storage`. Initially the `Glutton` needs to be initialized once with `initialize_pallet`. diff --git a/frame/glutton/src/benchmarking.rs b/frame/glutton/src/benchmarking.rs new file mode 100644 index 000000000..13576ae2f --- /dev/null +++ b/frame/glutton/src/benchmarking.rs @@ -0,0 +1,98 @@ +// This file is part of Substrate. + +// Copyright (C) 2022 Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +//! Glutton pallet benchmarking. +//! +//! Has to be compiled and run twice to calibrate on new hardware. + +#[cfg(feature = "runtime-benchmarks")] +use super::*; + +use frame_benchmarking::benchmarks; +use frame_support::{pallet_prelude::*, weights::constants::*}; +use frame_system::RawOrigin as SystemOrigin; + +use crate::Pallet as Glutton; +use frame_system::Pallet as System; + +benchmarks! { + initialize_pallet_grow { + let n in 0 .. 1_000; + }: { + Glutton::::initialize_pallet(SystemOrigin::Root.into(), n, None).unwrap() + } verify { + assert_eq!(TrashDataCount::::get(), n); + } + + initialize_pallet_shrink { + let n in 0 .. 1_000; + + Glutton::::initialize_pallet(SystemOrigin::Root.into(), n, None).unwrap(); + }: { + Glutton::::initialize_pallet(SystemOrigin::Root.into(), 0, Some(n)).unwrap() + } verify { + assert_eq!(TrashDataCount::::get(), 0); + } + + waste_ref_time_iter { + let i in 0..100_000; + }: { + Glutton::::waste_ref_time_iter(vec![0u8; 64], i); + } + + waste_proof_size_some { + let i in 0..5_000; + + (0..5000).for_each(|i| TrashData::::insert(i, [i as u8; 1024])); + }: { + (0..i).for_each(|i| { + TrashData::::get(i); + }) + } + + // For manual verification only. + on_idle_high_proof_waste { + (0..5000).for_each(|i| TrashData::::insert(i, [i as u8; 1024])); + let _ = Glutton::::set_compute(SystemOrigin::Root.into(), Perbill::from_percent(100)); + let _ = Glutton::::set_storage(SystemOrigin::Root.into(), Perbill::from_percent(100)); + }: { + let weight = Glutton::::on_idle(System::::block_number(), Weight::from_parts(WEIGHT_REF_TIME_PER_MILLIS * 100, WEIGHT_PROOF_SIZE_PER_MB * 5)); + } + + // For manual verification only. + on_idle_low_proof_waste { + (0..5000).for_each(|i| TrashData::::insert(i, [i as u8; 1024])); + let _ = Glutton::::set_compute(SystemOrigin::Root.into(), Perbill::from_percent(100)); + let _ = Glutton::::set_storage(SystemOrigin::Root.into(), Perbill::from_percent(100)); + }: { + let weight = Glutton::::on_idle(System::::block_number(), Weight::from_parts(WEIGHT_REF_TIME_PER_MILLIS * 100, WEIGHT_PROOF_SIZE_PER_KB * 20)); + } + + empty_on_idle { + }: { + // Enough weight do do nothing. + Glutton::::on_idle(System::::block_number(), T::WeightInfo::empty_on_idle()); + } + + set_compute { + }: _(SystemOrigin::Root, Perbill::from_percent(50)) + + set_storage { + }: _(SystemOrigin::Root, Perbill::from_percent(50)) + + impl_benchmark_test_suite!(Glutton, crate::mock::new_test_ext(), crate::mock::Test); +} diff --git a/frame/glutton/src/lib.rs b/frame/glutton/src/lib.rs new file mode 100644 index 000000000..e9a46374a --- /dev/null +++ b/frame/glutton/src/lib.rs @@ -0,0 +1,292 @@ +// This file is part of Substrate. + +// Copyright (C) 2022 Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +//! # Glutton Pallet +//! +//! Pallet that consumes `ref_time` and `proof_size` of a block. Based on the +//! `Compute` and `Storage` parameters the pallet consumes the adequate amount +//! of weight. + +#![cfg_attr(not(feature = "std"), no_std)] + +#[cfg(feature = "runtime-benchmarks")] +mod benchmarking; +#[cfg(test)] +mod mock; +#[cfg(test)] +mod tests; +pub mod weights; + +use blake2::{Blake2b512, Digest}; +use frame_support::{pallet_prelude::*, weights::WeightMeter}; +use frame_system::pallet_prelude::*; +use sp_runtime::{traits::Zero, Perbill}; +use sp_std::{vec, vec::Vec}; + +pub use pallet::*; +pub use weights::WeightInfo; + +#[frame_support::pallet] +pub mod pallet { + use super::*; + + #[pallet::config] + pub trait Config: frame_system::Config { + type RuntimeEvent: From + IsType<::RuntimeEvent>; + + /// Weight information for this pallet. + type WeightInfo: WeightInfo; + } + + #[pallet::pallet] + pub struct Pallet(_); + + #[pallet::event] + #[pallet::generate_deposit(pub(super) fn deposit_event)] + pub enum Event { + /// The pallet has been (re)initialized by root. + PalletInitialized { reinit: bool }, + /// The computation limit has been updated by root. + ComputationLimitSet { compute: Perbill }, + /// The storage limit has been updated by root. + StorageLimitSet { storage: Perbill }, + } + + #[pallet::error] + pub enum Error { + /// The pallet was already initialized. + /// + /// Set `witness_count` to `Some` to bypass this error. + AlreadyInitialized, + } + + /// Storage value used to specify what percentage of the left over `ref_time` + /// to consume during `on_idle`. + #[pallet::storage] + pub(crate) type Compute = StorageValue<_, Perbill, ValueQuery>; + + /// Storage value used the specify what percentage of left over `proof_size` + /// to consume during `on_idle`. + #[pallet::storage] + pub(crate) type Storage = StorageValue<_, Perbill, ValueQuery>; + + /// Storage map used for wasting proof size. + /// + /// It contains no meaningful data - hence the name "Trash". The maximal number of entries is + /// set to 65k, which is just below the next jump at 16^4. This is important to reduce the proof + /// size benchmarking overestimate. The assumption here is that we won't have more than 65k * + /// 1KiB = 65MiB of proof size wasting in practice. However, this limit is not enforced, so the + /// pallet would also work out of the box with more entries, but its benchmarked proof weight + /// would possibly be underestimated in that case. + #[pallet::storage] + pub(super) type TrashData = StorageMap< + Hasher = Twox64Concat, + Key = u32, + Value = [u8; 1024], + QueryKind = OptionQuery, + MaxValues = ConstU32<65_000>, + >; + + /// The current number of entries in `TrashData`. + #[pallet::storage] + pub(crate) type TrashDataCount = StorageValue<_, u32, ValueQuery>; + + #[pallet::hooks] + impl Hooks> for Pallet { + fn integrity_test() { + assert!( + !T::WeightInfo::waste_ref_time_iter(1).ref_time().is_zero(), + "Weight zero; would get stuck in an infinite loop" + ); + assert!( + !T::WeightInfo::waste_proof_size_some(1).proof_size().is_zero(), + "Weight zero; would get stuck in an infinite loop" + ); + } + + fn on_idle(_: BlockNumberFor, remaining_weight: Weight) -> Weight { + let mut meter = WeightMeter::from_limit(remaining_weight); + if !meter.check_accrue(T::WeightInfo::empty_on_idle()) { + return T::WeightInfo::empty_on_idle() + } + + let proof_size_limit = Storage::::get().mul_floor(meter.remaining().proof_size()); + let computation_weight_limit = + Compute::::get().mul_floor(meter.remaining().ref_time()); + let mut meter = WeightMeter::from_limit(Weight::from_parts( + computation_weight_limit, + proof_size_limit, + )); + + Self::waste_at_most_proof_size(&mut meter); + Self::waste_at_most_ref_time(&mut meter); + + meter.consumed + } + } + + #[pallet::call] + impl Pallet { + /// Initializes the pallet by writing into `TrashData`. + /// + /// Only callable by Root. A good default for `trash_count` is `5_000`. + #[pallet::call_index(0)] + #[pallet::weight( + T::WeightInfo::initialize_pallet_grow(witness_count.unwrap_or_default()) + .max(T::WeightInfo::initialize_pallet_shrink(witness_count.unwrap_or_default())) + )] + pub fn initialize_pallet( + origin: OriginFor, + new_count: u32, + witness_count: Option, + ) -> DispatchResult { + ensure_root(origin)?; + + let current_count = TrashDataCount::::get(); + ensure!( + current_count == witness_count.unwrap_or_default(), + Error::::AlreadyInitialized + ); + + if new_count > current_count { + (current_count..new_count).for_each(|i| TrashData::::insert(i, [i as u8; 1024])); + } else { + (new_count..current_count).for_each(TrashData::::remove); + } + + Self::deposit_event(Event::PalletInitialized { reinit: witness_count.is_some() }); + TrashDataCount::::set(new_count); + Ok(()) + } + + /// Set the `Compute` storage value that determines how much of the + /// block's weight `ref_time` to use during `on_idle`. + /// + /// Only callable by Root. + #[pallet::call_index(1)] + #[pallet::weight(T::WeightInfo::set_compute())] + pub fn set_compute(origin: OriginFor, compute: Perbill) -> DispatchResult { + ensure_root(origin)?; + Compute::::set(compute); + + Self::deposit_event(Event::ComputationLimitSet { compute }); + Ok(()) + } + + /// Set the `Storage` storage value that determines the PoV size usage + /// for each block. + /// + /// Only callable by Root. + #[pallet::call_index(2)] + #[pallet::weight(T::WeightInfo::set_storage())] + pub fn set_storage(origin: OriginFor, storage: Perbill) -> DispatchResult { + ensure_root(origin)?; + Storage::::set(storage); + + Self::deposit_event(Event::StorageLimitSet { storage }); + Ok(()) + } + } + + impl Pallet { + /// Waste at most the remaining proof size of `meter`. + /// + /// Tries to come as close to the limit as possible. + pub(crate) fn waste_at_most_proof_size(meter: &mut WeightMeter) { + let Ok(n) = Self::calculate_proof_size_iters(&meter) else { + return; + }; + + meter.defensive_saturating_accrue(T::WeightInfo::waste_proof_size_some(n)); + + (0..n).for_each(|i| { + TrashData::::get(i); + }); + } + + /// Calculate how many times `waste_proof_size_some` should be called to fill up `meter`. + fn calculate_proof_size_iters(meter: &WeightMeter) -> Result { + let base = T::WeightInfo::waste_proof_size_some(0); + let slope = T::WeightInfo::waste_proof_size_some(1).saturating_sub(base); + + let remaining = meter.remaining().saturating_sub(base); + let iter_by_proof_size = + remaining.proof_size().checked_div(slope.proof_size()).ok_or(())?; + let iter_by_ref_time = remaining.ref_time().checked_div(slope.ref_time()).ok_or(())?; + + if iter_by_proof_size > 0 && iter_by_proof_size <= iter_by_ref_time { + Ok(iter_by_proof_size as u32) + } else { + Err(()) + } + } + + /// Waste at most the remaining ref time weight of `meter`. + /// + /// Tries to come as close to the limit as possible. + pub(crate) fn waste_at_most_ref_time(meter: &mut WeightMeter) { + let Ok(n) = Self::calculate_ref_time_iters(&meter) else { + return; + }; + meter.defensive_saturating_accrue(T::WeightInfo::waste_ref_time_iter(n)); + + let clobber = Self::waste_ref_time_iter(vec![0u8; 64], n); + + // By casting it into a vec we can hopefully prevent the compiler from optimizing it + // out. Note that `Blake2b512` produces 64 bytes, this is therefore impossible - but the + // compiler does not know that (hopefully). + debug_assert!(clobber.len() == 64); + if clobber.len() == 65 { + TrashData::::insert(0, [clobber[0] as u8; 1024]); + } + } + + /// Wastes some `ref_time`. Receives the previous result as an argument. + /// + /// The ref_time of one iteration should be in the order of 1-10 ms. + pub(crate) fn waste_ref_time_iter(clobber: Vec, i: u32) -> Vec { + let mut hasher = Blake2b512::new(); + + // Blake2 has a very high speed of hashing so we make multiple hashes with it to + // waste more `ref_time` at once. + (0..i).for_each(|_| { + hasher.update(clobber.as_slice()); + }); + + hasher.finalize().to_vec() + } + + /// Calculate how many times `waste_ref_time_iter` should be called to fill up `meter`. + fn calculate_ref_time_iters(meter: &WeightMeter) -> Result { + let base = T::WeightInfo::waste_ref_time_iter(0); + let slope = T::WeightInfo::waste_ref_time_iter(1).saturating_sub(base); + if !slope.proof_size().is_zero() || !base.proof_size().is_zero() { + return Err(()) + } + + match meter + .remaining() + .ref_time() + .saturating_sub(base.ref_time()) + .checked_div(slope.ref_time()) + { + Some(0) | None => Err(()), + Some(i) => Ok(i as u32), + } + } + } +} diff --git a/frame/glutton/src/mock.rs b/frame/glutton/src/mock.rs new file mode 100644 index 000000000..c8be354f4 --- /dev/null +++ b/frame/glutton/src/mock.rs @@ -0,0 +1,80 @@ +// This file is part of Substrate. + +// Copyright (C) 2022 Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +use super::*; +use crate as pallet_glutton; + +use frame_support::traits::{ConstU32, ConstU64}; +use sp_core::H256; +use sp_runtime::{ + testing::Header, + traits::{BlakeTwo256, IdentityLookup}, +}; + +type UncheckedExtrinsic = frame_system::mocking::MockUncheckedExtrinsic; +type Block = frame_system::mocking::MockBlock; + +frame_support::construct_runtime!( + pub enum Test where + Block = Block, + NodeBlock = Block, + UncheckedExtrinsic = UncheckedExtrinsic, + { + System: frame_system::{Pallet, Call, Config, Storage, Event}, + Glutton: pallet_glutton::{Pallet, Event}, + } +); + +impl frame_system::Config for Test { + type BaseCallFilter = frame_support::traits::Everything; + type BlockWeights = (); + type BlockLength = (); + type DbWeight = (); + type RuntimeOrigin = RuntimeOrigin; + type Index = u64; + type BlockNumber = u64; + type Hash = H256; + type RuntimeCall = RuntimeCall; + type Hashing = BlakeTwo256; + type AccountId = u64; + type Lookup = IdentityLookup; + type Header = Header; + type RuntimeEvent = RuntimeEvent; + type BlockHashCount = ConstU64<250>; + type Version = (); + type PalletInfo = PalletInfo; + type AccountData = pallet_balances::AccountData; + type OnNewAccount = (); + type OnKilledAccount = (); + type SystemWeightInfo = (); + type SS58Prefix = (); + type OnSetCode = (); + type MaxConsumers = ConstU32<16>; +} + +impl Config for Test { + type RuntimeEvent = RuntimeEvent; + type WeightInfo = (); +} + +pub fn new_test_ext() -> sp_io::TestExternalities { + let t = frame_system::GenesisConfig::default().build_storage::().unwrap(); + + let mut ext = sp_io::TestExternalities::new(t); + ext.execute_with(|| System::set_block_number(1)); + ext +} diff --git a/frame/glutton/src/tests.rs b/frame/glutton/src/tests.rs new file mode 100644 index 000000000..d2d2d4ede --- /dev/null +++ b/frame/glutton/src/tests.rs @@ -0,0 +1,226 @@ +// This file is part of Substrate. + +// Copyright (C) 2023 Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +//! Tests for the glutton pallet. + +use super::*; +use mock::{new_test_ext, Glutton, RuntimeOrigin, System, Test}; + +use frame_support::{assert_err, assert_noop, assert_ok, weights::constants::*}; + +#[test] +fn initialize_pallet_works() { + new_test_ext().execute_with(|| { + assert_eq!(TrashData::::get(0), None); + + assert_noop!( + Glutton::initialize_pallet(RuntimeOrigin::signed(1), 3, None), + DispatchError::BadOrigin + ); + assert_noop!( + Glutton::initialize_pallet(RuntimeOrigin::none(), 3, None), + DispatchError::BadOrigin + ); + + assert_ok!(Glutton::initialize_pallet(RuntimeOrigin::root(), 2, None)); + System::assert_last_event(Event::PalletInitialized { reinit: false }.into()); + assert_err!( + Glutton::initialize_pallet(RuntimeOrigin::root(), 2, None), + Error::::AlreadyInitialized + ); + + assert_eq!(TrashData::::get(0), Some([0; 1024])); + assert_eq!(TrashData::::get(1), Some([1; 1024])); + assert_eq!(TrashData::::get(2), None); + + assert_eq!(TrashDataCount::::get(), 2); + + assert_ok!(Glutton::initialize_pallet(RuntimeOrigin::root(), 20, Some(2))); + + assert_eq!(TrashDataCount::::get(), 20); + assert_eq!(TrashData::::iter_keys().count(), 20); + }); +} + +#[test] +fn expand_and_shrink_trash_data_works() { + new_test_ext().execute_with(|| { + assert_eq!(TrashDataCount::::get(), 0); + + assert_ok!(Glutton::initialize_pallet(RuntimeOrigin::root(), 5000, None)); + assert_eq!(TrashDataCount::::get(), 5000); + assert_eq!(TrashData::::iter_keys().count(), 5000); + + assert_ok!(Glutton::initialize_pallet(RuntimeOrigin::root(), 8000, Some(5000))); + assert_eq!(TrashDataCount::::get(), 8000); + assert_eq!(TrashData::::iter_keys().count(), 8000); + + assert_ok!(Glutton::initialize_pallet(RuntimeOrigin::root(), 6000, Some(8000))); + assert_eq!(TrashDataCount::::get(), 6000); + assert_eq!(TrashData::::iter_keys().count(), 6000); + + assert_noop!( + Glutton::initialize_pallet(RuntimeOrigin::root(), 0, None), + Error::::AlreadyInitialized + ); + assert_ok!(Glutton::initialize_pallet(RuntimeOrigin::root(), 0, Some(6000))); + assert_eq!(TrashDataCount::::get(), 0); + assert_eq!(TrashData::::iter_keys().count(), 0); + }); +} + +#[test] +fn setting_compute_works() { + new_test_ext().execute_with(|| { + assert_eq!(Compute::::get(), Perbill::from_percent(0)); + + assert_ok!(Glutton::set_compute(RuntimeOrigin::root(), Perbill::from_percent(70))); + assert_eq!(Compute::::get(), Perbill::from_percent(70)); + System::assert_last_event( + Event::ComputationLimitSet { compute: Perbill::from_percent(70) }.into(), + ); + + assert_noop!( + Glutton::set_compute(RuntimeOrigin::signed(1), Perbill::from_percent(30)), + DispatchError::BadOrigin + ); + assert_noop!( + Glutton::set_compute(RuntimeOrigin::none(), Perbill::from_percent(30)), + DispatchError::BadOrigin + ); + }); +} + +#[test] +fn setting_storage_works() { + new_test_ext().execute_with(|| { + assert_eq!(Storage::::get(), Perbill::from_percent(0)); + + assert_ok!(Glutton::set_storage(RuntimeOrigin::root(), Perbill::from_percent(30))); + assert_eq!(Storage::::get(), Perbill::from_percent(30)); + System::assert_last_event( + Event::StorageLimitSet { storage: Perbill::from_percent(30) }.into(), + ); + + assert_noop!( + Glutton::set_storage(RuntimeOrigin::signed(1), Perbill::from_percent(90)), + DispatchError::BadOrigin + ); + assert_noop!( + Glutton::set_storage(RuntimeOrigin::none(), Perbill::from_percent(90)), + DispatchError::BadOrigin + ); + }); +} + +#[test] +fn on_idle_works() { + new_test_ext().execute_with(|| { + assert_ok!(Glutton::set_compute(RuntimeOrigin::root(), Perbill::from_percent(100))); + assert_ok!(Glutton::set_storage(RuntimeOrigin::root(), Perbill::from_percent(100))); + + Glutton::on_idle(1, Weight::from_ref_time(20_000_000)); + }); +} + +/// Check that the expected is close enough to the consumed weight. +#[test] +fn on_idle_weight_high_proof_is_close_enough_works() { + new_test_ext().execute_with(|| { + assert_ok!(Glutton::set_compute(RuntimeOrigin::root(), Perbill::from_percent(100))); + assert_ok!(Glutton::set_storage(RuntimeOrigin::root(), Perbill::from_percent(100))); + + let should = Weight::from_parts(WEIGHT_REF_TIME_PER_SECOND, WEIGHT_PROOF_SIZE_PER_MB * 5); + let got = Glutton::on_idle(1, should); + assert!(got.all_lte(should), "Consumed too much weight"); + + let ratio = Perbill::from_rational(got.proof_size(), should.proof_size()); + assert!( + ratio >= Perbill::from_percent(99), + "Too few proof size consumed, was only {:?} of expected", + ratio + ); + + let ratio = Perbill::from_rational(got.ref_time(), should.ref_time()); + assert!( + ratio >= Perbill::from_percent(99), + "Too few ref time consumed, was only {:?} of expected", + ratio + ); + }); +} + +#[test] +fn on_idle_weight_low_proof_is_close_enough_works() { + new_test_ext().execute_with(|| { + assert_ok!(Glutton::set_compute(RuntimeOrigin::root(), Perbill::from_percent(100))); + assert_ok!(Glutton::set_storage(RuntimeOrigin::root(), Perbill::from_percent(100))); + + let should = Weight::from_parts(WEIGHT_REF_TIME_PER_SECOND, WEIGHT_PROOF_SIZE_PER_KB * 20); + let got = Glutton::on_idle(1, should); + assert!(got.all_lte(should), "Consumed too much weight"); + + let ratio = Perbill::from_rational(got.proof_size(), should.proof_size()); + // Just a sanity check here. + assert!( + ratio >= Perbill::from_percent(80), + "Too few proof size consumed, was only {:?} of expected", + ratio + ); + + let ratio = Perbill::from_rational(got.ref_time(), should.ref_time()); + assert!( + ratio >= Perbill::from_percent(99), + "Too few ref time consumed, was only {:?} of expected", + ratio + ); + }); +} + +#[test] +fn waste_at_most_ref_time_weight_close_enough() { + new_test_ext().execute_with(|| { + let mut meter = + WeightMeter::from_limit(Weight::from_parts(WEIGHT_REF_TIME_PER_SECOND, u64::MAX)); + // Over-spending fails defensively. + Glutton::waste_at_most_ref_time(&mut meter); + + // We require it to be under-spend by at most 1%. + assert!( + meter.consumed_ratio() >= Perbill::from_percent(99), + "Consumed too few: {:?}", + meter.consumed_ratio() + ); + }); +} + +#[test] +fn waste_at_most_proof_size_weight_close_enough() { + new_test_ext().execute_with(|| { + let mut meter = + WeightMeter::from_limit(Weight::from_parts(u64::MAX, WEIGHT_PROOF_SIZE_PER_MB * 5)); + // Over-spending fails defensively. + Glutton::waste_at_most_proof_size(&mut meter); + + // We require it to be under-spend by at most 1%. + assert!( + meter.consumed_ratio() >= Perbill::from_percent(99), + "Consumed too few: {:?}", + meter.consumed_ratio() + ); + }); +} diff --git a/frame/glutton/src/weights.rs b/frame/glutton/src/weights.rs new file mode 100644 index 000000000..1e09d02c6 --- /dev/null +++ b/frame/glutton/src/weights.rs @@ -0,0 +1,324 @@ +// This file is part of Substrate. + +// Copyright (C) 2023 Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +//! Autogenerated weights for pallet_glutton +//! +//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev +//! DATE: 2023-02-20, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! WORST CASE MAP SIZE: `1000000` +//! HOSTNAME: `runner-ehxwxxsd-project-145-concurrent-0`, CPU: `Intel(R) Xeon(R) CPU @ 2.60GHz` +//! EXECUTION: Some(Wasm), WASM-EXECUTION: Compiled, CHAIN: Some("dev"), DB CACHE: 1024 + +// Executed Command: +// target/production/substrate +// benchmark +// pallet +// --steps=50 +// --repeat=20 +// --extrinsic=* +// --execution=wasm +// --wasm-execution=compiled +// --heap-pages=4096 +// --json-file=/builds/parity/mirrors/substrate/.git/.artifacts/bench.json +// --pallet=pallet_glutton +// --chain=dev +// --header=./HEADER-APACHE2 +// --output=./frame/glutton/src/weights.rs +// --template=./.maintain/frame-weight-template.hbs + +#![cfg_attr(rustfmt, rustfmt_skip)] +#![allow(unused_parens)] +#![allow(unused_imports)] + +use frame_support::{traits::Get, weights::{Weight, constants::RocksDbWeight}}; +use sp_std::marker::PhantomData; + +/// Weight functions needed for pallet_glutton. +pub trait WeightInfo { + fn initialize_pallet_grow(n: u32, ) -> Weight; + fn initialize_pallet_shrink(n: u32, ) -> Weight; + fn waste_ref_time_iter(i: u32, ) -> Weight; + fn waste_proof_size_some(i: u32, ) -> Weight; + fn on_idle_high_proof_waste() -> Weight; + fn on_idle_low_proof_waste() -> Weight; + fn empty_on_idle() -> Weight; + fn set_compute() -> Weight; + fn set_storage() -> Weight; +} + +/// Weights for pallet_glutton using the Substrate node and recommended hardware. +pub struct SubstrateWeight(PhantomData); +impl WeightInfo for SubstrateWeight { + /// Storage: Glutton TrashDataCount (r:1 w:1) + /// Proof: Glutton TrashDataCount (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) + /// Storage: Glutton TrashData (r:0 w:1000) + /// Proof: Glutton TrashData (max_values: Some(65000), max_size: Some(1036), added: 3016, mode: MaxEncodedLen) + /// The range of component `n` is `[0, 1000]`. + fn initialize_pallet_grow(n: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `4` + // Estimated: `499` + // Minimum execution time: 9_248 nanoseconds. + Weight::from_ref_time(9_582_000) + .saturating_add(Weight::from_proof_size(499)) + // Standard Error: 1_144 + .saturating_add(Weight::from_ref_time(1_624_225).saturating_mul(n.into())) + .saturating_add(T::DbWeight::get().reads(1_u64)) + .saturating_add(T::DbWeight::get().writes(1_u64)) + .saturating_add(T::DbWeight::get().writes((1_u64).saturating_mul(n.into()))) + } + /// Storage: Glutton TrashDataCount (r:1 w:1) + /// Proof: Glutton TrashDataCount (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) + /// Storage: Glutton TrashData (r:0 w:1000) + /// Proof: Glutton TrashData (max_values: Some(65000), max_size: Some(1036), added: 3016, mode: MaxEncodedLen) + /// The range of component `n` is `[0, 1000]`. + fn initialize_pallet_shrink(n: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `65` + // Estimated: `499` + // Minimum execution time: 10_210 nanoseconds. + Weight::from_ref_time(7_260_854) + .saturating_add(Weight::from_proof_size(499)) + // Standard Error: 1_459 + .saturating_add(Weight::from_ref_time(1_053_844).saturating_mul(n.into())) + .saturating_add(T::DbWeight::get().reads(1_u64)) + .saturating_add(T::DbWeight::get().writes(1_u64)) + .saturating_add(T::DbWeight::get().writes((1_u64).saturating_mul(n.into()))) + } + /// The range of component `i` is `[0, 100000]`. + fn waste_ref_time_iter(i: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 605 nanoseconds. + Weight::from_ref_time(1_102_112) + .saturating_add(Weight::from_proof_size(0)) + // Standard Error: 28 + .saturating_add(Weight::from_ref_time(120_597).saturating_mul(i.into())) + } + /// Storage: Glutton TrashData (r:5000 w:0) + /// Proof: Glutton TrashData (max_values: Some(65000), max_size: Some(1036), added: 3016, mode: MaxEncodedLen) + /// The range of component `i` is `[0, 5000]`. + fn waste_proof_size_some(i: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `119036 + i * (1053 ±0)` + // Estimated: `0 + i * (3016 ±0)` + // Minimum execution time: 464 nanoseconds. + Weight::from_ref_time(552_000) + .saturating_add(Weight::from_proof_size(0)) + // Standard Error: 1_962 + .saturating_add(Weight::from_ref_time(5_780_304).saturating_mul(i.into())) + .saturating_add(T::DbWeight::get().reads((1_u64).saturating_mul(i.into()))) + .saturating_add(Weight::from_proof_size(3016).saturating_mul(i.into())) + } + /// Storage: Glutton Storage (r:1 w:0) + /// Proof: Glutton Storage (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) + /// Storage: Glutton Compute (r:1 w:0) + /// Proof: Glutton Compute (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) + /// Storage: Glutton TrashData (r:1738 w:0) + /// Proof: Glutton TrashData (max_values: Some(65000), max_size: Some(1036), added: 3016, mode: MaxEncodedLen) + fn on_idle_high_proof_waste() -> Weight { + // Proof Size summary in bytes: + // Measured: `1955391` + // Estimated: `5242806` + // Minimum execution time: 55_398_405 nanoseconds. + Weight::from_ref_time(55_594_848_000) + .saturating_add(Weight::from_proof_size(5242806)) + .saturating_add(T::DbWeight::get().reads(1740_u64)) + } + /// Storage: Glutton Storage (r:1 w:0) + /// Proof: Glutton Storage (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) + /// Storage: Glutton Compute (r:1 w:0) + /// Proof: Glutton Compute (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) + /// Storage: Glutton TrashData (r:6 w:0) + /// Proof: Glutton TrashData (max_values: Some(65000), max_size: Some(1036), added: 3016, mode: MaxEncodedLen) + fn on_idle_low_proof_waste() -> Weight { + // Proof Size summary in bytes: + // Measured: `11717` + // Estimated: `19094` + // Minimum execution time: 98_888_667 nanoseconds. + Weight::from_ref_time(99_157_239_000) + .saturating_add(Weight::from_proof_size(19094)) + .saturating_add(T::DbWeight::get().reads(8_u64)) + } + /// Storage: Glutton Storage (r:1 w:0) + /// Proof: Glutton Storage (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) + /// Storage: Glutton Compute (r:1 w:0) + /// Proof: Glutton Compute (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) + fn empty_on_idle() -> Weight { + // Proof Size summary in bytes: + // Measured: `4` + // Estimated: `998` + // Minimum execution time: 3_967 nanoseconds. + Weight::from_ref_time(4_121_000) + .saturating_add(Weight::from_proof_size(998)) + .saturating_add(T::DbWeight::get().reads(2_u64)) + } + /// Storage: Glutton Compute (r:0 w:1) + /// Proof: Glutton Compute (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) + fn set_compute() -> Weight { + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 7_112 nanoseconds. + Weight::from_ref_time(7_484_000) + .saturating_add(Weight::from_proof_size(0)) + .saturating_add(T::DbWeight::get().writes(1_u64)) + } + /// Storage: Glutton Storage (r:0 w:1) + /// Proof: Glutton Storage (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) + fn set_storage() -> Weight { + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 7_056 nanoseconds. + Weight::from_ref_time(7_414_000) + .saturating_add(Weight::from_proof_size(0)) + .saturating_add(T::DbWeight::get().writes(1_u64)) + } +} + +// For backwards compatibility and tests +impl WeightInfo for () { + /// Storage: Glutton TrashDataCount (r:1 w:1) + /// Proof: Glutton TrashDataCount (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) + /// Storage: Glutton TrashData (r:0 w:1000) + /// Proof: Glutton TrashData (max_values: Some(65000), max_size: Some(1036), added: 3016, mode: MaxEncodedLen) + /// The range of component `n` is `[0, 1000]`. + fn initialize_pallet_grow(n: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `4` + // Estimated: `499` + // Minimum execution time: 9_248 nanoseconds. + Weight::from_ref_time(9_582_000) + .saturating_add(Weight::from_proof_size(499)) + // Standard Error: 1_144 + .saturating_add(Weight::from_ref_time(1_624_225).saturating_mul(n.into())) + .saturating_add(RocksDbWeight::get().reads(1_u64)) + .saturating_add(RocksDbWeight::get().writes(1_u64)) + .saturating_add(RocksDbWeight::get().writes((1_u64).saturating_mul(n.into()))) + } + /// Storage: Glutton TrashDataCount (r:1 w:1) + /// Proof: Glutton TrashDataCount (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) + /// Storage: Glutton TrashData (r:0 w:1000) + /// Proof: Glutton TrashData (max_values: Some(65000), max_size: Some(1036), added: 3016, mode: MaxEncodedLen) + /// The range of component `n` is `[0, 1000]`. + fn initialize_pallet_shrink(n: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `65` + // Estimated: `499` + // Minimum execution time: 10_210 nanoseconds. + Weight::from_ref_time(7_260_854) + .saturating_add(Weight::from_proof_size(499)) + // Standard Error: 1_459 + .saturating_add(Weight::from_ref_time(1_053_844).saturating_mul(n.into())) + .saturating_add(RocksDbWeight::get().reads(1_u64)) + .saturating_add(RocksDbWeight::get().writes(1_u64)) + .saturating_add(RocksDbWeight::get().writes((1_u64).saturating_mul(n.into()))) + } + /// The range of component `i` is `[0, 100000]`. + fn waste_ref_time_iter(i: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 605 nanoseconds. + Weight::from_ref_time(1_102_112) + .saturating_add(Weight::from_proof_size(0)) + // Standard Error: 28 + .saturating_add(Weight::from_ref_time(120_597).saturating_mul(i.into())) + } + /// Storage: Glutton TrashData (r:5000 w:0) + /// Proof: Glutton TrashData (max_values: Some(65000), max_size: Some(1036), added: 3016, mode: MaxEncodedLen) + /// The range of component `i` is `[0, 5000]`. + fn waste_proof_size_some(i: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `119036 + i * (1053 ±0)` + // Estimated: `0 + i * (3016 ±0)` + // Minimum execution time: 464 nanoseconds. + Weight::from_ref_time(552_000) + .saturating_add(Weight::from_proof_size(0)) + // Standard Error: 1_962 + .saturating_add(Weight::from_ref_time(5_780_304).saturating_mul(i.into())) + .saturating_add(RocksDbWeight::get().reads((1_u64).saturating_mul(i.into()))) + .saturating_add(Weight::from_proof_size(3016).saturating_mul(i.into())) + } + /// Storage: Glutton Storage (r:1 w:0) + /// Proof: Glutton Storage (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) + /// Storage: Glutton Compute (r:1 w:0) + /// Proof: Glutton Compute (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) + /// Storage: Glutton TrashData (r:1738 w:0) + /// Proof: Glutton TrashData (max_values: Some(65000), max_size: Some(1036), added: 3016, mode: MaxEncodedLen) + fn on_idle_high_proof_waste() -> Weight { + // Proof Size summary in bytes: + // Measured: `1955391` + // Estimated: `5242806` + // Minimum execution time: 55_398_405 nanoseconds. + Weight::from_ref_time(55_594_848_000) + .saturating_add(Weight::from_proof_size(5242806)) + .saturating_add(RocksDbWeight::get().reads(1740_u64)) + } + /// Storage: Glutton Storage (r:1 w:0) + /// Proof: Glutton Storage (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) + /// Storage: Glutton Compute (r:1 w:0) + /// Proof: Glutton Compute (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) + /// Storage: Glutton TrashData (r:6 w:0) + /// Proof: Glutton TrashData (max_values: Some(65000), max_size: Some(1036), added: 3016, mode: MaxEncodedLen) + fn on_idle_low_proof_waste() -> Weight { + // Proof Size summary in bytes: + // Measured: `11717` + // Estimated: `19094` + // Minimum execution time: 98_888_667 nanoseconds. + Weight::from_ref_time(99_157_239_000) + .saturating_add(Weight::from_proof_size(19094)) + .saturating_add(RocksDbWeight::get().reads(8_u64)) + } + /// Storage: Glutton Storage (r:1 w:0) + /// Proof: Glutton Storage (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) + /// Storage: Glutton Compute (r:1 w:0) + /// Proof: Glutton Compute (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) + fn empty_on_idle() -> Weight { + // Proof Size summary in bytes: + // Measured: `4` + // Estimated: `998` + // Minimum execution time: 3_967 nanoseconds. + Weight::from_ref_time(4_121_000) + .saturating_add(Weight::from_proof_size(998)) + .saturating_add(RocksDbWeight::get().reads(2_u64)) + } + /// Storage: Glutton Compute (r:0 w:1) + /// Proof: Glutton Compute (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) + fn set_compute() -> Weight { + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 7_112 nanoseconds. + Weight::from_ref_time(7_484_000) + .saturating_add(Weight::from_proof_size(0)) + .saturating_add(RocksDbWeight::get().writes(1_u64)) + } + /// Storage: Glutton Storage (r:0 w:1) + /// Proof: Glutton Storage (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) + fn set_storage() -> Weight { + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 7_056 nanoseconds. + Weight::from_ref_time(7_414_000) + .saturating_add(Weight::from_proof_size(0)) + .saturating_add(RocksDbWeight::get().writes(1_u64)) + } +} From b77175df6b7c73e0f56bb38aca875f8096b25379 Mon Sep 17 00:00:00 2001 From: Daniel Shiposha Date: Tue, 21 Feb 2023 22:56:07 +0000 Subject: [PATCH 150/558] Introduce EnsureOrigin to democracy.propose (#13410) * feat: add submitorigin to democracy * fix: democracy tests * fix: SubmitOrigin doc comment Co-authored-by: Kian Paimani <5588131+kianenigma@users.noreply.github.com> --------- Co-authored-by: Kian Paimani <5588131+kianenigma@users.noreply.github.com> --- bin/node/runtime/src/lib.rs | 1 + frame/democracy/src/lib.rs | 7 ++++++- frame/democracy/src/tests.rs | 3 ++- 3 files changed, 9 insertions(+), 2 deletions(-) diff --git a/bin/node/runtime/src/lib.rs b/bin/node/runtime/src/lib.rs index 3e3a0d753..e02792b95 100644 --- a/bin/node/runtime/src/lib.rs +++ b/bin/node/runtime/src/lib.rs @@ -948,6 +948,7 @@ impl pallet_democracy::Config for Runtime { /// (NTB) vote. type ExternalDefaultOrigin = pallet_collective::EnsureProportionAtLeast; + type SubmitOrigin = EnsureSigned; /// Two thirds of the technical committee can have an ExternalMajority/ExternalDefault vote /// be tabled immediately and with a shorter voting/enactment period. type FastTrackOrigin = diff --git a/frame/democracy/src/lib.rs b/frame/democracy/src/lib.rs index b3ac3dfe7..e1e111e96 100644 --- a/frame/democracy/src/lib.rs +++ b/frame/democracy/src/lib.rs @@ -308,6 +308,11 @@ pub mod pallet { /// of a negative-turnout-bias (default-carries) referendum. type ExternalDefaultOrigin: EnsureOrigin; + /// Origin from which the new proposal can be made. + /// + /// The success variant is the account id of the depositor. + type SubmitOrigin: EnsureOrigin; + /// Origin from which the next majority-carries (or more permissive) referendum may be /// tabled to vote according to the `FastTrackVotingPeriod` asynchronously in a similar /// manner to the emergency origin. It retains its threshold method. @@ -590,7 +595,7 @@ pub mod pallet { proposal: BoundedCallOf, #[pallet::compact] value: BalanceOf, ) -> DispatchResult { - let who = ensure_signed(origin)?; + let who = T::SubmitOrigin::ensure_origin(origin)?; ensure!(value >= T::MinimumDeposit::get(), Error::::ValueLow); let index = Self::public_prop_count(); diff --git a/frame/democracy/src/tests.rs b/frame/democracy/src/tests.rs index eb1d2650a..9e7357659 100644 --- a/frame/democracy/src/tests.rs +++ b/frame/democracy/src/tests.rs @@ -27,7 +27,7 @@ use frame_support::{ }, weights::Weight, }; -use frame_system::{EnsureRoot, EnsureSignedBy}; +use frame_system::{EnsureRoot, EnsureSigned, EnsureSignedBy}; use pallet_balances::{BalanceLock, Error as BalancesError}; use sp_core::H256; use sp_runtime::{ @@ -177,6 +177,7 @@ impl Config for Test { type MinimumDeposit = ConstU64<1>; type MaxDeposits = ConstU32<1000>; type MaxBlacklisted = ConstU32<5>; + type SubmitOrigin = EnsureSigned; type ExternalOrigin = EnsureSignedBy; type ExternalMajorityOrigin = EnsureSignedBy; type ExternalDefaultOrigin = EnsureSignedBy; From 86c6bb9614c437b63f3dbd2afddef52f32af7866 Mon Sep 17 00:00:00 2001 From: Koute Date: Wed, 22 Feb 2023 16:49:25 +0900 Subject: [PATCH 151/558] Rework storage iterators (#13284) * Rework storage iterators * Make sure storage iteration is also accounted for when benchmarking * Use `trie-db` from crates.io * Appease clippy * Bump `trie-bench` to 0.35.0 * Fix tests' compilation * Update comment to clarify how `IterArgs::start_at` works * Add extra tests * Fix iterators on `Client` so that they behave as before * Add extra `unwrap`s in tests * More clippy fixes * Come on clippy, give me a break already * Rename `allow_missing` to `stop_on_incomplete_database` * Add `#[inline]` to `with_recorder_and_cache` * Use `with_recorder_and_cache` in `with_trie_db`; add doc comment * Simplify code: use `with_trie_db` in `next_storage_key_from_root` * Remove `expect`s in the benchmarking CLI * Add extra doc comments * Move `RawIter` before `TrieBackendEssence` (no code changes; just cut-paste) * Remove a TODO in tests * Update comment for `StorageIterator::was_complete` * Update `trie-db` to 0.25.1 --- Cargo.lock | 14 +- client/api/src/backend.rs | 186 +++++--- client/db/Cargo.toml | 1 + client/db/src/bench.rs | 228 ++++++---- client/db/src/lib.rs | 82 ++-- client/db/src/record_stats_state.rs | 86 ++-- client/rpc/src/state/state_full.rs | 22 +- .../service/src/chain_ops/export_raw_state.rs | 46 +- client/service/src/client/client.rs | 62 +-- client/service/test/src/client/mod.rs | 102 ++++- primitives/state-machine/Cargo.toml | 6 +- primitives/state-machine/src/backend.rs | 236 +++++++++- primitives/state-machine/src/ext.rs | 55 ++- primitives/state-machine/src/lib.rs | 2 +- primitives/state-machine/src/testing.rs | 7 +- primitives/state-machine/src/trie_backend.rs | 236 +++++++--- .../state-machine/src/trie_backend_essence.rs | 413 +++++++----------- primitives/storage/src/lib.rs | 1 + primitives/trie/Cargo.toml | 4 +- primitives/trie/src/cache/mod.rs | 4 +- primitives/trie/src/lib.rs | 3 +- primitives/trie/src/recorder.rs | 3 +- test-utils/runtime/Cargo.toml | 2 +- .../frame/benchmarking-cli/src/storage/cmd.rs | 4 +- .../benchmarking-cli/src/storage/read.rs | 7 +- .../benchmarking-cli/src/storage/write.rs | 7 +- .../rpc/state-trie-migration-rpc/Cargo.toml | 2 +- 27 files changed, 1088 insertions(+), 733 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 7c0b40cd9..0f271f807 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2902,6 +2902,9 @@ name = "hashbrown" version = "0.13.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "43a3c133739dddd0d2990f9a4bdf8eb4b21ef50e4851ca85ab661199821d510e" +dependencies = [ + "ahash 0.8.3", +] [[package]] name = "heck" @@ -8090,6 +8093,7 @@ dependencies = [ name = "sc-client-db" version = "0.10.0-dev" dependencies = [ + "array-bytes", "criterion", "hash-db", "kitchensink-runtime", @@ -11354,9 +11358,9 @@ dependencies = [ [[package]] name = "trie-bench" -version = "0.34.0" +version = "0.35.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2fbb0a830db7c42ae97ce4e21b30e2cf9dbcc1b4f7853bd1aedad3d806c281d0" +checksum = "22c1d18c423077531e693e87ace54ed9e4af1e4ce0a3ea8c9aa6608741074e2b" dependencies = [ "criterion", "hash-db", @@ -11370,12 +11374,12 @@ dependencies = [ [[package]] name = "trie-db" -version = "0.24.0" +version = "0.25.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "004e1e8f92535694b4cb1444dc5a8073ecf0815e3357f729638b9f8fc4062908" +checksum = "3390c0409daaa6027d6681393316f4ccd3ff82e1590a1e4725014e3ae2bf1920" dependencies = [ "hash-db", - "hashbrown 0.12.3", + "hashbrown 0.13.2", "log", "rustc-hex", "smallvec", diff --git a/client/api/src/backend.rs b/client/api/src/backend.rs index bc799a418..486ac5061 100644 --- a/client/api/src/backend.rs +++ b/client/api/src/backend.rs @@ -31,14 +31,13 @@ use sp_runtime::{ Justification, Justifications, StateVersion, Storage, }; use sp_state_machine::{ - backend::AsTrieBackend, ChildStorageCollection, IndexOperation, OffchainChangesCollection, - StorageCollection, + backend::AsTrieBackend, ChildStorageCollection, IndexOperation, IterArgs, + OffchainChangesCollection, StorageCollection, StorageIterator, }; use sp_storage::{ChildInfo, StorageData, StorageKey}; use std::collections::{HashMap, HashSet}; pub use sp_state_machine::{Backend as StateBackend, KeyValueStates}; -use std::marker::PhantomData; /// Extracts the state backend type for the given backend. pub type StateBackendFor = >::State; @@ -303,32 +302,61 @@ pub trait AuxStore { } /// An `Iterator` that iterates keys in a given block under a prefix. -pub struct KeyIterator { +pub struct KeysIter +where + State: StateBackend>, + Block: BlockT, +{ + inner: >>::RawIter, state: State, - child_storage: Option, - prefix: Option, - current_key: Vec, - _phantom: PhantomData, + skip_if_first: Option, } -impl KeyIterator { - /// create a KeyIterator instance - pub fn new(state: State, prefix: Option, current_key: Vec) -> Self { - Self { state, child_storage: None, prefix, current_key, _phantom: PhantomData } +impl KeysIter +where + State: StateBackend>, + Block: BlockT, +{ + /// Create a new iterator over storage keys. + pub fn new( + state: State, + prefix: Option<&StorageKey>, + start_at: Option<&StorageKey>, + ) -> Result { + let mut args = IterArgs::default(); + args.prefix = prefix.as_ref().map(|prefix| prefix.0.as_slice()); + args.start_at = start_at.as_ref().map(|start_at| start_at.0.as_slice()); + + let start_at = args.start_at; + Ok(Self { + inner: state.raw_iter(args)?, + state, + skip_if_first: start_at.map(|key| StorageKey(key.to_vec())), + }) } - /// Create a `KeyIterator` instance for a child storage. + /// Create a new iterator over a child storage's keys. pub fn new_child( state: State, child_info: ChildInfo, - prefix: Option, - current_key: Vec, - ) -> Self { - Self { state, child_storage: Some(child_info), prefix, current_key, _phantom: PhantomData } + prefix: Option<&StorageKey>, + start_at: Option<&StorageKey>, + ) -> Result { + let mut args = IterArgs::default(); + args.prefix = prefix.as_ref().map(|prefix| prefix.0.as_slice()); + args.start_at = start_at.as_ref().map(|start_at| start_at.0.as_slice()); + args.child_info = Some(child_info); + + let start_at = args.start_at; + Ok(Self { + inner: state.raw_iter(args)?, + state, + skip_if_first: start_at.map(|key| StorageKey(key.to_vec())), + }) } } -impl Iterator for KeyIterator +impl Iterator for KeysIter where Block: BlockT, State: StateBackend>, @@ -336,25 +364,78 @@ where type Item = StorageKey; fn next(&mut self) -> Option { - let next_key = if let Some(child_info) = self.child_storage.as_ref() { - self.state.next_child_storage_key(child_info, &self.current_key) - } else { - self.state.next_storage_key(&self.current_key) + let key = self.inner.next_key(&self.state)?.ok().map(StorageKey)?; + + if let Some(skipped_key) = self.skip_if_first.take() { + if key == skipped_key { + return self.next() + } } - .ok() - .flatten()?; - // this terminates the iterator the first time it fails. - if let Some(ref prefix) = self.prefix { - if !next_key.starts_with(&prefix.0[..]) { - return None + + Some(key) + } +} + +/// An `Iterator` that iterates keys and values in a given block under a prefix. +pub struct PairsIter +where + State: StateBackend>, + Block: BlockT, +{ + inner: >>::RawIter, + state: State, + skip_if_first: Option, +} + +impl Iterator for PairsIter +where + Block: BlockT, + State: StateBackend>, +{ + type Item = (StorageKey, StorageData); + + fn next(&mut self) -> Option { + let (key, value) = self + .inner + .next_pair(&self.state)? + .ok() + .map(|(key, value)| (StorageKey(key), StorageData(value)))?; + + if let Some(skipped_key) = self.skip_if_first.take() { + if key == skipped_key { + return self.next() } } - self.current_key = next_key.clone(); - Some(StorageKey(next_key)) + + Some((key, value)) } } -/// Provides acess to storage primitives +impl PairsIter +where + State: StateBackend>, + Block: BlockT, +{ + /// Create a new iterator over storage key and value pairs. + pub fn new( + state: State, + prefix: Option<&StorageKey>, + start_at: Option<&StorageKey>, + ) -> Result { + let mut args = IterArgs::default(); + args.prefix = prefix.as_ref().map(|prefix| prefix.0.as_slice()); + args.start_at = start_at.as_ref().map(|start_at| start_at.0.as_slice()); + + let start_at = args.start_at; + Ok(Self { + inner: state.raw_iter(args)?, + state, + skip_if_first: start_at.map(|key| StorageKey(key.to_vec())), + }) + } +} + +/// Provides access to storage primitives pub trait StorageProvider> { /// Given a block's `Hash` and a key, return the value under the key in that block. fn storage( @@ -363,13 +444,6 @@ pub trait StorageProvider> { key: &StorageKey, ) -> sp_blockchain::Result>; - /// Given a block's `Hash` and a key prefix, return the matching storage keys in that block. - fn storage_keys( - &self, - hash: Block::Hash, - key_prefix: &StorageKey, - ) -> sp_blockchain::Result>; - /// Given a block's `Hash` and a key, return the value under the hash in that block. fn storage_hash( &self, @@ -377,22 +451,23 @@ pub trait StorageProvider> { key: &StorageKey, ) -> sp_blockchain::Result>; - /// Given a block's `Hash` and a key prefix, return the matching child storage keys and values - /// in that block. - fn storage_pairs( + /// Given a block's `Hash` and a key prefix, returns a `KeysIter` iterates matching storage + /// keys in that block. + fn storage_keys( &self, hash: Block::Hash, - key_prefix: &StorageKey, - ) -> sp_blockchain::Result>; + prefix: Option<&StorageKey>, + start_key: Option<&StorageKey>, + ) -> sp_blockchain::Result>; - /// Given a block's `Hash` and a key prefix, return a `KeyIterator` iterates matching storage - /// keys in that block. - fn storage_keys_iter( + /// Given a block's `Hash` and a key prefix, returns an iterator over the storage keys and + /// values in that block. + fn storage_pairs( &self, - hash: Block::Hash, + hash: ::Hash, prefix: Option<&StorageKey>, start_key: Option<&StorageKey>, - ) -> sp_blockchain::Result>; + ) -> sp_blockchain::Result>; /// Given a block's `Hash`, a key and a child storage key, return the value under the key in /// that block. @@ -403,24 +478,15 @@ pub trait StorageProvider> { key: &StorageKey, ) -> sp_blockchain::Result>; - /// Given a block's `Hash`, a key prefix, and a child storage key, return the matching child - /// storage keys. - fn child_storage_keys( - &self, - hash: Block::Hash, - child_info: &ChildInfo, - key_prefix: &StorageKey, - ) -> sp_blockchain::Result>; - /// Given a block's `Hash` and a key `prefix` and a child storage key, - /// return a `KeyIterator` that iterates matching storage keys in that block. - fn child_storage_keys_iter( + /// returns a `KeysIter` that iterates matching storage keys in that block. + fn child_storage_keys( &self, hash: Block::Hash, child_info: ChildInfo, prefix: Option<&StorageKey>, start_key: Option<&StorageKey>, - ) -> sp_blockchain::Result>; + ) -> sp_blockchain::Result>; /// Given a block's `Hash`, a key and a child storage key, return the hash under the key in that /// block. diff --git a/client/db/Cargo.toml b/client/db/Cargo.toml index 7a62cb1ea..73eca1dff 100644 --- a/client/db/Cargo.toml +++ b/client/db/Cargo.toml @@ -44,6 +44,7 @@ quickcheck = { version = "1.0.3", default-features = false } kitchensink-runtime = { path = "../../bin/node/runtime" } sp-tracing = { version = "6.0.0", path = "../../primitives/tracing" } substrate-test-runtime-client = { version = "2.0.0", path = "../../test-utils/runtime/client" } +array-bytes = "4.1" [features] default = [] diff --git a/client/db/src/bench.rs b/client/db/src/bench.rs index b8fb3b6ce..b1fe3f206 100644 --- a/client/db/src/bench.rs +++ b/client/db/src/bench.rs @@ -22,6 +22,7 @@ use crate::{DbState, DbStateBuilder}; use hash_db::{Hasher, Prefix}; use kvdb::{DBTransaction, KeyValueDB}; use linked_hash_map::LinkedHashMap; +use parking_lot::Mutex; use sp_core::{ hexdisplay::HexDisplay, storage::{ChildInfo, TrackedStorageKey}, @@ -31,7 +32,8 @@ use sp_runtime::{ StateVersion, Storage, }; use sp_state_machine::{ - backend::Backend as StateBackend, ChildStorageCollection, DBValue, StorageCollection, + backend::Backend as StateBackend, ChildStorageCollection, DBValue, IterArgs, StorageCollection, + StorageIterator, StorageKey, StorageValue, }; use sp_trie::{ cache::{CacheSize, SharedTrieCache}, @@ -59,6 +61,19 @@ impl sp_state_machine::Storage> for StorageDb, TrackedStorageKey>, + /// Key tracker for keys in a child trie. + /// Child trie are identified by their storage key (i.e. `ChildInfo::storage_key()`) + /// We track the total number of reads and writes to these keys, + /// not de-duplicated for repeats. + child_keys: LinkedHashMap, LinkedHashMap, TrackedStorageKey>>, +} + /// State that manages the backend database reference. Allows runtime to control the database. pub struct BenchmarkingState { root: Cell, @@ -67,22 +82,52 @@ pub struct BenchmarkingState { db: Cell>>, genesis: HashMap, (Vec, i32)>, record: Cell>>, - /// Key tracker for keys in the main trie. - /// We track the total number of reads and writes to these keys, - /// not de-duplicated for repeats. - main_key_tracker: RefCell, TrackedStorageKey>>, - /// Key tracker for keys in a child trie. - /// Child trie are identified by their storage key (i.e. `ChildInfo::storage_key()`) - /// We track the total number of reads and writes to these keys, - /// not de-duplicated for repeats. - child_key_tracker: RefCell, LinkedHashMap, TrackedStorageKey>>>, + key_tracker: Arc>, whitelist: RefCell>, proof_recorder: Option>>, proof_recorder_root: Cell, - enable_tracking: bool, shared_trie_cache: SharedTrieCache>, } +/// A raw iterator over the `BenchmarkingState`. +pub struct RawIter { + inner: as StateBackend>>::RawIter, + child_trie: Option>, + key_tracker: Arc>, +} + +impl StorageIterator> for RawIter { + type Backend = BenchmarkingState; + type Error = String; + + fn next_key(&mut self, backend: &Self::Backend) -> Option> { + match self.inner.next_key(backend.state.borrow().as_ref()?) { + Some(Ok(key)) => { + self.key_tracker.lock().add_read_key(self.child_trie.as_deref(), &key); + Some(Ok(key)) + }, + result => result, + } + } + + fn next_pair( + &mut self, + backend: &Self::Backend, + ) -> Option> { + match self.inner.next_pair(backend.state.borrow().as_ref()?) { + Some(Ok((key, value))) => { + self.key_tracker.lock().add_read_key(self.child_trie.as_deref(), &key); + Some(Ok((key, value))) + }, + result => result, + } + } + + fn was_complete(&self) -> bool { + self.inner.was_complete() + } +} + impl BenchmarkingState { /// Create a new instance that creates a database in a temporary dir. pub fn new( @@ -103,12 +148,14 @@ impl BenchmarkingState { genesis: Default::default(), genesis_root: Default::default(), record: Default::default(), - main_key_tracker: Default::default(), - child_key_tracker: Default::default(), + key_tracker: Arc::new(Mutex::new(KeyTracker { + main_keys: Default::default(), + child_keys: Default::default(), + enable_tracking, + })), whitelist: Default::default(), proof_recorder: record_proof.then(Default::default), proof_recorder_root: Cell::new(root), - enable_tracking, // Enable the cache, but do not sync anything to the shared state. shared_trie_cache: SharedTrieCache::new(CacheSize::new(0)), }; @@ -123,7 +170,7 @@ impl BenchmarkingState { ) }); let (root, transaction): (B::Hash, _) = - state.state.borrow_mut().as_mut().unwrap().full_storage_root( + state.state.borrow().as_ref().unwrap().full_storage_root( genesis.top.iter().map(|(k, v)| (k.as_ref(), Some(v.as_ref()))), child_delta, state_version, @@ -157,36 +204,51 @@ impl BenchmarkingState { } fn add_whitelist_to_tracker(&self) { - let mut main_key_tracker = self.main_key_tracker.borrow_mut(); + self.key_tracker.lock().add_whitelist(&self.whitelist.borrow()); + } + + fn wipe_tracker(&self) { + let mut key_tracker = self.key_tracker.lock(); + key_tracker.main_keys = LinkedHashMap::new(); + key_tracker.child_keys = LinkedHashMap::new(); + key_tracker.add_whitelist(&self.whitelist.borrow()); + } + + fn add_read_key(&self, childtrie: Option<&[u8]>, key: &[u8]) { + self.key_tracker.lock().add_read_key(childtrie, key); + } + + fn add_write_key(&self, childtrie: Option<&[u8]>, key: &[u8]) { + self.key_tracker.lock().add_write_key(childtrie, key); + } - let whitelist = self.whitelist.borrow(); + fn all_trackers(&self) -> Vec { + self.key_tracker.lock().all_trackers() + } +} +impl KeyTracker { + fn add_whitelist(&mut self, whitelist: &[TrackedStorageKey]) { whitelist.iter().for_each(|key| { let mut whitelisted = TrackedStorageKey::new(key.key.clone()); whitelisted.whitelist(); - main_key_tracker.insert(key.key.clone(), whitelisted); + self.main_keys.insert(key.key.clone(), whitelisted); }); } - fn wipe_tracker(&self) { - *self.main_key_tracker.borrow_mut() = LinkedHashMap::new(); - *self.child_key_tracker.borrow_mut() = LinkedHashMap::new(); - self.add_whitelist_to_tracker(); - } - // Childtrie is identified by its storage key (i.e. `ChildInfo::storage_key`) - fn add_read_key(&self, childtrie: Option<&[u8]>, key: &[u8]) { + fn add_read_key(&mut self, childtrie: Option<&[u8]>, key: &[u8]) { if !self.enable_tracking { return } - let mut child_key_tracker = self.child_key_tracker.borrow_mut(); - let mut main_key_tracker = self.main_key_tracker.borrow_mut(); + let child_key_tracker = &mut self.child_keys; + let main_key_tracker = &mut self.main_keys; let key_tracker = if let Some(childtrie) = childtrie { child_key_tracker.entry(childtrie.to_vec()).or_insert_with(LinkedHashMap::new) } else { - &mut main_key_tracker + main_key_tracker }; let should_log = match key_tracker.get_mut(key) { @@ -216,18 +278,18 @@ impl BenchmarkingState { } // Childtrie is identified by its storage key (i.e. `ChildInfo::storage_key`) - fn add_write_key(&self, childtrie: Option<&[u8]>, key: &[u8]) { + fn add_write_key(&mut self, childtrie: Option<&[u8]>, key: &[u8]) { if !self.enable_tracking { return } - let mut child_key_tracker = self.child_key_tracker.borrow_mut(); - let mut main_key_tracker = self.main_key_tracker.borrow_mut(); + let child_key_tracker = &mut self.child_keys; + let main_key_tracker = &mut self.main_keys; let key_tracker = if let Some(childtrie) = childtrie { child_key_tracker.entry(childtrie.to_vec()).or_insert_with(LinkedHashMap::new) } else { - &mut main_key_tracker + main_key_tracker }; // If we have written to the key, we also consider that we have read from it. @@ -261,11 +323,11 @@ impl BenchmarkingState { fn all_trackers(&self) -> Vec { let mut all_trackers = Vec::new(); - self.main_key_tracker.borrow().iter().for_each(|(_, tracker)| { + self.main_keys.iter().for_each(|(_, tracker)| { all_trackers.push(tracker.clone()); }); - self.child_key_tracker.borrow().iter().for_each(|(_, child_tracker)| { + self.child_keys.iter().for_each(|(_, child_tracker)| { child_tracker.iter().for_each(|(_, tracker)| { all_trackers.push(tracker.clone()); }); @@ -283,6 +345,7 @@ impl StateBackend> for BenchmarkingState { type Error = as StateBackend>>::Error; type Transaction = as StateBackend>>::Transaction; type TrieBackendStorage = as StateBackend>>::TrieBackendStorage; + type RawIter = RawIter; fn storage(&self, key: &[u8]) -> Result>, Self::Error> { self.add_read_key(None, key); @@ -356,58 +419,6 @@ impl StateBackend> for BenchmarkingState { .next_child_storage_key(child_info, key) } - fn for_keys_with_prefix(&self, prefix: &[u8], f: F) { - if let Some(ref state) = *self.state.borrow() { - state.for_keys_with_prefix(prefix, f) - } - } - - fn for_key_values_with_prefix(&self, prefix: &[u8], f: F) { - if let Some(ref state) = *self.state.borrow() { - state.for_key_values_with_prefix(prefix, f) - } - } - - fn apply_to_key_values_while, Vec) -> bool>( - &self, - child_info: Option<&ChildInfo>, - prefix: Option<&[u8]>, - start_at: Option<&[u8]>, - f: F, - allow_missing: bool, - ) -> Result { - self.state.borrow().as_ref().ok_or_else(state_err)?.apply_to_key_values_while( - child_info, - prefix, - start_at, - f, - allow_missing, - ) - } - - fn apply_to_keys_while bool>( - &self, - child_info: Option<&ChildInfo>, - prefix: Option<&[u8]>, - start_at: Option<&[u8]>, - f: F, - ) { - if let Some(ref state) = *self.state.borrow() { - state.apply_to_keys_while(child_info, prefix, start_at, f) - } - } - - fn for_child_keys_with_prefix( - &self, - child_info: &ChildInfo, - prefix: &[u8], - f: F, - ) { - if let Some(ref state) = *self.state.borrow() { - state.for_child_keys_with_prefix(child_info, prefix, f) - } - } - fn storage_root<'a>( &self, delta: impl Iterator)>, @@ -437,19 +448,19 @@ impl StateBackend> for BenchmarkingState { .map_or(Default::default(), |s| s.child_storage_root(child_info, delta, state_version)) } - fn pairs(&self) -> Vec<(Vec, Vec)> { - self.state.borrow().as_ref().map_or(Default::default(), |s| s.pairs()) - } - - fn keys(&self, prefix: &[u8]) -> Vec> { - self.state.borrow().as_ref().map_or(Default::default(), |s| s.keys(prefix)) - } - - fn child_keys(&self, child_info: &ChildInfo, prefix: &[u8]) -> Vec> { + fn raw_iter(&self, args: IterArgs) -> Result { + let child_trie = + args.child_info.as_ref().map(|child_info| child_info.storage_key().to_vec()); self.state .borrow() .as_ref() - .map_or(Default::default(), |s| s.child_keys(child_info, prefix)) + .map(|s| s.raw_iter(args)) + .unwrap_or(Ok(Default::default())) + .map(|raw_iter| RawIter { + inner: raw_iter, + key_tracker: self.key_tracker.clone(), + child_trie, + }) } fn commit( @@ -587,7 +598,7 @@ impl StateBackend> for BenchmarkingState { } fn register_overlay_stats(&self, stats: &sp_state_machine::StateMachineStats) { - self.state.borrow_mut().as_mut().map(|s| s.register_overlay_stats(stats)); + self.state.borrow().as_ref().map(|s| s.register_overlay_stats(stats)); } fn usage_info(&self) -> sp_state_machine::UsageInfo { @@ -639,6 +650,29 @@ mod test { use crate::bench::BenchmarkingState; use sp_state_machine::backend::Backend as _; + fn hex(hex: &str) -> Vec { + array_bytes::hex2bytes(hex).unwrap() + } + + #[test] + fn iteration_is_also_counted_in_rw_counts() { + let storage = sp_runtime::Storage { + top: vec![( + hex("ce6e1397e668c7fcf47744350dc59688455a2c2dbd2e2a649df4e55d93cd7158"), + hex("0102030405060708"), + )] + .into_iter() + .collect(), + ..sp_runtime::Storage::default() + }; + let bench_state = + BenchmarkingState::::new(storage, None, false, true).unwrap(); + + assert_eq!(bench_state.read_write_count(), (0, 0, 0, 0)); + assert_eq!(bench_state.keys(Default::default()).unwrap().count(), 1); + assert_eq!(bench_state.read_write_count(), (1, 0, 0, 0)); + } + #[test] fn read_to_main_and_child_tries() { let bench_state = diff --git a/client/db/src/lib.rs b/client/db/src/lib.rs index 24c66fd77..b78c66228 100644 --- a/client/db/src/lib.rs +++ b/client/db/src/lib.rs @@ -86,8 +86,9 @@ use sp_runtime::{ }; use sp_state_machine::{ backend::{AsTrieBackend, Backend as StateBackend}, - ChildStorageCollection, DBValue, IndexOperation, OffchainChangesCollection, StateMachineStats, - StorageCollection, UsageInfo as StateUsageInfo, + ChildStorageCollection, DBValue, IndexOperation, IterArgs, OffchainChangesCollection, + StateMachineStats, StorageCollection, StorageIterator, StorageKey, StorageValue, + UsageInfo as StateUsageInfo, }; use sp_trie::{cache::SharedTrieCache, prefixed_key, MemoryDB, PrefixedMemoryDB}; @@ -159,10 +160,36 @@ impl std::fmt::Debug for RefTrackingState { } } +/// A raw iterator over the `RefTrackingState`. +pub struct RawIter { + inner: as StateBackend>>::RawIter, +} + +impl StorageIterator> for RawIter { + type Backend = RefTrackingState; + type Error = as StateBackend>>::Error; + + fn next_key(&mut self, backend: &Self::Backend) -> Option> { + self.inner.next_key(&backend.state) + } + + fn next_pair( + &mut self, + backend: &Self::Backend, + ) -> Option> { + self.inner.next_pair(&backend.state) + } + + fn was_complete(&self) -> bool { + self.inner.was_complete() + } +} + impl StateBackend> for RefTrackingState { type Error = as StateBackend>>::Error; type Transaction = as StateBackend>>::Transaction; type TrieBackendStorage = as StateBackend>>::TrieBackendStorage; + type RawIter = RawIter; fn storage(&self, key: &[u8]) -> Result>, Self::Error> { self.state.storage(key) @@ -212,45 +239,6 @@ impl StateBackend> for RefTrackingState { self.state.next_child_storage_key(child_info, key) } - fn for_keys_with_prefix(&self, prefix: &[u8], f: F) { - self.state.for_keys_with_prefix(prefix, f) - } - - fn for_key_values_with_prefix(&self, prefix: &[u8], f: F) { - self.state.for_key_values_with_prefix(prefix, f) - } - - fn apply_to_key_values_while, Vec) -> bool>( - &self, - child_info: Option<&ChildInfo>, - prefix: Option<&[u8]>, - start_at: Option<&[u8]>, - f: F, - allow_missing: bool, - ) -> Result { - self.state - .apply_to_key_values_while(child_info, prefix, start_at, f, allow_missing) - } - - fn apply_to_keys_while bool>( - &self, - child_info: Option<&ChildInfo>, - prefix: Option<&[u8]>, - start_at: Option<&[u8]>, - f: F, - ) { - self.state.apply_to_keys_while(child_info, prefix, start_at, f) - } - - fn for_child_keys_with_prefix( - &self, - child_info: &ChildInfo, - prefix: &[u8], - f: F, - ) { - self.state.for_child_keys_with_prefix(child_info, prefix, f) - } - fn storage_root<'a>( &self, delta: impl Iterator)>, @@ -274,16 +262,8 @@ impl StateBackend> for RefTrackingState { self.state.child_storage_root(child_info, delta, state_version) } - fn pairs(&self) -> Vec<(Vec, Vec)> { - self.state.pairs() - } - - fn keys(&self, prefix: &[u8]) -> Vec> { - self.state.keys(prefix) - } - - fn child_keys(&self, child_info: &ChildInfo, prefix: &[u8]) -> Vec> { - self.state.child_keys(child_info, prefix) + fn raw_iter(&self, args: IterArgs) -> Result { + self.state.raw_iter(args).map(|inner| RawIter { inner }) } fn register_overlay_stats(&self, stats: &StateMachineStats) { diff --git a/client/db/src/record_stats_state.rs b/client/db/src/record_stats_state.rs index 91d6c2fe5..2776802dd 100644 --- a/client/db/src/record_stats_state.rs +++ b/client/db/src/record_stats_state.rs @@ -26,7 +26,7 @@ use sp_runtime::{ }; use sp_state_machine::{ backend::{AsTrieBackend, Backend as StateBackend}, - TrieBackend, + IterArgs, StorageIterator, StorageKey, StorageValue, TrieBackend, }; use std::sync::Arc; @@ -73,10 +73,43 @@ impl>, B: BlockT> RecordStatsState { } } +pub struct RawIter +where + S: StateBackend>, + B: BlockT, +{ + inner: >>::RawIter, +} + +impl StorageIterator> for RawIter +where + S: StateBackend>, + B: BlockT, +{ + type Backend = RecordStatsState; + type Error = S::Error; + + fn next_key(&mut self, backend: &Self::Backend) -> Option> { + self.inner.next_key(&backend.state) + } + + fn next_pair( + &mut self, + backend: &Self::Backend, + ) -> Option> { + self.inner.next_pair(&backend.state) + } + + fn was_complete(&self) -> bool { + self.inner.was_complete() + } +} + impl>, B: BlockT> StateBackend> for RecordStatsState { type Error = S::Error; type Transaction = S::Transaction; type TrieBackendStorage = S::TrieBackendStorage; + type RawIter = RawIter; fn storage(&self, key: &[u8]) -> Result>, Self::Error> { let value = self.state.storage(key)?; @@ -122,28 +155,6 @@ impl>, B: BlockT> StateBackend> for Record self.state.exists_child_storage(child_info, key) } - fn apply_to_key_values_while, Vec) -> bool>( - &self, - child_info: Option<&ChildInfo>, - prefix: Option<&[u8]>, - start_at: Option<&[u8]>, - f: F, - allow_missing: bool, - ) -> Result { - self.state - .apply_to_key_values_while(child_info, prefix, start_at, f, allow_missing) - } - - fn apply_to_keys_while bool>( - &self, - child_info: Option<&ChildInfo>, - prefix: Option<&[u8]>, - start_at: Option<&[u8]>, - f: F, - ) { - self.state.apply_to_keys_while(child_info, prefix, start_at, f) - } - fn next_storage_key(&self, key: &[u8]) -> Result>, Self::Error> { self.state.next_storage_key(key) } @@ -156,23 +167,6 @@ impl>, B: BlockT> StateBackend> for Record self.state.next_child_storage_key(child_info, key) } - fn for_keys_with_prefix(&self, prefix: &[u8], f: F) { - self.state.for_keys_with_prefix(prefix, f) - } - - fn for_key_values_with_prefix(&self, prefix: &[u8], f: F) { - self.state.for_key_values_with_prefix(prefix, f) - } - - fn for_child_keys_with_prefix( - &self, - child_info: &ChildInfo, - prefix: &[u8], - f: F, - ) { - self.state.for_child_keys_with_prefix(child_info, prefix, f) - } - fn storage_root<'a>( &self, delta: impl Iterator)>, @@ -196,16 +190,8 @@ impl>, B: BlockT> StateBackend> for Record self.state.child_storage_root(child_info, delta, state_version) } - fn pairs(&self) -> Vec<(Vec, Vec)> { - self.state.pairs() - } - - fn keys(&self, prefix: &[u8]) -> Vec> { - self.state.keys(prefix) - } - - fn child_keys(&self, child_info: &ChildInfo, prefix: &[u8]) -> Vec> { - self.state.child_keys(child_info, prefix) + fn raw_iter(&self, args: IterArgs) -> Result { + self.state.raw_iter(args).map(|inner| RawIter { inner }) } fn register_overlay_stats(&self, stats: &sp_state_machine::StateMachineStats) { diff --git a/client/rpc/src/state/state_full.rs b/client/rpc/src/state/state_full.rs index ffc97487a..efd208539 100644 --- a/client/rpc/src/state/state_full.rs +++ b/client/rpc/src/state/state_full.rs @@ -213,23 +213,29 @@ where .map_err(client_err) } + // TODO: This is horribly broken; either remove it, or make it streaming. fn storage_keys( &self, block: Option, prefix: StorageKey, ) -> std::result::Result, Error> { + // TODO: Remove the `.collect`. self.block_or_best(block) - .and_then(|block| self.client.storage_keys(block, &prefix)) + .and_then(|block| self.client.storage_keys(block, Some(&prefix), None)) + .map(|iter| iter.collect()) .map_err(client_err) } + // TODO: This is horribly broken; either remove it, or make it streaming. fn storage_pairs( &self, block: Option, prefix: StorageKey, ) -> std::result::Result, Error> { + // TODO: Remove the `.collect`. self.block_or_best(block) - .and_then(|block| self.client.storage_pairs(block, &prefix)) + .and_then(|block| self.client.storage_pairs(block, Some(&prefix), None)) + .map(|iter| iter.collect()) .map_err(client_err) } @@ -241,9 +247,7 @@ where start_key: Option, ) -> std::result::Result, Error> { self.block_or_best(block) - .and_then(|block| { - self.client.storage_keys_iter(block, prefix.as_ref(), start_key.as_ref()) - }) + .and_then(|block| self.client.storage_keys(block, prefix.as_ref(), start_key.as_ref())) .map(|iter| iter.take(count as usize).collect()) .map_err(client_err) } @@ -284,7 +288,7 @@ where } // The key doesn't point to anything, so it's probably a prefix. - let iter = match client.storage_keys_iter(block, Some(&key), None).map_err(client_err) { + let iter = match client.storage_keys(block, Some(&key), None).map_err(client_err) { Ok(iter) => iter, Err(e) => return Ok(Err(e)), }; @@ -531,6 +535,7 @@ where storage_key: PrefixedStorageKey, prefix: StorageKey, ) -> std::result::Result, Error> { + // TODO: Remove the `.collect`. self.block_or_best(block) .and_then(|block| { let child_info = match ChildType::from_prefixed_key(&storage_key) { @@ -538,8 +543,9 @@ where ChildInfo::new_default(storage_key), None => return Err(sp_blockchain::Error::InvalidChildStorageKey), }; - self.client.child_storage_keys(block, &child_info, &prefix) + self.client.child_storage_keys(block, child_info, Some(&prefix), None) }) + .map(|iter| iter.collect()) .map_err(client_err) } @@ -558,7 +564,7 @@ where ChildInfo::new_default(storage_key), None => return Err(sp_blockchain::Error::InvalidChildStorageKey), }; - self.client.child_storage_keys_iter( + self.client.child_storage_keys( block, child_info, prefix.as_ref(), diff --git a/client/service/src/chain_ops/export_raw_state.rs b/client/service/src/chain_ops/export_raw_state.rs index f5e154795..fde2c5617 100644 --- a/client/service/src/chain_ops/export_raw_state.rs +++ b/client/service/src/chain_ops/export_raw_state.rs @@ -21,7 +21,10 @@ use sc_client_api::{StorageProvider, UsageProvider}; use sp_core::storage::{well_known_keys, ChildInfo, Storage, StorageChild, StorageKey, StorageMap}; use sp_runtime::traits::Block as BlockT; -use std::{collections::HashMap, sync::Arc}; +use std::{ + collections::{BTreeMap, HashMap}, + sync::Arc, +}; /// Export the raw state at the given `block`. If `block` is `None`, the /// best block will be used. @@ -31,35 +34,30 @@ where B: BlockT, BA: sc_client_api::backend::Backend, { - let empty_key = StorageKey(Vec::new()); - let mut top_storage = client.storage_pairs(hash, &empty_key)?; + let mut top = BTreeMap::new(); let mut children_default = HashMap::new(); - // Remove all default child storage roots from the top storage and collect the child storage - // pairs. - while let Some(pos) = top_storage - .iter() - .position(|(k, _)| k.0.starts_with(well_known_keys::DEFAULT_CHILD_STORAGE_KEY_PREFIX)) - { - let (key, _) = top_storage.swap_remove(pos); - - let key = - StorageKey(key.0[well_known_keys::DEFAULT_CHILD_STORAGE_KEY_PREFIX.len()..].to_vec()); - let child_info = ChildInfo::new_default(&key.0); - - let keys = client.child_storage_keys(hash, &child_info, &empty_key)?; - let mut pairs = StorageMap::new(); - keys.into_iter().try_for_each(|k| { - if let Some(value) = client.child_storage(hash, &child_info, &k)? { - pairs.insert(k.0, value.0); + for (key, value) in client.storage_pairs(hash, None, None)? { + // Remove all default child storage roots from the top storage and collect the child storage + // pairs. + if key.0.starts_with(well_known_keys::DEFAULT_CHILD_STORAGE_KEY_PREFIX) { + let child_root_key = StorageKey( + key.0[well_known_keys::DEFAULT_CHILD_STORAGE_KEY_PREFIX.len()..].to_vec(), + ); + let child_info = ChildInfo::new_default(&child_root_key.0); + let mut pairs = StorageMap::new(); + for child_key in client.child_storage_keys(hash, child_info.clone(), None, None)? { + if let Some(child_value) = client.child_storage(hash, &child_info, &child_key)? { + pairs.insert(child_key.0, child_value.0); + } } - Ok::<_, Error>(()) - })?; + children_default.insert(child_root_key.0, StorageChild { child_info, data: pairs }); + continue + } - children_default.insert(key.0, StorageChild { child_info, data: pairs }); + top.insert(key.0, value.0); } - let top = top_storage.into_iter().map(|(k, v)| (k.0, v.0)).collect(); Ok(Storage { top, children_default }) } diff --git a/client/service/src/client/client.rs b/client/service/src/client/client.rs index 968044ba5..ddeec5c73 100644 --- a/client/service/src/client/client.rs +++ b/client/service/src/client/client.rs @@ -40,8 +40,8 @@ use sc_client_api::{ }, execution_extensions::ExecutionExtensions, notifications::{StorageEventStream, StorageNotifications}, - CallExecutor, ExecutorProvider, KeyIterator, OnFinalityAction, OnImportAction, ProofProvider, - UsageProvider, + CallExecutor, ExecutorProvider, KeysIter, OnFinalityAction, OnImportAction, PairsIter, + ProofProvider, UsageProvider, }; use sc_consensus::{ BlockCheckParams, BlockImportParams, ForkChoiceStrategy, ImportResult, StateAction, @@ -1462,52 +1462,37 @@ where Block: BlockT, { fn storage_keys( - &self, - hash: Block::Hash, - key_prefix: &StorageKey, - ) -> sp_blockchain::Result> { - let keys = self.state_at(hash)?.keys(&key_prefix.0).into_iter().map(StorageKey).collect(); - Ok(keys) - } - - fn storage_pairs( &self, hash: ::Hash, - key_prefix: &StorageKey, - ) -> sp_blockchain::Result> { + prefix: Option<&StorageKey>, + start_key: Option<&StorageKey>, + ) -> sp_blockchain::Result> { let state = self.state_at(hash)?; - let keys = state - .keys(&key_prefix.0) - .into_iter() - .map(|k| { - let d = state.storage(&k).ok().flatten().unwrap_or_default(); - (StorageKey(k), StorageData(d)) - }) - .collect(); - Ok(keys) + KeysIter::new(state, prefix, start_key) + .map_err(|e| sp_blockchain::Error::from_state(Box::new(e))) } - fn storage_keys_iter( + fn child_storage_keys( &self, hash: ::Hash, + child_info: ChildInfo, prefix: Option<&StorageKey>, start_key: Option<&StorageKey>, - ) -> sp_blockchain::Result> { + ) -> sp_blockchain::Result> { let state = self.state_at(hash)?; - let start_key = start_key.or(prefix).map(|key| key.0.clone()).unwrap_or_else(Vec::new); - Ok(KeyIterator::new(state, prefix.cloned(), start_key)) + KeysIter::new_child(state, child_info, prefix, start_key) + .map_err(|e| sp_blockchain::Error::from_state(Box::new(e))) } - fn child_storage_keys_iter( + fn storage_pairs( &self, hash: ::Hash, - child_info: ChildInfo, prefix: Option<&StorageKey>, start_key: Option<&StorageKey>, - ) -> sp_blockchain::Result> { + ) -> sp_blockchain::Result> { let state = self.state_at(hash)?; - let start_key = start_key.or(prefix).map(|key| key.0.clone()).unwrap_or_else(Vec::new); - Ok(KeyIterator::new_child(state, child_info, prefix.cloned(), start_key)) + PairsIter::new(state, prefix, start_key) + .map_err(|e| sp_blockchain::Error::from_state(Box::new(e))) } fn storage( @@ -1532,21 +1517,6 @@ where .map_err(|e| sp_blockchain::Error::from_state(Box::new(e))) } - fn child_storage_keys( - &self, - hash: ::Hash, - child_info: &ChildInfo, - key_prefix: &StorageKey, - ) -> sp_blockchain::Result> { - let keys = self - .state_at(hash)? - .child_keys(child_info, &key_prefix.0) - .into_iter() - .map(StorageKey) - .collect(); - Ok(keys) - } - fn child_storage( &self, hash: ::Hash, diff --git a/client/service/test/src/client/mod.rs b/client/service/test/src/client/mod.rs index 9d021be5b..b2b029f22 100644 --- a/client/service/test/src/client/mod.rs +++ b/client/service/test/src/client/mod.rs @@ -341,7 +341,20 @@ fn block_builder_works_with_transactions() { .expect("block 1 was just imported. qed"); assert_eq!(client.chain_info().best_number, 1); - assert_ne!(client.state_at(hash1).unwrap().pairs(), client.state_at(hash0).unwrap().pairs()); + assert_ne!( + client + .state_at(hash1) + .unwrap() + .pairs(Default::default()) + .unwrap() + .collect::>(), + client + .state_at(hash0) + .unwrap() + .pairs(Default::default()) + .unwrap() + .collect::>() + ); assert_eq!( client .runtime_api() @@ -394,8 +407,18 @@ fn block_builder_does_not_include_invalid() { assert_eq!(client.chain_info().best_number, 1); assert_ne!( - client.state_at(hashof1).unwrap().pairs(), - client.state_at(hashof0).unwrap().pairs() + client + .state_at(hashof1) + .unwrap() + .pairs(Default::default()) + .unwrap() + .collect::>(), + client + .state_at(hashof0) + .unwrap() + .pairs(Default::default()) + .unwrap() + .collect::>() ); assert_eq!(client.body(hashof1).unwrap().unwrap().len(), 1) } @@ -1688,7 +1711,7 @@ fn returns_status_for_pruned_blocks() { } #[test] -fn storage_keys_iter_prefix_and_start_key_works() { +fn storage_keys_prefix_and_start_key_works() { let child_info = ChildInfo::new_default(b"child"); let client = TestClientBuilder::new() .add_extra_child_storage(&child_info, b"first".to_vec(), vec![0u8; 32]) @@ -1703,7 +1726,7 @@ fn storage_keys_iter_prefix_and_start_key_works() { let child_prefix = StorageKey(b"sec".to_vec()); let res: Vec<_> = client - .storage_keys_iter(block_hash, Some(&prefix), None) + .storage_keys(block_hash, Some(&prefix), None) .unwrap() .map(|x| x.0) .collect(); @@ -1717,7 +1740,7 @@ fn storage_keys_iter_prefix_and_start_key_works() { ); let res: Vec<_> = client - .storage_keys_iter( + .storage_keys( block_hash, Some(&prefix), Some(&StorageKey(array_bytes::hex2bytes_unchecked("3a636f6465"))), @@ -1728,7 +1751,7 @@ fn storage_keys_iter_prefix_and_start_key_works() { assert_eq!(res, [array_bytes::hex2bytes_unchecked("3a686561707061676573")]); let res: Vec<_> = client - .storage_keys_iter( + .storage_keys( block_hash, Some(&prefix), Some(&StorageKey(array_bytes::hex2bytes_unchecked("3a686561707061676573"))), @@ -1739,19 +1762,14 @@ fn storage_keys_iter_prefix_and_start_key_works() { assert_eq!(res, Vec::>::new()); let res: Vec<_> = client - .child_storage_keys_iter(block_hash, child_info.clone(), Some(&child_prefix), None) + .child_storage_keys(block_hash, child_info.clone(), Some(&child_prefix), None) .unwrap() .map(|x| x.0) .collect(); assert_eq!(res, [b"second".to_vec()]); let res: Vec<_> = client - .child_storage_keys_iter( - block_hash, - child_info, - None, - Some(&StorageKey(b"second".to_vec())), - ) + .child_storage_keys(block_hash, child_info, None, Some(&StorageKey(b"second".to_vec()))) .unwrap() .map(|x| x.0) .collect(); @@ -1759,7 +1777,7 @@ fn storage_keys_iter_prefix_and_start_key_works() { } #[test] -fn storage_keys_iter_works() { +fn storage_keys_works() { let client = substrate_test_runtime_client::new(); let block_hash = client.info().best_hash; @@ -1767,7 +1785,7 @@ fn storage_keys_iter_works() { let prefix = StorageKey(array_bytes::hex2bytes_unchecked("")); let res: Vec<_> = client - .storage_keys_iter(block_hash, Some(&prefix), None) + .storage_keys(block_hash, Some(&prefix), None) .unwrap() .take(9) .map(|x| array_bytes::bytes2hex("", &x.0)) @@ -1787,8 +1805,56 @@ fn storage_keys_iter_works() { ] ); + // Starting at an empty key nothing gets skipped. + let res: Vec<_> = client + .storage_keys(block_hash, Some(&prefix), Some(&StorageKey("".into()))) + .unwrap() + .take(9) + .map(|x| array_bytes::bytes2hex("", &x.0)) + .collect(); + assert_eq!( + res, + [ + "00c232cf4e70a5e343317016dc805bf80a6a8cd8ad39958d56f99891b07851e0", + "085b2407916e53a86efeb8b72dbe338c4b341dab135252f96b6ed8022209b6cb", + "0befda6e1ca4ef40219d588a727f1271", + "1a560ecfd2a62c2b8521ef149d0804eb621050e3988ed97dca55f0d7c3e6aa34", + "1d66850d32002979d67dd29dc583af5b2ae2a1f71c1f35ad90fff122be7a3824", + "237498b98d8803334286e9f0483ef513098dd3c1c22ca21c4dc155b4ef6cc204", + "26aa394eea5630e07c48ae0c9558cef75e0621c4869aa60c02be9adcc98a0d1d", + "29b9db10ec5bf7907d8f74b5e60aa8140c4fbdd8127a1ee5600cb98e5ec01729", + "3a636f6465", + ] + ); + + // Starting at an incomplete key nothing gets skipped. + let res: Vec<_> = client + .storage_keys( + block_hash, + Some(&prefix), + Some(&StorageKey(array_bytes::hex2bytes_unchecked("3a636f64"))), + ) + .unwrap() + .take(8) + .map(|x| array_bytes::bytes2hex("", &x.0)) + .collect(); + assert_eq!( + res, + [ + "3a636f6465", + "3a686561707061676573", + "52008686cc27f6e5ed83a216929942f8bcd32a396f09664a5698f81371934b56", + "5348d72ac6cc66e5d8cbecc27b0e0677503b845fe2382d819f83001781788fd5", + "5c2d5fda66373dabf970e4fb13d277ce91c5233473321129d32b5a8085fa8133", + "6644b9b8bc315888ac8e41a7968dc2b4141a5403c58acdf70b7e8f7e07bf5081", + "66484000ed3f75c95fc7b03f39c20ca1e1011e5999278247d3b2f5e3c3273808", + "7d5007603a7f5dd729d51d93cf695d6465789443bb967c0d1fe270e388c96eaa", + ] + ); + + // Starting at a complete key the first key is skipped. let res: Vec<_> = client - .storage_keys_iter( + .storage_keys( block_hash, Some(&prefix), Some(&StorageKey(array_bytes::hex2bytes_unchecked("3a636f6465"))), @@ -1811,7 +1877,7 @@ fn storage_keys_iter_works() { ); let res: Vec<_> = client - .storage_keys_iter( + .storage_keys( block_hash, Some(&prefix), Some(&StorageKey(array_bytes::hex2bytes_unchecked( diff --git a/primitives/state-machine/Cargo.toml b/primitives/state-machine/Cargo.toml index 595dfe286..2969a51c4 100644 --- a/primitives/state-machine/Cargo.toml +++ b/primitives/state-machine/Cargo.toml @@ -16,7 +16,7 @@ targets = ["x86_64-unknown-linux-gnu"] [dependencies] codec = { package = "parity-scale-codec", version = "3.2.2", default-features = false } hash-db = { version = "0.15.2", default-features = false } -log = { version = "0.4.17", optional = true } +log = { version = "0.4.17", default-features = false } parking_lot = { version = "0.12.1", optional = true } rand = { version = "0.8.5", optional = true } smallvec = "1.8.0" @@ -33,7 +33,7 @@ array-bytes = "4.1" pretty_assertions = "1.2.1" rand = "0.8.5" sp-runtime = { version = "7.0.0", path = "../runtime" } -trie-db = "0.24.0" +trie-db = "0.25.1" assert_matches = "1.5" [features] @@ -41,7 +41,7 @@ default = ["std"] std = [ "codec/std", "hash-db/std", - "log", + "log/std", "parking_lot", "rand", "sp-core/std", diff --git a/primitives/state-machine/src/backend.rs b/primitives/state-machine/src/backend.rs index eeb264aed..a8e742d1d 100644 --- a/primitives/state-machine/src/backend.rs +++ b/primitives/state-machine/src/backend.rs @@ -24,12 +24,135 @@ use crate::{ StorageKey, StorageValue, UsageInfo, }; use codec::Encode; +use core::marker::PhantomData; use hash_db::Hasher; use sp_core::storage::{ChildInfo, StateVersion, TrackedStorageKey}; #[cfg(feature = "std")] use sp_core::traits::RuntimeCode; use sp_std::vec::Vec; +/// A struct containing arguments for iterating over the storage. +#[derive(Default)] +#[non_exhaustive] +pub struct IterArgs<'a> { + /// The prefix of the keys over which to iterate. + pub prefix: Option<&'a [u8]>, + + /// The prefix from which to start the iteration from. + /// + /// This is inclusive and the iteration will include the key which is specified here. + pub start_at: Option<&'a [u8]>, + + /// The info of the child trie over which to iterate over. + pub child_info: Option, + + /// Whether to stop iteration when a missing trie node is reached. + /// + /// When a missing trie node is reached the iterator will: + /// - return an error if this is set to `false` (default) + /// - return `None` if this is set to `true` + pub stop_on_incomplete_database: bool, +} + +/// A trait for a raw storage iterator. +pub trait StorageIterator +where + H: Hasher, +{ + /// The state backend over which the iterator is iterating. + type Backend; + + /// The error type. + type Error; + + /// Fetches the next key from the storage. + fn next_key( + &mut self, + backend: &Self::Backend, + ) -> Option>; + + /// Fetches the next key and value from the storage. + fn next_pair( + &mut self, + backend: &Self::Backend, + ) -> Option>; + + /// Returns whether the end of iteration was reached without an error. + fn was_complete(&self) -> bool; +} + +/// An iterator over storage keys and values. +pub struct PairsIter<'a, H, I> +where + H: Hasher, + I: StorageIterator, +{ + backend: Option<&'a I::Backend>, + raw_iter: I, + _phantom: PhantomData, +} + +impl<'a, H, I> Iterator for PairsIter<'a, H, I> +where + H: Hasher, + I: StorageIterator, +{ + type Item = Result<(Vec, Vec), >::Error>; + fn next(&mut self) -> Option { + self.raw_iter.next_pair(self.backend.as_ref()?) + } +} + +impl<'a, H, I> Default for PairsIter<'a, H, I> +where + H: Hasher, + I: StorageIterator + Default, +{ + fn default() -> Self { + Self { + backend: Default::default(), + raw_iter: Default::default(), + _phantom: Default::default(), + } + } +} + +/// An iterator over storage keys. +pub struct KeysIter<'a, H, I> +where + H: Hasher, + I: StorageIterator, +{ + backend: Option<&'a I::Backend>, + raw_iter: I, + _phantom: PhantomData, +} + +impl<'a, H, I> Iterator for KeysIter<'a, H, I> +where + H: Hasher, + I: StorageIterator, +{ + type Item = Result, >::Error>; + fn next(&mut self) -> Option { + self.raw_iter.next_key(self.backend.as_ref()?) + } +} + +impl<'a, H, I> Default for KeysIter<'a, H, I> +where + H: Hasher, + I: StorageIterator + Default, +{ + fn default() -> Self { + Self { + backend: Default::default(), + raw_iter: Default::default(), + _phantom: Default::default(), + } + } +} + /// A state backend is used to read state data and can have changes committed /// to it. /// @@ -44,6 +167,9 @@ pub trait Backend: sp_std::fmt::Debug { /// Type of trie backend storage. type TrieBackendStorage: TrieBackendStorage; + /// Type of the raw storage iterator. + type RawIter: StorageIterator; + /// Get keyed storage or None if there is nothing associated. fn storage(&self, key: &[u8]) -> Result, Self::Error>; @@ -95,43 +221,103 @@ pub trait Backend: sp_std::fmt::Debug { /// Otherwise an error is produced. /// /// Returns `true` if trie end is reached. + // TODO: Remove this. fn apply_to_key_values_while, Vec) -> bool>( &self, child_info: Option<&ChildInfo>, prefix: Option<&[u8]>, start_at: Option<&[u8]>, - f: F, + mut f: F, allow_missing: bool, - ) -> Result; + ) -> Result { + let args = IterArgs { + child_info: child_info.cloned(), + prefix, + start_at, + stop_on_incomplete_database: allow_missing, + ..IterArgs::default() + }; + let mut iter = self.pairs(args)?; + while let Some(key_value) = iter.next() { + let (key, value) = key_value?; + if !f(key, value) { + return Ok(false) + } + } + Ok(iter.raw_iter.was_complete()) + } /// Retrieve all entries keys of storage and call `f` for each of those keys. /// Aborts as soon as `f` returns false. + // TODO: Remove this. fn apply_to_keys_while bool>( &self, child_info: Option<&ChildInfo>, prefix: Option<&[u8]>, start_at: Option<&[u8]>, - f: F, - ); + mut f: F, + ) -> Result<(), Self::Error> { + let args = + IterArgs { child_info: child_info.cloned(), prefix, start_at, ..IterArgs::default() }; + + for key in self.keys(args)? { + if !f(&key?) { + return Ok(()) + } + } + Ok(()) + } /// Retrieve all entries keys which start with the given prefix and /// call `f` for each of those keys. - fn for_keys_with_prefix(&self, prefix: &[u8], mut f: F) { - self.for_key_values_with_prefix(prefix, |k, _v| f(k)) + // TODO: Remove this. + fn for_keys_with_prefix( + &self, + prefix: &[u8], + mut f: F, + ) -> Result<(), Self::Error> { + let args = IterArgs { prefix: Some(prefix), ..IterArgs::default() }; + self.keys(args)?.try_for_each(|key| { + f(&key?); + Ok(()) + }) } /// Retrieve all entries keys and values of which start with the given prefix and /// call `f` for each of those keys. - fn for_key_values_with_prefix(&self, prefix: &[u8], f: F); + // TODO: Remove this. + fn for_key_values_with_prefix( + &self, + prefix: &[u8], + mut f: F, + ) -> Result<(), Self::Error> { + let args = IterArgs { prefix: Some(prefix), ..IterArgs::default() }; + self.pairs(args)?.try_for_each(|key_value| { + let (key, value) = key_value?; + f(&key, &value); + Ok(()) + }) + } /// Retrieve all child entries keys which start with the given prefix and /// call `f` for each of those keys. + // TODO: Remove this. fn for_child_keys_with_prefix( &self, child_info: &ChildInfo, prefix: &[u8], - f: F, - ); + mut f: F, + ) -> Result<(), Self::Error> { + let args = IterArgs { + child_info: Some(child_info.clone()), + prefix: Some(prefix), + ..IterArgs::default() + }; + self.keys(args)?.try_for_each(|key| { + f(&key?); + Ok(()) + }) + } /// Calculate the storage root, with given delta over what is already stored in /// the backend, and produce a "transaction" that can be used to commit. @@ -156,21 +342,25 @@ pub trait Backend: sp_std::fmt::Debug { where H::Out: Ord; - /// Get all key/value pairs into a Vec. - fn pairs(&self) -> Vec<(StorageKey, StorageValue)>; + /// Returns a lifetimeless raw storage iterator. + fn raw_iter(&self, args: IterArgs) -> Result; - /// Get all keys with given prefix - fn keys(&self, prefix: &[u8]) -> Vec { - let mut all = Vec::new(); - self.for_keys_with_prefix(prefix, |k| all.push(k.to_vec())); - all + /// Get an iterator over key/value pairs. + fn pairs<'a>(&'a self, args: IterArgs) -> Result, Self::Error> { + Ok(PairsIter { + backend: Some(self), + raw_iter: self.raw_iter(args)?, + _phantom: Default::default(), + }) } - /// Get all keys of child storage with given prefix - fn child_keys(&self, child_info: &ChildInfo, prefix: &[u8]) -> Vec { - let mut all = Vec::new(); - self.for_child_keys_with_prefix(child_info, prefix, |k| all.push(k.to_vec())); - all + /// Get an iterator over keys. + fn keys<'a>(&'a self, args: IterArgs) -> Result, Self::Error> { + Ok(KeysIter { + backend: Some(self), + raw_iter: self.raw_iter(args)?, + _phantom: Default::default(), + }) } /// Calculate the storage root, with given delta over what is already stored @@ -309,7 +499,7 @@ where #[cfg(feature = "std")] pub struct BackendRuntimeCode<'a, B, H> { backend: &'a B, - _marker: std::marker::PhantomData, + _marker: PhantomData, } #[cfg(feature = "std")] @@ -332,7 +522,7 @@ where { /// Create a new instance. pub fn new(backend: &'a B) -> Self { - Self { backend, _marker: std::marker::PhantomData } + Self { backend, _marker: PhantomData } } /// Return the [`RuntimeCode`] build from the wrapped `backend`. diff --git a/primitives/state-machine/src/ext.rs b/primitives/state-machine/src/ext.rs index 548bab56a..695868c96 100644 --- a/primitives/state-machine/src/ext.rs +++ b/primitives/state-machine/src/ext.rs @@ -159,9 +159,10 @@ where use std::collections::HashMap; self.backend - .pairs() - .iter() - .map(|&(ref k, ref v)| (k.to_vec(), Some(v.to_vec()))) + .pairs(Default::default()) + .expect("never fails in tests; qed.") + .map(|key_value| key_value.expect("never fails in tests; qed.")) + .map(|(k, v)| (k, Some(v))) .chain(self.overlay.changes().map(|(k, v)| (k.clone(), v.value().cloned()))) .collect::>() .into_iter() @@ -757,28 +758,34 @@ where let mut delete_count: u32 = 0; let mut loop_count: u32 = 0; let mut maybe_next_key = None; - self.backend - .apply_to_keys_while(maybe_child, maybe_prefix, maybe_cursor, |key| { - if maybe_limit.map_or(false, |limit| loop_count == limit) { - maybe_next_key = Some(key.to_vec()); - return false - } - let overlay = match maybe_child { - Some(child_info) => self.overlay.child_storage(child_info, key), - None => self.overlay.storage(key), - }; - if !matches!(overlay, Some(None)) { - // not pending deletion from the backend - delete it. - if let Some(child_info) = maybe_child { - self.overlay.set_child_storage(child_info, key.to_vec(), None); - } else { - self.overlay.set_storage(key.to_vec(), None); + let result = + self.backend + .apply_to_keys_while(maybe_child, maybe_prefix, maybe_cursor, |key| { + if maybe_limit.map_or(false, |limit| loop_count == limit) { + maybe_next_key = Some(key.to_vec()); + return false } - delete_count = delete_count.saturating_add(1); - } - loop_count = loop_count.saturating_add(1); - true - }); + let overlay = match maybe_child { + Some(child_info) => self.overlay.child_storage(child_info, key), + None => self.overlay.storage(key), + }; + if !matches!(overlay, Some(None)) { + // not pending deletion from the backend - delete it. + if let Some(child_info) = maybe_child { + self.overlay.set_child_storage(child_info, key.to_vec(), None); + } else { + self.overlay.set_storage(key.to_vec(), None); + } + delete_count = delete_count.saturating_add(1); + } + loop_count = loop_count.saturating_add(1); + true + }); + + if let Err(error) = result { + log::debug!(target: "trie", "Error while iterating the storage: {}", error); + } + (maybe_next_key, delete_count, loop_count) } } diff --git a/primitives/state-machine/src/lib.rs b/primitives/state-machine/src/lib.rs index 94fede96f..538c63837 100644 --- a/primitives/state-machine/src/lib.rs +++ b/primitives/state-machine/src/lib.rs @@ -123,7 +123,7 @@ impl sp_std::fmt::Display for DefaultError { } pub use crate::{ - backend::Backend, + backend::{Backend, IterArgs, KeysIter, PairsIter, StorageIterator}, error::{Error, ExecutionError}, ext::Ext, overlayed_changes::{ diff --git a/primitives/state-machine/src/testing.rs b/primitives/state-machine/src/testing.rs index 23e0ed2b5..a9f6399a9 100644 --- a/primitives/state-machine/src/testing.rs +++ b/primitives/state-machine/src/testing.rs @@ -241,7 +241,12 @@ where H::Out: Ord + codec::Codec, { fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { - write!(f, "overlay: {:?}\nbackend: {:?}", self.overlay, self.backend.pairs()) + let pairs: Vec<_> = self + .backend + .pairs(Default::default()) + .expect("creating an iterator over all of the pairs doesn't fail in tests") + .collect(); + write!(f, "overlay: {:?}\nbackend: {:?}", self.overlay, pairs) } } diff --git a/primitives/state-machine/src/trie_backend.rs b/primitives/state-machine/src/trie_backend.rs index a42976fb5..ec6db9be3 100644 --- a/primitives/state-machine/src/trie_backend.rs +++ b/primitives/state-machine/src/trie_backend.rs @@ -20,6 +20,7 @@ #[cfg(feature = "std")] use crate::backend::AsTrieBackend; use crate::{ + backend::IterArgs, trie_backend_essence::{TrieBackendEssence, TrieBackendStorage}, Backend, StorageKey, StorageValue, }; @@ -28,7 +29,6 @@ use codec::Codec; use hash_db::HashDB; use hash_db::Hasher; use sp_core::storage::{ChildInfo, StateVersion}; -use sp_std::vec::Vec; #[cfg(feature = "std")] use sp_trie::{cache::LocalTrieCache, recorder::Recorder}; #[cfg(feature = "std")] @@ -51,6 +51,7 @@ pub trait AsLocalTrieCache: sealed::Sealed { impl AsLocalTrieCache for LocalTrieCache { #[cfg(feature = "std")] + #[inline] fn as_local_trie_cache(&self) -> &LocalTrieCache { self } @@ -58,6 +59,7 @@ impl AsLocalTrieCache for LocalTrieCache { #[cfg(feature = "std")] impl AsLocalTrieCache for &LocalTrieCache { + #[inline] fn as_local_trie_cache(&self) -> &LocalTrieCache { self } @@ -236,6 +238,7 @@ where type Error = crate::DefaultError; type Transaction = S::Overlay; type TrieBackendStorage = S; + type RawIter = crate::trie_backend_essence::RawIter; fn storage_hash(&self, key: &[u8]) -> Result, Self::Error> { self.essence.storage_hash(key) @@ -273,51 +276,8 @@ where self.essence.next_child_storage_key(child_info, key) } - fn for_keys_with_prefix(&self, prefix: &[u8], f: F) { - self.essence.for_keys_with_prefix(prefix, f) - } - - fn for_key_values_with_prefix(&self, prefix: &[u8], f: F) { - self.essence.for_key_values_with_prefix(prefix, f) - } - - fn apply_to_key_values_while, Vec) -> bool>( - &self, - child_info: Option<&ChildInfo>, - prefix: Option<&[u8]>, - start_at: Option<&[u8]>, - f: F, - allow_missing: bool, - ) -> Result { - self.essence - .apply_to_key_values_while(child_info, prefix, start_at, f, allow_missing) - } - - fn apply_to_keys_while bool>( - &self, - child_info: Option<&ChildInfo>, - prefix: Option<&[u8]>, - start_at: Option<&[u8]>, - f: F, - ) { - self.essence.apply_to_keys_while(child_info, prefix, start_at, f) - } - - fn for_child_keys_with_prefix( - &self, - child_info: &ChildInfo, - prefix: &[u8], - f: F, - ) { - self.essence.for_child_keys_with_prefix(child_info, prefix, f) - } - - fn pairs(&self) -> Vec<(StorageKey, StorageValue)> { - self.essence.pairs() - } - - fn keys(&self, prefix: &[u8]) -> Vec { - self.essence.keys(prefix) + fn raw_iter(&self, args: IterArgs) -> Result { + self.essence.raw_iter(args) } fn storage_root<'a>( @@ -579,7 +539,11 @@ pub mod tests { cache: Option, recorder: Option, ) { - assert!(!test_trie(state_version, cache, recorder).pairs().is_empty()); + assert!(!test_trie(state_version, cache, recorder) + .pairs(Default::default()) + .unwrap() + .next() + .is_none()); } #[test] @@ -589,8 +553,163 @@ pub mod tests { Default::default(), ) .build() - .pairs() - .is_empty()); + .pairs(Default::default()) + .unwrap() + .next() + .is_none()); + } + + parameterized_test!(storage_iteration_works, storage_iteration_works_inner); + fn storage_iteration_works_inner( + state_version: StateVersion, + cache: Option, + recorder: Option, + ) { + let trie = test_trie(state_version, cache, recorder); + + // Fetch everything. + assert_eq!( + trie.keys(Default::default()) + .unwrap() + .map(|result| result.unwrap()) + .take(5) + .collect::>(), + vec![ + b":child_storage:default:sub1".to_vec(), + b":code".to_vec(), + b"key".to_vec(), + b"value1".to_vec(), + b"value2".to_vec(), + ] + ); + + // Fetch starting at a given key (full key). + assert_eq!( + trie.keys(IterArgs { start_at: Some(b"key"), ..IterArgs::default() }) + .unwrap() + .map(|result| result.unwrap()) + .take(3) + .collect::>(), + vec![b"key".to_vec(), b"value1".to_vec(), b"value2".to_vec(),] + ); + + // Fetch starting at a given key (partial key). + assert_eq!( + trie.keys(IterArgs { start_at: Some(b"ke"), ..IterArgs::default() }) + .unwrap() + .map(|result| result.unwrap()) + .take(3) + .collect::>(), + vec![b"key".to_vec(), b"value1".to_vec(), b"value2".to_vec(),] + ); + + // Fetch starting at a given key (empty key). + assert_eq!( + trie.keys(IterArgs { start_at: Some(b""), ..IterArgs::default() }) + .unwrap() + .map(|result| result.unwrap()) + .take(5) + .collect::>(), + vec![ + b":child_storage:default:sub1".to_vec(), + b":code".to_vec(), + b"key".to_vec(), + b"value1".to_vec(), + b"value2".to_vec(), + ] + ); + + // Fetch starting at a given key and with prefix which doesn't match that key. + assert!(trie + .keys(IterArgs { + prefix: Some(b"value"), + start_at: Some(b"key"), + ..IterArgs::default() + }) + .unwrap() + .map(|result| result.unwrap()) + .next() + .is_none()); + + // Fetch starting at a given key and with prefix which does match that key. + assert_eq!( + trie.keys(IterArgs { + prefix: Some(b"value"), + start_at: Some(b"value"), + ..IterArgs::default() + }) + .unwrap() + .map(|result| result.unwrap()) + .collect::>(), + vec![b"value1".to_vec(), b"value2".to_vec(),] + ); + + // Also test out the wrapper methods. + // TODO: Remove this once these methods are gone. + + let mut list = Vec::new(); + assert!(trie + .apply_to_key_values_while( + None, + None, + Some(b"key"), + |key, _| { + list.push(key); + true + }, + false + ) + .unwrap()); + assert_eq!(list[0..3], vec![b"key".to_vec(), b"value1".to_vec(), b"value2".to_vec(),]); + + let mut list = Vec::new(); + trie.apply_to_keys_while(None, None, Some(b"key"), |key| { + list.push(key.to_vec()); + true + }) + .unwrap(); + assert_eq!(list[0..3], vec![b"key".to_vec(), b"value1".to_vec(), b"value2".to_vec(),]); + + let mut list = Vec::new(); + trie.apply_to_keys_while(None, None, Some(b"k"), |key| { + list.push(key.to_vec()); + true + }) + .unwrap(); + assert_eq!(list[0..3], vec![b"key".to_vec(), b"value1".to_vec(), b"value2".to_vec(),]); + + let mut list = Vec::new(); + trie.apply_to_keys_while(None, None, Some(b""), |key| { + list.push(key.to_vec()); + true + }) + .unwrap(); + assert_eq!( + list[0..5], + vec![ + b":child_storage:default:sub1".to_vec(), + b":code".to_vec(), + b"key".to_vec(), + b"value1".to_vec(), + b"value2".to_vec(), + ] + ); + + let mut list = Vec::new(); + trie.apply_to_keys_while(None, Some(b"value"), Some(b"key"), |key| { + list.push(key.to_vec()); + true + }) + .unwrap(); + assert!(list.is_empty()); + + let mut list = Vec::new(); + trie.apply_to_keys_while(None, Some(b"value"), Some(b"value"), |key| { + list.push(key.to_vec()); + true + }) + .unwrap(); + assert_eq!(list, vec![b"value1".to_vec(), b"value2".to_vec(),]); } parameterized_test!(storage_root_is_non_default, storage_root_is_non_default_inner); @@ -638,7 +757,8 @@ pub mod tests { trie.for_keys_with_prefix(b"value", |key| { let for_first_time = seen.insert(key.to_vec()); assert!(for_first_time, "Seen key '{:?}' more than once", key); - }); + }) + .unwrap(); let mut expected = HashSet::new(); expected.insert(b"value1".to_vec()); @@ -664,7 +784,8 @@ pub mod tests { .collect::>(); let trie = test_trie(state_version, cache, recorder); - let keys = trie.keys(&[]); + let keys: Vec<_> = + trie.keys(Default::default()).unwrap().map(|result| result.unwrap()).collect(); assert_eq!(expected, keys); } @@ -724,7 +845,18 @@ pub mod tests { .with_recorder(Recorder::default()) .build(); assert_eq!(trie_backend.storage(b"key").unwrap(), proving_backend.storage(b"key").unwrap()); - assert_eq!(trie_backend.pairs(), proving_backend.pairs()); + assert_eq!( + trie_backend + .pairs(Default::default()) + .unwrap() + .map(|result| result.unwrap()) + .collect::>(), + proving_backend + .pairs(Default::default()) + .unwrap() + .map(|result| result.unwrap()) + .collect::>() + ); let (trie_root, mut trie_mdb) = trie_backend.storage_root(std::iter::empty(), state_version); diff --git a/primitives/state-machine/src/trie_backend_essence.rs b/primitives/state-machine/src/trie_backend_essence.rs index d92caeb84..81a627a65 100644 --- a/primitives/state-machine/src/trie_backend_essence.rs +++ b/primitives/state-machine/src/trie_backend_essence.rs @@ -19,24 +19,23 @@ //! from storage. use crate::{ - backend::Consolidate, debug, trie_backend::AsLocalTrieCache, warn, StorageKey, StorageValue, + backend::{Consolidate, IterArgs, StorageIterator}, + trie_backend::AsLocalTrieCache, + warn, StorageKey, StorageValue, }; use codec::Codec; use hash_db::{self, AsHashDB, HashDB, HashDBRef, Hasher, Prefix}; #[cfg(feature = "std")] use parking_lot::RwLock; use sp_core::storage::{ChildInfo, ChildType, StateVersion}; -#[cfg(not(feature = "std"))] -use sp_std::marker::PhantomData; -use sp_std::{boxed::Box, vec::Vec}; +use sp_std::{boxed::Box, marker::PhantomData, vec::Vec}; #[cfg(feature = "std")] use sp_trie::recorder::Recorder; use sp_trie::{ child_delta_trie_root, delta_trie_root, empty_child_trie_root, read_child_trie_hash, read_child_trie_value, read_trie_value, trie_types::{TrieDBBuilder, TrieError}, - DBValue, KeySpacedDB, NodeCodec, Trie, TrieCache, TrieDBIterator, TrieDBKeyIterator, - TrieRecorder, + DBValue, KeySpacedDB, NodeCodec, Trie, TrieCache, TrieDBRawIterator, TrieRecorder, }; #[cfg(feature = "std")] use std::{collections::HashMap, sync::Arc}; @@ -76,6 +75,109 @@ impl Cache { } } +enum IterState { + Pending, + FinishedComplete, + FinishedIncomplete, +} + +/// A raw iterator over the storage. +pub struct RawIter +where + H: Hasher, +{ + stop_on_incomplete_database: bool, + root: H::Out, + child_info: Option, + trie_iter: TrieDBRawIterator>, + state: IterState, + _phantom: PhantomData<(S, C)>, +} + +impl RawIter +where + H: Hasher, + S: TrieBackendStorage, + H::Out: Codec + Ord, + C: AsLocalTrieCache + Send + Sync, +{ + #[inline] + fn prepare( + &mut self, + backend: &TrieBackendEssence, + callback: impl FnOnce( + &sp_trie::TrieDB>, + &mut TrieDBRawIterator>, + ) -> Option::Out>>>>, + ) -> Option> { + if !matches!(self.state, IterState::Pending) { + return None + } + + let result = backend.with_trie_db(self.root, self.child_info.as_ref(), |db| { + callback(&db, &mut self.trie_iter) + }); + match result { + Some(Ok(key_value)) => Some(Ok(key_value)), + None => { + self.state = IterState::FinishedComplete; + None + }, + Some(Err(error)) => { + self.state = IterState::FinishedIncomplete; + if matches!(*error, TrieError::IncompleteDatabase(_)) && + self.stop_on_incomplete_database + { + None + } else { + Some(Err(format!("TrieDB iteration error: {}", error))) + } + }, + } + } +} + +impl Default for RawIter +where + H: Hasher, +{ + fn default() -> Self { + Self { + stop_on_incomplete_database: false, + child_info: None, + root: Default::default(), + trie_iter: TrieDBRawIterator::empty(), + state: IterState::FinishedComplete, + _phantom: Default::default(), + } + } +} + +impl StorageIterator for RawIter +where + H: Hasher, + S: TrieBackendStorage, + H::Out: Codec + Ord, + C: AsLocalTrieCache + Send + Sync, +{ + type Backend = crate::TrieBackend; + type Error = crate::DefaultError; + + #[inline] + fn next_key(&mut self, backend: &Self::Backend) -> Option> { + self.prepare(&backend.essence, |trie, trie_iter| trie_iter.next_key(&trie)) + } + + #[inline] + fn next_pair(&mut self, backend: &Self::Backend) -> Option> { + self.prepare(&backend.essence, |trie, trie_iter| trie_iter.next_item(&trie)) + } + + fn was_complete(&self) -> bool { + matches!(self.state, IterState::FinishedComplete) + } +} + /// Patricia trie-based pairs storage essence. pub struct TrieBackendEssence, H: Hasher, C> { storage: S, @@ -168,6 +270,7 @@ impl, H: Hasher, C: AsLocalTrieCache> TrieBackendEss /// /// If the given `storage_root` is `None`, `self.root` will be used. #[cfg(feature = "std")] + #[inline] fn with_recorder_and_cache( &self, storage_root: Option, @@ -193,6 +296,7 @@ impl, H: Hasher, C: AsLocalTrieCache> TrieBackendEss } #[cfg(not(feature = "std"))] + #[inline] fn with_recorder_and_cache( &self, _: Option, @@ -262,6 +366,31 @@ impl, H: Hasher, C: AsLocalTrieCache + Send + Sync> where H::Out: Codec + Ord, { + /// Calls the given closure with a [`TrieDb`] constructed for the given + /// storage root and (optionally) child trie. + #[inline] + fn with_trie_db( + &self, + root: H::Out, + child_info: Option<&ChildInfo>, + callback: impl FnOnce(&sp_trie::TrieDB>) -> R, + ) -> R { + let backend = self as &dyn HashDBRef>; + let db = child_info + .as_ref() + .map(|child_info| KeySpacedDB::new(backend, child_info.keyspace())); + let db = db.as_ref().map(|db| db as &dyn HashDBRef>).unwrap_or(backend); + + self.with_recorder_and_cache(Some(root), |recorder, cache| { + let trie = TrieDBBuilder::::new(db, &root) + .with_optional_recorder(recorder) + .with_optional_cache(cache) + .build(); + + callback(&trie) + }) + } + /// Return the next key in the trie i.e. the minimum key that is strictly superior to `key` in /// lexicographic order. pub fn next_storage_key(&self, key: &[u8]) -> Result> { @@ -316,21 +445,7 @@ where child_info: Option<&ChildInfo>, key: &[u8], ) -> Result> { - let dyn_eph: &dyn HashDBRef<_, _>; - let keyspace_eph; - if let Some(child_info) = child_info.as_ref() { - keyspace_eph = KeySpacedDB::new(self, child_info.keyspace()); - dyn_eph = &keyspace_eph; - } else { - dyn_eph = self; - } - - self.with_recorder_and_cache(Some(*root), |recorder, cache| { - let trie = TrieDBBuilder::::new(dyn_eph, root) - .with_optional_recorder(recorder) - .with_optional_cache(cache) - .build(); - + self.with_trie_db(*root, child_info, |trie| { let mut iter = trie.key_iter().map_err(|e| format!("TrieDB iteration error: {}", e))?; // The key just after the one given in input, basically `key++0`. @@ -429,246 +544,42 @@ where }) } - /// Retrieve all entries keys of storage and call `f` for each of those keys. - /// Aborts as soon as `f` returns false. - /// - /// Returns `true` when all keys were iterated. - pub fn apply_to_key_values_while( - &self, - child_info: Option<&ChildInfo>, - prefix: Option<&[u8]>, - start_at: Option<&[u8]>, - f: impl FnMut(Vec, Vec) -> bool, - allow_missing_nodes: bool, - ) -> Result { - let root = if let Some(child_info) = child_info.as_ref() { - match self.child_root(child_info)? { - Some(child_root) => child_root, - None => return Ok(true), - } - } else { - self.root - }; - - self.trie_iter_inner(&root, prefix, f, child_info, start_at, allow_missing_nodes) - } - - /// Retrieve all entries keys of a storage and call `f` for each of those keys. - /// Aborts as soon as `f` returns false. - pub fn apply_to_keys_while bool>( - &self, - child_info: Option<&ChildInfo>, - prefix: Option<&[u8]>, - start_at: Option<&[u8]>, - f: F, - ) { - let root = if let Some(child_info) = child_info.as_ref() { - match self.child_root(child_info) { - Ok(Some(v)) => v, - // If the child trie doesn't exist, there is no need to continue. - Ok(None) => return, - Err(e) => { - debug!(target: "trie", "Error while iterating child storage: {}", e); - return - }, - } + /// Create a raw iterator over the storage. + pub fn raw_iter(&self, args: IterArgs) -> Result> { + let root = if let Some(child_info) = args.child_info.as_ref() { + let root = match self.child_root(&child_info)? { + Some(root) => root, + None => return Ok(Default::default()), + }; + root } else { self.root }; - self.trie_iter_key_inner(&root, prefix, f, child_info, start_at) - } - - /// Execute given closure for all keys starting with prefix. - pub fn for_child_keys_with_prefix( - &self, - child_info: &ChildInfo, - prefix: &[u8], - mut f: impl FnMut(&[u8]), - ) { - let root = match self.child_root(child_info) { - Ok(Some(v)) => v, - // If the child trie doesn't exist, there is no need to continue. - Ok(None) => return, - Err(e) => { - debug!(target: "trie", "Error while iterating child storage: {}", e); - return - }, - }; - - self.trie_iter_key_inner( - &root, - Some(prefix), - |k| { - f(k); - true - }, - Some(child_info), - None, - ) - } - - /// Execute given closure for all keys starting with prefix. - pub fn for_keys_with_prefix(&self, prefix: &[u8], mut f: F) { - self.trie_iter_key_inner( - &self.root, - Some(prefix), - |k| { - f(k); - true - }, - None, - None, - ) - } - - fn trie_iter_key_inner bool>( - &self, - root: &H::Out, - maybe_prefix: Option<&[u8]>, - mut f: F, - child_info: Option<&ChildInfo>, - maybe_start_at: Option<&[u8]>, - ) { - let mut iter = move |db| -> sp_std::result::Result<(), Box>> { - self.with_recorder_and_cache(Some(*root), |recorder, cache| { - let trie = TrieDBBuilder::::new(db, root) - .with_optional_recorder(recorder) - .with_optional_cache(cache) - .build(); - let prefix = maybe_prefix.unwrap_or(&[]); - let iter = match maybe_start_at { - Some(start_at) => - TrieDBKeyIterator::new_prefixed_then_seek(&trie, prefix, start_at), - None => TrieDBKeyIterator::new_prefixed(&trie, prefix), - }?; - - for x in iter { - let key = x?; - - debug_assert!(maybe_prefix - .as_ref() - .map(|prefix| key.starts_with(prefix)) - .unwrap_or(true)); - - if !f(&key) { - break - } - } - - Ok(()) - }) - }; - - let result = if let Some(child_info) = child_info { - let db = KeySpacedDB::new(self, child_info.keyspace()); - iter(&db) - } else { - iter(self) - }; - if let Err(e) = result { - debug!(target: "trie", "Error while iterating by prefix: {}", e); + if self.root == Default::default() { + // A special-case for an empty storage root. + return Ok(Default::default()) } - } - fn trie_iter_inner, Vec) -> bool>( - &self, - root: &H::Out, - prefix: Option<&[u8]>, - mut f: F, - child_info: Option<&ChildInfo>, - start_at: Option<&[u8]>, - allow_missing_nodes: bool, - ) -> Result { - let mut iter = move |db| -> sp_std::result::Result>> { - self.with_recorder_and_cache(Some(*root), |recorder, cache| { - let trie = TrieDBBuilder::::new(db, root) - .with_optional_recorder(recorder) - .with_optional_cache(cache) - .build(); - - let prefix = prefix.unwrap_or(&[]); - let iterator = if let Some(start_at) = start_at { - TrieDBIterator::new_prefixed_then_seek(&trie, prefix, start_at)? + let trie_iter = self + .with_trie_db(root, args.child_info.as_ref(), |db| { + let prefix = args.prefix.as_deref().unwrap_or(&[]); + if let Some(start_at) = args.start_at { + TrieDBRawIterator::new_prefixed_then_seek(db, prefix, &start_at) } else { - TrieDBIterator::new_prefixed(&trie, prefix)? - }; - for x in iterator { - let (key, value) = x?; - - debug_assert!(key.starts_with(prefix)); - - if !f(key, value) { - return Ok(false) - } - } - - Ok(true) - }) - }; - - let result = if let Some(child_info) = child_info { - let db = KeySpacedDB::new(self, child_info.keyspace()); - iter(&db) - } else { - iter(self) - }; - match result { - Ok(completed) => Ok(completed), - Err(e) if matches!(*e, TrieError::IncompleteDatabase(_)) && allow_missing_nodes => - Ok(false), - Err(e) => Err(format!("TrieDB iteration error: {}", e)), - } - } - - /// Execute given closure for all key and values starting with prefix. - pub fn for_key_values_with_prefix(&self, prefix: &[u8], mut f: F) { - let _ = self.trie_iter_inner( - &self.root, - Some(prefix), - |k, v| { - f(&k, &v); - true - }, - None, - None, - false, - ); - } - - /// Returns all `(key, value)` pairs in the trie. - pub fn pairs(&self) -> Vec<(StorageKey, StorageValue)> { - let collect_all = || -> sp_std::result::Result<_, Box>> { - self.with_recorder_and_cache(None, |recorder, cache| { - let trie = TrieDBBuilder::::new(self, self.root()) - .with_optional_cache(cache) - .with_optional_recorder(recorder) - .build(); - - let mut v = Vec::new(); - for x in trie.iter()? { - let (key, value) = x?; - v.push((key.to_vec(), value.to_vec())); + TrieDBRawIterator::new_prefixed(db, prefix) } - - Ok(v) }) - }; - - match collect_all() { - Ok(v) => v, - Err(e) => { - debug!(target: "trie", "Error extracting trie values: {}", e); - Vec::new() - }, - } - } + .map_err(|e| format!("TrieDB iteration error: {}", e))?; - /// Returns all keys that start with the given `prefix`. - pub fn keys(&self, prefix: &[u8]) -> Vec { - let mut keys = Vec::new(); - self.for_keys_with_prefix(prefix, |k| keys.push(k.to_vec())); - keys + Ok(RawIter { + stop_on_incomplete_database: args.stop_on_incomplete_database, + child_info: args.child_info, + root, + trie_iter, + state: IterState::Pending, + _phantom: Default::default(), + }) } /// Return the storage root after applying the given `delta`. diff --git a/primitives/storage/src/lib.rs b/primitives/storage/src/lib.rs index 77aa260c9..06995005d 100644 --- a/primitives/storage/src/lib.rs +++ b/primitives/storage/src/lib.rs @@ -273,6 +273,7 @@ impl ChildInfo { /// Returns byte sequence (keyspace) that can be use by underlying db to isolate keys. /// This is a unique id of the child trie. The collision resistance of this value /// depends on the type of child info use. For `ChildInfo::Default` it is and need to be. + #[inline] pub fn keyspace(&self) -> &[u8] { match self { ChildInfo::ParentKeyId(..) => self.storage_key(), diff --git a/primitives/trie/Cargo.toml b/primitives/trie/Cargo.toml index 1e83a5d6d..33a62cdd9 100644 --- a/primitives/trie/Cargo.toml +++ b/primitives/trie/Cargo.toml @@ -29,7 +29,7 @@ parking_lot = { version = "0.12.1", optional = true } scale-info = { version = "2.1.1", default-features = false, features = ["derive"] } thiserror = { version = "1.0.30", optional = true } tracing = { version = "0.1.29", optional = true } -trie-db = { version = "0.24.0", default-features = false } +trie-db = { version = "0.25.0", default-features = false } trie-root = { version = "0.17.0", default-features = false } sp-core = { version = "7.0.0", default-features = false, path = "../core" } sp-std = { version = "5.0.0", default-features = false, path = "../std" } @@ -38,7 +38,7 @@ schnellru = { version = "0.2.1", optional = true } [dev-dependencies] array-bytes = "4.1" criterion = "0.4.0" -trie-bench = "0.34.0" +trie-bench = "0.35.0" trie-standardmap = "0.15.2" sp-runtime = { version = "7.0.0", path = "../runtime" } diff --git a/primitives/trie/src/cache/mod.rs b/primitives/trie/src/cache/mod.rs index e55a56971..0100e2876 100644 --- a/primitives/trie/src/cache/mod.rs +++ b/primitives/trie/src/cache/mod.rs @@ -438,8 +438,8 @@ enum ValueCache<'a, H: Hasher> { impl ValueCache<'_, H> { /// Get the value for the given `key`. - fn get<'a>( - &'a mut self, + fn get( + &mut self, key: &[u8], shared_cache: &SharedTrieCache, stats: &HitStats, diff --git a/primitives/trie/src/lib.rs b/primitives/trie/src/lib.rs index 34517d6aa..175fb32d4 100644 --- a/primitives/trie/src/lib.rs +++ b/primitives/trie/src/lib.rs @@ -51,7 +51,7 @@ pub use trie_db::{ nibble_ops, node::{NodePlan, ValuePlan}, CError, DBValue, Query, Recorder, Trie, TrieCache, TrieConfiguration, TrieDBIterator, - TrieDBKeyIterator, TrieLayout, TrieMut, TrieRecorder, + TrieDBKeyIterator, TrieDBRawIterator, TrieLayout, TrieMut, TrieRecorder, }; /// The Substrate format implementation of `TrieStream`. pub use trie_stream::TrieStream; @@ -442,6 +442,7 @@ fn keyspace_as_prefix_alloc(ks: &[u8], prefix: Prefix) -> (Vec, Option) impl<'a, DB: ?Sized, H> KeySpacedDB<'a, DB, H> { /// instantiate new keyspaced db + #[inline] pub fn new(db: &'a DB, ks: &'a [u8]) -> Self { KeySpacedDB(db, ks, PhantomData) } diff --git a/primitives/trie/src/recorder.rs b/primitives/trie/src/recorder.rs index d496408b2..3bdfda015 100644 --- a/primitives/trie/src/recorder.rs +++ b/primitives/trie/src/recorder.rs @@ -83,6 +83,7 @@ impl Recorder { /// /// - `storage_root`: The storage root of the trie for which accesses are recorded. This is /// important when recording access to different tries at once (like top and child tries). + #[inline] pub fn as_trie_recorder( &self, storage_root: H::Out, @@ -147,7 +148,7 @@ struct TrieRecorder { impl>> trie_db::TrieRecorder for TrieRecorder { - fn record<'b>(&mut self, access: TrieAccess<'b, H::Out>) { + fn record(&mut self, access: TrieAccess) { let mut encoded_size_update = 0; match access { diff --git a/test-utils/runtime/Cargo.toml b/test-utils/runtime/Cargo.toml index 0852003a6..33dbfce68 100644 --- a/test-utils/runtime/Cargo.toml +++ b/test-utils/runtime/Cargo.toml @@ -41,7 +41,7 @@ pallet-timestamp = { version = "4.0.0-dev", default-features = false, path = ".. sp-finality-grandpa = { version = "4.0.0-dev", default-features = false, path = "../../primitives/finality-grandpa" } sp-trie = { version = "7.0.0", default-features = false, path = "../../primitives/trie" } sp-transaction-pool = { version = "4.0.0-dev", default-features = false, path = "../../primitives/transaction-pool" } -trie-db = { version = "0.24.0", default-features = false } +trie-db = { version = "0.25.1", default-features = false } sc-service = { version = "0.10.0-dev", default-features = false, optional = true, features = ["test-helpers"], path = "../../client/service" } sp-state-machine = { version = "0.13.0", default-features = false, path = "../../primitives/state-machine" } sp-externalities = { version = "0.13.0", default-features = false, path = "../../primitives/externalities" } diff --git a/utils/frame/benchmarking-cli/src/storage/cmd.rs b/utils/frame/benchmarking-cli/src/storage/cmd.rs index 10ea8caae..99cadbe8e 100644 --- a/utils/frame/benchmarking-cli/src/storage/cmd.rs +++ b/utils/frame/benchmarking-cli/src/storage/cmd.rs @@ -20,7 +20,6 @@ use sc_client_api::{Backend as ClientBackend, StorageProvider, UsageProvider}; use sc_client_db::DbHash; use sc_service::Configuration; use sp_blockchain::HeaderBackend; -use sp_core::storage::StorageKey; use sp_database::{ColumnId, Database}; use sp_runtime::traits::{Block as BlockT, HashFor}; use sp_state_machine::Storage; @@ -192,8 +191,7 @@ impl StorageCmd { BA: ClientBackend, { let hash = client.usage_info().chain.best_hash; - let empty_prefix = StorageKey(Vec::new()); - let mut keys = client.storage_keys(hash, &empty_prefix)?; + let mut keys: Vec<_> = client.storage_keys(hash, None, None)?.collect(); let (mut rng, _) = new_rng(None); keys.shuffle(&mut rng); diff --git a/utils/frame/benchmarking-cli/src/storage/read.rs b/utils/frame/benchmarking-cli/src/storage/read.rs index c2aef1c79..fe7236426 100644 --- a/utils/frame/benchmarking-cli/src/storage/read.rs +++ b/utils/frame/benchmarking-cli/src/storage/read.rs @@ -17,7 +17,6 @@ use sc_cli::Result; use sc_client_api::{Backend as ClientBackend, StorageProvider, UsageProvider}; -use sp_core::storage::StorageKey; use sp_runtime::traits::{Block as BlockT, Header as HeaderT}; use log::info; @@ -42,8 +41,7 @@ impl StorageCmd { info!("Preparing keys from block {}", best_hash); // Load all keys and randomly shuffle them. - let empty_prefix = StorageKey(Vec::new()); - let mut keys = client.storage_keys(best_hash, &empty_prefix)?; + let mut keys: Vec<_> = client.storage_keys(best_hash, None, None)?.collect(); let (mut rng, _) = new_rng(None); keys.shuffle(&mut rng); @@ -55,8 +53,7 @@ impl StorageCmd { match (self.params.include_child_trees, self.is_child_key(key.clone().0)) { (true, Some(info)) => { // child tree key - let child_keys = client.child_storage_keys(best_hash, &info, &empty_prefix)?; - for ck in child_keys { + for ck in client.child_storage_keys(best_hash, info.clone(), None, None)? { child_nodes.push((ck.clone(), info.clone())); } }, diff --git a/utils/frame/benchmarking-cli/src/storage/write.rs b/utils/frame/benchmarking-cli/src/storage/write.rs index 66d5a30ea..faca3b536 100644 --- a/utils/frame/benchmarking-cli/src/storage/write.rs +++ b/utils/frame/benchmarking-cli/src/storage/write.rs @@ -61,7 +61,7 @@ impl StorageCmd { info!("Preparing keys from block {}", best_hash); // Load all KV pairs and randomly shuffle them. - let mut kvs = trie.pairs(); + let mut kvs: Vec<_> = trie.pairs(Default::default())?.collect(); let (mut rng, _) = new_rng(None); kvs.shuffle(&mut rng); info!("Writing {} keys", kvs.len()); @@ -70,11 +70,12 @@ impl StorageCmd { // Generate all random values first; Make sure there are no collisions with existing // db entries, so we can rollback all additions without corrupting existing entries. - for (k, original_v) in kvs { + for key_value in kvs { + let (k, original_v) = key_value?; match (self.params.include_child_trees, self.is_child_key(k.to_vec())) { (true, Some(info)) => { let child_keys = - client.child_storage_keys_iter(best_hash, info.clone(), None, None)?; + client.child_storage_keys(best_hash, info.clone(), None, None)?; for ck in child_keys { child_nodes.push((ck.clone(), info.clone())); } diff --git a/utils/frame/rpc/state-trie-migration-rpc/Cargo.toml b/utils/frame/rpc/state-trie-migration-rpc/Cargo.toml index 17a68e2f4..1dd9da9a5 100644 --- a/utils/frame/rpc/state-trie-migration-rpc/Cargo.toml +++ b/utils/frame/rpc/state-trie-migration-rpc/Cargo.toml @@ -21,7 +21,7 @@ log = { version = "0.4.17", default-features = false } sp-core = { path = "../../../../primitives/core" } sp-state-machine = { path = "../../../../primitives/state-machine" } sp-trie = { path = "../../../../primitives/trie" } -trie-db = "0.24.0" +trie-db = "0.25.1" jsonrpsee = { version = "0.16.2", features = ["client-core", "server", "macros"] } From 473b5d06354c5b82e8f6df904c5f50d95c6fcc22 Mon Sep 17 00:00:00 2001 From: Doordashcon Date: Wed, 22 Feb 2023 13:17:11 +0100 Subject: [PATCH 152/558] permissionless `bond_extra` in nomination pools (#12608) * create enum * logic check * add benchmarks * -enum * update * bond extra other * update * update * update * cargo fmt * Permissioned * update * cargo fmt * update * update index * doc update Co-authored-by: Ankan <10196091+Ank4n@users.noreply.github.com> * doc update Co-authored-by: Kian Paimani <5588131+kianenigma@users.noreply.github.com> * cargo fmt * bond_extra auto compound * bond_extra_other * Apply suggestions from code review * Fixes from kian * updates docs & test * Update frame/nomination-pools/src/lib.rs * Update frame/nomination-pools/src/lib.rs * Update frame/nomination-pools/src/lib.rs * Update frame/nomination-pools/src/lib.rs * Update frame/nomination-pools/src/lib.rs * Update frame/nomination-pools/src/tests.rs * Update frame/nomination-pools/src/lib.rs * Update frame/nomination-pools/src/tests.rs * Update frame/nomination-pools/src/tests.rs * Update frame/nomination-pools/src/tests.rs * Update frame/nomination-pools/src/tests.rs * Update frame/nomination-pools/src/tests.rs * Update frame/nomination-pools/src/lib.rs * Update frame/nomination-pools/src/tests.rs * fixes + fmt * expand ClaimPermissions + add benchmarks * ".git/.scripts/commands/bench/bench.sh" pallet dev pallet_nomination_pools * tidy up claim payout benches * fix * + test: claim_payout_other_works * comments, rename to set_claim_permission * fix comment * remove ClaimPermission on leave pool * fix test * ".git/.scripts/commands/fmt/fmt.sh" * + test for ClaimPermissions::remove() * impl can_bond_extra & can_claim_payout --------- Co-authored-by: Ankan <10196091+Ank4n@users.noreply.github.com> Co-authored-by: Kian Paimani <5588131+kianenigma@users.noreply.github.com> Co-authored-by: kianenigma Co-authored-by: parity-processbot <> Co-authored-by: Ross Bulat --- .../nomination-pools/benchmarking/src/lib.rs | 46 ++- frame/nomination-pools/src/lib.rs | 214 +++++++++++--- frame/nomination-pools/src/tests.rs | 179 ++++++++++++ frame/nomination-pools/src/weights.rs | 270 +++++++++++------- 4 files changed, 567 insertions(+), 142 deletions(-) diff --git a/frame/nomination-pools/benchmarking/src/lib.rs b/frame/nomination-pools/benchmarking/src/lib.rs index 3b6df669a..094289ee0 100644 --- a/frame/nomination-pools/benchmarking/src/lib.rs +++ b/frame/nomination-pools/benchmarking/src/lib.rs @@ -30,9 +30,9 @@ use frame_election_provider_support::SortedListProvider; use frame_support::{assert_ok, ensure, traits::Get}; use frame_system::RawOrigin as RuntimeOrigin; use pallet_nomination_pools::{ - BalanceOf, BondExtra, BondedPoolInner, BondedPools, ConfigOp, MaxPoolMembers, - MaxPoolMembersPerPool, MaxPools, Metadata, MinCreateBond, MinJoinBond, Pallet as Pools, - PoolMembers, PoolRoles, PoolState, RewardPools, SubPoolsStorage, + BalanceOf, BondExtra, BondedPoolInner, BondedPools, ClaimPermission, ClaimPermissions, + ConfigOp, MaxPoolMembers, MaxPoolMembersPerPool, MaxPools, Metadata, MinCreateBond, + MinJoinBond, Pallet as Pools, PoolMembers, PoolRoles, PoolState, RewardPools, SubPoolsStorage, }; use sp_runtime::traits::{Bounded, StaticLookup, Zero}; use sp_staking::{EraIndex, StakingInterface}; @@ -252,17 +252,22 @@ frame_benchmarking::benchmarks! { ); } - bond_extra_reward { + bond_extra_other { + let claimer: T::AccountId = account("claimer", USER_SEED + 4, 0); + let origin_weight = Pools::::depositor_min_bond() * 2u32.into(); let scenario = ListScenario::::new(origin_weight, true)?; let extra = (scenario.dest_weight - origin_weight).max(CurrencyOf::::minimum_balance()); + // set claim preferences to `PermissionlessAll` to any account to bond extra on member's behalf. + let _ = Pools::::set_claim_permission(RuntimeOrigin::Signed(scenario.creator1.clone()).into(), ClaimPermission::PermissionlessAll); + // transfer exactly `extra` to the depositor of the src pool (1), let reward_account1 = Pools::::create_reward_account(1); assert!(extra >= CurrencyOf::::minimum_balance()); CurrencyOf::::deposit_creating(&reward_account1, extra); - }: bond_extra(RuntimeOrigin::Signed(scenario.creator1.clone()), BondExtra::Rewards) + }: _(RuntimeOrigin::Signed(claimer), T::Lookup::unlookup(scenario.creator1.clone()), BondExtra::Rewards) verify { assert!( T::Staking::active_stake(&scenario.origin1).unwrap() >= @@ -271,6 +276,8 @@ frame_benchmarking::benchmarks! { } claim_payout { + let claimer: T::AccountId = account("claimer", USER_SEED + 4, 0); + let origin_weight = Pools::::depositor_min_bond() * 2u32.into(); let ed = CurrencyOf::::minimum_balance(); let (depositor, pool_account) = create_pool_account::(0, origin_weight); @@ -279,6 +286,10 @@ frame_benchmarking::benchmarks! { // Send funds to the reward account of the pool CurrencyOf::::make_free_balance_be(&reward_account, ed + origin_weight); + // set claim preferences to `PermissionlessAll` so any account can claim rewards on member's + // behalf. + let _ = Pools::::set_claim_permission(RuntimeOrigin::Signed(depositor.clone()).into(), ClaimPermission::PermissionlessAll); + // Sanity check assert_eq!( CurrencyOf::::free_balance(&depositor), @@ -286,7 +297,7 @@ frame_benchmarking::benchmarks! { ); whitelist_account!(depositor); - }:_(RuntimeOrigin::Signed(depositor.clone())) + }:claim_payout_other(RuntimeOrigin::Signed(claimer), depositor.clone()) verify { assert_eq!( CurrencyOf::::free_balance(&depositor), @@ -298,6 +309,7 @@ frame_benchmarking::benchmarks! { ); } + unbond { // The weight the nominator will start at. The value used here is expected to be // significantly higher than the first position in a list (e.g. the first bag threshold). @@ -654,6 +666,28 @@ frame_benchmarking::benchmarks! { assert!(T::Staking::nominations(Pools::::create_bonded_account(1)).is_none()); } + set_claim_permission { + // Create a pool + let min_create_bond = Pools::::depositor_min_bond(); + let (depositor, pool_account) = create_pool_account::(0, min_create_bond); + + // Join pool + let min_join_bond = MinJoinBond::::get().max(CurrencyOf::::minimum_balance()); + let joiner = create_funded_user_with_balance::("joiner", 0, min_join_bond * 4u32.into()); + let joiner_lookup = T::Lookup::unlookup(joiner.clone()); + Pools::::join(RuntimeOrigin::Signed(joiner.clone()).into(), min_join_bond, 1) + .unwrap(); + + // Sanity check join worked + assert_eq!( + T::Staking::active_stake(&pool_account).unwrap(), + min_create_bond + min_join_bond + ); + }:_(RuntimeOrigin::Signed(joiner.clone()), ClaimPermission::PermissionlessAll) + verify { + assert_eq!(ClaimPermissions::::get(joiner), ClaimPermission::PermissionlessAll); + } + impl_benchmark_test_suite!( Pallet, crate::mock::new_test_ext(), diff --git a/frame/nomination-pools/src/lib.rs b/frame/nomination-pools/src/lib.rs index f61a37644..b296eb048 100644 --- a/frame/nomination-pools/src/lib.rs +++ b/frame/nomination-pools/src/lib.rs @@ -61,6 +61,10 @@ //! //! After joining a pool, a member can claim rewards by calling [`Call::claim_payout`]. //! +//! A pool member can also set a `ClaimPermission` with [`Call::set_claim_permission`], to allow +//! other members to permissionlessly bond or withdraw their rewards by calling +//! [`Call::bond_extra_other`] or [`Call::claim_payout_other`] respectively. +//! //! For design docs see the [reward pool](#reward-pool) section. //! //! ### Leave @@ -411,6 +415,43 @@ enum AccountType { Reward, } +/// The permission a pool member can set for other accounts to claim rewards on their behalf. +#[derive(Encode, Decode, MaxEncodedLen, Clone, Copy, Debug, PartialEq, Eq, TypeInfo)] +pub enum ClaimPermission { + /// Only the pool member themself can claim their rewards. + Permissioned, + /// Anyone can compound rewards on a pool member's behalf. + PermissionlessCompound, + /// Anyone can withdraw rewards on a pool member's behalf. + PermissionlessWithdraw, + /// Anyone can withdraw and compound rewards on a member's behalf. + PermissionlessAll, +} + +impl ClaimPermission { + fn can_bond_extra(&self) -> bool { + match self { + ClaimPermission::PermissionlessAll => true, + ClaimPermission::PermissionlessCompound => true, + _ => false, + } + } + + fn can_claim_payout(&self) -> bool { + match self { + ClaimPermission::PermissionlessAll => true, + ClaimPermission::PermissionlessWithdraw => true, + _ => false, + } + } +} + +impl Default for ClaimPermission { + fn default() -> Self { + Self::Permissioned + } +} + /// A member in a pool. #[derive(Encode, Decode, MaxEncodedLen, TypeInfo, RuntimeDebugNoBound, CloneNoBound)] #[cfg_attr(feature = "std", derive(frame_support::PartialEqNoBound, DefaultNoBound))] @@ -1313,6 +1354,11 @@ pub mod pallet { pub type ReversePoolIdLookup = CountedStorageMap<_, Twox64Concat, T::AccountId, PoolId, OptionQuery>; + /// Map from a pool member account to their opted claim permission. + #[pallet::storage] + pub type ClaimPermissions = + StorageMap<_, Twox64Concat, T::AccountId, ClaimPermission, ValueQuery>; + #[pallet::genesis_config] pub struct GenesisConfig { pub min_join_bond: BalanceOf, @@ -1470,6 +1516,8 @@ pub mod pallet { PoolIdInUse, /// Pool id provided is not correct/usable. InvalidPoolId, + /// Bonding extra is restricted to the exact pending reward amount. + BondExtraRestricted, } #[derive(Encode, Decode, PartialEq, TypeInfo, frame_support::PalletError, RuntimeDebug)] @@ -1560,44 +1608,18 @@ pub mod pallet { /// accumulated rewards, see [`BondExtra`]. /// /// Bonding extra funds implies an automatic payout of all pending rewards as well. + /// See `bond_extra_other` to bond pending rewards of `other` members. // NOTE: this transaction is implemented with the sole purpose of readability and // correctness, not optimization. We read/write several storage items multiple times instead // of just once, in the spirit reusing code. #[pallet::call_index(1)] #[pallet::weight( T::WeightInfo::bond_extra_transfer() - .max(T::WeightInfo::bond_extra_reward()) + .max(T::WeightInfo::bond_extra_other()) )] pub fn bond_extra(origin: OriginFor, extra: BondExtra>) -> DispatchResult { let who = ensure_signed(origin)?; - let (mut member, mut bonded_pool, mut reward_pool) = Self::get_member_with_pools(&who)?; - - // payout related stuff: we must claim the payouts, and updated recorded payout data - // before updating the bonded pool points, similar to that of `join` transaction. - reward_pool.update_records(bonded_pool.id, bonded_pool.points)?; - let claimed = - Self::do_reward_payout(&who, &mut member, &mut bonded_pool, &mut reward_pool)?; - - let (points_issued, bonded) = match extra { - BondExtra::FreeBalance(amount) => - (bonded_pool.try_bond_funds(&who, amount, BondType::Later)?, amount), - BondExtra::Rewards => - (bonded_pool.try_bond_funds(&who, claimed, BondType::Later)?, claimed), - }; - - bonded_pool.ok_to_be_open()?; - member.points = - member.points.checked_add(&points_issued).ok_or(Error::::OverflowRisk)?; - - Self::deposit_event(Event::::Bonded { - member: who.clone(), - pool_id: member.pool_id, - bonded, - joined: false, - }); - Self::put_member_with_pools(&who, member, bonded_pool, reward_pool); - - Ok(()) + Self::do_bond_extra(who.clone(), who, extra) } /// A bonded member can use this to claim their payout based on the rewards that the pool @@ -1606,16 +1628,13 @@ pub mod pallet { /// /// The member will earn rewards pro rata based on the members stake vs the sum of the /// members in the pools stake. Rewards do not "expire". + /// + /// See `claim_payout_other` to caim rewards on bahalf of some `other` pool member. #[pallet::call_index(2)] #[pallet::weight(T::WeightInfo::claim_payout())] pub fn claim_payout(origin: OriginFor) -> DispatchResult { - let who = ensure_signed(origin)?; - let (mut member, mut bonded_pool, mut reward_pool) = Self::get_member_with_pools(&who)?; - - let _ = Self::do_reward_payout(&who, &mut member, &mut bonded_pool, &mut reward_pool)?; - - Self::put_member_with_pools(&who, member, bonded_pool, reward_pool); - Ok(()) + let signer = ensure_signed(origin)?; + Self::do_claim_payout(signer.clone(), signer) } /// Unbond up to `unbonding_points` of the `member_account`'s funds from the pool. It @@ -1715,7 +1734,6 @@ pub mod pallet { // Now that we know everything has worked write the items to storage. SubPoolsStorage::insert(&member.pool_id, sub_pools); Self::put_member_with_pools(&member_account, member, bonded_pool, reward_pool); - Ok(()) } @@ -1840,6 +1858,9 @@ pub mod pallet { }); let post_info_weight = if member.total_points().is_zero() { + // remove any `ClaimPermission` associated with the member. + ClaimPermissions::::remove(&member_account); + // member being reaped. PoolMembers::::remove(&member_account); Self::deposit_event(Event::::MemberRemoved { @@ -2114,6 +2135,67 @@ pub mod pallet { ensure!(bonded_pool.can_nominate(&who), Error::::NotNominator); T::Staking::chill(&bonded_pool.bonded_account()) } + + /// `origin` bonds funds from `extra` for some pool member `member` into their respective + /// pools. + /// + /// `origin` can bond extra funds from free balance or pending rewards when `origin == + /// other`. + /// + /// In the case of `origin != other`, `origin` can only bond extra pending rewards of + /// `other` members assuming set_claim_permission for the given member is + /// `PermissionlessAll` or `PermissionlessCompound`. + #[pallet::call_index(14)] + #[pallet::weight( + T::WeightInfo::bond_extra_transfer() + .max(T::WeightInfo::bond_extra_other()) + )] + pub fn bond_extra_other( + origin: OriginFor, + member: AccountIdLookupOf, + extra: BondExtra>, + ) -> DispatchResult { + let who = ensure_signed(origin)?; + Self::do_bond_extra(who, T::Lookup::lookup(member)?, extra) + } + + /// Allows a pool member to set a claim permission to allow or disallow permissionless + /// bonding and withdrawing. + /// + /// By default, this is `Permissioned`, which implies only the pool member themselves can + /// claim their pending rewards. If a pool member wishes so, they can set this to + /// `PermissionlessAll` to allow any account to claim their rewards and bond extra to the + /// pool. + /// + /// # Arguments + /// + /// * `origin` - Member of a pool. + /// * `actor` - Account to claim reward. // improve this + #[pallet::call_index(15)] + #[pallet::weight(T::DbWeight::get().reads_writes(1, 1))] + pub fn set_claim_permission( + origin: OriginFor, + permission: ClaimPermission, + ) -> DispatchResult { + let who = ensure_signed(origin)?; + + ensure!(PoolMembers::::contains_key(&who), Error::::PoolMemberNotFound); + ClaimPermissions::::mutate(who, |source| { + *source = permission; + }); + Ok(()) + } + + /// `origin` can claim payouts on some pool member `other`'s behalf. + /// + /// Pool member `other` must have a `PermissionlessAll` or `PermissionlessWithdraw` in order + /// for this call to be successful. + #[pallet::call_index(16)] + #[pallet::weight(T::WeightInfo::claim_payout())] + pub fn claim_payout_other(origin: OriginFor, other: T::AccountId) -> DispatchResult { + let signer = ensure_signed(origin)?; + Self::do_claim_payout(signer, other) + } } #[pallet::hooks] @@ -2398,6 +2480,64 @@ impl Pallet { Ok(()) } + fn do_bond_extra( + signer: T::AccountId, + who: T::AccountId, + extra: BondExtra>, + ) -> DispatchResult { + if signer != who { + ensure!( + ClaimPermissions::::get(&who).can_bond_extra(), + Error::::DoesNotHavePermission + ); + ensure!(extra == BondExtra::Rewards, Error::::BondExtraRestricted); + } + + let (mut member, mut bonded_pool, mut reward_pool) = Self::get_member_with_pools(&who)?; + + // payout related stuff: we must claim the payouts, and updated recorded payout data + // before updating the bonded pool points, similar to that of `join` transaction. + reward_pool.update_records(bonded_pool.id, bonded_pool.points)?; + let claimed = + Self::do_reward_payout(&who, &mut member, &mut bonded_pool, &mut reward_pool)?; + + let (points_issued, bonded) = match extra { + BondExtra::FreeBalance(amount) => + (bonded_pool.try_bond_funds(&who, amount, BondType::Later)?, amount), + BondExtra::Rewards => + (bonded_pool.try_bond_funds(&who, claimed, BondType::Later)?, claimed), + }; + + bonded_pool.ok_to_be_open()?; + member.points = + member.points.checked_add(&points_issued).ok_or(Error::::OverflowRisk)?; + + Self::deposit_event(Event::::Bonded { + member: who.clone(), + pool_id: member.pool_id, + bonded, + joined: false, + }); + Self::put_member_with_pools(&who, member, bonded_pool, reward_pool); + + Ok(()) + } + + fn do_claim_payout(signer: T::AccountId, who: T::AccountId) -> DispatchResult { + if signer != who { + ensure!( + ClaimPermissions::::get(&who).can_claim_payout(), + Error::::DoesNotHavePermission + ); + } + let (mut member, mut bonded_pool, mut reward_pool) = Self::get_member_with_pools(&who)?; + + let _ = Self::do_reward_payout(&who, &mut member, &mut bonded_pool, &mut reward_pool)?; + + Self::put_member_with_pools(&who, member, bonded_pool, reward_pool); + Ok(()) + } + /// Ensure the correctness of the state of this pallet. /// /// This should be valid before or after each state transition of this pallet. diff --git a/frame/nomination-pools/src/tests.rs b/frame/nomination-pools/src/tests.rs index 59fd17be5..33eee0194 100644 --- a/frame/nomination-pools/src/tests.rs +++ b/frame/nomination-pools/src/tests.rs @@ -2104,6 +2104,36 @@ mod claim_payout { ); }) } + + #[test] + fn claim_payout_other_works() { + ExtBuilder::default().add_members(vec![(20, 20)]).build_and_execute(|| { + Balances::make_free_balance_be(&default_reward_account(), 8); + // ... of which only 3 are claimable to make sure the reward account does not die. + let claimable_reward = 8 - ExistentialDeposit::get(); + // NOTE: easier to read if we use 3, so let's use the number instead of variable. + assert_eq!(claimable_reward, 3, "test is correct if rewards are divisible by 3"); + + // given + assert_eq!(Balances::free_balance(10), 35); + + // Permissioned by default + assert_noop!( + Pools::claim_payout_other(RuntimeOrigin::signed(80), 10), + Error::::DoesNotHavePermission + ); + + assert_ok!(Pools::set_claim_permission( + RuntimeOrigin::signed(10), + ClaimPermission::PermissionlessWithdraw + )); + assert_ok!(Pools::claim_payout_other(RuntimeOrigin::signed(80), 10)); + + // then + assert_eq!(Balances::free_balance(10), 36); + assert_eq!(Balances::free_balance(&default_reward_account()), 7); + }) + } } mod unbond { @@ -2130,10 +2160,21 @@ mod unbond { Error::::MinimumBondNotMet ); + // Make permissionless + assert_eq!(ClaimPermissions::::get(10), ClaimPermission::Permissioned); + assert_ok!(Pools::set_claim_permission( + RuntimeOrigin::signed(20), + ClaimPermission::PermissionlessAll + )); + // but can go to 0 assert_ok!(Pools::unbond(RuntimeOrigin::signed(20), 20, 15)); assert_eq!(PoolMembers::::get(20).unwrap().active_points(), 0); assert_eq!(PoolMembers::::get(20).unwrap().unbonding_points(), 20); + assert_eq!( + ClaimPermissions::::get(20), + ClaimPermission::PermissionlessAll + ); }) } @@ -4098,6 +4139,49 @@ mod withdraw_unbonded { assert!(!Metadata::::contains_key(1)); }) } + + #[test] + fn withdraw_unbonded_removes_claim_permissions_on_leave() { + ExtBuilder::default().add_members(vec![(20, 20)]).build_and_execute(|| { + // Given + CurrentEra::set(1); + assert_eq!(PoolMembers::::get(20).unwrap().points, 20); + + assert_ok!(Pools::set_claim_permission( + RuntimeOrigin::signed(20), + ClaimPermission::PermissionlessAll + )); + assert_ok!(Pools::unbond(RuntimeOrigin::signed(20), 20, 20)); + assert_eq!(ClaimPermissions::::get(20), ClaimPermission::PermissionlessAll); + + assert_eq!( + pool_events_since_last_call(), + vec![ + Event::Created { depositor: 10, pool_id: 1 }, + Event::Bonded { member: 10, pool_id: 1, bonded: 10, joined: true }, + Event::Bonded { member: 20, pool_id: 1, bonded: 20, joined: true }, + Event::Unbonded { member: 20, pool_id: 1, balance: 20, points: 20, era: 4 }, + ] + ); + + CurrentEra::set(5); + + // When + assert_ok!(Pools::withdraw_unbonded(RuntimeOrigin::signed(20), 20, 0)); + + assert_eq!( + pool_events_since_last_call(), + vec![ + Event::Withdrawn { member: 20, pool_id: 1, balance: 20, points: 20 }, + Event::MemberRemoved { pool_id: 1, member: 20 } + ] + ); + + // Then + assert_eq!(PoolMembers::::get(20), None); + assert_eq!(ClaimPermissions::::contains_key(20), false); + }); + } } mod create { @@ -4280,6 +4364,45 @@ mod create { } } +#[test] +fn set_claimable_actor_works() { + ExtBuilder::default().build_and_execute(|| { + // Given + Balances::make_free_balance_be(&11, ExistentialDeposit::get() + 2); + assert!(!PoolMembers::::contains_key(&11)); + + // When + assert_ok!(Pools::join(RuntimeOrigin::signed(11), 2, 1)); + + // Then + assert_eq!( + pool_events_since_last_call(), + vec![ + Event::Created { depositor: 10, pool_id: 1 }, + Event::Bonded { member: 10, pool_id: 1, bonded: 10, joined: true }, + Event::Bonded { member: 11, pool_id: 1, bonded: 2, joined: true }, + ] + ); + + // Make permissionless + assert_eq!(ClaimPermissions::::get(11), ClaimPermission::Permissioned); + assert_noop!( + Pools::set_claim_permission( + RuntimeOrigin::signed(12), + ClaimPermission::PermissionlessAll + ), + Error::::PoolMemberNotFound + ); + assert_ok!(Pools::set_claim_permission( + RuntimeOrigin::signed(11), + ClaimPermission::PermissionlessAll + )); + + // then + assert_eq!(ClaimPermissions::::get(11), ClaimPermission::PermissionlessAll); + }); +} + mod nominate { use super::*; @@ -4592,6 +4715,7 @@ mod bond_extra { // when assert_ok!(Pools::bond_extra(RuntimeOrigin::signed(10), BondExtra::Rewards)); + assert_eq!(Balances::free_balance(&default_reward_account()), 7); // then assert_eq!(Balances::free_balance(10), 35); @@ -4623,6 +4747,61 @@ mod bond_extra { ); }) } + + #[test] + fn bond_extra_other() { + ExtBuilder::default().add_members(vec![(20, 20)]).build_and_execute(|| { + Balances::make_free_balance_be(&default_reward_account(), 8); + // ... of which only 3 are claimable to make sure the reward account does not die. + let claimable_reward = 8 - ExistentialDeposit::get(); + // NOTE: easier to read if we use 3, so let's use the number instead of variable. + assert_eq!(claimable_reward, 3, "test is correct if rewards are divisible by 3"); + + // given + assert_eq!(PoolMembers::::get(10).unwrap().points, 10); + assert_eq!(PoolMembers::::get(20).unwrap().points, 20); + assert_eq!(BondedPools::::get(1).unwrap().points, 30); + assert_eq!(Balances::free_balance(10), 35); + assert_eq!(Balances::free_balance(20), 20); + + // Permissioned by default + assert_noop!( + Pools::bond_extra_other(RuntimeOrigin::signed(80), 20, BondExtra::Rewards), + Error::::DoesNotHavePermission + ); + + assert_ok!(Pools::set_claim_permission( + RuntimeOrigin::signed(10), + ClaimPermission::PermissionlessAll + )); + assert_ok!(Pools::bond_extra_other(RuntimeOrigin::signed(50), 10, BondExtra::Rewards)); + assert_eq!(Balances::free_balance(&default_reward_account()), 7); + + // then + assert_eq!(Balances::free_balance(10), 35); + assert_eq!(PoolMembers::::get(10).unwrap().points, 10 + 1); + assert_eq!(BondedPools::::get(1).unwrap().points, 30 + 1); + + // when + assert_noop!( + Pools::bond_extra_other(RuntimeOrigin::signed(40), 40, BondExtra::Rewards), + Error::::PoolMemberNotFound + ); + + // when + assert_ok!(Pools::bond_extra_other( + RuntimeOrigin::signed(20), + 20, + BondExtra::FreeBalance(10) + )); + + // then + assert_eq!(Balances::free_balance(20), 12); + assert_eq!(Balances::free_balance(&default_reward_account()), 5); + assert_eq!(PoolMembers::::get(20).unwrap().points, 30); + assert_eq!(BondedPools::::get(1).unwrap().points, 41); + }) + } } mod update_roles { diff --git a/frame/nomination-pools/src/weights.rs b/frame/nomination-pools/src/weights.rs index c24689558..1c3d1c633 100644 --- a/frame/nomination-pools/src/weights.rs +++ b/frame/nomination-pools/src/weights.rs @@ -18,25 +18,26 @@ //! Autogenerated weights for pallet_nomination_pools //! //! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev -//! DATE: 2023-01-24, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! DATE: 2023-02-21, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` //! WORST CASE MAP SIZE: `1000000` -//! HOSTNAME: `bm2`, CPU: `Intel(R) Core(TM) i7-7700K CPU @ 4.20GHz` +//! HOSTNAME: `runner-ehxwxxsd-project-145-concurrent-0`, CPU: `Intel(R) Xeon(R) CPU @ 2.60GHz` //! EXECUTION: Some(Wasm), WASM-EXECUTION: Compiled, CHAIN: Some("dev"), DB CACHE: 1024 // Executed Command: -// ./target/production/substrate +// target/production/substrate // benchmark // pallet -// --chain=dev // --steps=50 // --repeat=20 -// --pallet=pallet_nomination_pools // --extrinsic=* // --execution=wasm // --wasm-execution=compiled // --heap-pages=4096 -// --output=./frame/nomination-pools/src/weights.rs +// --json-file=/builds/parity/mirrors/substrate/.git/.artifacts/bench.json +// --pallet=pallet_nomination_pools +// --chain=dev // --header=./HEADER-APACHE2 +// --output=./frame/nomination-pools/src/weights.rs // --template=./.maintain/frame-weight-template.hbs #![cfg_attr(rustfmt, rustfmt_skip)] @@ -50,7 +51,7 @@ use sp_std::marker::PhantomData; pub trait WeightInfo { fn join() -> Weight; fn bond_extra_transfer() -> Weight; - fn bond_extra_reward() -> Weight; + fn bond_extra_other() -> Weight; fn claim_payout() -> Weight; fn unbond() -> Weight; fn pool_withdraw_unbonded(s: u32, ) -> Weight; @@ -63,6 +64,7 @@ pub trait WeightInfo { fn set_configs() -> Weight; fn update_roles() -> Weight; fn chill() -> Weight; + fn set_claim_permission() -> Weight; } /// Weights for pallet_nomination_pools using the Substrate node and recommended hardware. @@ -98,8 +100,9 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `3573` // Estimated: `37988` - // Minimum execution time: 140_155 nanoseconds. - Weight::from_parts(141_098_000, 37988) + // Minimum execution time: 169_857 nanoseconds. + Weight::from_ref_time(173_895_000) + .saturating_add(Weight::from_proof_size(37988)) .saturating_add(T::DbWeight::get().reads(17_u64)) .saturating_add(T::DbWeight::get().writes(12_u64)) } @@ -125,11 +128,14 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `3615` // Estimated: `38583` - // Minimum execution time: 136_048 nanoseconds. - Weight::from_parts(136_767_000, 38583) + // Minimum execution time: 167_372 nanoseconds. + Weight::from_ref_time(168_776_000) + .saturating_add(Weight::from_proof_size(38583)) .saturating_add(T::DbWeight::get().reads(14_u64)) .saturating_add(T::DbWeight::get().writes(12_u64)) } + /// Storage: NominationPools ClaimPermissions (r:1 w:0) + /// Proof: NominationPools ClaimPermissions (max_values: None, max_size: Some(41), added: 2516, mode: MaxEncodedLen) /// Storage: NominationPools PoolMembers (r:1 w:1) /// Proof: NominationPools PoolMembers (max_values: None, max_size: Some(237), added: 2712, mode: MaxEncodedLen) /// Storage: NominationPools BondedPools (r:1 w:1) @@ -148,15 +154,18 @@ impl WeightInfo for SubstrateWeight { /// Proof: VoterList ListNodes (max_values: None, max_size: Some(154), added: 2629, mode: MaxEncodedLen) /// Storage: VoterList ListBags (r:2 w:2) /// Proof: VoterList ListBags (max_values: None, max_size: Some(82), added: 2557, mode: MaxEncodedLen) - fn bond_extra_reward() -> Weight { + fn bond_extra_other() -> Weight { // Proof Size summary in bytes: - // Measured: `3615` - // Estimated: `38583` - // Minimum execution time: 151_473 nanoseconds. - Weight::from_parts(152_375_000, 38583) - .saturating_add(T::DbWeight::get().reads(14_u64)) + // Measured: `3680` + // Estimated: `41099` + // Minimum execution time: 186_346 nanoseconds. + Weight::from_ref_time(191_308_000) + .saturating_add(Weight::from_proof_size(41099)) + .saturating_add(T::DbWeight::get().reads(15_u64)) .saturating_add(T::DbWeight::get().writes(13_u64)) } + /// Storage: NominationPools ClaimPermissions (r:1 w:0) + /// Proof: NominationPools ClaimPermissions (max_values: None, max_size: Some(41), added: 2516, mode: MaxEncodedLen) /// Storage: NominationPools PoolMembers (r:1 w:1) /// Proof: NominationPools PoolMembers (max_values: None, max_size: Some(237), added: 2712, mode: MaxEncodedLen) /// Storage: NominationPools BondedPools (r:1 w:1) @@ -167,11 +176,12 @@ impl WeightInfo for SubstrateWeight { /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) fn claim_payout() -> Weight { // Proof Size summary in bytes: - // Measured: `1189` - // Estimated: `10489` - // Minimum execution time: 51_146 nanoseconds. - Weight::from_parts(51_570_000, 10489) - .saturating_add(T::DbWeight::get().reads(4_u64)) + // Measured: `1254` + // Estimated: `13005` + // Minimum execution time: 61_423 nanoseconds. + Weight::from_ref_time(63_219_000) + .saturating_add(Weight::from_proof_size(13005)) + .saturating_add(T::DbWeight::get().reads(5_u64)) .saturating_add(T::DbWeight::get().writes(4_u64)) } /// Storage: NominationPools PoolMembers (r:1 w:1) @@ -202,14 +212,17 @@ impl WeightInfo for SubstrateWeight { /// Proof: NominationPools SubPoolsStorage (max_values: None, max_size: Some(24382), added: 26857, mode: MaxEncodedLen) /// Storage: NominationPools CounterForSubPoolsStorage (r:1 w:1) /// Proof: NominationPools CounterForSubPoolsStorage (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) + /// Storage: NominationPools ClaimPermissions (r:0 w:1) + /// Proof: NominationPools ClaimPermissions (max_values: None, max_size: Some(41), added: 2516, mode: MaxEncodedLen) fn unbond() -> Weight { // Proof Size summary in bytes: // Measured: `3858` // Estimated: `67379` - // Minimum execution time: 141_400 nanoseconds. - Weight::from_parts(141_822_000, 67379) + // Minimum execution time: 174_532 nanoseconds. + Weight::from_ref_time(180_032_000) + .saturating_add(Weight::from_proof_size(67379)) .saturating_add(T::DbWeight::get().reads(18_u64)) - .saturating_add(T::DbWeight::get().writes(13_u64)) + .saturating_add(T::DbWeight::get().writes(14_u64)) } /// Storage: NominationPools BondedPools (r:1 w:0) /// Proof: NominationPools BondedPools (max_values: None, max_size: Some(164), added: 2639, mode: MaxEncodedLen) @@ -226,10 +239,11 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `1779` // Estimated: `13025` - // Minimum execution time: 49_021 nanoseconds. - Weight::from_parts(49_954_282, 13025) - // Standard Error: 378 - .saturating_add(Weight::from_ref_time(5_165).saturating_mul(s.into())) + // Minimum execution time: 55_327 nanoseconds. + Weight::from_ref_time(58_947_746) + .saturating_add(Weight::from_proof_size(13025)) + // Standard Error: 1_589 + .saturating_add(Weight::from_ref_time(40_696).saturating_mul(s.into())) .saturating_add(T::DbWeight::get().reads(5_u64)) .saturating_add(T::DbWeight::get().writes(2_u64)) } @@ -256,10 +270,11 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `2303` // Estimated: `45696` - // Minimum execution time: 92_473 nanoseconds. - Weight::from_parts(93_901_972, 45696) - // Standard Error: 618 - .saturating_add(Weight::from_ref_time(12_032).saturating_mul(s.into())) + // Minimum execution time: 105_923 nanoseconds. + Weight::from_ref_time(110_572_476) + .saturating_add(Weight::from_proof_size(45696)) + // Standard Error: 2_438 + .saturating_add(Weight::from_ref_time(69_045).saturating_mul(s.into())) .saturating_add(T::DbWeight::get().reads(9_u64)) .saturating_add(T::DbWeight::get().writes(7_u64)) } @@ -308,8 +323,9 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `2690` // Estimated: `68812` - // Minimum execution time: 150_063 nanoseconds. - Weight::from_parts(152_321_387, 68812) + // Minimum execution time: 169_700 nanoseconds. + Weight::from_ref_time(178_693_541) + .saturating_add(Weight::from_proof_size(68812)) .saturating_add(T::DbWeight::get().reads(20_u64)) .saturating_add(T::DbWeight::get().writes(17_u64)) } @@ -359,8 +375,9 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `1321` // Estimated: `31522` - // Minimum execution time: 131_430 nanoseconds. - Weight::from_parts(132_214_000, 31522) + // Minimum execution time: 145_976 nanoseconds. + Weight::from_ref_time(150_664_000) + .saturating_add(Weight::from_proof_size(31522)) .saturating_add(T::DbWeight::get().reads(21_u64)) .saturating_add(T::DbWeight::get().writes(15_u64)) } @@ -393,10 +410,11 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `1909` // Estimated: `21998 + n * (2520 ±0)` - // Minimum execution time: 61_798 nanoseconds. - Weight::from_parts(61_504_758, 21998) - // Standard Error: 4_046 - .saturating_add(Weight::from_ref_time(1_159_175).saturating_mul(n.into())) + // Minimum execution time: 69_288 nanoseconds. + Weight::from_ref_time(71_075_293) + .saturating_add(Weight::from_proof_size(21998)) + // Standard Error: 10_508 + .saturating_add(Weight::from_ref_time(1_384_674).saturating_mul(n.into())) .saturating_add(T::DbWeight::get().reads(12_u64)) .saturating_add(T::DbWeight::get().reads((1_u64).saturating_mul(n.into()))) .saturating_add(T::DbWeight::get().writes(5_u64)) @@ -412,8 +430,9 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `1498` // Estimated: `8752` - // Minimum execution time: 32_433 nanoseconds. - Weight::from_parts(32_894_000, 8752) + // Minimum execution time: 36_410 nanoseconds. + Weight::from_ref_time(37_585_000) + .saturating_add(Weight::from_proof_size(8752)) .saturating_add(T::DbWeight::get().reads(3_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -428,10 +447,11 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `559` // Estimated: `5883` - // Minimum execution time: 13_608 nanoseconds. - Weight::from_parts(13_966_346, 5883) - // Standard Error: 44 - .saturating_add(Weight::from_ref_time(1_511).saturating_mul(n.into())) + // Minimum execution time: 14_322 nanoseconds. + Weight::from_ref_time(15_328_204) + .saturating_add(Weight::from_proof_size(5883)) + // Standard Error: 161 + .saturating_add(Weight::from_ref_time(1_406).saturating_mul(n.into())) .saturating_add(T::DbWeight::get().reads(3_u64)) .saturating_add(T::DbWeight::get().writes(2_u64)) } @@ -449,8 +469,9 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 5_832 nanoseconds. - Weight::from_ref_time(6_117_000) + // Minimum execution time: 5_968 nanoseconds. + Weight::from_ref_time(6_245_000) + .saturating_add(Weight::from_proof_size(0)) .saturating_add(T::DbWeight::get().writes(5_u64)) } /// Storage: NominationPools BondedPools (r:1 w:1) @@ -459,8 +480,9 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `559` // Estimated: `2639` - // Minimum execution time: 18_160 nanoseconds. - Weight::from_parts(18_567_000, 2639) + // Minimum execution time: 18_979 nanoseconds. + Weight::from_ref_time(19_795_000) + .saturating_add(Weight::from_proof_size(2639)) .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -486,11 +508,26 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `2136` // Estimated: `20489` - // Minimum execution time: 58_991 nanoseconds. - Weight::from_parts(59_528_000, 20489) + // Minimum execution time: 68_145 nanoseconds. + Weight::from_ref_time(70_444_000) + .saturating_add(Weight::from_proof_size(20489)) .saturating_add(T::DbWeight::get().reads(9_u64)) .saturating_add(T::DbWeight::get().writes(5_u64)) } + /// Storage: NominationPools PoolMembers (r:1 w:0) + /// Proof: NominationPools PoolMembers (max_values: None, max_size: Some(237), added: 2712, mode: MaxEncodedLen) + /// Storage: NominationPools ClaimPermissions (r:1 w:1) + /// Proof: NominationPools ClaimPermissions (max_values: None, max_size: Some(41), added: 2516, mode: MaxEncodedLen) + fn set_claim_permission() -> Weight { + // Proof Size summary in bytes: + // Measured: `542` + // Estimated: `5228` + // Minimum execution time: 15_112 nanoseconds. + Weight::from_ref_time(15_897_000) + .saturating_add(Weight::from_proof_size(5228)) + .saturating_add(T::DbWeight::get().reads(2_u64)) + .saturating_add(T::DbWeight::get().writes(1_u64)) + } } // For backwards compatibility and tests @@ -525,8 +562,9 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `3573` // Estimated: `37988` - // Minimum execution time: 140_155 nanoseconds. - Weight::from_parts(141_098_000, 37988) + // Minimum execution time: 169_857 nanoseconds. + Weight::from_ref_time(173_895_000) + .saturating_add(Weight::from_proof_size(37988)) .saturating_add(RocksDbWeight::get().reads(17_u64)) .saturating_add(RocksDbWeight::get().writes(12_u64)) } @@ -552,11 +590,14 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `3615` // Estimated: `38583` - // Minimum execution time: 136_048 nanoseconds. - Weight::from_parts(136_767_000, 38583) + // Minimum execution time: 167_372 nanoseconds. + Weight::from_ref_time(168_776_000) + .saturating_add(Weight::from_proof_size(38583)) .saturating_add(RocksDbWeight::get().reads(14_u64)) .saturating_add(RocksDbWeight::get().writes(12_u64)) } + /// Storage: NominationPools ClaimPermissions (r:1 w:0) + /// Proof: NominationPools ClaimPermissions (max_values: None, max_size: Some(41), added: 2516, mode: MaxEncodedLen) /// Storage: NominationPools PoolMembers (r:1 w:1) /// Proof: NominationPools PoolMembers (max_values: None, max_size: Some(237), added: 2712, mode: MaxEncodedLen) /// Storage: NominationPools BondedPools (r:1 w:1) @@ -575,15 +616,18 @@ impl WeightInfo for () { /// Proof: VoterList ListNodes (max_values: None, max_size: Some(154), added: 2629, mode: MaxEncodedLen) /// Storage: VoterList ListBags (r:2 w:2) /// Proof: VoterList ListBags (max_values: None, max_size: Some(82), added: 2557, mode: MaxEncodedLen) - fn bond_extra_reward() -> Weight { + fn bond_extra_other() -> Weight { // Proof Size summary in bytes: - // Measured: `3615` - // Estimated: `38583` - // Minimum execution time: 151_473 nanoseconds. - Weight::from_parts(152_375_000, 38583) - .saturating_add(RocksDbWeight::get().reads(14_u64)) + // Measured: `3680` + // Estimated: `41099` + // Minimum execution time: 186_346 nanoseconds. + Weight::from_ref_time(191_308_000) + .saturating_add(Weight::from_proof_size(41099)) + .saturating_add(RocksDbWeight::get().reads(15_u64)) .saturating_add(RocksDbWeight::get().writes(13_u64)) } + /// Storage: NominationPools ClaimPermissions (r:1 w:0) + /// Proof: NominationPools ClaimPermissions (max_values: None, max_size: Some(41), added: 2516, mode: MaxEncodedLen) /// Storage: NominationPools PoolMembers (r:1 w:1) /// Proof: NominationPools PoolMembers (max_values: None, max_size: Some(237), added: 2712, mode: MaxEncodedLen) /// Storage: NominationPools BondedPools (r:1 w:1) @@ -594,11 +638,12 @@ impl WeightInfo for () { /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) fn claim_payout() -> Weight { // Proof Size summary in bytes: - // Measured: `1189` - // Estimated: `10489` - // Minimum execution time: 51_146 nanoseconds. - Weight::from_parts(51_570_000, 10489) - .saturating_add(RocksDbWeight::get().reads(4_u64)) + // Measured: `1254` + // Estimated: `13005` + // Minimum execution time: 61_423 nanoseconds. + Weight::from_ref_time(63_219_000) + .saturating_add(Weight::from_proof_size(13005)) + .saturating_add(RocksDbWeight::get().reads(5_u64)) .saturating_add(RocksDbWeight::get().writes(4_u64)) } /// Storage: NominationPools PoolMembers (r:1 w:1) @@ -629,14 +674,17 @@ impl WeightInfo for () { /// Proof: NominationPools SubPoolsStorage (max_values: None, max_size: Some(24382), added: 26857, mode: MaxEncodedLen) /// Storage: NominationPools CounterForSubPoolsStorage (r:1 w:1) /// Proof: NominationPools CounterForSubPoolsStorage (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) + /// Storage: NominationPools ClaimPermissions (r:0 w:1) + /// Proof: NominationPools ClaimPermissions (max_values: None, max_size: Some(41), added: 2516, mode: MaxEncodedLen) fn unbond() -> Weight { // Proof Size summary in bytes: // Measured: `3858` // Estimated: `67379` - // Minimum execution time: 141_400 nanoseconds. - Weight::from_parts(141_822_000, 67379) + // Minimum execution time: 174_532 nanoseconds. + Weight::from_ref_time(180_032_000) + .saturating_add(Weight::from_proof_size(67379)) .saturating_add(RocksDbWeight::get().reads(18_u64)) - .saturating_add(RocksDbWeight::get().writes(13_u64)) + .saturating_add(RocksDbWeight::get().writes(14_u64)) } /// Storage: NominationPools BondedPools (r:1 w:0) /// Proof: NominationPools BondedPools (max_values: None, max_size: Some(164), added: 2639, mode: MaxEncodedLen) @@ -653,10 +701,11 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `1779` // Estimated: `13025` - // Minimum execution time: 49_021 nanoseconds. - Weight::from_parts(49_954_282, 13025) - // Standard Error: 378 - .saturating_add(Weight::from_ref_time(5_165).saturating_mul(s.into())) + // Minimum execution time: 55_327 nanoseconds. + Weight::from_ref_time(58_947_746) + .saturating_add(Weight::from_proof_size(13025)) + // Standard Error: 1_589 + .saturating_add(Weight::from_ref_time(40_696).saturating_mul(s.into())) .saturating_add(RocksDbWeight::get().reads(5_u64)) .saturating_add(RocksDbWeight::get().writes(2_u64)) } @@ -683,10 +732,11 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `2303` // Estimated: `45696` - // Minimum execution time: 92_473 nanoseconds. - Weight::from_parts(93_901_972, 45696) - // Standard Error: 618 - .saturating_add(Weight::from_ref_time(12_032).saturating_mul(s.into())) + // Minimum execution time: 105_923 nanoseconds. + Weight::from_ref_time(110_572_476) + .saturating_add(Weight::from_proof_size(45696)) + // Standard Error: 2_438 + .saturating_add(Weight::from_ref_time(69_045).saturating_mul(s.into())) .saturating_add(RocksDbWeight::get().reads(9_u64)) .saturating_add(RocksDbWeight::get().writes(7_u64)) } @@ -735,8 +785,9 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `2690` // Estimated: `68812` - // Minimum execution time: 150_063 nanoseconds. - Weight::from_parts(152_321_387, 68812) + // Minimum execution time: 169_700 nanoseconds. + Weight::from_ref_time(178_693_541) + .saturating_add(Weight::from_proof_size(68812)) .saturating_add(RocksDbWeight::get().reads(20_u64)) .saturating_add(RocksDbWeight::get().writes(17_u64)) } @@ -786,8 +837,9 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `1321` // Estimated: `31522` - // Minimum execution time: 131_430 nanoseconds. - Weight::from_parts(132_214_000, 31522) + // Minimum execution time: 145_976 nanoseconds. + Weight::from_ref_time(150_664_000) + .saturating_add(Weight::from_proof_size(31522)) .saturating_add(RocksDbWeight::get().reads(21_u64)) .saturating_add(RocksDbWeight::get().writes(15_u64)) } @@ -820,10 +872,11 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `1909` // Estimated: `21998 + n * (2520 ±0)` - // Minimum execution time: 61_798 nanoseconds. - Weight::from_parts(61_504_758, 21998) - // Standard Error: 4_046 - .saturating_add(Weight::from_ref_time(1_159_175).saturating_mul(n.into())) + // Minimum execution time: 69_288 nanoseconds. + Weight::from_ref_time(71_075_293) + .saturating_add(Weight::from_proof_size(21998)) + // Standard Error: 10_508 + .saturating_add(Weight::from_ref_time(1_384_674).saturating_mul(n.into())) .saturating_add(RocksDbWeight::get().reads(12_u64)) .saturating_add(RocksDbWeight::get().reads((1_u64).saturating_mul(n.into()))) .saturating_add(RocksDbWeight::get().writes(5_u64)) @@ -839,8 +892,9 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `1498` // Estimated: `8752` - // Minimum execution time: 32_433 nanoseconds. - Weight::from_parts(32_894_000, 8752) + // Minimum execution time: 36_410 nanoseconds. + Weight::from_ref_time(37_585_000) + .saturating_add(Weight::from_proof_size(8752)) .saturating_add(RocksDbWeight::get().reads(3_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } @@ -855,10 +909,11 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `559` // Estimated: `5883` - // Minimum execution time: 13_608 nanoseconds. - Weight::from_parts(13_966_346, 5883) - // Standard Error: 44 - .saturating_add(Weight::from_ref_time(1_511).saturating_mul(n.into())) + // Minimum execution time: 14_322 nanoseconds. + Weight::from_ref_time(15_328_204) + .saturating_add(Weight::from_proof_size(5883)) + // Standard Error: 161 + .saturating_add(Weight::from_ref_time(1_406).saturating_mul(n.into())) .saturating_add(RocksDbWeight::get().reads(3_u64)) .saturating_add(RocksDbWeight::get().writes(2_u64)) } @@ -876,8 +931,9 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 5_832 nanoseconds. - Weight::from_ref_time(6_117_000) + // Minimum execution time: 5_968 nanoseconds. + Weight::from_ref_time(6_245_000) + .saturating_add(Weight::from_proof_size(0)) .saturating_add(RocksDbWeight::get().writes(5_u64)) } /// Storage: NominationPools BondedPools (r:1 w:1) @@ -886,8 +942,9 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `559` // Estimated: `2639` - // Minimum execution time: 18_160 nanoseconds. - Weight::from_parts(18_567_000, 2639) + // Minimum execution time: 18_979 nanoseconds. + Weight::from_ref_time(19_795_000) + .saturating_add(Weight::from_proof_size(2639)) .saturating_add(RocksDbWeight::get().reads(1_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } @@ -913,9 +970,24 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `2136` // Estimated: `20489` - // Minimum execution time: 58_991 nanoseconds. - Weight::from_parts(59_528_000, 20489) + // Minimum execution time: 68_145 nanoseconds. + Weight::from_ref_time(70_444_000) + .saturating_add(Weight::from_proof_size(20489)) .saturating_add(RocksDbWeight::get().reads(9_u64)) .saturating_add(RocksDbWeight::get().writes(5_u64)) } + /// Storage: NominationPools PoolMembers (r:1 w:0) + /// Proof: NominationPools PoolMembers (max_values: None, max_size: Some(237), added: 2712, mode: MaxEncodedLen) + /// Storage: NominationPools ClaimPermissions (r:1 w:1) + /// Proof: NominationPools ClaimPermissions (max_values: None, max_size: Some(41), added: 2516, mode: MaxEncodedLen) + fn set_claim_permission() -> Weight { + // Proof Size summary in bytes: + // Measured: `542` + // Estimated: `5228` + // Minimum execution time: 15_112 nanoseconds. + Weight::from_ref_time(15_897_000) + .saturating_add(Weight::from_proof_size(5228)) + .saturating_add(RocksDbWeight::get().reads(2_u64)) + .saturating_add(RocksDbWeight::get().writes(1_u64)) + } } From b0ed48e2295861b79ca7399323d6d64157fa2303 Mon Sep 17 00:00:00 2001 From: Jegor Sidorenko <5252494+jsidorenko@users.noreply.github.com> Date: Wed, 22 Feb 2023 15:50:40 +0200 Subject: [PATCH 153/558] [NFTs] Update attributes with offchain signature (#13390) * Allow to mint with the pre-signed signatures * Another try * WIP: test encoder * Fix the deposits * Refactoring + tests + benchmarks * Add sp-core/runtime-benchmarks * Remove sp-core from dev deps * Enable full_crypto for benchmarks * Typo * Fix * Update frame/nfts/src/mock.rs Co-authored-by: Squirrel * ".git/.scripts/commands/bench/bench.sh" pallet dev pallet_nfts * Add docs * Add attributes into the pre-signed object & track the deposit owner for attributes * Update docs * ".git/.scripts/commands/bench/bench.sh" pallet dev pallet_nfts * Add the number of attributes provided to weights * Support pre-signed attributes * Update docs * Fix merge artifacts * Update docs * Add more tests * ".git/.scripts/commands/bench/bench.sh" pallet dev pallet_nfts * Update frame/nfts/src/types.rs Co-authored-by: joe petrowski <25483142+joepetrowski@users.noreply.github.com> * Update types.rs --------- Co-authored-by: Squirrel Co-authored-by: command-bot <> Co-authored-by: joe petrowski <25483142+joepetrowski@users.noreply.github.com> --- frame/nfts/src/benchmarking.rs | 50 +++ frame/nfts/src/features/attributes.rs | 53 +++ frame/nfts/src/lib.rs | 35 ++ frame/nfts/src/tests.rs | 308 ++++++++++++++++ frame/nfts/src/types.rs | 22 +- frame/nfts/src/weights.rs | 497 ++++++++++++++++---------- 6 files changed, 782 insertions(+), 183 deletions(-) diff --git a/frame/nfts/src/benchmarking.rs b/frame/nfts/src/benchmarking.rs index efa0a7412..ae628fbef 100644 --- a/frame/nfts/src/benchmarking.rs +++ b/frame/nfts/src/benchmarking.rs @@ -773,5 +773,55 @@ benchmarks_instance_pallet! { assert_last_event::(Event::ItemMetadataSet { collection, item, data: metadata }.into()); } + set_attributes_pre_signed { + let n in 0 .. T::MaxAttributesPerCall::get() as u32; + let (collection, _, _) = create_collection::(); + + let item_owner: T::AccountId = account("item_owner", 0, SEED); + let item_owner_lookup = T::Lookup::unlookup(item_owner.clone()); + + let signer_public = sr25519_generate(0.into(), None); + let signer: T::AccountId = MultiSigner::Sr25519(signer_public).into_account().into(); + + T::Currency::make_free_balance_be(&item_owner, DepositBalanceOf::::max_value()); + + let item = T::Helper::item(0); + assert_ok!(Nfts::::force_mint( + SystemOrigin::Root.into(), + collection, + item, + item_owner_lookup.clone(), + default_item_config(), + )); + + let mut attributes = vec![]; + let attribute_value = vec![0u8; T::ValueLimit::get() as usize]; + for i in 0..n { + let attribute_key = make_filled_vec(i as u16, T::KeyLimit::get() as usize); + attributes.push((attribute_key, attribute_value.clone())); + } + let pre_signed_data = PreSignedAttributes { + collection, + item, + attributes, + namespace: AttributeNamespace::Account(signer.clone()), + deadline: One::one(), + }; + let message = Encode::encode(&pre_signed_data); + let signature = MultiSignature::Sr25519(sr25519_sign(0.into(), &signer_public, &message).unwrap()); + + frame_system::Pallet::::set_block_number(One::one()); + }: _(SystemOrigin::Signed(item_owner.clone()), pre_signed_data, signature.into(), signer.clone()) + verify { + assert_last_event::( + Event::PreSignedAttributesSet { + collection, + item, + namespace: AttributeNamespace::Account(signer.clone()), + } + .into(), + ); + } + impl_benchmark_test_suite!(Nfts, crate::mock::new_test_ext(), crate::mock::Test); } diff --git a/frame/nfts/src/features/attributes.rs b/frame/nfts/src/features/attributes.rs index e310a5e2e..fbd1c9324 100644 --- a/frame/nfts/src/features/attributes.rs +++ b/frame/nfts/src/features/attributes.rs @@ -165,6 +165,59 @@ impl, I: 'static> Pallet { Ok(()) } + pub(crate) fn do_set_attributes_pre_signed( + origin: T::AccountId, + data: PreSignedAttributesOf, + signer: T::AccountId, + ) -> DispatchResult { + let PreSignedAttributes { collection, item, attributes, namespace, deadline } = data; + + ensure!( + attributes.len() <= T::MaxAttributesPerCall::get() as usize, + Error::::MaxAttributesLimitReached + ); + + let now = frame_system::Pallet::::block_number(); + ensure!(deadline >= now, Error::::DeadlineExpired); + + let item_details = + Item::::get(&collection, &item).ok_or(Error::::UnknownItem)?; + ensure!(item_details.owner == origin, Error::::NoPermission); + + // Only the CollectionOwner and Account() namespaces could be updated in this way. + // For the Account() namespace we check and set the approval if it wasn't set before. + match &namespace { + AttributeNamespace::CollectionOwner => {}, + AttributeNamespace::Account(account) => { + ensure!(account == &signer, Error::::NoPermission); + let approvals = ItemAttributesApprovalsOf::::get(&collection, &item); + if !approvals.contains(account) { + Self::do_approve_item_attributes( + origin.clone(), + collection, + item, + account.clone(), + )?; + } + }, + _ => return Err(Error::::WrongNamespace.into()), + } + + for (key, value) in attributes { + Self::do_set_attribute( + signer.clone(), + collection, + Some(item), + namespace.clone(), + Self::construct_attribute_key(key)?, + Self::construct_attribute_value(value)?, + origin.clone(), + )?; + } + Self::deposit_event(Event::PreSignedAttributesSet { collection, item, namespace }); + Ok(()) + } + pub(crate) fn do_clear_attribute( maybe_check_owner: Option, collection: T::CollectionId, diff --git a/frame/nfts/src/lib.rs b/frame/nfts/src/lib.rs index 41313e651..2c3ab290c 100644 --- a/frame/nfts/src/lib.rs +++ b/frame/nfts/src/lib.rs @@ -526,6 +526,12 @@ pub mod pallet { price: Option>>, deadline: ::BlockNumber, }, + /// New attributes have been set for an `item` of the `collection`. + PreSignedAttributesSet { + collection: T::CollectionId, + item: T::ItemId, + namespace: AttributeNamespace, + }, } #[pallet::error] @@ -614,6 +620,8 @@ pub mod pallet { IncorrectMetadata, /// Can't set more attributes per one call. MaxAttributesLimitReached, + /// The provided namespace isn't supported in this call. + WrongNamespace, } #[pallet::call] @@ -1824,6 +1832,33 @@ pub mod pallet { ensure!(signature.verify(&*msg, &signer), Error::::WrongSignature); Self::do_mint_pre_signed(origin, mint_data, signer) } + + /// Set attributes for an item by providing the pre-signed approval. + /// + /// Origin must be Signed and must be an owner of the `data.item`. + /// + /// - `data`: The pre-signed approval that consists of the information about the item, + /// attributes to update and until what block number. + /// - `signature`: The signature of the `data` object. + /// - `signer`: The `data` object's signer. Should be an owner of the collection for the + /// `CollectionOwner` namespace. + /// + /// Emits `AttributeSet` for each provided attribute. + /// Emits `ItemAttributesApprovalAdded` if the approval wasn't set before. + /// Emits `PreSignedAttributesSet` on success. + #[pallet::call_index(38)] + #[pallet::weight(T::WeightInfo::set_attributes_pre_signed(data.attributes.len() as u32))] + pub fn set_attributes_pre_signed( + origin: OriginFor, + data: PreSignedAttributesOf, + signature: T::OffchainSignature, + signer: T::AccountId, + ) -> DispatchResult { + let origin = ensure_signed(origin)?; + let msg = Encode::encode(&data); + ensure!(signature.verify(&*msg, &signer), Error::::WrongSignature); + Self::do_set_attributes_pre_signed(origin, data, signer) + } } } diff --git a/frame/nfts/src/tests.rs b/frame/nfts/src/tests.rs index fb8265007..8d937b0f7 100644 --- a/frame/nfts/src/tests.rs +++ b/frame/nfts/src/tests.rs @@ -3176,3 +3176,311 @@ fn pre_signed_mints_should_work() { ); }) } + +#[test] +fn pre_signed_attributes_should_work() { + new_test_ext().execute_with(|| { + let user_1_pair = sp_core::sr25519::Pair::from_string("//Alice", None).unwrap(); + let user_1_signer = MultiSigner::Sr25519(user_1_pair.public()); + let user_1 = user_1_signer.clone().into_account(); + let user_2 = account(2); + let user_3_pair = sp_core::sr25519::Pair::from_string("//Bob", None).unwrap(); + let user_3_signer = MultiSigner::Sr25519(user_3_pair.public()); + let user_3 = user_3_signer.clone().into_account(); + let collection_id = 0; + let item_id = 0; + + Balances::make_free_balance_be(&user_1, 100); + Balances::make_free_balance_be(&user_2, 100); + Balances::make_free_balance_be(&user_3, 100); + assert_ok!(Nfts::create( + RuntimeOrigin::signed(user_1.clone()), + user_1.clone(), + collection_config_with_all_settings_enabled(), + )); + assert_ok!(Nfts::mint( + RuntimeOrigin::signed(user_1.clone()), + collection_id, + item_id, + user_2.clone(), + None, + )); + + // validate the CollectionOwner namespace + let pre_signed_data = PreSignedAttributes { + collection: 0, + item: 0, + attributes: vec![(vec![0], vec![1]), (vec![2], vec![3])], + namespace: AttributeNamespace::CollectionOwner, + deadline: 10000000, + }; + let message = Encode::encode(&pre_signed_data); + let signature = MultiSignature::Sr25519(user_1_pair.sign(&message)); + + assert_ok!(Nfts::set_attributes_pre_signed( + RuntimeOrigin::signed(user_2.clone()), + pre_signed_data.clone(), + signature.clone(), + user_1.clone(), + )); + + assert_eq!( + attributes(0), + vec![ + (Some(0), AttributeNamespace::CollectionOwner, bvec![0], bvec![1]), + (Some(0), AttributeNamespace::CollectionOwner, bvec![2], bvec![3]), + ] + ); + let attribute_key: BoundedVec<_, _> = bvec![0]; + let (_, deposit) = Attribute::::get(( + 0, + Some(0), + AttributeNamespace::CollectionOwner, + &attribute_key, + )) + .unwrap(); + assert_eq!(deposit.account, Some(user_2.clone())); + assert_eq!(deposit.amount, 3); + + assert_eq!(Balances::free_balance(&user_1), 100 - 2 - 1); // 2 - collection deposit, 1 - item deposit + assert_eq!(Balances::free_balance(&user_2), 100 - 6); // 6 - attributes + + // validate the deposit gets returned on attribute update from collection's owner + assert_ok!(Nfts::set_attribute( + RuntimeOrigin::signed(user_1.clone()), + collection_id, + Some(item_id), + AttributeNamespace::CollectionOwner, + bvec![0], + bvec![1], + )); + let (_, deposit) = Attribute::::get(( + 0, + Some(0), + AttributeNamespace::CollectionOwner, + &attribute_key, + )) + .unwrap(); + assert_eq!(deposit.account, None); + assert_eq!(deposit.amount, 3); + + // validate we don't partially modify the state + assert_eq!(item_attributes_approvals(collection_id, item_id), vec![]); + let pre_signed_data = PreSignedAttributes { + collection: 0, + item: 0, + attributes: vec![(vec![0], vec![1]), (vec![2; 51], vec![3])], + namespace: AttributeNamespace::Account(user_3.clone()), + deadline: 10000000, + }; + let message = Encode::encode(&pre_signed_data); + let signature = MultiSignature::Sr25519(user_3_pair.sign(&message)); + + assert_noop!( + Nfts::set_attributes_pre_signed( + RuntimeOrigin::signed(user_2.clone()), + pre_signed_data.clone(), + signature.clone(), + user_3.clone(), + ), + Error::::IncorrectData + ); + + // no new approval was set + assert_eq!(item_attributes_approvals(collection_id, item_id), vec![]); + + // no new attributes were added + assert_eq!( + attributes(0), + vec![ + (Some(0), AttributeNamespace::CollectionOwner, bvec![0], bvec![1]), + (Some(0), AttributeNamespace::CollectionOwner, bvec![2], bvec![3]), + ] + ); + + // validate the Account namespace + let pre_signed_data = PreSignedAttributes { + collection: 0, + item: 0, + attributes: vec![(vec![0], vec![1]), (vec![2], vec![3])], + namespace: AttributeNamespace::Account(user_3.clone()), + deadline: 10000000, + }; + let message = Encode::encode(&pre_signed_data); + let signature = MultiSignature::Sr25519(user_3_pair.sign(&message)); + + assert_ok!(Nfts::set_attributes_pre_signed( + RuntimeOrigin::signed(user_2.clone()), + pre_signed_data.clone(), + signature.clone(), + user_3.clone(), + )); + + assert_eq!( + attributes(0), + vec![ + (Some(0), AttributeNamespace::CollectionOwner, bvec![0], bvec![1]), + (Some(0), AttributeNamespace::Account(user_3.clone()), bvec![0], bvec![1]), + (Some(0), AttributeNamespace::CollectionOwner, bvec![2], bvec![3]), + (Some(0), AttributeNamespace::Account(user_3.clone()), bvec![2], bvec![3]), + ] + ); + assert_eq!(item_attributes_approvals(collection_id, item_id), vec![user_3.clone()]); + + let attribute_key: BoundedVec<_, _> = bvec![0]; + let (_, deposit) = Attribute::::get(( + 0, + Some(0), + AttributeNamespace::Account(user_3.clone()), + &attribute_key, + )) + .unwrap(); + assert_eq!(deposit.account, Some(user_2.clone())); + assert_eq!(deposit.amount, 3); + + assert_eq!(Balances::free_balance(&user_2), 100 - 9); + assert_eq!(Balances::free_balance(&user_3), 100); + + // validate the deposit gets returned on attribute update from user_3 + assert_ok!(Nfts::set_attribute( + RuntimeOrigin::signed(user_3.clone()), + collection_id, + Some(item_id), + AttributeNamespace::Account(user_3.clone()), + bvec![0], + bvec![1], + )); + let (_, deposit) = Attribute::::get(( + 0, + Some(0), + AttributeNamespace::Account(user_3.clone()), + &attribute_key, + )) + .unwrap(); + assert_eq!(deposit.account, Some(user_3.clone())); + assert_eq!(deposit.amount, 3); + + assert_eq!(Balances::free_balance(&user_2), 100 - 6); + assert_eq!(Balances::free_balance(&user_3), 100 - 3); + + // can't update with the wrong signature + assert_noop!( + Nfts::set_attributes_pre_signed( + RuntimeOrigin::signed(user_2.clone()), + pre_signed_data.clone(), + signature.clone(), + user_1.clone(), + ), + Error::::WrongSignature + ); + + // can't update if I don't own that item + assert_noop!( + Nfts::set_attributes_pre_signed( + RuntimeOrigin::signed(user_3.clone()), + pre_signed_data.clone(), + signature.clone(), + user_3.clone(), + ), + Error::::NoPermission + ); + + // can't update the CollectionOwner namespace if the signer is not an owner of that + // collection + let pre_signed_data = PreSignedAttributes { + collection: 0, + item: 0, + attributes: vec![(vec![0], vec![1]), (vec![2], vec![3])], + namespace: AttributeNamespace::CollectionOwner, + deadline: 10000000, + }; + let message = Encode::encode(&pre_signed_data); + let signature = MultiSignature::Sr25519(user_3_pair.sign(&message)); + + assert_noop!( + Nfts::set_attributes_pre_signed( + RuntimeOrigin::signed(user_2.clone()), + pre_signed_data.clone(), + signature.clone(), + user_3.clone(), + ), + Error::::NoPermission + ); + + // validate signature's expiration + System::set_block_number(10000001); + assert_noop!( + Nfts::set_attributes_pre_signed( + RuntimeOrigin::signed(user_2.clone()), + pre_signed_data.clone(), + signature.clone(), + user_3.clone(), + ), + Error::::DeadlineExpired + ); + System::set_block_number(1); + + // validate item & collection + let pre_signed_data = PreSignedAttributes { + collection: 1, + item: 1, + attributes: vec![(vec![0], vec![1]), (vec![2], vec![3])], + namespace: AttributeNamespace::CollectionOwner, + deadline: 10000000, + }; + let message = Encode::encode(&pre_signed_data); + let signature = MultiSignature::Sr25519(user_1_pair.sign(&message)); + + assert_noop!( + Nfts::set_attributes_pre_signed( + RuntimeOrigin::signed(user_2.clone()), + pre_signed_data.clone(), + signature.clone(), + user_1.clone(), + ), + Error::::UnknownItem + ); + + // validate max attributes limit + let pre_signed_data = PreSignedAttributes { + collection: 1, + item: 1, + attributes: vec![(vec![0], vec![1]), (vec![2], vec![3]), (vec![2], vec![3])], + namespace: AttributeNamespace::CollectionOwner, + deadline: 10000000, + }; + let message = Encode::encode(&pre_signed_data); + let signature = MultiSignature::Sr25519(user_1_pair.sign(&message)); + + assert_noop!( + Nfts::set_attributes_pre_signed( + RuntimeOrigin::signed(user_2.clone()), + pre_signed_data.clone(), + signature.clone(), + user_1.clone(), + ), + Error::::MaxAttributesLimitReached + ); + + // validate the attribute's value length + let pre_signed_data = PreSignedAttributes { + collection: 0, + item: 0, + attributes: vec![(vec![0], vec![1]), (vec![2], vec![3; 51])], + namespace: AttributeNamespace::CollectionOwner, + deadline: 10000000, + }; + let message = Encode::encode(&pre_signed_data); + let signature = MultiSignature::Sr25519(user_1_pair.sign(&message)); + + assert_noop!( + Nfts::set_attributes_pre_signed( + RuntimeOrigin::signed(user_2.clone()), + pre_signed_data.clone(), + signature.clone(), + user_1.clone(), + ), + Error::::IncorrectData + ); + }) +} diff --git a/frame/nfts/src/types.rs b/frame/nfts/src/types.rs index 860f937aa..8c43024cd 100644 --- a/frame/nfts/src/types.rs +++ b/frame/nfts/src/types.rs @@ -67,6 +67,12 @@ pub(super) type PreSignedMintOf = PreSignedMint< ::AccountId, ::BlockNumber, >; +pub(super) type PreSignedAttributesOf = PreSignedAttributes< + >::CollectionId, + >::ItemId, + ::AccountId, + ::BlockNumber, +>; pub trait Incrementable { fn increment(&self) -> Self; @@ -484,7 +490,7 @@ impl_codec_bitflags!(CollectionRoles, u8, CollectionRole); pub struct PreSignedMint { /// A collection of the item to be minted. pub(super) collection: CollectionId, - /// Item's id. + /// Item's ID. pub(super) item: ItemId, /// Additional item's key-value attributes. pub(super) attributes: Vec<(Vec, Vec)>, @@ -495,3 +501,17 @@ pub struct PreSignedMint { /// A deadline for the signature. pub(super) deadline: Deadline, } + +#[derive(Clone, Eq, PartialEq, Encode, Decode, RuntimeDebug, TypeInfo)] +pub struct PreSignedAttributes { + /// Collection's ID. + pub(super) collection: CollectionId, + /// Item's ID. + pub(super) item: ItemId, + /// Key-value attributes. + pub(super) attributes: Vec<(Vec, Vec)>, + /// Attributes' namespace. + pub(super) namespace: AttributeNamespace, + /// A deadline for the signature. + pub(super) deadline: Deadline, +} diff --git a/frame/nfts/src/weights.rs b/frame/nfts/src/weights.rs index dc59330f3..9eedea958 100644 --- a/frame/nfts/src/weights.rs +++ b/frame/nfts/src/weights.rs @@ -18,9 +18,9 @@ //! Autogenerated weights for pallet_nfts //! //! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev -//! DATE: 2023-01-31, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! DATE: 2023-02-15, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` //! WORST CASE MAP SIZE: `1000000` -//! HOSTNAME: `runner-b3zmxxc-project-145-concurrent-0`, CPU: `Intel(R) Xeon(R) CPU @ 2.60GHz` +//! HOSTNAME: `runner-osnnfcqu-project-145-concurrent-0`, CPU: `Intel(R) Xeon(R) CPU @ 2.60GHz` //! EXECUTION: Some(Wasm), WASM-EXECUTION: Compiled, CHAIN: Some("dev"), DB CACHE: 1024 // Executed Command: @@ -87,6 +87,7 @@ pub trait WeightInfo { fn cancel_swap() -> Weight; fn claim_swap() -> Weight; fn mint_pre_signed(n: u32, ) -> Weight; + fn set_attributes_pre_signed(n: u32, ) -> Weight; } /// Weights for pallet_nfts using the Substrate node and recommended hardware. @@ -106,8 +107,9 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `214` // Estimated: `3054` - // Minimum execution time: 33_666 nanoseconds. - Weight::from_parts(34_405_000, 3054) + // Minimum execution time: 33_769 nanoseconds. + Weight::from_ref_time(36_031_000) + .saturating_add(Weight::from_proof_size(3054)) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().writes(5_u64)) } @@ -125,8 +127,9 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `42` // Estimated: `3054` - // Minimum execution time: 22_028 nanoseconds. - Weight::from_parts(23_030_000, 3054) + // Minimum execution time: 21_767 nanoseconds. + Weight::from_ref_time(22_565_000) + .saturating_add(Weight::from_proof_size(3054)) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().writes(5_u64)) } @@ -156,21 +159,22 @@ impl WeightInfo for SubstrateWeight { fn destroy(_n: u32, m: u32, a: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `172781 + m * (127 ±0) + a * (402 ±0)` - // Estimated: `3347427 + a * (2921 ±0) + m * (2615 ±0)` - // Minimum execution time: 27_944_985 nanoseconds. - Weight::from_parts(19_865_318_850, 3347427) - // Standard Error: 32_345 - .saturating_add(Weight::from_ref_time(8_729_316).saturating_mul(m.into())) - // Standard Error: 32_345 - .saturating_add(Weight::from_ref_time(10_264_491).saturating_mul(a.into())) + // Estimated: `3347427 + m * (2615 ±0) + a * (2921 ±0)` + // Minimum execution time: 26_973_627 nanoseconds. + Weight::from_ref_time(19_692_361_714) + .saturating_add(Weight::from_proof_size(3347427)) + // Standard Error: 17_036 + .saturating_add(Weight::from_ref_time(7_797_219).saturating_mul(m.into())) + // Standard Error: 17_036 + .saturating_add(Weight::from_ref_time(9_504_128).saturating_mul(a.into())) .saturating_add(T::DbWeight::get().reads(1004_u64)) .saturating_add(T::DbWeight::get().reads((1_u64).saturating_mul(m.into()))) .saturating_add(T::DbWeight::get().reads((1_u64).saturating_mul(a.into()))) .saturating_add(T::DbWeight::get().writes(3005_u64)) .saturating_add(T::DbWeight::get().writes((1_u64).saturating_mul(m.into()))) .saturating_add(T::DbWeight::get().writes((1_u64).saturating_mul(a.into()))) - .saturating_add(Weight::from_proof_size(2921).saturating_mul(a.into())) .saturating_add(Weight::from_proof_size(2615).saturating_mul(m.into())) + .saturating_add(Weight::from_proof_size(2921).saturating_mul(a.into())) } /// Storage: Nfts CollectionConfigOf (r:1 w:0) /// Proof: Nfts CollectionConfigOf (max_values: None, max_size: Some(73), added: 2548, mode: MaxEncodedLen) @@ -188,8 +192,9 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `448` // Estimated: `13506` - // Minimum execution time: 43_925 nanoseconds. - Weight::from_parts(45_885_000, 13506) + // Minimum execution time: 44_837 nanoseconds. + Weight::from_ref_time(46_794_000) + .saturating_add(Weight::from_proof_size(13506)) .saturating_add(T::DbWeight::get().reads(5_u64)) .saturating_add(T::DbWeight::get().writes(4_u64)) } @@ -209,8 +214,9 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `448` // Estimated: `13506` - // Minimum execution time: 42_832 nanoseconds. - Weight::from_parts(44_621_000, 13506) + // Minimum execution time: 43_976 nanoseconds. + Weight::from_ref_time(44_831_000) + .saturating_add(Weight::from_proof_size(13506)) .saturating_add(T::DbWeight::get().reads(5_u64)) .saturating_add(T::DbWeight::get().writes(4_u64)) } @@ -236,8 +242,9 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `647` // Estimated: `13573` - // Minimum execution time: 47_787 nanoseconds. - Weight::from_parts(49_204_000, 13573) + // Minimum execution time: 48_233 nanoseconds. + Weight::from_ref_time(50_113_000) + .saturating_add(Weight::from_proof_size(13573)) .saturating_add(T::DbWeight::get().reads(5_u64)) .saturating_add(T::DbWeight::get().writes(7_u64)) } @@ -263,8 +270,9 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `882` // Estimated: `16109` - // Minimum execution time: 55_524 nanoseconds. - Weight::from_parts(56_962_000, 16109) + // Minimum execution time: 55_452 nanoseconds. + Weight::from_ref_time(57_642_000) + .saturating_add(Weight::from_proof_size(16109)) .saturating_add(T::DbWeight::get().reads(6_u64)) .saturating_add(T::DbWeight::get().writes(6_u64)) } @@ -279,10 +287,11 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `756 + i * (140 ±0)` // Estimated: `5103 + i * (3336 ±0)` - // Minimum execution time: 15_246 nanoseconds. - Weight::from_parts(15_671_000, 5103) - // Standard Error: 20_348 - .saturating_add(Weight::from_ref_time(14_692_422).saturating_mul(i.into())) + // Minimum execution time: 15_598 nanoseconds. + Weight::from_ref_time(15_926_000) + .saturating_add(Weight::from_proof_size(5103)) + // Standard Error: 13_692 + .saturating_add(Weight::from_ref_time(14_040_741).saturating_mul(i.into())) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().reads((1_u64).saturating_mul(i.into()))) .saturating_add(T::DbWeight::get().writes((1_u64).saturating_mul(i.into()))) @@ -296,8 +305,9 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `401` // Estimated: `5067` - // Minimum execution time: 19_270 nanoseconds. - Weight::from_parts(19_775_000, 5067) + // Minimum execution time: 19_686 nanoseconds. + Weight::from_ref_time(20_404_000) + .saturating_add(Weight::from_proof_size(5067)) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -309,8 +319,9 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `401` // Estimated: `5067` - // Minimum execution time: 19_364 nanoseconds. - Weight::from_parts(20_274_000, 5067) + // Minimum execution time: 19_172 nanoseconds. + Weight::from_ref_time(20_151_000) + .saturating_add(Weight::from_proof_size(5067)) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -322,8 +333,9 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `289` // Estimated: `5092` - // Minimum execution time: 17_036 nanoseconds. - Weight::from_parts(17_750_000, 5092) + // Minimum execution time: 17_063 nanoseconds. + Weight::from_ref_time(17_482_000) + .saturating_add(Weight::from_proof_size(5092)) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -337,8 +349,9 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `381` // Estimated: `5082` - // Minimum execution time: 22_104 nanoseconds. - Weight::from_parts(23_022_000, 5082) + // Minimum execution time: 21_974 nanoseconds. + Weight::from_ref_time(22_770_000) + .saturating_add(Weight::from_proof_size(5082)) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().writes(4_u64)) } @@ -350,8 +363,9 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `362` // Estimated: `2555` - // Minimum execution time: 24_516 nanoseconds. - Weight::from_parts(25_300_000, 2555) + // Minimum execution time: 24_341 nanoseconds. + Weight::from_ref_time(25_059_000) + .saturating_add(Weight::from_proof_size(2555)) .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(T::DbWeight::get().writes(5_u64)) } @@ -363,8 +377,9 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `304` // Estimated: `2555` - // Minimum execution time: 16_974 nanoseconds. - Weight::from_parts(17_654_000, 2555) + // Minimum execution time: 16_897 nanoseconds. + Weight::from_ref_time(17_560_000) + .saturating_add(Weight::from_proof_size(2555)) .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) } @@ -376,8 +391,9 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `242` // Estimated: `2555` - // Minimum execution time: 13_190 nanoseconds. - Weight::from_parts(13_826_000, 2555) + // Minimum execution time: 13_239 nanoseconds. + Weight::from_ref_time(13_963_000) + .saturating_add(Weight::from_proof_size(2555)) .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -389,8 +405,9 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `445` // Estimated: `5078` - // Minimum execution time: 17_336 nanoseconds. - Weight::from_parts(18_242_000, 5078) + // Minimum execution time: 17_187 nanoseconds. + Weight::from_ref_time(17_942_000) + .saturating_add(Weight::from_proof_size(5078)) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -406,8 +423,9 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `474` // Estimated: `10547` - // Minimum execution time: 40_791 nanoseconds. - Weight::from_parts(42_489_000, 10547) + // Minimum execution time: 40_925 nanoseconds. + Weight::from_ref_time(42_733_000) + .saturating_add(Weight::from_proof_size(10547)) .saturating_add(T::DbWeight::get().reads(4_u64)) .saturating_add(T::DbWeight::get().writes(2_u64)) } @@ -419,8 +437,9 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `337` // Estimated: `5476` - // Minimum execution time: 24_620 nanoseconds. - Weight::from_parts(25_370_000, 5476) + // Minimum execution time: 24_486 nanoseconds. + Weight::from_ref_time(25_409_000) + .saturating_add(Weight::from_proof_size(5476)) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().writes(2_u64)) } @@ -434,8 +453,9 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `916` // Estimated: `7999` - // Minimum execution time: 36_411 nanoseconds. - Weight::from_parts(37_439_000, 7999) + // Minimum execution time: 36_643 nanoseconds. + Weight::from_ref_time(37_805_000) + .saturating_add(Weight::from_proof_size(7999)) .saturating_add(T::DbWeight::get().reads(3_u64)) .saturating_add(T::DbWeight::get().writes(2_u64)) } @@ -447,8 +467,9 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `379` // Estimated: `6492` - // Minimum execution time: 16_696 nanoseconds. - Weight::from_parts(17_411_000, 6492) + // Minimum execution time: 16_798 nanoseconds. + Weight::from_ref_time(17_326_000) + .saturating_add(Weight::from_proof_size(6492)) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -465,10 +486,11 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `899 + n * (396 ±0)` // Estimated: `12016 + n * (2921 ±0)` - // Minimum execution time: 25_928 nanoseconds. - Weight::from_parts(26_440_000, 12016) - // Standard Error: 9_158 - .saturating_add(Weight::from_ref_time(9_271_441).saturating_mul(n.into())) + // Minimum execution time: 25_524 nanoseconds. + Weight::from_ref_time(26_107_000) + .saturating_add(Weight::from_proof_size(12016)) + // Standard Error: 5_460 + .saturating_add(Weight::from_ref_time(9_030_830).saturating_mul(n.into())) .saturating_add(T::DbWeight::get().reads(4_u64)) .saturating_add(T::DbWeight::get().reads((1_u64).saturating_mul(n.into()))) .saturating_add(T::DbWeight::get().writes(2_u64)) @@ -487,8 +509,9 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `474` // Estimated: `10241` - // Minimum execution time: 34_150 nanoseconds. - Weight::from_parts(35_398_000, 10241) + // Minimum execution time: 34_400 nanoseconds. + Weight::from_ref_time(35_469_000) + .saturating_add(Weight::from_proof_size(10241)) .saturating_add(T::DbWeight::get().reads(4_u64)) .saturating_add(T::DbWeight::get().writes(2_u64)) } @@ -502,8 +525,9 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `609` // Estimated: `7693` - // Minimum execution time: 31_871 nanoseconds. - Weight::from_parts(33_057_000, 7693) + // Minimum execution time: 31_560 nanoseconds. + Weight::from_ref_time(33_081_000) + .saturating_add(Weight::from_proof_size(7693)) .saturating_add(T::DbWeight::get().reads(3_u64)) .saturating_add(T::DbWeight::get().writes(2_u64)) } @@ -517,8 +541,9 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `333` // Estimated: `7665` - // Minimum execution time: 28_843 nanoseconds. - Weight::from_parts(30_057_000, 7665) + // Minimum execution time: 28_821 nanoseconds. + Weight::from_ref_time(30_010_000) + .saturating_add(Weight::from_proof_size(7665)) .saturating_add(T::DbWeight::get().reads(3_u64)) .saturating_add(T::DbWeight::get().writes(2_u64)) } @@ -532,8 +557,9 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `476` // Estimated: `7665` - // Minimum execution time: 27_777 nanoseconds. - Weight::from_parts(28_471_000, 7665) + // Minimum execution time: 27_608 nanoseconds. + Weight::from_ref_time(28_766_000) + .saturating_add(Weight::from_proof_size(7665)) .saturating_add(T::DbWeight::get().reads(3_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -547,8 +573,9 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `466` // Estimated: `8428` - // Minimum execution time: 23_726 nanoseconds. - Weight::from_parts(24_455_000, 8428) + // Minimum execution time: 23_987 nanoseconds. + Weight::from_ref_time(24_819_000) + .saturating_add(Weight::from_proof_size(8428)) .saturating_add(T::DbWeight::get().reads(3_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -560,8 +587,9 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `474` // Estimated: `5880` - // Minimum execution time: 21_051 nanoseconds. - Weight::from_parts(21_722_000, 5880) + // Minimum execution time: 21_254 nanoseconds. + Weight::from_ref_time(21_826_000) + .saturating_add(Weight::from_proof_size(5880)) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -573,8 +601,9 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `474` // Estimated: `5880` - // Minimum execution time: 20_095 nanoseconds. - Weight::from_parts(20_770_000, 5880) + // Minimum execution time: 20_272 nanoseconds. + Weight::from_ref_time(20_922_000) + .saturating_add(Weight::from_proof_size(5880)) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -584,8 +613,9 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `42` // Estimated: `2527` - // Minimum execution time: 14_078 nanoseconds. - Weight::from_parts(14_582_000, 2527) + // Minimum execution time: 14_287 nanoseconds. + Weight::from_ref_time(14_960_000) + .saturating_add(Weight::from_proof_size(2527)) .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -597,8 +627,9 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `333` // Estimated: `5103` - // Minimum execution time: 17_677 nanoseconds. - Weight::from_parts(18_381_000, 5103) + // Minimum execution time: 17_948 nanoseconds. + Weight::from_ref_time(18_780_000) + .saturating_add(Weight::from_proof_size(5103)) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -610,8 +641,9 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `333` // Estimated: `5103` - // Minimum execution time: 16_295 nanoseconds. - Weight::from_parts(17_036_000, 5103) + // Minimum execution time: 16_616 nanoseconds. + Weight::from_ref_time(17_155_000) + .saturating_add(Weight::from_proof_size(5103)) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -627,8 +659,9 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `516` // Estimated: `8407` - // Minimum execution time: 22_847 nanoseconds. - Weight::from_parts(23_536_000, 8407) + // Minimum execution time: 22_777 nanoseconds. + Weight::from_ref_time(23_955_000) + .saturating_add(Weight::from_proof_size(8407)) .saturating_add(T::DbWeight::get().reads(3_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -652,8 +685,9 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `934` // Estimated: `16129` - // Minimum execution time: 60_517 nanoseconds. - Weight::from_parts(62_528_000, 16129) + // Minimum execution time: 61_131 nanoseconds. + Weight::from_ref_time(62_791_000) + .saturating_add(Weight::from_proof_size(16129)) .saturating_add(T::DbWeight::get().reads(6_u64)) .saturating_add(T::DbWeight::get().writes(6_u64)) } @@ -662,10 +696,11 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_866 nanoseconds. - Weight::from_ref_time(3_949_301) - // Standard Error: 11_044 - .saturating_add(Weight::from_ref_time(3_424_466).saturating_mul(n.into())) + // Minimum execution time: 1_952 nanoseconds. + Weight::from_ref_time(3_975_700) + .saturating_add(Weight::from_proof_size(0)) + // Standard Error: 11_254 + .saturating_add(Weight::from_ref_time(3_501_698).saturating_mul(n.into())) } /// Storage: Nfts Item (r:2 w:0) /// Proof: Nfts Item (max_values: None, max_size: Some(861), added: 3336, mode: MaxEncodedLen) @@ -675,8 +710,9 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `524` // Estimated: `6672` - // Minimum execution time: 21_174 nanoseconds. - Weight::from_parts(21_619_000, 6672) + // Minimum execution time: 20_327 nanoseconds. + Weight::from_ref_time(21_714_000) + .saturating_add(Weight::from_proof_size(6672)) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -688,8 +724,9 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `511` // Estimated: `5882` - // Minimum execution time: 20_606 nanoseconds. - Weight::from_parts(21_150_000, 5882) + // Minimum execution time: 20_668 nanoseconds. + Weight::from_ref_time(21_416_000) + .saturating_add(Weight::from_proof_size(5882)) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -713,8 +750,9 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `1097` // Estimated: `21970` - // Minimum execution time: 88_414 nanoseconds. - Weight::from_parts(91_830_000, 21970) + // Minimum execution time: 88_006 nanoseconds. + Weight::from_ref_time(90_390_000) + .saturating_add(Weight::from_proof_size(21970)) .saturating_add(T::DbWeight::get().reads(8_u64)) .saturating_add(T::DbWeight::get().writes(11_u64)) } @@ -739,16 +777,45 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `596` // Estimated: `16180 + n * (2921 ±0)` - // Minimum execution time: 124_354 nanoseconds. - Weight::from_parts(133_779_491, 16180) - // Standard Error: 38_452 - .saturating_add(Weight::from_ref_time(25_110_697).saturating_mul(n.into())) + // Minimum execution time: 124_967 nanoseconds. + Weight::from_ref_time(131_602_642) + .saturating_add(Weight::from_proof_size(16180)) + // Standard Error: 36_480 + .saturating_add(Weight::from_ref_time(25_811_394).saturating_mul(n.into())) .saturating_add(T::DbWeight::get().reads(6_u64)) .saturating_add(T::DbWeight::get().reads((1_u64).saturating_mul(n.into()))) .saturating_add(T::DbWeight::get().writes(6_u64)) .saturating_add(T::DbWeight::get().writes((1_u64).saturating_mul(n.into()))) .saturating_add(Weight::from_proof_size(2921).saturating_mul(n.into())) } + /// Storage: Nfts Item (r:1 w:0) + /// Proof: Nfts Item (max_values: None, max_size: Some(861), added: 3336, mode: MaxEncodedLen) + /// Storage: Nfts ItemAttributesApprovalsOf (r:1 w:1) + /// Proof: Nfts ItemAttributesApprovalsOf (max_values: None, max_size: Some(681), added: 3156, mode: MaxEncodedLen) + /// Storage: Nfts Collection (r:1 w:1) + /// Proof: Nfts Collection (max_values: None, max_size: Some(80), added: 2555, mode: MaxEncodedLen) + /// Storage: Nfts CollectionConfigOf (r:1 w:0) + /// Proof: Nfts CollectionConfigOf (max_values: None, max_size: Some(73), added: 2548, mode: MaxEncodedLen) + /// Storage: Nfts Attribute (r:10 w:10) + /// Proof: Nfts Attribute (max_values: None, max_size: Some(446), added: 2921, mode: MaxEncodedLen) + /// Storage: System Account (r:1 w:1) + /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) + /// The range of component `n` is `[0, 10]`. + fn set_attributes_pre_signed(n: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `716` + // Estimated: `14198 + n * (2921 ±0)` + // Minimum execution time: 84_153 nanoseconds. + Weight::from_ref_time(96_401_623) + .saturating_add(Weight::from_proof_size(14198)) + // Standard Error: 70_244 + .saturating_add(Weight::from_ref_time(26_866_222).saturating_mul(n.into())) + .saturating_add(T::DbWeight::get().reads(4_u64)) + .saturating_add(T::DbWeight::get().reads((1_u64).saturating_mul(n.into()))) + .saturating_add(T::DbWeight::get().writes(2_u64)) + .saturating_add(T::DbWeight::get().writes((1_u64).saturating_mul(n.into()))) + .saturating_add(Weight::from_proof_size(2921).saturating_mul(n.into())) + } } // For backwards compatibility and tests @@ -767,8 +834,9 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `214` // Estimated: `3054` - // Minimum execution time: 33_666 nanoseconds. - Weight::from_parts(34_405_000, 3054) + // Minimum execution time: 33_769 nanoseconds. + Weight::from_ref_time(36_031_000) + .saturating_add(Weight::from_proof_size(3054)) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().writes(5_u64)) } @@ -786,8 +854,9 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `42` // Estimated: `3054` - // Minimum execution time: 22_028 nanoseconds. - Weight::from_parts(23_030_000, 3054) + // Minimum execution time: 21_767 nanoseconds. + Weight::from_ref_time(22_565_000) + .saturating_add(Weight::from_proof_size(3054)) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().writes(5_u64)) } @@ -817,21 +886,22 @@ impl WeightInfo for () { fn destroy(_n: u32, m: u32, a: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `172781 + m * (127 ±0) + a * (402 ±0)` - // Estimated: `3347427 + a * (2921 ±0) + m * (2615 ±0)` - // Minimum execution time: 27_944_985 nanoseconds. - Weight::from_parts(19_865_318_850, 3347427) - // Standard Error: 32_345 - .saturating_add(Weight::from_ref_time(8_729_316).saturating_mul(m.into())) - // Standard Error: 32_345 - .saturating_add(Weight::from_ref_time(10_264_491).saturating_mul(a.into())) + // Estimated: `3347427 + m * (2615 ±0) + a * (2921 ±0)` + // Minimum execution time: 26_973_627 nanoseconds. + Weight::from_ref_time(19_692_361_714) + .saturating_add(Weight::from_proof_size(3347427)) + // Standard Error: 17_036 + .saturating_add(Weight::from_ref_time(7_797_219).saturating_mul(m.into())) + // Standard Error: 17_036 + .saturating_add(Weight::from_ref_time(9_504_128).saturating_mul(a.into())) .saturating_add(RocksDbWeight::get().reads(1004_u64)) .saturating_add(RocksDbWeight::get().reads((1_u64).saturating_mul(m.into()))) .saturating_add(RocksDbWeight::get().reads((1_u64).saturating_mul(a.into()))) .saturating_add(RocksDbWeight::get().writes(3005_u64)) .saturating_add(RocksDbWeight::get().writes((1_u64).saturating_mul(m.into()))) .saturating_add(RocksDbWeight::get().writes((1_u64).saturating_mul(a.into()))) - .saturating_add(Weight::from_proof_size(2921).saturating_mul(a.into())) .saturating_add(Weight::from_proof_size(2615).saturating_mul(m.into())) + .saturating_add(Weight::from_proof_size(2921).saturating_mul(a.into())) } /// Storage: Nfts CollectionConfigOf (r:1 w:0) /// Proof: Nfts CollectionConfigOf (max_values: None, max_size: Some(73), added: 2548, mode: MaxEncodedLen) @@ -849,8 +919,9 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `448` // Estimated: `13506` - // Minimum execution time: 43_925 nanoseconds. - Weight::from_parts(45_885_000, 13506) + // Minimum execution time: 44_837 nanoseconds. + Weight::from_ref_time(46_794_000) + .saturating_add(Weight::from_proof_size(13506)) .saturating_add(RocksDbWeight::get().reads(5_u64)) .saturating_add(RocksDbWeight::get().writes(4_u64)) } @@ -870,8 +941,9 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `448` // Estimated: `13506` - // Minimum execution time: 42_832 nanoseconds. - Weight::from_parts(44_621_000, 13506) + // Minimum execution time: 43_976 nanoseconds. + Weight::from_ref_time(44_831_000) + .saturating_add(Weight::from_proof_size(13506)) .saturating_add(RocksDbWeight::get().reads(5_u64)) .saturating_add(RocksDbWeight::get().writes(4_u64)) } @@ -897,8 +969,9 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `647` // Estimated: `13573` - // Minimum execution time: 47_787 nanoseconds. - Weight::from_parts(49_204_000, 13573) + // Minimum execution time: 48_233 nanoseconds. + Weight::from_ref_time(50_113_000) + .saturating_add(Weight::from_proof_size(13573)) .saturating_add(RocksDbWeight::get().reads(5_u64)) .saturating_add(RocksDbWeight::get().writes(7_u64)) } @@ -924,8 +997,9 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `882` // Estimated: `16109` - // Minimum execution time: 55_524 nanoseconds. - Weight::from_parts(56_962_000, 16109) + // Minimum execution time: 55_452 nanoseconds. + Weight::from_ref_time(57_642_000) + .saturating_add(Weight::from_proof_size(16109)) .saturating_add(RocksDbWeight::get().reads(6_u64)) .saturating_add(RocksDbWeight::get().writes(6_u64)) } @@ -940,10 +1014,11 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `756 + i * (140 ±0)` // Estimated: `5103 + i * (3336 ±0)` - // Minimum execution time: 15_246 nanoseconds. - Weight::from_parts(15_671_000, 5103) - // Standard Error: 20_348 - .saturating_add(Weight::from_ref_time(14_692_422).saturating_mul(i.into())) + // Minimum execution time: 15_598 nanoseconds. + Weight::from_ref_time(15_926_000) + .saturating_add(Weight::from_proof_size(5103)) + // Standard Error: 13_692 + .saturating_add(Weight::from_ref_time(14_040_741).saturating_mul(i.into())) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().reads((1_u64).saturating_mul(i.into()))) .saturating_add(RocksDbWeight::get().writes((1_u64).saturating_mul(i.into()))) @@ -957,8 +1032,9 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `401` // Estimated: `5067` - // Minimum execution time: 19_270 nanoseconds. - Weight::from_parts(19_775_000, 5067) + // Minimum execution time: 19_686 nanoseconds. + Weight::from_ref_time(20_404_000) + .saturating_add(Weight::from_proof_size(5067)) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } @@ -970,8 +1046,9 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `401` // Estimated: `5067` - // Minimum execution time: 19_364 nanoseconds. - Weight::from_parts(20_274_000, 5067) + // Minimum execution time: 19_172 nanoseconds. + Weight::from_ref_time(20_151_000) + .saturating_add(Weight::from_proof_size(5067)) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } @@ -983,8 +1060,9 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `289` // Estimated: `5092` - // Minimum execution time: 17_036 nanoseconds. - Weight::from_parts(17_750_000, 5092) + // Minimum execution time: 17_063 nanoseconds. + Weight::from_ref_time(17_482_000) + .saturating_add(Weight::from_proof_size(5092)) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } @@ -998,8 +1076,9 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `381` // Estimated: `5082` - // Minimum execution time: 22_104 nanoseconds. - Weight::from_parts(23_022_000, 5082) + // Minimum execution time: 21_974 nanoseconds. + Weight::from_ref_time(22_770_000) + .saturating_add(Weight::from_proof_size(5082)) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().writes(4_u64)) } @@ -1011,8 +1090,9 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `362` // Estimated: `2555` - // Minimum execution time: 24_516 nanoseconds. - Weight::from_parts(25_300_000, 2555) + // Minimum execution time: 24_341 nanoseconds. + Weight::from_ref_time(25_059_000) + .saturating_add(Weight::from_proof_size(2555)) .saturating_add(RocksDbWeight::get().reads(1_u64)) .saturating_add(RocksDbWeight::get().writes(5_u64)) } @@ -1024,8 +1104,9 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `304` // Estimated: `2555` - // Minimum execution time: 16_974 nanoseconds. - Weight::from_parts(17_654_000, 2555) + // Minimum execution time: 16_897 nanoseconds. + Weight::from_ref_time(17_560_000) + .saturating_add(Weight::from_proof_size(2555)) .saturating_add(RocksDbWeight::get().reads(1_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) } @@ -1037,8 +1118,9 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `242` // Estimated: `2555` - // Minimum execution time: 13_190 nanoseconds. - Weight::from_parts(13_826_000, 2555) + // Minimum execution time: 13_239 nanoseconds. + Weight::from_ref_time(13_963_000) + .saturating_add(Weight::from_proof_size(2555)) .saturating_add(RocksDbWeight::get().reads(1_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } @@ -1050,8 +1132,9 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `445` // Estimated: `5078` - // Minimum execution time: 17_336 nanoseconds. - Weight::from_parts(18_242_000, 5078) + // Minimum execution time: 17_187 nanoseconds. + Weight::from_ref_time(17_942_000) + .saturating_add(Weight::from_proof_size(5078)) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } @@ -1067,8 +1150,9 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `474` // Estimated: `10547` - // Minimum execution time: 40_791 nanoseconds. - Weight::from_parts(42_489_000, 10547) + // Minimum execution time: 40_925 nanoseconds. + Weight::from_ref_time(42_733_000) + .saturating_add(Weight::from_proof_size(10547)) .saturating_add(RocksDbWeight::get().reads(4_u64)) .saturating_add(RocksDbWeight::get().writes(2_u64)) } @@ -1080,8 +1164,9 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `337` // Estimated: `5476` - // Minimum execution time: 24_620 nanoseconds. - Weight::from_parts(25_370_000, 5476) + // Minimum execution time: 24_486 nanoseconds. + Weight::from_ref_time(25_409_000) + .saturating_add(Weight::from_proof_size(5476)) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().writes(2_u64)) } @@ -1095,8 +1180,9 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `916` // Estimated: `7999` - // Minimum execution time: 36_411 nanoseconds. - Weight::from_parts(37_439_000, 7999) + // Minimum execution time: 36_643 nanoseconds. + Weight::from_ref_time(37_805_000) + .saturating_add(Weight::from_proof_size(7999)) .saturating_add(RocksDbWeight::get().reads(3_u64)) .saturating_add(RocksDbWeight::get().writes(2_u64)) } @@ -1108,8 +1194,9 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `379` // Estimated: `6492` - // Minimum execution time: 16_696 nanoseconds. - Weight::from_parts(17_411_000, 6492) + // Minimum execution time: 16_798 nanoseconds. + Weight::from_ref_time(17_326_000) + .saturating_add(Weight::from_proof_size(6492)) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } @@ -1126,10 +1213,11 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `899 + n * (396 ±0)` // Estimated: `12016 + n * (2921 ±0)` - // Minimum execution time: 25_928 nanoseconds. - Weight::from_parts(26_440_000, 12016) - // Standard Error: 9_158 - .saturating_add(Weight::from_ref_time(9_271_441).saturating_mul(n.into())) + // Minimum execution time: 25_524 nanoseconds. + Weight::from_ref_time(26_107_000) + .saturating_add(Weight::from_proof_size(12016)) + // Standard Error: 5_460 + .saturating_add(Weight::from_ref_time(9_030_830).saturating_mul(n.into())) .saturating_add(RocksDbWeight::get().reads(4_u64)) .saturating_add(RocksDbWeight::get().reads((1_u64).saturating_mul(n.into()))) .saturating_add(RocksDbWeight::get().writes(2_u64)) @@ -1148,8 +1236,9 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `474` // Estimated: `10241` - // Minimum execution time: 34_150 nanoseconds. - Weight::from_parts(35_398_000, 10241) + // Minimum execution time: 34_400 nanoseconds. + Weight::from_ref_time(35_469_000) + .saturating_add(Weight::from_proof_size(10241)) .saturating_add(RocksDbWeight::get().reads(4_u64)) .saturating_add(RocksDbWeight::get().writes(2_u64)) } @@ -1163,8 +1252,9 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `609` // Estimated: `7693` - // Minimum execution time: 31_871 nanoseconds. - Weight::from_parts(33_057_000, 7693) + // Minimum execution time: 31_560 nanoseconds. + Weight::from_ref_time(33_081_000) + .saturating_add(Weight::from_proof_size(7693)) .saturating_add(RocksDbWeight::get().reads(3_u64)) .saturating_add(RocksDbWeight::get().writes(2_u64)) } @@ -1178,8 +1268,9 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `333` // Estimated: `7665` - // Minimum execution time: 28_843 nanoseconds. - Weight::from_parts(30_057_000, 7665) + // Minimum execution time: 28_821 nanoseconds. + Weight::from_ref_time(30_010_000) + .saturating_add(Weight::from_proof_size(7665)) .saturating_add(RocksDbWeight::get().reads(3_u64)) .saturating_add(RocksDbWeight::get().writes(2_u64)) } @@ -1193,8 +1284,9 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `476` // Estimated: `7665` - // Minimum execution time: 27_777 nanoseconds. - Weight::from_parts(28_471_000, 7665) + // Minimum execution time: 27_608 nanoseconds. + Weight::from_ref_time(28_766_000) + .saturating_add(Weight::from_proof_size(7665)) .saturating_add(RocksDbWeight::get().reads(3_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } @@ -1208,8 +1300,9 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `466` // Estimated: `8428` - // Minimum execution time: 23_726 nanoseconds. - Weight::from_parts(24_455_000, 8428) + // Minimum execution time: 23_987 nanoseconds. + Weight::from_ref_time(24_819_000) + .saturating_add(Weight::from_proof_size(8428)) .saturating_add(RocksDbWeight::get().reads(3_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } @@ -1221,8 +1314,9 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `474` // Estimated: `5880` - // Minimum execution time: 21_051 nanoseconds. - Weight::from_parts(21_722_000, 5880) + // Minimum execution time: 21_254 nanoseconds. + Weight::from_ref_time(21_826_000) + .saturating_add(Weight::from_proof_size(5880)) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } @@ -1234,8 +1328,9 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `474` // Estimated: `5880` - // Minimum execution time: 20_095 nanoseconds. - Weight::from_parts(20_770_000, 5880) + // Minimum execution time: 20_272 nanoseconds. + Weight::from_ref_time(20_922_000) + .saturating_add(Weight::from_proof_size(5880)) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } @@ -1245,8 +1340,9 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `42` // Estimated: `2527` - // Minimum execution time: 14_078 nanoseconds. - Weight::from_parts(14_582_000, 2527) + // Minimum execution time: 14_287 nanoseconds. + Weight::from_ref_time(14_960_000) + .saturating_add(Weight::from_proof_size(2527)) .saturating_add(RocksDbWeight::get().reads(1_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } @@ -1258,8 +1354,9 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `333` // Estimated: `5103` - // Minimum execution time: 17_677 nanoseconds. - Weight::from_parts(18_381_000, 5103) + // Minimum execution time: 17_948 nanoseconds. + Weight::from_ref_time(18_780_000) + .saturating_add(Weight::from_proof_size(5103)) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } @@ -1271,8 +1368,9 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `333` // Estimated: `5103` - // Minimum execution time: 16_295 nanoseconds. - Weight::from_parts(17_036_000, 5103) + // Minimum execution time: 16_616 nanoseconds. + Weight::from_ref_time(17_155_000) + .saturating_add(Weight::from_proof_size(5103)) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } @@ -1288,8 +1386,9 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `516` // Estimated: `8407` - // Minimum execution time: 22_847 nanoseconds. - Weight::from_parts(23_536_000, 8407) + // Minimum execution time: 22_777 nanoseconds. + Weight::from_ref_time(23_955_000) + .saturating_add(Weight::from_proof_size(8407)) .saturating_add(RocksDbWeight::get().reads(3_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } @@ -1313,8 +1412,9 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `934` // Estimated: `16129` - // Minimum execution time: 60_517 nanoseconds. - Weight::from_parts(62_528_000, 16129) + // Minimum execution time: 61_131 nanoseconds. + Weight::from_ref_time(62_791_000) + .saturating_add(Weight::from_proof_size(16129)) .saturating_add(RocksDbWeight::get().reads(6_u64)) .saturating_add(RocksDbWeight::get().writes(6_u64)) } @@ -1323,10 +1423,11 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_866 nanoseconds. - Weight::from_ref_time(3_949_301) - // Standard Error: 11_044 - .saturating_add(Weight::from_ref_time(3_424_466).saturating_mul(n.into())) + // Minimum execution time: 1_952 nanoseconds. + Weight::from_ref_time(3_975_700) + .saturating_add(Weight::from_proof_size(0)) + // Standard Error: 11_254 + .saturating_add(Weight::from_ref_time(3_501_698).saturating_mul(n.into())) } /// Storage: Nfts Item (r:2 w:0) /// Proof: Nfts Item (max_values: None, max_size: Some(861), added: 3336, mode: MaxEncodedLen) @@ -1336,8 +1437,9 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `524` // Estimated: `6672` - // Minimum execution time: 21_174 nanoseconds. - Weight::from_parts(21_619_000, 6672) + // Minimum execution time: 20_327 nanoseconds. + Weight::from_ref_time(21_714_000) + .saturating_add(Weight::from_proof_size(6672)) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } @@ -1349,8 +1451,9 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `511` // Estimated: `5882` - // Minimum execution time: 20_606 nanoseconds. - Weight::from_parts(21_150_000, 5882) + // Minimum execution time: 20_668 nanoseconds. + Weight::from_ref_time(21_416_000) + .saturating_add(Weight::from_proof_size(5882)) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } @@ -1374,8 +1477,9 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `1097` // Estimated: `21970` - // Minimum execution time: 88_414 nanoseconds. - Weight::from_parts(91_830_000, 21970) + // Minimum execution time: 88_006 nanoseconds. + Weight::from_ref_time(90_390_000) + .saturating_add(Weight::from_proof_size(21970)) .saturating_add(RocksDbWeight::get().reads(8_u64)) .saturating_add(RocksDbWeight::get().writes(11_u64)) } @@ -1400,14 +1504,43 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `596` // Estimated: `16180 + n * (2921 ±0)` - // Minimum execution time: 124_354 nanoseconds. - Weight::from_parts(133_779_491, 16180) - // Standard Error: 38_452 - .saturating_add(Weight::from_ref_time(25_110_697).saturating_mul(n.into())) + // Minimum execution time: 124_967 nanoseconds. + Weight::from_ref_time(131_602_642) + .saturating_add(Weight::from_proof_size(16180)) + // Standard Error: 36_480 + .saturating_add(Weight::from_ref_time(25_811_394).saturating_mul(n.into())) .saturating_add(RocksDbWeight::get().reads(6_u64)) .saturating_add(RocksDbWeight::get().reads((1_u64).saturating_mul(n.into()))) .saturating_add(RocksDbWeight::get().writes(6_u64)) .saturating_add(RocksDbWeight::get().writes((1_u64).saturating_mul(n.into()))) .saturating_add(Weight::from_proof_size(2921).saturating_mul(n.into())) } + /// Storage: Nfts Item (r:1 w:0) + /// Proof: Nfts Item (max_values: None, max_size: Some(861), added: 3336, mode: MaxEncodedLen) + /// Storage: Nfts ItemAttributesApprovalsOf (r:1 w:1) + /// Proof: Nfts ItemAttributesApprovalsOf (max_values: None, max_size: Some(681), added: 3156, mode: MaxEncodedLen) + /// Storage: Nfts Collection (r:1 w:1) + /// Proof: Nfts Collection (max_values: None, max_size: Some(80), added: 2555, mode: MaxEncodedLen) + /// Storage: Nfts CollectionConfigOf (r:1 w:0) + /// Proof: Nfts CollectionConfigOf (max_values: None, max_size: Some(73), added: 2548, mode: MaxEncodedLen) + /// Storage: Nfts Attribute (r:10 w:10) + /// Proof: Nfts Attribute (max_values: None, max_size: Some(446), added: 2921, mode: MaxEncodedLen) + /// Storage: System Account (r:1 w:1) + /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) + /// The range of component `n` is `[0, 10]`. + fn set_attributes_pre_signed(n: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `716` + // Estimated: `14198 + n * (2921 ±0)` + // Minimum execution time: 84_153 nanoseconds. + Weight::from_ref_time(96_401_623) + .saturating_add(Weight::from_proof_size(14198)) + // Standard Error: 70_244 + .saturating_add(Weight::from_ref_time(26_866_222).saturating_mul(n.into())) + .saturating_add(RocksDbWeight::get().reads(4_u64)) + .saturating_add(RocksDbWeight::get().reads((1_u64).saturating_mul(n.into()))) + .saturating_add(RocksDbWeight::get().writes(2_u64)) + .saturating_add(RocksDbWeight::get().writes((1_u64).saturating_mul(n.into()))) + .saturating_add(Weight::from_proof_size(2921).saturating_mul(n.into())) + } } From 56de870f943679fede8443d06d3aa96a9f7321d6 Mon Sep 17 00:00:00 2001 From: Sam Johnson Date: Wed, 22 Feb 2023 09:09:11 -0500 Subject: [PATCH 154/558] generation of real benchmark functions for benchmarking v2 (#13224) * function generation with _name working, need to modify signature * WIP * support custom BenchmarkResult type * full support for BenchmarkResult on benchmark function defs * support () return type for benchmark function defs that don't use ? * uncomment * fix where clause handling * fix benchmark function call bodies * proper parsing of return type * add UI tests for bad return type * fix detection of missing last_stmt with defined return type * UI tests covering missing last_stmt * properly detect and complain about empty benchmark function defs * fix missing Comma in Result parsing + test * add additional UI test * allow complex path for BenchmarkResult and BenchmarkError in fn defs * add UI tests covering complex path for BenchmarkResult, BenchmarkError * retain doc comments and attributes * also add attributes to struct * add docs for benchmark function definition support * fix imports on benchmark example * fix issue with unused variables in extrinsic call fn def * fix up docs * remove support for v2::BenchmarkResult because it was confusing * fix typo * remove ability to use custom T for Result in v2 * use missing call error instead of empty_fn() * remove unneeded match statement * Add a proper QED Co-authored-by: Keith Yeung * fix other QED Co-authored-by: Keith Yeung * cargo fmt * add an explicit error for non TypePath as return type * tweak error warning and add a UI test for non TypePath return * remove comment * add docs about T and I generic params * improve docs referring to section "below" * pull out return type checking logic into its own function * pull out params parsing into its own function * pull out call_def parsing into its own function * add doc comment for missing_call() * replace spaces with tabs * add a result-based example to the benchmarking examples --------- Co-authored-by: Keith Yeung --- frame/balances/src/benchmarking.rs | 4 +- frame/benchmarking/src/lib.rs | 108 ++++++- frame/examples/basic/src/benchmarking.rs | 20 +- frame/support/procedural/src/benchmark.rs | 297 ++++++++++++++---- .../bad_return_non_benchmark_err.rs | 19 ++ .../bad_return_non_benchmark_err.stderr | 5 + .../benchmark_ui/bad_return_non_type_path.rs | 17 + .../bad_return_non_type_path.stderr | 5 + .../benchmark_ui/bad_return_non_unit_t.rs | 15 + .../benchmark_ui/bad_return_non_unit_t.stderr | 5 + .../bad_return_type_blank_with_question.rs | 22 ++ ...bad_return_type_blank_with_question.stderr | 10 + .../bad_return_type_no_last_stmt.rs | 16 + .../bad_return_type_no_last_stmt.stderr | 9 + .../bad_return_type_non_result.rs | 19 ++ .../bad_return_type_non_result.stderr | 5 + .../benchmark_ui/bad_return_type_option.rs | 18 ++ .../bad_return_type_option.stderr | 5 + .../test/tests/benchmark_ui/empty_function.rs | 13 + .../tests/benchmark_ui/empty_function.stderr | 5 + .../test/tests/benchmark_ui/missing_call.rs | 4 +- .../tests/benchmark_ui/missing_call.stderr | 7 +- .../valid_complex_path_benchmark_result.rs | 17 + .../benchmark_ui/pass/valid_no_last_stmt.rs | 16 + .../pass/valid_path_result_benchmark_error.rs | 17 + .../tests/benchmark_ui/pass/valid_result.rs | 18 ++ 26 files changed, 601 insertions(+), 95 deletions(-) create mode 100644 frame/support/test/tests/benchmark_ui/bad_return_non_benchmark_err.rs create mode 100644 frame/support/test/tests/benchmark_ui/bad_return_non_benchmark_err.stderr create mode 100644 frame/support/test/tests/benchmark_ui/bad_return_non_type_path.rs create mode 100644 frame/support/test/tests/benchmark_ui/bad_return_non_type_path.stderr create mode 100644 frame/support/test/tests/benchmark_ui/bad_return_non_unit_t.rs create mode 100644 frame/support/test/tests/benchmark_ui/bad_return_non_unit_t.stderr create mode 100644 frame/support/test/tests/benchmark_ui/bad_return_type_blank_with_question.rs create mode 100644 frame/support/test/tests/benchmark_ui/bad_return_type_blank_with_question.stderr create mode 100644 frame/support/test/tests/benchmark_ui/bad_return_type_no_last_stmt.rs create mode 100644 frame/support/test/tests/benchmark_ui/bad_return_type_no_last_stmt.stderr create mode 100644 frame/support/test/tests/benchmark_ui/bad_return_type_non_result.rs create mode 100644 frame/support/test/tests/benchmark_ui/bad_return_type_non_result.stderr create mode 100644 frame/support/test/tests/benchmark_ui/bad_return_type_option.rs create mode 100644 frame/support/test/tests/benchmark_ui/bad_return_type_option.stderr create mode 100644 frame/support/test/tests/benchmark_ui/empty_function.rs create mode 100644 frame/support/test/tests/benchmark_ui/empty_function.stderr create mode 100644 frame/support/test/tests/benchmark_ui/pass/valid_complex_path_benchmark_result.rs create mode 100644 frame/support/test/tests/benchmark_ui/pass/valid_no_last_stmt.rs create mode 100644 frame/support/test/tests/benchmark_ui/pass/valid_path_result_benchmark_error.rs create mode 100644 frame/support/test/tests/benchmark_ui/pass/valid_result.rs diff --git a/frame/balances/src/benchmarking.rs b/frame/balances/src/benchmarking.rs index c2ed0ace5..b36fe1e34 100644 --- a/frame/balances/src/benchmarking.rs +++ b/frame/balances/src/benchmarking.rs @@ -225,7 +225,7 @@ mod benchmarks { } #[benchmark] - fn force_unreserve() { + fn force_unreserve() -> Result<(), BenchmarkError> { let user: T::AccountId = account("user", 0, SEED); let user_lookup = T::Lookup::unlookup(user.clone()); @@ -244,6 +244,8 @@ mod benchmarks { assert!(Balances::::reserved_balance(&user).is_zero()); assert_eq!(Balances::::free_balance(&user), balance); + + Ok(()) } impl_benchmark_test_suite! { diff --git a/frame/benchmarking/src/lib.rs b/frame/benchmarking/src/lib.rs index f5dc51365..7110c378d 100644 --- a/frame/benchmarking/src/lib.rs +++ b/frame/benchmarking/src/lib.rs @@ -114,21 +114,22 @@ pub use v1::*; /// Within a `#[benchmarks]` or `#[instance_benchmarks]` module, you can define individual /// benchmarks using the `#[benchmark]` attribute, as shown in the example above. /// -/// The `#[benchmark]` attribute expects a function definition with a blank return type and -/// zero or more arguments whose names are valid -/// [BenchmarkParameter](`crate::BenchmarkParameter`) parameters, such as `x`, `y`, `a`, `b`, -/// etc., and whose param types must implement [ParamRange](`v2::ParamRange`). At the moment -/// the only valid type that implements [ParamRange](`v2::ParamRange`) is -/// [Linear](`v2::Linear`). -/// -/// The valid syntax for defining a [Linear](`v2::Linear`)is `Linear` where `A`, and `B` +/// The `#[benchmark]` attribute expects a function definition with a blank return type (or a +/// return type compatible with `Result<(), BenchmarkError>`, as discussed below) and zero or +/// more arguments whose names are valid [BenchmarkParameter](`crate::BenchmarkParameter`) +/// parameters, such as `x`, `y`, `a`, `b`, etc., and whose param types must implement +/// [ParamRange](`v2::ParamRange`). At the moment the only valid type that implements +/// [ParamRange](`v2::ParamRange`) is [Linear](`v2::Linear`). +/// +/// The valid syntax for defining a [Linear](`v2::Linear`) is `Linear` where `A`, and `B` /// are valid integer literals (that fit in a `u32`), such that `B` >= `A`. /// -/// Note that the benchmark function definition does not actually expand as a function -/// definition, but rather is used to automatically create a number of impls and structs -/// required by the benchmarking engine. For this reason, the visibility of the function -/// definition as well as the return type are not used for any purpose and are discarded by the -/// expansion code. +/// Anywhere within a benchmark function you may use the generic `T: Config` parameter as well +/// as `I` in the case of an `#[instance_benchmarks]` module. You should not add these to the +/// function signature as this will be handled automatically for you based on whether this is a +/// `#[benchmarks]` or `#[instance_benchmarks]` module and whatever [where clause](#where-clause) +/// you have defined for the the module. You should not manually add any generics to the +/// signature of your benchmark function. /// /// Also note that the `// setup code` and `// verification code` comments shown above are not /// required and are included simply for demonstration purposes. @@ -189,10 +190,10 @@ pub use v1::*; /// /// #### `skip_meta` /// -/// Specifies that the benchmarking framework should not analyze the storage keys that +/// Specifies that the benchmarking framework should not analyze the storage keys that the /// benchmarked code read or wrote. This useful to suppress the prints in the form of unknown -/// 0x… in case a storage key that does not have metadata. Note that this skips the analysis -/// of all accesses, not just ones without metadata. +/// 0x… in case a storage key that does not have metadata. Note that this skips the analysis of +/// all accesses, not just ones without metadata. /// /// ## Where Clause /// @@ -231,6 +232,79 @@ pub use v1::*; /// ); /// } /// ``` +/// +/// ## Benchmark Function Generation +/// +/// The benchmark function definition that you provide is used to automatically create a number +/// of impls and structs required by the benchmarking engine. Additionally, a benchmark +/// function is also generated that resembles the function definition you provide, with a few +/// modifications: +/// 1. The function name is transformed from i.e. `original_name` to `_original_name` so as not +/// to collide with the struct `original_name` that is created for some of the benchmarking +/// engine impls. +/// 2. Appropriate `T: Config` and `I` (if this is an instance benchmark) generics are added to +/// the function automatically during expansion, so you should not add these manually on +/// your function definition (but you may make use of `T` and `I` anywhere within your +/// benchmark function, in any of the three sections (setup, call, verification). +/// 3. Arguments such as `u: Linear<10, 100>` are converted to `u: u32` to make the function +/// directly callable. +/// 4. A `verify: bool` param is added as the last argument. Specifying `true` will result in +/// the verification section of your function executing, while a value of `false` will skip +/// verification. +/// 5. If you specify a return type on the function definition, it must conform to the [rules +/// below](#support-for-result-benchmarkerror-and-the--operator), and the last statement of +/// the function definition must resolve to something compatible with `Result<(), +/// BenchmarkError>`. +/// +/// The reason we generate an actual function as part of the expansion is to allow the compiler +/// to enforce several constraints that would otherwise be difficult to enforce and to reduce +/// developer confusion (especially regarding the use of the `?` operator, as covered below). +/// +/// Note that any attributes, comments, and doc comments attached to your benchmark function +/// definition are also carried over onto the resulting benchmark function and the struct for +/// that benchmark. As a result you should be careful about what attributes you attach here as +/// they will be replicated in multiple places. +/// +/// ### Support for `Result<(), BenchmarkError>` and the `?` operator +/// +/// You may optionally specify `Result<(), BenchmarkError>` as the return type of your +/// benchmark function definition. If you do so, you must return a compatible `Result<(), +/// BenchmarkError>` as the *last statement* of your benchmark function definition. You may +/// also use the `?` operator throughout your benchmark function definition if you choose to +/// follow this route. See the example below: +/// +/// ```ignore +/// #![cfg(feature = "runtime-benchmarks")] +/// +/// use super::{mock_helpers::*, Pallet as MyPallet}; +/// use frame_benchmarking::v2::*; +/// +/// #[benchmarks] +/// mod benchmarks { +/// use super::*; +/// +/// #[benchmark] +/// fn bench_name(x: Linear<5, 25>) -> Result<(), BenchmarkError> { +/// // setup code +/// let z = x + 4; +/// let caller = whitelisted_caller(); +/// +/// // note we can make use of the ? operator here because of the return type +/// something(z)?; +/// +/// #[extrinsic_call] +/// extrinsic_name(SystemOrigin::Signed(caller), other, arguments); +/// +/// // verification code +/// assert_eq!(MyPallet::::my_var(), z); +/// +/// // we must return a valid `Result<(), BenchmarkError>` as the last line of our benchmark +/// // function definition. This line is not included as part of the verification code that +/// // appears above it. +/// Ok(()) +/// } +/// } +/// ``` pub mod v2 { pub use super::*; pub use frame_support_procedural::{ @@ -240,7 +314,7 @@ pub mod v2 { // Used in #[benchmark] implementation to ensure that benchmark function arguments // implement [`ParamRange`]. #[doc(hidden)] - pub use static_assertions::assert_impl_all; + pub use static_assertions::{assert_impl_all, assert_type_eq_all}; /// Used by the new benchmarking code to specify that a benchmarking variable is linear /// over some specified range, i.e. `Linear<0, 1_000>` means that the corresponding variable diff --git a/frame/examples/basic/src/benchmarking.rs b/frame/examples/basic/src/benchmarking.rs index 0d2e9c5b3..4b2ebb41f 100644 --- a/frame/examples/basic/src/benchmarking.rs +++ b/frame/examples/basic/src/benchmarking.rs @@ -21,11 +21,7 @@ #![cfg(feature = "runtime-benchmarks")] use crate::*; -use frame_benchmarking::v1::{ - impl_benchmark_test_suite, - v2::{benchmarks, Linear}, - whitelisted_caller, -}; +use frame_benchmarking::v2::*; use frame_system::RawOrigin; // To actually run this benchmark on pallet-example-basic, we need to put this pallet into the @@ -55,19 +51,31 @@ mod benchmarks { assert_eq!(Pallet::::dummy(), Some(value)) } + // An example method that returns a Result that can be called within a benchmark + fn example_result_method() -> Result<(), BenchmarkError> { + Ok(()) + } + // This will measure the execution time of `accumulate_dummy`. // The benchmark execution phase is shorthanded. When the name of the benchmark case is the same // as the extrinsic call. `_(...)` is used to represent the extrinsic name. // The benchmark verification phase is omitted. #[benchmark] - fn accumulate_dummy() { + fn accumulate_dummy() -> Result<(), BenchmarkError> { let value = 1000u32.into(); // The caller account is whitelisted for DB reads/write by the benchmarking macro. let caller: T::AccountId = whitelisted_caller(); + // an example of calling something result-based within a benchmark using the ? operator + // this necessitates specifying the `Result<(), BenchmarkError>` return type + example_result_method()?; + // You can use `_` if the name of the Call matches the benchmark name. #[extrinsic_call] _(RawOrigin::Signed(caller), value); + + // need this to be compatible with the return type + Ok(()) } /// You can write helper functions in here since its a normal Rust module. diff --git a/frame/support/procedural/src/benchmark.rs b/frame/support/procedural/src/benchmark.rs index 6cf1d7ecc..1aca2f87b 100644 --- a/frame/support/procedural/src/benchmark.rs +++ b/frame/support/procedural/src/benchmark.rs @@ -25,11 +25,13 @@ use quote::{quote, quote_spanned, ToTokens}; use syn::{ parenthesized, parse::{Nothing, ParseStream}, + parse_quote, punctuated::Punctuated, spanned::Spanned, token::{Colon2, Comma, Gt, Lt, Paren}, Attribute, Error, Expr, ExprBlock, ExprCall, ExprPath, FnArg, Item, ItemFn, ItemMod, LitInt, - Pat, Path, PathArguments, PathSegment, Result, Stmt, Token, Type, WhereClause, + Pat, Path, PathArguments, PathSegment, Result, ReturnType, Signature, Stmt, Token, Type, + TypePath, Visibility, WhereClause, }; mod keywords { @@ -41,6 +43,8 @@ mod keywords { custom_keyword!(extra); custom_keyword!(extrinsic_call); custom_keyword!(skip_meta); + custom_keyword!(BenchmarkError); + custom_keyword!(Result); } /// This represents the raw parsed data for a param definition such as `x: Linear<10, 20>`. @@ -145,62 +149,121 @@ struct BenchmarkDef { setup_stmts: Vec, call_def: BenchmarkCallDef, verify_stmts: Vec, + last_stmt: Option, extra: bool, skip_meta: bool, + fn_sig: Signature, + fn_vis: Visibility, + fn_attrs: Vec, } -impl BenchmarkDef { - /// Constructs a [`BenchmarkDef`] by traversing an existing [`ItemFn`] node. - pub fn from(item_fn: &ItemFn, extra: bool, skip_meta: bool) -> Result { - let mut params: Vec = Vec::new(); +/// used to parse something compatible with `Result` +#[derive(Parse)] +struct ResultDef { + _result_kw: keywords::Result, + _lt: Token![<], + unit: Type, + _comma: Comma, + e_type: TypePath, + _gt: Token![>], +} - // parse params such as "x: Linear<0, 1>" - for arg in &item_fn.sig.inputs { - let invalid_param = |span| { - return Err(Error::new(span, "Invalid benchmark function param. A valid example would be `x: Linear<5, 10>`.", )) +/// Ensures that `ReturnType` is a `Result<(), BenchmarkError>`, if specified +fn ensure_valid_return_type(item_fn: &ItemFn) -> Result<()> { + if let ReturnType::Type(_, typ) = &item_fn.sig.output { + let non_unit = |span| return Err(Error::new(span, "expected `()`")); + let Type::Path(TypePath { path, qself: _ }) = &**typ else { + return Err(Error::new( + typ.span(), + "Only `Result<(), BenchmarkError>` or a blank return type is allowed on benchmark function definitions", + )) }; + let seg = path + .segments + .last() + .expect("to be parsed as a TypePath, it must have at least one segment; qed"); + let res: ResultDef = syn::parse2(seg.to_token_stream())?; + // ensure T in Result is () + let Type::Tuple(tup) = res.unit else { return non_unit(res.unit.span()) }; + if !tup.elems.is_empty() { + return non_unit(tup.span()) + } + let TypePath { path, qself: _ } = res.e_type; + let seg = path + .segments + .last() + .expect("to be parsed as a TypePath, it must have at least one segment; qed"); + syn::parse2::(seg.to_token_stream())?; + } + Ok(()) +} - let FnArg::Typed(arg) = arg else { return invalid_param(arg.span()) }; - let Pat::Ident(ident) = &*arg.pat else { return invalid_param(arg.span()) }; +/// Parses params such as `x: Linear<0, 1>` +fn parse_params(item_fn: &ItemFn) -> Result> { + let mut params: Vec = Vec::new(); + for arg in &item_fn.sig.inputs { + let invalid_param = |span| { + return Err(Error::new( + span, + "Invalid benchmark function param. A valid example would be `x: Linear<5, 10>`.", + )) + }; - // check param name - let var_span = ident.span(); - let invalid_param_name = || { - return Err(Error::new( + let FnArg::Typed(arg) = arg else { return invalid_param(arg.span()) }; + let Pat::Ident(ident) = &*arg.pat else { return invalid_param(arg.span()) }; + + // check param name + let var_span = ident.span(); + let invalid_param_name = || { + return Err(Error::new( var_span, "Benchmark parameter names must consist of a single lowercase letter (a-z) and no other characters.", )) - }; - let name = ident.ident.to_token_stream().to_string(); - if name.len() > 1 { - return invalid_param_name() - }; - let Some(name_char) = name.chars().next() else { return invalid_param_name() }; - if !name_char.is_alphabetic() || !name_char.is_lowercase() { - return invalid_param_name() - } + }; + let name = ident.ident.to_token_stream().to_string(); + if name.len() > 1 { + return invalid_param_name() + }; + let Some(name_char) = name.chars().next() else { return invalid_param_name() }; + if !name_char.is_alphabetic() || !name_char.is_lowercase() { + return invalid_param_name() + } - // parse type - let typ = &*arg.ty; - let Type::Path(tpath) = typ else { return invalid_param(typ.span()) }; - let Some(segment) = tpath.path.segments.last() else { return invalid_param(typ.span()) }; - let args = segment.arguments.to_token_stream().into(); - let Ok(args) = syn::parse::(args) else { return invalid_param(typ.span()) }; - let Ok(start) = args.start.base10_parse::() else { return invalid_param(args.start.span()) }; - let Ok(end) = args.end.base10_parse::() else { return invalid_param(args.end.span()) }; + // parse type + let typ = &*arg.ty; + let Type::Path(tpath) = typ else { return invalid_param(typ.span()) }; + let Some(segment) = tpath.path.segments.last() else { return invalid_param(typ.span()) }; + let args = segment.arguments.to_token_stream().into(); + let Ok(args) = syn::parse::(args) else { return invalid_param(typ.span()) }; + let Ok(start) = args.start.base10_parse::() else { return invalid_param(args.start.span()) }; + let Ok(end) = args.end.base10_parse::() else { return invalid_param(args.end.span()) }; + + if end < start { + return Err(Error::new( + args.start.span(), + "The start of a `ParamRange` must be less than or equal to the end", + )) + } - if end < start { - return Err(Error::new( - args.start.span(), - "The start of a `ParamRange` must be less than or equal to the end", - )) - } + params.push(ParamDef { name, typ: typ.clone(), start, end }); + } + Ok(params) +} - params.push(ParamDef { name, typ: typ.clone(), start, end }); - } +/// Used in several places where the `#[extrinsic_call]` or `#[body]` annotation is missing +fn missing_call(item_fn: &ItemFn) -> Result { + return Err(Error::new( + item_fn.block.brace_token.span, + "No valid #[extrinsic_call] or #[block] annotation could be found in benchmark function body." + )) +} - // #[extrinsic_call] / #[block] handling - let call_defs = item_fn.block.stmts.iter().enumerate().filter_map(|(i, child)| { +/// Finds the `BenchmarkCallDef` and its index (within the list of stmts for the fn) and +/// returns them. Also handles parsing errors for invalid / extra call defs. AKA this is +/// general handling for `#[extrinsic_call]` and `#[block]` +fn parse_call_def(item_fn: &ItemFn) -> Result<(usize, BenchmarkCallDef)> { + // #[extrinsic_call] / #[block] handling + let call_defs = item_fn.block.stmts.iter().enumerate().filter_map(|(i, child)| { if let Stmt::Semi(Expr::Call(expr_call), _semi) = child { // #[extrinsic_call] case expr_call.attrs.iter().enumerate().find_map(|(k, attr)| { @@ -234,25 +297,60 @@ impl BenchmarkDef { None } }).collect::>>()?; - let (i, call_def) = match &call_defs[..] { - [(i, call_def)] => (*i, call_def.clone()), // = 1 - [] => return Err(Error::new( // = 0 - item_fn.block.brace_token.span, - "No valid #[extrinsic_call] or #[block] annotation could be found in benchmark function body." - )), - _ => return Err(Error::new( // > 1 + Ok(match &call_defs[..] { + [(i, call_def)] => (*i, call_def.clone()), // = 1 + [] => return missing_call(item_fn), + _ => + return Err(Error::new( call_defs[1].1.attr_span(), - "Only one #[extrinsic_call] or #[block] attribute is allowed per benchmark." + "Only one #[extrinsic_call] or #[block] attribute is allowed per benchmark.", )), + }) +} + +impl BenchmarkDef { + /// Constructs a [`BenchmarkDef`] by traversing an existing [`ItemFn`] node. + pub fn from(item_fn: &ItemFn, extra: bool, skip_meta: bool) -> Result { + let params = parse_params(item_fn)?; + ensure_valid_return_type(item_fn)?; + let (i, call_def) = parse_call_def(&item_fn)?; + + let (verify_stmts, last_stmt) = match item_fn.sig.output { + ReturnType::Default => + // no return type, last_stmt should be None + (Vec::from(&item_fn.block.stmts[(i + 1)..item_fn.block.stmts.len()]), None), + ReturnType::Type(_, _) => { + // defined return type, last_stmt should be Result<(), BenchmarkError> + // compatible and should not be included in verify_stmts + if i + 1 >= item_fn.block.stmts.len() { + return Err(Error::new( + item_fn.block.span(), + "Benchmark `#[block]` or `#[extrinsic_call]` item cannot be the \ + last statement of your benchmark function definition if you have \ + defined a return type. You should return something compatible \ + with Result<(), BenchmarkError> (i.e. `Ok(())`) as the last statement \ + or change your signature to a blank return type.", + )) + } + let Some(stmt) = item_fn.block.stmts.last() else { return missing_call(item_fn) }; + ( + Vec::from(&item_fn.block.stmts[(i + 1)..item_fn.block.stmts.len() - 1]), + Some(stmt.clone()), + ) + }, }; Ok(BenchmarkDef { params, setup_stmts: Vec::from(&item_fn.block.stmts[0..i]), call_def, - verify_stmts: Vec::from(&item_fn.block.stmts[(i + 1)..item_fn.block.stmts.len()]), + verify_stmts, + last_stmt, extra, skip_meta, + fn_sig: item_fn.sig.clone(), + fn_vis: item_fn.vis.clone(), + fn_attrs: item_fn.attrs.clone(), }) } } @@ -643,6 +741,7 @@ fn expand_benchmark( let traits = quote!(#krate::frame_support::traits); let setup_stmts = benchmark_def.setup_stmts; let verify_stmts = benchmark_def.verify_stmts; + let last_stmt = benchmark_def.last_stmt; let test_ident = Ident::new(format!("test_{}", name.to_string()).as_str(), Span::call_site()); // unroll params (prepare for quoting) @@ -661,7 +760,8 @@ fn expand_benchmark( true => quote!(T: Config, I: 'static), }; - let (pre_call, post_call) = match benchmark_def.call_def { + // used in the benchmarking impls + let (pre_call, post_call, fn_call_body) = match &benchmark_def.call_def { BenchmarkCallDef::ExtrinsicCall { origin, expr_call, attr_span: _ } => { let mut expr_call = expr_call.clone(); @@ -705,36 +805,97 @@ fn expand_benchmark( qself: None, path: Path { leading_colon: None, segments: punct }, }); - + let pre_call = quote! { + let __call = Call::<#type_use_generics>::#expr_call; + let __benchmarked_call_encoded = #codec::Encode::encode(&__call); + }; + let post_call = quote! { + let __call_decoded = as #codec::Decode> + ::decode(&mut &__benchmarked_call_encoded[..]) + .expect("call is encoded above, encoding must be correct"); + let __origin = #origin.into(); + as #traits::UnfilteredDispatchable>::dispatch_bypass_filter( + __call_decoded, + __origin, + ) + }; ( - // (pre_call, post_call): - quote! { - let __call = Call::<#type_use_generics>::#expr_call; - let __benchmarked_call_encoded = #codec::Encode::encode(&__call); - }, + // (pre_call, post_call, fn_call_body): + pre_call.clone(), + quote!(#post_call?;), quote! { - let __call_decoded = as #codec::Decode> - ::decode(&mut &__benchmarked_call_encoded[..]) - .expect("call is encoded above, encoding must be correct"); - let __origin = #origin.into(); - as #traits::UnfilteredDispatchable>::dispatch_bypass_filter( - __call_decoded, - __origin, - )?; + #pre_call + #post_call.unwrap(); }, ) }, - BenchmarkCallDef::Block { block, attr_span: _ } => (quote!(), quote!(#block)), + BenchmarkCallDef::Block { block, attr_span: _ } => + (quote!(), quote!(#block), quote!(#block)), + }; + + let vis = benchmark_def.fn_vis; + + // remove #[benchmark] attribute + let fn_attrs: Vec<&Attribute> = benchmark_def + .fn_attrs + .iter() + .filter(|attr| !syn::parse2::(attr.path.to_token_stream()).is_ok()) + .collect(); + + // modify signature generics, ident, and inputs, e.g: + // before: `fn bench(u: Linear<1, 100>) -> Result<(), BenchmarkError>` + // after: `fn _bench , I: 'static>(u: u32, verify: bool) -> Result<(), + // BenchmarkError>` + let mut sig = benchmark_def.fn_sig; + sig.generics = parse_quote!(<#type_impl_generics>); + if !where_clause.is_empty() { + sig.generics.where_clause = parse_quote!(where #where_clause); + } + sig.ident = + Ident::new(format!("_{}", name.to_token_stream().to_string()).as_str(), Span::call_site()); + let mut fn_param_inputs: Vec = + param_names.iter().map(|name| quote!(#name: u32)).collect(); + fn_param_inputs.push(quote!(verify: bool)); + sig.inputs = parse_quote!(#(#fn_param_inputs),*); + + // used in instance() impl + let impl_last_stmt = match &last_stmt { + Some(stmt) => quote!(#stmt), + None => quote!(Ok(())), + }; + + let fn_def = quote! { + #( + #fn_attrs + )* + #vis #sig { + #( + #setup_stmts + )* + #fn_call_body + if verify { + #( + #verify_stmts + )* + } + #last_stmt + } }; // generate final quoted tokens let res = quote! { + // benchmark function definition + #fn_def + // compile-time assertions that each referenced param type implements ParamRange #( #home::assert_impl_all!(#param_types: #home::ParamRange); )* #[allow(non_camel_case_types)] + #( + #fn_attrs + )* struct #name; #[allow(unused_variables)] @@ -773,7 +934,7 @@ fn expand_benchmark( #verify_stmts )* } - Ok(()) + #impl_last_stmt })) } } diff --git a/frame/support/test/tests/benchmark_ui/bad_return_non_benchmark_err.rs b/frame/support/test/tests/benchmark_ui/bad_return_non_benchmark_err.rs new file mode 100644 index 000000000..5e332801d --- /dev/null +++ b/frame/support/test/tests/benchmark_ui/bad_return_non_benchmark_err.rs @@ -0,0 +1,19 @@ +use frame_benchmarking::v2::*; +#[allow(unused_imports)] +use frame_support_test::Config; + +#[benchmarks] +mod benches { + use super::*; + + #[benchmark] + fn bench() -> Result<(), BenchmarkException> { + let a = 2 + 2; + #[block] + {} + assert_eq!(a, 4); + Ok(()) + } +} + +fn main() {} diff --git a/frame/support/test/tests/benchmark_ui/bad_return_non_benchmark_err.stderr b/frame/support/test/tests/benchmark_ui/bad_return_non_benchmark_err.stderr new file mode 100644 index 000000000..ab0bff54a --- /dev/null +++ b/frame/support/test/tests/benchmark_ui/bad_return_non_benchmark_err.stderr @@ -0,0 +1,5 @@ +error: expected `BenchmarkError` + --> tests/benchmark_ui/bad_return_non_benchmark_err.rs:10:27 + | +10 | fn bench() -> Result<(), BenchmarkException> { + | ^^^^^^^^^^^^^^^^^^ diff --git a/frame/support/test/tests/benchmark_ui/bad_return_non_type_path.rs b/frame/support/test/tests/benchmark_ui/bad_return_non_type_path.rs new file mode 100644 index 000000000..a4b0d007e --- /dev/null +++ b/frame/support/test/tests/benchmark_ui/bad_return_non_type_path.rs @@ -0,0 +1,17 @@ +use frame_benchmarking::v2::*; +#[allow(unused_imports)] +use frame_support_test::Config; + +#[benchmarks] +mod benchmarks { + use super::*; + + #[benchmark] + fn bench() -> (String, u32) { + #[block] + {} + (String::from("hey"), 23) + } +} + +fn main() {} diff --git a/frame/support/test/tests/benchmark_ui/bad_return_non_type_path.stderr b/frame/support/test/tests/benchmark_ui/bad_return_non_type_path.stderr new file mode 100644 index 000000000..69d61b422 --- /dev/null +++ b/frame/support/test/tests/benchmark_ui/bad_return_non_type_path.stderr @@ -0,0 +1,5 @@ +error: Only `Result<(), BenchmarkError>` or a blank return type is allowed on benchmark function definitions + --> tests/benchmark_ui/bad_return_non_type_path.rs:10:16 + | +10 | fn bench() -> (String, u32) { + | ^^^^^^^^^^^^^ diff --git a/frame/support/test/tests/benchmark_ui/bad_return_non_unit_t.rs b/frame/support/test/tests/benchmark_ui/bad_return_non_unit_t.rs new file mode 100644 index 000000000..15289c298 --- /dev/null +++ b/frame/support/test/tests/benchmark_ui/bad_return_non_unit_t.rs @@ -0,0 +1,15 @@ +use frame_benchmarking::v2::*; +#[allow(unused_imports)] +use frame_support_test::Config; + +#[benchmarks] +mod benchmarks { + #[benchmark] + fn bench() -> Result { + #[block] + {} + Ok(10) + } +} + +fn main() {} diff --git a/frame/support/test/tests/benchmark_ui/bad_return_non_unit_t.stderr b/frame/support/test/tests/benchmark_ui/bad_return_non_unit_t.stderr new file mode 100644 index 000000000..4181ea099 --- /dev/null +++ b/frame/support/test/tests/benchmark_ui/bad_return_non_unit_t.stderr @@ -0,0 +1,5 @@ +error: expected `()` + --> tests/benchmark_ui/bad_return_non_unit_t.rs:8:23 + | +8 | fn bench() -> Result { + | ^^^ diff --git a/frame/support/test/tests/benchmark_ui/bad_return_type_blank_with_question.rs b/frame/support/test/tests/benchmark_ui/bad_return_type_blank_with_question.rs new file mode 100644 index 000000000..a6a2c6112 --- /dev/null +++ b/frame/support/test/tests/benchmark_ui/bad_return_type_blank_with_question.rs @@ -0,0 +1,22 @@ +use frame_benchmarking::v2::*; +#[allow(unused_imports)] +use frame_support_test::Config; + +#[benchmarks] +mod benches { + use super::*; + + fn something() -> Result<(), BenchmarkError> { + Ok(()) + } + + #[benchmark] + fn bench() { + something()?; + #[block] + {} + assert_eq!(2 + 2, 4); + } +} + +fn main() {} diff --git a/frame/support/test/tests/benchmark_ui/bad_return_type_blank_with_question.stderr b/frame/support/test/tests/benchmark_ui/bad_return_type_blank_with_question.stderr new file mode 100644 index 000000000..1f8c9f1e1 --- /dev/null +++ b/frame/support/test/tests/benchmark_ui/bad_return_type_blank_with_question.stderr @@ -0,0 +1,10 @@ +error[E0277]: the `?` operator can only be used in a function that returns `Result` or `Option` (or another type that implements `FromResidual`) + --> tests/benchmark_ui/bad_return_type_blank_with_question.rs:15:14 + | +5 | #[benchmarks] + | ------------- this function should return `Result` or `Option` to accept `?` +... +15 | something()?; + | ^ cannot use the `?` operator in a function that returns `()` + | + = help: the trait `FromResidual>` is not implemented for `()` diff --git a/frame/support/test/tests/benchmark_ui/bad_return_type_no_last_stmt.rs b/frame/support/test/tests/benchmark_ui/bad_return_type_no_last_stmt.rs new file mode 100644 index 000000000..76f129900 --- /dev/null +++ b/frame/support/test/tests/benchmark_ui/bad_return_type_no_last_stmt.rs @@ -0,0 +1,16 @@ +use frame_benchmarking::v2::*; +#[allow(unused_imports)] +use frame_support_test::Config; + +#[benchmarks] +mod benches { + use super::*; + + #[benchmark] + fn bench() -> Result<(), BenchmarkError> { + #[block] + {} + } +} + +fn main() {} diff --git a/frame/support/test/tests/benchmark_ui/bad_return_type_no_last_stmt.stderr b/frame/support/test/tests/benchmark_ui/bad_return_type_no_last_stmt.stderr new file mode 100644 index 000000000..ff501a620 --- /dev/null +++ b/frame/support/test/tests/benchmark_ui/bad_return_type_no_last_stmt.stderr @@ -0,0 +1,9 @@ +error: Benchmark `#[block]` or `#[extrinsic_call]` item cannot be the last statement of your benchmark function definition if you have defined a return type. You should return something compatible with Result<(), BenchmarkError> (i.e. `Ok(())`) as the last statement or change your signature to a blank return type. + --> tests/benchmark_ui/bad_return_type_no_last_stmt.rs:10:43 + | +10 | fn bench() -> Result<(), BenchmarkError> { + | ______________________________________________^ +11 | | #[block] +12 | | {} +13 | | } + | |_____^ diff --git a/frame/support/test/tests/benchmark_ui/bad_return_type_non_result.rs b/frame/support/test/tests/benchmark_ui/bad_return_type_non_result.rs new file mode 100644 index 000000000..c206ec36a --- /dev/null +++ b/frame/support/test/tests/benchmark_ui/bad_return_type_non_result.rs @@ -0,0 +1,19 @@ +use frame_benchmarking::v2::*; +#[allow(unused_imports)] +use frame_support_test::Config; + +#[benchmarks] +mod benches { + use super::*; + + #[benchmark] + fn bench(y: Linear<1, 2>) -> String { + let a = 2 + 2; + #[block] + {} + assert_eq!(a, 4); + String::from("test") + } +} + +fn main() {} diff --git a/frame/support/test/tests/benchmark_ui/bad_return_type_non_result.stderr b/frame/support/test/tests/benchmark_ui/bad_return_type_non_result.stderr new file mode 100644 index 000000000..b830b8eb5 --- /dev/null +++ b/frame/support/test/tests/benchmark_ui/bad_return_type_non_result.stderr @@ -0,0 +1,5 @@ +error: expected `Result` + --> tests/benchmark_ui/bad_return_type_non_result.rs:10:31 + | +10 | fn bench(y: Linear<1, 2>) -> String { + | ^^^^^^ diff --git a/frame/support/test/tests/benchmark_ui/bad_return_type_option.rs b/frame/support/test/tests/benchmark_ui/bad_return_type_option.rs new file mode 100644 index 000000000..4b5588593 --- /dev/null +++ b/frame/support/test/tests/benchmark_ui/bad_return_type_option.rs @@ -0,0 +1,18 @@ +use frame_benchmarking::v2::*; +#[allow(unused_imports)] +use frame_support_test::Config; + +#[benchmarks] +mod benches { + use super::*; + + #[benchmark] + fn bench() -> Option { + #[block] + {} + assert_eq!(2 + 2, 4); + None + } +} + +fn main() {} diff --git a/frame/support/test/tests/benchmark_ui/bad_return_type_option.stderr b/frame/support/test/tests/benchmark_ui/bad_return_type_option.stderr new file mode 100644 index 000000000..050da1676 --- /dev/null +++ b/frame/support/test/tests/benchmark_ui/bad_return_type_option.stderr @@ -0,0 +1,5 @@ +error: expected `Result` + --> tests/benchmark_ui/bad_return_type_option.rs:10:16 + | +10 | fn bench() -> Option { + | ^^^^^^ diff --git a/frame/support/test/tests/benchmark_ui/empty_function.rs b/frame/support/test/tests/benchmark_ui/empty_function.rs new file mode 100644 index 000000000..bc04101dd --- /dev/null +++ b/frame/support/test/tests/benchmark_ui/empty_function.rs @@ -0,0 +1,13 @@ +use frame_benchmarking::v2::*; +#[allow(unused_imports)] +use frame_support_test::Config; + +#[benchmarks] +mod benches { + use super::*; + + #[benchmark] + fn bench() {} +} + +fn main() {} diff --git a/frame/support/test/tests/benchmark_ui/empty_function.stderr b/frame/support/test/tests/benchmark_ui/empty_function.stderr new file mode 100644 index 000000000..69d753036 --- /dev/null +++ b/frame/support/test/tests/benchmark_ui/empty_function.stderr @@ -0,0 +1,5 @@ +error: No valid #[extrinsic_call] or #[block] annotation could be found in benchmark function body. + --> tests/benchmark_ui/empty_function.rs:10:13 + | +10 | fn bench() {} + | ^^ diff --git a/frame/support/test/tests/benchmark_ui/missing_call.rs b/frame/support/test/tests/benchmark_ui/missing_call.rs index bc04101dd..f39e74286 100644 --- a/frame/support/test/tests/benchmark_ui/missing_call.rs +++ b/frame/support/test/tests/benchmark_ui/missing_call.rs @@ -7,7 +7,9 @@ mod benches { use super::*; #[benchmark] - fn bench() {} + fn bench() { + assert_eq!(2 + 2, 4); + } } fn main() {} diff --git a/frame/support/test/tests/benchmark_ui/missing_call.stderr b/frame/support/test/tests/benchmark_ui/missing_call.stderr index 3b55f77d4..908d97043 100644 --- a/frame/support/test/tests/benchmark_ui/missing_call.stderr +++ b/frame/support/test/tests/benchmark_ui/missing_call.stderr @@ -1,5 +1,8 @@ error: No valid #[extrinsic_call] or #[block] annotation could be found in benchmark function body. --> tests/benchmark_ui/missing_call.rs:10:13 | -10 | fn bench() {} - | ^^ +10 | fn bench() { + | ________________^ +11 | | assert_eq!(2 + 2, 4); +12 | | } + | |_____^ diff --git a/frame/support/test/tests/benchmark_ui/pass/valid_complex_path_benchmark_result.rs b/frame/support/test/tests/benchmark_ui/pass/valid_complex_path_benchmark_result.rs new file mode 100644 index 000000000..4930aedd6 --- /dev/null +++ b/frame/support/test/tests/benchmark_ui/pass/valid_complex_path_benchmark_result.rs @@ -0,0 +1,17 @@ +use frame_benchmarking::v2::*; +#[allow(unused_imports)] +use frame_support_test::Config; + +#[benchmarks] +mod benches { + use super::*; + + #[benchmark] + fn bench() -> Result<(), frame_benchmarking::v2::BenchmarkError> { + #[block] + {} + Ok(()) + } +} + +fn main() {} diff --git a/frame/support/test/tests/benchmark_ui/pass/valid_no_last_stmt.rs b/frame/support/test/tests/benchmark_ui/pass/valid_no_last_stmt.rs new file mode 100644 index 000000000..ce09b437a --- /dev/null +++ b/frame/support/test/tests/benchmark_ui/pass/valid_no_last_stmt.rs @@ -0,0 +1,16 @@ +use frame_benchmarking::v2::*; +#[allow(unused_imports)] +use frame_support_test::Config; + +#[benchmarks] +mod benches { + use super::*; + + #[benchmark] + fn bench() { + #[block] + {} + } +} + +fn main() {} diff --git a/frame/support/test/tests/benchmark_ui/pass/valid_path_result_benchmark_error.rs b/frame/support/test/tests/benchmark_ui/pass/valid_path_result_benchmark_error.rs new file mode 100644 index 000000000..4930aedd6 --- /dev/null +++ b/frame/support/test/tests/benchmark_ui/pass/valid_path_result_benchmark_error.rs @@ -0,0 +1,17 @@ +use frame_benchmarking::v2::*; +#[allow(unused_imports)] +use frame_support_test::Config; + +#[benchmarks] +mod benches { + use super::*; + + #[benchmark] + fn bench() -> Result<(), frame_benchmarking::v2::BenchmarkError> { + #[block] + {} + Ok(()) + } +} + +fn main() {} diff --git a/frame/support/test/tests/benchmark_ui/pass/valid_result.rs b/frame/support/test/tests/benchmark_ui/pass/valid_result.rs new file mode 100644 index 000000000..33d71ece4 --- /dev/null +++ b/frame/support/test/tests/benchmark_ui/pass/valid_result.rs @@ -0,0 +1,18 @@ +use frame_benchmarking::v2::*; +use frame_support_test::Config; + +#[benchmarks] +mod benches { + use super::*; + + #[benchmark] + fn bench() -> Result<(), BenchmarkError> { + let a = 2 + 2; + #[block] + {} + assert_eq!(a, 4); + Ok(()) + } +} + +fn main() {} From 5a1d521f88ec338e46d60023ee048c72af1fe616 Mon Sep 17 00:00:00 2001 From: Koute Date: Thu, 23 Feb 2023 19:56:11 +0900 Subject: [PATCH 155/558] Bump `wasmtime` to 6.0.0 (#13429) * Bump `wasmtime` to 6.0.0 * Disable cranelift egraphs --- Cargo.lock | 99 +++++++++++-------- client/executor/wasmtime/Cargo.toml | 6 +- .../executor/wasmtime/src/instance_wrapper.rs | 3 +- client/executor/wasmtime/src/runtime.rs | 11 ++- primitives/wasm-interface/Cargo.toml | 2 +- 5 files changed, 71 insertions(+), 50 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 0f271f807..6326d1ffd 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1185,16 +1185,18 @@ checksum = "dcb25d077389e53838a8158c8e99174c5a9d902dee4904320db714f3c653ffba" [[package]] name = "cranelift-bforest" -version = "0.92.0" -source = "git+https://github.com/paritytech/wasmtime.git?branch=v5.0.0_lto_fix#8a02705ad378108e43abe23c538688adf73f3b71" +version = "0.93.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "91b18cf92869a6ae85cde3af4bc4beb6154efa8adef03b18db2ad413d5bce3a2" dependencies = [ "cranelift-entity", ] [[package]] name = "cranelift-codegen" -version = "0.92.0" -source = "git+https://github.com/paritytech/wasmtime.git?branch=v5.0.0_lto_fix#8a02705ad378108e43abe23c538688adf73f3b71" +version = "0.93.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "567d9f6e919bac076f39b902a072686eaf9e6d015baa34d10a61b85105b7af59" dependencies = [ "arrayvec 0.7.2", "bumpalo", @@ -1213,29 +1215,33 @@ dependencies = [ [[package]] name = "cranelift-codegen-meta" -version = "0.92.0" -source = "git+https://github.com/paritytech/wasmtime.git?branch=v5.0.0_lto_fix#8a02705ad378108e43abe23c538688adf73f3b71" +version = "0.93.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e72b2d5ec8917b2971fe83850187373d0a186db4748a7c23a5f48691b8d92bb" dependencies = [ "cranelift-codegen-shared", ] [[package]] name = "cranelift-codegen-shared" -version = "0.92.0" -source = "git+https://github.com/paritytech/wasmtime.git?branch=v5.0.0_lto_fix#8a02705ad378108e43abe23c538688adf73f3b71" +version = "0.93.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3461c0e0c2ebbeb92533aacb27e219289f60dc84134ef34fbf2d77c9eddf07ef" [[package]] name = "cranelift-entity" -version = "0.92.0" -source = "git+https://github.com/paritytech/wasmtime.git?branch=v5.0.0_lto_fix#8a02705ad378108e43abe23c538688adf73f3b71" +version = "0.93.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "af684f7f7b01427b1942c7102673322a51b9d6f261e9663dc5e5595786775531" dependencies = [ "serde", ] [[package]] name = "cranelift-frontend" -version = "0.92.0" -source = "git+https://github.com/paritytech/wasmtime.git?branch=v5.0.0_lto_fix#8a02705ad378108e43abe23c538688adf73f3b71" +version = "0.93.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7d361ed0373cf5f086b49c499aa72227b646a64f899f32e34312f97c0fadff75" dependencies = [ "cranelift-codegen", "log", @@ -1245,13 +1251,15 @@ dependencies = [ [[package]] name = "cranelift-isle" -version = "0.92.0" -source = "git+https://github.com/paritytech/wasmtime.git?branch=v5.0.0_lto_fix#8a02705ad378108e43abe23c538688adf73f3b71" +version = "0.93.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cef4f8f3984d772c199a48896d2fb766f96301bf71b371e03a2b99f4f3b7b931" [[package]] name = "cranelift-native" -version = "0.92.0" -source = "git+https://github.com/paritytech/wasmtime.git?branch=v5.0.0_lto_fix#8a02705ad378108e43abe23c538688adf73f3b71" +version = "0.93.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f98e4e99a353703475d5acb402b9c13482d41d8a4008b352559bd560afb90363" dependencies = [ "cranelift-codegen", "libc", @@ -1260,8 +1268,9 @@ dependencies = [ [[package]] name = "cranelift-wasm" -version = "0.92.0" -source = "git+https://github.com/paritytech/wasmtime.git?branch=v5.0.0_lto_fix#8a02705ad378108e43abe23c538688adf73f3b71" +version = "0.93.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1e3f4f0779a1b0f286a6ef19835d8665f88326e656a6d7d84fa9a39fa38ca32" dependencies = [ "cranelift-codegen", "cranelift-entity", @@ -11951,9 +11960,9 @@ dependencies = [ [[package]] name = "wasmparser" -version = "0.96.0" +version = "0.100.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "adde01ade41ab9a5d10ec8ed0bb954238cf8625b5cd5a13093d6de2ad9c2be1a" +checksum = "64b20236ab624147dfbb62cf12a19aaf66af0e41b8398838b66e997d07d269d4" dependencies = [ "indexmap", "url", @@ -11970,8 +11979,9 @@ dependencies = [ [[package]] name = "wasmtime" -version = "5.0.0" -source = "git+https://github.com/paritytech/wasmtime.git?branch=v5.0.0_lto_fix#8a02705ad378108e43abe23c538688adf73f3b71" +version = "6.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9010891d0b8e367c3be94ca35d7bc25c1de3240463bb1d61bcfc8c2233c4e0d0" dependencies = [ "anyhow", "bincode", @@ -11997,16 +12007,18 @@ dependencies = [ [[package]] name = "wasmtime-asm-macros" -version = "5.0.0" -source = "git+https://github.com/paritytech/wasmtime.git?branch=v5.0.0_lto_fix#8a02705ad378108e43abe23c538688adf73f3b71" +version = "6.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "65805c663eaa8257b910666f6d4b056b5c7329750da754ba5df54f3af7dbf35c" dependencies = [ "cfg-if", ] [[package]] name = "wasmtime-cache" -version = "5.0.0" -source = "git+https://github.com/paritytech/wasmtime.git?branch=v5.0.0_lto_fix#8a02705ad378108e43abe23c538688adf73f3b71" +version = "6.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2049ddfc1b10efc3c5591d0e84b9570ca50478f8818f3bfabb1a467918f53fb4" dependencies = [ "anyhow", "base64 0.13.1", @@ -12024,8 +12036,9 @@ dependencies = [ [[package]] name = "wasmtime-cranelift" -version = "5.0.0" -source = "git+https://github.com/paritytech/wasmtime.git?branch=v5.0.0_lto_fix#8a02705ad378108e43abe23c538688adf73f3b71" +version = "6.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f9065cad6a724fa838ec8497567e0b23acc26417bb2449f8d9d2021925c72f2" dependencies = [ "anyhow", "cranelift-codegen", @@ -12044,8 +12057,9 @@ dependencies = [ [[package]] name = "wasmtime-environ" -version = "5.0.0" -source = "git+https://github.com/paritytech/wasmtime.git?branch=v5.0.0_lto_fix#8a02705ad378108e43abe23c538688adf73f3b71" +version = "6.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4f964bb0b91fa021b8d1b488c62cc77b346c1dae6e3ebd010050b57c1f2ca657" dependencies = [ "anyhow", "cranelift-entity", @@ -12062,8 +12076,9 @@ dependencies = [ [[package]] name = "wasmtime-jit" -version = "5.0.0" -source = "git+https://github.com/paritytech/wasmtime.git?branch=v5.0.0_lto_fix#8a02705ad378108e43abe23c538688adf73f3b71" +version = "6.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b7a1d06f5d109539e0168fc74fa65e3948ac8dac3bb8cdbd08b62b36a0ae27b8" dependencies = [ "addr2line 0.17.0", "anyhow", @@ -12085,8 +12100,9 @@ dependencies = [ [[package]] name = "wasmtime-jit-debug" -version = "5.0.0" -source = "git+https://github.com/paritytech/wasmtime.git?branch=v5.0.0_lto_fix#8a02705ad378108e43abe23c538688adf73f3b71" +version = "6.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f76ef2e410329aaf8555ac6571d6fe07711be0646dcdf7ff3ab750a42ed2e583" dependencies = [ "object 0.29.0", "once_cell", @@ -12095,8 +12111,9 @@ dependencies = [ [[package]] name = "wasmtime-jit-icache-coherence" -version = "5.0.0" -source = "git+https://github.com/paritytech/wasmtime.git?branch=v5.0.0_lto_fix#8a02705ad378108e43abe23c538688adf73f3b71" +version = "6.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec1fd0f0dd79e7cc0f55b102e320d7c77ab76cd272008a8fd98e25b5777e2636" dependencies = [ "cfg-if", "libc", @@ -12105,8 +12122,9 @@ dependencies = [ [[package]] name = "wasmtime-runtime" -version = "5.0.0" -source = "git+https://github.com/paritytech/wasmtime.git?branch=v5.0.0_lto_fix#8a02705ad378108e43abe23c538688adf73f3b71" +version = "6.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "271aef9b4ca2e953a866293683f2db33cda46f6933c5e431e68d8373723d4ab6" dependencies = [ "anyhow", "cc", @@ -12128,8 +12146,9 @@ dependencies = [ [[package]] name = "wasmtime-types" -version = "5.0.0" -source = "git+https://github.com/paritytech/wasmtime.git?branch=v5.0.0_lto_fix#8a02705ad378108e43abe23c538688adf73f3b71" +version = "6.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b18144b0e45479a830ac9fcebfc71a16d90dc72d8ebd5679700eb3bfe974d7df" dependencies = [ "cranelift-entity", "serde", diff --git a/client/executor/wasmtime/Cargo.toml b/client/executor/wasmtime/Cargo.toml index e29a19527..ca7daa970 100644 --- a/client/executor/wasmtime/Cargo.toml +++ b/client/executor/wasmtime/Cargo.toml @@ -19,13 +19,13 @@ log = "0.4.17" # When bumping wasmtime do not forget to also bump rustix # to exactly the same version as used by wasmtime! -wasmtime = { version = "5.0.0", default-features = false, features = [ +wasmtime = { version = "6.0.0", default-features = false, features = [ "cache", "cranelift", "jitdump", "parallel-compilation", "pooling-allocator" -], git = "https://github.com/paritytech/wasmtime.git", branch = "v5.0.0_lto_fix" } +] } anyhow = "1.0.68" sc-allocator = { version = "4.1.0-dev", path = "../../allocator" } sc-executor-common = { version = "0.10.0-dev", path = "../common" } @@ -38,7 +38,7 @@ sp-wasm-interface = { version = "7.0.0", path = "../../../primitives/wasm-interf # By default rustix directly calls the appropriate syscalls completely bypassing libc; # this doesn't have any actual benefits for us besides making it harder to debug memory # problems (since then `mmap` etc. cannot be easily hooked into). -rustix = { version = "0.36.0", default-features = false, features = ["std", "mm", "fs", "param", "use-libc"] } +rustix = { version = "0.36.7", default-features = false, features = ["std", "mm", "fs", "param", "use-libc"] } once_cell = "1.12.0" [dev-dependencies] diff --git a/client/executor/wasmtime/src/instance_wrapper.rs b/client/executor/wasmtime/src/instance_wrapper.rs index f80c7b70d..2fb27180f 100644 --- a/client/executor/wasmtime/src/instance_wrapper.rs +++ b/client/executor/wasmtime/src/instance_wrapper.rs @@ -402,8 +402,7 @@ fn decommit_works() { let code = wat::parse_str("(module (memory (export \"memory\") 1 4))").unwrap(); let module = wasmtime::Module::new(&engine, code).unwrap(); let linker = wasmtime::Linker::new(&engine); - let mut store = create_store(&engine, None); - let instance_pre = linker.instantiate_pre(&mut store, &module).unwrap(); + let instance_pre = linker.instantiate_pre(&module).unwrap(); let mut wrapper = InstanceWrapper::new(&engine, &instance_pre, None).unwrap(); unsafe { *wrapper.memory.data_ptr(&wrapper.store) = 42 }; assert_eq!(unsafe { *wrapper.memory.data_ptr(&wrapper.store) }, 42); diff --git a/client/executor/wasmtime/src/runtime.rs b/client/executor/wasmtime/src/runtime.rs index 1728feeee..ce3f889fc 100644 --- a/client/executor/wasmtime/src/runtime.rs +++ b/client/executor/wasmtime/src/runtime.rs @@ -298,6 +298,11 @@ fn common_config(semantics: &Semantics) -> std::result::Result wasmtime::ProfilingStrategy::JitDump, None => wasmtime::ProfilingStrategy::None, @@ -368,7 +373,7 @@ fn common_config(semantics: &Semantics) -> std::result::Result(&mut linker, &module, config.allow_missing_func_imports)?; - let mut store = - crate::instance_wrapper::create_store(module.engine(), config.semantics.max_memory_size); let instance_pre = linker - .instantiate_pre(&mut store, &module) + .instantiate_pre(&module) .map_err(|e| WasmError::Other(format!("cannot preinstantiate module: {:#}", e)))?; Ok(WasmtimeRuntime { diff --git a/primitives/wasm-interface/Cargo.toml b/primitives/wasm-interface/Cargo.toml index 21b0bb432..65c4ae42f 100644 --- a/primitives/wasm-interface/Cargo.toml +++ b/primitives/wasm-interface/Cargo.toml @@ -18,7 +18,7 @@ codec = { package = "parity-scale-codec", version = "3.2.2", default-features = impl-trait-for-tuples = "0.2.2" log = { version = "0.4.17", optional = true } wasmi = { version = "0.13", optional = true } -wasmtime = { version = "5.0.0", default-features = false, optional = true, git = "https://github.com/paritytech/wasmtime.git", branch = "v5.0.0_lto_fix" } +wasmtime = { version = "6.0.0", default-features = false, optional = true } anyhow = { version = "1.0.68", optional = true } sp-std = { version = "5.0.0", default-features = false, path = "../std" } From 4be5dd2937ab9fffd4f13772b547450a95a59018 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gon=C3=A7alo=20Pestana?= Date: Thu, 23 Feb 2023 11:21:00 +0000 Subject: [PATCH 156/558] Abstracts elections-phragmen pallet to use NposSolver (#12588) * Abstracts elections-phragmen pallet to use NposSolver * Update frame/elections-phragmen/src/lib.rs Co-authored-by: Kian Paimani <5588131+kianenigma@users.noreply.github.com> * Update frame/elections-phragmen/src/lib.rs Co-authored-by: Kian Paimani <5588131+kianenigma@users.noreply.github.com> * changes the name of the pallet; adds changelog * update changelog * Adds weight testing * Adds log macro_rules * renames elections-phragment dir to elections * weights rename * fixes typo in cargo toml * pre/post solve weight scafolding * refactor do_post_election * refactors into pre and post election solve for independent benchmarking * deconstructs PreElectionResults struct * updates benchmarking pre and post election solve; mock weights * Update frame/elections/src/lib.rs Co-authored-by: Kian Paimani <5588131+kianenigma@users.noreply.github.com> * Update frame/elections/src/lib.rs Co-authored-by: Kian Paimani <5588131+kianenigma@users.noreply.github.com> * addresses PR comments * adds pre_solve and post_sove weights * Adds comments on election pallet id param name change * ".git/.scripts/bench-bot.sh" pallet dev pallet_elections * Finishes pre-post solve weights * Update frame/elections/src/lib.rs Co-authored-by: Kian Paimani <5588131+kianenigma@users.noreply.github.com> * Update frame/elections/src/lib.rs Co-authored-by: Kian Paimani <5588131+kianenigma@users.noreply.github.com> * Addresses PR comments: no panic in on_init path; nits * Fixes node build * Implements approval voting to use as a `NposSolver` (#13367) * Implements the approval voting methods in sp_npos_elections * fmt * remove unecessary file * comment clarification * re-run weights * fix typo * updates MaxVoters in tests for integrity_tests to pass * Refactors election provider support benchmarks outside its own crate (#13431) * Refactors election provider support benchmarks outside its own crate --------- Co-authored-by: command-bot <> --------- Co-authored-by: Kian Paimani <5588131+kianenigma@users.noreply.github.com> Co-authored-by: parity-processbot <> Co-authored-by: Ross Bulat --- Cargo.lock | 265 ++++----- Cargo.toml | 3 +- bin/node/runtime/Cargo.toml | 12 +- bin/node/runtime/src/lib.rs | 30 +- .../election-provider-multi-phase/Cargo.toml | 2 - frame/election-provider-support/Cargo.toml | 8 +- .../benchmarking/Cargo.toml | 37 -- .../src/lib.rs => src/benchmarking.rs} | 18 +- frame/election-provider-support/src/lib.rs | 26 + .../election-provider-support/src/onchain.rs | 30 +- .../election-provider-support/src/weights.rs | 147 +++-- frame/elections-phragmen/src/weights.rs | 559 ------------------ .../CHANGELOG.md | 11 + .../Cargo.toml | 5 +- .../README.md | 6 +- .../src/benchmarking.rs | 57 +- .../src/lib.rs | 482 +++++++++------ .../src/migrations/mod.rs | 0 .../src/migrations/v3.rs | 0 .../src/migrations/v4.rs | 0 .../src/migrations/v5.rs | 0 frame/elections/src/weights.rs | 397 +++++++++++++ .../npos-elections/src/approval_voting.rs | 79 +++ primitives/npos-elections/src/lib.rs | 4 + primitives/npos-elections/src/phragmen.rs | 2 +- primitives/npos-elections/src/tests.rs | 49 +- scripts/run_all_benchmarks.sh | 2 - utils/frame/remote-externalities/Cargo.toml | 2 +- 28 files changed, 1206 insertions(+), 1027 deletions(-) delete mode 100644 frame/election-provider-support/benchmarking/Cargo.toml rename frame/election-provider-support/{benchmarking/src/lib.rs => src/benchmarking.rs} (86%) delete mode 100644 frame/elections-phragmen/src/weights.rs rename frame/{elections-phragmen => elections}/CHANGELOG.md (80%) rename frame/{elections-phragmen => elections}/Cargo.toml (90%) rename frame/{elections-phragmen => elections}/README.md (94%) rename frame/{elections-phragmen => elections}/src/benchmarking.rs (85%) rename frame/{elections-phragmen => elections}/src/lib.rs (90%) rename frame/{elections-phragmen => elections}/src/migrations/mod.rs (100%) rename frame/{elections-phragmen => elections}/src/migrations/v3.rs (100%) rename frame/{elections-phragmen => elections}/src/migrations/v4.rs (100%) rename frame/{elections-phragmen => elections}/src/migrations/v5.rs (100%) create mode 100644 frame/elections/src/weights.rs create mode 100644 primitives/npos-elections/src/approval_voting.rs diff --git a/Cargo.lock b/Cargo.lock index 6326d1ffd..e485a6512 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -27,7 +27,7 @@ version = "0.19.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a76fd60b23679b7d19bd066031410fb7e458ccc5e958eb5c325888ce4baedc97" dependencies = [ - "gimli 0.27.1", + "gimli 0.27.2", ] [[package]] @@ -246,7 +246,7 @@ dependencies = [ "num-traits", "rusticata-macros", "thiserror", - "time 0.3.17", + "time 0.3.19", ] [[package]] @@ -262,7 +262,7 @@ dependencies = [ "num-traits", "rusticata-macros", "thiserror", - "time 0.3.17", + "time 0.3.19", ] [[package]] @@ -358,19 +358,20 @@ dependencies = [ [[package]] name = "async-stream" -version = "0.3.3" +version = "0.3.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dad5c83079eae9969be7fadefe640a1c566901f05ff91ab221de4b6f68d9507e" +checksum = "ad445822218ce64be7a341abfb0b1ea43b5c23aa83902542a4542e78309d8e5e" dependencies = [ "async-stream-impl", "futures-core", + "pin-project-lite 0.2.9", ] [[package]] name = "async-stream-impl" -version = "0.3.3" +version = "0.3.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "10f203db73a71dfa2fb6dd22763990fa26f3d2625a6da2da900d23b87d26be27" +checksum = "e4655ae1a7b0cdf149156f780c5bf3f1352bc53cbd9e0a361a7ef7b22947e965" dependencies = [ "proc-macro2", "quote", @@ -579,9 +580,9 @@ dependencies = [ [[package]] name = "bindgen" -version = "0.60.1" +version = "0.64.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "062dddbc1ba4aca46de6338e2bf87771414c335f7b2f2036e8f3e9befebf88e6" +checksum = "c4243e6031260db77ede97ad86c27e501d646a27ab57b59a574f725d98ab1fb4" dependencies = [ "bitflags", "cexpr", @@ -594,6 +595,7 @@ dependencies = [ "regex", "rustc-hash", "shlex", + "syn", ] [[package]] @@ -625,24 +627,24 @@ dependencies = [ [[package]] name = "blake2b_simd" -version = "1.0.0" +version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "72936ee4afc7f8f736d1c38383b56480b5497b4617b4a77bdbf1d2ababc76127" +checksum = "3c2f0dc9a68c6317d884f97cc36cf5a3d20ba14ce404227df55e1af708ab04bc" dependencies = [ "arrayref", "arrayvec 0.7.2", - "constant_time_eq 0.1.5", + "constant_time_eq", ] [[package]] name = "blake2s_simd" -version = "1.0.0" +version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "db539cc2b5f6003621f1cd9ef92d7ded8ea5232c7de0f9faa2de251cd98730d4" +checksum = "6637f448b9e61dfadbdcbae9a885fadee1f3eaffb1f8d3c1965d3ade8bdfd44f" dependencies = [ "arrayref", "arrayvec 0.7.2", - "constant_time_eq 0.1.5", + "constant_time_eq", ] [[package]] @@ -655,7 +657,7 @@ dependencies = [ "arrayvec 0.7.2", "cc", "cfg-if", - "constant_time_eq 0.2.4", + "constant_time_eq", ] [[package]] @@ -715,9 +717,9 @@ checksum = "8d696c370c750c948ada61c69a0ee2cbbb9c50b1019ddb86d9317157a99c2cae" [[package]] name = "bounded-collections" -version = "0.1.4" +version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "de2aff4807e40f478132150d80b031f2461d88f061851afcab537d7600c24120" +checksum = "a071c348a5ef6da1d3a87166b408170b46002382b1dda83992b5c2208cefb370" dependencies = [ "log", "parity-scale-codec", @@ -733,9 +735,9 @@ checksum = "771fe0050b883fcc3ea2359b1a96bcfbc090b7116eae7c3c512c7a083fdf23d3" [[package]] name = "bstr" -version = "1.2.0" +version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b7f0778972c64420fdedc63f09919c8a88bda7b25135357fd25a5d9f3257e832" +checksum = "5ffdb39cb703212f3c11973452c2861b972f757b021158f3516ba10f2fa8b2c1" dependencies = [ "memchr", "once_cell", @@ -917,7 +919,7 @@ name = "chain-spec-builder" version = "2.0.0" dependencies = [ "ansi_term", - "clap 4.1.4", + "clap 4.1.6", "node-cli", "rand 0.8.5", "sc-chain-spec", @@ -1010,9 +1012,9 @@ dependencies = [ [[package]] name = "clang-sys" -version = "1.4.0" +version = "1.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fa2e27ae6ab525c3d369ded447057bca5438d86dc3a68f6faafb8269ba82ebf3" +checksum = "77ed9a53e5d4d9c573ae844bfac6872b159cb1d1585a83b29e7a64b7eef7332a" dependencies = [ "glob", "libc", @@ -1033,9 +1035,9 @@ dependencies = [ [[package]] name = "clap" -version = "4.1.4" +version = "4.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f13b9c79b5d1dd500d20ef541215a6423c75829ef43117e1b4d17fd8af0b5d76" +checksum = "ec0b0588d44d4d63a87dbd75c136c166bbfd9a86a31cb89e09906521c7d3f5e3" dependencies = [ "bitflags", "clap_derive", @@ -1048,11 +1050,11 @@ dependencies = [ [[package]] name = "clap_complete" -version = "4.1.1" +version = "4.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3d6540eedc41f8a5a76cf3d8d458057dcdf817be4158a55b5f861f7a5483de75" +checksum = "bd125be87bf4c255ebc50de0b7f4d2a6201e8ac3dc86e39c0ad081dc5e7236fe" dependencies = [ - "clap 4.1.4", + "clap 4.1.6", ] [[package]] @@ -1122,12 +1124,6 @@ version = "0.9.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cec318a675afcb6a1ea1d4340e2d377e56e47c266f28043ceccbf4412ddfdd3b" -[[package]] -name = "constant_time_eq" -version = "0.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "245097e9a4535ee1e3e3931fcfcd55a796a44c643e8596ff6566d68f09b87bbc" - [[package]] name = "constant_time_eq" version = "0.2.4" @@ -1515,9 +1511,9 @@ dependencies = [ [[package]] name = "cxx" -version = "1.0.89" +version = "1.0.91" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bc831ee6a32dd495436e317595e639a587aa9907bef96fe6e6abc290ab6204e9" +checksum = "86d3488e7665a7a483b57e25bdd90d0aeb2bc7608c8d0346acf2ad3f1caf1d62" dependencies = [ "cc", "cxxbridge-flags", @@ -1527,9 +1523,9 @@ dependencies = [ [[package]] name = "cxx-build" -version = "1.0.89" +version = "1.0.91" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "94331d54f1b1a8895cd81049f7eaaaef9d05a7dcb4d1fd08bf3ff0806246789d" +checksum = "48fcaf066a053a41a81dfb14d57d99738b767febb8b735c3016e469fac5da690" dependencies = [ "cc", "codespan-reporting", @@ -1542,15 +1538,15 @@ dependencies = [ [[package]] name = "cxxbridge-flags" -version = "1.0.89" +version = "1.0.91" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "48dcd35ba14ca9b40d6e4b4b39961f23d835dbb8eed74565ded361d93e1feb8a" +checksum = "a2ef98b8b717a829ca5603af80e1f9e2e48013ab227b68ef37872ef84ee479bf" [[package]] name = "cxxbridge-macro" -version = "1.0.89" +version = "1.0.91" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "81bbeb29798b407ccd82a3324ade1a7286e0d29851475990b612670f6f5124d2" +checksum = "086c685979a698443656e5cf7856c95c642295a38599f12fb1ff76fb28d19892" dependencies = [ "proc-macro2", "quote", @@ -2051,9 +2047,9 @@ checksum = "4443176a9f2c162692bd3d352d745ef9413eec5782a80d8fd6f8a1ac692a07f7" [[package]] name = "fastrand" -version = "1.8.0" +version = "1.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a7a407cfaa3385c4ae6b23e84623d48c2798d06e3e6a1878f7f59f17b3f86499" +checksum = "e51093e27b0797c359783294ca4f0a911c270184cb10f85783b118614a1501be" dependencies = [ "instant", ] @@ -2095,14 +2091,14 @@ dependencies = [ [[package]] name = "filetime" -version = "0.2.19" +version = "0.2.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4e884668cd0c7480504233e951174ddc3b382f7c2666e3b7310b5c4e7b0c37f9" +checksum = "8a3de6e8d11b22ff9edc6d916f890800597d60f8b2da1caf2955c274638d6412" dependencies = [ "cfg-if", "libc", "redox_syscall", - "windows-sys 0.42.0", + "windows-sys 0.45.0", ] [[package]] @@ -2222,7 +2218,7 @@ dependencies = [ "Inflector", "array-bytes", "chrono", - "clap 4.1.4", + "clap 4.1.6", "comfy-table", "frame-benchmarking", "frame-support", @@ -2295,6 +2291,7 @@ dependencies = [ name = "frame-election-provider-support" version = "4.0.0-dev" dependencies = [ + "frame-benchmarking", "frame-election-provider-solution-type", "frame-support", "frame-system", @@ -2313,7 +2310,7 @@ dependencies = [ name = "frame-election-solution-type-fuzzer" version = "2.0.0-alpha.5" dependencies = [ - "clap 4.1.4", + "clap 4.1.6", "frame-election-provider-solution-type", "frame-election-provider-support", "frame-support", @@ -2366,7 +2363,7 @@ dependencies = [ "frame-support", "futures", "log", - "pallet-elections-phragmen", + "pallet-elections", "parity-scale-codec", "serde", "sp-core", @@ -2796,9 +2793,9 @@ dependencies = [ [[package]] name = "gimli" -version = "0.27.1" +version = "0.27.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "221996f774192f0f718773def8201c4ae31f02616a54ccfc2d358bb0e5cefdec" +checksum = "ad0a93d233ebf96623465aad4046a8d3aa4da22d4f4beba5388838c8a434bbb4" [[package]] name = "git2" @@ -2941,9 +2938,9 @@ dependencies = [ [[package]] name = "hermit-abi" -version = "0.3.0" +version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "856b5cb0902c2b6d65d5fd97dfa30f9b70c7538e770b98eab5ed52d8db923e01" +checksum = "fed44880c466736ef9a5c5b5facefb5ed0785676d0c02d612db14e54f0d84286" [[package]] name = "hex" @@ -3035,9 +3032,9 @@ dependencies = [ [[package]] name = "http" -version = "0.2.8" +version = "0.2.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "75f43d41e26995c17e71ee126451dd3941010b0514a81a9d11f3b341debc2399" +checksum = "bd6effc99afb63425aff9b05836f029929e345a6148a14b7ecd5ab67af944482" dependencies = [ "bytes", "fnv", @@ -3321,7 +3318,7 @@ version = "0.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "22e18b0a45d56fe973d6db23972bf5bc46f988a4a2385deac9cc29572f09daef" dependencies = [ - "hermit-abi 0.3.0", + "hermit-abi 0.3.1", "io-lifetimes", "rustix", "windows-sys 0.45.0", @@ -3547,8 +3544,7 @@ dependencies = [ "pallet-conviction-voting", "pallet-democracy", "pallet-election-provider-multi-phase", - "pallet-election-provider-support-benchmarking", - "pallet-elections-phragmen", + "pallet-elections", "pallet-fast-unstake", "pallet-glutton", "pallet-grandpa", @@ -4113,9 +4109,9 @@ dependencies = [ [[package]] name = "librocksdb-sys" -version = "0.8.0+7.4.4" +version = "0.8.3+7.4.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "611804e4666a25136fcc5f8cf425ab4d26c7f74ea245ffe92ea23b85b6420b5d" +checksum = "557b255ff04123fcc176162f56ed0c9cd42d8f357cf55b3fabeb60f7413741b3" dependencies = [ "bindgen", "bzip2-sys", @@ -4374,9 +4370,9 @@ dependencies = [ [[package]] name = "memmap2" -version = "0.5.8" +version = "0.5.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4b182332558b18d807c4ce1ca8ca983b34c3ee32765e47b3f0f69b90355cc1dc" +checksum = "2af2c65375e552a67fe3829ca63e8a7c27a378a62824594f43b2851d682b5ec2" dependencies = [ "libc", ] @@ -4444,14 +4440,14 @@ dependencies = [ [[package]] name = "mio" -version = "0.8.5" +version = "0.8.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e5d732bc30207a6423068df043e3d02e0735b155ad7ce1a6f76fe2baa5b158de" +checksum = "5b9d9a46eff5b4ff64b45a9e316a6d1e0bc719ef429cbec4dc630684212bfdf9" dependencies = [ "libc", "log", "wasi 0.11.0+wasi-snapshot-preview1", - "windows-sys 0.42.0", + "windows-sys 0.45.0", ] [[package]] @@ -4733,7 +4729,7 @@ name = "node-bench" version = "0.9.0-dev" dependencies = [ "array-bytes", - "clap 4.1.4", + "clap 4.1.6", "derive_more", "fs_extra", "futures", @@ -4770,7 +4766,7 @@ version = "3.0.0-dev" dependencies = [ "array-bytes", "assert_cmd", - "clap 4.1.4", + "clap 4.1.6", "clap_complete", "criterion", "frame-benchmarking-cli", @@ -4890,7 +4886,7 @@ dependencies = [ name = "node-inspect" version = "0.9.0-dev" dependencies = [ - "clap 4.1.4", + "clap 4.1.6", "parity-scale-codec", "sc-cli", "sc-client-api", @@ -4949,7 +4945,7 @@ dependencies = [ name = "node-runtime-generate-bags" version = "3.0.0" dependencies = [ - "clap 4.1.4", + "clap 4.1.6", "generate-bags", "kitchensink-runtime", ] @@ -4958,7 +4954,7 @@ dependencies = [ name = "node-template" version = "4.0.0-dev" dependencies = [ - "clap 4.1.4", + "clap 4.1.6", "frame-benchmarking", "frame-benchmarking-cli", "frame-system", @@ -5214,9 +5210,9 @@ dependencies = [ [[package]] name = "once_cell" -version = "1.17.0" +version = "1.17.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6f61fba1741ea2b3d6a1e3178721804bb716a68a6aeba1149b5d52e3d464ea66" +checksum = "b7e5500299e16ebb147ae15a00a942af264cf3688f47923b8fc2cd5858f23ad3" [[package]] name = "oorandom" @@ -5722,7 +5718,6 @@ dependencies = [ "frame-system", "log", "pallet-balances", - "pallet-election-provider-support-benchmarking", "parity-scale-codec", "parking_lot 0.12.1", "rand 0.8.5", @@ -5738,22 +5733,11 @@ dependencies = [ ] [[package]] -name = "pallet-election-provider-support-benchmarking" -version = "4.0.0-dev" -dependencies = [ - "frame-benchmarking", - "frame-election-provider-support", - "frame-system", - "parity-scale-codec", - "sp-npos-elections", - "sp-runtime", -] - -[[package]] -name = "pallet-elections-phragmen" +name = "pallet-elections" version = "5.0.0-dev" dependencies = [ "frame-benchmarking", + "frame-election-provider-support", "frame-support", "frame-system", "log", @@ -6793,9 +6777,9 @@ dependencies = [ [[package]] name = "parity-scale-codec" -version = "3.3.0" +version = "3.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c3840933452adf7b3b9145e27086a5a3376c619dca1a21b1e5a5af0d54979bed" +checksum = "637935964ff85a605d114591d4d2c13c5d1ba2806dae97cea6bf180238a749ac" dependencies = [ "arrayvec 0.7.2", "bitvec", @@ -6940,9 +6924,9 @@ checksum = "478c572c3d73181ff3c2539045f6eb99e5491218eae919370993b890cdbdd98e" [[package]] name = "pest" -version = "2.5.4" +version = "2.5.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4ab62d2fa33726dbe6321cc97ef96d8cde531e3eeaf858a058de53a8a6d40d8f" +checksum = "028accff104c4e513bad663bbcd2ad7cfd5304144404c31ed0a77ac103d00660" dependencies = [ "thiserror", "ucd-trie", @@ -6950,9 +6934,9 @@ dependencies = [ [[package]] name = "pest_derive" -version = "2.5.4" +version = "2.5.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8bf026e2d0581559db66d837fe5242320f525d85c76283c61f4d51a1238d65ea" +checksum = "2ac3922aac69a40733080f53c1ce7f91dcf57e1a5f6c52f421fadec7fbdc4b69" dependencies = [ "pest", "pest_generator", @@ -6960,9 +6944,9 @@ dependencies = [ [[package]] name = "pest_generator" -version = "2.5.4" +version = "2.5.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2b27bd18aa01d91c8ed2b61ea23406a676b42d82609c6e2581fba42f0c15f17f" +checksum = "d06646e185566b5961b4058dd107e0a7f56e77c3f484549fb119867773c0f202" dependencies = [ "pest", "pest_meta", @@ -6973,9 +6957,9 @@ dependencies = [ [[package]] name = "pest_meta" -version = "2.5.4" +version = "2.5.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9f02b677c1859756359fc9983c2e56a0237f18624a3789528804406b7e915e5d" +checksum = "e6f60b2ba541577e2a0c307c8f39d1439108120eb7903adeb6497fa880c59616" dependencies = [ "once_cell", "pest", @@ -7547,7 +7531,7 @@ checksum = "6413f3de1edee53342e6138e75b56d32e7bc6e332b3bd62d497b1929d4cfbcdd" dependencies = [ "pem", "ring", - "time 0.3.17", + "time 0.3.19", "x509-parser 0.13.2", "yasna", ] @@ -7560,7 +7544,7 @@ checksum = "ffbe84efe2f38dea12e9bfc1f65377fdf03e53a18cb3b995faedf7934c7e785b" dependencies = [ "pem", "ring", - "time 0.3.17", + "time 0.3.19", "yasna", ] @@ -8034,7 +8018,7 @@ version = "0.10.0-dev" dependencies = [ "array-bytes", "chrono", - "clap 4.1.4", + "clap 4.1.6", "fdlimit", "futures", "futures-timer", @@ -9069,7 +9053,7 @@ dependencies = [ name = "sc-storage-monitor" version = "0.1.0" dependencies = [ - "clap 4.1.4", + "clap 4.1.6", "futures", "log", "nix 0.26.2", @@ -9465,9 +9449,9 @@ dependencies = [ [[package]] name = "serde_json" -version = "1.0.92" +version = "1.0.93" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7434af0dc1cbd59268aa98b4c22c131c0584d2232f6fb166efb993e2832e896a" +checksum = "cad406b69c91885b5107daf2c29572f6c8cdb3c66826821e286c533490c0bc76" dependencies = [ "itoa", "ryu", @@ -9550,9 +9534,9 @@ checksum = "43b2853a4d09f215c24cc5489c992ce46052d359b5109343cbafbf26bc62f8a3" [[package]] name = "signal-hook-registry" -version = "1.4.0" +version = "1.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e51e73328dc4ac0c7ccbda3a494dfa03df1de2f46018127f60c693f2648455b0" +checksum = "d8229b473baa5980ac72ef434c4415e70c4b5e71b423043adb4ba059f89c99a1" dependencies = [ "libc", ] @@ -9582,9 +9566,9 @@ dependencies = [ [[package]] name = "slab" -version = "0.4.7" +version = "0.4.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4614a76b2a8be0058caa9dbbaf66d988527d86d003c11a94fbd335d7661edcef" +checksum = "6528351c9bc8ab22353f9d776db39a20288e8d6c37ef8cfe3317cf875eecfc2d" dependencies = [ "autocfg", ] @@ -10128,7 +10112,7 @@ dependencies = [ name = "sp-npos-elections-fuzzer" version = "2.0.0-alpha.5" dependencies = [ - "clap 4.1.4", + "clap 4.1.6", "honggfuzz", "parity-scale-codec", "rand 0.8.5", @@ -10485,9 +10469,9 @@ checksum = "6e63cff320ae2c57904679ba7cb63280a3dc4613885beafb148ee7bf9aa9042d" [[package]] name = "spin" -version = "0.9.4" +version = "0.9.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7f6002a767bff9e83f8eeecf883ecb8011875a21ae8da43bffb817a57e78cc09" +checksum = "7dccf47db1b41fa1573ed27ccf5e08e3ca771cb994f776668c5ebda893b248fc" [[package]] name = "spki" @@ -10605,7 +10589,7 @@ dependencies = [ name = "subkey" version = "3.0.0" dependencies = [ - "clap 4.1.4", + "clap 4.1.6", "sc-cli", ] @@ -10633,7 +10617,7 @@ dependencies = [ name = "substrate-frame-cli" version = "4.0.0-dev" dependencies = [ - "clap 4.1.4", + "clap 4.1.6", "frame-support", "frame-system", "sc-cli", @@ -10943,9 +10927,9 @@ checksum = "55937e1799185b12863d447f42597ed69d9928686b8d88a1df17376a097d8369" [[package]] name = "target-lexicon" -version = "0.12.5" +version = "0.12.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9410d0f6853b1d94f0e519fb95df60f29d2c1eff2d921ffdf01a4c8a3b54f12d" +checksum = "8ae9980cab1db3fceee2f6c6f643d5d8de2997c58ee8d25fb0cc8a9e9e7348e5" [[package]] name = "tempfile" @@ -11010,10 +10994,11 @@ checksum = "3bf63baf9f5039dadc247375c29eb13706706cfde997d0330d05aa63a77d8820" [[package]] name = "thread_local" -version = "1.1.4" +version = "1.1.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5516c27b78311c50bf42c071425c560ac799b11c30b31f87e3081965fe5e0180" +checksum = "3fdd6f064ccff2d6567adcb3873ca630700f00b5ad3f060c25b5dcfd9a4ce152" dependencies = [ + "cfg-if", "once_cell", ] @@ -11049,9 +11034,9 @@ dependencies = [ [[package]] name = "time" -version = "0.3.17" +version = "0.3.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a561bf4617eebd33bca6434b988f39ed798e527f51a1e797d0ee4f61c0a38376" +checksum = "53250a3b3fed8ff8fd988587d8925d26a83ac3845d9e03b220b37f34c2b8d6c2" dependencies = [ "itoa", "serde", @@ -11067,9 +11052,9 @@ checksum = "2e153e1f1acaef8acc537e68b44906d2db6436e2b35ac2c6b42640fff91f00fd" [[package]] name = "time-macros" -version = "0.2.6" +version = "0.2.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d967f99f534ca7e495c575c62638eebc2898a8c84c119b89e250477bc4ba16b2" +checksum = "a460aeb8de6dcb0f381e1ee05f1cd56fcf5a5f6eb8187ff3d8f0b11078d38b7c" dependencies = [ "time-core", ] @@ -11171,9 +11156,9 @@ dependencies = [ [[package]] name = "tokio-stream" -version = "0.1.11" +version = "0.1.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d660770404473ccd7bc9f8b28494a811bc18542b915c0855c51e8f419d5223ce" +checksum = "8fb52b74f05dbf495a8fba459fdc331812b96aa086d9eb78101fa0d4569c3313" dependencies = [ "futures-core", "pin-project-lite 0.2.9", @@ -11196,9 +11181,9 @@ dependencies = [ [[package]] name = "tokio-util" -version = "0.7.4" +version = "0.7.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0bb2e075f03b3d66d8d8785356224ba688d2906a371015e225beeb65ca92c740" +checksum = "5427d89453009325de0d8f342c9490009f76e999cb7672d77e46267448f7e6b2" dependencies = [ "bytes", "futures-core", @@ -11470,7 +11455,7 @@ name = "try-runtime-cli" version = "0.10.0-dev" dependencies = [ "async-trait", - "clap 4.1.4", + "clap 4.1.6", "frame-remote-externalities", "frame-try-runtime", "hex", @@ -11815,9 +11800,9 @@ checksum = "0046fef7e28c3804e5e38bfa31ea2a0f73905319b677e57ebe37e49358989b5d" [[package]] name = "wasm-encoder" -version = "0.22.1" +version = "0.24.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9a584273ccc2d9311f1dd19dc3fb26054661fa3e373d53ede5d1144ba07a9acd" +checksum = "704553b4d614a47080b4a457a976b3c16174b19ce95b931b847561b590dd09ba" dependencies = [ "leb128", ] @@ -11913,7 +11898,7 @@ version = "0.20.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "01bf50edb2ea9d922aa75a7bf3c15e26a6c9e2d18c56e862b49737a582901729" dependencies = [ - "spin 0.9.4", + "spin 0.9.5", "wasmi_arena", "wasmi_core 0.5.0", "wasmparser-nostd", @@ -12158,9 +12143,9 @@ dependencies = [ [[package]] name = "wast" -version = "52.0.3" +version = "54.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "15942180f265280eede7bc38b239e9770031d1821c02d905284216c645316430" +checksum = "f0d3df4a63b10958fe98ab9d7e9a57a7bc900209d2b4edd10535bfb0703e6516" dependencies = [ "leb128", "memchr", @@ -12170,9 +12155,9 @@ dependencies = [ [[package]] name = "wat" -version = "1.0.57" +version = "1.0.59" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "37212100d4cbe6f0f6ff6e707f1e5a5b5b675f0451231ed9e4235e234e127ed3" +checksum = "3e9a7c7d177696d0548178c36e377d49eba54170e885801d4270e2d44e82ac84" dependencies = [ "wast", ] @@ -12242,7 +12227,7 @@ dependencies = [ "sha2 0.10.6", "stun", "thiserror", - "time 0.3.17", + "time 0.3.19", "tokio", "turn", "url", @@ -12315,9 +12300,9 @@ dependencies = [ [[package]] name = "webrtc-ice" -version = "0.9.0" +version = "0.9.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "494483fbb2f5492620871fdc78b084aed8807377f6e3fe88b2e49f0a9c9c41d7" +checksum = "465a03cc11e9a7d7b4f9f99870558fe37a102b65b93f8045392fef7c67b39e80" dependencies = [ "arc-swap", "async-trait", @@ -12675,7 +12660,7 @@ dependencies = [ "ring", "rusticata-macros", "thiserror", - "time 0.3.17", + "time 0.3.19", ] [[package]] @@ -12693,7 +12678,7 @@ dependencies = [ "oid-registry 0.6.1", "rusticata-macros", "thiserror", - "time 0.3.17", + "time 0.3.19", ] [[package]] @@ -12722,7 +12707,7 @@ version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "aed2e7a52e3744ab4d0c05c20aa065258e84c49fd4226f5191b2ed29712710b4" dependencies = [ - "time 0.3.17", + "time 0.3.19", ] [[package]] @@ -12767,9 +12752,9 @@ dependencies = [ [[package]] name = "zstd-sys" -version = "2.0.6+zstd.1.5.2" +version = "2.0.7+zstd.1.5.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "68a3f9792c0c3dc6c165840a75f47ae1f4da402c2d006881129579f6597e801b" +checksum = "94509c3ba2fe55294d752b79842c530ccfab760192521df74a081a78d2b3c7f5" dependencies = [ "cc", "libc", diff --git a/Cargo.toml b/Cargo.toml index 89a52fd16..2ae924bf6 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -97,10 +97,9 @@ members = [ "frame/democracy", "frame/fast-unstake", "frame/try-runtime", - "frame/elections-phragmen", + "frame/elections", "frame/election-provider-multi-phase", "frame/election-provider-support", - "frame/election-provider-support/benchmarking", "frame/election-provider-support/solution-type", "frame/election-provider-support/solution-type/fuzzer", "frame/examples/basic", diff --git a/bin/node/runtime/Cargo.toml b/bin/node/runtime/Cargo.toml index 37a99414b..d7b42efd4 100644 --- a/bin/node/runtime/Cargo.toml +++ b/bin/node/runtime/Cargo.toml @@ -66,8 +66,7 @@ pallet-contracts-primitives = { version = "7.0.0", default-features = false, pat pallet-conviction-voting = { version = "4.0.0-dev", default-features = false, path = "../../../frame/conviction-voting" } pallet-democracy = { version = "4.0.0-dev", default-features = false, path = "../../../frame/democracy" } pallet-election-provider-multi-phase = { version = "4.0.0-dev", default-features = false, path = "../../../frame/election-provider-multi-phase" } -pallet-election-provider-support-benchmarking = { version = "4.0.0-dev", default-features = false, path = "../../../frame/election-provider-support/benchmarking", optional = true } -pallet-elections-phragmen = { version = "5.0.0-dev", default-features = false, path = "../../../frame/elections-phragmen" } +pallet-elections = { version = "5.0.0-dev", default-features = false, path = "../../../frame/elections" } pallet-fast-unstake = { version = "4.0.0-dev", default-features = false, path = "../../../frame/fast-unstake" } pallet-nis = { version = "4.0.0-dev", default-features = false, path = "../../../frame/nis" } pallet-grandpa = { version = "4.0.0-dev", default-features = false, path = "../../../frame/grandpa" } @@ -124,7 +123,6 @@ with-tracing = ["frame-executive/with-tracing"] std = [ "pallet-whitelist/std", "pallet-offences-benchmarking?/std", - "pallet-election-provider-support-benchmarking?/std", "pallet-asset-tx-payment/std", "frame-system-benchmarking?/std", "frame-election-provider-support/std", @@ -145,7 +143,7 @@ std = [ "pallet-contracts-primitives/std", "pallet-conviction-voting/std", "pallet-democracy/std", - "pallet-elections-phragmen/std", + "pallet-elections/std", "pallet-fast-unstake/std", "frame-executive/std", "pallet-nis/std", @@ -218,6 +216,7 @@ runtime-benchmarks = [ "frame-benchmarking-pallet-pov/runtime-benchmarks", "frame-support/runtime-benchmarks", "frame-system/runtime-benchmarks", + "frame-election-provider-support/runtime-benchmarks", "sp-runtime/runtime-benchmarks", "pallet-alliance/runtime-benchmarks", "pallet-assets/runtime-benchmarks", @@ -231,8 +230,7 @@ runtime-benchmarks = [ "pallet-conviction-voting/runtime-benchmarks", "pallet-democracy/runtime-benchmarks", "pallet-election-provider-multi-phase/runtime-benchmarks", - "pallet-election-provider-support-benchmarking/runtime-benchmarks", - "pallet-elections-phragmen/runtime-benchmarks", + "pallet-elections/runtime-benchmarks", "pallet-fast-unstake/runtime-benchmarks", "pallet-nis/runtime-benchmarks", "pallet-grandpa/runtime-benchmarks", @@ -289,7 +287,7 @@ try-runtime = [ "pallet-conviction-voting/try-runtime", "pallet-democracy/try-runtime", "pallet-election-provider-multi-phase/try-runtime", - "pallet-elections-phragmen/try-runtime", + "pallet-elections/try-runtime", "pallet-fast-unstake/try-runtime", "pallet-nis/try-runtime", "pallet-grandpa/try-runtime", diff --git a/bin/node/runtime/src/lib.rs b/bin/node/runtime/src/lib.rs index e02792b95..1ed43c211 100644 --- a/bin/node/runtime/src/lib.rs +++ b/bin/node/runtime/src/lib.rs @@ -24,7 +24,8 @@ use codec::{Decode, Encode, MaxEncodedLen}; use frame_election_provider_support::{ - onchain, BalancingConfig, ElectionDataProvider, SequentialPhragmen, VoteWeight, + onchain, weights::SubstrateWeight, ApprovalVoting, BalancingConfig, ElectionDataProvider, + SequentialPhragmen, VoteWeight, }; use frame_support::{ construct_runtime, @@ -1010,18 +1011,21 @@ parameter_types! { pub const TermDuration: BlockNumber = 7 * DAYS; pub const DesiredMembers: u32 = 13; pub const DesiredRunnersUp: u32 = 7; - pub const MaxVotesPerVoter: u32 = 16; pub const MaxVoters: u32 = 512; pub const MaxCandidates: u32 = 64; - pub const ElectionsPhragmenPalletId: LockIdentifier = *b"phrelect"; + pub const MaxVotesPerVoter: u32 = 16; + // The ElectionsPalletId parameter name was changed along with the renaming of the elections + // pallet, but we keep the same lock ID to prevent a migration from current runtimes. + // Related to https://github.com/paritytech/substrate/issues/8250 + pub const ElectionsPalletId: LockIdentifier = *b"phrelect"; } // Make sure that there are no more than `MaxMembers` members elected via elections-phragmen. const_assert!(DesiredMembers::get() <= CouncilMaxMembers::get()); -impl pallet_elections_phragmen::Config for Runtime { +impl pallet_elections::Config for Runtime { type RuntimeEvent = RuntimeEvent; - type PalletId = ElectionsPhragmenPalletId; + type PalletId = ElectionsPalletId; type Currency = Balances; type ChangeMembers = Council; // NOTE: this implies that council's genesis members cannot be set directly and must come from @@ -1039,7 +1043,9 @@ impl pallet_elections_phragmen::Config for Runtime { type MaxVoters = MaxVoters; type MaxVotesPerVoter = MaxVotesPerVoter; type MaxCandidates = MaxCandidates; - type WeightInfo = pallet_elections_phragmen::weights::SubstrateWeight; + type ElectionSolver = ApprovalVoting; + type SolverWeightInfo = SubstrateWeight; + type WeightInfo = pallet_elections::weights::SubstrateWeight; } parameter_types! { @@ -1737,7 +1743,7 @@ construct_runtime!( Democracy: pallet_democracy, Council: pallet_collective::, TechnicalCommittee: pallet_collective::, - Elections: pallet_elections_phragmen, + Elections: pallet_elections, TechnicalMembership: pallet_membership::, Grandpa: pallet_grandpa, Treasury: pallet_treasury, @@ -1851,6 +1857,7 @@ mod benches { frame_benchmarking::define_benchmarks!( [frame_benchmarking, BaselineBench::] [frame_benchmarking_pallet_pov, Pov] + [frame_election_provider_support, EPSBench::] [pallet_alliance, Alliance] [pallet_assets, Assets] [pallet_babe, Babe] @@ -1863,8 +1870,7 @@ mod benches { [pallet_contracts, Contracts] [pallet_democracy, Democracy] [pallet_election_provider_multi_phase, ElectionProviderMultiPhase] - [pallet_election_provider_support_benchmarking, EPSBench::] - [pallet_elections_phragmen, Elections] + [pallet_elections, Elections] [pallet_fast_unstake, FastUnstake] [pallet_nis, Nis] [pallet_grandpa, Grandpa] @@ -2279,7 +2285,7 @@ impl_runtime_apis! { // which is why we need these two lines below. use pallet_session_benchmarking::Pallet as SessionBench; use pallet_offences_benchmarking::Pallet as OffencesBench; - use pallet_election_provider_support_benchmarking::Pallet as EPSBench; + use frame_election_provider_support::benchmarking::Pallet as EPSBench; use frame_system_benchmarking::Pallet as SystemBench; use baseline::Pallet as BaselineBench; use pallet_nomination_pools_benchmarking::Pallet as NominationPoolsBench; @@ -2302,14 +2308,14 @@ impl_runtime_apis! { // which is why we need these two lines below. use pallet_session_benchmarking::Pallet as SessionBench; use pallet_offences_benchmarking::Pallet as OffencesBench; - use pallet_election_provider_support_benchmarking::Pallet as EPSBench; + use frame_election_provider_support::benchmarking::Pallet as EPSBench; use frame_system_benchmarking::Pallet as SystemBench; use baseline::Pallet as BaselineBench; use pallet_nomination_pools_benchmarking::Pallet as NominationPoolsBench; impl pallet_session_benchmarking::Config for Runtime {} impl pallet_offences_benchmarking::Config for Runtime {} - impl pallet_election_provider_support_benchmarking::Config for Runtime {} + impl frame_election_provider_support::benchmarking::Config for Runtime {} impl frame_system_benchmarking::Config for Runtime {} impl baseline::Config for Runtime {} impl pallet_nomination_pools_benchmarking::Config for Runtime {} diff --git a/frame/election-provider-multi-phase/Cargo.toml b/frame/election-provider-multi-phase/Cargo.toml index aa734850a..996f40feb 100644 --- a/frame/election-provider-multi-phase/Cargo.toml +++ b/frame/election-provider-multi-phase/Cargo.toml @@ -33,7 +33,6 @@ frame-election-provider-support = { version = "4.0.0-dev", default-features = fa # Optional imports for benchmarking frame-benchmarking = { version = "4.0.0-dev", default-features = false, path = "../benchmarking", optional = true } -pallet-election-provider-support-benchmarking = { version = "4.0.0-dev", default-features = false, path = "../election-provider-support/benchmarking", optional = true } rand = { version = "0.8.5", default-features = false, features = ["alloc", "small_rng"], optional = true } strum = { version = "0.24.1", default-features = false, features = ["derive"], optional = true } @@ -50,7 +49,6 @@ frame-benchmarking = { version = "4.0.0-dev", path = "../benchmarking" } [features] default = ["std"] std = [ - "pallet-election-provider-support-benchmarking?/std", "codec/std", "scale-info/std", "log/std", diff --git a/frame/election-provider-support/Cargo.toml b/frame/election-provider-support/Cargo.toml index 114caee79..f9cb5233c 100644 --- a/frame/election-provider-support/Cargo.toml +++ b/frame/election-provider-support/Cargo.toml @@ -23,6 +23,8 @@ sp-runtime = { version = "7.0.0", default-features = false, path = "../../primit sp-std = { version = "5.0.0", default-features = false, path = "../../primitives/std" } sp-core = { version = "7.0.0", default-features = false, path = "../../primitives/core" } +frame-benchmarking = { version = "4.0.0-dev", default-features = false, path = "../benchmarking", optional = true } + [dev-dependencies] rand = { version = "0.8.5", features = ["small_rng"] } sp-io = { version = "7.0.0", path = "../../primitives/io" } @@ -41,6 +43,10 @@ std = [ "sp-core/std", "sp-runtime/std", "sp-std/std", + + "frame-benchmarking?/std", +] +runtime-benchmarks = [ + "frame-benchmarking/runtime-benchmarks", ] -runtime-benchmarks = [] try-runtime = [] diff --git a/frame/election-provider-support/benchmarking/Cargo.toml b/frame/election-provider-support/benchmarking/Cargo.toml deleted file mode 100644 index bef371ec5..000000000 --- a/frame/election-provider-support/benchmarking/Cargo.toml +++ /dev/null @@ -1,37 +0,0 @@ -[package] -name = "pallet-election-provider-support-benchmarking" -version = "4.0.0-dev" -authors = ["Parity Technologies "] -edition = "2021" -license = "Apache-2.0" -homepage = "https://substrate.io" -repository = "https://github.com/paritytech/substrate/" -description = "Benchmarking for election provider support onchain config trait" - -[package.metadata.docs.rs] -targets = ["x86_64-unknown-linux-gnu"] - -[dependencies] -codec = { package = "parity-scale-codec", version = "3.2.2", default-features = false, features = [ - "derive", -] } -frame-benchmarking = { version = "4.0.0-dev", default-features = false, optional = true, path = "../../benchmarking" } -frame-election-provider-support = { version = "4.0.0-dev", default-features = false, path = ".." } -frame-system = { version = "4.0.0-dev", default-features = false, path = "../../system" } -sp-npos-elections = { version = "4.0.0-dev", default-features = false, path = "../../../primitives/npos-elections" } -sp-runtime = { version = "7.0.0", default-features = false, path = "../../../primitives/runtime" } - -[features] -default = ["std"] -std = [ - "codec/std", - "frame-benchmarking?/std", - "frame-election-provider-support/std", - "frame-system/std", - "sp-npos-elections/std", - "sp-runtime/std", -] -runtime-benchmarks = [ - "frame-benchmarking/runtime-benchmarks", - "frame-election-provider-support/runtime-benchmarks", -] diff --git a/frame/election-provider-support/benchmarking/src/lib.rs b/frame/election-provider-support/src/benchmarking.rs similarity index 86% rename from frame/election-provider-support/benchmarking/src/lib.rs rename to frame/election-provider-support/src/benchmarking.rs index 774b7036f..5c5b952b6 100644 --- a/frame/election-provider-support/benchmarking/src/lib.rs +++ b/frame/election-provider-support/src/benchmarking.rs @@ -18,12 +18,9 @@ //! Election provider support pallet benchmarking. //! This is separated into its own crate to avoid bloating the size of the runtime. -#![cfg(feature = "runtime-benchmarks")] -#![cfg_attr(not(feature = "std"), no_std)] - +use crate::{ApprovalVoting, NposSolver, PhragMMS, SequentialPhragmen}; use codec::Decode; use frame_benchmarking::v1::{benchmarks, Vec}; -use frame_election_provider_support::{NposSolver, PhragMMS, SequentialPhragmen}; pub struct Pallet(frame_system::Pallet); pub trait Config: frame_system::Config {} @@ -88,4 +85,17 @@ benchmarks! { ::solve(d as usize, targets, voters).is_ok() ); } + + approval_voting { + let v in (VOTERS[0]) .. VOTERS[1]; + let t in (TARGETS[0]) .. TARGETS[1]; + let d in (VOTES_PER_VOTER[0]) .. VOTES_PER_VOTER[1]; + + let (voters, targets) = set_up_voters_targets::(v, t, d as usize); + }: { + assert!( + ApprovalVoting:: + ::solve(d as usize, targets, voters).is_ok() + ); + } } diff --git a/frame/election-provider-support/src/lib.rs b/frame/election-provider-support/src/lib.rs index 750ccca46..423ce3d67 100644 --- a/frame/election-provider-support/src/lib.rs +++ b/frame/election-provider-support/src/lib.rs @@ -199,6 +199,9 @@ pub use sp_arithmetic; #[doc(hidden)] pub use sp_std; +#[cfg(feature = "runtime-benchmarks")] +pub mod benchmarking; + pub mod weights; pub use weights::WeightInfo; @@ -660,6 +663,29 @@ impl(sp_std::marker::PhantomData<(AccountId, Accuracy)>); + +impl NposSolver + for ApprovalVoting +{ + type AccountId = AccountId; + type Accuracy = Accuracy; + type Error = sp_npos_elections::Error; + fn solve( + winners: usize, + targets: Vec, + voters: Vec<(Self::AccountId, VoteWeight, impl IntoIterator)>, + ) -> Result, Self::Error> { + sp_npos_elections::approval_voting(winners, targets, voters) + } + + fn weight(voters: u32, targets: u32, vote_degree: u32) -> Weight { + T::approval_voting(voters, targets, vote_degree) + } +} + /// A voter, at the level of abstraction of this crate. pub type Voter = (AccountId, VoteWeight, BoundedVec); diff --git a/frame/election-provider-support/src/onchain.rs b/frame/election-provider-support/src/onchain.rs index 296ac976d..60c7853c7 100644 --- a/frame/election-provider-support/src/onchain.rs +++ b/frame/election-provider-support/src/onchain.rs @@ -185,7 +185,7 @@ impl ElectionProvider for OnChainExecution { #[cfg(test)] mod tests { use super::*; - use crate::{ElectionProvider, PhragMMS, SequentialPhragmen}; + use crate::{ApprovalVoting, ElectionProvider, PhragMMS, SequentialPhragmen}; use frame_support::{assert_noop, parameter_types, traits::ConstU32}; use sp_npos_elections::Support; use sp_runtime::Perbill; @@ -235,6 +235,7 @@ mod tests { struct PhragmenParams; struct PhragMMSParams; + struct ApprovalVotingParams; parameter_types! { pub static MaxWinners: u32 = 10; @@ -261,6 +262,16 @@ mod tests { type TargetsBound = ConstU32<400>; } + impl Config for ApprovalVotingParams { + type System = Runtime; + type Solver = ApprovalVoting; + type DataProvider = mock_data_provider::DataProvider; + type WeightInfo = (); + type MaxWinners = MaxWinners; + type VotersBound = ConstU32<600>; + type TargetsBound = ConstU32<400>; + } + mod mock_data_provider { use frame_support::{bounded_vec, traits::ConstU32}; @@ -333,4 +344,21 @@ mod tests { ); }) } + + #[test] + fn onchain_approval_voting_works() { + sp_io::TestExternalities::new_empty().execute_with(|| { + DesiredTargets::set(3); + + // note that the `OnChainExecution::elect` implementation normalizes the vote weights. + assert_eq!( + as ElectionProvider>::elect().unwrap(), + vec![ + (10, Support { total: 20, voters: vec![(1, 5), (3, 15)] }), + (20, Support { total: 15, voters: vec![(1, 5), (2, 10)] }), + (30, Support { total: 25, voters: vec![(2, 10), (3, 15)] }) + ] + ) + }) + } } diff --git a/frame/election-provider-support/src/weights.rs b/frame/election-provider-support/src/weights.rs index fd4db1374..c28794267 100644 --- a/frame/election-provider-support/src/weights.rs +++ b/frame/election-provider-support/src/weights.rs @@ -1,13 +1,13 @@ // This file is part of Substrate. -// Copyright (C) Parity Technologies (UK) Ltd. +// Copyright (C) 2023 Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // -// http://www.apache.org/licenses/LICENSE-2.0 +// http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, @@ -15,25 +15,29 @@ // See the License for the specific language governing permissions and // limitations under the License. -//! Autogenerated weights for pallet_election_provider_support_benchmarking +//! Autogenerated weights for frame_election_provider_support //! //! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev -//! DATE: 2022-04-23, STEPS: `1`, REPEAT: 1, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! DATE: 2023-02-22, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! WORST CASE MAP SIZE: `1000000` +//! HOSTNAME: `runner-ehxwxxsd-project-145-concurrent-0`, CPU: `Intel(R) Xeon(R) CPU @ 2.60GHz` //! EXECUTION: Some(Wasm), WASM-EXECUTION: Compiled, CHAIN: Some("dev"), DB CACHE: 1024 // Executed Command: -// target/release/substrate +// target/production/substrate // benchmark // pallet -// --chain=dev -// --steps=1 -// --repeat=1 -// --pallet=pallet_election_provider_support_benchmarking +// --steps=50 +// --repeat=20 // --extrinsic=* // --execution=wasm // --wasm-execution=compiled // --heap-pages=4096 -// --output=frame/election-provider-support/src/weights.rs +// --json-file=/builds/parity/mirrors/substrate/.git/.artifacts/bench.json +// --pallet=frame_election_provider_support +// --chain=dev +// --header=./HEADER-APACHE2 +// --output=./frame/election-provider-support/src/weights.rs // --template=./.maintain/frame-weight-template.hbs #![cfg_attr(rustfmt, rustfmt_skip)] @@ -43,53 +47,108 @@ use frame_support::{traits::Get, weights::{Weight, constants::RocksDbWeight}}; use sp_std::marker::PhantomData; -/// Weight functions needed for pallet_election_provider_support_benchmarking. +/// Weight functions needed for frame_election_provider_support. pub trait WeightInfo { fn phragmen(v: u32, t: u32, d: u32, ) -> Weight; fn phragmms(v: u32, t: u32, d: u32, ) -> Weight; + fn approval_voting(v: u32, t: u32, d: u32, ) -> Weight; } -/// Weights for pallet_election_provider_support_benchmarking using the Substrate node and recommended hardware. +/// Weights for frame_election_provider_support using the Substrate node and recommended hardware. pub struct SubstrateWeight(PhantomData); impl WeightInfo for SubstrateWeight { - fn phragmen(v: u32, t: u32, d: u32, ) -> Weight { - Weight::from_ref_time(0 as u64) - // Standard Error: 667_000 - .saturating_add(Weight::from_ref_time(32_973_000 as u64).saturating_mul(v as u64)) - // Standard Error: 1_334_000 - .saturating_add(Weight::from_ref_time(1_334_000 as u64).saturating_mul(t as u64)) - // Standard Error: 60_644_000 - .saturating_add(Weight::from_ref_time(2_636_364_000 as u64).saturating_mul(d as u64)) + /// The range of component `v` is `[1000, 2000]`. + /// The range of component `t` is `[500, 1000]`. + /// The range of component `d` is `[5, 16]`. + fn phragmen(v: u32, _t: u32, d: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 5_789_174 nanoseconds. + Weight::from_ref_time(5_826_449_000) + .saturating_add(Weight::from_proof_size(0)) + // Standard Error: 130_342 + .saturating_add(Weight::from_ref_time(5_332_741).saturating_mul(v.into())) + // Standard Error: 13_325_769 + .saturating_add(Weight::from_ref_time(1_416_874_101).saturating_mul(d.into())) } - fn phragmms(v: u32, t: u32, d: u32, ) -> Weight { - Weight::from_ref_time(0 as u64) - // Standard Error: 73_000 - .saturating_add(Weight::from_ref_time(21_073_000 as u64).saturating_mul(v as u64)) - // Standard Error: 146_000 - .saturating_add(Weight::from_ref_time(65_000 as u64).saturating_mul(t as u64)) - // Standard Error: 6_649_000 - .saturating_add(Weight::from_ref_time(1_711_424_000 as u64).saturating_mul(d as u64)) + /// The range of component `v` is `[1000, 2000]`. + /// The range of component `t` is `[500, 1000]`. + /// The range of component `d` is `[5, 16]`. + fn phragmms(v: u32, _t: u32, d: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 4_151_790 nanoseconds. + Weight::from_ref_time(4_215_936_000) + .saturating_add(Weight::from_proof_size(0)) + // Standard Error: 125_135 + .saturating_add(Weight::from_ref_time(4_730_609).saturating_mul(v.into())) + // Standard Error: 12_793_390 + .saturating_add(Weight::from_ref_time(1_474_383_961).saturating_mul(d.into())) + } + /// The range of component `v` is `[1000, 2000]`. + /// The range of component `t` is `[500, 1000]`. + /// The range of component `d` is `[5, 16]`. + fn approval_voting(v: u32, _t: u32, d: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 1_800_445 nanoseconds. + Weight::from_ref_time(1_824_645_000) + .saturating_add(Weight::from_proof_size(0)) + // Standard Error: 26_266 + .saturating_add(Weight::from_ref_time(1_229_576).saturating_mul(v.into())) + // Standard Error: 2_685_343 + .saturating_add(Weight::from_ref_time(213_080_804).saturating_mul(d.into())) } } // For backwards compatibility and tests impl WeightInfo for () { - fn phragmen(v: u32, t: u32, d: u32, ) -> Weight { - Weight::from_ref_time(0 as u64) - // Standard Error: 667_000 - .saturating_add(Weight::from_ref_time(32_973_000 as u64).saturating_mul(v as u64)) - // Standard Error: 1_334_000 - .saturating_add(Weight::from_ref_time(1_334_000 as u64).saturating_mul(t as u64)) - // Standard Error: 60_644_000 - .saturating_add(Weight::from_ref_time(2_636_364_000 as u64).saturating_mul(d as u64)) + /// The range of component `v` is `[1000, 2000]`. + /// The range of component `t` is `[500, 1000]`. + /// The range of component `d` is `[5, 16]`. + fn phragmen(v: u32, _t: u32, d: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 5_789_174 nanoseconds. + Weight::from_ref_time(5_826_449_000) + .saturating_add(Weight::from_proof_size(0)) + // Standard Error: 130_342 + .saturating_add(Weight::from_ref_time(5_332_741).saturating_mul(v.into())) + // Standard Error: 13_325_769 + .saturating_add(Weight::from_ref_time(1_416_874_101).saturating_mul(d.into())) + } + /// The range of component `v` is `[1000, 2000]`. + /// The range of component `t` is `[500, 1000]`. + /// The range of component `d` is `[5, 16]`. + fn phragmms(v: u32, _t: u32, d: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 4_151_790 nanoseconds. + Weight::from_ref_time(4_215_936_000) + .saturating_add(Weight::from_proof_size(0)) + // Standard Error: 125_135 + .saturating_add(Weight::from_ref_time(4_730_609).saturating_mul(v.into())) + // Standard Error: 12_793_390 + .saturating_add(Weight::from_ref_time(1_474_383_961).saturating_mul(d.into())) } - fn phragmms(v: u32, t: u32, d: u32, ) -> Weight { - Weight::from_ref_time(0 as u64) - // Standard Error: 73_000 - .saturating_add(Weight::from_ref_time(21_073_000 as u64).saturating_mul(v as u64)) - // Standard Error: 146_000 - .saturating_add(Weight::from_ref_time(65_000 as u64).saturating_mul(t as u64)) - // Standard Error: 6_649_000 - .saturating_add(Weight::from_ref_time(1_711_424_000 as u64).saturating_mul(d as u64)) + /// The range of component `v` is `[1000, 2000]`. + /// The range of component `t` is `[500, 1000]`. + /// The range of component `d` is `[5, 16]`. + fn approval_voting(v: u32, _t: u32, d: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 1_800_445 nanoseconds. + Weight::from_ref_time(1_824_645_000) + .saturating_add(Weight::from_proof_size(0)) + // Standard Error: 26_266 + .saturating_add(Weight::from_ref_time(1_229_576).saturating_mul(v.into())) + // Standard Error: 2_685_343 + .saturating_add(Weight::from_ref_time(213_080_804).saturating_mul(d.into())) } } diff --git a/frame/elections-phragmen/src/weights.rs b/frame/elections-phragmen/src/weights.rs deleted file mode 100644 index 24ab3bc15..000000000 --- a/frame/elections-phragmen/src/weights.rs +++ /dev/null @@ -1,559 +0,0 @@ -// This file is part of Substrate. - -// Copyright (C) Parity Technologies (UK) Ltd. -// SPDX-License-Identifier: Apache-2.0 - -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -//! Autogenerated weights for pallet_elections_phragmen -//! -//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev -//! DATE: 2023-02-03, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` -//! WORST CASE MAP SIZE: `1000000` -//! HOSTNAME: `runner-b3zmxxc-project-145-concurrent-0`, CPU: `Intel(R) Xeon(R) CPU @ 2.60GHz` -//! EXECUTION: Some(Wasm), WASM-EXECUTION: Compiled, CHAIN: Some("dev"), DB CACHE: 1024 - -// Executed Command: -// target/production/substrate -// benchmark -// pallet -// --steps=50 -// --repeat=20 -// --extrinsic=* -// --execution=wasm -// --wasm-execution=compiled -// --heap-pages=4096 -// --json-file=/builds/parity/mirrors/substrate/.git/.artifacts/bench.json -// --pallet=pallet_elections_phragmen -// --chain=dev -// --header=./HEADER-APACHE2 -// --output=./frame/elections-phragmen/src/weights.rs -// --template=./.maintain/frame-weight-template.hbs - -#![cfg_attr(rustfmt, rustfmt_skip)] -#![allow(unused_parens)] -#![allow(unused_imports)] - -use frame_support::{traits::Get, weights::{Weight, constants::RocksDbWeight}}; -use sp_std::marker::PhantomData; - -/// Weight functions needed for pallet_elections_phragmen. -pub trait WeightInfo { - fn vote_equal(v: u32, ) -> Weight; - fn vote_more(v: u32, ) -> Weight; - fn vote_less(v: u32, ) -> Weight; - fn remove_voter() -> Weight; - fn submit_candidacy(c: u32, ) -> Weight; - fn renounce_candidacy_candidate(c: u32, ) -> Weight; - fn renounce_candidacy_members() -> Weight; - fn renounce_candidacy_runners_up() -> Weight; - fn remove_member_without_replacement() -> Weight; - fn remove_member_with_replacement() -> Weight; - fn clean_defunct_voters(v: u32, d: u32, ) -> Weight; - fn election_phragmen(c: u32, v: u32, e: u32, ) -> Weight; -} - -/// Weights for pallet_elections_phragmen using the Substrate node and recommended hardware. -pub struct SubstrateWeight(PhantomData); -impl WeightInfo for SubstrateWeight { - /// Storage: Elections Candidates (r:1 w:0) - /// Proof Skipped: Elections Candidates (max_values: Some(1), max_size: None, mode: Measured) - /// Storage: Elections Members (r:1 w:0) - /// Proof Skipped: Elections Members (max_values: Some(1), max_size: None, mode: Measured) - /// Storage: Elections RunnersUp (r:1 w:0) - /// Proof Skipped: Elections RunnersUp (max_values: Some(1), max_size: None, mode: Measured) - /// Storage: Elections Voting (r:1 w:1) - /// Proof Skipped: Elections Voting (max_values: None, max_size: None, mode: Measured) - /// Storage: Balances Locks (r:1 w:1) - /// Proof: Balances Locks (max_values: None, max_size: Some(1299), added: 3774, mode: MaxEncodedLen) - /// The range of component `v` is `[1, 16]`. - fn vote_equal(v: u32, ) -> Weight { - // Proof Size summary in bytes: - // Measured: `499 + v * (80 ±0)` - // Estimated: `9726 + v * (320 ±0)` - // Minimum execution time: 27_362 nanoseconds. - Weight::from_parts(28_497_963, 9726) - // Standard Error: 3_968 - .saturating_add(Weight::from_ref_time(176_840).saturating_mul(v.into())) - .saturating_add(T::DbWeight::get().reads(5_u64)) - .saturating_add(T::DbWeight::get().writes(2_u64)) - .saturating_add(Weight::from_proof_size(320).saturating_mul(v.into())) - } - /// Storage: Elections Candidates (r:1 w:0) - /// Proof Skipped: Elections Candidates (max_values: Some(1), max_size: None, mode: Measured) - /// Storage: Elections Members (r:1 w:0) - /// Proof Skipped: Elections Members (max_values: Some(1), max_size: None, mode: Measured) - /// Storage: Elections RunnersUp (r:1 w:0) - /// Proof Skipped: Elections RunnersUp (max_values: Some(1), max_size: None, mode: Measured) - /// Storage: Elections Voting (r:1 w:1) - /// Proof Skipped: Elections Voting (max_values: None, max_size: None, mode: Measured) - /// Storage: Balances Locks (r:1 w:1) - /// Proof: Balances Locks (max_values: None, max_size: Some(1299), added: 3774, mode: MaxEncodedLen) - /// The range of component `v` is `[2, 16]`. - fn vote_more(v: u32, ) -> Weight { - // Proof Size summary in bytes: - // Measured: `467 + v * (80 ±0)` - // Estimated: `9598 + v * (320 ±0)` - // Minimum execution time: 37_120 nanoseconds. - Weight::from_parts(38_455_302, 9598) - // Standard Error: 5_478 - .saturating_add(Weight::from_ref_time(219_678).saturating_mul(v.into())) - .saturating_add(T::DbWeight::get().reads(5_u64)) - .saturating_add(T::DbWeight::get().writes(2_u64)) - .saturating_add(Weight::from_proof_size(320).saturating_mul(v.into())) - } - /// Storage: Elections Candidates (r:1 w:0) - /// Proof Skipped: Elections Candidates (max_values: Some(1), max_size: None, mode: Measured) - /// Storage: Elections Members (r:1 w:0) - /// Proof Skipped: Elections Members (max_values: Some(1), max_size: None, mode: Measured) - /// Storage: Elections RunnersUp (r:1 w:0) - /// Proof Skipped: Elections RunnersUp (max_values: Some(1), max_size: None, mode: Measured) - /// Storage: Elections Voting (r:1 w:1) - /// Proof Skipped: Elections Voting (max_values: None, max_size: None, mode: Measured) - /// Storage: Balances Locks (r:1 w:1) - /// Proof: Balances Locks (max_values: None, max_size: Some(1299), added: 3774, mode: MaxEncodedLen) - /// The range of component `v` is `[2, 16]`. - fn vote_less(v: u32, ) -> Weight { - // Proof Size summary in bytes: - // Measured: `499 + v * (80 ±0)` - // Estimated: `9726 + v * (320 ±0)` - // Minimum execution time: 36_928 nanoseconds. - Weight::from_parts(38_334_669, 9726) - // Standard Error: 5_271 - .saturating_add(Weight::from_ref_time(232_355).saturating_mul(v.into())) - .saturating_add(T::DbWeight::get().reads(5_u64)) - .saturating_add(T::DbWeight::get().writes(2_u64)) - .saturating_add(Weight::from_proof_size(320).saturating_mul(v.into())) - } - /// Storage: Elections Voting (r:1 w:1) - /// Proof Skipped: Elections Voting (max_values: None, max_size: None, mode: Measured) - /// Storage: Balances Locks (r:1 w:1) - /// Proof: Balances Locks (max_values: None, max_size: Some(1299), added: 3774, mode: MaxEncodedLen) - fn remove_voter() -> Weight { - // Proof Size summary in bytes: - // Measured: `989` - // Estimated: `7238` - // Minimum execution time: 34_338 nanoseconds. - Weight::from_parts(35_672_000, 7238) - .saturating_add(T::DbWeight::get().reads(2_u64)) - .saturating_add(T::DbWeight::get().writes(2_u64)) - } - /// Storage: Elections Candidates (r:1 w:1) - /// Proof Skipped: Elections Candidates (max_values: Some(1), max_size: None, mode: Measured) - /// Storage: Elections Members (r:1 w:0) - /// Proof Skipped: Elections Members (max_values: Some(1), max_size: None, mode: Measured) - /// Storage: Elections RunnersUp (r:1 w:0) - /// Proof Skipped: Elections RunnersUp (max_values: Some(1), max_size: None, mode: Measured) - /// The range of component `c` is `[1, 64]`. - fn submit_candidacy(c: u32, ) -> Weight { - // Proof Size summary in bytes: - // Measured: `1697 + c * (48 ±0)` - // Estimated: `6576 + c * (144 ±0)` - // Minimum execution time: 31_864 nanoseconds. - Weight::from_parts(33_490_161, 6576) - // Standard Error: 2_643 - .saturating_add(Weight::from_ref_time(158_386).saturating_mul(c.into())) - .saturating_add(T::DbWeight::get().reads(3_u64)) - .saturating_add(T::DbWeight::get().writes(1_u64)) - .saturating_add(Weight::from_proof_size(144).saturating_mul(c.into())) - } - /// Storage: Elections Candidates (r:1 w:1) - /// Proof Skipped: Elections Candidates (max_values: Some(1), max_size: None, mode: Measured) - /// The range of component `c` is `[1, 64]`. - fn renounce_candidacy_candidate(c: u32, ) -> Weight { - // Proof Size summary in bytes: - // Measured: `349 + c * (48 ±0)` - // Estimated: `844 + c * (48 ±0)` - // Minimum execution time: 27_292 nanoseconds. - Weight::from_parts(28_364_955, 844) - // Standard Error: 1_335 - .saturating_add(Weight::from_ref_time(78_086).saturating_mul(c.into())) - .saturating_add(T::DbWeight::get().reads(1_u64)) - .saturating_add(T::DbWeight::get().writes(1_u64)) - .saturating_add(Weight::from_proof_size(48).saturating_mul(c.into())) - } - /// Storage: Elections Members (r:1 w:1) - /// Proof Skipped: Elections Members (max_values: Some(1), max_size: None, mode: Measured) - /// Storage: Elections RunnersUp (r:1 w:1) - /// Proof Skipped: Elections RunnersUp (max_values: Some(1), max_size: None, mode: Measured) - /// Storage: Council Prime (r:1 w:1) - /// Proof Skipped: Council Prime (max_values: Some(1), max_size: None, mode: Measured) - /// Storage: Council Proposals (r:1 w:0) - /// Proof Skipped: Council Proposals (max_values: Some(1), max_size: None, mode: Measured) - /// Storage: Council Members (r:0 w:1) - /// Proof Skipped: Council Members (max_values: Some(1), max_size: None, mode: Measured) - fn renounce_candidacy_members() -> Weight { - // Proof Size summary in bytes: - // Measured: `2027` - // Estimated: `12115` - // Minimum execution time: 45_975 nanoseconds. - Weight::from_parts(47_103_000, 12115) - .saturating_add(T::DbWeight::get().reads(4_u64)) - .saturating_add(T::DbWeight::get().writes(4_u64)) - } - /// Storage: Elections RunnersUp (r:1 w:1) - /// Proof Skipped: Elections RunnersUp (max_values: Some(1), max_size: None, mode: Measured) - fn renounce_candidacy_runners_up() -> Weight { - // Proof Size summary in bytes: - // Measured: `975` - // Estimated: `1470` - // Minimum execution time: 29_243 nanoseconds. - Weight::from_parts(30_582_000, 1470) - .saturating_add(T::DbWeight::get().reads(1_u64)) - .saturating_add(T::DbWeight::get().writes(1_u64)) - } - /// Storage: Benchmark Override (r:0 w:0) - /// Proof Skipped: Benchmark Override (max_values: None, max_size: None, mode: Measured) - fn remove_member_without_replacement() -> Weight { - // Proof Size summary in bytes: - // Measured: `0` - // Estimated: `0` - // Minimum execution time: 2_000_000_000 nanoseconds. - Weight::from_ref_time(2_000_000_000_000) - } - /// Storage: Elections Members (r:1 w:1) - /// Proof Skipped: Elections Members (max_values: Some(1), max_size: None, mode: Measured) - /// Storage: System Account (r:1 w:1) - /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) - /// Storage: Elections RunnersUp (r:1 w:1) - /// Proof Skipped: Elections RunnersUp (max_values: Some(1), max_size: None, mode: Measured) - /// Storage: Council Prime (r:1 w:1) - /// Proof Skipped: Council Prime (max_values: Some(1), max_size: None, mode: Measured) - /// Storage: Council Proposals (r:1 w:0) - /// Proof Skipped: Council Proposals (max_values: Some(1), max_size: None, mode: Measured) - /// Storage: Council Members (r:0 w:1) - /// Proof Skipped: Council Members (max_values: Some(1), max_size: None, mode: Measured) - fn remove_member_with_replacement() -> Weight { - // Proof Size summary in bytes: - // Measured: `2027` - // Estimated: `14718` - // Minimum execution time: 52_527 nanoseconds. - Weight::from_parts(53_538_000, 14718) - .saturating_add(T::DbWeight::get().reads(5_u64)) - .saturating_add(T::DbWeight::get().writes(5_u64)) - } - /// Storage: Elections Voting (r:513 w:512) - /// Proof Skipped: Elections Voting (max_values: None, max_size: None, mode: Measured) - /// Storage: Elections Members (r:1 w:0) - /// Proof Skipped: Elections Members (max_values: Some(1), max_size: None, mode: Measured) - /// Storage: Elections RunnersUp (r:1 w:0) - /// Proof Skipped: Elections RunnersUp (max_values: Some(1), max_size: None, mode: Measured) - /// Storage: Elections Candidates (r:1 w:0) - /// Proof Skipped: Elections Candidates (max_values: Some(1), max_size: None, mode: Measured) - /// Storage: Balances Locks (r:512 w:512) - /// Proof: Balances Locks (max_values: None, max_size: Some(1299), added: 3774, mode: MaxEncodedLen) - /// Storage: System Account (r:512 w:512) - /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) - /// The range of component `v` is `[256, 512]`. - /// The range of component `d` is `[0, 256]`. - fn clean_defunct_voters(v: u32, _d: u32, ) -> Weight { - // Proof Size summary in bytes: - // Measured: `1115 + v * (875 ±0)` - // Estimated: `8448 + v * (12352 ±0)` - // Minimum execution time: 14_934_185 nanoseconds. - Weight::from_parts(15_014_057_000, 8448) - // Standard Error: 245_588 - .saturating_add(Weight::from_ref_time(35_586_946).saturating_mul(v.into())) - .saturating_add(T::DbWeight::get().reads(4_u64)) - .saturating_add(T::DbWeight::get().reads((3_u64).saturating_mul(v.into()))) - .saturating_add(T::DbWeight::get().writes((3_u64).saturating_mul(v.into()))) - .saturating_add(Weight::from_proof_size(12352).saturating_mul(v.into())) - } - /// Storage: Elections Candidates (r:1 w:1) - /// Proof Skipped: Elections Candidates (max_values: Some(1), max_size: None, mode: Measured) - /// Storage: Elections Members (r:1 w:1) - /// Proof Skipped: Elections Members (max_values: Some(1), max_size: None, mode: Measured) - /// Storage: Elections RunnersUp (r:1 w:1) - /// Proof Skipped: Elections RunnersUp (max_values: Some(1), max_size: None, mode: Measured) - /// Storage: Elections Voting (r:513 w:0) - /// Proof Skipped: Elections Voting (max_values: None, max_size: None, mode: Measured) - /// Storage: Council Proposals (r:1 w:0) - /// Proof Skipped: Council Proposals (max_values: Some(1), max_size: None, mode: Measured) - /// Storage: System Account (r:44 w:44) - /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) - /// Storage: Elections ElectionRounds (r:1 w:1) - /// Proof Skipped: Elections ElectionRounds (max_values: Some(1), max_size: None, mode: Measured) - /// Storage: Council Members (r:0 w:1) - /// Proof Skipped: Council Members (max_values: Some(1), max_size: None, mode: Measured) - /// Storage: Council Prime (r:0 w:1) - /// Proof Skipped: Council Prime (max_values: Some(1), max_size: None, mode: Measured) - /// The range of component `c` is `[1, 64]`. - /// The range of component `v` is `[1, 512]`. - /// The range of component `e` is `[512, 8192]`. - fn election_phragmen(c: u32, v: u32, e: u32, ) -> Weight { - // Proof Size summary in bytes: - // Measured: `0 + v * (638 ±0) + e * (28 ±0)` - // Estimated: `330033 + v * (5229 ±6) + e * (89 ±0) + c * (2135 ±7)` - // Minimum execution time: 1_273_671 nanoseconds. - Weight::from_parts(1_279_716_000, 330033) - // Standard Error: 543_277 - .saturating_add(Weight::from_ref_time(20_613_753).saturating_mul(v.into())) - // Standard Error: 34_857 - .saturating_add(Weight::from_ref_time(688_354).saturating_mul(e.into())) - .saturating_add(T::DbWeight::get().reads(21_u64)) - .saturating_add(T::DbWeight::get().reads((1_u64).saturating_mul(c.into()))) - .saturating_add(T::DbWeight::get().reads((1_u64).saturating_mul(v.into()))) - .saturating_add(T::DbWeight::get().writes(6_u64)) - .saturating_add(T::DbWeight::get().writes((1_u64).saturating_mul(c.into()))) - .saturating_add(Weight::from_proof_size(5229).saturating_mul(v.into())) - .saturating_add(Weight::from_proof_size(89).saturating_mul(e.into())) - .saturating_add(Weight::from_proof_size(2135).saturating_mul(c.into())) - } -} - -// For backwards compatibility and tests -impl WeightInfo for () { - /// Storage: Elections Candidates (r:1 w:0) - /// Proof Skipped: Elections Candidates (max_values: Some(1), max_size: None, mode: Measured) - /// Storage: Elections Members (r:1 w:0) - /// Proof Skipped: Elections Members (max_values: Some(1), max_size: None, mode: Measured) - /// Storage: Elections RunnersUp (r:1 w:0) - /// Proof Skipped: Elections RunnersUp (max_values: Some(1), max_size: None, mode: Measured) - /// Storage: Elections Voting (r:1 w:1) - /// Proof Skipped: Elections Voting (max_values: None, max_size: None, mode: Measured) - /// Storage: Balances Locks (r:1 w:1) - /// Proof: Balances Locks (max_values: None, max_size: Some(1299), added: 3774, mode: MaxEncodedLen) - /// The range of component `v` is `[1, 16]`. - fn vote_equal(v: u32, ) -> Weight { - // Proof Size summary in bytes: - // Measured: `499 + v * (80 ±0)` - // Estimated: `9726 + v * (320 ±0)` - // Minimum execution time: 27_362 nanoseconds. - Weight::from_parts(28_497_963, 9726) - // Standard Error: 3_968 - .saturating_add(Weight::from_ref_time(176_840).saturating_mul(v.into())) - .saturating_add(RocksDbWeight::get().reads(5_u64)) - .saturating_add(RocksDbWeight::get().writes(2_u64)) - .saturating_add(Weight::from_proof_size(320).saturating_mul(v.into())) - } - /// Storage: Elections Candidates (r:1 w:0) - /// Proof Skipped: Elections Candidates (max_values: Some(1), max_size: None, mode: Measured) - /// Storage: Elections Members (r:1 w:0) - /// Proof Skipped: Elections Members (max_values: Some(1), max_size: None, mode: Measured) - /// Storage: Elections RunnersUp (r:1 w:0) - /// Proof Skipped: Elections RunnersUp (max_values: Some(1), max_size: None, mode: Measured) - /// Storage: Elections Voting (r:1 w:1) - /// Proof Skipped: Elections Voting (max_values: None, max_size: None, mode: Measured) - /// Storage: Balances Locks (r:1 w:1) - /// Proof: Balances Locks (max_values: None, max_size: Some(1299), added: 3774, mode: MaxEncodedLen) - /// The range of component `v` is `[2, 16]`. - fn vote_more(v: u32, ) -> Weight { - // Proof Size summary in bytes: - // Measured: `467 + v * (80 ±0)` - // Estimated: `9598 + v * (320 ±0)` - // Minimum execution time: 37_120 nanoseconds. - Weight::from_parts(38_455_302, 9598) - // Standard Error: 5_478 - .saturating_add(Weight::from_ref_time(219_678).saturating_mul(v.into())) - .saturating_add(RocksDbWeight::get().reads(5_u64)) - .saturating_add(RocksDbWeight::get().writes(2_u64)) - .saturating_add(Weight::from_proof_size(320).saturating_mul(v.into())) - } - /// Storage: Elections Candidates (r:1 w:0) - /// Proof Skipped: Elections Candidates (max_values: Some(1), max_size: None, mode: Measured) - /// Storage: Elections Members (r:1 w:0) - /// Proof Skipped: Elections Members (max_values: Some(1), max_size: None, mode: Measured) - /// Storage: Elections RunnersUp (r:1 w:0) - /// Proof Skipped: Elections RunnersUp (max_values: Some(1), max_size: None, mode: Measured) - /// Storage: Elections Voting (r:1 w:1) - /// Proof Skipped: Elections Voting (max_values: None, max_size: None, mode: Measured) - /// Storage: Balances Locks (r:1 w:1) - /// Proof: Balances Locks (max_values: None, max_size: Some(1299), added: 3774, mode: MaxEncodedLen) - /// The range of component `v` is `[2, 16]`. - fn vote_less(v: u32, ) -> Weight { - // Proof Size summary in bytes: - // Measured: `499 + v * (80 ±0)` - // Estimated: `9726 + v * (320 ±0)` - // Minimum execution time: 36_928 nanoseconds. - Weight::from_parts(38_334_669, 9726) - // Standard Error: 5_271 - .saturating_add(Weight::from_ref_time(232_355).saturating_mul(v.into())) - .saturating_add(RocksDbWeight::get().reads(5_u64)) - .saturating_add(RocksDbWeight::get().writes(2_u64)) - .saturating_add(Weight::from_proof_size(320).saturating_mul(v.into())) - } - /// Storage: Elections Voting (r:1 w:1) - /// Proof Skipped: Elections Voting (max_values: None, max_size: None, mode: Measured) - /// Storage: Balances Locks (r:1 w:1) - /// Proof: Balances Locks (max_values: None, max_size: Some(1299), added: 3774, mode: MaxEncodedLen) - fn remove_voter() -> Weight { - // Proof Size summary in bytes: - // Measured: `989` - // Estimated: `7238` - // Minimum execution time: 34_338 nanoseconds. - Weight::from_parts(35_672_000, 7238) - .saturating_add(RocksDbWeight::get().reads(2_u64)) - .saturating_add(RocksDbWeight::get().writes(2_u64)) - } - /// Storage: Elections Candidates (r:1 w:1) - /// Proof Skipped: Elections Candidates (max_values: Some(1), max_size: None, mode: Measured) - /// Storage: Elections Members (r:1 w:0) - /// Proof Skipped: Elections Members (max_values: Some(1), max_size: None, mode: Measured) - /// Storage: Elections RunnersUp (r:1 w:0) - /// Proof Skipped: Elections RunnersUp (max_values: Some(1), max_size: None, mode: Measured) - /// The range of component `c` is `[1, 64]`. - fn submit_candidacy(c: u32, ) -> Weight { - // Proof Size summary in bytes: - // Measured: `1697 + c * (48 ±0)` - // Estimated: `6576 + c * (144 ±0)` - // Minimum execution time: 31_864 nanoseconds. - Weight::from_parts(33_490_161, 6576) - // Standard Error: 2_643 - .saturating_add(Weight::from_ref_time(158_386).saturating_mul(c.into())) - .saturating_add(RocksDbWeight::get().reads(3_u64)) - .saturating_add(RocksDbWeight::get().writes(1_u64)) - .saturating_add(Weight::from_proof_size(144).saturating_mul(c.into())) - } - /// Storage: Elections Candidates (r:1 w:1) - /// Proof Skipped: Elections Candidates (max_values: Some(1), max_size: None, mode: Measured) - /// The range of component `c` is `[1, 64]`. - fn renounce_candidacy_candidate(c: u32, ) -> Weight { - // Proof Size summary in bytes: - // Measured: `349 + c * (48 ±0)` - // Estimated: `844 + c * (48 ±0)` - // Minimum execution time: 27_292 nanoseconds. - Weight::from_parts(28_364_955, 844) - // Standard Error: 1_335 - .saturating_add(Weight::from_ref_time(78_086).saturating_mul(c.into())) - .saturating_add(RocksDbWeight::get().reads(1_u64)) - .saturating_add(RocksDbWeight::get().writes(1_u64)) - .saturating_add(Weight::from_proof_size(48).saturating_mul(c.into())) - } - /// Storage: Elections Members (r:1 w:1) - /// Proof Skipped: Elections Members (max_values: Some(1), max_size: None, mode: Measured) - /// Storage: Elections RunnersUp (r:1 w:1) - /// Proof Skipped: Elections RunnersUp (max_values: Some(1), max_size: None, mode: Measured) - /// Storage: Council Prime (r:1 w:1) - /// Proof Skipped: Council Prime (max_values: Some(1), max_size: None, mode: Measured) - /// Storage: Council Proposals (r:1 w:0) - /// Proof Skipped: Council Proposals (max_values: Some(1), max_size: None, mode: Measured) - /// Storage: Council Members (r:0 w:1) - /// Proof Skipped: Council Members (max_values: Some(1), max_size: None, mode: Measured) - fn renounce_candidacy_members() -> Weight { - // Proof Size summary in bytes: - // Measured: `2027` - // Estimated: `12115` - // Minimum execution time: 45_975 nanoseconds. - Weight::from_parts(47_103_000, 12115) - .saturating_add(RocksDbWeight::get().reads(4_u64)) - .saturating_add(RocksDbWeight::get().writes(4_u64)) - } - /// Storage: Elections RunnersUp (r:1 w:1) - /// Proof Skipped: Elections RunnersUp (max_values: Some(1), max_size: None, mode: Measured) - fn renounce_candidacy_runners_up() -> Weight { - // Proof Size summary in bytes: - // Measured: `975` - // Estimated: `1470` - // Minimum execution time: 29_243 nanoseconds. - Weight::from_parts(30_582_000, 1470) - .saturating_add(RocksDbWeight::get().reads(1_u64)) - .saturating_add(RocksDbWeight::get().writes(1_u64)) - } - /// Storage: Benchmark Override (r:0 w:0) - /// Proof Skipped: Benchmark Override (max_values: None, max_size: None, mode: Measured) - fn remove_member_without_replacement() -> Weight { - // Proof Size summary in bytes: - // Measured: `0` - // Estimated: `0` - // Minimum execution time: 2_000_000_000 nanoseconds. - Weight::from_ref_time(2_000_000_000_000) - } - /// Storage: Elections Members (r:1 w:1) - /// Proof Skipped: Elections Members (max_values: Some(1), max_size: None, mode: Measured) - /// Storage: System Account (r:1 w:1) - /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) - /// Storage: Elections RunnersUp (r:1 w:1) - /// Proof Skipped: Elections RunnersUp (max_values: Some(1), max_size: None, mode: Measured) - /// Storage: Council Prime (r:1 w:1) - /// Proof Skipped: Council Prime (max_values: Some(1), max_size: None, mode: Measured) - /// Storage: Council Proposals (r:1 w:0) - /// Proof Skipped: Council Proposals (max_values: Some(1), max_size: None, mode: Measured) - /// Storage: Council Members (r:0 w:1) - /// Proof Skipped: Council Members (max_values: Some(1), max_size: None, mode: Measured) - fn remove_member_with_replacement() -> Weight { - // Proof Size summary in bytes: - // Measured: `2027` - // Estimated: `14718` - // Minimum execution time: 52_527 nanoseconds. - Weight::from_parts(53_538_000, 14718) - .saturating_add(RocksDbWeight::get().reads(5_u64)) - .saturating_add(RocksDbWeight::get().writes(5_u64)) - } - /// Storage: Elections Voting (r:513 w:512) - /// Proof Skipped: Elections Voting (max_values: None, max_size: None, mode: Measured) - /// Storage: Elections Members (r:1 w:0) - /// Proof Skipped: Elections Members (max_values: Some(1), max_size: None, mode: Measured) - /// Storage: Elections RunnersUp (r:1 w:0) - /// Proof Skipped: Elections RunnersUp (max_values: Some(1), max_size: None, mode: Measured) - /// Storage: Elections Candidates (r:1 w:0) - /// Proof Skipped: Elections Candidates (max_values: Some(1), max_size: None, mode: Measured) - /// Storage: Balances Locks (r:512 w:512) - /// Proof: Balances Locks (max_values: None, max_size: Some(1299), added: 3774, mode: MaxEncodedLen) - /// Storage: System Account (r:512 w:512) - /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) - /// The range of component `v` is `[256, 512]`. - /// The range of component `d` is `[0, 256]`. - fn clean_defunct_voters(v: u32, _d: u32, ) -> Weight { - // Proof Size summary in bytes: - // Measured: `1115 + v * (875 ±0)` - // Estimated: `8448 + v * (12352 ±0)` - // Minimum execution time: 14_934_185 nanoseconds. - Weight::from_parts(15_014_057_000, 8448) - // Standard Error: 245_588 - .saturating_add(Weight::from_ref_time(35_586_946).saturating_mul(v.into())) - .saturating_add(RocksDbWeight::get().reads(4_u64)) - .saturating_add(RocksDbWeight::get().reads((3_u64).saturating_mul(v.into()))) - .saturating_add(RocksDbWeight::get().writes((3_u64).saturating_mul(v.into()))) - .saturating_add(Weight::from_proof_size(12352).saturating_mul(v.into())) - } - /// Storage: Elections Candidates (r:1 w:1) - /// Proof Skipped: Elections Candidates (max_values: Some(1), max_size: None, mode: Measured) - /// Storage: Elections Members (r:1 w:1) - /// Proof Skipped: Elections Members (max_values: Some(1), max_size: None, mode: Measured) - /// Storage: Elections RunnersUp (r:1 w:1) - /// Proof Skipped: Elections RunnersUp (max_values: Some(1), max_size: None, mode: Measured) - /// Storage: Elections Voting (r:513 w:0) - /// Proof Skipped: Elections Voting (max_values: None, max_size: None, mode: Measured) - /// Storage: Council Proposals (r:1 w:0) - /// Proof Skipped: Council Proposals (max_values: Some(1), max_size: None, mode: Measured) - /// Storage: System Account (r:44 w:44) - /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) - /// Storage: Elections ElectionRounds (r:1 w:1) - /// Proof Skipped: Elections ElectionRounds (max_values: Some(1), max_size: None, mode: Measured) - /// Storage: Council Members (r:0 w:1) - /// Proof Skipped: Council Members (max_values: Some(1), max_size: None, mode: Measured) - /// Storage: Council Prime (r:0 w:1) - /// Proof Skipped: Council Prime (max_values: Some(1), max_size: None, mode: Measured) - /// The range of component `c` is `[1, 64]`. - /// The range of component `v` is `[1, 512]`. - /// The range of component `e` is `[512, 8192]`. - fn election_phragmen(c: u32, v: u32, e: u32, ) -> Weight { - // Proof Size summary in bytes: - // Measured: `0 + v * (638 ±0) + e * (28 ±0)` - // Estimated: `330033 + v * (5229 ±6) + e * (89 ±0) + c * (2135 ±7)` - // Minimum execution time: 1_273_671 nanoseconds. - Weight::from_parts(1_279_716_000, 330033) - // Standard Error: 543_277 - .saturating_add(Weight::from_ref_time(20_613_753).saturating_mul(v.into())) - // Standard Error: 34_857 - .saturating_add(Weight::from_ref_time(688_354).saturating_mul(e.into())) - .saturating_add(RocksDbWeight::get().reads(21_u64)) - .saturating_add(RocksDbWeight::get().reads((1_u64).saturating_mul(c.into()))) - .saturating_add(RocksDbWeight::get().reads((1_u64).saturating_mul(v.into()))) - .saturating_add(RocksDbWeight::get().writes(6_u64)) - .saturating_add(RocksDbWeight::get().writes((1_u64).saturating_mul(c.into()))) - .saturating_add(Weight::from_proof_size(5229).saturating_mul(v.into())) - .saturating_add(Weight::from_proof_size(89).saturating_mul(e.into())) - .saturating_add(Weight::from_proof_size(2135).saturating_mul(c.into())) - } -} diff --git a/frame/elections-phragmen/CHANGELOG.md b/frame/elections/CHANGELOG.md similarity index 80% rename from frame/elections-phragmen/CHANGELOG.md rename to frame/elections/CHANGELOG.md index 231de1d2e..b173557be 100644 --- a/frame/elections-phragmen/CHANGELOG.md +++ b/frame/elections/CHANGELOG.md @@ -4,6 +4,17 @@ All notable changes to this crate will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this crate adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## [5.0.0] - UNRELEASED + +### Added + +### Changed +Generalized the pallet to use `NposSolver` instead of hard coding the phragmen algorithm; Changed the name of the pallet from `pallet-elections-phragmen` to `pallet-elections` + +### Fixed + +### Security + ## [4.0.0] - UNRELEASED ### Added diff --git a/frame/elections-phragmen/Cargo.toml b/frame/elections/Cargo.toml similarity index 90% rename from frame/elections-phragmen/Cargo.toml rename to frame/elections/Cargo.toml index ce39e42b8..14d07eca7 100644 --- a/frame/elections-phragmen/Cargo.toml +++ b/frame/elections/Cargo.toml @@ -1,12 +1,12 @@ [package] -name = "pallet-elections-phragmen" +name = "pallet-elections" version = "5.0.0-dev" authors = ["Parity Technologies "] edition = "2021" license = "Apache-2.0" homepage = "https://substrate.io" repository = "https://github.com/paritytech/substrate/" -description = "FRAME pallet based on seq-Phragmén election method." +description = "FRAME pallet for generic elections." readme = "README.md" [package.metadata.docs.rs] @@ -24,6 +24,7 @@ frame-system = { version = "4.0.0-dev", default-features = false, path = "../sys sp-core = { version = "7.0.0", default-features = false, path = "../../primitives/core" } sp-io = { version = "7.0.0", default-features = false, path = "../../primitives/io" } sp-npos-elections = { version = "4.0.0-dev", default-features = false, path = "../../primitives/npos-elections" } +frame-election-provider-support = { version = "4.0.0-dev", default-features = false, path = "../election-provider-support" } sp-runtime = { version = "7.0.0", default-features = false, path = "../../primitives/runtime" } sp-std = { version = "5.0.0", default-features = false, path = "../../primitives/std" } diff --git a/frame/elections-phragmen/README.md b/frame/elections/README.md similarity index 94% rename from frame/elections-phragmen/README.md rename to frame/elections/README.md index 26b3f260d..0f27bdd18 100644 --- a/frame/elections-phragmen/README.md +++ b/frame/elections/README.md @@ -1,6 +1,6 @@ -# Phragmén Election Module. +# Generic Elections Module. -An election module based on sequential phragmen. +A generic elections module. ### Term and Round @@ -60,7 +60,7 @@ being re-elected at the end of each round. ### Module Information -- [`election_sp_phragmen::Config`](https://docs.rs/pallet-elections-phragmen/latest/pallet_elections_phragmen/trait.Config.html) +- [`elections::Config`](https://docs.rs/pallet-elections-phragmen/latest/pallet_elections_phragmen/trait.Config.html) - [`Call`](https://docs.rs/pallet-elections-phragmen/latest/pallet_elections_phragmen/enum.Call.html) - [`Module`](https://docs.rs/pallet-elections-phragmen/latest/pallet_elections_phragmen/struct.Module.html) diff --git a/frame/elections-phragmen/src/benchmarking.rs b/frame/elections/src/benchmarking.rs similarity index 85% rename from frame/elections-phragmen/src/benchmarking.rs rename to frame/elections/src/benchmarking.rs index 56ea19578..da755fe9b 100644 --- a/frame/elections-phragmen/src/benchmarking.rs +++ b/frame/elections/src/benchmarking.rs @@ -15,14 +15,14 @@ // See the License for the specific language governing permissions and // limitations under the License. -//! Elections-Phragmen pallet benchmarking. +//! Elections pallet benchmarking. #![cfg(feature = "runtime-benchmarks")] use super::*; use frame_benchmarking::v1::{account, benchmarks, whitelist, BenchmarkError, BenchmarkResult}; -use frame_support::{dispatch::DispatchResultWithPostInfo, traits::OnInitialize}; +use frame_support::dispatch::DispatchResultWithPostInfo; use frame_system::RawOrigin; use crate::Pallet as Elections; @@ -37,7 +37,7 @@ fn endowed_account(name: &'static str, index: u32) -> T::AccountId { let amount = default_stake::(T::MaxVoters::get()) * BalanceOf::::from(BALANCE_FACTOR); let _ = T::Currency::make_free_balance_be(&account, amount); // important to increase the total issuance since T::CurrencyToVote will need it to be sane for - // phragmen to work. + // the election to work. T::Currency::issue(amount); account @@ -122,7 +122,7 @@ fn distribute_voters( fn fill_seats_up_to(m: u32) -> Result, &'static str> { let _ = submit_candidates_with_self_vote::(m, "fill_seats_up_to")?; assert_eq!(>::candidates().len() as u32, m, "wrong number of candidates."); - >::do_phragmen(); + >::do_election(); assert_eq!(>::candidates().len(), 0, "some candidates remaining."); assert_eq!( >::members().len() + >::runners_up().len(), @@ -382,11 +382,11 @@ benchmarks! { assert_eq!(>::iter().count() as u32, 0); } - election_phragmen { - // This is just to focus on phragmen in the context of this module. We always select 20 - // members, this is hard-coded in the runtime and cannot be trivially changed at this stage. - // Yet, change the number of voters, candidates and edge per voter to see the impact. Note - // that we give all candidates a self vote to make sure they are all considered. + pre_solve_election { + // We always select 20 members, this is hard-coded in the runtime and cannot be trivially + // changed at this stage. Yet, change the number of voters, candidates and edge per voter + // to see the impact. Note that we give all candidates a self vote to make sure they are + // all considered. let c in 1 .. T::MaxCandidates::get(); let v in 1 .. T::MaxVoters::get(); let e in (T::MaxVoters::get()) .. T::MaxVoters::get() * T::MaxVotesPerVoter::get(); @@ -404,7 +404,44 @@ benchmarks! { let all_candidates = submit_candidates_with_self_vote::(c, "candidates")?; let _ = distribute_voters::(all_candidates, v.saturating_sub(c), votes_per_voter as usize)?; }: { - >::on_initialize(T::TermDuration::get()); + >::do_pre_solve_election().unwrap(); + } + + post_solve_election { + // We always select 20 members, this is hard-coded in the runtime and cannot be trivially + // changed at this stage. Yet, change the number of voters, candidates and edge per voter + // to see the impact. Note that we give all candidates a self vote to make sure they are + // all considered. + let c in 1 .. T::MaxCandidates::get(); + let v in 1 .. T::MaxVoters::get(); + let e in (T::MaxVoters::get()) .. T::MaxVoters::get() as u32 * T::MaxVotesPerVoter::get() as u32; + clean::(); + + // so we have a situation with v and e. we want e to basically always be in the range of `e + // -> e * T::MaxVotesPerVoter`, but we cannot express that now with the benchmarks. So what we do + // is: when c is being iterated, v, and e are max and fine. when v is being iterated, e is + // being set to max and this is a problem. In these cases, we cap e to a lower value, namely + // v * `T::MaxVotesPerVoter`. when e is being iterated, v is at max, and again fine. all in all, + // votes_per_voter can never be more than `T::MaxVotesPerVoter`. Note that this might cause `v` to be + // an overestimate. + let votes_per_voter = (e / v).min(T::MaxVotesPerVoter::get() as u32); + + let all_candidates = submit_candidates_with_self_vote::(c, "candidates")?; + let _ = distribute_voters::(all_candidates, v.saturating_sub(c), votes_per_voter as usize)?; + + let pre_election_result = >::do_pre_solve_election().unwrap(); + let election_result = T::ElectionSolver::solve( + pre_election_result.num_to_elect, + pre_election_result.candidate_ids, + pre_election_result.voters_and_votes, + ).unwrap(); + + }: { + >::do_post_solve_election( + election_result.winners, + pre_election_result.candidates_and_deposit, + pre_election_result.voters_and_stakes, + ); } verify { assert_eq!(>::members().len() as u32, T::DesiredMembers::get().min(c)); diff --git a/frame/elections-phragmen/src/lib.rs b/frame/elections/src/lib.rs similarity index 90% rename from frame/elections-phragmen/src/lib.rs rename to frame/elections/src/lib.rs index fa63762c5..bdf0161ba 100644 --- a/frame/elections-phragmen/src/lib.rs +++ b/frame/elections/src/lib.rs @@ -15,9 +15,9 @@ // See the License for the specific language governing permissions and // limitations under the License. -//! # Phragmén Election Module. +//! # Generic Election Module. //! -//! An election module based on sequential phragmen. +//! An election module based on a generic election algorithm. //! //! ### Term and Round //! @@ -99,6 +99,7 @@ #![cfg_attr(not(feature = "std"), no_std)] use codec::{Decode, Encode}; +use frame_election_provider_support::NposSolver; use frame_support::{ traits::{ defensive_prelude::*, ChangeMembers, Contains, ContainsLengthBound, Currency, @@ -108,12 +109,12 @@ use frame_support::{ weights::Weight, }; use scale_info::TypeInfo; -use sp_npos_elections::{ElectionResult, ExtendedBalance}; +use sp_npos_elections::{ElectionResult, ExtendedBalance, VoteWeight}; use sp_runtime::{ traits::{Saturating, StaticLookup, Zero}, - DispatchError, Perbill, RuntimeDebug, + DispatchError, RuntimeDebug, }; -use sp_std::{cmp::Ordering, prelude::*}; +use sp_std::{cmp::Ordering, iter::IntoIterator, prelude::*}; mod benchmarking; pub mod weights; @@ -122,7 +123,17 @@ pub use weights::WeightInfo; /// All migrations. pub mod migrations; -const LOG_TARGET: &str = "runtime::elections-phragmen"; +pub(crate) const LOG_TARGET: &str = "runtime::elections"; + +// logging helper. +macro_rules! log { + ($level:tt, $patter:expr $(, $values:expr)* $(,)?) => { + log::$level!( + target: crate::LOG_TARGET, + concat!("[{:?}] 🗳️ ", $patter), >::block_number() $(, $values)* + ) + }; +} type BalanceOf = <::Currency as Currency<::AccountId>>::Balance; @@ -173,6 +184,17 @@ pub struct SeatHolder { pub deposit: Balance, } +/// The results of running the pre-election step. +#[derive(Debug, Clone)] +struct PreElectionResults { + pub num_to_elect: usize, + pub candidate_ids: Vec, + pub candidates_and_deposit: Vec<(T::AccountId, BalanceOf)>, + pub voters_and_stakes: Vec<(T::AccountId, BalanceOf, Vec)>, + pub voters_and_votes: Vec<(T::AccountId, VoteWeight, Vec)>, + pub num_edges: u32, +} + pub use pallet::*; type AccountIdLookupOf = <::Lookup as StaticLookup>::Source; @@ -196,7 +218,7 @@ pub mod pallet { pub trait Config: frame_system::Config { type RuntimeEvent: From> + IsType<::RuntimeEvent>; - /// Identifier for the elections-phragmen pallet's lock + /// Identifier for the elections pallet's lock #[pallet::constant] type PalletId: Get; @@ -249,7 +271,7 @@ pub mod pallet { #[pallet::constant] type TermDuration: Get; - /// The maximum number of candidates in a phragmen election. + /// The maximum number of candidates in an election. /// /// Warning: This impacts the size of the election which is run onchain. Chose wisely, and /// consider how it will impact `T::WeightInfo::election_phragmen`. @@ -258,7 +280,7 @@ pub mod pallet { #[pallet::constant] type MaxCandidates: Get; - /// The maximum number of voters to allow in a phragmen election. + /// The maximum number of voters to allow in an election. /// /// Warning: This impacts the size of the election which is run onchain. Chose wisely, and /// consider how it will impact `T::WeightInfo::election_phragmen`. @@ -267,6 +289,12 @@ pub mod pallet { #[pallet::constant] type MaxVoters: Get; + /// Something that will calculate the result of elections. + type ElectionSolver: NposSolver; + + /// Weight information for the [`Config::ElectionSolver`]. + type SolverWeightInfo: frame_election_provider_support::WeightInfo; + /// Maximum numbers of votes per voter. /// /// Warning: This impacts the size of the election which is run onchain. Chose wisely, and @@ -286,7 +314,7 @@ pub mod pallet { fn on_initialize(n: T::BlockNumber) -> Weight { let term_duration = T::TermDuration::get(); if !term_duration.is_zero() && (n % term_duration).is_zero() { - Self::do_phragmen() + Self::do_election() } else { Weight::zero() } @@ -295,11 +323,26 @@ pub mod pallet { fn integrity_test() { let block_weight = T::BlockWeights::get().max_block; // mind the order. - let election_weight = T::WeightInfo::election_phragmen( + let pre_solve_weight = T::WeightInfo::pre_solve_election( T::MaxCandidates::get(), T::MaxVoters::get(), T::MaxVotesPerVoter::get() * T::MaxVoters::get(), ); + // mind the order. + let post_solve_weight = T::WeightInfo::post_solve_election( + T::MaxCandidates::get(), + T::MaxVoters::get(), + T::MaxVotesPerVoter::get() * T::MaxVoters::get(), + ); + // mind the order. + let election_weight = T::ElectionSolver::weight::( + T::MaxCandidates::get(), + T::MaxVoters::get(), + T::MaxVotesPerVoter::get() * T::MaxVoters::get(), + ); + let election_weight = pre_solve_weight + .saturating_sub(post_solve_weight) + .saturating_add(election_weight); let to_seconds = |w: &Weight| { w.ref_time() as f32 / @@ -534,8 +577,8 @@ pub mod pallet { /// the outgoing member is slashed. /// /// If a runner-up is available, then the best runner-up will be removed and replaces the - /// outgoing member. Otherwise, if `rerun_election` is `true`, a new phragmen election is - /// started, else, nothing happens. + /// outgoing member. Otherwise, if `rerun_election` is `true`, a new election is started, + /// else, nothing happens. /// /// If `slash_bond` is set to true, the bond of the member being removed is slashed. Else, /// it is returned. @@ -565,7 +608,7 @@ pub mod pallet { Self::deposit_event(Event::MemberKicked { member: who }); if rerun_election { - Self::do_phragmen(); + Self::do_election(); } // no refund needed. @@ -664,6 +707,8 @@ pub mod pallet { InvalidRenouncing, /// Prediction regarding replacement after member removal is wrong. InvalidReplacement, + /// No candidates for the next term. + EmptyTerm, } /// The current elected members. @@ -742,7 +787,7 @@ pub mod pallet { Members::::mutate(|members| { match members.binary_search_by(|m| m.who.cmp(member)) { Ok(_) => { - panic!("Duplicate member in elections-phragmen genesis: {}", member) + panic!("Duplicate member in elections genesis: {}", member) }, Err(pos) => members.insert( pos, @@ -934,11 +979,77 @@ impl Pallet { debug_assert!(_remainder.is_zero()); } - /// Run the phragmen election with all required side processes and state updates, if election - /// succeeds. Else, it will emit an `ElectionError` event. + /// Run an election with all required side processes and state updates, if election + /// succeeds. Else, it will emit an `ElectionError` event. The election algorithm is defined + /// by the implementor of `Self::ElectionSolver`. /// /// Calls the appropriate [`ChangeMembers`] function variant internally. - fn do_phragmen() -> Weight { + fn do_election() -> Weight { + let PreElectionResults { + num_to_elect, + candidate_ids, + candidates_and_deposit, + voters_and_stakes, + voters_and_votes, + num_edges, + } = match Self::do_pre_solve_election() { + Ok(results) => results, + Err(err) => match err { + Error::EmptyTerm => { + Self::deposit_event(Event::EmptyTerm); + return T::DbWeight::get().reads(3) + }, + Error::TooManyVotes => { + Self::deposit_event(Event::ElectionError); + log!(error, "Failed to run election. Number of voters exceeded",); + let max_voters = ::MaxVoters::get() as usize; + return T::DbWeight::get().reads(3 + max_voters as u64) + }, + _ => { + log!(error, "Unexpected pre-election error",); + let max_voters = ::MaxVoters::get() as usize; + return T::DbWeight::get().reads(3 + max_voters as u64) + }, + }, + }; + + let num_candidates = candidates_and_deposit.len() as u32; + let num_voters = voters_and_votes.len() as u32; + let num_edges = num_edges; + + let election_winners = + T::ElectionSolver::solve(num_to_elect, candidate_ids, voters_and_votes) + .map( + |ElectionResult::< + T::AccountId, + ::Accuracy, + > { + winners, + assignments: _, + }| winners, + ) + .map_err(|e| { + log!(warn, "Failed to run election [{:?}].", e); + Self::deposit_event(Event::ElectionError); + }); + + let post_election_weight = if let Ok(winners) = election_winners { + Self::do_post_solve_election(winners, candidates_and_deposit, voters_and_stakes); + T::WeightInfo::post_solve_election(num_candidates, num_voters, num_edges) + } else { + Weight::zero() + }; + + T::ElectionSolver::weight::(num_candidates, num_voters, num_edges) + .saturating_add(T::WeightInfo::pre_solve_election( + num_candidates, + num_voters, + num_edges, + )) + .saturating_add(post_election_weight) + } + + fn do_pre_solve_election() -> Result, Error> { let desired_seats = T::DesiredMembers::get() as usize; let desired_runners_up = T::DesiredRunnersUp::get() as usize; let num_to_elect = desired_runners_up + desired_seats; @@ -948,44 +1059,33 @@ impl Pallet { candidates_and_deposit.append(&mut Self::implicit_candidates_with_deposit()); if candidates_and_deposit.len().is_zero() { - Self::deposit_event(Event::EmptyTerm); - return T::DbWeight::get().reads(3) + return Err(Error::EmptyTerm) } - // All of the new winners that come out of phragmen will thus have a deposit recorded. + // All of the new winners that come out of the election will thus have a deposit recorded. let candidate_ids = candidates_and_deposit.iter().map(|(x, _)| x).cloned().collect::>(); // helper closures to deal with balance/stake. let total_issuance = T::Currency::total_issuance(); let to_votes = |b: BalanceOf| T::CurrencyToVote::to_vote(b, total_issuance); - let to_balance = |e: ExtendedBalance| T::CurrencyToVote::to_currency(e, total_issuance); let mut num_edges: u32 = 0; let max_voters = ::MaxVoters::get() as usize; // used for prime election. let mut voters_and_stakes = Vec::new(); - match Voting::::iter().try_for_each(|(voter, Voter { stake, votes, .. })| { + + Voting::::iter().try_for_each(|(voter, Voter { stake, votes, .. })| { if voters_and_stakes.len() < max_voters { voters_and_stakes.push((voter, stake, votes)); Ok(()) } else { - Err(()) + Err(Error::TooManyVotes) } - }) { - Ok(_) => (), - Err(_) => { - log::error!( - target: LOG_TARGET, - "Failed to run election. Number of voters exceeded", - ); - Self::deposit_event(Event::ElectionError); - return T::DbWeight::get().reads(3 + max_voters as u64) - }, - } + })?; - // used for phragmen. + // used for elections. let voters_and_votes = voters_and_stakes .iter() .cloned() @@ -995,157 +1095,144 @@ impl Pallet { }) .collect::>(); - let weight_candidates = candidates_and_deposit.len() as u32; - let weight_voters = voters_and_votes.len() as u32; - let weight_edges = num_edges; - let _ = - sp_npos_elections::seq_phragmen(num_to_elect, candidate_ids, voters_and_votes, None) - .map(|ElectionResult:: { winners, assignments: _ }| { - // this is already sorted by id. - let old_members_ids_sorted = >::take() - .into_iter() - .map(|m| m.who) - .collect::>(); - // this one needs a sort by id. - let mut old_runners_up_ids_sorted = >::take() - .into_iter() - .map(|r| r.who) - .collect::>(); - old_runners_up_ids_sorted.sort(); - - // filter out those who end up with no backing stake. - let mut new_set_with_stake = winners - .into_iter() - .filter_map( - |(m, b)| if b.is_zero() { None } else { Some((m, to_balance(b))) }, - ) - .collect::)>>(); - - // OPTIMIZATION NOTE: we could bail out here if `new_set.len() == 0`. There - // isn't much left to do. Yet, re-arranging the code would require duplicating - // the slashing of exposed candidates, cleaning any previous members, and so on. - // For now, in favor of readability and veracity, we keep it simple. - - // split new set into winners and runners up. - let split_point = desired_seats.min(new_set_with_stake.len()); - let mut new_members_sorted_by_id = - new_set_with_stake.drain(..split_point).collect::>(); - new_members_sorted_by_id.sort_by(|i, j| i.0.cmp(&j.0)); - - // all the rest will be runners-up - new_set_with_stake.reverse(); - let new_runners_up_sorted_by_rank = new_set_with_stake; - let mut new_runners_up_ids_sorted = new_runners_up_sorted_by_rank - .iter() - .map(|(r, _)| r.clone()) - .collect::>(); - new_runners_up_ids_sorted.sort(); - - // Now we select a prime member using a [Borda - // count](https://en.wikipedia.org/wiki/Borda_count). We weigh everyone's vote for - // that new member by a multiplier based on the order of the votes. i.e. the - // first person a voter votes for gets a 16x multiplier, the next person gets a - // 15x multiplier, an so on... (assuming `T::MaxVotesPerVoter` = 16) - let mut prime_votes = new_members_sorted_by_id - .iter() - .map(|c| (&c.0, BalanceOf::::zero())) - .collect::>(); - for (_, stake, votes) in voters_and_stakes.into_iter() { - for (vote_multiplier, who) in - votes.iter().enumerate().map(|(vote_position, who)| { - ((T::MaxVotesPerVoter::get() as usize - vote_position) as u32, who) - }) { - if let Ok(i) = prime_votes.binary_search_by_key(&who, |k| k.0) { - prime_votes[i].1 = prime_votes[i] - .1 - .saturating_add(stake.saturating_mul(vote_multiplier.into())); - } - } - } - // We then select the new member with the highest weighted stake. In the case of - // a tie, the last person in the list with the tied score is selected. This is - // the person with the "highest" account id based on the sort above. - let prime = prime_votes.into_iter().max_by_key(|x| x.1).map(|x| x.0.clone()); - - // new_members_sorted_by_id is sorted by account id. - let new_members_ids_sorted = new_members_sorted_by_id - .iter() - .map(|(m, _)| m.clone()) - .collect::>(); - - // report member changes. We compute diff because we need the outgoing list. - let (incoming, outgoing) = T::ChangeMembers::compute_members_diff_sorted( - &new_members_ids_sorted, - &old_members_ids_sorted, - ); - T::ChangeMembers::change_members_sorted( - &incoming, - &outgoing, - &new_members_ids_sorted, - ); - T::ChangeMembers::set_prime(prime); - - // All candidates/members/runners-up who are no longer retaining a position as a - // seat holder will lose their bond. - candidates_and_deposit.iter().for_each(|(c, d)| { - if new_members_ids_sorted.binary_search(c).is_err() && - new_runners_up_ids_sorted.binary_search(c).is_err() - { - let (imbalance, _) = T::Currency::slash_reserved(c, *d); - T::LoserCandidate::on_unbalanced(imbalance); - Self::deposit_event(Event::CandidateSlashed { - candidate: c.clone(), - amount: *d, - }); - } - }); + Ok(PreElectionResults { + num_to_elect, + candidate_ids, + candidates_and_deposit, + voters_and_stakes, + voters_and_votes, + num_edges, + }) + } - // write final values to storage. - let deposit_of_candidate = |x: &T::AccountId| -> BalanceOf { - // defensive-only. This closure is used against the new members and new - // runners-up, both of which are phragmen winners and thus must have - // deposit. - candidates_and_deposit - .iter() - .find_map(|(c, d)| if c == x { Some(*d) } else { None }) - .defensive_unwrap_or_default() - }; - // fetch deposits from the one recorded one. This will make sure that a - // candidate who submitted candidacy before a change to candidacy deposit will - // have the correct amount recorded. - >::put( - new_members_sorted_by_id - .iter() - .map(|(who, stake)| SeatHolder { - deposit: deposit_of_candidate(who), - who: who.clone(), - stake: *stake, - }) - .collect::>(), - ); - >::put( - new_runners_up_sorted_by_rank - .into_iter() - .map(|(who, stake)| SeatHolder { - deposit: deposit_of_candidate(&who), - who, - stake, - }) - .collect::>(), - ); + fn do_post_solve_election( + winners: Vec<(T::AccountId, u128)>, + candidates_and_deposit: Vec<(T::AccountId, BalanceOf)>, + voters_and_stakes: Vec<(T::AccountId, BalanceOf, Vec)>, + ) { + let desired_seats = T::DesiredMembers::get() as usize; + let total_issuance = T::Currency::total_issuance(); + let to_balance = |e: ExtendedBalance| T::CurrencyToVote::to_currency(e, total_issuance); - // clean candidates. - >::kill(); + // this is already sorted by id. + let old_members_ids_sorted = + >::take().into_iter().map(|m| m.who).collect::>(); + // this one needs sorted by id. + let mut old_runners_up_ids_sorted = + >::take().into_iter().map(|r| r.who).collect::>(); + old_runners_up_ids_sorted.sort(); - Self::deposit_event(Event::NewTerm { new_members: new_members_sorted_by_id }); - >::mutate(|v| *v += 1); - }) - .map_err(|e| { - log::error!(target: LOG_TARGET, "Failed to run election [{:?}].", e,); - Self::deposit_event(Event::ElectionError); - }); + // filter out those who end up with no backing stake. + let mut new_set_with_stake = winners + .into_iter() + .filter_map(|(m, b)| if b.is_zero() { None } else { Some((m, to_balance(b))) }) + .collect::)>>(); + + // OPTIMIZATION NOTE: we could bail out here if `new_set.len() == 0`. There + // isn't much left to do. Yet, re-arranging the code would require duplicating + // the slashing of exposed candidates, cleaning any previous members, and so on. + // For now, in favor of readability and veracity, we keep it simple. + + // split new set into winners and runners up. + let split_point = desired_seats.min(new_set_with_stake.len()); + let mut new_members_sorted_by_id = + new_set_with_stake.drain(..split_point).collect::>(); + new_members_sorted_by_id.sort_by(|i, j| i.0.cmp(&j.0)); + + // all the rest will be runners-up + new_set_with_stake.reverse(); + let new_runners_up_sorted_by_rank = new_set_with_stake; + let mut new_runners_up_ids_sorted = + new_runners_up_sorted_by_rank.iter().map(|(r, _)| r.clone()).collect::>(); + new_runners_up_ids_sorted.sort(); + + // Now we select a prime member using a [Borda + // count](https://en.wikipedia.org/wiki/Borda_count). We weigh everyone's vote for + // that new member by a multiplier based on the order of the votes. i.e. the + // first person a voter votes for gets a 16x multiplier, the next person gets a + // 15x multiplier, an so on... (assuming `T::MaxVotesPerVoter` = 16). + let mut prime_votes = new_members_sorted_by_id + .iter() + .map(|c| (&c.0, BalanceOf::::zero())) + .collect::>(); + for (_, stake, votes) in voters_and_stakes.into_iter() { + for (vote_multiplier, who) in votes.iter().enumerate().map(|(vote_position, who)| { + ((T::MaxVotesPerVoter::get() as usize - vote_position) as u32, who) + }) { + if let Ok(i) = prime_votes.binary_search_by_key(&who, |k| k.0) { + prime_votes[i].1 = prime_votes[i] + .1 + .saturating_add(stake.saturating_mul(vote_multiplier.into())); + } + } + } + // We then select the new member with the highest weighted stake. In the case of + // a tie, the last person in the list with the tied score is selected. This is + // the person with the "highest" account id based on the sort above. + let prime = prime_votes.into_iter().max_by_key(|x| x.1).map(|x| x.0.clone()); - T::WeightInfo::election_phragmen(weight_candidates, weight_voters, weight_edges) + // new_members_sorted_by_id is sorted by account id. + let new_members_ids_sorted = new_members_sorted_by_id + .iter() + .map(|(m, _)| m.clone()) + .collect::>(); + + // report member changes. We compute diff because we need the outgoing list. + let (incoming, outgoing) = T::ChangeMembers::compute_members_diff_sorted( + &new_members_ids_sorted, + &old_members_ids_sorted, + ); + T::ChangeMembers::change_members_sorted(&incoming, &outgoing, &new_members_ids_sorted); + T::ChangeMembers::set_prime(prime); + + // All candidates/members/runners-up who are no longer retaining a position as a + // seat holder will lose their bond. + candidates_and_deposit.iter().for_each(|(c, d)| { + if new_members_ids_sorted.binary_search(c).is_err() && + new_runners_up_ids_sorted.binary_search(c).is_err() + { + let (imbalance, _) = T::Currency::slash_reserved(c, *d); + T::LoserCandidate::on_unbalanced(imbalance); + Self::deposit_event(Event::CandidateSlashed { candidate: c.clone(), amount: *d }); + } + }); + + // write final values to storage. + let deposit_of_candidate = |x: &T::AccountId| -> BalanceOf { + // defensive-only. This closure is used against the new members and new + // runners-up, both of which are election winners and thus must have + // deposit. + candidates_and_deposit + .iter() + .find_map(|(c, d)| if c == x { Some(*d) } else { None }) + .defensive_unwrap_or_default() + }; + // fetch deposits from the one recorded one. This will make sure that a + // candidate who submitted candidacy before a change to candidacy deposit will + // have the correct amount recorded. + >::put( + new_members_sorted_by_id + .iter() + .map(|(who, stake)| SeatHolder { + deposit: deposit_of_candidate(who), + who: who.clone(), + stake: *stake, + }) + .collect::>(), + ); + >::put( + new_runners_up_sorted_by_rank + .into_iter() + .map(|(who, stake)| SeatHolder { deposit: deposit_of_candidate(&who), who, stake }) + .collect::>(), + ); + + // clean candidates. + >::kill(); + + log!(info, "New term election successful."); + Self::deposit_event(Event::NewTerm { new_members: new_members_sorted_by_id }); + >::mutate(|v| *v += 1); } } @@ -1196,7 +1283,8 @@ impl ContainsLengthBound for Pallet { #[cfg(test)] mod tests { use super::*; - use crate as elections_phragmen; + use crate as elections; + use frame_election_provider_support::{weights::SubstrateWeight, ApprovalVoting}; use frame_support::{ assert_noop, assert_ok, dispatch::DispatchResultWithPostInfo, @@ -1208,7 +1296,7 @@ mod tests { use sp_runtime::{ testing::Header, traits::{BlakeTwo256, IdentityLookup}, - BuildStorage, + BuildStorage, Perbill, }; use substrate_test_utils::assert_eq_uvec; @@ -1307,13 +1395,13 @@ mod tests { } parameter_types! { - pub const ElectionsPhragmenPalletId: LockIdentifier = *b"phrelect"; - pub const PhragmenMaxVoters: u32 = 1000; - pub const PhragmenMaxCandidates: u32 = 100; + pub const ElectionsPalletId: LockIdentifier = *b"phrelect"; + pub const MaxVoters: u32 = 256; + pub const MaxCandidates: u32 = 64; } impl Config for Test { - type PalletId = ElectionsPhragmenPalletId; + type PalletId = ElectionsPalletId; type RuntimeEvent = RuntimeEvent; type Currency = Balances; type CurrencyToVote = frame_support::traits::SaturatingCurrencyToVote; @@ -1328,9 +1416,11 @@ mod tests { type LoserCandidate = (); type KickedMember = (); type WeightInfo = (); - type MaxVoters = PhragmenMaxVoters; + type MaxVoters = MaxVoters; + type MaxCandidates = MaxCandidates; + type ElectionSolver = ApprovalVoting; + type SolverWeightInfo = SubstrateWeight; type MaxVotesPerVoter = ConstU32<16>; - type MaxCandidates = PhragmenMaxCandidates; } pub type Block = sp_runtime::generic::Block; @@ -1345,7 +1435,7 @@ mod tests { { System: frame_system::{Pallet, Call, Event}, Balances: pallet_balances::{Pallet, Call, Event, Config}, - Elections: elections_phragmen::{Pallet, Call, Event, Config}, + Elections: elections::{Pallet, Call, Event, Config}, } ); @@ -1406,9 +1496,7 @@ mod tests { (6, 60 * self.balance_factor), ], }, - elections: elections_phragmen::GenesisConfig:: { - members: self.genesis_members, - }, + elections: elections::GenesisConfig:: { members: self.genesis_members }, } .build_storage() .unwrap() @@ -1466,7 +1554,7 @@ mod tests { .get(0) .cloned() .map(|lock| { - assert_eq!(lock.id, ElectionsPhragmenPalletId::get()); + assert_eq!(lock.id, ElectionsPalletId::get()); lock.amount }) .unwrap_or_default() @@ -1657,7 +1745,7 @@ mod tests { } #[test] - #[should_panic = "Duplicate member in elections-phragmen genesis: 2"] + #[should_panic = "Duplicate member in elections genesis: 2"] fn genesis_members_cannot_be_duplicate() { ExtBuilder::default() .desired_members(3) diff --git a/frame/elections-phragmen/src/migrations/mod.rs b/frame/elections/src/migrations/mod.rs similarity index 100% rename from frame/elections-phragmen/src/migrations/mod.rs rename to frame/elections/src/migrations/mod.rs diff --git a/frame/elections-phragmen/src/migrations/v3.rs b/frame/elections/src/migrations/v3.rs similarity index 100% rename from frame/elections-phragmen/src/migrations/v3.rs rename to frame/elections/src/migrations/v3.rs diff --git a/frame/elections-phragmen/src/migrations/v4.rs b/frame/elections/src/migrations/v4.rs similarity index 100% rename from frame/elections-phragmen/src/migrations/v4.rs rename to frame/elections/src/migrations/v4.rs diff --git a/frame/elections-phragmen/src/migrations/v5.rs b/frame/elections/src/migrations/v5.rs similarity index 100% rename from frame/elections-phragmen/src/migrations/v5.rs rename to frame/elections/src/migrations/v5.rs diff --git a/frame/elections/src/weights.rs b/frame/elections/src/weights.rs new file mode 100644 index 000000000..8caa93f0a --- /dev/null +++ b/frame/elections/src/weights.rs @@ -0,0 +1,397 @@ +// This file is part of Substrate. + +// Copyright (C) 2022 Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +//! Autogenerated weights for pallet_elections +//! +//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev +//! DATE: 2022-11-22, STEPS: `50`, REPEAT: 20, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! HOSTNAME: `bm3`, CPU: `Intel(R) Core(TM) i7-7700K CPU @ 4.20GHz` +//! EXECUTION: Some(Wasm), WASM-EXECUTION: Compiled, CHAIN: Some("dev"), DB CACHE: 1024 + +// Executed Command: +// /home/benchbot/cargo_target_dir/production/substrate +// benchmark +// pallet +// --steps=50 +// --repeat=20 +// --extrinsic=* +// --execution=wasm +// --wasm-execution=compiled +// --heap-pages=4096 +// --json-file=/var/lib/gitlab-runner/builds/zyw4fam_/0/parity/mirrors/substrate/.git/.artifacts/bench.json +// --pallet=pallet_elections +// --chain=dev +// --header=./HEADER-APACHE2 +// --output=./frame/elections/src/weights.rs +// --template=./.maintain/frame-weight-template.hbs + +#![cfg_attr(rustfmt, rustfmt_skip)] +#![allow(unused_parens)] +#![allow(unused_imports)] + +use frame_support::{traits::Get, weights::{Weight, constants::RocksDbWeight}}; +use sp_std::marker::PhantomData; + +/// Weight functions needed for pallet_elections. +pub trait WeightInfo { + fn vote_equal(v: u32, ) -> Weight; + fn vote_more(v: u32, ) -> Weight; + fn vote_less(v: u32, ) -> Weight; + fn remove_voter() -> Weight; + fn submit_candidacy(c: u32, ) -> Weight; + fn renounce_candidacy_candidate(c: u32, ) -> Weight; + fn renounce_candidacy_members() -> Weight; + fn renounce_candidacy_runners_up() -> Weight; + fn remove_member_without_replacement() -> Weight; + fn remove_member_with_replacement() -> Weight; + fn clean_defunct_voters(v: u32, d: u32, ) -> Weight; + fn pre_solve_election(c: u32, v: u32, e: u32, ) -> Weight; + fn post_solve_election(c: u32, v: u32, e: u32, ) -> Weight; +} + +/// Weights for pallet_elections using the Substrate node and recommended hardware. +pub struct SubstrateWeight(PhantomData); +impl WeightInfo for SubstrateWeight { + // Storage: Elections Candidates (r:1 w:0) + // Storage: Elections Members (r:1 w:0) + // Storage: Elections RunnersUp (r:1 w:0) + // Storage: Elections Voting (r:1 w:1) + // Storage: Balances Locks (r:1 w:1) + /// The range of component `v` is `[1, 16]`. + fn vote_equal(v: u32, ) -> Weight { + // Minimum execution time: 37_500 nanoseconds. + Weight::from_ref_time(38_575_649) + // Standard Error: 2_961 + .saturating_add(Weight::from_ref_time(154_225).saturating_mul(v.into())) + .saturating_add(T::DbWeight::get().reads(5)) + .saturating_add(T::DbWeight::get().writes(2)) + } + // Storage: Elections Candidates (r:1 w:0) + // Storage: Elections Members (r:1 w:0) + // Storage: Elections RunnersUp (r:1 w:0) + // Storage: Elections Voting (r:1 w:1) + // Storage: Balances Locks (r:1 w:1) + /// The range of component `v` is `[2, 16]`. + fn vote_more(v: u32, ) -> Weight { + // Minimum execution time: 48_836 nanoseconds. + Weight::from_ref_time(49_566_969) + // Standard Error: 3_258 + .saturating_add(Weight::from_ref_time(153_342).saturating_mul(v.into())) + .saturating_add(T::DbWeight::get().reads(5)) + .saturating_add(T::DbWeight::get().writes(2)) + } + // Storage: Elections Candidates (r:1 w:0) + // Storage: Elections Members (r:1 w:0) + // Storage: Elections RunnersUp (r:1 w:0) + // Storage: Elections Voting (r:1 w:1) + // Storage: Balances Locks (r:1 w:1) + /// The range of component `v` is `[2, 16]`. + fn vote_less(v: u32, ) -> Weight { + // Minimum execution time: 47_664 nanoseconds. + Weight::from_ref_time(48_768_157) + // Standard Error: 3_321 + .saturating_add(Weight::from_ref_time(215_112).saturating_mul(v.into())) + .saturating_add(T::DbWeight::get().reads(5)) + .saturating_add(T::DbWeight::get().writes(2)) + } + // Storage: Elections Voting (r:1 w:1) + // Storage: Balances Locks (r:1 w:1) + fn remove_voter() -> Weight { + // Minimum execution time: 47_146 nanoseconds. + Weight::from_ref_time(47_846_000) + .saturating_add(T::DbWeight::get().reads(2)) + .saturating_add(T::DbWeight::get().writes(2)) + } + // Storage: Elections Candidates (r:1 w:1) + // Storage: Elections Members (r:1 w:0) + // Storage: Elections RunnersUp (r:1 w:0) + /// The range of component `c` is `[1, 1000]`. + fn submit_candidacy(c: u32, ) -> Weight { + // Minimum execution time: 42_799 nanoseconds. + Weight::from_ref_time(46_920_164) + // Standard Error: 780 + .saturating_add(Weight::from_ref_time(81_672).saturating_mul(c.into())) + .saturating_add(T::DbWeight::get().reads(3)) + .saturating_add(T::DbWeight::get().writes(1)) + } + // Storage: Elections Candidates (r:1 w:1) + /// The range of component `c` is `[1, 1000]`. + fn renounce_candidacy_candidate(c: u32, ) -> Weight { + // Minimum execution time: 40_946 nanoseconds. + Weight::from_ref_time(53_109_738) + // Standard Error: 1_220 + .saturating_add(Weight::from_ref_time(60_643).saturating_mul(c.into())) + .saturating_add(T::DbWeight::get().reads(1)) + .saturating_add(T::DbWeight::get().writes(1)) + } + // Storage: Elections Members (r:1 w:1) + // Storage: Elections RunnersUp (r:1 w:1) + // Storage: Council Prime (r:1 w:1) + // Storage: Council Proposals (r:1 w:0) + // Storage: Council Members (r:0 w:1) + fn renounce_candidacy_members() -> Weight { + // Minimum execution time: 53_454 nanoseconds. + Weight::from_ref_time(53_921_000) + .saturating_add(T::DbWeight::get().reads(4)) + .saturating_add(T::DbWeight::get().writes(4)) + } + // Storage: Elections RunnersUp (r:1 w:1) + fn renounce_candidacy_runners_up() -> Weight { + // Minimum execution time: 41_275 nanoseconds. + Weight::from_ref_time(42_444_000) + .saturating_add(T::DbWeight::get().reads(1)) + .saturating_add(T::DbWeight::get().writes(1)) + } + // Storage: Benchmark Override (r:0 w:0) + fn remove_member_without_replacement() -> Weight { + // Minimum execution time: 2_000_000_000 nanoseconds. + Weight::from_ref_time(2_000_000_000_000) + } + // Storage: Elections Members (r:1 w:1) + // Storage: System Account (r:1 w:1) + // Storage: Elections RunnersUp (r:1 w:1) + // Storage: Council Prime (r:1 w:1) + // Storage: Council Proposals (r:1 w:0) + // Storage: Council Members (r:0 w:1) + fn remove_member_with_replacement() -> Weight { + // Minimum execution time: 62_040 nanoseconds. + Weight::from_ref_time(62_569_000) + .saturating_add(T::DbWeight::get().reads(5)) + .saturating_add(T::DbWeight::get().writes(5)) + } + // Storage: Elections Voting (r:5001 w:5000) + // Storage: Elections Members (r:1 w:0) + // Storage: Elections RunnersUp (r:1 w:0) + // Storage: Elections Candidates (r:1 w:0) + // Storage: Balances Locks (r:5000 w:5000) + // Storage: System Account (r:5000 w:5000) + /// The range of component `v` is `[5000, 10000]`. + /// The range of component `d` is `[0, 5000]`. + fn clean_defunct_voters(v: u32, _d: u32, ) -> Weight { + // Minimum execution time: 298_212_195 nanoseconds. + Weight::from_ref_time(298_678_889_000) + // Standard Error: 264_713 + .saturating_add(Weight::from_ref_time(38_222_955).saturating_mul(v.into())) + .saturating_add(T::DbWeight::get().reads(4)) + .saturating_add(T::DbWeight::get().reads((3_u64).saturating_mul(v.into()))) + .saturating_add(T::DbWeight::get().writes((3_u64).saturating_mul(v.into()))) + } + // Storage: Elections Candidates (r:1 w:0) + // Storage: Elections Members (r:1 w:0) + // Storage: Elections RunnersUp (r:1 w:0) + // Storage: Elections Voting (r:10001 w:0) + /// The range of component `c` is `[1, 1000]`. + /// The range of component `v` is `[1, 10000]`. + /// The range of component `e` is `[10000, 160000]`. + fn pre_solve_election(_c: u32, v: u32, _e: u32, ) -> Weight { + // Minimum execution time: 6_543_626 nanoseconds. + Weight::from_ref_time(6_627_885_000) + // Standard Error: 14_605 + .saturating_add(Weight::from_ref_time(7_613_226).saturating_mul(v.into())) + .saturating_add(T::DbWeight::get().reads(296)) + .saturating_add(T::DbWeight::get().reads((1_u64).saturating_mul(v.into()))) + } + // Storage: Elections Members (r:1 w:1) + // Storage: Elections RunnersUp (r:1 w:1) + // Storage: Council Proposals (r:1 w:0) + // Storage: Elections ElectionRounds (r:1 w:1) + // Storage: Elections Candidates (r:0 w:1) + // Storage: Council Members (r:0 w:1) + // Storage: Council Prime (r:0 w:1) + // Storage: System Account (r:1 w:1) + /// The range of component `c` is `[1, 1000]`. + /// The range of component `v` is `[1, 10000]`. + /// The range of component `e` is `[10000, 160000]`. + fn post_solve_election(c: u32, v: u32, _e: u32, ) -> Weight { + // Minimum execution time: 3_843_566 nanoseconds. + Weight::from_ref_time(3_854_020_000) + // Standard Error: 59_766 + .saturating_add(Weight::from_ref_time(9_622_797).saturating_mul(c.into())) + // Standard Error: 5_975 + .saturating_add(Weight::from_ref_time(541_471).saturating_mul(v.into())) + .saturating_add(T::DbWeight::get().reads(4)) + .saturating_add(T::DbWeight::get().reads((1_u64).saturating_mul(c.into()))) + .saturating_add(T::DbWeight::get().writes(6)) + .saturating_add(T::DbWeight::get().writes((1_u64).saturating_mul(c.into()))) + } +} + +// For backwards compatibility and tests +impl WeightInfo for () { + // Storage: Elections Candidates (r:1 w:0) + // Storage: Elections Members (r:1 w:0) + // Storage: Elections RunnersUp (r:1 w:0) + // Storage: Elections Voting (r:1 w:1) + // Storage: Balances Locks (r:1 w:1) + /// The range of component `v` is `[1, 16]`. + fn vote_equal(v: u32, ) -> Weight { + // Minimum execution time: 37_500 nanoseconds. + Weight::from_ref_time(38_575_649) + // Standard Error: 2_961 + .saturating_add(Weight::from_ref_time(154_225).saturating_mul(v.into())) + .saturating_add(RocksDbWeight::get().reads(5)) + .saturating_add(RocksDbWeight::get().writes(2)) + } + // Storage: Elections Candidates (r:1 w:0) + // Storage: Elections Members (r:1 w:0) + // Storage: Elections RunnersUp (r:1 w:0) + // Storage: Elections Voting (r:1 w:1) + // Storage: Balances Locks (r:1 w:1) + /// The range of component `v` is `[2, 16]`. + fn vote_more(v: u32, ) -> Weight { + // Minimum execution time: 48_836 nanoseconds. + Weight::from_ref_time(49_566_969) + // Standard Error: 3_258 + .saturating_add(Weight::from_ref_time(153_342).saturating_mul(v.into())) + .saturating_add(RocksDbWeight::get().reads(5)) + .saturating_add(RocksDbWeight::get().writes(2)) + } + // Storage: Elections Candidates (r:1 w:0) + // Storage: Elections Members (r:1 w:0) + // Storage: Elections RunnersUp (r:1 w:0) + // Storage: Elections Voting (r:1 w:1) + // Storage: Balances Locks (r:1 w:1) + /// The range of component `v` is `[2, 16]`. + fn vote_less(v: u32, ) -> Weight { + // Minimum execution time: 47_664 nanoseconds. + Weight::from_ref_time(48_768_157) + // Standard Error: 3_321 + .saturating_add(Weight::from_ref_time(215_112).saturating_mul(v.into())) + .saturating_add(RocksDbWeight::get().reads(5)) + .saturating_add(RocksDbWeight::get().writes(2)) + } + // Storage: Elections Voting (r:1 w:1) + // Storage: Balances Locks (r:1 w:1) + fn remove_voter() -> Weight { + // Minimum execution time: 47_146 nanoseconds. + Weight::from_ref_time(47_846_000) + .saturating_add(RocksDbWeight::get().reads(2)) + .saturating_add(RocksDbWeight::get().writes(2)) + } + // Storage: Elections Candidates (r:1 w:1) + // Storage: Elections Members (r:1 w:0) + // Storage: Elections RunnersUp (r:1 w:0) + /// The range of component `c` is `[1, 1000]`. + fn submit_candidacy(c: u32, ) -> Weight { + // Minimum execution time: 42_799 nanoseconds. + Weight::from_ref_time(46_920_164) + // Standard Error: 780 + .saturating_add(Weight::from_ref_time(81_672).saturating_mul(c.into())) + .saturating_add(RocksDbWeight::get().reads(3)) + .saturating_add(RocksDbWeight::get().writes(1)) + } + // Storage: Elections Candidates (r:1 w:1) + /// The range of component `c` is `[1, 1000]`. + fn renounce_candidacy_candidate(c: u32, ) -> Weight { + // Minimum execution time: 40_946 nanoseconds. + Weight::from_ref_time(53_109_738) + // Standard Error: 1_220 + .saturating_add(Weight::from_ref_time(60_643).saturating_mul(c.into())) + .saturating_add(RocksDbWeight::get().reads(1)) + .saturating_add(RocksDbWeight::get().writes(1)) + } + // Storage: Elections Members (r:1 w:1) + // Storage: Elections RunnersUp (r:1 w:1) + // Storage: Council Prime (r:1 w:1) + // Storage: Council Proposals (r:1 w:0) + // Storage: Council Members (r:0 w:1) + fn renounce_candidacy_members() -> Weight { + // Minimum execution time: 53_454 nanoseconds. + Weight::from_ref_time(53_921_000) + .saturating_add(RocksDbWeight::get().reads(4)) + .saturating_add(RocksDbWeight::get().writes(4)) + } + // Storage: Elections RunnersUp (r:1 w:1) + fn renounce_candidacy_runners_up() -> Weight { + // Minimum execution time: 41_275 nanoseconds. + Weight::from_ref_time(42_444_000) + .saturating_add(RocksDbWeight::get().reads(1)) + .saturating_add(RocksDbWeight::get().writes(1)) + } + // Storage: Benchmark Override (r:0 w:0) + fn remove_member_without_replacement() -> Weight { + // Minimum execution time: 2_000_000_000 nanoseconds. + Weight::from_ref_time(2_000_000_000_000) + } + // Storage: Elections Members (r:1 w:1) + // Storage: System Account (r:1 w:1) + // Storage: Elections RunnersUp (r:1 w:1) + // Storage: Council Prime (r:1 w:1) + // Storage: Council Proposals (r:1 w:0) + // Storage: Council Members (r:0 w:1) + fn remove_member_with_replacement() -> Weight { + // Minimum execution time: 62_040 nanoseconds. + Weight::from_ref_time(62_569_000) + .saturating_add(RocksDbWeight::get().reads(5)) + .saturating_add(RocksDbWeight::get().writes(5)) + } + // Storage: Elections Voting (r:5001 w:5000) + // Storage: Elections Members (r:1 w:0) + // Storage: Elections RunnersUp (r:1 w:0) + // Storage: Elections Candidates (r:1 w:0) + // Storage: Balances Locks (r:5000 w:5000) + // Storage: System Account (r:5000 w:5000) + /// The range of component `v` is `[5000, 10000]`. + /// The range of component `d` is `[0, 5000]`. + fn clean_defunct_voters(v: u32, _d: u32, ) -> Weight { + // Minimum execution time: 298_212_195 nanoseconds. + Weight::from_ref_time(298_678_889_000) + // Standard Error: 264_713 + .saturating_add(Weight::from_ref_time(38_222_955).saturating_mul(v.into())) + .saturating_add(RocksDbWeight::get().reads(4)) + .saturating_add(RocksDbWeight::get().reads((3_u64).saturating_mul(v.into()))) + .saturating_add(RocksDbWeight::get().writes((3_u64).saturating_mul(v.into()))) + } + // Storage: Elections Candidates (r:1 w:0) + // Storage: Elections Members (r:1 w:0) + // Storage: Elections RunnersUp (r:1 w:0) + // Storage: Elections Voting (r:10001 w:0) + /// The range of component `c` is `[1, 1000]`. + /// The range of component `v` is `[1, 10000]`. + /// The range of component `e` is `[10000, 160000]`. + fn pre_solve_election(_c: u32, v: u32, _e: u32, ) -> Weight { + // Minimum execution time: 6_543_626 nanoseconds. + Weight::from_ref_time(6_627_885_000) + // Standard Error: 14_605 + .saturating_add(Weight::from_ref_time(7_613_226).saturating_mul(v.into())) + .saturating_add(RocksDbWeight::get().reads(296)) + .saturating_add(RocksDbWeight::get().reads((1_u64).saturating_mul(v.into()))) + } + // Storage: Elections Members (r:1 w:1) + // Storage: Elections RunnersUp (r:1 w:1) + // Storage: Council Proposals (r:1 w:0) + // Storage: Elections ElectionRounds (r:1 w:1) + // Storage: Elections Candidates (r:0 w:1) + // Storage: Council Members (r:0 w:1) + // Storage: Council Prime (r:0 w:1) + // Storage: System Account (r:1 w:1) + /// The range of component `c` is `[1, 1000]`. + /// The range of component `v` is `[1, 10000]`. + /// The range of component `e` is `[10000, 160000]`. + fn post_solve_election(c: u32, v: u32, _e: u32, ) -> Weight { + // Minimum execution time: 3_843_566 nanoseconds. + Weight::from_ref_time(3_854_020_000) + // Standard Error: 59_766 + .saturating_add(Weight::from_ref_time(9_622_797).saturating_mul(c.into())) + // Standard Error: 5_975 + .saturating_add(Weight::from_ref_time(541_471).saturating_mul(v.into())) + .saturating_add(RocksDbWeight::get().reads(4)) + .saturating_add(RocksDbWeight::get().reads((1_u64).saturating_mul(c.into()))) + .saturating_add(RocksDbWeight::get().writes(6)) + .saturating_add(RocksDbWeight::get().writes((1_u64).saturating_mul(c.into()))) + } +} diff --git a/primitives/npos-elections/src/approval_voting.rs b/primitives/npos-elections/src/approval_voting.rs new file mode 100644 index 000000000..2fcf17b60 --- /dev/null +++ b/primitives/npos-elections/src/approval_voting.rs @@ -0,0 +1,79 @@ +// This file is part of Substrate. + +// Copyright (C) 2023 Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +//! Implementation of the approval voting election method. +//! +//! This method allows voters to select many candidates and backing each of them with the same +//! vote weight. The candidates with the most backing are the election winners. + +use crate::{setup_inputs, ElectionResult, IdentifierT, PerThing128, VoteWeight}; +use sp_arithmetic::traits::Zero; +use sp_std::{cmp::Reverse, vec::Vec}; + +/// Execute an approvals voting election scheme. The return type is a list of winners. The weight +/// vector of all voters who contribute to the winners, which for this scheme is always 100% per +/// vote. +/// +/// - The vote assignment distribution for each vote is always 100%, since a voter backs a candidate +/// with its full stake, regardless of how many candidates are backed by the same stake. However, +/// the caller may normalize votes on site if required. +/// - Returning winners are sorted based on desirability. Voters are unsorted. +/// - The returning winners are zipped with their final backing stake. Yet, to get the exact final +/// weight distribution from the winner's point of view, one needs to build a support map. See +/// [`crate::SupportMap`] for more info. Note that this backing stake is computed in +/// ExtendedBalance and may be slightly different that what will be computed from the support map, +/// due to accuracy loss. +/// +/// This can only fail if the normalization fails. This can happen if for any of the resulting +/// assignments, `assignment.distribution.map(|p| p.deconstruct()).sum()` fails to fit inside +/// `UpperOf